summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--angdos.cfg634
-rw-r--r--changes.old6027
-rw-r--r--changes.txt82
-rw-r--r--credits.txt67
-rw-r--r--lib/.gitignore2
-rw-r--r--lib/apex/delete.me1
-rw-r--r--lib/bone/bone001.0124
-rw-r--r--lib/bone/bone004.0014
-rw-r--r--lib/bone/bone004.0024
-rw-r--r--lib/bone/bone004.0034
-rw-r--r--lib/bone/bone004.0044
-rw-r--r--lib/bone/bone004.0054
-rw-r--r--lib/bone/bone004.0064
-rw-r--r--lib/bone/bone004.0074
-rw-r--r--lib/bone/bone004.0084
-rw-r--r--lib/bone/bone004.0094
-rw-r--r--lib/bone/bone004.0104
-rw-r--r--lib/cmov/delete.me1
-rw-r--r--lib/core/auto.lua803
-rw-r--r--lib/core/building.lua15
-rw-r--r--lib/core/crpt_aux.lua243
-rw-r--r--lib/core/dungeon.lua106
-rw-r--r--lib/core/gen_idx.lua261
-rw-r--r--lib/core/gods.lua40
-rw-r--r--lib/core/help.lua141
-rw-r--r--lib/core/init.lua84
-rw-r--r--lib/core/load.lua37
-rw-r--r--lib/core/load2.lua63
-rw-r--r--lib/core/mimc_aux.lua95
-rw-r--r--lib/core/monsters.lua16
-rw-r--r--lib/core/objects.lua45
-rw-r--r--lib/core/player.lua140
-rw-r--r--lib/core/powers.lua105
-rw-r--r--lib/core/quests.lua57
-rw-r--r--lib/core/s_aux.lua742
-rw-r--r--lib/core/stores.lua32
-rw-r--r--lib/core/util.lua257
-rw-r--r--lib/core/xml.lua347
-rw-r--r--lib/data/delete.me0
-rw-r--r--lib/dngn/dun1.142
-rw-r--r--lib/dngn/dun10.03
-rw-r--r--lib/dngn/dun11.202
-rw-r--r--lib/dngn/dun11.222
-rw-r--r--lib/dngn/dun17.155
-rw-r--r--lib/dngn/dun18.02
-rw-r--r--lib/dngn/dun18.12
-rw-r--r--lib/dngn/dun19.115
-rw-r--r--lib/dngn/dun2.312
-rw-r--r--lib/dngn/dun22.102
-rw-r--r--lib/dngn/dun22.55
-rw-r--r--lib/dngn/dun24.03
-rw-r--r--lib/dngn/dun29.156
-rw-r--r--lib/dngn/dun3.185
-rw-r--r--lib/dngn/dun3.285
-rw-r--r--lib/dngn/dun3.35
-rw-r--r--lib/dngn/dun5.03
-rw-r--r--lib/dngn/dun5.1414
-rw-r--r--lib/dngn/dun6.03
-rw-r--r--lib/edit/a_info.txt2889
-rw-r--r--lib/edit/ab_info.txt118
-rw-r--r--lib/edit/al_info.txt2097
-rw-r--r--lib/edit/ba_info.txt283
-rw-r--r--lib/edit/between.map71
-rw-r--r--lib/edit/d_info.txt512
-rw-r--r--lib/edit/dragons.map43
-rw-r--r--lib/edit/e_info.txt2210
-rw-r--r--lib/edit/evil.map52
-rw-r--r--lib/edit/f_info.txt933
-rw-r--r--lib/edit/haunted.map49
-rw-r--r--lib/edit/k_info.txt6418
-rw-r--r--lib/edit/maeglin.map85
-rw-r--r--lib/edit/misc.txt91
-rw-r--r--lib/edit/nirnaeth.map64
-rw-r--r--lib/edit/numenor.txt80
-rw-r--r--lib/edit/ow_info.txt447
-rw-r--r--lib/edit/p_info.txt1974
-rw-r--r--lib/edit/qrand1.map32
-rw-r--r--lib/edit/qrand10.map36
-rw-r--r--lib/edit/qrand11.map36
-rw-r--r--lib/edit/qrand12.map36
-rw-r--r--lib/edit/qrand14.map37
-rw-r--r--lib/edit/qrand5.map27
-rw-r--r--lib/edit/qrand6.map37
-rw-r--r--lib/edit/qrand7.map35
-rw-r--r--lib/edit/r_info.txt18978
-rw-r--r--lib/edit/ra_info.txt1927
-rw-r--r--lib/edit/re_info.txt183
-rw-r--r--lib/edit/readme.txt96
-rw-r--r--lib/edit/s_crypt.map109
-rw-r--r--lib/edit/s_death.map104
-rw-r--r--lib/edit/s_doom.map226
-rwxr-xr-xlib/edit/s_factory.map238
-rw-r--r--lib/edit/s_gates.map117
-rw-r--r--lib/edit/s_info.txt546
-rw-r--r--lib/edit/s_name.map110
-rw-r--r--lib/edit/s_orc.map109
-rwxr-xr-xlib/edit/s_ship.map239
-rw-r--r--lib/edit/set_info.txt77
-rw-r--r--lib/edit/special.txt67
-rw-r--r--lib/edit/spiders.map66
-rw-r--r--lib/edit/st_info.txt744
-rw-r--r--lib/edit/t_basic.txt80
-rw-r--r--lib/edit/t_bree.txt131
-rw-r--r--lib/edit/t_d_bree.txt101
-rw-r--r--lib/edit/t_d_gond.txt118
-rw-r--r--lib/edit/t_d_khaz.txt87
-rw-r--r--lib/edit/t_d_lori.txt101
-rw-r--r--lib/edit/t_d_mina.txt91
-rw-r--r--lib/edit/t_gondol.txt219
-rw-r--r--lib/edit/t_info.txt41
-rw-r--r--lib/edit/t_khazad.txt105
-rw-r--r--lib/edit/t_lorien.txt162
-rw-r--r--lib/edit/t_minas.txt144
-rw-r--r--lib/edit/t_pref.txt111
-rw-r--r--lib/edit/thieves.map70
-rw-r--r--lib/edit/thrain.map35
-rw-r--r--lib/edit/tr_info.txt817
-rw-r--r--lib/edit/trolls.map58
-rw-r--r--lib/edit/v_info.txt2287
-rw-r--r--lib/edit/volcano.txt83
-rw-r--r--lib/edit/w_info.txt120
-rw-r--r--lib/edit/wf_info.txt170
-rw-r--r--lib/edit/wights.map82
-rw-r--r--lib/edit/wolves.map55
-rw-r--r--lib/file/book-0.txt86
-rw-r--r--lib/file/book-1.txt83
-rw-r--r--lib/file/book-10.txt250
-rw-r--r--lib/file/book-101.txt4
-rw-r--r--lib/file/book-102.txt4
-rw-r--r--lib/file/book-103.txt6
-rw-r--r--lib/file/book-104.txt6
-rw-r--r--lib/file/book-105.txt7
-rw-r--r--lib/file/book-106.txt5
-rw-r--r--lib/file/book-107.txt6
-rw-r--r--lib/file/book-11.txt250
-rw-r--r--lib/file/book-12.txt250
-rw-r--r--lib/file/book-13.txt250
-rw-r--r--lib/file/book-14.txt250
-rw-r--r--lib/file/book-15.txt250
-rw-r--r--lib/file/book-16.txt250
-rw-r--r--lib/file/book-17.txt250
-rw-r--r--lib/file/book-18.txt250
-rw-r--r--lib/file/book-19.txt250
-rw-r--r--lib/file/book-2.txt90
-rw-r--r--lib/file/book-20.txt139
-rw-r--r--lib/file/book-200.txt5
-rw-r--r--lib/file/book-201.txt5
-rw-r--r--lib/file/book-202.txt5
-rw-r--r--lib/file/book-203.txt5
-rw-r--r--lib/file/book-4.txt11
-rw-r--r--lib/file/book-6.txt263
-rw-r--r--lib/file/book-7.txt141
-rw-r--r--lib/file/book-8.txt47
-rw-r--r--lib/file/book-9.txt240
-rw-r--r--lib/file/bravado.txt106
-rw-r--r--lib/file/chainswd.txt8
-rw-r--r--lib/file/dam_huge.txt9
-rw-r--r--lib/file/dam_lots.txt21
-rw-r--r--lib/file/dam_med.txt25
-rw-r--r--lib/file/dam_none.txt24
-rw-r--r--lib/file/dam_xxx.txt11
-rw-r--r--lib/file/dead.txt24
-rw-r--r--lib/file/death.txt351
-rw-r--r--lib/file/elvish.txt218
-rw-r--r--lib/file/error.txt67
-rw-r--r--lib/file/mondeath.txt334
-rw-r--r--lib/file/monfear.txt63
-rw-r--r--lib/file/monspeak.txt361
-rw-r--r--lib/file/news.txt24
-rw-r--r--lib/file/news2.txt24
-rw-r--r--lib/file/rart_f.txt86
-rw-r--r--lib/file/rart_s.txt87
-rw-r--r--lib/file/readme!36
-rw-r--r--lib/file/rumors.txt201
-rw-r--r--lib/file/sample.txt5
-rw-r--r--lib/file/sfail.txt34
-rw-r--r--lib/file/silly.txt301
-rw-r--r--lib/file/smeagol.txt29
-rw-r--r--lib/file/smeagolr.txt5
-rw-r--r--lib/file/speakpet.txt53
-rw-r--r--lib/file/timefun.txt92
-rw-r--r--lib/file/timenorm.txt83
-rw-r--r--lib/help/TANG.txt134
-rw-r--r--lib/help/ability.txt115
-rw-r--r--lib/help/advanced.hlp15
-rw-r--r--lib/help/attack.txt148
-rw-r--r--lib/help/automat.txt504
-rw-r--r--lib/help/birth.txt593
-rw-r--r--lib/help/bldg.txt59
-rw-r--r--lib/help/c_alchem.txt135
-rw-r--r--lib/help/c_archer.txt68
-rw-r--r--lib/help/c_assass.txt58
-rw-r--r--lib/help/c_axemas.txt51
-rw-r--r--lib/help/c_bard.txt69
-rw-r--r--lib/help/c_demono.txt54
-rw-r--r--lib/help/c_druid.txt55
-rw-r--r--lib/help/c_geoman.txt59
-rw-r--r--lib/help/c_hafted.txt54
-rw-r--r--lib/help/c_lorema.txt54
-rw-r--r--lib/help/c_mage.txt67
-rw-r--r--lib/help/c_merch.txt29
-rw-r--r--lib/help/c_mimic.txt53
-rw-r--r--lib/help/c_mindcr.txt57
-rw-r--r--lib/help/c_monk.txt87
-rw-r--r--lib/help/c_necro.txt80
-rw-r--r--lib/help/c_palad.txt49
-rw-r--r--lib/help/c_polear.txt52
-rw-r--r--lib/help/c_posses.txt70
-rw-r--r--lib/help/c_pr_drk.txt57
-rw-r--r--lib/help/c_pr_eru.txt55
-rw-r--r--lib/help/c_pr_man.txt54
-rw-r--r--lib/help/c_priest.txt13
-rw-r--r--lib/help/c_ranger.txt55
-rw-r--r--lib/help/c_rogue.txt62
-rw-r--r--lib/help/c_runecr.txt110
-rw-r--r--lib/help/c_sorcer.txt68
-rw-r--r--lib/help/c_summon.txt80
-rw-r--r--lib/help/c_swordm.txt52
-rw-r--r--lib/help/c_symbia.txt68
-rw-r--r--lib/help/c_thaum.txt84
-rw-r--r--lib/help/c_unbel.txt65
-rw-r--r--lib/help/c_warper.txt60
-rw-r--r--lib/help/c_warrio.txt54
-rw-r--r--lib/help/command.txt1252
-rw-r--r--lib/help/corspoil.txt136
-rw-r--r--lib/help/debug.txt278
-rw-r--r--lib/help/def.aux3
-rw-r--r--lib/help/defines.txt639
-rw-r--r--lib/help/dungeon.txt706
-rw-r--r--lib/help/dunspoil.txt173
-rw-r--r--lib/help/essences.txt219
-rw-r--r--lib/help/experien.hlp28
-rw-r--r--lib/help/explore.hlp16
-rw-r--r--lib/help/fatespoi.txt28
-rw-r--r--lib/help/foot.aux4
-rw-r--r--lib/help/g_eru.txt65
-rw-r--r--lib/help/g_manwe.txt62
-rw-r--r--lib/help/g_melkor.txt64
-rw-r--r--lib/help/g_tulkas.txt45
-rw-r--r--lib/help/g_yavann.txt62
-rw-r--r--lib/help/gambling.txt29
-rw-r--r--lib/help/general.txt39
-rw-r--r--lib/help/gods.txt38
-rw-r--r--lib/help/head.aux10
-rw-r--r--lib/help/help.hlp33
-rw-r--r--lib/help/index.txt592
-rw-r--r--lib/help/inscrip.txt65
-rw-r--r--lib/help/lua.hlp34
-rw-r--r--lib/help/lua_gf.txt45
-rw-r--r--lib/help/lua_intr.txt133
-rw-r--r--lib/help/lua_mon.txt535
-rw-r--r--lib/help/lua_play.txt1225
-rw-r--r--lib/help/lua_pow.txt266
-rw-r--r--lib/help/lua_ques.txt299
-rw-r--r--lib/help/lua_skil.txt342
-rw-r--r--lib/help/lua_spel.txt2150
-rw-r--r--lib/help/lua_util.txt898
-rw-r--r--lib/help/luckspoi.txt112
-rw-r--r--lib/help/m_air.txt42
-rw-r--r--lib/help/m_convey.txt71
-rw-r--r--lib/help/m_demono.txt44
-rw-r--r--lib/help/m_divin.txt37
-rw-r--r--lib/help/m_earth.txt35
-rw-r--r--lib/help/m_fire.txt49
-rw-r--r--lib/help/m_geoman.txt75
-rw-r--r--lib/help/m_mana.txt39
-rw-r--r--lib/help/m_meta.txt75
-rw-r--r--lib/help/m_mimic.txt33
-rw-r--r--lib/help/m_mind.txt49
-rw-r--r--lib/help/m_mindcr.txt54
-rw-r--r--lib/help/m_music.txt77
-rw-r--r--lib/help/m_nature.txt53
-rw-r--r--lib/help/m_necrom.txt35
-rw-r--r--lib/help/m_symbio.txt50
-rw-r--r--lib/help/m_tempo.txt41
-rw-r--r--lib/help/m_thaum.txt31
-rw-r--r--lib/help/m_udun.txt35
-rw-r--r--lib/help/m_water.txt33
-rw-r--r--lib/help/macrofaq.txt2366
-rw-r--r--lib/help/magic.hlp41
-rw-r--r--lib/help/magic.txt143
-rw-r--r--lib/help/newbie.hlp16
-rw-r--r--lib/help/option.txt715
-rw-r--r--lib/help/r_beorn.txt33
-rw-r--r--lib/help/r_deathm.txt33
-rw-r--r--lib/help/r_drkelf.txt33
-rw-r--r--lib/help/r_dunad.txt32
-rw-r--r--lib/help/r_dwarf.txt39
-rw-r--r--lib/help/r_elf.txt32
-rw-r--r--lib/help/r_ent.txt39
-rw-r--r--lib/help/r_gnome.txt36
-rw-r--r--lib/help/r_hafelf.txt31
-rw-r--r--lib/help/r_hafogr.txt31
-rw-r--r--lib/help/r_hielf.txt34
-rw-r--r--lib/help/r_hobbit.txt37
-rw-r--r--lib/help/r_human.txt23
-rw-r--r--lib/help/r_kobold.txt31
-rw-r--r--lib/help/r_maia.txt22
-rw-r--r--lib/help/r_orc.txt35
-rw-r--r--lib/help/r_pettyd.txt30
-rw-r--r--lib/help/r_rohank.txt33
-rw-r--r--lib/help/r_thlord.txt32
-rw-r--r--lib/help/r_troll.txt34
-rw-r--r--lib/help/r_wodelf.txt37
-rw-r--r--lib/help/r_yeek.txt30
-rw-r--r--lib/help/rm_barb.txt38
-rw-r--r--lib/help/rm_class.txt14
-rw-r--r--lib/help/rm_herm.txt36
-rw-r--r--lib/help/rm_lsoul.txt26
-rw-r--r--lib/help/rm_skel.txt40
-rw-r--r--lib/help/rm_spec.txt44
-rw-r--r--lib/help/rm_vamp.txt32
-rw-r--r--lib/help/rm_zomb.txt39
-rw-r--r--lib/help/skills.txt539
-rw-r--r--lib/help/spoil_faq.txt73
-rw-r--r--lib/help/spoiler.hlp18
-rw-r--r--lib/help/tome_faq.txt381
-rw-r--r--lib/help/version.txt354
-rw-r--r--lib/help/whattome.txt30
-rw-r--r--lib/help/wishing.txt24
-rw-r--r--lib/info/delete.me1
-rw-r--r--lib/mods/mods_aux.lua185
-rw-r--r--lib/mods/modules.lua5
-rw-r--r--lib/module.lua36
-rw-r--r--lib/note/delete.me1
-rw-r--r--lib/patch/delete.me1
-rw-r--r--lib/pref/422color.prf909
-rw-r--r--lib/pref/colors.prf53
-rw-r--r--lib/pref/font-ami.prf28
-rw-r--r--lib/pref/font-dos.prf8
-rw-r--r--lib/pref/font-ibm.prf363
-rw-r--r--lib/pref/font-mac.new108
-rw-r--r--lib/pref/font-mac.prf18
-rw-r--r--lib/pref/font-win.prf298
-rw-r--r--lib/pref/font-x11.prf22
-rw-r--r--lib/pref/font-xxx.prf472
-rw-r--r--lib/pref/font.prf60
-rw-r--r--lib/pref/graf-ami.prf64
-rw-r--r--lib/pref/graf-dos.prf15
-rw-r--r--lib/pref/graf-ibm.prf6237
-rw-r--r--lib/pref/graf-iso.prf6875
-rw-r--r--lib/pref/graf-mac.prf15
-rw-r--r--lib/pref/graf-new.prf6844
-rw-r--r--lib/pref/graf-sdl.prf37
-rw-r--r--lib/pref/graf-win.prf16
-rw-r--r--lib/pref/graf-x11.prf37
-rw-r--r--lib/pref/graf-xxx.prf6345
-rw-r--r--lib/pref/graf.prf51
-rw-r--r--lib/pref/pref-acn.prf24
-rw-r--r--lib/pref/pref-ami.prf7
-rw-r--r--lib/pref/pref-emx.prf19
-rw-r--r--lib/pref/pref-gcu.prf70
-rw-r--r--lib/pref/pref-iso.prf118
-rw-r--r--lib/pref/pref-mac.prf243
-rw-r--r--lib/pref/pref-sdl.prf144
-rw-r--r--lib/pref/pref-win.prf534
-rw-r--r--lib/pref/pref-x11.prf413
-rw-r--r--lib/pref/pref.prf311
-rw-r--r--lib/pref/trap-iso.prf429
-rw-r--r--lib/pref/trap-xxx.prf428
-rw-r--r--lib/pref/user.prf50
-rw-r--r--lib/pref/xtra-gcu.prf34
-rw-r--r--lib/pref/xtra-new.prf1128
-rw-r--r--lib/pref/xtra-xxx.prf137
-rw-r--r--lib/save/delete.me1
-rw-r--r--lib/scpt/bounty.lua90
-rw-r--r--lib/scpt/corrupt.lua440
-rw-r--r--lib/scpt/drunk.lua21
-rw-r--r--lib/scpt/fireprof.lua480
-rw-r--r--lib/scpt/god.lua640
-rw-r--r--lib/scpt/gods.lua26
-rw-r--r--lib/scpt/help.lua411
-rw-r--r--lib/scpt/init.lua46
-rw-r--r--lib/scpt/intro.lua105
-rw-r--r--lib/scpt/joke.lua31
-rw-r--r--lib/scpt/library.lua513
-rw-r--r--lib/scpt/mimic.lua385
-rw-r--r--lib/scpt/mkeys.lua95
-rw-r--r--lib/scpt/player.lua76
-rw-r--r--lib/scpt/powers.lua61
-rw-r--r--lib/scpt/s_air.lua193
-rw-r--r--lib/scpt/s_convey.lua227
-rw-r--r--lib/scpt/s_demon.lua337
-rw-r--r--lib/scpt/s_divin.lua230
-rw-r--r--lib/scpt/s_earth.lua184
-rw-r--r--lib/scpt/s_eru.lua130
-rw-r--r--lib/scpt/s_fire.lua227
-rw-r--r--lib/scpt/s_geom.lua656
-rw-r--r--lib/scpt/s_mana.lua132
-rw-r--r--lib/scpt/s_manwe.lua144
-rw-r--r--lib/scpt/s_melkor.lua154
-rw-r--r--lib/scpt/s_meta.lua287
-rw-r--r--lib/scpt/s_mind.lua132
-rw-r--r--lib/scpt/s_music.lua443
-rw-r--r--lib/scpt/s_nature.lua152
-rw-r--r--lib/scpt/s_stick.lua444
-rw-r--r--lib/scpt/s_tempo.lua162
-rw-r--r--lib/scpt/s_tulkas.lua81
-rw-r--r--lib/scpt/s_udun.lua180
-rw-r--r--lib/scpt/s_water.lua154
-rw-r--r--lib/scpt/s_yavann.lua157
-rw-r--r--lib/scpt/spells.lua475
-rw-r--r--lib/scpt/stores.lua151
-rw-r--r--lib/scpt/test.lua364
-rw-r--r--lib/user/automat.atm0
-rw-r--r--lib/user/delete.me1
-rw-r--r--lib/xtra/ang16.bdf5017
-rw-r--r--lib/xtra/angband.fntbin0 -> 4096 bytes
-rw-r--r--lib/xtra/font/10X20.FONbin0 -> 8704 bytes
-rw-r--r--lib/xtra/font/12X24.FONbin0 -> 9728 bytes
-rw-r--r--lib/xtra/font/5X8.FONbin0 -> 4608 bytes
-rw-r--r--lib/xtra/font/6X10.FONbin0 -> 4608 bytes
-rw-r--r--lib/xtra/font/6X12.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/6X13.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/6X13B.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/6X9.FONbin0 -> 4608 bytes
-rw-r--r--lib/xtra/font/7X13.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/7X13B.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/8X13.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/8X13B.FONbin0 -> 5120 bytes
-rw-r--r--lib/xtra/font/9X15.FONbin0 -> 7168 bytes
-rw-r--r--lib/xtra/font/9X15B.FONbin0 -> 7168 bytes
-rw-r--r--lib/xtra/font/VeraMono.ttfbin0 -> 49224 bytes
-rw-r--r--lib/xtra/font/XM10X17.FNTbin0 -> 3286 bytes
-rw-r--r--lib/xtra/font/XM10X17B.FNTbin0 -> 3286 bytes
-rw-r--r--lib/xtra/font/XM12X20.FNTbin0 -> 3856 bytes
-rw-r--r--lib/xtra/font/XM12X20B.FNTbin0 -> 3856 bytes
-rw-r--r--lib/xtra/font/XM16X25.FNTbin0 -> 4806 bytes
-rw-r--r--lib/xtra/font/XM16X25B.FNTbin0 -> 4806 bytes
-rw-r--r--lib/xtra/font/XM5X8.FNTbin0 -> 1679 bytes
-rw-r--r--lib/xtra/font/XM6X12.FNTbin0 -> 2061 bytes
-rw-r--r--lib/xtra/font/XM6X12B.FNTbin0 -> 2059 bytes
-rw-r--r--lib/xtra/font/XM8X16B.FNTbin0 -> 2439 bytes
-rw-r--r--lib/xtra/font/xm4x6.fntbin0 -> 1489 bytes
-rw-r--r--lib/xtra/font/xm8x13.fntbin0 -> 2156 bytes
-rw-r--r--lib/xtra/font/xm8x13b.fntbin0 -> 2154 bytes
-rw-r--r--lib/xtra/font/xm8x16.fntbin0 -> 2441 bytes
-rw-r--r--lib/xtra/graf/16x16.bmpbin0 -> 1164238 bytes
-rw-r--r--lib/xtra/graf/16x16.pngbin0 -> 210021 bytes
-rw-r--r--lib/xtra/graf/8x8.bmpbin0 -> 203830 bytes
-rw-r--r--lib/xtra/graf/8x8.pngbin0 -> 44451 bytes
-rw-r--r--lib/xtra/graf/mask.bmpbin0 -> 1164342 bytes
-rw-r--r--lib/xtra/graf/tome-128.pngbin0 -> 45589 bytes
-rw-r--r--lib/xtra/music/delete.me1
-rw-r--r--lib/xtra/sound/Sound.cfg79
-rw-r--r--lib/xtra/sound/readme.txt33
-rw-r--r--src/.#dungeon.c.1.279.2.46140
-rw-r--r--src/.gitignore3
-rw-r--r--src/A-mac-h.pch104
-rw-r--r--src/ENGLISH.txt35
-rw-r--r--src/angband.h102
-rw-r--r--src/angband.icobin0 -> 766 bytes
-rw-r--r--src/angband.rc132
-rw-r--r--src/birth.c3920
-rw-r--r--src/bldg.c2273
-rw-r--r--src/carbon/Angband.icnsbin0 -> 66668 bytes
-rw-r--r--src/carbon/Carbon.r1568
-rw-r--r--src/carbon/Data.icnsbin0 -> 36416 bytes
-rw-r--r--src/carbon/Edit.icnsbin0 -> 35735 bytes
-rw-r--r--src/carbon/Image-DS_Storebin0 -> 6148 bytes
-rw-r--r--src/carbon/Info.plist39
-rw-r--r--src/carbon/Save.icnsbin0 -> 43952 bytes
-rwxr-xr-xsrc/carbon/getversion2
-rw-r--r--src/cave.c5266
-rw-r--r--src/cmd1.c5501
-rw-r--r--src/cmd2.c5266
-rw-r--r--src/cmd3.c2531
-rw-r--r--src/cmd4.c4808
-rw-r--r--src/cmd5.c2583
-rw-r--r--src/cmd6.c7948
-rw-r--r--src/cmd7.c7984
-rw-r--r--src/cmovie.c514
-rw-r--r--src/config.h584
-rw-r--r--src/defines.h4722
-rw-r--r--src/dungeon.c6149
-rw-r--r--src/dungeon.pkg1618
-rw-r--r--src/externs.h1920
-rw-r--r--src/files.c7219
-rw-r--r--src/gen_evol.c159
-rw-r--r--src/gen_maze.c297
-rw-r--r--src/generate.c9024
-rw-r--r--src/ghost.c1140
-rw-r--r--src/gods.c140
-rw-r--r--src/h-basic.h25
-rw-r--r--src/h-config.h318
-rw-r--r--src/h-define.h133
-rw-r--r--src/h-system.h141
-rw-r--r--src/h-type.h185
-rw-r--r--src/help.c23
-rw-r--r--src/init1.c12170
-rw-r--r--src/init2.c6891
-rw-r--r--src/irc.c297
-rw-r--r--src/iso/hackdef.h44
-rw-r--r--src/iso/readme.txt6
-rw-r--r--src/iso/simgraph.c1429
-rw-r--r--src/iso/simgraph.h159
-rw-r--r--src/iso/simsys.h197
-rw-r--r--src/iso/simview.c87
-rw-r--r--src/iso/simview.h41
-rw-r--r--src/iso/walls4.h288
-rw-r--r--src/iso/walls9.h288
-rw-r--r--src/iso/world_adaptor.c226
-rw-r--r--src/iso/world_adaptor.h117
-rw-r--r--src/iso/world_view.c499
-rw-r--r--src/iso/world_view.h49
-rw-r--r--src/lauxlib.h100
-rw-r--r--src/levels.c240
-rw-r--r--src/load_gif.c384
-rw-r--r--src/loadsave.c3977
-rw-r--r--src/lua/array.lua203
-rw-r--r--src/lua/basic.lua190
-rw-r--r--src/lua/class.lua85
-rw-r--r--src/lua/clean.lua74
-rw-r--r--src/lua/code.lua73
-rw-r--r--src/lua/container.lua311
-rw-r--r--src/lua/declaration.lua399
-rw-r--r--src/lua/define.lua72
-rw-r--r--src/lua/doit.lua73
-rw-r--r--src/lua/enumerate.lua93
-rw-r--r--src/lua/feature.lua72
-rw-r--r--src/lua/function.lua317
-rw-r--r--src/lua/lapi.c494
-rw-r--r--src/lua/lapi.h17
-rw-r--r--src/lua/lauxlib.c216
-rw-r--r--src/lua/lauxlib.h100
-rw-r--r--src/lua/lbaselib.c651
-rw-r--r--src/lua/lcode.c701
-rw-r--r--src/lua/lcode.h70
-rw-r--r--src/lua/ldblib.c188
-rw-r--r--src/lua/ldebug.c466
-rw-r--r--src/lua/ldebug.h21
-rw-r--r--src/lua/ldo.c385
-rw-r--r--src/lua/ldo.h33
-rw-r--r--src/lua/lfunc.c109
-rw-r--r--src/lua/lfunc.h24
-rw-r--r--src/lua/lgc.c353
-rw-r--r--src/lua/lgc.h18
-rw-r--r--src/lua/liolib.c710
-rw-r--r--src/lua/llex.c411
-rw-r--r--src/lua/llex.h72
-rw-r--r--src/lua/llimits.h204
-rw-r--r--src/lua/lmem.c150
-rw-r--r--src/lua/lmem.h42
-rw-r--r--src/lua/lobject.c125
-rw-r--r--src/lua/lobject.h204
-rw-r--r--src/lua/lopcodes.h168
-rw-r--r--src/lua/lparser.c1129
-rw-r--r--src/lua/lparser.h60
-rw-r--r--src/lua/lstate.c121
-rw-r--r--src/lua/lstate.h77
-rw-r--r--src/lua/lstring.c155
-rw-r--r--src/lua/lstring.h37
-rw-r--r--src/lua/lstrlib.c621
-rw-r--r--src/lua/ltable.c303
-rw-r--r--src/lua/ltable.h34
-rw-r--r--src/lua/ltests.c543
-rw-r--r--src/lua/ltm.c163
-rw-r--r--src/lua/ltm.h59
-rw-r--r--src/lua/lua.h248
-rw-r--r--src/lua/lua2c.lua29
-rw-r--r--src/lua/luadebug.h46
-rw-r--r--src/lua/lualib.h34
-rw-r--r--src/lua/lundump.c244
-rw-r--r--src/lua/lundump.h35
-rw-r--r--src/lua/lvm.c710
-rw-r--r--src/lua/lvm.h32
-rw-r--r--src/lua/lzio.c84
-rw-r--r--src/lua/lzio.h53
-rw-r--r--src/lua/module.lua69
-rw-r--r--src/lua/operator.lua111
-rw-r--r--src/lua/package.lua222
-rw-r--r--src/lua/print.h55
-rw-r--r--src/lua/tolua.c149
-rw-r--r--src/lua/tolua.h123
-rw-r--r--src/lua/tolua_bd.c214
-rw-r--r--src/lua/tolua_eh.c66
-rw-r--r--src/lua/tolua_eh.h24
-rw-r--r--src/lua/tolua_gp.c197
-rw-r--r--src/lua/tolua_lb.c160
-rw-r--r--src/lua/tolua_rg.c243
-rw-r--r--src/lua/tolua_rg.h22
-rw-r--r--src/lua/tolua_tm.c585
-rw-r--r--src/lua/tolua_tm.h32
-rw-r--r--src/lua/tolua_tt.c316
-rw-r--r--src/lua/tolua_tt.h31
-rw-r--r--src/lua/tolualua.c2975
-rw-r--r--src/lua/tolualua.h2713
-rw-r--r--src/lua/tolualua.pkg21
-rw-r--r--src/lua/typedef.lua59
-rw-r--r--src/lua/variable.lua192
-rw-r--r--src/lua/verbatim.lua77
-rw-r--r--src/lua_bind.c652
-rw-r--r--src/maid-x11.c894
-rw-r--r--src/maim-iso.c873
-rw-r--r--src/main-ami.c3250
-rw-r--r--src/main-cap.c1075
-rw-r--r--src/main-crb.c6450
-rw-r--r--src/main-dmy.c315
-rw-r--r--src/main-dos.c2417
-rw-r--r--src/main-emx.c1268
-rw-r--r--src/main-gcu.c1261
-rw-r--r--src/main-gtk.c5251
-rw-r--r--src/main-gtk2.c5412
-rw-r--r--src/main-ibm.c1594
-rw-r--r--src/main-lsl.c598
-rw-r--r--src/main-mac.c5504
-rw-r--r--src/main-net.c442
-rw-r--r--src/main-ros.c8670
-rw-r--r--src/main-sdl-iso.c1969
-rw-r--r--src/main-sdl.c2865
-rw-r--r--src/main-sla.c455
-rw-r--r--src/main-vme.c1198
-rw-r--r--src/main-win.c4486
-rw-r--r--src/main-x11.c3295
-rw-r--r--src/main-x11.c.orig3299
-rw-r--r--src/main-x11.c.rej17
-rw-r--r--src/main-xaw.c1993
-rw-r--r--src/main-xxx.c785
-rw-r--r--src/main.c1076
-rw-r--r--src/makefile.WHICH64
-rw-r--r--src/makefile.ami110
-rw-r--r--src/makefile.bcc222
-rw-r--r--src/makefile.bsd298
-rw-r--r--src/makefile.cyg433
-rw-r--r--src/makefile.dos169
-rw-r--r--src/makefile.emx205
-rw-r--r--src/makefile.gdb155
-rw-r--r--src/makefile.ibm165
-rw-r--r--src/makefile.lsl42
-rw-r--r--src/makefile.mingw431
-rw-r--r--src/makefile.my519
-rw-r--r--src/makefile.osx210
-rw-r--r--src/makefile.ros159
-rw-r--r--src/makefile.sdliso496
-rw-r--r--src/makefile.std566
-rw-r--r--src/makefile.wat71
-rw-r--r--src/makefile.win206
-rw-r--r--src/melee1.c3108
-rw-r--r--src/melee2.c7837
-rw-r--r--src/modules.c289
-rw-r--r--src/monster.pkg2324
-rw-r--r--src/monster1.c1998
-rw-r--r--src/monster2.c4099
-rw-r--r--src/monster3.c679
-rw-r--r--src/notes.c189
-rw-r--r--src/object.pkg1171
-rw-r--r--src/object1.c6866
-rw-r--r--src/object2.c6663
-rw-r--r--src/player.pkg3525
-rw-r--r--src/player_c.pkg1060
-rw-r--r--src/plots.c473
-rw-r--r--src/plots.h47
-rw-r--r--src/powers.c1412
-rw-r--r--src/q_betwen.c190
-rw-r--r--src/q_dragons.c149
-rw-r--r--src/q_eol.c194
-rw-r--r--src/q_evil.c116
-rw-r--r--src/q_haunted.c145
-rw-r--r--src/q_hobbit.c195
-rw-r--r--src/q_invas.c233
-rw-r--r--src/q_main.c176
-rw-r--r--src/q_narsil.c118
-rw-r--r--src/q_nazgul.c115
-rw-r--r--src/q_nirna.c111
-rw-r--r--src/q_one.c363
-rw-r--r--src/q_poison.c237
-rw-r--r--src/q_rand.c442
-rw-r--r--src/q_shroom.c291
-rw-r--r--src/q_spider.c110
-rw-r--r--src/q_thief.c172
-rw-r--r--src/q_thrain.c246
-rw-r--r--src/q_troll.c180
-rw-r--r--src/q_ultrae.c11
-rw-r--r--src/q_ultrag.c276
-rw-r--r--src/q_wight.c156
-rw-r--r--src/q_wolves.c128
-rw-r--r--src/quest.pkg170
-rw-r--r--src/randart.c496
-rw-r--r--src/readdib.c342
-rw-r--r--src/readdib.h21
-rw-r--r--src/script.c641
-rw-r--r--src/skills.c1795
-rw-r--r--src/spells.pkg2498
-rw-r--r--src/spells1.c9412
-rw-r--r--src/spells2.c8214
-rw-r--r--src/squeltch.c551
-rw-r--r--src/status.c778
-rw-r--r--src/store.c4531
-rw-r--r--src/tables.c4804
-rw-r--r--src/traps.c3426
-rw-r--r--src/types.h2551
-rw-r--r--src/util.c4873
-rw-r--r--src/util.pkg2735
-rw-r--r--src/variable.c1628
-rw-r--r--src/wild.c1316
-rw-r--r--src/wizard1.c2830
-rw-r--r--src/wizard2.c2065
-rw-r--r--src/xtra1.c4855
-rw-r--r--src/xtra2.c7795
-rw-r--r--src/z-form.c831
-rw-r--r--src/z-form.h47
-rw-r--r--src/z-rand.c355
-rw-r--r--src/z-rand.h89
-rw-r--r--src/z-sock.c787
-rw-r--r--src/z-sock.h127
-rw-r--r--src/z-term.c3137
-rw-r--r--src/z-term.h363
-rw-r--r--src/z-util.c231
-rw-r--r--src/z-util.h84
-rw-r--r--src/z-virt.c187
-rw-r--r--src/z-virt.h179
-rw-r--r--src/z_pack.pkg693
-rw-r--r--tome.ini94
714 files changed, 466200 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..7272d331
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.o
+*.~*
+*.#*
diff --git a/angdos.cfg b/angdos.cfg
new file mode 100644
index 00000000..d541b389
--- /dev/null
+++ b/angdos.cfg
@@ -0,0 +1,634 @@
+# File: angdos.cfg
+
+#
+# This file contains configuration data for Angband when compiled
+# (and run) with the "main-dos.c" file.
+#
+# It may also contain configuration data for the Allegro library,
+# which should (perhaps) precede the Angband configuration data.
+#
+
+
+#
+# Optional: graphics driver (Allegro)
+#
+# 1 = VGA mode 13h 2 = Mode-X
+# 3 = VESA 1.x 4 = VBE 2.0 (banked)
+# 5 = VBE 2.0 (linear) 6 = VBE/AF
+# 7 = Xtended mode 8 = ATI 18800/28800
+# 9 = ATI mach64 10 = Cirrus 64xx
+# 11 = Cirrus 54xx 12 = Paradise
+# 13 = S3 14 = Trident
+# 15 = Tseng ET3000 16 = Tseng ET4000
+# 17 = Video-7
+#
+### gfx_card =
+
+########### Sound-support settings ##########
+
+[sound]
+# Section containing sound configuration information, using the variables:
+# digi_card = x
+# Sets the driver to use for playing samples, where x is one of the values:
+# 0 = none 1 = SB (autodetect breed)
+# 2 = SB 1.0 3 = SB 1.5
+# 4 = SB 2.0 5 = SB Pro
+# 6 = SB16 7 = GUS (unfinished)
+
+# midi_card = x
+# Sets the driver to use for MIDI music, where x is one of the values:
+# 0 = none 1 = Adlib (autodetect OPL version)
+# 2 = OPL2 3 = Dual OPL2 (SB Pro-1)
+# 4 = OPL3 5 = SB MIDI interface
+# 6 = MPU-401 7 = GUS (unfinished)
+# 8 = DIGMID 9 = AWE32
+
+# digi_voices = x
+# Specifies the minimum number of voices to reserve for use by the digital
+# sound driver. How many are possible depends on the driver.
+
+# midi_voices = x
+# Specifies the minimum number of voices to reserve for use by the MIDI sound
+# driver. How many are possible depends on the driver.
+
+# flip_pan = x
+# Toggling this between 0 and 1 reverses the left/right panning of samples,
+# which might be needed because some SB cards get the stereo image the
+# wrong way round.
+
+# sb_port = x
+# Sets the port address of the SB (this is usually 220).
+
+# sb_dma = x
+# Sets the DMA channel for the SB (this is usually 1).
+
+# sb_irq = x
+# Sets the IRQ for the SB (this is usually 7).
+
+# sb_freq = x
+# Sets the sample frequency, which defaults to 16129. Possible values are:
+# 11906 - works with any SB
+# 16129 - works with any SB
+# 22727 - on SB 2.0 and above
+# 45454 - only on SB 2.0 or SB16 (not the stereo SB Pro driver)
+
+# fm_port = x
+# Sets the port address of the OPL synth (this is usually 388).
+
+# mpu_port = x
+# Sets the port address of the MPU-401 MIDI interface (this is usually 330).
+
+
+# Volume Settings:
+digi_volume = 0
+midi_volume = 0
+
+########### General Angband settings ##########
+
+[Angband]
+Graphics = 0
+Sound = 0
+
+Resolution = 2
+
+########### Screen Resolution 640 x 480 ##########
+
+[Mode-1]
+screen_wid = 640
+screen_hgt = 480
+
+Description =
+
+bitmap_wid = 8
+bitmap_hgt = 8
+
+bitmap_file = 8x8.gif
+
+num_windows = 3
+
+[Term-1-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 8
+tile_hgt = 13
+
+font_wid = 8
+font_hgt = 13
+
+font_file = xm8x13b.fnt
+
+[Term-1-1]
+
+x = 0
+y = 336
+
+cols = 80
+rows = 24
+
+tile_wid = 4
+tile_hgt = 6
+
+font_wid = 4
+font_hgt = 6
+
+font_file = xm4x6.fnt
+
+[Term-1-2]
+
+x = 320
+y = 336
+
+cols = 80
+rows = 24
+
+tile_wid = 4
+tile_hgt = 6
+
+font_wid = 4
+font_hgt = 6
+
+font_file = xm4x6.fnt
+
+
+########### Screen Resolution 640 x 480 ##########
+
+[Mode-2]
+screen_wid = 640
+screen_hgt = 480
+
+Description = with new graphics
+
+bitmap_wid = 16
+bitmap_hgt = 16
+
+bitmap_file = 16x16.gif
+
+graf-mode = new
+
+num_windows = 3
+
+[Term-2-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 8
+tile_hgt = 13
+
+font_wid = 8
+font_hgt = 13
+
+font_file = xm8x13b.fnt
+
+[Term-2-1]
+
+x = 0
+y = 336
+
+cols = 80
+rows = 24
+
+tile_wid = 4
+tile_hgt = 6
+
+font_wid = 4
+font_hgt = 6
+
+font_file = xm4x6.fnt
+
+[Term-2-2]
+
+x = 320
+y = 336
+
+cols = 80
+rows = 24
+
+tile_wid = 4
+tile_hgt = 6
+
+font_wid = 4
+font_hgt = 6
+
+font_file = xm4x6.fnt
+
+
+########## Screen Resolution 800 x 600 ##########
+
+[Mode-3]
+screen_wid = 800
+screen_hgt = 600
+
+Description =
+
+bitmap_wid = 8
+bitmap_hgt = 8
+
+bitmap_file = 8x8.gif
+
+num_windows = 3
+
+[Term-3-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 10
+tile_hgt = 17
+
+font_wid = 10
+font_hgt = 17
+
+font_file = xm10x17.fnt
+
+[Term-3-1]
+
+x = 0
+y = 408
+
+cols = 80
+rows = 24
+
+tile_wid = 5
+tile_hgt = 8
+
+font_wid = 5
+font_hgt = 8
+
+font_file = xm5x8.fnt
+
+[Term-3-2]
+
+x = 400
+y = 408
+
+cols = 80
+rows = 24
+
+tile_wid = 5
+tile_hgt = 8
+
+font_wid = 5
+font_hgt = 8
+
+font_file = xm5x8.fnt
+
+
+########## Screen Resolution 800 x 600 ##########
+
+[Mode-4]
+screen_wid = 800
+screen_hgt = 600
+
+Description = with new graphics
+
+bitmap_wid = 16
+bitmap_hgt = 16
+
+bitmap_file = 16x16.gif
+
+graf-mode = new
+
+num_windows = 3
+
+[Term-4-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 10
+tile_hgt = 17
+
+font_wid = 10
+font_hgt = 17
+
+font_file = xm10x17.fnt
+
+[Term-4-1]
+
+x = 0
+y = 408
+
+cols = 80
+rows = 24
+
+tile_wid = 5
+tile_hgt = 8
+
+font_wid = 5
+font_hgt = 8
+
+font_file = xm5x8.fnt
+
+[Term-4-2]
+
+x = 400
+y = 408
+
+cols = 80
+rows = 24
+
+tile_wid = 5
+tile_hgt = 8
+
+font_wid = 5
+font_hgt = 8
+
+font_file = xm5x8.fnt
+
+
+########## Screen Resolution 1024 x 768 ##########
+
+[Mode-5]
+screen_wid = 1024
+screen_hgt = 768
+
+Description =
+
+bitmap_wid = 8
+bitmap_hgt = 8
+
+bitmap_file = 8x8.gif
+
+num_windows = 3
+
+[Term-5-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 12
+tile_hgt = 20
+
+font_wid = 12
+font_hgt = 20
+
+font_file = xm12x20.fnt
+
+[Term-5-1]
+
+x = 0
+y = 480
+
+cols = 80
+rows = 24
+
+tile_wid = 6
+tile_hgt = 12
+
+font_wid = 6
+font_hgt = 12
+
+font_file = xm6x12.fnt
+
+[Term-5-2]
+
+x = 481
+y = 480
+
+cols = 80
+rows = 24
+
+tile_wid = 6
+tile_hgt = 12
+
+font_wid = 6
+font_hgt = 12
+
+font_file = xm6x12.fnt
+
+
+########## Screen Resolution 1024 x 768 ##########
+
+[Mode-6]
+screen_wid = 1024
+screen_hgt = 768
+
+Description = with new graphics
+
+bitmap_wid = 16
+bitmap_hgt = 16
+
+bitmap_file = 16x16.gif
+
+graf-mode = new
+
+num_windows = 3
+
+[Term-6-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 12
+tile_hgt = 20
+
+font_wid = 12
+font_hgt = 20
+
+font_file = xm12x20.fnt
+
+[Term-6-1]
+
+x = 0
+y = 480
+
+cols = 80
+rows = 24
+
+tile_wid = 6
+tile_hgt = 12
+
+font_wid = 6
+font_hgt = 12
+
+font_file = xm6x12.fnt
+
+[Term-6-2]
+
+x = 481
+y = 480
+
+cols = 80
+rows = 24
+
+tile_wid = 6
+tile_hgt = 12
+
+font_wid = 6
+font_hgt = 12
+
+font_file = xm6x12.fnt
+
+
+########## Screen Resolution 1280 x 1024 ##########
+
+[Mode-7]
+screen_wid = 1280
+screen_hgt = 1024
+
+Description =
+
+bitmap_wid = 8
+bitmap_hgt = 8
+
+bitmap_file = 8x8.gif
+
+num_windows = 3
+
+[Term-7-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 16
+tile_hgt = 25
+
+font_wid = 16
+font_hgt = 25
+
+font_file = xm16x25.fnt
+
+[Term-7-1]
+
+x = 0
+y = 610
+
+cols = 80
+rows = 24
+
+tile_wid = 8
+tile_hgt = 16
+
+font_wid = 8
+font_hgt = 16
+
+font_file = xm8x16.fnt
+
+[Term-7-2]
+
+x = 640
+y = 610
+
+cols = 80
+rows = 24
+
+tile_wid = 8
+tile_hgt = 16
+
+font_wid = 8
+font_hgt = 16
+
+font_file = xm8x16.fnt
+
+
+########## Screen Resolution 1280 x 1024 ##########
+
+[Mode-8]
+screen_wid = 1280
+screen_hgt = 1024
+
+Description = with new graphics
+
+bitmap_wid = 16
+bitmap_hgt = 16
+
+bitmap_file = 16x16.gif
+
+graf-mode = new
+
+num_windows = 3
+
+[Term-8-0]
+
+x = 0
+y = 0
+
+cols = 80
+rows = 24
+
+tile_wid = 16
+tile_hgt = 25
+
+font_wid = 16
+font_hgt = 25
+
+font_file = xm16x25.fnt
+
+[Term-8-1]
+
+x = 0
+y = 610
+
+cols = 80
+rows = 24
+
+tile_wid = 8
+tile_hgt = 16
+
+font_wid = 8
+font_hgt = 16
+
+font_file = xm8x16.fnt
+
+[Term-8-2]
+
+x = 640
+y = 610
+
+cols = 80
+rows = 24
+
+tile_wid = 8
+tile_hgt = 16
+
+font_wid = 8
+font_hgt = 16
+
+font_file = xm8x16.fnt
+
+########### Background settings ##########
+
+[Background]
+# Background-0 : Standard background
+# Background-1 : inven/equip
+# Background-2 : equip/inven
+# Background-3 : player (basic)
+# Background-4 : player (extra)
+# Background-5 : XXX
+# Background-6 : XXX
+# Background-7 : messages
+# Background-8 : overhead view
+# Background-9 : monster recall
+# Background-10 : object recall
+# Background-11 : XXX
+# Background-12 : snap-shot
+# Background-13 : XXX
+# Background-14 : XXX
+# Background-15 : borg messages
+# Background-16 : borg status
+
+Background-0 = backgrnd.gif
diff --git a/changes.old b/changes.old
new file mode 100644
index 00000000..dabf85a0
--- /dev/null
+++ b/changes.old
@@ -0,0 +1,6027 @@
+24/10/1998 - PernAngband 2.9.9a
+- Added the DragonRider from the Anne McCaffrey's Books, masters of
+ teleportation.
+- The CTRL+l key leaves the game without saving
+- Added the Tanker point, like the mana point, but can only be refilled
+ by eating the Firestone (for DragonRiders)
+- Added the RohanKnight race, fast and wise
+- Added the Ent race, can grow trees, but slow
+- Removed some races from Zangband which were not enough tolkienian...
+- Added the BeastMaster class which can summon pets
+- Added the Blood of Life
+- DragonRider now can choose to breathe a bolt, a beam or a ball
+- Added the Unique level patch from Glenn Vanzanden
+- Added the Mage Staves , with new ego items:
+ -of Mana : multiply your max mana
+ -of Spell : Increase your spell power
+ -of Mana & Spell : try to find !:)
+ -of Power : Activate for some spell, they carry 2 spells
+ All the ego mage staves have a penalty to damage and to hit
+- Added The Mage Staff of Gandalf, extremely powerful but even rarer than
+ the One Ring !
+- Added the boots of jumping activatable for phase door every 10+d10 turns
+- Added Helm of the Dwarves and of the Noldor
+- Beastmasters' pets now give kill experience to the player
+- Added the Alchemist class which use power batteries to convert magic item
+- New unique level : Treasure room at level 11
+- Replaced the Trump Hound from the Trump realm by the Trump Home spell, which
+ allows the player to access to her/his home from everywhere in the dungeon
+- Added the new artifact The Ring of F'lar, which belonged to F'lar the
+ powerful DragonRider
+
+26/10/1998
+- Added potions of Mutation
+- Warrior-Mage now can choose Sorcery instead of Arcane
+- Invisibility, Potion of Invisibility, Ring of Invisibility and
+ The One Ring now has invisibility like in the Lord Of The Rings !!!
+- Added the parchments from Kamband
+
+30/10/1998
+- Ported PernAngband to the new Zangband 2.2.0
+- Added the mimic class
+- Some Zangband bugs are fixed
+- The DragonRider can now only teleport on known terrain, because they must
+ mentally show the destination to their dragons but to do this they MUST
+ have seen the destination before (like in the Anne McCaffrey books) !
+- The Ringwraiths are back!
+- Some uniques like Bert, Tom, Bill are back, while others (Gandalf, Fangorn)
+ are gone!
+- Morgoth and Sauron are back as the final quests
+
+01/11/1998 - PernAngband 3.0.0
+- All reference to Zangband have been removed
+- Added monster corpses
+- Added the Beastmaster Shanty for the beastmaster, the Mimic Tower for the
+ Mimic, and the Weaver Tower, where you can ask for corpse quests
+- The corpse quests require you to bring back to the weaver some corpse,
+ head,... of a kind of monster, and you'll get a reward, like maybe
+ the full knowledge about this monster, or some armor made with the corpse
+ (maybe dragon scale mail for dragon, ...)
+- Added susceptibility to fire, for the Ents! They take twice more damage from
+ fire, but it's hopefully cancelled by resist fire.
+- Changed some artifacts / unique monsters to be more pernish
+- Added a new armor type : the DragonRider flying suit[9,+0] resist fire/cold
+- New monster flag : PET , the monster is your pet when it's created
+- Added the army man which use the PET flag will be used to give you an army
+ in some future quest.
+
+03/11/1998
+- The exe file is now pernang.exe(in the dos version)
+- Replaced the AMBERITE monster flag by the DRAGONRIDER flag
+- Some Unique will have the PET flag so they will help you !
+- All the Amberite are now gone and replaced by DragonRiders or uniques
+ (with some of them friendly)
+- Can sell the Mage staves in the Weapon smith and Magic shop.
+- Now when an alchemist tries to extract a power from an item, if he can't
+ it won't show up again in the extractable list the next time
+- The DragonRider must eat more because of their dragon
+- The Troll Fortress quest
+- Added The Battle of the five armies quest !!!
+
+05/11/1998
+- Nature spell : Stop breeders
+- New ego item sword of life, it multiplies your max hitpoints
+- Now some Mimic forms give you bonuses to blows per round like the Vala:
+ +5 blows !!!
+- Added a new Unique : The Philosophy Teacher !
+- Pink horrors are replaced with 2 Blue horrors when they die
+- Bloodletters of Khorne drop a blade of chaos when they die
+- Absorb light mutation bug fixed (I think)
+- Vampires and vampire mimics no longer can get food in the inns.
+- Renamed the artifacts with their real names
+- Replaced the pattern by the Straight Road (idea from GSN-Band)
+- Replaced the pattern weapons with weapons of Valinor (idea from GSN-Band)
+- Player and monsters can't pass through the trees (unless they can fly:)
+- When a Dragonrider dies he drops some firestones
+- Added the Whip of Gothmog, which can be dropped by Gothmog when killed
+- Begin to add a new feature : the between
+- Add a great description of the Ents and the Rohan's Knights from Akhronath
+
+07/11/1998
+- The between is fully implemented now.
+- The dimension door spell is replaced by the between gate.
+ It put a between gate at the player position and at the destination
+ location. And when you walk on the between you are transported on the other
+ between gate. But the between is SO cold that when you are in you lose some
+ hp even if resistant or immune to cold! Only the dragonrider can go between
+ without taking damage.
+- Stole the Stone of Lore from Oangband ! :)
+- Monsters can't use the between now because of a bug
+- Now you can eat corpses and temporarily gain some of the powers of monsters!
+- Raise Death replaces Terror in the death realm, cast it on a monster corpse
+ on the floor and the monster is brought back to life!
+
+09/11/1998
+- Added the boomerang, the player wears them instead of the bow and with the 'f'
+ command (like for the bow) can throw them but them come back ... if they don't
+ break !
+- Add 2 boomerang artifacts
+- Add another dragonrider artifact belonging to Mardra
+- Add The Anchor of Space-Time, it prevents disruption of the space-time
+ continuum
+
+11/11/1998
+- Add the carry slot in the equipment, to wield monsters that can't move :)
+- Fixed a bug that prevented dragonriders from being summoned
+- Add a Neuter sex (for the neuter players) :-)
+- Add the Death Mold race (stolen from Kamband) :-)
+
+13/11/1998
+- Add in spell description the bonus of mage staves
+- Add the scroll of summon never-moving pet for wielding them !:)
+- When a monster is wielded it can attack monsters when you attack
+ It can sometimes attack the wielder too, because he/she is not in perfect
+ symbiosis
+- Muar the balrog is back and will drop Calris when killed
+- Add the Symbiotic realm, like a life realm for your monster
+- Add the Symbiant class which can be in perfect symbiosis with their monster
+
+15/11/1998
+- The number of artifacts is now 132
+- Add some spells to the symbiotic realm
+- Add a spell to use the spells of the monster you wear
+- Add a light-speed (nearly stop time) spell
+- Finished the Symbiotic realm, only two books because they can use their
+ monster's powers.
+
+18/11/1998
+- Warriors can now spread their attacks after level 34
+- The experience factor of the races can now be > than 255
+- The DragonRider need more experience to advance (+220%) because it's a REALLY
+ powerful race.
+- Monsters can now use the between but if they are not immune to cold they
+ will lose some hp
+- Added a health bar for the monster you are carrying ( MH xxxxx/xxxxx )
+- Added sanity (from Kamband) ( the line SN xxxxx/xxxxx in the window)
+- Stolen the aquatic monsters from Kamband
+- Add the Gods from Kamband and GSNBand
+- The Philosophy Teacher now causes insanity !:)
+- Now only the Symbiants can carry monsters in their inventory, in fact they
+ act like "living-books" !:)
+- Add the Tactics from ADOM. You can change the tactic in the Character window
+- Add a new town unique : Mathilde, the science student, she is in my class
+ and giggles all the time, don't kill her ! :)
+- Fixed bug : The carried monster cannot absorb damage done by itself !
+- The Battle of the five armies now has a reward (The Arkenstone)
+
+20/11/1998 - PernAngband 3.0.2
+- Now there will be some between gates randomly placed in the levels
+- If you are in great favor with your god they can resurrect you
+
+22/11/1998
+- Add Random artifacts from Kamband - They are junk items which can be
+ activated every few turns and their activation is chosen randomly
+- Fixed (I think) a bug in the unique levels
+- Add a flooded level type (enable those levels in the PernAngband options)
+ to make the aquatic monsters more useful
+- Gandalf and Fangorn are back as Friendly uniques
+- Changed the Necklace of the Dwarves into The Nauglamir to be more
+ Tolkien-like
+- Changed The Serpent of the Chaos into Dark God, Mighty Coder from Hell :)
+- Reworked the corpse system (stolen from Rangband), now they all have
+ different weights based on the monster weight. You can hack up the corpse
+ with 'h' and cure it with 'K'.
+
+24/11/1998
+- Add a new Unique : The Physics Teacher !
+- Changed the Town layout of Gondolin with the one made by Akhronath
+ with 2 quests : Hunt for Eol and Maeglin's Mine
+- Add a new Unique : Eol the Dark Elf
+- Stole the silly messages when you hit a monster from Kamband
+
+26/11/1998
+- Add a scroll of Craftmanship to enchant weapon's pval.
+- The first 300 monsters have received a weight
+- Add a new spell type : Identify, now you can have a Ball of identification !
+- Add a new set of object flags (mainly unused now) :-)
+- Add a NEVER_BLOW flag for object
+- Add the NEVER_BLOW flag to The Mage Staff of Gandalf to rebalance it.
+- Add a new ego item weapon : of Nothing (with the NEVER_BLOW flag) !! :)
+
+29/11/1998
+- Add a Harper (bard) class !!! they use music books and most
+ of their spells are prolonged in time, ex: you cast a hiding song,
+ and for each turn you lose some mana (breath) while the effect (invisibility)
+ continues. If you start singing another song the old one is stopped.
+ There is also a spell which costs no mana which is used to stop singing.
+- Stolen the *Defender* ego-item from Pziband
+- Stolen the Spectral ego-item from Pziband
+
+01/12/1998
+- Added musical instruments : you wear them in the bow slot and you can
+ activate them for a song. The longer the song lasts the longer it'll be
+ charging, you can stop the song by activating it when it sing.
+- Finished the Harpers
+- Now the Harper mana stat is Charisma !
+- New Artifact : The Palantir of Minas Tirith, which can be activated for
+ the list of the uniques of the level !
+- Added 3 new artifacts : The Harp of Master Robinton, The Drum of Piemur,
+ The Flute of Menolly
+- Added the Micro$oft quest at Minas Anor
+- Changed the niceness description of the gods
+- The Harper and ONLY the harper can use a musical instrument without any
+ chance of failure.
+- The wilderness size is now taken from misc.txt
+- All the monsters have a weight now ... pfff, it was a lot of work !:)
+- The Alchemists are easier to play
+- Add the Vapor quest at Minas Anor
+- Add the Rebellion in Gondolin(from Akhronath) quest at Gondolin
+
+02/12/1998 - PernAngband 3.0.4
+- Changed certain god realm name on the suggestion of Akhronath
+- Add the bounties list like in Kamband (OK, ok, stolen from Kamband) :)
+- Add the Fire-lizards, they are friendly
+- New roguelike command : 'i' for hack up corpse, 'I' for curing meat, 'C'
+ for Sacrificing at altars
+
+05/12/1998
+- Add the Power Mage class, like the Corrupted in Kamband, with 100 randomly
+ generated attack spells
+- Corrected the character generation bug !!!! Thanks to Tim Baker for the fix
+- Corrected the segmentation fault bug for the object 653. Thanks to Tim baker
+- Corrected the files.c bug in get_line(). Thanks to Tim baker
+- Add the GF_DESTRUCTION type of attack to allow Power Mage to have destruction
+- Changed the names of the songs, Thanks to Akhronath
+
+07/12/1998
+- Add the Eggs, some monsters (only Firelizards for the moment) can be found
+ as eggs and will hatch after a certain amount of time based on the monster
+ weight, you can also stop the development by "activating" them ('A' key),
+ or resume the development by activating them an other time.
+- Add the last of the Gondolin's quest from Akhronath :
+ Invasion of Gondolin (Danger level 90, with some funny stuff like Gothmog,
+ Muar,..., some Great hell wyrms) !!!
+- Fixed a bug which made it so when you drop your wielded monster you also
+ drop Nothing if you are not a Symbiant
+- You can imprint some monsters (Firelizards for now), when it has
+ been given an imprint it will follow you on each levels. They may not
+ appear on a certain level but they will be back for the next. To
+ imprint a monster you must find an egg, carry it on you and when it'll
+ hatch you'll give it the imprint
+- The Harper will begin the game with a Blue Firelizard egg
+
+09/12/1998
+- The Black market now generate items based on your level
+- Add a new shop : The Pet Shop where you can find some eggs
+
+12/12/1998
+- Your godly favor will go down when they resurrect you
+- Repaired a stupid bug : I haven't coded the Satisfy Hunger effect of the
+ Symbiant class !:)
+- Add the easy floor feature !!!
+- Add an always small dungeon option
+- The scrolls of Artifact Creation can now be used on rings and amulets
+
+15/12/1998
+- The One Ring is now "just" heavy cursed !:)
+- Add 3 new vaults
+
+18/12/1998
+- Add the fate : you can be fated to meet some monsters or to find an object
+ on a certain level or ..... to DIE (very rare). You don't know your
+ fate until a soothsayer or a scroll of divination shows it to you. You
+also get a feeling when entering a level where you will meet your fate.
+ In the future there will be more fates, like finding a great vault, an
+ artifact or even, as Murazor the Witch-King of Angmar, to never die by the
+ hands of a mortal !:)
+- Changed Trump Weapon to Dragon Weapon
+- Smeagol will now drop ......... A ring of invisibility (because the One Ring
+ would be a little too powerful :)
+
+23/12/1998
+- Replaced the Amber notation in the player characteristics by the old one
+ from Vanilla Angband
+- When the player is on a tree he can be hit by a monster for half
+ normal damage
+- Paladin and Priests are given a religion at the beginning
+- In the wizard spoiler creation (A + ") you can chose to make a spoiler for
+ batteries
+- The random artifacts now have a charging time (no more mass genocide every
+ turn)
+- Add a Soothsayer at Bree
+- Add the batteries of extra life, you can now make a sword of Life !
+
+25/12/1998
+- The Alchemists are now better fighters, equal to the rangers
+- New extremely powerful power for the Alchemist (level 30 and higher) :
+ Artifact creation !
+ They just have to chose the flags, the name and to suffer the bad side effect
+ like curse equipment, permanent stat loss, high failure rate and ..... they
+ have their artifact ! But they can't give it an activation !
+ They can also add some bad flags (like cursed, drain exp, ...) to reduce the
+ chance of failure. Merry Christmas ! :)
+- Add the Runecrafter class, they use runes instead of books (see birth.txt)
+- Cleaned the Alchemist's code
+
+27/12/1998 - PernAngband 3.0.7
+- Added (.... stolen from Sangband) the Pick of Erebor. It can be activated for
+ passing through secret passages in the walls.
+- The Potion of *Enlightenment* now show every grid of the dungeon, like the
+ debug command 'u', I just think it's more beautiful :)
+- WOOOOOOOH I'm sorry there was a BIG bug in the PernAngband 3.0.7 release.
+ The game was sometimes hanging without any solution to stop it !
+ After some hours of debugging I've found it, it resulted from the correction
+ of the bug of the glyph of warding! Now everything works (I hope)
+- Change the titles of the Runecrafter with the proposition of Jonathan R Lewis
+- Add TANG, The Angband Newbie Guide made by Chris Weisiger
+ (jmartin@inreach.com) It's in the help directory
+
+29/12/1998
+- Fixed a bug which allowed beastmasters to sing when they summon pets!
+- Fixed the bug in the Fate screen
+- Some cosmetic changes in the r_info.txt and birth.txt with the help of
+ Chris Weisiger (jmartin@inreach.com)
+- Add new ego-items for the musical instrument and the horn, there were
+ suggested by Akhronath
+- New sentence to say for the speaking unique pets
+- 4 new musical instrument artifacts from Akhronath and I'm happy to declare
+ there is 143 artifacts in PernAngband!
+- Added, .... stolen, 5 new artifact from Angband/64
+
+31/12/1998
+- Add the quiver slot to wield your ammo
+- A scroll of remove curse have 1 chance into (55-level) to reverse the curse
+ effect ie: a ring of speed (-2) can become a ring of speed (+2)
+
+01/01/1999
+- Add a new monster flag : MORTAL, which means that the monster is a mortal
+ being. It's used for the new fate : Never to die by the hand of a mortal
+ being. The orcs are considered mortal, but I'm not sure, it's not mentioned
+ in the silmarillion or the Lord of The Rings, the trolls are immortals along
+ with the dragons, BUT the Dragonriders are mortal and Dark God (Yes, let your
+ joy explode!) :-). But well, don't be afraid your preferred monsters
+ (Morgoth and Sauron) are immortals :-)
+ PS: If someone thinks that a monster should or not be mortal just email me
+ because I'm not certain for some (ok, ok a lot of) monsters
+
+03/01/1999
+- Add the ring of precognition which work as if the player had activated the
+ cheat mode (peek into object, monster, vault creation). There is no need
+ to say it's very rare :-)
+- Add the black breath concept from Tolkien. Beware now the weapons of morgul
+ they confer the black breath! The undead can also give it.
+
+05/01/1999
+- Changed the harpers ranking names, some racial histories with the ones from
+ Akhronath
+- The One Ring is now A LOT more powerful, because at the time you find it
+ you already have one or two immunities, your stats are near the max, plus
+ some other things that made it less useful. Now it confers 5 blows, mana x5
+ spell power x5, after all Sauron has put the most part of his power in it.
+ I also plan to change the activation.
+- Add 2 wand artifacts, The Wand of Stone to Mud of Thrain and the Wand of
+ Fire Balls of Mithrandir, they can be recharged at will and will never be
+ destroyed, for this purpose the new RECHARGE flags has been used (note :
+ it works only for the artifacts)
+
+07/01/1999
+- The runecaster is a little more playable at low levels and starts with a
+ Rune [Arrow] and a Rune [Fire]
+
+10/01/1999
+- The Chaos Warriors now get chaos resistance at level 25
+- The Maia mimics get +7 speed, the Vala mimics get +15 speed
+- Updated the Rebellion in Gondolin and Hunt for Eol quests with the help of
+ Akhronath (zzhou22876@aol.com)
+
+13/01/1999
+- Reimplemented the old magic system from vanilla to complement the new one
+ The old mage and priest are come back as the Wizard and the Prior
+
+17/01/1999
+- Add susceptibilities to fire, cold, acid, lightning, poison for the monsters.
+ ie: Now a white dragon will take a x3 damage from a fire attack.
+ Thanks to Jerome Wojcik <Jerome.Wojcik@lmcp.jussieu.fr> for his help.
+- Add the pets command (press P) from Zangband 2.2.3
+- Changed the Trump realm into the Dragon Realm (mainly just name changes)
+
+19/01/1999
+- Changed the activation of the One Ring
+- The One Ring is now Permanently cursed
+- The One Ring is no longer aggravating because of it's new summoning ability
+- It's now possible to run through the grass
+
+21/01/1999
+- Add the persistent dungeon option at birth
+- When a breeder multiplies it have a 7/100 chance of mutating into an other
+ monster. (in general a worse one) This would prevent monster farming abuse :)
+
+24/01/1999
+- NEW : the body changing feature! with a new class to use it. Your spirit
+ can leave your body to go into another one but while your spirit is alone
+ your max hp is of 1. When in the corpse of a monster your first blows are
+ the monster's one and you can use it's magical power. Your life is also
+ changed by the monster's one and you gain all it's abilities and resistances
+ while losing those of your real race because you have left your body.
+- Add a lot of new vaults mainly from Weisiger <jmartin@inreach.com>
+
+28/01/1999
+- Death Molds now can teleport onto store entrances
+- A new randart activation that fires light after absorbing the ambient one
+
+05/02/1999
+- Changed the Roguelike commands. Now ^G to sacrifice, $ to hack up a corpse
+ ^O to Cure meat
+- Updated the command.txt file
+- Add the AB's gfx for the weapon/armor
+- Multiple dungeons : The old levels 1-33 are now in the Mirkwood forest with
+ trees and grass, 34-66 are the Land of Mordor with mountain, wall and dirt
+ 67-127 are the Dungeon of Angband with wall and normal floor and occasionally
+ flooded levels (if chosen in the options).
+ The ability to levitate is now a GREAT advantage in the Mirkwood forest !
+ You can reach Mirkwood from Menegroth, Mordor from Minas Anor and Angband
+ from Gondolin. In the vanilla town there are three stairs.
+ When you take a staircase down in the last level of a dungeon you arrive back
+ in town.
+
+07/02/1999 - PernAngband 3.0.9
+- X in the roguelike mode for the pet commands
+- Updated the magic.txt file with the new classes
+
+09/02/1999
+- Updated version.txt, modified dungeon.txt
+- Shelob is now a little bit more powerful in order to make her the quest
+ monster of the last Mirkwood level.
+- The Vanilla books are now sellable in the magic shop/temple and the bookstore
+
+13/02/1999
+- New class : the Sorcerer, they can't wield any weapon useless it's a Mage
+ Staff and without it they are the WORST fighters of the game even bare-handed.
+ But to compensate this they are the BEST magical class : the can use any
+ book of any realm without having to learn the spell, but they also don't
+ gain any experience from casting a spell.
+- Added the susceptibilities flags to the monsters.
+
+15/02/1999
+- Stolen the exploring system (like the tactic one) from Angband/64
+- Fix the bug of vanilla town, now the vanilla town flag is saved
+- Ents are rebalanced, they have a -9 speed penalty
+- Tweaked the Troll Fortress quest to make it less easy
+
+21/02/1999
+- Corrected some bugs in the town files. The crashing wilderness bug
+ seem to be gone.
+
+23/02/1999
+- Fixed the bug of the traps. No more grey "invisible traps" on a level
+ covered with grass, same for the dirt
+- Updated the Gondolin town and quest with the Akhronath's ones
+- Enabled the S-lang support
+- A new script directory is in lib, it contain a file script.sl which indicate
+ every S-lang script files that must be loaded
+- Add the event's gestion to the slang script. An event is produced in certain
+ conditions (like a keypress,...) and a script can put an handle on it to have
+ a function called each time the event happens. I've made a stupid example
+ that use the key 'y' to shout at the monsters.
+ PS: Take a look at slang.txt in the help directory
+- Add the load/save function to the script
+
+24/02/1999 - PernAngband 3.1.0
+
+26/02/1999
+- I've finally successfully compiled Python. So the S-Lang support is replaced
+ with the python one. The 'y' key and the thieves quest are there too.
+- Stolen and modified intro.py from Pangband
+- The random quest bug is fixed (maybe :) ). I say maybe because as for the
+ wilderness one it doesn't happens by me so I can't test it :(
+- Today one of my dream have become true! With the help of a python script
+ I've made a new quest for the DragonRiders that can be given at the Weyr
+ of Arda in Menegroth. You enter forest area and the only thing you can use
+ is the flame of your dragon(no magic, no wands, ...). And you need to
+ destroy every thread while saving at least 50% of the trees, and your own
+ flame can consume them!
+
+28/02/1999
+- Yeah, the wilderness bug is now gone. And a minor bug which looks like the
+ the wilderness one is gone too. It happened when you try under certain
+ conditions to go in the wilderness with an imprinted pet.
+- Fixed the random quest bug
+- Fixed the inventory bug that when picking up an object said you have foo
+ in slot r and reorder it after that o the letter is no more r. I know it was
+ really annoying but well it's now gone :)
+- The undead races now start at night
+
+02/03/1999
+- The spectres can now pass through trees and mountains
+- A new dungeon type at Bree: The Upper reaches of Galgals(level 1-10) !
+- Changed the code for the different dungeon types. Now it's a lot easier to
+ add a new dungeon type. I'll maybe make a d_info.txt to define the dungeon
+ types
+- Add a new dungeon: The Volcano, you reach it by going to the north east of
+ Minas Anor. Level 30-45
+
+05/03/1999
+- Spectres can't pass through trees because trees are pure and natural
+ things :)
+- New dungeon type: The Hell !!! Level 500 to 530, Yes 530 ! With a little
+ present for you at the bottom... if you manage to survive :) I can just
+ said one more thing: don't go there with your level 1 character :)
+ You can reach it near the volcano hole.
+- Now fire balls can burn the trees
+- Invisibility now consume a lot of food
+- Fixed a strange bug with the pool of deep water... It was possible to
+ sacrifice at and to worship (before the crash:) one :). Thanks to Steve Dice
+- Add a very nasty surprise at level 1.... Moldoux, the defenceless mold with
+ 1 hp no powers, can't move, can't attack... Oh and if it's killed there just
+ one Great Wyrm of Power which can be summoned to avenge it !
+
+07/03/1999
+- The look command doesn't stop anymore on the trees, grass, ...
+
+07/03/1999 - PernAngband 3.1.2
+
+13/03/1999
+- Sorcerors have now a bad pseudo-id
+- Sorcerors have now a -25% penalty of hp
+- Add a scroll of deincarnation
+- Add the possibility to engrave the floor with some magic words found in some
+ parchments. The words are in Adunaic(Numeronean), Quenya, Sindarin or any
+ other tongues of Middle Earth. But the parchment also give useless words
+ with the useful ones, so you have to try different combinations.
+- No more "human" sex when randomly chosen
+
+16/03/1999
+- Mathilde will say the right sentences now
+- Fixed a bug in melee2.c which could produce an x and an y out of the limits
+- Disabled the engraving command for now because it's too unbalancing...
+- If you pray while low on hitpoints (< 20%) your god will do something to help
+ you, like summoning pets, curing wounds, killing enemies...
+
+19/03/1999
+- There is now a minimum level needed to enter some dungeons, to prevent
+ gaining great objects easily
+- Fixed the OLD bug of the pink horrors, now only 2 blue horrors
+ will be summoned
+- Fixed a bug with the tiles for the non unique DragonRiders
+
+21/03/1999
+- The Dragon realm users receive some experience for the kills of their pets
+- The Palantir of Minas Tirith now can teleport you next to the quest monster
+ but it'll take more time to recharge
+- Fixed the possessor's number of blows bug. Also the town uniques corpses (
+ Maggot, Martti, Mathilde) can't be used to attack because their attack is
+ moan with no damage
+- Added a faq (see pern_faq.txt)
+- Added a new monster ally, the Dolphiner. They came from the planet Pern
+ and can summon dragonriders, their friends. They are men riding dolphins
+
+******************** Attempts to rebalance the game **************************
+- Changed the xp modifiers of the new races in an attempt to make them more
+ balanced. Thanks to Shawn Cheng <mfighter@hotmail.com>
+- Invulnerability, Wraith Form and Life Multiplier now consume a *lot* of food
+- RohanKnight's gain less speed at each levels
+- Sound attacks of the harpers are less likely to stun...
+- IMPORTANT NOTE: At the character birth if you choose a forbidden class for
+ your race, you'll be considered as a cheater and the game won't be scored.
+ It's mainly to prevent the too powerful combination of Deathmold Mimic or
+ Deathmold Possessor. The characters made before the next release won't be
+ affected by that
+- Some more attempts that I don't remember :)
+- Changed the Mana and life multipliers, now mage staff of mana(100%) will
+ ADD 100% of mana to your max mana and not multiply it by 100 :)
+******************************************************************************
+
+23/03/1999
+- Replaced Menegroth by the new Lothlorien skilfully made by Akhronath
+ (zzhou22876@aol.com), with 2 new quests + The Battle of the Five Armies +
+ (only for DragonRiders) The Fight against a Threadfall
+ Thanks again Akhronath !
+- Deathmolds can now enter wilderness
+- Radically changed the score calculation, in the future it'll serve to choose
+ which characters must be turned into monsters. Note that the old score files
+ are compatible but the old scores mean nothing with the new system
+
+26/03/1999
+- Changed some aspect of the python implementation to make it more portable
+- The usleep python function may react strangely
+- When you pick up the same type of ammo that you wield in your quiver it
+ automatically combine with it
+- The specials levels are now restricted to a certain depth AND a certain
+ dungeon type
+- The number of collected bounties now count for the final score
+- Add a new option to not pick up monster's corpses. It's yes by default.
+ I've made it to avoid losing good characters by auto picking up a great
+ wyrm of power corpse and getting -500 speed !
+
+28/03/1999
+- Reimplemented the player ghosts, but in a different way. First the bone files
+ are a lot more complex to include more aspects of the dead character. Second,
+ only the best characters are allowed to become ghosts when they die, or commit
+ suicide, it's based on their final score. In the bone directory there is a new
+ file which held the name of the bone files that the player want to be used.
+ To add a bone file, put it in the bone directory, add it to the bones.txt file
+ and increment the number of bone file in the same file.
+ When a game is loaded it looks for this file, try to find some unallocated
+ ghosts and modify r_info with the new info from the ghost. When this is done
+the bone file is not used anymore because all the info is saved in the
+ player savefile. The race/class characteristics are not fully handled now.
+- Now even with a Mage Staff the Sorcerors have a penalty of -10 to hit/dam
+- A Vampire can now wield the Phial of Undeath without being scorched, while
+ all the other light artifacts scorch them
+
+30/03/1999
+- The Arcane Zap spell now works correctly
+- Updated the FAQ with the help of Leon Marrick
+- Updated magic.txt with the help of Leon Marrick
+- WOH ! Impressive ! Great work indeed ! Well, Andreas Koch has made a
+ powerful graphic editor for PernAngband !!!! And it has also drew some new
+ tiles! The editor is really impressive, you load the graphic file, the prf
+ file and you can assign a tile to every monsters/objects/... you want, just
+ with a click ! It can even scan the *_info.txt to search for new things that
+ are not in the current prf file and to add them !
+ You just need a big screen resolution(1152x864x16bits works for me)
+ Thanks for the great work Andreas(akoch@rbg.informatik.tu-darmstadt.de) !
+- The Archer class as suggested by Mike Hommel <jamul@hamumu.com>. They gain
+ some extra shot and might with levels and can also create ammo with rubble
+ and junks.
+
+05/04/1999
+- Fixed a bug in the Weyr of Arda at Lothlorien
+- Add the artifact's descriptions made by ... well I don't have his/her name.
+ Anyway, great work.
+- The rings of teleportation can now be activated but they'll be destroyed in
+ the process. It's like a last chance option :)
+
+08/04/1999
+- The uniques used in the special levels are no longer shown as killed in the
+ list and can only be created in the special level.
+ Note : This will only affect the new characters.
+- Fixed : When a Power Mage spell fails it is not cast anyway.
+- Wall creation works for Power Mage
+- Divia wrote some missing artifact descriptions
+
+12/04/1999
+- Reimplemented the engraving feature. There are some parchments in the
+ dungeon which contain Adunaic/Quenya/Sindarin/... words you can use to
+ engrave the floor with 'x'(']' in the roguelike keyset). You must read them
+ before engraving even if you know the words from your last game. Also the
+ inscription is magical and so needs some mana to work. So there is a new
+ and unique feature : each grid in the dungeon have some mana on it's own.
+ You can sense the mana of one grid by pressing 'X'('[' in the roguelike
+ keyset), but this is not very precise nor easy, it's based on the magical
+ ability. When an inscription is used it decrease the grid's mana and when
+ there is not enough mana the inscription don't work anymore. The maximum
+ mana of each grid is 255. i.e.: when you engrave the protection inscription
+ which use 8 mana on a grid with 80 mana, a monster won't be able to walk on
+ it for 80/8=10 turns, but well, this can save your life !
+ Note : There are not a lot of inscriptions, if you have some ideas, don't
+ hesitate, email me !
+
+14/04/1999
+- Changes in the r_info for the HAS_EGG flag as suggested by Akhronath
+- Bloodletters of Khorne only have a 20% chance of dropping Blade of Chaos
+
+17/04/1999
+- Raal's Tomes of Destruction have a 20% chance of dropping a Raal's Tome of
+ Destruction
+- Symbiants are more likely to receive the grow molds mutation
+
+20/04/1999
+- Quest monsters can not be pets or breeders
+- Can now place Trees(T), Mountain(M), Shallow & Deep lava(l, L) and
+ Shallow & Deep water(w, W) in the special levels
+- Add a new dungeon type: The lost city of Numenor which is mainly composed
+ of water and which can be found to the west of Bree
+********************* IMPORTANT *************************
+- The savefile from the old versions of PernAngband are no longer compatible
+ with the new one. It's mainly due to the new magic system but also some other
+ changes. I'm sorry but this would be hard to write a converter since all
+ the realm change (they have more spells) and so the books too and also the
+ way to remember which spells are known, forgotten, tried...
+*********************************************************
+
+22/04/1999
+- Some new graphics from Andreas Koch
+- Fixed the wilderness generation bug which sometimes hung the computer when
+ trying to go on a wilderness border
+- The features(in f_info.txt) have some flags now. This allows me to create 2
+ new types of wall: The Glass walls which you can see through but that you
+ can't pass and the Illusion walls that you can pass but not see through
+- The screen is now correctly redrawn when leaving/entering a body
+- Add the Wands of Wall Creation
+- Death Mold powers have moved to the activation command('U')
+
+24/04/1999
+- Your are no longer crushed when flying over trees/mountain without being a
+ DragonRider
+- The Vanilla feelings are back
+
+26/04/1999
+- All the spellbooks are now placed on the top of the inventory.
+- The Sorcerors can now use the Wizard and Prior books
+- The new magic system is ready. I'm just waiting for Akhronath and Shawn
+ Cheng. And now adding a new realm or an old fashioned magic system is
+ performed the same way and easily
+- Add the Illusionists from Kangband. Someone has asked for them so... :)
+
+28/04/1999
+- Finished the Valarin Realm
+- Between gates are now allowed in the vaults. To place them place 2 times a
+ number from 0 to 7. I.e.(from weisiger <jmartin@inreach.com>):
+ D:XXXXXXXXXXXXXXXXXXXXXXXXX
+ D:#,,,,,X&...@X..**&X....9X
+ D:#...&1X.,,,2X..**&X3...9X
+ D:#...&.X1,,,.X2.**3X....9X
+ D:#,,,,,X&...@X..**&X....9X
+ D:XXXXXXXXXXXXXXXXXXXXXXXXX
+ But BEWARE ! You MUST put the 2 occurrences of the number, no check are done
+ so it'll surely crash or bug if not ! You've been warmed.
+- You can now walk over the water with a Valarin spell
+
+30/04/1999
+- A monster on a between gate when the player is using the other is teleported
+ to the player location
+- Add some Between vaults from weisiger
+- Added the Sigaldry realm
+- The Rings and Amulets are now allowed to be random artifacts
+- You can use the between gates in the special levels via the numbers 4,5,6,7,
+ 8,9,0
+- Alchemists MUST wear gloves (no cesti nor gauntlets) in order to do alchemy.
+ They also begin the game with some gloves
+- Add a Potion of Learning which allow you to learn one more spell than you
+ normally can. No need to say that they are very rare
+- The more you learn spells in a realm the more efficient with that realm
+ you are
+
+02/05/1999
+- Implemented the Nether Realm
+
+09/05/1999
+- Magical stair at the bottom of each dungeon
+- Reworked the imprinted pet system, now it will work lot better
+- Add the scrolls of spell which can cast a spell like a book. They must be
+ created by the Sigaldry magic, they can't be randomly generated
+- Updated the Sigaldry realm
+- Added Staves of Wishing! You can wish for objects, monsters, spells but
+ NOT FOR ARTIFACTS nor for staves of wishing
+- Flying monsters can now pass over trees and mountains
+
+11/05/1999
+- The game will ask you to choose ammo from your inventory if your quiver is
+ empty
+
+26/05/1999
+- Add the Crusade Realm
+- Add some new inscriptions
+- It's now possible to add up to 32 realms
+
+03/06/1999
+- Wraith form is less powerful since it's not so rare. Now damage is only
+ divided by 5 and armor raised by 50
+
+05/06/1999
+- Add the speaking patch from Matt Graham. Just edit the monspeak.txt file
+ to add new entries. And don't hesitate to send me what you've added !
+
+11/06/1999
+- There are fewer gods and each god has one or two preferred races and if one of
+ this race pray to him they gain better benefits. Also the praying effects
+ are more differentiated (based on the different gods)
+- Add the 'only once' quest type
+- It's harder to please your deity
+- The Nazguls are MUCH more powerful (ideas from Akhronath):
+ ******** Quote from Akhronath mail ********
+ All weapons which are not ego-items cannot harm them, and will be destroyed if
+ the character hits a Nazgul with it. Ego-items which have slay evil or slay
+ undead can harm them, but each hit has a 25% chance of destroying the weapon.
+ Other ego-items can't damage them, and have the same 25% chance of
+ destruction. An artifact weapon will be able to hit them only if the weapon
+ has slay evil or undead, but the weapon will be hit with disenchantment every
+ time it connects (but this is negated with disenchantment resistance on the
+ character). And even these artifacts can be destroyed, although there's only a
+ 5% chance. Other artifacts, of course, can't damage the Nazgul, and still have
+ the 5% destruction chance.
+
+ When a character does any damage to a Nazgul or is damaged by a Nazgul, he has
+ a 25% chance of being afflicted by the Black Breath. Thenceforth the character
+ will slowly be drained of experience and sanity. The character can stay alive
+ by using restoring and curing potions, but to cure the Black Breath he must
+ either drink a Potion of Life or eat (a new item) Athelas Leaf. (Athelas isn't
+ really eaten, of course, but I can't think of another simple way to implement
+ it.)
+
+ This will really make the player think twice before going up to a Nazgul, and
+ makes it very useful to carry spare weapons.
+ *******************************************
+ So if you encounter a Nazgul .... RUN AWAY !
+- Sensing the grid's mana is now done by pressing 'x' key to engrave the floor
+ The X key is free for some new function (maybe for the new riding system)
+
+17/06/1999
+- When browsing the spells while being a Power Mage there are no more bugs
+ when pressing escape
+- Removed the forbidden race/class combination feature. But it's always
+ possible to recompile PernAngband with it with the FORBID_BAD_COMBINAISON
+ define(in config.h)
+- New god : The RNG
+
+19/06/1999
+- The priest/prior/paladin's god is now chosen dependent on their race
+- I have enough of hunting those damn ghost bug which makes them unable to
+ move. So it's now a feature ! They don't move because they are GREAT
+ adventurers who now just want to stand there to find peace :)
+ Hey, fixing bugs is easy this way :))
+
+22/06/1999
+- When 'A'ctivating the first screen showed is the equipment and no more the
+ inventory
+- Some new gfx drew by Andreas Koch
+
+06/07/1999
+- You can place object kind in the special levels instead of artifacts by
+ using 1000+k_idx as the number instead of just the artifact index
+
+17/07/1999
+- Add the Yeek race, but not as in Zangband. It's the one from Drangband.
+ I was thinking of a race who advance even quicker than the humans in levels
+ since a long time and I've found it !
+ So they suffer lots of disadvantages but need less exp than the other
+races, i.e.: When a DragonRider comes to level 2 a Yeek can already be at
+ level 7 !!
+- Added the FLY attribute, it allows you to fly over trees and mountains
+ because it's no longer possible with the levitation one. It's also a lot
+ rarer since it's POWERFUL in Galgals and Mirkwood. DragonRiders have it
+ by default. There is also a change in the features flags, CAN_LEVITATE
+ means that you can just avoid them by floating over it (like a pit,
+ water, ...) and CAN_FLY means that you must have the FLY flag to cross it.
+
+19/07/1999
+- The monster's drop are now created at the same time as the monster is
+ created. First it disallows scumming of monster drops. And the most
+ important thing is that it will allow me to add the new ability of stealing
+ objects from monsters (everyone will be able to do it, but only Rogue will
+ be excellent at it). For the point of view of the player there is no change.
+ It may produce some strange object disappearance in the late game since
+ all the objects are generated with the dungeon. And thus it may need to
+ increment the maximum size of o_list in misc.txt. If such problems shows
+ up, email me.
+- Press 'Z'('[' in roguelike mode) to STEAL an item from a monster. The
+ success rate is based on your dexterity, the fact that you are or aren't
+ a Rogue (being one makes it easier), the weight of the object to steal and
+ the base level of the monster. A Rogue will also gain some experience
+ if he/she succeeds. This made them more Rogue and less Warriors.
+
+22/07/199
+- Add more graphics from Andreas Koch along with a new version of the editor
+
+26/07/1999
+- The resurrection effect of the gods is now harder to get and decreases a lot
+ more your grace. It was ... well ... too powerful for the cost of some
+ crappy artifacts
+- Finished the Magery realm created by Chris Weisiger (jmartin@inreach.com)
+
+03/08/1999
+- I've finally fixed it ! You know, this ugly bug that disallows the darkening
+ of the grass & mud tiles when they have been lit.
+
+18/08/1999
+- Yeah ! It's done! The new magic system is finished (I will add Spirit Realm
+ later). The final form is 4 Major Realms : Valarin, Nether, Magery, Shadow
+ and 4 Minor Realms : Crusade, Tribal, Sigaldry, Illusion.
+ The only problem is be that it could be unbalanced, especially the
+ Shadow realm since I've created it :)
+ Anyway it should be great. There are still a lot of old spells but some new
+ coloured funny spells for you to discover, as Control the Ring, Infuse Amulet,
+ Scribe Scroll, Doppleganger, Volcano Flow, Tidal Wave, and a lot of others!
+ The max number of spells a character can learn depends on his class. But
+ generally the player can learn fewer spells than the total number of spells he
+ has in all the books he can read.
+ I want to thank Akhronath (who has the first idea and who write some realms),
+ Shawn Cheng, Chris Weisiger and DSCreamer<DSCreamer@aol.com> for their great
+ help.
+- Moldoux can not appear at any other level than level 1
+
+21/08/1999
+- A new version of the Tile Editor plus a full set of graphs (expect the books)!
+- Moved the dungeon types definitions from tables.c to a new info file in the
+ lib directory: d_info.txt
+ It allows you to add new dungeon types easily.
+ There is also a brand new feature: You can specify which monster flags that
+ are NEEDED for a monster to be generated in the dungeon.
+ There is also an addition of dungeon flags. Here is the structure:
+ N:<index>:<name>
+ D:<long name>
+ W:<min depth>:<min depth>:<min player level>:<next dungeon>:<flags mode>:<min alloc>:<max alloc chance>
+ L:<floor1>:<%1>:<floor2>:<%2>:<floor3>:<%3>
+ A:<wall1>:<%1>:<wall2>:<%2>:<wall3>:<%3>:<outer wall>:<inner wall>
+ F:<flags>
+ M:<monster flags>
+ S:<monster spells>
+- The quest rewards are always good
+- I've finally found and fixed the Mirkwood Spiders Quest
+
+24/08/1999
+- Add the MAZE flag to the dungeon type flag list, which instead of a normal
+ dungeon generate a full big maze ! And it's EVIL ! Note that there will be
+ no rooms and no vaults generated, just a plain evil maze
+ The algorithm is from <Pierre.Bru@spotimage.fr> on rec.games.roguelike.development
+- Note about the dungeons : A dungeon can't have more than 40 levels
+- You can use SMALLEST, SMALL and BIG as dungeon flags to specify the level's
+ size
+- 6 new dungeon types: a Graveyard, an Illusory Castle, an Orc cave, a Maze
+ a Dragon's Lair and A Forest with specific gfxs and monsters restrictions
+- Some new gfx form Andreas Koch mostly to add more flavor to the towns
+
+29/08/1999
+- You can add a guardian and a guardian artifact to each dungeons types with
+ the following flags: FINAL_ARTIFACT_artifact_number and
+ FINAL_GUARDIAN_guardian_number
+
+29/08/1999 - PernAngband 4.0.0
+
+01/09/1999
+- Fixed the max_dlv non allocation error
+- Fixed the Tribal realm spell descriptions
+
+01/09/1999 - PernAngband 4.0.0b
+- New dungeon type, the Netherworld with Muar and the Wand of Fireball of
+ Mithrandir at the bottom
+- You can't gain a fate before being level 10
+- Some cosmetic changes suggested by Akhronath
+- A new Artifact by Akhronath, The Long Sword of Murazor 4d5, so ... the next
+ time you see a long sword 4d5, beware ...
+
+05/09/1999
+- Add a feature flag to allow running
+- Fixed the Chaos Warriors bug (they were unable to use magic !)
+- Fixed the Chaos Realm descriptions
+- You can't rest on a between gate
+- You can now steal from stores. But beware of the consequences if you fail...
+- Fixed a bug in that when recalling to the town the player was sometimes
+ placed in the upper left corner
+
+08/09/1999
+- Fixed a bug that disallowed the creation of entrances to some vaults
+
+14/09/1999
+- Fixed the Dragon Tower bug
+- Add minimum player level to the random dungeons
+- Add shafts, you can use them do go up/down for up to 4 levels(but you can't
+ ignore quest levels). Original patch from Static Chaos. Idea from GSN
+- Add a dungeon flags to forbid generation of doors
+
+27/09/1999
+- Some tweaks to the possessor code as written by Static Chaos i.e:
+ You must possess a monster that is able to open to door to do so. The
+ same applies to bashing. Your sex is modified by your body. Lots of tweaks
+ to the spells that can be used...
+- The racial power of the Dwarves is now the same as The Pick of Erebor
+ activation: they can find hidden passage in the walls
+- There is 1 chance in 10 that the level is an magical level, which means that
+ each grid will contain more mana than a normal level.
+- Add some new sentences to the monsters sentence lists
+
+04/10/1999
+- Began to add the Druid class. But it's TOTALLY different from the other
+ variant's Druids. They make intense use of the mana flow from within the
+ earth and the elements, thus their spells can regenerate the mana they have
+ used by pumping it from the floor, like the Tunnel spell which will blink
+ the player and will try to absorb enough mana form the grid he/she lands on.
+ They can also (and they are the only to be able to do that) regenerate the
+ mana flow of the earth. They also can use enhanced attack types called
+ Druidic Bolt/Beam/Ball that can follow a mana flow laid by the player.
+- The Druids can sense precisely the amount of mana of a grid
+
+08/10/1999
+- Added some revised graphics by Andreas Koch
+- Added the code provided by Alexey Guzeev(aga@russia.crosswinds.net) to
+ compile PernAngband under OS/2 with EMX
+- It's finally fixed ! the damn bug that was crashing the game when leaving
+ a town on certain system (Mac and Windows) ! this will never happen again !
+
+27/10/1999
+- Finished the Druid class. They have 40 spells (8 * 5 books). The spells that
+ activate Mana Runes is not active for now, but it will in a near future.
+- Add a slightly modified Tom Morton fake artifact patch (it won't print {} at
+ the end)
+
+30/10/1999
+- You can inscribe an object with !! to notify the player when it's recharged
+- Lots of bug fixes thanks to Lothar Lange <100644.2004@compuserve.com>
+- Lots of new ideas thanks to Lothar Lange <100644.2004@compuserve.com>
+
+30/10/1999 - PernAngband 4.0.1
+- "Foolish mortal! Thou hast not pleased me! I'll doom your game with the new
+ horrible DG curse !"
+ Yes I've done it, the DG curse is even worse than the TY one. One of it's
+ most perverse feature is that it can replicate itself to contaminate sane
+ objects that can't be cured. Also if you remove the curse of a DG cursed
+ object, it will recurse itself automatically !
+ Beware of the horrible Long Sword of Murazor because it carries the ancient
+ morgothian curse !
+ Oh, there are also some monsters that give the DG Curse when dying ... but
+ I'll let you find whose they are by yourself :-<|> (Dark God makes an evil
+ smile). I can just say one thing : you should *REALLY* *NOT* kill Mathilde...
+ But do not worry she's got much more HP, so you can't kill her anymore :)
+
+01/11/1999
+- Add the Rod of Home Summoning, which acts like the old dragon spells
+ "Call Home"
+
+08/11/1999
+- Drastically reduced the recharging time of the Dragon Scale mails in order
+ to render them useful.
+- Added a new weapon ego-item: of Spinning, which will make you spin around
+ in order to hit every monster around you. Note that you'll only get one
+ blow for each monsters whatever your normal number of blow is.
+- Added (ok stolen) the question in the GSNband FAQ about wrists hurting (it can
+ be useful, so everyone should know)
+- Rogues and monks gain more and more stealth as they gain levels (SBF)
+- Added some sentences for the Dwarves (in monspeak.txt)
+- Added the support of two-handed weapons (from GSN). Thus there are weapons
+ that can be handled with two or one hand (but with one hand it halves the
+ damage). A 2 handed weapon can't be wielded with a shield.
+- Added the random character name generation from Cthangband
+- Added rivers (water & lava) from the Steven Fuerst fractal patch. Note that
+ you have to add WATER_RIVER, LAVA_RIVER, WATER_RIVERS or LVA_RIVERS to a
+ dungeon to have some rivers generated (actually in Mordor(lava), Dragon lair
+ (Lava) and Forest(Multiple Water))
+- Updated generate.c to the Steven Fuerst fractal patch room generator (using
+ room_alloc), in order to implement the remaining part of the patch easier.
+ But I'm still having problems adapting it to my extended dungeon generator.
+- Ahah ! I've resolved the problem !! there are now fractal rooms (15%, can be
+ always enabled by specifying the CAVE flag), now the orc caves looks great
+
+10/11/1999
+- Added the Caverns(enabled via the CAVERN flag)
+- Hell has been moved down, it now begins at level 666! :)
+
+12/11/1999
+- Add the Static Chaos persistent dungeon patch that save every level of a
+ dungeon (which has the PERSISTENT flag) on disk and reload it instead of
+ regenerating a new level.
+- Add the Outer World dungeon type (this is an entrance to a place of an
+ incredible stability). It use the PERSISTENT flag and thus will be saved.
+ Only level 50 characters can go there to prevent abuse(like have an old
+ ultra powerful character and a new one and going into the Outer World to
+ exchange some stuff). And yes the saved dungeons are the same for EVERY
+ characters. But to add ... err ... variety, I've put it from level 300 to
+ 305, I've put it to have at least 30 monsters from the beginning AND to have
+ an higher monster generation rate(1/60 instead of 1/160 !), thus you'll
+ NEVER get bored there :)
+- Implemented the much more complex trap system from Angband/64 thanks to the
+ patch made by Static Chaos. There is 169 traps (see tr_info.txt).
+ Beware that on average the traps are deadlier...
+- It comes along with a new feature for stat system: temporary stat drain,
+ it's cool
+
+14/11/1999
+- Renamed Power Batteries to Essences
+- The DG_CURSE can put the NEVER_BLOW flag on a weapon ... sadistic !!!
+
+16/11/1999
+- DSMs now have been granted the FLY flag
+
+19/11/1999
+- The monster memory now colors some important things about the monster (like
+ resists, speed, ...)
+- Fixed a wand of wishing bug, that made them sometimes NOT use a charge
+- Moved the special level DeathWatch to the Orc Cave
+- Moved the special level Treasure Room to the Maze so people are forced
+ to play those dungeons if they want the artifacts
+- Random quests will only occurs on PRINCIPAL dungeons (the one created by
+ splitting the vanilla dungeon into 4 smaller ones, Galgals, Mirkwood,
+ Mordor and Angband itself)
+- To prevent too many YASD from the DG curse of Mathilde she has now been
+ granted 22500 hp, but as usual she can't attack :))
+- Fixed a bug that disabled the guardian at the bottom of some dungeons
+
+25/11/1999
+- Added main-gcu from Angband/64 to allow larger screens
+- Added the new note command that dumps to a file from "Tom Morton"
+ <tmorton@yikesstation.freeserve.co.uk>
+
+27/11/1999
+- Added funny time message from Z
+- Added object experience as suggested by Ceilti <Ceilti@aol.com>
+- Fixed a bug when the magestaves were cursed
+
+29/11/1999
+- Added the small trees (along with a new feature flag: SUPPORT_LIGHT) to
+ replace the walls in the Forest and Mirkwood
+- Some new gfx from Andreas, and the town of Bree with the new tiles
+
+02/12/1999
+- Add 4 dungeons by variaz@hotmail.com: Vanilla Dungeon(only from Vanilla Town,
+ The Small Water Cave, The Sacred Land Of Mountains(if you don't fly ...
+ do not even plan on going there),
+ The Wild Land Of Kurukar(It sure looks great)
+
+04/12/1999
+- New dungeon flags: HOT and COLD that affect object decay
+- Add YA*IF (Yet Another *_info File): wt_info.txt
+ It defines wilderness features *, allowing a better wilderness map. Since
+ it now defines nearly everything a wilderness need to have, the map itself
+ only have to be an array of feature and seed, thus allowing a much bigger
+ wilderness. I also plan to have a Omega wilderness mode where you can have
+ an overview of the wilderness to travel quicker (but time will pass much
+ quicker too:)
+ The structure of w_info is also changed, it's uglier but well .... :)
+ The wilderness size must be EXACTLY the same as the size defined in misc.txt.
+
+06/12/1999
+- Scrolls of Reset Recall are now WORKING !!!!!
+- The new wilderness mode is done ! When you reach the border of a wilderness
+ screen you go to the small scale wilderness map, where you can see all
+ the wilderness map with the towns/dungeons/... A lot of commands are
+ disabled there and moving takes MUCH more time, so beware of the food !
+ You can enter the big scale mode by pressing > (this allow you to enter towns)
+ NOTE: The Death Molds CAN'T reach the small scale mode because their
+ erratic movements are impossible to handle in such a scale.
+ BEWARE: Your imprinted pets WON'T follow you in the wilderness !!!
+
+11/12/1999
+- Add wilderness encounters
+- Add Oangband rod/wand stacking
+- Amulets of the Serpent from Sadi Khan <sadik@christa.unh.edu>:
+ Resist Poison, Dexterity, some AC and Poison breathing
+
+12/12/1999 - PernAngband 4.0.5
+
+18/12/1999
+- Updated the makefiles and renamed object3.c to traps.c
+- Add 5 new essences from Sadi Khan: Force, Darkness, Lightning, Mana and
+ Knowledge
+- Fixed the bug that was placing the player at the town entrance when
+ leaving a building
+
+20/12/1999
+- Add the tool slot. It's actually only used to wield a shovel/pick but I've
+ some plans for it
+- You can only dig walls if you use a shovel/pick in your tool slot
+
+24/12/1999
+- Removed the player ghosts, well in fact made them a compile-time option(see
+ config.h)
+
+26/12/1999
+- Added the Necromancer class. But again not quite the same as you've seen
+ before. They can't be killed as easily as the other class. In fact when
+ their HP comes to 0 they don't die but become undead for a while. While
+ being undead their HP is replaced by DP(Death Points) that have a max
+ inferior to the max HP and that are slowly decreasing(1 DP less each turn).
+ They can use all the healing spells/potions/... but this won't stop them
+ from being reduced again. The only way for them to come back in the world
+ of the living is to kill 2 * player_level monsters.
+
+01/01/2000
+- Happy New Year !
+
+02/01/2000
+- Add a new curse(as suggested by LucFrench@aol.com). When a weapon has this
+ curse and that the wielder attacks a monster with it, it can clone it !
+ And it's REALLY REALLY annoying, especially when fighting some kinds of
+ wyrms :)
+
+10/01/2000
+- Finally fixed the flags shown on the character display.
+- The quit without save command(CTRL + L) is now a compile time option disabled
+ by default
+- The Necromancer class now has 5 spells (used the same way as the mindcrafter
+ powers)
+
+17/01/2000
+- Power gained by levelling artifacts has been reduced
+- New unique monster: The Greater Lag Monster... beware :)
+- AHAHHAHAH ! I got it ! the wilderness bug that made the game crash when
+ entering a town is fixed ! And the wilderness system has been greatly
+ improved.
+ Now to switch the wilderness mode, just press < or >.
+ Everything should now works properly
+
+16/02/2000
+- Each Nazgul will now drop his Ring of Power. They are randomly generated
+ and thus they don't appear in a_info.txt. They always give invisibility,
+ life draining and are heavy cursed.
+
+21/02/2000
+- Changed the format of w_info.txt again, it's much more readable now.
+ I also added a additional parameter to the W lines of wf_info to define
+ which letter will be used in w_info.txt
+- 60x20, this is the scale of the new wilderness, it tries to looks like
+ the 3rd age middle earth map. Thus some dungeons leaved the towns they
+ were used to be to join their "real" place on the map. Only the Upper
+ Reaches of Galgals are still reachable from Bree.
+
+24/03/2000
+- Added a small hack from Static chaos to allow running faster with the
+ overhead map enabled.
+
+24/03/2000 - PernAngband 4.0.6
+- Possessors now have a MUCH better pseudo id, equal to warrior's one
+
+06/04/2000
+- Changed the wilderness map with the one provided by Gwidon S. Naskrent
+ (naskrent@artemida.amu.edu.pl). It's bigger, it's better :)
+- Fixed a bug with the chasm opening inscription
+
+08/04/2000
+- Fixed a bug with the staff of wishing
+- Druids can now pass through trees
+- Stairs are now noticed again (when you see one it will stay on your screen
+ even when out of torch radius)
+
+10/04/2000
+- When a Necromancer turns into an undead he/she/it gets cured of everything
+
+05/05/2000
+- MAYBE fixed the damn wild bug ... again ? oh please GREAT RNG help me !
+
+08/05/2000
+- Added the Notes patch from Chris Kern. That allows you to get a .txt file
+ recording great events in your life, your notes, ...
+ Very useful I think
+
+11/05/2000
+- Fixed the artifact wands stacking bug
+- Removed the Netherworld dungeon
+- Added the Moria dungeon, it's not random and can be found in ... the moria
+ mountain chain :) There is Muar waiting you at the bottom, guarding the
+ Quarterstaff of Olorin
+- Waldern, the king of water is back, and he is the final monster of the
+ small water cave, guarding a the Trident of Ulmo. But beware it is more
+ powerful than in vanilla
+- Dungeon guardians and dungeon guarding's artifacts can only be generated
+ in their respective dungeons
+- Removed rumors that were still Zangbandish and some others I didn't like :)
+
+14/05/2000
+- The MONSTER_PERCENT_ flag can be added to some dungeons to specify the
+ amount of monsters that are affected by the dungeon specific restrictions
+- Random dungeons now appear on the overhead wilderness view
+- COULD IT BE !!!!!! yes it could !!!! thanks to Tom Demuyt that sent me a
+ savefile where he wasn't able to learn spells, I may have finally fixed the
+ dreaded spell bug !!!! I say may because I'm waiting for some fellow
+ mac user to compile it to see if it really fixes the bug.
+- IT WASN'T ! but now it *IS* !!!!
+ That was an other bug. And now Gob, the great Gob, the powerful Gob,
+ found the dreaded mac spell bug !!! thanks Gob !!!!
+- Changed the price for Runes/Essences to be more accurate
+- Changed some uniques in r_info to their former vanilla glory
+- The look command can now show the trap's types
+
+18/05/2000
+- Fixed some typos(thanks whimsy)
+- Began to add the Body parts system to allow more rings, weapons,
+ everything, depending on your current corpse (dragons will have more rings,
+ mariliths 2 weapons, ...)
+- If your corpse allows the use of more than one weapon, you can use them :)
+ let see a Sorceror using Ringil AND Gandalf ! :) well first he has to find
+ the rare scroll to change body and then to find a corpse that allows 2
+ weapons
+- Changed color of shafts (brown now) because they were hardly visible
+- Flying no longer allows passing over mountains, you MUST have a Climbing Set
+
+22/05/2000
+- Fast autoroller as a birth option
+- E'x'amining books in stores now browses them if your class can read them
+- Add a bookstore to Lothlorien town
+
+26/05/2000
+- When in battle rage(berserk) and using the ascii mode the screen goes all
+ red (as it is white for invulnerability and black for wraithform)
+- r_info is modified to give all monsters some inventory slots. the format is
+ E:weapon:torso:arms:finger:head:legs
+
+27/05/2000 - PernAngband 4.0.7
+- add a new dir to lib, DNGN which contain the definition of the LEVELS
+ not the dungeons. one file for each level named dun$dungeon$.$level$
+ In it you can have some declarations, like B:dungeontype which will
+ create a stairway to an other dungeon, thus allowing to have "branches"
+ in the dungeons.
+ L:dungeontype and L:level specify which is the "father" dungeon and at which
+ level the stair appear.
+ S:ext means to save the level(and thus to reload it) in the player.ext file
+ so allowing the use of persistent levels(the persistent dungeons option
+ is gone)
+
+29/05/2000
+- Necromancers can use the Nether realm in addition to their old set of
+ powers(now accessible through 'U')
+
+01/06/2000
+- Boomerangs of spinning are no longer allowed (that was silly)
+- Increased the rarity of the ULTIMATE artifacts (Magestaff of Gandalf,
+ Longsword of Eru, Seeker Bolt/Xbow of the Elves)
+- Fixed the displaying of attributes under the 'C'haracter display
+
+-04/06/2000
+- Players can now choose which kinds of monsters they don't want in their game
+ at birth (wipe all Pern monsters, all Cth, monsters, joke monsters, ..)
+ Note that monsters in fixed quests are not affected.
+ Thanks Static Chaos.
+- In the Maze you can no longer remember anything, that means everything you
+ see is what it's in your light radius ! MUAHAHHAH, I'm *NASTY*
+- Archers now get a better chance of not breaking their ammo as they
+ advance levels
+
+08/06/2000
+- Note taking bug should be fixed
+- Fixed a bug with the incarnation code
+
+11/06/2000
+- Added the Orc Barracks special level (level 35 of Moria) from
+ Chris Weisiger (jmartin@inreach.com)
+- The maze is now guarded by The Baby Minotaur who is holding The Steel
+ helm of Hammerhand
+- The graveyard is guarded by Vecna holding Doomcaller
+- You can now use the 'l'ook command in the small wilderness view
+***********IMPORTANT************
+- Between gates are no longer automatic, you must press > while standing on
+ one to activate it
+********************************
+
+16/06/2000
+- Revised the Adventurer's guide thanks to Chris Weisiger(jmartin@inreach.com)
+- You can no more recall from a quest
+- You can no longer target pets
+- Re-enabled the "Control the rings" spells of the Shadow realm
+- Between gates are now purple +
+
+02/07/2000
+- Only priest-like characters can use the Valarin realm
+- Removed all the references to Logrus
+- Tunnelling rubble will place the standard floor of the current dungeon and
+ not the floor feature
+- Grow trees no longer crashes when used near the dungeon edge
+
+05/07/2000
+- Fixed the character dump resistances
+- When a full character dump is requested (upon death) the self-knowledge
+ screen is used instead of the grid of abilities, that looks better :)
+- Fixed a bug in the placement of stairs which sometimes led to a stairway
+ to another dungeon when not requested
+- Fixed the bug that generated 'unknown' grids
+- Should now compile (nearly) without any warnings
+- Fixed the mountain bug that hung the game when entering mountain chains
+ in the wilderness
+
+07/07/2000
+- Some more monsters from Angband/64 and some more work on r_info from
+ Static Chaos. That brings the number of monsters to ... 1023 !
+- Damage taken from going between is divided by (ac / 50) + 1
+ So between 0 and 49 ac it's full damage, between 50 and 99 it's half
+ damage and so on
+- Greater Hell Beasts now have .. 1 hp :)
+
+11/07/2000
+- Add the Unbeliever class, they are anti magicians. Great warriors, they
+ have the worst magic device skill of all the classes, they continuously
+ generate an anti-summoning field around them disabling monsters from
+ summoning. The radius of the field increase with levels. After level 20
+ they can also disrupt the magic continuum, thus getting the same effects
+ as Anti-Magic, Anti-Tele and The Stone of Lore(nobody on the level can
+ teleport)
+
+13/07/2000
+- Add an option to auto tunnel walls when bumping into them.
+ NOTE that it'll ALWAYS take a turn then, so beware...
+- Really fixed the note taking bug (ok stole the fix from Zangband :) )
+- Changed the object description so now it's "Smeagol's Corpse" instead of
+ "The Smeagol's Corpse"
+
+13/07/2000 - PernAngband 4.0.9
+
+29/07/2000
+- Orc Barrack's between doors are fixed
+- God favor is reinitialized upon birth
+- Added the Daemologist class along with a brand new realm and lots of new
+ cool effects(try making all your pets explode ! :)
+ All made by Static Chaos
+
+31/07/2000
+- Fixed lots of compilation warnings thanks to Static Chaos
+- Added the random vaults and crypt vaults from Zangband with the help of
+ Static Chaos
+- Fixed some bugs pointed out by Bablos
+- The Ray Rune now relay make a beam and not a pseudo bolt
+
+03/08/2000
+- 2 handed weapons wielded with a shield now really restrict your fighting
+ abilities (I forgot to add the code before) :)
+- Possessors can't leave their body while they wear cursed items
+- Word of Destruction is no longer allowed in quests
+- Add a fate spoiler created by Dustin Ragan
+- Removed the old new player ghosts implementation
+- Added the new old player ghost implementation (stolen from Drangband)
+- Rod system have been totally reworked. Now you can find "base" rods without
+ power (rods of nothing) with a certain quality (wooden, iron, ...) on which
+ depends the amount of mana they contain. You can also find rod tips which
+ contain the real spells but cannot be activated alone. You must attach (with
+ the 'z' command) a rod tip to a base rod before using it. The spells of the
+ different rod tips need different amounts of mana (so illumination is less
+ mana-hungry than healing) :). You can also have base rods ego-items (artifacts
+ soon) that can decrease the mana cost of the spell, increase the capacity
+ of the rod, decrease the time needed to zap the rod, ...
+ Oh BTW, you should be eager to get an Adamantite Rod of Healing of the
+ Istari :)
+ Now rods will hopefully be useful.
+ Note that you have to identify the base rod to get the mana indicator
+- Note files are now playername.nte instead of playername.txt
+So if you want your note file to be carried to the next version you'd better
+ rename it :)
+- Fixed a bug that prevented Azog from being generated at the bottom of the Orc
+ Caves
+
+07/08/2000
+****************** IMPORTANT *****************
+- Removed the Python support because it was slowing the game, adding
+ weight to the executable (and thus to the archive), was mainly unused,
+ and was never present in some ports
+**********************************************
+
+09/08/2000
+- Unbelievers are now much better at perception, and actually become even
+ better as they go up levels
+- Fixed several bugs thanks to Iain McFall
+- Reset recall is now much nicer to use thanks to Iain (again :)
+- When a character is infected by the Black Breath it is show in the char
+ screen & dump
+
+15/08/2000
+- Cursed mage staves should no longer crash the game when identified
+- A Marilith can now drop it's corpse when killed
+- Add the gods spoiler made by Dustin Ragan
+- Fixed a bug that could eventually crash the game when looking at the quests
+ screen
+- Fixed a bug in the random junk artifacts that made them cure fear when said to
+ cure confusion
+- Add the squelch patch from Iain McFall
+- Boomerangs are now pseudo-id'd
+
+19/08/2000
+- The *thanc artifact daggers are less common because they are so powerful
+
+24/08/2000
+- The Midas Touch 30k gold limit has been removed (suggestion from Chris)
+- Fixed (ok ok Iain fixed :) ) several trap related bugs
+- Squelch now use the pseudo-id thanks to Iain
+- Pseudo-id inscriptions are no longer part of the real inscription of an object
+ and thus ... it's better :)
+- Tribal spell "Life Drain" now reduces your stat permanently 30% of the time
+ and the other 70% it can be cured via restore stats pots. The damage is now
+ always 50d50 and is no longer affected by mage staves of spell
+- Tribal spell "Meditate" no longer hastes/heals you
+ (it still heals your sanity) and the glyphs' radius have been decreased
+- Add the Trap of Acquirement that will give a great object and then mutate
+ into an other trap. It can never be identified and the color varies.
+ Idea from Iain
+- When casting charm upon monsters only the non-pet ones will be affected
+- Elder Aranea HP reduced
+- Small trees are burned by fire
+- Randomly activated mutations are no longer activated in the overview
+ wilderness
+- Ghoulkings are now z instead of p
+- Thrown potions now give xp when killing by a ball effect
+- Scrolls of reincarnation have been removed now you can only find scrolls of
+ deincarnation. To reincarnate into a new body 'U'se the appropriate power
+- Recharging is now useful again
+- Breeders should be a BIT slower
+- Reduced mutation chance of breeders from 7% to 3%
+
+03/09/2000
+- New, unified store/building code
+
+08/09/2000
+- Add quest plots. Feature 75, 76, 77, 78 are now permanent walls but used
+ as quest plot info holders. Before the new building code quest info was
+ stored in the building feature but now all stores/buildings use the same
+ feature(74) and the "special" value defines them.
+- Add semi-random towns. Now if the ' ' (space) character is used in the town
+ definition this grid will be replaced by the feature calculated by the
+ plasma generator as if it was a normal wilderness screen. So the forest of
+ Lothlorien is plasma generated and not fixed. It add variety and consistency
+ with the rest of the wilderness
+- All towns are now updated to the new store/building code
+- Removed the "you are being crushed" bug when going in the overview map thanks
+ to Iain McFall (as usual :) )
+
+10/09/2000
+- When you use a 2 handed weapon and press 'e' the equipment list will show
+ the weapon(and some info) in the shield slot too
+- Monsters can't have fewer arms than weapons
+- Mimics have been upgraded. They now have 5 powers. First is to use books of
+ lore as before. Second is invisibility. The other ones are Mimicries.
+ There are 3 mimicries, legs mimicry, wall mimicry and arms mimicry.
+ Legs and arms mimicry will "create" a new body part (or some) for a certain
+ duration. Wall mimicry will make the caster able to walk in walls (he becomes
+ a wall) but ONLY in walls .. not on floor .. so beware with this one
+
+27/09/2000
+- For your god to resurrect you need 3 times more grace and your grace
+ will drop to -100000
+
+30/09/2000
+- Add exploding ammos (bolt, arrows, shots) thanks to a patch by Static Chaos
+
+13/10/2000
+- Add some patches from Kusunose Toru <kusunose@hcn.zaq.ne.jp>
+- Neuters get a weight & height
+
+13/10/2000 - PernAngband 4.1.2
+
+14/10/2000
+- Building doors can't be erased
+
+28/10/2000
+- Fixed a bug in the rod system when attaching form the floor
+- Added fountains that you can quaff from ('H' in normal mode, 'V' in roguelike
+ mode, yeah I know it was for version number .. but .. mhh who is using it ?:)
+ Thanks to Static Chaos for the patch
+- You can fill empty options with fountains(thanks static chaos)
+- Added the Iain McFall Show Monster patch to show all viewable monsters
+
+08/11/2000
+- Fixed numerous bugs
+- Fixed the st_info & ba_info files thanks to Kusunose Toru
+
+16/11/2000
+- Godly blasts can't be reflected anymore, so beware, puny mortal !!! :)
+- Added a whole bunch of new floor features (ice, mud, sand, sandwalls, ...)
+ thanks to Static Chaos.
+- More spell effect will affect the dungeon, for example, fire will melt ice,
+ nether will kill trees, fire will create ash, cold will freeze water, ...
+ thanks to Static Chaos again :)
+- Cleaned up the feature code again (that was a REALLY messy part of angband...
+ and especially of Zangband)
+- Note: Sandwalls can be dug WITHOUT any digging tools
+- Teleport scrolls/staves are back in the shops
+- Mages, Wizards and Sorcerors now only get a wand of fire bolt at birth
+- Finally fixed the Alchemists extracting powers
+- Fixed numerous bugs thanks to KUSUNOSE Toru
+- Desert and Glacier wilderness features now really look like desert &
+ glacier :)
+
+18/11/2000
+- Debug commands are allowed in the overview map, but beware, do not create
+ objects & monsters there ...
+- The wilderness map is no longer fully known, you have to explore it
+- New wild.c file to support all wilderness functions
+- Maps can be found to reveal some places of the wilderness
+
+27/11/2000
+- Exp gaining weapons will gain levels way slower
+- Ring & Amulet random artifacts will only be "Ring of foo" and not
+ "Ring of Slow digestion of foo"
+
+30/11/2000
+- Level gaining Artifacts have been significantly toned down
+- Sauron lost his chances to drop The One Ring
+- Blood of Life potions & Staves of Wishing are more rare
+- Your god wont always resurrect you even if you have enough piety
+- When leaving corpses, Possessor equipment will be dropped to floor
+ to prevent their form being "lost"
+- Sandworm lair, a new dungeon
+- Staves of wishing now only have 1 charge
+
+12/12/2000
+- A patch to the alchemist patch from KUSUNOSE Toru, they should now work
+ perfectly
+- h-system.h fixed so it should compile fine on Linux
+
+29/12/2000
+- New r_info.txt with the HAS_LITE flag, thanks to Static Chaos :)
+- Now some monsters can have a light around them
+
+03/01/2001
+- Phial of Galadriel and Phial of Undeath now use the same symbol(yellow ~)
+
+06/01/2001
+- Fates now show up in the character dump (thanks Kusunose)
+- Scrolls of Divination are more friendly now (thanks Kusunose)
+- Artifacts in monster inventories not yet seen are not shown in the
+ artifact list (thanks Kusunose)
+
+08/01/2001
+- Zweihanders are now 2-handed weapons(as they always should have been :) )
+
+11/01/2001
+- Ego Rods are now "xxx rod of egoname of power" and not
+ "xxx rod of power of egoname"
+- Fixed some typos(parchEment, NumeRoNean, ...)
+- Fixed a bug with junk randarts
+
+18/01/2001
+- Gods now need more sacrifices
+- Disabled the monster lite feature right now because it's HORRIBLY buggy
+- The Sandworm Queen will now drop the Sandworm armour when killed
+
+03/02/2001
+- Now you can separate a dungeon entrance from a dungeon exit on the
+ wilderness map using the WILD_ix_iy__ox_oy flag in d_info.txt.
+ ix, iy are the coordinates on the wilderness map of the entrance.
+ ox, oy are the coordinates of the exit.
+ Note that each of them(entrance and exit) must have a physical entrance
+ on the wilderness map(that is a line in w_info.txt).
+ When you use the entrance to enter you get to the first level but when you
+ use the exit to enter you get to the last level.
+ So now you can have dungeons that create shortcuts in the map.
+ Or even allow to go in previously unreachable places(like now the dungeon
+ of Moria allows you to reach a secret valley in the Mountains of the Moria
+ which allow getting to some other dungeons)
+- Fixed a bug with fountains (yeah, fountains of the Blood of Life were quite
+ unbalancing :)
+- Priests can't use Tribal magic anymore
+- Mages can't use Crusade & Illusion Magic anymore
+- Rogues can't use Crusade anymore
+
+05/02/2001
+- Found why under certain circumstances forbidden objects could still be
+ generated (and that's also fixed now :) )
+- Dungeons & Monsters can now have object themes (yeah I know, I stole that
+ from Zangband, though the dungeon theme was in my head for long, but
+ when I steal I do admit it ...)
+ For example The Sandworm lair is really full of magical items, while the
+ Orc Caves are more on the side of the weapons & armors. And the Dragon Lair
+ is filled with ... oh well everything :) and so on.
+ So it's now really worth to get out of the 4 basic dungeons and see the
+ wild world!
+ All dungeons are done with the themes, it could take a bit longer for
+ the (1031) monsters... :)
+ NOTE: Dragon Scale Mails are considered TREASURES
+
+07/02/2001
+- Ego Monsters, yes like Ego items but for monsters. New file re_info.txt
+ defines all possible ego monsters types (skeletons, zombie, ...). It can
+ modify, the flags, spells, level, speed, ac, ... of the monster that is
+ turned into an ego monster.
+ This will increase the randomness of the game, increase the number of
+ monsters from 1031 to .. a lot... :)
+ There are not much ego monsters right now, so feel free to submit them
+
+09/02/2001
+- Sorcerors can't use much armor
+- Beastmasters have been give access to the Tribal realm, the Beastmaster's
+ powers are now available through the 'U' key
+- Player Raise death spells will now really create an undead. For example
+ you cast raise death upon a kobold corpse and you can get a Skeleton Kobold,
+ or a Zombie or a Spectre one. New Undead types will be added later. And I'm
+ open to ideas naturally
+- Narya activation is now Healing (500)
+- Nenya activation is now Healing (800)
+- Vilya activation is now Healing (900) and cures Black Breath
+- Daemonologists can now wield their books (in the weapon slot). When used this
+ way, spells are cast in 1/3 of a turn otherwise it takes 5 turns
+
+11/02/2001
+- Specialized ESP, like Orc ESP, Troll ESP, Dragon ESP, ...
+ They are less rare than full esp, but full esp is more rare
+- Fixed a Runecrafter bug which could crash the game if no second rune was
+ selected
+
+11/02/2001 - PernAngband 4.1.5
+
+11/02/2001
+- Ents get nearly no sustenance from eating
+
+15/02/2001
+- Center player option
+- Alchemist artifact creation now requires player level magic essences to
+ work. The stats are permanently reduced upon failure (as it always meant to
+ be). You PERMANENTLY lose 100 max hp when trying to create an artifact.
+ This should reduce the number of mad alchemists :)
+- Dark Swords as a new item type. They generate an antimagic field of 50%
+ minus the sum of the enchantment (a +5+5 one will only do 40%) on a
+ 5 tile radius (also minus the enchantment)
+- Unbelievers now generate a (player level)% antimagic field on a distance of
+ (player level / 10)
+- An antimagic field disables any form of magic on the user, and can prevent
+ monsters from casting spells (not breathing). A 50% antimagic field will have
+ a 50% chance of stopping a spell
+
+18/02/2001
+- Normal Artifacts. Normal Artifacts are artifacts found directly in k_info
+ that doesn't require a place in a_info. That allow strange artifacts, like
+ wands, rods, staff (those were possible already but ugly), ... and even
+ food
+- The Rod Tip of Home Summoning is now a Normal Artifact
+- The Greater Ration of Health is the first (very rare) food artifact !
+ When eaten it provide +70 hp permanently
+- The Potion of the Blood of Life is now a Normal Artifact
+- Invulnerability can no longer appear in fountains
+- The Ring of Precognition is now a Normal Artifact
+- The Ring of Wraithform is now a Normal Artifact
+- The Scroll of Deincarnation is now a Normal Artifact
+- Hell is now the Nether Realm
+- Fixed a bug that created stairs at bottoms of dungeons
+- Extra blows can only be provided by things in the weapon slots
+- The Scroll of Mass Resurrection
+- Renamed Warrior-Mage to Warlock
+
+20/02/2001
+- The Wand of Stone to Mud of Thrain and The Wand of Fireball of Gandalf
+ got the EASY_USE flag, allowing them to be used even by unskilled
+ characters
+- As does the Stone of lore
+
+23/02/2001
+- Fixed a bug that allowed people in quests to use the '<' key to get to the
+ wilderness timescale
+- Fixed a quest bug that allowed players to regenerate the quest many times
+- The Control the Three shadow spell is now much much more effective, since
+ it removes black breath and DG_CURSE. The only little annoying thing is that
+ it requires that player to wear the *THREE* rings. And that can only be
+ accomplished with either the Possessor class (though they CAN'T cast the
+ spell -- no hope for them) or The Scroll of Deincarnation
+
+10/03/2001
+- Monks can choose which spell to learn
+- Fixed a bug with limited ESP description
+- Fixed a bug in Possessor titles
+
+09/04/2001
+- DeathMolds can now teleport onto stores & such (all features that are
+ considered floor by the game)
+- Objects cannot be dropped on traps anymore
+- The Sandworm Queen no longer appears multiple times
+- Harpers wont crash at the start anymore
+- Panic saves will use the playername.pnc filename (thanks Improv)
+- Symbiants can't hypnotize monsters that aren't pets anymore
+
+15/04/2001
+- Add an old patch from SC which allows dungeons to project an attack every
+ few turns. For example a player in the Nether Realm will be hurt by nether
+ every 3 turns for a damage of 10d10.
+ The syntax is(up to 4 lines):
+ E:<dice>d<sides>:<frequency>:<attack type>
+- Volcano now does 2d10 damage each 10 turns, beware scrolls on the floor :)
+- Lost Ruins of Numenor does 1d1 acid damage each turn (water will RUST you)
+- Cirith Ungol will poison you every 20 turns for 4d4
+- Illusory Castle will confuse you every 6 turns for 6d2
+- Small Water Cave does 1d1 acid damage each 20 turns (water will RUST you)
+- Nether Realm is unlightable (as is the Maze) and always empty levels
+- Nether Realm is now guarded by Tik'srvzllat who guards the Ring of Phasing
+ a powerful ring that allow wraithform and immunity to nether
+
+16/04/2001
+- New Race system, Now you select a race AND a race modifier at birth.
+ For example you choose to be a human vampire, or a dwarf skeleton, ...
+ Then some modifiers apply (stats, skills, extra powers, ...). Note that
+ all races can't use all race modifiers
+- New race modifier, Barbarian, the old barbarian race is gone
+- New race, the Wood Elf from CathAngband, masters of the bow, with 1 extra
+ might and 1 more if they use bow and are high level enough
+- Extra Might can now be > 1
+- Penalty for priests using non blessed swords/axes/polearms is now (-15,-15)
+- New weapon category: Axes
+- Add Weaponmaster class from Gumband, trained into one weapon category, being
+ great with it but bad with anything else
+
+18/04/2001
+- Aranruth is a broad sword, 15lb, 3d5
+- Fixed lots of spelling errors/typos thanks to Improv
+- Add a new birth option: Astral (ghosts from Kamband, name from Gumband)
+ It enforces vanilla town and makes you start at level 98, you can't recall
+ and need to reach town
+- Trap doors can't appear at the last level of dungeons or in non dungeons
+- The concept of non-dungeon places: some "dungeons" got the FLAT flag
+ meaning they are ... flat, like a forest
+- The concept of Towers, going up instead of down
+- Updated birth.txt to take in account the race modifiers
+- Wood Elves can go through trees
+- Ammo similar to the one in your quiver will always be picked up
+
+20/04/2001
+- The death fate is way different now. When the player enters level he is
+ fated to die on he gets teleported to a special level in a special dungeon.
+ the level is empty, small and full of really out of depth monsters. Recall,
+ genocide, ... are forbidden. There are no stairs, the only way to leave it is
+ to kill every single monster of the level. Then the player is teleported
+ back to town. Chance of surviving are *LOW* but not nonexistent :)
+- The knowledge rune will now probe monsters
+- Runecrafters are now playable (new damage formula) even at low levels !
+ Now lets hope they are not TOO powerful :)
+- Runecrafters upgraded. They can now:
+ 1) Cast a spell on the fly (as before)
+2) Cast a runespell they memorized before (can memorize up to 100)
+ 3) Cast it from a carved runestone (uses 75% mana and does NOT need the
+ runes to be present in the inventory, but consumes the runes during
+ creation and it must be carried around to be used)
+- Mormegil is now a Darksword and is quite nice
+- Lesser & Greater Krakens now drop corpses
+
+24/04/2001
+- Add random towns in the dungeons. There can be up to 4 towns per dungeons.
+ Not all dungeons (well should say places) can hold towns. Random town shops
+ are took from the possible shops in st_info.txt with the RANDOM flag
+- Stores in st_info.txt got flags
+- When carving a Runestone the involved runes are destroyed
+- Random towns can have different shapes, from vanilla one to hidden one
+ (the stores are placed randomly on the level, without any buildings)
+- When the player is invisible and does not have see invisible the @ symbol
+ disappears
+- Cannot locate undetected traps by simply 'l'ooking around
+- Mariliths cannot use boots
+
+26/04/2001
+- The player is no longer "teleported away" when leaving some buildings
+- Level gaining artifacts rarity have been increased
+- Death Ray will actually kill the player
+- Removed traps of death ray (now that death ray insta-kills)
+- Recharged wands/staves cannot be extracted anymore
+- Wraithform no longer reduces damage
+- New hp formula for Possessors
+- Some sentences for the DarkGod monster to say, based on #angband :)
+
+28/04/2001
+- *WARNING*, squelch list has to be checked upon importing an old savefile
+- Every item can provides blows since Alchemists can't create that many arts
+ now
+- Wielding a mage staff (even non ego ones) will provide with a decrease of 20%
+ of the casting speed (using 80 energy instead of 100)
+- Add makefile.bcc thanks to Arch
+
+29/04/2001
+- Some Spelling/Grammar fixes in lib/help/ -- Improv
+- Levelling artifacts will now use a new scheme:
+ There are now groups of abilities (the Fire realm, the Cold Realm and so on)
+ Whenever a weapon goes up a level, it gets to either:
+ 1. become enchanted by +2/+1
+ 2. gain another attribute from a group it already has.
+ 3. gain +1 to hit, and a point.
+ when a weapon gains a certain number of points, it might buy access to a new
+ group. Note that some groups can contain good AND bad abilities
+- Pet shop now sells scrolls of summon never moving pets
+- Lots of fixes (ammo weight, god flags, ...) thanks to Kusunose Toru
+- Towers deactivated for the time begin
+- Rods considered good
+
+01/05/2001 - PernAngband 4.2.2
+- Force attacks will pull away monsters (from Dr)
+- Fist of Force is now a force attack
+- Unbelievers can now detect traps at level 25 and destroy them at level 35
+ Press 'm'
+
+03/05/2001
+- GoI no longer protects from insanity
+- Colored messages
+- Artifact creation results in a 40hp loss
+
+05/05/2001
+- Hermit subrace, magic adepts weaker physically but have more mana reserves
+- Use upx for exe compression of the DOS version in makefile
+
+07/05/2001
+- Fixed makefile difficulties in makefile.org, and uncommented one of
+ the safer sets of CFLAGS/LIBS in that file as a good default -- Improv
+- Fixed some other spelling problems, notably in cmd7.c -- Improv
+
+13/05/2001
+- Reworked the old (ugly) activatable mutations, race powers, ... system to a
+ new unified one. Race, subrace, class powers are defined in the tables in
+ tables.c. Now the new system will allow for artifacts to grant powers, it
+ also allow intrinsic powers (i.e. you quaff The Potion of Blinking
+ and from now on you can blink at will)
+- There is a 2% chance of gaining the grow mold mutation when eating a slime
+ mold
+- Priests gets the curse detection power
+- Oops the Sandworm Queen wasn't confusion resistant :)
+- Added various granted powers to various artifacts and ego items
+- Sorceror allowed spellbooks are now in tables.c (Mrealm_choice)
+- Changed the install rule to do something sensible on Unix. Changed config.h
+ to suit. Default dir for lib is now /usr/lib/games/pernband/
+ Hopefully this won't make too many people angry -- Improv
+
+15/05/2001
+- New GFX by Andreas Koch
+- Fixed some entries in a_info
+- Monster memory now tells people when they're facing a Nazgul (Kevin W Thomas)
+- Fixed a bug in dungeon town generation (Kevin W Thomas)
+
+16/05/2001
+- Added makefile.WHICH, fixed up my earlier patches to add a real install
+ rule - Improv
+- Pseudo id now works for potions, scrolls, wands, staves and rods. Magicians
+are better at pseudo id-ing those than warriors (SC)
+- Can only use one ultimate artifact at a time (not that it really
+ matters given that no-one will ever find one :) )
+- Fixed a bug preventing The Baby Minotaur from being generated if it was
+ already generated for a previous character using the same savefile
+
+17/05/2001
+- Upgraded to latest z-term code
+- Illusory castle got a guardian, The Glass Golem (a NASTY thing) hoarding
+ The Helm of Knowledge, which auto ids every item you walk onto and activates
+ for insanity + *id*
+- Crushed all oriental items(nearly) of the game, they just doesn't fit the
+ general theme
+- Gigantic dungeons (flag DOUBLE) from SC
+- Ice lair as a Gigantic dungeon from SC
+- Dragon Lair is gigantic now
+- Between gates travelling damage is now /2
+
+18/05/2001
+- Checked in an EXTREMELY raw and broken version of my z-term changes that
+ produce cmovie files. It's disabled by default, if you want to play with
+ it, play with z-term.c ... Until I incorporate a cmovie player into
+ Pernangband, you can use the one I wrote for MY roguelike MoLD, available
+ at http://www.sourceforge.net/projects/mold/ . Little plug there. -- Improv
+
+21/05/2001
+- Moria WILL have downstairs at the bottom
+
+24/05/2001
+- New quest code ! Quests are no longer in *_info.txt files but are able to do
+ many new things, and the code is less ugly than it was :)
+- Random Quests refitted to use new quest code, now each level with a random
+ quest will have a vault with the monsters you need to kill inside, and a
+ princess that is held prisoner (your goal being to save her)
+- Refitted Thieves quest
+- New Bree quest, The Lost Hobbit ! save Merton !!
+- New key, 'y' to give items to monsters
+- Elven vampires no longer get resist lite, that was silly AND unbalancing
+- Artifact arrows wont come in piles anymore
+
+26/05/2001
+- Removed Wizard & prior classes, Mages & Priests can now use their realms
+- Refitted Crusade realm, some new (fun) spells
+- cmovie changes to make it work better. Should have it all cleaned up and
+ portable very soon. -- Improv
+- Updated pern_faq -- Improv
+- Power mages start with a 2d4 spells
+- Fixed a bug which made random artifacts destroyed by auto-squelch. --
+ Kusunose
+- Changed spellbooks colors to be more .. accurate
+- Add the Spirit realm, taking the place that was designed for it a long time
+ ago, the one that tribal used until now, between Valarin and Shadow.
+ Tribal is not removed, but it can only be used by some nature-like classes
+ (like beastmasters, rangers, ...). Some spell names & spellbooks names are
+ took from psiband Psionicists
+
+28/05/2001
+- disturb_move option off by default
+- Bree town totally redesigned by Mynstral to be more accurate the LoTR
+ description of it and to be more .. beautiful :)
+- New quest in Bree after the hobbit one, The Trolls Glade
+- Dark Horseman quest required level raised to 35
+- Massive updates to tables.c, fixed spelling of several spells, renamed a
+ few, fixed spelling of several lvl-specific class names, renamed a few.
+ Also working on maintainability for race/class restriction code in same
+ file. -- Improv
+- Entirely replaced TANG.txt with new stuff I wrote -- Improv
+- Permanent wraithform no more do the "You feel opaque" every few turns
+- Recoded the passwall() function to be... less ugly and less buggy
+- Archers learn to protect their arrows from fire as they advance levels
+- The Toris Mejistos guarded by Ar-Pharazon the Golden at the bottom of
+ the lost ruins of Numenor
+- Fixed random artifacts activation bug
+
+30/05/2001
+- Spell lists are now colored
+- Spell descriptions when browsing
+- Half magery spells descriptions done by Parak
+- The potion of learning is a k_info artifact now
+- Added color to seen unique list, quests list and a few others
+
+31/05/2001
+- Descriptions added to first five Shadow books (Shaun "arch" Sides)
+- Nazguls lost the DG_CURSE upon death
+- The Spiders of Mirkwood quest is the first Lothlorien quest
+- Magestaff of power now increases spell power while magestaff of spell
+ holds 2 spells (switching their previous behaviors)
+- New ego item system, externalized everything, it's half based on Matthias
+ Kurzke patch
+- Rewrote e_info.txt for the new ego system, the rarity should look like the
+ same
+- Bows of Numenor & Lothlorien as new ego items
+- DSM can be ego
+- some new ego & tweaks to existing egos
+- 3 new light types: Everburning torches, Dwarven lanterns and Feanorian
+ lamps
+- New ego lights: of Brightness, of *Brightness*, of Illumination, of Boldness,
+ of the Shadows, of Infravision, of the Eternal Eye
+- The Phial of Galadriel is now level 20 rarity 10, the ego lights & other
+ perm lights should be enough until then
+- Ego light: of Fading, will make non-permanent the permanent lights :)
+
+01/06/2001
+- Add auto curse
+- The One Ring will not be cursed when generated but will have the auto_curse
+ flag, so it is possible to use it and take it off for VERY brief periods...
+ but if it becomes cursed while you're wearing it .. you're stuck with it
+- The same applies for the Toris Mejistos (except that it's not permacursed)
+- Moved options around
+- No more books of lore, they are now Cloaks of Mimicry (and can be ego items
+ or randart)
+- Monsters that can suicide cannot be random quest monsters
+- Alchemist artifact creation totally changed. It now takes player level
+ essences of magic and 1 hp to "imbue" a normal non-artifact, non-ego item
+ into a pseudo artifact.
+ You will then wield/wear it and it'll gain some xp when you do
+ (reducing the amount you gain)
+ When you think you have enough xp, you finalize it (actually select the
+ flags that now cost xp) with the xp it has and the pval you choose.
+- Did more spelling updates on this file -- Improv :)
+- Finished moving the class-race combos in tables.c to a cleaner
+ format -- Improv
+
+02/06/2001
+- Copied Vanilla random artifacts name generator
+- Random ring & amulets are now "The Ring of foo"
+- Lights can be random artifacts
+- Described all Valarin spells
+- All non spellbook spells (Mindcrafters, necromancers, mimics) got a
+ description
+- All harper songs described
+- DragonRiders will learn to fly at level 17 (but they always can levitate),
+that's an old Divia suggestion
+- Magestaves of Spell still carry 2 spells, but they are randomly generated
+ using the runespell (magic of the Runecrafters) system
+- First draft of monsters gaining xp & levels
+- Magestaff of Mana & Power renamed to Magestaff of Wizardry (suggested by JLE)
+
+03/06/2001
+- Beginning work on unifying the load/save code to make
+ maintenance easier -- Improv
+- load2.c and save.c are now one file, loadsave.c ... Will now be working on
+ moving them to using unified functions so this code will be easier to
+ maintain. -- Improv
+- Reworked monster AI. A monster can now be enemy, neutral(oriented toward
+ player or monster or full neutral), friendly, pet or companion(will follow
+ you on other levels)
+- Pets (and other friends) will be less stupid
+
+04/06/2001
+- You can now assign a target to a pet
+- The Phial will now have a similar effect to song of morning (tribal)
+- The number of companions killed is taken into account in the score
+- New quest at Lothlorien, the poisoned water, with an unique reward,
+ a DSM of elvenkind (cannot be generated under normal circumstances)
+- Renamed Nibelungs to Petty Dwarves
+- New curse, you cannot drop the item
+
+06/06/2001
+- Multiple messages will show up as only one message with a multiplier
+- New Minas Anor layout (MUCH BIGGER) by Mynstral
+- Fixed the princess not appearing (she WAS there but got killed)
+
+08/06/2001
+- Fixed Mormegil (it was possible to use it with magic)
+- Companions stay even if you use the overview wilderness mode
+- Some monsters will tease the player but always stay out of melee range
+- A new pet command to make them forget their target
+- New ego type for heavy armors (Dwarven) with + to STR and maybe CON
+- Ammo & diggers doesn't add the tohit/todam to your total
+- Herbal healing at Gondolin will cure black breath
+- New store type: Master Archer
+- Rings can be ego objects now
+- Scrolls can be ego now (Fireproof)
+- Wands & Staffs (except of wishing) can be ego item: of Plenty
+
+10/06/2001
+- Possessor now have mana (based on INT) and use it to cast the spells of the
+ monster they are using
+
+12/06/2001
+- Deathmolds can now use the overview wilderness map but the travel time is
+ higher and there is a chance to not blink right and move onto an undesired
+ square
+- Changed Summon Cyberdemon to Summon High Demon (with the incoming new JLE
+ demons it'll be nasty)
+- Added Possessor monsters, they'll hunt corpses and incarnate into them !!
+ Now you must fear even dead monsters
+- The Phial of Undeath now has a radius of 5
+- Unique monsters list is now sorted (but Morgoth is always at the bottom)
+- Add monster traps from PsiAngband. Rogues can now set them (with the powers
+ menu, 'U' key)
+ Direct quote from Psiband change file:
+ Rogues can set traps for monsters. This requires a "trapping kit" as a trigger
+ and something to "load" the trap with.
+ All scrolls, potions, wands, staffs and rods can be used (with the appropriate
+ trigger) as traps to confuse, poison, teleport, genocide, ... unwary monsters.
+ But standing next to a trap with area effects will hurt the player, too.
+ There are also traps that shoot ammo: hidden catapults, bows and xbows.
+
+ Some monsters can disarm traps, and a monster that has disarmed one of your
+ traps will learn how to disarm all of them...
+
+ Ammo Traps can have (+hit,+dam) just like bows. They can also be enchanted.
+ All traps can have a [+AC] showing how hard it is to disarm it.
+ There are also ego and artifact traps.
+- Add the ego & artifacts trap kits from PsiAngband
+
+15/06/2001
+- Hallucination monster attack (JLE will use it for the review of r_info)
+- You can use up to 5 R_CHAR_x flags in the F: line or re_info.txt to specify
+ races to which the ego powers are available
+- Crusade realm described
+- The Star of Elendil now has a light radius of 4
+
+17/06/2001
+- The dungeon info file (d_info) now allows more than one monster generation
+ rule. The R: line specifies the percent of monsters affected by the rule
+ and the mode of the rule(AND, NAND, OR, NOR). So it is now possible to
+ have 60% of orcs, 30% of trolls and 10% dragons
+- New dungeon in Mirkwood, Dol Guldur !
+- Sorcerors cannot use Valarin (prayers should not be available to mages) and
+ Tribal (instead they get Spirit)
+- Fixed some misspellings in lib/help/ -- Kusunose
+
+18/06/2001
+- Auto pickup option now defaults to false
+- Fixed bugs with the cursed ego items
+- Described the remaining Magery spells
+
+19/06/2001
+- Point based character generation
+
+20/06/2001
+- Finish cmovie support !
+- Added an interface to cmovie (press | key in both normal or roguelike set)
+ It asks for a name (it will add the extension itself) and then if you wish
+ to play or record it.
+ The cmovie files (.cmv) are located in lib/cmov, note that they quickly
+ become huge and so you REALLY should compress them before sending to friends
+- Fixed a bug that prevented mutations from being correctly cleaned
+
+21/06/2001
+- Special artifacts can be placed anywhere in a_info, they just need the
+ INSTA_ART flag
+- Emptying lite warnings should work now
+- Added new quest, not found in a castle.. I won't say anything more :)
+
+23/06/2001
+- Added 2 new dungeon flags:
+ LIFE_LEVEL will generate levels with a cellular automaton algorithm (looks
+ like a game of life)
+ EVOLVE will make a LIFE_LEVEL be continuously parsed by the cellular
+ automaton algorithm while the player moves, resulting in a living effect
+- New dungeon: The Heart of the Earth, branching on level 25 of Mirkwood
+ it uses the evolving algorithm :)
+- Added the new Minas Anor (one map screen total). Fully functional
+ (except for the random terrain via the plasma generator *hint,
+ hint -> DG*) --Mynstral
+- Moved the rarity of the Phial of Undeath to the one of Galadriel
+- Add Death Orb monster as suggested by Prfnoff a long time ago.
+ They only move when in LOS. They multiply (quickly) and can hit to parasite
+ which will make a new death orb spawns out of you later
+- Fixed a bug in cmovie that messed up the recording when the user specified
+ different char than the normal ones
+- Loadsave work is now pretty much complete. Barring the addition of
+ transparent compression, which might happen later, there shouldn't be any
+ reason why savefile compatibility should ever break again. -- Improv
+
+24/06/2001
+- Several bugfixes -- Kusunose
+ Player's symbol was never displayed if VARIABLE_PLAYER_GRAPH or
+ USE_GRAPHICS was #undef'ed
+ Eating a corpse sometimes crashed the game.
+ Stealing from a monster sometimes crashed the game.
+ Nether immunity won't work if player had nether resistance.
+ and some minor bugfixes.
+- Birth classes selection is now more user friendly
+
+27/06/2001
+- D: line enabled in k_info.txt
+- Added ingame information about the different objects kind one can find
+ It is accessed via the observe key ('I' in original keyset)
+
+28/06/2001
+- Add back the Eol quest at Gondolin, but it's now dynamically created
+ (the level layout and trap places are random)
+
+30/06/2001
+- Reimplemented Nirnaeth Arnoediad quest at Gondolin, but with a different
+ reward
+- Add a spoiler menu to the help menu, thanks to Dawnmist
+- Fixed a bug; Steal Item Trap sometimes crashed the game. -- Kusunose
+- Working on rewriting more documents in lib/help/ -- Improv
+
+03/07/2001
+- Fixed a long-lived bug with power mages. This greatly affects play-balance
+ with them -- no longer will the cost of many of their high level spells
+ be merely 1 mana. Also, removed some of their effects that never really
+ worked anyhow. -- Improv
+
+07/07/2001
+- Some more misspellings on this file are fixed. -- Kusunose
+
+11/07/2001
+- Cannot add essences to artifacts
+- Object descriptions are added in the character dump
+- Notes are saved in lib/note
+- Priests and paladins begins with 10 times more grace with their god
+
+12/07/2001
+- Rods with rod tips attached sells for higher price
+- Down shafts cannot get you out of a dungeon
+- Cannot enter water and such in overview mode when too burdened
+- The no_pickup_corpse option changed to prompt_pickup_heavy which will ask
+ a confirmation before picking up objects that might slow you
+- Add an menu to the option screen to dump/load options to a pref file
+- Fixed a bug with dungeon guardians being generated more than once
+
+13/07/2001
+- Unique list is now in 2 columns
+- Race selection screen made more user friendly with race desc
+
+14/07/2001
+- Reworked the spell system
+ Now each spell got a level that you increase as you learn it more and more.
+ The higher the spell level is the more damage it does, the more time it
+ lasts and such.
+ Also each class have a specific max on the number of levels they can achieve
+ in each spells. This should help the high mage class since they get twice the
+ max level of mages. Also illusionists gets more than mages.
+ Also note that sometimes one more level wont change anything while 2 or 3
+ more WILL change. And lastly some spells are not affected by levels at all
+ One last thing, sorcerors cannot learn spells and thus cannot increase their
+ spell level which will always be 1
+- Player races now have inherent body parts, so a deathmold cannot use
+ headgear or boots but can wear more rings
+- New DarkGod sentences by Static Chaos
+- Fortune cookie by SC
+
+15/07/2001
+- The more you known a spell the faster it is to cast
+- Every class can now choose which spell/prayer to learn
+- Began reworking the spell bonuses(from spell levels or mage staves) to a much
+smoother distribution, so if a point if used it WILL have impact. Done the
+ Valarin Realm
+- Done Magery Realm
+- Done Symbiotic Realm
+- Done Music Realm
+- Done Shadow Realm
+
+16/07/2001
+- Done Chaos Realm
+- Ingame contextual help(on by default, can be turned off in the options)
+ Only few stuff got help right now, but that'll be extended
+
+17/07/2001
+- Spell list with levels included in the character dump
+- Updated docs thanks to Dawnmist
+
+18/07/2001
+- Hypertext help system, use up/down/space/- to scroll, left/right to move
+ between links and enter to activate a link
+- Race mods selection screen upgraded
+- The k_info artifacts will have a name again(was a stupid bug)
+- Done Nether Realm
+- Unified savefile loading screen. Now when the game is started it presents
+ a screen allowing to create new characters or to load/destroy existing ones
+
+19/07/2001
+- No more breeders in poisoned quest
+- Add an option to show exp needed for next level instead of total exp
+- Objects with the temporary flag will be destroyed when it's timeout
+becomes 0, allowing spells to summon fiery blades and such
+- Valarin Realm got a spell to create a temporary Holy Avenger
+
+20/07/2001
+- Created a cygwin makefile (makefile.cyg) for Windows -- Dawnmist
+- Split spoiler help files into true spoilers and newbie help files -- Dawnmist
+- Continued minor edits of help files. -- Dawnmist
+- Possessors reworked again
+ Now they get between 1 and 20 mana based on the spell rate of the monster.
+ When you cast a spell the failure rate is determined based on the player
+ level, player wisdom, monster level and spell difficulty. If the spell
+ fails it is still cast but you lose a few mana. If it succeeds you don't
+ lose mana. If it takes you below 0 mana you are forced to leave your corpse.
+- Done Spirit Realm
+- Done Tribal Realm
+
+22/07/2001
+- fixed a bug in project_meteor() that crashed the game. -- Kusunose
+- Updated option.txt for exp_need option. -- Dawnmist.
+- Continued edits on help files. -- Dawnmist.
+- Done Crusade Realm
+- Rewrote bldg.txt for Pern 4.x.x. -- Dawnmist.
+
+23/07/2001
+- Redone help file colour scheme to allow orange/yellow hyperlinks -- Dawnmist.
+- Continued edits on help files -- Dawnmist.
+- Spell checked tables.c -- Dawnmist.
+- Spell checked this file :-) -- Dawnmist.
+- Updated features and objects in dungeon.txt help file. -- Dawnmist.
+- JLE reworked the monster list, some monsters are gone, some monsters are
+ changed and some are new
+- Along with r_info JLE also modified d_info, big thanks to him
+- No more quest for shelob in mirkwood(she guards cirith ungol now)
+ Instead you must hunt the necromancer that is said to lurk in dol guldur
+- As long as sauron his alive the nazguls cannot be permanently slain
+- Attacking a nazgul no longer destroy artifacts(well it can, but the chance
+ is 1 in 1000)
+- Done Daemon and Sigaldry realms
+
+24/07/2001
+- SURPRISE!! I did something (but what it was is a surprise too) -- Mynstral
+- Added debug 'B' command, changes body -- Improv
+- Added colours and hyperlinks throughout command.txt -- Dawnmist
+- Updated most command descriptions in command.txt -- Dawnmist
+- Minor updates to some other help files -- Dawnmist
+
+25/07/2001
+- Massive merges from my internal cvs tree of loadsave.c
+ Hooked experimental bzlib integration in an #ifdef
+ Gutted the old 'encryption' code
+ Hoping to gut RLE if bzlib changes work out
+ Added nice sentinel function -- Improv
+- Fixed class selection screen bugs, fixed savefile manager so it will work
+ with unix. -- Improv
+
+26/07/2001
+- Added a quest map for Gondolin. -- Mynstral
+- Gondolon last quest: Invasion of Gondolin !
+- Upgraded max suport to the new birth interface thanks to pelpel
+- Several fixes thanks to pelpel
+- Add an option to use either the new r_info coloring scheme(based on V one)
+ or the old one(based on Z)
+
+28/07/2001
+- Fixed a lock problem with global.svg on multiuser system thanks to pelpel
+- Done Magic Realm
+- Done Druidisic Realm
+
+30/07/2001
+- One can press 4 (left arrow) in class selection to get back to meta-class
+ selection
+
+02/08/2001
+- Updated several *_info files thanks to JLE, new arts, ego, objects, ego
+ monsters
+- Fixed a bug in ego monsters, now they works perfectly :)
+
+03/08/2001
+- Done Illusion Realm
+- Added macrofaq.txt to help system -- Dawnmist.
+- Updated/fixed a few problems in the help files, and linked in new macro FAQ -- Dawnmist.
+- Increased chance for player ghost to appear
+- No more ego monsters in town
+- Spectre subrace can only use 1 ring
+- Made bree and gondolin finally use the plasma generator, and added two terrain
+ features. -- Mynstral
+
+04/08/2001
+- Fixed a bug with ego monster generation that inversed ego rarities, thus
+ explaining the incredible numbre of spectres running around
+
+05/08/2001
+- Fixed the orc level in the moria
+- Bladeturner is now an UNTIMATE artifact, but can be activated for invulnerability
+- Major rewrite of magic and class help files -- Dawnmist.
+- Fixed a bug with saving/loading while doing invasion of ogondolin quest
+
+07/08/2001
+- Fixed Tribal realm
+- Major rewrite of race modifier help files -- Dawnmist.
+- Spellchecked this file - Improv
+- Reintroduced water hounds - Improv
+- Major rewrite of race help files -- Dawnmist
+- Graphics for shop doors -- Kusunose
+- With help of Antimatter, squashed item bugs related to negative pvals and
+ the signedness of some functions. -- Improv
+- Done Prayer Realm
+
+09/08/2001
+- Fixed a bug with evolving dungeon when saved/loaded
+- Only the quest monsters counts for quest, not clones, not normaly generated
+ ones
+- undead ego monsters cannot be mortal
+- Monster ego name can appear before or after the monster name(:B or :A at the
+ end of the W: line)
+- Object ego name can appear before or after the object name(B or A on the X:
+ line)
+- Fixed a long-lived item-pickup bug -- Improv
+
+10/08/2001
+- Ego monsters will be drawn in gfx mode
+- Add a birth menu to select the god to worship, druids are forced to get
+ Yavanna, priests and paladins are forced to have a god, others can choose
+ to be atheist
+- No more multiple artifact arrows
+- Continued edits on the help files -- Dawnmist
+- Completed the spell descriptions for the Shadow Realm -- Dawnmist
+- Vampiric weapons can be sold again
+- Staff & Wand of nothing of plenty cannot exist
+- Staff of wishing CAN be of plenty
+
+11/08/2001
+- fixed a bug with esp and flags5 not being saved thanks to kevin w.thomas
+- fixed a bug in nirnaeth quest
+- Reduced sorceror hp penality to -10%
+- Removed the screen coloration when berserk/wraith/goi
+
+12/08/2001
+- Fixed invasion of gondolin quest
+- Fixed lightning res amu
+- Add the Merchant class(thanks static chaos), they can get loans(watch out if
+ you dont pay back quickly) and request items at the merchant guild.
+ they can appraise items, identify items, warp items in chests. They get
+ an object based ESP(see monsters carrying objects) at higher levels, they
+ constantly detect objects around them and they can use portable holes
+ to get a bigger inventory(but items in it weights more)
+
+13/08/2001
+- Fixed font-ibm thanks to pelpel
+- Merchants gets the midas touch
+- Vanilla town option removed. Astral mode now make the player start in the
+ Halls of Mandos(levels 1-98) which do not have any entrance from the
+ wilderness(or any dungeons). When they get out they are in the wilderness
+ and can start playing normaly. And uniques cannot be generated in the halls
+ of mandos
+
+14/08/2001
+- Updated font-ibm and font-win thanks to pelpel
+- When selecting the number of random quests, * will get a random number
+- Score list now shows the subrace, but it breaks the compatibility with old
+ ones
+- Reduced xp needed by monsters to gain levels
+- Ego items are wishable now. i.e you can ask for "fiery dagger"
+- Updated font-ibm and font-win thanks to pelpel
+
+14/08/2001
+- Added the map for a new quest. -- Mynstral
+- Finished the Last Alliance quest
+- Number of companion a player can have at max depends of the class
+ necro gets 1 + (plev / 10), beastmasters 4 + (plev / 10, harpers 5
+ all others 2
+
+16/08/2001
+- Fixed ambushes
+- Added merchant help
+- updated mac suport thanks to pelpel
+- Reduced by about 6mb the memory req
+- Hidden towns are generable
+- Fixed a bug with weapon specialty at birth -- Kusunose
+- New item ability, some rare rings can add %(based on pval) to the chance
+ of getting a critical hit
+- Objects can now shimmer(ATTR_MULTI), but only base types (k_info) can have
+ the flag
+- Features can now shimmer, note that this can be slow. Please report if it
+ is unbearable on your machine(you can also disabled the avoid_other option)
+- Reduced memory footprint again
+- Reworked the 'U' power menu to be more nicer and intuitive while still
+ allowing macros. Each powers gets a fixed number that can be used as a
+ prefix to the command to bypass the menu. For example teleport is 5 so
+ to directly get teleport one can always do 05U
+
+17/08/2001
+- Described Chaos realm
+- Described Nether realm
+- Described Sigaldry realm
+- described Spirit realm
+- Fixed a bug with random quests
+- Fixed a bug in note taking, thanks to pelpel
+- Fixed the interact with visuals menu thanks to pelpel(the fix is not even
+ in vanilla yet :)
+- Fixed cmovie recording
+- Merchants can request ego items(same interface as wishes)
+- Towns in dungeons now get townpeople in it ... leveled townpeople
+- Eggs are now , instead of o
+
+17/08/2001 - PernAngband 5.0.0 aka "Mirmidonic Carbonizer"
+- Birth options now appear in the normal option screen, but as read only
+
+18/08/2001
+- Special levels reworked
+- Volcano is now Mount Doom and is found on level 65 of Mordor
+- fixed help system crash thanks to pelpel
+
+21/08/2001
+- fixed the hypnotic gaze bug
+- reduced between gates damage
+- daemon books can be enchanted
+
+19/08/2001
+- Beginning work on a new, better status screen. For now, it's assigned
+ to the debugging menu as the 'A' command. -- Improv
+- fatespoil.txt changed to fatespoi.txt
+- rogues get some shots for their trapkit at birth
+- Cannot dismiss the princess anymore
+
+23/08/2001
+- Fixed the special levels, thanks to mynstral
+- Described Daemon realm
+
+23/08/2001 - PernAngband 5.0.0b aka "Mirmidonic Carbonizer II"
+- Default pref files are in lib/pref
+- Random quests are disabled when using ironman_room
+- Add auto_more option -- beware it can be very dangerous
+
+24/08/2001
+- Increased general store price limit
+- Trapkits added to squelch list
+- Fixed a bug when displaying some k_info artifacts
+- Wand of Gandalf and Thrain are now k_info artifacts
+- Fixed 2 bugs with rogue traps
+
+25/08/2001
+- Necromancers can turn pets into companions
+
+26/08/2001
+- Special levels are no more fully known
+- Fixed fountain bug; shape of XXX did not take effect -- Kusunose
+- Described Illusion and Tribal realm spells -- Dawnmist.
+
+27/08/2001
+- Fixed bug with scrollable list of powers
+- Added Magical diggers, they grant stone to mud power
+- Added the Corrupted subrace gaining corruptions as they level thanks to
+ Luc French
+- Changed mutations to corruptions
+- Dragonriders are no more considered evil
+
+28/08/2001
+- Fixed some cmovie bugs
+- Fixed *godness* song on non artifacts
+- Greatly increased herbal healing price in gondolin, since it cures black
+ breath
+- Fixed bug with store item creation -- Kusunose
+
+29/08/2001
+- Totaly rewrote the random artifact system. It now use an external ra_info
+ file to define what power the artifact will get. The format is very similar
+ to e_info(expect it doesnt include the 5 flags sets). Each entry define for
+ which tval/sval it can be applied. Then when the game wants to create a
+ randart it simply defines a number of powers to grab and randomly pick up
+ the powers from ra_info(following the restriction, rarity and levels).
+ This allow a totaly change of the randarts if needed, an easy addition of
+ new powers, ...
+
+30/08/2001
+- Hidden doors are now hidden, in mirkwood they will look like trees, in
+ barrow downs they will be mountains or trees, and so on
+- Beastmasters can turn a pet into a companion
+
+31/08/2001
+- Half elf lost the str penality
+- Half orcs and half Trolls or now Orcs and Trolls
+- Saved levels are deleted when a new character is created
+- Renamed Holy Advangers to weapons of Aman, thanks to Timo Pietila for the
+ idea
+- Cannot reset recall to Mount Doom
+- The game time is now counted with the elven calendar (reckoning of Imladris)
+ (It isnt PERFECT, every 12 years it should have years of 368 days, but I
+ dont think many will get upset :)
+ A year is 365 days, with 6 months (Tuile, Laire, Yavie, Quelle, Hrive and
+ Coire), 3 middle days(Enderi) and one starting(Yestare) and one ending
+ (Mettare) day.
+ The game begins the 43rd Yavie the year 2890 of the third age, note that it
+ is the birthday of Bilbo :)
+- Note files (mainly) use the elven calendar to record things
+
+02/09/2001
+- Fixed several bugs (runespell deletion, ego wand and staff stacking etc)
+ thanks to oops -- Kusunose
+
+03/09/2001
+- Changed the 'Lev xxx' thing to a 3 letter name corresponding to the current
+ place(dungeon, orc cave, ..)
+- Enabled f_info D: line with an index(0 is general desc, 1 is to (forbid)
+ tunneling)
+- Changed the way total weight is calculed thanks to Kieron Dunbar
+- One can edit his background history at birth :)
+- Lich ego monsters can use the base monster powers
+- Raise death can bring a lich
+
+04/09/2001
+- Enabled E: line in f_info(same format as d_info)
+- F: lines enabled in lib/dngn files, they specify flags for the level
+- Fixed a bug with special level generation
+- Add bleeding and poisoning monster effects from EyAngband
+- The One Ring quest
+
+05/09/2001
+- Fixed the crashing bug after level 100 in angband
+- Gave Sauron 30% chances to drop The One if it is not already created
+- Code now allows bi-ego objects, thoght they are impossible to generate yet.
+ It will allow additing weapon/armors qulities(crude, broken, ..) easily
+- Add a quick start option that let you use the same char as you previous one.
+ Everything is the same execpt life rating
+
+06/09/2001
+- Abort menu in the Windows version is now compile option, disabled by
+ default. -- Kusunose
+
+07/09/2001
+- When a new day comes the game tells the player(at midnight)
+- Drain hp and mana are cumulative
+- Fixed file existence function for dumb systems :)
+
+07/09/2001 - PernAngband 5.0.1 aka "Brunswik Disipator"
+
+09/09/2001
+[B]- Turns in scores are based upon the start of the game not of the year
+[M]- Mimic are REALLY mimics, they can look like any object and appears as
+ objects when looked at. And they got an emperor
+[I]- Enhanced realm selection menu
+[B]- Farmer maggot shows up in the unique list
+[M]- Monster vs monster damage is now x3 to shorten fights
+
+10/09/2001
+[P]- Changed the boring Half-Giants race to the much more interresting
+ Beorning race. Descandant of beorn they can all shapeshift into a powerful
+ bear form at will. In bear form you cannot use weapons, gloves, shields
+ and boots. But you get powerful stunning/slowing/wounding attacks
+ (claw, bite, swat, hug) and innate blows. You also get bonuses and minuses
+ to some stats and to to hit, to dam and speed
+
+12/09/2001
+[O]- One Ring cannot be dropped by traps
+[G]- Implemented luck, an invisible stat which can only be approximately known
+ via self knowledge spell/potion. It affects quite a few dice rolls.
+ Each race gets a luck modifier, for most it is 0, but some gets penalities
+ or bonuses(hobbits are a lucky bunch).
+ Some objects also can grant luck
+
+14/09/2001
+[D]- The graveyard will now project a raise death spell on all bodies, meaning
+ you'll have to kill a monster, and destroy its body before it get raised
+ back in the form of a skeleton, spectre, lich, ...
+ PS: MOUHAHAHAHHAHA
+
+15/09/2001
+[B]- Bugfixes -- Kusunose
+ A message "XXXX blocking your way" was displayed when you went through
+ some walls or trees.
+ Hidden doors remained hidden if they were detected by spells, or if they
+ were opened by monsters and then closed. Thanks to kobayasi.
+ and some minor fixes.
+
+16/09/2001
+[m]- Implemented lua scripting language! More on that on the to-be docs
+ You need to get lua3.2 to make it work.
+[m]- Integrated lua3.2 source into the source, no more need for external
+ librairie
+[M]- Fixed store lockup bug thanks to Kusunose toru
+[I]- Partial 8x8 tile update -- Dawnmist.
+[m]- Cygwin makefile update -- Dawnmist.
+[P]- Externalized player races and histories
+
+17/09/2001
+[P]- Elves cannot be vampire anymore
+[P]- Hobbits get intrinsinc xtra might with slings at lvl 25
+[P]- Extended the possibilities of the player races file, it is now possible to
+ select what flags will be applied at what level and for what pval(if
+ revelant)
+[B]- Fixed non connected stairs on quests, thanks to luc french
+[I]- Removed screen dump/load commands, redundant with cmovie, and frees to keys
+[I]- Y( ( in roguelike keyset) key to chat with monsters
+[G]- Changed the way to get the lost hobbit quest
+[B]- Fixed a bug with eat magic corruption
+
+18/09/2001
+[G]- System shock when thrown out of one's body while wzearing cursed items
+[G]- Set the joke/Z/cth monsters off by default
+[P]- Externalized subraces to p_info
+[M]- No more joke mosters as quests
+[m]- Integrated an automatic help file converter(creates html files), command
+ line option -h
+
+28/09/2001
+[m]- makefile.org now supports Lua, uses Improv's installation rule
+ in all cases, it also understands make depend as well.
+ Read it, make necessary changes for your system, then make depend
+ followed by make.
+[B]- validate_bg() referenced a null pointer.
+[m]- CodeWarrior project file for the Mac port supports Lua.
+[m]- main-xxx.c for the Mac is now in sync with my Vanilla port -- pelpel
+
+29/09/2001
+[B]- Fixed herbal healing price
+[I]- 16x16 bmp/mask added to the cvs
+[G]- Cth/Z/Joke monsters options appears in char dump
+[B]- Fixed Crusade spell book browsing bug (Bug list #33) -- pelpel
+[B]- Fixed a bug; the first harper book was never sold in the bookstore
+ (Bug list #15) -- Kusunose
+
+30/09/2001
+[G]- Increased min player level for some dugneons
+[I]- Message can be multi colored
+[m]- Yesterday I got OS 10.1 and found that it fixed the new window
+ position bug (search for (_ _#) in main-crb.c), so I conditionalised
+ the workaround -- pelpel
+[B]- Fixed the combined rod pricing (Bug list #30) -- pelpel
+[B]- Typo in files.c: cht_monsters -> cth_monsters. Replaced non-ASCII
+ 0xa0's in angdos.cfg with 0x20 (i.e. space) -- pelpel
+
+01/10/2001
+[B]- Monks and Bear form didn't have combat messages -- since many lines
+ in the current flavoured messages mention weapons, I introduced a
+ hack to temporarily disable flavored_attacks for them, then just
+ process messages as usual, and restore the flag later (Bug list
+ #44 and #53) -- pelpel
+
+02/10/2001
+[B]- The trap/door destruction spell didn't destroy doors (Bug list #25) --
+ use of floor_type[rand_int(100)] results in a 'natural' terrain,
+ but isn't compatible with digging etc. Do they need fixing too? -- pelpel
+[m]- The main window resist resizing in the Windows port, like the Mac
+ ones (Arcum's problem, future Bug list #74 and #75) -- pelpel
+
+07/10/2001
+[B]- Fixed PowerMage mana consumption bug (Bug list #114) thanks to krosky
+ -- pelpel
+
+08/10/2001
+[B]- Fixed light source stacking bug (Bug list #7) -- Kusunose
+
+21/10/2001
+[M]- Add NO_CUT to some monsters
+
+22/10/2001
+[I]- Colors to news.txt
+
+25/10/2001
+[M]- Added gamma correction function taken from V2.9.x in util.c, since
+ new X11 file requires it, so does my main-gtk.c, ready to be submitted
+ -- pelpel
+[G]- Cannot use overhead wild map if being recalled
+
+26/10/2001
+[B]- Fixed long-lived dragonrider bug (xtra1.c), Darkgod please check to see
+ if the behavior is as you want it -- Improv
+
+27/10/2001
+[P]- Externalized classes to p_info.txt
+[P]- Externalized meta classes to p_info.txt
+
+28/10/2001
+[B]- Fixed display of race/subrace/class flags
+[I]- Add some color to the identify screen
+[I]- Ignore Acid, elec, fire and cold are merged in identify screen
+[I]- Inventory/equipment letter are color-coded when *identified*
+[O]- Item sets ! Some artifacts are working together. If you wear them
+ at the same time you get bonus powers. You can get partial bonuses i.e:
+ You have 2 artifacts of 3 artifacts set, you will get some bonuses for
+ them but not all.
+[M]- Add NO_CUT flag to monsters needing it thanks to
+ Runescrye
+
+01/11/2001
+[B]- Changed king to the actual winner name in the quest dump
+
+03/11/2001
+[m]- Upgraded to lua4
+[m]- It is now possible to define new magic powers(ala mindcrafter, necro, ..
+ powers) with a small lua script
+[O]- New item set the Dragon Slayer
+[m]- New gtk port thanks to pelpel !
+[m]- Mac makefile equivalent is updated, now with lua4 for all targets -- pelpel
+[m]- src/lauxlib.h is synchronised with src/lua/lauxlib.h -- pelpel
+[m]- LUA_NUM_TYPE in makefile.org is removed because src/lua/llimits.h now
+ has correct (read integer :-) default -- pelpel
+
+04/11/2001
+[M]- All dragons now shimmer, according to their breath types(took from LM's 4GAI)
+ That is also thanks to the neat info editor of static chaos :)
+[m]- Change my email adress to darkgod@pernangband.net, adds ideas@pernangband.net
+ and bugs@pernangband.net thanks to Tom Le
+[I]- Added gamma correction subcommand to "Interact with colours", and
+ updated main-gtk.c and maid-x11.c to work with that (they formerly
+ used an environment variable for this). Also added gamma correction
+ support to main-ibm.c and main-dos.c. CAVEAT: tiles don't react to
+ changes in gamma immediately, for performance reasons -- pelpel
+
+05/11/2001
+[m]- Removed notice of sf_extra value from savefile load code. We probably should
+ make the spot where it loads it a ls_skip and remove that older family of
+ versioning variables -- Improv
+[m]- Took a stab at code to make a dynamically allocated loadsave section. It's
+ not called because it needs support code that presumably DG will write.
+ With any luck it'll be sufficient to do the job -- Improv
+[I]- Updates to experimental new status screen -- Improv
+[B]- Fixed a bug that caused all monsters drop inappropriate things. Hydras now
+ just drop money again. Yay. -- Improv
+
+06/11/2001
+[m]- Added an automatic nice changelog generator based on changes.txt. To all people
+ with cvs access, please use the new format for changes.txt.
+ It is activated with the -c flag: pernangband -c changes.txt changes.nice
+
+07/11/2001
+[P]- It is possible to define player power(under the U menu) with lua scripts
+ (and thats easy :)
+
+09/11/2001
+[B]- (IBM, DOS, GTK) gamma_val is reset as well as old_gamma_val
+ when invalid values are specified, ensuring correct reaction -- pelpel
+[B]- File-Save was never active in the Gtk port -- pelpel
+[m]- gamma correction for Windows port. I'm too lazy to save it in preferences,
+ though. May be redundant since many drivers provide similar functions
+ -- pelpel
+[G]- Flat places no more have stairs, they have ways to next/previous areas that
+ are always placed on the edge of the level
+[D]- Level borders are now of the same type as the level walls, no more forest
+ surrounded by granite walls
+[B]- Haste Monster cannot haste as much as before
+[I]- Pets infight wont disturb the player anymore if disturb_other is off
+ (it is by default). In combinaison with auto_more it will totaly ignore
+ pet messages
+[O]- New item set, The Trinity
+[m]- Lua-ified item types(can add new ones with simple lua script)
+
+10/11/2001
+[m]- Added Gtk entries in the system pref files, also enabled new graphics
+ for X11/Gtk ports -- pelpel
+[I]- The target prompt will indicate if the targetted monster is a quest
+ monster or not
+
+12/11/2001
+[m]- Updated main-xaw.c to that from the same version as main-x11.c
+ i.e. 2.9.2. It now reacts pref colours and has graphics mode
+ (doesn't work with 8bpp though). CAVEAT: main-x11.c and main-xaw.c
+ should free unused colours after allocating new one. -- pelpel
+[G]- Troll galde quest only available at night
+[G]- Genocide is forbiden in the last alliance quest
+[G]- Invasion of gondolin quest will wait the end of the current quest to be
+ generated
+
+13/11/2001
+[m]- Variable savefile ! Lua scripts(or C scripts(quests)) can now add stuff
+ to the savefile without ever breaking the compatibility !
+ See an example ofthat in test.lua
+[B]- SPECIAL_GENE objects were generated out of context -- pelpel
+[B]- Inappropriate monsters could be assigned to randquests in
+ rare occasions. CAVEAT: it now uses an infinite loop to
+ avoid illegal choices! -- pelpel
+
+14/11/2001
+[G]- New quest, try cahtting with maggot instead of killing him, you murderer !
+[m]- Re-implemented object allocation table caching -- pelpel
+
+15/11/2001
+[B]- Inserted /* Paranoia */ code in kind_is_theme() to prevent "(Nothing)"
+ from generated due to missing drop theme in r_info. Player ghosts
+ had this behaviour as far as I know -- pelpel
+[m]- Changed the rand quest reward generation to make it compatible with
+ yesterday's fix -- pelpel
+[O]- Added new ego rod, of simplicity, thanks to Runescyre for the idea
+[m]- Max number of classes, races, subraces and realms is now 64
+
+16/11/2001
+[I]- Stole the death screen of KaMband, much nicer :)
+[P]- Removed the astral option. It is replaced by a subrace, Lostsoul, they
+ have intrinsinc wraithform(until they get back to the surface), start
+ with some identify scrolls and have all levels enlightened
+[B]- The find artefact fate was able to grant SPECIAL_GENE artefacts -- pelpel
+
+17/11/2001
+[D]- Dragon Lair renamed to Erebor, the misty mountains
+[P]- Rogues gets level/3% more changes of critical hits
+[D]- The wilderness "borders" are now Ekkaia, the Encircling Sea
+[I]- I ran out of keys, so I added an extended command mode. Press '#' (')' in
+ roguelike command mode) to acess to it. You can then enter the command
+ name or a shortcut if the command have one(usualy a command have a 1
+ letter name and a complete name). You can also press ? or help to get
+ the list of commands
+[I]- The time command is avaiable again for roguelike command set in the form
+ of the t/time extended command
+[I]- D/html-dump extended command to take an html screenshot
+[I]- Quest list is now ordered by danger level
+
+18/11/2001
+[B]- Attempting to go down shaft gave the message "I see no down staircase
+ here", thanks to Kevin W. Thomas -- pelpel
+
+19/11/2001
+[G]- Reduced the Spirit Realm Project Force spell radius as it increase levels
+[B]- Entering Nether Realm crashed the game, thanks to M.Itakura
+ -- Kusunose
+[B]- Polymorphing monsters in the wall crashed the game, thanks to to
+ M.Itakura -- Kusunose
+[B]- Sometimes random quest appeared on deep(dlv>98) Angband -- Kusunose
+[B]- Cursed EASY_KNOW items were not squelched -- Kusunose
+[B]- Replaced all checks against RF1_QUESTOR to MFLAG_QUEST -- Kusunose
+[B]- Shafts were not detected by detect stair spell -- Kusunose
+
+20/11/2001
+[G]- Traps have been tweaked down to be a bit less deadly, thanks Runescrye
+[P]- Summoner class! Thanks to Luc French for the idea and quite a bit of
+ the code
+[I]- New splash screen made by Jans
+
+21/11/2001
+[B]- Mathilde was allowed to use wepaon(when possessed)
+[I]- Finished all spells description, thanks to Runescrye
+
+22/11/2001
+[m]- Quests are lua-ified
+[m]- flush() is called before *that* DragonRider question, to prevent
+ catastrophic(?) accidents, taken from Kusunose's Japanese version
+ -- pelpel
+
+25/11/2001
+[B]- (GTK)Widget instance names should have begun with lowercase letters
+ -- pelpel
+[B]- (makefile)LUA_NUM_TYPE macro removed (see my 3/11/2001 mod
+ for makefile.org) -- pelpel
+[D]- Museum(Mathon-house) added to Bree thanks to Kusunose
+
+27/11/2001
+[I]- Added an option to allow the @ to turn into a number when health
+ drops. ideas from Mangband/PernMangband
+
+28/11/2001
+[m]- ANGBAND_DIR_USER is moved to ~/.pernangband on multiuser systems
+ (Unix, GNU/Linux), according to DG's preference, and against the
+ V/Z way... IMPORTANT NOTE FOR 422color USERS: please move
+ 422color.prf to lib/pref directory -- pelpel
+
+30/11/2001
+[B]- I forgot to give Tom Demuyt credits for the Unbelievers idea.
+
+01/12/2001
+[m]- Updated makefile.bcc. It now supports lua4. -- Kusunose
+[B]- The broken sword quest generates the real reward, thanks
+ Dawnmist
+[m]- Windows port now saves gamma_val in .INI file. -- Kusunose
+
+03/12/2001
+[M]- Rejoice ! no more nazguls as random quests
+
+04/12/2001
+[m]- Some patches from Kieron, thanks
+[I]- Kieron save squelch patch, modified to not save from chars to chars
+ it can just be used to dump suqelch to a .prf file and load it
+
+05/12/2001
+[P]- New class from Luc French, the Blade, weaponless fighter able to dodge
+ melee & some spells
+
+07/12/2001
+[B]- One could steal guardians' artefacts and reenter level to
+ obtain multiple copies (buglist??) -- pelpel
+[B]- Fates could be lost during level regeneration (auto_scum and/or
+ too many objects/monsters) -- pelpel
+[B]- (GTK)Fixed terribly stupid menu crash bug -- pelpel
+[B]- Climbing sets can now be pluralized thanks to John Q. Smith
+
+10/12/2001
+[I]- No more annoying infighting monster messages when turning disturb_other
+ off
+
+11/12/2001
+[B]- Randquests and special levels must be immune to all types of level
+ regeneration now -- pelpel
+
+12/12/2001
+[B]- Fixed alchemist art creation bug that allowed to use 4x exp
+
+13/12/2001
+[M]- Krakens lost their 6 ring slots, the tentacles are too big to put
+ rings on them ;)
+[B]- Bashed down doors weren't revealed -- pelpel
+[B]- The Phial and other activatable items didn't have (charging) message
+ -- pelpel
+[B]- There were extra spaces after names of unique corpses -- pelpel
+
+14/12/2001
+[B]- Detected traps should prevent running now -- pelpel
+[B]- The easy disarm code used easy_disarm and always_pickup flags in a very
+ strange way, resulting in undesirable pickup behaviours -- pelpel
+[B]- "Grass with flowers" shouldn't disturb running now -- pelpel
+
+15/12/2001
+[B]- Entrances to vaults used dungeon-themed terrain features,
+ sometimes making it impossible to dig them through -- pelpel
+[B]- DG's 27/11 mod is conditionalised to avoid doing so in the graphics
+ modes, because it hardcoded characters and breaks the graphics support
+ -- pelpel
+[B]- Mimic features are cleared when overwritten by streamers, to
+ avoid, say, tree-looking deep water -- pelpel
+
+16/12/2001
+[I]- Added overlay graphics for ego monsters, player subraces and traps
+ -- Kusunose
+[I]- Added 'search by name' feature to '/' command. -- Kusunose
+
+17/12/2001
+[I]- Player now drops out of the overhead wilderness view when becoming hungry/
+ emptying lite
+[B]- Change in spell stat did not affect Mana/Spells if spell stat were
+ not INT/WIS/CHR, thanks to kobayasi-san. -- Kusunose
+[B]- Change in WIS did not affect Sanity Points if WIS was not spell stat.
+ -- Kusunose
+[B]- Costs of Symbiontic power were not displayed in first page, thanks to
+ kobayasi-san. -- Kusunose
+[B]- Symbiontic power, scare and blind, was misordered, thanks to
+ kobayasi-san. -- Kusunose
+
+18/12/2001
+[I]- Graphics overlay for the three X11 ports (untested). It requires
+ USE_TRANSPARENCY and USE_EGO_GRAPHICS. USE_TRANSPARENCY was ugly,
+ but this... -- pelpel
+[I]- Graphics overlay for the two Macintosh ports, again, untested -- pelpel
+[I]- Add ra_info.txt (randart generator info file) thanks to Runescrye
+
+19/12/2001
+[I]- Graphics overlay for the DOS port (USE_DOS). Also fixed a bug in
+ the overlay support in the Mac ports. I removed always_pict
+ code from the GTK+ port, because it's really slow, judging from
+ the graphics performance on the Mac ports which use the mode
+ to support tile width/height customisation -- pelpel
+[B]- Left good_item_flag for special levels, but adjusted rating boost
+ a bit, because later fixes made +50 boost unnecessary
+ Also toned down the rating boost for randquests -- pelpel
+[D]- New quest in Dol Guldur
+
+21/12/2001
+[B]- Fixed the very old artefact generation bug (present in
+ Angband 2.7.8 -- 2.8.2) that forced "special artefacts" to be
+ generated in the a_info.txt order. It is really problematic
+ in Pern because of the depth of the Phial -- pelpel
+
+22/12/2001
+[B]- A Beorning's father was a Storm Giant sometime, and A Petty-Dwarve
+ was one of several children of a Nibelung. -- Kusunose
+
+23/12/2001
+[B]- Fixed a bug that allowed to throw items with CURSE_NO_DROP.
+ -- Kusunose
+[m]- Added spell spoiler creation in wizard mode. -- Kusunose
+
+24/12/2001
+[m]- (Mac)Eliminated busy waits in CheckEvents and TERM_XTRA_DELAY.
+ It works well on my machine, but I left the original code using
+ "#if 1/0" just in case -- pelpel
+[I]- Player now drops out of overhead wilderness view if a vampire and it's
+ daylight
+
+25/12/2001
+[B]- Reintroduced OoD restriction on randquest monsters (until dlev 49)
+ -- pelpel
+[B]- a fix for RNG problem with 64-bit machines, taken from V(?) -- pelpel
+
+27/12/2001
+[I]- Basic IRC facilities! 3 new extended commands: C to connect
+ D to disconnect and : to chat. Highly experimental.
+[B]- Find artefact fate could cause permanent loss of artefacts. The code
+ now tries to create a randart when there are no good choices instead
+ of always defaults to the Phial -- pelpel
+[B]- Randquests sometimes requested players to kill slain uniques,
+ making it impossible to continue the game -- pelpel
+[B]- (Carbon)Removed the dialogue indicating errors in AEProcessAppleEvent,
+ because an r.g.r.a post pointed out that this can be quite annoying
+ and Apple says they should generally be ignored -- pelpel
+[I]- DOS support for the irc client, needs libsocket:
+ http://www.phekda.freeserve.co.uk/richdawe/lsck/lsck.htm
+[O]- Updated ra_info, thanks to Runescrye !
+[I]- X11 Support for the IRC client.
+
+29/12/2001
+[G]- a New randquest type !
+[B]- Companions cannot be hurt by the player anymore
+
+30/12/2001
+[B]- Reduced all force spells because of the side effects of force attacks
+[M]- Monster breathing/casting force attacks will bounce the player.
+ Just like player's force attacks bounce monsters :)
+[M]- Fixed the IRC client some more.
+
+01/01/2001 - PernAngband 5.1.0 aka "Into the Fire"
+
+2002/02/19
+[B]- Normal (non-vampire) races didn't get mana regenerated at Inn -- pelpel
+[B]- The Mathom house acted like a normal shop when selling. -- Kusunose
+[B]- '-s' crashed the game. Thanks kobayashi for the fix -- pelpel
+[O]- Combining rod and rod tip now considers rod's cheapness flag.
+ -- Kusunose
+[B]- Beastmaster Shanty overpayed a bounty if monster's corpses are stacked.
+ -- Kusunose
+[B]- The random text code left files open in many error cases -- pelpel
+
+2002/02/20
+[B]- WeaponMasters were not restricted with their weapons. -- Kusunose
+[B]- Info text of 'disrupt mind' was described as 'dam'. -- Kusunose
+[B]- Fixed the "automatic ego filter" bug in the squeltch filter -- pelpel
+
+2002/02/26
+[B]- The polymorph random effect could crash the game. Thanks Kevin W.
+ Thomas for the detailed analysis of the problem -- pelpel
+[B]- Stat draining effect of Black breath could crash the game. Thanks
+ Kevin W. Thomas again for the patch -- pelpel
+[B]- With easy_disarm set, players were totally safe from detected traps,
+ whatever messages might say -- pelpel
+[B]- Ego graphics code is now only active when and only when 16x16 tiles
+ are selected -- pelpel
+[B]- Quest entrances/exits now require players to type '>'/'<' commands
+ to move in/out -- pelpel
+[m]- Shimmering terrain features no longer shimmer while running/resting,
+ to make them more bearable on slower machines and slow I/O systems
+ like GCU -- pelpel
+[I]- Incorporated the hopefully improved running code from the CVS version.
+ It uses CAN_RUN and DONT_NOTICE_RUNNING flags for non-conditional
+ checks (i.e. not controlled by the disturbance flags or requiring
+ immunity), so that most running problem can be fixed by editing
+ f_info.txt -- pelpel
+
+2002/02/27
+[B]- Many room walls didn't have the CAVE_ROOM flag set -- pelpel
+[m]- The tunnel code (ordinary one) now performs double check for
+ feat_wall_outer and CAVE_ROOM, so it is safe to use any terrain
+ features for outer wall, including those identical to fill_type.
+ Note: These two changes have very significant effect on the Sacred
+ Land of Mountains. I'm not 100% sure if this is what DG intended,
+ but I believe so reading flags given to it in d_info.txt -- pelpel
+[D]- There should be less not-at-all secret "secret" doors.
+ This does *not* mean that ancient prob, but those mountains enbedded in
+ plain wall in Barrow-Downs, for example -- pelpel
+[I]- Inven/equip/item choice in subwindows now clears to the bottom of screen,
+ to avoid glitches and in accordance with the way it worked -- pelpel
+[I]- Identify and *Identify* don't list known/fully known items in
+ object selection -- pelpel
+[m]- Depth/field name area is now 13 character long, so that names like
+ "shallow water" will fit, and "Lothlorien" is not truncated in GCU.
+ Also added short dungeon name prefix to the depth-in-feet mode -- pelpel
+
+2002/2/28
+[B]- Dungeon town generation could crash the game. Thanks Mogami
+ for the analysis of the problem -- pelpel
+
+1/03/2002
+[B]- Supplied missing suid code in 1) savefile removal action in the startup
+ screen, 2) time table lookup, 3) dungeon savefile removal, and
+ 4) bone file removal. Thanks kobayasi for the patch -- pelpel
+[B]- Less platform-dependent savefile processing code for the game start
+ menu, thanks again for kobayasi. It now uses files.c utility routines
+ for building appropriate savefile names -- pelpel
+[B]- Magical branding of weapon/ammo, when successful, now sets enchanted
+ item's discount rate to 100%, so that one can no longer make vast
+ profit or easily gain his/her deity's favour. Code adopted from
+ T.o.M.E. 2.0.0 CVS -- pelpel
+
+03/03/2002
+[P]- Give Ents scrolls of satisfy hunger instead of some food. -- Kusunose
+[M]- Added a command to dismiss companions in the pet menu. Code adopted
+ from T.o.M.E. 2.0.0 CVS -- Kusunose
+
+04/03/2002
+[B]- '/' in item selection didn't update screen correctly -- pelpel
+[m]- Moved auto-squelch code from process_player() to process_world() -- pelpel
+
+05/03/2002
+[m]- CAVE_TRDT wasn't cleared when a trap is disarmed, which had some
+ subtle effects and forced coders to double check c_ptr->info and
+ c_ptr->t_idx in various places -- pelpel
+
+06/03/2002
+[B]- Level generation could cause infinite loop in the Sacred Land of
+ Mountains -- pelpel
+[B]- Player ghosts were disabled in a way causing special feeling on
+ every level -- pelpel
+[m]- Changed repeated message code to V-CVS one -- pelpel
+[m]- Added another graphics mode variable called graphics_mode :),
+ somewhat like use_graphics in ZAngband, but it doesn't
+ require any changes to main-xxx.c. It's set within reset_visuals()
+ and used by map_info(), so that it doesn't have to do streq()
+ each time it is called -- pelpel
+[B]- Took 64-bit safe RNG from Vanilla -- pelpel
+
+11/04/2002
+[B]- Gave SPECIAL_GENE flags to the k_info.txt entries of SPECIAL_GENE
+ special artefacts -- pelpel
+
+20/04/2002
+[B]- Player is now guaranteed to have initialive after entering a level.
+ Thanks Joseph William Dixon for the patch -- pelpel
+
+22/04/2002
+[m]- Renamed 422colors.prf to 422color.prf, to make it fit with the 8.3 naming
+ convention -- pelpel
+
+24/04/2002
+[m]- Add support for multiline comments in lua: --[[ ... ]]
+
+28/04/2002
+[B]- Fixed a bug that caused gods start casting nasty effects when player's
+ grace becomes negative. It should have been -60000. -- Kusunose
+[D]- The inn in Bree is now The Prancing Pony. -- Kusunose
+[m]- Improved lua interface for defining new 'm' keys, magic powers, quests, ...
+
+05/05/2002 - T.o.M.E. 1.0.0 aka "Between the Darkness and the Light"
+
+02/1/2002
+[m]- (Mac, Carbon)Graphics mode performance improvement. When a user
+ chooses a fixed width font (as is almost always the case) and
+ doesn't change tile width & height, the higher_pict method is
+ used instead of *very* slow and inefficient always_pict to
+ draw things -- pelpel
+
+04/1/2002
+[B]- Normal (non-vampire) races didn't get mana regenerated at Inn -- pelpel
+[B]- Fixed the monster casting at other monsters but targetting you bug
+
+11/01/2002
+[B]- Sacrificing wands now decreases charges
+
+12/1/2002
+[B]- The random text code left files open in many error cases -- pelpel
+[m]- The prf file loader always searches for the user directory first,
+ then the pref directory if it can't find it there, so that user
+ can override the system defaults keeping the distributed files
+ intact. This also simpifies the pref loading codes in several
+ places -- pelpel
+
+13/01/2002
+[B]- Fixed more running problems (FEAT_SAND and FEAT_ASH) -- pelpel
+[B]- Some routines called malloc/free instead of C_MAKE/C_FREE in
+ the vault generation -- pelpel
+[B]- One wrong sign in the fractal cave code (vertical average) -- pelpel
+[B]- Fixed the store info file thanks to wrabhit23
+
+14/01/2002
+[B]- Ego items had extra spaces in their names -- pelpel
+[B]- (XAW)Terminals used wrong names. USE_EGO_GRAPHICS didn't even
+ compile -- pelpel
+[I]- IRC code for the XAW port. Caveat: it causes a linker error
+ if you USE_X11 and USE_XAW at the same time... -- pelpel
+[m]- the html help file converter now gets it's header and footer
+ from head.aux and foot.aux in lib/help to help website
+ designer adapt the generated files to whatever they want
+
+19/01/2002
+[B]- The Mathom house acted like a normal shop when selling. -- Kusunose
+[I]- Removed hard-coded direction keys in the skills menu -- pelpel
+[B]- Many room walls didn't have the CAVE_ROOM flag set -- pelpel
+[B]- More running problem (small trees in Mirkwood) -- pelpel
+
+20/01/2002
+[B]- final artifacts were not generated if they are k_info artifacts.
+ -- Kusunose
+[B]- Fixed get_com interface for lua
+
+22/01/2002
+[B]- Squelch on sense now destroys {good} items if it's told
+ "destroy good", but only when the "strong" pseudo-ID is in
+ effect. Doing so for the "weak" method would be too dangerous -- pelpel
+[m]- The running code now uses DONT_NOTICE_RUNNING and CAN_RUN terrain
+ feature flags for non-conditional checks, i.e. everything but doors,
+ stairs and alike, and those requiring levitation or immunity -- pelpel
+
+23/01/2002
+[m]- Included the lua tutorials by Fearof4s
+
+24/01/2002
+[m]- Replaced the field of view code with that from Angband 2.8.3--
+ -- pelpel
+[I]- view_special_lite and view_granite_lite that work with non-white
+ terrains (for ASCII mode only, already done for 16x16 tiles) -- pelpel
+
+25/01/2002
+[B]- '-s' crashed the game. Thanks kobayashi for the fix -- pelpel
+[B]- Doors are remembered, so that they work better with easy_open.
+ The easy_open code now performs checks for the perennially hard-to-handle
+ feature mimic field -- pelpel
+
+27/01/2002
+[D]- A fair number of dungeon guardians lost their defined artifact drop
+ and got a randart drop instead. The removed arts are back top normal
+ behavior(can be found)
+[I]- Inven/equip/item choice in subwindows now clears to the bottom of screen,
+ to avoid glitches and in accordance with the way it worked -- pelpel
+[I]- Identify and *Identify* don't list known/fully known items in
+ object selection -- pelpel
+
+29/01/2002
+[B]- monster_carry() could cause permanant artefact loss. A similar code
+ in quests and guardian artefact generation are also fixed -- pelpel
+[D]- There should be less not-at-all secret "secret" doors.
+ This does *not* mean that ancient prob, but those mountains enbedded in
+ plain wall in Barrow-Downs, for example -- pelpel
+
+20/01/2002
+[D]- Some spells can now stay in effect for a while, like a cloud of poison
+ will stay in place and poison everything passing in it
+[P]- Changed the magic system, it now uses schools of magic instead of
+ realms. A school contains much less spells than a realm. Each spell
+ is unique and dont make others redundant. Also all spells tries to stay
+ usefull for the whole game, by increasing in power and effects with levels.
+ A spell can be assigned to more than more school, in which case all the
+ schools need to be raised to obtain more power. All spells are implemented
+ in lua, to make it easy to tweak them.
+ All spells arent coded yet but my plans are for those schools:
+ Mana, Fire, Water, Air, Earth, Mind, Conveyance, Meta, Temporal,
+ Divination, Nature and Nether
+ It may change somewhat but thats the general idea :)
+ Books are not specific to a school, they can contain any spell from any
+ schools, it is even imaginable to have randomly created books
+
+01/2/2002
+[D]- Streamers use small tress instead of trees, a la KAngband and
+ variants that borrowed it's code. And they should look more like
+ streamers in most dungeons -- pelpel
+[D]- Neither player or monsters can see through, breath, or cast spells
+ over small trees, that are outer wall in Mirkwood -- arena levels
+ were too nasty otherwise... -- pelpel
+
+02/2/2002
+[m]- los, player's field of view, and spell/breath projection now all use
+ FF1_NO_VISION for the sake of consistency (in addition to the wall
+ check in case of spell/breath projection) -- pelpel
+[M]- Added a command to dismiss companions in the pet menu
+
+03/2/2002
+[D]- Moved wiz_dark() implementing the maze level from move_player() to
+ process_player(), so that a spellcaster can no longer do magic mapping--
+ detect monsters--detect traps then casting teleport as often as s/he
+ wishes to make things incredibly easy. Because of my laziness detected
+ traps aren't displayed if they are out of sight. This would require
+ tremendous hack. Anyway, don't worry, they are still remembered -- pelpel
+[I]- Auto-squelch menu now accepts ^R as an alternative to ^S, to be nice
+ for those who USE_GCU. Thanks Skylar Thompson for the problem report
+ -- pelpel
+[m]- CAVE_TRDT wasn't cleared when a trap is disarmed, which had some
+ subtle effects and forced coders to double check c_ptr->info and
+ c_ptr->t_idx in various places -- pelpel
+
+06/02/2002
+[B]- Do not drop from wild if player were not in wild mode in previous turn;
+ it put player in wrong place and could crash the game. -- Kusunose
+[B]- Summoning monsters from totems could crash the game. -- Kusunose
+[B]- The Heavy Crossbow of the Elves was 'the ultimate armor' in the
+ fully identified description. -- Kusunose
+
+07/02/2002
+[m]- Added FF1_DOOR flag to open doors for lightning effects in map_info()
+ and have the trap creation code explicitly avoid them, because traps
+ have and should never been performance bottlenecks -- pelpel
+
+08/02/2002
+[m]- Some process_world() sections (most notably monster generation and
+ lingering spell effects) are conditionalised so that they don't run
+ in the overhead map -- pelpel
+[D]- Added a dungeon flag that prevents generation of streamers, and
+ gave it to those with water/lava rivers and places like Numenor
+ and the Sacred Land of Mountains. Maze and evolving levels don't
+ require this. Also made lava deeper (dlev 34 or below). Trees can
+ appear on any flat levels. And eliminated undesirable calls to
+ the RNG in the streamer code -- pelpel
+[m]- Changed all the signed (!) flags I was able to find to unsigned, because
+ they don't make any sense. Savefile code should be modified as well
+ if we ever care for savefile portability -- pelpel
+[m]- The tunnel code (ordinary one) now performs double check for
+ feat_wall_outer and CAVE_ROOM (which was added a couple of weeks ago),
+ so it is safe to use any terrain features for outer wall, including
+ those identical to fill_type -- pelpel
+
+09/02/2002
+[D]- Rewrote place_new_way() so that it doesn't create unconnected dungeon
+ sections, destroy outer walls of rooms, or dig room corners -- pelpel
+[I]- Hopefully finished special lighting effects. Now it works this way
+ (with all lighting effects options on):
+ Perma-lit grids and lit walls/doors within sight, and remembered important
+ features = f_info colours, Perma-lit floors and walls/doors out of
+ sight = darker colours, torch-lit "boring" floor = yellow, torch-lit
+ grids out-of-sight = dark grey, blindness = B&W -- pelpel
+[D]- Angband dungeon now adjusts monster levels to the dungeon level.
+ The monsters here will have a minimun level in the range of:
+ level / 2 to level. So at level 67 the lowest monster you can encounter
+ will be level 33. This means that if a white icky thing is generated
+ it will be a level 33 white icky thing. If the base monster level is higher
+ then nothing is adjusted.
+
+11/02/2002
+[m]- (Temporary note) Separated staying effect handling code from
+ process_world() and made it a function, so that it can be called in
+ any place with in dungeon() [can be activated by #define pelpel :)]
+ I temporarily placed it after process_player(), but we have to spend
+ some time testing this, seeking for the best turn structure -- pelpel
+[m]- Auto-squelch is performed *before* a player turn, just before the
+ pack overflow code to prevent some interface problems. This means
+ that squelching occurs as the very last action of a player turn,
+ after monster drops or even after pseudo-ID, so that you don't have to
+ press extra space to squelch items. Another possible arrangement
+ would be within process_world(), right after sensing -- pelpel
+
+12/02/2002
+[B]- Fixed a bug in the hook code that may or may not be the cause of
+ various quest-related bugs -- pelpel
+[D]- Altars now have CAN_RUN flag, so that you no longer see the
+ message "You cannot run in that direction" when trying to run
+ across them -- pelpel
+[m]- Added flush() before all the quest questions to prevent accidental
+ loss/declination/acceptance of quest rewards/quests, also added
+ very important flush_failure and flush() to the lua interface and
+ put that in the spell failure code, so that spells can be macroed
+ safely -- pelpel
+[B]- Fixed a bug in the 6 monster randquest thanks to Louis-Frederic Michaud
+
+13/02/2002
+[O]- Elvish waybread renamed to lembas, thanks to nimloth
+[I]- Because I find detection no longer works on panels, added DTrap status
+ line as well as a new disturbance option disturb_detect. Also removed
+ hardcoded row/column positions in the status line code, in preparation
+ for big-screen support -- pelpel
+
+16/02/2002
+[I]- Added scrolling target/look code, which is bastardised :) form of
+ Z and V ones -- pelpel
+[m]- Depth/field name area is now 13 character long, so that names like
+ "shallow water" will fit, and "Lothlorien" is not truncated in GCU.
+ Also added short dungeon name prefix to the depth-in-feet mode -- pelpel
+[m]- Because map_info() is the #1 bottleneck routine and because I felt
+ hack_map_info_default is incredibly ugly :(, I removed
+ hack_map_info_default and added specialised version of map_info()
+ for use by cmovie and the HTML screen shot saver -- pelpel
+[B]- Fixed the "automatic ego filter" bug in the squeltch filter -- pelpel
+[I]- Big screen is sort of working now, but main-xxx.c (have
+ to remove restrinction on size of the PernAngband window) and
+ cmovie are still needing upgrade -- pelpel
+
+17/02/2002
+[B]- Made panel_bound() more paranoid about the current dungeon size,
+ to prevent many big screen-related crashes -- pelpel
+
+18/02/2002
+[m]- Added 8x8 graphics support to trap display code -- pelpel
+[m]- Added another graphics mode variable called graphics_mode :),
+ somewhat like use_graphics in ZAngband, but it doesn't
+ require any changes to main-xxx.c. It's set within reset_visuals()
+ and used by map_info(), so that it doesn't have to do streq()
+ each time it is called -- pelpel
+
+19/02/2002
+[m]- Added term resize hooks so that one doesn't have to hit the redraw
+ key and alike when s/he resizes windows. Hooked functions are placed
+ in xtra2.c, because they are only related to big screen support
+ and other panel related codes are there. Don't like the way stuffs
+ are initialised -- assumption about max number of terms, setting terms
+ package hooks from within the upper layer codes -- but the existing
+ implementations do it this way, and the "right" way requires changes
+ to main-xxx.c, which I'm too lazy to do... -- pelpel
+[I]- Big screen support for X11 and XAW ports (already done for Gtk, Mac
+ and Windows ports). Also added an option -o to force the use of 8x8
+ tiles in graphics mode. Say "-g -- -o" to activate it. And please note
+ that because of the way transparency effect is implemented in
+ X11/XAW/Gtk ports, it is available even with 8x8 tiles -- pelpel
+
+20/02/2002
+[B]- Beastmaster Shanty overpayed a bounty if monster's corpses are stacked.
+ -- Kusunose
+[O]- Combining rod and rod tip now considers rod's cheapness flag.
+ -- Kusunose
+
+22/02/2002
+[M]- Restored monster light code, modeled after Steven Fuerst's implementation
+ insteand of APW one this time, but without his support for multiple radii.
+ -- pelpel
+[M]- Made monster light a run-time option. CAVEAT: It'll cause display
+ weirdness when you turn this option from ON to OFF while playing.
+ Save/Restart or entering new level will fix the problem.
+ Should call forget_mon_lite() when the game detects it... -- pelpel
+[M]- monster lite option is on by default
+
+23/02/2002
+[B]- A failure in disarming traps using easy-disarm caused another attempt
+ at disarming traps (because it calls move_player_aux which calls
+ do_cmd_disarm_aux which calls move_player_aux...), growing call
+ stack infinitely. It's dangerous, and, in fact, players were totally
+ safe from traps (messages said "You set off...", but traps were never
+ activated) -- pelpel
+[B]- Black breath could crash the game (it's really evil, isn't it :)
+ Thanks Kevin W. Thomas for the patch -- pelpel
+[m]- Ego graphics code now only tries to use graphics overlays for
+ monsters and players if and only if 16x16 tiles are used -- pelpel
+[B]- map_info() had problem with walls mimicking floors -- pelpel
+[m]- Shimmering terrain features no longer shimmer while running or resting
+ to make themselves somewhat more bearable on slower machines -- pelpel
+
+24/02/2002
+[m]- Updated lighting effect code so that it works the same as rr9's Angband
+ versions. Also removed hardcoded IBM pseudo graphics code points, so that
+ if anyone is ever interested in updating its font & prf files, s/he
+ can do so freely without editing the source code.
+ And map_info / map_info_default is now fully aware of c_ptr->mimic,
+ so that map edges will not have strange lighting effects etc.,
+ hopefully -- pelpel
+
+26/02/2002
+[B]- Polymorphing random effect of Chaos Warriors could crash the game.
+ Special thanks for Kevin W. Thomas for the detailed analysis
+ of the problem -- pelpel
+[I]- Moving onto quest entrance/exit no longer causes annoying automatic
+ stair movements. You have to tell the game '>' or '<' to enter/leave
+ -- pelpel
+[m]- Renamed see_wall() in cmd1.c to see_obstacle() also added a grid-based
+ version of the function to be used by run_test(), so that those who have
+ immunity and/or levitation can run over lava fields, deep water etc.
+ -- pelpel
+[O]- There are 2 kinds of spellbooks for the magic schools, the ones that
+ are named "a Spellbook of foo" where foo is a randomly choosen spell
+ (selection is based on the dungeon level)
+ and other names that are fixed books
+
+27/02/2002
+[O]- School spellbooks can be fireproof ego items
+[B]- Dungeon town generation could crash the game. Thanks Mogami
+ for the analysis of the problem -- pelpel
+
+01/03/2002
+[B]- Supplied missing suid code in 1) savefile removal action in the startup
+ screen, 2) dungeon savefile removal, and 3) bone file removal.
+ Thanks kobayasi for the patch -- pelpel
+[m]- Less platform-dependent savefile processing code for the game start
+ menu, thanks again for kobayasi. It now uses files.c utility routines
+ for building appropriate savefile names -- pelpel
+
+03/03/2002
+[B]- Special level names wherent corrently displayed if the N: line was the
+ first of the file in lib/dngn
+
+03/03/2002
+[m]- Added initialization code to add SPECIAL_GENE flag to final guardians
+ and their artifacts (and DROP_RANDART if there are no final artifacts).
+ -- Kusunose
+[D]- Put yet another fractal code (unlike the others it's quite simple)
+ in the level filler generator, and added a F: parameter in d_info.txt
+ to control its behaviour. Its syntax is FILL_METHOD_#, where # is
+ 0: use the first filler w/o calling RNG (for Angband, Mirkwood etc.),
+ 1: the same as the previous versions (default), 2: slightly smoothed,
+ 3: more smoothed, or 4: max smoothing (initial step of 8 grids) -- pelpel
+
+04/03/2002
+[B]- '/' in item selection didn't update screen correctly -- pelpel
+[m]- Moved squelch-on-sense code from process_player() to process_world()
+ -- pelpel
+
+04/03/2002
+[P]- High intelligence increase mana regeneration rate
+
+06/03/2002
+[B]- Level generation could cause infinite loop in the Sacred Land of
+ Mountains -- pelpel
+[m]- Changed repeated message code to V-CVS one -- pelpel
+[B]- Player ghosts were disabled in a way causing special feeling on
+ every level -- pelpel
+
+07/03/2002
+[m]- Trap display now uses x_attr/x_char of FEAT_TRAP (f_info N:17)
+ 1) if a trap is set on a "boring" terrain in the ASCII mode, where
+ only x_char is used and attr is taken from tr_info.txt XXX XXX XXX
+ or 2) if attr/char for a trap is not defined in prf files in the
+ graphics modes -- pelpel
+[m]- Removed FAKE_VER_*, because it has been quite long since we lost savefile
+ compatibility with Angband 2.8.1 and info.txt files can be updated
+ by sed/perl/whatever script in a snap -- pelpel
+[B]- Took 64-bit safe RNG from Vanilla -- pelpel
+
+08/03/2002
+[m]- To accommodate the practice of not upgrading version stamp of info.txt
+ files at each release (never done in V-based variants, but common among
+ Z-based ones for historical reasons), version stamp checks against game
+ data files in lib/edit is made a compile time option
+ (VERIFY_VERSION_STAMP), off by default. With this option off, version
+ stamp checks for the binary files are still performed and they always
+ have version signature of the game, not those specified by the V: lines
+ -- pelpel
+[B]- Wide light radius worked in the small scale wilderness map,
+ where it should have been WILDERNESS_SEE_RADIUS -- pelpel
+
+10/03/2002
+[m]- Upgraded the lua bitlib so it can handle stuff like bor(1, 2, 4, 8)
+
+11/03/2002
+[M]- Confusion, stunning, charming now works on uniques(those that dont have the
+ corresponding resistance)
+
+24/03/2002
+[I]- Stats can now be displayed in a linear mode(from 3 to 37) instead of
+ 3 to 18/***, the option is off by default
+
+25/03/2002
+[D]- No more shafts in the halls of mandos
+[m]- Block comments( --[[...]] ) in Lua enabled
+
+27/03/2002
+[D]- Tweaked the door code a bit so that some doors are much harder
+ to find -- pelpel
+[I]- Added recall depth subcommand to the knowledge menu -- pelpel
+
+03/04/2002
+[I]- Removed the flavoured_attack option and made most 'silly' messages
+ off by default. They are now controlled by 'insanity roll', which is
+ d100 roll against (max_sanity - current_sanity) * 100 / max_sanity.
+ The same roll is also used to determine if the game should use
+ silly monster descriptions (in addition to the hallucination
+ effects) -- pelpel
+
+04/04/2002
+[I]- The skill interface no longer asks question whenever you increase skills.
+ Changes to skill values are made permanent if the player confirms them
+ when s/he leave the skill menu, otherwise, they are simply ignored
+ -- pelpel
+[P]- The skill system is ready ! Some small tweakings are needed, but it is
+ mostly ready. Each level you gain a few skill points that you can spend
+ on various skills(like combat, weaponmastery, magic, ...). Most actions
+ are now tied to skills.
+[P]- Classes revamped as more or less skill templates. Classes now can have
+ specializations which allow starting with somewhat different skill set.
+[B]- Since streamers can create unconnected dungeon sections, the code for
+ it is moved after stair and player allocation -- pelpel
+[m]- Reorganised the linkage order of makefile.org a bit, so that similar
+ files are grouped together, also frequently called files are not coupled
+ together with rarely used ones. This *might* help on-demand-paging memory
+ manager a bit -- pelpel
+[m]- Changed all occurances of " ?" in strings to "? ". --takkaria
+
+08/04/2002
+[B]- Some player_type fields and corresponding lua interface definitions
+ were of different types. Fix thanks to rr9. -- pelpel
+
+09/04/2002
+[I]- Bigtile patch for windows, x11 and mac ports thanks to Takeshi Mogami
+
+11/04/2002
+[B]- Gave SPECIAL_GENE flags to the k_info.txt entries of SPECIAL_GENE
+ special artefacts -- pelpel
+
+15/04/2002
+[M]- Revision of the conf/stun/sleep resists for uniques thanks to Runescrye
+
+18/04/2002
+[G]- The adventurer quest reward is now either the adventurer or some skills
+[I]- Asks for a last screenshot upon death
+
+20/04/2002
+[I]- You can now dump a frame of a cmovie as an html screenshot(while playing
+ the movie)
+[P]- New god system, each god will be much more different from all others.
+ Altars are less vital. There are less gods.
+[m]- Renamed inappropriately called global array 'town' to 'town_info', also
+ moved some boolean town_type members into 'flags', in order to support
+ dungeon town information subcommand of do_cmd_knowledge -- pelpel
+[m]- Commented out or moved many local variables causing 'unused' warnings,
+ because of badly placed if 0's -- pelpel
+[B]- Player is now guaranteed to have initialive after entering a level.
+ Thanks Joseph William Dixon for the patch -- pelpel
+[I]- The commands for skills and spells are swapped. Please use 'G' to learn/
+ check skills and '$' ('\$' in roguelike) to learn spells.
+
+22/04/2002
+[m]- Renamed 422colors.prf to 422color.prf, to make it fit with the 8.3 naming
+ convention -- pelpel
+
+23/04/2002
+[O]- Bound the wands & staves damage/power to the Magic skill, attack wands
+ should actually be usefull now
+
+25/04/2002
+[P]- Magic skill now allows you to copy spells from books into various
+ objects. Not all objects can contain spells naturally, but most object
+ of the Magi can, and all mage staves
+[P]- Spectres loses HP while in walls, it was too abusable(and in fact, I never
+ planned to remove it)
+
+28/04/2002
+[D]- The inn in Bree is now The Prancing Pony. -- Kusunose
+
+01/05/2002
+[D]- Small levels cannot generate full levels anymore
+[I]- The prompt to cast a spell now understand to press @, which will ask for
+ a spell name, it'll look all books and cast it if you can.
+ It EASES macros, a macro will now look like:
+ 02m@Manathrust\r*t
+[I]- Message recall now understands bigscreen thanks to pav
+[I]- Help now understands bigscreen thanks to pav
+[I]- Skill screen now understands bigscreen thanks to pav
+[I]- No more pickup prompt with autopickup when inventory is full, thanks to pav
+
+03/05/2002
+[B]- The skill increase/decrease code was unable to detect underflow -- pelpel
+
+06/05/2002
+[P]- Made running commands exempt from do_nothing processing, because this
+ can be abusable and makes no sense -- you could, for example, hold down
+ movment keys when following Eru and use running when following other
+ deities -- pelpel
+[I]- Took big screen code for horizontal scrolling of message recalls
+ from Vanilla -- pelpel
+[I]- You can navigate through option menus with roguelike_keys -- in fact,
+ I didn't know users of the original keyset can go up before I play
+ the latest V and see its code... Taken from Vanilla CVS -- pelpel
+[m]- Entirely removed function definining macros from script.c, because
+ it can confuse some compilers (a lcc case was reported), and some
+ preprocessor reports syntax error for missing macro arguments -- pelpel
+[I]- Added big screen support for GCU -- pelpel
+
+12/05/2002
+[m]- Some, if not all, command line options are documented -- pelpel
+[B]- Full map command displayed only half of current dungeon level when
+ bigtile mode is used. Thanks for Takeshi Mogami for tha patch
+ -- Kusunose
+
+19/05/2002
+[m]- 1) Slightly reorganised menus in the Mac ports, because there were so
+ many of them on the menu bar. 2) added a switch (-b) to the GCU port
+ to select multiple terms or one big screen. 3) the GTK port issued
+ huge number of fatal warnings if the graphics mode was turned on and off.
+ 4) Compiled latest V CVS with main-gtk.c in T.o.M.E. and fixed some
+ portability problems. -- pelpel
+[P]- Pseudo-id is now bound to the Combat skill(for weapons/armors) and the
+ Magic skill(potions, ...)
+[P]- It is now possible to press @ at the 'm' key prompt to select a skill action
+ by it's full name. Thus allowing unbreakable macro:
+ m@Cast a spell\r@Manathrust\r*t
+[I]- The skill screen cannot be abused to get more skills anymore
+
+27/05/2002
+[P]- New god Melkor Bauglir
+[P]- Weaponmastery, Archery and Barehand skills increase Combat skill much more
+
+02/06/2002
+[B]- Various spell effects that polymorph monsters could crash the game.
+ -- Kusunose
+[B]- Compare weapons command miscalculated muliplying bonus. -- Kusunose
+
+12/06/2002
+[P]- Deathmold fetch ability pickups gold and objects(if autopickup is set) thanks
+ to "kenderband" <kenderband@hotmail.com>
+[m]- Exported cur_hgt and cur_wid to lua by request of Fearof4s. (takkaria)
+
+14/02/2002
+[m]- Updated the lua help files thanks to Fearof4s and Chris Hadgis
+
+17/06/2002
+[B]- Fixed junk artifacts and music instruments stacking bug in stores
+ -- Kusunose
+
+03/07/2002
+[M]- Monsters drop stolen gold when killed
+
+04/07/2002
+[B]- Capped max number of extra blows at 2 when the limit_blow flag is set
+ in two places -- pelpel
+
+09/07/2002
+[B]- Cancelling a scroll of reset recall didn't work. -- Kusunose
+[O]- Reset recall now lists all the dungeons a player has visited and
+ additionally allow a player to select by name. -- Kusunose
+
+13/07/2002
+[O]- Enabled the generation of double ego items. An item cannot get 2 prefix
+ or 2 suffix, it will always be a prefix and a suffix
+
+14/07/2002
+[P]- New bounty quest available at the beastmaster shanty! Bring back a corpse
+ and get some monster-lore skill and the ability to learn corpse-preservation
+ skill if you couldn't already
+
+15/07/2002
+[P]- Replaced first necromancy spell with Horrify from the old Nether realm and
+ the third spell with absorb soul, provides some health upon monster death
+
+16/07/2002
+[m]- Updated lua_ques.txt to fix some misinformation. -- fearoffours
+
+17/07/2002
+[B]- One was able to have another entry in the score file for a dead/retired
+ character by loading him/her with the -w option, then answering 'n' to
+ the wizard mode confirmation. Bug report and fix (for Angband 3.0.1, but
+ applicable for all variants) by Hallvard B. Furuseth, Takeshi Mogami and
+ Robert Ruehlmann -- pelpel
+
+22/07/2002
+[m]- More lua documentation fixes, particularly adding square brackets to field
+ names on lines that would allow this without mucking up colour formatting.
+ -- fearoffours
+
+23/07/2002 - T.o.M.E 2.0.0 aka "Point of No Return"
+
+24/07/2002
+[m]- The splash screen now shows "Tales of Middle Earth" or "Troubles of Middle
+ Earth" randomly. Thanks to Scott Holder for the idea and some code.
+ (takkaria)
+[m]- makefile.org now defaultds to ./lib for the lib directory
+[m]- makefile.org is now makefile.std
+[B]- Fixed a silly bug preventing Polearm masstery from wroking
+
+26/07/2002
+[B]- In multiuser installations, notes and cmovie were written under the lib
+ directory using the game's permission, making them inaccessible by
+ the player. I moved them to ~/.tome. Note: This means that a coder
+ shouldn't grab permission before opening files in these directories
+ -- pelpel
+[B]- There are still inappropriate (and even unbalanced!!!) calls to
+ safe_setuid_grab() and safe_setuid_drop() in the game. Thanks Neil
+ for pointing out those in the macro save commands. There are so many
+ places that I have to look at, but I'm trying... -- pelpel
+[I]- Better (hopefully) object identification screen
+
+28/07/2002
+[m]- Added safe_setuid_grab/safe_setuid_drop to all the functions that
+ access files in the lib directory. I did so even for the game
+ initialisation, knowing that it's very unorthodox. This is because
+ I found some of them in the init[12].c. There's no other sure ways
+ to keep them from messing multiuser installations. -- pelpel
+[m]- Added makefile.dos, which is a copy of makefile (now), in order to
+ prevent overwriting accidents. Ideally, 'makefile' should be in the
+ .cvsignore, so that every developper can feel at ease with his/her
+ own preferred environment -- pelpel
+
+01/08/2002
+[P]- New corruption system, savefiles are compatibles but you will loose
+ all your corruptions.
+ Most corruptions now have a good and a bad effect. Some corruptions
+ depends of others, it means that you can only get them when you have
+ the ones they depend of(i.e: Balrog Form need Balrog Wings, Balrog
+ Aura and balrog Strength to work). Some corruptions can be mutualy
+ exclusive. The list of corruption is totaly rewrote. They are more
+ in-theme now.
+[B]- Perma curse cannot happen on randarts anymore
+
+02/08/2002
+[B]- Player could not pick up items from home if he did not have enough
+ gold. -- Kusunose
+[B]- Traps of wasting wand dont mess up wands
+[B]- Dragon helms get resistances
+
+03/08/2002
+[B]- No races were allowed to be loremasters
+[B]- Fixed a bug in Character classes allowed, tahnks to Alex Wilkins
+[m]- Added Melkor to the gods docs.
+[B]- Spells cannot be cast while blinded or confused(execpt for a few)
+[G]- Wielding the One Ring has some ... disadvantages now ...
+[B]- Poor Melkor didnt had his altar generated in dungeons, while he is the
+ only god for a use of an altar
+
+05/08/2002
+[m]- Improved the adventurer guide and added a section for macros to it
+[P]- All Udun spells are no more multi-school spells
+[P]- Reorganized the skill tree to remove the Misc skill tree. Alchemy is now
+ under Magic, Antimagic is in Combat and Music in Spirituality
+
+07/08/2002
+[B]- Fixed the -1 activation power of some mage staves of spell
+[G]- Troll Glade/Wight Grave selection is now (usualy) based on the
+ Combat/Magic skills level
+
+08/08/2002
+[I]- The game asks confirmation before learning a skill that can exclude an
+ already known one
+[I]- When 'I'nspecting(or upon *id*) a fully *id* weapon/ammo the game will
+ tell you the damage it would do if you used it(idea from Ey)
+
+10/08/2002
+[I]- 'U' power menu is now usable with repeat key 'n'
+
+11/08/2002
+[G]- The chance for combat item pseudo-ID now improves exponentially,
+ just like V Warriors (combat skill 0 == plev 0, maxed == plev 50),
+ while that for magic items improves slowly, just like V Rangers
+ and Mages but with higher success rate (more than ten times as
+ frequent as V Rangers) -- pelpel
+
+12/08/2002
+[B]- Added missing spell frequency to Fire golem. -- Kusunose
+[B]- Symbiant cannot pickup a hypnotized pet from a pile. Thanks to kobayasi
+ for the patch. -- Kusunose
+[P]- Give Ents scrolls of satisfy hunger instead of some food. -- Kusunose
+[B]- Stats more than 18/220 were displayed incorrectly if linear_stats was ON.
+ -- Kusunose
+
+13/08/2002
+[m]- Made chg_to_txt in files.c conditional, so that it won't be included in
+ Windows, Mac and RISCOS ports (they don't call the function, so it has
+ been dead code) -- pelpel
+[D]- Since Tome generated small levels once in three times when requested,
+ which I think is too often, and in Z and Ey the chance is 1/5 and 1/10
+ respectively, I lowered the chance to 1/6 -- pelpel
+[B]- The small_level option had the same effect as always_small_levels
+ -- pelpel
+[O]- Removed CURSE_NO_DROP from ego items. Players want new curses, and when
+ they get them they complain...
+
+14/08/2002
+[I]- Added avoid_shimmer efficiency option to suppress shimmering of terrain
+ features, because I have had problems with them on really big screens
+ -- pelpel
+[P]- Increased the modifier of the masteries for warriors
+[P]- New Warrior subclass, the Demonologist, spell enhanced warriors. They
+ use the new Demon school and the renewed Demonblades, Demonshields and
+ Demonhorns. Their spells enhance their fighting potential. They can either
+ be seen as a force of good, fighting against demons with their own powers
+ or as a force of evil fighting to bring corruption to the world.
+[P]- Sorcerors begin with a robe instead of a dagger
+[I]- Easy close is now slightly more intelligent in their handling of
+ broken doors -- pelpel
+
+15/08/2002
+[O]- Ring/Amulet of Spell, cheap, early objects that can contain a spell
+[B]- Exploding ammo dont stack with normal ammo
+
+16/08/2002
+[B]- Ammo creation is bound to Arechery skill instead of player level
+[B]- Fixed a bug in related skills, when one increased a skills sometimes the
+ related skills didnt increased
+
+17/08/2002
+[m]- Do not grab/drop permissions twice. Thanks to kobayasi for the patch.
+ -- Kusunose
+
+18/08/2002
+[B]- Temporal stat drain no longer cancels normal stat drain. -- Kusunose
+
+19/08/2002
+[O]- Temple will now stock random spells from god schools, Magic Shop will
+ only stock non god spells and bookstore will contain both
+[O]- Stores buyable list is now lua defined
+[B]- The amount of mana in each grid was always magical level. -- Kusunose
+[P]- Changed the formula to calc the player HP when using possession so that
+ a higher possession skill will be prefered
+
+20/08/2002
+[B]- Extracted essences overwrote a weapon slot when inventry was full.
+ -- Kusunose
+
+21/08/2002
+[I]- Ingame contextual help is on by default(it was before, but it was bugged..)
+ It is now proccessed by lua, file help.lua and should be much easier to
+ add new sections to than before. So all feel free :)
+[G]- Added town of Khazad-Dum (where 'exit' from Moria was) with nice Mining
+ supply shop. -- fearoffours
+
+23/08/2002
+[I]- Contextual help for the birth screens too. Press ? when over a race,
+ class, ... and it'll bring the specific help for it
+[M]- Monster ego wont start awake
+[P]- lost Souls now have see invisible, I know I'm far too nice :)
+[I]- Contexual help to skill screen, press ?
+[I]- Easy macro recorder! Just press $ and press a normal key sequence!
+
+26/08/2002
+[m]- Removed the autosquelch to replace it with a new Automatizer that should
+ be much more powerful. It lacks a gui, but that should soon change :)
+
+28/08/2002
+[I]- The new Automatizer got a GUI :) It should be quite easy to grasp, you
+ define rules to match and action to take. But it does allow very complex
+ rules.
+
+27/08/2002
+[m]- Added updated class help documents. Removed extraneous ones. Amended
+ links in birth.txt to reflect changes. Added appropriate entries to
+ help.lua for contextual class help at birth. Thanks to Mef. -- fearoffours
+[m]- More contextual help: races and god selection at birth, rod tips, rods and
+ trapping kits. -- feaoffours
+
+29/08/2002
+[G]- Added new god quest. Given at random time by your god, you have to retrieve
+ a piece of a relic generated at a random level of a randomly places dungeon.
+ More than one of these quests may be given (currently up to 4), though only
+ if you complete previous quests. The relic is only generated once in each
+ dungeon, so look carefully for it. Diving won't help you. -- fearoffours
+
+31/08/2002
+[P]- New Water spell: Vapor, it create a low damage wide radius short lasting
+ cloud of water. It is designed to be a cheap attack spell for annoying
+ critters. Beware, random spellbooks & inscribed objects spells will "morph"
+ to other ones, sorry, unavoidable
+
+03/09/2002
+[B]- Fix mushrooms stacking for the maggot quest
+[m]- Fix birth.txt crash -- fearoffours
+
+04/09/2002
+[O]- Scythes of Slicing are now vorpal, rarer and deeper
+
+05/09/2002
+[G]- Added the fireproofing quest. Visit the Mage tower in lothlorien to get the
+ quest, bring back something for the mage, get books fireproofed in return.
+ -- fearoffours
+
+08/09/2002
+[I]- Updated the forgotten IBM pseudographics, based on my unknown work for
+ Pern 4.1.2. Also added an X11 BDF file so that it can be used on
+ the three X ports -- copy graf-ibm.prf to font-x11.prf in your user
+ directory (i.e. ~/.tome) and set the font of main window to the font
+ made from lib/xtra/ang16.bdf. Instruction for installing new X fonts
+ should be found in the bdftopcf, mkfontdir and xset man pages.
+ -- pelpel
+
+10/09/2002
+[I]- Current location(town, level, ...) is show in orange when about to recall
+
+17/09/2002
+[m]- Updated skill docs, thanks to lemming
+[m]- More minor help file updates. thanks mef and Chris Hadgis
+
+21/09/2002
+[P]- Mindcrafters gets esp even after level 40
+
+22/09/2002
+[I]- You can press $ at object destruction prompt to automaticaly create a new rule
+ for the Automatizer about the object being destroyed
+[B]- Casting "Disperse Magic" when spell level is 20 or more produced a lua
+ error. -- Kusunose
+[m]- Reformatted the makefile.WHICH file. Do we really need this file anymore?
+ other variants get by without it, I don't really think it's needed...
+ (takkaria)
+[m]- Reformatted the todo list, removed some entries which have been done.
+ Also added a section for stuff which I will do (one day). (takkaria)
+[m]- I've reformatted and reorganized a fair bit of notes.c; I've made stuff
+ less hacky and made the function which writes notes to file use
+ my_fputs()... Also replaced some hardcoded buffer sizes with sizeof().
+ (takkaria)
+[m]- Removed hardcoded buffer sizes in wizard1.c. (takkaria)
+
+23/09/2002
+[M]- Farmer maggot quest provides a better reward, thanks to Revanant Morituri
+ and Wil Hunt for the idea
+
+25/09/2002
+[B]- The centre player option failed to do so while running. Thanks Neil
+ Stevens for the patch. -- pelpel
+[B]- Random Artifact arrows should now stack properly. -- wilh
+[B]- Mindcrafters lost their ESP at level 40. Now they have it permanently
+ at that level. (p_info.txt) -- wilh
+[B]- Above bug was supposed to be skill level 40, not clvl 40. Fixed. :)
+
+28/09/2002
+[I]- Macro-patch from Mogami, now the macro prf files are portable between Windows,
+ X11 and Mac. It also shows readable triggers like: \[shift-F1] and such
+
+29/09/2002
+[m]- Added .cvsignore to the src directory to prevent makefile overwriting
+ accidents from happening. Currently it excludes makefile (developers
+ are expected to update platform-specific makefile if s/he indents to
+ make permanent changes to them), tome, tolua, TOME.EXE and TOLUA.EXE.
+ -- pelpel
+
+01/10/2002
+[G]- Alchemy totaly changed! Thanks to John Gilmore for the patch! It has now entered
+ cvs to begin being tested and "balanced" ;)
+
+05/10/2002
+[P]- Symbiotic power changed to be bookless
+
+06/10/2002
+[P]- Available skills to leanr via random quests are defined in s_info.txt
+[P]- Trying to mimic when already mimiced(with the skill) will turn you back to
+ normal form
+[O]- All objects now have a description, thanks to Konijn
+ When identifying an item you can then press 'I'nspect to check it. It si most
+ usefull for scrolls & such, they tell you what exactly they will do
+[B]- Fixed a bug that made mage staves very costy
+
+07/10/2002
+[I]- Replaced font-ibm.prf, graf-ibm.prf, font-win.prf and font-mac.new (a
+ font-mac.prf replacement that has IBM-like pseudographics definitions)
+ with program-generated ones, hopefully up-to-date with recent changes
+ in info.txt files -- pelpel
+
+11/10/2002
+[I]- Unusable skills do not show anymore on the skill screen
+
+17/10/2002
+[I]- New option to not have the equipment/inventory windows move items around
+ when a prompt ask for an item. Thanks to John Gilmore
+[B]- Fix a bug of wands in store
+[B]- Fix an old spelling error: "droped" not "dropped" -- Neil
+
+18/10/2002
+[O]- Behold! The new Grand Scheme For Wands, Staves and Rods!;)
+ Now all sticks in the game use the same system as the spells. The unified
+ spell system could we say ;)
+ What does it changes? sticks should now be usefull, jsut as spells are.
+ For example a wand of manathrust is not to be laughed at.
+ Hw does it work? 'I'nspect the stick you want to know about you'll see
+ he details, each stick gets a spell and a base level. The base level
+ means the spell will be cast at that level if you have 0 in the
+ Magic Device skill. If you have Magic Device it increases the level.
+ Works a bit like Spell-power for the spells.
+ Also the distinction of wands/staves is tenuous now, wands are attack
+ stuff and staves not but wands can sometimes NOT be targeted spells
+
+20/10/2002
+[P]- Yavanna Kementari as a new Vala, protectress of nature. Along with her
+ priests, the Druids
+
+21/10/2002
+[m]- Updated symbiant helpfiles including full list of their new bookless spells
+ in m_symbio.txt -- fearoffours
+
+23/10/2002
+[I]- Recording cmovies is now working on a microsecond timeframe, much better looking
+ Let's just hope that silly systems know gettimeofday()
+
+26/10/2002
+[m]- Introduced a performance measure for process_world_hook, which I suspect
+ is causing slow movement problems in the reduced map mode. Please read
+ the comment in process_world() [in dungeon.c] for alternative solutions.
+ -- pelpel
+
+27/10/2002
+[m]- Changed every instance of "essense" to the correctly-spelled "essence".
+ Update your macros. -- neil
+[M]- Changed "The Balrog of Moria" to "Durin's Bane". Less of a mouthful, and more
+ in-theme -- fearoffours
+[M]- added the "spirits". They are the inhabitants of the void. They're nasty.
+ -- fearoffours
+
+28/10/2002
+[P]- Typo in Druid description: Kemenari -> Kementari. --fearoffours
+[B]- Fixed bug where ironman_rooms would give you the number of random quests
+ you played last time, instead of zero. -- neil
+
+29/10/2002
+[D]- Added SPIRIT flag to The Void, so that Spirits are actually generated there now
+ -- fearoffours
+
+02/11/2002
+[P]- Subraces can now have skill mods
+[P]- Spells bound to two or more schools now require at least one point in
+ each of the schools to be used
+[m]- Trimmed down the size of chardumps(removed uselessness from object
+ descriptions)
+
+05/11/2002
+[I]- Obvious object flags will show up when 'I'nspecting items when they are
+ only identified(no more need for *id*). Obvious flags are like acid
+ resisatnce on an armor of resist acid and such
+
+06/11/2002
+[P]- New skill, Stunning-blows, a haftedmastery subskill, just like critical
+ hits is a subskill of swordmasery. It requires a hafted weapon > 5 lb
+[O]- Activations are now a_info/k_info/e_info definable with a: lines
+ You can do a:HARDCORE=NAME where NAME is an activation name
+ that is hardcoded in the C source.
+ Or a:SPELL=Name where Name is a "spell" name as defined in the unified
+ spell system(the first defined spell(index 0) cannot be used this way
+
+07/11/2002
+[B]- Allow non-artifact, non-ego items with activations be activated. -- neil
+[I]- Cause of death is now shown in chardumps
+
+08/11/2002
+[m]- Added descriptions to Totems, removed redundant need to identify them.
+ -- neil
+[G]- Yavanna's followers now have a God quest. -- fearoffours
+[m]- Help docs for Yavanna and Druids, thanks Mef and Massimiliano Marangio.
+ -- fearoffours
+
+09/11/2002
+[M]- Only gain exp from pet kills, not from every monsters, when using Monster
+ Lore skill
+
+10/11/2002
+[m]- Yes, more help updates. In tome_faq.txt and TANG.txt I added links to
+ skills.txt, magic.txt, birth.txt and gods.txt where appropriate. Removed
+ two references to Pern that got missed in tome_faq.txt.Added a
+ c_priest.txt file for contextual help at birth when choosing a main class.
+ Corrected typo in r_info (SPirit became Spirit). -- fearoffours
+
+11/11/2002
+[I]- CTRL+] to save an html screenshot anytime(ie: to preserve the current
+ message)
+[P]- Necromancers start with corpse preservation skill
+
+13/11/2002
+[B]- Don't let the Maggot sling be generated randomly. -- neil
+
+15/11/2002
+[m]- Added a new TERM_XTRA call, TERM_XTRA_GET_DELAY which should return the
+ time in microseconds. What time exactly isnt meaningfull but it must be
+ usable to compute the length of time an action takes.
+[I]- Thanks to the new TERM_XTRA_GET_DELAY the cmovie code is now cleaner
+ and should work fine:) Windows and unix platforms should now be able
+ of microsecond resolution cmovies!
+[m]- Made all the socket code into z-sock.[ch] and wrapped it into a
+ virtualizing layer so that the rest of the code can use it independantly
+ of the implementation. Currently unix and windows sockets are supported.
+
+16/11/2002
+[B]- Prevent the automatizer from treating "good" bows as "average" -- neil
+[B]- Prevent earthquake damage for characters with wraithform, and prevent
+ some of the damage for characters with semi-wraithform -- neil
+[B]- Fix spelling of Book of Teleportation -- neil
+
+17/11/2002
+[P]- New Meta/Conveyance Spell: Tracker, it will track the last teleportation
+ that happened on the level and teleport you to its destination
+
+18/11/2002
+[G]- The Old Mage in Lothlorien will now fireproof staves or scrolls as well as
+ books, making it profitable for non-spellcasters. Currently he has enough
+ errr 'fireproofing material' for 3 books or 4 staves or 12 scrolls, or a
+ combination of these. Check fireprof.lua for the code. -- fearoffours
+[I]- Skills that have sub-skills in which you do not (and cannot) have points
+ no longer display the redundant +/- sign. thanks lemming for the code.
+ -- fearoffours
+[m]- Lots and lots of itsy-witsy help file updates. Stunning-blows, other
+ references to Pern, more links to other files... -- fearoffours
+
+19/11/2002
+[m]- Compress the grid in the character dump, eliminating blank lines and only
+ showing lines without '+'s for the resistances and sustains page -- neil
+[m]- Show the Mathom-house contents in the character dump -- neil
+
+21/11/2002
+[O]- When 'I'nspecting objects you'll know how you found them
+[B]- Magelock cannot override permanent walls(or walls for taht matter) and works
+ only in LOS
+
+22/11/2002
+[O]- 3 new artifacts of Gothmog, regrouped in an item set
+[m]- Cleaned up the notes code a little. (takkaria)
+[P]- Summon skill doesnt caerte drops for partial summons
+[P]- Summon skill doesnt work in an antimagic field
+
+23/11/2002
+[B]- Automatizer should not treat enchanted ammo and tools as average, either.
+ -- neil
+[B]- Fixed the bug that allowed to use unallowed gods
+[P]- Lost souls start with many satisfy hunger scrolls, I'm really too nice...
+[M]- Themed townspeople! In Lothlorien and GOndolin you find elven people and
+ dwarven in Khazad-dum
+
+24/11/2002
+[m]- Display in the dump how interesting items were found -- neil
+[B]- Two-handed artifact weapons now act as such -- neil
+[O]- Darksword antimagic field now scales on the antimagic skill
+ So using one with 0 in the skill is not worth much
+[m]- DESTDIR support in makefile.std -- neil
+
+25/11/2002
+[B]- Don't mark items as store bought until they are bought -- neil
+[O]- Distinguish stolen items from bought items -- neil
+[m]- main-net.c display module which redirects display over the z-sock layer
+ and thus over any ip network to allow to create very lightweight terminals
+ to play ToME everywhere. This is still experimental
+[B]- Don't show Gondolin twice in the dump -- neil
+[O]- Cut down the price of Spectral weapon given their .. usefullness
+[I]- Fixed the bug that told you you could buy more items than you really could.
+ At least when using auto_haggle. Poor sods that dont use it shall burn
+ in hell
+[I]- Damage info is displayed for items that are only identified
+
+26/11/2002
+[G]- OK the god quest is a little easier now, as you're told the dungeon is to
+ the north/south and east/west of you. -- fearoffours
+[G]- Having autosquelch on will now not affect the essence in the fireproof
+ quest. And the mage gives a warning about the fact it may be easy to
+ destroy it too. -- fearoffours
+[B]- Fixed the incredible townspeople generation rate
+[B]- String fixes from lemming -- neil
+[B]- Don't print discomfort messages when examining and comparing weapons -- neil
+[I]- new <state> function to the automatizer to detect identified state
+[I]- Automatizer rules are automatically apply upon exit of the screen or
+ autogeneration of a rule with the 'k' command
+
+28/11/2002
+[B]- Add experimental support to the automatizer for marking "bad" rings and
+ amulets -- neil
+[m]- Distinguish average, disarmed, and empty chests in the automatizer -- neil
+[B]- Let summoners summon again (patch by masmarangio) -- neil
+[B]- Remove duplicate warning of an empty quiver (patch by masmarangio) -- neil
+
+29/11/2002
+[O]- Let Demonshields and Demonhorns be treated as armor -- neil
+[m]- Can include string terminators in help file tags i.e:
+ *****foo.txt*7[see:\] it works;)]
+[m]- Can save screenshots to help file format, this is only usefull to
+ documentation writters and thus the option only appears when wizard mode
+ is activated. It uses the new verbatim mode &&&&& at the line beginning
+
+01/12/2002
+[m]- The helpfile typo corrections just keep coming! Added Udun school help,
+ and demonologist school help, thanks Mef and masmarangio. -- fearoffours
+[m]- Some more spelling errors corrected. Updated warriors skills in the
+ help files -- masmarangio
+
+02/12/2002
+[m]- Skill updates for more character classes in the help files. -- masmarangio
+[m]- Automatizer tutorial added. -- fearoffours
+[G]- God quest relic is now inscribed to prevent automatizer 'accidents'
+ -- fearoffours
+
+02/12/2002
+[O]- Wands/staves are now described this way: a wand of foo[bonus|max]
+ Bonus is the bonus spell levels you get, and max is the limit of spell
+ levels for that wand. It means even if you have Magic Device at level 50
+ you cannot get a level 50 spell from a wand with max 30. Naturally the
+ max(and bonus) increase with the depth you find the wand/staff :)
+[P]- Players in monster form can now use barehanded combat, instead of
+ reverting to monster attacks. -- neil
+[O]- Fix and reduce the wand and staff pricing -- neil
+[B]- Make the automatizer work independently of the player's body -- neil
+
+03/12/2002
+[m]- Clean out some unused variables and some other valid warnings -- neil
+[B]- Don't spoil things in the dump -- neil
+
+04/12/2002
+[m]- Updates of the character races help files -- masmarangio
+
+05/12/2002
+[B]- Maiar can't choose a god anymore -- masmarangio
+[I]- Added a message if you don't have powers but press 'U' -- masmarangio
+
+06/12/2002
+[B]- Staves are properly fireproofed now. -- fearoffours
+[m]- God quest gets more clues to the location, and they're printed in the
+ 'Ctrl-Q'uest screen. I'm too nice. -- fearoffours
+
+07/12/2002
+[I]- Added the extended command #quest (or #Q for short) to get quest list
+ because some prts catch the CTRL+Q combo
+[m]- Added contextual help for wands/staves, also added a section in the help
+ about it. --fearoffours
+[m]- Some small help file updates (Muar becomes Durin's Bane) -- masmarangio
+[m]- Added full mindcrafting spell info in the same form as other schools of
+ magic. Removed help on the Music skill, as this will not be included for
+ 2.1.0. Added some other help stuff. -- fearoffours
+
+08/12/2002
+[B]- Correct Damage/Round display for bare and bear combat. -- neil
+[m]- Help now has an alphabetical index! -- fearoffours
+[B]- Melkor wants you to sacrifice corpses, not raw meat -- neil
+
+09/12/2002
+[B]- Fixed an off-by-one error in loadsave.c that caused savefile corruption
+ if the number of monsters or objects saved was maximal -- masmarangio
+[m]- Removed help on the Druidistic skill since Druids are normal Priests now.
+ Minor changes to the skill example to match human warriors.
+ Updated Option help file, some other minor changes -- masmarangio
+[O]- Allow Wooden Rods to be sold at the Magic Shop -- neil
+
+10/12/2002
+[B]- Corrected the price of enchanted boomerangs and instruments. -- masmarangio
+[B]- Corrected the used multiplier while 'I'nspecting ammo.
+ Fixed the damage of throwing items / boomerangs -- masmarangio
+
+11/12/2002
+[I]- 'Compare weapons' works only with melee weapons, item selection changed.
+ Expanded the description of a god at birth. -- masmarangio
+
+12/12/2002
+[m]- Removed Tank Points and firestones. -- neil
+[m]- Removed PERNANGBAND monster flag and renamed as many Pern monsters,
+ artifacts, and other references as I could find -- neil
+[M]- Firebirds (formerly firelizards) and Thunderlords are B, not d and D
+ -- neil
+[B]- Fix some artifact activations -- neil
+[B]- Fixed (temporarily) item activation and description. Some items could
+ activate for the unified activation, and display the other activation, so
+ further changes are needed. -- masmarangio
+[I]- Removed the silly 'You are shooting with a flute' message. -- masmarangio
+
+13/12/2002
+[m]- Renamed instruments in luckspoi.txt, comment changes -- masmarangio
+[I]- Added OBJ_FOUND_SELFMADE for items that were created by the player.
+ Alchemist should also use it (not changed yet). Replaced the plain
+ description "in the Town" for items fond on the surface -- masmarangio
+
+14/12/2002
+[O]- Monster traps are disarmed by GF_KILL_TRAP and GF_KILL_DOOR -- masmarangio
+[m]- The description of ACT_DEST_DOOR includes traps, better description of
+ deactivating music instruments. -- masmarangio
+[M]- Increase Thunderlord rarities -- neil
+
+14/12/2002 -- T.o.M.E 2.1.0 aka "No Surrender, No Retreat"
+
+14/12/2002
+[B]- Artifact spoiler was messed up
+
+15/12/2002
+[m]- some small help files updates (Melkor, GoI, races) -- masmarangio
+[B]- some Orc Cave crashes fixed -- masmarangio
+
+16/12/2002
+[B]- Fixed the Mushroom Quest: mushrooms are taken before creating the rewards,
+ and these are created even with a full inventory -- masmarangio
+[B]- Grammar fix for breathing messages -- neil
+[B]- Changed order of level feelings, removed unused feelings. -- masmarangio
+[B]- A Rod of Drain Life is needed for vampiric artifacts instead of the Wand.
+ Renamed one ego light of Boldness (now of Fearlessness). -- masmarangio
+[m]- Updated and reformatted the essence spoiler. -- masmarangio
+
+18/12/2002
+[O]- Removed the double pval for Mana-items (40%)(+2). The description of
+ Mana and Life items is now with percents. -- masmarangio
+[m]- Small updates to m_demono.txt -- fearoffours
+[m]- Added mindcraft in magic.txt and a paragraph in m_mindcr.txt -- masmarangio
+
+19/12/2002
+[B]- Adjusted the melee damage shown in the status screen. -- masmarangio
+[B]- Spelling errors corrected. Thanks to markrax -- masmarangio
+[B]- Fixed bashing the trigger doors in the Thieves Quest -- masmarangio
+[B]- Allow Amulets of the Magi and *Defender* weapons to be sold -- neil
+[B]- Fix alchemist creation of ego staves, etc. (patch from "oops") -- neil
+[B]- Don't let gold into the inventory -- neil
+[B]- Temple doesn't want unblessed edged weapons -- neil
+[B]- Properly handle obvious flags on non-*ID*d objects (fixes things like
+ resistances grid and selling Blessed weapons in the temple) -- neil
+[B]- Squelch at different times, to avoid destroying the wrong items -- neil
+
+20/12/2002
+[m]- Removed some compiler warnings. Thanks to markrax -- masmarangio
+[I]- Work / fixes in spoiler creation (items, essences, menu) -- masmarangio
+[B]- Automatizer: <symbol> works with graphic modes -- masmarangio
+[B]- Fixed pval3 for artifact staves and wands in apply_magic -- masmarangio
+[m]- Corrected some typos and some comments. Thanks to markrax -- masmarangio
+[m]- Spell description edit -- neil
+[B]- Exclusive skill fix (patch by markrax) -- neil
+[B]- Yet more copyediting by markrax -- neil
+
+21/12/2002
+[m]- Corrected most of the typos found by vrak. -- masmarangio
+[B]- Fixed a display bug when passing through walls, thanks jup
+[B]- Copyediting of lib/edit/* -- markrax
+[B]- Copyediting of monster description books (lib/file/book-1[0-9].txt)
+ -- markrax
+[B]- Copyediting of misc. files in lib/file/ -- markrax
+[B]- Copyediting of most files in lib/help/ -- markrax
+[B]- Copyediting and description tweaks to tables.c -- markrax
+[B]- More minor copyediting -- markrax
+[m]- Minor externs.h code cleanup, remove dups -- markrax
+[B]- Let keypad 5 key work in X11 target -- markrax
+[I]- Add columns, tweak UI in character info sheet -- markrax
+[I]- Updated the artifact spoiler creation -- masmarangio
+[m]- Corrected some errors in a_info.txt -- masmarangio
+[B]- Display the arm slot when not wearing a two-handed weapon -- neil
+[I]- Cleaned up some code in the character info sheet -- masmarangio
+[B]- Fix drop message for junk artifacts (patch by jup) -- neil
+
+22/12/2002
+[m]- Copyediting of lib/edit/k_info.txt -- markrax
+[m]- Copyediting of lib/edit/al_info.txt -- markrax
+[m]- Added ENGLISH.txt file with ToME textual conventions -- markrax
+[m]- Standardized on "Middle-earth" spelling -- markrax
+[m]- More grammatical fixes (mostly "it's") -- markrax
+[m]- Removal of many Americanisms, grammatical fixes -- markrax
+[O]- Centered Map around Minas Anor -- masmarangio
+[I]- Allowed the user to abort when asked to engrave on a grid. Also, don't
+ let the player engrave on a grid with no mana. Thanks to Pav of
+ angband.~.cz for this patch. -- takkaria
+[m]- Updated and relocated style guide -- markrax
+[O]- CURSED two items with HEAVY_CURSE, thanks jup -- markrax
+[O]- CURSED Ring of Durin (had HEAVY_CURSE), thanks jup -- markrax
+[B]- Let *Identify* of your pack work the same as an individual *Identify*
+ for the purposes of alchemists -- neil
+
+23/12/2002
+[I]- Updated the broken spell spoiler creation (works for the bookless powers).
+ LUA schools should be added -- masmarangio
+[m]- Parchment titles capitalised. Spacing of artifacts and some americanisms
+ and errors in a_info.txt, tables.c, and other files corrected.
+ -- masmarangio
+
+24/12/2002
+[O]- Added description from Sangband to the Shield of Gil-Galad -- masmarangio
+[B]- tried to prevent printing of alchemy items in spell spoiler -- masmarangio
+[B]- Fixed two alchemy bugs: Same sval for Rings of Critical Hits and the Ring
+ of Durin. Corrected the sval of Rings of Constitution -- masmarangio
+
+25/12/2002 - Mery xmass!
+[P]- Boulder throwing skill :) For Ents .. Don't get used to it as I don't know
+ if I'll let it in
+[B]- Destruction cannot kill quest monsters
+[B]- The lost temple (god quest) dungeon really cannot be generated in
+ inaccessible places now. -- fearoffours
+[I]- Unidentified objects are marked in slate(the inventory letter)
+
+26/12/2002
+[m]- Updated the skill gain in skills.txt, corrected some spelling errors.
+ Added Boulder-throwing to the help files -- masmarangio
+
+27/12/2002
+[B]- Fixed note file problem under windows -- masmarangio
+[m]- Corrected some americanisms and spelling errors -- masmarangio
+[B]- Fixed an alchemy error with fireproofed staves, removed tabs from c_prt
+ calls -- masmarangio
+
+28/12/2002
+[P]- Added dynamic subrace. This means that "things" in the game can somewhat
+ change your subrace. This is used by the 3 new Vampire corruptions, which
+ when combined will permanently turn you into a full blown Vampire.
+ The Vampire subrace has been removed, it might come back as a template for
+ the corruptions, or not. All others undead subrace will have the same
+ fate. This means that soon you'll be able to turn into an undead rather
+ than start as one. The penalties will be tweaked too so we don't end up
+ with every char being an undead.
+[B]- Fixed a branding bug that allowed to modify artifacts and ego-items.
+ -- Kusunose
+[m]- Updated corruption_spoiler_generation (includes index tags, don't show the
+ Lose message for not removable corruptions) and corspoil.txt -- masmarangio
+
+29/12/2002
+[I]- Stores/homes inventory letters are colored just like inventory/equipment
+ thanks to Pav
+[m]- Music system performance improvement -- neil
+[m]- Added help on pets to dungeon.txt and some clarification about Spell-power
+ affecting only the 11 primary schools of magic. -- fearoffours
+[m]- Added to Spell-power the other affected Schools (Udun, Demonology, Gods),
+ as well as naming Sorcery in the Udun School help file -- masmarangio
+
+30/12/2002
+[O]- init1.c: added two times OBJ_FOUND_SPECIAL -- masmarangio
+[m]- al_info.txt: removed double acid essences in amulet of resistance, grouped
+ entries for ring of extra attacks, renamed some entries -- masmarangio
+[m]- wizard1.c: Added IS_CVS to header, create artifacts at 25, not 35, added
+ index in Essence Spoiler. essences.txt: new spoiler file -- masmarangio
+[m]- cmd1.c: removed a compiler warning. caves.c: simplified a test -- masmarangio
+[m]- Info on spell-power and multi-school-spell interaction, typo fix in
+ m_mindcr.txt -- fearoffours
+
+31/12/2002
+[B]- object2.c: Initialised Artifacts with LEVELS in apply_magic -- masmarangio
+[O]- generate.c: OBJ_FOUND_MONSTER for final artifacts / objects -- masmarangio
+[B]- al_info.txt: Svals of Ring of Sustain Con and Dex exchanged -- masmarangio
+
+01/01/2003 - Happy new year !!!
+[B]- store.c: Fixed bug with prices of wands, indentation -- masmarangio
+[I]- Better fountain command interface. -- Kusunose
+[B]- al_info.txt: Added Ammo of slay animal. Amulet of regeneration with new
+ sval. Ego Weapons of Life added -- masmarangio
+
+02/01/2003
+[B]- find_ignore_stairs, set or unset, failed to handle some quest related
+ stairs properly. -- pelpel
+[B]- cmd7.c: Fixed bug with TR4_ART_EXP not set correctly -- masmarangio
+[O]- cmd7.c: Added OBJ_FOUND_SELFMADE for created items/artifacts -- masmarangio
+[B]- Don't allow a change from weapon combat if a cursed weapon is wielded
+ -- neil
+[m]- wizard1.c: Changed position of IS_CVS, added double ego items in essence
+ spoiler. essences.txt: reflects this -- masmarangio
+
+03/01/2003
+[B]- wizard2.c: Some bound checks to prevent crashes -- masmarangio
+[B]- cmd7.c: Fixed reduced capacity of ego rods -- masmarangio
+[B]- object1.c: Added a "It cannot be destroyed" msg in place of "it has blah%
+ chance of breaking upon hit" for artifact ammo. thanks lemming for fix
+ -- fearoffours
+
+04/01/2003
+[O]- e_info.txt: Added indestructible and cursed amulets to simplify alchemy
+ -- masmarangio
+[B]- cmd7.c: Allow creation of artifacts from single ego items; don't change
+ artifacts and double ego items if you don't wish to create an artifact.
+ Simplified amulet handling and other code fragments -- masmarangio
+[m]- QUITING-> QUITTING, COMBINAISON->COMBINATION: two cases of typos
+ that I've long been aware of... -- pelpel
+[D]- Changed the marker(#172) to look like open floor(#1) -- masmarangio
+[B]- Updated Amulets and Rings in defines.h / defines.txt -- masmarangio
+[B]- al_info.txt: Amulet of Adornment without pval. cmd7.c: skill >= 25
+ needed to create artifacts, not > 25 -- masmarangio
+[B]- Warning about leaving a trap detected zone don't cost energy -- masmarangio
+
+05/01/2003
+[m]- First update of commands.txt -- masmarangio
+[B]- spells2.c, self_knowledge(): f4 through esp were not initialised before
+ referenced. -- pelpel
+[B]- cmd7.c, extern.h: changed the variable "tocreate" in alchemist_items_check()
+ from bool to int -- masmarangio
+[B]- xtra1.c, apply_flags(): Added bounds checks for antimagic. -- pelpel
+[B]- xtra1.c, apply_flags(): changed the type of bit field variables from s32b
+ to u32b, for obvious reasons. -- pelpel
+[G]- Princess now offer 3 reward and the player selects one
+
+06/01/2003
+[B]- cmd7.c: Fixing three bugs in Alchemy: Extracting from double ego items and
+ from stacks of staves, as well as generating all leeched empty items.
+ Problems may still arise when you leech a stack of objects from the floor
+ and essences get dropped over them. Boomerangs can be enchanted now.
+ -- masmarangio
+[B]- generate.c: Moved the room creating loop (fix from 15/12). Something better
+ should be used than this brute force method -- masmarangio
+[B]- Minor spelling and grammar fix -- neil
+[B]- Changed the type of dungeon_flags to u32b, as in types.h -- masmarangio
+[B]- cmd7.c: Instruments can be enchanted now. -- masmarangio
+[G]- Applied the Artifact Activation patch -- masmarangio
+[B]- Fix activations -- neil
+[G]- Alchemists can no longer make artifacts granting Precognition or Immunity
+ to Nether -- neil
+[B]- Avoid some bad periods in monster descriptions -- neil
+
+07/01/2003
+[P]- p_info.txt: Updated, compacted history-chart. Trolls, Orcs, Elves with new
+ entries, some other small changes. Deleted the unused races -- masmarangio
+[m]- Added Activations to essence spoiler, new help file created -- masmarangio
+[B]- Reset the pval of extracted rings and amulets, prevent creation of cursed
+ empty items -- masmarangio
+[B]- Make ego staves / wands of nothing extractable, disallow empowering of ego
+ items / artifacts that are not wearable (e.g. staves) -- masmarangio
+
+08/01/2003
+[I]- squeltch.c: Ask for overwriting rules file and display saving message in the
+ same box -- masmarangio
+
+10/01/2003
+[m]- Added a (conservative and preliminary) port to OS X + gcc + makefile.
+ The four *.icns files are faithful conversions of the traditional Angband
+ icons that have long been used by Mac ports, even before Ben.
+ makefile.std itself is not updated. Please read comments in main-crb.c.
+ -- pelpel
+[m]- Module support. It means that people can write ToME "modules" which will
+ go in lib/mods and contain "variants" that use the same game engine but
+ different lib/foo files. The module selection screen only appears when
+ the game detects more than one module. The new -Mfoo command line option
+ allows to bypass module selection
+[m]- 'Death' Message of a Nazgul: Choose pronoun 'she' or 'he' according to the
+ sex -- masmarangio
+[G]- God quest wont trigger for Lost Souls until they reach the surface
+[m]- Turn off some ToME things when using a non-ToME module -- neil
+[B]- Fix the monster description messages even better -- neil
+[B]- Better honor the NO_TARGET flag -- neil
+[M]- Give Farmer Maggot the NO_TARGET flag -- neil
+[B]- Fixed (hopefully) the wrong activation bug -- masmarangio
+[m]- (Mac, Carbon) Modernised file system interface (currently only turned on
+ for Mach-O Carbon port), improved Mach-O Carbon support, incorporated my
+ 32x32 tiles support code for V3.0.2, and more gcc porting note -- pelpel
+[m]- Let the player see which game module he selected in the character
+ selection screen -- neil
+
+11/01/2003
+[B]- Fixed one wrong sval in al_info.txt -- masmarangio
+[m]- Added makefile and some auxiliary files for developer CD gcc compilation
+ of Carbon port -- pelpel
+[m]- (OS X Carbon) Changed the format of graphics tiles from PICT in the
+ resource fork to plain PNG files, only for gcc compilation at the moment
+ -- pelpel
+
+12/01/2003
+[B]- Alchemy: The song of music instruments, the type of explosive ammo and
+ the level of sticks are not changed by enchanting / leeching the items.
+ -- masmarangio
+[m]- Expanded the description of Antimagic, small index updates -- masmarangio
+[I]- wizard2.c: Initialised the lua command (^A >) with the help file index
+ generation -- masmarangio
+[m]- (OS X Carbon) Cleaned up my recent updates. It's fairly stable now.
+ To do: move to .nib based menu/dialogues, pulling sound out of resource
+ fork, and better sound code. -- pelpel
+[B]- cmd7.c: Allow extraction of the charges of Sticks of Plenty -- masmarangio
+
+13/01/2003
+[m]- preliminary SCANDIR support for gcu, gtk, xaw borrowed from x11 -- neil
+
+14/01/2003
+[m]- Modules can control the number of levels over the player level a skill
+ may have allocated now, using max_skill_overage. -- neil
+[m]- HOOK_FIRE added -- neil
+[m]- Changed the help files about vampires; classes, races etc. are listed
+ in alphabetical order in birth.txt -- masmarangio
+[m]- Races & subraces get a starting object list in p_info, less hacks in birth.c
+[I]- Automatizer can now be used to auto-inscribe items, thanks to jepler
+[I]- Automatizer can now display rules in a more english like maner, xml mode
+ is also available, switch by pressing 'x', thanks to jepler
+
+15/01/2003
+[G]- Summoner can extract totems from skeletons / carapaces. -- masmarangio
+[B]- Trap of Wasting Wands (affects also Staves) change the Wand/Staff to the of
+ nothing type. Deleted SV_WAND(STAFF)_NASTY_WAND(STAFF), since in the new
+ stick system the worthless sticks don't have the first svals -- masmarangio
+[B]- Fixed a fountain filling bug, simplified the item_tester -- masmarangio
+
+16/01/2003
+[B]- (Mac, OS X) Tilewidth/height code ceased to work because menu API started
+ to return Unicode crap instead of holy ASCII -- pelpel
+
+17/01/2003
+[m]- HOOK_EAT added -- neil
+[O]- New artifact: The Sling of the Thain -- neil
+
+18/01/2003
+[B]- empty chests are generated as known (i.e. they were opened) -- masmarangio
+[B]- do_cmd_rest flushes input if the player can't do so for some reasons
+ and flush_failure is set.
+ Note: flush() doesn't seem to work as it used to do... Any changes have
+ been made to inkey, or some code touches its various control variables?
+ -- pelpel
+[m]- (Gtk) Added support for the "bigtile" mode. Please start the game with
+ "tome -- -w" if you don't like to see horrible glitches... This is not
+ Gtk-specific problem by the way. Scrolling the map and hitting ^R fixes
+ it too, if you prefer menu command -- pelpel
+[G]- Trees are now passable by wraith beings. Monsters that fly will pass trees.
+ Dead small trees will be dead small trees. Monsters tunneling will also
+ destroy trees
+[P]- Extra HP bonus(from quest, melkor, ...) is applied before sorcery penality
+
+19/01/2003
+[B]- (Gtk) Fixed problems with wide tile mode when it is used with backing
+ store. Seems more like the problem is in z-term (it only updates every
+ two columns of the dungeon map...), but it works now -- pelpel
+[B]- (Mac) The asynchronous sound player unlocked and released sound data
+ without confirming its completion. Wrote an alternative implementation
+ that holds data until a channel it is played is reused, to avoid using
+ interrupt-time code (which is quite complicated business in 68K).
+ Because this means that a fairly large amount of data can be locked in
+ memory for a long time, I reduced number of channels used to
+ 4 in Classic and 8 for Carbon -- pelpel
+
+20/01/2003
+[G]- Trees/grass can only grow one some terrains(yes that DO exclude lava;)
+[I]- z) slot is hidden until needed -- thanks to Scott Bigham
+[I]- Some cosmetic changes and improvements to char dumps -- thanks to Scott Bigham
+
+21/01/2003
+[B]- Monster traps: Changed the code so that the new wands and staves are
+ functional in device monster traps. This is commented out with #if 0 since
+ the code must be tested. Perhaps a lua solution is better ? -- masmarangio
+
+22/01/2003
+[B]- Deathmolds could use their racial power without spending a turn!
+[B]- 'U' powers didn't remove Disruption Shield
+[m]- (Mac, Windows) Added an in-game menu command to view current scoreboard.
+ Taken from the latest [V] (Win port only there), minus the use of
+ display_scores_aux, which I regard first-degree Mega-Hack -- pelpel
+[G]- Wielding the One Ring now has real disadvantages, you've been warned :)
+ What ? I didn't mention what disadvantages ? Oh yes, I didn't.
+[G]- Bump required level and difficulty rating of Minas Anor quest, as the
+ trees changes have made it just a little harder. -- neil
+
+23/01/2003
+[I]- Hopeless attempts to dig will be noticed to the player -- thanks to jepler
+[B]- Potions of Cure Water are no more extractable by Alchemy
+
+25/01/2003
+[P]- Bard class is back(was called Harper before) with a new Music skill.
+ Instruments carries the spells, and they work a bit like Psi foci in
+ psiband. An instrument (+2) can only play songs that are marked (I) or
+ (II) not (III) or (IV)
+[B]- Ordered the music songs according to the needed pval, then level.
+ Fixed the svals of artifact instruments, changing the Flute to a Harp.
+ Added a better description of Bards. Please check it ! -- masmarangio
+
+26/01/2003
+[B]- An Alchemist can now fireproof spellbooks -- masmarangio
+[m]- Bard and Music docs are done. Please check em. -- fearoffours
+[B]- A possessor (the creature, not the class) can only possess whole
+ corpses and skeletons, not pieces of meat -- masmarangio
+
+27/01/2003
+[B]- Make wielding more than one demonshield (or demonsword I suppose) work
+ -- neil
+
+28/01/2003
+[B]- Possession something while encumbered to the point of having 0 mana
+ allowed free use of body spells without failure
+[P]- Mana is now function of the Magic skill and either INT or WIS, whichever
+ is higher
+
+30/01/2003
+[B]- Possibly the relic in the god quest couldn't have been generated.
+ Wasn't me honest guv. -- fearoffours
+[G]- The lost temples for god quests now have harder monsters to make the piety
+ gain rather more substantial and obvious I hope. Deeper monsters appear
+ as player clvl increases. -- fearoffours
+[m]- Help file corrections on Music skill. -- fearoffours
+
+01/02/2003
+[B]- Some menus exits when 'e' was hit on some comapilation.
+ Note: '\e' for escape is compiler dependant extension so do not
+ use it. Thanks kobayasi for fix. -- Kusunose
+[B]- All known staves and wands are displayed as 'Globe of Light' in
+ the Known Object list. Thanks kobayasi for fix. -- Kusunose
+
+08/02/2003
+[m]- Make HOOK_QUAFF take an o_ptr, and let HOOK_EAT return the identify success
+ -- neil
+
+12/02/2003
+[I]- Useless but fun, when a wield monster takes damage instead of you
+ the monster will be named. If you named it(with the #foo inscription)
+
+16/02/2003
+[B]- Chaging the melee style did not update bonuses. Thanks to kobayasi for
+ the patch. -- Kusunose
+[B]- <sval> in automatizer worked even when the player was not aware of
+ the object. It was possible to use this as a free ID. Thanks to
+ kobayasi for the patch. -- Kusunose
+
+24/02/2003
+[B]- Cancelling the reward skill sellection could crash the game. -- Kusunose
+
+26/02/2003
+[I]- Boomerangs now display damage with 'I'nspecting, thanks to Scott Bigham
+
+27/02/2003
+[B]- Inspecting or Identifying something shouldn't infect you with the
+ Black Breath -- neil
+
+28/02/2003
+[I]- Genocide and genocide like effects that require monster race now allow
+ targetting of a monster to select the race. Gfx users can now correctly
+ genocide :)
+
+01/03/2003
+[I]- d/s and g/p work in both home & stores. Pfft.
+
+02/03/2003
+[m]- Fixed a typo in The Dragon Helm of Turin Turambar -- WeZ
+[G]- Fixed the teleport-level ability of Deathmolds, so they
+ can teleport out of a dungeon, instead of getting a
+ message saying there's only air above them. -- WeZ
+
+04/03/2003
+[P]- Probability travel level teleport ability & teleport level spells
+ wont work in some dungeon
+
+05/03/2003
+[G]- A new quest! An ultra ending! "Falling Toward Apotheosis"
+ After banning Morgoth spirit in the Void, you can pursue him
+ there to forever end the darkness. But beware, it is more than
+ likely that you will die. But for the few that succeed, the fame
+ will be great!
+ Note: you can only do it if you choosed the way of Good and thus
+ destroyed the One Ring. An evil ultra ending is planned and will
+ come a bit latter.
+
+06/03/2003
+[P]- Dwarves get a bonus to axe mastery, as they rightfully should
+[O]- Cloak of Air. Generates an air bubble around you so you can breath.
+ Just in case you need it ..
+
+08/03/2003
+[G]- Another new quest! An old mage in Minas Anor needs help, too. -- neil
+
+12/03/2003
+[B]- Non-enemy monsters now won't hold up victory in the Spiders and Library
+ quests. -- neil
+[B]- Various Library quest fixes -- neil
+
+13/03/2003
+[O]- Lebohaum activation is now true to the original material..
+[P]- All mages start with 0.1 less mod to Sorcery. Specialists dont have sorcery.
+ Runecrafters dont iether, nor Alchemists. Thaumaturgists do.
+ Specialists also get a bit better Spell-power
+
+16/03/2003
+[I]- Only show enemies in the uniques list -- neil
+
+20/03/2003
+[m]- The various ?_info.txt files can now include otehr files with the <:file.txt
+ command. This allows people to split up too big files
+
+23/03/2003
+[I]- X11 port can now do text selection copy&paste with the mouse, thanks to kieron
+
+27/03/2003
+[B]- Mage staves, rod tips, and magic tomes are now "good" items. This should
+ fix the problem of Saruman, Sauron, and others dropping nothing but rods.
+ -- neil
+
+28/03/2003
+[I]- Show "ironman_rooms" setting in the character dump -- neil
+
+29/03/2003
+[P]- The adventurer quest skill reward will now only provide a modifier of 0.3
+ if you dont have the skill, but if you have it it will improve the modifier
+ by 0.1 if it is below 0.5
+[I]- Automatizer rules now have an module attribute, defaulting to "ToME". The
+ module name is case sensitive, as always. <rule module="ToME" name="Die"
+ type="destroy"> will only destroy items when you're playing the ToME
+ module. -- neil
+
+02/04/2003
+[B]- Fix grammar error pointed out by "JanaRaissa" -- neil
+[B]- Prevent instrument-wielders from firing things, as pointed out by
+ "Chudus" -- neil
+[B]- Those who can breathe water or don't need to breathe at all should not
+ drown in pools of water, as pointed out by "Zonk" -- neil
+[B]- Fix Rods of Simplicity for those who need it most, as pointed out by
+ "Zizzo" -- neil
+
+03/04/2003
+[O]- Dragon Scale Mail now has more options for egos and random artifacts
+ -- neil
+
+04/04/2003
+[B]- Fix god quest temple placement error pointed out by "Fringe Worthy"
+ -- neil
+
+05/04/2003
+[I]- Somewhat nicer char screen, thanks to lemmings(I think ;)
+[B]- No more (broken) pattern vaults -- neil
+
+08/04/2003
+[m]- Ability to add timers in lua
+[B]- Fix spelling: PARCHEMENT -> PARCHMENT. Automatizer rules and other scripts
+ that use PARCHEMENT will still work, though. -- neil
+
+09/04/2003
+[M]- Companions and pet uniques can now be killed
+
+11/04/2003
+[P]- Trolls cannot get the troll blood corruption
+[P]- At Monster lore skill level 12 one can turn a pet in a loyal companion.
+ Note that you are limited in companion number
+[P]- Reduced Necromancy spell failure damage
+
+12/04/2003
+[P]- Skill multipliers of Rogues and Assassins are higher now. -- neil
+[B]- Fixed alchemy making of xtra shots/might
+[P]- Some skills have more chances to be taugth than others by fumblefinger
+
+13/04/2003
+[P]- Melkor Curse spell autocast chance formula changed
+[O]- Wishing for dual ego items works
+
+14/04/2003
+[P]- Spellbinder spell will reveal more info about itself when cast while
+ already active
+[P]- Increase Mage's Weaponmastery Multiplier by 0.1 -- neil
+[P]- Experimentally double Barehanded Combat martial arts damage dice
+ to make Monks more attractive -- neil
+
+16/04/2003
+[O]- Goods stolen from stores now get a 100% discount. -- neil
+[P]- One can now steal from more places -- neil
+
+17/04/2003
+[B]- Everburning torches, Feanorian Lanterns, and Dwarven Lanterns of Fading
+ couldn't be filled -- neil
+[B]- The Crumpled Scroll of Mass Resurrection no longer revives special
+ monsters who won't ever appear again anyway. -- neil
+
+18/04/2003
+[B]- Gauntlets that contain spells no longer hinder spellcasters -- neil
+[I]- Some junk removed from the character resistances grid -- neil
+
+18/04/2003
+[G]- Bree house is not available from start anymore, you have to get it
+ as a reward from the thieves quest
+
+20/04/2003
+[G]- The Lothlorien and Gondolin homes aren't free anymore, either -- neil
+
+21/04/2003
+[G]- The Minas Anor and Khazad-dum homes must be won, too -- neil
+
+24/04/2003
+[I]- Reworked 16x16 gfx by P_laloli@hotmail.com
+
+25/04/2003
+[B]- Fix the case where some birth items wouldn't stack -- neil
+[B]- Disarming a trap on a stair should not destroy the stair -- neil
+
+26/04/2003
+[B]- Player invisibility shouldn't hinder monsters attacking targets other
+ than the player (patch by Jeff Epler) -- neil
+
+27/04/2003
+[B]- Mana and Life drains should not cause infinite rest loops -- neil
+
+28/04/2003
+[O]- Handmade items like arrows and totems are fully identified -- neil
+
+29/04/2003
+[m]- Max skills internaly increased to 200
+[G]- Random quest are now disabled when persistent dungeon levels are enabled.
+ -- neil
+
+30/04/2003
+[O]- The artifact that had aggravation and a stealth bonus no longer has
+ that useless stealth bonus -- neil
+[O]- It's now much harder for sentient weapons to get the Acid realm -- neil
+
+01/05/2003
+[I]- '+' command(alter terrain) wil ltry to open doors instead of tunneling
+ them
+
+02/05/2003
+[P]- Shots now pierce when they used to ricochet in random directions -- neil
+[P]- Piercing shots are now togglable -- neil
+[G]- Singing happy drunks will freely accept alcoholic beverages from you.
+ -- fearoffours
+[m]- Class helpfiles updated to reflect some changes in skill allocation.
+ -- fearoffours
+[I]- Turned on by default the linear stats display
+[m]- Chris Hadgis submitetd the first documented package of the ToME API!
+ More comming :)
+[I]- Maxxed stats now show a "!" in the character screen and dump, too -- neil
+[P]- If you play something experimental, it now shows up in the character
+ dump. Alchemists and Death Molds are marked as such for now -- neil
+[P]- Necromantic magic got a new spell: Necromantic Teeth
+
+05/05/2003
+[B]- Stone Prison spell would create floor where it shouldn't, possibly
+ trapping you in places like the Eol quest. -- neil
+
+06/05/2003
+[G]- Angband now blocks the magic of the Thunderlords' Nest -- neil
+[B]- Fix activation descriptions patch by Chris Hadgis -- neil
+
+09/05/2003
+[I]- ToME should try harder to use proper thematic flooring when appropriate
+ -- neil
+[B]- Stone Prison should not be able to turn walls into floors. -- neil
+
+11/05/2003
+[B]- Sound effects shouldn't give knowledge the player wouldn't otherwise
+ know (like the generation of the relic) -- neil
+
+12/05/2003
+[B]- Don't show silly chance to break for exploding ammo -- neil
+
+14/05/2003
+[m]- lib/patch directory to contains auto-loaded patches.
+ Each patch must be contained in it's own subdirectory and
+ must contain a patch.lua file with a patch_init() function in it
+ that must return the patch name and version
+
+16/05/2003
+[P]- Abilities. Abilities are like skills, but are booleans. That is
+ you either know them or not, there is no scale. One can learn
+ abilities by spending skill points in the abilitie screen(press 'N').
+ Abilities can have prerequisites to be meet before one can learn them.
+ (Like some can need a stat level, a skill level, ...)
+ Some classes get abilities for free at certain level. Like warriors
+ will get Spread-blows ability at level 25 for free(even if they dont
+ actually meet the prereqs).
+[P]- All classes now get 4 max blows. But there abilities that can increase
+ the max. Warriors gets them for free at birth, and other classes also
+ get some so they are not less powerful than before the change. This
+ will however help people create warrior-mages for example.
+[m]- Internal change: Magic Realms are gone. -- neil
+[I]- No more spell list window flag. It hadn't done anything since ToME 2.0
+ anyway -- neil
+[m]- Total savefile compatibility break. Hopefully we can do better from now
+ on -- neil
+
+21/05/2003
+[I]- The player now gets warned during race/class/subrace selection that a
+ class is experimental. -- neil
+
+23/05/2003
+[B]- Innate monster friendliness shoudln't take precedence over summoned pet
+ or companion status -- neil
+[B]- The Helm of Knowledge and Automatizer pickup now get along -- neil
+
+26/05/2003
+[m]- Initial helpfiles update for Abilities. -- fearoffours
+
+27/05/2003
+[I]- Princess quest reward selection is not cancelable
+
+28/05/2003
+[P]- New skill: Geomancy, works in combinaison with the 4 elemental skills
+ to produce various effects from the raw elements around teh caster
+ (grass, sand, ...). Elementalists becomes Geomancers now.
+ Thanks to Fubar Obfusco for the detailed idea
+[P]- Runecraft is not gainable from fumblefingers until it is reworked
+
+29/05/2003
+[m]- More help updates - Artifact Creation ability, plus addtions to FAQ and
+ skills.txt and others. Thanks to Michael Beatty for some suggestions.
+ -- fearoffours
+[I]- Autoroller now uses linear stats when requested
+[I]- Object window will now display the 'I'nspection of the last manipulated
+ item
+
+30/05/2003
+[B]- Resting ignores HP or SP if a drain on HP or SP is induced by an object
+[O]- Beat down the power of randart rings yet more -- neil
+
+31/05/2003
+[m]- Geomancy help. -- fearoffours
+[O]- heavy Xbows of Siegecraft, thanks to Fang
+
+01/06/2003
+[B]- The automatic drop from wilderness should no longer be abusable. No longer
+ can you hit < when you're hungry, cut, poisoned, or sensitive to light during
+ the day -- neil
+[P]- Far reaching attack ability. When using a long polearm you can attack monsters
+ 1 or even 2 grids away(at the cost of a reduced number of blows)
+ Idea taken from Adamant
+[O]- Ingeborg S. Norden's artifact bolts added -- neil
+[P]- Trapping is now an ability instead of a skill. Rogues, but not Assassins,
+ start with it -- neil
+[m]- Help updated to reflect new abilities. Deathmold question added to FAQ.
+ -- fearoffours
+
+06/06/2003
+[P]- Undead Form is now an ability
+[m]- Documentation for Undead Form added. -- fearoffours
+
+08/06/2003
+[m]- Added skill tests abilities to .prf files thanks to Scott Bigham
+[B]- Thaumaturgy attacks will travel more than one grid when not targeted
+ at a monster thanks to Scott Bigham
+[I]- Inscription and discount clauses for the automatizer thanks to Scott Bigham
+
+09/06/2003
+[O]- Boomerangs now have just a 1% chance to break, have their chance to hit
+ improved by Boomerang-mastery, get sold in shops, and are a bit less
+ rare -- neil
+
+10/06/2003
+[G]- The god quest is a (little) bit more forgiving about the direction the
+ dungeon lies in, and you also get an approximation of it's distance.
+ -- fearoffours
+
+11/06/2003
+[B]- Thaumaturgy spells could be too weak -- neil
+[B]- Demonologists couldn't use the Demon Summon spell -- fearoffours
+
+14/06/2003
+[P]- Thaumaturgy spell levels now range from 1-50 instead of 1-100, so now
+ one won't get spells with impossible failure rates, as suggested by
+ Jules Bean -- neil
+
+16/06/2003 -- T.o.M.E 2.2.0 aka "Born to the Purple"
+
+18/06/2003
+[B]- Fix manwe spell Avatar
+
+19/06/2003
+[B]- Silly silly bug in god quest fixed. -- fearoffours
+
+20/06/2003
+[B]- Mimics starts with a cloak
+[B]- Using the @ key in skill menu wont crash
+[B]- Cannot copy uncopiable spells
+[B]- Dripping Tread will put feats on the last grid, not the current one
+
+21/06/2003
+[m]- Clearer descriptions for spells which require more than one school
+ -- fearoffours
+[P]- Geomancer tweaks: Geyser's mana progression now matches its damage
+ progression. Geomancy now gives much larger bonuses to its child
+ skills, so Geomancers are now intended to max out the elements. -- neil
+
+22/06/2003
+[B]- Potions of Cure Water are inscribed for you to prevent mishaps with
+ the automatizer -- neil
+[B]- Don't let people travel with extra limbs, to prevent them losing
+ weapons by mistake -- neil
+[B]- Allow hypnosis of companions, make hypnosis failure messages more
+ useful - neil
+
+23/06/2003
+[m]- Mimicry help. Magic help now has it's own menu, other minor help
+ updates. -- fearoffours
+
+29/06/2003
+[m]- "Investing in the <foo> skill? You might be interested in the <bar>
+ ability." links in skills.txt --fearoffours
+[B]- God quest bug genuinely fixed now -- fearoffours
+[P]- Bear form will now starts at -5 speed but end at +5
+
+29/06/2003 -- T.o.M.E 2.2.1 aka "There All the Honor Lies"
+
+09/07/2003
+[B]- Prevent forbidden staves from being generated
+
+14/07/2003
+[B]- Really fix the God quest this time, we hope
+
+15/07/2003
+[m]- Mac OS X Module support
+[m]- Solaris compile workaround from Kevin W. Thomas
+
+16/07/2003
+[B]- Fix broken status in auto automatizer rules -- neil
+[m]- Valid XHTML in the HTML screenshots now -- neil
+
+17/07/2003
+[B]- HPUX support -- neil
+
+20/07/2003 -- T.o.M.E 2.2.2 aka 'And Now For a Word'
+
+20/07/2003
+[P]- No more involuntary corruptions
+
+02/08/2003
+[B]- Fixed crash on 'l'ook in rare conditions, thanks slappy0042
+[B]- Fixed inflation of ammo damage display, patch by 'Zizzo' -- neil
+
+05/08/2003
+[m]- Help updates - spectres lose hp when passing walls, thieves cannot shoot
+ arrows (very well), non-geomancer mages have a 0.00 modifier to Geomancy.
+ -- fearoffours
+[m]- Help updates - typo in rm_spec.txt and High-elves have good wisdom
+ -- fearoffours.
+
+08/08/2003
+[B]- Tulkas piety gains now match the documentation -- jules
+
+10/08/2003
+[m]- helpfiles - Clearer geomancy skill values for non-Geomancer Mages, a note
+ about effect of stealing from shops in skills.txt, and some FAQ additions
+ about FF and random quests. -- fearoffours
+
+13/08/2003
+[B]- Manwe, Eru, Yavanna, and Tulkas will not yet you start following them if you
+ are wearing the One -- neil
+
+24/08/2003
+[B]- Set number of charges correctly when a wand is stolen -- neil
+
+26/08/2003
+[m]- Scott Bigham's tome-autoabil.diff: Adds a new automatizer class
+ <ability>, which is true when you have the given ability -- neil
+
+27/08/2003
+[m]- Riscos support thanks to Antony Sidwell
+
+30/08/2003
+[m]- Savefiles are now stores in ~/.tome/save[/modulename] on multiuser systems
+[m]- Apply patches based on those from FreeBSD ports -- neil
+
+06/09/2003
+[O]- Beaked Axes, Battle Axes and Lochaber Axes are recognized as polearms
+ again. -- neil
+[O]- Misleading "polearm" description removed from non-polearm
+ Quarterstaff -- neil
+
+14/09/2003
+[m]- Added help for 'repeat last command' to command.txt. -- fearoffours
+
+15/09/2003
+[B]- Fixed autopickup of ammo crash under some circumstances
+[B]- Fix monster list mimic bug
+[B]- Tree Roots incorrect bonuses fixed
+[B]- Fixed incorrect display of some skill bonuses
+
+15/09/2003 -- T.o.M.E 2.2.3 aka 'The Quality of Mercy'
+
+22/09/2003
+[B]- Detect doors and traps displays as costing 3, not 5 now. -- fearoffours
+[B]- Maeglin quest reward wording altered (saves confusion for sorcerors).
+ -- fearoffours
+[I]- Adam Bolt's Tiles are now "New Tiles", as others have worked on them and
+ stuff. -- fearoffours
+
+25/09/2003
+[B]- Typos in One Ring parchment and Necromancy help fixed. -- fearoffours
+
+27/09/2003
+[B]- Typos in ultra-good ending, lost-sword quest rewards and flag description
+ fixed. -- fearoffours
+[O]- Restore spaces to artifact descriptions -- neil
+
+28/09/2003
+[B]- Axemasters now start with a Hatchet -- neil
+[B]- Fix "analyze monster" activation -- neil
+
+04/10/2003
+[B]- Fix library quest breakage of Melkor curses -- neil
+
+05/10/2003
+[B]- Fix failure rates for abilities in possessed bodies, patch by 'Slappy'
+ -- neil
+
+06/10/2003
+[B]- Monsters detected ONLY by telepathy were not removed from visible monsters
+ list after they had gone out of range. Thanks Slappy for fix. -- fearoffours
+[B]- Recharged junkarts would not always give a notification message. Thanks
+ Slappy for fix. -- fearoffours
+[O]- Changed descriptions of some junkart activations. -- fearoffours
+[B]- Completion of Necromancer quest could be triggered incorrectly.
+ -- fearoffours
+[D]- "The lava burns you", becomes "you move across the lava" to accomodate
+ IM_FIRE and flying characters. -- fearoffours
+
+07/10/2003
+[m]- Applied Ken Dubuc's patches to parsers to make them line ending independant
+[m]- Savefiles on multiuser systems are now in HOME/.tome/2.2/save, to comply
+ with the new 3.0.0 naming scheme that is coming.
+ People upgrading from 2.2.x to 2.2.4 should move their savefiles there
+
+08/10/2003
+[B]- Monsters don't appear to disappear for a turn if you polymorph them -- neil
+[m]- Setting module to "all" for an automatizer rule makes the rule apply
+ to all modules -- neil
+
+11/10/2003
+[B]- Flying semi-wraiths shouldn't be hurt by flying over trees -- neil
+
+13/10/2003
+[B]- Recalling to new lost temples could have crashed the game. -- fearoffours
+[B]- God quest directions could be grammatically incorrect. -- fearoffours
+
+23/10/2003
+[B]- Items containing spells should be browsable again -- neil
+[B]- Random artifact mimicry cloaks should now show up correctly -- neil
+[B]- Random artifacts that can contain spells will not come with
+ Globe of Light anymore -- neil
+[B]- Certain non-random artifacts that can contain spells will not come with
+ Globe of Light anymore, either -- neil
+[B]- Pack overflow/weapon disappearing bug fixed, with big thanks to Kevin
+ W. Thomas for telling me where to look -- neil
+[m]- Wizard mode is now turned off at birth -- neil
+[B]- Trap of Divine Wrath would benefit you -- neil
+[B]- Blood of Life would revive necromancers in undead form -- neil
+[B]- Hostile monsters won't get free kills of friendly monsters anymore -- neil
+
+28/10/2003
+[B]- Eggs work again -- neil
+[B]- Wishing for ego rods works -- neil
+
+29/10/2003
+[B]- Temples now accept blessed boomerangs -- neil
+[O]- Non-functioning artifact instrument activations removed -- neil
+[B]- If you learn anti-magic, you stop following any Valar -- neil
+[P]- Wood Elves and Hobbits weren't getting the extra might bonuses
+ they were entitled to -- neil
+[B]- typo in v_info.txt -- fearoffours
+[m]- Help updates: macros and macro recorder, dwarf racial ability, more on
+ dodging, link to m_mimic.txt from skills.txt, object inventory letter
+ colouring significance. -- fearoffours
+[B]- Old Mage's quest wouldn't always disappear from quest screen when fully
+ rewarded. -- fearoffours
+
+30/10/2003
+[m]- Helpfiles updated to reflect that corruptions are permanent and there is
+ no corrupted sub-race. --fearoffours
+
+03/11/2003
+[B]- No more shafts in certain places -- neil
+[B]- Percing shots will not grossly inflate in damage anymore -- neil
+[B]- No genocide in home quests -- neil
+
+11/11/2003
+[B]- Silent Switching can no longer take off permanently cursed items -- neil
+[B]- Fix memory error in command handling -- neil
+[B]- Hypnotizing and restoring levelled monsters now works correctly -- neil
+[B]- permanent_levels marked experimental because it is broken -- neil
+
+17/11/2003 -- T.o.M.E 2.2.4 aka 'A Bug's Life'
+
+19/11/2003
+[B]- Fix monster possessor crash -- neil
+[B]- Fix random quest skill requester -- neil
+
+20/11/2003
+[B]- Fix crash when picking up ammo into your quiver that can't be fired
+ with the launcher you are wielding -- neil
+
+22/11/2003
+[B]- God quest fix -- neil
+
+24/11/2003
+[B]- Disintegrating walls should not bother Yavanna -- neil
+
+01/12/2003
+[B]- Yet another try at fixing levelled carried monsters. Their attacks
+ are affected by their level now and their hitpoints are handled better.
+ I should have said this for the last release, but un-hypnotize any pets
+ before upgrading from 2.2.2. Otherwise compatibility is fine. -- neil
+
+05/12/2003
+[B]- Semi-wraiths should not be hurt by climbing over mountains -- neil
+
+08/12/2003
+[B]- Fixed Flame of Udun spell
+
+10/12/2003
+[B]- Diggers are not weapons and should not be displayed as such -- neil
+[O]- Some items that give damage bonuses will be more clear about it -- neil
+[M]- Drain attacks can't drain the one artifact wand, staff, or horn -- neil
+
+[V] T.o.M.E 2.2.5 aka "Death of a Bug"
+
+23/12/2003
+[B]- Prevent recall to Lost Temple before getting the quest -- neil
+
+01/01/2004
+[I]- Pressing Escape gets you out of the pet dismissal list -- neil
+
+03/01/2004
+[B]- Automatizer now accepts TV_TOTEM -- neil
+
+08/01/2004
+[m]- Helpfiles: bearform combat help, music typos, barbarian revisions.
+ -- fearoffours
+[B]- Some vaults incorrectly named -- fearoffours
+
+15/01/2004
+[B]- Wight quest crash fix by 'amaurea' -- neil
+
+16/01/2004
+[B]- God choosen at random was broken -- masmarangio
+
+18/01/2004
+[B]- Typo in q_one.c (or -> of)
+
+20/01/2004
+[B]- Don't use a turn when cancelling a possessor action -- neil
+
+21/01/2004
+[B] - summon_true crashed the game with a summon skill < 1 -- masmarangio
+[B] - test_object_wish: aware status is saved and restored -- masmarangio
+
+27/01/2004
+[m] - Typos in the description of arrows, shots, bolts; punctuation in the
+ mushroom quest -- masmarangio
+
+30/01/2004
+[m]- HOOK_CALC_BONUS_END hooks
+
+06/02/2004
+[m]- Helpfiles: corrected starting equipment of mindcrafters -- masmarangio
+
+07/02/2004
+[m] - Backport of old helpfile updates: no Geomancy for Alchemists and Rune-
+ crafters, updated luck spoiler, corruption spoiler in crpt_aux.lua, a
+ link in skills.txt, an example in automatizer.txt.
+ monsters3.c: changed 'golem' to 'creature', since the Mind Steal Spell
+ also allows to control a monster. -- masmarangio
+
+[m]- Typo (massage -> message) from the forum -- masmarangio
+[m]- Capitalisation in the names of junkarts -- masmarangio
+
+13/02/2004
+[m]- mindcraft_info: Corrected and expanded the info for mindcraft powers
+ -- masmarangio
+[m]- Small corrections in luck spoiler, description of Manwe's Blessing and
+ m_mimic.txt -- masmarangio
+
+19/02/2004
+[B]- God quest will no longer give inaccurate or misleading directions. It
+ also will now give directions from two static features, and an
+ approximate, relative distance from each of those points.
+ -- fearoffours
+
+[V] T.o.M.E 2.2.6 "Won't Get Fooled Again"
+
+27/02/2004
+[m]- Missing space in the description of Vecna -- masmarangio
+
+12/03/2004
+[B]- Eating some corpses produced a division by 0 error -- masmarangio
+
+15/03/2004
+[B]- Wearing an item of life (-100%) reduced the mhp to 0, causing a
+ division by 0 error in cave.c -- masmarangio
+
+16/03/2004
+[B]- Corrected the spell selection for monsters:
+ defines.h: Added RF6_S_ANIMALS in RF6_SUMMON_MASK.
+ melee2.c: Updated comments in monst_spell_monst and make_attack_spell.
+ Removed the spells RF4_MULTIPLY, RF4_S_ANIMAL, RF5_SCARE from spell_attack.
+ Corrected the spell numbers of RF6_TELE_AWAY and RF6_TELE_LEVEL in
+ spell_escape (formerly RF6_DARKNESS and RF6_TRAPS were used).
+ Corrected the spell numbers of RF6_TELE_TO, RF6_DARKNESS, RF6_TRAPS,
+ RF6_FORGET in spell_annoy (formerly RF6_RAISE_DEAD, RF6_S_BUG, RF6_S_RNG
+ were used).
+ Added RF4_S_ANIMAL and RF6_S_ANIMALS and limited the spell number to
+ 160 + 31 in spell_summon. -- masmarangio
+
+18/03/2004
+[O]- The activation of an items with the ACTIVATE_NO_WIELD flag is described
+ without the line "if it is being worn. " -- masmarangio
+[B]- Barad-dur doesn't exist, it is still Mordor. Corrected god quest directions
+ to reflect this. -- fearoffours
+
+20/03/2004
+[m]- Corrected some typos (mostly from the forum, thanks to Carg85)
+ s_yavann.lua: spell 'grow grass' description: 'a grass' -> on grass
+ q_haunted.c: cave -> building
+ a_info.txt: some missing spaces in the description of artifact bolts
+ corrupt.lua, corspoil.txt: 'teeth allows' -> 'teeth allow'
+ k_info.txt: missing space (Golden Horn of the Thunderlords)
+ s_stick.lua: 'a thunderlords' -> 'a thunderlord'
+ d_info.txt: 'the the land of Rhun' -> 'the land of Rhun'
+ -- masmarangio
+
+23/03/2004
+[B]- Fixed the speed of Bearform combat -- masmarangio
+[m]- Updated luck spoiler -- masmarangio
+
+26/03/2004
+[B]- Fix for Flame Imperishable from the forum -- masmarangio
+[B]- You should now receieve a message about failing the god quest at the
+ correct moment, and not when in a random dungeon. -- fearoffours
+[m]- The Old Mage's quest will erase from your quest screen when fully rewarded.
+ -- fearoffours
+
+29/03/2004
+[B]- Further measures taken to ensure god quest relic is created appropriately.
+ -- fearoffours
+[B]- Scumming for god quests (by losing and regaining xp) is now prevented.
+ -- fearoffours
+
+02/04/2004
+[m]- Backport of mostly old helpfile updates: wrong school in m_divin.txt,
+ comments in spells.lua, deleted hint about shoes in r_hobbit.txt,
+ ability.txt, ab_info.txt: updated Ammo-creation and Far-reaching attack,
+ c_axemas.txt: starting weapon is a hatchet,
+ magic.txt, skills.txt: Updated schools improved by Spell-power,
+ corspoil.txt: Updated the info about permanent corruptions,
+ birth.txt: removed corrupted subrace, vampires are a normal subrace,
+ m_air.txt: Updated description of Noxious Cloud -- masmarangio
+
+03/04/2004
+[B]- Fixed device trapkits loaded with rods -- masmarangio
+
+05/04/2004
+[B]- Exploding ammo used in trapkits will explode after hitting a monster.
+ -- masmarangio
+[m]- Corrected Alchemist, Assassin, Axemaster, Rogue helpfiles -- masmarangio
+
+08/04/2004
+[B]- Mage staves were without the WIELD_CAST flag -- masmarangio
+
+09/04/2004
+[G]- No more nuke traps -- neil
+
+21/04/2004
+[m]- skills.txt: Corrected information about Spell-power -- masmarangio
+
+02/05/2004
+[B]- Between quest crash fix, also for Thieves and Trolls quests -- masmarangio
+
+07/05/2004
+[m]- Capitalisation: Metal Boomerang, Lay of Protection.
+ r_pettyd.txt: magic items -> magically enchanted items -- masmarangio
+
+08/05/2004
+[B]- god.lua: The relic could be created in a wrong position -- masmarangio
+
+09/05/2004
+[O]- object1.c: Added descriptions for SENS_FIRE (thanks to Scott Bigham)
+ and IMMOVABLE, removed IM_NETHER from the resistances -- masmarangio
+
+10/05/2004
+[m]- bounty.lua: Added a period -- masmarangio
+[B]- cmd7.c: Preserve the spell stored in random spellbooks --masmarangio
+[O]- Show "to damage" and "to accuracy" messages for yet more confusing items
+ -- neil
+
+11/05/2004
+[B]- xtra1.c: Lucky characters (current luck > 0) don't get death fates;
+ fixed a typo in luckspoi.txt (from the forum) -- masmarangio
+[m]- spells2.c: Typo (vulerable -> vulnerable) -- masmarangio
+
+13/05/2004
+[m]- skills.txt: Fixed broken link -- masmarangio
+[B]- generate.c: Fixed arena levels by refilling the level -- masmarangio
+
+14/05/2004
+[m]- Added new help file for the debug commands -- iain_mac
+
+26/05/2004
+[B]- Correctly cap the spell levels of wands (patch by 'Sumendar') -- neil
+
+[V] T.o.M.E 2.2.7 aka "Stoke Me A Clipper"
+
+04/06/2004
+[P]- Class no longer influences the internal Angband 'skills' of Disarming,
+ Magic Devices, Saving Throw, Stealth, Searching, Perception, Hand-to-Hand
+ combat, Missile Combat, and Throwing. ToME skills instead have the same
+ effect for all classes. -- neil
+
+05/06/2004
+[G]- Added the new Mimic shapes and updated the old ones -- masmarangio
+
+07/06/2004
+[G]- Destroying items manually now takes no time -- neil
+
+10/06/2004
+[B]- dungeon.c: Light should consume fuel at a rate of 1 / turn -- masmarangio
+
+11/06/2004
+[P]- Water Bite no longer has a damage cap -- neil
+
+12/06/2004
+[B]- A store (e.g. the mathom house) can contain up to 255 items (in defines.h
+ STORE_INVEN_MAX limited the number of items to 24) (Note: the limit is
+ stored in a byte in loadsaves.c) -- masmarangio
+
+21/06/2004
+[m]- a_info.txt: Updated names of artifacts in the comments -- masmarangio
+
+23/06/2004
+[m]- object.pkg: Added psychometry() for easier mindcraft testing.
+
+24/06/2004
+[m]- birth.txt, index.txt : Corrected and added abbreviations
+ gen_idx.lua: removed non-existent file and sorted file list
+[G]- Added the spell Sterilize and Staves of Sterilization from ToME 3.0.0.
+[D]- Added the first new special level from ToME 3.0.0, Galleon in Helcaraxe
+ -- masmarangio
+
+02/07/2004
+[D]- Added the special level Factory in the Illusory Castle -- masmarangio
+
+04/07/2004
+[G]- Added the spell Inertia Control from ToME 3.0.0 -- masmarangio
+
+05/072004
+[m]- Updated luckspoiler -- masmarangio
+
+09/07/2004
+[m]- Typo in s_fire.lua, from the wiki -- masmarangio
+[m]- rm_skeleton.txt, rm_zombie.txt: They cannot restore life force, and zombies
+ are not resistant to nether -- masmarangio
+
+19/07/2004
+[P]- Except for infravision, all innate class or racial effects on skills are
+ gone. All skills have equal effect for all classes, and races now give
+ starting skill bonuses. -- Neil
+
+22/07/2004
+[B]- s_meta.lua: Inertia controlled spells are not casted in wilderness mode.
+ s_mana.lua: Inertia level of Disruption Shield is 9 (needed spell level 45)
+ m_meta.txt: Added a list of controllable spells -- masmarangio
+
+24/07/2004
+[P]- Mages are more geared toward a mix of Magic and Combat, while Sorcerors
+ have more options than pure Sorcery -- Neil
+
+26/07/2004
+[B]- p_info.txt: Archers and Rangers gain the missing Spirituality skill
+ p_info.txt: Removed the old Mimic Cloak (new cloak in player.lua)
+[m]- Helpfile updates for all character classes. -- masmarangio
+
+30/07/2004
+[B]- p_info.txt: Thunderlords start with Stealth -16.000 (from the wiki)
+[B]- cmd7.c: Fixed Alchemy recharging bug (thanks to Scott)
+[B]- al_info.txt: Removed the old Mimic Potions -- masmarangio
+[m]- util.pkg: Added lite_spot() and note_spot() for modules -- masmarangio
+
+31/07/2004
+[B]- monspeak.txt: Added some lines for Groo to fix a bug -- masmarangio
+[B]- files.c: Corrected display of Climb flag, immunity to Nether, negative
+ pvals < -9. Added Sentient, Clone, Spider ESP flags. -- masmarangio
+
+02/08/2004
+[m]- g_melkor.txt: Added fire resistance for worshippers of Melkor
+[B]- files.c: Added flags from the gods and spell schools to the character
+ screen. Added also flags from wielded symbiotes. -- masmarangio
+
+04/08/2004
+[m]- p_info.txt: Removed the useless skill Prayer for Maiar -- masmarangio
+[B]- cmd4.c: Quest list without random quests in DL > 98 -- masmarangio
+
+19/08/2004
+[m]- help file documentation restructuring, copying appropriate rewrites from
+ wiki. -- fearoffours
+
+20/08/2004
+[B]- randart.c: An item with pval > 0 (e.g. an Elven Cloak) can gain parts
+ with a max_pval = 0 (e.g. resistances and immunities) -- masmarangio
+
+24/08/2004
+[m]- Various minor changes to helpfiles, reflecting current changes to
+ documentaiton on the wiki. --fearoffours
+
+28/08/2004
+[O]- Removed portable holes as have been useless for as long as merchants have
+ been removed from game. --fearoffours
+
+30/08/2004
+[m]- k_info.txt: Fixed name of the commented out portable holes -- masmarangio
+
+14/09/2004
+[m]- Fixed some typographical errors, mostly from the wiki:
+ cmd1.c: [The monster] fall -> falls, deleted space (Bug # 80 from the wiki)
+ k_info.txt: Added & for Climbing sets (Bug # 81 from the wiki)
+ q_one.c: You felt -> You fell (Bug # 94 from the wiki)
+ monster2.c: It tries to breed but he fails: he -> it (Bug # 98 from the wiki)
+ bldg.c: Changed wording of the soothsayer (Bug # 106 from the wiki)
+ tables.c: Minor changes in the One Ring quest (Bug # 117 from the wiki)
+ q_invas.c: jumps out of the between -> appears, deleted spaces,
+ added single quotes in direct speech (Bug # 119 from the wiki)
+ q_between.c: Deleted space, changed comments -- masmarangio
+
+20/09/2004
+[m]- book-4.txt: Capitalised 'Ring' (Bug # 135 from the wiki) -- masmarangio
+[B]- ow_info.txt: missing C: lines reduced the purse to 0 -- masmarangio
+[B]- object1.c: don't wield bolts with instruments and pebbles with boomerangs
+ (Bug # 127 from the wiki) -- masmarangio
+
+21/09/2004
+[B]- object1.c: mention_use and describe_use list all available slots, check all
+ weapon weights and distinguish between instruments and bows (Bug # 87)
+ object1.c: Res Chaos implies Res Confusion (for the character screen)
+ xtra1.c: Magical breath implies Water breath (from the wiki) -- masmarangio
+
+23/09/2004
+[B]- Z and Cth monster options removed, as in ToME 3. This fixes, among other
+ things, the Death Orb issues. -- neil
+
+24/09/2004
+[m]- options.txt: Also removed the options from the help file -- masmarangio
+[B]- a_info.txt: Corrected two typos (Bugs # 140, 146 from the wiki)
+ k_info.txt: Changed description of Bastard Sword, added RES_CHAOS to the
+ known flags of a Blade of Chaos (it's mentioned in the description)
+ files.c: Terminated highscore strings with \0, changed total_points
+ slightly to prevent an overflow error (Bug # 139 from the wiki)
+ v_info.txt: Corrected the x size of vault 99 and 104 -- masmarangio
+
+28/09/2004
+[D]- dungeon.c: Level of the Death dungeon is the minimum level from d_info.txt
+[B]- dungeon.c: Set dungeon_type to wilderness when recalling out. This should
+ fix the various Moria recalling bugs (Bug # 95)
+[m]- cmd6.c: replaced the recall activation code by recall_player -- masmarangio
+[m]- monster1.c: missing spaces in description (Bug # 169) -- masmarangio
+
+
+29/09/2004
+[m]- Modules need to define three new variables to control the chance or
+ random artifact generation. random_artifact_weapon_chance,
+ random_artifact_armor_chance, random_artifact_jewelry_chance control
+ the chance for different types of items. -- neil
+
+30/09/2004
+[B]- spells2.c: Redraw trap status after passwall (Bug # 51)
+ store.c: Removed '))' when displaying a large store -- masmarangio
+
+01/10/2004
+[m]- mods_aux.lua: Added default values for random artifact generation;
+ updated the skill values -- masmarangio
+
+02/10/2004
+[B]- al_info.txt: removed recipe for Scroll of Spell (Bug # 179), added recipe
+ for Staff of Sterilisation (Bug # 77) -- masmarangio
+[m]- cmd6.c: protect evil -> protection from evil, s_stick.lua: town -> surface
+ q_betwen.c, q_invas.c: speak -> speaks (from the forum) -- masmarangio
+
+03/10/2004
+[m]- tr_info.txt: Spelling of Lite (Bug # 182), Armor, Paralyzing -- masmarangio
+[B]- cmd6.c: Added timeout for junkarts in the activation description (ugly fix)
+ tables.c: Replaced ACT_CURE_POISON by not used ACT_CURE_POIS -- masmarangio
+[B]- Once a god quest is failed, you will not receive any more god quests.
+ -- fearoffours
+[m]- The (Ctrl-Q) Quest screen now shows which number god quest you have been given
+ and an additional line in your character dump shows how many have been
+ successfully completed. -- fearoffours
+[m]- Help updates from the wiki - lots of it Maylith's work, esp FAQ updates.
+ -- fearoffours
+
+04/10/2004
+[m]- Corrected the description of the Disarm, Call the Elements and Channel
+ Elements spells (without changing the code) (Bug # 175) -- masmarangio
+
+08/10/2004
+[B]- bldg.c: Research item (Bug # 191) and research monster are now paid
+ correctly -- masmarangio
+[B]- spells2.c: Diggers cannot be enchanted with scrolls -- masmarangio
+[m]- tome-faq.txt, index.txt: Typo (Bug # 196) -- masmarangio
+
+10/10/2004
+[m]- s_info.txt: Antimagic: generates -> generate (Bug # 198) -- masmarangio
+[B]- files.c: Fixed displayed barehanded damage (Patch from Scott, Bug # 195)
+ -- masmarangio
+
+11/10/2004
+[m]- powers.c: replaced the recall power code by recall_player
+ q_troll.c: Fixed typos from the wiki (Bug # 208) -- masmarangio
+
+12/10/2004
+[m]- tables.c: Removed harpers and some other small changes (Bug # 212)
+ cmd6.c: Added "and" in the description of ACT_ROHAN (Bug # 213)
+ -- masmarangio
+
+13/10/2004
+[m]- m_demono.txt, s_demon.lua: armor -> armour class (Bug # 217)
+[O]- k_info.txt: Changed comments and descriptions of the items, mostly from
+ the wiki (Bug # 176) and added missing descriptions (IdeaArchive)
+ Added article (&) in the name of armours (Bug # 81),
+ The spelling of some item names was changed: Scroll of Enchant Armour,
+ *Enchant Armour*, Curse Armour, Summon Monsters, Basilard
+ Added COULD2H to the Claymore and MUST2H to the Espadon.
+[m]- dun3.18: description of DimGates: fills -> fill (Bug # 223)
+ -- masmarangio
+
+15/10/2004
+[B]- files.c: Remove / restore CAVE_VIEW before / after saving the game.
+ This solves a long standing bug with the lighting of the dungeon
+ since the temporary arrays that hold the position of the viewed
+ grids are not stored in the save file (Bug # 19). -- masmarangio
+
+16/10/2004
+[m]- init1.c: The parser adds missing spaces at the end of the
+ description of artifacts, like it did for objects. -- masmarangio
+[m]- Race, class and race modifier help files updates to reflect changes
+ in skill bonuses. -- fearoffours
+
+18/10/2004
+ Some changes to random artifact and scrolls of artifact creation
+ (See Bugs # 206, 222, 226 on the wiki):
+[m]- externs.h: Moved some functions listed under spells2.c to proper sections
+[m]- k_info.txt: Added "mundane" to the description of the scroll
+[B]- cmd6.c: the selection of artifactable items can be escaped now
+ randart.c: *ID* the object before listing the powers, some re-ordering
+[B]- spells2.c: Re-add diggers to item_tester_artifactable, and limit the
+ selection to normal items due to complains (no ego items or artifacts)
+[O]- ra_info.txt: Added a STR-increasing part without combat bonuses for diggers
+[B]- ra_info.txt: Fixed two W-lines with 4 entries and added a missing C-line
+[O]- e_info.txt: Diggers cannot be of Earthquakes anymore (there are combat boni
+ involved) - perhaps an own ego type should be added... -- masmarangio
+[m]- q_ultrag.c: Quest texts changed as reported in Bug # 210 -- masmarangio
+
+23/10/2004
+[B]- k_info.txt: Reduced throwing damage of totems to 1 -- masmarangio
+
+25/10/2004
+[P]- Priests disarm as well as Warriors do now -- neil
+[B]- st_info.txt: Fixed the changed item names in the stores (StatusReport3)
+[m]- spells.lua: Sorted the Conveyance spells by level (Bug # 233)
+ -- masmarangio
+[m]- Helpfiles reflect changes to skills (priest disarming and racial
+ spirituality update). -- fearoffours
+
+01/11/2004
+[m]- library.lua: Added OBJ_FOUND_REWARD to the tome (Bug # 237) -- masmarangio
+
+13/11/2004
+[B]- Fix for disappearing artifacts (especially guardian artifacts) during load / save
+ thanks to SimonSorc
+
+17/11/2004
+[O]- No more blessed boomerangs -- neil
+
+[V] T.o.M.E 2.3.0 aka "One more try to get Mages working"
+
+10/12/2004
+[B]- Fix loading and saving of skills, I hope. Unfortunately this breaks save
+ compatiability, though. The saves must be deleted again. -- Neil
+
+29/12/2004
+[B]- Fix negative skills -- Neil
+[B]- Don't use weaponmastery combat when weaponmastery skill is negative -- Neil
+
+[V] T.o.M.E 2.3.1 aka "2.3.0.1"
+
+2005/05/19
+[I]- If easy_disarm is off, don't trigger known traps while walking normally.
+ Added a new extended command "blunder" to let players trigger traps on
+ purpose. -- gwooledge
+[I]- Lots of documentation, spelling and grammar fixes, including:
+ * the now-outdated race/class ability tables, replaced with skill tables
+ * the missing documentation for the set of extended commands
+ * far too many others to mention here
+ -- gwooledge
+
+2005/05/20
+[I]- Added sanity and speed to the character screen (and hence the text dump).
+ Consolidated HP and SP into one line to make room. -- gwooledge
+
+2005/05/21
+[B]- Don't allow trap doors on quest levels or on chests. -- gwooledge
+[I]- Allow shopping to use the correct keys in roguelike mode. -- gwooledge
+
+2005/05/22
+[B]- Update view after high-powered globe of light. -- gwooledge
+
+2005/05/26
+[I]- Push a certain potion type a little deeper into the dungeon -- Neil
+[I]- Make piety display light blue when praying, to make it easier to tell
+ when you're praying. -- gwooledge
+
+2005/06/02
+[I]- Don't display ordinary resists when there's also an immunity to the same
+ element, in an object description. -- gwooledge
+[B]- Don't allow use of stairs (any < or > movement command) while rooted
+ to the floor (by the Yavanna spell). -- gwooledge
+
+2005/06/04
+[I]- Display the (colored) character for uniques in the Known Uniques list (~2).
+ -- gwooledge
+
+2005/06/05
+[I]- Add "Check abilities" extended command/macro. This gives roguelike keyset
+ players a way to access the ability screen other than "\N", although it's
+ still one more keystroke than "\N" is.... -- gwooledge
+
+2005/06/11
+[B]- Try again to keep traps from wrecking a certain plot element -- Neil
+
+2005/06/18
+[B]- Fix module file handling for multi-user installs. Now character sheets,
+ automatizer file, and the rest will be read and written in
+ ~/.tome/2.3/modulename as they should. -- Neil
+
+2005/06/19
+[B]- Try harder to save persistent levels when recalling out -- Neil
+[B]- Fix all sub-racial skill bonuses, along with Maia racial skill bonuses
+ -- Neil
+[O]- Prevent random artifact bolts from giving extra blows -- Neil
+
+2005/06/21
+[O]- Correct the types of certain artifact trap sets to match their weights
+ and descriptions. -- gwooledge
+
+2005/07/13
+[I]- Clean up some offensive messages, patch courtesy of 'The Fury' -- Neil
+
+2005/07/14
+[I]- Include the resistances grid on character sheets dumped on death.
+ This makes them consistent with the ones generated before death, and
+ is more informative and useful for post mortem analyses. -- gwooledge
+
+2005/07/15
+[P]- Warriors no longer get a secret special three bonus blows spread over the
+ 50 character levels. The three blows are now tied to Weaponmastery.
+
+ Module authors should adjust accordingly, or their warriors may get three
+ blows they didn't have before. -- Neil
+
+2005/07/16
+[O]- Potions of Cure Insanity were too cheap. -- gwooledge
+
+2005/07/24
+[I]- Examining a totem will recall the monster it summons -- Neil
+[I]- Examining a corpse will recall the monster it was -- Neil
+
+2005/07/27
+[V] T.o.M.E 2.3.2 aka "Unrealized Reality"
+
+28/07/2005
+[G]- Lost sword quest rewards always give a minimum skill modifier of 0.3.
+ -- gwooledge
+
+29/07/2005
+[D]- Edit one vault to open up some inaccessible rooms -- Neil
+
+11/08/2005
+[B]- Alchemy: disallow repowering double-ego items, unless the character has
+ the artifact creation ability. Based on patch by Andrey Egoshin.
+ -- gwooledge
+[B]- Lost sword quest skill reward probabilities were computed incorrectly.
+ Fix suggested by Dan Rosenberry. -- gwooledge
+[I]- Miscellaneous documentation, spelling and grammar fixes. -- gwooledge
+
+12/08/2005
+[B]- Don't let a player trick the Valar by getting drained and re-gaining
+ levels -- Neil
+[I]- Update AC display after fixing armor in the buildings. -- gwooledge
+
+16/08/2005
+[B]- Don't allow Runecraft and Thaumaturgy spells to go explode inside walls
+ and seep through -- Neil
+
+17/08/2005
+[I]- Fix damage display for Thaumaturgy ball spells. -- gwooledge
+[O]- Removed pointless slays, brands, and bonuses on Pick of Erebor -- Neil
+
+19/08/2005
+[B]- When consuming magic essences, don't stop prematurely. Based on patch
+ by Andrey Egoshin. -- gwooledge
+
+30/08/2005
+[B]- Upkeep cost for partial summons was not always charged. -- gwooledge
+
+05/09/2005
+[B]- Some staves were being generated with the wrong tval, causing several bugs
+ including (but not limited to) staves being unrechargeable. -- gwooledge
+
+11/09/2005
+[B]- Saving throw was not calculated correctly. -- gwooledge
+
+14/09/2005
+[P]- All new partial summon upkeep formula -- neil
+
+26/09/2005
+[B]- Disallow negative experience alchemy abuses. Based on patch by Andrey
+ Egoshin. -- gwooledge
+[O]- When examining books, demonology equipment and instruments in stores, show
+ both the object's powers and its spells. -- gwooledge
+[B]- Nonliving and undead pets won't be angered by lack of breathable air.
+ -- gwooledge
+[D]- A certain early trap should be less deadly (and appear a bit later).
+ -- gwooledge
+[I]- Honor exp_need option when displaying object experience. -- gwooledge
+
+27/09/2005
+[I]- Restored and updated some missing help files. -- gwooledge
+[G]- (Mass) Genocide damage is applied all at once to avoid bug #228.
+ -- gwooledge
+
+28/09/2005
+[B]- Don't use the "POSIX" setuid calls on Mac OS X, as they apparently break
+ compilation -- neil
+
+29/09/2005
+[O]- Junk should stack just like skeletons. Patch by StarweaverBlue.
+ -- gwooledge
+[M]- Kavlax should be many-headed. -- gwooledge
+
+14/10/2005
+[B]- Certain monster spells were hard-coded for the wrong number of equipment
+ slots. -- gwooledge
+
+18/10/2005
+[B]- Incorrect operator used in cave generation code. Effect unknown, but it
+ *might* possibly fix some of the Orc cave crashes.-- gwooledge
+
+26/10/2005
+[M]- Regular (non-Joke, non-Cth, non-Z) monsters should not breathe nuke,
+ because it has a side effect we don't want in ToME -- Neil
+
+29/10/2005
+[O]- Mac OS X builds now put all the game data into the bundle, storing all
+ user data in the user's Library (some preferences in
+ Library/Preferences/net.t-o-m-e.tome.plist, the rest in
+ Library/Application Support/ToME. -- Neil
+
+16/11/2005
+[I]- Handling of Command key modified in Mac OS X UI. It should be accessible
+ in macros now if it wasn't before -- Neil
+
+26/11/2005
+[B]- Don't allow uniques or quest monsters to just disappear to the move of
+ another monster -- Neil
+
+14/12/2005 - ToME 2.3.3 "Realized Unreality"
+
+15/10/2006
+[B]- Remove buggy trap of Stair Movement -- Neil
+
+12/12/2005
+[B]- Fix typo in one monster's flags - Iain
+
+31/1/2005
+[I]- Fix window position saving on Mac OS, patch by John Love-Jensen
+ -- Neil
+
+19/2/2005
+[B]- Fix word wrapping in character sheet, patch from "ZizzoTheInfinite"
+ -- Neil
+
+[V] T.o.M.E 2.3.4 aka "An Unexpected Party"
diff --git a/changes.txt b/changes.txt
new file mode 100644
index 00000000..1f08933a
--- /dev/null
+++ b/changes.txt
@@ -0,0 +1,82 @@
+T.o.M.E 2.3.5 aka "Into the unknown" changes
+
+Interface changes:
+- The X11 and Xaw interfaces now save the dungeon and player when the
+ window is closed.
+- Fixed cpu churning bug that occurs when using certain window managers and
+ ToME is maximized.
+
+Gameplay changes:
+- Player speed now set correctly when Demon Hide corruption is enabled.
+- ToME now correctly sets various Balrog flags when player in Balrog form.
+- ToME now correctly sets the teleport flags when teleport corruption is
+ enabled.
+- ToME now uses the qrand7.map file when generating princess quests.
+- Bigs changes for generate.c to get it to produce the princess and thrain
+ rooms. Also code clean up of room geranation code.
+
+Object changes:
+- Slings of Buckland can now be generated.
+- Wiki Bug 510. Added the WIELD_CAST flag to all artifact instruments to
+ fix problems when casting spells.
+
+Misc changes:
+- Fixed small typo in the commands help file.
+- Added help for the Mathom House.
+- Fixed various compile time warnings in various files.
+- Added makefile support for main-gtk2.c in makefile.std
+- Values found in documentation for spectral race modifiers now match values
+ found in p_info.txt.
+- Wiki Bug 837. Removed references to old inscriptions handling code in
+ documentation.
+- Wiki Bug 564. Do not use the word 'restrict' as a variable name anymore.
+ It conflicts with keywords used by the Sun Studio Compiler.
+- Wiki Bug 517. Fixed incorrect descriptoin of artifacts in help files.
+- Changed description of Disarm spell to more accurately reflect what it
+ does.
+- ToME now correctly compiles main-gtk2.c on 64-bit machines.
+
+Bug fixes:
+- Wiki Bugs 841, 405, 360. Changes to get ToME to correctly build 64-bit
+ executables.
+- Applied killerbunnies patch to identify objects on grid before squelching.
+- Applied killer bunnies patch to keep the fate "you are fated to find
+ something special" from creating something special with an inappropriate
+ base object.
+- Applied killerbunnies patch stops symbiotes from gaining levels simply
+ by being hypnotized and released.
+- ToME now saves tim_fly, tim_poison, tim_regen and tim_regen_power.
+- Stores now display the inventory correctly after a purchase.
+- The race the legends display now works correctly with more than 10 dead
+ characters in history.
+- Characters are no longer generated with 0 mana points.
+- Wiki Bug 839. ToME no longer penalizes an object when it is not actually
+ cursed.
+- Wiki Bug 838. The melee style will now switch correctly from Bear to the
+ primary melee style when switching out of Bear form.
+- Wiki Bug 826. The inventory and equipment windows now update when the
+ player identifies the entire pack or uses the *Greater Identify* spell.
+- Wiki Bug 819. No more bogus level leaving messages.
+- Wiki Bug 722. ToME no longer crashes purple staircases have been trapped.
+- Wiki Bug 624. Ensure savefiles go to save and not scpt when using modules.
+- Wiki Bug 537. Partial fix of infinite loop during stair allocation on
+ small levels.
+- Wiki Bug 530. ToME no longer drops items inappropriately when changing
+ melee styles.
+- Wiki Bug 528. Character dumps now show the correct number of princess and
+ lost sword quests.
+- Wiki Bug 526. ToME no longer enters an infinite loop when fighting in
+ bare-hand combat sylte and bare-hand skill is < 1.
+- Wiki Bug 523. All types of recall check if the user really wants to leave
+ a unique level.
+- Wiki Bug 506. ToME no longer crashes attempting to drop non-existant
+ artifacts.
+- Wiki Bug 419. Use SKILL_BOULDER instead of SKILL_ARCHERY when throwing
+ a boulder.
+- Wiki Bug 411. Black breath no longer gets 3 chances to happen.
+- Wiki Bug 394 and 393. Inertia Control autocasting can no longer cast a
+ spell when antimagic field > 0 or when wielding a dark sword.
+- Wiki Bug 334. Companions are no longer saved in dungone save files.
+- Work around Mac OS 10.4.11 getlogin() bug - Neil
+- Wiki Bug 397. ToME no longer crashes on XP and Vista systems when viewing
+ quests or other info from the knowledge menu.
diff --git a/credits.txt b/credits.txt
new file mode 100644
index 00000000..96e8ef60
--- /dev/null
+++ b/credits.txt
@@ -0,0 +1,67 @@
+Credit list(lots are still not present here, if you think you should
+drop me a line please) in no particular order:
+
+- Chris Weisiger <chrisweisiger@hotmail.com>, lots and lots of stuff,
+ and mainly lots of vaults, a whole magic realm, some special levels,
+ TANG
+- Ceilti <Ceilti@aol.com>, idea of weapons gaining xp, comments, ideas,
+ lots of PernAngband related posts on rgra
+- Static Chaos <schaos@freemail.hu>, huge numbers of patches, ideas, ...
+- Improv <qc@apk.net>, spelling/grammar fixes, ideas, safer_panics patch,
+ some new monster and artifact descriptions, cmovie stuff, and lots
+ of things
+- Mynstral, new towns and more
+- Kusunose Toru <kusunose@hcn.zaq.ne.jp> for LOTS of bugfixes
+- Akhronath for the ideas of the magic system, new realms and much more
+ oh yeah the Nazguls :)
+- Jerome Wojcik <Jerome.Wojcik@lmcp.jussieu.fr> for monster susceptibilities
+- Andreas Koch(akoch@rbg.informatik.tu-darmstadt.de) for the tiles, the
+ graphic editor and surely more :)
+- Gob for the mac ports and a nasty bug fix
+- Iain McFall for bug fixes, auto squelch and more
+- Bablos for the amiga port
+- Arch for the windows port
+- Dawnmist for the new docs
+- Jonathan Ellis for the rework of the info files
+- Hansjoerg Malthaner for the Isometric engine
+- Pelpel for bugfixes and other mac things
+- Tom Le for the website hosting
+- Willam Tanksley for lots of useful comments, ideas and bitching ;)
+- Tom Demuyt for the unbeliever idea
+- Luc French for lots of ideas & code
+- Kieron for some patches
+- Runescrye for ra_info.txt and surely more :)
+- Fearof4s for lua patches, lua tutorial, quests and stuff.
+- Pav Lucistnik for some patches and hosting the CVS
+- Mef for the huge task of updating class and magic help documents for 2.x
+ and lots and lots of other help updates!
+- Erik J Brown for some changes to help docs.
+- vrak (Per-Arne Holtmon Akoe) for magic.txt
+- Kat B for other help updates.
+- Chris Hadgis for Luck spoiler, lua *.pkg help docs, and other help docs.
+- lemming for skills.txt and stuff
+- Paladin Rithe (Brian Ronk) for birth.txt
+- John Gilmore for alchemy improvements patch
+- Massimiliano Marangio for lots and lots of help updates, typo corrections,
+ bug fixes etc.
+- Neil Stevens <neil@hakubi.us> for making stable releases
+- Jeff Epler for automatizer improvements, and certainly more in the future ;)
+- Scott Bigham for some cool patches
+- Magua(magua@speakeasy.net) who wrote the java applet to play cmovies online
+- Maylith for lots of help with the documentation, especially on the wiki
+- Furiosity for some missing item descriptions
+- SimonSorc for bugfixes
+- Greg Wooledge <greg@wooledge.org> for still more bug fixes
+- Phillip Neiswanger for tons of bug fixes and mainly 2.3.5 and possibly 2.4.0
+
+- All that I forgot in the list, hit me a bit then email me to get added
+
+- All the friendly people of the ToME mailing list
+- All the friendly people of #angband
+
+- TeCGraf for the Lua scripting language(www.lua.org)
+- Reuben Thomas for the lua bitlib
+- Waldemar Celes for tolua(automatic wrapper generator for lua, like swig, but
+ this one actually works :)
+
+- J.R.R. Tolkien for the best fantasy ever !
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 00000000..2de989fd
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,2 @@
+*.raw
+
diff --git a/lib/apex/delete.me b/lib/apex/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/apex/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/bone/bone001.012 b/lib/bone/bone001.012
new file mode 100644
index 00000000..ee81bc54
--- /dev/null
+++ b/lib/bone/bone001.012
@@ -0,0 +1,4 @@
+Truigar
+162
+7
+13
diff --git a/lib/bone/bone004.001 b/lib/bone/bone004.001
new file mode 100644
index 00000000..1c6921d6
--- /dev/null
+++ b/lib/bone/bone004.001
@@ -0,0 +1,4 @@
+Olorin
+13
+21
+10
diff --git a/lib/bone/bone004.002 b/lib/bone/bone004.002
new file mode 100644
index 00000000..0669019f
--- /dev/null
+++ b/lib/bone/bone004.002
@@ -0,0 +1,4 @@
+Pruirk
+108
+12
+24
diff --git a/lib/bone/bone004.003 b/lib/bone/bone004.003
new file mode 100644
index 00000000..93350245
--- /dev/null
+++ b/lib/bone/bone004.003
@@ -0,0 +1,4 @@
+Bearn
+166
+11
+24
diff --git a/lib/bone/bone004.004 b/lib/bone/bone004.004
new file mode 100644
index 00000000..cbd93dd5
--- /dev/null
+++ b/lib/bone/bone004.004
@@ -0,0 +1,4 @@
+Druldo
+64
+19
+0
diff --git a/lib/bone/bone004.005 b/lib/bone/bone004.005
new file mode 100644
index 00000000..6231f5bc
--- /dev/null
+++ b/lib/bone/bone004.005
@@ -0,0 +1,4 @@
+Sook
+56
+0
+23
diff --git a/lib/bone/bone004.006 b/lib/bone/bone004.006
new file mode 100644
index 00000000..ae40af60
--- /dev/null
+++ b/lib/bone/bone004.006
@@ -0,0 +1,4 @@
+Sook
+55
+0
+23
diff --git a/lib/bone/bone004.007 b/lib/bone/bone004.007
new file mode 100644
index 00000000..357c390b
--- /dev/null
+++ b/lib/bone/bone004.007
@@ -0,0 +1,4 @@
+Sook
+45
+0
+23
diff --git a/lib/bone/bone004.008 b/lib/bone/bone004.008
new file mode 100644
index 00000000..1f61a166
--- /dev/null
+++ b/lib/bone/bone004.008
@@ -0,0 +1,4 @@
+Sook
+44
+0
+23
diff --git a/lib/bone/bone004.009 b/lib/bone/bone004.009
new file mode 100644
index 00000000..f0b57b40
--- /dev/null
+++ b/lib/bone/bone004.009
@@ -0,0 +1,4 @@
+Sook
+46
+0
+23
diff --git a/lib/bone/bone004.010 b/lib/bone/bone004.010
new file mode 100644
index 00000000..1e215878
--- /dev/null
+++ b/lib/bone/bone004.010
@@ -0,0 +1,4 @@
+Luthien
+114
+20
+22
diff --git a/lib/cmov/delete.me b/lib/cmov/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/cmov/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/core/auto.lua b/lib/core/auto.lua
new file mode 100644
index 00000000..fa2457ff
--- /dev/null
+++ b/lib/core/auto.lua
@@ -0,0 +1,803 @@
+-- This file is the core of the Automatizer
+-- Please do not touch unless you know what you are doing
+
+__rules = {}
+__rules_max = 0
+
+rule_aux = {}
+
+-- Rule apply function, does .. nothing
+function auto_nothing(obj, item)
+ return
+end
+
+function auto_inscribe(obj, item, note)
+ if obj.note ~= 0 then return end
+ msg_print("<Auto-Inscribe {"..note.."}>")
+ obj.note = quark_add(note)
+ return TRUE
+end
+
+-- Rule apply function, pickup object
+function auto_pickup(obj, item)
+ if item >= 0 then return end
+ if inven_carry_okay(obj) == FALSE then return end
+ msg_print("<Auto-pickup>")
+ object_pickup(-item)
+ return TRUE
+end
+
+-- Rule apply function, destroy item
+function auto_destroy(obj, item)
+ -- be carefull to what we can destroy
+ -- Unaware things won't be destroyed.
+ if is_aware(obj) == FALSE then return end
+
+ -- Inscribed things won't be destroyed!
+ if obj.note ~= 0 then return end
+
+ -- Keep Artifacts -- they cannot be destroyed anyway
+ if is_artifact(obj) == TRUE then return end
+
+ -- Cannot destroy CURSE_NO_DROP objects
+ local f1, f2, f3, f4, f5, esp = object_flags(obj);
+ if band(f4, TR4_CURSE_NO_DROP) ~= 0 and band(obj.ident, IDENT_CURSED) then return end
+
+ msg_print("<Auto-destroy>")
+
+ -- Eliminate the item (from the pack)
+ if item >= 0 then
+ inven_item_increase(item, -obj.number)
+ inven_item_describe(item)
+ inven_item_optimize(item)
+ -- Eliminate the item (from the floor)
+ else
+ floor_item_increase(0 - item, -obj.number)
+ floor_item_describe(0 - item)
+ floor_item_optimize(0 - item)
+ end
+ return TRUE
+end
+
+-- Report the status of an object
+function object_status(obj)
+ local sense =
+ {
+ [SENSE_CURSED] = "bad",
+ [SENSE_WORTHLESS] = "very bad",
+ [SENSE_AVERAGE] = "average",
+ [SENSE_GOOD_LIGHT] = "good",
+ [SENSE_GOOD_HEAVY] = "good",
+ [SENSE_EXCELLENT] = "very good",
+ [SENSE_SPECIAL] = "special",
+ [SENSE_TERRIBLE] = "terrible",
+ }
+
+ if is_known(obj) == FALSE then
+ if sense[obj.sense] then
+ return sense[obj.sense]
+ else
+ return ""
+ end
+ else
+if nil then -- test
+ local osense = -1
+ local type = select_sense(obj, TRUE, TRUE)
+ if type == 1 then
+ osense = value_check_aux1(obj)
+ elseif type == 2 then
+ osense = value_check_aux1_magic(obj)
+ end
+print("type : "..type)
+ if sense[osense] then
+ print("sense: "..sense[osense])
+ return sense[osense]
+ else
+ print("sense: ")
+ return ""
+ end
+
+else -- the real one
+
+ local slot = wield_slot_ideal(obj, TRUE)
+
+ -- Arts items
+ if is_artifact(obj) == TRUE then
+ if band(obj.ident, IDENT_CURSED) == 0 then return "special"
+ else return "terrible" end
+ -- Ego items
+ elseif (obj.name2 > 0 or obj.name2b > 0) then
+ if band(obj.ident, IDENT_CURSED) == 0 then return "very good"
+ else return "very bad" end
+ -- weapon
+ elseif (slot == INVEN_WIELD) or (slot == INVEN_BOW) or (slot == INVEN_AMMO) or (slot == INVEN_TOOL) then
+ if obj.to_h + obj.to_d < 0 then
+ return "bad"
+ elseif obj.to_h + obj.to_d > 0 then
+ return "good"
+ else
+ return "average"
+ end
+ -- armor
+ elseif (slot >= INVEN_BODY) and (slot <= INVEN_FEET) then
+ if obj.to_a < 0 then
+ return "bad"
+ elseif obj.to_a > 0 then
+ return "good"
+ else
+ return "average"
+ end
+ -- ring
+ elseif slot == INVEN_RING then
+ if (obj.to_d + obj.to_h < 0) or (obj.to_a < 0) or (obj.pval < 0) then
+ return "bad"
+ else
+ return "average"
+ end
+ -- amulet
+ elseif slot == INVEN_NECK then
+ if (obj.pval < 0) then
+ return "bad"
+ else
+ return "average"
+ end
+ -- chests
+ elseif obj.tval == TV_CHEST then
+ if obj.pval == 0 then
+ return "empty"
+ elseif obj.pval < 0 then
+ return "disarmed"
+ else
+ return "average"
+ end
+ else
+ return "average"
+ end
+end
+ end
+end
+
+-- Recursive function to generate a rule function tree
+function gen_rule_fct(r)
+ -- It is a test rule (or, and, ...)
+ if r.label == "and" or r.label == "or" then
+ local i
+ local fct_tbl = {}
+ for i = 1, getn(r) do
+ if r[i].label ~= "comment" then
+ tinsert(fct_tbl, gen_rule_fct(r[i]))
+ end
+ end
+ if r.label == "and" then
+ return function(object)
+ local fcts = %fct_tbl
+ local i
+ for i = 1, getn(fcts) do
+ if not fcts[i](object) then return end
+ end
+ return TRUE
+ end
+ elseif r.label == "or" then
+ return function(object)
+ local fcts = %fct_tbl
+ local i
+ for i = 1, getn(fcts) do
+ if fcts[i](object) then return TRUE end
+ end
+ end
+ end
+ -- It is a condition rule (name, type, level, ...)
+ else
+ if r.label == "not" then
+ local f
+ if not r[1] then
+ f = function (object) return TRUE end
+ else
+ f = gen_rule_fct(r[1])
+ end
+ return function(object) return not %f(object) end
+ elseif r.label == "name" then
+ return function(object) if strlower(object_desc(object, -1, 0)) == strlower(%r[1]) then return TRUE end end
+ elseif r.label == "contain" then
+ return function(object) if strfind(strlower(object_desc(object, -1, 0)), strlower(%r[1])) then return TRUE end end
+ elseif r.label == "symbol" then
+ return function(object) if strchar(get_kind(object).d_char) == %r[1] then return TRUE end end
+ elseif r.label == "inscribed" then
+ return function(object) if object.note ~= 0 and strfind(strlower(quark_str(object.note)), strlower(%r[1])) then return TRUE end end
+ elseif r.label == "discount" then
+ local d1 = r.args.min
+ local d2 = r.args.max
+ if tonumber(d1) == nil then d1 = getglobal(d1) else d1 = tonumber(d1) end
+ if tonumber(d2) == nil then d2 = getglobal(d2) else d2 = tonumber(d2) end
+ return function(object) if is_aware(object) == TRUE and object.discount >= %d1 and object.discount <= %d2 then return TRUE end end
+ elseif r.label == "tval" then
+ local tv = r[1]
+ if tonumber(tv) == nil then tv = getglobal(tv) else tv = tonumber(tv) end
+ return function(object) if object.tval == %tv then return TRUE end end
+ elseif r.label == "sval" then
+ assert(r.args.min and r.args.max, "sval rule lacks min or max")
+ local sv1 = r.args.min
+ local sv2 = r.args.max
+ if tonumber(sv1) == nil then sv1 = getglobal(sv1) else sv1 = tonumber(sv1) end
+ if tonumber(sv2) == nil then sv2 = getglobal(sv2) else sv2 = tonumber(sv2) end
+ return function(object) if is_aware(object) == TRUE and object.sval >= %sv1 and object.sval <= %sv2 then return TRUE end end
+ elseif r.label == "status" then
+ return function(object) if object_status(object) == strlower(%r[1]) then return TRUE end end
+ elseif r.label == "state" then
+ if r[1] == "identified" then
+ return function(object) if is_known(object) == TRUE then return TRUE end end
+ else
+ return function(object) if is_known(object) == FALSE then return TRUE end end
+ end
+ elseif r.label == "race" then
+ return function(object) if strlower(get_race_name()) == strlower(%r[1]) then return TRUE end end
+ elseif r.label == "subrace" then
+ return function(object) if strlower(get_subrace_name()) == strlower(%r[1]) then return TRUE end end
+ elseif r.label == "class" then
+ return function(object) if strlower(get_class_name()) == strlower(%r[1]) then return TRUE end end
+ elseif r.label == "level" then
+ assert(r.args.min and r.args.max, "level rule lacks min or max")
+ return function(object) if player.lev >= tonumber(%r.args.min) and player.lev <= tonumber(%r.args.max) then return TRUE end end
+ elseif r.label == "skill" then
+ assert(r.args.min and r.args.max, "skill rule lacks min or max")
+ local s = find_skill_i(r[1])
+ assert(s ~= -1, "no skill "..r[1])
+ return function(object) if get_skill(%s) >= tonumber(%r.args.min) and get_skill(%s) <= tonumber(%r.args.max) then return TRUE end end
+ elseif r.label == "ability" then
+ local s = find_ability(r[1])
+ assert(s ~= -1, "no ability "..r[1])
+ return function(object) if has_ability(%s) == TRUE then return TRUE end end
+ end
+ end
+end
+
+function auto_inscribe_maker(inscription)
+ return function(...)
+ arg.n = arg.n + 1
+ arg[getn(arg)] = %inscription
+ return call(auto_inscribe, arg)
+ end
+end
+
+-- Generate a rule from a table
+function gen_full_rule(t)
+ -- only honor rules for this module
+ if not t.args.module then
+ t.args.module = "ToME"
+ end
+
+ if not ((t.args.module == "all") or (t.args.module == game_module)) then
+ return function() end
+ end
+
+ -- Check for which action to do
+ local apply_fct = auto_nothing
+ if t.args.type == "destroy" then apply_fct = auto_destroy
+ elseif t.args.type == "pickup" then apply_fct = auto_pickup
+ elseif t.args.type == "inscribe" then apply_fct = auto_inscribe_maker(t.args.inscription)
+ end
+
+ -- create the function tree
+ local rf
+ if t[1] then
+ rf = gen_rule_fct(t[1])
+ else
+ rf = function (object) end
+ end
+
+ -- create the final function
+ return function(...)
+ local rf = %rf
+ if rf(arg[1]) then
+ if call(%apply_fct, arg) == TRUE then return TRUE end
+ end
+ end
+end
+
+-- Create a function that checks for the rules(passed in xml form)
+function add_ruleset(s)
+ local tbl = xml:collect(s)
+ local i
+
+ -- Add all rules
+ for i = 1, getn(tbl) do
+ local t = tbl[i]
+
+ if t.label == "rule" then
+ -- Create the function tree
+ local fct = gen_full_rule(t)
+
+ -- Create the test function
+ __rules[__rules_max] =
+ {
+ ["table"] = t,
+ ["fct"] = fct
+ }
+ __rules_max = __rules_max + 1
+ end
+ end
+end
+
+-- Apply the current rules to an object
+-- call with at least (object, idx)
+function apply_rules(...)
+ local i
+ for i = 0, __rules_max - 1 do
+ if call(__rules[i].fct, arg) then return TRUE end
+ end
+ return FALSE
+end
+
+-- Clear the current rules
+function clean_ruleset()
+ __rules_max = 0
+ __rules = {}
+end
+
+------ helper fonctions for the GUI
+
+auto_aux = {}
+auto_aux.stack = { n = 0 }
+auto_aux.idx = 1
+auto_aux.rule = 1
+function auto_aux:go_right()
+ if auto_aux.rule[1] and type(auto_aux.rule[1]) == "table" then
+ tinsert(auto_aux.stack, auto_aux.idx)
+ tinsert(auto_aux.stack, auto_aux.rule)
+ auto_aux.rule = auto_aux.rule[1]
+ auto_aux.idx = 1
+ end
+end
+
+function auto_aux:go_left(sel)
+ local n = getn(auto_aux.stack)
+
+ if n > 0 then
+ auto_aux.idx = auto_aux.stack[n - 1]
+ auto_aux.rule = auto_aux.stack[n]
+ tremove(auto_aux.stack)
+ tremove(auto_aux.stack)
+ end
+end
+
+function auto_aux:go_down()
+ if getn(auto_aux.stack) > 1 then
+ if auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx + 1] then
+ auto_aux.idx = auto_aux.idx + 1
+ auto_aux.rule = auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx]
+ end
+ end
+end
+
+function auto_aux:go_up()
+ if getn(auto_aux.stack) > 1 then
+ if auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx - 1] then
+ auto_aux.idx = auto_aux.idx - 1
+ auto_aux.rule = auto_aux.stack[getn(auto_aux.stack)][auto_aux.idx]
+ end
+ end
+end
+
+function auto_aux:scroll_up()
+ xml.write_off_y = xml.write_off_y - 1
+end
+
+function auto_aux:scroll_down()
+ xml.write_off_y = xml.write_off_y + 1
+end
+
+function auto_aux:scroll_left()
+ xml.write_off_x = xml.write_off_x + 1
+end
+
+function auto_aux:scroll_right()
+ xml.write_off_x = xml.write_off_x - 1
+end
+
+function auto_aux:adjust_current(sel)
+ if __rules_max == 0 then return end
+
+ xml.write_off_y = 0
+ xml.write_off_x = 0
+ auto_aux.idx = 1
+ auto_aux.stack = { n = 0 }
+ auto_aux.rule = __rules[sel].table
+end
+
+function auto_aux:move_up(sel)
+ if sel > 0 then
+ local u = __rules[sel - 1]
+ local d = __rules[sel]
+ __rules[sel - 1] = d
+ __rules[sel] = u
+ return sel - 1
+ end
+ return sel
+end
+
+function auto_aux:move_down(sel)
+ if sel < __rules_max - 1 then
+ local u = __rules[sel]
+ local d = __rules[sel + 1]
+ __rules[sel + 1] = u
+ __rules[sel] = d
+ return sel + 1
+ end
+ return sel
+end
+
+function auto_aux:new_rule(sel, nam, typ, arg)
+ local r
+
+
+ -- nam can also directly be the table itself
+ if type(nam) == "table" then
+ r =
+ {
+ ["table"] = nam,
+ ["fct"] = function (object) end
+ }
+ elseif typ == "inscribe" then
+ if arg == "" then
+ arg = input_box("Inscription?", 79)
+ end
+ r =
+ {
+ ["table"] =
+ {
+ label = "rule",
+ args = { name = nam, type = typ, inscription = arg, module = game_module },
+ },
+ ["fct"] = function (object) end
+ }
+ else
+ r =
+ {
+ ["table"] =
+ {
+ label = "rule",
+ args = { name = nam, type = typ, module = game_module },
+ },
+ ["fct"] = function (object) end
+ }
+ end
+ tinsert(__rules, sel, r)
+ __rules_max = __rules_max + 1
+end
+
+function auto_aux:rename_rule(sel, nam)
+ if sel >= 0 and sel < __rules_max then
+ __rules[sel].table.args.name = nam
+ end
+end
+
+function auto_aux:save_ruleset()
+ xml.write = xml.write_file
+
+ print_hook("clean_ruleset()\nadd_ruleset\n[[\n")
+ local i
+ for i = 0, __rules_max - 1 do
+ xml:print_xml(__rules[i].table, '')
+ end
+ print_hook("]]\n")
+
+ xml.write = xml.write_screen
+end
+
+function auto_aux:del_self(sel)
+ if auto_aux.rule.label == "rule" then
+ tremove(__rules, sel)
+ __rules_max = __rules_max - 1
+ return sel - 1
+ else
+ local idx = auto_aux.idx
+ auto_aux:go_left(sel)
+ tremove(auto_aux.rule, idx)
+ return sel
+ end
+end
+
+auto_aux.types_desc =
+{
+ ["and"] =
+ {
+ "Check is true if all rules within it are true",
+ xml:collect([[<and><foo1>...</foo1><foo2>...</foo2><foo3>...</foo3></and>]]),
+ function ()
+ return xml:collect("<and></and>")
+ end,
+ },
+ ["or"] =
+ {
+ "Check is true if at least one rule within it is true",
+ xml:collect([[<or><foo1>...</foo1><foo2>...</foo2><foo3>...</foo3></or>]]),
+ function ()
+ return xml:collect("<or></or>")
+ end,
+ },
+ ["not"] =
+ {
+ "Invert the result of its child rule",
+ xml:collect([[<not><foo1>...</foo1></not>]]),
+ function ()
+ return xml:collect("<not></not>")
+ end,
+ },
+ ["comment"] =
+ {
+ "Comments are meaningless",
+ xml:collect([[<comment>Comment explaining something</comment>]]),
+ function ()
+ local n = input_box("Comment?", 79)
+ if n == "" then return end
+ return xml:collect("<comment>"..n.."</comment>")
+ end,
+ },
+ ["name"] =
+ {
+ "Check is true if object name matches name",
+ xml:collect([[<name>potion of healing</name>]]),
+ function ()
+ local n = input_box("Object name to match?", 79)
+ if n == "" then return end
+ return xml:collect("<name>"..n.."</name>")
+ end,
+ },
+ ["contain"] =
+ {
+ "Check is true if object name contains word",
+ xml:collect([[<contain>healing</contain>]]),
+ function ()
+ local n = input_box("Word to find in object name?", 79)
+ if n == "" then return end
+ return xml:collect("<contain>"..n.."</contain>")
+ end,
+ },
+ ["inscribed"] =
+ {
+ "Check is true if object inscription contains word",
+ xml:collect([[<inscribed>=g</inscribed>]]),
+ function ()
+ local n = input_box("Word to find in object inscription?", 79)
+ if n == "" then return end
+ return xml:collect("<inscribed>"..n.."</inscribed>")
+ end,
+ },
+ ["discount"] =
+ {
+ "Check is true if object discount is between 2 values",
+ xml:collect([[<sval min='50' max='100'></sval>]]),
+ function ()
+ local s = "<discount "
+
+ local n = input_box("Min discount?", 79)
+ if n == "" then return end
+ s = s.."min='"..n.."' "
+
+ n = input_box("Max discount?", 79)
+ if n == "" then return end
+ s = s.."max='"..n.."'></discount>"
+ return xml:collect(s)
+ end,
+ },
+ ["symbol"] =
+ {
+ "Check is true if object symbol is ok",
+ xml:collect([[<symbol>!</symbol>]]),
+ function ()
+ local n = input_box("Symbol to match?", 1)
+ if n == "" then return end
+ return xml:collect("<symbol>"..n.."</symbol>")
+ end,
+ },
+ ["status"] =
+ {
+ "Check is true if object status is ok",
+ xml:collect([[<status>good</status>]]),
+ function ()
+ local n = msg_box("[t]errible, [v]ery bad, [b]ad, [a]verage, [G]ood, [V]ery good, [S]pecial?")
+ local t =
+ {
+ ["t"] = "terrible",
+ ["v"] = "very bad",
+ ["b"] = "bad",
+ ["a"] = "average",
+ ["G"] = "good",
+ ["V"] = "very good",
+ ["S"] = "special",
+ }
+ if not t[strchar(n)] then return end
+ return xml:collect("<status>"..t[strchar(n)].."</status>")
+ end,
+ },
+ ["state"] =
+ {
+ "Check is true if object is identified/unidentified",
+ xml:collect([[<state>identified</state>]]),
+ function ()
+ local n = msg_box("[i]dentified, [n]on identified?")
+ local t =
+ {
+ ["i"] = "identified",
+ ["n"] = "not identified",
+ }
+ if not t[strchar(n)] then return end
+ return xml:collect("<state>"..t[strchar(n)].."</state>")
+ end,
+ },
+ ["tval"] =
+ {
+ "Check is true if object tval(from k_info.txt) is ok",
+ xml:collect([[<tval>55</tval>]]),
+ function ()
+ local n = input_box("Tval to match?", 79)
+ if n == "" then return end
+ return xml:collect("<tval>"..n.."</tval>")
+ end,
+ },
+ ["sval"] =
+ {
+ {
+ "Check is true if object sval(from k_info.txt) is between",
+ "2 values",
+ },
+ xml:collect([[<sval min='0' max='100'></sval>]]),
+ function ()
+ local s = "<sval "
+
+ local n = input_box("Min sval?", 79)
+ if n == "" then return end
+ s = s.."min='"..n.."' "
+
+ n = input_box("Max sval?", 79)
+ if n == "" then return end
+ s = s.."max='"..n.."'></sval>"
+ return xml:collect(s)
+ end,
+ },
+ ["race"] =
+ {
+ "Check is true if player race is ok",
+ xml:collect([[<race>dunadan</race>]]),
+ function ()
+ local n = input_box("Player race to match?", 79)
+ if n == "" then return end
+ return xml:collect("<race>"..n.."</race>")
+ end,
+ },
+ ["subrace"] =
+ {
+ "Check is true if player subrace is ok",
+ xml:collect([[<subrace>vampire</subrace>]]),
+ function ()
+ local n = input_box("Player subrace to match?", 79)
+ if n == "" then return end
+ return xml:collect("<subrace>"..n.."</subrace>")
+ end,
+ },
+ ["class"] =
+ {
+ "Check is true if player class is ok",
+ xml:collect([[<class>sorceror</class>]]),
+ function ()
+ local n = input_box("Player class to match?", 79)
+ if n == "" then return end
+ return xml:collect("<class>"..n.."</class>")
+ end,
+ },
+ ["level"] =
+ {
+ "Check is true if player level is between 2 values",
+ xml:collect([[<level min='20' max='50'></level>]]),
+ function ()
+ local s = "<level "
+
+ local n = input_box("Min player level?", 79)
+ if n == "" then return end
+ s = s.."min='"..n.."' "
+
+ n = input_box("Max player level?", 79)
+ if n == "" then return end
+ s = s.."max='"..n.."'></level>"
+
+ return xml:collect(s)
+ end,
+ },
+ ["skill"] =
+ {
+ "Check is true if player skill level is between 2 values",
+ xml:collect([[<skill min='10' max='20'>Divination</skill>]]),
+ function ()
+ local s = "<skill "
+
+ local n = input_box("Min skill level?", 79)
+ if n == "" then return end
+ s = s.."min='"..n.."' "
+
+ n = input_box("Max skill level?", 79)
+ if n == "" then return end
+ s = s.."max='"..n.."'>"
+
+ n = input_box("Skill name?", 79)
+ if n == "" then return end
+ if find_skill_i(n) == -1 then return end
+ s = s..n.."</skill>"
+
+ return xml:collect(s)
+ end,
+ },
+ ["ability"] =
+ {
+ "Check is true if player has the ability",
+ xml:collect([[<ability>Ammo creation</ability>]]),
+ function()
+ local n = input_box("Ability name?", 79)
+ if n == "" then return end
+ if find_ability(n) == -1 then return end
+ return xml:collect("<ability>"..n.."</ability>")
+ end,
+ },
+}
+
+function auto_aux:display_desc(sel)
+ local d = auto_aux.types_desc[sel][1]
+ if type(d) == "string" then
+ c_prt(TERM_WHITE, d, 1, 17)
+ else
+ local k, e, i
+ i = 0
+ for k, e in d do
+ c_prt(TERM_WHITE, e, 1 + i, 17)
+ i = i + 1
+ end
+ end
+end
+
+function auto_aux:add_child(sel)
+ -- <rule> and <not> contain only one match
+ if (auto_aux.rule.label == "rule" or auto_aux.rule.label == "not") and auto_aux.rule[1] then return end
+
+ -- Only <and> and <or> can contain
+ if auto_aux.rule.label ~= "rule" and auto_aux.rule.label ~= "and" and auto_aux.rule.label ~= "or" and auto_aux.rule.label ~= "not" then return end
+
+ -- get it
+ local r = auto_aux.types_desc[sel][3]()
+ if not r then return end
+
+ -- Ok add it
+ tinsert(auto_aux.rule, r[1])
+end
+
+function auto_aux.regen_ruleset()
+ local i
+ for i = 0, __rules_max - 1 do
+ __rules[i].fct = gen_full_rule(__rules[i].table)
+ end
+end
+
+
+-- Easily add new rules
+function easy_add_rule(typ, mode, do_status, obj)
+ local detect_rule
+
+ if mode == "tval" then
+ detect_rule = "<tval>"..obj.tval.."</tval>"
+ elseif mode == "tsval" then
+ detect_rule = "<and><tval>"..obj.tval.."</tval><sval min='"..obj.sval.."' max='"..obj.sval.."'></sval></and>"
+ elseif mode == "name" then
+ detect_rule = "<name>"..strlower(object_desc(obj, -1, 0)).."</name>"
+ end
+
+ if do_status == TRUE then
+ local status = object_status(obj)
+ if status and not (status == "") then
+ detect_rule = "<and>"..detect_rule.."<status>"..status.."</status></and>"
+ end
+ end
+
+ local rule = "<rule module='"..game_module.."' name='"..typ.."' type='"..typ.."'>"..detect_rule.."</rule>"
+ auto_aux:new_rule(0, xml:collect(rule)[1], '')
+ auto_aux.regen_ruleset()
+ msg_print("Rule added. Please go to the Automatizer screen (press = then T)")
+ msg_print("to save the modified ruleset.")
+end
diff --git a/lib/core/building.lua b/lib/core/building.lua
new file mode 100644
index 00000000..8e88888a
--- /dev/null
+++ b/lib/core/building.lua
@@ -0,0 +1,15 @@
+__building_actions = {}
+
+function add_building_action(a)
+ assert(a.index, "No building action index")
+ assert(a.action, "No building action action")
+ __building_actions[a.index] = a.action
+end
+
+function __bact_activate(bact)
+ if __building_actions[bact] then
+ return __building_actions[bact]()
+ end
+end
+
+add_hook_script(HOOK_BUILDING_ACTION, "__bact_activate", "__bact_activate")
diff --git a/lib/core/crpt_aux.lua b/lib/core/crpt_aux.lua
new file mode 100644
index 00000000..e4f16e2a
--- /dev/null
+++ b/lib/core/crpt_aux.lua
@@ -0,0 +1,243 @@
+-- Core functions for corruptions
+
+__corruptions = {}
+__corruptions_max = 0
+__corruptions_callbacks_max = 0
+
+-- Get the corruption
+function player.corruption(c, set)
+ if set then
+ player.corruptions_aux[c + 1] = set
+ player.redraw = bor(player.redraw, PR_BASIC)
+ player.update = bor(player.update, PU_BONUS, PU_TORCH, PU_BODY, PU_POWERS)
+ if (set == TRUE) and (__corruptions[c].gain) then
+ __corruptions[c].gain()
+ end
+ if (set == FALSE) and (__corruptions[c].lose) then
+ __corruptions[c].lose()
+ end
+ else
+ return player.corruptions_aux[c + 1]
+ end
+end
+
+-- Test if we have that corruption
+-- We must:
+-- 1) have it or be willing to get it
+-- 2) have all its dependancies
+-- 3) have none of its opposing corruptions
+-- 4) pass the possible tests
+function test_depend_corrupt(corrupt, can_gain)
+ local i, c
+
+ if not can_gain then can_gain = FALSE end
+
+ if can_gain == TRUE then
+ if (player.corruption(corrupt) ~= FALSE) then
+ return FALSE
+ end
+ else
+ if (player.corruption(corrupt) ~= TRUE) then
+ return FALSE
+ end
+ end
+
+ for c, i in __corruptions[corrupt].depends do
+ if test_depend_corrupt(c) ~= TRUE then
+ return FALSE
+ end
+ end
+
+ for c, i in __corruptions[corrupt].oppose do
+ if test_depend_corrupt(c) ~= FALSE then
+ return FALSE
+ end
+ end
+
+ -- are we even allowed to get it?
+ if __corruptions[corrupt].can_gain and (not __corruptions[corrupt].can_gain()) then
+ return FALSE
+ end
+
+ return TRUE
+end
+
+-- Gain a new corruption
+function gain_corruption(group)
+ local i, max
+ local pos = {}
+
+ -- Get the list of all possible ones
+ max = 0
+ for i = 0, __corruptions_max - 1 do
+ if __corruptions[i].group == group and test_depend_corrupt(i, TRUE) == TRUE and __corruptions[i].random == TRUE and __corruptions[i].allow() then
+ pos[max] = i
+ max = max + 1
+ end
+ end
+
+ -- Ok now get one of them
+ if (max > 0) then
+ local ret = rand_int(max)
+
+ player.corruption(pos[ret], TRUE)
+ cmsg_print(TERM_L_RED, __corruptions[pos[ret]].get_text)
+
+ return pos[ret]
+ else
+ return -1
+ end
+end
+
+-- Lose an existing corruption
+function lose_corruption()
+ local i, max
+ local pos = {}
+
+ -- Get the list of all possible ones
+ max = 0
+ for i = 0, __corruptions_max - 1 do
+ if test_depend_corrupt(i) == TRUE and __corruptions[i].removable == TRUE then
+ pos[max] = i
+ max = max + 1
+ end
+ end
+
+ -- Ok now get one of them
+ if (max > 0) then
+ local ret = rand_int(max)
+
+ player.corruption(pos[ret], FALSE)
+ cmsg_print(TERM_L_RED, __corruptions[pos[ret]].lose_text)
+
+ -- Ok now lets see if it broke some dependancies
+ for i = 0, max - 1 do
+ if player.corruption(pos[i]) ~= test_depend_corrupt(pos[i]) then
+ player.corruption(pos[i], FALSE)
+ cmsg_print(TERM_L_RED, __corruptions[pos[i]].lose_text)
+ end
+ end
+
+ return pos[ret]
+ else
+ return -1
+ end
+end
+
+-- Lose all corruptions (for e.g. Potion of New Life)
+function lose_all_corruptions()
+ local i;
+ for i = 0, __corruptions_max - 1 do
+ lose_corruption()
+ end
+ return -1
+end
+
+-- Creates a new corruption
+function add_corruption(c)
+ assert(c.color, "No corruption color")
+ assert(c.name, "No corruption name")
+ assert(c.get_text, "No corruption get_text")
+ assert(c.lose_text, "No corruption lose_text")
+ assert(c.desc, "No corruption desc")
+ assert(c.hooks, "Nothing to do for corruption")
+ if not c.random then c.random = TRUE end
+ if not c.removable then c.removable = TRUE end
+ if not c.allow then c.allow = function() return not nil end end
+
+ if c.depends == nil then c.depends = {} end
+ if c.oppose == nil then c.oppose = {} end
+
+ -- We must make sure the other ones opposes too
+ local o, i
+ for o, i in c.oppose do
+ __corruptions[o].oppose[__corruptions_max] = TRUE
+ end
+
+ local index, h
+ for index, h in c.hooks do
+ add_hook_script(index, "__lua__corrupt_callback"..__corruptions_callbacks_max, "__lua__corrupt_callback"..__corruptions_callbacks_max)
+ setglobal("__lua__corrupt_callback"..__corruptions_callbacks_max,
+ function (...)
+ if test_depend_corrupt(%__corruptions_max) == TRUE then
+ return call(%h, arg)
+ end
+ end
+ )
+ __corruptions_callbacks_max = __corruptions_callbacks_max + 1
+ end
+
+ if type(c.desc) == "table" then
+ local new_desc = ""
+ for index, h in c.desc do
+ new_desc = new_desc..h.."\n"
+ end
+ c.desc = new_desc
+ end
+
+ __corruptions[__corruptions_max] = c
+ __corruptions_max = __corruptions_max + 1
+ return (__corruptions_max - 1)
+end
+
+
+
+---------- Corruption spoiler generator -----------
+function corruption_spoiler_generate()
+ make_temp_file()
+ print_hook(
+[[~~~~~01|Corruptions (Spoiler)
+~~~~~02|Spoilers|Corruptions
+#####R=== ToME Corruptions Spoiler ===
+
+Sometimes adventurers become exposed to the dark powers of Morgoth. If they
+are unable to resist these powers, they become corrupted. Corruptions can
+change their physical or mental abilities, some of which can be good, and
+some bad. Most corruptions will affect you permanently, although some only
+operate when they are activated (whether by player choice or as a random
+event). You can check which corruptions you have in the knowledge screen 6
+(accessed through the '~' menu) or in a character dump.
+
+#####GGaining and (not) losing corruptions
+There are several ways that you can become corrupted.
+
+You can become corrupted by quaffing a Potion of Corruption or by drinking
+from a Fountain of Corruption. Also some strange items can be activated
+for corruption.
+
+Corruptions are permanent. Once you have one, you have it for life.
+
+]])
+ local i, e
+ for i = 0, __corruptions_max - 1 do
+ print_hook("[[[[[B"..__corruptions[i].name.."]\n")
+ print_hook(__corruptions[i].desc)
+ print_hook("[[[[[GGain message: "..__corruptions[i].get_text.."]\n")
+ if __corruptions[i].removable == TRUE then
+ print_hook("[[[[[RLose message: "..__corruptions[i].lose_text.."]\n")
+ else
+ print_hook("It is not removable.\n")
+ end
+
+ local ok
+ ok = nil
+ for e, _ in __corruptions[i].depends do ok = not nil end
+ if ok then
+ print_hook("It depends on:\n")
+ for e, _ in __corruptions[i].depends do
+ print_hook(" "..__corruptions[e].name.."\n")
+ end
+ end
+ ok = nil
+ for e, _ in __corruptions[i].oppose do ok = not nil end
+ if ok then
+ print_hook("It is opposed to:\n")
+ for e, _ in __corruptions[i].oppose do
+ print_hook(" "..__corruptions[e].name.."\n")
+ end
+ end
+ print_hook("\n\n")
+ end
+ close_temp_file()
+ msg_print("File created as: "..get_temp_name())
+end
diff --git a/lib/core/dungeon.lua b/lib/core/dungeon.lua
new file mode 100644
index 00000000..2877838d
--- /dev/null
+++ b/lib/core/dungeon.lua
@@ -0,0 +1,106 @@
+-- Internal lua file in charge of dungeon stuff
+
+function place_dungeon(y, x, d_idx)
+ if d_idx then
+ wild_map(y, x).entrance = 1000 + d_idx
+ else
+ wild_map(y, x).entrance = 0
+ end
+end
+
+function dungeon(d_idx)
+ return d_info[1 + d_idx]
+end
+
+function wild_feat(wild)
+ return wf_info[1 + wild.feat]
+end
+
+function explode_dir(dir)
+ return ddy[dir + 1], ddx[dir + 1]
+end
+
+function rotate_dir(dir, mov)
+ if mov > 0 then
+ if dir == 7 then dir = 8
+ elseif dir == 8 then dir = 9
+ elseif dir == 9 then dir = 6
+ elseif dir == 6 then dir = 3
+ elseif dir == 3 then dir = 2
+ elseif dir == 2 then dir = 1
+ elseif dir == 1 then dir = 4
+ elseif dir == 4 then dir = 7
+ end
+ elseif mov < 0 then
+ if dir == 7 then dir = 4
+ elseif dir == 4 then dir = 1
+ elseif dir == 1 then dir = 2
+ elseif dir == 2 then dir = 3
+ elseif dir == 3 then dir = 6
+ elseif dir == 6 then dir = 9
+ elseif dir == 9 then dir = 8
+ elseif dir == 8 then dir = 7
+ end
+ end
+
+ return dir
+end
+
+-- Check if the map is a filename or directly a map
+function load_map(map, y, x)
+ if strsub(map, 1, 5) == "#!map" then
+ %load_map(TRUE, map, y, x)
+ else
+ %load_map(FALSE, map, y, x)
+ end
+end
+function get_map_size(map)
+ if strsub(map, 1, 5) == "#!map" then
+ return %get_map_size(TRUE, map)
+ else
+ return %get_map_size(FALSE, map)
+ end
+end
+
+-- Place a trap for a specific level
+function place_trap(y, x, level)
+ local old_dun = dun_level
+ dun_level = level
+ %place_trap(y, x)
+ dun_level = old_dun
+end
+
+-- Level generators processing
+__level_generators = {}
+
+function level_generator(t)
+ assert(t.name, "no generator name")
+ assert(t.gen, "no generator function")
+
+ if not t.stairs then t.stairs = TRUE end
+ if not t.monsters then t.monsters = TRUE end
+ if not t.objects then t.objects = TRUE end
+ if not t.miscs then t.miscs = TRUE end
+
+ __level_generators[t.name] = t.gen
+ add_scripted_generator(t.name, t.stairs, t.monsters, t.objects, t.miscs)
+end
+
+function level_generate(name)
+ assert(__level_generators[name], "Unknown level generator '"..name.."'")
+ return __level_generators[name]()
+end
+
+--[[ Example
+level_generator
+{
+ ["name"] = "test",
+ ["gen"] = function()
+ print("zog")
+ for i = 1, 30 do
+ cave(i, 2).feat = 1
+ end
+ return new_player_spot(get_branch())
+ end,
+}
+]]
diff --git a/lib/core/gen_idx.lua b/lib/core/gen_idx.lua
new file mode 100644
index 00000000..5f3af435
--- /dev/null
+++ b/lib/core/gen_idx.lua
@@ -0,0 +1,261 @@
+-- Place here the list of files to parse
+files =
+{
+ "birth.txt",
+ "experien.hlp",
+ "gods.txt",
+ "explore.hlp",
+ "newbie.hlp",
+ "advanced.hlp",
+ "help.hlp",
+ "general.txt",
+ "whattome.txt",
+ "dungeon.txt",
+ "spoiler.hlp",
+ "g_melkor.txt",
+ "skills.txt",
+ "c_bard.txt",
+ "c_druid.txt",
+ "c_lorema.txt",
+ "c_mage.txt",
+ "c_mimic.txt",
+ "c_mindcr.txt",
+ "c_monk.txt",
+ "c_palad.txt",
+ "c_posses.txt",
+ "c_pr_drk.txt",
+ "c_pr_eru.txt",
+ "c_pr_man.txt",
+ "c_symbia.txt",
+ "c_alchem.txt",
+ "c_archer.txt",
+ "c_assass.txt",
+ "c_axemas.txt",
+ "c_demono.txt",
+ "c_geoman.txt",
+ "c_hafted.txt",
+ "c_necro.txt",
+ "c_polear.txt",
+ "c_ranger.txt",
+ "c_rogue.txt",
+ "c_runecr.txt",
+ "c_sorcer.txt",
+ "c_swordm.txt",
+ "c_thaum.txt",
+ "c_unbel.txt",
+ "c_warper.txt",
+ "c_warrio.txt",
+ "m_meta.txt",
+ "rm_skel.txt",
+ "rm_zomb.txt",
+ "luckspoi.txt",
+ "m_air.txt",
+ "dunspoil.txt",
+ "g_eru.txt",
+ "g_manwe.txt",
+ "g_tulkas.txt",
+ "m_divin.txt",
+ "m_mimic.txt",
+ "m_water.txt",
+ "magic.txt",
+ "r_drkelf.txt",
+ "r_dwarf.txt",
+ "r_elf.txt",
+ "r_hielf.txt",
+ "r_hobbit.txt",
+ "r_pettyd.txt",
+ "r_wodelf.txt",
+ "rm_spec.txt",
+ "tome_faq.txt",
+ "ability.txt",
+ "automat.txt",
+ "c_summon.txt",
+ "command.txt",
+ "corspoil.txt",
+ "debug.txt",
+ "m_music.txt",
+ "rm_barb.txt",
+ "macrofaq.txt",
+ "m_necrom.txt",
+ "m_mindcr.txt",
+ "m_symbio.txt",
+ "m_thaum.txt",
+ "magic.hlp",
+ "m_convey.txt",
+ "m_fire.txt",
+ "m_mana.txt",
+ "m_mind.txt",
+ "m_nature.txt",
+ "m_tempo.txt",
+ "m_udun.txt",
+ "m_geoman.txt",
+ "essences.txt",
+ "r_ent.txt",
+ "g_yavann.txt",
+ "defines.txt",
+ "rm_vamp.txt",
+ "inscrip.txt",
+ "m_earth.txt",
+ "option.txt",
+ "attack.txt",
+ "version.txt",
+ "m_demono.txt",
+ "r_beorn.txt",
+ "r_deathm.txt",
+ "r_rohank.txt",
+ "r_hafogr.txt",
+ "r_human.txt",
+ "r_kobold.txt",
+ "r_maia.txt",
+ "r_orc.txt",
+ "r_thlord.txt",
+ "r_troll.txt",
+ "r_yeek.txt",
+ "rm_class.txt",
+ "rm_herm.txt",
+ "rm_lsoul.txt",
+ "wishing.txt",
+ "c_priest.txt",
+ "fatespoi.txt",
+ "gambling.txt",
+ "r_dunad.txt",
+ "r_gnome.txt",
+ "r_hafelf.txt",
+ "c_merch.txt",
+ "spoil_faq.txt",
+}
+
+out_file = "index.txt"
+
+index = {}
+
+function parse_file(file)
+ local fff = openfile(path_build(ANGBAND_DIR_HELP, file), "r")
+ local line
+
+ line = read(fff, "*l")
+ while line do
+ local i, j, anchor, name, subname = strfind(line, "~~~~~(%d+)|([%d%a -]+)|([%d%a -]+)")
+ if not i then
+ i, j, anchor, name = strfind(line, "~~~~~(%d+)|([%d%a -]+)")
+
+ subname = nil
+ end
+
+ if i then
+ if not index[name] then
+ index[name] = {}
+ end
+ if subname then
+ tinsert(index[name], { __name__ = subname, __file__ = file, __anchor__ = anchor})
+ else
+ tinsert(index[name], { __name__ = "__primary__", __file__ = file, __anchor__ = anchor})
+ end
+ end
+
+ line = read(fff, "*l")
+ end
+
+ closefile(fff)
+end
+
+function sort_fct(a, b)
+ local i, len
+
+ a = a.__name__
+ b = b.__name__
+
+ if strlen(a) > strlen(b) then len = strlen(b) else len = strlen(a) end
+
+ for i = 1, len do
+ local ac = strbyte(a, i)
+ local bc = strbyte(b, i)
+
+ if ac < bc then
+ return not nil
+ elseif ac > bc then
+ return nil
+ end
+ end
+ if strlen(a) > strlen(b) then return nil else return not nil end
+end
+
+function generate_index()
+ local k, e, index_list
+ for _, e in files do
+ parse_file(e)
+ end
+
+ index_list = {}
+ for k, e in index do
+ -- Ok either my sort function or lua sort function sucks ass ..
+ sort(e, sort_fct)
+ sort(e, sort_fct)
+ sort(e, sort_fct)
+ sort(e, sort_fct)
+ sort(e, sort_fct)
+ tinsert(index_list, {__name__= k, __table__ = e})
+ end
+
+ -- Ok either my sort function or lua sort function sucks ass ..
+ sort(index_list, sort_fct)
+ sort(index_list, sort_fct)
+ sort(index_list, sort_fct)
+ sort(index_list, sort_fct)
+ sort(index_list, sort_fct)
+ index = index_list
+end
+
+function out_link(fff, space, name, file, anchor)
+ write(fff, space.."*****"..file.."*"..anchor.."["..name.."]\n")
+end
+
+function print_index()
+ local i, j, c, new_c
+ local fff = openfile(path_build(ANGBAND_DIR_HELP, out_file), "w")
+
+ write(fff,
+[[|||||oy
+#####R /----------------------------------------\
+#####R < Help Index >
+#####R \----------------------------------------/
+
+This is the index of everything in the T.o.M.E. documentation.
+
+#####BHit a letter key to jump to the entries for that letter.
+
+Some entries in the index link to the same place as other entries. This is
+intentional, so that the information you want is easy to find.
+
+Don't forget you can browse the help from the *****help.hlp*02[Main menu].
+
+#####sSpotted a problem with the help files, or some content thats missing?
+#####sContact fearoffours@t-o-m-e.net .
+
+]])
+
+ c = ' '
+ for i = 1, getn(index) do
+ new_c = strbyte(index[i].__name__, 1)
+ if c ~= new_c then
+ c = new_c
+ write(fff, "~~~~~"..c.."\n")
+ write(fff, "*****/"..strchar(c)..out_file.."*"..c.."["..strchar(c).."]\n")
+ end
+ for j = 1, getn(index[i].__table__) do
+ if index[i].__table__[j].__name__ == "__primary__" then
+ out_link(fff, " ", index[i].__name__, index[i].__table__[j].__file__, index[i].__table__[j].__anchor__)
+ end
+ end
+ for j = 1, getn(index[i].__table__) do
+ if index[i].__table__[j].__name__ ~= "__primary__" then
+ out_link(fff, " ", index[i].__table__[j].__name__, index[i].__table__[j].__file__, index[i].__table__[j].__anchor__)
+ end
+ end
+ end
+ closefile(fff)
+end
+
+generate_index()
+
+print_index()
diff --git a/lib/core/gods.lua b/lib/core/gods.lua
new file mode 100644
index 00000000..77e0aad5
--- /dev/null
+++ b/lib/core/gods.lua
@@ -0,0 +1,40 @@
+-- Gods helper files
+
+-- Gods structs
+
+__gods_hook = {}
+__gods_callbacks = {}
+__gods_callbacks_max = 0
+
+function add_god(q)
+ local i, index, d, z, qq
+
+ assert(q.name, "No god name")
+ assert(q.desc, "No god desc")
+ assert(q.hooks, "No god hooks")
+
+ i = add_new_gods(q.name);
+
+ z = 0
+ for index, d in q.desc do
+ desc_god(i, z, d);
+ z = z + 1
+ end
+
+ __gods_hook[i] = q.hooks
+ for index, d in q.hooks do
+ add_hook_script(index, "__lua__gods_callback"..__gods_callbacks_max, "__lua__gods_callback"..__gods_callbacks_max)
+ setglobal("__lua__gods_callback"..__gods_callbacks_max, d)
+ __gods_callbacks_max = __gods_callbacks_max + 1
+ end
+ if q.data then
+ for index, d in q.data do
+ -- Besure it exists
+ setglobal(index, d)
+
+ -- Make it save & load
+ add_loadsave(index, d)
+ end
+ end
+ return i
+end
diff --git a/lib/core/help.lua b/lib/core/help.lua
new file mode 100644
index 00000000..a581fe63
--- /dev/null
+++ b/lib/core/help.lua
@@ -0,0 +1,141 @@
+-- Ingame contextual help
+
+-- We use our own hook list as to not overburn the hook proccessor
+-- with many hooks that would slow down things
+-- It would be very meaningless if the option is not even on
+__ingame_hooks = {}
+
+__ingame_help_max = 0
+
+function ingame_help(t, ...)
+ -- This function can also be used to call the callbacks
+ if type(t) == "string" then
+ local f = getglobal("__ingame_help_fct_"..t)
+ call(f, arg)
+ return
+ end
+
+ assert(t.desc or t.fct, "no ingame help desc/fct")
+ assert(t.hook or t.callback, "no ingame help hook/callback")
+ if t.hook then assert(t.event, "no ingame hepl event needed by hook") end
+
+ -- Set it to only trigger once
+ setglobal("__ingame_help_activated_"..__ingame_help_max, FALSE)
+ -- Save/load it
+ add_loadsave("__ingame_help_activated_"..__ingame_help_max, FALSE)
+
+ if t.hook then
+ -- If the hok list didnt exist yet, add it
+ if not __ingame_hooks[t.hook] then
+ -- Set it to empty, we'll fill it later
+ __ingame_hooks[t.hook] = {}
+ -- Add the global hook
+ add_hooks
+ {
+ [t.hook] = function (...)
+ if option_ingame_help ~= TRUE then return end
+ local k, e
+ for k, e in __ingame_hooks[%t.hook] do
+ if k ~= "n" then
+ call(e, arg)
+ end
+ end
+ end
+ }
+ end
+ if t.desc then
+ tinsert(__ingame_hooks[t.hook],
+ function (...)
+ local tbl = %t
+ if getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE then
+ if call(tbl.event, arg) == TRUE then
+ local k, e
+ for k, e in tbl.desc do
+ msg_print(TERM_YELLOW, e)
+ end
+ setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE)
+ end
+ end
+ end
+ )
+ elseif t.fct then
+ tinsert(__ingame_hooks[t.hook],
+ function (...)
+ local tbl = %t
+ if getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE then
+ if call(tbl.event, arg) == TRUE then
+ if tbl.fct() == TRUE then
+ setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE)
+ end
+ end
+ end
+ end
+ )
+ end
+ else
+ local no_test = FALSE
+ if t.no_test == TRUE then no_test = TRUE end
+ if t.desc then
+ setglobal
+ (
+ "__ingame_help_fct_"..(t.callback),
+ function (...)
+ local tbl = %t
+ if ((option_ingame_help == TRUE) or (%no_test == TRUE)) and (getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE) then
+ local k, e
+ for k, e in tbl.desc do
+ msg_print(TERM_YELLOW, e)
+ end
+ setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE)
+ end
+ end
+ )
+ elseif t.fct then
+ setglobal
+ (
+ "__ingame_help_fct_"..(t.callback),
+ function (...)
+ local tbl = %t
+ if ((option_ingame_help == TRUE) or (%no_test == TRUE)) and (getglobal("__ingame_help_activated_"..%__ingame_help_max) == FALSE) then
+ if call(tbl.fct, arg) == TRUE then
+ setglobal("__ingame_help_activated_"..%__ingame_help_max, TRUE)
+ end
+ end
+ end
+ )
+ end
+ end
+
+ __ingame_help_max = __ingame_help_max + 1
+end
+
+-- Clean up the ingame help seen at birth
+add_hooks
+{
+ [HOOK_BIRTH_OBJECTS] = function()
+ local i
+ for i = 0, __ingame_help_max - 1 do
+ setglobal("__ingame_help_activated_"..i, FALSE)
+ end
+ end
+}
+
+function ingame_clean()
+ local i
+ for i = 0, __ingame_help_max - 1 do
+ setglobal("__ingame_help_activated_"..i, FALSE)
+ end
+end
+
+-- helper function, brings up a doc
+function ingame_help_doc(name, anchor)
+ -- Save screen
+ screen_save();
+
+ -- Peruse the help file
+ if not anchor then anchor = 0 end
+ show_file(name, 0, -anchor, 0)
+
+ -- Load screen
+ screen_load()
+end
diff --git a/lib/core/init.lua b/lib/core/init.lua
new file mode 100644
index 00000000..9a9ec1ee
--- /dev/null
+++ b/lib/core/init.lua
@@ -0,0 +1,84 @@
+--
+-- This file is loaded at the initialisation of ToME
+-- Load the system functions
+--
+
+-- Name of globals to save
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "load.lua")
+
+-- Very thin xml parser(49 lines ;)
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "xml.lua")
+
+-- various vital helper code
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "util.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "player.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "objects.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "monsters.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "powers.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "building.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "dungeon.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "s_aux.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "crpt_aux.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "mimc_aux.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "quests.lua")
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "gods.lua")
+
+-- Load the ingame contextual help
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "help.lua")
+
+-- let the store specific stuff happen!
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "stores.lua")
+
+--------------------------------------------------------------
+--------------------------------------------------------------
+--------------------------------------------------------------
+-------------Here we load the non vital scripts---------------
+-----------------------from lib/scpt--------------------------
+--------------------------------------------------------------
+--------------------------------------------------------------
+tome_dofile("init.lua")
+
+-- The dofile functions for each patch
+patch_dofile = {}
+
+-- Now load patches
+function load_patches()
+ scansubdir(ANGBAND_DIR_PATCH)
+ for i = 0, scansubdir_max - 1 do
+ if (scansubdir_result[i + 1] ~= ".") and (scansubdir_result[i + 1] ~= "..") then
+ local dir = path_build(ANGBAND_DIR_PATCH, scansubdir_result[i + 1])
+ local file = path_build(dir, "patch.lua")
+ if file_exist(file) == TRUE then
+ patch_init = nil
+ tome_dofile_anywhere(dir, "patch.lua", TRUE)
+ unset_safe_globals()
+ if patch_init == nil then
+ set_safe_globals()
+ quit("Patch in "..file.." did not include a patch_init() function")
+ else
+ set_safe_globals()
+
+ -- create the dofile function
+ patch_dofile[scansubdir_result[i + 1]] = function(f)
+ tome_dofile_anywhere(%dir, f, TRUE)
+ end
+
+ local name, version = patch_init()
+ if name == nil or version == nil then
+ quit("Patch in "..file.." did not return valid name or version.\nIt must return name, version")
+ end
+ patch_version(name, version)
+ end
+ end
+ end
+ end
+end
+load_patches()
+
+--------------------------------------------------------------
+--------------------------------------------------------------
+--------------------------------------------------------------
+--
+-- Do not thouch after this line
+--
+tome_dofile_anywhere(ANGBAND_DIR_CORE, "load2.lua")
diff --git a/lib/core/load.lua b/lib/core/load.lua
new file mode 100644
index 00000000..9522ec91
--- /dev/null
+++ b/lib/core/load.lua
@@ -0,0 +1,37 @@
+-- Savefile stuff
+-- Do not meddle in the affairs of savefiles for they are subtle and quick to be become incompatible
+
+__loadsave_name = {}
+__loadsave_max = 0
+__loadsave_tmp = 0
+
+function add_loadsave(name, default)
+ assert(name, "No variable name to save")
+ assert(default, "No default value")
+
+ -- if it is a table we must create many entries
+ if type(default) == "table" then
+ for k, e in default do
+ add_loadsave(name.."."..k, e)
+ end
+ else
+ __loadsave_name[__loadsave_max] = { name = name, default = default }
+ __loadsave_max = __loadsave_max + 1
+ end
+end
+
+-- Example of how to save a table
+-- NOTE: { 1, 2, 3 } will NOT work, the key MUST be a string
+--[[
+add_loadsave("t",
+{
+ foo = 7,
+ tab = {
+ a = 1,
+ b = 2,
+ tab = {
+ a=1, b=2, c=3,
+ },
+ },
+})
+]]
diff --git a/lib/core/load2.lua b/lib/core/load2.lua
new file mode 100644
index 00000000..7ea432e9
--- /dev/null
+++ b/lib/core/load2.lua
@@ -0,0 +1,63 @@
+-- Savefile helpers
+
+-- function called when a key in the variable part ofthe savefile is read
+-- if the key matches what we need, we use it, otehrwise just ignore it
+function __savefile_load(key, val)
+ local index, elem
+
+ for index, elem in __loadsave_name do
+ if (key == elem.name) then
+ dostring(elem.name.." = "..val)
+ end
+ end
+end
+
+function dump_loadsave()
+ local k, e
+ for k, e in __loadsave_name do
+ msg_print(k.." :: ".. e.name.." ["..e.default.."]")
+ end
+end
+
+-- called when the game is saved, can only save numbers
+-- assosiate a key with them to allow the loading code to recognize them
+function __savefile_save()
+ local index, elem
+ for index, elem in __loadsave_name do
+ dostring("__loadsave_tmp = "..elem.name)
+ save_number_key(elem.name, __loadsave_tmp);
+ end
+end
+
+register_savefile(__loadsave_max)
+add_hook_script(HOOK_LOAD_GAME, "__savefile_load", "__hook_load")
+add_hook_script(HOOK_SAVE_GAME, "__savefile_save", "__hook_save")
+
+-- Parse a flattened(i.e: foo.bar.zog) table path and recrate tables
+function reconstruct_table(name)
+ for i = 1, strlen(name) - 1 do
+ if strsub(name, i, i) == "." then
+ local tbl = strsub(name, 1, i - 1)
+
+ if dostring("return "..tbl) == nil then
+ dostring(tbl.."={}")
+ end
+ end
+ end
+end
+
+-- Automagically set unkown variables, otherwise the savefile code
+-- might get VERY upset
+do
+ local k, e
+ -- We need to be able to check for unknown globals
+ unset_safe_globals()
+ for k, e in __loadsave_name do
+ reconstruct_table(e.name)
+ if dostring("return "..(e.name)) == nil then
+ dostring((e.name).." = "..(e.default))
+ end
+ end
+ -- Now taht we did, we set it back, for it is usefull ;)
+ set_safe_globals()
+end
diff --git a/lib/core/mimc_aux.lua b/lib/core/mimc_aux.lua
new file mode 100644
index 00000000..c37a869b
--- /dev/null
+++ b/lib/core/mimc_aux.lua
@@ -0,0 +1,95 @@
+-- Mimic shapes helper file
+
+__mimics = {}
+__mimics_max = 1
+__mimics_names = {}
+
+function add_mimic_shape(t)
+ assert(t.name, "no mimic name")
+ assert(t.desc, "no mimic desc")
+ assert(t.calc, "no mimic calc")
+ assert(t.level, "no mimic level")
+ assert(t.duration, "no mimic duration")
+
+ if not t.limit then t.limit = 0 end
+
+ if not t.obj_name then
+ t.obj_name = t.name
+ end
+
+ t.show_name = '['..t.name..']'
+
+ -- if it needs hooks, add them
+ if t.hooks then
+ add_hooks(t.hooks)
+ end
+
+ -- Add it in a name to index hash table
+ __mimics_names[t.name] = __mimics_max
+
+ __mimics[__mimics_max] = t
+ __mimics_max = __mimics_max + 1
+end
+
+function resolve_mimic_name(name)
+ if __mimics_names[name] then
+ return __mimics_names[name]
+ else
+ return -1
+ end
+end
+
+function find_random_mimic_shape(level, limit, realm)
+ local mimic, tries
+
+ tries = 1000
+ while tries > 0 do
+ tries = tries - 1
+ mimic = rand_range(1, __mimics_max - 1)
+ if (not realm) or (__mimics[mimic].realm == realm) then
+ if limit >= __mimics[mimic].limit then
+ if (rand_int(__mimics[mimic].level * 3) < level) and (__mimics[mimic].rarity < 100) and (magik(100 - __mimics[mimic].rarity) == TRUE) then
+ break
+ end
+ end
+ end
+ end
+ if tries > 0 then
+ return mimic
+ else
+ return resolve_mimic_name("Abomination")
+ end
+end
+
+function get_mimic_info(mimic, info)
+ if not __mimics[mimic] then return 0 end
+ return __mimics[mimic][info]
+end
+
+function get_mimic_rand_dur(mimic)
+ return rand_range(__mimics[mimic].duration[1], __mimics[mimic].duration[2])
+end
+
+function calc_mimic(mimic)
+ return __mimics[mimic].calc()
+end
+
+function calc_mimic_power(mimic)
+ if __mimics[mimic].power then __mimics[mimic].power() end
+end
+
+--- Here comes the only vital shape
+
+add_mimic_shape
+{
+ ["name"] = "Abomination",
+ ["obj_name"] = "Abominable Cloak",
+ ["desc"] = "Abominations are failed experiments of powerful wizards.",
+ ["realm"] = nil,
+ ["level"] = 1,
+ ["rarity"] = 101,
+ ["duration"] = {20, 100},
+ ["calc"] = function ()
+ apply_flags(TR1_SPEED + TR1_STR + TR1_INT + TR1_WIS + TR1_DEX + TR1_CON + TR1_CHR, 0, TR3_AGGRAVATE, 0, 0, 0, -10)
+ end,
+}
diff --git a/lib/core/monsters.lua b/lib/core/monsters.lua
new file mode 100644
index 00000000..ca2851a0
--- /dev/null
+++ b/lib/core/monsters.lua
@@ -0,0 +1,16 @@
+-- SYSTEM FILE
+--
+-- Monster stuff, do not touch
+--
+
+function summon_monster(y, x, lev, friend, typ)
+ if type(typ) == "number" then
+ if friend == TRUE then
+ return summon_specific_friendly(y, x, lev, typ, FALSE)
+ else
+ return summon_specific(y, x, lev, typ)
+ end
+ else
+ return summon_monster_aux(y, x, lev, friend, typ)
+ end
+end
diff --git a/lib/core/objects.lua b/lib/core/objects.lua
new file mode 100644
index 00000000..97320b82
--- /dev/null
+++ b/lib/core/objects.lua
@@ -0,0 +1,45 @@
+-- SYSTEM FILE
+--
+-- Lua object funtions
+--
+
+function create_object(tval, sval)
+ local obj = new_object()
+ object_prep(obj, lookup_kind(tval, sval))
+ return (obj)
+end
+
+function set_item_tester(tester)
+ if tolua.type(tester) == "number" then
+ lua_set_item_tester(tester, "")
+ end
+ if tolua.type(tester) == "string" then
+ lua_set_item_tester(0, tester)
+ end
+ if tolua.type(tester) == "function" then
+ __get_item_hook_default = tester
+ lua_set_item_tester(0, "__get_item_hook_default")
+ end
+end
+
+function create_artifact(a_idx)
+ local obj
+ local tval, sval
+
+ tval = a_info[a_idx + 1].tval
+ sval = a_info[a_idx + 1].sval
+ obj = create_object(tval, sval)
+ obj.name1 = a_idx
+ apply_magic(obj, -1, TRUE, TRUE, TRUE)
+
+ return (obj)
+end
+
+function get_kind(obj)
+ return k_info[obj.k_idx + 1]
+end
+
+function get_item(ask, deny, flags, mask)
+ set_item_tester(mask)
+ return get_item_aux(0, ask, deny, flags)
+end
diff --git a/lib/core/player.lua b/lib/core/player.lua
new file mode 100644
index 00000000..3017d94f
--- /dev/null
+++ b/lib/core/player.lua
@@ -0,0 +1,140 @@
+-- SYSTEM FILE
+--
+-- Lua player funtions
+--
+
+-- Gods
+function deity(i)
+ return deity_info[1 + i]
+end
+
+-------- skill stuff ---------
+
+-- Easy skill access
+function skill(i)
+ return s_info[i + 1]
+end
+
+-- Sart a lasting spell
+function player.start_lasting_spell(spl)
+ player.music_extra = -spl
+end
+
+-- stat mods
+function player.modify_stat(stat, inc)
+ player.stat_add[1 + stat] = player.stat_add[1 + stat] + inc
+end
+
+-- powers mods
+function player.add_power(pow)
+ player.powers[1 + pow] = TRUE
+end
+
+-- easier inventory access
+function player.inventory(i)
+ return player.inventory_real[i + 1]
+end
+
+-- modify mana
+-- returns TRUE if there is a pb
+function increase_mana(amt)
+ player.csp = player.csp + amt
+ player.redraw = bor(player.redraw, PR_MANA)
+ if (player.csp < 0) then
+ player.csp = 0
+ return TRUE
+ end
+ if (player.csp > player.msp) then
+ player.csp = player.msp
+ end
+ return FALSE
+end
+
+
+-- Return the coordinates of the player whether in wild or not
+function player.get_wild_coord()
+ if player.wild_mode == TRUE then
+ return player.py, player.px
+ else
+ return player.wilderness_y, player.wilderness_x
+ end
+end
+
+-- Create a new power
+__power_fct = {}
+function add_power(p)
+ local i
+
+ assert(p.name, "No power name!")
+ assert(p.desc, "No power desc!")
+ assert(p.desc_get, "No power desc get!")
+ assert(p.desc_lose, "No power desc lose!")
+ assert(p.stat, "No power stat!")
+ assert(p.level, "No power level!")
+ assert(p.cost, "No power cost!")
+ assert(p.fail, "No power fail!")
+ assert(p.power, "No power power!")
+
+ i = add_new_power(p.name, p.desc, p.desc_get, p.desc_lose, p.level, p.cost, p.stat, p.fail)
+ __power_fct[i] = p.power
+ return i
+end
+
+function __power_fct_activate(power)
+ if __power_fct[power] then
+ __power_fct[power]()
+ return TRUE
+ else
+ return FALSE
+ end
+end
+
+-- Register in the hook list
+add_hook_script(HOOK_ACTIVATE_POWER, "__power_fct_activate", "__power_fct_activate")
+
+
+--- Mkeys
+
+-- Create a new power
+__mkey_fct = {}
+function add_mkey(p)
+ local i
+
+ assert(p.mkey, "No mkey mkey!")
+ assert(p.fct, "No mkeey fct!")
+
+ __mkey_fct[p.mkey] = p.fct
+end
+
+function __mkey_fct_activate(power)
+ if __mkey_fct[power] then
+ __mkey_fct[power]()
+ return TRUE
+ else
+ return FALSE
+ end
+end
+
+-- Register in the hook list
+add_hook_script(HOOK_MKEY, "__mkey_fct_activate", "__mkey_fct_activate")
+
+
+-- Subraces
+function subrace(racem)
+ return race_mod_info[racem + 1]
+end
+
+function subrace_add_power(subrace, power)
+ for i = 1, 4 do
+ if subrace.powers[i] == -1 then
+ subrace.powers[i] = power
+ return not nil
+ end
+ end
+ return nil
+end
+
+-- Body parts
+function player.add_body_part(part, nb)
+ player.extra_body_parts[part + 1] = player.extra_body_parts[part + 1] + nb
+end
diff --git a/lib/core/powers.lua b/lib/core/powers.lua
new file mode 100644
index 00000000..839a921d
--- /dev/null
+++ b/lib/core/powers.lua
@@ -0,0 +1,105 @@
+--
+-- Helper functions for magic powers
+--
+
+__magic_powers = {}
+
+function add_magic(m)
+ local i, ret
+
+ if type(m.spell_list) ~= "table" then
+ error("add_magic called without a table")
+ end
+
+ -- Ok iterate over all the powers to add
+ local index, p, max
+
+ -- First, count them
+ max = 0
+ for index, p in m.spell_list do
+ max = max + 1
+ end
+
+ -- Now register it
+ ret = {}
+ i = new_magic_power(max)
+ ret.spells = i
+ ret.max = max
+ ret.fail_fct = m.fail
+ if m.stat then
+ ret.stat = m.stat
+ else
+ ret.stat = A_INT
+ end
+ if m.get_level then
+ ret.get_current_level = m.get_level
+ else
+ ret.get_current_level = function()
+ return player.lev
+ end
+ end
+
+ -- And add each spells
+ max = 0
+ ret.info = {}
+ ret.spell = {}
+ for index, p in m.spell_list do
+ assert(p.name, "No name for the spell!")
+ assert(p.desc, "No desc for the spell!")
+ assert(p.mana, "No mana for the spell!")
+ assert(p.level, "No level for the spell!")
+ assert(p.fail, "No fail for the spell!")
+ assert(p.info, "No info for the spell!")
+ assert(p.spell, "No spell for the spell!")
+
+ get_magic_power(i, max).name = p.name
+ get_magic_power(i, max).desc = p.desc
+ get_magic_power(i, max).mana_cost = p.mana
+ get_magic_power(i, max).min_lev = p.level
+ get_magic_power(i, max).fail = p.fail
+ ret.info[max] = p.info
+ ret.spell[max] = p.spell
+
+ max = max + 1
+ end
+
+ return ret
+end
+
+function __get_magic_info(power)
+ return __current_magic_power_info[power]()
+end
+
+function execute_magic(m)
+ local sn, ret
+
+ -- Ask for a spell
+ __current_magic_power_info = m.info
+ ret, sn = select_magic_power(0, m.spells, m.max, "__get_magic_info", m.get_current_level(), m.stat)
+ if (ret == FALSE) then return end
+
+ -- Verify mana needs
+ if (get_magic_power(m.spells, sn).mana_cost > player.csp) then msg_print("Not enough mana!") return end
+
+ -- Verify failure(second parameter is optional)
+ if m.fail then
+ __current_magic_power_fail = m.fail_fct
+ if (magic_power_sucess(get_magic_power(m.spells, sn), m.stat, "__current_magic_power_fail") == FALSE) then return end
+ else
+ if (magic_power_sucess(get_magic_power(m.spells, sn), m.stat) == FALSE) then return end
+ end
+
+ -- Actually cast the spells
+ m.spell[sn]()
+
+ -- use up some mana
+ increase_mana(-get_magic_power(m.spells, sn).mana_cost)
+end
+
+-- Get the level of a power
+function get_level_power(s, max, min)
+ if not max then max = 50 end
+ if not min then min = 1 end
+
+ return value_scale(s.get_current_level(), 50, max, min)
+end
diff --git a/lib/core/quests.lua b/lib/core/quests.lua
new file mode 100644
index 00000000..dfe9db51
--- /dev/null
+++ b/lib/core/quests.lua
@@ -0,0 +1,57 @@
+-- Quest helper files
+
+-- Quest structs
+
+__quest_hook = {}
+__quest_callbacks = {}
+__quest_callbacks_max = 0
+__quest_dynamic_desc = {}
+
+function add_quest(q)
+ local i, index, d, z, qq
+
+ assert(q.global, "No quest global name")
+ assert(q.name, "No quest name")
+ assert(q.desc, "No quest desc")
+ assert(q.level, "No quest level")
+ assert(q.hooks, "No quest hooks")
+
+ i = new_quest(q.name);
+ setglobal(q.global, i)
+
+ -- Make it save & load
+ add_loadsave("quest("..q.global..").status", QUEST_STATUS_UNTAKEN)
+
+ if type(q.desc) == "table" then
+ z = 0
+ for index, d in q.desc do
+ quest_desc(i, z, d);
+ z = z + 1
+ end
+ else
+ __quest_dynamic_desc[i] = q.desc
+ quest(i).dynamic_desc = TRUE
+ end
+ quest(i).level = q.level
+ if not q.silent then
+ quest(i).silent = FALSE
+ else
+ quest(i).silent = q.silent
+ end
+ __quest_hook[i] = q.hooks
+ for index, d in q.hooks do
+ add_hook_script(index, "__lua__quest_callback"..__quest_callbacks_max, "__lua__quest_callback"..__quest_callbacks_max)
+ setglobal("__lua__quest_callback"..__quest_callbacks_max, d)
+ __quest_callbacks_max = __quest_callbacks_max + 1
+ end
+ if q.data then
+ for index, d in q.data do
+ -- Besure it exists
+ setglobal(index, d)
+
+ -- Make it save & load
+ add_loadsave(index, d)
+ end
+ end
+ return i
+end
diff --git a/lib/core/s_aux.lua b/lib/core/s_aux.lua
new file mode 100644
index 00000000..abd1269d
--- /dev/null
+++ b/lib/core/s_aux.lua
@@ -0,0 +1,742 @@
+-- Functions to help with spells, do not touch
+
+__schools = {}
+__schools_num = 0
+
+__tmp_spells = {}
+__tmp_spells_num = 0
+
+function add_school(s)
+ __schools[__schools_num] = s
+
+ __schools_num = __schools_num + 1
+ return (__schools_num - 1)
+end
+
+function finish_school(i)
+ local s
+
+ s = __schools[i]
+ assert(s.name, "No school name!")
+ assert(s.skill, "No school skill!")
+
+ -- Need hooks?
+ if s.hooks then
+ add_hooks(s.hooks)
+ end
+
+ new_school(i, s.name, s.skill)
+end
+
+function add_spell(s)
+ __tmp_spells[__tmp_spells_num] = s
+
+ __tmp_spells_num = __tmp_spells_num + 1
+ return (__tmp_spells_num - 1)
+end
+
+function finish_spell(must_i)
+ local i, s
+
+ s = __tmp_spells[must_i]
+ assert(s.name, "No spell name!")
+ assert(s.school, "No spell school!")
+ assert(s.level, "No spell level!")
+ assert(s.mana, "No spell mana!")
+ if not s.mana_max then s.mana_max = s.mana end
+ assert(s.fail, "No spell failure rate!")
+ assert(s.spell, "No spell function!")
+ if not s.info then s.info = function() return "" end end
+ assert(s.desc, "No spell desc!")
+ if not s.random then s.random = SKILL_MAGIC end
+ if s.lasting then
+ assert(type(s.lasting) == "function", "Spell lasting is not function")
+ end
+ if s.stick then
+ local k, e
+ for k, e in s.stick do
+ if type(k) == "table" then
+ assert(e.base_level, "Arg no stick base level")
+ assert(e.max_level, "Arg no stick max level")
+ end
+ end
+ end
+
+ i = new_spell(must_i, s.name)
+ assert(i == must_i, "ACK ! i != must_i ! please contact the maintainer")
+ if type(s.school) == "number" then __spell_school[i] = {s.school}
+ else __spell_school[i] = s.school end
+ spell(i).mana = s.mana
+ spell(i).mana_max = s.mana_max
+ spell(i).fail = s.fail
+ spell(i).skill_level = s.level
+ __spell_spell[i] = s.spell
+ __spell_info[i] = s.info
+ __spell_desc[i] = s.desc
+ return i
+end
+
+-- Creates the school books array
+__spell_spell = {}
+__spell_info = {}
+__spell_desc = {}
+__spell_school = {}
+school_book = {}
+
+-- Find a spell by name
+function find_spell(name)
+ local i
+
+ i = 0
+ while (i < __tmp_spells_num) do
+ if __tmp_spells[i].name == name then return i end
+ i = i + 1
+ end
+ return -1
+end
+
+-- Find if the school is under the influence of a god, returns nil or the level
+function get_god_level(sch)
+ if __schools[sch].gods[player.pgod] then
+ return (s_info[__schools[sch].gods[player.pgod].skill + 1].value * __schools[sch].gods[player.pgod].mul) / __schools[sch].gods[player.pgod].div
+ else
+ return nil
+ end
+end
+
+-- Change this fct if I want to switch to learnable spells
+function get_level_school(s, max, min)
+ local lvl, sch, index, num, bonus
+ local allow_spell_power = TRUE
+
+ lvl = 0
+ num = 0
+ bonus = 0
+
+ -- No max specified ? assume 50
+ if not max then
+ max = 50
+ end
+ if not min then
+ min = 1
+ end
+
+ -- Do we pass tests?
+ if __tmp_spells[s].depend then
+ if __tmp_spells[s].depend() ~= TRUE then
+ return min, "n/a"
+ end
+ end
+
+ for index, sch in __spell_school[s] do
+ local r, s, p, ok = 0, 0, 0, 0
+
+ -- Does it require we worship a specific god?
+ if __schools[sch].god then
+ if __schools[sch].god ~= player.pgod then
+ if min then return min, "n/a"
+ else return 1, "n/a" end
+ end
+ end
+
+ -- Take the basic skill value
+ r = s_info[(school(sch).skill) + 1].value
+
+ -- Do we pass tests?
+ if __schools[sch].depend then
+ if __schools[sch].depend() ~= TRUE then
+ return min, "n/a"
+ end
+ end
+
+ -- Are we under sorcery effect ?
+ if __schools[sch].sorcery then
+ s = s_info[SKILL_SORCERY + 1].value
+ end
+
+ -- Are we affected by spell power ?
+ -- All teh schools must allow it for it to work
+ if not __schools[sch].spell_power then
+ allow_spell_power = nil
+ end
+
+ -- Are we under a god effect ?
+ if __schools[sch].gods then
+ p = get_god_level(sch)
+ if not p then p = 0 end
+ end
+
+ -- Find the higher
+ ok = r
+ if ok < s then ok = s end
+ if ok < p then ok = p end
+
+ -- Do we need to add a special bonus ?
+ if __schools[sch].bonus_level then
+ bonus = bonus + (__schools[sch].bonus_level() * (SKILL_STEP / 10))
+ end
+
+ -- All schools must be non zero to be able to use it
+ if ok == 0 then return min, "n/a" end
+
+ -- Apply it
+ lvl = lvl + ok
+ num = num + 1
+ end
+
+ -- Add the Spellpower skill as a bonus
+ if allow_spell_power then
+ bonus = bonus + (get_skill_scale(SKILL_SPELL, 20) * (SKILL_STEP / 10))
+ end
+
+ -- Add bonus from objects
+ bonus = bonus + (player.to_s * (SKILL_STEP / 10))
+
+ -- / 10 because otherwise we can overflow a s32b and we can use a u32b because the value can be negative
+ -- The loss of information should be negligible since 1 skill = 1000 internally
+ lvl = (lvl / num) / 10
+ lvl = lua_get_level(s, lvl, max, min, bonus)
+
+ return lvl, nil
+end
+
+-- This is the function to use when casting through a stick
+function get_level_device(s, max, min)
+ local lvl
+
+ -- No max specified ? assume 50
+ if not max then
+ max = 50
+ end
+
+ lvl = s_info[SKILL_DEVICE + 1].value
+ lvl = lvl + (get_level_use_stick * SKILL_STEP)
+
+ -- Sticks are limited
+ if lvl - ((spell(s).skill_level + 1) * SKILL_STEP) >= get_level_max_stick * SKILL_STEP then
+ lvl = (get_level_max_stick + spell(s).skill_level - 1) * SKILL_STEP
+ end
+
+ -- / 10 because otherwise we can overflow a s32b and we can use a u32b because the value can be negative
+ -- The loss of information should be negligible since 1 skill = 1000 internally
+ lvl = lvl / 10
+ if not min then
+ lvl = lua_get_level(s, lvl, max, 1, 0)
+ else
+ lvl = lua_get_level(s, lvl, max, min, 0)
+ end
+
+ return lvl
+end
+
+-- The real get_level, works for schooled magic and for innate powers
+get_level_use_stick = -1
+get_level_max_stick = -1
+function get_level(s, max, min)
+ if type(s) == "number" then
+ -- Ahah shall we use Magic device instead ?
+ if get_level_use_stick > -1 then
+ return get_level_device(s, max, min)
+ else
+ local lvl, na = get_level_school(s, max, min)
+ return lvl
+ end
+ else
+ return get_level_power(s, max, min)
+ end
+end
+
+-- Can we cast the spell ?
+function is_ok_spell(s, obj)
+ if get_level(s, 50, 0) == 0 then return nil end
+ if __tmp_spells[s].pval and obj.pval < __tmp_spells[s].pval then return nil end
+ return 1
+end
+
+-- Get the amount of mana(or power) needed
+function get_mana(s)
+ return spell(s).mana + get_level(s, spell(s).mana_max - spell(s).mana, 0)
+end
+
+-- Return the amount of power(mana, piety, whatever) for the spell
+function get_power(s)
+ if check_affect(s, "piety", FALSE) then
+ return player.grace
+ else
+ return player.csp
+ end
+end
+
+-- Return the amount of power(mana, piety, whatever) for the spell
+function get_power_name(s)
+ if check_affect(s, "piety", FALSE) then
+ return "piety"
+ else
+ return "mana"
+ end
+end
+
+-- Changes the amount of power(mana, piety, whatever) for the spell
+function adjust_power(s, x)
+ if check_affect(s, "piety", FALSE) then
+ inc_piety(GOD_ALL, x)
+ else
+ increase_mana(x)
+ end
+end
+
+-- Print the book and the spells
+function print_book(book, spl, obj)
+ local x, y, index, sch, size, s
+
+ x = 0
+ y = 2
+ size = 0
+
+ -- Hack if the book is 255 it is a random book
+ if book == 255 then
+ school_book[book] = {spl}
+ end
+
+ -- Parse all spells
+ for index, s in school_book[book] do
+ local color = TERM_L_DARK
+ local lvl, na = get_level_school(s, 50, -50)
+ local xx, sch_str
+
+ if is_ok_spell(s, obj) then
+ if get_mana(s) > get_power(s) then color = TERM_ORANGE
+ else color = TERM_L_GREEN end
+ end
+
+ xx = nil
+ sch_str = ""
+ for index, sch in __spell_school[s] do
+ if xx then
+ sch_str = sch_str.."/"..school(sch).name
+ else
+ xx = 1
+ sch_str = sch_str..school(sch).name
+ end
+ end
+
+ if na then
+ c_prt(color, format("%c) %-20s%-16s %3s %4s %3d%s %s", size + strbyte("a"), spell(s).name, sch_str, na, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x)
+ else
+ c_prt(color, format("%c) %-20s%-16s %3d %4s %3d%s %s", size + strbyte("a"), spell(s).name, sch_str, lvl, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x)
+ end
+ y = y + 1
+ size = size + 1
+ end
+ prt(format(" %-20s%-16s Level Cost Fail Info", "Name", "School"), 1, x)
+ return y
+end
+
+-- Output the describtion when it is used as a spell
+function print_spell_desc(s, y)
+ local index, desc, x
+
+ x = 0
+
+ if type(__spell_desc[s]) == "string" then c_prt(TERM_L_BLUE, __spell_desc[s], y, x)
+ else
+ for index, desc in __spell_desc[s] do
+ c_prt(TERM_L_BLUE, desc, y, x)
+ y = y + 1
+ end
+ end
+ if check_affect(s, "piety", FALSE) then
+ c_prt(TERM_L_WHITE, "It uses piety to cast.", y, x)
+ y = y + 1
+ end
+ if not check_affect(s, "blind") then
+ c_prt(TERM_ORANGE, "It is castable even while blinded.", y, x)
+ y = y + 1
+ end
+ if not check_affect(s, "confusion") then
+ c_prt(TERM_ORANGE, "It is castable even while confused.", y, x)
+ y = y + 1
+ end
+end
+
+-- Output the desc when sued as a device
+function print_device_desc(s)
+ local index, desc
+
+ if type(__spell_desc[s]) == "string" then text_out(__spell_desc[s])
+ else
+ for index, desc in __spell_desc[s] do
+ text_out("\n" .. desc)
+ end
+ end
+end
+
+function book_spells_num(book)
+ local size, index, sch
+
+ size = 0
+
+ -- Hack if the book is 255 it is a random book
+ if book == 255 then
+ return 1
+ end
+
+ -- Parse all spells
+ for index, s in school_book[book] do
+ size = size + 1
+ end
+ return size
+end
+
+function spell_x(book, spl, s)
+ if book == 255 then
+ return spl
+ else
+ local i, x, val
+
+ i, val = next(school_book[book], nil)
+ x = 0
+ while x < s do
+ i, val = next(school_book[book], i)
+ x = x + 1
+ end
+ return val
+ end
+end
+
+function spell_in_book(book, spell)
+ local i, s
+
+ for i, s in school_book[book] do
+ if s == spell then return TRUE end
+ end
+ return FALSE
+end
+
+-- Returns spell chance of failure for spell
+function spell_chance(s)
+ local chance, s_ptr
+
+ s_ptr = spell(s)
+
+ -- Extract the base spell failure rate
+ if get_level_use_stick > -1 then
+ chance = lua_spell_device_chance(s_ptr.fail, get_level(s, 50), s_ptr.skill_level)
+ else
+ chance = lua_spell_chance(s_ptr.fail, get_level(s, 50), s_ptr.skill_level, get_mana(s), get_power(s), get_spell_stat(s))
+ end
+
+ -- Return the chance
+ return chance
+end
+
+function check_affect(s, name, default)
+ local s_ptr = __tmp_spells[s]
+ local a
+
+ if type(s_ptr[name]) == "number" then
+ a = s_ptr[name]
+ else
+ a = default
+ end
+ if a == FALSE then
+ return nil
+ else
+ return TRUE
+ end
+end
+
+-- Returns the stat to use for the spell, INT by default
+function get_spell_stat(s)
+ if not __tmp_spells[s].stat then return A_INT
+ else return __tmp_spells[s].stat end
+end
+
+function cast_school_spell(s, s_ptr, no_cost)
+ local use = FALSE
+
+ -- No magic
+ if (player.antimagic > 0) then
+ msg_print("Your anti-magic field disrupts any magic attempts.")
+ return
+ end
+
+ -- No magic
+ if (player.anti_magic == TRUE) then
+ msg_print("Your anti-magic shell disrupts any magic attempts.")
+ return
+ end
+
+ -- if it costs something then some condition must be met
+ if not no_cost then
+ -- Require lite
+ if (check_affect(s, "blind")) and ((player.blind > 0) or (no_lite() == TRUE)) then
+ msg_print("You cannot see!")
+ return
+ end
+
+ -- Not when confused
+ if (check_affect(s, "confusion")) and (player.confused > 0) then
+ msg_print("You are too confused!")
+ return
+ end
+
+ -- Enough mana
+ if (get_mana(s) > get_power(s)) then
+ if (get_check("You do not have enough "..get_power_name(s)..", do you want to try anyway?") == FALSE) then return end
+ end
+
+ -- Invoke the spell effect
+ if (magik(spell_chance(s)) == FALSE) then
+ if (__spell_spell[s]() ~= nil) then
+ use = TRUE
+ end
+ else
+ local index, sch
+
+ -- added because this is *extremely* important --pelpel
+ if (flush_failure) then flush() end
+
+ msg_print("You failed to get the spell off!")
+ for index, sch in __spell_school[s] do
+ if __schools[sch].fail then
+ __schools[sch].fail(spell_chance(s))
+ end
+ end
+ use = TRUE
+ end
+ else
+ __spell_spell[s]()
+ end
+
+ if use == TRUE then
+ -- Reduce mana
+ adjust_power(s, -get_mana(s))
+
+ -- Take a turn
+ if is_magestaff() == TRUE then energy_use = 80
+ else energy_use = 100 end
+ end
+
+ player.redraw = bor(player.redraw, PR_MANA)
+ player.window = bor(player.window, PW_PLAYER)
+end
+
+
+-- Helper functions
+HAVE_ARTIFACT = 0
+HAVE_OBJECT = 1
+HAVE_EGO = 2
+function have_object(mode, type, find, find2)
+ local o, i, min, max
+
+ max = 0
+ min = 0
+ if band(mode, USE_EQUIP) == USE_EQUIP then
+ min = INVEN_WIELD
+ max = INVEN_TOTAL
+ end
+ if band(mode, USE_INVEN) == USE_INVEN then
+ min = 0
+ if max == 0 then max = INVEN_WIELD end
+ end
+
+ i = min
+ while i < max do
+ o = get_object(i)
+ if o.k_idx ~= 0 then
+ if type == HAVE_ARTIFACT then
+ if find == o.name1 then return i end
+ end
+ if type == HAVE_OBJECT then
+ if find2 == nil then
+ if find == o.k_idx then return i end
+ else
+ if (find == o.tval) and (find2 == o.sval) then return i end
+ end
+ end
+ if type == HAVE_EGO then
+ if (find == o.name2) or (find == o.name2b) then return i end
+ end
+ end
+ i = i + 1
+ end
+ return -1
+end
+
+-- Can the spell be randomly found(in random books)
+function can_spell_random(i)
+ return __tmp_spells[i].random
+end
+
+-- Find a random spell
+function get_random_spell(typ, level)
+ local spl, tries
+
+ tries = 1000
+ while tries > 0 do
+ tries = tries - 1
+ spl = rand_int(__tmp_spells_num)
+ if (can_spell_random(spl) == typ) and (rand_int(spell(spl).skill_level * 3) < level) then
+ break
+ end
+ end
+ if tries > 0 then
+ return spl
+ else
+ return -1
+ end
+end
+
+-- Execute a lasting spell
+function exec_lasting_spell(spl)
+ assert(__tmp_spells[spl].lasting, "No lasting effect for spell "..__tmp_spells[spl].name.." but called as such")
+ return __tmp_spells[spl].lasting()
+end
+
+-- Helper function for spell effect to know if they are or not obvious
+function is_obvious(effect, old)
+ if old then
+ if old == TRUE or effect == TRUE then
+ return TRUE
+ else
+ return FALSE
+ end
+ else
+ return effect
+ end
+end
+
+-------------------------Sticks-------------------------
+
+-- Fire off the spell
+function activate_stick(spl)
+ local ret = __spell_spell[spl]()
+ local charge, obvious
+ if not ret then
+ charge = FALSE
+ obvious = FALSE
+ else
+ charge = TRUE
+ obvious = ret
+ end
+ return obvious, charge
+end
+
+----------------------------------- Wand, Staves, Rods specific functions ----------------------------
+
+-- Get a spell for a given stick(wand, staff, rod)
+function get_random_stick(stick, level)
+ local spl, tries
+
+ tries = 1000
+ while tries > 0 do
+ tries = tries - 1
+ spl = rand_int(__tmp_spells_num)
+ if __tmp_spells[spl].stick and (type(__tmp_spells[spl].stick[stick]) == "table") then
+ if (rand_int(spell(spl).skill_level * 3) < level) and (magik(100 - __tmp_spells[spl].stick[stick].rarity) == TRUE) then
+ break
+ end
+ end
+ end
+ if tries > 0 then
+ return spl
+ else
+ return -1
+ end
+end
+
+-- Get a random base level
+function get_stick_base_level(stick, level, spl)
+ -- Paranoia
+ if spl < 0 or spl >= __tmp_spells_num or not __tmp_spells[spl].stick[stick] then return 0 end
+
+ local min, max = __tmp_spells[spl].stick[stick].base_level[1], __tmp_spells[spl].stick[stick].base_level[2]
+ local range = max - min;
+
+ -- Ok the basic idea is to have a max possible level of half the dungeon level
+ if range * 2 > level then range = level / 2 end
+
+ -- Randomize a bit
+ range = m_bonus(range, dun_level)
+
+ -- And get the result
+ return min + range
+end
+
+-- Get a random max level
+function get_stick_max_level(stick, level, spl)
+ -- Paranoia
+ if spl < 0 or spl >= __tmp_spells_num or not __tmp_spells[spl].stick[stick] then return 0 end
+
+ local min, max = __tmp_spells[spl].stick[stick].max_level[1], __tmp_spells[spl].stick[stick].max_level[2]
+ local range = max - min;
+
+ -- Ok the basic idea is to have a max possible level of half the dungeon level
+ if range * 2 > level then range = level / 2 end
+
+ -- Randomize a bit
+ range = m_bonus(range, dun_level)
+
+ -- And get the result
+ return min + range
+end
+
+-- Get the number of desired charges
+function get_stick_charges(spl)
+ return __tmp_spells[spl].stick.charge[1] + randint(__tmp_spells[spl].stick.charge[2]);
+end
+
+-- Get activation desc
+function get_activation_desc(spl)
+ local turns
+ if type(__tmp_spells[spl].activate) == 'number' then
+ turns = __tmp_spells[spl].activate
+ else
+ turns = __tmp_spells[spl].activate[1] .. '+d' .. __tmp_spells[spl].activate[2]
+ end
+ return __tmp_spells[spl].desc[1] .. ' every ' .. turns .. ' turns'
+end
+
+-- Compute the timeout of an activation
+function get_activation_timeout(spl)
+ if type(__tmp_spells[spl].activate) == 'number' then
+ return __tmp_spells[spl].activate
+ else
+ return __tmp_spells[spl].activate[1] + randint(__tmp_spells[spl].activate[2])
+ end
+end
+
+-- Fire off the spell
+function activate_activation(spl, item)
+ __spell_spell[spl](item)
+end
+
+
+------- Add new GF type ----------
+max_gf = MAX_GF
+function add_spell_type(t)
+ t.index = max_gf
+ max_gf = max_gf + 1
+ assert(t.color, "No GF color")
+ if not t.monster then t.monster = function() end end
+ if not t.angry then t.angry = function() end end
+ if not t.object then t.object = function() end end
+ if not t.player then t.player = function() end end
+ if not t.grid then t.grid = function() end end
+
+ add_hooks
+ {
+ [HOOK_GF_COLOR] = function (gf, new_gfx)
+ local t = %t
+ if gf == t.index then return TRUE, t.color[new_gfx + 1] end
+ end,
+ [HOOK_GF_EXEC] = function (action, who, gf, dam, rad, y, x, extra)
+ local t = %t
+ if t.index == gf then
+ return t[action](who, dam, rad, y, x, extra)
+ end
+ end,
+ }
+ return t.index
+end
diff --git a/lib/core/stores.lua b/lib/core/stores.lua
new file mode 100644
index 00000000..d4a63168
--- /dev/null
+++ b/lib/core/stores.lua
@@ -0,0 +1,32 @@
+-- Take care of all concerning stores
+function store_buy_list(t)
+ assert(type(t) == "table", "store_buy_list got no table")
+ add_hooks
+ {
+ [HOOK_STORE_BUY] = function (index, name, obj)
+ local tbl = %t
+ local elt = tbl[index]
+ if not elt then
+ elt = tbl[name]
+ end
+ if elt then
+ if elt then
+ if type(elt) == "function" then
+ return TRUE, elt(obj)
+ elseif type(elt) == "table" then
+ local k, e
+ for k, e in elt do
+ if type(e) == "number" then
+ if obj.tval == e then return TRUE, TRUE end
+ else
+ if (obj.tval == e[1]) and (obj.sval >= e[2]) and (obj.sval <= e[3]) then return TRUE, TRUE end
+ end
+ end
+ elseif elt == -1 then
+ return TRUE, FALSE
+ end
+ end
+ end
+ end,
+ }
+end
diff --git a/lib/core/util.lua b/lib/core/util.lua
new file mode 100644
index 00000000..997f1eba
--- /dev/null
+++ b/lib/core/util.lua
@@ -0,0 +1,257 @@
+-- various stuff to make scripters life easier
+
+-- Beware of the scary undefined globals
+function safe_getglobal(x)
+ local v = rawget(globals(), x)
+
+ if v then
+ return v
+ else
+ error("undefined global variable '"..x.."'")
+ end
+end
+
+function set_safe_globals()
+ settagmethod(tag(nil), "getglobal", safe_getglobal)
+end
+function unset_safe_globals()
+ settagmethod(tag(nil), "getglobal", nil)
+end
+
+set_safe_globals()
+
+-- Patch modules
+__patch_modules = {}
+
+function patch_version(name, version)
+ assert(not __patch_modules[name], "Patch " .. name .. " already loaded!!!")
+ __patch_modules[name] = version
+end
+
+function patchs_list()
+ local k, e, first
+ first = FALSE
+ for k, e in __patch_modules do
+ if first == FALSE then print_hook("\n\n [Patch modules]\n") first = TRUE end
+ print_hook("\n "..k.." version "..e)
+ end
+ if first == TRUE then print_hook("\n") end
+end
+
+function patchs_display()
+ local k, e
+ for k, e in __patch_modules do
+ msg_print("Patch: "..k.." version "..e)
+ end
+end
+
+
+-- Better hook interface
+__hooks_list_callback = {}
+__hooks_list_callback_max = 0
+
+function add_hooks(h_table, name_prefix)
+ local k, e
+
+ if not name_prefix then name_prefix = "" end
+ for k, e in h_table do
+ add_hook_script(k, "__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max, "__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max)
+ setglobal("__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max, e)
+ __hooks_list_callback_max = __hooks_list_callback_max + 1
+ end
+end
+
+-- Wrapper for the real msg_print and cmsg_print
+-- it understands if we want color or not
+function msg_print(c, m)
+ if type(c) == "number" then
+ cmsg_print(c, m)
+ else
+ call(%msg_print, { c })
+ end
+end
+
+-- Returns the direction of the compass that y2, x2 is from y, x
+-- the return value will be one of the following: north, south,
+-- east, west, north-east, south-east, south-west, north-west,
+-- or "close" if it is within 2 tiles.
+function compass(y, x, y2, x2)
+ local y_axis, x_axis, y_diff, x_diff, compass_dir
+
+ -- is it close to the north/south meridian?
+ y_diff = y2 - y
+
+ -- determine if y2, x2 is to the north or south of y, x
+ if (y_diff > -3) and (y_diff < 3) then
+ y_axis = nil
+ elseif y2 > y then
+ y_axis = "south"
+ else
+ y_axis = "north"
+ end
+
+ -- is it close to the east/west meridian?
+ x_diff = x2 - x
+
+ -- determine if y2, x2 is to the east or west of y, x
+ if (x_diff > -3) and (x_diff < 3) then
+ x_axis = nil
+ elseif x2 > x then
+ x_axis = "east"
+ else
+ x_axis = "west"
+ end
+
+ -- Maybe it is very close
+ if ((not x_axis) and (not y_axis)) then compass_dir = "close"
+ -- Maybe it is (almost) due N/S
+ elseif not x_axis then compass_dir = y_axis
+ -- Maybe it is (almost) due E/W
+ elseif not y_axis then compass_dir = x_axis
+ -- or if it is neither
+ else compass_dir = y_axis.."-"..x_axis
+ end
+
+ return compass_dir
+end
+
+-- Returns a relative approximation of the 'distance' of y2, x2 from y, x.
+function approximate_distance(y, x, y2, x2)
+ local y_diff, x_diff, most_dist
+
+ -- how far to away to the north/south
+ y_diff = y2 - y
+
+ -- make sure it's a positive integer
+ if y_diff < 0 then
+ y_diff = 0 - y_diff
+ end
+
+ -- how far to away to the east/west
+ x_diff = x2 - x
+
+ -- make sure it's a positive integer
+ if x_diff < 0 then
+ x_diff = 0 - x_diff
+ end
+
+ -- find which one is the larger distance
+ if x_diff > y_diff then
+ most_dist = x_diff
+ else
+ most_dist = y_diff
+ end
+
+ -- how far away then?
+ if most_dist >= 41 then
+ how_far = "a very long way"
+ elseif most_dist >= 25 then
+ how_far = "a long way"
+ elseif most_dist >= 8 then
+ how_far = "quite some way"
+ else
+ how_far = "not very far"
+ end
+
+ return how_far
+
+end
+
+-- better timer add function
+__timers_callback_max = 0
+
+function new_timer(t)
+ assert(t.delay > 0, "no timer delay")
+ assert(t.enabled, "no timer enabled state")
+ assert(t.callback, "no timer callback")
+
+ local timer
+ if type(t.callback) == "function" then
+ setglobal("__timers_callback_"..__timers_callback_max, t.callback)
+ timer = %new_timer("__timers_callback_"..__timers_callback_max, t.delay)
+ __timers_callback_max = __timers_callback_max + 1
+ else
+ timer = %new_timer(t.callback, t.delay)
+ end
+
+ timer.enabled = t.enabled
+
+ return timer
+end
+
+-- saves all timer values
+function save_timer(name)
+ add_loadsave(name..".enabled", FALSE)
+ add_loadsave(name..".delay", 1)
+ add_loadsave(name..".countdown", 1)
+end
+
+
+-- displays a scrolling list
+function display_list(y, x, h, w, title, list, begin, sel, sel_color)
+ local l = create_list(getn(list))
+
+ for i = 1, getn(list) do
+ add_to_list(l, i - 1, list[i])
+ end
+
+ %display_list(y, x, h, w, title, l, getn(list), begin - 1, sel - 1, sel_color)
+
+ delete_list(l, getn(list))
+end
+
+-- Easier access to special gene stuff
+function set_monster_generation(monster, state)
+ if type(monster) == "string" then
+ m_allow_special[test_monster_name(monster) + 1] = state
+ else
+ m_allow_special[monster + 1] = state
+ end
+end
+function set_object_generation(obj, state)
+ if type(obj) == "string" then
+ m_allow_special[test_item_name(obj) + 1] = state
+ else
+ m_allow_special[obj + 1] = state
+ end
+end
+function set_artifact_generation(obj, state)
+ m_allow_special[obj + 1] = state
+end
+
+-- Strings
+function strcap(str)
+ if strlen(str) > 1 then
+ return strupper(strsub(str, 1, 1))..strsub(str, 2)
+ elseif strlen(str) == 1 then
+ return strupper(str)
+ else
+ return str
+ end
+end
+
+function msg_format(...)
+ msg_print(call(format, arg))
+end
+
+-- Stacks
+function stack_push(stack, val)
+ tinsert(stack, val)
+end
+function stack_pop(stack)
+ if getn(stack) >= 1 then
+ return tremove(stack)
+ else
+ error("Tried to unstack an empty stack")
+ return nil
+ end
+end
+
+-- A way to check if the game is now running(as opposed to initialization/character gen)
+game = {}
+add_hooks
+{
+ [HOOK_GAME_START] = function ()
+ game.started = TRUE
+ end
+}
diff --git a/lib/core/xml.lua b/lib/core/xml.lua
new file mode 100644
index 00000000..f1188d51
--- /dev/null
+++ b/lib/core/xml.lua
@@ -0,0 +1,347 @@
+-- The xml module
+xml = {}
+
+function xml:parseargs (s)
+ local arg = {}
+ gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
+ %arg[w] = a
+ end)
+ return arg
+end
+
+-- s is a xml stream, returns a table
+function xml:collect (s)
+ local stack = {n=0}
+ local top = {n=0}
+ tinsert(stack, top)
+ local ni,c,label,args, empty
+ local i, j = 1, 1
+ while 1 do
+ ni,j,c,label,args, empty = strfind(s, "<(%/?)(%w+)(.-)(%/?)>", j)
+ if not ni then break end
+ local text = strsub(s, i, ni-1)
+ if not strfind(text, "^%s*$") then
+ tinsert(top, text)
+ end
+ if empty == "/" then -- empty element tag
+ tinsert(top, {n=0, label=label, args=xml:parseargs(args), empty=1})
+ elseif c == "" then -- start tag
+ top = {n=0, label=label, args=xml:parseargs(args)}
+ tinsert(stack, top) -- new level
+ else -- end tag
+ local toclose = tremove(stack) -- remove top
+ top = stack[stack.n]
+ if stack.n < 1 then
+ error("nothing to close with "..label)
+ end
+ if toclose.label ~= label then
+ error("trying to close "..toclose.label.." with "..label)
+ end
+ tinsert(top, toclose)
+ end
+ i = j+1
+ end
+ local text = strsub(s, i)
+ if not strfind(text, "^%s*$") then
+ tinsert(stack[stack.n], text)
+ end
+ if stack.n > 1 then
+ error("unclosed "..stack[stack.n].label)
+ end
+ return stack[1]
+end
+
+-- Viewport coordinates
+xml.write_out_y = 0
+xml.write_out_x = 0
+xml.write_out_h = 24
+xml.write_out_w = 80
+
+-- Offsets
+xml.write_off_y = 0
+xml.write_off_x = 0
+
+-- Current position
+xml.write_y = 0
+xml.write_x = 0
+
+xml.write_screen = function(color, s)
+ local i
+ for i = 1, strlen(s) do
+ local c = strsub(s, i, i + 1)
+ if c ~= "\n" then
+ if xml.write_y - xml.write_off_y >= 0 and xml.write_y - xml.write_off_y < xml.write_out_h and xml.write_x - xml.write_off_x >= 0 and xml.write_x - xml.write_off_x < xml.write_out_w then
+ Term_putch(xml.write_x - xml.write_off_x + xml.write_out_x, xml.write_y - xml.write_off_y + xml.write_out_y, color, strbyte(c))
+ end
+ xml.write_x = xml.write_x + 1
+ else
+ xml.write_x = 0
+ xml.write_y = xml.write_y + 1
+ end
+ end
+end
+
+xml.write_file = function (color, s)
+ print_hook(s)
+end
+
+xml.write = xml.write_screen
+
+xml.rule2string = {
+ ['name'] = {"Its ", "name", " is"},
+ ['contain'] = {"Its ", "name", " contains"},
+ ['symbol'] = {"Its ", "symbol", " is"},
+ ['inscribed'] = {"Its ", "inscription", " contains"},
+ ['state'] = {"Its ", "state", " is"},
+ ['status'] = {"Its ", "status", " is"},
+ ['tval'] = {"Its ", "tval", " is"},
+ ['race'] = {"Your ", "race", " is"},
+ ['subrace'] = {"Your ", "subrace", " is"},
+ ['class'] = {"Your ", "class", " is"},
+ ['foo1'] = {"The result of ", "test 1 ", "is"},
+ ['foo2'] = {"The result of ", "test 2 ", "is"},
+ ['foo3'] = {"The result of ", "test 3 ", "is"},
+}
+
+xml.display_english = 1
+function xml:display_xml(t, tab)
+ if xml.display_english then
+ xml:english_xml(t, tab)
+ else
+ xml:print_xml(t, tab)
+ end
+end
+
+function xml:english_xml(t, tab, not_flag)
+ local i, k, e
+ local pre, post, recurse
+ local children_not_flag
+ local nextlevel
+ local bcol, ecol = TERM_L_GREEN, TERM_GREEN
+
+ if xml.write_active and t == auto_aux.rule then bcol, ecol = TERM_VIOLET, TERM_VIOLET end
+
+ nextlevel = tab .. " "
+
+ recurse = 1
+
+ if t.label == "rule" then
+ if t.args.type == "inscribe" then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "A rule named \"")
+ xml.write(TERM_WHITE, tostring(t.args.name))
+ xml.write(ecol, "\" to ")
+ xml.write(bcol, "inscribe")
+ xml.write(ecol, " an item with \"")
+ xml.write(TERM_WHITE, t.args.inscription)
+ xml.write(ecol, "\" when")
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "A rule named \"")
+ xml.write(TERM_WHITE, tostring(t.args.name))
+ xml.write(ecol, "\" to ")
+ xml.write(bcol, t.args.type)
+ xml.write(ecol, " when")
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "and" then
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "At least one of the following is false:")
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "All of the following are true:")
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "or" then
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "All of the following are false:")
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "At least one of the following are true:")
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "not" then
+ if bcol == TERM_VIOLET or getn(t) == 0 then
+ xml.write(ecol, "(a negating rule)")
+ xml.write(TERM_WHITE, "\n")
+ else
+ nextlevel = tab
+ end
+ children_not_flag = not nil
+ elseif t.label == "comment" then
+ xml.write(TERM_WHITE, tab)
+ xml.write(TERM_WHITE, "(" .. t[1] .. ")")
+ xml.write(TERM_WHITE, "\n")
+ elseif t.label == "skill" then
+ local s = t[1]
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Your skill in ")
+ xml.write(bcol, s)
+ xml.write(ecol, " is not from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Your skill in ")
+ xml.write(bcol, s)
+ xml.write(ecol, " is from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "ability" then
+ local s = t[1]
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "You do not have the ")
+ xml.write(bcol, s)
+ xml.write(ecol, " ability")
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "You have the ")
+ xml.write(bcol, s)
+ xml.write(ecol, " ability")
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "level" then
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Your ")
+ xml.write(bcol, "level")
+ xml.write(ecol, " is not from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Your ")
+ xml.write(bcol, "level")
+ xml.write(ecol, " is from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "sval" then
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Its ")
+ xml.write(bcol, "sval")
+ xml.write(ecol, " is not from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Its ")
+ xml.write(bcol, "sval")
+ xml.write(ecol, " is from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ end
+ elseif t.label == "discount" then
+ if not_flag then
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Its ")
+ xml.write(bcol, "discount")
+ xml.write(ecol, " is not from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ else
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, "Its ")
+ xml.write(bcol, "discount")
+ xml.write(ecol, " is from ")
+ xml.write(TERM_WHITE, tostring(t.args.min))
+ xml.write(ecol, " to ")
+ xml.write(TERM_WHITE, tostring(t.args.max))
+ xml.write(TERM_WHITE, "\n")
+ end
+ else
+ if xml.rule2string[t.label] then
+ local rule = xml.rule2string[t.label]
+ a, b, c = rule[1], rule[2], rule[3]
+ if not_flag then c = c .. " not" end
+ xml.write(TERM_WHITE, tab)
+ xml.write(ecol, a)
+ xml.write(bcol, b)
+ xml.write(ecol, c)
+ xml.write(ecol, " \"")
+ xml.write(TERM_WHITE, t[1])
+ xml.write(ecol, "\"")
+ xml.write(TERM_WHITE, "\n")
+ else
+ if not_flag then
+ xml.write(bcol, "Not:\n")
+ tab = tab .. " "
+ xml:print_xml(t, tab)
+ return
+ end
+ end
+ end
+
+ for i = 1, getn(t) do
+ if type(t[i]) == "string" then
+ -- xml.write(TERM_WHITE, t[i].."\n")
+ else
+ xml:english_xml(t[i], nextlevel, children_not_flag)
+ end
+ end
+end
+
+function xml:print_xml(t, tab)
+ local i, k, e
+ local inside = nil
+ local bcol, ecol = TERM_L_GREEN, TERM_GREEN
+
+ if xml.write_active and t == auto_aux.rule then bcol, ecol = TERM_VIOLET, TERM_VIOLET end
+
+ xml.write(bcol, tab.."<"..t.label)
+ for k, e in t.args do
+ xml.write(TERM_L_BLUE, " "..k)
+ xml.write(TERM_WHITE, "=\"")
+ xml.write(TERM_YELLOW, e)
+ xml.write(TERM_WHITE, "\"")
+ end
+ xml.write(bcol, ">")
+
+ for i = 1, getn(t) do
+ if type(t[i]) == "string" then
+ xml.write(TERM_WHITE, t[i])
+ else
+ if not inside then xml.write(TERM_WHITE, "\n") end
+ inside = not nil
+ xml:print_xml(t[i], tab.." ")
+ end
+ end
+
+ if not inside then
+ xml.write(ecol, "</"..t.label..">\n")
+ else
+ xml.write(ecol, tab.."</"..t.label..">\n")
+ end
+end
+
+-- t is a table representing xml, outputs the xml code via xml.write()
+function xml:output(t)
+ local i
+ for i = 1, getn(t) do
+ xml:print_xml(t[i], "")
+ end
+end
diff --git a/lib/data/delete.me b/lib/data/delete.me
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/lib/data/delete.me
diff --git a/lib/dngn/dun1.14 b/lib/dngn/dun1.14
new file mode 100644
index 00000000..6421e80b
--- /dev/null
+++ b/lib/dngn/dun1.14
@@ -0,0 +1,2 @@
+# On this level there is a stairway leading to the Heart of the Earth
+B:10
diff --git a/lib/dngn/dun10.0 b/lib/dngn/dun10.0
new file mode 100644
index 00000000..b89ce05e
--- /dev/null
+++ b/lib/dngn/dun10.0
@@ -0,0 +1,3 @@
+# Father branch is Mirkwood(1), on level 14
+A:1
+L:14
diff --git a/lib/dngn/dun11.20 b/lib/dngn/dun11.20
new file mode 100644
index 00000000..9cc611d3
--- /dev/null
+++ b/lib/dngn/dun11.20
@@ -0,0 +1,2 @@
+# On this level there is a stairway leading to the Nether Realm
+B:6
diff --git a/lib/dngn/dun11.22 b/lib/dngn/dun11.22
new file mode 100644
index 00000000..149e2c33
--- /dev/null
+++ b/lib/dngn/dun11.22
@@ -0,0 +1,2 @@
+#I'm downright evil
+F:NO_GENO |
diff --git a/lib/dngn/dun17.15 b/lib/dngn/dun17.15
new file mode 100644
index 00000000..d08bf5e7
--- /dev/null
+++ b/lib/dngn/dun17.15
@@ -0,0 +1,5 @@
+N:Machine
+U:s_factory.map
+D:The clatter of strange machinery surrounds you.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
diff --git a/lib/dngn/dun18.0 b/lib/dngn/dun18.0
new file mode 100644
index 00000000..da1b6c27
--- /dev/null
+++ b/lib/dngn/dun18.0
@@ -0,0 +1,2 @@
+# The level is SAVED in the playername.mz? file
+#S:mz0
diff --git a/lib/dngn/dun18.1 b/lib/dngn/dun18.1
new file mode 100644
index 00000000..70f27718
--- /dev/null
+++ b/lib/dngn/dun18.1
@@ -0,0 +1,2 @@
+# The level is SAVED in the playername.mz? file
+#S:mz1
diff --git a/lib/dngn/dun19.11 b/lib/dngn/dun19.11
new file mode 100644
index 00000000..7fba690d
--- /dev/null
+++ b/lib/dngn/dun19.11
@@ -0,0 +1,5 @@
+N:Deathwatch
+U:s_death.map
+D:This level looks filled with evilness.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
diff --git a/lib/dngn/dun2.31 b/lib/dngn/dun2.31
new file mode 100644
index 00000000..dd8669a5
--- /dev/null
+++ b/lib/dngn/dun2.31
@@ -0,0 +1,2 @@
+# On this level there is a stairway leading to the Mount Doom
+B:5
diff --git a/lib/dngn/dun22.10 b/lib/dngn/dun22.10
new file mode 100644
index 00000000..e7eb116e
--- /dev/null
+++ b/lib/dngn/dun22.10
@@ -0,0 +1,2 @@
+# On this level there is a stairway leading to the Small Water Cave
+B:24
diff --git a/lib/dngn/dun22.5 b/lib/dngn/dun22.5
new file mode 100644
index 00000000..11d8e51f
--- /dev/null
+++ b/lib/dngn/dun22.5
@@ -0,0 +1,5 @@
+N:Orc Town
+U:s_orc.map
+D:You hear orc warcries.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
diff --git a/lib/dngn/dun24.0 b/lib/dngn/dun24.0
new file mode 100644
index 00000000..bbb93f85
--- /dev/null
+++ b/lib/dngn/dun24.0
@@ -0,0 +1,3 @@
+# Father branch is the Moria(22), on level 10
+A:22
+L:10
diff --git a/lib/dngn/dun29.15 b/lib/dngn/dun29.15
new file mode 100644
index 00000000..4df873b5
--- /dev/null
+++ b/lib/dngn/dun29.15
@@ -0,0 +1,6 @@
+N:Galleon
+U:s_ship.map
+D:A ship of antique design lies jammed in the ice here.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
+
diff --git a/lib/dngn/dun3.18 b/lib/dngn/dun3.18
new file mode 100644
index 00000000..84c0a74a
--- /dev/null
+++ b/lib/dngn/dun3.18
@@ -0,0 +1,5 @@
+N:Dim Gates
+U:s_gates.map
+D:Visions of death fill your mind.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
diff --git a/lib/dngn/dun3.28 b/lib/dngn/dun3.28
new file mode 100644
index 00000000..0acd4193
--- /dev/null
+++ b/lib/dngn/dun3.28
@@ -0,0 +1,5 @@
+N:Nameless
+U:s_name.map
+D:You sense a powerful artifact here.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
diff --git a/lib/dngn/dun3.3 b/lib/dngn/dun3.3
new file mode 100644
index 00000000..710ef5f8
--- /dev/null
+++ b/lib/dngn/dun3.3
@@ -0,0 +1,5 @@
+N:Crypt
+U:s_crypt.map
+D:Looks like a forgotten crypt...
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT
diff --git a/lib/dngn/dun5.0 b/lib/dngn/dun5.0
new file mode 100644
index 00000000..48a7bea6
--- /dev/null
+++ b/lib/dngn/dun5.0
@@ -0,0 +1,3 @@
+# Father branch is the Mordor, on level 32
+A:2
+L:31
diff --git a/lib/dngn/dun5.14 b/lib/dngn/dun5.14
new file mode 100644
index 00000000..3d7a3080
--- /dev/null
+++ b/lib/dngn/dun5.14
@@ -0,0 +1,14 @@
+# The level is SAVED in the playername.mdm file
+S:mdm
+
+# Use the map in s_doom.map file
+U:s_doom.map
+
+# Use Mt Doom as level name
+N:Mt Doom
+
+D:You finally reach the top of Mount Doom, here must lie the Great Fire.
+F:DESC
+F:NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:NO_TELEPORT
+
diff --git a/lib/dngn/dun6.0 b/lib/dngn/dun6.0
new file mode 100644
index 00000000..c750ea67
--- /dev/null
+++ b/lib/dngn/dun6.0
@@ -0,0 +1,3 @@
+# Father branch is Void(11), on level 20
+A:11
+L:20
diff --git a/lib/edit/a_info.txt b/lib/edit/a_info.txt
new file mode 100644
index 00000000..427d5060
--- /dev/null
+++ b/lib/edit/a_info.txt
@@ -0,0 +1,2889 @@
+# File: a_info.txt
+
+
+# This file is used to initialize the "lib/raw/a_info.raw" file, which is
+# used to initialize the "artifact" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/raw/a_info.raw" file.
+
+
+# The artifact indexes are defined in "defines.h", and must not be changed.
+
+# Artifacts 1-15 are "special", 16-63 are "armor", and 64-127 are "weapons".
+
+# Hack -- "Grond" and "Morgoth" MUST have a rarity of one, or they might
+# not be dropped when Morgoth is killed. Note that they, like the "special"
+# artifacts, are never created "accidentally".
+
+# Artifacts now have descriptions. Special thanks to J.R.R Tolkien,
+# without whom the words would be unwritten, the images unconceived,
+# the deed undone.
+# -Leon Marrick
+# Contributors: Jeff Butler, Neal Hackler, Ethan Sicotte, Pat Tracy, Divia
+
+# Version stamp (required)
+
+V:2.0.0
+
+
+
+# The Phial of Galadriel
+
+N:1:of Galadriel
+I:39:100:4
+W:20:10:10:10000
+P:0:1d1:0:0:0
+F:ACTIVATE | SEARCH | LITE3 | LUCK
+F:INSTA_ART | HIDE_TYPE
+a:HARDCORE=LIGHT
+D:A small crystal phial, with the light of Earendil's Star contained inside.
+D:Its light is imperishable, and near it darkness cannot endure.
+
+
+# The Star of Elendil
+
+N:2:of Elendil
+I:39:101:1
+W:30:25:5:32500
+P:0:1d1:0:0:0
+F:ACTIVATE | SEE_INVIS | HOLD_LIFE |
+F:INSTA_ART | SPEED | LITE3 | LITE1 | HIDE_TYPE
+a:HARDCORE=MAP_LIGHT
+Z:detect curses
+D:The shining Star of the West, a famed heirloom of Elendil's house.
+
+
+# The Arkenstone of Thrain
+# Was +2 WIS/DEX
+
+N:3:of Thrain
+I:39:102:3
+W:50:50:5:50000
+P:0:1d1:0:0:0
+F:ACTIVATE | SEE_INVIS | HOLD_LIFE | RES_CHAOS | HIDE_TYPE | LUCK
+F:INSTA_ART | SPEED | RES_LITE | RES_DARK | ESP_ORC | LITE3
+a:HARDCORE=THRAIN
+D:A great globe seemingly filled with moonlight, the famed Heart of the
+D:Mountain, which splinters the light that falls upon it into a thousand
+D:glowing shards.
+
+
+# The Amulet of Carlammas
+
+N:4:of Carlammas
+I:40:10:2
+W:50:10:3:60000
+F:CON | HIDE_TYPE |
+F:ACTIVATE | RES_FIRE |
+F:INSTA_ART
+a:HARDCORE=PROT_EVIL
+D:A fiery circle of bronze, with mighty spells to ward off evil.
+
+
+# The Amulet of Ingwe
+
+N:5:of Ingwe
+I:40:11:3
+W:65:30:3:90000
+F:INT | WIS | CHR | SEARCH | INFRA | HIDE_TYPE |
+F:SEE_INVIS | FREE_ACT | ACTIVATE |
+F:RES_ACID | RES_COLD | RES_ELEC |
+F:INSTA_ART
+a:HARDCORE=DISP_EVIL
+D:The ancient heirloom of Ingwe, high lord of the Vanyar, against whom nothing
+D:of evil could stand.
+
+
+# The Necklace 'Nauglamir'
+
+N:6:'Nauglamir'
+I:40:12:3
+W:70:50:3:75000
+F:STR | CON | DEX | INFRA | HIDE_TYPE | RES_FEAR |
+F:SEE_INVIS | FREE_ACT | REGEN | LITE3 | SPEED |
+F:INSTA_ART
+D:A carencet of gold, set with a multitude of shining gems of
+D:Valinor. Despite its size, its weight seems as that of gossamer.
+
+
+# The Ring of Flare
+# (note: pval reduced to 3, otherwise it would probably be preferable
+# even to The One Ring!)
+
+N:7:of Flare
+I:45:52:3
+W:50:35:2:75000
+F:STR | CON | CHR | HIDE_TYPE |
+F:IM_FIRE | ACTIVATE | SEARCH |
+F:ESP_THUNDERLORD | SEE_INVIS | FLY |
+F:INSTA_ART
+a:HARDCORE=DIM_DOOR
+Z:swap position
+D:The mighty ring of the Thunderlord Flare that makes the wearer
+D:strong and healthy. Once a ring of power, it was
+D:given to Flare by Celegorm when he arrived on Middle-earth with a full nest
+D:of Thunderlords.
+
+
+# The Ring of Barahir
+
+N:8:of Barahir
+I:45:32:1
+W:50:25:2:75000
+F:STR | INT | WIS | DEX | CON | CHR | STEALTH | HIDE_TYPE |
+F:RES_POIS | RES_DARK | ACTIVATE | SEE_INVIS | SEARCH |
+F:INSTA_ART
+a:HARDCORE=BARAHIR
+D:A ring shaped into twinned serpents with eyes of emerald meeting beneath
+D:a crown of flowers, an ancient treasure of Isildur's house.
+
+
+# The Ring of Tulkas
+
+N:9:of Tulkas
+I:45:33:4
+W:70:50:2:175000
+F:STR | DEX | CON | HIDE_TYPE |
+F:ACTIVATE | SPEED | ESP_EVIL |
+F:INSTA_ART
+a:HARDCORE=TULKAS
+D:The treasure of Tulkas, most fleet and wrathful of the Valar.
+
+
+# The Ring of Power 'Narya'
+
+N:10:of Power 'Narya'
+I:45:34:1
+W:70:30:2:100000
+P:0:1d1:6:6:0
+F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | LUCK
+F:ACTIVATE | FREE_ACT | SEE_INVIS |
+F:SUST_STR | SUST_CON | SUST_WIS | SUST_CHR | SPECIAL_GENE |
+F:IM_FIRE | RES_NETHER | RES_FEAR | REGEN |
+F:INSTA_ART
+a:HARDCORE=NARYA
+D:The Ring of Fire, set with a ruby that glows like flame. Narya is one
+D:of the three Rings of Power created by the Elves and hidden by them from
+D:Sauron.
+
+
+# The Ring of Power 'Nenya'
+
+N:11:of Power 'Nenya'
+I:45:35:2
+W:80:40:2:200000
+P:0:1d1:9:9:0
+F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | LUCK
+F:ACTIVATE | HOLD_LIFE | FREE_ACT | SEE_INVIS |
+F:SUST_INT | SUST_WIS | SUST_CHR |
+F:IM_COLD | RES_BLIND | STEALTH | ESP_ALL |
+F:INSTA_ART
+a:HARDCORE=NENYA
+D:The Ring of Adamant, with a pure white stone as centrepiece. Nenya is one
+D:of the three Rings of Power created by the Elves and hidden by them from
+D:Sauron.
+
+
+# The Ring of Power 'Vilya'
+
+N:12:of Power 'Vilya'
+I:45:36:3
+W:90:50:2:300000
+P:0:1d1:12:12:0
+F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE | LUCK
+F:ACTIVATE | HOLD_LIFE | FREE_ACT | SEE_INVIS |
+F:FEATHER | SLOW_DIGEST | REGEN |
+F:SUST_STR | SUST_DEX | SUST_CON |
+F:IM_ELEC | RES_POIS | RES_DISEN |
+F:INSTA_ART
+a:HARDCORE=VILYA
+D:The Ring of Sapphire, with clear blue gems that shine like stars,
+D:glittering untouchable despite all that Sauron ever wrought. Vilya is
+D:one of the three Rings of Power created by the Elves and hidden by them
+D:from Sauron.
+
+
+# The Ring of Power 'The One Ring'
+
+N:13:of Power 'The One Ring'
+I:45:37:5
+W:100:100:2:5000000
+P:0:1d1:15:15:0
+F:STR | INT | WIS | DEX | CON | CHR | SPEED | HIDE_TYPE |
+F:ACTIVATE | AUTO_CURSE | HEAVY_CURSE | INVIS | SPELL | MANA |
+F:SEE_INVIS | REGEN | FREE_ACT | CURSED | CURSE_NO_DROP |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | PERMA_CURSE |
+F:SUST_STR | SUST_DEX | SUST_CON |
+F:SUST_INT | SUST_WIS | SUST_CHR |
+F:RES_BLIND | RES_POIS | RES_DISEN | RES_NETHER | ESP_ALL |
+F:DRAIN_MANA | DRAIN_HP | DRAIN_EXP |
+F:INSTA_ART
+a:HARDCORE=POWER
+Z:change the world
+D:"Ash nazg durbatuluk, ash nazg gimbatul, ash nazg thrakatuluk agh
+D:burzum-ishi krimpatul". Unadorned, made of massive gold,
+D:set with runes in the foul speech of Mordor, with power so great that it
+D:inevitably twists and masters any earthly being who wears it.
+
+
+# The Anchor of Space-Time
+
+N:14:of Space-Time
+I:39:105:0
+W:30:12:15:50000
+P:0:1d1:0:0:0
+F:INSTA_ART | LITE1
+D:A powerful stone that provides a strong light for any who
+D:wields it. It is rumoured that it may even protect the wearer from
+D:the passing of time.
+
+
+# This artifact has a low artifact level to prevent it being worthless for
+# chars with poor magic skills.
+
+# The Stone of Lore
+
+N:15:of Lore
+I:39:106:0
+W:15:12:15:20000
+P:0:1d1:0:0:0
+F:ACTIVATE | SPECIAL_GENE | EASY_USE | LITE1 |
+F:INSTA_ART
+a:HARDCORE=STONE_LORE
+D:A great emerald that fills your mind with images of knowledge and dreadful
+D:understanding as you stare into its depths.
+
+
+# The Multi-Hued Dragon Scale Mail 'Razorback'
+
+N:16:'Razorback'
+I:38:6:0
+W:90:9:500:400000
+P:30:2d4:-4:0:25
+F:FREE_ACT | IM_ELEC | SPECIAL_GENE |
+F:RES_FIRE | RES_COLD | RES_POIS | RES_LITE | RES_DARK |
+F:LITE1 | SEE_INVIS | AGGRAVATE | ESP_DRAGON
+F:ACTIVATE
+a:HARDCORE=RAZORBACK
+D:A massive suit of heavy dragon scales deeply saturated with many colours.
+D:It throbs with angry energies, and you feel the raw elemental might of
+D:untamed Lightning as you put it on.
+
+
+# The Power Dragon Scale Mail of Eternity
+# The ULTIMATE armour
+
+N:17:of Eternity
+I:38:30:0
+W:100:16:600:500000
+P:50:2d4:-8:0:35
+F:HOLD_LIFE | REGEN | ESP_DRAGON |
+F:RES_ACID | RES_FIRE | RES_COLD | RES_ELEC | RES_POIS | FEATHER | FLY |
+F:RES_NETHER | RES_NEXUS | RES_CHAOS | RES_LITE | RES_DARK | ULTIMATE |
+F:RES_SHARDS | RES_SOUND | RES_DISEN | RES_BLIND | RES_CONF |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | SPECIAL_GENE
+F:ACTIVATE
+a:HARDCORE=BLADETURNER
+D:A suit of adamant, set with scales of every colour, surrounded in a nimbus
+D:of perfectly untramelled yet inextricably intermingled and utterly mastered
+D:powers elemental and ethereal.
+
+
+# The Spear of Melkor
+
+N:18:of Melkor
+I:22:2:-4
+W:65:45:200:100000
+P:0:4d6:12:24:0
+F:STEALTH | WIS | CURSED | HEAVY_CURSE | TY_CURSE | ESP_GOOD |
+F:DRAIN_MANA | DRAIN_HP |
+F:RES_DARK | RES_BLIND | RES_LITE | RES_NETHER | BRAND_POIS | RES_CONF
+D:The mighty spear used once by Melkor to slay the trees of Valinor.
+
+
+# The Adamantite Plate Mail 'Soulkeeper'
+
+N:19:'Soulkeeper'
+I:37:30:2
+W:75:9:420:300000
+P:40:2d4:-4:0:20
+F:CON |
+F:HOLD_LIFE | SUST_CON | ESP_UNDEAD | RES_CONF | RES_FEAR |
+F:RES_ACID | RES_COLD | RES_DARK | RES_NETHER | RES_NEXUS | RES_CHAOS |
+F:ACTIVATE
+a:HARDCORE=CURE_1000
+D:A suit of imperishable adamant, with unconquerable strength to endure evil
+D:and disruptive magics, that protects the life force of its wearer as
+D:nothing else can.
+
+
+# The Full Plate Armour of Isildur
+
+N:20:of Isildur
+I:37:15:1
+W:30:3:300:50000
+P:25:2d4:0:0:25
+F:CON |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:RES_SOUND | RES_CONF | RES_NEXUS
+D:A gleaming steel suit covering the wearer from neck to foot, with runes of
+D:warding and stability deeply engraved into its surface.
+
+
+# The Metal Brigandine Armour of the Rohirrim
+
+N:21:of the Rohirrim
+I:37:9:2
+W:30:3:200:30000
+P:19:1d4:0:0:15
+F:STR | DEX | SPEED | HIDE_TYPE | RES_FEAR |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_CONF | RES_SOUND
+D:A stiff suit of armour composed of small metal plates sewn to an
+D:inner layer of heavy canvas, and covered with a second layer of
+D:cloth. Within it is the spirit of Eorl the Young, matchless in combat.
+
+
+# The Mithril Chain Mail 'Belegennon'
+
+N:22:'Belegennon'
+I:37:20:4
+W:40:10:150:135000
+P:28:1d4:-1:0:20
+F:STEALTH | WIS | INT |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS |
+F:HOLD_LIFE | RES_DARK | RES_FEAR |
+F:SEE_INVIS |
+F:ACTIVATE
+a:HARDCORE=BELEGENNON
+D:This wondrous suit of fine-linked chain shimmers as though of pure silver.
+D:It stands untouched amidst the fury of the elements, and a power of
+D:concealment rests within.
+
+
+# The Mithril Plate Mail of Celeborn
+
+N:23:of Celeborn
+I:37:25:4
+W:40:3:250:150000
+P:35:2d4:-3:0:25
+F:STR | CHR | HIDE_TYPE | ESP_ORC
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_DARK |
+F:RES_DISEN | ACTIVATE
+a:HARDCORE=GENOCIDE
+D:A shimmering suit of true-silver, forged long ago by dwarven smiths of
+D:legend. It gleams with purest white as you gaze upon it, and mighty are
+D:its powers to protect and banish.
+
+
+# The Chain Mail of Arvedui
+
+N:24:of Arvedui
+I:37:4:2
+W:20:3:220:32000
+P:14:1d4:-2:0:15
+F:STR | CHR | HIDE_TYPE |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_SHARDS | RES_NEXUS
+D:A hauberk, leggings, and sleeves of interlocking steel rings, well padded
+D:with leather. You feel strong and tall as Arvedui, last king of Arnor,
+D:as you put it on.
+
+
+# The Augmented Chain Mail of Caspanion
+
+N:25:of Caspanion
+I:37:6:3
+W:25:9:270:40000
+P:16:1d4:-2:0:20
+F:INT | WIS | CON | HIDE_TYPE |
+F:RES_ACID | RES_POIS | RES_CONF | ACTIVATE
+a:HARDCORE=DEST_DOOR
+D:A hauberk, leggings, and sleeves of interlocking steel rings, strategically
+D:reinforced at vital locations with a second layer of chain. Magics to
+D:enhance body and mind lie within, and no door can hope to resist the wearer.
+
+
+# The Thunderlord Coat of Marda
+
+N:26:of Marda
+I:36:16:5
+W:70:3:80:80000
+P:9:0d0:0:0:25
+F:FREE_ACT | RES_BLIND | RES_CONF | RES_FEAR | DRAIN_MANA |
+F:REFLECT | RES_NEXUS | SH_FIRE | SUST_INT | SUST_CON | SUST_CHR |
+F:ESP_THUNDERLORD | CON | CHR | INT |
+F:RES_ACID | RES_ELEC | IM_COLD | RES_COLD | AGGRAVATE | HEAVY_CURSE |
+F:CURSED
+D:The flying suit of Marda, very powerful armour that protects
+D:the wearer from cold. Wonderful as this mighty
+D:armour is, beware wearing it, for it has been cursed by a
+D:powerful wizard since Marda had it and is said to have gained
+D:a couple of undesirable side-effects...
+
+
+# The Thunderlord Coat of Trone
+
+N:27:of Trone
+I:36:16:4
+W:30:3:80:65000
+P:9:0d0:0:0:20
+F:REFLECT | RES_NEXUS | SH_FIRE | FLY | SPECIAL_GENE |
+F:STEALTH | ESP_THUNDERLORD | CON | INT | SPEED |
+F:RES_ACID | RES_ELEC | IM_FIRE | RES_COLD
+D:The flying suit of Trone. It protects the user from fire and
+D:imps are said to be less annoying with this on.
+
+
+# The Leather Scale Mail 'Thalkettoth'
+
+N:28:'Thalkettoth'
+I:36:11:3
+W:20:3:60:25000
+P:11:1d1:-1:0:25
+F:DEX | SPEED | HIDE_TYPE | SPECIAL_GENE |
+F:RES_ACID | RES_SHARDS
+D:A tunic and skirt sewn with thick, overlapping scales of hardened
+D:leather whose wearer moves with agility and assurance.
+
+
+# The Pair of Soft Leather Boots of Wormtongue
+
+N:29:of Wormtongue
+I:30:2:3
+W:40:20:20:50000
+P:2:1d1:-10:-10:10
+F:INT | DEX | CHR | STEALTH | SEARCH | SPEED | HIDE_TYPE |
+F:FREE_ACT | FEATHER | RES_DARK | RES_LITE | ESP_GOOD | ESP_UNIQUE
+Z:panic hit
+D:The pair of boots used by Grima son of Galmod, also named the Wormtongue:
+D:a treacherous but persuasive counsellor, ever ready to betray, sneak,
+D:lie, cheat and steal - but never ready to actually fight.
+
+
+# The Small Metal Shield of Thorin
+
+N:30:of Thorin
+I:34:3:4
+W:30:6:65:60000
+P:3:1d2:0:0:25
+F:STR | CON | HIDE_TYPE |
+F:FREE_ACT | IM_ACID | RES_SOUND |
+F:RES_CHAOS | ESP_ORC
+D:Invoking the strength and endurance of Thorin, King under the Mountain,
+D:this little metal shield is proof against the Element of Earth.
+
+
+# The Large Leather Shield of Celegorm
+
+N:31:of Celegorm
+I:34:4:0
+W:30:3:60:12000
+P:4:1d2:0:0:20
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_LITE | RES_DARK
+D:This shield emblazoned with a multitude of creatures not seen for ages
+D:once protected Celegorm, lord of Himlad; around it lies a mystic balance
+D:that contains the conflicts of the elements.
+
+
+# The Large Metal Shield of Anarion
+
+N:32:of Anarion
+I:34:5:0
+W:40:9:120:160000
+P:5:1d3:0:0:20
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | SUST_STR | SUST_INT |
+F:SUST_WIS | SUST_DEX | SUST_CON | SUST_CHR | ESP_EVIL
+D:The great metal-bound shield of Anarion, son of Elendil, who Sauron found
+D:himself powerless to wither or diminish.
+
+
+# The Beaked Axe of Hurin
+
+N:33:of Hurin
+I:22:10:3
+W:20:15:180:90000
+P:0:2d6:12:20:0
+F:STR | CON | HIDE_TYPE | BRAND_ACID | RES_ACID | LITE1 | DRAIN_MANA |
+F:SLAY_ORC | KILL_DEMON | SLAY_TROLL | ACTIVATE | SHOW_MODS
+F:MUST2H
+f:MUST2H
+a:HARDCORE=HURIN
+D:Wielded by Hurin Thalion in the Fifth Battle of Beleriand, this
+D:troll-bane smoked in the black blood of Gothmog's guards.
+
+
+# The Massive Iron Crown of Morgoth
+
+N:34:of Morgoth
+I:33:50:125
+W:100:1:20:10000000
+P:0:1d1:0:0:0
+F:STR | INT | WIS | DEX | CON | CHR | INFRA | HIDE_TYPE |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS |
+F:RES_LITE | RES_DARK | RES_CONF | RES_NEXUS | RES_NETHER |
+F:LITE1 | SEE_INVIS | ESP_ALL |
+F:CURSED | HEAVY_CURSE | PERMA_CURSE |
+F:INSTA_ART | SPECIAL_GENE
+D:Two Silmarils of Feanor blaze from the thunderous crown of twisted
+D:iron. The corrupted metal feels at once as infernal as hellfire
+D:and as chilling as the Outer Darkness. One protrusion from the
+D:crown is abruptly ended where a third jewel might have shone.
+
+
+# The Iron Crown of Beruthiel
+
+N:35:of Beruthiel
+I:33:10:-5
+W:40:12:20:0
+P:0:1d1:0:0:20
+F:STR | DEX | CON | HIDE_TYPE |
+F:FREE_ACT | SEE_INVIS | ESP_ANIMAL | ESP_EVIL | ESP_NONLIVING | ESP_ALL |
+F:CURSED | AUTO_CURSE
+D:The midnight-hued steel circlet of the sorceress-queen Beruthiel, which
+D:grants extraordinary powers of sight and awareness at a terrible physical
+D:cost.
+
+
+# The Hard Leather Cap of Thranduil
+
+N:36:of Thranduil
+I:32:2:2
+W:20:2:15:50000
+P:2:0d0:0:0:10
+F:INT | WIS | HIDE_TYPE |
+F:RES_BLIND | ESP_ORC | ESP_EVIL | ESP_TROLL
+D:The hunting cap of King Thranduil, to whose ears come all the secrets of
+D:his forest domain.
+
+
+# The Metal Cap of Thengel
+
+N:37:of Thengel
+I:32:3:3
+W:10:2:20:22000
+P:3:1d1:0:0:12
+F:WIS | CHR | RES_CONF | HIDE_TYPE | LUCK
+D:A ridged helmet made of steel, and embossed with scenes of valour in fine-
+D:engraved silver. It grants the wearer nobility, clarity of thought and
+D:understanding.
+
+
+# The Steel Helm of Hammerhand
+
+N:38:of Hammerhand
+I:32:6:3
+W:20:2:60:45000
+P:6:1d3:0:0:20
+F:STR | DEX | CON | HIDE_TYPE | SPECIAL_GENE | RES_FEAR |
+F:SUST_STR | SUST_DEX | SUST_CON |
+F:RES_ACID | RES_NEXUS | RES_COLD | RES_DARK | SLOW_DIGEST |
+Z:berserk
+D:A great helm as steady as the heroes of the Westdike. Mighty were the
+D:blows of Helm, the Hammerhand!
+
+
+# The Dragon Helm of Dor-Lomin
+
+N:39:of Dor-Lomin
+I:32:7:4
+W:40:12:75:300000
+P:8:1d3:0:0:20
+F:STR | DEX | CON | HIDE_TYPE |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_LITE | RES_BLIND |
+F:LITE1 | SEE_INVIS | ESP_DRAGON | ESP_THUNDERLORD | ACTIVATE
+a:HARDCORE=GORLIM
+D:The legendary dragon helm of Turin Turambar, an object of dread to the
+D:servants of Morgoth.
+
+# The Iron Helm 'Holhenneth'
+
+N:40:'Holhenneth'
+I:32:5:2
+W:20:5:75:100000
+P:5:1d3:0:0:10
+F:INT | WIS | SEARCH | HIDE_TYPE |
+F:RES_BLIND | SEE_INVIS | ACTIVATE
+a:HARDCORE=DETECT_ALL
+D:A famous helm of forged iron granting extraordinary powers of mind and
+D:awareness.
+
+
+# The Iron Helm of Gorlim
+
+N:41:of Gorlim
+I:32:5:-5
+W:20:5:75:0
+P:5:1d3:25:25:10
+F:INT | WIS | SEARCH | HIDE_TYPE | SHOW_MODS |
+F:SEE_INVIS | NO_MAGIC | HEAVY_CURSE | TY_CURSE
+F:RES_DISEN | RES_FEAR | FREE_ACT | RES_ACID | RES_FIRE | RES_POIS |
+F:IM_COLD | ACTIVATE | DRAIN_HP |
+F:TELEPORT | CURSED
+a:HARDCORE=GORLIM
+D:A headpiece, gaudy and barbaric, that betrayed a warrior when he most
+D:needed succor.
+
+
+# The Golden Crown of Gondor
+
+N:42:of Gondor
+I:33:11:3
+W:40:40:30:125000
+P:0:1d1:0:0:15
+F:STR | WIS | CON | HIDE_TYPE | SPEED | RES_CONF | RES_SOUND |
+F:RES_COLD | RES_FIRE | RES_LITE | RES_BLIND | RES_ELEC | RES_CHAOS |
+F:LITE1 | SEE_INVIS | REGEN | ACTIVATE
+a:HARDCORE=CURE_700
+D:The shining winged circlet brought by Elendil from dying Numenor, emblem of
+D:Gondor through an age of the world.
+
+
+# The Jewel Encrusted Crown of Numenor
+# Stolen from Oangband
+
+N:43:of Numenor
+I:33:12:3
+W:60:30:40:50000
+P:0:1d1:0:0:18
+F:INT | DEX | CHR | SEARCH | SPEED | HIDE_TYPE |
+F:SEE_INVIS | FREE_ACT | RES_DARK | RES_BLIND |
+F:RES_SHARDS | RES_SOUND | RES_LITE | RES_COLD |
+F:LITE1 | ACTIVATE | DRAIN_MANA
+a:HARDCORE=NUMENOR
+D:A crown of massive gold, set with wondrous jewels of thought and warding,
+D:worn by the kings of ancient Numenor. Its wearer may go into battle
+D:always knowing what he faces - unless his own folly blinds him to the
+D:nature and magnitude of the task.
+
+
+# The Cloak 'Colluin'
+
+N:44:'Colluin'
+I:35:1:0
+W:5:45:10:40000
+P:1:0d0:0:0:20
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS | ACTIVATE | ESP_GOOD
+a:HARDCORE=COLLUIN
+D:A cape worn by a hero from Valinor, a land utterly beyond the strife
+D:of Elements.
+
+
+# The Cloak 'Holcolleth'
+
+N:45:'Holcolleth'
+I:35:1:2
+W:5:25:10:13000
+P:1:0d0:0:0:4
+F:INT | WIS | SPEED | STEALTH | HIDE_TYPE |
+F:RES_ACID | ACTIVATE
+a:HARDCORE=SLEEP
+D:This elven-grey mantle possesses great powers of tranquility and of
+D:concealment, and grants the wearer the knowledge and understanding of
+D:the Sindar.
+
+
+# The Cloak of Thingol
+
+N:46:of Thingol
+I:35:1:3
+W:10:50:10:35000
+P:1:0d0:0:0:18
+F:DEX | CHR | HIDE_TYPE |
+F:FREE_ACT | RES_ACID | RES_FIRE | RES_COLD | ACTIVATE
+a:HARDCORE=RECHARGE
+D:A sable-hued cloak, with glowing elven-runes to restore magic showing calm
+D:and clear as moonlight on still water.
+
+
+# The Cloak of Thorongil
+
+N:47:of Thorongil
+I:35:1:0
+W:5:10:10:8000
+P:1:0d0:0:0:10
+F:FREE_ACT | RES_ACID | SEE_INVIS | RES_FEAR
+D:A cloak of shimmering green and brown that grants sight beyond sight and
+D:shakes off holding magics, worn by Aragorn son of Arathorn in his youth
+D:as he adventured under the name of Thorongil.
+
+
+# The Cloak 'Colannon'
+
+N:48:'Colannon'
+I:35:1:3
+W:5:20:10:11000
+P:1:0d0:0:0:15
+F:STEALTH | SPEED | RES_NEXUS |
+F:RES_ACID | ACTIVATE
+a:HARDCORE=TELEPORT
+D:A crystal-blue cape of fine silk worn by a silent messenger of
+D:the forces of Law. Somehow, its wearer is always able to escape
+D:trouble.
+
+
+# The Shadow Cloak of Luthien
+
+N:49:of Luthien
+I:35:6:2
+W:40:40:5:55000
+P:6:0d0:0:0:20
+F:INT | WIS | CHR | HIDE_TYPE | SPEED | STEALTH | INVIS | LUCK
+F:RES_ACID | RES_FIRE | RES_COLD | SPECIAL_GENE | ACTIVATE | SPELL_CONTAIN | WIELD_CAST
+a:HARDCORE=REST_LIFE
+D:The opaque midnight folds, inset with a multitude of tiny diamonds, of
+D:this cloak swirl around you and you feel a hint, a fragment of the
+D:knowledge and power to restore that lay in Luthien, the most beautiful
+D:being that ever knew death.
+
+
+# The Shadow Cloak of Tuor
+
+N:50:of Tuor
+I:35:6:4
+W:40:40:5:35000
+P:6:0d0:0:0:12
+F:STEALTH | DEX | HIDE_TYPE | INVIS | WATER_BREATH
+F:FREE_ACT | IM_ACID | SEE_INVIS | CLIMB
+D:From the ruin of Gondolin did Tuor escape, through secret ways and travail,
+D:shielded by his cloak from a multitude of hostile eyes.
+
+
+# The Main Gauche of Azaghal, which wounded Glaurung
+N:51:of Azaghal
+I:23:5:0
+W:15:30:30:40000
+P:0:2d5:12:14:0
+F:KILL_DRAGON | IM_FIRE | ESP_DRAGON | RES_FEAR
+D:The weapon of Azaghal when he wounded Glaurung. It is deadly
+D:when fighting dragons and is said to make the breaths of fire
+D:completely harmless.
+
+
+# The Set of Leather Gloves 'Cambeleg'
+
+N:52:'Cambeleg'
+I:31:1:2
+W:10:6:5:36000
+P:1:0d0:8:8:15
+F:STR | CON | HIDE_TYPE |
+F:FREE_ACT | SHOW_MODS
+D:A hero's handgear that lends great prowess in battle.
+
+
+# The Set of Leather Gloves 'Cammithrim'
+
+N:53:'Cammithrim'
+I:31:1:0
+W:10:3:5:30000
+P:1:0d0:0:0:10
+F:FREE_ACT | RES_LITE | SUST_CON | LITE1 | ACTIVATE
+F:SPECIAL_GENE
+a:HARDCORE=BO_MISS_1
+D:These gloves glow so brightly as to light the way for their owner and cast
+D:magical bolts with great frequency.
+
+
+# The Set of Gauntlets 'Paurhach'
+
+N:54:'Paurhach'
+I:31:2:0
+W:10:5:25:15000
+P:2:1d1:0:0:15
+F:RES_FIRE | ACTIVATE | SPELL_CONTAIN | WIELD_CAST
+a:HARDCORE=BO_FIRE_1
+D:A fiery set of gauntlets that can even shoot fire from the user's
+D:hands.
+
+
+# The Set of Gauntlets 'Paurnimmen'
+
+N:55:'Paurnimmen'
+I:31:2:4
+W:10:5:25:33000
+P:2:1d1:0:0:15
+F:RES_COLD | ACTIVATE
+F:SUST_CON | CON | REGEN | SPELL_CONTAIN | WIELD_CAST
+a:HARDCORE=BO_COLD_1
+D:A set of handgear so icy as to be able to fire frost bolts.
+
+
+# The Set of Gauntlets 'Pauraegen'
+
+N:56:'Pauraegen'
+I:31:2:0
+W:10:5:25:11000
+P:2:1d1:0:0:15
+F:RES_ELEC | ACTIVATE | SPELL_CONTAIN | WIELD_CAST
+a:HARDCORE=BO_ELEC_1
+D:A set of handgear with sparks surrounding it, able to fire
+D:bolts of electricity.
+
+
+# The Set of Gauntlets 'Paurnen'
+
+N:57:'Paurnen'
+I:31:2:0
+W:10:5:25:12000
+P:2:1d1:0:0:15
+F:RES_ACID | ACTIVATE | SPELL_CONTAIN | WIELD_CAST
+a:HARDCORE=BO_ACID_1
+D:A set of handgear so corrosive that it may fire bolts of acid.
+
+
+# The Set of Gauntlets 'Camlost'
+
+N:58:'Camlost'
+I:31:2:-3
+W:10:20:25:0
+P:2:1d1:-11:-12:0
+F:STR | DEX | HIDE_TYPE | DRAIN_MANA |
+F:RES_POIS | IM_FIRE | IM_COLD | RES_DISEN | RES_NETHER | FREE_ACT |
+F:AGGRAVATE | SHOW_MODS | HEAVY_CURSE | TY_CURSE | TELEPORT | CURSED
+D:A pair of gauntlets that sap combat ability, named after the empty hand
+D:of Beren that once clasped a Silmaril.
+
+
+# The Set of Cesti of Fingolfin
+
+N:59:of Fingolfin
+I:31:5:4
+W:40:15:40:110000
+P:5:1d1:10:10:20
+F:DEX | HIDE_TYPE | LUCK
+F:FREE_ACT | RES_ACID | ACTIVATE | SHOW_MODS
+a:HARDCORE=BO_MISS_2
+Z:magic missile
+D:The hand-sheathing of Fingolfin, warrior-king of Elves and Men, who gave
+D:Morgoth seven mighty wounds and pain that will last forever.
+
+
+# The Pair of Hard Leather Boots of Feanor
+
+N:60:of Feanor
+I:30:3:15
+W:40:120:40:300000
+P:3:1d1:0:0:20
+F:SPEED | HIDE_TYPE |
+F:RES_NEXUS | ACTIVATE
+a:HARDCORE=SPEED
+D:This wondrous pair of leather boots once sped Feanor, creator of the
+D:Silmarils and the mightiest of the Eldar, along the Grinding Ice and to
+D:Middle-earth at last.
+
+
+# The Pair of Soft Leather Boots 'Dal-i-thalion'
+
+N:61:'Dal-i-thalion'
+I:30:2:5
+W:10:25:20:40000
+P:2:1d1:0:0:15
+F:DEX | HIDE_TYPE | CHR | SUST_CHR |
+F:ACTIVATE | FREE_ACT |
+F:RES_NETHER | RES_CHAOS | RES_CONF | SUST_CON
+a:HARDCORE=CURE_POISON
+D:A pair of high-laced shoes, strong against the powers of corruption and
+D:withering, that grant the wearer extraordinary agility.
+
+
+# The Pair of Metal Shod Boots of Thror
+
+N:62:of Thror
+I:30:6:3
+W:30:25:80:15000
+P:6:1d1:0:0:20
+F:STR | CON | HIDE_TYPE | SPEED | RES_FEAR | CLIMB
+D:Sturdy footwear of leather and steel as enduring as the long-suffering
+D:Dwarven King-in-exile who wore them. Of dwarven make, these boots will
+D:make their wearer completely at home in the mountains.
+
+
+# The Seeker Arrow of Bard
+# This is, of course, from my 'Artifact Ammo' thread and doesn't need much
+# explanation. It's rather nasty and deals 1000 or more damage on a
+# normal hit to a dragon. :) Doesn't put a dent in Sky Drakes and Power
+# Dragons, tho it's still good for helping to take down Smaug, Tiamat and
+# the other dragon uniques.
+# P+ changed name, rarity to 30
+
+N:63:of Bard
+I:17:2:0
+W:55:30:2:50000
+P:0:8d4:20:15:0
+F:SLAY_ANIMAL | SLAY_EVIL | SLAY_UNDEAD | SLAY_DEMON |
+F:SLAY_ORC | SLAY_TROLL | SLAY_GIANT | SLAY_DRAGON | KILL_DRAGON |
+F:BRAND_ACID | BRAND_ELEC | BRAND_FIRE | BRAND_COLD | BRAND_POIS
+D:Deadliest of arrows, imbued with elemental strength, this shaft is
+D:feared especially by the wyrmkin.
+
+
+# The Main Gauche of Maedhros
+
+N:64:of Maedhros
+I:23:5:3
+W:15:30:30:22500
+P:0:2d5:12:15:0
+F:INT | DEX | HIDE_TYPE | SPEED | SPECIAL_GENE
+F:SLAY_TROLL | SLAY_GIANT | FREE_ACT | SEE_INVIS | SHOW_MODS
+D:A short thrusting blade with a large guard worn by Maedhros the Tall,
+D:eldest son of Feanor, and wielded with his left hand after the loss of
+D:his right hand in the pits of Thangorodrim.
+
+
+# The Dagger 'Angrist'
+
+N:65:'Angrist'
+I:23:4:4
+W:20:80:12:125000
+P:0:2d4:10:15:5
+F:DEX | HIDE_TYPE | STEALTH | SEARCH | BRAND_POIS |
+F:SLAY_EVIL | SLAY_TROLL | SLAY_ORC | BRAND_ACID |
+F:FREE_ACT | RES_DARK | SUST_DEX | SEE_INVIS |
+F:SHOW_MODS
+D:Forged from meteoric iron, this long chopping dagger slices through
+D:ordinary metal as easily as its title, "Iron Cleaver", suggests.
+
+
+# The Dagger 'Narthanc'
+
+N:66:'Narthanc'
+I:23:4:0
+W:4:100:12:12000
+P:0:1d4:4:6:0
+F:BRAND_FIRE | RES_FIRE | ACTIVATE | SHOW_MODS | LITE1 | LEVELS
+a:HARDCORE=BO_FIRE_1
+D:A fiery dagger finely balanced for deadly throws.
+
+
+# The Dagger 'Nimthanc'
+
+N:67:'Nimthanc'
+I:23:4:0
+W:3:100:12:11000
+P:0:1d4:4:6:0
+F:BRAND_COLD | RES_COLD | ACTIVATE | SHOW_MODS | LEVELS
+a:HARDCORE=BO_COLD_1
+D:A frosty dagger finely balanced for deadly throws.
+
+
+# The Dagger 'Dethanc'
+
+N:68:'Dethanc'
+I:23:4:0
+W:5:100:12:13000
+P:0:1d4:4:6:0
+F:BRAND_ELEC | RES_ELEC | ACTIVATE | SHOW_MODS | LEVELS
+a:HARDCORE=BO_ELEC_1
+D:A dagger covered in sparks and finely balanced for deadly throws.
+
+
+# The Dagger of Rilia
+
+N:69:of Rilia
+I:23:4:0
+W:5:40:12:35000
+P:0:2d4:4:3:0
+F:SLAY_ORC | RES_POIS | RES_DISEN | ACTIVATE | SHOW_MODS | BRAND_POIS
+a:HARDCORE=BA_POIS_1
+D:A large stiletto dagger that glistens with odourless poison, to which the
+D:wearer seems oddly immune.
+
+
+# The Dagger 'Belangil'
+
+N:70:'Belangil'
+I:23:4:2
+W:10:40:12:50000
+P:0:2d4:6:9:0
+F:DEX | HIDE_TYPE | SPEED | BLOWS |
+F:BRAND_COLD | RES_COLD |
+F:SEE_INVIS | SLOW_DIGEST | REGEN |
+F:ACTIVATE | SHOW_MODS | BRAND_POIS
+a:HARDCORE=BELANGIL
+D:A frosty dagger surrounded in a nimbus of ice with a hilt of elk horn and
+D:an edge to wound the wind.
+
+
+# The Bastard Sword 'Calris'
+
+N:71:'Calris'
+I:23:21:5
+W:30:15:140:100000
+P:0:5d4:-20:20:0
+F:CON | HIDE_TYPE | DRAIN_HP |
+F:KILL_DRAGON | SLAY_EVIL | SLAY_DEMON | SLAY_TROLL | RES_DISEN |
+F:AGGRAVATE | CURSED | HEAVY_CURSE | SHOW_MODS | ESP_DRAGON | ESP_DEMON
+F:AUTO_CURSE
+F:COULD2H
+f:COULD2H
+D:This sword has runes of power incised on its ornate hilt and a single
+D:blood channel that gleams coldly blue as you grasp this mighty weapon of
+D:peril.
+
+
+# The Broad Sword 'Aranruth'
+
+N:72:'Aranruth'
+I:23:16:4
+W:20:45:150:125000
+P:0:3d5:20:12:0
+F:STR | DEX | CON | SUST_CON | SUST_STR
+F:REGEN | FREE_ACT | SEE_INVIS |
+F:RES_CHAOS | RES_NETHER | HOLD_LIFE | RES_FEAR |
+F:RES_COLD |
+F:SLAY_DEMON | SLAY_EVIL | SLAY_DRAGON | SLAY_UNDEAD |
+F:BRAND_COLD |
+F:SLOW_DIGEST | SHOW_MODS | HIDE_TYPE | BLESSED
+D:The beautiful sword of Thingol with a hilt of gold and silver inlay,
+D:glistening icily enough to freeze the hearts of demons. You feel supple
+D:and lightfooted as you hold it.
+
+
+# The Broad Sword 'Glamdring'
+
+N:73:'Glamdring'
+I:23:16:1
+W:20:20:150:40000
+P:0:2d5:10:15:0
+F:SEARCH | HIDE_TYPE | BLESSED | SLAY_DEMON |
+F:SLAY_EVIL | BRAND_FIRE | SLAY_ORC | RES_FIRE | RES_LITE | LITE1 |
+F:SLOW_DIGEST | SHOW_MODS | ESP_ORC | SPECIAL_GENE |
+D:This fiery, shining blade earned its sobriquet "Foe-Hammer" from dying orcs
+D:who dared to come near hidden Gondolin.
+
+
+# The Broad Sword 'Aeglin'
+
+N:74:'Aeglin'
+I:23:16:4
+W:20:90:150:95000
+P:0:2d5:12:16:0
+F:SEARCH | BLESSED | LITE1 | HIDE_TYPE |
+F:BRAND_ELEC | SLAY_ORC | SLAY_GIANT | SLAY_TROLL | RES_FEAR |
+F:RES_ELEC | RES_FIRE | RES_BLIND | ESP_ORC | ESP_GIANT | ESP_TROLL |
+F:SLOW_DIGEST | SHOW_MODS
+D:Like unto Orcrist and Glamdring, and like them long lost, this sword is
+D:continually coved in tiny arcs of captive lightning that flash and dance
+D:eerily in the globe of light they create.
+
+
+# The Broad Sword 'Orcrist'
+
+N:75:'Orcrist'
+I:23:16:3
+W:20:20:150:40000
+P:0:2d5:10:15:0
+F:SEARCH | ESP_ORC | SLAY_DRAGON | ESP_DRAGON | RES_COLD | HIDE_TYPE |
+F:SLAY_EVIL | BRAND_COLD | SLAY_ORC | RES_COLD | LITE1 | RES_DARK |
+F:SLOW_DIGEST | SHOW_MODS
+D:This coldly gleaming blade is called simply "Biter", by orcs who came to
+D:know its power all too well.
+
+
+# The Two-Handed Sword 'Gurthang'
+
+N:76:'Gurthang'
+I:23:25:2
+W:30:30:200:100000
+P:0:3d6:13:17:0
+F:STR | HIDE_TYPE | VORPAL | ESP_DRAGON | DRAIN_HP |
+F:RES_FIRE | RES_POIS | BRAND_FIRE | BRAND_POIS |
+F:KILL_DRAGON | SLAY_TROLL | FREE_ACT | SLOW_DIGEST | REGEN | SHOW_MODS
+F:MUST2H
+f:MUST2H
+D:A giant sword once wielded by mighty Turin, and a great dragonbane which
+D:bathed in Glaurung's blood: but beware, it will drink the blood of those
+D:who wield it eventually.
+
+
+# The Two-Handed Sword 'Zarcuthra'
+
+N:77:'Zarcuthra'
+I:23:25:4
+W:30:180:250:205000
+P:0:4d6:19:21:0
+F:STR | CHR | INFRA | HIDE_TYPE | VORPAL | DRAIN_MANA |
+F:KILL_DRAGON | SLAY_ANIMAL | SLAY_EVIL | BRAND_FIRE |
+F:SLAY_UNDEAD | SLAY_DEMON | SLAY_TROLL | SLAY_GIANT | SLAY_ORC |
+F:RES_FIRE | RES_CHAOS | FREE_ACT | SEE_INVIS | AGGRAVATE | SHOW_MODS
+F:MUST2H
+f:MUST2H
+D:Dark and deadly runes stand stark against the naked steel of this awesome
+D:weapon, and you feel a stunning power of slaying and rending as you
+D:slowly approach.
+
+
+# The Dark Sword 'Mormegil'
+
+N:78:'Mormegil'
+I:23:33:2
+W:30:15:250:0
+P:0:6d7:0:0:-20
+F:SPEED | IM_FIRE | RES_FIRE | BRAND_FIRE | RES_DISEN | RES_FEAR |
+F:AGGRAVATE | CURSED | HEAVY_CURSE | SHOW_MODS | LEVELS | TY_CURSE |
+F:BLOWS | SLAY_DRAGON | RES_CHAOS | ANTIMAGIC_50 |
+F:DRAIN_MANA | DRAIN_HP | DRAIN_EXP
+D:A foul, twisted sword with blackened spines and knobs, whose very name is a
+D:curse upon the lips of Elves and Men.
+
+
+# The Cutlass 'Gondricam'
+
+N:79:'Gondricam'
+I:23:12:3
+W:20:8:110:28000
+P:0:1d7:10:11:0
+F:DEX | STEALTH | HIDE_TYPE |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | FEATHER |
+F:SEE_INVIS | REGEN | SHOW_MODS
+D:Famed sea-defender of Lebennin. A short, slightly curved chopping blade
+D:with a perfect edge shining cleanly in the sun, an object of hate to the
+D:men of Umbar who met it in combat.
+
+
+# The Executioner's Sword 'Crisdurian'
+
+N:80:'Crisdurian'
+I:23:28:0
+W:40:15:260:111000
+P:0:4d5:18:19:0
+F:SLAY_DRAGON | SLAY_EVIL | SLAY_UNDEAD | SLAY_TROLL | SLAY_GIANT |
+F:SLAY_ORC | SEE_INVIS | SHOW_MODS | VORPAL | BRAND_POIS | WOUNDING
+F:MUST2H
+f:MUST2H
+D:A giant's weapon, with a long blade tall and straight thrusting out from a
+D:massive double-pronged hilt. On its blade are written doomspells against
+D:both the living and undead.
+
+
+# The Katana 'Aglarang'
+
+N:81:'Aglarang'
+I:23:20:5
+W:35:25:50:40000
+P:0:8d4:0:0:0
+F:DEX | TUNNEL | SPEED | HIDE_TYPE |
+F:SUST_DEX | SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:An utterly perfect, cleanly chiselled sword, with a edge that effortlessly
+D:slices rock and bone, and spells to render the wearer lithe and nimble. It
+D:is combat incarnate.
+
+
+# The Long Sword 'Ringil'
+
+N:82:'Ringil'
+I:23:17:10
+W:20:120:130:300000
+P:0:4d5:22:25:0
+F:SPEED | HIDE_TYPE | RES_FEAR | BLESSED |
+F:SLAY_EVIL | BRAND_COLD | SLAY_UNDEAD | KILL_DEMON | SLAY_TROLL |
+F:FREE_ACT | RES_COLD | RES_LITE | LITE1 | SEE_INVIS | SLOW_DIGEST | REGEN |
+F:ACTIVATE | SHOW_MODS
+a:HARDCORE=BA_COLD_2
+D:The weapon of Fingolfin, High King of the Noldor; it shines like a column
+D:of ice lit by light unquenchable. Morgoth came but unwillingly to meet it
+D:of old; his lame foot will remind him of its might should he meet it again.
+
+
+# The Long Sword 'Anduril'
+
+N:83:'Anduril'
+I:23:17:4
+W:20:40:130:100000
+P:0:3d5:10:15:5
+F:STR | DEX | HIDE_TYPE | RES_FEAR | FREE_ACT | BLESSED | LUCK
+F:SLAY_EVIL | BRAND_FIRE | SLAY_TROLL | SLAY_ORC | FREE_ACT |
+F:RES_FIRE | SUST_DEX | SEE_INVIS | ACTIVATE | SHOW_MODS | LITE1
+F:RES_DISEN | SPECIAL_GENE
+a:HARDCORE=BA_FIRE_1
+D:The famed "Flame of the West", the sword that was broken and is forged
+D:again. It glows with the essence of fire, its wearer is mighty in combat,
+D:and no creature of Sauron can withstand it. It will never be stained or
+D:broken even in defeat.
+
+
+# The Long Sword 'Anguirel'
+
+N:84:'Anguirel'
+I:23:17:2
+W:20:30:130:40000
+P:0:2d5:8:12:0
+F:STR | CON | SPEED | HIDE_TYPE |
+F:SLAY_EVIL | BRAND_ELEC | SLAY_DEMON | FREE_ACT | RES_ELEC |
+F:RES_LITE | RES_DARK | SEE_INVIS | SHOW_MODS | VORPAL | WOUNDING
+F:AGGRAVATE | CURSED
+D:Forged of black galvorn by the Dark-Elven smith Eol, this blade has the
+D:living lightning trapped inside.
+
+
+# The Long Sword 'Elvagil'
+
+N:85:'Elvagil'
+I:23:17:2
+W:20:8:130:20000
+P:0:2d5:5:7:0
+F:DEX | CHR | STEALTH | HIDE_TYPE | ESP_ORC | ESP_TROLL
+F:SLAY_TROLL | SLAY_ORC | FEATHER | SEE_INVIS | SHOW_MODS
+D:The "Singing Blade", whose wearer can slay Orcs and Trolls in the hidden
+D:and secret places of the earth.
+
+
+# The Rapier 'Forasgil'
+
+N:86:'Forasgil'
+I:23:7:0
+W:15:8:40:15000
+P:0:1d6:12:19:0
+F:SLAY_ANIMAL | BRAND_COLD | RES_COLD | RES_LITE | SHOW_MODS
+D:A slender, tapered blade whose wielder strikes icy blows with deadly
+D:accuracy.
+
+
+# The Sabre 'Careth Asdriag'
+
+N:87:'Careth Asdriag'
+I:23:11:2
+W:15:8:50:25000
+P:0:2d7:6:8:0
+F:DEX | BLOWS | SPEED | CON |
+F:SLAY_DRAGON | SLAY_ANIMAL | SLAY_TROLL | SLAY_GIANT |
+F:SLAY_ORC | SHOW_MODS | ESP_ANIMAL
+D:An heirloom of the Lords of Rhun far to the east, and a name of
+D:dismay to creatures natural and unnatural.
+
+
+# The Small Sword 'Sting'
+
+N:88:'Sting'
+I:23:8:2
+W:20:205:75:100000
+P:0:1d6:7:8:0
+F:STR | DEX | CON | BLOWS | SPEED | LEVELS |
+F:ESP_ORC | ESP_UNDEAD | ESP_SPIDER |
+F:SLAY_EVIL | SLAY_UNDEAD | SLAY_ORC | SLAY_ANIMAL | LITE1 |
+F:FREE_ACT | RES_LITE | SEE_INVIS | SHOW_MODS |
+D:"I will give you a name, and I shall call you Sting." The perfect size
+D:for Bilbo, and stamped forever by the courage he found in Mirkwood, this
+D:sturdy little blade grants the wearer combat prowess and survival
+D:abilities they did not know they had.
+
+
+# The Scimitar 'Haradekket'
+
+N:89:'Haradekket'
+I:23:18:2
+W:20:8:130:111111
+P:0:2d5:9:11:0
+F:INT | WIS | BLOWS |
+F:SLAY_ANIMAL | SLAY_EVIL | SLAY_UNDEAD | SLAY_DRAGON | SLAY_DEMON |
+F:RES_CHAOS | RES_DISEN | RES_NEXUS |
+F:SEE_INVIS | BLESSED |
+F:SHOW_MODS
+D:A damascened scimitar that seems wondrously easy to hold. Famed in song as
+D:the "Sickle of Harad", and a deadly foe to the undead.
+
+
+# The Short Sword 'Gilettar'
+
+N:90:'Gilettar'
+I:23:10:2
+W:20:8:80:35000
+P:0:1d7:3:7:0
+F:BLOWS | HIDE_TYPE |
+F:SLAY_ANIMAL | SLOW_DIGEST | REGEN | SHOW_MODS | SEE_INVIS | RES_DISEN
+D:A stubby blade worn by Beren, whose horn sounded of old in the glades of
+D:Brethil.
+
+
+# The Blade of Chaos 'Doomcaller'
+
+N:91:'Doomcaller'
+I:23:30:0
+W:70:25:180:250000
+P:0:6d5:18:28:-50
+F:KILL_DRAGON | SLAY_ANIMAL | SLAY_EVIL | BRAND_COLD | SLAY_TROLL |
+F:SLAY_ORC | FREE_ACT | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:RES_CHAOS | SEE_INVIS | ESP_EVIL | AGGRAVATE | SHOW_MODS |
+F:CHAOTIC | VORPAL | BRAND_FIRE | BRAND_POIS | SPECIAL_GENE
+D:This weapon of wrath, cursed with a violent anger, dives hungrily
+D:into the flesh of its enemies. It gathers shadows of death into its
+D:owner as they inflict wounds that will never heal.
+
+
+# The Long Sword 'Vorpal Blade'
+
+N:92:'Vorpal Blade'
+I:23:17:2
+W:50:30:150:250000
+P:0:5d5:32:32:0
+F:VORPAL | SLAY_EVIL | WOUNDING
+F:FREE_ACT | SEE_INVIS | SLOW_DIGEST | REGEN | SPEED | STR | DEX
+D:"One, two! One, two! And through, and through, the vorpal blade
+D:went snicker-snack!"
+
+
+# The Beaked Axe of Theoden
+
+N:93:of Theoden
+I:22:10:3
+W:20:15:180:40000
+P:0:2d6:8:10:0
+F:WIS | CON | HIDE_TYPE |
+F:SLAY_DRAGON | ESP_EVIL | ESP_UNDEAD | SLOW_DIGEST | ACTIVATE | SHOW_MODS
+a:HARDCORE=DRAIN_2
+D:The narrow axe head of this weapon, finely balanced by a crow's beak,
+D:would pierce even the armour of Smaug, and its wielder becomes aware of
+D:the minds of their enemies.
+
+
+# The Glaive of Pain
+
+N:94:of Pain
+I:22:13:0
+W:30:155:190:50000
+P:0:9d6:0:30:0
+F:SHOW_MODS | LEVELS | DRAIN_MANA
+F:COULD2H
+f:COULD2H
+D:The massive chopper that crowns this glaive glows blood-red and black;
+D:fell spells of annihilation swirl and dance as you swing death's myrmidon
+D:down.
+
+
+# The Halberd 'Osondir'
+
+N:95:'Osondir'
+I:22:15:3
+W:20:8:190:22000
+P:0:3d5:6:9:0
+F:CHR | HIDE_TYPE |
+F:BRAND_FIRE | SLAY_UNDEAD | SLAY_GIANT | RES_FIRE | RES_SOUND |
+F:FEATHER | SEE_INVIS | SHOW_MODS | ESP_GIANT
+F:COULD2H
+f:COULD2H
+D:Lordly and tall did Osondir stand against the wrath of giants, and
+D:clear-eyed in barrows fell, wielding a halberd glowing ruby red.
+
+
+# The Pike 'Til-i-arc'
+
+N:96:'Til-i-arc'
+I:22:8:2
+W:20:15:160:32000
+P:0:2d5:10:12:10
+F:INT | HIDE_TYPE |
+F:BRAND_COLD | BRAND_FIRE | SLAY_DEMON | SLAY_TROLL | SLAY_GIANT | ESP_GIANT
+F:RES_FIRE | RES_COLD | SUST_INT | SLOW_DIGEST | SHOW_MODS |
+F:COULD2H
+f:COULD2H
+D:Within this long thrusting spear lie the spirits of frost giants and fire
+D:demons, who war forever, trapped by magely spells.
+
+
+# The Spear 'Aeglos'
+
+N:97:'Aeglos'
+I:22:2:4
+W:15:45:50:180000
+P:0:3d6:15:25:5
+F:DEX | WIS | HIDE_TYPE |
+F:BRAND_COLD | BRAND_ELEC | LITE1 |
+F:SLAY_TROLL | SLAY_ORC | SLAY_GIANT | KILL_UNDEAD |
+F:FREE_ACT | RES_COLD | RES_ELEC | RES_LITE |
+F:SLOW_DIGEST | ACTIVATE | BLESSED | SHOW_MODS |
+a:HARDCORE=BA_ELEC_2
+D:The mighty spear of Gil-galad, famed as "Snow-point" in the songs of
+D:Elves, against which all the foul corruptions of Sauron dashed in vain.
+
+
+# The Spear of Orome
+
+N:98:of Orome
+I:22:2:4
+W:15:45:50:77777
+P:0:4d6:15:15:0
+F:INT | WIS | SPEED | TUNNEL | INFRA | HIDE_TYPE | SEARCH |
+F:BRAND_FIRE |
+F:SLAY_GIANT | SLAY_EVIL | SLAY_DEMON | SLAY_UNDEAD | SLAY_DRAGON |
+F:RES_FIRE | RES_LITE | HOLD_LIFE | RES_FEAR |
+F:FEATHER | ESP_GIANT
+F:SEE_INVIS |
+F:ACTIVATE | BLESSED | SHOW_MODS
+a:HARDCORE=STONE_MUD
+D:The thrusting spear of wise Orome the Vala, strong against giants of frost,
+D:which can melt rock or flesh with ease.
+
+
+# The Spear 'Nimloth'
+
+N:99:'Nimloth'
+I:22:2:3
+W:15:12:50:30000
+P:0:1d6:11:13:0
+F:STEALTH | RES_DARK | INFRA | SPEED | BLESSED |
+F:BRAND_COLD | SLAY_UNDEAD | RES_COLD | SEE_INVIS | SHOW_MODS
+D:A thin spike of thrice-forged steel caps a straight sylvan shaft cut from
+D:a legendary tree; spells to break the will of the undead and strike cold
+D:fear into the hearts of foes lie on this perfectly balanced spear.
+
+
+# The Lance of Eorlingas
+
+N:100:of Eorlingas
+I:22:20:2
+W:20:23:360:55000
+P:0:3d8:3:21:0
+F:STR | DEX | SPEED | HIDE_TYPE | RES_FEAR |
+F:SLAY_EVIL | SLAY_TROLL | SLAY_ORC | SEE_INVIS | SHOW_MODS
+F:MUST2H
+f:MUST2H
+D:"Forth Eorlingas!" To the field of Cormallen came Eorl the Young
+D:to save beleaguered Gondor, and from his lance fled massive trolls
+D:and dire wolves.
+
+
+# The Great Axe of Durin
+
+N:101:of Durin
+I:24:25:3
+W:30:90:230:150000
+P:0:4d4:10:20:15
+F:STR | CON | TUNNEL | HIDE_TYPE | ESP_EVIL | RES_FEAR |
+F:SLAY_DRAGON | KILL_DEMON | SLAY_TROLL | SLAY_ORC | FREE_ACT |
+F:RES_ACID | RES_FIRE | RES_LITE | RES_DARK | RES_CHAOS | SHOW_MODS |
+F:BRAND_ACID | BRAND_FIRE
+F:MUST2H
+f:MUST2H
+D:The twin massive axe heads of this ancient demon's dread gleam with
+D:mithril inlay, which tell sagas of endurance, invoking the powers of
+D:Khazad-dum to protect the wearer and slay all evils found underground.
+
+
+# The Great Axe of Eonwe
+
+N:102:of Eonwe
+I:24:25:2
+W:30:120:230:200000
+P:0:4d4:15:18:8
+F:STR | INT | WIS | DEX | CON | CHR | HIDE_TYPE |
+F:SLAY_EVIL | BRAND_COLD | KILL_DEMON | SLAY_UNDEAD | ESP_NONLIVING
+F:SLAY_ORC | FREE_ACT | IM_COLD | SEE_INVIS | ACTIVATE |
+F:BLESSED | SHOW_MODS
+a:HARDCORE=MASS_GENO
+D:The axe of Eonwe, leader of the Hosts of the West before the gates of
+D:Thangorodrim, strikes with icy wrath at the undead, disperses hosts of
+D:evil at a word, and grants Maia-like powers of body and mind.
+
+
+# The Battle Axe of Balli Stonehand
+
+N:103:of Balli Stonehand
+I:22:22:3
+W:30:15:170:90000
+P:0:3d8:8:11:5
+F:STR | CON | STEALTH | HIDE_TYPE | ESP_NONLIVING
+F:SLAY_DEMON | SLAY_TROLL | SLAY_ORC | FREE_ACT |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_BLIND | FEATHER |
+F:SEE_INVIS | REGEN | SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:The twin blades of this weapon were forged in Belegost, and powerful forces
+D:to resist and endure lie ready for he who shall wield it once more.
+
+
+# The Battle Axe 'Lotharang'
+
+N:104:'Lotharang'
+I:22:22:1
+W:30:15:170:21000
+P:0:2d8:4:3:0
+F:STR | DEX | HIDE_TYPE |
+F:SLAY_TROLL | SLAY_ORC | ACTIVATE | SHOW_MODS
+a:HARDCORE=CURE_MW
+D:A superbly crafted double-bladed axe that slays the creatures of earth and
+D:allows rapid recovery from their blows.
+
+
+# The Lochaber Axe 'Mundwine' -> of the Dwarves
+
+N:105:of the Dwarves
+I:22:28:10
+W:30:8:250:80000
+P:0:3d8:12:17:0
+F:SLAY_EVIL | TUNNEL | INFRA | SEARCH | SLAY_GIANT |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_FEAR |
+F:SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:A massive axe with twin razor-sharp heads, so large that it usually
+D:requires two hands to wield, intricately engraved in gold with spells
+D:to ward off the elements and smite evil.
+
+
+# The Broad Axe 'Barukkheled'
+
+N:106:'Barukkheled'
+I:24:11:3
+W:20:8:160:50000
+P:0:2d6:13:19:0
+F:CON | HIDE_TYPE |
+F:SLAY_EVIL | SLAY_TROLL | SLAY_GIANT | SLAY_ORC |
+F:SEE_INVIS | SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:A royal heirloom of the southern coast, strong in combat against evil
+D:creatures of the earth.
+
+
+# The Trident of Wrath
+
+N:107:of Wrath
+I:22:5:2
+W:15:35:300:90000
+P:0:3d8:16:18:0
+F:STR | DEX | HIDE_TYPE | CHAOTIC | DRAIN_MANA |
+F:SLAY_EVIL | KILL_UNDEAD | RES_LITE | RES_DARK | SEE_INVIS |
+F:BLESSED | SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:A massive triple-pronged spear, so great it normally requires two hands to
+D:wield, evoking the spirit of Osse who with it pierced legions of
+D:evil and undead.
+
+
+# The Trident of Ulmo
+
+N:108:of Ulmo
+I:22:5:4
+W:30:90:70:120000
+P:0:4d8:15:19:0
+F:DEX | HIDE_TYPE |
+F:SLAY_DRAGON | SLAY_ANIMAL | FREE_ACT | HOLD_LIFE | IM_ACID |
+F:RES_NETHER | SEE_INVIS | SLOW_DIGEST | REGEN | ACTIVATE |
+F:BLESSED | SHOW_MODS | WATER_BREATH
+a:HARDCORE=TELE_AWAY
+D:The awesome weapon of the Vala Ulmo, Lord of Waters. Mightiest of all the
+D:powers of good save Manwe himself, Ulmo laughs in scorn at the dread powers
+D:of the undead, and is utterly in command of the element of water.
+
+
+# The Scythe 'Avavir'
+
+N:109:'Avavir'
+I:22:17:3
+W:40:8:250:18000
+P:0:5d3:8:8:10
+F:DEX | CHR | HIDE_TYPE |
+F:BRAND_COLD | BRAND_FIRE | FREE_ACT | RES_FIRE | RES_COLD |
+F:RES_LITE | SEE_INVIS | ACTIVATE | SHOW_MODS
+F:COULD2H
+f:COULD2H
+a:HARDCORE=RECALL
+D:With elemental powers whose struggles turn this weapon red and purest
+D:white, this shining reaper bears within it a power of going forth and
+D:returning.
+
+
+# The Long Sword of the Dawn
+
+N:110:of the Dawn
+I:23:17:3
+W:40:160:130:250000
+P:0:3d5:20:20:0
+F:ACTIVATE | BRAND_FIRE | FREE_ACT | RES_FIRE | INFRA | LEVELS |
+F:SLAY_EVIL | SLAY_DRAGON | SLAY_UNDEAD | SLAY_DEMON | VORPAL | CLONE |
+F:CHR | SUST_CHR | RES_FEAR | RES_LITE | RES_BLIND | REGEN | SHOW_MODS
+a:HARDCORE=DAWN
+D:Forged in the farthest East by a race of mighty spellcasters, this
+D:shiny pale sword gleams with the rays of rising sun as you invoke
+D:its power of commanding legions of powerful immortal warriors...
+
+
+# The Mighty Hammer 'Grond'
+
+N:111:'Grond'
+I:21:50:2
+W:100:1:1000:500000
+P:0:9d9:25:25:10
+F:KILL_DRAGON | SLAY_ANIMAL | SLAY_EVIL | IMPACT | KILL_UNDEAD | NO_MAGIC |
+F:KILL_DEMON | SLAY_TROLL | SLAY_ORC | RES_ACID | RES_ELEC | RES_FIRE |
+F:RES_COLD | SEE_INVIS | ESP_ALL | AGGRAVATE | SHOW_MODS | INSTA_ART |
+F:LEVELS | ACTIVATE | SPECIAL_GENE
+F:MUST2H
+f:MUST2H
+a:HARDCORE=GROND
+D:The mighty Hammer of the Underworld, blackened by doomspells of shattering,
+D:whose wielder holds the lives of all Morgoth's servants in his hand.
+
+
+# The Flail 'Totila'
+
+N:112:'Totila'
+I:21:13:2
+W:20:8:150:55000
+P:0:3d6:6:8:0
+F:STEALTH |
+F:SLAY_EVIL | BRAND_FIRE | RES_FIRE | RES_CONF | ACTIVATE |
+F:SHOW_MODS | LITE1
+F:COULD2H
+f:COULD2H
+a:HARDCORE=CONFUSE
+D:A flail whose head befuddles those who stare as you whirl it around, and
+D:becomes a fiery comet as you bring it down.
+
+
+# The Two-Handed Flail 'Thunderfist'
+
+N:113:'Thunderfist'
+I:21:18:4
+W:45:38:300:160000
+P:0:3d6:5:18:0
+F:STR | CON | HIDE_TYPE | RES_FEAR |
+F:SLAY_ANIMAL | BRAND_FIRE | BRAND_ELEC | SLAY_TROLL | SLAY_ORC |
+F:RES_ELEC | RES_FIRE | RES_DARK | SHOW_MODS |
+F:MUST2H
+f:MUST2H
+D:The long-lost weapon of Kzurin, Dwarven champion of ancient Belegost,
+D:with runes of strength in its handle, and flames and sparks that roar and
+D:crackle around its massive head.
+
+
+# The Morning Star 'Bloodspike'
+
+N:114:'Bloodspike'
+I:21:12:4
+W:20:30:150:30000
+P:0:2d6:8:22:0
+F:STR | HIDE_TYPE | BRAND_POIS |
+F:SLAY_ANIMAL | SLAY_TROLL | SLAY_ORC | RES_NEXUS | SEE_INVIS |
+F:SHOW_MODS
+D:You feel strong and firm of foot as you whip the chain-suspended spiked orb
+D:around - and bathe it in the blood of your foes.
+
+
+# The Morning Star 'Firestar'
+
+N:115:'Firestar'
+I:21:12:0
+W:20:100:150:35000
+P:0:2d6:5:7:2
+F:BRAND_FIRE | IM_FIRE | ACTIVATE | SHOW_MODS | LITE1
+a:HARDCORE=FIRESTAR
+D:A famed battle-lord of old, with a ruddy head, coloured as embers are that
+D:can yet rise up in wrath.
+
+
+# The Mace 'Taratol'
+
+N:116:'Taratol'
+I:21:5:0
+W:20:15:200:50000
+P:0:3d4:12:12:0
+F:KILL_DRAGON | BRAND_ELEC | IM_ELEC | ACTIVATE | SHOW_MODS
+F:COULD2H
+f:COULD2H
+a:HARDCORE=SPEED
+D:A great ridged mace that calls around you a nimbus of living lightning;
+D:you remain utterly untouched even as fat sparks arc around your
+D:fingers and eyebrows.
+
+
+# The War Hammer of Aule
+
+N:117:of Aule
+I:21:8:4
+W:40:75:120:250000
+P:0:9d3:19:21:5
+F:WIS | TUNNEL | HIDE_TYPE | RES_FEAR |
+F:KILL_DRAGON | SLAY_EVIL | BRAND_ELEC | SLAY_UNDEAD | SLAY_DEMON |
+F:FREE_ACT | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_NEXUS |
+F:SEE_INVIS | SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:The wondrous hammer of Aule, creator of the wise Dwarven lords of old.
+D:It bears magics of demolishing that no serpent or demon can withstand, and
+D:invokes the strength of mountains to ward off the tumult of the elements.
+
+
+# The Quarterstaff 'Nar-i-vagil'
+
+N:118:'Nar-i-vagil'
+I:21:3:3
+W:20:18:150:70000
+P:0:1d9:10:20:0
+F:INT | HIDE_TYPE |
+F:SLAY_ANIMAL | BRAND_FIRE | RES_FIRE | SHOW_MODS
+F:COULD2H
+f:COULD2H
+D:Named for a fiery star and set with gems of great worth binding mystic
+D:virtues of protection and thought.
+
+
+# The Quarterstaff 'Eriril'
+
+N:119:'Eriril'
+I:21:3:4
+W:20:18:150:20000
+P:0:1d9:3:5:0
+F:INT | WIS | HIDE_TYPE | ESP_EVIL | SPELL_CONTAIN | WIELD_CAST
+F:SLAY_EVIL | RES_LITE | SEE_INVIS | ACTIVATE | SHOW_MODS
+a:HARDCORE=ID_PLAIN
+D:The radiant golden staff of an Istari of legend, this wizard's companion
+D:grants keen sight and the knowledge of many hidden things.
+
+
+# The Quarterstaff of Olorin
+
+N:120:of Olorin
+I:21:3:4
+W:30:105:150:140000
+P:0:2d9:10:13:0
+F:INT | WIS | CHR | HIDE_TYPE | SEARCH | BRAND_FIRE |
+F:SLAY_EVIL | BRAND_FIRE | SLAY_TROLL | SLAY_ORC | SPELL_CONTAIN | WIELD_CAST
+F:HOLD_LIFE | RES_FIRE | RES_NETHER | SEE_INVIS | ACTIVATE | SHOW_MODS
+a:HARDCORE=DETECT_XTRA
+D:A staff tall and sturdy, with rough-hewn runes that invoke the element of
+D:Earth, and which strikes down all creatures who live in the shadow of
+D:mountains.
+
+
+# The Mace of Disruption 'Deathwreaker'
+
+N:121:'Deathwreaker'
+I:21:20:6
+W:80:38:400:444444
+P:0:7d8:18:18:0
+F:STR | TUNNEL | HIDE_TYPE | NO_TELE | DRAIN_MANA |
+F:SLAY_DRAGON | SLAY_ANIMAL | SLAY_EVIL | KILL_UNDEAD | BRAND_FIRE |
+F:IM_FIRE | RES_DARK | RES_CHAOS | RES_DISEN | AGGRAVATE |
+F:SHOW_MODS | BRAND_POIS | VAMPIRIC
+F:MUST2H
+f:MUST2H
+D:A weapon so massive it seems beyond the strength of mortals, yet you feel
+D:the might of giants within you as you heft it. As you grip the handle
+D:of ebony and steel, coronas of fire blaze and mighty spells to preserve
+D:magic activate around you. You wield the Fear of Dragons and the Despair
+D:of the Undead!
+
+
+# The Lucerne Hammer 'Turmil'
+
+N:122:'Turmil'
+I:21:10:4
+W:20:15:120:30000
+P:0:2d5:10:6:8
+F:WIS | INFRA | HIDE_TYPE |
+F:BRAND_COLD | SLAY_ORC | RES_COLD | RES_LITE | REGEN |
+F:ACTIVATE | SHOW_MODS | ESP_ORC | ESP_TROLL | ESP_GIANT
+F:COULD2H
+f:COULD2H
+a:HARDCORE=TURMIL
+D:Wielded by the High Priest of Meneltarma, this great mace gleams coldly as
+D:though moonlit, and it can strike as mighty a blow spiritually as
+D:physically.
+
+
+# The Whip of Gothmog
+
+N:123:of Gothmog
+I:21:2:-2
+W:20:15:120:100000
+P:0:3d6:15:16:0
+F:INT | DEX | INFRA | HIDE_TYPE | DRAIN_HP |
+F:HEAVY_CURSE | CURSED | AGGRAVATE |
+F:BRAND_FIRE | SLAY_ANIMAL | SLAY_DEMON | RES_FIRE | ESP_SPIDER
+F:VORPAL | RES_LITE | LITE1 | REGEN | ESP_DEMON | WOUNDING
+F:SHOW_MODS
+D:With this unbearably bright whip of flame, the Balrog Gothmog has become
+D:known for never having lost in combat.
+
+
+# The Long Bow 'Belthronding'
+
+N:124:'Belthronding'
+I:19:13:3
+W:40:20:40:35000
+P:0:0d0:20:22:0
+F:DEX | STEALTH | HIDE_TYPE |
+F:RES_DISEN | XTRA_SHOTS | SHOW_MODS
+D:The great bow of Beleg, made of black yew and strung with elven hair that
+D:faintly shines a pale clear gold.
+
+
+# The Long Bow of Bard
+
+N:125:of Bard
+I:19:13:2
+W:30:20:40:20000
+P:0:0d0:17:19:0
+F:DEX | HIDE_TYPE | ESP_DRAGON | LUCK
+F:FREE_ACT | XTRA_MIGHT | SHOW_MODS
+D:The great yew bow of grim-faced Bard, who shot the mightiest arrow that
+D:songs record.
+
+
+# The Light Crossbow 'Cubragol'
+
+N:126:'Cubragol'
+I:19:23:10
+W:50:25:110:50000
+P:0:0d0:10:14:0
+F:SPEED | HIDE_TYPE |
+F:RES_FIRE | ACTIVATE | SHOW_MODS
+a:HARDCORE=CUBRAGOL
+D:A crossbow that grants fiery speed to he who finds it, and from which
+D:shoot bolts that blaze with flame unquenchable.
+
+
+# The Mage Staff of Eternity
+# Really powerful for a mage but extremely rare
+# The ULTIMATE "weapon" for a Sorceror or a magic class
+
+N:127:of Eternity
+I:6:1:12
+W:127:220:20:9000000
+P:0:1d4:-19:-19:0
+F:INT | CHR | WIS | MANA | SPELL | ACTIVATE |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | LUCK | SPECIAL_GENE
+F:SEE_INVIS | ESP_EVIL | ESP_DEMON | NEVER_BLOW | INFRA
+F:PRECOGNITION | IM_FIRE | ULTIMATE | SPELL_CONTAIN | WIELD_CAST
+F:COULD2H
+f:COULD2H
+a:HARDCORE=GANDALF
+D:A simple, wooden wizard's staff. Unremarkable in all aspects...
+D:except that it pulses with overwhelming power.
+
+
+# Boomerang Artifacts
+# The Metal Boomerang of Beor
+
+N:128:of Beor
+I:15:4:4
+W:20:10:20:40000
+P:0:4d5:8:12:0
+F:DEX | SPEED |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD
+D:Beor's boomerang makes its wielder as agile as the winds,
+D:and as hard to harm.
+
+
+# The Metal Boomerang 'Glimdrir'
+
+N:129:'Glimdrir'
+I:15:4:3
+W:40:20:20:60000
+P:0:5d5:15:16:0
+F:DEX | SPEED | FREE_ACT | BRAND_POIS | SLAY_EVIL | SLAY_UNDEAD | REGEN
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_SOUND | NO_TELE | CURSED
+D:A powerful boomerang that makes one agile and fast, with a thirst for
+D:evil and undead creatures, but demands its wielder not teleport, for fear
+D:of desertion.
+
+
+# The Robe of Incanus [aka Gandalf]
+
+N:130:of Incanus
+I:36:2:3
+W:30:20:20:60000
+P:2:0d0:0:0:20
+F:INT | WIS | SEARCH | HIDE_TYPE | SPELL_CONTAIN | WIELD_CAST
+F:SUST_INT | SUST_WIS | FREE_ACT | SEE_INVIS |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+Z:weigh magic
+D:Gandalf's long, flowing robe. It provides insight and allows the
+D:wearer to see things not seen by all.
+
+
+# The Sling of the Thain
+
+N:131:of the Thain
+I:19:2:4
+W:40:20:40:35000
+P:0:0d0:15:15:0
+F:HIDE_TYPE | DEX | CON
+F:RES_NETHER | XTRA_SHOTS | XTRA_MIGHT | SHOW_MODS
+D:This sling was crafted by Faramir I, Thain of the Shire, just in case
+D:the nasties of his father's stories ever dare to enter the Shire again.
+
+
+# The Whip 'Lasher'
+
+N:134:'Lasher'
+I:21:2:3
+W:20:5:30:50000
+P:0:1d6:12:15:0
+F:DEX | BLOWS | HIDE_TYPE |
+F:SLAY_ANIMAL | SLAY_ORC | BRAND_POIS | VORPAL |
+F:RES_POIS | FREE_ACT | ESP_ORC
+D:A powerful whip that is deadly against orcs. It poisons your foes
+D:and is said to go "snicker snack".
+
+
+# The Seeker Arrow 'Bullseye'
+
+N:135:'Bullseye'
+I:17:2:0
+W:45:1:2:50000
+P:0:7d4:20:15:0
+F:SLAY_ANIMAL | SLAY_EVIL | SLAY_UNDEAD | KILL_DEMON |
+F:SLAY_ORC | SLAY_TROLL | SLAY_GIANT | SLAY_DRAGON |
+F:BRAND_ACID | BRAND_ELEC | BRAND_FIRE | BRAND_COLD | BRAND_POIS
+D:A powerful arrow that is feared by even the mightiest demons.
+
+
+# The Rounded Pebble 'Travak'
+
+N:136:'Travak'
+I:16:0:0
+W:5:1:2:5000
+P:0:3d6:8:5:0
+F:BRAND_ACID | BRAND_ELEC | BRAND_FIRE | BRAND_COLD | BRAND_POIS
+D:A rounded pebble imbued with the powers of the elements.
+
+
+# The Harp of Maglor
+
+N:137:of Maglor
+I:14:59:3
+W:60:10:20:100000
+P:0:3d4:0:0:0
+F:CHR | SPEED | WIS | SEE_INVIS | RES_SOUND | STEALTH | LUCK
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | ESP_UNIQUE | WIELD_CAST
+D:This harp that once belonged to Maglor makes those who use it seem
+D:more forceful and convincing. It is also said that those who have
+D:used it found themselves walking faster, as if to an unheard beat.
+
+
+# The Drum of the Sky
+
+N:138:of the Sky
+I:14:58:2
+W:40:10:15:80000
+P:0:3d4:0:0:0
+F:CHR | SPEED | WIS | SEE_INVIS | RES_SOUND | STEALTH | LUCK
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | WIELD_CAST
+D:The drum is decorated with the images of the stars, the clouds, the
+D:Sun guided by Arien and the Moon with Tilion. It imparts to the
+D:wearer an echo of the beauty of the sky, and protects him from the
+D:elements day or night. The beat of the drum marks the passage of
+D:time, and will make time pass differently for the wearer.
+
+
+# The Harp of Daeron
+
+N:139:of Daeron
+I:14:59:1
+W:20:10:10:50000
+P:0:3d4:0:0:0
+F:CHR | SPEED | WIS | RES_SOUND | STEALTH | LUCK
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | WIELD_CAST
+D:A pretty harp that makes those who play it beautiful, wise and
+D:fast.
+
+
+# The Dwarven Pick of Erebor
+
+N:140:of Erebor
+I:20:6:5
+W:50:15:200:55000
+P:0:3d4:0:0:0
+F:STR | TUNNEL | SUST_STR | HIDE_TYPE | LITE1 | ACTIVATE | CLIMB
+F:RES_CHAOS | RES_LITE | RES_DARK
+a:HARDCORE=EREBOR
+D:A pick that provides a magical light by which to see while tunnelling.
+
+
+# The Drum of the Druedain
+
+N:141:of the Druedain
+I:14:58:4
+W:19:10:15:10000
+P:0:3d4:0:0:0
+F:ACTIVATE | STEALTH | SEARCH | INFRA | RES_POIS | RES_DARK | WIELD_CAST
+a:HARDCORE=DRUEDAIN
+D:The fabled Drum of the Druedain that will protect those who play it
+D:from darkness and poison attacks. It also aids in the seeing of
+D:warm-blooded creatures.
+
+
+# The Horn of Rohan
+
+N:142:of Rohan
+I:14:60:2
+W:14:10:15:80000
+P:0:3d4:0:0:0
+F:ACTIVATE | CHR | WIS | ESP_DRAGON | WIELD_CAST
+a:HARDCORE=ROHAN
+D:A horn carved from the bones of the Dragon of Ered-Mithrin, this
+D:heirloom of the House of Eorl bestows to its user the gifts of
+D:courage and command.
+
+
+# The Horn of Helm
+
+N:143:of Helm
+I:14:60:2
+W:16:10:15:15000
+P:0:3d4:0:0:0
+F:ACTIVATE | STR | CON | IM_COLD | RES_NETHER | RES_FEAR | WIELD_CAST
+a:HARDCORE=HELM
+D:Heedless of cold, fearless of darkness -- besiegers fled at the wind
+D:of the solitary coming of King Helm Hammerhand, proclaimed by a single
+D:horn-blast in the dead of winter.
+
+
+# The Horn of Boromir
+
+N:144:of Boromir
+I:14:60:3
+W:18:10:15:18000
+P:0:3d4:0:0:0
+F:ACTIVATE | STR | CON | RES_FEAR | RES_FIRE | AGGRAVATE | WIELD_CAST
+a:HARDCORE=BOROMIR
+D:Boromir's horn gives courage and endurance to the wearer, provided he does
+D:not wish to travel in secrecy: for it must always sound when its wielder
+D:sets forth on a journey. "Loud and clear it sounds in the valleys of the
+D:hills... and then let all the foes of Gondor flee!"
+
+
+# The Lochaber Axe of Gothmog, which slew Fingon
+
+N:145:of Gothmog
+I:22:28:-4
+W:30:8:250:30000
+P:0:3d8:14:19:0
+F:BRAND_FIRE | IM_FIRE | CHR | ACTIVATE | SHOW_MODS | CURSED | TY_CURSE
+a:HARDCORE=AXE_GOTHMOG
+D:The black axe of Gothmog, which struck Fingon at Nirnaeth. Mighty
+D:spells of evil make it unsafe in any hands but those of its original wielder.
+
+
+# The Seeker Arrow of Gondor
+
+N:146:of Gondor
+I:17:2:0
+W:20:5:3:25000
+P:0:10d8:10:20:0
+F:SLAY_EVIL | SLAY_DEMON
+D:An arrow that was created to rid the world of demons.
+
+
+# The Long Sword of Eternity
+# The ULTIMATE weapon for a warrior class
+
+N:147:of Eternity
+I:23:17:10
+W:127:220:130:9000000
+P:0:5d6:21:26:50
+F:LIFE | CON | CHR | LUCK
+F:SUST_STR | SUST_INT | SUST_WIS | SUST_DEX | SUST_CON | SUST_CHR |
+F:BRAND_FIRE | BRAND_COLD | BRAND_ELEC | VORPAL | IM_COLD |
+F:SLAY_EVIL | SLAY_UNDEAD | SLAY_DEMON | SLAY_TROLL | SLAY_DEMON |
+F:FREE_ACT | RES_FIRE | RES_DARK | LITE1 | SEE_INVIS | SLOW_DIGEST | REGEN |
+F:ACTIVATE | SHOW_MODS | BLESSED |
+F:PRECOGNITION | NO_MAGIC | ULTIMATE | SPECIAL_GENE
+a:HARDCORE=ERU
+D:A warm light bathes this translucent blade. The power of the fates are
+D:at the command of its wielder as the weapon passes Supreme Judgment on
+D:the inhabitants of Angband.
+
+
+# The Robe of Great Luck
+
+N:148:of Great Luck
+I:36:2:60
+W:50:120:20:60000
+P:-30:0d0:0:0:-20
+F:LUCK | HIDE_TYPE |
+F:FREE_ACT | DRAIN_HP | DRAIN_MANA
+D:A powerful wizard once created this robe to grant him incredible luck....
+D:It seems he forgot to wear it.
+
+
+# The Sling of Farmer Maggot
+
+N:149:of Farmer Maggot
+I:19:2:2
+W:10:10:5:20000
+P:0:0d0:20:0:0
+F:INFRA | SEARCH | HIDE_TYPE |
+F:XTRA_SHOTS | SHOW_MODS | ACTIVATE | SPECIAL_GENE
+a:SPELL=Artifact Maggot
+D:This ordinary seeming leather sling has been raised to legendary
+D:status amongst generations of hobbit children. Farmer Maggot's
+D:ability to notice and strike any mushroom thief anywhere within
+D:his patch almost keeps young poachers at bay, but once they get
+D:within range they soon flee for less painful pastures, frequently
+D:with rounded pebbles stinging their backsides...
+
+
+# The Long Sword of Angmar (a.k.a. anti-Ringil)
+# The next time someone wields an unidentified Long Sword (4d5) ...
+
+N:150:of Angmar
+I:23:17:-10
+W:20:40:130:30000
+P:0:4d5:-22:-25:0
+F:SPEED | STR | WIS | CHR | ESP_UNDEAD
+F:BRAND_FIRE | SEE_INVIS | SLOW_DIGEST | FREE_ACT |
+F:VAMPIRIC | NO_TELE | AGGRAVATE | WRAITH | INVIS |
+F:CURSED | HEAVY_CURSE | DG_CURSE | SHOW_MODS | CLONE |
+D:Dark flames wreath the naked steel of the Witch-King of Angmar.
+D:A mighty curse to all those who wield it apart from its master,
+D:the torture of the wraithworld awaits those who dare.
+
+
+# The Seeker Bolt of Feanor
+
+N:151:of Feanor
+I:18:2:0
+W:127:220:130:100000
+P:0:5d5:5:6:0
+F:BRAND_COLD | BRAND_FIRE | BRAND_ELEC | BRAND_ACID | BRAND_POIS |
+F:SLAY_DRAGON | SLAY_GIANT | SLAY_TROLL | KILL_UNDEAD | SLAY_ORC |
+F:SLAY_DEMON | SLAY_EVIL | SPECIAL_GENE
+D:Made during the war against Morgoth by Feanor, this powerful
+D:bolt is the bane of Morgoth's power, and has especial strength
+D:against those foes who are already dead.
+
+
+# The Heavy Crossbow of Eternity
+# The ULTIMATE bow for an archer class
+
+N:152:of Eternity
+I:19:24:5
+W:127:220:130:8000000
+P:0:0d0:36:28:0
+F:SEE_INVIS | SLOW_DIGEST | FREE_ACT | SPEED | DEX | CON | FLY | LUCK
+F:XTRA_MIGHT | XTRA_SHOTS | IM_ELEC | REFLECT | INVIS | STEALTH |
+F:SUST_STR | SUST_INT | SUST_WIS | SUST_DEX | SUST_CON | SUST_CHR |
+F:RES_CHAOS | RES_DISEN | RES_CONF | RES_BLIND | INFRA | ESP_ORC | ESP_TROLL | ESP_EVIL |
+F:PRECOGNITION | NO_MAGIC | ULTIMATE | SPECIAL_GENE
+D:Designed to be used with the Seeker Bolt of Feanor, this Crossbow
+D:is perfect against the terrible powers of Morgoth.
+
+
+# The Soft Leather Armour of the Sandworm
+
+N:153:of the Sandworm
+I:36:4:5
+W:30:3:80:65000
+P:30:0d0:0:0:0
+F:RES_POIS | RES_ELEC | RES_FIRE | RES_ACID | SPECIAL_GENE
+F:TUNNEL | STR | STEALTH | INFRA | ESP_ANIMAL
+D:This powerful piece of armour was made using the remains of
+D:the Sandworm Queen.
+
+
+# The Lochaber Axe 'Dragonbane'
+
+N:154:'Dragonbane'
+I:22:28:2
+W:70:20:260:33000
+P:0:3d8:20:20:0
+F:BLOWS | KILL_DRAGON | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:RES_POIS | SHOW_MODS
+D:Forged by the Dwarves to defend their home of Khazad-dum from dragons,
+D:this axe has been lost to time... until now.
+
+
+# The Light War Axe 'Limbslicer'
+
+N:155:'Limbslicer'
+I:24:8:4
+W:15:3:140:12000
+P:0:2d5:12:15:0
+F:DEX | VORPAL | HIDE_TYPE | SHOW_MODS | WOUNDING
+D:The Petty-dwarves of Bathak forged this blade, and it shares their thirst
+D:for blood.
+
+
+# The Broad Axe 'Orchast'
+
+N:156:'Orchast'
+I:24:11:4
+W:15:2:170:12000
+P:0:2d7:20:14:0
+F:DEX | SEARCH | SLAY_ORC | ACTIVATE | HIDE_TYPE | SHOW_MODS
+F:COULD2H
+f:COULD2H
+a:HARDCORE=ORCHAST
+D:Forged by the dwarves of Khazad-dum in a time of desperation,
+D:this axe turned many a battle against the invading orcs.
+
+
+# The Hatchet of the Night
+
+N:157:of the Night
+I:24:1:4
+W:45:20:45:34000
+P:0:2d6:34:22:0
+F:DEX | STEALTH | VAMPIRIC | KILL_UNDEAD | RES_DARK | HIDE_TYPE |
+F:SHOW_MODS | SEE_INVIS | ACTIVATE | DRAIN_EXP
+a:HARDCORE=NIGHT
+D:Found on an unmarked grave after a violent storm, this hatchet
+D:has a sinister aura of darkness and decay.
+
+
+# The Slaughter Axe 'Naturebane'
+
+N:158:'Naturebane'
+I:24:30:3
+W:70:20:300:28400
+P:0:5d7:31:27:0
+F:STR | SLAY_ANIMAL | SUST_STR | RES_SHARDS | RES_NEXUS | FEATHER |
+F:HIDE_TYPE | SHOW_MODS | ACTIVATE | DRAIN_HP
+a:HARDCORE=NATUREBANE
+D:Used by the orcs in their battle at Dagor Bragollach against the elves, this
+D:axe has a bloodthirst for nature.
+
+
+# The Light War Axe of Ice
+
+N:159:of Ice
+I:24:8:3
+W:30:25:140:26550
+P:0:2d5:3:15:0
+F:INT | CHR | SUST_DEX | BRAND_COLD | IM_COLD | RES_NEXUS | HIDE_TYPE |
+F:SHOW_MODS
+D:Crafted of purest ice and held solid by powerful spells, this icy axe
+D:delivers a chill of death to its victims.
+
+
+# The Iron Helm of Knowledge
+
+N:160:of Knowledge
+I:32:5:-6
+W:20:5:75:100000
+P:6:1d3:0:0:20
+F:LITE1 | HIDE_TYPE | SPECIAL_GENE | LUCK
+F:AUTO_ID | ACTIVATE
+a:HARDCORE=KNOWLEDGE
+D:This helm, designed by Petty-Dwarves ages ago to act as the brain of a
+D:long lost project, is made of finest glass. Its light banishes all secrets,
+D:and makes audible whispers from the deceased.
+
+
+### Trapping Kits ###
+
+### note prices and rarities may have to be adjusted ###
+
+# The Catapult Trap Set of Ahromarwar
+
+N:161:of Ahromarwar
+I:46:1:3
+W:20:10:40:20000
+P:0:0d0:25:15:30
+F:STEALTH | AUTOMATIC_99 | XTRA_MIGHT | HIDE_TYPE
+D:A trap that can almost never be detected. Its missiles may be mere pebbles,
+D:but fired at an incredibly high velocity to penetrate even the toughest
+D:hide or armour.
+
+
+# The Device Trap Set 'Hanisbroner's Surprise'
+
+N:162:'Hanisbroner's Surprise'
+I:46:6:3
+W:20:20:40:20000
+P:0:0d0:0:0:25
+F:STEALTH | XTRA_SHOTS | TELEPORT_TO | HIDE_TYPE | AUTOMATIC_99
+D:A magical trap, armed with a wand. Unaccountably, its victims keep
+D:on coming back for more...
+
+
+# The Bolt Trap Set 'Merlion Karc's Demonbane'
+
+N:163:'Merlion Karc's Demonbane'
+I:46:3:2
+W:20:20:200:20000
+P:0:0d0:17:27:37
+F:STEALTH | XTRA_SHOTS | XTRA_MIGHT | HIDE_TYPE | ONLY_DEMON
+D:A snare set not for animals, or people, but for demons alone, and
+D:enchanted so that whenever the demon sets foot or claw into the
+D:(hidden) pentagram, its hide is immediately pierced by many magical
+D:crossbow bolts.
+
+
+# The Broken Sword 'Narsil'
+
+N:164:'Narsil'
+I:23:2:2
+W:20:5:30:2000
+P:0:3d2:6:10:0
+F:STR | DEX | HIDE_TYPE | BLESSED |
+F:SLAY_ORC | SLAY_TROLL | RES_FIRE
+D:The sword that was broken shall be reforged...
+
+
+# The Steel Helm 'Lebohaum'
+# The name comes from a french parody of dungeon dwelling in mp3
+# http://penofchaos.com/warham.htm
+
+N:165:'Lebohaum'
+I:32:6:0
+W:20:15:15:25000
+P:20:0d0:0:0:80
+F:ACTIVATE
+a:SPELL=Artifact Lebauhaum
+D:With the Helm 'Lebohaum' your head is safe!
+
+
+# The Power Dragon Scale Mail 'Mediator'
+
+N:166:'Mediator'
+I:38:30:0
+W:95:12:500:400000
+P:50:2d4:-8:0:35
+F:FEATHER | FLY | ESP_DRAGON |
+F:RES_NEXUS | RES_CHAOS | AGGRAVATE | REGEN |
+F:RES_SHARDS | RES_SOUND | RES_DISEN | RES_CONF |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD |
+F:ACTIVATE
+a:HARDCORE=MEDIATOR
+D:A mighty suit of dragon armour, set with the scales of dragons of both
+D:Law and Chaos, and with power over both.
+
+
+# The Hard Leather Armour of Himring
+
+N:167:of Himring
+I:36:6:0
+W:50:20:100:35000
+P:6:0d0:0:0:15
+F:RES_CHAOS | RES_NETHER | RES_POIS | ACTIVATE
+a:HARDCORE=PROT_EVIL
+D:Contained within this studded cuirass of pliable leather is the memory of
+D:unvanquished Himring, defiant fortress surrounded by the legions of Morgoth.
+
+
+# The Soft Leather Armour 'Hithlomir'
+
+N:168:'Hithlomir'
+I:36:4:4
+W:20:3:80:45000
+P:4:0d0:0:0:20
+F:STEALTH | HIDE_TYPE | SEARCH |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_DARK
+D:Familiar with the secret ways hidden in darkness, this leather cuirass is
+D:truly more than it appears.
+
+
+# The Shield of Deflection of Gil-galad
+# Description from Sangband
+
+N:169:of Gil-galad
+I:34:10:5
+W:70:4:80:65000
+P:10:1d3:0:0:20
+F:ACTIVATE |
+F:LITE1 | WIS | CHR | SEARCH | LUCK
+F:RES_ELEC | RES_ACID | RES_DISEN | RES_DARK | HIDE_TYPE |
+F:SUST_WIS | SUST_DEX | SUST_CHR
+a:HARDCORE=GILGALAD
+D:The legendary shield of Gil-Galad, who fought his way to the gates of
+D:the Dark Tower, and with whom came light even to Gorgoroth.
+
+
+# The Metal Cap of Celebrimbor
+
+N:170:of Celebrimbor
+I:32:3:3
+W:55:12:20:45000
+P:3:1d1:0:0:18
+F:INT | DEX | CHR | SPELL | SEARCH |
+F:RES_FIRE | RES_ACID | RES_DISEN | RES_SHARDS |
+F:ACTIVATE
+a:HARDCORE=CELEBRIMBOR
+D:This once belonged to Celebrimbor, maker of the Rings of Power. One who
+D:knows both fire and acid, from the business of forging and engraving, will
+D:fear neither: nor have his enchantments ever faded. Celebrimbor was even
+D:aware of Sauron before Sauron became aware of him, when Sauron put on the
+D:One Ring for the first time.
+
+
+# The Heavy Crossbow of Umbar
+
+N:171:of Umbar
+I:19:24:2
+W:60:20:200:35000
+P:0:4d1:18:18:0
+F:STR | CON | XTRA_MIGHT | AGGRAVATE |
+F:RES_LITE | RES_DARK | RES_BLIND | RES_ELEC |
+F:HIDE_TYPE | ACTIVATE | SHOW_MODS
+a:HARDCORE=UMBAR
+D:A great brazen arbalest with arms of gleaming steel, shooting quarrels with
+D:speed and power for those brave enough to risk betrayal.
+
+
+# The Short Bows of Amrod and Amras, Feanor's twin sons
+# The Short Bow of Amrod
+
+N:172:of Amrod
+I:19:12:2
+W:25:10:30:9000
+P:0:0d0:12:15:0
+F:STR | CON | XTRA_MIGHT |
+F:RES_FIRE | RES_ELEC | RES_COLD | REGEN
+D:This bow, and its twin, belonged to Feanor's last two twin sons, Amrod
+D:and Amras, who both hunted with the Green-elves for a time. Like the
+D:twins, the bows are similar, for both protect their wielders from the
+D:elements: and yet they are also unlike, for this bow gives endurance
+D:and strength, while the other gives quickness and subtlety.
+
+
+# The Short Bow of Amras
+
+N:173:of Amras
+I:19:12:1
+W:25:10:30:9000
+P:0:0d0:12:15:0
+F:INT | WIS | DEX | XTRA_SHOTS | XTRA_MIGHT | SPEED |
+F:RES_FIRE | RES_ELEC | RES_COLD | SLOW_DIGEST
+D:This bow, and its twin, belonged to Feanor's last two twin sons, Amrod
+D:and Amras, who both hunted with the Green-elves for a time. Like the
+D:twins, the bows are similar, for both protect their wielders from the
+D:elements: and yet they are also unlike, for this bow gives quickness
+D:and subtlety, while the other gives endurance and strength.
+
+
+# The Mattock of Nain
+
+N:174:of Nain
+I:20:7:6
+W:60:5:250:30000
+P:0:3d8:12:18:0
+F:TUNNEL | INFRA | SEARCH | STR | ESP_ORC | CLIMB |
+F:SLAY_ORC | SLAY_TROLL | SLAY_GIANT | SLAY_DRAGON |
+F:BRAND_ACID | RES_ACID | RES_DARK | RES_DISEN |
+F:ACTIVATE
+a:HARDCORE=STONE_MUD
+D:Wielded by Nain of the Iron Hills at the Battle of Azanulbizar, this great
+D:mattock brought victory to the Dwarves over Azog's Orcs - though Nain
+D:himself fell at the last, even with victory already assured.
+
+
+# The Ball-and-Chain of Fundin Bluecloak
+
+N:175:of Fundin Bluecloak
+I:21:6:4
+W:25:100:130:60000
+P:0:5d4:13:17:10
+F:STR | WIS | SPEED | LITE1 | HIDE_TYPE |
+F:SLAY_EVIL | SLAY_UNDEAD |
+F:RES_FIRE | RES_ELEC | RES_NETHER | RES_DISEN | HOLD_LIFE |
+F:ACTIVATE
+F:COULD2H
+f:COULD2H
+a:HARDCORE=FUNDIN
+D:The weapon of one of the great dwarven priests, with powers
+D:to preserve body, soul and enchantments, and the bane of those
+D:who seek life beyond death.
+
+
+# The Large Leather Shield of the Haradrim
+
+N:176:of the Haradrim
+I:34:4:2
+W:35:12:120:25000
+P:4:1d2:0:0:15
+F:ACTIVATE |
+F:STR | CON | SUST_STR | SUST_CON | HIDE_TYPE |
+F:RES_FEAR | RES_BLIND | RES_POIS | AGGRAVATE
+a:HARDCORE=HARADRIM
+D:A great shield from the far lands of the South, whose wielder
+D:will go charging into battle heedless of danger, with the
+D:strength and endurance of a madman. Nor will he fear poison, for
+D:the Southron barbarians handle poisoned darts naturally.
+
+
+# The Lead-Filled Mace 'Skullcleaver'
+
+N:177:'Skullcleaver'
+I:21:15:5
+W:30:15:500:60000
+P:0:5d4:11:23:20
+F:STR | TUNNEL | INFRA | HIDE_TYPE |
+F:CURSED | AGGRAVATE | NO_MAGIC |
+F:RES_NEXUS | RES_BLIND | RES_SOUND |
+F:KILL_DRAGON | SLAY_ANIMAL | BRAND_POIS | BRAND_ELEC |
+F:ACTIVATE
+F:COULD2H
+f:COULD2H
+a:HARDCORE=SKULLCLEAVER
+D:This mighty bludgeon brings destruction to all around it, and is the
+D:bane of dragons and magic.
+
+
+# The Set of Gauntlets of Eol
+
+N:178:of Eol
+I:31:2:3
+W:55:35:25:40000
+P:3:1d1:0:0:15
+F:INT | MANA | FREE_ACT | FEATHER | RES_ELEC | RES_DARK | RES_POIS | ACTIVATE
+F:LUCK | SPELL_CONTAIN | WIELD_CAST
+a:HARDCORE=EOL
+D:The iron-shod gauntlets of the Dark Elven smith Eol, tingling with magics
+D:that he could channel in battle.
+
+
+# The Pair of Hard Leather Boots of Nevrast
+
+N:179:of Nevrast
+I:30:3:3
+W:20:8:40:35000
+P:3:1d1:0:0:13
+F:STEALTH | CON | SPEED | HIDE_TYPE
+D:Footgear made of bear leather and set with opals, which grant the wearer
+D:silent, hasted movement.
+
+
+# The Pair of Metal Shod Boots of Gimli
+
+N:180:of Gimli
+I:30:6:4
+W:40:8:60:22500
+P:4:1d1:5:5:10
+F:INFRA | SEARCH | TUNNEL | CLIMB | HIDE_TYPE
+Z:magic map
+D:A set of iron-shod boots stamped by Gimli's combat prowess, a peerless
+D:ally to those journeying through halls of stone under mountains.
+
+
+# The demon garbs of Gothmog
+# The Demonblade of Gothmog
+
+N:181:of Gothmog
+I:115:55:-20
+W:10:0:150:500
+P:0:7d6:13:13:0
+F:SHOW_MODS | SLAY_DEMON | SLAY_EVIL | BRAND_FIRE | BRAND_POIS
+F:LUCK | CHAOTIC | LITE1 | WOUNDING | RES_MORGUL | WIELD_CAST
+F:HEAVY_CURSE | AUTO_CURSE
+
+
+# The Demonshield of Gothmog
+
+N:182:of Gothmog
+I:115:56:4
+W:15:0:70:500
+P:13:1d1:0:0:13
+F:DEX | INVIS | SUST_STR | SUST_CON | SUST_DEX
+F:FEATHER | SH_FIRE | FREE_ACT | HOLD_LIFE
+F:HEAVY_CURSE | AUTO_CURSE | WIELD_CAST
+
+
+# The Demonhorn of Gothmog
+
+N:183:of Gothmog
+I:115:57:-5
+W:20:0:30:500
+P:2:1d1:0:0:13
+F:LITE2 | REGEN | ESP_DEMON
+F:CHR | SLOW_DIGEST | SEE_INVIS
+F:HEAVY_CURSE | AUTO_CURSE | WIELD_CAST
+
+
+# The Long Sword 'Durandil'
+# The name comes from a french parody of dungeon dwelling in mp3
+# http://penofchaos.com/warham.htm
+N:184:'Durandil'
+I:23:17:3
+W:5:10:130:500
+P:0:2d5:5:6:0
+F:RES_FEAR | LUCK
+F:ACTIVATE | SHOW_MODS
+a:SPELL=Artifact Durandil
+D:Don't go adventuring without your Durandil sword!
+
+
+# The Phial of Undeath
+
+N:200:of Undeath
+I:39:103:-5
+W:20:10:10:0
+P:0:1d1:0:0:0
+F:CURSED | INT | WIS | CON | DEX | CHR | STR | ACTIVATE |
+F:LITE3 | LITE2 | LUCK | MAGIC_BREATH
+F:INSTA_ART | DG_CURSE | ESP_UNDEAD |
+a:HARDCORE=UNDEATH
+D:It appears like the Phial of Galadriel at first - but wait! It
+D:is a cursed phial created by an evil wizard to lure adventurers
+D:into wielding it unknowingly.
+
+
+# The template for artifacts corpses
+
+N:201:
+I:9:1:0
+W:200:1:10:0
+P:0:1d1:0:0:0
+F:INSTA_ART | SPECIAL_GENE
+
+
+# The Palantir of Orthanc
+
+N:202:of Orthanc
+I:39:104:2
+W:75:60:200:100000
+P:0:10d10:0:0:0
+F:WIS | INT | SEARCH | INFRA | HIDE_TYPE | ACTIVATE | ESP_ALL |
+F:SEE_INVIS | RES_BLIND | AGGRAVATE | DRAIN_MANA | LITE2
+F:INSTA_ART
+a:HARDCORE=PALANTIR
+D:A shining white ball of unbreakable crystal, the ancient Palantiri
+D:were used by kings of Numenor and later by the Exiles for rapid
+D:communication between distant lands. Nothing is hidden from one who
+D:gazes into a Palantir, but the observed will also be aware of the
+D:observer, as was Sauron when Saruman tried to spy on him with this
+D:particular Palantir.
+
+
+# The Ring of Phasing
+
+N:203:of Phasing
+I:45:55:15
+W:110:0:2:3000000
+P:0:1d1:0:0:0
+F:SPEED | SEE_INVIS | LUCK | MAGIC_BREATH
+F:CURSED | HEAVY_CURSE | REGEN
+F:WRAITH | IM_NETHER | DRAIN_EXP | HOLD_LIFE | SPECIAL_GENE |
+F:INSTA_ART
+Z:teleport
+D:Imbued with the screams of the victims of undead everywhere, this
+D:ring is more a hole in reality than anything else. Strange forces ripple over
+D:its surface, giving you visions of a reality considerably less solid than this
+D:one. You feel your senses reel, and must make a conscious effort not to throw
+D:this ring as far from you as possible.
+
+
+# The Blue Stone 'Toris Mejistos'
+
+N:204:'Toris Mejistos'
+I:40:18:2
+W:50:10:3:60000
+Z:restore life
+F:INT | WIS | HIDE_TYPE | MANA | LUCK
+F:SUST_INT | SUST_WIS | LITE1 | REGEN
+F:SLOW_DIGEST | AUTO_CURSE | HEAVY_CURSE
+F:ESP_GOOD | ESP_EVIL | HOLD_LIFE
+F:INSTA_ART | SPECIAL_GENE | WATER_BREATH
+F:SPELL_CONTAIN | WIELD_CAST
+D:A blue stone, with an incredible number of incredibly small runes of power
+D:on it. It carries many secrets.
+
+
+# The Ring of Durin - last of the Seven Rings of the Dwarf-lords
+
+N:205:of Durin
+I:45:57:2
+W:70:70:2:65000
+F:CON | CHR | STR | SUST_CHR | SUST_CON | SUST_STR | HIDE_TYPE |
+F:ESP_EVIL | AGGRAVATE | HEAVY_CURSE | HOLD_LIFE | DRAIN_EXP |
+F:RES_DARK | RES_CHAOS | RES_NETHER | RES_COLD | RES_ACID |
+F:INSTA_ART | SPECIAL_GENE | CURSED
+Z:Midas touch
+D:The greatest of the Seven Rings of the Dwarf-lords, and the last to be
+D:lost. Alone among the Seven, it was not taken by Sauron when he made
+D:war on the Elves, but was given as a gift from Celebrimbor to King Durin
+D:III of Moria in token of friendship: nevertheless, Sauron in disguise
+D:had a hand in its making, and so it is cursed, and draws evil towards it.
+
+
+# The Elfstone 'Elessar'
+
+N:206:'Elessar'
+I:40:19:4
+W:60:60:3:40000
+P:0:0d0:7:7:10
+F:STR | WIS | CHR | SPEED | LITE3 | INSTA_ART |
+F:RES_FEAR | RES_FIRE | RES_POIS | RES_DISEN | HIDE_TYPE |
+F:ACTIVATE
+a:HARDCORE=ELESSAR
+D:This green gem glows with inner light. Aragorn son of Arathorn wore
+D:it at the Battle of the Pelennor Fields, and he was himself given the
+D:name of 'Elessar' by the people of Gondor because of this.
+
+
+# The Jewel 'Evenstar'
+
+N:207:'Evenstar'
+I:40:20:3
+W:50:50:3:35000
+F:HOLD_LIFE | SUST_CON | SUST_WIS | SUST_INT | LITE1 | CON |
+F:RES_DARK | RES_COLD | RES_NETHER | REGEN | INSTA_ART |
+F:ACTIVATE
+a:HARDCORE=REST_ALL
+D:A pure white jewel, the last gift of Queen Arwen Undomiel to Frodo
+D:Baggins, intended to be worn around his neck on the chain that had
+D:once borne the One Ring.
+
+
+# The Palantir of Minas Ithil (be warned - it's *cursed*!)
+
+N:208:of Minas Ithil
+I:39:107:-3
+W:75:60:200:0
+P:0:10d10:0:0:-30
+F:LIFE | CON | INT | WIS | ESP_ALL | LITE3 | LITE1
+F:CURSED | HEAVY_CURSE | TY_CURSE | DRAIN_EXP |
+F:RES_BLIND | SEE_INVIS | ACTIVATE
+a:HARDCORE=PALANTIR
+D:A shining white ball of unbreakable crystal, the ancient Palantiri
+D:were used by kings of Numenor and later by the Exiles for rapid
+D:communication between distant lands. This Palantir, however, was
+D:taken by Sauron long ago, and mastered to his evil uses, to the
+D:destruction of all others who would gaze into it.
+
+
+# some artifact bolts
+# The Silver Bolt 'Balefire'
+
+N:209:'Balefire'
+I:18:3:0
+W:55:30:2:50000
+P:0:6d5:20:15:0
+F:ESP_DEMON | ESP_UNDEAD | LITE1 |
+F:BRAND_FIRE | KILL_DEMON | KILL_UNDEAD |
+D:This silver-tipped bolt, ablaze with undying celestial fire,
+D:is especially potent against undead and creatures of the
+D:netherworld; it even points the way to places where such
+D:enemies lurk.
+
+
+# The Silver Bolt 'Stone-biter'
+
+N:210:'Stone-biter'
+I:18:3:3
+W:55:30:2:50000
+P:0:6d5:20:15:0
+F:ESP_ORC | ESP_TROLL |
+F:INFRA | SEARCH | TUNNEL | LUCK
+F:BRAND_ACID | SLAY_ORC | SLAY_TROLL |
+D:Wherever it strikes, this silver-tipped bolt eats through rock
+D:and metal as easily as through flesh. The dwarf-smith who
+D:crafted Stone-biter also inscribed the shaft with powerful
+D:doom-spells against the orcs and trolls who had destroyed his
+D:ancestral home.
+
+
+# The Seeker Bolt 'Heart's Blood'
+
+N:211:'Heart's Blood'
+I:18:2:5
+W:85:40:3:35000
+P:0:8d5:15:20:0
+F:VORPAL | WOUNDING | CRIT |
+D:The barbed head of this bolt glows deep red with terrible runes
+D:of destruction; legend has it that Heart's Blood cannot hit its
+D:mark without causing a mortal wound.
+
+
+# The Seeker Bolt 'Scale-piercer'
+
+N:212:'Scale-piercer'
+I:18:2:0
+W:85:40:3:35000
+P:0:8d5:15:20:0
+F:ESP_DRAGON | RES_FEAR |
+F:KILL_DRAGON |
+D:This bolt, crafted from the bones of a Great Wyrm, is less famous
+D:and less powerful than Bard's black arrow. Nonetheless it enables
+D:the owner to find dragons unerringly, face them bravely, and kill
+D:them swiftly.
+
+
+# Artifacts from ToME 3.0.0 for the new maps of Lord Dimwit
+# The pval was set to the average of the flag values.
+
+
+# The Mage Staff of Forochel
+
+N:213:of Forochel
+I:6:1:3
+W:65:70:60:60000
+P:0:3d4:-12:-8:0
+F:INT | WIS | MANA | SPELL | INFRA | SEE_INVIS
+F:SUST_INT | SUST_WIS | RES_BLIND | IM_COLD | SENS_FIRE
+F:SPECIAL_GENE | WIELD_CAST
+F:COULD2H
+f:COULD2H
+D:A shaft of pure, invincible crystal cut from the heart of one
+D:of the great glaciers ringing the Ice-Bay of Forochel.
+D:While you hold it, your mind feels as clear as the winter sky.
+
+
+# The Elven Cloak of Mellyrn
+
+N:214:of Mellyrn
+I:35:2:4
+W:40:40:5:65000
+P:4:0d0:0:0:20
+F:HIDE_TYPE | INVIS | DEX | SPEED | STEALTH | LUCK
+F:SUST_DEX | RES_LITE | RES_DARK | SPECIAL_GENE
+D:Bearing the same lyrical name as the great trees of Lothlorien
+D:and containing in its close-woven folds the speed and skill of
+D:the Galadrim, this grey cloak is ideal for those who travel in
+D:forests.
+
+
+# The Bluesteel Blade of Ephel Duath
+
+N:215:of Ephel Duath
+I:23:31:-3
+W:60:60:50:30000
+P:0:2d6:-20:-18:0
+F:STR | WIS | CHR | BRAND_POIS | VAMPIRIC | VORPAL
+F:INVIS | AGGRAVATE | CURSED | HEAVY_CURSE | SHOW_MODS
+F:SPECIAL_GENE
+D:This filthy orc-blade is famed for vile deeds of torture and blood,
+D:and its wielder will never cease to fear treachery.
+
+
+# The Slaughter Axe 'Garachoth'
+
+N:216:'Garachoth'
+I:24:30:2
+W:70:300:400:91000
+P:0:7d5:18:18:-20
+F:STR | CON | SPEED | LEVELS | BLACK_BREATH
+F:KILL_DEMON | SLAY_ANIMAL | BRAND_FIRE | VORPAL
+F:RES_FEAR | RES_FIRE | RES_CHAOS | RES_NETHER
+F:HIDE_TYPE | SHOW_MODS | SPECIAL_GENE
+D:A ghastly axe with the soul of a demon lord trapped inside, this horrifying
+D:creation reverberates with the screams of the damned. As you gaze into its
+D:glassy, translucent blade, it seems that endless sulphrous wastelands
+D:stretch away from you into the distance, obscured by sheets of fire.
+
+
+# The Set of Cesti 'Skycleaver'
+
+N:217:'Skycleaver'
+I:31:5:1
+W:40:45:40:100000
+P:5:1d1:16:7:16
+F:STR | CON | DEX | CHR | LUCK | FLY
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS
+F:HIDE_TYPE | SHOW_MODS | SPECIAL_GENE
+D:The handgear of a legendary dragonslaying hero. The wearer of these
+D:wyrmskin gauntlets will be versed in all aerial ways, and will fear no
+D:dragon that walks or flies.
+
+
+# The Pair of Metal Shod Boots of the Machine
+# Replaced the stealth malus with AGGRAVATE
+
+N:218:of the Machine
+I:30:6:3
+W:30:100:170:19000
+P:6:1d1:0:0:24
+F:INT | SPEED | TUNNEL | AGGRAVATE
+F:RES_CHAOS | RES_SHARDS | RES_CONF
+F:ESP_NONLIVING | HIDE_TYPE | SPECIAL_GENE
+D:A massive pair of adamantine boots studded with gold, the final and
+D:greatest product of the petty-dwarven magical forge. Despite
+D:the great powers they contain, they are heavy and awkward enough to
+D:make quite a racket whenever you move.
diff --git a/lib/edit/ab_info.txt b/lib/edit/ab_info.txt
new file mode 100644
index 00000000..7b9aa152
--- /dev/null
+++ b/lib/edit/ab_info.txt
@@ -0,0 +1,118 @@
+# File: ab_info.txt
+
+
+# This file is used to initialize the "lib/data/ab_info.raw" file, which is
+# used to initialize the "abilities" information for the ToME game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# The ToME abilities indexes are defined in "defines.h", and must not be changed.
+# If you want to add new ones, add them after the tome ones
+
+# N:idx:name
+# D:desc
+# I:cost(in skill points)
+# A:action mkey:action desc
+
+# Prerequisites
+# k:level:skill
+# S:level(linear mode):stats
+# a:needed ability
+
+# E:excluding ability:excluding ability
+
+# If you need more sophisticated prereqs use the HOOK_LEARN_ABILITY
+
+# Version stamp (required)
+
+# Do not forget to update misc.txt with an entry like the following :
+# Maximum number of traits in ab_info.txt
+# M:b:50
+
+V:2.2.0
+
+N:0:Spread blows
+D:If a monster dies to your attack but you still have blows left
+D:you won't lose the full turn, allowing you to attack some other
+D:monster in the same turn
+D:Prereq: Weaponmastery skill@30, Dex@17
+I:5
+k:30:Weaponmastery
+S:17:DEX
+
+N:1:Tree walking
+D:Allows you to walk in dense forest
+D:Prereq: Nature skill@20
+I:7
+k:20:Nature
+
+N:2:Perfect casting
+D:Allows you to reach 0% failure rate on spells
+D:Prereq: Magic skill@35
+I:6
+k:35:Magic
+
+N:3:Extra Max Blow(1)
+D:Increases your max possible blows number by 1
+D:Prereq: Combat@10
+I:7
+k:10:Combat
+
+N:4:Extra Max Blow(2)
+D:Increases your max possible blows number by 1
+D:Prereq: Combat@20, Extra Max Blow(1)
+I:7
+k:20:Combat
+a:Extra Max Blow(1)
+
+N:5:Ammo creation
+D:Allows you to create shots, arrows and bolts from various materials
+D:Prereq: Archery@10
+A:10:Forge ammo
+I:8
+k:10:Archery
+
+N:6:Touch of death
+D:Your melee blows can insta-kill, but you only receive 1/3 of the experience
+D:Prereq: Necromancy@50, Combat@40, DEX@30, STR@30
+A:100:Activate touch of death
+I:15
+k:50:Necromancy
+k:40:Combat
+S:30:DEX
+S:30:STR
+
+N:7:Artifact Creation
+D:In combination with a high alchemy skill this ability will let you
+D:design your very own artifacts
+D:Prereq: Alchemy@40, INT@35, WIS@35
+I:70
+k:40:Alchemy
+S:35:INT
+S:35:WIS
+
+N:8:Far reaching attack
+D:You can attack an enemy one square far using a long polearm.
+D:At high levels of Polearm-mastery skill, you can even hit two enemies at once.
+D:Prereq: Combat@15, Polearm-mastery@15
+I:10
+A:102:Far reaching attack
+k:15:Combat
+k:15:Polearm-mastery
+
+N:9:Trapping
+D:Ability to set monster traps
+D:Prereq: Disarming@15
+I:10
+A:14:Set trap
+k:15:Disarming
+
+N:10:Undead Form
+D:Ability to turn into a weak undead being when you "die".
+D:You must then kill enough monsters to absorb enough life energy
+D:to come back to life.
+D:Prereq: Necromancy@30, INT@25
+I:15
+k:30:Necromancy
+S:25:INT
diff --git a/lib/edit/al_info.txt b/lib/edit/al_info.txt
new file mode 100644
index 00000000..fbd4c9a0
--- /dev/null
+++ b/lib/edit/al_info.txt
@@ -0,0 +1,2097 @@
+# File: al_info.txt
+
+
+# This file is used to initialize the "lib/raw/al_info.raw" file, which is
+# used as the alchemist recipes in ToME
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes.
+
+# Version stamp (required)
+
+V:2.0.0
+
+
+#Note: when you add anything to this file, you also need to change the M:a
+#line of misc.txt. Unlike other files, there is no 'count' entry. The
+#easiest and fastest way to find out what to say in misc.txt is:
+#
+# grep '^[Ia]:' <al.txt | wc -l
+# you need to add one to the result of the above statement.
+#
+# Which only works on unix systems. But if you have the misfortune not to
+#be working on a unix system, you can use an editor and to a global search
+#and replace, searching for I: and then for a:, which will give you a count
+#that's a few high, but will still work.
+
+
+#Format: There's only one kind of line here, and that's the
+# Item line. Goes like this:
+
+
+# I:tval:sval:Qty:essence
+#
+# The problem here is that it doesn't matter what order things are in.
+# Since the tval and sval are specified on each line, the lines that
+# describe a particular recipe for a particular item don't have to be
+# anywhere even close to each other in the file. This could cause a problem:
+# if the same item has two different entries for the same essence, it will
+# find one, assume that the player has enough, and display that one in green.
+# But when the player goes to make it, they may not have enough. It would also
+# cause weird recipe displays, with one essence listed twice. Funny-looking
+
+
+
+# Some special tvals:
+# 0 Not a tval, used internally by the parser to store the artifact flag essences.
+# We don't currently have any mechanism to control which tvals flags are used on.
+# 1 Not actually a tval, used for ego items, in which case the 'sval' is the e_idx
+# We don't need tvals or svals for ego items, because that information is
+# contained in the e_info record, which the e_idx points us to.
+#
+# 40 Amulet
+# 45 ring
+# 55 staff
+# 65 wand
+# 66 rod tip
+# 70 scroll
+# 71 potion
+# 40 Amulet
+# 45 ring
+# 55 staff
+# 65 wand
+# 66 rod tip
+# 70 scroll
+# 71 potion
+# 72 potion2
+# 80 Food ('shrooms, etc)
+
+# I:tval:sval:Qty:essence
+
+#**********************Mushrooms***********************
+
+#SV_FOOD_POISON 0
+I:80:0:1:LIFE
+I:80:0:8:POISON
+
+#SV_FOOD_BLINDNESS 1
+I:80:1:1:LIFE
+I:80:1:6:DARKNESS
+I:80:1:2:LITE
+
+#SV_FOOD_PARANOIA 2
+I:80:2:1:LIFE
+I:80:2:4:KNOWLEDGE
+I:80:2:4:CONFUSION
+
+#SV_FOOD_CONFUSION 3
+I:80:3:1:LIFE
+I:80:3:8:CONFUSION
+
+#SV_FOOD_HALLUCINATION 4
+I:80:4:1:LIFE
+I:80:4:4:CONFUSION
+I:80:4:4:MANA
+I:80:4:4:TELEPORT
+
+#SV_FOOD_PARALYSIS 5
+I:80:5:1:LIFE
+I:80:5:8:FORCE
+
+#SV_FOOD_WEAKNESS 6
+I:80:6:1:LIFE
+I:80:6:4:POISON
+I:80:6:4:FORCE
+
+#SV_FOOD_SICKNESS 7
+I:80:7:8:LIFE
+I:80:7:1:POISON
+
+#SV_FOOD_STUPIDITY 8
+I:80:8:1:LIFE
+I:80:8:1:MANA
+I:80:8:8:KNOWLEDGE
+
+#SV_FOOD_NAIVETY 9
+I:80:9:1:LIFE
+I:80:9:1:MANA
+I:80:9:4:CONFUSION
+I:80:9:4:KNOWLEDGE
+
+#SV_FOOD_UNHEALTH 10
+I:80:10:8:LIFE
+I:80:10:1:FORCE
+I:80:10:1:POISON
+
+#SV_FOOD_DISEASE 11
+I:80:11:1:LIFE
+I:80:11:1:TIME
+I:80:11:8:POISON
+
+#SV_FOOD_CURE_POISON 12
+I:80:12:1:LIFE
+I:80:12:8:POISON
+
+#SV_FOOD_CURE_BLINDNESS 13
+I:80:13:1:LIFE
+I:80:13:6:LITE
+I:80:13:2:DARKNESS
+
+#SV_FOOD_CURE_PARANOIA 14
+I:80:14:1:LIFE
+I:80:14:1:TIME
+
+#SV_FOOD_CURE_CONFUSION 15
+I:80:15:1:LIFE
+I:80:15:12:KNOWLEDGE
+
+#SV_FOOD_CURE_SERIOUS 16
+I:80:16:8:LIFE
+
+#SV_FOOD_RESTORE_STR 17
+I:80:17:20:LIFE
+I:80:17:2:TIME
+
+#SV_FOOD_RESTORE_CON 18
+I:80:18:20:LIFE
+I:80:18:2:TIME
+
+#SV_FOOD_RESTORING 19
+I:80:19:40:LIFE
+I:80:19:4:TIME
+
+#/* many missing mushrooms */
+#Note - the comment below appears on the list in defines.h, but I can't find
+#any more mushrooms in k_info.txt
+
+#define SV_FOOD_BISCUIT 32
+#define SV_FOOD_JERKY 33
+#define SV_FOOD_RATION 35
+#define SV_FOOD_SLIME_MOLD 36
+#define SV_FOOD_WAYBREAD 37
+#define SV_FOOD_PINT_OF_ALE 38
+#define SV_FOOD_PINT_OF_WINE 39
+#define SV_FOOD_ATHELAS 40
+#define SV_FOOD_GREAT_HEALTH 41
+#define SV_FOOD_FORTUNE_COOKIE 42
+
+
+
+#***************************Artifact Flags************************************
+#
+#A:Group:tval:sval:pval:pval?:level:xp
+# The first three describe the required item, they can be left unspecified
+# for no object, or specify starting with tval for increasingly specific
+# objects.
+# Note: pval? is boolean (0 or 1) if true, then this flag has a
+# variable effect, and we should require more experience and times for
+# increasing pvals.
+# Note:for tval=TV_CORPSE, sval=corpse type, pval=monster idx,
+# or use f:moster_race_flags, or leave all blank for any corpse at all.
+#F:object flag to be set
+#D:Description of flag
+#x:Description of activation (instead of description of flag, see below)
+#d:Description of required item
+#p:Description of required item (plural, optional. Illegal if pval != 1)
+#a:qty:object_flag_to_be_set Essence_name (not used)
+
+# Note that like I: lines, a: lines can be anywhere and in any order.
+# Note: 'flag' is the flag name from a_info.txt,
+# 'monster_race_flag' is from r_info.txt
+
+#The group numbers are 1-5, and the descriptions of the groups
+#are hard coded. see cmd7.c
+
+
+A:1:45:24:1:1:40:5000
+F:STR
+D:Add to Strength
+d:Ring of Strength
+p:Rings of Strength
+
+A:1:45:25:1:1:43:5000
+F:INT
+D:Add to Intelligence
+d:Ring of Intelligence
+p:Rings of Intelligence
+
+A:1:40:28:1:1:46:5000
+F:WIS
+D:Add to Wisdom
+d:Amulet of Wisdom
+p:Amulets of Wisdom
+
+A:1:45:26:1:1:46:5000
+F:DEX
+D:Add to Dexterity
+d:Ring of Dexterity
+p:Rings of Dexterity
+
+A:1:45:27:1:1:42:5000
+F:CON
+D:Add to Constitution
+d:Ring of Constitution
+p:Rings of Constitution
+
+A:1:40:2:0:1:30:5000
+F:CHR
+D:Add to Charisma
+d:Amulet of Adornment
+p:Amulets of Adornment
+
+A:1:45:10:0:0:32:1000
+F:SUST_STR
+D:Sustain Strength
+d:Ring of Sustain Strength
+
+A:1:45:11:0:0:34:1000
+F:SUST_INT
+D:Sustain Intelligence
+d:Ring of Sustain Intelligence
+
+A:1:45:12:0:0:28:1000
+F:SUST_WIS
+D:Sustain Wisdom
+d:Ring of Sustain Wisdom
+
+A:1:45:14:0:0:36:1000
+F:SUST_DEX
+D:Sustain Dexterity
+d:Ring of Sustain Dexterity
+
+A:1:45:13:0:0:36:1000
+F:SUST_CON
+D:Sustain Constitution
+d:Ring of Sustain Constitution
+
+A:1:45:15:0:0:25:1000
+F:SUST_CHR
+D:Sustain Charisma
+d:Ring of Sustain Charisma
+
+A:1:45:31:1:1:40:50000
+F:SPEED
+D:Speed
+d:Ring of Speed
+p:Rings of Speed
+
+A:1:45:49:1:1:38:150000
+F:BLOWS
+D:Extra Attacks
+d:Ring of Extra Attacks
+p:Rings of Extra Attacks
+
+A:1:30:2:0:1:32:5000
+F:STEALTH
+D:Stealthy
+d:Left Insole from a Used Soft Boot
+p:Left Insoles from Used Soft Boots
+
+A:1:36:1:0:1:29:2000
+F:SEARCH
+D:Adds to Searching
+d:Filthy Rag
+p:Filthy Rags
+
+A:1:39:1:0:1:6:1000
+F:INFRA
+D:Helps Infravision
+d:Brass Lantern
+p:Brass Lanterns
+
+A:1:9:-1:5:1:30:1000
+F:LUCK
+D:Lucky
+d:Rabbit's Left Forefoot
+p:Rabbit's Left Forefeet
+
+A:1:20:4:0:1:25:30000
+F:TUNNEL
+D:Aids in digging
+d:Pick
+p:Picks
+
+A:1:9:1:0:1:40:50000
+F:LIFE
+f:TROLL
+D:Multiplies Life
+d:Troll's Heart
+p:Troll's Hearts
+
+A:2:71:8:0:0:20:15000
+F:INVIS
+D:Invisibility
+d:Potion of Invisibility
+
+A:2:71:8:0:0:20:4000
+F:SEE_INVIS
+D:See Invisible
+d:Potion of Invisibility
+
+A:2:5:0:0:0:20:30000
+F:FREE_ACT
+D:Free Action
+d:Iron Spike
+
+A:2:34:5:0:0:38:90000
+F:REFLECT
+D:Reflection
+d:Large Metal Shield
+
+A:2:9:1:644:0:20:30000
+F:SH_FIRE
+D:Aura of Fire
+d:Lungs from an Ancient Red Dragon
+
+A:2:9:1:601:0:25:30000
+F:SH_ELEC
+D:Aura of Lightning
+d:Lungs from an Ancient Blue Dragon
+
+A:2:39:2:0:0:8:1000
+F:LITE1
+D:Light
+d:Everburning Torch
+
+A:2:39:3:0:0:20:10000
+F:LITE2
+D:Bright Light
+d:Dwarven Lantern
+
+A:2:39:4:0:0:40:100000
+F:LITE3
+D:Sunlight
+d:Feanorian Lamp
+
+A:2:38:-1:0:0:40:200000
+F:FLY
+D:Flight
+d:Suit of Dragon Armour (any colour)
+
+A:2:9:-1:862:0:50:10000000
+F:AUTO_ID
+D:Automatically IDs
+d:Morgoth's Testicles
+
+A:2:40:14:0:0:29:2000
+F:NO_TELE
+D:Anti-Teleportation
+d:Teleport Inhibiting Amulet
+
+A:2:40:13:0:0:34:2000
+F:NO_MAGIC
+D:Anti-Magic
+d:Magic Inhibiting Amulet
+
+A:2:71:62:0:0:50:100000
+F:WRAITH
+D:Wraith Form
+d:Potion of Invulnerability
+
+A:2:71:33:0:0:15:1000
+F:FEATHER
+D:Levitation
+d:Potion of Berserk Strength
+
+A:2:80:37:0:0:20:10000
+F:SLOW_DIGEST
+D:Slow Digestion
+d:Lembas Wafer
+
+A:2:80:10:0:0:32:20000
+F:REGEN
+D:Regenerate
+d:Mushroom of Unhealth
+
+A:2:80:3:0:0:12:20000
+F:TELEPORT
+D:Teleport
+d:Mushroom of Confusion
+
+A:3:21:2:0:1:30:20000
+F:CRIT
+D:Extra Critical Hits
+d:Whip
+p:Whips
+
+A:3:23:30:0:0:30:30000
+F:WOUNDING
+D:Wounds Monsters
+d:Blade of Chaos
+
+A:3:66:18:0:1:26:6000
+F:VAMPIRIC
+D:Vampiric
+d:Rod Tip of Drain Life
+
+A:3:9:1:0:0:16:2000
+F:SLAY_ANIMAL
+f:ANIMAL
+D:Slay Animal
+d:Dead Animal's Body
+
+A:3:9:-1:0:0:25:2000
+F:SLAY_EVIL
+f:EVIL
+D:Slay Evil
+d:Evil Dead Thing's Remains
+
+A:3:9:-1:0:0:30:2000
+F:SLAY_UNDEAD
+f:UNDEAD
+D:Slay Undead
+d:Remains of Undead Monster
+
+A:3:9:1:0:0:40:1500
+F:SLAY_DEMON
+f:DEMON
+D:Slay Demon
+d:Demon's Corpse
+
+A:3:9:1:0:0:10:700
+F:SLAY_ORC
+f:ORC
+D:Slay Orc
+d:Dead Orc
+
+A:3:9:1:0:0:16:700
+F:SLAY_TROLL
+f:TROLL
+D:Slay Troll
+d:Dead Troll
+
+A:3:9:1:0:0:25:900
+F:SLAY_GIANT
+f:GIANT
+D:Slay Giant
+d:Dead Giant
+
+A:3:9:1:0:0:33:2000
+F:SLAY_DRAGON
+f:DRAGON
+D:Slay Dragon
+d:Dead Dragon (any size will do)
+
+A:3:9:-1:593:0:41:5000
+F:KILL_DRAGON
+D:*Slay* Dragon
+d:Mature Multi-Hued Dragon's Remains
+
+A:3:9:-1:0:0:41:90000
+F:KILL_UNDEAD
+f:S_HI_UNDEAD
+D:*Slay* Undead
+d:Dead Summoner of Greater Undead
+
+A:3:9:-1:996:0:41:90000
+F:KILL_DEMON
+D:*Slay* Demon
+d:Lesser Balrog's Corpse
+
+A:3:0:0:0:0:36:20000
+F:VORPAL
+D:Vorpal
+
+A:3:0:0:0:0:40:90000
+F:IMPACT
+D:Earthquakes
+
+A:3:0:0:0:0:3:2000
+F:BRAND_POIS
+D:Poison Brand
+
+A:3:0:0:0:0:12:2000
+F:BRAND_ACID
+D:Acid Brand
+
+A:3:0:0:0:0:10:2000
+F:BRAND_ELEC
+D:Lightning Brand
+
+A:3:0:0:0:0:6:2000
+F:BRAND_FIRE
+D:Fire Brand
+
+A:3:0:0:0:0:8:2000
+F:BRAND_COLD
+D:Frost Brand
+
+A:3:0:0:0:1:30:3000
+F:XTRA_MIGHT
+D:Extra Might (Bows Only)
+
+A:3:0:0:0:1:35:3000
+F:XTRA_SHOTS
+D:Extra Shots (Bows Only)
+
+A:4:9:1:624:0:49:500000
+F:IM_ACID
+D:Immune to Acid
+d:Ancient Black Dragon's Foreskin
+
+A:4:9:1:601:0:50:500000
+F:IM_ELEC
+D:Immune to Lightning
+d:Ancient Blue Dragon's Foreskin
+
+A:4:9:1:644:0:49:500000
+F:IM_FIRE
+D:Immune to Fire
+d:Ancient Red Dragon's Foreskin
+
+A:4:9:1:617:0:50:500000
+F:IM_COLD
+D:Immune to Cold
+d:Ancient White Dragon's Foreskin
+
+A:4:40:8:0:0:30:30000
+F:HOLD_LIFE
+D:Hold Life
+d:Amulet of the Magi
+
+A:4:45:17:0:0:12:10000
+F:RES_ACID
+D:Resist Acid
+d:Ring of Acid
+
+A:4:45:56:0:0:15:10000
+F:RES_ELEC
+D:Resist Lightning
+d:Ring of Lightning
+
+A:4:71:30:0:0:13:10000
+F:RES_FIRE
+D:Resist Fire
+d:Potion of Resist Heat
+
+A:4:71:31:0:0:14:10000
+F:RES_COLD
+D:Resist Cold
+d:Potion of Resist Cold
+
+A:4:71:27:0:0:25:30000
+F:RES_POIS
+D:Resist Poison
+d:Potion of Cure Poison
+
+A:4:45:38:0:0:26:10000
+F:RES_FEAR
+D:Resist Fear
+d:Ring of Fear Resistance
+
+A:4:45:39:0:0:31:60000
+F:RES_LITE
+D:Resist Light
+d:Ring of Light and Darkness Resistance
+
+A:4:45:39:0:0:31:60000
+F:RES_DARK
+D:Resist Darkness
+d:Ring of Light and Darkness Resistance
+
+A:4:45:47:0:0:30:30000
+F:RES_BLIND
+D:Resist Blindness
+d:Ring of Blindness Resistance
+
+A:4:45:43:0:0:30:30000
+F:RES_CONF
+D:Resist Confusion
+d:Ring of Confusion Resistance
+
+A:4:45:42:0:0:30:60000
+F:RES_SOUND
+D:Resist Sound
+d:Ring of Sound Resistance
+
+A:4:45:44:0:0:30:60000
+F:RES_SHARDS
+D:Resist Shards
+d:Ring of Shard Resistance
+
+A:4:45:40:0:0:30:60000
+F:RES_NETHER
+D:Resist Nether
+d:Ring of Nether Resistance
+
+A:4:45:41:0:0:30:60000
+F:RES_NEXUS
+D:Resist Nexus
+d:Ring of Nexus Resistance
+
+A:4:45:46:0:0:30:60000
+F:RES_CHAOS
+D:Resist Chaos
+d:Ring of Chaos Resistance
+
+A:4:45:45:0:0:30:60000
+F:RES_DISEN
+D:Resist Disenchantment
+d:Ring of Disenchantment Resistance
+
+A:5:9:1:0:0:50:-100000
+F:TEMPORARY
+D:Temporary Item
+d:Corpse, any corpse
+
+A:5:36:1:0:0:10:-2000
+F:AUTO_CURSE
+D:Self-Cursing
+d:Filthy Rag
+
+A:5:80:40:0:0:45:-10000
+F:BLACK_BREATH
+D:Causes the Black Breath
+d:Sprig of Athelas
+
+A:5:70:15:0:0:40:-5000
+F:TY_CURSE
+D:Ancient Curse
+d:Scroll of *Remove Curse*
+
+A:5:0:0:0:0:40:-5000
+F:DRAIN_EXP
+D:Drains your Experience
+
+A:5:0:0:0:0:30:-5000
+F:AGGRAVATE
+D:Aggravates Monsters
+
+A:5:70:14:0:0:30:-500
+F:CURSED
+D:Curse
+d:Scroll of Remove Curse
+
+#Removed for balance - allows you to trade two essences of extra life,
+# and 25+ magic essence for 10000xp on your artifact, which isn't
+# anything to sneeze at. Curse, above, has the same problem, but
+# for 1000xp, I figure it's a fair trade.
+#A:5:70:15:0:0:40:-10000
+#D:Heavy Curse
+#F:HEAVY_CURSE
+#d:Scroll of *Remove Curse*
+
+A:5:0:0:0:0:50:-5000
+F:PERMA_CURSE
+D:Permanently Cursed
+
+A:5:0:0:0:0:35:-2000
+F:CURSE_NO_DROP
+D:Can't be Dropped
+
+A:5:0:0:0:0:45:-5000
+F:DRAIN_HP
+D:Drains your Hit Points
+
+A:5:0:0:0:0:20:-50000
+F:IMMOVABLE
+D:Wielder Can't Move
+
+#/* Floating eye corpse for esp all :) other ESP's don't require anything at all...*/
+A:5:9:1:32:0:40:20000
+F:ESP_ALL
+D:Telepathy
+d:Formerly Floating Eye
+
+A:5:0:0:0:0:25:3000
+F:ESP_ORC
+D:Sense Orcs
+
+A:5:0:0:0:0:25:3000
+F:ESP_TROLL
+D:Sense Trolls
+
+A:5:0:0:0:0:25:5000
+F:ESP_DRAGON
+D:Sense Dragons
+
+A:5:0:0:0:0:25:5000
+F:ESP_GIANT
+D:Sense Giants
+
+A:5:0:0:0:0:25:5000
+F:ESP_DEMON
+D:Sense Demons
+
+A:5:0:0:0:0:25:5000
+F:ESP_UNDEAD
+D:Sense Undead
+
+A:5:0:0:0:0:25:5000
+F:ESP_EVIL
+D:Sense Evil
+
+A:5:0:0:0:0:25:5000
+F:ESP_ANIMAL
+D:Sense Animals
+
+A:5:0:0:0:0:25:5000
+F:ESP_THUNDERLORD
+D:Sense Thunderlords
+
+A:5:0:0:0:0:25:5000
+F:ESP_GOOD
+D:Sense Good
+
+A:5:0:0:0:0:25:5000
+F:ESP_NONLIVING
+D:Sense Nonliving
+
+A:5:0:0:0:0:25:5000
+F:ESP_UNIQUE
+D:Sense Unique Monsters
+
+A:5:0:0:0:0:25:2000
+F:ESP_SPIDER
+D:Sense Spiders
+
+
+#***************************Activations for artifacts***********************
+# Activations follow all of the rules for artifact flags.
+# except: group number and pval are IGNORED
+# They MUST come after ALL artifact flags in this file!!!
+# There is no way (currently) to require essences...
+#
+# They use a LOWER CASE x: instead of the F: object flag
+#
+# all ACT_ constants are supported, anything else must be coded into init1.c
+# Internally, they are assigned the magic group number of '88'
+# and a NEGATIVE flag number (which is the activation number)
+# Note that although you can use the p: to give activations a plural
+# item description, it will never be used, because pval is forced to 0.
+#
+#A:<îgnored>:tval:sval:<ignored>:<ignored>:level:xp
+# tval and sval describe the required item, they can be left unspecified
+# for no object, or specify starting with tval for increasingly specific
+# objects.
+#F:object flag to be set
+#D:Description of flag
+#x:Description of activation (instead of description of flag)
+#d:Description of required item
+#p:Description of required item (plural, not used for activations)
+#a:qty:object_flag_to_be_set Essence_name
+
+#define ACT_PET_SUMMON 150
+#define ACT_CURE_PARA 151
+#define ACT_CURE_HALLU 152
+#define ACT_CURE_POIS 153
+#define ACT_CURE_HUNGER 154
+#define ACT_CURE_STUN 155
+#define ACT_CURE_CUTS 156
+#define ACT_CURE_FEAR 157
+#define ACT_CURE_CONF 158
+#define ACT_CURE_BLIND 159
+#define ACT_CURING 160
+#define ACT_ACQUIREMENT 163
+#define ACT_MUT 166
+#define ACT_CURE_INSANITY 167
+#define ACT_CURE_MUT 168
+#define ACT_REST_LIFE 84
+#define ACT_REST_ALL 85
+#define ACT_CURE_LW 81
+#define ACT_CURE_MW 82
+#define ACT_CURE_POISON 83
+#define ACT_CURE_700 86
+#define ACT_CURE_1000 87
+
+#define ACT_LIGHT 111
+
+#define ACT_SUNLIGHT 1
+A:0:70:15:0:0:40:40000
+x:SUNLIGHT
+D:Sunlight
+d:Brass Lantern
+
+#define ACT_MAP_LIGHT 112
+#define ACT_DETECT_ALL 113
+#define ACT_DETECT_XTRA 114
+#define ACT_ID_FULL 115
+#define ACT_ID_PLAIN 116
+
+#define ACT_GROW_MOLD 197
+
+
+#define ACT_BO_MISS_1 2
+A:0:0:0:0:0:20:4000
+x:BO_MISS_1
+D:Magic Missile (1)
+
+#define ACT_BO_MISS_2 15
+A:0:0:0:0:0:30:300000
+x:BO_MISS_2
+D:Magic Missile (2)
+
+#define ACT_BA_MISS_3 24
+A:0:0:0:0:0:40:400000
+x:BA_MISS_3
+D:Ball of Missiles
+
+#define ACT_BO_ELEC_1 4
+A:0:0:0:0:0:30:300000
+x:BO_ELEC_1
+D:Bolt of Lightning
+
+#define ACT_BA_ELEC_2 12
+A:0:0:0:0:0:30:300000
+x:BA_ELEC_2
+D:Ball of Lightning
+
+#define ACT_BA_ELEC_3 18
+A:0:0:0:0:0:35:350000
+x:BA_ELEC_3
+D:Ball of Lightning(2)
+
+#define ACT_BA_ELEC_H 172
+A:0:0:0:0:0:40:400000
+x:BA_ELEC_H
+D:Ball of Lightning(3)
+
+#define ACT_BA_ELEC_4 183
+A:0:0:0:0:0:40:400000
+x:BA_ELEC_4
+D:Ball of Lightning(4)
+
+#define ACT_BR_ELEC 184
+A:0:0:0:0:0:45:450000
+x:BR_ELEC
+D:Breathe Lightning
+
+#define ACT_BO_ACID_1 5
+#define ACT_BA_COLD_1 8
+#define ACT_BA_ACID_H 173
+#define ACT_BA_ACID_4 182
+#define ACT_BR_ACID 187
+#define ACT_BO_COLD_1 6
+#define ACT_BA_COLD_2 11
+#define ACT_BA_COLD_3 17
+#define ACT_BA_COLD_H 171
+#define ACT_BA_COLD_4 180
+#define ACT_BR_COLD 185
+#define ACT_BO_FIRE_1 7
+#define ACT_BA_FIRE_1 9
+#define ACT_BA_FIRE_2 16
+#define ACT_BA_FIRE_H 170
+#define ACT_BA_FIRE_4 181
+#define ACT_BR_FIRE 186
+#define ACT_BA_POIS_1 3
+#define ACT_BA_POIS_4 179
+#define ACT_BR_POIS 188
+#define ACT_BR_MANY 189
+#define ACT_BR_CONF 190
+#define ACT_BR_SOUND 191
+#define ACT_BR_CHAOS 192
+#define ACT_BR_SHARD 193
+#define ACT_BR_BALANCE 194
+#define ACT_BR_LIGHT 195
+#define ACT_BR_POWER 196
+#define ACT_ROCKET 22
+A:0:0:0:0:0:50:40000
+x:ROCKET
+D:Fire a Rocket
+
+#define ACT_JUMP 177
+#define ACT_WHIRLWIND 19
+#define ACT_CALL_CHAOS 21
+#define ACT_DISP_EVIL 23
+#define ACT_DISP_GOOD 25
+#define ACT_DAWN 61
+#define ACT_CHARM_ANIMAL 65
+#define ACT_CHARM_UNDEAD 66
+#define ACT_CHARM_OTHER 67
+#define ACT_CHARM_ANIMALS 68
+#define ACT_CHARM_OTHERS 69
+#define ACT_SUMMON_ANIMAL 70
+#define ACT_SUMMON_PHANTOM 71
+#define ACT_SUMMON_ELEMENTAL 72
+#define ACT_SUMMON_DEMON 73
+#define ACT_SUMMON_UNDEAD 74
+#define ACT_RUNE_EXPLO 117
+#define ACT_RUNE_PROT 118
+#define ACT_SATIATE 119
+#define ACT_DEST_DOOR 120
+#define ACT_STONE_MUD 121
+#define ACT_RECHARGE 122
+#define ACT_ALCHEMY 123
+#define ACT_DIM_DOOR 124
+#define ACT_TELEPORT 125
+#define ACT_RECALL 126
+
+#define ACT_SPIN 174
+#define ACT_NOLDOR 175
+#define ACT_SPECTRAL 176
+#define ACT_DEST_TELE 178
+#define ACT_DRAIN_1 10
+#define ACT_DRAIN_2 13
+#define ACT_VAMPIRE_1 14
+#define ACT_VAMPIRE_2 20
+#define ACT_GILGALAD 26
+#define ACT_CELEBRIMBOR 27
+#define ACT_SKULLCLEAVER 28
+#define ACT_HARADRIM 29
+#define ACT_FUNDIN 30
+#define ACT_EOL 31
+#define ACT_UMBAR 32
+#define ACT_NUMENOR 33
+#define ACT_KNOWLEDGE 34
+#define ACT_UNDEATH 35
+#define ACT_THRAIN 36
+#define ACT_BARAHIR 37
+#define ACT_TULKAS 38
+#define ACT_NARYA 39
+#define ACT_NENYA 40
+#define ACT_VILYA 41
+#define ACT_POWER 42
+#define ACT_STONE_LORE 43
+#define ACT_RAZORBACK 44
+#define ACT_BLADETURNER 45
+#define ACT_MEDIATOR 46
+#define ACT_BELEGENNON 47
+#define ACT_GORLIM 48
+#define ACT_COLLUIN 49
+#define ACT_BELANGIL 50
+#define ACT_CONFUSE 51
+#define ACT_SLEEP 52
+#define ACT_QUAKE 53
+#define ACT_TERROR 54
+#define ACT_TELE_AWAY 55
+#define ACT_BANISH_EVIL 56
+#define ACT_GENOCIDE 57
+#define ACT_MASS_GENO 58
+#define ACT_ANGUIREL 59
+#define ACT_ERU 60
+#define ACT_FIRESTAR 62
+#define ACT_TURMIL 63
+#define ACT_CUBRAGOL 64
+#define ACT_ELESSAR 75
+#define ACT_GANDALF 76
+#define ACT_MARDA 77
+#define ACT_PALANTIR 78
+#define ACT_ROBINTON 79
+#define ACT_PIEMUR 80
+#define ACT_MENOLLY 88
+#define ACT_EREBOR 89
+#define ACT_DRUEDAIN 90
+#define ACT_ESP 91
+#define ACT_BERSERK 92
+#define ACT_PROT_EVIL 93
+#define ACT_RESIST_ALL 94
+#define ACT_SPEED 95
+#define ACT_XTRA_SPEED 96
+#define ACT_WRAITH 97
+#define ACT_INVULN 98
+#define ACT_ROHAN 99
+#define ACT_HELM 100
+#define ACT_BOROMIR 101
+#define ACT_HURIN 102
+#define ACT_AXE_GOTHMOG 103
+#define ACT_MELKOR 104
+#define ACT_GROND 105
+#define ACT_NATUREBANE 106
+#define ACT_NIGHT 107
+#define ACT_ORCHAST 108
+
+#define ACT_DEATH 127
+#define ACT_RUINATION 128
+#define ACT_DESTRUC 129
+#define ACT_UNINT 130
+#define ACT_UNSTR 131
+#define ACT_UNCON 132
+#define ACT_UNCHR 133
+#define ACT_UNDEX 134
+#define ACT_UNWIS 135
+#define ACT_STATLOSS 136
+#define ACT_HISTATLOSS 137
+#define ACT_EXPLOSS 138
+#define ACT_HIEXPLOSS 139
+#define ACT_SUMMON_MONST 140
+#define ACT_PARALYZE 141
+#define ACT_HALLU 142
+#define ACT_POISON 143
+#define ACT_HUNGER 144
+#define ACT_STUN 145
+#define ACT_CUTS 146
+#define ACT_PARANO 147
+#define ACT_CONFUSION 148
+#define ACT_BLIND 149
+#define ACT_DARKNESS 161
+#define ACT_LEV_TELE 162
+#define ACT_WEIRD 164
+#define ACT_AGGRAVATE 165
+#define ACT_LIGHT_ABSORBTION 169
+#define ACT_MUSIC 200
+
+
+#***************************Amulets***********************
+
+#SV_AMULET_ADORNMENT
+I:40:2:4:DARKNESS
+
+#SV_AMULET_BRILLANCE
+I:40:6:1:KNOWLEDGE
+I:40:6:2:CONFUSION
+
+#SV_AMULET_CHARISMA
+I:40:7:4:DARKNESS
+
+#SV_AMULET_DEVOTION
+I:40:25:1:MAGIC
+I:40:25:2:CONFUSION
+I:40:25:8:KNOWLEDGE
+
+#SV_AMULET_ESP
+I:40:22:4:KNOWLEDGE
+
+#SV_AMULET_INFRA
+I:40:26:1:LITE
+
+#SV_AMULET_NO_MAGIC
+I:40:13:4:MAGIC
+
+#SV_AMULET_NO_TELE
+I:40:14:8:TELEPORT
+
+#SV_AMULET_REFLECTION
+I:40:9:10:FORCE
+I:40:9:10:MANA
+
+#SV_AMULET_REGENERATION
+I:40:30:4:LIFE
+
+#SV_AMULET_RESISTANCE
+I:40:15:4:ACID
+I:40:15:4:COLD
+I:40:15:4:FIRE
+I:40:15:4:LIGHTNING
+
+#SV_AMULET_RESIST_ACID
+I:40:4:4:ACID
+
+#SV_AMULET_RESIST_ELEC
+I:40:29:4:LIGHTNING
+
+#SV_AMULET_SEARCHING
+I:40:5:4:KNOWLEDGE
+I:40:5:4:LITE
+
+#SV_AMULET_SERPENT
+I:40:17:1:MANA
+I:40:17:4:ACID
+
+#SV_AMULET_SLOW_DIGEST
+I:40:3:4:LIFE
+
+#SV_AMULET_SUSTENANCE
+I:40:21:8:LIFE
+
+#SV_AMULET_TELEPORT
+I:40:1:8:TELEPORT
+
+#SV_AMULET_THE_MAGI
+I:40:8:1:MAGIC
+I:40:8:4:KNOWLEDGE
+
+#SV_AMULET_TRICKERY
+#I:40:23:1:MAGIC
+I:40:23:8:CONFUSION
+
+#SV_AMULET_WEAPONMASTERY
+I:40:24:12:EXPLOSION
+I:40:24:1:MAGIC
+
+#SV_AMULET_WISDOM
+I:40:28:1:KNOWLEDGE
+I:40:28:2:CONFUSION
+
+#*********Potions******************************
+#Note that these first few potions are the ones which
+#can be thrown for much damage, and are thus an integral part of
+#the alchemist's arsenal.
+
+#SV_POTION_DETONATIONS
+I:71:22:6:EXPLOSION
+
+#SV_POTION_DEATH
+I:71:23:10:LIFE
+
+#SV_POTION_RUINATION
+I:71:15:5:DARKNESS
+
+#SV_POTION_APPLE_JUICE
+I:71:1:1:LIFE
+
+#SV_POTION_AUGMENTATION
+I:71:55:16:POISON
+I:71:55:24:CONFUSION
+I:71:55:6:MAGIC
+I:71:55:8:EXPLOSION
+I:71:55:16:LIFE
+I:71:55:8:LIGHTNING
+I:71:55:16:DARKNESS
+
+#SV_POTION_BESERK_STRENGTH
+I:71:33:1:FIRE
+
+#SV_POTION_BLINDNESS
+I:71:7:1:DARKNESS
+
+#SV_POTION_BOLDNESS
+I:71:28:1:LITE
+
+#SV_POTION_CONFUSION
+I:71:9:1:CONFUSION
+
+#SV_POTION_CURE_CRITICAL
+I:71:36:4:LIFE
+
+#SV_POTION_CURE_LIGHT
+I:71:34:1:LIFE
+
+#SV_POTION_CURE_SERIOUS
+I:71:35:2:LIFE
+
+#SV_POTION_CURING
+I:71:61:1:LIFE
+
+#SV_POTION_DEC_CHR
+I:71:21:1:MANA
+I:71:21:8:DARKNESS
+I:71:21:8:CONFUSION
+
+#SV_POTION_DEC_CON
+I:71:20:1:MANA
+I:71:20:8:LIFE
+I:71:20:8:POISON
+
+#SV_POTION_DEC_DEX
+I:71:19:1:MANA
+I:71:19:8:LIGHTNING
+I:71:19:8:DARKNESS
+
+#SV_POTION_DEC_INT
+I:71:17:1:MANA
+I:71:17:8:CONFUSION
+I:71:17:8:KNOWLEDGE
+
+#SV_POTION_DEC_STR
+I:71:16:1:MANA
+I:71:16:8:EXPLOSION
+I:71:16:8:POISON
+
+#SV_POTION_DEC_WIS
+I:71:18:1:MANA
+I:71:18:8:CONFUSION
+I:71:18:8:LIFE
+
+#SV_POTION_DETECT_INVIS
+I:71:25:1:DARKNESS
+I:71:25:1:LITE
+
+#SV_POTION_ENLIGHTENMENT
+I:71:56:8:KNOWLEDGE
+
+#SV_POTION_EXPERIENCE
+I:71:59:30:EXTRALIFE
+
+#SV_POTION_HEALING
+I:71:37:1:EXTRALIFE
+
+#SV_POTION_HEROISM
+I:71:32:1:COLD
+
+#SV_POTION_INC_CHR
+I:71:53:1:MAGIC
+I:71:53:8:DARKNESS
+I:71:53:8:CONFUSION
+
+#SV_POTION_INC_CON
+I:71:52:1:MAGIC
+I:71:52:8:LIFE
+I:71:52:8:POISON
+
+#Potion of Slow Poison
+I:71:26:1:POISON
+
+#Potion of Cure Poison
+I:71:27:2:POISON
+
+#Potion of Poison
+I:71:6:1:POISON
+
+#SV_POTION_INC_DEX
+I:71:51:1:MAGIC
+I:71:51:8:LIGHTNING
+I:71:51:8:DARKNESS
+
+#SV_POTION_INC_INT
+I:71:49:1:MAGIC
+I:71:49:8:KNOWLEDGE
+I:71:49:8:CONFUSION
+
+#SV_POTION_INC_STR
+I:71:48:1:MAGIC
+I:71:48:8:EXPLOSION
+I:71:48:8:POISON
+
+#SV_POTION_INC_WIS
+I:71:50:1:MAGIC
+I:71:50:8:CONFUSION
+I:71:50:8:LIFE
+
+#SV_POTION_INFRAVISION
+I:71:24:1:LITE
+
+#SV_POTION_INVIS
+I:71:8:4:DARKNESS
+
+#SV_POTION_INVULNERABILITY
+I:71:62:10:FORCE
+I:71:62:4:MAGIC
+
+#SV_POTION_LIFE
+I:71:39:8:EXTRALIFE
+
+#SV_POTION_LOSE_MEMORIES
+I:71:13:20:DARKNESS
+
+#SV_POTION_MUTATION
+I:71:10:12:CHAOS
+
+#SV_POTION_NEW_LIFE
+I:71:63:16:EXTRALIFE
+
+#SV_POTION_RESISTANCE
+I:71:60:2:ACID
+I:71:60:2:COLD
+I:71:60:2:FIRE
+I:71:60:2:LIGHTNING
+
+#SV_POTION_RESIST_COLD
+I:71:31:1:COLD
+
+#SV_POTION_RESIST_HEAT
+I:71:30:1:FIRE
+
+#SV_POTION_RESTORE_EXP
+I:71:41:8:LIFE
+I:71:41:3:KNOWLEDGE
+
+#SV_POTION_RESTORE_MANA
+I:71:40:12:MANA
+
+#SV_POTION_RES_CHR
+I:71:47:12:LIFE
+
+#SV_POTION_RES_CON
+I:71:46:12:LIFE
+
+#SV_POTION_RES_DEX
+I:71:45:12:LIFE
+
+#SV_POTION_RES_INT
+I:71:43:12:LIFE
+
+#SV_POTION_RES_STR
+I:71:42:12:LIFE
+
+#SV_POTION_RES_WIS
+I:71:44:12:LIFE
+
+#SV_POTION_SALT_WATER
+I:71:5:1:LIGHTNING
+
+#SV_POTION_SELF_KNOWLEDGE
+I:71:58:2:KNOWLEDGE
+
+#SV_POTION_SLEEP
+I:71:11:1:MANA
+
+#SV_POTION_SLIME_MOLD
+I:71:2:1:LIFE
+
+#SV_POTION_SLOWNESS
+I:71:4:1:MANA
+
+#SV_POTION_SPEED
+I:71:29:1:TIME
+
+#SV_POTION_STAR_ENLIGHTENMENT
+I:71:57:12:KNOWLEDGE
+
+#SV_POTION_STAR_HEALING
+I:71:38:4:EXTRALIFE
+
+#SV_POTION_WATER
+I:71:0:1:LIFE
+
+#********************************Potion 2*********************************
+
+#SV_POTION2_MIMIC 1
+I:72:1:2:LIFE
+#SV_POTION2_CURE_LIGHT_SANITY 14
+I:72:14:1:CONFUSION
+I:72:14:1:DARKNESS
+#SV_POTION2_CURE_SERIOUS_SANITY 15
+I:72:15:2:CONFUSION
+I:72:15:2:DARKNESS
+#SV_POTION2_CURE_CRITICAL_SANITY 16
+I:72:16:4:CONFUSION
+I:72:16:4:DARKNESS
+#SV_POTION2_CURE_SANITY 17
+I:72:17:8:CONFUSION
+I:72:17:8:DARKNESS
+#SV_POTION2_CURE_WATER 18
+#I:72:18:4:EXPLOSION
+#I:72:18:4:POISON
+#I:72:18:2:EXTRALIFE
+
+#************Rod Tips.****************************
+
+#SV_ROD_ACID_BALL
+I:66:24:1:MAGIC
+I:66:24:8:ACID
+
+#SV_ROD_ACID_BOLT
+I:66:20:4:ACID
+
+#SV_ROD_COLD_BALL
+I:66:27:1:MAGIC
+I:66:27:8:COLD
+
+#SV_ROD_COLD_BOLT
+I:66:23:4:COLD
+
+#SV_ROD_CURING
+I:66:8:3:LIFE
+
+#SV_ROD_DETECTION
+I:66:6:8:KNOWLEDGE
+
+#SV_ROD_DETECT_DOOR
+I:66:1:1:KNOWLEDGE
+
+#SV_ROD_DETECT_TRAP
+I:66:29:1:KNOWLEDGE
+
+#SV_ROD_DISARMING
+I:66:14:4:TELEPORT
+
+#SV_ROD_DRAIN_LIFE
+I:66:18:10:LIFE
+
+#SV_ROD_ELEC_BALL
+I:66:25:1:MAGIC
+I:66:25:8:LIGHTNING
+
+#SV_ROD_ELEC_BOLT
+I:66:21:4:LIGHTNING
+
+#SV_ROD_FIRE_BALL
+I:66:26:1:MAGIC
+I:66:26:8:FIRE
+
+#SV_ROD_FIRE_BOLT
+I:66:22:4:FIRE
+
+#SV_ROD_HAVOC
+I:66:28:5:CHAOS
+
+#SV_ROD_HEALING
+I:66:9:4:EXTRALIFE
+
+#SV_ROD_IDENTIFY
+I:66:2:4:MANA
+I:66:2:4:KNOWLEDGE
+
+#SV_ROD_ILLUMINATION
+I:66:4:4:LITE
+
+#SV_ROD_LITE
+I:66:15:1:LITE
+
+#SV_ROD_MAPPING
+I:66:5:1:KNOWLEDGE
+I:66:5:8:LITE
+
+#SV_ROD_POLYMORPH
+I:66:19:1:CHAOS
+
+#SV_ROD_PROBING
+I:66:7:20:KNOWLEDGE
+I:66:7:3:MANA
+
+#SV_ROD_RECALL
+I:66:3:3:FORCE
+I:66:3:9:TELEPORT
+
+#SV_ROD_RESTORATION
+I:66:10:30:LIFE
+
+#SV_ROD_SLEEP_MONSTER
+I:66:16:1:MANA
+
+#SV_ROD_SLOW_MONSTER
+I:66:17:1:TIME
+
+#SV_ROD_SPEED
+I:66:11:10:TIME
+
+#SV_ROD_TELEPORT_AWAY
+I:66:13:8:TELEPORT
+
+#**************************Scrolls ********************
+
+#SV_SCROLL_ACQUIREMENT
+I:70:46:10:MAGIC
+
+#SV_SCROLL_ARTIFACT
+I:70:52:99:MAGIC
+
+#SV_SCROLL_BLESSING
+I:70:33:1:LIFE
+
+#SV_SCROLL_CHAOS
+I:70:50:2:CHAOS
+
+#SV_SCROLL_DARKNESS
+I:70:0:1:DARKNESS
+
+#SV_SCROLL_DETECT_DOOR
+I:70:29:1:KNOWLEDGE
+
+#SV_SCROLL_DETECT_GOLD
+I:70:26:1:KNOWLEDGE
+
+#SV_SCROLL_DETECT_INVIS
+I:70:30:1:DARKNESS
+I:70:30:1:KNOWLEDGE
+
+#SV_SCROLL_DETECT_ITEM
+I:70:27:2:KNOWLEDGE
+
+#SV_SCROLL_DETECT_TRAP
+I:70:28:1:KNOWLEDGE
+
+#SV_SCROLL_DISPEL_UNDEAD
+I:70:42:1:EXTRALIFE
+
+#SV_SCROLL_ENCHANT_ARMOR
+I:70:16:3:EXPLOSION
+
+#SV_SCROLL_ENCHANT_WEAPON_PVAL
+I:70:19:10:MAGIC
+
+#SV_SCROLL_ENCHANT_WEAPON_TO_DAM
+I:70:18:3:EXPLOSION
+
+#SV_SCROLL_ENCHANT_WEAPON_TO_HIT
+I:70:17:3:LITE
+
+#SV_SCROLL_FIRE
+I:70:48:1:FIRE
+
+#SV_SCROLL_GENOCIDE
+I:70:44:1:FORCE
+I:70:44:5:DARKNESS
+
+#SV_SCROLL_HOLY_CHANT
+I:70:34:2:LIFE
+
+#SV_SCROLL_HOLY_PRAYER
+I:70:35:3:LIFE
+
+#SV_SCROLL_ICE
+I:70:49:1:COLD
+
+#SV_SCROLL_IDENTIFY
+I:70:12:1:KNOWLEDGE
+
+#SV_SCROLL_LIGHT
+I:70:24:1:LITE
+
+#SV_SCROLL_MAPPING
+I:70:25:2:KNOWLEDGE
+I:70:25:5:LITE
+
+#SV_SCROLL_MASS_GENOCIDE
+I:70:45:1:FORCE
+I:70:45:30:DARKNESS
+
+#SV_SCROLL_MONSTER_CONFUSION
+I:70:36:1:CONFUSION
+
+#SV_SCROLL_PHASE_DOOR
+I:70:8:1:TELEPORT
+
+#SV_SCROLL_PROTECTION_FROM_EVIL
+I:70:37:1:MANA
+I:70:37:9:LIFE
+
+#SV_SCROLL_RECHARGING
+I:70:22:4:LIGHTNING
+
+#SV_SCROLL_REMOVE_CURSE
+I:70:14:1:LIFE
+
+#SV_SCROLL_RESET_RECALL
+I:70:23:1:FORCE
+
+#SV_SCROLL_RUMOR
+I:70:51:1:LIGHTNING
+
+#SV_SCROLL_RUNE_OF_PROTECTION
+I:70:38:1:EXTRALIFE
+I:70:38:6:FORCE
+
+#SV_SCROLL_SATISFY_HUNGER
+I:70:32:1:LIFE
+
+#SV_SCROLL_STAR_ACQUIREMENT
+I:70:47:20:MAGIC
+
+#SV_SCROLL_STAR_DESTRUCTION
+I:70:41:12:FORCE
+
+#SV_SCROLL_STAR_ENCHANT_ARMOR
+I:70:20:9:EXPLOSION
+
+#SV_SCROLL_STAR_ENCHANT_WEAPON
+I:70:21:9:EXPLOSION
+I:70:21:9:LITE
+
+#SV_SCROLL_STAR_IDENTIFY
+I:70:13:1:MAGIC
+I:70:13:20:KNOWLEDGE
+
+#SV_SCROLL_STAR_REMOVE_CURSE
+I:70:15:1:EXTRALIFE
+
+#SV_SCROLL_TELEPORT
+I:70:9:1:TELEPORT
+
+#SV_SCROLL_TELEPORT_LEVEL
+I:70:10:5:TELEPORT
+
+#SV_SCROLL_TRAP_CREATION
+I:70:7:1:CONFUSION
+I:70:7:1:TELEPORT
+
+#SV_SCROLL_TRAP_DOOR_DESTRUCTION
+I:70:39:1:FORCE
+
+#SV_SCROLL_WORD_OF_RECALL
+I:70:11:1:FORCE
+I:70:11:3:TELEPORT
+
+#***************************Staves************************
+
+#Globe of Light
+I:55:3:1:LITE
+#Fiery Shield
+I:55:4:2:FIRE
+I:55:4:1:MANA
+#Remove Curse
+I:55:5:2:LIFE
+#Wings of Winds
+I:55:6:1:FORCE
+#Shake
+I:55:7:4:FORCE
+#Disarm
+I:55:8:1:FORCE
+I:55:8:1:KNOWLEDGE
+#Teleportation
+I:55:9:1:TELEPORT
+#Probability Travel
+I:55:10:1:MANA
+I:55:10:1:TELEPORT
+#11 Recovery
+I:55:11:1:LIFE
+#12 Healing
+I:55:12:1:EXTRALIFE
+#13 Vision
+I:55:13:1:LITE
+#Identify
+I:55:14:1:KNOWLEDGE
+I:55:14:1:MANA
+#Sense Hidden
+I:55:15:1:KNOWLEDGE
+#Reveal Ways
+I:55:16:1:KNOWLEDGE
+#Sense Monsters
+I:55:17:1:KNOWLEDGE
+#Genocide
+I:55:18:1:FORCE
+I:55:18:5:DARKNESS
+#19 Summon
+I:55:19:1:LIFE
+I:55:19:1:TELEPORT
+#Wish
+I:55:20:99:MAGIC
+#Mana
+I:55:21:1:MANA
+#Sterilize
+I:55:24:1:POISON
+I:55:24:1:MANA
+
+#********************Wands*****************
+
+#MannaThrust
+I:65:3:4:MANA
+
+#FireFlash
+I:65:4:1:FIRE
+
+#FireWall
+I:65:5:4:FIRE
+I:65:5:1:MANA
+
+#Tidal Wave
+I:65:6:1:POISON
+I:65:6:1:ACID
+I:65:6:1:COLD
+
+#Ice Storm
+I:65:7:4:COLD
+I:65:7:1:MANA
+
+#Wand of Noxious Cloud
+I:65:8:1:POISON
+
+#Poison Blood
+I:65:9:2:POISON
+I:65:9:1:LIFE
+
+#Thunderstorm
+I:65:10:4:LIGHTNING
+
+#DIG
+I:65:11:1:FORCE
+
+#Stone Prison
+I:65:12:3:FORCE
+I:65:12:1:MANA
+
+#Strike
+I:65:13:1:FORCE
+#Teleport Away
+I:65:14:1:TELEPORT
+#Summon Animal
+I:65:15:4:LIFE
+I:65:15:1:MANA
+#MageLock
+I:65:16:1:FORCE
+#Slow Monster
+I:65:17:1:MANA
+#Essence of Speed
+I:65:18:1:TIME
+#Banishment
+I:65:19:2:TELEPORT
+#20 Disperse Magic
+I:65:20:4:LIFE
+I:65:20:4:MANA
+#Charm
+I:65:21:1:MANA
+I:65:21:1:LIFE
+#Confuse
+I:65:22:1:CONFUSION
+#Deamon Blade
+I:65:23:1:FORCE
+I:65:23:1:FIRE
+#Heal Monster
+I:65:24:1:LIFE
+#25 Haste Monster
+I:65:25:1:MANA
+
+
+
+#RINGS*********************************************************
+
+#ring of poison resistance
+I:45:20:8:POISON
+
+#Ring of Critical Hits
+I:45:59:8:MANA
+
+#Damage
+I:45:29:1:EXPLOSION
+
+#Slaying
+I:45:30:8:EXPLOSION
+I:45:30:8:LITE
+
+#Sound Resistance
+I:45:42:4:EXPLOSION
+I:45:42:4:LITE
+
+#Shard Resistance
+I:45:44:4:EXPLOSION
+I:45:44:4:TELEPORT
+
+#Flying
+I:45:54:12:TELEPORT
+
+#Extra Attacks
+I:45:49:8:TELEPORT
+I:45:49:4:TIME
+
+#Teleportation
+I:45:4:8:TELEPORT
+
+#Lordly Protection
+I:45:48:4:TELEPORT
+I:45:48:12:LITE
+I:45:48:1:EXTRALIFE
+
+#Ring of Ice
+I:45:19:8:COLD
+
+#Ring of Cold Resistance
+I:45:9:4:COLD
+
+#Ring of Flames
+I:45:18:8:FIRE
+
+#Ring of Fire Resistance
+I:45:8:4:FIRE
+
+#Ring of Acid
+I:45:17:8:ACID
+
+#Ring of Disenchantment Resistance
+I:45:45:8:ACID
+I:45:45:2:CHAOS
+
+#Ring of slow digestion
+I:45:6:4:LIFE
+I:45:6:1:TIME
+
+#ring of confusion resistance
+I:45:43:8:CONFUSION
+
+#Ring of Stupidity
+I:45:3:1:CONFUSION
+
+#Ring of Blindness Resistance
+I:45:47:8:LITE
+
+#Ring of Accuracy
+I:45:28:1:LITE
+
+#Ring of Searching
+I:45:23:4:LITE
+I:45:23:3:KNOWLEDGE
+
+#Ring of Chaos
+I:45:46:8:LITE
+
+#Ring of Light and Darkness Resistance
+I:45:39:8:LITE
+I:45:39:8:DARKNESS
+
+#Ring of Speed
+I:45:31:12:TIME
+
+#ring of Weakness
+I:45:2:1:POISON
+
+#ring of Constitution
+I:45:27:2:POISON
+I:45:27:8:LIFE
+
+#ring of Strength
+I:45:24:1:POISON
+I:45:24:1:EXPLOSION
+
+#Ring of Dexterity
+I:45:26:1:KNOWLEDGE
+I:45:26:1:LIGHTNING
+
+#ring of Sustain Constitution
+I:45:13:1:POISON
+I:45:13:4:LIFE
+I:45:13:1:MANA
+
+#ring of Sustain Strength
+I:45:10:1:POISON
+I:45:10:1:EXPLOSION
+I:45:10:1:MANA
+
+#ring of Sustain Intelligence
+I:45:11:1:CONFUSION
+I:45:11:1:MANA
+
+#ring of Intelligence
+I:45:25:1:CONFUSION
+I:45:25:12:KNOWLEDGE
+
+#Ring of Sustain Wisdom
+I:45:12:1:MANA
+I:45:12:4:CONFUSION
+
+#Ring of Sustain Dexterity
+I:45:14:1:MANA
+I:45:14:1:LIGHTNING
+
+#Ring of Sustain Charisma
+I:45:15:1:MANA
+I:45:15:1:DARKNESS
+
+#Ring of See Invisible
+I:45:22:8:DARKNESS
+
+#Ring of Invisibility
+I:45:53:8:DARKNESS
+
+#Ring of Fear resistance
+I:45:38:1:MAGIC
+
+#Ring of Levitation
+I:45:7:1:MANA
+
+#Ring of Nether Resistance
+I:45:40:1:MANA
+I:45:40:12:LIFE
+
+#Ring of Nexus Resistance
+I:45:41:1:MANA
+
+#Ring of Free Action
+I:45:21:1:FORCE
+
+#Ring of Protection
+I:45:16:10:FORCE
+
+#Ring of Lightning
+I:45:56:12:LIGHTNING
+
+#EGO ITEMS, in order by e_idx
+I:1:1:1:MAGIC },/*of Mana*/
+I:1:2:2:MAGIC },/*of Power*/
+I:1:3:3:MAGIC },/*of Wizardry*/
+I:1:4:2:MAGIC },/*of Spell*/
+I:1:5:4:ACID },/*of Resist Acid*/
+I:1:6:4:LIGHTNING },/*of Resist Lightning*/
+I:1:7:4:FIRE },/*of Resist Fire*/
+I:1:8:4:COLD },/*of Resist Cold*/
+I:1:9:4:ACID },/*of Resistance*/
+I:1:9:4:COLD },/*of Resistance*/
+I:1:9:4:FIRE },/*of Resistance*/
+I:1:9:4:LIGHTNING },/*of Resistance*/
+I:1:10:5:ACID },/*Elven*/
+I:1:10:5:COLD },/*Elven*/
+I:1:10:5:FIRE },/*Elven*/
+I:1:10:5:LIGHTNING },/*Elven*/
+I:1:11:1:MAGIC },/*of Permanence*/
+I:1:11:6:ACID },/*of Permanence*/
+I:1:11:6:COLD },/*of Permanence*/
+I:1:11:6:FIRE },/*of Permanence*/
+I:1:11:6:LIGHTNING },/*of Permanence*/
+I:1:12:1:POISON },/*of Leprousness*/
+I:1:13:3:MAGIC },/*of Immunity*/
+I:1:14:5:MAGIC },/*of Defense*/
+I:1:15:4:TELEPORT },/*of Jumping*/
+I:1:16:4:ACID },/*of Resist Acid*/
+I:1:17:4:LIGHTNING },/*of Resist Lightning*/
+I:1:18:4:FIRE },/*of Resist Fire*/
+I:1:19:4:COLD },/*of Resist Cold*/
+I:1:20:4:ACID },/*of Resistance*/
+I:1:20:4:COLD },/*of Resistance*/
+I:1:20:4:FIRE },/*of Resistance*/
+I:1:20:4:LIGHTNING },/*of Resistance*/
+I:1:21:12:FORCE },/*of Reflection*/
+I:1:22:8:LIGHTNING },/*of Electricity*/
+I:1:23:4:DARKNESS },/*of the Noldor*/
+I:1:24:4:KNOWLEDGE },/*of Intelligence*/
+I:1:25:4:KNOWLEDGE },/*of Wisdom*/
+I:1:26:4:KNOWLEDGE },/*of Beauty*/
+I:1:27:12:KNOWLEDGE },/*of the Magi*/
+I:1:28:12:EXPLOSION },/*of Might*/
+I:1:29:4:MANA },/*of Lordliness*/
+I:1:30:8:KNOWLEDGE },/*of Seeing*/
+I:1:31:1:LITE },/*of Infravision*/
+I:1:32:1:LITE },/*of Light*/
+I:1:33:8:KNOWLEDGE },/*of Telepathy*/
+I:1:34:4:LIFE },/*of Regeneration*/
+I:1:35:4:TELEPORT },/*of Teleportation*/
+I:1:40:8:EXPLOSION },/*Dwarven*/
+I:1:40:8:FIRE },/*Dwarven*/
+I:1:40:8:LIFE },/*Dwarven*/
+I:1:40:8:LITE },/*Dwarven*/
+I:1:40:8:MANA },/*Dwarven*/
+I:1:41:4:FORCE },/*of Protection*/
+I:1:42:4:DARKNESS },/*of Stealth*/
+I:1:43:4:ACID },/*of Aman*/
+I:1:43:4:COLD },/*of Aman*/
+I:1:43:4:DARKNESS },/*of Aman*/
+I:1:43:4:FIRE },/*of Aman*/
+I:1:43:4:LIGHTNING },/*of Aman*/
+I:1:44:8:FIRE },/*of Immolation*/
+I:1:48:8:LIGHTNING },/*of Electricity*/
+I:1:49:1:FORCE },/*of Free Action*/
+I:1:50:4:FORCE },/*of Slaying*/
+I:1:50:4:LITE },/*of Slaying*/
+I:1:51:4:LIGHTNING },/*of Agility*/
+I:1:52:4:EXPLOSION },/*of Power*/
+I:1:54:2:DARKNESS },/*of Charming*/
+I:1:57:3:DARKNESS },/*of Levitation*/
+I:1:58:3:DARKNESS },/*of Stealth*/
+I:1:59:3:DARKNESS },/*of Free Action*/
+I:1:60:5:TIME },/*of Speed*/
+I:1:61:2:DARKNESS },/*of Dwarvish Endurance*/
+I:1:61:2:EXPLOSION },/*of Dwarvish Endurance*/
+I:1:65:4:ACID },/*of Aman*/
+I:1:65:4:COLD },/*of Aman*/
+I:1:65:4:DARKNESS },/*of Aman*/
+I:1:65:4:FIRE },/*of Aman*/
+I:1:65:4:LIGHTNING },/*of Aman*/
+I:1:66:1:MAGIC },/*(Defender)*/
+I:1:66:4:ACID },/*(Defender)*/
+I:1:66:4:COLD },/*(Defender)*/
+I:1:66:4:FIRE },/*(Defender)*/
+I:1:66:4:LIGHTNING },/*(Defender)*/
+I:1:67:8:KNOWLEDGE },/*Blessed*/
+I:1:68:12:LIFE },/*of Greater Life*/
+I:1:68:2:MAGIC },/*of Greater Life*/
+I:1:69:4:DARKNESS },/*of Westernesse*/
+I:1:69:4:EXPLOSION },/*of Westernesse*/
+I:1:69:4:LIGHTNING },/*of Westernesse*/
+I:1:69:4:LITE },/*of Westernesse*/
+I:1:70:2:TIME },/*of Extra Attacks*/
+I:1:71:4:FORCE },/*of Slaying*/
+I:1:71:4:LITE },/*of Slaying*/
+I:1:72:4:EXPLOSION },/*of Spinning*/
+I:1:72:4:LIGHTNING },/*of Spinning*/
+I:1:73:4:ACID },/*Acidic*/
+I:1:74:4:LIGHTNING },/*Shocking*/
+I:1:75:4:FIRE },/*Fiery*/
+I:1:76:4:COLD },/*Frozen*/
+I:1:77:4:POISON },/*Venomous*/
+I:1:78:4:CHAOS },/*Chaotic*/
+I:1:79:4:FORCE },/*Sharp*/
+I:1:80:4:FORCE },/*of Earthquakes*/
+I:1:81:8:CHAOS },/*of Slay Animal*/
+I:1:82:8:CHAOS },/*of Slay Evil*/
+I:1:83:8:CHAOS },/*of Slay Undead*/
+I:1:84:8:CHAOS },/*of Slay Demon*/
+I:1:85:8:CHAOS },/*of Slay Orc*/
+I:1:86:8:CHAOS },/*of Slay Troll*/
+I:1:87:8:CHAOS },/*of Slay Giant*/
+I:1:88:8:CHAOS },/*of Slay Dragon*/
+I:1:89:12:CHAOS },/*of *Slay Animal**/
+I:1:90:12:CHAOS },/*of *Slay Evil**/
+I:1:91:12:CHAOS },/*of *Slay Undead**/
+I:1:92:12:CHAOS },/*of *Slay Demon**/
+I:1:93:12:CHAOS },/*of *Slay Orc**/
+I:1:94:12:CHAOS },/*of *Slay Troll**/
+I:1:95:12:CHAOS },/*of *Slay Giant**/
+I:1:96:12:CHAOS },/*of *Slay Dragon**/
+I:1:97:8:LIFE },/*Vampiric*/
+I:1:98:4:MAGIC },/*(*Defender*)*/
+I:1:98:8:ACID },/*(*Defender*)*/
+I:1:98:8:COLD },/*(*Defender*)*/
+I:1:98:8:FIRE },/*(*Defender*)*/
+I:1:98:8:LIGHTNING },/*(*Defender*)*/
+I:1:98:8:POISON },/*(*Defender*)*/
+I:1:99:12:TELEPORT },/*of the Thunderlords*/
+I:1:100:12:KNOWLEDGE },/*of Gondolin*/
+I:1:100:1:MAGIC },/*of Gondolin*/
+I:1:101:1:FORCE },/*of Digging*/
+I:1:102:12:LIFE },/*Spectral*/
+I:1:102:4:MANA },/*Spectral*/
+I:1:105:1:LITE },/*of Accuracy*/
+I:1:106:2:FORCE },/*of Power*/
+I:1:107:2:FORCE },/*of Extra Might*/
+I:1:108:1:FORCE },/*of Extra Shots*/
+I:1:108:1:LITE },/*of Extra Shots*/
+I:1:108:1:TIME },/*of Extra Shots*/
+I:1:109:2:FORCE },/*of Lothlorien*/
+I:1:109:2:LITE },/*of Lothlorien*/
+I:1:110:2:FORCE },/*of the Haradrim*/
+I:1:110:2:LITE },/*of the Haradrim*/
+I:1:111:2:FORCE },/*of Buckland*/
+I:1:111:2:LITE },/*of Buckland*/
+I:1:112:1:CHAOS },/*of Slay Animal*/
+I:1:113:1:CHAOS },/*of Slay Evil*/
+I:1:114:1:CHAOS },/*of Slay Undead*/
+I:1:115:1:POISON },/*of Venom*/
+I:1:116:1:ACID },/*of Acid*/
+I:1:117:12:ACID },/*Elemental*/
+I:1:117:12:COLD },/*Elemental*/
+I:1:117:12:FIRE },/*Elemental*/
+I:1:117:12:LIGHTNING },/*Elemental*/
+I:1:117:12:POISON },/*Elemental*/
+I:1:118:1:CHAOS },/*of Slay Demon*/
+I:1:119:1:CHAOS },/*of Slay Dragon*/
+I:1:120:1:FORCE },/*of Slaying*/
+I:1:120:1:LITE },/*of Slaying*/
+I:1:121:1:LIGHTNING },/*of Lightning*/
+I:1:121:1:LITE },/*of Lightning*/
+I:1:122:1:FIRE },/*of Flame*/
+I:1:122:1:LITE },/*of Flame*/
+I:1:123:1:COLD },/*of Frost*/
+I:1:123:1:LITE },/*of Frost*/
+I:1:124:1:LIFE },/*of Wounding*/
+I:1:128:2:ACID },/*of the Eldar*/
+I:1:128:2:DARKNESS },/*of the Eldar*/
+I:1:129:2:ACID },/*of Power*/
+I:1:129:2:COLD },/*of Power*/
+I:1:129:2:DARKNESS },/*of Power*/
+I:1:129:2:FIRE },/*of Power*/
+I:1:129:2:LIGHTNING },/*of Power*/
+I:1:130:1:MANA },/*Dragon*/
+I:1:131:1:MANA },/*Capacity of */
+I:1:132:1:LIFE },/*Cheapness of */
+I:1:133:1:TIME },/*Quickness of */
+I:1:134:1:TIME },/*Charging of */
+I:1:135:3:LIFE },/*the Istari of */
+I:1:135:3:MANA },/*the Istari of */
+I:1:135:3:TIME },/*the Istari of */
+I:1:136:1:LITE },/*of Boldness*/
+I:1:137:1:LITE },/*of Fearlessness*/
+I:1:138:2:LITE },/*of Illumination*/
+I:1:139:2:LITE },/*of Brightness*/
+I:1:140:4:LITE },/*of *Brightness**/
+I:1:141:4:DARKNESS },/*of the Shadows*/
+I:1:141:4:LITE },/*of the Shadows*/
+I:1:142:4:DARKNESS },/*of Infravision*/
+I:1:142:4:LITE },/*of Infravision*/
+I:1:143:8:DARKNESS },/*of the Eternal Eye*/
+I:1:144:4:MANA },/*of the Ethereal Eye*/
+I:1:146:4:DARKNESS },/*Dwarven*/
+I:1:146:4:EXPLOSION },/*Dwarven*/
+I:1:146:4:LITE },/*Dwarven*/
+I:1:147:1:MAGIC },/*Indestructible*/
+I:1:147:4:ACID },/*Indestructible*/
+I:1:147:4:COLD },/*Indestructible*/
+I:1:147:4:FIRE },/*Indestructible*/
+I:1:147:4:LIGHTNING },/*Indestructible*/
+I:1:147:4:MANA },/*Indestructible*/
+I:1:149:1:FIRE },/*Fireproof*/
+I:1:163:3:DARKNESS },/*of the Magi*/
+I:1:163:3:KNOWLEDGE },/*of the Magi*/
+I:1:163:3:MANA },/*of the Magi*/
+I:1:166:4:MAGIC },/*of Preservation*/
+I:1:166:4:MANA },/*of Preservation*/
+I:1:167:12:MANA },/*of Serenity*/
+I:1:168:4:DARKNESS },/*of Night and Day*/
+I:1:168:4:LITE },/*of Night and Day*/
+I:1:169:1:TIME },/*of the Magi*/
+I:1:169:8:KNOWLEDGE },/*of the Magi*/
+I:1:170:4:DARKNESS },/*of Invisibility*/
+I:1:171:8:DARKNESS },/*of the Bat*/
+I:1:171:8:TELEPORT },/*of the Bat*/
+I:1:172:4:LIGHTNING },/*of Thievery*/
+I:1:173:4:FORCE },/*of Combat*/
+I:1:174:4:TELEPORT },/*of Stability*/
+I:1:175:4:ACID },/*of Elvenkind*/
+I:1:175:4:COLD },/*of Elvenkind*/
+I:1:175:4:FIRE },/*of Elvenkind*/
+I:1:175:4:LIGHTNING },/*of Elvenkind*/
+I:1:176:1:MAGIC },/*of Fury*/
+I:1:176:4:CHAOS },/*of Fury*/
+I:1:176:4:EXPLOSION },/*of Fury*/
+I:1:176:4:MANA },/*of Fury*/
+I:1:178:8:FORCE },/*Magical*/
+I:1:179:4:KNOWLEDGE },/*Simplicity of */
+I:1:180:1:FIRE },/*of Warmth*/
+I:1:185:12:LIFE },/*of Life*/
+I:1:185:2:MAGIC },/*of Life*/
diff --git a/lib/edit/ba_info.txt b/lib/edit/ba_info.txt
new file mode 100644
index 00000000..8156fd2f
--- /dev/null
+++ b/lib/edit/ba_info.txt
@@ -0,0 +1,283 @@
+# File: ba_info.txt
+
+
+# This file is used to initialize the "lib/raw/ba_info.raw" file, which is
+# used to initialize the "store/building actions type" information for
+# the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# N:<index>:<name>
+# C:<hated cost>:<normal cost>:<liked cost>
+# I:<action>:<acttion restriction>:<letter>
+
+# Restriction:
+# 0 = No restrictions
+# 1 = Restrict to normal & liked
+# 2 = Restrict to liked
+
+# Version stamp (required)
+
+V:2.0.0
+
+N:0:Nothing
+C:0:0:0
+I:0:0:.
+
+N:1:Sell an item
+C:0:0:0
+I:43:0:s:d
+
+N:2:Purchase an item
+C:0:0:0
+I:44:0:p:g
+
+N:3:Examine an item
+C:0:0:0
+I:45:0:x
+
+N:4:Steal an item
+C:0:0:0
+I:46:0:Z
+
+N:5:Rest for the night
+C:25:20:15
+I:17:0:r
+
+N:6:Buy food and drink
+C:3:2:1
+I:18:0:f
+
+N:7:Listen for rumours
+C:0:0:0
+I:19:0:u
+
+N:8:Presage fate
+C:600:500:480
+I:42:0:l
+
+N:9:In-Between
+C:0:0:0
+I:12:0:b
+
+N:10:Play craps
+C:0:0:0
+I:14:0:c
+
+N:11:Spin the wheel
+C:0:0:0
+I:15:0:s
+
+N:12:Play dice slots
+C:0:0:0
+I:16:0:d
+
+N:13:Game rules
+C:0:0:0
+I:13:0:r
+
+N:14:Research item
+C:1400:1300:1200
+I:1:0:a
+
+N:15:Town history
+C:0:0:0
+I:2:0:h
+
+N:16:Race legends
+C:0:0:0
+I:3:0:l
+
+N:17:Look at busts of Kings
+C:0:0:0
+I:5:0:l
+
+N:18:Research monster
+C:1600:1500:1400
+I:20:0:r
+
+N:19:View bounties
+C:0:0:0
+I:38:0:v
+
+N:20:Receive bounty money
+C:0:0:0
+I:39:0:b
+
+N:21:Get quest monster
+C:0:0:0
+I:54:0:q
+
+N:22:Turn in quest corpse
+C:0:0:0
+I:55:0:m
+
+N:23:Compare weapons
+C:220:200:180
+I:21:0:c
+
+N:24:Enchant weapon
+C:750:700:150
+I:23:0:w
+
+N:25:Enchant armour
+C:750:700:150
+I:24:0:a
+
+N:26:Recharge item
+C:350:300:75
+I:25:0:r
+
+N:27:Identify possessions
+C:900:800:100
+I:26:0:i
+
+N:28:Healing prayer
+C:600:400:0
+I:28:0:h
+
+N:29:Restoration
+C:600:500:100
+I:29:0:r
+
+N:30:Get share of stolen gold
+C:0:0:0
+I:7:2:g
+
+N:31:Enchant arrows
+C:550:500:100
+I:30:0:a
+
+N:32:Enchant bow
+C:550:500:100
+I:31:0:b
+
+N:33:Recall to dungeon
+C:300:200:100
+I:33:0:r
+
+N:34:Teleport to dungeon-level
+C:15000:10000:1000
+I:34:0:t
+
+N:35:Get a quest
+C:0:0:0
+I:6:0:q
+
+# Restrict to liked/normal
+N:36:Get a quest
+C:0:0:0
+I:46:1:q
+
+N:37:Get a quest
+C:0:0:0
+I:47:0:q
+
+N:38:Get a quest
+C:0:0:0
+I:49:0:q
+
+N:39:Herbal Healing
+C:32000:10000:0
+I:50:0:h
+
+N:40:Song of Lore
+C:2000:800:50
+I:26:0:s
+
+N:41:Distribute earnings
+C:0:0:0
+I:7:2:d
+
+N:42:Morph restoration
+C:3000:1500:750
+I:37:0:r
+
+#for The Mirror
+N:43:View fate
+C:500:500:500
+I:42:0:v
+
+#for The Mirror
+N:44:Research item
+C:1500:1500:1500
+I:1:0:a
+
+#for library in gondol
+N:45:Research item
+C:2000:2000:2000
+I:1:0:a
+
+#for Star-Dome
+N:46:Identify possessions
+C:1200:1000:250
+I:26:0:i
+
+#for Star-Dome
+N:47:Recharge item
+C:1200:1000:150
+I:25:0:r
+
+#for Valarin Temple
+N:48:Restoration
+C:1200:1000:200
+I:29:0:r
+
+#for Sea-Dome
+N:49:Morph restoration
+C:1500:1500:1500
+I:37:0:r
+
+#for The Golden Flower
+N:50:Enchant arrows
+C:1100:1000:200
+I:30:0:a
+
+#for The Golden Flower
+N:51:Enchant bow
+C:1100:1000:200
+I:31:0:b
+
+#for The Fountain
+N:52:Enchant armour
+C:1100:1000:200
+I:24:0:a
+
+#for The Fountain
+N:53:See Healers
+C:1100:1000:0
+I:28:0:h
+
+N:54:Drop an item
+C:0:0:0
+I:43:0:d:s
+
+N:55:Get an item
+C:0:0:0
+I:44:0:g:p
+
+N:56:Request an item
+C:0:0:0
+I:51:2:r
+
+N:57:Ask for loan
+C:0:0:0
+I:52:2:a
+
+N:58:Pay back loan
+C:0:0:0
+I:53:2:p
+
+N:59:Donate an item
+C:0:0:0
+I:43:0:d
+
+# Mage Tower quest in Lothlorien
+N:60:Get a quest
+C:0:0:0
+I:56:0:q
+
+N:61:Get a quest
+C:0:0:0
+I:61:0:q
diff --git a/lib/edit/between.map b/lib/edit/between.map
new file mode 100644
index 00000000..16fcf484
--- /dev/null
+++ b/lib/edit/between.map
@@ -0,0 +1,71 @@
+# Created by Mynstral (mynstral@thehelm.com)
+# Made for PernAngband on 14/08/2001
+
+# Monsters starts awake
+N:0
+
+# Permanent wall
+F:X:63:3
+
+# Floor with dirt
+F:.:88:3
+
+# Floor with grass
+F:,:89:3
+
+# Tree
+F:T:96:3
+
+# Floor with grass with a green thunderlord
+F:G:89:5:955
+
+# Floor with grass with a blue thunderlord
+F:L:89:5:956
+
+# Floor with grass with a brown thunderlord
+F:B:89:5:957
+
+# Floor with grass with a bronze thunderlord
+F:z:89:5:958
+
+# Floor with dirt with a bronze thunderlord
+F:Z:88:5:958
+
+# Floor with grass with a gold thunderlord
+F:D:88:5:959
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X..T.TT..T...T...T...T.....T....T......T...T.,,.....T......T....T...T.T..T..TT...T..T.TT.T.TT.TX
+D:X.T.TTTTT...T..TT..T..T.T.T.T...T.....T..T.TT,,..T...T.....T..T......T..T.T..T.T.TT.T.T.T..TTT.X
+D:XTTT.T.T.T.T..TTT.T.T.TTT.T.T.T..T..T.T..T..T.,,..T....T....T..T.T.T..T.T..T.T.TT.T.TT.TT.TTT.TX
+D:XTTT.T...T....T....T..T.TT..T.T.T...T...GG....,,...GG....T...T...T.TTT.T..TTTT.TT...T.TT..T.T.TX
+D:X.TTT..T.T..TTTT.T.T..T..T.TT...T......G..L..,,...L..G..T..T.T...T....T.TTTTTT..TTTTT.T..TT..T.X
+D:XTT.T.TTT.T.T.T.T.T.T.T...T..T...T..T.G..L..B,,.B..L..G....T..T...TT.T.T..TTT.T.TTTT.TTTT..T.T.X
+D:X.T.TT..T.TT.TTT.T.T.T.T.T.T...T.T.T.G..L..B.,,..B..L..G..T..T...T...T.T.T..TTTT.T.T.TT.T.T.TT.X
+D:X.TT.T.TTT.T.T.T.T..T.T..T....T..T..G..L..B..,Z...B..L..G..T...T.T..T...T...T.T.T.TTT.T.T.T..T.X
+D:XT.T.TT.TTTT.TT.T.TTT...T.T.T...T.T.G..L..B..ZDz..B..L..G...T.T...T.T...T..T..T...T..T.TT.T.T.TX
+D:XTTT..TTTT.T.T.T..T.T....T...T..T.T..G..L..B.,,..B..L..G..T....T...T....T....T..T..TT.T.T.TTT..X
+D:XTT.TT..T.T.T.T.T...T...T....T.T..T.T.G..L...,,....L..G..T..T...T..T.......T...T....T..T..TT...X
+D:XTTT.T..T.T..T.T..T...T..T....TT..T.T..GG....,,.....GG...T.T...T.T...T..T.T...T....T...T..T.T..X
+D:XTT...T.T..T....T....T....T....T...T....T.T..,,,.....T...T..T...T.....T...T...T..T..T...T..T.T.X
+D:XT..T.T...T...T...T.....T....T....T....T...TTTTTTTT...T...T...T....T.....T......T....T.....T..TX
+D:XT.T...T...T..T.T...T.T.T..T...T.T...T...T.T...,.T...T...T...T...T..T.....T....T....T...T...T.TX
+D:XTT.T.T.T.T..T..T.T.T.....T....T..T...T.......,,..T........T...T...T...T...T.....T..T..T...TTT.X
+D:X..TT...T..T.....T...T....T...T...T...T..T...,,.....T..T..T.....T.T...T...T...T..T..T.T...T.TT.X
+D:X.TT..T....T.T..T...T..T...T..T.T.T..T..T.T.,,..T..T..T....T..T..T...T.T....T....T...TT.TTTTTT.X
+D:X.T.TTTTTT.T.T.T...T..T..T.....T.T..T...T.T.,,...T...T......T.....T....T...T..T....T..TT.TTTTTTX
+D:XTTT.T.T.TTTTT.TTT...T..T...TT.T.T...T.T....,,.T...T.....T.....T....T.......T..T...T.TT.T.T.TT.X
+D:XTT.T..T.TT.T.T.TT.T...T.T..T.T.......T..T.,,.....T....T....T...........T..T...TT.TTTTTTTTTTTTTX
+D:XTTTT....TT.T.T.TTTTT.T...T..T.T..T......T.,,T......T....T....T.....T...T...T..TTTT.T.TT.T.TTTTX
+D:XTT.TTTTTT.T.TT.TTTTTT...T....TT.T...T....,,.T...T....T.....T....T....T....T..TT.T.TTT.T.T.T.TTX
+D:XTT.T..TTT.T.T.TTTTTTTT.T...T....T....T.T,,.........T....T....T....T...T.T.T.TTT.T.T.T.T..T.T.TX
+D:XTT.T..TTT..T.T.T.T.TT..T.T.T......T.T.T.,,..T...T...T...T..T...T...T.T.TTT.T.T.T.T.T.T.T.T..T.X
+D:X.T.T.T.T.T.T..T.T.T.T.T.T..T...T..T.TT.T.,,..T...T.....T.....T.....T..TTT.T.T.TTTTT.T.TTT.TT.TX
+D:XTT.T.T.T.TTTT.T..T.T.T.TTTT.T..T.T..T.TT.,,.T...T..T..T.......T.T....TTTTT.TTTT.T.TTT.TTTT.T.TX
+D:XT.T.TT.TTTT.T.T.T.TTT.T.T.TT......T...T...,,..T...T...T....T....T.TTT.T.T.T.T.T.T..T.T.T.T.TT.X
+D:XT.TTT.T.T.TTTT.T.T.T.T.TTT.TT..T...T...T..,,..T...T....T....T..TT.T.TT.TTTTT.T.T.TT.TT.TT.T.T.T
+D:XTT.T.T.TT.T.TTTT.T..TT.T.T.TT.T.T.T..T....,,.T...T...T....T..TTTTTTTT.T.T.T.T.T.TT.T.TTTTT.T.TX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:17:47
diff --git a/lib/edit/d_info.txt b/lib/edit/d_info.txt
new file mode 100644
index 00000000..76cde668
--- /dev/null
+++ b/lib/edit/d_info.txt
@@ -0,0 +1,512 @@
+# File: d_info.txt
+
+
+# This file is used to initialize the "lib/raw/d_info.raw" file, which is
+# used to initialize the "dungeon type" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# Some store indexes are defined in "defines.h", and must not be
+# changed.
+
+# N:<index>:<name>
+# D:<3 letter short name>:<long name>
+# W:<min depth>:<max depth>:<min player level>:<next dungeon>:<min alloc>:<max alloc chance>
+# L:<floor1>:<%1>:<floor2>:<%2>:<floor3>:<%3>
+# A:<wall1>:<%1>:<wall2>:<%2>:<wall3>:<%3>:<outer wall>:<inner wall>
+# O:<%treasure>:<%combat>:<%magic>:<%tools>
+# E:<dices>d<sides>:<frequency>:<attack type>
+# F:<flags>
+# R:<percent>:<flags mode>
+# M:<monster flags>
+# S:<monster spells>
+
+# Note for <flags mode> :
+# 0 = No restriction
+# 1 = AND
+# 2 = NAND
+# 3 = OR
+# 4 = NOR
+
+# Version stamp (required)
+
+V:2.0.0
+
+### Wilderness(purely cosmetic, never used) ###
+
+N:0:Wilderness
+D:Wil:a way to the Wilderness
+W:0:0:0:0:14:500
+L:89:80:199:20:1:0
+A:96:100:56:0:56:0:57:58
+O:20:20:20:20
+F:PRINCIPAL | FLAT | NO_RECALL
+R:100:0
+
+### The principal dungeons, they were created by spliting the vanilla dungeon ###
+
+N:1:Mirkwood
+D:Mkw:a way to the Mirkwood Forest.
+W:11:33:5:0:14:160
+L:89:95:199:5:88:0
+A:96:100:97:0:56:0:202:96
+O:20:20:20:20
+F:PRINCIPAL | NO_DOORS | NO_DESTROY | FLAT
+F:FILL_METHOD_0
+R:100:0
+
+N:2:Mordor
+D:Mdr:a door to the Land of Mordor.
+W:34:66:15:0:14:160
+L:88:67:93:33:1:0
+L:0:100:0
+A:97:50:56:50:56:0:57:97
+A:0:100:0
+O:20:20:20:20
+F:PRINCIPAL | LAVA_RIVER | CAVERN | NO_STREAMERS
+F:FILL_METHOD_2
+R:100:0
+
+N:3:Angband
+D:Ang:an entrance to the Pits of Angband.
+W:67:127:30:0:14:160
+L:1:100:1:0:1:0
+A:56:100:56:0:56:0:57:58
+O:20:20:20:20
+F:PRINCIPAL | CAVERN | NO_EASY_MOVE | NO_RECALL
+F:ADJUST_LEVEL_1_2 | ADJUST_LEVEL_1
+F:FILL_METHOD_0
+R:100:0
+
+N:4:Barrow-Downs
+D:BDw:a way to the Barrow-Downs.
+W:1:10:1:0:14:160
+L:88:78:89:18:199:4
+L:0:95:5
+A:96:34:97:66:56:0:57:97
+A:100:0:0
+O:20:20:20:20
+F:PRINCIPAL | FLAT
+F:FILL_METHOD_3
+R:25:1
+M:UNDEAD
+R:75:0
+
+# The Additional dungeons
+
+# Mount Doom
+# Levels 85-99
+N:5:Mount Doom
+D:MDm:a way to the top of the Mount Doom.
+W:85:99:18:0:14:160
+L:86:90:205:10:1:0
+A:177:100:0:0:0:0:85:87
+O:10:10:30:30
+E:2d10:10:FIRE
+F:CAVE | LAVA_RIVER | NO_RECALL | NO_STREAMERS
+F:FILL_METHOD_0 | NO_EASY_MOVE
+R:100:1
+M:IM_FIRE
+
+# Nether Realm
+# Levels 666-696 (!!!)
+# guarded by Tik'srvzllat, who has the Ring of Phasing
+N:6:Nether Realm
+D:Nth:a magical portal to the Nether Realm.
+W:666:696:40:0:14:160
+L:102:80:86:15:85:5
+A:85:80:87:20:87:0:57:85
+A:50:50:0
+O:25:25:25:25
+E:10d10:3:NETHER
+F:EMPTY | FORGET | NO_BREATH | NO_EASY_MOVE | NO_SHAFT
+F:RANDOM_TOWNS | ADJUST_LEVEL_2 | NO_RECALL | NO_STREAMERS
+F:LAVA_RIVER | FINAL_GUARDIAN_1032 | FINAL_ARTIFACT_203
+F:FILL_METHOD_2 | NO_RECALL_OUT
+R:5:0
+R:95:3
+M:RES_NETH | R_CHAR_G | R_CHAR_W | R_CHAR_U
+
+# The Lost Land of Numenor
+# levels 35-50
+# guarded by Ar-Pharazon the Golden, who has the stone "Toris Mejistos".
+N:7:Submerged Ruins
+D:Num:a submerged way to the lost land of Numenor.
+W:35:50:25:0:14:160
+L:84:95:187:5:1:0
+A:187:80:84:10:56:10:57:187
+A:60:0:40
+O:30:30:10:10
+E:1d1:1:ACID
+F:NO_STREAMERS
+F:FINAL_GUARDIAN_980 | FINAL_ARTIFACT_204
+F:FILL_METHOD_3 | WATER_BREATH
+R:20:0
+R:80:3
+M:AQUATIC | CAN_SWIM | CAN_FLY
+
+# Used for astral mode
+N:8:Halls of Mandos
+D:HMa:*A BUG*YOU should see this message!*
+W:1:98:1:0:14:160
+L:1:100:1:0:1:0
+O:20:20:20:20
+A:56:100:56:0:56:0:57:58
+F:RANDOM_TOWNS | NO_RECALL | NO_SHAFT
+F:FILL_METHOD_0
+R:100:2
+M:UNIQUE
+
+# Cirith Ungol
+# levels 25-50
+# guarded by Shelob.
+N:9:Cirith Ungol
+D:CUg:an entrance to Cirith Ungol.
+W:25:50:10:0:14:160
+L:87:5:88:65:16:30
+A:97:90:16:10:56:0:16:58
+O:30:30:30:10
+E:4d4:20:POISON
+F:FINAL_GUARDIAN_481
+F:CIRCULAR_ROOMS
+F:FILL_METHOD_2
+R:2:0
+R:49:3
+M:SPIDER | R_CHAR_c | R_CHAR_a | R_CHAR_I |
+R:49:3
+M:ORC | R_CHAR_w | R_CHAR_m | R_CHAR_j
+
+# The Heart of the Earth
+# levels 25-36
+# guarded by Golgarach, the Living Rock
+N:10:Heart of the Earth
+D:HoE:a passage leading into the very heart of the world.
+W:25:36:10:0:14:160
+L:1:100:1:0:1:0
+A:56:100:56:0:56:0:57:58
+O:40:10:10:20
+G:life
+F:EVOLVE | FINAL_GUARDIAN_1035 | NO_RECALL | NO_SHAFT
+R:40:3
+M:R_CHAR_# | R_CHAR_X | R_CHAR_g | R_CHAR_E |
+R:30:3
+M:PASS_WALL | KILL_WALL | HURT_ROCK
+R:30:0
+
+# The Void
+# Levels 128-150
+# Where Melkor lurks for the final battle!
+N:11:The Void
+D:Vod:a jumpgate to the Void
+W:128:150:40:0:20:160
+L:183:97:102:3:0:0
+A:183:90:102:10:0:0:102:102
+A:40:60:0
+O:25:25:25:25
+E:20d6:100:DARK
+F:EMPTY | FORGET | NO_BREATH | NO_EASY_MOVE | NO_RECALL_OUT | NO_RECALL |
+F:ADJUST_LEVEL_1_2 | ADJUST_LEVEL_1 | NO_STREAMERS | NO_SHAFT
+F:FILL_METHOD_2
+F:FINAL_GUARDIAN_1044 |
+R:1:0
+R:99:3
+M:UNDEAD | DEMON | DRAGON | NONLIVING | SPIRIT
+
+# TEST dungeon
+N:12:Test
+D:Tst:a way to test dungeon gen
+W:1:10:1:0:14:160
+L:88:78:89:18:199:4
+L:0:95:5
+A:177:100:0:0:0:0:85:87
+A:100:0:0
+O:20:20:20:20
+F:FILL_METHOD_3 | SMALL
+R:100:0
+G:dungeon2
+
+
+# The Paths of the Dead
+# levels 40-70
+# Feagwath is there, guarding Doomcaller
+N:16:Paths of the Dead
+D:PoD:the entrance to the Paths of the Dead.
+W:40:70:18:0:24:100
+L:88:85:84:15:1:0
+A:56:75:87:25:56:0:57:58
+O:30:30:30:2
+E:1d1:20:RAISE
+F:FINAL_GUARDIAN_804 | FINAL_ARTIFACT_91
+F:FILL_METHOD_3
+R:5:0
+R:10:3
+M:R_CHAR_p
+R:85:3
+M:UNDEAD | NONLIVING
+
+# The Illusory Castle
+# levels 35-52
+# Guarded by The Glass Golem guarding The Helm of Knowledge
+N:17:Illusory Castle
+D:Ill:an entrance to the Illusory Castle.
+W:35:52:10:0:24:100
+L:1:98:188:2:1:0
+A:56:50:189:50:56:0:57:58
+O:50:10:20:20
+E:6d2:6:CONFUSION
+F:RANDOM_TOWNS | NO_STREAMERS
+F:FINAL_GUARDIAN_1033 | FINAL_ARTIFACT_160
+F:FILL_METHOD_1
+R:30:0
+R:70:3
+M:STUPID | WEIRD_MIND | SHAPECHANGER | ATTR_MULTI | CHAR_MULTI | RAND_25 |
+M:RAND_50 | EMPTY_MIND | INVISIBLE | PASS_WALL | KILL_WALL
+S:BR_CONF | BR_CHAO | BA_CHAO | CONF | FORGET | TRAPS | MULTIPLY
+
+# The Maze
+# Levels 25-37
+# Guarded by The Minotaur of the Labyrinth with the Steel Helm of Hammerhand
+N:18:Maze
+D:Maz:a small tunnel leading to a maze of twisty little passages, all alike.
+W:25:37:15:0:20:160
+L:1:100:1:0:1:0
+A:56:98:48:2:56:0:57:58
+O:2:40:10:40
+G:maze
+F:SMALLEST | FORGET
+F:FINAL_GUARDIAN_1029 | FINAL_ARTIFACT_38
+R:80:0
+R:20:3
+M:R_CHAR_p
+
+# The Orc Cave
+# levels 10-22
+# There is Azog with the Wand of Thrain at the bottom
+N:19:Orc Cave
+D:Orc:a dark tunnel leading to an Orc Cave.
+W:10:22:8:0:35:200
+L:88:100:1:0:1:0
+A:97:100:56:0:56:0:57:97
+O:5:50:10:25
+F:RANDOM_TOWNS |
+F:FINAL_OBJECT_810 | FINAL_GUARDIAN_373 | CAVE |
+F:FILL_METHOD_0
+R:30:3
+M:TROLL
+R:20:0
+R:50:3
+M:ORC | R_CHAR_k | R_CHAR_o | R_CHAR_O
+
+# Erebor
+# levels 60-72
+# There is Glaurung
+N:20:Erebor
+D:Ere:a tunnel leading into depths of the Lonely Mountain.
+W:60:72:35:0:20:140
+L:88:100:1:0:1:0
+A:97:90:87:10:56:0:57:97
+O:40:40:40:40
+F:BIG | LAVA_RIVER | CAVERN | NO_RECALL | NO_STREAMERS
+F:CAVE | DOUBLE | FINAL_GUARDIAN_715 |
+F:FILL_METHOD_2
+R:10:0
+R:60:1
+M:DRAGON | R_CHAR_D
+R:30:1
+M:DRAGON | R_CHAR_d
+
+# The Old Forest
+# levels 13-25
+# Old Man Willow protects it
+N:21:The Old Forest
+D:OFr:a path into the Old Forest.
+W:13:25:5:0:15:100
+L:88:76:84:16:199:8
+L:68:16:16
+A:96:100:56:0:56:0:202:96
+O:20:5:15:30
+F:WATER_RIVERS | NO_DOORS | NO_DESTROY | FLAT | NO_STREAMERS
+F:RANDOM_TOWNS | FINAL_GUARDIAN_206
+F:FILL_METHOD_3
+R:30:0
+R:40:3
+M:ANIMAL
+R:30:3
+M:UNDEAD | R_CHAR_h
+
+# The Mines of Moria
+# levels 30-50
+# There is Durin's Bane
+N:22:Moria
+D:MoM:a stone door leading to the Mines of Moria.
+W:30:50:20:0:40:40
+L:88:100:1:0:1:0
+A:97:100:56:0:56:0:57:97
+O:30:50:10:5
+F:FINAL_GUARDIAN_872 | WATER_RIVER | BIG | NO_STREAMERS
+F:FORCE_DOWN
+F:RANDOM_TOWNS
+F:WILD_45_30__44_37
+F:FILL_METHOD_0
+R:40:3
+M:ORC
+R:30:3
+M:TROLL | GIANT
+R:20:3
+M:DEMON
+R:10:0
+
+# The tower of Dol Guldur
+# Levels 57-70
+# The Necromancer (weak Sauron) at the bottom, with the Ring of Durin
+N:23:Dol Guldur
+D:TDG:a gate leading to the tower of Dol Guldur.
+W:57:70:34:0:24:160
+L:1:80:174:20:1:0
+A:56:100:56:0:56:0:57:58
+O:20:1:70:9
+F:SMALL | FINAL_GUARDIAN_819 | FINAL_ARTIFACT_205
+F:FILL_METHOD_3
+R:30:3
+M:R_CHAR_p | R_CHAR_P
+R:10:3
+M:ORC | TROLL
+R:20:3
+M:UNDEAD
+R:30:3
+M:DEMON | DRAGON
+R:10:0
+
+# Dungeons from Variaz
+
+# The Small Water Cave
+# levels 32-34
+# The Watcher in the Water is at the bottom
+N:24:The Small Water Cave
+D:SWC:the entrance to a small water cave.
+W:32:34:20:0:14:160
+L:84:100:84:0:84:0
+A:97:100:56:0:56:0:57:58
+O:10:10:30:30
+E:1d1:20:ACID
+F:FINAL_GUARDIAN_517 | NO_RECALL
+F:FILL_METHOD_0
+R:10:0
+R:10:3
+M:AQUATIC
+R:40:1
+M:IM_COLD
+S:BA_WATE
+R:40:3
+M:IM_COLD
+
+# The Land of Mountains
+# Trone the rebel Thunderlord is hiding here, with his suit of
+# thunderlord armour.
+# Levels 45-70
+N:25:The Sacred Land Of Mountains
+D:LoM:the way to the Sacred Land of Mountains.
+W:45:70:20:0:14:160
+L:89:100:89:0:89:0
+A:97:100:56:0:56:0:97:97
+O:20:20:20:20
+F:RANDOM_TOWNS | FLAT | NO_STREAMERS
+F:FINAL_GUARDIAN_789 | FINAL_ARTIFACT_27
+F:FILL_METHOD_0
+R:60:3
+M:CAN_FLY
+R:40:0
+
+# The Land of Rhun
+# levels 26-40
+# Guarded by Ulfang the Black, Morgoth's first Easterling follower.
+N:26:The Land Of Rhun
+D:LoR:a way to the Land of Rhun.
+W:26:40:15:0:14:160
+L:89:100:1:0:1:0
+A:89:50:96:25:84:25:57:58
+O:20:20:20:20
+F:RANDOM_TOWNS | FLAT | NO_STREAMERS | FINAL_GUARDIAN_990
+F:FILL_METHOD_1
+R:30:3
+M:R_CHAR_p | R_CHAR_h
+R:30:3
+M:ANIMAL
+R:40:0
+
+# The Sandworm's Lair
+# level 22-30
+# guarded by the Sandworm Queen (and her children), who will drop her armour
+N:27:The Sandworm lair
+D:SwL:a sandhole.
+W:22:30:12:0:5:200
+L:91:85:94:10:93:5
+A:98:100:96:0:84:0:94:94
+O:15:5:60:20
+F:NO_DOORS | SAND_VEIN |
+F:FINAL_GUARDIAN_1030 | FINAL_ARTIFACT_153
+F:FILL_METHOD_0
+R:90:3
+M:R_CHAR_w
+R:10:3
+S:MULTIPLY
+
+# Used by the death fate
+N:28:Death fate
+D:Dth:a fated death.
+W:1:1:1:0:30:255
+L:1:100:1:0:1:0
+A:1:100:1:0:1:0:1:1
+O:1:1:1:1
+F:EMPTY | SMALLEST | NO_RECALL | NO_STREAMERS
+F:FILL_METHOD_0
+R:100:0
+
+# The Grinding Ice
+# levels 20-40
+# Guarded by the White Balrog
+N:29:The Helcaraxe
+D:Ice:the entrance to the Grinding Ice of the Helcaraxe.
+W:20:40:10:0:14:160
+L:90:0:88:70:84:30
+L:90:0:10
+A:95:0:56:100:56:0:57:58
+A:100:0:0
+O:20:20:20:20
+E:1d4:15:COLD
+F:DOUBLE | WATER_RIVER | CAVERN | NO_STREAMERS
+F:FINAL_GUARDIAN_1034 |
+F:FILL_METHOD_2
+R:100:1
+M:IM_COLD
+
+# The Lost Temple of "..player.pgod.."
+# Generated in god quest.
+# Most dungeon attributes altered during the quest.
+# See god.lua for details
+N:30:A lost temple
+D:LTm:the entrance to a lost temple.
+W:1:50:1:0:14:160
+L:1:100:1:0:1:0
+A:56:100:56:0:56:0:57:58
+O:20:20:20:20
+F:FILL_METHOD_4 | NO_RECALL
+R:100:0
+
+# N:<index>:<name>
+# D:<3 letter short name>:<long name>
+# W:<min depth>:<max depth>:<min player level>:<next dungeon>:<min alloc>:<max alloc chance>
+# L:<floor1>:<%1>:<floor2>:<%2>:<floor3>:<%3>
+# A:<wall1>:<%1>:<wall2>:<%2>:<wall3>:<%3>:<outer wall>:<inner wall>
+# O:<%treasure>:<%combat>:<%magic>:<%tools>
+# E:<dices>d<sides>:<frequency>:<attack type>
+# F:<flags>
+# R:<percent>:<flags mode>
+# M:<monster flags>
+# S:<monster spells>
+# 0 = No restriction
+# 1 = AND
+# 2 = NAND
+# 3 = OR
+# 4 = NOR
diff --git a/lib/edit/dragons.map b/lib/edit/dragons.map
new file mode 100644
index 00000000..164c97c8
--- /dev/null
+++ b/lib/edit/dragons.map
@@ -0,0 +1,43 @@
+# permanent wall
+F:X:61:0
+
+# Mountain Chain
+F:^:97:0
+
+# granite
+F:#:57:0
+
+# up staircase
+F:<:6:0
+
+# Dirt
+F:.:88:0
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X
+D:X^^^^^.....................^^^^^X
+D:X^^^.........................^^^X
+D:X^^...........................^^X
+D:X^^...........................^^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^.............................^X
+D:X^^...........................^^X
+D:X^^...........................^^X
+D:X^^^........................<^^^X
+D:X^^^^^.....................^^^^^X
+D:X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:6:6
+
diff --git a/lib/edit/e_info.txt b/lib/edit/e_info.txt
new file mode 100644
index 00000000..f01d8cf7
--- /dev/null
+++ b/lib/edit/e_info.txt
@@ -0,0 +1,2210 @@
+# File: e_info.txt
+
+
+# This file is used to initialize the "lib/data/e_info.raw" file, which is
+# used to initialize the "ego-item" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/data/e_info.raw" file.
+
+# The ego-item indexes are defined in "defines.h", and must not be changed.
+
+# Note that every "ego-item" type has a different "index", and can only be
+# created from items that belong to a certain "slot" in the equipment, if
+# one assumes that "ammo" belongs to an imaginary slot (23). However, it
+# is possible for several "ego-item" types to have the same "textual name",
+# such as with "Armor of Resistance" and "Shield of Resistance".
+
+# === Understanding e_info.txt ===
+
+# N: serial number : ego type
+# D: description
+# T: tval : min sval : max sval
+# R: rarity
+# X: position : slot : rating
+# W: depth : rarity1 : rarity2 : cost
+# C: to-hit : to-dam : to-ac : pval
+# r:N:needed flags on the base object
+# r:F:forbidden flags on the base object
+# Z: granted_power
+# F: flags
+
+# 'N' indicated the beginning of an entry. The serial number must increase
+# for each new item.
+
+# 'D' contains description. This field is currently not supported.
+
+# 'T' is for possible tval and sval value of the base item.
+# Up to 5 entries are possible.
+
+# 'R' stands for rarity, or randomness. It specifies percentual chance
+# of generated item to have following 'F' flags. I.e. 'R:40' followed
+# by 'F:SPEED' means, that 40% of item of this ego type will boost speed.
+
+# 'X' is for extra information. Position value 'A' means that ego-type name
+# will appear after base-item name ('Robe of Permanence'), value 'B' means
+# that ego-type name will appear before base-item name ('Elven Plate Mail').
+# Slot is determining in which equipment slots item could be equipped. This
+# value is currently ignored. Rating determines how level feeling will be
+# affected.
+
+# 'W' is for extra information. Depth is the depth the object is normally
+# found at, rarity determines how common the object is and cost is the items
+# value.
+
+# 'C' stands for 'creation'. It determines maximal to-hit, to-damage, AC and
+# stats (pval) values item can get.
+
+# 'Z' is granted power. See tables.c, array powers_type_init (lines 4511-4943).
+
+# 'F' contains flags. Most are self explaining, rest could be found in source.
+
+
+
+# Version stamp (required)
+
+V:2.0.0
+
+### Mage Staff ###
+
+N:1:of Mana
+X:A:24:20
+T:6:0:255
+W:5:3:8:10000
+C:-30:-30:0:3
+R:100
+F:MANA
+f:MANA
+R:70
+F:PVAL_M2
+
+N:2:of Power
+X:A:24:30
+T:6:0:255
+W:5:5:8:20000
+C:-30:-30:0:10
+R:100
+F:SPELL
+f:SPELL
+R:70
+F:PVAL_M2
+
+N:3:of Wizardry
+X:A:24:60
+T:6:0:255
+W:10:1:8:50000
+C:-40:-40:0:3
+R:100
+F:MANA | SPELL
+R:50
+F:PVAL_M2
+
+N:4:of Spell
+T:6:0:255
+X:A:24:60
+W:0:2:8:40000
+C:0:0:0:0
+R:100
+F:ACTIVATE
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+### Body Armor ###
+
+N:5:of Resist Acid
+T:36:0:255
+T:37:0:255
+X:A:30:16
+W:0:4:20:1000
+R:100
+F:RES_ACID | IGNORE_ACID
+f:RES_ACID | IGNORE_ACID
+
+N:6:of Resist Lightning
+T:36:0:255
+T:37:0:255
+X:A:30:10
+W:0:4:20:400
+R:100
+F:RES_ELEC | IGNORE_ELEC
+f:RES_ELEC | IGNORE_ELEC
+
+N:7:of Resist Fire
+T:36:0:15
+T:36:17:255
+T:37:0:255
+X:A:30:14
+W:0:4:20:800
+R:100
+F:RES_FIRE | IGNORE_FIRE
+f:RES_FIRE | IGNORE_FIRE
+
+N:8:of Resist Cold
+T:36:0:15
+T:36:17:255
+T:37:0:255
+X:A:30:12
+W:0:4:20:600
+R:100
+F:RES_COLD | IGNORE_COLD
+f:RES_COLD | IGNORE_COLD
+
+N:9:of Resistance
+T:36:0:255
+T:37:0:255
+X:A:30:20
+W:0:2:20:12500
+C:0:0:10:0
+R:100
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+f:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+R:25
+F:R_HIGH
+
+N:10:Elven
+T:36:0:255
+T:37:0:255
+X:B:30:25
+W:0:2:20:15000
+C:0:0:10:3
+R:100
+F:STEALTH | ESP_ORC
+f:STEALTH
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:OLD_RESIST
+R:25
+F:RES_POIS
+
+# Robe
+N:11:of Permanence
+T:36:2:2
+X:A:30:30
+W:0:1:10:30000
+C:0:0:10:0
+R:100
+F:SUST_STR | SUST_DEX | SUST_CON | SUST_INT | SUST_WIS | SUST_CHR |
+F:HOLD_LIFE | RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:OLD_RESIST
+R:2
+F:R_IMMUNITY
+
+# Filthy rags of leprousness
+N:12:of Leprousness
+T:36:1:1
+X:A:30:0
+W:0:1:10:0
+C:0:0:0:-6
+R:100
+F:CON | STR | R_STAT | CURSED
+# No CURSE_NO_DROP here, players seems to unlike surprises
+
+# Mithirl & Adamantite mails & PDSM
+N:13:of Immunity
+T:37:25:25
+T:37:30:30
+T:38:30:30
+X:A:30:40
+W:60:10:100:30000
+C:0:0:5:0
+R:100
+F:R_IMMUNITY
+
+# Ego DSM
+N:14:of Defense
+T:38:0:255
+X:A:30:5
+W:20:40:100:1000
+C:0:0:8:0
+R:100
+F:SUSTAIN
+
+# Boots of Jumping
+N:15:of Jumping
+T:30:0:255
+X:A:35:16
+W:0:3:27:500
+C:0:0:0:3
+Z:blink
+R:100
+F:ACTIVATE
+a:HARDCORE=JUMP
+
+### Shields ###
+
+N:16:of Resist Acid
+T:115:56:56
+T:34:0:5
+T:34:7:255
+X:A:32:16
+W:0:6:22:1000
+R:100
+F:RES_ACID | IGNORE_ACID
+f:RES_ACID | IGNORE_ACID
+
+N:17:of Resist Lightning
+T:34:0:5
+T:34:7:255
+T:115:56:56
+X:A:32:10
+W:0:6:22:400
+R:100
+F:RES_ELEC | IGNORE_ELEC
+f:RES_ELEC | IGNORE_ELEC
+
+N:18:of Resist Fire
+T:34:0:5
+T:34:7:255
+T:115:56:56
+X:A:32:14
+W:0:6:22:800
+R:100
+F:RES_FIRE | IGNORE_FIRE
+f:RES_FIRE | IGNORE_FIRE
+
+N:19:of Resist Cold
+T:115:56:56
+T:34:0:5
+T:34:7:255
+X:A:32:12
+W:0:6:22:600
+R:100
+F:RES_COLD | IGNORE_COLD
+f:RES_COLD | IGNORE_COLD
+
+N:20:of Resistance
+T:115:56:56
+T:34:0:5
+T:34:7:255
+X:A:32:20
+W:0:2:22:12500
+C:0:0:10:0
+R:100
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+f:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+N:21:of Reflection
+T:115:56:56
+T:34:0:5
+T:34:7:255
+X:A:32:20
+W:0:2:22:15000
+C:0:0:5:0
+R:100
+F:REFLECT
+f:REFLECT
+F:IGNORE_ELEC | IGNORE_ACID | IGNORE_COLD | IGNORE_FIRE
+
+# Metal shields only
+N:22:of Electricity
+T:34:3:3
+T:34:5:5
+T:34:10:10
+X:A:32:10
+W:0:2:22:400
+R:100
+F:RES_ELEC | IGNORE_ELEC | SH_ELEC
+f:SH_ELEC
+
+### Crowns and Helms ###
+
+N:23:of the Noldor
+T:115:57:57
+T:32:0:6
+T:32:8:99
+X:A:33:13
+C:0:0:0:2
+W:0:1:8:500
+R:100
+F:DEX | SUST_DEX | ACTIVATE | ESP_ORC
+a:HARDCORE=NOLDOR
+
+N:24:of Intelligence
+X:A:33:13
+C:0:0:0:2
+W:0:2:15:500
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:INT | SUST_INT
+f:INT
+
+N:25:of Wisdom
+X:A:33:13
+W:0:2:15:500
+C:0:0:0:2
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:WIS | SUST_WIS
+f:WIS
+
+N:26:of Beauty
+X:A:33:8
+W:0:2:15:1000
+C:0:0:0:4
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:CHR | SUST_CHR
+f:CHR
+
+# 40% chance of increase spell power
+N:27:of the Magi
+X:A:33:15
+W:0:1:8:7500
+C:0:0:0:3
+T:33:0:99
+R:100
+F:INT | SUST_INT |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:ABILITY | R_HIGH
+R:40
+F:SPELL
+R:50
+F:SPELL_CONTAIN | WIELD_CAST
+
+N:28:of Might
+X:A:33:19
+W:0:1:8:2000
+C:0:0:0:3
+T:33:0:99
+R:100
+F:STR | DEX | CON | SUST_STR | SUST_DEX | SUST_CON | FREE_ACT
+F:R_HIGH
+
+N:29:of Lordliness
+X:A:33:17
+W:0:1:8:2000
+C:0:0:0:3
+T:33:0:99
+R:100
+F:WIS | CHR | SUST_WIS | SUST_CHR
+F:R_HIGH
+
+N:30:of Seeing
+X:A:33:8
+W:0:1:8:1000
+C:0:0:0:5
+T:32:0:6
+T:32:8:99
+T:33:0:99
+T:115:57:57
+R:100
+F:SEARCH | RES_BLIND | SEE_INVIS
+f:SEARCH
+R:20
+F:ESP_ALL
+
+N:31:of Infravision
+X:A:33:11
+W:0:1:15:500
+C:0:0:0:5
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:INFRA | HIDE_TYPE
+f:INFRA
+
+N:32:of Light
+X:A:33:6
+W:0:2:15:500
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:LITE1 | RES_LITE
+f:LITE1
+
+N:33:of Telepathy
+X:A:33:20
+W:0:1:8:50000
+T:33:0:99
+R:100
+F:ESP_ALL
+f:ESP_ALL
+
+N:34:of Regeneration
+X:A:33:10
+W:0:1:8:1500
+T:32:0:6
+T:32:8:99
+T:33:0:99
+T:115:57:57
+R:100
+F:REGEN
+f:REGEN
+
+N:35:of Teleportation
+X:A:33:0
+W:0:1:7:50
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:TELEPORT
+f:TELEPORT
+R:90
+F:CURSED
+
+N:36:of Stupidity
+X:A:33:0
+C:0:0:0:-5
+W:0:2:7:0
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:INT | CURSED
+f:INT
+# No CURSE_NO_DROP here, players seems to unlike surprises
+
+N:37:of Naivety
+X:A:33:0
+C:0:0:0:-5
+W:0:2:7:0
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:WIS
+f:WIS
+
+N:38:of Ugliness
+X:A:33:0
+C:0:0:0:-5
+W:0:1:7:0
+T:32:0:6
+T:32:8:99
+T:115:57:57
+R:100
+F:CHR
+f:CHR
+
+N:39:of Sickliness
+X:A:33:0
+C:0:0:0:-5
+W:0:1:7:0
+T:33:0:99
+R:100
+F:STR | DEX | CON
+
+N:40:Dwarven
+T:32:0:6
+T:32:8:99
+X:B:33:13
+C:0:0:0:2
+W:0:1:8:500
+R:100
+F:INFRA | CON | RES_FIRE | ESP_TROLL | ESP_DRAGON
+
+
+### Cloaks ###
+
+N:41:of Protection
+X:A:31:10
+W:0:4:19:1500
+C:0:0:10:0
+T:35:0:255
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | RES_SHARDS
+
+N:42:of Stealth
+X:A:31:10
+W:0:8:18:500
+C:0:0:0:3
+T:35:0:99
+R:100
+F:STEALTH
+f:STEALTH
+
+N:43:of Aman
+X:A:31:20
+W:0:1:28:4000
+C:0:0:20:3
+T:35:0:255
+R:100
+F:STEALTH |
+f:STEALTH |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:OLD_RESIST
+
+# Aura, Fire
+N:44:of Immolation
+X:A:31:16
+W:0:1:18:4000
+C:0:0:4:0
+T:35:0:255
+R:100
+F:IGNORE_ACID | IGNORE_FIRE | SH_FIRE | RES_FIRE
+f:SH_FIRE
+
+N:45:of Enveloping
+X:A:31:0
+W:0:1:3:0
+C:-10:-10:0:0
+T:35:0:255
+R:100
+F:SHOW_MODS
+
+N:46:of Vulnerability
+X:A:31:0
+W:0:1:3:0
+C:0:0:-50:0
+T:35:0:255
+R:100
+F:AGGRAVATE
+
+N:47:of Irritation
+X:A:31:0
+W:0:1:3:0
+C:-15:-15:0:0
+T:35:0:255
+R:100
+F:AGGRAVATE | SHOW_MODS
+
+# Aura, Electricity
+N:48:of Electricity
+X:A:31:16
+W:0:1:18:4000
+C:0:0:4:0
+T:35:0:255
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | SH_ELEC | RES_ELEC
+
+### Gloves ###
+
+N:49:of Free Action
+X:A:34:11
+W:0:4:10:1000
+T:31:0:99
+R:100
+F:FREE_ACT
+f:FREE_ACT
+
+N:50:of Slaying
+X:A:34:17
+W:0:3:10:1500
+C:6:6:0:0
+T:31:0:99
+R:100
+F:SHOW_MODS
+
+N:51:of Agility
+X:A:34:14
+W:0:2:10:1000
+C:0:0:0:5
+T:31:0:99
+R:100
+F:DEX | HIDE_TYPE
+f:DEX
+
+N:52:of Power
+T:31:0:99
+X:A:34:22
+W:0:1:10:2500
+C:5:5:0:5
+R:100
+F:STR | SHOW_MODS | HIDE_TYPE
+f:STR
+F:R_HIGH
+
+# 53 Gauntlets only
+N:53:of Peace
+X:A:34:0
+W:0:1:3:0
+C:-10:-10:0:0
+T:31:2:2
+R:100
+F:HEAVY_CURSE | CURSED
+
+# 54 Gloves only
+N:54:of Charming
+X:A:34:5
+T:31:1:1
+W:0:1:11:400
+C:0:0:0:6
+R:100
+F:CHR
+R:33
+F:STEALTH
+f:STEALTH
+
+N:55:of Weakness
+T:31:0:99
+X:A:34:0
+W:0:1:3:0
+C:0:0:0:-10
+R:100
+F:STR
+
+N:56:of Clumsiness
+X:A:34:0
+W:0:1:3:0
+C:0:0:0:-10
+R:100
+F:DEX
+T:31:0:99
+
+
+### Boots ###
+
+N:57:of Levitation
+X:A:35:7
+W:0:8:27:250
+T:30:0:99
+R:100
+F:FEATHER
+f:FEATHER
+R:40
+F:R_HIGH
+
+N:58:of Stealth
+X:A:35:16
+W:0:8:27:500
+C:0:0:0:3
+T:30:0:99
+R:100
+F:STEALTH
+f:STEALTH
+
+N:59:of Free Action
+X:A:35:15
+W:0:5:27:1000
+T:30:0:99
+R:100
+F:FREE_ACT
+f:FREE_ACT
+
+N:60:of Speed
+X:A:35:25
+W:0:1:27:200000
+C:0:0:0:10
+T:30:0:99
+R:100
+F:SPEED | HIDE_TYPE
+f:SPEED
+R:10
+F:PVAL_M3
+
+# 61 Metal boots only
+
+N:61:of Dwarvish Endurance
+X:A:35:15
+W:0:1:20:5000
+C:0:0:0:6
+T:30:6:6
+R:100
+F:CON | INFRA | RES_DARK
+R:33
+F:STR
+
+N:62:of Noise
+X:A:35:0
+W:0:1:3:0
+T:30:0:99
+R:100
+F:AGGRAVATE
+f:AGGRAVATE
+
+N:63:of Slowness
+X:A:35:0
+W:0:1:3:0
+C:0:0:0:-5
+T:30:0:99
+R:100
+F:SPEED
+f:SPEED
+
+N:64:of Annoyance
+X:A:35:0
+W:0:1:3:0
+C:0:0:0:-10
+T:30:0:99
+R:100
+F:SPEED | AGGRAVATE
+
+
+### Weapons ###
+
+N:65:of Aman
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:30
+W:0:2:44:20000
+C:6:6:4:3
+R:100
+F:WIS |
+F:SLAY_EVIL | SLAY_UNDEAD | SLAY_DEMON |
+F:SEE_INVIS | BLESSED | RES_FEAR | ESP_EVIL
+F:SUSTAIN | LIMIT_BLOWS
+R:10
+F:BLOWS
+R:1
+F:PVAL_M1
+
+N:66:(Defender)
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:25
+W:0:2:44:15000
+C:4:4:8:4
+R:100
+F:STEALTH |
+f:STEALTH |
+F:FREE_ACT | SEE_INVIS | FEATHER | REGEN |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:SUSTAIN | R_HIGH
+R:33
+F:RES_POIS
+
+N:67:Blessed
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:B:24:20
+W:0:1:44:5000
+C:0:0:0:3
+R:100
+F:WIS | ESP_GOOD
+F:BLESSED | ABILITY
+f:BLESSED
+
+N:68:of Greater Life
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:20
+W:0:1:50:30000
+C:5:5:0:3
+r:N:MUST2H
+R:100
+F:LIFE | HOLD_LIFE
+f:LIFE
+
+N:69:of Westernesse
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:20
+W:0:2:44:20000
+C:5:5:0:2
+R:100
+F:STR | DEX | CON |
+F:SLAY_ORC | SLAY_TROLL | SLAY_GIANT |
+F:FREE_ACT | SEE_INVIS | ESP_ORC | ESP_TROLL | ESP_GIANT
+R:33
+F:RES_FEAR
+R:50
+F:RES_MORGUL
+
+N:70:of Extra Attacks
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:20
+W:0:1:44:10000
+C:0:0:0:3
+R:100
+F:BLOWS
+f:BLOWS
+
+N:71:of Slaying
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:15
+W:0:2:44:2500
+C:0:0:0:0
+R:100
+F:SLAY_WEAP | WOUNDING
+
+N:72:of Spinning
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:18
+W:0:1:44:9000
+C:8:8:0:2
+R:100
+F:DEX | STR | VORPAL | ACTIVATE
+a:HARDCORE=SPIN
+
+# The "Elemental" brands (4) (6)
+
+N:73:Acidic
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:15
+W:0:4:44:5000
+R:100
+F:BRAND_ACID | RES_ACID | IGNORE_ACID
+f:BRAND_ACID
+
+N:74:Shocking
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:20
+W:0:4:44:4500
+R:100
+F:BRAND_ELEC | RES_ELEC | IGNORE_ELEC
+f:BRAND_ELEC
+
+N:75:Fiery
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:20
+W:0:4:44:3500
+R:100
+F:BRAND_FIRE | RES_FIRE | IGNORE_FIRE | LITE1
+f:BRAND_FIRE |
+
+N:76:Frozen
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:15
+W:0:4:44:3000
+R:100
+F:BRAND_COLD | RES_COLD | IGNORE_COLD
+f:BRAND_COLD |
+
+N:77:Venomous
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:20
+W:0:4:44:4000
+R:100
+F:BRAND_POIS | RES_POIS
+f:BRAND_POIS |
+
+N:78:Chaotic
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:29
+T:23:31:255
+T:24:0:255
+T:115:55:55
+X:B:24:28
+W:0:1:44:10000
+R:100
+F:CHAOTIC | RES_CHAOS | IGNORE_ELEC | IGNORE_ACID | IGNORE_FIRE
+f:CHAOTIC
+F:R_ANY
+
+N:79:Sharp
+T:125:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:20
+W:0:2:44:5000
+R:100
+F:VORPAL
+f:VORPAL
+
+N:80:of Earthquakes
+T:125:0:255
+T:21:0:255
+T:115:55:55
+X:A:24:20
+W:0:1:44:4000
+C:10:10:0:6
+R:100
+F:IMPACT | STR | TUNNEL | HIDE_TYPE
+f:IMPACT
+
+# The "Slay" brands (8)
+
+N:81:of Slay Animal
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:18
+W:0:6:44:3500
+R:100
+F:SLAY_ANIMAL
+f:SLAY_ANIMAL
+
+N:82:of Slay Evil
+T:15:0:255
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:18
+W:0:6:44:3500
+R:100
+F:SLAY_EVIL
+f:SLAY_EVIL
+
+N:83:of Slay Undead
+T:15:0:255
+T:21:0:19
+T:21:21:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:18
+W:0:6:44:3500
+R:100
+F:SLAY_UNDEAD
+f:SLAY_UNDEAD
+
+N:84:of Slay Demon
+T:15:0:255
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:14
+W:0:6:44:2500
+R:100
+F:SLAY_DEMON
+f:SLAY_DEMON
+
+N:85:of Slay Orc
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:10
+W:0:6:44:2500
+R:100
+F:SLAY_ORC
+f:SLAY_ORC
+
+N:86:of Slay Troll
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:10
+W:0:6:44:2500
+R:100
+F:SLAY_TROLL
+f:SLAY_TROLL
+
+N:87:of Slay Giant
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:14
+W:0:6:44:2500
+R:100
+F:SLAY_GIANT
+f:SLAY_GIANT
+
+N:88:of Slay Dragon
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:18
+W:0:6:44:3500
+R:100
+F:SLAY_DRAGON
+f:SLAY_DRAGON
+
+# The "Kill" brands (8)
+
+N:89:of *Slay Animal*
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:20
+W:0:2:44:6000
+C:0:0:0:2
+R:100
+F:INT | SLAY_ANIMAL | SLOW_DIGEST | STEALTH | ESP_ANIMAL
+f:SLAY_ANIMAL | STEALTH
+
+N:90:of *Slay Evil*
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:20
+W:0:2:44:6000
+C:0:0:0:2
+R:100
+F:WIS | SLAY_EVIL | BLESSED | ESP_EVIL | RES_FEAR | ABILITY
+f:SLAY_EVIL |
+
+N:91:of *Slay Undead*
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:24
+W:0:2:44:8000
+C:0:0:0:2
+R:100
+F:WIS | KILL_UNDEAD | SEE_INVIS | ESP_UNDEAD | RES_NETHER
+f:KILL_UNDEAD |
+
+N:92:of *Slay Demon*
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:16
+W:0:2:44:8000
+C:0:0:0:2
+R:100
+F:INT | KILL_DEMON | ESP_DEMON | RES_FIRE | RES_CHAOS
+f:KILL_DEMON |
+
+N:93:of *Slay Orc*
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:14
+W:0:2:44:4000
+C:0:0:0:2
+R:100
+F:DEX | SLAY_ORC | ESP_ORC | SUST_DEX |
+f:SLAY_ORC |
+
+N:94:of *Slay Troll*
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:14
+W:0:2:44:4000
+C:0:0:0:2
+R:100
+F:STR | SLAY_TROLL | ESP_TROLL | REGEN | SUST_STR
+f:SLAY_TROLL |
+
+N:95:of *Slay Giant*
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:16
+W:0:2:44:4000
+C:0:0:0:2
+R:100
+F:STR | SLAY_GIANT | ESP_GIANT | RES_SHARDS | SUST_STR
+f:SLAY_GIANT |
+
+N:96:of *Slay Dragon*
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:24
+W:0:2:44:8000
+C:0:0:0:2
+R:100
+F:CON | KILL_DRAGON | ESP_DRAGON | RES_FEAR |
+f:KILL_DRAGON
+F:R_LOW | R_ELEM
+R:20
+F:RES_POIS
+
+N:97:Vampiric
+T:125:0:255
+T:23:0:255
+T:115:55:55
+X:B:24:25
+W:0:2:44:10000
+C:0:0:0:-2
+R:100
+F:LIFE | VAMPIRIC | HOLD_LIFE
+f:LIFE | VAMPIRIC
+
+N:98:(*Defender*)
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:35
+W:0:1:100:50000
+C:-15:-15:20:4
+R:100
+F:STEALTH | RES_POIS | DEX | CON | WIS | HOLD_LIFE |
+f:STEALTH
+F:FREE_ACT | SEE_INVIS | FEATHER | REGEN |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:R_ANY | R_LOW | SUSTAIN
+R:30
+F:R_ANY | R_LOW | SUSTAIN
+R:20
+F:R_ANY | R_LOW | SUSTAIN | R_HIGH
+R:10
+F:R_IMMUNITY | R_ANY
+
+N:99:of the Thunderlords
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:22
+W:0:1:100:7000
+C:4:4:0:2
+a:HARDCORE=TELEPORT
+R:100
+F:SLAY_EVIL | KILL_DRAGON | TELEPORT | FREE_ACT | SEARCH | BRAND_ELEC
+F:REGEN | SLOW_DIGEST | RES_NEXUS | ACTIVATE | FLY | ESP_DRAGON
+F:R_HIGH
+R:12
+F:ABILITY
+R:2
+F:R_P_ABILITY | PVAL_M3 | LIMIT_BLOWS
+
+N:100:of Gondolin
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:26
+W:0:1:44:25000
+C:7:7:0:3
+R:100
+F:STR | CON | ESP_EVIL | RES_FEAR |
+F:SLAY_EVIL | SLAY_TROLL | SLAY_DRAGON | SLAY_DEMON |
+F:FREE_ACT | SEE_INVIS | LITE1 | RES_DARK | ABILITY |
+F:IGNORE_ACID | IGNORE_FIRE
+R:33
+F:R_HIGH
+R:33
+F:HOLD_LIFE
+R:22
+F:DEX
+
+# Diggers only
+
+N:101:of Digging
+T:20:0:255
+X:A:24:4
+W:0:1:2:500
+C:0:0:0:5
+R:100
+F:TUNNEL |
+f:TUNNEL |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+# More weapons
+
+N:102:Spectral
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:B:24:30
+W:0:1:5:5000
+R:100
+F:SLAY_UNDEAD | SEE_INVIS | HOLD_LIFE | DRAIN_HP
+F:ACTIVATE
+a:HARDCORE=SPECTRAL
+
+N:103:of Morgul
+T:125:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:0
+W:0:1:1:0
+C:-20:-20:-10:-10
+R:100
+F:LUCK
+F:SEE_INVIS | AGGRAVATE | HEAVY_CURSE | CURSED | BLACK_BREATH | DRAIN_EXP |
+F:AUTO_CURSE | WOUNDING
+# No CURSE_NO_DROP here, players seems to unlike surprises
+
+N:104:of Nothingness
+T:125:0:255
+T:15:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+X:A:24:0
+W:0:1:2:0
+C:-100:-100:0:0
+R:100
+F:NEVER_BLOW | HEAVY_CURSE | CURSED | AUTO_CURSE
+
+
+### Missile Launchers ###
+
+N:105:of Accuracy
+T:19:0:255
+T:15:0:255
+X:A:25:10
+W:0:8:21:1000
+C:15:5:0:0
+
+N:106:of Power
+T:19:0:255
+T:15:0:255
+X:A:25:10
+W:0:8:21:1000
+C:5:15:0:0
+
+N:107:of Extra Might
+T:19:0:255
+X:A:25:20
+W:0:4:21:10000
+C:5:10:0:1
+R:100
+F:XTRA_MIGHT | PVAL_M3 | R_ANY
+f:XTRA_MIGHT |
+
+N:108:of Extra Shots
+T:19:0:255
+X:A:25:20
+C:10:5:0:1
+W:0:4:21:10000
+R:100
+F:XTRA_SHOTS | PVAL_M2
+f:XTRA_SHOTS |
+
+# Bows only
+N:109:of Lothlorien
+T:19:12:13
+X:A:25:20
+W:50:2:21:30000
+C:10:10:0:2
+R:100
+F:DEX | XTRA_MIGHT | FREE_ACT | IGNORE_ACID | IGNORE_FIRE | HIDE_TYPE |
+F:BLESSED | ABILITY
+
+# Crossbows only
+N:110:of the Haradrim
+T:19:23:24
+X:A:25:30
+W:50:2:21:20000
+C:5:15:0:1
+R:100
+F:XTRA_MIGHT | XTRA_SHOTS | IGNORE_ACID | IGNORE_FIRE | HIDE_TYPE
+
+# Slings only
+N:111:of Buckland
+X:A:25:25
+W:40:2:21:20000
+C:8:8:0:2
+T:19:2:2
+R:100
+F:DEX | XTRA_SHOTS | XTRA_MIGHT | IGNORE_ACID | IGNORE_FIRE | HIDE_TYPE
+
+
+### Ammo ###
+
+N:112:of Slay Animal
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+W:0:2:12:25
+R:100
+F:SLAY_ANIMAL
+f:SLAY_ANIMAL
+
+N:113:of Slay Evil
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+W:0:2:12:25
+R:100
+F:SLAY_EVIL
+f:SLAY_EVIL
+
+N:114:of Slay Undead
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+W:0:1:12:35
+R:100
+F:SLAY_UNDEAD
+f:SLAY_UNDEAD
+
+N:115:of Venom
+T:16:0:99
+T:17:0:2
+T:18:0:2
+X:A:23:10
+R:100
+F:BRAND_POIS
+f:BRAND_POIS
+W:0:2:12:25
+
+N:116:of Acid
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+R:100
+F:BRAND_ACID | IGNORE_ACID
+f:BRAND_ACID |
+W:0:1:12:30
+
+# 117 All Elements at once - melee weapon
+N:117:Elemental
+X:B:24:30
+W:10:1:50:26000
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+R:100
+F:BRAND_ACID | RES_ACID | IGNORE_ACID
+F:BRAND_ELEC | RES_ELEC | IGNORE_ELEC
+F:BRAND_FIRE | RES_FIRE | IGNORE_FIRE
+F:BRAND_COLD | RES_COLD | IGNORE_COLD
+F:BRAND_POIS | RES_POIS | DRAIN_MANA
+f:BRAND_ACID |
+f:BRAND_ELEC |
+f:BRAND_FIRE |
+f:BRAND_COLD |
+f:BRAND_POIS |
+
+N:118:of Slay Demon
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+W:0:1:12:35
+R:100
+F:SLAY_DEMON
+f:SLAY_DEMON
+
+N:119:of Slay Dragon
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+R:100
+F:SLAY_DRAGON
+f:SLAY_DRAGON
+W:0:1:12:35
+
+N:120:of Slaying
+X:A:23:15
+W:0:1:12:20
+C:12:12:0:0
+R:100
+F:DAM_DIE
+
+N:121:of Lightning
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+R:100
+F:BRAND_ELEC | IGNORE_ELEC
+f:BRAND_ELEC |
+W:0:1:12:30
+
+N:122:of Flame
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+R:100
+F:BRAND_FIRE | IGNORE_FIRE
+f:BRAND_FIRE |
+W:0:2:12:25
+
+N:123:of Frost
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:10
+R:100
+F:BRAND_COLD | IGNORE_COLD
+f:BRAND_COLD |
+W:0:2:12:25
+
+N:124:of Wounding
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:5
+W:0:3:12:20
+C:6:6:0:0
+
+N:125:of Backbiting
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:23:0
+W:0:1:2:0
+C:-50:-50:0:0
+
+
+### Special Broken Items ###
+
+# Destroyed Weapon
+N:126:Shattered
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:B:24:0
+W:0:1:2:0
+C:-5:-5:0:0
+
+# Destroyed Body Armor
+
+N:127:Blasted
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:B:30:0
+W:0:1:2:0
+C:0:0:-10:0
+
+
+# Instruments
+
+N:128:of the Eldar
+T:14:0:59
+T:14:61:255
+X:A:25:20
+W:0:2:3:1000
+C:0:0:0:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD |
+F:RES_ACID | CHR | SEE_INVIS
+F:R_ANY | PVAL_M2
+R:25
+F:PVAL_M1
+
+N:129:of Power
+T:14:0:59
+T:14:61:255
+X:A:25:20
+W:0:1:3:2000
+C:0:0:0:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | SUST_CHR |
+F:RES_FIRE | RES_COLD | RES_ELEC | RES_ACID | CHR | SEE_INVIS
+F:R_ANY | PVAL_M3
+R:50
+F:PVAL_M1
+R:35
+F:PVAL_M1
+
+# Horn, now four different ego items (for different GF_ values)
+# see items 181, 182 & 183.
+N:130:Dragon
+T:14:60:60
+X:B:25:20
+W:0:1:2:2000
+C:0:0:0:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | ACTIVATE
+F:R_ANY | PVAL_M2
+R:50
+F:PVAL_M1
+R:25
+F:PVAL_M1
+a:HARDCORE=BA_ACID_H
+
+# Rods ego
+N:131:Capacity of
+T:67:0:255
+X:A:51:10
+W:0:2:10:1000
+C:0:0:0:0
+R:100
+F:CAPACITY
+f:CAPACITY
+
+N:132:Cheapness of
+T:67:0:255
+X:A:51:10
+W:0:2:10:700
+C:0:0:0:0
+R:100
+F:CHEAPNESS
+f:CHEAPNESS
+
+N:133:Quickness of
+T:67:0:255
+X:A:51:15
+W:0:3:10:1100
+C:0:0:0:0
+R:100
+F:FAST_CAST
+f:FAST_CAST
+
+N:134:Charging of
+T:67:0:255
+X:A:51:15
+W:0:2:10:1500
+C:0:0:0:0
+R:100
+F:CHARGING
+f:CHARGING
+
+N:135:the Istari of
+T:67:0:255
+X:A:51:10
+W:0:1:10:10000
+C:0:0:0:0
+R:100
+F:CAPACITY | CHARGING | CHEAPNESS | FAST_CAST |
+
+### Lights ###
+
+N:136:of Boldness
+X:A:0:5
+T:39:0:99
+W:0:1:5:1000
+Z:remove fear
+
+N:137:of Fearlessness
+X:A:0:5
+T:39:0:99
+W:0:1:10:1500
+R:100
+F:RES_FEAR
+
+N:138:of Illumination
+X:A:0:5
+T:39:0:99
+W:0:3:10:1000
+Z:illuminate
+R:10
+F:LITE1
+R:5
+F:LITE2
+R:2
+F:LITE3
+
+N:139:of Brightness
+X:A:0:5
+T:39:0:99
+W:0:3:10:1000
+R:100
+F:LITE1
+f:LITE1
+R:60
+F:LITE2
+R:30
+F:LITE3
+R:30
+F:RES_DARK
+
+N:140:of *Brightness*
+X:A:0:9
+T:39:0:99
+W:0:1:40:5000
+R:100
+F:LITE1
+F:LITE2
+F:LITE3
+f:LITE1
+f:LITE2
+f:LITE3
+F:RES_DARK
+
+N:141:of the Shadows
+X:A:0:6
+T:39:0:99
+W:0:1:20:3000
+C:0:0:0:2
+R:100
+F:INVIS
+R:70
+F:RES_DARK
+R:50
+F:RES_LITE
+
+N:142:of Infravision
+X:A:0:3
+T:39:0:99
+W:0:1:10:700
+C:0:0:0:3
+R:100
+F:INFRA
+f:INFRA
+
+N:143:of the Eternal Eye
+X:A:0:7
+T:39:0:99
+W:0:3:40:4000
+C:0:0:0:0
+R:100
+F:RES_BLIND | SEE_INVIS
+
+N:144:of the Ethereal Eye
+X:A:0:7
+T:39:0:99
+W:0:3:40:4000
+C:0:0:0:0
+Z:magic map
+
+N:145:of Fading
+X:A:0:0
+T:39:2:99
+W:0:1:1:0
+C:0:0:0:0
+R:100
+F:FUEL_LITE
+
+# Armor (dwarven): must be heavy metal, and not rusty chain mail
+
+N:146:Dwarven
+T:37:2:255
+X:B:30:18
+W:0:2:20:5000
+C:0:0:15:2
+R:100
+F:STR | CON | INFRA | FREE_ACT | HIDE_TYPE |
+F:RES_FEAR | RES_DARK | SUST_STR | SUST_CON |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+# Ring and Amulet egos
+
+N:147:Indestructible
+X:B:0:2
+T:40:0:255
+T:45:0:255
+W:0:1:10:1000
+C:0:0:0:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+f:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+N:148:Cursed
+X:B:0:0
+T:40:0:255
+T:45:0:255
+W:0:1:10:0
+C:0:0:0:0
+R:100
+F:AUTO_CURSE
+f:AUTO_CURSE
+
+# Scrolls & school spellbooks & staves
+N:149:Fireproof
+X:B:0:1
+T:70:0:255
+T:111:0:255
+T:55:0:255
+W:0:1:10:1000
+C:0:0:0:0
+R:100
+F:IGNORE_FIRE
+f:IGNORE_FIRE
+
+# Wands & Staffs(NOT wishing nor nothing)
+N:150:of Plenty
+X:A:0:1
+T:55:0:29
+T:55:32:255
+T:65:0:29
+T:65:31:255
+W:0:1:20:1000
+C:0:0:0:3
+R:100
+F:PVAL_M5 | PVAL_M3
+R:50
+F:PVAL_M5 | PVAL_M3
+R:10
+F:PVAL_M5 | PVAL_M3
+R:1
+F:PVAL_M5 | PVAL_M3
+
+
+### Trapping Kits ###
+
+N:151:of Extra Might
+X:A:0:5
+T:46:1:3
+W:0:1:10:1000
+C:20:20:0:2
+R:100
+F:XTRA_MIGHT
+f:XTRA_MIGHT
+
+N:152:of Extra Shots
+X:A:0:10
+T:46:0:99
+W:0:1:10:2000
+C:20:20:0:3
+R:100
+F:XTRA_SHOTS
+f:XTRA_SHOTS
+
+N:153:Automatic
+X:B:0:15
+T:46:0:99
+W:0:1:10:3000
+C:10:10:0:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_COLD | IGNORE_FIRE |
+F:AUTOMATIC_5
+f:AUTOMATIC_5
+
+N:154:Fully Automatic
+X:B:0:15
+T:46:0:99
+W:0:1:15:5000
+C:10:10:0:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_COLD | IGNORE_FIRE |
+F:AUTOMATIC_99
+f:AUTOMATIC_99
+
+N:155:Well-hidden
+X:B:0:5
+T:46:0:99
+W:0:1:8:1000
+C:15:15:5:12
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_COLD | IGNORE_FIRE |
+F:STEALTH | HIDE_TYPE
+f:STEALTH
+
+N:156:Complicated
+X:B:0:10
+T:46:0:99
+W:0:1:12:2000
+C:15:15:30:0
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_COLD | IGNORE_FIRE
+
+N:157:Obvious
+X:B:0:0
+T:46:0:99
+W:0:1:1:0
+C:-20:-20:-20:-20
+R:100
+F:STEALTH | CURSED | HIDE_TYPE
+f:STEALTH
+
+N:158:for Dragons
+X:A:0:5
+T:46:0:99
+W:0:3:10:500
+C:20:20:10:4
+R:100
+F:STEALTH | ONLY_DRAGON | HIDE_TYPE | XTRA_SHOTS |
+F:IGNORE_ACID | IGNORE_FIRE
+
+N:159:for Demons
+X:A:0:5
+T:46:0:99
+W:0:3:10:500
+C:20:20:10:4
+R:100
+F:STEALTH | ONLY_DEMON | HIDE_TYPE | XTRA_SHOTS
+F:IGNORE_ACID | IGNORE_FIRE
+
+N:160:for Animals
+X:A:0:5
+T:46:0:99
+W:0:3:10:500
+C:20:20:10:4
+R:100
+F:STEALTH | ONLY_ANIMAL | HIDE_TYPE | XTRA_SHOTS
+
+N:161:for Undead
+X:A:0:5
+T:46:0:99
+W:0:3:10:500
+C:20:20:10:4
+R:100
+F:STEALTH | ONLY_UNDEAD | HIDE_TYPE | XTRA_SHOTS | KILL_GHOST
+
+N:162:for Evil
+X:A:0:5
+T:46:0:99
+W:0:3:10:500
+C:20:20:10:4
+R:100
+F:STEALTH | ONLY_EVIL | HIDE_TYPE | XTRA_SHOTS | KILL_GHOST
+
+# Lite ego
+N:163:of the Magi
+X:A:0:0
+T:39:1:99
+W:0:1:150:2000
+C:0:0:0:3
+Z:magic map
+R:100
+F:INT | WIS | CHR
+R:60
+F:INVIS | RES_BLIND
+R:30
+F:R_HIGH
+R:30
+F:PVAL_M2
+R:50:
+F:SPELL_CONTAIN | WIELD_CAST
+
+### New ego-items added by JLE
+
+# Armor of Vulnerability (the only cursed armor)
+N:164:of Vulnerability
+X:A:30:0
+W:0:2:20:0
+C:0:0:-50:0
+T:36:0:99
+T:37:0:99
+R:100
+F:AGGRAVATE | CURSED
+
+# Shield of Vulnerability (the only cursed shield)
+N:165:of Vulnerability
+X:A:32:0
+W:0:2:22:0
+C:0:0:-50:0
+T:115:56:56
+T:34:0:99
+R:100
+F:AGGRAVATE | CURSED
+
+# Shield of Preservation -
+N:166:of Preservation
+X:A:32:25
+W:40:2:44:20000
+C:-10:-10:20:0
+T:115:56:56
+T:34:0:99
+R:100
+F:RES_DISEN | SUST_STR | SUST_CON | SUST_DEX | HOLD_LIFE | R_HIGH |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+R:33
+F:R_LOW
+R:33
+F:R_LOW
+
+# Helm/Crown of Serenity
+N:167:of Serenity
+X:A:33:20
+W:35:1:15:4000
+T:32:0:6
+T:32:8:99
+T:33:0:99
+R:100
+F:RES_SOUND | RES_CONF | RES_FEAR
+
+# Crown of Night and Day
+N:168:of Night and Day
+X:A:33:18
+W:35:1:15:4000
+T:33:0:99
+R:100
+F:RES_LITE | RES_DARK | LITE1 | SEE_INVIS | RES_BLIND | IGNORE_ACID
+
+# Cloak of the Magi
+N:169:of the Magi
+X:A:31:15
+W:30:1:18:2000
+C:-5:-5:5:3
+T:35:0:99
+R:100
+F:INT | SPEED | SUST_INT | FREE_ACT | STEALTH | HIDE_TYPE | IGNORE_ACID
+R:30
+F:SPELL_CONTAIN | WIELD_CAST
+
+# Cloak of Invisibility
+N:170:of Invisibility
+X:A:31:20
+W:40:1:18:3000
+C:0:0:10:5
+T:35:0:99
+R:100
+F:STEALTH | HIDE_TYPE | INVIS
+f:INVIS
+
+# Cloak of the Bat
+N:171:of the Bat
+X:A:31:15
+W:50:1:35:3000
+C:-10:-10:10:3
+T:35:0:99
+R:100
+F:SPEED | FLY | RES_DARK | SEE_INVIS | INFRA | HIDE_TYPE | STEALTH
+
+# Leather Gloves of Thievery
+N:172:of Thievery
+X:A:34:22
+W:40:1:15:5000
+C:8:3:0:5
+T:31:1:1
+R:100
+F:DEX | SEARCH | SHOW_MODS | FEATHER | FREE_ACT | HIDE_TYPE | IGNORE_ACID
+R:10
+F:SPEED
+
+# Gauntlets and Cesti of Combat
+N:173:of Combat
+X:A:34:22
+W:50:1:15:7000
+C:6:8:-20:2
+T:31:2:99
+R:100
+F:STR | CON | SHOW_MODS | AGGRAVATE | HIDE_TYPE | IGNORE_ACID | RES_FEAR |
+F:DRAIN_HP
+R:25
+F:BLOWS
+
+# Boots of Stability
+N:174:of Stability
+X:A:35:20
+W:0:3:27:5000
+T:30:0:99
+R:100
+F:RES_NEXUS | FEATHER
+
+# Boots of Elvenkind (leather boots only)
+N:175:of Elvenkind
+X:A:35:30
+W:60:1:36:200000
+C:0:0:0:5
+T:30:2:3
+R:100
+F:STEALTH | SPEED | HIDE_TYPE | FEATHER | IGNORE_ACID | IGNORE_FIRE | ABILITY
+
+# Weapon of Fury (must be big heavy type of weapon, no daggers or whips)
+N:176:of Fury
+X:A:24:30
+W:40:1:66:20000
+T:21:12:99
+T:22:10:99
+T:23:16:99
+T:24:8:99
+T:125:0:99
+C:10:10:-20:2
+R:100
+F:STR | BLOWS | AGGRAVATE | RES_FEAR | HIDE_TYPE |
+F:IGNORE_ACID | IGNORE_FIRE | DRAIN_MANA
+
+# Staffs of wishing
+N:177:of Plenty
+X:A:0:1
+T:55:31:31
+W:0:1:20:1000
+C:0:0:0:2
+R:100
+F:PVAL_M2
+
+
+# Diggers only
+
+N:178:Magical
+T:20:0:255
+X:B:24:4
+W:0:1:10:500
+C:0:0:0:0
+Z:stone to mud
+R:100
+
+# Rod
+N:179:Simplicity of
+T:67:0:255
+X:A:51:8
+W:3:2:8:1000
+C:0:0:0:0
+R:100
+F:EASY_USE
+f:EASY_USE
+
+# Lite ego
+N:180:of Warmth
+X:A:0:0
+T:39:1:99
+W:0:1:10:500
+C:0:0:0:0
+R:100
+F:RES_COLD
+
+#Three more horn types, for different activation types...
+N:181:Dragon
+T:14:7:7
+X:B:25:20
+W:0:1:2:2000
+C:0:0:0:0
+a:HARDCORE=BA_COLD_3
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD |
+
+N:182:Dragon
+T:14:7:7
+X:B:25:20
+W:0:1:2:2000
+C:0:0:0:0
+a:HARDCORE=BA_ELEC_3
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD |
+
+N:183:Dragon
+T:14:7:7
+X:B:25:20
+W:0:1:2:2000
+C:0:0:0:0
+a:HARDCORE=BA_FIRE_H
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD |
+
+# Helm of water breathing
+N:184:of Water Breathing
+X:A:33:13
+C:0:0:0:2
+W:15:1:25:1000
+T:32:5:10
+R:100
+F:WATER_BREATH | IGNORE_ACID
+f:WATER_BREATH
+
+# A second of life for non MUST2H weapons, much lower value tho
+N:185:of Life
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+X:A:24:20
+W:0:1:50:30000
+C:5:5:0:1
+r:F:MUST2H
+R:100
+F:LIFE | HOLD_LIFE
+f:LIFE
+
+# Cloak of Air
+N:186:of Air
+X:A:31:10
+W:30:1:35:1500
+C:0:0:0:0
+T:35:0:255
+R:100
+F:MAGIC_BREATH
+
+# Ego DSM
+N:187:Polished
+T:38:0:255
+X:B:30:5
+W:40:25:100:25000
+C:0:0:0:0
+R:100
+F:REFLECT
+
+# Ego Heavy Crossbow
+N:188:of Siegecraft
+T:19:24:24
+X:A:25:30
+W:60:5:30:30000
+C:10:15:20:2
+R:120
+F:XTRA_MIGHT | XTRA_SHOTS | REFLECT | IMMOVABLE
+
+# N: serial number : ego type
+# D: description
+# T: tval : min sval : max sval
+# R: rarity
+# X: position : slot : rating
+# W: depth : rarity1 : rarity2 : cost
+# C: to-hit : to-dam : to-ac : pval
+# r:N:needed flags on the base object
+# r:F:forbidden flags on the base object
+# Z: granted_power
+# F: flags
diff --git a/lib/edit/evil.map b/lib/edit/evil.map
new file mode 100644
index 00000000..d407c4ef
--- /dev/null
+++ b/lib/edit/evil.map
@@ -0,0 +1,52 @@
+# permanent wall
+F:X:61:0
+
+# granite
+F:#:57:0
+
+# Mountain Chain
+F:^:97:0
+
+# up staircase
+F:<:6:0
+
+# Dirt
+F:.:88:0
+
+# Lesser Balrog
+F:b:88:0:996
+
+# Greater Balrog
+F:B:88:0:807
+
+# Pit Fiend
+F:P:88:0:812
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X^..^^^^^^^^^^^^^^^^^^^^^^^^^^^^X
+D:X^..^^^^^^^^^..^^^^^^^^^^^^^^^^^X
+D:X^...^^^^^^.........^^^^^^^^^^^^X
+D:X^^..^^^^^..^^^^..^B^^^^^^^^^^^^X
+D:X^...^^^^...^^^^^..^^^^^^^^^^^^^X
+D:X^^..^^^..^^.^^^^^^^^^^^^^^^^^^^X
+D:X^..^^^^^.....^^^^^.^^^^^^^^^^^^X
+D:X^^..^^^^..^^.^^^.^......^^^^^^^X
+D:X^..^^^^..^^...^^...^..^.^^^^^^^X
+D:X^^..^^^..^^^..^...^^^....^^^^^^X
+D:X^^^...^.^^^.....^^^^P...<^^^^^^X
+D:X^^^.....^^^^..^^^^^^^^..^^^^^^^X
+D:X^^^^^^.^^^^..^^^^^^^^^^^^^^^^^^X
+D:X^^^^^^^^^^^...^^^^^^^^^^^^^^^^^X
+D:X^^^^^.^^^^.^..^^^^^^^^^^^^^^^^^X
+D:X^^^^...^^...^^^...^^^^^^...^^^^X
+D:X^^^^.^....^^^^.^.....^..^..^^^^X
+D:X^^^^..^^^^^.....^...^..^^^B^^^^X
+D:X^^^P........^.....^^^^^^^^^^^^^X
+D:X^^^^^^...^^^^^^^^^^^^^^^^^^^^^^X
+D:X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:4
+
diff --git a/lib/edit/f_info.txt b/lib/edit/f_info.txt
new file mode 100644
index 00000000..21f7954d
--- /dev/null
+++ b/lib/edit/f_info.txt
@@ -0,0 +1,933 @@
+# File: f_info.txt
+
+
+# This file is used to initialize the "lib/raw/f_info.raw" file, which is
+# used to initialize the "terrain feature" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/raw/f_info.raw" file.
+
+# Note that the terrain feature are grouped into very regular groups,
+# such that each of the bits in the feature type conveys information.
+
+# Note that terrain feature zero contains the "darkness" picture.
+
+
+# Version stamp (required)
+
+V:2.0.0
+
+
+# 0x00 --> nothing
+
+N:0:nothing
+G: :w
+F:FLOOR
+
+# 0x01 --> open floor
+
+N:1:open floor
+G:.:w
+F:FLOOR | DONT_NOTICE_RUNNING | SUPPORT_LIGHT | CAN_RUN
+F:SUPPORT_GROWTH
+
+# 0x02 -> fountain
+N:2:fountain
+G:_:w
+F:FLOOR | NOTICE | REMEMBER | CAN_RUN
+D:0:The liquid here seems magical.
+
+# 0x03 --> glyph of warding
+
+N:3:glyph of warding
+G:;:y
+F:FLOOR | NOTICE | SUPPORT_LIGHT | CAN_RUN | REMEMBER
+D:0:There is a mighty spell of protection here.
+
+# 0x04 --> open door
+
+N:4:open door
+G:':U
+F:FLOOR | NOTICE | REMEMBER | CAN_RUN | DOOR
+
+# 0x05 --> broken door
+
+N:5:broken door
+G:':U
+F:FLOOR | NOTICE | REMEMBER | CAN_RUN | DOOR
+
+# 0x06 --> up stairs (perm)
+
+N:6:up staircase
+G:<:w
+F:FLOOR | PERMANENT | NOTICE | SUPPORT_LIGHT | REMEMBER | CAN_RUN
+D:0:There is an up staircase here.
+D:1:You cannot tunnel a stair.
+
+# 0x07 --> down stairs (perm)
+
+N:7:down staircase
+G:>:w
+F:FLOOR | PERMANENT | NOTICE | SUPPORT_LIGHT | REMEMBER | CAN_RUN
+D:0:There is a down staircase here.
+D:1:You cannot tunnel a stair.
+
+N:8:quest entrance
+G:>:y
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+D:1:You cannot tunnel a quest entrance.
+
+N:9:quest exit
+G:<:y
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+D:1:You cannot tunnel a quest exit.
+
+N:10:quest down level
+G:>:r
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+
+N:11:quest up level
+G:<:r
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+
+N:12:town exit
+G:>:g
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+
+N:13:shaft down
+G:>:U
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+D:0:There is a shaft down here.
+D:1:You cannot tunnel a shaft.
+
+N:14:shaft up
+G:<:U
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+D:0:There is a shaft up here.
+D:1:You cannot tunnel a shaft.
+
+# 0x0F -> empty fountain
+N:15:fountain
+G:_:D
+F:FLOOR | NOTICE | REMEMBER | CAN_RUN
+D:0:The fountain seems empty.
+
+N:16:web
+G:+:y
+F:CAN_PASS | NOTICE | WEB | NOTICE | TUNNELABLE
+D:1:You tunnel through the web.
+D:2:a web blocking your way
+
+# Trap -- the flags are not used by the program
+N:17:trap
+G:^:w
+F:FLOOR | NOTICE | REMEMBER
+
+# 0x12 --> 0x1F -- UNUSED
+
+# 0x2x --> locked door (power 0)
+
+N:32:door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 1)
+
+N:33:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 2)
+
+N:34:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 3)
+
+N:35:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 4)
+
+N:36:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 5)
+
+N:37:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 6)
+
+N:38:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> locked door (power 7)
+
+N:39:locked door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | REMEMBER | DOOR
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 0)
+
+N:40:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 1)
+
+N:41:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 2)
+
+N:42:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 3)
+
+N:43:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 4)
+
+N:44:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 5)
+
+N:45:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 6)
+
+N:46:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x2x --> jammed door (power 7)
+
+N:47:jammed door
+G:+:U
+M:32
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | REMEMBER | NOTICE
+F:TUNNELABLE
+D:1:You bash the boor.
+
+# 0x30 --> secret door
+
+N:48:secret door
+G:#:w
+M:56
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | DOOR
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel.
+
+# 0x31 --> rubble
+
+N:49:pile of rubble
+G:::w
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE
+F:CAN_FLY | SUPPORT_LIGHT
+F:TUNNELABLE
+D:1:You dig in the rubble.
+
+# 0x32 --> magma vein
+
+N:50:magma vein
+G:%:s
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the magma vein.
+
+# 0x33 --> quartz vein
+
+N:51:quartz vein
+G:%:w
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the quartz vein.
+
+# 0x34 --> magma vein + treasure
+
+N:52:magma vein
+G:%:s
+M:50
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the magma vein.
+
+# 0x35 --> quartz vein + treasure
+
+N:53:quartz vein
+G:%:w
+M:51
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the quartz vein.
+
+# 0x36 --> magma vein + known treasure
+
+N:54:magma vein with treasure
+G:*:o
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the magma vein.
+
+# 0x37 --> quartz vein + known treasure
+
+N:55:quartz vein with treasure
+G:*:o
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the quartz vein.
+
+# 0x38 --> granite wall -- basic
+
+N:56:granite wall
+G:#:w
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the granite wall.
+
+# 0x39 --> granite wall -- inner
+
+N:57:granite wall
+G:#:w
+M:56
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the granite wall.
+
+# 0x3A --> granite wall -- outer
+
+N:58:granite wall
+G:#:w
+M:56
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the granite wall.
+
+# 0x3B --> granite wall -- solid
+
+N:59:granite wall
+G:#:w
+M:56
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the granite wall.
+
+# 0x3C --> permanent wall -- basic (perm)
+
+N:60:permanent wall
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+# 0x3D --> permanent wall -- inner (perm)
+
+N:61:permanent wall
+G:#:w
+M:60
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+# 0x3E --> permanent wall -- outer (perm)
+
+N:62:permanent wall
+G:#:w
+M:60
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+# 0x3F --> permanent wall -- solid (perm)
+
+N:63:permanent wall
+G:#:w
+M:60
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+N:64:explosive rune
+G:*:R
+F:FLOOR | CAN_LEVITATE | CAN_FLY | NOTICE | SUPPORT_LIGHT
+D:0:This rune seems unstable.
+
+N:65:Straight Road startpoint
+G:*:w
+F:FLOOR | REMEMBER | NOTICE
+
+N:66:section of the Straight Road
+G:*:B
+F:FLOOR | REMEMBER | NOTICE
+
+N:67:section of the Straight Road
+G:*:b
+F:FLOOR | REMEMBER | NOTICE
+
+N:68:section of the Straight Road
+G:*:B
+F:FLOOR | REMEMBER | NOTICE
+
+N:69:section of the Straight Road
+G:*:b
+F:FLOOR | REMEMBER | NOTICE
+
+N:70:section of the Straight Road
+G:*:W
+F:FLOOR | REMEMBER | NOTICE
+
+N:71:section of the Straight Road (discharged)
+G:*:W
+F:FLOOR | REMEMBER | NOTICE
+
+N:72:Straight Road exit
+G:*:w
+F:FLOOR | REMEMBER | NOTICE
+
+N:73:corrupted section of the Straight Road
+G:*:D
+F:FLOOR | REMEMBER | NOTICE
+
+# 74 --> shop
+
+N:74:Building
+G:1:U
+F:FLOOR | PERMANENT | REMEMBER | NOTICE | CAN_RUN
+
+# 75 --> 78 Quests index
+
+N:75:permanent wall
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+N:76:permanent wall
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+N:77:permanent wall
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+N:78:permanent wall
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+# 79 --> 83 UNSUSED
+
+N:84:stream of shallow water
+G:~:B
+S:B:B:B:B:B:B:b
+F:ATTR_MULTI
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT | CAN_RUN
+F:DONT_NOTICE_RUNNING
+F:SUPPORT_GROWTH
+
+# -1 = player level
+N:85:pool of deep lava
+G:.:R
+E:-1d2:1:FIRE
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+D:0:You move across the deep lava.
+
+N:86:stream of shallow lava
+G:.:r
+E:-1d1:1:FIRE
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+D:0:You move across the shallow lava.
+
+N:87:dark pit
+G:#:D
+F:CAN_LEVITATE | CAN_FLY
+F:NO_WALK | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+D:0:Ohhh, it is dark and deep.
+
+N:88:dirt
+G:.:U
+F:FLOOR | SUPPORT_LIGHT | CAN_RUN
+F:DONT_NOTICE_RUNNING
+F:SUPPORT_GROWTH
+
+N:89:patch of grass
+G:.:G
+F:FLOOR | SUPPORT_LIGHT | CAN_RUN
+F:DONT_NOTICE_RUNNING
+F:SUPPORT_GROWTH
+
+N:90:ice
+G:.:W
+E:1d1:50:ICE
+F:FLOOR | NOTICE
+
+N:91:sand
+G:.:y
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+N:92:dead tree
+G:#:D
+F:CAN_FLY | CAN_PASS
+F:WALL | NO_WALK | NO_VISION | NOTICE
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You chop away at the dead tree.
+D:2:a tree blocking your way
+
+N:93:ash
+G:.:s
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+N:94:mud
+G:.:u
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+N:95:ice wall
+G:#:W
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the ice wall... #BOh chilly#w.
+D:2:an ice wall blocking your way
+
+N:96:tree
+G:#:G
+F:CAN_FLY | CAN_PASS | SUPPORT_LIGHT
+F:WALL | NO_WALK | NO_VISION
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You chop away at the tree.
+D:2:a tree blocking your way
+
+N:97:mountain chain
+G:^:U
+F:CAN_CLIMB | CAN_PASS | SUPPORT_LIGHT
+F:WALL | NO_WALK | NO_VISION
+F:DONT_NOTICE_RUNNING
+F:PERMANENT
+D:1:You cannot tunnel into such a hard stone.
+D:2:a hard stone block blocking your way
+
+# 0x62 --> sandwall
+
+N:98:sandwall
+G:#:y
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You easily dig into the sandwall.
+D:2:a sandwall blocking your way
+
+# 0x63 --> sandwall + treasure
+
+N:99:sandwall
+G:%:y
+M:98
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You easily dig into the sandwall.
+D:2:a sandwall blocking your way
+
+# 0x64 --> sandwall + known treasure
+
+N:100:sandwall with treasure
+G:*:o
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You easily tunnel into the sandwall.
+D:2:a sandwall blocking your way
+
+N:101:high mountain chain
+G:^:W
+F:WALL | NO_WALK | NO_VISION | PERMANENT
+F:DONT_NOTICE_RUNNING
+D:1:This rock is far too hard.
+D:2:a very hard stone block blocking your way
+
+N:102:nether mist
+G:.:v
+S:v:R:r:v:R:r:D
+E:1d1:40:NETHER
+F:ATTR_MULTI
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+
+N:160:Void Jumpgate
+G:+:v
+F:FLOOR | REMEMBER | NOTICE | PERMANENT | CAN_RUN
+D:0:A dark rift opens to the void here.
+
+###### Here are the altars. ######
+
+N:161:Altar of Being
+G:0:W
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You feel at peace.
+
+N:162:Altar of Winds
+G:0:B
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You grow a desire to become a bird.
+
+N:163:Altar of Force
+G:0:R
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You grow a desire to fight evil.
+
+N:164:Altar of Darkness
+G:0:D
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:Images of pain and death fill your mind.
+
+N:165:Altar of Nature
+G:0:g
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You feel the desire to walk in a great forest.
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+# Used as a marker for random quests
+N:172:open floor
+G:.:w
+F:FLOOR | CAN_RUN | DONT_NOTICE_RUNNING
+F:SUPPORT_GROWTH
+
+# Underground Tunnel
+N:173:Underground Tunnel
+G:#:s
+F:FLOOR | REMEMBER | SUPPORT_LIGHT | DONT_NOTICE_RUNNING | CAN_RUN
+D:0:Oh, an underground tunnel!
+
+# Tainted water
+N:174:stream of tainted water
+G:~:u
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+N:175:monster trap
+G:;:v
+F:FLOOR
+
+N:176:Void Jumpgate
+G:+:v
+F:FLOOR | REMEMBER | NOTICE | PERMANENT | CAN_RUN
+D:0:A dark rift opens to the void here.
+
+N:177:lava wall
+G:#:R
+S:R:R:r:r:U:u:R
+F:ATTR_MULTI
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+D:1:The lava is far too hot to tunnel into it.
+D:2:a lava wall blocking your way
+
+N:178:Great Fire
+G:%:v
+S:R:R:y:v:y:v:R
+E:150d2:1:HELL_FIRE
+F:ATTR_MULTI
+F:FLOOR | REMEMBER | NOTICE | PERMANENT
+D:0:This fire is so powerful it could destroy even the most powerful artifacts.
+
+N:179:path to the next area
+G:>:w
+F:FLOOR | PERMANENT | NOTICE | REMEMBER | CAN_RUN
+D:0:There is a path leading to the next area here.
+D:1:You cannot tunnel a path.
+
+N:180:path to the previous area
+G:<:w
+F:FLOOR | PERMANENT | NOTICE | REMEMBER | CAN_RUN
+D:0:There is a path leading to the previous area here.
+D:1:You cannot tunnel a path.
+
+N:181:field
+G:::g
+F:FLOOR | PERMANENT | NOTICE | REMEMBER
+F:DONT_NOTICE_RUNNING
+D:1:You cannot tunnel a field.
+
+N:182:Ekkaia, the Encircling Sea
+G:*:b
+S:b:b:b:b:b:b:B
+F:ATTR_MULTI
+F:WALL | NO_WALK | NO_VISION | PERMANENT | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+N:183:void
+G: :d
+F:FLOOR
+
+# XXX 182 - 186
+
+# 187 --> terrain -- deep water
+
+N:187:pool of deep water
+G:~:b
+S:b:b:b:b:b:b:B
+F:ATTR_MULTI
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+
+# Glass wall -- can see but not pass
+N:188:glass wall
+G:.:B
+F:NO_WALK | WALL | PERMANENT | NOTICE
+F:DONT_NOTICE_RUNNING
+D:1:This glass seems to be totaly impenetrable.
+D:2:a glass wall blocking your way
+
+# Illusion wall -- can't see but can pass
+N:189:illusion wall
+G:#:w
+F:FLOOR | NO_VISION | REMEMBER | SUPPORT_LIGHT | DONT_NOTICE_RUNNING
+D:0:Looks like this wall is not so real.
+
+# Grass roof
+N:190:Grass roof
+G:#:y
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# grass roof top
+N:191:grass roof top
+G:#:y
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# grass roof chimney
+N:192:grass roof chimney
+G:#:y
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# brick roof
+N:193:brick roof
+G:#:r
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# brick roof top
+N:194:brick roof top
+G:#:r
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# brick roof chimney
+N:195:brick roof chimney
+G:#:r
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# window
+N:196:window
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# small window
+N:197:small window
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# rain barrel
+N:198:rain barrel
+G:#:w
+F:WALL | NO_WALK | NO_VISION | PERMANENT | DONT_NOTICE_RUNNING
+
+# grass with flowers
+N:199:grass with flowers
+G:;:G
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+# cobblestone road
+N:200:cobblestone road
+G:.:w
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+
+# cobblestone with outlet
+N:201:cobblestone with outlet
+G:.:w
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+
+N:202:small tree
+G:#:g
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN | SUPPORT_LIGHT | REMEMBER
+F:NO_VISION
+
+# Just to have a town entrance picture
+N:203:town
+G:*:w
+F:FLOOR | NOTICE
+
+# Underground Tunnel
+N:204:Underground Tunnel
+G:^:U
+F:FLOOR | REMEMBER | SUPPORT_LIGHT | DONT_NOTICE_RUNNING | CAN_RUN
+D:0:Oh, an underground tunnel!
+
+# Fire
+N:205:a blazing fire
+G:%:y
+S:y:y:y:R:r:y:R
+E:-1d2:1:FIRE
+D:0:The blazing fire burns you!
+F:ATTR_MULTI
+F:FLOOR | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+
+# Permanent rubble -- town use
+N:206:pile of rubble
+G:::w
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE
+F:CAN_FLY | SUPPORT_LIGHT | PERMANENT
+D:1:Looks like this pile of rubble is quite hard.
+
+# Rocky ground - rougher terrain.
+N:207:rocky ground
+G:.:s
+F:FLOOR | SUPPORT_LIGHT | CAN_RUN
+F:DONT_NOTICE_RUNNING
+
+# cloud-like vapour. Floor for Eru's temple
+N:208:cloud-like vapour
+G:.:W
+S:W:B:B:W:w:W:B
+F:FLOOR | CAN_LEVITATE | CAN_FLY | SUPPORT_LIGHT
+F:ATTR_MULTI | CAN_RUN | DONT_NOTICE_RUNNING
+
+# condensing water
+N:209:condensing water
+G:~:B
+S:B:B:B:B:B:B:b
+F:ATTR_MULTI
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT | CAN_RUN
+F:DONT_NOTICE_RUNNING
+
+# Dense mist. Can pass through, but not see through
+N:210:dense mist
+G:#:w
+S:w:W:s:s:s:w:w
+F:FLOOR | NO_VISION | REMEMBER | SUPPORT_LIGHT
+F:ATTR_MULTI | DONT_NOTICE_RUNNING
+D:0:You wander through the mist.
+D:1:You cannot tunnel through mist!
+
+# Hail-stone wall
+N:211:hail-stone wall
+G:#:W
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:TUNNELABLE
+D:1:You tunnel into the hail-stone wall.
+
+N:212:dead small tree
+G:#:D
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN | SUPPORT_LIGHT | REMEMBER
+F:NO_VISION
+
+
+# New features for the Maps of Lord Dimwit
+
+N:213:copper pillar
+G:#:u
+S:u:u:u:o:u:u:u
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING | ATTR_MULTI
+D:1:The copper is too tough to tunnel through.
+D:2:a copper pillar blocking your way
+
+N:214:ethereal wall
+G:.:w
+F:WALL | NO_WALK | PERMANENT | NOTICE | DONT_NOTICE_RUNNING
+D:1:You can't even see your obstruction!
+D:2:an unseen force blocking your way
+
+N:215:glacial wall
+G:#:B
+F:WALL | NO_WALK | CAN_PASS | NO_VISION | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING | TUNNELABLE
+D:1:You tunnel into the glacial wall... #BOh chilly#w.
+D:2:a hard glacial wall blocking your way
+
+N:216:battlement
+G:#:w
+F:NO_WALK | CAN_PASS | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING | TUNNELABLE
+D:1:You tunnel into the battlement.
+D:2:a hard stone battlement blocking your way
diff --git a/lib/edit/haunted.map b/lib/edit/haunted.map
new file mode 100644
index 00000000..49f72d5b
--- /dev/null
+++ b/lib/edit/haunted.map
@@ -0,0 +1,49 @@
+# permanent wall
+F:X:61:0
+
+# granite
+F:#:57:0
+
+# up staircase
+F:<:6:0
+
+# Floor
+F:.:1:0
+
+# Locked Door
+F:D:38:0
+
+# Secret Door
+F:S:48:0
+
+# Great item
+F:g:1:0:0:*:*
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXX...D.........................X
+D:XXX...XXXXXXXXXXXXXXXXXXXXXXXXX.X
+D:XXX...X....X....X....X....X...X.X
+D:XXX...X....X....X....X....X...X.X
+D:XXX...X....X....X....X....X...X.X
+D:XXXDXXXDXXXXDXXXXDXXXXDXXXXDXXX.X
+D:XXX.............................X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X
+D:X.S.....X.....X.....X.....X...X.X
+D:X.X.....X.....X.....X.....X...X.X
+D:X.X.....X.....X.....X.....X...X.X
+D:X.X.....X.....X.....X.....X...X.X
+D:XgX.....X.....X.....X.....X...X.X
+D:XXXDXXXXXDXXXXXDXXXXXDXXXXXDXXX.X
+D:XXX.............................X
+D:XXXXXXXDXXXXXDXXXXXDXXXXXDXXXXXDX
+D:XgX<....X.....X.....X.....X.....X
+D:X.X.....X.....X.....X.....X.....X
+D:X.X.....X.....X.....X.....X.....X
+D:X.X.....X.....X.....X.....X.....X
+D:X.S.....X.....X.....X.....X.....X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:5
+
diff --git a/lib/edit/k_info.txt b/lib/edit/k_info.txt
new file mode 100644
index 00000000..ffae3ed1
--- /dev/null
+++ b/lib/edit/k_info.txt
@@ -0,0 +1,6418 @@
+# File: k_info.txt
+
+
+# This file is used to initialize the "lib/data/k_info.raw" file, which is
+# used to initialize the "object kind" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/data/k_info.raw" file.
+
+# Available slots are marked with # XXX: 294, 299, 444-464, ...
+
+# XXX XXX Add some "IGNORE_XXX" flags to Rings, Amulets, etc.
+
+# The old "MULTI_HUED" objects are now "violet", and no other object
+# is violet, so all six violet objects can be made "multi-hued", though
+# this would be a heinous hack. XXX XXX
+
+# Note that object zero is used for the "stack" picture (unused).
+
+
+# Version stamp (required)
+
+V:2.0.0
+
+
+##### Something special #####
+
+N:0:something
+G:&:w
+
+
+##### Mushrooms #####
+
+N:1:Blindness
+G:,:d
+I:80:1:500
+W:5:0:1:0
+A:5/1
+D:'E'ating this mushrooms will blind you. You cannot cast magic
+D:or see monsters when you are blinded. You can still use magic items or
+D:quaff potions.
+
+N:2:Paranoia
+G:,:d
+I:80:2:500
+W:5:0:1:0
+A:5/1
+D:'E'ating this mushroom will make you scared.
+D:You will not be able to hit your enemies in combat if you're scared.
+
+N:3:Confusion
+G:,:d
+I:80:3:500
+W:5:0:1:0
+A:5/1
+D:'E'ating this mushroom will confuse you. You will not be able to cast spells,
+D:use wands, staves or scrolls. You can still quaff potions, though.
+
+N:4:Hallucination
+G:,:d
+I:80:4:500
+W:10:0:1:0
+A:10/1
+D:'E'ating this mushroom will make you hallucinate.
+D:You will not be able to recognise any monster or item.
+
+N:5:Cure Poison
+G:,:d
+I:80:12:500
+W:10:0:1:60
+A:10/1
+D:'E'ating this mushroom will cure you from poison.
+
+N:6:Cure Blindness
+G:,:d
+I:80:13:500
+W:10:0:1:50
+A:10/1
+D:'E'ating this mushroom will cure you from blindness.
+
+N:7:Cure Paranoia
+G:,:d
+I:80:14:500
+W:10:0:1:25
+A:10/1
+D:'E'ating this mushroom will cure your paranoia. Paranoia is the state when you are
+D:too afraid to attack monsters.
+
+N:8:Cure Confusion
+G:,:d
+I:80:15:500
+W:10:0:1:50
+A:10/1
+D:'E'ating this mushroom will cure your confusion. Confusion is when you are too confused
+D:to cast spells, zap staves, aim wands or read scrolls.
+
+N:9:Weakness
+G:,:d
+I:80:6:500
+W:10:0:1:0
+A:10/1
+P:0:5d5:0:0:0
+D:'E'ating this mushroom will reduce your strength by one point.
+
+N:10:Unhealth
+G:,:d
+I:80:10:500
+W:15:0:1:50
+A:15/1
+P:0:10d10:0:0:0
+D:'E'ating this mushroom will reduce your constitution by one point.
+
+N:11:Restore Constitution
+G:,:d
+I:80:18:500
+W:20:0:1:350
+A:20/1
+D:'E'ating this mushroom will restore your constitution. Your constitution
+D:needs restoring when it is displayed in yellow.
+
+N:12:Restoring
+G:,:d
+I:80:19:500
+W:20:0:1:1000
+A:20/8:30/4:40/1
+D:'E'ating this mushroom will restore your strength, dexterity, constitution,
+D:intelligence, wisdom and charisma. These need restoring when they
+D:are displayed in yellow.
+
+N:13:Stupidity
+G:,:d
+I:80:8:500
+W:15:0:1:0
+A:15/1
+D:'E'ating this mushroom will reduce your intelligence by one point.
+D:That's a bad thing.
+
+N:14:Naivety
+G:,:d
+I:80:9:500
+W:15:0:1:0
+A:15/1
+D:'E'ating this mushroom will reduce your wisdom by one point.
+D:That's a bad thing.
+
+N:15:Poison
+G:,:d
+I:80:0:500
+W:5:0:1:0
+A:5/1:5/1
+P:0:4d4:0:0:0
+D:'E'ating this mushroom will poison you. Poisoning makes you lose one hitpoint
+D:per turn until you magically stop the poison or until your body has
+D:fought off the poison.
+D:That's a bad thing.
+
+N:16:Sickness
+G:,:d
+I:80:7:500
+W:10:0:1:0
+A:10/1
+P:0:4d4:0:0:0
+D:'E'ating this mushroom will reduce your constitution by one point.
+D:It will also damage you quite severely in the process.
+D:That's a bad thing.
+
+N:17:Paralysis
+G:,:d
+I:80:5:500
+W:20:0:1:0
+A:20/1
+D:'E'ating this mushroom will paralyse you for a certain time.
+D:Any nearby monsters will take this opportunity to kill you.
+D:That's a bad thing.
+
+N:18:Restore Strength
+G:,:d
+I:80:17:500
+W:20:0:1:350
+A:20/1
+D:'E'ating this mushroom will restore your strength. Your strength
+D:needs restoring when it is displayed in yellow.
+
+N:19:Disease
+G:,:d
+I:80:11:500
+W:20:0:1:50
+A:20/1
+P:0:10d10:0:0:0
+D:'E'ating this mushroom will reduce your strength by one point.
+D:It will also damage you quite severely in the process.
+D:That's a bad thing.
+
+N:20:Cure Serious Wounds
+G:,:d
+I:80:16:500
+W:15:0:1:75
+A:15/1
+D:'E'ating this mushroom will heal several hit points. Your hit points
+D:need healing when they are displayed in yellow or red.
+
+##### Normal Food #####
+
+N:21:& Ration~ of Food
+G:,:U
+I:80:35:5000
+W:0:0:10:3
+A:0/1:5/1:10/1
+D:Lightweight and filling. Not an incredible taste experience, but that'd be asking a bit much.
+D:You can 'E'at it.
+
+N:22:& Hard Biscuit~
+G:,:U
+I:80:32:500
+W:0:0:2:1
+D:It doesn't look great, and 'E'ating it will only fill your stomach a bit,
+D:for a short time.
+
+N:23:& Strip~ of Venison
+G:,:u
+I:80:33:1500
+W:0:0:2:2
+D:It looks great, and 'E'ating it will fill your stomach well.
+
+N:24:& Slime Mold~
+G:,:g
+I:80:36:3000
+W:1:0:5:2
+A:1/1
+D:It looks disgusting, but if you really want to you can 'E'at it.
+D:Not an incredible taste experience, but that'd be asking a bit much.
+
+# New - now Lembas works as a scroll of Satisfy Hunger
+N:25:& Lembas~
+G:,:B
+I:80:37:0
+W:5:0:3:10
+A:5/1:10/1:20/1
+D:A sort of cake, tasty and sustaining. It even helps to overcome weariness. Its fabrication
+D:is a secret of the elves. If you 'E'at it, you will be full.
+
+N:26:& Pint~ of Fine Ale
+G:,:y
+I:80:38:500
+W:0:0:5:1
+D:A bottle of a dark beer-like beverage. You can drink it by pressing 'E'.
+
+N:27:& Pint~ of Fine Wine
+G:,:r
+I:80:39:1000
+W:0:0:10:2
+D:A bottle of fine wine. You can drink it by pressing 'E'.
+
+##### Extra digger #####
+
+N:28:& Mattock~
+G:\:D
+I:20:7:3
+W:50:0:250:700
+A:50/2
+P:0:1d8:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:This is a digging tool. Use it to dig in walls, destroy doors, or cut wood.
+
+# The Blue Stone 'Toris Mejistos' -- see artifact list
+
+N:29:& Blue Stone~
+G:":B
+I:40:18:0
+W:60:0:3:90000
+F:INSTA_ART | SPECIAL_GENE
+
+##### Edged Weapons #####
+
+N:30:& Broken Dagger~
+G:|:D
+I:23:1:0
+W:0:0:5:1
+A:0/2:5/2
+P:0:1d1:-2:-4:0
+F:SHOW_MODS
+D:The blade itself is a foot long and broken off not far above the hilt.
+
+N:31:& Bastard Sword~
+G:|:W
+I:23:21:0
+W:15:0:140:350
+A:15/1
+P:0:3d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:This is a long, double-edged sword with a plain hilt that could
+D:be wielded in one or two hands. It's called a "bastard sword" because in
+D:size, it falls between the broad sword and the two-handed sword, thus not
+D:having a family of its own. It's typically around 51 inches long.
+D:It is effective for cutting through tougher armours. It could be used for
+D:thrusting, but most wielders swing it like a bat.
+
+N:32:& Scimitar~
+G:|:W
+I:23:18:0
+W:10:0:130:250
+A:10/1
+P:0:2d5:0:0:0
+F:SHOW_MODS
+D:This oriental blade has 2 edges and is deeply curved. It has a wide
+D:and very sharp end. It is the forefather of the sabre.
+
+N:33:& Tulwar~
+G:|:W
+I:23:15:0
+W:5:0:100:200
+A:5/1
+P:0:2d4:0:0:0
+F:SHOW_MODS
+D:This vicious sword is half sword and half club, with a slight hook on the tip.
+
+N:34:& Broad Sword~
+G:|:W
+I:23:16:0
+W:10:0:150:255
+A:10/1:15/1
+P:0:2d5:0:0:0
+F:SHOW_MODS
+D:This broader version of the long sword is a standard weapon in the army
+D:of Gondolin.
+
+N:35:& Short Sword~
+G:|:W
+I:23:10:0
+W:5:0:80:90
+A:5/1
+P:0:1d7:0:0:0
+F:SHOW_MODS
+D:This shorter version of the long sword is a common weapon for rogues
+D:and mages.
+
+N:36:& Blade~ of Chaos
+G:|:v
+I:23:30:0
+W:70:0:180:4000
+A:70/8
+P:0:6d5:0:0:0
+F:ATTR_MULTI
+F:RES_CHAOS | CHAOTIC | SHOW_MODS
+f:RES_CHAOS | CHAOTIC
+D:A mighty sword which seems to be completely blunt. However, it is a conduit
+D:into the realms of pure chaos and strikes its victims with the devastating
+D:might of chaos itself whenever it connects. It gives you resistance to chaos
+D:and it can polymorph, teleport, confuse or drain hit points from the monster
+D:you hit. It occasionally causes earthquakes as well.
+
+N:37:& Two-Handed Sword~
+G:|:W
+I:23:25:0
+W:30:0:200:775
+A:30/1:40/1
+P:0:3d6:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This blade is lot longer, wider and heavier than a long sword. You have to
+D:wield it with two hands. This means that wielding a shield makes fighting
+D:very difficult.
+
+N:38:& Main Gauche~
+G:|:W
+I:23:5:0
+W:3:0:30:25
+A:3/1
+P:0:1d5:0:0:0
+F:SHOW_MODS
+D:This blade is sinuously curved and tipped with a harpoon-like end.
+D:This blade has a large handguard and was designed as an off-hand weapon.
+D:This short but cruel blade is a favourite among orcs.
+
+N:39:& Cutlass~
+G:|:W
+I:23:12:0
+W:5:0:110:85
+A:5/1
+P:0:1d7:0:0:0
+F:SHOW_MODS
+D:This oriental weapon is a short, thick, curving sword
+D:with a single cutting edge. This simple slashing weapon
+D:is typically carried by buccaneers, pirates, and sailors.
+
+N:40:& Executioner's Sword~
+G:|:r
+I:23:28:0
+W:40:0:260:850
+A:40/1
+P:0:4d5:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:These weapons have been built in all sizes. They are custom-made
+D:for warriors that want to set out and kill their archenemy. These
+D:blades are rare, costly and very deadly.
+
+N:41:& Katana~
+G:|:W
+I:23:20:0
+W:20:0:120:400
+A:20/1
+P:0:3d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:The katana is a long blade with only a small disk for a guard.
+D:Its hilt is long enough for two hands, though it could be used
+D:with only one hand, and is usually wrapped in cloth or leather.
+D:The art of forging such swords is largely unknown in this part
+D:of Middle-earth, and such weapons are typically imported from
+D:distant lands.
+
+N:42:& Long Sword~
+G:|:W
+I:23:17:0
+W:10:0:130:300
+A:10/1:20/1
+P:0:2d5:0:0:0
+F:SHOW_MODS
+D:A long straight sword, tapering to a pronounced point. Mainly good for
+D:piercing attacks, but it can be used for slashing, too. It is a very
+D:popular design and has become standard issue in many armies.
+
+N:43:& Dagger~
+G:|:W
+I:23:4:0
+W:0:0:12:10
+A:0/1:5/1:10/1:20/1
+P:0:1d4:0:0:0
+F:SHOW_MODS
+D:It's the standard weapon of rogues and thieves. The blade is
+D:a foot long.
+
+N:44:& Rapier~
+G:|:W
+I:23:7:0
+W:5:0:40:42
+A:5/1
+P:0:1d6:0:0:0
+F:SHOW_MODS
+D:The rapier's hilt consisted of a pair of oval guards pierced with holes,
+D:recurved quillions, and a knuckle guard. The guard is very intricate
+D:and very effective as protection.
+
+N:45:& Sabre~
+G:|:W
+I:23:11:0
+W:5:0:50:50
+A:5/1
+P:0:1d7:0:0:0
+F:SHOW_MODS
+D:A long, one-edged, slightly curved sword with a knuckle guard and short
+D:hilt. It is two-edged in its lower part.
+
+N:46:& Small Sword~
+G:|:W
+I:23:8:0
+W:5:0:75:48
+A:5/1
+P:0:1d6:0:0:0
+F:SHOW_MODS
+D:It's the favourite weapon of strong mages and thieves. The blade is
+D:about twenty inches long. It's very easy to handle, although it is a lot less
+D:efficient than the longer and heavier designs.
+
+N:47:& Broken Sword~
+G:|:D
+I:23:2:0
+W:0:0:30:2
+A:0/2:5/2
+P:0:1d2:-2:-4:0
+F:SHOW_MODS
+D:Just a hilt and a few inches of blade, broken off in a jagged stump.
+D:Probably worthless.
+
+##### Hafted Weapons #####
+
+N:48:& Ball-and-Chain~
+G:\:D
+I:21:6:0
+W:20:0:150:200
+A:20/1
+P:0:2d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:This weapon has a ball linked with a chain to a wooden handle.
+D:Preferred tactic is smashing the brains of your opponent.
+
+N:49:& Whip~
+G:\:D
+I:21:2:0
+W:3:0:30:30
+A:3/1
+P:0:1d6:0:0:0
+F:SHOW_MODS
+D:Think Doctor Jones. This weapon is light and easy to fight with.
+D:It has nasty barbs and hooks fixed to the thong to make it useful
+D:in combat. Whips give easily multiple attacks.
+
+N:50:& Flail~
+G:\:D
+I:21:13:0
+W:10:0:150:353
+A:10/1
+P:0:2d6:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:This weapon was originally used to cut corn. More warlike versions
+D:sport a large blade stuck on a wooden handle. The hinge allows it to get
+D:past enemy defences or to strike with added momentum when used skillfully.
+
+N:51:& Two-Handed Flail~
+G:\:y
+I:21:18:0
+W:45:0:280:590
+A:45/1
+P:0:3d6:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This two-handed version of the flail gives the fighter a fearsome
+D:weapon that can do a fair amount of damage. It typically has several
+D:spiked metal lumps on chains.
+
+N:52:& Morning Star~
+G:\:D
+I:21:12:0
+W:10:0:150:396
+A:10/1
+P:0:2d6:0:0:0
+F:SHOW_MODS
+D:This weapon consists of a large club with chains that have wooden balls
+D:with metal spikes on the end.
+
+N:53:& Mace~
+G:\:D
+I:21:5:0
+W:5:0:120:130
+A:5/1
+P:0:2d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:This weapon is a club ending in a sphere. The sphere is studded
+D:with metal shards, and thus can both crush and cut your adversary.
+
+N:54:& Quarterstaff~
+G:\:U
+I:21:3:0
+W:10:0:150:200
+A:10/1
+P:0:1d9:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A long, wooden pole, usually the height of the wielder. Four of them can be
+D:made out of the trunk of one young tree, hence the name. The quarterstaff
+D:is an excellent weapon for travellers as it doubles both as a walking staff
+D:and as a deterrent against brigands. The quarterstaff is used more in
+D:fencing and brawling than melee combat.
+
+N:55:& War Hammer~
+G:\:D8
+I:21:8:0
+W:5:0:120:225
+A:5/1
+P:0:3d3:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A large hammer, designed to crush skulls with mighty strikes.
+
+N:56:& Lead-Filled Mace~
+G:\:D
+I:21:15:0
+W:15:0:180:502
+A:15/1
+P:0:3d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A large, mean mace filled with lead in order to wreak a maximum of havoc.
+
+N:57:& Mace~ of Disruption
+G:\:v
+I:21:20:0
+W:80:0:400:4300
+A:80/5
+P:0:5d8:0:0:0
+F:SLAY_UNDEAD | SHOW_MODS | MUST2H
+f:MUST2H | SLAY_UNDEAD
+D:This mace is custom-made for priests that go out to destroy evil.
+D:It is deadly, especially for undead.
+
+N:58:& Lucerne Hammer~
+G:\:B
+I:21:10:0
+W:10:0:120:376
+A:10/1
+P:0:2d5:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A war hammer combined with a spearpoint, mounted on a long pole.
+
+##### Polearms #####
+
+N:59:& Beaked Axe~
+G:/:s
+I:22:10:0
+W:15:0:180:408
+A:15/1
+P:0:2d6:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This polearm has a beak mounted opposite the blade.
+
+N:60:& Glaive~
+G:/:s
+I:22:13:0
+W:20:0:190:363
+A:20/1
+P:0:2d6:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A polearm with a long, slightly curved knife-like blade. It has spurs on
+D:the dull side of the blade. It's primarily a slashing and chopping weapon. Glaives
+D:are often used to protect archers, crossbowmen, and gunners while they reload.
+D:Outside of combat they are a popular processional weapon and therefore many
+D:have ornately carved blades.
+
+N:61:& Halberd~
+G:/:s
+I:22:15:0
+W:25:0:190:430
+A:25/1
+P:0:3d5:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:The halberd has a broad, short axe blade on a 5 - 6ft long haft, with a
+D:spearpoint at the top, often a back-spike and occasionally a butt-spike. Used to
+D:combat heavier armour. It's usually used for cutting and stabbing. It's the most
+D:versatile polearm in Middle-earth. The axe is used in general melee. The top
+D:pike is used to pierce armour, or is set against a cavalry charge. The back
+D:hook could unseat horses, trip opponents, parry a blow, or be a general
+D:piercing weapon.
+
+N:62:& Awl-Pike~
+G:/:s
+I:22:4:0
+W:10:0:160:340
+A:10/1
+P:0:1d8:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:This is a polearm with a long square-sectioned spike on the end.
+
+N:63:& Pike~
+G:/:s
+I:22:8:0
+W:15:0:160:358
+A:15/1
+P:0:2d5:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A staff, 16-18 feet long, that has a small piercing head about 10 inches
+D:long. The pike is often used by infantry to fend off cavalry. It is very
+D:effective against mounted troops.
+
+N:64:& Spear~
+G:/:s
+I:22:2:0
+W:5:0:50:36
+A:5/1
+P:0:1d6:0:0:0
+F:SHOW_MODS
+D:Spears tend to have a strong, wide, leaf-shaped head with a ridge down the
+D:middle. The spearhead is attached to the wooden shaft by a socket. The
+D:sockets are usually riveted to the shaft and some have two small lugs near
+D:the base of the socket to allow the head to be possibly bound on as well.
+D:This spear is 7 feet long.
+
+N:65:& Trident~
+G:/:y
+I:22:5:0
+W:5:0:70:120
+A:5/1
+P:0:1d8:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:The trident is based on the pitchfork. In fact, when not used as a weapon,
+D:it is often employed as a pitchfork. It is famous for its uses in
+D:gladiatorial arenas. It is used much like a spear and could even be thrown
+D:like one in desperate situations.
+
+N:66:& Lance~
+G:/:s
+I:22:20:0
+W:10:0:300:230
+A:10/1
+P:0:2d8:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This is the original polearm. It is shaped like a spear but is bigger. It's
+D:meant to fend off enemies, not to be thrown.
+
+N:67:& Great Axe~
+G:/:s
+I:24:25:0
+W:40:0:230:500
+A:40/1
+P:0:4d4:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:A huge and heavy two-headed axe.
+
+N:68:& Battle Axe~
+G:/:s
+I:22:22:0
+W:15:0:170:334
+A:15/1
+P:0:2d8:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:Nordic polearm with a broad blade and a hook mounted on the end of the shaft.
+D:The Nordics' take on the halberd. The polearm of choice for many Nordics,
+D:ideally suited for slashing, thrusting, and unseating cavalry.
+
+N:69:& Lochaber Axe~
+G:/:D
+I:22:28:0
+W:45:0:250:750
+A:45/1
+P:0:3d8:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:Nordic polearm with a broad blade and a hook mounted on the end of the shaft.
+D:A Nordic version of the halberd. The polearm of choice for many Nordics,
+D:this weapon is ideally suited for slashing, thrusting, and unseating cavalry.
+
+N:70:& Broad Axe~
+G:/:s
+I:24:11:0
+W:15:0:160:304
+A:15/1
+P:0:2d6:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A one-headed axe made for combat, with an elongated moon-shaped blade.
+
+N:71:& Scythe~
+G:/:s
+I:22:17:0
+W:45:0:250:800
+A:45/1
+P:0:5d3:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A simple farm implement, converted into a weapon by slightly straightening
+D:its blade and putting it in line with its pole, instead of the typical
+D:right angle.
+
+N:72:& Scythe~ of Slicing
+G:/:r
+I:22:30:0
+W:80:0:250:10000
+A:80/20
+P:0:8d4:0:0:0
+F:SHOW_MODS | MUST2H | WOUNDING | VORPAL
+f:MUST2H | VORPAL | WOUNDING
+D:The simple design of the war scythe, but this one uses a finely crafted and
+D:incredibly sharp steel blade which causes terrible wounds when it hits.
+
+##### Bows, Crossbows, Slings #####
+
+N:73:& Short Bow~
+G:}:U
+I:19:12:0
+W:3:0:30:50
+A:3/1:50/2
+F:SHOW_MODS
+D:A piece of bent wood, fitted with a string. Easy to transport and use, but
+D:its effect is not very impressive. You can 'f'ire it after you 'w'ield it.
+
+N:74:& Long Bow~
+G:}:U
+I:19:13:0
+W:10:0:40:120
+A:10/1:70/2
+F:SHOW_MODS
+D:A bow almost as long as a human. It takes considerable strength and skill to
+D:use and is rather awkward to carry around inside buildings. Nevertheless, it
+D:shoots arrows with astonishing force. You can 'f'ire it after you 'w'ield it.
+
+N:75:& Light Crossbow~
+G:}:s
+I:19:23:0
+W:15:0:110:140
+A:15/1:60/2
+F:SHOW_MODS
+D:A metal bow mounted on a wooden stock. It is used for shooting bolts. This
+D:design is relatively light and not too difficult to cock, but also shoots its
+D:quarrels with less force. You can 'f'ire with it after you 'w'ield it.
+
+N:76:& Heavy Crossbow~
+G:}:s
+I:19:24:0
+W:30:0:200:300
+A:30/1:80/2
+F:SHOW_MODS
+D:A metal bow mounted on a wooden stock, with attached cocking mechanism. It
+D:takes considerable time and strength to cock it and it's rather heavy.
+D:However, it hurls its ammunition with incredible force. You can 'f'ire with
+D:it after you 'w'ield it.
+
+N:77:& Sling~
+G:}:u
+I:19:2:0
+W:1:0:5:5
+A:1/1:40/2
+F:SHOW_MODS
+D:A simple cloth or leather pouch with two strings attached. It is used to hurl
+D:pebbles or iron shots. You can 'f'ire with it after you 'w'ield it.
+
+##### Missiles #####
+
+N:78:& Arrow~
+G:{:U
+I:17:1:0
+W:3:0:2:1
+A:3/1:15/1:50/1
+P:0:1d4:0:0:0
+F:SHOW_MODS
+D:A simple metal head on a piece of wood or bamboo, fitted with some feathers.
+D:You can use it for 'f'iring a bow.
+
+N:79:& Seeker Arrow~
+G:{:G
+I:17:2:0
+W:55:0:2:20
+A:55/2:80/2
+P:0:4d4:0:0:0
+F:SHOW_MODS
+D:A precision-made arrow, which allows you to hit precisely the most vulnerable
+D:place of an opponent. You can use it for 'f'iring a bow.
+
+N:80:& Bolt~
+G:{:s
+I:18:1:0
+W:3:0:3:2
+A:3/1:25/1:60/1
+P:0:1d5:0:0:0
+F:SHOW_MODS
+D:A short metal arrow, to be 'f'ired with a crossbow.
+
+N:81:& Seeker Bolt~
+G:{:B
+I:18:2:0
+W:65:0:3:25
+A:65/2:90/2
+P:0:4d5:0:0:0
+F:SHOW_MODS
+D:A precision-made bolt, which allows you to hit exactly the most vulnerable
+D:place of an opponent. You can use it for 'f'iring a crossbow.
+
+N:82:& Rounded Pebble~
+G:{:s
+I:16:0:0
+W:0:0:4:1
+A:0/1:10/2
+P:0:1d2:0:0:0
+F:SHOW_MODS
+D:Small round stones. When fired with a sling, they could even hurt a giant.
+D:You can use them for 'f'iring a sling.
+
+N:83:& Iron Shot~
+G:{:s
+I:16:1:0
+W:3:0:5:2
+A:3/1:40/2
+P:0:1d4:0:0:0
+F:SHOW_MODS
+D:Metal balls, made for shooting with slings. You can use them for 'f'iring a
+D:sling.
+
+##### Shovels and Picks #####
+
+N:84:& Shovel~
+G:\:s
+I:20:1:1
+W:1:0:60:10
+A:5/2
+P:0:1d2:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:A simple digging tool for shovelling away rubble and maybe even soft rock.
+
+N:85:& Gnomish Shovel~
+G:\:G
+I:20:2:2
+W:20:0:60:100
+A:20/3
+P:0:1d2:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:Crafted by the gnomes, its design profits greatly from the gnomes' expertise
+D:honed in the burrowing of their hovels.
+
+N:86:& Dwarven Shovel~
+G:\:B
+I:20:3:3
+W:40:0:120:200
+A:40/4
+P:0:1d3:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:The lighter digging tool used by dwarves to remove debris, but also good
+D:enough to clear away bits of rock when no pick is easily available.
+
+N:87:& Pick~
+G:\:s
+I:20:4:1
+W:5:0:150:50
+A:10/2
+P:0:1d3:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:A heavy digging tool, primarily for earthworking, but also good for tunnelling
+D:through stone, if one is willing to perform the time-consuming labour.
+
+N:88:& Orcish Pick~
+G:\:g
+I:20:5:2
+W:30:0:180:300
+A:30/3
+P:0:1d3:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:Although orcs tend to dig rather untidy mines and overcome difficulties in
+D:this work less by skill than mere stubbornness, they have amassed
+D:quite some expertise in mining, which has gone into this digging tool.
+
+N:89:& Dwarven Pick~
+G:\:b
+I:20:6:3
+W:50:0:200:600
+A:50/4
+P:0:1d4:0:0:0
+F:TUNNEL
+f:TUNNEL
+D:The dwarves, miners of legend, have made this pick, which is so expertly
+D:crafted that even weaklings can bore through solid rock with it.
+
+##### Armour #####
+
+N:90:& Elven Cloak~
+G:(:G
+I:35:2:0
+W:30:0:5:1500
+A:30/4
+P:4:0d0:0:0:4
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_FIRE | IGNORE_ELEC
+F:STEALTH | SEARCH | LUCK
+f:STEALTH
+D:A wonderfully light cloak coloured in brown and green hues. Its colouring
+D:greatly helps the wearer to avoid undesired attention. Wearing it makes you
+D:feel lucky and somehow you seem to see what was meant to stay unseen.
+
+N:91:& Pair~ of Soft Leather Boots
+G:]:U
+I:30:2:0
+W:3:0:20:7
+A:3/1
+P:2:1d1:0:0:0
+D:A pair of low boots, comfortable to wear.
+
+N:92:& Pair~ of Hard Leather Boots
+G:]:U
+I:30:3:0
+W:5:0:40:12
+A:5/1
+P:3:1d1:0:0:0
+D:A pair of boots, with hardened leather at the caps, offering a little
+D:extra protection for the feet.
+
+N:93:& Pair~ of Metal Shod Boots
+G:]:s
+I:30:6:0
+W:20:0:80:50
+A:20/1
+P:6:1d1:0:0:0
+D:Heavy boots, with metal strips at the toes, heels and other vulnerable parts,
+D:to better protect the wearer's feet from harm.
+
+N:94:& Hard Leather Cap~
+G:]:u
+I:32:2:0
+W:3:0:15:12
+A:3/1
+P:2:0d0:0:0:0
+D:A piece of protective headgear made from hardened leather, just covering the
+D:skull.
+
+N:95:& Metal Cap~
+G:]:s
+I:32:3:0
+W:10:0:20:30
+A:10/1
+P:3:1d1:0:0:0
+D:A metal skullcap with nose and cheekguards.
+
+N:96:& Iron Helm~
+G:]:s
+I:32:5:0
+W:20:0:75:75
+A:20/1
+P:5:1d3:0:0:0
+D:A large helmet that can protect the entire head. Ventilation and bad vision
+D:can be a problem, however.
+
+N:97:& Steel Helm~
+G:]:W
+I:32:6:0
+W:40:0:60:200
+A:40/1
+P:6:1d3:0:0:0
+D:A helmet which protects the entire head. The expensive steel as base material
+D:allows it to offer very good protection while being fairly light.
+
+N:98:& Iron Crown~
+G:]:s
+I:33:10:0
+W:45:0:20:500
+A:45/1
+P:0:1d1:0:0:0
+D:An iron circlet which might be worn, but which is purely ornamental unless it
+D:has special powers.
+
+N:99:& Golden Crown~
+G:]:y
+I:33:11:0
+W:45:0:30:1000
+A:45/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+D:A gilded crown, which just looks good. Sometimes such headgear also has
+D:additional properties which might make it worth wearing.
+
+N:100:& Jewel Encrusted Crown~
+G:]:v
+I:33:12:0
+W:50:0:40:2000
+A:50/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+D:A gorgeous-looking silver crown, adorned with several gems. You might wear it
+D:on your head, if you really think you're worthy.
+
+N:101:& Robe~
+G:(:b
+I:36:2:0
+W:1:0:20:4
+A:1/1:50/1
+P:2:0d0:0:0:0
+D:A full-length garment which can be worn on the body. It is not really
+D:armour, but mages often wear them as they are very light and don't hinder
+D:movement much.
+
+N:102:& Filthy Rag~
+G:(:D
+I:36:1:0
+W:0:0:20:1
+A:0/1
+P:1:0d0:0:0:-1
+D:A piece of discarded cloth, smelly and dirty. Eurgh. You're not going to
+D:wear this, are you?
+
+N:103:& Soft Leather Armour~
+G:(:U
+I:36:4:0
+W:3:0:80:18
+A:3/1
+P:4:0d0:0:0:0
+D:A leather jerkin, light and unencumbering, but not very protective.
+
+N:104:& Soft Studded Leather~
+G:(:U
+I:36:5:0
+W:3:0:90:35
+A:3/1
+P:5:1d1:0:0:0
+D:A leather jerkin with metal studs in critical places offering slightly better
+D:protection.
+
+N:105:& Hard Leather Armour~
+G:(:U
+I:36:6:0
+W:5:0:100:150
+A:5/1
+P:6:1d1:-1:0:0
+D:A leather armour covering only the body. It is made of hardened leather to
+D:make it harder to penetrate. It's also a bit harder to move in, as it is
+D:rather stiff.
+
+N:106:& Hard Studded Leather~
+G:(:U
+I:36:7:0
+W:10:0:110:200
+A:10/1
+P:7:1d2:-1:0:0
+D:A suit of hardened leather armour, reinforced with metal studs.
+
+N:107:& Leather Scale Mail~
+G:(:U
+I:36:11:0
+W:15:0:140:450
+A:15/1
+P:11:1d1:-1:0:0
+D:A suit of armour made of overlapping hardened leather scales. It offers good
+D:protection while still being rather lightweight.
+
+N:108:& Metal Scale Mail~
+G:[:s
+I:37:3:0
+W:25:0:250:550
+A:25/1
+P:13:1d4:-2:0:0
+D:A suit of overlapping metal scales, sewn onto a leather or cloth jerkin.
+
+N:109:& Chain Mail~
+G:[:s
+I:37:4:0
+W:25:0:220:750
+A:25/1
+P:14:1d4:-2:0:0
+D:A suit of interlinked metal rings, to be worn over a woollen garment.
+
+N:110:& Rusty Chain Mail~
+G:[:r
+I:37:1:0
+W:25:0:200:550
+A:25/1
+P:14:1d4:-5:0:-8
+D:This chain mail has rusted beyond repair. It can still be worn, but some of
+D:the rings have gone missing and the rust has made the suit inflexible.
+D:Consequently, it offers very poor protection and is cumbersome to move in.
+
+N:111:& Augmented Chain Mail~
+G:[:s
+I:37:6:0
+W:30:0:270:900
+A:30/1
+P:16:1d4:-2:0:0
+D:A suit of interlinked metal rings, with additional metal plates or scales
+D:covering vulnerable parts of the wearer.
+
+N:112:& Bar Chain Mail~
+G:[:s
+I:37:8:0
+W:35:0:280:950
+A:35/1
+P:18:1d4:-2:0:0
+D:A suit of interlinked metal rings, with additional short metal bars added in
+D:many places to prevent penetration of the armour by piercing attacks.
+
+N:113:& Metal Brigandine Armour~
+G:[:s
+I:37:9:0
+W:35:0:290:1100
+A:35/1
+P:19:1d4:-3:0:0
+D:This is a leather armour with many small metal plates fixed to it, covering
+D:it entirely.
+
+N:114:& Partial Plate Armour~
+G:[:W
+I:37:12:0
+W:45:0:260:1200
+A:45/1
+P:22:1d6:-3:0:0
+D:An armour made of steel plates, covering only the body of the wearer.
+
+N:115:& Metal Lamellar Armour~
+G:[:W
+I:37:13:0
+W:45:0:340:1250
+A:45/1
+P:23:1d6:-3:0:0
+D:Lamellar consists of small rectangular plates (lames) attached to each other
+D:at each edge or corner with leather lacings through small holes in the plates.
+
+N:116:& Full Plate Armour~
+G:[:W
+I:37:15:0
+W:45:0:380:1350
+A:45/1
+P:25:2d4:-3:0:0
+D:A suit of armour made of metal plates, covering the body, arms and upper legs.
+D:A very effective but very heavy armour.
+
+N:117:& Ribbed Plate Armour~
+G:[:W
+I:37:18:0
+W:50:0:380:1500
+A:50/1
+P:28:2d4:-3:0:0
+D:This full suit of armour has been strengthened in places to better deflect or
+D:absorb blows.
+
+N:118:& Adamantite Plate Mail~
+G:[:G
+I:37:30:0
+W:75:0:420:20000
+A:75/4
+P:40:2d4:-4:0:0
+F:IGNORE_ACID
+D:A suit of plate armour fashioned from an unbreakable crystal by mage-smiths.
+
+N:119:& Mithril Plate Mail~
+G:[:B
+I:37:25:0
+W:60:0:300:15000
+A:60/3
+P:35:2d4:-3:0:0
+F:IGNORE_ACID
+D:A full suit of plate armour, fashioned from the rare true-silver. Only the
+D:dwarves know the secret of making armour or weapons of this metal.
+
+N:120:& Mithril Chain Mail~
+G:[:B
+I:37:20:0
+W:55:0:150:7000
+A:55/3
+P:28:1d4:-1:0:0
+F:IGNORE_ACID
+D:A suit of chain mail, made by dwarven smiths from the rare and precious metal
+D:also called true-silver.
+
+N:121:& Double Chain Mail~
+G:[:s
+I:37:7:0
+W:30:0:250:850
+A:30/1
+P:16:1d4:-2:0:0
+D:A suit of chain mail, with an additional layer of mail in some places.
+
+# This shield does not belong here
+
+N:122:& Shield~ of Deflection
+G:[:B
+I:34:10:0
+W:70:0:100:10000
+A:70/3
+P:10:1d1:0:0:10
+F:IGNORE_ACID
+D:A large shield fashioned from a metal alloy that is not subject to corrosion.
+D:It was especially crafted to better deflect attacks.
+
+### The Cloaks ###
+
+N:123:& Cloak~
+G:(:g
+I:35:1:0
+W:1:0:10:3
+A:1/1:20/1
+P:1:0d0:0:0:0
+D:A cloth coat typically worn as a loose outer garment. It is spacious enough
+D:to be worn even over bulky metal armour.
+
+N:124:& Shadow Cloak~
+G:(:D
+I:35:6:1
+W:60:0:5:7500
+A:75/4
+P:6:0d0:0:0:4
+F:RES_DARK | RES_LITE | STEALTH
+f:STEALTH
+D:A rare cloak imbued with magic to radiate a strange twilight, absorbing both
+D:extreme brightness and darkness.
+
+### The Gloves ###
+
+N:125:& Set~ of Leather Gloves
+G:]:U
+I:31:1:0
+W:1:0:5:3
+A:1/1
+P:1:0d0:0:0:0
+D:Light gloves which do not seriously hinder finger movements, while still
+D:protecting the hands somewhat.
+
+N:126:& Set~ of Gauntlets
+G:]:U
+I:31:2:0
+W:10:0:25:35
+A:10/1
+P:2:1d1:0:0:0
+D:Metal gloves protecting the hands up to the middle of the lower arm.
+
+N:127:& Set~ of Cesti
+G:]:W
+I:31:5:0
+W:50:0:40:100
+A:50/1
+P:5:1d1:0:0:0
+D:A set of metal gloves with nasty spikes and barbs. Though originally made to
+D:help in combat, the additional metal on the backs of the hands also offers a
+D:lot more protection.
+
+### The shields ###
+
+N:128:& Small Leather Shield~
+G:):U
+I:34:2:0
+W:3:0:50:30
+A:3/1
+P:2:1d1:0:0:0
+D:A small disc of lindenwood, with a leather covering on one side.
+
+N:129:& Large Leather Shield~
+G:):U
+I:34:4:0
+W:15:0:100:120
+A:15/1
+P:4:1d2:0:0:0
+D:A large oval or rectangular shield. It is made of wood, typically linden, and
+D:covered with a layer of hardened leather to offer better protection.
+
+N:130:& Small Metal Shield~
+G:):s
+I:34:3:0
+W:10:0:65:50
+A:10/1
+P:3:1d2:0:0:0
+D:A small shield strengthened with a layer of metal.
+
+N:131:& Large Metal Shield~
+G:):s
+I:34:5:0
+W:30:0:120:200
+A:30/1
+P:5:1d3:0:0:0
+D:A large piece of wood, rectangular or oval in shape, and covered with metal
+D:to strengthen it. It's to be worn strapped to the arm not occupied by the
+D:weapon when fighting.
+
+##### Rings #####
+
+N:132:Strength
+G:=:d
+I:45:24:0
+W:30:0:2:500
+A:30/1
+F:STR | HIDE_TYPE
+f:STR
+D:This bauble magically improves your strength.
+
+N:133:Dexterity
+G:=:d
+I:45:26:0
+W:30:0:2:500
+A:30/1
+F:DEX | HIDE_TYPE
+f:DEX
+D:This piece of jewellery magically improves your agility.
+
+N:134:Constitution
+G:=:d
+I:45:27:0
+W:30:0:2:500
+A:30/1
+F:CON | HIDE_TYPE
+f:CON
+D:This ring magically grants you health, improving your constitution.
+
+N:135:Intelligence
+G:=:d
+I:45:25:0
+W:30:0:2:500
+A:30/1
+F:INT | HIDE_TYPE
+f:INT
+D:This magical piece of jewellery makes you smarter.
+
+N:136:Speed
+G:=:d
+I:45:31:0
+W:75:0:2:100000
+A:75/1
+F:SPEED | HIDE_TYPE
+f:SPEED
+D:This wonderful ring grants you additional energy, allowing you to act faster.
+
+N:137:Searching
+G:=:d
+I:45:23:0
+W:5:0:2:250
+A:5/1
+F:SEARCH | HIDE_TYPE
+f:SEARCH
+D:This ring magically improves your attention, so you can detect hidden things better.
+
+# New : It can be activated but at the cost of its destruction
+N:138:Teleportation
+G:=:d
+I:45:4:0
+W:5:0:2:250
+A:5/1
+a:HARDCORE=DEST_TELE
+F:CURSED | TELEPORT | EASY_KNOW | ACTIVATE
+f:TELEPORT
+D:This ring will uncontrollably send you to different places at its whim.
+D:You can use its power once at your will, but it will destroy the ring.
+
+N:139:Slow Digestion
+G:=:d
+I:45:6:0
+W:5:0:2:250
+A:5/1
+F:SLOW_DIGEST | EASY_KNOW
+f:SLOW_DIGEST
+D:This magical bauble grants you some sustenance, allowing you to subsist on less food.
+
+N:140:Fire Resistance
+G:=:d
+I:45:8:0
+W:10:0:2:250
+A:10/1
+F:RES_FIRE | IGNORE_FIRE | EASY_KNOW
+f:RES_FIRE
+D:This piece of jewellery grants you some protection from the burning heat of fire.
+
+N:141:Cold Resistance
+G:=:d
+I:45:9:0
+W:10:0:2:250
+A:10/1
+F:RES_COLD | IGNORE_COLD | EASY_KNOW
+f:RES_COLD
+D:This piece of jewellery grants you some protection from the chilling forces of cold.
+
+N:142:Levitation
+G:=:d
+I:45:7:0
+W:5:0:2:200
+A:5/1
+F:FEATHER | EASY_KNOW
+f:FEATHER
+D:When you put on this ring, you will be able to float just above the floor.
+D:It prevents you from drowning, and all your falls will be painless.
+
+N:143:Poison Resistance
+G:=:d
+I:45:20:0
+W:60:0:2:16000
+A:60/2
+F:RES_POIS | EASY_KNOW
+f:RES_POIS
+D:This magical ring grants protection from poison.
+D:It is rumoured that in deep dungeons monsters can kill you at once if you
+D:don't have poison resistance.
+
+N:144:Free Action
+G:=:d
+I:45:21:0
+W:20:0:2:1500
+A:20/1
+F:FREE_ACT | EASY_KNOW
+f:FREE_ACT
+D:This magical bauble prevents you from being held.
+D:Some monsters will paralyse you and then kill you if you lack free action.
+
+N:145:Weakness
+G:=:d
+I:45:2:-5
+W:5:0:2:0
+A:5/1
+F:CURSED | STR | HIDE_TYPE
+f:STR
+D:This accursed ring will sap your strength, rendering you much weaker as long as you wear it.
+
+N:146:Flames
+G:=:d
+I:45:18:0
+W:50:0:2:3000
+A:50/1
+P:0:0d0:0:0:15
+a:HARDCORE=BA_FIRE_4
+F:RES_FIRE | IGNORE_FIRE | ACTIVATE
+f:RES_FIRE
+D:This fiery circlet grants you protection, makes fire less dangerous and even
+D:allows you to call forth a ball of flame.
+
+N:147:Acid
+G:=:d
+I:45:17:0
+W:50:0:2:3000
+A:50/1
+P:0:0d0:0:0:15
+a:HARDCORE=BA_ACID_4
+F:RES_ACID | IGNORE_ACID | ACTIVATE
+f:RES_ACID
+D:This magical ring is imbued with spells of devouring acid, granting protection against such
+D:assaults and the ability to shoot acid at your foes.
+
+N:148:Ice
+G:=:d
+I:45:19:0
+W:50:0:2:3000
+A:50/1
+a:HARDCORE=BA_COLD_4
+P:0:0d0:0:0:15
+F:RES_COLD | IGNORE_COLD | ACTIVATE
+f:RES_COLD
+D:This ring is imbued with supernatural cold, which makes you less vulnerable to such effects
+D:and occasionally allows you to throw balls of ice at your foes.
+
+N:149:Woe
+G:=:d
+I:45:0:-5
+W:50:0:2:0
+A:50/1
+F:CURSED | TELEPORT | WIS | CHR | HIDE_TYPE | AUTO_CURSE
+D:This accursed ring will turn you into a bumbling fool and, in addition, magically
+D:transports you to places you never wanted to see. It can recurse itself if
+D:you leave it on too long.
+
+N:150:Stupidity
+G:=:d
+I:45:3:-5
+W:5:0:2:0
+A:5/1
+F:CURSED | INT | HIDE_TYPE
+f:INT
+D:This wicked ring feeds off your intellect, magically making you stupid.
+
+N:151:Damage
+G:=:d
+I:45:29:0
+W:20:0:2:500
+A:20/1
+F:HIDE_TYPE
+D:This ring makes your hands magically strong in combat, allowing you to inflict
+D:greater pain with your hand-to-hand attacks.
+
+N:152:Accuracy
+G:=:d
+I:45:28:0
+W:20:0:2:500
+A:20/1
+F:HIDE_TYPE
+D:This ring magically improves your control in combat, allowing you to hit more often.
+
+N:153:Protection
+G:=:d
+I:45:16:0
+W:10:0:2:500
+A:10/1
+D:This ring creates a magical aura around you, protecting you against the blows of your enemies.
+
+N:154:Aggravate Monster
+G:=:d
+I:45:1:0
+W:5:0:2:0
+A:5/1
+F:CURSED | AGGRAVATE | EASY_KNOW | AUTO_CURSE
+f:AGGRAVATE
+D:This faithless ring will draw opponents' attention towards its hapless owner.
+
+N:155:See Invisible
+G:=:d
+I:45:22:0
+W:30:0:2:340
+A:30/1
+F:SEE_INVIS | EASY_KNOW
+f:SEE_INVIS
+D:This magical piece of jewellery allows your eyes to perceive beings otherwise unseen.
+
+N:156:Sustain Strength
+G:=:d
+I:45:10:0
+W:20:0:2:400
+A:20/1
+F:SUST_STR | EASY_KNOW
+f:SUST_STR
+D:This magical bauble protects your physical force against attacks attempting to drain it.
+
+N:157:Sustain Intelligence
+G:=:d
+I:45:11:0
+W:20:0:2:400
+A:20/1
+F:SUST_INT | EASY_KNOW
+f:SUST_INT
+D:This magical ring protects your intellect against attempts to lower it.
+
+N:158:Sustain Wisdom
+G:=:d
+I:45:12:0
+W:20:0:2:400
+A:20/1
+F:SUST_WIS | EASY_KNOW
+f:SUST_WIS
+D:This magical ring protects you from attempts to make you more foolish.
+
+N:159:Sustain Constitution
+G:=:d
+I:45:13:0
+W:20:0:2:400
+A:20/1
+F:SUST_CON | EASY_KNOW
+f:SUST_CON
+D:This magical ring protects your health, making it impossible for your opponents to lower it.
+
+N:160:Sustain Dexterity
+G:=:d
+I:45:14:0
+W:20:0:2:400
+A:20/1
+F:SUST_DEX | EASY_KNOW
+f:SUST_DEX
+D:This magical ring protects your nerves, so that you will never become clumsy.
+
+N:161:Sustain Charisma
+G:=:d
+I:45:15:0
+W:20:0:2:400
+A:20/1
+F:SUST_CHR | EASY_KNOW
+f:SUST_CHR
+D:This ring magically protects your beauty and charm from attempts to make you ugly.
+
+N:162:Slaying
+G:=:d
+I:45:30:0
+W:40:0:2:1000
+A:40/1
+F:SHOW_MODS
+D:This ring magically improves your fighting prowess, allowing to hit more often and harder.
+
+##### Amulets #####
+
+N:163:Brilliance
+G:":d
+I:40:6:0
+W:50:0:3:1000
+A:50/4
+F:INT | WIS | HIDE_TYPE | LITE1
+D:This talisman grants a sharper wit, greater insight and brightness to light dark places.
+
+N:164:Charisma
+G:":d
+I:40:7:0
+W:30:0:3:500
+A:30/1
+F:CHR | HIDE_TYPE
+f:CHR
+D:This amulet grants beauty beyond mere looks.
+
+N:165:Searching
+G:":d
+I:40:5:0
+W:15:0:3:600
+A:15/1
+F:SEARCH | HIDE_TYPE
+f:SEARCH
+D:This amulet grants keen sight, finding things that are hidden.
+
+N:166:Teleportation
+G:":d
+I:40:1:0
+W:10:0:3:250
+A:10/1
+F:CURSED | TELEPORT | EASY_KNOW
+f:TELEPORT
+D:This amulet nastily throws you all over the place.
+
+N:167:Slow Digestion
+G:":d
+I:40:3:0
+W:15:0:3:200
+A:15/1
+F:SLOW_DIGEST | EASY_KNOW
+f:SLOW_DIGEST
+D:This talisman will make you hungry less quickly when worn.
+
+N:168:Acid Resistance
+G:":d
+I:40:4:0
+W:10:0:3:250
+A:10/1
+F:RES_ACID | IGNORE_ACID | EASY_KNOW
+f:RES_ACID
+D:This magical talisman will make the corroding forces of acid less threatening to your health.
+
+N:169:Adornment
+G:":d
+I:40:2:0
+W:10:0:3:20
+A:10/1
+F:EASY_KNOW
+D:This amulet is not magical. It just looks good.
+
+##### Extra armour #####
+
+N:170:& Double Ring Mail~
+G:[:s
+I:37:5:0
+W:25:0:230:700
+A:25/1
+P:15:1d4:-2:0:0
+D:A suit of leather armour with metal rings sewn onto it. In addition, in important parts it is
+D:reinforced with mail.
+
+##### Additional amulets #####
+
+N:171:the Magi
+G:":d
+I:40:8:0
+W:70:0:3:30000
+A:70/8
+P:0:0d0:-4:-4:0
+F:INT | SUST_INT | SEARCH | SPELL_CONTAIN | WIELD_CAST
+F:FREE_ACT | RES_BLIND | RES_CONF |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This rare amulet is highly desirable for mages, as it makes its wearer smarter, more attentive
+D:and impervious to magics which would make their own magic-use impossible.
+
+N:172:Doom
+G:":d
+I:40:0:-5
+W:50:0:3:0
+A:50/1
+F:CURSED | STR | INT | WIS | DEX | CON | CHR | HIDE_TYPE
+F:AUTO_CURSE | CURSE_NO_DROP
+D:This wicked amulet will drain all your abilities, turning you into a mere shadow of yourself. It
+D:is exceedingly hard to get rid of.
+
+##### Scrolls #####
+
+N:173:Enchant Weapon To-Hit
+G:?:d
+I:70:17:0
+W:15:0:5:125
+A:15/1
+D:This magical scroll will allow you to improve the accuracy of a weapon in your possession.
+D:However, weapons which are already very highly enchanted are more difficult to improve
+D:further.
+
+N:174:Enchant Weapon To-Dam
+G:?:d
+I:70:18:0
+W:15:0:5:125
+A:15/1
+D:Upon reading this scroll, a magical enchantment will be placed on a weapon in your
+D:possession, increasing the pain it inflicts when hitting. On very highly enchanted weapons this
+D:enchantment may fail, wasting the scroll.
+
+N:175:Enchant Armour
+G:?:d
+I:70:16:0
+W:15:0:5:125
+A:15/1
+D:This scroll will try to enchant a piece of armour in your possession, making it more effective
+D:in protecting you. Highly enchanted armour is likely not to accept this enchantment, however.
+
+N:176:Identify
+G:?:d
+I:70:12:0
+W:1:0:5:50
+A:1/1:5/1:10/1:30/1
+D:If you read this scroll, the identity of an item you specify will be laid open to you.
+
+N:177:*Identify*
+G:?:d
+I:70:13:0
+W:30:0:5:1000
+A:30/1:50/2:80/1:100/1
+D:This scroll will allow you to gain insight into an object's special properties.
+D:Only the highly magical objects, like rare rings and amulets or very unusual weapons
+D:and armour possess abilities which warrant the use of this magic.
+
+N:178:Rumour
+G:?:d
+I:70:51:0
+W:1:0:5:10
+A:1/1
+D:A piece of paper inscribed with a little text. You may meditate over it or ignore it at your
+D:leisure.
+
+N:179:Chaos
+G:?:d
+I:70:50:0
+W:100:0:5:10000
+A:100/8
+F:IGNORE_FIRE | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC
+D:A piece of paper inscribed with strange shifting runes. Upon reading them, they will release
+D:a blast of chaotic forces.
+
+N:180:Remove Curse
+G:?:d
+I:70:14:0
+W:10:0:5:100
+A:10/1:20/2:40/2
+D:A scroll inscribed with a beneficial formula. Upon reading it, evil magics will be removed
+D:from your possessions.
+
+N:181:Light
+G:?:d
+I:70:24:0
+W:0:0:5:15
+A:0/1:3/1:10/1
+D:A scroll which will create a permanent magical light, illuminating the surroundings.
+
+N:182:Fire
+G:?:d
+I:70:48:0
+W:50:0:5:1000
+A:50/4
+F:IGNORE_FIRE
+D:A piece of paper inscribed with runes glowing brightly red. Upon reading them, a large blast
+D:of fire will be released.
+
+N:183:Ice
+G:?:d
+I:70:49:0
+W:75:0:5:5000
+A:75/6
+F:IGNORE_COLD
+D:A piece of paper inscribed with light-blue runes that radiate a strange cold. Upon reading them,
+D:a large icy blast will be released.
+
+N:184:Summon Monsters
+G:?:d
+I:70:4:0
+W:1:0:5:0
+A:1/1
+D:This scroll was made by mischievous sorcerers. If it is read, a few creatures will appear to fight
+D:you.
+
+N:185:Phase Door
+G:?:d
+I:70:8:0
+W:1:0:5:15
+A:1/1
+D:Upon reading this scroll, you will be translocated over a short distance.
+
+N:186:Teleportation
+G:?:d
+I:70:9:0
+W:10:0:5:40
+A:10/1
+D:If you read this scroll, you will immediately be transported to another place on the level.
+
+N:187:Teleport Level
+G:?:d
+I:70:10:0
+W:20:0:5:50
+A:20/1
+D:This scroll will magically transport you to the level directly above or below, when read.
+
+N:188:Monster Confusion
+G:?:d
+I:70:36:0
+W:5:0:5:30
+A:5/1
+D:Reading this scroll will cause your hands to glow with a strange mesmerising light that will
+D:attempt to confuse the next creature you hit with a hand or weapon attack.
+
+N:189:Magic Mapping
+G:?:d
+I:70:25:0
+W:5:0:5:40
+A:5/1
+D:Reading this scroll will reveal the layout of your immediate surroundings to you.
+
+N:190:Rune of Protection
+G:?:d
+I:70:38:0
+W:50:0:5:500
+A:50/2:90/4
+D:This scroll is inscribed with a powerful protective incantation. When read, this will erect a
+D:strong magical ward around the location you currently stand on. Be aware that this magic is
+D:easily disturbed by already present structures and thus cannot work where an object is lying, or
+D:on a trap.
+
+N:191:*Remove Curse*
+G:?:d
+I:70:15:0
+W:50:0:5:8000
+A:50/2:75/2:85/2:95/1
+D:This valuable scroll is inscribed with a powerful blessing capable of dispelling all but the
+D:mightiest curses which may have been laid on your possessions.
+
+N:192:Treasure Detection
+G:?:d
+I:70:26:0
+W:0:0:5:15
+A:0/1
+D:This scroll magically reveals the locations of nearby loose change to you.
+
+N:193:Object Detection
+G:?:d
+I:70:27:0
+W:0:0:5:15
+A:0/1
+D:This scroll shows nearby objects to you. It only makes you aware of items on the floor,
+D:however, not those carried by creatures.
+
+N:194:Trap Detection
+G:?:d
+I:70:28:0
+W:5:0:5:35
+A:5/1:10/1
+D:This scroll is very helpful, because it reveals the locations of nearby snares and traps which you
+D:might otherwise blunder into.
+
+##### Extra ammunition #####
+
+N:195:& Sheaf Arrow~
+G:{:o
+I:17:1:0
+W:10:0:4:3
+A:15/2:50/2
+P:0:1d5:0:0:0
+F:SHOW_MODS
+D:These arrows have bigger arrowheads and bigger feathers.
+D:They also make bigger holes.
+
+N:196:& Mithril Shot~
+G:{:B
+I:16:2:0
+W:40:0:4:20
+A:40/2:65/1
+P:0:3d4:5:5:0
+F:SHOW_MODS | IGNORE_ACID
+D:Sling bullets made from the slags of mithril smelting. They are unusually heavy, hitting
+D:with great force, and are almost imperishable.
+
+##### Additional scrolls #####
+
+N:197:Door/Stair Location
+G:?:d
+I:70:29:0
+W:5:0:5:35
+A:5/1:10/1:15/1
+D:This scroll will reveal nearby passages.
+
+N:198:Acquirement
+G:?:d
+I:70:46:0
+W:20:0:5:100000
+A:20/8
+D:A great treasure is magically stored within the shimmering runes of this scroll. Reading the
+D:words will release it.
+
+N:199:*Acquirement*
+G:?:d
+I:70:47:0
+W:60:0:5:200000
+A:60/16
+D:Several great treasures have been hidden in a magical compartment. This scroll serves as the
+D:key and will release them when read.
+
+N:200:Mass Genocide
+G:?:d
+I:70:45:0
+W:50:0:5:1000
+A:50/4:100/4
+D:An astoundingly powerful death spell is stored in the runes of this spell. Reading
+D:it will annihilate all nearby creatures. Only a few beings of legendary stature
+D:can withstand it.
+
+N:201:Detect Invisible
+G:?:d
+I:70:30:0
+W:1:0:5:15
+A:1/1
+D:A minor detection spell is stored in this scroll, showing you the locations of otherwise
+D:unseen beings for a brief moment.
+
+N:202:Aggravate Monster
+G:?:d
+I:70:1:0
+W:5:0:5:0
+A:5/1
+D:This nasty scroll will make a loud noise, waking up foes in your vicinity.
+
+N:203:Trap Creation
+G:?:d
+I:70:7:0
+W:10:0:5:0
+A:10/1
+D:If you read this rather annoying scroll, snares and pitfalls will magically be planted all around
+D:you, ready to do nasty things to you once you walk onto them.
+
+N:204:Trap/Door Destruction
+G:?:d
+I:70:39:0
+W:10:0:5:50
+A:10/1
+D:A very specifically destructive spell is written on this scroll. It will smash all traps and all
+D:doors immediately next to you.
+
+N:205:Artifact Creation
+G:?:d
+I:70:52:0
+W:70:0:5:200000
+A:70/16
+D:The mighty magic on this scroll will imbue a mundane item you possess with supernatural powers,
+D:also rendering the object indestructible.
+
+N:206:Recharging
+G:?:d
+I:70:22:0
+W:40:0:5:200
+A:40/1
+D:This scroll is inscribed with a spell releasing the enchantments used to give wands and
+D:staves their power, allowing you to replenish their spent charges.
+
+N:207:Genocide
+G:?:d
+I:70:44:0
+W:40:0:5:750
+A:40/4:80/4
+D:This rare and powerful scroll will annihilate all members of a specified race of monsters in
+D:your current location, but this will also exact a price of pain from you.
+
+N:208:Darkness
+G:?:d
+I:70:0:0
+W:1:0:5:0
+A:1/1
+D:This scroll will create a blast of utter darkness, which then fades to leave the immediate
+D:surroundings in a lasting gloom. The extreme darkness can blind those who are not used to
+D:it.
+
+N:209:Protection from Evil
+G:?:d
+I:70:37:0
+W:30:0:5:250
+A:30/1
+D:Upon reading the runes on this piece of paper, a faint aura of holiness will spring into existence
+D:around you, protecting you from blows of evil creatures, unless they are more powerful than you.
+
+N:210:Satisfy Hunger
+G:?:d
+I:70:32:0
+W:5:0:5:10
+A:5/1:20/1:50/1:75/1
+D:Reading this scroll will transport a good portion of nourishing gruel directly into your
+D:stomach. Some people say that this is a ploy of a food producer whose food nobody wants to eat.
+
+N:211:Dispel Undead
+G:?:d
+I:70:42:0
+W:40:0:5:200
+A:40/1
+D:A powerful exorcism is inscribed on this scroll. When read it will hurt undead abominations
+D:nearby, possibly even destroying them.
+
+N:212:*Enchant Weapon*
+G:?:d
+I:70:21:0
+W:50:0:5:500
+A:50/1
+D:Reading this scroll will magically infuse a weapon of your choice, making it more effective
+D:in combat.
+
+N:213:Curse Weapon
+G:?:d
+I:70:3:0
+W:50:0:5:0
+A:50/1
+D:This scroll will ruin your weapon beyond repair. Only weapons of legend have a hope of
+D:escaping destruction, but even they will often be shattered.
+
+N:214:*Enchant Armour*
+G:?:d
+I:70:20:0
+W:50:0:5:500
+A:50/1:50/1
+D:This scroll will place a great enchantment on a piece of armour of your choice, making it much
+D:better at protecting you. Mark that improving armours which are already highly enchanted is very
+D:difficult and will often fail.
+
+N:215:Curse Armour
+G:?:d
+I:70:2:0
+W:50:0:5:0
+A:50/1
+D:This scroll bears a curse that will tear your armour to shreds. Don't read it!
+
+N:216:Summon Undead
+G:?:d
+I:70:5:0
+W:15:0:5:0
+A:15/1
+D:These spells scribed by ancient necromancers will call their horrible creatures to haunt you.
+
+N:217:Blessing
+G:?:d
+I:70:33:0
+W:1:0:5:15
+A:1/1
+D:The recitation of this scroll will grant you a blessing of the Valar, making you more confident
+D:in attack and defence for a few moments.
+
+N:218:Holy Chant
+G:?:d
+I:70:34:0
+W:10:0:5:40
+A:10/1
+D:This blessing will give you a holy warrior's prowess in battle for a while.
+
+N:219:Holy Prayer
+G:?:d
+I:70:35:0
+W:25:0:5:80
+A:25/1
+D:This incantation lets you fight as a warrior of Valinor for quite a while, supreme in attack and
+D:defence.
+
+N:220:Word of Recall
+G:?:d
+I:70:11:0
+W:5:0:5:150
+A:5/1
+D:The spell on this scroll will slowly build an ethereal conduit to the town for you if you are in
+D:a dungeon, and into the dungeon if you are above ground. Upon completion, which takes a while, you
+D:will suddenly be translocated to the other place.
+
+N:221:*Destruction*
+G:?:d
+I:70:41:0
+W:40:0:5:250
+A:40/1
+D:This scroll is inscribed with a mighty conjuration which wrecks the dungeon all around you.
+D:Monsters and objects will be annihilated by this blast; only completely indestructible things can
+D:withstand it.
+
+##### Potions #####
+
+N:222:Slime Mold Juice
+G:!:d
+I:71:2:400
+W:0:0:4:2
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A strange sort of yellowish-green slime, too viscous to even slosh in the bottle. And as I know
+D:you, you're going to slurp it down.
+
+N:223:Apple Juice
+G:!:d
+I:71:1:250
+W:0:0:4:1
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A healthy fruit beverage.
+
+N:224:Water
+G:!:d
+I:71:0:200
+W:0:0:4:1
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A small bottle filled with pure water.
+
+N:225:Strength
+G:!:d
+I:71:48:0
+W:20:0:4:8000
+A:20/6:25/3:30/1
+P:0:1d1:0:0:0
+D:This beneficial potion will magically improve your physical strength.
+
+N:226:Weakness
+G:!:d
+I:71:16:0
+W:3:0:4:0
+A:3/1
+P:0:3d12:0:0:0
+F:FOUNTAIN
+D:This nasty potion will sap your strength, making you weaker.
+
+N:227:Restore Strength
+G:!:d
+I:71:42:0
+W:25:0:4:300
+A:25/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical potion will bring back your physical power to its full extent, should it be drained.
+
+N:228:Intelligence
+G:!:d
+I:71:49:0
+W:20:0:4:8000
+A:20/6:25/3:30/1
+P:0:1d1:0:0:0
+D:This nice potion will magically enhance your wit, permanently improving your intellect.
+
+N:229:Stupidity
+G:!:d
+I:71:17:0
+W:20:0:4:0
+A:20/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This accursed potion will cloud your intellect, making you stupid.
+
+N:230:Restore Intelligence
+G:!:d
+I:71:43:0
+W:25:0:4:300
+A:25/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This beneficial potion will magically heal your brain, restoring drained intelligence.
+
+N:231:Wisdom
+G:!:d
+I:71:50:0
+W:20:0:4:8000
+A:20/6:25/3:30/1
+P:0:1d1:0:0:0
+D:This potion grants great insight, making you wiser.
+
+N:232:Naivety
+G:!:d
+I:71:18:0
+W:20:0:4:0
+A:20/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This potion casts a shadow on your knowledge, making you foolish.
+
+N:233:Restore Wisdom
+G:!:d
+I:71:44:0
+W:25:0:4:300
+A:25/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This potion benefits the naive, bringing back their wisdom to what it once was.
+
+N:234:Charisma
+G:!:d
+I:71:53:0
+W:20:0:4:1000
+A:20/1:25/1
+P:0:1d1:0:0:0
+D:This potion infuses you with beauty and charm.
+
+N:235:Ugliness
+G:!:d
+I:71:21:0
+W:20:0:4:0
+A:20/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This wicked potion slightly twists your features, making you appear less fair.
+
+N:236:Restore Charisma
+G:!:d
+I:71:47:0
+W:20:0:4:300
+A:20/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This useful potion restores drained charm.
+
+N:237:Curing
+G:!:d
+I:71:61:100
+W:18:0:4:250
+A:18/1:40/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This blessed potion helps to recover from a wide variety of ailments.
+
+### Note! Invulnerability decreases your food... ###
+
+N:238:Invulnerability
+G:!:d
+I:71:62:-2500
+W:90:0:4:100000
+A:90/9
+P:0:1d1:0:0:0
+D:This immensely powerful potion makes your physique almost indestructible for a very short
+D:time. Very rarely, an attempt to hurt you will still succeed, so don't be too confident.
+
+N:239:New Life
+G:!:d
+I:71:63:100
+W:50:0:4:750000
+A:50/20:100/10:120/5
+P:0:1d1:0:0:0
+D:This potion fundamentally twists your physical features, as if you were another mother's child.
+D:Your physique will be shaped anew.
+
+N:240:Cure Serious Wounds
+G:!:d
+I:71:35:100
+W:3:0:4:40
+A:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This beneficial potion will cure some wounds and other inhibiting ailments.
+
+N:241:Cure Critical Wounds
+G:!:d
+I:71:36:100
+W:5:0:4:100
+A:5/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This nice potion will cure a good bit of hurt you have suffered and also allows you to recover
+D:from unhealthy conditions like blood poisoning, confusion or blindness.
+
+N:242:Healing
+G:!:d
+I:71:37:200
+W:15:0:4:300
+A:15/1:30/1:60/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This blessed potion greatly heals you and also cures many other ailments from
+D:which you might suffer.
+
+N:243:Constitution
+G:!:d
+I:71:52:0
+W:20:0:4:8000
+A:20/6:25/3:30/1
+P:0:1d1:0:0:0
+D:This magical concoction greatly improves your health, making you permanently tougher.
+
+N:244:Experience
+G:!:d
+I:71:59:0
+W:65:0:4:25000
+A:65/1
+P:0:1d1:0:0:0
+D:This exceptional drink instills into you knowledge, allowing you to advance further in your trade.
+
+N:245:Sleep
+G:!:d
+I:71:11:100
+W:0:0:4:0
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This potion was made to help people with bad sleeping disorders to find rest. Surely you don't
+D:want to take a nap while nasty goblins are searching for you?
+
+N:246:Blindness
+G:!:d
+I:71:7:0
+W:0:0:4:0
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This slightly poisonous potion temporarily impedes your eyesight, making you unable to see
+D:a thing.
+
+N:247:Booze
+G:!:d
+I:71:9:50
+W:0:0:4:0
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A small bottle of a dark brown distilled beverage. By its look and smell, it contains a fair
+D:amount of still muck and wood alcohol.
+
+N:248:Poison
+G:!:d
+I:71:6:0
+W:3:0:4:0
+A:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This bottle is filled with a mild but still dangerous liquid poison. Drinking it would be highly
+D:unwise.
+
+N:249:Speed
+G:!:d
+I:71:29:0
+W:1:0:4:75
+A:1/1:40/1:60/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A magical drink which temporarily allows you to act much faster when imbibed.
+
+N:250:Slowness
+G:!:d
+I:71:4:50
+W:1:0:4:0
+A:1/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A magical concoction which drains force from you, forcing you to move and act a lot slower,
+D:until it wears off.
+
+N:251:Dexterity
+G:!:d
+I:71:51:0
+W:20:0:4:8000
+A:20/6:25/3:30/1
+P:0:1d1:0:0:0
+D:A strange magical drink which greatly improves your agility and nimbleness.
+
+N:252:Restore Dexterity
+G:!:d
+I:71:45:0
+W:25:0:4:300
+A:25/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical brew brings your agility back to its former glory should it have been reduced.
+
+N:253:Restore Constitution
+G:!:d
+I:71:46:0
+W:25:0:4:300
+A:25/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A beneficial magical concoction, restoring your damaged health.
+
+N:254:Lose Memories
+G:!:d
+I:71:13:0
+W:10:0:4:0
+A:10/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A wicked potion, making you less of an adventurer than by right you should be.
+
+N:255:Salt Water
+G:!:d
+I:71:5:0
+W:0:0:4:0
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A solution of salt in water, made for curing meat. Drinking it would cause violent nausea.
+
+N:256:Enlightenment
+G:!:d
+I:71:56:0
+W:25:0:4:800
+A:25/1:50/1:100/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:An exceptional magical drink which lets you "remember" your current location as if you had
+D:already seen all of it.
+
+N:257:Heroism
+G:!:d
+I:71:32:0
+W:1:0:4:35
+A:1/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:Quaffing this drink will temporarily make you fight like a hero of great might.
+
+N:258:Berserk Strength
+G:!:d
+I:71:33:0
+W:3:0:4:100
+A:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This strange drink will instill in you a wild battle rage for a while.
+
+N:259:Boldness
+G:!:d
+I:71:28:0
+W:1:0:4:10
+A:1/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This drink will improve your courage, dispelling all fear.
+
+N:260:Restore Life Levels
+G:!:d
+I:71:41:0
+W:40:0:4:400
+A:40/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:If your life force has been drained, this blessed brew will bring it back.
+
+N:261:Resist Heat
+G:!:d
+I:71:30:0
+W:1:0:4:30
+A:1/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical potion will make fire flow in your veins, rendering you less vulnerable to outward
+D:heat.
+
+N:262:Resist Cold
+G:!:d
+I:71:31:0
+W:1:0:4:30
+A:1/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical potion will for a short while grant you a familiarity with frost, so that you take
+D:less harm from extreme cold.
+
+N:263:Detect Invisible
+G:!:d
+I:71:25:0
+W:3:0:4:50
+A:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical concoction will temporarily improve your eyesight so that you can see creatures
+D:otherwise unseen.
+
+N:264:Slow Poison
+G:!:d
+I:71:26:0
+W:1:0:4:25
+A:1/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This healthy potion will dilute poisons in your blood.
+
+N:265:Neutralise Poison
+G:!:d
+I:71:27:0
+W:5:0:4:75
+A:5/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This powerful antidote will completely negate the effect of poisons currently affecting you.
+
+N:266:Restore Mana
+G:!:d
+I:71:40:0
+W:25:0:4:350
+A:25/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This potion infuses the drinker with pure magic force, bringing their magical potential back to
+D:its full extent.
+
+N:267:Infra-vision
+G:!:d
+I:71:24:0
+W:3:0:4:20
+A:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This potion temporarily grants or improves the ability to optically perceive heat sources.
+
+N:268:Resistance
+G:!:d
+I:71:60:100
+W:20:0:4:250
+A:20/1:45/1:80/1:100/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:FOUNTAIN
+D:This great potion infuses you with the power of the elements, so that you can better
+D:withstand their ravages.
+
+##### Wands #####
+
+# Wand of school spells
+N:269:Spell
+G:-:d
+I:65:1:0
+W:3:0:10:100
+A:3/1:13/1:23/1:43/1:63/1:83/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE
+
+N:270:Manathrust
+G:-:d
+I:65:3:-1:SPELL=Manathrust
+W:3:0:10:100
+A:3/1
+P:0:1d1:0:0:0
+
+N:271:Fireflash
+G:-:d
+I:65:4:-1:SPELL=Fireflash
+W:10:0:10:100
+A:10/2
+P:0:1d1:0:0:0
+
+N:272:Firewall
+G:-:d
+I:65:5:-1:SPELL=Firewall
+W:20:0:10:100
+A:20/1
+P:0:1d1:0:0:0
+
+N:273:Tidal Wave
+G:-:d
+I:65:6:-1:SPELL=Tidal Wave
+W:20:0:10:100
+A:20/1
+P:0:1d1:0:0:0
+
+N:274:Ice Storm
+G:-:d
+I:65:7:-1:SPELL=Ice Storm
+W:15:0:10:100
+A:15/1
+P:0:1d1:0:0:0
+
+N:275:Noxious Cloud
+G:-:d
+I:65:8:-1:SPELL=Noxious Cloud
+W:5:0:10:100
+A:5/2
+P:0:1d1:0:0:0
+
+N:276:Poison Blood
+G:-:d
+I:65:9:-1:SPELL=Poison Blood
+W:30:0:10:100
+A:30/2
+P:0:1d1:0:0:0
+
+N:277:Thunderstorm
+G:-:d
+I:65:10:-1:SPELL=Thunderstorm
+W:40:0:10:100
+A:40/2
+P:0:1d1:0:0:0
+
+N:278:Dig
+G:-:d
+I:65:11:-1:SPELL=Dig
+W:15:0:10:100
+A:15/1
+P:0:1d1:0:0:0
+
+N:279:Stone Prison
+G:-:d
+I:65:12:-1:SPELL=Stone Prison
+W:50:0:10:100
+A:50/3
+P:0:1d1:0:0:0
+
+N:280:Strike
+G:-:d
+I:65:13:-1:SPELL=Strike
+W:30:0:10:100
+A:30/1
+P:0:1d1:0:0:0
+
+N:281:Teleport Away
+G:-:d
+I:65:14:-1:SPELL=Teleport Away
+W:20:0:10:100
+A:20/1
+P:0:1d1:0:0:0
+
+N:282:Summon Animal
+G:-:d
+I:65:15:-1:SPELL=Summon Animal
+W:60:0:10:100
+A:60/1
+P:0:1d1:0:0:0
+
+N:283:Magelock
+G:-:d
+I:65:16:-1:SPELL=Magelock
+W:1:0:10:100
+A:3/2
+P:0:1d1:0:0:0
+
+N:284:Slow Monster
+G:-:d
+I:65:17:-1:SPELL=Slow Monster
+W:3:0:10:100
+A:3/2
+P:0:1d1:0:0:0
+
+N:285:Essence of Speed
+G:-:d
+I:65:18:-1:SPELL=Essence of Speed
+W:25:0:10:100
+A:25/2
+P:0:1d1:0:0:0
+
+N:286:Banishment
+G:-:d
+I:65:19:-1:SPELL=Banishment
+W:45:0:10:100
+A:45/2
+P:0:1d1:0:0:0
+
+N:287:Disperse Magic
+G:-:d
+I:65:20:-1:SPELL=Disperse Magic
+W:10:0:10:100
+A:10/2
+P:0:1d1:0:0:0
+
+N:288:Charm
+G:-:d
+I:65:21:-1:SPELL=Charm
+W:15:0:10:100
+A:15/1
+P:0:1d1:0:0:0
+
+N:289:Confuse
+G:-:d
+I:65:22:-1:SPELL=Confuse
+W:7:0:10:100
+A:7/2
+P:0:1d1:0:0:0
+
+N:290:Demon Blade
+G:-:d
+I:65:23:-1:SPELL=Demon Blade
+W:60:0:10:100
+A:60/1
+P:0:1d1:0:0:0
+
+N:291:Heal Monster
+G:-:d
+I:65:24:-1:SPELL=Heal Monster
+W:1:0:10:0
+A:1/4
+P:0:1d1:0:0:0
+
+N:292:Haste Monster
+G:-:d
+I:65:25:-1:SPELL=Haste Monster
+W:1:0:10:0
+A:1/4
+P:0:1d1:0:0:0
+
+
+##### Extra ammunition #####
+
+N:293:& Flight Arrow~
+G:{:y
+I:17:1:0
+W:3:0:1:1
+A:3/2
+P:0:1d3:0:0:0
+F:SHOW_MODS
+D:An arrow designed for longer flight. Consequently, it is lighter and hits with less force.
+
+# XXX
+
+N:295:& Boulder~
+G:*:W
+I:11:1:0
+W:3:0:50:1
+A:3/200
+P:0:5d5:0:0:0
+F:SPECIAL_GENE
+D:A big nasty-looking piece of rock.
+
+# Used for a quest
+N:296:& Flame~ Imperishable
+G:~:v
+I:11:255:0
+W:127:0:4:0
+A:127/255
+P:0:1d1:0:0:0
+T:39:2
+F:NORM_ART | FULL_NAME | SPECIAL_GENE
+F:ACTIVATE | ACTIVATE_NO_WIELD
+a:SPELL=Artifact Eternal Flame
+D:An impossibly bright, flickering living flame. It can be used
+D:once to imbue an object with the power of Eru Iluvatar himself.
+
+N:297:& Necromantic Teeth~
+G:|:D
+I:23:34:0
+W:0:0:7:10
+A:0/1:5/1:10/1:20/1
+P:0:1d4:0:0:0
+F:SHOW_MODS | VAMPIRIC | SPECIAL_GENE
+D:This looks like some animal's teeth or at least you think
+D:it comes from an animal...
+
+# The Horn of the Thunderlords
+N:298:& Golden Horn~ of the Thunderlords
+G:_:d
+I:55:23:-1:SPELL=Artifact Thunderlords
+W:50:10:10:12000
+P:0:1d4:0:0:0
+A:50/200
+T:55:8
+F:NO_RECHARGE | EASY_USE | RECHARGED | NORM_ART | FULL_NAME | SPECIAL_GENE
+D:This horn was given to you as a reward. Blow it if you are in dire need
+D:of leaving your current location fast.
+
+# XXX
+
+##### Staves #####
+
+N:300:Spell
+G:_:d
+I:55:1:0
+W:5:0:50:100
+A:5/1:15/1:35/1:45/1:65/1:75/1:85/1:95/1
+P:0:1d2:0:0:0
+F:SPECIAL_GENE
+
+N:301:Nothing
+G:_:d
+I:55:2:-1:SPELL=Nothing
+W:5:0:50:100
+A:5/1
+P:0:1d2:0:0:0
+
+N:302:Globe of Light
+G:_:d
+I:55:3:-1:SPELL=Globe of Light
+W:7:0:50:100
+A:7/1
+P:0:1d2:0:0:0
+
+N:303:Fiery Shield
+G:_:d
+I:55:4:-1:SPELL=Fiery Shield
+W:15:0:50:100
+A:15/2
+P:0:1d2:0:0:0
+
+N:304:Remove Curses
+G:_:d
+I:55:5:-1:SPELL=Remove Curses
+W:10:0:50:100
+A:10/1
+P:0:1d2:0:0:0
+
+N:305:Wings of Winds
+G:_:d
+I:55:6:-1:SPELL=Wings of Winds
+W:25:0:50:100
+A:25/2
+P:0:1d2:0:0:0
+
+N:306:Shake
+G:_:d
+I:55:7:-1:SPELL=Shake
+W:30:0:50:100
+A:30/1
+P:0:1d2:0:0:0
+
+N:307:Disarm
+G:_:d
+I:55:8:-1:SPELL=Disarm
+W:2:0:50:100
+A:2/1
+P:0:1d2:0:0:0
+
+N:308:Teleportation
+G:_:d
+I:55:9:-1:SPELL=Teleportation
+W:20:0:50:100
+A:20/1
+P:0:1d2:0:0:0
+
+N:309:Probability Travel
+G:_:d
+I:55:10:-1:SPELL=Probability Travel
+W:50:0:50:100
+A:50/3
+P:0:1d2:0:0:0
+
+N:310:Recovery
+G:_:d
+I:55:11:-1:SPELL=Recovery
+W:20:0:50:100
+A:20/1
+P:0:1d2:0:0:0
+
+N:311:Healing
+G:_:d
+I:55:12:-1:SPELL=Healing
+W:25:0:50:100
+A:25/2
+P:0:1d2:0:0:0
+
+N:312:Vision
+G:_:d
+I:55:13:-1:SPELL=Vision
+W:30:0:50:100
+A:30/1
+P:0:1d2:0:0:0
+
+N:313:Identify
+G:_:d
+I:55:14:-1:SPELL=Identify
+W:10:0:50:100
+A:10/1
+P:0:1d2:0:0:0
+
+N:314:Sense Hidden
+G:_:d
+I:55:15:-1:SPELL=Sense Hidden
+W:10:0:50:100
+A:10/1
+P:0:1d2:0:0:0
+
+N:315:Reveal Ways
+G:_:d
+I:55:16:-1:SPELL=Reveal Ways
+W:5:0:50:100
+A:5/1
+P:0:1d2:0:0:0
+
+N:316:Sense Monsters
+G:_:d
+I:55:17:-1:SPELL=Sense Monsters
+W:5:0:50:100
+A:5/1
+P:0:1d2:0:0:0
+
+N:317:Genocide
+G:_:d
+I:55:18:-1:SPELL=Genocide
+W:55:0:50:100
+A:55/2
+P:0:1d2:0:0:0
+
+N:318:Summon
+G:_:d
+I:55:19:-1:SPELL=Summon
+W:5:0:50:100
+A:5/1
+P:0:1d2:0:0:0
+
+N:319:Sterilization
+G:_:d
+I:55:24:-1:SPELL=Sterilize
+W:20:0:50:100
+A:20/3
+P:0:1d2:0:0:0
+
+N:320:Wish
+G:_:d
+I:55:20:-1:SPELL=Wish
+W:95:0:50:10000
+A:95/40
+P:0:1d2:0:0:0
+F:NO_RECHARGE
+
+N:321:Mana
+G:_:d
+I:55:21:-1:SPELL=Mana
+W:60:0:50:100
+A:60/2
+P:0:1d2:0:0:0
+
+# XXX
+# ...
+# XXX
+
+##### School Books #####
+
+N:330:& Tome~ of Magical Energy
+G:?:B
+I:111:0:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:The bright blue cover of this tome seems to glow
+D:with an inner violet light. You feel more attuned
+D:to raw magic as you hold it.
+
+N:331:& Tome~ of the Eternal Flame
+G:?:R
+I:111:1:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW | IGNORE_FIRE
+D:The cover of this tome is bright red, with flickering
+D:flames dancing across it once in a while. As you hold
+D:it, you begin to gain a much closer knowledge of all
+D:that is fiery.
+
+N:332:& Tome~ of the Blowing Wind
+G:?:b
+I:111:2:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW | IGNORE_ELEC
+D:The pages of this tome have a tendency to turn themselves,
+D:as though flipped by an errant wind. As you hold it,
+D:you start feeling wind at your fingertips.
+
+N:333:& Tome~ of the Impenetrable Earth
+G:?:U
+I:111:3:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW | IGNORE_ACID
+D:The solid leather cover of this tome seems permanently
+D:stained with caked mud and grass. Heavy it is to lift,
+D:yet strangely comforting to hold - you feel stronger
+D:and better supported.
+
+N:334:& Tome~ of the Everrunning Wave
+G:?:B
+I:111:4:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:The cover and pages of this tome seem to be perpetually
+D:wet, though they are not wet to the touch. As you hold
+D:it, you begin to understand ocean storms better.
+
+N:335:& Tome~ of Translocation
+G:?:B
+I:111:5:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This book seems to flicker strangely. It's one of those books
+D:with an annoying tendency to disappear when you need it and
+D:reappear in the unlikeliest places. As you hold it, you start
+D:to understand what conveyance really means.
+
+N:336:& Tome~ of the Tree
+G:?:G
+I:111:6:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:The cover of this tome is a bright shade of green, and it gives off
+D:a healthy, zesty scent that makes your thoughts clearer. As you
+D:hold it, your heart goes out to all living things upon Arda.
+
+N:337:& Tome~ of Knowledge
+G:?:D
+I:111:7:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:A thick book with solid leather binding. It looks entirely
+D:unremarkable, but as you hold it, you feel strangely able
+D:to learn the inner workings of things and creatures.
+
+##### Chests #####
+
+N:338:& Small wooden chest~
+G:~:s
+I:7:1:0
+W:5:0:250:20
+A:5/1
+P:0:2d3:0:0:0
+D:A small wooden box, locked and possibly trapped. You wonder what might be inside.
+
+N:339:& Large wooden chest~
+G:~:s
+I:7:5:0
+W:15:0:500:60
+A:15/1
+P:0:2d5:0:0:0
+D:A large wooden box. It doesn't seem to be locked - why not risk a look inside?
+
+N:340:& Small iron chest~
+G:~:s
+I:7:2:0
+W:25:0:300:100
+A:25/1
+P:0:2d4:0:0:0
+D:A small rectangular container made of wood and reinforced with iron corners and latches.
+
+N:341:& Large iron chest~
+G:~:s
+I:7:6:0
+W:35:0:1000:150
+A:35/1
+P:0:2d6:0:0:0
+D:A big container made of wood, with a heavy iron lock.
+
+N:342:& Small steel chest~
+G:~:s
+I:7:3:0
+W:45:0:500:200
+A:45/1
+P:0:2d4:0:0:0
+D:A small wooden box with strong steel locks and reinforcements.
+
+N:343:& Large steel chest~
+G:~:s
+I:7:7:0
+W:55:0:1000:250
+A:55/1
+P:0:2d6:0:0:0
+D:A nearly indestructible chest of wood and steel. The lock doesn't look impenetrable, but it
+D:might be trapped.
+
+N:344:& Ruined chest~
+G:~:s
+I:7:0:0
+W:0:0:250:0
+A:75/1
+D:A broken, empty chest.
+
+##### Various Stuff #####
+
+N:345:& Iron Spike~
+G:~:W
+I:5:0:0
+W:1:0:10:1
+A:1/1
+P:0:1d1:0:0:0
+D:A small spur of iron. Ramming one between a door and its frame might jam it.
+
+N:346:& Wooden Torch~
+G:~:u
+I:39:0:0:4000
+W:1:0:30:2
+A:1/1
+P:0:1d1:0:0:0
+F:EASY_KNOW | LITE1 | FUEL_LITE
+f:LITE1 | FUEL_LITE
+D:A piece of wood with an oily rag wrapped around it. When lit, it will give off a little light and
+D:much smoke.
+
+N:347:& Brass Lantern~
+G:~:U
+I:39:1:0:7500
+W:3:0:50:35
+A:3/1
+P:0:1d1:0:0:0
+F:EASY_KNOW | IGNORE_FIRE | LITE2 | FUEL_LITE
+f:LITE2 | FUEL_LITE
+D:A brass container with a wick emerging from it, protected from draughts by a sheet of greased
+D:paper. It can be carried by a handle.
+
+N:348:& Flask~ of oil
+G:!:y
+I:77:0:7500
+W:1:0:10:3
+A:1/1
+P:0:2d6:0:0:0
+D:A small clay container, filled with thick oil. The oil is flammable and can be used as lantern
+D:fuel.
+
+N:349:& Empty Bottle~
+G:!:w
+I:2:1:0
+W:0:0:2:1
+A:0/1
+P:0:1d1:0:0:0
+D:A small glass bottle. It's empty.
+
+
+##### Here are the Rod Tips #####
+
+N:350:Havoc
+G:-:d
+I:66:28:90
+W:95:0:15:150000
+A:100/16
+P:0:1d1:0:0:0
+D:This powerful rod will release great blasts of destructive energy, but there's no knowing what
+D:this effect will concentrate on.
+
+N:351:Door/Stair Location
+G:-:d
+I:66:1:10
+W:15:0:15:1000
+A:15/1
+P:0:1d1:0:0:0
+D:When fuelled with enough ambient mana, this rod can detect nearby passages.
+
+N:352:Trap Location
+G:-:d
+I:66:29:8
+W:5:0:15:100
+A:5/1:10/1:20/1
+P:0:1d1:0:0:0
+D:Zapping this rod will release a minor detection magic, alerting you of nearby pits and snares.
+
+N:353:Probing
+G:-:d
+I:66:7:50
+W:40:0:15:4000
+A:40/4
+P:0:1d1:0:0:0
+D:A rod of knowledge which will tell you about nearby creatures' health.
+
+N:354:Recall
+G:-:d
+I:66:3:80
+W:30:0:15:4500
+A:30/4
+P:0:1d1:0:0:0
+D:A rod which can transport you from the depths to your home and back.
+
+N:355:Illumination
+G:-:d
+I:66:4:8
+W:20:0:15:1000
+A:20/1
+P:0:1d1:0:0:0
+D:This rod carries a minor spell of brightness, lighting your immediate surroundings whenever
+D:activated.
+
+N:356:Light
+G:-:d
+I:66:15:15
+W:10:0:15:500
+A:10/1
+P:0:1d1:0:0:0
+D:This rod can shoot a lance of bright light, hurting creatures which lurk in the dark.
+
+N:357:Lightning Bolts
+G:-:d
+I:66:21:30
+W:20:0:15:2000
+A:20/1
+P:0:1d1:0:0:0
+D:This rod shoots a small spark of lightning, zapping the creature it hits.
+
+N:358:Frost Bolts
+G:-:d
+I:66:23:35
+W:25:0:15:2500
+A:25/1
+P:0:1d1:0:0:0
+D:A small but extremely cold shard of ice will fly from this rod to your enemy.
+
+N:359:Fire Bolts
+G:-:d
+I:66:22:40
+W:30:0:15:3000
+A:30/1
+P:0:1d1:0:0:0
+D:This rod fires a magical flaming arrow at your foe, burning it.
+
+N:360:Polymorph
+G:-:d
+I:66:19:25
+W:35:0:15:1200
+A:35/1
+P:0:1d1:0:0:0
+D:This rod of change will cause its target creature to mutate into something else.
+
+N:361:Slow Monster
+G:-:d
+I:66:17:25
+W:30:0:15:1500
+A:30/1
+P:0:1d1:0:0:0
+D:This obstructive rod emits an energy bolt which will slow the creature it hits.
+
+N:362:Sleep Monster
+G:-:d
+I:66:16:25
+W:30:0:15:1500
+A:30/1
+P:0:1d1:0:0:0
+D:This sorcerous rod will cause its target to stop in its tracks.
+
+N:363:Drain Life
+G:-:d
+I:66:18:30
+W:75:0:15:3600
+A:75/4
+P:0:1d1:0:0:0
+D:This necromantic magical rod will hurt a living creature struck by its spell.
+
+N:364:Teleport Other
+G:-:d
+I:66:13:60
+W:45:0:15:1400
+A:45/2
+P:0:1d1:0:0:0
+D:This rod of movement will displace its target to another location.
+
+N:365:Disarming
+G:-:d
+I:66:14:50
+W:35:0:15:2100
+A:35/1
+P:0:1d1:0:0:0
+D:This rod will clear a path for you, triggering and thus rendering harmless all traps on the way.
+
+N:366:Lightning Balls
+G:-:d
+I:66:25:50
+W:55:0:15:4000
+A:55/1
+P:0:1d1:0:0:0
+D:This rod will hurl a large ball of lightning at its target, electrifying all it engulfs.
+
+N:367:Cold Balls
+G:-:d
+I:66:27:55
+W:60:0:15:4500
+A:60/1
+P:0:1d1:0:0:0
+D:This rod will call forth a minor storm of ice which freezes everything in the area of its blast.
+
+N:368:Fire Balls
+G:-:d
+I:66:26:60
+W:75:0:15:5000
+A:75/1
+P:0:1d1:0:0:0
+D:This rod will cause a small storm of flame to rage in a small circular area of your choice.
+
+N:369:Acid Balls
+G:-:d
+I:66:24:60
+W:70:0:15:5500
+A:70/1
+P:0:1d1:0:0:0
+D:This destructive rod will drown its target and its immediate surroundings in caustic acid.
+
+N:370:Acid Bolts
+G:-:d
+I:66:20:40
+W:40:0:15:3500
+A:40/1
+P:0:1d1:0:0:0
+D:This rod will shoot a small glob of powerful acid at its target.
+
+N:371:Enlightenment
+G:-:d
+I:66:5:40
+W:65:0:15:10000
+A:65/4
+P:0:1d1:0:0:0
+D:This rod grants you knowledge of your surroundings.
+
+N:372:Perception
+G:-:d
+I:66:2:20
+W:50:0:15:13000
+A:50/8:100/8
+P:0:1d1:0:0:0
+D:This rod makes you insightful, laying open the identity of your possessions.
+
+N:373:Curing
+G:-:d
+I:66:8:35
+W:65:0:15:15000
+A:65/8
+P:0:1d1:0:0:0
+D:This is a rod with minor healing powers, alleviating many disabling conditions.
+
+N:374:Healing
+G:-:d
+I:66:9:120
+W:80:0:15:20000
+A:80/8
+P:0:1d1:0:0:0
+D:This rod has major healing powers and can restore your health if you have been wounded.
+
+N:375:Detection
+G:-:d
+I:66:6:80
+W:30:0:15:5000
+A:30/8
+P:0:1d1:0:0:0
+D:This rod grants knowledge about all things worthy of notice in your vicinity.
+
+N:376:Restoration
+G:-:d
+I:66:10:140
+W:80:0:15:25000
+A:80/16
+P:0:1d1:0:0:0
+D:This rod lets you remember your previous abilities and gain them back, should they have been
+D:reduced.
+
+N:377:Speed
+G:-:d
+I:66:11:100
+W:95:0:15:50000
+A:95/16
+P:0:1d1:0:0:0
+D:This energising rod will allow you to act a lot faster for some time.
+
+# Ring of Spell
+
+N:378:Spell
+G:=:d
+I:45:58:0
+W:10:0:2:1000
+A:10/1
+F:SPELL_CONTAIN | WIELD_CAST
+f:SPELL_CONTAIN
+D:This ring is a container for spells. Those that are skilled in copying spells can inscribe a
+D:spell into it.
+
+# Amulet of Spell
+
+N:379:Spell
+G:":d
+I:40:27:0
+W:10:0:2:1000
+A:10/1
+F:SPELL_CONTAIN | WIELD_CAST
+f:SPELL_CONTAIN
+D:This amulet is a container for spells. Those that are skilled in copying spells can inscribe a
+D:spell into it.
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+##### Skeletons #####
+
+N:391:& Broken Skull~
+G:~:w
+I:1:1:0
+W:0:0:1:0
+A:0/1
+P:0:1d1:0:0:0
+D:Good old Yorick. So that's where he ended up.
+
+N:392:& Broken Bone~
+G:~:w
+I:1:2:0
+W:0:0:2:0
+A:0/1
+P:0:1d1:0:0:0
+D:A yellowish bone, broken off. Obviously, its owner no longer has a use for it.
+
+N:393:& Canine Skeleton~
+G:~:w
+I:1:4:0
+W:1:0:10:0
+A:1/1
+P:0:1d1:0:0:0
+D:Lassie?
+
+N:394:& Rodent Skeleton~
+G:~:w
+I:1:3:0
+W:1:0:10:0
+A:1/1
+P:0:1d1:0:0:0
+D:The remains of a cat's meal.
+
+N:395:& Human Skeleton~
+G:~:w
+I:1:8:0
+W:5:0:60:0
+A:5/1
+P:0:1d2:0:0:0
+D:It's dead, Jim.
+
+N:396:& Dwarf Skeleton~
+G:~:w
+I:1:7:0
+W:5:0:50:0
+A:5/1
+P:0:1d2:0:0:0
+D:The remains of a dwarf who met his or her demise here.
+
+N:397:& Elf Skeleton~
+G:~:w
+I:1:6:0
+W:5:0:40:0
+A:5/1
+P:0:1d2:0:0:0
+D:A person of about human shape, but considerably more slightly built, has died here long ago.
+
+N:398:& Gnome Skeleton~
+G:~:w
+I:1:5:0
+W:5:0:30:0
+A:5/1
+P:0:1d2:0:0:0
+D:This was once a gnome. Looks very dead now.
+
+##### Additional weapon #####
+
+N:399:& Great Hammer~
+G:\:D
+I:21:19:0
+W:45:0:300:350
+A:45/3
+P:0:4d6:0:0:0
+F:SHOW_MODS
+D:A massive smith's hammer, so large and heavy it can be used as a weapon.
+
+##### Dragon Scale Mail #####
+
+N:400:& Black Dragon Scale Mail~
+G:[:s
+I:38:1:0
+W:60:0:200:50000
+A:60/8
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_ACID
+F:RES_ACID | FLY |
+f:RES_ACID |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:An armour made of a black dragon's hide, containing some of this beast's powers.
+
+N:401:& Blue Dragon Scale Mail~
+G:[:b
+I:38:2:0
+W:50:0:200:40000
+A:50/8
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_ELEC
+F:RES_ELEC | FLY |
+f:RES_ELEC |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A piece of dragon hide fashioned into an armour, shimmering bright blue.
+
+N:402:& White Dragon Scale Mail~
+G:[:w
+I:38:3:0
+W:50:0:200:40000
+A:50/8
+a:HARDCORE=BR_COLD
+P:30:2d4:-2:0:10
+F:RES_COLD | FLY |
+f:RES_COLD |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:An armour fashioned from dragon hide, glistening the white of snow.
+
+N:403:& Red Dragon Scale Mail~
+G:[:r
+I:38:4:0
+W:60:0:200:50000
+A:60/8
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_FIRE
+F:RES_FIRE | FLY |
+f:RES_FIRE |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:The skin of a dragon made into a suit of armour. It glows a bright red and radiates heat.
+
+N:404:& Green Dragon Scale Mail~
+G:[:g
+I:38:5:0
+W:50:0:200:40000
+A:50/8
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_POIS
+F:RES_POIS | FLY |
+f:RES_POIS |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour fashioned of dragon hide. It is dirty green and smells awful.
+
+N:405:& Multi-Hued Dragon Scale Mail~
+G:[:v
+I:38:6:0
+W:90:0:200:150000
+A:90/32
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_MANY
+F:ATTR_MULTI
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS | FLY |
+f:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | RES_POIS |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A powerful armour made of dragonhide. It glows red, blue, green, black and white.
+
+N:406:& Pseudo Dragon Scale Mail~
+G:[:v
+I:38:10:0
+W:70:0:200:70000
+A:70/16
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_LIGHT
+F:RES_LITE | RES_DARK | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour made of dragon hide, glowing with a strange light, or is it darkness?
+
+N:407:& Law Dragon Scale Mail~
+G:[:B
+I:38:12:0
+W:80:0:200:80000
+A:80/16
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_SHARD
+F:RES_SOUND | RES_SHARDS | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A piece of dragonhide cut and shaped so it can be worn as armour. The scales are very sharp,
+D:and the roaring of a storm seems to come from it, but you're not afraid of either.
+
+N:408:& Bronze Dragon Scale Mail~
+G:[:U
+I:38:14:0
+W:50:0:200:40000
+A:50/8
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_CONF
+F:RES_CONF | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour made from dragon skin. Its brownish scales glitter in a dazzling light.
+
+N:409:& Gold Dragon Scale Mail~
+G:[:y
+I:38:16:0
+W:60:0:200:50000
+A:60/8
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_SOUND
+F:RES_SOUND | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of golden-hued armour made of dragonhide. The rustle of its scales occasionally
+D:increases to a loud boom.
+
+N:410:& Chaos Dragon Scale Mail~
+G:[:v
+I:38:18:0
+W:80:0:200:80000
+A:80/16
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_CHAOS
+F:ATTR_MULTI
+F:RES_CHAOS | RES_DISEN | FLY |
+f:RES_CHAOS |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour made of dragon hide. It glows in colours you have never seen before. As you
+D:put it on, you feel like you could change the world and are no longer afraid of your equipment
+D:losing its magic.
+
+N:411:& Balance Dragon Scale Mail~
+G:[:v
+I:38:20:0
+W:95:0:200:100000
+A:95/32
+P:30:2d4:-2:0:10
+a:HARDCORE=BR_BALANCE
+F:RES_CHAOS | RES_DISEN | RES_SOUND | RES_SHARDS | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour made of the hide of a dead dragon. When wearing it, you feel like you
+D:understand the principles of law and chaos, and no longer fear either.
+
+N:412:& Power Dragon Scale Mail~
+G:[:v
+I:38:30:0
+W:100:0:250:350000
+A:100/64
+P:40:2d4:-3:0:15
+a:HARDCORE=BR_POWER
+F:ATTR_MULTI
+F:RES_ACID | RES_FIRE | RES_COLD | RES_ELEC | RES_POIS | FLY |
+F:RES_NETHER | RES_NEXUS | RES_CHAOS | RES_LITE | RES_DARK |
+F:RES_SHARDS | RES_SOUND | RES_DISEN | RES_CONF |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour made of a very thick richly coloured dragonhide. You think you'll never have
+D:to fear dragons if you put it on.
+
+#### PDSM has been restored to (almost) its former glory in Zangband
+
+N:413:& Dragon Helm~
+G:]:G
+I:32:7:0
+W:45:0:50:10000
+A:80/4
+P:8:1d3:0:0:10
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_ELEC | IGNORE_COLD
+D:An iron helmet, covered with a layer of dragonhide. It offers great protection and may grant
+D:protection against some dragon's attacks, based on the dragon the hide was taken from.
+
+N:414:& Dragon Shield~
+G:[:G
+I:34:6:0
+W:70:0:100:10000
+A:80/4
+P:8:1d3:0:0:10
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_ELEC | IGNORE_COLD
+D:A large shield, with a dragonskin cover. Depending on which dragon the hide came from, it
+D:might grant protection against some sorts of dragon breath.
+
+##### Extra potions #####
+
+N:415:Death
+G:!:d
+I:71:23:0
+W:55:0:4:0
+A:55/4
+P:0:20d20:0:0:0
+F:FOUNTAIN
+D:A potent and very quickly working poison, sometimes utilised by those who wish to end their
+D:lives. It is immensely toxic and will seriously hurt you even if you just get some of it on your
+D:hands.
+
+N:416:Ruination
+G:!:d
+I:71:15:0
+W:40:0:4:0
+A:40/8
+P:0:20d20:0:0:0
+F:FOUNTAIN
+D:This wicked potion will seriously damage your abilities beyond the powers of magical
+D:restoration.
+
+N:417:Detonations
+G:!:d
+I:71:22:0
+W:60:0:4:10000
+A:60/8
+P:0:25d25:0:0:0
+F:FOUNTAIN
+D:This bottle is filled with a strange substance which will violently explode if strongly agitated.
+D:You don't want to know what it might do to your intestines.
+
+N:418:Augmentation
+G:!:d
+I:71:55:0
+W:40:0:4:60000
+A:40/16
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This blessed potions will greatly improve all your abilities, if they can still be improved.
+
+N:419:*Healing*
+G:!:d
+I:71:38:0
+W:40:0:4:1500
+A:40/4:60/2:80/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This highly desirable potion will greatly help recovering from wounds, typically healing you
+D:fully.
+
+N:420:Life
+G:!:d
+I:71:39:0
+W:60:0:4:5000
+A:60/4:100/2
+P:0:1d1:0:0:0
+D:This wonderful potion will fully heal you no matter how badly you're hurt, allow drained
+D:abilities to recover and remove various other ailments.
+
+N:421:Self Knowledge
+G:!:d
+I:71:58:0
+W:40:0:4:2000
+A:40/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A drink of insight, letting you know yourself better.
+
+N:422:*Enlightenment*
+G:!:d
+I:71:57:0
+W:70:0:4:80000
+A:70/4
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical concoction will grant you great insight, magically improving your wits and
+D:wisdom, letting you know precisely who and where you are and what you possess.
+
+# XXX
+# XXX
+
+N:425:Fear Resistance
+G:=:d
+I:45:38:0
+W:10:0:2:300
+A:10/2
+F:RES_FEAR | EASY_KNOW
+f:RES_FEAR |
+D:This ring grants courage, so that you can never become afraid.
+
+N:426:Light and Darkness Resistance
+G:=:d
+I:45:39:0
+W:30:0:2:3000
+A:30/2
+F:RES_LITE | RES_DARK | EASY_KNOW
+f:RES_LITE | RES_DARK |
+D:This ring protects against fluctuations of the light.
+
+N:427:Nether Resistance
+G:=:d
+I:45:40:0
+W:34:0:2:14500
+A:34/2
+F:RES_NETHER | HOLD_LIFE | EASY_KNOW
+f:RES_NETHER |
+D:This blessed ring improves your life force, protecting you from the draining forces of nether
+D:and other attempts to suck your lifeblood.
+
+N:428:Nexus Resistance
+G:=:d
+I:45:41:0
+W:24:0:2:3000
+A:24/2
+F:RES_NEXUS | EASY_KNOW
+f:RES_NEXUS |
+D:This ring of stability protects you from the warping forces of nexus.
+
+N:429:Sound Resistance
+G:=:d
+I:45:42:0
+W:26:0:2:3000
+A:26/2
+F:RES_SOUND | EASY_KNOW
+f:RES_SOUND |
+D:This ring projects an aura of quiet around you, protecting you from loud noise.
+
+N:430:Confusion Resistance
+G:=:d
+I:45:43:0
+W:22:0:2:3000
+A:22/2
+F:RES_CONF | EASY_KNOW
+f:RES_CONF |
+D:This ring stabilises your mind, protecting you from all kinds of befuddlement.
+
+N:431:Shard Resistance
+G:=:d
+I:45:44:0
+W:25:0:2:3000
+A:25/2
+F:RES_SHARDS | EASY_KNOW
+f:RES_SHARDS |
+D:This piece of jewellery magically toughens your skin, protecting you from flying shrapnel.
+
+N:432:Disenchantment Resistance
+G:=:d
+I:45:45:0
+W:90:0:2:15000
+A:90/10
+F:RES_DISEN | EASY_KNOW
+f:RES_DISEN |
+D:This rare ring of preservation protects your equipment from attempts to sap its magic, also
+D:causing you to suffer less pain from such attacks.
+
+N:433:Chaos Resistance
+G:=:d
+I:45:46:0
+W:50:0:2:13000
+A:50/2
+F:RES_CHAOS | RES_CONF | EASY_KNOW
+f:RES_CHAOS |
+D:This ring protects you from the horribly warping forces of chaos.
+
+N:434:Blindness Resistance
+G:=:d
+I:45:47:0
+W:60:0:2:7500
+A:60/2
+F:RES_BLIND | EASY_KNOW
+f:RES_BLIND |
+D:This ring magically preserves your eyesight, making you impervious to any attempt to blind
+D:you.
+
+N:435:Lordly Protection
+G:=:d
+I:45:48:0
+W:100:0:2:100000
+A:100/5
+F:RES_DISEN | RES_POIS | HOLD_LIFE | FREE_ACT
+f:RES_DISEN | RES_POIS | HOLD_LIFE | FREE_ACT
+D:This blessed ring will protect you from disenchantment, poison, attempts to drain your life
+D:force and holding magic.
+
+N:436:Extra Attacks
+G:=:d
+I:45:49:0
+W:50:0:2:100000
+A:50/2
+F:BLOWS
+f:BLOWS
+D:This powerful ring of fighters greatly enhances your fighting speed, allowing you to attack
+D:more often in a round of combat.
+
+N:437:Cure Light Wounds
+G:!:d
+I:71:34:50
+W:0:0:4:15
+A:0/1:1/1:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This healthy drink heals a little damage you have taken.
+
+N:438:Clumsiness
+G:!:d
+I:71:19:0
+W:5:0:4:0
+A:5/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This nasty concoction will numb your nerves, making you clumsier.
+
+N:439:Sickliness
+G:!:d
+I:71:20:0
+W:10:0:4:0
+A:10/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This unhealthy drink damages your health, reducing your physical constitution.
+
+###### Here start the maps #####
+
+# Map of Bree
+N:440:Map of Bree
+G:?:s
+I:8:200:0
+W:3:100:5:100
+A:3/3
+D:A map, showing the town of Bree and the landscape around it.
+
+# Map of Gondolin
+N:441:Map of Gondolin
+G:?:s
+I:8:201:0
+W:70:100:5:50000
+A:70/3
+D:A map detailing the place of the hidden city of Gondolin. Consider yourself lucky someone
+D:lost a map here, since the location is yet unknown even to Morgoth.
+
+# Map of Lothlorien
+N:442:Map of Lothlorien
+G:?:s
+I:8:202:0
+W:6:100:5:1000
+A:6/3
+D:A map vaguely describing the forest of Lothlorien and the elven settlements therein.
+
+# Map of Minas Anor
+N:443:Map of Minas Anor
+G:?:s
+I:8:203:0
+W:36:100:5:10000
+A:36/3
+D:A map of the great city of Gondor.
+
+# XXX Numbers 444-464 unused #
+
+N:465:& Silver Arrow~
+G:{:W
+I:17:3:0
+W:55:0:2:35
+A:50/4:90/2
+P:0:3d4:0:0:0
+F:SHOW_MODS | SLAY_EVIL | IGNORE_ACID | IGNORE_FIRE
+D:An arrow to be shot with a bow, its iron head coated with hallowed silver,
+D:a material that sears the flesh of all evil creatures.
+
+N:466:& Silver Bolt~
+G:{:w
+I:18:3:0
+W:50:0:2:40
+A:60/4:95/2
+P:0:3d5:0:0:0
+F:SHOW_MODS | SLAY_EVIL | IGNORE_ACID | IGNORE_FIRE
+D:This crossbow bolt has a silver tip, blessed by the Valar for fighting evil.
+
+N:467:Lightning Resistance
+G:":d
+I:40:29:0
+W:10:0:3:250
+A:10/1
+F:RES_ELEC | IGNORE_ELEC | EASY_KNOW
+f:RES_ELEC |
+D:This amulet will protect you from electrical discharges and storms.
+
+N:468:Wisdom
+G:":d
+I:40:28:0
+W:30:0:3:500
+A:30/1
+F:WIS | SUST_WIS | HIDE_TYPE
+f:WIS |
+D:This magical amulet will magically make you wiser, and fend off
+D:attacks that would reduce your wisdom. Beware: if cursed, the
+D:amulet will do the opposite.
+
+N:469:Regeneration
+G:":d
+I:40:30:0
+W:30:0:3:600
+A:30/3
+F:REGEN | EASY_KNOW
+f:REGEN |
+D:Wearing this amulet will trigger your body's regenerational
+D:processes quicker and make them proceed faster.
+
+N:470:Infravision
+G:":d
+I:40:26:0
+W:10:0:3:200
+A:10/1
+F:INFRA | HIDE_TYPE
+f:INFRA |
+D:This amulet will increase your ability to sense warm-blooded
+D:creatures in your vicinity. Beware: if cursed, it will do
+D:just the opposite.
+
+N:471:Devotion
+G:":d
+I:40:25:0
+W:70:0:3:30000
+A:70/8
+F:WIS | CHR | SUST_WIS | SUST_CHR | LITE1 | HIDE_TYPE |
+F:RES_DARK | RES_LITE | RES_FIRE | HOLD_LIFE |
+D:This blessed amulet will protect your wisdom and charms from
+D:diminishing, often adding to them as well. It also grants
+D:some extra protective magics by the grace of the Valar.
+
+N:472:Weaponmastery
+G:":d
+I:40:24:0
+W:70:0:3:30000
+A:70/8
+F:STR | CON | SUST_STR | SUST_CON | FREE_ACT | HIDE_TYPE |
+F:RES_FEAR | RES_DISEN |
+D:The ultimate amulet for a warrior, it will grant protection
+D:in the face of some evil magics, protect your strength and health,
+D:also increasing them. Beware: if cursed, the amulet will
+D:sap your strength and health instead.
+
+N:473:Trickery
+G:":d
+I:40:23:0
+W:70:0:3:30000
+A:70/8
+F:DEX | SUST_DEX | STEALTH | SPEED | INFRA | HIDE_TYPE |
+F:RES_NEXUS | RES_POIS
+D:The ultimate amulet for a rogue or assassin, it protects the
+D:wearer against some evil magics, granting improvements in
+D:the abilities vital to these adventurers. Beware: if cursed,
+D:the amulet will do just the opposite.
+
+N:474:Telepathy
+G:":d
+I:40:22:0
+W:50:0:3:25000
+A:50/6
+F:ESP_ALL |
+f:ESP_ALL |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This rare and powerful amulet lays bare the minds of monsters
+D:before the wearer.
+
+N:475:Sustenance
+G:":d
+I:40:21:0
+W:60:0:3:20000
+A:60/4
+F:SUST_STR | SUST_INT | SUST_WIS | SUST_DEX | SUST_CON | SUST_CHR |
+F:HOLD_LIFE | SLOW_DIGEST | EASY_KNOW |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This blessed amulet will make the wearer impervious to evil magics
+D:that would sap innate abilities. It also slows down the digestive
+D:system, making food less necessary on long journeys.
+
+# The Palantir of Minas Ithil -- see artifact list
+
+N:476:& Palantir~
+G:~:y
+I:39:107:0
+W:75:0:200:0
+P:0:10d10:0:0:0
+F:INSTA_ART
+
+# The Elfstone 'Elessar' -- see artifact list
+
+N:477:& Elfstone~
+G:":g
+I:40:19:0
+W:60:0:3:50000
+F:INSTA_ART
+
+# The Jewel 'Evenstar' -- see artifact list
+
+N:478:& Jewel~
+G:":w
+I:40:20:0
+W:50:0:3:35000
+F:INSTA_ART
+
+# The Ring of Durin -- see artifact list
+
+N:479:& Ring~
+G:=:d
+I:45:57:0
+W:70:0:2:65000
+F:INSTA_ART | SPECIAL_GENE
+
+##### And here starts the gold/gems #####
+
+N:480:copper
+G:$:u
+I:100:1:0
+W:1:0:0:3
+
+N:481:copper
+G:$:u
+I:100:2:0
+W:1:0:0:4
+
+N:482:copper
+G:$:u
+I:100:3:0
+W:1:0:0:5
+
+N:483:silver
+G:$:s
+I:100:4:0
+W:1:0:0:6
+
+N:484:silver
+G:$:s
+I:100:5:0
+W:1:0:0:7
+
+N:485:silver
+G:$:s
+I:100:6:0
+W:1:0:0:8
+
+N:486:garnets
+G:$:r
+I:100:7:0
+W:1:0:0:9
+
+N:487:garnets
+G:$:r
+I:100:8:0
+W:1:0:0:10
+
+N:488:gold
+G:$:y
+I:100:9:0
+W:1:0:0:12
+
+N:489:gold
+G:$:y
+I:100:10:0
+W:1:0:0:14
+
+N:490:gold
+G:$:y
+I:100:11:0
+W:1:0:0:16
+
+N:491:opals
+G:$:W
+I:100:12:0
+W:1:0:0:18
+
+N:492:sapphires
+G:$:b
+I:100:13:0
+W:1:0:0:20
+
+N:493:rubies
+G:$:r
+I:100:14:0
+W:1:0:0:24
+
+N:494:diamonds
+G:$:w
+I:100:15:0
+W:1:0:0:28
+
+N:495:emeralds
+G:$:g
+I:100:16:0
+W:1:0:0:32
+
+N:496:mithril
+G:$:B
+I:100:17:0
+W:1:0:0:40
+
+N:497:adamantite
+G:$:G
+I:100:18:0
+W:1:0:0:80
+
+
+##### Objects 498 and 499 are the "Morgoth Artifacts" #####
+
+# These objects, like objects 500 to 511, are never created
+# without being turned into artifacts. This simplifies the
+# code for "killing the winner monster".
+
+N:498:& Mighty Hammer~
+G:\:D
+I:21:50:0
+W:15:0:200:1000
+P:0:3d9:0:0:0
+F:SHOW_MODS | INSTA_ART | MUST2H | SPECIAL_GENE
+f:MUST2H
+
+N:499:& Massive Iron Crown~
+G:]:D
+I:33:50:0
+W:44:0:20:1000
+P:0:1d1:0:0:0
+F:INSTA_ART | SPECIAL_GENE
+
+
+##### Objects 500 to 511 are "Special Artifacts" #####
+
+# These objects do not specify "full names" because the artifact name
+# is added in "obj-desc.c" based on the artifact index ("i_ptr->name1").
+#
+# These objects do specify a "base name", which is used when the object
+# is "aware" (always true for lites)
+#
+# The Lites (and the One Ring) specify "physical colors", which
+# over-ride the "flavor" colors, if any. Note that the "One Ring"
+# also has a specific check for "unknown" in which it changes its
+# name to "a plain gold ring". See "object1.c"
+#
+# Note that ALL artifacts are given "IGNORE_ACID/ELEC/FIRE/COLD",
+# so we do not need to specify that here.
+#
+# Note that the "INSTA_ART" flag is used to prevent these objects from
+# being created without also turning them into artifacts. This flag
+# must be specified both here and in the artifact template.
+#
+# The only reason for having six different "ring" templates and three
+# different "amulet" templates is to allow each "special artifact" to
+# have a different "color" and "flavor", and also to allow the use of
+# special "base names" (such as "Necklace").
+
+
+# The Phial of Galadriel -- see artifact list
+
+N:500:& Phial~
+G:~:y
+I:39:100:0
+W:20:0:10:10000
+P:0:1d1:0:0:0
+F:INSTA_ART
+
+# The Star of Elendil -- see artifact list
+
+N:501:& Star~
+G:~:B
+I:39:101:0
+W:30:0:5:25000
+P:0:1d1:0:0:0
+F:INSTA_ART
+
+# The Arkenstone of Thrain -- see artifact list
+
+N:502:& Arkenstone~
+G:~:R
+I:39:102:0
+W:60:0:5:60000
+P:0:1d1:0:0:0
+F:INSTA_ART
+
+
+# The Amulet of Carlammas -- see artifact list
+
+N:503:& Amulet~
+G:":d
+I:40:10:0
+W:50:0:3:60000
+F:INSTA_ART
+
+# The Amulet of Ingwe -- see artifact list
+
+N:504:& Amulet~
+G:":d
+I:40:11:0
+W:60:0:3:90000
+F:INSTA_ART
+
+# The Necklace 'Nauglamir' -- see artifact list
+
+N:505:& Necklace~
+G:":d
+I:40:12:0
+W:70:0:3:75000
+F:INSTA_ART
+
+
+# The Ring of Barahir -- see artifact list
+
+N:506:& Ring~
+G:=:d
+I:45:32:0
+W:50:0:2:65000
+F:INSTA_ART
+
+# The Ring of Tulkas -- see artifact list
+
+N:507:& Ring~
+G:=:d
+I:45:33:0
+W:90:0:2:150000
+F:INSTA_ART
+
+# The Ring of Power 'Narya' -- see artifact list
+
+N:508:& Ring~
+G:=:d
+I:45:34:0
+W:80:0:2:100000
+F:INSTA_ART | SPECIAL_GENE
+
+# The Ring of Power 'Nenya' -- see artifact list
+
+N:509:& Ring~
+G:=:d
+I:45:35:0
+W:90:0:2:200000
+F:INSTA_ART
+
+# The Ring of Power 'Vilya' -- see artifact list
+
+N:510:& Ring~
+G:=:d
+I:45:36:0
+W:100:0:2:300000
+F:INSTA_ART
+
+# The Ring of Power 'The One Ring' -- see artifact list
+
+N:511:& Ring~
+G:=:y
+I:45:37:0
+W:110:0:2:5000000
+F:INSTA_ART
+
+
+### Room for new objects added after 511 (Zangband 2.1.0): 512-575
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+N:520:Reflection
+G:":d
+I:40:9:0
+W:60:0:3:30000
+A:60/4
+F:REFLECT | EASY_KNOW
+f:REFLECT |
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This wondrous amulet will magically make the wearer
+D:reflect arrows and bolts launched by adversaries.
+
+#521 and 522 cannot have EASY_KNOW because they may be cursed
+
+N:521:Anti-Magic
+G:":d
+I:40:13:0
+W:40:0:3:30000
+A:40/4
+F:NO_MAGIC
+f:NO_MAGIC
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This amulet wards off magic of any kind, good or bad.
+
+N:522:Anti-Teleportation
+G:":d
+I:40:14:0
+W:30:0:3:15000
+A:30/4
+F:NO_TELE
+f:NO_TELE
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This amulet will prevent the space-time continuum from
+D:being disrupted around the wearer.
+
+#523 cannot have EASY_KNOW because it can get random resistances
+
+N:523:Resistance
+G:":d
+I:40:15:0
+W:50:0:3:25000
+A:50/4
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD
+f:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This amulet will make the wearer resist the elements.
+
+##### New arms #####
+
+N:524:& Zweihander~
+G:|:w
+I:23:29:0
+W:40:0:280:580
+A:40/3
+P:0:4d6:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This great sword of foreign origin is approximately 6 feet long. The hilt is
+D:long enough for even four hands to grip. A mighty weapon for a warrior.
+
+# Dwarven lantern
+N:525:& Dwarven Lantern~
+G:~:b
+I:39:3:0
+W:15:0:50:5000
+A:15/2
+P:0:1d1:0:0:0
+F:EASY_KNOW | IGNORE_FIRE | LITE2
+f:LITE2
+D:Made by the dwarves, this lantern provides light in the
+D:darkest recesses of the earth.
+
+N:526:& Splint Mail~
+G:[:D
+I:37:10:0
+W:35:0:250:950
+A:35/1
+P:19:1d4:-2:0:0
+D:A suit of metal armour that consists of metal strips applied vertically
+D:to the backing of chain, leather, or cloth.
+
+# Everburning torch
+N:527:& Everburning Torch~
+G:~:R
+I:39:2:0
+W:5:0:50:2500
+A:5/1
+P:0:1d1:0:0:0
+F:EASY_KNOW | IGNORE_FIRE | LITE1
+f:LITE1
+D:This enchanted torch never needs to be fuelled.
+
+N:528:& Trifurcate Spear~
+G:/:o
+I:22:26:0
+W:35:0:140:400
+A:35/3
+P:0:2d9:0:0:0
+F:SHOW_MODS
+D:This deadly spear's point has three branches, suited for inflicting
+D:greater damage than a regular spear.
+
+N:529:& Three Piece Rod~
+G:\:u
+I:21:11:0
+W:20:0:120:350
+A:20/3
+P:0:3d3:0:0:0
+F:SHOW_MODS
+D:A descendant of the threshing flail, consisting of three short wooden
+D:pieces linked by chain or rope.
+
+# Feanorian Lamp
+N:530:& Feanorian Lamp~
+G:~:B
+I:39:4:0
+W:25:0:50:15000
+A:25/3
+P:0:1d1:0:0:0
+F:EASY_KNOW | IGNORE_FIRE | LITE3
+f:LITE3
+D:Made by the descendants of the Noldo craftsman, this lamp
+D:contains a part of the flame which burned inside Feanor.
+
+N:531:& Fur Cloak~
+G:(:W
+I:35:3:0
+W:20:0:30:100
+A:20/2:30/2
+P:3:0d0:0:0:0
+D:A coat made from the fur of various wild animals - it is
+D:somewhat bulky, but still spacious enough to wear over armour.
+
+N:532:Water Curing
+G:!:d
+I:72:18:80
+W:0:0:4:0
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE
+D:It is a magical component that can purify water.
+
+N:533:& Hatchet~
+G:/:s
+I:24:1:0
+W:10:0:60:120
+A:10/2
+P:0:1d5:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:This is a large axe that could be wielded in one or two hands. It has
+D:a single blade with a pick on the reverse, designed for armour piercing.
+
+##### New armour #####
+
+N:535:& Rhino Hide Armour~
+G:(:s
+I:36:8:0
+W:15:0:110:400
+A:15/1
+P:8:1d1:-1:0:0
+D:A hard leather armour made from the thick hide of a rhinoceros.
+
+N:536:& Leather Jacket~
+G:(:U
+I:36:12:0
+W:20:0:130:550
+A:20/3
+P:12:1d2:-1:0:0
+D:A garment fashioned from animal hide, resembling a robe that has been
+D:shortened to waist-length, with straight-cut sleeves and a bit of decorative
+D:fringe around the collar.
+
+##### New weapons #####
+
+N:537:& Sickle~
+G:/:s
+I:22:3:0
+W:10:0:70:110
+A:10/3
+P:0:2d3:0:0:0
+F:SHOW_MODS
+D:A semicircular blade attached to a short handle, good for chopping
+D:things up into pieces.
+
+# XXX
+# XXX
+# XXX
+# XXX
+
+N:542:& Club~
+G:\:u
+I:21:1:0
+W:0:0:100:3
+A:0/1
+P:0:1d4:0:0:0
+F:SHOW_MODS
+D:A stout heavy stick, thicker at one end. Useful for
+D:shattering and smashing things.
+
+N:543:& Broad Spear~
+G:/:w
+I:22:7:0
+W:14:0:100:240
+A:14/3
+P:0:1d9:0:0:0
+F:SHOW_MODS
+D:Designed specifically for foot soldiers combatting cavalry, this spear
+D:is too heavy to be effective when thrown.
+
+N:544:& Khopesh~
+G:|:W
+I:23:14:0
+W:10:0:130:190
+A:10/2
+P:0:2d4:0:0:0
+F:SHOW_MODS
+D:This sword comes from the regions of Far Harad. The blade is straight
+D:for 18 inches, and then it curves for another 2 feet.
+
+N:545:& Flamberge~
+G:|:W
+I:23:26:0
+W:40:0:230:600
+A:40/2
+P:0:3d7:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:A large, two-handed sword with a blade that weaves
+D:left and right until it reaches the hilt.
+
+N:546:& Claymore~
+G:|:W
+I:23:23:0
+W:40:0:200:600
+A:40/2
+P:0:2d8:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:Also known as a Claidhmore, or Greatsword, this weapon is favoured
+D:by powerful mercenaries. The blade is large, straight, and broad,
+D:almost as large as a two-handed sword.
+
+N:547:& Espadon~
+G:|:W
+I:23:24:0
+W:40:0:200:600
+A:40/3
+P:0:2d9:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This is the strictly two-handed version of the bastard sword.
+D:The blade is of medium length, double-edged, and considerably
+D:heavy to wield.
+
+N:548:& Great Scimitar~
+G:|:W
+I:23:22:0
+W:40:0:240:500
+A:40/3
+P:0:4d5:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This is a larger version of the curved oriental blade.
+D:Runes of war decorate its golden hilt.
+
+
+### Trapping Kits
+
+N:549:Arrow
+G:`:r
+I:46:2:0
+W:10:0:60:150
+A:10/2:50/2
+F:SHOW_MODS
+D:It must be loaded with arrows, which will be
+D:fired at the monster who triggers the trap.
+
+N:550:Bolt
+G:`:o
+I:46:3:0
+W:20:0:220:300
+A:20/2:50/2
+F:SHOW_MODS
+D:It must be loaded with crossbow bolts, which will
+D:be fired at the monster who triggers the trap.
+
+N:551:& Fauchard~
+G:/:s
+I:22:6:0
+W:18:0:155:301
+A:18/2
+P:0:1d10:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:It is a type of glaive with two ornate hooks on the back
+D:of the blade. It is typically 8 to 9 feet long.
+
+N:552:& Guisarme~
+G:/:s
+I:22:16:0
+W:21:0:165:320
+A:21/1
+P:0:2d5:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:Mounted on a long shaft for maximum reach, this weapon is
+D:effective at repelling both cavalry and infantry.
+
+N:553:& Heavy Lance~
+G:/:s
+I:22:29:0
+W:43:0:400:700
+A:43/2
+P:0:4d8:0:0:0
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:This is a shock weapon. Its purpose is to unhorse a rider
+D:in single combat, or smash through the armour of opposing lines.
+
+N:554:& Basilard~
+G:|:w
+I:23:9:0
+W:15:0:80:220
+A:15/3
+P:0:1d8:0:0:0
+F:SHOW_MODS
+D:This is a two-edged dagger with a long blade. A favourite among travellers
+D:and warriors alike, it can be worn comfortably with plain clothes
+D:as well as armour.
+
+### Trapping Kits
+
+N:555:Catapult
+G:`:R
+I:46:1:0
+W:1:0:50:40
+A:1/2:20/2
+F:SHOW_MODS
+D:It must be loaded with sling bullets, which will
+D:be fired at the monster who triggers the trap.
+
+N:556:& Ring Mail~
+G:[:s
+I:37:2:0
+W:20:0:200:500
+A:20/1
+P:12:1d4:-2:0:0
+D:A suit of non-overlapping metal rings sewn onto a
+D:heavy leather backing.
+
+N:557:& Cord Armour~
+G:(:y
+I:36:9:0
+W:5:0:80:40
+A:5/1
+P:6:1d1:0:0:0
+D:Fibres of hemp or other natural material woven and knotted
+D:into a thick, tough fabric.
+
+N:558:& Paper Armour~
+G:(:w
+I:36:3:0
+W:5:0:30:40
+A:5/2
+P:4:1d1:0:0:0
+D:Thickly pleated sheets of paper assembled together into
+D:a suit of armour.
+
+N:559:& Padded Armour~
+G:(:y
+I:36:10:0
+W:2:0:60:40
+A:2/1
+P:4:1d1:0:0:0
+D:Heavy, multi-layered cloth sewn together to cover the body,
+D:with extra padding between layers.
+
+### Trapping Kits
+
+N:560:Fumes
+G:`:G
+I:46:4:0
+W:2:0:20:50
+A:2/2:30/2
+D:It must be loaded with potions, which will splatter
+D:over the monster who triggers the trap.
+
+N:561:& Stone and Hide Armour~
+G:(:U
+I:36:15:0
+W:35:0:200:500
+A:35/7
+P:15:1d1:-1:0:0
+D:A primitive armour made from a thick hide reinforced by stone shards.
+
+### Trapping Kits
+
+N:562:Magic
+G:`:g
+I:46:5:0
+W:5:0:20:50
+A:5/2:40/2
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC
+D:It must be loaded with scrolls, which will release
+D:their spells at the monster who triggers the trap.
+
+N:563:Device
+G:`:v
+I:46:6:0
+W:20:0:20:50
+A:20/2:40/2:60/2
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC
+D:It must be loaded with a magic device (wand, staff, or rod), which
+D:will fire its spell at the monster who triggers the trap.
+
+N:564:Nothing
+G:?:d
+I:70:53:0
+W:1:0:5:2
+A:1/1
+P:0:1d1:0:0:0
+
+N:565:Poison
+G:*:G
+I:4:1:5
+W:0:0:4:2
+A:0/1
+P:0:1d1:0:0:0
+
+# wand
+N:566:Nothing
+G:-:d
+I:65:2:-1:SPELL=Nothing
+W:2:0:10:20
+A:2/1
+P:0:1d1:0:0:0
+F:NO_RECHARGE | SPECIAL_GENE
+
+N:567:Nothing
+G:=:d
+I:45:50:0
+W:5:0:2:20
+A:5/1
+P:0:1d1:0:0:0
+
+N:568:Nothing
+G:_:d
+I:55:30:0
+W:5:0:50:50
+A:5/1
+P:0:1d1:0:0:0
+F:NO_RECHARGE | SPECIAL_GENE
+
+N:569:Nothing
+G:-:d
+I:66:0:1
+W:5:0:10:50
+A:5/1
+P:0:1d1:0:0:0
+
+
+# Here comes the Essences (randomly interspersed with other stuff...)
+
+N:570:Explosion
+G:*:G
+I:4:2:50
+W:0:0:4:2
+A:0/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+N:571:Teleport
+G:*:G
+I:4:3:190
+W:0:0:4:2
+A:5/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+# Amulet of Nothing
+
+N:572:Nothing
+G:":d
+I:40:16:0
+W:10:0:3:20
+A:10/1
+P:0:1d1:0:0:0
+
+# An artifact potion! The Blood of Life...
+
+N:573:& Blood~ of Life
+G:!:d
+I:71:3:200
+W:70:0:4:10000
+A:70/16
+P:0:1d1:0:0:0
+T:71:2
+F:NORM_ART | FULL_NAME
+D:Quaffing this measure of living blood will imbue your body and soul
+D:with the power to escape death one time.
+
+N:574:Cold
+G:*:G
+I:4:4:200
+W:0:0:4:2
+A:5/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+N:575:Fire
+G:*:G
+I:4:5:200
+W:0:0:4:2
+A:5/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+N:576:Acid
+G:*:G
+I:4:6:200
+W:0:0:4:2
+A:6/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+# Mage staffs (for Sorcerers to wield.)
+
+N:577:& Mage Staff~
+G:\:B
+I:6:1:0
+W:5:0:12:300
+A:5/1:20/1:50/1:80/1
+P:0:1d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:It looks like a simple walking stick, plain and nondescript.
+D:In the hands of a spellcaster, it can be a deadly weapon.
+
+# An extra ring, out of place
+
+N:578:Lightning
+G:=:d
+I:45:56:0
+W:50:0:2:3000
+A:50/1
+P:0:0d0:0:0:15
+a:HARDCORE=BA_ELEC_4
+F:RES_ELEC | IGNORE_ELEC | ACTIVATE
+f:RES_ELEC |
+D:This sparkling circlet grants you protection, makes electricity less
+D:dangerous and even allows you to call forth a ball of lightning.
+
+# More essences
+
+N:579:Life
+G:*:G
+I:4:7:300
+W:0:0:4:2
+A:0/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+N:580:Confusion
+G:*:G
+I:4:8:100
+W:0:0:4:2
+A:0/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+N:581:Light
+G:*:G
+I:4:9:60
+W:0:0:4:2
+A:0/1:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+# The Ring of Flare -- see artifact list
+
+N:582:& Ring~
+G:=:y
+I:45:52:5
+W:50:25:2:75000
+F:INSTA_ART
+
+# Potion of Invisibility
+
+N:583:Invisibility
+G:!:d
+I:71:8:0
+W:3:0:4:50
+A:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This magical brew will temporarily hide you from sight, and also attunes you
+D:to this state so that your eyes can still perceive your hidden form.
+
+N:584:Chaos
+G:*:G
+I:4:10:200
+W:20:0:4:2
+A:20/1
+P:0:1d1:0:0:0
+F:ATTR_MULTI
+
+# Potion of Corruption
+
+N:585:Corruption
+G:!:d
+I:71:10:0
+W:3:0:4:0
+A:20/1:30/1:40/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This concoction of toxic wastes will strangely warp your shape.
+
+# Ring of Invisibility
+
+N:586:Invisibility
+G:=:d
+I:45:53:4
+W:50:0:2:10000
+A:50/1
+F:INVIS | HIDE_TYPE
+f:INVIS |
+D:This magical bauble will hide you from sight.
+
+### Just another essence, to confuse the edit file hackers ;) ###
+
+N:587:Time
+G:*:G
+I:4:11:600
+W:20:0:4:2
+A:20/1:40/1:60/1
+P:0:1d1:0:0:0
+
+######### Here are the parchments ########
+
+N:588:Deep Thoughts
+G:?:o
+I:8:0:0
+W:3:0:5:50
+A:3/1
+D:This parchment contains the thoughts of a powerful
+D:wizard who departed Middle-earth long ago.
+
+N:589:More Deep Thoughts
+G:?:o
+I:8:1:0
+W:4:0:5:50
+A:4/1
+D:This parchment contains the thoughts of a powerful
+D:wizard who departed Middle-earth long ago.
+
+N:590:Compendium of Deep Thoughts
+G:?:o
+I:8:2:0
+W:5:0:5:50
+A:5/1
+D:This parchment contains the thoughts of a powerful
+D:wizard who departed Middle-earth long ago.
+
+N:591:Artifact Lore Vol. I
+G:?:o
+I:8:6:0
+W:40:0:5:50
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+N:592:Artifact Lore Vol. II
+G:?:o
+I:8:7:0
+W:40:0:5:50
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+N:593:Artifact Lore Vol. III
+G:?:o
+I:8:8:0
+W:40:0:5:50
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+N:594:Monstrous Compendium 1
+G:?:o
+I:8:9:0
+W:10:0:5:50
+A:10/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:595:Monstrous Compendium 2
+G:?:o
+I:8:10:0
+W:11:0:5:50
+A:11/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:596:Monstrous Compendium 3
+G:?:o
+I:8:11:0
+W:12:0:5:50
+A:12/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:597:Monstrous Compendium 4
+G:?:o
+I:8:12:0
+W:13:0:5:50
+A:13/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:598:Monstrous Compendium 5
+G:?:o
+I:8:13:0
+W:14:0:5:50
+A:14/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:599:Monstrous Compendium 6
+G:?:o
+I:8:14:0
+W:15:0:5:50
+A:15/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:600:Monstrous Compendium 7
+G:?:o
+I:8:15:0
+W:16:0:5:50
+A:16/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:601:Monstrous Compendium 8
+G:?:o
+I:8:16:0
+W:17:0:5:50
+A:17/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:602:Monstrous Compendium 9
+G:?:o
+I:8:17:0
+W:18:0:5:50
+A:18/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:603:Monstrous Compendium 10
+G:?:o
+I:8:18:0
+W:19:0:5:50
+A:19/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+N:604:Monstrous Compendium 11
+G:?:o
+I:8:19:0
+W:20:0:5:50
+A:20/2
+D:This parchment contains a small part of the collected
+D:lore concerning monsters inhabiting Arda.
+
+#### Here come the shape-shifting potions. ####
+
+N:605:& Morphic Oil~ of #
+G:!:d
+I:72:1:0
+W:5:0:4:100
+A:1/3:5/1:10/1
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This concoction can transform your body for a short period of time.
+
+# XXX 606 -> 617
+
+# The Mimic's cloaks
+
+N:618:& #~
+G:(:y
+I:35:100:0:50
+W:5:130:30:100
+A:5/1:15/1:35/1:55/1:75/1
+P:1:1d1:0:0:0
+D:Combined with proper skill, this cloak can make you seem
+D:like a different creature. Otherwise, it just provides some
+D:extra protection.
+
+# XXX 619 -> 639
+
+# One more essence...
+
+N:640:Magic
+G:*:G
+I:4:12:700
+W:20:0:4:2
+A:30/1:50/1:80/1
+P:0:1d1:0:0:0
+
+# Here are the corpses
+
+N:641:corpse
+G:~:U
+I:9:1:3000
+W:20:0:80:0
+A:30/1
+P:0:1d1:0:0:0
+F:DECAY
+D:Whatever happened with this one, it wasn't pretty.
+
+N:642:skeleton
+G:~:U
+I:9:2:800
+W:20:0:2:0
+A:30/1
+P:0:1d1:0:0:0
+D:The sorry, bony remains of some hapless creature.
+
+N:643:head
+G:~:U
+I:9:3:600
+W:20:0:10:0
+A:30/1
+P:0:1d1:0:0:0
+F:DECAY
+D:2 eyes, 2 ears, a mouth, some hair -- yep, that's a head.
+
+N:644:skull
+G:~:U
+I:9:4:1000
+W:20:0:20:0
+A:30/1
+P:0:1d1:0:0:0
+D:It's a white bony skull, smiling to you without teeth.
+
+N:645:raw meat
+G:~:U
+I:9:5:1200
+W:20:0:10:2
+A:30/1
+P:0:1d1:0:0:0
+F:DECAY
+D:Yuck, just looking at it makes your stomach upset.
+
+# New armour: Thunderlord Coat. (With two low resists, it shouldn't be
+# at only 5th level. Make it 25th. -JLE...)
+
+N:646:& Thunderlord Coat~
+G:(:y
+I:36:16:0
+W:5:0:60:400
+A:25/1
+P:9:1d1:0:0:0
+F:RES_FIRE | RES_COLD |
+D:This suit of thick impregnated cloth is worn by the riders of flying steeds,
+D:and protects them from extremes of temperatures.
+
+# The Stone of Lore -- see artifact list
+N:647:& Stone~
+G:~:g
+I:39:106:0
+W:15:0:15:20000
+F:INSTA_ART | SPECIAL_GENE
+
+# Here are the boomerangs
+
+N:648:& Small Wooden Boomerang~
+G:{:y
+I:15:1:0
+W:1:0:60:10
+A:1/1:5/2:10/2:20/2
+P:0:1d4:0:0:0
+D:A small curved piece of wood.
+
+N:649:& Wooden Boomerang~
+G:{:y
+I:15:2:0
+W:10:0:60:100
+A:10/1:20/2
+P:0:1d8:0:0:0
+D:A strange, curved leaf-shaped piece of wood.
+
+N:650:& Small Metal Boomerang~
+G:{:y
+I:15:3:0
+W:20:0:60:400
+A:20/1:30/2
+P:0:3d4:0:0:0
+D:A short boomerang with metal blades on the "forward" edges.
+
+N:651:& Metal Boomerang~
+G:{:y
+I:15:4:0
+W:30:0:60:800
+A:30/1:50/2
+P:0:4d5:0:0:0
+D:A strange curved leaf-shaped piece of wood, its "forward" edges enhanced with metal blades.
+
+# The Space-Time Anchor -- see artifact list
+N:652:& Anchor~
+G:~:v
+I:39:105:0
+W:30:0:15:50000
+F:INSTA_ART
+
+# To convert monsters into objects for wielding them ! ! ! :)
+# Funny, funny, funny :)
+# pval the monster idx, pval2 for the monster hp
+N:653:& ~
+G:~:y
+I:99:1:0
+W:127:200:60:0
+A:127/255
+P:0:0d0:0:0:0
+
+N:654:Summon Never-Moving Pet
+G:?:d
+I:70:6:0
+W:5:0:5:125
+A:5/1:15/1:25/1:35/1:65/1:85/1:95/1
+D:A piece of paper, inscribed with runes which will summon an immobile creature to your aid.
+
+# XXX
+# XXX
+
+###The potions of cure insanity, in light, medium, regular, and full
+###versions. Cure light's W:x entry reduced to 1, so it doesn't boost level rating when appearing
+###at 50' - nyra
+
+N:657:Cure Light Insanity
+G:!:d
+I:72:14:0
+W:1:5:4:15
+A:5/1:1/1:3/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A magical potion which drives away irrational quirks of behaviour.
+
+N:658:Cure Serious Insanity
+G:!:d
+I:72:15:0
+W:10:5:4:40
+A:10/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A magical brew which will lift shadows that have been cast on your soul.
+
+N:659:Cure Critical Insanity
+G:!:d
+I:72:16:0
+W:15:5:4:100
+A:15/3
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This drink will cure you even of serious mental disorders.
+
+N:660:Cure Insanity
+G:!:d
+I:72:17:0
+W:25:5:4:300
+A:25/3
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:This blessed potion can make you completely sane and healthy, even if you have only the
+D:faintest shred of sanity left.
+
+# The Phial of Undeath -- see artifact list. He he he he...
+
+N:661:& Phial~
+G:~:y
+I:39:103:0
+W:1:0:10:0
+P:0:1d1:0:0:0
+F:INSTA_ART
+
+# Here is the random artifact type.
+# This is used as a template -- the sval will be chosen later
+# on in the game to be an index to the random_artifacts array,
+# which in turn determines:
+#
+# Name
+# Color
+# Level
+# Cost
+
+N:662:Random Artifact
+G:~:o
+I:102:0:0
+W:1:0:50:0
+A:1/1:10/1:20/1:30/1
+F:INSTA_ART | ACTIVATE | ACTIVATE_NO_WIELD
+
+N:663:Craftsmanship
+G:?:d
+I:70:19:0
+W:80:0:5:200000
+A:80/16
+D:A powerful scroll, desired by many, as it can magically improve the special powers of magical
+D:weaponry.
+
+# A Parchment, not the Artifact
+N:664:The One Ring
+G:?:s
+I:8:4:0
+W:10:100:5:50
+A:10/2
+D:This parchment contains words in the Black Speech and Westron,
+D:and they seem to speak of a powerful Ring... could it be true?
+
+# XXX 665 - 668 were the old music books
+
+### Musical Instruments ###
+
+N:669:& Horn~
+G:/:W
+I:14:60:1
+W:7:0:30:400
+A:7/2:20/1:40/1:80/1
+P:0:1d1:0:0:0
+F:CON | ACTIVATE | WIELD_CAST
+D:A simple wind instrument made from brass. If used by inexperienced musicians it sounds
+D:like somebody making "prbbt!" noises down a drainpipe.
+
+N:670:& Drum~
+G:/:W
+I:14:58:1
+W:7:0:30:400
+A:7/2:20/1:40/1:80/1
+P:0:1d1:0:0:0
+F:STR | WIELD_CAST
+D:A sort of clay pot with a bit of skin stretched over its mouth.
+
+N:671:& Harp~
+G:/:W
+I:14:59:1
+W:7:0:30:400
+A:7/2:20/1:40/1:80/1
+P:0:1d1:0:0:0
+F:CHR | WIELD_CAST
+D:A number of strings held by a wooden frame.
+
+#N:672:& Banjo~
+#G:/:W
+#I:14:2:0
+#W:15:0:30:200
+#A:15/1
+#P:0:1d1:0:0:0
+#D:A combination of kithara and tambourine. It looks strange and sounds bad.
+
+#N:673:& Lute~
+#G:/:W
+#I:14:3:0
+#W:20:0:30:200
+#A:20/1
+#P:0:1d1:0:0:0
+#D:A string instrument, to be plucked with your fingers.
+
+#N:674:& Mandolin~
+#G:/:W
+#I:14:4:0
+#W:25:0:30:300
+#A:25/1
+#P:0:1d1:0:0:0
+#D:A small string instrument, typically strummed with your fingers or a plectron.
+
+# The Palantir of Orthanc -- see artifact list
+
+N:675:& Palantir~
+G:~:y
+I:39:104:0
+W:75:0:200:100000
+P:0:10d10:0:0:0
+F:INSTA_ART
+
+### The Monster Egg template; note the theoretical weight of 3 lbs ###
+
+N:676:Egg
+G:,:W
+I:10:1:0
+W:5:0:30:100
+A:5/1:15/1:25/1:35/1
+P:0:1d1:0:0:0
+F:ACTIVATE | ACTIVATE_NO_WIELD
+
+### Two more scrolls ###
+
+N:677:Reset Recall
+G:?:d
+I:70:23:0
+W:20:0:5:125
+A:20/1:25/1:35/1
+D:A strange formula is inscribed on this scroll, which allows you to define another place as the
+D:location to which recalls shall move you.
+
+N:678:Divination
+G:?:d
+I:70:31:0
+W:20:0:5:600
+A:30/1:45/1:55/1
+D:This scroll is inscribed with a ritual which allows you to discern what fate holds in store for
+D:you.
+
+### Here comes the Runes ###
+
+N:679:Self
+G:?:b
+I:105:0:0
+W:3:5:2:40
+A:3/1
+P:0:1d1:0:0:0
+
+N:680:Ray
+G:?:b
+I:105:2:0
+W:10:5:2:300
+A:10/1
+P:0:1d1:0:0:0
+F:IGNORE_COLD | IGNORE_ELEC
+
+N:681:Sphere
+G:?:b
+I:105:3:0
+W:15:5:2:1000
+A:15/1
+P:0:1d1:0:0:0
+F:IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+N:682:Knowledge
+G:?:b
+I:104:91:0
+W:6:5:2:200
+A:6/1
+P:0:1d1:0:0:0
+
+N:683:Life
+G:?:D
+I:104:53:0
+W:3:5:2:200
+A:3/1
+P:0:1d1:0:0:0
+
+N:684:Fire
+G:?:r
+I:104:5:0
+W:10:5:2:300
+A:10/1
+P:0:1d1:0:0:0
+F:IGNORE_FIRE
+
+N:685:Cold
+G:?:b
+I:104:4:0
+W:12:5:2:300
+A:12/1
+P:0:1d1:0:0:0
+F:IGNORE_COLD
+
+N:686:Lightning
+G:?:W
+I:104:1:0
+W:13:5:2:300
+A:13/1
+P:0:1d1:0:0:0
+F:IGNORE_ELEC
+
+N:687:Acid
+G:?:B
+I:104:3:0
+W:16:5:2:300
+A:16/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+
+N:688:Element
+G:?:g
+I:104:10:0
+W:23:5:2:1000
+A:23/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+N:689:Chaos
+G:?:v
+I:104:30:0
+W:26:5:2:2000
+A:26/1
+P:0:1d1:0:0:0
+F:ATTR_MULTI
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+N:690:Mind
+G:?:D
+I:104:85:0
+W:19:5:2:3000
+A:19/1
+P:0:1d1:0:0:0
+F:IGNORE_ELEC
+
+N:691:Holding
+G:?:B
+I:104:75:0
+W:5:5:2:500
+A:5/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+
+N:692:Arrow
+G:?:b
+I:105:1:0
+W:6:5:2:100
+A:6/1
+P:0:1d1:0:0:0
+F:IGNORE_ELEC
+
+N:693:Power Surge
+G:?:b
+I:105:4:0
+W:50:5:2:5000
+A:50/1
+P:0:1d1:0:0:0
+F:IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | IGNORE_ACID
+
+N:694:Armageddon
+G:?:b
+I:105:5:0
+W:30:5:2:4000
+A:30/1
+P:0:1d1:0:0:0
+F:IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | IGNORE_ACID
+
+N:695:Gravity
+G:?:G
+I:104:35:0
+W:16:5:2:300
+A:16/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+
+# And, among the runes, one more essence...
+
+N:696:Extra Life
+G:*:G
+I:4:13:900
+W:50:0:4:2
+A:50/1:70/1:90/1
+P:0:1d1:0:0:0
+
+# Now, the rest of the runes...
+
+N:697:Undeath
+G:?:G
+I:104:92:0
+W:35:5:2:1000
+A:35/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+
+N:698:Protection
+G:?:G
+I:104:74:0
+W:45:5:2:1500
+A:45/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID
+
+# XXX
+
+# The Ring of Precognition (now a k_info.txt artifact)
+N:700:& Ring~ of Precognition
+G:=:d
+I:45:51:0
+W:90:0:2:300000
+A:90/100
+T:45:23
+F:PRECOGNITION |
+f:PRECOGNITION |
+F:NORM_ART | FULL_NAME
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:This magical ring allows you to know what you will encounter in the near future.
+
+# Athelas, cures Black Breath
+
+N:701:& Sprig~ of Athelas
+G:,:g
+I:80:40:0
+W:25:5:2:450
+A:25/2:55/1:85/1
+D:'When the black breath blows / And death's shadow grows /
+D:And all lights pass / Come Athelas! Come Athelas! /
+D:Life to the dying / In the King's hands lying.'
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+# The Scroll of Deincarnation (now an artifact)
+
+N:720:& Old Scroll~ of Deincarnation
+G:?:d
+I:70:40:0
+W:90:0:5:160000
+A:90/140
+T:70:51
+F:NORM_ART | FULL_NAME
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:It allows you to leave your body to reincarnate into
+D:another one, however, your current body is lost in the process.
+
+N:721:& Dark Sword~
+G:|:D
+I:23:33:0
+W:25:0:70:500
+A:25/1:80/2
+P:0:3d7:0:0:0
+F:SHOW_MODS | ANTIMAGIC_50
+f:ANTIMAGIC_50
+D:A strange, very sharp long sword, which seems to drain light from its surroundings. As you
+D:wield it, you feel much less attuned to magic.
+
+N:722:Numenorean for Beginners (I)
+G:?:s
+I:8:101:0
+W:10:100:5:50
+A:10/2
+D:These ancient words still contain magic.
+
+N:723:Numenorean for Beginners (II)
+G:?:s
+I:8:102:0
+W:5:100:5:50
+A:5/2
+D:These ancient words still contain magic.
+
+N:724:Advanced Lessons of Numenorean
+G:?:s
+I:8:103:0
+W:20:100:5:50
+A:20/2
+D:These ancient words still contain magic.
+
+N:725:Advanced Lessons of Sindarin
+G:?:s
+I:8:104:0
+W:20:100:5:50
+A:20/2
+D:These ancient words still contain magic.
+
+##### Junk #####
+
+N:726:& Shard~ of Pottery
+G:~:r
+I:11:3:0
+W:0:0:5:0
+A:0/1
+P:0:1d1:0:0:0
+F:EASY_KNOW
+D:Half a clay jar -- how many failed artists have taken refuge in these dungeons?
+
+N:727:& Broken Stick~
+G:~:r
+I:11:6:0
+W:0:0:3:0
+A:0/1
+P:0:1d1:0:0:0
+F:EASY_KNOW
+D:A piece of rotten wood.
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+N:738:& Book~ of Beginner Cantrips
+G:?:w
+I:111:50:0
+W:5:0:30:100
+A:5/1
+P:0:1d1:0:0:0
+D:The blood smudged on the cover makes you wonder how many people had it before you...
+
+N:739:& Book~ of Teleportation
+G:?:w
+I:111:51:0
+W:10:0:30:1000
+A:10/1
+P:0:1d1:0:0:0
+D:A standard spellbook with a few spells.
+
+# XXX
+
+N:741:& Book~ of Summoning
+G:?:w
+I:111:52:0
+W:7:0:30:700
+A:7/1
+P:0:1d1:0:0:0
+D:A standard spellbook with a few spells.
+D:It smells like camel.
+
+# XXX
+
+# The Potion of Learning - another artifact!
+
+N:743:& Potion~ of Learning
+G:!:d
+I:71:12:200
+W:90:0:4:100000
+A:90/25
+P:0:1d1:0:0:0
+T:71:49
+F:NORM_ART | FULL_NAME
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC
+D:This old potion is supposed to grant more learning power
+D:to its user.
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+N:751:Khuzdul - The Hidden Tongue of the Dwarves
+G:?:s
+I:8:105:0
+W:2:100:5:50
+A:2/2
+D:These ancient words still contain magic.
+
+N:752:Nandorin for Dummies
+G:?:s
+I:8:106:0
+W:20:100:5:50
+A:20/2
+D:These ancient words still contain magic.
+
+N:753:Advanced Lessons of Orcish
+G:?:s
+I:8:107:0
+W:30:100:5:50
+A:30/2
+D:These ancient words still contain magic.
+
+# Here's the Ring of Flying
+N:755:Flying
+G:=:d
+I:45:54:0
+W:20:0:2:16000
+A:20/3
+F:FLY | EASY_KNOW
+f:FLY |
+D:This ring is imbued with the power of eagles. It grants you the power of flight.
+
+N:756:& Tome~ of the Time
+G:?:b
+I:111:8:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This tome seems to have trouble deciding whether it really exists now. Its flickering pages
+D:contain all that is known about the currents of time.
+
+N:757:& Spellbook~ of #
+G:?:w
+I:111:255:0
+W:10:0:30:200
+A:10/1:20/1:30/1:40/1:50/1:60/1:70/1
+P:0:1d1:0:0:0
+D:This book contains a single spell within its pages.
+
+N:758:& Tome~ of Meta Spells
+G:?:v
+I:111:9:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW | IGNORE_FIRE | ATTR_MULTI
+D:This tome gives you deeper insights on the works of magic.
+
+N:759:& Tome~ of the Mind
+G:?:B
+I:111:10:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This tome has no pages; knowledge is transferred to you if you simply
+D:hold it.
+
+N:760:& Holy Tome~ of Eru Iluvatar
+G:?:G
+I:111:20:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This dusty tome is filled with ancient rituals,
+D:designed to uncover all that is hidden.
+
+N:761:& Holy Tome~ of Manwe Sulimo
+G:?:B
+I:111:21:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:A large jewel-encrusted tome that transfers
+D:wisdom and understanding to its wearer.
+
+N:762:& War Tome~ of Tulkas
+G:?:R
+I:111:22:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This tome fills you with glorious visions of total devastation.
+D:Anyone in your way shall be destroyed.
+
+N:763:& Unholy Tome~ of the Hellflame
+G:?:v
+I:111:11:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW | ATTR_MULTI
+D:This singed book smells like burned flesh. Its power is as evident
+D:as its thirst for your blood.
+
+N:764:& Corrupted Tome~ of Melkor
+G:?:D
+I:111:23:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:A black and scarlet flame springs from this tome, issuing
+D:a thunderous roar under which you think you hear the screams of tormented souls.
+
+N:768:& Forest Tome~ of Yavanna
+G:?:G
+I:111:24:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+
+# for the Library Quest
+# Tome of PLAYER
+N:769:Tome of#
+G:?:v
+I:111:61:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW | ATTR_MULTI | SPECIAL_GENE
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | NORM_ART
+D:This book has some of your favourite spells inside.
+
+# The Ring of Phasing -- see artifact list
+
+N:770:& Ring~
+G:=:d
+I:45:55:0
+W:110:0:2:300000
+A:110/5
+F:INSTA_ART | SPECIAL_GENE
+
+N:771:[Earth]
+G:?:R
+I:114:0:0
+W:10:0:30:100
+A:10/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE
+D:A heavy lump of rock. As you touch it, you think you can see into the earth and perceive its
+D:secrets.
+
+N:772:[Fire]
+G:?:R
+I:114:1:0
+W:20:0:30:1000
+A:20/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE
+D:A bright red crystal, filled with a liquid flame that scorches you even though the surface of
+D:the gem is cool to the touch.
+
+N:773:[Air]
+G:?:r
+I:114:2:0
+W:50:0:30:2500
+A:50/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:SPECIAL_GENE
+D:A completely translucent gem; as you behold it, you hear a great wind and feel like you're
+D:about to take off and fly away.
+
+N:774:[Water]
+G:?:r
+I:114:3:0
+W:70:0:30:50000
+A:70/3
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:SPECIAL_GENE
+D:A large gem, filled with dark blue water. It feels strangely heavy and cold.
+
+N:775:[Mana]
+G:?:r
+I:114:4:0
+W:100:0:30:100000
+A:100/3
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+F:SPECIAL_GENE
+D:A large gem that seems somehow immaterial and is filled with a strange insubstantial... power?
+
+# Rod Tip of Home Summoning: now an artifact
+#N:776:& Great Rod Tip~ of Home Summoning
+N:776:Home Summoning
+G:-:d
+I:66:30:75
+W:90:0:15:150000
+A:100/14
+P:0:1d1:0:0:0
+T:66:1
+F:NORM_ART | FULL_NAME
+F:IGNORE_FIRE | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC
+D:This rod creates a little hatch, allowing access to your home, no matter how far away the abode
+D:is.
+
+# Additional swords
+N:777:& Shadow Blade~
+G:|:D
+I:23:32:1
+W:50:900:45:2000
+A:48/4:60/2:80/1
+P:0:4d4:-2:2:0
+F:IGNORE_ACID | RES_DARK | STEALTH
+f:STEALTH
+D:A thin long sword made of a completely black metal, which reflects no light.
+
+N:778:& Bluesteel Blade~
+G:|:b
+I:23:31:0
+W:60:1800:50:6000
+A:60/20
+P:0:1d6:4:0:0
+F:SHOW_MODS | VORPAL
+D:A small sword made of a blueish metal with a strangely rough surface. As anything is hurt
+D:with it, the weapon will stick inside the wound and cause horrible wounds when torn away.
+
+# Amulet
+N:779:the Serpents
+G:":G
+I:40:17:0
+W:25:0:3:10000
+A:25/1
+a:HARDCORE=BA_POIS_4
+F:RES_POIS | DEX | ACTIVATE
+D:A petrified serpent's tongue, hung on a thin chain to be clasped around your neck. It makes you
+D:like unto snakes, able to wriggle out of tight corners, impervious to poisons and poisonous
+D:yourself.
+
+# Here comes the new Essences
+
+N:780:Darkness
+G:*:G
+I:4:14:20
+W:0:0:4:1
+A:0/1:20/1:40/1
+P:0:1d1:0:0:0
+D:It's a gem that eats all light that reaches it. It's perfectly black.
+
+N:781:Knowledge
+G:*:G
+I:4:15:100
+W:0:0:4:1
+A:20/1:30/1:70/1
+P:0:1d1:0:0:0
+D:It's a blue gem with countless formulae scribbled on it.
+
+N:782:Force
+G:*:G
+I:4:16:180
+W:0:0:4:1
+A:10/1:40/1
+P:0:1d1:0:0:0
+D:It's a green gem that can barely contain the forces in it.
+
+N:783:Lightning
+G:*:G
+I:4:17:200
+W:0:0:4:1
+A:28/1
+P:0:1d1:0:0:0
+D:It's a white gem. Inside you see lightning rage.
+
+N:784:Mana
+G:*:G
+I:4:18:400
+W:0:0:4:1
+A:45/2
+P:0:1d1:0:0:0
+D:It's an everchanging gem. You feel the magic throbbing through it.
+
+# The Nine Rings for mortal men doomed to die! When a Nazgul is
+# destroyed, it drops a Ring of Power with random powers.
+
+N:785:Ring~ of Power
+G:=:d
+I:45:5:0
+W:100:0:2:1
+A:100/100
+F:INVIS | DRAIN_EXP | CURSED | HEAVY_CURSE | CURSE_NO_DROP
+f:INVIS
+F:SPECIAL_GENE | FULL_NAME
+
+# To help people climb mountains...
+
+N:786:& Climbing Set~
+G:`:B
+I:12:0:0
+W:40:0:2:50000
+A:40/3
+F:CLIMB
+f:CLIMB
+D:A bunch of screws and hooks, a small pick and a very long rope. As long as a wall or rock face
+D:isn't really high, this collection of tools should help you across it.
+
+# Parchment to help beginners
+N:787:Adventurer's Guide to Middle-earth
+G:?:o
+I:8:20:0
+W:0:0:5:1
+A:0/1
+D:Read it!
+
+### Demonblades ###
+
+N:788:& Demonblade~
+G:|:R
+I:115:55:0
+W:10:0:150:500
+A:10/1
+P:0:4d6:0:0:0
+F:SHOW_MODS | SLAY_DEMON | WIELD_CAST
+D:This blade has been taken from the corpse of a demon.
+D:Some demonic energy is still coursing through it, helping
+D:you slay other demons.
+
+N:789:& Demonshield~
+G:]:R
+I:115:56:0
+W:15:0:70:500
+A:15/1
+P:5:1d1:0:0:0
+F:REGEN | WIELD_CAST
+D:This shield has been taken from the corpse of a demon.
+D:Some demonic energy is still coursing through it, giving
+D:life to any that wield it.
+
+N:790:& Demonhorn~
+G:[:R
+I:115:57:0
+W:20:0:30:500
+A:20/1
+P:2:1d1:0:0:0
+F:LITE2 | WIELD_CAST
+D:This horn is about six feet long. Just looking at it makes you nervous.
+
+# XXX
+# XXX
+
+### Rods ###
+
+N:793:& Wooden Rod~ of#
+G:-:u
+I:67:10:0
+W:10:0:15:100
+A:5/1:10/1
+P:0:1d1:0:0:0
+D:The most common rod of all. It's the wood of oak, cut at midnight by a fair maiden.
+
+N:794:& Copper Rod~ of#
+G:-:s
+I:67:20:0
+W:15:0:15:200
+A:15/1
+P:0:1d1:0:0:0
+D:This is the common rod of the dwarves. It is created by chanting incessantly for 48 hours.
+
+N:795:& Iron Rod~ of#
+G:-:D
+I:67:50:0
+W:20:0:15:500
+A:20/1
+P:0:1d1:0:0:0
+D:This is the better version of the copper rod. It has been forged with iron that comes from
+D:the greatest deeps, where even the dwarves fear to go.
+
+# Aluminium only exists since 30 years
+N:796:& Moonstone Rod~ of#
+G:-:U
+I:67:75:0
+W:25:0:15:750
+A:25/1
+P:0:1d1:0:0:0
+D:This rod has been fashioned from moonrock. Its alien nature gives it quite a bit of capacity.
+
+N:797:& Silver Rod~ of#
+G:-:s
+I:67:100:0
+W:30:0:15:1000
+A:30/1
+P:0:1d1:0:0:0
+D:This rod is used often used by court mages. Its creation costs an insane amount
+D:of gold but it is still lesser than a Golden Rod.
+
+N:798:& Golden Rod~ of#
+G:-:y
+I:67:125:0
+W:40:0:15:1250
+A:40/2
+P:0:1d1:0:0:0
+D:These rods are rare, since finding gold that can withstand the great magic
+D:capacity that this rod holds is very difficult.
+
+N:799:& Mithril Rod~ of#
+G:-:B
+I:67:160:0
+W:50:0:15:1600
+A:50/5
+P:0:1d1:0:0:0
+D:The mithril of this rod comes from very deep, too deep. Unknown powers have been
+D:infused in this rod, making it the second most powerful type of rod on Middle-earth.
+
+N:800:& Adamantite Rod~ of#
+G:-:v
+I:67:200:0
+W:60:0:15:2000
+A:60/10
+P:0:1d1:0:0:0
+D:This is the rarest and most powerful kind of rod there is. Treasure it!
+
+# Greater Ration of Health - artifact food item (!)
+N:801:& Greater Ration~ of Health
+G:,:g
+I:80:41:0
+W:90:5:2:60000
+A:90/50
+T:80:40
+F:NORM_ART | FULL_NAME
+D:This food will, once eaten, permanently add 70 HP.
+
+# Scroll of Mass Resurrection - artifact scroll
+N:802:& Crumpled Scroll~ of Mass Resurrection
+G:?:d
+I:70:43:0
+W:55:0:5:0
+A:55/1
+T:70:1
+F:NORM_ART | FULL_NAME
+F:IGNORE_FIRE | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC
+D:This magical scroll sends a call to the halls of Mandos, issuing forth all those who have been
+D:slain by the reader.
+
+### Axes ###
+
+N:803:& Cleaver~
+G:/:s
+I:24:2:0
+W:13:0:110:175
+A:13/1
+P:0:2d4:0:0:0
+F:SHOW_MODS
+D:A small axe with a heavy rectangular blade.
+
+N:804:& Light War Axe~
+G:/:s
+I:24:8:0
+W:16:0:140:300
+A:16/1
+P:0:2d5:0:0:0
+F:SHOW_MODS
+D:A broad-bladed axe, suited for battle.
+
+N:805:& Slaughter Axe~
+G:/:G
+I:24:30:0
+W:70:0:300:6000
+A:70/8
+P:0:5d7:0:0:0
+F:SLAY_ANIMAL | SHOW_MODS
+D:A huge axe, the sort used for slaughtering animals, this weapon is unusually deadly against
+D:natural creatures.
+
+N:806:& Runestone~
+G:?:v
+I:105:255:0
+W:10:5:2:300
+A:10/3:20/2:30/1:60/1
+P:0:1d1:0:0:0
+F:IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | IGNORE_ACID
+D:A small oval stone. One surface is flat, as if something ought to be scratched or inscribed into
+D:it.
+
+N:807:& Fortune cookie~
+G:,:U
+I:80:42:500
+W:0:0:2:10
+A:0/1:5/1:10/1
+D:A small, sweet bakery product, with a scrap of paper inside.
+
+# XXX was the Portable Hole
+
+N:809:Critical Hits
+G:=:d
+I:45:59:0
+W:50:0:2:10000
+A:50/3
+F:CRIT
+f:CRIT
+D:A magical ring to make it likelier for the wearer to hit exceptionally heavily.
+
+# The Wand of Stone to Mud of Thrain
+N:810:& Wand~ of Digging of Thrain
+G:-:d
+I:65:26:-1:SPELL=Dig
+W:10:10:10:3200
+A:10/200
+P:0:10d10:0:0:0
+T:65:6
+F:RECHARGE | SPECIAL_GENE | EASY_USE | RECHARGED | NORM_ART | FULL_NAME
+D:The miner's friend. This wand was used by Thrain to dig into the
+D:walls of the dungeon. Its indestructible nature makes it quite useful.
+
+# The Staff of Holy Fire of Mithrandir
+N:811:& Gnarled Staff~ of Holy Fire of Mithrandir
+G:_:d
+I:55:22:-1:SPELL=Holy Fire of Mithrandir
+W:50:10:10:12000
+P:0:10d4:0:0:0
+A:50/200
+T:55:8
+F:RECHARGE | EASY_USE | RECHARGED | NORM_ART | FULL_NAME
+D:Mithrandir's staff that throws powerful fire attacks at all enemies. It
+D:can be recharged without blowing up, for it is built to hold
+D:much magical energy.
+
+### Summoner totems ###
+
+N:812:Partial Totem
+G:":v
+I:54:1:0
+W:10:10:10:120
+P:0:1d1:0:0:0
+A:10/200
+F:SPECIAL_GENE
+D:An item which a Summoner can use to animate a copy of a creature that
+D:only exists by the will of its master.
+
+N:813:True Totem
+G:":v
+I:54:2:0
+W:10:10:10:120
+P:0:1d1:0:0:0
+A:10/200
+F:SPECIAL_GENE
+D:An item which a Summoner can use to revive a true copy of a creature.
+
+### Holy relics for God Quests ###
+
+N:814:& Piece~ of the Relic of Eru
+G:~:v
+I:11:7:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | IGNORE_ACID
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to Priests of Eru. The relic now lies in pieces, hidden
+D:from all but but his most dedicated followers.
+
+N:815:& Piece~ of the Relic of Manwe
+G:~:v
+I:11:8:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | IGNORE_ACID
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to Priests of Manwe. The relic now lies in pieces, hidden
+D:from all but his most dedicated followers.
+
+N:816:& Piece~ of the Relic of Tulkas
+G:~:v
+I:11:9:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | IGNORE_ACID
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to worshippers of Tulkas. The relic now lies in pieces,
+D:hidden from all but his most dedicated followers.
+
+N:817:& Piece~ of the Relic of Melkor
+G:~:v
+I:11:10:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | IGNORE_ACID
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to Dark Priests. The relic now lies in pieces, hidden
+D:from all but the most faithful followers of Melkor.
+
+N:818:& Piece~ of the Relic of Yavanna
+G:~:v
+I:11:11:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | IGNORE_ACID
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to Druids. The relic now lies in pieces, hidden
+D:from all but the most faithful followers of Yavanna.
diff --git a/lib/edit/maeglin.map b/lib/edit/maeglin.map
new file mode 100644
index 00000000..a230c68a
--- /dev/null
+++ b/lib/edit/maeglin.map
@@ -0,0 +1,85 @@
+# Created by Mynstral (mynstral@thehelm.com)
+# Made for PernAngband on 26/07/2001
+
+# Monsters starts awake
+N:0
+
+# Permanent wall
+F:X:63:3
+
+# Marker
+F:<:172:3
+
+# up stairs with maeglin
+F:{:6:3:825
+
+# Floor with dirt
+F:.:88:5
+
+# Floor with dirt with an Ettin
+F:e:88:5:621
+
+# Floor with dirt with a War Troll
+F:w:88:5:631
+
+# Floor with dirt with a Troll Chieftan
+F:t:88:5:799
+
+# Floor with dirt with a Snagga sapper
+F:s:88:5:251
+
+# Floor with dirt with an Orc Captain
+F:o:88:5:285
+
+# Floor with dirt with an Elite Uruk
+F:u:88:5:866
+
+# Floor with dirt with an Ancient blue dragon
+F:L:88:5:601
+
+# Floor with dirt with an Ancient bronze dragon
+F:Z:88:5:602
+
+# Floor with dirt with an Ancient white dragon
+F:W:88:5:617
+
+# Floor with dirt with an Ancient green dragon
+F:G:88:5:618
+
+# Floor with dirt with an Ancient black dragon
+F:B:88:5:624
+
+# Floor with dirt with an Ancient red dragon
+F:R:88:5:644
+
+# Floor with dirt with an Ancient gold dragon
+F:O:88:5:645
+
+# Floor with dirt with a Lesser Balrog
+F:U:88:5:996
+
+# Granite wall
+F:#:56:5
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X##..#########...#........###..R.....###.e...{X
+D:X#.wt.######..O....#s..#..##w.#..###.....#.t..X
+D:X#wBLe.####.U.###....##.U.#...##..u####.#####.X
+D:X#eRe.####..#..######.B...w...#.U#uuu##.#U...eX
+D:X##...#...G###s...##....##..####..##uu#B..##..X
+D:X####...e...#####.t...####...####.###u#####.w#X
+D:X###....##.w.######.e.#..##...###..##uu#e...##X
+D:X##..W#####...###.....#...##.e####.L##...#####X
+D:X##...######e.##Z..w.###.......####.###t.#####X
+D:X....########s....#s...##R..#.U.##..####..####X
+D:Xs..####....##.tU####...#####t...#.######..###X
+D:X#...##..##..#..######.t..e..##.w...#####w.###X
+D:X##.....####s..######.e.#.#t...#.#U.####..####X
+D:X#...####uu##.t..#####...e####s#........w.####X
+D:X...####uou...##...#####.....###..#####...####X
+D:X..#####uu##...###s.##........##..#####..#####X
+D:X.o.#######...e.###.....o.###s.##..###s...#..#X
+D:X#s..o#......#s.......u...####..#s.##...o.G...X
+D:Xsss##o....u###....u..o.o.#####........###.OZ.X
+D:X<ss...###############...##################U.#X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
diff --git a/lib/edit/misc.txt b/lib/edit/misc.txt
new file mode 100644
index 00000000..08e35c1f
--- /dev/null
+++ b/lib/edit/misc.txt
@@ -0,0 +1,91 @@
+# File: misc.txt
+
+# Maximum number of towns
+M:T:100
+
+# Maximum number of non random towns(must be < 20)
+M:t:5
+
+# Maximum x size of the wilderness
+M:X:101
+
+# Maximum y size of the wilderness
+M:Y:66
+
+# Maximum number of randart parts in ra_info.txt
+M:Z:516
+
+# Maximum number of monsters in r_info.txt
+# WARNING ! add one more to the real count for the player ghost !!
+M:R:1078
+
+# Maximum number of monsters in re_info.txt
+# WARNING ! Use the exact amount of ego types used, if not you
+# will get weird results !
+M:r:14
+
+# Maximum number of items in k_info.txt
+M:K:819
+
+# Maximum number of vaults in v_info.txt
+M:V:108
+
+# Maximum number of terrain features in f_info.txt
+M:F:256
+
+# Maximum number of alchemist recipes
+M:a:1000
+
+# Maximum number of artifacts in a_info.txt
+M:A:219
+
+# Maximum number of sets types in set_info.txt
+M:s:10
+
+# Maximum number of ego-items in e_info.txt
+M:E:200
+
+# Maximum number of dungeon types in d_info.txt
+M:D:31
+
+# Maximum number of trap types in tr_info.txt
+M:U:176
+
+# Maximum number of terrain types in wf_info.txt
+M:W:30
+
+# Maximum number of owners types in ow_info.txt
+M:N:70
+
+# Maximum number of building actions in ba_info.txt
+M:B:62
+
+# Maximum number of store types in st_info.txt
+M:S:61
+
+# Maximum size for "o_list[]"
+M:O:1024
+
+# Maximum size for "m_list[]"
+M:M:768
+
+# Maximum number of race types in p_info.txt
+M:P:R:22
+
+# Maximum number of subrace types in p_info.txt
+M:P:S:10
+
+# Maximum number of class types in p_info.txt
+M:P:C:32
+
+# Maximum number of meta class types in p_info.txt
+M:P:M:1
+
+# Maximum number of histories types in p_info.txt
+M:P:H:266
+
+# Maximum number of skills in s_info.txt
+M:k:60
+
+# Maximum number of traits in ab_info.txt
+M:b:50
diff --git a/lib/edit/nirnaeth.map b/lib/edit/nirnaeth.map
new file mode 100644
index 00000000..2ce0e886
--- /dev/null
+++ b/lib/edit/nirnaeth.map
@@ -0,0 +1,64 @@
+# Permanent wall
+F:X:63:3
+
+# up stairs
+F:<:6:3
+
+# Floor with dirt
+F:.:1:5
+
+# Dirt (no-tele)
+F:s:88:5
+
+# Shallow water
+F:V:84:5
+
+# Dirt with Olog
+F:a:88:5:538
+
+# Dirt with Cave Troll
+F:b:88:5:496
+
+# Dirt with with Eldrak
+F:c:88:1:620
+
+# Dirt with with Ettin
+F:e:88:1:621
+
+# Dirt with with War troll
+F:f:88:1:631
+
+# Dirt with with Hru
+F:g:88:1:709
+
+# Dirt with Ulik the Troll
+F:h:88:5:729
+
+# Dirt with Ancient green dragon
+F:i:88:5:618
+
+# Dungeon
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X...a.bab....f.....f.XXXXXXXXXXX
+D:X..ab...b..ff..aa..b..XXXXXXXXXX
+D:X.aa..b..fff...a..g.f.b.XXXXXXXX
+D:Xab.....b....f.......f..bf..XXXX
+D:X..b.f......ff.f..aa...f..b.XXXX
+D:X...ff..f..ff.c.a..f.g.a..fXXXXX
+D:Xb.f....fff.ff...aaa....f..XXXXX
+D:X...ff...ff...f......aa..a..XXXX
+D:Xb.ff.a...f...bbb..a..aa.....XXX
+D:X.f.a...a....bbebb......f.fa.XXX
+D:X.....a.....bbecebb..g.a.a....XX
+D:X.a.a....aa.beccceb....f.a..c.XX
+D:X......a..f.bbecebba.f....ea...X
+D:XX.b.aa.f..f.bbebb...a.aa...g.aX
+D:XX..bb...a..f.bbb..a..a..aae..iX
+D:XXX....aa.aa.f...aa...e..b..h..X
+D:XXXXXX........bb...g....aa...gcX
+D:XXXXXXXX.g..aa...a...a.e...ac.<X
+D:XXXXXXXX........bb....e.c.i.e.iX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:3
diff --git a/lib/edit/numenor.txt b/lib/edit/numenor.txt
new file mode 100644
index 00000000..ec8621b1
--- /dev/null
+++ b/lib/edit/numenor.txt
@@ -0,0 +1,80 @@
+# File: numenor.txt
+
+# Way to the lost land of Numenor
+F:>:7:3:0:0:0:0:0:7
+
+############### Town Layout ###############
+
+D:######################################################################################################################################################################################################
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW>VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:######################################################################################################################################################################################################
+
+
+############### Starting positions ###############
+
+# Standard starting position for normal races
+?:[EQU $LEAVING_QUEST 0]
+P:46:128
diff --git a/lib/edit/ow_info.txt b/lib/edit/ow_info.txt
new file mode 100644
index 00000000..a1e3d0a3
--- /dev/null
+++ b/lib/edit/ow_info.txt
@@ -0,0 +1,447 @@
+# File: ow_info.txt
+
+
+# This file is used to initialize the "lib/raw/ow_info.raw" file, which is
+# used to initialize the "owner info type" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# N:<index>:<name>
+# I:<max_cost>:<max_inflate>:<min_inflate>:<haggle_per>:<insult_max>
+# C:<hated cost>:<normal cost>:<liked cost>
+# L:liked races
+# H:hated races
+
+# Version stamp (required)
+
+V:2.0.0
+
+N:0:Bilbo the Friendly(Hobbit)
+I:20000:170:108:5:15
+C:120:100:80
+L:Elf | Half-Elf | High-Elf | Dunadan | Hobbit | Dwarf | RohanKnight
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold
+
+N:1:Uldrik(Human)
+I:20000:170:108:1:1
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:2:Otick(Human)
+I:100:170:108:4:10
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:3:Merana(Human)
+I:0:170:108:1:1
+C:200:100:95
+L:Human
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:4:Mirimbar(High-Elf)
+I:0:170:108:1:1
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:5:Raistlin the Chicken(Human)
+I:20000:175:108:4:12
+C:120:100:80
+L:Human
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:6:Sultan the Midget(Gnome)
+I:30000:170:107:5:15
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:7:Lyar-el the Comely(Elf)
+I:30000:165:107:6:18
+C:120:100:80
+L:Elf | Half-Elf | Dark-Elf | High-Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:8:Kon-Dar the Ugly(Half-Orc)
+I:5000:210:115:5:7
+C:120:100:80
+L:Orc | Troll | Half-Ogre | Beorning | Kobold
+H:Gnome | Dwarf | Human | RohanKnight | Elf | Half-Elf | High-Elf
+
+N:9:Darg-Low the Grim(Human)
+I:10000:190:111:4:9
+C:120:100:80
+L:Human
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:10:Decado the Handsome(Dunadan)
+I:25000:200:112:4:10
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:11:Wieland the Smith(Dwarf)
+I:30000:200:112:4:5
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:12:Arnold the Beastly(Barbarian)
+I:5000:210:115:6:6
+C:120:100:80
+
+N:13:Arndal Beast-Slayer(Half-Elf)
+I:10000:185:110:5:9
+C:120:100:80
+L:Elf | Half-Elf | Dark-Elf | High-Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:14:Eddie Beast-Master(Half-Orc)
+I:25000:190:115:5:7
+C:120:100:80
+L:Orc | Troll | Half-Ogre | Beorning | Kobold
+H:Gnome | Dwarf | Human | RohanKnight | Elf | Half-Elf | High-Elf
+
+N:15:Oglign Dragon-Slayer(Dwarf)
+I:30000:195:112:4:8
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:16:Aragorn(Dunadan)
+I:20000:200:112:4:10
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:17:Sondar(Human)
+I:0:200:112:4:10
+C:120:100:80
+
+N:18:Celebor(Half-Elf)
+I:100:170:108:4:10
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:19:Sharra(Human)
+I:25000:200:112:4:10
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:20:Hjolgar(Barbarian)
+I:5000:210:115:6:6
+C:120:100:80
+#L:Warrior |
+
+N:21:Tanistil(Elf)
+I:5000:210:115:6:6
+C:120:100:80
+#L:Mage | Sorceror | Thaumaturgist
+#H:Warrior |
+
+N:22:Eldore(Human)
+I:5000:210:115:6:6
+C:120:100:80
+#L:Priest
+#H:Necromancer
+
+N:23:Vilios(Human)
+I:5000:210:115:6:6
+C:120:100:80
+#L:Paladin
+#H:Necromancer
+
+N:24:Angros(Elf)
+I:5000:210:115:6:6
+C:120:100:80
+#L:Ranger
+
+N:25:Palano(Thunderlord)
+I:0:210:115:6:6
+C:120:100:80
+L:Thunderlord
+
+N:26:Ludwig the Humble(Dwarf)
+I:5000:175:109:6:15
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:27:Gunnar the Paladin(Half-Troll)
+I:10000:185:110:5:23
+C:120:100:80
+L:Orc | Troll | Half-Ogre | Beorning | Kobold
+H:Gnome | Dwarf | Human | RohanKnight | Elf | Half-Elf | High-Elf
+
+N:28:Torin the Chosen(High-Elf)
+I:25000:180:107:6:20
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:29:Sarastro the Wise(Human)
+I:30000:185:109:5:15
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:30:Mauser the Chemist(Half-Elf)
+I:10000:190:111:5:8
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:31:Wizzle the Chaotic(Hobbit)
+I:10000:190:110:6:8
+C:120:100:80
+L:Elf | Half-Elf | High-Elf | Dunadan | Hobbit | Dwarf | RohanKnight
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:32:Midas the Greedy(Gnome)
+I:15000:200:116:6:9
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:33:Ja-Far the Alchemist(Elf)
+I:15000:220:111:4:9
+C:120:100:80
+L:Elf | Half-Elf | Dark-Elf | High-Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:34:Ariel the Sorceress(Half-Elf)
+I:20000:200:110:7:8
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:35:Buggerby the Great(Gnome)
+I:20000:215:113:6:10
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:36:Inglorian the Mage(Human)
+I:30000:200:110:7:10
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:37:Luthien Starshine(High-Elf)
+I:30000:175:110:5:11
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:38:Gary Gygaz(Half-Troll)
+I:20000:250:150:10:5
+C:120:100:80
+#L:Rogue
+H:Gnome | Dwarf | Human | RohanKnight | Elf | Half-Elf | High-Elf
+
+N:39:Histor the Goblin(Half-Orc)
+I:20000:250:150:10:5
+C:120:100:80
+#L:Rogue
+H:Gnome | Dwarf | Human | RohanKnight | Elf | Half-Elf | High-Elf
+
+N:40:Zorak the Smart(Dwarf)
+I:30000:250:150:10:5
+C:120:100:80
+#L:Rogue
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:41:Tipo the Fair(Human)
+I:30000:250:150:10:5
+C:120:100:80
+#L:Rogue
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:42:Dolaf the Greedy(Human)
+I:10000:175:108:4:12
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:43:Odnar the Sage(High-Elf)
+I:15000:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:44:Gandar the Neutral(Dark-Elf)
+I:25000:120:110:7:19
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold
+
+N:45:Ro-sha the Patient(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+L:Elf | Half-Elf | Dark-Elf | High-Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:46:N'rak the Summoner(Human)
+I:10000:175:108:4:12
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:47:Esperion the Beastlover(High-Elf)
+I:15000:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:48:Flarim the Shopkeeper(Dunadan)
+I:25000:120:110:7:19
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:49:Tril-akheb the Supreme(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+L:Elf | Half-Elf | Dark-Elf | High-Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:50:Dorchel(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+L:Elf | Half-Elf | Dark-Elf | High-Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:51:Galadriel(High-Elf)
+I:15000:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:52:Celeborn(High-Elf)
+I:15000:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:53:Aulendil(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+#L:Warrior |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:54:Valceronwe(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+#L:Mage | Thaumaturgist | Sorceror
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:55:Voronwe(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+#L:Priest | Paladin
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:56:Celegail(Elf)
+I:30000:140:105:6:12
+C:120:100:80
+#L:Ranger
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:57:Turgon(High-Elf)
+I:30000:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:58:Pengolodh(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:59:Aerandir(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:60:Celebrimbor(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+#L:Warrior |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:61:Lomelosse(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:62:Arlindel(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+#L:Harper | Ranger
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:63:Sulraen(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+#L:Mage | Sorceror
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:64:Firiel(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:65:Earendur(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:66:Glorfindel(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf
+#L:Ranger
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:67:Ecthelion(High-Elf)
+I:0:120:105:6:16
+C:120:100:80
+L:High-Elf | Half-Elf
+#L:Paladin
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:68:Kanris(Human)
+I:5000:210:115:6:6
+C:120:100:80
+#L:Merchant
+#H:Rogue
+
+N:69:Barliman Butterbur(Human)
+I:100:170:108:4:10
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
diff --git a/lib/edit/p_info.txt b/lib/edit/p_info.txt
new file mode 100644
index 00000000..1531f4fd
--- /dev/null
+++ b/lib/edit/p_info.txt
@@ -0,0 +1,1974 @@
+# File: p_info.txt
+
+
+# This file is used to initialize the "lib/raw/p_info.raw" file, which is
+# used to initialize the "player race/race mod/class" information for the
+# Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+V:2.0.0
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# General skills, that everybody starts with
+# G:k:value:modifier:skill name
+G:k:+0:+500:Monster-lore
+G:k:+1000:+0:Spell-learning
+G:k:+0:+500:Prayer
+G:k:+0:+400:Udun
+G:k:+1000:+1000:Magic-Device
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# C:N:index:name
+# C:D:0:class desc
+# C:D:1:titles
+# C:S:str:int:wis:dex:con:chr:mana:bonus blows
+# C:K:dis:dev:sav:stl:srh:fos:thn:thb
+# C:X:dis:dev:sav:stl:srh:fos:thn:thb
+# C:P:hitdie:xp%
+# C:B:num:wgt:mul
+# C:C:(H|L):(H|L):base:pl:plus
+# first (H|L) is for weapons/.. second is for magic things
+# H = heavy sensing, L = light sensing; result(lower = better) = base / (pl * plev + plus)
+# C:G:class flags
+# C:R:level:pval
+# C:F:flags
+# C:Z:power
+# C:E:weapons:torso:arms:finger:head:legs
+# C:O:tval:sval:xdy
+# C:k:value:modifier:skill name
+# C:b:level:ability
+
+# Specialities, autoskiller
+# C:a:N:Name
+# C:a:D:Desc
+# C:a:K:lvl 50 skill value:skill name
+# C:a:k:value:mod:skill name
+# C:a:b:level:ability
+# C:a:O:tval:sval:xdy
+
+C:N:0:Warrior
+C:D:0:Simple fighters, they hack away with their trusty weapon.
+C:D:1:Rookie
+C:D:1:Soldier
+C:D:1:Mercenary
+C:D:1:Veteran
+C:D:1:Swordsman
+C:D:1:Champion
+C:D:1:Hero
+C:D:1:Baron
+C:D:1:Duke
+C:D:1:Lord
+C:S:5:-2:-2:2:2:-1:0:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:4:30:5
+C:C:H:L:9000:2:40
+C:P:9:0
+C:R:30:0
+C:F:RES_FEAR
+C:E:0:0:0:0:0:0
+C:O:45:38:1d1
+C:O:37:4:1d1
+C:k:+2000:+800:Combat
+C:k:+1000:+850:Weaponmastery
+C:k:+0:+400:Sword-mastery
+C:k:+0:+400:Axe-mastery
+C:k:+0:+400:Hafted-mastery
+C:k:+0:+400:Polearm-mastery
+C:k:+1000:+600:Archery
+C:k:+1000:+900:Sneakiness
+C:k:+0000:+400:Stealth
+C:k:+1000:+900:Disarming
+C:k:+1000:+300:Magic
+C:k:+1000:+400:Spirituality
+C:k:+0:+550:Antimagic
+C:k:+0:+150:Magic-Device
+C:b:25:Spread blows
+C:b:1:Extra Max Blow(1)
+C:b:1:Extra Max Blow(2)
+
+# Specialities, autoskiller
+C:a:N:Warrior
+C:a:D:Simple fighters, they hack away with their trusty weapon.
+C:a:O:23:16:1d1
+C:a:g:All Gods
+
+C:a:N:Swordmaster
+C:a:D:Fighters specialised in the use of swords
+C:a:k:+1000:+300:Sword-mastery
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+100:Combat
+C:a:k:+0:-100:Axe-mastery
+C:a:k:+0:-100:Hafted-mastery
+C:a:k:+0:-100:Polearm-mastery
+C:a:O:23:16:1d1
+C:a:g:Nobody
+C:a:g:Manwe Sulimo
+C:a:g:Tulkas
+C:a:g:Melkor Bauglir
+C:a:g:Yavanna Kementari
+
+C:a:N:Axemaster
+C:a:D:Fighters specialised in the use of axes
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+100:Combat
+C:a:k:+1000:+300:Axe-mastery
+C:a:k:+0:-100:Sword-mastery
+C:a:k:+0:-100:Hafted-mastery
+C:a:k:+0:-100:Polearm-mastery
+C:a:O:24:1:1d1
+C:a:g:Nobody
+C:a:g:Manwe Sulimo
+C:a:g:Tulkas
+C:a:g:Melkor Bauglir
+C:a:g:Yavanna Kementari
+
+C:a:N:Haftedmaster
+C:a:D:Fighters specialised in the use of hafted weapons
+C:a:k:+1000:+300:Hafted-mastery
+C:a:k:+0:+500:Stunning-blows
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+100:Combat
+C:a:k:+0:-100:Axe-mastery
+C:a:k:+0:-100:Sword-mastery
+C:a:k:+0:-100:Polearm-mastery
+C:a:O:21:13:1d1
+C:a:g:All Gods
+
+C:a:N:Polearmmaster
+C:a:D:Fighters specialised in the use of polearms
+C:a:k:+1000:+300:Polearm-mastery
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+100:Combat
+C:a:k:+0:-100:Axe-mastery
+C:a:k:+0:-100:Hafted-mastery
+C:a:k:+0:-100:Sword-mastery
+C:a:O:22:8:1d1
+C:a:g:All Gods
+
+C:a:N:Unbeliever
+C:a:D:They don't believe in magic and can even prevent its usage around them
+C:a:k:=0:=0:Prayer
+C:a:k:=0:=0:Magic
+C:a:k:=0:=0:Magic-Device
+C:a:k:=0:=0:Spirituality
+C:a:k:+1000:+100:Antimagic
+C:a:O:23:33:1d1
+C:a:g:Nobody
+
+C:a:N:Demonologist
+C:a:D:Masters of the school of demonology, they are trained in both melee
+C:a:D:fighting and using demon spells to enhance their combat potential.
+C:a:O:115:55:1d1
+C:a:k:+0:-50:Combat
+C:a:k:+0:-100:Weaponmastery
+C:a:k:+0:+200:Sword-mastery
+C:a:k:=0:=0:Axe-mastery
+C:a:k:=0:=0:Hafted-mastery
+C:a:k:=0:=0:Polearm-mastery
+C:a:k:+1000:-200:Archery
+C:a:k:+1000:+900:Sneakiness
+C:a:k:+1000:+900:Disarming
+C:a:k:+1000:+400:Magic
+C:a:k:+1000:+300:Spirituality
+C:a:k:=0:=0:Antimagic
+C:a:k:+1000:+1000:Demonology
+C:a:g:Nobody
+C:a:g:Manwe Sulimo
+C:a:g:Tulkas
+C:a:g:Melkor Bauglir
+
+C:N:1:Mage
+C:D:0:The basic spellcaster with lots of different skills
+C:D:1:Apprentice
+C:D:1:Trickster
+C:D:1:Illusionist
+C:D:1:Spellbinder
+C:D:1:Evoker
+C:D:1:Conjurer
+C:D:1:Warlock
+C:D:1:Sorcerer
+C:D:1:Ipsissimus
+C:D:1:Archimage
+C:S:-5:3:0:1:-2:1:50:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:4:40:2
+C:C:L:H:240000:1:5
+C:P:0:30
+C:E:0:0:0:0:0:0
+C:k:+1000:+900:Magic
+C:k:+0:+200:Magic-Device
+C:k:+0:+600:Spell-power
+C:k:+1000:+600:Mana
+C:k:+0:+700:Fire
+C:k:+0:+700:Water
+C:k:+0:+700:Air
+C:k:+0:+700:Earth
+C:k:+0:+700:Temporal
+C:k:+0:+700:Divination
+C:k:+0:+700:Conveyance
+C:k:+0:+700:Nature
+C:k:+0:+700:Meta
+C:k:+0:+700:Mind
+C:k:+0:+700:Necromancy
+C:k:+0:+700:Runecraft
+C:k:+0:+700:Thaumaturgy
+C:k:+1000:+550:Spirituality
+C:k:+1000:+200:Combat
+C:k:+700:+500:Weaponmastery
+C:k:+1000:+900:Sneakiness
+C:k:+0000:+400:Stealth
+C:b:1:Perfect casting
+C:g:All Gods
+
+C:a:N:Mage
+C:a:D:The basic unspecialized warrior-spellcaster
+C:a:k:+0:+300:Combat
+C:a:k:+0:+200:Sorcery
+C:a:k:+0:+300:Mana
+C:a:k:+0:+200:Fire
+C:a:k:+0:+200:Water
+C:a:k:+0:+200:Air
+C:a:k:+0:+200:Earth
+C:a:k:+0:+200:Conveyance
+C:a:k:+0:+200:Nature
+C:a:k:+0:+200:Temporal
+C:a:k:+0:+200:Divination
+C:a:k:+0:+200:Meta
+C:a:k:+0:+200:Mind
+C:a:O:23:4:1d1
+C:a:O:111:50:1d1
+
+C:a:N:Geomancer
+C:a:D:The master of the four elements
+C:a:k:+0:-150:Magic-Device
+C:a:k:+1000:+100:Spell-power
+C:a:k:-1000:-600:Mana
+C:a:k:+1000:+700:Geomancy
+C:a:k:+1000:+350:Fire
+C:a:k:+1000:+350:Water
+C:a:k:+1000:+350:Air
+C:a:k:+1000:+350:Earth
+C:a:k:+0:-100:Weaponmastery
+C:a:O:6:1:1d1
+
+C:a:N:Warper
+C:a:D:The master of space and time
+C:a:k:+0:-150:Magic-Device
+C:a:k:+1000:+100:Spell-power
+C:a:k:+0:+100:Mana
+C:a:k:+0:+100:Fire
+C:a:k:+0:+100:Water
+C:a:k:+0:+100:Air
+C:a:k:+0:+100:Earth
+C:a:k:+0:+500:Conveyance
+C:a:k:+0:+100:Nature
+C:a:k:+0:+500:Temporal
+C:a:k:+0:+500:Divination
+C:a:k:+0:+100:Meta
+C:a:k:+0:-100:Weaponmastery
+C:a:O:23:4:1d1
+C:a:O:111:50:1d1
+
+C:a:N:Sorceror
+C:a:D:The master of all magic schools
+C:a:k:+0:-200:Magic-Device
+C:a:k:=0:=0:Weaponmastery
+C:a:k:=0:=0:Combat
+C:a:k:+1000:+700:Sorcery
+C:a:k:+0:+100:Magic
+C:a:k:-1000:+300:Mana
+C:a:k:+0:+300:Fire
+C:a:k:+0:+300:Water
+C:a:k:+0:+300:Air
+C:a:k:+0:+300:Earth
+C:a:k:+0:+300:Conveyance
+C:a:k:+0:+300:Nature
+C:a:k:+0:+300:Temporal
+C:a:k:+0:+300:Divination
+C:a:k:+0:+300:Meta
+C:a:k:+0:+300:Mind
+C:a:k:+0:+200:Necromancy
+C:a:k:+0:+200:Runecraft
+C:a:k:+0:+200:Thaumaturgy
+C:a:O:36:2:1d1
+C:a:O:111:50:1d1
+
+C:a:N:Necromancer
+C:a:D:The master of death, and undeath
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+200:Combat
+C:a:k:+1000:+300:Necromancy
+C:a:k:-1000:+0:Mana
+C:a:k:+0:+100:Fire
+C:a:k:+0:+100:Earth
+C:a:k:+0:-200:Nature
+C:a:k:+0:+100:Temporal
+C:a:k:+0:+200:Mind
+C:a:k:+1000:+600:Monster-lore
+C:a:k:+5000:+900:Corpse-preservation
+C:a:O:23:4:1d1
+C:a:O:111:50:1d1
+C:a:b:25:Undead Form
+
+C:a:N:Runecrafter
+C:a:D:Runecrafters use the runes found in Middle-earth to create
+C:a:D:finely tuned spells for each specific situation.
+C:a:k:+1000:+50:Magic
+C:a:k:+1000:+300:Runecraft
+C:a:k:+0:-100:Weaponmastery
+C:a:O:111:50:1d1
+C:a:O:105:1:1d1
+C:a:O:104:5:1d1
+C:a:O:23:4:1d1
+
+C:a:N:Thaumaturgist
+C:a:D:Thaumaturgy spells come from within and are different for each character.
+C:a:D:Since attack is the best defence, all their spells are offensive.
+C:a:k:-1000:+0:Mana
+C:a:k:+2000:+50:Magic
+C:a:k:+1000:+300:Thaumaturgy
+C:a:k:+0:-100:Weaponmastery
+C:a:k:+0:-150:Magic-Device
+C:a:O:23:4:1d1
+C:a:O:111:50:1d1
+
+C:a:N:Alchemist
+C:a:D:Alchemists can quickly create powerful magic items through the correct use
+C:a:D:of the essences of magic they can extract from magical objects.
+C:a:k:+2000:+0:Magic
+C:a:k:+0:-600:Spell-power
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:-600:Thaumaturgy
+C:a:k:+0:+250:Spirituality
+C:a:k:+0:+500:Combat
+C:a:k:+0:+200:Weaponmastery
+C:a:k:-1000:-600:Mana
+C:a:k:+0:-600:Fire
+C:a:k:+0:-600:Water
+C:a:k:+0:-600:Air
+C:a:k:+0:-600:Earth
+C:a:k:+0:-600:Conveyance
+C:a:k:+0:-600:Nature
+C:a:k:+0:-600:Temporal
+C:a:k:+0:-200:Divination
+C:a:k:+0:-200:Meta
+C:a:k:+0:-600:Mind
+C:a:k:+1000:+800:Alchemy
+C:a:k:+0:+50:Magic-Device
+C:a:O:31:1:1d1
+C:a:O:4:2:6d1
+C:a:O:2:1:1d1
+C:a:O:23:4:1d1
+C:a:G:EXPERIMENTAL
+
+C:N:2:Archer
+C:D:0:'Kill them before they see you' could be the motto of the archer class.
+C:D:0:As deadly with a bow as a warrior is with a sword.
+C:D:1:Rock Thrower
+C:D:1:Slinger
+C:D:1:Great Slinger
+C:D:1:Tosser
+C:D:1:Bowman
+C:D:1:Great Bowman
+C:D:1:Great Bowman
+C:D:1:Archer
+C:D:1:Archer
+C:D:1:Great Archer
+C:S:2:1:0:2:1:1:0:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:4:35:4
+C:C:H:L:9000:2:40
+C:P:4:30
+C:E:0:0:0:0:0:0
+C:k:+1000:+800:Combat
+C:k:+1000:+500:Weaponmastery
+C:k:+1000:+750:Archery
+C:k:+0:+300:Bow-mastery
+C:k:+0:+300:Crossbow-mastery
+C:k:+0:+300:Sling-mastery
+C:k:+0:+300:Boomerang-mastery
+C:k:+0:%150:Boulder-throwing
+C:k:+1000:+900:Sneakiness
+C:k:+0000:+400:Stealth
+C:k:+1000:+900:Disarming
+C:k:+1000:+300:Magic
+C:k:+0:+100:Magic-Device
+C:k:+1000:+400:Spirituality
+C:b:2:Ammo creation
+
+C:a:N:Archer
+C:a:D:'Kill them before they see you' could be the motto of the archer class.
+C:a:D:As deadly with a bow as a warrior is with a sword.
+C:a:k:+0:+100:Archery
+C:a:k:+0:+200:Bow-mastery
+C:a:k:+0:+200:Crossbow-mastery
+C:a:k:+0:+200:Sling-mastery
+C:a:k:+0:+200:Boomerang-mastery
+C:a:k:-1000:-100:Magic
+C:a:O:19:12:1d1
+C:a:O:19:2:1d1
+C:a:O:17:1:10d3
+C:a:O:17:1:10d3
+C:a:g:All Gods
+
+C:a:N:Ranger
+C:a:D:Rangers are capable archers but are also trained in hand to hand combat
+C:a:D:and nature/conveyance/divination magic schools
+C:a:k:+0:+400:Magic
+C:a:k:+0:+500:Nature
+C:a:k:+0:+500:Divination
+C:a:k:+0:+500:Conveyance
+C:a:k:+0:+700:Disarming
+C:a:k:+0:+50:Sneakiness
+C:a:k:+0:+200:Monster-lore
+C:a:O:19:12:1d1
+C:a:O:17:1:10d3
+C:a:O:23:10:1d1
+C:a:g:Nobody
+C:a:g:Manwe Sulimo
+C:a:g:Tulkas
+C:a:g:Yavanna Kementari
+
+C:N:3:Rogue
+C:D:0:Rogues are masters of tricks. They can steal from shops and monsters,
+C:D:0:and lure monsters into deadly monster traps.
+C:D:1:Cutpurse
+C:D:1:Robber
+C:D:1:Burglar
+C:D:1:Filcher
+C:D:1:Sharper
+C:D:1:Low Thief
+C:D:1:High Thief
+C:D:1:Master Thief
+C:D:1:Assassin
+C:D:1:Guildmaster
+C:S:2:1:-2:3:1:-1:0:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:4:30:3
+C:C:H:H:20000:2:40
+C:P:6:25
+C:O:23:4:1d1
+C:G:EASE_STEAL
+C:R:3:1
+C:F:CRIT
+C:R:6:1
+C:F:CRIT
+C:R:9:1
+C:F:CRIT
+C:R:12:1
+C:F:CRIT
+C:R:15:1
+C:F:CRIT
+C:R:18:1
+C:F:CRIT
+C:R:21:1
+C:F:CRIT
+C:R:24:1
+C:F:CRIT
+C:R:27:1
+C:F:CRIT
+C:R:30:1
+C:F:CRIT
+C:R:33:1
+C:F:CRIT
+C:R:36:1
+C:F:CRIT
+C:R:39:1
+C:F:CRIT
+C:R:42:1
+C:F:CRIT
+C:R:45:1
+C:F:CRIT
+C:R:48:1
+C:F:CRIT
+C:E:0:0:0:0:0:0
+C:k:+1000:+700:Combat
+C:k:+1000:+700:Weaponmastery
+C:k:+1000:+300:Sword-mastery
+C:k:+1000:+500:Critical-hits
+C:k:+1000:+700:Magic
+C:k:+0:+550:Magic-Device
+C:k:+0:+500:Conveyance
+C:k:+0:+500:Divination
+C:k:+0:+500:Temporal
+C:k:+1000:+700:Spirituality
+C:k:+1000:+2000:Sneakiness
+C:k:+1000:+1500:Stealth
+C:k:+1000:+2000:Disarming
+C:k:+1000:+1000:Backstab
+C:k:+1000:+2000:Stealing
+C:k:+1000:+2000:Dodging
+C:g:All Gods
+C:b:10:Extra Max Blow(1)
+
+C:a:N:Rogue
+C:a:D:Rogues are masters of tricks. They can steal from shops and monsters,
+C:a:D:and lure monsters into deadly monster traps.
+C:a:b:1:Trapping
+C:a:O:46:1:1d1
+
+C:a:N:Assassin
+C:a:D:Assassins are stealthy killers.
+C:a:k:+0:+100:Combat
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+300:Sword-mastery
+C:a:k:+1000:+300:Critical-hits
+C:a:k:+1000:+300:Boomerang-mastery
+C:a:k:+0:-500:Magic
+C:a:k:+0:-400:Conveyance
+C:a:k:+0:-400:Divination
+C:a:k:+0:-300:Temporal
+C:a:k:+0:+500:Stealth
+C:a:k:+0:-1000:Disarming
+C:a:k:+0:+1000:Backstab
+C:a:k:+0:-1800:Stealing
+C:a:k:+0:-800:Magic-Device
+
+C:N:4:Loremaster
+C:D:0:Loremasters are skilled in most combat and monster skills.
+C:D:1:Apprentice
+C:D:1:Apprentice
+C:D:1:Initiate
+C:D:1:Initiate
+C:D:1:Sage
+C:D:1:Sage
+C:D:1:Lorekeeper
+C:D:1:Lorekeeper
+C:D:1:Loremaster
+C:D:1:Loremaster
+C:S:1:-2:1:1:0:1:0:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:4:30:3
+C:C:H:L:9000:2:40
+C:P:8:40
+C:E:0:0:0:0:0:0
+C:k:+1000:+700:Combat
+C:k:+1000:+700:Weaponmastery
+C:k:+1000:+700:Archery
+C:k:+1000:+700:Barehand-combat
+C:k:+0:+600:Magic
+C:k:+1000:+700:Sneakiness
+C:k:+1000:+700:Stealth
+C:k:+1000:+700:Disarming
+C:k:+1000:+700:Spirituality
+C:k:+1000:+600:Monster-lore
+C:k:+0:+500:Possession
+C:k:+1000:+700:Corpse-preservation
+C:k:+0:+500:Summoning
+C:k:+0:+500:Symbiosis
+C:k:+0:+500:Mimicry
+C:k:+0:+300:Music
+C:g:All Gods
+
+C:a:N:Loremaster
+C:a:D:Loremasters are skilled in most combat and monster skills.
+C:a:O:21:3:1d1
+C:a:O:36:6:1d1
+C:a:O:19:2:1d1
+C:a:O:16:0:3d10
+
+C:a:N:Possessor
+C:a:D:Only the soul matters; a possessor can abandon his/her current body to
+C:a:D:incarnate in the body of a dead monster, thus gaining its powers
+C:a:D:and weaknesses.
+C:a:O:71:37:1d1
+C:a:O:23:10:1d1
+C:a:O:36:6:1d1
+C:a:k:+0:-100:Combat
+C:a:k:+0:-100:Weaponmastery
+C:a:k:+0:-300:Archery
+C:a:k:-1000:-700:Barehand-combat
+C:a:k:+0:-200:Disarming
+C:a:k:+0:-200:Spirituality
+C:a:k:+1000:+300:Possession
+C:a:k:+0:+200:Corpse-preservation
+C:a:k:+0:-500:Summoning
+C:a:k:+0:-500:Symbiosis
+C:a:k:+0:-500:Mimicry
+
+C:a:N:Mimic
+C:a:D:Disguise is the way of the mimic. Through the use of cloaks of mimicry
+C:a:D:they can change shape for a limited time. They also can temporarily
+C:a:D:change part of their anatomy.
+C:a:O:71:37:1d1
+C:a:O:23:4:1d1
+C:a:k:+0:+100:Combat
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:-100:Barehand-combat
+C:a:k:+1000:+100:Magic
+C:a:k:+0:+100:Sneakiness
+C:a:k:+0:+100:Stealth
+C:a:k:+0:-200:Spirituality
+C:a:k:+0:-400:Possession
+C:a:k:+0:+200:Corpse-preservation
+C:a:k:+0:-500:Summoning
+C:a:k:+0:-500:Symbiosis
+C:a:k:+1000:+300:Mimicry
+
+C:a:N:Symbiant
+C:a:D:A symbiant can merge his/her body with one of a monster unable to move
+C:a:D:by itself. They also have a few spells to help the symbiosis.
+C:a:O:23:4:1d1
+C:a:O:70:6:1d1
+C:a:k:+0:+100:Combat
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:-100:Barehand-combat
+C:a:k:+1000:+100:Magic
+C:a:k:+0:+100:Sneakiness
+C:a:k:+0:+100:Stealth
+C:a:k:+0:-200:Spirituality
+C:a:k:+0:-400:Possession
+C:a:k:+0:+200:Corpse-preservation
+C:a:k:+0:-500:Summoning
+C:a:k:+1000:+300:Symbiosis
+C:a:k:+0:-500:Mimicry
+
+C:a:N:Summoner
+C:a:D:The summoner can conjure monsters from totems made from defeated foes.
+C:a:O:71:37:1d1
+C:a:O:23:10:1d1
+C:a:O:36:6:1d1
+C:a:k:+0:-100:Combat
+C:a:k:+0:-100:Weaponmastery
+C:a:k:+0:-300:Archery
+C:a:k:-1000:-700:Barehand-combat
+C:a:k:+1000:+200:Magic
+C:a:k:+0:+0:Sneakiness
+C:a:k:+0:+0:Stealth
+C:a:k:+0:-200:Disarming
+C:a:k:+0:-200:Spirituality
+C:a:k:+15000:+100:Monster-lore
+C:a:k:+0:-500:Possession
+C:a:k:+0:+300:Corpse-preservation
+C:a:k:+1000:+200:Summoning
+C:a:k:+0:-500:Symbiosis
+C:a:k:+0:-500:Mimicry
+
+C:a:N:Monk
+C:a:D:Barehanded, lightly armoured fighters, they wreak havoc with
+C:a:D:their bare fists, and can also use a few prayers.
+C:a:O:71:37:1d1
+C:a:O:36:4:1d1
+C:a:k:-1000:+200:Combat
+C:a:k:-1000:-400:Weaponmastery
+C:a:k:-1000:-300:Archery
+C:a:k:+0:+200:Barehand-combat
+C:a:k:+0:+0:Magic
+C:a:k:+0:+200:Sneakiness
+C:a:k:+0:+200:Stealth
+C:a:k:+0:+200:Disarming
+C:a:k:+0:+200:Spirituality
+C:a:k:+0:-400:Possession
+C:a:k:+0:-200:Corpse-preservation
+C:a:k:+0:-500:Summoning
+C:a:k:+0:-500:Symbiosis
+C:a:k:+0:-500:Mimicry
+C:a:k:+0:+500:Meta
+C:a:k:+0:+500:Mind
+C:a:k:+0:+500:Temporal
+C:a:k:+0:+700:Dodging
+
+C:a:N:Bard
+C:a:D:Bards sing and play songs full of power, beauty or sadness to affect
+C:a:D:everything that can hear them, using music instruments of various types.
+C:a:O:71:37:1d1
+C:a:O:23:10:1d1
+C:a:O:36:2:1d1
+C:a:O:14:59:1d1
+C:a:k:+1000:+0:Magic
+C:a:k:-1000:-700:Archery
+C:a:k:+0:-100:Barehand-combat
+C:a:k:+0:-100:Disarming
+C:a:k:+0:+100:Spirituality
+C:a:k:+0:-500:Possession
+C:a:k:+0:-100:Summoning
+C:a:k:+0:-100:Symbiosis
+C:a:k:+0:-100:Mimicry
+C:a:k:+1000:+500:Music
+
+C:N:5:Priest
+C:D:0:A priest serves a god (Vala, Maia or Eru himself) to bring down
+C:D:0:the empire of fear and shadows of Morgoth.
+C:D:1:Believer
+C:D:1:Acolyte
+C:D:1:Adept
+C:D:1:Curate
+C:D:1:Canon
+C:D:1:Priest
+C:D:1:High Priest
+C:D:1:Cardinal
+C:D:1:Inquisitor
+C:D:1:Pope
+C:S:-1:-3:3:-1:0:2:0:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:4:35:3
+C:C:L:H:10000:2:40
+C:P:2:20
+C:Z:detect curses
+C:G:GOD_FRIEND |
+C:E:0:0:0:0:0:0
+C:k:+1000:+900:Magic
+C:k:+0:+600:Spell-power
+C:k:+0:+600:Necromancy
+C:k:+0:+600:Mindcraft
+C:k:+1000:+1000:Spirituality
+C:k:+1000:+700:Prayer
+C:k:+2000:+700:Combat
+C:k:+1000:+700:Weaponmastery
+C:k:+1000:+900:Sneakiness
+C:k:+0000:+900:Disarming
+C:k:+0000:+400:Stealth
+C:k:+0:+50:Magic-Device
+C:b:1:Perfect casting
+
+C:a:N:Priest(Eru)
+C:a:D:A priest that serves Eru Iluvatar to bring down
+C:a:D:the empire of fear and shadows of Morgoth.
+C:a:O:21:5:1d1
+C:a:g:Eru Iluvatar
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+100:Spell-power
+
+C:a:N:Priest(Manwe)
+C:a:D:A priest that serves Manwe Sulimo to bring down
+C:a:D:the empire of fear and shadows of Morgoth.
+C:a:O:21:5:1d1
+C:a:g:Manwe Sulimo
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+50:Weaponmastery
+
+C:a:N:Druid
+C:a:D:A priest that serves Yavanna Kementari to protect
+C:a:D:and help the regrowth of nature on Arda.
+C:a:O:21:5:1d1
+C:a:g:Yavanna Kementari
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+50:Weaponmastery
+C:a:k:+1000:+700:Monster-lore
+C:a:k:+1000:+700:Summoning
+
+C:a:N:Dark-Priest
+C:a:D:A priest that serves Melkor Bauglir to bring chaos
+C:a:D:and destruction to Arda.
+C:a:O:21:5:1d1
+C:a:g:Melkor Bauglir
+C:a:k:+0:+200:Prayer
+C:a:k:+0:+200:Necromancy
+C:a:k:+0:-600:Mindcraft
+C:a:k:+0:+200:Spell-power
+C:a:k:+1000:+1000:Corpse-preservation
+
+C:a:N:Paladin
+C:a:D:A fighting priest that serves Tulkas to bring down
+C:a:D:the empire of fear and shadows of Morgoth.
+C:a:O:23:25:1d1
+C:a:g:Tulkas
+C:a:k:+0:+200:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+100:Weaponmastery
+C:a:k:+0:+100:Combat
+C:a:k:+0:+900:Barehand-combat
+C:a:b:1:Extra Max Blow(1)
+
+C:a:N:Mindcrafter
+C:a:D:A priest who has learned to tap in his own mental powers
+C:a:O:21:5:1d1
+C:a:g:Eru Iluvatar
+C:a:g:Manwe Sulimo
+C:a:g:Tulkas
+C:a:g:Melkor Bauglir
+C:a:g:Yavanna Kementari
+C:a:k:+0:-300:Prayer
+C:a:k:+0:-200:Necromancy
+C:a:k:+1000:+300:Mindcraft
+C:a:k:+0:+200:Sneakiness
+C:a:k:+0:+100:Magic-Device
+
+###############################TEST###############################
+C:N:30:Test
+C:D:0:Simple testers.
+C:D:1:Rookie
+C:D:1:Soldier
+C:D:1:Mercenary
+C:D:1:Veteran
+C:D:1:Swordsman
+C:D:1:Champion
+C:D:1:Hero
+C:D:1:Baron
+C:D:1:Duke
+C:D:1:Lord
+C:S:5:-2:-2:2:2:-1:0:3
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:B:6:30:5
+C:C:H:L:9000:2:40
+C:P:9:0
+C:R:30:0
+C:F:RES_FEAR
+C:E:0:0:0:0:0:0
+C:O:45:38:1d1
+C:O:37:4:1d1
+C:k:+1000:+800:Combat
+C:k:+1000:+850:Weaponmastery
+C:k:+0:+200:Sword-mastery
+C:k:+0:+200:Axe-mastery
+C:k:+0:+200:Hafted-mastery
+C:k:+0:+200:Polearm-mastery
+C:k:+1000:+600:Archery
+C:k:+1000:+900:Sneakiness
+C:k:+1000:+900:Disarming
+C:k:+1000:+300:Magic
+C:k:+0:+550:Antimagic
+
+C:a:N:Shinny test
+C:a:D:Simple testers, they test all with their shiny hacks !
+C:a:O:23:16:1d1
+###############################TEST###############################
+
+
+
+
+
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# R: Race definition
+# R:N:index:name
+# R:D:race desc
+# R:S:str:int:wis:dex:con:chr:luck
+# R:K:dis:dev:sav:stl:srh:fos:thn:thb
+# R:P:hitdie:xp%:infra:history chart
+# R:M:b_age:m_age:m_b_ht:m_m_ht:m_b_wt:m_m_wt:f_b_ht:f_m_ht:f_b_wt:f_m_wt
+# R:E:weapons:torso:arms:finger:head:legs
+# R:C:allowed classes
+# R:G:race flags
+# R:R:level:pval
+# R:F:flags
+# R:k:value:modifier:skill name
+# R:b:level:ability
+
+I:
+
+R:N:0:Human
+R:D:Humans are the second born, the Edain.
+R:D:They are the basic race to which all others are compared.
+R:D:Average in ability, they can be any class.
+R:S:0:0:0:0:0:0:0
+R:K:0:0:0:0:0:10:0:0
+R:P:10:100:0:1
+R:M:14:6:72:6:180:25:66:4:150:20
+R:E:1:1:1:2:1:1
+R:C:Warrior | Archer | Mage | Rogue | Priest | Loremaster
+
+R:N:1:Half-Elf
+R:D:A crossbreed of elf and human, they get the best of the two races.
+R:S:0:1:1:1:-1:1:0
+R:K:2:3:3:1:6:11:-1:5
+R:P:9:110:2:4
+R:M:24:16:66:6:130:15:62:6:100:10
+R:E:1:1:1:2:1:1
+R:C:Warrior | Archer | Mage | Rogue | Priest | Loremaster
+R:G:ELF
+R:k:+200:+000:Disarming
+R:k:+300:+000:Magic-Device
+R:k:+1500:+000:Spirituality
+R:k:+1000:+000:Stealth
+R:k:+600:+000:Sneakiness
+R:k:-100:+000:Weaponmastery
+R:k:+500:+000:Archery
+
+R:N:2:Elf
+R:D:Elves are the first born, the Eldar.
+R:D:More spiritual than physical beings, they are weaker than humans
+R:D:but are more intelligent.
+R:S:-1:2:2:1:-2:2:0
+R:K:5:6:6:2:8:12:-5:15
+R:P:8:120:3:5
+R:M:75:75:60:4:100:6:54:4:80:6
+R:E:1:1:1:2:1:1
+R:R:1:0
+R:F:RES_LITE |
+R:C:Warrior | Archer | Mage | Priest | Loremaster
+R:G:ELF
+R:k:+500:+000:Disarming
+R:k:+600:+000:Magic-Device
+R:k:+3000:+000:Spirituality
+R:k:+2000:+000:Stealth
+R:k:+800:+000:Sneakiness
+R:k:-500:+000:Weaponmastery
+R:k:+1500:+000:Archery
+
+R:N:3:Hobbit
+R:D:An old but quiet race related to humans.
+R:D:They are small and quite weak but good at many things.
+R:S:-2:2:1:3:2:1:5
+R:K:15:18:18:5:12:15:-10:20
+R:P:7:110:4:10
+R:M:21:12:36:3:60:3:33:3:50:3
+R:E:1:1:1:2:1:1
+R:Z:create food
+R:G:RESIST_BLACK_BREATH | XTRA_MIGHT_SLING |
+R:R:1:0
+R:F:SUST_DEX |
+R:C:Warrior | Archer | Mage | Rogue | Loremaster
+R:k:+0:+300:Sling-mastery
+R:k:+1500:+000:Disarming
+R:k:+1800:+000:Magic-Device
+R:k:+9000:+000:Spirituality
+R:k:+5000:+000:Stealth
+R:k:+1200:+000:Sneakiness
+R:k:-1000:+000:Weaponmastery
+R:k:+2000:+000:Archery
+
+R:N:4:Gnome
+R:D:Related to dwarves, Gnomes are between Dwarves and Hobbits in size.
+R:D:Very good at magic use, they are poor as fighters.
+R:S:-1:2:0:2:1:-2:2
+R:K:10:12:12:3:6:13:-8:12
+R:P:8:135:4:13
+R:M:50:40:42:3:90:6:39:3:75:3
+R:E:1:1:1:2:1:1
+R:Z:blink
+R:R:1:0
+R:F:FREE_ACT |
+R:C:Warrior | Mage | Rogue
+R:k:+1000:+000:Disarming
+R:k:+1200:+000:Magic-Device
+R:k:+6000:+000:Spirituality
+R:k:+3000:+000:Stealth
+R:k:+600:+000:Sneakiness
+R:k:-800:+000:Weaponmastery
+R:k:+1200:+000:Archery
+
+R:N:5:Dwarf
+R:D:The children of Aule, a strong but small race.
+R:D:Miners and fighters of legend.
+R:S:2:-2:2:-2:2:-3:0
+R:K:2:9:10:-1:7:10:15:0
+R:P:11:125:5:16
+R:M:35:15:48:3:150:10:46:3:120:10
+R:E:1:1:1:2:1:1
+R:Z:find secret passages
+R:R:1:0
+R:F:RES_BLIND |
+R:C:Warrior | Priest
+R:k:+0:+200:Axe-mastery
+R:k:+200:+000:Disarming
+R:k:+900:+000:Magic-Device
+R:k:+5000:+000:Spirituality
+R:k:-1000:+000:Stealth
+R:k:+700:+000:Sneakiness
+R:k:+1500:+000:Weaponmastery
+R:k:+500:+000:Archery
+
+R:N:6:Orc
+R:D:Quite strong but not very smart.
+R:S:2:-1:0:1:1:-4:-3
+R:K:-3:-3:-3:-1:0:7:12:-5
+R:P:10:110:3:25
+R:M:11:4:66:1:150:5:62:1:120:5
+R:E:1:1:1:2:1:1
+R:Z:remove fear
+R:R:1:0
+R:F:RES_DARK |
+R:C:Warrior | Archer | Rogue | Priest
+R:k:-300:+000:Disarming
+R:k:-300:+000:Magic-Device
+R:k:-1000:+000:Spirituality
+R:k:-1000:+000:Stealth
+R:k:+000:+000:Sneakiness
+R:k:+1200:+000:Weaponmastery
+R:k:-500:+000:Archery
+
+R:N:7:Troll
+R:D:They can bear the light of the sun.
+R:D:They are extremely strong and dumb.
+R:S:4:-4:-2:-4:3:-6:-4
+R:K:-5:-8:-8:-2:-1:5:20:-10
+R:P:12:137:3:22
+R:M:20:10:96:10:250:50:84:8:225:40
+R:E:1:1:1:2:1:1
+R:Z:berserk
+R:R:1:0
+R:F:SUST_STR |
+R:R:15:0
+R:F:REGEN |
+R:C:Warrior
+R:k:-500:+000:Disarming
+R:k:-800:+000:Magic-Device
+R:k:-4000:+000:Spirituality
+R:k:-2000:+000:Stealth
+R:k:-100:+000:Sneakiness
+R:k:+2000:+000:Weaponmastery
+R:k:-1000:+000:Archery
+
+R:N:8:Dunadan
+R:D:The greatest of the Edain, humans in all respects but
+R:D:stronger, smarter and wiser.
+R:S:1:2:2:2:3:2:2
+R:K:4:5:5:2:3:13:15:10
+R:P:10:180:0:1
+R:M:50:50:82:5:190:20:78:6:180:15
+R:E:1:1:1:2:1:1
+R:R:1:0
+R:F:SUST_CON | REGEN |
+R:C:Warrior | Archer | Mage | Rogue | Priest | Loremaster
+R:k:+400:+000:Disarming
+R:k:+500:+000:Magic-Device
+R:k:+2500:+000:Spirituality
+R:k:+2000:+000:Stealth
+R:k:+800:+000:Sneakiness
+R:k:+1500:+000:Weaponmastery
+R:k:+1000:+000:Archery
+
+R:N:9:High-Elf
+R:D:Elves are the first born, the Eldar.
+R:D:High elves are the best of the Eldar, strong, fast, intellectual, though
+R:D:they sometimes lack wisdom.
+R:S:1:3:2:3:1:5:0
+R:K:4:20:20:4:3:14:10:25
+R:P:10:200:4:7
+R:M:100:30:90:10:190:20:82:10:180:15
+R:E:1:1:1:2:1:1
+R:R:1:0
+R:F:SEE_INVIS |
+R:F:RES_LITE |
+R:G:ELF
+R:C:Warrior | Archer | Mage | Priest | Loremaster
+R:k:+400:+000:Disarming
+R:k:+2000:+000:Magic-Device
+R:k:+10000:+000:Spirituality
+R:k:+4000:+000:Stealth
+R:k:+300:+000:Sneakiness
+R:k:+1000:+000:Weaponmastery
+R:k:+2500:+000:Archery
+
+R:N:10:Half-Ogre
+R:D:A crossbreed between a human and an ogre.
+R:D:They are similar to half-trolls, strong and dumb.
+R:S:3:-1:-1:-1:3:-3:-2
+R:K:-3:-5:-5:-2:-1:5:20:0
+R:P:12:130:3:74
+R:M:40:10:92:10:255:60:80:8:235:60
+R:E:1:1:1:2:1:1
+R:Z:set explosive rune
+R:R:1:0
+R:F:SUST_STR | RES_DARK |
+R:C:Warrior | Priest
+R:k:-300:+000:Disarming
+R:k:-500:+000:Magic-Device
+R:k:-2500:+000:Spirituality
+R:k:-2000:+000:Stealth
+R:k:-100:+000:Sneakiness
+R:k:+2000:+000:Weaponmastery
+R:k:+000:+000:Archery
+
+R:N:11:Beorning
+R:D:A race of men shapeshifters.
+R:D:They have the unique power of being able to polymorph to bear forms.
+R:S:4:-2:-2:-1:3:-5:1
+R:K:-6:-8:-6:-2:-1:5:25:5
+R:P:12:150:3:75
+R:M:40:10:100:10:255:65:80:10:240:64
+R:E:1:1:1:2:1:1
+R:Z:turn into a bear
+R:R:1:0
+R:F:SUST_STR |
+R:R:20:1
+R:F:STR |
+R:C:Warrior | Rogue | Loremaster
+R:k:+1000:+1000:Bearform-combat
+R:k:-600:+000:Disarming
+R:k:-800:+000:Magic-Device
+R:k:-3000:+000:Spirituality
+R:k:-2000:+000:Stealth
+R:k:-100:+000:Sneakiness
+R:k:+2500:+000:Weaponmastery
+R:k:+500:+000:Archery
+
+R:N:12:Kobold
+R:D:A weaker kind of goblin, related to orcs.
+R:S:1:-1:0:1:0:-4:0
+R:K:-2:-3:-2:-1:1:8:10:-8
+R:P:9:125:3:82
+R:M:11:3:60:1:130:5:55:1:100:5
+R:E:1:1:1:2:1:1
+R:Z:poison dart
+R:R:1:0
+R:F:RES_POIS |
+R:C:Warrior | Archer | Rogue
+R:k:-200:+000:Disarming
+R:k:-300:+000:Magic-Device
+R:k:-1000:+000:Spirituality
+R:k:-1000:+000:Stealth
+R:k:+100:+000:Sneakiness
+R:k:+1000:+000:Weaponmastery
+R:k:-800:+000:Archery
+
+R:N:13:Petty-Dwarf
+R:D:A nearly extinct subrace of dwarves.
+R:D:They prefer to live in the darkness.
+R:S:1:-1:2:0:2:-4:-5
+R:K:3:5:10:1:5:10:9:0
+R:P:11:135:5:87
+R:M:40:12:43:3:92:6:40:3:78:3
+R:E:1:1:1:2:1:1
+R:Z:detect doors and traps
+R:R:1:0
+R:F:RES_DARK | RES_DISEN |
+R:C:Warrior | Rogue
+R:k:+300:+000:Disarming
+R:k:+500:+000:Magic-Device
+R:k:+5000:+000:Spirituality
+R:k:+1000:+000:Stealth
+R:k:+500:+000:Sneakiness
+R:k:+000:+000:Weaponmastery
+R:k:+000:+000:Archery
+
+R:N:14:Dark-Elf
+R:D:Elves are the first born, the Eldar.
+R:D:Dark elves are rare on Middle-earth and even though not evil
+R:D:they are not good.
+R:S:-1:3:2:2:-2:1:-2
+R:K:5:15:20:3:8:12:-5:10
+R:P:9:150:5:69
+R:M:75:75:60:4:100:6:54:4:80:6
+R:E:1:1:1:2:1:1
+R:Z:magic missile
+R:R:1:0
+R:F:RES_DARK |
+R:R:20:0
+R:F:SEE_INVIS |
+R:C:Warrior | Archer | Mage | Rogue | Priest
+R:G:ELF
+R:k:+0:+200:Magic
+R:k:+500:+000:Disarming
+R:k:+1500:+000:Magic-Device
+R:k:+10000:+000:Spirituality
+R:k:+3000:+000:Stealth
+R:k:+800:+000:Sneakiness
+R:k:-500:+000:Weaponmastery
+R:k:+1000:+000:Archery
+
+R:N:15:Ent
+R:D:Guardian of the forests of Middle-earth, summoned by Yavanna before
+R:D:even the elves awoke. It is said 'Trolls are strong, Ents are STRONGER'.
+R:S:10:-3:2:-5:11:-3:-2
+R:K:5:5:20:-6:5:4:15:5
+R:P:14:210:5:95
+R:M:255:70:72:6:100:25:66:4:100:20
+R:E:1:1:1:2:1:1
+R:Z:grow trees
+R:G:NO_STUN | NO_FOOD |
+R:G:AC_LEVEL |
+R:R:1:-5
+R:F:SPEED | SENS_FIRE | SLOW_DIGEST |
+R:R:5:0
+R:F:SEE_INVIS |
+R:R:20:0
+R:F:ESP_ORC |
+R:F:ESP_TROLL | ESP_EVIL |
+R:C:Warrior | Priest | Loremaster
+R:O:70:32:2d3
+R:b:1:Tree walking
+R:k:+0:+200:Barehand-combat
+R:k:+0:+600:Boulder-throwing
+R:k:+500:+000:Disarming
+R:k:+500:+000:Magic-Device
+R:k:+10000:+000:Spirituality
+R:k:-6000:+000:Stealth
+R:k:+500:+000:Sneakiness
+R:k:-300:+000:Weaponmastery
+R:k:-200:+000:Archery
+
+R:N:16:RohanKnight
+R:D:Humans from the land of Rohan, riding the great Mearas.
+R:D:Fast and powerful in battle.
+R:S:4:-2:3:1:4:2:0
+R:K:10:5:5:-8:1:1:5:5
+R:P:10:220:0:84
+R:M:20:3:60:3:80:4:54:3:70:4
+R:E:1:1:1:2:1:1
+R:Z:Rohan Knight's Powers
+R:R:1:3
+R:F:SPEED |
+R:R:5:1
+R:F:SPEED |
+R:R:10:1
+R:F:SPEED |
+R:R:15:1
+R:F:SPEED |
+R:R:20:1
+R:F:SPEED |
+R:R:25:1
+R:F:SPEED |
+R:R:30:1
+R:F:SPEED |
+R:R:35:1
+R:F:SPEED |
+R:R:40:1
+R:F:SPEED |
+R:R:45:1
+R:F:SPEED |
+R:C:Warrior | Priest
+R:k:+1000:+000:Disarming
+R:k:+500:+000:Magic-Device
+R:k:+2500:+000:Spirituality
+R:k:-8000:+000:Stealth
+R:k:+100:+000:Sneakiness
+R:k:+100:+200:Weaponmastery
+R:k:+500:+000:Archery
+
+R:N:17:Thunderlord
+R:D:A thunderlord is a Great Eagle of Manwe, ridden by a Maia of Manwe.
+R:D:They carry the power of wind and thunder.
+R:S:6:2:1:1:3:8:2
+R:K:6:0:10:-16:30:10:15:5
+R:P:12:400:0:89
+R:M:14:6:180:6:255:25:150:4:230:20
+R:E:1:1:1:2:1:1
+R:Z:Thunderlord's Powers
+R:R:1:0
+R:F:FEATHER |
+R:R:4:0
+R:F:ESP_DRAGON |
+R:R:5:0
+R:F:RES_ELEC |
+R:R:10:0
+R:F:RES_COLD |
+R:R:15:0
+R:F:RES_ACID |
+R:R:17:0
+R:F:FLY |
+R:R:35:0
+R:F:RES_POIS |
+R:R:45:0
+R:F:IM_ELEC |
+R:C:Warrior | Mage | Archer | Priest
+R:k:+600:+000:Disarming
+R:k:+000:+000:Magic-Device
+R:k:+5000:+000:Spirituality
+R:k:-16000:+000:Stealth
+R:k:+3000:+000:Sneakiness
+R:k:+1500:+000:Weaponmastery
+R:k:+500:+000:Archery
+
+R:N:18:DeathMold
+R:D:A pure mass of evilness, DeathMolds cannot move, but they have much more
+R:D:power than an average race.
+R:S:10:0:10:0:10:-15:-5
+R:K:15:-5:15:25:0:10:25:25
+R:P:15:250:10:100
+R:M:5:15:10:1:50:1:10:1:50:1
+R:E:1:1:1:4:0:0
+R:Z:Death Mold's Powers
+R:G:EXPERIMENTAL
+R:R:1:0
+R:F:IMMOVABLE | HOLD_LIFE |
+R:F:RES_NETHER | RES_NEXUS |
+R:C:Mage | Priest
+R:k:+0:+200:Necromancy
+R:k:+1500:+000:Disarming
+R:k:-500:+000:Magic-Device
+R:k:+7500:+000:Spirituality
+R:k:+25000:+000:Stealth
+R:k:+000:+000:Sneakiness
+R:k:+2500:+000:Weaponmastery
+R:k:+2500:+000:Archery
+
+R:N:19:Yeek
+R:D:The weakest of all the races, bad at everything except gaining levels quickly.
+R:S:-5:-5:-5:-5:-5:-5:-5
+R:K:-5:-5:-10:0:-5:0:-10:-10
+R:P:6:25:2:29
+R:M:10:4:40:5:50:10:35:4:45:10
+R:E:1:1:1:2:1:1
+R:C:Warrior | Archer | Mage | Rogue | Priest | Loremaster
+R:k:-500:+000:Disarming
+R:k:-500:+000:Magic-Device
+R:k:-2500:+000:Spirituality
+R:k:-5000:+000:Stealth
+R:k:-500:+000:Sneakiness
+R:k:-500:+000:Weaponmastery
+R:k:-500:+000:Archery
+
+R:N:20:Wood-Elf
+R:D:Elves are the first born, the Eldar.
+R:D:Wood elves live in the great forests of Middle-earth.
+R:S:-3:2:1:5:-4:1:0
+R:K:5:6:6:5:8:12:-25:40
+R:P:7:130:4:5
+R:M:75:75:60:4:100:6:54:4:80:6
+R:E:1:1:1:2:1:1
+R:G:XTRA_MIGHT_BOW |
+R:R:1:1
+R:F:XTRA_MIGHT | RES_LITE |
+R:C:Warrior | Archer | Mage | Priest | Loremaster
+R:G:ELF
+R:k:+0:+200:Archery
+R:b:1:Tree walking
+R:k:+500:+000:Disarming
+R:k:+600:+000:Magic-Device
+R:k:+3000:+000:Spirituality
+R:k:+5000:+000:Stealth
+R:k:+800:+000:Sneakiness
+R:k:-2500:+000:Weaponmastery
+R:k:+4000:+000:Archery
+
+R:N:21:Maia
+R:D:An old race, dating from before the creation of Arda, the Maiar were
+R:D:created by Eru to help the Valar in their task.
+R:S:0:0:0:0:0:0:4
+R:K:0:0:0:0:0:10:0:0
+R:P:10:100:0:91
+R:M:14:6:72:6:180:25:66:4:150:20
+R:E:1:1:1:2:1:1
+R:G:NO_GOD
+R:R:1:0
+R:F:AGGRAVATE |
+R:R:20:0
+R:F:DRAIN_EXP |
+R:R:6:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:12:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:18:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:24:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:30:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:36:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:42:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:R:48:1
+R:F:STR | INT | WIS | DEX | CON | CHR |
+R:C:Warrior | Archer | Mage
+R:k:+000:+000:Disarming
+R:k:+000:+000:Magic-Device
+R:k:+000:+000:Spirituality
+R:k:=0:=0:Prayer
+R:k:+000:+000:Stealth
+R:k:+000:+000:Sneakiness
+R:k:+000:+000:Weaponmastery
+R:k:+000:+000:Archery
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# S: Subrace definition
+# S:N:index:name
+# S:D:'A'fter/'B'efore:subrace desc
+# S:S:str:int:wis:dex:con:chr:luck:mana
+# S:K:dis:dev:sav:stl:srh:fos:thn:thb
+# S:P:hitdie:xp%:infra
+# S:M:b_age:m_age:m_b_ht:m_m_ht:m_b_wt:m_m_wt:f_b_ht:f_m_ht:f_b_wt:f_m_wt
+# S:E:weapons:torso:arms:finger:head:legs
+# S:A:allowed races
+# S:C:'A'llow/'F'orbid:allowed/forbiden classes
+# S:G:subrace flags
+# S:R:level:pval
+# S:F:flags
+# S:k:value:modifier:skill name
+# S:b:level:ability
+
+# Make the parser actually work :)
+I:
+
+S:N:0:
+S:D:A:A normal member of the race.
+S:S:0:0:0:0:0:0:0:100
+S:K:0:0:0:0:0:0:0:0
+S:P:0:0:0
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Human | Half-Elf | Elf | Hobbit | Gnome | Dwarf |
+S:A:Orc | Troll | Dunadan | High-Elf | Half-Ogre | Beorning |
+S:A:Kobold | Petty-Dwarf | Dark-Elf | Ent | RohanKnight | Thunderlord |
+S:A:DeathMold | Yeek | Wood-Elf | Maia |
+
+# Just a place holder, the actualy setting are done with corruptions, see
+# corrupt.lua and player.lua
+S:N:1:Vampire
+S:D:B:Vampires are powerful undead, wielding great powers. They still fear the
+S:D:B:sunlight and cannot easily satiate their hunger.
+S:S:0:0:0:0:0:0:0:100
+S:K:0:0:0:0:0:0:0:0
+S:P:0:0:0
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Human | Half-Elf | Hobbit | Gnome | Dwarf | Orc |
+S:A:Troll | Dunadan | Half-Ogre | Beorning | Kobold | Petty-Dwarf |
+S:A:Dark-Elf | RohanKnight | Yeek |
+S:C:A:Mage
+S:O:70:0:5d3
+S:O:70:32:2d3
+
+S:N:2:Spectre
+S:D:B:Spectres only partially exist in the mortal world and so they can
+S:D:B:pass through walls. They are somewhat physically weak.
+S:S:-5:1:1:2:-3:-6:-3:105
+S:K:2:8:7:2:2:7:-5:-2
+S:P:-4:80:3
+S:M:50:15:0:0:-10:-5:0:0:-10:-5
+S:E:0:0:0:0:0:0
+S:A:Human | Half-Elf | Elf | Hobbit | Gnome | Dwarf |
+S:A:Orc | Troll | Dunadan | High-Elf | Half-Ogre | Beorning |
+S:A:Kobold | Petty-Dwarf | Dark-Elf | RohanKnight | Yeek | Wood-Elf |
+S:C:F:Warrior | Archer
+S:G:UNDEAD | NO_CUT | NO_FOOD | SEMI_WRAITH | NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SEE_INVIS | HOLD_LIFE |
+S:F:SLOW_DIGEST | RES_COLD | RES_POIS | RES_NETHER |
+S:O:70:32:2d3
+S:k:+200:+000:Disarming
+S:k:+800:+000:Magic-Device
+S:k:+700:+000:Spirituality
+S:k:+2000:+000:Stealth
+S:k:+200:+000:Sneakiness
+S:k:-500:+000:Weaponmastery
+S:k:-200:+000:Archery
+
+S:N:3:Skeleton
+S:D:B:Yet an other kind of undead. Their physical 'body' is not very vulnerable
+S:D:B:to sharp things.
+S:S:0:-2:-2:0:1:-4:-3:70
+S:K:-5:-5:5:-1:-1:8:8:0
+S:P:0:45:1
+S:M:50:15:0:0:-10:-5:0:0:-10:-5
+S:E:0:0:0:0:0:0
+S:A:Human | Half-Elf | Elf | Hobbit | Gnome | Dwarf |
+S:A:Orc | Troll | Dunadan | High-Elf | Half-Ogre | Beorning |
+S:A:Kobold | Petty-Dwarf | Dark-Elf | RohanKnight | Yeek | Wood-Elf |
+S:G:UNDEAD | NO_CUT | NO_FOOD | NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SEE_INVIS | HOLD_LIFE |
+S:F:RES_POIS | RES_SHARDS |
+S:R:10:0
+S:F:RES_COLD |
+S:O:70:32:2d3
+S:k:-500:+000:Disarming
+S:k:-500:+000:Magic-Device
+S:k:+500:+000:Spirituality
+S:k:-1000:+000:Stealth
+S:k:-100:+000:Sneakiness
+S:k:+800:+000:Weaponmastery
+S:k:+000:+000:Archery
+
+S:N:4:Zombie
+S:D:B:Strong and dumb is a zombie.
+S:S:2:-6:-6:1:4:-5:-4:70
+S:K:-2:-2:5:-1:-1:2:5:0
+S:P:3:45:1
+S:M:50:15:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Human | Half-Elf | Elf | Hobbit | Gnome | Dwarf |
+S:A:Orc | Troll | Dunadan | High-Elf | Half-Ogre | Beorning |
+S:A:Kobold | Petty-Dwarf | Dark-Elf | RohanKnight | Yeek | Wood-Elf |
+S:C:F:Mage
+S:G:UNDEAD | NO_FOOD | NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SEE_INVIS | HOLD_LIFE |
+S:F:SLOW_DIGEST | RES_POIS |
+S:R:5:0
+S:F:RES_COLD |
+S:O:70:32:2d3
+S:k:-200:+000:Disarming
+S:k:-200:+000:Magic-Device
+S:k:+500:+000:Spirituality
+S:k:-1000:+000:Stealth
+S:k:-100:+000:Sneakiness
+S:k:+500:+000:Weaponmastery
+S:k:+000:+000:Archery
+
+S:N:5:Barbarian
+S:D:A:Hardy members of their race, they are strong fighters but poor spellcasters.
+S:S:2:-3:-2:1:1:-3:1:50
+S:K:-2:-10:2:-2:0:1:12:5
+S:P:1:25:0
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Human | Dwarf | Orc | Troll | Half-Ogre | Beorning |
+S:C:F:Mage
+S:R:10:0
+S:F:RES_FEAR |
+S:k:-200:+000:Disarming
+S:k:-1000:+000:Magic-Device
+S:k:+200:+000:Spirituality
+S:k:-2000:+000:Stealth
+S:k:+000:+000:Sneakiness
+S:k:+1200:+000:Weaponmastery
+S:k:+500:+000:Archery
+
+S:N:6:Hermit
+S:D:A:Through years of isolation hermits can manage to increase their mana
+S:D:A:reserves but at the cost of an increased physical weakness.
+S:S:-3:1:1:-3:-3:1:0:120
+S:K:5:10:5:3:4:10:-5:-5
+S:P:-3:20:1
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Human | Half-Elf | Elf | Hobbit | Gnome | Dwarf |
+S:A:Dunadan | High-Elf | Petty-Dwarf | Dark-Elf | Ent | RohanKnight |
+S:A:Thunderlord | DeathMold | Yeek | Wood-Elf | Maia |
+S:C:F:Warrior | Archer
+S:k:+500:+000:Disarming
+S:k:+1000:+000:Magic-Device
+S:k:+500:+000:Spirituality
+S:k:+3000:+000:Stealth
+S:k:+400:+000:Sneakiness
+S:k:-500:+000:Weaponmastery
+S:k:-500:+000:Archery
+
+S:N:8:LostSoul
+S:D:A:In some very rare occasions souls can come back from the Halls of Mandos.
+S:S:0:0:0:0:0:0:0:100
+S:K:0:0:0:0:0:0:0:0
+S:P:0:0:0
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:G:ASTRAL | NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SEE_INVIS
+S:A:Human | Half-Elf | Elf | Hobbit | Gnome | Dwarf |
+S:A:Orc | Troll | Dunadan | High-Elf | Half-Ogre | Beorning |
+S:A:Kobold | Petty-Dwarf | Dark-Elf | Ent | RohanKnight | Thunderlord |
+S:A:DeathMold | Yeek | Wood-Elf | Maia |
+S:O:70:32:25d2
+S:O:70:12:25d3
+
+# Used for corruptions that can change your subrace
+S:N:9:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+S:D:A:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+S:S:0:0:0:0:0:0:0:100
+S:K:0:0:0:0:0:0:0:0
+S:P:0:0:0
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# H: Race history
+# H:index:chance:chart:next chart:social class bonus:desc
+
+# Dunadan, Human: 1 -> 2 -> 3 -> 50 -> 51 -> 52 -> 53.
+# RohanKnight: 84 -> 85 -> 50 -> 51 -> 52 -> 53.
+# Half-Ogre: 74 -> 20 -> 2 -> 3 -> 50 -> 51 -> 52 -> 53.
+# Beorning: 75 -> 76 -> 20 -> 2 -> 3 -> 50 -> 51 -> 52 -> 53.
+# Half-elf: 4 -> 1 -> 2 -> 3 -> 50 -> 51 -> 52 -> 53.
+# Elf, Wood-elf: 5 -> 6 -> 9 -> 54 -> 55 -> 56.
+# High-elf: 7 -> 8 -> 9 -> 54 -> 55 -> 56.
+# Dark-elf: 69 -> 70 -> 71 -> 72 -> 73.
+# Hobbit: 10 -> 11 -> 3 -> 50 -> 51 -> 52 -> 53.
+# Gnome: 13 -> 14 -> 3 -> 50 -> 51 -> 52 -> 53.
+# Dwarf: 16 -> 17 -> 18 -> 57 -> 58 -> 59 -> 60 -> 61.
+# Petty-Dwarf: 87 -> 88 -> 18 -> 57 -> 58 -> 59 -> 60 -> 61.
+# Thunderlord: 89 -> 90 -> 93 -> 94.
+# Maia: 91 -> 92 -> 93 -> 94.
+# Ent: 95 -> 96.
+# Troll: 22 -> 23 -> 24 -> 62 -> 63 -> 64 -> 65 -> 66.
+# Orc 25 -> 26 -> 27 -> 28 -> 80 -> 81 -> 65 -> 66.
+# Yeek: 29 -> 3 -> 50 -> 51 -> 52 -> 53.
+# (not used: 78 -> 79 -> 80 -> 81 -> 65 -> 66.)
+# Kobold: 82 -> 83 -> 80 -> 81 -> 65 -> 66.
+# Deathmold: 100 -> 101 -> 102 -> 103 -> 104.
+
+H:0:10:1:2:25:You are the illegitimate and unacknowledged child
+H:1:20:1:2:35:You are the illegitimate but acknowledged child
+H:2:95:1:2:45:You are one of several children
+H:3:100:1:2:50:You are the first child
+
+H:4:40:2:3:65:of a Serf.
+H:5:65:2:3:80:of a Yeoman.
+H:6:80:2:3:90:of a Townsman.
+H:7:90:2:3:105:of a Guildsman.
+H:8:96:2:3:120:of a Landed Knight.
+H:9:99:2:3:130:of a Noble Lord.
+H:10:100:2:3:140:of the Royal Blood Line.
+
+H:11:20:3:50:20:You are the black sheep of the family.
+H:12:80:3:50:55:You are a credit to the family.
+H:13:100:3:50:60:You are a well-liked child.
+
+H:14:25:4:1:40:Your mother was of the Avari.
+H:15:40:4:1:50:Your father was of the Avari.
+H:16:65:4:1:60:Your mother was of the Nandor.
+H:17:80:4:1:60:Your father was of the Nandor.
+H:18:96:4:1:70:Your mother was of the Sindar.
+H:19:99:4:1:70:Your father was of the Sindar.
+H:20:100:4:1:100:Your ancestry traces to Elrond.
+
+H:21:60:5:6:50:You are one of several children
+H:22:100:5:6:55:You are the only child
+
+H:23:40:6:9:40:of an Avarin
+H:24:70:6:9:50:of a Nandorin
+H:25:100:6:9:60:of a Sindarin
+
+H:26:60:7:8:50:You are one of several children
+H:27:100:7:8:55:You are the only child
+
+H:28:75:8:9:50:of a Telerin
+H:29:95:8:9:55:of a Noldorin
+H:30:100:8:9:60:of a Vanyarin
+
+H:31:40:9:54:80:Ranger.
+H:32:70:9:54:90:Archer.
+H:33:87:9:54:110:Warrior.
+H:34:95:9:54:125:Mage.
+H:35:99:9:54:140:Prince.
+H:36:100:9:54:145:King.
+
+H:37:85:10:11:45:You are one of several children of a Hobbit
+H:38:100:10:11:55:You are the only child of a Hobbit
+
+H:39:20:11:3:55:Bum.
+H:40:30:11:3:80:Tavern Owner.
+H:41:40:11:3:90:Miller.
+H:42:50:11:3:100:Home Owner.
+H:43:80:11:3:110:Burglar.
+H:44:95:11:3:115:Warrior.
+H:45:99:11:3:125:Mage.
+H:46:100:11:3:140:Clan Elder.
+
+H:47:85:13:14:45:You are one of several children of a Gnome
+H:48:100:13:14:55:You are the only child of a Gnome
+
+H:49:20:14:3:55:Beggar.
+H:50:50:14:3:70:Braggart.
+H:51:75:14:3:85:Prankster.
+H:52:95:14:3:100:Warrior.
+H:53:100:14:3:125:Mage.
+
+H:54:25:16:17:40:You are one of two children of a Dwarven
+H:55:100:16:17:50:You are the only child of a Dwarven
+
+H:56:10:17:18:60:Thief.
+H:57:35:17:18:80:Smith.
+H:58:75:17:18:90:Miner.
+H:59:90:17:18:110:Warrior.
+H:60:99:17:18:130:Priest.
+H:61:100:17:18:150:King.
+
+H:62:15:18:57:10:You are the black sheep of the family.
+H:63:85:18:57:50:You are a credit to the family.
+H:64:100:18:57:55:You are a well liked child.
+
+H:65:100:20:2:50:You are the adopted child
+
+H:66:100:22:23:50:You are the offspring of a
+
+H:67:30:23:24:20:Forest-Troll
+H:68:60:23:24:25:Cave-Troll
+H:69:75:23:24:30:Hill-Troll
+H:70:90:23:24:35:Stone-Troll
+H:71:95:23:24:40:Snow-Troll
+H:72:100:23:24:45:Water-Troll
+
+H:73:25:24:62:50:Worker.
+H:74:95:24:62:55:Warrior.
+H:75:99:24:62:65:Shaman.
+H:76:100:24:62:80:Clan Chief.
+
+H:77:100:25:26:50:You are one of several children of
+
+H:78:40:26:27:40:a Snaga
+H:79:80:26:27:50:an Orc
+H:80:100:26:27:60:an Uruk
+
+H:81:30:27:28:20:Slave
+H:82:60:27:28:50:Archer
+H:83:90:27:28:60:Warrior
+H:84:95:27:28:80:Shaman
+H:85:100:27:28:100:Chieftain
+
+H:86:30:28:80:50:from the Misty Mountains.
+H:87:60:28:80:50:from the Grey Mountains.
+H:88:90:28:80:70:from the orc-hold of Mount Gundabad.
+H:89:100:28:80:80:from the Pits of Angband.
+
+H:90:25:29:3:50:You are one of five children of a blue Yeek.
+H:91:75:29:3:75:You are one of five children of a brown Yeek.
+H:92:100:29:3:100:You are one of five children of a master Yeek.
+
+H:93:20:50:51:50:You have dark brown eyes,
+H:94:60:50:51:50:You have brown eyes,
+H:95:70:50:51:50:You have hazel eyes,
+H:96:80:50:51:50:You have green eyes,
+H:97:90:50:51:50:You have blue eyes,
+H:98:100:50:51:50:You have blue-gray eyes,
+
+H:99:70:51:52:50:straight
+H:100:90:51:52:50:wavy
+H:101:100:51:52:50:curly
+
+H:102:30:52:53:50:black hair,
+H:103:70:52:53:50:brown hair,
+H:104:80:52:53:50:auburn hair,
+H:105:90:52:53:50:red hair,
+H:106:100:52:53:50:blond hair,
+
+H:107:10:53:0:50:and a very dark complexion.
+H:108:30:53:0:50:and a dark complexion.
+H:109:80:53:0:50:and an average complexion.
+H:110:90:53:0:50:and a fair complexion.
+H:111:100:53:0:50:and a very fair complexion.
+
+H:112:85:54:55:50:You have light grey eyes,
+H:113:95:54:55:50:You have light blue eyes,
+H:114:100:54:55:50:You have light green eyes,
+
+H:115:75:55:56:50:straight
+H:116:100:55:56:50:wavy
+
+H:117:75:56:0:50:black hair, and a fair complexion.
+H:118:85:56:0:50:brown hair, and a fair complexion.
+H:119:95:56:0:50:blond hair, and a fair complexion.
+H:120:100:56:0:50:silver hair, and a fair complexion.
+
+H:121:99:57:58:50:You have dark brown eyes,
+H:122:100:57:58:60:You have glowing red eyes,
+
+H:123:90:58:59:50:straight
+H:124:100:58:59:50:wavy
+
+H:125:75:59:60:50:black hair,
+H:126:100:59:60:50:brown hair,
+
+H:127:25:60:61:50:a one foot beard,
+H:128:60:60:61:51:a two foot beard,
+H:129:90:60:61:53:a three foot beard,
+H:130:100:60:61:55:a four foot beard,
+
+H:131:100:61:0:50:and a dark complexion.
+
+H:132:60:62:63:50:You have slime green eyes,
+H:133:85:62:63:50:You have puke yellow eyes,
+H:134:99:62:63:50:You have blue-bloodshot eyes,
+H:135:100:62:63:55:You have glowing red eyes,
+
+H:136:33:63:64:50:dirty
+H:137:66:63:64:50:mangy
+H:138:100:63:64:50:oily
+
+H:139:33:64:65:50:sea-weed green hair,
+H:140:66:64:65:50:bright red hair,
+H:141:100:64:65:50:dark purple hair,
+
+H:142:25:65:66:50:and green
+H:143:50:65:66:50:and blue
+H:144:75:65:66:50:and white
+H:145:100:65:66:50:and black
+
+H:146:33:66:0:50:ulcerous skin.
+H:147:66:66:0:50:scabby skin.
+H:148:100:66:0:50:leprous skin.
+
+H:149:85:69:70:45:You are one of several children of a Dark Elven
+H:150:100:69:70:55:You are the only child of a Dark Elven
+
+H:151:50:70:71:60:Warrior.
+H:152:80:70:71:75:Warlock.
+H:153:100:70:71:95:Noble.
+
+H:154:100:71:72:50:You have black eyes,
+
+H:155:70:72:73:50:straight
+H:156:90:72:73:50:wavy
+H:157:100:72:73:50:curly
+
+H:158:100:73:0:50:black hair and a very dark complexion.
+
+H:159:25:74:20:25:Your mother was an Ogre, but it is unacknowledged.
+H:160:100:74:20:25:Your father was an Ogre, but it is unacknowledged.
+
+H:161:90:75:76:50:You are a descendant of Beorn to the
+H:162:100:75:20:100:Your father was Beorn.
+
+H:163:13:76:20:55:9th degree.
+H:164:25:76:20:60:8th degree.
+H:165:38:76:20:65:7th degree.
+H:166:50:76:20:70:6th degree.
+H:167:63:76:20:75:5th degree.
+H:168:75:76:20:80:4th degree.
+H:169:88:76:20:85:3rd degree.
+H:170:100:76:20:90:2nd degree.
+
+H:171:100:78:79:50:You are one of several children of
+
+H:172:50:79:80:50:a Brown Yeek.
+H:173:75:79:80:50:a Blue Yeek.
+H:174:95:79:80:85:a Master Yeek.
+H:175:100:79:80:120:Boldor, the King of the Yeeks.
+
+H:176:25:80:81:50:You have pale eyes,
+H:177:50:80:81:50:You have glowing eyes,
+H:178:75:80:81:50:You have tiny black eyes,
+H:179:100:80:81:50:You have shining black eyes,
+
+H:180:20:81:65:50:no hair at all,
+H:181:40:81:65:50:short black hair,
+H:182:60:81:65:50:long black hair,
+H:183:80:81:65:50:bright red hair,
+H:184:100:81:65:50:colourless albino hair,
+
+H:185:100:82:83:50:You are one of several children of
+
+H:186:40:83:80:50:a Small Kobold.
+H:187:75:83:80:55:a Kobold.
+H:188:95:83:80:65:a Large Kobold.
+H:189:100:83:80:100:Mughash, the Kobold Lord.
+
+H:190:85:84:85:45:You are one of several children
+H:191:100:84:85:50:You are the first child
+
+H:192:60:85:50:40:of a Serf.
+H:193:85:85:50:55:of a Devoted Mercenary.
+H:194:96:85:50:60:of a Landed Knight.
+H:195:99:85:50:100:of a Marshal of the Riddermark.
+H:196:100:85:50:120:of a King of the Rohirrim.
+
+H:197:100:87:88:89:You are one of several children of
+
+H:198:30:88:18:20:a Petty-Dwarf Slave.
+H:199:50:88:18:40:a Petty-Dwarf Thief.
+H:200:70:88:18:60:a Petty-Dwarf Smith.
+H:201:90:88:18:75:a Petty-Dwarf Miner.
+H:202:95:88:18:100:a Petty-Dwarf Shaman.
+H:203:100:88:18:100:Mim, Betrayer of Turin.
+
+H:204:85:89:90:50:You are one of many Manwe Maia.
+H:205:100:89:90:60:You are the one of the most famous Manwe Maia.
+
+H:206:90:90:93:100:Your eagle looks very good.
+H:207:100:90:93:120:Your eagle is splendid.
+
+H:208:10:91:92:20:You are a unnoticed minion of
+H:209:25:91:92:30:You are a minor servant of
+H:210:45:91:92:40:You are a subject of
+H:211:65:91:92:50:You have attached yourself to
+H:212:85:91:92:65:You are associated with
+H:213:95:91:92:80:You are a notable follower of
+H:214:100:91:92:100:You are a celebrated assistant to
+
+H:215:20:92:93:55:Nessa.
+H:216:40:92:93:60:Vana.
+H:217:50:92:93:65:Tulkas.
+H:218:80:92:93:75:Mandos.
+H:219:90:92:93:80:Nienna.
+H:220:95:92:93:90:Varda.
+H:221:100:92:93:95:Manwe.
+
+H:222:100:93:94:50:In the past you dwelt on earth in the form of
+
+H:223:25:94:0:50:various animals.
+H:224:55:94:0:55:a spirit of forest and river.
+H:225:70:94:0:60:a beneficent but unseen force.
+H:226:96:94:0:70:a wise and ancient counsellor.
+H:227:100:94:0:80:a Wizard of legend.
+
+H:228:30:95:96:30:You are of an unknown generation of the Ents.
+H:229:40:95:96:50:You are of the third generation of the Ents.
+H:230:60:95:96:60:You are of the second generation of the Ents.
+H:231:100:95:96:80:You are one of the first beings who awoke on Arda.
+
+H:232:50:96:0:50:You have green skin and inflexible members.
+H:233:100:96:0:50:You have brown skin and inflexible members.
+
+H:234:10:100:101:30:You were born in dirty bilge-water,
+H:235:20:100:101:35:You were born in dirty straw,
+H:236:30:100:101:40:You were born in wet mud,
+H:237:40:100:101:45:You were born in a pile of dust,
+H:238:50:100:101:50:You were born in sand,
+H:239:60:100:101:50:You were born in pebbles,
+H:240:70:100:101:55:You were born in a kobold corpse,
+H:241:80:100:101:60:You were born in dragon droppings,
+H:242:90:100:101:65:You were born in a pile of bones,
+H:243:100:100:101:70:You were born in a corpse of a mighty hero,
+
+H:244:10:101:102:30:created by rotting flesh.
+H:245:20:101:102:35:created by a kobold magician.
+H:246:30:101:102:40:created by a corrupted apprentice.
+H:247:40:101:102:45:created by a curious mage apprentice.
+H:248:50:101:102:50:created by an evil Symbiant.
+H:249:60:101:102:50:created by a practicing Necromancer.
+H:250:70:101:102:55:created by the Mutant Breeders.
+H:251:80:101:102:60:created by a curious adventurer.
+H:252:90:101:102:65:called to life by the Witch-King of Angmar.
+H:253:100:101:102:70:called to life by Sauron himself.
+
+H:254:100:102:103:50:Since then you have given life to
+
+H:255:10:103:104:30:no
+H:256:20:103:104:35:one weak-willed
+H:257:30:103:104:40:two
+H:258:40:103:104:45:three
+H:259:50:103:104:50:four
+H:260:60:103:104:50:five
+H:261:70:103:104:55:about twenty
+H:262:80:103:104:60:dozens of
+H:263:90:103:104:65:hundreds of
+H:264:100:103:104:70:uncounted multitudes of
+
+H:265:100:104:0:50:foul offspring.
+
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# M:N:idx:color:Meta class name
+# M:C:class name
+
+I:
+
+M:N:0:U:Classes -- The Classes of Middle-earth
+M:C:Warrior
+M:C:Archer
+M:C:Rogue
+M:C:Mage
+M:C:Priest
+M:C:Loremaster
+#M:C:Test
+#M:C:Chaos-Warrior
+
+#M:N:1:B:Spellcasters -- Magic is The One True Way
+
+#M:N:2:y:Priests -- Hail the powers of the Ainur
+#M:C:Mindcrafter
+
+#M:N:3:G:Beastfriends -- Monsters are fun
+#M:C:BeastMaster
+
+#M:N:4:v:Others -- The way to your independence
+#M:C:Harper
+#M:C:Merchant
+
+#M:N:5:o:Tests -- Test is you dare !
+#M:C:Test
+#M:C:Blade
+#M:C:Black-Knight
diff --git a/lib/edit/qrand1.map b/lib/edit/qrand1.map
new file mode 100644
index 00000000..f42cbf1c
--- /dev/null
+++ b/lib/edit/qrand1.map
@@ -0,0 +1,32 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep lava
+F:F:85:12
+
+# Dungeon layout
+D:
+D: xxxxxxx
+D: xpGF..x
+D: xGGF,.D
+D: xFFF..x
+D: xxxxxxx
+D:
diff --git a/lib/edit/qrand10.map b/lib/edit/qrand10.map
new file mode 100644
index 00000000..ae45b9cb
--- /dev/null
+++ b/lib/edit/qrand10.map
@@ -0,0 +1,36 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:2056:0:0:0:0:*
+
+# Deep lava
+F:F:85:12
+
+# Dungeon layout
+D:
+D: xxxxxxx
+D: xFFFFFx
+D: xxxxxx,t.ttxxxx
+D: xFFG,t....t,,,xxx
+D: xFpG.......t.t.DD
+D: xFFG,t....t,,,xxx
+D: xxxxxx,t.ttxxxx
+D: xFFFFFx
+D: xxxxxxx
+D:
diff --git a/lib/edit/qrand11.map b/lib/edit/qrand11.map
new file mode 100644
index 00000000..4af3c266
--- /dev/null
+++ b/lib/edit/qrand11.map
@@ -0,0 +1,36 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep lava
+F:L:85:6
+
+# Shallow lava
+F:l:86:6
+
+# Dungeon layout
+D: ,llllllll ,
+D:, llLLLLLllll,
+D:llLLLGGGLLLlll,
+D:llLLLGpGLLLLlll
+D:lllLLGGGLLLllll
+D:lllllLLLLlllll,
+D:,lllllllllll,
+D: , ,llllll,
diff --git a/lib/edit/qrand12.map b/lib/edit/qrand12.map
new file mode 100644
index 00000000..4621ef0b
--- /dev/null
+++ b/lib/edit/qrand12.map
@@ -0,0 +1,36 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep water
+F:W:187:6
+
+# Shallow water
+F:w:84:6
+
+# Dungeon wayout
+D: ,wwwwwwww ,
+D:,,wwWWWWWwwww,
+D:wwWWWGGGWWWwww,
+D:wwWWWGpGWWWWwww
+D:wwwWWGGGWWWwwww
+D:wwwwwWWWWwwwww,
+D:,wwwwwwwwwww,
+D: , ,wwwwww,
diff --git a/lib/edit/qrand14.map b/lib/edit/qrand14.map
new file mode 100644
index 00000000..9f339db0
--- /dev/null
+++ b/lib/edit/qrand14.map
@@ -0,0 +1,37 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep water
+F:W:84:6
+
+# Shallow water
+F:w:187:6
+
+# Dungeon wayout
+D:
+D: xxxx xxxx
+D: x,,x x,,x
+D: xxDxxxxxxxDxx
+D: D,,,GpG,,,D
+D: xxDxxxxxxxDxx
+D: x,,x x,,x
+D: xxxx xxxx
+D:
diff --git a/lib/edit/qrand5.map b/lib/edit/qrand5.map
new file mode 100644
index 00000000..cc5d79ee
--- /dev/null
+++ b/lib/edit/qrand5.map
@@ -0,0 +1,27 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Dungeon layout
+D:
+D: xxxx.xxxx
+D: xx.......xx
+D: xxx..,...,..xxx
+D: xx.....GGG.....xx
+D: x......GpG......x
+D: xx..,..GGG..,..xx
+D: xxx....,....xxx
+D: xx.......xx
+D: xxxx.xxxx
+D:
diff --git a/lib/edit/qrand6.map b/lib/edit/qrand6.map
new file mode 100644
index 00000000..3b55e985
--- /dev/null
+++ b/lib/edit/qrand6.map
@@ -0,0 +1,37 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep water
+F:W:84:6
+
+# Shallow water
+F:w:187:6
+
+# Dungeon wayout
+D:
+D: xxxx xxxx
+D: xxxx xxxx
+D: xxxxxxxxxxDxx
+D: D,,,GpG,,,D
+D: xxxxxxxxxxDxx
+D: xxxx xxxx
+D: xxxx xxxx
+D:
diff --git a/lib/edit/qrand7.map b/lib/edit/qrand7.map
new file mode 100644
index 00000000..a7c0607f
--- /dev/null
+++ b/lib/edit/qrand7.map
@@ -0,0 +1,35 @@
+# Floor
+F:.:1:6
+
+# Marker
+F:,:172:6
+
+# Princess
+F:p:1:6:969
+
+# Lit permanent wall
+F:x:61:6
+
+# Lit glass wall
+F:G:188:6
+
+# Door
+F:D:38:6
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep water
+F:W:84:6
+
+# Shallow water
+F:w:187:6
+
+# Dungeon wayout
+D:ttttt
+D:tGGGt ,x,
+D:tGpGt x,x
+D:tGGGt ,x,
+D:ttttt
+D: ,x,
+D:
diff --git a/lib/edit/r_info.txt b/lib/edit/r_info.txt
new file mode 100644
index 00000000..e7801d09
--- /dev/null
+++ b/lib/edit/r_info.txt
@@ -0,0 +1,18978 @@
+# File: r_info.txt
+# With new monsters for Zangband 2.2 (or 2.3)
+# With new monsters for PernAngband 3.x.x
+# With lots of monsters for PernAngband 4.x.x
+# With Spirits for ToME 2.1.x
+
+# This file is used to initialize the "lib/raw/r_info.raw" file, which is
+# used to initialize the "monster race" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/raw/r_info.raw" file.
+
+# PernAngband notes:
+
+# Currently, "unique" monsters are just "special" monster races, with
+# the requirement that only one monster of that race can exist at a time,
+# and when it is killed, it can never again be generated.
+
+# ATTR_CLEAR monsters acquire their attr from the item/floor below them,
+# and use "white" for the recall window. See "cave.c" for info.
+
+# ATTR_MULTI monsters have a "flickering" attr, and use "violet" for the
+# recall window. See "cave.c" for info.
+
+# CHAR_CLEAR monsters use special symbols (.) as given below,
+# and use those symbols for the recall window. In theory, normally,
+# these monsters cannot be targetted, and when examined look like normal
+# floors, until the player "notices" them (perhaps check "awake").
+
+# CHAR_MULTI monsters use special symbols (!, ?, =) as given below,
+# and use those symbols for the recall window. In theory, normally,
+# these monsters cannot be targetted, and when examined look like normal
+# objects, until the player "notices" them (perhaps check "awake").
+
+# Note that there are (a few) normal monsters who are "violet" but not
+# ATTR_MULTI, and a lot of monsters which are "white" but not ATTR_CLEAR.
+
+# Note that currently both CHAR_CLEAR and CHAR_MULTI monsters are treated
+# as normal monsters that are just a little hard to see.
+
+# Note that the monster list underwent several changes for Angband 2.7.9,
+# including some monster name changes, some symbol redistributions, and
+# some color changes.
+
+# The Umber Hulk joined the Xorn/Xaren (X). The ticks (t) joined the
+# spiders (S). The townspeople (t) left the people (p). The "Jabberwock"
+# became the "Chaos beetle" (K). The major demons (&) became (U) and the
+# minor demons (I) became (u). Multiplying insects (fleas, fruit flies,
+# hummerhorns) became (I), visually "matching" the multiplying lice (l).
+# The "ant lions" (a) became "ants" (a). The mummified monsters (M)
+# joined the zombified monsters (z). The multi-headed hydras (M) left
+# the reptiles (R). The snakes (J) left the reptiles (R).
+
+# Some of the old "red" or "brown" monsters became "pink" if they lower
+# strength, while some of the old "fire" monsters became simply "red"
+# monsters. The "dragons" and "hounds" and related monsters underwent
+# a "color scheme regularization" ('w' = White/Cold, 's' = Black/Acid,
+# 'o' = Lite/Dark, 'r' = Red/Fire, 'g' = Green/Poison, 'b' = Blue/Elec,
+# 'u' = Brown/Earth/Force, 'D' = Dark/etc, 'W' = Stone/Inertia/Gravity/etc,
+# 'v' = Multihued/Chaos/Disenchantment/etc, 'y' = Gold/Sound, 'R' = Nexus,
+# 'G' = Nether, 'B' = Left-overs, and 'U' = Bronze/Confusion).
+
+# In several situations, two or more monsters with identical symbols and
+# colors were changed so that maximal information is conveyed by the symbol
+# and color.
+
+# The "people" (p), with more than 50 entries, got a new "color scheme"
+# ('w' = Paladin, 's' = Knight, 'o' = Mystic, 'r' = Mage, 'g' = High Priest,
+# 'b' = Thief, 'u' = Warrior, 'D' = Death knight, 'W' = Ranger/Archer,
+# 'v' = Sorcerer, 'y' = Ninja, 'R' = High Mage, 'G' = Priest, 'B' = High
+# Thief, 'U' = High Warrior). Note that most non-unique "people" already
+# had these colors, or colors close to these colors. A similar color scheme
+# was enforced for the "humanoid" (h) monsters as well, more or less.
+
+# TY: This is no longer entirely accurate. The monster coloring has been
+# changed 'back' to pre-2.7.* coloring in several cases. For example, I
+# prefer "black" thief characters. Also color can be (and should) be used to
+# convey information, but more importantly it is a visual presentation
+# of the creature and should be what the creature "looks" like.
+
+# Many of the "unique" monsters were changed to "match" the "base" monster
+# from which they were derived. Angband 2.8.0 may require every "unique"
+# monster to be based on a "normal" monster, and may enforce color matching.
+# This may result in the addition of some new monsters, to serve as "base"
+# monsters, possibly including Ogre captains, Greater Balrogs, Black Trolls,
+# Vampire Queens, Giant Werewolves, and others. This may be accompanied by
+# a separation of the monster list into a "normal" monster list (r_info)
+# with 512 entries, and a "unique" monster list (u_info) with 128 entries,
+# which will require reorganization of the list. Some new "player ghost"
+# unique monsters will probably be added at the same time.
+
+# Mushrooms look just like food (and use the "," symbol for both the recall
+# window and for normal display), Creeping coins look just like coins (and
+# use the "$" symbol for both the recall window and for normal display), and
+# Trappers/Lurkers can never be seen (and use the "." symbol for the recall
+# window). All other monsters use "alphabetic" symbols, and "alphabetic"
+# symbols are used only for monsters.
+
+# The "0" and "9" symbols are reserved for internal debugging use.
+
+# The "&" symbol is reserved for future use as a special "terrain feature".
+
+# The "`" symbol is reserved as an "alternate" open door picture, since
+# the "'" symbol looks like the "," symbol in some fonts (esp. IBM).
+
+# The "x" symbol is free for use as an "attr/char mapping" for annoying
+# monsters, such as magic mushrooms, drolems, etc.
+
+# Certain symbols ("X", "Y", "B", "l", "I", etc) are used by
+# very few monsters, and could be reorganized somewhat.
+
+# There are still too many "p" monsters, perhaps they should be broken up.
+
+# As always, you can enforce any "visual picture" you want with a "pref file".
+
+
+###### Understanding the entries ######
+
+# N: serial number : monster name
+# G: symbol : color
+# I: speed : hit points : vision : armor class : alertness
+# W: depth : rarity : corpse weight : experience for kill
+# E: weapons : torso : arms : finger : head : leg
+# O: treasure : combat : magic : tool
+# B: attack method : attack effect : damage
+# F: flag | flag | etc
+# S: spell frequency |
+# S: spell type | spell type | etc
+# D: Description
+
+# 'N' indicates the beginning of an entry. The serial number must
+# increase for each new item. Entry 0 is used for the player.
+
+# 'G' is for graphics - symbol and color. There are 16 colors, as
+# follows:
+
+# D - Dark Gray w - White s - Gray o - Orange
+# r - Red g - Green b - Blue u - Brown
+# d - Black W - Light Gray v - Violet y - Yellow
+# R - Light Red G - Light Green B - Light Blue U - Light Brown
+
+# 'I' is for information - speed, health, vision in tens of feet,
+# armor class, and alertness. 110 is normal speed. Alertness ranges
+# from 0 (ever vigilant for intruders) to 255 (prefers to ignore
+# intruders).
+
+# 'W' is for more information - level, rarity, corpse weight (expressed
+# in deci-pounds) [19 deci-pounds seem to get added to these number
+# in practice], and experience for killing.
+
+# 'E' is for equipment slots - weapon slots, torso slots (the "on body"
+# and "about body" equipment slots), arm slots (expressed in number
+# of pairs), finger slots, head slots, and leg slots (expressed
+# in number of pairs).
+
+# 'O' is for object drop chances - % chance to drop treasure, % chance
+# to drop combat items, % chance to drop magic type items (not
+# items of good blessing), and % chance to drop tools. The four
+# percents added together must be equal to or less than 100. If
+# the percent is less than 100, that is the chance for the monster
+# to drop junk.
+
+# 'B' is for blows - method of attack, effect of attack, and damage
+# from attack. There may be up to four of these lines; effect and
+# damage are optional.
+
+# 'S' is for spells. The first S: line must be S:1_IN_X with X the
+# number of monster turns, on average, before the monster will cast
+# one of its spells. X must not be zero.
+
+# 'F' is for flags. These are fairly self-explanatory. As many F:
+# lines may be used as are needed to specify all the flags and flags
+# are separated by the '|' symbol. The '|' symbol must also be used
+# to end all but the last line.
+
+# 'D' is for description. As many D: lines may be used as are needed
+# to describe the monster. Note that lines may need spaces at their
+# ends to prevent words from running together in the monster memory.
+
+
+# Note that monster zero is used for the "player" picture.
+
+# Version stamp (required)
+
+V:2.2.0
+
+##### The Player #####
+
+N:0:Player
+G:@:w
+E:1:1:1:2:1:1
+O:0:0:0:0
+
+##### Town monsters #####
+
+N:1:Filthy street urchin
+G:t:D
+I:110:1d4:4:1:40
+W:0:2:1200:0
+E:1:1:1:2:1:1
+O:1:1:1:1
+B:BEG:*
+B:TOUCH:EAT_GOLD
+F:MALE | EVIL | WILD_TOWN | WILD_ONLY |
+F:RAND_25 | FRIENDS |
+F:TAKE_ITEM | OPEN_DOOR | DROP_CORPSE | DROP_SKELETON |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He looks squalid and thoroughly revolting.
+
+N:2:Scrawny cat
+G:f:U
+I:110:1d2:30:1:10
+W:0:3:100:0
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d1
+F:RAND_25 | WILD_TOO | WILD_GRASS | WILD_TOWN | WILD_ONLY |
+F:ANIMAL | DROP_CORPSE | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:A skinny little furball with sharp claws and a menacing look.
+
+N:3:Sparrow
+G:B:U
+I:110:1d1:30:1:10
+W:0:3:90:0
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:RAND_25 | CAN_FLY | WILD_TOWN | WILD_ONLY |
+F:ANIMAL | DROP_SKELETON | HAS_EGG | IMPRESED |
+F:MORTAL | BASEANGBAND
+D:Utterly harmless, except when angry.
+
+N:4:Chaffinch
+G:B:r
+I:110:1d1:30:1:10
+W:0:3:80:0
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:RAND_25 | CAN_FLY | WILD_ONLY | WILD_WOOD | WILD_GRASS |
+F:ANIMAL | DROP_SKELETON | HAS_EGG | IMPRESED |
+F:MORTAL | BASEANGBAND
+D:Utterly harmless, except when angry.
+
+N:5:Wild rabbit
+G:r:U
+I:110:1d2:30:1:10
+W:0:3:100:0
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:RAND_50 | WILD_ONLY | WILD_GRASS | WILD_WOOD |
+F:ANIMAL | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:It is not a carnivore, but will defend itself if you stray too
+D:close.
+
+N:6:Woodsman
+G:t:g
+I:110:3d3:10:1:255
+W:0:1:1000:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d6
+F:MALE | WILD_ONLY | WILD_WOOD |
+F:RAND_25 | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | HAS_LITE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND
+D:He has a strong axe with a sharp edge.
+
+N:7:Scruffy little dog
+G:C:U
+I:110:1d3:20:1:5
+W:0:3:300:0
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:RAND_25 | DROP_SKELETON | DROP_CORPSE | WILD_TOWN | WILD_ONLY |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A thin flea-ridden mutt, growling as you get close.
+
+N:8:Farmer Maggot
+G:h:w
+I:110:35d10:40:10:3
+W:0:4:730:0
+E:0:1:1:2:1:1
+O:0:100:0:0
+#B:MOAN:*
+#B:MOAN:*
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE |
+F:FORCE_MAXHP | WILD_TOWN | WILD_ONLY | NO_TARGET
+F:NEVER_MOVE
+F:OPEN_DOOR | BASH_DOOR | SPECIAL_GENE
+F:NEUTRAL | NO_DEATH
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He's lost his dogs. He's had his mushrooms stolen. He's not a happy
+D:hobbit!
+
+N:9:Blubbering idiot
+G:t:W
+I:110:1d2:6:1:0
+W:0:1:1500:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:DROOL:*
+F:MALE | DROP_CORPSE | DROP_SKELETON | WILD_TOWN | WILD_ONLY |
+F:RAND_25 | DROP_1D2 | TAKE_ITEM |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He tends to blubber a lot.
+
+N:10:Boil-covered wretch
+G:t:g
+I:110:1d2:6:1:0
+W:0:1:1400:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:DROOL:*
+F:MALE | DROP_SKELETON | DROP_CORPSE | WILD_TOWN | WILD_ONLY |
+F:RAND_25 | DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:Ugly doesn't begin to describe him.
+
+N:11:Village idiot
+G:t:G
+I:120:4d4:6:1:0
+W:0:1:1400:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:DROOL:*
+F:MALE | DROP_CORPSE | DROP_SKELETON | WILD_TOWN | WILD_ONLY |
+F:RAND_25 | DROP_1D2 | TAKE_ITEM |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:Drooling and comical, but then, what do you expect?
+
+N:12:Pitiful-looking beggar
+G:t:U
+I:110:1d4:10:1:40
+W:0:1:1300:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BEG:*
+F:MALE | DROP_SKELETON | DROP_CORPSE |
+F:RAND_25 | WILD_TOWN | WILD_ONLY | DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:You just can't help feeling sorry for him.
+
+N:13:Mangy-looking leper
+G:t:u
+I:110:1d1:10:1:50
+W:0:1:1300:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BEG:*
+B:TOUCH:DISEASE
+F:MALE | DROP_CORPSE | DROP_SKELETON |
+F:RAND_25 | WILD_TOWN | WILD_ONLY | DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:You feel it isn't safe to touch him.
+
+N:14:Agent of the black market
+G:t:b
+I:110:2d8:10:8:99
+W:0:1:1200:0
+E:1:1:1:2:1:1
+O:25:50:20:5
+B:HIT:HURT:1d6
+B:TOUCH:EAT_ITEM
+B:INSULT:*
+F:MALE | DROP_CORPSE | DROP_SKELETON |
+F:DROP_60 | WILD_TOWN |
+F:WILD_SWAMP | WILD_WOOD | WILD_GRASS | WILD_MOUNTAIN | WILD_ONLY |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+D:He 'finds' new wares for the Black Market. From unwary adventurers...
+
+N:15:Singing, happy drunk
+G:t:y
+I:110:2d3:10:1:0
+W:0:1:1100:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BEG:*
+F:MALE |
+F:RAND_50 | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | WILD_TOWN | WILD_ONLY |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He makes you glad to be sober.
+
+N:16:Aimless-looking merchant
+G:t:o
+I:110:3d3:10:1:255
+W:0:1:1500:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d3
+F:MALE | RAND_50 |
+F:ONLY_GOLD | DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_TOWN | WILD_ONLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:The typical ponce around town, with purse jingling, and looking for more
+D:amulets of adornment to buy.
+
+N:17:Mean-looking mercenary
+G:t:r
+I:110:5d8:10:20:250
+W:0:1:1700:0
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d10
+F:MALE | DROP_SKELETON | DROP_CORPSE |
+F:RAND_50 | DROP_90 | WILD_GRASS | WILD_TOWN | WILD_WOOD | WILD_ONLY |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+D:No job is too low for him.
+
+N:18:Battle-scarred veteran
+G:t:B
+I:110:7d8:10:30:250
+W:0:1:1650:0
+E:1:1:1:2:1:1
+O:25:50:25:0
+B:HIT:HURT:2d6
+F:MALE | DROP_SKELETON | DROP_CORPSE |
+F:RAND_50 | DROP_90 | WILD_TOWN | WILD_ONLY |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He doesn't take to strangers kindly.
+
+N:19:Martti Ihrasaari
+G:P:w
+I:109:35d20:50:15:4
+W:0:4:2794:0
+E:0:1:1:2:1:1
+O:50:50:0:0
+B:SHOW:*
+B:SHOW:*
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | DROP_CORPSE | WILD_TOWN | WILD_ONLY |
+F:ONLY_ITEM | DROP_90 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:JOKEANGBAND | HAS_LITE
+D:He weighs 127 kg. He is the president of some remote country.
+
+##### Normal monsters #####
+
+N:20:Grey mold
+G:m:s
+I:110:1d2:2:1:0
+W:1:1:20:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:HURT:1d4
+B:SPORE:HURT:1d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+D:A small strange growth.
+
+N:21:Large white snake
+G:J:w
+I:100:3d6:4:30:99
+W:1:1:600:2
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+B:CRUSH:HURT:1d1
+F:RAND_50 | WILD_TOO |
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | HAS_EGG | MORTAL | BASEANGBAND
+D:It is about eight feet long.
+
+N:22:Grey mushroom patch
+G:,:s
+I:110:1d2:2:1:0
+W:1:1:10:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:CONFUSE:1d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:Yum! It looks quite tasty.
+
+N:23:Newt
+G:R:y
+I:110:2d6:8:12:30
+W:1:1:10:2
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:BITE:HURT:1d3
+F:WEIRD_MIND | CAN_SWIM | WILD_TOO | DROP_CORPSE |
+F:WILD_GRASS | WILD_WASTE | WILD_SHORE | WILD_SWAMP | WILD_MOUNTAIN |
+F:ANIMAL | HAS_EGG | MORTAL | BASEANGBAND
+D:A small, harmless lizard.
+
+N:24:Giant white centipede
+G:c:w
+I:110:3d5:7:10:40
+W:1:1:500:2
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+B:STING:HURT:1d2
+F:RAND_50 | DROP_SKELETON |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is about four feet long and carnivorous.
+
+N:25:White icky thing
+G:i:w
+I:110:2d5:12:7:10
+W:1:1:500:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:1d2
+F:RAND_50 | RAND_25 | CAN_SWIM |
+F:EMPTY_MIND | DROP_CORPSE | BASEANGBAND
+D:It is a smallish, slimy, icky creature.
+
+N:26:Clear icky thing
+G:i:B
+I:110:2d5:12:6:10
+W:1:1:500:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:1d2
+F:ATTR_CLEAR | CAN_SWIM |
+F:RAND_50 | RAND_25 |
+F:INVISIBLE | EMPTY_MIND | DROP_CORPSE | BASEANGBAND
+D:It is a smallish, slimy, icky, blobby creature.
+
+N:27:Giant white mouse
+G:r:w
+I:110:1d3:8:4:20
+W:1:1:600:1
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+F:RAND_50 | WILD_TOO | WILD_GRASS |
+F:CAN_SWIM |
+F:ANIMAL | DROP_CORPSE |
+F:MORTAL | BASEANGBAND |
+S:MULTIPLY
+D:It is about three feet long with large teeth.
+
+N:28:Large brown snake
+G:J:u
+I:100:4d6:4:35:99
+W:1:1:800:3
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:CRUSH:HURT:1d4
+F:RAND_25 | CAN_SWIM |
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | HAS_EGG | MORTAL | BASEANGBAND
+D:It is about eight feet long.
+
+N:29:Small kobold
+G:k:y
+I:110:2d7:20:16:10
+W:1:1:800:5
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:1d5
+F:DROP_60 | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a small, dog-headed humanoid figure.
+
+N:30:Kobold
+G:k:G
+I:110:3d7:20:16:10
+W:2:1:900:5
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:1d8
+F:DROP_60 | WILD_TOO |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a squat and ugly dog-headed humanoid.
+
+N:31:White worm mass
+G:w:w
+I:100:4d4:7:1:10
+W:1:1:30:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:POISON:1d2
+F:RAND_50 | RAND_25 | CAN_SWIM |
+F:STUPID | WEIRD_MIND |
+F:ANIMAL | IM_POIS | HURT_LITE | NO_FEAR
+F:MORTAL | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a large slimy mass of worms.
+
+N:32:Floating eye
+G:e:o
+I:110:3d6:2:6:10
+W:1:1:500:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:PARALYZE
+F:NEVER_MOVE | CAN_FLY | DROP_CORPSE |
+F:HURT_LITE | NO_FEAR | BASEANGBAND
+D:A disembodied eye, floating a few feet above the ground.
+
+N:33:Rock lizard
+G:R:U
+I:110:3d4:20:4:15
+W:1:1:100:2
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:ANIMAL | CAN_SWIM | WILD_TOO | WILD_MOUNTAIN |
+F:DROP_CORPSE | HAS_EGG |
+F:MORTAL | BASEANGBAND
+D:It is a small lizard with a hardened hide.
+
+N:34:Grid bug
+G:I:v
+I:110:2d4:10:2:10
+W:1:1:10:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:ELEC:1d4
+F:RAND_25 | FRIENDS | CAN_FLY |
+F:STUPID | WEIRD_MIND |
+F:ANIMAL | NO_FEAR | IM_ELEC | ZANGBAND
+D:A strange electric bug.
+
+N:35:Jackal
+G:C:U
+I:110:1d4:10:3:10
+W:1:1:400:1
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:FRIENDS |
+F:WILD_TOO | WILD_WOOD | WILD_GRASS | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is a yapping snarling dog, dangerous when in a pack.
+
+N:36:Soldier ant
+G:a:u
+I:110:2d5:10:3:10
+W:1:1:300:3
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON |
+F:ANIMAL | WILD_TOO | WILD_GRASS |
+F:MORTAL | BASEANGBAND
+D:A large ant with powerful mandibles.
+
+N:37:Fruit bat
+G:b:o
+I:120:1d6:20:3:10
+W:1:1:20:1
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:ANIMAL | CAN_FLY | WILD_TOO | WILD_WOOD | WILD_SWAMP | DROP_CORPSE
+F:MORTAL | BASEANGBAND | AI_ANNOY
+D:A fast-moving pest.
+
+N:38:Insect swarm
+G:I:u
+I:120:1d5:20:4:10
+W:1:1:100:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+B:STING:HURT:1d1
+F:ANIMAL | WEIRD_MIND | CAN_FLY | RAND_25 | WILD_TOO | WILD_GRASS |
+F:WILD_WOOD | WILD_SWAMP |
+F:MORTAL | SUSCEP_FIRE | BASEANGBAND | NO_CUT
+D:A lone insect may be harmless, but there's a whole swarm of
+D:them here!
+
+N:39:The Greater hell-beast
+G:U:s
+I:120:15d100:10:1:99
+W:1:16:2000:2500
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:*
+B:GAZE:*
+B:CRUSH:*
+F:EVIL | IM_ACID | IM_ELEC | IM_FIRE | IM_POIS | IM_COLD | UNIQUE |
+F:RES_NETH | RES_WATE | RES_PLAS | RES_DISE | RES_NEXU | NO_SLEEP | NO_CONF
+F:KILL_WALL | FORCE_MAXHP | CAN_SWIM | DROP_CORPSE | JOKEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:TPORT | BLINK | TELE_AWAY
+D:This unholy abomination will crush you. Flee while you can!
+
+N:40:Shrieker mushroom patch
+G:,:R
+I:110:1d1:4:1:0
+W:2:1:40:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | WILD_TOO | WILD_SWAMP | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:SHRIEK
+D:Yum! It looks quite tasty. It doesn't sound so nice, though...
+
+N:41:Blubbering icky thing
+G:i:W
+I:110:5d6:14:4:10
+W:2:1:400:8
+E:0:0:0:0:0:0
+O:20:20:20:20
+B:CRAWL:POISON:1d4
+B:CRAWL:EAT_FOOD
+B:DROOL:*
+B:DROOL:*
+F:RAND_50 | DROP_90 | CAN_SWIM | DROP_CORPSE |
+F:EMPTY_MIND | TAKE_ITEM | KILL_BODY |
+F:IM_POIS | BASEANGBAND
+D:It is a smallish, slimy, icky, hungry creature.
+
+N:42:Metallic green centipede
+G:c:g
+I:120:4d4:5:4:10
+W:3:1:500:5
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CRAWL:HURT:1d2
+F:RAND_50 | WILD_TOO | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is about four feet long and carnivorous.
+
+N:43:Novice warrior
+G:p:u
+I:110:9d4:20:16:5
+W:2:1:1600:6
+E:1:1:1:2:1:1
+O:25:50:0:20
+B:HIT:HURT:1d7
+B:HIT:HURT:1d6
+F:MALE |
+F:DROP_60 | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He looks inexperienced but tough.
+
+N:44:Novice rogue
+G:p:b
+I:110:8d4:20:12:5
+W:2:1:1400:6
+E:1:1:1:2:1:1
+O:50:25:0:20
+B:HIT:HURT:1d6
+B:TOUCH:EAT_GOLD
+F:MALE |
+F:DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_TOO |
+F:EVIL | MORTAL | BASEANGBAND
+D:A rather shifty individual.
+
+N:45:Novice priest
+G:p:g
+I:110:7d4:20:10:10
+W:2:1:1500:6
+E:1:1:1:2:1:1
+O:25:0:50:20
+B:HIT:HURT:1d5
+F:MALE |
+F:FORCE_SLEEP | GOOD | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_12 |
+S:HEAL | SCARE | CAUSE_1
+D:He is tripping over his priestly robes.
+
+N:46:Novice mage
+G:p:r
+I:110:6d4:20:6:5
+W:2:1:1400:6
+E:1:1:1:2:1:1
+O:25:0:70:0
+B:HIT:HURT:1d4
+F:MALE |
+F:FORCE_SLEEP | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_12 |
+S:BLINK | BLIND | CONF | MISSILE
+D:He is leaving behind a trail of dropped spell components.
+
+N:47:Yellow mushroom patch
+G:,:y
+I:110:1d1:2:1:0
+W:2:1:30:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:TERRIFY:1d6
+F:NEVER_MOVE | WILD_TOO | WILD_SWAMP |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+D:Yum! It looks quite tasty.
+
+N:48:White jelly
+G:j:w
+I:120:8d8:2:1:99
+W:2:1:2000:10
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:POISON:1d2
+F:NEVER_MOVE | CAN_SWIM |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It's a large pile of white flesh.
+
+N:49:Giant black ant
+G:a:D
+I:110:3d6:8:20:80
+W:2:1:500:8
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+F:RAND_25 |
+F:WEIRD_MIND | DROP_SKELETON |
+F:BASH_DOOR | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is about three feet long.
+
+N:50:Salamander
+G:R:o
+I:110:4d6:8:20:80
+W:2:1:100:10
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:1d3
+F:RAND_25 | CAN_SWIM | WILD_TOO | WILD_VOLCANO | DROP_CORPSE |
+F:ANIMAL | IM_FIRE | SUSCEP_COLD |
+F:MORTAL | BASEANGBAND
+D:A small black and orange lizard.
+
+N:51:White harpy
+G:H:w
+I:110:2d5:16:17:10
+W:2:1:500:5
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d1
+B:CLAW:HURT:1d1
+B:BITE:HURT:1d2
+F:FEMALE | CAN_FLY | WILD_TOO | WILD_MOUNTAIN |
+F:RAND_50 | DROP_CORPSE | ANIMAL | EVIL | MORTAL | BASEANGBAND
+D:A flying, screeching bird with a woman's face.
+
+N:52:Blue yeek
+G:y:b
+I:110:2d6:18:14:10
+W:2:1:700:4
+E:1:1:1:2:1:1
+O:25:0:0:55
+B:HIT:HURT:1d5
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:ANIMAL | IM_ACID |
+F:MORTAL | BASEANGBAND
+D:A small humanoid figure.
+
+N:53:Grip, Farmer Maggot's dog
+G:C:w
+I:120:7d5:30:30:0
+W:2:2:600:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:UNIQUE | SPECIAL_GENE
+F:FORCE_MAXHP | RAND_25 | DROP_CORPSE
+F:BASH_DOOR | ANIMAL
+F:MORTAL | BASEANGBAND
+D:A rather vicious dog belonging to Farmer Maggot. It thinks you are
+D:stealing mushrooms.
+
+N:54:Wolf, Farmer Maggot's dog
+G:C:w
+I:120:7d5:30:30:0
+W:2:2:650:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:UNIQUE | SPECIAL_GENE
+F:FORCE_MAXHP | RAND_25 | DROP_CORPSE
+F:BASH_DOOR
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A rather vicious dog belonging to Farmer Maggot. It thinks you are
+D:stealing mushrooms.
+
+N:55:Fang, Farmer Maggot's dog
+G:C:w
+I:120:7d5:30:30:0
+W:2:2:700:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:UNIQUE | SPECIAL_GENE
+F:FORCE_MAXHP | RAND_25 | DROP_CORPSE
+F:BASH_DOOR
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A rather vicious dog belonging to Farmer Maggot. It thinks you are
+D:stealing mushrooms.
+
+N:56:Giant green frog
+G:R:g
+I:110:2d8:12:8:30
+W:2:1:200:6
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+F:RAND_25 | WILD_ONLY | WILD_SHORE | WILD_SWAMP | DROP_CORPSE |
+F:BASH_DOOR |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is as big as a wolf.
+
+N:57:Freesia
+G:f:u
+I:120:6d5:30:30:0
+W:2:1:450:32
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d4
+F:UNIQUE |
+F:FORCE_MAXHP | DROP_SKELETON
+F:BASH_DOOR |
+F:ANIMAL
+F:MORTAL | ZANGBAND
+D:A striped housecat who enjoys hunting.
+
+N:58:Green worm mass
+G:w:g
+I:100:6d4:7:3:10
+W:2:1:40:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:ACID:1d3
+F:RAND_50 | RAND_25 |
+F:STUPID | WEIRD_MIND |
+F:ANIMAL | IM_ACID | CAN_SWIM |
+F:HURT_LITE | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a large slimy mass of worms.
+
+N:59:Large yellow snake
+G:J:y
+I:100:4d8:5:38:75
+W:2:1:1000:9
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:CRUSH:HURT:1d6
+F:RAND_25 | CAN_SWIM | WILD_TOO | DROP_SKELETON | DROP_CORPSE
+F:BASH_DOOR | HAS_EGG | ANIMAL | MORTAL | BASEANGBAND
+D:It is about ten feet long.
+
+N:60:Cave spider
+G:S:D
+I:120:2d6:8:16:80
+W:2:1:400:7
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+F:FRIENDS |
+F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | SPIDER | HURT_LITE |
+F:MORTAL | BASEANGBAND
+D:It is a black spider that moves in fits and starts.
+
+N:61:Crow
+G:B:s
+I:120:3d5:40:12:0
+W:2:2:300:8
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:BITE:HURT:1d3
+F:ANIMAL | WILD_TOO | WILD_WOOD | CAN_FLY | DROP_CORPSE
+F:MORTAL | HAS_EGG | BASEANGBAND
+D:It is a hooded crow, gray except for the black wings and head.
+
+N:62:Wild cat
+G:f:U
+I:120:3d5:40:12:0
+W:2:2:200:8
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+F:BASH_DOOR | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A larger than normal feline, hissing loudly. Its velvet claws conceal a
+D:fistful of needles.
+
+N:63:Smeagol
+G:h:B
+I:130:20d20:20:12:5
+W:3:2:670:16
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:TOUCH:EAT_GOLD
+F:UNIQUE | MALE | CAN_SWIM | DROP_SKELETON | DROP_CORPSE | DROP_CHOSEN |
+F:FORCE_MAXHP | CAN_SPEAK | SMART |
+F:RAND_50 | RAND_25 | WILD_TOO |
+F:ONLY_ITEM | DROP_90 | DROP_GOOD | DROP_GREAT |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | INVISIBLE
+F:EVIL | BASEANGBAND
+D:Usually known as Gollum. He's been sneaking, and he wants his 'precious.'
+
+N:64:Green ooze
+G:j:g
+I:120:3d4:8:16:80
+W:3:2:300:4
+E:0:0:0:0:0:0
+O:50:0:25:20
+B:CRAWL:ACID:1d3
+F:RAND_50 | RAND_25 | DROP_90 |
+F:STUPID | EMPTY_MIND |
+F:IM_ACID | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It's green and it's oozing.
+
+N:65:Poltergeist
+G:G:s
+I:130:2d5:8:15:10
+W:3:1:0:8
+E:0:0:0:0:0:0
+O:50:5:30:10
+B:TOUCH:TERRIFY
+F:RAND_50 | RAND_25 | CAN_FLY |
+F:DROP_60 | DROP_90 |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | TAKE_ITEM |
+F:EVIL | UNDEAD |
+F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLINK
+D:It is a ghastly, ghostly form.
+
+N:66:Yellow jelly
+G:j:y
+I:120:10d8:2:1:99
+W:3:1:2000:12
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:POISON:1d3
+F:NEVER_MOVE | CAN_SWIM |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:DRAIN_MANA
+D:It's a large pile of yellow flesh.
+
+N:67:Metallic blue centipede
+G:c:b
+I:120:4d5:6:6:15
+W:4:1:770:6
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CRAWL:HURT:1d3
+F:RAND_50 | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is about four feet long and carnivorous.
+
+N:68:Raven
+G:B:D
+I:120:4d5:40:12:0
+W:4:2:500:8
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:BITE:HURT:1d4
+F:ANIMAL | WILD_TOO | WILD_WOOD | CAN_FLY | DROP_CORPSE
+F:MORTAL | HAS_EGG | BASEANGBAND
+D:Larger than a crow, and pitch black.
+
+N:69:Giant white louse
+G:I:w
+I:120:1d1:6:5:10
+W:3:1:100:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:RAND_50 | RAND_25 | CAN_FLY |
+F:WEIRD_MIND | ANIMAL | MORTAL | BASEANGBAND
+S:MULTIPLY
+D:It is six inches long.
+
+N:70:Giant yellow centipede
+G:c:y
+I:110:3d6:8:12:30
+W:2:1:500:3
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:STING:HURT:1d3
+F:RAND_50 | DROP_SKELETON |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is about four feet long and carnivorous.
+
+N:71:Black naga
+G:n:D
+I:110:6d8:16:40:120
+W:3:1:1700:20
+E:0:0:0:0:1:0
+O:0:75:20:5
+B:CRUSH:HURT:1d8
+F:FEMALE |
+F:RAND_25 | DROP_60 | DROP_CORPSE |
+F:BASH_DOOR | CAN_SWIM |
+F:EVIL | MORTAL | BASEANGBAND
+D:A large black serpent's body with a female torso.
+
+N:72:Spotted mushroom patch
+G:,:o
+I:110:1d1:2:1:0
+W:3:1:30:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:POISON:2d4
+F:NEVER_MOVE | WILD_TOO | WILD_SWAMP |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:Yum! It looks quite tasty.
+
+N:73:Silver jelly
+G:j:W
+I:120:10d8:2:1:99
+W:3:2:2000:12
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:EAT_LITE:1d3
+B:TOUCH:EAT_LITE:1d3
+F:NEVER_MOVE | CAN_SWIM |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:DRAIN_MANA
+D:It is a large pile of silver flesh that sucks all light from its
+D:surroundings.
+
+N:74:Scruffy-looking hobbit
+G:h:s
+I:110:3d5:16:8:10
+W:3:1:1000:4
+E:1:1:1:2:1:1
+O:0:50:0:40
+B:HIT:HURT:1d4
+B:TOUCH:EAT_GOLD
+F:MALE |
+F:DROP_60 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL
+F:MORTAL | BASEANGBAND
+D:A short little guy, in bedraggled clothes. He appears to be looking
+D:for a good tavern.
+
+N:75:Giant white ant
+G:a:w
+I:110:3d6:8:16:80
+W:3:1:800:7
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | WILD_GRASS |
+F:ANIMAL | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:It is about two feet long and has sharp pincers.
+
+N:76:Yellow mold
+G:m:y
+I:110:8d8:2:10:99
+W:3:1:30:9
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:HURT:1d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange growth on the dungeon floor.
+
+N:77:Metallic red centipede
+G:c:r
+I:120:4d8:8:9:20
+W:5:1:800:10
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CRAWL:HURT:2d3
+F:RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_SKELETON
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is about four feet long and carnivorous.
+
+N:78:Yellow worm mass
+G:w:y
+I:100:4d8:7:4:10
+W:3:2:200:4
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:LOSE_DEX:1d3
+F:RAND_50 | RAND_25 | CAN_SWIM |
+F:STUPID | WEIRD_MIND |
+F:ANIMAL | HURT_LITE | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a large slimy mass of worms.
+
+N:79:Clear worm mass
+G:w:B
+I:100:4d4:7:1:10
+W:3:2:200:4
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:POISON:1d2
+F:ATTR_CLEAR | CAN_SWIM |
+F:RAND_50 | RAND_25 |
+F:STUPID | WEIRD_MIND | INVISIBLE |
+F:ANIMAL |
+F:IM_POIS | HURT_LITE | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a disgusting mass of poisonous worms.
+
+N:80:Radiation eye
+G:e:R
+I:110:3d6:2:6:10
+W:3:1:500:6
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:LOSE_STR:1d6
+F:NEVER_MOVE | CAN_FLY | DROP_CORPSE |
+F:HURT_LITE | NO_FEAR | BASEANGBAND | HAS_LITE
+S:1_IN_11 |
+S:DRAIN_MANA
+D:A disembodied eye, crackling with energy.
+
+N:81:Yellow light
+G:*:y
+I:120:2d6:8:12:30
+W:4:1:0:4
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:BLIND
+F:EMPTY_MIND | CAN_FLY | NONLIVING | SUSCEP_ELEC |
+F:BASEANGBAND | HAS_LITE | RAND_50 | RAND_25 | NO_CUT
+D:A fast-moving bright light, apparently totally random in its movement.
+
+N:82:Cave lizard
+G:R:u
+I:110:3d6:8:16:80
+W:4:1:100:8
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d5
+F:ANIMAL | CAN_SWIM | DROP_CORPSE | HAS_EGG |
+F:MORTAL | BASEANGBAND
+D:It is an armoured lizard with a powerful bite.
+
+N:83:Novice ranger
+G:p:W
+I:110:6d8:20:8:5
+W:4:1:1400:18
+E:1:1:1:2:1:1
+O:25:45:25:0
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+F:MALE |
+F:FORCE_SLEEP | DROP_SKELETON | DROP_CORPSE
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_WOOD |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:ARROW_2 | MISSILE
+D:An agile hunter, ready and relaxed.
+
+N:84:Blue jelly
+G:j:b
+I:110:12d8:2:1:99
+W:4:1:2000:14
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:COLD:1d6
+F:NEVER_MOVE | COLD_BLOOD |
+F:STUPID | EMPTY_MIND | CAN_SWIM |
+F:IM_COLD | HURT_LITE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | SUSCEP_FIRE | BASEANGBAND | NO_CUT
+D:It's a large pile of pulsing blue flesh.
+
+N:85:Creeping copper coins
+G:$:u
+I:100:7d8:3:24:10
+W:4:3:0:9
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d4
+B:TOUCH:POISON:2d4
+F:ONLY_GOLD | DROP_1D2 | SUSCEP_ACID |
+F:COLD_BLOOD | BASH_DOOR |
+F:IM_ELEC | IM_POIS | CHAR_MULTI |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It appears to be a pile of copper coins, until it starts crawling towards you
+D:on tiny legs.
+
+N:86:Giant white rat
+G:r:W
+I:110:2d2:8:7:30
+W:4:1:200:1
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d3
+F:RAND_25 |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:MULTIPLY
+D:It is a very vicious rodent.
+
+N:87:Snotling
+G:o:U
+I:110:5d5:20:32:30
+W:4:1:900:15
+E:1:1:1:2:1:1
+O:25:50:0:20
+B:HIT:HURT:1d6
+F:MALE |
+F:FRIENDS | DROP_60 | RAND_50 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_WOOD |
+F:EVIL | ORC | HURT_LITE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:Immature Orclings, running wild and screaming all the time.
+
+N:88:Swordfish
+G:~:W
+I:120:4d7:14:10:20
+W:4:2:800:15
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+F:ANIMAL | AQUATIC | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:A fish with a swordlike "beak".
+
+N:89:Blue worm mass
+G:w:b
+I:100:5d8:7:12:10
+W:4:1:40:5
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:COLD:1d4
+F:RAND_50 | RAND_25 |
+F:STUPID | WEIRD_MIND | COLD_BLOOD |
+F:ANIMAL | IM_COLD | CAN_SWIM |
+F:HURT_LITE | NO_FEAR |
+F:MORTAL | SUSCEP_FIRE | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a large slimy mass of worms.
+
+N:90:Large grey snake
+G:J:s
+I:100:6d8:6:41:50
+W:4:1:1300:14
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d5
+B:CRUSH:HURT:1d8
+F:RAND_25 | CAN_SWIM | DROP_SKELETON | DROP_CORPSE | WILD_TOO |
+F:BASH_DOOR | HAS_EGG | ANIMAL | MORTAL | BASEANGBAND
+D:It is about ten feet long.
+
+N:91:Skeleton kobold
+G:s:W
+I:110:5d8:20:26:40
+W:5:1:800:12
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d6
+F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a small animated kobold skeleton.
+
+N:92:Ewok
+G:h:G
+I:120:3d5:10:10:10
+W:9:2:700:20
+E:1:1:1:2:1:1
+O:50:0:50:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:DROP_60 | OPEN_DOOR | BASH_DOOR | FRIENDS | DROP_CORPSE |
+F:WILD_TOO | WILD_WOOD |
+F:MORTAL | JOKEANGBAND
+S:1_IN_8
+S:ARROW_1
+D:A cute little bear, full of merchandising potential.
+
+N:93:Novice mage
+G:p:r
+I:110:6d4:20:6:10
+W:6:2:1400:6
+E:1:1:1:2:1:1
+O:25:0:70:0
+B:HIT:HURT:1d4
+F:MALE |
+F:FORCE_SLEEP |
+F:FRIENDS | DROP_60 | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_12 |
+S:BLINK | BLIND | CONF | MISSILE
+D:He is leaving behind a trail of dropped spell components.
+
+N:94:Green naga
+G:n:g
+I:110:9d8:18:40:120
+W:5:1:1700:30
+E:0:0:0:0:1:0
+O:0:25:0:65
+B:CRUSH:HURT:1d8
+B:SPIT:ACID:2d6
+F:FEMALE |
+F:RAND_25 | TAKE_ITEM | DROP_60 | DROP_CORPSE |
+F:BASH_DOOR | CAN_SWIM | WILD_TOO | WILD_SHORE |
+F:EVIL | IM_ACID | MORTAL | BASEANGBAND
+D:A large green serpent with a female torso. Her green skin glistens with
+D:acid.
+
+N:95:Giant leech
+G:w:u
+I:120:6d8:10:20:50
+W:5:1:30:20
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:3d1
+B:BITE:HURT:3d1
+F:ANIMAL | AQUATIC | WEIRD_MIND | RAND_25 | BASEANGBAND
+D:Yech! The disgusting thing only wants your blood!
+
+N:96:Barracuda
+G:~:G
+I:120:6d8:20:45:20
+W:5:2:150:30
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d10
+B:BITE:HURT:1d10
+F:AQUATIC | ANIMAL | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:A predatory fish with razor-sharp teeth.
+
+N:97:Novice paladin
+G:p:w
+I:110:6d8:20:16:5
+W:4:1:1700:18
+E:1:1:1:2:1:1
+O:0:70:25:0
+B:HIT:HURT:1d7
+B:HIT:HURT:1d7
+F:MALE | GOOD | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_SLEEP |
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:SCARE | CAUSE_1
+D:An adventurer both devoutly religious and skilful in combat.
+D:He seems to consider you an agent of the devil.
+
+N:98:Zog
+G:h:b
+I:110:13d9:20:20:20
+W:5:1:600:25
+E:0:1:0:2:1:0
+O:50:0:25:20
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+B:DROOL:*
+F:EVIL | OPEN_DOOR | BASH_DOOR | DROP_90 | DROP_SKELETON |
+F:MORTAL | ZANGBAND
+D:Drooling, insectoid aliens with disgusting habits.
+
+N:99:Blue ooze
+G:j:b
+I:110:3d4:8:16:80
+W:5:1:300:7
+E:0:0:0:0:0:0
+O:45:20:20:0
+B:CRAWL:COLD:1d4
+F:RAND_50 | RAND_25 | DROP_60 |
+F:STUPID | EMPTY_MIND | CAN_SWIM |
+F:IM_COLD | SUSCEP_FIRE
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It's blue and it's oozing.
+
+N:100:Green glutton ghost
+G:G:g
+I:130:3d4:10:20:10
+W:5:1:0:15
+E:0:0:0:0:0:0
+O:30:30:30:5
+B:TOUCH:EAT_FOOD:1d1
+F:RAND_50 | RAND_25 |
+F:DROP_60 | DROP_90 | CAN_FLY |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It is a very ugly green ghost with a voracious appetite.
+
+N:101:Green jelly
+G:j:g
+I:120:22d8:2:1:99
+W:5:1:2500:18
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:ACID:1d2
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND | CAN_SWIM |
+F:IM_ACID | HURT_LITE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a large pile of pulsing green flesh.
+
+N:102:Large kobold
+G:k:b
+I:110:13d9:20:32:30
+W:5:1:1000:25
+E:1:1:1:2:1:1
+O:0:90:0:5
+B:HIT:HURT:1d10
+F:DROP_90 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It a man-sized figure with the all too recognisable face of a kobold.
+
+N:103:Grey icky thing
+G:i:s
+I:110:4d8:14:12:15
+W:5:1:500:10
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:1d5
+F:RAND_50 | CAN_SWIM | DROP_CORPSE |
+F:EMPTY_MIND | BASEANGBAND
+D:It is a smallish, slimy, icky, nasty creature.
+
+N:104:Disenchanter eye
+G:e:v
+I:100:7d8:2:10:10
+W:5:2:500:20
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:UN_BONUS
+F:ATTR_MULTI | ATTR_ANY | RES_DISE | DROP_CORPSE |
+F:NEVER_MOVE | CAN_FLY |
+F:HURT_LITE | NO_FEAR | BASEANGBAND
+S:1_IN_9 |
+S:DRAIN_MANA
+D:A disembodied eye, crackling with magic.
+
+N:105:Red worm mass
+G:w:r
+I:100:5d8:7:12:10
+W:5:1:40:6
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:FIRE:1d6
+F:RAND_50 | RAND_25 | SUSCEP_COLD |
+F:STUPID | EMPTY_MIND | BASH_DOOR |
+F:ANIMAL | IM_FIRE | CAN_SWIM |
+F:HURT_LITE | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a large slimy mass of worms.
+
+N:106:Copperhead snake
+G:J:o
+I:110:4d6:6:20:1
+W:5:1:200:15
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:POISON:2d4
+F:RAND_50 | CAN_SWIM | WILD_TOO | DROP_SKELETON | DROP_CORPSE | BASH_DOOR |
+F:ANIMAL | IM_POIS | HAS_EGG | MORTAL | BASEANGBAND
+D:It has a copper head and sharp venomous fangs.
+
+N:107:Death sword
+G:|:W
+I:130:6d6:20:40:0
+W:6:5:0:30
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:NEVER_MOVE | NONLIVING | NO_FEAR | SUSCEP_ACID |
+F:STUPID | EMPTY_MIND | COLD_BLOOD | CHAR_MULTI | NO_CONF | NO_SLEEP |
+F:DROP_90 | EVIL | IM_COLD | IM_FIRE | FORCE_MAXHP | IM_ELEC | IM_POIS |
+F:BASEANGBAND | HAS_LITE | NO_CUT
+D:A bloodthirsty blade lurking for prey. Beware!
+
+N:108:Purple mushroom patch
+G:,:v
+I:110:1d1:2:1:0
+W:6:2:40:15
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:LOSE_CON:1d2
+B:SPORE:LOSE_CON:1d2
+B:SPORE:LOSE_CON:1d2
+F:NEVER_MOVE | CAN_SWIM |
+F:STUPID | EMPTY_MIND |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:Yuk! It doesn't look so tasty.
+
+N:109:Novice priest
+G:p:g
+I:110:7d4:20:10:5
+W:6:2:1500:6
+E:1:1:1:2:1:1
+O:20:50:20:5
+B:HIT:HURT:1d5
+F:MALE | GOOD |
+F:FORCE_SLEEP | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_12 |
+S:HEAL | SCARE | CAUSE_1
+D:He is tripping over his priestly robes.
+
+N:110:Novice warrior
+G:p:u
+I:110:9d4:20:16:5
+W:6:2:1600:6
+E:1:1:1:2:1:1
+O:0:95:0:0
+B:HIT:HURT:1d7
+B:HIT:HURT:1d6
+F:MALE |
+F:FRIENDS | DROP_60 | WILD_TOO |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He looks inexperienced but tough.
+
+N:111:Nibelung
+G:h:D
+I:110:8d4:20:12:5
+W:6:1:900:6
+E:1:1:1:2:1:1
+O:90:0:0:5
+B:HIT:HURT:1d6
+B:TOUCH:EAT_GOLD
+F:MALE |
+F:FRIENDS | DROP_60 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | HURT_LITE | RES_DISE | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | ZANGBAND | HAS_LITE
+D:Night dwarfs collecting riches.
+
+N:112:The disembodied hand that strangled people
+G:z:g
+I:130:7d8:30:15:20
+W:6:2:300:20
+E:0:0:0:1:0:0
+O:0:0:0:0
+B:CRUSH:HURT:1d8
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_POIS | CAN_FLY | UNIQUE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | ZANGBAND | NO_CUT
+D:Even today, nobody knows where it lurks...
+
+N:113:Brown mold
+G:m:u
+I:110:15d8:2:12:99
+W:6:1:50:20
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:CONFUSE:1d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A strange brown growth on the dungeon floor.
+
+N:114:Giant brown bat
+G:b:u
+I:130:3d8:10:15:30
+W:6:1:600:10
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+F:RAND_50 | CAN_FLY | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD |
+F:ANIMAL | DROP_CORPSE | AI_ANNOY
+F:MORTAL | BASEANGBAND
+D:It screeches as it attacks.
+
+N:115:Rat-thing
+G:r:R
+I:120:9d9:12:20:20
+W:6:1:1000:10
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:3d2
+B:BITE:HURT:3d1
+B:BITE:HURT:3d2
+F:EVIL | ANIMAL | DROP_CORPSE |
+F:MORTAL | ZANGBAND
+S:1_IN_9
+S:SCARE | CONF
+D:A ratlike creature with a humanlike face.
+
+N:116:Novice rogue
+G:p:b
+I:110:8d4:20:12:5
+W:6:2:1400:6
+E:1:1:1:2:1:1
+O:50:25:0:20
+B:HIT:HURT:1d6
+B:TOUCH:EAT_GOLD
+F:MALE | FRIENDS |
+F:DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_TOO |
+F:EVIL | MORTAL | BASEANGBAND
+D:A rather shifty individual.
+
+N:117:Creeping silver coins
+G:$:s
+I:100:12d8:4:30:10
+W:6:3:0:18
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d6
+B:TOUCH:POISON:2d6
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:COLD_BLOOD | BASH_DOOR | SUSCEP_ACID | CHAR_MULTI |
+F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It appears to be a pile of silver coins, until it starts crawling towards you
+D:on tiny legs.
+
+N:118:Snaga
+G:o:U
+I:110:8d8:20:32:30
+W:6:1:1600:15
+E:1:1:1:2:1:1
+O:20:50:5:15
+B:HIT:HURT:1d8
+F:MALE |
+F:FRIENDS | DROP_60 | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | HURT_LITE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is one of the many weaker 'slave' orcs, often mistakenly known as a
+D:goblin.
+
+N:119:Rattlesnake
+G:J:r
+I:110:6d7:6:24:1
+W:6:1:200:20
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:POISON:2d5
+F:RAND_50 | CAN_SWIM | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:BASH_DOOR | HAS_EGG | ANIMAL | IM_POIS | MORTAL | BASEANGBAND
+D:It is recognised by the hard-scaled end of its body that is often rattled
+D:to frighten its prey.
+
+N:120:Giant slug
+G:w:U
+I:100:12d9:10:25:25
+W:6:1:600:25
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:ACID:2d4
+B:BITE:ACID:2d6
+F:ANIMAL | EMPTY_MIND | KILL_ITEM | KILL_BODY | CAN_SWIM | WILD_TOO |
+F:DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_10
+S:BR_ACID
+D:It is slowly making its way towards you, eating everything in
+D:its path...
+
+N:121:Giant pink frog
+G:R:r
+I:110:5d8:12:16:50
+W:7:1:200:16
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:LOSE_STR:2d4
+F:RAND_50 | WILD_ONLY | WILD_SHORE | WILD_SWAMP |
+F:BASH_DOOR | DROP_CORPSE
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It looks poisonous.
+
+N:122:Dark elf
+G:h:D
+I:110:7d10:20:16:20
+W:7:2:1200:25
+E:1:1:1:2:1:1
+O:20:20:50:10
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:MALE |
+F:FORCE_SLEEP |
+F:DROP_90 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_10 |
+S:CONF | DARKNESS | MISSILE
+D:An elven figure with jet black skin and white hair, his eyes are large and
+D:twisted with evil.
+
+N:123:Zombified kobold
+G:z:s
+I:110:6d8:20:14:30
+W:7:1:750:14
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d2
+B:HIT:HURT:1d2
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is an animated kobold corpse. Flesh falls off in large chunks as it
+D:shambles forward.
+
+N:124:Crypt creep
+G:s:D
+I:110:6d8:20:12:14
+W:7:2:0:25
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:1d2
+B:CLAW:HURT:1d2
+B:BITE:POISON
+F:RAND_25
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | FRIENDS |
+F:EVIL | UNDEAD | IM_POIS | IM_COLD |
+F:NO_CONF | NO_SLEEP | HURT_LITE | BASEANGBAND | NO_CUT
+S:1_IN_10
+S:CAUSE_1 | S_UNDEAD
+D:A frightening skeletal figure in a black robe.
+
+N:125:Rotting corpse
+G:z:R
+I:110:8d8:20:20:20
+W:8:1:0:15
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:POISON:1d3
+B:CLAW:POISON:1d3
+F:OPEN_DOOR | BASH_DOOR | FRIENDS |
+F:NO_CONF | NO_SLEEP | UNDEAD | EVIL | NO_FEAR | IM_POIS |
+F:IM_COLD | COLD_BLOOD | EMPTY_MIND | BASEANGBAND | NO_CUT
+D:Corpses awakened from their sleep by dark sorcery.
+
+N:126:Cave orc
+G:o:G
+I:110:11d9:20:32:30
+W:7:1:1900:20
+E:1:1:1:2:1:1
+O:20:70:0:0
+B:HIT:HURT:1d8
+F:MALE |
+F:FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | HURT_LITE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is often found in huge numbers in deep caves.
+
+N:127:Wood spider
+G:S:U
+I:120:3d6:8:16:80
+W:7:3:600:15
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:STING:POISON:1d4
+F:FRIENDS | DROP_SKELETON |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | WILD_WOOD |
+F:ANIMAL | SPIDER | IM_POIS |
+F:MORTAL | BASEANGBAND
+D:It scuttles towards you.
+
+N:128:Manes
+G:u:r
+I:110:8d8:20:32:30
+W:7:2:300:16
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d8
+F:FRIENDS |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | BASEANGBAND
+D:It is a minor but aggressive demon.
+
+N:129:Bloodshot eye
+G:e:r
+I:110:10d8:2:6:10
+W:7:3:550:30
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:BLIND:2d6
+F:NEVER_MOVE | CAN_FLY | DROP_CORPSE |
+F:HURT_LITE | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:DRAIN_MANA
+D:A disembodied eye, bloodshot and nasty.
+
+N:130:Red naga
+G:n:r
+I:110:11d8:20:40:120
+W:7:2:1800:40
+E:0:0:0:0:1:0
+O:50:0:50:0
+B:CRUSH:HURT:1d10
+B:BITE:LOSE_STR:1d4
+F:FEMALE | CAN_SWIM | WILD_TOO | WILD_SHORE |
+F:RAND_25 | DROP_60 |
+F:TAKE_ITEM | BASH_DOOR | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND
+D:A large red snake with a woman's torso.
+
+N:131:Red jelly
+G:j:r
+I:110:26d8:2:1:99
+W:7:1:2500:26
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:LOSE_STR:1d5
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:HURT_LITE | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a large pulsating mound of red flesh.
+
+N:132:Green icky thing
+G:i:g
+I:110:5d8:14:12:20
+W:7:2:500:18
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:ACID:2d5
+F:RAND_50 | CAN_SWIM | DROP_CORPSE |
+F:EMPTY_MIND |
+F:IM_ACID | BASEANGBAND
+D:It is a smallish, slimy, icky, acidic creature.
+
+N:133:Lost soul
+G:G:W
+I:110:2d8:12:10:10
+W:7:2:0:18
+E:0:0:0:0:0:0
+O:60:0:25:0
+B:HIT:HURT:2d2
+B:TOUCH:LOSE_WIS
+F:RAND_50 | DROP_60 | DROP_90 | CAN_FLY |
+F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL |
+F:EVIL | UNDEAD |
+F:IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:TPORT | DRAIN_MANA
+D:It is almost insubstantial.
+
+N:134:Night lizard
+G:R:b
+I:110:4d8:20:16:30
+W:7:2:400:35
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+B:BITE:HURT:1d6
+F:ANIMAL | CAN_SWIM | WILD_TOO | DROP_CORPSE
+F:MORTAL | HAS_EGG | BASEANGBAND
+D:It is a black lizard with overlapping scales and a powerful jaw.
+
+N:135:Mughash, the Kobold Lord
+G:k:v
+I:110:17d10:20:20:20
+W:7:3:1100:100
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+F:UNIQUE | MALE | CAN_SPEAK
+F:FORCE_MAXHP |
+F:ESCORT | ESCORTS | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:Strong and powerful, for a kobold.
+
+N:136:Skeleton orc
+G:s:W
+I:110:10d8:20:36:40
+W:8:1:1700:26
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:2d5
+F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | UNDEAD |
+F:IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is an animated orc skeleton.
+
+N:137:Wormtongue, Agent of Saruman
+G:p:B
+I:110:28d10:20:30:20
+W:9:2:1500:150
+E:1:1:1:2:1:1
+O:10:50:35:0
+B:HIT:HURT:1d7
+B:HIT:HURT:1d7
+B:TOUCH:EAT_GOLD
+B:INSULT:*
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_GREAT |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON |
+F:EVIL | RES_TELE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:HEAL | SLOW | TRAPS | BO_COLD | BA_POIS
+D:He's been spying for Saruman. He is a snivelling wretch with no morals.
+
+N:138:Robin Hood, the Outlaw
+G:p:G
+I:120:16d12:20:30:20
+W:10:2:1600:150
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+B:TOUCH:EAT_GOLD
+B:TOUCH:EAT_ITEM
+F:UNIQUE | MALE | FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_GREAT | WILD_TOO | WILD_WOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | EVIL | DROP_SKELETON | DROP_CORPSE
+F:MORTAL | ZANGBAND | HAS_LITE
+S:1_IN_5
+S:ARROW_2 | HEAL | TRAPS
+D:The legendary archer steals from the rich (you qualify).
+
+N:139:Nurgling
+G:u:o
+I:110:9d8:20:32:30
+W:8:2:800:19
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BITE:DISEASE:1d8
+F:FRIENDS | FRIEND |
+F:OPEN_DOOR | BASH_DOOR | IM_POIS |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | ZANGBAND
+D:It is a minor demon servitor of Nurgle. It looks like a hairless
+D:teddy bear, with twisted eyes and rotting, ghoulish skin.
+
+N:140:Lagduf, the Snaga
+G:o:y
+I:110:22d10:20:32:30
+W:8:2:1700:80
+E:1:1:1:2:1:1
+O:10:80:0:0
+B:HIT:HURT:1d11
+B:HIT:HURT:1d11
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+F:UNIQUE | MALE | EVIL | ORC | FORCE_MAXHP | ESCORT |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | SPECIAL_GENE |
+F:OPEN_DOOR | BASH_DOOR | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A captain of a regiment of weaker orcs, Lagduf keeps his troop in order
+D:with displays of excessive violence.
+
+N:141:Brown yeek
+G:y:u
+I:110:4d8:18:18:10
+W:8:1:800:11
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:1d6
+F:DROP_60 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | IM_ACID |
+F:MORTAL | BASEANGBAND
+D:It is a strange small humanoid.
+
+N:142:Novice ranger
+G:p:W
+I:110:6d8:20:8:5
+W:8:2:1500:18
+E:1:1:1:2:1:1
+O:0:80:0:15
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+F:MALE |
+F:FORCE_SLEEP | FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:ARROW_2 | MISSILE
+D:An agile hunter, ready and relaxed.
+
+N:143:Giant salamander
+G:R:R
+I:110:6d7:6:40:1
+W:8:1:600:50
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:3d6
+F:FORCE_SLEEP | SUSCEP_COLD |
+F:RAND_25 |
+F:ANIMAL | IM_FIRE | CAN_SWIM | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_9
+S:BR_FIRE
+D:A large black and yellow lizard. You'd better run away!
+
+N:144:Space monster
+G:.:d
+I:110:21d8:30:14:20
+W:8:2:0:28
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:TERRIFY:1d4
+F:PASS_WALL | NO_CONF | NO_SLEEP | NONLIVING | IM_ACID | CAN_FLY | JOKEANGBAND | NO_CUT
+D:A black hole in the fabric of reality.
+
+N:145:Carnivorous flying monkey
+G:H:R
+I:110:20d8:30:20:20
+W:8:2:800:30
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:1d8
+B:CLAW:HURT:1d8
+B:BITE:HURT:5d1
+F:ANIMAL | CAN_FLY | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN |
+F:DROP_CORPSE | MORTAL | ZANGBAND | HAS_LITE
+D:It looks fantastic, yet frightening.
+
+N:146:Green mold
+G:m:g
+I:110:21d8:2:14:75
+W:8:1:40:28
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:TERRIFY:1d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_ACID | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange growth on the dungeon floor.
+
+N:147:Novice paladin
+G:p:w
+I:110:6d8:20:16:5
+W:8:2:1500:18
+E:1:1:1:2:1:1
+O:30:55:10:0
+B:HIT:HURT:1d7
+B:HIT:HURT:1d7
+F:MALE | GOOD | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_SLEEP |
+F:FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:SCARE | CAUSE_1
+D:He thinks you are an agent of the devil.
+
+N:148:Lemure
+G:u:U
+I:110:13d9:20:32:30
+W:8:3:1000:16
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:HIT:HURT:1d8
+F:FRIENDS |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | BASEANGBAND
+D:It is the larval form of a major demon.
+
+N:149:Hill orc
+G:o:u
+I:110:13d9:20:32:30
+W:8:1:2000:25
+E:1:1:1:2:1:1
+O:10:70:10:0
+B:HIT:HURT:1d10
+F:MALE |
+F:FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | ORC | HURT_LITE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is a hardy well-weathered survivor.
+
+N:150:Bandit
+G:p:b
+I:110:8d8:20:24:10
+W:10:2:1500:26
+E:1:1:1:2:1:1
+O:25:60:0:0
+B:HIT:HURT:2d4
+B:TOUCH:EAT_GOLD
+F:MALE |
+F:DROP_1D2 | WILD_TOO | WILD_WOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+D:He is after your possessions!
+
+N:151:Hunting hawk
+G:B:u
+I:120:8d8:30:25:10
+W:8:2:800:22
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d4
+F:ANIMAL | NO_FEAR | CAN_FLY | WILD_WOOD | WILD_TOO | DROP_CORPSE
+F:MORTAL | HAS_EGG | BASEANGBAND
+D:Trained to hunt and kill without fear.
+
+N:152:Phantom warrior
+G:G:B
+I:110:5d5:20:10:40
+W:8:1:0:15
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d11
+B:HIT:HURT:1d11
+F:PASS_WALL | NO_SLEEP | FRIENDS | COLD_BLOOD | NONLIVING |
+F:NO_FEAR | EMPTY_MIND | CAN_FLY | BASEANGBAND | NO_CUT
+D:Spectral creatures that are half real, half illusion.
+
+N:153:Gremlin
+G:u:u
+I:110:5d5:30:30:20
+W:8:3:500:6
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:EAT_FOOD:1d2
+B:CLAW:EAT_FOOD:1d2
+B:BITE:EAT_FOOD:1d3
+F:IM_POIS | HURT_LITE | EVIL | DEMON | OPEN_DOOR |
+F:TAKE_ITEM | CAN_SWIM |
+F:MORTAL | ZANGBAND
+S:MULTIPLY
+D:Don't feed them after midnight!
+
+N:154:Yeti
+G:Y:w
+I:110:11d9:20:24:10
+W:9:3:3500:30
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d4
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_MOUNTAIN | DROP_CORPSE |
+F:ANIMAL | IM_COLD |
+F:MORTAL | SUSCEP_FIRE | BASEANGBAND
+D:A large white figure covered in shaggy fur.
+
+N:155:Bloodshot icky thing
+G:i:r
+I:110:7d8:14:18:20
+W:9:3:60:24
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:1d4
+B:CRAWL:ACID:2d4
+F:RAND_50 |
+F:EMPTY_MIND | CAN_SWIM | DROP_CORPSE |
+F:IM_POIS | BASEANGBAND
+S:1_IN_11 |
+S:DRAIN_MANA
+D:It is a strange, slimy, icky creature.
+
+N:156:Giant grey rat
+G:r:s
+I:110:2d3:8:12:20
+W:9:1:250:2
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d4
+F:RAND_25 |
+F:ANIMAL | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:MULTIPLY
+D:It is a rodent of unusual size.
+
+N:157:Black harpy
+G:H:D
+I:120:3d8:16:22:10
+W:9:1:600:19
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d2
+B:CLAW:HURT:1d2
+B:BITE:HURT:1d3
+F:FEMALE | CAN_FLY | WILD_TOO | WILD_MOUNTAIN | DROP_CORPSE |
+F:RAND_25 | ANIMAL | EVIL | MORTAL | BASEANGBAND
+D:A woman's face on the body of a vicious black bird.
+
+N:158:Skaven
+G:r:G
+I:110:11d8:15:25:20
+W:9:1:600:20
+E:1:1:1:2:1:1
+O:35:35:20:0
+B:HIT:HURT:1d4
+B:HIT:HURT:1d4
+F:EVIL | FRIENDS | DROP_60 | DROP_90 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | MALE | WILD_TOO | WILD_WASTE | WILD_SWAMP |
+F:MORTAL | ZANGBAND
+D:A mutated rat-creature from the great waste, it is vaguely
+D:humanoid in appearance and walks on its hind legs. This race
+D:serves chaos fervently and is greatly feared by others.
+
+N:159:The wounded bear
+G:q:r
+I:110:11d10:10:35:10
+W:12:1:3000:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d10
+B:CLAW:HURT:1d10
+B:BITE:HURT:1d11
+F:BASH_DOOR | FORCE_MAXHP | FORCE_SLEEP | UNIQUE | DROP_CORPSE |
+F:ANIMAL | WILD_ONLY | WILD_WOOD | WILD_GRASS | WILD_MOUNTAIN |
+F:MORTAL | BASEANGBAND
+D:A wounded bear, who has occasionally attacked humans.
+
+N:160:Cave bear
+G:q:u
+I:110:8d8:10:35:10
+W:9:1:3000:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d6
+B:CLAW:HURT:1d6
+B:BITE:HURT:1d8
+F:BASH_DOOR | FORCE_MAXHP | FORCE_SLEEP | DROP_CORPSE |
+F:ANIMAL | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN |
+F:MORTAL | BASEANGBAND
+D:A large bear appears to have made its home in this cave. It is hungry,
+D:and you are trespassing in its territory.
+
+N:161:Rock mole
+G:r:s
+I:110:10d10:20:30:75
+W:9:2:60:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d10
+B:BITE:HURT:1d10
+F:WEIRD_MIND | BASH_DOOR | KILL_WALL | KILL_ITEM | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:Despite its unimpressive size, this mole creature has fangs powerful
+D:enough to bore through solid rock.
+
+N:162:Mindcrafter
+G:p:y
+I:110:9d8:20:15:20
+W:16:2:1700:50
+E:1:1:1:2:1:1
+O:30:40:30:0
+B:HIT:HURT:2d4
+B:HIT:HURT:2d4
+F:MALE |
+F:FORCE_SLEEP | DROP_90 | WILD_TOO |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:CONF | BLIND | HOLD | SLOW | MIND_BLAST | S_MONSTER | BLINK
+D:A master of the mental arts, able to damage or dominate the
+D:minds of others.
+
+N:163:Baby blue dragon
+G:d:b
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 | HAS_EGG | IMPRESED |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | DRAGON | IM_ELEC | BASEANGBAND | ATTR_MULTI | ATTR_MULTI
+S:1_IN_12 |
+S:BR_ELEC
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a pale blue.
+
+N:164:Baby white dragon
+G:d:w
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | ATTR_MULTI
+F:EVIL | DRAGON | IM_COLD | SUSCEP_FIRE | HAS_EGG | IMPRESED | BASEANGBAND
+F:ATTR_MULTI
+S:1_IN_12 |
+S:BR_COLD
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a pale white.
+
+N:165:Baby green dragon
+G:d:g
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY | DROP_CORPSE |
+F:EVIL | DRAGON | IM_POIS | HAS_EGG | IMPRESED | BASEANGBAND | ATTR_MULTI
+S:1_IN_12 |
+S:BR_POIS
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a sickly green.
+
+N:166:Baby black dragon
+G:d:s
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_ACID | HAS_EGG | IMPRESED | BASEANGBAND | ATTR_MULTI
+S:1_IN_12 |
+S:BR_ACID
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a dull black.
+
+N:167:Baby red dragon
+G:d:r
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE | SUSCEP_COLD |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_FIRE | HAS_EGG | IMPRESED | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_11 |
+S:BR_FIRE
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a pale red.
+
+N:168:Giant red ant
+G:a:r
+I:110:4d8:12:34:60
+W:9:2:600:22
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:STING:LOSE_STR:1d4
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_SKELETON
+F:ANIMAL | MORTAL | BASEANGBAND | HAS_LITE
+D:It is large and has venomous mandibles.
+
+N:169:Brodda, the Easterling
+G:p:U
+I:110:24d10:20:25:20
+W:9:2:2200:100
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:1d13
+B:HIT:HURT:1d13
+B:HIT:HURT:1d13
+B:HIT:HURT:1d13
+F:UNIQUE | MALE | EVIL
+F:FORCE_MAXHP | CAN_SPEAK | WILD_TOO | DROP_SKELETON | DROP_CORPSE
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A nasty piece of work, Brodda picks on defenceless women and children.
+
+N:170:Bloodfang, the Wolf
+G:C:R
+I:120:8d6:30:30:20
+W:9:1:1600:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d12
+B:BITE:HURT:1d12
+F:BASH_DOOR | WILD_ONLY | WILD_GRASS | WILD_WOOD | DROP_CORPSE |
+F:ANIMAL | UNIQUE | FORCE_MAXHP |
+F:MORTAL | BASEANGBAND
+D:It has been terrorising the nearby villages.
+
+N:171:King cobra
+G:J:g
+I:110:8d10:8:30:1
+W:9:2:300:28
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:SPIT:BLIND:1d2
+B:BITE:POISON:3d4
+F:RAND_50 | WILD_TOO | WILD_SWAMP | WILD_WOOD | DROP_SKELETON | DROP_CORPSE |
+F:BASH_DOOR | CAN_SWIM | ANIMAL | IM_POIS | HAS_EGG | MORTAL | BASEANGBAND
+D:It is a large snake with a hooded face.
+
+N:172:Eagle
+G:B:u
+I:120:9d9:30:25:10
+W:12:2:600:22
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d6
+F:ANIMAL | CAN_FLY | WILD_ONLY | WILD_WASTE | WILD_MOUNTAIN | WILD_WOOD |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | HAS_EGG
+D:A magnificent huge predatory bird.
+
+N:173:War bear
+G:q:u
+I:110:10d10:10:35:10
+W:9:1:2000:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+B:BITE:HURT:1d6
+F:WEIRD_MIND | BASH_DOOR | FRIENDS | DROP_SKELETON | DROP_CORPSE
+F:ANIMAL
+F:MORTAL | BASEANGBAND
+D:Bears with tusks, trained to kill.
+
+N:174:Killer bee
+G:I:y
+I:120:2d4:12:34:10
+W:9:2:50:22
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:STING:POISON:1d4
+B:STING:LOSE_STR:1d4
+F:WEIRD_MIND | FRIENDS | CAN_FLY | WILD_TOO |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is poisonous and aggressive.
+
+N:175:Giant spider
+G:S:v
+I:110:10d10:8:16:80
+W:10:2:700:35
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d10
+B:BITE:POISON:1d6
+B:BITE:POISON:1d6
+B:BITE:HURT:1d10
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_SKELETON |
+F:ANIMAL | SPIDER | IM_POIS |
+F:MORTAL | BASEANGBAND
+D:It is a vast spider whose bulbous body is bloated with poison.
+
+N:176:Giant white tick
+G:S:w
+I:100:12d8:12:40:20
+W:10:2:200:27
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:2d6
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY |
+F:ANIMAL | IM_POIS |
+F:MORTAL | BASEANGBAND
+D:It is moving slowly towards you.
+
+N:177:The Borshin
+G:g:w
+I:110:12d20:40:30:0
+W:10:2:600:45
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:2d12
+B:CRUSH:HURT:2d15
+B:TOUCH:TERRIFY
+F:BASH_DOOR | UNIQUE | FORCE_MAXHP | NO_CONF
+F:IM_POIS | IM_COLD | NO_FEAR
+F:MORTAL | CTHANGBAND | NO_CUT
+D:Pallid and twisted, this creature hates the very sight of you.
+D:"It looked like something that had started out to be a man but had never
+D:quite made it. It had been stepped on, twisted, had holes poked into the
+D:sickly dough of its head-bulge. Bones showed through the transparent flesh
+D:of its torso and its short legs were as thick as trees, terminating in
+D:disk-shaped pads from which dozens of long toes hung like roots or worms.
+D:its arms were longer than its entire body. it was a crushed slug, a thing
+D:that had been frozen and thawed before it was fully baked. It was -
+D:'It is the Borshin', said the Lord of Bats."
+
+N:178:Dark elven mage
+G:h:r
+I:120:7d10:20:16:20
+W:10:1:1200:50
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:MALE |
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_POIS | HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BLIND | CONF | MISSILE | DARKNESS | BA_POIS
+D:A dark elven figure, dressed all in black, hurling spells at you.
+
+N:179:Kamikaze yeek
+G:y:r
+I:113:4d8:18:18:10
+W:10:1:800:10
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:EXPLODE:HURT:15d2
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | IM_ACID |
+F:MORTAL | ZANGBAND
+D:The evil wizard Bruce has trained them to be living weapons.
+
+N:180:Orfax, Son of Boldor
+G:y:B
+I:120:14d10:18:20:10
+W:11:3:600:80
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d9
+B:INSULT:*
+B:INSULT:*
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS |
+F:ONLY_ITEM | DROP_90 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | EVIL | IM_ACID |
+F:MORTAL | BASEANGBAND
+S:1_IN_4 |
+S:HEAL | BLINK | TELE_TO | SLOW | CONF |
+S:S_MONSTER
+D:He's just like his daddy! He knows mighty spells, but fortunately he is a
+D:yeek.
+
+N:181:Servant of Glaaki
+G:z:G
+I:110:9d9:20:20:20
+W:10:1:600:25
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:1d8
+B:CLAW:DISEASE:1d3
+F:OPEN_DOOR | BASH_DOOR | FRIENDS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | UNDEAD | EVIL | NO_FEAR | IM_POIS |
+F:IM_COLD | COLD_BLOOD | CTHANGBAND | NO_CUT
+S:1_IN_12
+S:CAUSE_1 | SCARE
+D:"...the hand of a corpse -- bloodless and skeletal, and with
+D:impossibly long, cracked nails."
+
+N:182:Dark elven warrior
+G:h:u
+I:110:10d11:20:16:20
+W:10:1:1400:50
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:MALE |
+F:DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_12
+S:MISSILE
+D:A dark elven figure in armour, ready with his sword.
+
+N:183:Sand-dweller
+G:u:y
+I:110:9d9:20:20:20
+W:10:1:600:30
+E:1:1:1:2:1:1
+O:20:50:20:5
+B:CLAW:HURT:1d6
+B:CLAW:HURT:1d6
+F:FRIENDS | WILD_TOO | WILD_WASTE | DROP_SKELETON |
+F:OPEN_DOOR | BASH_DOOR | HURT_LITE | EVIL | DROP_60 | DROP_90 |
+F:MALE
+F:MORTAL | CTHANGBAND
+D:"Rough-skinned, large-eyed, large-eared, with a horrible,
+D:distorted resemblance to the koala bear facially, though
+D:his body had an appearance of emaciation."
+
+N:184:Clear mushroom patch
+G:,:B
+I:120:1d1:4:1:0
+W:10:2:30:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:HURT:1d1
+F:ATTR_CLEAR |
+F:NEVER_MOVE | INVISIBLE | COLD_BLOOD |
+F:STUPID | EMPTY_MIND | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:Yum! It smells quite tasty. If you could only see it...
+
+N:185:Quiver slot
+G:,:U
+I:120:1d1:4:1:0
+W:10:2:60:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:CONFUSE:1d1
+F:NEVER_MOVE | COLD_BLOOD |
+F:STUPID | EMPTY_MIND |
+F:NO_CONF | NO_SLEEP | NO_FEAR | JOKEANGBAND | NO_CUT
+S:MULTIPLY |
+S:1_IN_5 | ARROW_1
+D:What looks like the remains of a quiver dropped by a past adventurer
+D:has become overgrown with a strange mold intent on using the contents
+D:of the quiver to grab prey.
+
+N:186:Grishnakh, the Hill Orc
+G:o:y
+I:110:25d10:20:20:20
+W:10:3:2300:160
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d13
+B:HIT:HURT:1d11
+B:HIT:HURT:1d13
+B:HIT:HURT:1d11
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | SPECIAL_GENE |
+F:ESCORT | WILD_TOO |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is a cunning and devious orc with a chaotic nature.
+
+N:187:Giant tan bat
+G:b:U
+I:130:3d8:12:20:50
+W:10:2:600:18
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:TERRIFY:1d3
+B:CLAW:HURT:1d2
+B:CLAW:HURT:1d2
+F:RAND_50 | CAN_FLY | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD |
+F:FORCE_SLEEP | ANIMAL | DROP_CORPSE | AI_ANNOY |
+F:MORTAL | BASEANGBAND
+D:A giant bat, the beating of whose wings produces a strangely unnerving noise.
+
+N:188:Owlbear
+G:H:o
+I:110:12d12:20:20:20
+W:10:1:2000:35
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:CRUSH:HURT:1d10
+F:EVIL | ANIMAL | OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A bizarre bear-creature with the claws and the face of an owl.
+
+N:189:Blue horror
+G:u:B
+I:110:14d9:20:35:20
+W:10:3:1200:25
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:TERRIFY:1d8
+B:CLAW:TERRIFY:1d8
+F:FRIENDS |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | NO_CONF | ZANGBAND
+D:An ugly screaming little demon servant of Tzeentch.
+
+N:190:Hairy mold
+G:m:o
+I:110:15d8:2:15:70
+W:10:1:50:32
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:POISON:1d3
+F:NEVER_MOVE | CAN_SWIM |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange hairy growth on the dungeon floor.
+
+N:191:Grizzly bear
+G:q:U
+I:110:15d15:10:35:10
+W:16:2:2600:55
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d8
+B:CLAW:HURT:1d8
+B:BITE:HURT:1d12
+B:CRUSH:HURT:1d10
+F:WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_CORPSE |
+F:ANIMAL | BASH_DOOR |
+F:MORTAL | BASEANGBAND
+D:A huge, beastly bear, more savage than most of its kind.
+
+N:192:Disenchanter mold
+G:m:v
+I:110:16d8:2:20:70
+W:10:1:40:40
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:UN_BONUS:1d6
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND | RES_DISE |
+F:IM_POIS | ATTR_MULTI | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_11 |
+S:DRAIN_MANA
+D:It is a strange glowing growth on the dungeon floor.
+
+N:193:Pseudo dragon
+G:d:o
+I:110:20d10:20:30:40
+W:10:2:10000:150
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE |
+F:DROP_60 | BASH_DOOR | HAS_EGG |
+F:EVIL | DRAGON | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_11 |
+S:CONF | SCARE | BR_LITE | BR_DARK
+D:A small relative of the dragon that inhabits dark caves.
+
+N:194:Tengu
+G:u:b
+I:120:16d9:20:32:30
+W:10:1:600:40
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d8
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | RES_TELE | CAN_FLY | BASEANGBAND
+S:1_IN_3 |
+S:BLINK | TELE_TO | TELE_AWAY | TPORT
+D:It is a fast-moving demon that blinks quickly in and out of existence; no
+D:other demon matches its teleporting mastery.
+
+N:195:Creeping gold coins
+G:$:y
+I:100:18d8:5:36:10
+W:10:3:0:32
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:2d5
+B:TOUCH:POISON:3d5
+F:ONLY_GOLD | DROP_90 | DROP_1D2 |
+F:COLD_BLOOD | BASH_DOOR |
+F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It appears to be a pile of gold coins, until it starts crawling towards you
+D:on tiny legs.
+
+N:196:Wolf
+G:C:u
+I:120:6d6:30:30:20
+W:10:1:600:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:RAND_25 | FRIENDS |
+F:BASH_DOOR | WILD_TOO | WILD_WOOD | WILD_WASTE | WILD_MOUNTAIN |
+F:ANIMAL | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:It howls and snaps at you.
+
+N:197:Giant fruit fly
+G:I:G
+I:120:2d2:8:14:10
+W:10:3:100:4
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+F:RAND_50 | RAND_25 | CAN_FLY | WEIRD_MIND |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:MULTIPLY
+D:A fast-breeding, annoying pest.
+
+N:198:Panther
+G:f:D
+I:120:10d8:40:30:0
+W:10:2:1300:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d8
+B:CLAW:HURT:1d8
+F:BASH_DOOR | WILD_TOO | WILD_WOOD | WILD_GRASS | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A large black cat, stalking you with intent. It thinks you're its next
+D:meal.
+
+N:199:Brigand
+G:p:b
+I:110:9d8:20:32:10
+W:10:2:1700:35
+E:1:1:1:2:1:1
+O:25:60:0:0
+B:HIT:HURT:2d4
+B:TOUCH:EAT_ITEM
+F:MALE |
+F:DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | DROP_SKELETON |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is eyeing your backpack.
+
+N:200:Hobbes the Tiger
+G:f:y
+I:120:12d10:40:30:0
+W:10:2:1600:45
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d12
+B:CLAW:HURT:1d12
+B:BITE:HURT:1d5
+F:BASH_DOOR | UNIQUE | FORCE_MAXHP |
+F:ANIMAL | MALE | CAN_SPEAK | DROP_CORPSE |
+F:MORTAL | JOKEANGBAND
+D:Fast-moving, with a taste for tuna sandwiches.
+
+N:201:Shadow Creature of Fiona
+G:h:s
+I:110:11d8:12:12:16
+W:10:2:1000:35
+E:1:1:1:2:1:1
+O:20:30:50:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:BASH_DOOR | OPEN_DOOR | FRIENDS | DROP_60 | IM_POIS | NO_SLEEP | NO_CONF
+F:MALE | DROP_SKELETON | ZANGBAND
+D:"There was something unusual about their appearance... For one thing,
+D:all had uniformly bloodshot eyes. Very, very bloodshot eyes. With them,
+D:though, the condition seemed normal. For another, all had an extra joint
+D:to each finger and thumb, and sharp, forward-curving spurs on the backs
+D:of their hands. All of them had prominent jaws and forty-four teeth,
+D:most of them longer than human teeth, and several looking to be much
+D:sharper. Their flesh was grayish and hard and shiny. There were
+D:undoubtedly other differences also, but those were sufficient to prove
+D:a point of some sort."
+
+N:202:Undead mass
+G:j:u
+I:110:8d8:70:12:5
+W:10:2:200:33
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:DISEASE:1d6
+B:TOUCH:LOSE_CON:1d6
+F:UNDEAD | EMPTY_MIND | NO_CONF | NO_SLEEP | IM_POIS | IM_COLD | NO_FEAR |
+F:HURT_LITE | COLD_BLOOD | EVIL | NEVER_MOVE | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:A sickening mound of decaying flesh, bones, hands and so on. It seems to
+D:be growing.
+
+N:203:Chaos shapechanger
+G:H:v
+I:110:20d9:10:14:12
+W:11:2:0:38
+E:1:1:1:2:1:1
+O:20:50:20:6
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+B:HIT:CONFUSE:1d3
+F:DROP_60 | EVIL | SHAPECHANGER | ATTR_MULTI | ATTR_ANY
+F:MORTAL | ZANGBAND
+S:1_IN_5
+S:BO_FIRE | BO_COLD | CONF
+D:A vaguely humanoid form constantly changing its appearance.
+
+N:204:Baby multi-hued dragon
+G:d:v
+I:110:13d10:20:30:70
+W:11:2:5000:45
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+B:BITE:HURT:1d6
+F:ATTR_MULTI |
+F:FORCE_MAXHP | FORCE_SLEEP |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:EVIL | DRAGON | CAN_FLY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | HAS_EGG | IMPRESED |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_12 |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales shimmering with a hint of colour.
+
+N:205:Vorpal bunny
+G:r:w
+I:120:10d10:40:40:0
+W:11:3:600:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:6d1
+B:BITE:HURT:7d1
+F:BASH_DOOR | WILD_TOO | WILD_MOUNTAIN | DROP_CORPSE |
+F:ANIMAL | MORTAL | ZANGBAND
+S:1_IN_8
+S:BLINK
+D:It looks very cute, except for the razor sharp teeth. It moans
+D:ominously as it jumps at your throat!
+
+N:206:Old Man Willow
+G:#:s
+I:110:32d30:20:20:20
+W:25:5:3000:150
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:TOUCH:PARALYZE:1d14
+B:TOUCH:PARALYZE:1d14
+B:CRUSH:HURT:2d12
+F:ANIMAL | NEVER_MOVE | COLD_BLOOD | DROP_RANDART
+F:EMPTY_MIND | UNIQUE | FORCE_MAXHP | FORCE_SLEEP |
+F:RES_WATE | IM_POIS | IM_ACID | SUSCEP_FIRE | SPECIAL_GENE |
+F:DROP_1D2 | DROP_GOOD | ONLY_ITEM | BASEANGBAND | NO_CUT
+S:1_IN_10 |
+S:TELE_TO | HOLD |
+D:The ancient grey willow tree, ruler of the Old Forest. He despises
+D:trespassers in his territory. "...a huge willow-tree, old and hoary.
+D:Enormous it looked, its sprawling branches going up like racing arms
+D:with may long-fingered hands, its knotted and twisted trunk gaping in
+D:wide fissures that creaked faintly as the boughs moved."
+
+N:207:Hippocampus
+G:H:B
+I:110:20d9:12:14:10
+W:11:1:900:30
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d5
+B:BITE:HURT:2d5
+F:AQUATIC | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A truly strange hybrid of a horse and a fish.
+
+N:208:Zombified orc
+G:z:s
+I:110:11d8:20:24:25
+W:11:1:1800:30
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d4
+B:HIT:HURT:1d4
+B:HIT:HURT:1d4
+F:COLD_BLOOD | EMPTY_MIND | OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | UNDEAD |
+F:IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a shambling orcish corpse leaving behind a trail of flesh.
+
+N:209:Hippogryph
+G:H:U
+I:110:20d9:12:14:10
+W:11:1:1500:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:HIT:HURT:2d5
+B:BITE:HURT:2d5
+F:BASH_DOOR | CAN_FLY | WILD_TOO | WILD_MOUNTAIN | WILD_GRASS |
+F:ANIMAL | DROP_CORPSE | MORTAL | BASEANGBAND
+D:A strange hybrid of eagle and horse. It looks weird.
+
+N:210:Black mamba
+G:J:D
+I:120:10d8:10:32:1
+W:12:3:300:40
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:POISON:4d4
+F:RAND_50 | BASH_DOOR | CAN_SWIM |
+F:WILD_TOO | WILD_WOOD | WILD_GRASS | WILD_SWAMP |
+F:ANIMAL | IM_POIS | DROP_SKELETON | DROP_CORPSE | HAS_EGG |
+F:MORTAL | BASEANGBAND
+D:It has glistening black skin, a sleek body, and highly venomous fangs.
+
+N:211:White wolf
+G:C:w
+I:120:7d7:30:30:20
+W:12:1:700:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:BITE:HURT:1d4
+F:RAND_25 |
+F:FRIENDS | SUSCEP_FIRE |
+F:BASH_DOOR | WILD_TOO | WILD_WASTE |
+F:ANIMAL | IM_COLD | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A large and muscled wolf from the northern wastes. Its breath is cold and
+D:icy and its fur coated in frost.
+
+N:212:Grape jelly
+G:j:v
+I:110:52d8:2:1:99
+W:12:3:2600:60
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:EXP_10
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | HURT_LITE | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_11 |
+S:DRAIN_MANA
+D:Yum! It looks quite tasty. It is a pulsing mound of glowing flesh.
+
+N:213:Nether worm mass
+G:w:D
+I:100:5d8:10:15:3
+W:12:4:200:6
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:EXP_10
+F:RAND_50 | RAND_25 | CAN_SWIM |
+F:STUPID | WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | HURT_LITE | NO_FEAR | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a disgusting mass of dark worms, eating each other, the floor,
+D:the air, you...
+
+N:214:Abyss worm mass
+G:w:D
+I:100:5d8:10:15:3
+W:12:4:200:7
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:EXP_10
+F:RAND_50 | RAND_25 |
+F:STUPID | WEIRD_MIND | BASH_DOOR | EVIL | CAN_SWIM |
+F:ANIMAL | HURT_LITE | NO_FEAR | KILL_WALL | COLD_BLOOD | INVISIBLE |
+F:MORTAL | ZANGBAND | NO_CUT
+S:MULTIPLY
+D:Even more disgusting dark worms, their essence that of unbeing.
+
+N:215:Golfimbul, the Hill Orc Chief
+G:o:y
+I:110:26d10:20:60:20
+W:12:3:2200:230
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:1d13
+B:HIT:HURT:1d13
+B:HIT:HURT:1d11
+B:HIT:HURT:1d11
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | WILD_TOO | SPECIAL_GENE |
+F:ESCORT | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | CAN_SPEAK |
+F:EVIL | ORC | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A leader of a band of raiding orcs, he picks on hobbits.
+
+N:216:Swordsman
+G:p:u
+I:110:12d8:20:34:20
+W:12:1:1800:40
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:MALE | WILD_TOO |
+F:DROP_1D2 | DROP_SKELETON | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A warrior of considerable skill.
+
+N:217:Skaven shaman
+G:r:g
+I:110:10d8:20:15:20
+W:12:1:600:36
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:1d7
+B:HIT:HURT:1d7
+F:MALE | WILD_TOO | WILD_SWAMP | WILD_WASTE |
+F:FORCE_SLEEP | DROP_90 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | ZANGBAND
+S:1_IN_8 |
+S:BLINK | CAUSE_1 | MISSILE | CONF | SCARE
+D:The shaman of a skaven tribe gets his powers from a mystic
+D:stone corrupted by chaos, called a Warp Stone.
+
+N:218:Baby bronze dragon
+G:d:U
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | NO_CONF | HAS_EGG | IMPRESED | BASEANGBAND | ATTR_MULTI
+S:1_IN_12 |
+S:BR_CONF
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a dull bronze.
+
+N:219:Baby gold dragon
+G:d:y
+I:110:10d10:20:30:70
+W:9:2:4000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | CAN_FLY | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | NO_STUN | HAS_EGG | IMPRESED | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_12 |
+S:BR_SOUN
+D:This hatchling dragon is still soft, its eyes unaccustomed to light and
+D:its scales a pale gold.
+
+N:220:Evil eye
+G:e:D
+I:110:15d8:2:6:10
+W:18:3:600:80
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:EXP_10
+B:GAZE:EXP_10
+F:NEVER_MOVE | EVIL | CAN_FLY | DROP_CORPSE |
+F:HURT_LITE | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:HOLD | TELE_TO
+D:A huge disembodied eye. As you stare into the black nothingness of its pupil,
+D:you feel your will and vitality draining away, and are unable to do anything
+D:except approach it in horrified fascination.
+
+N:221:Mine-dog
+G:C:u
+I:120:6d6:30:30:20
+W:12:4:500:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:EXPLODE:HURT:6d6
+F:RAND_50 | FRIENDS | BASH_DOOR | ANIMAL |
+F:MORTAL | ZANGBAND
+D:An explosive charge has been attached to this poor animal, who
+D:has been trained to search for its target and detonate.
+
+N:222:Hellcat
+G:f:R
+I:120:9d8:20:30:30
+W:12:1:400:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d5
+B:CLAW:HURT:1d5
+B:BITE:HURT:1d8
+F:ANIMAL | WEIRD_MIND | FRIENDS | RAND_25 | IM_FIRE | EVIL | SUSCEP_COLD |
+F:MORTAL | ZANGBAND | HAS_LITE
+D:It is as large as a tiger, and its yellow eyes are pupilless.
+
+N:223:Moon beast
+G:q:W
+I:120:9d10:30:30:20
+W:12:1:800:57
+E:0:1:0:2:1:0
+O:50:0:40:5
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BUTT:HURT:1d6
+F:DROP_1D2 | ONLY_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_FIRE | ANIMAL | DROP_CORPSE
+F:MORTAL | ZANGBAND
+S:1_IN_6
+S:HEAL | BLIND | DARKNESS | CONF | CAUSE_2
+D:"Great greyish-white slippery things which could expand and
+D:contract at will, and whose principle shape... was that of a
+D:sort of toad without any eyes, but with a curious vibrating mass
+D:of short pink tentacles on the end of its blunt, vague snout."
+
+N:224:Master yeek
+G:y:g
+I:110:12d9:18:24:10
+W:12:2:600:28
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:1d8
+F:FORCE_SLEEP |
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | EVIL | IM_ACID | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_4 |
+S:BLINK | TPORT | BLIND | SLOW | BA_POIS |
+S:S_MONSTER
+D:A small humanoid that radiates some power.
+
+N:225:Priest
+G:p:g
+I:110:12d8:20:22:40
+W:12:1:1500:36
+E:1:1:1:2:1:1
+O:0:20:80:0
+B:HIT:HURT:2d3
+B:HIT:HURT:2d3
+F:MALE | GOOD |
+F:FORCE_SLEEP |
+F:DROP_1D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | SCARE | CAUSE_2 |
+S:S_MONSTER
+D:A robed man dedicated to his god.
+
+N:226:Dark elven priest
+G:h:g
+I:120:7d10:20:30:30
+W:12:1:1200:50
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:HIT:HURT:1d9
+B:HIT:HURT:1d10
+F:MALE |
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_1D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:HEAL | BLIND | CONF | CAUSE_2 | DARKNESS | MISSILE
+D:A dark elven figure, dressed all in black, chanting curses and waiting to
+D:deliver your soul to hell.
+
+N:227:Air spirit
+G:E:B
+I:130:8d8:12:40:20
+W:12:2:0:40
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d3
+F:RAND_50 | RAND_25 | IM_ELEC | IM_POIS |
+F:EMPTY_MIND | INVISIBLE | COLD_BLOOD | BASH_DOOR |
+F:IM_POIS | CAN_FLY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A whirlwind of sentient air.
+
+N:228:Skeleton human
+G:s:W
+I:110:10d8:20:30:30
+W:12:1:1500:38
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d8
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is an animated human skeleton.
+
+N:229:Zombified human
+G:z:s
+I:110:12d8:20:24:20
+W:12:1:1500:34
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d4
+B:HIT:HURT:1d4
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a shambling human corpse dropping chunks of flesh behind it.
+
+N:230:Tiger
+G:f:o
+I:120:12d10:40:40:0
+W:12:2:1500:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d8
+B:CLAW:HURT:1d8
+B:BITE:HURT:1d6
+F:BASH_DOOR | WILD_TOO | WILD_GRASS | WILD_WOOD | WILD_MOUNTAIN |
+F:ANIMAL | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:One of the largest of its species, a sleek orange and black shape creeps
+D:towards you, ready to pounce.
+
+N:231:Moaning spirit
+G:G:u
+I:120:5d8:14:20:10
+W:12:2:0:44
+E:0:0:0:0:0:0
+O:45:15:25:0
+B:WAIL:TERRIFY
+B:TOUCH:LOSE_DEX:1d8
+F:FORCE_SLEEP | RAND_25 |
+F:DROP_60 | DROP_90 | CAN_FLY |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:TPORT | SCARE
+D:A ghostly apparition that shrieks horribly.
+
+N:232:Stegocentipede
+G:c:u
+I:120:13d8:12:30:30
+W:12:2:1200:40
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d4
+B:BITE:HURT:2d4
+B:STING:HURT:2d4
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | DROP_SKELETON | MORTAL | BASEANGBAND
+D:It is a vast armoured centipede with massive mandibles and a spiked tail.
+
+N:233:Spotted jelly
+G:j:o
+I:120:13d8:12:18:1
+W:12:3:2500:33
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:2d6
+B:TOUCH:ACID:2d6
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND | COLD_BLOOD |
+F:IM_ACID | IM_POIS | HURT_LITE | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A strange jelly thing, covered in discoloured blotches.
+
+N:234:Drider
+G:S:b
+I:110:10d13:8:30:80
+W:13:2:2000:55
+E:1:1:1:2:1:0
+O:0:0:0:0
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+B:BITE:POISON:1d6
+F:FORCE_SLEEP |
+F:BASH_DOOR | DROP_SKELETON |
+F:EVIL | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:CONF | CAUSE_1 | DARKNESS | MISSILE | ARROW_2
+D:A dark elven torso merged with the bloated form of a giant spider.
+
+N:235:Mongbat
+G:b:U
+I:110:10d10:20:80:8
+W:13:2:800:65
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+B:BITE:POISON:1d8
+F:ANIMAL | EVIL | FRIENDS | CAN_FLY | FORCE_MAXHP |
+F:IM_COLD | IM_ELEC | IM_POIS | WEIRD_MIND | DROP_CORPSE
+F:MORTAL | BASEANGBAND
+D:Devil-bats, notoriously difficult to kill.
+
+N:236:Killer brown beetle
+G:K:u
+I:110:13d8:10:40:30
+W:13:1:500:38
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:3d4
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is a vicious insect with a tough carapace.
+
+N:237:Boldor, King of the Yeeks
+G:y:v
+I:120:20d10:18:24:10
+W:13:3:650:200
+E:1:1:1:2:1:1
+O:0:90:10:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+B:HIT:HURT:1d9
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS |
+F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR | CAN_SPEAK | DROP_CORPSE |
+F:ANIMAL | EVIL | IM_ACID |
+F:MORTAL | BASEANGBAND
+S:1_IN_2 |
+S:HEAL | BLINK | TPORT | BLIND | SLOW |
+S:S_KIN | S_MONSTER
+D:A great yeek, powerful in magic and sorcery, but a yeek all the same.
+
+N:238:Ogre
+G:O:U
+I:110:13d9:20:33:30
+W:13:2:2100:50
+E:1:1:1:2:1:1
+O:10:85:0:0
+B:HIT:HURT:2d8
+F:FRIENDS |
+F:DROP_60 | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A hideous, smallish giant that is often found near or with orcs.
+
+N:239:Creeping mithril coins
+G:$:B
+I:110:20d8:5:50:10
+W:13:3:0:45
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:2d5
+B:TOUCH:POISON:3d5
+F:ONLY_GOLD | DROP_90 | DROP_2D2 |
+F:COLD_BLOOD | BASH_DOOR | IM_ACID | CHAR_MULTI |
+F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It appears to be a pile of sentient mithril coins that doesn't like being
+D:picked up.
+
+N:240:Illusionist
+G:p:R
+I:110:12d8:20:10:10
+W:13:2:1500:50
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:HIT:HURT:2d2
+F:MALE |
+F:FORCE_SLEEP | DROP_1D2 | DROP_SKELETON | DROP_CORPSE
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HASTE | BLINK | TPORT | BLIND | HOLD | SLOW | CONF | DARKNESS
+D:A deceptive spell caster.
+
+N:241:Druid
+G:p:G
+I:110:12d12:20:10:10
+W:13:2:1400:50
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:HIT:HURT:2d4
+B:HIT:HURT:2d4
+F:MALE | WILD_TOO | WILD_WOOD |
+F:FORCE_SLEEP | DROP_1D2 | DROP_SKELETON | DROP_CORPSE
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HASTE | BLINK | BLIND | HOLD | SLOW | BO_FIRE | BO_ELEC | S_ANIMAL
+D:A priest devoted to Nature.
+
+N:242:Pink horror
+G:u:R
+I:110:15d9:20:35:20
+W:13:3:1200:64
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:TERRIFY:1d8
+B:CLAW:TERRIFY:1d8
+B:BITE:CONFUSE:1d6
+F:FRIENDS |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | NO_CONF | ZANGBAND
+S:1_IN_8 |
+S:CONF | SCARE
+D:An ugly screaming little demon servant of Tzeentch.
+
+N:243:Cloaker
+G:(:g
+I:130:7d7:20:40:0
+W:13:5:60:30
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:PARALYZE:5d5
+B:HIT:TERRIFY:5d5
+F:NEVER_MOVE | NO_FEAR |
+F:STUPID | EMPTY_MIND | COLD_BLOOD | CHAR_MULTI | NO_CONF | NO_SLEEP |
+F:DROP_90 | IM_COLD | FORCE_MAXHP | IM_ELEC | IM_POIS |
+F:BASEANGBAND | NO_CUT
+D:It resembles a normal cloak until some poor fool ventures too close!
+
+N:244:Black orc
+G:o:D
+I:110:12d10:20:36:20
+W:13:1:2000:45
+E:1:1:1:2:1:1
+O:10:50:20:15
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:MALE | WILD_TOO | DROP_SKELETON | DROP_CORPSE |
+F:FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | HURT_LITE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9
+S:ARROW_2
+D:He is a large orc with powerful arms and deep black skin.
+
+N:245:Ochre jelly
+G:j:U
+I:120:13d8:12:18:1
+W:13:3:2300:40
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:2d6
+B:TOUCH:ACID:2d6
+F:STUPID | EMPTY_MIND | COLD_BLOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A fast moving highly acidic jelly thing, that is eating away the floor it
+D:rests on.
+
+N:246:Software bug
+G:I:r
+I:120:2d2:8:25:10
+W:14:1:0:4
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+F:RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY |
+F:ANIMAL | MORTAL | JOKEANGBAND
+S:MULTIPLY
+D:Oh no! They are everywhere!
+
+N:247:Lurker
+G:.:w
+I:110:20d10:30:25:10
+W:14:3:0:80
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:CHAR_CLEAR | ATTR_CLEAR | CHAR_MULTI |
+F:NEVER_MOVE | FORCE_MAXHP |
+F:EMPTY_MIND | INVISIBLE | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+D:A strange creature that merges with the dungeon floor, trapping its
+D:victims by enveloping them within its perfectly disguised form.
+
+N:248:Tangleweed
+G:#:g
+I:100:5d5:5:5:5
+W:10:4:50:10
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:PARALYZE
+B:HIT:PARALYZE
+F:FORCE_SLEEP | NEVER_MOVE | STUPID | EMPTY_MIND | FRIENDS |
+F:KILL_TREES | SUSCEP_FIRE | ANIMAL |
+F:WILD_ONLY | COLD_BLOOD | WILD_WOOD | WILD_GRASS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | MORTAL | BASEANGBAND | DROP_60 | NO_CUT
+D:A mass of vegetation. As you pass near it, it reaches out tendrils to
+D:ensnare you. You can just make out skeletons of its previous victims
+D:deep within the thickets.
+
+N:249:Vlasta
+G:R:B
+I:120:12d6:12:20:12
+W:14:3:1000:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:BLIND:1d10
+B:BITE:BLIND:1d10
+F:OPEN_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | ZANGBAND
+D:This strange creature looks like a miniature tyrannosaur. It has
+D:empty, pale eyes and a sharp beak, which it aims at your eyes
+D:as it jumps at you!
+
+N:250:Giant white dragon fly
+G:F:w
+I:110:3d8:12:20:50
+W:14:2:150:60
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:COLD:1d6
+F:FORCE_SLEEP | WILD_TOO | WILD_WASTE |
+F:RAND_50 | CAN_FLY | SUSCEP_FIRE |
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | IM_COLD |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_COLD
+D:It is a large fly that drips frost.
+
+N:251:Snaga sapper
+G:o:U
+I:111:8d8:20:32:30
+W:14:1:80:15
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d8
+B:EXPLODE:HURT:20d2
+F:MALE |
+F:WILD_TOO | SUSCEP_FIRE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | HURT_LITE |
+F:MORTAL | ZANGBAND | HAS_LITE
+D:He is one of the many weaker 'slave' orcs, often mistakenly known as a
+D:goblin. He is equipped with an explosive charge.
+
+N:252:Blue icky thing
+G:i:b
+I:100:10d6:15:20:20
+W:14:4:600:20
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:POISON:1d4
+B:CRAWL:EAT_FOOD
+B:HIT:HURT:1d4
+B:HIT:HURT:1d4
+F:FORCE_SLEEP |
+F:RAND_50 |
+F:OPEN_DOOR | BASH_DOOR | CAN_SWIM | DROP_CORPSE |
+F:EVIL | IM_POIS | BASEANGBAND
+S:MULTIPLY |
+S:1_IN_8 |
+S:BLIND | CONF | SCARE
+D:It is a strange, slimy, icky creature, with rudimentary intelligence,
+D:but evil cunning. It hungers for food, and you look tasty.
+
+N:253:Gibbering mouther
+G:j:o
+I:110:8d6:15:20:20
+W:14:4:2600:20
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:POISON:1d4
+F:NEVER_MOVE | EVIL | CAN_SWIM |
+F:IM_POIS | EMPTY_MIND | NO_FEAR | BASEANGBAND | NO_CUT
+S:MULTIPLY |
+S:1_IN_7 |
+S:SCARE | CONF | BR_LITE
+D:A chaotic mass of pulsating flesh, mouths and eyes.
+
+N:254:Wolfhound of Flora
+G:C:s
+I:120:9d9:20:20:0
+W:14:2:1600:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d5
+B:BITE:HURT:1d5
+F:ANIMAL | NO_FEAR | FRIENDS | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | ZANGBAND
+D:Well-trained watchdogs, obedient to death.
+
+N:255:Hill giant
+G:P:U
+I:110:30d15:20:45:50
+W:25:1:3500:150
+E:1:1:1:2:1:1
+O:20:50:20:5
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+F:DROP_60 | WILD_TOO | WILD_MOUNTAIN | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | MALE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A ten foot tall humanoid with powerful muscles.
+
+N:256:Flesh golem
+G:g:R
+I:110:12d8:12:30:10
+W:14:2:3000:50
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:EMPTY_MIND | BASH_DOOR | CAN_SWIM |
+F:IM_ELEC | IM_COLD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING |
+F:MORTAL | BASEANGBAND | NO_CUT
+D:A shambling humanoid monster with long scars.
+
+N:257:Warg
+G:C:D
+I:120:8d8:20:20:40
+W:16:2:700:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+B:BITE:HURT:1d6
+F:RAND_25 | BASH_DOOR | FRIENDS |
+F:WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | EVIL | MORTAL | BASEANGBAND
+D:It is a large wolf with eyes full of cunning.
+
+N:258:Cheerful leprechaun
+G:h:G
+I:120:2d5:8:6:6
+W:14:2:800:23
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:TOUCH:EAT_GOLD
+B:TOUCH:EAT_FOOD
+F:DROP_60 | ONLY_GOLD | RAND_50 | OPEN_DOOR | MALE |
+F:GOOD | MORTAL | ZANGBAND
+S:MULTIPLY |
+S:1_IN_6 |
+S:BLINK
+D:A merry little gnome.
+
+N:259:Giant flea
+G:I:s
+I:120:1d2:6:7:10
+W:14:3:90:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+F:RAND_50 | CAN_FLY | WEIRD_MIND |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:MULTIPLY
+D:It makes you itch just to look at it.
+
+N:260:Ufthak of Cirith Ungol
+G:o:g
+I:110:34d10:20:50:20
+W:14:3:2600:250
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:ESCORT |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_COLD | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A strong orc guarding the pass of Cirith Ungol. He is mortally afraid of
+D:spiders: he was captured by Shelob once, but escaped when she forgot
+D:completely about him.
+
+N:261:Clay golem
+G:g:U
+I:110:14d8:12:30:10
+W:15:2:3200:60
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a massive animated statue made out of hardened clay.
+
+N:262:Black ogre
+G:O:D
+I:110:20d9:20:33:30
+W:15:2:2300:70
+E:1:1:1:2:1:1
+O:0:70:0:15
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+F:RAND_25 | WILD_TOO | WILD_MOUNTAIN | DROP_CORPSE |
+F:FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A massive orc-like figure with black skin and powerful arms.
+
+N:263:Dweller on the threshold
+G:Y:s
+I:110:30d8:30:30:20
+W:15:5:3000:50
+E:1:1:1:2:1:1
+O:45:0:40:10
+B:GAZE:PARALYZE:4d4
+B:GAZE:TERRIFY:3d3
+B:GAZE:CONFUSE:3d3
+F:NEVER_MOVE | DROP_60 | EVIL | DEMON | DROP_CORPSE |
+F:IM_POIS | IM_COLD |IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP
+F:MORTAL | ZANGBAND
+S:1_IN_6
+S:BO_ACID | S_MONSTER | SCARE | DRAIN_MANA
+D:A gorilla-shaped arcane guardian with an appetite for mages.
+
+N:264:Half-orc
+G:o:s
+I:110:16d10:20:40:20
+W:15:2:1700:50
+E:1:1:1:2:1:1
+O:30:30:30:5
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:MALE | WILD_TOO |
+F:FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | ORC | MORTAL | BASEANGBAND | HAS_LITE
+D:He is a hideous deformed cross-breed of man and orc, combining man's
+D:strength and cunning with orcish evil. The traitorous wizard Saruman is
+D:generally believed to be responsible for this abomination.
+
+N:265:Dark naga
+G:n:s
+I:110:22d11:60:65:60
+W:15:2:1900:90
+E:0:0:0:0:1:0
+O:0:0:80:20
+B:STING:HURT:1d10
+B:BITE:HURT:1d10
+F:FEMALE |
+F:RAND_25 | DROP_60 | DROP_1D2 | IM_POIS | IM_COLD | RES_WATE |
+F:OPEN_DOOR | BASH_DOOR | EMPTY_MIND | CAN_SWIM | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND
+S:1_IN_8
+S:HOLD | CONF | BO_COLD | HEAL | DARKNESS
+D:A giant snake-like figure with a woman's torso, talented in magic.
+
+N:266:Poison ivy
+G:#:g
+I:100:5d5:5:5:5
+W:10:4:50:10
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:POISON:2d2
+B:HIT:POISON:2d2
+F:FORCE_SLEEP | NEVER_MOVE | STUPID | EMPTY_MIND | FRIENDS |
+F:KILL_TREES | SUSCEP_FIRE | ANIMAL |
+F:WILD_ONLY | COLD_BLOOD | WILD_WOOD | WILD_GRASS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | MORTAL | BASEANGBAND | DROP_60 | NO_CUT
+S:MULTIPLY
+D:A mass of vegetation. It seems to be growing...
+
+N:267:Magic mushroom patch
+G:,:B
+I:140:1d1:40:10:0
+W:15:2:50:10
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:CONFUSE
+B:SPORE:CONFUSE
+B:SPORE:HALLU
+B:SPORE:HALLU
+F:FORCE_SLEEP | NEVER_MOVE |
+F:STUPID | RES_TELE | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:BLINK | SLOW | SCARE | DARKNESS
+D:Yum! It looks quite tasty. It seems to glow with an unusual light.
+
+N:268:Plaguebearer of Nurgle
+G:z:o
+I:110:9d10:20:50:20
+W:15:2:2800:75
+E:1:1:1:2:1:1
+O:50:20:20:10
+B:CLAW:DISEASE:2d5
+B:CLAW:DISEASE:2d5
+B:BUTT:HURT:3d5
+F:FORCE_MAXHP | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | IM_COLD |
+F:EVIL | DEMON | UNDEAD | IM_POIS | NONLIVING | ZANGBAND | NO_CUT
+S:1_IN_8 |
+S:SCARE | S_ANT | CAUSE_2 | SLOW
+D:An unfortunate individual, who was killed by the incurable
+D:disease known as Nurgle's Rot, and was transformed into a
+D:rotting demon zombie. It has but one eye, and a single
+D:pale horn in its forehead.
+
+N:269:Guardian naga
+G:n:y
+I:110:24d11:20:65:120
+W:15:2:1900:80
+E:0:0:0:0:1:0
+O:0:0:80:20
+B:CRUSH:HURT:2d8
+B:BITE:HURT:1d8
+B:BITE:HURT:1d8
+F:FEMALE |
+F:RAND_25 | DROP_60 | DROP_1D2 | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:EVIL
+F:MORTAL | BASEANGBAND
+D:A giant snake-like figure with a woman's torso.
+
+N:270:Wererat
+G:r:D
+I:110:20d8:10:10:10
+W:15:2:30:55
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d8
+B:CLAW:HURT:1d8
+B:BITE:HURT:2d6
+F:FORCE_SLEEP |
+F:ONLY_GOLD | DROP_60 | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | EVIL
+F:MORTAL | BASEANGBAND
+S:1_IN_9 |
+S:BLINK | CAUSE_2 | BO_COLD | BA_POIS | S_KIN
+D:A large rat with glowing red eyes, which can also assume human form. The wererat
+D:is a disgusting creature, relishing in filth and disease.
+
+N:271:Light hound
+G:Z:o
+I:110:6d6:30:30:0
+W:15:1:600:50
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+B:BITE:HURT:1d6
+F:FORCE_SLEEP | DROP_CORPSE |
+F:FRIENDS |
+F:BASH_DOOR |
+F:ANIMAL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BR_LITE
+D:A brilliant canine form whose light hurts your eyes, even at this distance.
+
+N:272:Dark hound
+G:Z:D
+I:110:6d6:30:30:0
+W:15:1:600:50
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+B:BITE:HURT:1d6
+F:FORCE_SLEEP | DROP_CORPSE |
+F:FRIENDS |
+F:BASH_DOOR | HURT_LITE |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_DARK
+D:A hole in the air in the shape of a huge hound. No light falls upon its
+D:form.
+
+N:273:Flying skull
+G:s:s
+I:110:10d10:30:30:20
+W:15:3:500:50
+E:0:0:0:0:1:0
+O:90:0:10:0
+B:BITE:POISON:1d3
+B:BITE:LOSE_STR:1d4
+F:UNDEAD | EVIL | IM_POIS | IM_COLD | WEIRD_MIND | NO_FEAR | CAN_FLY |
+F:NO_CONF | NO_SLEEP | DROP_60 | BASH_DOOR | FRIENDS | COLD_BLOOD |
+F:BASEANGBAND | NO_CUT
+D:A pack of skulls animated by necromantic spells.
+
+N:274:Mi-Go
+G:I:R
+I:120:13d8:20:30:20
+W:15:2:800:80
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:STING:POISON:1d4
+B:BITE:LOSE_STR:1d2
+F:IM_POIS | IM_COLD | COLD_BLOOD | ANIMAL | EVIL |
+F:NO_SLEEP | NO_CONF | CAN_FLY | DROP_SKELETON | CTHANGBAND
+S:1_IN_6
+S:CONF | S_MONSTER | S_DEMON
+D:"They were pinkish things about five feet long; with crustaceous
+D:bodies bearing vast pairs of dorsal fins or membranous wings and
+D:several sets of articulate limbs, and with a sort of convoluted
+D:ellipsoid, covered with multitudes of very short antenna, where
+D:a head would ordinarily be..."
+
+N:275:Giant tarantula
+G:S:o
+I:120:10d15:8:32:80
+W:15:3:1100:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d6
+B:BITE:POISON:1d6
+B:BITE:POISON:1d6
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | SPIDER | IM_POIS | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:A giant spider with hairy black and red legs.
+
+N:276:Giant clear centipede
+G:c:B
+I:110:5d8:12:30:30
+W:15:2:400:30
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d4
+B:STING:HURT:2d4
+F:ATTR_CLEAR |
+F:INVISIBLE | WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:It is about four feet long and carnivorous.
+
+N:277:Mirkwood spider
+G:S:G
+I:120:9d8:15:25:80
+W:15:2:1200:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d8
+B:BITE:POISON:1d6
+B:BITE:POISON:1d6
+F:FRIENDS | WILD_TOO | WILD_WOOD |
+F:WEIRD_MIND | BASH_DOOR | HURT_LITE |
+F:ANIMAL | SPIDER | EVIL | IM_POIS | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:A strong and powerful spider from Mirkwood forest. Cunning and evil, it
+D:seeks to taste your juicy insides.
+
+N:278:Frost giant
+G:P:w
+I:110:32d15:20:50:50
+W:28:1:4000:180
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:COLD:5d8
+B:HIT:COLD:5d8
+F:DROP_60 | WILD_TOO | WILD_WASTE | WILD_MOUNTAIN |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | GIANT | MALE | AURA_COLD | SUSCEP_FIRE |
+F:IM_COLD | BASEANGBAND | HAS_LITE | MORTAL
+D:A twelve foot tall giant covered in furs.
+
+N:279:Griffon
+G:H:u
+I:110:30d8:12:15:10
+W:15:1:2500:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:HIT:HURT:3d4
+B:BITE:HURT:2d6
+F:BASH_DOOR | CAN_FLY | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | WILD_GRASS |
+F:ANIMAL | DROP_CORPSE | MORTAL | BASEANGBAND
+D:It is half lion, half eagle. It flies menacingly towards you.
+
+N:280:Homunculus
+G:u:y
+I:110:8d8:20:32:30
+W:15:3:100:40
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:PARALYZE:1d2
+B:HIT:HURT:1d10
+F:OPEN_DOOR | BASH_DOOR | NONLIVING | CAN_FLY |
+F:EVIL | DEMON | IM_FIRE | NO_FEAR | BASEANGBAND | HAS_LITE
+D:It is a small demonic spirit full of malevolence.
+
+N:281:Gnome mage
+G:h:R
+I:110:7d8:20:20:20
+W:15:2:900:40
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:HIT:HURT:1d5
+F:MALE |
+F:FORCE_SLEEP | FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLINK | DARKNESS | BO_COLD |
+S:S_MONSTER
+D:A mage of short stature.
+
+N:282:Clear hound
+G:Z:B
+I:110:6d6:30:30:0
+W:15:1:600:50
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d6
+B:CLAW:HURT:1d6
+B:BITE:HURT:1d8
+F:ATTR_CLEAR |
+F:FRIENDS | DROP_SKELETON | DROP_CORPSE |
+F:INVISIBLE | BASH_DOOR |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A faint sense of motion in the air, hound shaped, stands before you.
+
+N:283:Umber hulk
+G:X:U
+I:110:20d10:20:50:10
+W:16:1:5000:75
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:BITE:HURT:2d6
+F:EMPTY_MIND | COLD_BLOOD |
+F:BASH_DOOR | KILL_WALL | DROP_SKELETON |
+F:ANIMAL | EVIL |
+F:IM_POIS |
+F:HURT_ROCK | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:This bizarre creature has glaring eyes and large mandibles capable of
+D:slicing through rock.
+
+N:284:Rust monster
+G:q:o
+I:110:20d15:12:55:10
+W:16:2:900:50
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+F:STUPID | WEIRD_MIND | KILL_ITEM |
+F:IM_ACID | IM_POIS | DROP_CORPSE |
+F:NO_CONF |
+F:MORTAL | BASEANGBAND
+D:It is a weird, small animal with two antennae popping forth from
+D:its forehead. It looks hungry.
+
+N:285:Ogrillon
+G:O:W
+I:110:22d9:20:33:30
+W:16:2:2400:75
+E:1:1:1:2:1:1
+O:20:70:0:10
+B:HIT:HURT:2d10
+B:HIT:HURT:2d10
+F:FRIENDS | DROP_60 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | ORC |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:An unholy hybrid of ogre and orc.
+
+N:286:Gelatinous cube
+G:j:G
+I:110:36d10:12:18:1
+W:16:4:40000:80
+E:0:0:0:0:0:0
+O:40:30:20:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+F:FORCE_MAXHP |
+F:DROP_1D2 | DROP_4D2 |
+F:STUPID | EMPTY_MIND | COLD_BLOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange, vast gelatinous structure that assumes cubic proportions
+D:as it lines all four walls of the corridors it patrols. Through its
+D:transparent jelly structure you can see treasures it has engulfed, and a
+D:few corpses as well.
+
+N:287:Giant green dragon fly
+G:F:G
+I:110:3d8:12:20:50
+W:16:2:150:65
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 | WILD_TOO | WILD_SWAMP |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY |
+F:ANIMAL | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_POIS
+D:A vast, foul-smelling dragonfly.
+
+N:288:Fire giant
+G:P:r
+I:110:34d16:20:60:50
+W:30:1:5000:220
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:FIRE:6d8
+B:HIT:FIRE:6d8
+F:DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_VOLCANO | SUSCEP_COLD |
+F:EVIL | GIANT | MALE | AURA_FIRE | DROP_SKELETON | DROP_CORPSE |
+F:IM_FIRE | BASEANGBAND | HAS_LITE | MORTAL
+D:A glowing fourteen foot tall giant. Flames drip from its red skin.
+
+N:289:Hummerhorn
+G:I:y
+I:120:2d2:8:14:10
+W:16:4:100:4
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:CONFUSE:2d2
+F:RAND_50 | RAND_25 | CAN_FLY |
+F:WEIRD_MIND | ANIMAL | BASEANGBAND
+S:MULTIPLY
+D:A giant buzzing wasp, its stinger drips venom.
+
+N:290:Lizard man
+G:h:G
+I:110:16d10:20:40:20
+W:16:3:1300:55
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:4d4
+B:HIT:HURT:4d4
+F:MALE | CAN_SWIM | IM_ACID |
+F:FRIENDS | DROP_60 | WILD_TOO | WILD_SHORE |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON
+F:EVIL
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:Intelligent lizard beings from the depths.
+
+N:291:Ulfast, Son of Ulfang
+G:p:U
+I:110:37d10:20:40:40
+W:16:3:1700:200
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:UNIQUE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:MALE |
+F:FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+D:A short and swarthy Easterling.
+
+N:292:Crebain
+G:B:D
+I:120:3d5:40:12:0
+W:16:4:500:20
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+F:ANIMAL | EVIL | MORTAL | FRIENDS | BASEANGBAND | DROP_CORPSE | HAS_EGG |
+F:WILD_TOO | WILD_WASTE | WILD_MOUNTAIN | WILD_WOOD | WILD_VOLCANO |
+F:WILD_GRASS | WILD_SWAMP | WILD_SHORE | WILD_OCEAN | CAN_FLY
+S:1_IN_8 | SHRIEK
+D:A type of crow, specially bred by the forces of evil as spies; their
+D:rudimentary intelligence guided by an evil mind has tracked you down,
+D:and now they seek to alert other evil creatures to your presence.
+
+N:293:Berserker
+G:p:u
+I:120:60d25:20:80:10
+W:45:2:2300:2500
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:7d7
+B:HIT:HURT:7d7
+B:HIT:HURT:7d7
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 | HASTE | SCARE
+D:Even the strongest of normal human warriors fears the Berserker - the one who
+D:can drive himself into such a terrible battle-frenzy that he can survive blows
+D:which should kill him, and still apparently feel no pain. He tramples weaker
+D:creatures underfoot in his eagerness to get to his real enemy, and his
+D:battle-cry strikes terror into his foes.
+
+N:294:Quasit
+G:u:o
+I:110:6d8:20:30:20
+W:16:2:500:50
+E:1:1:1:2:1:1
+O:0:50:30:10
+B:BITE:LOSE_DEX:1d6
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+F:FORCE_SLEEP | CAN_FLY |
+F:RAND_25 |
+F:ONLY_ITEM | DROP_1D2 |
+F:SMART | INVISIBLE | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | NONLIVING | BASEANGBAND | HAS_LITE
+S:1_IN_10 |
+S:BLINK | TPORT | TELE_TO | TELE_LEVEL | BLIND | CONF | SCARE
+D:The chaotic evil master's favourite pet.
+
+N:295:Sphinx
+G:H:o
+I:110:60d5:20:60:20
+W:17:2:6000:80
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+F:FORCE_SLEEP |
+F:ONLY_GOLD | DROP_1D2 | CAN_FLY | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_MOUNTAIN |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:1_IN_11 |
+S:SCARE | CONF
+D:It will eat you if you cannot answer its riddle.
+
+N:296:Imp
+G:u:g
+I:110:6d8:20:30:20
+W:17:2:400:55
+E:0:1:1:0:1:0
+O:30:20:50:0
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+F:FORCE_SLEEP | CAN_FLY |
+F:RAND_25 |
+F:ONLY_ITEM | DROP_1D2 |
+F:SMART | INVISIBLE | COLD_BLOOD | BASH_DOOR |
+F:EVIL | DEMON | IM_FIRE | RES_TELE | BASEANGBAND | HAS_LITE
+S:1_IN_10 |
+S:BLINK | TPORT | TELE_TO | TELE_LEVEL | BLIND | CONF | SCARE | BO_FIRE
+D:The lawful evil master's favourite pet.
+
+N:297:Forest troll
+G:T:g
+I:110:20d10:20:50:40
+W:17:1:3000:70
+E:1:1:1:2:1:1
+O:30:70:0:0
+B:HIT:HURT:1d4
+B:HIT:HURT:1d4
+B:BITE:HURT:1d6
+F:MALE |
+F:FRIENDS | DROP_60 | WILD_TOO | WILD_WOOD | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | SUSCEP_FIRE | REGENERATE |
+F:EVIL | TROLL | HURT_LITE | BASEANGBAND
+D:He is green-skinned and ugly.
+
+
+N:298:Freezing sphere
+G:*:w
+I:120:6d6:100:30:0
+W:17:1:0:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:COLD:8d8
+F:FORCE_SLEEP | CAN_FLY | SUSCEP_FIRE | RAND_50 | RAND_25 |
+F:EMPTY_MIND | AURA_COLD |
+F:IM_COLD | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT
+D:A semi-sentient snowball, hurling itself at targets at random.
+
+N:299:Jumping fireball
+G:*:r
+I:120:6d6:100:30:0
+W:17:1:0:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:FIRE:8d8
+F:FORCE_SLEEP | CAN_FLY | SUSCEP_COLD |
+F:EMPTY_MIND | AURA_FIRE | RAND_50 | RAND_25 |
+F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT
+D:A semi-sentient fireball that moves around randomly.
+
+N:300:Ball lightning
+G:*:B
+I:120:6d6:100:30:0
+W:17:1:0:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:ELEC:8d8
+F:FORCE_SLEEP | CAN_FLY | RAND_25 | RAND_50 |
+F:EMPTY_MIND | AURA_ELEC |
+F:IM_ELEC | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | HAS_LITE |NO_CUT
+D:A crackling ball of energy, zooming about seemingly at random.
+
+N:301:2-headed hydra
+G:M:u
+I:110:100d3:20:60:20
+W:17:2:4000:80
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+F:FORCE_SLEEP | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:ONLY_GOLD | DROP_1D2 | CAN_SWIM | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | HAS_EGG | IMPRESED |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:1_IN_11 |
+S:SCARE
+D:A strange reptilian creature with two heads, guarding its hoard.
+
+N:302:Swamp thing
+G:H:g
+I:110:8d12:20:60:30
+W:17:2:2000:80
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:TERRIFY:2d5
+B:CLAW:TERRIFY:5d2
+F:CAN_SWIM | OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_SWAMP
+F:MORTAL | BASEANGBAND
+D:A creature that was once human, but is now as green as moss.
+
+N:303:Water spirit
+G:E:b
+I:120:9d8:12:28:40
+W:17:2:0:58
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:2d4
+B:HIT:HURT:2d4
+F:RAND_25 |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR |
+F:IM_POIS | IM_ACID | CAN_FLY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A whirlpool of sentient liquid.
+
+N:304:Giant red scorpion
+G:S:r
+I:110:11d8:12:44:20
+W:17:1:1000:62
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d4
+B:STING:LOSE_STR:1d7
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:It is fast and poisonous.
+
+N:305:Earth spirit
+G:E:u
+I:120:13d8:10:40:50
+W:17:2:0:64
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:RAND_25 |
+F:EMPTY_MIND | COLD_BLOOD |
+F:PASS_WALL | CAN_FLY |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | HURT_ROCK |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A whirling form of sentient rock.
+
+N:306:Fire spirit
+G:E:r
+I:120:10d9:16:30:20
+W:18:2:0:75
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:FIRE:2d6
+B:HIT:FIRE:2d6
+F:RAND_25 |
+F:EMPTY_MIND | BASH_DOOR | CAN_FLY | SUSCEP_COLD |
+F:IM_FIRE | IM_POIS | WILD_TOO | WILD_VOLCANO |
+F:NO_CONF | NO_SLEEP | NO_FEAR | AURA_FIRE | BASEANGBAND | HAS_LITE | NO_CUT
+D:A whirlwind of sentient flame.
+
+N:307:Fire hound
+G:Z:r
+I:110:10d6:30:30:0
+W:18:1:600:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:FIRE:2d6
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:BASH_DOOR | SUSCEP_COLD |
+F:ANIMAL | IM_FIRE | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_10 |
+S:BR_FIRE
+D:Flames lick at its feet and its tongue is a blade of fire. You can feel a
+D:furnace heat radiating from the creature.
+
+N:308:Cold hound
+G:Z:w
+I:110:10d6:30:30:0
+W:18:1:600:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:COLD:2d6
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | IM_COLD | SUSCEP_FIRE |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_COLD
+D:A hound as tall as a man, this creature appears to be composed of angular
+D:planes of ice. Cold radiates from it and freezes your breath in the air.
+
+N:309:Energy hound
+G:Z:b
+I:110:10d6:30:30:0
+W:18:1:600:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:ELEC:2d6
+F:FORCE_SLEEP |
+F:FRIENDS | DROP_SKELETON | DROP_CORPSE |
+F:BASH_DOOR |
+F:ANIMAL | IM_ELEC |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_10 |
+S:BR_ELEC
+D:Saint Elmo's Fire forms a ghostly halo around this hound, and sparks sting
+D:your fingers as energy builds up in the air around you.
+
+N:310:Lesser Mimic
+G:m:y
+I:110:10d10:25:30:250
+W:18:3:100:60
+E:0:0:0:0:0:0
+O:10:10:10:10
+B:HIT:POISON:3d4
+B:HIT:HURT:2d3
+B:HIT:HURT:2d3
+F:MIMIC |
+F:FORCE_SLEEP | NEVER_MOVE | SUSCEP_COLD |
+F:EMPTY_MIND | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BLIND | CONF | SCARE | CAUSE_2 | BO_COLD
+D:A strange creature that disguises itself as some object to lure
+D:unsuspecting adventurers within reach of its venomous claws.
+
+N:311:Door mimic
+G:+:U
+I:110:10d10:25:30:0
+W:18:6:100:70
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:POISON:3d4
+B:HIT:CONFUSE:2d3
+B:HIT:PARALYZE:2d3
+F:CHAR_MULTI |
+F:FORCE_SLEEP | NEVER_MOVE |
+F:EMPTY_MIND | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BLIND | CONF | SCARE | CAUSE_2 | BO_COLD
+D:A strange creature that disguises itself as a door to lure
+D:unsuspecting adventurers within reach of its venomous claws.
+
+N:312:Blink dog
+G:C:B
+I:120:8d8:20:20:10
+W:18:2:400:50
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d8
+F:RAND_25 | FRIENDS | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | RES_TELE | MORTAL | BASEANGBAND
+S:1_IN_4 | BLINK | TELE_TO
+D:A strange magical member of the canine race, its form seems to shimmer and
+D:fade in front of your very eyes.
+
+N:313:Uruk
+G:o:B
+I:110:8d10:20:50:20
+W:16:1:2300:60
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:MALE |
+F:FORCE_MAXHP | FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:ARROW_2
+D:It is a cunning orc of power, as tall as a man, and stronger. It fears
+D:little.
+
+N:314:Shagrat, the Orc Captain
+G:o:g
+I:110:42d10:20:60:20
+W:19:2:2600:400
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:3d9
+B:HIT:HURT:3d9
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | SPECIAL_GENE
+F:ESCORT | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is an orc of power and great cunning, leader of the garrison at Cirith Ungol.
+
+N:315:Gorbag, the Orc Captain
+G:o:g
+I:110:42d10:20:60:20
+W:19:2:2600:400
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:3d9
+B:HIT:HURT:3d9
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP |
+F:ESCORT | DROP_SKELETON | DROP_CORPSE
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is an orc of power and great cunning, leader of the garrison at Minas Morgul.
+
+N:316:Shambling mound
+G:,:g
+I:110:20d6:20:16:40
+W:18:2:3000:75
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:ONLY_GOLD | DROP_90 | WILD_TOO | WILD_SWAMP |
+F:STUPID | EMPTY_MIND | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:SHRIEK
+D:A pile of rotting vegetation that slides towards you with a disgusting
+D:stench, waking all it nears.
+
+N:317:Giant Venus Flytrap
+G:#:g
+I:120:10d10:20:5:0
+W:15:5:200:30
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:PARALYZE:3d3
+B:HIT:PARALYZE:3d3
+B:HIT:PARALYZE:3d3
+F:NEVER_MOVE | EMPTY_MIND | STUPID | CHAR_CLEAR | CHAR_MULTI |
+F:WILD_ONLY | WILD_WOOD | WILD_SWAMP | ANIMAL | SUSCEP_FIRE | NO_CUT
+D:A carnivorous plant that is difficult to detect, until it suddenly snaps shut
+D:around its prey and releases paralysing enzymes to stop its struggles.
+
+N:318:Chaos beastman
+G:H:u
+I:110:20d8:20:50:30
+W:18:2:2300:75
+E:0:0:0:0:0:0
+O:25:45:20:0
+B:HIT:CONFUSE:3d5
+B:HIT:EXP_20:3d5
+F:DROP_1D2
+F:OPEN_DOOR | BASH_DOOR | NO_CONF | NO_SLEEP | DROP_CORPSE
+F:EVIL | IM_POIS | IM_FIRE | ATTR_ANY | ATTR_MULTI
+F:MORTAL | ZANGBAND
+S:1_IN_8
+S:MISSILE | BO_FIRE | CONF | TPORT
+D:A truly loathsome thing, twisted by chaos, it is a mixture
+D:of several different kinds of creature.
+
+N:319:Daemonette of Slaanesh
+G:u:R
+I:120:12d8:20:50:30
+W:18:2:2000:75
+E:1:1:1:0:1:1
+O:30:0:30:40
+B:CLAW:CONFUSE:3d5
+B:CLAW:CONFUSE:3d5
+F:FORCE_MAXHP | DROP_60 | FEMALE |
+F:OPEN_DOOR | BASH_DOOR | IM_COLD | NO_CONF | NO_SLEEP |
+F:EVIL | DEMON | IM_POIS | IM_FIRE | ZANGBAND
+S:1_IN_8
+S:SCARE | S_DEMON | CAUSE_2 | CONF | BO_FIRE | BO_COLD
+D:It is minor female demon, vaguely human-like, but with crab-like
+D:pincers instead of hands. She wears a rather indecent skimpy
+D:leather bikini, moves quickly and casts deadly spells!
+
+N:320:Giant bronze dragon fly
+G:F:U
+I:120:3d8:12:20:50
+W:18:2:150:80
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:CONFUSE:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 | CAN_FLY |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | WILD_MOUNTAIN |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_CONF
+D:This vast gleaming bronze fly has wings which beat mesmerically fast.
+
+N:321:Stone giant
+G:P:W
+I:110:35d18:20:75:50
+W:33:1:7000:250
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+F:DROP_60 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | GIANT | MALE | WILD_TOO | WILD_MOUNTAIN | BASEANGBAND | HAS_LITE
+D:It is eighteen feet tall and looking at you.
+
+N:322:Giant black dragon fly
+G:F:s
+I:120:3d8:12:20:50
+W:20:2:150:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:ACID:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | IM_ACID |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_ACID
+D:The size of a large bird, this fly drips caustic acid.
+
+N:323:Stone golem
+G:g:W
+I:100:28d8:12:75:10
+W:19:2:3500:100
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+F:COLD_BLOOD | EMPTY_MIND | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+D:It is a massive animated statue.
+
+N:324:Red mold
+G:m:r
+I:110:17d8:2:16:70
+W:19:1:40:64
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:FIRE:4d4
+F:NEVER_MOVE | SUSCEP_COLD |
+F:STUPID | EMPTY_MIND |
+F:IM_FIRE | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange red growth on the dungeon floor; it seems to burn with
+D:flame.
+
+N:325:Giant gold dragon fly
+G:F:y
+I:120:3d8:12:20:50
+W:22:2:150:75
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:FORCE_SLEEP |
+F:RAND_50 | RAND_25 | WILD_TOO | WILD_MOUNTAIN |
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | IM_FIRE | CAN_FLY |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_SOUN
+D:Large beating wings support this dazzling insect. A loud buzzing noise
+D:pervades the air.
+
+N:326:Stunwall
+G:#:W
+I:110:4d8:45:25:0
+W:18:5:1000:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:PARALYZE:10d1
+B:TOUCH:PARALYZE:10d1
+F:NEVER_MOVE | IM_COLD | COLD_BLOOD | IM_ACID | IM_ELEC | NO_FEAR |
+F:IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | CHAR_MULTI |
+F:EMPTY_MIND | HURT_ROCK | ZANGBAND | NO_CUT
+D:A sentient section of wall.
+
+N:327:Ghast
+G:z:u
+I:120:30d10:40:40:20
+W:30:1:1500:130
+E:1:1:1:2:1:1
+O:20:35:25:10
+B:CLAW:PARALYZE:2d4
+B:CLAW:PARALYZE:2d4
+B:BITE:LOSE_CON:2d4
+B:BITE:LOSE_CHR:2d4
+F:DROP_60 | OPEN_DOOR | BASH_DOOR | ESCORT |
+F:NO_SLEEP | NO_CONF | UNDEAD | EVIL | IM_POIS | IM_COLD |
+F:COLD_BLOOD | HURT_LITE | CAN_SWIM | BASEANGBAND | NO_CUT
+D:This vile abomination is a relative of ghouls, and often leads packs
+D:of them. It smells foul, and its bite carries a rotting disease.
+
+N:328:Neekerbreeker
+G:I:D
+I:120:3d2:8:18:10
+W:19:4:100:4
+B:BITE:POISON:2d2
+F:RAND_50 | RAND_25 | CAN_FLY | WILD_SWAMP | WILD_TOO |
+F:WEIRD_MIND | ANIMAL | EVIL | BASEANGBAND
+S:MULTIPLY |
+S:1_IN_12 |
+S:SHRIEK
+D:Believed to be an evil relative of the cricket, this creature gets its name
+D:from its incessant squeaking, which can best be described as "neek-breek,
+D:neek-breek". The noise can drive people frantic, and worse still, can be
+D:heard for quite some distance, alerting other monsters to your presence.
+
+N:329:Huorn
+G:#:g
+I:110:50d10:40:45:20
+W:19:1:4000:75
+E:0:0:0:0:0:0
+O:30:30:30:5
+B:CRUSH:HURT:3d6
+B:CRUSH:HURT:3d6
+B:CRUSH:HURT:3d6
+B:CRUSH:HURT:3d6
+F:DROP_60 | NO_SLEEP | NO_CONF | ANIMAL | WEIRD_MIND | SUSCEP_FIRE |
+F:RES_WATE | IM_COLD | NEVER_MOVE | WILD_ONLY | WILD_WOOD |
+F:BASEANGBAND | NO_CUT
+S:1_IN_9
+S:BLINK | TELE_TO
+D:A very strong near-sentient tree, which has become hostile to other living things.
+
+N:330:Bolg, Son of Azog
+G:o:v
+I:120:52d10:20:50:20
+W:20:4:2300:800
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:3d7
+B:HIT:HURT:3d7
+B:HIT:HURT:3d7
+B:HIT:HURT:3d7
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | SPECIAL_GENE |
+F:ESCORT | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A large and powerful orc, he looks just like his father. He is tall and
+D:fast, but fortunately blessed with orcish brains.
+
+N:331:Phase spider
+G:S:B
+I:120:6d8:15:25:80
+W:20:2:500:60
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d8
+B:BITE:POISON:1d6
+B:BITE:POISON:1d6
+F:FRIENDS | WILD_TOO | WILD_WOOD | DROP_SKELETON |
+F:WEIRD_MIND | BASH_DOOR | CAN_SWIM |
+F:ANIMAL | SPIDER | IM_POIS | RES_TELE |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BLINK | TELE_TO
+D:A spider that never seems quite there. Everywhere you look it is just
+D:half-seen in the corner of one eye.
+
+N:332:Lizard king
+G:h:g
+I:120:18d11:20:40:20
+W:20:3:1600:150
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:WAIL:TERRIFY
+F:MALE | CAN_SWIM | IM_ACID | IM_POIS | WILD_SHORE |
+F:DROP_60 | DROP_1D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | FORCE_MAXHP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A lizardman leader.
+
+N:333:Landmine
+G:.:w
+I:110:6d6:30:25:10
+W:20:5:300:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:HURT:25d2
+F:CHAR_CLEAR | ATTR_CLEAR | CHAR_MULTI |
+F:NEVER_MOVE | FORCE_MAXHP |
+F:EMPTY_MIND | INVISIBLE | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | JOKEANGBAND | NO_CUT
+D:It was left here to be used against intruders.
+
+N:334:Wyvern
+G:d:g
+I:120:100d5:20:65:20
+W:22:2:4500:250
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+B:STING:POISON:1d6
+F:FORCE_SLEEP |
+F:ONLY_GOLD | DROP_2D2 | IM_POIS | CAN_FLY | HAS_EGG |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_CORPSE |
+F:WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_SKELETON |
+F:ANIMAL | EVIL | DRAGON | BASEANGBAND | ATTR_MULTI
+D:A fast-moving and deadly draconian animal. Beware its poisonous sting!
+
+N:335:Great eagle
+G:B:u
+I:120:100d5:20:65:20
+W:20:2:1000:150
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:6d3
+B:CLAW:HURT:6d3
+B:BITE:HURT:3d6
+F:CAN_FLY |
+F:WILD_ONLY | WILD_MOUNTAIN | WILD_VOLCANO | WILD_WASTE | WILD_WOOD |
+F:ANIMAL | GOOD | DROP_CORPSE | BASEANGBAND | HAS_EGG
+D:Greater and more intelligent than most of its kind, this great eagle is
+D:a messenger between the forces of good.
+
+N:336:Livingstone
+G:#:W
+I:110:6d8:45:28:20
+W:20:4:1000:56
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:2d5
+B:HIT:HURT:2d5
+F:NEVER_MOVE | IM_COLD | COLD_BLOOD | IM_ACID | IM_ELEC | NO_FEAR |
+F:IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | FRIENDS | CHAR_MULTI | HURT_ROCK |
+F:BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:A sentient section of wall.
+
+N:337:Earth hound
+G:Z:u
+I:110:15d8:30:30:0
+W:20:1:600:200
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d8
+B:BITE:HURT:1d8
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+F:FORCE_SLEEP |
+F:FRIENDS | DROP_SKELETON | DROP_CORPSE |
+F:BASH_DOOR |
+F:ANIMAL | MORTAL | BASEANGBAND | NO_CUT
+S:1_IN_10 |
+S:BR_SHAR
+D:A beautiful crystalline shape does not disguise the danger this hound
+D:clearly presents. Your flesh tingles as it approaches.
+
+N:338:Air hound
+G:Z:g
+I:110:15d8:30:30:0
+W:20:1:600:200
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d8
+B:BITE:POISON:1d8
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+F:FORCE_SLEEP |
+F:FRIENDS | CAN_FLY |
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | IM_POIS | MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_POIS
+D:Swirling vapours surround this beast as it floats towards you, seemingly
+D:walking on air. Noxious gases sting your throat.
+
+N:339:Sabre-tooth tiger
+G:f:y
+I:120:20d14:40:50:0
+W:20:2:1800:120
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d10
+B:CLAW:HURT:1d10
+B:BITE:HURT:1d10
+B:BITE:HURT:1d10
+F:BASH_DOOR | WILD_WOOD | WILD_TOO | WILD_GRASS |
+F:ANIMAL | DROP_SKELETON | DROP_CORPSE
+F:MORTAL | BASEANGBAND
+D:A fierce and dangerous cat, its huge tusks and sharp claws would lacerate
+D:even the strongest armour.
+
+N:340:Acid hound
+G:Z:s
+I:110:15d8:30:30:0
+W:20:1:600:200
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:ACID:1d8
+B:BITE:ACID:1d8
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:BASH_DOOR | CAN_SWIM | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | IM_ACID | MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_ACID
+D:Footprints are burned in the ground behind this hound as it pads
+D:around the dungeon. An acrid smell of acid rises from the dog's pelt.
+
+N:341:Chimaera
+G:H:r
+I:110:20d15:12:15:10
+W:20:2:1600:200
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BUTT:HURT:2d8
+B:BITE:HURT:2d10
+B:BITE:FIRE:2d6
+F:FORCE_SLEEP | CAN_FLY | DROP_CORPSE | SUSCEP_COLD |
+F:BASH_DOOR | WILD_TOO | WILD_MOUNTAIN |
+F:IM_FIRE | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_10 |
+S:BR_FIRE
+D:It is a strange concoction of goat, lion and dragon, with the heads of all
+D:three beasts.
+
+N:342:Quylthulg
+G:Q:y
+I:110:6d8:10:1:0
+W:20:1:3000:250
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW
+F:EMPTY_MIND | INVISIBLE | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_4 |
+S:BLINK |
+S:S_MONSTER
+D:It is a strange pulsing mound of flesh.
+
+N:343:Sasquatch
+G:Y:W
+I:120:20d19:15:40:10
+W:20:3:3500:180
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:1d10
+B:CLAW:HURT:1d10
+B:BITE:HURT:2d8
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD | WILD_WASTE |
+F:ANIMAL | IM_COLD | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A tall shaggy, furry humanoid, it could call the yeti brother.
+
+N:344:Weir
+G:C:W
+I:110:10d12:15:30:40
+W:20:2:2010:150
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BITE:HURT:1d8
+B:BITE:HURT:1d8
+F:RAND_25 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | EVIL | FRIENDS |
+F:MORTAL | ZANGBAND
+D:It is a tracker; half human, half beast.
+
+N:345:Ranger
+G:p:W
+I:110:15d11:20:40:40
+W:20:1:1700:55
+E:1:1:1:2:1:1
+O:20:50:20:0
+B:HIT:HURT:5d4
+B:HIT:HURT:5d4
+F:MALE |
+F:DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:DROP_SKELETON | DROP_CORPSE | BASEANGBAND | MORTAL | HAS_LITE
+S:1_IN_4 |
+S:ARROW_2 | MISSILE | BO_COLD | BO_ELEC | BLINK | S_ANIMAL
+D:A warrior who is at one with nature. A master of both bow and sword, with
+D:minor spellcasting skills.
+
+N:346:Paladin
+G:p:w
+I:110:15d11:20:40:40
+W:20:1:1700:55
+E:1:1:1:2:1:1
+O:20:60:0:10
+B:HIT:HURT:4d5
+B:HIT:HURT:4d5
+F:MALE | GOOD | DROP_SKELETON | DROP_CORPSE |
+F:DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:BASEANGBAND | MORTAL | HAS_LITE
+S:1_IN_4 |
+S:HEAL | CAUSE_2 | SLOW | SCARE | BLIND
+D:A warrior for a holy cause. Unfortunately, his god is not yours, and there is
+D:rivalry even between the various gods of Good, so he is your enemy.
+
+N:347:Werewolf
+G:C:D
+I:110:20d22:15:30:70
+W:20:2:900:150
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:2d10
+F:RAND_25 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON |
+F:ANIMAL | EVIL | MORTAL | BASEANGBAND
+D:It is a huge wolf with eyes that glow with manly intelligence.
+
+N:348:Dark elven lord
+G:h:s
+I:120:18d15:20:40:30
+W:20:2:1400:500
+E:1:1:1:2:1:1
+O:0:80:20:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d5
+F:MALE | FORCE_SLEEP |
+F:ONLY_ITEM | DROP_2D2 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:HASTE | BLIND | CONF | DARKNESS | BO_FIRE | BO_COLD | MISSILE
+D:A dark elven figure in armour and radiating evil power.
+
+N:349:Cloud giant
+G:P:b
+I:110:35d20:20:60:50
+W:36:1:9000:500
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:ELEC:8d8
+B:HIT:ELEC:8d8
+F:DROP_90 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | GIANT | IM_ELEC | MALE | BASEANGBAND | HAS_LITE
+D:It is a twenty foot tall giant wreathed in clouds.
+
+N:350:Ugluk, the Uruk
+G:o:v
+I:110:72d10:20:95:20
+W:21:3:2400:600
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:ESCORT |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_FIRE | IM_COLD | IM_POIS
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A strong and cunning orc warrior, the commander of Saruman's orcish horde.
+
+N:351:Blue dragon bat
+G:b:b
+I:130:4d4:12:26:50
+W:21:1:100:54
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:ELEC:1d3
+F:FORCE_SLEEP |
+F:RAND_50 |
+F:BASH_DOOR | CAN_FLY |
+F:ANIMAL | IM_ELEC | AI_ANNOY
+F:MORTAL | BASEANGBAND
+S:1_IN_4 |
+S:BR_ELEC
+D:It is a glowing blue bat with a sharp tail.
+
+N:352:Mimic
+G:m:y
+I:110:10d14:30:40:0
+W:21:3:100:70
+E:0:0:0:0:0:0
+O:20:20:20:20
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+B:HIT:HURT:2d3
+B:HIT:HURT:2d3
+F:MIMIC |
+F:FORCE_SLEEP | NEVER_MOVE |
+F:EMPTY_MIND | COLD_BLOOD | SUSCEP_FIRE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BLIND | CONF | SCARE | CAUSE_2 | BO_FIRE |
+S:S_MONSTER
+D:A strange creature that disguises itself as an object to lure
+D:unsuspecting adventurers within reach of its venomous claws.
+
+N:353:Ultimate Mimic
+G:m:y
+I:110:15d40:30:40:0
+W:35:4:100:250
+E:0:0:0:0:0:0
+O:25:25:25:25
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+B:BUTT:CONFUSE:4d4
+B:SPIT:BLIND:4d4
+F:MIMIC |
+F:FORCE_SLEEP | NEVER_MOVE |
+F:EMPTY_MIND | COLD_BLOOD
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BLIND | CONF | SCARE | CAUSE_2 | BA_POIS |
+S:S_MONSTER
+D:A strange creature that disguises itself as an object to lure
+D:unsuspecting adventurers within reach of its venomous claws.
+
+
+N:354:Fire vortex
+G:v:r
+I:110:9d9:100:30:0
+W:21:1:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:FIRE:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | WILD_VOLCANO | WILD_TOO | SUSCEP_COLD |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | AURA_FIRE |
+F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | HAS_LITE
+F:NO_CUT
+S:1_IN_6 |
+S:BR_FIRE
+D:A whirling maelstrom of fire.
+
+N:355:Acid vortex
+G:v:s
+I:110:9d9:100:30:0
+W:21:1:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:ACID:3d3
+F:FORCE_SLEEP | RAND_50 |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY |
+F:IM_ACID | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_ACID
+D:A caustic spinning whirlpool of foaming, steaming water.
+
+N:356:Lugdush, the Uruk
+G:o:v
+I:110:66d10:20:90:20
+W:21:4:2500:550
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:ESCORT |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A large and powerful orc, captain of one of Saruman's orcish regiments.
+
+N:357:Arch-vile
+G:u:W
+I:130:11d11:100:30:0
+W:21:1:100:300
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:3d9
+B:CLAW:HURT:3d9
+F:RAND_50 | EVIL | DEMON | FORCE_SLEEP | FORCE_MAXHP |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | COLD_BLOOD |
+F:IM_FIRE | RES_NETH | NO_CONF | NO_SLEEP | NONLIVING | NO_STUN |
+F:ZANGBAND
+S:1_IN_3 |
+S:BA_FIRE | ANIM_DEAD
+D:A pale, corpse-like lesser demon, who moves very fast and spawns evil
+D:everywhere.
+
+N:358:Cold vortex
+G:v:w
+I:110:9d9:100:30:0
+W:21:1:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:COLD:3d3
+F:FORCE_SLEEP | RAND_50 | AURA_COLD | COLD_BLOOD | SUSCEP_FIRE |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY |
+F:IM_COLD | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_COLD
+D:A twisting whirlpool of frost.
+
+N:359:Energy vortex
+G:v:b
+I:110:9d9:100:30:0
+W:21:1:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:ELEC:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | HAS_LITE |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | AURA_ELEC |
+F:IM_ELEC | NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_ELEC
+D:A shimmering tornado of air, sparks crackle along its length.
+
+N:360:Globefish
+G:~:w
+I:110:10d10:20:30:30
+W:21:1:600:111
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:POISON:10d5
+B:BITE:POISON:10d5
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | AQUATIC |
+F:IM_POIS | NO_STUN | WILD_TOO | COLD_BLOOD |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:1_IN_8 |
+S:BR_POIS
+D:This fish is among the most poisonous creatures there are.
+
+N:361:Giant firefly
+G:I:r
+I:120:3d2:8:18:10
+W:24:4:100:4
+B:BITE:BLIND:1d2
+F:RAND_50 | RAND_25 | CAN_FLY |
+F:WEIRD_MIND | ANIMAL | BASEANGBAND
+S:MULTIPLY
+D:Clouds of these monsters light up the dungeon - so brightly that you can
+D:barely see through them.
+
+N:362:Mummified orc
+G:z:w
+I:110:15d8:20:28:75
+W:21:1:1700:56
+E:1:1:1:2:1:1
+O:10:70:0:10
+B:HIT:HURT:2d4
+B:HIT:HURT:2d4
+F:DROP_90 |
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | UNDEAD |
+F:IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is an orcish figure covered in wrappings.
+
+N:363:Wolf chieftain
+G:C:D
+I:120:22d22:20:20:5
+W:26:5:1000:120
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:2d10
+B:WAIL:TERRIFY
+F:ESCORTS | FORCE_MAXHP | IM_COLD | IM_ACID |
+F:SMART | ESCORT | ANIMAL | EVIL | MORTAL | BASEANGBAND |
+F:OPEN_DOOR | BASH_DOOR | NO_FEAR | MALE
+S:1_IN_8 | DARKNESS
+D:A great wolf-chieftain whose pack is in the service of the Dark Lord,
+D:and whose howls strike fear into even the boldest heart.
+
+N:364:Serpent man
+G:J:G
+I:120:15d10:20:40:20
+W:22:3:900:75
+E:1:1:1:2:1:1
+O:25:20:25:20
+B:BITE:POISON:5d5
+B:BITE:POISON:5d5
+F:MALE | CAN_SWIM | IM_POIS | IM_ACID |
+F:DROP_60 | DROP_2D2 | FRIENDS | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | EVIL | MORTAL | ZANGBAND
+S:1_IN_8
+S:BA_POIS | S_MONSTER | SCARE | HOLD
+D:"They walked lithely and sinuously erect on pre-mammalian
+D:members, their pied and hairless bodies bending with great
+D:suppleness. There was a loud hissing of formulae as they
+D:went to and fro."
+
+N:365:Vampiric mist
+G:#:D
+I:110:10d8:12:55:30
+W:22:1:0:40
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:EXP_20:1d6
+B:ENGULF:EXP_20:1d6
+F:RAND_25 | SUSCEP_ELEC | UNDEAD |
+F:IM_COLD | IM_POIS | IM_ACID | RES_NETH | WILD_TOO | WILD_SWAMP |
+F:EVIL | EMPTY_MIND | COLD_BLOOD | FRIENDS | BASEANGBAND | NO_CUT
+D:A cloud of evil, sentient mist.
+
+N:366:Killer stag beetle
+G:K:g
+I:110:15d8:12:55:30
+W:22:1:500:80
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d12
+B:CLAW:HURT:1d12
+F:RAND_25 | WILD_TOO | DROP_CORPSE |
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | CAN_FLY | MORTAL | BASEANGBAND
+D:It is a giant beetle with vicious claws.
+
+N:367:Iron golem
+G:g:s
+I:110:80d12:12:80:10
+W:22:2:3800:160
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d12
+F:FORCE_SLEEP | SUSCEP_ACID |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:SLOW
+D:It is a massive metal statue that moves steadily towards you.
+
+N:368:Auto-roller
+G:g:s
+I:120:70d12:10:80:12
+W:22:2:0:230
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:1d8
+B:CRUSH:HURT:1d8
+B:CRUSH:HURT:1d8
+B:CRUSH:HURT:1d8
+F:FORCE_SLEEP | RES_TELE
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS
+F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | JOKEANGBAND | NO_CUT
+D:It looks like a huge spiked roller, moving on its own towards you.
+
+N:369:Giant yellow scorpion
+G:S:y
+I:110:12d8:12:38:20
+W:22:1:1200:60
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d8
+B:STING:POISON:2d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO |
+F:ANIMAL | DROP_SKELETON |
+F:MORTAL | BASEANGBAND
+D:It is a giant scorpion with a sharp stinger.
+
+N:370:Jade monk
+G:p:G
+I:120:10d9:22:45:5
+W:23:1:1200:100
+E:1:1:1:2:1:1
+O:30:0:60:10
+B:KICK:HURT:4d1
+B:KICK:HURT:4d1
+B:HIT:HURT:4d5
+B:HIT:HURT:4d5
+F:OPEN_DOOR | BASH_DOOR | NO_FEAR | NO_CONF | NO_SLEEP |
+F:MALE | DROP_60 | DROP_1D2 | IM_FIRE | IM_COLD | IM_POIS |
+F:DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | ZANGBAND | HAS_LITE
+D:A monk trained in martial arts.
+
+N:371:Black ooze
+G:j:D
+I:90:6d8:10:6:1
+W:23:1:400:7
+E:0:0:0:0:0:0
+O:30:0:40:15
+B:TOUCH:ACID:2d6
+F:RAND_50 | DROP_60 | STUPID | EMPTY_MIND | CAN_SWIM |
+F:TAKE_ITEM | KILL_BODY | OPEN_DOOR | BASH_DOOR |
+F:IM_POIS | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:MULTIPLY
+S:1_IN_11 |
+S:DRAIN_MANA
+D:It is a strangely moving puddle.
+
+N:372:Hardened warrior
+G:p:u
+I:110:15d11:20:40:40
+W:23:1:1900:60
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:6d5
+B:HIT:HURT:6d5
+F:MALE |
+F:DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+D:A scarred warrior who moves with confidence.
+
+N:373:Azog, King of the Uruk-Hai
+G:o:v
+I:120:94d10:20:80:20
+W:23:5:2700:1111
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:5d6
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:ESCORT | ESCORTS | SPECIAL_GENE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is also known as the King of Khazad-dum. His ego is renowned to be
+D:bigger than his head.
+
+N:374:Fleshhound of Khorne
+G:C:R
+I:120:25d25:15:30:70
+W:30:3:700:150
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:4d1
+B:CLAW:HURT:4d1
+B:BITE:HURT:6d1
+B:BITE:HURT:6d1
+F:BASH_DOOR | DEMON | NO_STUN | NO_FEAR |
+F:ANIMAL | EVIL | IM_FIRE | NO_SLEEP | NO_CONF |
+F:RES_NETH | RES_NEXU | RES_DISE | RES_PLAS | ZANGBAND
+D:A revolting canine creature, a huge demon hound with a somewhat
+D:reptilian head.
+
+N:375:Dark elven warlock
+G:h:v
+I:120:7d10:20:16:20
+W:23:1:1700:75
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:MALE |
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR | FRIENDS
+F:EVIL | IM_POIS | HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:CONF | MISSILE | DARKNESS | BO_MANA
+D:A dark elven mage with spells of frightening destructive power.
+
+N:376:Master rogue
+G:p:b
+I:120:15d9:20:30:40
+W:23:2:1600:110
+E:1:1:1:2:1:1
+O:80:10:10:0
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+B:HIT:EAT_GOLD:4d4
+F:MALE | DROP_SKELETON | DROP_CORPSE |
+F:DROP_2D2 | SUSCEP_ELEC |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+D:A thief of great power and shifty speed.
+
+N:377:Red dragon bat
+G:b:r
+I:130:3d8:12:28:50
+W:23:1:100:60
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:FIRE:1d3
+F:FORCE_SLEEP | RAND_50 | SUSCEP_COLD |
+F:BASH_DOOR | CAN_FLY | DROP_CORPSE | AI_ANNOY
+F:ANIMAL | IM_FIRE | BASEANGBAND
+S:1_IN_4 |
+S:BR_FIRE
+D:It is a sharp-tailed bat, wreathed in fire.
+
+N:378:Killer white beetle
+G:K:w
+I:110:18d8:14:55:30
+W:23:1:500:85
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:4d5
+F:RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | CAN_FLY | MORTAL | BASEANGBAND
+D:It is looking for prey.
+
+N:379:Ice skeleton
+G:s:w
+I:110:16d9:20:34:60
+W:23:1:0:70
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:CLAW:COLD:2d3
+B:CLAW:COLD:2d3
+F:ONLY_ITEM | DROP_90 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | SUSCEP_FIRE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a skeleton covered in frost.
+
+N:380:Angamaite of Umbar
+G:p:U
+I:110:82d10:25:80:25
+W:24:2:2400:400
+E:1:1:1:2:1:1
+O:0:80:20:0
+B:HIT:HURT:4d7
+B:HIT:HURT:4d7
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_FIRE | IM_ELEC
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:SLOW | FORGET
+D:A Black Numenorean who hates the men of the west.
+
+N:381:Forest wight
+G:W:g
+I:110:12d8:20:30:30
+W:24:1:0:140
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:TOUCH:EXP_20
+F:FORCE_SLEEP | RAND_25 |
+F:DROP_60 | DROP_90 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_10 |
+S:SCARE | DRAIN_MANA
+D:It is a ghostly apparition with a humanoid form.
+
+N:382:Khim, Son of Mim
+G:h:o
+I:110:84d10:20:80:10
+W:24:2:2300:300
+E:1:1:1:2:1:1
+O:10:80:0:10
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:HURT:3d7
+B:HIT:UN_BONUS
+F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:IM_FIRE | IM_COLD
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:HEAL | SLOW | BO_FIRE
+D:One of the last of the Petty-Dwarves. Khim is a tricky sorcerous little
+D:being, full of mischief.
+
+N:383:Ibun, Son of Mim
+G:h:o
+I:110:84d10:20:80:10
+W:24:2:2300:300
+E:1:1:1:2:1:1
+O:10:80:0:5
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:HURT:3d7
+B:HIT:UN_BONUS
+F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:IM_FIRE | IM_COLD
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:HEAL | SLOW | BO_FIRE
+D:One of the last of the Petty-Dwarves. Ibun is a tricky sorcerous little
+D:being, full of mischief.
+
+N:384:Meneldor the Swift
+G:B:u
+I:140:80d10:20:65:20
+W:24:6:1200:360
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:7d3
+B:CLAW:HURT:7d3
+B:BITE:HURT:3d7
+F:CAN_FLY | UNIQUE | FORCE_MAXHP | DROP_CORPSE | CAN_SPEAK |
+F:WILD_ONLY | WILD_MOUNTAIN | WILD_VOLCANO | PET |
+F:ANIMAL | GOOD | BASEANGBAND
+D:Among all the eagles of Middle-earth he is the swiftest, and in his time
+D:has borne messages between all of the Wise. It was Meneldor who bore the
+D:Ring-bearer away from the destruction of Mount Doom.
+
+N:385:Phantom beast
+G:G:B
+I:110:12d12:20:40:40
+W:24:1:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:2d33
+B:HIT:HURT:2d44
+F:PASS_WALL | NO_SLEEP | COLD_BLOOD | NONLIVING | NO_FEAR |
+F:FORCE_MAXHP | RES_TELE | EMPTY_MIND | CAN_FLY | BASEANGBAND | NO_CUT
+D:A creature that is half real, half illusion.
+
+N:386:Giant silver ant
+G:a:W
+I:110:9d8:10:38:40
+W:23:1:800:45
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:ACID:4d4
+F:RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_SKELETON |
+F:ANIMAL | MORTAL | BASEANGBAND | HAS_LITE
+D:A giant silver ant with a caustic bite.
+
+N:387:4-headed hydra
+G:M:y
+I:120:100d6:20:70:20
+W:24:2:5000:450
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+F:FORCE_SLEEP |
+F:ONLY_GOLD | DROP_4D2 | WILD_TOO | WILD_SWAMP | WILD_SHORE |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:1_IN_7 |
+S:SCARE
+D:A strange reptilian creature with four heads, guarding its hoard.
+
+N:388:Lesser hell-beast
+G:U:s
+I:115:15d100:10:100:50
+W:24:4:4000:400
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:TERRIFY:1d4
+B:GAZE:TERRIFY:1d4
+B:CRUSH:HURT:44d1
+F:EVIL | IM_ACID | IM_ELEC | IM_FIRE | IM_POIS | IM_COLD |
+F:RES_NETH | RES_WATE | RES_PLAS | RES_DISE | RES_NEXU |
+F:KILL_WALL | FORCE_MAXHP | CAN_SWIM | DROP_CORPSE | JOKEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:TPORT | BLINK | TELE_AWAY
+D:This creature just might crush you.
+
+N:389:Tyrannosaur
+G:R:g
+I:120:200d3:20:70:20
+W:24:2:5000:350
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d6
+B:CLAW:HURT:1d6
+B:BITE:HURT:3d6
+B:BITE:HURT:3d6
+F:FORCE_SLEEP |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_CORPSE |
+F:ANIMAL | MORTAL | ZANGBAND
+D:A horror from prehistory, reawakened by chaos.
+
+N:390:Mummified human
+G:z:w
+I:110:17d9:20:34:60
+W:24:1:1500:70
+E:1:1:1:2:1:1
+O:20:40:20:10
+B:HIT:HURT:2d4
+B:HIT:HURT:2d4
+F:ONLY_ITEM | DROP_90 |
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a human form encased in mouldy wrappings.
+
+N:391:Vampire bat
+G:b:D
+I:120:9d10:12:40:50
+W:24:2:50:150
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:EXP_40:1d4
+B:BITE:EXP_40:1d4
+F:RAND_50 | COLD_BLOOD | REGENERATE | CAN_FLY |
+F:EVIL | ANIMAL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:A blood-sucking bat that flies at your neck hungrily.
+
+N:392:Sangahyando of Umbar
+G:p:U
+I:110:82d10:25:80:25
+W:24:2:2400:400
+E:1:1:1:2:1:1
+O:0:90:10:0
+B:HIT:HURT:4d7
+B:HIT:HURT:4d7
+B:HIT:HURT:4d7
+B:HIT:HURT:4d7
+F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_FIRE | IM_ELEC
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:SLOW | FORGET
+D:A Black Numenorean with a blacker heart.
+
+N:393:It
+G:.:W
+I:110:77d9:25:80:25
+W:24:3:500:400
+E:0:0:0:0:0:0
+O:10:0:90:0
+B:GAZE:BLIND:8d8
+B:TOUCH:TERRIFY
+B:GAZE:EXP_40
+B:TOUCH:EAT_ITEM
+F:ONLY_ITEM | DROP_90 | DROP_1D2 | DROP_GOOD | DROP_GREAT | DROP_CORPSE |
+F:CHAR_MULTI | CHAR_CLEAR | ATTR_CLEAR | INVISIBLE | COLD_BLOOD |
+F:NO_CONF | UNIQUE | FORCE_MAXHP | NO_SLEEP | CAN_SPEAK | REFLECTING |
+F:IM_FIRE | IM_ELEC | EMPTY_MIND | EVIL | SMART | RES_TELE | CAN_FLY |
+F:ZANGBAND
+S:1_IN_4
+S:DRAIN_MANA | BLINK | BLIND | SCARE | CONF | S_UNDEAD | S_MONSTER |
+S:HEAL | TELE_AWAY | DARKNESS | S_HYDRA | TRAPS | FORGET | TELE_TO | SHRIEK
+D:Nobody has ever seen It.
+
+N:394:Banshee
+G:G:b
+I:120:6d8:20:24:10
+W:24:2:0:60
+E:0:0:0:0:0:0
+O:80:0:0:15
+B:WAIL:TERRIFY
+B:TOUCH:EXP_20
+F:FEMALE |
+F:RAND_50 | DROP_1D2 |
+F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_15 |
+S:TPORT | DRAIN_MANA
+D:It is a ghostly woman's form that wails mournfully.
+
+N:395:Carrion crawler
+G:c:o
+I:110:20d12:15:40:10
+W:25:2:700:60
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:STING:PARALYZE:2d6
+B:STING:PARALYZE:2d6
+F:RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON |
+F:ANIMAL | IM_POIS | MORTAL | BASEANGBAND
+D:A hideous centipede covered in slime and with glowing tentacles around its
+D:head.
+
+N:396:Xiclotlan
+G:#:D
+I:110:25d13:15:60:10
+W:25:2:6000:60
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:6d4
+B:CRUSH:HURT:6d4
+B:BITE:HURT:3d2
+F:RAND_25 | SUSCEP_ACID |
+F:EMPTY_MIND | BASH_DOOR |
+F:IM_POIS | IM_ELEC |
+F:MORTAL | CTHANGBAND | NO_CUT
+D:"...a metallically grey tree... about sixteen feet high with
+D:very thick cylindrical branches... cylinders further
+D:divided into six flat circular extensions... and from the top
+D:of what I had taken for a trunk rose a featureless oval... an
+D:orifice gaping at the top."
+
+N:397:Silent watcher
+G:g:s
+I:110:80d25:60:80:0
+W:35:3:4000:800
+E:3:0:3:3:2:0
+O:0:0:0:0
+B:GAZE:TERRIFY
+B:GAZE:PARALYZE
+B:GAZE:LOSE_STR
+B:GAZE:HALLU
+F:EMPTY_MIND | COLD_BLOOD | NONLIVING | NEVER_MOVE |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | EVIL |
+F:HURT_ROCK | COLD_BLOOD | HURT_LITE | NO_FEAR |
+F:NO_CONF | NO_SLEEP | NO_STUN | NONLIVING | RES_TELE | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:SHRIEK | S_MONSTER | S_MONSTERS | HOLD | CONF | MIND_BLAST | DRAIN_MANA
+D:A figure carved from stone, with three vulture faces whose eyes glow
+D:with a malevolent light. None escape its vigilance.
+
+N:398:Pukelman
+G:g:D
+I:110:80d12:12:80:10
+W:25:3:10000:600
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d12
+B:HIT:HURT:3d6
+F:FORCE_SLEEP |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:SLOW | CONF | BO_ACID
+D:A stumpy figure carved from stone, with glittering eyes.
+
+N:399:Disenchanter beast
+G:q:v
+I:110:30d30:12:60:12
+W:25:2:2000:250
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:TOUCH:UN_BONUS:1d10
+B:TOUCH:UN_BONUS:1d10
+B:TOUCH:UN_BONUS:1d10
+B:TOUCH:UN_BONUS:1d10
+F:STUPID | WEIRD_MIND | RES_DISE | DROP_CORPSE | ATTR_MULTI |
+F:IM_ACID | IM_POIS |
+F:NO_CONF | ZANGBAND
+D:It looks like an anteater, and there is a static feeling
+D:crackling around its long trunk.
+
+N:400:Dark elven druid
+G:h:G
+I:120:20d20:15:75:10
+W:25:3:1200:500
+E:1:1:1:2:1:1
+O:10:0:80:10
+B:HIT:HURT:1d7
+B:HIT:HURT:1d7
+B:HIT:HURT:3d8
+F:MALE |
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:HEAL | CONF | DARKNESS |
+S:S_MONSTER | S_SPIDER | S_ANIMAL
+D:A powerful dark elf, with mighty nature-controlling enchantments.
+
+N:401:Stone troll
+G:T:W
+I:110:23d10:20:50:50
+W:25:1:5000:85
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:BITE:HURT:3d4
+F:MALE |
+F:FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | REGENERATE |
+F:EVIL | TROLL | HURT_LITE | HURT_ROCK | BASEANGBAND | NO_CUT
+D:He is a giant troll with scabrous black skin.
+
+N:402:Black
+G:j:d
+I:111:12d12:12:65:30
+W:25:1:0:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:EXP_40:2d6
+B:ENGULF:EXP_40:2d6
+F:RAND_25 |
+F:IM_COLD | IM_POIS | IM_ACID | RES_NETH |
+F:EVIL | EMPTY_MIND | COLD_BLOOD | FRIENDS | CTHANGBAND | NO_CUT
+D:The eldritch blood of Yibb-Tstll is know only as "the Black": it is
+D:an amorphous substance, which will suck your life and deliver it to
+D:Yibb-Tstll.
+
+N:403:Hill troll
+G:T:s
+I:110:21d10:20:65:40
+W:21:1:4000:75
+E:1:1:1:2:1:1
+O:30:70:0:0
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+B:BITE:HURT:2d6
+F:MALE |
+F:FRIENDS | DROP_60 | WILD_TOO | WILD_WASTE | WILD_MOUNTAIN |
+F:DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | SUSCEP_FIRE | REGENERATE |
+F:EVIL | TROLL | HURT_LITE | BASEANGBAND
+D:A large troll with an extremely tough and warty hide.
+
+N:404:Wereworm
+G:w:u
+I:110:100d11:15:70:20
+W:25:3:6000:300
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:GAZE:EXP_20
+B:CRAWL:ACID:2d4
+B:BITE:HURT:1d10
+B:BITE:POISON:1d6
+F:BASH_DOOR | EVIL | CAN_SWIM | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | IM_ACID | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A huge wormlike shape dripping acid, twisted by evil sorcery into a foul
+D:monster that breeds on death.
+
+N:405:Killer red beetle
+G:K:r
+I:110:20d8:14:50:30
+W:25:1:600:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:LOSE_STR:4d4
+F:RAND_25 | WILD_TOO |
+F:WEIRD_MIND | BASH_DOOR | DROP_CORPSE |
+F:ANIMAL | CAN_FLY | MORTAL | BASEANGBAND
+D:A giant beetle with poisonous mandibles.
+
+N:406:Disenchanter bat
+G:b:v
+I:130:6d8:12:28:50
+W:26:4:50:75
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:HIT:UN_BONUS
+B:HIT:UN_BONUS
+F:FORCE_SLEEP | RAND_50 | ANIMAL | MORTAL | ATTR_MULTI |
+F:DROP_CORPSE | AI_ANNOY | CAN_FLY | BASEANGBAND | WEIRD_MIND
+D:A giant bat which feeds on raw magical energy.
+
+N:407:Gnoph-Keh
+G:q:s
+I:110:20d8:12:50:25
+W:26:2:1500:95
+E:0:1:0:2:1:0
+O:20:40:20:10
+B:CLAW:COLD:2d4
+B:CLAW:COLD:2d4
+B:BUTT:HURT:2d9
+F:RAND_25 | DROP_90 | DROP_60
+F:OPEN_DOOR | BASH_DOOR | AURA_COLD | IM_COLD | SUSCEP_FIRE |
+F:ANIMAL | DROP_CORPSE | CTHANGBAND
+S:1_IN_8
+S:BR_COLD | BO_ICEE | BO_COLD | BA_COLD
+D:A creature with a sharp horn: "the hairy myth-thing of the
+D:Greenland ice, that walked sometimes on two legs, sometimes
+D:on four, and sometimes on six."
+
+N:408:Giant grey ant
+G:a:s
+I:110:19d8:10:40:40
+W:26:1:700:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d12
+F:RAND_25 | KILL_BODY | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | BASEANGBAND
+D:It is an ant encased in shaggy grey fur.
+
+N:409:Khufu, the Mummified King
+G:z:v
+I:110:85d11:20:40:40
+W:26:4:1800:500
+E:1:1:1:2:1:1
+O:0:80:20:0
+B:GAZE:TERRIFY
+B:HIT:DISEASE:6d6
+B:CLAW:LOSE_CON:4d4
+B:CLAW:LOSE_CON:4d4
+F:UNIQUE | MALE | CAN_SPEAK | UNDEAD | EVIL | ESCORTS | ESCORT |
+F:FORCE_MAXHP | COLD_BLOOD | IM_POIS | IM_COLD | NO_FEAR | NO_CONF | NO_SLEEP |
+F:ONLY_ITEM | DROP_90 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | RES_TELE | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:TRAPS | CAUSE_3 | DARKNESS | S_UNDEAD | SCARE | SLOW |
+S:S_KIN | ANIM_DEAD
+D:He is out to have revenge on those who have desecrated his tomb.
+
+N:410:Gwaihir the Windlord
+G:B:u
+I:130:85d10:20:65:20
+W:24:6:1200:360
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:15d2
+B:CLAW:HURT:15d2
+B:BITE:HURT:3d10
+F:CAN_FLY | UNIQUE | FORCE_MAXHP | DROP_CORPSE | CAN_SPEAK |
+F:WILD_ONLY | WILD_MOUNTAIN | WILD_VOLCANO | PET |
+F:ANIMAL | GOOD | BASEANGBAND
+D:The greatest of eagles in the Third Age of Middle-earth, Gwaihir rescued
+D:Gandalf the Wizard from Orthanc, and has twice brought his flock to the
+D:aid of Sauron's enemies in battle - first outside the gates of Erebor in
+D:the Battle of Five Armies, and then before the Black Gate of Mordor itself.
+
+N:411:Giant fire tick
+G:S:R
+I:110:16d8:14:54:20
+W:26:2:200:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:3d6
+F:RAND_25 | SUSCEP_COLD |
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | IM_FIRE | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is smoking and burning with great heat.
+
+N:412:Displacer beast
+G:f:b
+I:110:25d10:35:100:20
+W:26:2:1800:100
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d8
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+F:INVISIBLE | BASH_DOOR | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is a huge black panther, clubbed tentacles sprouting from its shoulders.
+
+N:413:Ulwarth, Son of Ulfang
+G:p:U
+I:110:85d10:20:40:40
+W:26:4:1800:500
+E:1:1:1:2:1:1
+O:40:60:0:0
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_MAXHP | WILD_TOO |
+F:ONLY_ITEM | DROP_90 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A short and swarthy Easterling.
+
+N:414:Werebear
+G:q:D
+I:110:25d25:20:50:20
+W:24:2:0:200
+E:0:1:0:2:1:0
+O:25:25:25:20
+B:CLAW:HURT:1d10
+B:CLAW:HURT:1d10
+B:BITE:HURT:2d8
+B:CRUSH:HURT:2d6
+F:BASH_DOOR | OPEN_DOOR |
+F:WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_CORPSE |
+F:ANIMAL | EVIL | DROP_1D2 | BASEANGBAND
+D:In the eyes of this bear, there glimmers the faintest light of intelligence.
+D:And then its form begins to change... The combination of animal cunning,
+D:human intelligence and the great physical strength of the bear makes for
+D:a dangerous enemy.
+
+N:415:Cave ogre
+G:O:u
+I:110:30d9:20:33:30
+W:26:2:2500:80
+E:1:1:1:2:1:1
+O:20:70:0:10
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+F:FRIENDS | DROP_60 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A giant orc-like figure with an awesomely muscled frame.
+
+N:416:White wraith
+G:W:w
+I:110:15d8:20:40:10
+W:26:1:0:175
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:TOUCH:EXP_20
+F:FORCE_SLEEP |
+F:DROP_1D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:SCARE | CAUSE_2 | DARKNESS
+D:It is a tangible but ghostly form made of white fog.
+
+N:417:Angel
+G:A:o
+I:110:30d10:30:60:255
+W:26:4:2600:220
+E:1:1:1:2:1:1
+O:0:40:60:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR |
+F:ONLY_ITEM | DROP_2D2 | GOOD | CAN_FLY |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:IM_ACID | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BLIND | CONF | SCARE | FORGET
+D:Who knows why you are now facing an angel as an enemy? Is it a punishment
+D:from your own god for your sins, or a test of your mettle, or has this angel
+D:been sent by another god on behalf of someone who wishes to claim the glory
+D:for his own god? Perhaps you will never know: but it is here, and you must
+D:fight it.
+
+N:418:Ghoul
+G:z:U
+I:110:15d9:30:30:20
+W:25:1:0:95
+E:1:1:1:2:1:1
+O:0:45:35:10
+B:CLAW:DISEASE:1d4
+B:CLAW:DISEASE:1d4
+B:BITE:PARALYZE:1d5
+F:DROP_60 | OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:EVIL | UNDEAD | FRIENDS | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP |
+F:COLD_BLOOD | HURT_LITE | BASEANGBAND | NO_CUT
+S:1_IN_9
+S:SCARE | HOLD
+D:Flesh is falling off in chunks from this decaying abomination.
+
+N:419:Mim, Betrayer of Turin
+G:h:o
+I:120:11d105:20:80:20
+W:27:4:1200:1000
+E:1:1:1:2:1:1
+O:10:80:10:0
+B:HIT:HURT:3d9
+B:HIT:HURT:3d9
+B:HIT:HURT:3d9
+B:HIT:UN_BONUS:3d12
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | FORCE_SLEEP | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_DISE | RES_TELE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:HEAL | SCARE | BO_ACID | BA_ACID | TPORT | S_MONSTER
+D:The last of his race, Mim is a Petty-Dwarf. Petty-Dwarves are strange
+D:creatures, powerful in sorcery and originating in the East. They have
+D:been hunted nearly to extinction by the High Elves.
+
+N:420:Hellblade
+G:|:v
+I:120:13d13:20:40:20
+W:27:2:0:130
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:EXP_20:2d13
+B:HIT:EXP_20:2d13
+F:CHAR_MULTI | EVIL | IM_POIS | IM_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | SUSCEP_ACID |
+F:COLD_BLOOD | BASH_DOOR | NONLIVING |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | HAS_LITE | NO_CUT
+D:A deadly blade of chaos, moving of its own volition.
+
+N:421:Killer fire beetle
+G:K:R
+I:110:22d8:14:45:30
+W:27:1:600:95
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:3d4
+B:SPIT:FIRE:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | WILD_VOLCANO | DROP_CORPSE |
+F:ANIMAL | IM_FIRE | CAN_FLY | SUSCEP_COLD |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle wreathed in flames.
+
+N:422:Beast of Nurgle
+G:q:y
+I:110:15d7:14:50:30
+W:27:2:2300:100
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:TOUCH:POISON:3d3
+B:TOUCH:DISEASE:3d3
+B:BITE:ACID:4d5
+F:WEIRD_MIND | BASH_DOOR | IM_ACID | IM_POIS |
+F:DEMON | EVIL | IM_FIRE | CAN_FLY | ZANGBAND
+D:It walks on four legs, but mostly resembles a slug-shaped jelly.
+D:It even has two disgusting antennae in its head. Yecch!
+
+N:423:Creeping adamantite coins
+G:$:G
+I:120:20d25:5:50:10
+W:27:3:0:60
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:POISON:3d4
+B:TOUCH:POISON:3d5
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+F:ONLY_GOLD | DROP_90 | DROP_2D2 |
+F:COLD_BLOOD | BASH_DOOR | CHAR_MULTI |
+F:IM_ELEC | IM_ACID | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It appears to be a pile of coins made of precious adamant, slithering toward
+D:you on lots of tiny legs.
+
+N:424:Algroth
+G:T:o
+I:110:21d12:20:60:40
+W:27:1:6000:150
+E:1:1:1:2:1:1
+O:10:80:0:10
+B:CLAW:POISON:3d3
+B:CLAW:POISON:3d3
+B:BITE:HURT:1d6
+F:FRIENDS | DROP_60 | WILD_WOOD | WILD_MOUNTAIN | WILD_TOO | WILD_SWAMP |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | REGENERATE |
+F:EVIL | TROLL | BASEANGBAND
+D:A powerful troll form. Venom drips from its needlelike claws.
+
+N:425:Flamer of Tzeentch
+G:,:r
+I:110:60d15:10:70:20
+W:27:2:0:500
+E:1:0:1:2:0:0
+O:0:0:0:0
+B:ENGULF:FIRE:3d6
+B:ENGULF:FIRE:3d6
+F:FORCE_SLEEP | BASH_DOOR | SUSCEP_COLD |
+F:IM_FIRE | IM_POIS | AURA_FIRE | DEMON | EVIL | RES_PLAS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | ZANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BO_FIRE | BA_FIRE
+D:It looks like an inverted mushroom, with two flexible arms.
+
+N:426:Roper
+G:#:D
+I:115:30d10:30:60:255
+W:27:5:0:235
+E:0:0:0:6:0:0
+O:0:0:0:0
+B:CRUSH:PARALYZE:3d5
+B:CRUSH:PARALYZE:3d5
+B:CRUSH:PARALYZE:3d5
+B:CRUSH:PARALYZE:3d5
+F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR | NEVER_MOVE |
+F:ONLY_GOLD | DROP_2D2 | DROP_60 | DROP_1D2 | EVIL |
+F:IM_ACID | IM_POIS | NO_CONF | NO_SLEEP | IM_COLD | IM_FIRE | BASEANGBAND
+F:NO_CUT
+S:1_IN_5 |
+S:BA_FIRE | BA_ELEC | BA_POIS | HASTE |
+S:TRAPS | SHRIEK | HOLD | CONF
+D:This creature look like a pillar of rock. However, a closer
+D:inspection reveals a glaring eye and powerful tentacles,
+D:which crush its prey and feed it to the creature's hungry
+D:mouth.
+
+N:427:Headless
+G:H:u
+I:110:25d12:20:50:40
+W:27:1:1600:175
+E:1:1:1:2:0:1
+O:0:100:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:FRIENDS | DROP_60 | OPEN_DOOR | BASH_DOOR | WILD_TOO |
+F:WILD_MOUNTAIN | WILD_WASTE | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | ZANGBAND
+S:1_IN_6
+S:SCARE
+D:Headless humanoid abominations created by a magical mutation.
+
+N:428:Vibration hound
+G:Z:y
+I:110:25d10:30:30:0
+W:27:2:600:250
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d8
+B:BITE:HURT:2d8
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:BASH_DOOR |
+F:ANIMAL | NO_CONF | NO_SLEEP | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_SOUN
+D:A blurry canine form which seems to be moving as fast as the eye can
+D:follow. You can feel the earth resonating beneath your feet.
+
+N:429:Nexus hound
+G:Z:v
+I:110:25d10:30:30:0
+W:27:2:600:250
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d8
+B:BITE:HURT:2d8
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+F:FORCE_SLEEP | RES_NEXU | DROP_SKELETON | DROP_CORPSE |
+F:FRIENDS |
+F:BASH_DOOR |
+F:ANIMAL | RES_TELE | NO_SLEEP | MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_NEXU
+D:A locus of conflicting points coalesce to form the vague shape of a huge
+D:hound. Or is it just your imagination?
+
+N:430:Half-ogre
+G:O:o
+I:110:35d9:20:33:30
+W:27:2:2700:80
+E:1:1:1:2:1:1
+O:20:70:0:10
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+F:FRIENDS | DROP_60 | DROP_CORPSE | SMART |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A giant, brutish figure, as ugly as an orc, but with some of the
+D:intelligence of his half-human ancestry.
+
+N:431:Lokkak, the Ogre Chieftain
+G:O:v
+I:120:15d103:20:100:20
+W:32:2:3000:1500
+E:1:1:1:2:1:1
+O:5:85:0:5
+B:HIT:HURT:6d7
+B:HIT:HURT:6d7
+B:HIT:HURT:6d7
+F:UNIQUE | MALE | CAN_SPEAK | WILD_TOO | WILD_SWAMP | WILD_SHORE |
+F:FORCE_MAXHP | ESCORT |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:EVIL | GIANT | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:An ogre renowned for acts of surprising cruelty, Lokkak is the leader of a large
+D:band of violent ogres.
+
+N:432:Vampire
+G:V:W
+I:110:25d12:20:45:10
+W:27:1:1700:175
+E:1:1:1:2:1:1
+O:20:40:20:10
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:BITE:EXP_20:2d6
+B:BITE:EXP_20:2d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:COLD_BLOOD | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP |
+F:BASEANGBAND | NO_CUT
+S:1_IN_9 |
+S:TELE_TO | HOLD | SCARE | CAUSE_2 | MIND_BLAST | FORGET | DARKNESS
+D:It is a humanoid with an aura of power. You notice a sharp set of front
+D:teeth.
+
+N:433:Gorgimaera
+G:H:o
+I:110:25d20:12:55:10
+W:27:2:2300:400
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BUTT:HURT:2d10
+B:BITE:FIRE:2d10
+B:GAZE:PARALYZE:2d6
+F:FORCE_SLEEP | SUSCEP_COLD |
+F:BASH_DOOR | CAN_FLY | DROP_CORPSE |
+F:IM_FIRE | BASEANGBAND
+S:1_IN_8 |
+S:BR_FIRE
+D:The result of evil experiments, this travesty of nature should never be
+D:alive. It has three heads - goat, dragon and gorgon - all attached to a
+D:lion's body.
+
+N:434:Shantak
+G:H:D
+I:120:25d20:12:55:10
+W:27:2:6000:280
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+B:BITE:HURT:1d6
+B:BITE:HURT:1d6
+F:ANIMAL | IM_ACID | EVIL | ELDRITCH_HORROR | CAN_FLY | DROP_CORPSE |
+F:MORTAL | ZANGBAND
+S:1_IN_6
+S:SCARE | HASTE
+D:A scaly bird larger than an elephant, with a horse-like head.
+
+N:435:Colbran
+G:g:y
+I:120:80d12:12:80:10
+W:27:2:0:900
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:ELEC:3d8
+B:HIT:ELEC:3d8
+F:FORCE_SLEEP |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | CAN_FLY |
+F:IM_ELEC | IM_POIS | AURA_ELEC | REFLECTING |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BO_ELEC
+D:A man-shaped form of living lightning, sparks and shocks crackle all over
+D:this madly capering figure, as it leaps and whirls around and about you.
+
+N:436:Spirit naga
+G:n:w
+I:110:30d15:20:75:120
+W:28:2:0:60
+E:0:0:0:0:1:0
+O:20:0:80:0
+B:CRUSH:HURT:2d8
+B:CRUSH:HURT:2d8
+B:BITE:HURT:1d8
+B:BITE:HURT:1d8
+F:FEMALE |
+F:FORCE_SLEEP | CAN_FLY |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 | DROP_CORPSE |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:HEAL | BLIND | MIND_BLAST | DARKNESS
+D:A wraithly snake-like form with the torso of a beautiful woman, it is the
+D:most powerful of its kind.
+
+N:437:Corpser
+G:,:D
+I:112:30d15:20:75:120
+W:28:2:0:65
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:2d8
+B:CRUSH:HURT:2d8
+B:CRUSH:HURT:2d8
+B:CRUSH:HURT:2d8
+F:WILD_TOO | WILD_GRASS |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:MORTAL | ZANGBAND
+D:A creature who lives underground and eats surface dwellers that it catches
+D:with its poisonous tentacle, which is the only part of the creature that
+D:is ever seen. Perhaps it is better not to see the part which remains
+D:underground...
+
+N:438:Fiend of Slaanesh
+G:S:R
+I:120:15d20:12:50:40
+W:28:4:0:225
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:STING:POISON:8d1
+B:STING:LOSE_STR:8d1
+B:WAIL:TERRIFY:1d4
+F:BASH_DOOR |
+F:EVIL | DEMON | ZANGBAND
+D:Slaanesh's pet, a large scorpion-like creature with a human face and
+D:reptile skin.
+
+N:439:Stairway to Hell
+G:>:W
+I:120:15d8:90:40:20
+W:28:5:0:125
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:WAIL:UN_BONUS
+B:WAIL:EXP_20
+B:WAIL:EAT_GOLD
+B:WAIL:EAT_ITEM
+F:CHAR_MULTI | COLD_BLOOD | EVIL | NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING |
+F:UNDEAD | FORCE_MAXHP | IM_FIRE | IM_ELEC | IM_POIS | IM_ACID | EMPTY_MIND
+F:NEVER_MOVE | JOKEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_15
+S:S_DEMON | SHRIEK
+D:Often found in graveyards.
+
+N:440:5-headed hydra
+G:M:g
+I:120:100d8:20:80:20
+W:28:2:5500:650
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+F:FORCE_SLEEP |
+F:ONLY_GOLD | DROP_1D2 | DROP_4D2 | DROP_SKELETON | DROP_CORPSE
+F:BASH_DOOR | MOVE_BODY | CAN_SWIM |
+F:ANIMAL | IM_POIS | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:SCARE | BA_POIS
+D:A strange reptilian creature with five heads dripping venom.
+
+N:441:Barney the Dinosaur
+G:R:v
+I:120:110d9:10:90:20
+W:29:2:5000:500
+E:0:1:0:2:1:0
+O:10:0:90:0
+B:SHOW:LOSE_INT
+B:SHOW:LOSE_WIS
+B:CHARGE:EAT_GOLD
+B:CHARGE:EAT_GOLD
+F:DROP_1D2 | DROP_90 | DROP_GOOD | DROP_GREAT | ONLY_ITEM |
+F:EVIL | MALE | CAN_SPEAK | SMART | RES_TELE | CAN_SWIM | DROP_CORPSE
+F:ANIMAL | IM_POIS | FORCE_MAXHP | UNIQUE | FORCE_SLEEP
+F:MORTAL | JOKEANGBAND
+S:1_IN_3
+S:SHRIEK | CONF | S_HYDRA | SLOW | BLIND | DRAIN_MANA
+S:BA_POIS | BR_CHAO | FORGET | DARKNESS | BR_NUKE
+D:The lovable purple reptile is making a guest appearance here.
+
+N:442:Black knight
+G:p:s
+I:120:30d10:20:70:10
+W:28:1:2400:240
+E:1:1:1:2:1:1
+O:0:90:10:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:MALE |
+F:FORCE_SLEEP | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:BLIND | SCARE | CAUSE_3 | DARKNESS
+D:He is a figure encased in deep black plate armour; he looks at you
+D:menacingly.
+
+N:443:Seahorse
+G:~:o
+I:120:111d7:20:60:20
+W:28:2:3000:360
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:PARALYZE:4d5
+B:BITE:LOSE_DEX:4d5
+B:BITE:LOSE_CON:4d5
+F:FORCE_SLEEP | AQUATIC | GOOD | ANIMAL |
+F:IM_COLD | IM_POIS | IM_FIRE | IM_ELEC | WILD_TOO | WILD_OCEAN
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BO_WATE | BO_COLD | BO_ICEE | BO_MANA
+D:Your mind is filled with admiration as you view this wondrous,
+D:magical seahorse.
+
+N:444:Cyclops
+G:P:u
+I:120:60d20:20:90:20
+W:45:2:3500:1500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:9d9
+B:HIT:HURT:9d9
+F:FORCE_SLEEP |
+F:DROP_1D2 | TAKE_ITEM | WILD_TOO | WILD_SHORE | WILD_MOUNTAIN |
+F:BASH_DOOR | OPEN_DOOR | MOVE_BODY | DROP_CORPSE |
+F:EVIL | IM_POIS | IM_ACID | IM_FIRE | IM_COLD | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:ARROW_4
+D:A gigantic humanoid with but one eye.
+
+N:445:Clairvoyant
+G:p:y
+I:120:25d10:100:50:10
+W:28:3:1600:250
+E:1:1:1:2:1:1
+O:0:0:90:10
+B:HIT:CONFUSE:5d5
+B:HIT:TERRIFY:5d5
+F:MALE |
+F:FORCE_SLEEP | DROP_1D2 | DROP_SKELETON | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR | SMART |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:BLIND | MIND_BLAST | HOLD | CAUSE_3 | FORGET | S_MONSTER
+D:He is using his supernatural talents to bring about your
+D:destruction.
+
+N:446:Purple worm
+G:w:v
+I:110:65d8:14:65:30
+W:29:4:1000:400
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:HIT:HURT:1d8
+B:BITE:ACID:2d8
+B:STING:POISON:1d8
+F:BASH_DOOR | DROP_CORPSE |
+F:ANIMAL | IM_ACID | IM_POIS | CAN_SWIM |
+F:MORTAL | BASEANGBAND
+D:It is a massive worm form, many feet in length. Its vast maw drips acid
+D:and poison.
+
+N:447:Catoblepas
+G:q:g
+I:110:30d10:15:55:40
+W:29:2:0:400
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:TERRIFY:2d4
+B:GAZE:BLIND:2d4
+B:BUTT:HURT:2d6
+B:BITE:HURT:2d12
+F:ONLY_GOLD | DROP_2D2 | DROP_CORPSE
+F:BASH_DOOR | CAN_SWIM | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | IM_POIS
+F:MORTAL | BASEANGBAND
+D:A strange ox-like form with a huge head but a thin, weak neck, it looks
+D:like the creation of some deranged alchemist.
+
+N:448:Lesser wall monster
+G:#:W
+I:110:13d8:20:75:40
+W:28:4:0:600
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+F:FORCE_SLEEP | COLD_BLOOD | EMPTY_MIND | KILL_WALL | RAND_50 |
+F:BASH_DOOR | IM_COLD | IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | NONLIVING |
+F:HURT_ROCK | NO_CONF | NO_SLEEP | CHAR_MULTI | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:A sentient, moving section of wall.
+
+N:449:Mage
+G:p:r
+I:110:15d8:20:40:10
+W:28:1:1500:150
+E:1:1:1:2:1:1
+O:10:0:90:0
+B:HIT:HURT:2d5
+B:HIT:HURT:2d5
+F:MALE |
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON | DROP_CORPSE |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HASTE | TPORT | TELE_TO | BLIND | CONF |
+S:BO_FIRE | BO_COLD | BO_ELEC |
+S:S_MONSTER
+D:A fat mage with glasses. And considerable power, too - as you can
+D:tell from the size of his hat.
+
+N:450:Mind flayer
+G:h:v
+I:110:15d10:20:60:10
+W:28:1:1400:200
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:CRUSH:LOSE_INT:2d6
+B:GAZE:INSANITY:2d6
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:ONLY_ITEM | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP
+F:MORTAL | BASEANGBAND
+S:1_IN_8 |
+S:BLIND | HOLD | SCARE | MIND_BLAST | BRAIN_SMASH | FORGET
+D:A humanoid form with a gruesome head, tentacular mouth, and piercing
+D:eyes. Claws reach out for you and you feel a presence invade your mind.
+
+N:451:The Ultimate Dungeon Cleaner
+G:g:D
+I:120:70d12:10:80:12
+W:28:2:0:555
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:2d10
+B:CRUSH:HURT:2d10
+B:CRUSH:HURT:2d10
+B:CRUSH:HURT:2d10
+F:FORCE_SLEEP | FORCE_MAXHP | KILL_BODY | KILL_ITEM | UNIQUE | REFLECTING |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS
+F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | RES_TELE | JOKEANGBAND | NO_CUT
+D:It looks like a huge spiked roller. It was designed to keep this dungeon
+D:clean, and you are the biggest spot of dirt in sight.
+
+N:452:Deep one
+G:u:g
+I:120:35d12:20:50:20
+W:28:5:4000:150
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:CLAW:POISON:2d4
+B:CLAW:POISON:2d4
+B:BITE:HURT:4d4
+F:FRIENDS | WILD_TOO | WILD_SHORE | DROP_CORPSE |
+F:RAND_25 |
+F:DROP_60 | DROP_90 |
+F:CAN_SWIM | BASH_DOOR | ELDRITCH_HORROR | RES_TELE |
+F:EVIL | DEMON | IM_FIRE | IM_COLD | IM_POIS | RES_WATE | CTHANGBAND
+D:"I thought their predominant color was a greyish-green,
+D:though they had white bellies. They were mostly shiny and
+D:slippery, but the ridges of their backs were scaly. Their
+D:forms vaguely suggested the anthropoid, while their heads were
+D:the heads of fish, with prodigious bulging eyes that never
+D:closed. At the sides of their necks were palpitating gills and
+D:their long paws were webbed."
+
+N:453:Basilisk
+G:R:s
+I:120:20d30:15:90:30
+W:28:3:400:350
+E:0:1:0:2:1:0
+O:50:0:30:20
+B:GAZE:PARALYZE
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+F:ONLY_ITEM | DROP_1D2 | WILD_TOO | WILD_MOUNTAIN |
+F:OPEN_DOOR | BASH_DOOR | EVIL | IM_POIS | DROP_CORPSE |
+F:ANIMAL | NO_CONF | NO_SLEEP | CAN_SWIM |
+F:MORTAL | BASEANGBAND
+S:1_IN_8 |
+S:BR_POIS
+D:An evil reptile that preys on unsuspecting travellers. Its eyes stare
+D:deeply at you and your soul starts to wilt!
+
+N:454:Ice troll
+G:T:w
+I:110:24d10:20:56:50
+W:28:1:5000:200
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d5
+B:HIT:HURT:1d5
+B:BITE:COLD:2d6
+B:BITE:COLD:2d6
+F:MALE |
+F:FRIENDS | DROP_60 | WILD_TOO | WILD_WASTE | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | SUSCEP_FIRE | REGENERATE |
+F:EVIL | TROLL | IM_COLD | HURT_LITE | BASEANGBAND
+D:He is a white troll with powerful clawed hands.
+
+N:455:Dhole
+G:w:s
+I:110:65d8:14:64:25
+W:29:4:1000:555
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:SPIT:ACID:1d8
+B:ENGULF:ACID:2d8
+B:CRUSH:HURT:4d8
+F:ANIMAL | EVIL | KILL_WALL | IM_ACID | ELDRITCH_HORROR |
+F:CAN_SWIM | FORCE_MAXHP | DROP_CORPSE |
+F:MORTAL | CTHANGBAND
+S:1_IN_6
+S:BR_ACID
+D:A gigantic worm dripping with acid. "...even as he looked, one
+D:reared up several hundred feet and leveled a bleached, viscous end
+D:at him."
+
+N:456:Archangel
+G:A:B
+I:110:60d10:30:68:255
+W:34:4:4000:1000
+E:1:1:1:2:1:1
+O:0:40:60:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR | GOOD |
+F:ONLY_ITEM | DROP_2D2 | CAN_FLY |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | RES_TELE |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | HASTE | BLIND | CONF | SCARE
+D:A lesser angel protected by an aura of holiness. Its muscular form looks
+D:extremely powerful next to your own frail body.
+
+N:457:Greater Mimic
+G:m:y
+I:120:10d35:30:60:100
+W:29:3:100:200
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+F:MIMIC |
+F:FORCE_SLEEP | NEVER_MOVE | SUSCEP_ACID |
+F:EMPTY_MIND | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:BLIND | CONF | SCARE | CAUSE_2 | FORGET |
+S:BO_ACID | BO_FIRE | BO_COLD | BO_ELEC |
+S:S_MONSTER
+D:A strange creature that disguises itself as an object to lure
+D:unsuspecting adventurers within reach of its venomous claws.
+
+N:458:Chaos tile
+G:.:v
+I:120:3d5:30:60:100
+W:29:6:100:200
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:POISON:3d4
+B:HIT:CONFUSE:3d4
+F:CHAR_MULTI | ATTR_MULTI | ATTR_ANY |
+F:FORCE_SLEEP | NEVER_MOVE |
+F:EMPTY_MIND | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | ZANGBAND | NO_CUT
+S:MULTIPLY |
+S:1_IN_4 |
+S:BLIND | CONF | SCARE | CAUSE_2 | BLINK | S_MONSTER
+D:It is a floor tile corrupted by chaos.
+
+N:459:Young blue dragon
+G:d:b
+I:110:27d10:20:50:70
+W:29:1:20000:500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:2d6
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD |
+F:DROP_2D2 | DROP_CORPSE | ATTR_MULTI
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | DRAGON | IM_ELEC | BASEANGBAND | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_ELEC
+D:It has a form out of legend. Its still-tender scales are a
+D:deep blue in hue. Sparks crackle along its length.
+
+N:460:Young white dragon
+G:d:w
+I:110:27d10:20:50:70
+W:29:1:20000:500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:2d6
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN | WILD_WASTE |
+F:DROP_2D2 | DROP_CORPSE | ATTR_MULTI
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY | SUSCEP_FIRE |
+F:EVIL | DRAGON | IM_COLD | BASEANGBAND | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_COLD
+D:It has a form out of legend. Its still-tender scales are a
+D:frosty white in hue. Icy blasts of cold air come from it as it breathes.
+
+N:461:Young green dragon
+G:d:g
+I:110:27d10:20:50:70
+W:29:1:20000:500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:2d6
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN | WILD_SWAMP |
+F:DROP_2D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | DRAGON | IM_POIS | BASEANGBAND | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_POIS
+D:It has a form out of legend. Its still-tender scales are a
+D:deep green in hue. Foul gas seeps through its scales.
+
+N:462:Young bronze dragon
+G:d:U
+I:110:27d10:20:50:70
+W:29:1:20000:500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:2d6
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN |
+F:DROP_2D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | DRAGON | NO_CONF | BASEANGBAND | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_CONF
+D:It has a form out of legend. Its still-tender scales are a
+D:rich bronze hue, and its shape masks its true form.
+
+N:463:Aklash
+G:T:R
+I:110:30d8:14:64:25
+W:29:4:4000:300
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:1d6
+B:CLAW:HURT:1d6
+B:BITE:POISON:1d20
+B:CRUSH:HURT:2d9
+F:TROLL | EVIL | FRIENDS | OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:IM_POIS | IM_ACID | REGENERATE | ZANGBAND | REGENERATE |
+S:1_IN_9 | BR_POIS
+D:Pale, bald, fat, hairless troll creatures. Ugly beyond description.
+D:Not to mention how bad their breath smells...
+
+N:464:Mithril golem
+G:g:B
+I:110:80d15:12:100:10
+W:30:4:10000:500
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:ONLY_GOLD | DROP_2D2 |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | REFLECTING |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+D:It is a massive statue of purest mithril. It looks expensive!
+
+N:465:Skeleton troll
+G:s:W
+I:110:20d10:20:55:20
+W:30:1:5000:225
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:BITE:HURT:3d4
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | TROLL | UNDEAD |
+F:IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a troll skeleton animated by dark dweomers.
+
+N:466:Skeletal tyrannosaur
+G:R:w
+I:120:50d10:20:55:20
+W:30:2:6000:225
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d8
+B:CLAW:HURT:1d8
+B:BITE:LOSE_CON:4d6
+B:GAZE:TERRIFY
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | ANIMAL | UNDEAD |
+F:IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | ZANGBAND | NO_CUT
+D:It is the skeleton of a tyrannosaur, animated by dark dweomers.
+
+N:467:Beorn, the Shape-Changer
+G:q:D
+I:120:20d70:25:60:25
+W:28:3:0:1000
+E:0:1:0:2:1:0
+O:20:60:20:0
+B:CLAW:HURT:2d10
+B:CLAW:HURT:2d10
+B:BITE:HURT:4d8
+B:CRUSH:HURT:3d6
+F:UNIQUE | MALE | FORCE_MAXHP | FORCE_SLEEP |
+F:BASH_DOOR | ANIMAL | MOVE_BODY | SMART | PET
+F:WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | BASEANGBAND
+S:1_IN_6 | S_ANIMAL
+D:Beorn is only occasionally seen in human form these days, preferring to
+D:appear in the shape of a giant black bear: he also prefers the company of
+D:beasts to that of humans. He has never taken kindly to strangers, even in
+D:human form - and still less when in bear's shape, as he is now.
+
+N:468:Thorondor, Lord of Eagles
+G:B:u
+I:130:85d12:20:65:20
+W:30:6:1600:555
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:16d2
+B:CLAW:HURT:16d2
+B:BITE:HURT:4d10
+F:CAN_FLY | UNIQUE | FORCE_MAXHP | DROP_CORPSE | CAN_SPEAK |
+F:WILD_ONLY | WILD_MOUNTAIN | WILD_VOLCANO | PET |
+F:ANIMAL | GOOD | BASEANGBAND
+D:Among the mightiest of birds, Thorondor is the messenger of the Valar, and
+D:brings news of Middle-earth to Valinor itself. Nothing that can be seen
+D:from the airs of the world is hidden from him.
+
+N:469:Giant blue ant
+G:a:b
+I:110:8d8:10:50:60
+W:30:2:600:80
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:ELEC:5d5
+F:RAND_25 | WILD_TOO | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | IM_ELEC
+F:MORTAL | BASEANGBAND
+D:It is a giant ant that crackles with energy.
+
+N:470:Grave wight
+G:W:b
+I:110:12d10:20:50:30
+W:30:1:0:325
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:1d7
+B:HIT:HURT:1d7
+B:TOUCH:EXP_20
+F:FORCE_SLEEP | RAND_25 |
+F:ONLY_ITEM | DROP_1D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | CAN_FLY |
+F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:SCARE | CAUSE_3 | DARKNESS
+D:It is a ghostly form with eyes that haunt you.
+
+N:471:Shadow drake
+G:d:G
+I:110:30d10:20:70:70
+W:33:3:18000:1100
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:EXP_10:3d6
+F:FORCE_SLEEP | FORCE_MAXHP | PASS_WALL |
+F:ONLY_ITEM | DROP_3D2 | CAN_FLY | DROP_CORPSE |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:BR_NETH | SLOW | CONF | SCARE | DARKNESS
+D:It is a dragon-like form wrapped in shadow. Glowing red eyes shine in
+D:the dark, and it is surrounded by an aura of unearthly cold that chills
+D:the soul rather than the body.
+
+N:472:Manticore
+G:H:y
+I:120:25d10:12:15:10
+W:30:2:1900:300
+E:1:1:1:2:1:0
+O:0:0:0:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:BASH_DOOR | CAN_FLY | WILD_TOO | WILD_WOOD | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:ARROW_4
+D:It is a winged lion's body with a human torso and a tail covered in
+D:vicious spikes.
+
+N:473:Giant army ant
+G:a:o
+I:120:19d6:10:40:40
+W:30:3:600:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d12
+F:RAND_25 | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL
+F:MORTAL | BASEANGBAND
+D:An armoured form moving with purpose. Powerful on its own, flee when
+D:hordes of them march.
+
+N:474:Killer slicer beetle
+G:K:y
+I:110:25d10:14:60:30
+W:30:2:600:200
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:5d8
+B:BITE:HURT:5d8
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | WILD_GRASS | DROP_CORPSE
+F:ANIMAL | CAN_FLY | MORTAL | BASEANGBAND
+D:It is a beetle with deadly sharp cutting mandibles and a rock-hard
+D:carapace.
+
+N:475:Gorgon
+G:H:b
+I:110:30d20:12:88:20
+W:31:2:3000:275
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BUTT:HURT:3d9
+B:BUTT:HURT:3d9
+B:BITE:POISON:1d10
+B:KICK:HURT:2d4
+F:FORCE_SLEEP | ANIMAL | MOVE_BODY | WILD_TOO | WILD_WOOD |
+F:BASH_DOOR | IM_POIS | IM_ACID | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_8 |
+S:BR_POIS
+D:A bull-like creature whose skin is made of steel plates. Watch out for
+D:its deadly breath!
+
+N:476:Gug
+G:P:G
+I:110:22d11:14:60:30
+W:31:2:1500:210
+E:2:0:2:4:1:1
+O:0:50:50:0
+B:BITE:HURT:10d4
+B:CLAW:HURT:2d7
+B:CLAW:HURT:2d7
+B:CLAW:HURT:2d7
+F:FORCE_SLEEP |
+F:DROP_1D2 | TAKE_ITEM |
+F:BASH_DOOR | OPEN_DOOR | DROP_CORPSE
+F:EVIL | IM_POIS | IM_ACID | GIANT | ZANGBAND
+D:A hideous, beastly, four-armed unclean giant: "...large as a
+D:barrel... The eyes jutted two inches from each side, shaded by
+D:bony protuberances overgrown of the mouth. That mouth had great
+D:yellow fangs and ran from the top to the tally."
+
+N:477:Ghost
+G:G:w
+I:120:13d8:20:30:10
+W:31:1:0:350
+E:0:0:0:0:0:0
+O:30:40:0:20
+B:WAIL:TERRIFY
+B:TOUCH:EXP_20
+B:CLAW:LOSE_INT:1d6
+B:CLAW:LOSE_WIS:1d6
+F:FORCE_SLEEP | RAND_25 | DROP_60 | DROP_1D2 |
+F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | CAN_FLY |
+F:IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLIND | HOLD | DRAIN_MANA
+D:You don't believe in them.
+
+N:478:Death watch beetle
+G:K:D
+I:110:25d12:16:60:30
+W:31:3:800:220
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:5d4
+B:WAIL:TERRIFY:5d6
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | CAN_FLY | MORTAL | BASEANGBAND
+D:It is a giant beetle that produces a chilling sound.
+
+N:479:Mountain ogre
+G:O:s
+I:110:40d9:20:33:30
+W:30:2:3000:100
+E:1:1:1:2:1:1
+O:20:70:0:10
+B:HIT:HURT:5d8
+B:HIT:HURT:5d8
+F:FRIENDS | DROP_60 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:The largest breed of ogre, he is awesomely strong and awesomely ugly.
+
+N:480:Nexus quylthulg
+G:Q:v
+I:110:10d12:10:1:0
+W:32:1:3000:300
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | RES_NEXU | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | RES_TELE | BASEANGBAND
+S:1_IN_1 |
+S:BLINK | TELE_AWAY
+D:It is a very unstable, strange pulsing mound of flesh.
+
+N:481:Shelob, Spider of Darkness
+G:S:D
+I:120:35d100:30:120:80
+W:55:3:1400:27000
+E:0:1:0:2:1:0
+O:20:30:40:0
+B:CLAW:POISON:5d6
+B:CLAW:POISON:5d6
+B:BITE:PARALYZE:5d10
+B:STING:LOSE_STR:5d4
+F:UNIQUE | FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP | SPECIAL_GENE |
+F:ESCORT | ESCORTS | DROP_CORPSE |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD | DROP_RANDART
+F:SMART | BASH_DOOR | IM_POIS | IM_ACID |
+F:ANIMAL | SPIDER | EVIL | HURT_LITE | NO_SLEEP | BASEANGBAND
+S:1_IN_4 |
+S:HEAL | BLIND | SLOW | CONF | SCARE | CAUSE_3 | CAUSE_4 |
+S:TRAPS | BR_DARK | BR_POIS |
+S:S_SPIDER
+D:Shelob is an enormous bloated spider, the last daughter of Ungoliant the
+D:Unlight. Her poison is legendary, as is her ego. She normally guards the
+D:pass through Cirith Ungol, but occasionally goes out foraging for food to
+D:feed her voracious appetite.
+
+N:482:Giant squid
+G:~:g
+I:110:80d10:8:80:80
+W:32:3:3000:600
+E:3:0:3:6:1:0
+O:0:0:0:0
+B:CRUSH:HURT:8d4
+B:CRUSH:HURT:8d4
+B:CRUSH:HURT:8d4
+F:IM_ACID | RES_WATE | AQUATIC | ANIMAL | IM_COLD | MOVE_BODY
+F:FORCE_MAXHP | WILD_TOO | WILD_OCEAN | BASEANGBAND | COLD_BLOOD
+S:1_IN_8
+S:BR_ELEC | BR_ACID | BR_POIS
+D:Besides being capable of dragging whole ships underwater,
+D:this creature can also harm you with ranged attacks.
+
+N:483:Ghoulking
+G:z:D
+I:120:40d12:20:60:10
+W:32:2:0:340
+E:1:1:1:2:1:1
+O:20:40:30:0
+B:CLAW:LOSE_STR:3d4
+B:CLAW:DISEASE:3d4
+B:CLAW:DISEASE:3d4
+B:BITE:PARALYZE:3d5
+F:DROP_60 | OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:DROP_1D2 | FORCE_MAXHP | ESCORT | FORCE_SLEEP |
+F:EVIL | UNDEAD | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP |
+F:COLD_BLOOD | HURT_LITE | BASEANGBAND | NO_CUT
+S:1_IN_7
+S:SCARE | HOLD | DARKNESS | SCARE | S_UNDEAD | ANIM_DEAD
+D:Flesh is falling off in chunks from this decaying abomination.
+
+N:484:Doombat
+G:b:R
+I:120:24d14:16:75:30
+W:32:2:150:250
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:FIRE:5d4
+B:BITE:FIRE:5d4
+B:BITE:FIRE:5d4
+F:WEIRD_MIND | BASH_DOOR | AURA_FIRE | CAN_FLY | DROP_CORPSE |
+F:IM_FIRE | AI_ANNOY |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is a fast moving creature of chaos, a gigantic black bat
+D:surrounded by flickering bright red flames.
+
+N:485:Ninja
+G:p:u
+I:120:13d12:20:60:10
+W:32:2:1600:300
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:POISON:3d4
+B:HIT:LOSE_STR:3d4
+B:HIT:LOSE_STR:3d4
+F:MALE |
+F:DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A humanoid clothed in black who moves with blinding speed.
+
+N:486:Memory moss
+G:,:b
+I:110:1d2:30:1:5
+W:32:3:50:150
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:CONFUSE:1d4
+B:HIT:CONFUSE:1d4
+F:FORCE_SLEEP | NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:FORGET
+D:A mass of vegetation. You don't remember seeing anything like it
+D:before.
+
+N:487:Storm giant
+G:P:B
+I:110:40d20:20:60:40
+W:40:1:13000:1000
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:ELEC:10d8
+B:HIT:ELEC:10d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_1D2 | WILD_TOO | WILD_MOUNTAIN |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | AURA_ELEC | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | GIANT | IM_COLD | IM_ELEC | MALE | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:BLINK | TELE_TO | CONF | SCARE | BO_ELEC | BA_ELEC
+D:It is a twenty-five foot tall giant wreathed in lightning.
+
+N:488:Spectator
+G:e:B
+I:110:15d13:30:1:5
+W:28:2:1600:150
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:PARALYZE:1d4
+B:GAZE:CONFUSE:1d4
+B:BITE:HURT:1d8
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:STUPID | EMPTY_MIND | CAN_FLY | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | BASEANGBAND
+S:1_IN_6 |
+S:FORGET | CAUSE_2 | HOLD | SLOW
+D:A lesser relative of the beholder: a globular body with a large toothy mouth,
+D:a large central eye, and four smaller eyes on stalks protruding from the top
+D:of its body.
+
+N:489:Bokrug
+G:R:v
+I:110:11d90:20:70:50
+W:33:7:0:2200
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:BITE:HURT:5d5
+B:CRUSH:HURT:2d10
+B:CRUSH:HURT:2d10
+F:UNIQUE | CAN_SPEAK |
+F:FORCE_MAXHP | NONLIVING | NO_CONF | NO_FEAR |
+F:ESCORT | ESCORTS | ELDRITCH_HORROR |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:EVIL | IM_COLD | IM_POIS | DEMON | CTHANGBAND
+S:1_IN_5
+S:S_UNDEAD | MIND_BLAST | CAUSE_3 | SCARE | BO_WATE | S_KIN
+D:A lizard-like Great Old One worshipped by the men of Sarnath.
+
+N:490:Biclops
+G:P:u
+I:120:65d20:20:90:20
+W:48:5:3000:1700
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:10d9
+B:HIT:HURT:10d9
+F:FORCE_SLEEP |
+F:DROP_1D2 | TAKE_ITEM | DROP_CORPSE |
+F:BASH_DOOR | OPEN_DOOR | MOVE_BODY |
+F:EVIL | IM_POIS | IM_ACID | IM_FIRE | IM_COLD | GIANT |
+F:MORTAL | ZANGBAND | HAS_LITE
+S:1_IN_8 |
+S:ARROW_4
+D:Oh, no! Aaargh! It is the most unnatural, most disgusting
+D:creature imaginable: a two-eyed cyclops! This perversion
+D:of nature must be exterminated!
+
+N:491:Half-troll
+G:T:U
+I:110:25d14:20:50:50
+W:34:2:3000:400
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:CLAW:HURT:1d5
+B:CLAW:HURT:1d5
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+F:MALE |
+F:FRIENDS |
+F:ONLY_ITEM | DROP_90 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | REGENERATE |
+F:EVIL | TROLL | IM_POIS | BASEANGBAND
+D:A huge, ugly, half-human in search of plunder.
+
+N:492:Ivory monk
+G:p:w
+I:120:38d9:22:55:5
+W:33:1:1600:900
+E:1:1:1:2:1:1
+O:40:0:50:10
+B:KICK:HURT:8d1
+B:HIT:HURT:8d4
+B:KICK:HURT:8d1
+B:HIT:HURT:8d4
+F:OPEN_DOOR | BASH_DOOR | NO_FEAR | NO_CONF | NO_SLEEP | DROP_SKELETON |
+F:DROP_CORPSE | MALE | DROP_60 | DROP_1D2 | IM_FIRE | IM_COLD | IM_POIS |
+F:MORTAL | ZANGBAND | HAS_LITE
+D:A monk trained in the most lethal martial arts.
+
+N:493:Bert the Stone Troll
+G:T:W
+I:120:11d100:20:70:50
+W:34:7:5000:2000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:BITE:HURT:3d10
+F:UNIQUE | MALE |
+F:FORCE_MAXHP |
+F:ESCORT | SPECIAL_GENE | MOVE_BODY | REGENERATE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | WILD_TOO | WILD_WOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | HURT_ROCK | BASEANGBAND | NO_CUT
+D:Big, brawny, powerful and with a taste for hobbit. He has friends called
+D:Bill and Tom.
+
+N:494:Bill the Stone Troll
+G:T:W
+I:120:11d100:20:70:50
+W:34:7:5000:2000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:BITE:HURT:3d10
+F:UNIQUE | MALE |
+F:FORCE_MAXHP |
+F:ESCORT | SPECIAL_GENE | MOVE_BODY | REGENERATE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | WILD_TOO | WILD_WOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE
+F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | HURT_ROCK | BASEANGBAND | NO_CUT
+D:Big, brawny, powerful and with a taste for hobbit. He has friends called
+D:Bert and Tom.
+
+N:495:Tom the Stone Troll
+G:T:W
+I:120:11d100:20:70:50
+W:34:7:5000:2000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:BITE:HURT:3d10
+F:UNIQUE | MALE |
+F:FORCE_MAXHP |
+F:ESCORT | SPECIAL_GENE | MOVE_BODY | REGENERATE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | WILD_TOO | WILD_WOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | HURT_ROCK | BASEANGBAND | NO_CUT
+D:Big, brawny, powerful and with a taste for hobbit. He has friends called
+D:Bert and Bill.
+
+N:496:Cave troll
+G:T:u
+I:110:24d12:20:50:50
+W:33:1:6000:350
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:BITE:HURT:1d8
+B:BITE:HURT:1d8
+F:MALE |
+F:FRIENDS | DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | SUSCEP_FIRE | REGENERATE |
+F:EVIL | TROLL | IM_POIS | HURT_LITE | BASEANGBAND
+D:He is a vicious monster, feared for his ferocity.
+
+N:497:Anti-paladin
+G:p:D
+I:120:30d20:30:50:30
+W:33:2:2400:450
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:2d6
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:MALE | OPEN_DOOR | BASH_DOOR | TAKE_ITEM | DROP_1D2 | ONLY_ITEM |
+F:EVIL | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP | DROP_SKELETON | DROP_CORPSE
+F:MORTAL | ZANGBAND | HAS_LITE
+S:1_IN_4
+S:HOLD | SCARE | BLIND | CAUSE_3 | TRAPS | DARKNESS | FORGET | HASTE
+D:An embodiment of all the cardinal vices, he beholds you scornfully.
+
+N:498:Chaos master
+G:p:v
+I:120:30d10:30:50:5
+W:37:3:2300:550
+E:1:1:1:2:1:1
+O:10:60:10:10
+B:HIT:HURT:10d2
+B:KICK:HURT:10d2
+B:PUNCH:HURT:10d2
+B:KICK:HURT:10d2
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | EVIL |
+F:ONLY_ITEM | DROP_1D2 | ATTR_ANY | DROP_SKELETON | DROP_CORPSE
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_POIS | NO_CONF | NO_SLEEP
+F:MORTAL | ZANGBAND | HAS_LITE
+S:1_IN_6 |
+S:HEAL |
+S:S_SPIDER | BA_CHAO | S_DEMON
+D:An adept of chaos, feared for his skill of invoking raw Chaos.
+
+N:499:Barrow wight
+G:W:v
+I:110:15d10:20:40:10
+W:33:3:0:375
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+B:TOUCH:EXP_40
+F:FORCE_SLEEP | FRIENDS | DROP_60 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:HOLD | SCARE | CAUSE_2 | DARKNESS
+D:It is a ghostly nightmare of an entity.
+
+N:500:Skeleton ettin
+G:s:W
+I:110:45d10:20:50:20
+W:33:1:7000:325
+E:1:1:1:2:2:1
+O:0:0:0:0
+B:HIT:HURT:1d9
+B:HIT:HURT:1d9
+B:BITE:HURT:1d5
+B:BITE:HURT:1d5
+F:FORCE_MAXHP |
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | TROLL | UNDEAD |
+F:IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is the animated skeleton of a massive two-headed troll.
+
+N:501:Chaos drake
+G:d:v
+I:110:50d10:20:70:70
+W:33:3:18000:1400
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:3d6
+F:ATTR_MULTI | ATTR_ANY | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_3D2 | RES_DISE |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:EVIL | DRAGON |
+F:IM_FIRE | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_DISE | BR_CHAO
+D:A dragon twisted by the forces of chaos. It seems first ugly, then fair,
+D:as its form shimmers and changes in front of your eyes.
+
+N:502:Law drake
+G:d:B
+I:110:50d10:20:70:70
+W:33:3:18000:1400
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:3d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_3D2 | CAN_FLY |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:DRAGON | EVIL |
+F:IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT | ATTR_MULTI
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_SOUN | BR_SHAR
+D:This dragon is clever and cunning. It laughs at your puny efforts to
+D:disturb it.
+
+N:503:Balance drake
+G:d:v
+I:110:60d10:20:70:70
+W:33:3:18000:1600
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:3d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_3D2 | RES_DISE |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE
+F:EVIL | DRAGON | CAN_FLY |
+F:IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT
+F:ATTR_MULTI
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_SOUN | BR_SHAR | BR_DISE | BR_CHAO
+D:A mighty dragon, the balance drake seeks to maintain the Cosmic Balance,
+D:and despises your feeble efforts to destroy evil.
+
+N:504:Ethereal drake
+G:d:o
+I:110:40d10:20:70:70
+W:33:3:0:1200
+E:0:1:0:6:1:0
+O:40:50:10:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:3d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_3D2 | CAN_FLY |
+F:INVISIBLE | PASS_WALL |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_LITE | BR_DARK | BR_CONF
+D:A dragon of great power with control over light and dark, the
+D:ethereal drake's eyes glare with white hatred from the shadows.
+
+N:505:Groo, the Wanderer
+G:p:U
+I:120:13d113:20:70:50
+W:35:7:5000:2000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:9d1
+B:HIT:HURT:6d5
+B:HIT:HURT:25d1
+B:HIT:HURT:6d5
+F:UNIQUE | MALE | WEIRD_MIND | CAN_SPEAK |
+F:FORCE_MAXHP | WILD_TOO | DROP_CORPSE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:DROP_CHOSEN |
+F:OPEN_DOOR | BASH_DOOR |
+F:IM_COLD | IM_POIS | ZANGBAND
+D:He who laughs at Groo's brains will find there is nothing to laugh
+D:about... erm, nobody laughs at Groo and lives.
+
+N:506:Fasolt the Giant
+G:P:u
+I:110:11d111:20:70:50
+W:33:4:10000:2000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:7d10
+B:HIT:HURT:7d10
+B:HIT:EAT_GOLD:2d10
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | WILD_TOO | DROP_CORPSE |
+F:ESCORT |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | IM_COLD | IM_POIS | BASEANGBAND | HAS_LITE
+D:Big, brawny, powerful and with a greed for gold.
+
+N:507:Shade
+G:G:D
+I:120:14d20:20:30:10
+W:33:3:0:350
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:WAIL:TERRIFY
+B:TOUCH:EXP_40
+B:CLAW:LOSE_INT:1d10
+F:FORCE_SLEEP | RAND_25 |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 |
+F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | CAN_FLY |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLIND | HOLD | DRAIN_MANA | FORGET
+D:A shadowy form clutches at you from the darkness. A powerful undead with
+D:a deadly touch.
+
+N:508:Spectre
+G:G:U
+I:120:14d20:20:30:10
+W:33:3:0:350
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:WAIL:TERRIFY
+B:TOUCH:EXP_40
+B:CLAW:LOSE_WIS:5d5
+F:FORCE_SLEEP | RAND_25 |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 | CAN_FLY |
+F:COLD_BLOOD | TAKE_ITEM | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLIND | HOLD | DRAIN_MANA | FORGET
+D:A phantasmal shrieking spirit. Its wail drives the intense cold of pure
+D:evil deep within your body.
+
+N:509:Water troll
+G:T:B
+I:110:36d10:20:50:50
+W:35:1:4000:420
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d9
+B:HIT:HURT:1d9
+B:HIT:HURT:2d2
+B:HIT:HURT:2d2
+F:MALE | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:FRIENDS | DROP_60 | REGENERATE |
+F:OPEN_DOOR | BASH_DOOR | CAN_SWIM |
+F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | BASEANGBAND
+D:He is a troll that reeks of brackish water and mud.
+
+N:510:Fire elemental
+G:E:r
+I:110:30d8:12:50:50
+W:33:2:0:350
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:FIRE:4d6
+B:HIT:FIRE:4d6
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | CAN_FLY | WILD_TOO | WILD_VOLCANO |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL | AURA_FIRE |
+F:IM_FIRE | IM_POIS | IM_ELEC | IM_ACID |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_6 |
+S:BO_FIRE
+D:It is a towering inferno of flames.
+
+N:511:Cherub
+G:A:G
+I:120:100d10:30:68:255
+W:39:4:0:2700
+E:1:1:1:2:1:1
+O:0:90:10:0
+B:HIT:HURT:4d3
+B:HIT:HURT:3d8
+B:HIT:HURT:4d3
+B:HIT:HURT:3d8
+F:FORCE_SLEEP | FORCE_MAXHP | NO_FEAR | GOOD | CAN_FLY |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | REFLECTING | RES_TELE
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | HASTE | BLIND | SCARE | MIND_BLAST | BO_FIRE |
+S:S_MONSTERS
+D:It is an angel moving very quickly, wielding a holy war hammer and casting
+D:a volley of powerful spells in your direction.
+
+N:512:Water elemental
+G:E:b
+I:110:25d8:12:40:50
+W:33:2:0:325
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | COLD_BLOOD | CAN_FLY |
+F:KILL_BODY | KILL_ITEM | BASH_DOOR | POWERFUL |
+F:IM_POIS | IM_ACID | IM_FIRE | IM_COLD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BO_COLD
+D:It is a towering tempest of water.
+
+N:513:Multi-hued hound
+G:Z:v
+I:110:30d10:25:40:0
+W:33:3:600:600
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:4d4
+B:BITE:HURT:4d4
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:BASH_DOOR | ATTR_MULTI | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:IM_ELEC | IM_POIS | IM_ACID | IM_FIRE | IM_COLD |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BR_ACID | BR_POIS | BR_COLD | BR_FIRE | BR_ELEC
+D:Shimmering in rainbow hues, this hound is beautiful and deadly.
+
+N:514:Invisible stalker
+G:E:y
+I:130:19d12:20:46:20
+W:35:3:0:300
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:RAND_50 |
+F:RES_TELE |
+F:EMPTY_MIND | INVISIBLE | COLD_BLOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING
+F:BASEANGBAND | NO_CUT
+D:It is impossible to define its form but its violence is legendary.
+
+N:515:Carrion crawler
+G:c:o
+I:110:20d12:15:40:10
+W:34:2:2000:100
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:STING:PARALYZE:2d6
+B:STING:PARALYZE:2d6
+F:RAND_25 | FRIENDS | DROP_SKELETON |
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | IM_POIS | MORTAL | BASEANGBAND
+D:A hideous centipede covered in slime and with glowing tentacles around its
+D:head.
+
+N:516:Master thief
+G:p:b
+I:130:18d10:20:30:40
+W:34:2:1300:350
+E:1:1:1:2:1:1
+O:90:10:0:0
+B:HIT:HURT:2d8
+B:HIT:HURT:3d4
+B:HIT:EAT_GOLD:4d4
+B:HIT:EAT_ITEM:4d5
+F:MALE |
+F:DROP_90 | DROP_2D2 | DROP_SKELETON | DROP_CORPSE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_6
+S:TRAPS | ARROW_2 | BLINK
+D:Cool and confident, fast and lithe; protect your possessions quickly!
+
+N:517:The Watcher in the Water
+G:~:v
+I:120:27d100:30:100:80
+W:45:3:7000:12000
+E:3:0:3:6:1:0
+O:50:50:0:0
+B:CRUSH:ACID:8d8
+B:CRUSH:POISON:8d8
+B:CRUSH:PARALYZE:8d8
+F:UNIQUE | FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | AQUATIC | ANIMAL |
+F:IM_ACID | IM_COLD | IM_POIS | RES_WATE | RES_TELE | DROP_CORPSE |
+F:NO_CONF | NO_FEAR | EVIL | COLD_BLOOD | BASEANGBAND |
+F:ONLY_ITEM | DROP_GOOD | DROP_4D2 | DROP_1D2 | SPECIAL_GENE | DROP_RANDART
+S:1_IN_5 | BA_WATE | BO_WATE | HOLD | BR_POIS | BO_ICEE | TELE_TO
+D:A vile creature which seems to consist mostly of tentacles, it seeks to
+D:drag people to their doom in the water. Few have ever escaped its grasp.
+
+N:518:Lich
+G:L:o
+I:110:30d10:20:60:60
+W:34:2:0:1000
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:TOUCH:EXP_40
+B:TOUCH:UN_POWER
+B:TOUCH:LOSE_DEX:2d8
+B:TOUCH:LOSE_DEX:2d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_1D2 |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:BLINK | TELE_TO | TELE_AWAY | BLIND | HOLD | SLOW | SCARE |
+S:CAUSE_3 | DRAIN_MANA | BRAIN_SMASH
+D:It is a skeletal form dressed in robes. It radiates vastly evil power.
+
+N:519:Gas spore
+G:e:g
+I:110:25d10:30:1:5
+W:34:4:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:DISEASE:30d2
+F:FORCE_SLEEP |
+F:STUPID | EMPTY_MIND | CAN_FLY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:From a distance, this creature is often mistaken for the
+D:much more dangerous beholder.
+
+N:520:Master vampire
+G:V:g
+I:110:34d20:20:60:10
+W:36:1:1600:750
+E:1:1:1:2:1:1
+O:20:40:30:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+B:BITE:EXP_40:3d6
+B:BITE:EXP_40:3d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_4D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP |
+F:BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:TELE_TO | HOLD | CONF | SCARE | CAUSE_3 | MIND_BLAST | FORGET |
+S:DARKNESS | BO_NETH
+D:It is a humanoid form dressed in robes. Power emanates from its chilling
+D:frame.
+
+N:521:Oriental vampire
+G:V:s
+I:110:30d30:20:60:10
+W:40:3:0:750
+E:1:1:1:2:1:1
+O:10:45:35:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:BITE:EXP_40:3d6
+B:BITE:EXP_40:3d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_4D2 | CAN_FLY |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | INVISIBLE | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE |
+F:BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:TELE_TO | HOLD | CONF | SCARE | CAUSE_3 | MIND_BLAST | FORGET |
+S:DARKNESS | BO_NETH
+D:The oriental vampire can transform into a mist at will.
+
+N:522:Greater mummy
+G:z:y
+I:110:34d10:30:68:255
+W:36:3:0:800
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:CLAW:LOSE_CON:3d6
+B:CLAW:DISEASE:3d6
+B:GAZE:EXP_40:3d4
+B:GAZE:TERRIFY:3d4
+F:FORCE_SLEEP | FORCE_MAXHP | NO_FEAR | EVIL | UNDEAD |COLD_BLOOD |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | RES_TELE |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:HEAL | HASTE | BLIND | SCARE | S_UNDEAD | ANIM_DEAD |
+S:BA_POIS | BA_NETH | BA_COLD | DRAIN_MANA |
+S:MIND_BLAST | CAUSE_3 | DARKNESS | FORGET
+D:Once a powerful ruler, now an even more powerful undead menace.
+
+N:523:Bloodletter of Khorne
+G:U:r
+I:120:30d8:20:40:30
+W:34:1:0:450
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:EXP_20:13d1
+B:HIT:EXP_20:13d1
+B:HIT:EXP_20:13d1
+F:FORCE_MAXHP | OPEN_DOOR | FRIENDS | DROP_60 | REGENERATE |
+F:ONLY_ITEM | DROP_CHOSEN | NO_FEAR | NONLIVING |
+F:EVIL | IM_POIS | IM_COLD | IM_FIRE | DEMON | ZANGBAND | HAS_LITE
+D:Slender, red-skinned demons twisting in nightmarish shapes.
+D:They are armed with hellblades, which will suck the life from
+D:your body.
+
+N:524:Giant grey scorpion
+G:S:s
+I:120:18d20:12:50:40
+W:34:4:1200:275
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+B:STING:POISON:1d4
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_SKELETON |
+F:ANIMAL
+F:MORTAL | BASEANGBAND
+D:It is a giant grey scorpion. It looks poisonous.
+
+N:525:Earth elemental
+G:E:u
+I:100:30d10:10:60:90
+W:34:2:0:375
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:FORCE_SLEEP |
+F:EMPTY_MIND | COLD_BLOOD |
+F:KILL_ITEM | KILL_BODY | PASS_WALL | POWERFUL |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | HURT_ROCK |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:BO_ACID
+D:It is a towering form composed of rock with fists of awesome power.
+
+N:526:Air elemental
+G:E:B
+I:120:30d5:12:50:50
+W:34:2:0:390
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d10
+B:HIT:CONFUSE:1d4
+B:HIT:HURT:1d10
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | COLD_BLOOD | CAN_FLY |
+F:KILL_BODY | KILL_ITEM | BASH_DOOR | POWERFUL |
+F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:BO_ELEC
+D:It is a towering tornado of winds.
+
+N:527:Shimmering mold
+G:m:b
+I:110:32d8:2:24:70
+W:27:1:0:140
+B:SPORE:ELEC:5d4
+B:SPORE:ELEC:5d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_ELEC | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange growth on the dungeon floor, glowing and crackling with sparks.
+
+N:528:Gargoyle
+G:u:s
+I:110:18d12:10:50:15
+W:34:2:3000:110
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:1d6
+F:DROP_60 | ONLY_GOLD | EVIL | DEMON | FRIENDS | HURT_LITE |
+F:WILD_TOO | WILD_MOUNTAIN | WILD_WASTE |
+F:IM_POIS | IM_FIRE | IM_COLD | IM_ELEC | HURT_ROCK | NONLIVING |
+F:BASEANGBAND | NO_CUT
+S:1_IN_12
+S:BR_ELEC | BR_FIRE
+D:A weird demon creature with a stone-like skin.
+
+N:529:Malicious leprechaun
+G:h:v
+I:120:4d5:8:13:8
+W:35:4:1200:85
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:TOUCH:EAT_GOLD
+B:TOUCH:EAT_ITEM
+F:INVISIBLE | RAND_25 | TAKE_ITEM | COLD_BLOOD |
+F:HURT_LITE | EVIL | OPEN_DOOR | MALE |
+F:MORTAL | BASEANGBAND
+S:MULTIPLY |
+S:1_IN_6 |
+S:BLINK | TPORT | TELE_TO | CAUSE_1
+D:This little creature has a fiendish gleam in its eyes.
+
+N:530:Eog golem
+G:g:u
+I:100:100d20:12:125:10
+W:34:4:12000:1200
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+F:ONLY_GOLD | DROP_2D2 |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | REFLECTING |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+D:It is a massive deep brown statue, striding towards you with an
+D:all-too-familiar purpose. Your magic surprisingly feels much less
+D:powerful now.
+
+N:531:Little Boy
+G:{:D
+I:120:12d12:10:80:12
+W:35:2:0:200
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:SHATTER:100d2
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | REFLECTING |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS
+F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | RES_TELE | JOKEANGBAND | NO_CUT
+D:A shining machine of death and destruction.
+
+N:532:Dagashi
+G:p:u
+I:120:13d25:20:70:10
+W:35:4:1600:500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:POISON:3d4
+B:HIT:LOSE_STR:3d4
+B:HIT:LOSE_STR:3d4
+B:HIT:POISON:3d4
+F:MALE |
+F:DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A human warrior, moving with lightning speed.
+
+N:533:Headless ghost
+G:G:u
+I:120:20d25:20:30:10
+W:35:3:0:550
+E:0:0:0:0:0:0
+O:35:25:25:0
+B:TOUCH:TERRIFY
+B:TOUCH:EXP_40
+B:CLAW:LOSE_INT:5d5
+B:CLAW:LOSE_WIS:5d5
+F:FORCE_SLEEP | RAND_25 |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 | CAN_FLY |
+F:COLD_BLOOD | TAKE_ITEM | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLIND | DRAIN_MANA | SCARE | BO_COLD | FORGET
+D:A phantasmal apparition with no head.
+
+N:534:Dread
+G:G:o
+I:120:25d20:20:30:10
+W:35:2:0:600
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:LOSE_STR:3d4
+F:FORCE_SLEEP | RAND_25 |
+F:ONLY_ITEM | DROP_60 | DROP_2D2 |
+F:TAKE_ITEM | INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | CAN_FLY |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLIND | HOLD | CONF | DRAIN_MANA | BO_NETH
+D:It is a form that screams its presence against the eye. Death incarnate,
+D:its hideous black body seems to struggle against reality as the universe
+D:itself struggles to banish it.
+
+N:535:Leng spider
+G:S:v
+I:120:16d20:12:50:40
+W:35:4:600:250
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d6
+B:STING:POISON:1d6
+F:WEIRD_MIND | BASH_DOOR | FRIENDS | ELDRITCH_HORROR | DROP_CORPSE |
+F:ANIMAL | SPIDER | CTHANGBAND
+D:Bloated purple spiders with long, bristly legs.
+
+N:536:Gauth
+G:e:s
+I:110:15d20:20:50:20
+W:36:2:1600:600
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:UN_BONUS:5d2
+B:GAZE:UN_BONUS:5d2
+B:GAZE:UN_POWER:5d2
+B:GAZE:UN_POWER:5d2
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE |
+F:BASH_DOOR | EVIL | BASEANGBAND | MORTAL
+S:1_IN_5 |
+S:CAUSE_2 | TELE_AWAY | BA_COLD | BO_ELEC | HOLD | DRAIN_MANA
+D:Another lesser relative of the beholder, this six-eyed creature feeds on magic.
+
+N:537:Smoke elemental
+G:E:R
+I:120:15d10:10:80:90
+W:36:3:0:375
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:HURT:2d6
+B:ENGULF:HURT:2d6
+F:FORCE_SLEEP |
+F:EMPTY_MIND |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | CAN_FLY | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:DARKNESS | BO_FIRE
+D:It is a towering blackened form, crackling with heat.
+
+N:538:Olog
+G:T:y
+I:110:42d10:20:50:50
+W:36:1:6000:450
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+B:BITE:HURT:2d3
+B:BITE:HURT:2d3
+F:FORCE_MAXHP | OPEN_DOOR | FRIENDS | DROP_60 | REGENERATE |
+F:SMART | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | TROLL | IM_POIS | BASEANGBAND
+D:It is a massive troll, more intelligent than most of its kind, with needle-
+D:sharp fangs.
+
+N:539:Halfling slinger
+G:h:U
+I:110:30d9:20:40:30
+W:35:1:900:330
+E:1:1:1:2:1:1
+O:100:0:0:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+F:FORCE_MAXHP | OPEN_DOOR | FRIENDS | DROP_90 |
+F:SMART | EVIL | IM_POIS | IM_COLD | MALE | DROP_SKELETON | DROP_CORPSE
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3
+S:ARROW_4
+D:A rebel halfling who has rejected the halfling tradition of archery.
+
+N:540:Gravity hound
+G:Z:W
+I:110:35d10:30:30:0
+W:35:2:700:500
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:2d8
+B:BITE:HURT:2d8
+F:FORCE_SLEEP | FRIENDS | DROP_SKELETON | DROP_CORPSE |
+F:BASH_DOOR |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_GRAV
+D:Unfettered by the usual constraints of gravity, these unnatural creatures
+D:are walking on the walls and even the ceiling! The earth suddenly feels
+D:rather less solid as you see gravity warp all around these monsters.
+
+N:541:Acidic cytoplasm
+G:j:s
+I:120:40d10:12:18:1
+W:35:5:3000:180
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+F:FORCE_MAXHP | TAKE_ITEM | COLD_BLOOD |
+F:DROP_1D2 | DROP_4D2 | CAN_SWIM |
+F:STUPID | EMPTY_MIND | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_FEAR | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:A disgusting animated blob of destruction. Flee its gruesome hunger!
+
+N:542:Inertia hound
+G:Z:W
+I:110:35d10:30:30:0
+W:35:2:700:500
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:2d8
+B:BITE:HURT:2d8
+F:FORCE_SLEEP | FRIENDS | DROP_SKELETON | DROP_CORPSE |
+F:BASH_DOOR |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_INER
+D:Bizarrely, this hound seems to be hardly moving at all, yet it approaches
+D:you with deadly menace. It makes you tired just to look at it.
+
+N:543:Impact hound
+G:Z:u
+I:110:35d10:30:30:0
+W:35:2:700:500
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:2d8
+B:BITE:HURT:2d8
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | NO_CONF | NO_SLEEP
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_WALL
+D:A deep brown shape is visible before you, its canine form striking you with
+D:an almost physical force. The dungeon floor buckles as if struck by a
+D:powerful blow as it stalks towards you.
+
+N:544:Shardstorm
+G:v:u
+I:120:32d10:40:12:0
+W:37:1:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:HURT:6d6
+F:FORCE_SLEEP | RAND_50 | NONLIVING | CAN_FLY |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL |
+F:NO_FEAR | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:BR_SHAR
+D:A howling blast of razor-sharp mountain fragments, kept intact by perilous
+D:magics.
+
+N:545:Ooze elemental
+G:E:g
+I:110:13d10:10:80:90
+W:36:3:0:300
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+F:FORCE_SLEEP |
+F:EMPTY_MIND | COLD_BLOOD | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_ACID | IM_FIRE | CAN_SWIM | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BO_ACID | BA_ACID
+D:It is a towering mass of filth, an eyesore of ooze.
+
+N:546:Young black dragon
+G:d:s
+I:110:30d10:20:60:70
+W:31:1:20000:700
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:2d8
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_SWAMP |
+F:DROP_3D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | DRAGON | IM_ACID | BASEANGBAND | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_ACID
+D:It has a form out of legend. Its still-tender scales are a
+D:darkest black hue. Acid drips from its body.
+
+N:547:Mumak
+G:q:s
+I:110:90d10:20:55:100
+W:35:3:150000:2100
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BUTT:HURT:8d6
+B:BUTT:HURT:8d6
+B:CRUSH:HURT:8d4
+F:BASH_DOOR | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:A massive elephantine form with eyes twisted by madness.
+
+N:548:Giant fire ant
+G:a:R
+I:110:20d10:14:49:40
+W:35:1:700:350
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:3d12
+B:BITE:FIRE:3d12
+F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR | SUSCEP_COLD |
+F:ANIMAL | IM_FIRE
+F:MORTAL | BASEANGBAND
+D:A giant ant covered in shaggy fur. Its powerful jaws glow with heat.
+
+N:549:Mature white dragon
+G:d:w
+I:110:40d10:20:70:70
+W:34:1:110000:1200
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:4d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_4D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_WASTE | WILD_MOUNTAIN |
+F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP | SUSCEP_FIRE | BASEANGBAND |
+F:HAS_LITE | ATTR_MULTI
+S:1_IN_9 |
+S:SCARE |
+S:BR_COLD
+D:A large dragon, scales gleaming bright white.
+
+N:550:Xorn
+G:X:u
+I:110:16d10:20:80:10
+W:36:2:30000:650
+E:0:0:0:3:0:0
+O:0:0:0:0
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:FORCE_MAXHP |
+F:EMPTY_MIND | COLD_BLOOD |
+F:KILL_ITEM | PASS_WALL |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:A huge creature of the element Earth. Able to merge with its element, it
+D:has four huge arms protruding from its enormous torso.
+
+N:551:Rogrog the Black Troll
+G:T:D
+I:120:15d100:20:70:50
+W:41:5:9000:5000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:6d7
+B:HIT:HURT:6d7
+B:BITE:HURT:4d10
+B:SPIT:ACID:4d8
+F:UNIQUE | MALE |
+F:FORCE_MAXHP |
+F:ESCORT | MOVE_BODY |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_CORPSE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | REGENERATE |
+F:EVIL | TROLL | IM_COLD | IM_POIS | BASEANGBAND
+D:A massive and cruel troll of great power, drool slides caustically down
+D:his muscular frame. Despite his bulk, he strikes with stunning speed.
+
+N:552:Mist giant
+G:#:B
+I:120:35d10:20:50:50
+W:36:2:0:450
+E:0:0:0:0:0:0
+O:10:90:0:0
+B:CRUSH:HURT:4d8
+B:CRUSH:HURT:4d8
+B:CRUSH:HURT:4d8
+B:BITE:EXP_40:3d9
+F:FORCE_MAXHP | OPEN_DOOR | DROP_60 | WILD_TOO | WILD_SWAMP |
+F:SMART | BASH_DOOR |
+F:EVIL | GIANT | IM_POIS | CAN_FLY | CTHANGBAND | NO_CUT
+D:"Two eyes, the colour of a thin, yellow wine, were set high in the
+D:thing's body; though it had no separate head. A mouthing, obscene slit,
+D:filled with fangs lay just beneath the eyes. It had no nose or ears...
+D:Four appendages sprang from its upper parts and its lower body
+D:slithered along the ground, unsupported by any limbs... incredibly
+D:disgusting to behold and its amorphous body gave off a stench of death
+D:and decay..."
+
+N:553:Phantom
+G:G:v
+I:120:20d25:30:30:20
+W:36:3:0:400
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:EXP_80
+B:TOUCH:EXP_40
+B:CLAW:LOSE_INT:1d10
+B:CLAW:LOSE_WIS:1d10
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_1D2 |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_5 |
+S:FORGET | MIND_BLAST
+D:An unholy creature of darkness, the aura emanating from this evil being
+D:saps your very soul.
+
+N:554:Grey wraith
+G:W:s
+I:110:19d10:20:50:10
+W:36:1:0:700
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+B:TOUCH:EXP_40
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_60 | DROP_90 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | HURT_LITE | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:HOLD | SCARE | CAUSE_3 | DARKNESS
+D:A tangible but ghostly form, made of grey fog. The air around it feels
+D:deathly cold.
+
+N:555:Revenant
+G:W:u
+I:110:2d111:20:50:10
+W:36:1:0:725
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:GAZE:PARALYZE
+B:CLAW:LOSE_CON:1d10
+B:CLAW:LOSE_CON:1d10
+B:GAZE:EXP_40
+F:FORCE_SLEEP | FORCE_MAXHP | REGENERATE |
+F:DROP_60 | DROP_90 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | HURT_LITE | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:CONF | SCARE | CAUSE_3 | DARKNESS
+D:Back from the grave, to wreak vengeance upon the living. A skeletal figure
+D:wearing a black robe, with eyes that burn with undying hatred.
+
+N:556:Young multi-hued dragon
+G:d:v
+I:110:32d10:20:60:70
+W:32:1:20000:900
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:3d8
+F:ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:DROP_1D2 | DROP_3D2 | CAN_FLY |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC |
+F:IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:SCARE |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS
+D:It has a form out of legend. Beautiful scales of shimmering
+D:and magical colours cover it.
+
+N:557:Raal's Tome of Destruction
+G:?:r
+I:120:50d15:20:150:15
+W:36:4:0:1500
+E:0:0:0:0:0:0
+O:20:0:80:0
+F:NEVER_MOVE | NEVER_BLOW | NONLIVING |
+F:FORCE_SLEEP | DROP_90 | DROP_GOOD | EVIL | COLD_BLOOD | EMPTY_MIND |
+F:FORCE_MAXHP | NO_CONF | NO_FEAR | NO_SLEEP | CHAR_MULTI |
+F:IM_ACID | IM_POIS | IM_COLD | IM_ELEC | SUSCEP_FIRE | RES_NETH | RES_TELE |
+F:ZANGBAND | HAS_LITE | NO_CUT
+S:1_IN_2 |
+S:BO_ACID | BR_FIRE | BO_MANA | BR_COLD | BR_POIS |
+S:BO_WATE | BA_POIS | BR_NETH
+D:A sentient arcane tome casting spells with malevolent intent.
+
+N:558:Colossus
+G:g:G
+I:110:30d100:15:150:10
+W:36:4:35000:900
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+F:FORCE_MAXHP |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_ELEC |
+F:IM_POIS | NONLIVING | REFLECTING |
+F:NO_CONF | NO_SLEEP | NO_FEAR
+F:MORTAL | BASEANGBAND | NO_CUT
+S:1_IN_8
+S:ARROW_4
+D:An enormous construct resembling a titan made from stone. It strides
+D:purposefully towards you, swinging its slow fists with earth-shattering
+D:power.
+
+N:559:Young gold dragon
+G:d:y
+I:110:30d10:20:60:70
+W:31:1:20000:700
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:2d8
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_3D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | WILD_TOO | WILD_MOUNTAIN |
+F:EVIL | DRAGON | BASEANGBAND | NO_STUN | HAS_LITE | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_SOUN
+D:It has a form out of legend. Its still-tender scales are a
+D:tarnished gold hue, and light is reflected from its form.
+
+N:560:Mature blue dragon
+G:d:b
+I:110:40d10:20:70:70
+W:34:1:110000:1200
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:4d8
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_4D2 | DROP_CORPSE
+F:BASH_DOOR | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD |
+F:EVIL | DRAGON | IM_ELEC | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_9 |
+S:SCARE |
+S:BR_ELEC
+D:A large dragon, scales tinted deep blue.
+
+N:561:Mature green dragon
+G:d:g
+I:110:40d10:20:70:70
+W:34:1:110000:1200
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:4d8
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_4D2 | DROP_CORPSE |
+F:BASH_DOOR | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD |
+F:EVIL | DRAGON | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_9 |
+S:SCARE |
+S:BR_POIS
+D:A large dragon, scales tinted deep green.
+
+N:562:Mature bronze dragon
+G:d:U
+I:110:40d10:20:70:70
+W:34:1:110000:1200
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d8
+B:CLAW:HURT:2d8
+B:BITE:HURT:4d8
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN |
+F:DROP_4D2 | CAN_FLY |
+F:BASH_DOOR | DROP_CORPSE |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_9 |
+S:CONF | SCARE |
+S:BR_CONF
+D:A large dragon with scales of rich bronze.
+
+N:563:Young red dragon
+G:d:r
+I:110:30d10:20:60:70
+W:31:1:20000:700
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:2d8
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | SUSCEP_COLD |
+F:DROP_3D2 | WILD_TOO | WILD_MOUNTAIN | WILD_VOLCANO |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE
+F:EVIL | DRAGON | IM_FIRE | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_FIRE
+D:It has a form out of legend. Its still-tender scales are a
+D:deepest red hue. Heat radiates from its form.
+
+N:564:Nightblade
+G:h:D
+I:120:19d13:20:60:10
+W:36:2:1600:315
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+B:HIT:LOSE_CON:3d4
+F:MALE |
+F:DROP_1D2 | FRIENDS | INVISIBLE | DROP_SKELETON | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR | HURT_LITE |
+F:EVIL | NO_CONF | NO_SLEEP | ZANGBAND
+D:A dark elven assassin, so stealthy that he is almost impossible to see.
+
+N:565:Trapper
+G:.:w
+I:120:60d10:30:75:10
+W:36:3:1300:580
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:PARALYZE:15d1
+B:HIT:PARALYZE:15d1
+F:CHAR_CLEAR | ATTR_CLEAR | CHAR_MULTI |
+F:NEVER_MOVE | FORCE_MAXHP |
+F:INVISIBLE | EMPTY_MIND | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | BASEANGBAND
+D:This creature traps unsuspecting victims
+D:and paralyzes them, to be slowly digested later.
+
+N:566:Bodak
+G:u:r
+I:110:35d10:10:68:90
+W:36:2:0:750
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:FIRE:4d6
+B:HIT:FIRE:4d6
+B:GAZE:EXP_20
+F:FORCE_SLEEP |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | AURA_FIRE | NONLIVING |
+F:EVIL | DEMON | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:HAS_LITE
+S:1_IN_4 |
+S:BO_FIRE | BA_FIRE |
+S:S_DEMON
+D:It is a humanoid form composed of flames and hatred.
+
+N:567:Time bomb
+G:.:w
+I:130:12d12:30:40:0
+W:36:5:0:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:TIME:30d2
+F:CHAR_CLEAR | ATTR_CLEAR | CHAR_MULTI |
+F:NEVER_MOVE | FORCE_MAXHP |
+F:EMPTY_MIND | INVISIBLE | COLD_BLOOD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | JOKEANGBAND | NO_CUT
+D:It was left here to be used against intruders.
+
+N:568:Mezzodaemon
+G:u:o
+I:110:40d10:10:68:90
+W:36:2:0:750
+E:1:1:1:0:1:1
+O:0:0:0:0
+B:CLAW:HURT:5d6
+B:CLAW:HURT:5d6
+F:FORCE_SLEEP | PASS_WALL | INVISIBLE |
+F:IM_POIS | IM_COLD | IM_ACID | IM_FIRE |
+F:NO_SLEEP | NO_CONF | NO_STUN | NONLIVING |
+F:EVIL | DEMON | BASEANGBAND
+S:1_IN_4 |
+S:BLINK | DARKNESS | S_DEMON
+D:An ugly demon with insect-like extremities and large bulbous eyes.
+
+N:569:Elder thing
+G:u:G
+I:110:35d10:10:70:50
+W:36:3:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:4d6
+B:CRUSH:HURT:4d6
+B:CRUSH:HURT:4d6
+B:TOUCH:LOSE_WIS
+F:FORCE_SLEEP | OPEN_DOOR | BASH_DOOR | ELDRITCH_HORROR | NONLIVING |
+F:EVIL | DEMON | IM_POIS | IM_ACID | NO_CONF | NO_SLEEP | RES_TELE |
+F:CAN_SWIM | CTHANGBAND
+S:1_IN_4 |
+S:SCARE | TELE_AWAY | BA_NUKE | CAUSE_4 | BA_POIS |
+S:CONF | S_DEMON | S_UNDEAD
+D:"...some ridged barrel-shaped objects with thin
+D:horizontal arms radiating spoke-like from a central ring and with
+D:vertical knobs or bulbs projecting from the head and base of the
+D:barrel. Each of these knobs was the hub of a system of five long,
+D:flat, triangularly tapering arms arranged around it like the arms
+D:of a starfish."
+
+N:570:Ice elemental
+G:E:w
+I:110:35d10:10:60:90
+W:37:3:0:650
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:COLD:4d3
+B:HIT:HURT:4d6
+B:TOUCH:COLD:4d3
+F:FORCE_SLEEP |
+F:EMPTY_MIND | COLD_BLOOD | AURA_COLD |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_COLD | IM_ELEC | CAN_SWIM | IM_POIS | IM_ACID |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BO_ICEE | BA_COLD
+D:It is a towering glacier of ice.
+
+N:571:Necromancer
+G:p:R
+I:110:28d10:20:50:10
+W:36:2:1600:666
+E:1:1:1:2:1:1
+O:10:0:90:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_SKELETON
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HASTE | TPORT | TELE_TO | BLIND | HOLD | SCARE | CAUSE_3 |
+S:BO_NETH | MIND_BLAST | FORGET |
+S:S_UNDEAD | ANIM_DEAD
+D:A gaunt figure, clothed in black robes.
+
+N:572:The Greater hell magic mushroom were-quylthulg
+G:Q:s
+I:120:19d99:50:80:50
+W:36:3:0:2500
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:GAZE:EXP_40:4d8
+B:GAZE:EXP_40:4d8
+B:CRUSH:ACID:8d8
+B:CRUSH:ACID:8d8
+F:FORCE_MAXHP | FORCE_SLEEP | UNIQUE | NO_STUN | NO_CONF |
+F:NO_SLEEP| EVIL | IM_ACID | IM_ELEC | IM_FIRE | IM_POIS |
+F:IM_COLD | RES_NETH | RES_WATE | RES_PLAS | RES_DISE | SMART |
+F:RES_NEXU | NONLIVING | RES_TELE | KILL_WALL | ELDRITCH_HORROR |
+F:BASH_DOOR | DEMON | COLD_BLOOD | ANIMAL | CAN_SWIM |
+F:DROP_GOOD | DROP_GREAT | ONLY_ITEM | DROP_2D2 | JOKEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLINK | SLOW | SCARE | DARKNESS | HEAL | ANIM_DEAD
+S:TPORT | TELE_AWAY | HASTE | S_MONSTER | DRAIN_MANA |
+S:S_UNDEAD | S_DEMON | S_DRAGON | S_KIN
+D:This unholy abomination will crush you too. Flee while you can!
+
+N:573:Lorgan, Chief of the Easterlings
+G:p:v
+I:120:18d100:25:100:10
+W:36:2:0:1200
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:TELE_TO | S_MONSTERS
+D:A mighty warrior from the east, Lorgan hates everything that he cannot
+D:control.
+
+N:574:Chaos spawn
+G:e:s
+I:110:21d21:20:50:20
+W:38:2:1900:700
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:HURT:10d10
+B:GAZE:UN_BONUS:5d2
+B:GAZE:EXP_40:5d2
+B:GAZE:PARALYZE:5d2
+F:FORCE_MAXHP | BASH_DOOR | EVIL | CAN_FLY | ZANGBAND
+D:It has two eyestalks and a large central eye. Its gaze can kill.
+
+N:575:Mummified troll
+G:z:w
+I:110:25d10:20:50:50
+W:34:1:5000:420
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:FORCE_MAXHP |
+F:DROP_60 |
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE |
+F:EVIL | TROLL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a massive figure clothed in wrappings. You are wary of its massive
+D:fists.
+
+N:576:Storm of Unmagic
+G:v:v
+I:130:32d20:50:40:0
+W:53:3:0:4000
+B:ENGULF:EXP_80:5d5
+B:ENGULF:UN_POWER:5d5
+B:ENGULF:UN_BONUS:5d5
+B:HIT:LOSE_ALL:5d5
+F:ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:RAND_50 | RAND_25 | NONLIVING | CAN_FLY |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_TIME | BR_DISE
+D:Howling through the disintegrating dungeon, this awesome whirlpool of Unmagic
+D:rips the enchantments from everything it touches.
+
+N:577:Crypt thing
+G:L:G
+I:120:80d10:20:60:60
+W:37:2:0:2500
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:TOUCH:EXP_40
+B:TOUCH:UN_POWER
+B:TOUCH:LOSE_DEX:2d10
+B:TOUCH:LOSE_DEX:2d10
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_1D2 | RES_TELE |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLINK | TELE_TO | TELE_AWAY | TELE_LEVEL |
+S:CAUSE_3 | DRAIN_MANA | BRAIN_SMASH
+D:It is a skeletal form dressed in robes. It looks evil and devious...
+
+N:578:Chaos butterfly
+G:I:G
+I:120:60d10:40:60:10
+W:37:2:0:1000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:3d7
+B:CLAW:HURT:3d7
+B:CRUSH:HURT:10d5
+F:FORCE_SLEEP |
+F:CAN_FLY |
+F:WEIRD_MIND | BASH_DOOR | ATTR_MULTI | ATTR_ANY |
+F:NO_CONF | NO_SLEEP | MORTAL | ZANGBAND
+S:1_IN_9
+S:BR_CONF | BR_CHAO
+D:With fractal patterns on its wings, it is clearly one of those butterflies
+D:that mathematicians keep talking about - the ones that flap their wings on the
+D:other side of the world to cause storms here. Now's your chance to stop it...
+
+N:579:Time elemental
+G:E:G
+I:120:35d10:90:70:10
+W:39:2:0:1000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:TIME:3d4
+B:TOUCH:TIME:3d4
+F:PASS_WALL | IM_POIS | IM_FIRE | IM_ELEC | IM_FIRE | IM_ACID | CAN_FLY |
+F:NO_CONF | NO_SLEEP | EMPTY_MIND | KILL_ITEM | RAND_50 |
+F:ZANGBAND | NO_CUT
+S:1_IN_7
+S:SLOW | BR_TIME |
+D:You have not seen it yet.
+
+N:580:Flying polyp
+G:~:R
+I:120:35d10:90:70:10
+W:37:2:0:1000
+E:3:0:3:6:1:0
+O:0:0:0:0
+B:CRUSH:PARALYZE:8d4
+B:CRUSH:PARALYZE:8d4
+B:CRUSH:PARALYZE:8d4
+F:PASS_WALL | INVISIBLE | FORCE_MAXHP | RES_DISE |
+F:IM_POIS | IM_COLD | IM_ACID | ELDRITCH_HORROR |
+F:NO_CONF | NO_SLEEP | EVIL | CAN_FLY | CTHANGBAND |NO_CUT
+S:1_IN_7
+S:BR_WALL |
+D:"They were only partly material and had the power of aerial motion,
+D:despite the absence of wings... Suggestions of monstrous plasticity
+D:and of temporary lapses of visibility..."
+
+N:581:The Queen Ant
+G:a:v
+I:120:15d100:30:100:10
+W:37:2:2000:1000
+E:0:1:0:2:1:0
+O:50:50:0:0
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d8
+B:BITE:HURT:2d8
+F:UNIQUE | FEMALE | GOOD | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:WEIRD_MIND | OPEN_DOOR | BASH_DOOR |
+F:ANIMAL |
+F:MORTAL | BASEANGBAND
+S:1_IN_2 |
+S:S_KIN
+D:She's upset because you hurt her children.
+
+N:582:Will o' the wisp
+G:E:W
+I:130:20d10:30:150:0
+W:38:4:0:500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:1d9
+B:HIT:HURT:1d9
+B:HIT:HALLU:1d9
+B:HIT:HALLU:1d9
+F:FORCE_SLEEP | FORCE_MAXHP | RAND_50 |
+F:SMART | EMPTY_MIND | INVISIBLE |
+F:PASS_WALL | POWERFUL | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_2 |
+S:BLINK | TPORT | CONF | CAUSE_2
+D:A strange ball of glowing light. It disappears and reappears and seems to
+D:draw you to it. You seem somehow compelled to stand still and watch its
+D:strange dancing motion.
+
+N:583:Shan
+G:I:B
+I:120:20d8:20:120:20
+W:37:4:0:250
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:IM_POIS | IM_COLD | COLD_BLOOD | ANIMAL | EVIL |
+F:NO_SLEEP | NO_CONF | CAN_FLY | NEVER_BLOW | CTHANGBAND |
+S:1_IN_2
+S:CONF | HOLD | DRAIN_MANA | FORGET | MIND_BLAST | SHRIEK
+D:"Those huge lidless eyes which stared with hate at me, the jointed
+D:tendrils which seemed to twist from the head in cosmic rhythms,
+D:the ten legs, covered with black shining tentacles and folded into
+D:the pallid underbelly, and the semi-circular ridged wings covered
+D:with triangular scales -- all this cannot convey the soul-ripping
+D:horror of the shape which darted at me. I saw the three mouths
+D:of the thing move moistly, and then it was upon me."
+
+N:584:Magma elemental
+G:E:o
+I:110:35d10:10:70:90
+W:37:3:0:950
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:FIRE:3d7
+B:HIT:HURT:4d6
+B:HIT:FIRE:3d7
+F:FORCE_SLEEP |
+F:EMPTY_MIND | AURA_FIRE | WILD_TOO | WILD_VOLCANO |
+F:KILL_ITEM | KILL_BODY | PASS_WALL | POWERFUL |
+F:IM_FIRE | IM_ELEC | IM_ACID | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_7 |
+S:BO_PLAS | BA_FIRE
+D:It is a towering glowing form of molten rock.
+
+N:585:Black pudding
+G:j:D
+I:110:40d10:12:18:1
+W:37:5:300:50
+E:0:0:0:0:0:0
+O:90:0:0:0
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+B:TOUCH:ACID:1d10
+F:FORCE_MAXHP |
+F:FRIENDS |
+F:DROP_60 | DROP_90 | DROP_1D2 |
+F:STUPID | EMPTY_MIND | COLD_BLOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | CAN_SWIM | BASEANGBAND | NO_CUT
+D:A lump of rotting black flesh that slurrrrrrrps across the dungeon floor.
+
+N:586:Killer iridescent beetle
+G:K:v
+I:110:25d15:16:60:30
+W:37:2:600:850
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:ELEC:1d12
+B:CLAW:ELEC:1d12
+B:GAZE:PARALYZE
+F:ATTR_MULTI | FORCE_MAXHP |
+F:WEIRD_MIND | BASH_DOOR | AURA_ELEC | DROP_CORPSE |
+F:ANIMAL | IM_ELEC | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle, whose carapace shimmers with vibrant energies.
+
+N:587:Nexus vortex
+G:v:v
+I:120:32d10:100:40:0
+W:37:1:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:HURT:5d5
+F:FORCE_SLEEP |
+F:RAND_50 | RAND_25 | RES_NEXU | CAN_FLY |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_6 |
+S:BR_NEXU
+D:A maelstrom of potent magical energy.
+
+N:588:Plasma vortex
+G:v:R
+I:120:32d10:100:40:0
+W:37:1:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:FIRE:4d8
+B:ENGULF:ELEC:4d8
+F:FORCE_SLEEP | SUSCEP_COLD |
+F:RAND_50 | RAND_25 | RES_PLAS | AURA_FIRE | AURA_ELEC |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL |
+F:IM_FIRE | CAN_FLY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_6 |
+S:BR_PLAS
+D:A whirlpool of intense flame, charring the stones at your feet.
+
+N:589:Mature red dragon
+G:d:r
+I:110:50d10:20:80:70
+W:36:1:110000:1500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d10
+B:CLAW:HURT:2d10
+B:BITE:HURT:4d10
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN | WILD_VOLCANO |
+F:DROP_1D2 | DROP_4D2 | CAN_FLY | DROP_CORPSE | SUSCEP_COLD |
+F:BASH_DOOR |
+F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_9 |
+S:CONF | SCARE |
+S:BR_FIRE
+D:A large dragon, scales tinted deep red.
+
+N:590:Mature gold dragon
+G:d:y
+I:110:50d10:20:80:70
+W:36:1:110000:1500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d10
+B:CLAW:HURT:2d10
+B:BITE:HURT:4d10
+F:FORCE_SLEEP | FORCE_MAXHP | WILD_TOO | WILD_MOUNTAIN |
+F:DROP_1D2 | DROP_4D2 | CAN_FLY | DROP_CORPSE |
+F:BASH_DOOR |
+F:EVIL | DRAGON | NO_STUN | NO_SLEEP | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_9 |
+S:CONF | SCARE |
+S:BR_SOUN
+D:A large dragon with scales of gleaming gold.
+
+N:591:Crystal drake
+G:d:u
+I:110:45d10:20:70:70
+W:33:3:18000:1350
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:3d6
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:ONLY_ITEM | DROP_3D2 | REFLECTING |
+F:OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT
+F:ATTR_MULTI
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_SHAR
+D:A dragon of strange crystalline form. Light shines through it, dazzling
+D:your eyes with spectrums of colour.
+
+N:592:Mature black dragon
+G:d:s
+I:110:50d10:20:80:70
+W:36:1:110000:1500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d10
+B:CLAW:HURT:2d10
+B:BITE:HURT:4d10
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE |
+F:DROP_1D2 | DROP_4D2 | WILD_TOO | WILD_SWAMP | WILD_MOUNTAIN |
+F:BASH_DOOR |
+F:EVIL | DRAGON | IM_ACID | NO_CONF | NO_SLEEP | BASEANGBAND | ATTR_MULTI
+S:1_IN_9 |
+S:SCARE |
+S:BR_ACID
+D:A large dragon, with scales of deepest black.
+
+N:593:Mature multi-hued dragon
+G:d:v
+I:110:60d10:20:80:70
+W:38:1:110000:1800
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d12
+B:CLAW:HURT:2d12
+B:BITE:HURT:4d12
+F:ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_2D2 | DROP_4D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD |
+F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_7 |
+S:BLIND | CONF | SCARE |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS
+D:A large dragon, scales shimmering many colours.
+
+N:594:Sky whale
+G:~:G
+I:110:80d10:20:75:30
+W:38:6:4000:1750
+E:0:0:0:0:1:0
+O:50:50:0:0
+B:CRUSH:HURT:20d2
+B:CRUSH:HURT:20d2
+B:CRUSH:HURT:20d2
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_60 | DROP_90 | DROP_2D2 | DROP_CORPSE |
+F:BASH_DOOR | SMART | GOOD |
+F:NO_CONF | NO_SLEEP | RES_NEXU | RES_TELE | RES_DISE
+F:MORTAL | ZANGBAND
+S:1_IN_9 |
+S:BRAIN_SMASH | CONF | SCARE | FORGET | TELE_TO | TELE_AWAY | SHRIEK
+D:A vastly intelligent whale-like being from the stars.
+
+N:595:Draebor, the Imp
+G:u:v
+I:120:19d99:12:80:50
+W:38:3:3000:3250
+E:0:1:1:0:1:0
+O:30:20:50:0
+B:CLAW:POISON:8d4
+B:CLAW:POISON:8d4
+B:BITE:HURT:8d8
+B:INSULT:*
+F:ESCORT
+F:DROP_60 | DROP_90 | DROP_1D2 | DROP_GOOD | ONLY_ITEM | DROP_CORPSE |
+F:CAN_SWIM | BASH_DOOR | RES_TELE | CAN_SPEAK |
+F:EVIL | DEMON | IM_FIRE | IM_COLD | IM_POIS | RES_WATE |
+F:UNIQUE | MALE | FORCE_SLEEP | FORCE_MAXHP | POWERFUL |
+F:NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:S_KIN | BLINK | TPORT | TELE_TO | TELE_AWAY | TELE_LEVEL | BLIND |
+S:CONF | SCARE
+D:An intensely irritating git of a monster.
+
+N:596:Mother Hydra
+G:u:v
+I:120:25d99:12:80:50
+W:40:3:30000:3250
+E:0:1:0:2:2:0
+O:0:50:50:0
+B:CLAW:POISON:8d4
+B:CLAW:POISON:8d4
+B:BITE:HURT:8d8
+F:ESCORT |
+F:DROP_60 | DROP_90 | DROP_1D2 | DROP_GOOD | ONLY_ITEM | DROP_CORPSE |
+F:CAN_SWIM | BASH_DOOR | ELDRITCH_HORROR | RES_TELE | CAN_SPEAK |
+F:EVIL | DEMON | IM_FIRE | IM_COLD | IM_POIS | RES_WATE |
+F:UNIQUE | FEMALE | FORCE_SLEEP | FORCE_MAXHP | POWERFUL |
+F:NO_CONF |
+F:MORTAL | CTHANGBAND
+S:1_IN_7 |
+S:S_HYDRA | S_DEMON | DARKNESS | BA_WATE | BO_ACID | BA_ACID
+D:The queen of the deep ones. "Vast, Polyphemus-like, and loathsome, it
+D:darted like a stupendous monster of nightmares..."
+
+N:597:Death knight
+G:p:D
+I:120:60d10:20:100:10
+W:38:1:2700:1111
+E:1:1:1:2:1:1
+O:0:90:0:10
+B:HIT:EXP_20:6d6
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_NETH |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_COLD | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_5 |
+S:BLIND | SCARE | CAUSE_3 | BO_NETH |
+S:S_MONSTERS
+D:It is a humanoid figure dressed in armour of an ancient form. From beneath
+D:its helmet, eyes glow a baleful red and seem to pierce you like lances of
+D:fire.
+
+N:598:Castamir the Usurper
+G:p:R
+I:120:88d10:20:90:40
+W:38:5:0:1600
+E:1:1:1:2:1:1
+O:10:60:10:10
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | TAKE_ITEM | BASH_DOOR |
+F:EVIL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | TRAPS | BO_FIRE | BO_COLD | BO_ELEC | BO_ICEE
+D:A Black Numenorean who usurped the throne of Gondor, he is treacherous and
+D:evil.
+
+N:599:Time vortex
+G:v:B
+I:130:32d10:100:40:0
+W:38:4:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:TIME:5d5
+F:FORCE_SLEEP |
+F:RAND_50 | RAND_25 |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_TIME
+D:You haven't seen it yet.
+
+N:600:Shimmering vortex
+G:v:o
+I:140:32d10:100:40:0
+W:38:4:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:BLIND:4d4
+B:ENGULF:BLIND:4d4
+F:FORCE_SLEEP |
+F:RAND_50 | RAND_25 | CAN_FLY |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_4 |
+S:BR_LITE | SHRIEK
+D:A strange pillar of shining light that hurts your eyes. Its shape changes
+D:constantly as it cuts through the air towards you. It is like a beacon,
+D:waking monsters from their slumber.
+
+N:601:Ancient blue dragon
+G:D:b
+I:120:72d10:20:90:80
+W:40:1:170000:2000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d8
+B:CLAW:HURT:4d8
+B:BITE:ELEC:7d8
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_3D2 | DROP_4D2 | DROP_CORPSE |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY |
+F:EVIL | DRAGON | IM_ELEC | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_ELEC
+D:A huge draconic form. Lightning crackles along its length.
+
+N:602:Ancient bronze dragon
+G:D:U
+I:120:72d10:20:90:80
+W:40:1:170000:2000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d8
+B:CLAW:HURT:4d8
+B:BITE:HURT:7d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:DROP_3D2 | DROP_4D2 | CAN_FLY |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_CONF
+D:A huge draconic form enveloped in a cascade of colour.
+
+N:603:Beholder
+G:e:U
+I:120:16d100:30:80:10
+W:40:3:1600:6000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:EXP_20:2d6
+B:GAZE:UN_POWER:2d6
+B:GAZE:INSANITY:2d6
+B:BITE:HURT:6d6
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | CAN_FLY |
+F:BASH_DOOR | DROP_CORPSE |
+F:EVIL | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_2 |
+S:BLIND | SLOW | CONF | SCARE | DRAIN_MANA | MIND_BLAST |
+S:FORGET | DARKNESS | BO_ACID | BO_FIRE | BO_COLD | BO_ELEC
+D:A vile creature with one huge central eye, twelve smaller eyes on stalks, and
+D:a huge mouth filled with sharp teeth.
+
+N:604:Emperor wight
+G:W:r
+I:120:38d10:20:40:10
+W:38:2:0:1600
+E:0:0:0:0:0:0
+O:0:40:60:0
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+B:TOUCH:EXP_80
+B:TOUCH:EXP_80
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | DROP_4D2 | CAN_FLY |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:HOLD | SCARE | CAUSE_3 | BO_NETH
+D:Your life force is torn from your body as this powerful unearthly being
+D:approaches.
+
+N:605:Seraph
+G:A:r
+I:120:150d10:30:68:255
+W:45:4:3100:5000
+E:1:1:1:2:1:1
+O:0:30:70:0
+B:HIT:HURT:4d6
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:4d6
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | NO_FEAR | GOOD | CAN_FLY |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | REFLECTING | RES_TELE
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_11 |
+S:HEAL | HASTE | TELE_AWAY | CONF | BO_MANA | BO_PLAS |
+S:S_MONSTERS | S_ANGEL
+D:It is an angel, fast and strong. You are stunned by its extreme holiness
+D:and try to resist all desires to obey it.
+
+N:606:Vargo, Tyrant of Fire
+G:E:r
+I:120:24d100:12:50:50
+W:43:3:0:4000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:FIRE:6d6
+B:HIT:FIRE:6d6
+B:HIT:FIRE:6d6
+B:HIT:FIRE:6d6
+F:UNIQUE | SUSCEP_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | CAN_SPEAK |
+F:EMPTY_MIND | CAN_SPEAK | MALE | AURA_FIRE |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_FIRE | IM_ACID | IM_POIS | IM_ELEC | NO_STUN |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_4 |
+S:BO_PLAS | BA_FIRE
+D:A towering fire elemental, Vargo burns everything beyond recognition.
+
+N:607:Black wraith
+G:W:D
+I:120:50d10:20:55:10
+W:38:2:0:1700
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+B:TOUCH:EXP_40
+B:TOUCH:EXP_40
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP
+F:BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | BO_NETH
+D:A figure that seems made of void, its strangely human shape is cloaked in
+D:shadow. It reaches out at you.
+
+N:608:Nightgaunt
+G:U:D
+I:110:24d10:20:50:80
+W:38:2:0:1000
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:STING:LOSE_STR:1d5
+B:TOUCH:PARALYZE:3d4
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_60 | ELDRITCH_HORROR |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY |
+F:EVIL | DEMON |
+F:IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | ZANGBAND
+S:1_IN_7 |
+S:BLIND | CONF | BO_FIRE
+D:It is a black, horned humanoid with wings.
+
+N:609:Baron of hell
+G:U:U
+I:110:150d12:10:130:40
+W:38:3:0:1000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:11d2
+B:CLAW:HURT:11d2
+B:CLAW:HURT:22d1
+F:IM_POIS | OPEN_DOOR | BASH_DOOR | MALE | RES_PLAS | IM_FIRE | NONLIVING |
+F:IM_FIRE | NO_CONF | NO_SLEEP | EVIL | DEMON | FORCE_MAXHP | RES_TELE |
+F:ZANGBAND | HAS_LITE
+S:1_IN_2 |
+S:BO_PLAS
+D:A minor demon lord with a goat's head, tough to kill.
+
+N:610:Scylla
+G:M:B
+I:125:100d20:20:100:70
+W:42:1:15000:13000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:POISON:10d3
+B:BITE:POISON:10d3
+B:BITE:POISON:10d3
+B:BITE:POISON:10d3
+F:FORCE_SLEEP | UNIQUE | AQUATIC | FORCE_MAXHP | COLD_BLOOD |
+F:ONLY_GOLD | DROP_2D2 | DROP_4D2 | DROP_CORPSE |
+F:BASH_DOOR | MOVE_BODY | EVIL | ZANGBAND | RES_WATE |
+F:ANIMAL | IM_POIS | IM_FIRE
+S:1_IN_5 |
+S:BR_POIS | BR_FIRE | SCARE | CONF | S_HYDRA
+D:A many-headed sea-monster, this foul creature preys on all those who
+D:escape the clutches of Charybdis.
+
+N:611:Monastic lich
+G:L:u
+I:120:12d100:30:80:30
+W:39:2:2000:5000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:KICK:HURT:24d1
+B:KICK:HURT:24d1
+B:CLAW:EXP_80:4d2
+B:CLAW:LOSE_DEX:4d2
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | SMART |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_3 |
+S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 |
+S:DRAIN_MANA | BRAIN_SMASH | ANIM_DEAD
+D:A skeletal form wrapped in priestly robes. Before its un-death, it
+D:was a monk in an evil order.
+
+N:612:Nether wraith
+G:W:G
+I:120:48d10:20:55:10
+W:39:2:0:1700
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:HIT:HURT:1d12
+B:HIT:HURT:1d12
+B:TOUCH:EXP_80
+B:TOUCH:EXP_80
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_90 | DROP_4D2 |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BLIND | SCARE | CAUSE_3 | MIND_BLAST | DARKNESS | BO_NETH
+D:A form that hurts the eye, death permeates the air around it. As it nears
+D:you, a coldness saps your soul.
+
+N:613:Hellhound
+G:C:r
+I:120:48d10:25:80:30
+W:35:3:600:600
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:3d12
+B:BITE:FIRE:3d12
+B:BITE:FIRE:3d12
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:RAND_25 | FRIENDS | AURA_FIRE | SUSCEP_COLD |
+F:BASH_DOOR | MOVE_BODY |
+F:ANIMAL | EVIL | IM_FIRE | BASEANGBAND | HAS_LITE |
+S:1_IN_5 | BR_FIRE
+D:It is a giant dog that glows with heat. Flames pour from its nostrils.
+
+N:614:7-headed hydra
+G:M:G
+I:120:100d10:20:90:20
+W:39:2:7000:2000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:POISON:3d9
+B:BITE:POISON:3d9
+B:BITE:POISON:3d9
+B:SPIT:BLIND:1d2
+F:FORCE_SLEEP | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:ONLY_GOLD | DROP_2D2 | DROP_4D2 | CAN_SWIM | DROP_CORPSE
+F:BASH_DOOR | MOVE_BODY |
+F:ANIMAL | IM_POIS | MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:SCARE | BA_POIS |
+S:BR_POIS
+D:A strange reptilian creature with seven heads dripping venom.
+
+N:615:Waldern, King of Water
+G:E:b
+I:130:25d100:12:80:50
+W:43:3:0:4250
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:8d5
+B:HIT:HURT:8d5
+B:HIT:HURT:8d5
+B:HIT:HURT:8d5
+F:UNIQUE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:EMPTY_MIND | COLD_BLOOD |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_ACID | IM_FIRE | IM_POIS | IM_COLD |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BO_ICEE | BO_WATE | BA_COLD | BA_WATE
+D:A towering water elemental, Waldern is master of all things liquid.
+D:Wave after wave drowns your frail body.
+
+N:616:Kavlax the Many-Headed
+G:d:v
+I:120:13d100:20:85:30
+W:39:3:90000:3000
+E:0:1:0:6:2:0
+O:50:50:0:0
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+F:UNIQUE | MALE | ATTR_MULTI | CAN_SPEAK | ATTR_ANY |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | RES_NEXU |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY |
+F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC |
+F:BASEANGBAND | HAS_LITE |
+S:1_IN_4 |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_SOUN | BR_CONF |
+S:BR_SHAR | BR_GRAV | BR_NEXU
+D:A large dragon with a selection of heads, all shouting and arguing as they
+D:look for prey, but each with its own deadly breath weapon.
+
+N:617:Ancient white dragon
+G:D:w
+I:120:72d10:20:90:80
+W:40:1:170000:2000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d8
+B:CLAW:HURT:4d8
+B:BITE:COLD:7d8
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_3D2 | DROP_4D2 | DROP_CORPSE |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY |
+F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP | SUSCEP_FIRE | BASEANGBAND
+F:HAS_LITE | ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_COLD
+D:A huge draconic form. Frost covers it from head to tail.
+
+N:618:Ancient green dragon
+G:D:g
+I:120:72d10:20:90:80
+W:40:1:170000:2000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d8
+B:CLAW:HURT:4d8
+B:BITE:POISON:7d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:DROP_3D2 | DROP_4D2 | CAN_FLY |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_POIS
+D:A huge draconic form enveloped in clouds of poisonous vapour.
+
+N:619:Chthonian
+G:w:D
+I:120:100d10:20:90:20
+W:39:2:12000:2300
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:SHATTER:3d11
+B:CRUSH:SHATTER:3d11
+B:TOUCH:LOSE_CON:1d2
+B:TOUCH:LOSE_CON:1d2
+F:IM_FIRE | RES_PLAS | IM_COLD | IM_POIS | ELDRITCH_HORROR | RES_TELE |
+F:KILL_WALL | ONLY_GOLD | DROP_4D2 | DROP_2D2 | CAN_SWIM | DROP_CORPSE |
+F:EVIL | FORCE_MAXHP | CTHANGBAND
+S:1_IN_5 |
+S:SCARE | CONF | HOLD | S_DEMON |
+S:MIND_BLAST | HEAL | HASTE | FORGET | BRAIN_SMASH
+D:"Flowing tentacles and a pulpy gray-black elongated sack of a body...
+D:no distinguishing features at all other than the reaching, groping
+D:tentacles. Or was there -- yes! -- a lump in the upper body of
+D:the thing... a container of sorts for the brain, ganglia, or
+D:whichever diseased organ governed this horror's loathsome life!"
+
+N:620:Eldrak
+G:T:r
+I:110:75d10:20:80:50
+W:38:3:7000:1500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:BITE:HURT:3d4
+B:BITE:HURT:3d4
+F:FORCE_MAXHP | MOVE_BODY |
+F:ONLY_ITEM | DROP_2D2 | REGENERATE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_SHORE | WILD_WOOD |
+F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP | DROP_CORPSE | BASEANGBAND
+D:A massive troll of huge strength. Eldraks are extremely stupid and extremely
+D:violent.
+
+N:621:Ettin
+G:T:b
+I:110:15d100:20:100:30
+W:39:3:8000:2000
+E:1:1:1:2:2:1
+O:0:100:0:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:BITE:HURT:3d8
+B:BITE:HURT:3d8
+F:FORCE_MAXHP | REGENERATE | MOVE_BODY |
+F:ONLY_ITEM | DROP_1D2 | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD | WILD_SWAMP |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+D:A massive two-headed troll, larger and stronger than many men together.
+D:Fortunately, ettins are solitary creatures. They are also living proof that
+D:two heads are not necessarily more intelligent than one...
+
+N:622:Night mare
+G:q:G
+I:120:15d100:30:85:0
+W:39:3:6000:2900
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:EXP_80:2d6
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:CONFUSE:6d6
+F:FORCE_MAXHP |
+F:ONLY_GOLD | DROP_2D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:HAS_LITE | NO_CUT
+D:A fearsome skeletal horse with glowing eyes that watch you with little
+D:more than a hatred of all that lives. Its flying hooves do not touch the
+D:ground.
+
+N:623:Vampire lord
+G:V:b
+I:120:20d100:20:70:10
+W:42:1:1700:1800
+E:1:1:1:2:1:1
+O:0:70:30:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:BITE:EXP_80:4d6
+B:BITE:EXP_80:4d6
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_60 | DROP_4D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | RES_TELE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | DARKNESS | BO_NETH
+D:A foul wind chills your bones as this ghastly figure approaches.
+
+N:624:Ancient black dragon
+G:D:s
+I:120:10d100:20:90:80
+W:41:1:170000:2500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d9
+B:CLAW:HURT:4d9
+B:BITE:ACID:7d9
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_3D2 | DROP_4D2 | DROP_CORPSE |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | IM_ACID | NO_CONF | NO_SLEEP | BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_ACID
+D:A huge draconic form. Pools of acid melt the floor around it.
+
+N:625:Weird fume
+G:#:v
+I:120:35d10:100:40:0
+W:40:2:0:800
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:CONFUSE:8d4
+B:ENGULF:CONFUSE:8d4
+F:FORCE_SLEEP |
+F:RAND_50 | RAND_25 | RES_NEXU | AURA_ELEC | IM_FIRE | IM_ELEC |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL |
+F:CAN_FLY | ATTR_MULTI | ATTR_ANY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | ZANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_CHAO | BR_NEXU | BR_NUKE
+D:A swirling spiral of mist, constantly changing its appearance.
+
+N:626:Spawn of Ubbo-Sathla
+G:j:v
+I:120:30d10:100:40:0
+W:40:5:0:300
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:ACID:8d4
+B:CRUSH:ACID:8d4
+F:FORCE_SLEEP | ATTR_MULTI | ATTR_ANY | EVIL |
+F:RAND_25 | RES_NEXU | AURA_ELEC | IM_FIRE | IM_ELEC |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | KILL_BODY |
+F:CAN_SWIM | NO_CONF | NO_SLEEP | CTHANGBAND | NO_CUT
+S:MULTIPLY
+D:Weird, jelly like creatures. No two look the same.
+
+N:627:Fat Man
+G:{:D
+I:120:14d14:10:80:12
+W:40:2:0:200
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:SHATTER:200d2
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | REFLECTING |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS
+F:NO_FEAR | NO_CONF | NO_SLEEP | NONLIVING | RES_TELE | JOKEANGBAND | NO_CUT | NO_STUN
+D:A shining machine of death and destruction.
+
+N:628:Malekith the Accursed
+G:h:v
+I:125:25d100:20:70:10
+W:44:2:3500:7500
+E:1:1:1:2:1:1
+O:50:0:50:0
+B:HIT:HURT:12d9
+B:HIT:HURT:12d9
+F:MALE | REGENERATE | UNIQUE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:ONLY_ITEM | DROP_90 | DROP_4D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | HURT_LITE | ZANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | TELE_TO | BLIND | CONF | CAUSE_3 | DARKNESS | FORGET | HOLD |
+S:S_MONSTER | S_MONSTERS | S_DEMON | TPORT | BA_NETH | MIND_BLAST |
+S:S_KIN
+D:One of the oldest and most powerful dark elves, Malekith is a master
+D:sorcerer devoted to evil. The left side of his face is pale, and the other
+D:is dark. His hair is long and white.
+
+N:629:Shadowfax, steed of Gandalf
+G:q:v
+I:130:30d100:20:100:50
+W:40:3:2600:2000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:KICK:HURT:5d5
+B:KICK:HURT:5d5
+B:BITE:HURT:6d6
+F:FORCE_MAXHP | UNIQUE | ANIMAL | GOOD |
+F:REGENERATE | BASH_DOOR | IM_FIRE | IM_COLD | IM_ELEC |
+F:IM_POIS | NO_FEAR | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:Shadowfax is the chief of the "Mearas", the greatest of all horses that are
+D:bred in the fields of Rohan. Although the Mearas should technically be only
+D:ridden by the royal family of Rohan, only Gandalf the Wizard has ever
+D:succeeded in taming Shadowfax: and even then, Shadowfax will not be subjected
+D:to a bridle or saddle, but must be ridden bareback.
+
+N:630:Spirit troll
+G:G:G
+I:110:10d100:20:90:5
+W:40:3:0:900
+E:0:0:0:0:0:0
+O:10:90:0:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:FORCE_MAXHP |
+F:DROP_90 |
+F:INVISIBLE | PASS_WALL | CAN_FLY |
+F:EVIL | TROLL | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:A weird ghostly troll-like being from the ethereal plane.
+
+N:631:War troll
+G:T:b
+I:120:50d10:20:100:50
+W:40:3:6500:800
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:BITE:HURT:3d5
+B:BITE:HURT:3d5
+F:FORCE_MAXHP | SUSCEP_FIRE | REGENERATE |
+F:DROP_90 | REGENERATE | FRIENDS |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:EVIL | TROLL | IM_POIS | NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+D:A massive troll, equipped with a scimitar and heavy armour.
+
+N:632:Disenchanter worm mass
+G:w:v
+I:100:10d8:7:5:10
+W:40:3:50:30
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRAWL:UN_BONUS:1d4
+F:RAND_50 | RES_DISE | ATTR_MULTI | CAN_SWIM |
+F:STUPID | WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | HURT_LITE | NO_FEAR | BASEANGBAND | NO_CUT
+S:MULTIPLY
+D:It is a strange mass of squirming worms. Magical energy crackles
+D:around its disgusting form.
+
+N:633:Rotting quylthulg
+G:Q:u
+I:120:48d10:20:1:0
+W:45:1:3000:3000
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TPORT |
+S:S_UNDEAD
+D:It is a pulsing flesh mound that reeks of death and putrefaction.
+
+N:634:Lesser titan
+G:P:y
+I:120:24d100:30:80:15
+W:56:3:30000:6000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:CONFUSE:9d9
+B:HIT:CONFUSE:9d9
+B:HIT:CONFUSE:9d9
+B:HIT:CONFUSE:9d9
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_GOOD | DROP_4D2 | DROP_SKELETON | DROP_CORPSE |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | MALE | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | TELE_TO | SCARE |
+S:S_MONSTERS
+D:It is a humanoid figure thirty feet tall that gives off an aura of power
+D:and hate.
+
+N:635:9-headed hydra
+G:M:r
+I:120:100d12:20:95:20
+W:40:2:8000:3000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:3d10
+B:BITE:FIRE:3d10
+B:BITE:FIRE:3d10
+B:BITE:FIRE:3d10
+F:FORCE_SLEEP | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:ONLY_GOLD | DROP_3D2 | DROP_4D2 | CAN_SWIM |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_CORPSE |
+F:ANIMAL | IM_FIRE | MORTAL | BASEANGBAND
+S:1_IN_4 |
+S:SCARE | BO_FIRE | BR_FIRE
+D:A strange reptilian creature with nine smouldering heads.
+
+N:636:Enchantress
+G:p:R
+I:130:52d10:20:60:10
+W:40:4:1700:2100
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+B:HIT:HURT:2d8
+F:FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:BLIND |
+S:S_DRAGON
+D:This elusive female spellcaster has a special affinity for dragons, without
+D:whom she rarely fights.
+
+N:637:Ranger chieftain
+G:p:W
+I:120:50d20:20:60:10
+W:41:2:1800:1800
+E:1:1:1:2:1:1
+O:30:50:20:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:MALE | INVISIBLE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE
+F:ONLY_ITEM | DROP_2D2 | TAKE_ITEM |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP | IM_FIRE | IM_ELEC | IM_COLD | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:S_MONSTERS |
+S:ARROW_2 | ARROW_3 | ARROW_4 | MISSILE | BO_FIRE | BO_ELEC | BA_COLD |
+S:HASTE | BLINK | S_ANIMALS
+D:A chieftain among the Rangers. His understanding of nature gives him
+D:powerful elemental spells to use against you, in addition to his skills
+D:as an archer and a warrior. Furthermore, he is a master of camouflage, so
+D:you will need magically enhanced seeing to spot him.
+
+N:638:Sorcerer
+G:p:R
+I:130:52d10:20:60:10
+W:40:2:1700:2150
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_90 | DROP_4D2 |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:BLINK | TELE_TO | BLIND | CONF | CAUSE_3 | TRAPS |
+S:BO_ACID | BA_FIRE | BA_COLD |
+S:S_MONSTER | S_UNDEAD | S_DRAGON
+D:A human figure in robes, he moves with magically improved speed, and his
+D:hands are ablur with spell casting.
+
+N:639:Xaren
+G:X:s
+I:120:32d10:20:80:10
+W:40:1:500000:1200
+E:0:0:0:3:0:0
+O:0:0:0:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:FORCE_MAXHP | SUSCEP_ACID |
+F:EMPTY_MIND | COLD_BLOOD |
+F:ONLY_GOLD | DROP_2D2 |
+F:KILL_ITEM | PASS_WALL |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:It is a tougher relative of the Xorn. Its hide glitters with metal ores.
+
+N:640:Giant roc
+G:B:u
+I:110:80d13:20:70:10
+W:40:3:6000:1000
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:CRUSH:HURT:8d12
+B:CRUSH:HURT:8d12
+B:HIT:ELEC:12d12
+F:BASH_DOOR | CAN_FLY | WILD_MOUNTAIN | WILD_WOOD |
+F:ANIMAL | IM_ELEC | DROP_CORPSE | SUSCEP_ACID | HAS_EGG
+F:MORTAL | BASEANGBAND
+D:A vast legendary bird, its iron talons rake the most impenetrable of
+D:surfaces and its screech echoes through the many winding dungeon corridors.
+
+N:641:Minotaur
+G:H:U
+I:130:100d10:13:25:10
+W:40:2:15000:2100
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BUTT:HURT:4d6
+B:BUTT:HURT:4d6
+B:BUTT:HURT:2d6
+B:BUTT:HURT:2d6
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND
+D:It is a cross between a human and a bull.
+
+N:642:Medusa, the Gorgon
+G:n:v
+I:120:24d100:30:100:5
+W:40:3:4000:9000
+E:1:1:1:2:0:1
+O:30:35:35:0
+B:GAZE:EXP_80
+B:GAZE:PARALYZE
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+F:UNIQUE | FEMALE | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_ACID | IM_FIRE | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:1_IN_2 |
+S:HOLD | SCARE | CAUSE_3 | BO_FIRE | BO_PLAS | BA_ACID |
+S:S_HYDRA | S_KIN
+D:One of the original three ugly sisters. Her face could sink a thousand
+D:ships. Her scales rattle as she slithers towards you, venom dripping from
+D:her ghastly mouth.
+
+N:643:Death drake
+G:D:G
+I:120:21d100:25:100:80
+W:45:2:170000:10000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d10
+B:CLAW:HURT:4d10
+B:BITE:EXP_80:7d10
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | RES_TELE
+F:INVISIBLE | TAKE_ITEM | CAN_FLY |
+F:PASS_WALL | POWERFUL | MOVE_BODY | RES_NETH |
+F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_NETH
+D:It is a dragon-like form wrapped in darkness. You cannot make out its
+D:true form but you sense its evil.
+
+N:644:Ancient red dragon
+G:D:r
+I:120:10d100:20:90:80
+W:41:1:170000:2500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d9
+B:CLAW:HURT:4d9
+B:BITE:FIRE:7d9
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:DROP_3D2 | DROP_4D2 | CAN_FLY | SUSCEP_COLD |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_FIRE
+D:A huge draconic form. Wisps of smoke steam from its nostrils and the
+D:extreme heat surrounding it makes you gasp for breath.
+
+N:645:Ancient gold dragon
+G:D:y
+I:120:10d100:20:90:80
+W:41:1:170000:2500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d9
+B:CLAW:HURT:4d9
+B:BITE:HURT:7d9
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:DROP_3D2 | DROP_4D2 | CAN_FLY |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | NO_STUN | NO_SLEEP | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF | SCARE |
+S:BR_SOUN
+D:A huge draconic form wreathed in a nimbus of light. Its roar stuns and
+D:deafens you.
+
+N:646:Great crystal drake
+G:D:U
+I:120:21d100:25:100:80
+W:45:2:170000:10000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d10
+B:CLAW:HURT:4d10
+B:BITE:HURT:7d10
+F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | DROP_CORPSE
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | CAN_FLY |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT
+F:ATTR_MULTI
+S:1_IN_6 |
+S:SLOW | CONF | SCARE |
+S:BR_SHAR
+D:A huge crystalline dragon. Its claws could cut you to shreds and its
+D:teeth are razor sharp. Strange colours ripple through it as it moves in
+D:the light.
+
+N:647:Wyrd sister
+G:p:v
+I:125:50d11:20:60:10
+W:40:4:1600:1900
+E:1:1:1:2:1:1
+O:10:0:80:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d8
+F:FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_SKELETON
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP
+F:MORTAL | ZANGBAND
+S:1_IN_2 |
+S:BLIND |
+S:S_DEMON | CONF | SCARE | DARKNESS | BA_CHAO
+D:This old crone is rumoured to be a witch of chaos... but you don't
+D:really believe in witches, do you?
+
+N:648:Vrock
+G:U:s
+I:110:40d10:20:50:80
+W:40:2:2700:2000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:CRUSH:HURT:8d12
+B:CRUSH:HURT:8d12
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:FRIENDS |
+F:ONLY_ITEM | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_8 |
+S:BLIND | CONF
+D:It is a demon with a long neck and raking claws.
+
+N:649:Death quasit
+G:u:D
+I:130:44d10:20:80:0
+W:40:3:600:1000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:BITE:LOSE_DEX:3d6
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 | DROP_4D2 | NONLIVING |
+F:SMART | INVISIBLE | PASS_WALL | CAN_FLY |
+F:EVIL | DEMON | IM_FIRE | IM_POIS | RES_TELE
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_10 |
+S:BLIND | CONF | SCARE | CAUSE_3 | FORGET |
+S:S_DEMON
+D:It is a demon of small stature, but its armoured frame moves with
+D:lightning speed and its powers make it a tornado of death and destruction.
+
+N:650:Giganto, the Gargantuan
+G:~:s
+I:110:80d10:20:75:30
+W:38:6:8000:1750
+E:0:1:0:0:1:0
+O:60:40:0:0
+B:CRUSH:HURT:30d2
+B:CRUSH:HURT:30d2
+B:CRUSH:HURT:30d2
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | IM_FIRE |
+F:DROP_60 | DROP_90 | DROP_2D2 | UNIQUE | WEIRD_MIND |
+F:BASH_DOOR | SMART | EVIL | IM_COLD | DROP_CORPSE
+F:RES_WATE | WILD_OCEAN |
+F:MORTAL | ZANGBAND
+S:1_IN_9 |
+S:BR_NUKE | BA_WATE
+D:A gargantuan mutant whale, who has grown legs that enable it to walk
+D:on dry land as well.
+
+N:651:Strygalldwir
+G:U:W
+I:120:12d100:90:60:10
+W:41:3:5000:8000
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:CLAW:HURT:5d5
+B:CLAW:HURT:5d5
+B:HIT:LOSE_STR:4d4
+B:TOUCH:EXP_80:8d1
+F:UNIQUE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | MOVE_BODY | NONLIVING | NO_SLEEP | NO_CONF|
+F:OPEN_DOOR | BASH_DOOR | IM_POIS | IM_COLD | DEMON | EVIL | ZANGBAND
+S:1_IN_3 |
+S:CAUSE_3 | HOLD | SCARE | BLIND | BO_ACID | S_DEMON |
+S:FORGET | BO_NETH | MIND_BLAST | DARKNESS
+D:"it was well over six feet in height, with great branches of antlers
+D:growing out of its forehead. Nude, its flesh was a uniform ash-gray
+D:in color. It appeared to be sexless, and it had gray, leathery wings
+D:extending far out behind it."
+
+N:652:Fallen angel
+G:A:s
+I:130:100d25:30:90:255
+W:49:6:0:8000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:GAZE:EXP_40:4d4
+B:GAZE:EXP_40:4d4
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | NO_FEAR | EVIL | REFLECTING |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | RES_TELE | RES_NETH |
+F:CAN_FLY | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:TELE_TO | BLIND | SCARE | CAUSE_2 | CAUSE_4 |
+S:S_DEMON | BO_NETH
+D:An angelic being, who was mighty once, but dared defy its Creator.
+
+N:653:Giant headless
+G:H:u
+I:110:30d12:20:50:40
+W:41:2:4000:900
+E:1:1:1:2:1:0
+O:0:100:0:0
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+F:FRIENDS | DROP_60 | DROP_90 | OPEN_DOOR | BASH_DOOR |
+F:DROP_SKELETON | DROP_CORPSE
+F:EVIL | ZANGBAND
+S:1_IN_6
+S:SCARE | BA_NUKE | BLIND
+D:Giant headless humanoid abominations created by a magical mutation.
+
+N:654:Judge Fire
+G:s:R
+I:120:18d100:90:70:10
+W:41:3:0:12000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:FIRE:5d5
+B:HIT:FIRE:5d5
+B:GAZE:EXP_80
+B:WAIL:TERRIFY
+F:UNIQUE | MALE | CAN_SPEAK | SUSCEP_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | POWERFUL |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | SUSCEP_COLD | IM_POIS | IM_FIRE | RES_PLAS |
+F:NO_CONF | NO_SLEEP | JOKEANGBAND | HAS_LITE | NO_CUT | NO_STUN
+S:1_IN_3 |
+S:CAUSE_3 | BO_FIRE | BA_FIRE | BR_FIRE | BO_PLAS
+S:DARKNESS | S_MONSTER | S_DEMON | S_UNDEAD | TPORT | BLINK | SCARE
+D:One of the Dark Judges, he has come to punish your crime of living.
+D:He looks like a skeleton enveloped in flames.
+
+N:655:Ubbo-Sathla, the Unbegotten Source
+G:j:W
+I:120:20d100:90:80:10
+W:41:3:0:13500
+E:0:0:0:0:0:0
+O:30:50:0:10
+B:CRUSH:ACID:5d5
+B:HIT:POISON:5d5
+B:CRUSH:ACID:5d5
+B:HIT:POISON:5d5
+F:UNIQUE | CAN_SPEAK | NO_STUN | NO_SLEEP | NO_CONF | NO_STUN |
+F:FORCE_SLEEP | FORCE_MAXHP | ELDRITCH_HORROR | ESCORT |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | REGENERATE | SUSCEP_FIRE |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | KILL_BODY |
+F:EVIL | SUSCEP_FIRE | IM_COLD | IM_POIS | CTHANGBAND | NO_CUT
+D:"There, in the gray beginning of Earth, the formless mass that was
+D:Ubbo-Sathla reposed amid the slime and the vapors. Headless,
+D:without organs or members..."
+
+N:656:Judge Mortis
+G:z:G
+I:120:18d100:90:70:10
+W:41:3:0:13000
+E:1:1:1:2:1:1
+O:0:75:0:15
+B:HIT:POISON:5d5
+B:HIT:DISEASE:5d5
+B:TOUCH:LOSE_ALL
+B:TOUCH:EXP_80
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | SUSCEP_FIRE | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | JOKEANGBAND | NO_CUT | NO_STUN
+S:1_IN_3 |
+S:BLIND | SCARE | CAUSE_3 | BO_ACID | BO_NETH | BR_POIS |
+S:BR_NETH | BO_NETH | BLINK | TPORT | ANIM_DEAD
+S:BO_POIS | S_UNDEAD
+D:Another Dark Judge, he is a rotting humanoid with a cow's skull as
+D:his head.
+
+N:657:Dark elven sorcerer
+G:h:R
+I:130:80d10:20:70:10
+W:41:2:1300:3000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE
+F:ONLY_ITEM | DROP_90 | DROP_4D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | BLINK | TELE_TO | BLIND | CONF | CAUSE_3 | DARKNESS |
+S:BO_ACID | BA_FIRE | BA_COLD | ANIM_DEAD
+S:S_MONSTER | S_UNDEAD | S_DEMON | MISSILE
+D:A dark elven figure, dressed in deepest black. Power seems to crackle
+D:from his slender frame.
+
+N:658:Master lich
+G:L:r
+I:120:18d100:20:80:50
+W:41:2:1800:7000
+E:1:1:1:2:1:1
+O:10:45:25:10
+B:TOUCH:EXP_80
+B:TOUCH:UN_POWER
+B:TOUCH:LOSE_DEX:2d12
+B:TOUCH:LOSE_DEX:2d12
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_3 |
+S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 |
+S:DRAIN_MANA | BRAIN_SMASH | S_UNDEAD
+D:A skeletal form wrapped in robes. Powerful magic crackles along its
+D:bony fingers.
+
+N:659:Byakhee
+G:U:D
+I:110:40d10:20:40:80
+W:41:3:1300:1500
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:CLAW:LOSE_STR:3d4
+B:BITE:EXP_20:3d4
+F:FORCE_SLEEP | FORCE_MAXHP | FRIENDS |
+F:ONLY_ITEM | DROP_2D2 | ELDRITCH_HORROR |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | CTHANGBAND
+S:1_IN_9 |
+S:BO_FIRE |
+S:S_DEMON | CONF
+D:"There flapped rhythmically a horde of tame, trained, hybrid
+D:winged things... not altogether crows, nor moles, nor buzzards,
+D:nor ants, nor decomposed human beings, but something I cannot
+D:and must not recall."
+
+N:660:Eol, the Dark Elf
+G:h:D
+I:130:80d30:20:100:60
+W:49:2:1400:25000
+E:1:1:1:2:1:1
+O:10:40:40:10
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | CAN_SPEAK |
+F:ONLY_ITEM | DROP_4D2 | DROP_2D2 | DROP_GOOD | DROP_CHOSEN |
+F:SMART | IM_ELEC | IM_COLD | IM_POIS | IM_FIRE |
+F:REFLECTING | OPEN_DOOR | BASH_DOOR | SPECIAL_GENE |
+F:HURT_LITE | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | BLINK | TELE_TO | BLIND | CONF | CAUSE_4 | DARKNESS |
+S:BA_NETH | BA_ELEC | BA_ACID | BA_FIRE | BA_COLD | BO_MANA |
+S:S_MONSTERS | S_UNDEAD | S_DRAGON | S_DEMON
+D:A lord of the Teleri, Eol is a mighty metalsmith, the first
+D:one ever to forge weapons of meteorite iron. His dark
+D:countenance glares at you in disdain.
+
+N:661:Archon
+G:A:y
+I:130:100d35:30:140:255
+W:55:5:3200:12000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:GAZE:TERRIFY:4d4
+B:GAZE:TERRIFY:4d4
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | NO_FEAR | GOOD | REFLECTING |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | RES_TELE | CAN_FLY
+F:BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:TELE_TO | BLIND | SCARE | CAUSE_2 | CAUSE_4 | BO_MANA |
+S:S_ANGEL
+D:Never a more heavenly being have you seen. The very holiness of its
+D:presence makes you deeply respect it. Few creatures can match the powers
+D:of an Archon; fewer still live to tell the tale after attacking one.
+
+N:662:Formless spawn of Tsathoggua
+G:U:D
+I:110:22d20:20:40:80
+W:41:2:0:1850
+E:0:0:0:0:0:0
+O:35:45:0:10
+B:HIT:ACID:2d4
+B:HIT:ACID:2d4
+B:CRUSH:HURT:3d4
+B:BITE:ACID:6d6
+F:FORCE_SLEEP | FORCE_MAXHP | ELDRITCH_HORROR | NONLIVING |
+F:ONLY_ITEM | DROP_90 | REGENERATE | RES_TELE |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_SWIM |
+F:EVIL | DEMON | NO_CONF | NO_SLEEP | SUSCEP_FIRE | IM_POIS | CTHANGBAND | NO_CUT
+S:1_IN_9 |
+S:BO_FIRE | BO_ACID |
+S:S_DEMON | MIND_BLAST | DARKNESS |
+D:"...living things that oozed along stone channels...
+D:But they were not toads like Tsathoggua himself. Far worse --
+D:they were amorphous lumps of viscous black slime that took
+D:temporary shapes for various purposes."
+
+N:663:Hunting horror
+G:U:D
+I:110:30d17:20:90:80
+W:42:2:0:2300
+E:0:0:0:0:0:0
+O:45:35:0:10
+B:BITE:LOSE_DEX:1d3
+B:BITE:POISON:1d3
+B:CRUSH:HURT:9d4
+F:FORCE_SLEEP | FORCE_MAXHP | ELDRITCH_HORROR |
+F:ONLY_ITEM | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | CAN_FLY |
+F:EVIL | DEMON | HURT_LITE | IM_POIS |
+F:IM_FIRE | NO_CONF | NO_SLEEP | CTHANGBAND
+S:1_IN_9 |
+S:BLIND | CONF | S_DEMON | BR_DARK
+D:"And in the air there were great viperine creatures,
+D:which had curiously distorted heads, and grotesquely great
+D:clawed appendages, supporting themselves with ease by the aid
+D:of black rubbery wings of singularly monstrous dimensions."
+
+N:664:Undead beholder
+G:e:u
+I:120:27d100:30:100:10
+W:45:3:1600:8000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:EXP_40:3d6
+B:GAZE:UN_POWER:3d6
+B:GAZE:INSANITY:3d6
+B:BITE:EXP_40:7d6
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE |
+F:COLD_BLOOD | BASH_DOOR |
+F:EVIL | UNDEAD | CAN_FLY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:SLOW | CONF | CAUSE_4 | DRAIN_MANA | MIND_BLAST | FORGET |
+S:BO_MANA | BO_NETH | BRAIN_SMASH | BA_FIRE | BA_COLD | BO_ACID |
+S:S_UNDEAD | ANIM_DEAD
+D:A beholder which has cheated death. Black nether storms rage around the
+D:bloodshot pupil of its central giant eye, and light seems to bend as it
+D:sucks its power from the very air around it. Your soul chills as it drains
+D:your vitality for its evil enchantments.
+
+N:665:Shadow
+G:G:D
+I:120:10d20:30:30:20
+W:37:3:0:400
+E:0:0:0:0:0:0
+O:90:10:0:0
+B:TOUCH:EXP_80
+B:TOUCH:EXP_40
+B:CLAW:LOSE_INT:1d10
+B:CLAW:LOSE_WIS:1d10
+F:FORCE_SLEEP | CAN_FLY |
+F:ONLY_ITEM | DROP_1D2 | POWERFUL | REGENERATE | HURT_LITE |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | RES_NETH |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_8 |
+S:BO_NETH | TELE_TO | SLOW
+D:A mighty spirit of darkness of vaguely humanoid form. Razor-edged claws
+D:reach out to end your life as it glides towards you, seeking to suck the
+D:energy from your soul to feed its power.
+
+N:666:Iron lich
+G:L:s
+I:120:22d100:30:100:10
+W:42:4:0:10000
+E:0:0:0:0:1:0
+O:0:0:100:0
+B:BUTT:COLD:3d6
+B:BUTT:FIRE:3d6
+B:BUTT:ELEC:3d6
+F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING |
+F:COLD_BLOOD | BASH_DOOR | CAN_FLY | SUSCEP_ACID |
+F:EVIL | UNDEAD | POWERFUL | SMART |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | RES_TELE |
+F:ONLY_ITEM | DROP_60 | DROP_GOOD |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:BA_WATE | BA_FIRE | BO_ICEE | BA_ELEC | BA_COLD |
+S:CAUSE_4 | DRAIN_MANA | BRAIN_SMASH | S_UNDEAD
+D:It is a huge, twisted grey skull floating through the air. Its cold eyes
+D:burn with hatred towards all who live.
+
+N:667:Dread
+G:G:o
+I:120:25d20:20:30:10
+W:42:1:1000:600
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:LOSE_STR:3d4
+F:FORCE_SLEEP |
+F:RAND_25 | FRIENDS | CAN_FLY |
+F:ONLY_ITEM | DROP_60 |
+F:TAKE_ITEM | INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_15 |
+S:BLIND | HOLD | CONF | DRAIN_MANA | BO_NETH
+D:It is a form that screams its presence against the eye. Death incarnate,
+D:its hideous black body seems to struggle against reality as the universe
+D:itself struggles to banish it.
+
+N:668:Greater basilisk
+G:R:D
+I:120:20d100:25:100:15
+W:42:2:3000:10000
+E:0:1:0:2:1:0
+O:0:50:50:0
+B:GAZE:PARALYZE:3d12
+B:GAZE:PARALYZE:3d12
+B:BITE:POISON:2d12
+B:BITE:POISON:2d12
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | POWERFUL |
+F:OPEN_DOOR | BASH_DOOR | EVIL | IM_POIS | DROP_CORPSE |
+F:ANIMAL | NO_CONF | NO_SLEEP | CAN_SWIM |
+F:MORTAL | BASEANGBAND
+S:1_IN_8
+S:BR_POIS | BR_DARK | BR_NEXU
+D:A large basilisk, whose shape resembles that of a great wyrm.
+
+N:669:Charybdis
+G:~:r
+I:120:20d100:20:100:70
+W:42:1:15000:13000
+E:0:0:0:0:1:0
+O:50:50:0:0
+B:ENGULF:HURT:10d8
+B:ENGULF:HURT:10d8
+B:ENGULF:HURT:10d8
+F:AQUATIC | EVIL | UNIQUE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD |
+F:SMART | POWERFUL | MOVE_BODY | NEVER_MOVE |
+F:IM_ACID | IM_FIRE | IM_COLD | RES_WATE | COLD_BLOOD |
+F:IM_ELEC | IM_POIS | NO_STUN | ZANGBAND
+S:1_IN_5 |
+S:BA_WATE
+D:A monstrous dweller of the depths; its hungry maw has been the doom
+D:of innumerable sailors!
+
+N:670:Jack of Shadows
+G:p:s
+I:150:30d100:70:150:4
+W:60:4:2300:10000
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:5d10
+B:HIT:HURT:5d10
+B:HIT:EAT_ITEM:2d10
+B:HIT:EAT_ITEM:2d10
+F:MALE | FORCE_MAXHP | CAN_SPEAK | REFLECTING | DROP_SKELETON | DROP_CORPSE |
+F:REGENERATE |
+F:IM_ACID | IM_ELEC | IM_COLD | IM_FIRE | IM_POIS | RES_TELE |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | DROP_GREAT | UNIQUE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL |
+F:MORTAL | ZANGBAND
+S:1_IN_3
+S:BLIND | HEAL | BA_DARK | HASTE | CONF |
+D:Deriving his strength from the shadows, this king of thieves
+D:steals only for the challenge.
+
+N:671:Zephyr Lord
+G:W:v
+I:130:80d10:20:70:10
+W:43:2:600:3000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:EXP_20:8d2
+B:HIT:LOSE_STR:8d2
+B:HIT:LOSE_CON:8d2
+F:MALE | ATTR_MULTI | UNDEAD | EVIL |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS |
+F:ONLY_ITEM | DROP_90 | DROP_4D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:NO_STUN | NO_CONF | NO_SLEEP | ZANGBAND | NO_CUT
+S:1_IN_5 |
+S:SHRIEK | S_HOUND
+D:An undead master of the vicious Zephyr hounds.
+
+N:672:Juggernaut of Khorne
+G:g:D
+I:120:90d19:12:90:10
+W:43:3:6000:2500
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BUTT:HURT:6d6
+B:CRUSH:HURT:8d6
+B:CRUSH:HURT:8d6
+B:BUTT:HURT:6d6
+F:COLD_BLOOD | BASH_DOOR | REFLECTING | KILL_ITEM |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | ZANGBAND | NO_CUT
+D:A great, vicious beast whose flesh is made of steel and whose
+D:blood is fire. It charges at you ignoring everything else.
+
+N:673:Mumak
+G:q:s
+I:110:90d10:20:55:100
+W:63:2:150000:2100
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BUTT:HURT:8d6
+B:BUTT:HURT:8d6
+B:CRUSH:HURT:8d4
+F:FRIENDS | DROP_CORPSE |
+F:BASH_DOOR | ANIMAL | MORTAL | BASEANGBAND
+D:A massive elephantine form with eyes twisted by madness.
+
+N:674:Judge Fear
+G:W:D
+I:120:18d100:90:70:10
+W:43:4:0:12000
+E:1:1:1:2:1:1
+O:100:0:0:0
+B:GAZE:TERRIFY
+B:GAZE:EXP_40
+B:GAZE:EXP_40
+B:GAZE:HURT:2d20
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | JOKEANGBAND | NO_CUT | NO_STUN
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | MIND_BLAST | BRAIN_SMASH |
+S:S_UNDEAD | DRAIN_MANA | FORGET | ANIM_DEAD
+D:A Dark Judge, reputedly so frightening that his gaze can kill.
+
+N:675:Ancient multi-hued dragon
+G:D:v
+I:120:21d100:25:100:80
+W:43:1:170000:13000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d10
+B:CLAW:HURT:4d10
+B:BITE:HURT:7d10
+F:ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | IM_ACID | IM_FIRE | IM_COLD |
+F:IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BLIND | CONF | SCARE |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS
+D:A huge draconic form. Many colours ripple down its massive frame. Few
+D:live to see another.
+
+N:676:Ethereal dragon
+G:D:o
+I:120:21d100:25:100:80
+W:45:2:170000:10000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d10
+B:CLAW:HURT:4d10
+B:BITE:HURT:7d10
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 |
+F:INVISIBLE | CAN_FLY |
+F:PASS_WALL | POWERFUL | MOVE_BODY |
+F:DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | CONF |
+S:BR_LITE | BR_DARK | BR_CONF
+D:A huge dragon emanating from the ethereal plane, this terrible creature is
+D:a master of light and dark. Its form disappears from sight as it cloaks
+D:itself in unearthly shadows.
+
+N:677:Dark young of Shub-Niggurath
+G:U:g
+I:120:12d100:20:75:80
+W:43:2:0:5000
+E:0:0:0:0:0:0
+O:0:40:30:20
+B:CRUSH:HURT:5d6
+B:CRUSH:HURT:5d6
+B:BITE:LOSE_STR:1d6
+B:BITE:LOSE_STR:1d6
+F:FORCE_SLEEP | FORCE_MAXHP | ELDRITCH_HORROR |
+F:ONLY_ITEM | DROP_1D2 | NONLIVING | CAN_SWIM |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | HURT_LITE |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | RES_TELE | CTHANGBAND
+S:1_IN_9 |
+S:BLIND | CAUSE_2 |
+S:S_DEMON | HEAL
+D:"Something black in the road, something that wasn't a tree.
+D:Something big and black and ropy, just squatting there, waiting,
+D:with ropy arms squirming and reaching... It came crawling up the
+D:hillside... and it was the black thing of my dreams... that black,
+D:ropy, slimy jelly tree-thing out of the woods. It crawled up and it
+D:flowed up on its hoofs and mouths and snaky arms."
+
+N:678:Colour out of space
+G:.:v
+I:120:12d100:20:100:10
+W:43:2:0:8000
+E:0:0:0:0:0:0
+O:35:35:10:10
+B:TOUCH:LOSE_ALL:7d6
+B:TOUCH:LOSE_ALL:7d6
+F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 |
+F:INVISIBLE | PASS_WALL | CAN_FLY |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | ATTR_MULTI | ATTR_ANY |
+F:CHAR_CLEAR | SMART | COLD_BLOOD | EVIL |
+F:IM_COLD | IM_FIRE | IM_ACID | IM_ELEC | IM_POIS |
+F:NO_STUN | NO_CONF | NO_SLEEP | CTHANGBAND | HAS_LITE | NO_CUT
+S:1_IN_9 |
+S:DRAIN_MANA | BR_DISE | BR_NETH | TPORT
+D:"The shaft of phosphorescence from the well... was no longer shining
+D:out, it was pouring out; and as the shapeless stream of unplaceable
+D:colour left the well it seemed to flow directly into the sky."
+
+N:679:Quaker, Master of Earth
+G:E:u
+I:110:28d100:10:97:90
+W:43:3:0:4500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:SHATTER:10d10
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:EMPTY_MIND | COLD_BLOOD | KILL_WALL |
+F:KILL_ITEM | KILL_BODY | PASS_WALL | POWERFUL |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT | NO_STUN
+S:1_IN_6 |
+S:BO_ACID | BA_ACID
+D:A towering stone elemental stands before you. The walls and ceiling are
+D:reduced to rubble as Quaker advances.
+
+N:680:Death leprechaun
+G:h:D
+I:120:6d6:8:13:8
+W:44:6:0:85
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:TOUCH:EXP_40:1d10
+B:TOUCH:EAT_GOLD
+B:TOUCH:EAT_ITEM
+F:INVISIBLE | RAND_25 | TAKE_ITEM | COLD_BLOOD | SMART |
+F:HURT_LITE | EVIL | OPEN_DOOR | MALE | UNDEAD | RES_NETH | ZANGBAND | NO_CUT
+S:MULTIPLY |
+S:1_IN_6 |
+S:BLINK | TPORT | TELE_TO | CAUSE_3 | ANIM_DEAD
+D:Nasty undead little creatures. They are chanting the name of the
+D:sinister wizard who created them: 'Bruce! Bruce..!'
+
+N:681:Chaugnar Faugn, Horror from the Hills
+G:q:D
+I:110:20d100:10:125:90
+W:44:4:0:6250
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:10d10
+B:CRUSH:HURT:10d10
+B:BITE:LOSE_CON:8d2
+B:BITE:LOSE_CON:8d1
+F:UNIQUE | CAN_SPEAK | EVIL | DEMON |
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE |
+F:ELDRITCH_HORROR | POWERFUL |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_ROCK |
+F:NO_CONF | NO_FEAR | CTHANGBAND
+S:1_IN_6 |
+S:HOLD | CAUSE_4 | CONF | SCARE | FORGET | BRAIN_SMASH |
+S:MIND_BLAST | CAUSE_3 | S_DEMON | S_MONSTERS
+D:"The ears were webbed and tentacled, the trunk terminated in
+D:a huge flaring disk at least a foot in diameter... Its forelimbs
+D:were bent stiffly at the elbow, and its hands... rested palms
+D:upward on its lap."
+
+N:682:Lloigor
+G:v:B
+I:120:100d15:20:100:20
+W:44:2:0:6000
+E:0:0:0:0:0:0
+O:90:0:10:0
+B:ENGULF:LOSE_CON:4d10
+B:ENGULF:LOSE_CON:4d10
+B:ENGULF:LOSE_CON:4d10
+F:FORCE_SLEEP | ELDRITCH_HORROR | PASS_WALL |
+F:INVISIBLE | DROP_2D2 | DROP_4D2 | CAN_FLY |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | POWERFUL |
+F:SMART | IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS |
+F:MORTAL | CTHANGBAND | HAS_LITE | NO_CUT
+S:1_IN_4 |
+S:BR_WALL | TELE_AWAY | BR_GRAV |
+S:S_KIN | MIND_BLAST | BLIND | BLINK
+D:"Invisible ones from the stars... They sometimes took forms...
+D:but existed as vortices of power in their natural state."
+
+N:683:Utgard-Loke
+G:P:v
+I:120:40d100:30:125:15
+W:44:3:0:13500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:COLD:12d12
+B:HIT:COLD:12d12
+B:HIT:COLD:12d12
+B:HIT:COLD:12d12
+F:ESCORT | UNIQUE | IM_COLD | AURA_COLD | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | MOVE_BODY |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | MALE | ZANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | TELE_TO |
+S:S_KIN | BR_COLD
+D:A frost giant chieftain, unusually smart for a giant.
+
+N:684:Quachil Uttaus, Treader of the Dust
+G:z:D
+I:120:50d66:30:80:15
+W:44:3:0:20000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:CLAW:HURT:50d1
+B:TOUCH:TIME:1d50
+B:TOUCH:TIME:1d50
+F:ESCORT | UNIQUE | IM_COLD | ELDRITCH_HORROR | RES_NETH |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | KILL_BODY | DROP_GREAT |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | NO_SLEEP | NO_CONF
+F:EVIL | UNDEAD | DEMON | REGENERATE | CTHANGBAND | NO_CUT
+S:1_IN_3 |
+S:BR_TIME | SLOW | HASTE | HEAL
+D:"It was a figure no larger than a young child, but severe and
+D:shriveled as some millenial mummy. Its hairless head, its
+D:unfeatured face, borne on a neck of skeleton thinness, were
+D:lined with a thousand reticulated wrinkles. The body was like
+D:that of some monstrous, withered abortion that had never drawn
+D:breath. The pipelike arms, ending in bony claws, were outhrust as if
+D:enclosed in a posture of an eternal dreadful grasping."
+
+N:685:Shoggoth
+G:j:D
+I:140:50d20:20:80:20
+W:44:2:0:2500
+E:0:0:0:0:0:0
+O:30:70:0:0
+B:CRUSH:ACID:5d6
+B:CRUSH:ACID:5d6
+B:CRUSH:ACID:5d6
+B:CRUSH:HURT:9d9
+F:REGENERATE | ONLY_ITEM | KILL_ITEM | DROP_2D2 | DROP_90 | DROP_60
+F:BASH_DOOR | EVIL | NO_CONF | NO_SLEEP | KILL_BODY | CAN_SWIM |
+F:FORCE_MAXHP | FORCE_SLEEP | HURT_LITE | POWERFUL |
+F:IM_ACID | IM_FIRE | RES_PLAS | IM_POIS | IM_COLD | IM_ELEC | RES_TELE
+F:CTHANGBAND | HAS_LITE | NO_CUT
+D:"The nightmare, plastic column of fetid, black iridescence oozed
+D:tightly onward... A shapeless congerie of protoplasmic bubbles,
+D:faintly self-luminous and with myriads of temporary eyes forming
+D:and unforming as pustules of greenish light all over the
+D:tunnel-filling front that bore down upon us, crushing the frantic
+D:penguins and slithering over glistening floor that it and its
+D:kind had swept so evilly free of all litter. Still came that eldritch
+D:mocking cry -- 'Tekeli-li! Tekeli-li!'"
+
+N:686:Judge Death
+G:W:D
+I:120:45d50:90:90:10
+W:43:3:0:14000
+E:1:1:1:2:1:1
+O:0:20:80:0
+B:CLAW:POISON:10d5
+B:CLAW:POISON:10d5
+B:CLAW:EXP_40:10d1
+B:GAZE:TERRIFY
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_ELEC | IM_COLD | SUSCEP_FIRE |
+F:IM_POIS | NO_CONF | NO_SLEEP | JOKEANGBAND | NO_CUT | NO_STUN
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_4 | BA_FIRE | BA_NETH | ANIM_DEAD
+S:S_MONSTERS | S_UNDEAD | S_HI_UNDEAD | DRAIN_MANA |
+D:The most powerful Dark Judge, whose touch means death.
+
+N:687:Ariel, Queen of Air
+G:E:B
+I:130:27d100:12:50:50
+W:43:3:0:4750
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:4d6
+B:HIT:CONFUSE:4d4
+B:HIT:HURT:4d6
+B:HIT:CONFUSE:4d4
+F:UNIQUE | FEMALE | AURA_ELEC | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:RAND_25 | CAN_FLY |
+F:EMPTY_MIND | COLD_BLOOD |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BO_ELEC | BA_COLD | BA_ELEC
+D:A towering air elemental, Ariel the sorceress avoids your blows
+D:with her extreme speed.
+
+N:688:11-headed hydra
+G:M:R
+I:120:100d18:20:100:20
+W:44:2:8500:6000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:3d12
+B:BITE:FIRE:3d12
+B:BITE:FIRE:3d12
+B:BITE:FIRE:3d12
+F:FORCE_SLEEP | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:ONLY_GOLD | DROP_2D2 | DROP_4D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:ANIMAL | IM_FIRE | CAN_SWIM |
+F:MORTAL | BASEANGBAND
+S:1_IN_4 |
+S:SCARE | BO_FIRE | BO_PLAS | BA_FIRE | BR_FIRE
+D:A strange reptilian hybrid with eleven smouldering heads.
+
+N:689:Patriarch
+G:p:G
+I:120:52d10:20:60:10
+W:40:2:1800:1800
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d5
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | BLIND | HOLD | CAUSE_4 | CAUSE_3 | ANIM_DEAD |
+S:S_MONSTERS | S_UNDEAD
+D:An evil priest, dressed all in black. Deadly spells hit you at an
+D:alarming rate as his black spiked mace rains down blow after blow on your
+D:pitiful frame.
+
+N:690:Dreadmaster
+G:G:y
+I:120:12d100:20:100:10
+W:44:2:1000:8000
+E:0:0:0:0:0:0
+O:10:40:25:10
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:LOSE_STR:3d4
+B:HIT:LOSE_STR:3d4
+F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | CAN_FLY |
+F:SMART | TAKE_ITEM | INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_9 |
+S:TELE_LEVEL | BLIND | HOLD | CONF | CAUSE_4 | DRAIN_MANA | BO_NETH |
+S:S_UNDEAD
+D:It is an unlife of power almost unequalled. An affront to existence, its
+D:very touch abuses and disrupts the flow of life, and its unearthly limbs,
+D:of purest black, crush rock and flesh with ease.
+
+N:691:Drolem
+G:g:g
+I:120:30d100:25:130:30
+W:44:3:40000:12000
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d10
+B:CLAW:HURT:3d10
+B:BITE:POISON:5d10
+B:BITE:POISON:5d10
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE |
+F:EMPTY_MIND | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:DRAGON | IM_FIRE | IM_COLD |
+F:IM_ELEC | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+F:ATTR_MULTI
+S:1_IN_6 |
+S:BLIND | SLOW | CONF | ARROW_3 |
+S:BR_POIS
+D:A constructed dragon, the drolem has massive strength. Powerful spells
+D:weaved during its creation make it a fearsome adversary. Its eyes show
+D:little intelligence, but it has been instructed to destroy all it meets.
+
+N:692:Scatha the Worm
+G:D:W
+I:120:22d100:30:130:70
+W:46:2:210000:17000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d10
+B:CLAW:HURT:4d10
+B:BITE:COLD:4d14
+B:BITE:COLD:4d14
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | SUSCEP_FIRE |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_CORPSE
+F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY |
+F:EVIL | DRAGON | IM_COLD | NO_CONF | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:CONF | CAUSE_3 |
+S:BR_COLD
+D:An ancient and wise dragon, Scatha has grown clever over the long years.
+D:His scales are covered with frost, and his breath sends a shower of ice
+D:into the air.
+
+N:693:Warrior of the Dawn
+G:p:R
+I:120:25d25:20:70:20
+W:45:2:1600:500
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:IM_POIS | IM_FIRE | IM_ELEC | IM_ACID | IM_COLD |
+F:NO_SLEEP | NO_FEAR |
+F:FRIENDS |
+F:MALE | OPEN_DOOR | BASH_DOOR | ZANGBAND | HAS_LITE
+D:Fierce, barbaric warriors, armed with great spiked clubs, and surrounded
+D:in an aura of scarlet. Whenever one of them is slain, another appears
+D:out of nowhere to take his place.
+
+N:694:Lesser black reaver
+G:L:D
+I:120:25d100:20:120:50
+W:45:3:2600:12000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:HIT:UN_BONUS:4d8
+B:HIT:UN_BONUS:4d8
+B:HIT:LOSE_STR:4d6
+B:HIT:LOSE_STR:4d6
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | CAN_SWIM |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | RES_TELE
+F:NO_CONF | NO_SLEEP | KILL_WALL | NO_FEAR
+F:MORTAL | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:TELE_TO | BLIND | HOLD | CONF | CAUSE_3 | DRAIN_MANA |
+S:MIND_BLAST | BA_NETH | ANIM_DEAD
+D:A humanoid form, black as night, advancing steadily and unstoppably.
+
+N:695:Zoth-Ommog
+G:R:v
+I:120:25d100:12:50:50
+W:45:4:0:18000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:HURT:25d3
+B:CRUSH:HURT:25d3
+B:BITE:LOSE_DEX:2d10
+B:BITE:LOSE_CON:2d10
+F:UNIQUE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:RAND_25 | CAN_SWIM | ELDRITCH_HORROR |
+F:KILL_ITEM | BASH_DOOR | POWERFUL |
+F:EVIL | IM_ACID | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_FEAR | CTHANGBAND
+S:1_IN_5 |
+S:S_MONSTER | SCARE | CAUSE_4 | S_HYDRA | S_SPIDER |
+S:BA_NETH | BA_POIS | BA_ACID | CAUSE_3 | HOLD | MIND_BLAST
+D:"A body shaped like a road-based, truncated cone. A flat, blunt,
+D:wedge-shaped, vaguely reptilian head surmounts this conical torso,
+D:and the head is almost entirely hidden behind swirling tresses.
+D:This hair, or beard and mane, consists of thickly carved and
+D:coiling ropes, like serpents or worms... Through this repulsive
+D:Medusa-mane of ropy tendrils, two fierce, serpent-like eyes
+D:glare in a horrible intermingling of cold, inhuman mockery and
+D:what I can only describe as gloating menace."
+
+N:696:Grand master thief
+G:p:b
+I:130:15d100:50:75:40
+W:46:2:0:1500
+E:1:1:1:2:1:1
+O:70:10:10:10
+B:HIT:EAT_ITEM:5d5
+B:HIT:EAT_ITEM:5d5
+B:HIT:EAT_GOLD:5d5
+B:HIT:EAT_GOLD:5d5
+F:MALE | DROP_2D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TPORT | TELE_TO | CONF | TRAPS | ARROW_2
+D:A class of its own: you are already too late to protect your possessions -
+D:and he seems to have studied magic too, and is a master of setting traps.
+
+N:697:Smaug the Golden
+G:D:R
+I:120:24d100:30:150:80
+W:48:2:230000:23000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:FIRE:5d14
+B:BITE:FIRE:5d14
+F:UNIQUE | MALE | REFLECTING | CAN_FLY | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:SMART | EVIL | DRAGON | IM_FIRE | BASEANGBAND
+F:HAS_LITE
+S:1_IN_4 |
+S:CONF | CAUSE_3 |
+S:BR_FIRE
+D:Smaug is one of the Uruloki that still survive, a fire-drake of immense
+D:cunning and intelligence. His speed through air is matched by few other
+D:dragons, his dragonfire is legendary, and his hide is
+D:armoured with diamonds. He is believed to be the greatest dragon still
+D:surviving into the Third Age.
+
+N:698:The Stormbringer
+G:|:D
+I:120:13d123:20:99:20
+W:45:2:0:13333
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:WAIL:TERRIFY
+B:HIT:EXP_80:64d1
+B:HIT:EXP_80:64d1
+B:HIT:EXP_80:8d8
+F:CHAR_MULTI | EVIL | IM_POIS | IM_COLD | IM_FIRE | RES_NETH |
+F:FORCE_SLEEP | UNIQUE | FORCE_MAXHP | CAN_FLY |
+F:COLD_BLOOD | BASH_DOOR | NONLIVING |
+F:NO_CONF | NO_SLEEP | NO_FEAR | ZANGBAND | HAS_LITE | NO_CUT | NO_STUN
+D:The mightiest of hellblades, a black runesword which thirsts for
+D:your soul.
+
+N:699:Knight Templar
+G:p:w
+I:120:60d20:20:60:10
+W:44:2:1800:2000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:IM_POIS | IM_FIRE | IM_ELEC | IM_ACID | IM_COLD | GOOD |
+F:RES_NETH | RES_NEXU | RES_DISE | RES_TELE | DROP_SKELETON | DROP_CORPSE
+F:NO_SLEEP | NO_CONF | NO_FEAR | NO_STUN |
+F:DROP_2D2 | DROP_90 | REFLECTING |
+F:MALE | OPEN_DOOR | BASH_DOOR | FORCE_MAXHP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:HEAL | CAUSE_3 | CAUSE_4 | HASTE | SCARE | BLIND | S_ANGEL
+D:It seems that the more devout the person, the more likely they are to cross
+D:the boundary between piety and sanctimoniousness. And such is the case with
+D:the Order of the Knights Templar; they are among the most pious and
+D:powerful of the religious knightly orders, but noted for their intolerance.
+D:Thus it is Morgoth's will that is unwittingly done, as the forces of good
+D:are set against each other.
+
+N:700:Leprechaun fanatic
+G:h:r
+I:123:6d6:8:13:8
+W:46:6:800:80
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:EXPLODE:HURT:20d3
+F:RAND_25 | TAKE_ITEM | SMART |
+F:EVIL | OPEN_DOOR | MALE |
+F:MORTAL | ZANGBAND
+S:MULTIPLY |
+S:1_IN_6 |
+S:BLINK | TPORT | TELE_TO
+D:These leprechauns are not afraid to die for their cause. They are
+D:carrying explosives and making terrorist strikes...
+
+N:701:Dracolich
+G:D:G
+I:120:35d100:25:120:80
+W:55:2:180000:18000
+E:0:1:0:6:1:0
+O:10:50:40:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:EXP_80:7d14
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | RES_TELE |
+F:COLD_BLOOD | CAN_FLY |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT | ATTR_MULTI
+S:1_IN_6 |
+S:CONF | SCARE |
+S:BR_COLD | BR_NETH
+D:The skeletal form of a once-great dragon, enchanted by magic most
+D:perilous. Its animated form strikes with speed and drains life from its
+D:prey to satisfy its hunger.
+
+N:702:Greater titan
+G:P:o
+I:120:38d100:30:125:15
+W:66:3:50000:23500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:CONFUSE:12d12
+B:HIT:CONFUSE:12d12
+B:HIT:CONFUSE:12d12
+B:HIT:CONFUSE:12d12
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_4D2 | DROP_1D2 | DROP_GOOD | MOVE_BODY |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | MALE | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | TELE_TO |
+S:S_MONSTERS
+D:A forty foot tall humanoid that shakes the ground as it walks. The power
+D:radiating from its frame shakes your courage, its hatred inspired by your
+D:defiance.
+
+N:703:Dracolisk
+G:D:R
+I:120:35d100:25:120:80
+W:55:2:180000:18000
+E:0:1:0:6:1:0
+O:0:50:40:10
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:FIRE:7d14
+B:GAZE:PARALYZE
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | RES_NEXU | RES_TELE |
+F:ANIMAL | EVIL | DRAGON | IM_ACID | IM_FIRE | NO_CONF | NO_SLEEP |
+F:BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:HOLD | SCARE |
+S:BR_FIRE | BR_NEXU
+D:A mixture of dragon and basilisk, the dracolisk stares at you with deep
+D:piercing eyes, its evil breath burning the ground where it stands.
+
+N:704:Winged Horror
+G:B:D
+I:120:25d80:30:80:5
+W:48:3:4500:4000
+B:CLAW:HURT:3d8
+B:CLAW:HURT:3d8
+B:BITE:EXP_40:4d6
+B:BITE:EXP_40:4d6
+F:ANIMAL | MORTAL | EVIL | CAN_FLY | BASH_DOOR | IM_COLD | IM_POIS
+F:WILD_TOO | WILD_WASTE | WILD_WOOD | WILD_SWAMP | BASEANGBAND
+S:1_IN_6
+S:BR_NETH | BR_DARK | BR_POIS
+D:A terrifying sight: a winged creature greater than any bird you have ever
+D:seen, and with no feathers on its horrid black leathery wings. Descended
+D:from a creature of an older world perhaps, bred by Sauron to be a winged
+D:steed for his Ringwraiths.
+
+N:705:Spectral tyrannosaur
+G:R:G
+I:120:70d50:25:120:30
+W:46:3:0:15000
+E:0:0:0:0:0:0
+O:20:20:20:20
+B:BITE:EXP_40:2d13
+B:BITE:EXP_40:2d13
+B:BITE:LOSE_STR:5d8
+B:GAZE:TERRIFY
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:EVIL | UNDEAD | ANIMAL | RES_NEXU | RES_TELE
+F:NO_CONF | NO_FEAR | NO_SLEEP |
+F:IM_POIS | IM_ACID | IM_COLD |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | ZANGBAND | NO_CUT
+S:1_IN_6 |
+S:HOLD | SCARE |
+S:BR_NEXU | BR_POIS | BR_NETH
+D:A deadly undead horror which looks like a skeletal tyrannosaur
+D:surrounded by a sickly green glow.
+
+N:706:Yibb-Tstll, the Patient One
+G:P:D
+I:120:99d21:20:100:20
+W:46:2:0:16000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:LOSE_ALL:1d166
+B:TOUCH:LOSE_ALL:1d166
+F:UNIQUE | FORCE_SLEEP | FORCE_MAXHP |
+F:ELDRITCH_HORROR | NEVER_MOVE | REGENERATE | POWERFUL |
+F:DROP_2D2 | DROP_4D2 | DROP_GOOD | ONLY_ITEM |
+F:SMART | IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS | CTHANGBAND
+S:1_IN_4 |
+S:BR_NUKE | S_DEMON | DARKNESS | S_UNDEAD | ANIM_DEAD
+S:BR_NEXU | BR_CHAO
+D:"There, about the pulsating body of the Ancient One, hugely
+D:winged reptilian creatures without faces cluttered and clutched
+D:at a multitude of blackly writhing, pendulous breasts! Its eyes
+D:moved quickly, independently -- sliding with vile viscosity over
+D:the whole rotten surface of Yib-Tstll's pulpy, glistening head!"
+
+N:707:Ghatanothoa
+G:v:D
+I:120:100d20:20:100:20
+W:46:2:0:16000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:CLAW:LOSE_INT:5d10
+B:CLAW:LOSE_WIS:5d10
+B:BITE:CONFUSE:5d10
+F:FORCE_SLEEP | ELDRITCH_HORROR | PASS_WALL | UNIQUE |
+F:INVISIBLE | DROP_2D2 | DROP_4D2 | CAN_FLY | DROP_GOOD | ONLY_ITEM |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | POWERFUL | NO_CONF | NO_SLEEP |
+F:SMART | IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS |
+F:MORTAL | CTHANGBAND | NO_CUT
+S:1_IN_4 |
+S:BR_WALL | TELE_AWAY | BR_GRAV | TELE_LEVEL | S_DEMON | S_UNDEAD | S_KIN |
+S:BRAIN_SMASH | HASTE | BLIND | BLINK | BR_INER | CAUSE_3 | CAUSE_4
+D:The chief among the creatures known as Lloigor. Ghatanothoa, unlike most of
+D:his kind, has assumed a shape, and one which is too horrible to describe:
+D:"Nothing I could say could even adumbrate the loathsome, unholy, non-human,
+D:extra-galactic horror and hatefulness and unutterable evil of that forbidden
+D:spawn of black chaos, and illimitable night."
+
+N:708:Ent
+G:#:G
+I:110:40d100:30:120:15
+W:46:3:0:12500
+E:1:1:1:2:1:1
+O:30:50:20:0
+B:CRUSH:HURT:12d13
+B:CRUSH:HURT:12d13
+B:CRUSH:HURT:12d13
+B:CRUSH:HURT:12d13
+F:FORCE_SLEEP | FORCE_MAXHP | PET | SUSCEP_FIRE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | MOVE_BODY |
+F:SMART | TAKE_ITEM | KILL_WALL | BASH_DOOR |
+F:GOOD | BASEANGBAND | NO_CUT
+D:A tree-herd: a sentient, moving tree. Its wrath is fearsome, and it could
+D:split stones and even the very rock of Isengard with its roots.
+
+N:709:Hru
+G:P:s
+I:120:40d100:30:150:15
+W:54:3:40000:14500
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:SHATTER:12d13
+B:HIT:SHATTER:12d13
+B:HIT:SHATTER:12d13
+B:HIT:SHATTER:12d13
+F:FORCE_SLEEP | FORCE_MAXHP | HURT_ROCK |
+F:ONLY_GOLD | DROP_4D2 | MOVE_BODY | KILL_WALL |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | GIANT | MALE | HAS_LITE | NO_CUT
+D:A rock giant, a being made of living stone.
+
+N:710:Itangast the Fire Drake
+G:D:R
+I:120:22d100:30:120:70
+W:47:2:220000:20000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d10
+B:CLAW:HURT:4d10
+B:BITE:FIRE:4d14
+B:BITE:FIRE:4d14
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | SUSCEP_COLD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | IM_FIRE | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:CONF | CAUSE_3 |
+S:BR_FIRE
+D:A mighty ancient dragon, Itangast's form scorches your flesh. Wisps of
+D:smoke curl up from his nostrils as he regards you with disdain.
+
+N:711:Death mold
+G:m:D
+I:140:100d20:200:60:0
+W:47:1:100:1000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:UN_BONUS:7d7
+B:SPORE:UN_BONUS:7d7
+B:SPORE:UN_BONUS:7d7
+B:SPORE:EXP_80:5d5
+F:FORCE_SLEEP | NEVER_MOVE | CAN_SWIM |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is the epitome of all that is evil, in a mold. Its lifeless form draws
+D:power from sucking the souls of those that approach it; a nimbus of pure
+D:evil surrounds it. Luckily for you, it can't move...
+
+N:712:Fafner the Dragon
+G:D:G
+I:120:25d110:30:130:80
+W:48:2:170000:25000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:FIRE:5d14
+B:BITE:POISON:5d14
+F:UNIQUE | MALE | SUSCEP_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | DROP_CORPSE |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | INVISIBLE |
+F:EVIL | DRAGON | IM_FIRE | IM_POIS | ZANGBAND |
+F:HAS_LITE
+S:1_IN_3 |
+S:CONF | CAUSE_3 |
+S:BR_FIRE | BR_POIS | SCARE | CAUSE_3 | CONF
+D:The mighty dragon of myth, Fafner was a giant who slew his
+D:brother to win a treasure hoard, and then transformed himself
+D:into a dragon, greedily watching over his gold.
+
+N:713:Charon, Boatman of the Styx
+G:W:B
+I:120:28d100:30:100:10
+W:47:3:0:25000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:WAIL:LOSE_ALL
+B:TOUCH:UN_POWER
+B:HIT:EXP_80:8d12
+B:HIT:EAT_GOLD:8d12
+F:UNIQUE | MALE | SMART | CAN_SWIM |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_GOLD | DROP_3D2 | DROP_4D2 |
+F:COLD_BLOOD | PASS_WALL | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_STUN | NO_CONF | NO_SLEEP | RES_TELE | ZANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | DARKNESS |
+S:BO_ICEE | BO_NETH | BA_COLD | BA_NETH | BA_WATE |
+S:S_UNDEAD
+D:The ferryman across the river Styx to the land of the dead.
+D:He wishes to receive payment for your entry.
+
+N:714:Quickbeam, the Ent
+G:#:G
+I:120:40d100:30:120:15
+W:47:3:0:13500
+E:1:1:1:2:1:1
+O:10:50:20:10
+B:CRUSH:HURT:12d13
+B:CRUSH:HURT:12d13
+B:CRUSH:HURT:12d13
+B:CRUSH:HURT:12d13
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | CAN_SPEAK |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | MOVE_BODY | SUSCEP_FIRE |
+F:SMART | TAKE_ITEM | KILL_WALL | BASH_DOOR |
+F:GOOD | PET | BASEANGBAND | NO_CUT
+D:Unusually hasty, for an ent. Quickbeam hates evil creatures, and orcs in
+D:particular, since the destruction of his beloved rowan-trees to feed the
+D:fires of Orthanc.
+
+N:715:Glaurung, Father of the Dragons
+G:D:R
+I:130:120d100:20:125:70
+W:70:2:300000:50000
+E:0:1:0:6:1:0
+O:30:70:0:0
+B:CLAW:HURT:5d12
+B:CLAW:HURT:5d12
+B:BITE:FIRE:8d14
+B:BITE:POISON:8d14
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_RANDART
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:SMART | EVIL | DRAGON | IM_POIS | IM_FIRE |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BLIND | CONF | SCARE |
+S:BR_FIRE | BR_POIS | BR_SOUN | S_HI_DRAGON
+D:Glaurung is the father of all dragons, and was for a long time the most
+D:powerful. Though this is no longer so, he still has full command over
+D:his brood and can command them to appear whenever he so wishes. He is
+D:the definition of dragonfire.
+
+N:716:Behemoth
+G:H:B
+I:120:50d100:25:180:30
+W:49:3:6000:16000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:5d8
+B:BITE:FIRE:5d8
+B:CRUSH:HURT:3d15
+B:CRUSH:HURT:3d15
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SWIM | ANIMAL | AQUATIC |
+F:IM_FIRE | IM_ACID | IM_COLD | IM_POIS | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | BASEANGBAND
+S:1_IN_9
+S:BR_FIRE
+D:A great water-beast, with an almost impenetrable skin.
+
+N:717:Garm, Guardian of Hel
+G:C:b
+I:120:30d100:20:120:70
+W:49:2:0:25000
+E:0:1:0:2:1:0
+O:50:50:0:0
+B:CLAW:HURT:7d13
+B:CLAW:HURT:7d13
+B:BITE:HURT:8d13
+B:BITE:HURT:8d13
+F:UNIQUE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | RES_NETH |
+F:ANIMAL | EVIL | IM_FIRE | NO_SLEEP | ZANGBAND
+S:1_IN_3 |
+S:BR_NETH | BR_COLD | BR_DARK |
+S:S_HOUND | S_UNDEAD
+D:Garm is a gigantic hound, whose job is to guard that none escapes
+D:the tortures of Hel, the place of punishment for the wicked
+D:and cowardly dead.
+
+N:718:Greater wall monster
+G:#:W
+I:120:15d40:20:80:20
+W:44:4:0:900
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:FORCE_SLEEP | COLD_BLOOD | EMPTY_MIND | PASS_WALL | RAND_50 |
+F:BASH_DOOR | IM_COLD | IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | NONLIVING |
+F:HURT_ROCK | NO_CONF | NO_SLEEP | CHAR_MULTI | CAN_FLY | BASEANGBAND
+F:NO_CUT
+S:MULTIPLY |
+D:A sentient, moving section of wall.
+
+N:719:Nycadaemon
+G:U:o
+I:120:29d99:20:80:80
+W:51:3:0:10000
+E:1:1:1:2:1:1
+O:50:0:50:0
+B:TOUCH:TERRIFY
+B:CLAW:HURT:6d6
+B:CLAW:HURT:6d6
+B:CLAW:HURT:6d6
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | NONLIVING |
+F:REGENERATE | IM_ACID | IM_COLD | IM_FIRE | CAN_FLY |
+F:NO_SLEEP | NO_STUN | NO_CONF | EVIL | DEMON |
+F:INVISIBLE | KILL_WALL |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | BASEANGBAND
+S:1_IN_4 |
+S:HOLD | BLINK | CONF | S_DEMON | BRAIN_SMASH | BR_NETH
+D:A loathsome, sturdy, winged, horned demon, with talons that could tear
+D:a stone wall down.
+
+N:720:Barbazu
+G:U:G
+I:120:120d10:25:60:80
+W:55:2:0:3000
+E:1:1:1:2:1:1
+O:20:40:20:20
+B:HIT:HURT:4d10
+B:HIT:HURT:4d10
+B:HIT:LOSE_CON:10d2
+B:STING:POISON:5d5
+F:FORCE_SLEEP | FORCE_MAXHP | FRIENDS |
+F:ONLY_ITEM | DROP_1D2 | NONLIVING | BASEANGBAND |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | IM_POIS | NO_CONF | NO_SLEEP
+S:1_IN_7 |
+S:SCARE | S_DEMON
+D:A foul, humanoid creature with a long tail, clawed hands and feet,
+D:and a disgusting, wiry, snaky beard. They are the elite shock troops
+D:of the hells, capable of a terrifying berserk fury.
+
+N:721:Goat of Mendes
+G:q:D
+I:120:18d111:30:66:40
+W:50:3:0:6666
+E:0:1:0:2:1:0
+O:0:0:100:0
+B:GAZE:TERRIFY
+B:BUTT:HURT:6d6
+B:BITE:EXP_40
+B:BITE:LOSE_CON
+F:EVIL | DEMON | FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | SMART | NONLIVING |
+F:NO_CONF | NO_SLEEP | HURT_LITE | IM_FIRE | IM_COLD | ZANGBAND
+S:1_IN_4 |
+S:BLIND | CONF | BRAIN_SMASH | SCARE | ANIM_DEAD
+S:BA_NETH | FORGET | S_UNDEAD | DRAIN_MANA |
+S:S_DEMON | CAUSE_4 | BA_COLD
+D:It is a demonic creature from the lowest hell, vaguely resembling a
+D:large black he-goat.
+
+N:722:Nightwing
+G:W:D
+I:120:60d60:20:120:10
+W:61:4:0:10000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:TOUCH:POISON:6d5
+B:TOUCH:POISON:6d5
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_BONUS:6d8
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | RES_TELE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:BLIND | SCARE | CAUSE_4 | BRAIN_SMASH |
+S:BO_MANA | BO_NETH | BA_NETH | S_UNDEAD
+D:Everywhere colours seem paler and the air chiller. At the centre of the
+D:cold stands a mighty figure. Its wings envelop you in the chill of death
+D:as the nightwing reaches out to draw you into oblivion. Your muscles sag
+D:and your mind loses all will to fight as you stand in awe of this mighty
+D:being.
+
+N:723:Maulotaur
+G:H:s
+I:130:250d13:13:50:10
+W:50:2:40000:4500
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:SHATTER:8d8
+B:HIT:SHATTER:8d8
+B:BUTT:HURT:4d6
+B:BUTT:HURT:4d6
+F:ONLY_ITEM | DROP_60 | DROP_GOOD | RES_TELE |
+F:BASH_DOOR | STUPID | DROP_CORPSE |
+F:EVIL | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BO_PLAS | BA_FIRE
+D:It is a belligerent minotaur with a destructive magical arsenal, armed
+D:with a hammer.
+
+N:724:Nether hound
+G:Z:G
+I:120:60d10:30:100:0
+W:51:2:800:5000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d12
+B:CLAW:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+F:FORCE_SLEEP |
+F:FRIENDS | RES_NETH | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BR_NETH
+D:You feel a soul-tearing chill upon viewing this beast, a ghostly form of
+D:darkness in the shape of a large dog.
+
+N:725:Time hound
+G:Z:B
+I:130:60d10:30:100:0
+W:51:2:800:5000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d12
+B:CLAW:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+F:FORCE_SLEEP |
+F:FRIENDS |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_TIME
+D:You get a terrible sense of deja vu, or is it a premonition? All at once
+D:you see a little puppy and a toothless old dog. Perhaps you should give
+D:up and go to bed.
+
+N:726:Plasma hound
+G:Z:R
+I:120:60d10:30:100:0
+W:51:2:800:5000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d12
+B:CLAW:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+F:FORCE_SLEEP | SUSCEP_COLD |
+F:FRIENDS | RES_PLAS |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | IM_FIRE | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_5 |
+S:BR_PLAS
+D:The very air warps as pure elemental energy stalks towards you in the
+D:shape of a giant hound. Your hair stands on end and your palms itch as
+D:you sense trouble.
+
+N:727:Demonic quylthulg
+G:Q:r
+I:120:48d10:20:1:0
+W:45:1:3000:3000
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TPORT |
+S:S_DEMON
+D:A pile of pulsing flesh that glows with an inner hellish fire. The world
+D:itself seems to cry out against it.
+
+N:728:Great Storm Wyrm
+G:D:b
+I:120:40d100:30:150:80
+W:63:2:190000:20000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:ELEC:6d14
+B:BITE:ELEC:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE |
+F:EVIL | DRAGON | IM_ELEC | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_ELEC
+D:A vast dragon of power. Storms and lightning crash around its titanic
+D:form. Deep blue scales reflect the flashes and highlight the creature's
+D:great muscles. It regards you with contempt.
+
+N:729:Ulik the Troll
+G:T:v
+I:130:35d100:30:120:30
+W:51:4:16000:18000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:SHATTER:20d12
+B:HIT:SHATTER:20d12
+B:BITE:POISON:6d14
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | SPECIAL_GENE |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:DROP_90 | REGENERATE | KILL_WALL | RES_TELE | KILL_BODY |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:EVIL | TROLL | IM_POIS |
+F:IM_ELEC | IM_COLD | IM_FIRE | BASEANGBAND
+D:Ulik is the strongest troll who has ever lived. He could challenge
+D:the immortals and pound them to dust with his great strength.
+
+N:730:Baphomet the Minotaur Lord
+G:H:v
+I:130:35d100:30:120:30
+W:58:4:16000:18000
+E:1:1:1:2:1:1
+O:0:80:20:0
+B:BUTT:HURT:12d13
+B:BUTT:HURT:12d13
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | DROP_CORPSE |
+F:EVIL | IM_FIRE | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:1_IN_6 |
+S:SLOW | ARROW_4 | BO_MANA | BO_PLAS | BA_ELEC |
+S:BR_WALL
+D:A fearsome bull-headed monster, Baphomet swings a mighty axe as he curses
+D:all that defy him.
+
+N:731:Hell knight
+G:p:D
+I:120:15d100:20:100:10
+W:52:1:2200:10000
+E:1:1:1:2:1:1
+O:0:40:60:0
+B:HIT:HURT:10d5
+B:HIT:HURT:10d5
+B:HIT:EXP_80:10d5
+B:HIT:EXP_80:10d5
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | IM_FIRE | IM_COLD | IM_POIS |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | RES_NETH | RES_NEXU | RES_PLAS |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_COLD |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BLIND | SCARE | CAUSE_3 | BA_NETH | BA_FIRE | BO_PLAS
+S:S_MONSTERS | S_DEMON
+D:It is a humanoid form dressed in armour of ancient style. From beneath
+D:its helmet, eyes glow with hellfire.
+
+N:732:Bull Gates
+G:p:D
+I:140:25d100:40:90:0
+W:52:3:1600:20000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:CHARGE:EAT_GOLD:5d5
+B:CHARGE:EAT_ITEM:5d5
+B:SPIT:BLIND:10d5
+B:DROOL:DISEASE:8d5
+F:UNIQUE | MALE | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:IM_POIS | EVIL | RES_TELE
+F:MORTAL | JOKEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:TRAPS | S_BUG
+D:He may not code worth a dime, but he certainly knows how to make money.
+
+N:733:Santa Claus
+G:h:r
+I:150:25d100:90:100:10
+W:52:3:2600:45000
+E:1:1:1:2:1:1
+O:20:20:20:20
+B:HIT:LOSE_CHR:5d5
+B:TOUCH:LOSE_ALL:10d1
+B:TOUCH:LOSE_ALL:10d1
+B:CHARGE:EAT_GOLD
+F:UNIQUE | MALE | CAN_SPEAK | REFLECTING | DROP_SKELETON | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:NO_STUN | NO_SLEEP |
+F:IM_ELEC | IM_FIRE | IM_POIS | IM_COLD |
+F:COLD_BLOOD | RES_TELE | JOKEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:CAUSE_4 | HOLD | DRAIN_MANA | SCARE | BLIND |
+S:S_UNDEAD | S_HI_UNDEAD | S_HI_DRAGON | S_UNIQUE |
+S:BA_NETH | FORGET | TRAPS | BRAIN_SMASH | TELE_AWAY
+D:Why would anybody want to kill Santa Claus? To get all the presents,
+D:of course!
+
+N:734:Eihort, the Thing in the Labyrinth
+G:j:R
+I:120:33d100:50:90:10
+W:53:3:0:45000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:BITE:PARALYZE:8d1
+B:CRUSH:HURT:10d10
+B:CRUSH:HURT:10d10
+B:CRUSH:HURT:10d10
+F:UNIQUE | CAN_SPEAK | ESCORT | REGENERATE |
+F:FORCE_SLEEP | FORCE_MAXHP | ELDRITCH_HORROR |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_SLEEP | RES_TELE | CTHANGBAND | NO_CUT
+S:1_IN_4 |
+S:S_UNDEAD | S_DEMON | S_MONSTER | BLINK | BA_ACID | FORGET
+D:"Then came pale movement in the well, and something clambered
+D:up from the dark, a bloated blanched oval supported on myriad
+D:fleshless legs. Eyes formed in the gelatinous oval and stared
+D:at him."
+
+N:735:The King in Yellow
+G:L:y
+I:120:32d100:90:100:10
+W:53:3:0:50000
+E:0:0:0:0:0:0
+O:50:0:40:10
+B:CRUSH:HURT:12d12
+B:CRUSH:HURT:12d12
+B:GAZE:LOSE_WIS:5d10
+B:GAZE:TERRIFY:5d10
+F:UNIQUE | MALE | CAN_SPEAK | ATTR_MULTI | ATTR_ANY |
+F:FORCE_SLEEP | FORCE_MAXHP | CTHANGBAND |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_ACID | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE | NO_CUT
+S:1_IN_2 |
+S:S_DEMON | S_UNDEAD | S_KIN | SCARE | CAUSE_3 | CAUSE_4 | ANIM_DEAD
+S:TELE_AWAY | TPORT | BRAIN_SMASH | CONF
+D:The King in Yellow is an Avatar of Hastur: "He has no
+D:face, and is twice as tall as a man. He wears pointed shoes under
+D:his tattered, fantastically colored robes, and a streamer of silk
+D:appears to fall from the pointed tip of his hood. At times he appears
+D:to be winged; at others, haloed."
+
+N:736:Great unclean one
+G:U:g
+I:120:80d80:30:150:20
+W:53:3:0:17500
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:BITE:DISEASE:10d10
+B:BITE:ACID:10d10
+B:BITE:POISON:10d10
+B:BITE:CONFUSE:10d10
+F:DEMON | EVIL | NONLIVING | CAN_SWIM |
+F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_2D2 | ELDRITCH_HORROR |
+F:OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | DROP_GOOD | KILL_BODY |
+F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP
+F:KILL_ITEM | ZANGBAND
+S:1_IN_3
+S:BR_ACID | BR_POIS | BR_NUKE | SCARE | ANIM_DEAD
+S:CAUSE_3 | CAUSE_4 | S_DEMON | S_UNDEAD
+D:This disgusting demon resembles a shambling pile of rotting
+D:green flesh, with dozens of mouths drooling and leaving a
+D:trail of foul-smelling goo behind. Nurgle must be
+D:proud of himself to have created this atrocity!
+
+N:737:Lord of Chaos
+G:p:v
+I:130:45d55:30:80:5
+W:60:3:0:17500
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:KICK:HURT:20d2
+B:KICK:HURT:10d2
+B:HIT:POISON:20d1
+B:HIT:LOSE_ALL:15d1
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_SKELETON |
+F:ONLY_ITEM | DROP_4D2 | ATTR_MULTI | EVIL | SHAPECHANGER | ATTR_ANY |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | EVIL |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:ZANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | MIND_BLAST | BA_CHAO |
+S:S_SPIDER | S_HOUND | S_DEMON
+D:He is one of the few true masters of the art, being extremely skillful in
+D:all forms of unarmed combat and controlling the Chaos
+D:with disdainful ease.
+
+N:738:Old Sorcerer
+G:p:R
+I:130:52d25:20:60:10
+W:54:2:0:5000:0
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:6d8
+B:HIT:HURT:6d8
+B:HIT:HURT:6d8
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 |
+F:OPEN_DOOR | BASH_DOOR | TAKE_ITEM |
+F:EVIL | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:BLINK | TELE_TO | BLIND | CONF | CAUSE_3 | CAUSE_4 | TRAPS |
+S:BO_ACID | BA_FIRE | BA_COLD | BA_POIS |
+S:S_MONSTER | S_DEMON | S_HI_DRAGON | S_UNDEAD
+D:A human figure in robes, he moves with magically improved speed, and his
+D:hands are ablur with spell casting. You stagger at the mighty sound of his
+D:spells as they echo hollowly through the dungeon.
+
+N:739:Ethereal hound
+G:Z:G
+I:120:60d15:30:100:0
+W:55:3:900:6000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:CLAW:HURT:2d12
+F:FORCE_SLEEP | FRIENDS |
+F:INVISIBLE | PASS_WALL |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BR_NETH
+D:A pale green hound. Pulsing red lines and strange fluorescent light
+D:hint at internal organs best left to the imagination.
+
+N:740:Lesser kraken
+G:~:G
+I:120:30d100:30:150:80
+W:54:2:8000:20000
+E:3:0:3:0:1:0
+O:25:25:50:0
+B:CRUSH:HURT:16d12
+B:CRUSH:HURT:16d12
+B:CRUSH:HURT:16d12
+B:CRUSH:HURT:16d12
+F:FORCE_SLEEP | FORCE_MAXHP | AQUATIC | WILD_TOO | WILD_OCEAN |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | DROP_CORPSE |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | SMART | RES_WATE |
+F:EVIL | IM_ELEC | NO_CONF | NO_SLEEP | IM_POIS | IM_FIRE | BASEANGBAND
+S:1_IN_4 |
+S:BLIND | CONF | SCARE | CAUSE_4 | CAUSE_3 |
+S:BA_WATE | DARKNESS | BR_DARK | TELE_TO
+D:An enormously fearsome and powerful inhabitant of the depths. It
+D:resembles a gargantuan octopus and its evil is almost tangible.
+
+N:741:Great Ice Wyrm
+G:D:w
+I:120:40d100:30:150:80
+W:63:2:190000:20000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:COLD:6d14
+B:BITE:COLD:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | AURA_COLD |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | SUSCEP_FIRE |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE
+F:EVIL | DRAGON | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_COLD
+D:An immense dragon capable of awesome destruction. You have never felt
+D:such extreme cold, or witnessed such an icy stare. Begone quickly or feel
+D:its wrath!
+
+N:742:Demilich
+G:L:U
+I:120:35d100:20:100:50
+W:54:2:3000:12500
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:EXP_80
+B:TOUCH:UN_POWER
+B:TOUCH:LOSE_DEX:4d12
+B:TOUCH:LOSE_DEX:4d12
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_3 |
+S:BLINK | TELE_TO | BLIND | HOLD | CONF | SCARE | CAUSE_3 | CAUSE_4 |
+S:DRAIN_MANA | BRAIN_SMASH | S_HI_UNDEAD | S_UNDEAD | FORGET | S_DEMON |
+S:TPORT | HEAL | ANIM_DEAD
+D:A lich who is partially immaterial, on its way to a new, ethereal form.
+
+N:743:The Phoenix
+G:B:r
+I:120:36d100:60:130:0
+W:54:3:6000:40000
+E:0:1:1:0:1:0
+O:0:50:50:0
+B:BITE:FIRE:12d6
+B:BITE:FIRE:12d6
+B:HIT:FIRE:9d12
+B:HIT:FIRE:9d12
+F:UNIQUE | CAN_SPEAK | RES_PLAS | AURA_FIRE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | SUSCEP_COLD |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP
+F:BASEANGBAND | HAS_LITE | REGENERATE
+S:1_IN_3 |
+S:BO_FIRE | BO_PLAS | BA_FIRE |
+S:BR_FIRE | BR_LITE | BR_PLAS
+D:A massive glowing eagle bathed in flames. The searing heat chars your
+D:skin and melts your armour.
+
+N:744:Nightcrawler
+G:W:D
+I:120:80d60:20:160:10
+W:69:3:10000:1500
+E:0:0:0:0:1:0
+O:40:0:50:10
+B:STING:LOSE_CON:8d8
+B:STING:LOSE_CON:8d8
+B:BITE:ACID:10d10
+B:BITE:ACID:10d10
+F:FORCE_SLEEP | SMART | KILL_WALL | CAN_SWIM | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS | RES_TELE |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:BLIND | SCARE | BRAIN_SMASH |
+S:BO_MANA | BO_NETH | BA_NETH | BR_NETH | S_UNDEAD
+D:This intensely evil creature bears the form of a gargantuan black worm.
+D:Its gaping maw is a void of blackness, and acid drips from its steely hide.
+D:It is like nothing you have ever seen before, and a terrible chill runs
+D:down your spine as you face it.
+
+N:745:Lord of Change
+G:U:v
+I:130:50d70:30:150:20
+W:54:3:0:17000
+E:0:1:1:0:1:0
+O:0:0:100:0
+B:CLAW:CONFUSE:10d10
+B:CLAW:CONFUSE:10d10
+B:BITE:BLIND:12d12
+F:DEMON | EVIL | ATTR_MULTI | DROP_GOOD | MOVE_BODY | NONLIVING |
+F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_2D2 | ELDRITCH_HORROR |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | CAN_FLY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:ZANGBAND
+S:1_IN_3
+S:MIND_BLAST | BA_CHAO | SCARE | BRAIN_SMASH | DRAIN_MANA |
+S:S_HOUND | S_DEMON | S_DRAGON | TPORT | HASTE | CONF | SCARE |
+S:TELE_AWAY | FORGET
+D:The most powerful of Tzeentch's servitors. This demon looks like
+D:a huge bird-creature, with the head of a predatory bird and
+D:fantastically multi-coloured wings.
+
+N:746:Keeper of Secrets
+G:H:G
+I:130:60d70:30:150:20
+W:54:3:0:17000
+E:2:0:2:2:1:1
+O:0:0:100:0
+B:HIT:CONFUSE:10d10
+B:HIT:TERRIFY:10d10
+B:HIT:BLIND:10d10
+B:HIT:TERRIFY:10d10
+F:DEMON | EVIL | DROP_GOOD | NONLIVING | CAN_SWIM |
+F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_2D2 | ELDRITCH_HORROR |
+F:OPEN_DOOR | BASH_DOOR | SMART | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | ZANGBAND
+S:1_IN_3
+S:SCARE | BRAIN_SMASH | DRAIN_MANA |
+S:BR_CONF | S_DEMON | S_UNDEAD | TPORT | HEAL |
+S:TELE_AWAY
+D:This demonic keeper of forbidden secrets looks like a hairless
+D:minotaur with extra arms, decorated with tattoos and nose rings.
+D:It is the embodiment of Slaanesh's perverted magic.
+
+N:747:Shudde M'ell
+G:w:s
+I:125:100d40:20:90:20
+W:59:2:0:23000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CRUSH:SHATTER:55d2
+B:CRUSH:SHATTER:55d2
+B:TOUCH:LOSE_CON:1d2
+B:TOUCH:LOSE_CON:1d2
+F:IM_FIRE | RES_PLAS | IM_COLD | IM_POIS | ELDRITCH_HORROR | RES_TELE |
+F:KILL_WALL | ONLY_GOLD | DROP_4D2 | DROP_2D2 | CAN_SWIM |
+F:EVIL | FORCE_MAXHP | SMART | UNIQUE | CTHANGBAND | NO_STUN
+S:1_IN_5 |
+S:SCARE | CONF | HOLD | S_DEMON | S_KIN |
+S:HEAL | HASTE | FORGET | BRAIN_SMASH |
+S:BR_DARK | BR_ACID | BR_DISE
+D:The most powerful and most evil of all Chthonians.
+D:"A great gray thing a mile long chanting and exuding strange acids...
+D:charging through the depths of the earth at a fantastic speed, in a
+D:dreadful fury... melting basaltic rocks like butter under a blowtorch."
+
+N:748:Hand druj
+G:s:y
+I:130:60d10:20:110:10
+W:57:2:10:18000
+E:0:0:0:1:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | RES_TELE |
+F:SMART | COLD_BLOOD | CAN_SWIM |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_1 |
+S:TELE_AWAY | BLIND | CONF | SCARE | CAUSE_3 | FORGET | DARKNESS |
+S:BO_FIRE | BO_ACID | BO_COLD | BO_ELEC
+D:A skeletal hand floating in the air, motionless except for its flexing
+D:fingers.
+
+N:749:Eye druj
+G:s:r
+I:130:10d100:20:90:10
+W:58:2:2:21000
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW | RES_TELE |
+F:SMART | COLD_BLOOD |
+F:EVIL | UNDEAD | CAN_SWIM |
+F:IM_FIRE | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_1 |
+S:BO_MANA | BO_NETH | BA_NETH | BRAIN_SMASH | S_UNDEAD
+D:A bloodshot eyeball floating in the air, you'd be forgiven for assuming it
+D:harmless.
+
+N:750:Skull druj
+G:s:o
+I:130:14d100:20:120:10
+W:59:2:1000:24000
+E:0:0:0:0:1:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | RES_TELE |
+F:SMART | COLD_BLOOD |
+F:EVIL | UNDEAD | CAN_SWIM |
+F:IM_FIRE | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_1 |
+S:SLOW | CAUSE_4 | MIND_BLAST | BRAIN_SMASH | TRAPS | BO_PLAS |
+S:BO_NETH | BA_WATE | S_UNDEAD
+D:A glowing skull possessed by sorcerous power. It need not move, but
+D:merely blast you with mighty magic.
+
+N:751:Chaos vortex
+G:v:v
+I:140:32d20:100:80:0
+W:53:1:0:4000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:CONFUSE:5d5
+B:ENGULF:CONFUSE:5d5
+B:ENGULF:HURT:5d5
+B:ENGULF:HURT:5d5
+F:ATTR_MULTI | ATTR_ANY | FORCE_SLEEP |
+F:RAND_50 | RAND_25 | CAN_FLY |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_CHAO
+D:Void, nothingness, spinning destructively.
+
+N:752:Aether vortex
+G:v:v
+I:130:40d20:100:40:0
+W:54:2:0:5000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:ELEC:5d5
+B:ENGULF:FIRE:5d5
+B:ENGULF:ACID:5d5
+B:ENGULF:COLD:5d5
+F:ATTR_MULTI | ATTR_ANY | FORCE_SLEEP | CAN_FLY |
+F:RAND_50 | RAND_25 | AURA_COLD | RES_WATE | RES_DISE |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | RES_NETH | RES_NEXU |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | AURA_FIRE | AURA_ELEC |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | RES_PLAS | BASEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS | BR_LITE |
+S:BR_DARK | BR_SOUN | BR_CONF | BR_CHAO | BR_SHAR | BR_NETH |
+S:BR_WALL | BR_INER | BR_TIME | BR_GRAV | BR_PLAS | BR_NEXU
+D:An awesome vortex of pure magic, power radiates from its frame.
+
+N:753:Nidhogg, the Hel-Drake
+G:D:D
+I:120:39d111:20:133:70
+W:55:2:260000:25000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:LOSE_CON:8d12
+B:CLAW:LOSE_CON:8d12
+B:BITE:EXP_80:8d15
+B:BITE:EXP_80:8d15
+F:UNIQUE | CAN_FLY | RES_NETH |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | IM_POIS |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_SPEAK | IM_ACID |
+F:EVIL | DRAGON | IM_FIRE | NO_SLEEP | IM_COLD | ZANGBAND |
+F:HAS_LITE
+S:1_IN_5 |
+S:CAUSE_3 | BR_NETH | BR_COLD |
+S:BR_ACID | BR_POIS |
+S:S_DRAGON | S_UNDEAD
+D:In the bowels of Hel, the dread Nidhogg, a dragon blacker than the
+D:night, feasts on the essences of the dead.
+
+N:754:The Lernaean Hydra
+G:M:v
+I:120:45d100:20:140:20
+W:55:2:13000:20000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:POISON:8d6
+B:BITE:POISON:8d6
+B:BITE:FIRE:12d6
+B:BITE:FIRE:12d6
+F:UNIQUE | SUSCEP_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | DROP_CORPSE |
+F:ONLY_GOLD | DROP_3D2 | DROP_4D2 | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:OPEN_DOOR | BASH_DOOR | KILL_BODY | POWERFUL | REGENERATE |
+F:ANIMAL | IM_FIRE | IM_POIS |
+F:NO_CONF | NO_STUN | CAN_SWIM |
+F:MORTAL | BASEANGBAND
+S:1_IN_3 |
+S:SCARE |
+S:BO_FIRE | BO_PLAS | BA_FIRE | BA_POIS |
+S:BR_FIRE | BR_POIS | S_HYDRA | S_KIN
+D:A massive legendary hydra. It has twelve powerful heads. Its many eyes
+D:stare at you as clouds of smoke and poisonous vapour rise from its
+D:seething form. It grows new heads as fast as you chop them off.
+
+N:755:Thuringwethil, the Vampire Messenger
+G:V:v
+I:130:50d100:20:145:10
+W:67:3:1500:23000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:BITE:EXP_80:6d6
+B:BITE:EXP_80:6d6
+B:HIT:CONFUSE:6d6
+B:HIT:CONFUSE:6d6
+F:UNIQUE | FEMALE | SPECIAL_GENE |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SPEAK | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | NO_STUN
+F:RES_TELE | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | BA_NETH | S_KIN | S_HI_UNDEAD
+D:Chief messenger between Sauron and Morgoth, she is the most deadly of her
+D:vampire race on Middle-earth. At first she is charming to meet, but her
+D:wings and eyes give away her true form.
+
+N:756:Great Hell Wyrm
+G:D:r
+I:120:50d100:30:150:80
+W:67:2:190000:23000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:FIRE:6d14
+B:BITE:FIRE:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | SUSCEP_COLD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | AURA_FIRE |
+F:EVIL | DRAGON | IM_FIRE | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_FIRE
+D:A vast dragon of immense power. Fire leaps continuously from its huge
+D:form. The air around it scalds you. Its slightest glance burns you, and
+D:you realise how truly insignificant you are.
+
+N:757:Hastur the Unspeakable
+G:H:b
+I:120:55d95:20:150:10
+W:55:4:0:23000
+E:2:0:2:4:1:0
+O:25:25:25:10
+B:CRUSH:HURT:14d8
+B:CRUSH:HURT:14d8
+B:BITE:EXP_80:6d6
+B:BITE:EXP_80:6d6
+F:UNIQUE | ELDRITCH_HORROR | CAN_SWIM |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SPEAK | AURA_ELEC |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | POWERFUL | SMART | NONLIVING |
+F:EVIL | DEMON | IM_COLD | IM_POIS | HURT_LITE | NO_SLEEP |
+F:RES_TELE | CTHANGBAND
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | BA_WATE | S_DEMON | HASTE |
+S:TPORT | TELE_AWAY | TELE_TO | HEAL | BR_DARK | BR_NETH
+D:His form is partially that of a reptile, partially that of a gigantic
+D:octopus. He will destroy you.
+
+N:758:Bloodthirster
+G:U:r
+I:130:60d70:30:180:20
+W:55:3:0:18500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:50d1
+B:HIT:HURT:50d1
+B:HIT:HURT:50d1
+F:DEMON | EVIL | DROP_GOOD | REGENERATE | CAN_FLY | NONLIVING |
+F:FORCE_SLEEP | FORCE_MAXHP | ONLY_ITEM | DROP_4D2 | ELDRITCH_HORROR |
+F:OPEN_DOOR | BASH_DOOR | RES_NETH | RES_NEXU | RES_TELE | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | NO_FEAR
+F:CAN_FLY | ZANGBAND | HAS_LITE
+D:Khorne's mightiest servant, a winged hound-demon walking on
+D:two paws and wielding a mighty axe and a whip in the other
+D:two. Intelligent, bloodthirsty eyes leer at you from inside
+D:the blood-soaked demon armour.
+
+N:759:Draconic quylthulg
+G:Q:g
+I:120:48d10:20:1:0
+W:45:1:3000:3000
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TPORT |
+S:S_DRAGON
+D:It looks like it was once a dragon corpse, now deeply infected with
+D:magical bacteria that make it pulse in a foul and degrading way.
+
+N:760:Nyogtha, the Thing that Should not Be
+G:j:D
+I:130:55d99:20:120:20
+W:56:2:0:20000
+E:0:0:0:0:0:0
+O:50:0:40:10
+B:CRUSH:ACID:10d6
+B:CRUSH:ACID:10d6
+B:CRUSH:ACID:10d6
+B:CRUSH:HURT:16d16
+F:UNIQUE | ELDRITCH_HORROR |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | SMART | CAN_SWIM |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:REGENERATE | ONLY_ITEM | KILL_ITEM | DROP_2D2 | DROP_90 | DROP_60 |
+F:BASH_DOOR | EVIL | NO_CONF | NO_SLEEP | KILL_BODY |
+F:FORCE_MAXHP | FORCE_SLEEP | HURT_LITE | POWERFUL | RES_NETH | NONLIVING |
+F:IM_ACID | IM_FIRE | RES_PLAS | IM_POIS | IM_COLD | IM_ELEC | RES_TELE
+F:CTHANGBAND | NO_CUT
+S:1_IN_5 |
+S:BRAIN_SMASH | MIND_BLAST | HASTE | TPORT |
+S:S_DEMON | S_UNDEAD | S_HI_UNDEAD | S_KIN |
+S:BR_DARK | BR_NUKE | BR_ACID | BR_POIS
+D:"...a little finger of blackness crept out from beneath its edge
+D:a great wave of iridescent blackness, neither liquid nor solid,
+D:a frightful gelatinous mass."
+
+N:761:Ahtu, Avatar of Nyarlathotep
+G:#:D
+I:130:50d110:30:120:15
+W:56:3:0:22500
+E:1:1:1:2:1:1
+O:0:30:60:10
+B:CRUSH:HURT:13d13
+B:CRUSH:FIRE:10d10
+B:CRUSH:HURT:13d13
+B:CRUSH:FIRE:10d10
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | CAN_SPEAK |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | MOVE_BODY |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | NONLIVING | ELDRITCH_HORROR | CTHANGBAND | NO_CUT | NO_SLEEP | NO_CONF | NO_STUN
+S:1_IN_6
+S:BR_FIRE | S_DEMON | CAUSE_4 | BR_PLAS | BR_NETH | BRAIN_SMASH |
+S:S_UNDEAD | BA_DARK
+D:"Higher already than the giants of the forest ringing it, the
+D:fifty-foot-thick column... sprouted a ring of tendrils, ruddy and
+D:golden and glittering overall with inclusions of quartz."
+
+N:762:Fundin Bluecloak
+G:h:B
+I:130:50d100:25:195:10
+W:56:2:1400:20000
+E:1:1:1:2:1:1
+O:0:80:20:0
+B:HIT:HURT:10d10
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP | PET |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:HEAL | BLIND | CONF | SCARE | CAUSE_3 | CAUSE_4 | BRAIN_SMASH |
+S:FORGET | S_MONSTERS | S_ANIMALS
+D:He is one of the greatest dwarven priests to walk the earth. Fundin has
+D:earned a high position in the church, and his skill with both weapon and
+D:spell only justify his position further. His combination of both dwarven
+D:strength and priestly wisdom are a true match for any adventurer.
+
+N:763:Bile Demon
+G:U:R
+I:120:35d100:40:90:80
+W:61:2:0:12000
+E:1:0:1:2:1:0
+O:30:70:0:0
+B:HIT:HURT:8d8
+B:HIT:HURT:8d8
+B:CRUSH:ACID:9d9
+B:CRUSH:ACID:9d9
+F:FORCE_SLEEP | FORCE_MAXHP | BASEANGBAND |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | NONLIVING |
+F:EVIL | DEMON | IM_POIS | IM_ACID | NO_CONF | NO_SLEEP
+S:1_IN_6 |
+S:BR_POIS | BR_ACID | S_DEMON | BLIND | CONF | ARROW_4
+D:It's big. It's fat. It's red. It's ugly. It's got a severe attack of
+D:highly poisonous flatulence. And its insides are corrosive. All of which
+D:go together to make the single most repulsive sight - and smell - you have
+D:ever experienced.
+
+N:764:Uriel, Angel of Fire
+G:A:R
+I:130:55d100:40:160:10
+W:61:3:3400:25000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:FIRE:4d12
+B:HIT:FIRE:4d12
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:UNIQUE | MALE | CAN_SPEAK | NO_FEAR | GOOD | AURA_FIRE | REFLECTING |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | SUSCEP_COLD |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | NO_SLEEP
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | RES_TELE | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:TELE_TO | BLIND |
+S:BO_FIRE | BO_MANA | BA_FIRE |
+S:BR_FIRE | BR_PLAS |
+S:S_ANGEL
+D:A creature of godly appearance. You dare not challenge Uriel's supremacy.
+D:Those who stood against him before are but a memory, cremated by his
+D:mastery of elemental fire.
+
+N:765:Azriel, Angel of Death
+G:A:D
+I:130:60d100:40:170:10
+W:62:3:3400:30000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:EXP_80:10d5
+B:HIT:EXP_80:10d5
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:UNIQUE | MALE | CAN_SPEAK | RES_NETH | NO_FEAR | GOOD |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | REFLECTING | AURA_COLD |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | CAN_FLY | NO_SLEEP
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | RES_TELE | BASEANGBAND
+F:HAS_LITE
+S:1_IN_2 |
+S:TELE_TO | BLIND |
+S:BO_MANA | BO_NETH | BA_NETH |
+S:BR_NETH |
+S:S_ANGEL
+D:Azriel commands awesome power, his visage holy enough to shrivel your
+D:soul. You shriek with disbelief as his mastery of death draws you to your
+D:grave. It is truly beyond all but the mightiest of warriors to stand
+D:against him and live.
+
+N:766:Ancalagon the Black
+G:D:D
+I:140:180d100:40:170:70
+W:90:1:330000:60000
+E:0:1:0:6:1:0
+O:60:40:0:0
+B:CLAW:HURT:10d12
+B:CLAW:HURT:10d12
+B:BITE:HURT:10d14
+B:BITE:HURT:10d14
+F:UNIQUE | MALE | CAN_FLY | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BR_ACID | BR_FIRE | BR_DISI | BR_WALL | BR_TIME |
+S:S_HI_DRAGON | S_KIN
+D:"Rushing Jaws" is his name, and death is his game; the greatest and most
+D:terrible of all dragonkind, his power dismayed even the Valar for a time.
+
+N:767:Daoloth, the Render of the Veils
+G:U:s
+I:120:72d100:20:125:70
+W:58:3:0:27500
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:CONFUSE:5d12
+B:TOUCH:CONFUSE:5d12
+B:TOUCH:CONFUSE:5d12
+B:TOUCH:CONFUSE:5d12
+F:UNIQUE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:IM_ACID | IM_FIRE | IM_POIS | IM_COLD | ELDRITCH_HORROR |
+F:NEVER_MOVE | RES_NEXU | REFLECTING | PASS_WALL
+F:NO_CONF | NO_SLEEP | NO_CUT | NO_STUN | NO_FEAR | CTHANGBAND
+S:1_IN_3 |
+S:TELE_AWAY | S_MONSTERS | TELE_LEVEL | BR_NEXU | TPORT | BLINK
+D:"Not shapeless, but so complex that the eye could recognise no
+D:describable shape. There were hemispheres and shining metal,
+D:coupled by long plastic rods. The rods were of a flat gray color,
+D:so that he could not make out which were nearer; they merged into
+D:a flat mass from which protruded individual cylinders. As he looked
+D:at it, he had a curious feeling that eyes gleamed from between
+D:these rods; but wherever he glanced at the construction, he saw
+D:only the spaces between them."
+
+N:768:Nightwalker
+G:W:D
+I:130:80d70:20:175:10
+W:73:3:75000:20000
+E:1:1:1:2:1:1
+O:30:0:70:0
+B:HIT:UN_BONUS:10d10
+B:HIT:UN_BONUS:10d10
+B:HIT:UN_BONUS:7d7
+B:HIT:UN_BONUS:7d7
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | CAN_SWIM |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | RES_TELE |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:BLIND | SCARE | BRAIN_SMASH |
+S:BO_MANA | BO_NETH | BA_NETH | S_UNDEAD
+D:A huge giant garbed in black, more massive than a titan and stronger than
+D:a dragon. With terrible blows, it breaks your armour from your back,
+D:leaving you defenceless against its evil wrath. It can smell your fear,
+D:and you in turn smell the awful stench of death as this ghastly figure
+D:strides towards you menacingly.
+
+N:769:Gabriel, the Messenger
+G:A:w
+I:130:75d100:40:180:10
+W:64:3:3500:35000
+E:1:1:1:2:1:1
+O:0:40:60:0
+B:HIT:UN_BONUS:6d8
+B:HIT:FIRE:6d8
+B:HIT:BLIND:10d10
+B:HIT:BLIND:10d10
+F:UNIQUE | MALE | FORCE_MAXHP | CAN_SPEAK | NO_SLEEP | GOOD |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | AURA_ELEC | REFLECTING | RES_TELE
+F:ESCORT | CAN_FLY |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:TELE_TO | BLIND | BO_MANA |
+S:S_ANGEL | S_KIN
+D:Commanding a legion of angels, Gabriel will destroy you for your sins. He
+D:will crush you like the pitiful insignificant being he sees you to be.
+D:Your very soul will be taken into judgement by his supreme authority as he
+D:cleanses the world of evil.
+
+N:770:Artsi, the Champion of Chaos
+G:h:v
+I:130:11d666:25:175:10
+W:59:2:1600:20000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:EXP_80:10d10
+B:HIT:EXP_80:10d10
+B:HIT:EXP_80:10d10
+F:UNIQUE | MALE | CAN_SPEAK | ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | DROP_CORPSE
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | RES_NEXU | RES_NETH |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:ZANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BA_FIRE | BA_CHAO | CONF | TPORT | S_DEMON | BR_CHAO | BA_MANA
+D:He is one of the greatest warriors of chaos to walk the earth.
+D:His bloody blade has slain thousands and tens of thousands, and still
+D:hungers for more.
+
+N:771:Saruman of Many Colours
+G:p:v
+I:120:70d100:100:100:0
+W:60:1:1600:35000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_BONUS:6d8
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:UNIQUE | MALE | ATTR_MULTI | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | RES_TELE
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:DROP_GREAT | DROP_CHOSEN |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_FIRE | IM_COLD |
+F:IM_ELEC | IM_POIS | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE |
+S:CAUSE_4 | MIND_BLAST | FORGET | TRAPS | ANIM_DEAD | BO_MANA |
+S:BO_ICEE | BA_ACID | BA_FIRE | BA_COLD | BA_WATE | BA_CHAO |
+S:S_UNDEAD | S_DEMON | S_HI_DRAGON | S_ANIMALS
+D:Originally known as the White, Saruman fell prey to Sauron's wiles. He
+D:searches forever for the One Ring, to become a mighty Sorcerer-King of the
+D:world.
+
+N:772:Harowen the Black Hand
+G:p:B
+I:140:25d100:30:36:0
+W:51:3:0:20000
+E:1:1:1:2:1:1
+O:90:10:0:0
+B:TOUCH:EAT_GOLD:5d5
+B:TOUCH:EAT_ITEM:5d5
+B:HIT:BLIND:10d5
+B:HIT:POISON:8d5
+F:UNIQUE | MALE | CAN_SPEAK | SMART |
+F:FORCE_SLEEP | FORCE_MAXHP | MOVE_BODY |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:IM_POIS | BASEANGBAND
+S:1_IN_6 |
+S:TELE_TO | TRAPS
+D:He is a master of disguise, an expert of stealth, a genius at traps, and
+D:moves with blinding speed. Check your pockets!
+
+N:773:Osyluth
+G:U:W
+I:130:40d100:20:75:80
+W:65:2:0:13000
+E:1:1:1:2:1:1
+O:20:40:20:20
+B:HIT:LOSE_CHR:8d8
+B:HIT:LOSE_CHR:8d8
+B:BITE:POISON:10d10
+B:STING:LOSE_STR:9d9
+F:FORCE_SLEEP | FORCE_MAXHP | NONLIVING |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY
+F:EVIL | DEMON | NO_CONF | NO_SLEEP | INVISIBLE | BASEANGBAND |
+F:IM_ACID | IM_ELEC | IM_POIS | IM_COLD | IM_FIRE | NO_CUT
+S:1_IN_6 |
+S:BA_ELEC | BA_COLD | BO_ICEE | SCARE | S_DEMON
+D:It is a demon made almost entirely out of bones. It is humanoid, but with
+D:a large tail similar to that of a giant scorpion, and emits a foul smell
+D:of decay and rot. They are despised even in the hells.
+
+N:774:Dreadlord
+G:G:r
+I:120:30d100:20:150:10
+W:62:2:0:20000
+E:0:0:0:0:0:0
+O:10:10:60:10
+B:HIT:EXP_40:6d6
+B:HIT:EXP_40:6d6
+B:HIT:LOSE_STR:4d6
+B:HIT:LOSE_STR:4d6
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 |
+F:INVISIBLE | COLD_BLOOD | TAKE_ITEM | PASS_WALL |
+F:EVIL | UNDEAD | CAN_FLY |
+F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | RES_TELE | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:HOLD | DRAIN_MANA | BLIND | S_UNDEAD | CONF |
+S:SCARE | TELE_TO | TPORT | BRAIN_SMASH | ANIM_DEAD
+S:BA_NETH | DARKNESS
+D:It is a massive form of animated death, its colour deeper than black. It
+D:drinks in light, and space around it is twisted and torn by the weight of
+D:its evil. It is unlife and it knows nothing but the stealing of souls and
+D:the stench of death. Flee its hunger!
+
+N:775:Greater kraken
+G:~:G
+I:120:40d100:30:175:80
+W:60:2:10000:25000
+E:3:0:3:0:1:0
+O:10:80:0:10
+B:CRUSH:HURT:15d15
+B:CRUSH:HURT:15d15
+B:CRUSH:HURT:15d15
+B:CRUSH:HURT:15d15
+F:FORCE_SLEEP | FORCE_MAXHP | AQUATIC | WILD_TOO | WILD_OCEAN |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | DROP_CORPSE |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | SMART | RES_WATE |
+F:EVIL | IM_ELEC | NO_CONF | NO_SLEEP | IM_POIS | IM_FIRE | BASEANGBAND
+S:1_IN_3 |
+S:BLIND | CONF | SCARE | CAUSE_4 | CAUSE_3 | TELE_TO | TELE_AWAY |
+S:BA_WATE | DARKNESS | BR_DARK | BR_ACID | BR_POIS
+D:An enormously fearsome and powerful inhabitant of the depths. It
+D:resembles a gargantuan octopus and its evil is almost tangible.
+
+N:776:Archlich
+G:L:B
+I:120:45d100:20:120:50
+W:64:2:0:20000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:EXP_80
+B:TOUCH:UN_POWER
+B:TOUCH:LOSE_DEX:8d12
+B:TOUCH:LOSE_DEX:8d12
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | RES_TELE | CAN_FLY |
+F:ONLY_ITEM | DROP_4D2 | DROP_2D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_3 |
+S:BLINK | TELE_TO | BLIND | HOLD | SCARE | CAUSE_4 |
+S:DRAIN_MANA | BRAIN_SMASH | S_HI_UNDEAD | FORGET |
+S:TPORT | HEAL | S_DEMON | BA_NETH | ANIM_DEAD
+D:A lich who has reached its ultimate evolutionary stage: a completely
+D:immaterial state.
+
+N:777:The Cat Lord
+G:f:v
+I:130:48d100:100:200:0
+W:66:3:0:30000
+E:0:1:0:2:1:0
+O:30:60:0:10
+B:CLAW:CONFUSE:12d12
+B:CLAW:LOSE_DEX:2d12
+B:CLAW:BLIND:10d5
+B:BITE:PARALYZE:15d1
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF | BASEANGBAND
+S:1_IN_3 |
+S:TELE_TO | S_KIN
+D:Master of all things feline, the Cat Lord moves with catlike stealth.
+
+N:778:Jabberwock
+G:H:v
+I:130:32d100:35:125:255
+W:68:3:2300:19000
+E:0:1:0:2:1:0
+O:20:50:20:0
+B:CLAW:HURT:10d10
+B:CLAW:HURT:10d10
+B:BITE:HURT:10d10
+B:BITE:HURT:10d10
+F:ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE |
+F:ONLY_ITEM | DROP_60 | DROP_90 |
+F:BASH_DOOR | CAN_FLY | DROP_CORPSE |
+F:ANIMAL | MORTAL | BASEANGBAND |
+S:1_IN_5 |
+S:CAUSE_4 |
+S:BR_CHAO
+D:'Twas brillig, and the slithy toves / Did gyre and gimble in the wabe: /
+D:All mimsy were the borogoves, / And the mome raths outgrabe. /
+D:"Beware the Jabberwock, my son! / The jaws that bite, the claws that catch!"
+
+N:779:Chaos hound
+G:Z:v
+I:120:60d30:30:100:0
+W:65:2:900:10000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:BITE:HURT:2d12
+B:CLAW:HURT:2d12
+F:ATTR_MULTI | ATTR_ANY | DROP_SKELETON | DROP_CORPSE |
+F:FORCE_SLEEP | FRIENDS | BASH_DOOR |
+F:ANIMAL | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_5 |
+S:BR_CHAO
+D:A constantly changing canine form, this hound rushes towards you as if
+D:expecting mayhem and chaos ahead. It appears to have an almost kamikaze
+D:relish for combat. You suspect all may not be as it seems.
+
+N:780:Vlad Dracula, Prince of Darkness
+G:V:D
+I:130:70d100:20:166:10
+W:76:4:1800:33333
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:BITE:EXP_80:8d8
+B:BITE:EXP_80:8d8
+B:HIT:CONFUSE:8d8
+B:HIT:CONFUSE:8d8
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SPEAK | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP |
+F:RES_TELE | ZANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA | S_HI_UNDEAD |
+S:BRAIN_SMASH | BA_NETH | S_KIN | DARKNESS | BA_DARK
+D:The most feared of all vampires, the Prince of Darkness himself.
+
+N:781:Beholder hive-mother
+G:e:y
+I:120:40d100:30:80:10
+W:67:3:2600:17000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:EXP_80:6d6
+B:GAZE:PARALYZE:5d5
+B:GAZE:INSANITY:5d5
+B:GAZE:UN_POWER:5d5
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | CAN_FLY |
+F:BASH_DOOR | FEMALE | SMART | DROP_CORPSE |
+F:EVIL | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_2 |
+S:BLIND | CONF | FORGET | SCARE | DRAIN_MANA | BRAIN_SMASH |
+S:BA_DARK | BO_MANA | BA_ACID | BA_FIRE | BA_COLD | BO_NETH |
+S:S_KIN
+D:A hive mother of the race of beholders, she can summon her brood to her aid
+D:whenever she wishes.
+
+N:782:Leviathan
+G:~:v
+I:120:50d100:40:180:30
+W:67:3:300000:28000
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:BITE:FIRE:5d12
+B:BITE:FIRE:5d12
+B:CRUSH:HURT:6d15
+B:CRUSH:HURT:6d15
+F:FORCE_SLEEP | FORCE_MAXHP | AQUATIC | WILD_TOO | WILD_OCEAN |
+F:IM_FIRE | IM_ACID | IM_COLD | IM_POIS |
+F:ANIMAL | NO_CONF | NO_SLEEP | NO_FEAR | DROP_CORPSE | BASEANGBAND
+S:1_IN_9
+S:BR_FIRE | BR_ACID | BR_DARK | BR_POIS | BA_WATE |
+S:S_DRAGON | S_HYDRA | HEAL | CONF | DARKNESS
+D:An enormous, terrible sea-monster, the true master of the ocean.
+
+N:783:Great Wyrm of Chaos
+G:D:v
+I:120:60d100:40:170:100
+W:75:2:190000:29000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:5d12
+B:CLAW:HURT:5d12
+B:BITE:HURT:7d14
+B:BITE:HURT:7d14
+F:ATTR_MULTI | ATTR_ANY |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_DISE | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_CHAO | BR_DISE |
+S:S_DRAGON | S_HI_DRAGON
+D:A massive dragon of changing form. As you watch, it appears first fair
+D:and then foul. Its body is twisted by chaotic forces as it strives to
+D:stay real. Its very existence distorts the universe around it.
+
+N:784:Great Wyrm of Law
+G:D:B
+I:120:60d100:40:170:100
+W:75:2:190000:29000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:5d12
+B:CLAW:HURT:5d12
+B:BITE:HURT:7d14
+B:BITE:HURT:7d14
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | CAN_FLY |
+F:EVIL | DRAGON | NO_STUN | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_SOUN | BR_SHAR |
+S:S_DRAGON | S_HI_DRAGON
+D:A massive dragon of powerful intellect. It seeks to dominate the universe
+D:and despises all other life. It sees all who do not obey it as mere
+D:insects to be crushed underfoot.
+
+N:785:Great Wyrm of Balance
+G:D:v
+I:120:70d100:40:170:100
+W:80:2:190000:31000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:6d12
+B:CLAW:HURT:6d12
+B:BITE:HURT:8d14
+B:BITE:HURT:8d14
+F:DRAGON | EVIL | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | RES_DISE |
+F:NO_STUN | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | NO_CUT | ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_SOUN | BR_CHAO | BR_SHAR | BR_DISE |
+S:S_DRAGON | S_HI_DRAGON
+D:A massive dragon, it is thousands of
+D:years old and seeks to maintain the Cosmic Balance. It sees you as an
+D:upstart troublemaker without the wisdom to control your actions.
+
+N:786:Shambler
+G:E:W
+I:130:50d100:40:150:50
+W:67:4:9000:22500
+E:0:0:0:0:0:0
+O:20:20:60:0
+B:CLAW:HURT:3d12
+B:CLAW:HURT:3d12
+B:CRUSH:HURT:8d12
+B:CRUSH:HURT:8d12
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | CAN_SWIM | DROP_CORPSE |
+F:BASH_DOOR | OPEN_DOOR | POWERFUL | MOVE_BODY |
+F:NO_CONF | NO_SLEEP | EVIL | ZANGBAND | NO_CUT
+S:1_IN_3 |
+S:BR_ELEC
+D:This elemental creature is power incarnate; it looks like a huge polar bear
+D:with a huge gaping maw instead of a head.
+
+N:787:Gelugon
+G:U:w
+I:130:45d100:20:100:80
+W:69:3:0:14000
+E:2:1:2:0:0:1
+O:20:60:20:0
+B:CLAW:COLD:6d10
+B:CLAW:COLD:6d10
+B:BITE:COLD:9d9
+B:HIT:PARALYZE:8d8
+F:FORCE_SLEEP | FORCE_MAXHP | BASEANGBAND | NONLIVING |
+F:ONLY_ITEM | DROP_GOOD | DROP_4D2 | CAN_FLY |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | REGENERATE |
+F:EVIL | DEMON | IM_COLD | NO_CONF | NO_SLEEP
+S:1_IN_6
+S:BR_COLD | BR_SHAR | BA_COLD | BO_ICEE | S_HI_DEMON |
+S:SLOW | SCARE | HOLD
+D:This demon from the ice planes is a truly terrifying sight. It has an
+D:extremely large, insect-like body towering a full twelve feet tall, with
+D:great claws on its hands and pincers on its mouth, and its head bulges
+D:with great multi-faceted eyes. Its tail is covered with razor-sharp
+D:spikes.
+
+N:788:Glaaki
+G:~:v
+I:130:52d99:20:150:10
+W:67:2:0:36000
+E:0:0:0:0:1:0
+O:0:0:100:0
+B:STING:HURT:20d1
+B:STING:DISEASE:20d1
+B:CRUSH:HURT:3d20
+F:UNIQUE | CAN_SPEAK | ATTR_MULTI | ELDRITCH_HORROR |
+F:NO_STUN | RES_NEXU | RES_WATE | CAN_SWIM | REGENERATE |
+F:FORCE_SLEEP | FORCE_MAXHP | EVIL | SMART | DEMON |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:NO_STUN |
+F:IM_POIS | IM_COLD | IM_ACID | CTHANGBAND
+S:1_IN_5
+S:BA_WATE | S_HYDRA | S_DRAGON | S_DEMON | SCARE |
+S:BLIND | CONF | CAUSE_4 | BR_POIS
+D:"From an oval body protruded countless thin, pointed spines of
+D:multi-colored metal; at the more rounded end of the oval a
+D:circular, thick-lipped mouth formed the centre of a spongy
+D:face, from which rose three yellow eyes on thin stalks. Around
+D:the underside of the body were many white pyramids, presumably
+D:used for locomotion. The diameter of the body must have been
+D:at least ten feet at its least wide..."
+
+N:789:Trone, the Rebel Thunderlord
+G:B:D
+I:130:80d100:20:120:80
+W:72:2:400000:45000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:12d10
+B:HIT:HURT:12d10
+B:CHARGE:HURT:10d10
+B:CHARGE:HURT:10d10
+F:UNIQUE | MALE | CAN_SPEAK | RES_TELE | SPECIAL_GENE |
+F:FORCE_MAXHP | REGENERATE | THUNDERLORD | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:SMART | OPEN_DOOR | BASH_DOOR | DROP_CHOSEN |
+F:EVIL | IM_FIRE | IM_COLD | IM_POIS |
+F:MORTAL | HAS_LITE
+S:1_IN_4 |
+S:TELE_TO | SCARE | BR_TIME | BR_FIRE |
+S:BO_MANA | S_THUNDERLORD
+D:As the Thunderlords came from afar to help the elves, Trone and his rebel
+D:wing came to defend Morgoth! He is an evil and powerful Thunderlord.
+
+N:790:Great Wyrm of Many Colours
+G:D:v
+I:120:70d100:40:170:255
+W:80:2:190000:31000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:6d12
+B:CLAW:HURT:6d12
+B:CLAW:HURT:8d14
+B:BITE:HURT:8d14
+F:ATTR_MULTI | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | AURA_ELEC | RES_TELE |
+F:IM_FIRE | IM_ACID | IM_POIS | IM_COLD | IM_ELEC | AURA_COLD |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE |
+F:DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BR_POIS | BR_ELEC | BR_ACID | BR_FIRE | BR_COLD |
+S:CONF | SCARE | BLIND
+D:A huge dragon whose scales shimmer in myriad hues.
+
+N:791:Marda, rider of gold Laronth
+G:B:y
+I:130:100d85:100:100:50
+W:75:6:420000:35000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:12d15
+B:HIT:HURT:12d15
+B:HIT:HURT:12d15
+B:HIT:HURT:12d15
+F:UNIQUE | FEMALE | CAN_SPEAK | THUNDERLORD | ONLY_ITEM | RES_TELE |
+F:DROP_CHOSEN | DROP_CORPSE | DROP_SKELETON | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_4D2 | DROP_1D2 | DROP_GOOD | DROP_60 | DROP_90 |
+F:EVIL | IM_POIS | IM_ELEC | SMART | REGENERATE | OPEN_DOOR | BASH_DOOR |
+F:MORTAL | HAS_LITE
+S:1_IN_4 |
+S:SCARE | BLIND | TPORT | BLINK | TELE_AWAY | TELE_TO |
+S:BR_FIRE | BA_MANA | S_THUNDERLORD
+D:Former leader among the Thunderlords, she and Trone have fallen under the
+D:control of Morgoth. Laronth, her eagle, can summon Thunderlords to her aid.
+
+N:792:Tselakus, the Dreadlord
+G:G:R
+I:130:65d100:20:150:10
+W:68:2:1000:35000
+E:1:1:1:2:1:1
+O:0:80:20:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:LOSE_STR:4d6
+B:HIT:LOSE_STR:4d6
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | CAN_FLY |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL |
+F:EVIL | UNDEAD | IM_COLD | AURA_COLD |
+F:IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT | NO_STUN
+S:1_IN_3 |
+S:BLIND | HOLD | CONF |
+S:BA_DARK | BA_NETH |
+S:S_WRAITH | S_HI_UNDEAD | S_KIN
+D:This huge affront to existence twists and tears at the fabric of space.
+D:Darkness itself recoils from the touch of Tselakus as he leaves a trail
+D:of death and destruction. Mighty claws rend reality as he
+D:annihilates all in his path to your soul!
+
+N:793:Sky Drake
+G:D:B
+I:130:60d100:40:200:255
+W:77:2:220000:40000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:6d12
+B:CLAW:HURT:6d12
+B:BITE:ELEC:8d14
+B:BITE:ELEC:8d14
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | AURA_COLD | DROP_CORPSE |
+F:IM_ELEC | EVIL | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY |
+F:DRAGON | NO_CONF | NO_SLEEP | RES_TELE | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BR_ELEC | BR_GRAV | BR_LITE |
+S:S_DRAGON | S_HI_DRAGON |
+S:SCARE | BLIND
+D:The mightiest elemental dragon of air, it can destroy you with ease.
+
+N:794:Eilinel the Entrapped
+G:p:D
+I:120:90d10:10:40:1
+W:42:10:0:3500
+E:1:1:1:2:1:1
+O:20:20:50:10
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:TOUCH:UN_POWER:3d3
+B:TOUCH:UN_BONUS:3d3
+F:UNIQUE | FEMALE |
+F:FORCE_MAXHP |
+F:DROP_1D2 | DROP_2D2 | DROP_3D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_FIRE | NO_CONF | NO_SLEEP | ZANGBAND | NO_CUT | NO_STUN
+S:1_IN_2 |
+S:BLINK | TELE_TO | HEAL | S_MONSTER | BO_ACID | BO_MANA | CAUSE_3
+D:In life, she was the wife of Gorlim. In death, her shade was
+D:entrapped by Morgoth and used to trick her husband into betraying
+D:Barahir. Now she is totally entrapped by Morgoth's power, and uses
+D:her magic to do his bidding.
+
+N:795:Horned Reaper
+G:U:B
+I:130:50d100:40:120:80
+W:72:3:0:18000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:11d11
+B:HIT:HURT:11d11
+B:HIT:HURT:11d11
+B:HIT:HURT:11d11
+F:FORCE_SLEEP | FORCE_MAXHP | REGENERATE | NONLIVING |
+F:ONLY_ITEM | DROP_GOOD | DROP_4D2 | DROP_2D2 | BASEANGBAND |
+F:OPEN_DOOR | BASH_DOOR | KILL_BODY | POWERFUL |
+F:EVIL | DEMON | NO_CONF | NO_SLEEP | CAN_FLY |
+F:IM_FIRE | IM_COLD | IM_POIS | IM_ACID | IM_ELEC
+S:1_IN_5 |
+S:HASTE | SLOW | SCARE | S_HI_DEMON
+D:A giant humanoid demon wielding a massive, heavy and sharp scythe. Feared
+D:by foes and friends alike when it flies into one of its berserk rages, the
+D:Horned Reaper will cut down anything in its path between it and you - even
+D:the minions it has just summoned.
+
+N:796:The Norsa
+G:H:B
+I:130:100d100:20:125:70
+W:70:4:16000:47500
+E:0:1:0:2:1:0
+O:50:0:50:0
+B:CRUSH:ACID:8d12
+B:CRUSH:FIRE:8d12
+B:CRUSH:ELEC:8d12
+B:CRUSH:POISON:10d14
+F:ATTR_MULTI | ELDRITCH_HORROR | DROP_CORPSE |
+F:UNIQUE | CAN_SPEAK | AURA_FIRE | AURA_ELEC | AURA_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | EVIL |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:ZANGBAND
+S:1_IN_2 |
+S:BLIND | CONF | SCARE |
+S:BR_NETH | BR_CHAO | BR_TIME | BR_GRAV | BR_DISI |
+S:S_HI_DRAGON | S_MONSTERS
+D:An elephantine horror with five trunks, each capable of breathing
+D:destructive blasts. It is said that it is better to face the fury
+D:of a thousand raging lions than the Norsa!
+
+N:797:Rhan-Tegoth
+G:S:b
+I:130:90d100:20:125:70
+W:70:4:0:42500
+E:0:1:0:2:1:0
+O:0:50:50:0
+B:CLAW:HURT:8d12
+B:CRUSH:LOSE_STR:5d12
+B:CLAW:HURT:8d12
+B:CRUSH:ACID:5d12
+F:ELDRITCH_HORROR | CAN_SWIM | EVIL |
+F:UNIQUE | CAN_SPEAK | AURA_FIRE | AURA_ELEC |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_ELEC | IM_POIS | NO_SLEEP | RES_WATE | CTHANGBAND
+S:1_IN_5 |
+S:S_DEMON | BR_ACID | S_UNDEAD | S_KIN | CONF | SCARE |
+S:MIND_BLAST | BR_CONF
+D:"An almost globular torso, with six long, sinuous limbs terminating
+D:in crab-like claws. From the upper end a subsidiary globe bulged
+D:forward bubble-like; its triangle of three staring, fishy eyes,
+D:its foot-long and evidently flexible proboscis, and a distended
+D:lateral system analogous to gills, suggested that it was a head."
+
+N:798:Black reaver
+G:L:D
+I:120:50d100:20:170:50
+W:74:3:1900:23000
+E:0:0:0:0:0:0
+O:0:60:40:0
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_BONUS:6d8
+B:HIT:LOSE_STR:4d6
+B:HIT:LOSE_STR:4d6
+F:FORCE_SLEEP | FORCE_MAXHP | SMART | CAN_SWIM |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | RES_TELE |
+F:NO_CONF | NO_SLEEP | KILL_WALL | NO_FEAR |
+F:MORTAL | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:TELE_TO | BLIND | HOLD | CONF | CAUSE_3 | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | BA_MANA | BA_NETH | S_UNDEAD
+D:A humanoid form, black as night, advancing steadily and unstoppably, even
+D:the very rock of the dungeon cannot prevent it reaching you.
+
+N:799:Master mindcrafter
+G:p:y
+I:120:80d10:20:60:10
+W:40:2:1700:2000
+E:1:1:1:2:1:1
+O:20:40:40:0
+B:HIT:HURT:4d5
+B:HIT:HURT:4d5
+B:HIT:HURT:4d5
+F:MALE | ONLY_ITEM | FORCE_SLEEP | FORCE_MAXHP | DROP_4D2 |
+F:SMART | BASH_DOOR | TAKE_ITEM | MOVE_BODY |
+F:EVIL | IM_FIRE | IM_ELEC | IM_POIS | BASEANGBAND |
+F:NO_CONF | NO_SLEEP | NO_STUN | NO_FEAR
+S:1_IN_2 |
+S:HEAL | BLIND | HOLD | CONF | SCARE | TPORT | BA_COLD | BA_FIRE |
+S:MIND_BLAST | BRAIN_SMASH | S_MONSTERS | TELE_AWAY | BLINK | BO_NETH
+D:A mindcrafter of the highest order. Powerful and evil, and a dangerous
+D:enemy: a master of mind over matter, of his own mind, and of the minds of
+D:others, who slavishly follow him into battle when he calls them.
+
+N:800:Greater demonic quylthulg
+G:Q:R
+I:120:15d100:20:1:0
+W:71:2:5000:10500
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TELE_TO |
+S:S_HI_DEMON
+D:A massive pulsating mound of flesh, glowing with an inner hellish light.
+
+N:801:Greater draconic quylthulg
+G:Q:G
+I:120:15d100:20:1:0
+W:71:2:5000:10500
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TELE_TO |
+S:S_HI_DRAGON
+D:A massive mound of scaled flesh, throbbing and pulsating with multi-hued
+D:light.
+
+N:802:Greater rotting quylthulg
+G:Q:U
+I:120:15d100:20:1:0
+W:71:2:5000:10500
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND |NO_CUT
+S:1_IN_2 |
+S:BLINK | TELE_TO |
+S:S_HI_UNDEAD
+D:A massive pile of rotting flesh. A disgusting stench fills the air as it
+D:throbs and writhes.
+
+N:803:Null, the Living Void
+G:.:d
+I:110:50d100:30:100:20
+W:72:2:0:32500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:LOSE_ALL:6d16
+B:TOUCH:EXP_80:6d16
+B:TOUCH:UN_POWER:6d16
+F:PASS_WALL | NONLIVING | IM_ACID | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | SMART | EMPTY_MIND |
+F:ELDRITCH_HORROR | IM_COLD | RES_NETH | NO_STUN | UNIQUE | NO_SLEEP | NO_CONF | NO_FEAR |
+F:REGENERATE | ZANGBAND | NO_CUT
+S:1_IN_5
+S:BR_NETH | BRAIN_SMASH | SCARE | S_UNDEAD | S_HI_UNDEAD |
+S:DRAIN_MANA | HEAL | ANIM_DEAD
+D:A black hole in the fabric of reality. It simply is not there.
+
+N:804:Feagwath, the Undead Sorcerer
+G:L:y
+I:130:60d100:20:85:50
+W:77:2:1900:30000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:TOUCH:EXP_80:6d12
+B:TOUCH:UN_POWER:6d12
+B:TOUCH:LOSE_DEX:6d12
+B:TOUCH:LOSE_DEX:6d12
+F:UNIQUE | MALE | CAN_SPEAK | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | SPECIAL_GENE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD |
+F:IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:BLINK | TELE_TO | HOLD | SCARE | CAUSE_4 |
+S:BRAIN_SMASH | TRAPS | BA_MANA |
+S:BO_MANA | BA_NETH |
+S:S_MONSTERS | S_UNDEAD | S_KIN | ANIM_DEAD
+D:A stench of corruption and decay surrounds this sorcerer, who has clearly
+D:risen from the dead to continue his foul plots and schemes.
+
+N:805:Omarax the Eye Tyrant
+G:e:v
+I:130:65d100:30:80:10
+W:73:3:600:25000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:UN_BONUS:6d6
+B:GAZE:UN_POWER:6d6
+B:GAZE:INSANITY:6d6
+B:BITE:EXP_80:8d8
+F:UNIQUE | MALE | CAN_SPEAK | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:SMART | BASH_DOOR |
+F:EVIL | IM_POIS |
+F:BASEANGBAND
+S:1_IN_2 |
+S:BLIND | CONF | FORGET | SCARE | DRAIN_MANA | BRAIN_SMASH |
+S:BA_DARK | BO_MANA | BA_NETH | BA_ACID | BA_FIRE | BA_COLD |
+S:S_KIN
+D:A beholder of great size and age, floating in the air. His gaze seems to
+D:shred your soul and his spells crush your will. He is ancient, his history
+D:steeped in forgotten evils, his atrocities numerous and sickening.
+
+N:806:Tsathoggua, the Sleeper of N'kai
+G:R:D
+I:130:66d100:30:80:100
+W:74:4:0:16500
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:CRUSH:LOSE_ALL:5d6
+B:CRUSH:ACID:5d6
+B:CRUSH:LOSE_ALL:5d6
+B:CRUSH:ACID:5d6
+F:UNIQUE | CAN_SPEAK | RES_TELE | ELDRITCH_HORROR |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_1D2 | DROP_2D2 |
+F:ONLY_ITEM | DROP_GOOD | MOVE_BODY |
+F:SMART | BASH_DOOR |
+F:EVIL | DEMON | IM_POIS | IM_ACID | IM_COLD |
+F:NO_CONF | NO_SLEEP | CTHANGBAND | HAS_LITE | NO_CUT
+S:1_IN_2 |
+S:S_DEMON | S_MONSTERS | S_UNDEAD | HOLD | SCARE | MIND_BLAST |
+S:BR_ACID | BR_NETH | CAUSE_4
+D:"...the formless bulking of a couchant mass. And the mass stirred
+D:a little... and put forth with infinite slothfulness a huge and
+D:toad-shaped head. And the head opened its eyes very slowly, as if
+D:half awakened from slumber, so that they were visible as two slits
+D:of oozing phosphor in the black browless face."
+
+N:807:Greater Balrog
+G:U:v
+I:130:75d100:40:140:40
+W:80:3:0:25000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:FIRE:8d12
+B:HIT:FIRE:8d12
+B:CRUSH:HURT:7d12
+B:TOUCH:UN_POWER
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | NONLIVING |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | SMART |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | KILL_WALL |
+F:RES_NETH | RES_PLAS | REGENERATE | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BLIND | CONF | BRAIN_SMASH |
+S:BR_FIRE | BA_NETH | BA_FIRE | BR_PLAS | BO_PLAS |
+S:S_HI_DEMON | S_UNDEAD | S_DEMON |
+D:Originally of the semi-divine Maiar, this evil spirit swore allegiance
+D:to Morgoth at the beginning of time and is now one of his most terrible
+D:demonic servants. With its flaming whip and sword it seeks to destroy you.
+
+N:808:Ungoliant, the Unlight
+G:S:D
+I:130:130d100:8:160:80
+W:75:1:1500:35000
+E:0:1:0:2:1:0
+O:20:0:80:0
+B:CLAW:POISON:8d6
+B:CLAW:POISON:8d6
+B:BITE:PARALYZE:8d10
+B:STING:LOSE_STR:8d4
+F:UNIQUE | FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:SMART | BASH_DOOR | IM_ACID |
+F:ANIMAL | SPIDER | EVIL | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP
+F:BASEANGBAND
+S:1_IN_3 |
+S:HEAL | BLIND | SLOW | CONF | SCARE | DARKNESS | BA_DARK |
+S:BR_POIS | BR_DARK | S_SPIDER
+D:This enormous, hideous spirit of void is in the form of a spider of
+D:immense proportions. She is surrounded by a cloud of Unlight as she sucks
+D:in all living light into her bloated body, and breathes out the blackest of
+D:darkness. She is always ravenously hungry and would even eat herself to
+D:avoid starvation.
+
+N:809:Atlach-Nacha, the Spider God
+G:S:D
+I:120:110d100:8:160:80
+W:73:1:2000:31000
+E:0:1:0:2:1:0
+O:60:0:40:0
+B:BITE:POISON:3d9
+B:BITE:LOSE_STR:3d9
+B:STING:POISON:2d9
+B:STING:LOSE_STR:2d9
+F:UNIQUE | CAN_SPEAK | ELDRITCH_HORROR | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:SMART | BASH_DOOR | MOVE_BODY | NONLIVING |
+F:ANIMAL | SPIDER | EVIL | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP |
+F:CTHANGBAND
+S:1_IN_3 |
+S:SCARE | BLIND | CONF | HOLD | BR_POIS |
+S:DARKNESS | BR_DARK | S_KIN | S_HI_DEMON
+D:"...there was a kind of face on the squat ebon body, low down amid
+D:the several-jointed legs. The face peered up with a weird expression
+D:of doubt and inquiry..."
+
+N:810:Y'golonac
+G:H:R
+I:120:130d99:8:160:80
+W:75:1:0:37500
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:LOSE_INT:1d20
+B:BITE:HURT:40d1
+B:TOUCH:LOSE_WIS:1d20
+B:BITE:HURT:40d1
+F:UNIQUE | CAN_SPEAK | ELDRITCH_HORROR | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:SMART | BASH_DOOR | MOVE_BODY | NONLIVING |
+F:EVIL | IM_POIS | CTHANGBAND
+S:1_IN_3 |
+S:SCARE | BLIND | CONF | HOLD | HASTE | DRAIN_MANA | HASTE |
+S:CAUSE_3 | CAUSE_4 | DARKNESS | FORGET | S_DEMON | S_HOUND |
+S:TPORT | TELE_TO
+D:"He saw why the shadow on the frosted pane yesterday had been
+D:headless and he screamed... but before he could scream out his
+D:protest his breath was cut off, as the hands descended on his
+D:face and the wet red mouths opened in their palms."
+
+N:811:Aether hound
+G:Z:v
+I:120:60d40:30:100:0
+W:74:3:1000:10000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:3d12
+B:BITE:HURT:3d12
+B:CLAW:HURT:3d12
+B:CLAW:HURT:3d12
+F:ATTR_MULTI | ATTR_ANY |
+F:FORCE_SLEEP | CAN_FLY |
+F:FRIENDS | RES_NETH | RES_PLAS | RES_NEXU | RES_DISE |
+F:BASH_DOOR | AURA_FIRE | AURA_ELEC | AURA_COLD |
+F:DROP_CORPSE | DROP_SKELETON |
+F:ANIMAL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_5 |
+S:BR_ACID | BR_FIRE | BR_COLD | BR_ELEC | BR_POIS |
+S:BR_LITE | BR_DARK | BR_SOUN | BR_CONF | BR_CHAO | BR_SHAR |
+S:BR_NETH | BR_DISE | BR_WALL | BR_INER | BR_TIME |
+S:BR_GRAV | BR_PLAS | BR_NEXU
+D:A shifting, swirling form. It seems to be all colours and sizes and
+D:shapes, though the dominant form is that of a huge dog. You feel very
+D:uncertain all of a sudden.
+
+N:812:Pit Fiend
+G:U:o
+I:130:60d100:30:120:75
+W:77:3:0:22000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:CLAW:FIRE:8d10
+B:CLAW:FIRE:8d10
+B:BITE:POISON:7d10
+B:BITE:LOSE_CON:7d10
+F:FORCE_SLEEP | FORCE_MAXHP | REGENERATE | NONLIVING |
+F:ONLY_ITEM | DROP_GOOD | DROP_4D2 | DROP_2D2 | BASEANGBAND |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | POWERFUL | CAN_FLY |
+F:EVIL | DEMON | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP
+S:1_IN_5 |
+S:S_HI_DEMON | S_HI_DRAGON | BR_FIRE | BR_POIS | BR_CHAO | BA_CHAO |
+S:SCARE | BA_FIRE | CAUSE_4
+D:Appearing as a giant, clawed and winged humanoid with a scaly red body
+D:and massive fangs dripping a foul green liquid, the Pit Fiend is a
+D:dreadful enemy from the lowest depths of the hells. They are often the
+D:commanders of vast demon armies.
+
+N:813:The Serpent of Chaos
+G:J:v
+I:130:66d100:30:180:150
+W:75:2:0:25000
+E:0:0:0:0:1:0
+O:40:40:20:0
+B:CRUSH:POISON:15d10
+B:CRUSH:CONFUSE:15d10
+B:CRUSH:UN_BONUS:10d15
+B:CRUSH:UN_POWER:10d15
+F:UNIQUE | ATTR_MULTI | ATTR_ANY | AURA_FIRE | AURA_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | POWERFUL | REGENERATE |
+F:DROP_2D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | ANIMAL | EVIL | CAN_FLY |
+F:KILL_ITEM | KILL_BODY |
+F:NO_CONF | NO_SLEEP | NO_STUN | NO_FEAR | ZANGBAND |
+F:IM_COLD | IM_FIRE | IM_POIS
+S:1_IN_3 |
+S:BR_CHAO | BR_DISI | BA_CHAO | BR_NETH | BR_MANA |
+S:S_MONSTERS | S_HI_DEMON | S_HI_DRAGON
+D:Writhing coil upon coil, this mighty spawn of Chaos constantly disintegrates
+D:and reforms before your dazzled eyes. It gazes balefully upon you with its
+D:one remaining eye, and seeks to destroy you utterly.
+
+N:814:Yig, Father of Serpents
+G:J:b
+I:130:111d60:100:100:15
+W:76:1:2300:37500
+E:0:0:0:0:1:0
+O:80:0:20:0
+B:CLAW:POISON:8d10
+B:CLAW:POISON:8d10
+B:CRUSH:HURT:8d15
+B:BITE:HURT:100d1
+F:UNIQUE | MALE | CAN_SPEAK | SMART | RES_TELE | ANIMAL |
+F:ESCORT | ESCORTS | CAN_SWIM | ELDRITCH_HORROR |
+F:OPEN_DOOR | BASH_DOOR |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:DROP_4D2 | DROP_1D2 | DROP_GOOD | DROP_60 | DROP_90 | ONLY_ITEM |
+F:EVIL | IM_POIS | IM_ACID | IM_ELEC | REGENERATE | ZANGBAND
+S:1_IN_4 |
+S:BR_POIS | BR_NUKE | BR_ACID | S_HYDRA | S_KIN | S_DEMON |
+D:"The half-human father of serpents... the snake-god of the central
+D:plains tribes -- presumably the primal source of the more
+D:southerly Quetzalcoatl or Kukulcan -- was odd, half-anthropomorphic
+D:devil."
+
+N:815:Unmaker
+G:E:v
+I:120:6d66:60:50:60
+W:77:4:0:10000
+E:0:0:0:0:0:0
+O:20:50:20:0
+B:TOUCH:LOSE_ALL:10d10
+B:TOUCH:UN_BONUS:10d10
+B:TOUCH:UN_POWER:10d10
+F:KILL_WALL | KILL_ITEM | KILL_BODY | NO_FEAR |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ATTR_MULTI | SHAPECHANGER | ATTR_ANY |
+F:DROP_60 | DROP_GOOD | POWERFUL | AURA_ELEC | AURA_FIRE | AURA_COLD |
+F:BASH_DOOR | IM_ELEC | IM_FIRE | RES_NEXU | IM_COLD | ZANGBAND |
+F:IM_POIS | IM_ACID | RES_PLAS | RES_DISE | COLD_BLOOD | NONLIVING | RAND_50
+F:NO_CUT
+S:MULTIPLY |
+S:1_IN_5 |
+S:BR_CHAO
+D:Spawned from the Pits of the Abyss, it is a mass of sentient Chaos,
+D:spreading uncontrollably and destroying everything in its path.
+
+N:816:Cyberdemon
+G:U:u
+I:120:70d101:90:90:90
+W:77:4:15000:30000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d50
+B:HIT:HURT:1d50
+B:HIT:HURT:1d50
+B:HIT:HURT:1d50
+F:RAND_25 | EVIL | DEMON |
+F:DROP_2D2 | DROP_GOOD | ONLY_ITEM | RES_TELE | SUSCEP_ACID |
+F:IM_POIS | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP | NONLIVING
+F:MORTAL | ZANGBAND | HAS_LITE
+S:1_IN_4 |
+S:ROCKET
+D:Reverbrant metal steps announce the arrival of this huge creature,
+D:half demon half machine. It has an unsurpassable firepower.
+
+N:817:Hela, Queen of the Dead
+G:p:G
+I:130:74d100:60:110:10
+W:74:3:8000:45000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:TOUCH:LOSE_ALL:50d1
+B:TOUCH:EXP_80:50d1
+B:TOUCH:UN_BONUS:50d1
+F:UNIQUE | FEMALE | CAN_SPEAK | POWERFUL | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_1D2 | DROP_GREAT | DROP_GOOD |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | RES_NETH | IM_COLD | IM_POIS |
+F:IM_FIRE | NO_CONF | NO_SLEEP | ZANGBAND | HAS_LITE
+S:1_IN_3 |
+S:CAUSE_3 | CAUSE_4 | HAND_DOOM | TELE_TO | HOLD |
+S:S_UNDEAD | S_HI_UNDEAD | S_HI_DRAGON | FORGET | SCARE | BLIND |
+S:BA_DARK | BA_NETH | HEAL | ANIM_DEAD
+D:The Norse ruler of Hel is a merciless queen, who ever hunts for more
+D:souls to add to her collection of tortured spirits. She arrives in
+D:an ominous green robe, a certain sign of impending doom, to claim
+D:as her own those who die an ignoble death.
+
+N:818:The Mouth of Sauron
+G:p:v
+I:130:90d100:60:100:10
+W:78:3:1900:38000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_BONUS:6d8
+B:TOUCH:UN_POWER
+B:TOUCH:UN_POWER
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_1D2 | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_FIRE | IM_COLD |
+F:IM_ELEC | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:TELE_TO | HOLD | CAUSE_3 | TRAPS | ANIM_DEAD |
+S:BO_PLAS | BA_DARK | BA_MANA | BA_FIRE | BA_WATE | BA_NETH
+S:S_HI_DEMON | S_HI_UNDEAD
+D:The Mouth of Sauron is a mighty spell caster. So old that even he cannot
+D:remember his own name, his power and evil are undeniable. He believes
+D:unshakeably that he is unbeatable and laughs as he weaves his awesome
+D:spells.
+
+N:819:The Necromancer of Dol Guldur
+G:p:v
+I:130:82d100:60:100:10
+W:75:3:1900:40000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_POWER:6d8
+B:HIT:BLIND:6d8
+B:HIT:CONFUSE:6d8
+F:UNIQUE | MALE | CAN_SPEAK | POWERFUL | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_1D2 | DROP_GREAT | DROP_GOOD | SPECIAL_GENE |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_FIRE | IM_COLD |
+F:IM_ELEC | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_2 |
+S:CAUSE_3 | TELE_TO | BA_FIRE | DRAIN_MANA | HOLD |
+S:TRAPS | BA_WATE | BO_PLAS | BA_NETH |
+S:BA_MANA | BA_DARK | S_HI_UNDEAD | BA_CHAO | HAND_DOOM | ANIM_DEAD
+D:The dark master of the terrible fortress of southern Mirkwood. It is
+D:rumoured that this is in fact none other than Sauron in disguise:
+D:although if this is so, he has yet to reveal his full power - and perhaps
+D:will not do so while his deception lasts, in the hope of keeping it going.
+
+N:820:Lisa, rider of gold Romth
+G:B:y
+I:130:65d100:100:100:15
+W:78:6:420000:35500
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:10d15
+B:HIT:HURT:10d15
+B:HIT:HURT:10d15
+B:HIT:HURT:10d15
+F:UNIQUE | FEMALE | THUNDERLORD | RES_TELE | CAN_FLY | GOOD |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | DROP_SKELETON | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | PET |
+F:DROP_4D2 | DROP_1D2 | DROP_GOOD | DROP_60 | DROP_90 | ONLY_ITEM |
+F:IM_COLD | IM_POIS | IM_ACID | IM_ELEC | REGENERATE |
+F:MORTAL | HAS_LITE
+S:1_IN_4 |
+S:TPORT | TELE_TO | BR_FIRE | S_THUNDERLORD |
+S:TELE_AWAY | HEAL | BA_NETH | BO_NETH | BA_FIRE | BO_FIRE
+D:She came to help you.
+
+N:821:Master quylthulg
+G:Q:B
+I:120:30d100:20:1:0
+W:76:2:7000:15000
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:INVISIBLE | EMPTY_MIND | ANIMAL | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:S_MONSTERS | S_HI_UNDEAD | S_HI_DRAGON | S_HI_DEMON | S_ANIMALS | BLINK | TELE_TO
+D:A giant seething mass of flesh, overwhelming you with monster after monster.
+
+N:822:Qlzqqlzuup, the Lord of Flesh
+G:Q:v
+I:130:50d100:30:1:0
+W:79:3:10000:20000
+E:0:0:0:0:0:0
+O:20:20:20:20
+F:UNIQUE | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP | NEVER_MOVE | NEVER_BLOW |
+F:ONLY_ITEM | DROP_4D2 |
+F:INVISIBLE | ATTR_MULTI | EMPTY_MIND | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_1 |
+S:S_MONSTERS | S_ANGEL | S_HOUND | S_HYDRA | S_SPIDER | S_ANT | S_ANIMALS |
+S:S_HI_UNDEAD | S_HI_DRAGON | S_HI_DEMON | S_WRAITH | S_UNIQUE | S_KIN
+D:A gigantic seething mass of flesh, Qlzqqlzuup changes colours in front
+D:of your eyes. Pulsating first one colour then the next, it knows only it
+D:must bring help to protect itself.
+
+N:823:Cthugha, the Living Flame
+G:E:R
+I:133:50d100:30:1:20
+W:78:3:0:17500
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:BLIND
+B:TOUCH:FIRE:12d10
+B:TOUCH:FIRE:12d10
+B:TOUCH:FIRE:12d10
+F:UNIQUE | RES_TELE | AURA_FIRE | IM_FIRE | CAN_FLY | RES_PLAS |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | SMART | ELDRITCH_HORROR |
+F:EVIL | KILL_ITEM | KILL_BODY |
+F:NO_CONF | NO_SLEEP | NO_FEAR | CTHANGBAND | HAS_LITE | NO_CUT
+S:1_IN_4 |
+S:BR_FIRE | S_KIN
+D:"But even though we had shielded our eyes, it was impossible not
+D:to see the great amorphous shapes streaming skyward from the
+D:accursed place, nor the equally great being hovering like a cloud
+D:of living fire above the trees."
+
+N:824:Flare, rider of bronze Moonth
+G:B:U
+I:130:70d100:100:100:15
+W:79:5:400000:38500
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:15d15
+B:HIT:HURT:15d15
+B:HIT:HURT:15d15
+B:HIT:HURT:15d15
+F:UNIQUE | MALE | THUNDERLORD | RES_TELE | CAN_FLY | CAN_SPEAK |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | DROP_SKELETON |
+F:FORCE_SLEEP | FORCE_MAXHP | PET | GOOD |
+F:DROP_4D2 | DROP_1D2 | DROP_GOOD | DROP_60 | DROP_90 | ONLY_ITEM |
+F:IM_COLD | IM_POIS | IM_ACID | IM_ELEC | IM_FIRE | REGENERATE |
+F:MORTAL | HAS_LITE
+S:1_IN_4 |
+S:SCARE | TPORT | TELE_TO | S_THUNDERLORD |
+S:TELE_LEVEL | HEAL | BR_FIRE | BR_TIME | ROCKET
+D:A leader from afar, he has come with his eagle to help you in
+D:your battle. Having already saved his home, he now wants to save Arda!
+
+N:825:Maeglin, the Traitor of Gondolin
+G:h:D
+I:130:60d100:220:120:20
+W:81:2:1400:35000
+E:1:1:1:2:1:1
+O:30:70:0:0
+B:HIT:HURT:8d8
+B:HIT:HURT:8d8
+B:HIT:HURT:8d8
+B:HIT:HURT:8d8
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | CAN_SPEAK | SMART | EVIL | AI_SPECIAL |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | KILL_WALL | MOVE_BODY | TAKE_ITEM |
+F:IM_FIRE | IM_COLD | IM_POIS | SPECIAL_GENE |
+F:REGENERATE | REFLECTING | DROP_SKELETON | DROP_CORPSE | BASEANGBAND
+F:HAS_LITE
+S:1_IN_6 |
+S:S_MONSTERS | S_WRAITH | S_HI_UNDEAD |
+S:S_HI_DRAGON | S_HI_DEMON | S_UNIQUE | S_ANIMALS
+D:The son of Eol the Dark Elf, Maeglin is every bit as evil as his father
+D:and more. His greed for gold led him to betray the Hidden Kingdom of
+D:Gondolin to Morgoth's forces. He is a mighty warrior himself, and some
+D:of Morgoth's greatest servants answer to his call.
+
+N:826:Cyaegha
+G:e:G
+I:130:64d100:90:120:10
+W:80:3:0:44444
+E:0:0:0:0:0:0
+O:20:50:30:0
+B:CRUSH:HURT:15d15
+B:CRUSH:HURT:15d15
+B:CRUSH:HURT:15d15
+B:CRUSH:HURT:15d15
+F:UNIQUE | CAN_SPEAK | RES_TELE | ELDRITCH_HORROR | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | DEMON | IM_ACID | IM_ELEC |
+F:IM_COLD | IM_POIS | CTHANGBAND | HAS_LITE
+S:1_IN_3 |
+S:TELE_AWAY | BLIND | HOLD | SCARE | BRAIN_SMASH |
+S:BR_DARK | BA_DARK | BR_NETH | HAND_DOOM |
+S:S_HI_UNDEAD | S_DEMON | S_MONSTERS | S_HYDRA
+D:"...it was a gigantic eye staring down at them. Around the eye,
+D:the sky split; deep clefts opened through which the darkness
+D:began to ooze, a darkness blacker than the night, which crawled
+D:down as a set of slimy tentacles, taking on more form, more
+D:definite shape... something was standing, outlined against
+D:the black sky, something which had tentacles of darkness
+D:and a green-glowing eye."
+
+N:827:Pazuzu, Lord of Air
+G:U:b
+I:140:55d100:40:125:10
+W:82:2:0:30000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:ELEC:12d12
+B:HIT:ELEC:12d12
+B:HIT:ELEC:12d12
+B:HIT:ELEC:12d12
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | DEMON | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC |
+F:IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_3 |
+S:MIND_BLAST | BO_ELEC | BO_MANA | BA_ELEC | S_HI_DEMON
+D:A winged humanoid demon from the Planes of Hell, Pazuzu grins inhumanely at you
+D:as he decides your fate.
+
+N:828:Ithaqua the Windwalker
+G:Y:B
+I:140:55d100:40:125:10
+W:82:2:0:32500
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:CLAW:COLD:12d12
+B:CLAW:COLD:12d12
+B:CRUSH:ELEC:12d12
+B:CRUSH:ELEC:12d12
+F:UNIQUE | CAN_SPEAK | ESCORT | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | AURA_COLD |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | AURA_ELEC |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR | NONLIVING |
+F:EVIL | DEMON | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC |
+F:IM_POIS | NO_CONF | NO_SLEEP | CTHANGBAND
+S:1_IN_3 |
+S:BO_MANA | SCARE | BR_COLD | S_DEMON | BO_ELEC | BA_ELEC |
+S:MIND_BLAST | CAUSE_4 | BA_CHAO | BA_WATE | S_HI_UNDEAD | S_KIN
+D:The Wendigo, moving so fast that you can see little except two
+D:glowing eyes burning with hatred.
+
+N:829:Greater Hellhound
+G:C:r
+I:120:48d30:25:80:30
+W:78:2:600:600
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:5d12
+B:BITE:FIRE:5d12
+B:BITE:FIRE:5d12
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE
+F:RAND_25 | FRIENDS | AURA_FIRE | SUSCEP_COLD |
+F:BASH_DOOR | MOVE_BODY |
+F:ANIMAL | EVIL | IM_FIRE | BASEANGBAND | HAS_LITE |
+S:1_IN_5 | BR_FIRE
+D:It is a giant dog that glows with heat. Flames pour from its nostrils.
+
+N:830:Cantoras, the Skeletal Lord
+G:s:v
+I:140:75d100:20:120:80
+W:84:2:0:45000
+E:1:1:1:2:1:1
+O:50:0:50:0
+B:GAZE:EXP_80:5d5
+B:GAZE:EXP_80:5d5
+B:TOUCH:POISON:5d5
+B:TOUCH:POISON:5d5
+F:UNIQUE | MALE | CAN_SPEAK | RES_TELE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:TELE_TO | SLOW | SCARE | CAUSE_4 | BRAIN_SMASH |
+S:BO_ICEE | BO_MANA | BA_WATE | BA_NETH |
+S:S_HI_UNDEAD | ANIM_DEAD
+D:A legion of evil undead druj animating the skeleton of a once mighty
+D:sorcerer. His power is devastating and his speed unmatched in the
+D:underworld. Flee his wrath!
+
+N:831:Mephistopheles, Lord of Hell
+G:U:r
+I:140:30d222:20:150:50
+W:84:2:0:42500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:GAZE:EXP_80:10d5
+B:GAZE:TERRIFY:10d5
+B:TOUCH:FIRE:4d15
+B:TOUCH:UN_POWER:4d15
+F:MALE | UNIQUE | CAN_SPEAK | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:IM_FIRE | RES_PLAS | RES_NETH | AURA_FIRE |
+F:NO_CONF | NO_SLEEP | NONLIVING | EVIL | DEMON |
+F:ESCORTS | IM_COLD | IM_POIS | ZANGBAND | HAS_LITE
+S:1_IN_3 |
+S:TELE_TO | SCARE | HOLD | BRAIN_SMASH |
+S:S_DEMON | S_HI_UNDEAD | S_UNDEAD |
+S:BR_FIRE | BR_NETH | S_HI_DEMON | HAND_DOOM | ANIM_DEAD
+D:A duke of hell, in the flesh.
+
+N:832:Godzilla
+G:R:v
+I:130:85d100:50:185:20
+W:84:2:900000:35000
+E:0:1:0:2:1:0
+O:100:0:0:0
+B:CLAW:POISON:5d10
+B:CLAW:POISON:5d10
+B:BITE:HURT:20d10
+B:CRUSH:UN_BONUS:5d12
+F:UNIQUE | CAN_SWIM | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP | RES_PLAS | RES_DISE | RES_TELE
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | IM_FIRE | IM_COLD | IM_POIS
+F:MORTAL | JOKEANGBAND
+S:1_IN_2 |
+S:BR_DISE | BR_PLAS | BR_NUKE | BR_POIS | BR_ACID
+D:Godzilla rose from the contaminated sea. This terrible creature could
+D:level whole cities in anger.
+
+N:833:Abhoth, Source of Uncleanness
+G:j:G
+I:130:80d105:50:150:0
+W:85:3:0:40000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:ACID:11d11
+B:TOUCH:DISEASE:11d11
+B:TOUCH:ACID:11d11
+B:TOUCH:POISON:11d11
+F:ONLY_ITEM | DROP_1D2 | CAN_SWIM |
+F:DROP_GOOD | DROP_GREAT |
+F:CAN_SPEAK | EVIL | UNIQUE | NEVER_MOVE | FORCE_MAXHP | FORCE_SLEEP |
+F:IM_ACID | IM_POIS | IM_COLD | IM_ELEC | RES_NETH | RES_WATE | RES_NEXU |
+F:NO_FEAR | NO_CONF | NO_SLEEP | DEMON | SMART | ELDRITCH_HORROR |
+F:COLD_BLOOD | RES_DISE | NO_STUN | CTHANGBAND | NO_CUT
+S:1_IN_6 |
+S:S_MONSTERS | S_DEMON | S_HI_DRAGON | S_HI_UNDEAD | HEAL |
+S:TELE_AWAY | TPORT | CAUSE_4 | BRAIN_SMASH | DRAIN_MANA |
+S:BR_NUKE | BR_POIS | BR_CHAO | BR_NEXU | ANIM_DEAD
+D:"...in the pool [there was] a grayish, horrid mass that nearly choked
+D:it from rim to rim. Here, it seemed, was the ultimate source of all
+D:miscreation and abomination. For the gray mass quobbed and quivered,
+D:and swelled perpetually; and from it, in manifold fission, were
+D:spawned the anatomies that crept away on every side through the
+D:grotto."
+
+N:834:Ymir, the Ice Giant
+G:P:w
+I:130:86d110:50:160:20
+W:71:2:0:32500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:COLD:9d11
+B:HIT:HURT:9d11
+B:HIT:COLD:9d11
+B:HIT:HURT:9d11
+F:UNIQUE | CAN_SPEAK | SUSCEP_FIRE |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_WATE | MALE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | EVIL | ESCORT | GIANT |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | RES_DISE |
+F:EVIL | IM_COLD | IM_POIS | SUSCEP_FIRE |
+F:COLD_BLOOD | AURA_COLD | DROP_CORPSE | ZANGBAND | HAS_LITE | NO_CUT
+S:1_IN_5 |
+S:BR_COLD | BO_ICEE | DARKNESS | TELE_TO | S_KIN | S_HI_UNDEAD |
+S:TELE_AWAY | HAND_DOOM
+D:Ymir is one of the oldest beings in existence. He looks like a giant
+D:humanoid made of ice.
+
+N:835:Loki, the Trickster
+G:P:D
+I:130:110d100:50:160:20
+W:85:2:0:75000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:BLIND:6d11
+B:HIT:UN_BONUS:6d11
+B:HIT:UN_POWER:6d11
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_PLAS |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | EVIL | AURA_FIRE |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | CAN_SPEAK |
+F:EVIL | IM_FIRE | NO_CONF | NO_SLEEP | IM_POIS | SUSCEP_COLD | GIANT |
+F:REGENERATE | ZANGBAND | HAS_LITE
+S:1_IN_5 |
+S:SHRIEK | HASTE | HEAL | DRAIN_MANA | TPORT | TELE_TO | TELE_AWAY |
+S:TELE_LEVEL | FORGET | S_HI_DEMON | HAND_DOOM | S_HI_UNDEAD |
+S:S_UNIQUE | S_HI_DRAGON | BA_DARK | BA_MANA | ANIM_DEAD
+D:Loki, the god of mischief, is a nasty person. He will use every
+D:dirty trick in the book, and then some. In the end, his half-giant
+D:heritage is bound to show, as he will defect to the side of the
+D:giants and fight against the other gods of Asgard.
+
+N:836:Star-spawn of Cthulhu
+G:U:G
+I:130:75d100:90:90:90
+W:86:2:0:44000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:CLAW:POISON:1d30
+B:CLAW:ACID:1d30
+B:TOUCH:UN_POWER:1d10
+B:CRUSH:UN_BONUS:2d33
+F:RAND_25 | KILL_ITEM | OPEN_DOOR | BASH_DOOR | RES_NETH | ELDRITCH_HORROR |
+F:DROP_1D2 | DROP_2D2 | DROP_90 | ONLY_ITEM | FORCE_SLEEP | FORCE_MAXHP |
+F:EVIL | DEMON | IM_POIS | IM_COLD | IM_ACID | IM_ELEC | RES_TELE | NONLIVING |
+F:POWERFUL | IM_FIRE | CAN_SWIM | CTHANGBAND
+S:1_IN_3
+S:SCARE | CONF | S_DEMON | S_UNDEAD | DRAIN_MANA | BR_ACID |
+S:BR_FIRE | TPORT | S_MONSTERS | BRAIN_SMASH | BR_NETH |
+S:HEAL | MIND_BLAST | BA_NUKE | ANIM_DEAD
+D:The last remnants of sanity threaten to leave your brain as you
+D:behold this titanic bat-winged, octopus-headed unholy abomination.
+D:"They all lay in stone houses in their great city of R'lyeh,
+D:preserved by the spells of mighty Cthulhu for a glorious
+D:resurrection when the stars and the earth might once more
+D:be ready..."
+
+N:837:Surtur, the Fire Giant
+G:P:r
+I:130:86d110:50:160:20
+W:71:2:0:32500
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:FIRE:9d11
+B:HIT:HURT:9d11
+B:HIT:FIRE:9d11
+B:HIT:HURT:9d11
+F:UNIQUE | SUSCEP_COLD |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_PLAS | MALE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | EVIL | AURA_FIRE |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | CAN_SPEAK |
+F:EVIL | IM_FIRE | NO_CONF | NO_SLEEP | IM_POIS | SUSCEP_COLD | GIANT |
+F:ZANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BR_FIRE | BR_PLAS | BLIND | TELE_TO | S_KIN |
+S:HAND_DOOM | TELE_AWAY | S_HI_DEMON
+D:Surtur is another of the most ancient of all creatures. He is a demonic
+D:giant of fire, who is destined to set the nine worlds afire with his
+D:accursed sword of doom on the day of Ragnarok.
+
+N:838:The Tarrasque
+G:R:v
+I:130:130d100:50:185:20
+W:84:2:50000:35000
+E:1:1:1:2:1:1
+O:20:50:25:5
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:TOUCH:UN_POWER:10d10
+B:TOUCH:UN_POWER:10d10
+F:UNIQUE | ATTR_MULTI |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | IM_FIRE | IM_COLD | NO_SLEEP | BASEANGBAND
+S:1_IN_2 |
+S:BR_FIRE | BR_COLD | BR_DISE
+D:The Tarrasque is a massive reptile of legend, rumoured to be unkillable
+D:and immune to magic. Fear its anger, for its devastation is unmatched!
+
+N:839:Lungorthin, the Balrog of White Fire
+G:U:v
+I:130:80d100:20:125:80
+W:88:2:14000:37000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:FIRE:8d12
+B:HIT:FIRE:8d12
+B:CRUSH:HURT:8d12
+B:TOUCH:UN_POWER
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON | IM_FIRE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_FIRE | BR_PLAS |
+S:S_HI_DEMON | S_UNDEAD
+D:A massive form cloaked in flame. Lungorthin stares balefully at you with
+D:eyes that smoulder red. The dungeon floor where he stands is scorched by
+D:the heat of his body.
+
+N:840:Draugluin, Sire of All Werewolves
+G:C:v
+I:130:80d100:80:90:90
+W:83:2:1000:40000
+E:0:1:0:2:1:0
+O:0:0:100:0
+B:CLAW:HURT:6d8
+B:CLAW:HURT:6d8
+B:BITE:POISON:6d6
+B:BITE:POISON:6d6
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS | DROP_CORPSE |
+F:RAND_25 |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:ANIMAL | EVIL | IM_POIS | BASEANGBAND
+S:1_IN_3 |
+S:SCARE | S_MONSTERS | S_HOUND
+D:Draugluin provides Sauron with a fearsome personal guard. He is an
+D:enormous wolf inhabited by a human spirit. He is chief of all his kind.
+
+N:841:Shuma-Gorath
+G:e:G
+I:130:85d100:50:150:0
+W:88:3:0:47000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:CRUSH:HURT:12d12
+B:CRUSH:HURT:12d12
+B:GAZE:LOSE_INT:2d12
+B:GAZE:LOSE_WIS:2d12
+F:ONLY_ITEM | DROP_1D2 | CAN_SWIM |
+F:DROP_GOOD | DROP_GREAT |
+F:CAN_SPEAK | EVIL | UNIQUE | NEVER_MOVE | FORCE_MAXHP | FORCE_SLEEP |
+F:IM_ACID | IM_POIS | IM_COLD | IM_ELEC | RES_NETH | RES_WATE | RES_NEXU |
+F:NO_FEAR | NO_CONF | NO_SLEEP | DEMON | SMART | ELDRITCH_HORROR |
+F:COLD_BLOOD | RES_DISE | CTHANGBAND | NO_CUT
+S:1_IN_2 |
+S:FORGET | HEAL | HAND_DOOM | BA_MANA | TPORT | TELE_AWAY |
+S:TELE_LEVEL | CONF | BLIND | BRAIN_SMASH | DRAIN_MANA |
+S:S_MONSTERS | S_KIN | S_HI_DEMON | S_HOUND | S_HYDRA |
+S:S_UNIQUE | S_HI_UNDEAD | S_HI_DRAGON | ANIM_DEAD
+D:Shuma-Gorath is one of the immortal lords of chaos. The true form of
+D:this blasphemous horror is a huge, all-seeing eye surrounded by tentacles.
+
+N:842:Tulzscha, the Green Flame
+G:E:G
+I:130:100d100:90:100:50
+W:89:4:0:45000
+E:0:0:0:0:0:0
+O:0:100:0:0
+B:HIT:HURT:2d50
+B:HIT:HURT:2d50
+B:HIT:HURT:2d50
+B:HIT:HURT:2d50
+F:RAND_25 | EVIL | DEMON | AURA_FIRE | AURA_COLD | ELDRITCH_HORROR |
+F:DROP_2D2 | DROP_4D2 | DROP_GOOD | ONLY_ITEM | RES_TELE |
+F:IM_POIS | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP | NONLIVING |
+F:IM_COLD | IM_ACID | IM_ELEC | RES_NEXU | RES_NETH | RES_PLAS |
+F:REGENERATE | UNIQUE | CTHANGBAND | HAS_LITE | NO_CUT | NO_SLEEP | NO_FEAR
+S:1_IN_3 |
+S:BR_NEXU | BR_NETH | BR_COLD | BR_FIRE
+D:"A belching column of sick greenish flame... spouting volcanically
+D:from the depths profound and inconceivable, casting no shadows as
+D:healthy flame should, and coating the nitrous stone with a nasty,
+D:venomous verdigris. For all that seething combustion no warmth
+D:lay, but only the clamminess of death and corruption."
+
+N:843:Oremorj, the Cyberdemon Lord
+G:U:u
+I:130:90d100:90:90:90
+W:89:4:0:50000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:2d50
+B:HIT:HURT:2d50
+B:HIT:HURT:2d50
+B:HIT:HURT:2d50
+F:RAND_25 | EVIL | DEMON | SUSCEP_ACID |
+F:DROP_2D2 | DROP_GOOD | ONLY_ITEM | RES_TELE | DROP_GREAT | UNIQUE | NO_SLEEP |
+F:IM_POIS | IM_FIRE | FORCE_SLEEP | FORCE_MAXHP | NONLIVING | JOKEANGBAND |
+F:HAS_LITE
+S:1_IN_3 |
+S:ROCKET | S_HI_DEMON
+D:The mightiest of Cyberdemons, their lord and ruler.
+
+N:844:Vecna, the Emperor Lich
+G:L:v
+I:130:80d100:100:100:0
+W:92:3:0:45000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:EXP_80:7d12
+B:HIT:LOSE_DEX:7d12
+B:HIT:UN_POWER:7d12
+B:HIT:UN_POWER:7d12
+F:UNIQUE | MALE | CAN_SPEAK | RES_TELE
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_SLEEP | BASEANGBAND
+F:NO_CUT
+S:1_IN_3 |
+S:TPORT | BLIND | SCARE | CAUSE_4 | BRAIN_SMASH |
+S:BA_MANA | BO_MANA | BA_FIRE | BA_NETH |
+S:S_MONSTERS | S_HI_DEMON | S_HI_UNDEAD | S_KIN | HAND_DOOM | ANIM_DEAD
+D:The greatest of all undead sorcerers, even the gods once feared him. This
+D:ancient shadow of death wilts every living thing it passes.
+
+N:845:Yog-Sothoth, the All-in-One
+G:j:v
+I:130:66d99:100:100:20
+W:90:3:0:45000
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:TOUCH:HURT:40d5
+B:TOUCH:LOSE_CON:16d2
+B:TOUCH:LOSE_CON:16d2
+F:UNIQUE | CAN_SPEAK | SMART | ELDRITCH_HORROR | RES_TELE
+F:PASS_WALL | FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | AURA_FIRE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | ATTR_MULTI | ATTR_ANY | AURA_COLD |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | NONLIVING | CAN_FLY |
+F:EVIL | IM_ACID | IM_COLD | IM_ELEC | IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP
+F:CTHANGBAND | HAS_LITE | NO_CUT
+S:1_IN_3 |
+S:BO_MANA | BRAIN_SMASH | BA_MANA | S_MONSTERS | S_HI_DEMON |
+S:BA_CHAO | S_DEMON | S_HI_UNDEAD | S_HOUND | BR_MANA | BR_DISI
+D:"Great globes of light massing towards the opening... the breaking
+D:apart of the nearest globes, and the protoplasmic flesh that
+D:flowed blackly outward to join together and form that eldritch,
+D:hideous horror from outer space... whose mask was a congeries
+D:of iridescent globes... who froths as primal slime in nuclear
+D:chaos forever beyond the nethermost outposts of space and time!"
+
+N:846:Fenris Wolf
+G:C:D
+I:130:70d100:80:90:40
+W:90:2:0:35000
+E:0:1:0:2:1:0
+O:20:0:80:0
+B:BITE:HURT:20d6
+B:CLAW:HURT:8d8
+B:CLAW:HURT:8d8
+B:BITE:HURT:20d6
+F:UNIQUE | CAN_SPEAK | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:RAND_25 |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | KILL_BODY | NO_SLEEP
+F:ANIMAL | EVIL | IM_POIS | IM_FIRE | IM_COLD | ZANGBAND
+S:1_IN_8 |
+S:BR_DARK | BR_POIS | BR_COLD
+D:The immensely huge wolf who would swallow the sun to satisfy its
+D:hunger - and leave the gods for dessert.
+
+N:847:Great Wyrm of Power
+G:D:v
+I:130:111d111:40:160:70
+W:85:4:220000:47500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:8d12
+B:CLAW:HURT:8d12
+B:BITE:HURT:10d14
+B:BITE:HURT:10d14
+F:FORCE_SLEEP | FORCE_MAXHP | MOVE_BODY | AURA_FIRE | REFLECTING | AURA_ELEC |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | AURA_COLD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | RES_NETH | RES_DISE |
+F:DRAGON | GOOD | RES_TELE | DROP_CORPSE |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:RES_NEXU | RES_PLAS | CAN_FLY | BASEANGBAND | HAS_LITE | NO_CUT
+F:ATTR_MULTI
+S:1_IN_4 |
+S:S_HI_DRAGON | S_DRAGON | S_KIN |
+S:BR_ACID | BR_ELEC | BR_FIRE |
+S:BR_COLD | BR_POIS | BR_NETH | BR_LITE | BR_DARK |
+S:BR_CONF | BR_SOUN | BR_CHAO | BR_DISE | BR_NEXU |
+S:BR_TIME | BR_INER | BR_GRAV | BR_SHAR | BR_PLAS |
+S:BR_WALL | BR_MANA | BR_DISI
+D:The mightiest of all dragonkind, a great wyrm of power is seldom
+D:encountered in our world. It can crush stars with its might.
+
+N:848:Shub-Niggurath, Black Goat of the Woods
+G:U:D
+I:130:65d99:100:100:20
+W:91:3:0:47500
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:CRUSH:LOSE_WIS:20d5
+B:CRUSH:LOSE_INT:20d5
+B:BITE:LOSE_STR:10d2
+B:BITE:LOSE_CON:10d2
+F:UNIQUE | CAN_SPEAK | FEMALE | ELDRITCH_HORROR | AURA_ELEC | RES_TELE |
+F:ATTR_MULTI | FORCE_SLEEP | FORCE_MAXHP | PASS_WALL | ATTR_ANY |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_90 | NONLIVING | CAN_FLY |
+F:SMART | OPEN_DOOR | BASH_DOOR | REGENERATE | DEMON | AURA_COLD |
+F:EVIL | IM_ACID | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | CTHANGBAND
+S:1_IN_3 |
+S:BO_MANA | BRAIN_SMASH | BA_DARK | S_MONSTERS |
+S:CAUSE_4 | HEAL | BR_CHAO | BR_CONF | BR_POIS | BR_NUKE |
+S:BA_CHAO | S_HI_DEMON | S_HI_UNDEAD | S_UNIQUE | ANIM_DEAD
+D:This horrendous outer god looks like a writhing cloudy mass filled
+D:with mouths and tentacles.
+
+N:849:Nodens, Lord of the Great Abyss
+G:P:W
+I:130:75d99:100:100:20
+W:81:3:0:48000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:2d66
+B:HIT:HURT:2d66
+B:HIT:HURT:2d66
+B:HIT:HURT:2d66
+F:UNIQUE | CAN_SPEAK | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | PASS_WALL |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_90 | DROP_GREAT |
+F:SMART | OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY |
+F:GOOD | IM_ACID | IM_COLD | IM_POIS | IM_FIRE | IM_ELEC |
+F:RES_NEXU | RES_NETH | RES_WATE | ZANGBAND | HAS_LITE
+S:1_IN_1 |
+S:TELE_AWAY | TELE_TO | TELE_LEVEL | TPORT |
+S:BO_MANA | BA_MANA | S_MONSTERS | HEAL |
+S:CAUSE_4 | HASTE | HAND_DOOM |
+S:S_ANGEL | S_UNIQUE
+D:The hoary Lord of the Great Abyss seems a wizened man,
+D:but appearances can be deceiving.
+
+N:850:Carcharoth, the Jaws of Thirst
+G:C:D
+I:130:90d100:80:110:10
+W:94:1:3400:40000
+E:0:1:0:2:1:0
+O:30:10:60:0
+B:CLAW:POISON:9d12
+B:CLAW:POISON:9d12
+B:BITE:FIRE:9d12
+B:BITE:FIRE:9d12
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | AURA_FIRE |
+F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:ANIMAL | EVIL | IM_FIRE | IM_POIS | BASEANGBAND
+S:1_IN_3 |
+S:BR_DARK | BR_POIS | BR_FIRE | BR_NETH | S_HOUND
+D:The first guard of Angband, Carcharoth, also known as 'The Red Maw', is
+D:the largest wolf to ever walk the earth. He is highly intelligent and a
+D:deadly opponent in combat.
+
+N:851:Nyarlathotep, the Crawling Chaos
+G:U:r
+I:130:90d99:100:100:20
+W:93:3:0:49000
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:CRUSH:LOSE_CON:30d4
+B:CRUSH:LOSE_STR:30d4
+B:GAZE:LOSE_INT:1d50
+B:GAZE:LOSE_WIS:1d50
+F:UNIQUE | CAN_SPEAK | ELDRITCH_HORROR | RES_TELE | NONLIVING |
+F:FORCE_SLEEP | FORCE_MAXHP | PASS_WALL | DEMON | RES_NEXU |
+F:SHAPECHANGER | ATTR_MULTI | ATTR_ANY | MALE | AURA_ELEC |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT | DROP_90 |
+F:SMART | OPEN_DOOR | BASH_DOOR | REGENERATE | AURA_COLD |
+F:EVIL | IM_ACID | IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP | CTHANGBAND
+S:1_IN_3 |
+S:TELE_AWAY | TELE_TO | TELE_LEVEL | TPORT | BR_NEXU | BA_CHAO |
+S:BA_MANA | BA_FIRE | S_MONSTERS | BRAIN_SMASH | MIND_BLAST |
+S:CAUSE_4 | HASTE | S_HI_UNDEAD | S_HI_DRAGON | ANIM_DEAD |
+S:S_ANGEL | HEAL | S_SPIDER | S_HOUND | S_HI_DEMON | HAND_DOOM
+D:Nyarlathothep is the messenger, the heart and the soul of the outer gods.
+D:He is a shapechanger capable of assuming thousands of nightmarish forms.
+D:One of them looks like this: "A tall, slim figure with the young face of
+D:an antique pharaoh, gay with prismatic robes and crowned with a
+D:pshent that glowed with inherent light... the fascination of a
+D:dark god or fallen archangel, and around whose eyes there lurked
+D:the languid sparkle of capricious humor."
+
+N:852:Azathoth, the Daemon Sultan
+G:E:B
+I:130:99d99:100:150:100
+W:93:3:0:50000
+E:0:0:0:0:0:0
+O:0:50:0:50
+B:CRUSH:HURT:35d5
+B:CRAWL:ACID:35d5
+B:CRUSH:HURT:35d5
+B:CRAWL:ACID:35d5
+F:UNIQUE | ELDRITCH_HORROR | RES_TELE
+F:FORCE_SLEEP | FORCE_MAXHP | KILL_WALL | DEMON | AURA_FIRE | AURA_ELEC |
+F:ATTR_MULTI | ESCORTS | ESCORT | POWERFUL | ATTR_ANY | NONLIVING |
+F:KILL_ITEM | KILL_WALL | CAN_SWIM | AURA_COLD
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_90 | RES_DISE |
+F:STUPID | OPEN_DOOR | BASH_DOOR | REGENERATE | EMPTY_MIND |
+F:EVIL | IM_ACID | IM_POIS | IM_FIRE | NO_CONF | NO_SLEEP | NO_STUN | CTHANGBAND
+F:HAS_LITE | NO_CUT
+S:1_IN_5 |
+S:S_DEMON | BR_CHAO | BR_DISE | BR_MANA | BA_WATE | BR_DISI
+D:"That last amorphous blight of nethermost confusion which
+D:blasphemes and bubbles at the centre of all infinity --
+D:the boundless daemon sultan Azathoth, whose name no lips
+D:dare speak aloud, and who gnaws hungrily in inconceivable,
+D:unlighted chambers beyond time amidst the muffled, maddening
+D:beating of vile drums and the thin monotonous whine of
+D:accursed flutes."
+
+N:853:Huan, Wolfhound of the Valar
+G:C:W
+I:130:90d100:50:160:10
+W:93:2:3400:40000
+E:0:1:0:2:1:0
+O:30:10:60:0
+B:CLAW:COLD:9d12
+B:CLAW:COLD:9d12
+B:BITE:COLD:9d12
+B:BITE:COLD:9d12
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | AURA_COLD |
+F:SMART | OPEN_DOOR | BASH_DOOR | KILL_BODY |
+F:ANIMAL | GOOD |
+F:IM_COLD | IM_ACID | IM_ELEC | BASEANGBAND
+S:1_IN_5 |
+S:BR_COLD | BR_SHAR | BR_SOUN | BR_LITE |
+D:The wolfhound of the Valar, Huan has served many masters in his time, from
+D:Celegorm son of Feanor to Beren son of Barahir: but now he runs wild and
+D:acknowledges no master save himself, as he hunts alone for his nemesis -
+D:Carcharoth, the terrible wolf of Angband.
+
+N:854:Jormungand the Midgard Serpent
+G:J:v
+I:130:120d120:100:200:0
+W:94:1:3000:45000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CRUSH:HURT:20d10
+B:CRUSH:HURT:20d10
+B:BITE:HURT:5d50
+F:UNIQUE | FORCE_MAXHP | FORCE_SLEEP | WILD_TOO | WILD_OCEAN | WILD_SWAMP |
+F:MOVE_BODY | KILL_WALL | IM_FIRE | IM_POIS | IM_ACID | IM_COLD | COLD_BLOOD |
+F:RES_WATE | RES_PLAS | RES_NEXU | NO_STUN | REGENERATE | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | ZANGBAND
+S:1_IN_3
+S:BA_WATE | BR_MANA | BR_NETH | BR_POIS | BR_DISI | S_KIN
+D:The Midgard Serpent is so huge that its body surrounds the world of
+D:mortal men. It could grind even the gods into lifeless pulp.
+
+N:855:The Destroyer
+G:g:v
+I:130:140d140:100:200:0
+W:94:1:0:45000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:30d9
+B:HIT:HURT:30d9
+B:HIT:HURT:30d9
+B:HIT:HURT:30d9
+F:UNIQUE | NONLIVING | NO_FEAR | FORCE_MAXHP | FORCE_SLEEP | REFLECTING |
+F:MOVE_BODY | KILL_WALL | IM_FIRE | IM_ELEC | IM_POIS | IM_ACID | IM_COLD |
+F:RES_NETH | RES_WATE | RES_PLAS | RES_NEXU | NO_STUN | REGENERATE |
+F:NO_CONF | NO_SLEEP | NO_STUN | CAN_FLY |
+F:EMPTY_MIND | COLD_BLOOD | BASH_DOOR | ZANGBAND | NO_CUT
+S:1_IN_3 |
+S:BR_DISI | BO_MANA
+D:The Destroyer was built by the Norse deities to be their ultimate weapon
+D:against the space gods who had arrived to judge the world. Unfortunately,
+D:the Destroyer has gone berserk and is destroying everything it sees. The
+D:mystical Destroyer is nearly indestructible, and it is said that when it
+D:uses its power of complete disintegration, the day of Ragnarok is near.
+
+N:856:Gothmog, the High Captain of Balrogs
+G:U:v
+I:130:120d100:100:140:0
+W:95:1:17000:43000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:FIRE:9d12
+B:HIT:FIRE:9d12
+B:CRUSH:HURT:8d12
+B:TOUCH:UN_POWER
+F:UNIQUE | MALE | CAN_SPEAK | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ESCORT | ESCORTS | KILL_WALL | AURA_FIRE | NONLIVING |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON |
+F:IM_FIRE | IM_ELEC | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BLIND | CONF | SCARE |
+S:BR_FIRE | S_KIN |
+S:S_HI_DEMON | S_HI_UNDEAD
+D:Gothmog is the Chief Balrog in Morgoth's personal guard. He is renowned
+D:for slaying three High Kings of the Noldor Elves, and he has never been
+D:defeated in combat. With his whip of flame and awesome fiery breath he
+D:saved his master from Ungoliant's rage.
+
+N:857:Great Cthulhu
+G:U:g
+I:130:100d100:100:140:100
+W:97:2:4000:62500
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:CRUSH:HURT:50d4
+B:CLAW:UN_POWER:15d2
+B:CLAW:UN_BONUS:15d2
+B:TOUCH:DISEASE:100d1
+F:UNIQUE | CAN_SPEAK | DEMON | ELDRITCH_HORROR | NONLIVING |
+F:FORCE_SLEEP | FORCE_MAXHP | ESCORT | ESCORTS | SMART | RES_PLAS | RES_NEXU |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | REGENERATE | RES_NETH |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_1D2 | DROP_GOOD |
+F:DROP_GREAT | RES_DISE | RES_TELE | CAN_SWIM |
+F:EVIL | DEMON | IM_FIRE | IM_POIS | IM_ELEC | IM_ACID | NO_CONF | NO_SLEEP |
+F:CTHANGBAND
+S:1_IN_3
+S:TPORT | SCARE | BLIND | MIND_BLAST | BRAIN_SMASH | DRAIN_MANA |
+S:BR_POIS | BR_ACID | BR_FIRE | CONF | DARKNESS | FORGET | S_HI_UNDEAD |
+S:BR_NUKE | BR_NETH | BR_CHAO | BR_DISE | BR_DARK | BR_PLAS | BR_CONF
+S:BR_NEXU | S_HI_DEMON | BR_DISI | HAND_DOOM |
+S:ANIM_DEAD
+D:This creature is death incarnate. "A monster of vaguely anthropoid
+D:outline, but with an octopus-like head whose face was a mass of
+D:feelers, a scaly, rubbery-looking body, prodigious claws on hind
+D:fore feet, and long, narrow wings behind. This thing... was a
+D:somewhat bloated corpulence... It lumbered slobberingly into sight
+D:and gropingly squeezed its gelatinous green immensity through the
+D:black doorway... A mountain shambled or walked."
+
+N:858:Sarko, rider of gold Foronth
+G:B:y
+I:145:99d111:100:165:0
+W:97:6:420000:65000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:12d12
+B:HIT:HURT:12d12
+B:HIT:FIRE:12d12
+B:HIT:FIRE:12d12
+F:UNIQUE | FEMALE | DROP_CORPSE |
+F:ATTR_MULTI | THUNDERLORD | RES_TELE | PET | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | GOOD |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:DROP_CHOSEN | REFLECTING | AURA_FIRE | AURA_ELEC |
+F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY | REGENERATE |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | POWERFUL |
+F:MORTAL | HAS_LITE
+S:1_IN_4 |
+S:TPORT | TELE_TO | BR_FIRE | BR_TIME | S_THUNDERLORD | TELE_AWAY |
+D:Foronth is the first Eagle queen, and Sarko the first to discover
+D:the Firebirds, the ancestor of the Thunderlord eagles. She will try to
+D:help you!
+
+N:859:The Unicorn of Order
+G:q:w
+I:130:66d100:30:170:150
+W:75:2:0:65000
+E:0:1:0:2:1:0
+O:25:50:25:0
+B:KICK:UN_POWER:13d13
+B:KICK:UN_POWER:12d12
+B:BUTT:UN_BONUS:11d11
+B:BITE:TIME:10d10
+F:UNIQUE | FORCE_SLEEP | FORCE_MAXHP | REFLECTING | AURA_COLD |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:SMART | KILL_BODY | POWERFUL | BASH_DOOR | REGENERATE | CAN_FLY |
+F:ANIMAL | GOOD | IM_ACID | IM_COLD | IM_ELEC |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN | RES_TELE | ZANGBAND
+S:1_IN_3 |
+S:BR_SOUN | BR_LITE | BR_TIME | BR_SHAR | BR_MANA |
+S:S_MONSTERS | HEAL | S_ANGEL
+D:The Unicorn of Order, who once stole an eye from the great Serpent
+D:of Chaos, regards you as a mortal meddling in the affairs of immortals,
+D:and thus is attempting to eliminate you.
+
+N:860:Sauron, the Sorcerer
+G:p:v
+I:130:100d225:100:160:0
+W:99:1:2300:50000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:10d12
+B:HIT:UN_BONUS:10d12
+B:HIT:UN_POWER:8d12
+B:HIT:UN_POWER:8d12
+F:UNIQUE | MALE | CAN_SPEAK | REFLECTING |
+F:FORCE_SLEEP | FORCE_MAXHP | FORCE_DEPTH |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:SMART | OPEN_DOOR | BASH_DOOR | MOVE_BODY | REGENERATE | NO_SLEEP | NO_FEAR | NO_CONF |
+F:EVIL | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:TPORT | TELE_LEVEL | BLIND | CONF | SCARE | CAUSE_4 |
+S:BRAIN_SMASH | FORGET | BO_ICEE | BO_MANA | BO_PLAS |
+S:BA_MANA | BA_FIRE | BA_WATE | BA_NETH | BA_DARK | BA_CHAO |
+S:S_MONSTERS | S_HI_DEMON | S_HI_UNDEAD | S_HI_DRAGON | S_WRAITH | S_UNIQUE |
+S:HAND_DOOM | ANIM_DEAD
+D:Mighty in spells and enchantments, he created the One Ring.
+D:His eyes glow with power and with his gaze he seeks to destroy
+D:your soul. He has many servants, and rarely fights without them.
+
+N:861:DarkGod, the Mighty Coder of Hell
+G:P:B
+I:155:180d100:111:175:0
+W:127:1:1600:66666
+E:2:0:2:6:1:1
+O:20:20:20:20
+B:GAZE:EAT_GOLD:20d10
+B:HIT:SHATTER:20d10
+B:BITE:LOSE_ALL:10d12
+B:TOUCH:UN_POWER
+F:UNIQUE | CAN_SPEAK | ATTR_MULTI | ATTR_ANY | MALE |
+F:FORCE_MAXHP | WEIRD_MIND | DROP_CORPSE | DROP_SKELETON |
+F:REFLECTING | AURA_FIRE | AURA_ELEC | AURA_COLD |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_3D2 | DROP_4D2 |
+F:DROP_GOOD | DROP_GREAT | RES_NETH | INVISIBLE |
+F:SMART | KILL_WALL | KILL_BODY | POWERFUL | RES_TELE |
+F:REGENERATE | CAN_FLY | CAN_SWIM | DG_CURSE | WYRM_PROTECT |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF |NO_FEAR | NO_STUN | RES_TELE
+F:MORTAL | JOKEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:S_THUNDERLORD | BR_CHAO | BA_CHAO | ROCKET | BRAIN_SMASH | S_HI_DEMON |
+S:BR_NETH | HASTE | BR_MANA | S_HI_UNDEAD | S_HI_DRAGON | TRAPS | FORGET |
+S:BR_NUKE | BR_POIS | BR_DISI | HAND_DOOM | HEAL | TPORT | TELE_TO |
+S:S_BUG | S_RNG |
+D:He is the master of coding; none can match his skill. He created the
+D:Variant Maintainer, the RNGs, and the software bugs. Bull Gates is
+D:nothing next to him. Do not think that since he loves the novels of
+D:Tolkien he is to be ignored! He wishes to translate your soul into
+D:assembler, the one hitch being that he must kill you first. If you
+D:encounter him, pray your deity holds you in good stead.
+
+N:862:Morgoth, Lord of Darkness
+G:P:D
+I:140:200d150:100:150:0
+W:100:1:200000:60000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:SHATTER:24d10
+B:HIT:SHATTER:24d10
+B:HIT:LOSE_ALL:10d12
+B:TOUCH:UN_POWER
+F:UNIQUE | CAN_SPEAK | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | FORCE_DEPTH |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_3D2 | DROP_4D2 |
+F:DROP_GOOD | DROP_GREAT | DROP_CHOSEN | RES_NETH |
+F:SMART | KILL_WALL | MOVE_BODY | AURA_COLD |
+F:REGENERATE | POWERFUL |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_STUN | NO_SLEEP | NO_FEAR | RES_TELE | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BRAIN_SMASH |
+S:BA_MANA | BO_MANA | BA_NETH | BA_CHAO | BA_DARK | ANIM_DEAD |
+S:S_MONSTERS | S_UNIQUE | S_HI_DEMON | S_HI_UNDEAD | S_HI_DRAGON |
+S:ROCKET | BR_NETH | BR_DISI | HAND_DOOM | S_WRAITH
+D:He was the most powerful of the Valar, the equal of Manwe.
+D:He is the Master of the Pits of Angband. His figure is like a black
+D:mountain crowned with Lightning. He rages with everlasting anger, his
+D:body scarred by Fingolfin's eight mighty wounds. He can never rest from
+D:his pain, but seeks forever to dominate all that is light and good in the
+D:world. He is the origin of man's fear of darkness and created many foul
+D:creatures with his evil powers. Orcs, Dragons, and Trolls are his most
+D:foul corruptions, causing much pain and suffering in the world to please
+D:him. His disgusting visage, twisted with evil, is crowned with iron, the
+D:two remaining Silmarils forever burning him. Grond, the mighty Hammer of
+D:the Underworld, cries defiance as he strides towards you to crush you to a
+D:pulp!
+
+N:863:Human Warrior
+G:p:u
+I:110:9d8:10:10:0
+W:0:20:1700:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:5d8
+F:MALE |
+F:PET | FRIENDS | WILD_ONLY |
+F:ONLY_GOLD | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR
+F:DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:They are used as the main assault force of the human kings.
+D:They will help you in some quests.
+
+N:864:Elven archer
+G:h:W
+I:110:9d7:10:10:0
+W:0:20:1400:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:2d8
+F:MALE |
+F:PET | FRIENDS | WILD_ONLY |
+F:ONLY_GOLD | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR
+F:DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_1 |
+S:ARROW_2 | ARROW_1 | ARROW_2 | ARROW_1 |
+D:They are used as the main assault force of the elven kings.
+D:They will help you in some quests.
+
+N:865:Dwarven warrior
+G:h:U
+I:110:8d8:10:10:0
+W:0:20:1500:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:6d8
+F:MALE |
+F:PET | FRIENDS | WILD_ONLY |
+F:ONLY_GOLD | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR
+F:DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:They are used as the main assault force of the dwarven kings.
+D:They will help you in some quests.
+
+N:866:Elite uruk
+G:o:w
+I:110:10d10:20:50:0
+W:20:1:2000:70
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:8d5
+B:HIT:HURT:8d5
+F:MALE |
+F:FORCE_MAXHP | FRIENDS | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | ORC | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:ARROW_2
+D:It is a cunning orc of power, taller than a man, and stronger. It fears
+D:little.
+
+N:867:The Philosophy Teacher
+G:p:r
+I:120:50d100:30:50:0
+W:56:1:1900:20000
+E:1:1:1:2:1:1
+O:20:20:20:20
+B:GAZE:CONFUSE:15d5
+B:GAZE:PARALYZE:15d5
+B:TOUCH:INSANITY:5d5
+B:TOUCH:EXP_40:5d5
+F:FEMALE | UNIQUE | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD | DROP_GREAT |
+F:CAN_SPEAK | WEIRD_MIND | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | NO_CONF | NO_SLEEP | JOKEANGBAND | HAS_LITE
+S:1_IN_3
+S:BR_CONF | BR_SOUN | BR_CHAO | BR_TIME |
+S:HOLD | SCARE | SLOW | CONF | FORGET
+D:She is a feared teacher, mistress of chaos and strangely minded.
+D:She wants to take your mind. Fear her and flee while you can.
+
+N:868:The Variant Maintainer
+G:p:B
+I:160:10d10:30:50:0
+W:10:1:1700:3000
+E:1:1:1:2:1:1
+O:20:20:20:20
+B:INSULT:*
+B:HIT:INSANITY:1d8
+B:HIT:LOSE_WIS:1d8
+F:ONLY_ITEM | DROP_2D2 | UNIQUE | CAN_FLY |
+F:DROP_GOOD | WEIRD_MIND | CAN_SPEAK |
+F:RAND_50 | RAND_25 | INVISIBLE | EVIL |
+F:MORTAL | JOKEANGBAND | HAS_LITE
+S:1_IN_2
+S:S_BUG | S_RNG | BR_CONF
+D:A deranged programmer, scattering bizarre ideas and bad code everywhere.
+
+N:869:Random Number Generator
+G:I:b
+I:130:1d6:10:50:0
+W:10:1:0:10
+E:0:0:0:0:0:0
+O:20:20:20:20
+B:INSULT:*
+B:MOAN:*
+B:HIT:CONFUSE:1d6
+F:DROP_1D2 | EVIL |
+F:EMPTY_MIND | RAND_50 | RAND_25 | JOKEANGBAND
+S:MULTIPLY
+D:A feared creation of the Variant Maintainer, it tries to generate Morgoth
+D:in the town and the One Ring in the magic shop.
+
+N:870:Rocket mine
+G:.:R
+I:110:20d8:20:3:10
+W:50:10:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:POISON:10d5
+F:NEVER_MOVE | IM_POIS | NONLIVING | IM_ACID | STUPID |
+F:FORCE_MAXHP | UNDEAD | EVIL | JOKEANGBAND | NO_CUT
+S:1_IN_4
+S:ROCKET | ARROW_4
+D:It was left here to be used against intruders.
+
+N:871:Bouncing mine
+G:.:B
+I:120:20d15:50:5:10
+W:70:10:0:200
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:POISON:10d5
+F:NEVER_MOVE | IM_POIS | NONLIVING | IM_ACID | STUPID |
+F:FORCE_MAXHP | UNDEAD | EVIL | JOKEANGBAND | NO_CUT
+S:1_IN_3
+S:ROCKET | ARROW_4 | BLINK | BR_POIS | BR_CHAO | BR_NEXU
+D:It was left here to be used against intruders.
+
+N:872:Durin's Bane
+G:U:v
+I:130:30d100:20:100:80
+W:50:3:13000:30000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:FIRE:6d12
+B:HIT:FIRE:6d12
+B:CRUSH:HURT:5d12
+B:TOUCH:UN_POWER
+F:UNIQUE | MALE | SPECIAL_GENE | DROP_RANDART
+F:FORCE_SLEEP | FORCE_MAXHP | KILL_WALL |
+F:ESCORT | ESCORTS | DROP_CORPSE | NONLIVING |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_CHOSEN |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON | IM_FIRE |
+F:NO_CONF | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:CONF | SCARE |
+S:BR_FIRE |
+S:S_UNDEAD | S_DEMON
+D:A huge Balrog surrounded by raging pillars of fire, this is indeed a
+D:terrible opponent. Wielding a great whip of fire and a blazing sword, his
+D:fury blisters your skin and melts your flesh!
+
+N:873:The Icky Queen
+G:i:v
+I:120:40d10:20:50:10
+W:20:5:3000:400
+E:0:0:0:0:0:0
+O:40:30:10:10
+B:CRAWL:POISON:3d4
+B:CRAWL:EAT_FOOD:3d4
+B:TOUCH:ACID:3d5
+B:HIT:HURT:3d5
+F:UNIQUE | FEMALE |
+F:FORCE_MAXHP | CAN_SPEAK | SMART | ESCORT | ESCORTS |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | DROP_CORPSE |
+F:WEIRD_MIND | TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:DRAIN_MANA | BLIND | CONF | SCARE | S_KIN
+D:And you thought her offspring were icky!
+
+N:874:Rot jelly
+G:j:u
+I:120:20d8:2:30:99
+W:5:1:2000:15
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:EAT_FOOD:2d3
+B:TOUCH:LOSE_CHR:2d3
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND | CAN_SWIM |
+F:HURT_LITE | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a large pile of rotting flesh, whose touch spoils your food. The terrible
+D:smell it exudes is also very hard to get rid of...
+
+N:875:Death
+G:G:D
+I:130:50d100:200:120:5
+W:80:6:3000:25000
+E:1:1:1:2:1:1
+O:10:80:20:0
+B:HIT:HURT:20d5
+B:HIT:HURT:20d5
+B:TOUCH:EXP_80:20d5
+B:TOUCH:EXP_80:20d5
+F:UNIQUE |
+F:FORCE_MAXHP | DROP_CORPSE |
+F:DROP_GOOD | DROP_GREAT | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | PASS_WALL | NO_SLEEP | NO_STUN | NO_CONF |
+F:EVIL | UNDEAD | JOKEANGBAND | NO_CUT
+D:"And I looked, and behold a pale horse: and his name that sat on him was
+D:Death, and Hell followed with him. And power was given unto them over the
+D:fourth part of the earth, to kill with sword, and with hunger, and with
+D:death, and with the beasts of the earth."
+
+N:876:Famine
+G:G:U
+I:130:50d100:200:120:5
+W:77:6:3000:25000
+E:1:1:1:2:1:1
+O:10:80:20:0
+B:TOUCH:EAT_FOOD:20d4
+B:GAZE:UN_BONUS:20d4
+B:WAIL:LOSE_INT:10d5
+B:WAIL:LOSE_DEX:10d5
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | DROP_CORPSE |
+F:DROP_GOOD | DROP_GREAT | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | PASS_WALL | NO_STUN | NO_SLEEP | NO_CONF |
+F:EVIL | UNDEAD | JOKEANGBAND | NO_CUT
+D:One of the horsemen of the apocalypse, before you lies Famine. A
+D:figure so gaunt that the shape of the bones beneath are revealed,
+D:Famine rides a pale grey mare that appears near death.
+
+N:877:Pestilence
+G:G:G
+I:130:50d100:200:120:5
+W:74:6:3000:25000
+E:1:1:1:2:1:1
+O:10:80:20:0
+B:TOUCH:POISON:20d4
+B:TOUCH:POISON:20d4
+B:TOUCH:DISEASE:16d5
+B:TOUCH:DISEASE:16d5
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | DROP_CORPSE |
+F:DROP_GOOD | DROP_GREAT | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | PASS_WALL | NO_SLEEP | NO_STUN | NO_CONF |
+F:EVIL | UNDEAD | JOKEANGBAND | NO_CUT
+S:1_IN_2 |
+S:S_ANT | S_SPIDER
+D:One of the horsemen of the apocalypse, before you lies Pestilence.
+D:At first, it looks like a human, but then you notice ants, worms, and
+D:worse peeking out of the flesh. Pestilence rides a purple horse with
+D:skin that bulges and occasionally gives hints of the vermin within.
+
+N:878:War
+G:G:r
+I:130:50d100:200:120:5
+W:71:6:3000:25000
+E:1:1:1:2:1:1
+O:10:80:20:0
+B:TOUCH:PARALYZE:20d4
+B:WAIL:CONFUSE:20d4
+B:GAZE:BLIND:20d4
+B:WAIL:TERRIFY:20d4
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | DROP_CORPSE |
+F:DROP_GOOD | DROP_GREAT | DROP_60 | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | PASS_WALL | NO_SLEEP | NO_STUN | NO_CONF | NO_FEAR |
+F:EVIL | UNDEAD | JOKEANGBAND | NO_CUT
+S:1_IN_2 |
+S:S_MONSTER
+D:One of the horsemen of the apocalypse, before you lies War. A healthy and
+D:hearty warrior, War grins a little too wide at you as he prepares for
+D:combat. War rides a large well-groomed yellow horse that leaves behind
+D:puddles of blood where its hooves touch the ground.
+
+##### Some aquatic monsters. #####
+
+N:879:Pike
+G:~:s
+I:125:2d7:80:35:0
+W:2:1:100:7
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d3
+F:ANIMAL | AQUATIC | STUPID | DROP_CORPSE | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:It's a common fresh-water predatory fish.
+
+N:880:Electric eel
+G:J:B
+I:110:15d15:15:40:70
+W:20:2:500:145
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:TOUCH:ELEC:2d7
+B:TOUCH:ELEC:2d7
+B:TOUCH:ELEC:2d7
+F:AQUATIC | ANIMAL | RAND_25 | IM_ELEC | RES_WATE |
+F:WILD_TOO | WILD_OCEAN | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:This serpentine creature can create a deadly voltage. Better watch out!
+
+N:881:Giant crayfish
+G:~:R
+I:90:4d10:6:100:20
+W:4:1:1200:10
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d4
+B:CLAW:HURT:3d4
+F:ANIMAL | AQUATIC | STUPID | WEIRD_MIND | DROP_CORPSE | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:A man-sized, heavily armoured fresh-water relative of the lobster.
+
+N:882:Mermaid
+G:h:G
+I:110:5d8:50:30:0
+W:4:1:1600:20
+E:1:1:1:2:1:0
+O:20:50:10:5
+B:TOUCH:LOSE_WIS
+B:TOUCH:INSANITY:2d3
+B:TOUCH:CONFUSE
+F:FEMALE | RAND_25 | DROP_60 | SMART | AQUATIC | NO_CONF | DROP_CORPSE |
+F:MORTAL | ZANGBAND
+D:A green-skinned humanoid with a fishtail. Beware - there are rumours
+D:of adventures losing their minds under the fearsome
+D:charms of mermaids.
+
+N:883:Box jellyfish
+G:~:B
+I:110:10d10:20:30:75
+W:10:2:3000:25
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:PARALYZE:1d6
+B:TOUCH:PARALYZE:1d6
+F:ANIMAL | AQUATIC | IM_POIS | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:A strange water creature, whose touch can be deadly.
+
+N:884:Giant piranha
+G:~:g
+I:120:6d8:30:20:10
+W:10:2:300:40
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:5d1
+B:BITE:HURT:5d1
+F:NO_SLEEP | WILD_TOO | COLD_BLOOD |
+F:FRIENDS | AQUATIC | ANIMAL |
+F:MORTAL | BASEANGBAND
+D:A very large and bloodthirsty fish.
+
+N:885:Piranha
+G:~:g
+I:120:2d6:20:8:5
+W:3:1:200:8
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:FRIENDS | AQUATIC | ANIMAL | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:Bloodthirsty fish who can smell your blood from a great distance.
+
+N:886:Bullywug
+G:h:g
+I:110:6d10:20:15:0
+W:7:1:700:25
+E:1:1:1:2:1:1
+O:30:40:10:10
+B:HIT:HURT:2d4
+B:HIT:HURT:2d4
+F:MALE | ONLY_ITEM | DROP_1D2 | SMART | AQUATIC | DROP_CORPSE | FRIENDS |
+F:MORTAL | BASEANGBAND | HAS_LITE | COLD_BLOOD
+D:A vaguely humanoid creature, it looks like a cross between a hobbit and a
+D:frog.
+
+N:887:Bullywug warrior
+G:h:g
+I:110:8d10:20:15:0
+W:8:1:750:35
+E:1:1:1:2:1:1
+O:15:60:10:10
+B:HIT:HURT:2d5
+B:HIT:HURT:2d5
+F:MALE | ONLY_ITEM | DROP_1D2 | SMART | AQUATIC | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE | COLD_BLOOD
+D:A member of the frog-people clan. He is wielding a long, sharp shark tooth.
+
+N:888:Bullywug shaman
+G:h:g
+I:110:6d10:20:15:0
+W:8:1:800:35
+E:1:1:1:2:1:1
+O:25:10:50:10
+B:HIT:HURT:1d6
+B:HIT:HURT:1d6
+F:MALE | ONLY_ITEM | DROP_1D2 | SMART | AQUATIC | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE | COLD_BLOOD
+S:1_IN_5 |
+S:BLIND | CONF | DARKNESS | BO_COLD | HEAL | MISSILE | CAUSE_2
+D:A leader of a clan of frog-people, he is cloaked in a cloak made of
+D:shark skin.
+
+N:889:Whale
+G:~:G
+I:110:22d22:15:50:70
+W:20:4:9000:175
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CRUSH:HURT:1d20
+B:CRUSH:HURT:1d20
+F:RAND_25 | FORCE_MAXHP | RES_WATE |
+F:ANIMAL | AQUATIC | WILD_TOO |
+F:MORTAL | BASEANGBAND
+D:Although it looks like a fish and lives in water, it is in fact
+D:a mammal. And it is huge!
+
+N:890:Sand mite
+G:~:B
+I:110:3d10:5:25:80
+W:10:2:500:25
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:POISON:3d6
+B:BITE:POISON:3d6
+F:AQUATIC | ANIMAL | IM_POIS | FRIENDS |
+F:CHAR_CLEAR | ATTR_CLEAR | DROP_CORPSE | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+S:MULTIPLY
+D:A relative of crabs and shrimp, this is a tiny creature that inhabits sandy
+D:bottoms. It has a pair of dangerous-looking claws.
+
+N:891:Octopus
+G:~:g
+I:105:60d6:60:60:60
+W:15:2:1200:60
+E:3:0:3:6:1:0
+O:0:0:0:0
+B:SPIT:BLIND:1d3
+B:CRUSH:HURT:6d3
+B:CRUSH:HURT:6d3
+B:CRUSH:HURT:6d3
+F:RAND_25 | IM_COLD | RES_WATE | AQUATIC | ANIMAL | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:It doesn't move very fast, but when it does - watch out!
+
+N:892:Giant octopus
+G:~:g
+I:115:100d6:10:35:5
+W:30:1:1800:180
+E:3:0:3:6:1:0
+O:0:0:0:0
+B:SPIT:BLIND:1d4
+B:CRUSH:HURT:8d4
+B:CRUSH:HURT:8d4
+B:CRUSH:PARALYZE:8d4
+F:AQUATIC | SMART | IM_POIS | ANIMAL | NO_CONF | NO_SLEEP |
+F:NO_FEAR | SMART | WEIRD_MIND | DROP_CORPSE | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+S:1_IN_15 |
+S:DARKNESS | SLOW | CONF | SCARE
+D:A cunning and dangerous undersea opponent.
+
+N:893:Eye of the deep
+G:e:b
+I:120:16d100:10:20:40
+W:40:3:1600:6000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:EXP_20:2d6
+B:GAZE:UN_POWER:2d6
+B:GAZE:INSANITY:2d6
+B:BITE:HURT:6d6
+F:EVIL | IM_POIS | NO_CONF | NO_SLEEP | AQUATIC |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY | RES_TELE |
+F:SMART | DROP_CORPSE | BASEANGBAND
+S:1_IN_2 |
+S:BLIND | SLOW | CONF | SCARE | DRAIN_MANA | MIND_BLAST |
+S:FORGET | DARKNESS | BO_WATE | BO_ICEE | BO_MANA | BO_COLD
+D:A beholder that inhabits the depths of the sea, sleeping and pondering
+D:alien thoughts for centuries. Occasionally, it will float to the
+D:surface to wreck the lives of surface dwellers.
+
+N:894:Murk dweller
+G:S:s
+I:110:200d5:70:30:0
+W:27:3:3000:800
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:5d10
+B:CLAW:PARALYZE
+B:STING:INSANITY:5d10
+B:STING:UN_BONUS
+F:AQUATIC | ANIMAL | WEIRD_MIND | FORCE_MAXHP | HURT_LITE |
+F:EVIL | SMART | NO_CONF | NO_SLEEP | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_5 |
+S:BA_POIS | BR_DARK | BLIND | BR_POIS | SLOW | CONF | MIND_BLAST |
+S:BRAIN_SMASH | DARKNESS
+D:A gigantic aquatic monster, somewhat resembling a cross between a
+D:lobster and a spider. It is coated in poisonous slime and noxious
+D:parasites. This foul creature hides in the silt of the deep ocean floor,
+D:waiting to trap unsuspecting prey.
+
+N:895:Drowned soul
+G:G:B
+I:110:9d8:5:33:50
+W:11:1:0:30
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:BLIND:3d3
+B:TOUCH:POISON:2d4
+B:WAIL:TERRIFY
+F:COLD_BLOOD | EMPTY_MIND | EVIL | AQUATIC | UNDEAD | IM_COLD | INVISIBLE |
+F:IM_POIS | NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_8
+S:BLIND | HOLD | CONF
+D:A ghastly victim of drowning, forever doomed to wander the ocean waters
+D:looking for revenge.
+
+N:896:Tiger shark
+G:~:g
+I:120:10d5:100:32:0
+W:12:1:3000:40
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:4d4
+F:RAND_25 | AQUATIC | ANIMAL | STUPID | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A small species of shark, although the teeth are still as deadly.
+
+N:897:Hammerhead shark
+G:~:g
+I:115:16d10:20:59:20
+W:16:3:1500:40
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:3d4
+B:BUTT:HURT:3d4
+B:BITE:HURT:3d4
+F:ANIMAL | AQUATIC | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:A hungry shark with a strange head.
+
+N:898:Great white shark
+G:~:w
+I:120:100d6:20:70:20
+W:24:2:5000:250
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:4d6
+B:BITE:HURT:4d6
+B:BITE:HURT:4d6
+F:FORCE_SLEEP | AQUATIC | COLD_BLOOD |
+F:ANIMAL MORTAL | BASEANGBAND
+D:A very large carnivorous fish.
+
+N:899:Aquatic golem
+G:g:b
+I:100:25d10:35:75:10
+W:19:1:0:100
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:3d10
+B:HIT:HURT:3d10
+F:COLD_BLOOD | EMPTY_MIND | AQUATIC |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:An ingenious gnomish invention -- a golem designed for underwater
+D:usage.
+
+N:900:Aquatic kobold
+G:k:B
+I:110:13d9:10:30:15
+W:5:1:1500:35
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:1d10
+F:DROP_90 | EVIL | IM_POIS | AQUATIC | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:Yes, your favourite denizen of evil also comes in an aquatic variety.
+
+N:901:White shark
+G:~:W
+I:120:30d10:20:50:10
+W:18:1:3000:100
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:3d5
+B:BITE:HURT:3d5
+B:BITE:HURT:3d5
+F:ANIMAL | AQUATIC | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:A fast-moving hunter of the depths. When this creature moves,
+D:everybody in the water is in danger!
+
+N:902:Scrag
+G:T:B
+I:110:40d10:20:50:50
+W:35:1:5000:440
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+B:HIT:HURT:3d2
+B:HIT:HURT:3d2
+F:MALE |
+F:FORCE_MAXHP | AQUATIC | REGENERATE |
+F:FRIENDS | DROP_60 | WILD_TOO | WILD_OCEAN |
+F:OPEN_DOOR | BASH_DOOR | RES_WATE |
+F:EVIL | TROLL | IM_COLD | IM_POIS | HURT_LITE | BASEANGBAND
+D:A troll of the sea, he reeks of brine.
+
+N:903:Jaws
+G:~:w
+I:130:100d20:200:80:70
+W:40:2:7000:2000
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:11d2
+B:BITE:HURT:22d1
+B:BITE:HURT:11d2
+B:BITE:HURT:22d1
+F:FORCE_MAXHP | UNIQUE | MOVE_BODY |
+F:WILD_OCEAN | WILD_TOO | COLD_BLOOD |
+F:BASH_DOOR | IM_COLD | IM_ELEC | IM_POIS | ANIMAL | AQUATIC |
+F:NO_CONF | NO_SLEEP |
+F:MORTAL | ZANGBAND
+D:The biggest white shark who has ever lived, it is hunting for you now!
+
+N:904:Aquatic elf
+G:h:b
+I:110:14d8:30:30:6
+W:9:1:1400:25
+E:1:1:1:2:1:0
+O:20:20:50:10
+B:HIT:HURT:3d4
+F:MALE | AQUATIC | SMART | DROP_CORPSE |
+F:FRIENDS | DROP_60 | ZANGBAND | HAS_LITE
+D:A sleek form vaguely shaped like a dolphin, except with a humanoid
+D:head and arms. The facial features are decidedly elven.
+
+N:905:Aquatic elven warrior
+G:h:b
+I:110:20d8:40:35:5
+W:10:1:1500:35
+E:1:1:1:2:1:0
+O:20:60:10:10
+B:HIT:HURT:4d4
+B:HIT:HURT:4d4
+F:MALE | AQUATIC | FRIENDS | SMART | DROP_60 | DROP_CORPSE | ZANGBAND |
+F:HAS_LITE
+D:An aquatic elf trained in all forms of combat.
+
+N:906:Aquatic elven shaman
+G:h:b
+I:110:12d8:30:30:6
+W:10:1:1400:35
+E:1:1:1:2:1:0
+O:10:10:70:10
+B:TOUCH:UN_BONUS
+F:MALE | AQUATIC | SMART | DROP_2D2 | DROP_CORPSE | ZANGBAND |
+F:HAS_LITE
+S:1_IN_12 |
+S:BO_MANA | BO_COLD | S_MONSTERS
+D:A wizened aquatic elf skilled in the magical arts. You can see an
+D:iridescent film coating the water around him.
+
+N:907:Stargazer
+G:~:y
+I:100:15d9:10:25:30
+W:21:1:800:60
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:GAZE:PARALYZE
+F:AQUATIC | ANIMAL | SMART | RAND_25 | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:S_MONSTER
+D:A giant fish shaped like a flounder. There are two enormous eyes
+D:occupying half of the creature's body.
+
+N:908:Elder stargazer
+G:~:y
+I:100:20d10:15:25:30
+W:29:1:1000:75
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:GAZE:PARALYZE
+B:GAZE:CONFUSE
+F:AQUATIC | ANIMAL | SMART | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:S_MONSTERS
+D:A stargazer a bit larger than average, covered with barnacles.
+
+N:909:Flounder
+G:~:s
+I:100:10d5:5:25:30
+W:13:1:700:55
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d6
+F:AQUATIC | ANIMAL | RAND_25 | INVISIBLE | ATTR_CLEAR | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A flattened fish which is able to change body colouring for
+D:camouflage.
+
+N:910:Giant turtle
+G:R:G
+I:110:5d8:10:14:30
+W:7:1:3000:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:2d2
+B:BITE:HURT:2d2
+F:ANIMAL | AQUATIC | DROP_CORPSE |
+F:MORTAL | BASEANGBAND
+D:A giant turtle with flippers, adapted for life in the ocean.
+
+N:911:Baby dragon turtle
+G:d:W
+I:110:10d10:20:60:50
+W:9:1:20000:35
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d3
+B:CLAW:HURT:1d3
+B:BITE:HURT:1d5
+F:FORCE_MAXHP | FORCE_SLEEP | DROP_CORPSE |
+F:ONLY_GOLD | DROP_60 | DROP_1D2 |
+F:EVIL | DRAGON | AQUATIC | BASEANGBAND | ATTR_MULTI
+S:1_IN_12 |
+S:BR_SOUN
+D:A newly-hatched dragon turtle. It still hasn't grown a proper shell.
+
+N:912:Young dragon turtle
+G:d:W
+I:110:30d10:20:70:70
+W:31:1:80000:700
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d6
+B:CLAW:HURT:2d6
+B:BITE:HURT:2d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:DROP_3D2 |
+F:EVIL | DRAGON | AQUATIC | BASEANGBAND | ATTR_MULTI
+S:1_IN_11 |
+S:SCARE |
+S:BR_SOUN
+D:A dragon-like creature inhabiting lightless reaches of ocean caves. It has
+D:a long neck with a tiny head, and four massive flippers. It has a soft and
+D:flexible shell.
+
+N:913:Mature dragon turtle
+G:d:W
+I:110:50d10:20:80:70
+W:38:1:170000:1500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:2d10
+B:CLAW:HURT:2d10
+B:BITE:HURT:4d10
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:DROP_4D2 |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | AQUATIC | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_9 |
+S:SCARE |
+S:BR_SOUN
+D:A large dragon turtle, covered with a tough white shell.
+
+N:914:Ancient dragon turtle
+G:D:W
+I:120:70d10:20:90:80
+W:41:1:220000:2500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d8
+B:CLAW:HURT:4d8
+B:BITE:HURT:7d8
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE |
+F:DROP_1D2 | DROP_4D2 |
+F:SMART | AQUATIC | POWERFUL | MOVE_BODY |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_9 |
+S:BLIND | CONF | SCARE |
+S:BR_SOUN
+D:A huge dragon turtle. You can see many barnacles covering its body.
+
+N:915:Fastitocalon
+G:D:g
+I:120:40d100:25:150:30
+W:52:3:250000:16000
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:BITE:HURT:5d8
+B:BITE:HURT:5d8
+B:CRUSH:POISON:3d10
+B:CRUSH:POISON:3d10
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | AQUATIC | WILD_TOO | WILD_OCEAN |
+F:IM_FIRE | IM_ACID | IM_COLD | IM_POIS |
+F:DRAGON | NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | ATTR_MULTI
+S:1_IN_6 |
+S:BR_FIRE | BR_ACID | BR_SOUN | BA_WATE
+D:A huge aquatic dragon-turtle, its shell is as large as a small island.
+
+N:916:Undead stargazer
+G:~:y
+I:100:18d9:10:25:30
+W:25:1:0:100
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:GAZE:PARALYZE
+B:GAZE:EXP_20
+F:AQUATIC | ANIMAL | SMART | INVISIBLE | UNDEAD | BASEANGBAND | NO_CUT
+S:1_IN_10 |
+S:S_UNDEAD | S_MONSTER
+D:A stargazer brought back from the dead under control of some unholy
+D:sorceror.
+
+N:917:Killer whale
+G:~:w
+I:120:20d50:12:55:30
+W:25:1:9500:85
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:7d4
+B:BITE:HURT:7d4
+F:AQUATIC | WILD_TOO | WILD_OCEAN |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:An almost beautiful, deadly beast.
+
+N:918:Merrow
+G:O:B
+I:110:30d9:20:33:30
+W:28:2:2300:80
+E:1:1:1:2:1:1
+O:20:70:0:10
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+F:FRIENDS | DROP_60 | DROP_CORPSE | AQUATIC | WILD_TOO | WILD_OCEAN |
+F:OPEN_DOOR | BASH_DOOR | RES_WATE | IM_COLD | IM_POIS |
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A great ogre of the sea, it is violent and stupid.
+
+N:919:Water naga
+G:n:B
+I:110:30d10:10:55:10
+W:24:1:1600:60
+E:0:0:0:0:1:0
+O:40:0:50:10
+B:CRUSH:HURT:2d8
+B:CRUSH:HURT:2d8
+B:BITE:POISON:1d8
+B:BITE:POISON:1d8
+F:FEMALE |
+F:AQUATIC | DROP_CORPSE |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 |
+F:EVIL | NO_CONF | NO_SLEEP | SMART |
+F:MORTAL | BASEANGBAND
+S:1_IN_7 |
+S:DARKNESS | CAUSE_3 | BO_ICEE
+D:A naga adapted to underwater life. She has a fish-like tail and a pair
+D:of gills.
+
+N:920:Devilfish
+G:~:s
+I:105:10d5:10:10:5
+W:12:1:900:60
+E:0:0:0:0:1:0
+O:0:0:0:0
+F:NEVER_BLOW | ATTR_MULTI | AQUATIC | NO_CONF | DROP_CORPSE |
+F:MORTAL | ZANGBAND
+S:1_IN_4 |
+S:BR_CHAO | BR_LITE | BR_SOUN | BR_DISE | BR_TIME
+D:A disgusting fish, it has a large gaping mouth and a small lantern dangling
+D:from an outgrowth on its head.
+
+N:921:Undead devilfish
+G:~:D
+I:105:10d5:10:10:5
+W:15:1:0:75
+E:0:0:0:0:1:0
+O:0:0:0:0
+F:NEVER_BLOW | ATTR_MULTI | AQUATIC | INVISIBLE | NO_CONF | UNDEAD |
+F:ZANGBAND | NO_CUT
+S:1_IN_4 |
+S:BR_NETH | BR_DISE | BR_TIME | BR_NEXU | BR_POIS
+D:A devilfish brought back from the dead.
+
+N:922:Moby Dick, the White Whale
+G:~:w
+I:120:100d25:200:80:70
+W:50:2:10000:2500
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CRUSH:HURT:10d10
+B:CRUSH:HURT:10d10
+B:CRUSH:HURT:10d10
+B:CRUSH:HURT:10d10
+F:FORCE_MAXHP | UNIQUE | MOVE_BODY |
+F:WILD_OCEAN | WILD_TOO | COLD_BLOOD |
+F:BASH_DOOR | IM_COLD | IM_ELEC | IM_POIS | ANIMAL | AQUATIC |
+F:MORTAL | JOKEANGBAND
+S:1_IN_6 | BA_WATE
+D:The mightiest whale of the seas, he has sunk many ships in his time. With
+D:a mere flick of his tail he can create a mighty whirlpool, to the ruin
+D:of all who would travel the ocean.
+
+N:923:Aquatic hound
+G:Z:B
+I:110:15d5:60:60:0
+W:20:1:600:200
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:CONFUSE:2d8
+B:BITE:HURT:2d8
+B:CLAW:POISON:2d8
+F:FRIENDS | DROP_CORPSE |
+F:ANIMAL | AQUATIC |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BO_ICEE | BO_WATE
+D:A dog with a finned tail and large, muscular flippers for hind legs.
+D:It has a rubbery skin instead of fur.
+
+N:924:Water demon
+G:U:B
+I:110:35d20:30:50:10
+W:40:1:0:1000
+E:0:0:0:0:1:0
+O:20:60:20:0
+B:HIT:HURT:3d4
+B:GAZE:POISON:8d12
+B:CLAW:INSANITY:8d12
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:FRIENDS | SMART |
+F:ONLY_ITEM | DROP_60 |
+F:AQUATIC | POWERFUL |
+F:EVIL | DEMON | IM_ACID | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+S:1_IN_8 |
+S:BO_ICEE | BA_WATE
+D:A hideous scaled demon, it is a sleek form with many fins but no visible
+D:arms or legs. It has a toothed gaping maw, reminiscent of a caricature
+D:of a shark's jaws.
+
+N:925:Ixitxachitl
+G:~:s
+I:110:12d8:20:30:20
+W:12:1:1600:40
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:STING:POISON:2d5
+B:STING:POISON:2d5
+F:ANIMAL | EVIL | AQUATIC | IM_POIS | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+D:An intelligent devil-ray of the depths.
+
+N:926:Ixitxachitl priest
+G:~:s
+I:110:10d10:20:40:20
+W:19:1:1600:80
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:STING:POISON:2d7
+B:STING:POISON:2d7
+F:ANIMAL | EVIL | AQUATIC | IM_POIS | WILD_TOO | COLD_BLOOD |
+F:MORTAL | BASEANGBAND
+S:1_IN_6
+S:TELE_TO | HEAL | SCARE | CAUSE_2 | BLIND | S_MONSTER
+D:A devil-ray of the depths, with priestly magic.
+
+N:927:Vampiric ixitxachitl
+G:~:D
+I:110:15d15:20:50:20
+W:26:1:1800:120
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:STING:POISON:3d8
+B:STING:EXP_40:3d8
+F:ANIMAL | EVIL | AQUATIC | RES_NETH | IM_POIS | WILD_TOO |
+F:BASEANGBAND | COLD_BLOOD
+S:1_IN_6
+S:HEAL | SCARE | CAUSE_3 | BLIND | FORGET | HASTE
+D:A devil-ray of the depths, with vampiric powers.
+
+N:928:Mathilde, the Science Student
+G:h:y
+I:110:220d100:40:10:3
+W:0:4:1100:0
+E:0:1:1:2:1:1
+O:20:20:20:20
+F:UNIQUE | FEMALE | CAN_SPEAK | DROP_CORPSE | DROP_SKELETON |
+F:FORCE_MAXHP | WILD_TOWN | WILD_ONLY | RAND_25
+F:ONLY_ITEM | DROP_90 | DROP_GOOD |
+F:OPEN_DOOR | DG_CURSE |
+F:MORTAL | JOKEANGBAND | HAS_LITE
+D:She loves joking, and she's constantly giggling. A very happy girl.
+D:Beware, it is rumoured that Dark God has put a mighty curse on her.
+
+
+N:929:Child spirit
+G:G:W
+I:120:5d5:8:15:10
+W:5:3:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:TERRIFY
+B:TOUCH:HURT:3d4
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | UNDEAD |
+F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | SMART | PET | UNDEAD |
+F:BASEANGBAND | NO_CUT
+D:A helpful spirit from beyond the grave.
+
+N:930:Young spirit
+G:G:W
+I:120:8d8:8:15:10
+W:10:3:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:TERRIFY
+B:TOUCH:HURT:9d4
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | UNDEAD |
+F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | SMART | PET | UNDEAD |
+F:BASEANGBAND | NO_CUT
+D:A helpful spirit from beyond the grave.
+
+N:931:Mature spirit
+G:G:W
+I:120:16d16:8:15:10
+W:40:3:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:TERRIFY
+B:TOUCH:HURT:18d4
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | UNDEAD |
+F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | SMART | PET | UNDEAD |
+F:BASEANGBAND | NO_CUT
+D:A helpful spirit from beyond the grave.
+
+N:932:Experienced spirit
+G:G:W
+I:120:18d18:8:15:10
+W:60:3:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:TERRIFY
+B:TOUCH:HURT:20d4
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | UNDEAD |
+F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | SMART | PET | UNDEAD |
+F:BASEANGBAND | NO_CUT
+D:A helpful spirit from beyond the grave.
+
+N:933:Wise spirit
+G:G:W
+I:120:25d25:8:15:10
+W:90:3:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:TERRIFY
+B:TOUCH:HURT:30d4
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | UNDEAD |
+F:IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | SMART | PET | UNDEAD |
+F:BASEANGBAND | NO_CUT
+D:A helpful spirit from beyond the grave.
+
+N:934:Fangorn the Treebeard, Lord of the Ents
+G:#:G
+I:120:50d100:30:120:15
+W:52:3:6000:15500
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:CRUSH:HURT:13d13
+B:CRUSH:HURT:13d13
+B:CRUSH:HURT:13d13
+B:CRUSH:HURT:13d13
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | CAN_SPEAK | SUSCEP_FIRE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | MOVE_BODY | DROP_CORPSE |
+F:SMART | TAKE_ITEM | BASH_DOOR | KILL_WALL | NO_SLEEP |
+F:GOOD | PET | BASEANGBAND | NO_CUT
+D:The first being to awoke on Arda, apart from the Valar themselves. He is the
+D:first, oldest, greatest and most respected of all the Ents: and though he is
+D:slow to anger, he is a terrible foe when roused.
+
+N:935:Gandalf the Grey
+G:p:s
+I:120:49d101:101:100:0
+W:60:7:1600:35000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_BONUS:6d8
+B:HIT:TERRIFY:5d5
+B:HIT:TERRIFY:5d5
+F:UNIQUE | MALE | CAN_SPEAK | PET | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | RES_TELE |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:GOOD | IM_FIRE | IM_COLD |
+F:IM_ELEC | IM_POIS | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE |
+S:CAUSE_4 | BRAIN_SMASH | FORGET | TRAPS |
+S:BA_FIRE | BO_FIRE | BO_PLAS | BO_MANA | CAUSE_4 |
+S:S_MONSTERS | S_ANGEL | S_DRAGON | S_KIN
+D:The wizard who opposed Sauron and, in the end, was the only
+D:one of the Istari to succeed in his task. Gandalf is very
+D:wise and specialises in fire magic.
+
+N:936:Nar, the Dwarf
+G:h:y
+I:110:45d10:25:70:25
+W:17:2:1400:250
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:UNIQUE | MALE |
+F:FORCE_MAXHP | FORCE_SLEEP |
+F:OPEN_DOOR | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_POIS |
+F:DROP_CORPSE | DROP_SKELETON | BASEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:HEAL | BLIND | CONF | CAUSE_2 | MIND_BLAST
+D:The friend and companion of the dwarven king Thror, he went mad with
+D:grief after Thror's death at the hands of Azog the Orc. With torn beard
+D:and ragged clothes, he seems to have fixed on you as a convenient target
+D:to vent his anger.
+
+N:937:Novice mindcrafter
+G:p:y
+I:110:6d8:20:10:5
+W:8:1:900:18
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:1d6
+F:DROP_60 | WILD_TOO | FRIENDS |
+F:OPEN_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE | NO_CONF | NO_SLEEP
+S:1_IN_12 | BLIND | SLOW | CONF | SCARE
+D:A novice in the arts of mind over matter.
+
+N:938:Great Swamp Wyrm
+G:D:g
+I:120:40d100:30:150:80
+W:63:2:190000:20000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:POISON:6d14
+B:BITE:POISON:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE |
+F:EVIL | DRAGON | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_POIS
+D:A truly enormous dragon with great powers. The foul gases issuing
+D:from the beast nearly make you vomit; and while you may try to hold
+D:your breath as you fight it, it sees no reason to do likewise.
+
+N:939:Great Bile Wyrm
+G:D:s
+I:120:50d100:30:150:80
+W:67:2:190000:23000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:ACID:6d14
+B:BITE:ACID:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE |
+F:EVIL | DRAGON | IM_ACID | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_ACID
+D:A huge and very powerful dragon. Great steaming pools of acid drip from
+D:its form onto the ground. You shudder when you see the acid eating away
+D:the very stones of the dungeon - what could it do to you?
+
+N:940:Blue Firebird
+G:B:B
+I:120:4d5:8:15:10
+W:5:3:100:0
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d4
+B:CLAW:FIRE:5d4
+F:IM_COLD | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | IMPRESED |
+F:CAN_FLY | SMART | PET | DROP_CORPSE | HAS_EGG | GOOD |
+F:MORTAL | HAS_LITE
+S:1_IN_10
+S:TELE_TO |
+D:The ancestors of the Eagles of the Thunderlords, these are
+D:friendly funny flying creatures with power.
+
+N:941:Green Firebird
+G:B:G
+I:120:4d5:10:15:10
+W:7:3:110:0
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:4d4
+B:CLAW:FIRE:5d4
+F:IM_COLD | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | IMPRESED |
+F:CAN_FLY | SMART | PET | DROP_CORPSE | HAS_EGG | GOOD |
+F:MORTAL | HAS_LITE
+S:1_IN_10
+S:TELE_TO |
+D:The ancestors of the Eagles of the Thunderlords, these are
+D:friendly funny flying creatures with power.
+
+N:942:Brown Firebird
+G:B:u
+I:120:5d5:10:15:10
+W:10:3:120:0
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:5d4
+B:CLAW:FIRE:7d4
+F:IM_COLD | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | IMPRESED |
+F:CAN_FLY | SMART | PET | DROP_CORPSE | HAS_EGG | GOOD |
+F:MORTAL | HAS_LITE
+S:1_IN_10
+S:TELE_TO |
+D:The ancestors of the Eagles of the Thunderlords, these are
+D:friendly funny flying creatures with power.
+
+N:943:Bronze Firebird
+G:B:U
+I:120:6d5:15:15:10
+W:13:3:130:0
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:6d4
+B:CLAW:FIRE:7d4
+F:IM_COLD | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | IMPRESED |
+F:CAN_FLY | SMART | PET | DROP_CORPSE | HAS_EGG | GOOD |
+F:MORTAL | HAS_LITE
+S:1_IN_10
+S:TELE_TO |
+D:The ancestors of the Eagles of the Thunderlords, these are
+D:friendly funny flying creatures with power.
+
+N:944:Gold Firebird
+G:B:y
+I:120:6d5:20:15:10
+W:15:3:140:0
+E:0:1:0:6:1:0
+O:0:0:0:0
+B:CLAW:HURT:6d4
+B:CLAW:FIRE:8d4
+F:IM_COLD | IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP | IMPRESED |
+F:CAN_FLY | SMART | PET | DROP_CORPSE | HAS_EGG | GOOD |
+F:MORTAL | HAS_LITE
+S:1_IN_10
+S:TELE_TO |
+D:The ancestors of the Eagles of the Thunderlords, these are
+D:friendly funny flying creatures with power.
+
+N:945:High-elven ranger
+G:h:G
+I:120:50d30:20:70:0
+W:40:3:1400:500
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:10d8
+B:HIT:HURT:10d8
+F:MALE | FRIENDS | OPEN_DOOR | BASH_DOOR |
+F:GOOD | DROP_SKELETON | DROP_CORPSE | SMART | PET |
+F:IM_ACID | IM_COLD | RES_WATE | RES_NETH |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:ARROW_4
+D:An elf cloaked in green wielding a longbow.
+
+N:946:Uvatha the Horseman
+G:W:D
+I:120:24d100:90:60:10
+W:40:13:0:10000
+E:0:0:0:0:0:0
+O:30:30:30:10
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:HIT:EXP_80:4d6
+B:HIT:EXP_80:4d6
+F:UNIQUE | MALE |
+F:FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | NAZGUL |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | DROP_CHOSEN |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP |
+F:BASEANGBAND | SPECIAL_GENE | NO_CUT
+D:A tall black Ringwraith, he is a master of horsemanship. He longs
+D:to taste your blood.
+
+N:947:Adunaphel the Quiet
+G:W:D
+I:120:27d100:90:60:10
+W:43:13:0:13000
+E:1:1:1:2:1:1
+O:30:30:30:10
+B:HIT:HURT:6d6
+B:HIT:HURT:6d6
+B:TOUCH:EXP_80:5d6
+B:TOUCH:EXP_80:5d6
+F:UNIQUE | FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP | NAZGUL | DROP_CHOSEN |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | MOVE_BODY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP |
+F:BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | FORGET |
+S:BO_ACID | BO_FIRE | BO_COLD | BO_NETH |
+S:S_MONSTER
+D:A sorceress in life, Adunaphel quickly fell under Sauron's sway and the
+D:power of the rings.
+
+N:948:Akhorahil the Blind
+G:W:D
+I:120:30d100:90:70:10
+W:45:13:0:15000
+E:1:1:1:2:1:1
+O:30:30:30:10
+B:HIT:HURT:7d6
+B:HIT:HURT:7d6
+B:GAZE:EXP_80:6d6
+B:WAIL:TERRIFY:6d6
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | NAZGUL | DROP_CHOSEN |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | DARKNESS |
+S:BO_FIRE | BO_COLD | BO_NETH |
+S:S_MONSTERS
+D:A mighty sorcerer king, Akhorahil was blind in life. With powerful
+D:enchantments, he created jewelled eyes that enabled him to see better than
+D:any ordinary man ever could.
+
+N:949:Ren the Unclean
+G:W:D
+I:120:35d100:90:70:10
+W:48:13:0:18000
+E:1:1:1:2:1:1
+O:30:30:30:10
+B:HIT:HURT:7d7
+B:HIT:HURT:7d7
+B:TOUCH:EXP_80:6d7
+B:WAIL:TERRIFY:6d7
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | NAZGUL | DROP_CHOSEN |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:INVISIBLE | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | BO_FIRE | BO_NETH | BA_FIRE |
+S:S_MONSTER
+D:Ren was an insane eastern king who believed himself to be the son of a
+D:volcano god. At an early age his sanity was destroyed by a plague that
+D:wiped out his family, and he never recovered.
+
+N:950:Ji Indur Dawndeath
+G:W:D
+I:120:40d100:90:70:10
+W:52:13:0:22000
+E:0:0:0:0:0:0
+O:30:30:30:10
+B:HIT:HURT:8d7
+B:HIT:HURT:8d7
+B:TOUCH:EXP_40:6d7
+B:TOUCH:EXP_40:6d7
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | NAZGUL | DROP_CHOSEN |
+F:INVISIBLE | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 |
+S:BA_FIRE | BA_NETH | BA_COLD | BA_ELEC | BA_ACID |
+S:S_UNDEAD
+D:This Ringwraith was a weak-minded sorcerer-king who fell easily under
+D:Sauron's power.
+
+N:951:Dwar, Dog Lord of Waw
+G:W:D
+I:120:45d100:90:90:10
+W:56:13:0:25000
+E:0:1:0:2:1:0
+O:30:30:30:10
+B:HIT:HURT:8d8
+B:HIT:HURT:8d8
+B:BITE:EXP_40:6d7
+B:WAIL:TERRIFY:6d7
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | NAZGUL | DROP_CHOSEN |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | BA_FIRE | BA_NETH |
+S:S_MONSTERS | S_UNDEAD | S_HOUND
+D:Dwar had a special affinity for dogs in life, and can still command them
+D:at will. He howls manically as he reaches out to destroy you.
+
+N:952:Hoarmurath of Dir
+G:W:D
+I:130:60d100:90:100:10
+W:64:13:0:30000
+E:0:0:0:0:0:0
+O:30:30:30:10
+B:HIT:HURT:9d9
+B:HIT:HURT:9d9
+B:TOUCH:EXP_80:6d7
+B:WAIL:TERRIFY:6d7
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | NAZGUL | DROP_CHOSEN |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | MIND_BLAST |
+S:BO_COLD | BA_COLD | BA_NETH |
+S:S_UNDEAD
+D:A Ringwraith powerful in fell sorcery, he yearns for the life he has
+D:exchanged for an unlife of everlasting torment.
+
+N:953:Khamul, the Black Easterling
+G:W:D
+I:130:70d100:90:100:10
+W:72:13:2600:40000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:9d10
+B:HIT:HURT:9d10
+B:TOUCH:EXP_80:7d7
+B:TOUCH:EXP_80:7d7
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | NAZGUL | DROP_CHOSEN |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | IM_ACID | IM_FIRE | IM_COLD | IM_POIS |
+F:HURT_LITE | NO_CONF | NO_SLEEP | RES_TELE | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:TELE_LEVEL | BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | BO_MANA |
+S:BA_FIRE | BA_COLD | BA_NETH | ANIM_DEAD |
+S:S_HI_UNDEAD | S_KIN
+D:He was the warrior-king of the East, now a Ringwraith. Khamul is a powerful opponent, his skill in
+D:combat awesome and his form twisted by evil cunning.
+
+N:954:The Witch-King of Angmar
+G:W:D
+I:130:90d100:90:120:10
+W:80:14:1800:60000
+E:1:1:1:2:1:1
+O:30:30:30:10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:EXP_80:7d7
+B:HIT:EXP_80:7d7
+F:UNIQUE | MALE | CAN_SPEAK | RES_TELE
+F:FORCE_SLEEP | FORCE_MAXHP | SMART |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY | NAZGUL |
+F:EVIL | UNDEAD | DROP_CHOSEN |
+F:IM_COLD | IM_POIS | HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:TELE_AWAY | BLIND | HOLD | SCARE | CAUSE_3 | BRAIN_SMASH |
+S:BO_MANA | BA_NETH | S_WRAITH |
+S:S_KIN | S_HI_UNDEAD | S_HI_DRAGON | S_MONSTERS | ANIM_DEAD
+D:The Chief of the Ringwraiths. A fell being of devastating power, his
+D:spells are lethal and his combat blows crushingly hard. He moves at
+D:speed, and commands legions of evil to do his bidding. It is said that he
+D:is fated never to die by the hand of mortal man.
+
+N:955:Green Thunderlord
+G:B:g
+I:120:50d50:20:100:50
+W:30:4:30000:10000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+F:FEMALE |
+F:THUNDERLORD | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:IM_FIRE | REGENERATE | OPEN_DOOR | BASH_DOOR |
+F:RES_TELE | NO_CONF | NO_SLEEP | CAN_FLY | HAS_LITE
+S:1_IN_4 |
+S:BR_FIRE |
+S:BLINK | TELE_AWAY
+D:A Thunderlord. Among the weaker breeds, but still dangerous.
+
+N:956:Blue Thunderlord
+G:B:b
+I:120:60d60:20:100:50
+W:40:4:30000:20000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:8d7
+B:HIT:HURT:8d7
+F:MALE | SMART |
+F:THUNDERLORD | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:IM_FIRE | REGENERATE | OPEN_DOOR | BASH_DOOR |
+F:RES_TELE | NO_CONF | NO_SLEEP | CAN_FLY | HAS_LITE
+S:1_IN_4 |
+S:BR_FIRE |
+S:TPORT | BLINK
+D:A Thunderlord. Among the weaker breeds, but still dangerous.
+
+N:957:Brown Thunderlord
+G:B:u
+I:130:70d70:30:100:50
+W:50:4:30000:20000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:8d9
+B:HIT:HURT:8d9
+F:MALE | SMART |
+F:THUNDERLORD | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:IM_FIRE | REGENERATE | OPEN_DOOR | BASH_DOOR |
+F:RES_TELE | NO_CONF | NO_SLEEP | CAN_FLY | HAS_LITE
+S:1_IN_4 |
+S:BR_FIRE |
+S:TPORT | BLINK
+D:A Thunderlord. Beware its flame.
+
+N:958:Bronze Thunderlord
+G:B:U
+I:130:80d80:30:100:50
+W:60:5:30000:20000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:10d9
+B:HIT:HURT:10d9
+F:MALE | SMART |
+F:THUNDERLORD | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:IM_FIRE | REGENERATE | OPEN_DOOR | BASH_DOOR |
+F:RES_TELE | NO_CONF | NO_SLEEP | CAN_FLY | HAS_LITE
+S:1_IN_4 |
+S:BR_FIRE | BR_TIME |
+S:TPORT | BLINK
+D:A Thunderlord, mightiest among the males.
+
+N:959:Gold Thunderlord
+G:B:y
+I:130:90d90:30:100:50
+W:70:5:30000:20000
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:HIT:HURT:10d9
+B:HIT:HURT:10d9
+B:HIT:HURT:10d9
+B:HIT:HURT:10d9
+F:FEMALE | SMART |
+F:THUNDERLORD | DROP_SKELETON | DROP_CORPSE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:IM_FIRE | REGENERATE | OPEN_DOOR | BASH_DOOR |
+F:RES_TELE | NO_CONF | NO_SLEEP | CAN_FLY | HAS_LITE
+S:1_IN_4 |
+S:BR_FIRE | BR_TIME |
+S:TPORT | BLINK | TELE_TO | TELE_AWAY |
+S:S_THUNDERLORD
+D:A Thunderlord, among the queens of their kind.
+
+N:960:Blood Sprout
+G:,:g
+I:140:3d5:10:1:0
+W:50:1:50:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:3d15
+B:TOUCH:HURT:3d15
+B:TOUCH:HURT:3d15
+B:TOUCH:HURT:3d15
+F:STUPID | EMPTY_MIND | KILL_TREES |
+F:IM_POIS | IM_ELEC | IM_ACID | IM_COLD |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL
+S:MULTIPLY
+D:A kind of giant mycorrhiza, corrupted into a carnivore by Morgoth.
+
+N:961:Gorlim, Betrayer of Barahir
+G:p:s
+I:120:16d100:20:120:40
+W:41:3:1800:7000
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:8d6
+B:HIT:HURT:8d6
+B:HIT:UN_BONUS:6d8
+B:HIT:UN_BONUS:6d8
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DROP_SKELETON |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP | BASEANGBAND
+F:HAS_LITE
+S:1_IN_2 |
+S:CAUSE_3 | BO_WATE | BO_MANA
+D:This sad creature - once a mighty warrior - betrayed his former friends to
+D:Morgoth's army in return for, he thought, safety for himself and his wife.
+D:And so he fell under Morgoth's power and became little more than a mindless
+D:servant of evil, even though the other side of his "bargain" was not kept.
+
+N:962:The Blubbering idiot, agent of black market, Simon the weak
+G:t:W
+I:110:5d11:10:16:0
+W:5:1:1500:50
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:2d5
+B:TOUCH:EAT_ITEM
+B:TOUCH:INSANITY:2d5
+B:TOUCH:INSANITY:2d5
+F:MALE | DROP_CORPSE | DROP_SKELETON | WILD_TOO |
+F:RAND_25 | UNIQUE | STUPID | MALE | FORCE_MAXHP |
+F:TAKE_ITEM | CAN_SPEAK | CAN_SWIM | EVIL |
+F:MORTAL | JOKEANGBAND | HAS_LITE
+S:1_IN_8
+S:SHRIEK
+D:He tends to blubber a lot, he was a simple blubbering idiot before he lost
+D:himself in the dungeon and fell under Morgoth's will.
+D:Now he is a mindless servant of Morgoth. Destroy him - if you can...
+
+N:963:Aranea
+G:S:r
+I:120:20d10:20:45:50
+W:30:2:1000:250
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:POISON:2d8
+B:CLAW:POISON:2d8
+B:BITE:POISON:2d6
+B:BITE:POISON:2d6
+F:DROP_SKELETON | FORCE_MAXHP | FRIENDS |
+F:OPEN_DOOR | BASH_DOOR | HURT_LITE |
+F:ANIMAL | SPIDER | EVIL | IM_POIS | BASEANGBAND
+S:1_IN_4
+S:BO_FIRE | SLOW | HOLD | CAUSE_3 | MISSILE
+D:A red arachnid with legs weaving spells in the air.
+
+N:964:Elder aranea
+G:S:v
+I:120:30d20:20:65:50
+W:40:3:1800:2500
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:POISON:5d8
+B:CLAW:POISON:5d8
+B:BITE:HALLU:5d6
+B:BITE:HALLU:5d6
+F:DROP_SKELETON | FORCE_MAXHP |
+F:OPEN_DOOR | BASH_DOOR | HURT_LITE | SMART |
+F:ANIMAL | SPIDER | EVIL | IM_FIRE | IM_POIS | BASEANGBAND
+S:1_IN_6
+S:SLOW | HOLD | DRAIN_MANA | MIND_BLAST | HEAL |
+S:BA_FIRE | BO_FIRE | CAUSE_3 | S_SPIDER
+D:A vast, bloated arachnid, master of its brood: among the more terrible of
+D:Ungoliant's descendants, this is a monster such as those who haunted the dread
+D:valley of Nan Dungortheb long ago.
+
+N:965:Giant brown tick
+G:S:u
+I:110:16d8:12:50:20
+W:25:2:400:27
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:2d6
+B:STING:BLIND:1d1
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY |
+F:ANIMAL | IM_POIS | MORTAL | BASEANGBAND
+D:It is moving slowly towards you.
+
+N:966:Wavelord
+G:p:b
+I:120:16d100:30:120:40
+W:61:3:1800:7000
+E:1:1:1:2:1:1
+O:20:50:10:5
+B:HIT:HURT:8d9
+B:HIT:COLD:8d9
+B:HIT:HURT:8d9
+B:HIT:COLD:8d9
+F:SMART | PET | DROP_CORPSE | FRIENDS | WILD_SHORE | WILD_OCEAN | WILD_TOO |
+F:IM_POIS | AQUATIC | CAN_SWIM | DROP_USEFUL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | DROP_GOOD | DROP_3D2 |
+F:MORTAL | REGENERATE | TAKE_ITEM | GOOD | SUSCEP_FIRE |
+F:RES_WATE | RES_NEXU | HAS_LITE
+S:1_IN_4
+S:BO_WATE | BA_WATE | BO_ICEE
+D:The Dolphiners came with the Thunderlords from their far home.
+D:These friendly beings ride their sharks into combat to assist you.
+
+N:967:Novice possessor (soul)
+G:G:D
+I:110:1d1:30:1:10
+W:10:3:10:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:SMART | POSSESSOR |
+F:BASEANGBAND | NO_CUT
+D:It does not look that powerful.
+
+N:968:Bat of Gorgoroth
+G:b:g
+I:120:20d10:20:30:30
+W:28:3:150:100
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:POISON:1d10
+B:CLAW:HURT:1d4
+F:DROP_60 | RAND_25 | MOVE_BODY | CAN_FLY | DROP_CORPSE |
+F:BASH_DOOR | MOVE_BODY | FRIENDS | WEIRD_MIND |
+F:ANIMAL | IM_POIS | AI_ANNOY | MORTAL | BASEANGBAND
+S:1_IN_8 |
+S:SCARE | BR_POIS | BR_DARK
+D:Fed with horrid meats and grown to enormous size, this slavering creature
+D:seeks livelier prey.
+
+N:969:The Princess
+G:p:y
+I:110:1d1:40:250:3
+W:0:4:730:0
+E:0:1:1:2:1:1
+O:0:0:1:0
+F:FEMALE | CAN_SPEAK
+F:FORCE_MAXHP | SPECIAL_GENE | NO_TARGET
+F:NEVER_MOVE | NEVER_BLOW | GOOD | NO_DEATH | RES_TELE
+F:MORTAL | BASEANGBAND | UNIQUE | PET |
+D:The princess of an unknown kingdom, you need to save her!
+
+N:970:Merton Proudfoot, the lost hobbit
+G:h:v
+I:110:1d1:40:250:3
+W:1:1:730:0
+E:0:1:1:2:1:1
+O:0:0:0:1
+F:MALE | CAN_SPEAK
+F:FORCE_MAXHP | SPECIAL_GENE
+F:NEVER_MOVE | NEVER_BLOW | GOOD | NO_TARGET
+F:MORTAL | BASEANGBAND | UNIQUE | PET | NO_DEATH
+D:The poor hobbit got lost in the dreadful maze!
+
+N:971:The Wight-King of the Barrow-downs
+G:W:v
+I:120:38d22:30:45:0
+W:46:3:0:22000
+E:0:0:0:0:0:0
+O:15:55:15:0
+B:HIT:HURT:2d10
+B:WAIL:PARALYZE:2d6
+B:TOUCH:EXP_80:4d8
+B:TOUCH:EXP_80:4d8
+F:SPECIAL_GENE | FORCE_MAXHP | SMART | CAN_SPEAK |
+F:DROP_4D2 | DROP_2D2 | COLD_BLOOD | UNIQUE |
+F:CAN_SWIM | CAN_FLY | EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:HOLD | SCARE | CAUSE_3 | BA_NETH
+D:He has lived in the Barrow-Downs for centuries after his first death at the
+D:hands of the Witch-King of Angmar. A once loyal captain under the
+D:Witch-King's command, he now awaits a time when his undead forces shall
+D:rise again to avenge their deaths.
+
+N:972:Adventurer
+G:@:U
+I:115:3d20:50:50:10
+W:0:3:100:0
+O:50:50:0:0
+B:HIT:HURT:2d6
+B:PUNCH:HURT:1d7
+B:KICK:HURT:1d8
+F:SPECIAL_GENE | SMART | OPEN_DOOR
+F:FORCE_MAXHP | CAN_SWIM | BASEANGBAND |
+F:NO_SLEEP | NO_CONF
+D:A great warrior.
+
+N:973:Experienced possessor (soul)
+G:G:D
+I:120:5d5:30:50:10
+W:30:3:10:0
+O:0:0:0:0
+F:SMART | POSSESSOR |
+F:BASEANGBAND | NO_CUT
+S:1_IN_9
+S:S_KIN
+D:It does not look that powerful.
+
+N:974:Old possessor (soul)
+G:G:D
+I:130:10d10:30:100:10
+W:95:3:10:0
+O:0:0:0:0
+F:SMART | POSSESSOR |
+F:BASEANGBAND | NO_CUT
+S:1_IN_4
+S:S_KIN
+D:It does not look that powerful.
+
+N:975:Death orb
+G:E:D
+I:130:40d100:255:75:0
+W:80:5:10:5000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:PARASITE:20d10
+B:ENGULF:ACID:6d5
+B:ENGULF:EXP_80:6d5
+B:ENGULF:LOSE_ALL:6d5
+F:FORCE_MAXHP | ZANGBAND | KILL_ITEM | KILL_WALL | KILL_BODY |
+F:NONLIVING | COLD_BLOOD | EMPTY_MIND | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_STUN | REGENERATE |
+F:IM_ACID | IM_FIRE | IM_ELEC | IM_COLD | IM_POIS |
+F:RES_NETH | RES_PLAS | RES_WATE | RES_DISE | RES_NEXU |
+F:DEATH_ORB | NO_CUT
+D:A small, apparently unassuming orb which spawns death and destruction.
+
+N:976:Bronze dragon worm
+G:w:U
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | NO_CONF |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_CONF
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. Its scales glitter in
+D:a multitude of perplexing and distracting ways.
+
+N:977:Gold dragon worm
+G:w:y
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | NO_STUN |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_SOUN
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. You can feel the air itself
+D:vibrating as you near it.
+
+N:978:Moldoux, the Defenceless Mold
+G:m:v
+I:100:1d1:1:1:0
+W:1:1:20:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:NEVER_MOVE | UNIQUE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | FORCE_DEPTH |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:MORTAL | WYRM_PROTECT | ONLY_DEPTH | JOKEANGBAND | NO_CUT
+D:A small strange growth. It seems to be defenceless...
+
+N:979:The Physics Teacher
+G:p:w
+I:120:65d100:30:120:15
+W:62:3:100000:13500
+E:1:1:1:2:1:1
+O:20:20:20:20
+B:TOUCH:LOSE_INT:13d13
+B:TOUCH:LOSE_WIS:13d13
+B:GAZE:PARALYZE
+B:WAIL:EXP_80
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_SPEAK | DROP_CORPSE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:PASS_WALL | MOVE_BODY | AURA_FIRE |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | ANIMAL | DEMON | UNDEAD | JOKEANGBAND | NO_CUT
+S:1_IN_3
+S:BR_FIRE | BR_PLAS | BR_TIME | BA_WATE | ROCKET |
+S:FORGET
+D:A keeper of forbidden wisdom, this Teacher bombards you with his mastery
+D:over elements of matter.
+
+N:980:Ar-Pharazon the Golden
+G:p:y
+I:130:50d100:30:45:255
+W:55:1:0:2500
+E:1:1:1:2:1:1
+O:20:70:10:0
+B:HIT:HURT:8d12
+B:HIT:HURT:8d12
+B:HIT:HURT:8d12
+B:HIT:HURT:8d12
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | CAN_SPEAK | SPECIAL_GENE |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD | DROP_SKELETON | DROP_CORPSE |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | MOVE_BODY | BASEANGBAND |
+F:HAS_LITE | AQUATIC
+S:1_IN_6 |
+S:HEAL | HASTE | TELE_AWAY | S_MONSTERS | S_KIN
+D:Last and proudest king of ancient Numenor. Corrupted by power and
+D:avarice, he fell victim to Sauron's wiles, tried to fight the immortals
+D:themselves, and condemned Numenor to oblivion.
+
+N:981:Doppelganger
+G:@:w
+I:1:1d1:1:1:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:NEVER_MOVE | NEVER_BLOW | DOPPLEGANGER | BASEANGBAND | HAS_LITE
+D:It looks like you...
+
+N:982:Marylene, Heartbreakeress of the Netherworld
+G:P:W
+I:155:200d120:155:175:0
+W:127:1:1600:66666
+E:1:1:1:2:1:1
+O:30:30:30:10
+B:GAZE:PARALYZE:20d15
+B:HIT:EXP_80:20d17
+B:BITE:LOSE_ALL:10d12
+B:TOUCH:INSANITY:12d9
+F:UNIQUE | CAN_SPEAK | ATTR_MULTI | ATTR_ANY | FEMALE |
+F:FORCE_MAXHP | WEIRD_MIND | DROP_CORPSE | DROP_SKELETON |
+F:REFLECTING | AURA_FIRE | AURA_ELEC | AURA_COLD |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_3D2 | DROP_4D2 |
+F:DROP_GOOD | DROP_GREAT | RES_NETH | INVISIBLE |
+F:SMART | KILL_WALL | KILL_BODY | POWERFUL | RES_TELE |
+F:REGENERATE | CAN_FLY | CAN_SWIM | WYRM_PROTECT |
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN | RES_TELE | DEMON |
+F:MORTAL | DG_CURSE | JOKEANGBAND | HAS_LITE
+S:1_IN_1 |
+S:BR_CHAO | BA_CHAO | BRAIN_SMASH | SHRIEK | BR_CONF | BR_SOUN |
+S:BR_NETH | HASTE | TRAPS | FORGET | BR_DISE | BR_TIME | MIND_BLAST |
+S:HEAL | TPORT | TELE_TO | CAUSE_1 | CAUSE_2 | CAUSE_3 | CAUSE_4 | BLIND |
+S:CONF | SLOW | HOLD | S_ANGEL | S_UNIQUE
+D:A woman of mind-shattering beauty, none can match her beauty. She is perfect,
+D:and totally evil. She loves nothing but herself and her evil is as
+D:great as her beauty. No one can stand against her, even DarkGod.
+D:As you see her approaching, you feel your heart breaking.
+D:She is the perfection, don't even try against her; you will surely lose
+D:your mind...
+
+N:983:The Greater Lag Monster
+G:U:v
+I:110:60d100:40:10:3
+W:95:4:730:0
+E:0:0:0:0:0:0
+O:40:40:0:10
+B:GAZE:PARALYZE:20d15
+B:HIT:HURT:15d10
+B:TOUCH:EXP_80:10d5
+B:TOUCH:EAT_ITEM
+F:RAND_50 | TAKE_ITEM | FORCE_MAXHP | UNIQUE |
+F:DROP_3D2 | DROP_GOOD | DROP_GREAT | WYRM_PROTECT |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN | RES_TELE | DEMON |
+F:EVIL | WEIRD_MIND | RES_NETH | INVISIBLE |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | JOKEANGBAND | NO_CUT
+S:1_IN_2
+S:CONF | SHRIEK | BR_CONF | BR_TIME | BR_INER | BR_GRAV | BRAIN_SMASH |
+S:SCARE | BLIND | SLOW | HOLD | HASTE | HEAL | TELE_TO | FORGET |
+S:S_HI_DEMON | S_MONSTERS | S_HYDRA | S_HI_DRAGON | S_HI_UNDEAD | S_ANGEL |
+S:S_HOUND | S_UNIQUE |
+D:It is even worse than the RNG, coming from Mangband,
+D:it seeks to crush you to pulp... and surely can.
+
+N:984:Hrungnir, the Stone Giant
+G:P:W
+I:130:99d110:50:160:20
+W:78:2:0:45000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:SHATTER:9d11
+B:HIT:SHATTER:9d11
+B:HIT:HURT:9d11
+B:HIT:HURT:9d11
+F:UNIQUE | SUSCEP_ACID | HURT_ROCK |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_PLAS | MALE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | EVIL |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | CAN_SPEAK |
+F:EVIL | IM_FIRE | IM_POIS | GIANT |
+F:ZANGBAND | HAS_LITE | NO_CUT
+S:1_IN_5 |
+S:BR_WALL | BR_GRAV | BR_SHAR | BLIND | TELE_TO | S_KIN | S_MONSTERS |
+S:HAND_DOOM | TELE_AWAY |
+D:Hrungnir is the strongest of all the Stone Giants: one who would even dare
+D:to challenge Thor himself to single combat.
+
+N:985:Bullroarer the Hobbit
+G:h:U
+I:120:6d10:16:8:10
+W:5:3:1000:90
+E:1:1:1:2:1:1
+O:25:55:0:20
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DROP_SKELETON |
+F:FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He is a sturdy hobbit who is renowned for his unusual strength and
+D:vigour. He can prove a troublesome opponent.
+
+N:986:3-headed hydra
+G:M:o
+I:120:100d5:20:65:20
+W:20:2:4500:350
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+B:BITE:HURT:2d6
+F:FORCE_SLEEP | WILD_TOO | WILD_SHORE | WILD_SWAMP | CAN_SWIM |
+F:DROP_CORPSE | DROP_SKELETON | ONLY_GOLD | DROP_2D2 |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:1_IN_9 |
+S:SCARE
+D:A strange reptilian creature with three heads, guarding its hoard.
+
+N:987:Uldor the Accursed
+G:p:U
+I:110:10d100:20:70:40
+W:28:4:2000:600
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DROP_SKELETON |
+F:FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_10 | S_KIN
+D:An evil and cunning man from the East. Having once sworn allegiance to the
+D:sons of Feanor, it was Uldor's treachery that turned the tide of the Battle
+D:of Unnumbered Tears in Morgoth's favour.
+
+N:988:Mystic
+G:p:o
+I:120:35d10:30:50:5
+W:33:3:1400:500
+E:1:1:1:2:1:1
+O:30:0:60:10
+B:KICK:HURT:10d2
+B:KICK:HURT:10d2
+B:KICK:HURT:10d2
+B:KICK:HURT:10d2
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DROP_SKELETON |
+F:ONLY_ITEM | DROP_1D2 |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_POIS | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:HEAL |
+S:S_SPIDER | S_ANIMAL
+D:An adept at unarmed combat, the mystic strikes with stunning power. He
+D:can summon help from nature and is able to focus his power to ease any
+D:pain.
+
+N:989:Elder vampire
+G:V:r
+I:120:34d100:20:90:10
+W:54:3:1700:4500
+E:1:1:1:2:1:1
+O:0:70:30:0
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:BITE:EXP_80:5d6
+B:BITE:EXP_80:5d6
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:DROP_60 | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | RES_TELE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_7 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | DARKNESS | BO_NETH | S_UNDEAD
+D:A terrible robed undead figure, this creature has existed in its
+D:unlife for many centuries by stealing the life of others. It can
+D:summon the very shades of its victims from beyond the grave to
+D:come enslaved to its aid.
+
+N:990:Ulfang the Black
+G:p:U
+I:120:25d100:20:90:40
+W:44:5:2100:1200
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:5d8
+B:HIT:HURT:5d8
+B:HIT:HURT:5d8
+B:HIT:HURT:5d8
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE | DROP_SKELETON |
+F:FORCE_MAXHP | IM_FIRE | IM_COLD | IM_ELEC | SPECIAL_GENE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_RANDART
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_10 | S_KIN | S_MONSTERS
+D:A short and swarthy Easterling dressed in black. He and his three sons
+D:once openly swore allegiance to the High Elves, but were secretly in the
+D:pay of Morgoth.
+
+N:991:Demonologist
+G:p:R
+I:120:28d10:20:50:10
+W:36:2:1100:700
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DROP_SKELETON
+F:RES_NETH | RES_NEXU |
+F:ONLY_ITEM | DROP_1D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:TPORT | HOLD |
+S:S_DEMON | BO_FIRE
+D:A figure twisted by evil standing in robes of deepest crimson.
+
+N:992:Hezrou
+G:U:g
+I:110:52d10:20:40:80
+W:41:3:2100:2500
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:FORCE_SLEEP | FORCE_MAXHP | FRIENDS
+F:ONLY_ITEM | DROP_2D2 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING
+F:BASEANGBAND
+S:1_IN_9 |
+S:BO_FIRE |
+S:S_DEMON
+D:It is a demon of lizard form with cruel-looking jaws.
+
+N:993:Glabrezu
+G:U:U
+I:110:70d10:20:40:80
+W:43:2:2300:3000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND
+S:1_IN_9 |
+S:BO_FIRE |
+S:S_DEMON
+D:It is a demon with arms and pincers, its form a true mockery of life.
+
+N:994:Nalfeshnee
+G:U:r
+I:110:90d10:20:50:80
+W:45:2:6000:5000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+B:HIT:HURT:3d4
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE
+F:ONLY_ITEM | DROP_1D2 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON |
+F:IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING
+F:BASEANGBAND
+S:1_IN_9 |
+S:BLIND | CONF |
+S:BR_FIRE |
+S:S_DEMON
+D:It is a large demon with the head of a giant boar. Flames run up and down
+D:its length.
+
+N:995:Marilith
+G:U:y
+I:120:20d70:20:75:80
+W:47:2:3000:7000
+E:3:0:3:4:1:0
+O:0:50:50:0
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_1D2 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND
+S:1_IN_9 |
+S:BLIND | CAUSE_2 |
+S:S_DEMON
+D:She is a demon of female form with many arms, each bearing deadly weapons.
+
+N:996:Lesser Balrog
+G:U:v
+I:120:20d100:20:50:80
+W:49:2:9000:10000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:FIRE:4d12
+B:HIT:FIRE:4d12
+B:CRUSH:HURT:3d12
+B:TOUCH:UN_POWER
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_FIRE | KILL_WALL |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLIND | CONF |
+S:BR_FIRE |
+S:S_DEMON
+D:It is a massive humanoid demon wreathed in flames.
+
+N:997:Master mystic
+G:p:o
+I:130:11d100:30:60:5
+W:50:3:1600:6000
+E:1:1:1:2:1:1
+O:40:0:40:20
+B:KICK:HURT:10d2
+B:KICK:HURT:10d2
+B:HIT:POISON:20d1
+B:HIT:PARALYZE:15d1
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DROP_SKELETON |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL |
+S:S_SPIDER | S_ANIMALS
+D:A lord of all that is natural, skilled in the mystic ways. He is a master
+D:of martial arts and is at one with nature, able to summon help from the
+D:wild if need be.
+
+N:998:Grand master mystic
+G:p:o
+I:130:22d100:30:80:5
+W:57:3:1800:15000
+E:1:1:1:2:1:1
+O:40:0:40:20
+B:KICK:HURT:20d2
+B:KICK:HURT:10d2
+B:HIT:POISON:20d1
+B:HIT:PARALYZE:15d1
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | DROP_CORPSE | DROP_SKELETON |
+F:ONLY_ITEM | DROP_4D2 |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | MIND_BLAST |
+S:S_SPIDER | S_HOUND | S_ANIMALS
+D:He is one of the few true masters of the art, being extremely skillful in
+D:all forms of unarmed combat and controlling the world's natural creatures
+D:with disdainful ease.
+
+N:999:Erinyes
+G:U:u
+I:110:24d10:20:50:80
+W:38:2:0:1000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:TOUCH:LOSE_STR:1d5
+F:FEMALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON |
+F:IM_FIRE | IM_POIS | NO_CONF | NO_SLEEP |
+F:BASEANGBAND
+S:1_IN_7 |
+S:BLIND | CONF | BO_FIRE
+D:It is a lesser demon of female form; however, she takes little time to
+D:show her true colours.
+
+N:1000:Novice mindcrafter
+G:p:y
+I:110:6d8:20:10:5
+W:4:1:900:18
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:1d6
+F:DROP_60 | WILD_TOO |
+F:OPEN_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE | NO_CONF | NO_SLEEP
+S:1_IN_9 | BLIND | SLOW | CONF | SCARE
+D:A novice in the arts of mind over matter.
+
+N:1001:Polyphemus, the Blind Cyclops
+G:P:g
+I:130:65d100:40:140:40
+W:69:3:40000:29000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:11d10
+B:HIT:SHATTER:11d10
+B:HIT:HURT:11d10
+B:HIT:SHATTER:11d10
+F:UNIQUE | MALE | BASEANGBAND | HAS_LITE | CAN_SWIM |
+F:FORCE_SLEEP | FORCE_MAXHP | RAND_25 | DROP_CORPSE |
+F:DROP_2D2 | DROP_3D2 | DROP_GOOD | ONLY_ITEM | KILL_ITEM |
+F:BASH_DOOR | KILL_BODY |
+F:EVIL | GIANT | IM_ACID | IM_POIS | IM_FIRE | IM_COLD |
+S:1_IN_5 |
+S:BO_WATE | BA_WATE | BO_ICEE | BA_ACID | ARROW_4
+D:No ordinary cyclops, but the son of a sea-god: he wields the powers of
+D:elemental water as well as the considerable strength of a cyclops. His
+D:one eye was blinded long ago by the warrior Odysseus, but he has trained
+D:himself to hear so well that it nearly makes up for his disability.
+
+N:1002:Great Wyrm of Perplexity
+G:D:U
+I:120:40d100:30:150:80
+W:63:2:190000:20000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:CONFUSE:6d14
+B:BITE:CONFUSE:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:SMART | BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | BASEANGBAND | HAS_LITE | ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_CONF
+D:A dragon of great size and power. Its polished bronze scales reflect the
+D:light in strange and confusing patterns, and you find it hard to keep your
+D:mind on the job of fighting for your life.
+
+N:1003:Hound of Tindalos
+G:Z:s
+I:120:60d15:30:100:0
+W:59:3:600:8000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:TIME:2d12
+B:BITE:TIME:2d12
+B:CLAW:HURT:2d12
+B:CLAW:HURT:2d12
+F:FORCE_SLEEP | FRIENDS | RES_NETH |
+F:INVISIBLE | PASS_WALL | EVIL | CAN_FLY |
+F:ANIMAL | NO_CONF | NO_SLEEP | CTHANGBAND
+S:1_IN_5 | BLINK | TELE_TO |
+S:BR_NETH | BR_TIME
+D:"They are lean and athirst!... All the evil in the universe
+D:was concentrated in their lean, hungry bodies. Or had they
+D:bodies? I saw them only for a moment, I cannot be certain."
+
+N:1004:Great Wyrm of Thunder
+G:D:y
+I:120:50d100:30:150:80
+W:67:2:190000:23000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:4d12
+B:CLAW:HURT:4d12
+B:BITE:HURT:6d14
+B:BITE:HURT:6d14
+F:FORCE_SLEEP | FORCE_MAXHP | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:BASH_DOOR | POWERFUL | MOVE_BODY | DROP_CORPSE |
+F:EVIL | DRAGON | NO_CONF | NO_SLEEP | NO_STUN | BASEANGBAND | HAS_LITE
+F:ATTR_MULTI
+S:1_IN_4 |
+S:BLIND | CONF | SCARE |
+S:BR_SOUN
+D:A dragon of gigantic proportions, with destructive abilities to match. The
+D:sheer loudness of its roar leaves you stunned and unable to think clearly
+D:enough to defend yourself adequately.
+
+N:1005:Silver mouse
+G:r:W
+I:110:1d3:8:4:20
+W:4:1:200:1
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:EAT_LITE:1d2
+F:RAND_25 | CAN_SWIM | ANIMAL |
+F:DROP_CORPSE | MORTAL | BASEANGBAND
+S:MULTIPLY
+D:It is about three feet long with large teeth. As the light of your lamp falls
+D:on it, it seems to grow stronger.
+
+N:1006:The Rat King
+G:r:v
+I:120:20d12:30:30:0
+W:18:1:950:32
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:CLAW:HURT:3d2
+B:CLAW:HURT:3d2
+B:BITE:DISEASE:4d2
+B:BITE:DISEASE:4d2
+F:UNIQUE | ESCORT | ESCORTS | FORCE_MAXHP |
+F:BASH_DOOR | ANIMAL | NO_CONF | NO_SLEEP
+F:MORTAL | BASEANGBAND
+D:A massive rat. He's the leader of the pack.
+
+N:1007:Vort the Kobold Queen
+G:k:v
+I:110:12d10:20:20:20
+W:7:3:1000:100
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+F:UNIQUE | FEMALE | CAN_SPEAK |
+F:FORCE_MAXHP |
+F:ESCORT | ESCORTS | DROP_SKELETON | DROP_CORPSE
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_6 | MISSILE | HEAL | CAUSE_1 | CONF
+D:Where Mughash is the strongest of his kind, his consort Vort is the
+D:wisest: she is her tribe's chief shamaness.
+
+N:1008:Giant black louse
+G:I:D
+I:120:1d2:6:7:10
+W:14:1:100:3
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+F:RAND_50 | RAND_25 | CAN_FLY | WEIRD_MIND |
+F:ANIMAL | MORTAL | BASEANGBAND
+S:MULTIPLY
+D:It is six inches long.
+
+N:1009:Fire Phantom
+G:G:r
+I:120:10d100:20:90:40
+W:34:5:0:1200
+E:0:0:0:0:0:0
+O:0:0:100:0
+B:HIT:HURT:5d5
+F:UNIQUE | MALE | CAN_SPEAK
+F:FORCE_MAXHP | UNDEAD | NO_CONF | NO_SLEEP | REGENERATE | NO_STUN |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | PASS_WALL |
+F:MORTAL | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_6
+S:BR_FIRE | HOLD | CONF | SCARE | MIND_BLAST
+D:He's back from the grave for vengeance on those who
+D:burnt him. He has no mercy for those in his way.
+
+N:1010:The Insane Player
+G:p:v
+I:120:18d100:25:100:10
+W:36:2:1500:1200
+E:1:1:1:2:1:1
+O:20:20:20:20
+B:HIT:HURT:3d8
+F:UNIQUE | MALE | CAN_SPEAK | ATTR_MULTI |
+F:FORCE_MAXHP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_SKELETON | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR | RAND_25 |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR
+F:JOKEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:TELE_TO | SHRIEK | SCARE
+D:Once a powerful adventurer, this poor fighter has seen a few too many
+D:software bugs in his time. Any shred of lucidity is long gone, but
+D:he still remains dangerous. He wanders aimlessly through the dungeon
+D:randomly striking at foes both real and imagined, all the while screaming
+D:out at the world that caused his condition.
+
+N:1011:Glaryssa, Succubus Queen
+G:U:W
+I:120:12d100:90:60:10
+W:41:3:3000:8000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:CLAW:HURT:5d5
+B:HIT:LOSE_STR:4d4
+B:TOUCH:EXP_80:8d3
+F:UNIQUE | FEMALE | CAN_SPEAK |
+F:FORCE_SLEEP | FORCE_MAXHP | NO_SLEEP | NO_CONF
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+F:COLD_BLOOD | PASS_WALL | MOVE_BODY | NONLIVING |
+F:OPEN_DOOR | BASH_DOOR | IM_POIS | IM_COLD | DEMON | EVIL |
+F:ZANGBAND
+S:1_IN_3 |
+S:CAUSE_3 | HOLD | BLIND | BO_ACID | S_DEMON |
+S:FORGET | BO_NETH | MIND_BLAST | DARKNESS
+D:Drop dead gorgeous - literally.
+
+N:1012:Vermicious Knid
+G:j:s
+I:110:90d10:20:55:100
+W:44:2:1400:2100
+E:0:0:0:0:0:0
+O:40:30:20:10
+B:TOUCH:TERRIFY:4d6
+B:CRAWL:HURT:4d6
+B:ENGULF:HURT:4d6
+F:FRIENDS | EVIL | IM_COLD | SMART |
+F:COLD_BLOOD | NO_FEAR | WEIRD_MIND |
+F:OPEN_DOOR | ONLY_ITEM | DROP_2D2 | HURT_ROCK |
+F:NONLIVING |
+F:JOKEANGBAND | NO_CUT
+D:An amorphous shape that looks like wet grey clay with two pale eyes.
+D:It is totally silent as it oozes towards you.
+
+N:1013:Bone golem
+G:g:w
+I:120:35d100:20:170:50
+W:61:3:5000:23000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:UN_BONUS:8d8
+B:HIT:UN_BONUS:8d8
+B:HIT:LOSE_STR:6d6
+B:HIT:LOSE_STR:6d6
+F:FORCE_SLEEP | FORCE_MAXHP | EMPTY_MIND |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | IM_FIRE | IM_ELEC | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:TELE_TO | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | BA_NETH | S_HI_UNDEAD
+D:A skeletal form, black as night, constructed from the bones of its
+D:previous victims.
+
+N:1014:Snake of Yig
+G:J:b
+I:120:48d10:25:80:30
+W:38:4:2000:600
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:POISON:3d12
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:RAND_25 | FRIENDS | AURA_FIRE |
+F:BASH_DOOR | MOVE_BODY | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | HAS_EGG | EVIL | IM_FIRE | IM_POIS |
+F:MORTAL | ZANGBAND
+S:1_IN_5 |
+S:BR_POIS
+D:It is a giant snake that drips with poison.
+
+N:1015:Bronze golem
+G:g:o
+E:2:1:2:4:1:1
+O:0:0:0:0
+I:120:40d100:25:170:50
+W:65:3:5500:26000
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:FORCE_MAXHP | FORCE_SLEEP | EMPTY_MIND | COLD_BLOOD |
+F:OPEN_DOOR | BASH_DOOR | IM_FIRE | IM_ELEC | IM_POIS | RES_TELE |
+F:NO_CONF | NO_SLEEP | NO_STUN | NO_FEAR | NONLIVING | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BO_PLAS | BA_FIRE | BR_FIRE | BA_ELEC | S_HI_DEMON | TELE_TO
+D:A gigantic four-armed animated bronze statue, glowing with great heat.
+
+N:1016:Dimensional shambler
+G:h:B
+I:120:40d10:30:68:255
+W:29:6:1100:400
+E:0:0:0:0:0:0
+O:20:40:40:0
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:FORCE_MAXHP | FORCE_SLEEP | NO_FEAR | GOOD |
+F:ONLY_ITEM | DROP_2D2 | NONLIVING |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF | NO_SLEEP | RES_TELE |
+F:CTHANGBAND
+S:1_IN_3 |
+S:HEAL | HASTE | BLIND | CONF | SCARE | TPORT | BLINK
+D:A shabby humanoid with a wrinkled skin. It seems to shift
+D:in and out of existence as you watch.
+
+N:1017:Cultist
+G:p:G
+I:110:14d8:20:22:40
+W:17:1:1500:36
+E:1:1:1:2:1:1
+O:10:0:90:0
+B:HIT:HURT:4d3
+F:MALE |
+F:FORCE_SLEEP |
+F:DROP_1D2 | EVIL |
+F:DROP_SKELETON | DROP_CORPSE |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:MORTAL | CTHANGBAND | HAS_LITE
+S:1_IN_3 |
+S:HEAL | SCARE | CAUSE_2 |
+S:S_MONSTER | MIND_BLAST
+D:A robed humanoid dedicated to a demonic outer god.
+
+N:1018:Cult leader
+G:p:G
+I:120:52d14:20:60:10
+W:47:2:1800:1800
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:HIT:HURT:6d4
+B:HIT:HURT:6d4
+F:MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:DROP_SKELETON | DROP_CORPSE |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:MORTAL | CTHANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HEAL | BLIND | HOLD | CONF | CAUSE_3 |
+S:S_MONSTER | S_UNDEAD | S_DEMON | BA_CHAO | BRAIN_SMASH
+D:An evil priest, dressed all in black and devoted to one of the netherworld
+D:demons that call themselves "the outer gods".
+
+N:1019:Servitor of the outer gods
+G:H:y
+I:130:100d35:30:140:255
+W:41:6:2100:15000
+E:0:0:0:0:0:0
+O:10:20:70:0
+B:GAZE:TERRIFY:4d4
+B:HIT:HURT:8d6
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | NO_FEAR | REFLECTING |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NONLIVING |
+F:RES_TELE | ELDRITCH_HORROR | SHAPECHANGER | ATTR_MULTI |
+F:CTHANGBAND
+S:1_IN_3 |
+S:TELE_TO | BLIND | SCARE | CAUSE_2 | CAUSE_4 | BO_MANA |
+S:S_UNDEAD
+D:"Toad-like creatures which seemed constantly to be changing shape and
+D:appearance, and from whom emanated, by some means I could not distinguish,
+D:a ghostly ululation, a piping." August Derleth - "The Lurker at The
+D:Threshold".
+
+N:1020:Avatar of Nyarlathotep
+G:p:R
+I:120:25d25:20:70:20
+W:45:2:0:500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:HURT:5d5
+F:IM_POIS | IM_FIRE | IM_ELEC | IM_ACID | IM_COLD |
+F:NO_SLEEP | NO_FEAR | SHAPECHANGER | ELDRITCH_HORROR | NONLIVING |
+F:MALE | OPEN_DOOR | BASH_DOOR |
+F:CTHANGBAND | HAS_LITE
+D:The Crawling Chaos with a thousand forms. Nyarlathotep is amused at your
+D:puny attempts to kill him, and will keep coming back for another go
+D:until he gets bored with the game.
+
+N:1021:Thiazi, the Storm Giant
+G:P:B
+I:130:99d110:50:160:20
+W:78:2:0:45000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:ELEC:9d11
+B:HIT:HURT:9d11
+B:HIT:ELEC:9d11
+B:HIT:HURT:9d11
+F:UNIQUE | SUSCEP_COLD | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP | RES_TELE | RES_NETH | RES_PLAS | MALE |
+F:ONLY_ITEM | DROP_2D2 | DROP_4D2 | DROP_GOOD | EVIL | AURA_ELEC |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | TAKE_ITEM | CAN_SPEAK |
+F:EVIL | IM_FIRE | IM_POIS | SUSCEP_COLD | GIANT | DEMON
+F:ZANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BR_ELEC | BR_PLAS | BR_MANA | BLIND | TELE_TO | S_KIN | S_HI_DRAGON |
+S:HAND_DOOM | TELE_AWAY
+D:Thiazi is the greatest of all the Storm Giants: when travelling, he takes
+D:the form of a giant eagle. It was he who succeeded in kidnapping Iduna and
+D:her apples of youth from Asgard itself.
+
+N:1022:Hypnos, Lord of Sleep
+G:p:G
+I:130:51d99:20:150:10
+W:67:2:5000:36000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:TOUCH:LOSE_ALL:2d25
+B:GAZE:PARALYZE:1d20
+B:GAZE:TERRIFY:1d20
+B:GAZE:BLIND:1d20
+F:UNIQUE | MALE | CAN_SPEAK | ATTR_MULTI |
+F:NO_STUN | RES_DISE | RES_NEXU | CAN_FLY |
+F:FORCE_SLEEP | FORCE_MAXHP | EVIL | SMART | DROP_CORPSE
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:NO_SLEEP | NO_CONF | NO_STUN | IM_POIS | IM_COLD | IM_FIRE | IM_ACID
+F:ZANGBAND | HAS_LITE
+S:1_IN_3
+S:BR_NEXU | BR_CHAO | HOLD | SLOW | BR_INER | BA_NUKE |
+S:MIND_BLAST | BRAIN_SMASH | CONF | BLIND | TELE_TO | HEAL |
+S:TELE_AWAY | TELE_LEVEL | TPORT | S_UNDEAD | S_DEMON | DRAIN_MANA
+D:"Young with the youth that is outside time, and with a handsome
+D:bearded face, curved, smiling lips, Olympian brow; and dense locks
+D:waving and poppy-crowned."
+
+N:1023:Blue dragon worm
+G:w:B
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_ELEC |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_ELEC
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. Sparks fly from its jaws.
+
+N:1024:White dragon worm
+G:w:W
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_COLD |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_COLD
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. Its breath condenses in the air.
+
+N:1025:Green dragon worm
+G:w:G
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_POIS |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_POIS
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. You can smell foul gases
+D:on its breath.
+
+N:1026:Black dragon worm
+G:w:s
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_ACID |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_ACID
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. Acidic drool drips from its jaws.
+
+N:1027:Red dragon worm
+G:w:R
+I:100:10d15:10:40:80
+W:20:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_FIRE |
+F:MORTAL | BASEANGBAND | ATTR_MULTI
+S:MULTIPLY |
+S:1_IN_6 | BR_FIRE
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. Smoke comes from its mouth.
+
+N:1028:Multi-hued dragon worm
+G:w:v
+I:100:10d20:10:40:80
+W:23:3:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:HURT:3d3
+B:CLAW:HURT:3d3
+B:BITE:HURT:3d5
+F:RAND_50 | RAND_25 | ATTR_MULTI |
+F:DROP_60 | ONLY_GOLD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | DRAGON | IM_ELEC | IM_FIRE | IM_ACID | IM_COLD | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:MULTIPLY |
+S:1_IN_6 | BR_ELEC | BR_COLD | BR_FIRE | BR_ACID | BR_POIS
+D:You thought dragons used eggs, but this worm has the scales, and the bad
+D:breath, and the fiery eyes, of a real dragon. Its scales shimmer different
+D:colours as you watch.
+
+N:1029:The Minotaur of the Labyrinth
+G:H:s
+I:130:150d10:13:25:10
+W:40:2:17500:3100
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BUTT:HURT:4d6
+B:BUTT:HURT:4d6
+B:BUTT:HURT:3d6
+B:BUTT:HURT:3d6
+F:BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | UNIQUE | SPECIAL_GENE |
+F:MORTAL | BASEANGBAND
+D:It is a cross between a human and a bull.
+
+N:1030:The Sandworm Queen
+G:w:v
+I:120:30d20:10:40:80
+W:30:3:4500:12
+E:0:0:0:0:1:0
+O:30:60:0:10
+B:CLAW:ACID:5d4
+B:CLAW:POISON:5d4
+B:BITE:FIRE:5d4
+B:BITE:ELEC:5d4
+F:FORCE_MAXHP | DROP_60 | ONLY_ITEM | DROP_GREAT | DROP_GOOD | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | FEMALE | POWERFUL | SPECIAL_GENE | REFLECTING |
+F:EVIL | ANIMAL | IM_ELEC | IM_FIRE | IM_ACID | IM_POIS | ESCORT |
+F:MORTAL | BASEANGBAND | EMPTY_MIND | UNIQUE | NO_CONF
+S:1_IN_2 | BR_POIS | S_KIN
+D:Queen and mother of the sandworms, fear her and her prolific children.
+
+N:1031:Sandworm
+G:w:y
+I:115:10d15:10:40:80
+W:27:6:4500:12
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:CLAW:POISON:4d4
+B:CLAW:POISON:4d4
+B:CLAW:POISON:4d4
+B:BITE:HURT:5d5
+F:RAND_25 |
+F:FORCE_MAXHP | DROP_CORPSE | POWERFUL |
+F:EVIL | IM_ELEC | IM_FIRE | IM_POIS | EMPTY_MIND |
+F:MORTAL | BASEANGBAND
+S:MULTIPLY
+D:Offbreed of the Sandworm Queen, they are harmless alone...
+
+
+N:1032:Tik'srvzllat
+G:G:v
+I:142:180d100:200:170:0
+W:127:2:1000:350000
+E:1:1:1:2:1:1
+O:50:0:50:0
+B:GAZE:UN_BONUS:10d10
+B:GAZE:TIME:10d10
+B:GAZE:INSANITY:10d10
+B:GAZE:INSANITY:10d5
+F:UNIQUE | SPECIAL_GENE |
+F:FORCE_MAXHP | POWERFUL |
+F:ONLY_ITEM | DROP_3D2 | DROP_4D2 | DROP_GOOD | DROP_GREAT |
+F:INVISIBLE | COLD_BLOOD | PASS_WALL | WEIRD_MIND |
+F:EVIL | UNDEAD | IM_COLD | IM_FIRE | IM_ACID | IM_ELEC | AURA_COLD |
+F:IM_POIS | RES_NETH | BASEANGBAND | NO_CUT
+S:1_IN_2 |
+S:S_UNDEAD | S_DEMON | S_DRAGON |
+S:S_UNIQUE | S_WRAITH | S_HI_DEMON |
+S:S_HI_UNDEAD | S_HI_DRAGON | S_KIN |
+S:BR_DISE | BA_NETH
+D:A disembodied and barely sentient mind, Tik'srvzllat floated
+D:through the void for eons before being awakened by sorcery, pulled
+D:into the nether realm, and shaped into the being you see before you.
+D:A flickering purple outline of a sphere, with eerie yellow-purple
+D:mist circling rapidly around it, Tik'srvzllat threatens your sanity
+D:with its appearance alone.
+
+N:1033:The Glass Golem
+G:g:W
+I:130:100d15:200:170:0
+W:52:4:0:2000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:INSANITY:5d6
+B:HIT:INSANITY:6d6
+B:HIT:INSANITY:7d6
+B:HIT:INSANITY:6d6
+F:COLD_BLOOD | EMPTY_MIND | KILL_WALL | FORCE_MAXHP | POWERFUL | SPECIAL_GENE |
+F:BASH_DOOR | IM_COLD | IM_ACID | IM_FIRE | IM_ELEC | IM_POIS | NONLIVING |
+F:ESCORTS | ESCORT | UNIQUE | NO_SLEEP | NO_CONF | NO_FEAR | NO_STUN
+F:CHAR_MULTI | CAN_FLY | BASEANGBAND | NO_CUT
+S:1_IN_4
+S:BR_CONF | BR_LITE | BR_DARK | BR_WALL
+S:S_KIN | TELE_LEVEL | SHRIEK
+D:One of the last creations of the petty dwarves of Ludarin, its existence
+D:explains their destruction. A creation of finest glass, the body of this
+D:creature bends and amplifies light in a way that makes you unsure of its
+D:position. You feel somewhat dizzy as the radiant light reflects off the
+D:walls, the ceiling and your gear.
+
+N:1034:The White Balrog
+G:U:W
+I:120:25d100:20:100:100
+W:50:3:12000:25000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:COLD:4d12
+B:HIT:COLD:4d12
+B:CRUSH:HURT:3d12
+B:TOUCH:UN_POWER
+F:UNIQUE | FORCE_SLEEP | FORCE_MAXHP | AURA_COLD | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | NONLIVING | DROP_RANDART
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY | SMART |
+F:EVIL | DEMON | IM_COLD | NO_CONF | KILL_WALL | BASEANGBAND |
+F:HAS_LITE | SPECIAL_GENE
+S:1_IN_4 |
+S:BLIND | CONF | BRAIN_SMASH |
+S:BR_COLD | BO_COLD | BA_NETH | S_UNDEAD | S_DEMON
+D:It is a massive humanoid demon wreathed in frost, wielding a cruel looking
+D:pike in its hands.
+
+N:1035:Golgarach, the Living Rock
+G:#:W
+I:120:50d30:20:100:40
+W:45:2:0:1500
+E:0:0:0:0:0:0
+O:60:0:40:0
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:FORCE_SLEEP | COLD_BLOOD | EMPTY_MIND | PASS_WALL | KILL_BODY | ESCORT |
+F:BASH_DOOR | IM_COLD | IM_ACID | IM_ELEC | IM_POIS | NONLIVING | UNIQUE
+F:HURT_ROCK | NO_CONF | NO_SLEEP | NO_CUT | CHAR_MULTI | BASEANGBAND | SPECIAL_GENE
+F:NO_CUT | DROP_RANDART
+S:1_IN_10 | S_KIN | BO_ACID | BA_FIRE
+D:Deep in the heart of the earth, even the rock itself is sentient
+D:and has learned to despise intruders.
+
+N:1036:Atlas, the Titan
+G:P:s
+I:130:100d100:30:160:15
+W:76:3:70000:37000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:SHATTER:13d13
+B:HIT:CONFUSE:13d13
+B:HIT:SHATTER:13d13
+B:HIT:CONFUSE:13d13
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_GOOD | DROP_4D2 | KILL_BODY | KILL_WALL |
+F:SMART | TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_CORPSE |
+F:EVIL | GIANT | HURT_ROCK | BASEANGBAND | HAS_LITE |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+D:The strongest of all the Titans. Legend has it that he once held the sky
+D:on his shoulders; and the mountain range that now does so is named after him.
+
+N:1037:Kronos, Lord of the Titans
+G:P:v
+I:130:130d100:30:150:15
+W:87:3:80000:42000
+B:HIT:CONFUSE:12d12
+B:HIT:CONFUSE:12d12
+B:HIT:CONFUSE:12d12
+B:HIT:CONFUSE:12d12
+E:1:1:1:2:1:1
+O:0:100:0:0
+F:UNIQUE | MALE |
+F:FORCE_SLEEP | FORCE_MAXHP | MOVE_BODY |
+F:ONLY_ITEM | DROP_4D2 | DROP_3D2 | DROP_GOOD |
+F:SMART | TAKE_ITEM | BASH_DOOR | HAS_LITE |
+F:EVIL | GIANT | ESCORT | BASEANGBAND | DROP_CORPSE |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_POIS | IM_ELEC
+S:1_IN_3 |
+S:BR_FIRE | BR_COLD | BR_SHAR | BR_SOUN | ROCKET |
+S:S_MONSTERS | S_KIN | TELE_TO | HEAL
+D:The lord of the Titans, he has broken loose from his confinement in
+D:the nether hells to seek revenge on the world.
+
+N:1038:Water hound
+G:Z:r
+I:110:12d6:30:30:0
+W:43:1:600:150
+B:BITE:HURT:1d6
+B:CLAW:HURT:1d4
+B:CLAW:HURT:1d4
+E:0:1:0:2:1:0
+O:0:0:0:0
+F:FORCE_SLEEP | BASH_DOOR | DROP_CORPSE | FRIENDS
+F:ANIMAL | IM_COLD | MORTAL | BASEANGBAND | RES_WATE | NO_CUT
+S:1_IN_5 | BA_WATE
+D:The sound of a hundred waterfalls rushes through your ears as
+D:a huge wave of water, vaguely hound-shaped, rushes towards you.
+
+N:1039:Improv, the mighty MoLD
+G:m:v
+I:150:170d100:40:140:0
+W:127:2:3000:50000
+E:3:0:3:6:1:0
+O:20:20:20:20
+B:SPORE:UN_BONUS:10d10
+B:SPORE:EXP_80:10d10
+B:SPORE:TIME:10d10
+B:SPORE:TIME:10d10
+F:UNIQUE | NEVER_MOVE | CAN_SWIM |
+F:FORCE_MAXHP | WEIRD_MIND | DROP_CORPSE |
+F:REFLECTING | AURA_ELEC | ONLY_ITEM | DROP_4D2 |
+F:DROP_GOOD | DROP_GREAT | FORCE_SLEEP | RES_NETH |
+F:SMART | POWERFUL | RES_TELE | REGENERATE | CAN_FLY |
+F:DG_CURSE | WYRM_PROTECT | EVIL |
+F:IM_ACID | IM_FIRE | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NO_STUN |
+F:JOKEANGBAND | HAS_LITE | RES_WATE | NO_CUT
+S:1_IN_2 |
+S:S_HI_DRAGON | S_KIN | BR_DISI | HEAL | TPORT |
+S:TELE_LEVEL | TELE_TO
+D:An assistant to DarkGod, Improv has chosen the form of a dense
+D:purple smog for his incarnation into Middle-earth. It travels the
+D:dungeons, killing software bugs and creating random artifacts for
+D:guests to find.
+
+N:1040:Emperor Mimic
+G:m:y
+I:120:50d50:30:60:100
+W:40:3:100:200
+E:0:0:0:0:0:0
+O:25:25:25:25
+B:HIT:POISON:5d5
+B:HIT:POISON:5d5
+B:HIT:POISON:5d5
+B:HIT:POISON:5d5
+F:MIMIC | UNIQUE
+F:FORCE_SLEEP |
+F:EMPTY_MIND | COLD_BLOOD |
+F:IM_ACID | IM_FIRE | IM_ELEC | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_1 |
+S:BLIND | CONF | SCARE | CAUSE_4 | CAUSE_3 | FORGET |
+S:BA_ACID | BA_FIRE | BA_COLD | BA_ELEC |
+S:S_MONSTER | S_KIN | SHRIEK | BRAIN_SMASH | TRAPS
+D:A strange creature that disguises itself as an object to lure
+D:unsuspecting adventurers within reach of its venomous claws.
+
+N:1041:Melinda Proudfoot
+G:h:v
+I:110:1d1:40:250:3
+W:1:1:730:0
+E:0:1:1:2:1:1
+O:0:0:0:1
+F:FEMALE | CAN_SPEAK
+F:FORCE_MAXHP | SPECIAL_GENE
+F:NEVER_MOVE | NEVER_BLOW | GOOD | NO_TARGET
+F:MORTAL | BASEANGBAND | UNIQUE | NEUTRAL | NO_DEATH
+D:She seems to seek someone, you may help...
+
+N:1042:Thrain, the King Under the Mountain
+G:h:B
+I:110:1d1:40:250:3
+W:60:1:730:0
+E:0:1:1:2:1:1
+O:0:0:0:1
+F:MALE | CAN_SPEAK
+F:FORCE_MAXHP | SPECIAL_GENE
+F:NEVER_MOVE | NEVER_BLOW | GOOD | NO_TARGET
+F:MORTAL | BASEANGBAND | UNIQUE | NEUTRAL | NO_DEATH
+D:He must have suffered horrible tortures...
+
+N:1043:Fire golem
+G:g:r
+I:115:3d20:50:50:10
+W:0:3:100:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:2d6
+B:HIT:HURT:2d6
+B:HIT:FIRE:3d6
+B:HIT:FIRE:3d6
+F:BASH_DOOR | AURA_FIRE | HAS_LITE
+F:IM_FIRE | SPECIAL_GENE
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | NO_STUN
+F:MORTAL | BASEANGBAND | NO_CUT | AI_PLAYER
+S:1_IN_10 |
+S:BR_FIRE
+D:A sentient mass of pure fire.
+
+N:1044:Melkor, Lord of Darkness
+G:G:v
+I:150:300d300:100:150:0
+W:150:1:200000:60000
+E:1:1:1:2:1:1
+O:25:25:25:25
+B:HIT:ABOMINATION:3d10
+B:HIT:TIME:24d10
+B:HIT:INSANITY:24d10
+B:HIT:LOSE_ALL:24d10
+F:UNIQUE | CAN_SPEAK | MALE |
+F:FORCE_MAXHP | SPIRIT
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_3D2 | DROP_4D2 |
+F:DROP_GOOD | DROP_GREAT | DROP_CHOSEN | RES_NETH |
+F:SMART | KILL_WALL | MOVE_BODY | AURA_FIRE |
+F:REGENERATE | POWERFUL | SPECIAL_GENE | CAN_FLY | KILL_TREES
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_STUN | NO_SLEEP | NO_FEAR | RES_TELE | BASEANGBAND |
+S:1_IN_4 |
+S:BRAIN_SMASH |
+S:BA_MANA | BO_MANA | BA_NETH | BA_CHAO | BA_DARK | ANIM_DEAD | S_KIN |
+S:S_MONSTERS | S_UNIQUE | S_HI_DEMON | S_HI_UNDEAD | S_HI_DRAGON |
+S:BR_NETH | BR_DISI | HAND_DOOM | S_WRAITH | HEAL | BRAIN_SMASH |
+S:DRAIN_MANA | TELE_TO | DARKNESS | SHRIEK
+D:He was the most powerful of the Valar, the equal of Manwe.
+D:You banned him here, in the Void, and now you must destroy him
+D:forever. However here in the Void, his spirit gained much power
+D:for he is closer to the Flame Imperishable. He is coming to you in pure
+D:madness, which makes him even more dangerous. You are on the verge
+D:of dying!
+
+## Here are the Spirits, inhabitants of the Void, all called "Spirit" making it hard to know what we are up against ##
+## Note: I am nasty heheh :)
+
+# Spirit of nether
+N:1045:Spirit
+G:G:v
+I:120:40d80:30:50:20
+W:128:2:0:5000
+E:0:1:1:2:0:0
+O:25:0:75:0
+B:TOUCH:EXP_80:10d10
+B:TOUCH:EXP_80:10d10
+F:SPIRIT | BASEANGBAND | NEVER_MOVE | EMPTY_MIND | NO_CUT |
+F:COLD_BLOOD | INVISIBLE
+S:1_IN_1 |
+S:BA_NETH
+D:This strange, almost intangible spirit keeps assaulting you!
+
+# Spirit of annoyance (hahaha AI_ANNOY and MULTIPLY)
+N:1046:Spirit
+G:G:B
+I:130:40d20:30:70:70
+W:144:4:0:500
+E:0:0:0:0:0:0
+O:5:5:5:0
+B:TOUCH:POISON:10d10
+B:CRAWL:POISON:10d10
+B:CRAWL:EAT_ITEM:10d9
+B:BITE:UN_BONUS:9d9
+F:SPIRIT | BASEANGBAND | WEIRD_MIND | FRIENDS |
+F:AI_ANNOY | PASS_WALL
+S:MULTIPLY
+D:These things multiply at an apparently unstoppable rate!
+
+# Spirit of movement
+N:1047:Spirit
+G:G:B
+I:130:10d80:50:65:10
+W:132:1:10:5500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:LOSE_DEX:2d3
+B:TOUCH:PARALYZE:1d12
+B:TOUCH:PARALYZE:1d12
+F:SPIRIT | BASEANGBAND | NO_SLEEP | PASS_WALL | WEIRD_MIND
+S:1_IN_2 |
+S:SLOW | BLINK | HOLD | HASTE
+D:Coming towards you quickly, it seems intent on moving faster than you.
+
+# Spirit of confusion
+N:1048:Spirit
+G:G:v
+I:120:40d80:80:85:0
+W:135:2:0:5500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:HALLU:16d8
+B:ENGULF:HALLU:16d8
+B:ENGULF:CONFUSE:16d8
+B:ENGULF:CONFUSE:16d8
+F:SPIRIT | BASEANGBAND | RES_NEXU | AURA_ELEC | IM_FIRE | IM_ELEC |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | CAN_FLY | ATTR_MULTI |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NO_CUT
+S:1_IN_3 |
+S:BR_CHAO | BR_NEXU | BR_CONF
+D:A swirling mass, constantly changing its appearance.
+
+# Spirit of brawn
+N:1049:Spirit
+G:G:U
+I:130:140d100:50:180:30
+W:145:3:10000:8000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:SHATTER:18d18
+B:HIT:CONFUSE:18d18
+B:HIT:SHATTER:18d18
+B:HIT:CONFUSE:18d18
+F:SPIRIT | BASEANGBAND | DROP_4D2 | KILL_BODY | KILL_WALL |
+F:OPEN_DOOR | BASH_DOOR | DROP_CORPSE | KILL_TREES |
+F:EVIL | GIANT | HURT_ROCK | BASEANGBAND | HAS_LITE |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+D:Strong and swarthy, this spirit could bend metal with his bare hands.
+
+# Spirit of Wyrms
+N:1050:Spirit
+G:G:v
+I:130:151d151:50:190:50
+W:147:8:10000:87500
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:10d15
+B:CLAW:HURT:10d15
+B:BITE:HURT:14d18
+B:BITE:HURT:14d18
+F:SPIRIT | FORCE_MAXHP | MOVE_BODY | AURA_FIRE | REFLECTING | AURA_ELEC |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD | AURA_COLD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | RES_NETH | RES_DISE |
+F:DRAGON | GOOD | RES_TELE | DROP_CORPSE | KILL_TREES |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:RES_NEXU | RES_PLAS | CAN_FLY | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_3 |
+S:S_HI_DRAGON | S_DRAGON | S_KIN |
+S:BR_ACID | BR_ELEC | BR_FIRE |
+S:BR_COLD | BR_POIS | BR_NETH | BR_LITE | BR_DARK |
+S:BR_CONF | BR_SOUN | BR_CHAO | BR_DISE | BR_NEXU |
+S:BR_TIME | BR_INER | BR_GRAV | BR_SHAR | BR_PLAS |
+S:BR_WALL | BR_MANA | BR_DISI
+D:This spirit bears a remarkable similarity to some of the most powerful
+D:types of dragonkind found in Middle-earth. It appears to be even more
+D:fearsome though!
+
+# Spirit of snakes
+N:1051:Spirit
+G:G:g
+I:130:150d100:40:80:20
+W:133:3:90:750
+E:1:1:1:2:1:1
+O:25:20:25:20
+B:BITE:POISON:15d15
+B:BITE:POISON:15d15
+B:BITE:LOSE_ALL:10d12
+F:SPIRIT | CAN_SWIM | IM_POIS | IM_ACID |
+F:DROP_60 | DROP_2D2 | FRIENDS | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | EVIL | BASEANGBAND
+S:1_IN_4 |
+S:BA_POIS | S_MONSTER | SCARE | HOLD
+D:It slides towards you, a horrible scaly, slidy thing.
+
+# Spirit of seeing
+N:1052:Spirit
+G:G:v
+I:130:95d110:60:130:10
+W:141:3:60:50000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:GAZE:UN_BONUS:12d12
+B:GAZE:UN_POWER:12d10
+B:GAZE:INSANITY:12d14
+B:GAZE:LOSE_ALL:6d6
+F:SPIRIT | BASH_DOOR | EVIL | IM_POIS |
+F:CAN_FLY | BASEANGBAND
+S:1_IN_2 |
+S:BLIND | CONF | FORGET | SCARE | DRAIN_MANA | BRAIN_SMASH |
+S:BA_DARK | BO_MANA | BA_NETH | BA_ACID | BA_FIRE | BA_COLD
+D:You will find it difficult to avoid being seen by this spirit! And
+D:once it has you in its sight, beware!
+
+# Spirit of unseeing
+N:1053:Spirit
+G:.:W
+I:120:55d50:20:130:80
+W:142:6:60:4000
+E:0:0:0:0:0:0
+O:10:0:90:0
+B:TOUCH:UN_BONUS:12d12
+B:TOUCH:UN_POWER:12d10
+B:TOUCH:INSANITY:12d14
+B:TOUCH:LOSE_ALL:6d6
+F:SPIRIT | CAN_FLY | REFLECTING |
+F:SMART | BASH_DOOR | COLD_BLOOD | INVISIBLE | EMPTY_MIND |
+F:EVIL | BASEANGBAND | CHAR_MULTI | CHAR_CLEAR | ATTR_CLEAR
+S:1_IN_3 |
+S:DRAIN_MANA | BLINK | BLIND | SCARE | CONF |
+S:HEAL | TELE_AWAY | DARKNESS | TRAPS | FORGET | SHRIEK
+D:Hopefully you will kill this spirit before you realise it exists.
+
+# Spirit of ickyness
+N:1054:Spirit
+G:G:g
+I:130:80d80:30:60:10
+W:138:4:300:40000
+E:0:0:0:0:0:0
+O:40:30:10:10
+B:CRAWL:POISON:12d14
+B:CRAWL:EAT_FOOD:12d14
+B:TOUCH:ACID:13d15
+B:HIT:HURT:13d15
+F:SPIRIT | BASEANGBAND |
+F:EMPTY_MIND | OPEN_DOOR | BASH_DOOR |
+F:EVIL | IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | IM_POIS |
+F:EMPTY_MIND
+S:1_IN_4 |
+S:DRAIN_MANA | BLIND | CONF | SCARE | S_KIN
+D:A horrible slimy spirit, that seems to ooze evilness. I wouldn't get
+D:too close to it if I were you.
+
+# Spirit of friendship
+N:1055:Spirit
+G:G:W
+I:130:35d100:40:150:100
+W:136:3:200:10000
+E:0:1:0:2:1:0
+O:50:50:0:0
+B:BITE:HURT:12d12
+B:BITE:HURT:12d12
+B:BITE:HURT:12d8
+B:BITE:HURT:12d8
+F:SPIRIT | BASEANGBAND |
+F:ESCORT | ESCORTS |
+F:OPEN_DOOR | BASH_DOOR
+S:1_IN_2 |
+S:S_KIN
+D:This spirit appears to have lots of friends!
+
+# Spirit of abomination
+N:1056:Spirit
+G:G:d
+I:130:40d80:30:125:125
+W:134:2:200:5000
+E:0:0:0:0:0:0
+O:50:0:50:0
+B:WAIL:TERRIFY:8d9
+B:HIT:HURT:10d10
+B:HIT:ABOMINATION:6d10
+B:HIT:ABOMINATION:6d10
+F:SPIRIT | BASEANGBAND |
+F:EVIL | UNDEAD | IM_POIS | IM_COLD | NO_CONF | NO_SLEEP |
+F:COLD_BLOOD | HURT_LITE | NO_CUT
+S:1_IN_3 |
+S:SCARE | HOLD | DARKNESS | SCARE
+D:It seems to have been woken from the dead, this spirit. It lumbers
+D:towards you, seeking to turn you into a form as hideous as its own!
+
+### Here come the spirits of <player stat> ###
+#
+# Spirit of strength
+N:1057:Spirit
+G:G:u
+I:120:140d100:50:180:170
+W:129:2:10000:8000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:LOSE_STR:4d8
+B:HIT:LOSE_STR:4d8
+B:HIT:LOSE_STR:4d8
+F:SPIRIT | BASEANGBAND | DROP_2D2 | KILL_BODY | KILL_WALL |
+F:BASH_DOOR | EVIL | GIANT | HURT_ROCK |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+D:It is coming for you, this massive imposing tower of strength.
+D:It appears almost unstoppable!
+
+# Spirit of intelligence
+N:1058:Spirit
+G:G:r
+I:140:80d100:50:100:10
+W:131:2:10000:8000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:LOSE_INT:4d8
+B:HIT:LOSE_INT:4d8
+B:HIT:LOSE_INT:4d8
+F:EVIL | SPIRIT | BASEANGBAND | DROP_2D2 |
+F:OPEN_DOOR | SMART |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+S:1_IN_2 |
+S:HASTE | TPORT | TELE_TO | BLIND | CONF |
+S:BO_MANA | BO_FIRE | BO_COLD | BO_ELEC |
+D:This spirit looks very clever, cunning almost.
+
+# Spirit of wisdom
+N:1059:Spirit
+G:G:G
+I:130:120d100:50:200:130
+W:137:2:10000:8000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:LOSE_WIS:4d8
+B:HIT:LOSE_WIS:4d8
+B:HIT:LOSE_WIS:4d8
+F:EVIL | SPIRIT | BASEANGBAND | DROP_2D2 |
+F:OPEN_DOOR |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+S:1_IN_2 |
+S:HEAL | MIND_BLAST | CAUSE_4 | SCARE |
+S:DRAIN_MANA | BRAIN_SMASH | FORGET |
+D:This spirit has something of a priestly look about it.
+
+# Spirit of dexterity
+N:1060:Spirit
+G:G:W
+I:160:120d100:50:160:50
+W:139:2:10000:8000
+E:1:1:1:2:1:1
+O:0:50:25:25
+B:HIT:LOSE_DEX:4d8
+B:HIT:LOSE_DEX:4d8
+B:HIT:LOSE_DEX:4d8
+F:EVIL | SPIRIT | BASEANGBAND | DROP_2D2 |
+F:OPEN_DOOR | SMART |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+S:1_IN_1 |
+S:ARROW_4
+D:This spirit moves almost too quickly for you to see him.
+
+# Spirit of constitution
+N:1061:Spirit
+G:G:s
+I:120:140d100:50:180:50
+W:143:2:10000:8000
+E:1:1:1:2:1:1
+O:0:50:0:50
+B:HIT:LOSE_CON:4d8
+B:HIT:LOSE_CON:4d8
+B:HIT:LOSE_CON:4d8
+F:EVIL | SPIRIT | BASEANGBAND | DROP_2D2 | BASH_DOOR |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+D:This spirit moves slower than most, but thunders on and on
+D:towards you.
+
+# Spirit of charisma
+N:1062:Spirit
+G:G:b
+I:140:80d100:50:120:90
+W:146:2:10000:8000
+E:1:1:1:2:1:1
+O:70:10:10:10
+B:HIT:LOSE_CHR:4d8
+B:HIT:LOSE_CHR:4d8
+B:HIT:LOSE_CHR:4d8
+F:SPIRIT | BASEANGBAND | DROP_2D2 |
+F:OPEN_DOOR | EVIL | TAKE_ITEM |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_ELEC | IM_POIS
+S:1_IN_2 |
+S:TRAPS | ARROW_3 | BLINK | TELE_TO | CONF
+D:There is something attractive about this spirit, and it seems
+D:to have a full purse.
+
+### Here come some elemental spirits ###
+#
+# Spirit of flickering fire
+N:1063:Spirit
+G:G:r
+I:120:60d100:50:65:80
+W:130:1:5:6000
+E:1:1:1:2:1:1
+O:25:60:0:15
+B:HIT:FIRE:10d8
+B:HIT:FIRE:10d8
+B:HIT:FIRE:10d8
+F:SPIRIT | BASEANGBAND | AI_ANNOY | IM_FIRE | AURA_FIRE | HAS_LITE
+S:1_IN_2 |
+S:BO_FIRE | BA_FIRE
+D:Flickering towards you, and then away, this spirit will burn you badly!
+
+# Spirit of icy cold
+N:1064:Spirit
+G:G:w
+I:120:60d100:50:65:80
+W:148:1:5:6000
+E:1:1:1:2:1:1
+O:25:60:0:15
+B:HIT:COLD:10d8
+B:HIT:COLD:10d8
+B:HIT:COLD:10d8
+F:SPIRIT | BASEANGBAND | IM_COLD | AURA_COLD |
+F:COLD_BLOOD | BASH_DOOR
+S:1_IN_2 |
+S:BO_COLD | BA_COLD
+D:The temperature around you drops as soon as you set eyes on this spirit.
+
+# Spirit of corrosion (acid)
+N:1065:Spirit
+G:G:s
+I:120:60d100:50:65:80
+W:146:1:5:6000
+E:1:1:1:2:1:1
+O:25:60:0:15
+B:HIT:ACID:10d8
+B:HIT:ACID:10d8
+B:HIT:ACID:10d8
+F:SPIRIT | BASEANGBAND | IM_ACID | BASH_DOOR | KILL_TREES
+S:1_IN_2 |
+S:BO_ACID | BA_ACID
+D:The very fabric of the void heals itself where this spirit walks.
+
+# Spirit of shocking (electricity)
+N:1066:Spirit
+G:G:b
+I:120:60d100:50:65:80
+W:149:1:5:6000
+E:1:1:1:2:1:1
+O:25:60:0:15
+B:HIT:ELEC:10d8
+B:HIT:ELEC:10d8
+B:HIT:ELEC:10d8
+F:SPIRIT | BASEANGBAND | IM_ELEC | AURA_ELEC | BASH_DOOR
+S:1_IN_2 |
+S:BO_ELEC | BA_ELEC
+D:The air crackles as this spirit approaches, and you smell singed flesh.
+
+# Spirit of Valaraukar (Balrogs)
+N:1067:Spirit
+G:G:v
+I:130:130d100:100:140:10
+W:149:1:170:43000
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:FIRE:11d12
+B:HIT:FIRE:11d12
+B:CRUSH:HURT:10d12
+B:TOUCH:UN_POWER
+F:SPIRIT | CAN_FLY | KILL_WALL | AURA_FIRE | NONLIVING |
+F:ONLY_ITEM | DROP_2D2 | EVIL | DEMON |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:IM_FIRE | IM_ELEC | BASEANGBAND | HAS_LITE
+S:1_IN_3 |
+S:BLIND | CONF | SCARE | BR_FIRE
+D:Carrying a whip of fire, this spirit looks not dissimilar to a certain
+D:Balrog.
+
+# Spirit of shadows
+N:1068:Spirit
+G:G:d
+I:130:30d100:70:150:4
+W:140:4:2300:10000
+E:1:1:1:2:1:1
+O:20:80:0:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:TOUCH:EXP_80:20d8
+B:TOUCH:EXP_40:20d8
+F:SPIRIT | BASEANGBAND | REFLECTING | REGENERATE |
+F:IM_ACID | IM_ELEC | IM_COLD | IM_FIRE | IM_POIS | RES_TELE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | SMART | POWERFUL
+S:1_IN_3 |
+S:BLIND | HEAL | BA_DARK | HASTE | CONF
+D:Deriving his strength from the shadows, this spirit
+D:steals only for the challenge.
+
+# Spirit of vampire or something
+N:1069:Spirit
+G:G:W
+I:130:50d50:30:90:10
+W:143:2:1700:18000
+E:1:1:1:2:1:1
+O:0:70:30:0
+B:HIT:HURT:10d9
+B:HIT:HURT:9d9
+B:BITE:EXP_80:9d9
+B:BITE:EXP_80:9d9
+F:SPIRIT | FORCE_SLEEP | CAN_FLY |
+F:DROP_60 | DROP_4D2 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | REGENERATE | RES_TELE |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS | HURT_LITE |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT | SPIRIT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 | CAUSE_4 | DRAIN_MANA |
+S:BRAIN_SMASH | DARKNESS | BO_NETH
+D:Your blood curdles and your bones chill as this spirit approaches.
+
+# Spirit of unresistability (hehehehe)
+N:1070:Spirit
+G:G:v
+I:140:40d100:40:70:20
+W:149:4:1200:20000
+E:1:1:1:2:1:1:
+O:0:0:0:100
+B:HIT:SHATTER:18d10
+B:HIT:SHATTER:18d10
+B:HIT:LOSE_ALL:8d8
+B:TOUCH:UN_POWER
+F:BASEANGBAND | SPIRIT | SMART |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:RES_NEXU | RES_NETH |
+F:NO_CONF | NO_SLEEP | NO_FEAR |
+F:IM_POIS | IM_ELEC | IM_ACID | IM_COLD |
+S:1_IN_2 |
+S:BA_MANA | BR_PLAS | BR_TIME | ROCKET | HAND_DOOM | FORGET | BA_WATE
+D:This spirit appears to be afraid of very little, and confident in its
+D:ability to destroy you.
+
+# Spirit of time
+N:1071:Spirit
+G:G:G
+I:130:80d100:40:110:0
+W:150:3:800:50000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:TOUCH:TIME:6d12
+B:TOUCH:TIME:6d12
+B:TOUCH:TIME:6d12
+B:TOUCH:TIME:6d12
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:ANIMAL | NO_CONF | NO_SLEEP |
+F:SPIRIT | BASEANGBAND
+S:1_IN_2 |
+S:BR_TIME | SLOW | HASTE | HOLD
+D:All at once you see that which is to come and that which has gone before.
+
+# Spirit of Gold
+N:1072:Spirit
+G:G:y
+I:130:50d80:20:110:100
+W:130:4:80:8000
+E:0:1:0:2:1:0
+O:100:0:0:0
+B:TOUCH:EAT_GOLD:7d15
+B:TOUCH:EAT_GOLD:7d15
+B:HIT:POISON:6d12
+B:HIT:POISON:6d12
+F:ONLY_GOLD | SPIRIT | BASEANGBAND |
+F:DROP_4D2 | DROP_4D2 | DROP_4D2 | REFLECTING | COLD_BLOOD | REGENERATE |
+F:BASH_DOOR | MOVE_BODY | IM_ELEC | IM_COLD | IM_POIS |
+F:RES_TELE | NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP
+S:1_IN_4 |
+S:ARROW_4 | HEAL | FORGET
+D:It is the very essence of financial greed...
+
+# Spirit of doom
+N:1073:Spirit
+G:G:D
+I:136:60d70:40:70:30
+W:134:2:60:8000
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:HIT:HURT:14d14
+B:HIT:HURT:14d14
+B:HIT:HURT:14d14
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP |
+F:SPIRIT | BASEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:HAND_DOOM | CAUSE_4 | DARKNESS
+D:The very presence of this creature fills the air with an aura of doom.
+D:You feel waves of depression descending on you as it approaches.
+
+# Spirit of etherealism (?)
+N:1074:Spirit
+G:G:o
+I:120:40d100:30:120:40
+W:141:3:1700:10000
+E:0:1:0:6:1:0
+B:CLAW:HURT:14d10
+B:CLAW:HURT:14d10
+B:BITE:HURT:17d10
+F:INVISIBLE | CAN_FLY | SPIRIT | WEIRD_MIND |
+F:PASS_WALL | POWERFUL | MOVE_BODY |
+F:EVIL | NO_CONF | NO_SLEEP | BASEANGBAND | SPIRIT
+S:1_IN_2 |
+S:BLIND | CONF |
+S:BR_LITE | BR_DARK | BR_CONF
+D:This sprit seems to flicker in and out of this plane, and is a master
+D:of light and dark.
+
+# Spirit of orc (boring huh)
+N:1075:Spirit
+G:G:v
+I:120:90d10:30:160:100
+W:142:3:2700:11111
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:OPEN_DOOR | BASH_DOOR |
+F:EVIL | ORC | IM_POIS | ONLY_ITEM |
+F:MORTAL | BASEANGBAND | HAS_LITE | SPIRIT
+D:Stupid but strong, this spirit has an orcish aura about him.
+
+### Here endeth the Spirits ###
+
+N:1076:Neil, the Sorceror
+G:h:v
+# *Not* enough hitpoints
+I:110:50d100:20:30:30
+# Bottom of Erebor
+W:72:100:330:50000
+E:1:1:1:2:1:1
+O:0:5:90:5
+# Sorceror, not warrior
+B:HIT:HURT:1d1
+B:HIT:HURT:1d1
+F:OPEN_DOOR | SMART | MALE | DROP_SKELETON | DROP_CORPSE
+F:MORTAL | HAS_LITE | JOKEANGBAND | UNIQUE
+# Trone, of course
+F:CAN_FLY | REFLECTING | IM_FIRE
+# Thorin
+F:IM_ACID | FORCE_MAXHP
+# No cold or random gen... waiting on elven rings only at bottom of Erebor
+F:SPECIAL_GENE
+# Dig
+F:KILL_WALL
+# Obvious resistances
+F:NO_CONF | NO_SLEEP | NO_STUN | NO_FEAR
+# Well, he's been there a while
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | DROP_GREAT
+# Essence of speed, Manathrust, Noxious Cloud, and Fireflash
+S:1_IN_1 |
+S:HASTE | BO_MANA | BA_POIS | BA_FIRE
+D:He looks like he is looking for something, and the flecks of dragon
+D:blood on his face tell you he means business!
diff --git a/lib/edit/ra_info.txt b/lib/edit/ra_info.txt
new file mode 100644
index 00000000..57dcee95
--- /dev/null
+++ b/lib/edit/ra_info.txt
@@ -0,0 +1,1927 @@
+# File: ra_info.txt
+
+
+# This file is used to initialize the "lib/raw/ra_info.raw" file, which is
+# used to initialize the "randart parts" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/raw/ra_info.raw" file.
+
+# N:index
+# X:power value:max number of time it can appear on one object
+# T:tval:min sval:max sval (up to 20 T: lines)
+# W:mininum player level to create it:rarity1:rarity2
+# C:max to dam:max to hit:max to AC:max to pval
+# F:flags
+
+# Version stamp (required)
+
+V:2.0.0
+
+# General info, number of powers
+G:100:1d5:1
+G:14:0d0:1
+G:10:0d0:1
+G:3:0d0:1
+
+### Mage Staff randarts ###
+
+N:1
+X:10:1
+T:6:0:255
+W:5:1:4
+C:-5:-5:0:5
+F:MANA
+
+N:2
+X:14:1
+T:6:0:255
+W:10:1:8
+C:-10:-10:0:5
+F:SPELL
+
+N:3
+X:5:1
+T:6:0:255
+W:1:1:4
+C:-3:-3:0:5
+F:INT
+
+### Weapons ###
+
+# + To damage
+N:4
+X:5:2
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:19:0:255
+T:22:0:255
+T:24:0:255
+T:115:55:55
+W:1:1:2
+C:10:0:0:0
+
+# + To Damage for swords
+N:5
+X:5:2
+T:23:0:255
+T:115:55:55
+W:1:1:2
+C:9:0:0:0
+
+# + To damage for hafted weapons
+N:6
+X:5:2
+T:21:0:255
+W:1:1:2
+C:11:0:0:0
+
+# + To hit
+N:7
+X:5:2
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:19:0:255
+T:22:0:255
+T:24:0:255
+T:115:55:55
+W:1:1:2
+C:0:10:0:0
+
+# + To Hit for swords
+N:8
+X:5:2
+T:23:0:255
+T:115:55:55
+W:1:1:2
+C:0:11:0:0
+
+# + To Hit for hafted weapons
+N:9
+X:5:2
+T:21:0:255
+W:1:1:2
+C:0:9:0:0
+
+N:10
+X:15:1
+T:24:0:255
+T:115:55:55
+W:15:1:25
+C:4:0:0:0
+F:VORPAL
+
+N:11
+X:15:1
+T:23:2:2
+T:23:9:9
+T:23:11:33
+T:115:55:55
+W:15:1:25
+C:2:2:0:0
+F:VORPAL
+
+N:12
+X:15:1
+T:22:30:30
+W:10:1:16
+C:6:-2:0:0
+F:VORPAL
+
+N:13
+X:15:1
+T:22:17:17
+T:22:3:3
+T:22:0:15
+W:15:1:25
+C:4:0:0:0
+F:VORPAL
+
+N:14
+X:10:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+T:115:55:55
+W:4:1:10
+C:5:2:0:0
+F:BRAND_POIS
+
+N:15
+X:10:1
+T:23:0:255
+T:115:55:55
+W:4:1:10
+C:4:3:0:0
+F:BRAND_POIS
+
+N:16
+X:10:1
+T:21:0:255
+W:4:1:10
+C:5:2:0:0
+F:BRAND_POIS
+
+N:17
+X:11:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:5:1:11
+C:3:1:0:0
+F:BRAND_FIRE
+A:BRAND_COLD
+
+N:18
+X:11:1
+T:23:0:255
+T:115:55:55
+W:5:1:11
+C:2:2:0:0
+F:BRAND_FIRE
+A:BRAND_COLD
+
+N:19
+X:11:1
+T:21:0:255
+W:5:1:11
+C:4:0:0:0
+F:BRAND_FIRE
+A:BRAND_COLD
+
+N:20
+X:12:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:5:1:11
+C:5:1:0:0
+F:BRAND_COLD
+A:BRAND_FIRE
+
+N:21
+X:12:1
+T:23:0:255
+T:115:55:55
+W:5:1:11
+C:4:2:0:0
+F:BRAND_COLD
+A:BRAND_FIRE
+
+N:22
+X:12:1
+T:21:0:255
+W:5:1:11
+C:6:0:0:0
+F:BRAND_COLD
+A:BRAND_FIRE
+
+N:23
+X:13:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:5:1:13
+C:4:0:0:0
+F:BRAND_ELEC
+
+N:24
+X:13:1
+T:23:0:255
+T:115:55:55
+W:5:1:13
+C:3:1:0:0
+F:BRAND_ELEC
+
+N:25
+X:13:1
+T:21:0:255
+W:5:1:13
+C:5:-1:0:0
+F:BRAND_ELEC
+
+N:26
+X:15:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:7:1:15
+C:3:0:0:0
+F:BRAND_ACID
+
+N:27
+X:15:1
+T:23:0:255
+T:115:55:55
+W:7:1:15
+C:2:1:0:0
+F:BRAND_ACID
+
+N:28
+X:15:1
+T:21:0:255
+W:7:1:15
+C:4:0:0:0
+F:BRAND_ACID
+
+N:29
+X:5:1
+T:21:0:255
+W:15:1:35
+C:15:2:0:0
+F:IMPACT
+
+N:30
+X:5:1
+T:18:0:255
+T:19:0:255
+T:22:0:255
+T:24:0:255
+W:2:1:9
+C:2:2:0:6
+F:STR
+
+N:31
+X:5:1
+T:23:0:255
+T:115:55:55
+W:2:1:9
+C:0:4:0:6
+F:STR
+
+N:32
+X:5:1
+T:21:0:255
+W:1:1:7
+C:4:0:0:6
+F:STR
+
+N:33
+X:3:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:22:0:255
+T:24:0:255
+W:1:1:6
+C:0:0:0:6
+F:CHR
+
+N:34
+X:3:1
+T:23:0:255
+T:115:55:55
+W:2:1:6
+C:-1:1:0:6
+F:CHR
+
+N:35
+X:2:1
+T:21:0:255
+W:2:1:6
+C:1:-1:0:5
+F:CHR
+
+N:36
+X:4:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:22:0:255
+T:24:0:255
+W:2:1:8
+C:0:0:0:4
+F:INT
+
+N:37
+X:4:1
+T:21:0:255
+W:2:1:8
+C:1:-1:0:4
+F:INT
+
+N:38
+X:4:1
+T:23:0:255
+T:115:55:55
+W:2:1:8
+C:-1:1:0:4
+F:INT
+
+N:39
+X:6:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:22:0:255
+T:24:0:255
+W:3:1:10
+C:0:0:0:6
+F:CON
+
+N:40
+X:6:1
+T:23:0:255
+T:115:55:55
+W:3:1:10
+C:-1:1:0:6
+F:CON
+
+N:41
+X:6:1
+T:21:0:255
+C:1:-1:0:6
+F:CON
+
+N:42
+X:4:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:22:0:255
+T:24:0:255
+W:2:1:8
+C:0:0:0:5
+F:WIS
+
+N:43
+X:4:1
+T:21:0:255
+W:2:1:9
+C:1:-1:0:6
+F:WIS
+
+N:44
+X:4:1
+T:23:0:255
+T:115:55:55
+W:2:1:9
+C:-1:1:0:5
+F:WIS
+
+N:45
+X:4:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:22:0:255
+T:24:0:255
+W:2:1:7
+C:0:0:0:5
+F:DEX
+
+N:46
+X:4:1
+T:23:0:255
+T:115:55:55
+W:2:1:7
+C:-1:1:0:6
+F:DEX
+
+N:47
+X:4:1
+T:21:0:255
+W:2:1:7
+C:1:-1:0:5
+F:DEX
+
+N:48
+X:4:1
+T:21:0:255
+W:9:1:14
+C:3:0:0:6
+F:TUNNEL
+
+N:49
+X:40:1
+T:22:0:255
+T:24:0:255
+W:30:1:100
+C:0:-4:0:3
+F:BLOWS
+
+N:50
+X:40:1
+T:23:0:255
+T:115:55:55
+W:30:1:100
+C:-1:-3:0:3
+F:BLOWS
+
+N:51
+X:40:1
+T:21:0:255
+W:30:1:100
+C:1:-5:0:3
+F:BLOWS
+
+N:52
+X:50:1
+T:18:0:255
+T:19:0:255
+T:22:0:255
+T:24:0:255
+W:40:1:90
+C:-2:-2:0:5
+F:SPEED
+
+N:53
+X:50:1
+T:21:0:255
+W:40:1:90
+C:-1:-3:0:5
+F:SPEED
+
+N:54
+X:50:1
+T:23:0:255
+T:115:55:55
+W:40:1:90
+C:-3:-1:0:5
+F:SPEED
+
+N:55
+X:12:1
+T:18:0:255
+T:22:0:255
+T:21:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:11:1:20
+C:10:10:0:0
+F:CHAOTIC
+
+N:56
+X:8:1
+T:18:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:11:1:20
+C:-10:-10:0:0
+F:CHAOTIC
+
+N:57
+X:12:1
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:15:1:20
+C:4:0:0:4
+F:VAMPIRIC
+
+N:58
+X:12:1
+T:21:0:255
+W:15:1:20
+C:5:-1:0:0
+F:VAMPIRIC
+
+N:59
+X:12:1
+T:23:0:19
+T:115:55:55
+W:15:1:20
+C:3:1:0:4
+F:VAMPIRIC
+
+N:60
+X:11:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:10:1:10
+C:0:0:0:0
+F:SLAY_ANIMAL
+
+N:61
+X:11:1
+T:21:0:255
+W:10:1:10
+C:1:-1:0:0
+F:SLAY_ANIMAL
+
+N:62
+X:11:1
+T:23:0:255
+T:115:55:55
+W:10:1:10
+C:-1:1:0:0
+F:SLAY_ANIMAL
+
+N:63
+X:19:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:15:1:17
+C:0:0:0:0
+F:SLAY_EVIL
+
+N:64
+X:19:1
+T:21:0:255
+W:15:1:15
+C:1:-1:0:0
+F:SLAY_EVIL
+
+N:65
+X:19:1
+T:23:0:255
+T:115:55:55
+W:15:1:17
+C:-1:1:0:0
+F:SLAY_EVIL
+
+N:66
+X:15:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:13:1:15
+C:0:0:0:0
+F:SLAY_UNDEAD
+A:KILL_UNDEAD
+
+N:67
+X:15:1
+T:21:0:255
+W:13:1:15
+C:1:-1:0:0
+F:SLAY_UNDEAD
+A:KILL_UNDEAD
+
+N:68
+X:15:1
+T:23:0:255
+T:115:55:55
+W:13:1:15
+C:-1:1:0:0
+F:SLAY_UNDEAD
+A:KILL_UNDEAD
+
+N:69
+X:5:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:11:1:25
+C:0:0:0:0
+F:SLAY_DEMON
+A:KILL_DEMON
+
+N:70
+X:5:1
+T:21:0:255
+W:11:1:25
+C:1:-1:0:0
+F:SLAY_DEMON
+A:KILL_DEMON
+
+N:71
+X:5:1
+T:23:0:255
+T:115:55:55
+W:11:1:25
+C:-1:1:0:0
+F:SLAY_DEMON
+A:KILL_DEMON
+
+N:72
+X:10:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:3:1:10
+C:0:0:0:0
+F:SLAY_ORC
+
+N:73
+X:10:1
+T:21:0:255
+W:3:1:10
+C:1:-1:0:0
+F:SLAY_ORC
+
+N:74
+X:10:1
+T:23:0:255
+T:115:55:55
+W:3:1:10
+C:-1:1:0:0
+F:SLAY_ORC
+
+N:75
+X:11:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:15:2:17
+C:0:0:0:0
+F:SLAY_TROLL
+
+N:76
+X:11:1
+T:21:0:255
+W:15:2:17
+C:1:-1:0:0
+F:SLAY_TROLL
+
+N:77
+X:11:1
+T:23:0:255
+T:115:55:55
+W:15:2:17
+C:-1:1:0:0
+F:SLAY_TROLL
+
+N:78
+X:10:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:20:1:20
+C:0:0:0:0
+F:SLAY_GIANT
+
+N:79
+X:10:1
+T:21:0:255
+W:20:1:20
+C:-1:1:0:0
+F:SLAY_GIANT
+
+N:80
+X:10:1
+T:23:0:255
+T:115:55:55
+W:20:1:20
+C:0:0:0:0
+F:SLAY_GIANT
+
+N:81
+X:20:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:17:1:20
+C:0:0:0:0
+F:SLAY_DRAGON
+A:KILL_DRAGON
+
+N:82
+X:20:1
+T:21:0:255
+W:17:1:20
+C:1:-1:0:0
+F:SLAY_DRAGON
+A:KILL_DRAGON
+
+N:83
+X:20:1
+T:23:0:255
+T:115:55:55
+C:-1:1:0:0
+W:17:1:20
+F:SLAY_DRAGON
+A:KILL_DRAGON
+
+N:84
+X:31:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:17:1:35
+C:0:0:0:0
+F:KILL_DRAGON
+A:SLAY_DRAGON
+
+N:85
+X:31:1
+T:21:0:255
+W:17:1:35
+C:1:-1:0:0
+F:KILL_DRAGON
+A:SLAY_DRAGON
+
+N:86
+X:31:1
+T:23:0:255
+T:115:55:55
+W:17:1:35
+C:-1:1:0:0
+F:KILL_DRAGON
+A:SLAY_DRAGON
+
+N:87
+X:15:1
+T:18:0:255
+T:22:0:255
+T:24:0:255
+W:5:1:10
+C:-2:-2:0:0
+F:BLESSED
+A:CURSED
+
+N:88
+X:15:1
+T:21:0:255
+W:5:1:7
+C:-1:-3:0:0
+F:BLESSED
+A:CURSED
+
+N:89
+X:15:1
+T:23:0:255
+T:115:55:55
+W:5:1:10
+C:-3:-1:0:0
+F:BLESSED
+A:CURSED
+
+N:90
+X:-5:1
+T:15:0:255
+T:16:0:255
+T:18:0:255
+T:19:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:1:1:20
+C:0:0:0:0
+F:CURSED
+A:BLESSED
+
+N:93
+X:-10:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:1:1:20
+C:0:0:0:0
+F:AGGRAVATE
+A:STEALTH
+
+N:94
+X:13:1
+T:18:0:255
+T:19:0:255
+T:20:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:1:1:15
+C:0:0:0:0
+F:LITE1
+A:RES_LITE
+
+N:95
+X:40:1
+T:19:0:255
+W:20:1:38
+C:0:0:0:3
+F:XTRA_MIGHT
+
+N:96
+X:40:1
+T:19:0:255
+W:20:1:38
+C:0:0:0:0
+F:XTRA_SHOTS
+
+N:97
+X:25:1
+T:23:0:255
+T:24:0:255
+T:22:17:17
+T:22:3:3
+T:22:0:15
+T:115:55:55
+W:14:1:23
+C:3:0:0:0
+F:WOUNDING
+
+N:98
+X:60:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:27:1:50
+C:0:0:0:0
+F:KILL_UNDEAD
+A:SLAY_UNDEAD
+
+N:99
+X:45:1
+T:15:0:255
+T:16:0:255
+T:17:0:255
+T:18:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:25:1:34
+C:0:0:0:0
+F:KILL_DEMON
+A:SLAY_DEMON
+
+N:100
+X:20:1
+T:18:0:255
+T:21:0:255
+T:22:0:255
+T:23:0:255
+T:24:0:255
+T:115:55:55
+W:15:1:25
+C:2:0:0:0
+Z:BERSERK
+
+N:101
+X:15:1
+T:20:0:255
+T:21:0:255
+W:20:1:35
+C:0:0:0:0
+Z:EARTHQUAKE
+
+N:102
+X:5:1
+T:20:0:255
+W:2:1:9
+C:0:0:0:6
+F:STR
+
+
+### Armor ###
+
+N:295
+X:60:1
+T:36:0:255
+T:37:0:255
+W:20:1:90
+C:-70:-70:0:2
+F:LIFE
+
+N:296
+X:7:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:1:1:14
+C:0:0:0:0
+F:SUST_STR
+
+N:297
+X:6:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:6:0:255
+W:1:1:12
+C:0:0:0:0
+F:SUST_INT
+
+N:298
+X:6:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:6:0:255
+W:1:1:12
+C:0:0:0:0
+F:SUST_WIS
+
+N:299
+X:5:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:1:1:11
+C:0:0:1:0
+F:SUST_DEX
+
+N:300
+X:9:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:1:1:16
+C:0:0:0:0
+F:SUST_CON
+
+N:301
+X:4:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:1:1:9
+C:0:0:0:0
+F:SUST_CHR
+
+N:302
+X:50:1
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:30:1:70
+C:0:0:0:0
+F:IM_ACID
+
+N:303
+X:50:1
+T:34:0:255
+T:115:56:56
+W:30:1:90
+C:0:0:0:0
+F:IM_ACID
+
+N:304
+X:50:1
+T:35:0:255
+W:30:1:80
+C:0:0:0:0
+F:IM_ACID
+
+N:305
+X:45:1
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:30:1:78
+F:IM_ELEC
+
+N:306
+X:45:1
+T:34:0:255
+T:115:56:56
+W:30:1:98
+C:0:0:0:0
+F:IM_ELEC
+
+N:307
+X:45:1
+T:35:0:255
+W:30:1:88
+C:0:0:0:0
+F:IM_ELEC
+
+N:308
+X:55:1
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:30:1:86
+C:0:0:0:0
+F:IM_FIRE
+
+N:309
+X:55:1
+T:34:0:255
+T:115:56:56
+T:35:0:255
+W:30:1:95
+C:0:0:0:0
+F:IM_FIRE
+
+N:310
+X:47:1
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:30:1:90
+C:0:0:0:0
+F:IM_COLD
+
+N:311
+X:47:1
+T:34:0:255
+T:115:56:56
+T:35:0:255
+W:30:1:90
+C:0:0:0:0
+F:IM_COLD
+
+N:312
+X:47:1
+T:35:3:3
+W:30:1:100
+C:0:0:0:0
+F:IM_COLD
+
+N:313
+X:35:1
+T:34:0:255
+T:38:0:255
+T:115:56:56
+W:20:1:45
+C:0:0:0:0
+F:REFLECT
+
+N:314
+X:35:1
+T:34:10:10
+T:115:56:56
+W:10:1:15
+C:0:0:0:0
+F:REFLECT
+
+N:315
+X:17:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+T:6:0:255
+W:5:1:7
+C:0:0:0:0
+F:FREE_ACT
+
+N:316
+X:23:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+W:8:1:14
+C:0:0:0:0
+F:HOLD_LIFE
+
+N:317
+X:15:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:1:1:10
+C:0:0:0:0
+F:RES_ACID
+
+N:318
+X:15:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:1:1:10
+C:0:0:0:0
+F:RES_FIRE
+
+N:319
+X:13:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:1:1:9
+C:0:0:0:0
+F:RES_ELEC
+
+N:320
+X:12:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:1:1:8
+C:0:0:0:0
+F:RES_COLD
+
+N:321
+X:20:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:5:1:18
+C:0:0:0:0
+F:RES_POIS
+
+N:322
+X:15:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:6:1:15
+C:0:0:0:0
+F:RES_FEAR
+
+N:323
+X:10:1
+T:30:0:255
+T:31:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:56
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:5:1:10
+C:0:0:0:0
+F:RES_LITE
+
+N:324
+X:10:1
+T:32:0:255
+T:115:57:57
+W:1:1:7
+C:0:0:0:0
+F:RES_LITE
+
+N:325
+X:15:1
+T:32:0:255
+T:115:57:57
+W:5:1:15
+C:0:0:0:0
+F:LITE1
+A:RES_LITE
+
+N:326
+X:17:1
+T:32:0:255
+T:115:57:57
+W:10:1:20
+C:0:0:0:0
+F:LITE2
+A:RES_LITE
+
+N:327
+X:20:1
+T:32:0:255
+T:115:57:57
+W:15:1:25
+C:0:0:0:0
+F:LITE3
+A:RES_LITE
+
+N:328
+X:15:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:10:1:15
+C:0:0:0:0
+F:RES_DARK
+
+N:329
+X:20:1
+T:115:57:57
+T:32:0:255
+T:40:0:255
+T:45:0:255
+T:6:0:255
+W:13:1:27
+C:0:0:0:0
+F:RES_BLIND
+
+N:330
+X:17:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:13:1:18
+C:0:0:0:0
+F:RES_SOUND
+
+N:331
+X:15:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:115:56:57
+T:34:0:255
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:17:1:15
+C:0:0:0:0
+F:RES_NEXUS
+
+N:332
+X:19:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:16:1:19
+C:0:0:0:0
+F:RES_SHARDS
+
+N:333
+X:30:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:30:1:50
+C:0:0:0:0
+F:RES_NETHER
+
+N:334
+X:25:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+W:25:1:30
+F:RES_CHAOS
+
+N:335
+X:20:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+T:6:0:255
+W:25:1:35
+C:0:0:0:0
+F:RES_CONF
+
+N:336
+X:25:1
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:40:0:255
+T:45:0:255
+C:0:0:0:0
+W:23:1:32
+F:RES_DISEN
+
+N:337
+X:20:1
+T:35:0:255
+W:10:1:17
+C:0:0:0:0
+F:SH_FIRE | RES_FIRE
+
+N:338
+X:22:1
+T:35:0:255
+W:10:1:20
+C:0:0:0:0
+F:SH_ELEC | RES_ELEC
+
+N:339
+X:18:1
+T:115:57:57
+T:32:0:255
+T:33:0:255
+T:40:0:255
+T:45:0:255
+T:6:0:255
+W:5:1:15
+C:0:0:0:0
+F:SEE_INVIS
+
+N:340
+X:14:1
+T:30:0:255
+T:40:0:255
+T:45:0:255
+W:5:1:13
+C:0:0:0:0
+F:FEATHER
+A:FLY
+
+N:341
+X:80:1
+T:35:0:255
+T:30:0:255
+T:6:0:255
+W:40:1:90
+C:0:0:0:0
+F:FLY
+A:FEATHER
+
+N:342
+X:30:1
+T:115:57:57
+T:32:0:255
+T:33:0:255
+W:20:1:30
+C:0:0:0:0
+Z:MIND BLAST
+
+N:343
+X:30:1
+T:115:57:57
+T:32:0:255
+T:33:0:255
+W:20:1:30
+C:0:0:0:0
+Z:TELEKINESIS
+
+N:344
+X:35:1
+T:31:0:255
+W:25:1:40
+C:0:0:0:0
+Z:MIDAS TOUCH
+
+N:345
+X:15:1
+T:31:0:255
+C:0:0:0:0
+Z:COLD TOUCH
+
+N:346
+X:30:1
+T:35:0:255
+W:20:1:27
+C:0:0:0:0
+Z:BLINK
+A:RES_NEXUS
+
+### + To AC ###
+N:347
+X:15:4
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:115:56:57
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:6:0:255
+W:1:1:5
+C:0:0:5:0
+
+### + To Dam (Only Gloves) ###
+N:348
+X:40:1
+T:31:0:255
+W:1:1:45
+C:10:0:0:0
+
+### + To Hit (Only Gloves) ###
+N:349
+X:35:1
+T:31:0:255
+W:1:1:40
+C:0:10:0:0
+
+# N:index
+# X:power value:max number of time it can appear on one object
+# T:tval:min sval:max sval (up to 20 T: lines)
+# W:mininum player level to create it:rarity1:rarity2
+# C:max to dam:max to hit:max to AC:max to pval
+# F:flags
+
+# Helm of water breathing
+N:350
+X:50:1
+T:32:5:10
+W:20:1:25
+C:0:0:0:0
+F:WATER_BREATH
+
+
+### Amulets And Rings ###
+N:442
+X:15:1
+T:40:0:255
+T:45:0:255
+W:5:1:17
+C:0:0:0:5
+F:STR
+
+N:443
+X:11:1
+T:40:0:255
+T:45:0:255
+W:3:1:12
+C:0:0:0:5
+F:DEX
+
+N:444
+X:13:1
+T:40:0:255
+T:45:0:255
+W:5:1:15
+C:0:0:0:5
+F:WIS
+
+N:445
+X:13:1
+T:40:0:255
+T:45:0:255
+W:1:1:15
+C:0:0:0:5
+F:INT
+
+N:446
+X:7:1
+T:40:0:255
+T:45:0:255
+W:1:1:15
+C:0:0:0:5
+F:CHR
+
+N:447
+X:18:1
+T:40:0:255
+T:45:0:255
+W:7:1:20
+C:0:0:0:5
+F:CON
+
+N:448
+X:15:1
+T:40:0:255
+T:45:0:255
+W:5:1:15
+C:0:0:0:0
+F:SUST_STR
+
+N:449
+X:20:1
+T:40:0:255
+T:45:0:255
+W:7:1:20
+C:0:0:0:0
+F:SUST_CON
+
+N:450
+X:7:1
+T:40:0:255
+T:45:0:255
+W:1:1:15
+F:SUST_CHR
+
+N:451
+X:11:1
+T:40:0:255
+T:45:0:255
+W:1:1:11
+F:SUST_DEX
+
+N:452
+X:13:1
+T:40:0:255
+T:45:0:255
+W:5:1:13
+F:SUST_INT
+
+N:453
+X:13:1
+T:40:0:255
+T:45:0:255
+W:5:1:13
+F:SUST_WIS
+
+N:454
+X:40:1
+T:45:0:255
+C:0:0:0:5
+W:25:1:55
+F:INVIS
+
+N:455
+X:70:1
+T:45:0:255
+C:0:0:0:5
+W:40:1:90
+F:SPEED
+
+N:456
+X:17:1
+T:40:0:255
+T:45:0:255
+W:5:1:16
+C:0:0:0:0
+F:SLOW_DIGEST
+A:REGEN
+
+N:457
+X:20:1
+T:40:0:255
+T:45:0:255
+W:7:1:19
+C:0:0:0:0
+F:REGEN
+A:SLOW_DIGEST
+
+N:458
+X:15:1
+T:40:0:255
+T:45:0:255
+T:35:0:255
+W:10:1:16
+C:0:0:0:4
+F:STEALTH
+A:AGGRAVATE
+
+N:459
+X:7:1
+T:40:0:255
+T:45:0:255
+W:1:1:12
+C:0:0:0:6
+F:SEARCH
+
+N:460
+X:10:1
+T:40:0:255
+T:45:0:255
+W:1:1:12
+C:0:0:0:4
+F:INFRA
+
+N:461
+X:80:1
+T:45:0:255
+W:40:1:95
+C:0:0:0:3
+F:BLOWS
+
+N:462
+X:70:1
+T:45:0:255
+W:38:1:75
+C:0:0:0:0
+F:FLY
+A:FEATHER
+
+N:463
+X:80:1
+T:45:0:255
+W:43:1:85
+C:0:0:0:5
+F:CRIT
+
+
+### Lights ###
+
+N:501
+X:15:1
+T:39:0:255
+T:6:0:255
+W:5:1:15
+F:LITE1
+
+N:502
+X:20:1
+T:39:0:255
+W:10:1:25
+C:0:0:0:0
+F:LITE2
+
+N:503
+X:30:1
+T:39:0:255
+W:20:1:35
+C:0:0:0:0
+F:LITE3
+
+N:504
+X:15:1
+T:39:0:255
+W:10:1:15
+C:0:0:0:0
+F:RES_LITE
+
+N:505
+X:18:1
+T:39:0:255
+W:11:1:17
+C:0:0:0:0
+F:RES_DARK
+
+N:506
+X:20:1
+T:39:0:255
+W:15:1:22
+C:0:0:0:0
+F:SEE_INVIS
+
+N:507
+X:12:1
+T:39:0:255
+W:1:1:10
+C:0:0:0:4
+F:SEARCH
+
+N:508
+X:12:1
+T:39:0:255
+W:1:1:15
+C:0:0:0:4
+F:INFRA
+
+N:509
+X:21:1
+T:39:0:255
+W:5:1:20
+C:0:0:0:0
+Z:ILLUMINATE
+
+N:510
+X:35:1
+T:39:0:255
+W:20:1:27
+C:0:0:0:0
+Z:MAGIC MAP
+
+N:511
+X:30:1
+T:39:0:255
+W:20:1:24
+C:0:0:0:0
+Z:DETECT CURSES
+
+N:512
+X:25:1
+T:39:0:255
+W:20:1:17
+C:0:0:0:0
+Z:DAZZLE
+
+N:513
+X:40:1
+T:39:0:255
+W:20:1:50
+Z:DETECT DOORS AND TRAPS
diff --git a/lib/edit/re_info.txt b/lib/edit/re_info.txt
new file mode 100644
index 00000000..c0e36a92
--- /dev/null
+++ b/lib/edit/re_info.txt
@@ -0,0 +1,183 @@
+# File: re_info.txt
+
+# This file is used to initialize the "lib/raw/re_info.raw" file, which is
+# used to initialize the "monster ego race" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# Version stamp (required)
+
+# Most values can be used with the +, -, % and = operators, = will set the
+# monster value, + and - will modify it based on the normal monster
+# % will apply that percentage to the monster value
+# defaults : NO DEFAULT, MUST precise one
+
+# N:x:ego name
+# G:x:y (x=monster letter, y=colour, use * to use the same as the standard monster)
+# I:speed:(dice)d(side):aaf:ac:sleep
+# W:lev:rarity:weight:xp:place('B'efore or 'A'fter)
+# E:weapon:torso:arms:finger:head:legs
+# B:method:effect:(dice)d(side) (up to x4 lines)
+# F:flags that the standard monster MUST have - at least ONE of the R_CHAR_x
+# flags (if present, to determine which monster letters can have this ego
+# type), plus ALL of the rest
+# H:flags that the standard monster MUST NOT have
+# M:monster flags to add for the ego-type
+# O:monster flags to remove, use MF_ALL for all
+# S:monster spells to add for the ego-type
+# T:monster spells to remove, use MF_ALL for all
+
+V:2.0.0
+
+# A few undeads, to be created by the ANIM_DEAD spell
+
+N:1:Skeleton
+G:s:*
+I:%100:+1d+1:+0:+5:-5
+W:+5:13:%30:%95:B
+F:DROP_SKELETON
+H:UNDEAD | NONLIVING | R_CHAR_Z | R_CHAR_A | R_CHAR_E | R_CHAR_g
+M:DROP_SKELETON | UNDEAD | IM_COLD | IM_POIS | NO_FEAR | NO_CONF |
+M:NO_SLEEP | EMPTY_MIND | COLD_BLOOD | STUPID | EVIL
+O:GOOD | DROP_CORPSE | FRIEND | FRIENDS | ESCORT | ESCORTS | SMART |
+O:DROP_GREAT | DROP_GOOD | RAND_25 | RAND_50 | MORTAL
+T:MF_ALL
+
+N:2:Zombie
+G:z:*
+I:%95:%110d%100:%90:+10:-5
+W:+10:14:%70:%100:B
+F:DROP_CORPSE
+H:UNDEAD | NONLIVING | R_CHAR_Z | R_CHAR_A | R_CHAR_E | R_CHAR_g
+M:DROP_CORPSE | UNDEAD | IM_COLD | IM_POIS | NO_FEAR | NO_CONF
+M:NO_SLEEP | EVIL | EMPTY_MIND | COLD_BLOOD | STUPID | EVIL
+O:GOOD | DROP_SKELETON | FRIEND | FRIENDS | ESCORT | ESCORTS | SMART
+O:DROP_GREAT | DROP_GOOD | RAND_25 | RAND_50 | MORTAL
+T:MF_ALL
+
+N:3:Lich
+G:L:*
+I:%100:+0d+1:+10:+20:-10
+W:+30:22:+0:%200:B
+B:TOUCH:LOSE_DEX:+0d+0
+B:TOUCH:LOSE_DEX:+0d+0
+B:TOUCH:UN_POWER:+0d+0
+B:TOUCH:EXP_40:+0d+0
+F:DROP_SKELETON | SMART | R_CHAR_h | R_CHAR_p | R_CHAR_P | R_CHAR_O
+H:UNDEAD | NONLIVING | R_CHAR_Z | R_CHAR_A | R_CHAR_E | R_CHAR_g
+M:UNDEAD | IM_COLD | IM_POIS | NO_FEAR | NO_CONF
+M:NO_SLEEP | SMART | EVIL | COLD_BLOOD
+O:DROP_SKELETON | GOOD | DROP_CORPSE | FRIEND | FRIENDS | ESCORT | ESCORTS |
+O:DROP_GREAT | RAND_25 | RAND_50 | MORTAL
+S:1_IN_4 |
+S:BLINK | TELE_TO | TELE_AWAY | BRAIN_SMASH | DRAIN_MANA | CAUSE_3 |
+S:BLIND | HOLD | SLOW | SCARE
+
+N:4:Spectral
+G:G:*
+I:+10:%80d%100:+10:+20:-5
+W:+20:20:%10:%110:B
+B:*:EXP_20:+0d+0
+B:*:EXP_20:+0d+0
+F:DROP_CORPSE
+H:UNDEAD | NONLIVING | R_CHAR_Z | R_CHAR_A | R_CHAR_E | R_CHAR_g
+M:UNDEAD | IM_COLD | IM_POIS | NO_FEAR | NO_CONF
+M:NO_SLEEP | PASS_WALL | EVIL | COLD_BLOOD
+O:GOOD | DROP_CORPSE | FRIEND | FRIENDS | ESCORT | ESCORTS |
+O:DROP_GREAT | EMPTY_MIND | RAND_50 | MORTAL
+S:1_IN_5 | BLIND | HOLD | SCARE
+T:MF_ALL
+
+N:5:Captain
+G:*:v
+I:+5:%150d%100:+5:%120:-2
+W:+5:4:%120:%150:A
+B:*:*:+0d+1
+B:*:*:+0d+1
+B:*:*:+0d+1
+B:*:*:+0d+1
+F:R_CHAR_o | R_CHAR_y | R_CHAR_k | BASEANGBAND
+M:FORCE_MAXHP | FRIENDS | SMART | DROP_1D2
+
+N:6:Chieftain
+G:*:*
+I:+10:%200d%100:+10:%120:-3
+W:+5:4:%120:%200:A
+B:*:*:+1d+2
+B:*:*:+1d+2
+B:*:*:+1d+2
+B:*:*:+1d+2
+F:R_CHAR_T | R_CHAR_P | R_CHAR_O | BASEANGBAND
+M:FORCE_MAXHP | FRIENDS | SMART | DROP_1D2
+
+N:7:Shaman
+G:*:r
+I:+0:%90d%100:+10:%90:+0
+W:+2:1:%90:%120:A
+F:R_CHAR_o | R_CHAR_k | R_CHAR_n | BASEANGBAND
+M:SMART | FORCE_MAXHP | DROP_1D2
+O:FRIENDS
+S:1_IN_6 | MISSILE | CAUSE_1 | CONF | BLINK
+
+N:8:Priest
+G:*:G
+I:+0:%90d%100:+10:%90:+0
+W:+3:2:%90:%120:A
+F:R_CHAR_T | R_CHAR_P | R_CHAR_O | BASEANGBAND
+M:FRIENDS | SMART | FORCE_MAXHP | DROP_1D2
+S:1_IN_6 | CAUSE_2 | MISSILE | DARKNESS | CONF | SCARE | BLINK
+
+N:9:Mage
+G:*:r
+I:+0:%150d%100:+10:%120:+0
+W:+5:4:%120:%150:A
+B:*:*:+0d+0
+B:*:*:+0d+0
+B:HIT:HURT:=2d=8
+B:HIT:HURT:=2d=8
+F:R_CHAR_O | BASEANGBAND
+M:SMART | FORCE_MAXHP | DROP_1D2
+O:FRIENDS
+S:1_IN_6 | BA_COLD | BO_FIRE | TRAPS | HEAL | HOLD | S_MONSTER | TPORT
+
+N:10:Archer
+G:*:W
+I:+0:+0d+0:+0:+0:+0
+W:+1:1:%100:%110:A
+F:R_CHAR_y | R_CHAR_k | R_CHAR_O | R_CHAR_o | BASEANGBAND
+S:1_IN_4 | ARROW_2
+
+N:11:Rogue
+G:*:b
+I:+2:+0d+0:+0:+10:-30
+W:+1:2:%90:%100:A
+B:*:EAT_GOLD:+0d+0
+F:R_CHAR_y | R_CHAR_k | R_CHAR_o
+
+# For townpeople
+N:12:Elven
+G:*:*
+I:+2:+0d+0:+0:+0:+0
+W:+0:15:+0:+0:B
+F:WILD_TOWN
+H:ANIMAL
+
+# For townpeople
+N:13:Dwarven
+G:*:*
+I:+2:+0d+0:+0:+0:+0
+W:+0:15:+0:+0:B
+F:WILD_TOWN
+H:ANIMAL
+
+# N:x:ego name
+# G:x:y x is the char, y the attribute, * means the normal one
+# I:speed:(dice)d(side):aaf:ac:sleep
+# W:lev:rarity:weight:xp:place('B'efore or 'A'fter)
+# F:flags that the normal monster *must* have
+# H:flags that the normal monster *must not* have
+# M:monster flags that the ego-monster adds
+# O:monster flags to remove (use MF_ALL for all)
+# S:monster spells that the ego-monster adds
+# T:monster spells to remove (use MF_ALL for all)
diff --git a/lib/edit/readme.txt b/lib/edit/readme.txt
new file mode 100644
index 00000000..4c0ecbe7
--- /dev/null
+++ b/lib/edit/readme.txt
@@ -0,0 +1,96 @@
+# File: a_info.txt
+# This file is used to initialize the "artifact" information for the Angband game.
+# This is were you find Cubragol, The Phial , Ringil etc.
+
+# File: ba_info.txt
+# This file is used used to initialize the "store/building actions type" information for the Angband game.
+# This is where you find the ID numbers for 'Presage fate', 'Play craps' , 'Sell an item' etc.
+
+# File: d_info.txt
+# This file is used to used to set the dungeons for the Angband game.
+# This is where you find 'Barrow Downs', 'The Maze' , 'Mordor' etc.
+
+# File: e_info.txt
+# This file is used to initialize the "ego-item" information for the Angband game.
+# This is where you find 'Helms of the Noldor' , 'Filthy rags of leprousness' , 'Boots of Jumping' etc.
+
+# File: f_info.txt
+# This file is used to used to initialize the "terrain feature" information for the Angband game.
+# This is where you find the ID numbers for 'Underground tunnel' , 'grass with flowers' , 'open floor' etc.
+
+# File: k_info.txt
+# This file is used to initialize the "object kind" information for the Angband game.
+# This is where you find 'Katanas' , 'Mushrooms of Sickness' , 'Jewel Encrusted Crowns' etc.
+
+# File: misc.txt
+# This file contains a lot of 'maximums ' for the Angband game.
+# This is where you find 'Maximum number of skills in s_info.txt' ,
+#'Maximum number of items in k_info.txt' , 'Maximum number of artifacts in a_info.txt' etc.
+
+# File: ow_info.txt
+# This file is used to initialize the "owner info type" information for the Angband game.
+# This is where you find 'Bilbo the Friendly(Hobbit)' , 'Raistlin the Chicken(Human)' ,
+# Inglorian the Mage(Human) etc.
+
+# File: p_info.txt
+# This file is used to initialize the "player race/race mod/class" information for the Angband game.
+# You will find here races like 'Humans' , subraces like 'Vampire', classes like 'Monk'
+
+# File: r_info.txt
+# This file is used to initialize the "monster race" information for the Angband game.
+# You will find here monsters like 'Marylene, Heartbreakeress of the Netherworld',
+# 'The Minotaur of the Labyrinth' , 'Morgoth, Lord of Darkness' etc.
+
+# File: ra_info.txt
+# This file is used to initialize the "randart parts" information for the Angband game.
+# Here you will find info for random artefacts made of 'Mage Staves' , 'Lights' , 'Gloves' etc.
+
+# File: re_info.txt
+# This file is used to initialize the "monster ego race" information for the Angband game.
+# Here you will find ego monster types like 'Spectral','Skeleton','Archer' etc.
+
+# File: s_info.txt
+# This file is used to initialize the "skills" information for the ToME game.
+# Here you will find player & monster skills, 8you can use their IDs in other files(?),
+# You will find skills like 'Bearform-combat' , 'Necromancy' , 'Spell-power' etc.
+
+# File: set_info.txt
+# This file is used to initialize the "lib/raw/set_info.raw" file, which is
+# used to initialize the "item set" information for the Angband game.
+# You find linked Items like 'The bow of Bard' & 'The arrow of Bard'
+# It is like totally unclear to me what this does, especially because
+# the big spider doesnt drop Sting, hint hint !
+
+# File: special.txt
+# Contains terrain parsings for the special levels now being kept in seperate map files
+# You will find there entries as in f_info.txt
+
+# File: st_info.txt
+# This file is used to initialize the "store info type" information for the Angband game.
+# You will find there stores like 'Armoury' , 'Temple' , 'The Mathom-house' etc.
+
+# File: t_info.txt
+# Includes the town definitions of the game Angband
+# You will find here the towns like 'Gondor' , 'Bree' , 'Lothlorien' etc.
+
+# File: t_pref.txt
+# Defines the preferences for the town features
+# You will find there entries as in f_info.txt
+
+# File: tr_info.txt
+# This file comes from Angband64 written by Jurriaan Kalkman
+# and describes the traps items can have
+# You will find traps like 'Summon Fast Quylthulgs Trap' , 'Wisdom Trap' etc.
+
+# File: v_info.txt
+# This file is used to initialize the "vault template" information for the Angband game.
+# You will find vaults like 'The I in the Storm' , 'Greater vault (mortuary temple of sety)' ,
+# 'Lesser vault (amenhotep I)' etc.
+
+# File: W_info.txt
+# This is the wilderness
+# Change the dimensions at your perils, most likely the game will crash !!!
+
+# File: wf_info.txt
+# This file is used to initialize the "wilderness feats" information for the Angband game.
+# You will stuff like 'Ekkaia, the Encircling Sea' , 'mountain' , 'Minas Anor' etc.
diff --git a/lib/edit/s_crypt.map b/lib/edit/s_crypt.map
new file mode 100644
index 00000000..3d6ce71c
--- /dev/null
+++ b/lib/edit/s_crypt.map
@@ -0,0 +1,109 @@
+# Special level "The Forgotten Crypt" in The Pits of Angband
+#
+# Created for PernAngband 5.0.1 on 18/8/01
+# Written by Mynstral (mynstral@thehelm.com)
+
+%:special.txt
+
+### Guaranteed Monsters
+# Vampire on normal floor
+F:a:1:0:432
+
+# Ghoul on normal floor
+F:b:1:0:418
+
+# Rotting Quylthulg on normal floor
+F:c:1:0:633
+
+# Master Vampire on normal floor
+F:d:1:0:520
+
+# Vampire Lord on normal floor
+F:e:1:0:623
+
+# Greater Rotting Quylthulg on normal floor
+F:f:1:0:802
+
+# Ghast on normal floor
+F:g:1:0:327
+
+# Undead Beholder on normal floor
+F:h:1:0:664
+
+# Thuringwethil, the Vampire Messenger on normal floor
+F:i:1:0:755
+
+# Black Reaver on normal floor
+F:j:1:0:798
+
+### Random Monsters and/or Items
+# Random monster (upto 5 levels ood) on normal floor
+F:&:1:0:*75
+
+# Random monster (upto 11 levels ood) on normal floor
+F:@:1:0:*81
+
+# Random monster (upto 9 levels ood) and
+# Random object (upto 7 levels ood) on normal floor
+F:8:1:0:*79:*77
+
+# Random monster (upto 40 levels ood) and
+# Random object (upto 20 levels ood
+F:9:1:0:*110:*90
+
+# Random monster (upto 3 levels ood)
+F:-:1:0:*73
+
+# Random object (upto 7 levels ood)
+F:=:1:0:0:*77
+
+### Guaranteed Items
+# The Shadow Cloak of Luthien on normal floor
+F:1:1:0:0:0:0:49
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XA.%@=-=-X8XX6...XX.XXLLL..c..LLLXX.XX...7XX8X=-=-...5X
+D:X.%%@=-=-X99X.....X..XLL..X%X..LLX..XhVV..X99X=-=-..GGX
+D:X%%@@...XXX9XX....XX.XX...X.X...XX.XXVVVVXX9XXX=-=.XGGX
+D:X@@@....X8X-=XgggggX..X..dX4Xd..X..XWWWWWX-=X8X-=-.XGGX
+D:X......XX%XX=XXgddgXX.XX8GXXXG8XX.XXWWWWXX-XX%XX...XGGX
+D:XIXIXXIX*X=X-=XgddggX..X8G^.^G8X..XWWWWWX-=X.X.X&&&XGGX
+D:XXXIXIXX*X-XX=XXggggXX.XXG^.^GXX.XXWWWWXX-XX.X.XX&&XGGX
+D:XXXXXIX**X=-X=-XIIXXXX..XG^.^GX..XVWWWVX=-XX.XddX@@XGGX
+D:X^^^^XX**X=-XX=XXIXIIXX.XX^.^XX.XXVVVVXX-XX%.XedXX@XGGX
+D:X^^f^Xe**X%%.X=-XXIXXIXbbX^.^XbbX.VV.XX=-XbX.XXXIX@IIGX
+D:X^^^XXe..X...XX=-XXXIXXXbXX.XXbXX...XX=-XXbX.XaaaXXXIGX
+D:X^^XXbbb.XGGG.XX=-XXIIXXbbXDXbbX...XX=-XXbbX.XaaaaXXIGX
+D:X^XXcbab.XVdG.cXX=-XXXIXXabXbaXX.XXX=-XXbbbX.XaaaaaXXfX
+D:X%XbbbbbbXVWG.%.XX.DbXDXXXbXbXXXDXbD.XX....G.XGGGGGGX%X
+D:XX.bababa%eWG.%.BXXXbcbbbbX.XbbbbcbXXXC...fX.%.......XX
+D:X%XbbbbbbXVWG.%.XX.DbXDXXXbXbXXXDXbD.XX....G.XGGGGGGX%X
+D:X8XXcbab.XVdG.cXX=-XXX.XXabXbaXXLXXX=-XXbbbX.XaaaaaXX.X
+D:X88XXbbb.XGGG.XX=-XX...XbbXDXbbXLLLXX=-XXbbX.XaaaaXXX.X
+D:XXXIXXe..X...XX=-XX.GGXXbXX.XXbXXILIXX=-XXbX.XaaaXXXX.X
+D:X9889Xe**X%%.X=-XXG.GdXbbX^.^XbbXILLIXX=-XbX.XXXIXhGG.X
+D:XXIXXXX**X=-XX=XXdG.GXX.XX^.^XX.XXILIIXX-XX%.XedXXXXX.X
+D:X99999X**X=-X=-XGGG..X..XI^a^IX..XILLIIX=-XX.XddX..eeeX
+D:XXXXIXXX*X=XX-XX....XX.XXI^a^IXX.XXILLIXX=XX.X.XX.GGGGX
+D:X^^^^^cX*X-X=-X.....X..X.I^a^I.X..XIILLIX=-X.X.Xdddd..X
+D:XllllX^XX%XX=XXG.GGXX.XX.IXXXI.XX.XXIILIXX-XX%XXXXXXX.X
+D:X^^^cl^=X8X-=XeG.GeX..X...X4X...X..XILLLIX-=X8X.aaaaaaX
+D:XLLX^l^-XXX9XXGG.GXX.XX...X.X...XX.XXLLLLXX9XXX.GGGGGGX
+D:X..L^l^=-X99X.....X..XWW..X%X..WWX..XLLLL.X99XXbbbbb..X
+D:X7.L^l^=-X8XX5...XX.XXWWW..c..WWWXX.XXLL.AXX8XXbbbbb.6X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXIIIIXIIIIXXIIIXIIIIXXIIIXXXXggggXc..cXaaaaXbb.CX
+D:XIIIIIXIXXIIIXXIIXIXIXIXXIXXIXIXXXXgddg%....%aaaa%bb..X
+D:XIXXXIXIXXXXXXXXIIIXIXIIXIXXIXIXXXXggggXc..cXaaaaXbbbbX
+D:XIXXXIIIXIIIIIIXXXXXIXXIXIXIIXIXXXXXXXXXXIIXXXXXXXXXXXX
+D:X..XXXXXXIXXXXIXIIIXIXIIXIXIXXIXXXXVVVjXddddXe..eX....X
+D:XB.IIIIIXIIIXIIXIXIXIIIXXIXIXIIXIIIVVVEX....%.ee.%.hh.X
+D:X..XXXXIXXXIXIXXIXIXXXXXIIXIXIXXIXXVVVjXddddXe..eX....X
+D:XIXXXIXIIIXIXIIIIXIIIIXIIXXIXIXXIXXXXXXXXXXXXXXXXXXDDXX
+D:XIXXXIXXXIIIXXXXXXXXXIXIXXIIXIIIIXXGGGGXgggdddeIVX....X
+D:XIIXXIXXXXXXXIIIIXXIXXXIIIIXXXXXXXXE.heDgggaaaiG1X....X
+D:XXIIIIIIIIIIIIXXIIIIXIIIXXXXXXXXXXXGGGGXgggdddeIVX..j>X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting location
+P:17:29
diff --git a/lib/edit/s_death.map b/lib/edit/s_death.map
new file mode 100644
index 00000000..398b5fd1
--- /dev/null
+++ b/lib/edit/s_death.map
@@ -0,0 +1,104 @@
+# Special level "Deathwatch" in the Orc Caves
+#
+# Created for PernAngband 5.0.1 on 18/8/01
+# Altered by Mynstral (mynstral@thehelm.com)
+
+%:special.txt
+
+### Guaranteed monsters
+# Snaga on normal floor
+F:a:1:0:118
+
+# Cave orc on normal floor
+F:b:1:0:126
+
+# Hill orc on normal floor
+F:c:1:0:149
+
+# Black orc on normal floor
+F:d:1:0:244
+
+# Half-orc on normal floor
+F:e:1:0:264
+
+# Uruk on normal floor
+F:f:1:0:313
+
+# orc captain on normal floor
+F:g:1:0:285
+
+# Lagduf on normal floor
+F:h:1:0:140
+
+# Grishnakh on normal floor
+F:i:1:0:186
+
+# Golfimbul on normal floor
+F:j:1:0:215
+
+### Guaranteed items
+# Thalkettoth on normal floor
+F:1:1:0:0:0:0:28
+
+# Maedhros on normal floor
+F:2:1:0:0:0:0:64
+
+# Cammithrim on normal floor
+F:3:1:0:0:0:0:53
+
+### Dungeon Design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X XXXXXXXXXXXX X
+D:X X..........X XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X
+D:X X....>.....X X.XdX.X.X.XcX.X.X...XcX.X.XcX.X.XgXd+2X X
+D:X X..........X XDXDXDXDXDXDXDXDX...XDXDXDXDXDXDXDXDXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X
+D:X XXXXXDXXXXXX X...............D...D...............X X...f............a...XXXXX.......b..f....a....+1X X
+D:X X.X XDXDXDXDXDXDXDXDX..cXDXDXDXDXDXDXDXDX X........b...........XXXXX...a..........b.....XXX X
+D:X X.X X.XcX.XcXdX.X.X.X...XcX.XdXcX.X.X.XgX XXXXXXXXXXDDXXXXXXXXXXXXXXXXXXXXXXXXDDXXXXXXXXX X
+D:X X.X XXXXXXXXXXXXXXXXX.c.XXXXXXXXXXXXXXXXX XXXXXXXXXXDDXXXXXXXXXXXXXXXXXXXXXXXXDDXXXXXXXXX X
+D:X X.X XXX X...X X...........f...a....XXXXX....b.a...........b.X X
+D:X XX.XX X.XXXXXXXXXXXXDDDXXXXX X..f...b.............XXXXX..a....f.....b..a...X X
+D:X XX...XX XXXDXXXXXXXXX.......c.XX XXXXXXXXXXDDXXXXXXXXXXXXXXXXXXXXXXXXDDXXXXXXXXX X
+D:X XX.....XX XdD........D...c......dX X........a...............a....................X X
+D:X XX.......XX XXXDXXXXXXXX......c....X Xf....b..........f............a.........f.....X X
+D:X X.........X XeX XX.........XX X.............a.......b...........a...........X X
+D:X XXXXX.XXXXX X.X XXXXDDDXXXX X....a...............................a........X X
+D:X X.X X.X X...X XXXXXXXXXXXXXXXXXXXXX...XXXXXXXXXXXXXXXXXXXXXXX X
+D:X X.X X.X X...X X...X X
+D:X X.X X.X XX...XX X.b.X X
+D:X X.X X.X XX.....XX X...X X
+D:X XXDXX XXXX X.X XX.......XX X..bX X
+D:X X...XXXX..X XdX XX...ccc...XX X...X X
+D:X X...+..D..X X.X XX....ccc...dXX X...X X
+D:X X...XXXX..X X.X XX.d..ccicc....XX XXXXXXXXXXXXXDDDXXXXXXXXXXXX X
+D:X XX.XX XXXX X.X XX......ccc..g...XX XX......a...............f...XX X
+D:X X.X X.X XX...g...ccc.......XX XXX..f.........................XX XXXXX X
+D:X X.X X.X X......g.........g..X XX..................b....a.......XX Xa.aX X
+D:X X.X XgX XXXX..XXXXXXX..XXXXXX XX.....a.........a..............a..XX X.a.X X
+D:X X.X X.X X..X X..X XXXXXXXX..........f........................XX XDXXX X
+D:X X.X X.X Xd.XXXXXXX..XXXXXXX X.....D...b.................b...f...........XXXXXXXXXXX.X X
+D:X X.X X.X X...c.......D.....X X.....D...........a..h.............a........D......a....X X
+D:X X.X X.X X.......c.g.D.....X X...XXXX.....b.............................XXXXXXXXXXXX.X X
+D:X XXXX.XXXX X.X XXXXXXXXXXXXXXXX..XXXXXXX X...X XX..............a......a...........XX X.X X
+D:X X.......X X.X X........XXXXXXX...X XX.........f.............f.......XX X.X X
+D:X XXX...XXX X.X X..d...............X XX.....b...........b.........f.XX XaX X
+D:X X.......X X.X X........XXXXXXX...X XX..........b................XX X.X X
+D:X XXXX.XXXX X.X XXXXXXXXXX XXXXX XXXXXXXXXXXXDDDDDXXXXXXXXXXXX X.X X
+D:X X.X XDXXXXXXXXXXXXXXX X.....X X.X X
+D:X X.X X..d...e......d.XXXXXXXXXXX XXXXXXXXXXXX X.b...X X.X X
+D:X X.X X....e..g.......D.........X X.d..gg..eeX XXX.....XX X.X X
+D:X X.X XXXXX X..g..d.....e...XXXXXXXXDDXXXXXXXX....gg..eeXXXXX XX...XXX..XXX XXXXXXDXX X
+D:X X.X XeeeX XDXXXXXXXXXXXXXXX Xg.d.......D.e..e..gg..eej+3+% XX....XXX....XXXXXX XXX...D..aX X
+D:X X.X Xeg.X XX.XX X....e....eD.e.....gg..eedXXX% X.............X...X XaD...X...X X
+D:X XXXX.XXX XX+XXXX...X X.d........XXXX....gg..eeXX % XXXXX XXXXXXXXXXXX..D...X XXX..aX...X X
+D:X X......X XX.......X XXXX...e...d..XXXX...dgg..eeX % X...X XX.....D..XXXXXX XXXXXXXDXX X
+D:X X......X X.g.....XXXXXX X..D....g..e..Dd.XXXXXXXXXXXX % XXXDXX X......X..X....X X...X X
+D:X X......X X...d...DeeeeX X..XXXXXXXXXXXX..X + X...X XXXXXXXX..X....X X...X X
+D:X X......X X.....e.XXXXXX XXXX XXXX % X...XXXXXX X....D..D....X XXXXXXXXDXDXXXX X
+D:X XXXDDXXXXXXXXXXXXXX+XXXXXX % X....X...X X..XXX..XXXXXXXXX X.....a..X....X X
+D:X X........%........X %+%%....D...XXXXXX.D..D.....+.X X........X....X X
+D:X XXXXXXXXXXXXXXXXXXX XXXXXX..........X..X.....XXX XXXXXXXXXXXXXXX X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting Location
+P:51:125
diff --git a/lib/edit/s_doom.map b/lib/edit/s_doom.map
new file mode 100644
index 00000000..5be3b67d
--- /dev/null
+++ b/lib/edit/s_doom.map
@@ -0,0 +1,226 @@
+# Mount Doom
+
+### Terrain Features
+# Permanent Wall
+F:X:177:0
+
+# Lava Wall
+F:#:177:0
+F: :177:0
+
+# Great Fire
+F:$:178:0
+
+# Fire
+F:%:205:0
+
+# Hidden Door
+F:+:48:0:0:0:0:0:0:0:177
+
+# Normal Door
+F:D:32:0
+
+# Shallow Lava
+F:.:86:0
+
+# Deep Lava
+F:L:85:0
+
+# Treasure (random) on shallow lava
+F:*:86:0:0:*
+
+# Trap (random) on deep lava
+F:^:85:0:0:0:0:0:*
+
+# Trap (random) on shallow lava
+F:t:86:0:0:0:0:0:*
+
+# up staircase
+F:<:6:0
+
+### Guaranteed Monsters
+# Greater Balrog on deep lava
+F:A:85:0:807
+
+# Greater Balrog on shallow lava
+F:a:86:0:807
+
+# Lesser Balrog on deep lava
+F:B:85:0:996
+
+# Lesser Balrog on shallow lava
+F:b:86:0:996
+
+# Pit Fiend on deep lava
+F:C:85:0:812
+
+# Pit Fiend on shallow lava
+F:c:86:0:812
+
+# Great Wyrm of Power on deep lava
+F:E:85:0:847
+
+# Great Wyrm of Power on shallow lava
+F:e:86:0:847
+
+# Bone Golem on deep lava
+F:F:85:0:1013
+
+# Bone Golem on shallow lava
+F:f:86:0:1013
+
+# Dracolisk on deep lava
+F:G:85:0:703
+
+# Dracolisk on shallow lava
+F:g:86:0:703
+
+# Nycadaemon on deep lava
+F:H:85:0:719
+
+# Nycadaemon on shallow lava
+F:h:86:0:719
+
+# Barbazu on deep lava
+F:I:85:0:720
+
+# Barbazu on shallow lava
+F:i:86:0:720
+
+# Plasma Hounds on deep lava
+F:J:85:0:726
+
+# Plasma Hounds on shallow lava
+F:j:86:0:726
+
+# Hell knight on deep lava
+F:K:85:0:731
+
+# Hell knight on shallow lava
+F:k:86:0:731
+
+# Nightcrawler on deep lava
+F:M:85:0:744
+
+# Nightcrawler on shallow lava
+F:m:86:0:744
+
+# Aether Hound on deep lava
+F:N:85:0:811
+
+# Aether Hound on shallow lava
+F:n:86:0:811
+
+# Eye druj on deep lava
+F:O:85:0:749
+
+# Eye druj on shallow lava
+F:o:86:0:749
+
+# Skull druj on deep lava
+F:P:85:0:750
+
+# Skull druj on shallow lava
+F:p:86:0:750
+
+# Great Hell Wyrm on deep lava
+F:Q:85:0:756
+
+# Great Hell Wyrm on shallow lava
+F:q:86:0:756
+
+# Nightwalker on deep lava
+F:R:85:0:768
+
+# Nightwalker on shallow lava
+F:r:86:0:768
+
+# Osyluth on deep lava
+F:S:85:0:773
+
+# Osyluth on shallow lava
+F:s:86:0:773
+
+# Great Wyrm of Many Colours on deep lava
+F:U:85:0:790
+
+# Great Wyrm of Many Colours on shallow lava
+F:u:86:0:790
+
+# Horned Reaper on deep lava
+F:V:85:0:811
+
+# Horned Reaper on shallow lava
+F:v:86:0:811
+
+# Bronze Golem on deep lava
+F:W:85:0:1015
+
+# Bronze Golem on shallow lava
+F:w:86:0:1015
+
+### Random Monsters and/or Items
+# Random monster on deep lava
+F:!:85:0:*99
+
+# Random monster on shallow lava
+F:1:86:0:*99
+
+# Random monster (upto 10 levels ood) on deep lava
+F:@:85:0:*109
+
+# Random monster (upto 10 levels ood) on shallow lava
+F:2:86:0:*109
+
+# Random monster and
+# Random object on deep lava
+F:&:85:0:*99:*99
+
+# Random monster and
+# Random object on shallow lava
+F:7:86:0:*99:*99
+
+# Random monster (upto 10 levels ood) and
+# Random object (upto 5 levels ood) on deep lava
+F:(:85:0:*109:*104
+
+# Random monster (upto 10 levels ood) and
+# Random object (upto 5 levels ood) on shallow lava
+F:9:86:0:*109:*104
+
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X #%%NNLL...JJJ....LL# ############## X
+D:X ##### #NNNN...Ljjjjj..LLL# #############################################..1!!!!...L### X
+D:X##.LL## #nNNN...LLJJj.LLLLL# #L....&!&!&....L%%%%%%%L..mRR%R%%M%MML%LL...DL...!1&..@L%%# X
+D:X#LL<..# #nn.LL.LLLL...%%%LL# #..##########################################LLL....LLL%%%# X
+D:X##%LL## #....LLL%%LLL..^LLL# #.L# #LL2.t..q.LLL## X
+D:X ###+# #D##############+#######.L# ##%%.LLLL&L...# X
+D:X #!# #.# #.......L%%# ##%%LL..2...t.## X
+D:X #^# #.############ ##########+# XXXXXXXXXX ##.9.t.LLLLL## X
+D:X #.# #LL%%%%LL...p# #11111# X########X ###############.2..LQ#+# X
+D:X ##.# ############D## #11211# X#9....9#X #o.....t...# ###^L..#(# X
+D:X #L## ##%%V## #12221# XXXXXX#..$$..#XXXXXX#LLLLKkk...# ###D#### X
+D:X ##!# ##.VH^%## #12221# X######t....m######X#LLLLkkk.LL# #.# X
+D:X #.# ##.iH%%hv## #11211### X#sSSSrrt%.mVVVLLe#X#.LL.kkk...# ##.# X
+D:X ###D######### #tiI%HHhvL# #11111+9# X#rRssRr.%.mVvvm..#X#%...LLL...# ##Lo###### X
+D:X #.^t^LLL...L## #.LIH%Hc..# ######### X#.MmmM..%..MMLLL.#X#%%......L&# ##.L##EBBB# X
+D:X #.jJ%%%L....L# ##..ICCL.## X#.L..L..%ttL.....#X########D### #.####LAAB## X
+D:X #JJJJL%LLL&..# ##..L.L## X#9LLL...%..LL(LLq#X #..L..# #.LL..LB%%L# X
+D:X #J%J..LLL%%LL## ##.t.## X######......######X #L.#### ###..LL%%%L## X
+D:X #jJJ^..bL%LL..## ##D################# XXXXXX#.LUUL.#XXXXXX #.L# #.PL%%%@(L# X
+D:X ##....BABLL..L.## #.uLL^L..L%^^..L%%# X#.L%%LL#X #L.#### ###.L%LL..##X
+D:X ##....B%%L.....## #################L# X#LLL..t#X #..twW# #L..LL...+pX
+D:X ###....LLL...LLL####################.# X#%L..7.#X #####L# ###LL...1###oX
+D:X #9+L%%L...LLL....D.+..%%oF.%%^..%%LL.# X#LLLLt.#X #p# #%%%!@..## #tX
+D:X ##################L###.L###..###.L#### X#7tt.LL#X #.# ######### #EX
+D:X ##################.# ############## X#.LLLL.#X ######D###################LX
+D:X ##11!!!&LL.+(#L# X#LL%%LL#X #G..LLL.LL...D..LL....^LLL%X
+D:X ##1!!!%%%%Lp###L# X#L%%%%Q#X #..LF....K...##############X
+D:X ##!!%%%%%LLL..#.# XXXXDDXXXX########.LLLL..KKK..#### X
+D:X ##.LLLLLLL...+.# #%%%%%%%%%%%%%+LL7..LL.k..L+o7# X
+D:X ############### ############################### X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting location
+P:6:6
diff --git a/lib/edit/s_factory.map b/lib/edit/s_factory.map
new file mode 100755
index 00000000..111829f1
--- /dev/null
+++ b/lib/edit/s_factory.map
@@ -0,0 +1,238 @@
+# Map "s_factory.map"
+#
+# Special Level "Foul Factory" in the Illusory Castle
+#
+# Created for ToME 3.x on 11/11/2003
+# Written by Lord Dimwit (lorddimwit@hotmail.com)
+#
+# Middle-game dungeon
+# Comments: This level isn't designed to kill the player, really, just
+# induce such severe aggravation that their head explodes. There are
+# two parts of it, the "abusive trickery" part that leads the player
+# through a horrendous maze of strategically placed glass walls, permanent
+# walls, and ethereal walls (like permanent walls only invisible, very awful),
+# followed by a "Factory" stage that follows more traditional patterns of
+# throwing monsters systematically at the player.
+
+# Backported to ToME 2.x by Massimiliano Marangio on 02/07/2004
+
+# Replaced the letter : by R for Rubble
+# Changed the starting position to 21:51
+# Corrected the exits of the jumpgates
+
+### F:<letter>:<terrain>:<cave_info>:<monster>:<object>:<ego>:<artifact>:<trap>:<special>:<mimic>
+
+### Terrain Features
+
+
+# Permanent wall
+F:X:61:4
+
+# Fake wall
+F:I:189:0
+
+# Ethereal wall
+F:#:214:0
+
+# Copper Pillar
+F:;:213:0
+
+# Mountains
+F:M:97:0
+
+# granite
+F:G:57:4
+
+# Floor
+F:.:1:0
+
+# Deep Water
+F:~:187:4
+
+# Ash
+F:,:93:4
+
+# Fire
+F:%:205:0
+
+# Deep lava
+F:L:85:0
+
+# Shallow lava
+F:l:86:0
+
+# Glass Wall
+F:_:188:0
+
+# Rubble
+F:R:49:0
+
+# Trees
+F:T:96:0
+
+# Tainted water
+F:t:174:0
+
+# Chasm
+F:C:87:0
+
+# up staircase
+F:<:6:0
+
+# down staircase
+F:>:7:0
+
+# between gate 1
+F:1:160:6:0:0:0:0:0:845
+
+# between gate 2
+F:2:160:6:0:0:0:0:0:846
+
+# between gate 3
+F:3:160:6:0:0:0:0:0:4370
+
+# between gate 4
+F:4:160:6:0:0:0:0:0:3339
+
+# between gate 5
+F:5:160:6:0:0:0:0:0:4119
+
+# between gate 6
+F:6:160:6:0:0:0:0:0:6659
+
+# between gate 7
+F:7:160:6:0:0:0:0:0:9257
+
+# between gate 8
+F:8:160:6:0:0:0:0:0:8018
+
+# between gate 9
+F:9:160:6:0:0:0:0:0:9298
+
+# between gate A
+F:A:160:6:0:0:0:0:0:805
+
+# between gate B
+F:B:160:6:0:0:0:0:0:831
+
+# between gate D
+F:D:160:6:0:0:0:0:0:809
+
+# between gate E
+F:E:160:6:0:0:0:0:0:2826
+
+# between gate F
+F:F:160:6:0:0:0:0:0:2831
+
+# between gate H
+F:H:160:6:0:0:0:0:0:4631
+
+# between gate J
+F:J:160:6:0:0:0:0:0:7198
+
+# between gate K
+F:K:160:6:0:0:0:0:0:7990
+
+# between gate N
+F:N:160:6:0:0:0:0:0:9253
+
+# Treasure on floor
+F:$:1:0:0:*65
+
+# Trap (random) on floor
+F:^:1:0:0:0:0:0:*
+
+
+### Monsters
+
+# Eog Golem on floor
+F:g:1:0:530
+
+# Clay Golem on floor
+F:a:1:0:261
+
+# Aquatic Golem in water (OK, just for show)
+F:?:187:0:899
+
+# Stone Golem on floor
+F:b:1:0:323
+
+# Iron Golem on floor
+F:c:1:0:367
+
+# Colbran on floor
+F:d:1:0:435
+
+# Mithril golem on floor
+F:e:1:0:464
+
+# Colossus on floor
+F:f:1:0:558
+
+# Drolem on floor
+F:h:1:0:691
+
+# Demonic Q on shallow lava
+F:Q:86:0:727
+
+# Livingstone on floor
+F:i:1:0:336
+
+# Random monster (upto 8 levels ood) on normal floor
+F:&:1:0:*58
+
+
+### Items
+
+# Broken Stick
+F:s:1:0:0:727
+
+
+### Guaranteed Items
+
+# The Boots of the Machine
+F:*:205:0:0:0:0:218
+
+
+### Level layout
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X..e.e...I.......Xe..c.a..ca.c.....1X..3..XQQX,llllllllllllll2XX#Gi.X.X..XLABLX..X
+D:X.XXX...XXX.....XXXe.c..XXX__XX__XXXX.;.;.XllX,ll,ll,XXXXXXXXXXi.X#.XiXXXXL%%LXXXX
+D:X&XXXe.XX~XXeeeXX~XXe..XX~~~~~~ttttXX.....X,,,,Ll,ll,X..XII.X.X.i.XXG_XXXXL%%LXXXX
+D:X..&..XX~~~XXeXX~?~XX.&X~~~~t~ttttttX.;.;.X,lllll,ll,XX_..XX.XX#X..X#XXf.X_GG_X.fX
+D:XGXGXGX?~~~~_c_~~~~~X.&XX~~~~t~ttttXX.....X,L,,,,,lL,.X.#XXi..G.XXiX..XX.........X
+D:X.....XX~~?XXcXX~~~XXa..XXX__XX__XXXX.;.;.X,L,.XXX%L%XXXXcXX.XXX.X.X.X.XX.X.ff.X.X
+D:XXgXgXgXX~XXcccXX?XXXXX.a^^a.^^a...bXXXGXXX%L%XX.XX%XXa.aXIX.Xi#.XiXI.X.X........X
+D:XgXgXgXgXXX^^^^^XXX,ccXXXXXXXXXXXb.XX#...#XX%XX.XI_XX....X.X.#XXXIX.#GX.X.X....X.X
+D:X.......4X.^^5^^.X.c.c..c..c.c..c.XX.#...#.XXXIXX.X.a.c..XXi..I.X._.X.XXX...&&...X
+D:X..&&...XXXXXXXXXXc,c,c.XX_XX..ccXX..#...#..XX#iiX.X.a..a.XX.#XX._X.XX.XX.X....X.X
+D:X......XXEX,,,c,c,cc,ccXXMRMXXc.XX...#...#...XXX#iXXXXXc..IXGX.XXiX.....X........X
+D:XXXXXXXX..X,,,,,,,,,,,XXM..RMXXXX$$XXX...XXX$$XX..XX_GXXXXXX.Ii.X_X.X#XXX.X....X.X
+D:XT.T._.g.gXX,C,,,.,C,c_M.R.R.MXX$$$XXX...XXX$$$XX..XIXIXII.i.XX_.X.XX.XXXXGGGGGGXX
+D:X.T.T_.....XXlXXXXXlXFXXM..RMXX$$XXXXX...XXXXX$$XX.#.XX.X_X_XXI#.XXIIX.Xeee...eeeX
+D:X..T._.g..g.XlX_D_XlXXXXXM.MXX$$$#....%%%....#$$$X.X..XX_G.#.X.XXIXX..cXXXX...XXXX
+D:XMMMMX...&..XlX_._XlX6..XXXXXX$$$#....%*%....#$$$X._X#iX.XX..G_Xi.XXd..XLl#_._#lLX
+D:Xs.s._..&.&.XlX_._XlX..gg....X$$$#....%%%....#$$$X.IIX.X..XIXX.X.G#X..dXLl#_._#lLX
+D:X.s.s_g....XXlXXGXXlXX.g..d&.XX$$XXXXX...XXXXX$$XXXX._XG#X.XX.X_X.XX.c.XXXX...XXXX
+D:XXXXXXXXXXIXLLLXGXLLLXgg.g.d..XX$$$XXX...XXX$$$XX<...XiXX.X.Xi.X.X.Xd..X.........X
+D:Xgg.gXg.gXIXL%LXGXL%LXGXXX__XXXXX$$XXX...XXX$$XXXXXXIXXiX#XX#XX#X_XX...XXXXXXXIXXX
+D:X^^X^X^X^X^XX_XXGXX_XXGXXg..g.%.XX...X...X...XX.GI.X._iXXG...I..I.GX..dIX#I#I#I#IX
+D:X^^X^X^X^X^Xhl,&c.a&g.l.XX.g.g%RRXX..X###X..XX..#_X..XXX_iXXIXiXi_.Xc..X.IX.XXXIXX
+D:X^^X^X^X^X^X&,,,l&,lac.&.__.g.%.R.XX...>...XX#X.X..XX#.._X.X.X.#.XGX.X.XX_XX.X#i.X
+D:XH.Xg.gXg.gXla,ca&,f.fl.leXX..%R.RRXX.....XX.G.XX#XXI.XX.#.XGXXXiX.#X.XX.i._X.XGX#
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXGXXXXXXXG.X..X#XXX.XX.XXI._XIX.XIXXI#.XXX.X
+D:X,.,G,&&,ee,ff,hgg,&&,,,.,,I7XXXc..aX.;.;.X&..XXXG#XX.G.XXX..XG######.G.#..##..iXX
+D:X^X,G.&&.ee,ff..gg,&&,.,,,,XXX..MRM.X.....X.....XXX.IIX.XGI.XX..IXXXXXXXXXXX._.X._
+D:X^X,XXXXXXXXXXX.XXXXXX,,.XXX..$.....X.;.;.Xh..&...GXXXGXi#IXX.IXX..#.....#.XX##XXX
+D:X^X,llL,LlLL,LXXXlll,XXXXX..aMR..$.cX.....X.....XXXX8XXXXXXXX#IX###..##.#..#XI.XKX
+D:X^XlLCCCCCCCCLllll%%l.R.R.R.R..M.R..X.;.;.X&..XXX.#X#.i.#.XXX#X...###.#.##.#.X#IXX
+D:X^X,lL,llL,Ll,XXX,lllXXXXXc.M..R.c..X.....XXXXX#.#.XIX#Xi.X...X###.#####.#...X#XXX
+D:X^X,XXXXXXXXXXX.XXXXXX,.,XXX...$.M..X.;.;.Xh.##i#.#XX_IX.#X.##I....#.#.###.##X#I.X
+D:X^X.G,&&,,gg.&.,ee,ff,,.,.,XXXa..R..X.....X..#.###.XIXX.X.X.##XX####..#..###II.X.X
+D:X,,,G,&&,,gg.&h.ee,ff,..,.,I^XXXM..9X.;J;.Xh.##.###..iiX#.X....II.....#....IX..XNX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+
+### Starting location
+P:21:51
diff --git a/lib/edit/s_gates.map b/lib/edit/s_gates.map
new file mode 100644
index 00000000..3ac7ccbf
--- /dev/null
+++ b/lib/edit/s_gates.map
@@ -0,0 +1,117 @@
+# Special Level "The Dimensional Gates" in the Pits of Angband
+#
+# Created for PernAngband 5.0.1 on 18/8/01
+# Written by Mynstral (mynstral@thehelm.com)
+
+%:special.txt
+
+### Gauranteed monsters
+
+# Quylthulg on normal floor
+F:a:1:0:342
+
+# Demonic Quylthulg on normal floor
+F:b:1:0:727
+
+# Draconic Quylthulg on normal floor
+F:c:1:0:759
+
+# Rotting Quylthulg on normal floor
+F:d:1:0:633
+
+# Greater Draconic Quylthulg on normal floor
+F:e:1:0:801
+
+# Greater Rotting Quylthulg on normal floor
+F:f:1:0:802
+
+# Great Hell Wyrm on normal floor
+F:g:1:0:756
+
+# Master Quythulg on normal floor
+F:h:1:0:821
+
+### Random monsters and/or items
+
+# Random monster (upto 5 levels ood) on normal floor
+F:&:1:0:*90
+
+# Random monster (upto 11 levels ood) on normal floor
+F:@:1:0:*96
+
+# Random monster (upto 9 levels ood) and
+# Random object (upto 7 levels ood) on normal floor
+F:8:1:0:*94:*92
+
+# Random monster (upto 40 levels ood) and
+# Random object (upto 20 levels ood
+F:9:1:0:*125:*105
+
+### Guaranteed items
+
+# The Ring of Power 'Narya' on normal floor
+F:1:1:0:0:0:0:10
+
+
+### Level design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X*****...@%...aX.X9@********%%%%%%X9.VV....Xd.............A^^.......dX
+D:X*****...@%...aX.X@@********%%%%%%X.VVVV.X.XX.............B^^........X
+D:X*****...@%...aX.X99*XXX****%%88%%XVVWWVVX..XX.............^^.......9X
+D:X*****...@%...aX.X@@XXdXX***%%88%%XVVWWVVX...XX............^^.......CX
+D:X*****...@%...aX.X@XX...XX**%%%%%%X.VVVV.X.X..XX..........5^^........X
+D:X........@%...aX.XXX.....XX*%%%%%%X9.VV..X.XX..XX.........F^^.......dX
+D:X%X......@%...aX.X........XIXXXXXXXXXXX..X..XX..XXXXIXXXXXXXXXXXXXXXXX
+D:X.XXX....@%...aX.X..........XXX..>X...X..X...XX..XXXaXaXaXaXaXaXaXaXaX
+D:X...XXX..@%...aI.X..........XXX7..X...D..X.X..XX..XX.................X
+D:X.....XXX@%...aX.X..........XXXXXXXXXXX..X.XX..XX..XX................X
+D:X.......XXX...aX.X..........XXXXXXXaG....X..XX..XX..XX...............X
+D:X..X......XXX.XX%XX^^^^^X...XXXXXXXaG....X...XX..XX..XX..............X
+D:X^^XX.......XXX***X^^^^^X...XIIII7XXXXXXXXXX..XX..XX..XXXXXXXXXXXX+X8X
+D:X^^XX.........X.6.X^^^^^X...XIXXXXX&&&&&&&&XX..XX..XX...........cX.XXX
+D:X..XXX........X***X^^^^^X...XIXXXXX.........XX..XX..XXXXXXXXXXXXXX..XX
+D:X^^X8X........XXXXX^^^^^X...XIIIIIXVVV.......XX..XX.............bXX..X
+D:X..X8XX................XX...XXXXXIXVVVV.......XX..XXXXXXXXXXXXXXXXXX+X
+D:X^^X88X................XX...XIIIIIXVVVVVV..@...XX.^^^^^^^^^^^^^a.....X
+D:X^^X99XX...............XX...XIXXXXX4..VVVV......XXXXXXXXXXXXXXXXXXXXXX
+D:X..X999X...............XX...XIXXXXXXXXX.VVVV....***********.........bX
+D:X^^XIIIXX..............XX...XIX...5..fX...VVVVV.@....................X
+D:X..X....X........d.....XX...XIXf......X...VVVVVVV...............@....X
+D:X^^X....XX............XXX...XIXXXXXXXXX.......VVVVVVV................X
+D:X^^X&&&&&X............XXX...XIIIIIXe...........VVVVVVVVVVVV..........X
+D:X..X%%%.%XX..........XXX....XXXXXIX.............@VVVVVVVVVVVV@.......X
+D:X^^X88%.%8X.........XXX.....XXXXXIX...................VVVVVVVVVV.....X
+D:X..X%%%.%%XX........XX......XXXXXIX@.....................VVVVVVVVVVVVX
+D:X^^X......cX......XXXX......XXXXXIX........@..........@....VVVVVVVVVVX
+D:X^^^......cXX888XXXXX......dIIIIIIX.........................b.VVV...6X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXgLLLLLgXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLLLLXXLLLLLLXXXXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXgLLLLLXXXXXgLLLLLLLXXgXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXLLLLgXXXXXXXXXXXXXXLLLLLLLXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXLLXXXXXXXXXXXXXXXXXXgXXXLLLgXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXLgXXXXXXXXXXXXXXXXXXXXXXXXLLLLXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXfXXXXXXXXLXXXXXXXXXXXXXXXXXXXXXXXXXgLLLLXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXX.XXXXXXXghgXXXXbXXXXXXXXaXXXXXXXXXXXXXXLLLLgXXXXXXX
+D:XXXXXXXXXXXXXXXXXXX.XXXXXXXg1gXXXX.XXXXXXXX.XXXXXXXXXXXXXXXXgLLXXXXXXX
+D:XXXXXXXXXXXXXXXXXXX^XXXXXXXXXXXXXX^XXXXXXXX^XXXXXXXXXXXXXXXXXXLXXXXXXX
+D:XXXXXXXXXXXXXXX4..^^^..^^^.......^^^......^^^.........EXXXXXXXLXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXX^XXXXXXXXX^XXXXXXXX^XXXXXXXXXXXXXXXXXLLXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXX.XXXXXXXX.XXXXXXXXXXXXXXXXLLLXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXcXXXXXXXXaXXXXXXXXXXXXXXXLLXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXeXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXgLLLXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLLLXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLLXgXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLLXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLLXXXXXXXXXXXXXXX
+D:X...dXXXXXXXXXXe..0XXXXXXXXXXXXXXXD..EXXXXXXXXXXXXXXLLXXXXXXXXXXXcc..X
+D:X...dXXXXXXXXXX....XXXXXXXXXXXXXXX....XXXXXXXXXXXXXX.XXXXXXXXXXXX....X
+D:XA...XXXXXXXXXXF..eXXXXXXXXXXXXXXXC...XXXXXXXXXXXXXX0XXXXXXXXXXXX...BX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+
+# Starting location
+P:11:37
diff --git a/lib/edit/s_info.txt b/lib/edit/s_info.txt
new file mode 100644
index 00000000..5e41fe97
--- /dev/null
+++ b/lib/edit/s_info.txt
@@ -0,0 +1,546 @@
+# File: s_info.txt
+
+
+# This file is used to initialize the "lib/data/s_info.raw" file, which is
+# used to initialize the "skills" information for the PernAngband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/data/s_info.raw" file.
+
+# The skill indexes are defined in "defines.h", and must not be changed.
+
+# N:idx:name
+# D:desc
+# A:action mkey:action desc
+# I:rate
+
+# E:exclusive skill:exclusive skill
+# O:skill:opposing skill%percent
+# A:skill:friendly skill%percent
+
+# T:father:child
+
+# Version stamp (required)
+
+V:2.0.0
+
+################################## MAGIC ##################################
+
+N:56:Magic-Device
+D:Eases the use of magical devices, such as wands, staves and rods
+D:It also helps pseudo-id of magic objects
+I:1000
+F:RANDOM_GAIN
+
+N:54:Spell-learning
+D:You should not see that ! that is a BUG!
+#A:18:Learn a spell from a realm
+I:1000
+F:HIDDEN
+
+N:41:Sorcery
+D:Ability to use all the magic schools as if their skill was sorcery
+D:But the price to channel that much magic is your health
+A:17:Cast a spell
+I:1000
+
+N:1:Conveyance
+D:Ability to learn and use spells from the Conveyance school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+F:RANDOM_GAIN
+
+N:2:Mana
+D:Ability to learn and use spells from the Mana school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:3:Fire
+D:Ability to learn and use spells from the Fire school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:4:Air
+D:Ability to learn and use spells from the Air school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:5:Water
+D:Ability to learn and use spells from the Water school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:6:Nature
+D:Ability to learn and use spells from the Nature school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:7:Earth
+D:Ability to learn and use spells from the Earth school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:10:Divination
+D:Ability to learn and use spells from the Divination school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+F:RANDOM_GAIN
+
+N:11:Temporal
+D:Ability to learn and use spells from the Temporal school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:14:Meta
+D:Ability to learn and use spells from the Meta school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:51:Mind
+D:Ability to learn and use spells from the Mind school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:55:Udun
+D:Ability to learn and use spells from the Udun school
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+F:HIDDEN
+
+N:13:Demonology
+D:Ability to use incantations from the Demonblades
+D:Spells use the intelligence stat
+A:17:Cast a spell
+I:1000
+
+N:31:Necromancy
+D:Ability to harness the powers of the dead
+D:Spells use the intelligence stat
+A:7:Use Necromancy
+I:1000
+F:RANDOM_GAIN
+G:60
+
+N:34:Runecraft
+D:Ability to combine magic runes to create your own spells
+D:Runespells use the dexterity stat
+A:9:Use Runespells
+I:1000
+
+N:43:Thaumaturgy
+D:Ability to gain and cast innate spells
+D:Spells use the intelligence stat
+A:8:Cast a thaumaturgy spell
+I:1000
+F:RANDOM_GAIN
+
+N:15:Magic
+D:General ability to do magic, also affects mana reserves and
+D:magic device ability. Helps pseudo-id of magic objects
+A:19:Copy a spell
+I:1000
+F:RANDOM_GAIN
+
+N:45:Spell-power
+D:Ability to increase the power of spells
+I:1000
+
+N:59:Geomancy
+D:Ability to understand the raw elemental forces of nature and use
+D:them to your advantage. Most spells need Fire/Water/Earth/Air skills
+A:101:Use Geomancy
+I:1000
+
+# All magic skills affect magic skill
+f:Magic-Device:Magic%7
+f:Spell-power:Magic%20
+f:Sorcery:Magic%20
+f:Mana:Magic%10
+f:Fire:Magic%10
+f:Air:Magic%10
+f:Water:Magic%10
+f:Earth:Magic%10
+f:Geomancy:Fire%45
+f:Geomancy:Earth%45
+f:Geomancy:Air%45
+f:Geomancy:Water%45
+f:Conveyance:Magic%10
+f:Divination:Magic%10
+f:Nature:Magic%10
+f:Temporal:Magic%10
+f:Meta:Magic%10
+f:Mind:Magic%10
+f:Udun:Magic%10
+f:Demonology:Magic%10
+f:Necromancy:Magic%4
+f:Runecraft:Magic%12
+f:Thaumaturgy:Magic%6
+
+
+
+################################## COMBAT ##################################
+
+N:16:Combat
+D:General ability to fight and to pseudo-id armour and weapons
+D:It also allows the use of heavier armour without penalties
+I:1000
+F:RANDOM_GAIN
+
+N:17:Weaponmastery
+D:General ability to use melee weapons
+I:1000
+F:RANDOM_GAIN
+
+N:18:Sword-mastery
+D:Ability to use swords
+I:1000
+
+N:19:Axe-mastery
+D:Ability to use axes
+I:1000
+
+N:20:Polearm-mastery
+D:Ability to use polearms
+I:1000
+
+N:21:Hafted-mastery
+D:Ability to use hafted weapons
+I:1000
+
+N:22:Backstab
+D:Ability to backstab fleeing and sleeping monsters to increase damage
+I:1000
+
+N:23:Archery
+D:General ability to use ranged weapons
+I:1000
+F:RANDOM_GAIN
+
+N:24:Sling-mastery
+D:Ability to use slings
+A:23:Fire piercing shots
+I:1000
+
+N:25:Bow-mastery
+D:Ability to use bows
+A:23:Fire piercing shots
+I:1000
+
+N:26:Crossbow-mastery
+D:Ability to use crossbows
+A:23:Fire piercing shots
+I:1000
+
+N:27:Boomerang-mastery
+D:Ability to use boomerangs
+I:1000
+
+N:58:Boulder-throwing
+D:Ability to make and throw boulders
+A:21:Tear down a wall to create boulders
+I:1000
+
+N:42:Barehand-combat
+D:Ability to fight barehanded
+I:1000
+F:RANDOM_GAIN
+G:70
+
+N:47:Bearform-combat
+D:Ability to fight in bear form
+I:1000
+F:HIDDEN | AUTO_HIDE
+
+N:52:Critical-hits
+D:Ability to deal critical hits with swords < 5lb
+I:1000
+
+N:57:Stunning-blows
+D:Ability to stun opponents when doing critical hits with hafted weapons > 5 lb
+I:1000
+
+# List of combat friendly skills
+
+# Melee: Specific masteries improve generic mastery
+f:Critical-hits:Sword-mastery%5
+f:Sword-mastery:Weaponmastery%25
+f:Axe-mastery:Weaponmastery%25
+f:Polearm-mastery:Weaponmastery%25
+f:Stunning-blows:Hafted-mastery%5
+f:Hafted-mastery:Weaponmastery%25
+
+# Ranged: Specific masteries improve generic mastery
+f:Sling-mastery:Archery%25
+f:Bow-mastery:Archery%25
+f:Crossbow-mastery:Archery%25
+f:Boomerang-mastery:Archery%25
+
+# All combat skills improve Combat
+f:Weaponmastery:Combat%50
+f:Sword-mastery:Combat%7
+f:Axe-mastery:Combat%7
+f:Polearm-mastery:Combat%7
+f:Hafted-mastery:Combat%7
+f:Archery:Combat%50
+f:Sling-mastery:Combat%7
+f:Bow-mastery:Combat%7
+f:Crossbow-mastery:Combat%7
+f:Boomerang-mastery:Combat%7
+f:Barehand-combat:Combat%50
+f:Boulder-throwing:Combat%40
+
+# No more, let's see how it turns out
+# Sorcery and Weaponmastery aren't exactly friendly to each other
+#O:Sorcery:Weaponmastery%100
+#O:Sorcery:Archery%100
+#O:Sorcery:Barehand-combat%100
+#O:Weaponmastery:Sorcery%100
+#O:Archery:Sorcery%100
+#O:Barehand-combat:Sorcery%100
+
+
+
+############################### SPIRITUALITY SKILLS ###########################
+
+N:28:Spirituality
+D:General ability to use spiritual skills and also influence your Saving Throw
+I:1000
+F:RANDOM_GAIN
+
+N:53:Prayer
+D:Ability to learn and use spells from the gods' schools
+D:Spells use the wisdom stat and cost piety instead of mana
+A:17:Cast a spell
+I:1000
+
+N:12:Druidistic
+D:Ability to learn and use prayers from the Druidistic realm
+D:Nature powers use the wisdom stat
+A:1:Cast a druidistic spell
+I:1000
+
+N:29:Mindcraft
+D:Ability to focus the powers of the mind
+D:Mindpowers use the wisdom stat
+A:2:Use Mindcraft
+I:1000
+F:RANDOM_GAIN
+G:50
+
+N:9:Music
+D:Ability to learn and sing songs
+D:Songs use the charisma stat
+A:17:Cast a spell
+I:1000
+
+f:Prayer:Spirituality%10
+f:Druidistic:Spirituality%10
+f:Mindcraft:Spirituality%10
+f:Music:Spirituality%10
+
+f:Prayer:Magic%10
+f:Druidistic:Magic%10
+f:Mindcraft:Magic%10
+f:Music:Magic%10
+
+
+################################## MISC SKILLS ###############################
+
+N:30:Misc
+D:Not a real skill, it is only used to regroup some skills
+I:0
+
+N:33:Antimagic
+D:Ability to generate an antimagic field
+A:3:Use antimagic
+I:1000
+F:RANDOM_GAIN
+G:80
+
+N:39:Alchemy
+D:Ability to use essences to modify/create magic items
+A:5:Use Alchemy
+I:1000
+
+# Antimagic exclude all magic
+E:Magic-Device:Antimagic
+E:Mana:Antimagic
+E:Geomancy:Antimagic
+E:Fire:Antimagic
+E:Air:Antimagic
+E:Water:Antimagic
+E:Earth:Antimagic
+E:Conveyance:Antimagic
+E:Divination:Antimagic
+E:Temporal:Antimagic
+E:Meta:Antimagic
+E:Mind:Antimagic
+E:Nature:Antimagic
+E:Udun:Antimagic
+E:Sorcery:Antimagic
+E:Demonology:Antimagic
+E:Runecraft:Antimagic
+E:Necromancy:Antimagic
+E:Mindcraft:Antimagic
+E:Music:Antimagic
+E:Prayer:Antimagic
+E:Druidistic:Antimagic
+E:Thaumaturgy:Antimagic
+
+################################## SNEAKINESS SKILLS ###############################
+
+N:35:Sneakiness
+D:General ability at the sneakiness skills.
+D:It also affects the searching abilities
+I:0
+F:RANDOM_GAIN
+
+N:36:Stealth
+D:Ability to move unnoticed, silently
+I:0
+F:RANDOM_GAIN
+
+N:37:Disarming
+D:Ability to disarm the various traps
+I:0
+F:RANDOM_GAIN
+
+N:40:Stealing
+D:Ability to steal objects
+A:15:Steal object
+I:0
+
+N:46:Dodging
+D:Ability to dodge blows and bolts
+A:16:Check dodge chance
+I:0
+
+f:Stealth:Sneakiness%15
+f:Disarming:Sneakiness%10
+f:Backstab:Sneakiness%5
+f:Stealing:Sneakiness%15
+f:Dodging:Sneakiness%10
+
+
+################################## MONSTER SKILLS ################################
+
+N:48:Monster-lore
+D:General ability at the monster related skills, ability to gain experience
+D:from friendly kills. It also affects the number of companions you can have
+I:0
+A:22:Turn pet into companion
+F:RANDOM_GAIN
+
+N:44:Summoning
+D:Ability to create totems from monsters and use them to summon monsters
+A:13:Manipulate totems
+I:1000
+F:RANDOM_GAIN
+G:60
+
+N:49:Corpse-preservation
+D:Ability not to destroy the monsters' corpses when killing them
+I:0
+
+N:50:Possession
+D:Ability to incarnate into monsters
+A:11:Use the possession skill
+I:0
+
+N:8:Symbiosis
+D:Ability to enter in symbiosis with monsters unable to move by themselves
+D:Spells use the intelligence stat
+A:20:Use symbiotic powers
+I:1000
+F:RANDOM_GAIN
+G:70
+
+N:32:Mimicry
+D:Ability to use cloaks of mimicry to change form
+A:6:Use Mimicry
+I:1000
+F:RANDOM_GAIN
+G:80
+
+f:Possession:Monster-lore%10
+f:Corpse-preservation:Monster-lore%10
+f:Summoning:Monster-lore%10
+f:Symbiosis:Monster-lore%10
+f:Mimicry:Monster-lore%10
+
+################################## SKILL TREE ################################
+
+T:Main:Combat
+T:Combat:Weaponmastery
+T:Weaponmastery:Sword-mastery
+T:Sword-mastery:Critical-hits
+T:Weaponmastery:Axe-mastery
+T:Weaponmastery:Hafted-mastery
+T:Hafted-mastery:Stunning-blows
+T:Weaponmastery:Polearm-mastery
+T:Combat:Archery
+T:Archery:Sling-mastery
+T:Archery:Bow-mastery
+T:Archery:Crossbow-mastery
+T:Archery:Boomerang-mastery
+T:Combat:Barehand-combat
+T:Combat:Bearform-combat
+T:Combat:Boulder-throwing
+T:Combat:Antimagic
+
+T:Main:Sneakiness
+T:Sneakiness:Stealth
+T:Sneakiness:Disarming
+T:Sneakiness:Backstab
+T:Sneakiness:Stealing
+T:Sneakiness:Dodging
+
+T:Main:Magic
+T:Magic:Magic-Device
+T:Magic:Spell-power
+T:Magic:Sorcery
+T:Magic:Mana
+T:Magic:Geomancy
+T:Magic:Meta
+T:Magic:Conveyance
+T:Magic:Divination
+T:Magic:Temporal
+T:Magic:Mind
+T:Magic:Nature
+T:Magic:Udun
+T:Magic:Demonology
+T:Magic:Necromancy
+T:Magic:Runecraft
+T:Magic:Thaumaturgy
+T:Magic:Alchemy
+
+T:Geomancy:Fire
+T:Geomancy:Water
+T:Geomancy:Air
+T:Geomancy:Earth
+
+T:Main:Spirituality
+T:Spirituality:Prayer
+T:Spirituality:Mindcraft
+T:Spirituality:Music
+
+T:Main:Monster-lore
+T:Monster-lore:Summoning
+T:Monster-lore:Corpse-preservation
+T:Monster-lore:Possession
+T:Monster-lore:Symbiosis
+T:Monster-lore:Mimicry
diff --git a/lib/edit/s_name.map b/lib/edit/s_name.map
new file mode 100644
index 00000000..795d8786
--- /dev/null
+++ b/lib/edit/s_name.map
@@ -0,0 +1,110 @@
+# Special level "The Nameless Level" in the Pits of Angband
+#
+# Created for PernAngband 5.0.1 on 18/8/01
+# Written by Mynstral (mynstral@thehelm.com)
+
+%:special.txt
+
+# Great Storm Wyrm on normal floor
+F:a:1:0:728
+
+# Mature Red Dragon on normal floor
+F:b:1:0:589
+
+# Mature White Dragon on normal floor
+F:c:1:0:549
+
+# Ancient Blue Dragon on normal floor
+F:d:1:0:601
+
+# Ancient Black Dragon on normal floor
+F:e:1:0:624
+
+# Ancient Multi-hued Dragon on normal floor
+F:f:1:0:675
+
+# Great Hell Wyrm on normal floor
+F:g:1:0:756
+
+# Great Wyrm of Many Colours on normal floor
+F:h:1:0:790
+
+# Great Ice Wyrm on normal floor
+F:i:1:0:741
+
+# Sky Drake on normal floor
+F:j:1:0:793
+
+### Random Monsters and/or Items
+# Random monster (upto 5 levels ood) on normal floor
+F:&:1:0:*100
+
+# Random monster (upto 11 levels ood) on normal floor
+F:@:1:0:*106
+
+# Random monster (upto 9 levels ood) and
+# Random object (upto 7 levels ood) on normal floor
+F:8:1:0:*104:*102
+
+# Random monster (upto 40 levels ood) and
+# Random object (upto 20 levels ood
+F:9:1:0:*135:*115
+
+# Random monster (upto 3 levels ood)
+F:-:1:0:*98
+
+# Random object (upto 7 levels ood)
+F:=:1:0:0:*102
+
+### Guaranteed Items
+# The Multi-Hued Dragon Scale Mail 'Razorback' on normal floor
+F:1:1:0:0:0:0:16
+
+### Level Design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X.....LLLLLLLLL.....XXX.G=-&&=-G^.........>X
+D:X.LLL...........LLLXX...G=-&.=-G^........1.X
+D:X.L9L.LLLLLLLLLLLLX%...EGE.=&.FG^..........X
+D:X.L99.L&..........X%....G..&-..G^....j.....X
+D:X.LLLLL&LLLLLLLLL.LXX...G=-=&-=G^..........X
+D:X....&&&L.......L.L*XXX.G-=&&-=G^^^^^^^^^^^X
+D:XLLLLLLLL.LLLLL..&L**XXXXXXXGGGG%XXXXXXXXXXX
+D:XXXXXXLL..L***LLLLLLLX^^^^8Xi....%@@^^.^^@@X
+D:X5..8X.&.XXXXXX......D^^^^8XIIIIIXbb^^.^^ccX
+D:X...8X...XA...X.LLLLLX^^^^8X.....X..^^.^^..X
+D:X...8X.XXXC...X.LL...XXXIXXX^^^^^X..^^.^^..X
+D:XGXXXX.LfX7...X&LL.L.Xa^^^aXf...fX..^^.^^..X
+D:X.X5XL.L*XB...X&&..L.X^b^b^X^^^^^X..^^.^^..X
+D:X.X^XL.L*XXXXXXLLLLL.X^^7^^X.....X..^^.^^..X
+D:X.X^XL.L***fXL.....&&X^b^b^XIIIIIX..^^.^^..X
+D:X.X^XL.LLLLLXL.LLLLLLXa^^^aXi....X..^^.^^..X
+D:X.X^XL.........XXXXXXXXX%XXXXXXXXX..^^.^^..X
+D:X.X^XXXXXXXXXXLX^...^Xe-^=dX^^..&X..^^.^^..X
+D:X.X^^^^^^^^^^XLX.^^^.X-^^^=X^^..&X..^^.^^..X
+D:X.XXXXXXXXXX^X8Xf^B^.%^^6^^%^^C.&X..^^.^^..X
+D:X.LLLLLLLLL+^XXX.^^^.X-^^^=X^^..&X..^^.^^..X
+D:X.XXXXXXXXXXXXXX^...^Xd-^=eX^^..&X..^^.^^..X
+D:X.Xbbbbb......XXXXXXXXXX%XXXXXXXXX..^^.^^..X
+D:X.XbbbbXXXX^^^^XX....X^^^^^X...h.X..^^.^^..X
+D:X.XbbbXX^^XXX^^^XX...X^ccc^X....0X..^^.^^..X
+D:X.XbbXX.^X%bXX^^^XX..X^.A.^X.....X..^^0^^..X
+D:X.IbXX..XXbbbXX^^^XX.X^ccc^XIXXXXXXXXXXXXXXX
+D:X.XXX..XX.....X%^^^XXX^^^^^XlllllllllllllllX
+D:X.X99.XXW......XX^^^XXXXXXXXlllllllllllllllX
+D:X.X99XXWWW...a..XX^^.XXllllllLLLLLlXXXX%XXXX
+D:X.XXXX..WWW.....cXX^.XXXXXlllllllllXVVVVVVVX
+D:X.X8.....WWW....ccX^.X^^^XLLLlLLLLLXVVVVVVVX
+D:X.X%XXXXXXXWW...cXXX.X+X^XlllllllllXV%%%%%VX
+D:X.X.......XWWW..X%^X.XLX^XlLLLLLLLLXV%8^8%VX
+D:X.XXXXXX%.X.WWWXX^^X.XLX^XlllllllllXV%^a^%VX
+D:X.X.IIIIX.X..WXX..XXcXLX^XLLLlLLLLlXV%^^^%VX
+D:X.X%XXXIX.X..XX..XXccXLX^XlglllLgllXV%^^^%VX
+D:X.Xa.6XIX.X.XX..XXcccXLX^XlllllXXXXXV%^a^%VX
+D:X+XXX.XIX.X.X99XXccccXLX^XXXXXXX888XV%8^8%VX
+D:X%%%Xa%.X.%8X99XcccccXLX^^^^^^4X...XV%%%%%VX
+D:X%.%XXXXXXXXXXXXIXXXXXLXXXXXXXXX...XVVVVVVVX
+D:X.%%+..........................G..4X...F...X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting Location
+P:43:4
diff --git a/lib/edit/s_orc.map b/lib/edit/s_orc.map
new file mode 100644
index 00000000..48913e02
--- /dev/null
+++ b/lib/edit/s_orc.map
@@ -0,0 +1,109 @@
+# Special level "The Orc Barracks" in the Pits of Angband
+#
+# Created for PernAngband 5.0.1 on 18/8/01
+
+%:special.txt
+
+#Y:40:118:126:149:264:238:285:313:330:404:15:0:0:35:22
+# Shrieker
+F:a:1:0:40
+
+# snaga
+F:b:1:0:118
+
+# cave orc
+F:c:1:0:126
+
+# hill orc
+F:d:1:0:149
+
+# half orc
+F:e:1:0:264
+
+# ogre
+F:f:1:0:238
+
+# ogrillion
+F:g:1:0:285
+
+# bolg
+F:h:1:0:330
+
+# uruk
+F:i:1:0:313
+
+# black orc
+F:j:1:0:244
+
+### Random Monsters and/or Items
+# Random monster (upto 5 levels ood) on normal floor
+F:&:1:0:*35
+
+# Random monster (upto 11 levels ood) on normal floor
+F:@:1:0:*46
+
+# Random monster (upto 9 levels ood) and
+# Random object (upto 7 levels ood) on normal floor
+F:8:1:0:*44:*42
+
+# Random monster (upto 40 levels ood) and
+# Random object (upto 20 levels ood
+F:9:1:0:*75:*55
+
+# Random monster (upto 3 levels ood)
+F:-:1:0:*38
+
+# Random object (upto 7 levels ood)
+F:=:1:0:0:*42
+
+### Guaranteed Items
+
+# The stone of lore
+F:1:1:0:0:0:0:15
+
+### Level Design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:Xa.......................ccccccccc.......................aX
+D:X.lllllllllllllllllllllllllllllllllllllllllllllllllllllll.X
+D:X.l......................ccccccccc.....g.g..............l.X
+D:X.l.XXXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXDXXXXXXXXXXXXXX.l.X
+D:X.l.Xa.......efX1Xfe.......aX.X.aaa.D.gX.XacbdddccjjjaX.l.X
+D:X.l.D........efG.Gfe........D.D.bbb.XXXX.XcfbcbcbbjjeeX.l.X
+D:X.l.X........efX.Xfe........X.X.bbb.D.gX.XfbbgdcbdbjjjX.l.X
+D:X.l.XXXXXX...efG.Gfe...XXXXXX.X.bbb.XXXX.XebdcfbdccejjX.l.X
+D:X.l.X.c..XXX.efX.Xfe.XXXcc.aX.D.ccc.D.gX.XcfcbgeebdebfX.l.X
+D:X.l.D..d..aXXXfX.XfXXXe.....D.X.ccc.XXXX.XedbcdbcdcggeX.l.X
+D:X.l.X........XXX.XXX..bbb.ddX.X.ddd.D.gX.XdgfcfegbeebcX.l.X
+D:X.l.X..f.c.bf.G...G.c..cc...X.D.ddd.XXXX.XcbeefccbfbdbX.l.X
+D:X.l.X.d..bb.d.Gi.iG...cbb.bdX.X.eee.D.gX.XbgcfbgcfdebcX.l.X
+D:X.l.X.cc...c..G.7.G.cc..d.e.X.X.eee.XXXX.XcbdfeccdbcdbX.l.X
+D:X.l.X..b.f.bbXGGGGGX.bbb.bb.X.D.f.f.D.gX.XfdebbdgeefgdX.l.X
+D:X.l.D.c..dbbXXa...aXXX..d...D.X.f.f.XXXX.DdefddbcbdbddX.l.X
+D:X.l.X...c.XXX..g.g...XXX...bX.X.....D..XGXXeegcefdcefcX.l.X
+D:X.l.Xbb..aXg.gggjgggg.gX.bbaXDXaaa..XXjIa8XacbddbegbdaX.l.X
+D:X.l.XXXXXXXXDXXXXXXXXDXXXXXXXaXXXXXXXXXXXXXXXXXXXXXXXXX.l.X
+D:X.l........................Da.aD........................l.X
+D:X.l.XXXXXXXXXXXXXXXXXXXXXXXXXaXXXXXXXXXXXXXXXXXXXXXXXXX.l.X
+D:X.l.....eeeeee.........fffXXXDXXXfff.........eeeeee.....l.X
+D:X.l.....eeeeee.........fffX.....Xfff.........eeeeee.....l.X
+D:X.llllllllllllllllllllllllX.....Xllllllllllllllllllllllll.X
+D:Xa.....................XXXXGGGGGXXXX.....................aX
+D:XXXXXXXXXXIXXXXXXXXXXXXXgafff>fffagXXXXXXXXXXXXXIXXXXXXXXXX
+D:XjjjX.#########.XfffXXgggfffffffffgggXXfffX.#########.XjjjX
+D:XfffX...........XeeeXggggjjj...jjjggggXgggX...........XfffX
+D:XfffXGGGDX.XXXXXX...Xgggg....h....ggggX...XXXXXX.XDGGGXfffX
+D:Xgg......X.X........Xgggg.........ggggX........X.X......eeX
+D:Xgg....ggX.X.XXXXXX.XXgggggggggggggggXX.XXXXXX.X.Xee....eeX
+D:Xgg....ggX.X.X......XXXXgggggggggggXXXX......X.X.Xee....eeX
+D:Xgg....ggX.X.X.XXXXXX..XXXXfffffXXXX..XXXXXX.X.X.Xee....eeX
+D:Xgg....ggX.X.X.........#.4XXXDXXX4.#.........X.X.Xee....eeX
+D:Xgg....ggX.X.XXXXXXXXXIXXXXa...aXXXXIXXXXXXXXX.X.Xee....eeX
+D:Xgg....ggX.X.Xigfedcba.XiX..X#X..XiX.abcdefgiX.X.Xee....eeX
+D:Xgg....ggX.X.XGGGGGGGGGX....X.X....IGGGGGGGGGX.X.Xee....eeX
+D:XggggggggX.X.X@@@@@@@@@IXXXaX7XaXXXX@@@@@@@@@X.X.XeeeeeeeeX
+D:XggggggggI.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X.IeeeeeeeeX
+D:XaaaaagggX.......................................XeeeaaaaaX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting Location
+P:22:31
diff --git a/lib/edit/s_ship.map b/lib/edit/s_ship.map
new file mode 100755
index 00000000..f429d4ec
--- /dev/null
+++ b/lib/edit/s_ship.map
@@ -0,0 +1,239 @@
+# s_ship.map
+#
+# Special Level "Arvedui's Galleon" in the Helcaraxe#
+#
+# Created for ToME 3.x on 9/11/03
+#
+# Written by Lord Dimwit (lorddimwit@hotmail.com)
+# Middle-game dungeon
+# Comments: This large level is a variation on the classic 'ghost ship' theme;
+# the ship was caught in the Helcaraxe and everyone on board died. The polar waters
+# surrounding the ship contain some unpleasant surprises for inquisitive and unwary
+# adventurers--the hardest monsters on the map are actually there.
+
+# Ported to ToME 2.x on 6/6/04 by masmarangio
+#
+# Changed the starting position to 28:122
+# Replaced the undefined letter 'c' with '.'
+# The exits of the between gates pointed to incorrect positions.
+# I'm not sure esp. about the correct exit of gate #3.
+
+
+#%:special.txt
+
+
+### Terrain Features
+
+# up staircase
+F:<:6:0
+
+# down staircase
+F:>:7:0
+
+# Permanent wall
+F:X:61:4
+
+# Glacial Wall
+F:#:215:4
+
+# Ice Wall
+F:M:95:4
+
+# Deep Water
+F: :187:4
+
+# Fog
+F:*:210:4
+
+# Shallow water
+F:~:84:4
+
+# Ice
+F:.:90:4
+
+# Hidden Door
+F:+:48:0
+
+# Normal Door
+F:=:32:0
+
+# Ash
+F:,:93:4
+
+# Fake wall
+F:I:189:4
+
+
+### Monsters
+
+# Leviathan in deep water
+F:L:187:0:782
+
+# Greater Kraken in deep water
+F:K:187:0:775
+
+# Lesser Kraken in deep water
+F:k:187:0:740
+
+# Giant Squid in deep water
+F:s:187:0:482
+
+# Killer Whale in deep water
+F:w:187:0:917
+
+# Drowned Soul in shallow water
+F:G:84:0:895
+
+# Ancient White Dragon on ice
+F:D:90:0:617
+
+# Mature White Dragon on ice
+F:d:90:0:549
+
+# Ice Troll on ice
+F:T:90:0:454
+
+# Headless Ghost on ice
+F:H:90:0:533
+
+# Shadow on ice
+F:g:90:0:665
+
+# Young White Dragon on ice
+F:b:90:0:460
+
+# Zombified Human on ice
+F:z:90:0:229
+
+# Greater mummy on ice
+F:m:90:0:522
+
+# Cold Hound on ice
+F:Z:90:0:308
+
+# Giant White Dragon Fly on ice
+F:F:90:0:250
+
+# Ice Elemental on ice
+F:E:90:0:570
+
+# Yeti on ice
+F:Y:90:0:154
+
+# Ice skeleton on ice
+F:i:90:0:379
+
+# Skeleton human on ice
+F:h:90:0:228
+
+# Ghost on ice (apologies to Eldridge Cleever)
+F:W:90:0:477
+
+# Hand druj on ice
+F:S:90:0:748
+
+# Eye druj on ice
+F:J:90:0:749
+
+# Dread on ice
+F:o:90:0:534
+
+# Dreadmaster on ice
+F:O:90:0:690
+
+# Night mare on ice
+F:q:90:0:622
+
+# Random monster (upto 5 levels ood) on ice
+F:&:90:0:*40
+
+# Random monster (upto 9 levels ood) and
+# Random object (upto 7 levels ood) on ice
+F:8:90:0:*44:*42
+
+# Treasure (random) on ice
+F:$:90:0:0:*47
+
+# Treasure (good) on ice
+F:%:90:0:0:*60
+
+# Trap (random) on ice
+F:^:90:0:0:0:0:0:*
+
+# Trap (random) on shallow water
+F:t:84:0:0:0:0:0:*
+
+# Trap (random) on fog
+F:@:210:0:0:0:0:0:*
+
+# Human skeleton on ice
+F:x:90:0:0:395
+
+
+### Between Gates
+
+# between gate 3: was 711
+F:3:160:6:0:0:0:0:0:1136
+
+# between gate A: was 6247
+F:A:160:6:0:0:0:0:0:6761
+
+# between gate 4: was 3339
+F:4:160:6:0:0:0:0:0:3853
+
+# between gate B: was 3085
+F:B:160:6:0:0:0:0:0:3599
+
+
+### Guaranteed Items
+
+# The Mage Staff of Forochel
+F:!:90:0:0:0:0:213
+
+
+### Level Design
+
+D:XXXXXXXXXXXXXXXX XXXXXXXXXXXX XXXXXXX XXXXXXXXXXXXXXX XXXX
+D:X###############..~ .#. k w ~.#XX######### .~~ s G..#####.. w .###.######X##~~ .GX.###
+D:X####%####...~ ~.~ s .#.X#~~.##### .~ s w G..#.##.#.XGG~XXk~.####A####.~X ~~.X...
+D:X##%#$#######...~ w wXXXXXX#XXXX###XXX #~XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..XX....~.XX X#xX.#..#.#G. w ~X G~~
+D:X##$#########.... s XXXTT^...~.~#####w~###~~~~8....**.tE.&E ^^.M^.xx.....x..ZZ..^XTTTT M...GG.~.. w G.###~XX~~#XX . s X
+D:X#########...~ w XXXTTTT.^..x~.H#### ###~~D....8**.~M~.E..M^.MM^..xh.h...ZZZZ^~M.,.,.,.~~ .G...~~ ~###. w . w M.d.
+D:X#####.x.x~~~ XXXTTTXXXMxx.H.####### ~##~~~8...#.**.~tE&EE^^~%M^.h.x.E.xh.ZZ..^X.,.^....~ ...G.~ s ~# XX ~~ w X
+D:X######...~~ MM~ w XXX^^XXX.MMMxx...G#####w ~#~.~..&...**E.MEt.^^.MM^~..i.....ZZZZ^~M.,.,,.gM.~.,~.,G.G~~ G~. G~.MM XX..~
+D:X##.....~~ XXX^E=^XX$b.MMMMxH..HG#.## w ~#~##~8....*E~.&EM^^.M^h..i..x.x..ZZ..^XTTTTX.,.,~........~.G~~..w . G~...G~X
+D:X...~~~ K XXX^^EXXXX$$$..MMXXXXXXX#### #####XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..,.X..M~.........~G..G ~~.G...G~
+D: ~~~ w XXX^^EXXXdX%d$$b$$XXd~~~~##### s ###.,.W.**,.x.,.,.,.,.,.***,,....,x....,,.SX.,.,.,..,o.,.,.~~.G..~.G..G..~X k
+D: w MMM. w XXX^E^XXX.,*XDDd$$$XXd$..~~~~#### ###.,.,.,.**..,.,i.,.,W,.,**..z.,.,.q...g.,......~.,.,.,.,.~.~..F.~M~......~
+D: MM. XXX4EEXXXi^**.XX%%$dXX$d$.H#####~G~~####.~.,~..,.h*,,~,.,.,.,~,.,.,.H.,.~.,..~,.,,,.~,.,.~.,.,.X.~,g.~~..~.~..,..,X
+D: s XXXBXXXXX^.**.W.,XDD%XXb$$b####..G~~~G###~.,,.,.,.,...,,.x,.~.,.,~..,.~.,.,~,.,.,,.,.,~,.,.,.~,.,..,X.,.***.~***,W.,X
+D: XXX.o..X.g***i.*.,.XX%XX######^^^^t ##ttt^^^^^t^^^^^^t^^^^^t^^^^^^^t^^^^^^t^^^^t^^tt^^^tt^^^^t^tXo..x************X
+D: k XXX^E..o.XX8.****,.g..XXX..#........~~~~~ttt###~~.,.G~.......XXXXX..~,..,,..,,.~.,..,.,..,,..,.,x..,.ooX******W***g.*.g*X
+D: XXX%S^^E..ooX,*****,..,.X.....h........XXX~~~tt#tt w ~~G~...XXXXXXX.,**.,..D.**.G.,~,..XXX...~~~.....o8X***.~.,.****$$..X
+D: XX%%%^mm.E..oXH**.^.*i.....#.....i..*..XXXXX~~ G~~.XXXXXXXXX~.*..,.,*..~,..,.,XXXXX...~..,.XXXXXo.W.$$.g.x....o.X
+D: XXX%S^^E..ooX8.*~~...,~~##...,..,.***,.XXX~G~ k ~~~~XXXXXXX.,.*. g...*..x..W.~.XXX.***.,,o..oO8X.o.,.o.xx$$$XXXXX
+D: XXX^E..o.XXH.###....###~~~.,h..,.*.,..~~.~~ s ~~.XXXXX.~..**...i~**.....~.,,.,,.***~,,,.oXXXXXXXXXXXXXXXX.!.X
+D: s XXX.o..X&..w~##..##~~~X~^^^^^^^^^^t^ttt^tt w tt^tttt^^tt^^^^^ttt^^^^ttt@@^^^^ttt^^^^^ttt@^^ttXXXW ^^.M*.t^oo.^^...X s
+D: ~~~ XXX>XXH~G..#####~G~~Gt..,.,..,.,xx..x ~~~ i~~xx.~...x....~.~..~..,~..***..~..~...,~.~*.~.XXXE.^^H..SM.~^.oo....JX
+D: ~~....~~ XXX$8w~.^####G~G ~i.XX.,,.,...........G~~~s~~ iiF~...........~.......x.,.**~....,~....~.*XXX.EE.W.^^.M*.~^^^o^O^..X
+D: ~.#..#..~~ XXX8##t..##~#.G.t..XX..,H...,.W...,...,~~...,...*,,.x...,.i.....g.....**.........XXXXXXX...EE.^^H..SM.~.^oo...^JX
+D: ~.##.##..~ XXX###GG~~~*h~.^..XX.....,....,.W...,.G....W.***...,...,...,....,...,**..q....SX3tttW#...EE.W.^^.M*.t.oo.^.^.XX
+D: ~~.###..~~ X##.##~~G^~~~~i*^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX~~
+D: ~~...###.~ k ~###X.G~~@Gh~x*~^..^i.zX8~i.~..h....zzzXiX..^$.T.t$......F.t..XX..^TTT*..M..Y....^.Y.^*.^..~....X.x..x~.<XX~.
+D: ~.....####.....#####XXXx@@~i~.z..z.it,#**t..h..Gzh.zzX@.@Xd*t.~...*~.t......^X..T.^T*@*TXMM...^....^**@.^.x*...+..~....XX..~
+D: ~~.#$%#########....XXX~h~i.HH...h,i^X*.H..h.^G..WX@.E.^X**8..Y.~*8...Y...^+XXXXXXXXXXXMF...~....^.*..x.***..X..x..~XX...~~w
+D: ~~.#$$######.~ ~w XXX$~.x..~G^..,i.,.~.h.^~~..zzX@.@Xd*......F**..~....^XbM..^~ZZZZXMM..*.....~^......*.XXXXXXXIXX.~~#~w
+D: L ~~xx########.~~ XXX$$.G#GX#.i,,^ih.^W.zG^zzzXiX.$.t88.~..^**F..T...tXX~~.^^ZZZZM~..**@~~.Y~..^#^..xXI&X8&..XX ~~~## w
+D: ~.########..~~ XXXXX~#X~X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ~###~w
+D: ~~.#########..~ w K w ~##### w ~.M.~ ~..####.~ s~~~~.~~ s K ~##..~
+D: ~..#########.~ ~##### ~...~ k ~..###.~ ~~####~~~ w w ~###..~
+D: XXXXXXXXXXXX XXXXXX XXXXXX XXXXXXXXX XXXXXX
+
+
+### Starting Location
+P:28:122
+
+
+
+
+
diff --git a/lib/edit/set_info.txt b/lib/edit/set_info.txt
new file mode 100644
index 00000000..b6141e01
--- /dev/null
+++ b/lib/edit/set_info.txt
@@ -0,0 +1,77 @@
+# File: set_info.txt
+
+
+# This file is used to initialize the "lib/raw/set_info.raw" file, which is
+# used to initialize the "item set" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# Version stamp (required)
+
+# N:idx:name
+# D:description
+# P:artifact index:number of item needed:pval
+# F:flags
+
+V:2.0.0
+
+# The Elven Gifts, took from Oangband
+
+N:0:Elven Gifts
+D:It is from a group of Elven items once entrusted to Hobbits
+# Phial of Galadriel
+P:1:2:1
+F:WIS | CHR | RES_DARK
+# Sting
+P:88:2:2
+F:STEALTH | REGEN
+
+
+# The Dragon Slayer
+
+N:1:Dragon Slayer
+D:It is from a group of items rumoured to be the bane of dragons.
+# Bow of bard
+P:125:2:3
+F:DEX | CON | RES_FIRE
+# Arrow of Bard
+P:63:2:5
+F:SPEED
+
+
+# The Trinity -- Possessor set
+
+N:2:The Trinity
+D:It is one of the 3 legendary daggers.
+# Narthanc
+P:66:2:2
+F:STR
+P:66:3:1
+F:KILL_DRAGON | REGEN | SH_FIRE
+# Nimthanc
+P:67:2:2
+F:CON
+P:67:3:1
+F:KILL_DEMON | IM_COLD
+# Dethanc
+P:68:2:2
+F:DEX
+P:68:3:1
+F:KILL_UNDEAD | SH_ELEC | FLY
+
+
+# Gothmog's Armoury -- Demonologists set
+
+N:3:Gothmog's Armoury
+D:It is from a group of items that once belonged to Gothmog,
+D:the High Captain of the Balrogs
+# The demonblade of Gothmog
+P:181:3:7
+F:STR | CON | SPEED | VAMPIRIC
+# The demonshield of Gothmog
+P:182:3:0
+F:IM_FIRE | IM_COLD | SH_ELEC
+# The demonhorn of Gothmog
+P:183:3:0
+F:ESP_EVIL | ESP_GOOD | AUTO_ID
diff --git a/lib/edit/special.txt b/lib/edit/special.txt
new file mode 100644
index 00000000..8d1c94b9
--- /dev/null
+++ b/lib/edit/special.txt
@@ -0,0 +1,67 @@
+# Contains terrain parsings for the special levels now being kept in seperate map files
+#
+# Created for PernAngband 5.0.1 on 18/8/01
+# Written by Mynstral (mynstral@thehelm.com)
+
+# Permanent Wall
+F:X:63:0
+F: :63:0
+
+# Granite Wall
+F:#:57:0
+F:%:57:0
+
+# Hidden Door
+F:+:48:0
+
+# Normal Door
+F:D:32:0
+
+# Floor
+F:.:1:0
+
+# Tree
+F:T:96:0
+
+# Mountain
+F:M:97:0
+
+# Shallow Water
+F:w:84:0
+F:V:84:0
+
+# Deep Water
+F:W:187:0
+
+# Shallow Lava
+F:l:86:0
+
+# Deep Lava
+F:L:85:0
+
+# Glass Wall
+F:G:188:0
+
+# Illusion Wall
+F:I:189:0
+
+# Treasure (random) on normal floor
+F:*:1:0:0:*
+
+# Trap (random) on normal floor
+F:^:1:0:0:0:0:0:*
+
+# down staircase
+F:>:7:0
+
+# between gates
+F:4:160:0:0:0:0:0:0:-1
+F:5:160:0:0:0:0:0:0:-1
+F:6:160:0:0:0:0:0:0:-1
+F:7:160:0:0:0:0:0:0:-1
+F:A:160:0:0:0:0:0:0:-1
+F:B:160:0:0:0:0:0:0:-1
+F:C:160:0:0:0:0:0:0:-1
+F:E:160:0:0:0:0:0:0:-1
+F:F:160:0:0:0:0:0:0:-1
+F:0:160:0:0:0:0:0:0:-1
diff --git a/lib/edit/spiders.map b/lib/edit/spiders.map
new file mode 100644
index 00000000..95f20258
--- /dev/null
+++ b/lib/edit/spiders.map
@@ -0,0 +1,66 @@
+# Lit permanent wall
+F:X:61:6
+
+# up staircase
+F:<:6:8
+
+# Grass
+F:-:89:3
+
+# Trees
+F:t:96:3
+
+# Grass with Giant spider
+F:a:89:3:175
+
+# Grass with Giant tarantula
+F:b:89:3:275
+
+# Grass with Mirkwood Spider
+F:c:89:3:277
+
+# Grass with Aranea
+F:d:89:3:963
+
+# Grass with Elder aranea
+F:e:89:3:964
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X<---tttttttttttttttttttttttttttttttttttttttttttttttttttttttX
+D:X---ttttttttttttttttttttttttttta--ctttttttttttttttttttttttttX
+D:Xtt---------------ttttttttttttt--tttttttttttttttttttttttttttX
+D:Xttttttttt--------------cttttttt--ttttttttttttttttttttttttttX
+D:Xtttttttttttttt--tttttt---------c--tttttttttttttttttttttttttX
+D:Xttttttttttttta-bttt---cttttttttttttttttttttttttttttttttttttX
+D:Xttttttttttttttttttttt---tttt-ttd--c-ttt---c---tttttttttttttX
+D:Xtttttttttttttttttttc---btttttttt-----ttc-----ttttttttttttttX
+D:Xtttttttttttttttttt----tttttttttc-c---------d---c-ctttttttttX
+D:Xtttttttttttttttttb-tttt---c---t----tttttt----------ctttttttX
+D:Xtttttttttttttttttt----ttttt-----c---tttttt-c--cttttttttttttX
+D:Xttttttttttttttttc--at----c---ttttttttttttttttttttttttttttttX
+D:Xttttttttttttta----ttttt-b--tttttttttd--etttttttttttttttttttX
+D:Xtttttttttttt---c--ttttt------tttt-c-tttttttte----dtttttttttX
+D:Xttttttttttttttttttt----c--d----tttt---ttttttt--d----ettttttX
+D:Xttttttttttttttttttttt-c-d---tttttttc--cttttttt-----ttttttttX
+D:Xttttttttttta-c--ttt-------tttt-------ttttt----d--ttttttttttX
+D:Xtttttttttttt----ttttt--ttt--c--c--t-----c--ttttttttttttttttX
+D:Xttttttttttt--c---ttt--c--c---ttttt--c----t----tttttttttttttX
+D:Xtttttttttt-c----c---t-----tttttttt-----ttt-------dtttttttttX
+D:Xttttttttttt---c---tttt--c--ttttttttt-ttttttttt-d---ttttttttX
+D:Xtttttttttttta-----tttttt---d---dttd----cttttt-----tttttttttX
+D:Xtttttttttttttttttttt---c-----ttttt---c----ettd--tttttttttttX
+D:Xtttttttttttttttt----d----tttttttttttttttttttttt---tttttttttX
+D:Xttttttt-------c---tttt-d----c--ettttttttttttd-----dttttttttX
+D:Xtttttc--c-c------tttttt-------------ettttttt----tttttttttttX
+D:Xttttttt------ttttttt--c---d---ttttttttttttt--d--tttttttttttX
+D:Xttttt----cc-tttttttttttte----d-ttttttttttttttt----tttttttttX
+D:Xttttt--c------ttttttttttttttttttttttttttte--tt------ettttttX
+D:Xttt--c----ctttttttttttttttttttttttttte------------tttttttttX
+D:Xtttttttttttttttttttttttttttttttttttttttttt--e--ttttttttttttX
+D:XtttttttttttttttttttttttttttttttttttttttttttttttttttttttttttX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:3
+
diff --git a/lib/edit/st_info.txt b/lib/edit/st_info.txt
new file mode 100644
index 00000000..3762d0c5
--- /dev/null
+++ b/lib/edit/st_info.txt
@@ -0,0 +1,744 @@
+# File: st_info.txt
+
+# Fixed Potions of Cure Light/Serious Wounds in the Temple, Potions of
+# Restore Str/Con in the Alchemist
+# Magic Shop - Amulet of Slow Digestion, Wand of Light, Staffs of Enlightenment,
+# Door/Stair Location, Detect Invis/Evil, and Remove Curse
+
+# This file is used to initialize the "lib/raw/st_info.raw" file, which is
+# used to initialize the "store info type" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# Some store indexes are defined in "defines.h", and must not be
+# changed.
+
+# N:<index>:<name>
+# I:<proba>:<item name>
+# T:<proba>:<tval>:<sval>
+# G:char:attr
+# W:max number of items in the store
+
+# proba is the chance(out of 100) of the item being generated
+
+# Version stamp (required)
+
+V:2.0.0
+
+N:0:General Store
+I:100:& Wooden Torch~
+I:95:& Brass Lantern~
+I:95:& Flask~ of oil
+I:100:& Ration~ of Food
+I:100:& Hard Biscuit~
+I:90:& Strip~ of Venison
+I:70:& Pint~ of Fine Wine
+I:80:& Pint~ of Fine Ale
+I:60:& Shovel~
+I:50:& Pick~
+I:100:& Iron Spike~
+I:70:& Iron Shot~
+I:70:& Bolt~
+I:70:& Arrow~
+I:98:& Cloak~
+I:46:& Fur Cloak~
+A:0:0:1:2:3:4
+O:0:5:6:7
+G:1:U
+W:24
+
+N:1:Armoury
+I:70:& Pair~ of Soft Leather Boots
+I:60:& Pair~ of Hard Leather Boots
+I:80:& Hard Leather Cap~
+I:70:& Metal Cap~
+I:65:& Iron Helm~
+I:100:& Robe~
+I:90:& Soft Leather Armour~
+I:90:& Soft Studded Leather~
+I:90:& Hard Leather Armour~
+I:85:& Hard Studded Leather~
+I:90:& Leather Scale Mail~
+I:80:& Metal Scale Mail~
+I:75:& Chain Mail~
+I:80:& Augmented Chain Mail~
+I:75:& Metal Brigandine Armour~
+I:68:& Bar Chain Mail~
+I:100:& Set~ of Leather Gloves
+I:80:& Set~ of Gauntlets
+I:100:& Small Leather Shield~
+I:90:& Large Leather Shield~
+I:80:& Small Metal Shield~
+A:0:0:1:2:3:4
+O:8:9:10:11
+G:2:s
+W:24
+
+N:2:Weaponsmith
+I:100:& Dagger~
+I:100:& Main Gauche~
+I:100:& Rapier~
+I:100:& Small Sword~
+I:100:& Short Sword~
+I:100:& Sabre~
+I:100:& Cutlass~
+I:100:& Tulwar~
+I:100:& Broad Sword~
+I:100:& Long Sword~
+I:100:& Scimitar~
+I:100:& Katana~
+I:100:& Bastard Sword~
+I:100:& Spear~
+I:100:& Awl-Pike~
+I:100:& Trident~
+I:100:& Pike~
+I:100:& Beaked Axe~
+I:100:& Broad Axe~
+I:100:& Lance~
+I:100:& Battle Axe~
+I:100:& Hatchet~
+I:100:& Sling~
+I:100:& Short Bow~
+I:100:& Long Bow~
+I:100:& Light Crossbow~
+I:100:& Iron Shot~
+I:100:& Arrow~
+I:100:& Bolt~
+I:100:& Whip~
+I:100:& Small Wooden Boomerang~
+A:23:0:1:2:3:4
+O:12:13:14:15
+G:3:w
+W:24
+
+N:3:Temple
+I:100:& Nunchaku~
+I:100:& Quarterstaff~
+I:100:& Mace~
+I:100:& Bo Staff~
+I:100:& War Hammer~
+I:100:& Lucerne Hammer~
+I:100:& Morning Star~
+I:100:& Flail~
+I:100:& Lead-Filled Mace~
+I:100:Remove Curse
+I:100:Blessing
+I:100:Holy Chant
+I:100:Heroism
+I:100:Word of Recall
+I:100:Word of Recall
+I:100:Word of Recall
+#I:100:Cure Light Wounds
+T:100:71:34
+#I:100:Cure Serious Wounds
+#I:100:Cure Serious Wounds
+T:100:71:35
+T:100:71:35
+I:100:Cure Critical Wounds
+I:100:Cure Critical Wounds
+I:100:Restore Life Levels
+I:100:Restore Life Levels
+I:100:Restore Life Levels
+I:100:& Whip~
+I:100:& Mace~
+I:100:& Ball-and-Chain~
+I:100:& War Hammer~
+I:100:Word of Recall
+I:100:Word of Recall
+I:100:Word of Recall
+I:100:Cure Critical Wounds
+I:100:Cure Critical Wounds
+I:100:Restore Life Levels
+I:100:Restore Life Levels
+I:100:Restore Life Levels
+I:100:Remove Curse
+I:100:Remove Curse
+I:100:*Remove Curse*
+I:100:*Remove Curse*
+A:0:0:1:2:3:4
+O:26:27:28:29
+G:4:g
+W:24
+
+N:4:Alchemy shop
+I:100:Enchant Weapon To-Hit
+I:100:Enchant Weapon To-Dam
+I:100:Enchant Armour
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Light
+I:100:Phase Door
+I:100:Phase Door
+T:100:70:9
+I:100:Monster Confusion
+I:100:Magic Mapping
+I:100:Treasure Detection
+I:100:Object Detection
+I:100:Trap Detection
+I:100:Detect Invisible
+I:100:Recharging
+I:100:Satisfy Hunger
+I:100:Word of Recall
+I:100:Word of Recall
+I:100:Word of Recall
+I:100:Word of Recall
+T:100:70:9
+T:100:70:9
+#I:100:Restore Strength
+T:100:71:42
+I:100:Restore Intelligence
+I:100:Restore Wisdom
+I:100:Restore Dexterity
+#I:100:Restore Constitution
+T:100:71:46
+I:100:Restore Charisma
+I:100:Identify
+I:100:*Identify*
+I:100:*Identify*
+I:100:*Identify*
+I:100:*Identify*
+I:100:*Identify*
+I:100:*Identify*
+I:100:Light
+#I:100:Restore Strength
+T:100:71:42
+I:100:Restore Intelligence
+I:100:Restore Wisdom
+I:100:Restore Dexterity
+#I:100:Restore Constitution
+T:100:71:46
+I:100:Restore Charisma
+I:100:Enchant Armour
+I:100:Enchant Armour
+I:100:Recharging
+I:100:Satisfy Hunger
+I:100:Satisfy Hunger
+I:100:Satisfy Hunger
+A:0:0:1:2:3:4
+O:30:31:32:33
+G:5:b
+W:24
+
+N:5:Magic shop
+I:100:Protection
+I:100:Levitation
+I:100:Protection
+I:100:Fire Resistance
+I:100:Cold Resistance
+I:100:Charisma
+I:100:Slow Digestion
+T:100:40:3
+I:100:Acid Resistance
+I:100:Lightning Resistance
+I:100:Searching
+I:100:Cure Light Wounds
+# Rods
+I:100:Probing
+I:25:& Wooden Rod~ of#
+# Book
+T:100:111:50
+# Wands
+T:100:65:3
+T:100:65:8
+T:100:65:11
+T:100:65:16
+T:100:65:22
+# Staves
+T:100:55:3
+T:100:55:5
+T:100:55:8
+T:100:55:9
+T:100:55:14
+T:60:55:15
+T:60:55:16
+T:60:55:17
+A:0:0:1:2:3:4
+O:34:35:36:37
+G:6:r
+W:24
+
+N:6:Black Market
+A:30:0:1:2:3:4
+O:38:39:40:41
+G:7:D
+F:ALL_ITEM | MEDIUM_LEVEL
+W:24
+
+N:7:Home
+A:0:0:54:55:3:0
+O:0:0:0:0
+G:8:y
+W:24
+
+N:8:Book Store
+# & Book~ of Beginner Cantrips
+T:100:111:50
+T:100:111:50
+T:100:111:255
+T:100:111:255
+T:100:111:255
+T:100:111:255
+T:100:111:255
+T:100:111:255
+A:0:0:1:2:3:4
+O:42:43:44:45
+G:9:o
+W:24
+
+N:9:Pet Shop
+I:100:Egg
+T:100:70:6
+I:100:& Hard Biscuit~
+A:0:0:1:2:3:4
+O:46:47:48:49
+G:+:b
+F:MEDIUM_LEVEL
+W:12
+
+N:10:Mayor's Office
+A:0:0:16:15:35:0
+O:1:1:1:1
+G:+:o
+W:0
+
+N:11:Inn
+I:100:& Ration~ of Food
+I:100:& Hard Biscuit~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Fine Wine
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:2:2:18:18
+G:+:w
+W:8
+
+N:12:The Soothsayer
+I:20:Divination
+I:20:Divination
+I:20:Divination
+I:20:Divination
+I:20:Divination
+I:20:Divination
+A:0:0:2:0:8:0
+O:3:3:3:3
+G:+:B
+F:RANDOM
+W:2
+
+N:13:Library
+I:100:Identify
+A:0:0:14:15:16:2
+O:4:4:4:4
+G:+:U
+W:2
+
+N:14:Castle
+A:0:0:16:35:0:0
+O:16:16:16:16
+G:+:o
+W:0
+
+N:15:Casino
+A:13:0:9:10:0:12
+O:17:17:17:17
+G:+:s
+W:0
+
+N:16:Beastmaster Shanty
+# Disabled the bounty list for the time being to not confuse people
+# with the bounty quest
+# A:18:0:19:20:21:22
+A:18:0:21:22:0:0
+O:19:19:19:19
+G:+:g
+W:0
+
+N:17:Fighters Hall
+A:0:0:24:25:0:0
+O:20:20:20:20
+G:+:s
+W:0
+
+N:18:Tower of Magery
+A:0:0:26:27:0:0
+O:21:21:21:21
+G:+:b
+W:0
+
+N:19:Inner Temple
+A:0:0:28:29:0:0
+O:22:22:22:22
+G:+:G
+W:0
+
+N:20:Paladins Guild
+A:0:0:28:25:0:0
+O:23:23:23:23
+G:+:g
+W:0
+
+N:21:Rangers Guild
+A:0:0:31:32:0:0
+O:24:24:24:24
+G:+:u
+W:0
+
+N:22:Thunderlords' Nest
+A:0:0:33:2:34:0
+O:25:25:25:25
+G:+:U
+W:0
+
+N:23:The Mirror
+A:0:0:44:15:16:43
+O:51:51:51:51
+G:+:U
+W:0
+
+N:24:Seat of Ruling
+A:0:0:17:35:0:0
+O:52:52:52:52
+G:+:U
+W:0
+
+N:25:Wizards Spire
+A:60:0:26:27:0:0
+O:54:54:54:54
+G:+:U
+W:0
+
+N:26:Priests Circle
+A:0:0:28:29:0:0
+O:55:55:55:55
+G:+:o
+W:0
+
+N:27:Tower of the King
+A:0:0:17:35:0:0
+O:57:57:57:57
+G:+:U
+W:0
+
+N:28:Library
+I:100:Identify
+A:0:0:14:15:16:2
+O:58:58:58:58
+G:+:U
+W:2
+
+N:29:The White Tree
+I:100:& Ration~ of Food
+I:100:& Hard Biscuit~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Fine Wine
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:59:59:59:59
+G:+:w
+W:8
+
+N:30:Craftsmaster
+A:0:0:24:25:0:0
+O:60:60:60:60
+G:+:s
+W:0
+
+N:31:Earth-Dome (Nature)
+A:0:0:39:0:0:0
+O:61:61:61:61
+G:+:U
+W:0
+
+N:32:Minstrels Haven
+A:0:0:40:41:0:0
+O:62:62:62:62
+G:+:U
+W:0
+
+N:33:Star-Dome
+A:0:0:46:47:0:0
+O:63:63:63:63
+G:+:U
+W:0
+
+N:34:Valarin Temple
+A:0:0:28:48:0:0
+O:64:64:64:64
+G:+:U
+W:0
+
+N:35:Sea-Dome
+A:0:0:49:35:0:0
+O:65:65:65:65
+G:+:U
+W:0
+
+N:36:The Golden Flower
+A:0:0:50:51:0:0
+O:66:66:66:66
+G:+:U
+W:0
+
+N:37:The Fountain
+A:0:0:52:53:0:0
+O:67:67:67:67
+G:+:U
+W:0
+
+# Here begins the random shops, for the random towns
+N:38:Axe Smith
+T:100:24:256
+A:0:0:1:2:3:4
+O:12:13:14:15
+G:3:w
+F:RANDOM | MEDIUM_LEVEL
+W:12
+
+N:39:Hafted Smith
+T:100:21:256
+A:0:0:1:2:3:4
+O:12:13:14:15
+G:3:w
+F:RANDOM | MEDIUM_LEVEL
+W:12
+
+N:40:Polearm Smith
+T:100:22:256
+A:0:0:1:2:3:4
+O:12:13:14:15
+G:3:w
+F:RANDOM | MEDIUM_LEVEL
+W:12
+
+N:41:Sword Smith
+T:100:23:256
+A:0:0:1:2:3:4
+O:12:13:14:15
+G:3:w
+F:RANDOM | MEDIUM_LEVEL
+W:12
+
+N:42:Rare Jewelry Shop
+T:100:40:256
+T:100:45:256
+A:0:0:1:2:3:4
+O:34:35:36:37
+G:6:v
+F:RANDOM | DEPEND_LEVEL | DEEP_LEVEL | FORCE_LEVEL
+F:VERY_RARE
+W:10
+
+N:43:Jewelry Shop
+T:100:40:256
+T:100:45:256
+A:0:0:1:2:3:4
+O:34:35:36:37
+G:6:y
+F:RANDOM | DEPEND_LEVEL | MEDIUM_LEVEL | FORCE_LEVEL
+F:RARE
+W:20
+
+N:44:Footwear Shop
+T:100:30:256
+A:0:0:1:2:3:4
+O:8:9:10:11
+G:2:r
+F:RANDOM | MEDIUM_LEVEL
+F:COMMON
+W:12
+
+N:45:Rare Footwear Shop
+T:100:30:256
+A:0:0:1:2:3:4
+O:8:9:10:11
+G:2:r
+F:RANDOM | DEEP_LEVEL | MEDIUM_LEVEL
+F:VERY_RARE
+W:8
+
+N:46:Library
+T:100:110:256
+T:100:111:256
+T:100:112:256
+T:100:113:256
+T:100:114:256
+T:100:115:256
+T:100:116:256
+T:100:117:256
+T:100:118:256
+T:100:119:256
+T:100:120:256
+T:100:121:256
+T:100:122:256
+T:100:123:256
+T:100:124:256
+T:100:125:256
+A:27:0:1:2:3:4
+O:8:9:10:11
+G:9:y
+F:RANDOM | DEPEND_LEVEL | MEDIUM_LEVEL
+W:24
+
+N:47:Forbidden Library
+T:100:110:256
+T:100:111:256
+T:100:112:256
+T:100:113:256
+T:100:114:256
+T:100:115:256
+T:100:116:256
+T:100:117:256
+T:100:118:256
+T:100:119:256
+T:100:120:256
+T:100:121:256
+T:100:122:256
+T:100:123:256
+T:100:124:256
+T:100:125:256
+A:27:0:1:2:3:4
+O:8:9:10:11
+G:9:v
+F:RANDOM | DEPEND_LEVEL | DEEP_LEVEL
+F:RARE
+W:12
+
+N:48:Expensive Black Market
+A:0:0:1:2:3:4
+O:38:39:40:41
+G:7:v
+F:RANDOM | ALL_ITEM | DEEP_LEVEL | DEPEND_LEVEL | MEDIUM_LEVEL
+W:12
+
+N:49:Common Shop
+I:95:& Brass Lantern~
+I:100:& Flask~ of oil
+I:100:& Ration~ of Food
+I:60:& Shovel~
+I:50:& Pick~
+I:100:& Iron Spike~
+I:70:& Iron Shot~
+I:70:& Bolt~
+I:70:& Arrow~
+I:98:& Cloak~
+I:46:& Fur Cloak~
+I:100:Word of Recall
+I:100:Cure Critical Wounds
+A:0:0:1:2:3:4
+O:0:5:6:7
+G:1:U
+W:12
+F:RANDOM
+
+N:50:Dragon Hunter
+T:100:38:256
+A:0:0:1:2:3:4
+O:8:9:10:11
+G:2:v
+F:RANDOM | DEEP_LEVEL | DEPEND_LEVEL
+F:VERY_RARE
+W:12
+
+N:51:Speed Ring Market
+T:100:45:31
+A:0:0:1:2:3:4
+O:34:35:36:37
+G:6:G
+F:RANDOM | SHALLOW_LEVEL | DEPEND_LEVEL
+F:VERY_RARE
+W:6
+
+N:52:Scribe
+T:100:70:256
+A:0:0:1:2:3:4
+O:8:9:10:11
+G:5:B
+F:RANDOM | MEDIUM_LEVEL | DEPEND_LEVEL
+W:12
+
+N:53:Potion Store
+T:100:71:256
+T:100:72:256
+A:0:0:1:2:3:4
+O:8:9:10:11
+G:5:B
+F:RANDOM | MEDIUM_LEVEL | DEPEND_LEVEL
+W:12
+
+N:54:Recaller
+I:100:Word of Recall
+A:33:0:1:2:3:0
+O:8:9:10:11
+G:+:b
+F:RANDOM | COMMON
+W:2
+
+N:55:Master Archer
+T:100:19:2
+T:100:19:12
+T:20:19:13
+T:100:19:23
+T:20:19:24
+T:50:16:256
+T:50:17:256
+T:50:18:256
+A:0:0:1:2:3:4
+O:12:13:14:15
+G:3:g
+F:RANDOM | MEDIUM_LEVEL | DEPEND_LEVEL
+F:RARE
+W:24
+
+N:56:Merchants Guild
+A:0:0:56:57:58:0
+O:68:68:68:68
+G:+:g
+W:0
+
+N:57:The Mathom-house
+A:0:0:59:0:3:0
+O:0:0:0:0
+G:+:g
+F:MUSEUM
+W:255
+
+N:58:The Prancing Pony
+I:100:& Ration~ of Food
+I:100:& Hard Biscuit~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Fine Wine
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:69:69:69:69
+G:+:w
+W:8
+
+##### Mining equipment for Khazad-Dum #####
+
+N:59:Mining Supply store
+T:100:20:256
+I:100:& Wooden Torch~
+I:95:& Brass Lantern~
+I:95:& Flask~ of oil
+I:75:& Dwarven Lantern~
+I:60:& Feanorian Lamp~
+T:60:65:6
+T:70:71:22
+A:0:0:1:2:3:4
+O:11:15:26:40
+F:MEDIUM_LEVEL
+G:+:s
+W:24
+
+## Library quest in Minas Anor
+
+N:60:Library
+I:100:Identify
+A:61:0:14:15:16:2
+O:4:4:4:4
+G:+:U
+W:2
+
diff --git a/lib/edit/t_basic.txt b/lib/edit/t_basic.txt
new file mode 100644
index 00000000..78103425
--- /dev/null
+++ b/lib/edit/t_basic.txt
@@ -0,0 +1,80 @@
+# File: t_lite.txt
+
+# *Basic* town (vanilla style)
+
+
+############### Town Layout ###############
+
+F:*:7:3:0:0:0:0:0:8
+
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:###################################################################................................................................###################################################################
+D:###################################################################................................................................###################################################################
+D:###################################################################................................................................###################################################################
+D:###################################################################.....................#########...................#########......###################################################################
+D:###################################################################.......#########.....#########......#######......#########......###################################################################
+D:###################################################################.......#########.....####3####......#######......####1####......###################################################################
+D:###################################################################.......####4####....................#######.....................###################################################################
+D:###################################################################....................................###2###.....................###################################################################
+D:###################################################################................................................................###################################################################
+D:###################################################################.................................*..............................###################################################################
+D:###################################################################...##8##........................................................###################################################################
+D:###################################################################...#####..............####5####...................###7###.......###################################################################
+D:###################################################################...#####..............#########.....####6####.....#######.......###################################################################
+D:###################################################################...#####..............#########.....#########.....#######.......###################################################################
+D:###################################################################...........###9###....#########.....#########.....#######.......###################################################################
+D:###################################################################...........#######..................#########.....#######.......###################################################################
+D:###################################################################...........#######................................#######.......###################################################################
+D:###################################################################..................................................#######.......###################################################################
+D:###################################################################................................................................###################################################################
+D:###################################################################................................................................###################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+
+############### Starting positions ###############
+
+# Standard starting position for normal races
+P:32:100
diff --git a/lib/edit/t_bree.txt b/lib/edit/t_bree.txt
new file mode 100644
index 00000000..c74fd58a
--- /dev/null
+++ b/lib/edit/t_bree.txt
@@ -0,0 +1,131 @@
+# File: t_bree.txt
+
+# Bree
+
+############### Additional default terrain settings ###############
+
+# Default for Quest 1 = entrance is quest entrance
+F:z:8:3:0:0:0:0:0:4
+
+# Default for Quest 18 = entrance is tree
+F:y:96:3
+
+# Default for Quest 18 = entrance is tree
+F:x:96:3
+
+############### Quest 4 - Thieves Hideout finished = house ###############
+?:[EQU $QUEST4 2]
+F:z:74:3:0:0:0:0:0:7
+?:[EQU $QUEST4 5]
+F:z:74:3:0:0:0:0:0:7
+?:1
+
+############### Quest 8 - Troll Glade ###############
+?:[AND [EQU $QUEST8 1] [EQU $DAYTIME 0] ]
+F:y:8:3:0:0:0:0:0:8
+?:1
+
+############### Quest 9 - Wights Grave ###############
+?:[EQU $QUEST9 1]
+F:x:8:3:0:0:0:0:0:9
+?:1
+
+###### Additionnal buildings #######
+
+# Castle: Plot Bree
+F:B:75:3:0:0:0:0:0:1
+
+# Mayor's house
+F:b:74:3:0:0:0:0:0:10
+
+# The Prancing Pony
+F:a:74:3:0:0:0:0:0:58
+
+# Soothsayer
+F:c:74:3:0:0:0:0:0:12
+
+# Merchant Guild
+F:d:74:3:0:0:0:0:0:56
+
+# The Mathom-house
+F:e:74:3:0:0:0:0:0:57
+
+############### Town Layout ###############
+
+D:######################################################################################################################################################################################################
+D:# VV -- #
+D:# V -- #
+D:# VV -- #
+D:# VVV -- #
+D:# VV -- #
+D:#OOOO V -- #
+D:# OOOO V -- #
+D:#--- OOOOO VVV --- VV #
+D:#------ OOOOOO V -- VVVV #
+D:#---------- OO V -- VVWWVVV #
+D:#-------------- OO VVV -- VVVWWWWWVVV #
+D:#--------------- OOO VVV -- VVWWWWWWWWWVV #
+D:#----------------- OO VVVVVVV VVWWWWWWWWWWWV #
+D:#------------------- OOOOO --VVVV VVVVV VVWWWWWWWWWWWVV #
+D:###------------------ OO ,,,,,,,,,,,,,,,,,, -- VVVVV VV VVVVVVVWWWWWWWVVV #
+D:#CC####------------------ OOO ,,,,,CCCCCCCCCCCCCCCCCC,,,,,,,,, -- VVVVVV VVVVWWVVV #
+D:#TTCCCC###---------------,, OO ,,,CCCCCCTTTTTTTTTTTTTTTTCCCCCCCCCC,,,, -- VVVV #
+D:#--TTTTCCC###---------,,,-- OO ,,CCCCTTTTTT--------------TTTTTTTTTTCCCCC,,,,, ^-- #
+D:#------TTTCCC##-----,,------ O ,,CCCTTTT..................----------TTTTTCCCCCC,, ^^^ -- #
+D:#---------TTTCC##,,,-------- OO ,CCCTTT....--ssssssss-..---..........-----TTTTTTCCC,,, ^^ -- #
+D:#------------TC,,###- --- OO ,CCTTT...-----SSSSSSSS--.----------,,.....------TTTCCCC,, ^^ -- #
+D:#-------------,TTCCC### -- OOO ,,CCTT...-,,,,--ssssssss-..-sssssssssss,---......---TTTTCCT,, ^^ -- #
+D:#--------- ,,,--TTTCCC -- OO ,,CCCTT..-------,,##9#####-.--sssssssssss-,-------...----TTCCTT, ^^^ -- #
+D:#-------- ,, ,,----TTTCC O ,CCC T..--sssSsss,,,,------.--StSStSSSStS--,------.-....--TTCCTT,^^^ -- #
+D:#------ ,, , OO ,CCTTT...---ssstSSS---,,,----.--sssssssssss---,---...----..--TTCCT^^^^ ^^ -- #
+D:#------- x, ,, OO ,CCTT...-----sssssss--,,-,,---.--sssssssssss---,-...-------..--TTC^^^ ^ ---- #
+D:#------- {, OO ,CTT..---,,,,###6###,,,---,,--.--###########----..----------..--T^^^^ ^ --- -- #
+D:#--------- OOO ,CT..,,,,----,,,,,,,,------,,-.--###b###e###-....,---sssss---.--T^^ ^ -- -- #
+D:#---------- OO ,CCT.,sssssss----,-----------,,.-----.-----....-,--,,-StSSS---.-T^^^ ^ -- ----- #
+D:#------------- OO ,CCTT.-StStSSS---,-----ssssss--..-----.--....--,,,,,,-,sssss---.-T^^^ ^ -- -- ,,, #
+D:#----------- OOOOO ,CTT-.-sssssss---,-----SSSSSS--.------.-..--,,,-ssss--,#####---.-T^^ ^ -- ----- ,, #
+D:#-------- OO ,CCT-..-##5####----,----ssssss-..........--XXXX,-ssss--,------...-^^^ ^ ^^ ^ --- ---- ,,, #
+D:#- ---- OOOOOOOO ,C OOOO--OOOOO----,----#c##4#-.---.,----,-UUUU,-SSSS--,-----..---^^ ^ ^ --- -----, OOOOOOOOOOOOOO#
+D:#- ---- OOOOOOO ,CT-OssOOOO---OOOOOOOOO--OOOO--.-T-.-sss--,XXXX,-ssss---,---..--T^^ -- -- OOO #
+D:#- ----- OOOOOOOOSt.---------XXXX-OOOO--OOOO---.-StS--,#0##,-ssss---,---.---^^ ^ ^ -- --- OOOO #
+D:#- --- ----- ,,CT--.ss.-sssss---XXXX,---------OOOO.-sss---,,,,,-####--,---..-T^^ ^ ^ -- --- OO #
+D:# ---- ---- ,,CT--.B#.-SStSS---####,------------OO-#z#-ssssss-,,,---,----.--T^^ ^ -,- OOOOO #
+D:# ------- ---- ,,CTT-....-sssss--,,,,,,-------------OO-,--SSSSSt-ss-,-,---...-T^^ ^ ^ -, OOOO #
+D:# ------- ------ ,,CCTT---..#2###-,sssss,-SSSSSSSS-----OO,--ssssss-tS--,.....---^^ ^^ ^^^ -, OOOOOOOO #
+D:# ----- ----- ,,CCT----..,,,,,-StSSS,-ssssssss------OOO-#1##a#-ss....-----T^^ ^^ -,- OOOOO #
+D:# ---- -------- ,,CCTT----..----,sssss,-##7###d#--------OO,.,,.,-##.----TTTT^^ ^ ^^ -, OOOOO #
+D:# --------------- ,,,CCTTTT--..---,##3##-,--,,,---------...OOOOOOOOOOOTTTTTCC^^^ ^^ ^^ ,- OOO #
+D:# -------- ,,,CCCC --..........,-,,---,--.......-------TTTTTOCCCCC,,^^^^^^^^^^^ -.- OOOOO #
+D:# ---------- ,, ,,CCCTT----------.....-......-------TTTTTTTCCCCO,,,,, ^^^^^^^ -.- O #
+D:# ------- ,, ,,CCTTTTTTTTTT-----...------TTTTTTTCCCCCC,,,,OOO -.- OOOO #
+D:# ---- ,,, ,CCCCCCCCCCTTTTT-----TTTTTTCCCCCC,,,,,, OOO OOOOOOOOO #
+D:# ^^ ---- ,, ,,,,,,,,,,CCCCCTTTTTTCCCCC,,,,,, OO OOO-- #
+D:# ^^^^^ ,,, ,,,,,CCCCCC,,,,, OOOO OOO-- #
+D:# ^^^^^^^^^ ,, ,,,,,, OOOOOOO OOO--- #
+D:# ^^^^^^^^^ ,, OOOOOOO---- #
+D:# ^^^^^^^^^^^^^^^ ,,, ..------ #
+D:# ^^^^^^^^^^^^^^ ,,, ...... ....----- #
+D:# ^^^^^^^^^^^^^^^ ,,, ........... ............ ............-------,,,, #
+D:#^^^^^^^^^^^^^^^^^ ,, ... .........-------------- ,,,,,,, ,,,,,,,,, #
+D:# ^^^^^^^^^^^^^ ,, . ,,,,,, ,,,,,,,,, #
+D:# ^^^^^^^^^^^^^^ ,, ... ,,,,, ------ --- #
+D:# ^^^^^^^^^^^^^^^ ,, . ,,,,,,,,------- ----- #
+D:# ^^^^^^^^^^^^^^ , .. ---------------- #
+D:# ^^^^^^^^^^^^^^^^ ,, .. ---------y-------- #
+D:# ^^^^^^^^^^^^^^^ , .. ----------------- #
+D:# ^^^^^^^^^^^^^^^^ ,,. --- ---- ---- #
+D:# ^^^^^^^^^^^^^ ,. - -- -- #
+D:# ^^^^^^^^^^^^^^ . #
+D:# ^^^^^^^^^^^^^^ . #
+D:######################################################################################################################################################################################################
+
+
+############### Starting positions ###############
+
+# Standard starting position for normal races
+?:[AND [EQU $LEAVING_QUEST 0] [NOT [EQU $RACE Vampire] ] ]
+P:33:131
+
+# Standard starting position for vampires (at the dungeon entrance)
+?:[AND [EQU $LEAVING_QUEST 0] [EQU $RACE Vampire] ]
+P:31:150
diff --git a/lib/edit/t_d_bree.txt b/lib/edit/t_d_bree.txt
new file mode 100644
index 00000000..2112bc4e
--- /dev/null
+++ b/lib/edit/t_d_bree.txt
@@ -0,0 +1,101 @@
+# File: t_d_bree.txt
+#
+# Destroyed Bree
+
+# original town by someone else
+# screwing up by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+
+# Dead/burnt tree
+F:D:92:3
+
+# Ash
+F:A:93:3
+
+# Fire
+F:F:205:3
+
+# Permanent rubble
+F:R:206:3
+
+############### Town Layout ###############
+
+D:###########################################################################################################F##########################################################################################
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDFDDDDDDDDDDDDDDD VVDDDDDDDDDDDDDD--DDDDDDDDFFDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:# DDD DDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD VDDDDDDDDDDDDDD--DDDDDDDDDDDDDDDDD D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDD D D DDDDDDDDDDDDDDDDDDDDDDDDDD DD D D D DDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDVVDDDDDDD --DDDDDDDFFFFDDDDD DDDDDDDDDDDDDD DDD DD DDDDDDDDDDDDDDDD #
+D:#DDDDDDD D DDDDDDDDDDDDDDDDD DDDDDDDD DD D AADD D D DDDD D D D D D D DDAADDDDFDDDDDDDDDDDDDDDDDDDDDDDVVFFFDDDDD --DDDDDAAADDDDDD FDDDDFDDDDFFFDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDD#
+D:# DDDD DDD DD DDDDDDDDDDDDDDDDD D DD DDDDDDDDD D D DDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDDDD VVDDDDDDD --DDDDDDDDDDDDDDDDDDDDDDAAA DDDDDDDDDDDDDDAAADDDDDDDFDDDDDDD #
+D:#OOOODDDDDDD D D DDDDDDDD D DDDDDDDDD D DD D DDDDDDDDDDDDD D DDDDDDDDDDDDDDDAADDDDDDD DDDDDDDDDDDDDDD VDDDDDDD --DDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDFFFADDDDDDDDDDDD #
+D:# OOOODDDDDDD DDDDDDDDDDDDDDDDDDDDDDD AA DDDDDDDDD ADD DDDDDDDDDDDDD D DDDDDAAAAAADDDDDDDDDDDDAAADDDDDDDDDDDDDDD VDDDDDDD --DDDDDDDDDDDD DDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDFF #
+D:#--- OOOOODDDDDDDDDDDDDDDDD DDDDDDDD D DD D AAADDAADDDDDDDDD D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDFFDDDD VVV ---DDDDDDDDDDDD DDDDDDDDD VVDDDDDADDDDDDDDDDDDDDDDD #
+D:#------ OOOOOO DD D D DDDDDDDDDD DDD DDDDDDDDDDDAAAA F D DDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDDDDDDDDDDDDDDDD V --DDDDDDDDDDDDD DDDDAD VVVVDDDDDDDDDFF DDDDDDDDDDD #
+D:#---------- OO DDD ADAAADDD DDDDDDDDDDDDDDDDD D D DDDDAAADDDDDDDDDDDDD D ADDDDDDD FDDDDDDDDDDDDDDFFFDDDDDDDDDDDDD V -- DDDDDDDDDDD DD VVWWVVVDDDDDAAA DDDDDDDDDDDDDD #
+D:#-------------- OO DDDDDDDDDDDDDDDDD A D D D DDDDDAAADDDDDDDDDDDDDD DDD D DDDDDDDDDDDDDDDDDDDDDDAFFADDAADDDDDDDDDDVVV --DDDDDDDDAAAADDDDDD DDD VVVWWWWWVVVDDDDDDDDDDDDDDDDDDDDDDD#
+D:#------D-------- OOODDDDDDD D DDDDAAADDD AAD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDD FDDDDDDDDDDDDDDDD VVV --DDDDDDDDDDDDDDDD VVWWWWWWWWWVVDDDDDDDDDDDDDDDD #
+D:#----------------- OO A DDDDDDDDDDDDDDDDD AAA D DDDD DD DDDDDDDDDDDDDDDDDDDDD DDDDDD DDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDD VVVVVVVDDDDDDDDDDDDDDDD VVWWWWWWWWWWWVDDDDDD DDDDDDDD #
+D:#------------------- OOOOO DD DDDDDDDDDD DDDDDDDD D DDDDDDDDDDD DDD DDD DD DDD DDDDD DDDDDDDDDDDDDDDDDFDDDDDDDDDDDDDDDD--VVVV VVVVVDDDDDDD VVWWWWWWWWWWWVVDDDDDDDDD DDDDD #
+D:###------------------ OOA D AA DDD DDDDDDDDD DDDDDDDDDDDDDDDDD DDDD DDD ,,,,,,,,,,,,,,,AA,DDDDDDDDDDDDDDDDDDDDDFFDDDDDDDDD -- VVVVV VV VVVVVVVWWWWWWWVVVDDDDDDD DDDDDDDD #
+D:#CC####------------------ OOODDDDDDD DDD DDDDDDDDD DD D DDDD DD DDDD,,,,CCCCCCCCCCCCCCCCCC,,,,,,,A,DDDDDDDDDDDDDDDDDDDDDDD --DDDDDDD VVVVVV VVVVWWVVVDDDDDDDDDDDDDDDDDDDDDDD #
+D:#TTCCCC###----D----------,, OO DDDDDDDDDDDDDDDFDDDDDDDDDDDDDDDDDDDDDDD DD ,,,CCCCCCTTTDDTTTDD TTDDTCCCCCCCCCC,,,,DDDDDDDFDDDDDFFDDDDDDDD --DDDDDDDDDDDDDDDDDDDDDDDVVVVDDDDDDFFFAAADDDDFDDDDDD #
+D:#--TTTTCCC###---------,,,-- OO F DDD D DDDDDDDD DD D DDD DDDD,CCCCTTTTTD---R----------TT T TDDDTCCCCC,,,,,DDDDDDDDDDDDDD A ^--DDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#------TTTCCC##-----,,------ ODDDDDDDDDDDDDDDD DDDDDDDDD DDDDDDDD DD ,,CCCTTDDD....RRR........D.----A-----TDDTTCCCCCC,,DDDDDDDDDDDDDD^^^ --DDDDDDDDDDDDDFFDD DDDDDDDDDD DDDDDDDDDDDDDDDDDD #
+D:#---------TTTCC##,,,-------- OO D DDAAAADDDDDD DDDDDDDDDD D DD ,CCC TD....--ssss-sss-D.--A.......A..--R--TTDDDTCCC,,,DDDDDDD ^^ --DDDAAAD DDDDDDDD DDDDDDDDDDDDDDD DDDDD FDDDDDDD #
+D:#------------TC,,###- --- OODDDDDDD DDDD A FDDDDDDD DD D,CCTTT.DDAF---S.SFRSRS#-.------DD-R,,.....------TDDDCCC,,DDDDDDD^^ --DDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDFDDDDDDDD #
+D:#-------------,TTCCC### -- OOO DDD DDD DDDD D D DD DD ,,CCTD...F,,,,A-ssss.ss-...-ssFs--sssss,---...A..---TDDDCCT,A ^^DDDDDDD--DDDDDDDDDDDD DDDDD DDDDDDDDDDDDFFDDDDDDDDDDD #
+D:#--------- ,,,--TTTCCC -- OODDDDDDDDDDDADDDDDDDDDDDDDDDDDD D ,,CCCTT..-------,,#.,##,-#-.--sss.sRRRssss,--RAA--..R----TDCCTT, ^^^DDDDDDD --DDDDDDDDDDDDFFFDDDDDD DDDDDDDDDDDDDDDDDDDDDDDD #
+D:#-------- ,, ,,----TTTCCDDDDDDD O FDDDDDDDDD DDDDDAADDDDD DDDDCCC DF.A-sssSssRS,,,------.--S.SSSSFSSRR--,----DD.-....--FDCCTT,^^^DDDDDDD --DDDDFAAADDDD DDDDDD DDDDDDDDDDFDDDDDDDDDDDDDDD#
+D:#------ ,, ,DDDDDDDDDDDDDDDD OO DDDDDDDDDD DADDDDDDDD DDD,CCTDT.F.---ssRsSSS---,,,--A-.--ss,sRsFsssFs--,-R-.FF-R--..--TDCCT^^^^ ^^DDDDDDD --FDDDDDDDDDDDDDD DD DD FDDDDDDDDDDDDDDDD #
+D:#------- ,, ,,DDDDDDDDAAADDDDD OODDDDDDDDDDDDDADDDDDDDDDDD D ,CCTT...-----s,,-sss#-,,-,DD-R.--.ssssAsss,s#--,-...-------..-FFTC^^^ ^DDDDDDD ----DDDDDDDDDDDDDDDDDDDD DDDDDD DDDDDDDD #
+D:#------- {,DDDDDDDDDDDDDDDD F OODDDDDDD DDDDDDDDDD DDD,CTT..-DD,A,,####R###R,-R-,,--.--#####F.#R#A----..-DD---A---R.--D^^^^ ^DDDDDDD--- --DDDDDDDDDDDDDDDDDD DDDDDD DDDD #
+D:#-F-------DDDDDDD DDDDDADDDDDDDDDDDOOO D DDDDDDDD DDD D ,CT..,,,,----,,,,,#,,------,A-R--#.-#.##.##R-...R,---.sss----.--T^^ ^DDDDDDD -- --DDDDDDD ADDDDDDDDDDDDDDDDDDDDDDDFFFDDDDDD#
+D:#----------DDDDDDDDDDDDDDDDDDDDDDD OO DDDDDDDD DDD,CCT.,ssRsss#----,R---DD---#-,FF-----.----#....-,--,,sStSSS--R.-D^^^ ^DDDDDDD -- -----DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#-------------DDDDDDD ADDDDDDD F OODDDDDDDDDDDDDDDD D ,CCTT.-StS,RS----,-----RsRsA,--..-FA-----....--,,,R,,-.s.sss#--.-D^^^ ^ --DDDDDDD --DDDDDDD ,,DDDDDDDDDD DDDD #
+D:#----------FDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOOOOO DD D ,CTT-.--sFs,ss#-R,-----SRSSSS--.------R-..FF,,,-sssss-,##FF#---.-T^^ ^DDDDDDD --DDDDFFD -----DDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDD #
+D:#--------DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD OO D DD D DD,CCT-..-#####,#---AA---RsssRs,-...R...A.R--RXXX,-Rss---,------..F-^^^ ^ ^^ ^ ---DDDDDDD D---D ,,, ADDDDDDDDDDDDDDDD #
+D:#- ----DDDDDDD FDAADDDDDDDDDDDDD FDDDDDDD OOOOOOOOR DD RD ,C OOOO--OOOOO----,----######-.---.,----,-UUUUs-SSSR--,--A--..---^^ ^ ^ A ---DDDDDAADDDDDDDDD -----,DDDDDDD OOOOOOOOOOOOOO#
+D:#- ----DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOOOOOOOFF,CT-OssOOOO---OOOOOOOOO--OOOO--.-D-.-sRs--,XXXX,-Fsss-R-,---RR-FD^^DDDDDDDDDDDDDD--DDDDDDDAAADDDDDD --DDDDDDD OOADDDDDDD #
+D:#- -----DDDDDDDDDDDDAADD FDDDDD DDDDDDDDDDDDDDDD OOOOOOOOSt.---------XXXR-OOOO--OOOO---.SStS--,####,--sRs-F-,DD-.--F^^ ^ ^ --DDDDDDDDDDAAADDDDDDDDDD--- OOOO ADDDDDDD #
+D:#- --- -----DDDDDDAAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,CT--.ss.-RsAsR---RXXs,---------OOOO.-sRs---,,,,,-###.--,---..-T^^ ^ ^DDDDDDD --DDDDDDDADDDDDDDDDDDDDDDD --- OOADDDDDDDDDDDDDDDD #
+D:# ---D ----DDDDDDDDDDDDDDDDDDDDDDD F FDDDDDDD ,,CT--.s#.-SSt,S-AA##A#,---A-R------OO-#R#-ss,,ss-,,,---,---D.--DA^ ^DDDDDDD -,-DDDDDDDDDDDDDDDDDDDDDDD OOOOODDDDAAADDDDDDDDD #
+D:# ------- ---- ADDDDDDDDDDDDDDDDDDD DDDDDDDDDDD ,,CTT-..RR-FsssR--,,,,,,---------DDA-OO-,--SSSRSt-ss-,-RR--...-T^^ ^ ^DDDDDDD -,DDDDDDD FDDDDDDDDDDDDDDOOOODDDDDDDDDDDDAD DDDDDDD#
+D:# ------- ------DDDDDDDDD DD DDDDDDDDDDD DDDDDDDDDD ,,CCTD---.R#,#R,-,sss,,S-S,SSSSS--R---OO,--s-ss----S--,.....---^^ ^^ ^^^ -,DDDDDDDDDDDDDDDD OOOOOOOODDDDDDDDDDDDD DDDDDDDDD #
+D:# -----DDDDDDD -----DDDDDDDDDDDDDDDD FDDDDDDDDDDDDDDDD ,,CCT----..,,,,,-StRSS,-s,s,sRss--FF--OOO-#,##-R-ss....-FF--T^^DDDDDDD ^^ -,-DDDDDDDDDDDDDD OOOOODDDDDDDDDDDDDDDDDDD DDDDDDDDDDD #
+D:# ---- --------DDDDDDDDDDDDDDDDDDDDDDAADDDDDDDD A ,,CCTD--DD R--RRsssR,-##,##,#F--------OO,.,,.,-##.----DDDT^^ ^ ^^DDDDDDD-,DDDDDDD OOOOODDDDDDD DDDDD ADDDDDDDDDFFFDDDD#
+D:# ---------------DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD D ,,,CCTTTT--..---,##,###,--,,,--AA-----...OOOOOOOOOOOTTDDDCC^^^ ^^ ^^DDDDDDD ,-DDDDDDD OOODDDDDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDD #
+D:#DDDDDDD --------DDDDDDDD DDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDA,,CCCC --.DD.....AA,-,FF--,--FF..A..--A----TDDTTOCCCCC,,^^^^^^^^^F^DDDDDDD -.- OOOOODDDDDD DDDDDDDDDDDDDDDDAAADDDDDDDDDDDD #
+D:# ----------DDDDDDDDDDDDDD F ADDDDDAAADDDDDDDDDDDDDDD ,, ,,CCCTT-------D--.....R....A.-------TTTDDTTCCCCO,,,,, ^^^^^^^DDDDDDD -.- ODDDDDDDDDDDDDDDDDDAADD DDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDD -----D-DDDDDDDDDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDD ,, ,,CCTTT TTDTTDD-D--...------DTTTDDDCCCCCC,,,,OOOADDDDDDDDDDDDDDDD -.- OOOODDDDDDDDDDDDD DDAAADDDDDDDDD DDDDDD D #
+D:#DDDDDDD ----DDDDDDDDDDDDDDDDAADDDDDDDDDDDDDAAA FDDDDDDD ,,, ,CCCCCCCCCCTTDDT--FF-TTTTDTCCCCCC,,,,,, OOODDDDDDDDDDDDDDDD OOOOOOOOODDDDDDDD DDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDDD #
+D:# ^^ ----DDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDD ,, ,A,,,,,,,,CCCCCTTTDDTCCCCC,,,,,,DDDDDDDDDDDDDDOODDDDDDD DDDDDDDDDOOO--DDDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDDD DDDDD DAADDDDDDDD #
+D:# ^^^^^DDDDDDDDDDDDDDDD DDDDDDDDDDDD DDDDDDDDDDDDDDDDD ,,,DDDDDDDDDDDDDD,,,,,CCCCCC,,,,, ADDDDDDD D OOOODDDDDDD OOO--DDDDDDDAAADDDDDDDDDD DDDAADDDD DDDDDDDDDDDD #
+D:# ^^^^^^^^^DDDDDDDDDDAAAADDDDDDDDDDDAD DDDAAADDDDDDDDDDDDDD ,,DDDDDDDDDDDDDDDD ,,,,,,DDDDDDDDDDDDDDDDDDDDDDD OOOOOOO OOO---DDDDDDDDDDDDDDDD ADDDDDDDDDDDDDD DDDDDDDDDDDDD #
+D:# ^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,DDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOOOOOOO----DDDDDFFF DDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:# ^^^^^^^^^^^^^^^DDDDDDDDD DDDDDDDDDDDDDDDDDDDD AA FDDDDDDD ,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD FDDDDDDDDDDDDDDDDDDDDDDD ..------DDDDDDDDDDDDDDDDDDDDDDDDDDDD DDD DDDDDDDDDAAADDDDDDDD #
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDDDDD DDDDDDDDDDD DDDDDDDDDDD ,,,FDDDDDDDDDDDDDDDD AA......DDDDDDDDDDDDDDDDDDDDDDD ....-----DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAFFDDDDDD #
+D:# ^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDD DDDDDDDDDDDD DDDDDDDDDDDD,,,DDDDDDD ........... ............DDDDDDD....F.......-------,,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDAADDD #
+D:#^^^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,DDDDDDD .AADDDDDDDDDDDDDDDDDDDDDDD .........--------AA----DDDDDDD,,,,,,, ,,,,,,,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDFFDDD #
+D:# ^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDD DDDDDDDD DDDDDDDDDDDDDDD ,, A .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAFFDDDDDDDDDDD ,,,,,,DDDDDDD,,,,,,,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDD DDDDD DD DDDDDDDDDDDDDDDDDDDDD ,, AA... ADDDDDDDDDDDDDDFDAAADDDDDDDDD DDDDDDDDD DDD DDDDDDDDDDDDDDDDDDDDDDDDDDD,,,,,DDDDDDD ------ ---DDDDDDD #
+D:# ^^^^^^A^^^^^^^^DDDDDDD DDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDD,, .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,,,,,,,------- -----DDDDDDD#
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDDDDDDDDDDDDD , ..ADDDDDDDDDDDDDDFFDDDDDDDDDDDDDDDDDDDDDDDDDDAADADDDDDADDDDD DDDD DDDDDDDDDDDDDDDDDDDDDDDD A---------------- #
+D:# ^^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDD ,, ..DDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD---------D--------DDDDDDD#
+D:# ^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDD DDDDAAAD DDDDDDDDDDDDD , ..DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDADDDDDDDDDDDDFFFFDDDDDDDDDDDDDDDDDDDDDDDDD -----------------DDDDDDD #
+D:# ^^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDAAAD DDDDDDDDDDDDDDDDDDDDDDDD ,,.DDDDDDDDDDDDDDDDDDD FFFDDDDDD DDDDAADDDDDDDDDDAAFFDDDDDDDDDDDDAAADDDDDDDDDDDDD DDDDDDDDDDDDD --- ---- ----DDDDDDD#
+D:# ^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD ,.DDDDDDDDDDDDFFDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDFFFDDDDDDDDDDDDDDDDDDDD - -- --DDDFFDD #
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDD .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAADDFFDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDDDDDDDDDDDDDDDDDDAAADDD #
+D:# ^^^^^^^^^^^^^^ ADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAAAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:######################################################################################################################################################################################################
+
+
+############### Starting positions ###############
+
+# Standard starting position for normal races
+?:[AND [EQU $LEAVING_QUEST 0] [NOT [EQU $RACE Vampire] ] ]
+P:33:131
+
+# Standard starting position for vampires (at the dungeon entrance)
+?:[AND [EQU $LEAVING_QUEST 0] [EQU $RACE Vampire] ]
+P:31:150
diff --git a/lib/edit/t_d_gond.txt b/lib/edit/t_d_gond.txt
new file mode 100644
index 00000000..12483e32
--- /dev/null
+++ b/lib/edit/t_d_gond.txt
@@ -0,0 +1,118 @@
+# File: t_d_gond.txt
+
+# Destroyed Gondolin: Your failure has left the city in ruins.
+# Created by Mynstral (mynstral@thehelm.com)
+
+
+# Extra terrain features that aren't commonly used
+
+# Dead/burnt tree
+F:D:92:3
+
+# Ash
+F:A:93:3
+
+# Fire
+F:F:205:3
+
+# Permanent rubble
+F:R:206:3
+
+# Tainted water
+F:w:174:3
+
+# Decoration = Straight Road (B)
+F:":66:3
+
+# Decoration = Straight Road (W)
+F:$:70:3
+
+
+# Town Layout
+
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################^^^^^^^^^^^^R ####
+D:####################################^^^^^^^^^^^R ####
+D:##################################^^^^^^^ R R R ###
+D:#################################^^^^^^^^R C C R CCR CC CC C CCRC CR R ###
+D:################################^^^R^^^^^^^ C RCRCR#R######R#R#####R#####CCR CCR R R ###
+D:###############################^^^^^^^^^^^^ CRRR C# ###### DD D ####RDRD D D ###R###CC C R ^^^^^ ###
+D:##############################^^^^^^R^^^R C R####R##RD DDD R.R## R.DD DDD#RR#####C R R^^^^^^^ R ###
+D:#############################^^^^^^^^^^ # R##########D R######### .D##########R R ^^^^^^^^^^^^ ###
+D:############################^^RR^^ R ############ ..R###########R ..D############C R ^^^^^^^^^^^^^^R ###
+D:############################ C #############DR .###ww#####ww### .D#############CR ^^^^^^^^^^^^^ ###
+D:########################### RR R #############DD .#wwww#R###wwRw# R.RR############# R ^^^^^^^^^^^ ###
+D:########################### R . R RR C#R###########DR .ww###R###ww .DR############C R R ^^^^^^^ R R ###
+D:########################## R ##### RR R #### R R C######wwww### ..w## R##R ..D####wwww#####CR ####### # R ^^^^ ###
+D:########################## R #R##### R ##R## ####### C#R####D DDww#DR # .R# DRRwD DD######C R## ##R#RR ###
+D:####R#################R#w# ### ###R ### R ####### ###### DDw#R D#wDD D####R ##R#### ###
+D:####wR#w###R#R#####R#Rwww# R####R### #R##R ####### R######D R RR..R######R ###
+D:#####wR##w#w##R##wRwww#w## #####R### # ### #########R ######D R .. DD DD ..RRD######CR R ###
+D:#####RwRwRwww##RwRwww#RR## R######R# R ##R R##R#### RR R RC#######Rw ..DD D####### DD ..ww#R##### C # R ###
+D:######w##ww#ww#wwwww#R#### . R R R# R# #######R## D ##### R###### DD ########## R ## ## ### R RR ###
+D:######www#wwwRwRwRwwRw#### .. R CR####### .. D###### R# # ##### .. ########R #R#######R###R#### ###
+D:#######RwwwwRwwwRwwR#ww### R #####D DD .. D###R.R#R# #R## .###D ..DDDR#####CR R #### # #######R ###
+D:########RwwRwwRwwwR#w###### # ###### R R## #####R C# D ..D## ..## RR ..# R .. D DD# R RR#R#######R## ###
+D:#########R#wwwwRww########## #R#R####R ############ R#D R .R#R ## R ..R R.. ..## D R# # ..D#C R R ###R### R## ###
+D:##########R#################### # ####### ############ R #D R R####R D## R# R ..# @ @ ## .#R##R R RD# ## #R#R#R## @ @ ###
+D:#### R#################### ####R# ##R #####R####### RC#D # #R R. D## @@@ @ .##D ..R## R .D #RC #### R @@@@@@ ###
+D:#### ####RRRC##w#LL#""## #######R ## R##R#R ## C### R ###R . D ### R @w@@..###D D .#### ###R @ @ @@@@wwww@@@ ###
+D:#### R####R##RC# R#LL#""# # . R R ###D R##R D###.### .#####w@ @###.## R###R R### R @@ @@wwwwwww@ ###
+D:##### ################### . R C### ## #R D## ..##.R##wRww#..## ..# D ## R D###C @@@@wwwwwwwww@ ###
+D:# R#### D# .#..#ww#Rw#..# .#D R## .. @wwwwwwwwwwww@@ ###
+D:#.. ..#w#w#w# R @wwwwwwwwwwww@ @ ###
+D:# .##R# D# .#.R#ww#wR#..# .#D ####R .. @@wwwwwwwwwww@@ @ R ###
+D:############ ########## # . . . R###D ..#R###R ## ..##..##www##..## ..## #R # R R##C @wwwwwwwwww@@@@ ^^###
+D:#### #### K# C# #LL# "# R . R . . ### ##R# DD###.### .##### R###.###D R### . D#R# @@@wwwwwww@@@@@@ R ^^###
+D:#### ### K CC ww# L# "## ##### # R ## #### # # #### CR##D .. DD### ..###DD .###R DD####RC @@wwwww@@ @ @ ^^^^###
+D:#### ####KK#CC#ww#L # "### ## ###### ###R ###R# R# ##### R#DD D## R .##D ..R# # D #####CR @@wwww@ R R^^^^^^###
+D:######## ###################### ######### R####R#R### # ## R# CRD ..D## R#R .RR ## .#R##R .D##### R DD @@@@@@@ ^^^^^^^###
+D:####### #####R$R$#R#### #### ### ###R# R ## ########R# # # # ww ww .DD## R R.##RR ## RR## D #R# R###### DD DD DD DD DD ^^^^^^^^^###
+D:########$R$$R#R$RR$##### ## R##R###### ##R# ### R### R## R CRR..ww# R #Rw D####.#########R#R####D ..######C D DD DDD D D DD DDD ^^^^^^^^^###
+D:######$$R#$R$$#$#$##$#$### R C D..w#R .###w . DD#################D D .DR#####R DD DDD########DDD DDDD ^^^^^^^###
+D:#######R#$##$$$#$#$$R$#### C#D .w###.R# R D D### #########DD D #R###R DDD###DD D D### D DD R ^^^^^^###
+D:######R$R$$$#$#R#$R$$R$$# R R RR RC##D.w##R # Rw ..D D ####### D D ### DDD##DD D ##DDDDDD ^^^^###
+D:#####$$$$#$$R#$$$$$R$$R### ###### # RR### R###RR RC#D.ww#####ww R RR DD#C DD#DD D DD## D DD R ^^^^###
+D:#####R$$#$$R#####$R$$$R### ## ####R R #R## ##### ##R R ..ww###wwR ..D DR RDDDR R R .DD R R D DR R. DR R D ##D DDD ^^^^###
+D:####R$$###R########R#R$$$# ###R#####R ## ####### # C wwwRw .DDR######R#RDRD RDDD#R###R#R#DDD RR R R D#DD D DD##D ^^^^^^###
+D:###R$#################R### # ## ## # ###########R# CRDD .DD###wwwwww####R . R#R##wwwwww### D .DD#C R DDD##DDD D D##DD D ^^^^^^^###
+D:#######################R## R#### #R ############# R# .D##www .wRw###DDD###www .www#RDR .D#R DDDD D D D###DD D R ^^^^^^^###
+D:########################### R # R R CR# .D#ww ..ww#######ww R.ww#D D#R DD D DD# ## ## DDD DD ^^^^^###
+D:########################### R R# C#D R ..####### R ..DD#R DD D DDD DDD DD D ^^^^###
+D:############################ ##R #R R#D ..D#ww R R..ww#######wR R ..ww#D .. DR# R DDDD DD D D R R ^^^^^^###
+D:############################ ######### C ##DD .D##www .www#########www R.www##D . R## DDD D DD D ^^^^^^^^^###
+D:############################# #####R# R#D DD DD ##wwwwww## #### #####wwwwww###D DD D##RC ^^^^^^^^^^^^###
+D:############################## R ###R #### DD DDD ##### # ######R # ########DDDD RDRD ###RC R ^^^^^^^^^^^^^^###
+D:############################### ### RR CR R#R#R##DDD RDR DD##############D DRRD D D## #R#CCRR R^^^^^^^^^^^^^^^^^###
+D:################################ R # CC R ##R# #R#####R###R#R#R#####R###R###RCRC CR R ^^^^^^^^^^^^^^^^^###
+D:################################# R R R # R CRCR CCCRCC CRRCRCCC C R #C C CR RC C R ^^^^^^^^^^^^^^^^^^####
+D:################################## #### R R R # R ^^^^^^^^^^^^^^^^^^^^^####
+D:#################################### R ##### R ^^^^^^^^^^^^^^^^^^^######
+D:###################################### #### ######## R ^^^^^^^^^^^^^^^^^^########
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+
+# Default starting position
+?:[EQU $LEAVING_QUEST 0]
+P:33:50
+
+# Starting position when coming from quest 19
+?:[EQU $LEAVING_QUEST 19]
+P:51:190
+
+# Starting position when coming from quest 20
+?:[EQU $LEAVING_QUEST 20]
+P:33:13
+
+# Starting position when coming from quest 21
+?:[EQU $LEAVING_QUEST 21]
+P:27:168
+
+# Starting position when coming from quest 22
+?:[EQU $LEAVING_QUEST 22]
+P:6:42
diff --git a/lib/edit/t_d_khaz.txt b/lib/edit/t_d_khaz.txt
new file mode 100644
index 00000000..950f5c82
--- /dev/null
+++ b/lib/edit/t_d_khaz.txt
@@ -0,0 +1,87 @@
+# Town Name: Destroyed Khazad-Dum
+# by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+F:o:207:3
+
+# Ash
+F:A:93:3
+
+# Fire
+F:F:205:3
+
+# Permanent rubble
+F:R:206:3
+
+# Town Layout
+D:######################################################################################################################################################################################################
+D:#ooooooooooooo####^^^^^^^^^#######^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#####oooo#######oo######^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^##########oooooooooo CCCCCCC #
+D:#ooo##ooooooooo#####^^^^^##########^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^########ooo########ooo##########^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^################oooooooooooo CCCCCCCC #
+D:##oo###oo####oo##############################^^^^^^^^^^^^^^^^^########################oooo#########oo#### ###########^^^^^^^^^^^^^^^^^^^^^^^^####### #######oooo########## CCCCCCCC #
+D:##############o#########oo###oo###################^^^^^^^############################oooo#########oo###### ################################# #####ooooooooo############# CCCCCCCC ; #
+D:#^^^^########oooooooooooooo##ooooooooo############################# #################oooo########oo########### ########################## ######ooooooooo################ CCCCCCCC ; #
+D:#############ooooooooooooooooooooooooooo###### ########### ###### ############## ####oooo#######o##o############# ################### ###########ooo######################## CCCCCCCC ; #
+D:#^#############ooooo##################oo### # #### # ##### #### ############### ###ooo########ooo## ## ###### # ## ## ## # #######ooooo########################### CCCCCCCC #
+D:#^^################oo#################ooooo#### ## ; ## ;; o ; o ## # ooooooooo############################ CCCCCCCC #
+D:#^^^^################o##########o#####oooooooooooooooooooooo AAAAAAAA # o ; oooooooooooooooooooooooo##ooo############################ CCCCCCC #
+D:#^^^^^^################################# ## ; o ; AAFFFFFAA A o ; o R#o################################### CCCCCCC#
+D:#^^^^^^################################ ### o ### AAFFFFAAAA ## o ###; o ##A ######################################## CCCCCC#
+D:#^^^^^################################# ### o ### ### ## AAFFFFFAAo ### o ### o #AAFFA RR###################################### CCCCCC#
+D:#^^^^^^^################################ ###### ;; o ######### AAAFFFFFAo ###### o ## ### o ## #AFA #R##################################### ; CCCCCC#
+D:#^^^^^^^^################################ # ## #### o ; ## # # AAAAAAAAA ####### # #### # o ## ###FF# ###R#R################################## CCCCCC #
+D:#^^^^^^^################################### ## ## o ; ;### # AAAAo # # ##o # ## ; o # ##FF ;#R##RR################################# CCCCCCC #
+D:#^^^^^^^^^################################ ## AAAAAAAA ### o ### o # # o ## # #R################################## CCCCCC ; #
+D:#^^^^^^^^^################################# AAFFFFFFAA o o o #R ################################## CCCCCC #
+D:#^^^^^^^^^^################################ ; AAFFFFFFFAAoooooooo o o oooooooooooo AAAA R################################# CCCCCC ; #
+D:#^^^^^^^^^^^^^########################### AAAFFFFFAA o o o o FFFFF A ################################# CCCCCCC #
+D:#^^^^^^^^^^^^^^^########################## AAAAAAAAAAAAAAFF o ### # o o ;### o ### # AF FAA #R############################## CCCCCCC ; #
+D:#^^^^^^^^^^^^############################# AFFFAA #####; o # ## o o # ### o # # # FF ; ###############################; CCCCCCC ; #
+D:#^^^^^^^^^############################### AAFFFA; ## ## # o #### ## o ; o ####### o ### #A AAA ############################ CCCCCCC #
+D:#^######################################### AFAA; ## ### oooooooo #### # #o o ###### oooooo ###AAFF o ; R#############################; CCCCCCC #
+D:#################ooo###################### AA ; ### # o # ## o o ##### o ## AFFA# RR#############oo############## CCCCC; #
+D:####oo##ooo#####o#######o###ooo########### ## ; o ## o o ### o ; ###FFAA ;; R########oo###oooooo#####oo###### CCCCC ; #
+D:#ooooooooooooooooooooooooooooooo##oooooooo ; o ; o o o ; # ;; ; oooooooooooo##oooooooo##oooooooooo CCCCC ; #
+D:#ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooAAAFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo CCCCCC #
+D:#oooooooooooooooooo##oooooo##oooooo##ooooo AA ; o o AA FFA ; Roooooooo#oooooooooooooooooooooooo CCCCC #
+D:#^#######################oo####ooooo####### ##### FFAFA # # o o AFFF### # ######o####oo####oo######oo######; CCCCC #
+D:#^^#######################o########### #### AAFFFAA# oA o A FA## # ## ## ## RR####o####################oo#### CCCCC #
+D:#^####################################### ## ### ;; ; AAFFAA##### oA FFFAA ### ### ## #### R####oo######################### CCCCCC #
+D:#^^######################################## ####### ; #A##### oA o # #### # # ### RRR###o############oo########## CCCCCCC #
+D:#^^^###############ooo################### ### # ; ;# ## oA AA o FA ##### ; ### ## ;; R#####oo########ooooo####### CCCCCCCC #
+D:#####oo##oo#####oooooooo##############oo## ### ; #### o AA o F A ### ### R#######oooo##oooooo####### CCCCCCCCC ; #
+D:#ooooooooooo###ooooooooooooooooooooooooooo # o A oF # ooooooooooooooooooooooooooo CCCCCCCC #
+D:#ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooFoooFooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo CCCCCCCC ; #
+D:#^ooooooooooooooooo###oooooooooo##oooooooo o #o A o o AA # ooooooo####ooooooooooooo CCCCCCCC #
+D:#^^#o###ooo#####ooo#####ooooo########oo# # # ; o ### o A Fo ;### o A ### oR#########ooooo###oo# CCCCCCCC ; #
+D:#^^^^^###o################oo############# # # # ; o ###### # AF o ## # o #### ; R###########oo######## CCCCCCCC ; #
+D:#^^^^^^^##################################R ; #### ooooooo ##### o ; A o ###### ooooo A ####### ######################## CCCCCCCCC #
+D:#^^^^^^^^^^##############################RR AA; ## ##### o ; ####### o Ao ### # o FFF # ## # o ; #R######################## CCCCCCCC #
+D:#^^^^^^^^^^^^^^^######################### R AA FF A ; ## ## o #### o A ##### o AFF ### # ############################ CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^###################RR AAAFFFAA ## ; o ### o o # # o ; A ### ; ;; RR########################## CCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^#################### R AAAFFFAA ; o ;; o ; o o A ; R########################### CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^######################R AAFFA A ooooooooooo o o oooooooooooo ;; ;R ############################ CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^^^^#################RRR ; AFFA o ; ; o o ; A o R############################ CCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^##############R ### o # # o ### o ###; FF A o #### RR############################ CCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^############RRR ### # o AAA A # ### o ## # o # # # AAFF Ao # ## R ######################## CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^##########R R ### # #;; ; o AAAA ## #### o ## #### o ### ### FFAo ####### R########################### CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^##########RR ## ### o ;A A #### ## A A o ### # # o # ## ### AAF AA ####### ############################ CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^########### ### o ; ;## ## AAA o # ### o ##### ; FFAA #### ; ############################## CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^############## R ### ; o ## AAAA o # # o A ## A oFA ### R############################## CCCCCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^^^^#################R o A AoAA A Ao A A AAAAA FFF R#########oo###oo################# CCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^#############o#######oooooooooooooooooooooo ## AA A o A A A AR AAFFFAFA oooooooooooooooooRoooooooooooooooooo################## CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^############ooooooooooo# ### RRR RR # ### RR R o RR## AAR F FFA#FF A AA FFFFRR R ## R oooooo##oooooooooo################### CCCCCCC #
+D:#^^^^^^^^^^^#################ooooooooooo####R######R ## RR### ######### ###RR####o RR##### #ooo# ### R#F#F#RRRR A FF##R#R ##### ####################ooo##################### CCCCCCC #
+D:#^^^^^^^^^^###################oo######oo### ########### ######## ## #### ###########oooo########ooo ##########R#A##A A ######R# ###### ####################oo###################### CCCCCCC #
+D:#^^^^^^^##########oo####oo####ooo######### ############ ######### ###^^^ #########ooooo########oo##################A################### #########^######oooo##ooo################## CCCCCCC #
+D:##^^^^^#########oooooooooooooooo#################^^####### ###########^^^^^###########ooooo######ooo####################################### #####^^^#######ooooooooooooo############# ; CCCCCCC#
+D:#^^^##########ooooooooooo##ooooo######^#########^^^^##################^^^^^^^^^^^######ooo#######oooo#########^^^^^^^^^^^^^^^^^^^########### #####^^^#######oo##oooooooo############# CCCCCC#
+D:##############ooooo##########oo######^^^#####^^^^^^^^###############^^^^^^^^^^^^^^#####oooo#######ooo######^^^^^^^^^^^^^^^^^^^^^^^^^^^^###### ####^^^^^###############ooo############## CCCCC#
+D:########ooo#####oo##################^^^^^^^^^^^^^^^^^#############^^^^^^^^^^^^^^^^^####oo########oooo#####^^^^^^^^^^^^^^^^^^^^^^^^^^^########### #######^^^^^#############ooo#ooo######## CCCCCC#
+D:#o##oooooooooooooooo##############^^^^^^^^^^^^^^^^^^^^^^#########^^^^^^^^^^^^^^^^^^#####oo########oo######^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####### # ######^^^^^^^^^^^^########oooooooooooooo CCCCC #
+D:#oooooooooooooooooo########^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^########oooo#######ooooo###^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^##############^^^^^^^^^^^^^^^#######ooooooooooo CCCCC #
+D:######################################################################################################################################################################################################
+
+# Default starting position
+# ?:[EQU $LEAVING_QUES 0]
+# P:31:32
diff --git a/lib/edit/t_d_lori.txt b/lib/edit/t_d_lori.txt
new file mode 100644
index 00000000..e5ab3abf
--- /dev/null
+++ b/lib/edit/t_d_lori.txt
@@ -0,0 +1,101 @@
+# Town Name: Lothlorien (destroyed)
+#
+# original town by Akhronath (zzhou22876@aol.com)
+# destruction by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+
+
+
+# Dead/burnt tree
+F:D:92:3
+
+# Ash
+F:A:93:3
+
+# Fire
+F:F:205:3
+
+# Permanent rubble
+F:R:206:3
+
+# Town Layout
+D:######################################################################################################################################################################################################
+D:#DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD .DDDDDDDDDDDDDD,DDDD,DDDDDDDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDDDDDD ,DDDDDDDD DDDDAAA#
+D:#DDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDD , #
+D:#DDDDAADDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDAAADDDDD.DDDDD DD DDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDD ,,DDDDDDDDDDD, .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DDDDDDDDDDDDDDDDDD ,,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDD.AAADDDDDDDDDDD ,DDDDDDDDDDDDDD ,,DDDDDDDAAA,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDD,DDDDDDDDDDDDDDDDDDDDDDDD ,DDDD ,DDDDDDDDDDDDDDDDDD ,DDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDAADDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDD DDDDDDDDDDDDDDDDDDD ,DDDDDDAADDDDDDDDDD #
+D:#DDDDDDDDDD DDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDDDDD ,DDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDDDDDDDDDDDD ,,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDFDDDDDDDDDDDDDD ,DDDD ,DDDD,,DDDDDDDDD DDDDDDDDDDDDDDDD ,DDDDDDDDDDAADDDDDDDDDDDDDDDAAA,DDDDDDDDDDDDDD ,DDDDDDD #
+D:#DDDDDDDAAA,DDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD AADDDDDDDDDDDDDD AAAAFDDDDDDD ,DDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDD ADDDDDDDDDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDDDDD FDDDD FFDDDD ,DDDDDDDDDDDDDD,DDDD,DDDDDDDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDDDDDD ,DDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDDDDDAAADDD ,DDDDDDDDDAAADDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDD AADDDDDDDDDDD FDDDD F F F ,DDDDDDDDDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD , #
+D:#DDDD DDDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD AADDDDDDDAA ADDDDDDDADDDDDDD## F,AAA##DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDD DDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDD #### , # ##DDDDDDDDDDDDDDDDDDD DDAAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD , #
+D:#DDDDDDDDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDD AFDDDDDDD ADDDDDDD RDDDDDDD##DDDD FDDDD##R,,F,, ##DDDD ADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDD DDDDDDDDDDDDDDDDDD,DDDDDDADDDDDDDDDDDDDDDDD A ,DDDDDDDDDDDDDD## #DDDD#####, AADDDD## , , ###DDDD F ADDDDDDDDDDDDDDDDDDDDDDDD , DDAAA,DDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DDDADDDDDDDDDDDDDD ,,DDDD ,DDDDDDDDDDDDDD AAAAADDDDDDDDDDDFFDDDDDDD A # #A# A ### #DDDDDDD ##AAA,AAAA#DDDD AAAAAADDDDDDDDDDDADD ,,DDDDDDDAAA,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDA, A ,DDDDDDDDDDDDDDDDDDDDDDDD## ##AAA, #A##DDDD##AAAR#AAA,AAA##AAA, FAADDDDDDDDDDDDDDDDDDDDDDDDDDD AA ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDDDDD,AADDDDD AAAADDDDDDD###AADDDD FDDDDA,AAA#####DDDD##.##AAA####RR ## , ##DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDADDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDD ,,DDDD ,DDDDDDDDDDDDDD A ADDDD FFF##DDDDDDD # ## #DDDDA #DDDDDDD.DDDD ##########,,,, # #AAAF,,DDDDDDDDD DDDDDDDDDDDDDDDD ,DAADDDDDDDDDDDADDDDDDDDDDDDAAA,DDDDDDDDDDDDDD ,DDDDDDD #
+D:#DDDDDDDDD DDDDDDDDDDDDDDDD,DDDDDDD AA ADDDDDDD #### ,A R ####DDDD ,DDDDDDD , ,, ##F#####AAAA ####DDDDFDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DD DDDDDDDDDDDDDD ,,DDDD ,DDDDDDDDDDD ADDDDDDDDDDD###R# # ######DDDDDDD,DDDDDDD ,DDDD #R#AAA#AAA,AAA##DDDD FAAA,DDDDDDDDDDDDDD ,,DDDDDDD A,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDA,DDDD,DDDD #### ##A###F##DAADDDD, F , , AAAAF ,DDDDDDD ,AAA##AAA, F FDDDDDDD DDDDDDDDDDDD ADDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDDDDD,DDDDDDDAADDDDDDDDDDD ############,,,,AA,,,AAA,,AAA,DDDD F ,DDDD A , ###DDDDDDDDDDD ADDDDDDDDDDDDDDDDDDDD A ,DDDDDDDDDADDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDAAA,DDD DDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD##### #######DDDDDDD ,DDDD,,,,,,,,,,,A,,,F,,,,,,,,R###DDDDDDDDD AAAA ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDD ADDDDDDDDDDD ######## #####AAA,AAA,DDDDDDDDDDDDDD FDDDD AA , ###AAA,,DDDD, F ,ADDDDDDDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDAAAAA, ,DDDDDDDDDDD ,DDDDDDDDDDDDDDAAA#
+D:#DD DDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDAAADDDDFDDDD,## # RRAAA######DDDD ,DDDDDDDDDDDDDDDDDD FDDDD ,DDDD# ,DDDDRDDDDDDDDDDDAAADDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD , #
+D:#DDDDDDDDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDD FFAAA###DDDDDDD # R #DDDD ##DDDD A ## #AAA## A FF ,DDDD ,AAA### ###DDDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDD ,DDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDD #DDDDDDDDDDDDDD#,AAA#R###DDDD F ##DDDD####AAAA ,AAA,,,AAA##### ####DDDDDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA## ## AAAA###DDDD #FAAAF,,,,,,,,DDDD ##########DDDDAADDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDD ,DDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDD DDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ### # AAA ,#DDDD ,DDDD ,F ,AAA, ,DDDD### ###DDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDADDDDAAA,DDDDDDDDDDDDDD ,DDDDDDD #
+D:#DDDDDDDAAA,DDDDDDDAAAAAADDDDDDDDDDDDDDDDDDDDDDDDDDD ADDDDDDDDDDDDDDDDDDDDDDDD A ,DDDD ,DDDD,, ,AAA,DDDD AA,DDDD### ## ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDDDD DDDDDDDDDAAAAAFDDDDDDDDDDDDDDDDDDDD ,,AA ,,,,,,,,,,,,, ,F, ,DDDDDDD, , , # ##DDDDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDAAADDD ,DDDDDDDDDDD ,DDDDDDDDDDDDDDAAA#
+D:#DDDD DDD DDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDD ,FDDDDDDDDDDDDDDDDDDDD AA , ,,A,DDDDDDDDDDD,AA,F,F,,,,,,,,,,,,,AA,,R###DDDDDDD AADDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDAADDDDDAADDDDDDDDDDDAAA, #
+D:#DDDDDDDDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDD AADDDDDDDDDDD AA A ADDDD,,DDDDDDDDDDD ADDDD ,,, FDDDDDDDAA A,DDDD# ##DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDDDDDDDDDDDDD DDDDDDDDDAA,DDDDDDDDDDDDDDDDDDDD AADDDD ,DDDD AAAAADDDD --,--AAAA,AADDDDDDDDDDD ,AAAVVV@DDDD F#,DDDD # #DDDDDDDDDDADDDAAA,DDDD ,DDDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DDDDDDDDDDDDDDDDDD ,,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDD F FDDDDDDDAAAF AAAA----,---- AADDDDDDDDDDD AA VWWWV@DDDD #R#DDDDDDDDDDDDDD ,,DDDDDDD DD,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDD,DDDDDDDDDDD AAAAADADDDD ---VV,@@---ADDDDDDDDDDDDDD , @WWV@@ ,AAAF###DDDDDDDDDDDDDDDDDDDDDDDDDDD A ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDDDDDDDDD D ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDA AA A ---VVW,WV@---DDDDDDD A AAAA, V@VVDDDDDDD#F####DDDDF,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADADDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDDDDDDDDDDDDDDDDDDD FFAADDDDDDD A ,F---VWW@WV@---DDDDDDDDDDDAAAA,, @@DDDDDDDR###AAA,AAAFDDDDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDAAA, ,DDDDDDDAAADDD,DDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDFDDDDAADDDDADDDD---V@WWW@V---DDDDDDD ADDDDDDD,,ADDDDADDDD #DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DD DDDDDDDDDD ,,AAAD ,DDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDD AADDDDDDDAAA---V@VVV@--AAA,A,,,,,,,AA,,,, ,DDDDDDD,,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDDDDD ,DDDDDDD #
+D:#DDDDDDDDDAAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ------@@-DDDD,DDDDDDD ADDDD ,DDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDD DDD ,DDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDD FDDDDDDD ---DDDDDDD,DDDDDDD ADDDD #R#DDDD FDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD , #
+D:#DDDDDDDDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ADDDD F FDDDDDDD ,DDDDADDDDDDD R##DDDDAAADDDD FAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDD DDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDAADDDDAADDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDAAA,AAA#F##DDDDDDDF AADDDDDDDAAADDDDDDDDDD ,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD , #
+D:#DDDDDDDDDDDDDDDDDD ,, AAAAADDDDDDDDDDDDDD AAAAAAADDDDDDDDDDDDDDDDDD AADDDDDDDDDDD #R#DDDD ADDDDDDD #DDDD AAA ,ADDDDDDDDDDDDDD ,,DDDDDDDAAA,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDD ADA AA,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDD,DDDDDDDDDDDDDDDDDDADDDDDDD,DDDD ,DDDD ##R#DDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDD,DDDDDDDDDDDDDDAAAAADDDDDDDDDDDDDDAA AAAADDDDDDDDDDDAFAADDDDDDDAAAFDDDD####RAAAFDDDDDDD AADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDD AADDDDDDDDDDDDDD ADDDD FDDDDAA FDDDD#F#DDDD FDDDDDDD AADDDDDDDDDDDDDDDDDDDDDDDDDAAADDDDDDDDDD ,DDDDDDDDDDDDDD ,DDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDDDDDDDDDDDD ,, AA ,DDDDDDDDDDDDDDDDDDDD AADDDDDDDDDDDDDDDDDDDDDDDD FDDDDDDDDDDD #DDDDDDDDDDDDDD A ,DDDD,,DDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDADDD ,DDDDDDD #
+D:#DDDDDDDAAA,DDDDDDDAAAAAAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAADDDD AADDDDDDDAADDDDDDDDDDDDDDDDDDDDDDDD AAADDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDAAAAA, ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDD AADDDDDDDDDDDDDDDDDDAADDDDDDDDDDD ,DDDD,DDDDDDDDDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDDD , ,DDDDDDDDDDD ,DDDDDDDDDDDDDDAAA#
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDD ADDDD ,,DDDDDDDDDDDAADDDDDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDD ,AAAAAA,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DDDDDDD DDDDDDDDDD ,, AAA,DAADDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDD ,,DDDDDDD A ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDDDDDAA A ,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,AAAAADDDDDDDD DDDDDDDDDDDDDD ,DDDD ,DDDDDDDDDDDDDAAADDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD AA,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAA#
+D:#DDDDDDD A ,DDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDDDDDDDDDDD #
+D:#DDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDAADDDDDDDDDDDDDDDD ,DDDDDDDD DDDDD ,DDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#DDDDDDDDDDDDDD ,, AA ,DDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD , AAA,DDDDDDDDDDDDDDDDDAADDDDDDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDAAA,DDDDDDDDDDDAAAA ,DDDDDDD #
+D:#DDDDDDDDDDDDDDDDDDDDAA,DDDDDDDDDDDDDDDDDDDDAAAD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DDDDDDDDDDDDDD ,DDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDD DDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDDDDDDD DDDDDDDDDDDDDD ,,DDDDDDDDDDD,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,DDDD ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD, #
+D:#DDDDDD D DDDDDDDD ,,AAA ,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,DDDDDDDDDDDDDD ,,DDDDDDDAAA,DDDDDDDDDDDDDD,DDDDDDDDDDDDDD , ,DDDDDDDDDDDDDD,DDDD#
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:######################################################################################################################################################################################################
+
+# Default starting position
+?:[EQU $LEAVING_QUES 0]
+P:13:99
+
+# Starting position when coming from quest 12
+?:[EQU $LEAVING_QUES 12]
+P:26:109
+
+# Starting position when coming from quest 13
+?:[EQU $LEAVING_QUES 13]
+P:7:99
diff --git a/lib/edit/t_d_mina.txt b/lib/edit/t_d_mina.txt
new file mode 100644
index 00000000..49a32b04
--- /dev/null
+++ b/lib/edit/t_d_mina.txt
@@ -0,0 +1,91 @@
+# File: t_d_mina.txt
+
+# Minas Anor: The Royal City of Gondor (destroyed)
+# original town by Mynstral (mynstral@thehelm.com)
+#
+# screwing up by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+# Completed: 23/07/02
+
+# Dead/burnt tree
+F:D:92:3
+
+# Ash
+F:A:93:3
+
+# Fire
+F:F:205:3
+
+# Permanent rubble
+F:R:206:3
+
+############### Town Layout ###############
+
+D:######################################################################################################################################################################################################
+D:#^^########------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@@@@@@AAAAAAAAAAAAAAAAAAF@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF#
+D:#^^^----#####AF------##FF-------AAAAAAAAAAFFF^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@VVVVV@@AAAAAAAAAAAAAAFFF@@VVVVVVV@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF,,,AAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^^^----------#R##---###F###-------AAAAAAAAF^^^^^AAAAAAAAAAAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAA@VVVVVVV@@@@@AAAAAAAAAAFF@@VV@@@@@@VVVVVV@@@@AAAAAAAAADDDAAADDDAAAAAAAAAAAAAF,,AAAAAAAAAAAAAAAAAAAAAAAAFFF#
+D:#^^----ssss-----#R#--------###FF-----AAAAAAAA^^^^^^AAAAAAAAAAAAAAAAAAAAAAADDAAAAAAAAAAAAAFF@VVVVVV@@VVV@@@AAAAAAAAAA@VV@@AAAA@@@@@@VVVV@@AAAAAAAAADDDDDAAAAAAAAAAAAAFFF,,AAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^^---St-RF--------F--#ssss--###-------AAAAFF^^^^^^^^AAAAAAAAAAFFAAAAAAAAAAFFFAAAAAAAAAFFF@@VVVVVVVV@VVV@@@@@@@@F@@@V@@AAAAAAAAAA@@@@VV@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAA,AAAAAAAAAAAFFAAAAAAAAAAAAAAAA#
+D:#^^----sFss----OO---##--#StSS--FFF##F-----AAAAF^^^^^^^^AAAAAAAAAFFFAAAAAAAAAAAFFFAAAAAAAAFFF@@V@V,@@@@@@VVVVVVVV@@@VVV@AAAAAAAAAAAAAA@@VVVV@@AAAAAAAAAAAAAAAAAAAAAAAAA,,AAAAAAAAAAAFFADDDAAAAAAAAAAAA#
+D:#^^----##-#-----OOO--#---#s--RF----###------AAAA^^^^^^^^AAAAAAAAAAFFAAAAAAAAAAAAAAAAAAAAAAFFF@@@@@AAAAF@@@@@@@@VVVVV@@@AAAAAAAAAAAAAAF@@@@VV@@@AAAAAAAAAADDAAAAAAAAA,,,AAAAADDDAAAAAFFAAAAAAAAAAAAAAF#
+D:#^F---------------OO---#-####-#------AAAA----FFF^^^^^^^^^^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@VVV@@AAAAAAAAAAAAAAAAA@@VVV@@AAAAAAAAAAAAAAAAAFFF,O,AAAAAAAAADDDDAAAAAAAFAAAAAAAAAAAF#
+D:#^FStSSSS-----ss---OO---##-----OOOOO---###----FFF^^^^^^^^^^^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@AAAAAAAAAAAAAAAAAFFF@@VVV@@AAAAAAAAAAAAAAAAAFFOOAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAFF#
+D:#^^ss-AFF----Ssss---AFO--#-F--R#O-O-OO---##----FFFAD^^^^^^^AAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@VVV@AAAAAAAAAAAAAAAAAFFOOAAAAAAAAAAAAAAAAAAAAAAAAAADDAAAAAAAFFF#
+D:#^F#-R###---sAFR--F--OOO--F#-OOOOOOO-OOO--##----AAAA^^^^^AAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF@@VVV@@AAAAAAAAAAAAAAAAAFOOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^^-------##sssSss---AAO--#OOO--s--OOOOO--###F--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@AAAAF@@VVV@@AAAAAAAAAAAAAAAAAFOOAAAAAAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAF#
+D:#^^^##RR##---##AFA--s--OOO-OOO--StS#-OOOO----#FF--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF@@VVVVVVVV@@@@@@@V@@@@AAAAAAAAAAAAAAAAAFOOAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAFF#
+D:#^AA^----###---##--ssS--OOO--##-ssss--OOOOOO--#R---AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAADDDDAAAAF@VVVVVVVVVVVV@@VV@@AAAAFFOOOAAAAAAAAAAAAAAOOAAAAAAAAAAAAAAAAAAFFAAAAAAAAAAAAAFFF#
+D:#^^--------#R-----ssSs#--AF#-#--#AFAF#-OOAAAO--##-AAAAAAAAAAAAAA----AAAAAAAAAAAAAAAAAAAAAAAAFFFFFFFFFAAAAAAAAAAAAAAAAAAA@VVVV@@@@VVVVVVV@@AAAAFFOOOOOAAAAAAAAFOOOOOAAAAAAAAAAAAFAAAAAAAAAADDDAAAAAAAA#
+D:#^F-----------#--#s-RRF-OO-O--#---------OOOOOO--FF-----AAAAFFF--------AAAAAAAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAAAAAADAAAAAAF@@VVV@@FF@@VVV@@@@AAAAFFFOOFFOOAAAAFFFOOAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAFF#
+D:#^^----AAA----R---###--OOOOOO-#--#sss#RF-OOOOOO,#FFFF---AAAAF---#-------AAAAAAAAAAAAAAAAAAAAAAAAFFAAAAAAAAAAAAAAAAAAFF@@VVV@@AAAA@@@@@AAAAAAAAFOO-FFFOOOOOOOOOAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAFFF#
+D:#^^-------------##-#-#-O#-OOO--#--ssssss----OOO,,,,,#----FF-AAssAF--R----AAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAADDDAAAAAAF@VVV@@AAAAAAAAAAAAAAFFF-ORAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFAAAAAAAF#
+D:#^^-------------R#---O-O-t-OOO-F#-#SS-##--OOOOOO,##,##--------sssssssAAA-#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDAAAAAA@VV@@AAAAAAAAAAAAAAFF--OO-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAF#
+D:#^^^-FAF--AA-----##-OOO-sssOOAA-#--ssssss-OOOOOO--#,#RR##-----SSR---SS-----AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDAAAAAAA@@VV@AAAAAAAAAAAAAAFF--OR-AAAAAAAAAAAFFAAAAAAAADDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^^--AF----------#OOORA#-RFOOO-#--ss#sss--OAAOOOFFAAAF##FF---sFFFAFss--O---AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF@@VVV@AAAAAAAAAAAAAAFF-OO--AAAAAAAAAAAAAAAAAAAAAAAFFAAAAAFFFAAAAAAAAAAAAAAAAAAAAA#
+D:#^^--###--AAAA--AA--O-------OOAAA--####AAA-OOOOOO-##,###,,##--sssFAFAs--AAA-AAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAF@VVV@@AAAAAAAAAAAAAAF--O--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFAAAAAAAAAAAAAAAAF#
+D:#^^^-##AFA-------OOO#--SSStS---OAAAAA----AAAOOOO---#AAAAAAF#F-ssssR-#F--O-----AAAAAAAAAAAAAAAAAAAAAAAAAFFAAAAAAAFFF@@VV@@AAAAAAAAAAAAAAF--OR-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF#
+D:#^^-####R-------OOO-#--sssss-OOO-##---#s--OOOOO--T-#,--AAAAA--AFA##AA#--O---AA-AAAAAAAAAAAAAAAAAAAAFFAAAAAAAAAAAAFF@VVV@AAAAAAAAAAAAAAF--OO--AAAAAAAAAAFFAAAAAAAADDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^#######------OO--##-###R-AAAO-##-ssss---OOOO-TT-FAA-AAA,#----AAA,,,,,O------AAAAAAAAAAFFFAAAAAAAAAFFFFAAAAAAAAF@@VV@@AAAAAAAAAAAAAA--OO--AAAAAAAAAAAAAAAAAAAAADAFFAAAADDDAAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^^^###AFAF#----OOO--#---R---OOO--AAS#-R--OOOOAAAT-#---AA,,###----------O---AA--AAAAAAAAAFFAFAAAAAAAAAAFFFAAAAAAAA@VVV@@AAAAAAAAAAFFF--OO--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFAAAAAAAAAAAAAAAAAAFF#
+D:#^^#####R--####-OR---##-StSSS-AAO-#-sss-RFOOOOO--T-#F---,,##FF-s-ssAFF--O--------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@VV@@AAAAAAAAAAFFF--ODR--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^^^#########---OOOAAA#-sss-RF--O---ssss-AAOOOOO---FF,AAA#FF---SSSAFAFF-O--AAA-AA-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@@VVV@AAAAAAAAAAFFF--ORO--AAAAAAAAAFAAAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAAFFFAAAAAAFFF#
+D:#^^###AAA###----OOO#--#-###A##OOO-#-##A#---OOOOOO--##FF#A#-----sR-ssss--O----------AAAAAAAAFFFAAAAAAAAAAAAAAAAAAAA@VVVV@@AAAAAAAAAAF---OOO--AAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAA#
+D:#^^^##R##-------OOO---#-#--^--OOO-#----#----OO--RROOFOOOOOOOO--####--R#-O-----------AAAAAAAAFFAAAAAAAAAAAAAAAAAFF@@VVV@@AAAAAAAAF-A----OO--AAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAFFFAAAAAAAAF#
+D:#^^######-##OOOROOO-^^^^^^^^-^MMM^^^^^^^^#^^^OOOOOO-ROOOO-ROOAOOOOOOOAAOO----AAA----AAAAAAAAAAFAAAAAAAAAAAAAAAA@@@VVV@@AAAAAAAAFA-AOOOOOO--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDFAAAAFFAFFFAAAAAAAAF#
+D:#^^####AFFAAOROOOO######A#####III##-#########^OOAAAOOOOOOO#OOOOOOOOOO#OOOOO-------AAAAAAAAAAAAAAAAAAAAAAAAAAAF@@VVVV@@AAAAAAAAF---OOOOOO--AAAAAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^F######-##OOOO-##-####AA#AA#III#####-##--##^OOOOR-OOOOOOOOAAAOO-OOOOOOOOOO------A--AAAAAAAAAAAAAAAAAAAAAAAAA@VVVV@@AAAAAAAA----OOORRO----AAAAAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^F###-A##-OOOOOOOO-^#^^^^^#A^MM#^^^^#^^#^^-#AFOOAFOOOOOR-OOOOAOOOOOOOOOOOOOOO--AA---AAAAAAAAAAAAAAAAAAAAAAAAA@VVV@@AAAAFFA--AD-OOOR-----AAAAAAAAAAFAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF#
+D:#^^^#######-----OOO---A-sAA#--OOO##--##-----R--OOOOOOOOOOOOOO-----------OOO-R-O----AAAAAAAAAAAAAAAAFFF---------##@VVVR##F--------OOOOO-----AAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAFFFFFAAAAAAAAAA#
+D:#^^#########----OAO---#-sSss--OOO---ssss#--OOOOOO--##FF###---AA---ss-#-#O-OOOOO-AA--AAAAAAAAAAAAAAFFF--OOOO-----######R--------OOOOOO-----AAAAAAAAAAAAADDDDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAF#
+D:#^^####R-AFA#---OOR--R--sts##-O-R-#-SSASS-OOOOOO---##F##AFF---------t-S-O-OOROO----AAAAAAAAAAAAAAFFF--OOOOOOOOOOOOO#OOOOROOOOOOOOOOO-AAA-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFDDAAAAFFAAAAAAAAAAA#
+D:#^^###########--OOO--##-sSFR--OOO-R-s-#---AFOOO--TFF#F#,,,##F#--A-sssss-O--OOOOO---AAAAAAAAAAAAAAFF--ROOOOOOOOOOOOOOOOOOOOOOOOO#AOO-----AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFADDDAAAAAF#
+D:#^^#R--R--##----OOO--#--####-OOO--#-sssss-OOAOO-TT-#,,,,-AAFF-----sAAFA-R---OR-O---AAAAAAAAAAAAAAF--OOOAA-OOOOOOORAAO#OOOOOOOOOOO------AAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAAAF#
+D:#^^#######------O-R-##-------OO---#-###A#-OOOOO-TT-F#----AFF#F----###R--O-AAOOOOAA--AAAAAAAAAAFFF--OOOO---------#######------------A--AAAAAAAAAAAAAAAAAAAFAAAAADDAAAAAADDAAAAAAAAAAAAADDDAAAAAAAAAFFF#
+D:#^^-###R-#-AA---OOO-#--S-ss##OOO-#--------OOOOO--T-##-----AAA----A------A----OOOO----AAAAAAAAAAF--OOOA----DD---##@VVV@##-D-----A---AAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF#
+D:#^^^-###---------OO-#-#stss--OOO-#-ssssss--OR-RO---#,--AA,A#----sss-#s--O-----OOOO--A-AAAAAAAA---OOOO---AAAAAAAAF@VVV@@AAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAFFAAAAAAAAAA#
+D:#^^--####---------OOO--#s#s-OOO--FRStS##---OAFAOO-#FF-,,#F##----S--#AA--O------OOR-----AAAAFF--OOOOO----AAAAAAAAF@VVV@@AAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^^---#AA--AA-----#OOO--##--OOO-###ssssss--OOOOOO-#,,,##FF#------RsAFA--O--A----OOR#----AAAA--OOOOO----AAAAAAAAAA@VVVV@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^^--#------AFF-R--AFAAA--OO----#-###A##-OOR-OO--#F####AAA-AA--ss#-ss-OO----D---OOOO----AA--OOOOO---FFFAAAAAAAAF@@VVV@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@@@AAAAAAAAAAAAAAAAAAAAFFF#
+D:#^^^-------------#---OOO---OOO--#F------##OOOOOO##AAAAF-AA--AA--#-##-AAA-R-AA-----R--A----OOOOOO-AA--AAAAAAAAAAFFF@@VVVV@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@@@@@AAAAAAAAAADDAAAAAAAAFFF#
+D:#^^----------AFFA-----AFAAOOO-----ss------OOR-O,,##FAAA--------AAA-----AAA--A--AAA-OOOOOOOOROO-----AAAAAAAAAAAAAAFF@@@VVVV@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@V@@@AAAAAAAAAAAADDDDAAAAFF#
+D:#^^^----AAAA-R-##-#ss--OOO#OO-##F-#Ss----OOOA-O,F#AAA---AAAAA-------AA-O--------F---OOO#OOOOO--AAAAFFFAAAAAAAAAAAAAAF@@VVVV@@AAAAAAAAAAAAAAAAAAAA@@@@@AAAAAAAAAA@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^^---------##---ss-s--OOOO----Fsst#DD-OOOOOO--#F-----F------ssss-s--OR---A--AAAA---OOOOO-A--AAFFAAAAAAAAAAAAAAAAAFFF@@VVVV@@@AAAAAAAAAAAAAAFF@@@VVV@@@AAAAAAAA@@@V@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^^--------##R---sstss#--OOO-##FssS-#--OOO#OO--##F----AAAA-----#S--RR-OA-----AAAAFF----------AAAAAAAAAAAAAAAAAAAAAAAAAF@@@VVVV@@@@@AAAAAAAAF@@@@VVVVVVV@@FFF@@@@@V@@V@@@V@@AAAAAAAAAAAAAAAAAAAAAAAAFF#
+D:#^^-AAAA-###-----#Ss-R--OOOOO###sSs#--OOOOOO--##-----AAAAFF---ssssss--O-----AAAAAAAAAAA-AA-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@VVVVVV@@@@@@@@@@@VVVVV@@@VVV@@@@@VVVV@@@@F@@@@AAAAAAAAAAAAAAAAAAAAAAAAFFF#
+D:#^^^######----#---#s#--OOO-OOO--Ss#--#OAA#---##-#AAAAAAAAAAA--##--##-OO--DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFF@@VVVVVVVVVVVVVVVVV@@@@@F@@@VVVVVVV@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAADDAAAAAF#
+D:#^^^-------ss-#S---#--OOO---AA#--#--OOOOR--#-------AAAAAAAAAA--------O-----AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF@@@@@@@@@@@@@@@@@@@AAAAFFF@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^-sssss-#sssts-----OOO--#--OOO---OOOOO---#AF-AAAAAAAAAAAAAAF------OO-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFF#
+D:#^^-SSStA--#sSAFs#--OOO--#----AOOOOOOAA--#-------AAAAFFAAAFFAAAFAA--O----AAAAADDAAAAAAAAAAAAAAAAAAAAAFFFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^-sFAF----#ss-R--OO----#--AF-OAOOO------------AAAAAAAAFAAAFFAA---O---AAAAAAADDDAAAAAFFFFAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAADDAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAFF#
+D:#^^^#####----##---OO--###--XXX-OOO----#-------AAAAAAAAAAAAAFAAAAFF---AAAAAAAAAAAAAAAAAAAAAAAADDAAAADDDFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDAAAAAFAFFFAAAAAAAAAAAAAAAAFFFFFFFAAAAAAFFFAAAAAAAAAAAAAA#
+D:#^^^------------OOO--###--XXX#----FAFFA----AAAAAAAAADDDDAAFAAAFAAAAAAAAAAAADDDAAAAAAAAADDAAADDAAAAAFDDFAAAAAADDAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDDAAAAAAAAAAAAAFFAFFAAAAAAAAFFFFAAAAAAAAAAFFF#
+D:#^^--SStSSAAA--OO-AAA#--XXAA#---#AFA-AAA-AAAAAAADDDDDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAAFFFFAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAAAAAAAAAAAAAAFFAFFAAAAAAFFFFFADDAAAAADDDAAAAF#
+D:#^^^-sss-R----------#--###-----AF#-----AAAAAAAAADDDDAFAAFAAAFAAAFAAADDDAAAAAAAAAADDAAAAADDDAAAAAAAAAAAAAAAADDDAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAADDADDDAAAAAFFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAA#
+D:#^^--####-------###--------###-#-#---AAAAAAAAAAAAAAAAAAFAAFAAFAAAAAADDDDAAAAAAAAAFFAAADDDAAAAAAFFAFFFFFAADDAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF#
+D:#^^^----------##-----##-#-#-AFF---AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFFAAAAAAAAAAAAAFFFFAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFF#
+D:#^^^------##-#--##-###--#-------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDAAAAAAFFFFFAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF#
+D:#^^######-#-----------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF#
+D:######################################################################################################################################################################################################
diff --git a/lib/edit/t_gondol.txt b/lib/edit/t_gondol.txt
new file mode 100644
index 00000000..779c4fbb
--- /dev/null
+++ b/lib/edit/t_gondol.txt
@@ -0,0 +1,219 @@
+# File: Gondolin.txt
+
+# Gondolin: The Hidden Kingdom of the Noldor
+# Created by Akhronath (zzhou22876@aol.com)
+
+############### Additional default terrain settings ###############
+
+# Default for Quest 13 = entrance is mountain
+F:z:97:3
+
+# Default for Quest 14 = entrance is floor
+F:y:1:3
+
+# Default for Quest 15 = entrance is floor
+F:x:1:3
+
+# Default for Quest 23 = entrance is quest entrance
+F:w:8:3:0:0:0:0:0:23
+
+# Decoration = Straight Road (B)
+F:":66:3
+
+# Decoration = Straight Road (W)
+F:$:70:3
+
+# Rare jewelry shop -- unusable yet, need finish quest
+F:!:63:3
+
+# Between gate to minas anor -- unusable yet, need finish quest
+F:Z:63:3
+
+#################### Quest 13 - Eol the Dark Elf ####################
+
+# Quest 13 assigned, entrance is quest entrance
+?:[EQU $QUEST13 1]
+F:z:8:3:0:0:0:0:0:13
+
+?:1
+
+#################### Quest 14 - Nirnaeth Arnoediad ####################
+
+# Quest 14 assigned, entrance is quest entrance
+?:[EQU $QUEST14 1]
+F:y:8:3:0:0:0:0:0:14
+
+# Quest 14 finished, reward is a rare jewelry shop
+?:[EQU $QUEST14 5]
+F:!:74:3:0:0:0:0:0:42
+
+?:1
+
+#################### Quest 15 - Invasion of Gondolin ####################
+
+# Quest 15 assigned, entrance is quest entrance
+?:[EQU $QUEST15 1]
+F:x:8:3:0:0:0:0:0:15
+
+?:1
+
+#################### Quest 16 - The last Alliance ####################
+
+# Quest 16 finished, reward is a between gate
+?:[EQU $QUEST16 5]
+F:Z:176:3:0:0:0:0:0:1
+
+?:1
+
+############### Quest 23 - Wolves hut finished = house ###############
+?:[EQU $QUEST23 2]
+F:w:74:3:0:0:0:0:0:7
+?:[EQU $QUEST23 5]
+F:w:74:3:0:0:0:0:0:7
+?:1
+
+#################### Buildings ####################
+
+# h: Orange (Minstrel)
+# i: Red (Sorcery)
+# j: Green (Temple)
+# k: Violet (Chaos)
+# l: Dark Brown (Ranger)
+# m: White (Paladin)
+
+# Tower of the King
+F:a:74:3:0:0:0:0:0:27
+
+# Library
+F:b:74:3:0:0:0:0:0:28
+
+# Castle: Gondolin Plot
+F:B:75:3:0:0:0:0:0:4
+
+# The White Tree:Aerandir:High-Elf
+F:c:74:3:0:0:0:0:0:29
+
+# Craftsmaster
+F:d:74:3:0:0:0:0:0:30
+
+# Earth-Dome
+F:e:74:3:0:0:0:0:0:31
+
+# Prophet
+F:f:74:3:0:0:0:0:0:12
+
+# Minstrels Haven
+F:h:74:3:0:0:0:0:0:32
+
+# Star-Dome:Sulraen:High-Elf
+F:i:74:3:0:0:0:0:0:33
+
+# Valarin Temple
+F:j:74:3:0:0:0:0:0:34
+
+# Sea-Dome
+F:k:74:3:0:0:0:0:0:35
+
+# The Golden Flower
+F:l:74:3:0:0:0:0:0:36
+
+# The Fountain
+F:m:74:3:0:0:0:0:0:37
+
+# Thunderlord's Hide
+F:n:74:3:0:0:0:0:0:22
+
+# Merchant guild
+F:o:74:3:0:0:0:0:0:56
+
+# Force elven monsters
+f:ELVEN
+
+# Town Layout
+
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################^^^^^^^^^^^^^,,, ####
+D:####################################^^^^^^^^^^^^,,,, ####
+D:##################################^^^^^^^^,,,,,,,, ####
+D:###########B#####################^^^^^^^^^,,,,,, CCCCCCCCCCCCCCCCCCCCCCCCC ,,,,,,, ,,,,###
+D:################################^^^^^^^^^^^,,, CCCCCCCC#######################CCCCCCCC ,,,,,,,,,,, ,,,,,###
+D:###############################^^^^^^^^^^^^,, CCCCCCC########TTTTTTTT#####TTTTTTTT########CCCCCC ,,,,,^^^^^,,,, ,,,,###
+D:##############################^^^^^^^^^^^,,, CCCC########TTTTTT........###........TTTTTT########CCCC ,,,,^^^^^^^,,,,,, ,,,,,###
+D:#############################^^^^^^^n^^,,, CC###########T..........#########..........T###########CC ,,,,^^^^^^^^^^^^,,,,,,,,,,,###
+D:############################^^^^^^,,,,,, ......................... CC############T........#############........T############CC ,,,,^^^^^^^^^^^^^^,,,,,,,,,###
+D:############################,,,,,,,,, ......................... CC#############T.......###WW#####WW###.......T#############CC ,,,,^^^^^^^^^^^^^,,,,,,,,,###
+D:###########################,,,,,, ... . ... C#############TT.......#WWWW##h##WWWW#.......TT#############C ,,,,,^^^^^^^^^^^,,,,,,,,,###
+D:########################### ... . #### ... #### C#############T..........VV###.###VV..........T#############C ,,,,,,^^^^^^^,,, ,,,,,###
+D:########################## ... ##### ####5#### CC######WWWW###T...........V##...##V...........T####WWWW#####CC ######### ,,,,,,^^^^,,, ,,,,###
+D:########################## ######### ... ##### ####### C######TTTTWW#TT............#.....#............TT#WWTTTT######C ######### ,,,,,,,,,, ,,,,###
+D:####W###################W# ######### ... ##### ####### C#####l...TTW#T.................................T#WTT...T#####C ######### ,,,,,,,, ,,,,,,###
+D:####WWWW#############WWWW# ######### ... ##### ####### C######T...............................................T######C ,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,###
+D:#####WWWWWWW#####WWWWWWW## ######### ... ##### ######### C######TTT.................TTTTTTTTT.................TTT######C , ,,,,,,,,,, ,,,,,,,,,###
+D:#####WWWWWWWWWWWWWWWWWWW## ####w#### ... ##### ######5###### CC#######WW..............TTTT#######TTTT..............WW#######CC , ### ,,,,,,,,,,,,,,,,###
+D:######WWWWWWWWWWWWWWWWW### . ... ##...## C##########............TTT#############TTT............##########C , ########## ,,, ,,,,,,,,###
+D:######WWWWWWWWWWWWWWWWW### ........... ... C########TT...........TT#################TT...........TT########C , ################### ,,,,,###
+D:######WWWWWWWWWWWWWWWWW### ... ... C#####TTTT...........TT###..###aaa###..###TT...........TTTT#####C , ############### ,,,,,,###
+D:########WWWWWWWWWWWWW###### ######### ... ############# ... C#TTTTT..............T##.....#!...Z#.....##T..............TTTTT#C , ############## ,,,,###
+D:###########WWWWWWW########## ######### ... ############# ... C#T...........###...TT##.....#.....#.....##TT...###...........T#C , ############## ,,,###
+D:############################### ######### ... ############# ... C#T.........####....T##......#.....#......##T....####.........T#C , ########### ,,,,###
+D:####--####KK#CC#VV#LL#""## ######### ... ############# ... CC#TT.......####.....T##...................##T.....####.......TT#CC , #### , ## ,,,###
+D:####--####KK#CC#VV#LL#""## ######### ... ######1###### ... C###T......####....TTT###.................###TTT....####......T###C , , VVVVV ,,,,###
+D:####--####KK#CC#VV#LL#""# ... . ... C###T......d###...TT###.###....#####....###.###TT...###b......T###C ,,,,,,,,,,,,,,, VVWWWVV ,,,###
+D:######################### ... . ... C###T......d###...T##.....##..##WWW##..##.....##T...###b......T###C , VVVWWWWWV ,,,,###
+D:#..............................................................................................####...T#.......#..#WV#VW#..#.......#T...####........... , VVVVWWWWWWWV ,,,,,###
+D:#.x.........y.....................................................................................................#W#V#W#..............................,,,, VWWWWWWWWWWV ,,,,###
+D:#..............................................................................................####...T#.......#..#WV#VW#..#.......#T...####........... , VVWWWWWWWVVV ,,,,,###
+D:######################### . ... . . C###T......c###...T##.....##..##WWW##..##.....##T...####......T###C , VWWWWWVVVV ,,,,,^^###
+D:####--####KK#CC#VV#LL#""# . ... . . C###T......####...TT###.###....#####....###.###TT...####....TTT###C , VVWWWVV ,,,,,,^^###
+D:####--####KK#CC#VV#LL#""## ####1#### ... ######2###### ##3## C###T..............TTT###.................###TTT....####...TT#####C , VVWWV ,,,,,,^^^^###
+D:####--####KK#CC#VV#LL#""### ######### ... ############# ##### CC#TT................T##...................##T.....####...TT#####CC , VVVV ,,,,,,^^^^^^###
+D:############################### ########1...... ############# ##### C#T.................T##......#.....#......##T....####....T######C , ------TT------------ ,,,,,^^^^^^^###
+D:###########$$$$$$$########## ######### ... ############# ##### C#T...VV...VV.......TT##.....##...##.....##TT...###.......f#####C ,------TT-TTT--TT-TT-TT---- ,,,,^^^^^^^^^###
+D:########$$$$$$$$$$$$$###### ######### ... ############# ##### C#T..VV#...#VV.......T####.#####m#####.####T..............f#####C -,--TTTTTTTTTTTTTTT-TT-TTT---,,,,^^^^^^^^^###
+D:######$$$$$$$$$$$$$$$$$### ... C#T..V###.###V.......TTT#################TTT.............T######C ---,--TT-TTT########TTT-TTTT----,,,,,^^^^^^^###
+D:######$$$$$$$$$$$$$$$$$### ... C#TT.V##4.j##V.........TTT#############TTT...............TT#####C----,---TTT###TTTTTT###TT-T-TT----,,,,,^^^^^^###
+D:######$$$$$$$$$$$$$$$$$$## ... C##T.V#######V...........TTTT#######TTTT..................TT####C----,--TTT##TTT----TTT##TTTTTT------,,,,,^^^^###
+D:#####$$$$$$$$$$$$$$$$$$$## ######### ... ############# CC#T.VV#####VV.............................................TTT#CC----,---TT#TT--------TT##T-T-TT------,,,,^^^^###
+D:#####$$$$$$$#####$$$$$$$## ######### ... ############# C#T..VV###VV.....TTTTTTTTTTT.......TTTTTTTTTTT..............T#C-----,,,,,,,,,,,,,,,,,,,e#TT-TTT------,,,,^^^^###
+D:####$$$$#############$$$$# ########o.........7############ C#T...VVVVV....TTT#########TTT...TTT#########TTT............T#C----------T#TT--------TT##TTT-------,,,,^^^^^^###
+D:####$###################$# ######### ... ############# C#TT..........TT###VVVVVV####TT.TT####WWWWWW###TT..........TT#C--------TTT##TTT----TTT##TTTTT-----,,,,^^^^^^^###
+D:########################## ######### ... ############# C##T..........T##VVV....VVV###TTT###WWW....WWW##T..........T##C-------TTTTT###TTTTTT###TTTTT-----,,,,,^^^^^^^###
+D:########################### ... CC#T..........T#VV........VV#######WW........WW#T..........T#CC -----TTTT-TTT########TTTTTT------,,,,,,,^^^^^###
+D:########################### #...# C#TT.......................i#####k.......................TT#C -T---TTTTT-TTTTTTTTTT-TT------ ,,,,,,,z^^^###
+D:############################ ##...## C##TT........T#VV........VV#######WW........WW#T........TT##C ------TTTT-TT--TT---T----- ,,,,,,,^^^^^^###
+D:############################ ####6#### CC##TT.......T##VVV....VVV#########WWW....WWW##T.......TT##CC ------TTT-TTTT--T----- ,,,,,,,^^^^^^^^^###
+D:############################# ####### CC##TTTT...TTT###VVVVVV#############WWWWWW###TTT...TTTT##CC ------T-------- ,,,,,,^^^^^^^^^^^^###
+D:############################## ##### CC####TTTTTTTTT###########################TTTTTTTTT####CC ,,,,,,^^^^^^^^^^^^^^###
+D:############################### ##### CCCC######TTTTTTTTTTT##############TTTTTTTTTT######CCCC ,,,,,,^^^^^^^^^^^^^^^^^###
+D:################################ ##### CCCCCC#####################################CCCCCC ,,,,,,,,,,^^^^^^^^^^^^^^^^^###
+D:################################# ##### CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC ,,,,,,,,,,^^^^^^^^^^^^^^^^^^####
+D:################################## ####### ,,,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^####
+D:#################################### ######### ,,,,,,,,,,,,,^^^^^^^^^^^^^^^^^^^######
+D:###################################### ############# ,,,,,,,,,,,,,,,^^^^^^^^^^^^^^^^^^########
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+D:######################################################################################################################################################################################################
+
+# Default starting position
+?:[EQU $LEAVING_QUEST 0]
+P:33:50
+
+# Starting position when coming from quest 19
+?:[EQU $LEAVING_QUEST 19]
+P:51:190
+
+# Starting position when coming from quest 20
+?:[EQU $LEAVING_QUEST 20]
+P:33:13
+
+# Starting position when coming from quest 21
+?:[EQU $LEAVING_QUEST 21]
+P:27:168
+
+# Starting position when coming from quest 22
+?:[EQU $LEAVING_QUEST 22]
+P:6:42
diff --git a/lib/edit/t_info.txt b/lib/edit/t_info.txt
new file mode 100644
index 00000000..7aca3781
--- /dev/null
+++ b/lib/edit/t_info.txt
@@ -0,0 +1,41 @@
+# File: t_info.txt
+
+# Includes the town definitions
+
+# Preferences for the town features
+%:t_pref.txt
+
+# Town Bree
+?:[AND [EQU $TOWN 1] [EQU $TOWN_DESTROY1 1] ]
+%:t_d_bree.txt
+?:[AND [EQU $TOWN 1] [NOT [EQU $TOWN_DESTROY1 1] ] ]
+%:t_bree.txt
+?:1
+
+# Town Gondor
+?:[AND [EQU $TOWN 2] [EQU $TOWN_DESTROY2 1] ]
+%:t_d_gond.txt
+?:[AND [EQU $TOWN 2] [NOT [EQU $TOWN_DESTROY2 1] ] ]
+%:t_gondol.txt
+?:1
+
+# Minas Anor
+?:[AND [EQU $TOWN 3] [EQU $TOWN_DESTROY3 1] ]
+%:t_d_mina.txt
+?:[AND [EQU $TOWN 3] [NOT [EQU $TOWN_DESTROY3 1] ] ]
+%:t_minas.txt
+?:1
+
+# Town Lothlorien
+?:[AND [EQU $TOWN 4] [EQU $TOWN_DESTROY4 1] ]
+%:t_d_lori.txt
+?:[AND [EQU $TOWN 4] [NOT [EQU $TOWN_DESTROY4 1] ] ]
+%:t_lorien.txt
+?:1
+
+# Khazad-Dum
+?:[AND [EQU $TOWN 5] [EQU $TOWN_DESTROY5 1] ]
+%:t_d_khaz.txt
+?:[AND [EQU $TOWN 5] [NOT [EQU $TOWN_DESTROY5 1] ] ]
+%:t_khazad.txt
+?:1
diff --git a/lib/edit/t_khazad.txt b/lib/edit/t_khazad.txt
new file mode 100644
index 00000000..0139fa40
--- /dev/null
+++ b/lib/edit/t_khazad.txt
@@ -0,0 +1,105 @@
+# Town Name: Khazad-Dum
+# by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+F:o:207:3
+
+###################### Buildings ########################
+
+# Fighters Hall
+F:f:74:3:0:0:0:0:0:17
+
+# Paladins Guild
+F:g:74:3:0:0:0:0:0:20
+
+# Inner Temple
+F:h:74:3:0:0:0:0:0:19
+
+# Mining Supplies
+F:i:74:3:0:0:0:0:0:59
+
+# Default for Quest 25 = entrance is quest entrance
+F:w:8:3:0:0:0:0:0:25
+
+# Force dwarven monsters
+f:DWARVEN
+
+############### Quest 25 - Evil cave finished = house ###############
+?:[EQU $QUEST25 2]
+F:w:74:3:0:0:0:0:0:7
+?:[EQU $QUEST25 5]
+F:w:74:3:0:0:0:0:0:7
+?:1
+
+# Town Layout
+D:######################################################################################################################################################################################################
+D:#ooooooooooooo####^^^^^^^^^#######^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#####oooo#######oo######^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^##########oooooooooo CCCCCCC #
+D:#ooo##ooooooooo#####^^^^^##########^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^########ooo########ooo##########^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^################oooooooooooo CCCCCCCC #
+D:##oo###oo####oo##############################^^^^^^^^^^^^^^^^^########################oooo#########oo#### ###########^^^^^^^^^^^^^^^^^^^^^^^^####### #######oooo########## CCCCCCCC #
+D:##############o#########oo###oo###################^^^^^^^############################oooo#########oo###### ################################# #####ooooooooo############# CCCCCCCC ; #
+D:#^^^^########oooooooooooooo##ooooooooo############################# #################oooo########oo########### ########################## ######ooooooooo################ CCCCCCCC ; #
+D:#############ooooooooooooooooooooooooooo###### ###########1###### ##############2####oooo#######o##o##3####f##### ##g####4####h###### ###########ooo######################## CCCCCCCC ; #
+D:#^#############ooooo##################oo### # #### # ##### #### ############### ###ooo########ooo## ## ###### # ## ## ## # #######ooooo########################### CCCCCCCC #
+D:#^^################oo#################ooooo#### ## ; ;; o ; o ## # ooooooooo############################ CCCCCCCC #
+D:#^^^^################o##########o#####oooooooooooooooooooooo o o ; oooooooooooooooooooooooo##ooo############################ CCCCCCC #
+D:#^^^^^^################################# ## ; o ; ; o o ; o ##o################################### CCCCCCC#
+D:#^^^^^^################################ ### o ### o ### o ###; o ### ######################################## CCCCCC#
+D:#^^^^^################################# ##### o ##### o ##### o ##### o ##### ######################################## CCCCCC#
+D:#^^^^^^^################################ #######;; o ####### o ####### o ####### o ####### ####################################### ; CCCCCC#
+D:#^^^^^^^^################################ # ####### o ; ####### o ####### o ####### o ####### ######################################## CCCCCC #
+D:#^^^^^^^################################### ##### o ; ;##### o ##### o ##### ; o ##### ;####################################### CCCCCCC #
+D:#^^^^^^^^^################################ ### o ### o ### o ### o ### # #################################### CCCCCC ; #
+D:#^^^^^^^^^################################# o o o o ## ################################## CCCCCC #
+D:#^^^^^^^^^^################################ ; oooooooooooo o o oooooooooooo 5################################ CCCCCC ; #
+D:#^^^^^^^^^^^^^########################### o o o o ################################# CCCCCCC #
+D:#^^^^^^^^^^^^^^^########################## ### o ### o o ;### o ### ################################ CCCCCCC ; #
+D:#^^^^^^^^^^^^############################# ##### ; o ##### o o ##### o ##### ; ###############################; CCCCCCC ; #
+D:#^^^^^^^^^############################### ; ####### o ####### o ; o ####### o ####### ############################ CCCCCCC #
+D:#^######################################### ; ####### oooooooo ####### o o ####### oooooo ####### o ; ##############################; CCCCCCC #
+D:#################ooo###################### ; ##### o ##### o o ##### o ##### ###############oo############## CCCCC; #
+D:####oo##ooo#####o#######o###ooo########### ### ; o ### o o ### o ; ### ; ;; #########oo###oooooo#####oo###### CCCCC ; #
+D:#ooooooooooooooooooooooooooooooo##oooooooo ; o ; o o o ; ;; ; oooooooooooo##oooooooo##oooooooooo CCCCC ; #
+D:#ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo CCCCCC #
+D:#oooooooooooooooooo##oooooo##oooooo##ooooo ; o o ; ooooooooo#oooooooooooooooooooooooo CCCCC #
+D:#^#######################oo####ooooo####### ### ### o o ###; ### ######o####oo####oo######oo######; CCCCC #
+D:#^^#######################o########### ##### ##### o o ##### ##### ######o####################oo#### CCCCC #
+D:#^####################################### #######;; ; ####### o o ####### ####### ####oo######################### CCCCCC #
+D:#^^######################################## ####### ; ####### o o ####### ####### ######o############oo########## CCCCCCC #
+D:#^^^###############ooo################### ##### ; ;##### o o ##### ; ##### ;; ######oo########ooooo####### CCCCCCCC #
+D:#####oo##oo#####oooooooo##############oo## ### ; ### o o ### ### ########oooo##oooooo####### CCCCCCCCC ; #
+D:#ooooooooooo###ooooooooooooooooooooooooooo o o ooooooooooooooooooooooooooo CCCCCCCC #
+D:#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo CCCCCCCC ; #
+D:#^ooooooooooooooooo###oooooooooo##oooooooo o o o o ooooooo####ooooooooooooo CCCCCCCC #
+D:#^^#o###ooo#####ooo#####ooooo########oo# ### ; o ### o o ;### o ### oo#########ooooo###oo# CCCCCCCC ; #
+D:#^^^^^###o################oo############# ##### ; o ##### o o ##### o ##### ; ###########oo######## CCCCCCCC ; #
+D:#^^^^^^^################################## ; ####### ooooooo ####### o ; o ####### ooooo ####### ######################## CCCCCCCCC #
+D:#^^^^^^^^^^############################## ; ####### o ; ####### o o ####### o ####### o ; ########################## CCCCCCCC #
+D:#^^^^^^^^^^^^^^^######################### ; ##### o ##### o o ##### o ##### ############################ CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^################### ### ; o ### o o ### o ; ### ; ;; ############################ CCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^#################### ; o ;; o ; o o ; ############################ CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^###################### ooooooooooo o o oooooooooooo ;; ; 7########################### CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^^^^################# ; o ; ; o o ; o ############################# CCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^############## ### o ### o ### o ###; o ### ############################# CCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^############ ##### o ##### o ##### o ##### o ##### ######################## CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^########## #######;; ; o ####### o ####### o ####### o ####### ########################### CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^#########i ####### o ; ####### o ####### o ####### o ####### ############################ CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^########### ##### o ; ;##### o ##### o ##### ; o ##### ; ############################## CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^############## ### ; o ### o ### o ### o ### ############################## CCCCCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^^^^################# o o o o #########oo###oo################# CCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^#############o#######oooooooooooooooooooooo ## o o oooooooooooooooooooooooooooooooooooo################## CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^############ooooooooooo# ### ## ######## o o ## o ## ## oooooo##oooooooooo################### CCCCCCC #
+D:#^^^^^^^^^^^#################ooooooooooo#### ###### ### #### ############# ####oooo###### #ooo# ### ###### # ### ## ##### ####################ooo##################### CCCCCCC #
+D:#^^^^^^^^^^###################oo######oo### ########### #######9####################oooo########oooo############w### ######## ###### ####################oo###################### CCCCCCC #
+D:#^^^^^^^##########oo####oo####ooo######### ############# ##############^^^##########ooooo########oo###################################### #########^######oooo##ooo################## CCCCCCC #
+D:##^^^^^#########oooooooooooooooo#################^^####### ###########^^^^^###########ooooo######ooo####################################### #####^^^#######ooooooooooooo############# ; CCCCCCC#
+D:#^^^##########ooooooooooo##ooooo######^#########^^^^##################^^^^^^^^^^^######ooo#######oooo#########^^^^^^^^^^^^^^^^^^^########### #####^^^#######oo##oooooooo############# CCCCCC#
+D:##############ooooo##########oo######^^^#####^^^^^^^^###############^^^^^^^^^^^^^^#####oooo#######ooo######^^^^^^^^^^^^^^^^^^^^^^^^^^^^###### ####^^^^^###############ooo############## CCCCC#
+D:########ooo#####oo##################^^^^^^^^^^^^^^^^^#############^^^^^^^^^^^^^^^^^####oo########oooo#####^^^^^^^^^^^^^^^^^^^^^^^^^^^########### #######^^^^^#############ooo#ooo######## CCCCCC#
+D:#o##oooooooooooooooo##############^^^^^^^^^^^^^^^^^^^^^^#########^^^^^^^^^^^^^^^^^^#####oo########oo######^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####### # ######^^^^^^^^^^^^########oooooooooooooo CCCCC #
+D:#oooooooooooooooooo########^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^########oooo#######ooooo###^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^##############^^^^^^^^^^^^^^^#######ooooooooooo CCCCC #
+D:######################################################################################################################################################################################################
+
+# Default starting position
+# ?:[EQU $LEAVING_QUES 0]
+# P:31:32
diff --git a/lib/edit/t_lorien.txt b/lib/edit/t_lorien.txt
new file mode 100644
index 00000000..9c8c8ad3
--- /dev/null
+++ b/lib/edit/t_lorien.txt
@@ -0,0 +1,162 @@
+# Town Name: Lothlorien
+# by Akhronath (zzhou22876@aol.com)
+#
+# Created for PernAngband
+
+# Default for Mage/Fireproof Quest = entrance is tree
+F:z:96:3
+
+# Default for Quest 10 = entrance is tree
+F:y:96:3
+
+# Default for Quest 11 = entrance is tree
+F:x:96:3
+
+# Default for entrance to the Void, entrance is dirt
+F:v:88:3
+
+# Default for Quest 22 = entrance is quest entrance
+F:w:8:3:0:0:0:0:0:22
+
+############### Quest 22 - Wolves hut finished = house ###############
+?:[EQU $QUEST22 2]
+F:w:74:3:0:0:0:0:0:7
+?:[EQU $QUEST22 5]
+F:w:74:3:0:0:0:0:0:7
+?:1
+
+############### Entrance to the Void ###############
+?:[EQU $QUEST20 1]
+F:v:7:3:0:0:0:0:0:11
+?:1
+
+############### Quest 10 - Spiders of Mirkwood ###############
+# Quest 10 taken, entrance is quest entrance
+?:[EQU $QUEST10 1]
+F:y:8:3:0:0:0:0:0:10
+?:1
+
+############### Quest - Mage/Fireroof quest ###############
+# Mage/Fireproof Quest taken, entrance is quest entrance
+?:[EQU $QUEST"Old Mages quest" 1]
+F:z:8:3:0:0:0:0:0:"Old Mages quest"
+?:1
+
+###################### Buildings ########################
+
+# The Mirror
+F:a:74:3:0:0:0:0:0:23
+
+# Castle: Plot Lorien
+F:B:75:3:0:0:0:0:0:2
+
+# Seat of Ruling
+F:b:74:3:0:0:0:0:0:24
+
+# Inn
+F:c:74:3:0:0:0:0:0:11
+
+# Beastmaster Shanty
+F:d:74:3:0:0:0:0:0:16
+
+# Fighters Hall
+F:f:74:3:0:0:0:0:0:17
+
+# Wizards Spire
+F:g:74:3:0:0:0:0:0:25
+
+# Priests Circle
+F:h:74:3:0:0:0:0:0:26
+
+# Rangers Guild allows Ranger
+F:i:74:3:0:0:0:0:0:21
+
+# Nest
+F:j:74:3:0:0:0:0:0:22
+
+# Altars
+F:k:161:3
+F:l:162:3
+F:m:163:3
+F:n:165:3
+
+# Force elven monsters
+f:ELVEN
+
+# Town Layout
+D:######################################################################################################################################################################################################
+D:# , , , . , , ,, , , , #
+D:# , , . , #
+D:# , , . #
+D:# , , ,, , . , , , #
+D:# ,, , , , ,, , , , , , #
+D:# , , , , , , , , #
+D:# , , , ,x , , #
+D:# , , , , #
+D:# ,, , , , ,, , , , #
+D:# , , , , #
+D:# , , , , , , ,, , , , #
+D:# , , , , #
+D:# , , z,,,, ## , ## #
+D:# , , , #### , #### , #
+D:# , , , ### ###1,,,,,h### #
+D:# , , ### , #####, ####, , #### , , , #
+D:# ,, , ##### , ##### ## , ## , ,, , , , , , #
+D:# , , , ##### , ,##.## ### ## , ## , , #
+D:# , , # #, ##### , ##.## ######### , #### , , #
+D:# ,, , ### ### #g# , . ########c,,,,,4### ,, , , , #
+D:# , #### , #### , , , ,, ######### , #### , , , #
+D:# ,, , ############## ,,,,, , #9# ## , ## , ,, , , , , , #
+D:# , , , ############## , , , , , ## , , #
+D:# , , ###########j,,,,,,,,,,,,,, , , , #### , , #
+D:# , , #####B######## , ,,,,,,,,,,,,,,,,,,,,,,,,d### y , #
+D:# , , ############## , , , #### ,, , , ,, , , , #
+D:# , ,#### #### , , ## , , #
+D:# , , ### ### #6# ### ### , , ### ### #
+D:# , , # #, ##### ### ### , ,,, ########## , , #
+D:# ##### #7# #i# ,,,,,,,, ########## , , , #
+D:# ,, , ### , , , , , , #3# ### , , , #
+D:# , , , , ,, , , , , ## , #
+D:# , , ,,, ,,,,,,,,,,,,, ,,, , , , , #### ,, , , , #
+D:# , , , ,,,, ,,,,b,,,,,,,,,,,,,,,,,,,w### , #
+D:# , , v, ,,, , #### #
+D:# , , --,-- ,, , VVV , ## , , , #
+D:# ,, , l----,----m , VWWWV #2# ,, , , , , , #
+D:# , , , ---VV,VV--- , VWWVV , ##### , #
+D:# , , ---VVW,WVV--- , VVVV ##### , #
+D:# , , , ---VWWaWVV--- ,, ### , ,, , , , #
+D:# ---VVWWWVV--- ,,, , , , #
+D:# ,, , ---VVVVV--- ,,,,,,,,,,,,,,, , ,, , , , #
+D:# n---------k , , , , , #
+D:# , , --- , #5# , #
+D:# , , , ### #
+D:# , , , , ### , , , #
+D:# ,, , #0# , ,, , , , , , #
+D:# , , , , , ##### , , #
+D:# , , , ##### , , #
+D:# ### , , , #
+D:# ,, , , ,, , , , #
+D:# , , , #
+D:# , , , , , ,, , , , #
+D:# , , ,, , , , , #
+D:# ,, , , ,, , , , , , #
+D:# , , , , , , , #
+D:# , , , , , #
+D:# , , , #
+D:# ,, , , ,, , , , #
+D:# , , , #
+D:# , , #
+D:# , , ,, , , , , #
+D:# ,, , , ,, , , , , , #
+D:# #
+D:######################################################################################################################################################################################################
+
+# Default starting position
+?:[EQU $LEAVING_QUES 0]
+P:13:99
+
+# Starting position when coming from quest 12
+?:[EQU $LEAVING_QUES 12]
+P:26:109
+
+
diff --git a/lib/edit/t_minas.txt b/lib/edit/t_minas.txt
new file mode 100644
index 00000000..51c74da3
--- /dev/null
+++ b/lib/edit/t_minas.txt
@@ -0,0 +1,144 @@
+# File: t_minas.txt
+
+# Minas Anor: The Royal City of Gondor
+# Created by Mynstral (mynstral@thehelm.com)
+
+# Completed: 22/06/01
+
+# Between gate to gondolin -- need to finish the quest
+F:Z:63:3
+
+# Default for Quest 24 = entrance is quest entrance
+F:w:8:3:0:0:0:0:0:24
+
+#################### Quest 16 - The last Alliance ####################
+
+# Quest 16 finished, reward is a between gate
+?:[EQU $QUEST16 5]
+F:Z:176:3:0:0:0:0:0:0
+
+?:1
+
+############### Quest 24 - Haunted House finished = house ###############
+?:[EQU $QUEST24 2]
+F:w:74:3:0:0:0:0:0:7
+?:[EQU $QUEST24 5]
+F:w:74:3:0:0:0:0:0:7
+?:1
+
+#################### Buildings ####################
+
+# Library
+F:a:74:3:0:0:0:0:0:60
+
+# Castle
+F:b:74:3:0:0:0:0:0:14
+
+# Casino
+F:d:74:3:0:0:0:0:0:15
+
+# Inn
+F:e:74:3:0:0:0:0:0:11
+
+# Beastmaster Shanty
+F:f:74:3:0:0:0:0:0:16
+
+# Fighters hall
+F:g:74:3:0:0:0:0:0:17
+
+# Tower of Magery
+F:h:74:3:0:0:0:0:0:18
+
+# Inner temple
+F:i:74:3:0:0:0:0:0:19
+
+# Paladin guild
+F:j:74:3:0:0:0:0:0:20
+
+# Ranger guild
+F:k:74:3:0:0:0:0:0:21
+
+# Thunderlord's Hide
+F:l:74:3:0:0:0:0:0:22
+
+# Castle: Plot Minas Anor
+F:B:75:3:0:0:0:0:0:5
+
+# Merchant guild
+F:m:74:3:0:0:0:0:0:56
+
+# Library Quest
+F:x:63:3
+
+?:[EQU $QUEST"Library quest" 1]
+F:x:8:3:0:0:0:0:0:"Library quest"
+?:1
+
+############### Town Layout ###############
+
+D:######################################################################################################################################################################################################
+D:#^^########------------------ @@@@@@@ @@@@@@@@@ #
+D:#^^^------############---------- ^ @@VVVVV@@ @@VVVVVVV@@@@@@@ ,,, #
+D:#^^^----------###----#######------- ^^^^^ @VVVVVVV@@@@@ @@VV@@@@@@VVVVVV@@@@ ,, #
+D:#^^----ssss-----###--------####------ ^^^^^^ @VVVVVV@@VVV@@@ @VV@@ @@@@@@VVVV@@ ,, #
+D:#^^^---StSS-------###--#ssss--###------- ^^^^^^^^ @@VVVVVVVV@VVV@@@@@@@@ @@@V@@ @@@@VV@@@@ , #
+D:#^^----ssss----OO---##--#StSS---####------ ^^^^^^^^ @@V@V,@@@@@@VVVVVVVV@@@VVV@ @@VVVV@@ ,, #
+D:#^^----x#a#-----OOO--##--#sssss----###------ ^^^^^^^^ @@@@@ @@@@@@@@VVVVV@@@ @@@@VV@@@ ,,, #
+D:#^ ---------------OO--###-#m#7#------###----- ^^^^^^^^^^ @VVV@@ @@VVV@@ ,O, #
+D:#^ StSSSS-----ss---OO---##-----OOOOO---###---- ^^^^^^^^^^^ @@@ @@VVV@@ OO #
+D:#^^ssssss----Ssss---OOO--##---OOOOOOOO---##---- ^l^^^^^^^ @@VVV@ OO #
+D:#^ ####9#---sstSss---OOO--##-OOOOOOOOOOO--##---- ^^^^^ @@VVV@@ OO #
+D:#^^^-------##sssSss---OOO--#OOO--s--OOOOO--###--- @@@@@@@@@@ @@VVV@@ OO #
+D:#^^^######---##ssh--s--OOO-OOO--StS--OOOOO---##--- @@VVVVVVVV@@@@@@@V@@@@ OO #
+D:#^^^^----###---##--ssS--OOOOO#--ssss--OOOOOO--##--- @VVVVVVVVVVVV@@VV@@ OOO OO #
+D:#^^--------###----ssSs#--OOO-##-#####--OOOOOO--##--- ---- @VVVV@@@@VVVVVVV@@ OOOOO OOOOO #
+D:#^ ----------##--#stsi--OOOO--#---------OOOOOO--#------ -------- @@VVV@@ @@VVV@@@@ OO OO OO #
+D:#^^-----------###-#s#--OOOOOO-##-#sssss--OOOOOO,#####--- ----------- @@VVV@@ @@@@@ OO- OOOOOOOOO #
+D:#^^-------------##-#--OOO-OOO--#--ssssss--OOOOO,,,,,#---- ---ssssssss--- @VVV@@ -OO #
+D:#^^--------------#---OOO-t-OOO-##-#SStSS--OOOOOO,##,#---------ssssssss---- @VV@@ --OO- #
+D:#^^^--#----------##-OOO-sssOOO--#--ssssss-OOOOOO--#,#####-----SStSSSSS----- @@VV@ --OO- #
+D:#^^^--#-----------#OOO-##4##OOO-##-ssssss--OOOOOO-#,,######---ssssssss--O--- @@VVV@ -OO-- #
+D:#^^--###----------OOO-------OOO--#-####2#--OOOOOO-##,#k#,,##--ssssssss--O---- @VVV@@ --O-- #
+D:#^^^-###---------OOO#--SSStS-OOO-#---------OOOOO---#,,,,,,,#--ssssssss--O----- @@VV@@ --OO- #
+D:#^^-#####-------OOO-#--sssss-OOO-#--ssss--OOOOO--T-#,-----,#--#####d##--O------ @VVV@ --OO-- #
+D:#^^#######------OOO-##-###j#-OOO-##-ssss--OOOOO-TT-#------,#-------,,,,,O------ @@VV@@ --OO-- #
+D:#^^^########----OOO--#-------OOO--#-StSS--OOOOO-TT-#-----,,#------------O------- @VVV@@ --OO-- #
+D:#^^############-OOO--##-StSSS-OOO-#-ssss--OOOOO--T-#----,,##---ssssssss-O-------- @@VV@@ --OOO-- #
+D:#^^^#########---OOO---#-sssss-OOO-#-ssss--OOOOOO---#,,,,,##----SSSSStSS-O--------- @@@VVV@ --OOO-- #
+D:#^^#####B###----OOO---#-###6#-OOO-#-##g#---OOOOOO--#######-----ssssssss-O---------- @VVVV@@ ---OOO-- #
+D:#^^^#######-----OOO---#-------OOO-#---------OOOOOOOOOOOOOOOOO--###e####-O----------- @@VVV@@ ------OO-- #
+D:#^^#######bOOOOOOOO-^^^^^^^^^^MMM^^^^^^^^^^^^OOOOOOOOOOOOOOOOOOOOOOOOOOOO----------- @@@VVV@@ ---OOOOOO-- #
+D:#^^#######bOOOOOOO############III############^OOOOOOOOOOOOOOOOOOOOOOOOOOOOO--------- @@VVVV@@ ---OOOOOO-- #
+D:#^ #######bOOOOOOO############III############^OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO--------- @VVVV@@ ----OOOOOO---- #
+D:#^ #######bOOOOOOOO-^^^^^^^^^^MMM^^^^^^^^^^^^OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO------- @VVV@@ ------OOOO------ #
+D:#^^^######Z-----OOO---#-sSss--OOO-#---------OOOOOOOOOOOOOOOOO-----------OOOOOOO------ ---------##@VVV@## --------OOOOO----- #
+D:#^^#########----OOO---#-sSss--OOO-#-sssss--OOOOOO--#######--------sssss-O-OOOOO----- --OOOO-----#######--------OOOOOO----- #
+D:#^^##########---OOO---#-stss--OOO-#-SStSS-OOOOOO---####,,##-------SStSS-O-OOOOO---- --OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO----- #
+D:#^^############-OOO--##-sSss--OOO-#-sssss-OOOOO--T-##k#,,,##------sssss-O--OOOOO--- --OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO----- #
+D:#^^#########----OOO--#--####-OOO--#-sssss-OOOOO-TT-#,,,,-,,#------sssss-O---OOOO--- --OOOOO-OOOOOOOOOOOOOOOOOOOOOOO------- #
+D:#^^#######------OOO-##-------OOO-##-###3#-OOOOO-TT-#,-----,#------###0#-O---OOOOO--- --OOOO---------#######--------------- #
+D:#^^-#####-------OOO-#--Ssss--OOO-#--------OOOOO--T-#,-----,#------------O----OOOO---- --OOOO---------##@VVV@##----------- #
+D:#^^^-###---------OOO#-#stss--OOO-#-ssssss--OOOOO---#,----,,#----ssssss--O-----OOOO---- ---OOOO--- @VVV@@ #
+D:#^^--###----------OOO--#sSs-OOO--#-StSSSS--OOOOOO-##,-,,,,##----SStSSS--O------OOOO---- --OOOOO---- @VVV@@ #
+D:#^^---#-----------#OOO--###-OOO-##-ssssss--OOOOOO-#,,,,####-----ssssss--O-------OOOO---- --OOOOO---- @VVVV@ #
+D:#^^^--#----------##-OOO----OOO--#--###5##-OOOOOO--#,#####-------ssssss-OO--------OOOO--------OOOOO---- @@VVV@@@ @@@@ #
+D:#^^^-------------#---OOO---OOO-##---------OOOOOO,##,#-----------####1#-O----------OOOO----OOOOOO----- @@VVVV@@@ @@@@@@ #
+D:#^^-------------##----OOO-OOO--#--ss------OOOOO,,,,,#------------------O-----------OOOOOOOOOOO----- @@@VVVV@@ @@@@@@@@@V@@@ #
+D:#^^^----------###--ss--OOOOOO-##-ssSs----OOOOOO,#####------------------O-------- ---OOOOOOOOO--- @@VVVV@@ @@@@@ @@@@@@@@@@@@@ #
+D:#^^^---------##---ssSs--OOOO--#--ssts#--OOOOOO--#------ ------ssssss--OO------ ---OOOOO----- @@VVVV@@@ @@@VVV@@@ @@@V@@@@@@@@ #
+D:#^^--------###---sstss#--OOO-##-ssSs#--OOOOOO--##----- ----StSSSS--O------ ----------- @@@VVVV@@@@@ @@@@VVVVVVV@@ @@@@@V@@V@@@V@@ #
+D:#^^------###-----#Sss#--OOOOO#--sSsf--OOOOOO--##----- ---ssssss--O----- ------- @@@VVVVVV@@@@@@@@@@@VVVVV@@@VVV@@@@@VVVV@@@@ @@@@ #
+D:#^^^######---ss---#s#--OOO-OOO--Ss#--OOOOO---##----- --###w##-OO----- @@VVVVVVVVVVVVVVVVV@@@@@ @@@VVVVVVV@@@@ #
+D:#^^^-------ssssS---#--OOO--#OOO--#--OOOOO--###----- --------O----- @@@@@@@@@@@@@@@@@@@ @@@@@@@@@ #
+D:#^^-sssss-#ssstss----OOO--##-OOO---OOOOO--##------ ------OO---- #
+D:#^^-SSStS--#sSsss#--OOO--##---OOOOOOOO---##------ ----O---- #
+D:#^^-sssss---#ss##--OO---##--X--OOOOO---###------ ---O--- #
+D:#^^^#####----##---OO--###--XXX-OOO---###------ --- #
+D:#^^^------------OOO--##---XXX#-----###----- #
+D:#^^--SStSS-----OO---##--XX###---####----- #
+D:#^^^-sssss--------###--###----###------ #
+D:#^^--#####------###--------####------ #
+D:#^^^----------###----#######------ #
+D:#^^^------############---------- #
+D:#^^########----------------- #
+D:######################################################################################################################################################################################################
diff --git a/lib/edit/t_pref.txt b/lib/edit/t_pref.txt
new file mode 100644
index 00000000..84ff947d
--- /dev/null
+++ b/lib/edit/t_pref.txt
@@ -0,0 +1,111 @@
+# File: t_pref.txt
+
+# Defines the preferences for the town features
+# letter:feature:cave_info:monster:object:ego:artifact:trap:special
+
+# Barrow-Downs entrance
+F:{:7:3:0:0:0:0:0:4
+
+# Mirkwood Forest entrance
+F:~:7:3:0:0:0:0:0:1
+
+# Land of Mordor entrance
+F:|:7:3:0:0:0:0:0:2
+
+# Angband Dungeon entrance
+F:>:7:3:0:0:0:0:0:3
+
+# Mountain chain
+F:^:97:3
+
+# Floor
+F:.:1:3
+
+# Trees
+F:T:96:3
+
+# Deep water
+F:W:187:3
+
+# Shallow water
+F:V:84:3
+
+# Deep Lava
+F:L:85:3
+
+# Shallow Lava
+F:K:86:3
+
+# Chasm
+F:C:87:3
+
+# Dirt
+F:,:88:3
+
+# Mud
+F:@:94:3
+
+# Rubble
+F:;:49:3
+
+# Grass
+F:-:89:3
+
+# Permanent wall
+F:#:63:3
+
+# Brick Roof
+F:s:193:3
+# Brick Roof Top
+F:S:194:3
+# Brick Roof Chimney
+F:t:195:3
+
+# Grass Roof
+F:X:190:3
+# Grass Roof Top
+F:U:191:3
+# Grass Roof Chimney
+F:Y:192:3
+
+# Cobblestone Road
+F:O:200:3
+
+# General Store
+F:1:74:3:0:0:0:0:0:0
+
+# Armoury
+F:2:74:3:0:0:0:0:0:1
+
+# Weapons Smith
+F:3:74:3:0:0:0:0:0:2
+
+# Temple
+F:4:74:3:0:0:0:0:0:3
+
+# Alchemy Shop
+F:5:74:3:0:0:0:0:0:4
+
+# Magic Shop
+F:6:74:3:0:0:0:0:0:5
+
+# Black Market
+F:7:74:3:0:0:0:0:0:6
+
+# Home
+F:8:74:3:0:0:0:0:0:7
+
+# Bookstore
+F:9:74:3:0:0:0:0:0:8
+
+# Pet Shop
+F:0:74:3:0:0:0:0:0:9
+
+# Underground Tunnels -- used for tunnels in towns
+F:I:173:3
+
+# Underground Tunnels -- used for tunnels in towns
+F:M:204:3
+
+# Fake blank feature
+F: :0:3
diff --git a/lib/edit/thieves.map b/lib/edit/thieves.map
new file mode 100644
index 00000000..9c42e130
--- /dev/null
+++ b/lib/edit/thieves.map
@@ -0,0 +1,70 @@
+# Floor
+F:.:1:6
+
+# Dark floor
+F:,:1:4
+
+# Permanent wall
+F:X:61:4
+
+# Lit permanent wall
+F:x:61:6
+
+# Magically looked door
+F:M:38:22
+
+# Looked door
+F:D:38:6
+
+# Open door
+F:d:4:6
+
+# up staircase
+F:<:6:8
+
+# Floor with Novice rogue
+F:a:1:6:44
+
+# Floor with Bandit
+F:b:1:6:150:43:*
+
+# Floor with novice warrior
+F:c:1:6:43
+
+# Floor with novice mage
+F:e:1:6:46
+
+# Dark floor with novice warrior
+F:f:1:4:43
+
+# Floor with human skeleton
+F:z:1:6:0:395
+
+# Dungeon layout
+D:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+D:x.....x....zx.....x.............x
+D:x.....x.....x.....x.............x
+D:x.....x.....x.....x.............x
+D:xxxMxxxxxdxxxxxdxxxxxxxxxDxxxxxxx
+D:XXx..........................xXXX
+D:XXx.xxxxx.xxxxx.xxxxxxxxx.xx,xXXX
+D:XXxxxXXXxxxXXXxxxXXXXXXXxxx,,,XXX
+D:XXXXxxxxxxxxxxxxxxxxxXXXXX,,,,,XX
+D:XXXXxe..xb..x...x...xXXXX,,,,,,,X
+D:XXXXx...x...xa..x...xXXXX,,,,,,,X
+D:XXXXx...x...x...x.a.xXXXX,,,,,,,X
+D:xxxxxxDxxxDxxxDxxxDxxXXXXX,,,,,XX
+D:x,,,x...............xXXXXXX,,,XXX
+D:x,,,x..x.........x..xXXXXXXX,XXXX
+D:x,,,D.....x...x.....D,,,,,,,,XXXX
+D:x,,,x..x.........x..xXXXXXXXXXXXX
+D:x,,,x...............xXXXXXXXXXXXX
+D:x,,,xxDxxxDxxxDxxxDxxXXXXXXXXXXXX
+D:x,,,x...x...x.a.xa..xXXXXXXXXXXXX
+D:x,,,xc..x.a.x...x...xXXXXXXXXXXXX
+D:xf,fx...x...x...x...xXXXXXXXXXXXX
+D:xxxxxxxxxxxxxxxxxxxxxXXXXXXXXXXXX
+
+# Starting position
+P:4:4
+
diff --git a/lib/edit/thrain.map b/lib/edit/thrain.map
new file mode 100644
index 00000000..119d8763
--- /dev/null
+++ b/lib/edit/thrain.map
@@ -0,0 +1,35 @@
+# Floor
+F:.:1:0:0:0:0:0:0:0:61
+
+# Some Nazguls
+F:1:1:0:951:0:0:0:0:0:61
+F:2:1:0:952:0:0:0:0:0:61
+F:o:1:0:866:0:0:0:0:0:61
+
+# Marker
+F:,:172:6:0:0:0:0:0:0:61
+
+# Lit permanent wall
+F:x:61:6
+
+# Door
+F:D:48:0:0:0:0:0:0:0:61
+
+# Floor with Trap
+F:t:1:8:0:0:0:0:*
+
+# Deep lava
+F:l:85:0:0:0:0:0:0:0:61
+
+# Dungeon layout
+D:
+D: xxxxxxxx
+D: xoooo..x
+D: xo2oo..x
+D: xlloo..x
+D: x,loo..D
+D: xlloo..x
+D: xo1oo..x
+D: xoooo..x
+D: xxxxxxxx
+D:
diff --git a/lib/edit/tr_info.txt b/lib/edit/tr_info.txt
new file mode 100644
index 00000000..d6518305
--- /dev/null
+++ b/lib/edit/tr_info.txt
@@ -0,0 +1,817 @@
+# This file comes from Angband64 written by Jurriaan Kalkman
+# and describes the traps items can have
+#
+# byte type; /* this goes into sval */
+# s16b probability; /* probability of existence in 1000 */
+# s16b another; /* does this trap easily combine in 1000 */
+# s16b pvalinc; /* how much does this trap attribute to pval */
+# byte difficulty; /* how difficult to disarm */
+# byte level; /* minimum level - disenchantment trap at 200' is */
+# /* not so nice */
+# byte color;
+# cptr name; /* what name does this trap have */
+#
+# d TERM_DARK |r TERM_RED |D TERM_L_DARK |R TERM_L_RED
+# w TERM_WHITE |g TERM_GREEN|W TERM_L_WHITE|G TERM_L_GREEN
+# s TERM_SLATE |b TERM_BLUE |v TERM_VIOLET |B TERM_L_BLUE
+# o TERM_ORANGE|u TERM_UMBER|y TERM_YELLOW |U TERM_L_UMBER
+#
+# b blue for stat traps
+# w white for teleport traps
+# o orange for dungeon rearrangement traps
+# v violet for summoning traps
+# y yellow for stealing/equipment traps
+# r red for other character affecting traps
+# g green for elemental bolt trap
+# B umber for elemental ball trap
+# R l red for arrow/dagger traps
+# W for compound trap!!!
+# don't use U or you'll get trapped doors that are indistinguishable from untrapped doors!
+#
+# an unknown character is multi-hued!
+#
+# N:type:name
+# I:diff:prob:another:pval:minlevel:damage:color
+# I:diff:prob: :minlevel: :color
+# D:description
+
+V:2.0.0
+
+#
+# stat traps
+#
+
+N:1:Weakness Trap
+I:2:100:5:5:2:0d0:b
+D:A poisoned needle weakens you!
+F:FLOOR | CHEST | DOOR
+
+N:2:Weakness Trap
+I:5:100:5:5:20:0d0:b
+D:A poisoned needle seriously weakens you!
+F:FLOOR | CHEST | DOOR
+
+N:3:Weakness Trap
+I:7:100:5:8:40:0d0:b
+D:A poisoned needle permanently weakens you!
+F:FLOOR | CHEST | DOOR
+
+N:4:Intelligence Trap
+I:2:100:5:2:2:0d0:b
+D:A poisoned needle makes you feel stupid!
+F:FLOOR | CHEST | DOOR
+
+N:5:Intelligence Trap
+I:5:100:5:5:20:0d0:b
+D:A poisoned needle makes you feel very stupid!
+F:FLOOR | CHEST | DOOR
+
+N:6:Intelligence Trap
+I:7:100:5:8:40:0d0:b
+D:A poisoned needle makes you feel permanently stupid!
+F:FLOOR | CHEST | DOOR
+
+N:7:Wisdom Trap
+I:2:100:5:2:2:0d0:b
+D:A poisoned needle makes you feel naive!
+F:FLOOR | CHEST | DOOR
+
+N:8:Wisdom Trap
+I:5:100:5:5:20:0d0:b
+D:A poisoned needle makes you feel very naive!
+F:FLOOR | CHEST | DOOR
+
+N:9:Wisdom Trap
+I:7:100:5:8:40:0d0:b
+D:A poisoned needle makes you feel permanently naive!
+F:FLOOR | CHEST | DOOR
+
+N:10:Fumbling Fingers Trap
+I:2:100:5:2:2:0d0:b
+D:A poisoned needle makes you feel clumsy!
+F:FLOOR | CHEST | DOOR
+
+N:11:Fumbling Fingers Trap
+I:5:100:5:5:20:0d0:b
+D:A poisoned needle makes you feel very clumsy!
+F:FLOOR | CHEST | DOOR
+
+N:12:Fumbling Fingers Trap
+I:7:100:5:8:40:0d0:b
+D:A poisoned needle makes you feel permanently clumsy!
+F:FLOOR | CHEST | DOOR
+
+N:13:Wasting Trap
+I:2:100:5:2:2:0d0:b
+D:A poisoned needle makes you feel sickly!
+F:FLOOR | CHEST | DOOR
+
+N:14:Wasting Trap
+I:5:100:5:5:20:0d0:b
+D:A poisoned needle makes you feel very sickly!
+F:FLOOR | CHEST | DOOR
+
+N:15:Wasting Trap
+I:7:100:5:8:40:0d0:b
+D:A poisoned needle makes you feel permanently sickly!
+F:FLOOR | CHEST | DOOR
+
+N:16:Beauty Trap
+I:2:100:5:2:2:0d0:b
+D:A poisoned needle scars you!
+F:FLOOR | CHEST | DOOR
+
+N:17:Beauty Trap
+I:5:100:5:5:20:0d0:b
+D:A poisoned needle scars you horribly!
+F:FLOOR | CHEST | DOOR
+
+N:18:Beauty Trap
+I:7:100:5:8:40:0d0:b
+D:A poisoned needle scars you permanently!
+F:FLOOR | CHEST | DOOR
+
+#
+# miscellaneous traps
+#
+
+N:20:Trap of Curse Weapon
+I:5:10:0:12:20:0d0:y
+D:Your weapon will never be the same...
+F:FLOOR | CHEST | DOOR
+
+N:21:Trap of Curse Armour
+I:5:15:0:12:20:0d0:y
+D:Your armour doesn't exactly get better by setting off this trap...
+F:FLOOR | CHEST | DOOR
+
+N:22:Earthquake Trap
+I:5:20:0:10:10:0d0:o
+D:The ceiling collapses around you!
+F:FLOOR | CHEST | DOOR
+
+N:23:Poison Needle Trap
+I:1:50:50:3:2:0d0:r
+D:A poisoned needle pricks you!
+F:FLOOR | CHEST | DOOR
+
+N:24:Summon Monster Trap
+I:2:50:40:4:2:0d0:v
+D:Monsters defend the memory of the owner...
+F:FLOOR | CHEST | DOOR
+
+N:25:Summon Undead Trap
+I:4:25:40:6:10:0d0:v
+D:Undead rise from the grave to defend this!
+F:FLOOR | CHEST | DOOR
+
+N:26:Summon Greater Undead Trap
+I:8:10:50:20:20:0d0:v
+D:Greater undead defend this!
+F:FLOOR | CHEST | DOOR
+
+N:27:Teleport Trap
+I:3:100:50:3:2:0d0:w
+D:Now you know why nobody ever got close enough to disarm this trap...
+F:FLOOR | CHEST | DOOR
+
+N:28:Paralysing Trap
+I:1:100:20:2:2:0d0:r
+D:You suddenly cannot move!
+F:FLOOR | CHEST | DOOR
+
+N:29:Explosive Device
+I:3:100:80:0:3:3d8:r
+D:Ha! It explodes before your hands can illegally touch it!
+F:FLOOR | CHEST | DOOR
+
+N:30:Teleport Item Trap
+I:3:50:50:3:5:0d0:w
+D:The item magically disappears from your greedy hands!
+F:FLOOR | CHEST
+
+N:31:Lose Memory Trap
+I:6:30:30:6:10:0d0:r
+D:You suddenly can't remember what you were doing here...
+F:FLOOR | CHEST | DOOR
+
+N:32:Bitter Regret Trap
+I:9:15:20:9:20:0d0:r
+D:You already regret trying this...
+F:FLOOR | CHEST | DOOR
+
+N:33:Bowel Cramps Trap
+I:1:90:20:1:6:0d0:r
+D:Your stomach twists with a sharp pang!
+F:FLOOR | CHEST | DOOR
+
+N:34:Blindness/Confusion Trap
+I:4:100:50:4:6:0d0:r
+D:You suddenly can't see, and thinking is difficult too....
+F:FLOOR | CHEST | DOOR
+
+N:35:Aggravation Trap
+I:2:100:50:2:3:0d0:o
+D:Your hear a high-pitched humming noise...
+F:FLOOR | CHEST | DOOR
+
+N:36:Multiplication Trap
+I:3:90:0:3:5:0d0:o
+D:The floor around you doesn't seem the same...
+F:FLOOR | CHEST | DOOR
+
+N:37:Steal Item Trap
+I:3:100:50:3:6:0d0:y
+D:The chest seems to swell, while your backpack feels lighter..
+F:FLOOR | CHEST
+
+N:38:Summon Fast Quylthulgs Trap
+I:8:50:10:10:25:0d0:v
+D:Parts of the owner seem to return from somewhere else, as you slow in awe.
+F:FLOOR | CHEST | DOOR
+
+N:39:Trap of Sinking
+I:2:50:0:0:3:0d0:w
+D:A trapdoor opens up under you!
+F:FLOOR | DOOR
+
+N:40:Trap of Mana Drain
+I:4:100:50:3:4:0d0:r
+D:You suddenly can't think so clearly any more...
+F:FLOOR | CHEST | DOOR
+
+N:41:Trap of Missing Money
+I:2:100:50:2:2:0d0:y
+D:Money isn't everything, they say...
+F:FLOOR | CHEST | DOOR
+
+N:42:Trap of No Return
+I:5:20:10:4:8:0d0:y
+D:Do stay a while!
+F:FLOOR | CHEST | DOOR
+
+N:43:Trap of Silent Switching
+I:4:100:50:3:6:0d0:y
+D:You suddenly are a different person!
+F:FLOOR | CHEST | DOOR
+
+N:44:Trap of Walls
+I:6:100:50:2:10:0d0:o
+D:The room seems to shrink!
+F:FLOOR | CHEST | DOOR
+
+N:45:Trap of Calling Out
+I:10:100:100:5:15:0d0:v
+D:You hear something coming closer, much closer.
+F:FLOOR | CHEST | DOOR
+
+N:46:Trap of Sliding
+I:8:50:50:4:8:0d0:r
+D:Your feet seem to have a life of their own!
+F:FLOOR | CHEST | DOOR
+
+N:47:Trap of Charges Drain
+I:6:100:70:2:3:0d0:y
+D:You feel as if you've just lost something...
+F:FLOOR | CHEST | DOOR
+
+N:48:Trap of Stair Movement
+I:6:0:50:3:4:0d0:o
+D:The dungeon seems different...
+F:FLOOR | CHEST | DOOR
+
+N:49:Trap of New Trap
+I:5:100:5:0:4:0d0:o
+D:Somehow, disarming isn't over, you feel...
+F:FLOOR | CHEST | DOOR
+
+N:50:Trap of Scatter Items
+I:10:50:50:6:12:0d0:w
+D:You hear crashing sounds from all over the dungeon!
+F:FLOOR | CHEST | DOOR
+
+N:51:Trap of Decay
+I:4:100:50:4:4:0d0:r
+D:Your stomach isn't empty, but suddenly you think of food.
+F:FLOOR | CHEST | DOOR
+
+N:52:Trap of Wasting Wands
+I:6:100:40:4:5:0d0:y
+D:Your wands seem different...
+F:FLOOR | CHEST | DOOR
+
+N:53:Trap of Filling
+I:10:100:0:10:25:0d0:o
+D:The whole room vibrates in a strange way...
+F:FLOOR | CHEST | DOOR
+
+N:54:Trap of Drain Speed
+I:8:50:10:25:80:0d0:y
+D:You suddenly seem to have more time to self-reflect...
+F:FLOOR | CHEST | DOOR
+
+#
+# bolt traps
+#
+
+N:60:Lightning Bolt Trap
+I:2:80:5:3:2:2d8:g
+D:You are jolted with electricity!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:61:Poison Bolt Trap
+I:2:80:5:3:2:2d8:g
+D:A blast of poison gas hits you!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:62:Acid Bolt Trap
+I:2:80:5:3:2:2d8:g
+D:A jet of acid shoots out at you!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:63:Cold Bolt Trap
+I:2:80:5:3:2:2d8:g
+D:You are suddenly very cold!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:64:Fire Bolt Trap
+I:2:80:5:3:2:2d8:g
+D:You are suddenly very hot!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:65:Plasma Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:A bolt of plasma hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:66:Water Bolt Trap
+I:4:80:5:5:8:5d10:g
+D:A gush of water hits you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:67:Light Bolt Trap
+I:4:80:5:5:8:5d10:g
+D:There is a sudden flash of light around you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:68:Dark Bolt Trap
+I:4:80:5:5:8:5d10:g
+D:A bolt of pure elemental darkness hits you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:69:Shards Bolt Trap
+I:6:80:5:6:15:6d10:g
+D:A blast of crystal shards hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:70:Sound Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:A sudden roar of sound hurts your eardrums!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:71:Confusion Bolt Trap
+I:4:80:5:5:8:6d10:g
+D:A blast of confusion gas engulfs you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:72:Force Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:A bolt of pure force hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:73:Inertia Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:Your feet feel like lead!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:74:Mana Bolt Trap
+I:8:80:5:9:25:15d16:g
+D:A bolt of pure magic hits you!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:75:Ice Bolt Trap
+I:4:80:5:5:8:5d10:g
+D:A bolt of ice hits you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:76:Chaos Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:A blast of raw chaos hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:77:Nether Bolt Trap
+I:8:80:5:9:25:15d16:g
+D:A bolt of negative energy hits you!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:78:Disenchantment Bolt Trap
+I:8:80:5:9:25:15d16:g
+D:There is a static feeling in the air...
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:79:Nexus Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:A bolt of nexus hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:80:Time Bolt Trap
+I:8:80:5:9:25:15d16:g
+D:Suddenly, several months pass by in a second!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:81:Gravity Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:Gravity suddenly warps around you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+#
+# ball traps
+#
+
+N:82:Lightning Ball Trap
+I:3:60:5:5:8:3d10:B
+D:A massive electrical charge shoots through you!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:83:Poison Ball Trap
+I:3:60:5:5:8:3d10:B
+D:A large cloud of poison gas envelops you!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:84:Acid Ball Trap
+I:3:60:5:5:8:3d10:B
+D:You are suddenly drenched in acid!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:85:Cold Ball Trap
+I:3:60:5:5:8:3d10:B
+D:A blast of hideously cold air envelops you!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:86:Fire Ball Trap
+I:3:60:5:5:8:3d10:B
+D:You are suddenly in the centre of a raging inferno!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:87:Plasma Ball Trap
+I:8:60:5:8:20:12d18:B
+D:You are engulfed in plasma!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:88:Water Ball Trap
+I:5:60:5:6:15:8d12:B
+D:A whirlpool engulfs you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:89:Light Ball Trap
+I:5:60:5:6:15:8d12:B
+D:A massive flash of light erupts around you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:90:Darkness Ball Trap
+I:5:60:5:6:15:8d12:B
+D:A large patch of darkness erupts around you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:91:Shards Ball Trap
+I:8:60:5:8:20:12d18:B
+D:A violent blast of crystal shards hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:92:Sound Ball Trap
+I:8:60:5:8:20:12d18:B
+D:BOOM! Your eardrums nearly explode!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:93:Confusion Ball Trap
+I:5:60:5:6:15:8d12:B
+D:You are enveloped in a cloud of confusion gas!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:94:Force Ball Trap
+I:8:60:5:8:20:12d18:B
+D:A violent blast of pure force smashes down around you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:95:Inertia Ball Trap
+I:8:60:5:8:20:12d18:B
+D:Suddenly, your entire body feels like lead!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:96:Mana Ball Trap
+I:10:60:5:10:30:16d20:B
+D:You are hit by a blast of pure magic!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:97:Ice Ball Trap
+I:5:60:5:6:15:8d12:B
+D:A massive blast of ice crystals engulfs you!
+F:FLOOR | CHEST | DOOR | LEVEL2
+
+N:98:Chaos Ball Trap
+I:8:60:5:8:20:12d18:B
+D:A violent blast of raw chaos engulfs you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:99:Nether Ball Trap
+I:10:60:5:10:30:16d20:g
+D:A blast of energy from the netherworld engulfs you!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+# N:type:name
+# I:diff:prob:another:pval:minlevel:color
+# D:description
+
+N:100:Disenchantment Ball Trap
+I:10:60:5:10:30:16d20:B
+D:You are hit by a blast of pure anti-magic!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:101:Nexus Ball Trap
+I:8:60:5:8:20:12d18:B
+D:A ball of nexus hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:102:Time Ball Trap
+I:10:60:5:10:30:16d20:B
+D:Suddenly, several years pass by in a second!
+F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:103:Gravity Ball Trap
+I:8:60:5:8:20:12d18:B
+D:You suddenly feel gravity warp violently around you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:110:Arrow Trap
+I:2:100:0:5:2:0d0:R
+D:An arrow shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:111:Bolt Trap
+I:2:100:0:5:5:0d0:R
+D:A bolt shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:112:Seeker Arrow Trap
+I:2:100:0:6:10:0d0:R
+D:A seeker arrow shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:113:Seeker Bolt Trap
+I:2:100:0:6:12:0d0:R
+D:A seeker bolt shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:114:Poison Arrow Trap
+I:2:100:0:5:4:0d0:R
+D:A poisoned arrow shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:115:Poison Bolt Trap
+I:2:100:0:6:6:0d0:R
+D:A poisoned bolt shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:116:Poison Seeker Arrow Trap
+I:2:100:0:7:12:0d0:R
+D:A poisoned seeker arrow shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:117:Poison Seeker Bolt Trap
+I:2:100:0:7:15:0d0:R
+D:A poisoned seeker bolt shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:118:Broken Dagger Trap
+I:2:100:0:5:2:0d0:R
+D:An broken dagger shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:119:Dagger Trap
+I:2:100:0:5:5:0d0:R
+D:A dagger shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:120:Poison Broken Dagger Trap
+I:2:100:0:5:4:0d0:R
+D:A poisoned broken dagger shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+N:121:Poison Dagger Trap
+I:2:100:0:6:6:0d0:R
+D:A poisoned dagger shoots out at you.
+F:FLOOR | CHEST | DOOR
+
+#
+# multiple arrows/daggers traps
+#
+
+N:122:Arrows Trap
+I:4:100:0:7:16:0d0:R
+D:Some arrows shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:123:Bolts Trap
+I:4:100:0:7:18:0d0:R
+D:Some bolts shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:124:Seeker Arrow Trap
+I:5:100:0:8:20:0d0:R
+D:Some seeker arrows shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:125:Seeker Bolt Trap
+I:5:100:0:8:24:0d0:R
+D:Some seeker bolts shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:126:Poison Arrows Trap
+I:5:100:0:8:18:0d0:R
+D:Some poisoned arrows shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:127:Poison Bolt Trap
+I:6:100:0:8:20:0d0:R
+D:Some poisoned bolts shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:128:Poison Seeker Arrows Trap
+I:7:100:0:9:27:0d0:R
+D:Some poisoned seeker arrows shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:129:Poison Seeker Bolts Trap
+I:9:100:0:9:30:0d0:R
+D:Some poisoned seeker bolts shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:130:Broken Daggers Trap
+I:4:100:0:6:12:0d0:R
+D:Some broken daggers shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:131:Dagger Trap
+I:4:100:0:6:15:0d0:R
+D:Some daggers shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:132:Poison Broken Daggers Trap
+I:5:100:0:7:18:0d0:R
+D:Some poisoned broken daggers shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:133:Poison Daggers Trap
+I:6:100:0:7:23:0d0:R
+D:Some poisoned daggers shoot out at you.
+F:FLOOR | CHEST | DOOR
+
+N:140:Trap of Drop Item
+I:3:50:0:2:5:0d0:y
+D:A sudden sound startles you and you drop something!
+F:FLOOR | CHEST | DOOR
+
+N:141:Trap of Drop Items
+I:5:50:0:5:12:0d0:y
+D:A sudden sound startles you and you drop several things!
+F:FLOOR | CHEST | DOOR
+
+N:142:Trap of Drop Everything
+I:8:50:0:8:20:0d0:y
+D:A sudden sound startles you and you drop everything!
+F:FLOOR | CHEST | DOOR
+
+#-SC-
+N:150:Trap of Femininity
+I:4:30:5:0:10:2d8:r
+D:You feel like a new woman!
+F:FLOOR | CHEST | DOOR
+
+N:151:Trap of Masculinity
+I:4:30:5:0:10:2d8:r
+D:You feel like a new man!
+F:FLOOR | CHEST | DOOR
+
+N:152:Trap of Neutrality
+I:4:30:5:0:10:2d8:r
+D:You feel like a new woman... erm, a new man... er, WHAT did you say???
+F:FLOOR | CHEST | DOOR
+
+N:153:Trap of Aging
+I:5:50:5:0:15:1d8:r
+D:You suddenly age very fast!
+F:CHEST | DOOR
+
+N:154:Trap of Growing
+I:3:75:5:0:5:1d8:r
+D:You begin to grow!
+F:FLOOR | CHEST | DOOR
+
+N:155:Trap of Shrinking
+I:3:75:5:0:5:1d8:r
+D:You begin to shrink!
+F:FLOOR | CHEST | DOOR
+
+#N:156: UNUSED
+
+#N:157: UNUSED
+
+N:158:Trap of Divine Anger
+I:6:100:5:0:15:0d0:G
+D:A voice booms out "Have a care, mortal!"
+F:FLOOR | CHEST | DOOR
+
+N:159:Trap of Divine Wrath
+I:9:50:5:0:30:0d0:G
+D:A voice booms out "Sacrilege!"
+F:FLOOR | CHEST | DOOR
+
+N:160:Hallucination Trap
+I:3:100:10:0:4:0d0:r
+D:Your vision is clouded by a blast of kaleidoscopic light!
+F:FLOOR | CHEST | DOOR
+
+# Bolt traps
+N:161:Greater Magic Missile Trap
+I:6:80:5:6:75:25d20:g
+D:A greater magic missile hits you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+#N:162:Foulness Trap
+#I:6:80:5:6:15:10d12:g
+#D:You feel foul!
+#F:FLOOR | CHEST | DOOR | LEVEL3
+
+#N:163:Trap of Death Ray
+#I:8:80:5:9:25:15d16:g
+#D:A Ray of Death hits you!
+#F:FLOOR | CHEST | DOOR | LEVEL4
+
+N:164:Trap of Holy Fire
+I:6:80:5:6:15:10d12:g
+D:Holy fire rises around you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:165:Trap of Hell Fire
+I:6:80:5:6:15:10d12:g
+D:Hellfire rises around you!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:166:Psi Bolt Trap
+I:6:80:5:6:15:10d12:g
+D:Your mind is suddenly blasted!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:167:Psi Drain Trap
+I:6:80:5:6:15:8d10:r
+D:You suddenly can't think clearly any more...
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+# Ball Traps
+
+### this one *ought* to be a Nuke Ball trap, not plasma ball, as trap 87
+### is also plasma ball. I've put the description right in advance.
+
+#N:168:Plasma Ball Trap
+#I:8:60:5:8:20:12d18:B
+#D:A blast of radiation engulfs you!
+#F:FLOOR | CHEST | DOOR | LEVEL3
+
+N:169:Psi Ball Trap
+I:8:60:5:8:20:12d18:B
+D:Your brain is suddenly blasted!
+F:FLOOR | CHEST | DOOR | LEVEL3
+
+# Useful traps
+
+N:170:Acquirement Trap
+I:1:40:5:5:18:0d0:v
+D:Woah!
+F:FLOOR | DOOR
+
+# More bolt traps
+
+N:171:Greater Lightning Bolt Trap
+I:3:60:5:3:6:6d6:g
+D:You are jolted with electricity!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:172:Greater Poison Bolt Trap
+I:3:60:5:3:6:6d6:g
+D:A blast of deadly poison gas hits you!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:173:Greater Acid Bolt Trap
+I:3:60:5:3:6:6d6:g
+D:A jet of acid shoots out at you! It burns severely!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:174:Greater Cold Bolt Trap
+I:3:60:5:3:6:6d6:g
+D:You are suddenly extremely cold!
+F:FLOOR | CHEST | DOOR | LEVEL1
+
+N:175:Greater Fire Bolt Trap
+I:3:60:5:3:6:6d6:g
+D:You are suddenly extremely hot!
+F:FLOOR | CHEST | DOOR | LEVEL1
diff --git a/lib/edit/trolls.map b/lib/edit/trolls.map
new file mode 100644
index 00000000..14dc0efb
--- /dev/null
+++ b/lib/edit/trolls.map
@@ -0,0 +1,58 @@
+# Permanent wall
+F:X:63:3
+
+# up stairs
+F:<:6:3
+
+# Floor with tree
+F:T:96:3
+
+# Floor with tree(marked)
+F:H:96:1027
+
+# Floor with dirt
+F:.:88:3
+
+# Floor with grass
+F:;:89:3
+
+# Floor with forest troll
+F:f:89:3:297
+
+# Floor with stone troll
+F:s:89:3:401
+
+# Floor with algroth
+F:a:89:3:424
+
+# Floor with Bert
+F:b:89:3:493
+
+# Floor with Bill
+F:i:89:3:494
+
+# Floor with a Dwarven skeleton
+F:k:89:8:0:396
+
+# Marker
+F:,:172:6
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X..HTTTTTTTTTTTTTTTTTTTTTTTX
+D:XH...;TTTTTTTTTTTTTTTTTTTTTX
+D:XTTT;;HTTTTTTTTTTTTTa.TTTTTX
+D:XTTH.k...TTTHHkTHTs.fTTTTTTX
+D:XTTTT;..f.TT.;...HTTTTaTTTTX
+D:XTTTTTHT...;......TTT..HTTTX
+D:XTTTTTTT;;.k..;k.HH.i.TTTTTX
+D:XTTTTTTH.HTHTT..TH.,.HTTTTTX
+D:XTTTTT;THTTTTTHTH.bTTTTTTTTX
+D:XTTT.f.TTTTTTTaTTs;TTTTTTTTX
+D:XTTs..TTTTTTT...fHTTTTTTTTTX
+D:XTTTas.TTTTTTTTTTTTTTTTTTTTX
+D:XTTTTTTTTTTTTTTTTTTTTTTTTTTX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:3
diff --git a/lib/edit/v_info.txt b/lib/edit/v_info.txt
new file mode 100644
index 00000000..d079afea
--- /dev/null
+++ b/lib/edit/v_info.txt
@@ -0,0 +1,2287 @@
+# File: v_info.txt
+
+
+# This file is used to initialize the "lib/raw/v_info.raw" file, which is
+# used to initialize the "vault template" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/raw/v_info.raw" file.
+
+# Note that the "spacing" in the "description" lines is very important!
+
+
+# New vault types added for Zangband -TY
+
+# Quest vaults added - rr9
+
+# Version stamp (required)
+
+V:2.0.0
+
+
+### Simple Vaults (type 7) -- maximum size 44x22 ###
+
+
+N:0:Lesser vault (round)
+X:7:5:12:20
+D: %%%%%%
+D: %%%..##..%%%
+D: %%....####....%%
+D: %......#**#......%
+D:%...,.##+##+##.,...%
+D:%.,.,.#*#*&#*#.,.,.%
+D:%.,.,.#*#&*#*#.,.,.%
+D:%...,.##+##+##.,...%
+D: %......#**#......%
+D: %%....####....%%
+D: %%%..##..%%%
+D: %%%%%%
+
+
+N:1:Lesser vault (octagon)
+X:7:5:14:20
+D: %%%%%%%%%%%%%%
+D: %%.##########.%%
+D: %%..#..,,,,..#..%%
+D:%%,..#.,####,.#..,%%
+D:%....#.,#**#,.#....%
+D:%.###+,##&&##,+###.%
+D:%.#..,,#*&**#,,..#.%
+D:%.#..,,#**&*#,,..#.%
+D:%.###+,##&&##,+###.%
+D:%....#.,#**#,.#....%
+D:%%,..#.,####,.#..,%%
+D: %%..#..,,,,..#..%%
+D: %%.##########.%%
+D: %%%%%%%%%%%%%%
+
+
+N:2:Lesser vault (octagon)
+X:7:5:12:20
+D: %%%%%%%%%%%%
+D: %%%%..........%%%%
+D: %...###+##+###...%
+D:%%...#,,#,,#,,#...%%
+D:%.###+##+##+##+###.%
+D:%.#,,#&&#**#&&#,,#.%
+D:%.#,,#&&#**#&&#,,#.%
+D:%.###+##+##+##+###.%
+D:%%...#,,#,,#,,#...%%
+D: %...###+##+###...%
+D: %%%%..........%%%%
+D: %%%%%%%%%%%%
+
+
+N:3:Lesser vault (square)
+X:7:5:12:20
+D:%%%%%%%%%%%%%%%%%%%%
+D:%*.......&........*%
+D:%.################.%
+D:%.#,.,.,.,.,.,.,.#.%
+D:%.#.############,#.%
+D:%.#,+,&&+**#&*,#.#&%
+D:%&#.#,*&#**+&&,+,#.%
+D:%.#,############.#.%
+D:%.#.,.,.,.,.,.,.,#.%
+D:%.################.%
+D:%*........&.......*%
+D:%%%%%%%%%%%%%%%%%%%%
+
+
+N:4:Lesser vault (diagonal)
+X:7:5:12:20
+D:%%%%%%%%%%%%%%%%%
+D:%,,,##,,,,##....%%
+D:%,,,,##,,,,##....%%
+D:%#,,,,##,,,,##....%%
+D:%##,,,,##,,,,##....%
+D:%.##,,,,,,,,,,#+...%
+D:%..#+,,,,,,,,,,##..%
+D:%...##,,,,##,,,,##.%
+D:%%...##,,,,##,,,,##%
+D: %%...##,,,,##,,,,#%
+D: %%...##,,,,##,,,,%
+D: %%%%%%%%%%%%%%%%%
+
+
+N:5:Lesser vault (diagonal)
+X:7:5:12:20
+D: %%%%%%%%%%%%%%%%%
+D: %%....##,,,,##,,,%
+D: %%....##,,,,##,,,,%
+D:%%....##,,,,##,,,,#%
+D:%....##,,,,##,,,,##%
+D:%...+#,,,,,,,,,,##.%
+D:%..##,,,,,,,,,,+#..%
+D:%.##,,,,##,,,,##...%
+D:%##,,,,##,,,,##...%%
+D:%#,,,,##,,,,##...%%
+D:%,,,,##,,,,##...%%
+D:%%%%%%%%%%%%%%%%%
+
+
+N:6:Lesser vault (square)
+X:7:5:12:20
+D:%%%%%%%%%%%%%%%%%%%%
+D:%,################,%
+D:%^#.*...&..,....,#^%
+D:%^#...,......&...#^%
+D:%^#######++#######^%
+D:%^+.,..&+,*+*....+^%
+D:%^+..*.,+.&+.,.&.+^%
+D:%^#######++#######^%
+D:%^#....,.,.....,.#^%
+D:%^#..&......*....#^%
+D:%,################,%
+D:%%%%%%%%%%%%%%%%%%%%
+
+N:7:Lesser vault (spiral)
+X:7:5:19:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%...................%
+D:%.+################.%
+D:%.#^#*&..,.......*#.%
+D:%.#.#.###########.#.%
+D:%.#.#.#*.,.....*#.#.%
+D:%.#.#.#.#######.#.#.%
+D:%.#.#.#.#,...*#.#.#.%
+D:%.#.#.#.#,###.#.#.#.%
+D:%.#,#,#,#,,*#,#,#,#.%
+D:%.#.#.#.###,#.#.#.#.%
+D:%.#.#.#*.,.*#.#.#.#.%
+D:%.#.#.#######.#.#.#.%
+D:%.#.#*...,...*#.#.#.%
+D:%.#.###########.#.#.%
+D:%.#*.....,....&*#^#.%
+D:%.################+.%
+D:%...................%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+N:8:Lesser vault (layers)
+X:7:5:21:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%...................%
+D:%.########+########.%
+D:%.#.......,.......#.%
+D:%.#.#############.#.%
+D:%.#.#....+.#*...#.#.%
+D:%.#.#.####+####.#.#.%
+D:%.#.#.#...&...#.#.#.%
+D:%.#.#.#.#####.#.#.#.%
+D:%.#.#.#.#*,*#.#.#.#.%
+D:%.#^#^#^#,,,#^#^#.#.%
+D:%.#.#.#.#*,*#.#.#.#.%
+D:%.#.#.#.##+##.#.#.#.%
+D:%.#.#.#..+,#*.#.#.#.%
+D:%.#.#.#########.#.#.%
+D:%.#.#.....,.....#.#.%
+D:%.#.######+######.#.%
+D:%.#.....*#.+......#.%
+D:%.#################.%
+D:%...................%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+N:9:Lesser vault (bank)
+X:7:7:9:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%...................%
+D:%.&XXXXXXXXXXXXXXXXX%
+D:%&&XXXXX+XXX+XX*,XXX%
+D:%&&+###########,*XXX%
+D:%.&XXX+XXX+XXX+XXXXX%
+D:%.&XXXXXXXXXXXXXXXXX%
+D:%...................%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+N:10:Lesser vault (mine)
+X:7:7:9:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%...................%
+D:%.XXXXXXXXXXXXXXXXXX%
+D:%.XX##XXXX*,,XX,*,XX%
+D:%.*#XX,*##X*#XX***XX%
+D:%.XXXX*,XXXXX##,*,XX%
+D:%.XXXXXXXXXXXXXXXXXX%
+D:%...................%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+N:11:Lesser vault (maze)
+X:7:5:22:22
+D:%%%%%%%%%%%%%%%%%%%%%%
+D:%.XXXXXXX............%
+D:%.XX,,.XXXXXXXXXXXXX.%
+D:%.XXXX*...&........X.%
+D:%.X.,,XXXXXXXXXXXX*X.%
+D:%.X.XXX.....*......X.%
+D:%.X..XX.XXXXXXXXXX.X.%
+D:%.XX.X.&.XX.X.....*X.%
+D:%.XX.XXX...*X,XXXX.^.%
+D:%.X.XX,XXXX.XXXX,XXX.%
+D:%.X.XX.....*..&.*X,X.%
+D:%.X.,XXXXXXXXXXX.X,X.%
+D:%.XXXX....*..X,X.X,X.%
+D:%.X....XXX.X.X.X.X.X.%
+D:%.X.XXXX,X.X.&.X.X.X.%
+D:%.X.X...*X.XXXXX.X.X.%
+D:%.X.X,XX&X....&..X.X.%
+D:%.X.XXXX.XXXXXXXXX.X.%
+D:%.X.....*...X*X*X..X.%
+D:%.XX^XXXXXX..X*X,XXX.%
+D:%........XXXXXXXXXXX.%
+D:%%%%%%%%%%%%%%%%%%%%%%
+
+N:12:Lesser vault (prison)
+X:7:10:16:35
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.+..&..+..&..+..&..+..&..+..&..+.%
+D:%.###.#####.#####.#####.#####.###.%
+D:% #,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.%
+D:%.###.#####.#####.#####.#####.###.%
+D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.%
+D:%.###.#####.#####.#####.#####.###.%
+D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.%
+D:%.###.#####.#####.#####.#####.###.%
+D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.%
+D:%.###.#####.#####.#####.#####.###.%
+D:%.#,+.+,#,+.+,#,+.+,#,+.+,#,+.+,#.%
+D:%.###.#####.#####.#####.#####.###.%
+D:%&+..&..+..&..+..&..+..&..+..&..+.%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:13:Lesser vault (camp)
+X:7:10:15:37
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%...................................%
+D:%.####^^^^^^^^^^^^^^^^^^^^^^^^^####.%
+D:%.#,&############+++############&,#.%
+D:%.###+.........................+###.%
+D:%.^#....##+#.....#+#....##+#....#^..%
+D:%.^+....#,,#.....#,#....#,,#....+^..%
+D:%.^+....+&&+..&..+&+..&.+&&+....+^..%
+D:%.^+....#,,#.....#,#....#,,#....+^..%
+D:%.^#....#+##.....#+#....#+##....#^..%
+D:%.###+.........................+###.%
+D:%.#,&############+++############&,#.%
+D:%.####^^^^^^^^^^^^^^^^^^^^^^^^^####.%
+D:%...................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:14:Lesser vault (serpent)
+X:7:10:17:32
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%..............................%
+D:%..##########################..%
+D:%.##...#.^.#.^.#...#...#.,.###.%
+D:%.#..#.*.#...#...#.^.#...#...+.%
+D:%.#.##########################.%
+D:%.#..#..#.,.#.^.#.^.#.*.#...##.%
+D:%.##^#.#..#...#...#...#...#..#.%
+D:%.#..#.####################..#.%
+D:%.#,##.&+,,,,,,,,,,,,,,,,,#*##.%
+D:%.#..##&+,,,,,,,,,,,,,,,,,#..#.%
+D:%.##^#######################.#.%
+D:%.#..#...#...#...#...#.^.#...#.%
+D:%.##...#.^.#.,.#.*.#.^.#...###.%
+D:%..##########################..%
+D:%..............................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:15:Lesser vault (zelazny)
+X:7:5:18:19
+D:%%%%%%%%%%%%%%%%%%%
+D:%.................%
+D:%.###############.%
+D:%.+,,,,,,,,,,,,,#.%
+D:%.###########,,,#.%
+D:%.#,,,,+..##,,,##.%
+D:%.#,,,##.##,,,##..%
+D:%.#,*##.##,,,##...%
+D:%.#&##.##,,,##.##.%
+D:%.###.##,,,##.###.%
+D:%.##.##,,,##.##&#.%
+D:%...##,,,##.##*,#.%
+D:%..##,,,##..+,,,#.%
+D:%.##,,,##########.%
+D:%.#,,,,,,,,,,,,,+.%
+D:%.###############.%
+D:%.................%
+D:%%%%%%%%%%%%%%%%%%%
+
+N:16:Lesser vault (overlap)
+X:7:5:12:18
+D:%%%%%%%%%%%%%%%%%%
+D:%................%
+D:%.##########.....%
+D:%.#,,,^^^^^+&....%
+D:%.#,,,##########.%
+D:%.#,,,#****+,,,#.%
+D:%.#,,,+****#,,,#.%
+D:%.##########,,,#.%
+D:%....&+^^^^^,,,#.%
+D:%.....##########.%
+D:%................%
+D:%%%%%%%%%%%%%%%%%%
+
+N:17:Lesser vault (celtic)
+X:7:5:17:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%...................%
+D:%.#####..#+#..#####.%
+D:%.#&,##.##&##.##,&#.%
+D:%.#+##..#*^*#..##+#.%
+D:%.#....###.###....#.%
+D:%...####..&..####...%
+D:%.###*##.#+#.##*###.%
+D:%.+&.^..&+*+&..^.&+.%
+D:%.###*##.#+#.##*###.%
+D:%...####..&..####...%
+D:%.#....###.###....#.%
+D:%.#+##..#*^*#..##+#.%
+D:%.#&,##.##&##.##,&#.%
+D:%.#####..#+#..#####.%
+D:%...................%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+
+N:18:Lesser vault (mirror)
+X:7:5:17:19
+D:%%%%%%%%%%%%%%%%%%%
+D:%.................%
+D:%.+#############+.%
+D:%.##&,,,,#,,,,&##.%
+D:%.#&#,,,###,,,#&#.%
+D:%.#,,,,,,#,,,,,,#.%
+D:%.##,,,,,#,,,,,##.%
+D:%.###,,,^#^,,,###.%
+D:%.#######+#######.%
+D:%.###,,,^#^,,,###.%
+D:%.##,,,,,#,,,,,##.%
+D:%.#,,,,,,#,,,,,,#.%
+D:%.#&#,,,###,,,#&#.%
+D:%.##&,,,,#,,,,&##.%
+D:%.+#############+.%
+D:%.................%
+D:%%%%%%%%%%%%%%%%%%%
+
+
+N:19:Lesser vault (tower)
+X:7:5:18:15
+D:%%%%%%%%%%%%%%%
+D:%.............%
+D:%..XXX...XXX..%
+D:%..X&XXXXX&X..%
+D:%..XX*****XX..%
+D:%...XX***XX...%
+D:%....X#+#X....%
+D:%....X&&&X....%
+D:%....X^^^X....%
+D:%....X#+#X....%
+D:%....X,,,X....%
+D:%....X^^^X....%
+D:%...XX+#+XX...%
+D:%..XX,&,&,XX..%
+D:%..X^^^*^^^X..%
+D:%.##+#####+##.%
+D:%...&.....&...%
+D:%%%%%%%%%%%%%%%
+
+
+### Greater vaults (type 8) -- maximum size 66x44 ###
+
+N:20:Greater vault (huge)
+X:8:20:17:39
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%+&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X%
+D:%X8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&X%
+D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X%
+D:%X8#&#8#&#8#&#88888888888#8#&#8#&#8#&X%
+D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X%
+D:%X8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&X%
+D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#8#&#&+%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:21:Greater vault (large)
+X:8:35:18:40
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X,#,#,#,#,#,#,#,#*@@*#,#,#,#,#,#,#,#,X%
+D:%X+XXXXXXXXXXXXXXXX##XXXXXXXXXXXXXXXX+X%
+D:%X.,..,.X&.&.,*XX******XX*,.&.&X.,...,#%
+D:%X..,.^^X....,XX***@@***XX,....X^^..,.#%
+D:%XXXXXX+X^&.&XX***@##@***XX&.&^X+XXXXXX%
+D:%X,.&.^^X+XXXX***@#XX#@***XXXX+X^^.,..X%
+D:%X..,&,.X^^^@X**@#X88X#@**#@^^^X.,..&,X%
+D:%X.,....X^^^@#**@#X88X#@**X@^^^X.&.,..X%
+D:%X...,^^X+XXXX***@#XX#@***XXXX+X^^..,.X%
+D:%XXXXXX+X^&.&XX***@##@***XX&.&^X+XXXXXX%
+D:%#.,..^^X.....XX***@@***XX,....X^^.,..X%
+D:%#...,..X&.&.,*XX******XX*,.&.&X..,..,X%
+D:%X+XXXXXXXXXXXXXXXX##XXXXXXXXXXXXXXXX+X%
+D:%X,#,#,#,#,#,#,#,#*@@*#,#,#,#,#,#,#,#,X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:22:Greater vault (butterfly)
+X:8:25:18:40
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X*9..&XX***++^^^^^^^^^^^^++***XX&..*9X%
+D:%X9..&XX,,,,,XX^^^^^^^^^^XX,,,,,#X&..*X%
+D:%X..&#X.....,.XX^^^^^^^^XX..&....XX&..X%
+D:%X.&XX..,.&....XX^^^^^^XX..,...&..XX&.X%
+D:%X&XX..*...&.^..XX^^^^XX..*....,..,XX&X%
+D:%XXXX+XXXXXXXXXXXXX++XXXXXXXXXXXXX+XXXX%
+D:%+....,.,.X&&&&***+99+***&&&&X,.,.,...+%
+D:%+...,.,.,X&&&&***+99+***&&&&X.,.,....+%
+D:%XXXX+XXXXXXXXXXXXX++XXXXXXXXXXXXX+XXXX%
+D:%X&XX..*....&...XX^^^^XX...*...&,..#X&X%
+D:%X.&XX..&.^....XX^^^^^^XX....&....XX&.X%
+D:%X..&XX....&..XX^^^^^^^^XX..,..*.XX&..X%
+D:%X*..&#X,,,,,XX^^^^^^^^^^XX,,,,,XX&..9X%
+D:%X9*..&XX***++^^^^^^^^^^^^++***XX&..*9X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+N:23:Greater vault (castle)
+X:8:35:27:27
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.........................%
+D:%..XXXXX..XXX+XXX..XXXXX..%
+D:%..X,,,X..X.,,,.X..X,,,X..%
+D:%..X,,*XXXX.&&&.XXXX*,,X..%
+D:%..XXXX+....&&&....+XXXX..%
+D:%.....X.....,,,.....X.....%
+D:%.....X..,XXX+XXX,..X.....%
+D:%.....X.XXX^^^^^XXX.X.....%
+D:%.&...X.X,,*****,,X.X..&..%
+D:%....XX.X,XXX+XXX,X.XX....%
+D:%....X..X,X@@@@@X,X..X....%
+D:%....X..X,X@999@X,X..X....%
+D:%....X..X,X@989@X,X..X....%
+D:%....X..X,X@999@X,X..X....%
+D:%....X..X,X@@@@@X,X..X....%
+D:%....XX.X,XXX+XXX,X.XX....%
+D:%.....X.X,,*****,,X.X.....%
+D:%.....X.XXX^^^^^XXX.X.....%
+D:%.....X..,XXX+XXX,..X.....%
+D:%.....X.....&&&.....X.....%
+D:%..XXXX+....&&&....+XXXX..%
+D:%..X,,*XXXX.&&&.XXXX*,,X..%
+D:%..X,,,X..X.,,,.X..X,,,X..%
+D:%..XXXXX..XX^^^XX..XXXXX..%
+D:%.........................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:24:Greater vault (chambers)
+X:8:25:15:40
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%&+.^..^..^..^..^..^..^..^..^..^..^..+&%
+D:%+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+%
+D:%.X.&.^,X&^&^X****+^*^@^X.*.&..X..*.,X.%
+D:%^X.,.&^+^&^@X^^^^X@^*^*X....*^+.^...X^%
+D:%.X*..,.XXX+XXXX+XXXX+XXX.&.^..X..&,.X.%
+D:%^X..^.*X*..^&&@@*X,,,,,XXXX+XXX,....X^%
+D:%.XX+XXXXXXXXXXXXXX,*8*,X,,,,,,XXX+XXX.%
+D:%^X*&X.&,*.X,*&^*^X,,,,,X,,,,,,X....,X^%
+D:%.X&,+....*+,*&^*^XXXXXXXXXX+XXX.,...+.%
+D:%^X.,X.*.&.X,*&^*^+.,.&.^*.&^&^X.....X^%
+D:%.X^*X.,..,X,*&^*^X*.^*.,..&&&^X,..,.X.%
+D:%+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+%
+D:%&+..^..^..^..^..^..^..^..^..^..^..^.+&%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:25:Greater vault (Sierpinski)
+X:8:35:28:39
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%..................X..................%
+D:%.................XXX.................%
+D:%.................X8X.................%
+D:%.....&..........XX+XX................%
+D:%...............XX999XX........&......%
+D:%......&........X@X9X@X...............%
+D:%..............XXXX+XXXX..............%
+D:%.............XX*@*@*@*XX....&........%
+D:%.............X@X*@*@*X@X.............%
+D:%............XXXX**@**XXXX............%
+D:%..........&XX,,,X***X,,,XX&..........%
+D:%..........XX,X@X,X,X,X@X,XX..........%
+D:%.........XXXXX+XXXXXXX+XXXXX.........%
+D:%........XX+,,,,,,,,,,,,,,,+XX........%
+D:%........X@X,,,,,,,,,,,,,,,X@X........%
+D:%.......XX+XX,,,,,,,,,,,,,XX+XX.......%
+D:%......XX,,,XX,,,,,,,,,,,XX,,,XX......%
+D:%......X,X,X,X,,,,,,,,,,,X,X,X,X......%
+D:%.....XXXX+XXXX,,,,,,,,,XXXX+XXXX.....%
+D:%....XX*******XX,,,,,,,XX*******XX....%
+D:%....X,X*****X,X,,,,,,,X,X*****X,X....%
+D:%...XXXX*****XXXX,,,,,XXXX*****XXXX...%
+D:%..XX,,,X***X,,,XX,,,XX,,,X***X,,,XX..%
+D:%..X,X,X,X*X,X,X,X,,,X,X,X,X*X,X,X,X..%
+D:%.XXXXXXXX+XXXXXXXXXXXXXXXXX+XXXXXXXX.%
+D:%.........&.................&.........%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:26:Greater vault (swastika)
+X:8:25:23:29
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%....^^^^^^^^^^^^^^^^^^^^^..%
+D:%^^^^^###################^..%
+D:%^####+..#..............#^..%
+D:%^#.....####.XXXXXXXXXX.#^..%
+D:%^#...&.#,&..X,,,@,@,9X.#^..%
+D:%^#.XXX.####.X,XXXXXXXX.#^..%
+D:%^#.X9X..&,#.X,X......&.#^^^%
+D:%^#.X,X.####.X,X.#######+##^%
+D:%^#.X@X.....^X^X^.........#^%
+D:%^#.X@XXXXXXX+*+XXXXXXXXX.#^%
+D:%^#.X,,,,,,,^*X*^,,,,,,,X.#^%
+D:%^#.XXXXXXXXX+*+XXXXXXX@X.#^%
+D:%^#.........^X^X^.....X@X.#^%
+D:%^##+#######.X,X.####.X,X.#^%
+D:%^^^#.&......X,X.#,&..X9X.#^%
+D:%..^#.XXXXXXXX,X.####.XXX.#^%
+D:%..^#.X9,@,@,,,X..&,#.&...#^%
+D:%..^#.XXXXXXXXXX.####.....#^%
+D:%..^#..............#..+####^%
+D:%..^###################^^^^^%
+D:%..^^^^^^^^^^^^^^^^^^^^^....%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:27:Greater vault (great spiral)
+X:8:40:39:39
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.....................................%
+D:%.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X@X.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^^+.%
+D:%.X.X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X.X.X,..,..,..,..,..,..,..,..,..,.X.%
+D:%.X.X^X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.%
+D:%.X.X.X.X......&...&...&...&...@..X,X.%
+D:%.X.X^X,X@XXXXXXXXXXXXXXXXXXXXXXX.X.X.%
+D:%.X.X.X.X.X&.........8.........&X*X.X.%
+D:%.X.X^X.X.X.XXXXXXXXXXXXXXXXXXX.X.X,X.%
+D:%.X.X.X,X.X.X.^.^.^.^.^.^.^.^.X.X.X.X.%
+D:%.X.X^X.X.X.X^XXXXXXXXXXXXXXX*X.X*X.X.%
+D:%.X.X.X.X&X.X.X,..,..,..,..,X.X.X.X,X.%
+D:%.X.X^X,X.X.X^X.XXXXXXXXXXX.X*X.X.X.X.%
+D:%.X.X.X.X.X.X.X.X..&..&..9X.X.X.X*X.X.%
+D:%.X.X^X.X&X.X^X,X9XXXXXXX.X,X*X.X.X,X.%
+D:%.X.X.X,X.X.X.X.X&X@@..&X.X.X.X.X.X.X.%
+D:%.X.X^X.X.X9X^X.X*X+XXX.X&X.X*X.X*X.X.%
+D:%.X.X.X.X.X.X.X,X^+8+^X.X.X,X.X.X.X,X.%
+D:%.X9X^X,X.X.X^X.XXX+X^X.X*X.X*X.X.X.X.%
+D:%.X.X.X.X&X.X.X.,.,,X.X9X.X.X.X.X*X.X.%
+D:%.X.X^X.X.X.X^XXXXXXX^X.X*X,X*X9X.X,X.%
+D:%.X.X.X,X.X.X.^.^.^.^.X.X.X.X.X.X.X.X.%
+D:%.X.X^X.X.X.XXXXXXXXXXX.X^X.X^X.X*X.X.%
+D:%.X.X.X.X8X&.....9.....&X.X,X.X.X.X,X.%
+D:%.X.X^X,X.XXXXXXXXXXXXXXX^X.X^X.X.X.X.%
+D:%.X.X.X.X..&....8......@..X.X.X.X*X.X.%
+D:%.X.X^X.XXXXXXXXXXXXXXXXXXX,X^X.X.X,X.%
+D:%.X.X.X,..,..,..,..,..,..,..X.X.X.X.X.%
+D:%.X.X^XXXXXXXXXXXXXXXXXXXXXXX^X.X*X.X.%
+D:%.X.X.*.^.*.*.*.*.*.*.*.*.*.^.X.X.X,X.%
+D:%.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.%
+D:%.X&.............9.............&X*X.X.%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X,X.%
+D:%.+^^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.X,X.%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X.%
+D:%.....................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:28:Greater vault (greater castle)
+X:8:40:25:51
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.................................................%
+D:%...XXXXXX...............................XXXXXX...%
+D:%..XX,,,,XX.............................XX,,,,XX..%
+D:%.XX,*99*,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,*99*,XX.%
+D:%.XX,*99*,+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^+,*99*,XX.%
+D:%..XX,,,,XXXXXXXXXXXXXXX+X+XXXXXXXXXXXXXXX,,,,XX..%
+D:%...XX++XX...............X...............XX++XX...%
+D:%....X^^X.............XXXXXXX.............X^^X....%
+D:%....X^^X.X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X.X^^X....%
+D:%....X^^X.X^^^XXXXXXXXXXXXXXXXXXXXXXX^^^X.X^^X....%
+D:%....X^^+&X^^XX@.+***************+.@XX^^X&+^^X....%
+D:%....X^^XXX^^+@.@X*9999988899999*X@.@+^^XXX^^X....%
+D:%....X^^+.X^^XX@.+***************+.@XX^^X.+^^X....%
+D:%....X^^X.X^^^XXXXXXXXXXXXXXXXXXXXXXX^^^X.X^^X....%
+D:%....X^^X.X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X.X^^X....%
+D:%....X^^X............XXXX+XXXX............X^^X....%
+D:%...XX++XX..........XX&.&.&.&XX..........XX++XX...%
+D:%..XX,,,,XXXXXXXXXXXX&.&.&.&.&XXXXXXXXXXXX,,,,XX..%
+D:%.XX,*99*,+********9XXXXX+XXXXX9********+,*99*,XX.%
+D:%.XX,*99*,XXXXXXXXXXX&.&.&.&.&XXXXXXXXXXX,*99*,XX.%
+D:%..XX,,,,XX.........XX&.&.&.&XX.........XX,,,,XX..%
+D:%...XXXXXX...........XXXX+XXXX...........XXXXXX...%
+D:%.................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:29:Lesser vault (x-factor)
+X:8:25:25:26
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%^^^^^^^^^^^^^^^^^^^^^^^^%
+D:%^##########++##########^%
+D:%^#XX,,,,,,,,,,,,,,,,XX#^%
+D:%^#,XX,,,,,,,,,,,,,,XX,#^%
+D:%^#,,XX,,,,,,,,,,,,XX,,#^%
+D:%^#,,,XX,,,,,,,,,,XX,,,#^%
+D:%^#,,,,XX,,,,,,,,XX,,,,#^%
+D:%^#,,,,,XX,,,,,,XX,,,,,#^%
+D:%^#,,,,,,XX,,,,XX,,,,,,#^%
+D:%^#,,,,,,,X+XX+X,,,,,,,#^%
+D:%^+,,,,,,,,X99X,,,,,,,,+^%
+D:%^+,,,,,,,,X99X,,,,,,,,+^%
+D:%^+,,,,,,,,X99X,,,,,,,,+^%
+D:%^#,,,,,,,X+XX+X,,,,,,,#^%
+D:%^#,,,,,,XX,,,,XX,,,,,,#^%
+D:%^#,,,,,XX,,,,,,XX,,,,,#^%
+D:%^#,,,,XX,,,,,,,,XX,,,,#^%
+D:%^#,,,XX,,,,,,,,,,XX,,,#^%
+D:%^#,,XX,,,,,,,,,,,,XX,,#^%
+D:%^#,XX,,,,,,,,,,,,,,XX,#^%
+D:%^#XX,,,,,,,,,,,,,,,,XX#^%
+D:%^##########++##########^%
+D:%^^^^^^^^^^^^^^^^^^^^^^^^%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:30:Greater vault (university)
+X:8:30:29:38
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%....................................%
+D:%.##################################.%
+D:%.#*+..&#..,#,#..,#,#..,#,#.,#&+*#*#.%
+D:%.#####.#.###.#.###.#.###.#.##.###+#.%
+D:%.#***#.#.#*..#.#*..#.#*..#.#..#..&#.%
+D:%.#+###+#+###+#+###+#+###+#+##+###+#.%
+D:%.#...+&......................&+...#.%
+D:%.#.###.##########++##########.###.#.%
+D:%.#,#.+.#,,,,,,,,,,,,,,,,,,,,#.#*#,#.%
+D:%.###.#.#,.,..,..,@.,..,..,.,#.#.###.%
+D:%.#,..#.#,..,..,..,..,..,..,,#.+..,#.%
+D:%.#####.#,9&,,,9,,&,,&,,.,,,,#.#####.%
+D:%.+^^^+.+,,,&,,,&,,,9,,&,,&,,+.+^^^+.%
+D:%.+^^^+.+,.,...,...,...,...,,+.+^^^+.%
+D:%.###+#.#,,,,&,,&,,,9,&,,,&,,#.#####.%
+D:%.#*#&#.#,,,&,,,9,&,,&,,,,,9,#.#..,#.%
+D:%.#+#*#.#,.,...,....,....,..,#.#.###.%
+D:%.#.###.#,,,&,,,,&,9,,&,,,&,,#.+.#,#.%
+D:%.#&..+.#,,,,,,,,.,,,,,,,,,,,#.###.#.%
+D:%.#####.##########++##########.+...#.%
+D:%.#&..+&......................&#+###.%
+D:%.#.###+#+###+#+###+#+###+#+##+#...#.%
+D:%.#.+*#.#.#*..#.#*..#.#*..#.#..###,#.%
+D:%.#####.#.###.#.###.#.###.#.##&#*###.%
+D:%.#*+..&#..,#,#..,#,#..,#,#.,#.+,*,#.%
+D:%.##################################.%
+D:%....................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:31:Greater vault (nethack castle (almost))
+X:8:35:19:62
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%............................................................%
+D:%.XXXXXXX............................................XXXXXXX.%
+D:%.X,,9,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,9,,X.%
+D:%.X,,,,,+.......^....*....^ ..........^.......*...^..+,,,,,X.%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%......X,.,9,,,,X,,,,,,,,,+,.,.,.,.,.,X,.,.,.+^+,.,.,.X......%
+D:%......X,.9*9,.,X,,,,,,,,,X,.,..,..,.,X,.,.,.X^X,.,.,.X......%
+D:%....&.X.&*@*&,.XXXXXXXXXXX,.,.,9,.,.,XXXXXXXX+XXXXXXXX......%
+D:%......+9*@8@*9.+^^^^^^^^^+,.,,9@9,,.,+^^^^^^^^^^^^^^^+......%
+D:%....&.X.&*@*&,.XXXXXXXXXXX,.,.,9,.,.,XXXXXXXX+XXXXXXXX......%
+D:%......X,.9*9,.,X,,,,,,,,,X,.,.,,,.,.,X.,.,.,X^X.,.,.,X......%
+D:%......X,.,9,,,,X,,,,,,,,,+,.,.,.,.,.,X.,.,.,+^+.,.,.,X......%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X,,,,,+.......^....*....^...........^.......*...^..+,,,,,X.%
+D:%.X,,9,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,9,,X.%
+D:%.XXXXXXX............................................XXXXXXX.%
+D:%............................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:32:Greater vault (another nethack-style castle)
+X:8:30:18:52
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%..................................................%
+D:%.XXXXX......................................XXXXX.%
+D:%.X9,,X......................................X,,9X.%
+D:%.XXX+XXXXXXXXXXXXXXXXXXX++XXXXXXXXXXXXXXXXXXX+XXX.%
+D:%...X^^^^^^^^^^^^^^^^^^^+..+^^^^^^^^^^^^^^^^^^^X...%
+D:%...X^XXXXXXXXXXXXXXXXXXX++XXXXXXXXXXXXXXXXXXX^X...%
+D:%...X^X,,,,,,,,,,,,,,,,,X..X,,,,,,,,,X,,@@@@,X^X...%
+D:%...X^X,,,,,,,,,,,,,,,,,+..+,,,,,,,,,+,,,,998X^X...%
+D:%...X^X,,,,,,,,,,,,,,,,,+..+,,,,,,,,,+,,,,998X^X...%
+D:%...X^X,,,,,,,,,,,,,,,,,X..X,,,,,,,,,X,,@@@@,X^X...%
+D:%...X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX^X...%
+D:%...X^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^X...%
+D:%.XXX+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+XXX.%
+D:%.X9,,X......................................X,,9X.%
+D:%.XXXXX......................................XXXXX.%
+D:%..................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:33:Lesser vault (nethack-style tower)
+X:7:5:15:19
+D:%%%%%%%%%%%%%%%%%%%
+D:%.................%
+D:%...###.###.###...%
+D:%...#&#.#&#.#&#...%
+D:%.###+###+###+###.%
+D:%.#.,.,.,.+.+,,,#.%
+D:%.###+#####.#####.%
+D:%...+*&*&*#.+,#...%
+D:%.###+#####.#####.%
+D:%.#.,.,.,.+.+,,,#.%
+D:%.###+###+###+###.%
+D:%...#&#.#&#.#&#...%
+D:%...###.###.###...%
+D:%.................%
+D:%%%%%%%%%%%%%%%%%%%
+
+
+N:34:Lesser vault (nethack, rooms)
+X:7:5:14:27
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.........................%
+D:%.#+#####################.%
+D:%.#.^.^.^.+^+^...^#,,,,,#.%
+D:%.#^###+###^#####.+,,,,,#.%
+D:%.#.#,,,,,#.#,,,#^#,,,,,#.%
+D:%.#^#,,,,,#^+,,,#+#######.%
+D:%.#.#######.#,,,#.......#.%
+D:%.#^#,,,,,#^#,,,#..&.&..#.%
+D:%.#.#,,,,,#.#####.......#.%
+D:%.#^+,,,,,#^.^.^+..&.&.^+.%
+D:%.#######################.%
+D:%.........................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:35:Lesser vault (nethack city)
+X:7:9:17:33
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%...............................%
+D:%.############.....############.%
+D:%.#,,,#,,,#,,#.....#**#,,,#***#.%
+D:%.#,,,#,,,#,,#.....+**#,,,#***#.%
+D:%.#,,,#,,,#,,#.....#**#,,,#&&&#.%
+D:%.#^,,#,,,#+##.....####+###+###.%
+D:%.#+####+##.....................%
+D:%...............................%
+D:%.#+###+###....###+##...........%
+D:%.#^,,#^,,####.#,,,,#.######+##.%
+D:%.#,,,#,,,#**#.#,&&,#.+^^#,,,,#.%
+D:%.#,,,#,,,#*^+.#,&@,#.#^^#,,,,#.%
+D:%.#,,,#,,,#**#.#,,,,#.#**#,,,,#.%
+D:%.############.######.#########.%
+D:%...............................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:36:Greater vault (nethack, large city)
+X:8:25:21:54
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%....................................................%
+D:%.##################################################.%
+D:%.#................................................#.%
+D:%.#.####################.########..#####.#########.#.%
+D:%.#.#^,,,,^#^****^#^,,^#.#^,,,,^#..#^^^#.#^,,,,,^#.#.%
+D:%.#.#^,,,,^#^****^#^,,^#.#^,,,,^#..+^**#.#^,,,,,^#.#.%
+D:%.#.#^,,,,^#^,,,,^#^^^^#.#^^^^^^#..#^^^#.#^,,,,,^#.#.%
+D:%.#.#^^^^^^#^^^^^^###+##.###+####..#####.#^^^^^^^#.#.%
+D:%.#.#+#########+###......................######+##.#.%
+D:%.#................................................#.%
+D:%.#.###+######+####......###+########..............#.%
+D:%.#.#^^^^^^#^^^^^^######.#^^^^^^^^^^#...###+#####..#.%
+D:%.#.#^,,,,^#^,,,,^#^^^^+.#^,,,,,,,,^#...#^^^^^^^#..#.%
+D:%.#.#^****^#^,,,,^#,,,^#.#^@999999@^#...#^,,,,,^#..#.%
+D:%.#.#^****^#^,,,,^#,,,^#.#^^^^^^^^^^#...#^,,,,,^#..#.%
+D:%.+.####################.############...#########..#.%
+D:%.#................................................#.%
+D:%.##################################################.%
+D:%....................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:37:Lesser vault (nethack, tiny castle)
+X:7:5:14:34
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%................................%
+D:%.#####....................#####.%
+D:%.#*,&#....................#&,*#.%
+D:%.###+######################+###.%
+D:%...#,,,,,,,,,,,,,#,,,,,,,,,,#...%
+D:%...+,,,,,,,,,,,,,+,,,,,,,,,,+...%
+D:%...+,,,,,,,,,,,,,+,,,,,,,,,,+...%
+D:%...#,,,,,,,,,,,,,#,,,,,,,,,,#...%
+D:%.###+######################+###.%
+D:%.#*,&#....................#&,*#.%
+D:%.#####....................#####.%
+D:%................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:38:Greater vault (nethack mirror)
+X:8:25:22:41
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.......................................%
+D:%...............XXXX+XXXX...............%
+D:%...............X**X^X**X...............%
+D:%....XXXXXXXXXXXX**X^X**XXXXXXXXXXXX....%
+D:%....X^+99999999X+XX^XX+X99999999+^X....%
+D:%....X^X9*******X,,X^X,,X*******9X^X....%
+D:%....X^X9*******X,,X^X,,X*******9X^X....%
+D:%.XXXX+XXXXXXXXXXX+X+X+XXXXXXXXXXX+XXXX.%
+D:%.X**X,,,,,,,,,,X,,,,,,,X,,,,,,,,,,X**X.%
+D:%.X*@+,,,,,,,,,,+,,,,8,,X,,,,,,,,,,+@*X.%
+D:%.X*@+,,,,,,,,,,X,,,8,,,+,,,,,,,,,,+@*X.%
+D:%.X**X,,,,,,,,,,X,,,,,,,X,,,,,,,,,,X**X.%
+D:%.XXXX+XXXXXXXXXXX+X+X+XXXXXXXXXXX+XXXX.%
+D:%....X^X9*******X,,X^X,,X*******9X^X....%
+D:%....X^X9*******X,,X^X,,X*******9X^X....%
+D:%....X^+99999999X+XX^XX+X99999999+^X....%
+D:%....XXXXXXXXXXXX**X^X**XXXXXXXXXXXX....%
+D:%...............X**X^X**X...............%
+D:%...............XXXX+XXXX...............%
+D:%.......................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:39:Greater vault (nethack tomb)
+X:8:25:13:57
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.......................................................%
+D:%..........XXXXX.XXXXX.XXXXX.XXXXX.XXXXX................%
+D:%..........X,,,X.X,,,X.X,,,X.X,,,X.X,,,X................%
+D:%.XXXXXXXXXX,&,XXX,&,XXX,&,XXX,&,XXX,&,XXXXXXXXXXXXXXXX.%
+D:%.X&^&X....^...^...^...^...^...^...^..^..X,,,,,,,,X,,9X.%
+D:%.+^^^+..^...^...^...^...^...^...^...^.@.+,,,,,,,,+,98X.%
+D:%.X&^&X....^...^...^...^...^...^...^.....X,,,,,,,,X,,9X.%
+D:%.XXXXXXXXXX,&,XXX,.,XXX,.,XXX,&,XXX,&,XXXXXXXXXXXXXXXX.%
+D:%..........X,,,X.X,,,X.X,,,X.X,,,X.X,,,X................%
+D:%..........XXXXX.XXXXX.XXXXX.XXXXX.XXXXX................%
+D:%.......................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:40:Greater vault (nethack hell level #1)
+X:8:30:17:55
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.....................................................%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.XX*XXXXXXXXXX**XXXXXXXXXXXX***********************X.%
+D:%.XX*X,,,,X***X+XX,,,,,,X***XXXXXXXXXXXX^^^^^^^^^^^^X.%
+D:%.XX,X,,,,+***X@^+,,,,,,X***+,,,,,,,,,,X^^^^^^^^^^^^X.%
+D:%.XX&X,,,,XXXXXXXX^^^^^^^^^^X,,,XXXXXXXXXXXXXX^^^^^^X.%
+D:%.XX+X....XXXX.............^X,,,+,,,,,,,,,,,9XXXXX^^X.%
+D:%.+^^^^^^^^^^+^^^^^^^^^^^^^^XXXXX,,,,,,,,,,,9+888+@@X.%
+D:%.XX+X....XXXX.............^X,,,+,,,,,,,,,,,9XXXXX^^X.%
+D:%.XX&X,,,,XXXXXXXX^^^^^^^^^^X,,,XXXXXXXXXXXXXX^^^^^^X.%
+D:%.XX,X,,,,+***X@^+,,,,,,X***+,,,,,,,,,,X^^^^^^^^^^^^X.%
+D:%.XX*X,,,,X***X+XX,,,,,,X***XXXXXXXXXXXX^^^^^^^^^^^^X.%
+D:%.XX*XXXXXXXXXX**XXXXXXXXXXXX***********************X.%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.....................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:41:Greater vault (nethack hell level #2)
+X:8:30:15:54
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%....................................................%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...%
+D:%.+^XX,+^^^^^X^^^^^^^^^^^^^^^^^^^^^X,,,X,@^X*^*+9X...%
+D:%.X^XX,X^XXX,X@XXXXXXXXXXXXXXXXXXX^X,X,X,X^X*X*X9X...%
+D:%.X^XX,X^^^X,X^^^^^^^^^^^^^^^^^^@X^X,X,X,X^X*X*X9X...%
+D:%.X^XXXXXX^X,XXXXXXXXXXXXXXXXXXX^X^X,X,X,X^X*X*X+XXX.%
+D:%.X^^^^^^X^X,X,,,,,,,,,,,,,,,,,X^X^X,X,X,X^X*X*X888X.%
+D:%.X+XXXX^X^X,X,XXXXXXXXXXXXXXX,X^X^X,X,X,X^X*X*X+XXX.%
+D:%.X,XX,X^X^X,X,X,,,,,,,,,,,,,,,X^X^X,X,X,X^X*X*X9X...%
+D:%.X,XX,X^X&X,X,X,XXXXXXXXXXXXXXX^X^X,X,X,X^X*X*X9X...%
+D:%.X,XX,+^^^X,,,X^^^^^^^^^^^^^^^^^X^@,X,,,X^@*X*+9X...%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...%
+D:%....................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:42:Greater vault (nethack hell level #3)
+X:8:30:17:55
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.....................................................%
+D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.........%
+D:%...X,,,,,,,,,,,,,,,,,,,,,XXX^^^^^^^^^^^^^^^X.........%
+D:%...X,^^^^^^^^^^XXXX,,,,,,+&&^XXXXXXXXXXXX^^+.........%
+D:%...X,^XXXXXX******X,,XXXXXXXXX^^^^******XXXX.........%
+D:%...X,^X****X@*XXXXXXXX,,,,,,,,,,,XXXXXXXXXXXXXX......%
+D:%.XXXX+X,,,,XXXX,,,,,,,,,,,,,,,,,^+************XXXXX..%
+D:%.X@,,,,,XX,,,,+,,XXXXXXXXXXXXXXXXX****9999****+888X..%
+D:%.XXXX+X,,,,XXXX,,,,,,,,,,,,,,,,,^+************XXXXX..%
+D:%...X,^X****X@*XXXXXXXX,,,,,,,,,,,XXXXXXXXXXXXXX......%
+D:%...X,^XXXXXX******X,,XXXXXXXXX^^^^******XXXX.........%
+D:%...X,^^^^^^^^^^XXXX,,,,,+&&^^XXXXXXXXXXXX^^+.........%
+D:%...X,,,,,,,,,,,,,,,,,,,,XXX^^^^^^^^^^^^^^^^X.........%
+D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.........%
+D:%.....................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:43:Lesser vault (easter egg)
+X:7:5:14:19
+D:%%%%%%%%%%%%%%%%%%%
+D:%.................%
+D:%.###############.%
+D:%.#,^,^,^,^,^,^,#.%
+D:%.#+###########^#.%
+D:%.#,^,^,^,^,^,#,#.%
+D:%.###########,#^#.%
+D:%.#,^,^,^,^,#^#,#.%
+D:%.#+#######,#,#^#.%
+D:%.#,,,,,,,#^#^#,#.%
+D:%.#,,,,,,,#,+,#^+.%
+D:%.###############.%
+D:%.................%
+D:%%%%%%%%%%%%%%%%%%%
+
+N:44:Greater vault (nethack samurai castle)
+X:8:35:20:59
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.........................................................%
+D:%.XXXXX.............................................XXXXX.%
+D:%.X***X.............................................X***X.%
+D:%.X***XXX.....XX+XXXXXXXXXXXXXXXXXXXXXXXXX+XX.....XXX***X.%
+D:%.XXX@,,X.....X^&^X,,,X***X,,,X***X^^^+8X^&^X.....X,,@XXX.%
+D:%...X,,,XXXXXXX^^^X,,,X^^^X,,,X^^^X@@^X8X^^^XXXXXXX,,,X...%
+D:%...XXX^^^^^^^^^XXXXX+XXX+X+XXXXX+X+XXXXXXX^^^^^^^^^XXX...%
+D:%.....X^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^X.....%
+D:%.....X^^+,,,,,,,,+,,,,,,,,,99,,,,,,,,,,+,,,,,,,,+^^X.....%
+D:%.....X^^+,,,,,,,,+,,,,,,,,,99,,,,,,,,,,+,,,,,,,,+^^X.....%
+D:%.....X^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^X.....%
+D:%...XXX^^^^^^^^^XXXXXXX+X+XXXXX+X+XXX+XXXXX^^^^^^^^^XXX...%
+D:%...X,,,XXXXXXX^^^X8X^@@X^^^X^^^X,,,X,,,X^^^XXXXXXX,,,X...%
+D:%.XXX@,,X.....X^&^X8+^^^X***X***X,,,X,,,X^&^X.....X,,@XXX.%
+D:%.X***XXX.....XX+XXXXXXXXXXXXXXXXXXXXXXXXX+XX.....XXX***X.%
+D:%.X***X.............................................X***X.%
+D:%.XXXXX.............................................XXXXX.%
+D:%.........................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:45:Greater vault (nethack samurai castle #2)
+X:8:35:20:61
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%...........................................................%
+D:%.XXXXXXXXXXXXX...............................XXXXXXXXXXXXX.%
+D:%.X,,^^^^^^^,,X...............................X,,^^^^^^^,,X.%
+D:%.X,,^XXXXX^,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,^XXXXX^,,X.%
+D:%.X,,^+999X^,,X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X,,^X999+^,,X.%
+D:%.X,,^XXXXX^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^XXXXX^,,X.%
+D:%.X^^^^^^^^^^^XXXXXX+XXXXXXXXXXXXXXXXXXX+XXXXXX^^^^^^^^^^^X.%
+D:%.XXXX^^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^^XXXX.%
+D:%....+^^^X********+,,,,,,,,,,,,,,,,,,,,,,,+********X^^^+....%
+D:%....+^^^X********+,,,,,,,,,,,,,,,,,,,,,,,+********X^^^+....%
+D:%.XXXX^^^XXXXXXXX*X,,,,,,,,,,,,,,,,,,,,,,,X*XXXXXXXX^^^XXXX.%
+D:%.X^^^^^^^^^^^XXXXXX+XXXXXXXXXXXXXXXXXXX+XXXXXX^^^^^^^^^^^X.%
+D:%.X,,^XXXXX^,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^XXXXX^,,X.%
+D:%.X,,^+999X^,,X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X,,^X999+^,,X.%
+D:%.X,,^XXXXX^,,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,^XXXXX^,,X.%
+D:%.X,,^^^^^^^,,X...............................X,,^^^^^^^,,X.%
+D:%.XXXXXXXXXXXXX...............................XXXXXXXXXXXXX.%
+D:%...........................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:46:Greater vault (nethack spiral)
+X:8:30:19:38
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.........XXXXXXXXX+XXXXXXXXX........%
+D:%......XXXX^^^^^^^^^^^^^^^^^XXXX.....%
+D:%...XXXX^^^^^XXXXXXXXXXXXX^^^^^XXXX..%
+D:%..XX^^^^^XXXX^^^^^^^^^^^XXXX^^^^^XX.%
+D:%.XX^^^^XXX^^^^XXXXXXXXX^^^^XXX^^^^XX%
+D:%.X^^^^XX^^^^XXX,^^^^^,XXX^^^^XX^^^^X%
+D:%.X,,,XX,,,XXX,,^XX+XX^,,XXX,,,XX,,,X%
+D:%.X,,,X,,,,X,,,^XX***XX^,,,X,,,,X,,,X%
+D:%.X,,,X,99,X,,,^+@888@+^,,,X,,,,+,,,X%
+D:%.X,,,X,,,,X,,,^XX***XX^,,,X,,,,X,,,X%
+D:%.X,,,XX,,,XXX,,^XX+XX^,,XXX,,,XX,,,X%
+D:%.X,,,,XX^^^^XXX,^^^^^,XXX^^^^XX,,,,X%
+D:%.XX,,,,XXX^^^^XXXX+XXXX^^^^XXX,,,,XX%
+D:%..XX,,,**XXXX^^^^^^^^^^^XXXX,,,,,XX.%
+D:%...XXXX**,,,XXXXXXXXXXXXX,,,,,XXXX..%
+D:%......XXXX,,,,,,,,,,,,,,,,,XXXX.....%
+D:%.........XXXXXXXXXXXXXXXXXXX........%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:47:Greater vault (nethack building)
+X:8:30:16:41
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%...X^^^^^^^^^^^^^^^^X**@**X**@**X**@**X%
+D:%...X^^XXXXXXXXXXXX^^XXX+XXXXX+XXXXX+XXX%
+D:%...X^^X,,,,,,,,,,X^^X^^^^^^^^^^^^^^^^^X%
+D:%...X^^X,,,,,,,,,,X^^X+XXX+XXX+XXXXX+XXX%
+D:%.XXX^^X,,,,,,,,,,X^^^^^^X,,,X,,,X,,,,,X%
+D:%.+^&^^X,,,,,,,,,,+^^^^^^X,,,X,,,X,989,X%
+D:%.+^&^^X,,,,,,,,,,+^^^^^^X,,,X,,,X,989,X%
+D:%.XXX^^X,,,,,,,,,,X^^^^^^X,,,X,,,X,,,,,X%
+D:%...X^^X,,,,,,,,,,X^^X+XXXXX+XXX+XXX+XXX%
+D:%...X^^X,,,,,,,,,,X^^X^^^^^^^^^^^^^^^^^X%
+D:%...X^^XXXXXXXXXXXX^^XXX+XXXXX+XXXXX+XXX%
+D:%...X^^^^^^^^^^^^^^^^X**@**X**@**X**@**X%
+D:%...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:48:Lesser vault (nethack, spiral rooms)
+X:7:5:13:32
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%..............................%
+D:%....#######################...%
+D:%..###^+,,,,,,#^^^^^^^^^^+*###.%
+D:%..#,#^#,,,,,,#+############,#.%
+D:%..#^#^#,,,,,,#,,,+,,,,,,#^#^#.%
+D:%..+^+^#,,,,,,#,,,#,,,,,,#^+^+.%
+D:%..#^#^#,,,,,,+,,,#,,,,,,#^#^#.%
+D:%..#,############+#,,,,,,#^#,#.%
+D:%..###*+^^^^^^^^^^#,,,,,,+^###.%
+D:%....#######################...%
+D:%..............................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:49:Greater vault (nethack building)
+X:8:30:17:54
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%....................................................%
+D:%........XXXXXXXXXXXXX.................XXXXXXXXXXXXX.%
+D:%........X***********X.................X***********X.%
+D:%.XXXXXXXX***********XXXXXXXXXXXXXXXXXXX***********X.%
+D:%.+^^^^^^X,,,,,,,,,,,X**X**X**X**X**X**X*,,,,,,,,,*X.%
+D:%.X^,,,,^X^,,,,,,,,,,X,@X,@X,@X,@X,@X,@X,,,,,,,,,,,X.%
+D:%.X^^^^^^+^^^^^^^^^^^XX+XX+XX+XX+XX+XX+X,,,,,,,,,,,X.%
+D:%.XXXXXXXX^^^^^^^^^^^+^^^^^^^^^^^^^^^^^+,,,,,89,,,,X.%
+D:%.X^^^^^^+^^^^^^^^^^^XX+XX+XX+XX+XX+XX+X,,,,,98,,,,X.%
+D:%.X^,,,,^X^,,,,,,,,,,X,@X,@X,@X,@X,@X,@X,,,,,,,,,,,X.%
+D:%.+^^^^^^X,,,,,,,,,,,X**X**X**X**X**X**X*,,,,,,,,,*X.%
+D:%.XXXXXXXX***********XXXXXXXXXXXXXXXXXXX***********X.%
+D:%........X***********X.................X***********X.%
+D:%........XXXXXXXXXXXXX.................XXXXXXXXXXXXX.%
+D:%....................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:50:Lesser vault (nethack, head)
+X:7:7:17:29
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%........###########........%
+D:%...#####################...%
+D:%...######,,,,,,,,,######...%
+D:%..#+^^^^#,,,,,,,,,#^^^^+#..%
+D:%..##^^^^#,,,,,,,,,+^,,^##..%
+D:%.#####+##,,,,,,,,,#^^^^###.%
+D:%.##^^^^^^,,,,,,,,,########.%
+D:%.####+###++#########*@+*##.%
+D:%.##^^^^^#^^#,,,#^@^####*##.%
+D:%.##^^,,^#^^#,###^#^#****##.%
+D:%..##^,,^#^^#,,,.^#^#@####..%
+D:%..#+^^^^#^^#####^#^^^^^##..%
+D:%...######^^^^^^^^#######...%
+D:%...#####################...%
+D:%........###########........%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:51:Lesser vault (maze of rooms)
+X:7:10:16:32
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%..............................%
+D:%.#+##########################.%
+D:%.#^#,,,+^#*#^+,,+*#^+^#*#**@#.%
+D:%.#^+,,,#^+^+^######^#^#*###+#.%
+D:%.#########+###,,,,#^#,+@#,,,#.%
+D:%.#,,,,,,+^^+,,,,,,+^#######+#.%
+D:%.###+#######,,,,,#####,,+^^^#.%
+D:%.#,,,+**#**+,,,,,#^^^+,,#^,^#.%
+D:%.#############+###^,^#+##^^^#.%
+D:%.#***#^+,,#,,,,,,+^^^#^######.%
+D:%.#+###^#,,+,,,,,,#####^+,^^^#.%
+D:%.#,,,+^#,,########,,,+^#,^^^#.%
+D:%.##########################+#.%
+D:%..............................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:52:Lesser vault (tetris tiles)
+X:7:5:20:13
+D:%%%%%%%%%%%%%
+D:%..#######..%
+D:%..#**#**#..%
+D:%..#,###,#..%
+D:%..#,#^#,#..%
+D:%.##+#^#+##.%
+D:%.#&,#^#,&#.%
+D:%.#,,+^+,,#.%
+D:%.####+####.%
+D:%.###,^,###.%
+D:%.#*##^##*#.%
+D:%.#,,#+#,,#.%
+D:%.##,#^#,##.%
+D:%.##+#^#+##.%
+D:%.#,,#^#,,#.%
+D:%.##,+^+,##.%
+D:%..#,#+#,#..%
+D:%..###.###..%
+D:%...........%
+D:%%%%%%%%%%%%%
+
+N:53:Lesser vault (hospital ward)
+X:7:5:14:20
+D:%%%%%%%%%%%%%%%%%%%%
+D:%..................%
+D:%.################.%
+D:%.#,,#,,#,,#,,#,,#.%
+D:%.#,,#,,#,,#,,#,,#.%
+D:%.##+##+##+##+##+#.%
+D:%.+..............+.%
+D:%.+..............+.%
+D:%.#+##+##+##+##+##.%
+D:%.#,,#,,#,,#,,#,,#.%
+D:%.#,,#,,#,,#,,#,,#.%
+D:%.################.%
+D:%..................%
+D:%%%%%%%%%%%%%%%%%%%%
+
+N:54:Lesser vault (lesser crypt)
+X:7:5:13:26
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%................###.....%
+D:%................#,#.....%
+D:%......#######.###+###...%
+D:%..###.#,#,#,#.#^^^^^#...%
+D:%.##&###+#+#+###^###^###.%
+D:%.+^^^+^^^^^^^+^^#9#^+,#.%
+D:%.##&###+#+#+###^###^###.%
+D:%..###.#,#,#,#.#^^^^^#...%
+D:%......#######.###+###...%
+D:%................#,#.....%
+D:%................###.....%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:55:Lesser vault (arena)
+X:7:5:15:17
+D:%%%%%%%%%%%%%%%%%
+D:%...............%
+D:%.+###########+.%
+D:%.#...........#.%
+D:%.#.####^####.#.%
+D:%.#.#,,,,,,,#.#.%
+D:%.#.#,,,,,,,#.#.%
+D:%.#.^,,,,,,,^.#.%
+D:%.#.#,,,,,,,#.#.%
+D:%.#.#,,,,,,,#.#.%
+D:%.#.####^####.#.%
+D:%.#...........#.%
+D:%.+###########+.%
+D:%...............%
+D:%%%%%%%%%%%%%%%%%
+
+N:56:Lesser vault (monster wc)
+X:7:5:12:12
+D:%%%%%%%%%%%%
+D:%..#######.%
+D:%..#.&&&&#.%
+D:%..#^#####.%
+D:%.##...+,#.%
+D:%.#*...###.%
+D:%.#*...+,#.%
+D:%.#*...###.%
+D:%.##...+,#.%
+D:%..#+#####.%
+D:%..........%
+D:%%%%%%%%%%%%
+
+N:57:Lesser vault ('not' 'and')
+X:7:5:11:15
+D:%%%%%%%%%%%%%%%
+D:%.............%
+D:%..#########..%
+D:%.#+,,,#,,,+#.%
+D:%.#,#,#+#,#,#.%
+D:%.#,,#+*+#,,#.%
+D:%.#,#,#+#,#,#.%
+D:%.#+,,,#,,,+#.%
+D:%..#########..%
+D:%.............%
+D:%%%%%%%%%%%%%%%
+
+N:58:Lesser vault (brain's lair)
+X:7:5:18:17
+D:%%%%%%%%%%%%%%%%%
+D:%...............%
+D:%.#############.%
+D:%.#...........#.%
+D:%.#.####^####.#.%
+D:% #.#...&...#.#.%
+D:%.#.#.#####.#.#.%
+D:%.#.#.#,,,#.#.#.%
+D:%.#.#.#,,,#.#.#.%
+D:%.#.#.#,,,#.#.#.%
+D:%.#.#.##+##.#.#.%
+D:%.^.#..#^#..#.^.%
+D:%.####.#^#.####.%
+D:%.#,,#.#+#.#,,#.%
+D:%.#,,+..&..+,,#.%
+D:%.#############.%
+D:%...............%
+D:%%%%%%%%%%%%%%%%%
+
+N:59:Lesser vault (yin-yang)
+X:7:5:17:16
+D:%%%%%%%%%%%%%%%%
+D:%..............%
+D:%.#+##########.%
+D:%.#&#^^^^^^^^#.%
+D:%.#.#^######^#.%
+D:%.#&#^#****#^#.%
+D:%.#.#^#*,**#^#.%
+D:%.#&#^#****#^#.%
+D:%.#.#+####+#^#.%
+D:%.#&#,,,,#.#^#.%
+D:%.#.#,,*,#&#^#.%
+D:%.#&#,,,,#.#^#.%
+D:%.#.######&#^#.%
+D:%.#&.&.&.&.#^#.%
+D:%.##########+#.%
+D:%..............%
+D:%%%%%%%%%%%%%%%%
+
+N:60:Greater vault (der el bahri)
+X:8:35:28:45
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X***X,,X^^^^^^^^X9988899X^^^^^^^^+******X.%
+D:%.X***X,,+^..,...^X9999999X^...,..^XXXX***X.%
+D:%.X***XXXX^......^XXXX+XXXX^......^X**XXXXX.%
+D:%.X^^^X,,X^..,...^^^^^^^^^^^...,..^X******X.%
+D:%.X@@@X,,X^.......................^X,,,,,,X.%
+D:%.X^^^X,,X^..,..,..,..,..,..,..,..^X,,,,,,X.%
+D:%.X^^^X++X^.......................^X,,,,,,X.%
+D:%.X^^^X^^X^..,..,..,..,..,..,..,..^X,,,,,,X.%
+D:%.X^^^^^^+^.......................^X,,,,,,X.%
+D:%.X^^^^^^X^..,..,..,..,..,..,..,..^X,,,,,,X.%
+D:%.XXX+XXXX^.......................^X,,,,,,X.%
+D:%.X***X**X^..,..,..,..,..,..,..,..^X,,,,,,X.%
+D:%.XXXXX**X^.......................^X,,,,,,X.%
+D:%.X,,,,,,+^.......................^+,,,,,,X.%
+D:%.X,,,,,,+^^^^^^^^^^^^^^^^^^^^^^^^^+,,,,,,X.%
+D:%.XXXXXXXXXXXXXXXXXXX+++XXXXXXXXXXXXXXXXXXX.%
+D:%.X.............^^^^^^^^^^^^..............X.%
+D:%.X................^^^^^^^................X.%
+D:%.XXXXXXXXXXXXXXXXXXX^^^XXXXXXXXXXXXXXXXXXX.%
+D:%.X.................X.^.X.................X.%
+D:%.X.X.X.X.X.X.X.X.X.X^^^X.X.X.X.X.X.X.X.X.X.%
+D:%.X.................X.^.X.................X.%
+D:%.X.X.X.X.X.X.X.X.X.X^^^X.X.X.X.X.X.X.X.X.X.%
+D:%.X.................X.^.X.................X.%
+D:%...........................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:61:Lesser vault (der el bahri sanctuary)
+X:7:5:12:22
+D:%%%%%%%%%%%%%%%%%%%%%%
+D:%....................%
+D:%..........###.......%
+D:%..........#,#.......%
+D:%..###.###.#,#.......%
+D:%.##,###,###,#######.%
+D:%.+^^^^^^^+^^^+,,,,#.%
+D:%.+^^^^^^^+^^^+,,,,#.%
+D:%.######+###########.%
+D:%......###...........%
+D:%....................%
+D:%%%%%%%%%%%%%%%%%%%%%%
+
+N:62:Greater vault (hypostyle of ramses III)
+X:8:40:38:34
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X888888+*******X*******+88888X.%
+D:%.XXXXXXXXX+XXXXXXXXXXX+XXXXXXXX.%
+D:%.X99+***+^^^+^^^^^^^+^^^+**+99X.%
+D:%.XXXXXXXXXXXXXX+++XXXXXXXXXXXXX.%
+D:%.X**X,,X,,+^X^^^^^^^X^X,,,+***X.%
+D:%.X**X,,X,,X^X^X...X^X^X,,,XXXXX.%
+D:%.X+XX,,X,,X^X^..@..^X^X,,,X***X.%
+D:%.X,,X,,XXXX^X^X...X^X^X^^^X***X.%
+D:%.X,,X,,X,*X^X^^^^^^^X^XX+XXXX+X.%
+D:%.X+XXX+X+XX^XXX+++XXX^X^^X,,,,X.%
+D:%.X^^^^^X^^+^^^^^^^^^^^+^^X,**,X.%
+D:%.X+XXXXXXXX^X.X...X.X^XXXX,**,X.%
+D:%.X^^^^^X,,X^.........^X**X,**,X.%
+D:%.X^X,X^X,,X^X.X.@.X.X^X++X,,,,X.%
+D:%.X^^^^^X,,X^.........^+^^X^^^^X.%
+D:%.XXX+XXX++X^XXX...XXX^XXXX+XXXX.%
+D:%.X^^^^^^^^+^....&....^+****+^^X.%
+D:%.XXXXXXXXXX^X.X...X.X^XXXXXXX^X.%
+D:%.X^^^^^+^^X^.........^X^^^^^^^X.%
+D:%.X^#X#^X^^X^X.X.&.X.X^X^,*XXXXX.%
+D:%.X^X9X^X^^X^.........^X^,*X***X.%
+D:%.X^#X#^X^^X^X.X...X.X^X^,*XXX+X.%
+D:%.X^^^^^X^^X^^^^^^^^^^^X^^^X,,,X.%
+D:%.XXXXXXX++XXXXX+++XXXXXX+XX,,,X.%
+D:%.X*****+^^^^^^^^^^^^^^^^^+,,,,X.%
+D:%.XXXXXXX^.X.X.X...X.X.X.^XXXXXX.%
+D:%.X,,X,,X^...............^+,,,,X.%
+D:%.X,,X,,X^.X.X.X...X.X.X.^XXXXXX.%
+D:%.X++X++X^.....&.&.&.....^+,,,,X.%
+D:%.X^^^^^+^.X.X.X...X.X.X.^XXXXXX.%
+D:%.X++X++X^...............^+,,,,X.%
+D:%.X,,X,,X^.X.X.X...X.X.X.^XXXXXX.%
+D:%.X,,X,,X^^^^^^^^^^^^^^^^^+,,,,X.%
+D:%.XXXXXXXXXXXXXX+++XXXXXXXXXXXXX.%
+D:%................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:63:Lesser vault (amada temple)
+X:7:10:15:30
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%............................%
+D:%.######################.....%
+D:%.#**#,,,,+^^#.....&...#.....%
+D:%.#**#,,,,#^^#...&.....#.....%
+D:%.#+#######^^#..#.#.#.##.....%
+D:%.#,,,,,,,#^^#^^^^^^^^^#####.%
+D:%.#,,,,,,,+^^+^^^^^^^^^+^^^+.%
+D:%.#,,,,,,,#^^#^^^^^^^^^#####.%
+D:%.#+#######^^#..#.#.#.##.....%
+D:%.#**#,,,,#^^#...&..&..#.....%
+D:%.#**#,,,,+^^#.........#.....%
+D:%.######################.....%
+D:%............................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:64:Lesser vault (amenhotep I)
+X:7:10:18:34
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%................................%
+D:%...............######...........%
+D:%...............#.&&.#...........%
+D:%.###############.##.#...........%
+D:%.#**#,,,+^^^^^^^^##.###.........%
+D:%.#**#,,,#^^^^^^^^^^...#########.%
+D:%.##+#####^^#######^&#.#.......#.%
+D:%.#,,,,,,+^^+,*,*,+^^^^^^^^^^^^+.%
+D:%.#,,,,,,+^^+*,*,*+^^^^^^^^^^^^+.%
+D:%.##+#####^^#######^&#.#.......#.%
+D:%.#**#,,,#^^^^^^^^^^...#########.%
+D:%.#**#,,,+^^^^^^^^##.###.........%
+D:%.###############.##.#...........%
+D:%...............#.&&.#...........%
+D:%...............######...........%
+D:%................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:65:Lesser vault (hathor chapel)
+X:7:10:21:19
+D:%%%%%%%%%%%%%%%%%%%
+D:%.................%
+D:%......##+###.....%
+D:%.######^^^######.%
+D:%.#,,#^#...#^#,,#.%
+D:%.#,,+^..*..^+,,#.%
+D:%.#,,#^#...#^#,,#.%
+D:%.####^.....^####.%
+D:%....#^^^^^^^#....%
+D:%....#.##+##.#....%
+D:%....#&#^^^#&#....%
+D:%....###,^,###....%
+D:%....#,,,,,,,#....%
+D:%....####+####....%
+D:%....#,,,,,,,#....%
+D:%....####+####....%
+D:%....#,,,,,,,#....%
+D:%....####+####....%
+D:%.....#*****#.....%
+D:%.....#######.....%
+D:%%%%%%%%%%%%%%%%%%%
+
+
+N:66:Lesser vault (osiris halls)
+X:7:10:17:34
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%................................%
+D:%.###########################....%
+D:%.#*,+^^^^^#^^^^^^^^^^^^+,,*#....%
+D:%.####^#.#^#^#.#.#.#.#.^#####....%
+D:%.#*,+^.&.^+^..&...&...^+,,*#....%
+D:%.####^#.#^#^#.#.#.#.#.^#####....%
+D:%.#*,+^^^^^#^^^^^^^^^^^^+,,*#....%
+D:%.####################++########.%
+D:%.#,,#,,#,,#,,#,,#,,#....#,,#,,#.%
+D:%.#,,#,,#,,#,,#,,#,,#....#,,#,,#.%
+D:%.#,,#,,#,,#,,#,,#,,#.&..#,,#,,#.%
+D:%.#++#++#++#++#++#++#....#++#++#.%
+D:%.#^^^^^^^^^^^^^^^^^^^^^^^^^^^^+.%
+D:%.##############################.%
+D:%................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:67:Lesser vault (temple of sety)
+X:7:10:22:25
+D:%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.##########+##########.%
+D:%.#,,,,,,,#^^^#,,,,,,,#.%
+D:%.#,,,,,,,+^^^+,,,,,,,#.%
+D:%.#########^^^#########.%
+D:%.#,,,,,,,#^^^#,,,,,,,#.%
+D:%.#,,,,,,,+^^^+,,,,,,,#.%
+D:%.#########+###########.%
+D:%.#^^^+^^^+^^^^^#,.,.,#.%
+D:%.#.#^#^#.#^#.#.#.#.#.#.%
+D:%.#.&^#^.&#^.&..#,.,.,#.%
+D:%.#.#^+^#.#^#.#.#.#.#.#.%
+D:%.#^^^#^^^#^^^^^#,.,.,#.%
+D:%.#+#+###+###+###.#.#.#.%
+D:%.#,#^^^^^^^^^^^#,.,.,#.%
+D:%.#,#.#.#.#.#.#.#+###^#.%
+D:%.#,#...........#,**#^#.%
+D:%.#*#.#.#.#.#.#.#####+#.%
+D:%.#*#^^^^^^^^^^^+&^^^&#.%
+D:%.#######+#############.%
+D:%.......................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:68:Lesser vault (temple at abydos)
+X:7:10:21:23
+D:%%%%%%%%%%%%%%%%%%%%%%%
+D:%^^^^^^^^^^^^^^^^^^^^^%
+D:%^#+#+#####+#####+#+#^%
+D:%^#,#,#^^^^^^^^^#,#,#^%
+D:%^#,#,#^#.#.#.#^#,#,#^%
+D:%^#,#,#^...&...^#,#,#^%
+D:%^#####^#.#.#.#^#####^%
+D:%^#*,,+^^^^^^^^^+,,*#^%
+D:%^#########+#########^%
+D:%^#*,,+^^^^^^^^^+,,*#^%
+D:%^#####^#.#.#.#^#####^%
+D:%^#*,,+^...&...^+,,*#^%
+D:%^#####^#.#.#.#^#####^%
+D:%^#*,,+^^^^^^^^^+,,*#^%
+D:%^##+###+##+##+###+##^%
+D:%^#,,,,#,#^^^#,#,,,,#^%
+D:%^#,##,#,#,#,#,#,##,#^%
+D:%^#,,,,#,#***#,#,,,,#^%
+D:%^###################^%
+D:%^^^^^^^^^^^^^^^^^^^^^%
+D:%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:69:Greater vault (Spiral castle)
+X:8:35:31:25
+D:%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.......................%
+D:%.XXXXX...........XXXXX.%
+D:%.X,,,X...........X,,,X.%
+D:%.X,,,X...........X,,,X.%
+D:%.XXX+XXXXXXXXXXXXX+XXX.%
+D:%...X^^^^^^^^^^^^^^^X...%
+D:%...X^XXXXXXXXXXXXX^X...%
+D:%...X^X^^^^^^^^^^^+^X...%
+D:%...X^X^XXXXXXXXXXX^X...%
+D:%...X^X^X&..&....&X^X...%
+D:%...X^X^X.XXXXXXX.X^X...%
+D:%...X^X^X.X^^^^^X.X^X...%
+D:%...X^X^X&X+XXX^X.X^X...%
+D:%...X^X^X.X*9*X^X&X^X...%
+D:%...+^X^X,X989X^X.X^+...%
+D:%...X^X^X.X*9*X^X.X^X...%
+D:%...X^X^X.XXX+X^X,X^X...%
+D:%...X^X^X..&..X^X.X^X...%
+D:%...X^X.XXXXXXX^X.X^X...%
+D:%...X^X^^^^^^^^^X&X^X...%
+D:%...X^XXXXXXXXXXX.X^X...%
+D:%...X^+...&..&...,X^X...%
+D:%...X^XXXXXXXXXXXXX^X...%
+D:%...X^^^^^^^^^^^^^^^X...%
+D:%.XXX+XXXXXXXXXXXXX+XXX.%
+D:%.X,,,X...........X,,,X.%
+D:%.X,,,X...........X,,,X.%
+D:%.XXXXX...........XXXXX.%
+D:%.......................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:70:Lesser vault (temple of dendereh)
+X:7:10:20:22
+D:%%%%%%%%%%%%%%%%%%%%%%
+D:%....................%
+D:%.##################.%
+D:%.#***+^&#**#&^+***#.%
+D:%.#***#^^#,,#^^#***#.%
+D:%.#####++#++#++#####.%
+D:%.#,,,+^^^^^^^^+,,,#.%
+D:%.#####^######^#####.%
+D:%.#,,,+^#,,,,#^+,,,#.%
+D:%.#####^#,**,#^#####.%
+D:%.#,,,+^#,**,#^#***#.%
+D:%.#####^#,**,#^#^^^#.%
+D:%.#,,,+^#,**,#^##+##.%
+D:%.#####^#,,,,#^#,,,#.%
+D:%.#,,,#^##++##^#&,&#.%
+D:%.#,,,+^^^^^^^^##+##.%
+D:%.#,,,#^^^^^^^^^^^^#.%
+D:%.########++########.%
+D:%....................%
+D:%%%%%%%%%%%%%%%%%%%%%%
+
+
+N:71:Greater vault (Karnak, part I)
+X:8:35:24:44
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X*****+^+^^^^+^^^X,,,X***X***X***X,****X.%
+D:%.XXXXXXXXX^XX^X,,,X,X,X,X,X,X,X*X*X,X*X*X.%
+D:%.X,X,X,X,X^..^X,,,X,,,X^^^X^^^X**@+^,,,,X.%
+D:%.X^^^^^^^+^@.^X,,,XX+XX+XXXXX+XXXXX+XXXXX.%
+D:%.X,X^^^X,X^XX^X***X^^^+^^^^^^^+,,*X^^^^^X.%
+D:%.XXXX+XXXX^^^^X,,,XXXXX^X.X.X^XXXXX^X^X^X.%
+D:%.X^^^^^^^XXXXXX,,,X,,,X^..@..^+,,*X^^^^&X.%
+D:%.X^X,X,X^X,,,,X,,,X,X,X^X.X.X^XXXXX^XXXXX.%
+D:%.X^^^^^^^X,,,,X^^^X,,,X^^^^^^^+,,*X^+,,*X.%
+D:%.XXXX+XXXXX++XXX+XXX+XXXXX+XXXXXXXX^XXXXX.%
+D:%.X*,+^^^^^^^^^^^^^^^^^^^^^^^^^+,,*X^+,,*X.%
+D:%.XXXX^X.X.X.X.X.X.X.X.X.X.X.X^XXXXX^XXXXX.%
+D:%.X*,+^.........&.....&.......^X,,*X^+,,*X.%
+D:%.XXXX^X.X.X.X.X.X.X.X.X.X.X.X^+,X*X^XXXXX.%
+D:%.X**X^.......................^X,,*X^+,,*X.%
+D:%.X,,+^X.X.X.X.X.X.X.X.X.X.X.X^XXXXX^XXXXX.%
+D:%.XXXX^.........&.....&.......^+^^^^^+,,*X.%
+D:%.X,,+^X.X.X.X.X.X.X.X.X.X.X.X^X,X,X^XXXXX.%
+D:%.X**X^^^^^^^^^^^^^^^^^^^^^^^^^X.*.X^+,,*X.%
+D:%.XXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXX.%
+D:%..........................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:72:Lesser vault (Karnak, part II)
+X:7:10:20:29
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%...........................%
+D:%.###+######################%
+D:%.#,+^#,,#,,#^+^+^#*#,#^+,#.%
+D:%.###.#,,#,,#^#^#^#*#,#^###.%
+D:%.#,+^#,,#,,#^#^#^#*#+#^+,#.%
+D:%.###.#++#++#^#^#^#+#.#^###.%
+D:%.#,+^#^^^^^+^#^#^+,,,#^+,#.%
+D:%.###.#^^^^^+^#^#+#+###^###.%
+D:%.#,+^#+#+#+#^#^#,#^+,#^+,#.%
+D:%.###.#,#,#,#^#^#######^###.%
+D:%.#,+^#######+#,,+,,,,,^+,#.%
+D:%.###^^^^^^#,^#,,+,,,,#^###.%
+D:%.#,+^#,#*^##+####,,,,,^+,#.%
+D:%.###^,,,*^#^^^^^#,,,,#^###.%
+D:%.#,+^#,#*^#,,,,^+,,,,,^+,#.%
+D:%.###^^^^^^#^^^^^#,,,,,^###.%
+D:%...##########+##########...%
+D:%...........................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:73:Greater vault (mortuary temple of sety)
+X:8:35:25:34
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%................................%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X^+,,***X,,+^^^^^+,,X*X,,X*X9X.%
+D:%.X^XXXXXXX,,X^X*X^X,,X,X,,X*X9X.%
+D:%.X^+,,***XXXX^*@*^X,,X^X,,X@X9X.%
+D:%.X^XXXXXXX,,X^X*X^XXXX^X,,X^X9X.%
+D:%.X^+,,***X,,+^^^^^+..X^X,,X^X^X.%
+D:%.X^XXXXXXXXXXXX+XXXXXX+X++X+X+X.%
+D:%.X^X*X*X*X**X,+.+,X**X^^^^+^^^X.%
+D:%.X+X,X,X,X,,XXX.XXX,,X^^^^+^^^X.%
+D:%.X^X^X^X^X,,X,X.X,X,,X^^XXXXXXX.%
+D:%.X^X+X+X+X^^X,X.X,X^^X^^X,,,,,X.%
+D:%.X^+^^^^^X++X+X+X+X++X^^X,X,X,X.%
+D:%.X^XXXXXXX^^^^^^^^^^^X^^X,,,,,X.%
+D:%.X^X*X*X*XXXX^...^XXXX^^X,X,X,X.%
+D:%.X^X,X,X,X,,+^X*X^+,,X^^X,,,,,X.%
+D:%.X^X^X^X^XXXX^.&.^XXXX^^X,X,X,X.%
+D:%.X^X+X+X+X,,+^X*X^+,,X^^X,,,,,X.%
+D:%.X^X^^^^^XXXX^...^XXXX^^X,X,X,X.%
+D:%.X^X*X,X*X,,X^X*X^X,,X^^X,,,,,X.%
+D:%.X^+^^^^^+^^+^^^^^+^^+^^X,,,,,X.%
+D:%.XXXXXXXXXXXXXX+XXXXXXXXXXX+XXX.%
+D:%................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:74:Lesser vault (edfu)
+X:7:5:15:15
+D:%%%%%%%%%%%%%%%
+D:%.............%
+D:%.###########.%
+D:%.#,+^#,#^+,#.%
+D:%.###+#+#+###.%
+D:%.#,+^...^+,#.%
+D:%.###.###.###.%
+D:%.#,+.#*#.+,#.%
+D:%.###.#,#.###.%
+D:%.#,+.#^#.#,#.%
+D:%.###^...^#+#.%
+D:%.#,+,#+#,+,#.%
+D:%.#####^#####.%
+D:%.............%
+D:%%%%%%%%%%%%%%%
+
+N:75:Greater vault (kom ombo)
+X:8:40:36:30
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X****X,99,X9889X,99,X****X.%
+D:%.X****X,,,,X9999X,,,,X****X.%
+D:%.X+XXXX+XXXX+XXXX+XXXX+XXXX.%
+D:%.X@^^^^^^^^^^^^^^^^^^^^^^@X.%
+D:%.X^XXXXXXXXXXXXXXXXXXXXXX^X.%
+D:%.X^X**X,,X,,X,,X,,X,,X**X^X.%
+D:%.X^X,,X+XX+XX+XXX+XX+X,,X^X.%
+D:%.X^X,^+^^^^^^^^^^^^^^+^,X^X.%
+D:%.X^XXXX^XX+XX^^XX+XX^XXXX^X.%
+D:%.X^X,,X^X,,,X^^X,,,X^+^,X^X.%
+D:%.X^X,,X^X***X^^X***X^X,*X^X.%
+D:%.X^X,,X^X9X9X^^X9X9X^XXXX^X.%
+D:%.X^X,,X^X***X^^X***X^+^,X^X.%
+D:%.X^X++X^X,,,X^^X,,,X^X,*X^X.%
+D:%.X^X^^X^XX+XX^^XX+XX^XXXX^X.%
+D:%.X^X^^+^^^^^^^^^^^^^^X,*X^X.%
+D:%.X^X,,X^^^^^^^^^^^^^^+^,X^X.%
+D:%.X^XXXXXXX+XXXXXX+XXXXXXX^X.%
+D:%.X^X,X,X,,,,,XX,,,,,X***X^X.%
+D:%.X^X,X^X,***,++,***,+*@*X^X.%
+D:%.X^X,+^+,,,,,XX,,,,,X***X^X.%
+D:%.X^XXX+XXX+XXXXXX+XXXX+XX^X.%
+D:%.X^X*,^X,,,,,XX,,,,,+^^*X^X.%
+D:%.X^X,,^+,,*,,++,,*,,XX+XX^X.%
+D:%.X^X*,*X,,,,,XX,,,,,X,,,X^X.%
+D:%.X^XXXXXXX+XXXXXX+XXXXXXX^X.%
+D:%.X^X^^^^^^^^^^^^^^^^^^^^X^X.%
+D:%.X^X^^XX,XX^^XX^^XX,XX^^X^X.%
+D:%.X^X^^^^^^^^^^^^^^^^^^^^X^X.%
+D:%.X^+^^XX,XX^^XX^^XX,XX^^+^X.%
+D:%.X,X^^^^^^^^^^^^^^^^^^^^X,X.%
+D:%.XXXXXXXXX+XXXXXX+XXXXXXXXX.%
+D:%............................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:76:Lesser vault (belvoir keep)
+X:7:5:19:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%...................%
+D:%.###...........###.%
+D:%.#,######+######,#.%
+D:%.##+^^^^+&+^^^^+##.%
+D:%..#&^####+####^.#..%
+D:%..#.^#,^+^+,,#^.#..%
+D:%..#.^#,^#^#,,#^.#..%
+D:%..#.^#,*#^##+#^.#..%
+D:%..#.^#,^#^#,,#^&#..%
+D:%..#.^#,^+^#*,#^.#..%
+D:%..#.^####+####^.#..%
+D:%.##+^^^^^^^^^^^+##.%
+D:%.#,######+######,#.%
+D:%.###...#,*,#...###.%
+D:%.......#*,*#.......%
+D:%.......#####.......%
+D:%...................%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+#N:77:Lesser vault (Pattern)
+#X:8:30:31:47
+#D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+#D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+#D:%X@&+.............................+&@X,,,,,,X.%
+#D:%XXXX.p...bad.bad.bad.bad.cbb.....XXXX,,,,,,X.%
+#D:%X@&X.a.dcb.dcb.dcb.dcb.dcc.baa...X&@X,,,,,,X.%
+#D:%XX+X.b.d.....................ad..X+XX,,,,,,X.%
+#D:%X....c.a.dabcdabcda...........dc....X,,,,,,X.%
+#D:%X....d.b.d........a..cbadcba...c....X,,,,,,X.%
+#D:%X...ad.c.c.dcbadcba..c.....ad..cb...X++XXX+X.%
+#D:%X...a..d.b.d.........cdabc..d...b...X^^X^^^X.%
+#D:%X...b.ad.a.a..abcd.......cd.dcb.a...X**X^X+X.%
+#D:%X..cb.a..d.b..a..d..adcb..d...b.ad..X,,X^X^X.%
+#D:%X..c..b..c.c..d.ad..a..ba.a...a..d..X&&X^X&X.%
+#D:%X.dc.cb..b.d..c.a..PPP..a.a.cda..dc.X^^X^X+X.%
+#D:%X.d..c...a.a..b.b.PPAPP.d.b.c.....c.+^^X^X*X.%
+#D:%X.da.cd..d.b..a.c.PABAP.c.b.cba..bc.X^^X^X*X.%
+#D:%X..a..d..c.c..d.d.PPAPP.b.c...a..b..X&&X^XXX.%
+#D:%X..ab.da.b.d..c.a..PPP..a.c...d.ab..X,,X^X,X.%
+#D:%X...b..a.a.a..b.ab.....da.d..cd.a...XXXX+X+X.%
+#D:%X...c..b.d.b..a..bcd.bcd..a.bc..d...X**^^^,X.%
+#D:%X...cd.bcd.c..ad...dab...ba.b..cd...X,,,,,,X.%
+#D:%X....d.....d...dc......dcb..a..c....X,,,XXXX.%
+#D:%X....da....da...cbadcbad...da.bc....X,,,+,,X.%
+#D:%X.....a.....abc...........cd..b.....X,,,X,,X.%
+#D:%XX+X..ab......cdabcdabcdabc..ab..X+XXXXXX,,X.%
+#D:%X@&X...bc...................da...X&@X,,,,,,X.%
+#D:%XXXX....cddaabbccddaabbcdabcd....XXXX,,,,,&+.%
+#D:%X@&+.............................+&@X****&&+.%
+#D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX++X.%
+#D:%.............................................%
+#D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+#
+#
+#N:78:Greater vault (Pattern 2)
+#X:8:30:29:45
+#D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+#D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+#D:%X8+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^+8X%
+#D:%X+XXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXX+X%
+#D:%X^X.....................................X^X%
+#D:%X^X.................p...................X^X%
+#D:%X^X.........adcbadcba.dcbadcbad.........X^X%
+#D:%X@X......dcba.........d.......dcba......X@X%
+#D:%X,X...baad.....abcdab.dabcda.....addc...X,X%
+#D:%X,X..cb.....bcda....b......abcd.....cb..X,X%
+#D:%X,X.dc....dab....adcb.adcb....dab....ba.X,X%
+#D:%X,X.d....cd....cba....a..bad....bc....a.X,X%
+#D:%X,X.a...bc...adc...PPPPP...dcb...cd...d.X,X%
+#D:%X,X.b...b....a....PPPAPPP....b....d...c.X,X%
+#D:%X,X.c...a....b....PPABAPP....a....a...c.X,X%
+#D:%X,X.d...d....c....PPPAPPP....d....b...b.X,X%
+#D:%X,X.a...dc...cda...PPPPP...bcd...cb...b.X,X%
+#D:%X,X.b....cb....abc.......dab....dc....a.X,X%
+#D:%X,X.bc....bad....cddaabbcd....bad....da.X,X%
+#D:%X,X..cd.....dcba...........adcb.....cd..X,X%
+#D:%X,X...dabc.....adcbadcbadcba.....dabc...X,X%
+#D:%X^X......cdab.................abcd......X^X%
+#D:%X^X.........bccddaabcdabcdabcda.........X^X%
+#D:%X^X.....................................X^X%
+#D:%X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X%
+#D:%X9+^^^@,,,,,&,,,,,,,,&,,,,,,,&,,,,,,@^^^+9X%
+#D:%XXXXXXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXX%
+#D:%...........................................%
+#D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+#
+#
+#
+#N:79:Greater vault (Pattern 2)
+#X:8:30:30:42
+#D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+#D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+#D:%.X9+^^^^^^^^^^^^^^^^^+,,,,,,,,,,,,,,,+8X%
+#D:%.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X%
+#D:%.X,X.................................X^X%
+#D:%.X,X....adcbadcbadcbadcbadcbadc......X^X%
+#D:%.X,X...ba.....................cbad...X^X%
+#D:%.X,X..cb..bad.bad.bad.bad.bad....d...X^X%
+#D:%.X,X..c..cb.dcb.dcb.dcb.dcb.dcba.dc..X^X%
+#D:%.X&X..d.dc.....................a..c..X^X%
+#D:%.X&X..d.d..bbcc...cbadcba..adc.ad.b..X^X%
+#D:%.X+X..a.a.ab..cd.dc.....ad.a.c..d.a..X+X%
+#D:%.X^X..a.b.a....d.d..PPP..d.b.c.cd.d..X@X%
+#D:%&X^X..b.c.ad...a.a.PPAPP.c.c.b.c..dc.X9X%
+#D:%.+^X..b.d..dc..a.b.PABAP.b.d.b.cb..c.+8X%
+#D:%&X^X..c.a...cb.b.c.PPAPP.a.a.a..b..b.X9X%
+#D:%.X^X..d.b....b.c.d..PPP..d.b.d.ab..a.X@X%
+#D:%.X+X..a.c.cdab.d.da..d...dcb.d.a..da.X+X%
+#D:%.X&X..b.d.c....a..abcd.......c.ad.d..X^X%
+#D:%.X&X..c.a.cbad.ab......abc..bc..d.c..X^X%
+#D:%.X,X..d.b....d..bcdabcda.cdab..cd.b..X^X%
+#D:%.X,X..a.bcda.c................bc..a..X^X%
+#D:%.X,X..b....abc..dab.dab.dab.dab..da..X^X%
+#D:%.X,X..bcda.....cd.bcd.bcd.bcd...cd...X^X%
+#D:%.X,X.....abcdabc..............pbc....X^X%
+#D:%.X,X.................................X^X%
+#D:%.X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X%
+#D:%.X9+^^^^^^^^^^^^^^^^^+,,,,,,,,,,,,,,,+8X%
+#D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+#D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+# XXX
+
+# XXX
+
+N:82:Lesser vault (checker.oard)
+X:7:5:13:21
+D:%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXX%
+D:%X&#9#&#9#&#9#&#9#&+%
+D:%X#XXXXXXXXXXXXXXXXX%
+D:%X9#&#9#&#9#&#9#&#9X%
+D:%XXXXXXXXXXXXXXXXX#X%
+D:%X&#9#&#88888#&#9#&X%
+D:%X#XXXXXXXXXXXXXXXXX%
+D:%X9#&#9#&#9#&#9#&#9X%
+D:%XXXXXXXXXXXXXXXXX#X%
+D:%+&#9#&#9#&#9#&#9#&X%
+D:%XXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%
+
+
+N:83:Lesser vault (spiral checkers)
+X:7:5:14:17
+D:%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXX%
+D:%X&#9#&#9#&#9#&X%
+D:%+XXXXXXXXXXXX#X%
+D:%XXX@#9#@#9#@X9X%
+D:%X9#XXXXXXXX#X#X%
+D:%X#XXX98X89#9X&X%
+D:%X&X9#98X89XXX#X%
+D:%X#X#XXXXXXXX#9X%
+D:%X9X@#9#@#9#@XXX%
+D:%X#XXXXXXXXXXXX+%
+D:%X&#9#&#9#&#9#&X%
+D:%XXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%
+
+
+N:84:Greater vault (monstrosity)
+X:8:25:17:28
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X^@^@^@^@^@#X9X9X9X9X9X9XX%
+D:%X#XXXXXXXXXX9X9X9X9X9X9X9X%
+D:%XX9X9X9X9XXXXXXXXXXXXXXX#X%
+D:%X9X9X9X9X9X##############X%
+D:%XXXXXXXXX#X#XXXXXXXXXXXXXX%
+D:%X&,&X&,&X&X8888X&,&X&,&X&+%
+D:%X,X,X,X,X,XXXXXX,X,X,X,X,X%
+D:%+&X&,&X&,&X8888X&X&,&X&,&X%
+D:%XXXXXXXXXXXXXX#X#XXXXXXXXX%
+D:%X##############X9X9X9X9X9X%
+D:%X#XXXXXXXXXXXXXXX9X9X9X9XX%
+D:%X9X9X9X9X9X9X9XXXXXXXXXX#X%
+D:%XX9X9X9X9X9X9X#@^@^@^@^@^X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:85:Cyclone
+X:8:40:23:91
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%XXXX^^^^^^^^^XXX^^^^^^^^^XXX^^^#XXX8.X.&.X.&.X..*X+^.^.^.^.^.^..XX*^.^.^.^.^.^.^.^.^.^#8X%
+D:%XX***XXX...XXX...XXX...XXX...XXX^^X*.X^X^X^X^X^X.*XXXXXXXXXXXXX..XX^^XXXXXXXXXXXXXXXXXX#X%
+D:%X88XXX...XXX...XXX...XXX...XXX^^^,X*.X^X^X^X^X^X..*XX8@,,,,,,@XX,.XX^^..........@&.,**X.X%
+D:%XXXX@&@XXX@@@XXX&&&XXX&@&XXX^^^,..X*.X^X^X^X^X^X...@X@........@X,..X...........@&..,**X.X%
+D:%XX...XXX...XXX...XXX...XXX^^^,....X*.X^X^X^X^X^X...@X*..*9*..@XX^^XXXX*.......@&@..,**X.X%
+D:%X*.XXX...XXX...XXX...XXX^^^^....,,X*.&.X.&.X.&.X..*XX*.....,XXX.XXX@@XXX*....@&..@.,**X.X%
+D:%X*XX...XXX...XXX...XXX^^^,.^...XX+XXXXXXXXXXXXXX.*XX*....,,XXX.XXXXXX&&XXX*.@&....@8**X.X%
+D:%X^^^^XXX^^^^^^^^^XXX^^^,..*^.&XX.*X&^^*****^^#X.*XX*....,XXX&&XXX@@X#X*..XX@&.....8@**X.X%
+D:%XXXXXXXXXXXXXXXXXX^^^,...**^.&X8.*X@XXXXXXXXXX^.XX&....XXX&&XXX*****XXX*..XXXXXXXXXXXXX.X%
+D:%#^^^^^^^^^^^^^9#^^^.....*99^.@X8.*#^^^*****8XXXXX8@..XX#@@XX#@@@....8XXX*.+^.^.^.^.^.^+.X%
+D:%XXXXXXXXXXXXXXXXXX^^^,...**^.&X8.*X@XXXXXXXXXX^.XX&....XXX&&XXX*****XXX*..XXXXXXXXXXXXX.X%
+D:%X^^^^XXX^^^^^^^^^XXX^^^,..*^.&XX.*X&^^*****^^#X.*XX*....,XXX&&XXX@@X#X*..XX@&.....8@**X.X%
+D:%X*XX...XXX...XXX...XXX^^^,.^...XX+XXXXXXXXXXXXXX.*XX*....,,XXX.XXXXXX&&XXX*.@&....@8**X.X%
+D:%X*.XXX...XXX...XXX...XXX^^^^....,,X*.&.X.&.X.&.X..*XX*.....,XXX.XXX@@XXX*....@&..@.,**X.X%
+D:%XX...XXX...XXX...XXX...XXX^^^,....X*.X^X^X^X^X^X...@X*..*9*..@XX^^XXXX*.......@&@..,**X.X%
+D:%XXXX@&@XXX@@@XXX&&&XXX&@&XXX^^^,..X*.X^X^X^X^X^X...@X@........@X,..X...........@&..,**X.X%
+D:%X88XXX...XXX...XXX...XXX...XXX^^^,X*.X^X^X^X^X^X..*XX8@,,,,,,@XX,.XX^^..........@&.,**X.X%
+D:%XX***XXX...XXX...XXX...XXX...XXX^^X*.X^X^X^X^X^X.*XXXXXXXXXXXXX..XX^^XXXXXXXXXXXXXXXXXX#X%
+D:%XXXX^^^^^^^^^XXX^^^^^^^^^XXX^^^#XXX8.X.&.X.&.X..*X+^.^.^.^.^.^..XX*^.^.^.^.^.^.^.^.^.^#8X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:86:Miniature Cell
+X:7:5:5:5
+D:%%%%%
+D:%#8#%
+D:%888%
+D:%#8#%
+D:%%%%%
+
+N:87:Castle Death
+X:8:35:20:60
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+XX....+XX...X****X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX....XXX....@X****X%
+D:%X.........................................XXX....&.@X****X%
+D:%X+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX....@.&.@X*99*X%
+D:%X999X..9..X&&&&&X,,,,,X@@@@@X..&..X@@@XXX....&.@.&.@X*99*X%
+D:%X@@@X.&.&.X&&&&&X,,,,,X@@@@@X99.99X9XXX.X..@.&.@.&.@X****X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...X..@.&.@.&.@XX##XX%
+D:%+^...&.&.&+...@.@.@+...@@@@@+.....99+...+..@.&.@.&.8+^^^^X%
+D:%+^...&.&.&+...@.@.@+...@@@@@+.....99+...+..@.&.@.&.8+^^^^X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...X..@.&.@.&.@XX##XX%
+D:%X@@@X.&.&.X&&&&&X,,,,,X@@@@@X99.99X9XXX.X..@.&.@.&.@X****X%
+D:%X999X..9..X&&&&&X,,,,,X@@@@@X..&..X@@@XXX....&.@.&.@X*99*X%
+D:%X+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX+XXXXX....@.&.@X*99*X%
+D:%X.........................................XXX....&.@X****X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX....XXX....@X****X%
+D:%X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+XX....+XX...X****X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:88:Mirrored Quartet
+X:8:20:21:49
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X89@@@XXX,,,,XXX....^^^^^^^....XXX,,,,XXX@@@98X%
+D:%X9@@#XX....XXX**....XXX#XXX....**XXX....XX#@@9X%
+D:%X@XXX&&&&XXX**....@XX&&@&&XX@....**XXX&&&&XXX@X%
+D:%XXX....XXX**......@X**...**X@......**XXX....XXX%
+D:%X@...XXX**.......XXXXX...XXXXX.......**XXX...@X%
+D:%X@.#XX**.......9XX888XX^XX888XX9.......**XX#.@X%
+D:%XXXX&&&&&&&&&&&9#,,,,,X^X,,,,,#9&&&&&&&&&&&XXXX%
+D:%XXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXX%
+D:%%,,,,,,,,,,,,,,,,,,,,,#8#,,,,,,,,,,,,,,,,,,,,,%%
+D:%XXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXX%
+D:%XXXX&&&&&&&&&&&9#,,,,,X^X,,,,,#9&&&&&&&&&&&XXXX%
+D:%X@.#XX**.......9XX888XX^XX888XX9.......**XX#.@X%
+D:%X@...XXX**.......XXXXX...XXXXX.......**XXX...@X%
+D:%XXX....XXX**......@X**...**X@......**XXX....XXX%
+D:%X@XXX&&&&XXX**....@XX&&@&&XX@....**XXX&&&&XXX@X%
+D:%X9@@#XX....XXX**....XXX#XXX....**XXX....XX#@@9X%
+D:%X89@@@XXX,,,,XXX**..^^^^^^^..**XXX,,,,XXX@@@98X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:89:False Wall
+X:8:20:15:64
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%X+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX+X%
+D:%X^+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^+^X%
+D:%X^XXXXXXXXXXXXXXXXXXXXXXXXXX+X##X+XXXXXXXXXXXXXXXXXXXXXXXXXX^X%
+D:%X^+@*&*@*&*@*&*@*&*@*&*@*&*@*X@@X*@*&*@*&*@*&*@*&*@*&*@*&*@+^X%
+D:%X^XXXXXXXXXXXXXXXXXXXXXXXXX+XX,,XX+XXXXXXXXXXXXXXXXXXXXXXXXX^X%
+D:%X^+&.@.&.@.&.@.&.@.&.@.&.@.&X*..*X&.@.&.@.&.@.&.@.&.@.&.@.&+^X%
+D:%X^XXXXXXXXXXXXXXXXXXXXXXXX+XX*..*XX+XXXXXXXXXXXXXXXXXXXXXXXX^X%
+D:%X^+,,,,,,,,,,,,,,,,,,,,,,,,X,,..,,X,,,,,,,,,,,,,,,,,,,,,,,,+^X%
+D:%X^XXXXXXXXXXXXXXXXXXXXXXX+XX##99##XX+XXXXXXXXXXXXXXXXXXXXXXX^X%
+D:%X^+&.@.&.@.&.@.&.@.&.@.&.@X..####..X@.&.@.&.@.&.@.&.@.&.@.&+^X%
+D:%X^XXXXXXXXXXXXXXXXXXXXXX+XX...##...XX+XXXXXXXXXXXXXXXXXXXXXX^X%
+D:%X^+*&*@*&*@*&*@*&*@*&*@*&X&@&@88&@&@X&*@*&*@*&*@*&*@*&*@*&*+^X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:90:Hellpit
+X:8:20:19:60
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%..........................................................%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X@@&....#8.#9....^^&&&&&&&&&&&&###.^^,,......+8*9+,,,,,X.%
+D:%.X@&@.............^^................^^,,......+9*9+,,,,,X.%
+D:%.X&@@.............^^@@@@@@@@@@@###..^^,,......+9*9+,,,,,X.%
+D:%.X.....#8....#9...^^................^^,,......+9*9+,,,,,X.%
+D:%.X................^^&&&&&&&&&&&&###.^^,,.&&&&&+9*9+,,,,,X.%
+D:%.X.......@@@@.....^^................^^,,.&@@@&+9*9+,,,,,X.%
+D:%.#....#8.@@@@.#9..^^@@@@@@@@@@@###..^^,,.&@9@&+8*9+,,,,,X.%
+D:%.X.......@@@@.....^^................^^,,.&@@@&+9*9+,,,,,X.%
+D:%.X................^^&&&&&&&&&&&&###.^^,,.&&&&&+9*9+,,,,,X.%
+D:%.X.....#8....#9...^^................^^,,......+9*9+,,,,,X.%
+D:%.X&@@.............^^@@@@@@@@@@@###..^^,,......+9*9+,,,,,X.%
+D:%.X@&@.............^^................^^,,......+9*9+,,,,,X.%
+D:%.X@@&....#8.#9....^^&&&&&&&&&&&&###.^^,,......+8*9+,,,,,X.%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%..........................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:91:Roundabout Three
+X:8:30:20:97
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X,&,,,,,,,,,,,^^^^^^^^^^......XX.#X.XXX.......X.X.......XXX.X#.XX......^^^^^^^^^^,,,,,,,,,,,&,X%
+D:%X,&,,,,,,,,,,,^^^^^^^^^^.....XX.XX.XXX..XXXX..X.X..XXXX..XXX.XX.XX.....^^^^^^^^^^,,,,,,,,,,,&,X%
+D:%X,&,,,,,,,,,,,XXXXXXXXXX....XX.XX.XXX^^XX@@XX.X&X.XX@@XX^^XXX.XX.XX....XXXXXXXXXX,,,,,,,,,,,&,X%
+D:%X,&&&&&&&&&&XXX********XXX..XX.XX.XXX^^X****X.X&X.X****X^^XXX.XX.XX..XXX********XXX&&&&&&&&&&,X%
+D:%X,,,,,,,,,,XXX..XXXXXX...XX+XX.XX.XXX^^XX&&...X@X...&&XX^^XXX.XX.XX+XX...XXXXXX..XXX,,,,,,,,,,X%
+D:%X@@@@@@@@XXX..XXX****XXX.....XX.XX.XXX..XX...XX+XX...XX..XXX.XX.XX.....XXX****XXX..XXX@@@@@@@@X%
+D:%X9988899XXX..XXX.XXXX@@XXXXXX.XX.XX.XXX..XXXXX^^^XXXXX..XXX.XX.XX.XXXXXX@@XXXX.XXX..XXX9998999X%
+D:%X######XXX**X##&&@**XX&&&@98X^^^^^XX.##X^^^^^^^^^^^^^^^X##.XX^^^^^X89@&&&XX**@&&##X**XXX######X%
+D:%X######XXX**X##&&@**XX&&&@98X^^^^^XX.##X^^^^^^^^^^^^^^^X##.XX^^^^^X89@&&&XX**@&&##X**XXX######X%
+D:%X9988899XXX..XXX.XXXX@@XXXXXX.XX.XX.XXX..XXXXX^^^XXXXX..XXX.XX.XX.XXXXXX@@XXXX.XXX..XXX9998999X%
+D:%X@@@@@@@@XXX..XXX****XXX.....XX.XX.XXX..XX...XX+XX...XX..XXX.XX.XX.....XXX****XXX..XXX@@@@@@@@X%
+D:%X,,,,,,,,,,XXX..XXXXXX...XX+XX.XX.XXX^^XX&&...X@X...&&XX^^XXX.XX.XX+XX...XXXXXX..XXX,,,,,,,,,,X%
+D:%X,&&&&&&&&&&XXX********XXX..XX.XX.XXX^^X****X.X&X.X****X^^XXX.XX.XX..XXX********XXX&&&&&&&&&&,X%
+D:%X,&,,,,,,,,,,,XXXXXXXXXX....XX.XX.XXX^^XX@@XX.X&X.XX@@XX^^XXX.XX.XX....XXXXXXXXXX,,,,,,,,,,,&,X%
+D:%X,&,,,,,,,,,,,^^^^^^^^^^.....XX.XX.XXX..XXXX..X.X..XXXX..XXX.XX.XX.....^^^^^^^^^^,,,,,,,,,,,&,X%
+D:%X,&,,,,,,,,,,,^^^^^^^^^^......XX.#X.XXX.......X.X.......XXX.X#.XX......^^^^^^^^^^,,,,,,,,,,,&,X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:92:The Reward is Worth It
+X:8:20:16:63
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXX##XXXXXXXXXXXXX#XXXXXXXXXXXXX##XXXXXXXXXXXXXXX%
+D:%X@@@@@@@@@@@.XX88XX.......,,,X^X,,,.......XX88XX.@@@@@@@@@@@X%
+D:%X@XXXXXXXXXX..XXXX...XXXX.,,,X^X,,,.XXXX...XXXX..XXXXXXXXXX@X%
+D:%X@X8888888XXX..XX...XXX@XX,,,X^X,,,XX@XXX...XX..XXX8888888X@X%
+D:%X@X88899999XXX.....XXX@@@X..&X^X&..X@@@XXX.....XXX99999888X@X%
+D:%X@X8899.....XXX...XXX@@@@@..&X^X&..@@@@@XXX...XXX.....9988X@X%
+D:%X@X899.......XXX...XXX......&X^X&......XXX...XXX.......998X@X%
+D:%X+XXX...XXX&&&XXX^^^XXX.....&X^X&.....XXX^^^XXX&&&XXX...XXX+X%
+D:%X@@XXX.XXXX&&&&XXX^^^XXX....&X^X&....XXX^^^XXX&&&&XXXX.XXX@@X%
+D:%X^^^^XXX88X^^^^^^XXX^^^XXX..&X^X&..XXX^^^XXX^^^^^^X88XXX^^^^X%
+D:%X&&&&&&&&XX^^^^^^^^XXX...XXX&X^X&XXX...XXX^^^^^^^^XX&&&&&&&&X%
+D:%X,,,,,,,XX,,,,,,,,,,,XXX&&&XXX^XXX&&&XXX,,,,,,,,,,,XX,,,,,,,X%
+D:%X,,,,,,X#@@@@@@@@@@@@@@XXX&&&&&&&&&XXX@@@@@@@@@@@@@@#X,,,,,,X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:93:Little League Treasure Hoard
+X:7:2:5:19
+D:%%%%%%%%%%%%%%%%%%%
+D:%....#...&...#....%
+D:%.#.#.##***##.#.#.%
+D:%....#...&...#....%
+D:%%%%%%%%%%%%%%%%%%%
+
+N:94:Mini Maze
+X:7:2:15:25
+D:%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.......................%
+D:%.X+XXXXXXXXXXXXXXXXXXX.%
+D:%.X.X,.......X........X.%
+D:%.X.X..XXX.X...XXXX.X.X.%
+D:%.X.XX.X...X.XXX..X...X.%
+D:%.X......X.X.X....XXX.X.%
+D:%.X.XXXXX..X.X.X..X,X.X.%
+D:%.X.X,..XXXX.X.X.XX.X.X.%
+D:%.X.XXX........X...XX.X.%
+D:%.X...XXXXXX.XXXXXXX..X.%
+D:%.X.X........X,.......X.%
+D:%.XXXXXXXXXXXXXXXXXXXXX.%
+D:%.......................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:95:Circlular Room
+X:7:2:8:9
+D:%%%%%%%%%
+D:%.......%
+D:%..###..%
+D:%.#+*+#.%
+D:%.#+*+#.%
+D:%..###..%
+D:%.......%
+D:%%%%%%%%%
+
+N:96:Minor Boss Vault
+X:7:3:10:10
+D:%%%%%%%%%%
+D:%........%
+D:%..XXXX..%
+D:%.XX.9XX.%
+D:%.X....X.%
+D:%.XX++XX.%
+D:%.XX^^XX.%
+D:%..X++X..%
+D:%........%
+D:%%%%%%%%%%
+
+N:97:(Lesser) Major Boss Vault
+X:7:20:10:10
+D:%%%%%%%%%%
+D:%........%
+D:%..XXXX..%
+D:%.XX.8XX.%
+D:%.X....X.%
+D:%.XX++XX.%
+D:%.XX^^XX.%
+D:%..X++X..%
+D:%........%
+D:%%%%%%%%%%
+
+N:98:(Greater) Majorly Bossy Vault
+X:8:37:10:10
+D:%%%%%%%%%%
+D:%........%
+D:%.@XXXX@.%
+D:%.XX88XX.%
+D:%.X8888X.%
+D:%.XX++XX.%
+D:%.XX^^XX.%
+D:%.@X++X@.%
+D:%........%
+D:%%%%%%%%%%
+
+N:99:Bubbles
+X:8:35:25:41
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X8X#X8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8X%
+D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX%
+D:%XX.9.#.9.X.9.#.9.X.9.#.9.X.9.#.9.#.9.XX%
+D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX%
+D:%X8XXX8X#X8X#X8X#X8X#X8X#X8X#X8X#X8X#X8X%
+D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX%
+D:%XX.9.#.9.X.9.X.9.#.9.X.9.#.9.X.9.X.9..X%
+D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX%
+D:%X8X#X8XXX8X#X8XXX8XXX8XXX8XXX8XXX8X#X8X%
+D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX%
+D:%XX.9.X.9.#.9.X.9.#.9.#.9.#.9.#.9.X.9.XX%
+D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX%
+D:%X8X#X8X#X8X#X8XXX8XXX8XXX8XXX8X#X8X#X8X%
+D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX%
+D:%XX.9.#.9.X.9.#.9.#.9.#.9.#.9.#.9.X.9.XX%
+D:%XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.XXX%
+D:%X8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8X#X8X%
+D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX%
+D:%##.9.#.9.#.9.#.9.#.9.#.9.#.9.#.9.X.9.XX%
+D:%XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.XX#.#XX%
+D:%X8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8XXX8X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:100:Lesser Vault (Cross)
+X:7:10:13:20
+D:%%%%%%%%%%%%%%%%%%%%
+D:%########++########%
+D:%#*....*#..#*....*#%
+D:%#....&,#^^#,&....#%
+D:%#....,##..##,....#%
+D:%#+#####*..*#####+#%
+D:%#^^.....,,.....^^#%
+D:%#+#####*..*#####+#%
+D:%#....,##..##,....#%
+D:%#....&,#^^#,&....#%
+D:%#*....*#..#*....*#%
+D:%########++########%
+D:%%%%%%%%%%%%%%%%%%%%
+
+N:101:The I in the Storm
+X:8:30:25:41
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXX%
+D:%X&&..^..#9X..^^^^^^^^^^^^^..X9#..^..&&X%
+D:%X&...#....X..XXXXXX^XXXXXX..X....#...&X%
+D:%X...&#..#.X9XX.@8XX^XX8@.XX9X.#..#&...X%
+D:%X..&9#....XXX...@X,,,X@...XXX....#9&..X%
+D:%X^####..#...XX..XX,.,XX..XX...#..####^X%
+D:%X........,#..XX^X,,.,,X^XX..#,........X%
+D:%X.......#.....^XX,...,XX^.....#.......X%
+D:%X........,#...9X,,...,,X9...#,........X%
+D:%X.......#.....XX.......XX.....#.......X%
+D:%X.#......,#..XX^.......^XX..#,......#.X%
+D:%X.9#....#....#^^...9...^^#....#....#9.X%
+D:%X.#......,#..XX^.......^XX..#,......#.X%
+D:%X.......#.....XX.......XX.....#.......X%
+D:%X........,#...9X,,...,,X9...#,........X%
+D:%X.......#.....^XX,...,XX^.....#.......X%
+D:%X........,#..XX^X,,.,,X^XX..#,........X%
+D:%X^####..#...XX..XX,.,XX..XX...#..####^X%
+D:%X..&9#....XXX...@X,,,X@...XXX....#9&..X%
+D:%X...&#..#.X9XX.@8XX^XX8@.XX9X.#..#&...X%
+D:%X&...#....X&&XXXXXX^XXXXXX&&X....#...&X%
+D:%X&&..^..#9X..^^^^^^^^^^^^^..X9#..^..&&X%
+D:%XXXXXXXXXXXXXXXXXXX#XXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:102:Roundabout Two
+X:8:30:25:40
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%#^X.^^+X@XX.+X^^^^^+.X...X8#@&*X*****X%
+D:%X^X.^XX@XX.XX^^^^^^X.X.X^XXX@.&+9....X%
+D:%X^X.XX&XX.XX^^^^^^^X.X.X^X8#@&*X99...X%
+D:%X^X.X^^X&.XXXXXXXXXX.X.X^XXXXXXXXXX++X%
+D:%X^X.X.*X&.X.......&..X.X^X........X^^X%
+D:%X^X&X.*X^^X.XXXXXXXXXX.X&X#XXXXXX.X^.X%
+D:%X^X.X..X^^X...@........X.X......X.X^^X%
+D:%X^X.X..X^^XXXXXXXXXXXXXX.XXXXXX.X.X.^X%
+D:%X^X.X..X^^X..,.....,....&X8#,*X.X.X^^X%
+D:%X^X&X,,X^^X^XXXXXXXXXXXXXX##,,X.X.X^.X%
+D:%X^+&X..+^^X^^^^^^^^^^^^^^^9...#.X.#^^X%
+D:%X^X&X,,X^^X^XXXXXXXXXXXXXX##,*X.X.X.^X%
+D:%X^X.X..X^^X..,.....,....&X8#,,X.X.X^^X%
+D:%X^X.X..X^^XXXXXXXXXXXXXX.XXXXXX.X.X^.X%
+D:%X^X.X..X^^X...@........X.X......X.X^^X%
+D:%X^X&X.*X^^X.XXXXXXXXXX.X&X#XXXXXX.X.^X%
+D:%X^X.X.*X&.X.......&..X.X^X........X^^X%
+D:%X^X.X^^X&.XXXXXXXXXX.X.X^XXXXXXXXXX++X%
+D:%X^X.XX&XX.XX^^^^^^^X.X.X^X8#@&*X99...X%
+D:%X^X.^XX@XX.XX^^^^^^X.X.X^XXX@.&+9....X%
+D:%#^X.^^+X@XX.+X^^^^^+.X...X8#@&*X*****X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:103:Modified Greater Vault (Huge)
+X:8:45:17:39
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X%
+D:%X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X.X%
+D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X#X%
+D:%X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X8X.X%
+D:%X#X#XXXXXXXXXXXXXXXXXXXXXXXXXXX#X#X#X%
+D:%X8X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8X8X.X%
+D:%X#X#X#XXXXXXXXXXXXXXXXXXXXXXXXXXX#X#X%
+D:%X8X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8X.X%
+D:%X#X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X%
+D:%X8X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#.X%
+D:%X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%X8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#8#%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+N:104:The Bank from Hell
+X:8:30:20:31
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%XXXXXXXXXXXXXX+XXXXXXXXXXXXXX%
+D:%X.&.&.&.&.&.#...#.&.&.&.&.&.X%
+D:%X.#.#.#.#.#.#...#.#.#.#.#.#.X%
+D:%X...........#...#...........X%
+D:%X##########+##+##+##########X%
+D:%X@........^^^^^^^^^........@X%
+D:%X@^^^.....^^^...^^^.....^^^@X%
+D:%X##+#######+#####+#######+##X%
+D:%X.^^^.#@..^^^...^^^..@#.^^^.X%
+D:%X#...##...............#.###.X%
+D:%X@...@#.&.&.&.&.&.&.&.#..@..X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXX+X%
+D:%X.+^*****X*******^+^******X.X%
+D:%X@X^....^X^......^X^.....*X@X%
+D:%X.X*****^+^*******X*******X.X%
+D:%X@XXXXXXXXXXXXXXXXXXXXXXXXX@X%
+D:%X.@.@.@.@.@.@.@.@.@.@.@.@.@.X%
+D:%XXXXXXXXXXXXXXXXXXXXXXXXXXXXX%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+# Between vaults from weisiger
+N:105:The Between Vault
+X:8:15:6:25
+D:XXXXXXXXXXXXXXXXXXXXXXXXX
+D:#,,,,,X&...@X..**&X....9X
+D:#...&1X.,,,2X..**&X3...9X
+D:#...&.X1,,,.X2.**3X....9X
+D:#,,,,,X&...@X..**&X....9X
+D:XXXXXXXXXXXXXXXXXXXXXXXXX
+
+N:106:Greater Vault (Mix&Match)
+X:8:35:28:73
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D:%.......................................................................%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X1.^^^^*&XX,,,9,,,XX...X...X...X&..X@..X@.8X7X8#X8#X8#X8#X8#X8#X8#.7X.%
+D:%.X..^^^*,XX@&@&@&@&X.X...X...X..#X&..X&..X@.#XXX#8XX8XX8XX8XX8XX8XXXXX.%
+D:%.X^^^^*,XXXX.......X6.X...X...X.#.X&..X@..X@.8X8XXXXXXXXXXXXXXXXXX88XX.%
+D:%.X^^^*,XX&&XX.....XXXXXXXX.X...X#..X&.#X&..X@.XX#9#9#9#9X88XX^^XX#8XXX.%
+D:%.X^^*,X#^^^^XX^XXX.X***&@XXXX...X...X&#.X@..X@X8X9X9X9X9X8#X^^XX8#X#8X.%
+D:%.X^*,XX^^^^^^XX^XX5X***@&,,XXX...X...X#..X&..XX#X#X#X#X#XXX^^XX##XX88X.%
+D:%.X*,XX........XX^XXX***&@,,,,XXX..X..#X&..X@..X8X8X8X8X8XX^^XX88XXXXXX.%
+D:%.X&XX..........XX^XX***@&,,,,#.XXX.X.#.X&..X&.X#X#X#X#X#X^^X#8XXXX,#.X.%
+D:%.XXX************XX6X@&@&@,,,,#...XXXX#..X&.#X@X8X8X8X8X8#XXXXXX,,,,#.X.%
+D:%.XX&,&,&,&,&,&,&,XXXX@&@&,,,,#.....XXX...X&#.XX8X8X8X8X8XXXX,,,,,,,#.X.%
+D:%.XX#************XX.^#X,,,,,,,#.......XXXXXXXXXX8X8X8XXXXX@&#,,,,,,XX#X.%
+D:%.X^XX..........XXX.XXXX,,,,,,#................X8XXXXX&&#&@&#,,XXXXX#.X.%
+D:%.X.XXX........XX3X^X99XXXXXXXXXXXXXXX#XXXXXXX.XXXX,#&&&#&@&XXXX,#^X#.X.%
+D:%.X.XXXX......XXX.X.X@@,,,,,,,,,,,,X1X.......X.X,,,,#&&&#XXXX,,,,#^X.#X.%
+D:%.X^XXXXX....XXXX.^.XXXXXXXXXXXXXX*X^X+XXXXX.X.X,,,,#&&XXX##9,,,,#^X.#X.%
+D:%.X.X^^.XX@@XXXXXXXXX9@&,,,,,,,,,X*X.X^^&,,X.X.X#####XXX#9@##@,,,#^X#.X.%
+D:%.X.X^X.XXXXX^^^XXXXXXXXXXXXXXXX,X*X.X^^^,,X.X.X&&&XXX@8##&@##@,,#^X#.X.%
+D:%.X^X.X2XXXX3^^^XXX.^..^..^..^.X,X*X^X&^^^&X.X.X##XX5.@89##9@##&,#^#.#X.%
+D:%.X.X^XXXXXXXXX^^XX.XXXXXXXXXX.X,X*X.X..^^^X.X.X^XXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X.X^^.^..^..X^^^X.^..^..^.4X^X,X*X.X2.&^^+.+.X.^.^.^.^.^.^.^.^.^.^.4X.%
+D:%.X^XXXXXXXXX^X^^^XXXXXXXXXXXX#X#X#X^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.X..^..^..^..X^^^#................X..^..^..^..^..^..^..^..^..^..^..^.+.%
+D:%.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.%
+D:%.......................................................................%
+D:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+# Wilderness vault test
+N:107:The Wilderness Vault
+X:10:1:3:7
+D:XXXXXXX
+D:T,,,,8X
+D:XXXXXXX
diff --git a/lib/edit/volcano.txt b/lib/edit/volcano.txt
new file mode 100644
index 00000000..1b89cf3d
--- /dev/null
+++ b/lib/edit/volcano.txt
@@ -0,0 +1,83 @@
+# File: volcano.txt
+
+# Stairway to the Hell
+F:$:7:3:0:0:0:0:0:6
+
+# Hole to the center of the Volcano
+F:>:7:3:0:0:0:0:0:5
+
+############### Town Layout ###############
+
+D:######################################################################################################################################################################################################
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL$LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^>^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^K^^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^K^^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^^K^^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:#LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL^K^LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#
+D:######################################################################################################################################################################################################
+
+
+############### SLarting positions ###############
+
+# Standard starting position for normal races
+?:[EQU $LEAVING_QUEST 0]
+P:64:80
diff --git a/lib/edit/w_info.txt b/lib/edit/w_info.txt
new file mode 100644
index 00000000..65efc2fc
--- /dev/null
+++ b/lib/edit/w_info.txt
@@ -0,0 +1,120 @@
+# PernAngband wilderness definition file by DarkGod (darkgod@pernangband.org)
+# with the help form Gwidon S. Naskrent (naskrent@artemida.amu.edu.pl)
+# Designed to "look like" Middle-earth map
+
+W:D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+W:D:XgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:XgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xggggggggggg=========g=========gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xgggggg========t===ggg==========gggggtggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xggggg=========gtgg^g======ggg=gggggggggggtggtggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xggg=========gg^t^^g========ggggt^g^ttggtgtgggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:X==========,,,,&__ttgg=ggg=ggggt^^%t^^^tg&ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:X========..tt&&&^__,ttttttttttttt,gttttt&&&&ggtggggggggggtgggggtgggggggggggggggggtggggggggggggggtgggX
+W:D:X===^===....,&&&,t__,,,,,,g,t,,,,,,,,tttgt&^^&g&g&ggtggggggggggggggggtttggtgggggggtgggtgggggtgggggggX
+W:D:X=========.,.&&,,,,__t,,,,,t,,,,t,t,,,,,tttt&t&^MMM&ggggg&g&&&gggttttttgtttgttttttttttttttttttttttttX
+W:D:X=========.,.&&,,_,__,,,,,,,,,,,,,,,,,,,,,tttg...2Mt&&&&&&&t&t&&&&tgttttttttttttgtttttttttttttttttttX
+?:[EQU $TOWN_DESTROY2 1]
+W:M:0:1
+W:D:X=========.,.&&,,_,__,,,,,,,,,,,,,,,,,,,,,tttg...PMt&&&&&&&t&t&&&&tgttttttttttttgtttttttttttttttttttX
+?:1
+W:D:X========..T.&___.___,,^,,,,,,,,,,,,,,,,,,tttt.&MMM^^__t^&&&,t^^ttttt,t,t,ttttttttttttttttttttttttttX
+W:D:X======.,..__&&,,,,__,,^^,,,,,,,,,,,,,,,,,t....t&&t^^__^__&.&,&&&^^,,t,,,,,,,,t,t,t,ttttttttttttttttX
+W:D:X=====,..__.T&&..,,__,,^^,,,,.,,,^^,,,,,,,,^^^t^&&&&,__^__tTTT,,,,,,,,,,,,t,,,,,,,,,,,,,t,ttttttttttX
+W:D:X===....__.,.&&,.,,__,,_^,,,,,,,,^^,,,,,,,,^^,^^^_&_^^__TT__T_TT,,&_,,,,,,,,,,,,,^,^,t,,,,,,t,ttttttX
+W:D:X===..,.__.TT&&..,__,__^^==,,.,^^,,.,,.,,,,^^____^&&___,TTTT_T__,,__,,,,^^,^^^^^^^^,,,,,t,,,,,,,,,,tX
+W:D:X===....__..T&&....__,^^=,__....,.....,,.,,^__^^,^&&,__,TTTTT__T_==,,,,,,,^_,,^,,,,,,,,,,,,,,t,t,,,,X
+W:D:X===...__..TT&&...__,.^^^..__.........,,,,,__^^^^^&&,__,TTTTTT_TT_=,,,,,,,__,,,,,,,,,,,,,,,,,,,,,,t,X
+W:D:X===...__..TT&&&..__..^..,.__...,..^..,,,,__^.^^^^&&,__,TTTTT&T&T__,,,,,,,,,__,,,,,,,,,,,,,,,,,,,,,,X
+W:D:X===....==..T.&..__..,.....__TTT....^^...__..TTT._&&..__,TTT&T&T&T__,,,,,,,,,__,,,,,,,,,,,,,,,,,,,,,X
+W:D:X====...==.....==......^...__TTTT.1.^^..,__...T__&&...__.TTTTTTTTT__,,,,,,,,__,,,,,,,,,,,,,,,,,,,,,,X
+W:D:X=====..========...^.^......__TTT._.^....__..,__._&...__.TTTTTTTTTT,__,,,,,,,__,,,,,,,,,,,,,,,,,,,,,X
+W:D:X==============..............__TT........__..__^_&&..,.__.TTTTTTTTT,__,,,,,,,,__,,,,,,,,,,,,,,,,,,,,X
+W:D:X======..==....&&............__..^^^....__...__^&&.....__.*TTTTTTT,,,__,,,,,,__,,,,,,,,,,,,,,,,,,,,,X
+W:D:X======........&&......,.....__...^.^...__.__.^^&&..._.__.TTTTTTTT..,,______,__,,,,,,,,,,,,,,,,,,,,,X
+W:D:X======........&&............__.,......__..__.^^&&___.___.TTTTTTTT....,,.,,,__,,,,,,,,,,,,,,,,,,,,,,X
+W:D:X======........&&....^......__..........___...^^&&.....__.TTTTT.T.,.....,,,,,__,,,,,,,,,,,,,,,,,,,,,X
+W:D:X=======.....T&&&..^^^^....__..........__....^^&&^...,__...TTTT......,....,,,__,,,,,,,,,,,,,,,,,,,,,X
+W:D:X==========..T&&&T.......__..........__......^^&&&&^..__...TTTT...........,,,,__,,,_,,,,,,,T,T,,,,,,X
+W:D:X==========..TTT&&T&&...__........,___......^)^&&.&^.__.....TTTTTT....,.....,,__,__,_=,,,TTTTTT,,,,,X
+W:D:X==========...TT&TT..___.__.......__.@__..._^^^&&....__..TTTTTTTTTT...........,,_,.,,==...T=_TT,,,,,X
+W:D:X==========.....T.T.__.._........__..._____^___&__...__,TTTTTTTTTT...,..,..,....,,,,,========TT,,,,,X
+W:D:X===========.=......__.........__.........^^^&&&_T,T__,.TT&TTTTTTT..........,.,.....=========T,,,,,,X
+W:D:X================..__..........__,.......^.^M&&._T4T__..TTTTTTTTT......,......,.....=====..==,,,,,,,X
+W:D:X=================.==T....,...__.........^^^MM&,.TTT.__...TTTTT..........,..........========,,.,,,,,X
+W:D:X===================TT........__.........^^MMMTT.T.^.^__^^........,......,,......^^..=======,,,.,.,,X
+W:D:X===================T==......__......,...^^M)MTTTT^___^.__^.,....................^,..==...==..,,.,.,X
+W:D:X===================T==.....__...........^&MgMMTTTTT..___^^................,..,..^^.......=.....,.,,X
+W:D:X=====================.....__...,........^&M5MTTTTTT.^^^__.....,..,......,,,.....^^...,......,......X
+W:D:X=====================.....=_............&&MMMTTTTT..^^..__.,.........,.........,^^.......,.........X
+W:D:X======================...==........,....&&&&&T.._......__..,..................................,..,.X
+W:D:X=======================.===.............._&^.....__..,...__.....,.........,..,,,...,...,...........X
+W:D:X===========================..............__.......__....__......,.......,...........,..............X
+W:D:X==========================.........._._..__.......__...__^..^,..............................,..,...X
+W:D:X==========================...._.,.__._.___........__...^^_^^@.....,..,.....,.,,....,,....,.,.,.....X
+W:D:X==========================..__.__.___._..&&^^.....__...^^_^^_@..&.&.....,.......,....,.............X
+W:D:X============================....._..._.__.&&&.....__....__.@...&&DD&&....,..................,.,..,.X
+W:D:X===========================.............._&&&^^....__...___....&&D^^D&&.&.&.&.&.&.&.&.&^^^,,,,,,,,,X
+W:D:X===========================.......^.&.^^&&&&&&&......______.....&&DDD&^&^&D&D&D&,&,&,&,,,,,,,,,,,,,X
+W:D:X===========================.....^^.&.&_&^&.&^&_&&&^...._.___....&&^DDD&^DDDDDD^&_,,,,,,,,,,,,,D,D,,X
+W:D:X===========================....^....__^^...^^^__^&&........__...&&^D&DDDDDDD^^,_,__,,,,,,,,,,,,,,,,X
+W:D:X===========================..^^^_..__......^^.__^^&&&........__.&&^DDDDDDDDD^^,,,,__,,,,,D,,,,,,,,,X
+W:D:X============================.^__.__.__......__&^^^.&&&&&&&&&..__&&^DDDDDDD^__,,,,,,__,,,,,,,DDDD,,,X
+W:D:X============================.&^__.._....^^.__&&&^^.._&&&&&&&.^__&^^DDDDD^^,,__,,,,,,__,,,,,,,,,,,,,X
+W:D:X============================^&^__.^^^^^^^^...__...__^&^&&&&&.__^&&^DDD^^^,,,,__,,,D,,__,,,,,,,D,,D,X
+W:D:X===========================.^^.__..^.^.........___..^&^^.^^3.__.&&^D^D^^,,,,__,,D,,,D,__,,,D,,,,,,,X
+W:D:X==========================.^^^..__....===.====__..&^...._^...__.&&,^,^,,,,,,,____,,,,,__,,,,,,,,,,,X
+W:D:X==========================^^....__..===========.&&^....__....__.$&,,,,,,,,D,,,,,=_,,__,,,,,D,,D,D,,X
+W:D:X========================..^^=...=_.==========&^^^^.._.__....__..&&,,,,,,,,,,,,,,==__,,&,,,,,,,,,,,,X
+W:D:X======================^^.^=====.==============^^^._=._......__.._&&,,,,,,,,,======,,,,&&,,D,D,D,D,,X
+W:D:X==============================================.^^^==...._.__..__.&&,,,___,_======,D,,&&,D,,,D,,,DD,X
+W:D:X===============================================..====___.__.__...&&,__,,D_,,D,,=_,,,D&&,D,DD,DDD,DDX
+W:D:X===============================================.==^==_@@..._.....&&_,,,,,,,,,,,__,&&&&DDDDDDDDD,,DDX
+W:D:X=================================================^^===...........&&,&,&,&,&,&,&__&&&DD,DD,DDD,D,,DDX
+W:D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Definition of all the entrances to the dungeons
+# W:E:<dungeon>:<y>:<x>
+
+# Numenor
+W:E:7:25:1
+
+# Path of the Dead
+W:E:16:49:38
+
+# Illusory Castle
+W:E:17:34:42
+
+# Maze
+W:E:18:25:35
+
+# Orc Cave
+W:E:19:15:32
+
+# Erebor
+W:E:20:17:77
+
+# Old Forest
+W:E:21:21:31
+
+# The Sacred Land Of Mountains
+W:E:25:37:80
+
+# The Wild Land Of Rhun
+W:E:26:30:75
+
+# The Sandworm Lair
+W:E:27:28:10
+
+# The Grinding Ice
+W:E:29:3:3
+
+# Dol Guldur
+W:E:23:30:60
+
+# Cirith Ungol
+W:E:9:56:65
+
+
+# Starting position
+W:P:34:21
diff --git a/lib/edit/wf_info.txt b/lib/edit/wf_info.txt
new file mode 100644
index 00000000..80ab14a6
--- /dev/null
+++ b/lib/edit/wf_info.txt
@@ -0,0 +1,170 @@
+# File: wf_info.txt
+
+
+# This file is used to initialize the "lib/raw/wf_info.raw" file, which is
+# used to initialize the "wilderness feats" information for the Angband game.
+
+# Do not modify this file unless you know exactly what you are doing,
+# unless you wish to risk possible system crashes and broken savefiles.
+
+# After modifying this file, delete the "lib/raw/wf_info.raw" file.
+
+# The wilderness feat indexes are defined in "defines.h", and must not be
+# changed.
+
+# N:<index>:<name>
+# D:<long name>
+# W:<level>:<entrance>:<road>:<feat>:<terrain_idx>:<character(must be unique)>
+# F:<flags>
+# X:<feat1>...<feat18>
+
+# Note for <road> :
+# 1 = NORTH
+# 2 = SOUTH
+# 4 = EAST
+# 8 = WEST
+
+# Note for <entrance> :
+# if < 1000 then it points to a town
+# if >= 1000 then it points to the x - 1000 dungeon type
+
+# Version stamp (required)
+
+V:2.0.0
+
+N:0:Ekkaia
+D:the Encircling Sea
+W:1:0:0:182:0:X
+X:182:182:182:182:182:182:182:182:182:182:182:182:182:182:182:182:182:182
+
+N:1:Bree
+D:a small village
+W:1:1:0:203:1:1
+X:88:88:89:89:89:89:96:96:96:96:96:96:96:96:96:96:96:96
+
+N:2:Gondolin
+D:The hidden town of the Noldor
+W:1:2:0:203:1:2
+X:88:88:88:89:89:89:89:89:89:89:89:89:89:96:96:96:96:96
+
+N:3:Minas Anor
+D:The great town of Gondor
+W:1:3:0:203:1:3
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:4:Lothlorien
+D:The land of Galadriel
+W:1:4:0:203:1:4
+X:88:88:96:96:96:96:96:96:96:96:96:96:96:96:96:89:89:89
+
+N:5:grass
+D:a plain of grass
+W:5:0:0:89:6:.
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:6:forest
+D:a forest
+W:20:0:0:96:7:T
+X:88:88:96:96:96:96:96:96:96:96:96:96:96:96:96:89:89:89
+
+N:7:road
+D:a west-east road
+W:5:0:12:1:6:-
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:8:road
+D:a north-south road
+W:5:0:3:1:6:|
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:9:mountain
+D:a mountain chain
+W:50:0:0:97:11:&
+X:1:1:89:89:88:96:96:97:97:97:97:97:97:97:97:97:97:97
+
+N:10:road
+D:a west-east-south road
+W:5:0:14:1:6:,
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+# XXX
+
+N:12:The Nether Realm
+D:the entrance to the netherworld
+W:127:1006:0:7:10:6
+X:88:88:88:86:86:86:85:85:85:85:85:85:85:85:85:97:97:97
+
+N:13:deep water
+D:a deep water area
+W:70:0:0:187:2:=
+X:187:187:187:187:187:187:187:187:187:187:187:187:84:84:84:84:84:84
+
+N:14:shallow water
+D:a shallow water area
+W:20:0:0:84:3:_
+X:187:187:187:84:84:84:84:84:84:84:84:84:84:84:84:1:88:89
+
+N:15:Mirkwood
+D:The Forest of Mirkwood
+W:20:1001:0:7:7:*
+X:88:88:96:96:96:96:96:96:96:96:96:96:96:96:96:89:89:89
+
+N:16:Mordor
+D:The Gates of Mordor
+W:50:1002:0:7:11:$
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:17:Angband
+D:The Pits of Angband
+W:50:1003:0:7:11:%
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:18:hill
+D:a hill
+W:50:0:0:97:11:^
+X:1:1:89:89:88:96:96:97:97:97:97:97:97:97:97:97:97:97
+
+N:19:desert
+D:a desert
+W:5:0:0:91:6:D
+X:1:1:88:88:91:91:91:91:91:91:91:91:91:91:91:98:98:92
+
+N:20:jungle
+D:a jungle
+W:20:0:0:96:7:t
+X:88:88:96:96:96:96:96:96:96:96:96:96:96:96:96:89:89:89
+
+N:21:swamp
+D:a swamp
+W:5:0:0:89:6:@
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:22:glacier
+D:a glacier
+W:20:0:0:90:7:g
+X:94:94:90:90:90:90:90:90:90:90:90:90:90:90:90:90:92:92
+
+N:23:grass
+D:a plain of grass
+W:5:0:0:89:6:,
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:24:Moria
+D:The Doors of Moria
+W:30:1022:0:7:11:)
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96
+
+N:25:high mountain
+D:a high mountain chain
+W:80:0:0:101:11:M
+X:101:101:101:101:101:101:101:101:101:101:101:101:101:101:101:101:101:101
+
+N:26:Gondolin
+D:The pillaged city of the Noldor
+W:1:2:0:203:1:P
+X:49:49:49:88:88:88:92:92:92:92:93:93:94:94:94:174:205:205
+
+N:27:Khazad-dum
+D:The dwarven stronghold
+W:1:5:0:203:1:5
+X:1:1:1:1:1:1:1:1:1:1:88:88:88:88:88:88:88:88
diff --git a/lib/edit/wights.map b/lib/edit/wights.map
new file mode 100644
index 00000000..de83b616
--- /dev/null
+++ b/lib/edit/wights.map
@@ -0,0 +1,82 @@
+# Permanent wall
+F:X:63:3
+
+# up stairs
+F:<:6:3
+
+# Floor with mountain
+F:^:97:3
+
+# Floor
+F:.:1:3
+
+# Floor with dirt
+F:;:88:3
+
+# Floor with forest wight
+F:f:88:3:381
+
+# Floor with grave wight
+F:g:88:3:470
+
+# Floor with barrow wight
+F:b:88:3:499
+
+# Floor with Emperor Wight
+F:e:88:3:604
+
+# Floor with a Human Skeleton
+F:k:88:8:0:395
+
+# Marker
+F:,:172:6
+
+# between gate 1
+F:1:160:6:0:0:0:0:0:2057
+
+# between gate 2
+F:2:160:6:0:0:0:0:0:1036
+
+# between gate 3
+F:3:160:6:0:0:0:0:0:3847
+
+# between gate 4
+F:4:160:6:0:0:0:0:0:2321
+
+# between gate 5
+F:5:160:6:0:0:0:0:0:1043
+
+# between gate 6
+F:6:160:6:0:0:0:0:0:3599
+
+# between gate 7
+F:7:160:6:0:0:0:0:0:2071
+
+# between gate 8
+F:8:160:6:0:0:0:0:0:3350
+
+# between gate 9
+F:9:160:6:0:0:0:0:0:771
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X..k^^^^^^^^^^^^^^^^^^^^^^^XX
+D:Xfkff^^^^^...^^^^..ff^^^^^^XX
+D:X^f1^^^^^^.ffg^^^^.fgg^^^^^XX
+D:X^^^^^^^^^^^fg4^^^^f6^^^^^^XX
+D:X^^^^^^^^^^^^f^^^^^^^^^fg8^XX
+D:X^^^^^^.f^^^^^^^^^^^^.ffg^^XX
+D:X^^^^.ff2^^^^^^.^^^^^^.f^^^XX
+D:X^^^^^3.^^^^^^^.^^^^^^^^^^^XX
+D:X^^^^^^^^^^^^ff.^^^^^^^^^^^XX
+D:X^^^^^^^^^^^7gfg^^^^^^^^f^^XX
+D:X^^^^^^^^^^^^^^^^^^^..^..^^XX
+D:X^^^^^^^^^^^^.^^^^^^....ff^XX
+D:X^^^^...^^^^^ff^^^^^^..fg^^XX
+D:X^^^.fff^^^^gfg^^^^...fgee^^X
+D:X^^^^g5^^^^..8^^^^^f^fgfe,9^X
+D:X^^^^^^^^^^^^^^^^^^^^^^^^^^^X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:3
diff --git a/lib/edit/wolves.map b/lib/edit/wolves.map
new file mode 100644
index 00000000..0e3030ea
--- /dev/null
+++ b/lib/edit/wolves.map
@@ -0,0 +1,55 @@
+# permanent wall
+F:X:61:0
+
+# granite
+F:#:57:0
+
+# up staircase
+F:<:6:0
+
+# Grass
+F:-:89:0
+
+# Dirt
+F:.:88:0
+
+# Trees
+F:t:96:0
+
+# Open door
+F:D:4:0
+
+# Broken door
+F:d:5:0
+
+# Grass with elf skeleton
+F:z:89:0:0:397
+
+# Dungeon layout
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X...t.---.......-.-.....-...#.--X
+D:X-..#..-......-.....--......t...X
+D:X...t.--.tt#ddt##t#dd#t#....#...X
+D:Xz-.t....#.--...#...-..#.--.t...X
+D:X#d##....#.z.z..t---...t....#tD#X
+D:X....---.t......t..-.z.#..z.....X
+D:X...--.-.#...z..#.--...t........X
+D:X##t#-z-.#####dd#dd##tt#....#t#tX
+D:X..-t.--.t....-.t...--.#.---t...X
+D:X.--t---.t......#..--z-#----#-..X
+D:X...D....#.--z...---.--t--.-d--.X
+D:X...#....#........---..#--..#z-.X
+D:X#t#t....t..-...t-z-...t....#tt#X
+D:X..-.....#-..-..#......#.....--.X
+D:X...-.--.#dd#ttt##t##Dd#.....--.X
+D:X#Dt#....#-.....#......#....##t#X
+D:X...#...-t----..#......t...-tz--X
+D:X.z.t...-#--.-..t......#.---#-..X
+D:X.--t.---#-z.--.#.....zt--..d-..X
+D:X---#....###dd#tt##dd###--..t...X
+D:X.z-t..-...-....-..-.--.....#<zzX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:3:4
+
diff --git a/lib/file/book-0.txt b/lib/file/book-0.txt
new file mode 100644
index 00000000..07403e0d
--- /dev/null
+++ b/lib/file/book-0.txt
@@ -0,0 +1,86 @@
+
+ Mordekainen's Magical Compendum of Deep Thought.
+ ------------------------------------------------
+
+If they ever come up with a swashbuckling School, I think one of the
+courses should be Laughing, Then Jumping Off Something.
+
+It takes a big man to cry, but it takes a bigger man to laugh at that man.
+
+The people in the village were real poor, so none of the children had
+any toys. But this one little boy had gotten an old enema bag and
+filled it with rocks, and he would go around and whap the other
+children across the face with it. Man, I think my heart almost
+broke. Later the boy came up and offered to give me the toy. This was
+too much! I reached out my hand, but then he ran away. I chased him
+down and took the enema bag. He cried a little, but that's the way of
+these people.
+
+Dad always thought laughter was the best medicine, which I guess is
+why several of us died of tuberculosis.
+
+Maybe in order to understand mankind, we have to look at the word
+itself: "Mankind". Basically, it's made up of two separate words -
+"mank" and "ind". What do these words mean? It's a mystery, and
+that's why so is mankind.
+
+Ambition is like a frog sitting on a Venus Flytrap. The flytrap can
+bite and bite, but it won't bother the frog because it only has little
+tiny plant teeth. But some other stuff could happen and it could be
+like ambition.
+
+I'd rather be rich than stupid.
+
+We tend to scoff at the beliefs of the ancients. But we can't scoff at
+them personally, to their faces, and this is what annoys me.
+
+Probably the earliest flyswatters were nothing more than some sort of
+striking surface attached to the end of a long stick.
+
+As the evening sky faded from a salmon color to a sort of flint gray,
+I thought back to the salmon I caught that morning, and how gray he
+was, and how I named him Flint.
+
+When I was a kid my favorite relative was Uncle Caveman. After school
+we'd all go play in his cave, and every once in a while he would eat
+one of us. It wasn't until later that I found out that Uncle Caveman
+was a bear.
+
+Why do there have to be rules for everything? It's gotten to the point
+that rules dominate just about every aspect of our lives. In fact, it
+might be said that rules have become the foot-long sticks of mankind.
+
+If I had a mine shaft, I don't think I would just abandon it. There's
+got to be a better way.
+
+If I had a nickname, I think I would want it to be "Prince of
+Weasels", because then I could go up and bite people and they would
+turn around and go, "What the-?" And then they would recognize me, and
+go, "Oh, it's you, the Prince of Weasels."
+
+It's amazing to me that one of the world's most feared diseases would
+be carried by one of the world's smallest animals: the real tiny
+dog.
+
+Sometimes life seems like a dream, especially when I look down and see
+that I forgot to put on my pants.
+
+I bet it was pretty hard to pick up girls if you had the Black Death.
+
+It's fascinating to think that all around us there's an invisible
+world we can't even see. I'm speaking, of course, of the World of the
+Invisible Scary Skeletons.
+
+Whenever I hear the sparrow chirping, watch the woodpecker chirp,
+catch a chirping trout, or listen to the sad howl of the chirp rat, I
+think: Oh boy! I'm going insane again.
+
+He was the kind of man who was not ashamed to show affection. I guess
+that's what I hated about him.
+
+The next time I have meat and mashed potatoes, I think I'll put a very
+large blob of potatoes on my plate with just a little piece of
+meat. And if someone asks me why I didn't get more meat, I'll just
+say, "Oh, you mean this?" and pull out a big piece of meat from inside
+the blob of potatoes, where I've hidden it. Good magic trick, huh?
+
diff --git a/lib/file/book-1.txt b/lib/file/book-1.txt
new file mode 100644
index 00000000..e0f9232c
--- /dev/null
+++ b/lib/file/book-1.txt
@@ -0,0 +1,83 @@
+
+ Mordekainen's Magical Compendum of Deep Thought, Vol. 2
+ -------------------------------------------------------
+
+
+It makes me mad when people say I turned and ran like a scared
+rabbit. Maybe it was like an angry rabbit, who was running to go fight
+in another fight, away from the first fight.
+
+Perhaps, if I am very lucky, the feeble efforts of my lifetime will
+someday be noticed, and maybe, in small way, they will be acknowledged
+as the greatest works of genius ever created by Man.
+
+Sometimes I think I'd be better off dead. No, wait, not me, you.
+
+If you ever catch on fire, try to avoid looking in a mirror, because I
+bet that will really throw you into a panic.
+
+Children need encouragement. If a kid gets an answer right, tell him
+it was a lucky guess. That way he develops a good, lucky feeling.
+
+The crows seemed to be calling his name, thought Caw.
+
+If your friend is already dead, and being eaten by vultures, I think
+it's okay to feed some bits of your friend to one of the vultures, to
+teach him to do some tricks. But only if you're serious about adopting
+the vulture.
+
+Broken promises don't upset me. I just think, why did they believe me?
+
+One thing vampire children have to be taught early on is, don't run
+with a wooden stake.
+
+Consider the daffodil. And while you're doing that, I'll be over here,
+looking through your stuff.
+
+I think my new thing will be to try to be a real happy guy. I'll just
+walk around being real happy until some jerk says something stupid to
+me.
+
+I hope some animal never bores a hole in my head and lays its eggs in
+my brain, because later you might think you're having a good idea but
+it's just eggs hatching.
+
+Whenever you read a good book, it's like the author is right there, in
+the room talking to you, which is why I don't like to read good books.
+
+What is it about a beautiful sunny afternoon, with the birds singing
+and the wind rustling through the leaves, that makes you want to get
+drunk?
+
+Instead of a trap door, what about a trap window? The guy looks out
+it, and if he leans too far, he falls out. Wait. I guess that's like a
+regular window.
+
+If I ever get real rich, I hope I'm not real mean to poor people, like
+I am now.
+
+Most of the time it was probably real bad being stuck down in a
+dungeon. But some days, when there was a bad storm outside, you'd look
+out your little window and think, "Boy, I'm glad I'm not out in that."
+
+Sometimes you have to be careful when selecting a new name for
+yourself. For instance, let's say you have chosen the nickname "Fly
+Head." Normally you would think that "fly Head" would mean a person
+who has beautiful swept-back features, as if flying through the
+air. But think again. Couldn't it also mean "having a head like a
+fly"? I'm afraid some people might actually think that.
+
+I hope that after I die, people will say of me: "That guy sure owed me
+a lot of money."
+
+The tired and thirsty prospector threw himself down at the edge of the
+watering hole and started to drink. But then he looked around and saw
+skulls and bones everywhere. "Uh-oh," he thought. "This watering hole
+is reserved for skeletons."
+
+Anytime I see something screech across a room and latch onto someone's
+neck, and the guy screams and tries to get it off, I have to laugh,
+because what is that thing.
+
+I hope life isn't a big joke, because I don't get it.
+
diff --git a/lib/file/book-10.txt b/lib/file/book-10.txt
new file mode 100644
index 00000000..51d43c60
--- /dev/null
+++ b/lib/file/book-10.txt
@@ -0,0 +1,250 @@
+The Large yellow snake is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Cave spider is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Wild cat is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Smeagol is a unique monster,
+occurring at surface depths.
+He is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Green ooze is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Poltergeist is a common undead monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Metallic blue centipede is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Giant white louse is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Black naga is a common monster,
+occurring at surface depths.
+She is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Spotted mushroom patch is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Silver jelly is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Yellow jelly is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Scruffy looking hobbit is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Giant white ant is a common monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Yellow mold is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Metallic red centipede is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Yellow worm mass is an uncommon animal,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Clear worm mass is an uncommon animal,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Radiation eye is a common monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Cave lizard is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Novice ranger is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has an inkling of magical powers.
+
+The Novice paladin is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has an inkling of magical powers.
+
+The Blue jelly is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Creeping copper coins is an uncommon animal,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant white rat is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Blue worm mass is a common animal,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Large grey snake is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Bullroarer the Hobbit is a unique monster,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Novice mage is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, just a small bit dangerous,
+and has rudimentary magical powers.
+
+The Green naga is a common monster,
+occurring at surface depths.
+She is extremely weak, just a small bit dangerous,
+and has no magical powers.
+
+The Blue ooze is a common monster,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Green glutton ghost is a common undead monster,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Green jelly is a common monster,
+occurring at surface depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Large kobold is a common monster,
+occurring at surface depths.
+It is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Skeleton kobold is a common undead monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Grey icky thing is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Disenchanter eye is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Red worm mass is a common animal,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Copperhead snake is a common animal,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Purple mushroom patch is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, moderately dangerous,
+and has no magical powers.
+
+The Novice priest is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, just a little bit dangerous,
+and has small magical powers.
+
+The Novice warrior is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Novice rogue is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Brown mold is a common monster,
+occurring at surface depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant brown bat is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Novice archer is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has an inkling of magical powers.
+
+The Creeping silver coins is an uncommon animal,
+occurring at surface depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Snaga is a common orc,
+occurring at surface depths.
+He is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Rattlesnake is a common animal,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Cave orc is a common orc,
+occurring at surface depths.
+He is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-101.txt b/lib/file/book-101.txt
new file mode 100644
index 00000000..e45865a7
--- /dev/null
+++ b/lib/file/book-101.txt
@@ -0,0 +1,4 @@
+nalo means shadow
+ure means sun
+ido means now
+nimir means shine or elf
diff --git a/lib/file/book-102.txt b/lib/file/book-102.txt
new file mode 100644
index 00000000..3b737923
--- /dev/null
+++ b/lib/file/book-102.txt
@@ -0,0 +1,4 @@
+gimli means star
+kadar means city
+izindi means starlight
+lomi means night
diff --git a/lib/file/book-103.txt b/lib/file/book-103.txt
new file mode 100644
index 00000000..0ffeea37
--- /dev/null
+++ b/lib/file/book-103.txt
@@ -0,0 +1,6 @@
+attu means father
+aru means king
+bawiba means wind
+dulgi means black
+balik means ships
+daira means Earth
diff --git a/lib/file/book-104.txt b/lib/file/book-104.txt
new file mode 100644
index 00000000..3b60e8ca
--- /dev/null
+++ b/lib/file/book-104.txt
@@ -0,0 +1,6 @@
+naur means fire
+mellon means friend
+Edain means Dunedain
+pedo means speak
+Pheriain means Halflings
+a minno means and enter
diff --git a/lib/file/book-105.txt b/lib/file/book-105.txt
new file mode 100644
index 00000000..f3667d64
--- /dev/null
+++ b/lib/file/book-105.txt
@@ -0,0 +1,7 @@
+baraz means red
+Baruk means Axes
+dum means halls
+Khazad means Dwarves
+gabil means great
+aimenu means upon you
+gathol means fortress
diff --git a/lib/file/book-106.txt b/lib/file/book-106.txt
new file mode 100644
index 00000000..81c4043c
--- /dev/null
+++ b/lib/file/book-106.txt
@@ -0,0 +1,5 @@
+dori means land
+hrassa means precipice
+Danas means Green-elves
+dunna means black
+garma means wolf
diff --git a/lib/file/book-107.txt b/lib/file/book-107.txt
new file mode 100644
index 00000000..1abc6d8e
--- /dev/null
+++ b/lib/file/book-107.txt
@@ -0,0 +1,6 @@
+nazg means ring
+ghash means fire
+snaga means slave
+burz means black
+ronk means pool
+bubhosh means great
diff --git a/lib/file/book-11.txt b/lib/file/book-11.txt
new file mode 100644
index 00000000..bd3c0593
--- /dev/null
+++ b/lib/file/book-11.txt
@@ -0,0 +1,250 @@
+The Wood spider is a somewhat rare animal,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Manes is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Bloodshot eye is a somewhat rare monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Pink naga is an uncommon monster,
+occurring at surface depths.
+She is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Pink jelly is a common monster,
+occurring at surface depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant pink frog is a common animal,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Green icky thing is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Zombified kobold is a common undead monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Lost soul is an uncommon undead monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Dark elf is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has an inkling of magical powers.
+
+The Night lizard is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Mughash the Kobold Lord is a unique monster,
+occurring at surface depths.
+He is extremely weak, moderately dangerous,
+and has no magical powers.
+
+Wormtongue, Agent of Saruman is a unique monster,
+occurring at surface depths.
+He is very weak, not at all dangerous,
+and has remarkable magical powers.
+
+Lagduf, the Snaga is a unique orc,
+occurring at surface depths.
+He is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Brown yeek is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Novice ranger is a common monster,
+occurring at surface depths.
+He is laughably weak, just a little bit dangerous,
+and has an inkling of magical powers.
+
+The Giant salamander is a common animal,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Green mold is an uncommon monster,
+occurring at surface depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Skeleton orc is a common undead monster,
+occurring at surface depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Novice paladin is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, just a little bit dangerous,
+and has an inkling of magical powers.
+
+The Lemure is a somewhat rare monster,
+occurring at surface depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Hill orc is a common monster,
+occurring at surface depths.
+He is extremely weak, just a small bit dangerous,
+and has no magical powers.
+
+The Bandit is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Yeti is a somewhat rare animal,
+occurring at surface depths.
+It is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Bloodshot icky thing is a somewhat rare monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Giant grey rat is a common animal,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Black harpy is a common animal,
+occurring at surface depths.
+She is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Orc shaman is a common orc,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has rudimentary magical powers.
+
+The Baby blue dragon is an uncommon dragon,
+occurring at surface depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Baby white dragon is an uncommon dragon,
+occurring at surface depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Baby green dragon is an uncommon dragon,
+occurring at surface depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Baby black dragon is an uncommon dragon,
+occurring at surface depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Baby red dragon is an uncommon dragon,
+occurring at surface depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Giant pink ant is an uncommon monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+Brodda, the Easterling is a unique monster,
+occurring at surface depths.
+It is very weak, just a little bit dangerous,
+and has no magical powers.
+
+The King cobra is an uncommon animal,
+occurring at surface depths.
+It is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Giant spider is an uncommon animal,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Dark elven mage is a common monster,
+occurring at very shallow depths.
+He is laughably weak, not at all dangerous,
+and has simple magical powers.
+
+Orfax, Son of Boldor is a unique animal,
+occurring at very shallow depths.
+He is extremely weak, somewhat dangerous,
+and has remarkable magical powers.
+
+The Dark elven warrior is a common monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Clear mushroom patch is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+Grishnakh, the Hill Orc is a unique orc,
+occurring at very shallow depths.
+He is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant white tick is an uncommon animal,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Hairy mold is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Disenchanter mold is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Pseudo dragon is an uncommon monster,
+occurring at very shallow depths.
+It is very weak, not at all dangerous,
+and has simple magical powers.
+
+The Tengu is a common monster,
+occurring at very shallow depths.
+It is extremely weak, just a small bit dangerous,
+and has rudimentary magical powers.
+
+The Creeping gold coins is a somewhat rare animal,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Wolf is a common animal,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Giant fruit fly is a legendary animal,
+occurring at very shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-12.txt b/lib/file/book-12.txt
new file mode 100644
index 00000000..d111d15d
--- /dev/null
+++ b/lib/file/book-12.txt
@@ -0,0 +1,250 @@
+The Panther is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Brigand is an uncommon monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Baby multi-hued dragon is an uncommon dragon,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has arcane magical powers.
+
+The Hippogryph is a common monster,
+occurring at very shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Zombified orc is a common undead monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Gnome mage is an uncommon monster,
+occurring at very shallow depths.
+He is laughably weak, not one little bit dangerous,
+and has unremarkable magical powers.
+
+The Black mamba is a somewhat rare animal,
+occurring at very shallow depths.
+It is extremely weak, just a small bit dangerous,
+and has no magical powers.
+
+The White wolf is a common animal,
+occurring at very shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Grape jelly is a somewhat rare monster,
+occurring at very shallow depths.
+It is moderately strong, just a small bit dangerous,
+and has no magical powers.
+
+The Nether worm mass is a somewhat rare animal,
+occurring at very shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Golfimbul, the Hill Orc Chief is a unique orc,
+occurring at very shallow depths.
+He is somewhat weak, moderately dangerous,
+and has no magical powers.
+
+The Master yeek is an uncommon animal,
+occurring at very shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has arcane magical powers.
+
+The Priest is a common monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has unremarkable magical powers.
+
+The Dark elven priest is a common monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has unremarkable magical powers.
+
+The Air spirit is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Skeleton human is a common undead monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Zombified human is a common undead monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Tiger is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Moaning spirit is an uncommon undead monster,
+occurring at very shallow depths.
+It is laughably weak, moderately dangerous,
+and has rudimentary magical powers.
+
+The Swordsman is a common monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Stegocentipede is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Spotted jelly is a somewhat rare monster,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Drider is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, just a little bit dangerous,
+and has rudimentary magical powers.
+
+The Killer brown beetle is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+Boldor, King of the Yeeks is a unique animal,
+occurring at very shallow depths.
+He is very weak, somewhat dangerous,
+and has remarkable magical powers.
+
+The Ogre is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, just a small bit dangerous,
+and has no magical powers.
+
+The Creeping mithril coins is a rare animal,
+occurring at very shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Illusionist is an uncommon monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has arcane magical powers.
+
+The Druid is an uncommon monster,
+occurring at very shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has remarkable magical powers.
+
+The Black orc is an uncommon orc,
+occurring at very shallow depths.
+He is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Ochre jelly is a somewhat rare monster,
+occurring at very shallow depths.
+It is extremely weak, very dangerous,
+and has no magical powers.
+
+The Giant flea is a common monster,
+occurring at very shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Ufthak of Cirith Ungol is a unique orc,
+occurring at very shallow depths.
+He is somewhat weak, moderately dangerous,
+and has no magical powers.
+
+The Giant white dragon fly is a somewhat rare animal,
+occurring at very shallow depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Blue icky thing is a rare monster,
+occurring at very shallow depths.
+It is laughably weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Hill giant is a common monster,
+occurring at very shallow depths.
+It is very weak, not one little bit dangerous,
+and has no magical powers.
+
+The Flesh golem is a common monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Warg is an uncommon animal,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Giant black louse is a common monster,
+occurring at very shallow depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Lurker is a somewhat rare monster,
+occurring at very shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Wererat is an uncommon animal,
+occurring at very shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has simple magical powers.
+
+The Black ogre is an uncommon giant,
+occurring at very shallow depths.
+It is very weak, just a little bit dangerous,
+and has no magical powers.
+
+The Magic mushroom patch is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, not at all dangerous,
+and has arcane magical powers.
+
+The Guardian naga is an uncommon monster,
+occurring at very shallow depths.
+She is somewhat weak, not one little bit dangerous,
+and has no magical powers.
+
+The Light hound is a common monster,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has an inkling of magical powers.
+
+The Dark hound is a common monster,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has an inkling of magical powers.
+
+The Half-orc is a somewhat rare orc,
+occurring at very shallow depths.
+He is very weak, just a small bit dangerous,
+and has no magical powers.
+
+The Giant tarantula is a somewhat rare animal,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Giant clear centipede is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Mirkwood spider is an uncommon animal,
+occurring at very shallow depths.
+It is laughably weak, moderately dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-13.txt b/lib/file/book-13.txt
new file mode 100644
index 00000000..b8a15e3f
--- /dev/null
+++ b/lib/file/book-13.txt
@@ -0,0 +1,250 @@
+The Frost giant is a common giant,
+occurring at very shallow depths.
+It is very weak, not at all dangerous,
+and has no magical powers.
+
+The Griffon is a common animal,
+occurring at very shallow depths.
+It is very weak, not one little bit dangerous,
+and has no magical powers.
+
+The Homonculous is a somewhat rare monster,
+occurring at very shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Gnome mage is an uncommon monster,
+occurring at very shallow depths.
+He is laughably weak, just a little bit dangerous,
+and has unremarkable magical powers.
+
+The Clear hound is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Clay golem is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Umber hulk is a common animal,
+occurring at very shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Orc captain is a somewhat rare orc,
+occurring at very shallow depths.
+He is very weak, not one little bit dangerous,
+and has no magical powers.
+
+The Gelatinous cube is a rare monster,
+occurring at very shallow depths.
+It is somewhat weak, hellishly dangerous,
+and has no magical powers.
+
+The Giant green dragon fly is an uncommon animal,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Fire giant is an uncommon giant,
+occurring at very shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Hummerhorn is an extremely rare animal,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+Ulfast, Son of Ulfang is a unique monster,
+occurring at very shallow depths.
+He is somewhat weak, just a little bit dangerous,
+and has no magical powers.
+
+The Quasit is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, somewhat dangerous,
+and has average magical powers.
+
+The Imp is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, moderately dangerous,
+and has average magical powers.
+
+The Forest troll is a common troll,
+occurring at very shallow depths.
+He is very weak, just a little bit dangerous,
+and has no magical powers.
+
+Nar, the Dwarf is a unique monster,
+occurring at very shallow depths.
+He is somewhat strong, moderately dangerous,
+and has simple magical powers.
+
+The 2-headed hydra is an uncommon monster,
+occurring at very shallow depths.
+It is somewhat weak, not one little bit dangerous,
+and has no magical powers.
+
+The Water spirit is a common monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant pink scorpion is a common monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Earth spirit is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Fire spirit is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Fire hound is a common animal,
+occurring at very shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Cold hound is a common animal,
+occurring at very shallow depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Energy hound is a common animal,
+occurring at very shallow depths.
+It is laughably weak, moderately dangerous,
+and has no magical powers.
+
+The Mimic (potion) is a somewhat rare monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has small magical powers.
+
+The Blink dog is an uncommon monster,
+occurring at very shallow depths.
+It is laughably weak, just a little bit dangerous,
+and has rudimentary magical powers.
+
+The Uruk is a common orc,
+occurring at very shallow depths.
+He is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+Shagrat, the Orc Captain is a unique orc,
+occurring at very shallow depths.
+He is moderately strong, somewhat dangerous,
+and has no magical powers.
+
+Gorbag, the Orc Captain is a unique orc,
+occurring at very shallow depths.
+He is moderately strong, somewhat dangerous,
+and has no magical powers.
+
+The Shambling mound is an uncommon monster,
+occurring at very shallow depths.
+It is extremely weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Stone giant is a common monster,
+occurring at very shallow depths.
+It is very weak, not one little bit dangerous,
+and has no magical powers.
+
+The Giant black dragon fly is an uncommon animal,
+occurring at very shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Stone golem is an uncommon monster,
+occurring at very shallow depths.
+It is very weak, moderately dangerous,
+and has no magical powers.
+
+The Red mold is a common monster,
+occurring at very shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant gold dragon fly is an uncommon animal,
+occurring at very shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Bolg, Son of Azog is a unique orc,
+occurring at somewhat shallow depths.
+He is somewhat strong, somewhat dangerous,
+and has no magical powers.
+
+The Phase spider is an uncommon animal,
+occurring at somewhat shallow depths.
+It is laughably weak, moderately dangerous,
+and has rudimentary magical powers.
+
+The 3-headed hydra is an uncommon monster,
+occurring at somewhat shallow depths.
+It is somewhat strong, not one little bit dangerous,
+and has no magical powers.
+
+The Earth hound is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Air hound is a common animal,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Sabre-tooth tiger is an uncommon monster,
+occurring at somewhat shallow depths.
+It is somewhat weak, not one little bit dangerous,
+and has no magical powers.
+
+The Water hound is an uncommon animal,
+occurring at somewhat shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+The Chimera is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, not at all dangerous,
+and has no magical powers.
+
+The Quylthulg is a common monster,
+occurring at somewhat shallow depths.
+It is laughably weak, just a small bit dangerous,
+and has rudimentary magical powers.
+
+The Sasquatch is a somewhat rare animal,
+occurring at somewhat shallow depths.
+It is moderately strong, not one little bit dangerous,
+and has no magical powers.
+
+The Werewolf is a common animal,
+occurring at somewhat shallow depths.
+It is moderately strong, not one little bit dangerous,
+and has no magical powers.
+
+The Dark elven lord is an uncommon monster,
+occurring at somewhat shallow depths.
+He is somewhat weak, not one little bit dangerous,
+and has average magical powers.
+
+The Cloud giant is a common giant,
+occurring at somewhat shallow depths.
+It is somewhat weak, somewhat dangerous,
+and has no magical powers.
+
+Ugluk, the Uruk is a unique orc,
+occurring at somewhat shallow depths.
+He is very strong, somewhat dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-14.txt b/lib/file/book-14.txt
new file mode 100644
index 00000000..b987ea99
--- /dev/null
+++ b/lib/file/book-14.txt
@@ -0,0 +1,250 @@
+Lugdush, the Uruk is a unique orc,
+occurring at somewhat shallow depths.
+He is extremely strong, moderately dangerous,
+and has no magical powers.
+
+The Blue dragon bat is a common animal,
+occurring at somewhat shallow depths.
+It is laughably weak, not at all dangerous,
+and has an inkling of magical powers.
+
+The Mimic (scroll) is a somewhat rare monster,
+occurring at somewhat shallow depths.
+It is extremely weak, moderately dangerous,
+and has average magical powers.
+
+The Fire vortex is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Water vortex is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Cold vortex is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Energy vortex is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Mummified orc is a common undead monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Killer stag beetle is a common monster,
+occurring at somewhat shallow depths.
+It is very weak, not one little bit dangerous,
+and has no magical powers.
+
+The Iron golem is an uncommon monster,
+occurring at somewhat shallow depths.
+It is unimaginably strong, moderately dangerous,
+and has no magical powers.
+
+The Giant yellow scorpion is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Black ooze is a common monster,
+occurring at somewhat shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Hardened warrior is a common monster,
+occurring at somewhat shallow depths.
+He is very weak, not one little bit dangerous,
+and has no magical powers.
+
+Azog, King of the Uruk-Hai is a unique orc,
+occurring at somewhat shallow depths.
+He is alarmingly strong, moderately dangerous,
+and has no magical powers.
+
+The Master rogue is an uncommon monster,
+occurring at somewhat shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+The Red dragon bat is a common animal,
+occurring at somewhat shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has an inkling of magical powers.
+
+The Killer white beetle is a common monster,
+occurring at somewhat shallow depths.
+It is very weak, not one little bit dangerous,
+and has no magical powers.
+
+The Giant bronze dragon fly is a common animal,
+occurring at very shallow depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Forest wight is a common undead monster,
+occurring at somewhat shallow depths.
+It is extremely weak, just a little bit dangerous,
+and has an inkling of magical powers.
+
+Ibun, Son of Mim is a unique monster,
+occurring at somewhat shallow depths.
+He is alarmingly strong, somewhat dangerous,
+and has small magical powers.
+
+Khim, Son of Mim is a unique monster,
+occurring at somewhat shallow depths.
+He is alarmingly strong, somewhat dangerous,
+and has small magical powers.
+
+The 4-headed hydra is an uncommon monster,
+occurring at somewhat shallow depths.
+It is strong, not one little bit dangerous,
+and has no magical powers.
+
+The Mummified human is a common undead monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has no magical powers.
+
+The Vampire bat is an uncommon undead monster,
+occurring at somewhat shallow depths.
+It is extremely weak, moderately dangerous,
+and has no magical powers.
+
+Sangahyando of Umbar is a unique monster,
+occurring at somewhat shallow depths.
+He is extremely strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+Angamaite of Umbar is a unique monster,
+occurring at somewhat shallow depths.
+He is extremely strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Banshee is an uncommon undead monster,
+occurring at somewhat shallow depths.
+She is laughably weak, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Pukelman is a somewhat rare monster,
+occurring at somewhat shallow depths.
+It is unimaginably strong, moderately dangerous,
+and has small magical powers.
+
+The Dark elven druid is a somewhat rare monster,
+occurring at somewhat shallow depths.
+He is moderately strong, just a little bit dangerous,
+and has unremarkable magical powers.
+
+The Stone troll is a common troll,
+occurring at somewhat shallow depths.
+He is very weak, just a little bit dangerous,
+and has no magical powers.
+
+The Troll priest is a common troll,
+occurring at somewhat shallow depths.
+He is somewhat weak, just a little bit dangerous,
+and has unremarkable magical powers.
+
+The Wereworm is a somewhat rare animal,
+occurring at somewhat shallow depths.
+It is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+The Carrion crawler is an uncommon animal,
+occurring at somewhat shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Killer pink beetle is an uncommon monster,
+occurring at somewhat shallow depths.
+It is very weak, just a small bit dangerous,
+and has no magical powers.
+
+The Giant grey ant is a common monster,
+occurring at somewhat shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has no magical powers.
+
+Ulwarth, Son of Ulfang is a unique monster,
+occurring at somewhat shallow depths.
+He is extremely strong, just a little bit dangerous,
+and has no magical powers.
+
+The Displacer beast is an uncommon monster,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a little bit dangerous,
+and has no magical powers.
+
+The Giant red tick is a common animal,
+occurring at somewhat shallow depths.
+It is extremely weak, just a small bit dangerous,
+and has no magical powers.
+
+The Cave ogre is a common monster,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has no magical powers.
+
+The White wraith is a common undead monster,
+occurring at somewhat shallow depths.
+It is extremely weak, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Monadic Deva is a legendary monster,
+occurring at somewhat shallow depths.
+It is somewhat weak, moderately dangerous,
+and has small magical powers.
+
+Mim, Betrayer of Turin is a unique monster,
+occurring at somewhat shallow depths.
+He is hellishly strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The Killer red beetle is a common animal,
+occurring at somewhat shallow depths.
+It is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Creeping adamantite coins is a rare animal,
+occurring at somewhat shallow depths.
+It is somewhat strong, moderately dangerous,
+and has no magical powers.
+
+The Algroth is a common monster,
+occurring at somewhat shallow depths.
+It is somewhat weak, moderately dangerous,
+and has no magical powers.
+
+The Vibration hound is a somewhat rare animal,
+occurring at somewhat shallow depths.
+It is very weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Nexus hound is a somewhat rare animal,
+occurring at somewhat shallow depths.
+It is very weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Ogre mage is an uncommon giant,
+occurring at somewhat shallow depths.
+It is moderately strong, not one little bit dangerous,
+and has arcane magical powers.
+
+Lokkak, the Ogre Chieftain is a unique giant,
+occurring at somewhat shallow depths.
+He is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+The Vampire is a common undead monster,
+occurring at somewhat shallow depths.
+It is somewhat weak, moderately dangerous,
+and has arcane magical powers.
+
diff --git a/lib/file/book-15.txt b/lib/file/book-15.txt
new file mode 100644
index 00000000..5f33444b
--- /dev/null
+++ b/lib/file/book-15.txt
@@ -0,0 +1,250 @@
+The Gorgimera is an uncommon monster,
+occurring at somewhat shallow depths.
+It is somewhat strong, just a small bit dangerous,
+and has no magical powers.
+
+The Colbran is an uncommon monster,
+occurring at somewhat shallow depths.
+It is unimaginably strong, moderately dangerous,
+and has an inkling of magical powers.
+
+The Spirit naga is an uncommon monster,
+occurring at somewhat shallow depths.
+She is somewhat strong, somewhat dangerous,
+and has unremarkable magical powers.
+
+The 5-headed hydra is an uncommon animal,
+occurring at somewhat shallow depths.
+It is extremely strong, moderately dangerous,
+and has rudimentary magical powers.
+
+The Black knight is a common monster,
+occurring at somewhat shallow depths.
+He is somewhat weak, not one little bit dangerous,
+and has rudimentary magical powers.
+
+Uldor the Accursed is a unique monster,
+occurring at somewhat shallow depths.
+He is unimaginably strong, just a little bit dangerous,
+and has no magical powers.
+
+The Mage is a common monster,
+occurring at somewhat shallow depths.
+He is extremely weak, not one little bit dangerous,
+and has unimaginably powerful magical powers.
+
+The Mind flayer is a common monster,
+occurring at somewhat shallow depths.
+It is very weak, moderately dangerous,
+and has remarkable magical powers.
+
+Draebor, the Imp is a unique monster,
+occurring at somewhat shallow depths.
+It is somewhat strong, very dangerous,
+and has arcane magical powers.
+
+The Basilisk is a somewhat rare animal,
+occurring at somewhat shallow depths.
+It is strong, not at all dangerous,
+and has no magical powers.
+
+The Ice troll is a common troll,
+occurring at somewhat shallow depths.
+He is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant purple worm is a somewhat rare animal,
+occurring at somewhat shallow depths.
+It is somewhat strong, somewhat dangerous,
+and has no magical powers.
+
+The Movanic Deva is a legendary monster,
+occurring at somewhat shallow depths.
+It is moderately strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The Catoblepas is an uncommon animal,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has no magical powers.
+
+The Mimic (ring) is a somewhat rare monster,
+occurring at somewhat shallow depths.
+It is moderately strong, extremely dangerous,
+and has hellishly powerful magical powers.
+
+The Young blue dragon is a common dragon,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Young white dragon is a common dragon,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Young green dragon is a common dragon,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Young bronze dragon is a somewhat rare dragon,
+occurring at somewhat shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Mithril golem is a rare monster,
+occurring at shallow depths.
+It is hellishly strong, moderately dangerous,
+and has no magical powers.
+
+The Shadow drake is an uncommon dragon,
+occurring at shallow depths.
+It is very weak, just a small bit dangerous,
+and has simple magical powers.
+
+The Skeleton troll is a common undead monster,
+occurring at shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Manticore is an uncommon monster,
+occurring at shallow depths.
+It is very weak, not at all dangerous,
+and has an inkling of magical powers.
+
+The Giant blue ant is an uncommon animal,
+occurring at shallow depths.
+It is extremely weak, just a little bit dangerous,
+and has no magical powers.
+
+The Giant army ant is a somewhat rare monster,
+occurring at shallow depths.
+It is extremely weak, just a small bit dangerous,
+and has no magical powers.
+
+The Grave wight is a common undead monster,
+occurring at shallow depths.
+It is extremely weak, just a little bit dangerous,
+and has rudimentary magical powers.
+
+The Killer slicer beetle is an uncommon monster,
+occurring at shallow depths.
+It is very weak, not at all dangerous,
+and has no magical powers.
+
+The Ghost is a common undead monster,
+occurring at shallow depths.
+It is extremely weak, alarmingly dangerous,
+and has an inkling of magical powers.
+
+The Death watch beetle is a somewhat rare monster,
+occurring at shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has no magical powers.
+
+The Ogre shaman is an uncommon giant,
+occurring at shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has advanced magical powers.
+
+The Nexus quylthulg is a common monster,
+occurring at shallow depths.
+It is extremely weak, not at all dangerous,
+and has average magical powers.
+
+Shelob, Spider of Darkness is a unique animal,
+occurring at shallow depths.
+She is hellishly strong, unimaginably dangerous,
+and has very powerful magical powers.
+
+The Ninja is an uncommon monster,
+occurring at shallow depths.
+He is very weak, moderately dangerous,
+and has no magical powers.
+
+The Memory moss is a somewhat rare monster,
+occurring at shallow depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Storm giant is a common giant,
+occurring at shallow depths.
+It is moderately strong, moderately dangerous,
+and has average magical powers.
+
+The Cave troll is a common troll,
+occurring at shallow depths.
+He is somewhat weak, somewhat dangerous,
+and has no magical powers.
+
+The Half-troll is an uncommon troll,
+occurring at shallow depths.
+He is moderately strong, somewhat dangerous,
+and has no magical powers.
+
+The Mystic is a somewhat rare monster,
+occurring at shallow depths.
+He is moderately strong, moderately dangerous,
+and has rudimentary magical powers.
+
+The Barrow wight is a somewhat rare undead monster,
+occurring at shallow depths.
+It is extremely weak, somewhat dangerous,
+and has small magical powers.
+
+The Giant skeleton troll is a common undead monster,
+occurring at shallow depths.
+It is somewhat strong, moderately dangerous,
+and has no magical powers.
+
+The Chaos drake is a somewhat rare dragon,
+occurring at shallow depths.
+It is strong, somewhat dangerous,
+and has remarkable magical powers.
+
+The Law drake is a somewhat rare dragon,
+occurring at shallow depths.
+It is strong, somewhat dangerous,
+and has average magical powers.
+
+The Balance drake is a somewhat rare dragon,
+occurring at shallow depths.
+It is very strong, somewhat dangerous,
+and has hellishly powerful magical powers.
+
+The Ethereal drake is a somewhat rare dragon,
+occurring at shallow depths.
+It is somewhat strong, somewhat dangerous,
+and has unremarkable magical powers.
+
+Bert the Stone Troll is a unique troll,
+occurring at shallow depths.
+He is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+Bill the Stone Troll is a unique troll,
+occurring at shallow depths.
+He is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+Tom the Stone Troll is a unique troll,
+occurring at shallow depths.
+He is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+The Shade is a somewhat rare undead monster,
+occurring at shallow depths.
+It is somewhat weak, moderately dangerous,
+and has small magical powers.
+
+The Spectre is a somewhat rare undead monster,
+occurring at shallow depths.
+It is somewhat weak, moderately dangerous,
+and has small magical powers.
+
+The Water troll is a common troll,
+occurring at shallow depths.
+He is moderately strong, moderately dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-16.txt b/lib/file/book-16.txt
new file mode 100644
index 00000000..d2b36408
--- /dev/null
+++ b/lib/file/book-16.txt
@@ -0,0 +1,250 @@
+The Fire elemental is an uncommon monster,
+occurring at shallow depths.
+It is very weak, moderately dangerous,
+and has no magical powers.
+
+The Astral Deva is a legendary monster,
+occurring at shallow depths.
+It is somewhat strong, alarmingly dangerous,
+and has advanced magical powers.
+
+The Water elemental is an uncommon monster,
+occurring at shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Invisible stalker is a somewhat rare monster,
+occurring at shallow depths.
+It is very weak, moderately dangerous,
+and has no magical powers.
+
+The Carrion crawler is an uncommon animal,
+occurring at shallow depths.
+It is very weak, somewhat dangerous,
+and has no magical powers.
+
+The Master thief is an uncommon monster,
+occurring at shallow depths.
+He is very weak, not at all dangerous,
+and has no magical powers.
+
+Ulfang the Black is a unique monster,
+occurring at shallow depths.
+He is unimaginably strong, just a little bit dangerous,
+and has no magical powers.
+
+The Lich is a somewhat rare undead monster,
+occurring at shallow depths.
+It is somewhat weak, very dangerous,
+and has very powerful magical powers.
+
+The Master vampire is a somewhat rare undead monster,
+occurring at shallow depths.
+It is moderately strong, moderately dangerous,
+and has very powerful magical powers.
+
+The Giant grey scorpion is a rare monster,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has no magical powers.
+
+The Earth elemental is an uncommon monster,
+occurring at shallow depths.
+It is somewhat weak, very dangerous,
+and has no magical powers.
+
+The Air elemental is an uncommon monster,
+occurring at shallow depths.
+It is very weak, alarmingly dangerous,
+and has no magical powers.
+
+The Hell hound is a somewhat rare animal,
+occurring at shallow depths.
+It is moderately strong, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Eog golem is a rare monster,
+occurring at shallow depths.
+It is hellishly strong, moderately dangerous,
+and has no magical powers.
+
+The Olog is a common troll,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has no magical powers.
+
+The Dagashi is a rare monster,
+occurring at shallow depths.
+He is somewhat weak, extremely dangerous,
+and has no magical powers.
+
+The Gravity hound is an uncommon animal,
+occurring at shallow depths.
+It is somewhat weak, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Acidic cytoplasm is an extremely rare monster,
+occurring at shallow depths.
+It is moderately strong, hellishly dangerous,
+and has no magical powers.
+
+The Inertia hound is an uncommon animal,
+occurring at shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Impact hound is an uncommon animal,
+occurring at shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has no magical powers.
+
+The Dread is an uncommon undead monster,
+occurring at shallow depths.
+It is somewhat strong, moderately dangerous,
+and has simple magical powers.
+
+The Ooze elemental is a somewhat rare monster,
+occurring at shallow depths.
+It is very weak, hellishly dangerous,
+and has small magical powers.
+
+The Smoke elemental is a somewhat rare monster,
+occurring at shallow depths.
+It is very weak, moderately dangerous,
+and has rudimentary magical powers.
+
+The Young black dragon is a common dragon,
+occurring at shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Mumak is a somewhat rare monster,
+occurring at shallow depths.
+It is alarmingly strong, not one little bit dangerous,
+and has no magical powers.
+
+The Giant red ant is a common animal,
+occurring at shallow depths.
+It is very weak, moderately dangerous,
+and has no magical powers.
+
+The Mature white dragon is a common dragon,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Xorn is an uncommon monster,
+occurring at shallow depths.
+It is very weak, very dangerous,
+and has no magical powers.
+
+The Shadow is a somewhat rare undead monster,
+occurring at shallow depths.
+It is very weak, extremely dangerous,
+and has an inkling of magical powers.
+
+The Phantom is a somewhat rare undead monster,
+occurring at shallow depths.
+It is somewhat strong, extremely dangerous,
+and has an inkling of magical powers.
+
+The Grey wraith is a common undead monster,
+occurring at shallow depths.
+It is very weak, somewhat dangerous,
+and has small magical powers.
+
+The Young multi-hued dragon is a common dragon,
+occurring at shallow depths.
+It is somewhat weak, very dangerous,
+and has very powerful magical powers.
+
+The Colossus is a rare monster,
+occurring at shallow depths.
+It is hellishly strong, extremely dangerous,
+and has no magical powers.
+
+The Young gold dragon is an uncommon dragon,
+occurring at shallow depths.
+It is somewhat weak, not at all dangerous,
+and has an inkling of magical powers.
+
+Rogrog the Black Troll is a unique troll,
+occurring at shallow depths.
+He is hellishly strong, moderately dangerous,
+and has no magical powers.
+
+The Mature blue dragon is a common dragon,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Mature green dragon is a common dragon,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Mature bronze dragon is an uncommon dragon,
+occurring at shallow depths.
+It is somewhat strong, just a small bit dangerous,
+and has rudimentary magical powers.
+
+The Young red dragon is a common dragon,
+occurring at shallow depths.
+It is somewhat weak, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Trapper is a somewhat rare monster,
+occurring at shallow depths.
+It is strong, moderately dangerous,
+and has no magical powers.
+
+The Bodak is an uncommon monster,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has average magical powers.
+
+The Ice elemental is an uncommon monster,
+occurring at shallow depths.
+It is moderately strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The Necromancer is an uncommon monster,
+occurring at shallow depths.
+He is somewhat weak, just a little bit dangerous,
+and has unimaginably powerful magical powers.
+
+Lorgan, Chief of the Easterlings is a unique monster,
+occurring at shallow depths.
+He is hellishly strong, very dangerous,
+and has rudimentary magical powers.
+
+The Demonologist is an uncommon monster,
+occurring at shallow depths.
+He is somewhat weak, not at all dangerous,
+and has unremarkable magical powers.
+
+The Mummified troll is a common undead monster,
+occurring at shallow depths.
+It is very weak, moderately dangerous,
+and has no magical powers.
+
+The Queen Ant is a unique animal,
+occurring at shallow depths.
+She is hellishly strong, moderately dangerous,
+and has rudimentary magical powers.
+
+The Will o' the Wisp is a rare monster,
+occurring at shallow depths.
+It is somewhat weak, hellishly dangerous,
+and has average magical powers.
+
+The Magma elemental is an uncommon monster,
+occurring at shallow depths.
+It is moderately strong, extremely dangerous,
+and has simple magical powers.
+
+The Black pudding is an extremely rare monster,
+occurring at shallow depths.
+It is moderately strong, hellishly dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-17.txt b/lib/file/book-17.txt
new file mode 100644
index 00000000..18f2b663
--- /dev/null
+++ b/lib/file/book-17.txt
@@ -0,0 +1,250 @@
+The Killer blue beetle is an uncommon animal,
+occurring at shallow depths.
+It is somewhat weak, somewhat dangerous,
+and has no magical powers.
+
+The Nexus vortex is a common monster,
+occurring at shallow depths.
+It is somewhat weak, not one little bit dangerous,
+and has no magical powers.
+
+The Plasma vortex is a common monster,
+occurring at shallow depths.
+It is somewhat weak, somewhat dangerous,
+and has no magical powers.
+
+The Mature red dragon is a common dragon,
+occurring at shallow depths.
+It is somewhat strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Mature gold dragon is an uncommon dragon,
+occurring at shallow depths.
+It is strong, just a small bit dangerous,
+and has rudimentary magical powers.
+
+The Crystal drake is an uncommon dragon,
+occurring at shallow depths.
+It is strong, somewhat dangerous,
+and has small magical powers.
+
+The Mature black dragon is a common dragon,
+occurring at shallow depths.
+It is somewhat strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Mature multi-hued dragon is an uncommon dragon,
+occurring at shallow depths.
+It is very strong, extremely dangerous,
+and has extremely powerful magical powers.
+
+The Death knight is a common monster,
+occurring at shallow depths.
+It is very strong, somewhat dangerous,
+and has remarkable magical powers.
+
+Castamir the Usurper is a unique monster,
+occurring at shallow depths.
+He is alarmingly strong, not at all dangerous,
+and has extremely powerful magical powers.
+
+The Time vortex is a rare monster,
+occurring at shallow depths.
+It is somewhat weak, not one little bit dangerous,
+and has no magical powers.
+
+The Shimmering vortex is a rare monster,
+occurring at shallow depths.
+It is extremely weak, not one little bit dangerous,
+and has small magical powers.
+
+The Ancient blue dragon is a common dragon,
+occurring at shallow depths.
+It is very strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Ancient bronze dragon is an uncommon dragon,
+occurring at shallow depths.
+It is extremely strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Beholder is a rare monster,
+occurring at shallow depths.
+It is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Emperor wight is an uncommon undead monster,
+occurring at shallow depths.
+It is moderately strong, somewhat dangerous,
+and has small magical powers.
+
+The Planetar is a legendary monster,
+occurring at shallow depths.
+It is somewhat strong, extremely dangerous,
+and has hellishly powerful magical powers.
+
+Vargo, Tyrant of Fire is a unique monster,
+occurring at shallow depths.
+It is hellishly strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The Black wraith is an uncommon undead monster,
+occurring at shallow depths.
+It is somewhat strong, somewhat dangerous,
+and has small magical powers.
+
+The Erinyes is an uncommon monster,
+occurring at shallow depths.
+She is very weak, moderately dangerous,
+and has an inkling of magical powers.
+
+The Nether wraith is an uncommon undead monster,
+occurring at shallow depths.
+It is somewhat strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The Eldrak is a somewhat rare troll,
+occurring at shallow depths.
+It is extremely strong, somewhat dangerous,
+and has no magical powers.
+
+The Ettin is a somewhat rare troll,
+occurring at shallow depths.
+It is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+Waldern, King of Water is a unique monster,
+occurring at shallow depths.
+It is hellishly strong, somewhat dangerous,
+and has unimaginably powerful magical powers.
+
+Kavlax the Many-Headed is a unique dragon,
+occurring at shallow depths.
+He is hellishly strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+The Ancient white dragon is a common dragon,
+occurring at shallow depths.
+It is very strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Ancient green dragon is a common dragon,
+occurring at shallow depths.
+It is extremely strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The 7-headed hydra is an uncommon animal,
+occurring at shallow depths.
+It is unimaginably strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The Night mare is a somewhat rare undead monster,
+occurring at shallow depths.
+It is hellishly strong, very dangerous,
+and has no magical powers.
+
+The Vampire lord is a somewhat rare undead monster,
+occurring at shallow depths.
+It is hellishly strong, unimaginably dangerous,
+and has very powerful magical powers.
+
+The Ancient black dragon is a common dragon,
+occurring at shallow depths.
+It is extremely strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Disenchanter worm mass is a somewhat rare animal,
+occurring at shallow-to-moderate depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Rotting quylthulg is a common animal,
+occurring at shallow-to-moderate depths.
+It is extremely weak, just a small bit dangerous,
+and has unremarkable magical powers.
+
+The Spirit troll is a somewhat rare troll,
+occurring at shallow-to-moderate depths.
+It is unimaginably strong, very dangerous,
+and has no magical powers.
+
+The Lesser titan is a somewhat rare monster,
+occurring at shallow-to-moderate depths.
+It is unimaginably strong, moderately dangerous,
+and has unremarkable magical powers.
+
+The 9-headed hydra is an uncommon animal,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, just a small bit dangerous,
+and has simple magical powers.
+
+The Enchantress is a rare monster,
+occurring at shallow-to-moderate depths.
+She is somewhat strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Archpriest is an uncommon monster,
+occurring at shallow-to-moderate depths.
+He is somewhat strong, somewhat dangerous,
+and has arcane magical powers.
+
+The Sorcerer is an uncommon monster,
+occurring at shallow-to-moderate depths.
+He is somewhat strong, somewhat dangerous,
+and has hellishly powerful magical powers.
+
+The Xaren is a common monster,
+occurring at shallow-to-moderate depths.
+It is moderately strong, very dangerous,
+and has no magical powers.
+
+The Giant roc is a somewhat rare animal,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has no magical powers.
+
+Uvatha the Horseman is a unique undead monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, extremely dangerous,
+and has no magical powers.
+
+The Minotaur is an uncommon monster,
+occurring at shallow-to-moderate depths.
+It is unimaginably strong, not one little bit dangerous,
+and has no magical powers.
+
+Medusa, the Gorgon is a unique monster,
+occurring at shallow-to-moderate depths.
+She is hellishly strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+The Death drake is an uncommon dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has small magical powers.
+
+The Ancient red dragon is a common dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Ancient gold dragon is an uncommon dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Great crystal drake is an uncommon dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has small magical powers.
+
+The Vrock is an uncommon monster,
+occurring at shallow-to-moderate depths.
+It is moderately strong, moderately dangerous,
+and has no magical powers.
+
+The Death quasit is a somewhat rare monster,
+occurring at shallow-to-moderate depths.
+It is somewhat strong, alarmingly dangerous,
+and has unremarkable magical powers.
+
diff --git a/lib/file/book-18.txt b/lib/file/book-18.txt
new file mode 100644
index 00000000..c36b205f
--- /dev/null
+++ b/lib/file/book-18.txt
@@ -0,0 +1,250 @@
+Adunaphel the Quiet is a unique undead monster,
+occurring at shallow-to-moderate depths.
+She is hellishly strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+The Dark elven sorceror is an uncommon monster,
+occurring at shallow-to-moderate depths.
+He is extremely strong, just a small bit dangerous,
+and has hellishly powerful magical powers.
+
+The Master lich is an uncommon undead monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, extremely dangerous,
+and has hellishly powerful magical powers.
+
+The Hezrou is a somewhat rare monster,
+occurring at shallow-to-moderate depths.
+It is somewhat strong, moderately dangerous,
+and has rudimentary magical powers.
+
+Akhorahil the Blind is a unique undead monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has hellishly powerful magical powers.
+
+Gorlim, Betrayer of Barahir is a unique monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, hellishly dangerous,
+and has remarkable magical powers.
+
+The Solar is a legendary monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, alarmingly dangerous,
+and has advanced magical powers.
+
+The Glabrezu is an uncommon monster,
+occurring at shallow-to-moderate depths.
+It is strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+Ren the Unclean is a unique undead monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+The Nalfeshnee is an uncommon monster,
+occurring at shallow-to-moderate depths.
+It is very strong, somewhat dangerous,
+and has small magical powers.
+
+The Undead beholder is a rare undead monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Dread is a common undead monster,
+occurring at shallow-to-moderate depths.
+It is somewhat strong, extremely dangerous,
+and has simple magical powers.
+
+The Mumak is an uncommon monster,
+occurring at shallow-to-moderate depths.
+It is alarmingly strong, just a small bit dangerous,
+and has no magical powers.
+
+The Ancient multi-hued dragon is a common dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, alarmingly dangerous,
+and has extremely powerful magical powers.
+
+The Ethereal dragon is an uncommon dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, moderately dangerous,
+and has average magical powers.
+
+Ji Indur Dawndeath is a unique undead monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, moderately dangerous,
+and has very powerful magical powers.
+
+The Marilith is an uncommon monster,
+occurring at shallow-to-moderate depths.
+She is hellishly strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+Quaker, Master of Earth is a unique monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, hellishly dangerous,
+and has rudimentary magical powers.
+
+The Lesser Balrog is a somewhat rare monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, moderately dangerous,
+and has simple magical powers.
+
+Ariel, Queen of Air is a unique monster,
+occurring at shallow-to-moderate depths.
+She is hellishly strong, hellishly dangerous,
+and has average magical powers.
+
+The 11-headed hydra is an uncommon animal,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, just a small bit dangerous,
+and has extremely powerful magical powers.
+
+The Patriarch is an uncommon monster,
+occurring at shallow-to-moderate depths.
+He is extremely strong, somewhat dangerous,
+and has extremely powerful magical powers.
+
+The Dreadmaster is an uncommon undead monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, unimaginably dangerous,
+and has very powerful magical powers.
+
+The Drolem is a somewhat rare dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has small magical powers.
+
+Scatha the Worm is a unique dragon,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has small magical powers.
+
+Dwar, Dog Lord of Waw is a unique undead monster,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+Smaug the Golden is a unique dragon,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has small magical powers.
+
+The Dracolich is an uncommon undead monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, alarmingly dangerous,
+and has simple magical powers.
+
+The Greater titan is a somewhat rare monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, moderately dangerous,
+and has simple magical powers.
+
+The Dracolisk is an uncommon dragon,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has simple magical powers.
+
+The Death mold is a common monster,
+occurring at shallow-to-moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has no magical powers.
+
+Itangast the Fire Drake is a unique dragon,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has small magical powers.
+
+Glaurung, Father of the Dragons is a unique dragon,
+occurring at shallow-to-moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has unremarkable magical powers.
+
+The Master mystic is a somewhat rare monster,
+occurring at moderate depths.
+He is hellishly strong, hellishly dangerous,
+and has small magical powers.
+
+Durin's Bane is a unique monster,
+occurring at moderate depths.
+He is hellishly strong, moderately dangerous,
+and has remarkable magical powers.
+
+The Nightwing is a rare undead monster,
+occurring at moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Nether hound is an uncommon animal,
+occurring at moderate depths.
+It is very strong, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Time hound is a rare animal,
+occurring at moderate depths.
+It is very strong, just a small bit dangerous,
+and has no magical powers.
+
+The Plasma hound is an uncommon animal,
+occurring at moderate depths.
+It is very strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Demonic quylthulg is a common animal,
+occurring at moderate depths.
+It is moderately strong, just a small bit dangerous,
+and has unremarkable magical powers.
+
+The Great storm wyrm is an uncommon dragon,
+occurring at moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+Baphomet the Minotaur Lord is a unique monster,
+occurring at moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has hellishly powerful magical powers.
+
+Harowen the Black Hand is a unique monster,
+occurring at moderate depths.
+He is hellishly strong, moderately dangerous,
+and has no magical powers.
+
+Hoarmurath of Dir is a unique undead monster,
+occurring at moderate depths.
+He is hellishly strong, somewhat dangerous,
+and has hellishly powerful magical powers.
+
+The Grand master mystic is a somewhat rare monster,
+occurring at moderate depths.
+He is hellishly strong, hellishly dangerous,
+and has arcane magical powers.
+
+Khamul the Easterling is a unique undead monster,
+occurring at moderate depths.
+He is hellishly strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+The Ethereal hound is a somewhat rare animal,
+occurring at moderate depths.
+It is unimaginably strong, somewhat dangerous,
+and has an inkling of magical powers.
+
+The Great ice wyrm is an uncommon dragon,
+occurring at moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Phoenix is a unique animal,
+occurring at moderate depths.
+It is hellishly strong, alarmingly dangerous,
+and has hellishly powerful magical powers.
+
+The Nightcrawler is a rare undead monster,
+occurring at moderate depths.
+It is hellishly strong, unimaginably dangerous,
+and has hellishly powerful magical powers.
+
diff --git a/lib/file/book-19.txt b/lib/file/book-19.txt
new file mode 100644
index 00000000..62ecebc9
--- /dev/null
+++ b/lib/file/book-19.txt
@@ -0,0 +1,250 @@
+The Hand druj is a rare undead monster,
+occurring at moderate depths.
+It is very strong, somewhat dangerous,
+and has extremely powerful magical powers.
+
+The Eye druj is a rare undead monster,
+occurring at moderate depths.
+It is unimaginably strong, moderately dangerous,
+and has hellishly powerful magical powers.
+
+The Skull druj is a rare undead monster,
+occurring at moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Chaos vortex is a common monster,
+occurring at moderate depths.
+It is very strong, not one little bit dangerous,
+and has no magical powers.
+
+The Aether vortex is an uncommon monster,
+occurring at moderate depths.
+It is strong, unimaginably dangerous,
+and has hellishly powerful magical powers.
+
+The Lernean Hydra is a unique animal,
+occurring at moderate depths.
+It is hellishly strong, alarmingly dangerous,
+and has hellishly powerful magical powers.
+
+Thuringwethil is a unique undead monster,
+occurring at moderate depths.
+She is hellishly strong, alarmingly dangerous,
+and has very powerful magical powers.
+
+The Great hell wyrm is an uncommon dragon,
+occurring at moderate depths.
+It is hellishly strong, somewhat dangerous,
+and has rudimentary magical powers.
+
+The Draconic quylthulg is a somewhat rare animal,
+occurring at moderate depths.
+It is very strong, just a small bit dangerous,
+and has unremarkable magical powers.
+
+Fundin Bluecloak is a unique monster,
+occurring at moderate depths.
+It is hellishly strong, extremely dangerous,
+and has unimaginably powerful magical powers.
+
+Uriel, Angel of Fire is a unique monster,
+occurring at moderate depths.
+He is hellishly strong, alarmingly dangerous,
+and has hellishly powerful magical powers.
+
+Azriel, Angel of Death is a unique monster,
+occurring at moderate depths.
+He is hellishly strong, extremely dangerous,
+and has hellishly powerful magical powers.
+
+Ancalagon the Black is a unique dragon,
+occurring at moderate depths.
+He is hellishly strong, moderately dangerous,
+and has extremely powerful magical powers.
+
+The Nightwalker is a rare undead monster,
+occurring at moderate depths.
+It is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+Gabriel, the Messenger is a unique monster,
+occurring at moderate depths.
+He is hellishly strong, hellishly dangerous,
+and has remarkable magical powers.
+
+Saruman of Many Colours is a unique monster,
+occurring at moderate-to-deep depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Dreadlord is an uncommon undead monster,
+occurring at moderate-to-deep depths.
+It is hellishly strong, unimaginably dangerous,
+and has arcane magical powers.
+
+The Cat Lord is a unique monster,
+occurring at moderate-to-deep depths.
+He is hellishly strong, hellishly dangerous,
+and has an inkling of magical powers.
+
+The Chaos beetle is a rare monster,
+occurring at moderate-to-deep depths.
+It is hellishly strong, not at all dangerous,
+and has rudimentary magical powers.
+
+The Chaos hound is a common animal,
+occurring at moderate-to-deep depths.
+It is hellishly strong, just a small bit dangerous,
+and has an inkling of magical powers.
+
+The Great Wyrm of Chaos is an uncommon dragon,
+occurring at moderate-to-deep depths.
+It is hellishly strong, somewhat dangerous,
+and has very powerful magical powers.
+
+The Great Wyrm of Law is an uncommon dragon,
+occurring at moderate-to-deep depths.
+It is hellishly strong, somewhat dangerous,
+and has very powerful magical powers.
+
+The Great Wyrm of Balance is a rare dragon,
+occurring at moderate-to-deep depths.
+It is hellishly strong, somewhat dangerous,
+and has hellishly powerful magical powers.
+
+Tselakus, the Dreadlord is a unique undead monster,
+occurring at moderate-to-deep depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+Tiamat, Celestial Dragon of Evil is a unique dragon,
+occurring at deep depths.
+She is hellishly strong, unimaginably dangerous,
+and has hellishly powerful magical powers.
+
+The Black reaver is a somewhat rare undead monster,
+occurring at deep depths.
+It is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Master quylthulg is a somewhat rare animal,
+occurring at deep depths.
+It is hellishly strong, just a small bit dangerous,
+and has hellishly powerful magical powers.
+
+The Greater draconic quylthulg is a somewhat rare animal,
+occurring at deep depths.
+It is hellishly strong, just a small bit dangerous,
+and has simple magical powers.
+
+The Greater rotting quylthulg is a somewhat rare animal,
+occurring at deep depths.
+It is hellishly strong, just a small bit dangerous,
+and has simple magical powers.
+
+Vecna, the Emperor Lich is a unique undead monster,
+occurring at deep depths.
+He is hellishly strong, unimaginably dangerous,
+and has hellishly powerful magical powers.
+
+Omarax the Eye Tyrant is a unique monster,
+occurring at deep depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+Ungoliant, the Unlight is a unique animal,
+occurring at deep depths.
+She is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Aether hound is an uncommon animal,
+occurring at deep depths.
+It is hellishly strong, very dangerous,
+and has hellishly powerful magical powers.
+
+The Mouth of Sauron is a unique monster,
+occurring at deep depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Emperor Quylthulg is a unique animal,
+occurring at deep depths.
+It is hellishly strong, just a little bit dangerous,
+and has unimaginably powerful magical powers.
+
+Qlzqqlzuup, the Lord of Flesh is a unique animal,
+occurring at deep depths.
+It is hellishly strong, just a little bit dangerous,
+and has hellishly powerful magical powers.
+
+Murazor, the Witch-King of Angmar is a unique undead monster,
+occurring at very deep depths.
+He is hellishly strong, extremely dangerous,
+and has hellishly powerful magical powers.
+
+Pazuzu, Lord of Air is a unique monster,
+occurring at very deep depths.
+He is hellishly strong, hellishly dangerous,
+and has advanced magical powers.
+
+The Hell hound is a rare animal,
+occurring at very deep depths.
+It is somewhat strong, moderately dangerous,
+and has an inkling of magical powers.
+
+Cantoras, the Skeletal Lord is a unique undead monster,
+occurring at very deep depths.
+He is hellishly strong, unimaginably dangerous,
+and has hellishly powerful magical powers.
+
+The Tarrasque is a unique monster,
+occurring at very deep depths.
+It is hellishly strong, somewhat dangerous,
+and has average magical powers.
+
+Lungorthin, the Balrog of White Fire is a unique monster,
+occurring at very deep depths.
+He is hellishly strong, extremely dangerous,
+and has advanced magical powers.
+
+Draugluin, Sire of All Werewolves is a unique animal,
+occurring at very deep depths.
+He is hellishly strong, moderately dangerous,
+and has simple magical powers.
+
+Feagwath the Undead Sorceror is a unique undead monster,
+occurring at extremely deep depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+Carcharoth, the Jaws of Thirst is a unique animal,
+occurring at extremely deep depths.
+He is hellishly strong, moderately dangerous,
+and has advanced magical powers.
+
+Cerberus, Guardian of Hades is a unique animal,
+occurring at extremely deep depths.
+It is hellishly strong, moderately dangerous,
+and has very powerful magical powers.
+
+Gothmog, the High Captain of Balrogs is a unique monster,
+occurring at extremely deep depths.
+He is hellishly strong, unimaginably dangerous,
+and has advanced magical powers.
+
+Sauron, the Sorcerer is a unique monster,
+occurring at extremely deep depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+Morgoth, Lord of Darkness is a unique monster,
+occurring at bottomless depths.
+He is hellishly strong, hellishly dangerous,
+and has hellishly powerful magical powers.
+
+The Haughty noble is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
diff --git a/lib/file/book-2.txt b/lib/file/book-2.txt
new file mode 100644
index 00000000..75493c29
--- /dev/null
+++ b/lib/file/book-2.txt
@@ -0,0 +1,90 @@
+
+ Mordekainen's Magical Compendum of Deep Thought, Vol. 3
+ -------------------------------------------------------
+
+
+I think my new thing will be to try to be a real happy guy. I'll just
+walk around being real happy until some jerk says something stupid to
+me.
+
+When you're going up the stairs and you take a step, kick the other
+leg up high behind you to keep people from following too close.
+
+I wonder if angels believe in ghosts.
+
+I don't understand people who say life is a mystery, because what is
+it they want to know?
+
+I think people tend to forget that trees are living creatures. They're
+sort of like dogs. Huge, quiet, motionless dogs, with bark instead of
+fur.
+
+Sometimes I think the world has gone completely mad. And then I think,
+"Aw, who cares?" And then I think, "Hey, what's for supper?"
+
+If a kid asks where rain comes from, I think a cute thing to tell him
+is "God is crying". And if he asks why God is crying, another cute
+thing to tell him is "Probably because of something you did".
+
+Contrary to popular belief, the most dangerous animal is not the lion
+or tiger or even the elephant. The most dangerous animal is a shark
+riding on an elephant, just trampling and eating everything they see.
+
+As I bit into the nectarine, it had a crisp juiciness about it that
+was very pleasurable- until I realized it wasn't a nectarine at all,
+but a HUMAN HEAD!!
+
+Anytime I see something screech across a room and latch onto someone's
+neck, and the guy screams and tries to get it off, I have to laugh,
+because what _is_ that thing?!
+
+If you define cowardice as running away at the first sign of danger,
+screaming and tripping and begging for mercy, then yes, Mister Brave
+Man, I guess I am a coward.
+
+Blow ye winds, like the trumpet blows, but without that noise.
+
+The face of a child can say it all, especially the mouth part of the face.
+
+When I heard that trees grow a new "ring" for each year they live, I
+thought, we humans are kind of like that; we grow a new layer of skin
+each year; and after many years we are thick and unwieldy from all our
+skin layers.
+
+It's too bad that whole families have been torn apart by something as
+simple as wild dogs.
+
+Even though he was an enemy of mine, I had to admit that what he had
+accomplished was a brilliant piece of strategy. First, he punched me,
+then he kicked me, then he punched me again.
+
+To me, truth is not some vague, foggy notion. Truth is real. And, at
+the same unreal. Fiction and fact and everything in-between, plus some
+things I can't remember, all rolled into one big "thing". This is
+truth, to me.
+
+If you're ever stuck in some thick undergrowth, in your underwear,
+don't stop and start thinking of what other words have "under" in
+them, because that's probably the first sign of jungle madness.
+
+Sometimes the beauty of the world is so overwhelming, I just want to
+throw back my head and gargle. Just gargle and gargle, and I don't
+care who hears me, because I am beautiful.
+
+We used to laugh at Grandpa when he'd head off to go fishing. But we
+wouldn't be laughing when he'd come back with some whore he picked up
+in town.
+
+I think in one of my previous lives I was a mighty king, because I
+like people that do what I say.
+
+A man doesn't automatically get my respect. He has to get down in the
+dirt and beg for it.
+
+People think it would be fun to be a bird because you could fly. But
+they forget the negative side, which is the preening.
+
+When I think back on all the blessings I have been given in my life, I
+can't think of a single one, unless you count that rattlesnake that
+granted me all those wishes.
+
diff --git a/lib/file/book-20.txt b/lib/file/book-20.txt
new file mode 100644
index 00000000..c76012af
--- /dev/null
+++ b/lib/file/book-20.txt
@@ -0,0 +1,139 @@
+#####R /----------------------------------------\
+#####R < Adventurer's guide to the Middle-earth >
+#####R \----------------------------------------/
+
+Summary:
+*****/abook-20.txt*1[(a) The Towns]
+*****/bbook-20.txt*2[(b) Other strange and frightening places]
+*****/cbook-20.txt*3[(c) Equipment issues]
+*****/dbook-20.txt*4[(d) Macros]
+
+Introduction:
+
+Middle-earth is vast and mysterious, full of dangers but also full of
+rewards for the brave.
+
+New adventurers should know that pressing < and > can switch
+the wilderness view between a normal scale and a larger map. This map
+makes travelling safer and faster, but you can't enter wilderness
+dungeons from it.
+
+
+~~~~~1
+#####G(a) The Towns
+
+You start in a small village named Bree in the western part of the Middle-earth.
+Here you will also find the entrance to the Barrow-Downs, a fairly safe and
+simple dungeon.
+
+When the Barrow-Downs become too easy for you, and Bree too small,
+you might consider going to Lothlorien, the land of Galadriel. [[[[[BNote that]
+[[[[[Byou should take a lot of food with you, for it is a long journey.] You'll have
+to head south-east following the Moria mountain's chain, then walk around
+the forest of Fangorn to head north to finally find your destination. The Forest
+of Mirkwood (another dungeon) can be found to the north east of Lothlorien.
+
+If you survive this dangerous dungeon, you should head south, following
+the Anduin river. There, near the dark land of Mordor, you will find the
+great town of Gondor, Minas Anor. From there you can want to pay a "visit" to
+the land of Mordor, which is east of Minas Anor.
+
+After Mordor you should finally travel to Gondolin, the hidden town of the
+Noldor. First go back to Bree, and then from there walk northeast and you
+will find it. From this city, you will be able to attack Angband, the
+dungeon of Morgoth, which is north west west of Gondolin.
+
+
+~~~~~2
+#####G(b) Other strange and frightening places
+
+The Old Forest to the west is the last remains of the big forests of the first
+age, but it has been corrupted. It is said it is guarded by a living tree.
+
+You may also wish to investigate the Orc Caves north of Bree; they are
+another place suitable for those finished with the Barrow-Downs. They also
+are rumored to hold great
+mysteries.
+
+The Maze: To the south of Bree there is a magical Maze. Many adventurers
+that ventured there never came back... It is rumored that a Minotaur is
+lurking down there, guarding an ancient and powerful artifact.
+Bring along digging equipment and some means to recall.
+
+Durin's Bane, the Balrog of Moria, guards the Mines of Moria, to the south east
+of Bree.
+
+During the Second Age of the world there was a great island called
+Numenor. The people who dwelt there were wise and powerful, but as time
+passed their last kings fell under the power of Sauron. Under Sauron's
+orders, they tried to attack Valinor, the blessed land, and for this
+Numenor was destroyed, swallowed by the sea. The ruins are still
+accessible, far out to sea to the west of Bree.
+
+Many other strange places wait to be explored by the valiant adventurer,
+but their locations are secret. You will have to find them yourself!
+
+
+~~~~~3
+#####G(c) Equipment issues
+
+Beware adventurer! If you plan to go down into the dungeons be prepared. Some
+items you will need badly.
+
+First think of some light, maybe a lantern is better than the torches.
+
+Second things to mention are your combat equipment. Sometimes the weapon and
+armor you got from your mentor are not enough for the nasties inside the
+dungeons.
+
+And third and most important, [[[[[Balways carry a shovel or other digger with you],
+because there is much rubble in the dungeons, which you cannot clear with
+your hands.
+
+~~~~~4
+#####G(d) Macros
+
+Spellcasters might find that pressing 4 keys (at least) to cast a spell is a
+lot, they are right. That is why there are macros. You can access the macro
+screen by pressing @. You can find help on the *****macrofaq.txt*0[macros] in the docs.
+
+Now you either have the hard way or the easy way.
+
+**The Hard Way**
+
+What the fellow adventurer should know is how to create a basic spell macro.
+Press @ to enter the macro screen.
+Press 4 to create a new macro.
+Press the key to bind the macro to, usually one uses the F* keys(you can combine
+them with the ctrl, shift, ... keys too)
+Enter the key sequence to be done for the macro.
+Press 2 to save the macro.
+
+Now a problem that might arise, imagine your macro looks like: mcaa*t
+to cast manathrust spell('m' to use skill, 'c' for cast a spell skill,
+'a' for first book, 'a' for first spell, *t to… target the first monster).
+This macro will break if you gain a new skill so that "Cast a spell" is no more
+the skill 'c', or if you get a new book. There is a way around that.
+When the game asks for a skill or a spell it allows you to press @ to enter
+the skill/spell name directly, so your macro would become:
+m@Cast a spell\r@Manathrust\r*t
+Now this will always work as long as one of your books have the spell in it.
+
+
+**The Easy Way**
+
+This time you will only use the macro recorder. To do that:
+Press $ to start it
+Now each key you press will be recorded, so press all keys you want.
+It is recommended to start your macro by pressing Escape key a few time, so
+if there are messages when you use the macro it will first erase them instead
+of screwing your macro :)
+it is also recommended to take advantage of the @ key when selecting skills or
+objects whenever the possibility is offered to you. It will make sure you
+always use the good object/skill even if it moves in your inventory.
+Once all keys are pressed press $ again to stop it.
+It you are satisfied with your macro now you get to press the key to bind it
+to.
+
+As in the Hard Way you must use the macro screen if you want to permanently
+save your macros.
diff --git a/lib/file/book-200.txt b/lib/file/book-200.txt
new file mode 100644
index 00000000..21ad9089
--- /dev/null
+++ b/lib/file/book-200.txt
@@ -0,0 +1,5 @@
+4
+34
+21
+6
+0
diff --git a/lib/file/book-201.txt b/lib/file/book-201.txt
new file mode 100644
index 00000000..d89e8f16
--- /dev/null
+++ b/lib/file/book-201.txt
@@ -0,0 +1,5 @@
+4
+49
+11
+3
+0
diff --git a/lib/file/book-202.txt b/lib/file/book-202.txt
new file mode 100644
index 00000000..3c61caee
--- /dev/null
+++ b/lib/file/book-202.txt
@@ -0,0 +1,5 @@
+4
+50
+34
+4
+0
diff --git a/lib/file/book-203.txt b/lib/file/book-203.txt
new file mode 100644
index 00000000..c169a628
--- /dev/null
+++ b/lib/file/book-203.txt
@@ -0,0 +1,5 @@
+4
+60
+56
+3
+0
diff --git a/lib/file/book-4.txt b/lib/file/book-4.txt
new file mode 100644
index 00000000..29501076
--- /dev/null
+++ b/lib/file/book-4.txt
@@ -0,0 +1,11 @@
+Ash nazg durbatuluk,
+ash nazg gimbatul,
+ash nazg thrakatuluk
+agh burzum-ishi krimpatul.
+
+-------------------------------
+
+One Ring to rule them all,
+One Ring to find them,
+One Ring to bring them all,
+And in the darkness bind them.
diff --git a/lib/file/book-6.txt b/lib/file/book-6.txt
new file mode 100644
index 00000000..4a2638cb
--- /dev/null
+++ b/lib/file/book-6.txt
@@ -0,0 +1,263 @@
+
+ Artifact Lore, Vol. I
+ Ancient Weapons
+ ---------------------
+
+
+ The Longsword 'Ringil' (4d5) (+22,+25)
+ The Sword of Fingolfin (High-Elf). When wielded it will
+ haste its wielder. It is a blade of such deathly cold that
+ it shines bright white acting as a permanent light and
+ delivering cold criticals. It slays evil, demons, undead
+ and trolls. Allows you to resist cold, regenerate mana and
+ hit points more quickly with no extra food consumption. Also
+ lets you see invisible and be immune to paralyzation. Being
+ made of white eog, it is capable of casting Ice Storms.
+
+ The Longsword 'Anduril' (3d5) (+10,+15) [+5]
+ The Sword of Aragorn the Dunadan Ranger and King. It
+ increases your strength to match true Kingly strength,
+ increases armour class by +5. Being the 'Flame of the West'
+ it delivers heat criticals and defends against fire. It
+ slays evil, orcs and trolls. It allows you to see invisible
+ and avoid paralyzation. So near to the element fire is the
+ blade, that Fire Balls can be cast from its searing
+ surface.
+
+ The Lead-filled Mace 'Grond' (9d9) (+25,+25) [+10]
+ The Hammer of the Underworld, this is Morgoth's chief
+ weapon. It weighs so much that it slows anything of less
+ than godly status. It is so awesome that it aggravates all
+ that see it. It makes it's wielder see all and resist all.
+ It slays all of Morgoths personal creations of orcs, trolls,
+ and demons and it executes dragons doing five times normal
+ damage against them, (it can kill all but a very few dragons
+ in one mighty blow!). Plus it causes impact criticals that
+ cause earthquakes and it can tunnel through solid granite
+ walls!
+
+ The Two-Handed Sword 'Gurthang' (3d6) (+13,+17)
+ This Iron of Death belonging to Turin Turambar, made of
+ black eog, was designed to slay Dragons (and Trolls), doing
+ five times normal damage against dragons. It gives you the
+ strength to wield it, regenerates your hit points and mana
+ at no extra cost and allows you to be free of paralyzation.
+
+ The Two-Handed Sword 'Mormegil' (6d7) (0,0) [-20]
+ This evil black sword is heavily cursed, slowing its wielder
+ and aggravating all that see it...
+
+ The Broadsword 'Arunruth' (2d5) (+20,+12)
+ This Sword of Accuracy rarely misses. It increases the
+ wielders dexterity, saves from falls, slays orcs and demons,
+ and protects from paralyzation.
+
+ The Broadsword 'Glamdring' (2d5) (+10,+15)
+ This High-Elven sword (mate to Orcrist) was made during foul
+ orc wars of long ago. It was once wielded by Gandalf the
+ Grey Wizard. It slays orcs and other evil, shining bright
+ red continuously, which aids searching and decreases food
+ consumption. Its element is flame, and it gives resistance
+ to flame to the wielder, and slays those who have not.
+
+ The Broadsword 'Orcrist' (2d5) (+10,+15)
+ This High-Elven sword (mate to Glamdring) was made during
+ foul orc wars of long ago. It was once wielded by Thorin
+ Oakenshield the Dwarf-King-Under-the-Mountain. It slays orcs
+ and other evil, shining bright white continuously, which
+ decreases food consumption, plus making the wielder more
+ stealthy. Being of the element frost, it protects from cold,
+ and slays non-cold based creatures.
+
+ The Broadsword 'Aeglin' (2d5) (+12,+16)
+ This High-Elven sword, is the long, lost and forgotten third
+ mate to Orcrist and Glamdring. It is also the most powerful.
+ Like Glamdring and Orcrist, made during the Orc-wars, it
+ slays Orcish-kind, shining bright blue continuously, which
+ decreases food consumption and aiding searching. Being of
+ electric element, it delivers lightning criticals, which it
+ also defends against.
+
+ The Long Bow 'Belthronding' (+20,+22) (+3)
+ This Noldorin black-yew bow, belonged to the greatest elven
+ archer, Beleg Cuthalion, who was slain by his own blade, by
+ his best friend Turin Turumbar. The bow increases stealth
+ and dexterity, and rarely missed its target.
+
+ The Long Bow of Bard (+17,+19)
+ This bow of men gives free action and increased dexterity.
+
+ The Light Crossbow 'Cubragol' (+10,+14)
+ This amazing bow of fire hastes its wielder and brands
+ all bolts with its element.
+
+ The Bastard Sword 'Calris' (5d4) (-20,+20)
+ This sword of Lungorthin the Balrog of White Flame, is an
+ evil cursed sword that needs great mastering to control its
+ powers. If mastered it can execute dragons, slay other evil
+ including demons and trolls, this naturally aggravates them.
+ Also it gives its wielder far greater internal constitution.
+
+ The Spear 'Aeglos' (3d6) (+15,+25) [+5]
+ This Snow-thorn of Gil-Galad the High-Elf, delivers very
+ deep cold-criticals, while protecting you from cold and
+ increasing your armour class. Being elvish it naturally
+ slays orcs and trolls. It increases the wielders wisdom,
+ slows their digestion and frees their actions from holding
+ forces of evil, also casting Frost Balls occasionally.
+
+ The Spear 'Nimloth' (1d6) (+11,+13) (+3 to stealth)
+ This elven spear, branded with frost, allows its wielder
+ to creep up on the Undead and Slay them.
+
+ The Dagger 'Angrist' (2d5) (+10,+15) [+5]
+ This Iron-cleaver of Beren the Edain, increases and sustains
+ dexterity, increases you protection, prevents paralyzation
+ and slays orcs and trolls.
+
+ The Small sword 'Sting' (1d6) (+7,+8)
+ This small weapon is one of the most powerful weapons of
+ Westernesse. Once wielded by Bilbo and Frodo Baggins (of
+ Hobbit kind), Sting slays undead, evil, and orcs and also
+ shines a continuous, bright blue. It increases the wielder's
+ physical statistics and its piercing blue light lights up
+ those normally invisible to sight.
+
+ The Great Axe of Durin (4d4) (+10,+20) [+15]
+ This Wonderful Dwarven Axe was once wielded by Durin the
+ Deathless (Father and King of the Dwarves), gives its
+ wielder high protection, executes dragons, while resisting
+ fire and acid. It slays demons, trolls and orcs, frees
+ action and increases constitution.
+
+ The War Hammer of Aule (5d5) (+19,+21) [+5]
+ This is the great war hammer of the deity, Aule the Smith.
+ Forged in his great furnaces it delivers Shock criticals
+ causing five times normal damage to those not resistant to
+ this element, and even then it executes dragons, slays evil,
+ demons and undead. It resists fire, cold, acid and lightning
+ and allows you to see invisible and be free of paralyzation.
+ It increases its wielder's wisdom so that one may choose
+ wisely what to slay with it. Truly an awesome weapon.
+
+ The Two-Handed Great Flail 'Thunderfist' (3d6) (+5,+18)
+ This weapon of Electricity and Flame, delivers shock and
+ heat criticals, delivering five times or three times normal
+ damage against the respective types of creatures, also slays
+ animals, trolls and orcs. It also gives the wielder great
+ strength.
+
+ The Morningstar 'Bloodspike' (2d6) (+8,+22)
+ This Bloody weapon slays animals, trolls, and orcs. It allows
+ you to see invisible creatures, as well as giving the wielder
+ great strength.
+
+ The Quarterstaff 'Nar-i-vagil' (1d10) (+10,+20)
+ This fiery staff slays animals and resists fire, doing times
+ fire criticals. It also increases the wielders intelligence.
+
+ The Blade of Chaos 'Doomcaller' (6d5) (+18,+28) [-50]
+ This deadly blade calls doom to all who see it, this
+ naturally aggravates them. It shows you wherever a creature
+ is, be it invisible or blocked by a wall. It resists
+ all elements and executes dragons, slays evil, animal, orc
+ and troll. It delivers cold criticals, but severely impedes
+ its wielders health.
+
+ The Three Daggers, 'Narthanc', 'Nimthanc', 'Dethanc' (1d4) (+4,+6)
+ These elemental daggers of flame, frost, and electricity
+ respectively do their elemental criticals, and defend
+ against them. They also cast bolts of their element often.
+
+ The Dagger of Rilia (2d4) (+4,+3)
+ This ancient, and poisonous dagger casts stinking clouds
+ with great frequency.
+
+ The Dagger 'Belangil' (2d4) (+6,+9)
+ This nimble weapon of cold increases the wielder's dexterity
+ and regeneration, while slowing digestion. Being of dark
+ origins it see alls invisible and casts frost balls.
+
+ The Battle Axe of Balli Stonehand (3d6) (+8,+11) [+5]
+ This Dwarvish Battle axe protects from elements, falls and
+ the invisible. It slays all demons, trolls and orcs, giving
+ even dwarves the stealth, strength and constitution to do
+ so, never letting holding spells affect its use in a fight.
+
+ The Battle Axe 'Lotharang' (2d8) (+4,+3)
+ This petty-dwarvish axe, slays orcs and trolls, increasing
+ strength and dexterity. For those of faint heart it also
+ cures medium wounds and cuts.
+
+ The Morningstar 'Firestar' (2d6) (+5,+7) [+2]
+ This weapon of flame casts fire balls.
+
+ The Quarterstaff 'Eriril' (1d10) (+3,+5)
+ This staff of people who believe in the power of mind over
+ matter, greatly increases wisdom and intelligence and gives
+ the power to identify. It also sees invisible and slays all
+ evil.
+
+ The Longsword 'Elvagil' (2d5) (+2,+7)
+ This joyful sword increases dexterity, charisma and stealth.
+ It protects from falls and the invisible, and slays orcs and
+ trolls.
+
+ The Glaive of Pain (9d6) (+0,+30)
+ This weapon is designed to cause pain to anything without
+ discrimination.
+
+ The Lance of the Eorlingas (3d8) (+3,+21)
+ This heavy lance is suprisingly easy to control, allowing
+ slaughter of orcs, trolls and other evil (visible or not).
+
+ The Broad Axe 'Barukkheled' (2d6) (+13,+19)
+ This beautiful axe slays orcs, trolls, giants and other evil
+ (visible or not), while greatly increasing the wielders
+ internal constitution.
+
+ The Trident of Wrath (3d8) (+16,+18)
+ This extremely heavy and dangerous trident belonging to the
+ greatest Maiar spirit, Osse, slaughters evil and undead
+ without mercy wherever they hide, and increases the wielder's
+ strength and dexterity.
+
+ The Scimitar 'Haradekket' (2d5) (+9,+11)
+ This sword of the south slays the invisible undead, evil and
+ animals, and increases the wielder's dexterity. In addition,
+ the magically enhanced blade is rumored to give extra
+ attacks in combat.
+
+ The Lochaber Axe 'Mundwine' (3d8) (+12,+17)
+ This strong friend in battle, slays evil and resists the
+ elements.
+
+ The Cutlass 'Gondricam' (1d7) (+10,+11)
+ This defender increases the wielders dexterity.
+
+ The Sabre 'Careth Asdriag' (1d7) (+6,+8)
+ This lightning-quick blade slays dragons, giants, trolls,
+ orcs, and animals.
+
+ The Rapier 'Forasgil' (1d6) (+12,+19)
+ This glittering ice-blade also slays animals as well as
+ lighting the way.
+
+ The Executioner's Sword 'Crisdurian' (4d5) (+18,+19)
+ This executer slays evil, invisible undead, dragons, giants
+ orcs and trolls.
+
+ The Flail 'Totila' (3d6) (+6,+8) (+2)
+ This flaming flail slays evil in stealth. It also casts
+ confusion.
+
+ The Short sword 'Gilettar' (1d7) (+3,+7)
+ This roguish sword gives better regeneration and slower
+ digestion, slaying all animals with uncanny speed.
+
+ The Katana 'Aglarang' (8d4) (+0,+0)
+ This super-light and sharp katana greatly increases the
+ wielders dexterity and sustains it. Rarely does the wielder
+ get less than four attacks a round with it, and often as
+ many as six!
+
diff --git a/lib/file/book-7.txt b/lib/file/book-7.txt
new file mode 100644
index 00000000..f6a4a49b
--- /dev/null
+++ b/lib/file/book-7.txt
@@ -0,0 +1,141 @@
+
+ Artifact Lore, Vol. II
+ Ancient Armor
+ ----------------------
+
+
+ Adamantite Plate Mail 'Soulkeeper' [40,+20]
+ This amazing armour protects your soul from cold, and from
+ life level loss. It is also capable of fully healing you.
+
+ The Pair of Hard Leather Boots of Feanor [3,+20]
+ These amazing boots belonging to Feanor the High-Elf, haste
+ the wearer permanently and temporarily in combat, making him
+ or her stealthy as well.
+
+ The Pair of Soft Leather Boots 'Dal-i-thalion' [3,+15]
+ These amazing boots of agility, ensure free action in
+ combat, greatly increasing your dexterity and ensuring that
+ you will never become less agile. It is said that they can
+ also make you more confident and brave.
+
+ Full Plate Armour of Isildur [25,+25]
+ This armour of the Dunedain Lord, Isildur, Resists.
+
+ The Large Metal Shield of Anarion [5,+20]
+ This shield Resists and sustains your stats.
+
+ The Set of Cesti of Fingolfin (+10,+10) [5,+20]
+ These amazing gauntlets increase the wearers dexterity, and
+ slay creatures at (+10,+10). They will never be stopped by
+ paralyaztion and they also resist damage. They occasionally
+ grow magical spikes that can be fired causing great damage.
+
+ The Set of Leather Gloves 'Cambeleg' (+5,+5) [1,+15]
+ These Gloves of Might, increase strength and constitution.
+ They never allow their wearer to be paralyzed, and help
+ his/her slaying abilities.
+
+ The Set of Leather Gloves 'Cammithrim' [1,+10]
+ These Gloves of Light, sustain dexterity and give off light
+ so brightly that they can cast magic missiles almost
+ endlessly.
+
+ The Set of Gauntlets 'Paurhach' [2,+15]
+ These Fists of Fire resist fire and can cast fire bolts.
+
+ The Set of Gauntlets 'Paurnimmen' [2,+15]
+ These Fists of Frost resist cold and can cast frost bolts.
+
+ The Set of Gauntlets 'Pauraegen' [2,+15]
+ These Fists of Lightning resist lightning and can cast
+ lightning bolts.
+
+ The Set of Gauntlets 'Paurnen' [2,+15]
+ These Fists of Water resist acid and can cast acid bolts.
+
+ The Set of Gauntlets 'Camlost' (-11,-12) [2,+0] (-5)
+ The Empty Hand aggravates monsters, and greatly reduces
+ fighting ability. Named after the empty hand of Beren that
+ once clasped a Silmaril.
+
+ Mithril Chain Mail of Belegennon [28,+20]
+ This Chain Mail Resists and makes you stealthy.
+
+ The Iron Helm of Dor-Lomin [8,+20]
+ This is the Dragon Helm of Turin Turambar. It is rumored
+ that its wearer will never die in combat. It resists all
+ and sees all, and increases all fighting stats.
+
+ The Iron Helm of Holhenneth [5,+10]
+ This helm of brilliance and vision, greatly increases your
+ mental prowess. It allows you to see all that is hidden,
+ casting detection spells at frequent intervals.
+
+ The Iron Helm of Gorlim [5,+10] (-125)
+ This unhappy helm of betrayal ruins thought and sight.
+
+ Soft Leather Armour 'Hithlomir' [4,+20] (+4)
+ This dark-misty leather resists the elements and melds the
+ wearer into the background with incredible stealth.
+
+ Leather Scale Mail 'Thalkettoth' (+3) [11,+25]
+ This light leather scale mail is suprisingly good at
+ dodging attacks, and is resistant to acid. Often nicknamed
+ Blade-Turner.
+
+ Chain Mail of Arvedui [14,+15]
+ This wonderful chain mail belonged to the last king of
+ Arnor. It resists the elements and increases strength and
+ charisma.
+
+ The Hard Leather Cap of Thranduil [2,+10]
+ This acid resistant leather is a thinker's cap. Increasing
+ wisdom and intelligence.
+
+ The Metal Cap of Thengel [3,+12]
+ This cap of the Rohan King Thengel, gives kingly wisdom and
+ charisma.
+
+ The Steel Helm 'Hammerhand' [6,+20]
+ This warriors' helm increases the fighting stats.
+
+ The Large Leather Shield of Celegorm [4,+20]
+ A quality shield of Resistance.
+
+ The Pair of Metal Shod Boots of Thror [6,+20]
+ These Dwarf-king boots are ideal for combat, increasing
+ strength and constitution.
+
+ The Cloak 'Colluin' [1,+15]
+ This cloak of resistance even casts extra resistance spells
+ that can defend against poison.
+
+ The Cloak 'Holcolleth' [1,+4]
+ This mage cloak increases intelligence and wisdom and casts
+ spells to make monsters lose their concentration and fall
+ to sleep.
+
+ The Cloak 'Colannon' [1,+15]
+ This Gate-cloak, teleports the player at will, and gives
+ stealth so as to avoid awkward situations.
+
+ The Iron Crown of Beruthiel [0,+20] (-125)
+ This crown once belonged to the Cat-Queen of Gondor, who
+ disdained armed combat. Giving you cat like vision, and
+ sight within sight, it allows its wearer to be free of
+ combat, and infact finding armed combat beyond his or her
+ means.
+
+ The Iron Crown of Morgoth
+ This awesome artifact is a plain iron crown, mounted with
+ three jewels that capture the eternal light of the Trees
+ of the Valar, Teleperion and Laurelein. These Jewels were
+ made by the Noldorian High-Elf, Feanor who named them the
+ Silmarils. Their beauty, unsurpassed, drove Morgoth to
+ steal these jewels (with the aid of Ungoliant the Unlight).
+ The crown then, maximises all you stats, sustains all your
+ stats, is a permanent light source and allows the wearer to
+ see all.
+
+
diff --git a/lib/file/book-8.txt b/lib/file/book-8.txt
new file mode 100644
index 00000000..22a952fc
--- /dev/null
+++ b/lib/file/book-8.txt
@@ -0,0 +1,47 @@
+
+ Artifact Lore, Vol. III
+ Ancient Magical Tools
+ -----------------------
+
+
+ The Amulet of Ingwe (+3)
+ This amulet belonged to the high king of the Vanyar, the
+ most powerful of the High Elves. It gives resistance, and
+ greatly increases your wisdom and charisma. It gives you
+ good infravision, see invisible, and it casts a x5 strength
+ dispel evil.
+
+ The Amulet of Carlammas (+2)
+ This fiery amulet protects from flame, casts protection
+ from evil and increases your constitution.
+
+ The Phial of Galadriel (+4)
+ This wonderful object is an infinite light source, and once
+ identified it can light up rooms.
+
+ The Three Elven Rings
+ Made by Celebrimbor, Elf of the Girth-I-Mirdain. These
+ Rings of Power are of awesome power, and are very rare.
+
+ The Ring of Power 'Narya' (+1)
+ Celeborn gave this to Cirdan who gave it to Gandalf.
+ The least powerful of the Elven rings is of the element
+ fire. And as such makes you completely immune to fire. It
+ is also capable of casting very powerful fire balls. It
+ increases all your stats by one. It also protects from
+ from life draining and helps you regenerate.
+
+ The Ring of Power 'Nenya' (+2)
+ This was kept by Galadriel.
+ As Narya is to fire, Nenya is to Frost... Plus two to all
+ your stats.
+
+ The Ring of Power 'Vilya' (+3)
+ This was kept by Gil-galad who gave it to Elrond.
+ This gives immunity to lightning and resists poison. Plus three
+ to all your stats. Casts very powerful lightning balls.
+
+ Rumors exist of a Ring of Power known as the One Ring, which was
+ made to master the other Rings of Power. Its powers are what
+ legends are made of....
+
diff --git a/lib/file/book-9.txt b/lib/file/book-9.txt
new file mode 100644
index 00000000..2c37a3aa
--- /dev/null
+++ b/lib/file/book-9.txt
@@ -0,0 +1,240 @@
+The Filthy street urchin is an uncommon monster,
+occurring at surface depths.
+He is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Scrawny cat is a somewhat rare animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Scruffy little dog is a somewhat rare animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Farmer Maggot is a unique monster,
+occurring at surface depths.
+He is somewhat weak, somewhat dangerous,
+and has no magical powers.
+
+The Blubbering idiot is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Boil-covered wretch is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Village idiot is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Pitiful looking beggar is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Mangy looking leper is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Squint eyed rogue is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Singing, happy drunk is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Aimless looking merchant is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Mean looking mercenary is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Town Guardsman is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Grey mold is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Grey mushroom patch is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant yellow centipede is a common monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Giant white centipede is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The White icky thing is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Clear icky thing is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Giant white mouse is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Large brown snake is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Large white snake is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Small kobold is a common monster,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Kobold is a common monster,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The White worm mass is a common animal,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Floating eye is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Rock lizard is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Jackal is a common animal,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Soldier ant is a common monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has no magical powers.
+
+The Fruit bat is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Shrieker mushroom patch is a common monster,
+occurring at surface depths.
+It is laughably weak, not at all dangerous,
+and has an inkling of magical powers.
+
+The Blubbering icky thing is a common monster,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The Metallic green centipede is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Novice warrior is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Novice rogue is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Novice priest is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has small magical powers.
+
+The Novice mage is a common monster,
+occurring at surface depths.
+He is laughably weak, not one little bit dangerous,
+and has rudimentary magical powers.
+
+The Yellow mushroom patch is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The White jelly is a common monster,
+occurring at surface depths.
+It is laughably weak, somewhat dangerous,
+and has no magical powers.
+
+The Giant green frog is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Giant black ant is a common monster,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Salamander is a common animal,
+occurring at surface depths.
+It is laughably weak, just a little bit dangerous,
+and has no magical powers.
+
+The White harpy is a common animal,
+occurring at surface depths.
+She is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+The Blue yeek is a common animal,
+occurring at surface depths.
+It is laughably weak, not one little bit dangerous,
+and has no magical powers.
+
+Grip, Farmer Maggot's dog is a unique animal,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+Fang, Farmer Maggot's dog is a unique animal,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
+The Green worm mass is a common animal,
+occurring at surface depths.
+It is laughably weak, just a small bit dangerous,
+and has no magical powers.
+
diff --git a/lib/file/bravado.txt b/lib/file/bravado.txt
new file mode 100644
index 00000000..bf151229
--- /dev/null
+++ b/lib/file/bravado.txt
@@ -0,0 +1,106 @@
+104
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+cackles evilly.
+cackles diabolically.
+says: 'Surrender, miserable flea!'
+says: 'Come get some!'
+says: 'Let's rock!'
+laughs devilishly.
+says: 'Flee while you can, gnat!'
+says: 'You are about to die, maggot!'
+says: 'Read your prayers!'
+hisses: 'Die!'
+says: 'You don't have a chance, moron!'
+says: 'Fear my wrath, fool!'
+says: 'Feel my fury, dolt!'
+says: 'Groo is a genius, compared to you!'
+gives you a contemptuous glance.
+says: 'Prepare to meet your Maker, fool!'
+says: 'Perish, mortal!'
+says: 'Your puny efforts make me laugh!'
+says: 'Drop dead, wimp!'
+says: 'You should have fled while you had the chance.'
+screams: 'Die by my hand!'
+says: 'Your last wish, punk?'
+says: 'Your death shall be a slow, painful one.'
+says: 'Your head shall be my next trophy.'
+screams: 'You are DOOMED!'
+grins sadistically.
+says: 'This dungeon shall be your TOMB!'
+laughs fiendishly.
+says: 'Your fate is sealed, worm.'
+says: 'Resistance is useless.'
+says: 'Hell shall soon claim your remains.'
+says: 'Thou shalt repent of thy cunning.'
+says: 'Verily, thou shalt be one dead cretin.'
+says: 'Surrender or die!'
+says: 'Savor thy breath, it be thine last.'
+says: 'Prepare to die, miscreant!'
+says: 'You're history, dude!'
+says: 'Feeling lucky, punk?'
+says: 'You're toast!'
+says: 'You're dead meat.'
+says: 'Make my day.'
+says: 'I shall flatten you!'
+says: 'I could spare you, but why?'
+says: 'Take this, you sissy!'
+says: 'Nothing can save you now!'
+says: 'This dungeon ain't big enough for the both of us.'
+says: 'I'm gonna break your face!'
+says: 'I hope you enjoy pain!'
+says: 'Give me your best blow!'
+says: 'Draw, if you are a man!'
+says: 'A time to die, fool!'
+bellows frighteningly!
+says: 'You will never leave this dungeon alive!'
+says: 'You'll leave this dungeon only in a wooden box!'
+says: 'Your mother wears army boots!'
+says: 'Drop that weapon, NOW!'
+says: 'Life ain't for you, and I'm the cure!'
+says: 'Resistance is futile. You will be terminated.'
+says: 'Sight and smell of this, it gets me going.'
+says: 'Victim is your name and you shall fall.'
+says: 'Stepping out? You'll feel our hell on your back!'
+says: 'Now I will waste my hate on you.'
+says: 'Don't tread on me!'
+says: 'So be it! Threaten no more!'
+says: 'Kill for gain or shoot to maim, but I don't need a reason.'
+says: 'You'll die as you lived, in a flash of the blade.'
+says: 'You'd better stand cos there's no turning back.'
+says: 'I just want to see your blood, I just want to stand and stare.'
+says: 'I've been looking so long for you; you won't get away from my grasp.'
+says: 'I'm coming after you; you can kiss your arse good-bye.'
+says: 'It's official; you suck!'
+sings: 'I hate you, you hate me, we're a helluva family.'
+says: 'A mere mortal dares challenge *ME*?!'
+says: 'There is no escape and that's for sure.'
+says: 'This is the end; I won't take any more.'
+says: 'Say good-bye to the world you live in.'
+says: 'You've always been taking, but now you're giving.'
+says: 'My brain's on fire with the feeling to kill.'
+says: 'Don't try running away, because you're the one I'll find.'
+says: 'I was looking for you to start up a fight.'
+says: 'My innocent victims are slaughtered with wrath and despise!'
+says: 'I have found you, and there is no place to run.'
+says: 'My blood lust defies all my needs.'
+says: 'And damn'd be him that first cries: Hold, enough!'
+says: 'I can smell your blood, human!'
+says: 'Has your folly led to this?'
+wonders aloud how many experience points you're worth...
+says: 'Pride yourself on this, that you were slain by a champion.'
+thunders: 'May heaven have mercy on your soul, for I will have none.'
+screams for your blood!
+sighs: 'They send a poorer grade of adventurers down each year than the last.'
+says: 'Your life-blood will baptise my blade!'
+shouts: 'You will serve me in Valhalla!'
+snickers: 'Mommy's not here to save you now!'
+says: 'You're almost not worth killing... almost!'
+leaps towards you with death in its eye.
+sings: 'Cuts yer if ye stand, shoot yer if ye run.'
+says: 'Another adventurer? I just got through picking my teeth with the last.'
+says: 'Your two ears will decorate my belt.'
+says: 'I love all that blood.'
+says: 'I don't want to hurt you. I only want to kill you.'
+says: 'I like killing people, because it's so much fun.'
+screams: 'I'm out to destroy and I will cut you down!'
+says: 'Bring it on!'
diff --git a/lib/file/chainswd.txt b/lib/file/chainswd.txt
new file mode 100644
index 00000000..4755391c
--- /dev/null
+++ b/lib/file/chainswd.txt
@@ -0,0 +1,8 @@
+6
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+KILL, KILL, KILL!
+The Chainsword roars noisily!
+VROOM! VROOM!
+Kill, kill, kill, kill, kill, kill!
+Blood, blood, blood!
+Bloodbath!
diff --git a/lib/file/dam_huge.txt b/lib/file/dam_huge.txt
new file mode 100644
index 00000000..62f2ba60
--- /dev/null
+++ b/lib/file/dam_huge.txt
@@ -0,0 +1,9 @@
+7
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+You nearly decapitate %s!
+You impale %s on your weapon.
+Your weapon almost slices %s in half!
+%^s's head caves in!
+You broke %s's spine!
+Your weapon slices into %s's heart!
+You smashed %s's ribcage!
diff --git a/lib/file/dam_lots.txt b/lib/file/dam_lots.txt
new file mode 100644
index 00000000..1784d67c
--- /dev/null
+++ b/lib/file/dam_lots.txt
@@ -0,0 +1,21 @@
+18
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+You stab %s in the stomach.
+You cut off %s's hand.
+You chop down on %s's shins.
+You gave %s a deep gash.
+You gave %s a gigantic bruise.
+You cut off %s's arm!
+You cut off %s's leg!
+You stab %s in the heart.
+You slash at %s face.
+You throw %s down at the ground.
+You attempt to strangle %s.
+You grab %s's head and twist it.
+You knocked out several of %s's teeth!
+You broke some of %s's ribs!
+%^s spins around dizzily after your blow.
+%^s sputters at your tight, choking hold!
+%^s grunts under the force of your blows.
+%^s screams shrilly in fear.
+
diff --git a/lib/file/dam_med.txt b/lib/file/dam_med.txt
new file mode 100644
index 00000000..fc1db066
--- /dev/null
+++ b/lib/file/dam_med.txt
@@ -0,0 +1,25 @@
+23
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+You kick %s in the belly!
+You head-butt %s.
+You push %s over.
+You broke %s's finger!
+You broke %s's toe!
+You kick %s in the shins.
+You stab %s in the arm.
+You stab %s in the leg.
+You hit %s over the head.
+You knee %s in the groin!
+You aim a high kick at %s's head.
+You stab %s in the ribs.
+You chop at %s's neck.
+You put a tight choke-hold on %s.
+You attempt to topple %s over.
+You punch %s.
+You attempt to poke %s in the eye!
+You twist %s's leg.
+You twist %s's arm.
+You bend %s's fingers.
+You punch %s in the kidneys!
+You smash %s with your elbow.
+You smash %s with your knee.
diff --git a/lib/file/dam_none.txt b/lib/file/dam_none.txt
new file mode 100644
index 00000000..a6c20248
--- /dev/null
+++ b/lib/file/dam_none.txt
@@ -0,0 +1,24 @@
+22
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+You scratch %s.
+You give %s a nasty bruise.
+You stub %s's toe.
+You jab %s in the ribs.
+You almost poked your eye out while fighting %s!
+You made %s's nose bleed.
+You almost slipped while fighting %s.
+%^s doesn't even flinch!
+You accidentally hurt yourself while fighting %s.
+You slap %s.
+%^s slips and falls.
+%^s laughs at your wild swings.
+You pull at %s's hair.
+You punch %s in the nose.
+You pull at %s's ear.
+%^s growls at you.
+%^s is very, very annoyed!
+You scream an insult at %s.
+%^s makes a nasty face.
+You scowl at %s.
+%^s gnashes his teeth.
+%^s lets out a gurgling laugh.
diff --git a/lib/file/dam_xxx.txt b/lib/file/dam_xxx.txt
new file mode 100644
index 00000000..afceffe1
--- /dev/null
+++ b/lib/file/dam_xxx.txt
@@ -0,0 +1,11 @@
+9
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+%^s disintegrates into a fine mist!
+%^s splatters all over the floor.
+%^s explodes into tiny chunks.
+%^s's bones are crushed at the force of your blow!
+You almost squash %s into a pancake!
+Your weapons neatly slice %s into many little pieces.
+You cleave %s in half.
+%^s's head flies off in a wide trajectory.
+%^s is driven several feet into the ground under your blow!
diff --git a/lib/file/dead.txt b/lib/file/dead.txt
new file mode 100644
index 00000000..f32dae4d
--- /dev/null
+++ b/lib/file/dead.txt
@@ -0,0 +1,24 @@
+ #s_____#w #D,#w-#Wv#s___#w
+ #s_d&#y*#D'"#w #W/#w #D`#w|
+ #D,#W/#s?#D'#w | |#D'#w
+ #s?#D'#w|#D'#w | #D.,#W~#w-#D'""'#w |#D.#w
+ #D,#w| #sT#w --#y+#w-- #D,#W~#D'#w #D.,#W~#s:#w| #D`#W~\#w
+ | #sL#w | #D,#Wv#s_#D.#w #W/#D'#w #D,#W/#D"#w | #sT#D`#W\#D.`#W\#D.#w
+ |#D.#w |#W\#w | #D""'#w #s?#w--#Wv#D,#s?#w |#D'#w #s?#D,#w #W\#D.#w
+ #W\#w #D`#W\#D.#w #sT#w #D`#W~#w-#W\#s_]#w |
+ #D`#W\#D.#w #D"#W^~v#D.#w | #D"#W^#w-#W~v#w
+ #D`#W\#s_#w #sT#D.#w #D"#W^~v#w #D.#s__#D.#w #W/#w
+ #D``#w-#W~#w--| | |#D,#w #D"#s:#w| #D.#s___#D.#w #D.#w|
+ #D`#W\#w #s?#W\#w #W/#w #W/#w #D"#s:&#w #D,#w-#Wv#s_#W/#w
+ #D`#W\#D.#w #D`#W\#w| |#D'#w #D.,#W~#D'#w #D.#W/#D'#w
+ #D`#W~\#D.#w #D`#W^#D'"#w #D,#W~#D'#w
+ #D.#w| #s__#Wv#w-#W~#D''#w
+ #W/#w #W/#D'#w
+ #D,#w| #D.#w|
+ #W/#w #sJ#w
+ |#D'#w |
+---------------------------------------------#W/#w #W/#s:#w-------------------------
+ |#D'#w #D,#w|
+ |#D'#w #D'#w
+ #D`#w
+
diff --git a/lib/file/death.txt b/lib/file/death.txt
new file mode 100644
index 00000000..73ac47cd
--- /dev/null
+++ b/lib/file/death.txt
@@ -0,0 +1,351 @@
+349
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+Live and let live, right..?
+AAAAAAAAARRRRRRRRRRRRRRGGGGGGGGGGGGGHHHHHHHHHHHHHHH!!!!!!!!!!!!
+AAAARRRGGGHHH!!!
+Somehow, I have a bad feeling about this...
+Strangely, all of a sudden I don't feel so good.
+You can see armored women on winged horses coming for you.
+Oh well, you can't always win.
+I'm too young to die!
+I'll be back!
+O, untimely death!
+Slave, thou hast slain me!
+Ouch! That smarts!
+Who knocked?
+Did anybody get the number of that truck..?
+Ouch.
+Et tu, Brute! Then fall, Caesar!
+O! I die, Horatio...
+I told you to be careful with that sword...
+This guy's a little crazy...
+Ok, ok, I get it: No more pals.
+No more mr. nice guy!
+Who turned off the light..?
+Join the army, see the world, they said...
+Mom told me there'd be days like this...
+Rats!
+Shall this fellow live?
+Help, ho!
+What ho! Help!
+What hast thou done?
+I'll be revenged on the whole pack of you!
+You will *pay* for this!
+They say blood will have blood...
+Violence is no solution!
+Yes?
+#&%#&#%*#*&%!!!!!
+F***!
+No time to make a testament?
+Ugh!
+Aargh!
+Aaagghhh!
+I'm melting!
+Oof..
+Oh!
+Did somebody knock?
+Later, dude...
+CU!
+What? Who? Me? Oh, s..t!
+...amen!
+Eeek!
+Aacch!
+I hate it when that happens.
+One direct hit can ruin your whole day.
+Oh no!
+Not me!
+Ouch.
+Oh no, not again.
+Another one bites the dust.
+Goodbye.
+Help me!
+Farewell, cruel world.
+Oh man!
+Doough!
+This is the End, my only friend.
+It's all over.
+The fat lady sang.
+Why does everything happen to me?
+I'm going down.
+Crapola.
+Pow!
+Bif!
+Bam!
+Zonk!
+I should've listened to my mother...
+No... a Bud light!
+What was that noise?
+Mama said there'd be days like this.
+It's just one of those days...
+I see a bright light...
+Mommy? Is that you?
+I let you hit me!
+Sucker shot!
+I didn't want to live anyway.
+-<sob>-
+Hah haa! Missed me! Ha---
+Was that as close as I think it was?
+Monsters rejoice: the hero has been defeated.
+It wasn't just a job it was an adventure!
+I didn't like violence anyway!
+I thought you liked me?
+Such senseless violence! I don't understand it.
+I think this guy's a little crazy.
+Somehow I don't feel like killing anymore.
+Help me! I am undone!
+Hey! Killin' ain't cool.
+This fell sergeant, Death, is strict in his arrest...
+The rest is silence.
+Guh!
+It's game over, man!
+You've run out of life.
+Thou art slain.
+Finish him!
+Trust me, I know what I'm doing...
+Die, mortal!
+Kill men i' the dark! What be these bloody thieves?
+Ho! Murder! Murder!
+O! I am spoil'd, undone by villains!
+O murderous slave! O villain!
+O, falsely, falsely murder'd!
+A guiltless death I die.
+AAAAAAAAAAAAAAAAAAAAAAAAHHHHHHH!
+Trust me.
+Dammit, this thing won't die!
+He hit me for HOW MUCH?????
+Look, behind you!!!
+Who fed steroids to that kobold?
+Don't worry, be happy!
+I don't believe this!
+Oops.
+Oups.
+Can't you take a joke?
+Well, I didn't much like this character, anyway...
+Oops, sorry... didn't mean to disturb you.
+I never get to have any fun!
+Stop!
+Cut it out!
+Don't worry. I've got a plan.
+It didn't look so tough.
+Run away!
+All clear, guys.
+AGAIN!?!?!
+I don't like this dungeon...
+Maybe this wasn't such a good idea.
+My God will protect me.
+You wouldn't dare!
+But what about my Parry Skill? Tumbling?
+Don't worry - I have Pilot-7.
+And I've *never* done you any harm.
+I don't understand. It should be dead by now.
+I'm heir to the crown. They wouldn't dare!
+Hey! Where's my stomach? My hands?
+Ha! That's the oldest trick in the book.
+Cover me.
+Watch this.
+And damn'd be him that first cries, 'Hold, enough!'
+I will not yield.
+...but like a man he died.
+If you cut me down, I will only become more powerful.
+Well, at least I tried...?
+What could possibly have gone wrong?
+You die...
+What's with that weirdo with the teeth?
+Surrender? Never!
+I'm sure reinforcements will get here on time. They promised.
+Funny, didn't *look* like a cyberpsycho....
+I have a very bad feeling about this.
+Do something, SCHMUCK!
+I feel I could cast 'Speak with Dead' and talk to myself.
+Oh, that's just a light wound.
+Ach, is doch nur 'ne Fleischwunde...
+I thought you were on MY side...
+Next time, try talking!
+Oh shit... I'll try to teleport again.
+Somebody get me a Rod of Resurrection... QUICK!
+Uhh... oh-oh...
+Gee, where'd everybody go?
+I see it coming...aaargllhhhh! {sough}
+What do you mean 'aaargllhhhh'? Hey man, I've paid for this.
+Ay! Ay! Ay!
+Ohe! Ohe! Ohe!
+Et tu, Caesar! Then fall, Brute!
+Even the best laid plans...
+Hey, not too rough!
+The Random Number Generator hates me!
+So when I die, the first thing I will see in heaven is a score list?
+Can't we talk this thing over?
+Wait! Spare me and I'll make you rich! Money is not a problem!
+I hate you!
+By the kind gods, 'twas most ignobly done!
+Mein Leben!
+Meine Lieder!
+I'm the hero of this story! I CAN'T die!
+I thought heroes were supposed to win!
+Gee... thanks.
+You've fallen and can't get up!
+911?
+Sure don't look good...
+Oh No! Here I blow again!
+Hey - I've got lawyers.
+Thanks, I needed that.
+I AM toast!!
+Scheisse!
+Fatality!
+Brutality!
+Toasty!
+And you thought Tristan was unlucky...
+Just wait till I get my hands on the crook who sold me this crappy armor...
+All is lost. Monks, monks, monks!
+All my possessions for a moment of time!
+Don't let poor Nelly starve!
+Wally, what is this? It is death, my boy: they have deceived me.
+Everyone dances with the Grim Reaper.
+Adios.
+I'm going home, babe.
+I am innocent, innocent, innocent!
+Watch where you're pointing with that sword! You nearly...
+Hmm, some things are better wanted than had...
+And they told me it was not loaded.
+Of course I know what I am doing.
+It looked harmless.
+Hilfe, hilfe, hilfe!
+Look, dad! No head!
+Look! I'm flying!
+Think I'm gonna fall for that?
+I'll be back... as soon as I can.
+3... 2... 1... Liftoff!
+My wallet? In your dreams!
+Yes! Yes! YES! YES! YY... AAARRRGGGHH!
+See you later, alligator!
+Up, up and awaaaayyy!
+Been nice knowing you.
+But I just got a little prick!
+And I just wanted that fancy suit of armour you were carrying...
+Hey guys, where are you?
+Hey look... ARCHERS!
+I can't probably miss...
+I don't care. I have a Scroll of Raise Dead.
+I don't care. I have a Ring of Regeneration.
+I have this dungeon at home, I know where everything is!
+This HAS to be an illusion. I attempt to disbelieve it.
+I thought you could be trusted.
+Never try to sneak in a plate mail.
+I'll never surrender.
+I'll use the Cheat Death option...
+I'm invincible!
+I'm death incarnate! Nothing can harm me!
+Hey, it was only a joke, all right?
+Hey, don't talk to me like that!
+I have rights, too!
+Just because you're big and ugly doesn't mean you can push ME around.
+Me first! Me first!
+Let me handle this.
+No problem. That's easy.
+Oh, shit.
+So what?
+Tell me this is an illusion... please!
+I hate the RNG...
+They need a twenty to hit me! I'm invincible!
+Trust me.
+CHARGE!
+What do you mean, how many hit points do I have?
+What do you mean, my GOI expired?
+Yeah, I knew it was dangerous, but I was thinking about the experience points.
+You mean you get to use the critical hit chart too?
+You'd have to be a GOD to smile after that hit!
+I'm not afraid of death. I just don't want to be there when it happens.
+I have such sweet thoughts.
+I pray you all pray for me.
+I shall hear in heaven.
+Is not this dying with courage and true greatness?
+I must sleep now.
+Nurse, nurse, what murder! What blood! I have done wrong!
+It is finished.
+That unworthy hand! That unworthy hand!
+I am dying.
+Oh, dear.
+I will not kneel. Strike!
+I have led a happy life.
+Dying, dying.
+I feel the flowers growing over me.
+Now it is come.
+Let me die to the sound of sweet music.
+I will now enter the Halls of Mandos.
+Ungrateful traitors!
+We perish, we disappear, but the march of time goes on forever.
+Youth, I forgive thee.
+Treason! Treason!
+Coward! Why did you not protect me?
+I am absolutely undone.
+It is well. I die hard, but am not afraid to go.
+Do let me die in peace.
+Nothing is real but pain now.
+Violent use brings violent plans.
+Soldier boy, made of clay, now an empty shell.
+Bodies fill the fields I see, the slaughter never ends.
+Life planned out before my birth, nothing could I say.
+Blood will follow blood, dying time is here.
+Never happy endings on these dark sets.
+No one to play soldier now, no one to pretend.
+Time for lust, time for lie, time to kiss your life goodbye.
+Greetings, Death, he's yours to take away.
+I was born for dying.
+The higher you walk, the farther you fall.
+Where's your crown, King Nothing?
+Exit: light - enter: night!
+New blood joins this earth...
+You labeled me, I'll label you, so I dub thee unforgiven.
+If you're gonna die, die with your boots on!
+There's a time to live, and a time to die, when it's time to meet the maker.
+Isn't it strange, as soon as you're born you're dying?
+Only the good die young, all the evil seem to live forever.
+I don't wanna die, I'm a god, why can't I live on?
+And in my last hour, I'm a slave to the power of death.
+Now I am cold, but a ghost lives in my veins.
+You got to watch them - be quick or be dead.
+Heaven can wait 'till another day.
+You'll take my life but I'll take yours too.
+We won't live to fight another day.
+As I lay forgotten and alone, without fear I draw my parting groan.
+Somebody please tell me that I'm dreaming!
+Can't it be there's been some sort of error?
+Is it really the end not some crazy dream?
+Life down there is just a strange illusion.
+Your body tries to leave your soul.
+I'm so tired of living, I might as well end today.
+Life, life! Death, death! How curious it is!
+Catch my soul 'cos it's willing to fly away!
+Flames? Not yet, I think.
+Someone call the Gendarmes!
+I split my brain, melt through the floor.
+And now the dreams end.
+Off to Never-Never Land!
+Death greets me warm, now I will just say goodbye.
+What is this? I've been stricken by fate!
+This can't be happening to me!
+Flash before my eyes: now it's time to die.
+You have been dying since the day you were born.
+No point asking who's to blame.
+But for all his power he couldn't foresee his own demise.
+My creator will lay my soul to rest.
+Was that worth dying for?
+Can you say you are proud of what you've done?
+But there are some things which cannot be excused.
+Why is it some of us are here just so that we'll die?
+The shortest straw, pulled for you.
+There's got to be just more to it that this or tell me why do we exist?
+I can't believe that really my time has come.
+Too much of a good thing, I guess...
+I really screwed up this time.
+Wow, what a trip!
+What is Time, friend or foe
+Time waits for none
+Running through your fingers like sand
+Taking us along to future unknown
+And all too sudden, like it or not
+We become part of the Land.
+Hah! I'm not dead yet. I still have five hit points.
+I don't understand. It should be dead by now.
+I rolled a 20. How could that be a miss?
+What the frell?!
diff --git a/lib/file/elvish.txt b/lib/file/elvish.txt
new file mode 100644
index 00000000..a00b5a22
--- /dev/null
+++ b/lib/file/elvish.txt
@@ -0,0 +1,218 @@
+216
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+adan
+ael
+in
+agl
+ar
+aina
+alda
+al
+qua
+am
+arth
+amon
+anca
+an
+dune
+anga
+anna
+ann
+on
+ar
+ien
+atar
+band
+bar
+ad
+bel
+eg
+brag
+ol
+breth
+il
+brith
+cal
+en
+gal
+en
+cam
+car
+ak
+cel
+eb
+cor
+on
+cu
+cui
+vie
+cul
+curu
+dae
+dag
+or
+del
+din
+dol
+dor
+draug
+du
+duin
+dur
+ear
+ech
+or
+edh
+el
+eith
+elen
+er
+ereg
+es
+gal
+fal
+as
+far
+oth
+faug
+fea
+fin
+for
+men
+fuin
+gaer
+gaur
+gil
+gir
+ith
+glin
+gol
+odh
+gond
+gor
+groth
+grod
+gul
+gurth
+gwaith
+gwath
+wath
+had
+hod
+haudh
+heru
+him
+hini
+hith
+hoth
+hyar
+men
+ia
+iant
+iath
+iaur
+ilm
+iluve
+kal
+gal
+kano
+kel
+kemen
+khel
+ek
+khil
+kir
+lad
+laure
+lhach
+lin
+lith
+lok
+lom
+lome
+londe
+los
+loth
+luin
+maeg
+mal
+man
+mel
+men
+menel
+mer
+eth
+min
+as
+mir
+mith
+mor
+moth
+nan
+nar
+naug
+dil
+dur
+nel
+dor
+nen
+nim
+orn
+orod
+os
+pal
+an
+pel
+quen
+quet
+ram
+ran
+rant
+ras
+rauko
+ril
+rim
+ring
+ris
+roch
+rom
+rond
+ros
+ruin
+ruth
+sarn
+ser
+eg
+sil
+sir
+sul
+tal
+dal
+tal
+ath
+tar
+tath
+ar
+taur
+tel
+thal
+thang
+thar
+thaur
+thin
+thol
+thon
+thor
+on
+til
+tin
+tir
+tol
+tum
+tur
+uial
+ur
+val
+wen
+wing
+yave
diff --git a/lib/file/error.txt b/lib/file/error.txt
new file mode 100644
index 00000000..201ab8e9
--- /dev/null
+++ b/lib/file/error.txt
@@ -0,0 +1,67 @@
+65
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+What game do you think you are playing anyway?
+Aivan sairas kaveri kun tuollaista aikoo puuhata!
+Insufficient data for further analysis.
+Non sequitur. Your facts are uncoordinated.
+Type '?' or '\' for help.
+Invalid command.
+What?
+WHAT?!
+You must be out of your mind!
+You're killing me.
+Are you sure?
+Are you sure you know what you are doing?
+Aww, come on!
+That makes no sense.
+I beg your pardon.
+Degreelessness mode on.
+Degreelessness mode off.
+Syntax error.
+That doesn't compute.
+I don't understand you.
+???
+Sure. Piece of cake.
+Error.
+You can't do that!
+Help!
+Come again?
+Sorry?
+Sorry, I'm not sure I understand you.
+What's your point?
+Unknown command.
+Command not found.
+An unexpected error has occurred because an error of type 42 occurred.
+Somehow, you think that would never work.
+Welcome to level 42.
+Don't be ridiculous!
+lfae aierty agnxzcg?
+Soyha, azho bouate!
+I don't fully understand you.
+Why would anybody want to do THAT?
+Yes, yes, now tell me about your childhood.
+Satisfied?
+Something is wrong here.
+There's something wrong with YOU.
+You leap up 9' and perform a miraculous 5xSpiral in the air.
+Aw, shaddap!
+Shut up, smartass!
+I see little point in doing that.
+Oh, really?
+Very funny.
+You've got to be kidding!
+I'm not amused.
+I must have misheard you.
+Nothing happens.
+Where did you learn THAT command?
+When all else fails, read the instructions.
+Why not read the instructions first?
+Cut it out!
+Nothing interesting happens.
+Just how exactly am I supposed to do THAT?
+That's morally wrong and I won't do it.
+I'm not gonna take this abuse.
+AAAAAAAAAAAAAHHHHHHHHHHHHHHHRRRRRRRRRRRGGGGGGGGGGG!
+No more, if you value your character's life!
+Give it up, guy.
+Disk error. (a)bort, (r)etry, (f)ail?
diff --git a/lib/file/mondeath.txt b/lib/file/mondeath.txt
new file mode 100644
index 00000000..e78ca4e9
--- /dev/null
+++ b/lib/file/mondeath.txt
@@ -0,0 +1,334 @@
+332
+****** BUFFER LINE *********************************** DO NOT REMOVE *******
+'Live and let live, right..?'
+'AAAAAAAAARRRRRRRRRRRRRRRRGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHH!!!!!!!!!!!'
+'AAAARRRGGGHHH!!!'
+'Somehow, I have a bad feeling about this...'
+'Strangely, all of a sudden I don't feel so good.'
+'I can see armored women on winged horses coming for me.'
+'Oh well, you can't always win.'
+'I'm too young to die!'
+'I'll be back!'
+'O, untimely death!'
+'Slave, thou hast slain me!'
+'Ouch! That smarts!'
+'Who knocked?'
+'Did anybody get the number of that truck..?'
+'Ouch.'
+'Et tu, Brute! Then fall, Caesar!'
+'O! I die, Horatio...'
+'I told you to be careful with that sword...'
+'This guy's a little crazy...'
+'Ok, ok, I get it: No more pals.'
+'No more Mr. Nice Guy!'
+'Who turned off the light..?'
+'Join the army, see the world, they said...'
+'Mom told me there'd be days like this...'
+'Rats!'
+'Shall this fellow live?'
+'Help, ho!'
+'What ho! Help!'
+'What hast thou done?'
+'I'll be revenged on the whole pack of you!'
+'You will *pay* for this!'
+'They say blood will have blood...'
+'Violence is no solution!'
+'Yes?'
+'#&%#&#%*#*&%!!!!!'
+'F***!'
+'No time to make a testament?'
+'Ugh!'
+'Aargh!'
+'Aaagghhh!'
+'I'm melting!'
+'Oof..'
+'Oh!'
+'Did somebody knock?'
+'Later, dude...'
+'CU!'
+'What? Who? Me? Oh, s..t!'
+'...amen!'
+'Eeek!'
+'Aacch!'
+'I hate it when that happens.'
+'One direct hit can ruin your whole day.'
+'Oh no!'
+'Not me!'
+'Ouch.'
+'Oh no, not again.'
+'Another one bites the dust.'
+'Goodbye.'
+'Help me!'
+'Farewell, cruel world.'
+'Oh man!'
+'Doough!'
+'This is the End, my only friend.'
+'It's all over.'
+'The fat lady sang.'
+'Why does everything happen to me?'
+'I'm going down.'
+'Crapola.'
+'Pow!'
+'Bif!'
+'Bam!'
+'Zonk!'
+'I should've listened to my mother...'
+'No... a Bud light!'
+'What was that noise?'
+'Mama said there'd be days like this.'
+'It's just one of those days...'
+'I see a bright light...'
+'Mommy? Is that you?'
+'I let you hit me!'
+'Sucker shot!'
+'I didn't want to live anyway.'
+'-<sob>-'
+'Hah haa! Missed me! Ha---'
+'Was that as close as I think it was?'
+'The monsters rejoice: the hero has been defeated.'
+'It wasn't just a job it was an adventure!'
+'I didn't like violence anyway!'
+'I thought you liked me?'
+'Such senseless violence! I don't understand it.'
+'I think this guy's a little crazy.'
+'Somehow I don't feel like killing anymore.'
+'Help me! I am undone!'
+'Hey! Killin' ain't cool.'
+'This fell sergeant, Death, is strict in his arrest...'
+'The rest is silence.'
+'Guh!'
+'It's game over, man!'
+'Trust me, I know what I'm doing...'
+'Kill men i' the dark! What be these bloody thieves?'
+'Ho! Murder! Murder!'
+'O! I am spoil'd, undone by villains!'
+'O murderous slave! O villain!'
+'O, falsely, falsely murder'd!'
+'A guiltless death I die.'
+'AAAAAAAAAAAAAAAAAAAAAAAAHHHHHHH!'
+'Trust me.'
+'Dammit, this thing won't die!'
+'He hit me for HOW MUCH?????'
+'Look, behind you!!!'
+'Who fed steroids to that kobold?'
+'Don't worry, be happy!'
+'I don't believe this!'
+'Oops.'
+'Can't you take a joke?'
+'Well, I didn't much like this character, anyway...'
+'Oops, sorry... didn't mean to disturb you.'
+'I never get to have any fun!'
+'Stop!'
+'Cut it out!'
+'Don't worry. I've got a plan.'
+'It didn't look so tough.'
+'Run away!'
+'All clear, guys.'
+'AGAIN!?!?!'
+'I don't like this dungeon...'
+'Maybe this wasn't such a good idea.'
+'My God will protect me.'
+'You wouldn't dare!'
+'But what about my Parry Skill? Tumbling?'
+'Don't worry - I have Pilot-7.'
+'And I've *never* done you any harm.'
+'I don't understand. It should be dead by now.'
+'I'm heir to the crown. They wouldn't dare!'
+'Hey! Where's my stomach? My hands?'
+'Ha! That's the oldest trick in the book.'
+'Cover me.'
+'Watch this.'
+'And damn'd be him that first cries: Hold, enough!'
+'I will not yield.'
+'...but like a man he died.'
+'If you cut me down, I will only become more powerful.'
+'Well, at least I tried...?'
+'What could possibly have gone wrong?'
+'What's with that weirdo with the teeth?'
+'Surrender? Never!'
+'I'm sure reinforcements will get here on time. They promised.'
+'Funny, didn't *look* like a cyberpsycho....'
+'I have a very bad feeling about this.'
+'Do something, SCHMUCK!'
+'I feel I could cast Speak with Dead and talk to myself.'
+'Oh, that's just a light wound.'
+'Ach, is doch nur 'ne Fleischwunde...'
+'I thought you were on MY side...'
+'Next time, try talking!'
+'Oh shit... I'll try to teleport again.'
+'Somebody get me a Rod of Resurrection... QUICK!'
+'Uhh... oh-oh...'
+'Gee, where'd everybody go?'
+'I see it coming...aaargllhhhh! {sough}'
+'Ay! Ay! Ay!'
+'Ohe! Ohe! Ohe!'
+'Et tu, Caesar! Then fall, Brute!'
+'Even the best laid plans...'
+'Hey, not too rough!'
+'The Random Number Generator hates me!'
+'So when I die, the first thing I will see in heaven is a score list?'
+'Can't we talk this thing over?'
+'Wait! Spare me and I'll make you rich! Money is not a problem!'
+'I hate you!'
+'By the kind gods, 'twas most ignobly done!'
+'Mein Leben!'
+'Meine Lieder!'
+'I'm the hero of this story! I CAN'T die!'
+'Gee... thanks.'
+'I've fallen and I can't get up!'
+'911?'
+'Sure don't look good...'
+'Oh No! Here I blow again!'
+'I'll be back...'
+'Hey - I've got lawyers.'
+'Thanks, I needed that.'
+'I AM toast!!'
+'Scheisse!'
+'Oh, basely done! I had hoped for better of thee!'
+'I am death incarnate! NOTHING can harm me!'
+'And you thought Tristan was unlucky...'
+'Just wait till I get my hands on the crook who sold me this crappy armor...'
+'All is lost. Monks, monks, monks!'
+'All my possessions for a moment of time!'
+'Don't let poor Nelly starve!'
+'Wally, what is this? It is death, my boy: they have deceived me.'
+'Everyone dances with the Grim Reaper.'
+'Adios.'
+'I'm going home, babe.'
+'I am innocent, innocent, innocent!'
+'Watch where you're pointing with that sword! You nearly...'
+'Hmm, some things are better wanted than had...'
+'And they told me it was not loaded.'
+'Of course I know what I am doing.'
+'It looked harmless.'
+'Hilfe, hilfe, hilfe!'
+'Look, dad! No head!'
+'Look! I'm flying!'
+'Think I'm gonna fall for that?'
+'I'll be back... as soon as I can.'
+'3... 2... 1... Liftoff!'
+'My wallet? In your dreams!'
+'Yes! Yes! YES! YES! YY... AAARRRGGGHH!'
+'See you later, alligator!'
+'Up, up and awaaaayyy!'
+'Been nice knowing you.'
+'But I just got a little prick!'
+'And I just wanted that fancy suit of armour you were carrying...'
+'Hey guys, where are you?'
+'Hey look... ARCHERS!'
+'I can't probably miss...'
+'I don't care. I have a Scroll of Raise Dead.'
+'I don't care. I have a Ring of Regeneration.'
+'I have this dungeon at home, I know where everything is!'
+'This HAS to be an illusion. I attempt to disbelieve it.'
+'I thought you could be trusted.'
+'Never try to sneak in a plate mail.'
+'I'll never surrender.'
+'I'll use the Cheat Death option...'
+'I'm invincible!'
+'Hey, it was only a joke, all right?'
+'Hey, don't talk to me like that!'
+'I have rights, too!'
+'Just because you're big and ugly doesn't mean you can push ME around.'
+'Me first! Me first!'
+'Let me handle this.'
+'No problem. That's easy.'
+'Oh, shit.'
+'So what?'
+'Tell me this is an illusion... please!'
+'I hate the RNG...'
+'They need a twenty to hit me! I'm invincible!'
+'Trust me.'
+'CHARGE!'
+'What do you mean, how many hit points do I have?'
+'What do you mean, my GOI expired?'
+'Yeah, I knew it was dangerous, but I was thinking about the XP...'
+'You mean you get to use the critical hit chart too?'
+'You'd have to be a GOD to smile after that hit!'
+'Morons! I have morons on my payroll!'
+'Idiots! I am surrounded by incompetent idiots!'
+'I have such sweet thoughts'
+'I pray you all pray for me.'
+'Is not this dying with courage and true greatness?'
+'I must sleep now.'
+'Nurse, nurse, what murder! What blood! I have done wrong!'
+'That unworthy hand! That unworthy hand!'
+'I am dying.'
+'Oh, dear.'
+'I will not kneel. Strike!'
+'I have led a happy life.'
+'Dying, dying.'
+'I feel the flowers growing over me.'
+'Now it is come.'
+'Let me die to the sound of sweet music.'
+'I will now enter the Halls of Mandos.'
+'Ungrateful traitors!'
+'We perish, we disappear, but the march of time goes on forever.'
+'Youth, I forgive thee.'
+'Treason! Treason!'
+'Cowards! Why did you not protect me?'
+'I am absolutely undone.'
+'It is well. I die hard, but am not afraid to go.'
+'Do let me die in peace.'
+'Nothing is real but pain now.'
+'Violent use brings violent plans.'
+'Soldier boy, made of clay, now an empty shell.'
+'Bodies fill the fields I see, the slaughter never ends.'
+'Life planned out before my birth, nothing could I say.'
+'Blood will follow blood, dying time is here.'
+'Never happy endings on these dark sets.'
+'No one to play soldier now, no one to pretend.'
+'Time for lust, time for lie, time to kiss your life goodbye.'
+'Greetings, Death, I'm yours to take away.'
+'I was born for dying.'
+'The higher you walk, the farther you fall.'
+'Exit: light - enter: night!'
+'New blood joins this earth...'
+'You labeled me, I'll label you, so I dub thee unforgiven.'
+'If you're gonna die, die with your boots on!'
+'There's a time to live, and a time to die, when it's time to meet the maker.'
+'Isn't it strange, as soon as you're born you're dying?'
+'Only the good die young, all the evil seem to live forever.'
+'I don't wanna die, I'm a god, why can't I live on?'
+'And in my last hour, I'm a slave to the power of death.'
+'Now I am cold, but a ghost lives in my veins.'
+'You got to watch them - be quick or be dead.'
+'Heaven can wait till another day.'
+'You'll take my life but I'll take yours too.'
+'We won't live to fight another day.'
+'As I lay forgotten and alone, without fear I draw my parting groan.'
+'Somebody please tell me that I'm dreaming!'
+'Can't it be there's been some sort of error?'
+'Is it really the end not some crazy dream?'
+'Life down there is just a strange illusion.'
+'My life suffocates, planting seeds of hate.'
+'I split my brain, melt through the floor.'
+'My body tries to leave my soul.'
+'I'm so tired of living, I might as well end today.'
+'Life, life! Death, death! How curious it is!'
+'Catch my soul 'cos it's willing to fly away!'
+'Flames? Not yet, I think.'
+'Someone call the Gendarmes!'
+'And now the dreams end.'
+'I'm off to Never-Never Land!'
+'Death greets me warm, now I will just say goodbye.'
+'What is this? I've been stricken by fate!'
+'This can't be happening to me!'
+'Flash before my eyes: now it's time to die.'
+'You have been dying since the day you were born.'
+'No point asking who's to blame.'
+'But for all my power I couldn't foresee my own demise.'
+'My creator will lay my soul to rest.'
+'Was that worth dying for?'
+'Can you say you are proud of what you've done?'
+'But there are some things which cannot be excused.'
+'Why is it some of us are here just so that we'll die?'
+'The shortest straw, pulled for me.'
+'There's got to be just more to it that this or tell me why do we exist?'
+'I can't believe that really my time has come.'
+'Too much of a good thing, I guess...'
+'When the dream dies, the nightmare begins.'
+'You maggots make me sick. I'll be avenged. Lucifer dwells within all of us!'
+'I really screwed up this time.'
+'Wow, what a trip!'
+'I believe my kingdom will come'
diff --git a/lib/file/monfear.txt b/lib/file/monfear.txt
new file mode 100644
index 00000000..5bd2e91c
--- /dev/null
+++ b/lib/file/monfear.txt
@@ -0,0 +1,63 @@
+61
+*********** BUFFER LINE ********************** DO NOT REMOVE *****************
+says: 'I am too young to die.'
+says: 'Ok, ok! I get: no more pals.'
+screams: 'Help, ho!'
+screams: 'What ho! Help!'
+says: 'You will pay for this!'
+says: 'Violence is no solution!'
+says: 'I thought you liked me.'
+says: 'Such senseless violence! I don't understand it!'
+screams: 'Ho! Murder! Murder!'
+says: 'Look, behind you!'
+screams: 'Run away!'
+screams: 'Run to the hills! Run for your lives!'
+says: 'Wait! Spare me and I'll make you rich! Money isn't a problem!'
+says: 'I'll be back...'
+says: 'Hey -- I've got lawyers!'
+says: 'All my possession for a moment of time!'
+says: 'Hey, it was only a joke, all right?'
+says: 'Stop!'
+says: 'Cut it out, will you?'
+says: 'I will not kneel. Strike!'
+screams: 'Cowards! Why did you not protect me?'
+screams: 'Idiots! I am surrounded by incompetent idiots!'
+says: 'I don't wanna die, I'm a god, why can't I live on?'
+yells: 'Someone call the Gendarmes!'
+screams: 'Keep that lunatic away from me!'
+shouts: 'Drop that weapon, now!'
+says: 'Fool! You don't know what you're doing!'
+screams for help!
+begs for mercy.
+sobs.
+screams: 'Help! The maniac's murdering me!'
+says: 'Just what is it you want? Money? Babes? I can arrange it...'
+says: 'Wait! Let's make a deal!'
+says: 'Just can't stop this surmounting terror!'
+says: 'If there is a God, then why has he let me die?'
+says: 'I know where I'm going -- out!'
+says: 'No hope, no life, just pain and fear.'
+says: 'I am a fugitive, hunted down like game.'
+says: 'You'll live to regret this blasphemous offence!'
+says: 'All my life's blood is slowly draining away...'
+asks: 'Should we be fighting at all?'
+asks: 'What are we fighting for?'
+asks you: 'Can you say you are proud of what you've done?'
+says: 'Every minute I get weaker...'
+says: 'All my life I've run away...'
+says: 'All that I see, absolute horror!'
+says: 'I have fallen prey to failure.'
+says: 'Just leave me alone!'
+says: 'Please, save me!'
+says: 'You've won a battle, but I'll win the war!'
+says: 'You've won this round, next time it's *my* turn!'
+says: 'Another time, another battlefield, *my* victory.'
+says: 'I've got to keep running.'
+says: 'It's all so futile!'
+says: 'Cowards live to fight another day.'
+says: 'Life it seems will fade away, drifting further every day.'
+says: 'Emptiness is filling me, to the point of agony.'
+says: 'Cannot stand this hell I feel!'
+cries: 'Someone help me, oh please God help me!'
+cries: 'Please! I have a mate and six siblings!'
+cries: 'Mama, they try and break me!'
diff --git a/lib/file/monspeak.txt b/lib/file/monspeak.txt
new file mode 100644
index 00000000..5981f91c
--- /dev/null
+++ b/lib/file/monspeak.txt
@@ -0,0 +1,361 @@
+# This is the file for allowing uniques to speak their "own" lines.
+# Deleting this file will have no real effect on the game. Modifying it may
+# cause STRANGE unique lines to come up if the format's wrong, but shouldn't
+# crash anything. The format goes like so:
+#
+# N:45:whoever this is
+# 3
+# says bravado line 1
+# says bravado line 2
+# says bravado line 3
+# 2
+# says fear line 1
+# says fear line 2
+#
+# The number after the N: is the "monster index number" obtained from
+# r_info.txt. The text field after that number isn't actually used--it's
+# just there to help humans edit the file. The numbers on lines by
+# themselves say the number of "bravado" and "fear" lines. Getting these
+# numbers wrong won't crash anything, but will produce strange lines.
+#
+# Two or more monsters can share lines; just put their N: lines in a
+# contiguous block.
+#
+# To stop a certain monster from having unique lines, put a # in front of
+# its N: line.
+#
+# Have fun with this! --Matt G
+
+N:8:Farmer Maggot
+3
+says, 'I'm carrying CASH!'
+smirks, 'You wouldn't kill me, would you?'
+gleefully shows off a nifty object he just bought.
+2
+screams, 'Don't hurt a poor helpless hobbit!'
+yells, 'Where are my vicious dogs when I need them?'
+
+N:63:Smeagol
+27
+sniggers.
+grovels.
+picks his nose.
+pines for his precious.
+searches his pockets.
+eats some slimy creatures.
+mutters, 'My precious, wheres my precious?'
+shouts, 'No Master Hobbitsisisisis!'
+cries, 'The ring was ours for agesisisisis!'
+says, 'Smeagol sneeking! ME! Shneekingsisis!'
+screams, 'Nasty Hobbitsisisisis...'
+says, 'Come on, quickly, follow Smeagol'
+says, 'Every way is guarded, silly foolsis!'
+says, 'Nasty Bagginis, stole my precious.'
+says, 'She will kill them oh yes she will precious.'
+whines, 'Weees wants some fishises.'
+says, 'Whats has its got in its pocketses, hmmm?'
+whimpers, 'We've lost itses we have.'
+says, 'He'll eastus all the world if he getsitses it.'
+says, 'No food, no rest; Smeagol a SNEAK!'
+says, 'What a dainty little dish you will be for her.'
+says, 'Hobbitses always SOOOO Polite.'
+screams, 'Stop, Thief!'
+says, 'Makeses him drop his weapon precious.'
+grovels, 'He has only four fingers on the black hand.'
+growls, 'Not nice Hobbits, not sensible!'
+says, 'If you findesis it, give it us back.'
+3
+says, 'Don't hurt us, mastersisis.'
+says, 'Poor Smeagol, poor Smeagol.'
+says, 'No AH! Don't hurtsis us.'
+
+N:135:Mughash the Kobold Lord
+4
+says, 'I may be a kobold, but I can beat you!'
+says, 'Feel my wrath, fool!'
+says, 'Death and destruction make me happy!'
+snickers evilly.
+2
+screams, 'Cowards! Why did you abandon me?'
+begs for mercy.
+
+N:137:Wormtongue, Agent of Saruman
+6
+whines and sniggers.
+leafs through 'Cowards Monthly.'
+whispers nasty things.
+says, 'I'll slaughter you slowly...'
+giggles as he fingers his knife.
+says, 'Now, you shall taste my wrath!'
+3
+begs you to spare his miserable life.
+whines, 'This is not my fault!'
+screams, 'Help! Help!'
+
+N:138:Robin Hood, the Outlaw
+6
+eyes your money pouch covetously.
+says, 'You look like Nottingham's man to me!'
+says, 'I bet I can shoot better than you...'
+says, 'Give 'til it hurts!'
+says, 'Don't force me to put an arrow in your skull...'
+says, 'Kevin Costner has soiled my name!'
+3
+begs you to spare his life.
+says, 'But I'm a GOOD guy, really!'
+says, 'Money? Sure, take it all back!'
+
+#N:169:Brodda, the Easterling
+#N:291:Ulfast, Son of Ulfang
+
+N:180:Orfax, Son of Boldor
+N:237:Boldor, King of the Yeeks
+5
+wonders aloud about the quality of your weapon.
+spouts torrents of taunts.
+shouts, 'YEEK! YEEK! YEEK!'
+says, 'I'll teach you to respect yeeks!'
+says, 'Feel lucky, punk?'
+2
+sobs, 'I didn't MEAN it...'
+whimpers and moans.
+
+N:200:Hobbes the Tiger
+4
+says, 'Why were people put here? TIGER FOOD!'
+says, 'Yum! Adventurer sandwiches!'
+says, 'I ate Calvin, now I'll eat YOU!'
+says, 'I'll make your short life nasty and brutish!'
+1
+yells, 'Ow! Get me back to the comics!'
+
+N:140:Lagduf, the Snaga
+N:186:Grishnakh, the Hill Orc
+N:215:Golfimbul, the Hill Orc Chief
+N:260:Ufthak of Cirith Ungol
+N:314:Shagrat, the Orc Captain
+N:315:Gorbag, the Orc Captain
+N:330:Bolg, Son of Azog
+N:350:Ugluk, the Uruk
+N:356:Lugdush, the Uruk
+N:373:Azog, King of the Uruk-Hai
+19
+fingers his blade and grins evilly.
+snickers, 'Now, I strike a blow for *our* side!'
+says, 'Orcs don't get no respect... I'm gonna change that!'
+calls your mother nasty names.
+says, 'I'll bet your innards would taste real sweet...'
+belches and spits.
+scratches his armpits.
+says, 'I love the smell of fresh blood.'
+says, 'Yeeha! Another idiot to slaughter!'
+hawks a loogie in your direction.
+farts thunderously.
+wonders aloud how many experience points you're worth.
+says, 'I love being psychotic!'
+says, 'My brain's on fire with the feeling to kill!'
+says, 'I shall torture you slowly.'
+calls you a scum-sucking pig-dog.
+says, 'I shall break you!'
+says, 'You're not so tough, loser!'
+says, 'Heh-heh, heh-heh, killing people is cool.'
+6
+screams, 'Hey, orcs have rights too!'
+says, 'You're just prejudiced against orc-kind, aren't you?'
+begs, 'Spare me and I'll get you Ringil! Really!'
+says, 'Next time, I'm bringing more Uruks with me!'
+says, 'Don't hate me because I'm ugly!'
+whimpers and grovels.
+
+N:382:Mime, the Nibelung
+4
+says, 'Wagner misrepresented us!'
+says, 'I'll mess up all your stuff!'
+says, 'Give me the Rheingold, or die!'
+hums 'Ride of the Valkyries.'
+1
+screams, 'Help! Murder! Murder!'
+
+#N:392:Sangahyando of Umbar
+#N:380:Angamaite of Umbar
+
+#This next may be unnecessarily evil... :-]
+
+N:393:It
+6
+says, 'Nyah, nyah, betcha can't find me!'
+says, 'Come get some!'
+magically summons mighty undead opponents!
+chuckles evilly.
+magically summons Cyberdemons!
+summons special opponents!
+2
+howls, 'I'll be back!'
+whimpers, 'They said this invisibility thing was better than it is!'
+
+N:441:Barney the Dinosaur
+7
+says, 'Cooperation! That's the magic word!'
+mutters, 'I *hate* those Teletubbies...'
+says, 'Won't you be my friend?'
+says, 'Let's all sing a HAPPY SONG!'
+mugs for the camera.
+simpers disgustingly.
+chews up a 'Tinky Winky' doll.
+3
+begs, 'Don't! Think of the children!'
+screams, 'But I'm a big TV star!'
+sobs, 'All right! I apologise! I really really do!'
+
+N:505:Groo the Wanderer
+4
+says: 'A fray! A fray!'
+says: 'Groo does what Groo does best!'
+says: 'All right, you savage, mindless creature. Prepare to meet your equal!'
+says: 'I will slay you, and hack you, and waste you, and destroy you!'
+3
+says: 'Did I err?'
+says: 'I did not think Groo could be frightened...'
+says: 'I need no aid! Groo is beyond help!'
+
+N:934:Fangorn the Treebeard
+6
+says: 'The night stretches out on the Isengard!'
+says: 'Trolls are strong, Ents are STRONGER!'
+says: 'Saruman will now stop using his axes on the trees...'
+says: 'have you seen some Ent women ?'
+says: 'I will crush all those burarum ... those orcs!'
+says: 'I am totally of the side of nobody since nobody is totally of my side...'
+0
+
+N:732:Bull Gates
+10
+says, '640K should be enough for ANYBODY!'
+says, 'Buy Windows 2000; the filesystem rocks!'
+says, 'Linux? Never heard of it...'
+says, 'Resistance is futile--you will be assimilated.'
+says, 'NT is the solution for ALL your needs!'
+hacks out some code and calls it a Service Pack.
+says, 'We don't have a monopoly... Mac OS still exists!'
+wonders if he should buy a small country.
+says, 'Where will we let you go today? The Recycle Bin!'
+cackles diabolically.
+3
+sobs, 'OK, Linux doesn't suck. Let me live?'
+screams, 'Is megalomania THAT bad?'
+apologises for MS-DOS.
+
+N:733:Santa Claus
+8
+says, 'Ho ho ho! You're gonna die!'
+says, 'You're gettin' COAL in your stocking!'
+says, 'On Smasher, on Crasher, now dash away all!'
+chortles sadistically.
+says, 'You're on the Naughty List!'
+says, 'No presents for you, ever!'
+says, 'I'll sic my man-eating reindeer on you!'
+says, 'I hate Christmas so much that I've gone psychotic!'
+3
+sobs, 'Think of the children you'll disappoint!'
+sobs, 'No, Virginia, there isn't... not any more...'
+attempts to buy you off with offers of goodies.
+
+N:764:Uriel, Angel of Fire
+N:765:Azriel, Angel of Death
+N:769:Gabriel, the Messenger
+7
+says, 'Repent, evildoer!'
+says, 'My righteousness shall cleanse you!'
+says, 'Don't EVER steal from the collection plate!'
+says, 'God may love you, but *I* don't!'
+says, 'I shall smite thee with extreme prejudice!'
+says, 'Hope you like eternal damnation!'
+says, 'Verily, it is too late for thee.'
+3
+screams, 'Help! I am undone!'
+says, 'The Most High hath ordained this; I must follow.'
+screams, 'My God, my God, why hast thou forsaken me?'
+
+
+N:850:Carcharoth, the Jaws of Thirst
+N:846:Fenris Wolf
+N:840:Draugluin, Sire of All Werewolves
+8
+barks and bellows frighteningly!
+says, 'Oh good, another chew toy!'
+says, 'Yummy! I was getting tired of chicken...'
+lets out an earsplitting howl.
+drools all over the dungeon.
+lifts his leg at the nearest wall.
+says, 'Bad adventurer! No more living for you!'
+snarls and howls.
+3
+cringes and whimpers.
+says, 'Look, I promise I won't bite the mailman anymore!'
+says, 'Hey, put that rolled-up newspaper down!'
+
+N:830:Cantoras, the Skeletal Lord
+N:831:Mephistopheles, Lord of Hell
+N:818:The Mouth of Sauron
+N:819:Klingsor, Evil Master of Magic
+N:804:Vecna, the Emperor Lich
+N:844:Feagwath the Undead Sorceror
+N:856:Gothmog, the High Captain of Balrogs
+N:860:Sauron, the Sorcerer
+12
+brags, 'My power is beyond compare!'
+snorts, 'A mere mortal dares challenge my might? HA!'
+says, 'Not another one! I just finished chewing on the last!'
+wonders aloud how many XP you're worth.
+leafs through 'Evil Geniuses For Dummies'.
+mutters, 'Another darn loser to kill...'
+says, 'Angband shall claim your remains!'
+says, 'Another 12 skulls and I get that reward from the Boss!'
+yawns at your pathetic efforts to kill him.
+says, 'Minions, slaughter this fool!'
+says, 'Set thine house in order, for thou shalt die...'
+says, 'I'm no god... God has MERCY!'
+2
+screams, 'This CAN'T be happening!'
+shouts, 'Kill me if you want, the Boss will getcha!'
+
+N:928:Mathilde, the Science Student
+3
+waves.
+wishes you luck.
+skips along happily.
+1
+is surrounded by the purple aura of the RNG. Touch at your peril.
+
+N:861:Dark God, the Mighty Coder of Hell
+6
+says 'Hullo'.
+emits a low 'hmmmm'.
+screams 'I came from the Hells for YOU!'
+laughs out loudly.
+mutters something about bugs.
+asks you about the new version.
+1
+screams 'ToME rules!'.
+
+N:969:Princess
+4
+cries.
+screams 'Help me !!!'.
+whines 'I need your help, great hero...'.
+implores you to help her.
+1
+screams 'I am too young to die!'.
+
+N:970:Merton Proudfoot, the lost hobbit
+1
+whines 'Please help me, noble hero! My leg is broken, and I cannot walk.'
+1
+whines 'No, please don't hurt me!'
+
+N:1039:Improv, the mighty MoLD
+2
+screams 'Your code is ugly!'
+mutters something about bugs.
+1
+screams 'ToME rules!'.
diff --git a/lib/file/news.txt b/lib/file/news.txt
new file mode 100644
index 00000000..25907cb3
--- /dev/null
+++ b/lib/file/news.txt
@@ -0,0 +1,24 @@
+#G #W ~ ~~~ ~~~ ( #G
+#G ======/ ===== #W ~~ ) ~~ ) #G === === ===
+#G / // / __ \ #W )~ #r,.#W~ ( (#G // || \\ //
+#G // | | | | #W _#r,-"####`-^ #W ) #G || || || ||==:
+#G || | | | | #W ,#D###########r`W='#D###W`.#W () #G || || || ||
+#G \\_// \ -- / #W ,#D#######################D###W:#r,=. #G || || || \\===
+#G \_/ ===== #W /.#D###W,".#D#####W,".#D###########r"#W##\ #G #ov2.3.5#G
+#W ,-. _,-= /"._ ,-. #w ," #W `:' `:#D#####W[JW] ,. #G
+#W," `," `=._ `" -#w; #G The #W\#D#####W;"' V \#W __,-. ,-=.
+#W / " `-. #w / #GTroubles of #W `" `-.#W__,-""._," "--," >-=-
+#W ,-"`. #w _' #G Middle Earth#w "._ #W` _/
+ _," #y __ #w "=.".
+ _," #y _,' `-. #w `._,=-._ _,-.
+ _._ ,"._," #y ,'_,'Y`-. `. #w "-." \,-.
+-" `-" #y / / ,' `. \ #w `-
+ #y / / / \ \ #w One Ring to rule them all,
+Maintained by darkgod #y f f f l l#w One Ring to find them.
+ #o darkgod@t-o-m-e.net #y t t t j j
+ #y \ \ \ / / #w One Ring to bring them all
+ #ohttp://www.t-o-m-e.net #y \ \ \._ / /#w and in the Darkness bind them.
+ #y `_`-._J-' /
+ #y `-.___,-'
+ In the Land of Mordor, where the Shadows lie. [J.R.R. Tolkien]
+.
diff --git a/lib/file/news2.txt b/lib/file/news2.txt
new file mode 100644
index 00000000..ddcfbcef
--- /dev/null
+++ b/lib/file/news2.txt
@@ -0,0 +1,24 @@
+#G #W ~ ~~~ ~~~ ( #G
+#G ======/ ===== #W ~~ ) ~~ ) #G === === ===
+#G / // / __ \ #W )~ #r,.#W~ ( (#G // || \\ //
+#G // | | | | #W _#r,-"####`-^ #W ) #G || || || ||==:
+#G || | | | | #W ,#D###########r`W='#D###W`.#W () #G || || || ||
+#G \\_// \ -- / #W ,#D#######################D###W:#r,=. #G || || || \\===
+#G \_/ ===== #W /.#D###W,".#D#####W,".#D###########r"#W##\ #G #ov2.3.5#G
+#W ,-. _,-= /"._ ,-. #w ," #W `:' `:#D#####W[JW] ,. #G
+#W," `," `=._ `" -#w; #G The #W\#D#####W;"' V \#W __,-. ,-=.
+#W / " `-. #w / #G Tales of #W `" `-.#W__,-""._," "--," >-=-
+#W ,-"`. #w _' #G Middle Earth#w "._ #W` _/
+ _," #y __ #w "=.".
+ _," #y _,' `-. #w `._,=-._ _,-.
+ _._ ,"._," #y ,'_,'Y`-. `. #w "-." \,-.
+-" `-" #y / / ,' `. \ #w `-
+ #y / / / \ \ #w One Ring to rule them all,
+Maintained by darkgod #y f f f l l#w One Ring to find them.
+ #o darkgod@t-o-m-e.net #y t t t j j
+ #y \ \ \ / / #w One Ring to bring them all
+ #ohttp://www.t-o-m-e.net #y \ \ \._ / /#w and in the Darkness bind them.
+ #y `_`-._J-' /
+ #y `-.___,-'
+ In the Land of Mordor, where the Shadows lie. [J.R.R. Tolkien]
+.
diff --git a/lib/file/rart_f.txt b/lib/file/rart_f.txt
new file mode 100644
index 00000000..3bebf94d
--- /dev/null
+++ b/lib/file/rart_f.txt
@@ -0,0 +1,86 @@
+85
+The Bag of Tricks
+Kenault's Cantrip Generator
+The Deck of Wild Magic
+Baalzebub's Tormented Box
+Baalzebub's All-seeing Servant
+Boccob's Unfinished Works
+The Gem of Fire
+The Gem of Ice
+The Gem of Venom
+The Gem of Knowledge
+The Gem of Rage
+The Gem of Hate
+The Gem of Wisdom
+The Gem of Ghosts
+The Shrunken Head of Nightmares
+Raal's Voodoo Doll of Revenge
+The Mirror of Alternate Dimensions
+The Ebon Cube of Darkness
+The Diamond Prism of Light
+Gruumsh's Bottle of Death
+The Devil's Pentagram
+Mordekainen's Sneaking Eye
+Kelek's Practical Joke
+Kelek's Wormhole Machine
+Tenser's Alteration Manual
+Raal's Tome of Unconventional Warfare
+The Tome of Collected Weird Magic
+The Pendulum of Orcus
+Raal's Black Candle
+Mordekainen's Pocket Magician
+Benetar's Death Ray Experiment
+Valdarbon's Automatic Alchemist
+Lloth's Ceremonial Dagger
+The Tome of Elven Household Magic
+a Parchment titled ``Demigods and their Uses''
+a Parchment titled ``Magic for the Layman''
+Balrilbon's Bag of Wondrous Tricks
+Bumganir's Bag of Magic Toys
+The Skull of Ancient Wisdom
+Benetar's Mana Battery
+Benetar's Portable Plague
+Gamenlon's Summoning Manual
+Balrilbon's Soulgem
+The Lost Works of Kenault
+The Crystal Ball of Godly Sights
+The Box of Many Wonders
+Baalzebub's Tormented Skullcap
+The Jester's Cap of Insanity
+The Bottomless Bottle
+a Parchment titled ``Planar Travel Made Easy''
+The Wand Construction Kit
+The Clay Tablets of Antiquity
+Raal's Tormented Spirits
+Tenser's Mechanical Magician
+Boccob's Magical Mish-mash
+The Grail of Kenault
+a Parchment titled ``Household Magic''
+a Parchment titled ``Tenser's Last Words''
+The Hand of Vecna
+The Skull of Vecna
+The Eye of Vecna
+The Crystal Ball of The Witch-King of Angmar
+a Parchment titled ``Secrets of the Gnomish Wizards''
+The Medallion of Good Will
+The Immortal Skull of Benetar
+Heward's Excellent Experimental Earmuffs
+Bigby's Big Book of Brutality
+The Cunning Plan of Zog
+a Parchment titled ''Immortality For Dummies''
+Raistlin's Ready Ranger
+Tenser's Torch of Spontaneous Combustion
+Mordenkainen's Mysterious Mind-Masher
+a Parchment titled ''Famous Last Words''
+Jor's Buckler of Missile Attraction
+Jor's Compendium of Strange Behaviour
+Agannazar's Antique Acorn
+Cathal's Corrupting Cymbal
+Pytar's Portable Pandemonium
+The Toenail of Vecna
+Jor's Book of Impossible Occurences
+a Parchment titled ''Finer Points of Munchkinism''
+Agannazar's Altruistic Assassin
+Tenser's Top-Heavy Teaspoon
+Olive's Omnipotent Ostrich
+Cathal's Collapsible Crutch
diff --git a/lib/file/rart_s.txt b/lib/file/rart_s.txt
new file mode 100644
index 00000000..88515a84
--- /dev/null
+++ b/lib/file/rart_s.txt
@@ -0,0 +1,87 @@
+85
+a Bag
+a Shiny, Black Box
+a Deck of Cards
+a Rusty, Slimy Box
+an Eyeball
+a Red Tome
+a Red Gem
+a Blue Gem
+a Green Gem
+a White Gem
+an Orange Gem
+a Black Gem
+a Gray Gem
+a Translucent Gem
+a Shrunken Head
+a Voodoo Doll
+a Mirror
+a Black Cube
+a Prism
+a Black Bottle
+a Pentagram
+a Glass Eyeball
+something weird
+a Portable Vortex
+a Green Tome
+a Black Tome
+a White Tome
+a Pendulum
+a Black Candle
+a Tiny Doll
+a Black Staff
+a Tiny Doll
+a Slimy Dagger
+a Gray Tome
+a Parchment
+a Parchment
+a Bag
+a Bag
+a Human Skull
+a Blue Box
+a Gray Bottle
+an Orange Tome
+a Shiny Gem
+an Ancient Gray Tome
+a Crystal Ball
+a Golden Box
+a Skullcap
+a Jester's Cap
+a Broken Bottle
+an Arcane Parchment
+many Small Wooden Sticks
+some Clay Tablets
+a Smoky Vial
+a Doll
+some Multi-colored Clay
+a Clay Jar
+an Arcane Parchment
+an Arcane Parchment
+a Decayed Hand
+a Decayed Skull
+a Decayed Eye
+a Large Crystal Ball
+an Arcane Parchment
+a Medallion
+a Jewel-Encrusted Skull
+some Earmuffs
+a Battered Book
+a Small Note
+a Parchment
+a Small Figurine
+a Torch
+a Rune
+a Singed Parchment
+a Holed Buckler
+a Red Book
+an Acorn
+a Cymbal
+a Mechanical Music-Box
+a Toenail
+a Torn Book
+a Greasy Parchment
+a Blackened Figurine
+a Heavy Teaspoon
+an Avian Figurine
+a Crutch
+
diff --git a/lib/file/readme! b/lib/file/readme!
new file mode 100644
index 00000000..90b6bbf2
--- /dev/null
+++ b/lib/file/readme!
@@ -0,0 +1,36 @@
+Most files in this directory are use by the get_rnd_line function for various
+purposes in the game. They can be edited and customised by the user with any
+ascii text editor. If you add / remove lines you should modify the index (the
+first line) accordingly. If you set an invalid index or remove the buffer line
+weird messages and other things can appear, and you can crash the game. Please
+see sample.txt before you try modifying the files.
+The a_*.txt and w_*.txt are used when the game generates a random artifact.
+However, instead of picking a name from the appropriate file, the game may
+form a new name from syllables[].
+
+The files in this directory:
+
+A_CURSED TXT Possible names for randomly generated cursed armour artifacts
+A_HIGH TXT Possible names for randomly generated 'powerful' armour artifacts
+A_LOW TXT Possible names for randomly generated 'weak' armour artifacts
+A_MED TXT Possible names for randomly generated 'medium' armour artifacts
+BRAVADO TXT Possible lines for speaking uniques
+CHAINSWD TXT Possible noise for the Chainsword
+CRIME TXT Possible crimes that speaking uniques may have committed
+DEAD TXT The tombstone picture (the death screen)
+DEATH TXT Possible 'last words' when the player dies
+ELVISH TXT Syllables for the names of random artifacts
+ERROR TXT Possible random error messages (instead of "Type ? for help")
+MONDEATH TXT Possible 'last words' for speaking uniques
+MONFEAR TXT Possible lines for scared speaking uniques
+NEWS TXT The game intro screen
+README! You are reading this file right now
+RUMORS TXT Possible rumours (for scrolls or rumour and shopkeepers)
+SAMPLE TXT A sample file for the random line selecting function
+SILLY TXT Silly monster names for hallucination
+SMEAGOL TXT Smeagol lines
+SMEAGOLR TXT Smeagol fleeing
+W_CURSED TXT Possible names for randomly generated cursed weapon artifacts
+W_HIGH TXT Possible names for randomly generated 'powerful' weapon artifacts
+W_LOW TXT Possible names for randomly generated 'weak' weapon artifacts
+W_MED TXT Possible names for randomly generated 'medium' weapon artifacts
diff --git a/lib/file/rumors.txt b/lib/file/rumors.txt
new file mode 100644
index 00000000..1928670b
--- /dev/null
+++ b/lib/file/rumors.txt
@@ -0,0 +1,201 @@
+199
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+They say that you can't trust rumors.
+You have no more Black Potions of Death.
+They say that smart guys hang around at 1600'.
+They say that tough guys hang around at 1500'.
+They say that handsome guys hang around at 1200'.
+They say that a visit to 3250' can be quite an experience.
+They say that a visit to 3250' will only get you killed.
+Throw a Potion of Blindness at a monster and it cannot cast any spells!
+MAKE MONEY FAST! Find a Treasure Pit!
+Not satisfied with the artifacts you find? Then create your own!
+They say that Sauron has forged an all-powerful Ring.
+A good item will not corrode.
+They say that Nibelungs live in dark caves.
+Some weapons that slay dragons can be very deadly against them...
+Finding the Phial of Galadriel at 50' is nothing to be proud of.
+There are Black Market stores hidden deep in the dungeon, with COOL stuff!
+What a pity, you cannot read it!
+You will encounter a dark, tall stranger...
+A Mithril mail will not rust.
+An Adamantite mail will not rust.
+A Rusty Chain Mail cannot rust any further.
+If you are a mage, you will NOT want to find Raal's Tome of Destruction!
+You won't want to find Raal's Tome of Destruction!
+You won't want to find Raal's Tome of Destruction, unless you are a mage.
+A Wand of Death is useless against monsters that are tougher than you.
+A Wand of Death is of little use against foes that are dead already.
+Try taking off your armor before fighting a Gelatinous Cube!
+They say that only one sword can score *CRITICAL* hits.
+This rumor is not true.
+If you can fall like a feather, you need not care about gravity.
+They say that you should rejoice if you find a scroll labeled ""!
+You don't always have to kill everything you meet!
+If you can't beat it, leave it alone!
+An umber hulk can be a confusing sight.
+There *is* a good use for Potions of Detonations, Ruination and Death...
+Watch your step!
+It's a bad idea to throw away a Longsword (4d5).
+It's a bad idea to wield a Longsword (4d5).
+It's useless to bash monsters with bows - but there's one notable exception...
+Actually, Slime Mold Juice is not completely useless.
+Help me! I'm being held captive in a Vault at 2850'!
+Ever tried inscribing your armor {erodeproof}?
+Using a Morningstar in the evening has no effect.
+Why are you wasting time reading fortunes?
+There is a horrible, ghastly fate awaiting you... at 2700'!
+You can get the Longsword 'Ringil' by doing the following:
+You can protect yourself from Great Wyrms of Power by doing the following:
+Its true name is 249.
+You feel like someone's pulling your leg!
+AAAAAAAAAAARRRRRRRRGGGGHHHHHHH!
+Try inscribing the name of the first monster killed by it in the weapon!
+The richer the victim the happier the thief.
+Beware the Jabberwock, my son! The jaws that bite, the claws that catch...
+There's something bad about what you are carrying in your backpack...
+Thieves are more likely to appear if you are carrying a lot of money.
+Brand's sword, Werewindle, probably knows more than just one trick.
+They say that Scrolls of *Curse Weapon* can create powerful cursed artifacts.
+They say that the Chainsword makes monsters mad with its awful noise!
+They say that Ringil shines so brightly that it makes monsters angry.
+Orcs are mortally afraid of weapons that can slay them.
+There is a way to turn a Ring of Speed (-20) into a Ring of Speed (+20).
+There is no way to turn a Ring of Speed (-20) into a Ring of Speed (+20).
+VECNA LIVES!!!
+Cool guys can resist fire.
+They say that death incarnate wears heavy metal boots...
+You feel the Longsword (t) you are carrying in your backpack is special...
+If you start seeing red monsters, you have probably gained infravision.
+They say that the dungeon is deeper than the Abyss.
+When all else fails, read the instructions.
+No poison is immediately deadly.
+I have seen a Ring of Speed (+50) in the Black Market!
+Telepathy works like a two-way door.
+Elvish waybread might negate the effects of poison.
+Once uncursed, Calris will become a deadly weapon.
+If there's a stairway to hell, there must also be a stairway to heaven.
+You feel your luck is turning...
+If you thought Death swords were bad, wait until you meet Killer katanas!
+Overeating can be bad for your health if there are others nearby.
+Cave dwellers are accustomed to darkness and rarely enjoy bright light.
+A creature made of stone can be slain by a spell that turns stone to mud.
+It is often a good idea to throw items that you don't want to eat or drink.
+The faster you run the more food you will burn.
+Invisible monsters will often expose themselves if you drop items around you.
+They say that the key to killing tougher monsters is called "hit&run".
+They say that there is no such thing as free advice.
+Wearing an Amulet of Doom will take you into the Dungeons of Doom.
+You can often wrest one last charge from an empty wand if you try hard enough.
+Wands may recharge themselves if you leave them on the floor long enough.
+There is more than one way to deal with a locked door.
+Afraid of your valuables getting stolen? Carry more junk!
+Afraid of your money getting stolen? Invest it!
+If you hear something smash into splinters, you had better watch out.
+They say that you had better leave Greater hell-beasts alone.
+Selling unidentified potions to shopkeepers might be safer than quaffing them.
+Always look out for trapdoors on "special" feeling levels!
+There is a way to max out your stats with Potions of Charisma & Nexus.
+Unique opponents will recover their health faster than other creatures.
+"So when I die, the first thing I will see in heaven is a score list?"
+You're going into the morgue at midnight?
+How dare you! I will not buy that!
+A Potion of Detonations is also known as nitroglycerin...
+Operation OVERKILL has started now.
+There is a trap on this level!
+A weapon of Undead Slaying has all you need to kill a ghost.
+A weapon of Dragon Slaying may give you resistance to a dragon's breath attack.
+They say that only a Warrior will want to wear the Terror Mask.
+All that is shall come to an end - a dark day dawns for the gods.
+The One Ring is powerful, but will eventually destroy its owner.
+Having troubles with summoners? Door Creation is your friend!
+Stairway Creation may be slower than Teleport Level, but safer...
+Wands of Heal Monster are useful! Hint: ball spell, @....moo(o)ooo
+Guaranteed heal self - scenario: o'@, type c4c4c4c4
+If it can't see you, it can't hurt you!
+If it can't see you, you might still be able to hurt it...
+I love you, you love me, we are a happy family!
+No animal is interested in sex if it is mortally scared.
+The butler did it.
+The butler is innocent.
+There is a plenty of Longswords around 1000'.
+Groo is your worst nightmare.
+Freddy Krueger is your worst nightmare.
+Groo is an idiot! Groo is a dolt! He is a fool! He has no mind!
+There are often stairways in graveyards: bad people are carried to hell...
+Only a god of Thunder could ride a lightning bolt!
+When the day of Ragnarok comes, Surtur will set the world afire...
+Surtur's accursed sword, Twilight, burns with everlasting fire.
+Groo may be as dumb as an amoeba, but he knows a good sword when he sees one.
+Weapons of Flame will light your way.
+Want to invest some money? Contact $crooge McDuck, 1300'.
+Need a loan? Contact $crooge McDuck, 1300'.
+They say that the gods get angry if you pray too much.
+For any remedy there is a misery.
+Poison will kill you slowly.
+Didn't you forget to pay?
+Death is just life's way of telling you you've been fired.
+They say that nobody can defeat his own ghost.
+A greedy genocide can be a fatal mistake, especially if you are low on hits.
+PLEASE ignore the previous rumor.
+There are scrolls that can be read only by mages.
+Some undead opponents will come back if defeated, more powerful than before!
+The answer is 42.
+Your mother wears army boots!
+One level further down somebody is getting killed, right now.
+Meet me at 1900' if you are a man.
+Bashing a creature may sometimes stun it.
+And now, Groo does what Groo does best!
+One Ring to rule them all, One Ring to find them.
+One Ring to bring them all and in the darkness bind them.
+Three Rings for the Elven-kings under the sky...
+Never carry a Potion of Detonations if there is a fire trap nearby!
+Laugh to scorn the power of man, for none of woman born shall harm thee!
+All hail thee that shalt be king hereafter!
+He who laughs at Groo's brains will find there is nothing to laugh about.
+A wise man always speaks too soon...
+Let us not dwell on possible bad fortunes!
+Appearance is only the frosting, not the cake!
+A feeling of Death flows through your body.
+Violence is no solution.
+Boots of Speed (+50) are no myth!
+You will need to Restore the Constitution if the Anarchists strike.
+Drain you of your sanity: Face the Thing That Should Not Be!
+Since by curse it came to me, accursed be this Ring!
+Each shall itch to possess the Ring, but none in it shall find pleasure!
+I know whatever was; whatever is, whatever shall be.
+Barney MUST die!!!
+Pudpadnoy Tooboothokoot is possessed by a demon known only as "It".
+They say that the One Ring has a very special curse.
+They say that alcohol is bad for your health.
+What if you DON'T give a name to the artifact you create..?
+They say that ancient battlefields are often haunted.
+Beware of pits that fill the whole level!
+Liar! I have not the gold!
+They say that the true name of wall monsters is 177.
+Never mind the Phial of Galadriel - the Phial of the Gods kicks its butt!
+A Ring of Speed? Phooey! Try looking for a Ring of *Speed*!
+Thisss cccity isss guilty... the crime isss life... the ssentence isss DEATH!
+If you hear heavy steps - watch out!
+A visit to the Zoo is educational: you meet many strange animals.
+What happens if you wear a Ring of Extra Ring Fingers (-2) {cursed}?
+Oremor nhoj em llik tsum uoy emag siht niw ot.
+If I cancel tomorrow the undead will thank me today.
+Hellfire will burn your soul...
+Call the Void needs a lot of room to cast...
+Why doesn't Detect Monsters show invisible monsters? 'Cos you can't see 'em!
+I'll tell you the truth, son: your soul's gonna burn in a lake of fire!
+There is a rare spellbook called [M$ PowerFools] {cursed}
+*** LOW HITPOINT WARNING! ***
+You cruelly stab the helpless, sleeping Software bug!
+Slab: Jus' say AarrghaarrghpleeassennononoUGH.
+You feel the Windows (95) on your hard disk is broken...
+Spirit, hatch that painted spirit of the lamb sparrow.
+Gone insane from the pain that sure they know: for who the flange sound?
+With time the child dissipates within, this blinking boy made badly.
+Not to be never, never not to see, so as to dub the thee unforgiven.
+Soon in order to fill up our lungs: the warmth of twenty dead women.
+The eternal death of eons of the foreigner of the lie can die not absolutely.
+Drain you of your sanity: in front do the thing that does not have to be.
+The opinion you had were salt expresses.
+They say that the dark mists of Morgoth can both bestow and remove the curse.
diff --git a/lib/file/sample.txt b/lib/file/sample.txt
new file mode 100644
index 00000000..5ff826f7
--- /dev/null
+++ b/lib/file/sample.txt
@@ -0,0 +1,5 @@
+3
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+line # 1
+line # 2
+line # 3
diff --git a/lib/file/sfail.txt b/lib/file/sfail.txt
new file mode 100644
index 00000000..ca4d9cb5
--- /dev/null
+++ b/lib/file/sfail.txt
@@ -0,0 +1,34 @@
+32
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+rose petals
+dirty straw
+rusty metal cutlery
+small, furry animals
+assorted toy jewelry
+visions of merry, dancing gnomes
+old and dusty accounting records
+moldy crusts of bread
+grass
+wet mud
+smelly bilge water
+clean linens
+scrap metal
+rotting wood
+leaves
+small insects
+rainwater
+flowers
+perfumed water
+overcooked sausage
+twigs
+pine needles
+hard leather
+small bones
+feathers
+crumbling manuscripts
+fresh air
+stale, smelly air
+dust
+clay
+earth
+wood shavings
diff --git a/lib/file/silly.txt b/lib/file/silly.txt
new file mode 100644
index 00000000..9d46c8b8
--- /dev/null
+++ b/lib/file/silly.txt
@@ -0,0 +1,301 @@
+299
+******************BUFFER LINE -- DO NOT REMOVE!*****************************
+Borg
+Dalek
+Destructor
+Predator
+Wizard of Yendor
+Smurf
+Cosmic Horror
+Ultimate Unspeakable Lovecraftian Nightmare
+Space Invader
+Transformer
+Master of the Universe
+He-Man
+Pizza Worm
+Bogeyman
+Care Bear
+My Little Pony
+IRA Terrorist
+Nazi
+Klingon
+Cardassian
+Snark
+Luggage
+Killer penguin
+Were-penguin
+Giant were-penguin
+Peeping-Tom
+Jester
+Battlemech
+Invid
+Amazon
+Teenage Mutant Ninja Turtle
+Stork
+Hippo
+Roadrunner
+Fooglebird
+Ghoti
+Whale
+Apocalyptic Beast
+Anti-Christ
+Dark Avenger
+Evil Computer
+Finnish Sprayer
+Natas
+Poodle
+Fire Hydrant
+Multi-hued elephant
+Martian
+Purple alligator
+Space ship
+Miniature space fleet
+6-feet tall elephant
+Insurance salesman
+Benji
+Magnet
+Squid
+Typewriter
+Manitou
+Miniature Shetland pony
+Munchkin
+Mad Scientist
+Keystone Kop
+Magnificent big-game hunter
+Wicked witch
+B-1 Bomber
+Superman
+Batman
+Boy Wonder
+Incredible Hulk
+Amazing Spider-Man
+Orphan
+Fifth columnist
+Secret agent
+Spy
+Dog catcher
+Vacuum cleaner
+Lawnmower Man
+Bloodletter of Khorne
+Buzzard
+Oyster
+Wombat
+Midget elephant
+Chameleon
+Dungeon Master
+Dungeon Keeper
+Beyonder
+Robocop
+Robocod
+Paradroid
+Dominatrix
+Paranoid
+Wagtail
+Great tit
+Pope
+Tribble
+Jedi knight
+Dark Jedi
+Master of Teras Kasi
+Vulcan
+Rancor
+Sarlacc
+Warbot
+Tailgunner
+Skinhead
+Paul Bunyan Machine
+Bungee
+Sumo wrestler
+Gargantuan sumo wrestler
+Karateka
+President
+King
+Lobo
+Trashman
+Teacher
+Cheshire Cat
+Mad Hatter
+March Hare
+Voodoo doll
+Rag doll
+Scarecrow
+Porcupine
+Stone idol
+Hacker
+Samurai
+Samurai Cat
+Phantom of the Opera
+X-man
+Wumpus
+Walrus
+Silver Surfer
+Chimpanzee
+Omnipotent being
+Reaperbot
+G. I. Joe
+Cobra Commander
+Dreadnok
+Autobot
+Battlemage
+Saboteur
+Master of Sinanju
+Cyborg
+Lemming
+Green Goblin
+Galactus
+Gooey Kablooie
+Prowler
+Iron Fist
+Stuka
+Mahdi
+Xeroc
+Catwoman
+Stupendous Man
+Lost boy
+Hunter-killer
+Mad butcher
+Vore
+Rotfish
+Heresiarch
+False prophet
+Emperor
+Tumtum
+Androgyne
+Thieving magpie
+Boast of England
+Red Rose Knight
+Unholy cow
+Q
+Red Baron
+Giddy goon
+Vogon
+Lizard King
+Hutt
+Puppet master
+Wookie
+Giant sandworm
+Muad' Dib
+Ent
+Huorn
+Werepotato
+Mindless one
+Alien queen
+Alligator man
+Ancient ape
+Ancient astronaut
+Astro-zombie
+Beast from 20000 fathoms
+Beast with a million eyes
+Black goat of the woods with a thousand young
+Behemoth
+Leviathan
+Big Bug
+Blob
+Khyberdemon
+Body snatcher
+Brain from Planet Ardus
+Scatologist
+Brain of Venus
+Brain that would not die
+Breath blaster
+Chickenstein's monster
+Creature from the Black Lagoon
+Loch Ness monster
+Creeping death
+Creeping unknown
+Fifty-foot woman
+Flumsh
+4D-man
+Bride of Frankenstein
+Galaxy being
+Giant baby
+Giant chicken
+Krazy Kat
+Giant celery stalk
+Gorilla witch
+Great god Porno
+H-dial monster
+Heap
+Love lotus
+Herman munster
+Highest intelligence
+H-man
+Hound of the Baskervilles
+Wizball
+ID monster
+Mancubus
+Arch-vile
+Pain elemental
+I-ball
+Killdozer
+Killer tomato
+Lurking fear
+Madball
+M.U.L.E.
+Magnetic monster
+Mixed-up zombie
+Hunchback of Notre Dame
+Monster that challenged the World
+Monster from green hell
+Morlock
+Queen Kong
+Robot Kong
+Mecha-Godzilla
+Ghidhrah
+Saucerman
+Ethereal
+Space Brain
+Floating brain of Hitler
+Stay Puft Marshmallow Man
+Supercow
+Swamp Thing
+Thing
+Teenage Frankenstein
+Teenage Werewolf
+Terror from the year 5000
+Triffid
+Tripod
+Xenophobic man
+Undying monster
+Unknown terror
+Unnamable
+Womaneater
+E.T.
+Toxic Avenger
+Onslaught
+Pacman
+Paradroid
+Bantha
+Ass-headed fish
+Wizard of Oz
+Juggernaut
+Cheshire Cat
+Tauntaun
+Heretic
+Other
+Uncanny
+Libido
+Super-ego
+Boy Blunder
+Big nothing
+Parademon
+Zelda
+Super Mario
+Great Giana Sister
+Magnetic man
+Pink elephant
+Elephant man
+Great Unknown
+Lurking unknown
+Cenobite
+Cheerleader
+Model T-1000 Terminator
+Whippoorwill
+Moon Maniac
+Killer Clown
+Stocking Strangler
+Torture Doctor
+Beast of the Black Forest
+Demon of the Belfry
+Sex Beast
+Sunday Morning Slasher
diff --git a/lib/file/smeagol.txt b/lib/file/smeagol.txt
new file mode 100644
index 00000000..53b1955a
--- /dev/null
+++ b/lib/file/smeagol.txt
@@ -0,0 +1,29 @@
+27
+******** BUFFER LINE *******************
+sniggers.
+grovels.
+picks his nose.
+pines for his precious.
+searches his pockets.
+eats some slimy creatures.
+mutters: 'My precious, wheres my precious?'
+shouts: 'No Master Hobbitsisisisis!'
+cries: 'The ring was ours for agesisisisis!'
+says: 'Smeagol sneeking! ME! Shneekingsisis!'
+screams: 'Nasty Hobbitsisisisis...'
+says: 'Come on, quickly, follow Smeagol'
+says: 'Every way is guarded, silly foolsis!'
+says: 'Nasty Bagginis, stole my precious.'
+says: 'She will kill them oh yes she will precious.'
+whines: 'Weees wants some fishises.'
+says: 'Whats has its got in its pocketses, hmmm?'
+whimpers: 'We've lost itses we have.'
+says: 'He'll eastus all the world if he getsitses it.'
+says: 'No food, no rest; Smeagol a SNEAK!'
+says: 'What a dainty little dish you will be for her.'
+says: 'Hobbitses always SOOOO Polite.'
+screams: 'Stop, Thief!'
+says: 'Makeses him drop his weapon precious.'
+grovels: 'He has only four fingers on the black hand.'
+growls: 'Not nice Hobbits, not sensible!'
+says: 'If you findesis it, give it us back.'
diff --git a/lib/file/smeagolr.txt b/lib/file/smeagolr.txt
new file mode 100644
index 00000000..cc13c96f
--- /dev/null
+++ b/lib/file/smeagolr.txt
@@ -0,0 +1,5 @@
+3
+******** BUFFER LINE **************
+says: 'Don't hurt us, mastersisis.'
+says: 'Poor Smeagol, poor Smeagol.'
+says: 'No AH! Don't hurtsis us.'
diff --git a/lib/file/speakpet.txt b/lib/file/speakpet.txt
new file mode 100644
index 00000000..d369fe8a
--- /dev/null
+++ b/lib/file/speakpet.txt
@@ -0,0 +1,53 @@
+51
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+says: 'Oi, loan me 2 quid for a drink or I'll knock your head off!'
+says: 'Nice boots, mate.'
+says: 'Wow, isn't it dark down here?'
+says: 'I think I saw something move..'
+says: 'I really think I saw something move this time..'
+says: 'Phew, what's that smell?'
+says: 'There's slime all over the walls..'
+says: 'Isn't it dark down here?'
+says: 'Dungeon explorers should be *friends*, right?'
+says: 'I luv you, you luv me..'
+says: 'Are we there yet?'
+says: 'I'm hungry, look for a McMorgoth's..'
+says: 'Is there a 'Monster WC' vault on this level?'
+says: 'Not sure of max line length, but...'
+says: 'Excuse me, but do you know the way to vanilla?'
+says: 'I heard that the orcs just voted in a union'
+says: 'You gonna finish that?'
+says: 'You're gonna eat *that*?'
+says: 'Oops. I've got slime mold on my boot/claw/wisp of vapor.'
+says: 'It's a fantastic space. Knock in a skylight to open it up...'
+says: 'I love troll. Eat once, stay full for a week.'
+says: 'Your sword sure is shiny.'
+says: 'Hey! WATCH IT! You almost hit me.'
+says: 'So, you gonna do some abracadabra?'
+says: 'Keep that demon blade away from me.'
+says: 'Do the one where the ceiling collapses!'
+says: 'That oak tree ain't bad looking. Think it has a sister?'
+says: 'The One Ring. That sure is a silly name...'
+says: 'Groo and Stormbringer entered a bar...'
+says: 'If Lokkak asks, I'm not here.'
+says: 'buurrrrppp'
+says: 'Kill, kill, kill. Let's play chess.'
+says: 'Swordmanship: 8.4, Style: 9.0'
+says: 'When *I* was young, it was up stairs BOTH WAYS.'
+says: 'Lohengrin, schmohengrin.'
+says: 'Very fancy. Bet you can't do it again.'
+says: '10AU says I can behead that snaga!'
+says: 'I bet the chicks dig those boots.'
+says: 'Have you seen those Teletubbie uniques?'
+says: 'Were you looking for these?'
+says: 'Let's blitz 'em!'
+says: 'You da bomb!'
+says: 'Medusa: now, SHE is ugly.'
+says: 'Do you *really* like me?'
+says: 'ToME just switched to an HMO.'
+says: 'Do you like my outfit?'
+says: 'So, you're an auto-roller baby...'
+says: 'Where'd you put the snaga call?'
+says: 'I've split my reed. Bummer.'
+says: 'A wand of rockets. Yeah, that's the ticket.'
+says: 'It's Zymurgist's Time!'
diff --git a/lib/file/timefun.txt b/lib/file/timefun.txt
new file mode 100644
index 00000000..ca3f642f
--- /dev/null
+++ b/lib/file/timefun.txt
@@ -0,0 +1,92 @@
+S:0000
+E:0059
+D:It's the witching hour!
+
+S:0100
+E:0259
+D:There may be Vampires around!
+
+S:0200
+E:0459
+D:Only ToME players are up now!
+
+S:0100
+E:0459
+D:It's really *very* late!
+
+S:0500
+E:0558
+D:Aren't you sleepy yet?
+
+S:0559
+E:0559
+D:It doesn't matter what you found!
+
+S:0600
+E:0759
+D:The sun is up. Time to have fun!
+
+S:0601
+E:0800
+D:Are you having fun yet?
+
+S:0800
+E:0905
+D:@$#$@$!%@$#%$@&$^#%@$!^#&#*
+
+S:0800
+E:1059
+D:You feel there is something special about this level.
+
+S:1100
+E:1159
+D:You see a maze of twisty passages, all alike.
+
+S:1155
+E:1200
+D:Are you having fun yet?
+
+S:1159
+E:1200
+D:This fortune is broken!
+
+S:1200
+E:1205
+D:This fortune is still broken!
+
+S:1200
+E:1359
+D:Uh oh, now you've done it!
+
+S:1400
+E:1729
+D:What did you do with the Phial?
+
+S:1730
+E:1744
+D:You need your chocolate vitamin!
+
+S:1745
+E:1759
+D:Tornado Warning!
+
+S:1759
+E:1800
+D:Night is coming. Danger! Danger!
+
+S:1700
+E:1859
+D:Take a Vampire out for dinner!
+
+S:2100
+E:2159
+D:Warp Factor Nine. Now!
+
+S:2200
+E:2359
+D:PARTY!
+
+S:2359
+E:2359
+D:It's almost the witching hour!
+
diff --git a/lib/file/timenorm.txt b/lib/file/timenorm.txt
new file mode 100644
index 00000000..611da496
--- /dev/null
+++ b/lib/file/timenorm.txt
@@ -0,0 +1,83 @@
+S:0000
+E:0000
+D:It is midnight.
+
+S:0001
+E:0200
+D:It is deep night.
+
+S:0300
+E:0400
+D:It is early morning, but still dark.
+
+S:0500
+E:0544
+D:It will be day soon.
+
+S:0545
+E:0559
+D:The sun is rising.
+
+S:0600
+E:0614
+D:The sun has risen.
+
+S:0615
+E:0659
+D:Morning has broken.
+
+S:0700
+E:0859
+D:It is early morning.
+
+S:0900
+E:0959
+D:It is midmorning.
+
+S:1000
+E:1154
+D:It is late morning.
+
+S:1155
+E:1159
+D:It is almost noon.
+
+S:1200
+E:1200
+D:It is noon.
+
+S:1201
+E:1459
+D:It is early afternoon.
+
+S:1500
+E:1559
+D:It is midafternoon.
+
+S:1600
+E:1659
+D:It is late afternoon.
+
+S:1700
+E:1729
+D:It will be night soon.
+
+S:1730
+E:1759
+D:The sun is setting.
+
+S:1800
+E:1859
+D:The night has begun.
+
+S:1900
+E:2059
+D:It is early night.
+
+S:2100
+E:2358
+D:It is late night.
+
+S:2359
+E:2358
+D:It is almost midnight.
diff --git a/lib/help/TANG.txt b/lib/help/TANG.txt
new file mode 100644
index 00000000..ffbb5701
--- /dev/null
+++ b/lib/help/TANG.txt
@@ -0,0 +1,134 @@
+|||||oy
+~~~~~01|Help|Newbie help
+#####R The ToME Newbie Guide
+by Pat Gunn and others
+Based loosely off of Chris Weisieger's Angband Newbie Guide.
+
+ Welcome to ToME, a popular descendant of Moria/Angband.
+ToME has many unique features that set it apart from other
+Angband/Moria descendants, with a nice mix of races/classes available.
+This guide will walk you, a player of ToME, through your first
+game (or two), introducing you to basic features that will help your
+character survive.
+
+ After you start the game for the first time, you will be
+prompted to create a new character. For this guide, we will start
+out with simple, easy to play characters. Go through the prompts,
+and select one of the following class/race combinations that will
+make for a simple first character:
+
+ Wood Elf (Classical) Archer
+ Half-Troll (Zombie) Unbeliever
+ Half-Ogre (Barbarian) Warrior
+
+After class/race selection, you will roll your statistics. Roll a few times
+until you get decent strength, constitution, and dexterity scores (the higher
+the better). After naming your character, you will appear in the
+town.
+
+ The town is a (relatively) safe place for you to buy/sell equipment,
+start quests, drop off items for storage, and various other things. Each store
+is represented by a symbol (either a number or a '+') on the side of a
+*****bldg.txt*0[building]. To enter a store, move over that symbol. You can also 'l'ook
+around to see the identity of stores and other inhabitants of the town. At
+this level, be wary of merchants and mercenaries -- they can probably kill you
+with little difficulty.
+
+ You will want to 'w'ield the weapons/armour you have started with
+(if you chose the archer, wield the bow, and then wield the arrows). You
+can check to see what you're wearing/wielding at any time by pressing
+'e'. Ideally, throughout the game you'll always have some kind of missile
+weapon and a decent melee weapon. Initially, purchasing a short bow and
+some plain arrows might be wise (archers start with a bow and some arrows,
+but you'll probably want to buy more ammo). Choice of a melee weapon is
+much more complex, but for now, unless you start with a good melee weapon
+(unbelievers do), you might want to consider buying a whip from the
+weapon store '3' (or the temple '4'). If available, purchase a lantern
+and 2 or 3 flasks of oil from the general store '1'. If you have some
+money left, buy some of the cheaper armour pieces from the armoury '2',
+such as boots, gloves, cap, and the like. You should now be ready to
+do some adventuring.
+
+ Just west of Bree are some steps into the first dungeon. Move on top
+of these stairs and hit '>' to go down. You will notice that when you arrive
+in the dungeon, you will be on top of some stairs going back up, and in the
+lower right, you will see how deep in the dungeon you are. When you need to
+return to town, head upstairs from the top level of the dungeon (which you are
+in now). Other stairs down will take you deeper in the dungeon. For now, stay
+on the first level of the dungeon, and fight some monsters to gain some
+experience. Be careful only to fight one monster at a time -- avoid fighting
+in open rooms when monsters can gang up on you. Also, avoid floating eyes
+'e', and 'l'ook at monsters before getting involved in combat with them.
+The 'C' button will bring up a screen telling you how many experience points
+you need to go to next level, and 'l'ooking at monsters will tell you how
+many experience points they are worth.
+
+ Once you go up a level, you'll want to invest some skill points so
+that your character improves. See the *****skills.txt*0[skills] help file for details.
+
+ Grab treasure, and when you are carrying close to as much as you can
+carry, head back to Bree and sell your loot. Buy phase door scrolls, teleport
+scrolls, and potions that will cure your wounds. Use any remaining money to
+buy identify scrolls. After a few trips, and reaching experience level 3, try
+going deeper in the dungeon before coming up.
+
+ Before you start going past the 8th or 9th level down, you will
+want some means of detecting traps, as traps begin to be a problem around
+that depth. Eventually, you'll want a rod of trap location (unless you are
+an unbeliever), but scrolls or staves will do until you find a rod and a
+rod tip of the right kind. These items, when used, will display all traps
+in a radius around you. This means that you'll want to use them again
+every time you run to the edge of that radius and into an area where you
+haven't yet detected traps.
+
+ Some kinds of items grant useful resistances or powers. You can
+see your current resistances and other stats by hitting 'C' and scrolling
+through the screens that come up. If you come across a Thunderlord coat
+early in the game, they make a good choice, as they grant resistance to
+fire and cold. Resistances reduce the damage attacks of certain kinds do,
+and often reduce the effectiveness of side-effects they have.
+
+ As you progress deeper in the dungeon, you will want to start using
+scrolls of recall to move to and from the dungeon. These scrolls, when
+read, schedule your return either to the surface or to the dungeon level
+you were last exploring. They do, however, take several turns to take effect,
+so reading them in an emergency will do little good.
+
+ Eventually, once you are around level 13 or 14, you will be ready
+to make trips to some of the other towns. The easiest town to get to is
+northeast of Bree; the others are more difficult to find and reach. When
+planning a trip, be sure to carry lots of food and/or scrolls of
+satisfy hunger. Multiple light sources may also be wise, and a bow is
+almost essential. To make long trips easier, ToME has a map mode
+specifically designed for travel. To use it, hit '<' in the wilderness.
+This will change the scale of the map, and make time go by at a much
+faster rate per square of movement (each square being many normal grid
+squares). Be sure to de-equip any exhaustible light sources before
+the trip, and never allow yourself to get past hungry while in this mode --
+drop down to the normal mode (with '>') if you get a message that you are
+hungry. Eventually, you will also make surface trips to find new dungeons
+to visit.
+
+#####REquipment notation:
+#####GA Weapon (3d4) (+3,+7)
+ This weapon, when you attack an enemy, does base damage calculated
+ by rolling a 4-sided die 3 times (adding the results up). It gets a
+ +3 to hit, and +7 to damage. If the weapon is light enough and
+ you are strong/dextrous enough, you can attack with it multiple times,
+ getting the base and extra damage once for each attack.
+
+#####GA Shield [5,+4]
+ This is a piece of armour, with a base defence of 5 and a magical
+ bonus of +4. If you wear it, your armour rating will increase by 9.
+
+#####GA Weapon (Defender) (1d6) (+12,+5) [+6] (+2 to stealth)
+ This is a special type of weapon, with special powers. The powers
+ depend on the type (there are more types than just Defender),
+ and some types can grant a variety of different powers. To get the
+ full info on this kind of weapon, you can either 'x'amine it if it's
+ being sold in a store, use certain kinds of magic (*Id*), or use
+ the (very expensive) services of certain stores.
+
+Many armours also have special powers.
+Some very powerful items are unique, and are called artifacts. They are
+normally indestructible, and will prove very useful in your quest.
diff --git a/lib/help/ability.txt b/lib/help/ability.txt
new file mode 100644
index 00000000..175d6745
--- /dev/null
+++ b/lib/help/ability.txt
@@ -0,0 +1,115 @@
+|||||oy
+~~~~~01|Abilities
+#####R=== ToME Abilities ===
+As well as spending your skill points on *****skills.txt*0[skills], you can also choose
+to spend them on abilities.
+Abilities are bought once, and then have a permanent effect. You cannot
+continue adding skill points into them, (which you can with *****skills.txt*0[skills]) as there
+would be no way that these abilities can improve. They are a "one-off
+purchase".
+As a consequence of this, they are often quite powerful abilities and can
+cost quite a few skill points to buy.
+Most abilities have a prerequisite skill or stat level which you must reach
+before being able to buy.
+
+For instance, you can invest in the *****ability.txt*03[Tree walking] ability. This will allow
+you to pass through dense undergrowth. In order to purchase this skill you must
+first have your *****skills.txt*34[Nature] skill at 20 or greater, and then it will cost you 7
+skill points to buy. This means you have to "save up" enough skill points in
+order to be able to afford this ability.
+
+You can access the Ability screen by pressing 'N' (in the original keyset, or
+'\N' in the roguelike keyset). Scroll up and down the abilities and then
+navigate right to buy selected ability.
+
+Adapt your character with skills and abilities in order to make it unique,
+playable, and maybe even powerful enough to win the game!
+
+Here follows a list of all the available abilities:
+
+*****ability.txt*02[Spread blows] *****ability.txt*03[Tree walking]
+*****ability.txt*04[Perfect casting] *****ability.txt*05[Extra Max Blow(1)]
+*****ability.txt*06[Extra Max Blow(2)] *****ability.txt*07[Ammo creation]
+*****ability.txt*08[Touch of death] *****ability.txt*09[Artifact Creation]
+*****ability.txt*10[Far reaching attack] *****ability.txt*11[Trapping]
+*****ability.txt*12[Undead Form]
+
+~~~~~02|Abilities|Spread blows
+[[[[[BSpread blows]
+If a monster dies to your attack but you still have blows left
+you won't lose the full turn, allowing you to attack some other
+monster in the same turn.
+#####UPrereq: Weaponmastery skill@30, Dex@17
+#####rCost: 5
+Warriors (of all types) gain this ability for free at character level 25.
+~~~~~03|Abilities|Tree walking
+[[[[[BTree walking]
+Allows you to walk in dense forest.
+#####UPrereq: Nature skill@20
+#####rCost: 7
+Ents and Wood Elves are born with this ability.
+~~~~~04|Abilities|Perfect casting
+[[[[[BPerfect casting]
+Allows you to reach 0% failure rate on spells.
+#####UPrereq: Magic skill@35
+#####rCost: 6
+Priests and Mages (of all types) are born with this ability.
+~~~~~05|Abilities|Extra Max Blow 1
+[[[[[BExtra Max Blow(1)]
+Increases your max possible blows number by 1.
+#####UPrereq: Combat skill@10
+#####rCost: 7
+Warriors (of all types) and Paladins are born with this ability.
+Rogues (of all types) gain this ability for free at character level 10.
+~~~~~06|Abilities|Extra Max Blow 2
+[[[[[BExtra Max Blow(2)]
+Increases your max possible blows number by 1 (Cumulative with
+Extra Max Blow(1)).
+#####UPrereq: Combat skill@20, Extra Max Blow(1)
+#####rCost: 7
+Warriors (of all types) are born with this ability.
+~~~~~07|Abilities|Ammo creation
+[[[[[BAmmo creation]
+Allows you to create shots, arrows and bolts from various materials.
+You can always make shots.
+At Archery level 10 you can start making arrows.
+At Archery level 20 you can start making bolts.
+#####UPrereq: Archery skill@10
+#####rCost: 8
+Archers (of all types) gain this ability for free at character level 2.
+~~~~~08|Abilities|Touch of death
+[[[[[BTouch of death]
+Your melee blows can insta-kill, but you only receive 1/3 of the experience
+for that kill.
+You must activate this from your 'm' menu.
+#####UPrereq: Necromancy skill@50, Combat skill@40, DEX@30, STR@30
+#####rCost: 15
+~~~~~09|Abilities|Artifact Creation
+[[[[[BArtifact Creation]
+In combination with a high alchemy skill this ability will let you
+design your very own artifacts.
+Prereq: Alchemy@40, INT@35, WIS@35
+#####rCost: 70
+~~~~~10|Abilities|Far reaching attack
+[[[[[BFar reaching attack]
+You can attack an enemy one square far using a long polearm.
+At high levels of Polearm-mastery skill, you can even hit two enemies at once.
+#####UPrereq: Combat@15, Polearm-mastery@15
+#####rCost: 10
+~~~~~11|Abilities|Trapping
+[[[[[BTrapping]
+Enables you to set traps which harm monsters.
+#####UPrereq: Disarming@15
+#####rCost: 10
+Rogues are born with this ability.
+~~~~~12|Abilities|Undead Form
+[[[[[BUndead Form]
+With this ability your character receives a small amount of Death Points (DP)
+when they 'die'. Every normal (+0 speed) turn, one DP
+is lost; you can regain it like you would do with Hit Points.
+If you manage to kill enough monsters before your DP goes below 0, your
+character is brought back to life.
+#####UPrereq: Necromancy@30, INT@25
+#####rCost: 15
+Necromancers gain this ability for free at character level 25.
+
diff --git a/lib/help/advanced.hlp b/lib/help/advanced.hlp
new file mode 100644
index 00000000..3f6fe4bd
--- /dev/null
+++ b/lib/help/advanced.hlp
@@ -0,0 +1,15 @@
+|||||oy
+#####RWelcome to the ToME Advanced Help System.
+#####R=============================================
+
+Please choose one of the following help files:
+
+ *****/aoption.txt*0[(a) Options]
+ *****/bmacrofaq.txt*0[(b) Macros]
+ *****/cautomat.txt*0[(c) Automatizer help]
+ (d) Lua scripting help - coming soon
+ *****/edebug.txt*0[(e) Debug commands]
+ *****/fversion.txt*0[(f) Version information] A history of ToME's roots
+
+ *****/zhelp.hlp*0[(z) Main Help menu]
+
diff --git a/lib/help/attack.txt b/lib/help/attack.txt
new file mode 100644
index 00000000..68fce965
--- /dev/null
+++ b/lib/help/attack.txt
@@ -0,0 +1,148 @@
+|||||oy
+~~~~~01|Attacking Monsters
+~~~~~02|Monsters|Attacking
+#####R=== Attacking and Being Attacked ===
+
+{{{{{<p>}Attacking is simple in ToME; attempting to move over a creature
+attacks it. It is also possible to attack from a distance by firing a missile
+or by magical means (wand, rod, spell, etc). Creatures attack in the same way.
+This means that if you do not wish to melee with a creature, it is wise to keep
+distant from it. This strategy is not perfect -- some monsters, such as
+dragons, have a way to attack from a distance.
+
+Some creatures, such as Ghosts, have the ability to pass through walls.
+To attack creatures (that you can see) who are currently in a wall,
+attempt to move over it (even if you cannot pass through walls, the
+attack will still happen). Monsters will take less damage from attacks
+while they are in walls. Also, if you cannot see the monster (e.g. if you
+are blind, or it is invisible and you do not have see invisible), this
+will not work: you'll have to try to tunnel. Most spells and magic devices
+cannot target monsters in walls. In situations like this, it is usually wise to
+lure the monster out of the wall before attacking it.
+
+Melee attacks are handled as such:
+#####G To-hit:
+ Your strength grants a bonus or penalty to-hit
+ Your class might grant a bonus or penalty to-hit
+ Your skill with specific kinds of weapons gives a bonus
+ to-hit (e.g.: Swordmastery gives a to-hit bonus
+ when using swords)
+ Enchantments on your weapon grant a bonus or penalty to-hit
+ Certain rings (accuracy, slaying) may also grants a bonus
+ or penalty to-hit
+ Your Tactic setting may give bonuses o penalties to-hit
+ (to change it, press 'C' and then 't' or 'T')
+ Other temporary effects (berserk, etc) might affect your
+ chance to hit
+ The armor rating of the monster you're attacking makes a
+ big difference
+
+#####G Blows:
+ Your strength and dexterity affect how many blows you get
+ Your class and level might affect how many blows you get
+ The weight of the weapon very likely will affect how many
+ blows you get
+ Certain skills (e.g. Swordmastery) may affect how many blows
+ you get with particular weapons
+ Enchantments on your weapon might also affect how many
+ blows you get (this is rare)
+ Certain very rare rings (of attacks) may also affect how
+ many blows you get
+
+#####G Damage:
+ The base damage of your weapon is rolled
+ Strength grants a bonus or penalty
+ Your class might grant a bonus or penalty
+ Combat and Weaponmastery skills increase your melee damage
+ Ranged masteries increase the damage multiplier of slings,
+ bows, crossbows or boomerangs(see below)
+ Enchantments on your weapon might also grant a bonus or
+ penalty
+ Your Tactic setting may give bonuses o penalties to damage
+ (to change it, press 'C' and then 't'or'T')
+ Temporary effects also might grant a bonus or penalty
+
+So, each blow you are entitled to is checked for a hit. If this is the case,
+the damage is then applied to the monster. Note that unless you have some
+barehanded combat training or are possessing a monster, melee without a weapon
+will result in a single blow that does base damage of 1d1. This might, however,
+be useful in attacking certain rare monsters that destroy weapons.
+
+Combat with a bow/sling is similar, except ammo is used (which will eventually
+run out, requiring replacement). Bows don't have a base damage rating (ammo
+does), instead having a damage multiplier. Bows can, however, be enchanted,
+and enchantment on bows and arrows is cumulative (meaning that well-enchanted
+bows and arrows can be one of the more effective weapons in the game. They
+do, however, tend to be very expensive, as non-artifact arrows frequently break
+after being fired).
+Using ammo without the appropriate bow generally has poor results.
+~~~~~03|Armor
+#####R=== Armor ===
+
+As the armor class of a monster greatly affects how hard it is for it
+to be hit, your armor class affects how hard it is for it to hit you.
+A high armor rating will make it much easier to survive deep in the dungeon.
+For a warrior style class (Unbelievers, Fighters, Archers, etc), it is
+generally wise to wear as much and as powerful armor as possible (subject
+to weight limitations, of course). Spellcasting classes, however, have
+limits on how much armor they can wear before it disrupts their motion
+and makes it hard for them to cast spells properly. For many of these classes,
+gloves are especially bad for spellcasting. Monks and Rogues skilled
+at dodging will often find heavy armor cumbersome, too.
+Armor has a base rating and an enchantment rating. The base rating is constant
+for the type of armor (e.g. paper armor always has a base rating of 4), and
+the enchantment depends on the item. There are also ways to further enchant
+armor you have. Certain very powerful enchantments grant resistances to
+specific forms of magical attacks.
+~~~~~04|Attacking Monsters|Resistances
+~~~~~05|Armor|Resistances
+#####R=== Resistances and typed attacks===
+Many kinds of monsters, traps, and other effects do damage that has a type.
+Types can have side effects in addition to the raw damage they deal. Certain
+enchanted items can grant resistances, reducing the raw damage and possibly
+reducing or eliminating the side effects. Some monsters also have resistances,
+so watching the messages when attacking a monster can often reveal that a
+particular attack is ineffective.
+~~~~~06|Attacking Monsters|Damage Effect type (Fire/cold/nether etc)
+~~~~~09|Damage Effects
+#####GLow attacks
+ Fire - Destroys weapons, armor, scrolls, and staves. Reduces strength.
+ Cold - Shatters potions.
+ Elec - Reduces dexterity, destroys rings and wands.
+ Acid - Reduces bonuses on equipped armor, reduces charisma.
+
+#####GMiddle attacks
+ Poison - Player becomes poisoned
+ Light - Blinds player, perma-lights area
+ Dark - Blinds player, darkens area
+ Confusion - Confuses player
+
+#####GHigh attacks
+ Nether - Drains experience
+ Nexus - Scrambles statistics, teleports player randomly
+ Disenchantment - Reduces bonuses on equipped items
+ Chaos - Confuses, drains life, causes hallucination, and more
+ Sound - Shatters potions
+ Shards - Cuts player
+
+#####GUnresistable attacks
+ Water - Stuns player
+ Ice - Stuns player, shatters potions
+ Plasma - Stuns player, otherwise same as fire attacks
+ Force - Pushes player a few squares back
+ Inertia - Slows player
+ Gravity - Slows and teleports player a few squares
+ Disintegration - Destroys items on ground, destroys walls
+ Mana - Destroys items on ground
+~~~~~07|Monsters|Monster Memory
+#####R=== Monster Memory ===
+
+The thousands of different creatures in ToME have many different
+characteristics, including spells, resistances, health, attacks, and speed.
+The information you have learned about each monster from your encounters
+with them is recorded in the monster memory (accessed with '/' or by 'l'ooking
+at a monster). It is possible to eventually learn all the characteristics
+of any given monster by interacting with them enough, but this is not always
+desirable (hanging around great hell wyrms, for example, can be hazardous
+to one's health). Certain spells may help you learn faster, as well as
+research centres in town.
diff --git a/lib/help/automat.txt b/lib/help/automat.txt
new file mode 100644
index 00000000..bf6478f8
--- /dev/null
+++ b/lib/help/automat.txt
@@ -0,0 +1,504 @@
+|||||oy
+~~~~~01|Automatizer
+~~~~~02|Auto pick-up
+~~~~~03|Auto destroy
+~~~~~04|Autosquelch
+#####R /----------------------------------------\
+#####R < The Automatizer >
+#####R \----------------------------------------/
+
+#####GWhat is the Automatizer?
+The automatizer is an advanced auto-pickup or auto-squelch (auto-destroyer). At
+a basic level, it will allow you to automatically destroy things that you have
+no use for once you walk over them, providing that you have identified one of
+them with your current character.
+
+#####GIs that it?
+Well no. The automatizer is far more flexible than that. The old-fashioned auto
+squelch allowed you to destroy things dependent on how they pseudo-id'd - you
+could auto-destroy all {cursed} swords for instance.
+This is fine to start with, but once you get deep in the dungeon, and have a lot
+of money and a decent weapon, you'll be interested in destroying {average} and
+{good} items too right? Well the automatizer allows you to define destruction of
+things providing you are of a certain level.
+
+#####GSounds quite cool, but wha...
+STOP RIGHT THERE! I haven't finished yet! Let's look at some other examples.
+Most of the time, scrolls of darkness are pretty useless. Unless you are a
+vampire, or an alchemist, right? So you might think it was no good to add
+auto-destroy of scrolls of darkness to the automatizer. But you'd be wrong, for
+you can add rules that are dependent on certain conditions, like that you are
+of a certain race, or class.
+
+#####GHey this is sounding good. What if it destroys my artifacts?
+It can't. Artifacts can never be destroyed, by the automatizer. However, watch
+out for items that are VERY cool but not artifacts... you wouldn't want to go
+destroying boots of speed would you? Fortunately there are provisions for this
+too, you could set a rule that destroyed all excellent boots (providing you were
+past character level 45 say), but not if they were boots of speed.
+
+#####GWoah! This sounds amazing!
+Yes it is, isn't it.
+
+#####GSo how do I use it?
+Well the very simplest way is as follows. When you hit 'k' (^D in the roguelike
+keyset) to destroy an item, you'll see that one option is "$ new automatizer
+rule(OFF)". If you want to destroy all future items like that that you find,
+then hit the $ key. You'll see that (OFF) changes to (ON). Now pick the item you
+want to destroy. Let's say it is a Potion of Salt Water. You would now, as
+normal, see a message "You destroy a Potion of Salt Water". Following this you
+get a prompt, "Destroy all of the same [T]ype, [F]amily, or [N]ame, also use
+[S]tatus (no)?". Let's take the easy one first and hit 'n' to go with name.
+You'll now see a message, "Rule added, please go to the automatizer screen
+(press = then T) to save the modified ruleset". Let's do that then, shall we. If
+this is the first time you have used the automatizer with this savefile, you'll
+be asked to enable it as you hit T. Confirm that you would like to enable it,
+otherwise it won't work.
+
+#####GWhat's the point in having it disabled then?
+Well let's say you spotted something that you weren't sure how it would be
+affected by your rules, but didn't want to destroy it; you could just disable
+the automatizer for a moment and check it over before deciding what to do with
+it and switching the automatizer back on.
+
+#####GHmmm, ok. So I've enabled the automatizer, now what?
+Well, you'll see a screen like this:
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|GsGaGlGtG GwGaGtGeGrB B B w B|G<GrGuGlGeB BnBaBmBew=w"ysyaylyty ywyaytyeyrw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G<GnGaGmGeG>wpwowtwiwownw wowfw wswawlwtw wwwawtwewrg<g/gngagmgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g<g/grguglgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW WtWoW WsWcWrWoWlWlW,W BtBaBbW WtWoW WsWwWiWtWcWhW WtWoW WtWhWeW WrWuWlWeW WwWiWnWdWoWww w w w w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BuW/BdW WtWoW WmWoWvWeW WrWuWlWeWsW,W BnWeWwW WrWuWlWeW,W BrWeWnWaWmWeW WrWuWlWeW,W BsWaWvWeW WrWuWlWeWsw w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w RkW WtWoW rdrirsrarbrlreW WtWhWeW WaWuWtWoWmWaWtWiWzWeWrw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+
+(snipped for brevity).
+
+The automatizer rules are written in XML, so if you're familiar with that format
+you shouldn't find things too difficult. It's also similar in format to HTML, as
+you may notice from the way that there are opening tags (<name>) and closing
+tags (</name>), and that everything between those tags is affected by that
+rule.
+
+#####GXML? HTML? What is this? Isn't ToME enough acronyms to be going on with?
+OK, so you don't know anything about mark-up languages, it's no problem. Let's
+take a look at the automatizer screen bit by bit, starting with the instructions
+bottom right.
+
+up/down to scroll. Well as you move the up/down cursor keys or number keys, a
+different rule name is highlighted in the left hand window, and that rule is
+displayed in the right hand window.
+tab to switch to the rule window. Does what it says on the tin: switches to the
+rule window (the right hand pane) to allow editing of existing rules.
+u/d to move rules. Pressing 'u' or 'd' will move the rule up or down the order
+displayed in the left hand window. Purely for cosmetic reasons.
+new rule. Adds the beginning of a new rule.
+rename rule. This is handy, as every rule added from the destroy prompt (as
+explained above) is automatically named 'destroy', which gets a bit confusing
+when you have 50 or so rules... renaming your rules will make them much easier
+to edit later.
+save rules. Saves all existing rules to a filename you designate (default is
+automat.atm). Don't forget to do this after adding new rules/before exiting the
+game.
+k to disable the automatizer. Disables the automatizer, preserving the rules you
+have made.
+
+#####GYeah yeah, so what about the rule window on the right hand side itself?
+OK, here we go. This is the real juicy stuff.
+
+Anything inside pointy brackets (greater than and less than signs) is called a
+tag. You'll notice that each tag opens with a word, and then that same word
+appears in another tag later on, preceded by a slash. These are referred to as
+opening tags (like <name> ) and closing tags (like </name> ). Everything within
+a set of closing and opening tags is affected by those tags. This will make more
+sense as we continue. Tags and the things they enclose are like "mini-rules"
+which will together make up one rule. I call these mini-rules, clauses.
+
+the first line:
+<rule name="destroy" type="destroy">
+Each rule starts with the tag <rule>, and this contains the rule name, and the
+rule type. The rule name is how it is identified in the left hand window, and
+need not be unique. The rule type will be either "destroy" (destroys items when
+conditions stated below are met) or "pickup" (picks up item when conditions are
+met) or "nothing" (neither picks up, nor destroys item when conditions are met)
+
+The second line:
+ <name>Potion of Salt Water</name>
+This tells us that for the rule to be carried out, the name of the item must be
+"Potion of Salt Water"
+
+The third line:
+</rule>
+This tells us the rule is ended.
+
+In total then, the rule named "destroy" checks to see if the name of every item
+is Potion of Salt water, and if it is, it destroys it.
+
+Nice and simple huh?
+
+#####GYes Yes, very simple. It doesn't look very advanced at the moment.
+Well, we've barely scratched the surface.
+Let's take a look at those other options we got at the destroy prompt. Let's say
+I was at a stage in the game where I wanted to be able to switch on an auto-
+destroy for all swords that pseudo-id'd as {average}. So let's say I have a
+dagger in my backpack, and I want to create an auto-destroy rule for that and
+all subsequent swords... This is what I'd do:
+Hit 'k' to destroy items and hit '$' to turn on automatizer rules. Then I'd
+select the dagger and confirm the destruction. Hit 's' to switch the status
+toggle. This toggle will include (when ON) how the dagger pseudo-ids, and
+finally hit 'f' to add a rule saying destroy by family. What you'll see in the
+automatizer screen now is this:
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|GdGeGsGtGrGoGyB B B B B B w B|G<GrGuGlGeB BnBaBmBew=w"ydyeysytyryoyyw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wdwewswtwrwowyB B B B B B w B|G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G G G G G<GtGvGaGlG>w2w3g<g/gtgvgaglg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G<GsGtGaGtGuGsG>wawvwewrwawgweg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g<g/grguglgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW WtWoW WsWcWrWoWlWlW,W BtBaBbW WtWoW WsWwWiWtWcWhW WtWoW WtWhWeW WrWuWlWeW WwWiWnWdWoWww w w w w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BuW/BdW WtWoW WmWoWvWeW WrWuWlWeWsW,W BnWeWwW WrWuWlWeW,W BrWeWnWaWmWeW WrWuWlWeW,W BsWaWvWeW WrWuWlWeWsw w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w RkW WtWoW rdrirsrarbrlreW WtWhWeW WaWuWtWoWmWaWtWiWzWeWrw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+#####GOK, well I can see the pseudo-id status is there, what's that tval thing? And
+#####Gwhere does it say swords?
+Well actually the tval thing is where it says swords! This is what is added by
+the 'destroy by family' option.
+
+It's probably worth explaining a bit here about the internal structure of ToME
+code. Don't worry it's not too scary. In ToME, all objects are divided into
+types: swords, axes, hafted-weapons, scrolls, rings etc. Every type is further
+divided into sub-types. Eg swords are divided into daggers, broad swords, two-
+handed swords and so on. Scrolls are divided into their specific actions;
+scrolls of light, scrolls of satisfy hunger etc. Each type has a number assigned
+to it, that never changes, and so does each sub-type. In this way we can
+identify an exact object using just two values: it's type, or tvalue (tval) and
+it's sub-type, or svalue (sval). You see where we're going with this?
+Now daggers have a tval of 23 and an sval of 4. So you can see that we've said
+in the rule that all things with a tval of 23 and a status of average can be
+destroyed.
+
+#####GAh, right. I see. And what's with the <and> tags?
+Well, everything in those tags must be true for the rule to carry out. If we had
+the <tval>23</tval> and the <status>average</average> lines without the <and>
+tags, it would not be clear whether we wanted just one of those clauses to be
+true for the rule to be carried out, or both of them.
+
+#####GErr...
+In other words, without the <and> tags it might look like we wanted to destroy
+a) EVERYTHING that pseudo id'd as average, and
+b) EVERY sword, regardless of how it pseudo-id'd!
+
+#####GRight. What if I did want a rule that was more general, and had either/or
+options in it?
+Then there are tags to do that - the <or> </or> tags. If ANY of the clauses
+return as true within or tags, then the rule is carried out. Substitute <or>
+tags for the <and> tags in our sword example above, and the rule will operate in
+the rather unhelpful way I explained above (all {average} things destroyed, and
+all swords destroyed, regardless of how they pseudo-id). So essentially, if your
+rule has more than one clause, you will need to include either <and> tags or
+<or> tags, or in some cases both.
+
+#####GOK and so what does the [T]ype option do?
+It merely adds the sval, thus narrowing down the parameters for the auto-
+destroy. For instance if I'd chosen to destroy by type rather than family in the
+last example, we'd have ended up with this:
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|GdGeGsGtGrGoGyB B B B B B w B|G<GrGuGlGeB BnBaBmBew=w"ydyeysytyryoyyw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wdwewswtwrwowyB B B B B B w B|G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G<GtGvGaGlG>w2w3g<g/gtgvgaglg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G<GsGvGaGlB BmBiBnw=w"y4w"B BmBaBxw=w"y4w"G>g<g/gsgvgaglg>B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G<GsGtGaGtGuGsG>wawvwewrwawgweg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g<g/grguglgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW WtWoW WsWcWrWoWlWlW,W BtBaBbW WtWoW WsWwWiWtWcWhW WtWoW WtWhWeW WrWuWlWeW WwWiWnWdWoWww w w w w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BuW/BdW WtWoW WmWoWvWeW WrWuWlWeWsW,W BnWeWwW WrWuWlWeW,W BrWeWnWaWmWeW WrWuWlWeW,W BsWaWvWeW WrWuWlWeWsw w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w RkW WtWoW rdrirsrarbrlreW WtWhWeW WaWuWtWoWmWaWtWiWzWeWrw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+You can see in this example that we have nested <and> tags. Evaluating the tags
+from the ones nested deepest first we can see that if an object has a tval of 23
+and an sval between the values of 4 and 4 (i.e. if it is 4) then the rule will
+check against the next part, which is to see if the object identifies {average}.
+If it does, then all the clauses have been met and rule is carried out. In
+short, it destroys all average daggers, rather than all average swords. Strictly
+speaking the nested <and> tags aren't needed, but they do no harm, and are added
+automatically when destroying by [T]ype.
+
+#####GThis is all well and good but the numbers are going to get rather confusing
+#####Garen't they? Be much easier if I could just write 'swords' or 'daggers'.
+Well, you can. Kind of. Instead of using the admittedly rather obtuse numbers,
+you can use the name of that tvalue rather than the number it represents. So
+instead of <tval>23</tval> you could write <tval>TV_SWORD</tval>.
+
+#####GAh that would be better. But where can I find out what all the names, and
+#####Gnumbers of tvalues are?
+Well I've written a *****defines.txt*0[file] which lists tvalues and one which lists svalues for you
+to check on, and you may want to check the objects entry in k_info.txt in your
+lib/edit directory. If you look at the entry for dagger you'll see:
+
+N:43:& Dagger~
+G:|:W
+I:23:4:0
+W:0:0:12:10
+A:0/1:5/1:10/1:20/1
+P:0:1d4:0:0:0
+<snip>
+The only line we're interested in is the one that starts I (for Index). The
+first number is the tval, and the second is the sval.
+Of course you could always rename your rule to make things clearer.
+
+#####GOK so you mentioned something about setting rules up that happen only if a
+#####Gplayer is a certain level?
+Yeah, good point. Well, let's develop our destroy average swords rule for the
+moment. Let's say we always wanted to destroy average swords by the time we got
+to character level 20. They don't earn enough gold to make it worth carrying
+back to town, and we'll have a better weapon by then anyway. So here we are in
+the automatizer screen, with the destroy sword rule displaying in the right hand
+window. If we hit tab, the right window becomes active and the rules at the
+bottom change:
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|gdgegsgtgrgogyB B B B B B w B|v<vrvuvlveB BnBaBmBew=w"ydyeysytyryoyyw"B BtByBpBew=w"ydyeysytyryoyyw"v>B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wdwewswtwrwowyB B B B B B w B|G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G G G G G<GtGvGaGlG>w2w3g<g/gtgvgaglg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G<GsGtGaGtGuGsG>wawvwewrwawgweg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|v<v/vrvuvlvev>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW/BlBeBfBtW/BrBiBgBhBtW WtWoW WnWaWvWiWtWaWgWeW WrWuWlWeW,W B9W/B3W/B7W/B1W WtWoW WsWcWrWoWlWlw w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BtBaBbW WfWoWrW WsWwWiWtWcWhW,W BaWdWdW WcWlWaWuWsWeW,W BdWeWlWeWtWeW WcWlWaWuWsWeW/WrWuWlWew w w w w w w w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+You can see the <rule> tags have changed to purple, indicating they are the
+active ones, that all key presses will act upon. Let's just quickly scoot
+through these new commands:
+up/down/left/right to navigate rule: These will change which clause is active,
+enabling you to delete that clause, or add further clauses inside that one if it
+so allows it.
+9/3/7/1 to scroll: scrolls the whole screen which can become useful if you have
+a particularly long rule or one with lots of nested clauses.
+tab for switch: will make the left-hand window active again.
+add clause: adds a new clause within the active one, providing the active clause
+is either a <and>, <or> or <not> tag.
+delete clause/rule: deletes the active clause, and any clauses nested within
+that one. Therefore if the <rule> tags are active, the whole rule will be
+deleted. Beware!
+
+So if we now hit our right arrow we see that the <and> tags are active. We can
+now add a new clause here. Let's do so by pressing 'a'. You'll now see a list of
+different types of clauses you can add, which you simply scroll through using up
+and down keys, and select by hitting enter. A brief description of the selected
+clause is shown in the right hand window. Scroll down to the 'level' rule type
+and hit enter. You'll be asked to enter a player level. Let's go for 20. You'll
+then be asked for a maximum level, put 50, as we want the rule to be true from
+level 20 and upwards. You'll now see our rule displays as:
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|gdgegsgtgrgogyB B B B B B w B|G<GrGuGlGeB BnBaBmBew=w"ydyeysytyryoyyw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wdwewswtwrwowyB B B B B B w B|v v v v v<vavnvdv>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G G G G G<GtGvGaGlG>w2w3g<g/gtgvgaglg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G<GsGtGaGtGuGsG>wawvwewrwawgweg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y2y0w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|v v v v v<v/vavnvdv>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g<g/grguglgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW/BlBeBfBtW/BrBiBgBhBtW WtWoW WnWaWvWiWtWaWgWeW WrWuWlWeW,W B9W/B3W/B7W/B1W WtWoW WsWcWrWoWlWlw w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BtBaBbW WfWoWrW WsWwWiWtWcWhW,W BaWdWdW WcWlWaWuWsWeW,W BdWeWlWeWtWeW WcWlWaWuWsWeW/WrWuWlWew w w w w w w w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+So there we go. A rule which will destroy all {average} swords once the player
+is past character level 20.
+
+#####GHmmm I've tried this, it doesn't destroy cursed swords though...
+Nope. You haven't told it to. If you want a scaling rule, which always destroys
+cursed swords and then destroys average swords at character level 20, then {good}
+swords at character level 35, have a look at this, rather more complicated rule.
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|wswhwiwewlwdB B B B B B B w B|G<GrGuGlGeB BnBaBmBew=w"ysywyoyrydysw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wbwowwB B B B B B B B B B w B|G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|whwrwdw wawrwmwowuwrB B B w B|G G G G G G G G G<GtGvGaGlG>w2w3g<g/gtgvgaglg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wawxwewsB B B B B B B B B w B|G G G G G G G G G<GoGrG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|whwewlwmwsB B B B B B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswowfwtw wawrwmB B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y0w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B B w B|
+&&&&&B|wbwowlwtwsB B B B B B B B w B|G G G G G G G G G G G G G G G G G<GoGrG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wbwowowtwsB B B B B B B B w B|G G G G G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wvwewrwyw wbwawdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wgwlwowvwewsB B B B B B B w B|G G G G G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wbwawdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wpwowlwewawrwmwsB B B B B w B|g g g g g g g g g g g g g g g g g<g/gogrg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wcwlwowawkB B B B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|GsGwGoGrGdGsB B B B B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|whwawfwtwewdwsB B B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y1y5w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B w B|
+&&&&&B|wawrwrwowwwsB B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wawvwewrwawgweg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswhwowtB B B B B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wbwowowmwewrwawnwgB B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wtwowrwcwhB B B B B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y2y5w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B w B|
+&&&&&B|wrwowdw wtwiwpwsB B B B B w B|G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wgwowowdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wpwowtwiwownwsB B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wrwiwnwgwsB B B B B B B B w B|g g g g g g g g g<g/gogrg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wwwawnwdwsB B B B B B B B w B|g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wmwuwswhwrwowowmwsB B B B w B|g<g/grguglgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswcwrwowlwlwsB B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wawmwuwlwewtwsB B B B B B w B|w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w B|
+&&&&&B|wpwawrwcwhwmwewnwtwsB B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|wswhwawpwew wpwowtB B B B w B|w BuBpW/BdBoBwBnW WtWoW WsWcWrWoWlWlW,W BtBaBbW WtWoW WsWwWiWtWcWhW WtWoW WtWhWeW WrWuWlWeW WwWiWnWdWoWww w w w w w w w w w w w w
+&&&&&B|wswtwawvwewsw w w w w w w w B|w BuW/BdW WtWoW WmWoWvWeW WrWuWlWeWsW,W BnWeWwW WrWuWlWeW,W BrWeWnWaWmWeW WrWuWlWeW,W BsWaWvWeW WrWuWlWeWsw w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w RkW WtWoW rdrirsrarbrlreW WtWhWeW WaWuWtWoWmWaWtWiWzWeWrw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+#####GWoah! What the heck is that?!
+OK ok slow down a minute. Look at it carefully and a clause at a time. It's not
+difficult. Let's take it from the top. It's a destroying rule called swords. It
+opens with some <and> tags. The object must be a sword (tval 23). Now we have an
+OR tag, so we know that there are going to be some options:
+option 1: the player is between level 0 and level 50, and the sword pseudo id's
+as either cursed or worthless.
+option 2: the player is above character level 15, and the sword pseudo id's as
+average.
+option 3: the player is above character level 25, and the sword pseudo id's as
+good.
+If any of these options are true, the rule is carried out. This rule could
+easily be copied for other weapons or pieces of armour, substituting only the
+tval, and perhaps the name.
+
+#####GHmmm ok. looks good. I might want to change those character level values
+#####Gthough. they look a bit low...
+Well yeah, that's up to your playing style I guess... If you want to edit
+clauses directly rather than delete and re-add them, you might want to edit the
+automat.atm file directly. Just open it in your text editor (it's located in
+your lib/user directory or in ~/.tome on multiuser systems) and you'll see the
+format is identical to how it is displayed in the automatizer file. Just watch
+your spellings, and keep a back-up of the original to replace if things go
+wrong. Don't mess about in there if you feel unsure of what you're doing though.
+
+#####GSo what about those examples you gave up above? Like destroying scrolls of
+#####Gdarkness unless you are a vampire or an alchemist?
+OK, let's add this rule from scratch rather than from the destroy item prompt.
+In the automatizer screen, hit 'n' for new rule. It asks for a name and a type
+of rule (destroy/pickup/nothing). I called my rule "? of darkness", and it's a
+destroy rule. It is worthwhile thinking at this point how the rule is going to
+be structured; for very complicated rules pen and paper may help. We're going to
+need to define the object (either by name, or by tval and sval) then we're going
+to say unless the player race modifier is vampire, or the player class is
+alchemist. So we are obviously going to have more than one clause, and the
+clauses are going to have to BOTH be true for the rule to take effect so the
+first clause we add is an <and> one. So hit 'a' for add clause and enter to
+select the <and> clause. Lets now hit our right arrow, so the <and> tags are
+active, and add a new rule. We can now select the <name> tags. At this point
+we're prompted for the name. Now it's not case-sensitive, but you do need to
+spell it correctly!
+With the <and> tags still highlighted we now want to add our "unless vampire or
+alchemist" clause. Seeing as this is an "unless" type clause, we start with a
+<not> tag. So again, 'a'dd a new clause, and select <not>.
+Now make the <not> tag active by moving right and down with the cursor keys. We
+want "EITHER vampire or alchemist" so we include <or> tags, again by moving
+across to make the <not> tags active and 'a'dding a new <or> clause. Then select
+the <or> tags and 'a'dd a new <subrace> clause. Enter 'vampire'.
+So the last part is to add alchemist. Now this might at first glance seem to be
+a simple case of 'a'dding a new clause of the <class> type, within the <or>
+tags, and there is certainly nothing 'wrong' with doing this. At present there
+is no way for non-alchemists to get the alchemy skill, but it is sometimes best
+to 'future-proof' against such possibilities. That's what we're going to do
+here.
+So rather than add a <class> clause, we're going to add a <skill> clause.
+With the <or> tags active, 'a'dd a new <skill> clause. You're first asked for a
+minimum skill level. Well, we can't use any skill unless we have 1.00 whole
+skill points in it or more, so we can put 1 in here. We're then asked for a
+maximum skill level, so we'll put in 50 here. The skill name must be spelled
+correctly (as it appears on the skill screen) so we put 'Alchemy' here. Your
+complete rule now appears as follows:
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|G?G GdGaGrGkGnGeGsGsB B B w B|G<GrGuGlGeB BnBaBmBew=w"y?y ydyayrykynyeysysw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswwwowrwdwsB B B B B B B w B|G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswawlwtw wwwawtwewrB B B w B|G G G G G G G G G<GnGaGmGeG>wSwcwrwowlwlw wowfw wdwawrwkwnwewswsg<g/gngagmgeg>B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G G G G G<GnGoGtG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G<GoGrG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGuGbGrGaGcGeG>wvwawmwpwiwrweg<g/gsgugbgrgagcgeg>B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGkGiGlGlB BmBiBnw=w"y1w"B BmBaBxw=w"y5y0w"G>wAwlwcwhwewmwyg<g/gsgkgiglglg>B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g<g/gogrg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g<g/gngogtg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g<g/grguglgeg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW WtWoW WsWcWrWoWlWlW,W BtBaBbW WtWoW WsWwWiWtWcWhW WtWoW WtWhWeW WrWuWlWeW WwWiWnWdWoWww w w w w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BuW/BdW WtWoW WmWoWvWeW WrWuWlWeWsW,W BnWeWwW WrWuWlWeW,W BrWeWnWaWmWeW WrWuWlWeW,W BsWaWvWeW WrWuWlWeWsw w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w RkW WtWoW rdrirsrarbrlreW WtWhWeW WaWuWtWoWmWaWtWiWzWeWrw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+You'll notice I've also renamed the rules to make things a bit more legible.
+
+#####GOK I'm getting there now, what about the boots of speed thing you talked about?
+Heh. Take a look at this, and see if you can work out what it's doing first
+before I talk you through it
+
+&&&&&B/B-B-B-B-BRBuBlBeBsB-B-B-B-B-B/B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B\
+&&&&&B|GbGoGoGtGsB B B B B B B B w B|G<GrGuGlGeB BnBaBmBew=w"ybyoyoytysw"B BtByBpBew=w"ydyeysytyryoyyw"G>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|w?w wdwawrwkwnwewswsB B B w B|G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswwwowrwdwsB B B B B B B w B|G G G G G G G G G<GtGvGaGlG>wTwVw_wBwOwOwTwSg<g/gtgvgaglg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wswawlwtw wwwawtwewrB B B w B|G G G G G G G G G<GoGrG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|wfwowowdB B B B B B B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y0w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GoGrG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wvwewrwyw wbwawdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wbwawdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g g g g g<g/gogrg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y2y0w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wawvwewrwawgweg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y3y5w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wgwowowdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G<GaGnGdG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GlGeGvGeGlB BmBiBnw=w"y4y5w"B BmBaBxw=w"y5y0w"G>g<g/glgegvgeglg>B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGtGaGtGuGsG>wvwewrwyw wgwowowdg<g/gsgtgagtgugsg>B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GsGtGaGtGeG>wiwdwewnwtwiwfwiwewdg<g/gsgtgagtgeg>B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G<GnGoGtG>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|G G G G G G G G G G G G G G G G G G G G G<GcGoGnGtGaGiGnG>wswpwewewdg<g/gcgogngtgagigng>B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g g g g g<g/gngogtg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g g g g g<g/gogrg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g g g g g<g/gagngdg>B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B w B|
+&&&&&B|B B B B B B B B B B B B B w B|g<g/grguglgeg>w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w B|
+&&&&&B|B B B B B B B B B B B B B w B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/
+&&&&&B|B B B B B B B B B B B B B w B|w BuBpW/BdBoBwBnW WtWoW WsWcWrWoWlWlW,W BtBaBbW WtWoW WsWwWiWtWcWhW WtWoW WtWhWeW WrWuWlWeW WwWiWnWdWoWww w w w w w w w w w w w w
+&&&&&B|w w w w w w w w w w w w w w B|w BuW/BdW WtWoW WmWoWvWeW WrWuWlWeWsW,W BnWeWwW WrWuWlWeW,W BrWeWnWaWmWeW WrWuWlWeW,W BsWaWvWeW WrWuWlWeWsw w w w w w w w w w w w
+&&&&&B\B-B-B-B-B-B-B-B-B-B-B-B-B-B-B/w RkW WtWoW rdrirsrarbrlreW WtWhWeW WaWuWtWoWmWaWtWiWzWeWrw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+The majority of this is fairly similar to the swords rule; it destroys {average}
+boots at character level 20, {good} ones at character level 35 and cursed ones
+always. The addition is at the bottom. It adds clauses which will destroy
+excellent items providing that they have been identified and that the name does
+not contain the word "speed". The <state>identified</state> bit is important
+here, as it would destroy the boots on pseudo-id otherwise, before it even saw
+the full name of the boots.
+
+#####GThis is fantastic! Where can I get more help?
+email me: fearoffours@t-o-m-e.net
diff --git a/lib/help/birth.txt b/lib/help/birth.txt
new file mode 100644
index 00000000..00b1a921
--- /dev/null
+++ b/lib/help/birth.txt
@@ -0,0 +1,593 @@
+|||||oy
+~~~~~84|Birth
+~~~~~11|Character
+#####R /----------------------------------------\
+#####R < Creating a Character >
+#####R \----------------------------------------/
+
+ *****birth.txt*01[Creating a Character] *****birth.txt*02[Character Characteristics]
+ *****birth.txt*03[Races] *****birth.txt*04[Race Modifiers]
+ *****birth.txt*05[Classes] *****birth.txt*83[Gods]
+ *****birth.txt*06[Stats] *****birth.txt*07[Abilities]
+ *****birth.txt*08[Combinations of Race and Class] *****birth.txt*09[Stat bonus tables]
+
+~~~~~01|Character|Creating a Character
+~~~~~12|Creating a Character
+#####R=== Creating a Character ===
+
+ToME is a roleplaying game, in which you, the player, control a
+character in the various dungeons and places of Arda. Perhaps the most
+important thing you control is the birth of your character, in which you
+choose or allow to be chosen various attributes that will affect the future
+life of your character.
+
+Character creation, or birth, is controlled through a variety of choices
+as to constraints on the type of character you wish to play, followed by
+a series of random calculations to generate ("roll up") a random character
+matching the appropriate constraints.
+
+Once your character has been generated, you will be given the choice to
+generate a new character obeying the same constraints, and once you have
+generated more than one character, you can switch back and forth between
+the two most recent characters, until you are presented with a personality
+that you feel comfortable with.
+
+You may start the entire process over at any time.
+
+Once you have accepted a character you will asked to provide a name for the
+character. In general, the actual choice of a name is not important, but do
+keep in mind that it may have some effect on the game itself. For example,
+on some machines, the character name determines the filename that will be
+used to save the character to disk. On others, the character name specifies
+special "pref" files. And the character name is used on the high score list.
+
+~~~~~02|Character|Characteristics
+~~~~~13|Character|Stats 1
+~~~~~14|Stats|Display
+~~~~~37|Display
+~~~~~82|Stats
+#####R=== Character Characteristics ===
+
+Each character has four primary attributes -- gender, race, race modifier and
+class -- that are chosen before the character is generated, and all but gender
+stay fixed for the entire life of that character. These attributes have many
+effects, which will be mentioned as they come up. Keep in mind that in the
+current version of ToME, your choice of race may restrict your choice
+of class.
+
+Each character has a few secondary attributes -- height, weight, social class,
+and background history -- which are randomly determined, but which are affected
+by the gender and race of the character. In general, these attributes are only
+used to provide "flavor" to the character, to assist in the role playing, but
+they do have a few minor effects on the game. For example, background history
+affects social class, which affects the amount of money the character will
+start with.
+
+Each character also has six primary "stats": strength (STR), intelligence
+(INT), wisdom (WIS), dexterity (DEX), constitution (CON), and charisma (CHR).
+
+By default, primary statistics are represented in a linear way rather than a
+percentile way. You can change to percentile via the game option sequence '=',
+'5' (ToME options), arrow down to 'stats are represented in a linear way', and
+type 'n' for no. Don't forget to save your options when you are done.
+
+These stats modify the abilities of the character in a variety of ways. Every
+stat has a numerical value, ranging from a minimum of 3, up to a normal maximum
+of 18, and even higher, into the "percentile" range, represented as "18/01"
+through "18/100". Actually, every stat can be raised even above 18/100 by
+magical means, up to a pure maximum of 18/220, which is represented as
+"18/***". Traditionally, a percentile stat such as "18/50" has been though of
+as representing a value part way between 18 and 19, and this is one way to
+think of them. However, often, the best way to view the "bonus" values after
+the "18/" is as "tenth" points, since it often takes the same magic to raise a
+stat from, say, 4 to 5, or 16 to 17, as it does from, say, 18/40 to 18/50. The
+important thing to remember is that almost all internal calculations "ignore"
+the final digit of any "bonus", so that, for example, "18/40" and "18/49"
+generally have the same effects. During character generation, each stat is
+rolled out as a number from 8 to 17, with a normal (bell-curve) distribution,
+and is then immediately modified based on the race and class of the character.
+
+Each character also has several primary "skills" -- disarming, magic devices,
+saving throws, stealth, searching ability, searching frequency, fighting skill,
+and shooting skill -- which are derived from the character's race, class, level,
+stats, and current equipment. These skills have rather obvious effects, but
+will be described more completely below.
+
+Each character may have one or more "intrinsic racial skills", based on
+the race of the character. These may include special resistances, abilities
+such as infravision, or even activatable powers such as food creation.
+~~~~~16|Gold
+~~~~~81|Abbreviations
+~~~~~17|Abbreviations|AU
+~~~~~18|Money
+Money in ToME is referred to in gold pieces, also frequently abbreviated as AU
+(the chemical symbol for gold).
+
+Each character starts with some gold, which can be used to buy items from the
+shops in town. Additionally, gold can be obtained by several means:
+
+ * selling items you have, or find, to the shops
+ * taking it from dead monsters
+ * finding it lying around on the floor in the dungeon
+ * digging it out of the walls in certain dungeons
+
+Each character starts out with some gold. The amount you start with is based on
+social class (higher is better), charisma (higher is better), and some other
+stats (less powerful characters start with more gold).
+~~~~~85|Inventory - starting info
+Inventory is what you are carrying and/or wearing at the moment. All items you
+carry/wear have a certain weight. If the weight is very heavy for your
+strength, it can slow you down, and that is not a good thing for your
+character's continuing health. When your character is first created, all items
+you are granted upon creation will be in your inventory. Sometimes starting
+inventory includes something other than a weapon or armor (for example, a light
+source, scroll, potion, or food.)
+~~~~~86|Weapons - starting info
+Some characters start with a weapon. If yours does, you will need to 'w'ield it
+in order to gain its attack capabilities.
+~~~~~23|Character|Armor Class
+~~~~~19|Armor|Armor Class
+~~~~~20|Abbreviations|AC
+#####R === Armour Class ===
+Each character has an armor class, representing how well the character can
+avoid damage. The armor class is affected by dexterity, the equipment, and
+sometimes the race. The higher the AC, the better.
+
+The numbers following a piece of armor's name indicate how good it is. A Metal
+Cap [3,+0] is not as good as an Iron Helm [5,+0], AC-wise, since the 5 is a
+bigger number than the 3. On the other hand, an Iron Helm is heavier than a
+Metal Cap, and that may make a difference to you.
+
+The plusses following the first number (e.g. the +0) indicate a magical bonus.
+If the plus number is more than zero, it should be added on to the base
+number. E.g. a Metal Cap [3,+3] has a higher AC than an Iron Helm [5,+0].
+
+Some characters start with armor. If yours does, you need will need to 'w'ield
+it in order to gain its protection.
+~~~~~21|Abbreviations|HP
+~~~~~22|Character|Hit Points
+Each character has hit points (HP), representing how much damage the character
+can sustain before death. Hit points are derived from your race, class,
+level, and constitution, and can be temporarily boosted by magical means.
+Hit points may be regained by resting, or by a variety of magical means.
+~~~~~24|Abbreviations|SN
+~~~~~25|Character|Sanity Points
+Each character has sanity points (SN), representing how much mental damage the
+character can sustain before death. Sanity points are derived from your wisdom
+and character level. Sanity points may only be regained by magical means and
+won't be regained by resting.
+~~~~~26|Abbreviations|SP
+~~~~~27|Magic|Mana
+~~~~~28|Character|Mana
+Each character has a certain amount of mana. The amount of mana represents how
+many spells of a certain difficulty a character can cast. When a spell is
+cast, you lose amount of mana corresponding to the 'cost' of the spell.
+
+When all mana is gone, or the cost of a given spell is greater than the amount
+of mana you have left, you may attempt to cast a spell; beware, as there are
+consequences to such a rash act.
+
+Spell points may be regained by resting. They can also be restored by a few
+magical means.
+
+Your spell points are derived from your Magic skill, player level and the
+greatest of INT and WIS.
+
+Your total spell points are additionally affected by:
+
+ * your character's race modifier
+ * character class
+
+Your total spell points may be affected by:
+
+ * your encumbrance
+ * what you wear
+
+~~~~~29|Abbreviations|Pt
+~~~~~30|Gods|Piety
+~~~~~31|Character|Piety
+Finally, characters that have chosen to follow a *****gods.txt*0[God] will have piety points
+(Pt). These points represent the character's standing with their God, a
+standing which may rise or fall over time. A character may spend piety points
+to cast a spell granted by his/her God. Spent piety points can be regained in
+different fashions, depending on which God is involved -- each is pleased or
+displeased by assorted actions. Pleasing your God gains you piety, while
+displeasing your god will lose you piety.
+
+In addition to forming the basis for God-granted spells, accumulated piety
+points may also confer various benefits or penalties upon your character,
+depending on the God involved. For example, followers of Eru who have
+accmulated lots of piety points gain a WIS bonus. The actual rules are
+quite specific to each God.
+~~~~~03|Races
+#####R=== Races ===
+
+There are lots different races that you can choose from in ToME. Some
+races are restricted as to what profession they may be, and each race has
+its own adjustments to a character's stats and abilities. Most races also
+have intrinsic abilities, which can be accessed via the "U" command (original
+keyset, or "O" in the roguelike keyset).
+
+ *****r_beorn.txt*0[Beorning] *****r_hafelf.txt*0[Half-Elf] *****r_orc.txt*0[Orc]
+ *****r_drkelf.txt*0[Dark Elf] *****r_hafogr.txt*0[Half-Ogre] *****r_pettyd.txt*0[Petty-Dwarf]
+ *****r_deathm.txt*0[Death Mold] *****r_hielf.txt*0[High-Elf] *****r_rohank.txt*0[Rohan Knight]
+ *****r_dunad.txt*0[Dunadan] *****r_hobbit.txt*0[Hobbit] *****r_thlord.txt*0[Thunderlord]
+ *****r_dwarf.txt*0[Dwarf] *****r_human.txt*0[Human] *****r_troll.txt*0[Troll]
+ *****r_elf.txt*0[Elf] *****r_kobold.txt*0[Kobold] *****r_wodelf.txt*0[Wood Elf]
+ *****r_ent.txt*0[Ent] *****r_maia.txt*0[Maia] *****r_yeek.txt*0[Yeek]
+ *****r_gnome.txt*0[Gnome]
+
+~~~~~04|Race Modifiers
+#####R=== Race Modifiers ===
+
+There are many different race modifiers from which you can choose in ToME.
+Some are restricted as to what race they can be used with, and each one has
+its own adjustments to a character's stats and abilities. Most also have
+intrinsic abilities. If you are not asked for a race modifier, it is because
+your race only supports the classical form.
+
+ *****rm_class.txt*0[Classical] *****rm_barb.txt*0[Barbarian]
+ *****rm_herm.txt*0[Hermit] *****rm_lsoul.txt*0[Lost Soul]
+ *****rm_skel.txt*0[Skeleton] *****rm_spec.txt*0[Spectre]
+ *****rm_vamp.txt*0[Vampire] *****rm_zomb.txt*0[Zombie]
+
+~~~~~05|Classes
+#####R=== Classes ===
+
+Once a race has been chosen, you will need to pick a class. Some classes will
+not be available to certain races, for instance, a Troll cannot become a
+Paladin. For the first few adventures it is suggested that you run a warrior
+or rogue. Spell casting generally requires a more experienced player that is
+familiar with survival techniques.
+
+ *****c_alchem.txt*0[Alchemist] *****c_mage.txt*0[Mage] *****c_rogue.txt*0[Rogue]
+ *****c_archer.txt*0[Archer] *****c_mimic.txt*0[Mimic] *****c_runecr.txt*0[Runecrafter]
+ *****c_assass.txt*0[Assassin] *****c_mindcr.txt*0[Mindcrafter] *****c_sorcer.txt*0[Sorceror]
+ *****c_axemas.txt*0[Axemaster] *****c_monk.txt*0[Monk] *****c_summon.txt*0[Summoner]
+ *****c_bard.txt*0[Bard] *****c_necro.txt*0[Necromancer] *****c_swordm.txt*0[Swordmaster]
+ *****c_pr_drk.txt*0[Dark-Priest] *****c_palad.txt*0[Paladin] *****c_symbia.txt*0[Symbiant]
+ *****c_demono.txt*0[Demonologist] *****c_polear.txt*0[Polearmmaster] *****c_thaum.txt*0[Thaumaturgist]
+ *****c_druid.txt*0[Druid] *****c_posses.txt*0[Possessor] *****c_unbel.txt*0[Unbeliever]
+ *****c_geoman.txt*0[Geomancer] *****c_pr_eru.txt*0[Priest(Eru)] *****c_warper.txt*0[Warper]
+ *****c_hafted.txt*0[Haftedmaster] *****c_pr_man.txt*0[Priest(Manwe)] *****c_warrio.txt*0[Warrior]
+ *****c_lorema.txt*0[Loremaster] *****c_ranger.txt*0[Ranger]
+~~~~~83
+#####R=== Gods ===
+
+Once a class has been chosen you may be given the option to choose a god. Some
+classes (notably most Priests) will be given a God to worship automatically.
+Some classes are not necessarily suited to following a God, and Gods are not
+recommended for new players. You can also choose your mind about who (if
+anyone) to worship during the game by finding an *****tome_faq.txt*04[altar] of that God.
+
+Read *****gods.txt*0[gods.txt] for a little more information about Gods.
+
+ *****g_eru.txt*0[Eru Iluvatar] *****g_manwe.txt*0[Manwe Sulimo]
+ *****g_yavann.txt*0[Yavanna Kementari] *****g_tulkas.txt*0[Tulkas]
+ *****g_melkor.txt*0[Melkor Bauglir]
+
+
+~~~~~39|Character|Stats 2
+~~~~~06|Stats|Individual explanations
+#####R=== Stats ===
+~~~~~32|Stats|Strength
+~~~~~34|Strength
+#####G Strength (STR)
+ Strength is important in fighting with weapons and in melee
+ combat. A high strength can improve your chances of hitting
+ as well as the amount of damage done with each hit. Char-
+ acters with low strengths may receive penalties. Strength
+ is also useful in tunnelling and in carrying heavy items.
+~~~~~33|Stats|Intelligence
+~~~~~35|Intelligence
+#####G Intelligence (INT)
+ Intelligence affects the spellcasting abilities of mage-like
+ spell schools (whether these spells are learned directly through
+ their associated skills, or indirectly through the Prayer
+ skill), as well as some of the special abilities of various
+ classes (e.g. Symbiants). Intelligence will affect the number
+ of spell points you receive. A high intelligence may also
+ improve your chances of successfully casting a spell. You cannot
+ learn spells if your intelligence is 7 or lower. A good
+ intelligence can also help with using magic devices, picking
+ locks, and disarming traps.
+~~~~~36|Stats|Wisdom
+~~~~~38|Wisdom
+#####G Wisdom (WIS)
+ The primary function of wisdom is to determine the ability
+ of a priest or paladin to use prayers (God-granted spells), just
+ like intelligence affects mage spells. Again, high wisdom will
+ increase the number of spell points you have (even though
+ prayers use piety points), and will improve the chance that a
+ prayer will be successful. A good wisdom can also help to
+ improve your chances of resisting magical spells cast
+ upon you by monsters.
+~~~~~40|Stats|Dexterity
+~~~~~41|Dexterity
+#####G Dexterity (DEX)
+ Dexterity is a combination of agility and quickness. A high
+ dexterity may allow your character to get multiple blows with
+ lighter weapons, thus greatly increasing your kill power, and
+ will increase your chances of hitting with any weapon and
+ dodging blows from enemies. Dexterity is also useful in
+ picking locks, disarming traps, and protecting yourself from
+ some of the thieves that inhabit the dungeons. The unscrupulous
+ adventurer may also find dexterity effective in obtaining items
+ from stores without rendering payment.
+~~~~~42|Stats|Constitution
+~~~~~43|Constitution
+#####G Constitution (CON)
+ Constitution is a character's ability to resist damage to his
+ body, and to recover from damage received. Therefore a
+ character with a high constitution will receive more hit
+ points and also recover them faster while resting.
+~~~~~44|Stats|Charisma
+~~~~~45|Charisma
+#####G Charisma (CHR)
+ Charisma represents a character's personality and physical
+ appearance. A character with a high charisma will receive
+ better prices from store owners, whereas a character with a
+ very low charisma may be robbed blind. A high charisma will
+ also mean more starting money for the character.
+~~~~~07
+~~~~~46|Character|Abilities
+#####R=== Abilities ===
+
+ Characters possess some different abilities which can help them
+ to survive. The starting abilities of a character are based upon
+ race and class. Abilities may be adjusted by high or low stats,
+ and increase with the corresponding *****skills.txt*0[skill] level.
+~~~~~48|Attacking monsters|Fighting ability
+#####G Fighting
+ Fighting is the ability to hit and do damage with weapons or
+ fists. Normally a character gets a single blow from any
+ weapon, but if his dexterity and strength are high enough,
+ he may receive more blows per round with lighter weapons.
+ Strength and dexterity both modify the ability to hit an
+ opponent. This skill increases with the *****skills.txt*02[Weaponmastery] skill
+ and its sub-skills.
+~~~~~50|Attacking monsters|Shooting
+#####G Shooting Ability (Bows/Throw)
+ Using ranged missile weapons (and throwing objects) is
+ included in this skill. Different stats apply to different
+ weapons, but this ability may modify the distance an object
+ is thrown/fired, the amount of damage done, and the ability
+ to hit a creature. This skill increases with the *****skills.txt*08[Archery] skill
+ and its sub-skills.
+~~~~~52|Saving throw
+#####G Saving Throw
+ A Saving Throw is the ability of a character to resist the
+ effects of a spell cast on him by another person/creature.
+ This does not include spells cast on the player by his own
+ stupidity, such as quaffing a nasty potion. This ability
+ increases with the *****skills.txt*38[Spirituality] skill,
+ A high wisdom also increases this ability.
+~~~~~54|Stealth
+#####G Stealth
+ The ability to move silently about is very useful. Charac-
+ ters with good stealth can usually surprise their opponents,
+ gaining the first blow. Also, creatures may fail to notice
+ a stealthy character entirely, allowing a player to avoid
+ certain fights. This skill is based upon race, class and the
+ *****skills.txt*15[Stealth] skill.
+~~~~~56|Disarming traps
+#####G Disarming
+ Disarming is the ability to remove traps (safely), and
+ includes picking locks on traps and doors. A successful
+ disarming will gain the character some experience. A trap
+ must be found before it can be disarmed. Dexterity and
+ intelligence both modify the ability to disarm, and this
+ ability increases with the *****skills.txt*16[Disarming] skill.
+~~~~~58|Magical Devices
+#####G Magic Device
+ Using a magical device such as a wand or staff requires
+ experience and knowledge. Spell users such as magi and
+ priests are therefore much better at using a magical device
+ than say a warrior. This skill is modified by intelligence,
+ and increases with the *****skills.txt*54[Magic-Device] skill.
+~~~~~60|Searching
+~~~~~61|Searching|Searching Frequency - Perception
+~~~~~62|Perception
+#####G Searching Frequency (Perception)
+ Perception is the ability to notice something without
+ actively seeking it out. This skill is based upon race,
+ class and the *****skills.txt*14[Sneakiness] skill.
+~~~~~63|Searching|Searching Ability
+#####G Searching Ability (Searching)
+ To search is to actively look for secret doors, floor traps,
+ and traps on chests. Rogues are the best at searching, but
+ magi, rangers, and priests are also good at it. This skill
+ is based upon race, class and the *****skills.txt*14[Sneakiness] skill.
+~~~~~66|Infra-vision
+#####G Infra-vision
+ Infra-vision is the ability to see heat sources. Since most
+ of the dungeon is cool or cold, infra-vision will not allow
+ the player to see walls and objects. Infra-vision will allow
+ a character to see any warm-blooded creatures up to a cer-
+ tain distance. This ability works equally well with or with
+ out a light source. The majority of ToME's creatures are
+ cold-blooded, and will not be detected unless lit up by a
+ light source. Most non-human races have innate infra-vision
+ ability. Humans can gain infra-vision only through magic
+ enhancement.
+
+~~~~~08|Character|Race and Class Combinations
+~~~~~67|Races|Combinations with class
+~~~~~68|Classes|Combinations with Race
+~~~~~69|Tables
+~~~~~70|Tables|Combinations of Race and Class
+#####R=== Combinations of Race and Class ===
+
+These are the classes that are recommended for different races. You can
+still select a race that is not in the chart, but these combinations are
+either rather poor (like a zombie mage), a concept so silly that they
+are not recommended, or an incredibly unfair combination of race and class.
+If you pick a combination that is not on the chart, don't complain if things
+don't turn out as you expected them to.
+
+#####B Warrior Archer Rogue Mage Priest Loremaster
+#####B
+Beorning Yes No Yes No No Yes
+Dark Elf Yes Yes Yes Yes Yes No
+Death Mold No No No Yes Yes No
+Dunadan Yes Yes Yes Yes Yes Yes
+Dwarf Yes No No No Yes No
+Elf Yes Yes No Yes Yes Yes
+Ent Yes No No No Yes Yes
+Gnome Yes No Yes Yes No No
+Half-Elf Yes Yes Yes Yes Yes Yes
+Half-Ogre Yes No No No Yes No
+High-Elf Yes Yes No Yes Yes Yes
+Hobbit Yes Yes Yes Yes No Yes
+Human Yes Yes Yes Yes Yes Yes
+Kobold Yes Yes Yes No No No
+Maia Yes Yes Yes Yes Yes Yes
+Orc Yes Yes Yes No Yes No
+Petty Dwarf Yes No Yes No No No
+RohanKnight Yes No No No Yes No
+Thunderlord Yes Yes No Yes Yes No
+Troll Yes No No No No No
+Wood Elf Yes Yes No Yes Yes Yes
+Yeek Yes Yes Yes Yes Yes Yes
+~~~~~09|Character|Stat Bonus Table
+~~~~~71|Stats|Bonus table
+~~~~~72|Tables|Stat bonuses
+#####R=== Stat Bonus Tables ===
+
+Stat, hit dice, and experience points per level modifications due to race
+are listed in the following table.
+~~~~~75|Races|Stat Bonuses
+#####GRaces:
+#####B STR INT WIS DEX CON CHR Hit Dice Rqd Exp/level
+ Beorning +4 -2 -2 -1 +3 -5 12 +50%
+ Dark Elf -1 +3 +2 +2 -2 +1 9 +50%
+ Death Mold +10 0 +10 +0 +10 -15 15 +150%
+ Dunadan +1 +2 +2 +2 +3 +2 10 +80%
+ Dwarf +2 -2 +2 -2 +2 -3 11 +25%
+ Elf -1 +2 +2 +1 -2 +2 8 +20%
+ Ent +10 -3 +2 -5 +11 -3 14 +110%
+ Gnome -1 +2 0 +2 +1 -2 8 +35%
+ Half-Elf 0 +1 +1 +1 -1 +1 9 +10%
+ Half-Ogre +3 -1 -1 -1 +3 -3 12 +30%
+ High-Elf +1 +3 +2 +3 +1 +5 10 +100%
+ Hobbit -2 +2 +1 +3 +2 +1 7 +10%
+ Human 0 0 0 0 0 0 10 +0%
+ Kobold +1 -1 0 +1 0 -4 9 +25%
+ Maia 0 0 0 0 0 0 10 +0%
+ Petty Dwarf +1 -1 +2 0 +2 -4 11 +35%
+ Orc +2 -1 0 +1 +1 -4 10 +10%
+ RohanKnight +4 -2 +3 +1 +4 +2 10 +120%
+ Thunderlord +6 +2 +1 +1 +3 +8 12 +300%
+ Troll +4 -4 -2 -4 +3 -6 12 +37%
+ Wood Elf -3 +2 +1 +5 -4 +1 7 +30%
+ Yeek -5 -5 -5 -5 -5 -5 6 -75%
+~~~~~76|Race Modifiers|Stat Bonuses
+#####GRace Modifiers:
+#####B STR INT WIS DEX CON CHR Hit Dice Rqd Exp/level
+ Classical 0 0 0 0 0 0 +0 +0%
+ Barbarian +2 -3 -2 +1 +1 -3 +1 +25%
+ Hermit -3 +1 +1 -3 -3 +1 -3 +20%
+ Lost Soul 0 0 0 0 0 0 +0 +0%
+ Skeleton 0 -2 -2 0 +1 -4 +0 +45%
+ Spectre -5 +2 +2 +2 -3 -6 -4 +80%
+ Vampire +3 +2 -3 -2 +1 -4 +1 +100%
+ Zombie +2 -6 -6 +1 +4 -5 +3 +45%
+
+~~~~~77|Classes|Stat Bonuses
+#####GClasses:
+#####B STR INT WIS DEX CON CHR
+ Axemaster +5 -2 -2 +2 +2 -1
+ Demonologist +5 -2 -2 +2 +2 -1
+ Haftedmaster +5 -2 -2 +2 +2 -1
+ Polearmmaster +5 -2 -2 +2 +2 -1
+ Swordmaster +5 -2 -2 +2 +2 -1
+ Unbeliever +5 -2 -2 +2 +2 -1
+ Warrior +5 -2 -2 +2 +2 -1
+
+ Alchemist -5 +3 0 +1 -2 +1
+ Geomancer -5 +3 0 +1 -2 +1
+ Mage -5 +3 0 +1 -2 +1
+ Necromancer -5 +3 0 +1 -2 +1
+ Runecrafter -5 +3 0 +1 -2 +1
+ Sorceror -5 +3 0 +1 -2 +1
+ Thaumaturgist -5 +3 0 +1 -2 +1
+ Warper -5 +3 0 +1 -2 +1
+
+ Archer +2 +1 0 +2 +1 +1
+ Ranger +2 +2 0 +2 +1 +1
+
+ Assassin +2 +1 -2 +3 +1 -1
+ Rogue +2 +1 -2 +3 +1 -1
+
+ Bard +1 -2 +1 +1 0 +1
+ Loremaster +1 -2 +1 +1 0 +1
+ Mimic +1 -2 +1 +1 0 +1
+ Monk +1 -2 +1 +1 0 +1
+ Possessor +1 -2 +1 +1 0 +1
+ Summoner +1 -2 +1 +1 0 +1
+ Symbiant +1 -2 +1 +1 0 +1
+
+ Dark-Priest -1 -3 +3 -1 0 +2
+ Druid -1 -3 +3 -1 0 +2
+ Mindcrafter -1 -3 +3 -1 0 +2
+ Paladin -1 -3 +3 -1 0 +2
+ Priest(Eru) -1 -3 +3 -1 0 +2
+ Priest(Manwe) -1 -3 +3 -1 0 +2
+~~~~~10|Character|Skill tables
+~~~~~74|Tables|Skill Tables
+#####R=== Skill Tables ===
+
+~~~~~78|Races|Skill table
+#####GRaces:
+#####B Disarm Devices Sprtlty Stealth Sneak Wepnmas Archery
+ Beorning -0.6 -0.8 -3.0 -2.0 -0.1 +2.5 +0.5
+ Dark-Elf +0.5 +1.5 +10.0 +3.0 +0.8 -0.5 +1.0
+ DeathMold +1.5 -0.5 +7.5 +25 0 +2.5 +2.5
+ Dunadan +0.4 +0.5 +2.5 +2.0 +0.8 +1.5 +1.0
+ Dwarf +0.2 +0.9 +5.0 -1.0 +0.7 +1.5 +0.5
+ Elf +0.5 +0.6 +3.0 +2.0 +0.8 -0.5 +1.5
+ Ent +0.5 +0.5 +10.0 -6.0 +0.5 -0.3 -0.2
+ Gnome +1.0 +1.2 +6.0 +3.0 +0.6 -0.8 +1.2
+ Half-Elf +0.2 +0.3 +1.5 +1.0 +0.6 -0.1 +0.5
+ Half-Ogre -0.3 -0.5 -2.5 -2.0 -0.1 +2.0 0
+ High-Elf +0.4 +2.0 +10.0 +4.0 +0.3 +1.0 +2.5
+ Hobbit +1.5 +1.8 +9.0 +5.0 +1.2 -1.0 +2.0
+ Human 0 0 0 0 0 0 0
+ Kobold -0.2 -0.3 -1.0 -1.0 +0.1 +1.0 -0.8
+ Maia 0 0 0 0 0 0 0
+ Orc -0.3 -0.3 -1.0 -1.0 0 +1.2 -0.5
+ Petty Dwarf +0.3 +0.5 +5.0 +1.0 +0.5 0 0
+ RohanKnight +1.0 +0.5 +2.5 -8.0 +0.1 +0.1 +0.5
+ Thunderlord +0.6 0 +5.0 -16.0 +3.0 +1.5 +0.5
+ Troll -0.5 -0.8 -4.0 -2.0 -0.1 +2.0 -1.0
+ Wood-Elf +0.5 +0.6 +3.0 +5.0 +0.8 -2.5 +4.0
+ Yeek -0.5 -0.5 -2.5 -5.0 -0.5 -0.5 -0.5
+
+In addition to the racial starting bonuses for the standard skills listed
+above, there are some special bonuses to the skill modifier:
+
+ Beorning +1.0 Bearform-combat modifier (also 1.0 points to start)
+ Dark-Elf +0.2 Magic modifier
+ DeathMold +0.2 Necromancy modifier
+ Dwarf +0.2 Axe-mastery modifier
+ Ent +0.2 Barehand-combat modifier;
+ +0.6 Boulder-throwing modifier
+ Hobbit +0.3 Sling-mastery modifier
+ Maia Not allowed to use Prayer (modifier reduced to 0.000)
+ RohanKnight +0.2 Weaponmastery modifier
+ Wood-Elf +0.2 Archery modifier
+
+(If the character's class does not normally possess the skill, this racial
+modifier bonus will grant the skill at a starting level of 0.000.)
+
+~~~~~79|Race Modifiers|Skill table
+#####GRace Modifiers:
+#####B Disarm Devices Sprtlty Stealth Sneak Wepnmas Archery
+ Classical 0 0 0 0 0 0 0
+ Barbarian -0.2 -1.0 +0.2 -2.0 +0.0 +1.2 +0.5
+ Hermit +0.5 +1.0 +0.5 +3.0 +0.4 -0.5 -0.5
+ Lost Soul 0 0 0 0 0 0 0
+ Skeleton -0.5 -0.5 +0.5 -1.0 -0.1 +0.8 0
+ Spectre +0.2 +0.8 +0.7 +2.0 +0.2 -0.5 -0.2
+ Vampire 0 0 0 0 0 0 0
+ Zombie -0.2 -0.2 +0.5 -1.0 -0.1 +0.5 0
diff --git a/lib/help/bldg.txt b/lib/help/bldg.txt
new file mode 100644
index 00000000..3a767968
--- /dev/null
+++ b/lib/help/bldg.txt
@@ -0,0 +1,59 @@
+|||||oy
+~~~~~01|Buildings
+#####R=== Historical Town View ===
+The town is composed of both stores and buildings.
+
+#####RStores
+The stores are where you can pick up the supplies you need before entering the
+depths of the dungeon. These include:
+
+[[[[[GGeneral Store (1):] food, torches, shovels... the necessities.
+[[[[[GArmoury (2):] to protect from the ravages of the dungeon.
+[[[[[GWeaponsmith (3):] they carry anything sharp and to the point.
+[[[[[GTemple (4):] prayerbooks and those items holy.
+[[[[[GAlchemist (5):] for all sorts of bubbling potions and scrolls.
+[[[[[GMagic Shop (6):] get your wands and spellbooks here.
+[[[[[GBlack Market (7):] the prices are high, but the items unique.
+[[[[[GYour Home (8):] to store your precious treasures.
+[[[[[GBook Store (9):] for all sorts of basic spell book needs.
+
+#####ROther Buildings
+In addition to the basic stores, there are some special buildings that can be
+found in some towns. These buildings (represented by +'s) include:
+
+[[[[[GMayor's Office/Castle:] The administrative center of the town.
+Adventurers looking for work besides exploring the dungeon should hunt in here.
+[[[[[GPet Shop:] Great place to purchase eggs and get pets.
+[[[[[GThe Soothsayer:] To discover what *****/afatespoi.txt*0[fates ("a")] lie in store for you.
+[[[[[GThe Inn:] Wine, dine, rest and relax!
+[[[[[GThe Nest:] Thunderlords are masters of teleportation, and will consent to bear
+you to your chosen dungeon destination for a fee.
+[[[[[GBeastmaster Shanty:] For those who enjoy trophy hunting, and to research the
+strange animals seen during their adventures.
+[[[[[GFighters Hall:] The place to reforge weapons and armour.
+[[[[[GRangers Guild:] The place to reforge bows and arrows.
+[[[[[GLibrary:] For information of all kinds.
+[[[[[GGambling House:] Read the *****/bgambling.txt*0[rules ("b")] before paying. The games are not
+rigged, just naturally difficult.
+[[[[[GTower of Magery/Wizards Spire:] The wizards will identify your items or recharge
+your magical items for a fee.
+[[[[[GInner temple/Priests Circle:] A place of healing.
+[[[[[GPaladin guild:] Some healing and enchantments available.
+[[[[[GThe Mathom House:] Donate your unwanted items to this museum.
+... and several more!
+
+Some of the places have been known to give out quests to adventurers they
+deem capable enough. While the mayor's office is the obvious place to start,
+the Sea Dome or Beastmaster Shanty may also want you to bring in various heads
+for reasons of their own. Go into every building you can find. Quests may
+pop up in unexpected places.
+
+There are misty rumours that there may be stores outside of Bree. The faintest
+speak of stores of powerful items deep in the dungeon.
+Rumours can be helpful or just plain silly.
+All buildings are made of stone and unlikely to move around.
+
+
+ File Updated for Pern 4.x.x by Dawnmist.
+ File Updated for ToME 2.1.x by Kat B.
+ File Updated for ToME 2.3.x by gwooledge
diff --git a/lib/help/c_alchem.txt b/lib/help/c_alchem.txt
new file mode 100644
index 00000000..5c923f87
--- /dev/null
+++ b/lib/help/c_alchem.txt
@@ -0,0 +1,135 @@
+|||||oy
+~~~~~01|Alchemist
+~~~~~02|Classes|Alchemist
+#####R=== Alchemists ===
+
+#####GDescription
+Alchemists are the only class that can harness the abilities of the
+essences found in the dungeon. They can add these essences to staves,
+rings, wands, rods, and sometimes weapons and armour to create new items
+or recharge old ones. They can also extract essences from magical items
+they find. Using these abilities, Alchemists can get very good items at
+low levels. The trouble is getting them to survive later on.
+
+Alchemists are extremely proficient with all kinds of magical devices,
+and have made a fine art of extracting, storing, and using these
+objects' magical energies. Because they neither fight well nor cast
+powerful spells, Alchemists rely on their craftsmanship to cope with
+the dangers of the dungeons. They will quickly learn to create damage-
+dealing wands, rods, and staffs, forge powerful weapons and armour,
+make highly useful objects of other kinds, and eventually, at great cost
+to other knowledge, they can learn the perilous art of artifact creation
+itself.
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +0%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.700]
+ Weaponmastery 0.700 [0.700]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 3.000 [0.900]
+ Magic-Device 1.000 [1.250]
+ Geomancy
+ Fire 0.000 [0.100]
+ Water 0.000 [0.100]
+ Air 0.000 [0.100]
+ Earth 0.000 [0.100]
+ Meta 0.000 [0.500]
+ Conveyance 0.000 [0.100]
+ Divination 0.000 [0.500]
+ Temporal 0.000 [0.100]
+ Mind 0.000 [0.100]
+ Nature 0.000 [0.100]
+ Necromancy 0.000 [0.100]
+ Runecraft 0.000 [0.700]
+ Thaumaturgy 0.000 [0.100]
+ Alchemy 1.000 [0.800]
+Spirituality 1.000 [0.800]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*An Alchemist cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+An Alchemist begins the game with:
+ a Dagger
+ six Essences of Explosion
+ an Empty Bottle
+ a Set of Leather Gloves
+~~~~~03|Alchemist|Alchemy powers explained
+~~~~~05|Skills|Alchemy - Alchemy powers
+#####GAlchemy
+[[[[[BThe alchemical techniques are accessed using the 'm' key.]
+Alchemists may then 'E'xtract essences or add 'P'owers. The Alchemist [[[[[vmust]
+be wearing GLOVES to use their alchemy powers.
+
+An Alchemists is also capable of [[[[[Bcreating their own artifacts], but
+this is a time-consuming, and costly, activity. To do so, he must first
+learn the ability *****ability.txt*09[Artifact Creation], which costs a whopping 70 skill points
+to learn, and requires some high pre-requisites. Once he has done so,
+he needs to imbue an ego item with a number of essences of magic (equal
+to their skill level) and then wield the item. While using the item,
+it will gain experience (which reduces the amount of exp the wearer
+gains during this time). When the Alchemist feels that the item is
+powerful enough, he can finalise the artifact by "buying" powers using
+the experience the artifact has collected. He will also require exotic
+ingredients to add the abilities.
+
+Most (but not all) potions, scrolls, wands, staffs, rods, rings, and
+amulets contain usable magical energies. An Alchemist taps that
+energy, using the technique "'E'xtract essences" to create one or more
+essences of a type appropriate to the original object.
+
+The drained object itself is now called "of nothing". Unlike any
+other kind of object, it can have new magics added to it.
+
+An Alchemist can add power to such items by using essences he made or
+found. He does this with the technique "'P'ower". He can then select
+the object he wishes to enchant, and the kind of object he wishes to
+create. Only object types for which he knows the recipe will be displayed,
+and ones for which essences are lacking will be displayed in red. Objects
+whose level exceeds his own are sometimes difficult to create, though
+he can always add gold to improve the chances of his success.
+
+[[[[[BA few pointers:]
+ -Recipes are (usually) logical. Acid essences don't make Dragon
+ Weapons. And you can always look at the recipes you know with
+ the "recipe 'B'ook" command.
+ -Not all objects or ego-item types can be made using alchemy.
+ -Alchemy is mostly reversible. Most items that you can
+ 'E'xtract from, you can later re-create, if you learn about
+ them when extracting from them. You won't always get enough
+ essences from destroying an item to recreate it, though the
+ chance of doing so will get higher as you increase in skill.
+ -*Identifying* an object that you can destroy will always teach
+ you how to create it (if creating it is possible).
+ -Since you can't create abilities in artifacts that you aren't
+ aware of, make sure to *identify* all the artifacts you can.
+ -You can't *identify* things you buy in shops, so you'll have
+ to extract from them and take your chances.
+
+[[[[[BSome Recipes:]
+Any Alchemist worth his salt knows lots of recipes. By extracting from
+items in the dungeon, he learns to create more and more items. You can
+see the recipes you know using the "recipe 'B'ook" command. The Alchemist
+starts off knowing some basic recipes, and gains more recipes as time
+goes on.
+
+More details can be found in the *****essences.txt*0[Essences SPOILER]).
+
diff --git a/lib/help/c_archer.txt b/lib/help/c_archer.txt
new file mode 100644
index 00000000..852f1742
--- /dev/null
+++ b/lib/help/c_archer.txt
@@ -0,0 +1,68 @@
+~~~~~01|Archer
+~~~~~02|Classes|Archer
+#####R=== Archers ===
+
+#####GDescription
+Archers are to bows what warriors are to melee. They are the best class
+around with any bow/crossbow/sling/boomerang.
+
+Needing a lot of ammo, they will learn early how to make it from junk found
+in the dungeons. They also gain, at skill level 20, the unique ability to make
+their arrows/bolts/shots pierce through monsters!
+
+#####GStarting Stat Modifiers
+Strength +2
+Intelligence +1
+Wisdom +0
+Dexterity +2
+Constitution +1
+Charisma +1
+Hit Die +d4
+Spell Points +0%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.800]
+ Weaponmastery 1.000 [0.500]
+ Archery 1.000 [0.850]
+ Sling-mastery 0.000 [0.500]
+ Bow-mastery 0.000 [0.500]
+ Crossbow-mastery 0.000 [0.500]
+ Boomerang-mastery 0.000 [0.500]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Magic 0.000 [0.200]
+ Magic-Device 1.000 [1.100]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Ammo creation 2
+
+#####GStarting Equipment
+An Archer begins the game with:
+ a Short Bow
+ a Sling
+ some ammo
+~~~~~03|Creating Ammo
+#####GCreating AMmo
+Archers automatically gain the *****ability.txt*07[Ammo Creation] ability when they reach character
+level 2. (Other classes can purchase this ability when they have enough skill
+points.) This ability is accessed via the 'm' command. The first type of
+ammo you can make is sling ammo (pebbles or shots); as you increase in skill
+you'll be able to make other arrows (bow ammo) or bolts (crossbow ammo).
+
+Shots (or other sling ammo) are created from rubble piles found in the dungeons
+and other places. To make shots, stand next to a rubble pile, activate the
+ammo creation ability from the 'm' menu, select 's'hots, then specify the
+direction to the rubble pile, which will be consumed during the ammo creation.
+Arrows or bolts are made from "junk" items ('~' symbol, such as shards of
+pottery) that you can find in the dungeons and other places. To make arrows or
+bolts, assuming you have sufficient Archery skill, you must have the junk item
+in your inventory or on the ground at your feet. Specify the junk item after
+selecting 'a'rrows or 'b'olts from the ammo creation menu, and it will be
+consumed and replaced with a stack of ammo.
diff --git a/lib/help/c_assass.txt b/lib/help/c_assass.txt
new file mode 100644
index 00000000..4269e3e8
--- /dev/null
+++ b/lib/help/c_assass.txt
@@ -0,0 +1,58 @@
+~~~~~01|Assassin
+~~~~~02|Classes|Assassin
+#####R=== Assassins ===
+#####GDescription
+Assassins are similar to Rogues, but have trained their combat abilities more
+extensively by neglecting the study of magic. They also tend to be more stealthy
+and careful in their dungeon exploration, but aren't so good at stealing,
+trapping and disarming as their more "peaceful" counterparts.
+
+Assassins have access to the schools of *****m_convey.txt*0[Conveyance], *****m_divin.txt*0[Divination] and *****m_tempo.txt*0[Temporal] magic.
+
+#####GStarting Stat Modifiers
+Strength +2
+Intelligence +1
+Wisdom -2
+Dexterity +3
+Constitution +1
+Charisma -1
+Hit Die +d6
+Spell Points +0%
+Exp Penalty 25%
+
+#####GStarting Skills
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.800]
+ Weaponmastery 1.000 [0.800]
+ Sword-mastery 1.000 [0.600]
+ Critical-hits 2.000 [0.800]
+ Archery
+ Boomerang-mastery 1.000 [0.300]
+Sneakiness 1.000 [2.000]
+ Stealth 1.000 [2.000]
+ Disarming 1.000 [1.000]
+ Backstab 1.000 [2.000]
+ Stealing 1.000 [0.200]
+ Dodging 1.000 [2.000]
+Magic 1.000 [0.200]
+ Magic-Device 1.000 [0.750]
+ Conveyance 0.000 [0.100]
+ Divination 0.000 [0.100]
+ Temporal 0.000 [0.200]
+Spirituality 1.000 [0.700]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*An Assassin cannot learn the Archery skill, but it is shown in his skill
+screen because Boomerang-mastery is a sub-skill of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 10
+
+#####GStarting Equipment
+An Assassin begins the game with:
+ a Dagger
+
+
+
diff --git a/lib/help/c_axemas.txt b/lib/help/c_axemas.txt
new file mode 100644
index 00000000..cdd6ba88
--- /dev/null
+++ b/lib/help/c_axemas.txt
@@ -0,0 +1,51 @@
+~~~~~01|Axemaster
+~~~~~02|Classes|Axemaster
+#####R=== Axemasters ===
+
+#####GDescription
+The Axemaster is a Warrior who specialises in axes.
+The training is so intense and specific that Axemasters gain huge bonuses
+when fighting with their chosen weapon, dependent on their Axe-mastery skill.
+In most other respects they perform about as well as a generic Warrior.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.900]
+ Weaponmastery 1.000 [0.950]
+ Sword-mastery 0.000 [0.300]
+ Axe-mastery 1.000 [0.700]
+ Hafted-mastery 0.000 [0.300]
+ Polearm-mastery 0.000 [0.300]
+ Archery 1.000 [0.600]
+ Antimagic 0.000 [0.550]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Magic 1.000 [0.300]
+ Magic-Device 1.000 [1.150]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread blows 25
+
+#####GStarting Equipment
+An Axemaster begins the game with:
+ a Ring of Fear Resistance
+ a Chain Mail
+ a Hatchet
diff --git a/lib/help/c_bard.txt b/lib/help/c_bard.txt
new file mode 100644
index 00000000..23ba42ca
--- /dev/null
+++ b/lib/help/c_bard.txt
@@ -0,0 +1,69 @@
+|||||oy
+~~~~~01|Bard
+~~~~~02|Classes|Bard
+#####R=== Bards ===
+
+#####GDescription
+Bards are traditional musicians. Their magical attacks are sound-based, and
+last as long as the Bard has mana. If the Bard runs out of mana, he/she stops
+singing. There is also a low-level "spell" that will cease the song.
+
+While any class can use musical instruments, only Bards can tap the
+power of voice to aid themselves and hinder, dismay, and kill their
+foes.
+
+#####GMagic Usage
+
+Songs are continuous, and the song consumes mana every turn in order to
+maintain itself. The song will continue, once played, until either the 'Stop
+Singing' song is sung, or the player's mana runs out.
+
+Each song has a magic school level associated with, just as any other magic
+spell does. Each song also has a Roman numeral (e.g. I, II, III, IV, V)
+following its name. These numerals correspond to 1, 2, 3, 4, 5. The higher the
+numeral, the more music skill the Bard must have to play that song.
+
+Each musical instrument has a value between 1 and 4 assigned to it, as well.
+The higher the number, the better the craftmanship. A Bard can only play
+higher level songs with a more powerful instrument. You would be able to cast
+"Stop Singing (I)" and "Song of the Sun (I)" with a a Harp (+1). A Harp (+2)
+would allow you to sing those songs, as well as "Flow of Life (II)".
+
+For information on song effects see the *****m_music.txt*0[Music Songs] file.
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution +0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Barehand-combat 1.000 [0.600]
+Sneakiness 1.000 [0.700]
+ Stealth 1.000 [0.700]
+ Disarming 1.000 [0.600]
+Magic 1.000 [0.600]
+ Magic-Device 1.000 [1.000]
+Spirituality 1.000 [0.800]
+ Prayer 0.000 [0.500]
+ Music 1.000 [0.800]
+Monster-lore 1.000 [1.100]
+ Summoning 0.000 [0.400]
+ Corpse-preservation 1.000 [0.700]
+ Symbiosis 0.000 [0.400]
+ Mimicry 0.000 [0.400]
+
+#####GStarting Equipment
+A Bard begins the game with:
+ a Harp (+1)
+ a Short Sword
+ a Robe
+ a Potion of Healing
diff --git a/lib/help/c_demono.txt b/lib/help/c_demono.txt
new file mode 100644
index 00000000..98b0bc9b
--- /dev/null
+++ b/lib/help/c_demono.txt
@@ -0,0 +1,54 @@
+|||||oy
+~~~~~01|Demonologist
+~~~~~02|Classes|Demonologist
+#####R=== Demonologists ===
+
+#####GDescription
+Masters of the Demons, members of this class can gain tremendous power
+over demonkind, either for good or for evil ends.
+
+Their spells are contained in specific blades, shields and helms (actually
+shaped in the form of horns) which when wielded allow the Demonologist to
+cast spells unique to her class; each piece of equipment holds 3 spells.
+
+See the information on the *****m_demono.txt*0[Demonology] school of magic for more details.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.750]
+ Weaponmastery 1.000 [0.750]
+ Sword-mastery 0.000 [0.600]
+ Archery 2.000 [0.400]
+Sneakiness 2.000 [1.800]
+ Stealth 0.000 [0.400]
+ Disarming 2.000 [1.800]
+Magic 2.000 [0.700]
+ Magic-Device 1.000 [1.150]
+ Demonology 1.000 [1.000]
+Spirituality 2.000 [0.700]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread blows 25
+
+#####GStarting Equipment
+A Demonologist begins the game with:
+ a Ring of Fear Resistance
+ a Demonblade
+ a Chain Mail
+
diff --git a/lib/help/c_druid.txt b/lib/help/c_druid.txt
new file mode 100644
index 00000000..0b0493e2
--- /dev/null
+++ b/lib/help/c_druid.txt
@@ -0,0 +1,55 @@
+|||||oy
+~~~~~01|Druid
+~~~~~02|Classes|Druid
+~~~~~03|Yavanna|Druid
+#####R=== Druids ===
+
+#####GDescription
+When Melkor first dug up Utumno and Angband, rivers were polluted and gave
+birth to dark clouds of stinging insects; animals changed into dark, horrible
+horned things and the forests themselves screamed in horror at their corruption.
+
+*****g_yavann.txt*0[Yavanna] heard this scream and gave to some of the children of Eru the strength
+to defend Nature; at first Ents were chosen, but later on other races were
+allowed to tread the path of the Druid.
+
+The embodiment of life itself, a Druid is a true force of nature and nothing
+can prevent him from completing his final quest: returning Angband to the
+grassy sunlit plain it once was.
+
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Hit Die +d2
+Spell Points +0%
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.750]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 0.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 0.000 [0.600]
+Spirituality 1.000 [1.000]
+ Prayer 1.000 [1.500]
+ Mindcraft 0.000 [0.600]
+Monster-lore 1.000 [1.200]
+ Summoning 1.000 [0.700]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Druid begins the game with:
+ a Spellbook of Charm Animal
+ a Mace
diff --git a/lib/help/c_geoman.txt b/lib/help/c_geoman.txt
new file mode 100644
index 00000000..47855875
--- /dev/null
+++ b/lib/help/c_geoman.txt
@@ -0,0 +1,59 @@
+|||||oy
+~~~~~01|Geomancer
+~~~~~02|Classes|Geomancer
+#####R=== Geomancers ===
+
+#####GDescription
+Geomancers harness the power of the elements earth, air, fire and water.
+The level of their *****skills.txt*60[Geomancy] skill gives them access to their own
+*****m_geoman.txt*0[school of Geomancy], but the levels of the Fire, Water, Air, and Earth
+skills will have an effect on the outcome of each spell.
+
+Geomancers need the aid of a Mage Staff in order to use their powers.
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.200]
+ Weaponmastery 0.700 [0.400]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 1.000 [0.700]
+ Geomancy 1.000 [0.700]
+ Fire 1.000 [1.050]
+ Water 1.000 [1.050]
+ Air 1.000 [1.050]
+ Earth 1.000 [1.050]
+ Meta 0.000 [0.700]
+ Conveyance 0.000 [0.700]
+ Divination 0.000 [0.700]
+ Temporal 0.000 [0.700]
+ Mind 0.000 [0.700]
+ Nature 0.000 [0.700]
+ Necromancy 0.000 [0.700]
+ Runecraft 0.000 [0.700]
+ Thaumaturgy 0.000 [0.700]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Geomancer begins the game with:
+ a Spellbook of Geyser
+ a Mage Staff
diff --git a/lib/help/c_hafted.txt b/lib/help/c_hafted.txt
new file mode 100644
index 00000000..7e8a3f89
--- /dev/null
+++ b/lib/help/c_hafted.txt
@@ -0,0 +1,54 @@
+~~~~~01|Haftedmaster
+~~~~~02|Classes|Haftedmaster
+#####R=== Haftedmasters ===
+
+#####GDescription
+The Haftedmaster is a Warrior who specialises in blunt weapons.
+
+The training is so intense and specific that Haftedmasters gain huge bonuses
+when fighting with their chosen weapon, dependent on their *****skills.txt*06[Hafted-mastery] skill.
+
+In most other respects they perform about as well as a generic Warrior.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.900]
+ Weaponmastery 1.000 [0.950]
+ Sword-mastery 0.000 [0.300]
+ Axe-mastery 0.000 [0.300]
+ Hafted-mastery 1.000 [0.700]
+ Stunning-blows 0.000 [0.500]
+ Polearm-mastery 0.000 [0.300]
+ Archery 1.000 [0.600]
+ Antimagic 0.000 [0.550]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Magic 1.000 [0.300]
+ Magic-Device 1.000 [1.150]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread blows 25
+
+#####GStarting Equipment
+An Haftedmaster begins the game with:
+ a Ring of Fear Resistance
+ a Chain Mail
+ a Flail
diff --git a/lib/help/c_lorema.txt b/lib/help/c_lorema.txt
new file mode 100644
index 00000000..35c2093b
--- /dev/null
+++ b/lib/help/c_lorema.txt
@@ -0,0 +1,54 @@
+|||||oy
+~~~~~01|Loremaster
+~~~~~02|Classes|Loremaster
+#####R=== Loremasters ===
+
+#####GDescription
+Loremasters are students of the creatures of Arda.
+
+To protect themselves during their observations, they usually learn how to
+pass unobserved, but should their attempt fail they have decent saving throws
+and almost always learn some kind of combat style, but the exact preference
+varies from Loremaster to Loremaster.
+
+As a result of their studies, they become familiar with a broad range of
+skills, ranging from *****skills.txt*45[Possession] and *****m_symbio.txt*0[Symbiosis] to *****skills.txt*43[Summoning] and *****skills.txt*47[Mimicry].
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution +0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Archery 1.000 [0.700]
+ Barehand-combat 1.000 [0.700]
+Sneakiness 1.000 [0.700]
+ Stealth 1.000 [0.700]
+ Disarming 1.000 [0.700]
+Magic 0.000 [0.600]
+ Magic-Device 1.000 [1.000]
+Spirituality 1.000 [0.700]
+ Prayer 0.000 [0.500]
+ Music 0.000 [0.300]
+Monster-lore 1.000 [1.100]
+ Summoning 0.000 [0.500]
+ Corpse-preservation 1.000 [0.700]
+ Possession 0.000 [0.500]
+ Symbiosis 0.000 [0.500]
+ Mimicry 0.000 [0.500]
+
+#####GStarting Equipment
+A Loremaster begins the game with:
+ a Sling
+ a Hard Leather Armour
+ a Quarterstaff
+ some Rounded Pebbles
diff --git a/lib/help/c_mage.txt b/lib/help/c_mage.txt
new file mode 100644
index 00000000..949d3bcc
--- /dev/null
+++ b/lib/help/c_mage.txt
@@ -0,0 +1,67 @@
+|||||oy
+~~~~~01|Mage
+~~~~~02|Classes|Mage
+#####R=== Mages ===
+
+#####GDescription
+A Mage must live by his wits. He cannot hope to simply hack his way
+through the dungeon, and so must therefore use his magic to defeat,
+deceive, confuse, and escape. A Mage is not really complete without an
+assortment of magical devices to use in addition to his spells. He can
+master the higher level magical devices easily and has good saving throws.
+
+There is no reason a Mage cannot become a good fighter, but spells are
+his true realm. For more information on magic schools and spell effects see
+the *****magic.txt*01[Magic help file].
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.500]
+ Weaponmastery 0.700 [0.500]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.200]
+ Spell-power 0.000 [0.600]
+ Sorcery 0.000 [0.200]
+ Mana 1.000 [0.900]
+ Geomancy
+ Fire 0.000 [0.900]
+ Water 0.000 [0.900]
+ Air 0.000 [0.900]
+ Earth 0.000 [0.900]
+ Meta 0.000 [0.900]
+ Conveyance 0.000 [0.900]
+ Divination 0.000 [0.900]
+ Temporal 0.000 [0.900]
+ Mind 0.000 [0.900]
+ Nature 0.000 [0.900]
+ Necromancy 0.000 [0.700]
+ Runecraft 0.000 [0.700]
+ Thaumaturgy 0.000 [0.700]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*A Mage cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Mage begins the game with:
+ a Book of Beginner Cantrips
+ a Dagger
diff --git a/lib/help/c_merch.txt b/lib/help/c_merch.txt
new file mode 100644
index 00000000..31fb60dd
--- /dev/null
+++ b/lib/help/c_merch.txt
@@ -0,0 +1,29 @@
+#####R=== Merchants ===
+
+#####GDescription
+A Merchant is neither a warrior nor a spellcaster. They still have some great
+advantages, they can use chests to warp items into other items, they can
+indentify items, they soon learn to detect all objects in the area, and
+at higher level they can see all monsters carrying objects. They will also
+get the power to appraise items and to turn them into gold. A merchant will
+naturraly get better prices in shops and get access to the merchant guild
+services, loan and item request.
+
+#####GPrimary Stats
+Charisma
+Intelligence (Ability stat)
+
+#####GMagic Usage
+Merchants can use portable holes to carry more stuff than other classes but
+at the cost of an increased weight. To do that they must wear a portable hole
+and use it with 'm'.
+They also can use their merchants abilities and midas touch in the 'U' menu.
+
+#####GStarting Equipment
+A merchant begins the game with:
+ A portable hole
+ A small steel chest containing gold and items
+ A long sword
+ A wand of tame monsters
+
+
diff --git a/lib/help/c_mimic.txt b/lib/help/c_mimic.txt
new file mode 100644
index 00000000..b9378a03
--- /dev/null
+++ b/lib/help/c_mimic.txt
@@ -0,0 +1,53 @@
+~~~~~01|Mimic
+~~~~~02|Classes|Mimic
+#####R=== Mimics ===
+
+#####GDescription
+Mimics possess the ability to transform into various creatures using
+special cloaks. While transformed, they lose their intrinsic abilities,
+and gain those of the creature they have transformed into.
+
+At higher skill levels, Mimics gain additional Mimicry powers which help them
+to further blend in with their surroundings or modify themselves.
+
+See more on *****m_mimic.txt*0[Mimicry powers].
+
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution +0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.800]
+ Weaponmastery 1.000 [0.800]
+ Archery 1.000 [0.700]
+ Barehand-combat 1.000 [0.600]
+Sneakiness 1.000 [0.800]
+ Stealth 1.000 [0.800]
+ Disarming 1.000 [0.700]
+Magic 1.000 [0.700]
+ Magic-Device 1.000 [1.000]
+Spirituality 1.000 [0.500]
+ Prayer 0.000 [0.500]
+ Music 0.000 [0.300]
+Monster-lore 1.000 [1.100]
+ Corpse-preservation 1.000 [0.900]
+ Possession 0.000 [0.100]
+ Mimicry 1.000 [0.800]
+
+#####GStarting Equipment
+A Mimic begins the game with:
+ a Mouse Fur
+ a Potion of Healing
+ a Dagger
+
+
+
diff --git a/lib/help/c_mindcr.txt b/lib/help/c_mindcr.txt
new file mode 100644
index 00000000..c4ed2747
--- /dev/null
+++ b/lib/help/c_mindcr.txt
@@ -0,0 +1,57 @@
+|||||oy
+~~~~~01|Mindcrafter
+~~~~~02|Classes|Mindcrafter
+#####R=== Mindcrafters ===
+
+#####GDescription
+The Mindcrafter is a priest who uses the powers of mind instead of magic.
+These abilities vary from simple extrasensory perception to mental domination of
+others. Since these powers are developed by the practice of certain
+disciplines, a Mindcrafter requires no spellbooks to use them.
+The Mindcrafter uses the Mindcraft skill to determine how well she can
+perform these psychic "spells", and available powers are simply determined by
+the skill level. In combat a Mindcrafter is roughly the equivalent of a
+priest.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Hit Die +d2
+Spell Points +0%
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+Sneakiness 1.000 [1.100]
+ Stealth 0.000 [0.400]
+ Disarming 0.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.150]
+ Spell-power 0.000 [0.600]
+ Necromancy 0.000 [0.400]
+Spirituality 1.000 [1.000]
+ Prayer 1.000 [0.900]
+ Mindcraft 1.000 [0.900]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GMindcraft Powers
+Although the powers of a Mindcrafter may seem like magic, this is not
+-- strictly speaking -- the case. They are mental powers, independent
+of the ordinary sources of magic. Consequently, Mindcrafters are not
+interested in 'magic' and learn no spells. These mental powers can be
+accessed through the "m" command, and you can find a full list of their
+powers in the *****m_mindcr.txt*0[Mindcraft powers] document.
+
+#####GStarting Equipment
+A Mindcrafter begins the game with:
+ a Mace
diff --git a/lib/help/c_monk.txt b/lib/help/c_monk.txt
new file mode 100644
index 00000000..87730f18
--- /dev/null
+++ b/lib/help/c_monk.txt
@@ -0,0 +1,87 @@
+|||||oy
+~~~~~01|Monk
+~~~~~02|Classes|Monk
+#####R=== Monks ===
+
+#####GDescription
+The Monk character class is very different from all other classes.
+Although they can use weapons and armour just like any other class,
+their training in martial arts makes them much more powerful with no
+armour nor weapons.
+
+As the Monk's skill level rises, new and more powerful forms of attack become
+available. It is also rumoured that the monastic training makes experienced
+monks faster than any other character class!
+
+A Monk might need to wear some kind of armour to gain the resistances necessary
+for survival at higher levels, but if that armour is too heavy, it will
+severely disturb his/her martial arts maneuvers.
+
+If skill points are invested in *****skills.txt*20[Dodging], a Monk's defensive capabilities
+(blocking and dodging) will also increase. However, if armour is being worn,
+dodging is much less effective. Fortunately, a Monk can increase his/her
+ability to fight while still wearing armor by increasing his/her
+*****skills.txt*13[Barehanded-combat] skill.
+
+Monks also have access to the schools of *****m_meta.txt*0[Meta],
+*****m_tempo.txt*0[Temporal] and *****m_mind.txt*0[Mind] magic.
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution +0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 0.000 [0.900]
+ Weaponmastery 0.000 [0.300]
+ Archery 0.000 [0.400]
+ Barehand-combat 1.000 [0.900]
+Sneakiness 1.000 [0.900]
+ Stealth 1.000 [0.900]
+ Disarming 1.000 [0.900]
+ Dodging 0.000 [0.700]
+Magic 0.000 [0.600]
+ Magic-Device 1.000 [1.000]
+ Meta 0.000 [0.500]
+ Temporal 0.000 [0.500]
+ Mind 0.000 [0.500]
+Spirituality 1.000 [0.900]
+ Prayer 0.000 [0.500]
+ Music 0.000 [0.300]
+Monster-lore 1.000 [1.100]
+ Corpse-preservation 1.000 [0.500]
+ Possession 0.000 [0.100]
+
+#####GStarting Equipment
+A Monk begins the game with:
+ a Potion of Healing
+ a Soft Leather Armour
+~~~~~03|Monk|Monk attacks
+~~~~~04|Tables|Monk attacks
+#####GMonk Attacks
+#####BAttack Name Min.lvl Damage Stun Notes
+-----------------------------------------------------------------------------
+Punch 1 1d4 -
+Kick 2 1d6 -
+Strike 3 1d7 -
+Knee 5 2d3 * Painful to males; likely to stun them
+Elbow 7 1d8 -
+Butt 9 2d5 -
+Ankle Kick 11 3d4 - May slow down the opponent
+Uppercut 13 4d4 6
+Double-kick 16 5d4 8
+Cat's Claw 20 5d5 -
+Jump Kick 25 5d6 10
+Eagle's Claw 29 6d6 -
+Circle Kick 33 6d8 10
+Iron Fist 37 8d8 10
+Flying Kick 41 8d10 12
+Dragon Fist 45 10d10 16
+Crushing Blow 48 10d12 18
diff --git a/lib/help/c_necro.txt b/lib/help/c_necro.txt
new file mode 100644
index 00000000..f3a5ad2c
--- /dev/null
+++ b/lib/help/c_necro.txt
@@ -0,0 +1,80 @@
+|||||oy
+~~~~~01|Necromancer
+~~~~~02|Classes|Necromancer
+#####R=== Necromancers ===
+
+#####GDescription
+As a Priest devotes his life to his chosen deity, so Necromancers
+devote their lives to the study of death. Familiar with all of the
+forms of unbeing, they are able to manipulate spirit and flesh for
+great effect.
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.400]
+ Weaponmastery 0.700 [0.600]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.200]
+ Spell-power 0.000 [0.600]
+ Mana 0.000 [0.600]
+ Geomancy
+ Fire 0.000 [0.800]
+ Water 0.000 [0.700]
+ Air 0.000 [0.700]
+ Earth 0.000 [0.800]
+ Meta 0.000 [0.700]
+ Conveyance 0.000 [0.700]
+ Divination 0.000 [0.700]
+ Temporal 0.000 [0.800]
+ Mind 0.000 [0.900]
+ Nature 0.000 [0.500]
+ Necromancy 1.000 [1.000]
+ Runecraft 0.000 [0.700]
+ Thaumaturgy 0.000 [0.700]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 1.000 [1.100]
+ Corpse-preservation 5.000 [0.900]
+
+*A Necromancer cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+Undead Form 25
+
+#####GStarting Equipment
+A Necromancer begins the game with:
+ a Book of Beginner Cantrips
+ a Dagger
+
+#####GNecromancy
+*****m_necrom.txt*0[Necromancy powers] [[[[[Bare accessed using the 'm' key.]
+Even from the start of his career, an inexperienced Necromancer
+can channel forces from the underworld to assault the mind of
+living creatures, therefore terrifying them. With little effort he
+can also learn the art of reanimating a dead corpse, turning it into
+an undead slave. It is only at a later point in his career that this
+foul mage learns to manipulate his life force as well, gaining the
+ability to absorb hit points from both living and dead opponents.
+The greatest Necromancers even gain the ability to survive their own
+death: when they should be killed, they instead turn into a ghostly
+being; they then receive a set number of Death Points, which are in
+all respects similar to hit points except that they go automatically
+down each turn. If the Necromancer manages to kill a certain number
+of creatures before his Death Points reach 0 he is returned to life;
+otherwise his will dissipates and he finally achieves true death.
diff --git a/lib/help/c_palad.txt b/lib/help/c_palad.txt
new file mode 100644
index 00000000..b4cc650b
--- /dev/null
+++ b/lib/help/c_palad.txt
@@ -0,0 +1,49 @@
+|||||oy
+~~~~~01|Paladin
+~~~~~02|Classes|Paladin
+~~~~~03|Tulkas|Paladin
+#####R=== Paladins ===
+
+#####GDescription
+A Paladin is a warrior-priest at the service of *****g_tulkas.txt*0[Tulkas]. As such, his duty
+is to be ever vigilant against the forces of evil and even seek and destroy
+those monsters which are evil to the core, especially the foulest
+spawns of hell. Luckily, his quest is eased by the blessing bestowed
+by Tulkas himself.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Hit Die +d2
+Spell Points +0%
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.800]
+ Weaponmastery 1.000 [0.800]
+ Barehand-combat 0.000 [0.900]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 0.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 0.000 [0.600]
+Spirituality 1.000 [1.000]
+ Prayer 1.000 [1.400]
+ Mindcraft 0.000 [0.600]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+Extra Max Blow(1) 1
+
+#####GStarting Equipment
+A Paladin begins the game with:
+ a Spellbook of Divine Aim
+ a Two-Handed Sword
diff --git a/lib/help/c_polear.txt b/lib/help/c_polear.txt
new file mode 100644
index 00000000..8ea2f04a
--- /dev/null
+++ b/lib/help/c_polear.txt
@@ -0,0 +1,52 @@
+~~~~~01|Polearmmaster
+~~~~~02|Classes|Polearmmaster
+#####R=== Polearmmasters ===
+
+#####GDescription
+The Polearmmaster is a Warrior who specialises in polearms.
+
+The training is so intense and specific that Polearmmasters gain huge bonuses
+when fighting with their chosen weapon, dependent on their Polearm-mastery
+skill. In most other respects they perform about as well as a generic Warrior.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.900]
+ Weaponmastery 1.000 [0.950]
+ Sword-mastery 0.000 [0.300]
+ Axe-mastery 0.000 [0.300]
+ Hafted-mastery 0.000 [0.300]
+ Polearm-mastery 1.000 [0.700]
+ Archery 1.000 [0.600]
+ Antimagic 0.000 [0.550]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Magic 1.000 [0.300]
+ Magic-Device 1.000 [1.150]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread blows 25
+
+#####GStarting Equipment
+A Polearmmaster begins the game with:
+ a Ring of Fear Resistance
+ a Chain Mail
+ a Pike
diff --git a/lib/help/c_posses.txt b/lib/help/c_posses.txt
new file mode 100644
index 00000000..2d67a883
--- /dev/null
+++ b/lib/help/c_posses.txt
@@ -0,0 +1,70 @@
+~~~~~01|Possessor
+~~~~~02|Classes|Possessor
+#####R=== Possessors ===
+
+#####GDescription
+Possessors are unusual; they aren't good fighters, and they can't cast
+magic. Their special ability is that of being able to leave their
+bodies and inhabit corpses. While "in" a corpse, the Possessor gains
+the abilities of the monster, and their hit points and the monster's
+are averaged. (Most monsters have no, or very few, spell points.) While
+in the incorporeal form between bodies, they have only one hitpoint.
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution +0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.600]
+ Weaponmastery 1.000 [0.600]
+ Archery 1.000 [0.400]
+Sneakiness 1.000 [0.700]
+ Stealth 1.000 [0.700]
+ Disarming 1.000 [0.500]
+Magic 0.000 [0.600]
+ Magic-Device 1.000 [1.000]
+Spirituality 1.000 [0.500]
+ Prayer 0.000 [0.500]
+ Music 0.000 [0.300]
+Monster-lore 1.000 [1.100]
+ Corpse-preservation 1.000 [0.900]
+ Possession 1.000 [0.800]
+
+#####GStarting Equipment
+A Possessor begins the game with:
+ a Potion of Healing
+ a Short Sword
+ a Hard Leather Armour
+
+~~~~~03|Possessor|Possessor powers
+~~~~~04|Skills|Possession - Possessor powers
+#####GPossession
+The strange art of reanimating the corpse of a monster and making
+it one's own can be of extreme benefit to those skilled in it. The body
+the player possesses can grant powerful abilities, such as increased
+speed, summoning, healing, breathing, and various resistances, and often
+gives the player a large number of hit points. Possessing uniques is
+especially neat.
+
+Possessors leave their own body by using the 'm' command, and choosing
+"[I]incarnating powers". Lacking a life force to sustain it, a corpse may rot
+immediately when a Possessor leaves it, but a high Possession skill score
+may prevent this from happening. They then become a very vulnerable ghostly
+being which travels to where another corpse is lying on the floor, and
+possesses it by again using the 'm' command. Once possessed, many
+monster corpses allow the player to perform various special actions
+(such as blinking or summoning) by using the option "Use your [R]ace
+powers".
+
+All corpses alter the player's melee attacks. When they incarnate in a
+monster that allows the use of a weapon, monster blows are ignored
+[[[[[Btotally]. When they incarnate in a monster that doesn't allow use of a
+weapon (dragons for example) they use the monster blows [[[[[Band only them]!
diff --git a/lib/help/c_pr_drk.txt b/lib/help/c_pr_drk.txt
new file mode 100644
index 00000000..fa99f89f
--- /dev/null
+++ b/lib/help/c_pr_drk.txt
@@ -0,0 +1,57 @@
+|||||oy
+~~~~~01|Dark Priest
+~~~~~02|Classes|Dark Priest
+~~~~~03|Melkor|Dark Priests
+#####R=== Melkor's Priests ===
+
+#####GDescription
+All gods have priests; but those serving *****g_melkor.txt*0[Melkor], the dark enemy, are not
+like their good counterparts. While usually it takes devotion and awe to be
+inspired into serving a deity, Melkor's followers revere him because of the
+power he gives them. Some of them may even be so ambitious as to aspire to
+take his place upon the black throne of Angband. This he knows very well, but
+as long as he can use those puny mortals to inspire fear into the followers of
+the Valar and to bring destruction to Arda, he doesn't mind; he even lends power
+to the ones more bent on destruction, while sapping their minds to reduce them
+to unthinking slaves.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Hit Die +d2
+Spell Points +0%
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 0.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 0.000 [0.800]
+**Udun 0.000 [0.400]
+ Necromancy 0.000 [0.800]
+Spirituality 1.000 [1.000]
+ Prayer 1.000 [1.400]
+Monster-lore 0.000 [0.500]
+ Corpse-preservation 1.000 [1.000]
+
+**Actually, every character has this level of proficiency with the *****m_udun.txt*0[Udun] school,
+provided they are worshipping Melkor
+
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Priest serving Melkor begins the game with:
+ a Spellbook of Curse
+ a Mace
diff --git a/lib/help/c_pr_eru.txt b/lib/help/c_pr_eru.txt
new file mode 100644
index 00000000..ff5a0126
--- /dev/null
+++ b/lib/help/c_pr_eru.txt
@@ -0,0 +1,55 @@
+|||||oy
+~~~~~01|Priest - Eru
+~~~~~02|Classes|Priest - Eru
+~~~~~03|Eru|Priest - Eru
+#####R=== Eru's Priests ===
+
+#####GDescription
+*****g_eru.txt*0[Eru] is the father of the Valar: he created the world and all its inhabitants. He is the wisest being ever and even if he foresaw Morgoth's
+evil, his role was that of the creator; as such, he chose not to destroy
+anything on Arda.
+
+His priests are therefore expected to avoid all forms of bloodshed (so they
+can only use blunt weapons without penalty); however, Eru knows that sometimes
+destruction is unavoidable and marks a blade with his rune; his priests call
+them Blessed and use them without feeling guilty, for they know that divine
+wisdom will guide every swing of those weapons.
+
+Still, a priest serving Eru will find that his true strength lies in knowledge
+and in the use of the force of Mana to avoid close contact with evil beings,
+rather than in blind assault of Morgoth's hordes.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Hit Die +d2
+Spell Points +0%
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 0.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 0.000 [0.700]
+Spirituality 1.000 [1.000]
+ Prayer 1.000 [1.500]
+ Mindcraft 0.000 [0.600]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Priest serving Eru begins the game with:
+ a Spellbook of See the Music
+ a Mace
diff --git a/lib/help/c_pr_man.txt b/lib/help/c_pr_man.txt
new file mode 100644
index 00000000..3f83e8af
--- /dev/null
+++ b/lib/help/c_pr_man.txt
@@ -0,0 +1,54 @@
+|||||oy
+~~~~~01|Priest - Manwe
+~~~~~02|Classes|Priest - Manwe
+~~~~~03|Manwe|Priest - Manwe
+#####R=== Manwe's Priests ===
+
+#####GDescription
+As the forces of darkness arose, *****g_manwe.txt*0[Manwe], lord of air, realised that urgent
+action was needed to save Arda from the incoming darkness. So he started
+gathering followers from the ranks of men and elves and he instructed them to
+do battle against the forces of Udun.
+
+His priests must be quick and strike before the situation becomes
+critical, always keeping a direct line of prayer with their patron deity.
+
+Manwe doesn't tolerate laziness, but He also does not appreciate mindless
+killing; only a few measure up to his standards, but those are granted access
+to a whole series of divine powers which will help them in their enduring
+efforts.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Hit Die +d2
+Spell Points +0%
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.750]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 0.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 0.000 [0.600]
+Spirituality 1.000 [1.000]
+ Prayer 1.000 [1.500]
+ Mindcraft 0.000 [0.600]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Priest serving Manwe begins the game with:
+ a Spellbook of Manwe's Blessing
+ a Mace
diff --git a/lib/help/c_priest.txt b/lib/help/c_priest.txt
new file mode 100644
index 00000000..2054fbe5
--- /dev/null
+++ b/lib/help/c_priest.txt
@@ -0,0 +1,13 @@
+|||||oy
+~~~~~01|Priests
+#####R=== Priests ===
+
+#####GDescription
+There are 6 separate classes of Priest:
+
+*****c_pr_eru.txt*0[Priest(Eru)]
+*****c_pr_man.txt*0[Priest(Manwe)]
+*****c_druid.txt*0[Druid]
+*****c_palad.txt*0[Paladin]
+*****c_pr_drk.txt*0[Dark-Priest]
+*****c_mindcr.txt*0[Mindcrafter]
diff --git a/lib/help/c_ranger.txt b/lib/help/c_ranger.txt
new file mode 100644
index 00000000..5ca8ead9
--- /dev/null
+++ b/lib/help/c_ranger.txt
@@ -0,0 +1,55 @@
+|||||oy
+~~~~~01|Ranger
+~~~~~02|Classes|Ranger
+#####R=== Rangers ===
+
+#####GDescription
+Rangers are warrior-mages, devoted to nature. They are good fighters
+and quite effective with bows; their magic often allows them to avoid
+the worst of situations, but they have only a mild chance of resisting
+magical effects and are not terribly stealthy.
+
+They have access to the schools of *****m_divin.txt*0[Divination], *****m_convey.txt*0[Conveyance] and *****m_nature.txt*0[Nature] magic.
+
+#####GStarting Stat Modifiers
+Strength +2
+Intelligence +1
+Wisdom +0
+Dexterity +2
+Constitution +1
+Charisma +1
+Hit Die +d4
+Spell Points +0%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.800]
+ Weaponmastery 1.000 [0.500]
+ Archery 1.000 [0.750]
+ Sling-mastery 0.000 [0.300]
+ Bow-mastery 0.000 [0.300]
+ Crossbow-mastery 0.000 [0.300]
+ Boomerang-mastery 0.000 [0.300]
+Sneakiness 1.000 [0.950]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [1.600]
+Magic 1.000 [0.700]
+ Magic-Device 1.000 [1.100]
+ Conveyance 0.000 [0.500]
+ Divination 0.000 [0.500]
+ Nature 0.000 [0.500]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.700]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Ammo creation 2
+
+#####GStarting Equipment
+A Ranger begins the game with:
+ a Spellbook of Phase Door
+ a Short sword
+ a Short Bow
+ some Arrows
diff --git a/lib/help/c_rogue.txt b/lib/help/c_rogue.txt
new file mode 100644
index 00000000..b42df3d5
--- /dev/null
+++ b/lib/help/c_rogue.txt
@@ -0,0 +1,62 @@
+|||||oy
+~~~~~01|Rogue
+~~~~~02|Classes|Rogue
+#####R== Rogues ===
+
+#####GDescription
+A rogue is a jack of all trades, but the master of none. With the notable
+exception of Archery and Monster-related skills, Rogues are capable of adapting
+to almost any situation. Their strong point lies in stealth and careful
+planning: Where a Warrior would simply hack away (and risk being hacked up
+himself) or a Mage would Manathrust, a Rogue would awaken a monster with a
+dagger in the back, or would wait for the creature to be killed by the line of
+traps she had previously laid between her and her opponent.
+
+Rogues have access to the schools of *****m_convey.txt*0[Conveyance], *****m_divin.txt*0[Divination] and *****m_tempo.txt*0[Temporal] magic.
+
+#####GStarting Stat Modifiers
+Strength +2
+Intelligence +1
+Wisdom -2
+Dexterity +3
+Constitution +1
+Charisma -1
+Hit Die +d6
+Spell Points +0%
+Exp Penalty 25%
+
+#####GStarting Skills
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Sword-mastery 1.000 [0.300]
+ Critical-hits 1.000 [0.500]
+Sneakiness 1.000 [2.000]
+ Stealth 1.000 [1.500]
+ Disarming 1.000 [2.000]
+ Backstab 1.000 [1.000]
+ Stealing 1.000 [2.000]
+ Dodging 1.000 [2.000]
+Magic 1.000 [0.700]
+ Magic-Device 1.000 [1.550]
+ Conveyance 0.000 [0.500]
+ Divination 0.000 [0.500]
+ Temporal 0.000 [0.500]
+Spirituality 1.000 [0.700]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Trapping 1
+Extra Max Blow(1) 10
+
+#####GStarting Equipment
+A Rogue begins the game with:
+ a Dagger
+ a Catapult Trap Set
+ some Iron Shots
+
+
+
+
diff --git a/lib/help/c_runecr.txt b/lib/help/c_runecr.txt
new file mode 100644
index 00000000..8388eff9
--- /dev/null
+++ b/lib/help/c_runecr.txt
@@ -0,0 +1,110 @@
+~~~~~01|Runecrafter
+~~~~~02|Classes|Runecrafter
+#####R=== Runecrafters ===
+
+#####GDescription
+Instead of using spellbooks like the other spellcasters they instead
+use mystic runes. To cast a spell they select a primary rune of the
+elements (fire, cold, etc.) and they also choose a set of secondary runes,
+which shape the effect of the first one. The secondary runes include
+Self, Arrow, Ray, ... and Armageddon. After that they chose the amount
+of mana to use and the spell is cast! But the more secondary runes they
+chose the more mana is used to cast the spell. They also are bad
+fighters, but if they concentrate all their mana in one spell
+(especially with a mage staff of mana) they could kill nearly anything.
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.200]
+ Weaponmastery 0.700 [0.400]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 2.000 [0.950]
+ Magic-Device 1.000 [1.200]
+ Spell-power 0.000 [0.600]
+ Mana 1.000 [0.600]
+ Geomancy
+ Fire 0.000 [0.700]
+ Water 0.000 [0.700]
+ Air 0.000 [0.700]
+ Earth 0.000 [0.700]
+ Meta 0.000 [0.700]
+ Conveyance 0.000 [0.700]
+ Divination 0.000 [0.700]
+ Temporal 0.000 [0.700]
+ Mind 0.000 [0.700]
+ Nature 0.000 [0.700]
+ Necromancy 0.000 [0.700]
+ Runecraft 1.000 [1.000]
+ Thaumaturgy 0.000 [0.700]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*A Runecrafter cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+~~~~~03|Runecrafter|Runecrafter powers
+~~~~~04|Skills|Runecrafting - Runecrafter powers
+#####GRune Magic
+Runecrafters combine runes using the 'm' command. They first select a
+rune that controls magic type, then apply one or more runes to fine-tune
+effects, (pressing ESC when done), and then input the amount of mana
+they wish to expend on the spell.
+
+Runecrafters can cast the spells from their runes in several ways:
+1. On-the-fly by combining runes when they need them.
+2. Memorise rune combinations for quick use when needed (and they don't
+ need to be able to see then!), and then later cast from memory.
+3. Carving them into a Runestone, then using the Runestone later (takes
+ less mana, but they have to be able to see).
+
+[[[[[BSpell Types:]
+(Some kinds are not listed, and are left for the reader to discover...)
+ Knowledge: Identify all objects in affected grids, Self-knowledge
+ if Self rune is used.
+ Life: Heals monsters in affected grids, heals player if Self rune
+ is used.
+ Fire, Cold, Lightning, Acid: Casts magics of that element.
+ Elements: Irresistible damage.
+ Mind: A mind blast that badly effects intelligent monsters.
+ Temporary ESP if Self rune is used.
+ Gravity: A gravity spell that both does damage and whisks affected
+ creatures around.
+
+[[[[[BSpell Effects] (all are listed):
+ Self: Effects the caster. This rune can be used with any other;
+ if used alone, only the caster's grid is affected.
+ Arrow: Spell will include a bolt effect. This allows aiming.
+ Ray: Spell will include a beam effect. This allows aiming.
+ Increases difficulty slightly.
+ Sphere: Spell will end with a circular explosion. Increases
+ difficulty a bit. Can be used alone, or with Self, Arrow, or
+ Ray.
+ Power Surge: Not currently recommended for use. Increases
+ difficulty a lot.
+ Armageddon: Hurls down meteors of the magical type in the vicinity
+ of the caster. Increases difficulty noticeably, but can do a
+ great deal of damage.
+
+#####GStarting Equipment
+A Runecrafter begins the game with:
+ a Rune [Fire]
+ a Rune [Arrow]
+ a Dagger
+
diff --git a/lib/help/c_sorcer.txt b/lib/help/c_sorcer.txt
new file mode 100644
index 00000000..8a33184f
--- /dev/null
+++ b/lib/help/c_sorcer.txt
@@ -0,0 +1,68 @@
+|||||oy
+~~~~~01|Sorceror
+~~~~~02|Classes|Sorceror
+#####R=== Sorcerors ===
+
+#####GDescription
+Sorcerors are to magic what Unbelievers are to melee. They are the all-
+around best magicians, being able to cast spells from any school using
+only a single skill. They cannot comfortably use any weapon but a Mage Staff, and even with a Mage Staff, they will be unable to achieve anything remotely resembling battle effectiveness.
+
+The energies used for learning Sorcery also sap the life force of a
+Sorceror, penalising them with the loss of a percentage of their hit
+points equal to their Sorcery skill score and penalising their combat
+ability even more strongly.
+
+For more information on magic schools and spell effects see the
+*****magic.txt*01[Magic help file].
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 1.000 [1.000]
+ Magic-Device 1.000 [1.000]
+ Spell-power 0.000 [0.600]
+ Sorcery 1.000 [0.700]
+ Mana 0.000 [0.900]
+ Geomancy
+ Fire 0.000 [1.000]
+ Water 0.000 [1.000]
+ Air 0.000 [1.000]
+ Earth 0.000 [1.000]
+ Meta 0.000 [1.000]
+ Conveyance 0.000 [1.000]
+ Divination 0.000 [1.000]
+ Temporal 0.000 [1.000]
+ Mind 0.000 [1.000]
+ Nature 0.000 [1.000]
+ Necromancy 0.000 [1.000]
+ Runecraft 0.000 [0.900]
+ Thaumaturgy 0.000 [0.900]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*A Sorceror cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Sorceror begins the game with:
+ a Book of Beginner Cantrips
+ a Robe
+
diff --git a/lib/help/c_summon.txt b/lib/help/c_summon.txt
new file mode 100644
index 00000000..a3eca1de
--- /dev/null
+++ b/lib/help/c_summon.txt
@@ -0,0 +1,80 @@
+~~~~~01|Summoners
+~~~~~02|Classes|Summoners
+#####R=== Summoners ===
+
+#####GDescription
+A Summoner is, with one exception, a fairly weak class. While he starts
+out a decent enough fighter, his fighting skill doesn't improve that much,
+he doesn't cast magic and has little in the way of survival skills. However,
+this weakness doesn't trouble him much, because he can summon creatures to
+do his bidding and still gain experience from their kills.
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution 0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.600]
+ Weaponmastery 1.000 [0.600]
+ Archery 1.000 [0.400]
+Sneakiness 1.000 [0.700]
+ Stealth 1.000 [0.700]
+ Disarming 1.000 [0.500]
+Magic 1.000 [0.800]
+ Magic-Device 1.000 [1.000]
+Spirituality 1.000 [0.500]
+ Prayer 0.000 [0.500]
+ Music 0.000 [0.300]
+Monster-lore 16.000 [1.200]
+ Summoning 1.000 [0.700]
+ Corpse-preservation 1.000 [1.000]
+
+#####GStarting Equipment
+A Summoner starts the game with:
+ A Potion of Healing
+ A Short Sword
+ A Suit of Hard Leather Armour
+~~~~~03|Summoners|Summoning
+~~~~~04|Skills|Summoning - Summoning powers
+#####GSummoning
+[[[[[BThe summoner's powers of invocation are accessed using the 'm' key.]
+In order to invoke a monster, a summoner must possess a related totem.
+
+There are two kinds of summoning, decided by the totem used:
+
+[[[[[BPartial Summoning]
+Partial summoning creates a simulacrum of the monster, with little will
+of its own. The Summoner must maintain the simulacrum using mana; when
+he runs out, or stops paying the mana, the simulacrum vanishes.
+
+[[[[[BTrue Summoning]
+True summoning is quite different in effect from Partial Summoning, but the
+two are closely related in usage. A True Totem conjures a full copy of the
+creature in question; the process tends to make the monster a loyal ally to
+the summoner, but this loyalty is not guaranteed, nor is the survival of the
+totem used in the summoning.
+
+[[[[[BTotem Creation]
+In order to summon any creature, the Summoner needs a totem. Totems cannot be
+found; they must be created through a special process which involves taking
+the corpse of a creature (the summoner need not have been involved in the
+death of the creature in question), and extracting certain essences from it.
+
+The summoner can create a totem using the 'm' command, and unless one is
+working on a unique's corpse, must choose whether the resulting totem is a
+Partial or True Totem.
+
+[[[[[BImportant Note]
+Uniques are very willful creatures by definition, and thus have three special
+rules about their summoning:
+1. No partial totem can be made from a unique's corpse.
+2. The totem in question is always destroyed.
+3. The unique is twice as likely to be disloyal.
diff --git a/lib/help/c_swordm.txt b/lib/help/c_swordm.txt
new file mode 100644
index 00000000..a3b5ed05
--- /dev/null
+++ b/lib/help/c_swordm.txt
@@ -0,0 +1,52 @@
+~~~~~01|Swordmasters
+~~~~~02|Classes|Swordmasters
+#####R=== Swordmasters ===
+
+#####GDescription
+The Swordmaster is a Warrior who specialises in swords.
+
+The training is so intense and specific that Swordmasters gain huge bonuses
+when fighting with their chosen weapon, dependent on their Sword-mastery skill.
+In most other respects they perform about as well as a generic Warrior.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.900]
+ Weaponmastery 1.000 [0.950]
+ Sword-mastery 1.000 [0.700]
+ Axe-mastery 0.000 [0.300]
+ Hafted-mastery 0.000 [0.300]
+ Polearm-mastery 0.000 [0.300]
+ Archery 1.000 [0.600]
+ Antimagic 0.000 [0.550]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Magic 1.000 [0.300]
+ Magic-Device 1.000 [1.150]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread-blows 25
+
+#####GStarting Equipment
+A Swordmaster begins the game with:
+ a Ring of Fear Resistance
+ a Chain Mail
+ a Broad Sword
diff --git a/lib/help/c_symbia.txt b/lib/help/c_symbia.txt
new file mode 100644
index 00000000..9bbd92fd
--- /dev/null
+++ b/lib/help/c_symbia.txt
@@ -0,0 +1,68 @@
+|||||oy
+~~~~~01|Symbiant
+~~~~~02|Classes|Symbiant
+#####R=== Symbiants ===
+
+#####GDescription
+Symbiants live in harmony with all of the monsters that do not move:
+the Mushrooms, Molds, Floating Eyes, and such. They can hypnotise these
+creatures and then form a symbiotic relationship. Using other symbiotic
+powers, they can even use the magical abilities of their pets.
+
+Once hypnotised, the monster is placed onto the body, or "worn" in order to
+initiate the symbiotic relationship.
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution 0
+Charisma +1
+Hit Die +d8
+Spell Points +0%
+Exp Penalty 40%
+
+#####GStarting Skills
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.800]
+ Weaponmastery 1.000 [0.800]
+ Archery 1.000 [0.700]
+ Barehand-combat 1.000 [0.600]
+Sneakiness 1.000 [0.800]
+ Stealth 1.000 [0.800]
+ Disarming 1.000 [0.700]
+Magic 1.000 [0.700]
+ Magic-Device 1.000 [1.000]
+Spirituality 1.000 [0.500]
+ Prayer 0.000 [0.500]
+ Music 0.000 [0.300]
+Monster-lore 1.000 [1.100]
+ Corpse-preservation 1.000 [0.900]
+ Possession 0.000 [0.100]
+ Symbiosis 1.000 [0.800]
+
+#####GStarting Equipment
+A Symbiant begins the game with:
+ a Dagger
+ a Scroll of Summon Never-Moving Pet
+
+
+#####GSymbiosis
+Symbiants rely on the partnerships they form, hypnotising creatures
+and then "wearing" them: "I get by with a little help from my friends".
+Depending on the monster, this does nothing except protect the player
+slightly (the worn monster takes some of the damage the player would
+otherwise) or grant some very powerful attacks and summons.
+Their "spells" are used by the 'm' command; as their Symbiosis skill
+increases, they automatically gain more powers.
+For more detail about the specific powers, look here: *****m_symbio.txt*0[Symbiosis]
+~~~~~03|Symbiant|Naming your symbiote
+#####GNaming your symbiote
+For those sentimental Symbiants who like to name your symbiotes, you
+can use the Inscribe command '{'. Inscribe your hypnotized symbiote
+with "#named Fido" (or whatever name you choose, but don't forget the
+leading '#'), and it will be listed in your equipment as "a Red Mold
+named Fido", and the game will refer to your symbiote by name ("Fido
+is healed", for instance, rather than "Your Red mold is healed").
+
diff --git a/lib/help/c_thaum.txt b/lib/help/c_thaum.txt
new file mode 100644
index 00000000..653e84fa
--- /dev/null
+++ b/lib/help/c_thaum.txt
@@ -0,0 +1,84 @@
+~~~~~01|Thaumaturgist
+~~~~~02|Classes|Thaumaturgy
+#####R=== Thaumaturgists ===
+
+#####GDescription
+A Thaumaturgist is a Mage that prefers chaos to order. As such, they tend to
+learn random spells, and since attacking something creates more chaos than
+anything else, every spell they learn is an attack spell. They have no need
+for spell-books, as they harness the spells from within themselves. However,
+they also have no ability to improve the power of a particular spell - once
+learnt it will remain at the same strength for ever. Likewise, they also have
+no ability to control what they learn, and some of their spells will cause
+them damage (like darkness spells blinding them if they do not resist
+darkness).
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.200]
+ Weaponmastery 0.700 [0.400]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 3.000 [0.950]
+ Magic-Device 1.000 [1.050]
+ Spell-power 0.000 [0.600]
+ Mana 0.000 [0.600]
+ Geomancy
+ Fire 0.000 [0.700]
+ Water 0.000 [0.700]
+ Air 0.000 [0.700]
+ Earth 0.000 [0.700]
+ Meta 0.000 [0.700]
+ Conveyance 0.000 [0.700]
+ Divination 0.000 [0.700]
+ Temporal 0.000 [0.700]
+ Mind 0.000 [0.700]
+ Nature 0.000 [0.700]
+ Necromancy 0.000 [0.700]
+ Runecraft 0.000 [0.700]
+ Thaumaturgy 1.000 [1.000]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*A Thaumaturgist cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Thaumaturgist begins the game with:
+ a Book of Beginner Cantrips
+ a Dagger
+
+
+#####GThaumaturgy
+Thaumaturgists can wreak an amazing amount of destruction, and they don't
+even need spellbooks to do it. As they gain skill they memorise new,
+randomly generated attack spells. Note that they possess no guaranteed
+utility or alteration magics, and can never alter themselves.
+[[[[[BThaumaturgists just kill.]
+
+They use their magics through the 'm' key. They then select a general
+group of spells, followed by a specific spell. Thaumaturgists can learn
+more about a specific spell by browsing it; this is very useful for
+choosing the perfect spell for the occasion.
+
+Thaumaturgy spells can take the form of a bolt, a beam, a ball (either
+centred on the caster or targetable), an area (multiple balls in the
+vicinity of the caster), or a spell that affects all monsters in line of sight.
+
+You can find a little more information here: *****m_thaum.txt*0[Thaumaturgy].
diff --git a/lib/help/c_unbel.txt b/lib/help/c_unbel.txt
new file mode 100644
index 00000000..feec5723
--- /dev/null
+++ b/lib/help/c_unbel.txt
@@ -0,0 +1,65 @@
+~~~~~01|Unbeliever
+~~~~~02|Classes|Unbeliever
+#####R=== Unbelievers ===
+
+#####GDescription
+The full opposite of Sorcerors, Unbelievers so strongly despise magic
+that not only do they refuse to use magic spells, they refuse all
+training in the use of magic items, which leaves them almost totally
+incompetent when trying to use a magic item.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.800]
+ Weaponmastery 1.000 [0.850]
+ Sword-mastery 0.000 [0.400]
+ Axe-mastery 0.000 [0.400]
+ Hafted-mastery 0.000 [0.400]
+ Polearm-mastery 0.000 [0.400]
+ Archery 1.000 [0.600]
+ Antimagic 1.000 [0.650]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread blows 25
+
+#####GStarting Equipment
+An Unbeliever begins the game with:
+ a Ring of Fear Resistance
+ a Dark Sword
+ a Chain Mail
+
+~~~~~03|Unbelievers|Antimagic
+~~~~~04|Antimagic
+~~~~~05|Skills|Antimagic powers
+#####GAntimagic
+Thought carries power. And since Unbelievers think that magic doesn't
+exist, they can suppress its manifestation around them.
+Their magic-inhibiting ability and the area of effect around them are
+determined by the skill level in Antimagic. If they wield a Dark Sword,
+the strength and radius of the magic disrupting field are increased further,
+with best results if the blade is unenchanted.
+
+High levels of proficiency in Antimagic allow them also to stabilise the
+space-time continuum, so preventing teleportation, to sense the magical
+emanations coming from traps and to destroy these around them.
+These abilities are accessed using the 'm' key.
+
+
diff --git a/lib/help/c_warper.txt b/lib/help/c_warper.txt
new file mode 100644
index 00000000..55d16be5
--- /dev/null
+++ b/lib/help/c_warper.txt
@@ -0,0 +1,60 @@
+|||||oy
+~~~~~01|Warper
+~~~~~02|Classes|Warper
+#####R=== Warpers ===
+
+#####GDescription
+A Warper is a type of mage that prefers to deal in magic that alters the
+fabric of space and time. They specialise in the schools of *****m_convey.txt*0[Conveyance],
+*****m_divin.txt*0[Divination] and *****m_tempo.txt*0[Temporal] magic.
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Hit Die +d0
+Spell Points +50%
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.200]
+ Weaponmastery 0.700 [0.400]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.050]
+ Spell-power 1.000 [0.700]
+ Mana 1.000 [0.700]
+ Geomancy
+ Fire 0.000 [0.800]
+ Water 0.000 [0.800]
+ Air 0.000 [0.800]
+ Earth 0.000 [0.800]
+ Meta 0.000 [0.800]
+ Conveyance 0.000 [1.200]
+ Divination 0.000 [1.200]
+ Temporal 0.000 [1.200]
+ Mind 0.000 [0.700]
+ Nature 0.000 [0.800]
+ Necromancy 0.000 [0.700]
+ Runecraft 0.000 [0.700]
+ Thaumaturgy 0.000 [0.700]
+Spirituality 1.000 [0.550]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+*A Warper cannot learn the Geomancy skill, but it is shown in his skill
+screen because the elemental schools are sub-skills of it.
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1
+
+#####GStarting Equipment
+A Warper begins the game with:
+ a Book of Beginner Cantrips
+ a Dagger
diff --git a/lib/help/c_warrio.txt b/lib/help/c_warrio.txt
new file mode 100644
index 00000000..942b34bb
--- /dev/null
+++ b/lib/help/c_warrio.txt
@@ -0,0 +1,54 @@
+~~~~~01|Warrior
+~~~~~02|Classes|Warrior
+#####R=== Warriors ===
+
+#####GDescription
+A Warrior is a hack-and-slash character, who solves most of his problems
+by cutting them to pieces, but will occasionally fall back on the help
+of a magical device or a bow.
+
+A Warrior learns no magic, and can even suppress it like an Unbeliever through
+the Antimagic skill.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Hit Die +d9
+Spell Points +0%
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.800]
+ Weaponmastery 1.000 [0.850]
+ Sword-mastery 0.000 [0.400]
+ Axe-mastery 0.000 [0.400]
+ Hafted-mastery 0.000 [0.400]
+ Polearm-mastery 0.000 [0.400]
+ Archery 1.000 [0.600]
+ Antimagic 0.000 [0.550]
+Sneakiness 1.000 [0.900]
+ Stealth 0.000 [0.400]
+ Disarming 1.000 [0.900]
+Magic 1.000 [0.300]
+ Magic-Device 1.000 [1.150]
+Spirituality 1.000 [0.400]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1
+Spread blows 25
+
+#####GStarting Equipment
+A Warrior begins the game with:
+ a Ring of Fear Resistance
+ a Broad Sword
+ a Chain Mail
+
diff --git a/lib/help/command.txt b/lib/help/command.txt
new file mode 100644
index 00000000..0beffd33
--- /dev/null
+++ b/lib/help/command.txt
@@ -0,0 +1,1252 @@
+|||||oy
+~~~~~99|Commands
+#####R=== List of Commands ===
+
+
+ Angband commands are entered as an "underlying command" (a single key)
+plus a variety of optional or required arguments. You may choose how the
+"keyboard keys" are mapped to the "underlying commands" by choosing one of
+two standard "keysets", the "original" keyset or the "roguelike" keyset.
+
+ The original keyset is very similar to the "underlying" command set,
+with a few additions (such as the ability to use the numeric "directions" to
+"walk" or the "5" key to "stay still"). The roguelike keyset provides similar
+additions, and also allows the use of the h/j/k/l/y/u/b/n keys to "walk" (or,
+in combination with the shift or control keys, to run or tunnel), which thus
+requires a variety of key mappings to allow access to the underlying commands
+used for walking/running/tunneling. In particular, the "roguelike" keyset
+includes many more "capital" and "control" keys, as shown below.
+
+ Note that any keys that are not required for access to the underlying
+command set may be used by the user as "command macro" triggers (see below).
+You may always specify any "underlying command" directly by pressing backslash
+("\") plus the "underlying command" key. This is normally only used in "macro"
+definitions. [[[[[BYou may often enter "control-keys" as a caret ("^") plus the key]
+(so "^" + "p" often yields "^P").
+
+ Some commands allow an optional "repeat count", which allows you to tell
+the game that you wish to do the command multiple times, unless you press a
+key or are otherwise disturbed. To enter a "repeat count", type '0', followed
+by the numerical count, followed by the command. You must type "space" before
+entering certain commands. Skipping the numerical count yields a count of 99.
+An option allows certain commands (open, disarm, tunnel, etc) to auto-repeat.
+
+ Some commands will prompt for extra information, such as a direction, an
+inventory or equipment item, a spell, a textual inscription, the symbol of a
+monster race, a sub-command, a verification, an amount of time, a quantity,
+a file name, or various other things. Normally you can hit return to choose
+the "default" response, or escape to cancel the command entirely.
+
+ Some commands will prompt for a spell or an inventory item. Pressing
+space (or '*') will give you a list of choices. Pressing "-" (minus) selects
+the item on the floor. Pressing a lowercase letter selects the given item.
+Pressing a capital letter selects the given item after verification. Pressing
+a numeric digit '#' selects the first item (if any) whose inscription contains
+"@#" or "@x#", where "x" is the current "underlying command". You may only
+specify items which are "legal" for the command. Whenever an item inscription
+contains "!*" or "!x" (with "x" as above) you must verify its selection.
+
+ In ToME, there are items which occasionally teleport you away, asking
+for permission first. The recurring "Teleport (y/n)?" can be annoying, and
+this behavior can be eliminated by inscribing the object which causes the
+teleportation with "." (or any inscription containing the character ".").
+With this inscription, the object will no longer teleport you around nor
+keep asking you. If you want to restore the teleport ability to the object,
+just remove the "." from its inscription. Note that cursed items which
+teleport you are unaffected by the inscription.
+
+ Some commands will prompt for a direction. You may enter a "compass"
+direction using any of the "direction keys" shown below. Sometimes, you may
+specify that you wish to use the current "target", by pressing "t" or "5", or
+that you wish to select a new target, by pressing "*" (see "Target" below).
+~~~~~95
+#####G Original Keyset Directions Roguelike Keyset Directions
+
+ 7 8 9 y k u
+ 4 6 h l
+ 1 2 3 b j n
+
+ Each of the standard keysets provides some short-cuts over the "underlying
+commands". For example, both keysets allow you to "walk" by simply pressing
+an "original" direction key (or a "roguelike" direction key if you are using
+the roguelike keyset), instead of using the "walk" command plus a direction.
+[[[[[BThe roguelike keyset allows you to "run" or "tunnel" by simply holding the]
+[[[[[Bshift or control modifier key down while pressing a "roguelike" direction key,]
+[[[[[Binstead of using the "run" or "tunnel" command plus a direction.] Both keysets
+allow the use of the "5" key to "stand still", which is most convenient when
+using the original keyset.
+
+ Note that on many systems, it is possible to define "macros" (or "command
+macros") to various keys, or key combinations, so that it is often possible to
+make macros which, for example, allow the use of the shift or control modifier
+keys, plus a numeric keypad key, to specify the "run" or "tunnel" command, with
+the given direction, regardless of any keymap definitions, by using the fact
+that you can always, for example, use "\" + "." + "6", to specify "run east".
+~~~~~100|Commands|Original keyset
+#####R=== Original Keyset Command Summary (4.2.x) ===
+
+ *****command.txt*1[a Aim a wand] *****command.txt*2[A Activate an artifact]
+ *****command.txt*3[b Browse a book] *****command.txt*4[B Bash a door]
+ *****command.txt*5[c Close a door] *****command.txt*6[C Character description]
+ *****command.txt*7[d Drop an item] *****command.txt*8[D Disarm a trap]
+ *****command.txt*9[e Equipment list] *****command.txt*10[E Eat some food]
+ *****command.txt*11[f Fire (shoot) an item] *****command.txt*12[F Fuel your lantern/torch]
+ *****command.txt*13[g Stay still (flip pickup)] *****command.txt*14[G Gain new skills]
+ *****command.txt*15[h Hack up a corpse] *****command.txt*16[H Drink from a fountain]
+ *****command.txt*17[i Inventory list] *****command.txt*18[I Inspect (closely examine) an item]
+ *****command.txt*19[j Jam a door] J (unused)
+ *****command.txt*20[k Destroy an item] *****command.txt*21[K Cure meat]
+ *****command.txt*22[l Look around] *****command.txt*23[L Look around dungeon by sector]
+ *****command.txt*24[m Cast a spell / use mental power] *****command.txt*25[M Full dungeon map]
+ *****command.txt*85[n Repeat last command] *****command.txt*91[N Abilities Screen]
+ *****command.txt*26[o Open a door or chest] *****command.txt*27[O Sacrifice at an altar]
+ *****command.txt*28[p Pray to your god (if any)] *****command.txt*29[P Pet commands]
+ *****command.txt*30[q Quaff a potion] *****command.txt*31[Q Quit (commit suicide)]
+ *****command.txt*32[r Read a scroll] *****command.txt*33[R Rest for a period]
+ *****command.txt*34[s Search for traps/doors] *****command.txt*35[S Toggle search mode]
+ *****command.txt*36[t Take off equipment] *****command.txt*37[T Dig a tunnel]
+ *****command.txt*38[u Use a staff] *****command.txt*39[U Use bonus power (if any)]
+ *****command.txt*40[v Throw an item] *****command.txt*41[V Version Info]
+ *****command.txt*42[w Wear/wield equipment] W (unused)
+ *****command.txt*43[x Engrave the floor] X (unused)
+ *****command.txt*44[y Give item to monster] *****command.txt*96[Y Chat with a monster]
+ *****command.txt*45[z Zap a rod] *****command.txt*46[Z Steal]
+ *****command.txt*47[! Interact with system] *****debug.txt*101[^A (special - debug command)]
+ *****command.txt*49[@ Interact with macros] ^B (unused)
+ *****command.txt*89[# Begin extended command] ^C (special - break)
+ *****command.txt*97[$ Record macros] ^D (unused)
+ *****command.txt*51[% Interact with visuals] *****command.txt*52[^E Toggle choice window]
+ ^ (special - control key) *****command.txt*53[^F Repeat level feeling]
+ *****command.txt*54[& Interact with colors] ^G (unused)
+ *****command.txt*55[* Target monster or location] ^H (unused)
+ ( (unused) ^I (special - tab)
+ ) (unused) ^J (special - linefeed)
+ *****command.txt*58[{ Inscribe an object] ^K (unused)
+ *****command.txt*59[} Uninscribe an object] ^L (unused)
+ [ (unused) ^M (special - return)
+ ] (unused) ^N (unused)
+ *****command.txt*60[- Walk (flip pickup)] ^O (unused)
+ *****command.txt*61[_ Re-Enter store] *****command.txt*62[^P Show previous messages]
+ *****command.txt*63[+ Alter grid] *****command.txt*64[^Q Quit to next midi song]
+ *****command.txt*65[= Set options] *****command.txt*66[^R Redraw the screen]
+ *****command.txt*67[; Walk (with pickup)] *****command.txt*68[^S Save and don't quit]
+ *****command.txt*69[: Take notes] *****command.txt*70[^T Time of the day]
+ ' (unused) ^U (unused)
+ *****command.txt*71[" Enter a user pref command] ^V (unused)
+ *****command.txt*72[, Stay still (with pickup)] ^W (special - wizard mode)
+ *****command.txt*74[< Go up staircase] *****command.txt*75[^X Save and quit]
+ *****command.txt*76[. Run] ^Y (unused)
+ *****command.txt*77[> Go down staircase] ^Z (special - borg command)
+ *****command.txt*79[\ (special - bypass keymap)] *****command.txt*80[| Do cmovies]
+ *****command.txt*81[` (special - escape)] *****command.txt*82[~ Display current knowledge]
+ *****command.txt*83[/ Identify symbol] *****command.txt*84[? Help]
+ *****command.txt*98[^\] Take an html screenshot]
+
+
+~~~~~101|Commands|Roguelike keyset
+#####R=== Roguelike Keyset Command Summary (4.2.x) ===
+
+ *****command.txt*45[a Zap a rod (Activate)] *****command.txt*2[A Activate an artifact]
+ *****command.txt*95[b (walk - south west)] *****command.txt*95[B (run - south west)]
+ *****command.txt*5[c Close a door] *****command.txt*6[C Character description]
+ *****command.txt*7[d Drop an item] *****command.txt*8[D Disarm a trap or chest]
+ *****command.txt*9[e Equipment list] *****command.txt*10[E Eat some food]
+ *****command.txt*4[f Bash a door (force)] *****command.txt*12[F Fuel your lantern/torch]
+ *****command.txt*13[g Stay still (flip pickup)] *****command.txt*14[G Gain new skills]
+ *****command.txt*95[h (walk - west)] *****command.txt*95[H (run - west)]
+ *****command.txt*17[i Inventory list] *****command.txt*18[I Observe an item]
+ *****command.txt*95[j (walk - south)] *****command.txt*95[J (run - south)]
+ *****command.txt*95[k (walk - north)] *****command.txt*95[K (run - north)]
+ *****command.txt*95[l (walk - east)] *****command.txt*95[L (run - east)]
+ *****command.txt*24[m Spell casting / mental power] *****command.txt*25[M Full dungeon map]
+ *****command.txt*95[n (walk - south east)] *****command.txt*95[N (run - south east)]
+ *****command.txt*26[o Open a door or chest] *****command.txt*39[O Use bonus power (if any)]
+ *****command.txt*28[p Pray to your god (if any)] *****command.txt*3[P Browse a book]
+ *****command.txt*30[q Quaff a potion] *****command.txt*31[Q Quit (commit suicide)]
+ *****command.txt*32[r Read a scroll] *****command.txt*33[R Rest for a period]
+ *****command.txt*34[s Search for traps/doors] *****command.txt*97[S Record macros]
+ *****command.txt*11[t Fire an item] *****command.txt*36[T Take off equipment]
+ *****command.txt*95[u (walk - north east)] *****command.txt*95[U (run - north east)]
+ *****command.txt*40[v Throw an item] *****command.txt*16[V Drink from a fountain]
+ *****command.txt*42[w Wear/wield equipment] *****command.txt*23[W Locate player on map (Where)]
+ *****command.txt*22[x Look around] *****command.txt*29[X Pet commands]
+ *****command.txt*95[y (walk - north west)] *****command.txt*95[Y (run - north west)]
+ *****command.txt*1[z Aim a wand (Zap)] *****command.txt*38[Z Use a staff (Zap)]
+ *****command.txt*47[! Interact with system] ^A (special - debug command)
+ *****command.txt*49[@ Interact with macros] *****command.txt*95[^B (tunnel - south west)]
+ *****command.txt*35[# Toggle search mode] ^C (special - break)
+ *****command.txt*15[$ Hack up a corpse] *****command.txt*20[^D Destroy item]
+ *****command.txt*51[% Interact with visuals] *****command.txt*52[^E Toggle choice window]
+ ^ (special - control key) *****command.txt*53[^F Repeat level feeling]
+ *****command.txt*54[& Interact with colors] *****command.txt*27[^G Sacrifice at an altar]
+ *****command.txt*55[* Target monster or location] *****command.txt*95[^H (tunnel - west)]
+ *****command.txt*96[( Chat] ^I (special - tab)
+ *****command.txt*89[) Begin extended command] *****command.txt*95[^J (tunnel - south)]
+ *****command.txt*58[{ Inscribe an object] *****command.txt*95[^K (tunnel - north)]
+ *****command.txt*59[} Uninscribe an object] *****command.txt*95[^L (tunnel - east)]
+ [*****command.txt*46[ Steal] *****command.txt*95[^M (tunnel - south)]
+ ]*****command.txt*43[ Engrave the floor] *****command.txt*95[^N (tunnel - south east)]
+ *****command.txt*60[- Walk (flip pickup)] *****command.txt*21[^O Cure meat]
+ *****command.txt*61[_ Enter store] *****command.txt*62[^P Show previous messages]
+ *****command.txt*63[+ Alter grid] *****command.txt*64[^Q Quit to next midi song]
+ *****command.txt*65[= Set options] *****command.txt*66[^R Redraw the screen]
+ *****command.txt*67[; Walk (with pickup)] *****command.txt*68[^S Save and don't quit]
+ *****command.txt*69[: Take notes] *****command.txt*37[^T Dig a Tunnel]
+ *****command.txt*44[' Give object to monster] *****command.txt*95[^U (tunnel - north east)]
+ *****command.txt*71[" Enter a user pref command] ^V (unused)
+ *****command.txt*76[, Run] ^W (special - wizard mode)
+ *****command.txt*74[< Go up staircase] *****command.txt*75[^X Save and quit]
+ *****command.txt*72[. Stay still (with pickup)] *****command.txt*95[^Y (tunnel - north west)]
+ *****command.txt*77[> Go down staircase] ^Z (special - borg command)
+ *****command.txt*79[\ (special - bypass keymap)] *****command.txt*80[| Do cmovies]
+ *****command.txt*81[` (special - escape)] *****command.txt*82[~ Display current knowledge]
+ *****command.txt*83[/ Identify symbol] *****command.txt*84[? Help]
+
+~~~~~102|Commands|Special keys
+#####R=== Special Keys ===
+
+ Certain special keys may be intercepted by the operating system or
+the host machine, causing unexpected results. In general, these special keys
+are control keys, and often, you can disable their special effects.
+
+ If you are playing on a UNIX or similar system, then Ctrl-C will
+interrupt ToME. The second and third interrupt will induce a warning
+bell, and the fourth will induce both a warning bell and a special message,
+since the fifth will quit the game, after killing your character. Also,
+Ctrl-Z will suspend the game, and return you to the original command shell,
+until you resume the game with the "fg" command. There is now a compilation
+option to force the game to prevent the "double ctrl-z escape death trick".
+The Ctrl-\ and Ctrl-D and Ctrl-S keys should not be intercepted.
+
+ It is often possible to specify "control-keys" without actually
+pressing the control key, by typing a caret ("^") followed by the key.
+This is useful for specifying control-key commands which might be caught
+by the operating system as explained above.
+~~~~~79
+ Pressing [[[[[Gbackslash ("\\")] before a command will bypass all keymaps,
+and the next keypress will be interpreted as an "underlying command" key,
+unless it is a caret ("^"), in which case the keypress after that will be
+turned into a control-key and interpreted as a command in the underlying
+ToME keyset. The backslash key is useful for creating macro actions
+which are not affected by any keymap definitions that may be in force, for
+example, the sequence "\" + "." + "6" will always mean "run east", even if
+the "." key has been mapped to a different underlying command.
+
+ The "0" and "^" and "\" keys all have special meaning when entered
+at the command prompt, and there is no "useful" way to specify any of them
+as an "underlying command", which is okay, since they would have no effect.
+~~~~~81
+ For many input requests or queries, the [[[[[Gspecial character ESCAPE]
+will abort the command. The "[y/n]" prompts may be answered with "y" or
+"n", or escape. The "-more-" message prompts may be cleared (after reading
+the displayed message) by pressing ESCAPE, SPACE, RETURN, LINEFEED, or by
+any keypress, if the "quick_messages" option is turned on.
+~~~~~103|Commands|Command counts
+~~~~~104|Commands|Repeating a command
+#####R=== Command Counts ===
+
+ Some commands can be executed a fixed number of times by preceding
+them with a count. Counted commands will execute until the count expires,
+until you type any character, or until something significant happens, such
+as being attacked. Thus, a counted command doesn't work to attack another
+creature. While the command is being repeated, the number of times left
+to be repeated will flash by on the line at the bottom of the screen.
+
+ [[[[[BTo give a count to a command, type 0, the repeat count, and then]
+[[[[[Bthe command.] If you want to give a movement command and you are using the
+original command set (where the movement commands are digits), press space
+after the count and you will be prompted for the command.
+
+ Counted commands are very useful for searching or tunneling, as
+they automatically terminate on success, or if you are attacked. You may
+also terminate any counted command (or resting or running), by typing any
+character. This character is ignored, but it is safest to use a SPACE or
+ESCAPE which are always ignored as commands in case you type the command
+just after the count expires.
+
+ You can tell ToME to automatically use a repeat count of 99
+with commands you normally want to repeat (open, disarm, tunnel, bash,
+alter, etc) by setting the "always_repeat" option.
+
+
+#####R=== Selection of Objects ===
+
+ Many commands will also prompt for a particular object to be used.
+For example, the command to read a scroll will ask you which of the
+scrolls that you are carrying that you wish to read. In such cases, the
+selection is made by typing a letter of the alphabet. The prompt will
+indicate the possible letters, and will also allow you to type the key
+"*", which causes all of the available options to be described. The list
+of choices will also be shown in the Choice window, if you are using a
+windows environment and windows are turned on. Often you will be able to
+press "/" to select an object from your equipment instead of your
+inventory. Pressing space once will have the same effect as "*", and
+the second time will cancel the command and run the "i" or "e" command.
+
+ [[[[[BThe particular object may be selected by an upper case or a lower]
+[[[[[Bcase letter. If lower case is used, the selection takes place]
+[[[[[Bimmediately. If upper case is used, then the particular option is]
+[[[[[Bdescribed, and you are given the option of confirming or retracting that]
+[[[[[Bchoice.] Upper case selection is thus safer, but requires an extra key
+stroke. Also see the "!*" and "!x" inscriptions, below.
+
+ For many commands, [[[[[Byou can also use "-" to select an object on the]
+[[[[[Bfloor where you are standing.] This lets you read scrolls or quaff
+potions, for example, off the dungeon floor without picking them up.
+~~~~~90
+ If you enter a number between 0 and 9, the first item engraved
+with "@#" where "#" is the number you entered will be selected. For example,
+if you have a shovel engraved with "@0" and you type "w" (for wield) and
+then 0, you will wield the shovel. This is very useful for macros (see
+below), since you can use this to select an object regardless of its
+location in your pack. Multiple numbers can be engraved on the same object; for
+example, if a sword is engraved with @1@0, then either "w1" or "w0" will
+wield it. Normally, you inscribe "@1@0" on your primary weapon, and
+"@2@0" on your secondary weapon. [[[[[BNote that an inscription containing]
+[[[[[B"@x#" will act like "@#" but only when the current "ToME command"]
+[[[[[Bis "x".] Thus you can put "@z4" on a rod and "@u4" on a staff, and then
+use both "z4" and "u4" as desired.
+
+ Note that any object containing "!x" in its inscription, where
+"x" is the current "ToME command" (or containing "!*" ever) will induce
+"verification" whenever that object is "selected". Thus, inscribing, say,
+"!f!k!d" on an object will greatly reduce the odds of you "losing" it by
+accident, and [[[[[Binscribing "!*" on an object] will allow you to be very paranoid
+about the object. Note that "selling" and "dropping" both use the "d" command.
+
+~~~~~105|Pref files
+#####R=== User Pref Files ===
+
+ ToME allows you to change various aspects of the game to suit
+your tastes. You may define keymaps (changing the way ToME maps your
+keypresses to underlying commands), create macros (allowing you to map a
+single keypress to a series of keypresses), modify the visuals (allowing
+you to change the appearance of monsters, objects, or terrain features),
+change the colors (allowing you to make a given color brighter, darker,
+or even completely different), or set options (turning them off or on).
+
+ ToME stores your preferences in files called "user pref files",
+which contain comments and "user pref commands", which are simple strings
+describing one aspect of the system about which the user has a preference.
+There are many ways to load a user pref file, and in fact, some of these
+files are automatically loaded for you by the game. All of the files are
+kept in the "lib/user/" directory, though you may have to use one of the
+command line arguments to redirect this directory, especially on multiuser
+systems. You may also enter single user pref commands directly, using the
+special "Enter a user pref command" command, activated by "double quote".
+You may have to use the "redraw" command (^R) after changing certain of
+the aspects of the game, to allow ToME to adapt to your changes.
+
+ When the game starts up, after you have loaded an old character,
+or created a new character, some user pref files are loaded automatically.
+First, the "pref.prf" file is loaded. This file contains some user pref
+commands which will work on all platforms. Then one of "font-xxx.prf"
+(for normal usage) or "graf-xxx.prf" (for bitmap usage) is loaded. These
+files contain attr/char changes to allow the monsters, objects, and/or
+terrain features to look "better" on your system. Then the "pref-xxx.prf"
+file is loaded. This file contains pre-defined system specific stuff
+(macros, color definitions, etc). Then, the "user-xxx.prf" file is loaded.
+This file contains user-defined system specific stuff. The "user-xxx.prf"
+file is used as the "default" user pref file in many places. The "xxx" is
+the "system suffix" for your system, taken from the "main-xxx.c" file which
+was used to generate your executable. Finally, the "Race.prf", "Class.prf",
+and "Name.prf" files are loaded, where "Race", "Class", and "Name" are
+replaced by the actual race, class, and name of the current character.
+
+ Several commands allow you to both load existing user pref files,
+create new user pref files, append information to existing user pref files,
+and/or interact with various of the user preferences in a more intuitive
+way than the user pref commands allow. The commands include "Interact with
+macros" (@), "Interact with visuals" (%), and "Interact with colors" (&),
+described below.
+~~~~~106|Pref files|Macros
+#####G--- User Pref Files (Macros) ---
+
+ The "Interact with macros" command allows you to define or remove
+"macros", which are mappings from a single logical keypress to a sequence
+of keypresses, allowing you to use special keys on the keyboard, such as
+function keys or keypad keys, possibly in conjunction with modifier keys,
+to "automate" repetitive multi-keypress commands that you use a lot.
+
+ Since macros represent keypress sequences, and not all keypresses
+have a printable representation, macro triggers and actions must often be
+"encoded" into a human readable form. This is done using several types
+of encoding, including "\xHH" for character number HH in hexidecimal, "\e"
+for the "escape" code, "\n" for the "newline" code, "\r" for the "return"
+code, "\s" for the "space" code, "\\" for backslash, "\^" for caret, and
+"^X" for the code for any "control" key "ctrl-X". Note that the "action"
+of a macro will not be checked against other macro triggers (unless the
+macro action contains a "control-backslash"), so you cannot make infinite
+loops. You may specify extremely long macros, but you are limited in
+length by the underlying input mechanisms, which in general limit you
+to about 1024 keys in both triggers and actions.
+
+ The special "\" command (which must be encoded in macros as "\\")
+is very useful in macros, since it bypasses all keymaps and allows the next
+keystroke to be considered a command in the underlying ToME command set.
+For example, a macro which maps Shift-KP6 to "\" + "." + "6" will induce
+the "run east" behavior, regardless of what keyset the user has chosen, and
+regardless of what keymaps have been defined.
+
+ Macros can be specified in user pref files as a pair of lines, one
+of the form "A:<str>", which defines the encoded macro action, and one of
+the form "P:<str>", which defines the encoded macro trigger.
+
+ A [[[[[Bcommon example of a macro] to cast the first spell in your first spell
+book at the nearest monster would be: \e\e\em1a*t where \e is an escape (to make
+sure you are not still within another command), m1 selects the spell book that
+is inscribed ({) with @m1, a selects the first spell in that book, and *t targets
+the nearest monster.
+
+ More detailed information about specific macros can be found in
+*****macrofaq.txt*0[macrofaq.txt], originally written by Jim Lyon (jplyon@attglobal.net),
+modified for ToME with Jim's permission by Dawnmist
+(angband@dawnmist.8m.com).
+~~~~~107|Pref files|Keymaps
+#####G--- User Pref Files (Keymaps) ---
+
+ The "Interact with macros" command also allows you to define
+"keymaps", which are vaguely related to macros. A keymap maps a single
+keypress to a series of keypresses, which bypass both other keymaps and
+any macros. ToME uses keymaps to map the original and the roguelike
+keysets to the underlying command set, and allows the user to modify or
+add keymaps of their own. Note that all keymap actions must be specified
+using underlying commands, not keypresses from the original or roguelike
+keysets. The original keyset is almost identical to the underlying keyset,
+except that "numbers" are mapped to ";" plus a direction, "5" is mapped to
+",", and a few control-keys are mapped to various things. See "command.txt"
+for the full set of underlying commands. Some uses for keymaps include the
+ability to "disable" a command by mapping it to "\x00",
+
+ Keymaps can be specified in user pref files as line of the form
+"M:<T> <key> <str>", where <T> is the keyset (0/1 for original/roguelike),
+<key> is the encoded trigger key, and <str> is the encoded keymap action.
+~~~~~108|Pref files|Visuals
+#####G--- User Pref Files (Visuals) ---
+
+ You can use the "Interact with visuals" command to change various
+visual information, currently including the choice of what attr/char values
+are used to represent various monsters, objects, or terrain features. Note
+that in combination appropriate support in "main-xxx.c", and with the use of
+the "use_graphics" flag, you may be able to specify that "graphic bitmaps"
+should be used instead of normal "colored characters" for various things.
+
+ When interactively modifying the attr/char values for monsters,
+objects, or terrain features, pressing "n" or "N" will change which entry
+you are changing, pressing "a" or "A" will rotate through the available
+attr values, and pressing "c" or "C" will rotate though the available char
+values. Note that attr/char values with the "high bit" set may induce the
+display of special "graphic" pictures if the "use_graphics" flag is set,
+and your system supports the "use_graphics" flag.
+
+ Note that this command can be abused in various ways, and if you
+must do so, remember that you are only cheating yourself.
+
+ Keymaps can be specified in user pref files as line of the form
+"R:<N>:<A>/<C>" or "K:<N>:<A>/<C>" or "F:<N>:<A>/<C>" or "U:<N>:<A>/<C>".
+~~~~~109|Pref files|Colors
+#####G--- User Pref Files (Colors) ---
+
+ The "Interact with colors" command allows you to change the actual
+internal values used to display various colors. This command may or may
+not have any effect on your machine. Advanced machines may allow you to
+change the actual RGB values used to represent each of the 16 colors used
+by ToME, and perhaps even allow you to define new colors which are not
+currently used by ToME.
+
+ Colors can be specified in user pref files as line of the form
+"V:<N>:<V>:<R>:<G>:<B>".
+~~~~~110|Pref files|Options
+#####G--- User Pref Files (Options) ---
+
+ The "Interact with options" command allows you to turn options
+on or off. You may turn options off or on using the user pref commands
+of the form "X:<option>" or "Y:<option>" respectively.
+
+~~~~~111|Commands|Command descriptions
+#####R=== Command Descriptions ===
+
+ The following command descriptions are listed as the command name
+plus the "underlying command" key. This is followed by the command name
+and "roguelike" keyset key, if different from the underlying command key.
+Then comes a brief description of the command, including information about
+alternative methods of specifying the command in each keyset, when needed.
+Several commands (tunnel, disarm, bash, open) are repeated 99 times if the
+"always_repeat" option is set and no repeat count is given. Some commands
+use the "repeat count" to automatically repeat the command several times,
+while others use the "repeat count" as an "argument", for example, commands
+which need a "quantity" will use the "repeat count" instead of asking for
+a quantity, allowing the use of "0d" for "drop all". Commands which ask
+for a quantity will convert any "letters" into the maximal legal value.
+~~~~~112|Commands|Inventory
+#####R--- Inventory Commands ---
+~~~~~17
+[[[[[GInventory list (i)]
+ Displays a list of objects being carried but not equipped. You
+ can carry up to 23 different items, not counting those in your
+ equipment. Often, many identical objects can be "stacked" into
+ a "pile" which will count as a single item. This is always
+ true of things like potions, scrolls, and food, but you may have
+ to set options to allow wands, staves, and other such objects to
+ stack. Each object has a weight, and if you carry more objects
+ than your strength permits, you will begin to slow down.
+~~~~~9
+[[[[[GEquipment list (e)]
+ Use this command to display a list of the objects currently being
+ used by your character. The number and type of available slots for
+ equipment may vary. A human for example has 15 slots for equipment,
+ each corresponding to a different location on the body, and each of
+ which may contain only a single object at a time, and each of which
+ may only contain objects of the proper "type".
+ If the option "show_labels" is set, the slots are labelled as follows:
+ Wielding (weapon), Shooting (missile launcher or instruments),
+ On finger (ring), Around neck (amulet), Light source (light source),
+ On body (armor), About body (cloak), On arm (shield), On head (helmet),
+ On hands (gloves), On feet (boots), Carrying (symbiote), Quiver (ammo),
+ Using (tool). You must be using an object to receive any of its special
+ powers.
+~~~~~7
+[[[[[GDrop an item (d)]
+ This command drops an item from your inventory or equipment onto the
+ dungeon floor. If the place you are standing on already has objects
+ in it, ToME will attempt to drop the item onto an adjacent space.
+ A floor spot can hold more than one object, but there is still the
+ possibility that if the floor is too full and you attempt to drop
+ something, it may disappear and be destroyed. If the selected pile
+ contains multiple items, you may specify a quantity.
+~~~~~20
+[[[[[GDestroy an item (k) or Destroy an item (^D)]
+ This destroys an item in your inventory or on the dungeon floor.
+ If the selected pile contains multiple objects, you may specify
+ a quantity. You must always verify this command, unless the item
+ is cursed or worthless and the option "auto_destroy" is set.
+~~~~~42
+[[[[[GWear/Wield equipment (w)]
+ To wear or wield an object in your inventory, use this command.
+ Since only one object can be in each slot at a time, if you wear
+ or wield an item into a slot which is already occupied, the old
+ item will be first be taken off, and may in fact be dropped if
+ there is no room for it in your inventory.
+~~~~~36
+[[[[[GTake off equipment (t) or Take off equipment (T)]
+ Use this command to take off a piece of equipment and return it
+ to your inventory. Occasionally, you will run into a cursed item
+ which cannot be removed. These items normally penalise you in some
+ way and cannot be taken off until the curse is removed. If there
+ is no room in your inventory for the item, your pack will overflow
+ and you will drop the item after taking it off.
+~~~~~113|Commands|Movement
+#####R--- Movement Commands ---
+~~~~~67
+[[[[[GWalk (with pickup) (;)]
+ Moves one step in the given direction. The square you are moving
+ into must not be blocked by walls or doors. You will pick up any
+ items in the destination grid if the "always_pickup" option is set,
+ or if the "query_pickup" option is set and you respond correctly.
+ This command can take a count and requires a direction. You may
+ also use the "original" direction keys (both keysets) or the
+ "roguelike" direction keys (roguelike keyset) to walk in a
+ direction.
+~~~~~60
+[[[[[GWalk (flip pickup) (-)]
+ This is just like normal move, except that the "Pick things up"
+ option is inverted. In other words, if you normally pick up
+ anything you encounter (the default), you will not pick things up
+ when using this command. If you normally do not pick things up,
+ you will when using this command. This command can take a count
+ and requires a direction.
+~~~~~76
+[[[[[GRun (.) or Run (,)]
+ This command will move in the given direction, following any bends
+ in the corridor, until you either have to make a "choice" between
+ two directions or you are disturbed. You can configure what will
+ disturb you by setting the disturbance options. Run requires a
+ direction. You may also use shift plus the "roguelike" direction
+ keys (roguelike keyset), or shift plus the "original" direction keys
+ on the keypad (both keysets, some machines) to run in a direction.
+~~~~~74
+[[[[[GGo up staircase (<)]
+ Climbs up an up staircase you are standing on. There is always at
+ least one staircase going up on every level (this doesn't mean it's
+ easy to find) except for the surface, where '<' will bring up the
+ wilderness map. Going up a staircase will take you to a new dungeon
+ level unless you are at the first level of the dungeon, in which case
+ you will return to the surface. Note that whenever you leave a dungeon
+ level, you will never find it again, unless the "permanent_levels"
+ option is set or the level contains a dungeon town. This means that
+ for all intents and purposes, any objects on that level are destroyed.
+ This includes unknown artifacts unless the "Create characters in
+ preserve mode" option was set when your character was created, in which
+ case the artifacts may show up again later.
+~~~~~77
+[[[[[GGo down staircase (>)]
+ Descends a down staircase you are standing on. There are always
+ at least two staircases going down on each level, except for the
+ last level of a dungeon, and some "quest" levels, which have none until
+ the quest monsters are killed. Going down a staircase will take you
+ to a new dungeon level. See "Go Up Staircase" for more info.
+
+ This command is also used to enter Void Jumpgates, and to zoom in from
+ the wilderness map.
+~~~~~114|Commands|Resting
+#####R--- Resting Commands ---
+~~~~~72
+[[[[[GStay still (with pickup) (,) or Stay still (with pickup) (.)]
+ Stays in the same square for one move. If you normally pick up
+ objects you encounter, you will pick up whatever you are standing
+ on. This command can take a count. You may also use the "5" key
+ (both keysets).
+~~~~~13
+[[[[[GStay still (flip pickup) (g)]
+ Stays in the same square for one move. If you normally pick up
+ objects you encounter, you will not pick up whatever you are
+ standing on. If you normally do not pick up objects, you will
+ pick up what you are standing on. This command is normally only
+ used when the "always_pickup" option is false. This command can
+ take a count.
+~~~~~33
+[[[[[GRest (R)]
+ Resting is better for you than repeatedly staying still, and can
+ be told to automatically stop after a certain amount of time, or
+ when various conditions are met. In any case, you always wake up
+ when anything disturbing happens, or when you press any key. To
+ rest, enter the Rest command, followed by the number of turns you
+ want to rest, or "*" to rest until your hit points and mana are
+ restored, or "&" to rest until you are fully "healed". This command
+ can take a count, which is used for the number of turns to rest.
+~~~~~61
+[[[[[GEnter store (_)]
+ When standing on the door of a store, this command allows the character
+ to enter the store again.
+~~~~~115|Commands|Searching
+#####R--- Searching Commands ---
+~~~~~34
+[[[[[GSearch (s)]
+ This command can be used to locate hidden traps and secret doors
+ in the spaces adjacent to the player. More than a single turn of
+ searching will be required in most cases. You should always
+ search a chest before trying to open it, since they are generally
+ trapped. This command can take a count, which is useful if you
+ are fairly sure of finding something eventually, since the command
+ stops as soon as anything is found. This command can take a count.
+~~~~~35
+[[[[[GToggle search mode (S) or Toggle search mode (#)]
+ This command will take you into and out of search mode. When
+ first pressed, the message "Searching" will appear at the bottom
+ of the screen. You are now taking two turns for each command, one
+ for the command and one turn to search. This means that you are
+ taking twice the time to move around the dungeon, and therefore
+ twice the food. Search mode will automatically turn off if you
+ are disturbed. You may also turn off search mode by entering the
+ Search Mode command again.
+~~~~~116|Commands|Alteration commands
+~~~~~117|Commands|Terrain interaction
+#####R--- Alter Commands ---
+~~~~~37
+[[[[[GTunnel (T) or Tunnel (^T)]
+ Tunnelling or mining is a very useful art. There are many kinds of
+ rock, with varying hardness, including permanent rock (permanent),
+ granite (very hard), quartz veins (hard), magma veins (soft), and
+ rubble (very soft). Quartz and Magma veins may be displayed in a
+ special way, and may sometimes contain treasure, in which case they
+ will be displayed in a different way. Rubble sometimes covers an
+ object. It is only possible to tunnel if you are wielding a digging
+ tool such as a shovel or a pick. Tunnelling ability increases with
+ strength and tool weight. This command can take a count, requires a
+ direction, and is affected by the "always_repeat" option.
+~~~~~26
+[[[[[GOpen a door or chest (o)]
+ To open an object such as a door or chest, you must use this
+ command. If the object is locked, you will attempt to pick the
+ lock based on your disarming ability. If you open a trapped chest
+ without disarming the traps first, the trap will be set off. Some
+ doors will be jammed shut and may have to be forced open. You may
+ need several tries to open a door or chest. Open can take a count,
+ requires a direction, and is affected by the "always_repeat" option.
+~~~~~5
+[[[[[GClose a door (c)]
+ Non-intelligent and some other creatures cannot open doors, so
+ shutting doors can be quite valuable. Broken doors cannot be closed.
+ Bashing a door open may break it. Close can take a count, requires a
+ direction, and is affected by the "always_repeat" option.
+~~~~~19
+[[[[[GJam a door (j) or Spike a door (S)]
+ Many monsters can simply open closed doors, and can eventually
+ get through a locked door. You may therefore occasionally want
+ to jam a door shut with iron spikes. Each spike used on the door
+ will make it harder to bash down the door, up to a certain limit.
+ Smaller monsters are less able to bash down doors. In order to
+ use this command, you must be carrying iron spikes. Jam or Spike
+ requires a direction.
+~~~~~4
+[[[[[GBash a door (B) or Force a door (f)]
+ This command allows you to bash down jammed doors. Your bashing
+ ability increases with strength. Bashing open a door can (briefly)
+ throw you off balance. Doors that are stuck, or which have been
+ jammed closed with spikes can only be opened by bashing, and all
+ closed doors can be bashed open if desired. Bashing a door open
+ may permanently break it so that it can never be closed. Bash or
+ Force can take a count, requires a direction, and is affected by
+ the "always_repeat" option.
+~~~~~8
+[[[[[GDisarm a trap or chest (D)]
+ You can attempt to disarm traps on the floor or on chests. If you
+ fail, there is a chance that you will blunder and set it off. You
+ can only disarm a trap after you have found it (usually with the
+ Search command). Disarm can take a count, requires a direction,
+ and is affected by the "always_repeat" option.
+~~~~~63
+[[[[[GAlter (+)]
+ This special command allows the use of a single keypress to select
+ any of the "obvious" commands above (attack, tunnel, bash, open,
+ disarm, close), and, by using macros or keymaps, to combine this
+ keypress with directions. In general, this allows the use of the
+ "control" key plus the appropriate "direction" key (including the
+ roguelike direction keys in roguelike mode) as a kind of generic
+ "alter the terrain feature of an adjacent grid" command. Alter
+ can take a count, requires a direction, and is affected by the
+ "always_repeat" option.
+~~~~~43
+[[[[[GEngrave the floor (x)]
+ The dungeon is full of magics, and as such pools of it collect on
+ the floor in places. With the "inscribe" command, it is possible
+ to create some spell effects by inscribing words you have read from
+ various parchments detailing the languages used within the dungeon.
+ Then, if there is enough mana collected on that square, walking over
+ the inscription will trigger the spell. Some spells can be triggered
+ only by the player, some only by monsters, and some are triggered by
+ both.
+~~~~~46
+[[[[[GSteal (Z)]
+ Allows the player to try to steal items from shops. Also allows
+ rogues to steal from monsters.
+~~~~~118|Commands|Spells and prayers
+#####R--- Spell and Prayer Commands ---
+~~~~~3
+[[[[[GBrowse a book (b) or Peruse a book (P)]
+ Only characters with some knowledge in the magic schools, such as
+ mages, priests, rogues, and rangers, can read magic spellbooks.
+ Warriors normally cannot read any books. When this command is used,
+ all of the spells or prayers contained in the selected book are
+ displayed, along with information such as their level, the amount of
+ mana or piety required to cast them, and whether or not you know the
+ spell or prayer.
+~~~~~14
+[[[[[GGain new skills (G)]
+ Use this command to access the skills menu and spend the skill points
+ you gain at each new character level to increase the range of things
+ your character is able to do.
+~~~~~91
+[[[[[GGain new abilities (N)]
+ Use this command to access the Abilities menu and spend the skill points
+ you gain at each new character level to increase the range of things
+ your character is able to do.
+~~~~~24
+[[[[[GCast a spell / Pray a prayer (m) / Use a mental power]
+ To cast a spell or prayer, you must have the skill level required in
+ that school of magic to be able access that spell, and a book that
+ contains that spell in your inventory (for most schools). Each spell
+ has a chance of failure which starts out fairly large but decreases
+ as you gain levels. If you don't have enough mana to cast a spell,
+ you will be told you do not have enough mana to cast it. Since in most
+ cases you must read the spell from a book, you cannot be blind or
+ confused while casting, and there must be some light present.
+
+ Some classes (for example, Thaumaturgists) have the ability to use
+ some magic without actually needing spell books of any sort. These
+ classes are able to access their magical powers through the use of
+ the 'm' command.
+~~~~~39
+[[[[[GUse bonus power (if any) (U) or (O)]
+ Some races and classes have special natural abilities. All of these
+ possible abilities are listed in an index under the U (or O) command.
+ These type of abilities can include the Vampire's bite, a DeathMold's
+ telekinesis, and a RohanKnight's light speed capabilities.
+~~~~~119|Commands|Object manipulation
+#####R--- Object Manipulation Commands ---
+~~~~~10
+[[[[[GEat some food (E)]
+ You must eat regularly to prevent starvation. As you grow hungry,
+ a message will appear at the bottom of the screen saying "Hungry".
+ If you go hungry long enough, you will become weak, then start
+ fainting, and eventually, you may will die of starvation. You
+ may use this command to eat food in your inventory. Note that
+ you can sometimes find food in the dungeon, or you can butcher
+ corpses of killed creatures to obtain raw meat, but it is not
+ always wise to eat strange food.
+~~~~~12
+[[[[[GFuel your lantern/torch (F)]
+ If you are using a torch and have more torches in your pack,
+ or you are using a lantern and have flasks of oil in your pack,
+ then your can "refuel" them with this command. Torches and Lanterns
+ are limited in their maximal fuel. In general, two flasks will fully
+ fuel a lantern and two torches will fully fuel a torch.
+~~~~~30
+[[[[[GQuaff a potion (q)]
+ Use this command to drink a potion. Potions affect the player in
+ various ways, but the effects are not always immediately obvious.
+~~~~~32
+[[[[[GRead a scroll (r)]
+ Use this command to read a scroll. Scroll spells usually have an
+ area effect, except for a few cases where they act on other objects.
+ Reading a scroll causes the parchment to disintegrate as the scroll
+ takes effect, unless you are an Alchemist. Most scrolls which prompt
+ for more information can be aborted (by pressing escape), which will
+ stop reading the scroll before it disintegrates.
+~~~~~58
+[[[[[GInscribe an object ({)]
+ This command inscribes a string on an object. The inscription is
+ displayed inside curly braces after the object description. The
+ inscription is limited to the particular object (or pile) and is
+ not automatically transferred to all similar objects. Under certain
+ circumstances, ToME will display "fake" inscriptions on certain
+ objects ("cursed", "broken", "tried", "empty", "NN% off") when
+ appropriate. These "fake" inscriptions are "covered up" by real
+ inscriptions, but will re-appear if the real inscription is removed.
+ In addition, ToME will occasionally place a "real" inscription on
+ an object for you, normally as the result of your character getting
+ a "feeling" about the item. All characters will get "feelings" about
+ weapons and armor after carrying them for a while. Warriors get the
+ most detailed feelings, and get them quicker than any other class.
+ An item labeled as "{empty}" was found to be out of charges, and an
+ item labeled as "{tried}" is a "flavoured" item which the character
+ has used, but whose effects are unknown. Certain inscriptions have
+ a meaning to the game, see "@#", "@x#", "!*", and "!x", in the section
+ on *****command.txt*90[inventory object selection.]
+~~~~~59
+[[[[[GUninscribe an object (})]
+ This command removes the inscription on an object. This command will
+ have no effect on "fake" inscriptions added by the game itself.
+~~~~~15
+[[[[[GHack up a corpse (h or $)]
+ Corpses can be cut up into smaller pieces of meat, allowing the user to
+ eat the meat, or cure it for later use.
+~~~~~21
+[[[[[GCure meat (K)]
+ Curing meat requires the use of a potion of salt water, and is used to
+ protect meat from hacked-up corpses from going bad.
+~~~~~16
+[[[[[GDrink from a fountain (H)]
+ All fountains in Arda are magical, and act like magical potions. The
+ game will ask you whether you want to quaff from a fountain or to fill
+ empty bottles. The only way to identify the type of fountain is to
+ fill your bottles from it and see what you get.
+~~~~~44
+[[[[[GGive item to monster (y)]
+ This command is used to give an item within your inventory to a monster
+ standing next to you. The monster may not accept the item you give it,
+ however.
+~~~~~96
+[[[[[GChat (Y)]
+ This command allows you to chat with someone. Be warned that most
+ monsters won't chat
+
+#####R--- Magical Object Commands ---
+~~~~~2
+[[[[[GActivate an artifact (A)]
+ You have heard rumours of special weapons and armour deep in the
+ Pits, items that can let you breath fire like a dragon or light
+ rooms with just a thought. Should you ever be lucky enough to
+ find such an item, this command will let you activate its special
+ ability. Special abilities can only be used if you are wearing or
+ wielding the item.
+ Note that there are also a few common objects that can be activated,
+ e.g. music instruments, monster eggs and spell-storing mage staves,
+ and that some artifacts, so-called "junkarts", can't be wielded, but
+ must be activated from the backpack.
+~~~~~1
+[[[[[GAim a wand (a) or Zap a wand (z)]
+ Wands must be aimed in a direction to be used. Wands are magical
+ devices, and therefore there is a chance you will not be able to
+ figure out how to use them if you aren't good with magical
+ devices. They will fire a shot that affects the first object or
+ creature encountered or fire a beam that affects anything in a
+ given direction, depending on the wand. An obstruction such as a
+ door or wall will generally stop the effects from traveling any
+ farther. This command requires a direction and can use a target.
+~~~~~38
+[[[[[GUse a staff (u) or Zap a staff (Z)]
+ This command will use a staff. A staff is normally very similar
+ to a scroll, in that they normally either have an area effect or
+ affect a specific object. Staves are magical devices, and there
+ is a chance you will not be able to figure out how to use them.
+~~~~~45
+[[[[[GZap a rod (z) or Activate a rod (a)]
+ Rods are extremely powerful magical items, which cannot be burnt
+ or shattered, and which can have either staff-like or wand-like
+ effects, but unlike staves and wands, they don't have charges.
+ Instead, they draw on the ambient magical energy to recharge
+ themselves, and therefore can only be activated once every few
+ turns. The recharging time varies depending on the type of rod.
+ This command may require a direction (depending on the type of
+ rod, and whether you are aware of its type) and can use a target.
+~~~~~120|Commands|Throwing and missile weapons
+#####R--- Throwing and Missile Weapons ---
+~~~~~11
+[[[[[GFire an item (f) or Fire an item (t)]
+ You may throw any object carried by your character. Depending on
+ the weight, it may travel across the room or drop down beside you.
+ Only one object from a pile will be thrown at a time. Note that
+ throwing an object will often cause it to break, so be careful!
+ If you throw something at a creature, your chances of hitting it
+ are determined by your pluses to hit, your ability at throwing,
+ and the object's pluses to hit. Once the creature is it, the
+ object may or may not do any damage to it. You've heard rumors
+ that some objects found in the dungeon can do huge amounts of
+ damage when thrown, but you're not sure which objects those
+ are.... Note that flasks of oil will do a fairly large chunk
+ of damage to a monster on impact, supposedly representing fire
+ damage, but it works against fire elementals too... If you are
+ wielding a missile launcher compatible with the object you are
+ throwing, then you automatically use the launcher to fire the
+ missile with much higher range, accuracy, and damage, then you
+ would get by just throwing the missile. Fire or Throw requires
+ a direction. Targeting mode (see the next command) can be invoked
+ with "*" at the "Direction?" prompt.
+~~~~~40
+[[[[[GThrow an item (v)]
+ You may throw any object carried by your character. The lighter
+ the object, the farther you can throw it. Only one object from a
+ stack may be thrown at a time. Throwing an object may break it.
+ If you throw something at a monster, your chances of hitting it
+ are determined by your pluses to hit, your ability at throwing,
+ and the object's pluses to hit. If the object hits the monster,
+ it may or may not do damage. Some objects, such as weapons, or
+ flasks of oil, can do a substantial amount of damage. This
+ command requires a direction, and can take a target.
+~~~~~55
+[[[[[GTargeting Mode (*)]
+ This will allow you to aim your spells and such at a specific
+ monster or grid, so that you can point directly towards that
+ monster or grid (even if this is not a "compass" direction) when
+ you are asked for a direction. You can set a target using this
+ command, or you can set a new target at the "Direction?" prompt when
+ appropriate. At the targeting prompt, you have many options. First
+ of all, targetting mode starts targetting nearby monsters which can
+ be reached by "projectable" spells and thrown objects. In this mode,
+ you can press "t" (or "5" or ".") to select the current monster,
+ space to advance to the next monster, "-" to back up to the previous
+ monster, direction keys to advance to a monster more or less in that
+ direction, "r" to "recall" the current monster, "q" to exit targetting
+ mode, and "p" (or "o") to stop targetting monsters and enter the mode
+ for targetting a location on the floor or in a wall. Note that if
+ there are no nearby monsters, you will automatically enter this mode.
+ Note that hitting "o" is just like "p", except that the location
+ cursor starts on the last examined monster instead of on the player.
+ In this mode, you use the "direction" keys to move around, and the
+ "q" key to quit, and the "t" (or "5" or ".") key to target the cursor
+ location. Note that targetting a location is slightly "dangerous",
+ as the target is maintained even if you are far away. To cancel an
+ old target, simply hit "*" and then ESCAPE (or "q"). Note that when
+ you cast a spell or throw an object at the target location, the path
+ chosen is the "optimal" path towards that location, which may or may
+ not be the path you want. Sometimes, by clever choice of a location
+ on the floor for your target, you may be able to convince a thrown
+ object or cast spell to squeeze through a hole or corridor that is
+ blocking direct access to a different grid. Launching a ball spell
+ or breath weapon at a location in the middle of a group of monsters
+ can often improve the effects of that attack, since ball attacks are
+ not stopped by interposed monsters if the ball is launched at a target.
+ This command takes no time.
+~~~~~121|Commands|Looking
+#####R--- Looking Commands ---
+~~~~~25
+[[[[[GFull screen map (M)]
+ This command will show a map of the entire dungeon, reduced by a
+ factor of nine, on the screen. Only the major dungeon features
+ will be visible because of the scale, so even some important
+ objects may not show up on the map. This is particularly useful
+ in locating where the stairs are relative to your current
+ position, or for identifying unexplored areas of the dungeon.
+ This command takes no time.
+~~~~~23
+[[[[[GLocate player on map (L) or Where is the player (W)]
+ This command lets you scroll your map around, looking at all sectors
+ of the current dungeon level, until you press escape, at which point
+ the map will be re-centred on the player if necessary. To scroll
+ the map around, simply press any of the "direction" keys. The top
+ line will display the sector location, and the offset from your
+ current sector. This command takes no time.
+~~~~~22
+[[[[[GLook around (l) or Examine things (x)]
+ This command is used to look around at nearby monsters (to determine
+ their type and health) and objects (to determine their type). It is
+ also used to find out what objects (if any) are under monsters, and
+ if a monster is currently inside a wall. This command takes no time.
+ When you are looking at something, you may hit space for more details,
+ or to advance to the next interesting monster or object, or minus ("-")
+ to go back to the previous monster or object, or a direction key to
+ advance to the nearest interesting monster or object (if any) in that
+ general direction, or "r" to recall information about the current
+ monster race, or "q" or escape to stop looking around. You always
+ start out looking at the "nearest" interesting monster or object.
+~~~~~18
+[[[[[GObserve an item (I)]
+ This command lets you observe a previously *identified* item.
+ This will tell you things about the special powers of the object.
+ Currently, it only makes sense for artifacts and ego-items.
+~~~~~122|Commands|Messages
+#####R--- Message Commands ---
+~~~~~53
+[[[[[GRepeat level feeling (^F)]
+ Repeats the feeling about the dungeon level that you got when you
+ first entered the level.
+~~~~~62
+[[[[[GView previous messages (^P)]
+ This command shows you all the recent messages. You can scroll
+ through them, or exit with ESCAPE. This command takes no time.
+~~~~~69
+[[[[[GTake notes (:)]
+ This command allows you to take notes, which will then appear in your
+ note file, if the birth-option "take_notes" was set (the default), or
+ in your message list (prefixed with "Note:"), if the option was not set.
+ The note file can be displayed through the "Display Current Knowledge"
+ command (~ or |). This command takes no time.
+~~~~~123|Commands|Game status
+#####R--- Game Status Commands ---
+~~~~~6
+[[[[[GCharacter Description (C)]
+ Brings up a full description of your character, including your
+ skill levels, your current and potential stats, and various other
+ information. From this screen, you can change your name or use
+ the file character description command to save your character
+ status to a file. That command saves additional information,
+ including your background, your inventory, and the contents of
+ your house.
+~~~~~82
+[[[[[GDisplay Current Knowledge (~ or |)]
+ The command opens a menu from which you can lookup information
+ collected so far. This includes known artifacts, unique monsters,
+ identified objects, killed creatures, recall depths, acquired
+ corruptions, current pets, current quests, current fates, known
+ traps, known dungeon towns and last but not least the note file.
+
+ Display known artifacts
+ This selection lists all of the artifacts that you have encountered.
+ Any artifact that appears in this list, which you cannot seem to
+ find, has been lost forever. The "preserve" mode will prevent
+ you from accidentally losing any artifacts, but will also prevent
+ you from ever getting a "special" level feeling.
+
+ Display known uniques
+ Brings up a list of known unique monsters, plus their current
+ status. Once killed, unique monsters never show up again, with a
+ few remarkable exceptions.
+
+ Display known objects
+ This list all 'flavoured' objects (such as rings, scrolls, wands,
+ potions, etc.) which you have identified.
+
+ Display kill count
+ This lists all killed creatures together with a total kill count.
+
+ Display recall depths
+ This lists all recall depths of entered dungeons as well as marks
+ the current recall dungeon with an asterisk.
+
+ Display corruptions
+ This lists all acquired corruptions with their beneficial and
+ detrimental effects.
+
+ Display current pets
+ Display current quests
+ Display current fates
+ Display known traps
+ Display known dungeon towns
+
+ Display notes
+ If the option "take_notes" is set shows you your notes file, where all
+ remarkable events are noted. You can add notes yourself by using the
+ "Take notes" command (:).
+
+~~~~~70
+[[[[[GTime of the day (^T)]
+ This command is used to give the current date and time within the game.
+ Extremely useful for characters such as Vampires to check whether
+ it is safe to leave the dungeon.
+~~~~~124|Commands|Saving and Exiting
+~~~~~125|Saving and Exiting
+#####R--- Saving and Exiting Commands ---
+~~~~~75
+[[[[[GSave and Quit (Ctrl-X)]
+ To save your game so that you can return to it later, use this
+ command. Save files will also be generated (hopefully) if the
+ game crashes due to a system error. After you die, you can use
+ your savefile to play again with the same options and such.
+~~~~~68
+[[[[[GSave (Ctrl-S)]
+ This command saves the game but doesn't exit ToME. Use this
+ frequently if you are paranoid about having the computer crash
+ while you are playing.
+~~~~~31
+[[[[[GQuit (commit suicide) (Q)]
+ Kills your character and exits ToME. You will be prompted to
+ make sure you really want to do this, and then asked to verify
+ that choice. Note that dead characters are dead forever.
+~~~~~126|Commands|Pref files
+~~~~~127|Pref files|Commands
+#####R--- User pref file commands ---
+~~~~~65
+[[[[[GInteract with options (=)]
+ Allow you to interact with options. Note that using the "cheat"
+ options may mark your savefile as unsuitable for the high score
+ list. You may change normal options using the "X" and "Y" user
+ pref commands. You must use the "redraw" command (^R) after
+ changing certain options.
+~~~~~49
+[[[[[GInteract with macros (@)]
+ Allow you to interact with macros. You may load or save macros
+ from user pref files, create macros of various types, or define
+ keymaps. You must define a "current action", shown at the bottom
+ of the screen, before you attempt to use any of the "create macro"
+ commands, which use that "current action" as their action. This
+ is a horrible interface, and will be fixed eventually.
+~~~~~51
+[[[[[GInteract with visuals (%)]
+ Allow you to interact with visuals. You may load or save visuals
+ from user pref files, or modify the attr/char mappings for the
+ monsters, objects, and terrain features. You must use the "redraw"
+ command (^R) to redraw the map after changing attr/char mappings.
+~~~~~54
+[[[[[GInteract with colors (&)]
+ Allow the user to interact with colors. This command only
+ works on some systems.
+~~~~~47
+[[[[[GInteract with the system (!)]
+ Allow the user to interact with the underlying visual system.
+ This command is currently unused.
+~~~~~71
+[[[[[GEnter a user pref command (")]
+ ToME stores your preferences in files called "user pref files",
+ which contain comments and "user pref commands", which are simple strings
+ describing one aspect of the system about which the user has a preference.
+ You may enter single user pref commands directly, using the special "Enter
+ a user pref command" command, activated by "double quote". You may have to
+ use the "redraw" command (^R) after changing certain of the aspects of the
+ game, to allow ToME to adapt to your changes.
+~~~~~128|Commands|Help
+#####R--- Help ---
+~~~~~84
+[[[[[GHelp (?)]
+ Brings up the ToME on-line help system. Note that the help
+ files are just text files in a particular format, and that other
+ help files may be available on the Net. In particular, there are
+ a variety of spoiler files which do not come with the standard
+ distribution. Check the place you got ToME from or ask on the
+ newsgroup rec.games.roguelike.angband about them.
+~~~~~83
+[[[[[GIdentify Symbol (/)]
+ Use this command to find out what a character stands for. For
+ instance, by pressing "/.", you can find out that the "." symbol
+ stands for a floor spot. When used with a symbol that represents
+ creatures, the this command will tell you only what class of
+ creature the symbol stands for, not give you specific information
+ about a creature you can see. To get that, use the Look command.
+
+ There are three special symbols you can use with the Identify
+ Symbol command to access specific parts of your monster memory.
+ Typing Ctrl-A when asked for a symbol will recall details about
+ all monsters, typing Ctrl-U will recall details about all unique
+ monsters, and typing Ctrl-N will recall details about all
+ non-unique monsters.
+
+ If the character stands for a creature, you are asked if you want
+ to recall details. If you answer yes, information about the
+ creatures you have encountered with that symbol is shown in the
+ Recall window if available, or on the screen if not. You can also
+ answer "k" to see the list sorted by number of kills, or "p" to
+ see the list sorted by dungeon level the monster is normally found
+ on. Pressing ESCAPE at any point will exit this command.
+~~~~~41
+[[[[[GGame Version (V)]
+ This command will tell you what version of ToME you are using.
+ For more information, see the "version.txt" help file.
+
+~~~~~129|Commands|Extras
+#####R--- Extra Commands ---
+~~~~~85
+[[[[[GRepeat last command (n)]
+ This will automatically repeat the last command you inputted.
+~~~~~27
+[[[[[GSacrifice at an altar (O)]
+ Altars are places dedicated to the worship of a particular God. To
+ start worshipping the God who owns the altar, you must first sacrifice
+ on their altar.
+
+ Be warned, not all Gods are equal in power, and once you have selected
+ a God to worship, it is almost impossible to change which God you worship.
+ When your God is happy with you, you will receive more benefits from them.
+ Your God's happiness will decrease over time, so you will need to accomplish
+ deeds that increase your standing. Note that there is no requirement
+ for most classes to worship any God. (See *****gods.txt*0[gods.txt] for more information)
+~~~~~28
+[[[[[GPray to your God (p)]
+ If you worship a God, you have the option of praying. The effects of
+ praying differ considerably depending on the god, ranging from the
+ battle frenzy of paladins to the self-healing powers of druids.
+ However, Gods do not like being disturbed, with negative effects on
+ your piety. See *****gods.txt*0[gods.txt] for more information.
+~~~~~29
+[[[[[GPet commands (P)]
+ From time to time, you may acquire a pet within the dungeon. Pets are able
+ (to a more or less limited extent) to follow some simple commands, like
+ follow me. These commands are all accessed through the menu under "Pet
+ Commands".
+~~~~~52
+[[[[[GToggle Choice Window (^E)]
+ Toggles the display in the choice window (if available) between
+ your inventory and your equipment. This command only applies if
+ you are running ToME under a windowing environment and the
+ choice window is available. This also redraws the choice window.
+~~~~~66
+[[[[[GRedraw Screen (^R)]
+ This command adapts to various changes in global options, and
+ redraws all of the windows. This command should be used after
+ changing various global properties (options, attr/char mappings,
+ color definitions, etc). When in doubt, use it.
+~~~~~56
+[[[[[GLoad screen dump (left-paren)]
+ This command loads a "snap-shot" of the current screen from the file
+ "dump.txt", and displays it on the screen.
+~~~~~57
+[[[[[GSave screen dump (right-paren)]
+ This command dumps a "snap-shot" of the current screen to the file
+ "dump.txt", including encoded color information.
+~~~~~64
+[[[[[GQuit to next midi song (^Q)]
+ In the DOS binary (and maybe Windows) of ToME, it is possible for
+ the game to play any midi song in the lib/xtra/music directory. This
+ command allows the player to force the game to finish the current song
+ and move on to another one (i.e. if you are tired of hearing the current
+ song, you can change it).
+~~~~~80
+[[[[[GDo cmovies (|)]
+ The cmovie command (press | key in both normal or roguelike set) allows
+ you to make a "movie" that you can send to people showing your movement
+ through a part of the dungeon (like clearing that GCV . . .)
+
+ It asks for a name (it will add the extension itself) and then if you wish
+ to play or record it.
+
+ The cmovie files (.cmv) are located in lib/cmov, note that they quickly
+ become huge and so you REALLY should compress them before sending to friends.
+~~~~~97
+[[[[[GRecord macros ($)]
+ This is an easier way to create macros. Activate it, press the key
+ sequence for your macro, reactivate it and it will create the macro
+ for you. Note than when possible using the @ key at item selection
+ is a good idea since it removes the need to inscribe items.
+~~~~~98
+[[[[[GTake html screenshot (^\])]
+ Creates an html screenshot of the current screen.
+~~~~~89
+[[[[[GBegin extended command (#)]
+ Begins an extended command. Type "help" or "?" at the prompt for a
+ list of these commands.
+
+--
+Original: Alexander Cutler and Andy Astrand
+Updated (2.7.6): Russ Allbery (rra@cs.stanford.edu)
+Updated (2.7.9): Ben Harrison (benh@phial.com)
+Updated PernAngband 5.x.x: Dawnmist (angband@dawnmist.8m.com)
+Updated for ToME 2.1
diff --git a/lib/help/corspoil.txt b/lib/help/corspoil.txt
new file mode 100644
index 00000000..baac4fae
--- /dev/null
+++ b/lib/help/corspoil.txt
@@ -0,0 +1,136 @@
+~~~~~01|Corruptions (Spoiler)
+~~~~~02|Spoilers|Corruptions
+#####R=== ToME Corruptions Spoiler ===
+
+Sometimes adventurers become exposed to the dark powers of Morgoth. If they
+are unable to resist these powers, they become corrupted. Corruptions can
+change their physical or mental abilities, some of which can be good, and
+some bad. Most corruptions will affect you permanently, although some only
+operate when they are activated (whether by player choice or as a random
+event). You can check which corruptions do you have in the knowledge screen
+6 (accessed through the '~' menu) or in a character dump.
+
+#####GGaining and (not) losing corruptions
+There are several ways that you can become corrupted.
+
+You can become corrupted by quaffing a Potion of Corruption or by drinking
+from a Fountain of Corruption. Also some strange items can be activated
+for corruption.
+
+Corruptions are permanent. Once you have one, you have it for life.
+
+[[[[[BBalrog Aura]
+ Surrounds you with a fiery aura
+ But it can burn scrolls when you read them
+[[[[[GGain message: A corrupted wall of flames surrounds you.]
+[[[[[RLose message: The wall of corrupted flames abandons you.]
+
+
+[[[[[BBalrog Wings]
+ Creates ugly, but working, wings allowing you to fly
+ But it reduces charisma by 4 and dexterity by 2
+[[[[[GGain message: Wings of shadow grow in your back.]
+[[[[[RLose message: The wings in your back fall apart.]
+
+
+[[[[[BBalrog Strength]
+ Provides 3 strength and 1 constitution
+ But it reduces charisma by 1 and dexterity by 3
+[[[[[GGain message: Your muscles get unnatural strength.]
+[[[[[RLose message: Your muscles get weaker again.]
+
+
+[[[[[BBalrog Form]
+ Allows you to turn into a Balrog at will
+ You need Balrog Aura, Balrog Wings and Balrog Strength to activate it
+[[[[[GGain message: You feel the might of a Balrog inside you.]
+[[[[[RLose message: The presence of the Balrog seems to abandon you.]
+It depends on:
+ Balrog Aura
+ Balrog Wings
+ Balrog Strength
+
+
+[[[[[BDemon Spirit]
+ Increases your intelligence by 1
+ But reduce your charisma by 2
+[[[[[GGain message: Your spirit opens to corrupted thoughts.]
+[[[[[RLose message: Your spirit closes again to the corrupted thoughts.]
+
+
+[[[[[BDemon Hide]
+ Increases your armour class by your level
+ Provides immunity to fire at level 40
+ But reduces speed by your level / 7
+[[[[[GGain message: Your skin grows into a thick hide.]
+[[[[[RLose message: Your skin returns to a natural state.]
+
+
+[[[[[BDemon Breath]
+ Provides fire breath
+ But gives a small chance to spoil potions when you quaff them
+[[[[[GGain message: Your breath becomes mephitic.]
+[[[[[RLose message: Your breath is once again normal.]
+
+
+[[[[[BDemon Realm]
+ Provides access to the demon school skill and the use of demonic equipment
+ You need Demon Spirit, Demon Hide and Demon Breath to activate it
+[[[[[GGain message: You feel more attuned to the demon realm.]
+[[[[[RLose message: You lose your attunement to the demon realm.]
+It depends on:
+ Demon Spirit
+ Demon Hide
+ Demon Breath
+
+
+[[[[[BRandom teleportation]
+ Randomly teleports you around
+[[[[[GGain message: Space seems to fizzle around you.]
+[[[[[RLose message: Space solidify again around you.]
+It is opposed to:
+ Anti-teleportation
+
+
+[[[[[BAnti-teleportation]
+ Prevents all teleportations, be it of you or monsters
+[[[[[GGain message: Space continuum freezes around you.]
+[[[[[RLose message: Space continuum can once more be altered around you.]
+It is opposed to:
+ Random teleportation
+
+
+[[[[[BTroll Blood]
+ Troll blood flows in your veins, granting increased regeneration
+ It also enables you to feel the presence of other troll beings
+ But it will make your presence more noticeable and aggravating
+[[[[[GGain message: Your blood thickens, you sense corruption in it.]
+[[[[[RLose message: Your blood returns to a normal state.]
+
+
+[[[[[BVampiric Teeth]
+ Your teeth allow you to drain blood to feed yourself
+ However your stomach now only accepts blood.
+[[[[[GGain message: You grow vampiric teeth!]
+It is not removable.
+
+
+[[[[[BVampiric Strength]
+ Your body seems somewhat dead
+ In this near-undead state it has improved strength, constitution and
+ intelligence, but also reduced dexterity, wisdom and charisma.
+[[[[[GGain message: Your body seems more dead than alive.]
+It is not removable.
+It depends on:
+ Vampiric Teeth
+
+
+[[[[[BVampire]
+ You are a Vampire. As such you resist cold, poison, darkness and nether.
+ Your life is sustained, but you cannot stand the light of the sun.
+[[[[[GGain message: You die to be reborn in a Vampire form.]
+It is not removable.
+It depends on:
+ Vampiric Strength
+
+
diff --git a/lib/help/debug.txt b/lib/help/debug.txt
new file mode 100644
index 00000000..56d57098
--- /dev/null
+++ b/lib/help/debug.txt
@@ -0,0 +1,278 @@
+|||||oy
+~~~~~99|Debug
+#####R=== Debug Commands ===
+
+
+Debug commands are entered as an "underlying command" (a single key)
+plus a variety of optional or required arguments.
+
+The debug commands are used for debugging and experimenting. The game
+will not be scored if you use debug commands.
+
+~~~~~100|Debug|Command List
+#####R=== Command List Summary ===
+
+ *****debug.txt*1[a Autorestore] *****debug.txt*2[A Show all stats]
+ *****debug.txt*3[b Teleport to target] *****debug.txt*4[B HP to zero]
+ *****debug.txt*5[c Create object] *****debug.txt*6[C Create artifact]
+ *****debug.txt*7[d Detect all] *****debug.txt*8[D Teleport to the wilderness]
+ *****debug.txt*9[e Edit character attributes] *****debug.txt*10[E Change grid's mana]
+ *****debug.txt*11[f *IDENTIFY*] *****debug.txt*12[F Features]
+ *****debug.txt*13[g Create good item] G (unused)
+ *****debug.txt*15[h Change life rating] *****debug.txt*16[H Hostile monster creation]
+ *****debug.txt*17[i Identify] I (unused)
+ *****debug.txt*19[j Jump to other level] J (unused)
+ *****debug.txt*21[k Check attributes] K (unused)
+ *****debug.txt*23[l Learn about objects] L (unused)
+ *****debug.txt*25[m Magic Mapping] *****debug.txt*26[M Gain corruption]
+ *****debug.txt*27[n Summon named monster] *****debug.txt*28[N Summon _friendly_ named monster]
+ *****debug.txt*29[o Edit object attributes] O (unused)
+ *****debug.txt*31[p Phase door] *****debug.txt*32[P Panic save]
+ *****debug.txt*33[q Get a quest] Q (unused)
+ *****debug.txt*35[r Gain reward] *****debug.txt*36[R Create a trap]
+ *****debug.txt*37[s Summon monster] *****debug.txt*38[S Change the feature of the map]
+ *****debug.txt*39[t Teleport] *****debug.txt*40[T Teleport to a town]
+ *****debug.txt*41[u Complete map] *****debug.txt*42[U Become undead]
+ *****debug.txt*43[v Random artifact/ego item] V (unused)
+ *****debug.txt*45[w Wizard light the level] *****debug.txt*46[W Wish]
+ *****debug.txt*47[x XP boost] X (unused)
+ y (unused) Y (unused)
+ *****debug.txt*51[z Zap monsters] Z (unused)
+ ! (unused) ^A (unused)
+ *****debug.txt*55[@ Increment monster level] ^B (unused)
+ # (unused) ^C (unused)
+ $ (unused) ^D (unused)
+ % (unused) ^E (unused)
+ ^ (unused) ^F (unused)
+ & (unused) ^G (unused)
+ *****debug.txt*61[* Lose special powers] ^H (unused)
+ ( (unused) ^I (unused)
+ ) (unused) ^J (unused)
+ { (unused) ^K (unused)
+ } (unused) ^L (unused)
+ [ (unused) ^M (unused)
+ ] (unused) ^N (unused)
+ *****debug.txt*67[- Create object] ^O (unused)
+ *****debug.txt*69[_ The path to the god dark] ^P (unused)
+ *****debug.txt*71[+ Gain a fate] ^Q (unused)
+ *****debug.txt*73[= Align monster] ^R (unused)
+ ; (unused) ^S (unused)
+ : (unused) ^T (unused)
+ ' (unused) ^U (unused)
+ *****debug.txt*75[" Create spoiler] ^V (unused)
+ , (unused) ^W (unused)
+ < (unused) ^X (unused)
+ . (unused) ^Y (unused)
+ *****debug.txt*81[> Lua script] ^Z (unused)
+ \ (unused) | (unused)
+ ` (unused) ~ (unused)
+ *****debug.txt*91[/ Summon monster] *****debug.txt*92[? Help]
+ ^\ (unused)
+
+~~~~~111|Debug|Command descriptions
+#####R=== Command Descriptions ===
+
+ The following command descriptions are listed as the command name
+plus the "underlying command" key. Then comes a brief description of the
+command. Some commands use the "repeat count" to automatically repeat the
+command several times, while others use the "repeat count" as an "argument",
+for example, commands which need a "quantity" will use the "repeat count"
+instead of asking for a quantity, allowing the use of "0d" for "drop all".
+Commands which ask for a quantity will convert any "letters" into the
+maximal legal value.
+~~~~~112|Debug|General
+#####R--- General Commands ---
+~~~~~1
+[[[[[GAutorestore (a)]
+ Restores all your stats. This includes HP, SP, hunger, lost levels, etc.
+~~~~~2
+[[[[[GShow all stats (A)]
+ This brings up the Character status menu, where you can view
+ all the stats about your character.
+~~~~~3
+[[[[[GTeleport to target (b)]
+ You first need to have a monster targeted, then you can use
+ this command to teleport next to the monster.
+~~~~~4
+[[[[[GHP to zero (B)]
+ Bring your health down to zero.
+~~~~~5
+[[[[[GCreate object (c)]
+ Allows you to select and create a new object where you stand.
+ This brings up a menu where you can choose what type of object
+ you want created.
+~~~~~6
+[[[[[GCreate artifact (C)]
+ Allows you to select and create a new artifact where you stand.
+ Use the "Command count", aka 0, to specify a number from
+ a_info.txt to put it on the ground where you are standing.
+ For example : 03^AC will create the Arkenstone of Thrane (+3)
+~~~~~7
+[[[[[GDetect all (d)]
+ Sense ways out/monsters/objects/traps.
+~~~~~8
+[[[[[GTeleport to the wilderness (D)]
+ From a dungeon this will teleport you to the wilderness level
+ and if used in the wilderness it acts like teleport.
+~~~~~9
+[[[[[GEdit character attributes (e)]
+ Edit character attributes including Str, Int, Dex, experience, gold, luck, etc.
+~~~~~10
+[[[[[GChange grid's mana (E)]
+ Alter how much mana a grid has.
+ Use the "Command count", aka 0, to specify the amount of mana
+ that you want.
+~~~~~11
+[[[[[G*IDENTIFY* (f)]
+ Like a Scroll of *Identify*.
+~~~~~12
+[[[[[GFeatures (F)]
+ Use the "Command count", aka 0, to specify a number from
+ f_info.txt to put a feature on the ground where you are
+ standing.
+~~~~~13
+[[[[[GCreate good item (g)]
+ Create a random good item where you stand.
+~~~~~15
+[[[[[GChange life rating (h)]
+ Change your life rating.
+~~~~~16
+[[[[[GHostile monster creation (H)]
+ Summons a Pack of Creatures of the same kind. Will only work
+ if MONSTER_HORDES has been defined at compile time.
+~~~~~17
+[[[[[GIdentify (i)]
+ Like a Scroll of Identify.
+~~~~~19
+[[[[[GJump to other level (j)]
+ Jump to other dungeon level. This does not work in the
+ wilderness as it is treated as all one level.
+~~~~~21
+[[[[[GCheck attributes (k)]
+ Displays your characters attributes.
+~~~~~23
+[[[[[GLearn about objects (l)]
+ Make you know about all objects. Not sure how this works.
+~~~~~25
+[[[[[GMagic Mapping (m)]
+ Like a Scroll of Magic mapping.
+~~~~~26
+[[[[[GGain corruption (M)]
+ Allows your character to gain a corruption.
+~~~~~27
+[[[[[GSummon named monster (n)]
+ Summon a monster that will appear next to you.
+ Use the "Command count", aka 0, to specify a number from
+ r_info.txt to summon a monster.
+~~~~~28
+[[[[[GSummon _friendly_ named monster (N)]
+ The same as n but the creature will be your pet. Try this
+ with number 861, Darkgod is now your pet.
+~~~~~29
+[[[[[GEdit object attributes (o)]
+ Allows you to alter the attributes of any object that you
+ have in your backpack.
+~~~~~31
+[[[[[GPhase door (p)]
+ Like a Scroll of Phase Door.
+~~~~~32
+[[[[[GPanic save (P)]
+ Save and quit the game, which is the same as doing a ^X.
+~~~~~33
+[[[[[GGet a quest (q)]
+ Get a quest.
+ Use the "Command count", aka 0, to specify a number from
+ 1 to 25, as defined in defines.h ( the QUEST_XXX items ).
+ For example : 04^Aq will get you the thieves quest.
+~~~~~35
+[[[[[GGain reward (r)]
+ Some high being grants you a reward.
+~~~~~36
+[[[[[GCreate a trap (R)]
+ Use the "Command count", aka 0, to specify a number from
+ tr_info.txt to put a trap on the ground where you are
+ standing.
+~~~~~37
+[[[[[GSummon monster (s)]
+ Summon a random monster, next to where you stand.
+~~~~~38
+[[[[[GChange the feature of the map (S)]
+ This allows you to change the "special" field of the current
+ grid. This special field is used to store things like quest
+ ids, dungeon entries, and so on, not to be used unless
+ one knows what it's doing.
+~~~~~39
+[[[[[GTeleport (t)]
+ Like a Scroll of Teleport.
+~~~~~40
+[[[[[GTeleport to a town (T)]
+ Teleports you to a specific town.
+ Use the "Command count", aka 0, to specify a number from
+ wf_info.txt for where you want to go.
+ standing.
+ For example : 02^AT will teleport you to Gondolin
+~~~~~41
+[[[[[GComplete map (u)]
+ Displays the complete map of the dungeon.
+~~~~~42
+[[[[[GBecome undead (U)]
+ This is supposed to make you undead (as in the Necromantic power).
+~~~~~43
+[[[[[GRandom artifact/ego item (v)]
+ Create a random artifact/ego item where you stand.
+~~~~~45
+[[[[[GWizard light the level (w)]
+ Looks like the same as u.
+~~~~~46
+[[[[[GWish (W)]
+ Makes all your wishes come true.
+
+ Read the *****wishing.txt*0[wishing spoiler] to see how these work.
+~~~~~47
+[[[[[GXP boost (x)]
+ Use the "Command count", aka 0, to specify the increment,
+ if you do not specify a parameter it doubles your XP,
+ otherwise it increments by the specified amount.
+~~~~~51
+[[[[[GZap monsters (z)]
+ All monsters in sight range vanish like Mass Genocide, only with no
+ HP price.
+~~~~~55
+[[[[[GIncrement monster level (@)]
+ Level up a monster.
+~~~~~61
+[[[[[GLose special powers (*)]
+ Returns your powers to a normal level.
+~~~~~67
+[[[[[GCreate object (-)]
+ Allows you to create a new object where you stand. You must
+ specify an object number from k_info.txt.
+~~~~~69
+[[[[[GThe path to the god dark (_)]
+ Do not use this as it is used by DarkGod as a test for Lua
+ and will CRASH the game. You have been warned.
+~~~~~71
+[[[[[GGain a fate (+)]
+ Unearth more of your prophecy.
+~~~~~73
+[[[[[GAlign monster (=)]
+ Use the "Command count", aka 0, to specify one of the following
+ alignment types:
+ 0 monster becomes enemy
+ 1 monster becomes neutral
+ 2 monster becomes friendly
+ 3 monster becomes pet
+ 4 monster becomes companion
+ You then point at an enemy and press space.
+~~~~~75
+[[[[[GCreate spoiler (")]
+ Brings up a menu that allows you to create a spoiler file.
+~~~~~81
+[[[[[GLua script (>)]
+ Allows you to run a Lua script.
+~~~~~91
+[[[[[GSummon monster (/)]
+ Summons a random monster next to you.
+~~~~~91
+[[[[[GHelp (?)]
+ Displays the main help file.
diff --git a/lib/help/def.aux b/lib/help/def.aux
new file mode 100644
index 00000000..983e9683
--- /dev/null
+++ b/lib/help/def.aux
@@ -0,0 +1,3 @@
+file_ext="html"
+link_prefix=""
+link_suffix=""
diff --git a/lib/help/defines.txt b/lib/help/defines.txt
new file mode 100644
index 00000000..ac997501
--- /dev/null
+++ b/lib/help/defines.txt
@@ -0,0 +1,639 @@
+|||||oy
+~~~~~81|Defines
+~~~~~85|Defines|Tvals
+~~~~~82|Automatizer|Defines
+~~~~~83|Tvals
+#####R /----------------------------------------\
+#####R < Tvals and svals >
+#####R \----------------------------------------/
+
+Some objects don't have svals as such. Spellbooks, wands, and staves for
+instance don't have svalues as they are defined in lua.
+
+TV_SKELETON 1 /* Skeletons ('s') */
+TV_BOTTLE 2 /* Empty bottles ('!') */
+*****defines.txt*04[TV_BATERIE] 4 /* For the Alchemists */
+TV_SPIKE 5 /* Spikes ('~') */
+TV_MSTAFF 6 /* Mage Staffs */
+TV_CHEST 7 /* Chests ('~') */
+TV_PARCHMENT 8 /* Parchments from Kamband */
+*****defines.txt*09[TV_CORPSE] 9 /* Monster corpses */
+TV_EGG 10 /* Monster Eggs */
+TV_JUNK 11 /* Sticks, Pottery, etc ('~') */
+*****defines.txt*12[TV_TOOL] 12 /* Tools */
+*****defines.txt*14[TV_INSTRUMENT] 14 /* Musical instruments */
+*****defines.txt*15[TV_BOOMERANG] 15 /* Boomerangs */
+*****defines.txt*16[TV_SHOT] 16 /* Ammo for slings */
+*****defines.txt*16[TV_ARROW] 17 /* Ammo for bows */
+*****defines.txt*16[TV_BOLT] 18 /* Ammo for x-bows */
+*****defines.txt*19[TV_BOW] 19 /* Slings/Bows/Xbows */
+*****defines.txt*20[TV_DIGGING] 20 /* Shovels/Picks */
+*****defines.txt*21[TV_HAFTED] 21 /* Priest Weapons */
+*****defines.txt*22[TV_POLEARM] 22 /* Pikes/Glaives/Spears/etc. */
+*****defines.txt*23[TV_SWORD] 23 /* Edged Weapons */
+*****defines.txt*24[TV_AXE] 24 /* Axes/Cleavers */
+*****defines.txt*30[TV_BOOTS] 30 /* Boots */
+*****defines.txt*31[TV_GLOVES] 31 /* Gloves */
+*****defines.txt*32[TV_HELM] 32 /* Helms */
+*****defines.txt*32[TV_CROWN] 33 /* Crowns */
+*****defines.txt*34[TV_SHIELD] 34 /* Shields */
+*****defines.txt*35[TV_CLOAK] 35 /* Cloaks */
+*****defines.txt*36[TV_SOFT_ARMOR] 36 /* Soft Armor */
+*****defines.txt*37[TV_HARD_ARMOR] 37 /* Hard Armor */
+*****defines.txt*38[TV_DRAG_ARMOR] 38 /* Dragon Scale Mail */
+*****defines.txt*39[TV_LITE] 39 /* Lites (including Specials) */
+*****defines.txt*40[TV_AMULET] 40 /* Amulets (including Specials) */
+*****defines.txt*45[TV_RING] 45 /* Rings (including Specials) */
+*****defines.txt*46[TV_TRAPKIT] 46 /* Trapkits */
+TV_TOTEM 54 /* Summoner totems */
+*****defines.txt*55[TV_STAFF] 55 /* Staffs */
+*****defines.txt*65[TV_WAND] 65 /* Wands */
+*****defines.txt*66[TV_ROD] 66 /* Rod tips */
+*****defines.txt*67[TV_ROD_MAIN] 67 /* Rod body's */
+*****defines.txt*70[TV_SCROLL] 70 /* Scrolls */
+*****defines.txt*71[TV_POTION] 71 /* potions */
+*****defines.txt*72[TV_POTION2] 72 /* Second set of potion */
+TV_FLASK 77 /* Flasks of oil */
+*****defines.txt*80[TV_FOOD] 80 /* Food, including mushrooms */
+TV_HYPNOS 99 /* To wield monsters !:) */
+TV_GOLD 100 /* Gold can only be picked up by players */
+TV_RANDART 102 /* Random Artifacts */
+TV_RUNE1 104 /* Base runes */
+TV_RUNE2 105 /* Modifier runes */
+TV_BOOK 111 /* spell books */
+*****defines.txt*115[TV_DAEMON_BOOK] 115 /* Demon blades, shields and horns */
+~~~~~84|Defines|Svals
+~~~~~12|Svals
+/* The "sval" codes for TV_TOOL */
+ SV_TOOL_CLIMB 0
+ SV_PORTABLE_HOLE 1
+~~~~~16
+/* The "sval" codes for TV_SHOT/TV_ARROW/TV_BOLT */
+ SV_AMMO_LIGHT 0 /* pebbles */
+ SV_AMMO_NORMAL 1 /* shots, arrows, bolts */
+ SV_AMMO_HEAVY 2 /* seeker arrows and bolts, mithril shots */
+~~~~~14
+/* The "sval" codes for TV_INSTRUMENT */
+ SV_FLUTE 1
+ SV_BANJO 2
+ SV_LUTE 3
+ SV_MANDOLIN 4
+ SV_DRUM 5
+ SV_HARP 6
+ SV_HORN 7
+~~~~~46
+/* The "sval" codes for TV_TRAPKIT */
+ SV_TRAPKIT_SLING 1
+ SV_TRAPKIT_BOW 2
+ SV_TRAPKIT_XBOW 3
+ SV_TRAPKIT_POTION 4
+ SV_TRAPKIT_SCROLL 5
+ SV_TRAPKIT_DEVICE 6
+~~~~~15
+/* The "sval" codes for TV_BOOMERANG */
+ SV_BOOM_S_WOOD 1 /* 1d4 */
+ SV_BOOM_WOOD 2 /* 1d9 */
+ SV_BOOM_S_METAL 3 /* 1d8 */
+ SV_BOOM_METAL 4 /* 2d4 */
+~~~~~19
+/* The "sval" codes for TV_BOW (note information in "sval") */
+ SV_SLING 2 /* (x2) */
+ SV_SHORT_BOW 12 /* (x2) */
+ SV_LONG_BOW 13 /* (x3) */
+ SV_LIGHT_XBOW 23 /* (x3) */
+ SV_HEAVY_XBOW 24 /* (x4) */
+~~~~~20
+/* The "sval" codes for TV_DIGGING */
+ SV_SHOVEL 1
+ SV_GNOMISH_SHOVEL 2
+ SV_DWARVEN_SHOVEL 3
+ SV_PICK 4
+ SV_ORCISH_PICK 5
+ SV_DWARVEN_PICK 6
+ SV_MATTOCK 7
+~~~~~21
+/* The "sval" values for TV_HAFTED */
+ SV_CLUB 1 /* 1d4 */
+ SV_WHIP 2 /* 1d6 */
+ SV_QUARTERSTAFF 3 /* 1d9 */
+ SV_NUNCHAKU 4 /* 2d3 */
+ SV_MACE 5 /* 2d4 */
+ SV_BALL_AND_CHAIN 6 /* 2d4 */
+ SV_WAR_HAMMER 8 /* 3d3 */
+ SV_LUCERN_HAMMER 10 /* 2d5 */
+ SV_THREE_PIECE_ROD 11 /* 3d3 */
+ SV_MORNING_STAR 12 /* 2d6 */
+ SV_FLAIL 13 /* 2d6 */
+ SV_LEAD_FILLED_MACE 15 /* 3d4 */
+ SV_TWO_HANDED_FLAIL 18 /* 3d6 */
+ SV_GREAT_HAMMER 19 /* 4d6 */
+ SV_MACE_OF_DISRUPTION 20 /* 5d8 */
+ SV_GROND 50 /* 3d4 */
+~~~~~24
+/* The "sval" values for TV_AXE */
+ SV_HATCHET 1 /* 1d5 */
+ SV_CLEAVER 2 /* 2d4 */
+ SV_LIGHT_WAR_AXE 8 /* 2d5 */
+ SV_BEAKED_AXE 10 /* 2d6 */
+ SV_BROAD_AXE 11 /* 2d6 */
+ SV_BATTLE_AXE 22 /* 2d8 */
+ SV_GREAT_AXE 25 /* 4d4 */
+ SV_LOCHABER_AXE 28 /* 3d8 */
+ SV_SLAUGHTER_AXE 30 /* 5d7 */
+~~~~~22
+/* The "sval" values for TV_POLEARM */
+ SV_SPEAR 2 /* 1d6 */
+ SV_SICKLE 3 /* 2d3 */
+ SV_AWL_PIKE 4 /* 1d8 */
+ SV_TRIDENT 5 /* 1d9 */
+ SV_FAUCHARD 6 /* 1d10 */
+ SV_BROAD_SPEAR 7 /* 1d9 */
+ SV_PIKE 8 /* 2d5 */
+ SV_GLAIVE 13 /* 2d6 */
+ SV_HALBERD 15 /* 3d4 */
+ SV_GUISARME 16 /* 2d5 */
+ SV_SCYTHE 17 /* 5d3 */
+ SV_LANCE 20 /* 2d8 */
+ SV_TRIFURCATE_SPEAR 26 /* 2d9 */
+ SV_HEAVY_LANCE 29 /* 4d8 */
+ SV_SCYTHE_OF_SLICING 30 /* 8d4 */
+~~~~~23
+/* The "sval" codes for TV_SWORD */
+ SV_BROKEN_DAGGER 1 /* 1d1 */
+ SV_BROKEN_SWORD 2 /* 1d2 */
+ SV_DAGGER 4 /* 1d4 */
+ SV_MAIN_GAUCHE 5 /* 1d5 */
+ SV_RAPIER 7 /* 1d6 */
+ SV_SMALL_SWORD 8 /* 1d6 */
+ SV_BASILLARD 9 /* 1d8 */
+ SV_SHORT_SWORD 10 /* 1d7 */
+ SV_SABRE 11 /* 1d7 */
+ SV_CUTLASS 12 /* 1d7 */
+ SV_KHOPESH 14 /* 2d4 */
+ SV_TULWAR 15 /* 2d4 */
+ SV_BROAD_SWORD 16 /* 2d5 */
+ SV_LONG_SWORD 17 /* 2d5 */
+ SV_SCIMITAR 18 /* 2d5 */
+ SV_KATANA 20 /* 3d4 */
+ SV_BASTARD_SWORD 21 /* 3d4 */
+ SV_GREAT_SCIMITAR 22 /* 4d5 */
+ SV_CLAYMORE 23 /* 2d8 */
+ SV_ESPADON 24 /* 2d9 */
+ SV_TWO_HANDED_SWORD 25 /* 3d6 */
+ SV_FLAMBERGE 26 /* 3d7 */
+ SV_EXECUTIONERS_SWORD 28 /* 4d5 */
+ SV_ZWEIHANDER 29 /* 4d6 */
+ SV_BLADE_OF_CHAOS 30 /* 6d5 */
+ SV_SHADOW_BLADE 31 /* 4d4 */
+ SV_BLUESTEEL_BLADE 32 /* 3d9 */
+ SV_DARK_SWORD 33 /* 3d7 */
+~~~~~34
+/* The "sval" codes for TV_SHIELD */
+ SV_SMALL_LEATHER_SHIELD 2
+ SV_SMALL_METAL_SHIELD 3
+ SV_LARGE_LEATHER_SHIELD 4
+ SV_LARGE_METAL_SHIELD 5
+ SV_DRAGON_SHIELD 6
+ SV_SHIELD_OF_DEFLECTION 10
+~~~~~32
+/* The "sval" codes for TV_HELM */
+ SV_HARD_LEATHER_CAP 2
+ SV_METAL_CAP 3
+ SV_IRON_HELM 5
+ SV_STEEL_HELM 6
+ SV_DRAGON_HELM 7
+ SV_IRON_CROWN 10
+ SV_GOLDEN_CROWN 11
+ SV_JEWELED_CROWN 12
+ SV_MORGOTH 50
+~~~~~30
+/* The "sval" codes for TV_BOOTS */
+ SV_PAIR_OF_SOFT_LEATHER_BOOTS 2
+ SV_PAIR_OF_HARD_LEATHER_BOOTS 3
+ SV_PAIR_OF_METAL_SHOD_BOOTS 6
+~~~~~35
+/* The "sval" codes for TV_CLOAK */
+ SV_CLOAK 1
+ SV_ELVEN_CLOAK 2
+ SV_FUR_CLOAK 3
+ SV_SHADOW_CLOAK 6
+~~~~~31
+/* The "sval" codes for TV_GLOVES */
+ SV_SET_OF_LEATHER_GLOVES 1
+ SV_SET_OF_GAUNTLETS 2
+ SV_SET_OF_CESTI 5
+~~~~~36
+/* The "sval" codes for TV_SOFT_ARMOR */
+ SV_FILTHY_RAG 1
+ SV_ROBE 2
+ SV_PAPER_ARMOR 3 /* 4 */
+ SV_SOFT_LEATHER_ARMOR 4
+ SV_SOFT_STUDDED_LEATHER 5
+ SV_HARD_LEATHER_ARMOR 6
+ SV_HARD_STUDDED_LEATHER 7
+ SV_RHINO_HIDE_ARMOR 8
+ SV_CORD_ARMOR 9 /* 6 */
+ SV_PADDED_ARMOR 10 /* 4 */
+ SV_LEATHER_SCALE_MAIL 11
+ SV_LEATHER_JACK 12
+ SV_STONE_AND_HIDE_ARMOR 15 /* 15 */
+ SV_THUNDERLORD_SUIT 16
+~~~~~37
+/* The "sval" codes for TV_HARD_ARMOR */
+ SV_RUSTY_CHAIN_MAIL 1 /* 14- */
+ SV_RING_MAIL 2 /* 12 */
+ SV_METAL_SCALE_MAIL 3 /* 13 */
+ SV_CHAIN_MAIL 4 /* 14 */
+ SV_DOUBLE_RING_MAIL 5 /* 15 */
+ SV_AUGMENTED_CHAIN_MAIL 6 /* 16 */
+ SV_DOUBLE_CHAIN_MAIL 7 /* 16 */
+ SV_BAR_CHAIN_MAIL 8 /* 18 */
+ SV_METAL_BRIGANDINE_ARMOUR 9 /* 19 */
+ SV_SPLINT_MAIL 10 /* 19 */
+ SV_PARTIAL_PLATE_ARMOUR 12 /* 22 */
+ SV_METAL_LAMELLAR_ARMOUR 13 /* 23 */
+ SV_FULL_PLATE_ARMOUR 15 /* 25 */
+ SV_RIBBED_PLATE_ARMOUR 18 /* 28 */
+ SV_MITHRIL_CHAIN_MAIL 20 /* 28+ */
+ SV_MITHRIL_PLATE_MAIL 25 /* 35+ */
+ SV_ADAMANTITE_PLATE_MAIL 30 /* 40+ */
+~~~~~38
+/* The "sval" codes for TV_DRAG_ARMOR */
+ SV_DRAGON_BLACK 1
+ SV_DRAGON_BLUE 2
+ SV_DRAGON_WHITE 3
+ SV_DRAGON_RED 4
+ SV_DRAGON_GREEN 5
+ SV_DRAGON_MULTIHUED 6
+ SV_DRAGON_SHINING 10
+ SV_DRAGON_LAW 12
+ SV_DRAGON_BRONZE 14
+ SV_DRAGON_GOLD 16
+ SV_DRAGON_CHAOS 18
+ SV_DRAGON_BALANCE 20
+ SV_DRAGON_POWER 30
+~~~~~39
+/* The sval codes for TV_LITE */
+ SV_LITE_TORCH 0
+ SV_LITE_LANTERN 1
+ SV_LITE_TORCH_EVER 2
+ SV_LITE_DWARVEN 3
+ SV_LITE_FEANORIAN 4
+ SV_LITE_GALADRIEL 100
+ SV_LITE_ELENDIL 101
+ SV_LITE_THRAIN 102
+ SV_LITE_UNDEATH 103
+ SV_LITE_PALANTIR 104
+ SV_ANCHOR_SPACETIME 105
+ SV_STONE_LORE 106
+~~~~~40
+/* The "sval" codes for TV_AMULET */
+ SV_AMULET_DOOM 0
+ SV_AMULET_TELEPORT 1
+ SV_AMULET_ADORNMENT 2
+ SV_AMULET_SLOW_DIGEST 3
+ SV_AMULET_RESIST_ACID 4
+ SV_AMULET_SEARCHING 5
+ SV_AMULET_BRILLANCE 6
+ SV_AMULET_CHARISMA 7
+ SV_AMULET_THE_MAGI 8
+ SV_AMULET_REFLECTION 9
+ SV_AMULET_CARLAMMAS 10
+ SV_AMULET_INGWE 11
+ SV_AMULET_DWARVES 12
+ SV_AMULET_NO_MAGIC 13
+ SV_AMULET_NO_TELE 14
+ SV_AMULET_RESISTANCE 15
+ SV_AMULET_NOTHING 16
+ SV_AMULET_SERPENT 17
+ SV_AMULET_TORIS_MEJISTOS 18
+ SV_AMULET_ELESSAR 19
+ SV_AMULET_EVENSTAR 20
+ SV_AMULET_SUSTENANCE 21
+ SV_AMULET_TELEPATHY 22
+ SV_AMULET_TRICKERY 23
+ SV_AMULET_WEAPONMASTERY 24
+ SV_AMULET_DEVOTION 25
+ SV_AMULET_INFRA 26
+ SV_AMULET_SPELL 27
+ SV_AMULET_WISDOM 28
+ SV_AMULET_RESIST_ELEC 29
+ SV_AMULET_REGEN 30
+~~~~~45
+/* The sval codes for TV_RING */
+ SV_RING_WOE 0
+ SV_RING_AGGRAVATION 1
+ SV_RING_WEAKNESS 2
+ SV_RING_STUPIDITY 3
+ SV_RING_TELEPORTATION 4
+ SV_RING_SPECIAL 5
+ SV_RING_SLOW_DIGESTION 6
+ SV_RING_FEATHER_FALL 7
+ SV_RING_RESIST_FIRE 8
+ SV_RING_RESIST_COLD 9
+ SV_RING_SUSTAIN_STR 10
+ SV_RING_SUSTAIN_INT 11
+ SV_RING_SUSTAIN_WIS 12
+ SV_RING_SUSTAIN_CON 13
+ SV_RING_SUSTAIN_DEX 14
+ SV_RING_SUSTAIN_CHR 15
+ SV_RING_PROTECTION 16
+ SV_RING_ACID 17
+ SV_RING_FLAMES 18
+ SV_RING_ICE 19
+ SV_RING_RESIST_POIS 20
+ SV_RING_FREE_ACTION 21
+ SV_RING_SEE_INVIS 22
+ SV_RING_SEARCHING 23
+ SV_RING_STR 24
+ SV_RING_INT 25
+ SV_RING_DEX 26
+ SV_RING_CON 27
+ SV_RING_ACCURACY 28
+ SV_RING_DAMAGE 29
+ SV_RING_SLAYING 30
+ SV_RING_SPEED 31
+ SV_RING_BARAHIR 32
+ SV_RING_TULKAS 33
+ SV_RING_NARYA 34
+ SV_RING_NENYA 35
+ SV_RING_VILYA 36
+ SV_RING_POWER 37
+ SV_RING_RES_FEAR 38
+ SV_RING_RES_LD 39
+ SV_RING_RES_NETHER 40
+ SV_RING_RES_NEXUS 41
+ SV_RING_RES_SOUND 42
+ SV_RING_RES_CONFUSION 43
+ SV_RING_RES_SHARDS 44
+ SV_RING_RES_DISENCHANT 45
+ SV_RING_RES_CHAOS 46
+ SV_RING_RES_BLINDNESS 47
+ SV_RING_LORDLY 48
+ SV_RING_ATTACKS 49
+ SV_RING_NOTHING 50
+ SV_RING_PRECONITION 51
+ SV_RING_FLAR 52
+ SV_RING_INVIS 53
+ SV_RING_FLYING 54
+ SV_RING_WRAITH 55
+ SV_RING_ELEC 56
+ SV_RING_DURIN 57
+ SV_RING_SPELL 58
+ SV_RING_CRIT 59
+~~~~~55
+/* The "sval" codes for TV_STAFF */
+ SV_STAFF_SCHOOL 1
+ SV_STAFF_NOTHING 2
+~~~~~65
+/* The "sval" codes for TV_WAND */
+ SV_WAND_SCHOOL 1
+ SV_WAND_NOTHING 2
+~~~~~66
+/* The "sval" codes for TV_ROD(Rod Tips) */
+ SV_ROD_NOTHING 0
+ SV_ROD_DETECT_DOOR 1
+ SV_ROD_IDENTIFY 2
+ SV_ROD_RECALL 3
+ SV_ROD_ILLUMINATION 4
+ SV_ROD_MAPPING 5
+ SV_ROD_DETECTION 6
+ SV_ROD_PROBING 7
+ SV_ROD_CURING 8
+ SV_ROD_HEALING 9
+ SV_ROD_RESTORATION 10
+ SV_ROD_SPEED 11
+ SV_ROD_TELEPORT_AWAY 13
+ SV_ROD_DISARMING 14
+ SV_ROD_LITE 15
+ SV_ROD_SLEEP_MONSTER 16
+ SV_ROD_SLOW_MONSTER 17
+ SV_ROD_DRAIN_LIFE 18
+ SV_ROD_POLYMORPH 19
+ SV_ROD_ACID_BOLT 20
+ SV_ROD_ELEC_BOLT 21
+ SV_ROD_FIRE_BOLT 22
+ SV_ROD_COLD_BOLT 23
+ SV_ROD_ACID_BALL 24
+ SV_ROD_ELEC_BALL 25
+ SV_ROD_FIRE_BALL 26
+ SV_ROD_COLD_BALL 27
+ SV_ROD_HAVOC 28
+ SV_ROD_DETECT_TRAP 29
+ SV_ROD_HOME 30
+~~~~~67
+/* The "sval" codes for TV_ROD_MAIN(Rods) */
+ SV_ROD_WOODEN 10
+ SV_ROD_COPPER 20
+ SV_ROD_IRON 50
+ SV_ROD_ALUMINIUM 75
+ SV_ROD_SILVER 100
+ SV_ROD_GOLDEN 125
+ SV_ROD_MITHRIL 160
+ SV_ROD_ADMANTITE 200
+~~~~~70
+/* The "sval" codes for TV_SCROLL */
+ SV_SCROLL_DARKNESS 0
+ SV_SCROLL_AGGRAVATE_MONSTER 1
+ SV_SCROLL_CURSE_ARMOR 2
+ SV_SCROLL_CURSE_WEAPON 3
+ SV_SCROLL_SUMMON_MONSTER 4
+ SV_SCROLL_SUMMON_UNDEAD 5
+ SV_SCROLL_SUMMON_MINE 6
+ SV_SCROLL_TRAP_CREATION 7
+ SV_SCROLL_PHASE_DOOR 8
+ SV_SCROLL_TELEPORT 9
+ SV_SCROLL_TELEPORT_LEVEL 10
+ SV_SCROLL_WORD_OF_RECALL 11
+ SV_SCROLL_IDENTIFY 12
+ SV_SCROLL_STAR_IDENTIFY 13
+ SV_SCROLL_REMOVE_CURSE 14
+ SV_SCROLL_STAR_REMOVE_CURSE 15
+ SV_SCROLL_ENCHANT_ARMOR 16
+ SV_SCROLL_ENCHANT_WEAPON_TO_HIT 17
+ SV_SCROLL_ENCHANT_WEAPON_TO_DAM 18
+ SV_SCROLL_ENCHANT_WEAPON_PVAL 19
+ SV_SCROLL_STAR_ENCHANT_ARMOR 20
+ SV_SCROLL_STAR_ENCHANT_WEAPON 21
+ SV_SCROLL_RECHARGING 22
+ SV_SCROLL_RESET_RECALL 23
+ SV_SCROLL_LIGHT 24
+ SV_SCROLL_MAPPING 25
+ SV_SCROLL_DETECT_GOLD 26
+ SV_SCROLL_DETECT_ITEM 27
+ SV_SCROLL_DETECT_TRAP 28
+ SV_SCROLL_DETECT_DOOR 29
+ SV_SCROLL_DETECT_INVIS 30
+ SV_SCROLL_DIVINATION 31
+ SV_SCROLL_SATISFY_HUNGER 32
+ SV_SCROLL_BLESSING 33
+ SV_SCROLL_HOLY_CHANT 34
+ SV_SCROLL_HOLY_PRAYER 35
+ SV_SCROLL_MONSTER_CONFUSION 36
+ SV_SCROLL_PROTECTION_FROM_EVIL 37
+ SV_SCROLL_RUNE_OF_PROTECTION 38
+ SV_SCROLL_TRAP_DOOR_DESTRUCTION 39
+ SV_SCROLL_DEINCARNATION 40
+ SV_SCROLL_STAR_DESTRUCTION 41
+ SV_SCROLL_DISPEL_UNDEAD 42
+ SV_SCROLL_MASS_RESURECTION 43
+ SV_SCROLL_GENOCIDE 44
+ SV_SCROLL_MASS_GENOCIDE 45
+ SV_SCROLL_ACQUIREMENT 46
+ SV_SCROLL_STAR_ACQUIREMENT 47
+ SV_SCROLL_FIRE 48
+ SV_SCROLL_ICE 49
+ SV_SCROLL_CHAOS 50
+ SV_SCROLL_RUMOR 51
+ SV_SCROLL_ARTIFACT 52
+ SV_SCROLL_NOTHING 53
+ SV_SCROLL_SPELL 54
+~~~~~71
+/* The "sval" codes for TV_POTION */
+ SV_POTION_WATER 0
+ SV_POTION_APPLE_JUICE 1
+ SV_POTION_SLIME_MOLD 2
+ SV_POTION_BLOOD 3
+ SV_POTION_SLOWNESS 4
+ SV_POTION_SALT_WATER 5
+ SV_POTION_POISON 6
+ SV_POTION_BLINDNESS 7
+ SV_POTION_INVIS 8
+ SV_POTION_CONFUSION 9
+ SV_POTION_MUTATION 10
+ SV_POTION_SLEEP 11
+ SV_POTION_LEARNING 12
+ SV_POTION_LOSE_MEMORIES 13
+ SV_POTION_RUINATION 15
+ SV_POTION_DEC_STR 16
+ SV_POTION_DEC_INT 17
+ SV_POTION_DEC_WIS 18
+ SV_POTION_DEC_DEX 19
+ SV_POTION_DEC_CON 20
+ SV_POTION_DEC_CHR 21
+ SV_POTION_DETONATIONS 22
+ SV_POTION_DEATH 23
+ SV_POTION_INFRAVISION 24
+ SV_POTION_DETECT_INVIS 25
+ SV_POTION_SLOW_POISON 26
+ SV_POTION_CURE_POISON 27
+ SV_POTION_BOLDNESS 28
+ SV_POTION_SPEED 29
+ SV_POTION_RESIST_HEAT 30
+ SV_POTION_RESIST_COLD 31
+ SV_POTION_HEROISM 32
+ SV_POTION_BESERK_STRENGTH 33
+ SV_POTION_CURE_LIGHT 34
+ SV_POTION_CURE_SERIOUS 35
+ SV_POTION_CURE_CRITICAL 36
+ SV_POTION_HEALING 37
+ SV_POTION_STAR_HEALING 38
+ SV_POTION_LIFE 39
+ SV_POTION_RESTORE_MANA 40
+ SV_POTION_RESTORE_EXP 41
+ SV_POTION_RES_STR 42
+ SV_POTION_RES_INT 43
+ SV_POTION_RES_WIS 44
+ SV_POTION_RES_DEX 45
+ SV_POTION_RES_CON 46
+ SV_POTION_RES_CHR 47
+ SV_POTION_INC_STR 48
+ SV_POTION_INC_INT 49
+ SV_POTION_INC_WIS 50
+ SV_POTION_INC_DEX 51
+ SV_POTION_INC_CON 52
+ SV_POTION_INC_CHR 53
+ SV_POTION_AUGMENTATION 55
+ SV_POTION_ENLIGHTENMENT 56
+ SV_POTION_STAR_ENLIGHTENMENT 57
+ SV_POTION_SELF_KNOWLEDGE 58
+ SV_POTION_EXPERIENCE 59
+ SV_POTION_RESISTANCE 60
+ SV_POTION_CURING 61
+ SV_POTION_INVULNERABILITY 62
+ SV_POTION_NEW_LIFE 63
+~~~~~72
+/* The "sval" codes for TV_POTION2 */
+ SV_POTION2_MIMIC_ABOMINATION 1
+ SV_POTION2_MIMIC_WOLF 2
+ SV_POTION2_MIMIC_APE 3
+ SV_POTION2_MIMIC_GOAT 4
+ SV_POTION2_MIMIC_INSECT 5
+ SV_POTION2_MIMIC_SPARROW 6
+ SV_POTION2_MIMIC_STATUE 7
+ SV_POTION2_MIMIC_VAMPIRE 8
+ SV_POTION2_MIMIC_SPIDER 9
+ SV_POTION2_MIMIC_MANA_BALL 10
+ SV_POTION2_MIMIC_FIRE_CLOUD 11
+ SV_POTION2_MIMIC_COLD_CLOUD 12
+ SV_POTION2_MIMIC_CHAOS_CLOUD 13
+ SV_POTION2_CURE_LIGHT_SANITY 14
+ SV_POTION2_CURE_SERIOUS_SANITY 15
+ SV_POTION2_CURE_CRITICAL_SANITY 16
+ SV_POTION2_CURE_SANITY 17
+ SV_POTION2_CURE_WATER 18
+~~~~~80
+/* The "sval" codes for TV_FOOD */
+ SV_FOOD_POISON 0
+ SV_FOOD_BLINDNESS 1
+ SV_FOOD_PARANOIA 2
+ SV_FOOD_CONFUSION 3
+ SV_FOOD_HALLUCINATION 4
+ SV_FOOD_PARALYSIS 5
+ SV_FOOD_WEAKNESS 6
+ SV_FOOD_SICKNESS 7
+ SV_FOOD_STUPIDITY 8
+ SV_FOOD_NAIVETY 9
+ SV_FOOD_UNHEALTH 10
+ SV_FOOD_DISEASE 11
+ SV_FOOD_CURE_POISON 12
+ SV_FOOD_CURE_BLINDNESS 13
+ SV_FOOD_CURE_PARANOIA 14
+ SV_FOOD_CURE_CONFUSION 15
+ SV_FOOD_CURE_SERIOUS 16
+ SV_FOOD_RESTORE_STR 17
+ SV_FOOD_RESTORE_CON 18
+ SV_FOOD_RESTORING 19
+ SV_FOOD_BISCUIT 32
+ SV_FOOD_JERKY 33
+ SV_FOOD_RATION 35
+ SV_FOOD_SLIME_MOLD 36
+ SV_FOOD_WAYBREAD 37
+ SV_FOOD_PINT_OF_ALE 38
+ SV_FOOD_PINT_OF_WINE 39
+ SV_FOOD_ATHELAS 40
+ SV_FOOD_GREAT_HEALTH 41
+ SV_FOOD_FORTUNE_COOKIE 42
+~~~~~04
+/* The "sval" codes for TV_BATERIE */
+ SV_BATERIE_POISON 1
+ SV_BATERIE_EXPLOSION 2
+ SV_BATERIE_TELEPORT 3
+ SV_BATERIE_COLD 4
+ SV_BATERIE_FIRE 5
+ SV_BATERIE_ACID 6
+ SV_BATERIE_LIFE 7
+ SV_BATERIE_CONFUSION 8
+ SV_BATERIE_LITE 9
+ SV_BATERIE_CHAOS 10
+ SV_BATERIE_TIME 11
+ SV_BATERIE_MAGIC 12
+ SV_BATERIE_XTRA_LIFE 13
+ SV_BATERIE_DARKNESS 14
+ SV_BATERIE_KNOWLEDGE 15
+ SV_BATERIE_FORCE 16
+ SV_BATERIE_LIGHTNING 17
+ SV_BATERIE_MANA 18
+ MAX_BATERIE_SVAL 18
+~~~~~09
+/* The "sval" codes for TV_CORPSE */
+ SV_CORPSE_CORPSE 1
+ SV_CORPSE_SKELETON 2
+ SV_CORPSE_HEAD 3
+ SV_CORPSE_SKULL 4
+ SV_CORPSE_MEAT 5
+~~~~~115
+/* The "sval" codes for TV_DAEMON_BOOK */
+ SV_DEMONBLADE 55
+ SV_DEMONSHIELD 56
+ SV_DEMONHORN 57
diff --git a/lib/help/dungeon.txt b/lib/help/dungeon.txt
new file mode 100644
index 00000000..5c3cba9f
--- /dev/null
+++ b/lib/help/dungeon.txt
@@ -0,0 +1,706 @@
+|||||oy
+~~~~~02|Dungeons
+#####R /----------------------------------------\
+#####R < The Dungeons and Places of Middle-earth >
+#####R \----------------------------------------/
+
+ *****dungeon.txt*04[Symbols On Your Map] *****dungeon.txt*07[The Town and Buildings]
+ *****dungeon.txt*01[The Wilderness] *****dungeon.txt*06[In the Dungeon]
+ *****dungeon.txt*08[Objects] *****dungeon.txt*13[Mining]
+ *****dungeon.txt*12[Doors, Rooms, Staircases etc.] *****dungeon.txt*18[Pets]
+ *****dungeon.txt*14[Winning] *****dungeon.txt*15[Dying]
+ *****dungeon.txt*16[Where to get more help]
+
+After you have *****birth.txt*0[created your character], you will begin your ToME
+adventure. Symbols appearing on your screen will represent the world's
+walls, floor, objects, features, and creatures lurking about. In order
+to direct your character through his adventure, you will enter single
+character commands (see "*****command.txt*0[command.txt]").
+
+~~~~~03|Symbols
+~~~~~04|Identifying features
+#####R=== Symbols On Your Map ===
+
+Symbols on your map can be broken down into three categories: Features of
+the world such as walls, floor, doors, and traps; Objects which can be
+picked up such as treasure, weapons, magical devices, etc; and creatures
+which may or may not move about the dungeon, but are mostly harmful to your
+character's well being.
+
+Some symbols are used to represent more than one type of entity, and some
+symbols are used to represent entities in more than one category. The "@"
+symbol (by default) is used to represent the character.
+
+It will not be necessary to remember all of the symbols and their meanings.
+The "slash" command ("/") will identify any character appearing on your map
+(see "*****command.txt*0[command.txt]").
+
+Note that you can use a *****command.txt*105["user pref file"] to change any of these symbols to
+something you are more comfortable with.
+
+
+#####G Features that do not block line of sight
+
+ . A floor space 1 Entrance to General Store
+ . A trap (hidden) 2 Entrance to Armoury
+ ^ A trap (known) 3 Entrance to Weapon Smith
+ [[[[[y;] A glyph of warding 4 Entrance to Temple
+ [[[[[U'] An open door 5 Entrance to Alchemy Shop
+ [[[[[U'] A broken door 6 Entrance to Magic Shop
+ < A staircase up 7 Entrance to the Black Market
+ [[[[[y<] A quest exit 8 Entrance to your Home
+ [[[[[r<] A quest up level 9 Entrance to Bookstore
+ [[[[[U<] A shaft up [[[[[r>] A quest down level
+ > A staircase down [[[[[U>] A shaft down
+ [[[[[y>] A quest entrance [[[[[v>] Dungeon entrance
+ _ A fountain [[[[[D_] An empty fountain
+ * Straight road start/exit [[[[[B*] Section of the Straight Road
+ [[[[[b*] Section of the Straight Road [[[[[W*] Section of the Straight Road
+ [[[[[D*] Corrupted straight road [[[[[R*] An explosive rune
+ [[[[[B~] Stream of water (shallow) [[[[[b~] Stream of water (deep)
+ [[[[[u~] Tainted stream (water) [[[[[s#] Underground tunnel
+ [[[[[U#] Pool of lava (shallow) [[[[[r#] Pool of lava (deep)
+ [[[[[D#] Dark pit [[[[[u.] Dirt
+ [[[[[g.] Patch of Grass [[[[[W.] Ice
+ [[[[[y.] Sand [[[[[D.] Ash
+ [[[[[u.] Mud [[[[[v.] Nether mist
+ [[[[[r.] Floor [[[[[D0] Altar of Darkness
+ [[[[[R0] Altar of Force [[[[[B0] Altar of Winds
+ [[[[[W0] Altar of Being [[[[[v+] Void Jumpgate
+ [[[[[v;] Monster trap [[[[[B.] Glass wall
+ [[[[[w#] Illusion wall [[[[[g;] Grass with flowers
+ [[[[[w.] Cobblestone road [[[[[g#] Small tree
+ [[[[[w*] Town (in wilderness) [[[[[U^] Underground tunnel
+ [[[[[y+] A web
+
+#####G Features that block line of sight
+
+ [[[[[w#] A secret door # A wall
+ [[[[[U+] A closed door % A mineral vein
+ [[[[[U+] A locked door [[[[[o*] A mineral vein + treasure
+ [[[[[U+] A jammed door [[[[[w:] A pile of rubble
+ [[[[[D#] A dead tree [[[[[W#] Ice wall
+ [[[[[G#] A tree [[[[[y#] Sand wall
+ [[[[[U^] A mountain chain [[[[[W^] High mountain chain
+
+
+#####G Objects
+
+ ! A potion (or flask) / A pole-arm
+ ? A scroll, book, map, parchment / Music instrument
+ ? A rune, runestone | An edged weapon
+ , A mushroom (or food) \ A hafted weapon or digger
+ - A wand, rod or rod tip } A sling, bow, or x-bow
+ _ A staff { A shot, arrow, bolt, boomerang
+ = A ring ( Soft armour/cloak
+ " An amulet [ Hard armour
+ $ Gold or gems ] Misc. armour
+ ~ Lites, Tools, Chests, etc ) A shield
+ ~ Junk, Sticks, Skeletons, etc ` Trapping kit, climbing set
+ ~ Stone, random artifact o Egg
+ * An essence & (unused)
+
+~~~~~05|Monsters
+#####G Monsters
+
+ $ Creeping Coins , Mushroom Patch
+ a Giant Ant A Angelic being
+ b Giant Bat B Bird
+ c Giant Centipede C Canine
+ d Dragon D Ancient Dragon
+ e Floating Eye E Elemental
+ f Feline F Dragon Fly
+ g Golem G Ghost
+ h Humanoids H Hybrid
+ i Icky-Thing I Insect
+ j Jelly J Snake
+ k Kobold K Killer Beetle
+ l Giant Louse L Lich
+ m Mold M Multi-Headed Hydra
+ n Naga N (unused)
+ o Orc O Ogre
+ p Human P Giant Human(oid)
+ q Quadruped Q Quylthulg
+ r Rodent R Reptile/Amphibian
+ s Skeleton S Spider/Scorpion/Tick
+ t Townsperson T Troll
+ u Minor demon U Major demon
+ v Vortex V Vampire
+ w Worm or Worm Mass W Wight/Wraith
+ x (unused) X Xorn/Xaren
+ y Yeek Y Yeti
+ z Zombie/Mummy Z Zephyr Hound
+
+
+~~~~~07|Town
+#####R=== The Town Level ===
+
+The town level is where you will begin your adventure. The town consists of
+several buildings (most with an entrance), some townspeople, and a main wall
+which surrounds the town (with gates in it). Outside the gates may be found
+unclaimed lands and wilderness, where beasts still run wild. The first time
+you are in town it will be daytime (unless you are an undead character), but
+note that the sun will rise and set (rather instantly) as time passes.
+
+There are a few different towns around the world map, and your starting town
+will eventually become too small for you (if you survive the dangers of the
+dungeon). Other towns will have some different facilities, and you can find
+your way to other towns by reading the "Adventurer's Guide to Middle-earth"
+parchment with which *every* character begins the game.
+
+
+#####R=== Townspeople ===
+
+The town contains many different kinds of people. There are the street
+urchins, young children who will mob an adventurer for money, and seem to
+come out of the woodwork when excited. Blubbering idiots are a constant
+annoyance, but not harmful. Public drunks wander about the town singing,
+and are of no threat to anyone. Sneaky rogues who work for the black
+market are always greedily eyeing your backpack for potential new
+'purchases'... And finally, what town would be complete without a
+swarm of half drunk warriors, who take offense or become annoyed just for
+the fun of it.
+
+Most of the townspeople should be avoided by the largest possible distance
+when you wander from store to store. Fights will break out, though, so be
+prepared. Since your character grew up in this world of intrigue, no
+experience is awarded for killing the town inhabitants, though you may
+acquire treasure.
+
+~~~~~21|Buildings
+#####R=== Town Buildings ===
+
+Your character will begin his adventure with some basic supplies, and some
+extra gold with which to purchase more supplies at the town stores.
+
+You may enter any open store and barter with the owner for items you can
+afford. When bartering, you enter prices you will pay (or accept) for some
+object. You can either enter the absolute amount, or precede a number with
+a plus or minus sign to give a positive or negative increment on your
+previous offer. But be warned that the owners can easily be insulted, and
+may even throw you out for a while if you insult them too often. [[[[[BTo enter]
+[[[[[Ba store, simply move onto the entrance, which is represented by a number]
+[[[[[Bfrom 1 to 9.]
+
+If you consistently bargain well in a store, that is, you reach the final
+offer much more often than not, then the store owner will eventually
+recognise that you are a superb haggler, and will go directly to the final
+offer instead of haggling with you. Items which cost less than 10 gold
+pieces do not count, as haggling well with these items is usually either
+very easy or almost impossible. The more expensive the item is, the less
+likely the store owner is to assume that you are a good haggler. Note that
+you may disable haggling with a software option, though this will inflict a
+10% "sales tax" on all purchases for which the store owner would have
+required you to haggle.
+
+Once inside a store, you will see the name and race of the store owner, the
+name of the store, the maximum amount of cash that the store owner will pay
+for any one item, and the store inventory, listed along with tentative
+prices, which will become "fixed" (at the "final offer") should you ever
+manage to haggle a store owner down to his final offer.
+
+You will also see an (incomplete) list of available commands. Note that
+many of the commands which work in the dungeon work in the stores as well,
+but some do not, especially those which involve "using" objects.
+
+Stores do not always have everything in stock. As the game progresses, they
+may get new items, so check back from time to time. Also, if you sell them
+an item, it may get sold to a customer while you are adventuring, so don't
+always expect to be able to get back everything you have sold. If you have
+a lot of spare gold, you can purchase every item in a store, which will
+induce the store owner to bring out new stock, and perhaps even retire. If
+you are low on funds (and morals), you may attempt to steal an item from
+the store, but beware -- if you are caught, the store owner will not let you
+back in for a very long time.
+
+Store owners will not buy harmful or useless items. If an object is
+unidentified, they will pay you some base price for it. Once they have
+bought it they will immediately identify the object. If it is a good object,
+they will add it to their inventory. If it was a bad bargain, they simply
+throw the item away. In any case, you may receive some knowledge of the
+item in case another is encountered.
+
+#####GThe General Store ("1")
+ The General Store sells foods, drinks, some clothing, torches, lamps,
+ oil, shovels, picks, and spikes. All of these items and some others
+ can be sold back to the General store for money.
+
+#####GThe Armoury ("2")
+ The Armoury is where the town's armour is fashioned. All sorts of
+ protective gear may be bought and sold here.
+
+#####GThe Weaponsmith's Shop ("3")
+ The Weaponsmith's Shop is where the town's weapons are fashioned. Hand
+ and missile weapons may be purchased and sold here, along with arrows,
+ bolts, and shots.
+
+#####GThe Temple ("4")
+ The Temple deals in healing and restoration potions, as well as bless
+ scrolls, word of recall scrolls, some approved priestly weapons, and
+ priest spell books.
+
+#####GThe Alchemy shop ("5")
+ The Alchemy Shop deals in all types of potions and scrolls.
+
+#####GThe Magic User's Shop ("6")
+ The Magic User's Shop deals in all sorts of rings, wands, amulets, and
+ staves, as well as spell books.
+
+#####GThe Black Market ("7")
+ The Black Market will sell and buy anything at extortionate prices.
+ However it occasionally has VERY good items in it. The shopkeepers are
+ not known for their tolerance...
+
+#####GYour Home ("8")
+ This is your house where you can store objects that you cannot carry
+ on your travels, or will need at a later date.
+
+#####GThe Bookstore ("9")
+ The Bookstore deals in all sorts of magical books. You can purchase
+ and sell spellbooks for spellcasters and priests here.
+
+
+#####ROther Buildings
+In addition to the basic stores, there are some special buildings that can be
+found in some towns. These Buildings (represented by +'s) include:
+
+#####GMayor's Office/Castle
+ The home office for the town. Adventurers looking for work besides
+ exploring the dungeon should hunt in here.
+
+#####GPet Shop
+ Great place to purchase eggs and get pets.
+
+#####GThe Soothsayer
+ To discover what *****/afatespoi.txt*0[fates ("a")] lie in store for you.
+
+#####GThe Prancing Pony
+ Wine, dine, rest and relax!
+
+#####GThe Nest
+ Thunderlords are masters of teleportation, and will consent to bear you
+ to your chosen dungeon destination for a fee.
+
+#####GBeastmaster Shanty
+ For those who enjoy trophy hunting, and to research that strange animal
+ you saw during your adventures.
+
+#####GFighters Hall
+ The place to reforge weapons and armour.
+
+#####GRangers Guild
+ The place to reforge distance weapons and their ammunition.
+
+#####GLibrary
+ For information of all kinds.
+
+#####GGambling House
+ Read the *****/bgambling.txt*0[rules ("b")] before paying. The games are
+ not rigged, just naturally difficult.
+
+#####GTower of Magery/Wizards Spire
+ The wizards will identify your items or recharge your magical items for
+ a fee.
+
+#####GInner temple/Priests Circle
+ A place of healing.
+
+#####GPaladin guild
+ Some healing and enchantments available.
+
+#####GThe Mathom House
+ Donate important, but unwanted items you find in the dungeon.
+
+
+~~~~~1|Wilderness
+#####R=== The Wilderness and the Wilderness Map ===
+
+Between the towns, the hand of civilisation has not tamed the lands, and
+wild creatures run rampant. This is another place that is worth exploring.
+Hidden within the wilderness are several interesting locations, with the
+four main ones for any adventurer - Barrow Downs, Mirkwood, Mordor and
+Angband each being located at or near one of the main towns of Middle-
+earth. These locations should be explored consecutively, as each one
+increases in difficulty from the point where the previous dungeon finished.
+A new character should not try to go at Mordor or Angband as their first
+dungeon (well, not if you wish to survive your first step, anyway)!
+
+As well as these (and other) locations, the wilderness can be a good place
+to go when you are seeking a change from the scenery of the dungeons, or just
+a bit of fast experience.
+
+Be warned - some creatures found in the wilderness can be quite dangerous,
+and travel through the wilderness can be time-consuming. If you are wishing
+to simply move to another town, there is an overview map (called the
+"Wilderness Map") that can be travelled through by going up "<" from the
+town level. While travelling through this map, your character is still having
+to actually walk through each square of the normal view, but you only see the
+end result of them moving from one 4x4 panel to the next. As such, food
+consumption will appear to be much higher in the Wilderness View than it is
+normally, and it is recommended that you travel prepared. It is also possible
+for the wild creatures within the wilderness to ambush you when travelling,
+which will force you out of the Wilderness Map so that you can safely get
+yourself out of trouble, before continuing on your way.
+
+All of the special locations can be seen as downstairs (">") on the
+Wilderness Map and towns as "*"s. This makes it *much* easier to find your
+way from one interesting place to another.
+
+The "Adventurer's Guide to Middle-earth" (a parchment with which *every*
+character begins the game) contains details about the towns and some of
+the dungeons, including rough directions on how to get there.
+
+~~~~~06|Dungeons|In the dungeon
+#####R=== Within The Dungeon ===
+
+Once your character is adequately supplied with food, light, armor, and
+weapons, he is ready to enter Barrow Downs. Move on top of the ">" symbol
+and use the "Down" command (">").
+
+Your character will enter a maze of interconnecting staircases and finally
+arrive somewhere on the first level of the dungeon. Each level of the
+dungeon is fifty feet high (thus dungeon level "Lev 1" is often called
+"50 ft"), and is divided into rectangular regions several times
+larger than the screen. Once you leave a level by a
+staircase, you will never again find your way back to that region of that
+level, but there are an infinite number of other regions at that same "depth"
+that you can explore later. So be careful that you have found all the
+treasure before you leave a level, or you may never find it again! The
+monsters, of course, can use the stairs, and you may eventually encounter
+them again.
+
+In the dungeon, there are many things to find, but your character must
+survive many horrible and challenging encounters to find the treasure lying
+about and take it safely back to the town to sell.
+
+~~~~~23|Light
+There are two sources for light once inside the dungeon: permanent light
+which has been magically placed within rooms, and a light source carried by
+the player (or some of the monsters). If neither is present, the character
+will be unable to see. This will affect searching, picking locks, disarming
+traps, reading scrolls, casting spells, browsing books, etc. So be very
+careful not to run out of light!
+
+A character must wield a torch or lamp in order to supply his own light. A
+torch or lamp burns fuel as it is used, and once it is out of fuel, it stops
+supplying light. You will be warned as the light approaches this point. You
+may use the "Fuel" command ("F") to refuel your lantern (with flasks of oil)
+or your torch (with other torches), so it is a good idea to carry extra
+torches or flasks of oil, as appropriate. There are rumours of objects of
+exceptional power which glow with their own never-ending light.
+
+~~~~~08|Objects
+#####R=== Objects Found In The Dungeon ===
+
+The mines are full of objects just waiting to be picked up and used. How
+did they get there? Well, the main source for useful items are all the
+foolish adventurers (like you?) that proceeded into the dungeon before you.
+They get killed, and the helpful creatures scatter the various treasures
+throughout the dungeon. Most cursed items are placed there by the joyful evil
+sorcerers, who enjoy a good joke when it gets you killed.
+
+You pick up objects by moving on top of them. You can carry up to 23
+different items in your backpack while wearing and wielding up to 12 others.
+Although you are limited to 23 different items, each item may actually be a
+"pile" of up to 99 similar items. If you somehow manage to stuff 24 items
+into your pack, for example, by removing an item from your head while your
+pack is full, then your pack will "overflow" and the most recently added
+item will fall out and onto the ground. You will be warned about any command
+that seems likely to induce this behaviour.
+
+You are, in addition, limited in the total amount of weight that you can
+carry. As you approach this value, you become slower, making it easier for
+monsters to chase you. Note that there is no upper bound on how much you can
+carry, if you do not mind being slow. Your weight limit is determined by your
+strength.
+
+Objects do not block the line of sight, but may stack on top of one another,
+with the one on top hiding others beneath it.
+
+ Q: I'm standing on a pile of items. How do I see what's in the pile
+ without picking it all up, moving it, or destroying it all?
+ A: 1. Stand on the pile in question
+ 2. Type shift + i (examine)
+ 3. Type - (examine items on floor)
+ 4. Type * (expand list of items on floor)
+ 5. (as needed) Type letter associated with item to look at it more
+ closely.
+
+Objects may also obscure stairs, Ways and void jumpgates.
+
+ Q: I'm standing on a pile of items. Is there a command to see if
+ there is a stair beneath the pile?
+ A: Stairs, void jumpgates and Ways that obscured by clutter still
+ function.
+ You are advised to take a good hard look at your surroundings before
+ creating lots of dungeon clutter. You can see if there is a stair
+ beneath the pile with either of these methods:
+ 1. Pick up, move, or eliminate the pile.
+ 2. Press l (look), then select the square you wish to inquire about.
+ Press <enter>; it will scroll through everything on the ground,
+ and eventually it ends with "It is in a Void Jumpgate", or
+ whatever.
+
+
+Many objects found within the dungeon have special commands for their use.
+Wands must be Aimed, staves must be Used, scrolls must be Read, and potions
+must be Quaffed. You may, in general, not only use items in your pack, but
+also items on the ground, if you are standing on top of them. For a detailed
+list of the commands to use objects, see *****command.txt*0[command.txt].
+
+Chests are complex objects, containing traps, locks, and possibly treasure
+or other objects inside them once they are opened. Many of the commands that
+apply to traps or doors also apply to chests and, like traps and doors, these
+commands do not work if you are carrying the chest.
+
+One item in particular will be discussed here. [[[[[BThe scroll of "Word of]
+[[[[[BRecall"] can be found within the dungeon, or bought at the temple in
+town. It acts in two manners, depending upon your current location. If read
+within the dungeon, it will teleport you back to town. If read in town, it
+will teleport you back down to the deepest level of the dungeon to which your
+character has previously journeyed. This makes the scroll very useful for
+getting back to the deeper levels of the dungeon. Once the scroll has been
+read it takes a while for the spell to act, so don't expect it to save you
+in a crisis. Reading a second scroll before the first has had a chance to
+take effect will cancel both scrolls. Since an accidental dive to a new depth
+(via a trapdoor, for example), may result in the Word of Recall dungeon depth
+being 'broken', so to speak (meaning that the next Word of Recall in town
+will take you back deeper than you would like to), there is a feature in
+ToME which allows you to read a scroll of Word of Recall on a different
+level and 'reset' the recall depth to that level (instead of the deepest
+level). Some dungeons cannot be recalled into, though you can still recall
+out.
+
+You may "inscribe" any object with a textual inscription of your choice.
+These inscriptions are not limited in length, though you may not be able to
+see the whole inscription on the item. The game applies special meaning to
+inscriptions containing any text of the form "@#" or "@x#" or "!x" or "!*",
+see "*****command.txt*0[command.txt]" and "*****macrofaq.txt*0[macrofaq.txt]".
+
+The game provides some "fake" inscriptions to help you keep track of your
+possessions. Wands and staves which are known to be empty will be inscribed
+with "empty". Objects which have been tried at least once but haven't been
+identified yet will be inscribed with "tried". Cursed objects are inscribed
+with "cursed". Broken objects may be inscribed with "broken". Also, any
+item which was purchased at a discount, implying that it is slightly
+"sub-standard", will be inscribed with the appropriate "discount", such as
+"25% off". Note that these inscriptions are fake, and cannot be removed,
+though they can be covered up by a real inscription if you so desire. Try
+"_" as a nice short one.
+
+Also, occasionally you will notice that something in your inventory or
+equipment list seems to be magical. High level characters are much more
+likely to notice this than beginning characters. When you do notice this,
+the item in question will be inscribed with "good" or "cursed" as is
+relevant. You can increase your ability to notice magical effects of armour
+and weapons by increasing the *****skills.txt*01[Combat] skill. You can increase your ability
+to sense particularly well enchanted magical items (potions, scrolls. wands
+etc) by increasing your *****skills.txt*21[Magic] skill. If you increase these
+high enough, you will gain a special method of "sensing" your
+inventory/equipment items, which tells you not only whether an item is "good"
+or "cursed", but also if it is "average", "special", "excellent", "terrible" or
+"worthless".
+
+~~~~~21|Objects|Colour of inventory slot letter
+The colour of the letter that identifies each item in your backpack can tell
+you something about their magical status. Grey indicates the item has not been
+identified yet. After identification, the colour changes to one of the
+following: white, indicating it is normal; blue indicates it is an ego-item
+(pseudo-id's as {excellent}); yellow indicates it is an artifact {special};
+green shows it is an artifact which is part of a set.
+
+It is rumoured that rings of power and extra rare spell books may be found
+deeper in the dungeon....
+
+And lastly, a final warning: not all objects are what they seem. The line
+between tasty food and annoying mushroom is a fine one, and sometimes a
+potion will reach out and bite you...
+~~~~~09|Objects|Cursed Objects
+~~~~~10|Cursed Objects
+#####R=== Cursed Objects ===
+
+Some objects, mainly armour and weapons, have had curses laid upon them.
+These horrible objects will look like any other normal item, but will
+detract from your character's stats or abilities if worn. They will also
+be impossible to remove until the curse is removed. In fact some are
+so badly cursed that even this will not work, and more potent methods are
+needed.
+
+If you wear or wield a cursed item, you will immediately feel something
+wrong. The item will also be inscribed "cursed".
+
+Shopkeepers will refuse to buy any known cursed item.
+~~~~~13|Mining
+~~~~~11|Dungeons|Mining
+#####R=== Digging and Mining ===
+
+It is possible for you to be trapped within the dungeon. You will not be able
+to dig your way out without a digging tool (shovel, pick, or other means of
+digging). It is absolutely essential to always carry some kind of digging tool,
+even when you are not planning on tunnelling for treasure. Do not leave the
+town level of Bree without a digger!
+
+Picks and shovels have a digging ability expressed as "(+<num>)", e.g. (+2).
+The higher the number, the better the digging ability of the tool. Diggers are
+effective against rubble, trees, and many walls. Rubble and veins may hide
+treasure; trees do not.
+
+You dig in something with the tunnel (shift + t) command. Thorough digging
+removes one ASCII square (i.e. tile) of what is being dug. This may require
+multiple attempts depending on how good your digger is (and how high your
+strength is). Once the square is removed, you will be informed if you found
+anything there. If another diggable square exists beyond the area you just dug,
+you can begin the process again.
+
+Some dungeons contain rich strikes which may be found only by mining it out of
+the walls. Quartz veins are the richest, yielding the most metals and gems, but
+magma veins may also hide hoards within them. When digging rock, granite is
+much harder to dig through than quartz or magma veins, so it is much faster to
+follow a vein exactly and dig around the granite. There is also a game option
+for highlighting magma and quartz within the walls, which makes this easier.
+
+If the character has a scroll, staff, or spell of treasure location, she can
+immediately locate all strikes of treasure within a vein shown on the screen.
+This makes mining much easier and more profitable.
+~~~~~12|Dungeons|Doors, Passages, Rooms and Staircases
+#####R=== Staircases, Ways, Void jumpgates, Secret Doors, Passages, and Rooms ===
+
+Staircases are the manner in which you get deeper or climb out of the
+dungeon. The symbols for the up and down staircases are the same as the
+commands to use them. A "<" represents an up staircase and a ">" represents
+a down staircase. You must move your character over the staircase before
+you can use it. You use it by typing the same character as the staircase
+itself (either "<" or ">".)
+
+In flat environments such as forests, Ways replace staircases. On the map, Ways
+are identical to staircases and behave the same way.
+
+Yellow down stairs and Ways are quest entrances (although not every quest
+is reached by such means).
+
+Shafts are also represented by "<" or ">", but are brown. They work similarly
+to stairs and Ways, but if you use one, you might traverse more than one
+dungeon level all in one go as a result.
+
+Stairs, impenetrable walls, and shop entrances like titanium walls, and the
+doors into shops, cannot be destroyed by any means (although their location can
+occasionally change under the right circumstances).
+~~~~~23|Void jumpgates
+A void jumpgate appears on your map as a violet "+". Jumpgates always occur in
+pairs. To activate a jumpgate, stand on it and type ">". You will instantly
+appear on top of its paired jumpgate, which will be somewhere else on the same
+dungeon level.
+
+Many secret doors are used within the dungeon to confuse and demoralise
+adventurers foolish enough to enter. But with some luck, and lots of
+concentration, you can find these secret doors. Secret doors will sometimes
+hide rooms or corridors, or even entire sections of that level of the
+dungeon. Sometimes they simply hide small empty closets or even dead ends.
+Secret doors always look like granite walls, just like traps always look
+like normal floors.
+
+Creatures in the dungeon will generally know and use these secret doors, and
+can sometimes be counted on to leave them open behind them when they pass
+through.
+
+For historical reasons, secret doors are never locked.
+
+~~~~~18|Pets
+~~~~~19|Companions
+~~~~~20|Monsters|Pets
+#####R=== Pets and Companions ===
+You may, in the course of a game, acquire friendly monsters who will help you
+defeat enemies. There are several different types of these, you can determine
+which your monster is by 'l'ooking at it.
+[[[[[vneutral] This monster will not help you by attacking other monsters, but nor
+ will it attack you.
+[[[[[vco-aligned] This monster will attack other enemy monsters, but you will not
+ gain any experience for its kills.
+[[[[[vpet] This monster will kill things for you. The amount of experience you gain
+ from its kills is determined by the level of your *****skills.txt*42[Monster-lore] skill.
+ This monster will gain levels and experience of its own, but cannot travel
+ between dungeon levels.
+[[[[[vcompanion] This type of monster will not only take experience and level up like
+ pets, but will also follow you from one dungeon level to the next. If
+ you successfully complete an adventurer quest for a lost sword and let
+ him join you, he will become a companion. Once again the amount of
+ experience you gain from a companion's kill depends upon your
+ Monster-lore skill.
+
+Your Monster-lore skill also determines the maximum number of pets and
+companions you can have at any one time.
+
+Pets, companions and co-aligned creatures cannot deliver killing blows to
+uniques or quest monsters. You must do this yourself!
+
+You can give commands to pets and companions to make them more useful, using
+the "P" command. The list of available commands is as follows:
+[[[[[vdismiss companions] Dismisses your companions. They can be difficult to get rid
+ of any other way.
+[[[[[vdismiss pets] Dismisses pets. You will be given the opportunity to dismiss all
+ current pets, or if you answer no to that first question, to
+ dismiss specific pets.
+[[[[[vcall pets] Calls your pets (and companions) to you.
+[[[[[vfollow me] Asks your pets (and companions) to follow you. They do have a mind of
+ their own, and may not be able to travel as fast as you can.
+[[[[[vseek and destroy] Selecting this will cause your pets and companions to wander
+ further from you, looking for enemies to kill.
+[[[[[vallow/disallow open doors] Selecting this toggles whether your pets and
+ companions can open doors.
+[[[[[vallow/disallow pickup items] Selecting this toggles whether your pets and
+ companions can pick up items. Disallowing it will
+ cause the monster to drop any items he is carrying on
+ the floor.
+[[[[[vgive target to a friend] Selecting this will cause one of your pets or
+ companions to attack your current target.
+[[[[[vgive target to all friends] Causes all pets or companions to attack your
+ current target.
+[[[[[vfriend forget target] All your friends will follow their normal attack
+ patterns, neglecting any targets you have given them.
+
+~~~~~14|Objectives
+#####R=== Game Objectives ===
+
+In ToME you will be required to complete a certain number of quests. Your
+first quest is to discover the true nature of the evil lurking in the Tower of
+Dol Goldur near Mirkwood.
+
+Each quest may lead on to others, and most quests can be postponed until when
+you feel ready to tackle them. Simply explore other dungeons until you feel you
+have gained enough experience to tackle your next task. Other quests are
+optional and can be used for further experience.
+
+Once you have finished your final quest, when you are ready to retire, simply
+"commit suicide" ("^Q") to have your character entered into the high score list
+as a winner. Note that until you retire, you can still be killed, so you may
+want to retire before wandering into a hoard of nasties....
+
+You may also like to make a character sheet of your winning character (by
+going through the "C"haracter screen and choosing "f"ile), and post in the
+rec.games.roguelike.angband newsgroup with a text copy of the dump pasted
+into the post. Include details about anything major that happened to your
+character - did they find a ring of speed (+10) on dungeon level 2? Or had
+they reached dungeon level 60 before finding their first artifact? Did you
+have a really scary moment that stands out from the rest of the game? And how
+*did* you actually win the game, anyway?
+
+~~~~~15|Dying
+~~~~~17|Loading old characters
+#####R=== Upon Death and Dying ===
+
+If your character falls below 0 hit points, he has died and cannot be
+restored (for most classes, anyway). A tombstone showing information about
+your character will be displayed. You are also permitted to get a record of
+your character, and all your equipment (identified) either on the screen or
+in a file.
+
+Your character will leave behind a reduced save file, which contains only
+the monster memory and your option choices. It may be restored, in which
+case the new character is generated exactly as if the file was not there,
+but the new player will find his monster memory containing all the experience
+of past incarnations.
+
+In this way, death in ToME is permanent. You cannot simply 'reload at the last
+save' as in most other contemporary games. Death is permanent, just as it is
+in real life.
+
diff --git a/lib/help/dunspoil.txt b/lib/help/dunspoil.txt
new file mode 100644
index 00000000..2da6d6b7
--- /dev/null
+++ b/lib/help/dunspoil.txt
@@ -0,0 +1,173 @@
+|||||oy
+~~~~~01|Dungeons|Spoilers
+~~~~~02|Spoilers|Dungeons
+#####R=== ToME's DUNGEONS ===
+
+There are numerous dungeons within Middle-earth, but not all
+of them are required to be ventured into. The original Angband
+dungeon has been split into 4 parts, each found near or at a
+different town. Travel between towns is usually easiest by
+using the wilderness overview map ("<" from the town level),
+but remember to take lots of food with you!
+
+#####GThe Basic Dungeons
+#####G------------------
+
+#####G1. Barrow-Downs
+Found near the outskirts of Bree (the starting town), this
+dungeon is where you should begin your adventure. It
+contains the main Dungeon levels 1 (50') to 10 (500'),
+after which it becomes necessary for the character to seek
+out a new dungeon in order to be able to descend further in
+their quest to kill Morgoth.
+
+#####G2. Mirkwood
+The Mirkwood forest contains levels 11 (550') to 33 (1650'),
+and can be found to the north-east of Lothlorien.
+
+#####G3. Mordor
+The Land of Mordor is a hot, cavernous region, containing
+levels 34 (1700') to 66 (3300') of the dungeon. Located to
+the East of Minas Anor, it is a place filled with danger,
+and only a wary adventurer will make it through to the other
+end of this dungeon.
+
+#####G4. Angband
+Only the most successful of adventurers usually make it this
+far. The Dungeon Angband contains both Sauron and Morgoth,
+who will gladly wipe out any who dare to oppose them! This
+dungeon covers levels 67 (3350') to 127 (6350'), but Sauron
+waits for you at level 99, and Morgoth at level 100. This
+dungeon can be found near Gondolin.
+
+
+#####GAdditional ToME Dungeons
+#####G------------------------
+
+In addition to the basic 4 dungeons, there are numerous other
+dungeons scattered around the world for an adventurer to explore
+while preparing for the final fight. Many of the dungeons have a
+guardian at the bottom of them, and a few have unique levels
+somewhere within them (like an orc town) with guaranteed artifacts
+and nasties within them. Other than the unique levels, all of the
+dungeons can be exited and re-entered at your leisure.
+
+Be warned though that some dungeons are partly toxic to the
+adventurer, and will damage you BY THEMSELVES just by you walking
+within them!
+
+#####GOrc Cave
+A dark tunnel leading to an Orc Cave, guarded at its base by Azog,
+King of the Uruk-Hai. Also somewhere within this dungeon is the
+hidden special level called Deathwatch.
+Equivalent to dungeon levels 10 to 22.
+
+#####GThe Old Forest
+A Forest is a haven for many animals, both of the good variety, and
+the bad. This forest is no exception. In the past, unsuccessful
+attempts have been made to cultivate this land, and it is possible
+that some of the towns may remain. It is also rumoured that Old Man
+Willow has made his home here, and is the source of the Forest's
+resistance to cultivation.
+Equivalent to dungeon levels 13 to 25.
+
+#####GHelcaraxe
+The Grinding Ice of Helcaraxe is a bitterly cold series of caverns,
+and guarded by the White Balrog.
+Equivalent to dungeon levels 20 to 40.
+
+#####GThe Sandworm lair
+A deep sandhole where most worms originated, it is guarded at its
+base by the Sandworm Queen.
+Equivalent to dungeon levels 22 to 30.
+
+#####GThe Heart of the Earth
+A dark passage leading into the heart of the world, the Heart of
+the Earth is the source of all the earth's changes. Some claim it
+to be the home of Golgarach, the Living Rock, who assists in the
+creation of new, ever-changing landforms.
+Equivalent to dungeon levels 25 to 36.
+
+#####GMaze
+A strange Maze where it becomes very difficult to remember where you
+have been, it is guarded at the bottom by The Minotaur of the Labyrinth.
+Equivalent to dungeon levels 25 to 37.
+
+#####GCirith Ungol
+The dungeon Cirith Ungol is full of poisonous fumes rising from the
+ground, and the land here looks diseased. Shelob is said to lurk
+within these depths.
+Equivalent to dungeon levels 25 to 50.
+
+#####GThe Land Of Rhun
+The Land Of Rhun is located on a large plain, and has been taken over
+by Ulfang the Black, Morgoth's first Easterling follower.
+Equivalent to dungeon levels 26 to 40.
+
+#####GThe Mines of Moria
+A stone door leads down to the depths of the Moria. Once the home of
+the dwarves, the mines have been taken over by creeping evil things, and
+unlucky adventurers may well stumble upon their training grounds. In
+the depths lurks Durin's Bane, the Balrog of Moria, and return from
+these depths can be difficult....
+Equivalent to dungeon levels 30 to 50.
+
+#####GThe Small Water Cave
+A small water cave filled with salt water, which rusts and damages an
+adventurer's equipment. This cave is not very deep, and is guarded by
+The Watcher in the Water.
+Equivalent to dungeon levels 32 to 34.
+
+#####GSubmerged Ruins
+The lost land of Numenor lies submerged here. The salt water causes
+everything to rust. These ruins are guarded by Ar-Pharazon the
+Golden.
+Equivalent to dungeon levels 35 to 50.
+
+#####GIllusory Castle
+The Illusory Castle is a very strange and confusing place for an
+adventurer to visit, and is rumoured to contain many strange
+monsters. Deep within the castle can be found The Glass Golem.
+Equivalent to dungeon levels 35 to 52.
+
+#####GPaths of the Dead
+A dark underground graveyard, this place looks extremely dangerous.
+It is claimed that the dead have all arisen in these depths, and are
+lead by Feagwath, the Undead Sorcerer.
+Equivalent to dungeon levels 40 to 70.
+
+#####GThe Sacred Land Of Mountains
+The Sacred Land Of Mountains is located in a large mountain range,
+with the remains of many old towns. Considered a perfect place to hide
+for one who could fly, it is rumoured that Trone the rebel Thunderlord
+fled into the ruins here.
+Equivalent to dungeon levels 45 to 70.
+
+#####GThe Tower of Dol Guldur
+The tower of of Dol Guldur is the last known residence of one known only
+as "the Necromancer". It is rumoured to be filled with his conjurations,
+although none have made it out of the Tower alive to confirm or deny this
+rumour.
+Equivalent to dungeon levels 57 to 70.
+
+#####GErebor, the Lonely Mountain
+A big, dark and frightening tunnel leading to the depth of the Lonely
+Mountain, this large cave is the ancestral home of the Dragons.
+Glaurung, Father of the Dragons has long made his home here.
+Equivalent to dungeon levels 60 to 72.
+
+#####GMount Doom
+A steaming cave in the centre of Mount Doom, this place is *hot*.
+It is said that Sauron forged the One Ring here and that it's the only place
+where it could be destroyed.
+Equivalent to dungeon levels 85 to 99.
+
+#####GNether Realm
+The Nether Realm is accessible only through a magic portal. Also
+known as Hell, this land is lethal to any who are unprepared for the
+ravages of Nether, and is guarded by Tik'srvzllat.
+#####BEquivalent to dungeon levels 666 to 696!!!!!
+
+
+ Created by Dawnmist for PernAngband 5.x.x
+ Updated for ToME 2.1.x
diff --git a/lib/help/essences.txt b/lib/help/essences.txt
new file mode 100644
index 00000000..f329fa80
--- /dev/null
+++ b/lib/help/essences.txt
@@ -0,0 +1,219 @@
+|||||oy
+~~~~~01|Spoilers|Essences
+~~~~~02|Alchemist|Essence Spoiler
+#####REssence Spoiler for ToME 2.2.0
+#####R------------------------------
+
+Essences are the tools of the trade for Alchemists, and unfortunately are
+useless for any other class. Alchemists use essences to create magical
+items for them to use.
+
+They can be either found on the floor while exploring the dungeon, or
+extracted from other magical items the alchemist finds during his
+adventures.
+
+To create an artifact, the alchemist will first have to have learnt the
+Artifact Creation ability. This ability costs 70 skill points (yes, 70,
+it's a very powerful ability!). And you need an alchemy skill of at least
+40, plus INT and WIS at 35 (thats 18/170 in non-linear form).
+To create the artifact, the alchemist will have to sacrifice 10 hit points,
+and an amount of magic essence similar to his skill in alchemy. The
+alchemist then allows the artifact to gain experience, and when it has
+enough, uses that experience to add abilities to the artifact. The
+alchemist can allow the artifact to continue to gain experience, thus
+keeping open the option to add more abilities later. This requires a
+similar amount of magic essence, but does not require the sacrifice of
+more hit points.
+
+Note that the experience you gain is divided among the artifacts that you
+have as well as going to yourself, so you will gain levels more slowly when
+empowering artifacts. Also, the artifact only gets 60% of the experience.
+So killing a creature worth 20xp would gain 10 for you, and 6 for the
+artifact.
+
+You can also modify existing artifacts when you attain skill level 50. Also
+at skill level 50 you will gain the ability to make temporary artifacts,
+which don't require the complex empowerments that regular items require,
+but also vanish after awhile.
+
+You cannot give an artifact an ability unless you have *Identified* an
+artifact which has that ability.
+
+For every four levels gained in the alchemy skill, the alchemist learns
+about objects of level (skill level)/4, starting by learning about level 1
+objects at skill level 0. (actually 1, but who's counting?)
+
+At skill level 5 you gain the ability to make ego items - but watch it!
+Your base failure rate will be 90%, and won't be 0% until you reach skill
+level 50. Adding gold will increase the chances of success in direct
+proportion to the value of the item you are trying to create. Note that
+this results in automatic success when the item you are trying to create
+happens to pick up a curse in the process.
+
+At skill level 5 you also gain knowledge of some basic ego item recipes.
+These are: Acidic, Shocking, Fiery, Frozen, Venomous, and Chaotic weapons,
+Resist Fire armour, and light sources of Fearlessness.
+
+At skill level 10 you will gain knowledge of digging ego items, if you have
+selected the option "always generate very unusual rooms" (ironman_rooms).
+
+At skill level 15 you can create ego wands, staves, rings, etc.
+
+At skill level 25 you gain the ability to empower double ego items.
+
+At skill level 50 you gain the ability to create temporary artifacts, which
+don't require any exotic ingredients beyond a single corpse of any type.
+
+Between skill levels 25 and 50, you will steadily gain the ability to set
+more and more flags.
+
+To finalise an artifact, you "P"ower it, and select the powers you want.
+Powers are divided into the following six categories:
+*****essences.txt*03[Stats, Sustains, Luck, Speed, Vision, etc.]
+*****essences.txt*04[Misc. (Auras, Light, See Invisibility, etc.)]
+*****essences.txt*05[Weapon Brands]
+*****essences.txt*06[Resistances and Immunities]
+*****essences.txt*07[ESP and Curses]
+*****essences.txt*08[Artifact Activations]
+
+~~~~~03
+#####GStats, Sustains, Luck, Speed, Vision, etc.
+lvl xp Power
+40 5000 Add to Strength Ring of Strength
+43 5000 Add to Intelligence Ring of Intelligence
+46 5000 Add to Wisdom Amulet of Wisdom
+46 5000 Add to Dexterity Ring of Dexterity
+42 5000 Add to Constitution Ring of Constitution
+30 5000 Add to Charisma Amulet of Adornment
+32 1000 Sustain Strength Ring of Sustain Strength
+34 1000 Sustain Intelligence Ring of Sustain Intelligence
+28 1000 Sustain Wisdom Ring of Sustain Wisdom
+36 1000 Sustain Dexterity Ring of Sustain Dexterity
+36 1000 Sustain Constitution Ring of Sustain Constitution
+25 1000 Sustain Charisma Ring of Sustain Charisma
+40 50000 Speed Ring of Speed
+38 150000 Extra Attacks Ring of Extra Attacks
+32 5000 Stealthy Left Insole from a Used Soft Boot
+29 2000 Adds to Searching Filthy Rag
+ 6 1000 Helps Infravision Brass Lantern
+30 1000 Lucky Rabbit's Left Forefoot
+25 30000 Aids in digging Pick
+40 50000 Multiplies Life Troll's Heart
+
+~~~~~04
+#####GMisc. (Auras, Light, See Invisibility, etc.)
+lvl xp Power
+20 15000 Invisibility Potion of Invisibility
+20 4000 See Invisible Potion of Invisibility
+20 30000 Free Action Iron Spike
+38 90000 Reflection Large Metal Shield
+20 30000 Aura of Fire Lungs from an Ancient Red Dragon
+25 30000 Aura of Lightning Lungs from an Ancient Blue Dragon
+ 8 1000 Light Everburning Torch
+20 10000 Bright Light Dwarven Lantern
+40 100000 Sunlight Feanorian Lamp
+40 200000 Flight Suit of Dragon Armour (any colour)
+50 10000000 Automatically IDs Morgoth's Testicles
+29 2000 Anti-Teleportation Teleport Inhibiting Amulet
+34 2000 Anti-Magic Magic Inhibiting Amulet
+50 100000 Wraith Form Potion of Invulnerability
+15 1000 Levitation Potion of Berserk Strength
+20 10000 Slow Digestion Lembas Wafer
+32 20000 Regenerate Mushroom of Unhealth
+12 20000 Teleport Mushroom of Confusion
+
+~~~~~05
+#####GWeapon Brands
+lvl xp Power
+30 20000 Extra Critical Hits Whip
+30 30000 Wounds Monsters Blade of Chaos
+26 6000 Vampiric Rod Tip of Drain Life
+16 2000 Slay Animal Dead Animal's Body
+25 2000 Slay Evil Evil Dead Thing's Remains
+30 2000 Slay Undead Remains of Undead Monster
+40 1500 Slay Demon Demon's Corpse
+10 700 Slay Orc Dead Orc
+16 700 Slay Troll Dead Troll
+25 900 Slay Giant Dead Giant
+33 2000 Slay Dragon Dead Dragon (any size will do)
+41 5000 *Slay* Dragon Mature Multi-Hued Dragon's Remains
+41 90000 *Slay* Undead Dead Summoner of Greater Undead
+41 90000 *Slay* Demon Lesser Balrog's Corpse
+36 20000 Vorpal
+40 90000 Earthquakes
+ 3 2000 Poison Brand
+12 2000 Acid Brand
+10 2000 Lightning Brand
+ 6 2000 Fire Brand
+ 8 2000 Frost Brand
+30 3000 Extra Might (Bows Only)
+35 3000 Extra Shots (Bows Only)
+
+~~~~~06
+#####GResistances and Immunities
+lvl xp Power
+49 500000 Immune to Acid Ancient Black Dragon's Foreskin
+50 500000 Immune to Lightning Ancient Blue Dragon's Foreskin
+49 500000 Immune to Fire Ancient Red Dragon's Foreskin
+50 500000 Immune to Cold Ancient White Dragon's Foreskin
+30 30000 Hold Life Amulet of the Magi
+12 10000 Resist Acid Ring of Acid
+15 10000 Resist Lightning Ring of Lightning
+13 10000 Resist Fire Potion of Resist Heat
+14 10000 Resist Cold Potion of Resist Cold
+25 30000 Resist Poison Potion of Cure Poison
+26 10000 Resist Fear Ring of Fear Resistance
+31 60000 Resist Light Ring of Light and Darkness Resistance
+31 60000 Resist Darkness Ring of Light and Darkness Resistance
+30 30000 Resist Blindness Ring of Blindness Resistance
+30 30000 Resist Confusion Ring of Confusion Resistance
+30 60000 Resist Sound Ring of Sound Resistance
+30 60000 Resist Shards Ring of Shard Resistance
+30 60000 Resist Nether Ring of Nether Resistance
+30 60000 Resist Nexus Ring of Nexus Resistance
+30 60000 Resist Chaos Ring of Chaos Resistance
+30 60000 Resist Disenchantment Ring of Disenchantment Resistance
+
+~~~~~07
+#####GESP and Curses
+lvl xp Power
+50 -100000 Temporary Item Corpse, any corpse
+10 -2000 Self-Cursing Filthy Rag
+45 -10000 Causes the Black Breath Sprig of Athelas
+40 -5000 Ancient Curse Scroll of *Remove Curse*
+40 -5000 Drains your Experience
+30 -5000 Aggravates Monsters
+30 -500 Curse Scroll of Remove Curse
+50 -5000 Permanently Cursed
+35 -2000 Can't be Dropped
+45 -5000 Drains your Hit Points
+20 -50000 Wielder Can't Move
+40 20000 Telepathy Formerly Floating Eye
+25 3000 Sense Orcs
+25 3000 Sense Trolls
+25 5000 Sense Dragons
+25 5000 Sense Giants
+25 5000 Sense Demons
+25 5000 Sense Undead
+25 5000 Sense Evil
+25 5000 Sense Animals
+25 5000 Sense Thunderlords
+25 5000 Sense Good
+25 5000 Sense Nonliving
+25 5000 Sense Unique Monsters
+25 2000 Sense Spiders
+
+~~~~~08
+#####GArtifact Activations
+lvl xp Power
+40 40000 Sunlight Brass Lantern
+20 4000 Magic Missile (1)
+30 300000 Magic Missile (2)
+40 400000 Ball of Missiles
+30 300000 Bolt of Lightning
+30 300000 Ball of Lightning
+35 350000 Ball of Lightning(2)
+40 400000 Ball of Lightning(3)
+40 400000 Ball of Lightning(4)
+45 450000 Breath Lightning
+50 40000 Fire a Rocket
diff --git a/lib/help/experien.hlp b/lib/help/experien.hlp
new file mode 100644
index 00000000..5e0bb9eb
--- /dev/null
+++ b/lib/help/experien.hlp
@@ -0,0 +1,28 @@
+|||||oy
+~~~~~01|Experience
+#####RGaining experience
+#####R==============================================
+
+The principle way for your character to gain experience (XP) is to kill
+monsters. There are a few other ways too, like lockpicking, but these only
+give small rewards.
+
+Once your character has gained enough experience to go up a level, then she
+will be given skill points to spend as you see fit. These points can be
+spent on either
+
+ *****/askills.txt*0[(a) Skills] which improve more for each point you put into them,
+or
+ *****/bability.txt*0[(b) Abilities] which are one-off purchases and grant your character new,
+ non-improvable powers
+
+Certain monsters can "drain" your experience, and thus your level. Although you
+do not lose the skill points you've already gained, you also will not gain
+further points until you reach a character level you have not already
+achieved. Luckily, you can restore drained experience through magical means,
+or by simply regaining the experience all over again.
+
+ *****/ynewbie.hlp*0[(y) New player help menu]
+ *****/zhelp.hlp*0[(z) Main menu]
+
+
diff --git a/lib/help/explore.hlp b/lib/help/explore.hlp
new file mode 100644
index 00000000..0c302ab8
--- /dev/null
+++ b/lib/help/explore.hlp
@@ -0,0 +1,16 @@
+|||||oy
+~~~~~01|Help|Exploring menu
+~~~~~02|Exploring menu
+#####RWelcome to the ToME Help System.
+#####R==============================================
+
+Please choose one of the following online help files:
+
+ *****/acommand.txt*0[(a) Available commands] How to control your character
+ *****/battack.txt*0[(b) Attacking monsters] How to attack, elemental attacks and resistances etc
+ *****/cdungeon.txt*0[(c) Exploring the dungeons] Symbols on your map, mining, pets, objects + more
+
+ *****/ynewbie.hlp*0[(y) New player help menu]
+ *****/zhelp.hlp*0[(z) Main menu]
+
+ \ No newline at end of file
diff --git a/lib/help/fatespoi.txt b/lib/help/fatespoi.txt
new file mode 100644
index 00000000..2815129e
--- /dev/null
+++ b/lib/help/fatespoi.txt
@@ -0,0 +1,28 @@
+|||||oy
+~~~~~01|Spoilers|Fates
+~~~~~02|Fates (spoiler)
+#####R Fate Spoiler
+#####R Accurate for PernAngband 5.x.x
+#####R by Dustin Ragan
+
+Numerous spirits inhabit the land of Arda, from the murderous barrow wights to
+the enigmatic Tom Bombadil. These spirits usually are bound to a specific
+geographic region, but there are exceptions to this rule. Sometimes one of
+these spirits will take interest in an adventurer. This can either be very,
+very good or very, very bad. These spirits will "rig" reality to ensure that
+something happens--or doesn't happen.
+
+In order to attract the attention of a spirit, the adventurer must be somewhat
+experienced, having attained at least the 11th level of experience. Every 10
+game turns, which corresponds to 1 normal speed player turn, there is a
+1 in 50,000 chance of gaining a fate. When this fate is chosen, there is a
+7/18 chance of being fated to find a specific mundane item on a specific
+dungeon level. A more belligerent spirit will, 7/18 of the time, summon a
+malicious monster to do battle on a specific dungeon level. There is a 1/9
+chance to be destined to find an artifact on a given level. There is also
+a 1/18 chance that you will meet your demise on a given level. Finally, there
+is a 1/18 chance of becoming invulnerable to attacks from mortals.
+
+Whenever a level is being chosen for a fate, it is always chosen within 20
+levels of your current recall depth. Items, monsters, and artifacts are all
+generated up to 10 levels out of depth for your current recall depth.
diff --git a/lib/help/foot.aux b/lib/help/foot.aux
new file mode 100644
index 00000000..47328799
--- /dev/null
+++ b/lib/help/foot.aux
@@ -0,0 +1,4 @@
+</TT></PRE>
+</FONT>
+</body>
+</html>
diff --git a/lib/help/g_eru.txt b/lib/help/g_eru.txt
new file mode 100644
index 00000000..113875b3
--- /dev/null
+++ b/lib/help/g_eru.txt
@@ -0,0 +1,65 @@
+|||||oy
+~~~~~01|Gods|Eru
+~~~~~02|Eru
+#####R === Eru Iluvatar ===
+
+Eru Iluvatar is the father of the Valar. His most faithful followers are those
+of the class *****c_pr_eru.txt*0[Priest(Eru)].
+
+#####GThe benefits of Worshipping Eru Iluvatar
+1. As you increase your piety, Eru will grant you a boost to your wisdom.
+ Eventually he will also start increasing your ability to handle magical
+ power, resulting in a boost to your spellpoints.
+2. If you are praying to him at the time, there is a chance that he will
+ deflect some blows from evil monsters (that increases with your level of
+ piety).
+3. If you are praying to him at the time, there is a chance that he will
+ resurrect you from the dead (provided you are very pious!).
+4. Your piety automatically increases over time if you are:
+ a) Not praying, and
+ b) Actively doing something (i.e. not resting or in the Wilderness map).
+
+#####GThe disadvantages of Worshipping Eru Iluvatar
+1. He doesn't like it if you destroy blessed weapons.
+2. You can only wield blunt or blessed weapons without penalty.
+3. He doesn't like it if you kill monsters that are aligned with good.
+4. He will completely abandon you if you wear The One Ring.
+~~~~~~03|Eru|Prayers
+#####GEru Iluvatar's Magic
+Worshipping Eru Iluvatar gives the adventurer access to a set of special
+spells that come directly from the hands of Eru. These spells use your piety
+to cast rather than your spellpoints, and the level of spells that Eru will
+permit you to use is determined by your Prayer skill - how skillful you are
+in asking for his help without offending him!
+
+There is a special book called the "Holy Tome of Eru Iluvatar" which
+contains instructions for the procedure for each of the prayers Eru will
+grant. There are four prayers all told, which are:
+1. [[[[[BSee the Music] (Level 1)
+ Allows you to 'see' the Great Music from which the world originates,
+ allowing you to see unseen things, and can be cast while blind.
+ At spell level 10 it allows you to see your surroundings.
+ At spell level 20 it allows you to cure blindness.
+ At spell level 30 it allows you to fully see all the level.
+2. [[[[[BListen to the Music] (Level 7)
+ Allows you to listen to the Great Music from which the world originates,
+ allowing you to understand the meaning of things.
+ At spell level 14 it allows you to identify all your pack.
+ At spell level 30 it allows you to identify all items on the level.
+3. [[[[[BKnow the Music] (Level 30)
+ Allows you to understand the Great Music from which the world originates,
+ allowing you to know the full abilities of things.
+ At spell level 10 it allows you to *identify* all your pack.
+4. [[[[[BLay of Protection] (Level 35)
+ Creates a circle of safety around you.
+
+Each of these spells can be increased in level both by improving your Prayer
+skill, and by improving your Spell-power skill.
+
+In addition to his specific magic, Eru will also assist with your ability to
+use some magic from the "standard" schools, in relation to how skilled you
+are at Prayer. These schools are as follows:
+ *****m_mana.txt*0[Mana School] at 1/2 the Prayer skill level.
+ *****m_divin.txt*0[Divination School] at 2/3 the Prayer skill level.
+ *****m_mind.txt*0[Mind School] at 1/3 the Prayer skill level.
+The spells from these schools are all cast using your normal spellpoints.
diff --git a/lib/help/g_manwe.txt b/lib/help/g_manwe.txt
new file mode 100644
index 00000000..4bbc85fd
--- /dev/null
+++ b/lib/help/g_manwe.txt
@@ -0,0 +1,62 @@
+|||||oy
+~~~~~01|Gods|Manwe
+~~~~~02|Manwe
+#####R === Manwe Sulimo ===
+
+Manwe is the strongest of the Valar, next to Morgoth. His most faithful
+followers are those of the class *****c_pr_man.txt*0[Priest(Manwe)].
+
+#####GThe benefits of Worshipping Manwe Sulimo
+1. As you increase your piety, Manwe will grant boosts to your speed (up to
+ a maximum boost of +7 speed).
+2. If you are praying, Manwe likes it when you kill monsters that are aligned
+ with evil.
+3. As your piety increases, Manwe will grant you the following abilities (in
+ order):
+ a) Levitation
+ b) Free Action (while praying)
+ c) Flying (while praying)
+4. Manwe likes elves.
+
+#####GThe disadvantages of Worshipping Manwe Sulimo
+1. Your piety slowly decreases with time, whether you are praying or not.
+2. He doesn't like it if you kill monsters that are aligned with good.
+3. He will completely abandon you if you wear The One Ring.
+~~~~~03|Manwe|Prayers
+#####GManwe Sulimo's Magic
+Worshipping Manwe Sulimo gives the adventurer access to a set of special
+spells that come directly from the hands of Manwe. These spells use your piety
+to cast rather than your spellpoints, and the level of spells that Manwe will
+permit you to use is determined by your Prayer skill - how skillful you are
+in asking for his help without offending him!
+
+There is a special book called the "Holy Tome of Manwe Sulimo" which
+contains instructions for the procedure for each of the prayers Manwe will
+grant. There are four prayers all told, which are:
+1. [[[[[BManwe's Blessing] (Level 1)
+ Manwe's Blessing removes your fears, blesses you and surrounds you with holy
+ light.
+ At spell level 10 it also grants heroism.
+ At spell level 20 it also grants super heroism.
+ At spell level 30 it also grants holy luck and life protection.
+2. [[[[[BWind Shield] (Level 10)
+ It surrounds you with a shield of wind that deflects blows from evil
+ monsters.
+ At level 10 it increases your armour rating.
+ At level 20 it retaliates against monsters that melee you.
+3. [[[[[BManwe's Call] (Level 20)
+ Manwe's Call summons a Great Eagle to help you battle the forces of
+ Morgoth.
+4. [[[[[BAvatar] (Level 35)
+ It turns you into a full grown Maia.
+
+Each of these spells can be increased in level both by improving your Prayer
+skill, and by improving your Spell-power skill.
+
+In addition to his specific magic, Manwe will also assist with your ability to
+use some magic from the "standard" schools, in relation to how skilled you
+are at Prayer. These schools are as follows:
+ *****m_air.txt*0[Air School] at 2/3 the Prayer skill level.
+ *****m_convey.txt*0[Conveyance School] at 1/2 the Prayer skill level.
+ *****m_meta.txt*0[Meta School] at 1/3 the Prayer skill level.
+The spells from these schools are all cast using your normal spellpoints.
diff --git a/lib/help/g_melkor.txt b/lib/help/g_melkor.txt
new file mode 100644
index 00000000..3f38aa32
--- /dev/null
+++ b/lib/help/g_melkor.txt
@@ -0,0 +1,64 @@
+|||||oy
+~~~~~01|Melkor
+~~~~~02|Gods|Melkor
+#####R === Melkor Bauglir ===
+
+Melkor Bauglir is Morgoth, the Dark Enemy. He once was the most powerful
+of the Valar. His most faithful followers are those of the class *****c_pr_drk.txt*0[Dark Priest].
+
+#####GThe benefits of Worshipping Melkor Bauglir
+ 1. As you increase your piety, Melkor will grant boosts to your strength,
+ constitution and charisma and will decrease intelligence and wisdom.
+ 2. As a follower of Melkor you are resistant to fire.
+ 3. If you are praying, Melkor may make you invisible and immune to fire.
+ 4. If you are praying, Melkor may cast Curse on your foes when you melee them.
+ 5. If you are praying, Melkor likes it when you kill monsters.
+ 6. If you are praying, Melkor *likes* it when you kill monsters that are
+ aligned with good.
+ 7. Melkor likes the sacrifice of corpses and books at his altars.
+ 8. Melkor likes the permanent sacrifice of your own health at his altars.
+ 9. Melkor hates elves.
+10. Melkor grants access to the *****m_udun.txt*0[Udun] school of magic.
+11. Melkor can summon undead and demons to help you when your life goes down.
+
+#####GThe disadvantages of Worshipping Melkor Bauglir
+1. Your piety decreases with time.
+2. Your piety decreases with time even more if you are praying.
+3. He will completely abandon you if you destroy The One Ring.
+~~~~~03|Melkor|Prayers
+#####GMelkor Bauglir's Magic
+Worshipping Melkor Bauglir gives the adventurer access to a set of special
+spells that come directly from the hands of Melkor. These spells use your piety
+to cast rather than your spellpoints, and the level of spells that Melkor will
+permit you to use is determined by your Prayer skill - how skillful you are
+in asking for his help without offending him!
+
+There is a special book called the "Corrupted Tome of Melkor" which
+contains instructions for the procedure for each of the prayers Melkor will
+grant. There are three prayers all told, which are:
+1. [[[[[BCurse] (Level 1)
+ It curses a monster, reducing its melee power
+ At level 5 it can be auto-casted (with no piety cost) while fighting
+ if your piety is over 5000.
+ At level 15 it also reduces armor
+ At level 25 it also reduces speed
+ At level 35 it also reduces max life (but it is never fatal)
+2. [[[[[BCorpse Explosion] (Level 10)
+ It makes corpses in an area around you explode for a percent of their hit
+ points as damage
+3. [[[[[BMind Steal] (Level 20)
+ It allows your spirit to temporarily leave your own body, which will
+ be vulnerable, to control one of your enemies body
+
+Each of these spells can be increased in level both by improving your Prayer
+skill, and by improving your Spell-power skill.
+
+In addition to his specific magic, Melkor will also assist with your ability to
+use some magic from the "standard" schools, in relation to how skilled you
+are at Prayer. This school is as follows:
+ *****m_mind.txt*0[Mind School] at 1/3 the Prayer skill level.
+
+Melkor also grants all of his followers access to the *****m_udun.txt*0[Udun] school of magic,
+but only powerful mages are able to cast all of its spells.
+
+The spells from these schools are all cast using your normal spellpoints.
diff --git a/lib/help/g_tulkas.txt b/lib/help/g_tulkas.txt
new file mode 100644
index 00000000..2c05292a
--- /dev/null
+++ b/lib/help/g_tulkas.txt
@@ -0,0 +1,45 @@
+|||||oy
+~~~~~01|Tulkas
+~~~~~02|Gods|Tulkas
+#####R === Tulkas ===
+
+Another of the Valar. His most faithful followers are *****c_palad.txt*0[Paladins].
+
+#####GThe benefits of Worshipping Tulkas
+1. As you increase your piety, Tulkas will grant boosts to your constitution
+ and your strength (up to a maximum of +3 each).
+2. He likes it when you kill monsters that are aligned with evil.
+3. He loves it when you kill evil monsters while praying.
+4. He *adores* it when you kill demons while praying.
+5. If you are praying, Tulkas may increase the damage you do in melee combat.
+
+#####GThe disadvantages of Worshipping Tulkas
+1. When you are praying, your piety slowly decreases with time.
+2. He will completely abandon you if you wear The One Ring.
+~~~~~03|Tulkas|Prayers
+#####GTulkas's Magic
+Worshipping Tulkas gives the adventurer access to a set of special spells
+that come directly from the hands of Tulkas. These spells use your piety to
+cast rather than your spellpoints, and the level of spells that Tulkas will
+permit you to use is determined by your Prayer skill - how skillful you are
+in asking for his help without offending him!
+
+There is a special book called the "War Tome of Tulkas" which contains
+instructions for the procedure for each of the prayers Tulkas will grant.
+There are three prayers all told, which are:
+1. [[[[[BDivine Aim] (Level 1)
+ It makes you more accurate in combat.
+ At spell level 20 all your blows are critical hits.
+2. [[[[[BWhirlwind] (Level 10)
+ It allows you to spin around and hit all monsters nearby.
+3. [[[[[BWave of Power] (Level 20)
+ It allows you to project a number of melee blows across a distance.
+
+Each of these spells can be increased in level both by improving your Prayer
+skill, and by improving your Spell-power skill.
+
+In addition to his specific magic, Tulkas will also assist with your ability to
+use some magic from one of the "standard" schools, in relation to how skilled
+you are at Prayer. This school is as follows:
+ *****m_earth.txt*0[Earth School] at 4/5 the Prayer skill level.
+The spells from this school are all cast using your normal spellpoints.
diff --git a/lib/help/g_yavann.txt b/lib/help/g_yavann.txt
new file mode 100644
index 00000000..6e6937ca
--- /dev/null
+++ b/lib/help/g_yavann.txt
@@ -0,0 +1,62 @@
+|||||oy
+~~~~~01|Yavanna
+~~~~~02|Gods|Yavanna
+#####R === Yavanna Kementari===
+
+Yavanna, the Giver of Fruits, created all plants and animals and awakened the
+Ents to protect the forests of Arda. Kementari, Queen of the Earth, is her
+surname. Her most faithful followers are the *****c_druid.txt*0[Druids].
+
+#####GThe benefits of Worshipping Yavanna
+1. As you increase your piety, Yavanna will grant you the ability to pass
+ trees while praying.
+2. She makes you regenerate faster while praying on grass.
+3. She likes it when you kill nonliving creatures, undead or demons.
+4. She likes it if you charm animals (except evil ones).
+5. Yavanna likes Ents.
+
+#####GThe disadvantages of Worshipping Yavanna
+1. Your piety slowly decreases with time, whether you are praying or not.
+2. She doesn't like it if you kill monsters while praying.
+3. She hates it if you kill animals while praying.
+4. She hates it if you hurt your animal pets.
+5. She hates it if you burn or destroy trees with magic or allow monsters to
+ do so.
+6. She will completely abandon you if you wear The One Ring.
+~~~~~03|Yavanna|Prayers
+#####GYavanna's Magic
+Worshipping Yavanna gives the adventurer access to a set of special spells
+that come directly from the hands of Yavanna. These spells use your piety to
+cast rather than your spellpoints, and the level of spells that Yavanna will
+permit you to use is determined by your Prayer skill - how skillful you are
+in asking for her help without offending her!
+
+There is a special book called the "Forest Tome of Yavanna" which contains
+instructions for the procedure for each of the prayers Yavanna will grant.
+There are five prayers all told, which are:
+1. [[[[[BCharm Animal] (Level 1)
+ Tries to tame animals in a zone around your target.
+2. [[[[[BGrow Grass] (Level 10)
+ Creates a floor of grass around you. While on grass and praying,
+ a worshipper of Yavanna will know a greater regeneration rate.
+3. [[[[[BTree Roots] (Level 15)
+ Creates roots deep in the floor from your feet, making you more stable and
+ able to do better attacks, but preventing any movement (even teleportation).
+ It also makes you recover from stunning almost immediately.
+4. [[[[[BWater Bite] (Level 20)
+ Imbues your melee weapon with a natural stream of water.
+ At level 25, it spreads over a 1 radius zone around your target.
+5. [[[[[BUproot] (Level 35)
+ Awakes a tree to help you battle the forces of Morgoth.
+
+Each of these spells can be increased in level both by improving your Prayer
+skill, and by improving your Spell-power skill.
+
+In addition to her specific magic, Yavanna will also assist with your ability
+to use some magic from the "standard" schools, in relation to how skilled you
+are at Prayer. These schools are as follows:
+ *****m_earth.txt*0[Earth School] at 1/2 the Prayer skill level.
+ *****m_nature.txt*0[Nature School] at 1/2 the Prayer skill level.
+ *****m_water.txt*0[Water School] at 1/2 the Prayer skill level.
+ *****m_tempo.txt*0[Temporal School] at 1/6 the Prayer skill level.
+The spells from these schools are all cast using your normal spellpoints.
diff --git a/lib/help/gambling.txt b/lib/help/gambling.txt
new file mode 100644
index 00000000..62352600
--- /dev/null
+++ b/lib/help/gambling.txt
@@ -0,0 +1,29 @@
+|||||oy
+~~~~~01|Gambling
+#####R=== Gambling Rules ===
+
+#####GBetween :
+ Three 12-sided dice rolled; 2 black, 1 red. The red
+ die must be between both black to win. If the red die
+ matches a black die, you lose. Pays 3 to 1
+#####GCraps:
+ Two 6-sided dice are rolled. On first roll, a 7 or 11
+ wins. A 2, 3 or 12 loses. Otherwise roll until the first
+ roll is matched (win) or a 7 is rolled (loss). Pays 2 to 1
+#####GWheel:
+ Pick a number from 0-9. If the number shows on wheel
+ after it stops spinning, you win. Pays 10 to 1
+
+#####GSlots:
+ Three dice rolled. Matches win gold.
+ Numbers are:
+ 1=Lemon, 2=Orange, 3=Sword, 4=Shield, 5=Plum, 6=Cherry
+ Payoffs are as follows:
+ Cherry Cherry Lemon 2-1 Cherry Cherry Orange 3-1
+ Cherry Cherry Sword 4-1 Cherry Cherry Shield 5-1
+ Cherry Cherry Plum 6-1
+ Lemon Lemon Lemon 4-1 Orange Orange Orange 16-1
+ Sword Sword Sword 6-1 Shield Shield Shield 25-1
+ Plum Plum Plum 9-1 Cherry Cherry Cherry 36-1
+
+
diff --git a/lib/help/general.txt b/lib/help/general.txt
new file mode 100644
index 00000000..17dc187c
--- /dev/null
+++ b/lib/help/general.txt
@@ -0,0 +1,39 @@
+|||||oy
+~~~~~01|Help
+#####R=== Using the Online Help ===
+
+This help system has been designed to be read whilst in-game. Printing it off
+will make it look clumsy and a little difficult to read.
+
+Any text in orange/yellow colour is likely to be a hyperlink, and pressing
+<enter> while the link is active (yellow) will take you to the relevant page.
+Navigate between viewable links by using the left-right keys. Some links also
+have bracketed letters included in the links. Pressing these letters on your
+keyboard will activate the link even if it is orange.
+
+The help files total over 850 kb, so there is a lot of information. They have
+been designed to be browsed, but you may be looking for specific information,
+in which case you should try looking at the alphabetical *****index.txt*0[index].
+
+Here are all the relevant keypresses for navigating the help system.
+
+#####GKey | Action
+-------------------------------------------------------------------
+Escape | Leave the Online Help
+Backspace | Return to previous Help File
+Space | Advance 1 page (screen)
+2, down arrow | Advance 1 line
+8, up arrow | Back up 1 line
+- | Back up 1 page (screen)
+6, right arrow | Advance 1 link
+4, left arrow | Back up 1 link
+Return | Activate the selected link
+# | Go to a specific line (defaults to line 0)
+% | Go to a specific help file (defaults to help.hlp)
+= | Highlight lines containing a string (e.g. "word")
+/ | Search for a string (e.g. "word")
+-------------------------------------------------------------------
+
+There are other sources for help playing ToME. Try http://forum.t-o-m-e.net
+and http://wiki.t-o-m-e.net . We also have an IRC channel #tome on the
+worldirc and freenode networks (they are linked) which is fairly low traffic.
diff --git a/lib/help/gods.txt b/lib/help/gods.txt
new file mode 100644
index 00000000..0d091204
--- /dev/null
+++ b/lib/help/gods.txt
@@ -0,0 +1,38 @@
+|||||oy
+~~~~~01|Gods
+#####RThe Guide to the Gods.
+
+#####G1. Introduction
+ Everybody likes to have a little helping hand now and then. What
+could be better than having a god on your side? But it's not quite that easy.
+The gods won't help just any mortal who calls for help. You have to give them
+a little something too.
+
+#####G2. How Do I Get in on This?
+ When you start a character, you get a choice of whether or not you
+wish to begin worshipping a God (unless you're something like a priest - they
+*must* start with a God to worship). If you do choose to be a follower of one
+of the Gods, you will gain certain abilities provided you do not do things
+that displease them. The measure of how happy your God is with you is your
+Piety - the Pt stat is your measure of Piety. Different actions will allow you
+to gain or lose Piety over time. You can change your mind about whom (if
+anyone) to worship during the game by finding an *****tome_faq.txt*04[altar] of that God.
+
+#####G3. What about spells?
+God-granted spells are also known as prayers, and are cast using Piety instead
+of Mana. God-spells can be increased in level by improving either your Prayer
+or Spell-power skill. The chance of successfully invoking a prayer depends
+on the Prayer skill and your wisdom.
+
+Each God also grants access to standard magical spell schools; which school(s)
+vary depending on the Gods' individual preferences. Spells from these schools
+are cast using Mana, not Piety; and success depends on your intelligence, not
+your wisdom.
+
+#####G4. So, Who Are These Gods?
+ In ToME, there are five Gods you may choose to worship, being:
+1. *****g_eru.txt*0[Eru Iluvatar] - the father of the Valar.
+2. *****g_manwe.txt*0[Manwe Sulimo] - the second strongest of the Valar (Morgoth was the strongest).
+3. *****g_yavann.txt*0[Yavanna Kementari] - the Earth Queen who created plants and animals.
+4. *****g_tulkas.txt*0[Tulkas] - another of the Valar, Tulkas values strength and courage.
+5. *****g_melkor.txt*0[Melkor Bauglir] - the Dark Enemy himself, once the most powerful of the Valar.
diff --git a/lib/help/head.aux b/lib/help/head.aux
new file mode 100644
index 00000000..92e979d3
--- /dev/null
+++ b/lib/help/head.aux
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+ <title>T.o.M.E. Documentation</title>
+ <meta name="description" content="ToME and TomeNET homepage. ToME is a roguelike dungeon exploration game, based on Angband.">
+ <meta name="keywords" content="angband, tome, tomenet, library, angband, official, roguelike">
+</head>
+<body bgcolor="#000000" text="#FFFFFF" link="#CC0000" alink="#FF9900" vlink="#FFCC66">
+<FONT text="#CCCCCC">
+<PRE><TT>
diff --git a/lib/help/help.hlp b/lib/help/help.hlp
new file mode 100644
index 00000000..7c4b431f
--- /dev/null
+++ b/lib/help/help.hlp
@@ -0,0 +1,33 @@
+|||||oy
+~~~~~01|Help|Main Menu
+~~~~~02|Main Menu
+#####RWelcome to the ToME Online Help System.
+#####R==============================================
+
+Please choose one of the following online help files:
+
+ *****/ageneral.txt*0[(a) About this help system]
+ *****/bnewbie.hlp*0[(b) Help for new players] Skills, magic, races, classes + more
+ *****/cadvanced.hlp*0[(c) Help for more experienced players] Options, Macros, Automatizer etc
+ *****/dtome_faq.txt*0[(d) ToME FAQ] Common questions
+
+ *****/sspoiler.hlp*0[(s) Spoiler menu] Dungeons, fates, luck, corruptions and stuff
+
+
+ *****/xindex.txt*0[(x) Alphabetical index] Trouble finding something? Try here
+
+#####GBasic keys:
+Space | Advance 1 page (screen)
+2, down arrow | Advance 1 line
+8, up arrow | Back up 1 line
+- | Back up 1 page (screen)
+6, right arrow | Advance 1 link
+4, left arrow | Back up 1 link
+Return | Activate the selected link
+Escape | Leave the Online Help
+Backspace, ? | Return to previous Help File
+# | Go to a specific line (defaults to line 0)
+% | Go to a specific help file (defaults to help.hlp)
+= | Highlight lines containing a string (e.g. "word")
+/ | Search for a string (e.g. "word")
+
diff --git a/lib/help/index.txt b/lib/help/index.txt
new file mode 100644
index 00000000..d306b688
--- /dev/null
+++ b/lib/help/index.txt
@@ -0,0 +1,592 @@
+|||||oy
+#####R /----------------------------------------\
+#####R < Help Index >
+#####R \----------------------------------------/
+
+This is the index of everything in the T.o.M.E. documentation.
+
+#####BHit a letter key to jump to the entries for that letter.
+
+Some entries in the index link to the same place as other entries. This is
+intentional, so that the information you want is easy to find.
+
+Don't forget you can browse the help from the *****help.hlp*02[Main menu].
+
+#####sSpotted a problem with the help files, or some content thats missing?
+#####sContact fearoffours@t-o-m-e.net .
+
+~~~~~65
+*****/Aindex.txt*65[A]
+ *****birth.txt*81[Abbreviations]
+ *****birth.txt*20[AC]
+ *****birth.txt*17[AU]
+ *****tome_faq.txt*25[FF]
+ *****birth.txt*21[HP]
+ *****birth.txt*29[Pt]
+ *****birth.txt*24[SN]
+ *****birth.txt*26[SP]
+ *****ability.txt*01[Abilities]
+ *****ability.txt*07[Ammo creation]
+ *****ability.txt*09[Artifact Creation]
+ *****ability.txt*05[Extra Max Blow 1]
+ *****ability.txt*06[Extra Max Blow 2]
+ *****ability.txt*10[Far reaching attack]
+ *****ability.txt*04[Perfect casting]
+ *****ability.txt*02[Spread blows]
+ *****ability.txt*08[Touch of death]
+ *****ability.txt*11[Trapping]
+ *****ability.txt*03[Tree walking]
+ *****ability.txt*12[Undead Form]
+ *****m_air.txt*02[Air Magic]
+ *****c_alchem.txt*01[Alchemist]
+ *****c_alchem.txt*03[Alchemy powers explained]
+ *****essences.txt*02[Essence Spoiler]
+ *****tome_faq.txt*03[Altars]
+ *****tome_faq.txt*37[Anti-magic Amulets and the Anti-magic shell]
+ *****c_unbel.txt*04[Antimagic]
+ *****c_archer.txt*01[Archer]
+ *****attack.txt*03[Armor]
+ *****birth.txt*19[Armor Class]
+ *****attack.txt*05[Resistances]
+ *****tome_faq.txt*18[Artifacts that activate but I cannot wear or wield]
+ *****c_assass.txt*01[Assassin]
+ *****attack.txt*01[Attacking Monsters]
+ *****attack.txt*06[Damage Effect type ]
+ *****attack.txt*04[Resistances]
+ *****birth.txt*48[Fighting ability]
+ *****birth.txt*50[Shooting]
+ *****automat.txt*03[Auto destroy]
+ *****automat.txt*02[Auto pick-up]
+ *****automat.txt*01[Automatizer]
+ *****defines.txt*82[Defines]
+ *****automat.txt*04[Autosquelch]
+ *****c_axemas.txt*01[Axemaster]
+~~~~~66
+*****/Bindex.txt*66[B]
+ *****rm_barb.txt*01[Barbarian]
+ *****c_bard.txt*01[Bard]
+ *****tome_faq.txt*36[Beginner strategy]
+ *****r_beorn.txt*01[Beorning]
+ *****birth.txt*84[Birth]
+ *****dungeon.txt*21[Buildings]
+~~~~~67
+*****/Cindex.txt*67[C]
+ *****birth.txt*11[Character]
+ *****birth.txt*46[Abilities]
+ *****birth.txt*10[Ability tables]
+ *****birth.txt*23[Armor Class]
+ *****birth.txt*02[Characteristics]
+ *****birth.txt*01[Creating a Character]
+ *****birth.txt*22[Hit Points]
+ *****birth.txt*28[Mana]
+ *****birth.txt*31[Piety]
+ *****birth.txt*08[Race and Class Combinations]
+ *****birth.txt*25[Sanity Points]
+ *****birth.txt*09[Stat Bonus Table]
+ *****birth.txt*13[Stats 1]
+ *****birth.txt*39[Stats 2]
+ *****tome_faq.txt*34[Character choice is too confusing]
+ *****birth.txt*45[Charisma]
+ *****birth.txt*05[Classes]
+ *****c_alchem.txt*02[Alchemist]
+ *****c_archer.txt*02[Archer]
+ *****c_assass.txt*02[Assassin]
+ *****c_axemas.txt*02[Axemaster]
+ *****c_bard.txt*02[Bard]
+ *****birth.txt*68[Combinations with Race]
+ *****c_pr_drk.txt*02[Dark Priest]
+ *****c_demono.txt*02[Demonologist]
+ *****c_druid.txt*02[Druid]
+ *****c_geoman.txt*02[Geomancer]
+ *****c_hafted.txt*02[Haftedmaster]
+ *****c_lorema.txt*02[Loremaster]
+ *****c_mage.txt*02[Mage]
+ *****c_mimic.txt*02[Mimic]
+ *****c_mindcr.txt*02[Mindcrafter]
+ *****c_monk.txt*02[Monk]
+ *****c_necro.txt*02[Necromancer]
+ *****c_palad.txt*02[Paladin]
+ *****c_polear.txt*02[Polearmmaster]
+ *****c_posses.txt*02[Possessor]
+ *****c_pr_eru.txt*02[Priest - Eru]
+ *****c_pr_man.txt*02[Priest - Manwe]
+ *****c_ranger.txt*02[Ranger]
+ *****c_rogue.txt*02[Rogue]
+ *****c_runecr.txt*02[Runecrafter]
+ *****c_sorcer.txt*02[Sorceror]
+ *****birth.txt*77[Stat Bonuses]
+ *****c_summon.txt*02[Summoners]
+ *****c_swordm.txt*02[Swordmasters]
+ *****c_symbia.txt*02[Symbiant]
+ *****c_thaum.txt*02[Thaumaturgy]
+ *****c_unbel.txt*02[Unbeliever]
+ *****c_warper.txt*02[Warper]
+ *****c_warrio.txt*02[Warrior]
+ *****rm_class.txt*01[Classical]
+ *****command.txt*99[Commands]
+ *****command.txt*116[Alteration commands]
+ *****command.txt*103[Command counts]
+ *****command.txt*111[Command descriptions ]
+ *****command.txt*129[Extras]
+ *****command.txt*123[Game status]
+ *****command.txt*128[Help]
+ *****command.txt*112[Inventory]
+ *****command.txt*121[Looking ]
+ *****command.txt*122[Messages]
+ *****command.txt*113[Movement]
+ *****command.txt*119[Object manipulation]
+ *****command.txt*100[Original keyset]
+ *****command.txt*126[Pref files]
+ *****command.txt*104[Repeating a command]
+ *****command.txt*114[Resting]
+ *****command.txt*101[Roguelike keyset]
+ *****command.txt*124[Saving and Exiting]
+ *****command.txt*115[Searching]
+ *****command.txt*102[Special keys]
+ *****command.txt*118[Spells and prayers]
+ *****command.txt*117[Terrain interaction]
+ *****command.txt*120[Throwing and missile weapons]
+ *****dungeon.txt*19[Companions]
+ *****birth.txt*43[Constitution]
+ *****m_convey.txt*02[Conveyance Magic]
+ *****corspoil.txt*01[Corruptions ]
+ *****c_archer.txt*03[Creating Ammo]
+ *****birth.txt*12[Creating a Character]
+ *****dungeon.txt*10[Cursed Objects]
+~~~~~68
+*****/Dindex.txt*68[D]
+ *****attack.txt*09[Damage Effects]
+ *****r_drkelf.txt*01[Dark Elf]
+ *****c_pr_drk.txt*01[Dark Priest]
+ *****tome_faq.txt*29[Dark grey things are difficult to see]
+ *****r_deathm.txt*01[DeathMolds]
+ *****spoil_faq.txt*19[Deathmolds on Mount Doom and other quest areas]
+ *****debug.txt*99[Debug]
+ *****debug.txt*100[Command List]
+ *****debug.txt*111[Command descriptions ]
+ *****debug.txt*112[General]
+ *****defines.txt*81[Defines]
+ *****defines.txt*84[Svals]
+ *****defines.txt*85[Tvals]
+ *****c_demono.txt*01[Demonologist]
+ *****m_demono.txt*02[Demonology Magic]
+ *****version.txt*01[Development history]
+ *****birth.txt*41[Dexterity]
+ *****birth.txt*56[Disarming traps]
+ *****birth.txt*37[Display]
+ *****m_divin.txt*02[Divination Magic]
+ *****c_druid.txt*01[Druid]
+ *****r_dunad.txt*01[Dunedain]
+ *****dungeon.txt*02[Dungeons]
+ *****dungeon.txt*12[Doors]
+ *****dungeon.txt*06[In the dungeon]
+ *****dungeon.txt*11[Mining]
+ *****dunspoil.txt*01[Spoilers]
+ *****r_dwarf.txt*01[Dwarf]
+ *****dungeon.txt*15[Dying]
+~~~~~69
+*****/Eindex.txt*69[E]
+ *****m_earth.txt*02[Earth Magic]
+ *****r_elf.txt*01[Elf]
+ *****r_ent.txt*01[Ent]
+ *****g_eru.txt*02[Eru]
+ *****g_eru.txt*03[Prayers]
+ *****c_pr_eru.txt*03[Priest - Eru]
+ *****tome_faq.txt*10[Essences]
+ *****experien.hlp*01[Experience]
+ *****explore.hlp*02[Exploring menu]
+~~~~~70
+*****/Findex.txt*70[F]
+ *****tome_faq.txt*02[FAQ - Spoiler free]
+ *****spoil_faq.txt*02[FAQ - contains spoilers]
+ *****tome_faq.txt*13[Prophets]
+ *****fatespoi.txt*02[Fates ]
+ *****m_fire.txt*02[Fire Magic]
+ *****inscrip.txt*01[Floor Inscriptions ]
+ *****tome_faq.txt*28[Floor tiles displaying incorrectly]
+ *****tome_faq.txt*05[Fountains]
+ *****tome_faq.txt*24[Fumblefingers quests]
+~~~~~71
+*****/Gindex.txt*71[G]
+ *****gambling.txt*01[Gambling]
+ *****tome_faq.txt*30[Game ]
+ *****c_geoman.txt*01[Geomancer]
+ *****m_geoman.txt*02[Geomancy spells]
+ *****r_gnome.txt*01[Gnome]
+ *****gods.txt*01[Gods]
+ *****tome_faq.txt*04[Altars]
+ *****g_eru.txt*01[Eru]
+ *****g_manwe.txt*01[Manwe]
+ *****g_melkor.txt*02[Melkor]
+ *****birth.txt*30[Piety]
+ *****spoil_faq.txt*23[Quest - Spoilers]
+ *****g_tulkas.txt*02[Tulkas]
+ *****g_yavann.txt*02[Yavanna]
+ *****birth.txt*16[Gold]
+~~~~~72
+*****/Hindex.txt*72[H]
+ *****c_hafted.txt*01[Haftedmaster]
+ *****r_hafelf.txt*01[Half-Elf]
+ *****r_hafogr.txt*01[Half-Ogre]
+ *****general.txt*01[Help]
+ *****explore.hlp*01[Exploring menu]
+ *****tome_faq.txt*01[FAQ - Spoiler free]
+ *****magic.hlp*02[Magic]
+ *****help.hlp*01[Main Menu]
+ *****newbie.hlp*01[New players]
+ *****spoil_faq.txt*01[Spoiled FAQ]
+ *****rm_herm.txt*01[Hermit]
+ *****r_hielf.txt*01[High-Elf]
+ *****r_hobbit.txt*01[Hobbit]
+ *****tome_faq.txt*12[Homes]
+ *****r_human.txt*01[Human]
+~~~~~73
+*****/Iindex.txt*73[I]
+ *****tome_faq.txt*35[I STILL keep dying]
+ *****tome_faq.txt*31[I keep dying]
+ *****dungeon.txt*04[Identifying features]
+ *****birth.txt*66[Infra-vision]
+ *****birth.txt*35[Intelligence]
+ *****birth.txt*85[Inventory - starting info]
+ *****tome_faq.txt*32[Invisible character]
+~~~~~75
+*****/Kindex.txt*75[K]
+ *****macrofaq.txt*44[Keymaps]
+ *****macrofaq.txt*46[Macro recorder]
+ *****r_kobold.txt*01[Kobold]
+~~~~~76
+*****/Lindex.txt*76[L]
+ *****dungeon.txt*23[Light]
+ *****dungeon.txt*17[Loading old characters]
+ *****c_lorema.txt*01[Loremaster]
+ *****rm_lsoul.txt*01[Lost Soul]
+ *****luckspoi.txt*01[Luck ]
+~~~~~77
+*****/Mindex.txt*77[M]
+ *****macrofaq.txt*43[Macros]
+ *****macrofaq.txt*45[Macro recorder]
+ *****c_mage.txt*01[Mage]
+ *****magic.txt*03[Magic]
+ *****m_air.txt*01[Air School]
+ *****m_convey.txt*01[Conveyance School]
+ *****m_demono.txt*01[Demonology School]
+ *****m_divin.txt*01[Divination School]
+ *****m_earth.txt*01[Earth School]
+ *****m_fire.txt*01[Fire School]
+ *****m_geoman.txt*01[Geomancy]
+ *****magic.hlp*01[Index]
+ *****birth.txt*27[Mana]
+ *****m_mana.txt*01[Mana School]
+ *****m_meta.txt*01[Meta School]
+ *****m_mimic.txt*01[Mimicry]
+ *****m_mind.txt*01[Mind School]
+ *****m_mindcr.txt*01[Mindcraft]
+ *****m_music.txt*01[Music]
+ *****m_nature.txt*01[Nature School]
+ *****m_necrom.txt*01[Necromancy]
+ *****magic.txt*01[Schools]
+ *****m_symbio.txt*01[Symbiosis]
+ *****m_tempo.txt*01[Temporal School]
+ *****m_thaum.txt*01[Thaumaturgy]
+ *****m_udun.txt*01[Udun School]
+ *****magic.txt*04[Wands and Staves]
+ *****m_water.txt*01[Water School]
+ *****birth.txt*58[Magical Devices]
+ *****r_maia.txt*01[Maia]
+ *****help.hlp*02[Main Menu]
+ *****m_mana.txt*02[Mana Magic]
+ *****g_manwe.txt*02[Manwe]
+ *****g_manwe.txt*03[Prayers]
+ *****c_pr_man.txt*03[Priest - Manwe]
+ *****tome_faq.txt*14[Mathilde]
+ *****g_melkor.txt*01[Melkor]
+ *****c_pr_drk.txt*03[Dark Priests]
+ *****g_melkor.txt*03[Prayers]
+ *****m_meta.txt*02[Meta Magic]
+ *****c_mimic.txt*01[Mimic]
+ *****m_mimic.txt*02[Mimicry powers]
+ *****m_mind.txt*02[Mind Magic]
+ *****c_mindcr.txt*01[Mindcrafter]
+ *****m_mindcr.txt*02[Mindcraft powers]
+ *****dungeon.txt*13[Mining]
+ *****birth.txt*18[Money]
+ *****c_monk.txt*01[Monk]
+ *****c_monk.txt*03[Monk attacks]
+ *****dungeon.txt*05[Monsters]
+ *****attack.txt*02[Attacking]
+ *****attack.txt*07[Monster Memory]
+ *****dungeon.txt*20[Pets]
+ *****tome_faq.txt*08[They are talking to me]
+ *****m_music.txt*02[Music]
+~~~~~78
+*****/Nindex.txt*78[N]
+ *****m_nature.txt*02[Nature Magic]
+ *****c_necro.txt*01[Necromancer]
+ *****m_necrom.txt*02[Necromancy Magic]
+~~~~~79
+*****/Oindex.txt*79[O]
+ *****dungeon.txt*14[Objectives]
+ *****dungeon.txt*08[Objects]
+ *****dungeon.txt*21[Colour of inventory slot letter]
+ *****dungeon.txt*09[Cursed Objects]
+ *****tome_faq.txt*33[Piles]
+ *****option.txt*05[Options]
+ *****option.txt*16[Automatizer]
+ *****option.txt*15[Autosave]
+ *****option.txt*13[Base Delay Factor]
+ *****option.txt*18[Cheating]
+ *****option.txt*09[Disturbance]
+ *****option.txt*19[Dump]
+ *****option.txt*11[Efficiency]
+ *****option.txt*10[Game-play]
+ *****option.txt*14[Hitpoint Warning]
+ *****option.txt*07[Ingame]
+ *****option.txt*08[Interface]
+ *****option.txt*06[Startup]
+ *****option.txt*12[ToME Options]
+ *****option.txt*17[Window Flags]
+ *****r_orc.txt*01[Orc]
+~~~~~80
+*****/Pindex.txt*80[P]
+ *****c_palad.txt*01[Paladin]
+ *****birth.txt*62[Perception]
+ *****dungeon.txt*18[Pets]
+ *****r_pettyd.txt*01[Petty Dwarf]
+ *****c_polear.txt*01[Polearmmaster]
+ *****c_posses.txt*01[Possessor]
+ *****c_posses.txt*03[Possessor powers ]
+ *****command.txt*105[Pref files]
+ *****command.txt*109[Colors]
+ *****command.txt*127[Commands]
+ *****command.txt*107[Keymaps]
+ *****command.txt*106[Macros]
+ *****command.txt*110[Options]
+ *****command.txt*108[Visuals]
+ *****c_pr_eru.txt*01[Priest - Eru]
+ *****c_pr_man.txt*01[Priest - Manwe]
+ *****c_priest.txt*01[Priests]
+~~~~~82
+*****/Rindex.txt*82[R]
+ *****birth.txt*04[Race Modifiers]
+ *****birth.txt*79[Ability table]
+ *****rm_barb.txt*02[Barbarian]
+ *****rm_class.txt*02[Classical]
+ *****rm_herm.txt*02[Hermit]
+ *****rm_lsoul.txt*02[Lost Soul]
+ *****rm_skel.txt*02[Skeleton]
+ *****rm_spec.txt*02[Spectre]
+ *****birth.txt*76[Stat Bonuses]
+ *****rm_vamp.txt*02[Vampire]
+ *****rm_zomb.txt*02[Zombie]
+ *****birth.txt*03[Races]
+ *****birth.txt*78[Ability table]
+ *****r_beorn.txt*02[Beorning]
+ *****birth.txt*67[Combinations with class]
+ *****r_drkelf.txt*02[Dark Elf]
+ *****r_deathm.txt*02[DeathMolds]
+ *****r_dunad.txt*02[Dunedain]
+ *****r_dwarf.txt*02[Dwarf]
+ *****r_elf.txt*02[Elf]
+ *****r_ent.txt*02[Ent]
+ *****r_gnome.txt*02[Gnome]
+ *****r_hafelf.txt*02[Half-Elf]
+ *****r_hafogr.txt*02[Half-Ogre]
+ *****r_hielf.txt*02[High-Elf]
+ *****r_hobbit.txt*02[Hobbit]
+ *****r_human.txt*02[Human]
+ *****r_kobold.txt*02[Kobold]
+ *****r_maia.txt*02[Maia]
+ *****r_orc.txt*02[Orc]
+ *****r_pettyd.txt*02[Petty Dwarf]
+ *****r_rohank.txt*02[RohanKnight]
+ *****birth.txt*75[Stat Bonuses]
+ *****r_thlord.txt*02[Thunderlord]
+ *****r_troll.txt*02[Troll]
+ *****r_wodelf.txt*02[Wood Elf]
+ *****r_yeek.txt*02[Yeek]
+ *****tome_faq.txt*26[Random quests are not working]
+ *****tome_faq.txt*36[Random quests strategy]
+ *****c_ranger.txt*01[Ranger]
+ *****c_rogue.txt*01[Rogue]
+ *****r_rohank.txt*01[RohanKnight]
+ *****c_runecr.txt*01[Runecrafter]
+ *****c_runecr.txt*03[Runecrafter powers]
+ *****tome_faq.txt*11[Runes]
+~~~~~83
+*****/Sindex.txt*83[S]
+ *****command.txt*125[Saving and Exiting]
+ *****birth.txt*52[Saving throw]
+ *****birth.txt*60[Searching]
+ *****birth.txt*63[Searching Ability]
+ *****birth.txt*61[Searching Frequency - Perception]
+ *****tome_faq.txt*09[Sentient weapons]
+ *****rm_skel.txt*01[Skeleton]
+ *****skills.txt*55[Skills]
+ *****skills.txt*27[Air]
+ *****m_air.txt*03[Air - Spell Info]
+ *****skills.txt*49[Alchemy]
+ *****c_alchem.txt*05[Alchemy - Alchemy powers]
+ *****skills.txt*50[Antimagic]
+ *****c_unbel.txt*05[Antimagic powers]
+ *****skills.txt*08[Archery]
+ *****skills.txt*05[Axe-mastery]
+ *****skills.txt*18[Backstab]
+ *****skills.txt*13[Barehand-combat]
+ *****skills.txt*61[Bearform-combat]
+ *****skills.txt*12[Boomerang-mastery]
+ *****skills.txt*58[Boulder-throwing]
+ *****skills.txt*10[Bow-mastery]
+ *****skills.txt*01[Combat]
+ *****skills.txt*30[Conveyance]
+ *****m_convey.txt*03[Conveyance - Spell Info]
+ *****skills.txt*44[Corpse-preservation]
+ *****skills.txt*04[Critical-Hits]
+ *****skills.txt*11[Crossbow-mastery]
+ *****skills.txt*52[Demonology]
+ *****m_demono.txt*03[Demonology - Spell Info]
+ *****skills.txt*16[Disarming]
+ *****skills.txt*31[Divination]
+ *****m_divin.txt*03[Divination - Spell Info]
+ *****skills.txt*20[Dodging]
+ *****skills.txt*28[Earth]
+ *****m_earth.txt*03[Earth - Spell Info]
+ *****skills.txt*25[Fire]
+ *****m_fire.txt*03[Fire - Spell Info]
+ *****skills.txt*60[Geomancy]
+ *****m_geoman.txt*03[Geomancy - Spell Info]
+ *****skills.txt*06[Hafted-mastery]
+ *****skills.txt*57[List of skills]
+ *****skills.txt*21[Magic]
+ *****skills.txt*54[Magic-device]
+ *****skills.txt*24[Mana]
+ *****m_mana.txt*03[Mana - Spell Info]
+ *****skills.txt*29[Meta]
+ *****m_meta.txt*03[Meta - Spell Info]
+ *****skills.txt*47[Mimicry]
+ *****m_mimic.txt*03[Mimicry - mimicry powers]
+ *****skills.txt*33[Mind]
+ *****m_mind.txt*03[Mind - Spell Info]
+ *****skills.txt*41[Mindcraft]
+ *****m_mindcr.txt*03[Mindcraft - Spell Info]
+ *****skills.txt*42[Monster-lore]
+ *****skills.txt*59[Music]
+ *****m_music.txt*03[Music - Song Info]
+ *****skills.txt*34[Nature]
+ *****m_nature.txt*03[Nature - Spell Info]
+ *****skills.txt*35[Necromancy]
+ *****m_necrom.txt*03[Necromancy - Spell Info]
+ *****skills.txt*07[Polearm-mastery]
+ *****skills.txt*45[Possession]
+ *****c_posses.txt*04[Possession - Possessor powers ]
+ *****skills.txt*39[Prayer]
+ *****skills.txt*36[Runecraft]
+ *****c_runecr.txt*04[Runecrafting - Runecrafter powers]
+ *****skills.txt*56[Screen]
+ *****skills.txt*09[Sling-mastery]
+ *****skills.txt*14[Sneakiness]
+ *****skills.txt*23[Sorcery]
+ *****skills.txt*22[Spell-power]
+ *****skills.txt*38[Spirituality]
+ *****skills.txt*19[Stealing]
+ *****skills.txt*15[Stealth]
+ *****skills.txt*53[Stunning-blows]
+ *****skills.txt*43[Summoning]
+ *****c_summon.txt*04[Summoning - Summoning powers]
+ *****skills.txt*03[Sword-mastery]
+ *****skills.txt*46[Symbiosis]
+ *****m_symbio.txt*03[Symbiosis - Symbiotic Powers]
+ *****skills.txt*32[Temporal]
+ *****m_tempo.txt*03[Temporal - Spell Info]
+ *****skills.txt*37[Thaumaturgy]
+ *****m_thaum.txt*03[Thaumaturgy - Spell Info]
+ *****skills.txt*48[Udun]
+ *****m_udun.txt*03[Udun - Spell Info]
+ *****skills.txt*26[Water]
+ *****m_water.txt*03[Water - Spell Info]
+ *****skills.txt*02[Weaponmastery]
+ *****c_sorcer.txt*01[Sorceror]
+ *****rm_spec.txt*01[Spectre]
+ *****spoiler.hlp*01[Spoilers]
+ *****corspoil.txt*02[Corruptions]
+ *****dunspoil.txt*02[Dungeons]
+ *****essences.txt*01[Essences]
+ *****fatespoi.txt*01[Fates]
+ *****inscrip.txt*02[Floor Inscriptions]
+ *****spoil_faq.txt*20[God Quest - directions]
+ *****spoil_faq.txt*22[God Quest - how many]
+ *****spoil_faq.txt*21[God Quest - relic]
+ *****spoil_faq.txt*07[Lothlorien Poisoned water quest]
+ *****luckspoi.txt*02[Luck]
+ *****spoil_faq.txt*06[Merton the lost Hobbit quest]
+ *****wishing.txt*01[Wishing]
+ *****birth.txt*82[Stats]
+ *****birth.txt*71[Bonus table]
+ *****birth.txt*44[Charisma]
+ *****birth.txt*42[Constitution]
+ *****birth.txt*40[Dexterity]
+ *****birth.txt*14[Display]
+ *****birth.txt*06[Individual explanations]
+ *****birth.txt*33[Intelligence]
+ *****birth.txt*32[Strength]
+ *****birth.txt*36[Wisdom]
+ *****magic.txt*05[Staves]
+ *****birth.txt*54[Stealth]
+ *****tome_faq.txt*17[Strange items]
+ *****birth.txt*34[Strength]
+ *****c_summon.txt*01[Summoners]
+ *****c_summon.txt*03[Summoning]
+ *****defines.txt*12[Svals]
+ *****c_swordm.txt*01[Swordmasters]
+ *****c_symbia.txt*01[Symbiant]
+ *****c_symbia.txt*03[Naming your symbiote]
+ *****m_symbio.txt*02[Symbiosis Magic]
+ *****dungeon.txt*03[Symbols]
+~~~~~84
+*****/Tindex.txt*84[T]
+ *****birth.txt*69[Tables]
+ *****birth.txt*74[Ability Tables]
+ *****birth.txt*70[Combinations of Race and Class]
+ *****c_monk.txt*04[Monk attacks]
+ *****birth.txt*72[Stat bonuses]
+ *****m_tempo.txt*02[Temporal Magic]
+ *****m_thaum.txt*02[Thaumaturgical Magic]
+ *****c_thaum.txt*01[Thaumaturgist]
+ *****r_thlord.txt*01[Thunderlord]
+ *****whattome.txt*01[ToME - a General Description]
+ *****dungeon.txt*07[Town]
+ *****r_troll.txt*01[Troll]
+ *****g_tulkas.txt*01[Tulkas]
+ *****c_palad.txt*03[Paladin]
+ *****g_tulkas.txt*03[Prayers]
+ *****defines.txt*83[Tvals]
+~~~~~85
+*****/Uindex.txt*85[U]
+ *****m_udun.txt*02[Udun Magic]
+ *****c_unbel.txt*01[Unbeliever]
+ *****c_unbel.txt*03[Antimagic]
+~~~~~86
+*****/Vindex.txt*86[V]
+ *****rm_vamp.txt*01[Vampire]
+ *****tome_faq.txt*16[Void jumpgates]
+ *****dungeon.txt*23[Void jumpgates]
+~~~~~87
+*****/Windex.txt*87[W]
+ *****magic.txt*02[Wands]
+ *****c_warper.txt*01[Warper]
+ *****c_warrio.txt*01[Warrior]
+ *****m_water.txt*02[Water Magic]
+ *****birth.txt*86[Weapons - starting info]
+ *****tome_faq.txt*27[Weird display]
+ *****dungeon.txt*1[Wilderness]
+ *****birth.txt*38[Wisdom]
+ *****r_wodelf.txt*01[Wood Elf]
+ *****tome_faq.txt*15[Wrists hurting]
+~~~~~89
+*****/Yindex.txt*89[Y]
+ *****g_yavann.txt*01[Yavanna]
+ *****c_druid.txt*03[Druid]
+ *****g_yavann.txt*03[Prayers]
+ *****r_yeek.txt*01[Yeek]
+~~~~~90
+*****/Zindex.txt*90[Z]
+ *****rm_zomb.txt*01[Zombie]
diff --git a/lib/help/inscrip.txt b/lib/help/inscrip.txt
new file mode 100644
index 00000000..517c81c2
--- /dev/null
+++ b/lib/help/inscrip.txt
@@ -0,0 +1,65 @@
+~~~~~01|Floor Inscriptions (spoiler)
+~~~~~02|Spoilers|Floor Inscriptions
+#####R=== Floor Inscriptions ===
+
+It is possible to inscribe words on the floor in ToME. If you happen
+to be lucky and inscribe a spell formula from another language, the inscription
+will use the floor's mana to cast that spell when either you walk over it, a
+monster walks over it, or both walk over it. Not all inscriptions are triggered
+by monsters, and not all can be triggered by the player.
+
+In order to write an inscription in another language, you must first have found
+and read a parchment with some of that language's words upon it. So, just
+copying the inscriptions here won't work unless you have read the words on a
+parchment first ;-).
+
+#####GLight up the Room
+Inscription: 'ure nimir' (sun shine)
+Parchment: Numenorean for Beginners (I)
+Triggered by: Player, Monster.
+Grid Mana Needed: 30
+Effect: Lights up the current room
+
+#####GDarkness in Room
+Inscription: 'lomi gimli' (night stars)
+Parchment: Numenorean for Beginners (II)
+Triggered by: Player, Monster.
+Grid Mana Needed: 10
+Effect: Casts the room into darkness
+
+#####GStorm
+Inscription: 'dulgi bawiba' (black winds)
+Parchment: Advanced Lessons of Numenorean
+Triggered by: Player, Monster.
+Grid Mana Needed: 40
+Effect: Electrical Ball of energy released around the inscription
+
+#####GProtection
+Inscription: 'pedo mellon a minno' (say friend and enter)
+Parchment: Advanced Lessons of Sindarin
+Triggered by: Monster.
+Grid Mana Needed: 8
+Effect: Prevents a monster from stepping on the affected square
+
+#####GDwarven summoning
+Inscription: 'Baruk Khazad! Khazad aimenu!' (Axes of the Dwarves, the Dwarves
+ are upon you!)
+Parchment: Khuzdul - The Hidden Tongue of the Dwarves
+Triggered by: Player.
+Grid Mana Needed: 100
+Effect: Generates friendly Dwarven Warriors to help your cause
+
+#####GOpen Chasm
+Inscription: 'dunna hrassa' (black precipice)
+Parchment: Nandorin for Dummies
+Triggered by: Monster.
+Grid Mana Needed: 50
+Effect: Creates a bottomless hole in the floor that monsters (and potentially
+ objects) fall down
+
+#####GBlast of Black Fire
+Inscription: 'burz ghash ronk' (black fire pool)
+Parchment: Advanced Lessons of Orcish
+Triggered by: Player, Monster.
+Grid Mana Needed: 60
+Effect: Releases a ball of Hellfire around the inscription
diff --git a/lib/help/lua.hlp b/lib/help/lua.hlp
new file mode 100644
index 00000000..ba61676a
--- /dev/null
+++ b/lib/help/lua.hlp
@@ -0,0 +1,34 @@
+|||||oy
+~~~~~01|Help|Lua scripting for ToME
+#####R Welcome to the ToME Lua Help System.
+#####R=============================================
+
+Please choose one of the following help files:
+
+ *****/alua_intr.txt*0[(a) An Introduction to scripting]
+ *****/blua_pow.txt*0[(b) Adding a racial power (the 'U' menu)]
+ *****/clua_skil.txt*0[(c) Adding new skills (the 'm' menu)]
+ *****/dlua_ques.txt*0[(d) Adding a quest]
+
+
+ *****/elua_mon.txt*0[(e) Useful functions in monster.pkg]
+ *****/flua_play.txt*0[(f) Useful functions in player.pkg]
+ *****/glua_spel.txt*0[(g) Useful functions in spell.pkg]
+ *****/hlua_util.txt*0[(h) Useful functions in util.pkg]
+
+ *****/ilua_gf.txt*0[(g) A list of GF_FOO flags]
+
+
+ *****/zhelp.hlp*0[(z) Main Help menu]
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/help/lua_gf.txt b/lib/help/lua_gf.txt
new file mode 100644
index 00000000..000f4af5
--- /dev/null
+++ b/lib/help/lua_gf.txt
@@ -0,0 +1,45 @@
+|||||oy
+#####R /--------------------------\
+#####R< A partial list of GF_FLAGS >
+#####R \--------------------------/
+
+GF_ARROW: arrows
+GF_MISSILE: magic missiles
+GF_MANA: mana
+GF_LITE_WEAK: light
+GF_DARK_WEAK: dark
+GF_WATER: water
+GF_PLASMA: plasma
+GF_METEOR: meteors
+GF_ICE: ice
+GF_GRAVITY: gravity
+GF_INERTIA: inertia
+GF_FORCE: force
+GF_TIME: pure time
+GF_ACID: acid
+GF_ELEC: lightning
+GF_FIRE: flames
+GF_COLD: cold
+GF_POIS: poison
+GF_LITE: pure light
+GF_DARK: pure dark
+GF_CONFUSION: confusion
+GF_SOUND: sound
+GF_SHARDS: shards
+GF_NEXUS: nexus
+GF_NETHER: nether
+GF_CHAOS: chaos
+GF_DISENCHANT: disenchantment
+GF_KILL_WALL: wall destruction
+GF_KILL_DOOR: door destruction
+GF_KILL_TRAP: trap destruction
+GF_STONE_WALL: wall creation
+GF_MAKE_DOOR: door creation
+GF_MAKE_TRAP: trap creation
+GF_DESTRUCTION: destruction
+
+Back to the *****lua.hlp*0[lua help index] .
+
+ [[[[[gThis file by fearoffours (fearoffours@moppy.co.uk)]
+
+
diff --git a/lib/help/lua_intr.txt b/lib/help/lua_intr.txt
new file mode 100644
index 00000000..ccb87067
--- /dev/null
+++ b/lib/help/lua_intr.txt
@@ -0,0 +1,133 @@
+|||||oy
+#####R /----------------------------------------\
+#####R < Scripting for ToME with lua >
+#####R \----------------------------------------/
+
+So, you want to patch ToME eh? Maybe you've had a look at how the edit files
+work, maybe even added your own race/class, but want to go further and add
+new racial (U) or magic (m) powers. Well these help files will show a little
+bit of how to do that.
+
+I am not a master at this kind of thing. I wrote a small script, with much
+help from DarkGod, and he subsequently asked me to write these help files. I
+was looking forward to when the lua help files came out so that I could look
+at them myself. Little did I know I'd be asked to write them. Therefore I
+apologise for any inaccuracies or errors that you find, and if you care to let
+me know of any improvements which could be made (especially if you're an
+experienced programmer/scripter), I'd love to know. Email me at
+[[[[[gfearoffours@moppy.co.uk].
+
+#####R=== The example scripts ===
+
+These help files take the form of a tutorial, adding a line at a time to a
+script, and explaining important concepts along the way. To see it all in
+action, I strongly suggest that you download my example script pack from
+[[[[[Ghttp://www.moppy.co.uk/angband.htm]. As well as including all the
+scripts covered in these help files, they also include the addition of my
+"hina" race which has a Lua scripted racial power which you might like to look
+at. There's also a quest which I will be including documentation for as a
+tutorial soon. Plus there's all the other lua scripts in the lib\scpt
+directory to look at. Most of what you see in these files has been learned from
+those files anyway!
+
+The source code is invaluable as well. There's a searchable and browsable
+version of the latest ToME source code available online at
+[[[[[Ghttp://www.t-o-m-e.net/cvs.php]. Use it!
+
+If you don't want to download and install the example scripts, then just
+follow the tutorials with a text editor open! But I'll say it again, it's a lot
+easier if you download and install the example scripts.
+
+This file goes on to explain the concepts of scripting, programming,
+variables and functions. If you're familiar with these concepts, you might
+as well take a look at how to add a power to the U menu in the
+*****lua_pow.txt*0[Scripting a racial power] file.
+
+
+#####R=== Defining some basic stuff ===
+
+Computers don't do anything that they're not told to do. When we script, or
+program, we must assume they know nothing. We have to tell them each little
+bit of information from the ground up.
+
+A program, or a script (we'll talk about exact differences later) is like a
+set of instructions. Let's imagine that people responded to programs, and
+that we had a program called "Housework". Its series of instructions might
+look something like this:
+
+#####BDo the Washing up.
+#####BClean the kitchen.
+#####BDust the shelves.
+#####BHoover the lounge.
+
+Each step above could be called a function, as they are all actions that
+need to be carried out. Now to you and me, we'd understand that program just
+fine, but if someone didn't know HOW to wash, or what hoovering was, they'd
+soon run into problems. For those people we'd need to define each function
+clearly. Thus "do the washing up" might be -
+
+#####BRun hot water into bowl.
+#####BAdd washing up liquid.
+#####BPut dirty plates into bowl
+#####BScrub plates till clean
+#####BPlace clean plates on rack to dry,
+
+There's still plenty of problems here though. We've not said to turn the tap
+off, or what the bowl is, or to wash any dirty cutlery, mugs, saucepans, etc.,
+etc. Whilst this might seem fairly obvious to a person, this is how we need
+to think when writing programs for computers.
+
+Lets look now at some of the terms we're going to be using, in the rest of
+these help files, and what they mean...
+
+#####R=== Variables and Constants ===
+A variable is a way to store information in a computer. Just as you store
+things in your own memory, you can store things in the computer's memory. And
+just as things change in your memory, so things can change in the computer's
+memory. This factor of change is why they're called "variables" and not
+"statics".
+
+For instance, you may have a friend's email address committed to memory, but
+things change over time, they get a new ISP or domain, and so their email
+address changes. You commit this new address to memory, and eventually
+forget the old one. The thing you have stored in your memory is the same
+(your friend's address) but the value (property) of what you have stored has
+changed (from friend@old-address.com to friend@new-address.com).
+
+Variables are the building blocks out of which you will create your patch.
+
+A variable which will *never* change its value is called a constant.
+
+#####R===Functions===
+
+A function is a series of steps or statements, grouped together and given
+one name. When you want to carry out those steps, you simply ask the
+computer to carry out that function. To go back to our original example,
+rather than saying, "I'd like you to run some hot water into a bowl, add the
+washing up liquid, put the dirty plates into it, and then scrub them till
+they're clean", we just say "do the washing up".
+
+This is where we come to the difference between scripting and programming.
+With scripting we can use the functions and variables that exist in the
+ToME code. Maintainers like DarkGod have already made sure that the
+computer knows how to "do the washing up", including turning the tap off and
+what the bowl is. All we need to do in our script is say "do the washing
+up". Or to look at it in a more relevant way, the game has been coded so
+that when a magic missile is fired, a bolt or beam spell with a black line
+of asterisks will be drawn in the direction indicated by the player, the
+mana of that spell will be used up, the monster will take the appropriate
+amount of damage, and so on. All we need to do in our script is say "fire a
+magic missile".
+
+As you script, you will still be designing your own functions, and
+variables, but the hardest parts have been done for you!
+
+Not every function and global variable in the source-code has been exported to
+use in your scripting. But the ones that have are easily identifiable by
+looking in any source files with the extension .pkg . Chris Hadgis has written
+some excellent documentation which outline the use of the most important
+functions in some of these files. They outline the functions from the
+
+OK, the first tutorial proper is on *****lua_pow.txt*0[adding a racial power] .
+
+ [[[[[gThis file by fearoffours (fearoffours@moppy.co.uk)]
diff --git a/lib/help/lua_mon.txt b/lib/help/lua_mon.txt
new file mode 100644
index 00000000..9bb363c0
--- /dev/null
+++ b/lib/help/lua_mon.txt
@@ -0,0 +1,535 @@
+|||||oy
+
+#####R /----------------------------------------\
+#####R < monster.pkg functions helper file >
+#####R \----------------------------------------/
+
+
+----------------------------------------------------------------------
+
+#####R=== race_info_idx ===
+
+#####GDeclaration
+ extern monster_race* race_info_idx(int r_idx, int ego);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Return a (monster_race*) with the combinations of the monster
+ * properties and the ego type
+ */
+
+#####GDescription
+Get monster info and ego info for monster with monster index "r_idx"
+and monster ego "ego". The ego information is applied to the monster
+information and the new monster information is returned.
+
+For example, race_info_idx(141,7) will create a brown yeek (monster)
+shaman (ego).
+
+#####GParameters
+> "r_idx" is an entry from the "r_info.txt" file. Beware: there is no
+ range checking.
+> "ego" is an entry from the "re_info.txt". Beware: there is no range
+ checking.
+
+----------------------------------------------------------------------
+
+#####R=== delete_monster_idx ===
+
+#####GDeclaration
+ extern void delete_monster_idx(int i);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Delete a monster by index.
+ *
+ * When a monster is deleted, all of its objects are deleted.
+ */
+
+#####GDescription
+Delete monster "i" from the monster array.
+
+#####GParameters
+> "i" is the index for the monster list (m_list[]). Beware: there is
+ no range checking.
+
+----------------------------------------------------------------------
+
+#####R=== m_pop ===
+
+#####GDeclaration
+ extern s16b m_pop(void);
+
+#####GFile
+ monsters2.c
+
+#####GComment
+/*
+ * Acquires and returns the index of a "free" monster.
+ *
+ * This routine should almost never fail, but it *can* happen.
+ */
+
+#####GDescription
+Get an empty slot in the monster list (m_list[]). If there are no
+empty slots, a slot will be reclaimed from a "dead" monster. If all
+slots are full, 0 is returned, which means the function has failed
+("Too many monsters!").
+
+----------------------------------------------------------------------
+
+#####R=== get_mon_num_prep ===
+
+#####GDeclaration
+ extern errr get_mon_num_prep(void);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Apply a "monster restriction function" to the "monster allocation table"
+ */
+
+#####GDescription
+There are no parameters, but there are some other variables which will
+need to be set. They are get_mon_num_hook and get_mon_num2_hook. They
+are pointers to functions.
+
+For example, get_mon_num_hook = monster_volcano means when
+get_mon_num_hook is called (*get_mon_num_hook)(index), the actual
+function called is monster_volcano(index). This particular function
+returns TRUE if the monster indicated by "index" has the
+RF8_WILD_VOLCANO flag set.
+
+It is a good idea to store the old value of get_mon_num_hook before
+setting a new one, and restoring it when your function is finished.
+
+Following is a list of functions which can be assigned to
+get_mon_num_hook:
+
+create_molds_hook
+create_townpeople_hook
+mon_hook_bounty
+monster_dungeon
+monster_grass
+monster_mountain
+monster_ocean
+monster_quest
+monster_shore
+monster_town
+monster_volcano
+monster_waste
+monster_wood
+mutate_monster_okay
+place_monster_okay
+summon_specific_okay
+vault_aux_animal
+vault_aux_chapel
+vault_aux_clone
+vault_aux_demon
+vault_aux_dragon
+vault_aux_giant
+vault_aux_jelly
+vault_aux_kennel
+vault_aux_orc
+vault_aux_symbol
+vault_aux_treasure
+vault_aux_troll
+vault_aux_undead
+
+Or you can write your own. The function must take an integer (index)
+as a parameter and return boolean (TRUE if the monster is selected,
+or FALSE if it is not).
+
+----------------------------------------------------------------------
+
+#####R=== get_mon_num ===
+
+#####GDeclaration
+ extern s16b get_mon_num(int level);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Choose a monster race that seems "appropriate" to the given level
+ *
+ * This function uses the "prob2" field of the "monster allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" monster, in
+ * a relatively efficient manner.
+ *
+ * Note that "town" monsters will *only* be created in the town, and
+ * "normal" monsters will *never* be created in the town, unless the
+ * "level" is "modified", for example, by polymorph or summoning.
+ *
+ * There is a small chance (1/50) of "boosting" the given depth by
+ * a small amount (up to four levels), except in the town.
+ *
+ * It is (slightly) more likely to acquire a monster of the given level
+ * than one of a lower level. This is done by choosing several monsters
+ * appropriate to the given level and keeping the "hardest" one.
+ *
+ * Note that if no monsters are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ */
+
+Description:
+For the given level "level", return the index of an appropriate
+monster race.
+
+#####GParameters
+> "level" is a dungeon level
+
+----------------------------------------------------------------------
+
+#####R=== monster_desc ===
+
+#####GDeclaration
+ extern void monster_desc(char *desc, monster_type *m_ptr,
+ int mode);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Build a string describing a monster in some way.
+ *
+ * We can correctly describe monsters based on their visibility.
+ * We can force all monsters to be treated as visible or invisible.
+ * We can build nominatives, objectives, possessives, or reflexives.
+ * We can selectively pronominalize hidden, visible, or all monsters.
+ * We can use definite or indefinite descriptions for hidden monsters.
+ * We can use definite or indefinite descriptions for visible monsters.
+ *
+ * Pronominalization involves the gender whenever possible and allowed,
+ * so that by cleverly requesting pronominalization / visibility, you
+ * can get messages like "You hit someone. She screams in agony!".
+ *
+ * Reflexives are acquired by requesting Objective plus Possessive.
+ *
+ * If no m_ptr arg is given (?), the monster is assumed to be hidden,
+ * unless the "Assume Visible" mode is requested.
+ *
+ * If no r_ptr arg is given, it is extracted from m_ptr and r_info
+ * If neither m_ptr nor r_ptr is given, the monster is assumed to
+ * be neuter, singular, and hidden (unless "Assume Visible" is set),
+ * in which case you may be in trouble... :-)
+ *
+ * I am assuming that no monster name is more than 70 characters long,
+ * so that "char desc[80];" is sufficiently large for any result.
+ *
+ * Mode Flags:
+ * 0x01 --> Objective (or Reflexive)
+ * 0x02 --> Possessive (or Reflexive)
+ * 0x04 --> Use indefinites for hidden monsters ("something")
+ * 0x08 --> Use indefinites for visible monsters ("a kobold")
+ * 0x10 --> Pronominalize hidden monsters
+ * 0x20 --> Pronominalize visible monsters
+ * 0x40 --> Assume the monster is hidden
+ * 0x80 --> Assume the monster is visible
+ *
+ * Useful Modes:
+ * 0x00 --> Full nominative name ("the kobold") or "it"
+ * 0x04 --> Full nominative name ("the kobold") or "something"
+ * 0x80 --> Genocide resistance name ("the kobold")
+ * 0x88 --> Killing name ("a kobold")
+ * 0x22 --> Possessive, genderized if visible ("his") or "its"
+ * 0x23 --> Reflexive, genderized if visible ("himself") or "itself"
+ */
+
+#####GDescription
+Return a monster description "desc" for monster "monster_type" using
+flag "mode". The modes are described above.
+
+#####GParameters
+> "desc" is the returned description.
+> "monster type" is the monster (monster pointer).
+> "mode" is one of the modes described in the comments.
+
+----------------------------------------------------------------------
+
+#####R=== monster_race_desc ===
+
+#####GDeclaration
+ extern void monster_race_desc(char *desc, int r_idx,
+ int ego);
+
+#####GFile
+ monster2.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the monster description "desc" for monster with monster index
+"r_idx" and monster ego "ego". The monster description is made up of
+the ego name (if any) and monster name, or the unique name.
+
+#####GParameters
+> "desc" is the returned description.
+> "r_idx" is an entry from the "r_info.txt" file. Beware: there is no
+ range checking.
+> "ego" is an entry from the "re_info.txt". Beware: there is no range
+ checking.
+
+----------------------------------------------------------------------
+
+#####R=== place_monster_aux ===
+
+#####GDeclaration
+ extern bool place_monster_aux(int y, int x, int r_idx,
+ bool slp, bool grp, int status);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Attempt to place a monster of the given race at the given location
+ *
+ * Note that certain monsters are now marked as requiring "friends".
+ * These monsters, if successfully placed, and if the "grp" parameter
+ * is TRUE, will be surrounded by a "group" of identical monsters.
+ *
+ * Note that certain monsters are now marked as requiring an "escort",
+ * which is a collection of monsters with similar "race" but lower
+ * level.
+ *
+ * Some monsters induce a fake "group" flag on their escorts.
+ *
+ * Note the "bizarre" use of non-recursion to prevent annoying output
+ * when running a code profiler.
+ *
+ * Note the use of the new "monster allocation table" code to restrict
+ * the "get_mon_num()" function to "legal" escort types.
+ */
+
+#####GDescription
+Attempt to place a monster at grid "y", "x". The monster has monster
+index "m_idx". The monster may be asleep ("slp"). The monster may be
+surrounded by a group of identical monsters ("grp"). The monster has
+a status of "status" (see below). The function returns TRUE if the
+monster is placed successfully, otherwise FALSE.
+
+#####GParameters
+> "y" is the y co-ordinate of the target grid.
+> "x" is the x co-ordinate of the target grid.
+> "r_idx" is an entry from the "r_info.txt" file. Beware: there is no
+ range checking.
+> "slp" is TRUE if the monster is asleep, otherwise FALSE.
+> "grp" is TRUE if the monster is surrounded by a group, otherwise
+ FALSE.
+> "status" is the status of the monster
+ *****fields.txt*0[status]
+
+----------------------------------------------------------------------
+
+#####R=== place_monster ===
+
+#####GDeclaration
+ extern bool place_monster(int y, int x, bool slp,
+ bool grp);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Hack -- attempt to place a monster at the given location
+ *
+ * Attempt to find a monster appropriate to the "monster_level"
+ */
+
+#####GDescription
+Attempt to place a monster at grid "y", "x". The monster may be asleep
+("slp"). The monster may be surrounded by a group of identical
+monsters ("grp"). The monster is of the appropriate monster level. The
+function returns TRUE if the monster is placed successfully, otherwise
+FALSE.
+
+#####GParameters
+> "y" is the y co-ordinate of the target grid.
+> "x" is the x co-ordinate of the target grid.
+> "slp" is TRUE if the monster is asleep, otherwise FALSE.
+> "grp" is TRUE if the monster is surrounded by a group, otherwise
+ FALSE.
+
+----------------------------------------------------------------------
+
+#####R=== place_monster_one ===
+
+#####GDeclaration
+ extern s16b place_monster_one(int y, int x, int r_idx,
+ int ego, bool slp, int status);
+
+#####GFile
+ monster2.c
+
+#####GComment
+/*
+ * Attempt to place a monster of the given race at the given location.
+ *
+ * To give the player a sporting chance, any monster that appears in
+ * line-of-sight and is extremely dangerous can be marked as
+ * "FORCE_SLEEP", which will cause them to be placed with low energy,
+ * which often (but not always) lets the player move before they do.
+ *
+ * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
+ *
+ * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
+ * remove old "cur_num" and "max_num" fields.
+ *
+ * XXX XXX XXX Actually, do something similar for artifacts, to simplify
+ * the "preserve" mode, and to make the "what artifacts" flag more useful.
+ *
+ * This is the only function which may place a monster in the dungeon,
+ * except for the savefile loading code.
+ */
+
+#####GDescription
+Attempt to place a monster at grid "y", "x". The monster has monster
+index "m_idx". The monster may be asleep ("slp"). The monster may have
+an ego type ("ego"). The monster has a status of "status" (see below).
+The function returns TRUE if the monster is placed successfully,
+otherwise FALSE.
+
+#####GParameters
+> "y" is the y co-ordinate of the target grid.
+> "x" is the x co-ordinate of the target grid.
+> "r_idx" is an entry from the "r_info.txt" file. Beware: there is no
+ range checking.
+> "slp" is TRUE if the monster is asleep, otherwise FALSE.
+> "ego" is an entry from the "re_info.txt". Beware: there is no range
+ checking.
+> "status" is the status of the monster
+ *****fields.txt*0[status]
+
+----------------------------------------------------------------------
+
+#####R=== is_friend ===
+
+#####GDeclaration
+ extern int is_friend(monster_type *m_ptr);
+
+#####GFile
+ monster3.c
+
+#####GComment
+/*
+ * Is the monster in friendly state(pet, friend, ..)
+ * -1 = enemy, 0 = neutral, 1 = friend
+ */
+
+#####GDescription
+Return a value to indicate the status of monster "m_ptr".
+ *****fields.txt*0[status]
+
+#####GParameters
+> "m_ptr" is a pointer to a monster.
+
+----------------------------------------------------------------------
+
+#####R=== is_enemy ===
+
+#####GDeclaration
+ extern bool is_enemy(monster_type *m_ptr,
+ monster_type *t_ptr);
+
+#####GFile
+ monster3.c
+
+#####GComment
+/* Should they attack each others */
+
+#####GDescription
+Return TRUE if monster "m_ptr" should attack monster "t_ptr". If
+"m_ptr" is stupid and "r_ptr" is a different type of monster then the
+function will return TRUE. If "m_ptr" is not neutral and "r_ptr" is a
+breeder, and "r_ptr" is a different type of monster then the function
+will return TRUE (and vice versa). If both monsters are not neutral
+and one is friendly and the other isn't then the function will return
+TRUE. Otherwise the function returns FALSE.
+
+#####GParameters
+> "m_ptr" is a pointer to a monster.
+> "t_ptr" is a pointer to a monster (target).
+
+----------------------------------------------------------------------
+
+#####R=== change_side ===
+
+#####GDeclaration
+ extern bool change_side(monster_type *m_ptr);
+
+#####GFile
+ monster3.c
+
+#####GComment
+(none)
+
+#####GDescription
+Change the status of monster "m_ptr" from friendly to unfriendly and
+vice versa. Friends and pets become enemies. Neutral Ms become
+neutral Ps and vice versa. Companions are unaffected. The
+function returns TRUE if the status changed, otherwise FALSE.
+
+#####GParameters
+> "m_ptr" is a pointer to a monster.
+
+----------------------------------------------------------------------
+
+#####R=== find_position ===
+
+#####GDeclaration
+ extern void find_position(int y, int x, int *yy = 0,
+ int *xx = 0);
+
+#####GFile
+ lua_bind.c
+
+#####GComment
+(none)
+
+#####GDescription
+Find a new grid "yy", "xx" within 6 grids of target grid "y", "x".
+The new grid must be within line-of-sight of the target grid. A
+maximum of 5000 attempts is made.
+
+#####GParameters
+> "y" is the y co-ordinate of the target grid.
+> "x" is the x co-ordinate of the target grid.
+> "yy" is the y co-ordinate of the new grid.
+> "xx" is the x co-ordinate of the new grid.
+
+----------------------------------------------------------------------
+
+#####R=== can_create_companion ===
+
+#####GDeclaration
+ extern bool can_create_companion();
+
+#####GFile
+ monster3.c
+
+#####GComment
+/* Returns if a new companion is allowed */
+
+#####GDescription
+Return TRUE if a companion can be created, otherwise FALSE.
+
+----------------------------------------------------------------------
+
+Back to the *****lua.hlp*0[lua help index] .
+
+
+ [[[[[gThis file by Chris Hadgis]
diff --git a/lib/help/lua_play.txt b/lib/help/lua_play.txt
new file mode 100644
index 00000000..6ab64ddb
--- /dev/null
+++ b/lib/help/lua_play.txt
@@ -0,0 +1,1225 @@
+|||||oy
+
+#####R /----------------------------------------\
+#####R < player.pkg functions helper file >
+#####R \----------------------------------------/
+
+----------------------------------------------------------------------
+
+#####RFunction: set_parasite
+
+#####GDeclaration: bool set_parasite(int v, int r);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->parasite" and "p_ptr->parasite_r_idx"
+* notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until parasite with monster index "r" is created. The
+player gets the message "You feel something growing in you" if "v"
+is > 0. Otherwise the player gets the message "Your body convulse
+and spawn <monster name>" if the monster is created (80% chance) or
+"The hideous thing growing in you seems to die" if the monster dies.
+
+#####GParameters:
+>v is the time until the parasite gestates (must be between 0 and
+ 10000).
+>r is the monster index of parasite to be created.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_disrupt_shield
+
+#####GDeclaration: bool set_disrupt_shield(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->disrupt_shield"
+* notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until shield of invulnerability expires. The player gets
+the message "You feel invulnerable" if "v" is > 0. Otherwise the
+player gets the message "You are more vulnerable".
+
+#####GParameters:
+>v is the time until the shield expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_prob_travel
+
+#####GDeclaration: bool set_prob_travel(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->prob_travel"
+* notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until random teleportation expires. The player gets
+the message "You feel instable" if "v" is > 0. Otherwise the
+player gets the message "You are more stable".
+
+#####GParameters:
+>v is the time until random teleportation expires (must be between 0
+ and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_deadly
+
+#####GDeclaration: bool set_tim_deadly(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_deadly"
+*/
+
+#####GDescription:
+Set time "v" until deadly accuracy expires. The player gets the
+message "You feel extremely accurate" if "v" is > 0. Otherwise the
+player gets the message "You are suddenly much less accurate".
+
+#####GParameters:
+>v is the time until deadly accuracy expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_res_time
+
+#####GDeclaration: bool set_tim_res_time(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_res_time"
+*/
+
+#####GDescription:
+Set time "v" until space-time distortions expire. The player gets the
+message "You are now protected against the space-time distortions" if
+"v" is > 0. Otherwise the player gets the message "You are no longer
+protected against the space-time distortions".
+
+#####GParameters:
+>v is the time until space-time distortions expire (must be between
+ 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_reflect
+
+#####GDeclaration: bool set_tim_reflect(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_reflect"
+*/
+
+#####GDescription:
+Set time "v" until reflection expire. The player gets the message
+"You start reflecting the world around you" if "v" is > 0. Otherwise
+the player gets the message "You stop reflecting".
+
+#####GParameters:
+>v is the time until reflection expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_meditation
+
+#####GDeclaration: bool set_meditation(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->meditation"
+*/
+
+#####GDescription:
+Set time "v" until meditation expire. The player gets the message
+"You start meditating on yourself" if "v" is > 0. Otherwise the
+player gets the message "You stop your self meditation".
+
+#####GParameters:
+>v is the time until meditation expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_strike
+
+#####GDeclaration: bool set_strike(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->strike"
+*/
+
+#####GDescription:
+Set time "v" until accurate strikes expire. The player gets the
+message "You feel very accurate" if "v" is > 0. Otherwise the player
+gets the message "You are no longer very accurate".
+
+#####GParameters:
+>v is the time until accurate strikes expire (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_walk_water
+
+#####GDeclaration: bool set_walk_water(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->walk_water", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until walking on water expires. The player gets the
+message "You feel strangely insubmersible" if "v" is > 0. Otherwise
+the player gets the message "You are no longer insubmersible".
+
+#####GParameters:
+>v is the time until walking on water expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_ffall
+
+#####GDeclaration: bool set_tim_ffall(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_ffall"
+*/
+
+#####GDescription:
+Set time "v" until feather-fall expires. The player gets the message
+"You feel very light" if "v" is > 0. Otherwise the player gets the
+message "You are suddenly heavier".
+
+#####GParameters:
+>v is the time until feather-fall expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_fire_aura
+
+#####GDeclaration: bool set_tim_fire_aura(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_fire_aura"
+*/
+
+#####GDescription:
+Set time "v" until fiery aura expires. The player gets the message
+"You are enveloped in flames" if "v" is > 0. Otherwise the player
+gets the message "You are no longer enveloped in flames".
+
+#####GParameters:
+>v is the time until fiery aura expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_holy
+
+#####GDeclaration: bool set_holy(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->holy", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until holiness expires. The player gets the message
+"You feel a holy aura around you" if "v" is > 0. Otherwise the
+player gets the message "The holy aura vanishes".
+
+#####GParameters:
+>v is the time until holiness expires (must be between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_grace
+
+#####GDeclaration: void set_grace(s32b v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->grace", notice observable changes
+*/
+
+#####GDescription:
+Set grace to value "v". Don't allow grace to fall below -30000 or
+rise above 30000.
+
+#####GParameters:
+>v is the value of grace.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_mimic
+
+#####GDeclaration: bool set_mimic(int v, int p);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_mimic", and "p_ptr->mimic_form",
+* notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until morph into monster with monster index "p" expires.
+The player gets the message "You feel your body change" if "v" is > 0.
+Otherwise the player gets the message "You are no longer transformed".
+
+#####GParameters:
+>v is the time until transformation expires (must be between 0 and
+ 10000).
+>p is the monster index of the monster the player wants to mimic.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_no_breeders
+
+#####GDeclaration: bool set_no_breeders(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "no_breeds"
+*/
+
+#####GDescription:
+Set time "v" until breeders can breed again. The player gets the
+message "You feel an anti-sexual aura" if "v" is > 0. Otherwise the
+player gets the message "You no longer feel an anti-sexual aura".
+Okay...
+
+#####GParameters:
+>v is the time until breeders can breed again (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_invis
+
+#####GDeclaration: bool set_invis(int v,int p);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_invis", and "p_ptr->tim_inv_pow",
+* notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until invisibility expires. The player gets the message
+"You feel your body fade away" if "v" is > 0. Otherwise the player
+gets the message "You are no longer invisible".
+
+#####GParameters:
+>v is the time until invisibility expires (must be between 0 and
+ 10000).
+>p is the power of timed invisibility.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_lite
+
+#####GDeclaration: bool set_lite(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_lite", notice observable changes
+*
+* Note the use of "PU_VIEW", which is needed to
+* memorise any terrain features which suddenly become "visible".
+* Note that blindness is currently the only thing which can affect
+* "player_can_see_bold()".
+*/
+
+#####GDescription:
+Set time "v" until brightness expires. The player gets the message
+"You suddenly seem brighter" if "v" is > 0. Otherwise the player
+gets the message "You are no longer bright".
+
+#####GParameters:
+>v is the time until brightness expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_blind
+
+#####GDeclaration: bool set_blind(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->blind", notice observable changes
+*
+* Note the use of "PU_UN_VIEW", which is needed to memorise any terrain
+* features which suddenly become "visible".
+* Note that blindness is currently the only thing which can affect
+* "player_can_see_bold()".
+*/
+
+#####GDescription:
+Set time "v" until blindness expires. The player gets the message "You
+are blind" if "v" is > 0. Otherwise the player gets the message "You
+can see again".
+
+#####GParameters:
+>v is the time until blindness expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_confused
+
+#####GDeclaration: bool set_confused(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->confused", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until confusion expires. The player gets the message "You
+are confused" if "v" is > 0. Otherwise the player gets the message
+"You feel less confused now".
+
+#####GParameters:
+>v is the time until confusion expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_poisoned
+
+#####GDeclaration: bool set_poisoned(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->poisoned", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until poison expires. The player gets the message "You
+are poisoned" if "v" is > 0. Otherwise the player gets the message
+"You are no longer poisoned".
+
+#####GParameters:
+>v is the time until poison expires (must be between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_afraid
+
+#####GDeclaration: bool set_afraid(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->afraid", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until fear expires. The player gets the message "You are
+terrified" if "v" is > 0. Otherwise the player gets the message "You
+feel bolder now".
+
+#####GParameters:
+>v is the time until fear expires (must be between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_paralyzed
+
+#####GDeclaration: bool set_paralyzed(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->paralyzed", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until paralysis expires. The player gets the message "You
+are paralyzed" if "v" is > 0. Otherwise the player gets the message
+"You can move again".
+
+#####GParameters:
+>v is the time until paralysis expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_image
+
+#####GDeclaration: bool set_image(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->image", notice observable changes
+*
+* Note that we must redraw the map when hallucination changes.
+*/
+
+#####GDescription:
+Set time "v" until hallucination expires. The player gets the message
+"Oh, wow! Everything looks so cosmic now" if "v" is > 0. Otherwise
+the player gets the message "You can see clearly again".
+
+#####GParameters:
+>v is the time until hallucination expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_fast
+
+#####GDeclaration: bool set_fast(int v, int p);
+
+#####GFile: xtra2.c
+
+#####GComment:
+(none)
+
+#####GDescription:
+Set time "v" until speed of speed factor "p" expires. The player gets
+the message "You feel yourself moving faster" if "v" is > 0. Otherwise
+the player gets the message "You feel yourself slow down".
+
+#####GParameters:
+>v is the time until speed expires (must be between 0 and 10000).
+>p is the speed factor.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_light_speed
+
+#####GDeclaration: bool set_light_speed(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->lightspeed", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until light-speed expires. The player gets the message
+"You feel as if time has stopped" if "v" is > 0. Otherwise the player
+gets the message "You feel time returning to its normal rate".
+
+#####GParameters:
+>v is the time until light-speed expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_slow
+
+#####GDeclaration: bool set_slow(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->slow", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until slowness expires. The player gets the message "You
+feel yourself moving slower" if "v" is > 0. Otherwise the player gets
+the message "You feel yourself speed up".
+
+#####GParameters:
+>v is the time until slowness expires (must be between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_shield
+
+#####GDeclaration: bool set_shield(int v, int p, s16b o, s16b d1, s16b d2);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->shield", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until stone-shield expires. The player gets the message
+"Your skin turns to stone" if "v" is > 0. Otherwise the player gets
+the message "Your skin returns to normal". Stone-shield has spell
+power "p", spell option "o", and power options "d1" and "d2".
+
+#####GParameters:
+>v is the time until stone-shield expires (must be between 0 and
+ 10000).
+>p is the power of the stone-shield spell.
+>o is the option of the stone-shield spell.
+>d1 is the power for option 1 of the stone-shield spell.
+>d2 is the power for option 2 of the stone-shield spell.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_blessed
+
+#####GDeclaration: bool set_blessed(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->blessed", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until blessing expires. The player gets the message "You
+feel righteous" if "v" is > 0. Otherwise the player gets the message
+"The prayer has expired".
+
+#####GParameters:
+>v is the time until blessing expires (must be between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_hero
+
+#####GDeclaration: bool set_hero(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->hero", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until heroism expires. The player gets the message "You
+feel like a hero" if "v" is > 0. Otherwise the player gets the
+message "The heroism wears off".
+
+#####GParameters:
+>v is the time until heroism expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_shero
+
+#####GDeclaration: bool set_shero(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->shero", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until berserk expires. The player gets the message "You
+feel like a killing machine" if "v" is > 0. Otherwise the player gets
+the message "You feel less Berserk".
+
+#####GParameters:
+>v is the time until berserk expires (must be between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_protevil
+
+#####GDeclaration: bool set_protevil(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->protevil", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until protection from evil expires. The player gets the
+message "You feel safe from evil" if "v" is > 0. Otherwise the player
+gets the message "You no longer feel safe from evil".
+
+#####GParameters:
+>v is the time until protection from evil expires (must be between 0
+ and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_protgood
+
+#####GDeclaration: bool set_protgood(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->protgood", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until protection from good expires. The player gets the
+message "You feel safe from good" if "v" is > 0. Otherwise the player
+gets the message "You no longer feel safe from good".
+
+#####GParameters:
+>v is the time until protection from evil expires (must be between 0
+ and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_protundead
+
+#####GDeclaration: bool set_protundead(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->protundead", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until protection from undead expires. The player gets the
+message "You feel safe from undead" if "v" is > 0. Otherwise the
+player gets the message "You no longer feel safe from undead".
+
+#####GParameters:
+>v is the time until protection from undead expires (must be between
+ 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_invuln
+
+#####GDeclaration: bool set_invuln(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->invuln", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until invulnerability expires. The player gets the
+message "Invulnerability" if "v" is > 0. Otherwise the player gets
+the message "The invulnerability wears off".
+
+#####GParameters:
+>v is the time until invulnerability expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_invis
+
+#####GDeclaration: bool set_tim_invis(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_invis", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until see invisible expires. The player gets the message
+"Your eyes feel very sensitive" if "v" is > 0. Otherwise the player
+gets the message "Your eyes feel less sensitive".
+
+#####GParameters:
+>v is the time until see invisible expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_tim_infra
+
+#####GDeclaration: bool set_tim_infra(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_infra", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until infravision expires. The player gets the message
+"Your eyes begin to tingle" if "v" is > 0. Otherwise the player gets
+the message "Your eyes stop tingling".
+
+#####GParameters:
+>v is the time until infravision expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_mental_barrier
+
+#####GDeclaration: bool set_mental_barrier(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->tim_mental_barrier", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until mental barrier expires. The player gets the message
+"Your mind grows stronger" if "v" is > 0. Otherwise the player gets
+the message "Your mind is no longer especially strong".
+
+#####GParameters:
+>v is the time until mental barrier expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_acid
+
+#####GDeclaration: bool set_oppose_acid(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+
+#####GDescription:
+Set time "v" until feather-fall expires. The player gets the message
+"You feel very light" if "v" is > 0. Otherwise the player gets the
+message "You are suddenly heavier".
+
+#####GParameters:
+>v is the time until feather-fall expires (must be between 0 and
+ 10000).
+>v is the time until feather-fall expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_elec
+
+#####GDeclaration: bool set_oppose_elec(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_elec", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until electricity resistance expires. The player gets
+the message "You feel resistant to electricity" if "v" is > 0.
+Otherwise the player gets the message "You feel less resistant to
+electricity".
+
+#####GParameters:
+>v is the time until electricity resistance expires (must be between
+ 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_fire
+
+#####GDeclaration: bool set_oppose_fire(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_fire", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until fire resistance expires. The player gets the
+message "You feel resistant to fire" if "v" is > 0. Otherwise the
+player gets the message "You feel less resistant to fire".
+
+#####GParameters:
+>v is the time until fire resistance expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_cold
+
+#####GDeclaration: bool set_oppose_cold(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_cold", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until cold resistance expires. The player gets the
+message "You feel resistant to cold" if "v" is > 0. Otherwise the
+player gets the message "You feel less resistant to cold".
+
+#####GParameters:
+>v is the time until cold resistance expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_pois
+
+#####GDeclaration: bool set_oppose_pois(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_pois", notice observable changes
+*/
+
+#####GDescription:
+Set time "v" until poison resistance expires. The player gets the
+message "You feel resistant to poison" if "v" is > 0. Otherwise the
+player gets the message "You feel less resistant to poison".
+
+#####GParameters:
+>v is the time until poison resistance expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_ld
+
+#####GDeclaration: bool set_oppose_ld(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_ld"
+*/
+
+#####GDescription:
+Set time "v" until light and dark resistance expires. The player gets
+the message "You feel protected against the light's fluctuation" if
+"v" is > 0. Otherwise the player gets the message "You are no longer
+protected against the light's fluctuation".
+
+#####GParameters:
+>v is the time until light and dark resistance expires (must be
+ between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_cc
+
+#####GDeclaration: bool set_oppose_cc(int v);
+
+#####GFile: xtra2.c
+/*
+* Set "p_ptr->oppose_cc"
+*/
+
+#####GComment:
+
+#####GDescription:
+Set time "v" until chaos resistance expires. The player gets the
+message "You feel protected against raw chaos" if "v" is > 0.
+Otherwise the player gets the message "You are no longer protected
+against chaos".
+
+#####GParameters:
+>v is the time until chaos resistance expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_ss
+
+#####GDeclaration: bool set_oppose_ss(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_ss"
+*/
+
+#####GDescription:
+Set time "v" until sound and shard resistance expires. The player gets
+the message "You feel protected against the ravages of sound and
+shards" if "v" is > 0. Otherwise the player gets the message "You are
+no longer protected against the ravages of sound and shards".
+
+#####GParameters:
+>v is the time until sound and shard resistance expires (must be
+ between 0 and 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_oppose_nex
+
+#####GDeclaration: bool set_oppose_nex(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->oppose_nex"
+*/
+
+#####GDescription:
+Set time "v" until nexus resistance expires. The player gets the
+message "You feel protected against the strange forces of nexus" if
+"v" is > 0. Otherwise the player gets the message "You are no longer
+protected against the strange forces of nexus".
+
+#####GParameters:
+>v is the time until nexus resistance expires (must be between 0 and
+ 10000).
+
+----------------------------------------------------------------------
+
+#####RFunction: set_stun
+
+#####GDeclaration: bool set_stun(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->stun", notice observable changes
+*
+* Note the special code to only notice "range" changes.
+*/
+
+#####GDescription:
+Set stun level "v". If the player race can't be stunned then the level
+is forced to 0. A value > 100 means the player is knocked out. A value
+>50 is a heavy stun. A value > 0 is a stun. If the stun level has
+increased, a message is printed. There is a small chance of stun level
+in 1000, or a 1 in 16 chance of a vicious blow which decreases
+intelligence and/or wisdom for a while.
+
+#####GParameters:
+>v is the stun level.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_cut
+
+#####GDeclaration: bool set_cut(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->cut", notice observable changes
+*
+* Note the special code to only notice "range" changes.
+*/
+
+#####GDescription:
+Set cut level "v". If the player race can't be cut then the time is
+forced to 0. A value > 1000 is a mortal wound. A value > 200 is a deep
+gash. A value > 100 is a severe cut. A value > 50 is a nasty cut. A
+value > 25 is a bad cut. A value > 10 is a light cut. A value > 0 is a
+graze. If the cut level has increased, a message is printed. There is
+a small chance of stun level in 1000, or a 1 in 16 chance of scarring
+which decreases charisma for a while.
+
+#####GParameters:
+>v is the cut level.
+
+----------------------------------------------------------------------
+
+#####RFunction: set_food
+
+#####GDeclaration: bool set_food(int v);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Set "p_ptr->food", notice observable changes
+*
+* The "p_ptr->food" variable can get as large as 20000, allowing the
+* addition of the most "filling" item, Elvish Waybread, which adds
+* 7500 food units, without overflowing the 32767 maximum limit.
+*
+* Perhaps we should disturb the player with various messages,
+* especially messages about hunger status changes. XXX XXX XXX
+*
+* Digestion of food is handled in "dungeon.c", in which, normally,
+* the player digests about 20 food units per 100 game turns, more
+* when "fast", more when "regenerating", less with "slow digestion",
+* but when the player is "gorged", he digests 100 food units per 10
+* game turns, or a full 1000 food units per 100 game turns.
+*
+* Note that the player's speed is reduced by 10 units while gorged,
+* so if the player eats a single food ration (5000 food units) when
+* full (15000 food units), he will be gorged for (5000/100)*10 = 500
+* game turns, or 500/(100/5) = 25 player turns (if nothing else is
+* affecting the player speed).
+*/
+
+#####GDescription:
+Set hunger level "v". A value < 500 is fainting. A value < 1000 is
+weak. A value < 2000 is weak. A value < 10000 is full. A value
+< 15000 is bloated. A value < 20000 is gorged. If one of these
+levels is crossed a message is printed.
+
+#####GParameters:
+>v is the hunger level (must be between 0 and 20000).
+
+----------------------------------------------------------------------
+
+#####RFunction: check_experience
+
+#####GDeclaration: void check_experience(void);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Advance experience levels and print experience
+*/
+
+#####GDescription:
+Check if player experience level has changed. If a player has achieved
+a level for the first time, give reward or corruption (1 chance in 3)
+if they apply, and increase skill points.
+
+----------------------------------------------------------------------
+
+#####RFunction: check_experience_obj
+
+#####GDeclaration: void check_experience_obj(object_type *o_ptr);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Advance experience levels and print experience
+*/
+
+#####GDescription:
+Check if object "o_ptr" experience level has changed. If an object has
+achieved a level for the first time, apply gains.
+
+#####GParameters:
+>o_ptr is the pointer to the object gaining experience.
+
+----------------------------------------------------------------------
+
+#####RFunction: gain_exp
+
+#####GDeclaration: void gain_exp(s32b amount);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Gain experience (share it to objects if needed)
+*/
+
+#####GDescription:
+Gain "amount" of experience. Count the number of objects which will
+gain experience. The objects share equally 2/3 of "amount". Give
+corruption if it applies. Gain experience. If experience is less
+than maximum, then increase maximum experience by 20% of "amount".
+Check for level change and print experience (check_experience).
+
+#####GParameters:
+>amount is the amount of experience to share.
+
+----------------------------------------------------------------------
+
+#####RFunction: lose_exp
+
+#####GDeclaration: void lose_exp(s32b amount);
+
+#####GFile: xtra2.c
+
+#####GComment:
+/*
+* Lose experience
+*/
+
+#####GDescription:
+Decrease experience by "amount". Experience can not fall below zero.
+Check for level change and print experience (check_experience).
+
+#####GParameters:
+>amount is the amount of experience to lose.
+
+----------------------------------------------------------------------
+
+
+Back to the *****lua.hlp*0[lua help index] .
+
+
+ [[[[[gThis file by Chris Hadgis]
+
+
diff --git a/lib/help/lua_pow.txt b/lib/help/lua_pow.txt
new file mode 100644
index 00000000..c221a664
--- /dev/null
+++ b/lib/help/lua_pow.txt
@@ -0,0 +1,266 @@
+|||||oy
+#####R /----------------------------------------\
+#####R < Adding new racial powers >
+#####R \----------------------------------------/
+
+#####R=== Introduction ===
+
+You *must* download and install my lua example files from
+[[[[[Ghttp://www.moppy.co.uk/angband.htm]. And also you should read the
+*****lua_intr.txt*0[scripting introduction] file if you haven't already done
+so.
+
+The (commented) accompanying script file for this tutorial is
+lib\scpt\pheonix.lua. Open it in your text editor!
+
+#####R=== The Racial Power ===
+
+Let's start with something simple. Let's say you wanted your new race (let's
+call it "Pheonix") to be able to cast fire balls as their racial power.
+
+#####R=== Starting off ===
+
+If you have a look at pheonix.lua you'll note the first lines are comments.
+I'll talk a bit more about comments later, but it's worth pointing out that any
+lines of text preceded by a double hyphen ([[[[[B--]) will be ignored by the
+scripting engine, and are therefore comments.
+After the comments, the first 10 lines of code are as follows:
+
+#####BPHEONIX_POWER = add_power
+#####B{
+#####B ["name"] = "Fire Breath",
+#####B ["desc"] = "You are able to cast fireballs",
+#####B ["desc_get"] = "Your beak glows red",
+#####B ["desc_lose"] = "Your beak goes cold again",
+#####B ["level"] = 10,
+#####B ["cost"] = 11,
+#####B ["stat"] = A_INT,
+#####B ["fail"] = 14,
+
+So, [[[[[BPHEONIX_POWER = add_power] is registering our power. We're giving it
+a name (PHEONIX_POWER) and saying that it is defined by calling the special
+function [[[[[Badd_power]. This special function is only used to define lua-
+scripted
+powers, and has attributes which are identified by their inclusion in square
+brackets.
+Note the following:
+- Lua is case sensitive. The name of our power is a constant, will never change
+so that's been named with capitals. variables and functions are named all in
+lower case. Technically speaking, Lua does not support constants, but we can
+emulate them by variables in this way. They don't have to be capitalised, but
+if you capitalise all your constants, it makes things easier to read, and
+you'll be following normal programming protocol.
+- There are no spaces in the name of the power. Use an underscore for spaces
+if you need to improve legibility.
+
+[[[[[B"name" = "Fire Breath",] This is the name of the power as it is
+displayed in the
+U menu, and as you will define it in p_info.txt (more about that later).
+
+[[[[[B"desc" = "You are able to cast fireballs",] This is what would
+appear in the
+information display list if you drank a potion of self knowledge or
+similar.
+
+[[[[[B"desc_get" = "Your beak glows red",] This is the information displayed
+when you
+gain this power.
+
+[[[[[B"desc_lose" = "Your beak goes cold again",] This is the information
+displayed when
+you lose this power. Eg After a mutation/corruption.
+
+[[[[[B"level" = 10,] Character level which must be gained in order to use
+power,
+
+[[[[[B"cost" = 11,] Amount of mana to cast this power.
+
+[[[[[B"stat" = A_INT,] stat which will define whether it works or not,
+
+[[[[[B"fail" = 14,] how high that stat must be.
+
+So our Pheonix will be able to cast PHEONIX_POWER from clvl 10, at a cost of
+11 mana, providing that the player's intelligence is 14 and upwards.
+
+#####R=== The function ===
+
+The next section is a lot longer, so we'll look at it line by line. I'll
+strip the comments for the purpose of this helpfile.
+
+#####B["power"] = function()
+#####B local ret, dir, damage
+#####B ret, dir = get_aim_dir();
+#####B if (ret == FALSE) then
+#####B return
+#####B end
+#####B damage = player.lev*3
+#####B msg_print("You breathe fire.")
+#####B fire_ball(GF_FIRE, dir, damage, 3)
+#####Bend,
+
+The [[[[[B"power"] bit is what actually happens when the player accesses this
+power
+from their 'U' menu. Every function must start with the word [[[[[Bfunction].
+Normally, we'd also declare its name at this point, but as this is contained
+within the [[[[[Badd_power] function, we just use the word [[[[[Bfunction] The
+empty
+brackets after this denote that no arguments are passed to the function.
+Lets look at the next line.
+
+#####B local ret, dir, damage
+
+The [[[[[Blocal] bit is saying that we're going to declare some local
+variables. That
+is, that there will be three variables used in this function , that apply
+exclusively to this function, and to no others. Global variables are
+variables that apply to the whole script, in multiple functions. The three
+variables will be called [[[[[Bret] (return), [[[[[Bdir] (direction), and
+[[[[[Bdamage]. They will be used to determine the direction the ball
+will fire in, and how much damage it will do. We'll see them in use when we add
+the third line:
+
+#####B ret, dir = get_aim_dir();
+
+here we're saying that the variables will take their value from the result
+of a function. The program performs the function [[[[[Bget_aim_dir] which
+essentially asks the player to choose a direction or pick a target.
+[[[[[Bget_aim_dir]
+assigns the value [[[[[Bret] to either TRUE (if the player correctly selected a
+direction) or FALSE (if the player failed to do so (maybe they changed their
+mind, or hit the wrong key!)). The value [[[[[Bdir] is the direction which was
+selected (or the path to the target if a target was selected). OK so let's add
+the next line:
+
+#####B if (ret == FALSE) then return end
+
+This introduces another fundamental scripting concept - [[[[[Bif] statements.
+They work just as you would expect them too. You say "if a certain condition is
+met, then do the following things."
+So in this function we're saying, "if the value of [[[[[Bret] is FALSE then
+[[[[[Breturn]."
+As I mentioned above, [[[[[Bret] is false if the player aborted (either
+deliberately or accidentally) the spell casting whilst choosing a direction
+for the spell. The double equals sign are used to mean "is equal to" as a
+single equals sign is used for defining variables remember? A single equals
+sign is more of a "let x be equal to y" thing.
+[[[[[Breturn] means stop the current function. And [[[[[Bend] signifies the
+close of the [[[[[Bif]
+statement. Every [[[[[Bif] statement must begin with an [[[[[Bif] and finish
+with an [[[[[Bend].
+So, what our [[[[[Bif] statement is saying is; "if the player failed to specify
+a direction or target for the spell, stop the function here."
+If the player has correctly specified a direction/target, the function
+continues to the next line:
+
+#####B damage = player.lev*3
+
+Here we're saying that the variable [[[[[Bdamage] has a value equal to the
+players current character level, multiplied by 3.
+
+#####B msg_print("You breathe fire.")
+
+Fairly easy to see what this does - displays the message "You breathe fire."
+I could have put anything there obviously, like [[[[[Bmsg_print("You open]
+[[[[[Byour mouth and everyone falls over with the smell of hot curry")] or
+some other such rubbish. But note that the message is enclosed within double
+quotes. The quotes aren't displayed in the message on screen, but signify the
+start and end of the message.
+
+#####B fire_ball(GF_FIRE, dir, damage, 3)
+
+This is the line that casts the spell. it says execute the function
+[[[[[Bfire_ball]. Now, this doesn't mean a fireball, it means fire a ball.
+There's an important distinction there! All it knows it is doing is firing a
+ball, it doesn't know what kind of ball, or where, or how big, or how much
+damage.
+The [[[[[BGF_FIRE,] bit is what tell us it is a fire ball. If it was
+[[[[[BGF_COLD,]
+ we'd have a cold ball, or [[[[[BGF_CHAOS,] it would be a chaos ball and
+so on and so on. A full list of those types can be found in *****lua_gf.txt*0[lua_gf.txt].
+[[[[[B dir,] is the direction, from the [[[[[Bget_aim_dir()] bit.
+[[[[[B damage,] is the damage. As we've already said, this will be clvl*3.
+[[[[[B 3)] is the radius.
+and finally...
+
+#####B end,
+#####B}
+
+[[[[[Bend,] tells it the function has ended. Every function must finish with
+[[[[[Bend].
+You should have spotted that after the end of each attribute is a comma. Make
+sure you include this, and don't forget the braces at the very very end to
+close the [[[[[Badd_power] function.
+
+#####R=== Finishing the LUA file ===
+
+Save this as a text file 'pheonix.lua' Put it into the lib\scrpt directory
+of ToME, and open the init.lua file which you'll find in the same
+directory.
+
+Add the following line and resave the init.lua file.
+
+#####Btome_dofile("pheonix.lua")
+
+This ensures that ToME loads your file on start-up. Because you've installed
+the example scripts, this has all been done for you.
+
+#####R=== A quick word about comments ===
+
+One of the reasons Angband has so many variants is because the source code is
+clearly commented. Almost every line of code has an accompanying comment,
+explaining what that line does. It's good practice to add comments to any code
+or script you write. It's helpful to others who are learning, anyone who takes
+over your project, and also to yourself when you come back in a month's time
+and can't remember what you did! So comment your code clearly, and well!
+
+You can also add multi line comments which should be enclosed by [[[[[B--[[]
+and
+#####B]]
+
+#####R=== Tying it all together ===
+
+You'll now need to link this 'U' power to the pheonix race via the
+p_info.txt file. Simply add a line within the class definition file that has
+the format [[[[[BR:Z:<name>]the name of the power as it appears in the
+[[[[[B"name"]
+section we did right at the beginning, remember? Seeing as there is no pheonix
+race, I've gone ahead and made one up as a demonstration. If you've downloaded
+and installed the example files from [[[[[Ghttp://www.moppy.co.uk/angband.htm/]
+then
+you'll notice you already have the pheonix race available. Printed below is
+the p_info.txt entry for it.
+
+
+#####BR:N:23:Pheonix
+#####BR:D:Born from flame, these powerful bird like creatures gain fire related
+#####BR:D:abilities as they grow.
+#####BR:S:1:0:2:1:-3:2:-5
+#####BR:K:-8:15:20:-10:5:-1:-5:-5
+#####BR:P:8:130:5:210
+#####BR:M:255:70:2:1:20:5:2:1:18:3
+#####BR:E:1:1:1:2:1:1
+#####BR:C:Warrior | Mage | Priest | Rogue | Ranger | Paladin | Blade |
+#####BR:C:Warlock | Chaos-Warrior | Monk | Mindcrafter | High-Mage |
+#####BR:C:BeastMaster | Alchemist | Power-Mage | Runecrafter |
+#####BR:C:Sorceror | Archer | Illusionist | Druid | Necromancer | Black-Knight
+|
+#####BR:C:Daemonologist | Weaponmaster | Summoner |
+#####BR:Z:Fire Ball
+#####BR:R:1:0
+#####BR:F:RES_FIRE | FEATHER |
+
+Note the [[[[[BR:Z:] line.
+
+If all is well, you should be able to start ToME now and breathe fire! Once
+you get to lvl 10 anyhow. Don't forget, this is the kind of thing wizard mode
+was invented for! Ctrl-A and confirm at the prompt, and then 'e' will allow
+you to alter stats, including experience. Approx 1000 exp should do to get you
+to clvl 10.
+
+Ready for more? How about adding a new *****lua_skil.txt*0[skill] ?
+
+ [[[[[gThis file by fearoffours (fearoffours@moppy.co.uk)]
+
+
+
+
diff --git a/lib/help/lua_ques.txt b/lib/help/lua_ques.txt
new file mode 100644
index 00000000..1d4b9c65
--- /dev/null
+++ b/lib/help/lua_ques.txt
@@ -0,0 +1,299 @@
+|||||oy
+#####R /----------------------------------------\
+#####R < Adding a new quest >
+#####R \----------------------------------------/
+
+#####R=== Introduction ===
+
+Adding a quest involves a bit more work, and there is, in some ways, rather
+more potential for things to go wrong! But it's a great way of showing just
+WHAT can be done with lua scripting. It proves just how much a lua patch can
+change the overall feel of the game. And it will give you a much better idea of
+how lua interfaces with the game source. You should have read the
+*****lua_intr.txt*0[scripting introduction], *****lua_pow.txt*0[racial power tutorial]
+and *****lua_skil.txt*0[adding new skills tutorial] before going much
+further. All of the above files contain some fairly fundamental information
+which you will find necessary for full understanding of this file.
+
+The script we're looking at is going to create a quest entrance in the middle
+of Bree. Entering the quest you see a little girl who has had her necklace
+stolen. Your job is to travel down a corridor, killing some monsters on the
+way, pick up the amulet and return it to the girl. Once done, she'll reveal the
+stairs back to Bree, and give you a (randomly generated) ring. If you feel the
+monsters are too hard, the only thing to do is talk to the little girl who will
+reveal the stairs again, failing the quest for you, and also block off the
+entrance to the amulet so that you can't cheat and make off with the amulet!
+
+#####R=== Getting started ===
+
+Open amulet.lua (you have downloaded the example scripts from
+[[[[[Ghttp://www.moppy.co.uk/angband.htm], haven't you?). The first thing you
+should see is that yet again we're calling a function that takes its arguments
+from a table, making it easy to read what's going on in the script.
+
+This time our function is add_quest and we have the following keys and values:
+
+#####B["global"] = "AMULET_QUEST",
+#####B["name"] = "Hannahs lost amulet",
+#####B["desc"] = {
+#####B "Retrieve an amulet for Hannah Cooke. It's guarded!"
+#####B },
+#####B["level"] = 5,
+#####B["hooks"] = {
+
+[[[[[B"global" = ] is a constant that we set when we refer to this quest in
+various places...
+[[[[[B"name" = ] Obviously a long name for the quest. This will appear in the
+quest screen (Ctrl-Q) and we may use in some map files too.
+[[[[[B"desc" = ] This is a long description for the quest. Again this is what
+will appear in the quest screen, and each line should be not more than 80
+characters.
+[[[[[B"level" = ] This is a rough indicator of how hard the quest is, and again,
+appears in the quest screen
+[[[[[B"hooks" = ] This is the real 'meat' of the quest. Like the [[[[[B"spell_list"] key
+in the [[[[[Badd_magic] function, this is another sub-table.
+
+To understand fully the structure of the "hooks" key it's worth taking a bit of
+a detour at this point and discussing how the scripting interface works in
+general.
+
+#####R=== How the scripts work (ish) ===
+
+Essentially there's a list of events that happen in the game. As each of these
+events happen they run a check to see if there's any functions that they need
+to run at that event. When we ran the add_mkey part of adding a new skill
+power, we essentially said "when the 'm' key is pressed in the game, perform
+the [[[[[Bexecute_magic(constructor_powers)] function". Likewise we did a similar
+thing with adding the racial power, only we hooked onto the pressing of the
+'U' key.
+
+All of this was partly hidden because of the way that the [[[[[Badd_magic] and
+[[[[[Badd_power] functions work. But here in the [[[[[Badd_quest] function it's a bit more
+specific. We are going to specify what events we're going to hook onto, and
+what functions we want to trigger at that event.
+
+A full list of hooks can be found in the source-file util.pkg.
+
+#####R=== The hooks ===
+
+#####B[HOOK_BIRTH_OBJECTS] = function()
+#####B quest(AMULET_QUEST).status = QUEST_STATUS_TAKEN
+#####Bend,
+
+So here we are with our first hook. We've declared that we're adding it to the
+birth of your character. That is, the function will be called when you create
+your character. And what we're doing here is automatically declaring the quest
+as being taken, like the Dol Guldur quest is. Each quest has 7 different
+statuses:
+
+[[[[[BQUEST_STATUS_IGNORED -1 ] This is unused, but the quest is
+ignored (will not be taken and has not been taken).
+[[[[[BQUEST_STATUS_UNTAKEN 0 ] The quest has not been accepted yet
+[[[[[BQUEST_STATUS_TAKEN 1 ] You have accepted the quest
+[[[[[BQUEST_STATUS_COMPLETED 2 ] You have completed the quest
+successfully but not been rewarded for it
+[[[[[BQUEST_STATUS_REWARDED 3 ] You've completed and rewarded the quest
+[[[[[BQUEST_STATUS_FAILED 4 ] You've failed the quest
+[[[[[BQUEST_STATUS_FINISHED 5 ] The quest is completely finished
+successfully.
+[[[[[BQUEST_STATUS_FAILED_DONE 6 ] The quest is completely finished
+unsuccessfully.
+
+You see that we've used the constant we defined in the "global" section is
+passed as an argument to [[[[[Bquest.status].
+
+Next hook then:
+
+#####B[HOOK_GEN_QUEST] = function()
+#####B if (player.inside_quest ~= AMULET_QUEST) then
+#####B return FALSE
+#####B else
+#####B load_map("amulet.map", 2, 2)
+#####B return TRUE
+#####B end
+#####Bend,
+
+Ok, we're hooking onto the generation of the quest here. This is specifically
+triggered in this instance by going down the quest entrance stairs in Bree.
+Once you've gone down the stairs, you are technically inside the quest, which
+means we can say if the person is not inside the amulet quest, then ignore this
+function, otherwise load the file 'amulet.map' at co-ordinates x=2 y=2. You'll
+find the amulet.map file in the edit directory, make sure you check it out. The
+syntax for map files is fairly simple, though I might get round to writing a
+tutorial on them some day! In the mean time holler for me at the usual email
+address if you're unsure.
+
+#####B[HOOK_FEELING] = function()
+#####B if (player.inside_quest ~= AMULET_QUEST) then
+#####B return FALSE
+#####B else
+#####B cmsg_print(TERM_L_BLUE, "Hannah speaks to you:")
+#####B cmsg_print(TERM_YELLOW, "'Some nasty monsters stole my
+#####B favourite necklace.'")
+#####B cmsg_print(TERM_YELLOW, "'It's hidden at the back of that
+#####B corridor! Please fetch it for me'")
+#####B return TRUE
+#####B end
+#####Bend,
+
+We're moving into some rather more obvious territory here, and getting into the
+meat of the quest. The [[[[[BHOOK_FEELING] is triggered at the point when the level
+feeling appears. It's important that this is run only if the player is inside
+the amulet quest, as otherwise it will trigger EVERY time a level feeling
+occurs, when you go down a level in the barrow-downs, whenever! Returning TRUE
+will replace the level feeling with what's above, returning FALSE will still
+perform the function but will amend the normal level feeling - so here if we'd
+returned false we'd still get our custom messages, but they'd follow with
+'looks like a typical quest level'. Of course returning false may cause you
+other problems (see end of this file!) depending on what else you have in your
+function.
+
+#####B[HOOK_GIVE] = function(m_idx, item)
+
+#####B m_ptr = monster(m_idx)
+#####B o_ptr = get_object(item)
+
+#####B if (m_ptr.r_idx == test_monster_name("Hannah Cooke, a little girl"))
+#####B and (o_ptr.tval == TV_AMULET) and (o_ptr.sval == 2) then
+
+#####B cmsg_print(TERM_YELLOW, "'Thank-you!'")
+
+#####B inven_item_increase(item, -1)
+#####B inven_item_optimize(item)
+
+#####B quest(AMULET_QUEST).status = QUEST_STATUS_COMPLETED
+
+#####B cave_set_feat(7, 6, 6)
+
+#####B cmsg_print(TERM_YELLOW, "'Here, take this pretty ring I found
+#####B as a token of gratitude!'")
+#####B random_type = randint(57)
+#####B reward = create_object(TV_RING, random_type)
+#####B drop_near(reward, -1, py, px)
+#####B quest(AMULET_QUEST).status = QUEST_STATUS_REWARDED
+#####B return TRUE
+#####B else
+#####B return FALSE
+#####B end
+#####Bend,
+
+This is a fairly long function, but don't be intimidated. It's not really
+difficult to understand. As you can see we're hooking into the giving of an
+object to a monster (the 'y' key). Because of this, the function takes two
+arguments - [[[[[Bm_idx] (the monster that you're giving to) and [[[[[Bitem] (the item that
+you're giving).
+
+We then make it possible to work with the monster and item variables by
+referencing them to two functions which identify them from the edit files:
+[[[[[Bmonster()] and [[[[[Bget_object()]. This enables us to now say, 'if the name of the
+monster is "Hannah Cooke, a little girl" and the type of item is an amulet and
+that amulet is an amulet of adornment, then carry out the following commands'.
+
+We then say call the function [[[[[Binven_item_increase()] which places an object in
+the inventory. It takes two arguments, the first being what object to put in
+the inventory and the second being how many of that type of objects to put in
+the inventory. You can see that by placing -1 as the second argument it fairly
+obviously subtracts that item from the inventory. The [[[[[Binven_item_optimize()]
+function checks that there are no empty inventory slots, and if there are,
+erases them.
+
+The quest is then completed, and the stairs are revealed using the
+[[[[[Bcave_set_feat()] function. This function takes three arguments, the first is the
+x co-ordinate of the cave square you wish to change (counted from top left) the
+second is the y co-ordinate, and the third is the index number of the feature
+you wish the square to become as defined in f_info.txt.
+
+We then set about rewarding the player. As you can see we call [[[[[Bcreate_object()]
+which takes two variables: the first is the type of object (these are all
+listed in object.pkg) and the second is the sub-type of that object. I searched
+k_info.txt to see how many different types of ring there were (57) and used a
+randomly selected number with a maximum value of 57 as that specific sub-type.
+
+We then drop the object (although it's been created, it has only been created
+in the game's memory, it's nowhere that the player can interact with it until
+we drop it). The [[[[[Bdrop_near()] function takes 3 variables, the first being the
+object that you wish to drop, the second being the chance that it disappears
+(like an arrow, or mimicked creature) on drop. If you set it to -1, it won't
+ever disappear. The last two are the co-ordinates at which the object will be
+dropped. py and px are the global variables defined by where the player is
+standing, so in this case it will drop under the player. You could do
+[[[[[Binven_item_increase(reward, 1)] if you wanted, but I wanted to show a variety of
+ways of handling objects.
+
+OK, let's take a look at the next hook:
+
+#####B[HOOK_CHAT] = function(m_idx)
+#####B m_ptr = monster(m_idx)
+#####B if (m_ptr.r_idx == test_monster_name("Hannah Cooke, a little girl")) then
+#####B if (quest(AMULET_QUEST).status == QUEST_STATUS_REWARDED) then
+#####B cmsg_print(TERM_YELLOW, "'Bye!'")
+#####B else
+#####B cmsg_print(TERM_YELLOW, "'Are the monsters too tough?
+#####B Do you want to leave?'")
+#####B if (get_check("Really leave and fail the quest?") ==
+#####B FALSE)
+#####B then
+#####B cmsg_print(TERM_YELLOW, "'Go and get my
+#####B amulet then!'")
+#####B else
+#####B cmsg_print(TERM_YELLOW, "'Awww. Never
+#####B mind. It was only a bit of rabbits foot'")
+#####B quest(AMULET_QUEST).status =
+#####B QUEST_STATUS_FAILED
+#####B cave_set_feat(7, 6, 6)
+#####B cave_set_feat(12, 5, 60)
+#####B end
+#####B end
+#####B return TRUE
+#####B end
+#####B return FALSE
+#####Bend,
+
+This only looks complicated because of the nested 'if' statements. It's easy to
+lose your way when doing this kind of thing, always make sure you close all the
+statements and put the returns in the right place. [[[[[BHOOK_CHAT] functions have one
+argument - the monster you are chatting to. As you can see, we perform a check
+to make sure it's the right monster and then away we go.... If the player wants
+to leave the quest without completion they talk to Hannah, who gives them a
+chance to change their mind! If the player asks to leave the entrance to the
+corridor is blocked off (the second cave_set_feat()) so that the user can't
+then go and get the amulet. Gumband or Zangband players may at this point think
+they've lost out on the rabbits foot of burglary! (they haven't though as it
+doesn't exist in ToME).
+
+#####B[HOOK_CHAR_DUMP] = function()
+#####B if (quest(AMULET_QUEST).status == QUEST_STATUS_FAILED) then
+#####B print_hook("\n You chickened out of rescuing a necklace and
+#####B made a little girl sad. ")
+#####B elseif (quest(AMULET_QUEST).status == QUEST_STATUS_COMPLETED) or
+#####B (quest(AMULET_QUEST).status == QUEST_STATUS_REWARDED) or
+#####B (quest(AMULET_QUEST).status == QUEST_STATUS_FINISHED) then
+#####B print_hook("\n You rescued little Hannah Cooke's necklace from
+#####B the nasty monsters ")
+#####B end
+#####B return FALSE
+#####Bend,
+
+This quite simply and obviously prints an appropriate line in the character
+dump based on the status of the quest. The [[[[[B\n] bit ensures the text goes on a
+new line, so make sure you include it! Also you should return FALSE as
+returning TRUE will stop executing all the other character dump lines (and you
+may get other quests not having their lines printed).
+
+=== A word about returning TRUE and FALSE ===
+
+As I mentioned above, you need to be careful what you return when dealing with
+HOOKS as you can mess up the game a bit. Bear in mind that if you add a
+function to [[[[[BHOOK_GEN_QUEST], every time a quest is generated, that function will
+run. If you return TRUE, then no further functions attached to that hook will
+run. If you return FALSE, it continues processing functions on that hook.
+
+That is pretty much it. Do take a look at the other included scripts that I
+haven't gone into any detail about in the files, as you'll pick up some useful
+techniques there too. Especially worthy of note is the hina.lua file which uses
+hooks outside of the quest structure and also global variables and variables in
+a table. If you have any questions, let me know at the email addy below.
+
+Back to the *****lua.hlp*0[lua help index] .
+
+ [[[[[gThis file by fearoffours (fearoffours@moppy.co.uk)]
diff --git a/lib/help/lua_skil.txt b/lib/help/lua_skil.txt
new file mode 100644
index 00000000..87385e5d
--- /dev/null
+++ b/lib/help/lua_skil.txt
@@ -0,0 +1,342 @@
+|||||oy
+#####R /----------------------------------------\
+#####R < Adding new skill-based powers >
+#####R \----------------------------------------/
+
+#####R=== Introduction ===
+
+This is very much in the same vein as adding a racial/extra power, but has to
+be tied into skills, and we're defining more than one spell at once. You should
+have read the *****lua_intr.txt*0[scripting introduction] and
+*****lua_pow.txt*0[racial power tutorial] before going much further. Both of the above files
+contain some fairly fundamental information which you will find necessary for
+full understanding of this file.
+
+#####R=== Getting started ===
+
+Open construc.lua (you have downloaded the example scripts from
+[[[[[Ghttp://www.moppy.co.uk/angband.htm], haven't you?). The idea behind this
+script is that it adds a skill which affects you ability to build/knock down
+stuff. It treats the equivalent of stone-to-mud and trap-door destruction
+spells as if they were "building skills". It also adds quite a few high-level
+'spells' which do funky things like carving out corridors and chambers in a
+single turn, and building doors and stuff. Just think of it as if the person
+who has plenty of skills in this area would be a builder-type with lots of
+strength and constitution...
+
+In order to add these powers we're going to edit the s_info.txt file which
+lives in the edit folder, and add a new skill, underneath the 'misc' tree,
+called construction. The powers will then be accessed through the 'm' menu, in
+a similar way to mindcraft or alchemy skills or such. (That is, no books are
+needed to cast them, as we're treating them as a craft that has been learnt,
+rather than spells.) Our fist line of the script file reads:
+
+#####BSKILL_CONSTRUCT = 57
+
+This merely links the skill index that we'll be defining in s_info.txt to this
+file. We'll come back to this at the end of the tutorial.
+
+#####Bconstructor_powers = add_magic
+
+In a similar way to the [[[[[Badd_power] function we called when we added the
+Phoenix racial ability, this line calls a special function which we use to
+define new skills. It follows a very specific, but easy to understand form. It
+starts with a brace, which indicates the add_magic function will be storing
+these values in a table. Don't worry about this too much, but understand that a
+table starts and ends with braces [[[[[B{] and [[[[[B}] respectively. Each key
+(or field name) takes the format [[[[[B"key" = value,] (the comma is
+important!).
+
+#####B ["fail"] = function()
+#####B msg_print("You decide now is a good time for a cuppa")
+#####B end,
+#####B ["stat"] = A_STR,
+#####B ["get_level"] = function()
+#####B return get_skill_scale(SKILL_CONSTRUCT, 50)
+#####B end,
+#####B ["spell_list"] =
+
+[[[[[B"fail"] is a function that is called whenever you ##fail to cast the
+spells##. Here it does nothing spectacular.
+[[[[[B"stat"] defines the stat used to cast the spells. Here it is strength.
+Any other stat can be used, prefix it with [[[[[BA_].
+[[[[[B"get_level"] is used to determine the level of the spell. It's associated
+with spells that increase in power the more points that are invested in the
+associated skill. I know that's not terribly clear, I'll come back to it in a
+moment.
+[[[[[B"spell_list"] is just that, a list of all the spells.
+Each of these four properties within the table must end with a comma. If a
+function is defined in the property itself then we add the comma after the
+closing [[[[[Bend]. Again compare with construct.lua to see it. Any line NOT
+ending with a comma will cause a lua error on startup, probably of the type
+[[[[[V'}' expected to close '{' at line <whatever>.]
+
+#####R=== The spell list ===
+
+Each spell, within the [[[[[B"spell_list"] key has its own set of properties
+that we need to define from a sub-table so we open another set of braces to
+start the spell list, and then a third set of braces to start the first spell.
+So with all this, our first spell looks like:
+
+#####B ["spell_list"] =
+#####B {
+#####B {
+#####B ["name"] =
+#####B ["desc"] =
+#####B ["mana"] =
+#####B ["level"] =
+#####B ["fail"] =
+#####B ["spell"] =
+#####B ["info"] =
+#####B },
+
+[[[[[B"name"] is, as you would expect, the name of the spell, as you want it to
+appear in the spell list when choosing a spell. The maximum number of
+characters for this is 29.
+[[[[[B"desc"] is the description received when you hit the capital letter of
+that spell in the menu. (i.e., press 'a' to cast the first spell, but press 'A'
+to receive info about the first spell.
+[[[[[B"mana"] is the amount of mana required to cast the spell.
+[[[[[B"level"] is the level required to use that spell (that's level of the (in
+this case construction) skill, not character level!).
+[[[[[B"fail"] is base fail rate.
+[[[[[B"spell"] is the function that is executed when the spell is cast. Note
+that it MUST take the form [[[[[Bfunction() blah end] even if you're calling
+a C function directly. If you have a look at the end of the file, you'll see
+the "rebuild dungeon" spell which is identical to the "alter_reality" spell.
+However, rather than reading [[[[[B"spell" = alter_reality()], it reads:
+
+#####B["spell"] = function()
+#####B alter_reality()
+#####Bend,
+
+which appears to be a long way round to do the same thing, but this is how it
+must be done.
+
+In a similar way, the [[[[[B"info"] key must begin with a [[[[[Bfunction()]
+and return the value of what is to be displayed alongside the spell name,
+level and mana in the spell list. The maximum number of characters that can be
+displayed here is dependent on the width of the user's screen, but try to keep
+it under 12 if you can, as this will fit in a standard 80x24 terminal screen.
+The first character will need to be a space otherwise you'll have the info line
+squashed right up against the fail rate and it will look odd. If you wish to
+have this part blank in the spell list, you still need to return a value, so
+just a single space will do : [[[[[Breturn " "]
+
+All of these keys are repeated for each spell, with each spell in its own
+table (therefore, it's own set of braces). Again, check the lua file for
+clarification.
+
+When entering the spells in the "spell_list", you must take care to specify
+them in the order which they are gained, otherwise they display incorrectly in
+the spell list.
+
+You should by now be experienced enough to understand most of what's going on
+in the actual spell functions (especially if you dig around in the source a
+bit, and check out Chris Hadgis' excellent *****lua_spel.txt*0[spell.pkg] helper
+files. I'm not going to go through the whole file line by line, as this is
+something you should do yourself, figuring out what's going on. I'm going to
+examine a few of the things we haven't covered before though, so pay attention.
+
+#####R=== The get_level() function ===
+
+Probably one of the most important functions that you see reappearing in the
+file is the [[[[[Bget_level()] function. All this does is return the numerical
+value of the power that is given as the first argument. So [[[[[Bget_level]
+[[[[[B(constructor_power)] will return the current level of the constructor power.
+Given that the level of this is taken directly from the construction skill, (we
+defined that in the [[[[[B"get_level"] key, by saying [[[[[Bget_skill_scale]
+[[[[[B(SKILL_CONSTRUCT, 50)] ) it will return the value of your construction skill.
+[[[[[Bconstructor_power] is the name of the whole power, we named it thus on
+the second line of the script!
+
+[[[[[Bget_level] takes the following arguments: [[[[[Bget_level(power, max, ]
+[[[[[Bmin)]. The power is obviously which power we're taking the value from, and the
+max and min allow you to define boundaries for the spell. For instance the
+current maximum value that [[[[[Bget_level(constructor_power)] can return is
+50, as that is the maximum number of skill points you can have in that skill.
+If you were using this as the basis for the damage of a low-level bolt spell,
+you might decide that having a damage of 50 would be too much (unlikely, but
+still possible). You could therefore define a maximum value of 20 so that when
+the value of the construction skill was over 50, the maximum value for
+damage of that spell would be 20. To achieve this you'd have:
+[[[[[Bget_level(constructor_power, 20)]. In a similar way, you can force the
+minimum value of the spell to be higher than the actual construction skill
+level, with a [[[[[Bget_level(constructor_power, 50, 15)]. This would be useful
+say for spells that you wanted to be available when the construction skill
+level reaches 10, but for whom you wanted a (for example) base damage of 15
+right from the word go. These re-scale values rather than capping them!
+
+You can leave out the minimum value as I have done above. You can also leave
+the maximum value out (it will default to 50). If you want to specify a minimum
+value though, you MUST specify a maximum value as well.
+
+As you have hopefully been able to tell, the [[[[[Bget_level()] function
+enables us to have spells that increase in usefulness as you gain levels. Let's
+take the "Dismantle" spell. The function in the [[[[[B"spell"] key is as
+follows:
+
+#####Bfunction()
+#####B local ret, dir, dam
+
+#####B if (get_level(constructor_powers, 50) >= 11) then
+#####B ret, dir = get_aim_dir();
+#####B if (ret == FALSE) then return end
+#####B fire_beam(GF_KILL_TRAP, dir, 1)
+#####B else
+#####B fire_ball(GF_KILL_TRAP, 0, 1, 1)
+#####B end
+#####Bend,
+
+The [[[[[Bif] statement is obviously what really interests us here. You'll
+notice that this has the amendment of an [[[[[Belse] clause, which the [[[[[Bif]
+statement we used in the previous tutorial did not. As you would expect, if the
+condition on the first line of this statement is met, then the instructions
+immediately below it are carried out. If the condition is not met, then the
+statements that follow the [[[[[Belse] are executed.
+
+Coming back to the [[[[[Bget_level] function, we learnt from above, that the
+[[[[[Bget_level] part of this function translates as, "if the value of the
+construction_power level (which happens to be identical to the construction
+skill level) is greater than or equal to 11, cast a beam of trap disarming in
+the specified direction. (The first part of this is all straightforward,
+getting a direction, and cancelling correctly if the player presses 'ESC'.)
+Otherwise, cast a ball of trap disarming with a radius of one, centred on the
+player."
+
+In the same way, as you look at the construc.lua file, you will see that
+[[[[[Bget_level()] is used many times in this way, to increase the power of
+detection spells, to change bolt spells to ball spells, to keep a constantly
+increasing damage going, and so on.
+
+#####R=== Elseif's and things ===
+
+If you want to provide more than one alternative condition, in an
+[[[[[Bif-then-else] statement, you can use [[[[[Belseif]s which do what you
+might expect. Take a look at the first spell, "Survey area", for an example of
+this:
+
+#####Bif (get_level(constructor_powers, 50) >= 28) then
+#####B wiz_lite()
+#####Belseif (get_level(constructor_powers, 50) >= 15) then
+#####B map_area()
+#####B detect_traps(DEFAULT_RADIUS)
+#####Belseif (get_level(constructor_powers, 50) >= 5) then
+#####B detect_traps(DEFAULT_RADIUS)
+#####B detect_stairs(DEFAULT_RADIUS)
+#####B detect_doors(DEFAULT_RADIUS)
+#####Belse
+#####B detect_stairs(DEFAULT_RADIUS)
+#####B detect_doors(DEFAULT_RADIUS)
+#####Bend
+
+If the level of constructor powers is greater or equal to 28, then the function
+[[[[[Bwiz_lite()] is performed, and no other part of the if statement is
+executed. [[[[[Bwiz_lite()] is just the enlightenment spell. If it is less than
+28, the next condition is examined: that if the level of constructor powers is
+greater than or equal to 15, then [[[[[Bmap_area()](Magic mapping) and detect
+traps are called. If the level of constructor power is less than 15, it moves
+onto the next condition, which says that if the level of constructor power is
+greater than 5, then detect stairs, traps and doors. If none of these
+conditions are met,(that is, if the level of construction skill is less than 5)
+then we just detect doors and stairs.
+
+You'll note that each of the detection spells includes a DEFAULT_RADIUS
+constant. You could change this to a numerical value, or a variable defined
+somewhere else in your script. eg [[[[[Bdetect_traps(2)] would detect traps
+with a radius of 2 centred on the player.
+
+#####R=== Registering the skill type ===
+
+This is what we do at the end of the file, and is what ties the powers we've
+defined to the action of pressing the 'm' key in game. Once more we're calling
+a special function [[[[[Badd_mkey()] which takes its arguments for a table.
+There are only two keys in this table though which keeps things simple.
+
+#####Badd_mkey
+#####B{
+#####B ["mkey"] = MKEY_CONSTRUCT_POWERS,
+#####B ["fct"] = function()
+#####B execute_magic(constructor_powers)
+#####B energy_use = energy_use + 100;
+#####B end
+#####B}
+
+[[[[[B"mkey"] must be a UNIQUE value > 1000 . Here I've defined it as a
+constant, [[[[[BMKEY_CONSTRUCT_POWERS], which has the value 1004. This value
+we'll call again in the s_info.txt file.
+[[[[[B"fct"] is the function that's called when the user presses the key in the
+'m' menu. So here, it calls the [[[[[Bexecute_magic] function which actually
+displays a list of powers for the user to choose from. The argument it takes is
+the powers it will use (alchemy, mindcraft, etc., or in this case constructor),
+and then the [[[[[Benergy_use] line tells the game to take one game turn to do
+the action.
+
+#####R=== Adding the skill in s_info.txt ===
+
+Take a look in the s_info.txt file, under the Misc section. You'll see,
+
+#####BN:57:Construction
+#####BD:Ability to use constructor powers
+#####BD:Construction powers use strength
+#####BA:1004:Build or knock down stuff
+#####BI:1000
+
+The first line is the index of the skill; again this must be unique. The second
+property is the name of the skill. The [[[[[BD] lines are the lines displayed
+when the skill is highlighted in the skill screen.
+The first entry on the [[[[[BA] line is the value of the [[[[[B"mkey"] we
+defined in the [[[[[Badd_mkey] function in our script. The second entry is the
+display for selecting the construction power in the 'm' menu.
+The [[[[[BI] line is currently unused, but add a 1000 there anyway. That's what
+all the others have so when it's introduced, at least it will affect your
+powers identically to how it affects all the other powers.
+
+If you scroll to the very bottom of the file now, you'll see I've placed the
+skill at the bottom of the Misc branch of the skills tree. I then made a new
+class, constructor, which you can see in p_info.txt.
+
+That is all that is NEEDED when writing a script to add a skill - defining an
+mkey using add_mkey, and defining any powers that are called in the
+[[[[[B"fct"] (generally using [[[[[Badd_magic] ).
+
+And I've added the line
+
+#####Btome_dofile("construc.lua")
+
+in init.lua so the script is loaded on start-up!
+
+Below I'm going to talk in depth about a few other functions that you may find
+useful in your scripting.
+
+#####R=== fire_bolt() and fire_beam() ===
+
+In the last help file we looked at the routine for firing a ball -
+[[[[[Bfire_ball()]. Here's a quick note about beams and bolts...
+[[[[[Bfire_beam()] and [[[[[Bfire_bolt()] take 2 arguments:
+[[[[[B(type, direction, damage)]. So in the dismantle spell we have the
+direction passed from [[[[[Bget_aim_dir()] (the function that asks the player
+for a direction), the type of damage is [[[[[BGF_KILL_TRAP], which as you might
+expect disarms traps. And the damage is only 1 because it's not going to hurt
+monsters, just dismantle traps.
+
+#####R=== set_oppose_elec() ===
+
+OK here's another thing. Wander on down to the sparky_skills spell. After the
+appropriate bolt/ball is fired, we have the line:
+
+#####Bif player.oppose_elec == 0 then
+#####B set_oppose_elec(randint(10) + 20 + get_level(constructor_powers, 20)*3)
+#####Bend
+
+This is the bit that grants temporary resist electricity. We've called the
+function [[[[[Bset_oppose_elec(turns)], which sets the player's resist
+electricity to "on" for the time specified in the argument "turns". We're only
+calling this if the player is not already granted temporary resist electricity,
+and we've linked the number of turns it is active to the level of the
+construction skill. I've limited the maximum value of get_level to 20 in this
+instance. A similar idea can be used for temporarily granting levitation,
+extended infravision, protection against evil, resist fire, stuns, cuts and so
+on and so on. Have a look in player.pkg in the source for a full list....
+
+ [[[[[gThis file by fearoffours (fearoffours@moppy.co.uk)]
diff --git a/lib/help/lua_spel.txt b/lib/help/lua_spel.txt
new file mode 100644
index 00000000..aa4a532b
--- /dev/null
+++ b/lib/help/lua_spel.txt
@@ -0,0 +1,2150 @@
+|||||oy
+
+#####R /----------------------------------------\
+#####R < spell.pkg functions helper file >
+#####R \----------------------------------------/
+
+----------------------------------------------------------------------
+
+#####R=== teleport_player_directed ===
+
+#####GDeclaration
+ extern void teleport_player_directed(int rad, int dir);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Teleport player, using a distance and a direction as a rough guide.
+ *
+ * This function is not at all obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+
+#####GDescription
+Teleport a player up to "rad" grids away roughly in "dir" direction.
+
+#####GParameters
+> "rad" must not exceed 200. The distance teleported is a minimum of a
+ quarter of "rad".
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+
+----------------------------------------------------------------------
+
+#####R=== teleport_away ===
+
+#####GDeclaration
+ extern void teleport_away(int m_idx, int dis);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Teleport a monster, normally up to "dis" grids away.
+ *
+ * Attempt to move the monster at least "dis/2" grids away.
+ *
+ * But allow variation to prevent infinite loops.
+ */
+
+#####GDescription
+Teleport monster indicated by "m_idx" up to "dis" grids away.
+
+#####GParameters
+> "m_idx" is the index of the monster in m_list[].
+> "dis" must not exceed 200. The distance teleported is a minimum of a
+ quarter of "dis".
+
+----------------------------------------------------------------------
+
+#####R=== teleport_player ===
+
+#####GDeclaration
+ extern void teleport_player(int dis);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Teleport the player to a location up to "dis" grids away.
+ *
+ * If no such spaces are readily available, the distance may increase.
+ * Try very hard to move the player at least a quarter that distance.
+ */
+
+#####GDescription
+Teleport player up to "dis" grids away.
+
+#####GParameters
+> "dis" must not exceed 200. The distance teleported is a minimum of a
+ quarter of "dis".
+
+----------------------------------------------------------------------
+
+#####R=== teleport_player_to ===
+
+#####GDeclaration
+ extern void teleport_player_to(int ny, int nx);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Teleport player to a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+
+#####GDescription
+Teleport player to a grid near the given location ("ny", "nx"). If
+the location is empty, the player goes there, otherwise they go to
+a grid as close as possible to the location.
+
+#####GParameters
+> "ny" is the y co-ordinate of the location.
+> "nx" is the x co-ordinate of the location.
+
+----------------------------------------------------------------------
+
+#####R=== teleport_monster_to ===
+
+#####GDeclaration
+ extern void teleport_monster_to(int m_idx, int ny,
+ int nx);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Teleport a monster to a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ */
+
+#####GDescription
+Teleport monster indicated by "m_idx" to a grid near the given
+location ("ny", "nx"). If the location is empty, the monster goes
+there, otherwise they go to a grid as close as possible to the
+location.
+
+#####GParameters
+> "m_idx" is the index of the monster in m_list[].
+> "ny" is the y co-ordinate of the location.
+> "nx" is the x co-ordinate of the location.
+
+----------------------------------------------------------------------
+
+#####R=== teleport_player_level ===
+
+#####GDeclaration
+ extern void teleport_player_level(void);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Teleport the player one level up or down (random when legal)
+ */
+
+#####GDescription
+Teleport the player one level up or down at random.
+
+----------------------------------------------------------------------
+
+#####R=== recall_player ===
+
+#####GDeclaration
+ extern void recall_player(void);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Recall the player to town or dungeon
+ */
+
+#####GDescription
+Recall the player to town (if in dungeon) or dungeon (if in town).
+
+----------------------------------------------------------------------
+
+#####R=== take_hit ===
+
+#####GDeclaration
+ extern void take_hit(int damage, cptr kb_str);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Decreases players hit points and sets death flag if necessary
+ *
+ * XXX XXX XXX Invulnerability needs to be changed into a "shield"
+ *
+ * XXX XXX XXX Hack -- this function allows the user to save (or quit)
+ * the game when he dies, since the "You die." message is shown before
+ * setting the player to "dead".
+ */
+
+#####GDescription
+Reduce the player's current hit points by "damage" points. If the
+player dies, "kb_str" is used to record what the player was killed by
+(see high-score table).
+
+#####GParameters
+> "damage" is the amount of damage.
+> "kb_str" is a string describing what killed the player.
+
+----------------------------------------------------------------------
+
+#####R=== take_sanity_hit ===
+
+#####GDeclaration
+ extern void take_sanity_hit(int damage, cptr hit_from);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/* Decrease player's sanity. This is a copy of the function above. */
+
+#####GDescription
+Reduce the player's current sanity points by "damage" points. If the
+player dies, "hit_from" is used to record what the player was killed
+by (see high-score table).
+
+#####GParameters
+> "damage" is the amount of damage.
+> "hit_from" is a string describing what killed the player.
+
+----------------------------------------------------------------------
+
+#####R=== project ===
+
+#####GDeclaration
+ extern bool project(int who, int rad, int y, int x,
+ int dam, int typ, int flg);
+
+#####GFile
+ spells1.c
+
+#####GComment
+/*
+ * Generic "beam"/"bolt"/"ball" projection routine.
+ *
+ * Input:
+ * who: Index of "source" monster (negative for "player")
+ * jk -- -2 for traps, only used with project_jump
+ * rad: Radius of explosion (0 = beam/bolt, 1 to 9 = ball)
+ * y,x: Target location (or location to travel "towards")
+ * dam: Base damage roll to apply to affected monsters (or player)
+ * typ: Type of damage to apply to monsters (and objects)
+ * flg: Extra bit flags (see PROJECT_xxxx in "defines.h")
+ *
+ * Return:
+ * TRUE if any "effects" of the projection were observed, else FALSE
+ *
+ * Allows a monster (or player) to project a beam/bolt/ball of a given kind
+ * towards a given location (optionally passing over the heads of interposing
+ * monsters), and have it do a given amount of damage to the monsters (and
+ * optionally objects) within the given radius of the final location.
+ *
+ * A "bolt" travels from source to target and affects only the target grid.
+ * A "beam" travels from source to target, affecting all grids passed through.
+ * A "ball" travels from source to the target, exploding at the target, and
+ * affecting everything within the given radius of the target location.
+ *
+ * Traditionally, a "bolt" does not affect anything on the ground, and does
+ * not pass over the heads of interposing monsters, much like a traditional
+ * missile, and will "stop" abruptly at the "target" even if no monster is
+ * positioned there, while a "ball", on the other hand, passes over the heads
+ * of monsters between the source and target, and affects everything except
+ * the source monster which lies within the final radius, while a "beam"
+ * affects every monster between the source and target, except for the casting
+ * monster (or player), and rarely affects things on the ground.
+ *
+ * Two special flags allow us to use this function in special ways, the
+ * "PROJECT_HIDE" flag allows us to perform "invisible" projections, while
+ * the "PROJECT_JUMP" flag allows us to affect a specific grid, without
+ * actually projecting from the source monster (or player).
+ *
+ * The player will only get "experience" for monsters killed by himself
+ * Unique monsters can only be destroyed by attacks from the player
+ *
+ * Only 256 grids can be affected per projection, limiting the effective
+ * "radius" of standard ball attacks to nine units (diameter nineteen).
+ *
+ * One can project in a given "direction" by combining PROJECT_THRU with small
+ * offsets to the initial location (see "line_spell()"), or by calculating
+ * "virtual targets" far away from the player.
+ *
+ * One can also use PROJECT_THRU to send a beam/bolt along an angled path,
+ * continuing until it actually hits something (useful for "stone to mud").
+ *
+ * Bolts and Beams explode INSIDE walls, so that they can destroy doors.
+ *
+ * Balls must explode BEFORE hitting walls, or they would affect monsters
+ * on both sides of a wall. Some bug reports indicate that this is still
+ * happening in 2.7.8 for Windows, though it appears to be impossible.
+ *
+ * We "pre-calculate" the blast area only in part for efficiency.
+ * More importantly, this lets us do "explosions" from the "inside" out.
+ * This results in a more logical distribution of "blast" treasure.
+ * It also produces a better (in my opinion) animation of the explosion.
+ * It could be (but is not) used to have the treasure dropped by monsters
+ * in the middle of the explosion fall "outwards", and then be damaged by
+ * the blast as it spreads outwards towards the treasure drop location.
+ *
+ * Walls and doors are included in the blast area, so that they can be
+ * "burned" or "melted" in later versions.
+ *
+ * This algorithm is intended to maximise simplicity, not necessarily
+ * efficiency, since this function is not a bottleneck in the code.
+ *
+ * We apply the blast effect from ground zero outwards, in several passes,
+ * first affecting features, then objects, then monsters, then the player.
+ * This allows walls to be removed before checking the object or monster
+ * in the wall, and protects objects which are dropped by monsters killed
+ * in the blast, and allows the player to see all affects before he is
+ * killed or teleported away. The semantics of this method are open to
+ * various interpretations, but they seem to work well in practice.
+ *
+ * We process the blast area from ground-zero outwards to allow for better
+ * distribution of treasure dropped by monsters, and because it provides a
+ * pleasing visual effect at low cost.
+ *
+ * Note that the damage done by "ball" explosions decreases with distance.
+ * This decrease is rapid, grids at radius "dist" take "1/dist" damage.
+ *
+ * Notice the "napalm" effect of "beam" weapons. First they "project" to
+ * the target, and then the damage "flows" along this beam of destruction.
+ * The damage at every grid is the same as at the "centre" of a "ball"
+ * explosion, since the "beam" grids are treated as if they ARE at the
+ * centre of a "ball" explosion.
+ *
+ * Currently, specifying "beam" plus "ball" means that locations which are
+ * covered by the initial "beam", and also covered by the final "ball", except
+ * for the final grid (the epicentre of the ball), will be "hit twice", once
+ * by the initial beam, and once by the exploding ball. For the grid right
+ * next to the epicentre, this results in 150% damage being done. The centre
+ * does not have this problem, for the same reason the final grid in a "beam"
+ * plus "bolt" does not -- it is explicitly removed. Simply removing "beam"
+ * grids which are covered by the "ball" will NOT work, as then they will
+ * receive LESS damage than they should. Do not combine "beam" with "ball".
+ *
+ * The array "gy[],gx[]" with current size "grids" is used to hold the
+ * collected locations of all grids in the "blast area" plus "beam path".
+ *
+ * Note the rather complex usage of the "gm[]" array. First, gm[0] is always
+ * zero. Second, for N>1, gm[N] is always the index (in gy[],gx[]) of the
+ * first blast grid (see above) with radius "N" from the blast centre. Note
+ * that only the first gm[1] grids in the blast area thus take full damage.
+ * Also, note that gm[rad+1] is always equal to "grids", which is the total
+ * number of blast grids.
+ *
+ * Note that once the projection is complete, (y2,x2) holds the final location
+ * of bolts/beams, and the "epicentre" of balls.
+ *
+ * Note also that "rad" specifies the "inclusive" radius of projection blast,
+ * so that a "rad" of "one" actually covers 5 or 9 grids, depending on the
+ * implementation of the "distance" function. Also, a bolt can be properly
+ * viewed as a "ball" with a "rad" of "zero".
+ *
+ * Note that if no "target" is reached before the beam/bolt/ball travels the
+ * maximum distance allowed (MAX_RANGE), no "blast" will be induced. This
+ * may be relevant even for bolts, since they have a "1x1" mini-blast.
+ *
+ * Note that for consistency, we "pretend" that the bolt actually takes "time"
+ * to move from point A to point B, even if the player cannot see part of the
+ * projection path. Note that in general, the player will *always* see part
+ * of the path, since it either starts at the player or ends on the player.
+ *
+ * Hack -- we assume that every "projection" is "self-illuminating".
+ *
+ * Hack -- when only a single monster is affected, we automatically track
+ * (and recall) that monster, unless "PROJECT_JUMP" is used.
+ *
+ * Note that all projections now "explode" at their final destination, even
+ * if they were being projected at a more distant destination. This means
+ * that "ball" spells will *always* explode.
+ *
+ * Note that we must call "handle_stuff()" after affecting terrain features
+ * in the blast radius, in case the "illumination" of the grid was changed,
+ * and "update_view()" and "update_monsters()" need to be called.
+ */
+
+#####GDescription
+Generate a beam/bolt/ball starting from "who" with a radius of "rad"
+at target grid "y,x" for "damage" points of "typ" damage. The beam/
+bolt/ball can have various properties as denoted by "flg".
+
+#####GParameters
+> "who" is > 0 (index of monster in m_list[]), < 0 and
+ not -100 or -101 (player), -100 or -101 (trap).
+> "rad" is 0 for a beam/bolt and 1-9 for a ball.
+> "y" is the y co-ordinate of the target grid.
+> "x" is the x co-ordinate of the target grid.
+> "dam" is the number of points of damage.
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "flg" is the projection effect
+ *****fields.txt*0[PROJECT_fields]
+
+----------------------------------------------------------------------
+
+#####R=== corrupt_player ===
+
+#####GDeclaration
+ extern void corrupt_player(void);
+
+#####GFile
+ spells1.c
+
+#####GComment
+(none)
+
+#####GDescription
+Swap two of the players stats at random.
+
+----------------------------------------------------------------------
+
+#####R=== grow_trees ===
+
+#####GDeclaration
+ extern void grow_trees(int rad);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Grow trees
+ */
+
+#####GDescription
+Grow up to (("rad" x "rad") + 11) trees around the player.
+
+#####GParameters
+> "rad" is the radius of the area where trees may grow.
+
+----------------------------------------------------------------------
+
+#####R=== hp_player ===
+
+#####GDeclaration
+ extern bool hp_player(int num);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Increase players hit points, notice effects
+ */
+
+#####GDescription
+Add "num" points to the player's current hit points. The total can
+not exceed the maximum.
+
+#####GParameters
+> "num" is the number of points to add.
+
+----------------------------------------------------------------------
+
+#####R=== heal_insanity ===
+
+#####GDeclaration
+ extern bool heal_insanity(int val);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/* Heal insanity. */
+
+#####GDescription
+Add "val" points to the player's current sanity points. The total can
+not exceed the maximum.
+
+#####GParameters
+> "val" is the number of points to add.
+
+----------------------------------------------------------------------
+
+#####R=== warding_glyph ===
+
+#####GDeclaration
+ extern void warding_glyph(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Leave a "glyph of warding" which prevents monster movement
+ */
+
+#####GDescription
+Place a glyph at the player's location. The location must be bare.
+
+----------------------------------------------------------------------
+
+#####R=== explosive_rune ===
+
+#####GDeclaration
+ extern void explosive_rune(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+(none)
+
+#####GDescription
+Place a minor glyph (explosive rune) at the player's location. The
+location must be bare.
+
+----------------------------------------------------------------------
+
+#####R=== do_dec_stat ===
+
+#####GDeclaration
+ extern bool do_dec_stat(int stat, int mode);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Lose a "point"
+ */
+
+#####GDescription
+Attempt to reduce the player's "stat" statistic by a point.
+
+#####GParameters
+> "stat" is the statistic
+ *****fields.txt*0[A_fields]
+> "mode" is the type of decrease: temporary, normal, or permanent
+ *****fields.txt*0[STAT_DEC_fields]
+
+----------------------------------------------------------------------
+
+#####R=== do_res_stat ===
+
+#####GDeclaration
+ extern bool do_res_stat(int stat);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Restore lost "points" in a stat
+ */
+
+#####GDescription
+Restore the player's "stat" statistic.
+
+#####GParameters
+> "stat" is the statistic
+ *****fields.txt*0[A_fields]
+
+----------------------------------------------------------------------
+
+#####R=== do_inc_stat ===
+
+#####GDeclaration
+ extern bool do_inc_stat(int stat);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Gain a "point" in a stat
+ */
+
+#####GDescription
+Increase the player's "stat" statistic by a point.
+
+#####GParameters
+> "stat" is the statistic
+ *****fields.txt*0[A_fields]
+
+----------------------------------------------------------------------
+
+#####R=== identify_pack ===
+
+#####GDeclaration
+ extern void identify_pack(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Identify everything being carried.
+ * Done by a potion of "self knowledge".
+ */
+
+#####GDescription
+Identify all items in the inventory.
+
+----------------------------------------------------------------------
+
+#####R=== remove_curse ===
+
+#####GDeclaration
+ extern bool remove_curse(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Remove most curses
+ */
+
+#####GDescription
+Remove all curses except for heavy curses.
+
+----------------------------------------------------------------------
+
+#####R=== remove_all_curse ===
+
+#####GDeclaration
+ extern bool remove_all_curse(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Remove all curses
+ */
+
+#####GDescription
+Remove all curses including heavy curses.
+
+----------------------------------------------------------------------
+
+#####R=== restore_level ===
+
+#####GDeclaration
+ extern bool restore_level(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Restores any drained experience
+ */
+
+#####GDescription
+Restore all drained experience points (if any).
+
+----------------------------------------------------------------------
+
+#####R=== self_knowledge ===
+
+#####GDeclaration
+ extern void self_knowledge(FILE *fff);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * self-knowledge... idea from nethack. Useful for determining powers and
+ * resistances of items. It saves the screen, clears it, then starts listing
+ * attributes, a screenful at a time. (There are a LOT of attributes to
+ * list. It will probably take 2 or 3 screens for a powerful character whose
+ * using several artifacts...) -CFT
+ *
+ * It is now a lot more efficient. -BEN-
+ *
+ * See also "identify_fully()".
+ *
+ * XXX XXX XXX Use the "show_file()" method, perhaps.
+ */
+
+#####GDescription
+Show all attributes including racial powers, mutations, and equipment
+effects.
+
+#####GParameters
+> "*ffff" points to a file (write info to file) or is NULL (write info
+ to screen).
+
+----------------------------------------------------------------------
+
+#####R=== lose_all_info ===
+
+#####GDeclaration
+ extern bool lose_all_info(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Forget everything
+ */
+
+#####GDescription
+Forget about objects and the map.
+
+----------------------------------------------------------------------
+
+#####R=== detect_traps ===
+
+#####GDeclaration
+ extern bool detect_traps(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all traps on current panel
+ */
+
+#####GDescription
+Detect all traps on current panel.
+
+----------------------------------------------------------------------
+
+#####R=== detect_doors ===
+
+#####GDeclaration
+ extern bool detect_doors(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all doors on current panel
+ */
+
+#####GDescription
+Detect all doors on current panel.
+
+----------------------------------------------------------------------
+
+#####R=== detect_stairs ===
+
+#####GDeclaration
+ extern bool detect_stairs(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all stairs on current panel
+ */
+
+#####GDescription
+Detect all stairs on current panel.
+
+----------------------------------------------------------------------
+
+#####R=== detect_treasure ===
+
+#####GDeclaration
+ extern bool detect_treasure(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect any treasure on the current panel
+ */
+
+#####GDescription
+Detect any treasure on the current panel.
+
+----------------------------------------------------------------------
+
+Field: hack_no_detect_message
+Value: FALSE
+
+----------------------------------------------------------------------
+
+#####R=== detect_objects_gold ===
+
+#####GDeclaration
+ extern bool detect_objects_gold(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "gold" objects on the current panel
+ */
+
+#####GDescription
+Detect all objects with the TV_GOLD flag.
+
+----------------------------------------------------------------------
+
+#####R=== detect_objects_normal ===
+
+#####GDeclaration
+ extern bool detect_objects_normal(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "normal" objects on the current panel
+ */
+
+#####GDescription
+Detect all objects without the TV_GOLD flag.
+
+----------------------------------------------------------------------
+
+#####R=== detect_objects_magic ===
+
+#####GDeclaration
+ extern bool detect_objects_magic(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "magic" objects on the current panel.
+ *
+ * This will light up all spaces with "magic" items, including artifacts,
+ * ego-items, potions, scrolls, books, rods, wands, staves, amulets, rings,
+ * and "enchanted" items of the "good" variety.
+ *
+ * It can probably be argued that this function is now too powerful.
+ */
+
+#####GDescription
+Detect all "magic" objects which are artefacts, ego items, or have one
+of the following flags - TV_AMULET, TV_RING, TV_BATERIE, TV_STAFF,
+TV_WAND, TV_ROD, TV_ROD_MAIN, TV_SCROLL, TV_POTION, TV_POTION2,
+TV_VALARIN_BOOK, TV_MAGERY_BOOK, TV_SHADOW_BOOK, TV_CHAOS_BOOK,
+TV_SPIRIT_BOOK, TV_NETHER_BOOK, TV_DAEMON_BOOK, TV_CRUSADE_BOOK,
+TV_SIGALDRY_BOOK, TV_SYMBIOTIC_BOOK, TV_MUSIC_BOOK.
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_normal ===
+
+#####GDeclaration
+ extern bool detect_monsters_normal(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "normal" monsters on the current panel
+ */
+
+#####GDescription
+Detect all non-invisible monsters (without RF2_INVISIBLE).
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_invis ===
+
+#####GDeclaration
+ extern bool detect_monsters_invis(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "invisible" monsters on current panel
+ */
+
+#####GDescription
+Detect all invisible monsters (with RF2_INVISIBLE).
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_evil ===
+
+#####GDeclaration
+ extern bool detect_monsters_evil(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "evil" monsters on current panel
+ */
+
+#####GDescription
+Detect all evil monsters (with RF3_EVIL).
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_good ===
+
+#####GDeclaration
+ extern bool detect_monsters_good(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/* Detect good monsters */
+
+#####GDescription
+Detect all good monsters (with RF3_GOOD).
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_xxx ===
+
+#####GDeclaration
+ extern bool detect_monsters_xxx(u32b match_flag);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * A "generic" detect monsters routine, tagged to flags3
+ */
+
+#####GDescription
+Detect all monsters with "match_flag" flag.
+
+#####GParameters
+> "match_flag" can be any RF3_ flag (see defines.h) but only
+ RF3_DEMON, RF3_UNDEAD, RF3_GOOD work.
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_string ===
+
+#####GDeclaration
+ extern bool detect_monsters_string(cptr);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all (string) monsters on current panel
+ */
+
+#####GDescription
+Detect all monsters whose default monster character matches a
+character pointed to by "cptr".
+
+#####GParameters
+> "cptr" is a pointer to a single character, eg 'Z' for hounds. For
+ available characters, see the "symbol" field of the graphics (G)
+ line of r_info.txt.
+
+----------------------------------------------------------------------
+
+#####R=== detect_monsters_nonliving ===
+
+#####GDeclaration
+ extern bool detect_monsters_nonliving(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect all "nonliving", "undead" or "demonic" monsters on current panel
+ */
+
+#####GDescription
+Detect all non-living monsters (with RF3_NONLIVING, RF3_UNDEAD, or
+RF3_DEMON).
+
+----------------------------------------------------------------------
+
+#####R=== detect_all ===
+
+#####GDeclaration
+ extern bool detect_all(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Detect everything
+ */
+
+#####GDescription
+Detects traps, doors, stairs, treasure, gold objects, normal objects,
+invisible monsters, normal (visible) monsters.
+
+----------------------------------------------------------------------
+
+#####R=== stair_creation ===
+
+#####GDeclaration
+ extern void stair_creation(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Create stairs at the player location
+ */
+
+#####GDescription
+Create stairs at the player location. This is not allowed if the grid
+is not empty, the player is not in a dungeon, the player is on a
+special level, the player is in an arena or quest. If the player is
+in the town or wilderness the stairs will go down. If the player is
+on a quest level or at the bottom of a dungeon, the stairs will go up.
+Otherwise there is an even chance the stairs will go up or down.
+
+----------------------------------------------------------------------
+
+#####R=== wall_stone ===
+
+#####GDeclaration
+ extern bool wall_stone(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+(none)
+
+#####GDescription
+Create a stone wall on the player's grid. This function uses the
+project() function to create the stone wall. Apparently zero can be
+used as the "who" parameter for the player. See details for the
+project() function elsewhere in this document.
+
+----------------------------------------------------------------------
+
+#####R=== create_artifact ===
+
+#####GDeclaration
+ extern bool create_artifact(object_type *o_ptr,
+ bool a_scroll, bool get_name);
+
+#####GFile
+ randart.c
+
+#####GComment
+(none)
+
+#####GDescription
+Create an artifact from object "*optr".
+
+#####GParameters
+> "*optr* is a pointer to an object
+> "a_scroll" is true if the artifact is created by reading a scroll
+> "get_name" is true if the artifact is to be named by the player (if
+ a_scroll is true) or created randomly (a_scroll is false), or false
+ if an inscription is used.
+
+----------------------------------------------------------------------
+
+#####R=== ident_spell ===
+
+#####GDeclaration
+ extern bool ident_spell(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Identify an object in the inventory (or on the floor)
+ * This routine does *not* automatically combine objects.
+ * Returns TRUE if something was identified, else FALSE.
+ */
+
+#####GDescription
+Identify an object in the inventory (or on the floor).
+
+----------------------------------------------------------------------
+
+#####R=== identify_fully ===
+
+#####GDeclaration
+ extern bool identify_fully(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Fully "identify" an object in the inventory -BEN-
+ * This routine returns TRUE if an item was identified.
+ */
+
+#####GDescription
+Fully "identify" an object in the inventory (or on the floor).
+
+----------------------------------------------------------------------
+
+#####R=== recharge ===
+
+#####GDeclaration
+ extern bool recharge(int num);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Recharge a wand/staff/rod from the pack or on the floor.
+ * This function has been rewritten in Oangband. -LM-
+ *
+ * Mage -- Recharge I --> recharge(90)
+ * Mage -- Recharge II --> recharge(150)
+ * Mage -- Recharge III --> recharge(220)
+ *
+ * Priest or Necromancer -- Recharge --> recharge(140)
+ *
+ * Scroll of recharging --> recharge(130)
+ * Scroll of *recharging* --> recharge(200)
+ *
+ * It is harder to recharge high level, and highly charged wands,
+ * staffs, and rods. The more wands in a stack, the more easily and
+ * strongly they recharge. Staffs, however, each get fewer charges if
+ * stacked.
+ *
+ * XXX XXX XXX Beware of "sliding index errors".
+ */
+
+#####GDescription
+Recharge an object in the inventory (or on the floor) with "num"
+power.
+
+#####GParameters
+> "num" is the power used in recharging. It is compared to the
+ object's level to determine whether the item is recharged
+ successfully or destroyed. If it is recharged, it also determines
+ how many charges are added, or how much recharge time is reduced.
+
+----------------------------------------------------------------------
+
+#####R=== aggravate_monsters ===
+
+#####GDeclaration
+ extern void aggravate_monsters(int who);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Wake up all monsters, and speed up "los" monsters.
+ */
+
+#####GDescription
+Aggravate monsters, originating from "who".
+
+#####GParameters
+> "who" is the index of monster in m_list[] (1 if it is the player)
+ which triggers the aggravation;
+
+----------------------------------------------------------------------
+
+#####R=== genocide ===
+
+#####GDeclaration
+ extern bool genocide(bool player_cast);
+
+#####GFile
+ spells2.c
+
+#####GComment
+(none)
+
+#####GDescription
+Genocide a monster race.
+
+#####GParameters
+> "player_cast" is true if the player cast the spell so the player can
+ take damage.
+
+----------------------------------------------------------------------
+
+#####R=== mass_genocide ===
+
+#####GDeclaration
+ extern bool mass_genocide(bool player_cast);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Delete all nearby (non-unique) monsters
+ */
+
+#####GDescription
+Delete all nearby (non-unique) monsters.
+
+#####GParameters
+> "player_cast" is true if the player cast the spell so the player can
+ take damage.
+
+----------------------------------------------------------------------
+
+#####R=== probing ===
+
+#####GDeclaration
+ extern bool probing(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Probe nearby monsters
+ */
+
+#####GDescription
+Probe all nearby monsters.
+
+----------------------------------------------------------------------
+
+#####R=== banish_evil ===
+
+#####GDeclaration
+ extern bool banish_evil(int dist);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Banish evil monsters
+ */
+
+#####GDescription
+Banish nearby evil monsters doing "dist" points of GF_AWAY_EVIL
+damage.
+
+#####GParameters
+> "dist" is the amount of damage done to each monster.
+
+----------------------------------------------------------------------
+
+#####R=== dispel_evil ===
+
+#####GDeclaration
+ extern bool dispel_evil(int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Dispel evil monsters
+ */
+
+#####GDescription
+Dispel nearby evil monsters doing "dam" points of GF_DISP_EVIL
+damage.
+
+#####GParameters
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== dispel_good ===
+
+#####GDeclaration
+ extern bool dispel_good(int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Dispel good monsters
+ */
+
+#####GDescription
+Dispel nearby good monsters doing "dam" points of GF_DISP_GOOD
+damage.
+
+#####GParameters
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== dispel_undead ===
+
+#####GDeclaration
+ extern bool dispel_undead(int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Dispel undead monsters
+ */
+
+#####GDescription
+Dispel nearby undead monsters doing "dam" points of GF_DISP_UNDEAD
+damage.
+
+#####GParameters
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== dispel_monsters ===
+
+#####GDeclaration
+ extern bool dispel_monsters(int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Dispel all monsters
+ */
+
+#####GDescription
+Dispel all nearby monsters doing "dam" points of GF_DISP_ALL
+damage.
+
+#####GParameters
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== dispel_living ===
+
+#####GDeclaration
+ extern bool dispel_living(int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Dispel 'living' monsters
+ */
+
+#####GDescription
+Dispel nearby living monsters doing "dam" points of GF_DISP_LIVING
+damage.
+
+#####GParameters
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== dispel_demons ===
+
+#####GDeclaration
+ extern bool dispel_demons(int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Dispel demons
+ */
+
+#####GDescription
+Dispel nearby demon monsters doing "dam" points of GF_DISP_DEMON
+damage.
+
+#####GParameters
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== turn_undead ===
+
+#####GDeclaration
+ extern bool turn_undead(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Turn undead
+ */
+
+#####GDescription
+Turn nearby undead monsters doing a point of GF_TURN_UNDEAD damage for
+each player level.
+
+----------------------------------------------------------------------
+
+#####R=== wipe ===
+
+#####GDeclaration
+ extern void wipe(int y1, int x1, int r);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Wipe -- Empties a part of the dungeon
+ */
+
+#####GDescription
+Delete monsters and objects from an area of the dungeon centred at
+grid "y1,x1" for a radius "r". This does not work on special levels or
+quests. The player may be blinded. The player forgets the affected
+area and it becomes dark. All grids become floor.
+
+#####GParameters
+> "y1" is the y-coordinate of the wipe's origin.
+> "x1" is the x-coordinate of the wipe's origin.
+> "r" is the radius of the wipe.
+
+----------------------------------------------------------------------
+
+#####R=== destroy_area ===
+
+#####GDeclaration
+ extern void destroy_area(int y1, int x1, int r,
+ bool full);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * The spell of destruction
+ *
+ * This spell "deletes" monsters (instead of "killing" them).
+ *
+ * Later we may use one function for both "destruction" and
+ * "earthquake" by using the "full" to select "destruction".
+ */
+
+#####GDescription
+Delete monsters and objects from an area of the dungeon centred at
+grid "y1,x1" for a radius "r". This does not work on special levels or
+quests. The epicentre is NOT affected. The player may be blinded. The
+player forgets the affected area and it becomes dark. The grids can
+become granite, quartz, magma, or floor.
+
+#####GParameters
+> "y1" is the y-coordinate of the destruction's origin.
+> "x1" is the x-coordinate of the destruction's origin.
+> "r" is the radius of the destruction.
+> "full" is currently unused.
+
+----------------------------------------------------------------------
+
+#####R=== earthquake ===
+
+#####GDeclaration
+ extern void earthquake(int cy, int cx, int r);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Induce an "earthquake" of the given radius at the given location.
+ *
+ * This will turn some walls into floors and some floors into walls.
+ *
+ * The player will take damage and "jump" into a safe grid if possible,
+ * otherwise, he will "tunnel" through the rubble instantaneously.
+ *
+ * Monsters will take damage, and "jump" into a safe grid if possible,
+ * otherwise they will be "buried" in the rubble, disappearing from
+ * the level in the same way that they do when genocided.
+ *
+ * Note that thus the player and monsters (except eaters of walls and
+ * passers through walls) will never occupy the same grid as a wall.
+ * Note that as of now (2.7.8) no monster may occupy a "wall" grid, even
+ * for a single turn, unless that monster can pass_walls or kill_walls.
+ * This has allowed massive simplification of the "monster" code.
+ */
+
+#####GDescription
+Create an earthquake centred on grid "cy,cx" with a radius of "r".
+This does not work on quest levels. The epicentre is NOT affected.
+Only about 15% of the grids are affected. The player takes 300 points
+of damage if they can't be moved to a safe grid, otherwise damage is
+from 10 to 40 points. The player forgets the affected area and it
+becomes dark. The grids can become granite, quartz, magma, or floor.
+
+#####GParameters
+Parameters:
+> "cy" is the y-coordinate of the earthquake origin.
+> "cx" is the x-coordinate of the earthquake origin.
+> "r" is the radius of the earthquake.
+
+----------------------------------------------------------------------
+
+#####R=== map_area ===
+
+#####GDeclaration
+ extern void map_area(void);
+
+#####GFile
+ cave.c
+
+#####GComment
+/*
+ * Hack -- map the current panel (plus some) ala "magic mapping"
+ */
+
+#####GDescription
+Map the current panel plus up to 10 grids up and down, and up to 20
+grids left and right.
+
+----------------------------------------------------------------------
+
+#####R=== wiz_lite ===
+
+#####GDeclaration
+ extern void wiz_lite(void);
+
+#####GFile
+ cave.c
+
+#####GComment
+/*
+ * Light up the dungeon using "clairvoyance"
+ *
+ * This function "illuminates" every grid in the dungeon, memorises all
+ * "objects", memorises all grids as with magic mapping, and, under the
+ * standard option settings (view_perma_grids but not view_torch_grids)
+ * memorises all floor grids too.
+ *
+ * Note that if "view_perma_grids" is not set, we do not memorise floor
+ * grids, since this would defeat the purpose of "view_perma_grids", not
+ * that anyone seems to play without this option.
+ *
+ * Note that if "view_torch_grids" is set, we do not memorise floor grids,
+ * since this would prevent the use of "view_torch_grids" as a method to
+ * keep track of what grids have been observed directly.
+ */
+
+#####GDescription
+Light up the entire dungeon and show all monsters and objects.
+
+----------------------------------------------------------------------
+
+#####R=== wiz_lite_extra ===
+
+#####GDeclaration
+ extern void wiz_lite_extra(void);
+
+#####GFile
+ cave.c
+
+#####GComment
+(none)
+
+#####GDescription
+Light up the entire dungeon and show all monsters and objects. All
+squares are lit and remembered.
+
+----------------------------------------------------------------------
+
+#####R=== wiz_dark ===
+
+#####GDeclaration
+ extern void wiz_dark(void);
+
+#####GFile
+ cave.c
+
+#####GComment
+/*
+ * Forget the dungeon map (ala "Thinking of Maud...").
+ */
+
+#####GDescription
+Forget all grids and objects. All grids become dark.
+
+----------------------------------------------------------------------
+
+#####R=== lite_room ===
+
+#####GDeclaration
+ extern void lite_room(int y1, int x1);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Illuminate any room containing the given location.
+ */
+
+#####GDescription
+Light up the room (if any) of grid "y1,x1".
+
+#####GParameters
+> "y1" is the y-coordinate of the grid.
+> "x1" is the x-coordinate of the grid.
+
+----------------------------------------------------------------------
+
+#####R=== unlite_room ===
+
+#####GDeclaration
+ extern void unlite_room(int y1, int x1);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Darken all rooms containing the given location
+ */
+
+#####GDescription
+Darken all rooms (if any) of grid "y1,x1".
+
+#####GParameters
+> "y1" is the y-coordinate of the grid.
+> "x1" is the x-coordinate of the grid.
+
+----------------------------------------------------------------------
+
+#####R=== lite_area ===
+
+#####GDeclaration
+ extern bool lite_area(int dam, int rad);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Hack -- call light around the player
+ * Affect all monsters in the projection radius
+ */
+
+#####GDescription
+Light up the room (if any) of the player's grid. Monsters take "dam"
+points of GF_LITE_WEAK damage if they are within "r" grids of the
+player.
+
+#####GParameters
+> "dam" is the number of points of damage.
+> "rad" is the radius of the effect of the damage.
+
+----------------------------------------------------------------------
+
+#####R=== unlite_area ===
+
+#####GDeclaration
+ extern bool unlite_area(int dam, int rad);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Hack -- call darkness around the player
+ * Affect all monsters in the projection radius
+ */
+
+#####GDescription
+Darken the room (if any) of the player's grid. Monsters take "dam"
+points of GF_DARK_WEAK damage if they are within "r" grids of the
+player.
+
+#####GParameters
+> "dam" is the number of points of damage.
+> "rad" is the radius of the effect of the damage.
+
+----------------------------------------------------------------------
+
+#####R=== fire_ball_beam ===
+
+#####GDeclaration
+ extern bool fire_ball_beam(int typ, int dir, int dam,
+ int rad);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a ball-beamed spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+
+#####GDescription
+Cast a ball-beamed spell of type "typ" in direction "dir" for damage
+"dam" points with a radius of "rad" grids.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+> "rad" is the radius of the effect of the damage (must be <= 16).
+
+----------------------------------------------------------------------
+
+#####R=== fire_ball ===
+
+#####GDeclaration
+ extern bool fire_ball(int typ, int dir, int dam, int rad);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a ball spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+
+#####GDescription
+Cast a ball spell of type "typ" in direction "dir" for "dam" points of
+damage. The ball has a radius of "rad" grids.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+> "rad" is the radius of the effect of the damage (must be <= 16).
+
+----------------------------------------------------------------------
+
+#####R=== fire_bolt ===
+
+#####GDeclaration
+ extern bool fire_bolt(int typ, int dir, int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a bolt spell
+ * Stop if we hit a monster, as a "bolt"
+ * Affect monsters (not grids or objects)
+ */
+
+#####GDescription
+Cast a bolt spell of type "typ" in direction "dir" for "dam" points of
+damage.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== fire_beam ===
+
+#####GDeclaration
+ extern bool fire_beam(int typ, int dir, int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+
+#####GDescription
+Cast a beam spell of type "typ" in direction "dir" for "dam" points of
+damage.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== fire_druid_ball ===
+
+#####GDeclaration
+ extern bool fire_druid_ball(int typ, int dir, int dam,
+ int rad);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a druidic ball spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+
+#####GDescription
+Cast a ball spell of type "typ" in direction "dir" for "dam" points of
+damage. The ball has a radius of "rad" grids. The spell follows a mana
+path.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+> "rad" is the radius of the effect of the damage (must be <= 16).
+
+----------------------------------------------------------------------
+
+#####R=== fire_druid_bolt ===
+
+#####GDeclaration
+ extern bool fire_druid_bolt(int typ, int dir, int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a druidic bolt spell
+ * Stop if we hit a monster, as a "bolt"
+ * Affect monsters (not grids or objects)
+ */
+
+#####GDescription
+Cast a bolt spell of type "typ" in direction "dir" for "dam" points of
+damage. The spell follows a mana path.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== fire_druid_beam ===
+
+#####GDeclaration
+ extern bool fire_druid_beam(int typ, int dir, int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a druidistic beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+
+#####GDescription
+Cast a beam spell of type "typ" in direction "dir" for "dam" points of
+damage. The spell follows a mana path.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== fire_bolt_or_beam ===
+
+#####GDeclaration
+ extern bool fire_bolt_or_beam(int prob, int typ, int dir,
+ int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Cast a bolt spell, or rarely, a beam spell
+ */
+
+#####GDescription
+Cast a beam (chance of "prob" in 100) or bolt spell of type "typ" in
+direction "dir" for "dam" points of damage.
+
+#####GParameters
+> "prob" is the number of times out of 100 that the bolt will actually
+ be a beam. Obviously this value should range from 1 to 99 (0 will
+ always give a beam, 100 or higher will always give a beam. There are
+ separate functions for these cases).
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+#####R=== alchemy ===
+
+#####GDeclaration
+ extern bool alchemy(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/* Turns an object into gold, gain some of its value in a shop */
+
+#####GDescription
+The player selects an object (and quantity if it applies) from the
+inventory or the floor and attempts to turn it into gold. If the
+price of the item is < 0 then the player gains nothing (fool's gold),
+otherwise the player gets a third of the price in gold. Artifacts are
+not affected.
+
+----------------------------------------------------------------------
+
+#####R=== alter_reality ===
+
+#####GDeclaration
+ extern void alter_reality(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+(none)
+
+#####GDescription
+The player leaves the level immediately.
+
+----------------------------------------------------------------------
+
+#####R=== teleport_swap ===
+
+#####GDeclaration
+ extern void teleport_swap(int dir);
+
+#####GFile
+ spells2.c
+
+#####GComment
+(none)
+
+#####GDescription
+Player swaps places with target in direction "dir". The target must be
+a monster. It will not work if the space-time continuum can not be
+disrupted or if the monster resists teleportation.
+
+----------------------------------------------------------------------
+
+#####R=== project_meteor ===
+
+#####GDeclaration
+ extern void project_meteor(int radius, int typ, int dam,
+ u32b flg);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Apply a "project()" a la meteor shower
+ */
+
+#####GDescription
+Generate between "rad" and "rad" x 2 ball spells of type "typ" for
+"dam" points of damage. The ball can have various properties as
+denoted by "flg".
+
+#####GParameters
+> "rad" is the minimum number of balls created. "rad" + randint("rad")
+ balls are created. Each ball has a radius of 2 grids. Each target
+ grid is within 5 grids of the player.
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dam" is the number of points of damage.
+> "flg" is the projection effect
+ *****fields.txt*0[PROJECT_fields]
+
+----------------------------------------------------------------------
+
+#####R=== passwall ===
+
+#####GDeclaration
+ extern bool passwall(int dir, bool safe);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Send the player shooting through walls in the given direction until
+ * they reach a non-wall space, or a monster, or a permanent wall.
+ */
+
+#####GDescription
+Move the player through walls in direction "dir". if "safe" then the
+player can not end up in a wall - if they do, the wall is replaced by
+a floor. This does not work in the wilderness, on quest levels, or if
+teleport is not allowed. Stopping on monsters or inside vaults is not
+allowed.
+
+#####GParameters
+> "dir" must be from 0 to 9. It can not be 5.
+ *****fields.txt*0[direction]
+> "safe" must be true if the player is not to be trapped in a wall
+ when the movement is finished.
+
+----------------------------------------------------------------------
+
+#####R=== project_hook ===
+
+#####GDeclaration
+ extern bool project_hook(int typ, int dir, int dam,
+ int flg);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Hack -- apply a "projection()" in a direction (or at the target)
+ */
+
+#####GDescription
+Generate a beam/bolt of type "typ" in direction "dir" (or at a target)
+for "dam" points of damage. The beam/bolt can have various properties
+as denoted by "flg".
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dir" must be from 0 to 9. 5 means use the current target.
+ *****fields.txt*0[direction]
+> "dam" is the number of points of damage.
+> "flg" is the projection effect
+ *****fields.txt*0[PROJECT_fields]
+
+----------------------------------------------------------------------
+
+#####R=== reset_recall ===
+
+#####GDeclaration
+ extern bool reset_recall(void);
+
+#####GFile
+ spells2.c
+
+#####GComment
+(none)
+
+#####GDescription
+Ask the player for a dungeon and appropriate level within the dungeon.
+The player can not specify a dungeon they have not gone to yet. If the
+player chooses levels 99 or 100, the level is set to 98.
+
+----------------------------------------------------------------------
+
+#####R=== get_aim_dir ===
+
+#####GDeclaration
+ extern bool get_aim_dir(int *dp = 0);
+
+#####GFile
+ xtra2.c
+
+#####GComment
+/*
+ * Get an "aiming direction" from the user.
+ *
+ * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
+ * "0" for "current target", and "-1" for "entry aborted".
+ *
+ * Note that "Force Target", if set, will pre-empt user interaction,
+ * if there is a usable target already set.
+ *
+ * Note that confusion over-rides any (explicit?) user choice.
+ */
+
+#####GDescription
+Get an aiming direction from the user and store it in "dp". A target
+can be selected. If the player is confused, the direction will be
+random.
+
+#####GParameters
+> "dp" = player direction.
+
+----------------------------------------------------------------------
+
+#####R=== project_hack ===
+
+#####GDeclaration
+ extern bool project_hack(int typ, int dam);
+
+#####GFile
+ spells2.c
+
+#####GComment
+/*
+ * Apply a "project()" directly to all viewable monsters
+ *
+ * Note that affected monsters are NOT auto-tracked by this usage.
+ */
+
+#####GDescription
+Generate beam/bolt spells of type "typ" for "dam" points of damage to
+all viewable monsters in line of site.
+
+#####GParameters
+> "typ" is the type of damage
+ *****fields.txt*0[GF_fields]
+> "dam" is the number of points of damage.
+
+----------------------------------------------------------------------
+
+
+Back to the *****lua.hlp*0[lua help index] .
+
+ [[[[[gThis file by Chris Hadgis]
diff --git a/lib/help/lua_util.txt b/lib/help/lua_util.txt
new file mode 100644
index 00000000..8886a2b4
--- /dev/null
+++ b/lib/help/lua_util.txt
@@ -0,0 +1,898 @@
+|||||oy
+
+#####R /----------------------------------------\
+#####R < util.pkg functions helper file >
+#####R \----------------------------------------/
+
+----------------------------------------------------------------------
+
+#####R=== bst ===
+
+#####GDeclaration
+ s32b bst(s32b what, s32b t);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+ * Break scalar time
+ */
+
+#####GDescription
+Return the minute, hour, day, or year for turn "t". One turn takes 7.5
+seconds.
+
+#####GParameters
+> "what" is the unit to be returned and must be one of
+ MINUTE (number of turns per minute, which is 8)
+ HOUR (number of turns per hour, which is 480)
+ DAY (number of turns per day, which is 11,520)
+ YEAR (number of turns per year, which is 4,204,800)
+> "t" is the number of turns.
+
+----------------------------------------------------------------------
+
+#####R=== path_build ===
+
+#####GDeclaration
+ errr path_build(char *buf, int max, cptr path, cptr file);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Create a new path by appending a file (or directory) to a path
+*
+* This requires no special processing on simple machines, except
+* for verifying the size of the filename, but note the ability to
+* bypass the given "path" with certain special file-names.
+*
+* Note that the "file" may actually be a "sub-path", including
+* a path and a file.
+*
+* Note that this function yields a path which must be "parsed"
+* using the "parse" function above.
+*/
+
+#####GDescription
+Append file "file" to path "path" and return the result in "buf". The
+length of "buf" is a maximum of "max" characters. If "file" starts
+with '~' then return "file". If "file" starts with the path separator
+and the path separator is not blank then return "file". If there is no
+path then return "file". Otherwise return "path" + path separator +
+"file". The path separator is defined in "H-config.h".
+
+#####GParameters
+> "buf" contains the new path.
+> "max" is the maximum number of characters allowed in "buf".
+> "path" is the original path.
+> "file" is the original file.
+
+----------------------------------------------------------------------
+
+#####R=== move_cursor ===
+
+#####GDeclaration
+ void move_cursor(int row, int col);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Move the cursor
+*/
+
+#####GDescription
+Move the cursor to row "row" and column "col".
+
+#####GParameters
+> "row" is the row the cursor is to be moved to.
+> "col" is the column the cursor is to be moved to.
+
+----------------------------------------------------------------------
+
+#####R=== inkey ===
+
+#####GDeclaration
+ char inkey(void);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Get a keypress from the user.
+*
+* This function recognises a few "global parameters". These are variables
+* which, if set to TRUE before calling this function, will have an effect
+* on this function, and which are always reset to FALSE by this function
+* before this function returns. Thus they function just like normal
+* parameters, except that most calls to this function can ignore them.
+*
+* If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
+* and any macro processing in progress will be aborted. This flag is
+* set by the "flush()" function, which does not actually flush anything
+* itself, but rather, triggers delayed input flushing via "inkey_xtra".
+*
+* If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+* keypress is available, instead of waiting for a keypress.
+*
+* If "inkey_base" is TRUE, then all macro processing will be bypassed.
+* If "inkey_base" and "inkey_scan" are both TRUE, then this function will
+* not return immediately, but will wait for a keypress for as long as the
+* normal macro matching code would, allowing the direct entry of macro
+* triggers. The "inkey_base" flag is extremely dangerous!
+*
+* If "inkey_flag" is TRUE, then we will assume that we are waiting for a
+* normal command, and we will only show the cursor if "hilite_player" is
+* TRUE (or if the player is in a store), instead of always showing the
+* cursor. The various "main-xxx.c" files should avoid saving the game
+* in response to a "menu item" request unless "inkey_flag" is TRUE, to
+* prevent savefile corruption.
+*
+* If we are waiting for a keypress, and no keypress is ready, then we will
+* refresh (once) the window which was active when this function was called.
+*
+* Note that "back-quote" is automatically converted into "escape" for
+* convenience on machines with no "escape" key. This is done after the
+* macro matching, so the user can still make a macro for "backquote".
+*
+* Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
+* and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
+* provide support for simple keyboard "macros". These keys are so strange
+* that their loss as normal keys will probably be noticed by nobody. The
+* "ascii 30" key is used to indicate the "end" of a macro action, which
+* allows recursive macros to be avoided. The "ascii 31" key is used by
+* some of the "main-xxx.c" files to introduce macro trigger sequences.
+*
+* Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key,
+* which can be used to give a variety of "sub-commands" which can be used
+* any time. These sub-commands could include commands to take a picture of
+* the current screen, to start/stop recording a macro action, etc.
+*
+* If "angband_term[0]" is not active, we will make it active during this
+* function, so that the various "main-xxx.c" files can assume that input
+* is only requested (via "Term_inkey()") when "angband_term[0]" is active.
+*
+* Mega-Hack -- This function is used as the entry point for clearing the
+* "signal_count" variable, and of the "character_saved" variable.
+*
+* Hack -- Note the use of "inkey_next" to allow "keymaps" to be processed.
+*
+* Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
+* control of the keyboard from the user.
+*/
+
+#####GDescription
+Get a keypress from the user.
+
+----------------------------------------------------------------------
+
+#####R=== cmsg_print ===
+
+#####GDeclaration
+ void cmsg_print(byte color, cptr msg);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Output a message to the top line of the screen.
+*
+* Break long messages into multiple pieces (40-72 chars).
+*
+* Allow multiple short messages to "share" the top line.
+*
+* Prompt the user to make sure he has a chance to read them.
+*
+* These messages are memorised for later reference (see above).
+*
+* We could do "Term_fresh()" to provide "flicker" if needed.
+*
+* The global "msg_flag" variable can be cleared to tell us to
+* "erase" any "pending" messages still on the screen.
+*
+* XXX XXX XXX Note that we must be very careful about using the
+* "msg_print()" functions without explicitly calling the special
+* "msg_print(NULL)" function, since this may result in the loss
+* of information if the screen is cleared, or if anything is
+* displayed on the top line.
+*
+* XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
+* even if no messages are pending. This is probably a hack.
+*/
+
+#####GDescription
+In color "color", output message "msg" to the top line of the screen.
+If the message is blank or has more than 1000 characters, nothing is
+printed. Long messages are split after the 40th character and before
+the 72nd character.
+
+#####GParameters
+> "color" is the color of the message.
+ *****fields.txt*0[colors]
+> "msg" is the message.
+
+----------------------------------------------------------------------
+
+#####R=== msg_print ===
+
+#####GDeclaration
+ void msg_print(cptr msg);
+
+#####GFile
+ util.c
+
+#####GComment
+/* Hack -- for compatibility and easy sake */
+
+#####GDescription
+Print message "msg" in white (see cmsg_print() above).
+
+#####GParameters
+> "msg" is the message.
+
+----------------------------------------------------------------------
+
+#####R=== screen_save ===
+
+#####GDeclaration
+ void screen_save(void);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+ * Save the screen, and increase the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_load()".
+ */
+
+#####GDescription
+Save a screen shot.
+
+----------------------------------------------------------------------
+
+#####R=== screen_load ===
+
+#####GDeclaration
+ void screen_load(void);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+ * Load the screen, and decrease the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_save()".
+ */
+
+#####GDescription
+Load a previously saved screen shot.
+
+----------------------------------------------------------------------
+
+#####R=== c_put_str ===
+
+#####GDeclaration
+ void c_put_str(byte attr, cptr str, int row, int col);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Display a string on the screen using an attribute.
+*
+* At the given location, using the given attribute, if allowed,
+* add the given string. Do not clear the line.
+*/
+
+#####GDescription
+Put string "str" at row "row" and column "col" with attribute "attr".
+
+#####GParameters
+> "attr" is the color of the message.
+ *****fields.txt*0[colors]
+> "msg" is the message.
+> "row" is the row the message is to be printed at.
+> "col" is the column the message is to be printed at.
+
+----------------------------------------------------------------------
+
+#####R=== c_prt ===
+
+#####GDeclaration
+ void c_prt(byte attr, cptr str, int row, int col);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Display a string on the screen using an attribute, and clear
+* to the end of the line.
+*/
+
+#####GDescription
+Clear row "row" from column "col". Put string "str" at "row", "col"
+with attribute "attr".
+
+#####GParameters
+> "attr" is the color of the message.
+ *****fields.txt*0[colors]
+> "msg" is the message.
+> "row" is the row the message is to be printed at.
+> "col" is the column the message is to be printed at.
+
+----------------------------------------------------------------------
+
+#####R=== clear_from ===
+
+#####GDeclaration
+ void clear_from(int row);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Clear part of the screen
+*/
+
+#####GDescription
+Clear the screen from row "row" onwards.
+
+#####GParameters
+> "row" is the first row of the screen to be cleared.
+
+----------------------------------------------------------------------
+
+#####R=== askfor_aux ===
+
+#####GDeclaration
+ bool askfor_aux(char *buf, int len);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Get some input at the cursor location.
+* Assume the buffer is initialized to a default string.
+* Note that this string is often "empty" (see below).
+* The default buffer is displayed in yellow until cleared.
+* Pressing RETURN right away accepts the default entry.
+* Normal chars clear the default and append the char.
+* Backspace clears the default or deletes the final char.
+* ESCAPE clears the buffer and the window and returns FALSE.
+* RETURN accepts the current buffer contents and returns TRUE.
+*/
+
+#####GDescription
+Get string "buf" from the screen. "buf" is to be no more than "len"
+bytes. The string starts at the current cursor position. The length
+can not exceed the number of bytes from the cursor to the end of the
+line. Accept user input until the escape or return key is pressed.
+
+#####GParameters
+> "buf" is the string returned from the screen.
+> "len" is the length of the string. If it is <1 it is forced to 1.
+
+----------------------------------------------------------------------
+
+#####R=== get_string ===
+
+#####GDeclaration
+ bool get_string(cptr prompt, char *buf, int len);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Get a string from the user
+*
+* The "prompt" should take the form "Prompt: "
+*
+* Note that the initial contents of the string is used as
+* the default response, so be sure to "clear" it if needed.
+*
+* We clear the input, and return FALSE, on "ESCAPE".
+*/
+
+#####GDescription
+Print prompt "prompt" at the top-left corner of the screen and return
+response "buf" which will have a maximum length "length". If ESCAPE
+is entered, the function returns FALSE, otherwise it returns TRUE.
+
+#####GParameters
+> "prompt" is the prompt for input.
+> "buf" is the returned response.
+> "len" is the maximum length of the string.
+
+----------------------------------------------------------------------
+
+#####R=== get_check ===
+
+#####GDeclaration
+ bool get_check(cptr prompt);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Verify something with the user
+*
+* The "prompt" should take the form "Query? "
+*
+* Note that "[y/n]" is appended to the prompt.
+*/
+
+#####GDescription
+Ask the user question "prompt" which requires a yes/no answer. The
+prompt appears in the top-left corner of the screen. A response of
+'Y' (either case) returns TRUE. A response of 'N' (either case) or
+ESCAPE returns FALSE.
+
+#####GParameters
+> "prompt" is the question asked. It has a maximum length of 70
+ characters.
+
+----------------------------------------------------------------------
+
+#####R=== get_com_lua ===
+
+#####GDeclaration
+ bool get_com_lua @ get_com(cptr promtp, int *com);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Prompts for a keypress
+*
+* The "prompt" should take the form "Command: "
+*
+* Returns TRUE unless the character is "Escape"
+*/
+
+#####GDescription
+Ask the user for command "prompt" and return the key press "com". A
+response of ESCAPE returns FALSE. All other responses return TRUE.
+
+#####GParameters
+> "prompt" is the prompt for the key press.
+> "com" is the returned key press.
+
+----------------------------------------------------------------------
+
+#####R=== get_quantity ===
+
+#####GDeclaration
+ s32b get_quantity(cptr prompt, s32b max);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+* Request a "quantity" from the user
+*
+* Hack -- allow "command_arg" to specify a quantity
+*/
+
+#####GDescription
+Ask the user for quantity "prompt" of maximum value "max" and return
+a quantity. If the user quantity is higher than the maximum then the
+maximum is returned. If the response is a letter then the maximum is
+returned. If the user quantity is negative then zero is returned.
+
+#####GParameters
+> "prompt" is the prompt for a quantity.
+> "max" is the maximum value allowed.
+
+----------------------------------------------------------------------
+
+#####R=== test_monster_name ===
+
+#####GDeclaration
+ int test_monster_name(cptr name);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+ * Given monster name as string, return the index in r_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -GSN-
+ */
+
+#####GDescription
+Return the monster index for monster with name "name". If no match is
+found then zero is returned.
+
+#####GParameters
+> "name" is the monster name.
+
+----------------------------------------------------------------------
+
+#####R=== test_item_name ===
+
+#####GDeclaration
+ int test_item_name(cptr name);
+
+#####GFile
+ util.c
+
+#####GComment
+/*
+ * Given item name as string, return the index in k_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -DG-
+ */
+
+#####GDescription
+Return the item index for item with name "name". If no match is found
+then zero is returned.
+
+#####GParameters
+> "name" is the item name.
+
+----------------------------------------------------------------------
+
+#####R=== luck ===
+
+#####GDeclaration
+ int luck(int min, int max);
+
+#####GFile
+ xtra1.c
+
+#####GComment
+/*
+ * Return a luck number between a certain range
+ */
+
+#####GDescription
+Return a number for luck between minimum "min" and maximum "max". The
+value begins with the player's current luck. The value is forced to
+be between -30 and +30. 30 is added to give a value between 0 and 60.
+The value is multiplied by the range (maximum - minimum) and divided
+by 60. The value is increased by the minimum. The value is returned.
+
+For example, if the player's current luck is 15, the minimum is -10,
+and the maximum is 10 (range 20), then the value returned is
+(45 * 20) / 60 which is 900 / 60 which is 15 + the minimum -10 gives
+a returned value of 5.
+
+#####GParameters
+> "min" is the minimum luck.
+> "max" is the maximum luck. Beware: this should be greater than the
+ minimum but it is not checked!
+
+----------------------------------------------------------------------
+
+#####R=== get_player_race_name ===
+
+#####GDeclaration
+ cptr get_player_race_name(int pr, int ps);
+
+#####GFile
+ util.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the name for player race "pr" and player sub-race "ps".
+
+#####GParameters
+> "pr" is the index for player race.
+> "ps" is the index for player sub-race.
+
+----------------------------------------------------------------------
+
+#####R=== quit ===
+
+#####GDeclaration
+ void quit(cptr str);
+
+#####GFile
+ z-util.c
+
+#####GComment
+/*
+ * Exit (ala "exit()"). If 'str' is NULL, do "exit(0)".
+ * If 'str' begins with "+" or "-", do "exit(atoi(str))".
+ * Otherwise, plog() 'str' and exit with an error code of -1.
+ * But always use 'quit_aux', if set, before anything else.
+ */
+
+#####GDescription
+Quit the game. If "str" is a string then write the string to the
+error file or screen. If "str" is a number then exit with the
+number as the exit code.
+
+#####GParameters
+> "str" is an error message or exit code.
+
+----------------------------------------------------------------------
+
+#####R=== dump_hooks ===
+
+#####GDeclaration
+ void dump_hooks();
+
+#####GFile
+ plots.c
+
+#####GComment
+(none)
+
+#####GDescription
+Print the name and type (C or Lua) of hooks in the hook list.
+
+----------------------------------------------------------------------
+
+#####R=== add_hook_script ===
+
+#####GDeclaration
+ void add_hook_script(int h_idx, char *script, cptr name);
+
+#####GFile
+ plots.c
+
+#####GComment
+(none)
+
+#####GDescription
+To hook list with index "h_idx", add a script with script file
+"script" and name "name" as a Lua hook if a hook with that name
+does not already exist.
+
+#####GParameters
+> "h_idx" is the index of the hook list in the array of hook lists.
+> "script" is the name of the script file.
+> "name" is the name of the hook to be added.
+
+----------------------------------------------------------------------
+
+#####R=== del_hook_name ===
+
+#####GDeclaration
+ void del_hook_name(int h_idx, cptr name);
+
+#####GFile
+ plots.c
+
+#####GComment
+(none)
+
+#####GDescription
+Search hook list with index "h_idx" and remove the hook with name
+"name".
+
+#####GParameters
+> "h_idx" is the index of the hook list in the array of hook lists.
+> "name" is the name of the hook to be removed.
+
+----------------------------------------------------------------------
+
+#####R=== pern_dofile ===
+
+#####GDeclaration
+ bool pern_dofile(char *file);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Parse the Lua script file "file".
+
+#####GParameters
+> "file" is the Lua script file to be parsed.
+
+----------------------------------------------------------------------
+
+#####R=== intMod ===
+
+#####GDeclaration
+ s32b intMod(s32b a, s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of operation "a" mod "b" (a % b).
+
+#####GParameters
+> "a" is a number.
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== intAnd ===
+
+#####GDeclaration
+ s32b intAnd(s32b a, s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of bitwise operation "a" AND "b" (a & b).
+
+#####GParameters
+> "a" is a number.
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== intOr ===
+
+#####GDeclaration
+ s32b intOr(s32b a, s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of bitwise operation "a" OR "b" (a | b).
+
+#####GParameters
+> "a" is a number.
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== intXor ===
+
+#####GDeclaration
+ s32b intXor(s32b a, s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of bitwise operation "a" XOR "b" (a ^ b).
+
+#####GParameters
+> "a" is a number.
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== intShiftl ===
+
+#####GDeclaration
+ s32b intShiftl(s32b a, s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of bitwise operation "a" << "b".
+
+#####GParameters
+> "a" is a number.
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== intShiftr ===
+
+#####GDeclaration
+ s32b intShiftr(s32b a, s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of bitwise operation "a" >> "b".
+
+#####GParameters
+> "a" is a number.
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== intBitNot ===
+
+#####GDeclaration
+ s32b intBitNot(s32b b);
+
+#####GFile
+ script.c
+
+#####GComment
+(none)
+
+#####GDescription
+Return the result of bitwise operation NOT "b" (~ b).
+
+#####GParameters
+> "b" is a number.
+
+----------------------------------------------------------------------
+
+#####R=== register_savefile ===
+
+#####GDeclaration
+ void register_savefile(int num);
+
+#####GFile
+ loadsave.c
+
+#####GComment
+/*
+ * Add num slots to the savefile
+ */
+
+#####GDescription
+Add "num" slots to the save file.
+
+#####GParameters
+> "num" is the number of slots to add to the savefile. If num is <0
+ then "num" is forced to zero.
+
+----------------------------------------------------------------------
+
+#####R=== save_number_key ===
+
+#####GDeclaration
+ void save_number_key(char *key, s32b val);
+
+#####GFile
+ util.c
+
+#####GComment
+(none)
+
+#####GDescription
+Save the length of key "key", the key itself, and the value "val" as
+bytes in the savefile.
+
+#####GParameters
+> "key" is the key string for the value.
+> "val" is the value to be saved.
+
+----------------------------------------------------------------------
+
+
+
+Back to the *****lua.hlp*0[lua help index] .
+
+
+ [[[[[gThis file by Chris Hadgis]
+
diff --git a/lib/help/luckspoi.txt b/lib/help/luckspoi.txt
new file mode 100644
index 00000000..50466902
--- /dev/null
+++ b/lib/help/luckspoi.txt
@@ -0,0 +1,112 @@
+|||||oy
+~~~~~01|Luck (spoiler)
+~~~~~02|Spoilers|Luck
+#####RLuck
+
+"You've got to ask yourself one question: do I feel lucky? Well do ya
+punk?"
+ --Clint Eastwood (Dirty Harry)
+
+#####G1. Starting luck
+Most races start with no luck points. Those that start with luck are listed
+below from most luck to least luck.
+
+Hobbit (+5);
+Maia (+4);
+Gnome, Dunadan, Thunderlord (+2);
+Beorning (+1);
+Half-Ogre, Dark Elf, Ent (-2);
+Orc (-3);
+Troll (-4);
+Petty Dwarf, DeathMold, Yeek (-5).
+
+Most subraces start with no luck points. Those that start with luck are listed
+below from most luck to least luck.
+
+Barbarian (+1);
+Spectre, Skeleton (-3);
+Zombie (-4).
+
+So, a Hobbit starts with 5 luck points, a Beorning Barbarian starts with 2 luck
+points, while a Yeek Zombie starts with -9 luck points.
+
+#####G2. Descriptions
+Drinking a potion of self-knowledge will tell you how lucky or unlucky your
+character is.
+
+DESCRIPTION VALUE
+incredibly unlucky less than -27
+extremely unlucky less than -18
+very unlucky less than -9
+unlucky less than 0
+normal luck 0
+lucky more than 0
+very lucky more than 9
+extremely lucky more than 18
+incredibly lucky more than 27
+
+Luck < -30 is treated as -30 and luck > 30 is treated as +30.
+
+#####G3. Items affecting luck
+Elven cloaks give a bonus to luck.
+
+Only one type of ego item is lucky, or perhaps that should be unlucky.
+All Morgul items have -10 luck.
+
+Artifacts can affect your luck. Beware: not all artifacts give you good luck.
+Listed below are the artifacts which affect your luck.
+
+The Phial of Galadriel (+4)
+The Arkenstone of Thrain (+3)
+The Ring of Power 'Narya' (+1)
+The Ring of Power 'Nenya' (+2)
+The Ring of Power 'Vilya' (+3)
+The Metal Cap of Thengel (+3)
+The Shadow Cloak of Luthien (+2)
+The Set of Cesti of Fingolfin (+4)
+The Long Sword 'Anduril' (+4)
+The Long Bow of Bard (+2)
+The Mage Staff of Eternity (+12)
+The Harp of Maglor (+3)
+The Drum of the Sky (+2)
+The Harp of Daeron (+1)
+The Long Sword of Eternity (+10)
+The Robe of Great Luck (+60)
+The Heavy Crossbow of Eternity (+5)
+The Iron Helm of Knowledge (-6)
+The Shield of Deflection of Gil-galad (+5)
+The Set of Gauntlets of Eol (+3)
+The Demonblade of Gothmog (-20)
+The Long Sword 'Durandil' (+3)
+The Phial of Undeath (-5)
+The Ring of Phasing (+15)
+The Blue Stone 'Toris Mejistos' (+2)
+The Silver Bolt 'Stone-biter' (+3)
+The Elven Cloak of Mellyrn (+4)
+The Set of Cesti 'Skycleaver' (+1)
+
+#####G4. What does it affect.
+It affects your chance to-hit in ranged combat and melee combat.
+
+It affects your chance of critical hits in ranged combat and melee combat.
+
+It affects your chance of being missed by a monster attack. This effectively
+changes armour class by -13 (incredibly unlucky) to +13 (incredibly lucky).
+
+It affects the rarity of special objects, artifacts, and ego items.
+
+It affects the number of wishes on a staff of wishing.
+
+It affects the base level for applying magic to an item.
+
+It affects the chance of an item being good or great.
+
+It affects the chance of creating a special object.
+
+It prevents the death fate if you are lucky.
+
+It affects the number of skill points you receive when quaffing the Potion of
+Learning.
+
+
+ Written for ToME 2.0 by Chris Hadgis
diff --git a/lib/help/m_air.txt b/lib/help/m_air.txt
new file mode 100644
index 00000000..ee9fa8d0
--- /dev/null
+++ b/lib/help/m_air.txt
@@ -0,0 +1,42 @@
+|||||oy
+~~~~~01|Magic|Air School
+~~~~~02|Air Magic
+~~~~~03|Skills|Air - Spell Info
+#####R === ToME Magic - Air School ===
+
+The air school of magic contains spells where the element of air is used
+to create the final spell effect. There are rumours of a "Tome of the Blowing
+Wind" which contains all the air school spells within its bindings.
+
+Worshipping the God Manwe Sulimo also gives the ability to cast spells from
+the air school at a level of 2/3 of your prayer level. E.g. if the skill
+"Spirituality: Prayer" is at level 12, you can cast up to level 8 air school
+spells.
+
+#####BAir Spells
+There are six spells available for the air school. These spells are:
+1. [[[[[BNoxious Cloud] (school level 3)
+ Creates a cloud of poison.
+ The cloud will persist for some turns, damaging all monsters passing by.
+ At spell level 30 it turns into a thick gas attacking all living beings.
+2. [[[[[BPoison Blood] (school level 12)
+ Grants resist poison.
+ At spell level 15 it provides poison branding to the wielded weapon.
+3. [[[[[BInvisibility] (school level 16)
+ Grants invisibility.
+4. [[[[[BSterilize] (school level 20)
+ Prevents explosive breeding for a while.
+5. [[[[[BWings of Winds] (dual school level 22)
+ Grants the power of levitation.
+ At spell level 16 it grants the power of controlled flight.
+#####v Your Air and Conveyance skills must have reached a combined average level
+#####v of 22 in order to cast this spell.
+6. [[[[[BThunderstorm] (dual school level 25)
+ Charges up the air around you with electricity.
+ Each turn it will throw a bolt of thunder at a random monster in sight.
+ This thunder does 3 types of damage:
+ one third of lightning
+ one third of sound
+ and one third of light.
+#####v Your Air and Nature skills must have reached a combined average level
+#####v of 25 in order to cast this spell.
diff --git a/lib/help/m_convey.txt b/lib/help/m_convey.txt
new file mode 100644
index 00000000..91ed8556
--- /dev/null
+++ b/lib/help/m_convey.txt
@@ -0,0 +1,71 @@
+|||||oy
+~~~~~01|Magic|Conveyance School
+~~~~~02|Conveyance Magic
+~~~~~03|Skills|Conveyance - Spell Info
+#####R === ToME Magic - Conveyance School ===
+
+The conveyance school of magic contains spells where the forces of space are
+manipulated by the spell. There are rumours of a "Tome of of Translocation"
+which contains all the conveyance school spells within its bindings.
+
+Worshipping the God Manwe Sulimo also gives the ability to cast spells from
+the conveyance school at a level of 1/2 of your prayer level. E.g. if the
+skill "Spirituality: Prayer" is at level 10, you can cast up to level 5
+conveyance school spells.
+
+#####sConveyance Spells
+There are six spells available for the conveyance school. These Spells are:
+1. [[[[[sPhase Door] (school level 1)
+ Teleports you on a small scale range.
+ At spell level 30 it creates void jumpgates.
+2. [[[[[sDisarm] (school level 3)
+ Destroys doors and disarms traps in adjacent tiles.
+ At spell level 10 it unlocks doors and disarms traps.
+3. [[[[[sTeleportation] (school level 10)
+ Teleports you around the level.
+ The casting time decreases with level.
+4. [[[[[sTeleport Away] (school level 23)
+ Teleports a line of monsters away.
+ At spell level 10 it turns into a ball.
+ At spell level 20 it teleports all monsters in sight.
+5. [[[[[sRecall] (school level 30)
+ Cast on yourself, it will recall you to the surface/dungeon.
+ Cast at a monster, it will make you swap positions with the monster.
+ Cast at an object, it will fetch the object to you (note that you must have
+ an empty space under your feet for the object to fall onto).
+6. [[[[[sProbability Travel] (school level 35)
+ Renders you unstable. When you hit a wall you travel through it and
+ instantly appear on the other side of it. You can also float up and down
+ at will.
+
+#####GAir spells that can be cast with Conveyance skill
+
+1. [[[[[BWings of Winds] (dual school level 22)
+ Grants the power of levitation.
+ At spell level 16 it grants the power of controlled flight.
+#####v Your Air and Conveyance skills must have reached a combined average level
+#####v of 22 in order to cast this spell.
+
+#####GTemporal spells that can be cast with Conveyance skill
+
+1. [[[[[sBanishment] (dual school level 30)
+ Disrupts the space/time continuum in your area and teleports all monsters
+ away.
+ At spell level 15 it also may lock them in a time bubble for some turns.
+#####v Your Temporal and Conveyance skills must have reached a combined average level
+#####v of 30 in order to cast this spell.
+
+#####GMeta spells that can be cast with Conveyance skill
+
+1. [[[[[sTracker] (dual school level 30)
+ Tracks down the last teleportation that happened on the level and teleports
+ you to it.
+#####v Your Meta and Conveyance skills must have reached a combined average level
+#####v of 30 in order to cast this spell.
+
+#####GUdun spells that can be cast with Conveyance skill
+
+1. [[[[[DWraithform] (dual school level 30)
+ Turns you temporarily into an immaterial being.
+#####v Your Udun and Conveyance skills must have reached a combined average level
+#####v of 30 in order to cast this spell.
diff --git a/lib/help/m_demono.txt b/lib/help/m_demono.txt
new file mode 100644
index 00000000..cb32d360
--- /dev/null
+++ b/lib/help/m_demono.txt
@@ -0,0 +1,44 @@
+|||||oy
+~~~~~01|Magic|Demonology School
+~~~~~02|Demonology Magic
+~~~~~03|Skills|Demonology - Spell Info
+#####R === ToME Magic - Demonology ===
+
+Available only to Demonologists, or those sufficiently corrupted with
+demon-like powers, this school contains spells where demonic energies
+are used to create mainly devastating effects.
+
+#####oDemonic equipment
+Unlike other magic schools, the spells of a Demonologist are not written
+in spell books, but are contained within items of demonic origin known as
+Demonblades, Demonshields, and Demonhorns (helms), which when wielded allow
+the Demonologist to cast spells; each piece of equipment holds three spells.
+
+#####oDemonblade spells
+1. [[[[[oDemon Blade] (school level 1)
+ Imbues your blade with fire to deal more damage.
+ At level 30 it deals hell fire damage.
+ At level 45 it spreads over a 1 radius zone around your target.
+2. [[[[[oDemon Madness] (school level 10)
+ Fire 2 balls in opposite directions of randomly chaos, confusion or charm.
+3. [[[[[oDemon Field] (school level 20)
+ Fires a lingering cloud of deadly nexus over a radius of 7.
+
+#####oDemonshield spells
+1. [[[[[oDoom Shield] (school level 1)
+ Raises a mirror of pain around you, doing very high damage to your foes
+ that dare hit you, but greatly reduces your armour class.
+2. [[[[[oDemon Cloak] (school level 20)
+ Raises a mirror that can reflect bolts and arrows for a time.
+3. [[[[[oUnholy Word] (school level 25)
+ Kills a pet to heal you. There is a chance that the pet won't die but will
+ turn against you. This chance will decrease with higher spell levels.
+
+#####oDemonhorn spells
+1. [[[[[oSummon Demon] (school level 5)
+ Summons a leveled demon to your side.
+ At level 35 it summons a high demon.
+2. [[[[[oDischarge Minion] (school level 10)
+ The targeted pet will explode in a burst of gravity.
+3. [[[[[oControl Demon] (school level 25)
+ Attempts to control a demon.
diff --git a/lib/help/m_divin.txt b/lib/help/m_divin.txt
new file mode 100644
index 00000000..9d3455fc
--- /dev/null
+++ b/lib/help/m_divin.txt
@@ -0,0 +1,37 @@
+|||||oy
+~~~~~01|Magic|Divination School
+~~~~~02|Divination Magic
+~~~~~03|Skills|Divination - Spell Info
+#####R === ToME Magic - Divination School ===
+
+The divination school of magic contains spells where magic is used to
+psychically gain information about things. There are rumours of a "Tome of
+Knowledge" which contains all the divination school spells within its bindings.
+
+Worshipping the God Eru Iluvatar also gives the ability to cast spells from
+the divination school at a level of 2/3 of your prayer level. E.g. if the
+skill "Spirituality: Prayer" is at level 12, you can cast up to level 8
+divination school spells.
+
+#####sDivination Spells
+There are six spells available for the divination school. These Spells are:
+1. [[[[[sSense Monsters] (school level 1)
+ Detects all monsters near you.
+ At spell level 30 it allows you to sense monster minds for a while.
+2. [[[[[sSense Hidden] (school level 5)
+ Detects the traps in a certain radius around you.
+ At spell level 15 it allows you to sense invisible monsters for a while.
+3. [[[[[sIdentify] (school level 8)
+ Asks for an object and identifies it.
+ At spell level 17 it identifies all objects in the inventory.
+ At spell level 27 it identifies all objects in the inventory and in a
+ radius on the floor, as well as probing monsters in that radius.
+4. [[[[[sReveal Ways] (school level 9)
+ Detects the doors/stairs/ways in a certain radius around you.
+5. [[[[[sVision] (school level 15)
+ Detects the layout of the surrounding area.
+ At spell level 25 it maps and lights the whole level.
+6. [[[[[sGreater Identify] (school level 35)
+ Asks for an object and fully identifies it, providing the full list of
+ powers (as with a scroll of *Identify*).
+ Cast at yourself, it will reveal your powers (Self Knowledge).
diff --git a/lib/help/m_earth.txt b/lib/help/m_earth.txt
new file mode 100644
index 00000000..9c5623c3
--- /dev/null
+++ b/lib/help/m_earth.txt
@@ -0,0 +1,35 @@
+|||||oy
+~~~~~01|Magic|Earth School
+~~~~~02|Earth Magic
+~~~~~03|Skills|Earth - Spell Info
+#####R === ToME Magic - Earth School ===
+
+The earth school of magic contains spells where the element of earth is used
+to create the final spell effect. There are rumours of a "Tome of the
+Impenetrable Earth" which contains all the earth school spells within its
+bindings.
+
+Worshipping the God Tulkas or the Goddess Yavanna Kementari also gives the
+ability to cast spells from the earth school, at a level of 4/5 or 1/2,
+respectively, of your prayer level. E.g. if the skill "Spirituality: Prayer"
+is at level 10, a worshipper of Tulkas can cast up to level 8 earth school
+spells, whereas a worshipper of Yavanna can cast up to level 5 earth school
+spells.
+
+#####uEarth Spells
+There are five spells available for the earth school. These Spells are:
+1. [[[[[uStone Skin] (school level 1)
+ Creates a shield of earth around you to protect you.
+ At spell level 25 it starts dealing damage to attackers.
+2. [[[[[uDig] (school level 12)
+ Digs a hole in a wall much faster than any shovels.
+3. [[[[[uStone Prison] (school level 25)
+ Creates a prison of walls around you.
+ At spell level 10 it allows you to target a monster.
+4. [[[[[uShake] (school level 27)
+ Creates a localised earthquake.
+ At spell level 10 it can be targeted at any location.
+5. [[[[[uStrike] (school level 30)
+ Creates a micro-ball of force that will push monsters backwards.
+ If the monster is caught near a wall, it'll be crushed against it.
+ At spell level 12 it turns into a ball of radius 1.
diff --git a/lib/help/m_fire.txt b/lib/help/m_fire.txt
new file mode 100644
index 00000000..7ddda433
--- /dev/null
+++ b/lib/help/m_fire.txt
@@ -0,0 +1,49 @@
+|||||oy
+~~~~~01|Magic|Fire School
+~~~~~02|Fire Magic
+~~~~~03|Skills|Fire - Spell Info
+#####R === ToME Magic - Fire School ===
+
+The fire school of magic contains spells where the element of fire is used
+to create the final spell effect. There are rumours of a "Tome of the Eternal
+Flame" which contains all the fire school spells within its bindings.
+
+#####RFire Spells
+There are five spells available for the fire school. These spells are:
+1. [[[[[RGlobe of Light] (school level 1)
+ Creates a globe of pure light.
+ At spell level 3 it starts damaging monsters.
+ At spell level 15 it starts creating a more powerful kind of light.
+2. [[[[[RFire Golem] (dual school level 7)
+ Creates a fiery golem and controls it.
+ During the control the available keylist is:
+ Movement keys: move the golem (depending on its speed it can move more
+ than one square).
+ , : pickup all items on the floor
+ d : drop all carried items
+ i : list all carried items
+ m : end the possession/use golem powers
+ Most of the other keys are disabled; you cannot interact with your
+ real body while controlling the golem.
+ But to cast the spell you will need a lantern or a wooden torch to create
+ the golem.
+#####v Your Fire and Mind skills must have reached a combined average level
+#####v of 7 in order to cast this spell.
+3. [[[[[RFireflash] (school level 10)
+ Conjures a ball of fire to burn your foes to ashes.
+ At spell level 20 it turns into a ball of holy fire.
+4. [[[[[RFirewall] (school level 15)
+ Creates a fiery wall to incinerate monsters stupid enough to attack you.
+ At spell level 6 it turns into a wall of hell fire.
+5. [[[[[RFiery Shield] (school level 20)
+ Creates a shield of fierce flames around you.
+ At spell level 8 it turns into a greater kind of flame that cannot be
+ resisted by your foes.
+
+
+#####GUdun spells that can be cast with Fire skill
+
+1. [[[[[DFlame of Udun] (dual school level 35)
+ Turns you temporarily into a powerful Balrog.
+#####v Your Fire and Udun skills must have reached a combined average level
+#####v of 35 in order to cast this spell.
diff --git a/lib/help/m_geoman.txt b/lib/help/m_geoman.txt
new file mode 100644
index 00000000..97c1ac1a
--- /dev/null
+++ b/lib/help/m_geoman.txt
@@ -0,0 +1,75 @@
+|||||oy
+~~~~~01|Magic|Geomancy
+~~~~~02|Geomancer|Geomancy spells
+~~~~~03|Skills|Geomancy - Spell Info
+#####R === ToME Magic - Geomancy ===
+Geomancy harnesses the power of nature to awesome effect. Therefore neither
+books nor light are necessary, and as the geomancers's skill increases, so do
+his powers over the elements of nature.
+
+Because Geomancy relies so heavily on the environment, you will need
+sufficient knowledge of the elemental skills (Earth, Air, Fire and Water) in
+order to cast some of the spells, and the exact effects of a spell often
+depend upon your levels in the elemental skills and your current surroundings.
+
+The powers are accessed using the 'm' key and then selecting 'Use Geomancy';
+they are cast with spell points, like normal spells, which can be increased
+as usual through the *****skills.txt*21[Magic] skill.
+
+1. [[[[[RCall] [[[[[GThe] [[[[[BElements] (Level 1) Cost:2
+ Randomly creates various elements around you.
+ The chance for each type of element is controlled by your level in the
+ corresponding skill.
+ At level 17 it can be targeted.
+
+2. [[[[[UChannel] [[[[[GElements] (Level 3) Cost:3
+ Draws on the caster's immediate environs to form an attack or other effect.
+ Grass/Flower heals.
+ Water creates water bolt attacks.
+ Ice creates ice bolt attacks.
+ Sand creates a wall of burning sand around you, but the wall is thick and
+ blinds you.
+ Lava creates fire bolt attacks.
+ Deep lava creates fire ball attacks.
+ Chasm creates darkness bolt attacks.
+ At Earth level 18, darkness becomes nether.
+ At Water level 8, water attacks become beams with a striking effect.
+ At Water level 12, ice attacks become balls of ice shards.
+ At Water level 18, water attacks push monsters back.
+ At Fire level 15, fire becomes hellfire.
+
+3. [[[[[RElemental] [[[[[BWave] (Level 15) Cost:15
+ Draws on an adjacent special square to project a slow-moving wave of that
+ element in that direction.
+ Abyss squares cannot be channeled into a wave.
+
+4. [[[[[UElemental] [[[[[RMinion] (Level 20) Cost:40
+ Summons a minion from a nearby element.
+ Walls can summon Earth elmentals, Xorns and Xarens.
+ Dark Pits can summon Air elementals, Ancient blue dragons, Great Storm Wyrms
+ and Sky Drakes.
+ Sandwalls and lava can summon Fire elementals and Ancient red dragons.
+ Icewalls and water can summon Water elementals, Water trolls and Water
+ demons.
+
+5. [[[[[GVaporize] (Level 4) Cost:3
+ Draws upon your immediate environs to form a cloud of damaging vapors.
+#####v You must have reached at least level 4 in the Air skill to cast this
+#####v spell.
+
+6. [[[[[UGeolysis] (Level 7) Cost:15
+ Burrows deeply and slightly at random into a wall, leaving behind tailings
+ of various different sorts of walls in the passage.
+#####v You must have reached at least level 7 in the Earth skill to cast this
+#####v spell.
+
+7. [[[[[BDripping Tread] (Level 10) Cost:15
+ Causes you to leave random elemental forms behind as you walk.
+#####v You must have reached at least level 10 in the Water skill to cast this
+#####v spell.
+
+8. [[[[[UGrow Barrier] (Level 12) Cost:30
+ Creates impassable terrain (walls, trees, etc.) around you.
+ At air level 20 it can be projected around another area.
+#####v You must have reached at least level 12 in the Earth skill to cast this
+#####v spell.
diff --git a/lib/help/m_mana.txt b/lib/help/m_mana.txt
new file mode 100644
index 00000000..d4634e0e
--- /dev/null
+++ b/lib/help/m_mana.txt
@@ -0,0 +1,39 @@
+|||||oy
+~~~~~01|Magic|Mana School
+~~~~~02|Mana Magic
+~~~~~03|Skills|Mana - Spell Info
+#####R === ToME Magic - Mana School ===
+
+The mana school of magic contains spells where the raw force of magic is used
+to create the final spell effect. There are rumours of a "Tome of Magical
+Energy" which contains all the mana school spells within its bindings.
+
+Worshipping the God Eru Iluvatar also gives the ability to cast spells from
+the mana school at a level of 1/2 of your prayer level. E.g. if the skill
+"Spirituality: Prayer" is at level 10, you can cast up to level 5 mana school
+spells.
+
+#####sMana Spells
+There are four spells available for the mana school. These spells are:
+1. [[[[[sManathrust] (school level 1)
+ Conjures up mana into a powerful bolt.
+ The damage is irresistible and will increase with spell level.
+2. [[[[[sRemove Curses] (school level 10)
+ Removes curses of worn objects.
+ At spell level 20, removes heavy curses.
+3. [[[[[sElemental Shield] (school level 20)
+ Provides resistance to the four basic elements.
+4. [[[[[sDisruption Shield] (school level 45)
+ Uses mana instead of hitpoints to take damage.
+ At spell level 5 switches to Globe of Invulnerability.
+ The spell breaks as soon as a melee, shooting, throwing or magical skill
+ action is attempted, and lasts only a short time otherwise.
+
+
+#####GUdun spells that can be cast with Mana skill
+
+1. [[[[[DDrain] (dual school level 1)
+ Drains the mana contained in wands, staves and rods to increase yours.
+#####v Your Mana and Udun skills must have reached a combined average level
+#####v of 1 in order to cast this spell.
+
diff --git a/lib/help/m_meta.txt b/lib/help/m_meta.txt
new file mode 100644
index 00000000..f5570a2a
--- /dev/null
+++ b/lib/help/m_meta.txt
@@ -0,0 +1,75 @@
+|||||oy
+~~~~~01|Magic|Meta School
+~~~~~02|Meta Magic
+~~~~~03|Skills|Meta - Spell Info
+#####R === ToME Magic - Meta School ===
+
+The meta school of magic contains spells where the raw forces of magic are
+manipulated by the spell. There are rumours of a "Tome of Meta Spells" which
+contains all the meta school spells within its bindings.
+
+Worshipping the God Manwe Sulimo also gives the ability to cast spells from
+the meta school at a level of 1/3 of your prayer level. E.g. if the skill
+"Spirituality: Prayer" is at level 15, you can cast up to level 5 meta school
+spells.
+
+#####sMeta Spells
+There are five spells available for the meta school. These spells are:
+1. [[[[[sRecharge] (school level 5)
+ Taps the ambient mana to recharge an object's power (charges or mana).
+2. [[[[[sDisperse Magic] (school level 15)
+ Dispels a lot of magic that can affect you, be it good or bad:
+ Spell Level 1: blindness and light.
+ Spell Level 5: confusion and hallucination.
+ Spell Level 10: speed (either bad or good) and light speed.
+ Spell Level 15: stunning, meditation and cuts.
+ Spell Level 20: heroism, super heroism, blessing, shields, fear, parasites
+ and mimicry.
+3. [[[[[sSpellbinder] (school level 20)
+ Stores spells in a trigger.
+ When the condition is met all spells fire off at the same time. This spell
+ takes a long time to cast so you are advised to prepare it in a safe area.
+ Also it will use the mana for the Spellbinder and the mana for the selected
+ spells.
+4. [[[[[sTracker] (dual school level 30)
+ Tracks down the last teleportation that happened on the level and teleports
+ you to it.
+#####v Your Meta and Conveyance skills must have reached a combined average level
+#####v of 30 in order to cast this spell.
+5. [[[[[sInertia Control] (school level 37)
+ Changes the energy flow of a spell to be continuously recasted at a given
+ interval. The inertia-controlled spell reduces your maximum mana by four
+ times its cost.
+
+#####R=== List of Controllable Spells ===
+ Armor of Fear
+ Banishment
+ Disperse Magic
+ Disruption Shield
+ Elemental Shield
+ Ent's Potion
+ Essence of Speed
+ Fiery Shield
+ Flame of Udun
+ Globe of Light
+ Grow Trees
+ Ice Storm
+ Invisibility
+ Phase Door
+ Poison Blood
+ Probability Travel
+ Recovery
+ Regeneration
+ Remove Curses
+ Reveal Ways
+ Sense Hidden
+ Sense Monsters
+ Shake
+ Stone Skin
+ Teleportation
+ Thunderstorm
+ Tidal Wave
+ Vapor
+ Vision
+ Wings of Winds
+ Wraithform
diff --git a/lib/help/m_mimic.txt b/lib/help/m_mimic.txt
new file mode 100644
index 00000000..79d39521
--- /dev/null
+++ b/lib/help/m_mimic.txt
@@ -0,0 +1,33 @@
+|||||oy
+~~~~~01|Magic|Mimicry
+~~~~~02|Mimic|Mimicry powers
+~~~~~03|Skills|Mimicry - mimicry powers
+#####R === ToME Magic - Mimicry ===
+Mimics can alter their form using special cloaks called cloaks of mimicry.
+Even though this ability can give them access to increased stats, regeneration,
+speed and resistances, there is always the risk of the shape-change going
+horribly wrong, turning the character into an abomination for some time.
+Also, you should keep in mind that any racial power or stat modifier is
+overridden during the shape-shift.
+
+As your mimicry skill increases, your character is less likely to spoil
+the transformation attempt and may even gain other powers, such as making
+his colouration match his surroundings (becoming in all respects invisible)
+or even stranger abilities... rumours abound about Tarka the Chameleon and
+her two pairs of boots of speed.
+
+The powers are accessed using the 'm' key and then selecting 'Use Mimicry';
+they are cast with spell points, like normal spells, which can be increased
+as usual through the *****skills.txt*21[Magic] skill.
+
+1. [[[[[sMimic] (Level 1) Cost:2
+ Allows you to mimic the creature whose cloak you are wearing for a time.
+2. [[[[[sInvisibility] (Level 10) Cost:6
+ Turns you invisible for a time.
+3. [[[[[sLegs Mimicry] (Level 25) Cost:20
+ Gives you an additional set of legs for a time.
+4. [[[[[sWall Mimicry] (Level 30) Cost:40
+ Allows you to move within walls, and [[[[[Bonly] walls, for a time.
+[[[[[v Be careful you don't get stuck in a wall when it runs out.]
+5. [[[[[sArms Mimicry] (Level 35) Cost:100
+ Gives you an additional set of arms for a time.
diff --git a/lib/help/m_mind.txt b/lib/help/m_mind.txt
new file mode 100644
index 00000000..b6c1196f
--- /dev/null
+++ b/lib/help/m_mind.txt
@@ -0,0 +1,49 @@
+|||||oy
+~~~~~01|Magic|Mind School
+~~~~~02|Mind Magic
+~~~~~03|Skills|Mind - Spell Info
+#####R === ToME Magic - Mind School ===
+
+The mind school of magic contains spells which alter the mind. There are
+rumours of a "Tome of the Mind" which contains all the mind school spells
+within its bindings.
+
+Worshipping the God Eru Iluvatar or the God Melkor Bauglir also gives the
+ability to cast spells from the mind school at a level of 1/3 of your prayer
+level. E.g. if the skill "Spirituality: Prayer" is at level 12, you can cast
+up to level 4 mind school spells.
+
+#####sMind Spells
+There are four spells available for the mind school. These spells are:
+1. [[[[[sCharm] (school level 1)
+ Tries to manipulate the mind of a monster to make it friendly.
+ At spell level 15 it turns into a ball.
+ At spell level 35 it affects all monsters in sight.
+2. [[[[[sConfuse] (school level 5)
+ Tries to manipulate the mind of a monster to confuse it.
+ At spell level 15 it turns into a ball.
+ At spell level 35 it affects all monsters in sight.
+3. [[[[[sArmor of Fear] (school level 10)
+ Creates a shield of pure fear around you. Any monster attempting to hit
+ you must save or flee.
+4. [[[[[sStun] (school level 15)
+ Tries to manipulate the mind of a monster to stun it.
+ At spell level 20 it turns into a ball.
+
+#####GFire spells that can be cast with Mind skill
+
+1. [[[[[RFire Golem] (school level 7)
+ Creates a fiery golem and controls it.
+ During the control the available keylist is:
+ Movement keys: move the golem (depending on its speed it can move more
+ than one square).
+ , : pickup all items on the floor
+ d : drop all carried items
+ i : list all carried items
+ m : end the possession/use golem powers
+ Most of the other keys are disabled; you cannot interact with your
+ real body while controlling the golem.
+ But to cast the spell you will need a lantern or a wooden torch to create
+ the golem.
+#####v Your Fire and Mind skills must have reached a combined average level
+#####v of 7 in order to cast this spell.
diff --git a/lib/help/m_mindcr.txt b/lib/help/m_mindcr.txt
new file mode 100644
index 00000000..4f420656
--- /dev/null
+++ b/lib/help/m_mindcr.txt
@@ -0,0 +1,54 @@
+|||||oy
+~~~~~01|Magic|Mindcraft
+~~~~~02|Mindcrafter|Mindcraft powers
+~~~~~03|Skills|Mindcraft - Spell Info
+#####R === ToME Magic - Mindcraft ===
+Mindcrafting uses the power of the mind to cast its spells. Therefore neither
+books nor light are necessary, and as the mindcrafter's skill increases, so do
+the powers of his mind.
+
+The powers are accessed using the 'm' key and then selecting 'Use Mindcraft';
+they are cast with spell points, like normal spells, which can be increased
+as usual through the *****skills.txt*21[Magic] skill.
+
+1. [[[[[sPrecognition] (Level 1) Cost:1
+ Detects monster minds around you.
+ At level 5 it also detects traps.
+ At level 15 it also allows you to see invisible monsters for a time.
+ At level 20 it maps out the surrounding area.
+ At level 25 it gives you ESP for a time.
+ At level 30 it also detects objects and treasure.
+ At level 45 it fully shows the entire level.
+2. [[[[[sNeural blast] (Level 2) Cost:1
+ Fires a bolt or beam, to stun and damage monsters.
+3. [[[[[sMinor Displacement] (Level 3) Cost:2
+ Teleports you a short distance.
+ At level 25 it grants the ability to create void jumpgates.
+4. [[[[[sMajor Displacement] (Level 7) Cost:6
+ Teleports you a good distance.
+ At level 30 it also banishes the monsters around you.
+5. [[[[[sDomination] (Level 9) Cost:7
+ Attempts to dominate the minds of your foes, scaring them.
+ At level 30 it attempts to charm monsters.
+6. [[[[[sPulverise] (Level 11) Cost:7
+ Fires pure sound at your opponents, crushing their bodies.
+7. [[[[[sCharacter Armour] (Level 13) Cost:12
+ Raises a physical shield around your body.
+ At level 15 it also grants resistance to acid.
+ At level 20 it also grants resistance to fire.
+ At level 25 it also grants resistance to cold.
+ At level 30 it also grants resistance to electricity.
+ At level 35 it also grants resistance to poison.
+8. [[[[[sPsychometry] (Level 15) Cost:12
+ Senses the quality of an item.
+ At level 40 it identifies an item.
+9. [[[[[sMindwave] (Level 18) Cost:10
+ Blasts the minds of monsters close to you.
+ At level 25 it affect all monsters in line of sight.
+10.[[[[[sAdrenaline Channeling] (Level 23) Cost:15
+ Heals you, hastes you and cures you.
+11.[[[[[sPsychic Drain] (Level 25) Cost:10
+ Drains the life of your foes into your mana reserves.
+12.[[[[[sTelekinesis] (Level 28) Cost:20
+ Projects a wave of pure telekinetic force from your body, damaging and maybe
+ banishing monsters.
diff --git a/lib/help/m_music.txt b/lib/help/m_music.txt
new file mode 100644
index 00000000..0f84f08f
--- /dev/null
+++ b/lib/help/m_music.txt
@@ -0,0 +1,77 @@
+|||||oy
+~~~~~01|Magic|Music
+~~~~~02|Music
+~~~~~03|Skills|Music - Song Info
+#####R === ToME Spells - Music ===
+
+Musical songs can have powerful effects for those who have the ability to
+play instruments, chiefly *****c_bard.txt*0[Bards].
+
+In order to continue playing a song, mana is consumed each turn, until
+either the 'Stop Singing' song is sung, or the player's mana runs out.
+
+Each song, as well as having a school level like any other magic spell, also
+has a roman numeral following its name. The higher this number, the greater the
+craftmanship of the instrument required to play it.
+
+Each musical instrument has a value assigned to it as well, between 1 and 4. The
+higher the number, the better the craftmanship; hence it will be possible
+for the bard to play higher level songs only with more powerful instruments.
+E.g. a Harp(+1) will allow you to cast "Stop Singing(I)" and "Song of the
+Sun(I)". A Harp(+2) would allow you to sing those songs, as well as "Flow of
+Life(II)".
+
+There are 3 different types of instruments: Harps, Drums and Horns. Each type
+of instrument contains a different family of musical songs:
+
+
+#####vMusical Songs
+
+#####GAll Instruments
+1. [[[[[vStop Singing(I)] (school level 1)
+ Stops the current song, if any.
+
+#####GDrums
+1. [[[[[vHolding Pattern(I)] (school level 1)
+ Slows down all monsters listening the song.
+ Consumes mana each turn.
+2. [[[[[vIllusion Pattern(II)] (school level 5)
+ Tries to confuse all monsters listening the song.
+ Consumes mana each turn.
+3. [[[[[vStun Pattern(IV)] (school level 10)
+ Stuns all monsters listening to the song.
+ Consumes mana each turn.
+
+#####GHarps
+1. [[[[[vSong of the Sun(I)] (School level 1)
+ Provides light as long as you sing.
+ Consumes mana each turn.
+2. [[[[[vFlow of Life(II)] (School level 5)
+ Heals you as long as you sing.
+ Consumes mana each turn.
+3. [[[[[vHeroic Ballad(II)] (School level 10)
+ Increases melee accuracy.
+ At level 10 it increases it even more and reduces armour a bit.
+ At level 20 it increases it still more.
+ At level 25 it grants protection against chaos and confusion.
+ Consumes mana each turn.
+4. [[[[[vHobbit Melodies(III)] (School level 20)
+ Greatly increases your reflexes allowing you to block more melee blows.
+ At level 15 it also makes you faster.
+ Consumes mana each turn.
+5. [[[[[vClairaudience(IV)] (School level 25)
+ Allows you to sense monster minds as long as you sing.
+ At level 10 it identifies all objects in a radius on the floor,
+ as well as probing monsters in that radius.
+ Consumes mana each turn.
+
+#####GHorns
+1. [[[[[vBlow(I)] (School level 4)
+ Produces a powerful, blowing sound all around you.
+2. [[[[[vGush of Wind (II)] (School level 14)
+ Produces a outgoing gush of wind that sends monsters away.
+3. [[[[[vHorns of Ylmir(III)] (School level 20)
+ Produces an earth-shaking sound.
+4. [[[[[vAmbarkanta(IV)] (School level 25)
+ Produces a reality-shaking sound that transports you to a nearly
+ identical reality.
diff --git a/lib/help/m_nature.txt b/lib/help/m_nature.txt
new file mode 100644
index 00000000..3de854e1
--- /dev/null
+++ b/lib/help/m_nature.txt
@@ -0,0 +1,53 @@
+|||||oy
+~~~~~01|Magic|Nature School
+~~~~~02|Nature Magic
+~~~~~03|Skills|Nature - Spell Info
+#####R === ToME Magic - Nature School ===
+
+The nature school of magic contains spells that interact with nature. There
+are rumours of a "Tome of the Tree" which contains all the nature school
+spells within its bindings.
+
+Worshipping the Goddess Yavanna Kementari also gives the ability to cast spells
+from the nature school at a level of 1/2 of your prayer level. E.g. if the skill
+"Spirituality: Prayer" is at level 10, you can cast up to level 5 nature school
+spells.
+
+#####GNature Spells
+There are five spells available for the nature school. These spells are:
+1. [[[[[GGrow Trees] (dual school level 6)
+ Makes trees grow extremely quickly around you.
+#####v Your Nature and Temporal skills must have reached a combined average level
+#####v of 6 in order to cast this spell.
+2. [[[[[GHealing] (school level 10)
+ Heals a portion of your hitpoints.
+3. [[[[[GRecovery] (school level 15)
+ Reduces the length of time that you are poisoned.
+ At spell level 5 it cures poison and cuts.
+ At spell level 10 it restores drained stats.
+ At spell level 15 it restores lost experience.
+4. [[[[[GRegeneration] (school level 20)
+ Increases your body's regeneration rate.
+5. [[[[[GSummon Animal] (school level 25)
+ Summons a leveled animal to your aid.
+
+
+#####GAir spells that can be cast with Nature skill
+
+1. [[[[[BThunderstorm] (dual school level 25)
+ Charges up the air around you with electricity.
+ Each turn it will throw a bolt of thunder at a random monster in sight.
+ This thunder does 3 types of damage:
+ one third of lightning
+ one third of sound
+ and one third of light.
+#####v Your Nature and Air skills must have reached a combined average level
+#####v of 25 in order to cast this spell.
+
+#####GUdun spells that can be cast with Nature skill
+
+1. [[[[[DGenocide] (dual school level 25)
+ Genocides all monsters of a specified race on the level.
+ At level 10 it can genocide all monsters near you.
+#####v Your Nature and Udun skills must have reached a combined average level
+#####v of 25 in order to cast this spell.
diff --git a/lib/help/m_necrom.txt b/lib/help/m_necrom.txt
new file mode 100644
index 00000000..8de4fd37
--- /dev/null
+++ b/lib/help/m_necrom.txt
@@ -0,0 +1,35 @@
+|||||oy
+~~~~~01|Magic|Necromancy
+~~~~~02|Necromancy Magic
+~~~~~03|Skills|Necromancy - Spell Info
+#####R === ToME Magic - Necromancy ===
+The art of Necromancy is the foul practice of manipulating the life
+force of creatures.
+
+Necromancy powers are accessed using the 'm' key and then selecting
+'Use Necromancy'; they are cast with Spell Points, like normal spells,
+and do not need light to be performed.
+
+1. [[[[[DHorrify] (Level 1) Cost:2
+ Calls upon the dark forces and opens a channel into the mind of a
+ monster, stunning and scaring it.
+ At level 21 it affects all monsters in a beam.
+ At level 36 it affects all monsters in a ball.
+ At level 46 it affects all monsters in sight.
+2. [[[[[DRaise Dead] (Level 5) Cost:6
+ This power makes corpses in a small radius around the caster rise as
+ undead ego monsters at the service of the caster. The loyalty of those
+ monsters is not guaranteed, though. It also heals all monsters within the
+ same radius.
+3. [[[[[DNecromantic Teeth] (Level 12) Cost:20
+ This conjures up a temporary vampiric weapon.
+4. [[[[[DAbsorb Soul] (Level 20) Cost:10
+ This heals you by a substantial amount every time you kill a monster within
+ its duration. It is especially useful when your character is in
+ *****ability.txt*12[Undead Form].
+5. [[[[[DVampirism] (Level 30) Cost:15
+ Drains part of the life-force from a nearby monster and gives it to you.
+6. [[[[[DDeath] (Level 35) Cost:100
+ Yes, the name is not nice. Neither are the effects: the target dies
+ immediately, but so does your character (note that death is not such a great
+ annoyance for necromancers).
diff --git a/lib/help/m_symbio.txt b/lib/help/m_symbio.txt
new file mode 100644
index 00000000..b7e04632
--- /dev/null
+++ b/lib/help/m_symbio.txt
@@ -0,0 +1,50 @@
+|||||oy
+~~~~~01|Magic|Symbiosis
+~~~~~02|Symbiosis Magic
+~~~~~03|Skills|Symbiosis - Symbiotic Powers
+#####R === ToME Magic - Symbiosis ===
+
+Symbiosis is the art of joining body, fate and sometimes even mind with
+creatures not capable of moving on their own.
+
+While a humble student of the Craft of Slime (as it's sometimes called)
+has very few options apart from asking the symbiote to help him in combat,
+master practitioners have access to an incredible array of tricks.
+
+Once hypnotised, the monster is placed onto the body, or "worn", in order to
+initiate the symbiotic relationship.
+
+#####uSymbiotic Powers
+There are nine powers a symbiant can develop. They are:
+1. [[[[[uHypnotize] (level 1) Cost:1
+ The very basis of symbiosis itself, this asks a monster to lower its
+ natural defences so that it can be safely "worn".
+2. [[[[[uRelease] (level 1) Cost:1
+ Sometimes even life-long friends part. This power allows you to revert
+ a monster on the floor to its primal state, even though the shock of waking
+ up will lower it to 0 HP.
+3. [[[[[uCharm never-moving] (level 3) Cost:2
+ A symbiant soon learns to communicate with molds and slimes.
+ This power allows him to gain the "friendship" of such a creature.
+4. [[[[[uLife share] (level 5) Cost:5
+ The cells of the symbiant and the symbiote intermingle, spreading damage
+ evenly among the two organisms.
+5. [[[[[uUse minor powers] (level 10) Cost:10
+ Allows you to tap into minor magic abilities provided by your symbiote
+ such as Blink or Slow.
+6. [[[[[uHeal symbiote] (level 15) Cost:14
+ Consciously altering the metabolism of your symbiote, you can urge its
+ bodily structure to repair itself almost instantaneously.
+7. [[[[[uUse major powers] (level 25) Cost:30
+ Highly intelligent slimes such as Quylthulgs may be more than willing to
+ employ their summoning or teleporting powers on your behalf; this power
+ can also call upon the abilities normally invoked by "Use minor powers"
+ (but this would be a waste of mana).
+8. [[[[[uSummon never-moving pet] (level 30) Cost:35
+ By releasing certain chemicals in the air, a symbiant can attract the
+ attention of slimes and mold; the better specimens are found in deeper
+ dungeons, of course.
+9. [[[[[uForce symbiosis] (level 40) Cost:60
+ An expert symbiant has such control over the cells of primitive
+ life-forms that he can temporarily "charm" part of them, thus forcing
+ their powers to manifest at his own advantage.
diff --git a/lib/help/m_tempo.txt b/lib/help/m_tempo.txt
new file mode 100644
index 00000000..d0b5a1fa
--- /dev/null
+++ b/lib/help/m_tempo.txt
@@ -0,0 +1,41 @@
+|||||oy
+~~~~~01|Magic|Temporal School
+~~~~~02|Temporal Magic
+~~~~~03|Skills|Temporal - Spell Info
+#####R === ToME Magic - Temporal School ===
+
+The temporal school of magic contains spells where magic is used to meddle
+in the relationship between time and space. There are rumours of a "Tome of
+the Time" which contains all the temporal school spells within its bindings.
+
+Worshipping the Goddess Yavanna Kementari also gives the ability to cast spells
+from the temporal school at a level of 1/6 of your prayer level. E.g. if the
+skill "Spirituality: Prayer" is at level 12, you can cast up to level 2
+temporal school spells.
+
+
+#####sTemporal Spells
+There are four spells available for the temporal school. These Spells are:
+1. [[[[[sMagelock] (school level 1)
+ Magically locks a door.
+ At spell level 30 it creates a glyph of warding.
+ At spell level 40 the glyph can be placed anywhere in the field of vision.
+2. [[[[[sSlow Monster] (school level 10)
+ Magically slows down the passing of time around a monster.
+ At level 20 it affects a zone.
+3. [[[[[sEssence of Speed] (school level 15)
+ Magically decreases the passing of time around you, allowing you to move
+ and act more quickly with respect to the rest of the universe.
+4. [[[[[sBanishment] (school level 30)
+ Disrupts the space/time continuum in your area and teleports all monsters
+ away.
+ At spell level 15 it also may lock them in a time bubble for some turns.
+#####v Your Temporal and Conveyance skills must have reached a combined average level
+#####v of 30 in order to cast this spell.
+
+#####GNature spells that can be cast with Temporal skill
+
+1. [[[[[GGrow Trees] (dual school level 6)
+ Makes trees grow extremely quickly around you.
+#####v Your Nature and Temporal skills must have reached a combined average level
+#####v of 6 in order to cast this spell.
diff --git a/lib/help/m_thaum.txt b/lib/help/m_thaum.txt
new file mode 100644
index 00000000..253d52be
--- /dev/null
+++ b/lib/help/m_thaum.txt
@@ -0,0 +1,31 @@
+|||||oy
+~~~~~01|Magic|Thaumaturgy
+~~~~~02|Thaumaturgical Magic
+~~~~~03|Skills|Thaumaturgy - Spell Info
+#####R === ToME Magic - Thaumaturgy ===
+
+Thaumaturgy is a different type of magic where the spells learnt are not
+restricted to one school, nor are they read out of spellbooks. Each time an
+adventurer increases her thaumaturgy skill, she gains access to a few
+[[[[[Rrandom] attack spells, each one automatically "learnt" at a specific
+casting level. Since she doesn't need spellbooks, she does not end up with
+inventory slots being filled up so quickly by the necessary items for safe
+exploration of the dungeon, and can therefore collect more loot.
+
+The downside of this is that she has no ability to choose what spells she
+learns, and no ability to improve the power of a learnt spell. So, a bolt
+spell with damage 1d5 will remain at 1d5 damage for the whole game. And the
+spells learnt are *all* attack spells of some sort (remembering that things
+like light and stone-to-mud can damage some monsters) - so no teleporting, no
+identify, no healing spells are learnt. At most, some wall creation may be
+employed.
+
+Thaumaturgist use their magic through the 'm' key. They then select a general
+group of spells, followed by a specific spell.
+
+Thaumaturgy spells can take the form of:
+- a bolt, targeted at a single location;
+- a beam, which hits all monsters in a line;
+- a ball (either centred on the caster or targetable);
+- an meteor strike (multiple balls in the vicinity of the caster);
+- a spell that affects all monsters in line of sight.
diff --git a/lib/help/m_udun.txt b/lib/help/m_udun.txt
new file mode 100644
index 00000000..a903d9f9
--- /dev/null
+++ b/lib/help/m_udun.txt
@@ -0,0 +1,35 @@
+|||||oy
+~~~~~01|Magic|Udun School
+~~~~~02|Udun Magic
+~~~~~03|Skills|Udun - Spell Info
+#####R === ToME Magic - Udun School ===
+
+The Udun school of magic contains spells where the corrupted forces of Melkor
+Bauglir are used to create the final spell effect. There are rumours of an
+"Unholy Tome of the Hellflame" which contains all the Udun school spells within
+its bindings.
+
+The Udun school is available only to worshippers of *****g_melkor.txt*0[Melkor Bauglir]. They will also
+need some proficiency in the magic schools of Mana, Nature, Conveyance, and Fire
+(or alternatively Sorcery) to cast the Udun spells. On the other hand, the spell
+power of Udun spells is greatly increased by the level of the caster.
+
+#####DUdun Spells
+There are four spells available for the Udun school. These spells are:
+1. [[[[[DDrain] (dual school level 1)
+ Drains the mana contained in wands, staves and rods to increase yours.
+#####v Your Udun and Mana skills must have reached a combined average level
+#####v of 1 in order to cast this spell.
+2. [[[[[DGenocide] (dual school level 25)
+ Genocides all monsters of a specified race on the level.
+ At level 10 it can genocide all monsters near you.
+#####v Your Udun and Nature skills must have reached a combined average level
+#####v of 25 in order to cast this spell.
+3. [[[[[DWraithform] (dual school level 30)
+ Turns you temporarily into an immaterial being.
+#####v Your Udun and Conveyance skills must have reached a combined average level
+#####v of 30 in order to cast this spell.
+4. [[[[[DFlame of Udun] (dual school level 35)
+ Turns you temporarily into a powerful Balrog.
+#####v Your Udun and Fire skills must have reached a combined average level
+#####v of 35 in order to cast this spell.
diff --git a/lib/help/m_water.txt b/lib/help/m_water.txt
new file mode 100644
index 00000000..5daee1b1
--- /dev/null
+++ b/lib/help/m_water.txt
@@ -0,0 +1,33 @@
+|||||oy
+~~~~~01|Magic|Water School
+~~~~~02|Water Magic
+~~~~~03|Skills|Water - Spell Info
+#####R === ToME Magic - Water School ===
+
+The water school of magic contains spells where the element of water is
+used to create the final spell effect. There are rumours of a "Tome of the
+Everrunning Wave" which contains all the water school spells within its
+bindings.
+
+Worshipping the Goddess Yavanna Kementari also gives the ability to cast spells
+from the water school at a level of 1/2 of your prayer level. E.g. if the skill
+"Spirituality: Prayer" is at level 10, you can cast up to level 5 water school
+spells.
+
+#####bWater Spells
+There are four spells available for the water school. These Spells are:
+1. [[[[[bGeyser] (school level 1)
+ Shoots a geyser of water from your fingertips.
+ Sometimes it can blast through its first target.
+2. [[[[[bVapor] (school level 2)
+ Fills the air with toxic moisture to wash away annoying creatures.
+3. [[[[[bEnt's Potion] (school level 6)
+ Fills up your stomach (i.e. satisfy hunger).
+ At spell level 5 it emboldens your heart (boldness).
+ At level 12 it make you heroic.
+4. [[[[[bTidal Wave] (school level 16)
+ Summons a monstrous tidal wave that will expand and crush monsters under
+ its mighty waves.
+5. [[[[[bIce Storm] (school level 22)
+ Engulfs you in a storm of roaring cold that strikes your foes.
+ At spell level 10 it turns into shards of ice.
diff --git a/lib/help/macrofaq.txt b/lib/help/macrofaq.txt
new file mode 100644
index 00000000..ea41129f
--- /dev/null
+++ b/lib/help/macrofaq.txt
@@ -0,0 +1,2366 @@
+|||||oy
+~~~~~43|Macros
+~~~~~44|Keymaps
+#####R======================================================================
+#####B ToME Macro FAQ
+
+ Original Angband Macro FAQ by Jim Lyon
+ (jplyon@attglobal.net)
+ 09-Dec-2000
+ Compiled from usenet postings to r.g.r.a
+ and Angband source & documentation
+
+#####B Edited by Dawnmist (angband@dawnmist.8m.com)
+#####B for PernAngband 5.x.x on 03-Aug-2001
+#####B with permission from Jim Lyon
+~~~~~30
+#####R======================================================================
+#####R1. Introduction
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G1.1 About this FAQ
+#####G----------------------------------------------------------------------
+
+This FAQ is mean to be a companion to the standard ToME help files
+for using inscriptions, macros, and keymaps. The ToME help files
+which also describe them are listed in the "References" section.
+
+This documentation is for ToME, version 4.2.x.
+
+#####B==================================
+#####B SPOILER ALERT
+#####B==================================
+
+This document gives some information on how the game does or doesn't
+work that might be considered spoiling.
+(Most players advanced enough to use macros probably won't notice.)
+
+#####G----------------------------------------------------------------------
+#####G1.2 Table of contents
+#####G----------------------------------------------------------------------
+
+ *****macrofaq.txt*30[1. Introduction]
+ *****macrofaq.txt*5[2. Quick start tutorial]
+ *****macrofaq.txt*32[3. Overview]
+ *****macrofaq.txt*33[4. Common macros and techniques]
+ *****macrofaq.txt*34[5. Common questions]
+ *****macrofaq.txt*35[6. Common problems]
+ *****macrofaq.txt*36[7. Inscriptions added by the game]
+ *****macrofaq.txt*37[8. Keys and commands]
+ *****macrofaq.txt*38[9. Pref files]
+*****macrofaq.txt*39[10. Macro editing commands]
+*****macrofaq.txt*20[11. Advanced macro techniques]
+*****macrofaq.txt*41[12. Problems]
+*****macrofaq.txt*42[13. Miscellaneous]
+
+#####G----------------------------------------------------------------------
+#####G1.3 Notation
+#####G----------------------------------------------------------------------
+
+#####BSingle Quotes (')
+These are generally used to delimit a single character to be typed in.
+These shouldn't by typed in themselves.
+
+#####BDouble Quotes (")
+These are generally used to delimit a sequence of characters to be
+typed in. These shouldn't by typed in themselves.
+
+#####BParentheses ( )
+These are generally used for single-key Angband commands.
+
+#####BBraces { }
+These are used to enclose inscriptions. These aren't typed in as part
+of inscribing an item. They are added by the interface.
+
+#####G= Special Keys =
+#####G-----------------------------------
+The following abbreviations are used in this document. These keys may
+be named differently or missing on some keyboards. Some keys may be
+duplicated. These abbreviations shouldn't be typed in literally. For
+example, when F1 is encountered in a string of keys to press it means
+to press the F1 function key, not 'F','1', unless otherwise stated.
+Additional special keys may be listed later.
+
+Alt Alt
+Ctrl Control
+Del Delete
+Esc Escape
+Enter Enter / Return
+F1 Function key F1, ...
+Shift Shift
+
+#####G= System abbreviations =
+#####G-----------------------------------
+Each system that ToME compiles on has a semi-standard 3-letter
+abbreviation. These are commonly referred to in the source and docs
+using "xxx" as a "wildcard" standing for any one of them. In this
+document "***" is used instead because there are actual generic files
+not associated with any specific system that use "xxx". Sometimes this
+refers to a feature instead of a specific system: "xxx" is used for a
+generic / default file, and "new" is used for Adam Bolt's tiles.
+
+~~~~~5
+#####R======================================================================
+#####R2. Quick Start Tutorial
+#####R======================================================================
+
+This section is designed to get you quickly using the most common
+keymaps and macros. Later sections explain the techniques used in more
+detail. These examples may not be the "best" ones to use in real play.
+
+#####G----------------------------------------------------------------------
+#####G2.0 Definitions
+#####G----------------------------------------------------------------------
+
+First, you should know some fundamental terms:
+
+Actions are sequences of keypresses that the game can recognise. They
+can't be recorded by the game, but must be input manually.
+
+Macros and keymaps map a keypress to an action. They can be used to
+customise the keyboard, reduce typing, and speed up game play. Macros
+must be used when the trigger key doesn't have a system-independent
+representation. Keymap actions can only contain underlying commands.
+[[[[[BMost customisation should be done with keymaps instead of macros when]
+[[[[[Bthere is a choice.]
+
+Inscriptions are "markings" which you can put on any game item. One
+use is to label items in a way that doesn't depend on inventory
+position. Another allows verifying the selection of an item. Under the
+right conditions they can save your life.
+
+#####G----------------------------------------------------------------------
+#####G2.1 Swap weapons
+#####G----------------------------------------------------------------------
+
+First inscribe your main weapon:
+(Press the following keys in sequence)
+
+1) { Inscribe an object
+2) * Show inventory list
+3) / Switch to equipment list
+4) a Main weapon slot
+5) @w0 Wield when object 0 is chosen
+6) (Hit Enter)
+
+Now inscribe the second weapon:
+
+1) { Inscribe an object
+2) * Show inventory list
+3) (Choose letter of second weapon)
+4) @w0 Wield when object 0 is chosen
+5) (Hit Enter)
+
+Finally, ToME (unlike standard 'Vanilla' Angband) does
+not have an automatic trigger key to swap items, so it must be
+created. Select a key that is not being used for any other commands
+('X' in the normal keyset is free - see *****command.txt*0[command.txt]), and create
+the following keymap:
+
+1) @ Interact with macros
+2) 8 Create a keymap
+3) X The trigger key for the keymap
+4) w0 Wield object 0
+5) (Hit Enter)
+6) (Hit Esc to exit the editor)
+
+Now to swap weapons, just press the trigger key 'X' which is bound to
+the default keymap "w0". You may also press "w0" directly to swap.
+~~~~~45|Macros|Macro recorder
+~~~~~46|Keymaps|Macro recorder
+#####G----------------------------------------------------------------------
+#####G2.2 Using the macro-recorder
+#####G----------------------------------------------------------------------
+
+You may find all the key-presses involved in ToME a long and time-consuming way
+to play the game. There are ways to speed up repeated commands by assigning
+them to a trigger key which can help. These are called macros or keymaps.
+
+The most obvious use for these in ToME is for mage-types, especially sorcerors,
+who rely on their spells for just about everything. Typing mbaa*t in order to
+fire a Manathrust is fine if you only have to do it once or twice, but can
+quickly get annoying when you're doing it every move. Far easier to assign that
+sequence of keypresses to a single trigger key which you can then press as many
+times as you need. (Until your SP run out of course!)
+
+The easiest way to assign a macro is to use the macro-recorder. Start this by
+hitting the '$' key. You'll then receive a message telling you that the macro
+recorder has now started and you will need to press the '$' key a second time
+to stop the recorder. Preparation is all important here. It is best to be in a
+situation where you really NEED to cast the spell before recording the macro,
+so you use all the correct casting techniques and the game behaves as it would
+in a real (combat) situation (if the spell is to be generally cast in combat).
+
+So assuming we're going to create a macro for manathrust. We've walked into a
+room and there's a nasty small kobold. What do we do?
+
+1) $ Start macro recorder
+2) *t target monster (or player if no monster in Line of sight)
+3) m open skills menu
+4) @ enters verbose mode
+5) Cast a Spell selects skill to use - CASE SENSITIVE
+6) @ enters verbose mode
+7) Manathrust casts spell from any book/spell container - CASE SENSITIVE
+8) $ end macro recorder
+9) y confirms macro keystrokes[[[[[B*]
+10) (choose trigger key)
+11) @ opens macro saving/loading screen
+12) 2 appends macros to a file
+13) (choose a name for the file, e.g. sorceror.prf)
+14) (Hit Enter)
+15) (Hit Esc to exit the editor)
+
+This will search for the spell in all your books and equipment. If it is found,
+in either a book or a wielded item that contains a spell, then the spell is
+cast at the targetted monster.
+
+This technique can be used for all the other skills as well (Use Mindcraft,
+Forge Ammo) etc.
+
+[[[[[B*]If you answer no the recorder continues recording keypresses. If
+you know you have made a mistake, you need to answer yes, and then not
+save the macro!
+
+~~~~~12
+#####G----------------------------------------------------------------------
+#####G2.3 Prevent unwanted use of an item
+#####G----------------------------------------------------------------------
+
+#####BPrevent "losing" an item by "accident":
+
+1) { Inscribe an object
+2) * Show inventory list
+3) (Choose an item)
+4) !d!k!v
+ !d - don't drop (d)
+ !k - don't destroy (k)
+ !v - don't throw (v)
+5) (Hit Enter)
+
+This prevents dropping, destroying, or throwing the item. You will be
+asked if you really want to do so. This is one of the most common
+inscriptions used, and one of the most useful.
+
+#####BPrevent "using" an item at all:
+
+1) { Inscribe an object
+2) * Show inventory list
+3) (Choose an item)
+4) !* Don't do anything with this item without verifying
+5) (Hit Enter)
+
+This inscription is commonly used on Scrolls of Word of Recall...
+~~~~~8
+#####G----------------------------------------------------------------------
+#####G2.4 Saving these macros and keymaps for reuse
+#####G----------------------------------------------------------------------
+
+Save the macros and keymaps for reuse by the current character:
+
+1) @ Interact with macros
+2) 2 Append macros to a file
+ (optionally enter a filename: e.g. dump.prf or dump.txt)
+ (Hit Enter to save the file with the filename shown)
+3) 6 Append keymaps to a file
+ (Hit Enter to save the file with the filename shown)
+4) (Hit Esc to exit the editor)
+
+This pref file will be automatically loaded any time a character with
+a name, race, or class matching the filename is loaded.
+
+The "Append macros/keymaps to a file" commands will append ALL current
+macros/keymaps to the given file. They will not overwrite the file.
+
+You should edit the file to remove macros that weren't added by you,
+to reduce clutter and prevent errors. Unfortunately the best way to do
+this is still "by hand". Open up the pref file in a text editor and
+remove the duplicate macros and keymaps added. These will be added
+after the headers "# Automatic macro dump" and "# Automatic keymap
+dump". The ones you added will be the very last ones in the list. The
+others are the entire set of keymaps and macros from all other prefs
+loaded. (This step isn't necessary, but is very helpful.)
+
+~~~~~32
+#####R======================================================================
+#####R3. Overview
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G3.1 Inscriptions
+#####G----------------------------------------------------------------------
+
+Inscriptions are "markings" which you can inscribe on any game item.
+
+One common use is recording where you got one of your favorite items.
+Example: The Broad Sword 'Glamdring' (2d5) (+10,+15) {icky thing, 50'!}
+
+Another is to note important resists or activations on an item to make
+figuring out resist combinations easier.
+Example: The Nice Shiny Armor (-3) [35,+25] {resDk,resDis,A:Geno}
+
+Inscriptions can "number" an item so that you don't have to know where
+it is in your inventory.
+Example: inscribing your scrolls {@r1} lets you use '1' at the prompt
+for which scroll to read, instead of having to use the inventory letter,
+which can change unnoticed.
+
+Number inscriptions work together with macros to allow reproducible
+labeling of items independent of inventory position.
+
+Note that the game produces "fake" inscriptions, which look like real
+inscriptions, but are really just displayed the same way (e.g. "cursed").
+
+Finally, if you are in symbiosis with another creature, you can name it by
+inscribing it with "#named SomeName" (the word "#named" must be entered
+literally at the start of the inscription). This will tell the game that
+your symbiotic partner should be referred to by its name, rather than simply
+"Your white jelly". This has no effect on game play other than the aesthetic.
+
+#####G----------------------------------------------------------------------
+#####G3.2 Macros
+#####G----------------------------------------------------------------------
+
+Macros are mappings from a single "logical" keypress to a sequence of
+keypresses, allowing you to use special keys on the keyboard, such as
+function keys or keypad keys, possibly in conjunction with modifier
+keys, to "automate" repetitive multi-key commands that you use a lot.
+
+[[[[[BFor keys which don't have a system-independent representation, such as]
+[[[[[Bfunction keys, this is the only way to change their behavior.]
+
+#####G----------------------------------------------------------------------
+#####G3.3 Keymaps
+#####G----------------------------------------------------------------------
+
+Keymaps are vaguely related to macros. A keymap maps a single keypress
+to a series of keypresses, which bypass both other keymaps and any
+macros. Angband uses keymaps to map the original and the roguelike
+keysets to the underlying command set, and allows the user to modify or
+add keymaps of their own. All keymap actions must be specified using
+underlying commands. Keymaps and macros aren't expanded. The original
+keyset is almost identical to the underlying keyset, except that
+"numbers" are mapped to ";" plus a direction, "5" is mapped to ",",
+and a few control-keys are mapped to various things. See *****command.txt*0[command.txt]
+for the full set of underlying commands. Keymaps also allow the
+"disabling" of a command by mapping it to "\x00".
+
+#####G----------------------------------------------------------------------
+#####G3.4 Pref files
+#####G----------------------------------------------------------------------
+
+Preference files save commands such as macros and keymaps which are
+used to customise the game. They are used to implement the "original"
+and "roguelike" keysets. They provide default appearances for items.
+
+They also implement the default behaviors which make Angband look and
+play the same (for the most part) on different systems.
+
+Pref files can be saved with the name of a player name, class, and
+race, and anytime a player with a matching characteristic is loaded,
+the appropriate pref file is loaded. This makes some customisations
+transparent and automatic.
+
+Pref files let you do some things that could otherwise only reasonably
+be done by changing the info files or source, such as changing the
+appearance of a given terrain feature, or the symbol used for the
+player.
+
+Pref files let you set up and save your favorite game options, and
+have them available for new characters without having to redo them.
+
+~~~~~33
+#####R======================================================================
+#####R4. Common macros and techniques
+#####R======================================================================
+~~~~~13
+#####G----------------------------------------------------------------------
+#####G4.1 Clearing the command buffer
+#####G----------------------------------------------------------------------
+
+[[[[[v++++++++++ This is one of the most important techniques! ++++++++++]
+
+Almost all action strings should begin with a sequence to clear the
+buffer of existing commands and messages. These sequences are often
+omitted in usenet postings and in this FAQ because they clutter the
+description, but they should almost always be used.
+
+If an unfinished command is still waiting for input when you press the
+trigger key of a macro or keymap, the characters of its action string
+will be taken as input for the command. The command will ignore keys
+it doesn't know how to handle until it finds one that it does. This
+often leads to something completely unexpected, with embarrassing
+results such as your death, or losing a really nifty item.
+
+If there are still messages waiting, the first characters in the
+action will instead just clear the waiting messages. How this happens
+depends on whether the *****option.txt*1[option (quick_messages)] is set. Then the action
+will start executing in the middle of its action string, with equally
+dangerous results as in the above case. Note that some messages caused
+by a command are quite rare, and others could be produced by a game
+state change like becoming hungry that has nothing to do with the command.
+
+These types of problems are more common when using an action such as
+auto-firing where a trigger key is repeatedly pressed.
+
+[[[[[B"\e\e\e"]
+Multiple escapes are used to clear the command buffer. Escape cancels
+any command/input still being processed, and also clears a [single]
+message whether/not the (quick_messages) option is set. This string
+should be used to begin all action strings. It is also wise to put it
+between actions in multi-action macros. Note that this technique can
+also be dangerous, hiding warnings or important information.
+Example: an action like "*tf1" would be changed to "\e\e\e*tf1".
+
+There are a few situations in which you do not want to use escapes.
+For example, after the spell Detect Monsters, an escape will clear the
+detection. Or if the action is part of a multi-part action or is meant
+to wait for user input such as the firing action "f1" which will wait
+for targeting information.
+
+[[[[[B"\s\s\s"]
+Spaces are useful for clearing messages, but won't cancel a command
+that is waiting for an item choice. Sometimes it is useful to split
+an action into two parts and bind them to two separate trigger keys.
+Prefixing spaces before the 2nd action will discard any remaining
+messages without canceling unfinished commands from the 1st one. Some
+players create a macro consisting of a large number of spaces to be
+used to clear a flood of messages.
+
+See sections:
+- *****macrofaq.txt*1["Messages and Questions"] for how to deal with them.
+- *****macrofaq.txt*2["My macro outputs "e - Floating Eye"..."] for an example of a common
+ mistake using escape sequences.
+- *****macrofaq.txt*3["My macro drops/takes off my main weapon!"] for an example of the
+ kind of thing that can go wrong when you fail to use them.
+- *****macrofaq.txt*4["What just killed me?"] for an example of skipping over messages
+ *too* fast.
+
+#####G----------------------------------------------------------------------
+#####G4.2 Swap weapons
+#####G----------------------------------------------------------------------
+
+If you followed the *****macrofaq.txt*5[Quick-Start Tutorial] in Section 2.1, the 'X' key has
+the keymapping "w0", which will wield the first item inscribed as "@0"
+or "@w0". This will swap between 2 weapons which are both inscribed with
+{@0} or {@w0}. If there is more than one item in the inventory inscribed
+as 0, it will use the first one.
+
+This will also work for other wieldable items. For example, if you
+have a helm which gives telepathy, which can be a real pain when you
+need to rest, it can be used to swap it with another helm.
+
+Inscribing weapons with {@w1@w0}, {@w2@w0} allows directly wielding a
+specific one. If you don't want that ability, you can just inscribe
+both with {@w0}. These can also be inscribed as just {@1@0} and {@2@0}
+if you don't inscribe any other items with numbers, although this is
+not recommended.
+
+
+#####G----------------------------------------------------------------------
+#####G4.3 Resting
+#####G----------------------------------------------------------------------
+
+#####B= Rest as needed =
+"R&\r"
+R - rest
+& - until 100% healthy
+\r - return.
+
+Note that this isn't particularly dangerous, because the game will
+break out of the resting command if you are disturbed by hunger,
+sensing a monster, ...
+
+#####B= Rest for a specific duration =
+"R100\r"
+R - rest
+100 - turn count
+\r - return
+
+This version is useful if you are already healed up. For fighter
+pseudo-id, for example. Also useful for recovering a fixed amount of
+mana before/after a spell, waiting for a Recall spell to kick in,...
+
+Note that this is one of the few times when you can directly enter the
+count after the command. Usually you need to enter the count before
+the command, using the (0) count command.
+
+#####B= Maximum Rest =
+"R9999\r"
+
+The maximum number of turns you can use as an argument. This can be
+useful when waiting for shop inventory to change, etc. Be sure to take
+off any non-permanent light source, and have plenty of food when you
+rest for long periods of time.
+
+#####G----------------------------------------------------------------------
+#####G4.4 Activate the phial
+#####G----------------------------------------------------------------------
+
+"Am\s\s"
+A - Activate
+m - light source (the phial).
+\s - skip message
+\s - skip message
+
+"Am\s\sR50\r"
+Activate the phial and rest for 50 turns.
+
+This can be bound to 'F' key since you won't need to refuel lanterns
+much after you have the phial :).
+
+#####G----------------------------------------------------------------------
+#####G4.5 Kill item(s) on floor
+#####G----------------------------------------------------------------------
+
+The need to destroy large numbers of items arises as one reaches
+deeper levels of the dungeon. The auto-squelch feature only partially
+reduces the need for this. The behavior of these actions is affected
+by the option (quick_messages), and the possible presence of a pile of
+items on the floor.
+
+*****option.txt*1[(quick_messages)] allows any key to cancel a message.
+
+In ToME, multiple items on the floor are displayed in a list.
+This allows the player to select an item from the floor by entering an
+item index when there is a pile (more than one item). This generally
+complicates writing these macros.
+
+Note that space '\s' is used to clear messages in the action strings,
+but one could use escape '\e' just as well.
+
+Note: you cannot destroy artifacts, so these macros are safer and more
+useful than they might first appear. The (k) Destroy item command will
+fail when trying to destroy an artifact, leaving any following
+characters in the action string, which may be interpreted differently
+than anticipated.
+
+[[[[[B"k-yy"]
+k - Kill item
+- - Select item from floor
+y - "yes" to query "Really destroy a <item>?"
+y - skip the "you destroy the <item>" message.
+
+This version only works when (quick_messages) option is on. Here the
+last 'y' key gets rid of the last message, since any key will. This
+won't work for piles. The 'y' will be ignored as an invalid item choice.
+
+[[[[[B"k-y\s"]
+k - Kill item
+- - Select item from floor
+y - "yes" to query "Really destroy a <item>?"
+\s - skip the "you destroy the <item>" message.
+
+This version works as above, but also when (quick_messages) is off.
+
+[[[[[B"0k-y\s"]
+0 - enter count (causes to skip prompt for how many to destroy)
+k - Kill item
+- - Select item from floor
+\s - skip the "you destroy the <item>" message.
+
+Destroy a single item on floor below you. Doesn't prompt for a count.
+This won't work for piles, which will prompt you for the item. Works
+correctly when (quick_messages) is off, because there is no prompt
+for how many to destroy.
+
+[[[[[B"0k-ay\e"]
+0 - enter count (causes to skip prompt for how many to destroy)
+k - Kill item
+- - Select item from floor
+a - either item 'a', or ignored
+\e - escape (ignored), or cancel message
+
+Destroy a single item on floor below you. Doesn't ask to confirm.
+[[[[[vWARNING:] This action can destroy the first item in your inventory if
+there aren't any items on the floor below you!
+
+The leading '0' causes a prompt for a count to be skipped. If there is
+a pile, the 'a' key will select the top item in the pile. If not, the
+'a' will aim a wand, the following 'y' will be ignored, and the final
+escape will cancel the aiming.
+
+[[[[[B"0k-yay\e"]
+0 - enter count (causes to skip prompt for how many to destroy)
+k - Kill item
+- - Select item from floor
+y - "yes" to query "Really destroy a <item>?", or ignored.
+a - top item (a) in a pile, or activate wand.
+y - "yes" to query "Really destroy a <item>?" (for piles).
+\e - skip the "you destroy the <item>" message.
+
+The above action will work in most cases. This version works correctly
+for piles. If there is a pile, the 'y' is ignored and the 'a' selects
+the top item. If there isn't a pile, then the 'y' will correctly answer
+the yes/no question "Really destroy a <item>?", and the following "ay"
+will aim a rod, and then ignore the 'y' key press, and the final escape
+will cancel the aiming. Works correctly when (quick_messages) is off.
+When (quick_messages) is on, and there is no pile, the 'a' will cancel
+the message, and the following 'y' will be passed through as an
+unimplemented command, and the space will cancel the error message.
+
+Backslashes "\\" may be required to keep the 'y's from being
+interpreted as keymaps in case 'y' has been assigned one.
+
+#####G----------------------------------------------------------------------
+#####G4.6 Fire missile at nearest target
+#####G----------------------------------------------------------------------
+
+Each of these needs "\e\e\e\e" afterwards to cancel up to 4 possible
+messages. The first message will always occur. Note that adding too
+many escapes can cause you to miss the messages in which the monster
+fights back.
+- "You have NN <ammo> left. -more-"
+- "The <ammo> hits the <monster>. -more-"
+- "It was a <adj> hit! The monster ... -more-"
+- "The <monster> dies/grunts with pain/..."
+
+[[[[[B"f*t"]
+f - fire ammo from quiver slot
+* - target
+t - select first target
+
+Note that targeting is affected by the option (*****option.txt*4[use_old_target]). If
+this action is used with the (use_old_target) option set, the "f"
+part will fire the missile before the targeting part "*t" is reached.
+See section *****macrofaq.txt*6["My auto-firing macro shoots the wrong target!"].
+
+[[[[[B"*tf"]
+*t - select first target
+f - fire ammo from quiver slot
+
+If (use_old_target) is on, this works correctly, by selecting the
+target before firing. If the option is off, it will still prompt you
+to select a target, even though you just selected one.
+
+[[[[[B"*tf*t"]
+*t - select first target
+f - fire ammo from quiver slot
+*t - select first target
+
+This works correctly for (use_old_target) either on/off. If the option
+is on, this works by selecting the target before firing. If the option
+is off, the first target selection will be ignored, and firing will
+wait on the second target selection.
+
+Note that if there are no valid targets, "*t" will select the player's
+current position. Also, it is fairly easy to make safe assumptions
+about the (use_old_target) option, since a player doesn't tend to
+change back and forth. It is generally easier to make one set of
+actions that work for (use_old_target) on, and another for it off.
+
+Note also that the swapping weapon macro can be very useful for swapping
+the type of ammo in the quiver slot when you have two different sets of
+ammo you use - your standard "fire at everyone normal" ammo (usually
+not enchanted), and your big damage "Uniques and nasty monster"
+enchanted ammo.
+
+#####G----------------------------------------------------------------------
+#####G4.7 Preventing actions
+#####G----------------------------------------------------------------------
+
+#####B= Prevent selling =
+{!d}
+Prevent selling - use on main weapon, artifacts.
+
+#####B= Prevent going up/down =
+{^<}
+Verify before going up stairs.
+{^>}
+Inscribe boots to make confirm before going down stairs.
+This can also be useful on Charisma boosting items that you wear
+around town but don't want to take into the dungeon.
+{^>^r^z^m^p}
+Prevent leaving town with this item. Need to catch stairs, scrolls and
+rods of Recall, and Mage and Priest Word of Recall spells.
+
+#####G----------------------------------------------------------------------
+#####G4.8 Warrior macros and inscriptions
+#####G----------------------------------------------------------------------
+
+#####B= Identify with list =
+Inscribe Identify scrolls {@r0}.
+Then action "r0*" will cast Identify and bring up the inventory list.
+
+#####B= Identify floor =
+"r0-" will Identify without showing the inventory.
+
+#####G----------------------------------------------------------------------
+#####G4.9 Verification techniques
+#####G----------------------------------------------------------------------
+
+#####B= Verify All =
+{!*}
+verify any attempt to use this item. Useful for some things such as
+Scrolls/rods of Recall that you don't want to lose or use by accident.
+
+#####B= Verify drop, destroy, throw =
+{!d!k!v}
+don't drop, destroy, or throw this item. Prevents dropping your
+favorite weapon,... This is one of the most useful inscriptions, as it
+prevents the kind of typing accidents that can get you killed.
+
+#####B= Verify selling =
+{!d}
+Prevents selling as well as dropping.
+
+#####B= Multiple verification =
+These inscriptions can be repeated. So {!*!*} will make you confirm
+twice for any action using that item.
+
+#####G----------------------------------------------------------------------
+#####G4.10 Canceling targeting
+#####G----------------------------------------------------------------------
+
+[[[[[B"*\e\s"]
+* - start targeting
+\e - cancel targeting.
+\s - skip message "Target Aborted."
+
+OR
+
+[[[[[B"*q\r"]
+* - start targeting
+q - cancels targeting.
+\r - skip message "Target Aborted."
+
+This is useful before casting Stone to Mud, or a ball spell. For ball
+spells you usually want to target the middle of a pack, or sometimes
+to "miss" the creature in order to get the ball to detonate on a
+nearby wall.
+
+Using a targeted action after this will cause the user to be prompted
+for a target.
+
+Canceling targeting is made necessary by the existence of the option
+(use_last_target), which causes an action which requires a target to
+use the last target without prompting. See section
+*****macrofaq.txt*6["My auto-firing macro shoots the wrong target!"] for more on this.
+~~~~~15
+#####G----------------------------------------------------------------------
+#####G4.11 Automatically loading pref files
+#####G----------------------------------------------------------------------
+
+Angband automatically loads pref files when a character is loaded or
+born. Among the last ones loaded are: "<$RACE>.prf", "<$CLASS>.prf",
+and "<$PLAYER>.prf", in that order. These are the best ones to use to
+customise the game for individuals. The order is important because
+pref files which load later will overwrite options and macros/keymaps
+from previous ones.
+
+Here <$RACE> is the name of the character's race, <$CLASS> is the name
+of its class, and <$PLAYER> is the character's name. Both <$RACE> and
+<$CLASS> have a fixed number of choices. These are listed for ToME
+in section *****macrofaq.txt*7["Pref file loading order"].
+
+Your character's name is the one that appears during game play in the
+upper left hand corner of the screen. If it isn't visible there, you
+can use the (C) Character description command. Note that this name may
+be different than the filename of the character's save file.
+
+Filenames for the save and pref files are built from a "base name",
+which is the player name with all non-alphanumeric characters changed
+to underscores (_). On Windows and DOS systems the base name will also
+be truncated to 8 characters. This could lead to different characters
+having the same (default) pref files.
+Example: "Grog the Elder" and "Grog the Younger" would both try to
+load the same pref file "Grog_the.prf".
+
+In recent versions of ToME "<$PLAYER>.prf" is the default filename
+when saving your macros and keymaps from the (@) Interact with macros
+screen. Just hit Enter to accept that name.
+
+On some systems you may encounter problems automatically loading this
+file if your name is more than 8 characters, or if it contains spaces
+or special characters.
+
+See section *****macrofaq.txt*8["Saving these macros and keymaps for reuse"] for how to
+save your character's preferences.
+
+See section *****macrofaq.txt*7["Pref file loading order"] for the full list of pref files
+loaded and their order.
+
+Note: for pref files to load automatically they must end in the file
+extension "prf", which is the default when saving pref files. But it
+is possible to save and load pref files with any or no file extension,
+from the macros and visuals editor screens.
+
+#####G----------------------------------------------------------------------
+#####G4.12 Multiple macros bound to one trigger key
+#####G----------------------------------------------------------------------
+
+Angband supports using modifier keys on trigger keys. One thing this
+lets you do is easily choose between variants of an action. Another is
+to minimise the amount of moving your hands have to do, speeding up
+play and reducing stress on your wrists.
+
+[[[[[BYou can bind multiple versions of the same macro to the same trigger]
+[[[[[Bkey, using Alt, Control, Shift in different combinations] to choose
+among the different versions.
+Example: use <Alt> for targeted, and <Shift> for non-targeted.
+
+Another use is to heavily use modifiers on numeric keypad keys. The
+standard version already comes with Shift-<digit> bound to running in
+that direction. But by using various combinations of modifier keys, it
+is possibly to play with the right hand almost always on the numeric
+keypad, and the left on the modifier keys.
+
+#####G----------------------------------------------------------------------
+#####G4.13 Multi-part actions
+#####G----------------------------------------------------------------------
+
+Better to only do this when spells have ~0% failure.
+Be careful of the order of commands. Commands that leave useful info
+on the screen shouldn't be followed by ones that will clear it.
+Be careful of commands that set or clear targets.
+Use "\e\e\e\e" in-between commands to be safe.
+
+#####G----------------------------------------------------------------------
+#####G4.14 Easy running
+#####G----------------------------------------------------------------------
+
+Bind Shift+<keypad dir> to running for each of the directions.
+For example, running "North":
+
+1) @ Interact with macros
+2) 4 Create a macro
+3) Shift+8 (Trigger key for the macro)
+4) \\. Run
+ 8 "North"
+5) (Hit Enter)
+6) (Hit Esc to exit the editor)
+
+Macros for the other directions are added similarly. Remember that the
+original and roguelike keysets differ, but using the backslashes makes
+sure that the "underlying" keyset gets used:
+ 7 8 9
+ 4 6
+ 1 2 3
+
+These macros are already in the "pref-***.prf" files that ship with
+the standard Angband distribution for some systems, but not all.
+Systems "mac", "win", "x11" have them. If you aren't sure if you have
+them, just try moving in any direction with the shift key down.
+
+[[[[[BThese do have to be macros instead of keymaps], because they rely on
+keypad keys having different scan codes than the top-row number keys.
+
+There are also default macros for Ctrl+<dir> which applies the command
+(+) Alter to that direction.
+
+#####G----------------------------------------------------------------------
+#####G4.15 Farming techniques
+#####G----------------------------------------------------------------------
+
+#####v+++ NOTE: This is considered scumming! (cheating) +++
+
+Farming is the practice of automatically "harvesting" large numbers of
+weaker monsters for their experience value. This is usually done to
+advance a lower level character. There are apparently several methods
+that the "old timers" used to use. I don't know that farming is that
+popular any more. Even then it was sort of a lark.
+
+Apparently a golf ball is just the right size and weight for many
+keyboards to hold a key down and get it to auto repeat. Then you walk
+away, and the next morning you have gained several levels. Ballpoint
+pen caps are also supposed to be good at wedging a key down.
+
+Using a farming macro for long periods of time like this requires a
+way of getting food, so it really needs to be employed by a magic user
+who can create their own food.
+
+Use turn counts with attack (move) commands to move around the room,
+mowing down creatures as you move. Periodically rest enough to
+regenerate to full mana. This may not be necessary if it takes long
+enough to kill the monsters as you move.
+
+This requires an effective macro and a room full of breeders which
+can't attack for enough damage to kill you. Farming for short periods
+of time with a fighter class is quite feasible. The limitation for
+fighters is food. The limitation for spell casters is probably hit
+points. You may also need to insert action sequences for healing if
+the farmed monsters are capable of significantly damaging you.
+
+Note that red and green worm masses can knock down the doors of the
+room you're in. Blues can destroy potions and flasks, so you will need
+to stash your potions outside somewhere before farming.
+~~~~~10
+#####G----------------------------------------------------------------------
+#####G4.16 Macros can contain their own trigger key
+#####G----------------------------------------------------------------------
+
+It is permissible to make a macro or keymap which contains its own key
+in its action. It won't cause recursion, but there are a few wrinkles.
+
+Example: you can bind "*tf1" to the 'f' key, to cause it to auto-fire
+at the nearest target. You can still use the 'f' key for Fire in other
+macros or keymaps.
+
+If you bound 'f' as a keymap you will need to use "\\f" for Fire when
+it is used in a macro action string. You don't need to do anything
+special to use it in a keymap in its usual sense. Keymap and macro
+expansion isn't done inside keymap action strings.
+
+If you bound 'f' as a macro, the problem will be in entering the new
+macro or keymap in the editor. When you try to press an 'f' key for
+the action, it will expand to "*tf1", even when you don't want it to.
+You will first have to remove the macro bound to 'f', then add the
+macro or keymap that uses 'f' in its action, and then reenter the 'f'
+macro. You can also create the macro in a pref file and load it using
+the "Load pref file" command in the (@) Interact with macros screen.
+
+#####G----------------------------------------------------------------------
+#####G4.17 Changing the player's color and character (ASCII text display)
+#####G----------------------------------------------------------------------
+
+#####B= Using the Visuals Editor =
+1) % Interact with visuals.
+2) 6 Change monster attr/chars
+3) a (repeatedly) cycle thru colors (A moves backwards)
+4) c (repeatedly) cycle thru characters (C moves backwards)
+5) Esc accept changes
+6) Esc exit the editor
+
+#####B= Using the Enter User Pref command =
+1) " Enter user pref line
+2) "R:0:<attr>:<char>"
+ The user pref line.
+ <attr> - the attr (color) specified as an integer index.
+ <char> - the (ASCII) character specified as an integer.
+3) (Hit Enter)
+
+You can't directly use the Angband attr letters to specify colors, but
+must instead use their index. Eg 4 is Red. These indexes can be found
+from within the game using the (&) Interact with colors command. That
+editor also allows you to change the colors used by the game.
+
+The <char> integer is generally the index of an (ASCII) character.
+Non-ASCII characters may be available on some systems. Available
+characters can found using the (%) Interact with visuals command.
+
+These integers can be specified in decimal, hexadecimal, or octal
+notation. Decimal is the default, hexadecimal numbers are prefixed
+with "0x", and octal numbers are prefixed with 0 (zero). Example: the
+standard character for the player is '@'. This may be entered as "64",
+"0x40", or "0100". Yellow may be entered as "11", "0x0B", or "013".
+
+This pref line can be added to any pref file to save the change for
+future reuse. Changes made using the internal colors editor screen can
+be dumped from that screen. Note that colors and characters saved in
+pref dumps are in hexadecimal.
+
+After making a change, you must move the character or otherwise cause
+a screen redraw for the change to be visible.
+
+If you make a mistake, you can use the (0) command in the editor to
+reset the visuals to their original colors and characters.
+
+#####B= Changing Using the Monster Info file =
+#####B----------------------------------------
+
+This may also be done by changing the entry for the player in the info
+file "r_info.txt". The player data starts with line "N:0:Player". In
+the following line G:<c>:<a> the character <c> is entered directly,
+and the attr (color) is specified by letter. See the section "Message
+color lines" for the list of standard colors. In many versions you
+will have to delete the file "lib\data\r_info.raw" and restart the
+game for this change to take effect. Note that that char/attr pair is
+entered in the opposite order as for an R: user pref line.
+
+#####B= Options which Change Player appearance =
+#####B------------------------------------------
+
+These are accessible through the (=) Set options command.
+
+*****option.txt*2[(hilite_player)] -- causes the player's symbol to be drawn with the
+"cursor" on it. It will be drawn with the same color as the character.
+
+*****option.txt*3[(player_symbols)] -- for graphics mode only, and only works when option
+(use_graphics) is also on. This apparently varies the player graphic
+and its color based on class, race, and sex.
+
+#####G----------------------------------------------------------------------
+#####G4.18 Recharging a rod using a Recharge Item spell
+#####G----------------------------------------------------------------------
+
+(You can also recharge staffs and wands with this technique.)
+
+Inscribe the rod with {@m<d>}, where <d> is any decimal digit not
+already used as a label.
+
+The trick is that we want to use a digit to label the rod so we can
+refer to it using its label instead of its inventory letter, but we
+have usually used up many of the digits for spell books. If we were to
+label the rod with {@m1} and the first spellbook was also labeled with
+{@m1}, then the spellbook will sort first in the inventory so it will
+be found first when looking for item '1' to use with the (m) command.
+So that would try to recharge the spellbook, which will fail. Angband
+doesn't restrict itself to "appropriate" items when looking for a
+labeled item. It simply finds the first one whose command letter and
+digit match.
+
+You can't get around this by omitting the letter 'm', or using 'z'
+instead. Using no command letter means it will still match. And if you
+use a command letter it has to be 'm', because that is the built-in
+trigger for the current command.
+
+Note that the digits used with different command triggers can be
+different, so inscribing {@z1@m0} is perfectly legal. Use digit 1
+when zapping the rod, and 0 when referring to it during the casting
+of a [mage] spell.
+
+You can also use this technique with scrolls. In that case you need to
+use the inscription {@r<d>} since the item is being referred to while
+processing the (r) Read scroll command.
+
+#####G----------------------------------------------------------------------
+#####G4.19 Disabling a built-in command
+#####G----------------------------------------------------------------------
+
+At times it may be useful to disable an underlying Angband command.
+For example, a dangerous key may be too easy to press by accident.
+The macro editor commands for removing macros and keymaps can't be
+used in this situation. [[[[[BInstead bind the trigger key to the action]
+[[[[[Bstring "\x00"]. This special 'command' takes no "energy" and won't
+generate an error message. It truly does "nothing". This will work for
+both macros and keymaps. If possible you should use a keymap instead
+of a macro to disable the key. A keymap will catch occurrences of the
+key both in macros and by typing, and will still allow you to use the
+key when it isn't being interpreted as a command.
+
+If you use the "Query a macro/keymap" on a trigger key bound to this
+action, it will report having found it, but no action string will be
+printed.
+
+Another technique is to use the action "\e\e\e". This is used in some
+of the standard pref files.
+
+[[[[[BIf you need to use the built-in command again you can use the '\' key]
+[[[[[Bat any prompt to bypass its macro/keymap.]
+
+#####G----------------------------------------------------------------------
+#####G4.20 "Naming" an item (patch)
+#####G----------------------------------------------------------------------
+
+#####B= 'Fake artifact' name =
+# "Name" an item
+This isn't an actual command in the interface, but a flag character
+that alters the way an item description is generated. This isn't part
+of standard Angband. It's added by Tom Morton's 'fake artifact' patch.
+
+Example: inscription {#Thumper} will cause a Club (+8,+8) to display
+as Club 'Thumper' (+8,+8) in your inventory and messages.
+
+~~~~~34
+#####R======================================================================
+#####R5. Common Questions
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G5.1 Why can't I add a keymap for a function key?
+#####G----------------------------------------------------------------------
+
+Because keymaps can only be created for keys with system-independent
+representations. This leaves out function keys, and several other
+special keys. The "Create a keymap" command will continue waiting for
+a keypress until you press a valid keymap trigger. You can, however,
+create a macro for a function key.
+
+#####G----------------------------------------------------------------------
+#####G5.2 How can I automatically inscribe items when I pick them up?
+#####G----------------------------------------------------------------------
+
+You need to turn on the "Merge inscriptions when stacking" option.
+If you are already carrying the same item with an inscription, a new
+one will be added to the stack. Note that this WON'T merge discounts.
+Although discounts display like inscriptions, they are different.
+
+1) = Options
+2) 1 User interface options
+3) "Merge inscriptions when stacking" (stack_force_notes)
+ move down to this line and change to "yes".
+
+#####G----------------------------------------------------------------------
+#####G5.3 Can I use macros inside other macros?
+#####G----------------------------------------------------------------------
+
+No. Macros don't expand macro triggers they contain in their actions,
+in order to avoid recursion and other problems. However, keymap
+substitution is done, so you can use keymaps to alter the behavior of
+macros. This keymap expansion can be bypassed by preceding a trigger
+with "\\" in the action.
+
+Keymaps don't expand macro or keymap triggers in their actions.
+
+Also see section *****macrofaq.txt*9["Can I create an infinite loop using a macro?"].
+
+#####G----------------------------------------------------------------------
+#####G5.4 How do I find out what the standard commands are?
+#####G----------------------------------------------------------------------
+
+1) ? Angband help
+2) 6 *****command.txt*0[Command Descriptions (command.txt)]
+
+Space - moves you down by a page
+Minus - moves up by a page
+2 - moves down by a line
+8 - moves you up by a line
+
+Note that the original and roguelike command sets differ, and both are
+different from the "underlying" command set.
+
+#####G----------------------------------------------------------------------
+#####G5.5 How can I tell if a key has a keymap/macro?
+#####G----------------------------------------------------------------------
+
+#####B= Query Macro/Keymap =
+
+In ToME this is easy:
+1) @ Interact with macros
+2) 3 Query a macro
+OR 7 Query a keymap
+3) (Press the trigger key to test)
+4) Esc (Exit macro editor when done)
+
+It will report "Found no macro" or "Found a macro" at the top of the
+screen. The action it is bound to will be displayed at the bottom of
+the screen, under the "Current action..." line. You must test for both
+macros and keymaps, and keymaps will only show for the current "mode",
+i.e. original/roguelike.
+
+#####B= Notes =
+
+Note: on some machines, there are duplicate keys such as left and right
+Shift keys. These will generally produce different key codes and can
+have different macros and keymaps.
+
+Note: just because a key doesn't have a macro doesn't mean there isn't
+a command that uses that key.
+
+#####G----------------------------------------------------------------------
+#####G5.6 How can I tell if a key has a built-in command bound to it?
+#####G----------------------------------------------------------------------
+
+Er ... try pressing the key. If there is a command bound to that key
+it should usually generate a message of some kind. If there isn't one,
+it may respond: "Type '?' for help.". Some keys may not generate any
+message at all. Function keys are a good example.
+
+In ToME, the game will also generate "silly" error messages
+which may not look like error messages at first. After a few repeated
+key presses it uses the standard "Type '?' for help." message.
+
+Failing that, look in *****command.txt*0["command.txt"], which lists all
+commands, and is up-to-date. There is no specific method for checking if
+a key has a command bound to it from within the game.
+
+#####G----------------------------------------------------------------------
+#####G5.7 Can I inscribe multiple items with the same number?
+#####G----------------------------------------------------------------------
+
+You can, but it can cause problems if you aren't careful. Use the
+command letter in the inscriptions to minimise problems. For example,
+it is safe to inscribe both rods with {@z1} and a spell book with
+{@m1} because the command letter allows distinguishing the two.
+
+When the game looks for an item to use with a command, it tries the
+first one it finds that matches. If the command fails on that item, it
+doesn't continue looking. The search for a matching item also doesn't
+know how to only check the right kind of item, so if you have a Spellbook
+inscribed {@1} and scrolls inscribed {@1} or {@r1}, when you read a
+scroll it will find the spellbook first, even though it doesn't make
+sense to read it, and the command will fail.
+
+#####G----------------------------------------------------------------------
+#####G5.8 How do I convert a macro to a keymap?
+#####G----------------------------------------------------------------------
+
+You can simply remove it and re-add it "by hand", but for more complex
+actions there are faster, safer ways.
+
+#####B= Directly modifying the pref file =
+
+A macro with the following form in the pref file:
+ A:<action string>
+ P:<trigger>\r
+Should be converted to the keymap form:
+ A:<action string>
+ C:0:<trigger>
+The "\r" needs to be removed, and the 0 (zero) means standard keyset.
+Use 1 for roguelike keyset. To make a keymap for both keysets:
+ A:<action string>
+ C:0:<trigger>
+ C:1:<trigger>
+
+#####G= Using the "Interact with Macros" editor =
+
+1) @ Interact with Macros
+2) 3 Query a macro
+ <k> The trigger key for the macro
+ (its action string will now become the current action)
+3) 5 Remove a macro
+ <k> The trigger key for the macro
+4) 8 Create a keymap
+ <k> The trigger key for the keymap
+5) (Hit Enter to accept the current action)
+ (Hit Esc to clear the message "Added a keymap")
+
+This technique can also be used to move or copy actions between macros
+or keymaps of the same kind. [[[[[BAnd old macro MUST be removed from a key]
+[[[[[Bbefore it can be used as the trigger key for a keymap], otherwise the
+macro action will expand when you press the trigger key in the editor.
+Converting a keymap to a macro doesn't require removing the keymap
+first.
+
+Note that not all macros can be converted to keymaps. Keymaps don't do
+macro or keymap expansion on their action strings, so macros that rely
+on this will no longer work. Also, keymaps can only be bound to a
+trigger key with a printable internal representation. For example, a
+function key can't be a trigger for a keymap.
+~~~~~9
+#####G----------------------------------------------------------------------
+#####G5.9 Can I create an infinite loop using a macro?
+#####G----------------------------------------------------------------------
+
+No. Well, okay, you can, but only if you work *really* hard at it and
+abuse bugs in the macro handling code. This isn't something that will
+happen by accident just by using the trigger key inside its action.
+
+You also can't create recursion. So don't worry about this. See the
+section *****macrofaq.txt*10["Macros can contain their own trigger key"] for more info.
+~~~~~4
+#####G----------------------------------------------------------------------
+#####G5.10 What just killed me?
+#####G----------------------------------------------------------------------
+
+When you are using lots of escapes and spaces in your macros to skip
+over messages, you can miss important things happening. One of these
+is dying. Usually when something goes wrong, you can just use the (^P)
+Previous Messages command to see what happened. But if you died the
+escapes can take you past the tombstone screen, your last chance to
+examine the previous messages list. This also happens without macros.
+
+To examine your recall, load the savefile and start a new character.
+You will then be able to use the message recall command to see the
+last messages of that character's previous incarnation.
+
+~~~~~35
+#####R======================================================================
+#####R6. Common Problems
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G6.1 My macro works all the time when I press its key!
+#####G----------------------------------------------------------------------
+
+Macros *do* work all the time. Every time you press a key, macro
+expansion is done on it, and then keymap expansion. So if you use 'y'
+as a trigger key for a macro, and then you try and answer a yes/no
+prompt with 'y', instead you will get the macro's action string.
+
+The answer to this is to change your macro to a keymap. These can be
+bound to keys which have a system-independent representation in the
+game, which includes all keys that you would use when interacting with
+the game interface.
+
+If you don't want to change it to a keymap, try changing the trigger
+key to a "special" key, such as a function key.
+~~~~~6
+#####G----------------------------------------------------------------------
+#####G6.2 My auto-firing macro shoots the wrong target!
+#####G----------------------------------------------------------------------
+
+Your macro is probably firing at the previous target. This will happen
+if the option (*****option.txt*4[use_old_target]) is set. Then a macro will like "f1*t"
+or "m1a*t" will execute as:
+
+f Fire
+1 Ammo inscribed 1
+ (it will now fire at the last [wrong] target)
+* Choose a [new] target
+t Accept first target
+
+If there are no valid targets, the (t) targeting command will centre
+on your position. If you move, the target will still be your old
+square. The first time you use the "f1*t" macro it will fire at that
+square, even if there is now a valid target (monster) nearby.
+
+One fix is to turn off the (use_old_taret) option, since the action
+doesn't require it. This is done with the (=) Set Options command.
+
+Another is to change the action to choose the target before it fires.
+Example: "*tf1".
+
+[[[[[BNote:] just because you can "see" a monster doesn't mean you can target
+it. The code used for vision (line of sight) and firing (projection)
+is slightly different. So when shooting near corners or pillars it may
+happen that you can "see" a monster but not target it. If your action
+kills messages at the end, you could keep hitting your auto-fire macro
+and the only thing happening would be a large pile of missiles quietly
+accumulating underneath you.
+
+#####G----------------------------------------------------------------------
+#####G6.3 I used to have items inscribed, and now they aren't!
+#####G----------------------------------------------------------------------
+
+The game only knows about inscriptions that you are carrying. There is
+no way to "store" them independently of a character's save file. So if
+you lose all of an item that was inscribed, picking up another of that
+kind won't automatically inscribe it.
+
+Normal inscriptions aren't affected by your player's "memory".
+
+Note that some items, when fully identified, could have their
+descriptions grow so long that no inscription will show. In that case
+you can use the (I) Identify command. It will display the full
+description, even if nothing special is known about that item.
+
+#####G----------------------------------------------------------------------
+#####G6.4 I changed some macros in a pref file and nothing happened!
+#####G----------------------------------------------------------------------
+
+Settings loaded in later pref files will overwrite earlier ones. So if
+you add macros for the same trigger key to files "<$CLASS>.prf" and
+"<$PLAYER>.prf", the second one will get used because its file loads
+later. This affects macros, keymaps, actions, attrs/colors, and other
+info. See section *****macrofaq.txt*11["Pref lines summary"] for all the types of settings
+that can be loaded. Also see section *****macrofaq.txt*7["Pref file loading order"].
+
+#####G----------------------------------------------------------------------
+#####G6.5 I can't even FIND the macro editing commands!
+#####G----------------------------------------------------------------------
+
+Angband can be compiled without the ALLOW_MACROS symbol. This is done
+on some systems to reduce executable size. Macros and keymaps are
+still used by the game and loaded from files, but they can't be edited
+or saved from within the game. The "Interact with macros" screen only
+has the single option "Load a user pref file" in this case.
+
+#####G----------------------------------------------------------------------
+#####G6.6 It moves me when I try to use my bow/rod/wand!
+#####G----------------------------------------------------------------------
+
+Example: you type "f1" and it moves you in direction 1 (South West).
+What is happening is that the 'f' key isn't being handled correctly.
+It may be remapped to a bogus command, or one which doesn't take an
+argument. So the 'f' command is skipped/dealt with, and the '1' key is
+then treated as a direction. You can examine what is going on with the
+'f' key using the (@) Interact with macros screen to check for any
+macros or keymaps bound to that key. Use the appropriate "Remove ..."
+command to restore the built-in Angband command.
+~~~~~3
+#####G----------------------------------------------------------------------
+#####G6.7 My macro drops/takes off my main weapon!
+#####G----------------------------------------------------------------------
+
+This is probably caused by an auto-fire macro like "*tm1a" for magic
+missile. If you hold down the trigger key to repeatedly use it, and
+some game event (possibly caused by the macro) creates a message, then
+the action will be interpreted as:
+* (cancel message)
+t Take off item
+m (ignored as invalid)
+1 (ignored as invalid)
+a Item a (main weapon)
+
+If there is room in your inventory, it will be put there. If not, your
+inventory will overflow and it will be dropped on the ground. If this
+happens during combat this is a very good way to die. This is just
+another good reason to have {!d!k!v} on your main weapon. See the
+section *****macrofaq.txt*12["Prevent unwanted use of an item"].
+
+This can be fixed by using the escape sequence "\e\e\e" before and
+after the action string to cancel any pending messages or commands.
+See the section *****macrofaq.txt*13["Clearing the command buffer"].
+~~~~~2
+#####G----------------------------------------------------------------------
+#####G6.8 My macro outputs "e - Floating Eye" on the message line!
+#####G----------------------------------------------------------------------
+
+It is wise to add an escape sequence "\e\e\e" to the beginning and end
+of all macros for which this doesn't destroy useful information. See
+section *****macrofaq.txt*13["Clearing the command buffer"] for more information on this.
+
+But many players play on flavors of unix, which uses '/' as the path
+separator for files, and automatically type a forward slash when they
+mean to type a backslash. So many actions in macros/keymaps in usenet
+posts have the wrong type of slash. Angband "gurus" are perhaps more
+vulnerable to this than novices.
+
+The game sees this as:
+/ Identify a character
+e Character to be identified
+And outputs "e - Floating Eye" on the message line.
+
+If this sequence gets expanded when you are trying to select an item,
+it will lead to different behaviors.
+Example:
+/ Switch between inventory and equipment
+e Select item e.
+Or:
+/ Switch between inventory and equipment
+e (ignored because invalid) Select item e.
+/ Switch between inventory and equipment
+e Select item e.
+
+~~~~~36
+#####R======================================================================
+#####R7. Inscriptions added by the game
+#####R======================================================================
+
+Some inscriptions are added by the game itself. These can overwrite
+your inscriptions. There are also "fake" and "special" inscriptions,
+which "look" like real inscriptions to the player.
+
+#####G----------------------------------------------------------------------
+#####G7.1 Fake inscriptions
+#####G----------------------------------------------------------------------
+
+These "fake" inscriptions are "covered up" by real inscriptions, but
+will reappear if the real inscription is removed.
+
+"fake" inscriptions are unaffected by the uninscribe command (}).
+
+{cursed} - cursed item
+{empty} - item out of charges
+{tried} - a "flavored" item which the character
+ has used, but whose effects are unknown.
+{N% off} - item bought on sale
+{quest} - this item is a quest item. It may need to be taken to someone.
+
+#####G----------------------------------------------------------------------
+#####G7.2 Auto-inscriptions
+#####G----------------------------------------------------------------------
+
+These added when your character gets a "feeling" about an item.
+In ToME these are "special" inscriptions, like "fake" inscriptions
+above, which don't overwrite user inscriptions. They just hide user
+inscriptions, which are still there.
+
+{terrible} - cursed or broken artifact
+{broken} - broken item
+{cursed} - cursed item
+{uncursed} - previously cursed item
+{average}
+{good} - good (magical) item
+{excellent} - ego item
+{special} - unique item
+{on sale} - displayed only in the store
+
+~~~~~37
+#####R======================================================================
+#####R8. Keys and commands
+#####R======================================================================
+
+This section gives short descriptions of keys and commands used in
+actions and trigger key representations. They are only listed in this
+section if they aren't fully described elsewhere in this FAQ. Not all
+of these keys are actually for "commands". See the normal Angband help
+for a fuller description of these commands. The commands and keysets
+are documented in *****command.txt*0["command.txt"].
+
+#####G----------------------------------------------------------------------
+#####G8.1 Keysets
+#####G----------------------------------------------------------------------
+
+ToME supports two "keysets", which are fully customisable sets of
+keymaps. The "original" command set is close to the built-in commands,
+with some additions for ease of use such as number keys moving you in
+that direction. The "roguelike" command set allows easy movement on a
+keyboard without a numeric keypad. As a consequence its letter keys
+are almost completely "full". These used to be hard-coded by the game,
+but are now fully customisable. The default keymaps are in "pref.prf".
+
+#####G----------------------------------------------------------------------
+#####G8.2 Item selection
+#####G----------------------------------------------------------------------
+
+(*) - gives list of choices
+(-) - selects item on the floor
+(/) - toggles between the inventory and equipment lists.
+
+(space) - shows list of choices. Pressing (space) again hides the list.
+(lower) - selects the inventory item with that letter.
+(upper) - selects the inventory item with that letter, and requires
+ confirmation.
+(digit) - selects first item inscribed with "@#" or "@x#" where 'x' is
+ the command, and '#' is the digit. Only legal items are allowed.
+
+#####G----------------------------------------------------------------------
+#####G8.3 Directions and Movement
+#####G----------------------------------------------------------------------
+
+Original keyset directions
+7 8 9
+4 5 6
+1 2 3
+
+Roguelike keyset directions
+y k u
+h 5 l
+b j n
+
+#####B= Underlying command keys =
+
+;<dir> - walk (with pickup)
++<dir> - alter
+.<dir> - run
+
+Digits AREN'T built-in movement commands in ToME. They are actually
+keymaps found in the standard pref file "pref.prf". The digits are
+direction arguments to the (;) Walk command.
+
+#####G----------------------------------------------------------------------
+#####G8.4 Escape sequences
+#####G----------------------------------------------------------------------
+
+Many [non-printable] characters have a standard printable encoding
+which uses an "escape" character to change the meaning of the
+following character. The backslash character is used as in the C
+language for many keys. The caret '^' is used for control keys.
+
+#####B= Escape sequences =
+\b backspace
+\e escape
+\n newline
+\r return
+\s space
+\t tab
+\xNN hex ASCII char
+\\ (literal) backslash
+\^ (literal) caret
+
+#####B= Backslash =
+In a macro, "\\" followed by a character uses the "underlying" command
+for that character without translation. This is useful in macros to
+avoid keymaps changing the behavior of the macro. In particular this
+can be used to make macros which work for both original and roguelike
+keysets. Keymaps don't have this problem.
+
+#####B= Newline and Return =
+These two characters can be used interchangeably.
+
+#####B= ASCII chars =
+Any ASCII character can be encoded in this way. So many keys will have
+more than one representation. For example, [Enter] can be "\r", "^M",
+and "\x09". The backslash representations are case sensitive, so "\t"
+is [Tab], but "\T" will just be interpreted as "T". The hexadecimal
+number must be exactly 2 digits.
+
+#####B= Escape and Space =
+See section *****macrofaq.txt*13["Clearing the command buffer"] for their main uses.
+
+#####G----------------------------------------------------------------------
+#####G8.5 Repeats and Counts
+#####G----------------------------------------------------------------------
+
+#####B= Auto repeat =
+Some commands will automatically repeat. These are:
+(T) Tunnel
+(B) Bash
+(D) Disarm
+(o) Open
+(c) Close
+(+) Alter
+
+#####B= Number keys =
+
+0 - starts a repeat count. Some commands take a repeat count argument.
+They can be entered as "0<count><cmmd>". If the command is movement,
+it can (must) be preceded by space(s) to separate the direction
+(command) number from the count number.
+~~~~~1
+#####G----------------------------------------------------------------------
+#####G8.6 Messages and Questions
+#####G----------------------------------------------------------------------
+
+#####B= Yes/No queries =
+Yes/No questions can be answered with 'y', 'n', or Esc. These are not
+case sensitive. Only 'y' or 'Y' will respond Yes. 'n', 'N', and Esc
+are No. If the option (quick_messages) is on, any other keypress is
+also No. When the option is off, it will keep waiting for a valid key.
+
+#####B= "-more-" message prompts =
+These may be cleared by Esc(\e), Space(\s), Enter(\r), or Newline(\n).
+If the (quick_messages) option is on, they can be cleared by any key.
+
+#####G----------------------------------------------------------------------
+#####G8.7 Special keys
+#####G----------------------------------------------------------------------
+
+#####B= Function keys =
+Function keys are free for reassignment, but only as macros. [[[[[BFunction]
+[[[[[Bkeys can be modified by Alt, Ctrl, Shift like other keys.]
+
+#####B= Alt keys =
+Alt-modified keys are generally free for reassignment as either macros
+or keymaps.
+
+#####B= Control keys =
+Control keys can be entered in as "^x" where 'x' is the key. Note
+that the case of 'x' is unimportant. This also allows typing control
+keys which would be intercepted by the operating system, such as ^C.
+You must type the caret '^' and the following key separately. Note
+that some have special meanings, such as ^M for Return, and ^H for
+backspace. Some also have special Operating System meanings, such as
+"^Z" in un*x, and "^C" in DOS. Control keys can be trigger keys for
+both macros and keymaps.
+
+#####B= Interrupting the game =
+(^C) This will kill your character and quit the game, after verifying.
+
+#####G----------------------------------------------------------------------
+#####G8.8 Keys used in inscriptions
+#####G----------------------------------------------------------------------
+
+#####B= Confirm command =
+^ Confirm the following command.
+This isn't an actual command, but a character with a special meaning
+inside command strings. {^*} will confirm all actions for the item.
+
+~~~~~38
+#####R======================================================================
+#####R9. Pref files
+#####R======================================================================
+
+All pref files are loaded from and saved to folder "\lib\user". The
+folder "\lib\pref" is unused at this time! The location and name of
+this folder can be configured.
+
+Warning: the directory "\lib\pref" is unused by the game. Pref files
+moved there will never get used (unless the user has redirected the
+folder locations).
+
+Integers can be in hex "0x10", decimal "16", or octal "020" formats.
+These are converted using the C library fn strtol(), and are case
+insensitive.
+
+Decimal numbers start with '1'-'9'.
+Octal numbers must start with '0' (zero).
+Hex numbers start with '0x' or '0X'.
+
+#####G----------------------------------------------------------------------
+#####G9.1 Standard Pref files
+#####G----------------------------------------------------------------------
+
+Below "***" stands for the 3-letter system abbreviations, such as
+"acn", "mac", "win", "x11", ...
+
+"font.prf"
+Includes "font-***.prf" files.
+This file defines special attr/char mappings for "text" mode.
+
+"graf.prf"
+Includes "graf-***.prf" files.
+This file defines special attr/char mappings for "graphics" mode.
+
+"pref.prf"
+Includes "pref-***.prf" files.
+This file defines "default" actions of various kinds. This includes
+mapping the original and roguelike keysets to the underlying keyset.
+
+"user.prf"
+Includes "user-***.prf" files.
+This file defines "override" actions of various kinds. It includes the
+pref files based on system, race, and class.
+
+"xtra-***.prf"
+This file defines special attr/char mappings for "graphics" mode.
+Currently this just maps the player icon based on race and class.
+"new" refers to Adam Bolt's tiles.
+
+[[[[[vWarning:] you shouldn't edit the base pref files without a good reason,
+and understanding what you are doing. Breaking these files can make
+your game unusable. They are, however, the place to make changes that
+should affect all users.
+~~~~~7
+#####G----------------------------------------------------------------------
+#####G9.2 Pref file loading order
+#####G----------------------------------------------------------------------
+
+This loading order follows from the order of includes in "pref.prf".
+Files which are "hard-coded" in the source are preceded with an index.
+The rest are included by the other files. Files which come later will
+overwrite settings from earlier files.
+
+(1) "pref.prf"
+ "message.prf"
+ "pref-***.prf"
+
+(2) "graf.prf"
+ "font-xxx.prf"
+ "graf-***.prf"
+
+(3) "font.prf"
+ "font-xxx.prf"
+ "font-***.prf"
+
+(4) "user.prf"
+ "user-***.prf"
+ "<$RACE>.prf"
+ "<$CLASS>.prf"
+
+(5) "<$PLAYER>.prf"
+
+(6) ".angband.prf"
+
+
+= $RACE =
+Can be one of any of the races in ToME.
+
+= $CLASS =
+Can be one of any of the classes in ToME.
+
+= $PLAYER =
+The name of the current player being loaded or born. See section
+*****macrofaq.txt*15["Automatically loading pref files"] for more information.
+
+#####B= Specific pref files =
+#####B-----------------------------------
+
+"user-mac.prf"
+This is the only user pref file with example macros that ships with
+ToME. A good set of examples.
+
+"pref-win.prf"
+This is the same as (missing) "pref-dos.prf" and "pref-ibm.prf".
+
+"colours.prf"
+Amiga only. Contains Amiga palette.
+
+".angband.prf"
+Only on multi-user systems. This doesn't ship with the source. This
+file must be located in the directory contained in environ variable
+"HOME".
+~~~~~11
+#####G----------------------------------------------------------------------
+#####G9.3 Pref lines summary
+#####G----------------------------------------------------------------------
+
+Comment lines start with a '#' and extend to end of line.
+
+Note: integer values can be specified as decimal, as hexadecimal by
+preceding with an "x", or as octal by using a leading "0" (zero).
+
+E:<tv>:<a> - attr/char values for inventory objects by index
+F:<num>:<a>:<c> - attr/char values for features by index
+K:<num>:<a>:<c> - attr/char values for objects by index
+R:<num>:<a>:<c> - attr/char values for monsters by index
+S:<num>:<a>:<c> - attr/char values for special things by index
+
+A:<str> - action line
+ An action line should be followed by a keymap trigger "C:" line
+ or a macro trigger "P:" line. There can be intervening comments
+ and lines. The same action will be [re]used by all keymap and
+ command lines which follow it until there is another action line.
+P:<str> - macro line
+ <str> a macro encoding of a keypress. (system dependent)
+C:<mode>:<str> - keymap line
+ <mode> 0 = "original, 1 = "roguelike".
+ <str> logical keypress, including backslash codes such as "\e" and
+ control codes such as "^K". (system independent)
+ Note that there are 2 independent sets of keymaps now. Changing a
+ keymap in one doesn't affect the other.
+
+V:<num>:<kv>:<rv>:<gv>:<bv> - specify visual information
+ <num> is the color index (0-255, only 0-15 used)
+ <kv> black (?) value -- unused
+ <rv> red value (0-255)
+ <gv> green value (0-255)
+ <bv> blue value (0-255)
+W:<win>:<flag>:<value> - turn a window flag on/off.
+ <win> window number (1-7)
+ <flag> (0-31)
+ <value> 0 = off, 1 = on
+
+X:<str> - turn option off
+Y:<str> - turn option on
+ <str> the name of an option in option_text[].
+ These are the names displayed in the options screen (=).
+
+?: - conditional expression
+%: - include another pref file
+
+#####G----------------------------------------------------------------------
+#####G9.4 Option lines "X:" and "Y:"
+#####G----------------------------------------------------------------------
+
+Options and their descriptions are listed in help file *****option.txt*0["option.txt"].
+These options are set within the game using the (=) Options command,
+and the option names are the ones displayed within parentheses in the
+options screen.
+
+#####B= Common options =
+rogue_like_commands
+use_old_target
+always_pickup
+depth_in_feet
+alert_hitpoint
+auto_haggle
+auto_scum
+
+#####G----------------------------------------------------------------------
+#####G9.5 Conditional expression lines "?:"
+#####G----------------------------------------------------------------------
+
+expressions are lisp-like prefix notation.
+names (class, race, ...) aren't placed in quotes.
+AND - logical AND
+IOR - inclusive OR
+EQU - (string) equals
+NOT - logical negation
+LEQ - (string) less than or equal to
+GEQ - (string) greater than or equal to
+[,] - group expressions
+$CLASS - current class
+$GRAF - 3-letter graphics abbr in "graf-***.prf" (old, new)
+$PLAYER - current player name
+$RACE - current race
+$SYS - 3-letter system abbr in "pref-***.prf" (ami, mac, win,...)
+
+0 - false
+1 - true (can't just be non-zero)
+
+If the conditional expression is false all pref file commands
+encountered until the next conditional pref line are skipped.
+
+This isn't an actual command. It only works in pref files.
+
+The variables $CLASS, $GRAF, $PLAYER, $RACE, $PLAYER, $SYS and the
+string values they take on are case sensitive. The values also can't
+contain spaces. These constraints on the values hold when they are
+used in a pref file, but might not when used as pref filenames.
+
+This can be "turned back on" using the pref line "?:1", which is
+generally the last line in a file which contains conditional macros,
+to make sure that any files loaded after it don't get ignored as well.
+
+#####G----------------------------------------------------------------------
+#####G9.6 Macro trigger lines "P:"
+#####G----------------------------------------------------------------------
+
+All "special" keys are translated by "main-***.c" into encoded "macro
+triggers". These macro triggers have the encoded form "^_MMMxSS\r",
+where the "modifier" flags are stored in "MMM", and the two digit
+hexadecimal scan code of the keypress is stored in "SS". See source
+file "main-ibm.c" and others for more info. Note that because these
+scan codes are system-dependent, macro trigger encodings are as well.
+Keymaps are used for system independent mapping of triggers to actions.
+
+#####BModifier flags
+
+A - Alt
+C - Control
+S - Shift
+O - Option key (Mac)
+
+#####BIBM Scan codes
+
+x47 - keypad 7
+x48 - keypad 8
+x49 - keypad 9
+x4A - keypad -
+x4B - keypad 4
+x4C - keypad 5
+x4D - keypad 6
+x4E - keypad +
+x4F - keypad 1
+x50 - keypad 2
+x51 - keypad 3
+x52 - keypad Ins / .
+x53 - keypad Del / Enter
+x45 - Pause
+
+Others can be found using the "Query a macro" feature.
+
+Note that scan codes can't be assumed to be "in order", even for keys
+like function keys which "logically" should be!
+
+Note that you can't always just add a modifier to a known scan code
+because that combination might not be recognised by the hardware or
+the translation code in "main-***.c".
+
+Example: a Windows system will recognise function key F1, Shift-F1,
+and Ctrl-F1, but not Ctrl-Shift-F1. Similarly Pause and Alt-Pause are
+recognised, but not Ctrl-Pause, and Shift-Pause gives the same
+encoding as Pause alone.
+
+#####G----------------------------------------------------------------------
+#####G9.7 Saving to a pref file
+#####G----------------------------------------------------------------------
+
+Commands "Append macros to file" and "Append keymaps to file" don't
+erase the previous macros or keymaps. Instead they are appended. Note
+that this can produce *large* files after a while. Newer versions
+append to "<$PLAYER>.prf" by default, whereas older versions appended
+to "user.prf". The appended sections are preceded by headers of the
+form "Automatic macro/keymap dump". Using a distinctive comment line
+such as ###... after your entries can make editing the appended ones
+easier.
+
+[[[[[BNote: macros and keymaps aren't saved in the character file, so they]
+[[[[[Bmust be saved separately. All macros and keymaps entered by the user]
+[[[[[Bare lost when Angband terminates.]
+
+Note: keeping macros in the <$PLAYER>.prf files allows several users
+to share the same installation without interfering with each other.
+You can easily reuse or share preferences by moving them into a pref
+file "<my-name>.prf" and using the pref line "%:<my-name>.prf" to
+include them in "user.prf" for single user installations, or
+<$PLAYER>.prf for multi-user installations.
+
+#####G----------------------------------------------------------------------
+#####G9.8 Editing pref files
+#####G----------------------------------------------------------------------
+
+This is still most easily done in a text editor.
+
+~~~~~39
+#####R======================================================================
+#####R10. Macro editing commands
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G10.1 (") Enter a User Pref Command
+#####G----------------------------------------------------------------------
+
+This allows entering a single pref line.
+Example: "X:auto_scum" turns auto-scum off.
+
+Example "A:<str>" sets the current action string. If you open the
+"Interact with macros" screen this action will be the default used.
+Then using the (") command again with "P:<key>" will create a macro
+for the action <str> previously entered.
+
+Not all pref commands can be used here, or are meaningful.
+The "pseudo" pref commands (?), (%) cannot be used here.
+
+See section *****macrofaq.txt*20["Advanced macro techniques"] for ways to [ab]use this.
+
+#####G----------------------------------------------------------------------
+#####G10.2 (@) Interact with macros
+#####G----------------------------------------------------------------------
+
+#####B= Vanilla command set = (2.8.3 - 2.9.1)
+#####B-----------------------------------
+Load a user pref file
+Append macros to a file
+Query a macro action
+Create a macro
+Remove a macro
+Append keymaps to a file
+Query a keymap
+Create a keymap
+Remove a keymap
+Enter a new action
+
+#####B= Load a user pref file =
+#####B-----------------------------------
+Loads a user pref file from "lib\user". Defaults to the name of the
+current character. Macros/keymaps loaded will replace existing ones.
+
+#####B= Append macros to a file =
+#####B-----------------------------------
+Macros are dumped in macro list order. Newer ones are at the end.
+Macros are *appended* to the file. The old one isn't overwritten. This
+prevents you from accidentally wiping out your old pref file. However,
+the file can grow very long without your noticing it. Placing a line
+of ###'s at the end of your macros can help sort out what is what.
+Macros are labeled with comment "# Macro 'NNN' ". These numbers are the
+internal macro list numbers, and have no relation to key scan codes.
+The filename must end in ".prf". It will save correctly without this
+extension, or with a different one, but if you save as "<$NAME>"
+instead of "<$NAME>.prf", it won't be automatically loaded when you
+load the character with that name.
+
+#####B= Query a macro =
+#####B-----------------------------------
+Press the trigger key to test at the prompt.
+This will show "Found a macro" on the message line if it found one,
+and the line "Trigger: <trigger>". This will show "Found no macro"
+on the message line if it didn't find a macro. Some keys such as
+function keys won't be recognised by the prompt. It will wait until
+you hit a key it recognises.
+
+This command doesn't alter any settings. It will return to the main
+menu after you hit any key it recognises.
+
+#####B= Create a macro =
+#####B-----------------------------------
+After choosing this command, press the trigger key for the macro.
+The internal form will be shown after the "Trigger: " prompt.
+Note that some keys may not be recognised for remapping, such as the
+new Windows keys, as well as modifier keys such as Alt, Control, Shift
+pressed by themselves. In this case it will continue to wait for a
+valid trigger key.
+
+The current action (if any) will be shown *below* the "Trigger: "
+prompt line. On the prompt line ("Action: ") will be shown the last
+macro sequence entered. This is the action in the "action buffer".
+This isn't necessarily the macro sequence currently bound to this key.
+This is the action that will be bound to the current trigger key if
+you hit Enter.
+
+You may type in an action string to replace the one after the prompt.
+Hit Enter when you are finished.
+
+For ToME you can quit the command assignment by hitting
+Esc. The new action entered won't be assigned, and the previous one
+will remain unaltered.
+
+#####B= Remove a macro =
+#####B-----------------------------------
+Removes the macro from the trigger key by creating an identity macro
+on that key for itself. So the macro isn't completely removed, just
+overwritten. The new identity macro will be saved when the macros are
+appended to a file. This is different from the "Remove a keymap"
+command, which completely removes the keymap.
+
+#####B----------------------------------------------------------------------
+The following "keymap" commands only apply to the current "mode"
+(original/roguelike). Keymaps for the other mode will be unaffected.
+Because keymaps can only be bound to trigger keys which have a system
+independent representation, some key presses won't be recognised by
+these editing commands. They will instead wait until you press a valid
+trigger key.
+#####B----------------------------------------------------------------------
+
+#####B= Append keymaps to a file =
+#####B-----------------------------------
+Works just like "Append macros to a file". These are appended after a
+header comment "# Automatic keymap dump".
+
+#####B= Query a keymap =
+#####B-----------------------------------
+Works just like "Query a macro". This will show "Found a keymap" on
+the message line if it found one, and will display "Keypress: <map>".
+This will show "Found no keymap" on the message line if it didn't
+find a keymap. This command doesn't alter any settings. It will return
+to the main menu after you hit any key it recognises.
+
+#####B= Create a keymap =
+#####B-----------------------------------
+Works just like the "Create a macro" command. Keymaps can only be
+assigned to keys which have a system independent representation. Note
+that creating a keymap will cause the behavior of any macro whose
+action string contains that key to change.
+
+#####B= Remove a keymap =
+#####B-----------------------------------
+Removes the keymap completely from the trigger key. If the key had a
+built-in command it can now be used again. Note that removing a keymap
+will cause the behavior of any macro whose action string contained
+that key to change. This behaves differently from the "Remove a macro"
+command, which creates an identity macro.
+
+If the original "command" was itself a keymap, removing a user-entered
+keymap won't restore it. Example: the key (n) is bound to the built-in
+command "Repeat last action" in file "pref.prf" via a keymap. If you
+add a keymap for (n) and then remove it, the "Repeat last command"
+functionality won't be restored. You will have to add it back by hand,
+or reload a pref file that contains that stored keymap. [[[[[BIn particular]
+[[[[[Balmost all roguelike commands are now implemented as keymaps.]
+
+#####B= Enter a new action =
+#####B-----------------------------------
+Allows entering a new action. Actions are entered into a static buffer
+which is shared by both macros and keymaps. The action string entered
+will become the default action for creating a keymap or action, and
+will only change when a keymap or macro is created with a different
+action string, or when one is queried. Note that the same action can
+be bound to multiple trigger keys by hitting Enter when using the
+commands to create a keymap/macro.
+
+~~~~~20
+#####R======================================================================
+#####R11. Advanced Macro Techniques
+#####R======================================================================
+
+This section outlines advanced techniques not really required for game
+play. But macros become addictive after a while ...
+
+Action strings in this section are enclosed in braces {} because many
+use a double quote (") inside the action string. These are not
+inscriptions.
+
+#####G----------------------------------------------------------------------
+#####G11.1 Set current action using (@) command in an action
+#####G----------------------------------------------------------------------
+
+{"@0<str>\r\e}
+@ - Interact with macros
+0 - Enter a new action
+<str>- (action string)
+\r - Enter the action
+\e - Exit the macro editor
+
+This will work when bound to a macro.
+
+#####G----------------------------------------------------------------------
+#####G11.2 Set current action using (") command in an action
+#####G----------------------------------------------------------------------
+
+{"A:<action>\r} - sets the current action.
+" - Enter pref line
+A: - Action line
+<str>- (action string)
+\r - Enter the action
+
+This works in either a macro or keymap.
+
+#####G----------------------------------------------------------------------
+#####G11.3 Create a new keymap using (") command in an action
+#####G----------------------------------------------------------------------
+
+{"A:<act>\r"C:0:<key>\r}
+Here <act> can't contain an '\r' or '\e'.
+
+Example {"A:z0\r"C:0:J\r} binds action "z0" to (standard) keymap 'J'.
+
+#####G----------------------------------------------------------------------
+#####G11.4 Create a new macro using (") command in an action
+#####G----------------------------------------------------------------------
+
+{"A:<act>\r"P:<key>\r}
+Here <act> can't contain an '\r' or '\e'.
+Here <key> is a standard key. (not a "special" one like F1, \b, or ^A)
+
+Example
+{"A:<action1>\r"P:j\r} binds action <action1> to trigger 'j'.
+{"A:<action2>\r"P:j\r} binds action <action2> to trigger 'j'.
+If we bind these 2 macros to different trigger keys, the action that
+is on key (j) can be swapped back and forth.
+
+#####G----------------------------------------------------------------------
+#####G11.5 Turning an option on/off in an action
+#####G----------------------------------------------------------------------
+
+Turn an option on:
+{"Y:<option_name>\r}
+
+Turn an option of:
+{"Y:<option_name>\r}
+
+Example: Turn (quick_messages) on, do an action, and turn it back off:
+{"Y:quick_messages\r<action>"X:quick_messages\r}
+
+This will work in either a macro or keymap. <option_name> is the name
+of the option as it appears in the option editor accessed through the
+(=) command. These are also listed in the help file *****option.txt*0["option.txt"]. Note
+that option names contain underscores instead of spaces.
+
+#####G----------------------------------------------------------------------
+#####G11.6 Inscribe/Uninscribe an item in an action
+#####G----------------------------------------------------------------------
+
+(These action strings are enclosed in double quotes)
+
+Inscribe an item:
+"{<item>\s<inscr>\r"
+
+Uninscribe an item:
+"}<item>\r"
+
+<item> must be the inventory letter of the item, possibly preceded by
+a '/' to switch to the equipment list. You can't use digit labels for
+items with inscriptions that contain the command triggers '{' or '}',
+but you can use "@<digit>".
+
+This will work in either a macro or keymap.
+
+~~~~~41
+#####R======================================================================
+#####R12. Problems
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G12.1 Keys to avoid remapping
+#####G----------------------------------------------------------------------
+
+These don't really *need* to be avoided, but all carry dangers of one
+kind or another. You should think through potential problems before
+deciding to use them. You have been warned.
+
+#####B= Navigation keys =
+Enter, Esc, Backspace, ...
+These aren't a good choice unless you *really* need them. If you do it
+is far better to use keymaps. If you bind a macro to the Enter key,
+you will lose the ability to enter line-based commands like Inscribe.
+
+#####B= Commands generated internally =
+(_) Enter store
+This command is generated internally by the game when the player moves
+onto the door of a store. In some versions, if this key has a keymap
+bound to it, that will fire when you try to enter a store.
+
+#####B= Keys with important Operating System meanings =
+^Z (un*x) Suspends the game and returns to the command shell. This
+ is an operating system command, not an Angband command.
+ Command "fg" returns to Angband.
+^\, ^D, ^S
+ These are keys that shouldn't be bound to macros or have their
+ behavior altered.
+
+#####B= Keys with dangerous ToME meanings =
+(Q) Quit (commit suicide), (k) destroy item, (^A) Enter Debug mode...
+Using these as triggers is dangerous in case, for some reason, you
+wind up in a situation where the macro hasn't loaded or is disabled.
+You also don't want to get into a habit of typing these too fast.
+
+#####B= Selection keys =
+(e) Equipment, (i) Inventory, (-) Floor item, (/) Switch inventory
+lists. You should avoid binding these as macro triggers, to prevent
+making inventory and choice management next to impossible. But even as
+keymaps they hold some dangers.
+Example: you bind keymap on '-' to destroy item on the floor. Now if
+you try to do an action on a floor item, and it fails (such as using
+rod to identify), then the '-' can be taken from the input stream and
+used as a keymap, which would destroy the item you tried to identify.
+
+#####B= Response keys =
+(y) yes, (n) no, (Esc) cancel, (Space) skip message,...
+Binding macros to these is a [[[[[vVery Bad Idea.] Macro expansion will then
+be done when you answer a question like "Are you sure you want to quit
+the game without saving?". The expanded macro action string will be
+used as the input, and may not lead to the answer you were trying for.
+Keymaps don't have this problem. As a rule you should never use a
+macro instead of a keymap unless necessary.
+
+#####G----------------------------------------------------------------------
+#####G12.2 Num lock
+#####G----------------------------------------------------------------------
+
+Whether/not NumLock is on can make a difference for some macros.
+For example, if NumLock is on under X11 the 'X' macro won't work.
+
+#####G----------------------------------------------------------------------
+#####G12.3 Recovering
+#####G----------------------------------------------------------------------
+
+Restarting ToME clears all macros entered during the last session.
+
+You can use "Load a pref file" in the "Interact with macros" screen to
+reload a good set of prefs, overwriting bad ones being used. This will
+not "erase" a macro/keymap which doesn't have a corresponding saved
+one in the pref file. So if you add a macro/keymap to a trigger key
+which didn't have anything bound to it, reloading the pref file won't
+restore the key to its original state.
+
+If you still have problems, restore or edit any modified *.prf files
+that might be loaded.
+
+Try saving your macros, and examine them to see what went wrong.
+
+[[[[[BYou can use the backspace '\' key at the command prompt to use the]
+[[[[[Boriginal "underlying" command bound to that key. For example, if you]
+[[[[[Bbound the key '@' to a macro, you wouldn't be able to enter the macro]
+[[[[[Beditor to rebind it to itself.] Pressing '\' first, then '@' causes the
+command handler to use the built-in command, which allows you to enter
+the command editor. Note: when you use the backspace inside an action
+string, you have to double it as "\\". Do not use just a single back-
+slash, or it will be ignored, and possibly alter the meaning of the
+character that follows it.
+
+You can remove a macro/keymap from an essential key (such as the Esc
+key). Use the (@) "Interact with macros" command to access the remove
+commands.
+
+#####G----------------------------------------------------------------------
+#####G12.4 Unrecognised keys
+#####G----------------------------------------------------------------------
+
+#####B= Un*x =
+
+Function keys may not be recognised on some Un*x systems.
+
+#####B= PC/Dos/Windows =
+Doesn't recognise the WINDOWS key (start menu) or the APPLICATION key
+(context menu).
+
+On some systems, doesn't recognise modifier keys (Alt, Ctrl, Shift) on
+keypad keys when NumLock is on.
+
+See special_key_list[] in "main-win.c" for list of "special" keys that
+are recognised.
+
+#####G----------------------------------------------------------------------
+#####G12.5 Nonexistent commands
+#####G----------------------------------------------------------------------
+
+Macros and keymaps can only be bound to keypresses. The game state
+changing isn't a keypress, so you can't trigger an action when you
+become hungry, blind, confused, slowed, pseudo-id an item, pick up an
+item, gain a level, have a rod recharge, or any other event that isn't
+directly triggered by a keypress.
+
+"Attacking" also isn't a command, but you can use commands (+) Alter
+grid, (;) Walk, and (.) Run.
+
+So you don't really _attack_ Morgoth, you just _alter_ him. First he's
+alive, then he's not. :)
+
+#####G----------------------------------------------------------------------
+#####G12.6 File permissions
+#####G----------------------------------------------------------------------
+
+If you lack write permission to the pref file currently loaded by the
+game, try saving to a file with a new name. The macros can be copied
+over "by hand" later.
+
+~~~~~42
+#####R======================================================================
+#####R13. Miscellaneous
+#####R======================================================================
+
+#####G----------------------------------------------------------------------
+#####G13.1 References
+#####G----------------------------------------------------------------------
+
+*****command.txt*0["COMMAND.TXT"]
+- lists standard and roguelike keys and commands. full descriptions.
+- long description of command behavior.
+- intro to macros and user pref files.
+
+*****dungeon.txt*8["DUNGEON.TXT"]
+- look under "Objects Found in the Dungeon".
+
+*****option.txt*0["OPTION.TXT"]
+- list of options and their descriptions.
+
+"INSCRIPTIONS.HTML"
+- short intro by Julian Lighton. Available from
+"http://www.fragment.com/~jl8e/angband/inscriptions.html".
+
+#####G----------------------------------------------------------------------
+#####G13.2 Contributors
+#####G----------------------------------------------------------------------
+
+This FAQ was largely compiled from newsgroup postings to "r.g.r.a".
+So thanks to the generous contributors to the newsgroup! Email
+addresses have been removed to foil spam-bots.
+
+Ben Harrison -- maintainer: Angband 2.7.1 - 2.8.5, =Ben= in source.
+Robert Ruehlman -- maintainer: Angband 2.9.0 - present.
+DarkGod -- maintainer: PernAngband 2.9.9a - present
+
+Scott Bigham, DamonShawX, Jonathan Ellis, George W. Harris, Roger
+Hoyle, Graham S. Johnson, Chris Kern, Matthias Kurzke, Steve Lamb,
+Julian Lighton, Art Mruczek, Daniel Nash, Timo Pietilä, Jack Wise,
+Greg Wooledge, and others.
+
+#####G----------------------------------------------------------------------
+#####G13.3 Legalese
+#####G----------------------------------------------------------------------
+
+Copyright 2000 Jim Lyon and others. Redistribution of unaltered copies
+of this document is permitted without restriction. Distribution of
+altered copies is permitted without restriction as long as the
+alteration does not significantly alter the content. (For example,
+translation and conversion to another format is permitted.)
+Distribution of all other altered copies is permitted as long as credit
+for previous authors is maintained, the contact information is
+replaced with that of the alterer, and redistribution is not further
+restricted.
+
+Edited for PernAngband V5.x.x by Dawnmist with permission from Jim Lyon
+August 2001. All comments to angband@dawnmist.8m.com
diff --git a/lib/help/magic.hlp b/lib/help/magic.hlp
new file mode 100644
index 00000000..382451c3
--- /dev/null
+++ b/lib/help/magic.hlp
@@ -0,0 +1,41 @@
+|||||oy
+~~~~~01|Magic|Index
+~~~~~02|Help|Magic
+#####RWelcome to the ToME Magic Help System.
+#####R=============================================
+
+Please choose one of the following help files:
+
+General Info
+
+ *****/amagic.txt*0[(a) The ToME magic system]
+ *****/bmagic.txt*02[(b) Wands and Staves]
+
+Magic schools
+
+ *****/cm_air.txt*0[(c) The Air School]
+ *****/dm_convey.txt*0[(d) The Conveyance School]
+ *****/em_demono.txt*0[(e) The Demonology School]
+ *****/fm_divin.txt*0[(f) The Divination School]
+ *****/gm_earth.txt*0[(g) The Earth School]
+ *****/hm_fire.txt*0[(h) The Fire School]
+ *****/im_geoman.txt*0[(i) The Geomancy School]
+ *****/jm_mana.txt*0[(j) The Mana School]
+ *****/km_meta.txt*0[(k) The Meta School]
+ *****/lm_mind.txt*0[(l) The Mind School]
+ *****/mm_nature.txt*0[(m) The Nature School]
+ *****/nm_necrom.txt*0[(n) The Necromancy School]
+ *****/om_tempo.txt*0[(o) The Temporal School]
+ *****/pm_udun.txt*0[(p) The Udun School]
+ *****/qm_water.txt*0[(q) The Water School]
+
+Other powers accessed by the 'm' menu
+
+ *****/rm_mimic.txt*0[(r) Mimicry Powers]
+ *****/sm_mindcr.txt*0[(s) Mindcrafting Powers]
+ *****/tm_music.txt*0[(t) Musical Songs]
+ *****/um_symbio.txt*0[(u) Symbiotic Powers]
+ *****/vm_thaum.txt*0[(v) Thaumaturgical Spells]
+
+ *****/zhelp.hlp*0[(z) Main Help menu]
+ \ No newline at end of file
diff --git a/lib/help/magic.txt b/lib/help/magic.txt
new file mode 100644
index 00000000..93486f0b
--- /dev/null
+++ b/lib/help/magic.txt
@@ -0,0 +1,143 @@
+|||||oy
+~~~~~03|Magic
+#####R === ToME Magic system ===
+
+*****magic.txt*02[Wands and Staves]
+
+For the basics of how to use skills, please see *****skills.txt*0[Using Skills].
+
+In ToME you have a basic *****skills.txt*21[Magic] skill. This skill is one of the most
+important ones for a spellcaster, since it is responsible for how much mana you
+have. You can never have too much of it. If you like magical devices, the
+Magical Device skill is also important, since it controls the Magical
+Device ability of your character. This ability again dictates the fail rates
+of use of wands/rods/staffs and activation of random-artifacts/artifacts, and
+it will also increase the power of these items.
+~~~~~01|Magic|Schools
+ToME uses skills to define the various schools of magic. There are 11 primary
+schools:
+ *****m_mana.txt*0[Mana] *****m_fire.txt*0[Fire] *****m_water.txt*0[Water]
+ *****m_air.txt*0[Air] *****m_earth.txt*0[Earth] *****m_meta.txt*0[Meta]
+ *****m_convey.txt*0[Conveyance] *****m_divin.txt*0[Divination] *****m_tempo.txt*0[Temporal]
+ *****m_mind.txt*0[Mind] *****m_nature.txt*0[Nature]
+
+Other magical skills, generally being used primarily by characters of a
+specific class, are:
+ *****m_demono.txt*0[Demonology] *****m_necrom.txt*0[Necromancy] *****skills.txt*36[Runecraft]
+ *****m_thaum.txt*0[Thaumaturgy] *****skills.txt*49[Alchemy] *****m_geoman.txt*0[Geomancy]
+
+The *****m_demono.txt*0[Demonology] skill is primarily used by *****c_demono.txt*0[Demonologists] for their special
+spells, whereas the *****m_necrom.txt*0[Necromancy] skill is used by *****c_necro.txt*0[Necromancers] for their own set
+of special spells.
+The same goes for *****skills.txt*36[Runecraft], which is used by *****c_runecr.txt*0[Runecrafters] to allow use of more
+difficult runes or rune-combinations. *****m_thaum.txt*0[Thaumaturgy] gives you randomly chosen
+attack spells, and as such each game with it will be different. *****c_geoman.txt*0[Geomancers]
+harness the powers of the elements using *****m_geoman.txt*0[Geomancy]. Lastly we have
+*****skills.txt*49[Alchemy], which is used by *****c_alchem.txt*0[Alchemists].
+
+In addition to the schools of magic, you can get access to special sets of
+spells if you worship a God. There are currently four good Gods,
+*****g_eru.txt*0[Eru Iluvatar], *****g_manwe.txt*0[Manwe Sulimo], *****g_yavann.txt*0[Yavanna Kementari] and *****g_tulkas.txt*0[Tulkas]. There is also an evil
+god, *****g_melkor.txt*0[Melkor]. Each of them gives you access to different types of spells.
+
+*****c_pr_drk.txt*0[Worshippers of Melkor] also have access to the special *****m_udun.txt*0[Udun] school of magic,
+whereas other *****c_priest.txt*0[Priests] and *****c_mindcr.txt*0[Mindcrafters] can use *****m_mindcr.txt*0[Mindcrafting Powers].
+
+*****c_symbia.txt*0[Symbiants] have access to their own special brand of *****m_symbio.txt*0[magic powers], and *****c_bard.txt*0[Bards] have
+access to *****m_music.txt*0[Songs], which affect creatures in ways that can appear to be magical.
+
+The 11 different primary schools give you access to different spells of
+variable usefulness. The way they work is that adding skill points to a
+specific school will enable you to get higher level spells for that specific
+school. By level requirements for a specific spell you could actually say skill
+requirement, since they correlate exactly. Let's take a simple example:
+If you have the *****m_mana.txt*0[Mana] school skill at level 24.000, it means you can use any
+spell in the mana school up to and including those requiring level 24. [[[[[BThere are]
+[[[[[Balso some spells requiring a certain skill level in two schools, and there is a]
+[[[[[Bpossibility of spells requiring three or more. For this kind of spells the ]
+[[[[[Bspell level is determined by taking an average of the necessary skills. ]
+When calculating spell level for spells which require more than one school,
+sorcery (or god-granted access) can be used in place of the primary schools in
+the normal way. Once the average has been calculated, any bonus from the
+spell-power skill can also be applied as normal. If one of the schools required
+is the Udun school, then the appropriate bonus from character level will be
+applied. Lastly, if you look at a spell, and the spell level reads -2 or some
+other negative value while it's also grayed out, that means you need to
+increase the corresponding school's skill level by 3, since only 2 will have
+it end up on spell level 0, where it still is unusable. If it reads n/a, you
+currently have no skill points in that school.
+
+Another thing that should be explained about the skills and schools of magic
+right now, is that the skill doesn't stop being useful only for gaining spells.
+The higher the skill level, the higher the spell level will be, and the more
+powerful your spells will be. For instance, say you have the *****m_mana.txt*0[Mana] skill at
+level 24. Now, the Manathrust spell is one of the spells for that school
+that only requires skill level 1, but since you've got skill at level 24, the
+power of the spell is increased as well. For comparison, a level 1
+Manathrust costs 1 mana and does 4d2 damage, while at level 24 it costs 12
+mana and does a whopping 27d10 damage.
+
+The *****skills.txt*23[Sorcery] skill is a nice skill, since it gives you
+access to all the 11 primary schools of magic, just as if you'd spent an equal
+amount of skill points in all the skills. It's available to any mage character,
+but only a *****c_sorcer.txt*0[Sorceror] will be able to be proficient in it. Also, having this
+skill at level 1 will give you a hitpoint-penalty of 1%, all the way up to
+skill level 50, with a hitpoint-penalty of 50%. There are also ToHit and ToDam
+penalties for sorcery, so don't choose sorcery if you plan to do much fighting.
+
+There is also the *****skills.txt*22[Spell Power] skill. This skill is rather nice, since it
+will augment the power of spells you already know. The distinction between this
+and the others, is that it will not grant you new spells, but instead increases
+the levels of spells. At level 50 it grants 20 extra spell levels. [[[[[BThis skill ]
+[[[[[Bonly affects the 11 primary schools] (Mana, Earth, Air, Fire, Water, Meta,
+Mind, Temporal, Conveyance, Divination and Nature) as well as Geomancy and the
+spells granted by the Gods.
+
+There is also the Magic-Device skill which affects your ability to use wands,
+staves, rods and to activate special objects. It also affects the spell-levels
+of the staff and wand spells, as explained below.
+~~~~~02|Wands
+~~~~~04|Magic|Wands and Staves
+~~~~~05|Staves
+#####GWands and Staves
+
+Wands and staves (sticks) operate in a similar fashion, and in fact most of
+them use the same spells with the same effects. When you pick up a stick, you'll
+see it has two numbers in the format [x|y] in addition to the number of charges
+it holds. By increasing your magic-device skill you can increase the level (and
+hence the power) of the spell in that stick. The x value are skill level
+bonuses which the staff itself holds, and these are added onto your existing
+magic-device skill for the purpose of using the staff. The y value is the
+maximum possible skill level for that stick. Things are balanced by the use of a
+"minimum magic-device skill level required to raise spell level". Here's an
+example:
+A Staff of Sense Hidden [1|10]. Your magic device skill is at 6. If you were to
+identify the staff and then 'I'nspect it, you would see the following
+information:
+
+&&&&&w wSwpwewlwlw wdwewswcwrwiwbwtwiwownw:w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&w wDwewtwewcwtwsw wtwhwew wtwrwawpwsw wiwnw waw wcwewrwtwawiwnw wrwawdwiwuwsw wawrwowuwnwdw wywowuw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&w wAwtw wlwewvwewlw w1w5w wiwtw wawlwlwowwwsw wywowuw wtwow wswewnwswew wiwnwvwiwswiwbwlwew wfwowrw waw wwwhwiwlwew w w w w w w w w w w w w w w w w w w w w w w
+&&&&&w wSwpwewlwlw wlwewvwewlw:w B3w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&w wMwiwnwiwmwuwnw wMwawgwiwcw wDwewvwiwcwew wlwewvwewlw wtwow wiwnwcwrwewawswew wswpwewlwlw wlwewvwewlw:w B5w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&w wSwpwewlwlw wfwawiwlw:w g2g3w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&w wSwpwewlwlw wiwnwfwow:w yryaydy y1y3w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+
+The Spell level is the level at which the spell will actually be cast.
+Spell fail is the spell fail percentage. The spell info may contain the radius
+of effect, amount of damage, or duration the spell might last.
+The Minimum Magic Device level to increase your spell level is just that. If
+your magic device skill was less than this level, then the staff would be
+casting the spell at level one. Our magic device skill is 6. Therefore we are
+casting at level 2 (at skill level 5, we should be casting the spell at level
+1). Then we add the bonus from the staff of 1, which gives us our spell level
+of 3. If our magic device in this example had been 14, this would have given us
+a spell level of 1 + (14 - 5 + 1) = 11. This is calculated from the formula:
+spell level = staff bonus + (magic device - minimum magic device + 1)).
+However given that the maximum spell level with this staff is 10, you'll be
+casting with a spell level of 10.
+As you get deeper into the dungeons, you will find sticks with higher bonuses
+and maximum spell levels.
+
+ Written by: vrak AKA Per-Arne Holtmon Akoe
+ Wands and Staves section added by fearoffours
diff --git a/lib/help/newbie.hlp b/lib/help/newbie.hlp
new file mode 100644
index 00000000..2dda8dbc
--- /dev/null
+++ b/lib/help/newbie.hlp
@@ -0,0 +1,16 @@
+|||||oy
+~~~~~01|Help|New players
+#####RWelcome to the ToME New Players Help System.
+#####R=============================================
+
+Please choose one of the following help files:
+
+ *****/awhattome.txt*0[(a) What is ToME?]
+ *****/bbirth.txt*0[(b) Creating a character] Race, class, gods, stats + more
+ *****/cexplore.hlp*0[(c) Exploring the town and dungeon] Dungeons, commands, features
+ *****/dexperien.hlp*0[(d) Gaining experience] Skills and abilities
+ *****/emagic.hlp*0[(e) Using magic and magical items] Magic schools, the 'm' menu, wands etc
+ *****/fTANG.txt*0[(f) The ToME newbie guide]
+
+ *****/zhelp.hlp*0[(z) Main Help menu]
+
diff --git a/lib/help/option.txt b/lib/help/option.txt
new file mode 100644
index 00000000..33e0ce9e
--- /dev/null
+++ b/lib/help/option.txt
@@ -0,0 +1,715 @@
+|||||oy
+~~~~~05|Options
+#####R=== Options and Effects (ToME 2.1.x) ===
+
+Most of the options are accessible through the '=' command, which provides
+an interface to the various sets of options available to the player.
+
+In the descriptions below, each option is listed as the textual summary
+which is shown on the options screen, plus the internal name of the
+option in brackets, followed by a textual description of the option.
+
+Note that the internal name of the option can be used in user pref files
+to force the option to a given setting; see *****command.txt*105["command.txt"] for more info.
+
+Various concepts are mentioned in the descriptions below, including "disturb"
+(cancel any running, resting, or repeated commands, which are in progress),
+"flush" (forget any keypresses waiting in the keypress queue, including any
+macros in progress), and "fresh" (dump any pending output to the screen).
+
+~~~~~06|Options|Startup
+#####R=== Birth/Startup Options ===
+
+The birth or startup options are only able to be changed during character
+creation, and can be accessed by typing '=' during the creation process. They
+can also be viewed from the option menu while playing, but not changed then.
+
+#####GMaximise stats [maximize]
+ Maximise causes the race and class stat bonuses to be applied like
+ equipment bonuses. This usually makes the character harder at the
+ beginning of the game, but easier later on, since the stats are no longer
+ limited to a "natural" value of 28 (18/100).
+
+#####GPreserve artifacts [preserve]
+ Preserve artifacts cancels all level feelings of the "special" variety,
+ but allows missed artifacts to be "saved" by wandering monsters and
+ found again at a later time. This only works for non-identified artifacts.
+
+#####GSpecify 'minimal' stats [autoroll]
+ Uses the standard autoroller for character creation. This allows the player
+ to specify a set of minimum values for the stats, and the game will keep
+ rerolling until it achieves them (or 1 million rolls, whichever comes
+ first). Be warned, however, that there is a maximum total power permitted
+ for a starting character. Setting one stat to near maximum is easily
+ achievable; 2 is reasonable; but 3 would require the remaining 3 stats to
+ be near their minimum values.
+
+#####GGenerate character using a point system [point_based]
+ Allows the player to distribute a certain number of points among her stats.
+ It results in the player being able to get one or two really high stats, at
+ the expense of other not-so-important stats; or to have a well-rounded
+ character who is above average (but not great) in all stats. Unused points
+ convert into starting gold for the player.
+
+#####GGenerate persistent dungeons [permanent_levels]
+ Setting this option means that level 1 of the dungeon will always have
+ the same dungeon map each time you visit it (like the Nethack dungeon).
+
+#####GAlways generate very unusual rooms [ironman_rooms]
+ Tries to place a special room or vault on every dungeon level. Very fun,
+ but extremely deadly - imagine that Greater Checkerboard Vault with Lokkak
+ on dungeon level 1!
+
+#####GAllow notes to be written to a file [take_notes]
+ Allows any player-written notes (with the "|" command) to be written to
+ a file and kept (instead of being put in the message list).
+
+#####GAutomatically note important events [auto_notes]
+ Used in conjunction with the take_notes option, this option makes a note
+ each time you gain a level, kill a unique, find an artifact, etc.
+
+#####GFast autoroller (NOT on multiuser systems) [fast_autoroller]
+ The normal autoroller has a built-in delay that helps prevent it from
+ overloading a system. This option reduces that delay, allowing characters
+ to be rolled in a much shorter time, but should not be used on multiuser
+ systems.
+
+#####GAllow use of some 'joke' monsters [joke_monsters]
+ Allows monsters flagged as being some of DarkGod's jokes to be generated.
+
+#####GAlways make small levels [always_small_level]
+ Overrides the in-game option of small_levels, generating smaller levels
+ whenever possible.
+
+#####GYou can receive fates, good or bad [fate_option]
+ Allows the player to turn off ToME's *****fatespoi.txt*0[fates] for that character.
+
+~~~~~07|Options|Ingame
+#####RIN GAME OPTIONS
+#####R===============
+
+These options are available from within the game, and can be toggled on and
+off at will during the course of the game.
+
+~~~~~08|Options|Interface
+#####R=== Option Set 1 -- User Interface ===
+
+#####GRogue-like commands [rogue_like_commands]
+ Selects the "roguelike" command set (see *****command.txt*0["command.txt"] for info).
+~~~~~1
+#####GActivate quick messages [quick_messages]
+ Allows the use of any keypress as a response to the "-more-" prompt
+ (useful for monster farming). Allows most keys to mean "no" to any
+ "[y/n]" prompt.
+
+#####GPrompt for various information [other_query_flag]
+ No longer used.
+
+#####GPrompt before picking things up [carry_query_flag]
+ Forces the game to ask you for confirmation when you do something that
+ would normally cause an item to be picked up.
+~~~~~4
+#####GUse old target by default [use_old_target]
+ Forces all commands which normally ask for a direction to use the
+ current target if there is one. If the current target is a monster, it
+ becomes unset when that monster dies. Use of this option can be dangerous
+ if you target locations on the ground, unless you clear them when done.
+
+#####GPick things up by default [always_pickup]
+ Tells the game that walking onto an item should attempt to pick it up.
+ Otherwise, you must use the "g" command, or the "-" command while walking.
+ Combined with "carry_query_flag" (Prompt before picking things up), allows
+ you to selectively pick up all items which you step on.
+
+#####GPrompt before picking up heavy objects [prompt_pickup_heavy]
+ Generates a prompt whenever the character tries to pick up an item that
+ would slow him down.
+
+#####GRepeat obvious commands [always_repeat]
+ Tells the game that when you attempt to open a door or chest, bash
+ a door, tunnel through walls, or disarm traps or chests, that you
+ wish to repeat the command 99 times (see *****command.txt*0["command.txt"]).
+
+#####GShow dungeon level in feet [depth_in_feet]
+ Display the dungeon depth in "feet" instead of as a level number (one
+ level is equivalent to 50'). This also affects the monster memory display.
+
+#####GMerge inscriptions when stacking [stack_force_notes]
+ Force otherwise identical objects to merge, even if one has an empty
+ inscription and the other does not. The resulting stack keeps the
+ non-empty inscription.
+
+#####GMerge discounts when stacking [stack_force_costs]
+ Force otherwise identical objects to merge, even if they have different
+ discounts. The resulting stack keeps the largest discount. This option
+ may cause you to lose "value", but will give you optimal pack usage.
+
+#####GShow labels in object lists [show_labels]
+ Display the "labels" for objects in the equipment list, and in any
+ special window which is displaying the equipment. These labels
+ indicate what the player is using the object for, such as "wielding"
+ or "wearing" (in a given location). After you have played for a while,
+ this information is no longer useful, and can be annoying.
+ Note that in ToME this option no longer controls the "plain
+ flavoured object descriptions": a separate option for them has been added
+ under "ToME Options".
+
+#####GShow weights in object lists [show_weights]
+ Display the weight of objects in the inventory and equipment lists,
+ and in stores, and in any special window which is displaying any of
+ these lists.
+
+#####GShow graphics in inventory list [show_inven_graph]
+ Display the graphics of objects in the inventory list, and in any special
+ window which is displaying the inventory list.
+
+#####GShow graphics in equipment list [show_equip_graph]
+ Display the graphics of objects in the equipment list, and in any special
+ window which is displaying the equipment list.
+
+#####GShow graphics in stores [show_store_graph]
+ Display the graphics of objects in the store list.
+
+#####GShow choices in certain sub-windows [show_choices]
+ Indicate legal choices in special windows which display lists.
+
+#####GShow details in certain sub-windows [show_details]
+ Indicate extra details in special windows, currently used to activate
+ the display of death counts and monster descriptions when recalling
+ details about a monster.
+
+#####GAudible bell (on errors, etc) [ring_bell]
+ Attempt to make a "bell" noise when various errors occur.
+
+#####GUse color if possible (slow) [use_color]
+ This option enables the software support for colour. Since this makes
+ the game slower, you should always disable this option if you are using
+ a machine which is not capable of using colours.
+
+~~~~~09|Options|Disturbance
+#####R=== Option Set 2 -- Disturbance ===
+
+#####GRun past stairs [find_ignore_stairs]
+ Ignore stairs when running.
+
+#####GRun through open doors [find_ignore_doors]
+ Ignore open doors when running.
+
+#####GRun past known corners [find_cut]
+ Cut sharply around known corners when running. This will result in
+ faster running, but may cause you to run into a lurking monster.
+
+#####GRun into potential corners [find_examine]
+ Fully explore potential corners in hallways. This is strongly
+ recommended if your light source has a small radius (e.g. a torch).
+
+#####GDisturb whenever any monster moves [disturb_move]
+ Disturb the player when any monster moves, appears, or disappears.
+ This includes monsters which are only visible due to telepathy, so
+ you should probably turn this option off if you want to "rest" near
+ such monsters.
+
+#####GDisturb whenever viewable monster moves [disturb_near]
+ Disturb the player when any viewable monster moves, whenever any
+ monster becomes viewable for the first time, and also whenever any
+ viewable monster becomes no longer viewable. This option ignores
+ the existence of telepathy for the purpose of determining whether
+ a monster is viewable. See also the "view_reduce_view" option.
+
+#####GDisturb whenever map panel changes [disturb_panel]
+ This option causes you to be disturbed (stop running) when the screen
+ scrolls, as it does when you get close to the edge of the visible screen.
+
+#####GDisturb whenever leaving trap-detected area [disturb_detect]
+ This option causes you to be disturbed whenever you are leaving
+ a trap-detected area. This option is strongly recommended.
+
+#####GDisturb whenever player state changes [disturb_state]
+ This option causes you to be disturbed whenever the player state
+ changes, including changes in hunger, resistance, confusion, etc.
+
+#####GDisturb whenever boring things happen [disturb_minor]
+ This option causes you to be disturbed by various boring things,
+ including monsters bashing down doors, inventory feelings, and
+ beginning to run out of light-source fuel.
+
+#####GDisturb whenever random things happen [disturb_other]
+ In ToME, uncursed teleporting items may teleport you around sometimes,
+ asking for your confirmation (and possibly disturbing your rest). If you
+ unset this option, they will stop asking you and teleporting you randomly.
+ Cursed items will neither ask for confirmation nor stop teleporting you
+ even if this option is unset. (You may also inscribe an item with {.}
+ to suppress its random-teleportation power, unless it is cursed.)
+
+#####GAlert user to critical hitpoints [alert_hitpoint]
+ Produce a "bell" noise, and flushes all pending input, when your hitpoints
+ reach the warning point chosen elsewhere, preventing stupid deaths.
+
+#####GAlert user to various failures [alert_failure]
+ Produce a "bell" noise, and flushes all pending input, when various
+ failures occur, as described above.
+
+#####GGet last words when the character dies [last_words]
+ Display a random line from the "death.txt" file when your character
+ dies. If this option is not selected, the "You die." message is displayed
+ instead.
+
+#####GAllow shopkeepers and uniques to speak [speak_unique]
+ If this option is in use, shopkeepers may sometimes whisper rumours to
+ you. Also certain monsters start boasting as they attack you, and when
+ they die, they say their "last words".
+
+#####GNo query to destroy known worthless items [auto_destroy]
+ It can sometimes be annoying that the Destroy command asks for confirmation
+ when you are attempting to destroy a Broken sword {cursed}. If this option
+ is set, no confirmation will be asked if you attempt to destroy an object
+ which you know to be worthless. Of course, cursed artifacts cannot be
+ destroyed even if this option is set.
+
+#####GConfirm to wear/wield known cursed items [confirm_wear]
+ Some players may occasionally, due to a typing mistake, find themselves
+ wearing an item which they knew was cursed. If this option is set, you
+ should be safe from such typing mistakes: you will be prompted if you
+ attempt to wear or wield an item if your character knows it is cursed.
+
+#####GPrompt before exiting a dungeon level [confirm_stairs]
+ Some players (such as myself) often accidentally press the '<' key
+ and exit a Special feeling level. If this option is set, the program
+ asks for confirmation before you go up or down the stairs. Others may
+ find the prompt annoying; they should of course not set this option. :-)
+
+#####GDisturb when visible pets move [disturb_pets]
+ The player may wish that some of the disturbance options do not apply
+ to pets: for example, it can be annoying if your rest is always disturbed
+ by a pet dog who pops in every now and then. By default, pets do not
+ disturb you even if full monster disturbance options are set. If you
+ want your pets to disturb you like normal monsters, set this option.
+
+#####GAutomatically open doors [easy_open]
+ Opens (and unlocks) doors by walking into them. Also, if you are adjacent
+ to only one known door, using the "o"pen command will not prompt you for
+ a direction.
+
+#####GAutomatically disarm traps [easy_disarm]
+ Attempts to disarm traps by walking into/over them. Also, if you are
+ adjacent to only one known trap, using the "D"isarm command will not
+ prompt you for a direction. If your disarming ability is particularly
+ low, you should probably not enable this option, because you will often
+ fail to disarm the traps, and sometimes trigger them.
+
+#####GAutomatically tunnel walls [easy_tunnel]
+ Automatically tunnels into walls by walking into them.
+
+~~~~~10|Options|Game-play
+#####R=== Option Set 3 -- Game-play ===
+
+#####GAuto-haggle in stores [auto_haggle]
+ Disable haggling in stores, resulting in a ten percent sales tax
+ on items which you would have otherwise been forced to haggle for.
+ When this option is on, all prices listed in stores will be the
+ actual price that you pay for an item, as opposed to the price
+ that the shop-keeper will suggest.
+
+#####GAuto-scum for good levels [auto_scum]
+ This is a hack but allows you to force the generation of "good" levels
+ in the dungeon. This option may be extremely slow on some machines,
+ especially deep in the dungeon. The minimum "goodness" of the level
+ is based on the dungeon level, so the deeper you go, the better the
+ level will be.
+
+#####GAllow weapons and armor to stack [stack_allow_items]
+ Allow identical weapons and armor to be combined into a stack. This
+ also allows unidentified, but identical, ammo to be combined, which
+ may result in the auto-identification of some of the ammo, but which
+ makes it a lot easier to actually use unidentified ammo.
+
+#####GAllow wands/staffs/rods to stack [stack_allow_wands]
+ Allow identical wands/staffs/rods to be combined into a stack. This
+ may force the items to be unstacked to use them, which may result
+ in overflow of the pack. Also, the entire stack can be recharged
+ (and possibly destroyed) at the same time.
+
+#####GExpand the power of the look command [expand_look]
+ Expand the "l"ook command to allow the user to look at grids which
+ are not actually in view of the player, allowing the examination of
+ objects/monsters which have only been detected by spells, or sensed
+ via telepathy.
+
+#####GExpand the power of the list commands [expand_list]
+ Expand the "listing" commands so that they wrap at the edges of
+ the appropriate list. This allows the "l"ook and "t"arget commands
+ to cycle through all appropriate grids forever, and the "identify
+ symbol" to browse through all of the monsters of a given type.
+
+#####GMap remembers all perma-lit grids [view_perma_grids]
+ Memorise all perma-lit floor grids which are seen by the player.
+ This option allows you to keep track of which explored floor grids
+ were perma-lit, but does not distinguish between dark floor grids,
+ unexplored floor grids, and unknown grids. Turning off this option
+ allows the player to always know which lit floor grids are in line
+ of sight, but this is better accomplished by the "view_bright_lite"
+ option. Note that any non-floor grids which is seen by the player
+ are always memorised, and any object which is seen by the player is
+ memorised independently from the memorisation of the grid itself.
+
+#####GMap remembers all torch-lit grids [view_torch_grids]
+ Memorise all (torch-lit) floor grids which are seen by the player.
+ This option not only allows you to keep track of which floor grids
+ have been explored, but also which ones are dark, because the use
+ of this option activates a special color scheme for the display of
+ floor grids, in which dark grids are drawn in dark grey, lit grids
+ are drawn in white, and (if the "view_bright_lite" option is set)
+ lit grids which are also in line of sight are drawn in orange. Note
+ that grids which are currently torch-lit are considered to be "lit",
+ and are thus drawn in white, unless the "view_yellow_lite" option is
+ set, in which case they are drawn in yellow.
+
+#####GAllow some monsters to carry light [monster_lite]
+ This option allows some monsters to carry light sources around with them,
+ lighting up the space around them. It can also allow you to see when some
+ monsters are heading your way before they reach the bend in the corridor
+ where you are hiding in ambush....
+
+#####GGenerate dungeons with aligned rooms [dungeon_align]
+ Force all rooms to be aligned with the "panel" divisions. This results
+ in a much prettier dungeon, but may result in fewer greater vaults.
+
+#####GGenerate dungeons with connected stairs [dungeon_stair]
+ Always generate a staircase back to the level whence you came, if you used
+ a staircase to get to the level. This is more "realistic", and safer,
+ but less of a challenge for some people.
+
+#####GMonsters chase current location (v.slow) [flow_by_sound]
+ Allow monsters to make paths to the player when they are nearby. This
+ option is extremely slow, but can produce viciously smart monsters.
+
+#####GMonsters chase recent locations (v.slow) [flow_by_smell]
+ As above, but also allow monsters to take advantage of "old" trails
+ that you may have left in the dungeon.
+~~~~~3
+#####GUse special symbols for the player char [player_symbols]
+ If this option has been compiled in, it allows you to display your
+ character using race / class / sex dependent colours and graphical
+ symbols. Note that the support for this option may not have been
+ compiled in on all platforms.
+
+#####GPlain object descriptions [plain_descriptions]
+ In ToME, this option disables "full" names for identified flavoured
+ objects; in other words, if this option is not in use, an identified
+ Potion of Speed could be listed (for example) as a Blue Potion of Speed.
+ If you prefer simpler, less verbose descriptions, set this option.
+
+#####GMonsters learn from their mistakes [smart_learn]
+ Allow monsters to learn what spell attacks you are resistant to,
+ and to use this information to choose the best attacks.
+
+#####GMonsters exploit players weaknesses [smart_cheat]
+ Allow monsters to know what spell attacks you are resistant to, without
+ first having to observe such an attack upon you, and to use this
+ information to choose the best attacks.
+
+#####GMonsters behave stupidly [stupid_monsters]
+ ToME incorporates Keldon Jones' improved monster Artificial
+ Intelligence patch. While this patch most certainly makes monsters
+ behave more realistically, they will also be more deadly with the
+ improved AI. If you are a sissy, set this option to get the old,
+ really stupid monster AI.
+ Note that the new AI is a bit processing power expensive. If you have
+ an old computer (386sx) and ToME is running too slowly, you could
+ try turning stupid_monsters on. Or dumpster-dive for a Pentium so you can
+ run ToME. :-)
+
+#####GAllow unusually small dungeon levels [small_levels]
+ This option enables the creation of levels of varying sizes. Levels
+ that are as small as one "screen" (80x24) are possible, and they can be
+ quite dangerous, especially for a low level character, because they have
+ as many monsters and traps as their full-sized counterparts.
+ Note that this option has the side effect of enabling / disabling
+ 'destroyed' levels (they are enabled if small levels are).
+
+#####GAllow empty 'arena' levels [empty_levels]
+ Normal dungeon levels consist mostly of rock. If this option is in
+ use, levels which have empty floor instead of solid rock may also
+ be created (somewhat reminiscent of Nethack's "big-room" levels).
+ These levels can be extremely deadly, especially with breathing
+ monsters (since there are few obstructions to shield you). Arena levels
+ may have vaults, nests and pits in them like normal levels. Some
+ arena levels are dark when they are created, but most are lit.
+
+~~~~~11|Options|Efficiency
+#####R=== Option Set 4 -- Efficiency ===
+
+#####GReduce lite-radius when running [view_reduce_lite]
+ Reduce the radius of the player's light to that of a torch (radius 1)
+ when the player is running, which makes running more efficient (CPU-wise),
+ but is extremely annoying. Certain older versions of Angband used
+ this behavior always, so "purists" should turn it on.
+
+#####GReduce view-radius in town [view_reduce_view]
+ No longer in use.
+
+#####GAvoid checking for user abort [avoid_abort]
+ Avoid checking to see if the user has pressed a key during resting
+ or running or repeated commands. This not only makes the game much
+ more efficient (on many systems), but also allows the use of certain
+ obscure macro sequences, such as turning this option on, resting until
+ done, turning this option off, and casting a spell. Note that the use
+ of this option may be dangerous on certain "graphic" machines. Resting
+ for long periods of time with this option set is dangerous since the
+ resting may not stop until the user takes damage from starvation.
+
+#####GAvoid processing special colors [avoid_other]
+ Avoid processing the "multi-hued" or "clear" attributes of monsters.
+ This will cause all multi-hued monsters to appear violet and all
+ clear monsters to appear white, and will cause trappers and lurkers to
+ be visible on some machines, but it may greatly increase efficiency
+ especially when telepathy is active. Certain systems may choose to set
+ this option if they are unable to support the special color processing,
+ but if they handle graphics "correctly", by using attr/char pairs with
+ the "high bits" set, then not only will the game correctly avoid using
+ any "dangerous" color processing, but it will allow such processing to
+ occur when it is not dangerous. So if you are using graphics, and you
+ use a normal attr/char for the floor grids, then you can use the
+ "special lighting effects" for floors.
+
+#####GFlush input on various failures [flush_failure]
+ This option forces the game to flush all pending input whenever various
+ "failures" occur, such as failure to cast a spell, failure to use a wand,
+ etc. This is very useful if you use macros which include "directional"
+ components with commands that can fail, since it will prevent you from
+ walking towards monsters when your spells fail.
+
+#####GFlush input whenever disturbed [flush_disturb]
+ This option forces the game to flush all pending input whenever the
+ character is "disturbed". This is useful if you use macros which take
+ time, since it will prevent you from continuing your macro while being
+ attacked by a monster.
+
+#####GFlush input before every command [flush_command]
+ This option forces the game to flush all pending input before every
+ command. This option is silly, unless you are very paranoid.
+
+#####GFlush output before every command [fresh_before]
+ This option forces the game to flush all output before every command.
+ This will give you maximal information, but may slow down the game
+ somewhat. Note that this option is only useful when using macros,
+ resting, running, or repeating commands, since the output is always
+ flushed when the game is waiting for a keypress from the user.
+
+#####GFlush output after every command [fresh_after]
+ This option forces the game to flush all output after not only every
+ player command, but also after every round of processing monsters and
+ objects, which will give you maximal information, but may slow down
+ the game a lot, especially on slower machines; and on faster machines
+ you normally do not have a chance to see the results anyway.
+
+#####GFlush output after every message [fresh_message]
+ This option forces the game to flush all output after every message
+ displayed by the game. This will give you maximal information, but
+ may slow down the game somewhat.
+
+#####GCompress messages in savefiles [compress_savefile]
+ Compress the savefile, by only saving the most recent messages that
+ the player has received. This can cut the size of the savefile by a
+ drastic amount, but will result in the loss of message information.
+~~~~~2
+#####GHilite the player with the cursor [hilite_player]
+ Place the visible cursor on the player. This looks fine on some Unix
+ machines, but horrible on most graphics machines. Note that only some
+ machines are able to *not* show the cursor, but on those machines, hiding
+ the cursor often speeds up the game and looks better.
+
+#####GUse special colors for torch-lit grids [view_yellow_lite]
+ This option causes special colors to be used for "torch-lit" grids in
+ certain situations (see "view_granite_lite" and "view_special_lite").
+ Turning this option off will slightly improve game speed.
+
+#####GUse special colors for 'viewable' grids [view_bright_lite]
+ This option causes special colors to be used for non "viewable" grids
+ in certain situations (see "view_granite_lite" and "view_special_lite").
+ When this option is set, floor grids which are normally drawn in white
+ but which are not currently viewable by the player are instead drawn
+ in dark grey. This makes the viewable grids appear brighter than the
+ others, allowing the player to easily determine which floor grids are
+ in line of sight. Turning this option off will probably increase the
+ speed of the game.
+
+#####GUse special colors for wall grids (slow) [view_granite_lite]
+ This option activates a special color scheme for all wall grids which
+ are normally drawn in white (as walls and rubble normally are). When
+ the player is blind, we use dark grey, else if the grid is torch-lit,
+ we use yellow (or white, depending on the "view_yellow_lite" option),
+ else if the "view_bright_lite" option is set, and the grid is not in line
+ of sight, or the grid is dark, or the grid is only "partially" lit, then
+ we use grey, otherwise we use the normal white. Turning this option
+ off will probably increase the speed of the game.
+
+#####GUse special colors for floor grids (slow) [view_special_lite]
+ This option activates a special color scheme for all floor grids which
+ are normally drawn in white (as they normally are). When the player is
+ blind, we use dark grey, else if the grid is torch-lit, we use yellow
+ (or white, depending on the "view_yellow_lite" option), else if the grid
+ is dark, we use dark grey, else if the "view_bright_lite" option is
+ set, and the grid is not in line of sight, we use grey, otherwise we
+ use the normal white. Turning this option off will probably increase
+ the speed of the game.
+
+#####GCentre the view on the player (very slow) [center_player]
+ Keeps the player's character in the centre of the screen, and moves the
+ dungeon around the player. Can be useful to prevent off-screen breaths.
+
+~~~~~12|Options|ToME Options
+#####R=== ToME Options ===
+
+Features which are unique to ToME are collected in this menu.
+
+#####GIngame contextual help [ingame_help]
+ Setting this option allows the game to trigger a help message the first
+ time you come across an item or some other trigger. This is very useful
+ for new players. More experienced players may wish to switch this option
+ off.
+
+#####GShow the experience needed for the next level [exp_need]
+ Setting this option alters the display of experience on the left of
+ the main screen to the experience needed to reach the next character level,
+ instead of the character's current total experience.
+
+#####GUse the old(Z) coloring scheme(reload the game) [old_colors]
+ Setting this option toggles the ASCII game colour display from the
+ standard Angband monster colours to the Zangband-based monster colours.
+ Since this alters the display and monster memory display, you need to
+ reload the game when you alter this setting before it will display the
+ new colours.
+
+#####GAutomatically clear '-more-' prompts [auto_more]
+ Setting this option automatically clears any messages from the top
+ of the window. Be warned that this could be dangerous, as you don't
+ actually get to see the messages unless you use ^P.
+
+#####GPlayer char represent his/her health [player_char_health]
+ Setting this option only affects the game when playing without tiles.
+ As the player becomes injured, his icon changes to a figure representing
+ the percentage of health remaining; for example if he is down to 68% of
+ his maximum hitpoints, his character will be a '6' instead of an '@'.
+ The character used only starts changing once the player has lost at
+ least 30% of his maximum hitpoints.
+
+#####GStats are represented in a linear way [linear_stats]
+ Setting this option alters the display of character stats. The default
+ is 3 to 40 (linear), but the older 3 to 18/220 (Moria/Angband style) is
+ retained for players who prefer it.
+
+#####GIn option windows, just omit the select char [inventory_no_move]
+ If this option is set, the equipment/inventory windows don't move items
+ around when a prompt asks for an item.
+
+
+#####R=== Stacking Options ===
+
+In ToME items are allowed to stack on floors and monsters are allowed to
+maintain inventories. These features are enabled by default, and aren't
+accessible through the option menu, but can still be disabled through
+user pref files (see *****command.txt*105["command.txt"]).
+
+#####GAllow objects to stack on the floor [testing_stack]
+ Allows a cave grid to hold more than one object (or one kind of
+ object).
+
+#####GAllow monsters to carry objects [testing_carry]
+ If this option is set, monsters which "pick up" objects will drop
+ the objects they were carrying when you kill them. Note that monsters
+ which "crush" objects are not affected by this option.
+
+~~~~~13|Options|Base Delay Factor
+#####R=== Base Delay Factor ===
+
+The "delay_factor" value, if non-zero, is used to slow down the game, which is
+useful to allow you to observe the temporal effects of bolt, beam, and ball
+attacks. The actual delay is equal to "delay_factor" cubed, in milliseconds.
+Frequently used factors are 2 or 3.
+
+~~~~~14|Options|Hitpoint Warning
+#####R=== Hitpoint Warning ===
+
+The "hitpoint_warn" value, if non-zero, is the percentage of maximal hitpoints
+at which the player is warned that he may die. It is also used as the cut-off
+for using red to display hitpoints, mana and sanity. It is entered as a value
+between 0 and 9 (0% and 90%).
+
+~~~~~15|Options|Autosave
+#####R=== Autosave Options ===
+
+Ideally, the game should be so stable that these options are not needed
+at all. However, even if the game were 100% reliable (which, to be frank, it
+probably is not), the user might forget to save, and his hardware could fail
+him. For all of these reasons, you may want to use these options:
+
+#####GAutosave when entering new levels [autosave_l]
+ If this option is set, the program will attempt to save your
+ character every time before creating a new dungeon level. Useful
+ if you experience any game or computer crashes (or your dog enjoys
+ kicking your power cords out of the wall like mine does!).
+
+#####GTimed autosave [autosave_t]
+ If this option is set, the program will attempt to save your
+ character every n game turns, where n is the "frequency". To set
+ frequency, press n: it will increase the frequency to the next
+ category, these being every 50, 100, 250, 500, 1000, 2500, 5000,
+ 10000 or 25000 turns. (After 25000, pressing n again will cycle back
+ to 0.) Note that the frequency must be higher than 0 and the
+ "Timed autosave" set to "yes" for timed autosaves to take place.
+
+~~~~~16|Options|Automatizer
+#####R=== The Automatizer ===
+
+Allows you to set options for the game to automatically destroy or pick up
+objects when you identify them, for example skeletons, essences, cursed
+daggers, etc. Useful for reducing the clutter in the dungeon, and reducing the
+amount of loot to have to sort through. This *****automat.txt*0[Tutorial] may help you.
+
+~~~~~17|Options|Window Flags
+#####R=== Window Flags ===
+
+Selects what kind of information is displayed in which window, on platforms
+which use multiple windows.
+
+You can select a window to be able to toggle between 2 different
+sets of information (e.g. Basic Character stats and monster recall)
+by pressing the "y" key over the second display option.
+
+~~~~~18|Options|Cheating
+#####R=== Cheating Options ===
+
+#####GPeek into object creation [cheat_peek]
+ Cheaters never win. But they can peek at object creation.
+
+#####GPeek into monster creation [cheat_hear]
+ Cheaters never win. But they can peek at monster creation.
+
+#####GPeek into dungeon creation [cheat_room]
+ Cheaters never win. But they can peek at room creation.
+
+#####GPeek into something else [cheat_xtra]
+ Cheaters never win. But they can see debugging messages.
+
+#####GKnow complete monster info [cheat_know]
+ Cheaters never win. But they can know all about monsters.
+
+#####GAllow player to avoid death [cheat_live]
+ Cheaters never win. But they can cheat death.
+
+~~~~~19|Options|Dump/Load Options
+#####R=== Dump Options ===
+
+Allows the player to save the options to a file (defaults to charname.prf)
+so that they can be reloaded into other character files.
+
+
+#####R=== Load Options ===
+
+Allows you to load a preference file saved through the "Dump Options"
+command in another character file, hence saving all the initial time of having
+to reset all the options every time you wish to play.
+
+
++++ Ben +++ (Updated by Dark God and Dawnmist et al. for ToME)
diff --git a/lib/help/r_beorn.txt b/lib/help/r_beorn.txt
new file mode 100644
index 00000000..b000403f
--- /dev/null
+++ b/lib/help/r_beorn.txt
@@ -0,0 +1,33 @@
+~~~~~01|Beorning
+~~~~~02|Races|Beorning
+#####R=== Beornings ===
+
+#####GDescription
+Beornings are the descendants of Beorn, a powerful shapeshifter who
+dwells near Mirkwood. They have all inherited his shapeshifting abilities
+and can turn into powerful bears at will.
+
+#####GStat Modifiers
+Strength +4
+Intelligence -2
+Wisdom -2
+Dexterity -1
+Constitution +3
+Charisma -5
+Hit Dice Sides 12
+Exp Penalty +50%
+
+#####GSkill Bonuses (supplementary to existing skills)
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 2.500 [0.000]
+ Archery 0.500 [0.000]
+ Bearform-combat 1.000 [1.000]
+Sneakiness -0.100 [0.000]
+ Stealth -2.000 [0.000]
+ Disarming -0.600 [0.000]
+Magic
+ Magic-Device -0.800 [0.000]
+Spirituality -3.000 [0.000]
diff --git a/lib/help/r_deathm.txt b/lib/help/r_deathm.txt
new file mode 100644
index 00000000..d65403dd
--- /dev/null
+++ b/lib/help/r_deathm.txt
@@ -0,0 +1,33 @@
+~~~~~01|DeathMolds
+~~~~~02|Races|DeathMolds
+#####R=== DeathMolds ===
+
+#####GDescription
+Death Molds are incredibly powerful creatures. However, they are also
+molds. Lacking the ability to move as other creatures do, Death Molds have
+the powers of Phase Door, targeted teleportation, telekinesis, and
+controlled Teleport Level. They also intrinsically resist Nexus and Nether,
+and are more skilled at Necromancy than other races.
+
+#####GStat Modifiers
+Strength +10
+Intelligence 0
+Wisdom +10
+Dexterity 0
+Constitution +10
+Charisma -15
+Hit Dice Sides 15
+Exp Penalty +150%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 2.500 [0.000]
+ Archery 2.500 [0.000]
+Sneakiness
+ Stealth 25.000 [0.000]
+ Disarming 1.500 [0.000]
+Magic
+ Magic-Device -0.500 [0.000]
+ Necromancy 0.000 [0.200]
+Spirituality 7.500 [0.000]
diff --git a/lib/help/r_drkelf.txt b/lib/help/r_drkelf.txt
new file mode 100644
index 00000000..3f0758e5
--- /dev/null
+++ b/lib/help/r_drkelf.txt
@@ -0,0 +1,33 @@
+~~~~~01|Dark Elf
+~~~~~02|Races|Dark Elf
+#####R=== Dark Elves ===
+
+#####GDescription
+Another dark, cave-dwelling race, likewise unhampered by darkness attacks,
+the Dark Elves have a long tradition and knowledge of magic. With their
+intelligence and wisdom they can become superb mages or priests, and they
+have an inherent magic missile attack available to them at a low level. With
+their keen sight, they also learn to see invisible things as their relatives
+the High-Elves do, but at a higher level.
+
+#####GStat Modifiers
+Strength -1
+Intelligence +3
+Wisdom +2
+Dexterity +2
+Constitution -2
+Charisma +1
+Hit Dice Sides 9
+Exp Penalty +50%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -0.500 [0.000]
+ Archery 1.000 [0.000]
+Sneakiness 0.800 [0.000]
+ Stealth 3.000 [0.000]
+ Disarming 0.500 [0.000]
+Magic 0.000 [0.200]
+ Magic-Device 1.500 [0.000]
+Spirituality 10.000 [0.000]
diff --git a/lib/help/r_dunad.txt b/lib/help/r_dunad.txt
new file mode 100644
index 00000000..79b85049
--- /dev/null
+++ b/lib/help/r_dunad.txt
@@ -0,0 +1,32 @@
+~~~~~01|Dunedain
+~~~~~02|Races|Dunedain
+#####R=== Dunedain ===
+
+#####GDescription
+Dunedain are a race of hardy men from the West. This elder race surpasses
+human abilities in every field, especially constitution. However, being
+men of the world, very little is new to them, and levels are very hard for
+them to gain. They can play all classes. Their constitution cannot be
+reduced and they regain hit points quickly.
+
+#####GStat Modifiers
+Strength +1
+Intelligence +2
+Wisdom +2
+Dexterity +2
+Constitution +3
+Charisma +2
+Hit Dice Sides 10
+Exp Penalty +80%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.500 [0.000]
+ Archery 1.000 [0.000]
+Sneakiness 0.800 [0.000]
+ Stealth 2.000 [0.000]
+ Disarming 0.400 [0.000]
+Magic
+ Magic-Device 0.500 [0.000]
+Spirituality 2.500 [0.000]
diff --git a/lib/help/r_dwarf.txt b/lib/help/r_dwarf.txt
new file mode 100644
index 00000000..6c5a9b80
--- /dev/null
+++ b/lib/help/r_dwarf.txt
@@ -0,0 +1,39 @@
+~~~~~01|Dwarf
+~~~~~02|Races|Dwarf
+#####R=== Dwarves ===
+
+#####GDescription
+Dwarves are the headstrong miners and fighters of legend. Since dungeons
+are the natural home of a dwarf, they are excellent choices for a warrior
+or priest. Dwarves tend to be stronger and tougher but less agile and
+intelligent than humans. Because they are so headstrong and are somewhat
+wise, they resist spells which are cast on them. Dwarves also have very
+good infra-vision because they live underground. They do have one big
+drawback, though: dwarves are loud-mouthed and proud, singing in boisterous
+voices, arguing with themselves for no good reason, and screaming out
+challenges at nearby foes. In other words, dwarves have miserable
+stealth. They can never be blinded, and they can also open secret tunnels
+through rock.
+
+#####GStat Modifiers
+Strength +2
+Intelligence -2
+Wisdom +2
+Dexterity -2
+Constitution +2
+Charisma -3
+Hit Dice Sides 11
+Exp Penalty +25%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.500 [0.000]
+ Axe-mastery 0.000 [0.200]
+ Archery 0.500 [0.000]
+Sneakiness 0.700 [0.000]
+ Stealth -1.000 [0.000]
+ Disarming 0.200 [0.000]
+Magic
+ Magic-Device 0.900 [0.000]
+Spirituality 5.000 [0.000]
diff --git a/lib/help/r_elf.txt b/lib/help/r_elf.txt
new file mode 100644
index 00000000..6b4ceb3b
--- /dev/null
+++ b/lib/help/r_elf.txt
@@ -0,0 +1,32 @@
+~~~~~01|Elf
+~~~~~02|Races|Elf
+#####R=== Elves ===
+
+#####GDescription
+Elves are better magicians than humans, but not as good at fighting. They
+tend to be smarter and faster than either humans or half-elves and also
+have better wisdom. Elves are better at searching, disarming, perception,
+stealth, bows, and magic, but they are not as good at hand weapons.
+They resist light effects intrinsically.
+
+#####GStat Modifiers
+Strength -1
+Intelligence +2
+Wisdom +2
+Dexterity +1
+Constitution -2
+Charisma +2
+Hit Dice Sides 8
+Exp Penalty +20%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -5.000 [0.000]
+ Archery 1.500 [0.000]
+Sneakiness 0.800 [0.000]
+ Stealth 2.000 [0.000]
+ Disarming 0.500 [0.000]
+Magic
+ Magic-Device 0.600 [0.000]
+Spirituality 3.000 [0.000]
diff --git a/lib/help/r_ent.txt b/lib/help/r_ent.txt
new file mode 100644
index 00000000..66edf5c2
--- /dev/null
+++ b/lib/help/r_ent.txt
@@ -0,0 +1,39 @@
+~~~~~01|Ent
+~~~~~02|Races|Ent
+#####R=== Ents ===
+
+#####GDescription
+The Ents are a powerful race dating back to the beginning of the world and
+are the eldest of all animals or plants that inhabit Arda. Spirits of the land,
+they were summoned to guard the forests of Middle-earth. Being much like
+trees they are very slow but strong, and very susceptible to fire.
+As the Shepherds of the Trees, they have the innate ability to cause trees to
+rise about them for protection.
+
+#####GStat Modifiers
+Strength +10
+Intelligence -3
+Wisdom +2
+Dexterity -5
+Constitution +11
+Charisma -3
+Hit Dice Sides 14
+Exp Penalty +110%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -0.300 [0.000]
+ Archery -0.200 [0.000]
+ Barehand-combat 0.000 [0.200]
+ Boulder-throwing 0.000 [0.600]
+Sneakiness 0.500 [0.000]
+ Stealth -6.000 [0.000]
+ Disarming 0.500 [0.000]
+Magic
+ Magic-Device 0.500 [0.000]
+Spirituality 10.000 [0.000]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Tree-walking 1
diff --git a/lib/help/r_gnome.txt b/lib/help/r_gnome.txt
new file mode 100644
index 00000000..8272e8a5
--- /dev/null
+++ b/lib/help/r_gnome.txt
@@ -0,0 +1,36 @@
+~~~~~01|Gnome
+~~~~~02|Races|Gnome
+#####R=== Gnomes ===
+
+#####GDescription
+Gnomes are smaller than dwarves but larger than halflings. Like the hobbits,
+they live in the earth in burrow-like homes. Gnomes make excellent magi,
+and have very good saving throws. They are good at searching, disarming,
+perception, and stealth. They have lower strength than humans and they are
+not very good at fighting with hand weapons, but have developed a fondness
+for the crossbow. Gnomes have fair infra-vision, so they can detect
+warm-blooded creatures at a distance. Gnomes are intrinsically protected
+against paralysis and some slowing effects. At higher levels, gnomes learn
+to teleport at will.
+
+#####GStat Modifiers
+Strength -1
+Intelligence +2
+Wisdom 0
+Dexterity +2
+Constitution +1
+Charisma -2
+Hit Dice Sides 8
+Exp Penalty +35%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -0.800 [0.000]
+ Archery 1.200 [0.000]
+Sneakiness 0.600 [0.000]
+ Stealth 3.000 [0.000]
+ Disarming 1.000 [0.000]
+Magic
+ Magic-Device 1.200 [0.000]
+Spirituality 6.000 [0.000]
diff --git a/lib/help/r_hafelf.txt b/lib/help/r_hafelf.txt
new file mode 100644
index 00000000..ea748440
--- /dev/null
+++ b/lib/help/r_hafelf.txt
@@ -0,0 +1,31 @@
+~~~~~01|Half-Elf
+~~~~~02|Races|Half-Elf
+#####R=== Half-Elves ===
+
+#####GDescription
+Half-elves tend to be smarter and more agile than humans, but not as tough.
+Half-elves are slightly better at searching, disarming, saving throws,
+stealth, bows, and magic, but they are not as good at hand weapons. Half-
+elves may choose any class and do not receive any intrinsic abilities.
+
+#####GStat Modifiers
+Strength 0
+Intelligence +1
+Wisdom +1
+Dexterity +1
+Constitution -1
+Charisma +1
+Hit Dice Sides 9
+Exp Penalty +10%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -1.000 [0.000]
+ Archery 0.500 [0.000]
+Sneakiness 0.600 [0.000]
+ Stealth 1.000 [0.000]
+ Disarming 0.200 [0.000]
+Magic
+ Magic-Device 0.300 [0.000]
+Spirituality 1.500 [0.000]
diff --git a/lib/help/r_hafogr.txt b/lib/help/r_hafogr.txt
new file mode 100644
index 00000000..2a173e98
--- /dev/null
+++ b/lib/help/r_hafogr.txt
@@ -0,0 +1,31 @@
+~~~~~01|Half-Ogre
+~~~~~02|Races|Half-Ogre
+#####R=== Half-Ogres ===
+
+#####GDescription
+Half-Ogres are a crossbreed between a human and an ogre. They are big, bad, and
+stupid. For warriors, they have all the necessary attributes, and they can even
+become priests: after all, they are related to Ogre Magi, from whom they have
+learned the skill of setting trapped runes once their level is high enough. Like
+orcs, they resist darkness, and like trolls, they have their strength sustained.
+
+#####GStat Modifiers
+Strength +3
+Intelligence -1
+Wisdom -1
+Dexterity -1
+Constitution +3
+Charisma -3
+Hit Dice Sides 12
+Exp Penalty +30%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 2.000 [0.000]
+Sneakiness -0.100 [0.000]
+ Stealth -2.000 [0.000]
+ Disarming -0.300 [0.000]
+Magic
+ Magic-Device -0.500 [0.000]
+Spirituality -2.500 [0.000]
diff --git a/lib/help/r_hielf.txt b/lib/help/r_hielf.txt
new file mode 100644
index 00000000..3317a67b
--- /dev/null
+++ b/lib/help/r_hielf.txt
@@ -0,0 +1,34 @@
+~~~~~01|High-Elf
+~~~~~02|Races|High-Elf
+#####R=== High-Elves ===
+
+#####GDescription
+High-elves are a race of immortal beings dating from the beginning of
+time. They are masters of all skills, and are strong and intelligent.
+They can play all classes except rogues, and very well at that.
+High-elves begin their lives able to see the unseen, and resist light
+effects just like regular elves. However, there are few things that
+they have not seen already, and experience is very hard for them to
+gain.
+
+#####GStat Modifiers
+Strength +1
+Intelligence +3
+Wisdom +2
+Dexterity +3
+Constitution +1
+Charisma +5
+Hit Dice Sides 10
+Exp Penalty +100%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.000 [0.000]
+ Archery 2.500 [0.000]
+Sneakiness 0.300 [0.000]
+ Stealth 4.000 [0.000]
+ Disarming 0.400 [0.000]
+Magic
+ Magic-Device 2.000 [0.000]
+Spirituality 10.000 [0.000]
diff --git a/lib/help/r_hobbit.txt b/lib/help/r_hobbit.txt
new file mode 100644
index 00000000..30cd885f
--- /dev/null
+++ b/lib/help/r_hobbit.txt
@@ -0,0 +1,37 @@
+~~~~~01|Hobbit
+~~~~~02|Races|Hobbit
+#####R=== Hobbits ===
+
+#####GDescription
+Hobbits, or halflings, are very good at ranged combat (especially with slings),
+throwing, and have good saving throws. They also are very good at searching,
+disarming, perception and stealth; so they make excellent rogues, but prefer
+to be called burglars. They are much weaker than humans, and not good at melee
+fighting. Halflings have fair infra-vision, so they can detect warm creatures
+at a distance. Hobbits have their dexterity sustained and in time they learn to
+cook a delicious meal from available ingredients. Their sturdy constitutions
+also allow them to resist the insidious poison of the ring-wraiths.
+
+#####GStat Modifiers
+Strength -2
+Intelligence +2
+Wisdom +1
+Dexterity +3
+Constitution +2
+Charisma +1
+Hit Dice Sides 7
+Exp Penalty +10%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -1.000 [0.000]
+ Archery 2.000 [0.000]
+ Sling-Mastery 0.000 [0.300]
+Sneakiness 1.200 [0.000]
+ Stealth 5.000 [0.000]
+ Disarming 1.500 [0.000]
+Magic
+ Magic-Device 1.800 [0.000]
+Spirituality 9.000 [0.000]
+
diff --git a/lib/help/r_human.txt b/lib/help/r_human.txt
new file mode 100644
index 00000000..57606764
--- /dev/null
+++ b/lib/help/r_human.txt
@@ -0,0 +1,23 @@
+~~~~~01|Human
+~~~~~02|Races|Human
+#####R=== Humans ===
+
+#####GDescription
+Humans act as a baseline race -- all other races are compared to them.
+Humans can choose any class and are average at everything. Humans tend to
+go up levels faster than most other races because of their shorter life
+spans. No racial adjustments or intrinsics occur to characters choosing
+the human race.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma 0
+Hit Dice Sides 10
+Exp Penalty 0%
+
+#####GRacial Skill Modifiers:
+None. Humans are the baseline race.
diff --git a/lib/help/r_kobold.txt b/lib/help/r_kobold.txt
new file mode 100644
index 00000000..26d39f86
--- /dev/null
+++ b/lib/help/r_kobold.txt
@@ -0,0 +1,31 @@
+~~~~~01|Kobold
+~~~~~02|Races|Kobold
+#####R=== Kobolds ===
+
+#####GDescription
+Kobolds are a weak goblin race. They love poisoned weapons, and can learn
+to throw poisoned darts (of which they carry an unlimited supply). They
+are also inherently resistant to poison, and can become adequate fighters,
+although they are not one of the more powerful races.
+
+#####GStat Modifiers
+Strength +1
+Intelligence -1
+Wisdom 0
+Dexterity +1
+Constitution 0
+Charisma -4
+Hit Dice Sides 9
+Exp Penalty +25%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.000 [0.000]
+ Archery -0.800 [0.000]
+Sneakiness 0.100 [0.000]
+ Stealth -1.000 [0.000]
+ Disarming -0.200 [0.000]
+Magic
+ Magic-Device -0.300 [0.000]
+Spirituality -1.000 [0.000]
diff --git a/lib/help/r_maia.txt b/lib/help/r_maia.txt
new file mode 100644
index 00000000..fa575f82
--- /dev/null
+++ b/lib/help/r_maia.txt
@@ -0,0 +1,22 @@
+~~~~~01|Maia
+~~~~~02|Races|Maia
+#####R=== Maia ===
+
+#####GDescription
+An old race, dating from before the creation of Arda, the Maiar were created by
+Eru to help the Valar in their task. However, they can not worship a God, nor
+is experience easy to remember for them. Due to their perceived advantages,
+they are rather disliked by the denizens of the dungeon, and will find that most
+creatures take an instant dislike for the Maia. However, when they do finally
+manage to remember their encounters, they will find that their abilities will
+increase as they gain in knowledge.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma 0
+Hit Dice Sides 10
+Exp Penalty +0%
diff --git a/lib/help/r_orc.txt b/lib/help/r_orc.txt
new file mode 100644
index 00000000..c4bcf691
--- /dev/null
+++ b/lib/help/r_orc.txt
@@ -0,0 +1,35 @@
+~~~~~01|Orc
+~~~~~02|Races|Orc
+#####R=== Orcs ===
+
+#####GDescription
+Orcs make excellent warriors and decent priests, but are terrible at magic.
+They are as bad as dwarves at stealth, and horrible at searching, disarming,
+and perception. Orcs are quite ugly, and tend to pay more for goods in town.
+Orcs do make good warriors and rogues, for the simple reason that Orcs tend
+to have great constitutions and lots of hit points. Because of their
+preference for living underground rather than on the surface, orcs resist
+darkness attacks. Upon reaching experience level 3, an orc learns to dispel
+any fear that may be upon him.
+
+#####GStat Modifiers
+Strength +2
+Intelligence -1
+Wisdom 0
+Dexterity +1
+Constitution +1
+Charisma -4
+Hit Dice Sides 10
+Exp Penalty +10%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.200 [0.000]
+ Archery -0.500 [0.000]
+Sneakiness
+ Stealth -1.000 [0.000]
+ Disarming -0.300 [0.000]
+Magic
+ Magic-Device -0.300 [0.000]
+Spirituality -1.000 [0.000]
diff --git a/lib/help/r_pettyd.txt b/lib/help/r_pettyd.txt
new file mode 100644
index 00000000..c8b8eba3
--- /dev/null
+++ b/lib/help/r_pettyd.txt
@@ -0,0 +1,30 @@
+~~~~~01|Petty Dwarf
+~~~~~02|Races|Petty Dwarf
+#####R=== Petty Dwarves ===
+
+#####GDescription
+A hated and persecuted race of nocturnal dwarves, these cave-dwellers are not
+bothered much by darkness. Their natural inclination to magically augmented
+items has made them immune to effects which could drain away magical
+enchantments, and, like ordinary dwarves, they can examine the dungeon to
+discover traps and secret doors. They are quite proficient as priests,
+warriors or rogues.
+
+#####GStat Modifiers
+Strength +1
+Intelligence -1
+Wisdom +2
+Dexterity 0
+Constitution +2
+Charisma -4
+Hit Dice Sides 11
+Exp Penalty +35%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Sneakiness 0.500 [0.000]
+ Stealth 1.000 [0.000]
+ Disarming 0.300 [0.000]
+Magic
+ Magic-Device 0.500 [0.000]
+Spirituality 5.000 [0.000]
diff --git a/lib/help/r_rohank.txt b/lib/help/r_rohank.txt
new file mode 100644
index 00000000..03391d4b
--- /dev/null
+++ b/lib/help/r_rohank.txt
@@ -0,0 +1,33 @@
+~~~~~01|RohanKnight
+~~~~~02|Races|RohanKnight
+#####R=== RohanKnights ===
+
+#####GDescription
+Knights of the Riddermark, these warriors are mounted upon swift steeds.
+Thus they receive a bonus to speed from the beginning and gain in speed
+as they become more experienced in riding. Wise through their prolonged
+contact with the Dunedain, their wrath may be seen in auras of war that
+drive their foes to confusion, and in a ray of light when jumping at light
+speed.
+
+#####GStat Modifiers
+Strength +4
+Intelligence -2
+Wisdom +3
+Dexterity +1
+Constitution +4
+Charisma +2
+Hit Dice Sides 10
+Exp Penalty +120%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 0.100 [0.200]
+ Archery 0.500 [0.000]
+Sneakiness 0.100 [0.000]
+ Stealth -8.000 [0.000]
+ Disarming 1.000 [0.000]
+Magic
+ Magic-Device 0.500 [0.000]
+Spirituality 2.500 [0.000]
diff --git a/lib/help/r_thlord.txt b/lib/help/r_thlord.txt
new file mode 100644
index 00000000..6de113fe
--- /dev/null
+++ b/lib/help/r_thlord.txt
@@ -0,0 +1,32 @@
+~~~~~01|Thunderlord
+~~~~~02|Races|Thunderlord
+#####R=== Thunderlords ===
+
+#####GDescription
+The Thunderlords are supremely powerful spirits created by Manwe Sulimo,
+each riding a Great Eagle. They have the ability to conjure powerful
+thunderbolts, they are telepathic, and they gain the ability to use the
+Straight Road, which can carry them to any location they have previously been.
+Due to their special clothing, they can resist elemental damage. However,
+they take a very long time to gain levels as both rider and eagle must
+accumulate experience.
+
+#####GStat Modifiers
+Strength +6
+Intelligence +2
+Wisdom +1
+Dexterity +1
+Constitution +3
+Charisma +8
+Hit Dice Sides 12
+Exp Penalty +300%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.500 [0.000]
+ Archery 0.500 [0.000]
+Sneakiness 3.000 [0.000]
+ Stealth -16.000 [0.000]
+ Disarming 0.600 [0.000]
+Spirituality 5.000 [0.000]
diff --git a/lib/help/r_troll.txt b/lib/help/r_troll.txt
new file mode 100644
index 00000000..f35e5aa6
--- /dev/null
+++ b/lib/help/r_troll.txt
@@ -0,0 +1,34 @@
+~~~~~01|Troll
+~~~~~02|Races|Troll
+#####R=== Trolls ===
+
+#####GDescription
+Trolls are incredibly strong, and have more hit points than most other
+character races, so they make great warriors, and marginal priests. They
+are also very stupid and clumsy. They are bad at searching, disarming,
+perception, and stealth. They are so ugly that an orc grimaces in their
+presence. They also happen to be fun to play.... Trolls always have
+their strength sustained. At higher levels, trolls learn to enter a
+berserk fury, and regenerate from their wounds automatically.
+
+#####GStat Modifiers
+Strength +4
+Intelligence -4
+Wisdom -2
+Dexterity -4
+Constitution +3
+Charisma -6
+Hit Dice Sides 12
+Exp Penalty +37%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 2.000 [0.000]
+ Archery -1.000 [0.000]
+Sneakiness -0.100 [0.000]
+ Stealth -2.000 [0.000]
+ Disarming -0.500 [0.000]
+Magic
+ Magic-Device -0.800 [0.000]
+Spirituality -4.000 [0.000]
diff --git a/lib/help/r_wodelf.txt b/lib/help/r_wodelf.txt
new file mode 100644
index 00000000..d40295fa
--- /dev/null
+++ b/lib/help/r_wodelf.txt
@@ -0,0 +1,37 @@
+~~~~~01|Wood Elf
+~~~~~02|Races|Wood Elf
+#####R=== Wood Elves ===
+
+#####GDescription
+The first love of Wood Elves is hunting. As such, their skill with the bow
+is unparalleled. They train tirelessly with their bows, to the point of
+neglecting even melee skills. They resist light as with other elves, and
+do extra damage with a ranged weapon. They are almost custom made for the
+archer class, but also make interesting warriors. Even Wood Elf Mages
+are feasible, using the bow to attack and saving their magic for defence.
+
+#####GStat Modifiers
+Strength -3
+Intelligence +2
+Wisdom +1
+Dexterity +5
+Constitution -4
+Charisma +1
+Hit Dice Sides 7
+Exp Penalty +30%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -2.500 [0.000]
+ Archery 4.000 [0.000]
+Sneakiness 0.800 [0.000]
+ Stealth 5.000 [0.000]
+ Disarming 0.500 [0.000]
+Magic
+ Magic-Device 0.600 [0.000]
+Spirituality 4.000 [0.000]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Tree-walking 1
diff --git a/lib/help/r_yeek.txt b/lib/help/r_yeek.txt
new file mode 100644
index 00000000..5326f604
--- /dev/null
+++ b/lib/help/r_yeek.txt
@@ -0,0 +1,30 @@
+~~~~~01|Yeek
+~~~~~02|Races|Yeek
+#####R=== Yeeks ===
+
+#####GDescription
+Yeeks are the least powerful of all the races. They suffer disadvantages
+in nearly all skills and attributes but to compensate they learn (and thus
+gain levels) extremely quickly. "Live fast, die young!"
+
+#####GStat Modifiers
+Strength -5
+Intelligence -5
+Wisdom -5
+Dexterity -5
+Constitution -5
+Charisma -5
+Hit Dice Sides 6
+Exp Penalty -75%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -0.500 [0.000]
+ Archery -0.500 [0.000]
+Sneakiness -0.500 [0.000]
+ Stealth -5.000 [0.000]
+ Disarming -0.500 [0.000]
+Magic
+ Magic-Device -0.500 [0.000]
+Spirituality -2.500 [0.000]
diff --git a/lib/help/rm_barb.txt b/lib/help/rm_barb.txt
new file mode 100644
index 00000000..57d793ff
--- /dev/null
+++ b/lib/help/rm_barb.txt
@@ -0,0 +1,38 @@
+~~~~~01|Barbarian
+~~~~~02|Race Modifiers|Barbarian
+#####R=== Barbarian Race ===
+
+#####GDescription
+Barbarians are hardy members of their race. They are fierce in combat, and
+their wrath is feared throughout the world. Combat is their life: they learn
+to feel no fear. Barbarians are, however, suspicious of magic, which makes
+magic devices fairly hard for them to use, and also makes it impossible for
+them to play Mages.
+
+#####GStat Modifiers
+Strength +2
+Intelligence -3
+Wisdom -2
+Dexterity +1
+Constitution +1
+Charisma -3
+Hit Dice +1 side
+Spell Points -50%
+Exp Penalty +25%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 1.200 [0.000]
+ Archery 0.500 [0.000]
+Sneakiness
+ Stealth -2.000 [0.000]
+ Disarming -0.200 [0.000]
+Magic
+ Magic-Device -1.000 [0.000]
+Spirituality 0.200 [0.000]
+
+#####GStarting Equipment
+A barbarian character begins the game with:
+ Some rations
+ Some torches
diff --git a/lib/help/rm_class.txt b/lib/help/rm_class.txt
new file mode 100644
index 00000000..81f72d87
--- /dev/null
+++ b/lib/help/rm_class.txt
@@ -0,0 +1,14 @@
+~~~~~01|Classical
+~~~~~02|Race Modifiers|Classical
+#####R=== Classical Race ===
+
+#####GDescription
+This is the normal, classical character of your chosen race.
+
+#####GStat Modifiers
+No changes to stats.
+
+#####GStarting Equipment
+A classical character begins the game with:
+ Some rations
+ Some torches
diff --git a/lib/help/rm_herm.txt b/lib/help/rm_herm.txt
new file mode 100644
index 00000000..4787b8ae
--- /dev/null
+++ b/lib/help/rm_herm.txt
@@ -0,0 +1,36 @@
+~~~~~01|Hermit
+~~~~~02|Race Modifiers|Hermit
+#####R=== Hermit Race ===
+
+#####GDescription
+Hermits live retired from the world. Spending long hours studying, they
+weaken their physical side while they strengthen their spiritual powers.
+Thus they get higher mana reserves but are much worse at physical combat.
+
+#####GStat Modifiers
+Strength -3
+Intelligence +1
+Wisdom +1
+Dexterity -3
+Constitution -3
+Charisma +1
+Hit Dice -3 sides
+Spell Points +20%
+Exp Penalty +20%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -0.500 [0.000]
+ Archery -0.500 [0.000]
+Sneakiness 0.400 [0.000]
+ Stealth 3.000 [0.000]
+ Disarming 0.500 [0.000]
+Magic
+ Magic-Device 1.000 [0.000]
+Spirituality 0.500 [0.000]
+
+#####GStarting Equipment
+A hermit begins the game with:
+ Some rations
+ Some torches
diff --git a/lib/help/rm_lsoul.txt b/lib/help/rm_lsoul.txt
new file mode 100644
index 00000000..8a2e8a9c
--- /dev/null
+++ b/lib/help/rm_lsoul.txt
@@ -0,0 +1,26 @@
+~~~~~01|Lost Soul
+~~~~~02|Race Modifiers|Lost Soul
+#####R=== LostSoul ===
+
+#####GDescription
+
+#####RThis is a difficult modifier. Your character will almost always die quickly.
+#####RIt's probably best only to play it after you have some experience with normal
+#####Rcharacters.
+
+There are haunting whispers of souls that have come back from the Halls of
+Mandos, for purposes unknown. These are called Lost Souls, for it is presumed
+that their real body died off long ago, leaving only a soul to wander forever...
+or until killed again.
+Lost Souls start at level 98 of the Halls of Mandos. Very few ever make it out
+again. Those that do can continue as a fairly normal character, but with the
+advantage of any treasure and experience gained.
+
+#####GStat Modifiers
+No changes to stats.
+
+#####GStarting Equipment
+A Lost Soul starts the game with:
+ Some torches.
+ Over thirty scrolls of Identify.
+ Over twenty scrolls of Satisfy Hunger.
diff --git a/lib/help/rm_skel.txt b/lib/help/rm_skel.txt
new file mode 100644
index 00000000..aff6408d
--- /dev/null
+++ b/lib/help/rm_skel.txt
@@ -0,0 +1,40 @@
+~~~~~01|Skeleton
+~~~~~02|Race Modifiers|Skeleton
+#####R=== Skeletal Race ===
+
+#####GDescription
+As undead beings, skeletons need to worry very little about poison or
+attacks that can drain life. They do not really use eyes for perceiving
+things, and are thus not fooled by invisibility. Their bones are resistant
+to sharp shrapnels (not much to cut there), and they will quickly become
+resistant to cold. It is very hard for skeletons to eat food or drink potions.
+Although the magical effects of these will affect the skeleton even without
+entering the skeleton's (non-existent) belly, the potion / food itself will
+fall through the skeleton's jaws, giving no nutritional benefit.
+
+#####GStat Modifiers
+Strength 0
+Intelligence -2
+Wisdom -2
+Dexterity 0
+Constitution +1
+Charisma -4
+Hit Dice +0 sides
+Spell Points -30%
+Exp Penalty +45%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 0.800 [0.000]
+Sneakiness -0.100 [0.000]
+ Stealth -1.000 [0.000]
+ Disarming -0.500 [0.000]
+Magic
+ Magic-Device -0.500 [0.000]
+Spirituality 0.500 [0.000]
+
+#####GStarting Equipment
+A skeletal character begins the game with:
+ Some scrolls of satisfy hunger
+ Some torches
diff --git a/lib/help/rm_spec.txt b/lib/help/rm_spec.txt
new file mode 100644
index 00000000..4a11e0fd
--- /dev/null
+++ b/lib/help/rm_spec.txt
@@ -0,0 +1,44 @@
+~~~~~01|Spectre
+~~~~~02|Race Modifiers|Spectre
+#####R=== Spectral Race ===
+
+#####GDescription
+
+Another powerful undead creature, the spectre is a ghastly apparition,
+surrounded by an unearthly glow. They exist only partially on our
+plane of existence: half-corporeal, they can pass through walls, though
+this requires a sacrifice of some of their life-force. As a result
+they cannot rest whilst passing through a wall.
+
+As undead, they have a firm hold on their life force, can see invisible, and
+resist poison and cold. They also resist nether. Spectres make superb
+spellcasters, but their physical form is very weak. Like Zombies,
+Spectres gain almost no nutrition from ordinary food.
+
+#####GStat Modifiers
+Strength -5
+Intelligence +2
+Wisdom +2
+Dexterity +2
+Constitution -3
+Charisma -6
+Hit Dice -3 sides
+Spell Points +5%
+Exp Penalty +80%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery -0.500 [0.000]
+ Archery -0.200 [0.000]
+Sneakiness 0.200 [0.000]
+ Stealth 2.000 [0.000]
+ Disarming 0.200 [0.000]
+Magic
+ Magic-Device 0.800 [0.000]
+Spirituality 0.700 [0.000]
+
+#####GStarting Equipment
+A spectral character begins the game with:
+ Some scrolls of satisfy hunger
+ Some torches
diff --git a/lib/help/rm_vamp.txt b/lib/help/rm_vamp.txt
new file mode 100644
index 00000000..22ae6514
--- /dev/null
+++ b/lib/help/rm_vamp.txt
@@ -0,0 +1,32 @@
+~~~~~01|Vampire
+~~~~~02|Race Modifiers|Vampire
+#####R=== Vampire ===
+
+#####GDescription
+One of the mightier undead creatures, the vampire is an awe-inspiring
+sight. Yet this mighty creature has a serious weakness: the bright rays of
+sun are its bane, and it will need to flee the surface to the deep
+recesses of the earth until the sun finally sets. Darkness, on the other
+hand, only makes the vampire stronger. Being undead, the vampire has a firm
+hold on its life force, and resists nether attacks. The vampire also
+resists cold and poison based attacks. It is, however, susceptible to its
+perpetual hunger for fresh blood, which can only be satiated by sucking
+the blood from a nearby monster, which is the vampire's special power.
+
+It should be noted that the vampires are so sensitive to daylight that even
+certain artifact light items which are filled with daylight will hurt them
+if they try to wield the items. Fortunately, the vampires do not really
+need these items, since they radiate an aura of 'dark light' of their own.
+Light resistance will, in any case, protect the vampire from the adverse
+effects of sunlight.
+
+#####GStat Modifiers
+Strength +3
+Intelligence +2
+Wisdom -3
+Dexterity -2
+Constitution +1
+Charisma -4
+Hit Dice +1 side
+Spell Points +0%
+Exp penalty +100%
diff --git a/lib/help/rm_zomb.txt b/lib/help/rm_zomb.txt
new file mode 100644
index 00000000..be89162b
--- /dev/null
+++ b/lib/help/rm_zomb.txt
@@ -0,0 +1,39 @@
+~~~~~01|Zombie
+~~~~~02|Race Modifiers|Zombie
+#####R=== Zombie Race ===
+
+#####GDescription
+Much like Skeletons, zombies too are undead horrors: they are resistant to
+life-draining attacks, they become resistant to cold-based attacks (actually
+earlier than skeletons), resist poison and can see invisible, while being still
+vulnerable to cuts (unlike skeletons). They also gain very little nutrition from
+the food of mortals. However, zombies are, as the name implies, practically
+mindless.
+
+#####GStat Modifiers
+Strength +2
+Intelligence -6
+Wisdom -6
+Dexterity +1
+Constitution +4
+Charisma -5
+Hit Dice +3 sides
+Spell Points -30%
+Exp Penalty +45%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat
+ Weaponmastery 0.500 [0.000]
+Sneakiness -0.100 [0.000]
+ Stealth -1.000 [0.000]
+ Disarming -0.200 [0.000]
+Magic
+ Magic-Device -0.200 [0.000]
+Spirituality 0.500 [0.000]
+
+#####GStarting Equipment
+A zombie character begins the game with:
+ Some scrolls of satisfy hunger
+ Some torches
+
diff --git a/lib/help/skills.txt b/lib/help/skills.txt
new file mode 100644
index 00000000..c4a02c06
--- /dev/null
+++ b/lib/help/skills.txt
@@ -0,0 +1,539 @@
+|||||oy
+~~~~~55|Skills
+#####R=== ToME Skills System ===
+One of the big differences between standard "Vanilla" Angband and ToME is the
+implementation of a skill system where the player can choose what skills she
+will improve as her character progresses. As such, many abilities such as
+spell casting, fighting and trap disarming *do not* increase automatically -
+the player must choose to use skill points to improve those abilities. This
+gives the player the chance to tailor a character to suit their playing style
+with a lot more flexibility than has existed with a fixed progression system
+in the past. However, not all types of characters are able to gain skills to
+the same degree; while a fighter can learn some magic, he's unlikely to become
+as good at it as a mage can. So the number of skill points required to raise
+a skill to the next level varies according to the starting "type" of character.
+
+You can also spend skill points in "one-off purchase" *****ability.txt*0[Abilities].
+~~~~~56|Skills|Screen
+#####GThe Skills Menu
+Each time you gain a level of experience, you receive 6 skill points to spread
+around as you wish. To use these skill points, you need to access the skills
+menu ("G" for both keysets). This opens up a long list of abilities that can
+be improved. The menu may look something like this:
+
+&&&&&w w w w w w w w w w w w w w w w w w w w w w w w w w w w wTwowMwEw wSwkwiwlwlwsw wSwcwrwewewnw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&BEBnBtBeBrW WtWoW WdWeWvWeWlWoWpW WaW WbWrWaWnWcWhW,W BuBpW/BdBoBwBnW WtWoW WmWoWvWeW,W BrBiBgBhBtW/BlBeBfBtW WtWoW WmWoWdWiWfWyW,W B?W WfWoWrW WhWeWlWpw w w w
+&&&&&BSBkBiBlBlB BpBoBiBnBtBsB BlBeBfBtB:B B6w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&yGyeynyeyryayly yaybyiylyiytyyy ytyoy yfyiygyhyty yaynydy ytyoy ypysyeyuydyoy-yiydy yayrymyoyrysy yaynydy ywyeyaypyoynysy.w w w w w w w w w w w w w w w w w w w
+&&&&&yIyty yaylysyoy yaylylyoywysy ytyoy yuysyey yhyeyayvyiyeyry yayrymyoyuyrysy ywyiytyhyoyuyty ypyeynyaylytyiyeysw w w w w w w w w w w w w w w w w w w w w w w w w
+&&&&&G[G-G]GCGoGmGbGaGtw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w G0G2G.G0G0G0G G[G0G.G8G0G0G]w w w w w w
+&&&&&w w w w w w-w wWwewawpwownwmwawswtwewrwyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w0w1w.w0w0w0w w[w0w.w8w5w0w]w w w w w w
+&&&&&w w w w w w w w o o.o oSowooorodo-omoaosotoeoroyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o4o0o0o]w w w w w w
+&&&&&w w w w w w w w o o.o oAoxoeo-omoaosotoeoroyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o4o0o0o]w w w w w w
+&&&&&w w w w w w w w o o.o oHoaofotoeodo-omoaosotoeoroyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o4o0o0o]w w w w w w
+&&&&&w w w w w w w w o o.o oPoooloeoaoromo-omoaosotoeoroyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o4o0o0o]w w w w w w
+&&&&&w w w w w w.w wAwrwcwhwewrwyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w0w1w.w0w0w0w w[w0w.w6w0w0w]w w w w w w
+&&&&&w w w w o o.o oAonotoiomoaogoiocw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o5o5o0o]w w w w w w
+&&&&&w w+w wSwnwewawkwiwnwewswsw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w0w1w.w0w0w0w w[w0w.w9w0w0w]w w w w w w
+&&&&&w w+w wMwawgwiwcw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w0w1w.w0w0w0w w[w0w.w3w0w0w]w w w w w w
+&&&&&w w-w wSwpwiwrwiwtwuwawlwiwtwyw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w0w1w.w0w0w0w w[w0w.w4w0w0w]w w w w w w
+&&&&&w w w w o o.o oPoroaoyoeorw w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o5o0o0o]w w w w w w
+&&&&&o o.o oMooonosotoeoro-oloooroew w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w o0o0o.o0o0o0o o[o0o.o5o0o0o]w w w w w w
+
+Now, looking at this screen, there are several things to be aware of. The
+first line lets you know if you have any available skill points to spend, and
+how many there are. Following that are 2 description lines for the currently
+selected skill - in this case they are describing the "Combat" skill. When
+looking at the list of skills, there are a few different colours used - the
+light green coloured skill (which also has its starting character in square
+brackets []) is the currently selected one - Combat in the example above.
+Skills that you cannot learn are omitted from the list. Skills that you are
+capable of learning, but as yet have not, are coloured in orange, while skills
+of which you have some knowledge are shown in white.
+
+At the end of each skill is a pair of numbers. The first represents your
+current level of knowledge in the skill, and the second how much an advance
+in this knowledge investing one skill point in this skill would produce. So,
+in the above example, if the player invested one skill point in their Combat
+skill, the skill would increase from 02.000 to 02.800.
+
+In addition, investing in some skills may raise your knowledge in others.
+This improvement is based on the modifier in the related class (the one which
+gets the free points). For example, a skill point put into Weaponmastery
+raises Combat by 0.5 skill points. This is actually multiplied by the skill
+modifier that your character has in the Combat skill. For example, a
+Swordmaster investing a skill point into Weaponmastery would have his Combat
+skill raised by 0.5 * [0.900] while a Runecrafter would have his Combat
+skill raised by 0.5 * [0.200].
+
+As well as this, skills are grouped together in similar types. Looking under
+the Combat skill, there are subtypes of Weaponmastery, Archery and Antimagic.
+An increase of one of these subskills may also increase the main skill by a
+small amount. You can tell which skills have subskills by the + (or -) in front
+of their name. The + indicates that there are more skills within this category.
+To open a skill category up, move the cursor up/down until the skill category
+is green, then hit the "Enter" key. Likewise, the - indicates that the category
+is already opened, and selecting this and hitting the "Enter" key will close it
+up again. Skills which don't have usable subskills start with a ".".
+
+To spend points on a skill (including skill categories), use the left/right
+arrow (right arrow or "6" adds one skill point, left arrow or "4" removes
+one). Spending points on a sub-skill will also marginally improve the parent
+skill (or skill category). When you've finished spending skill points (and any
+unspent points *will* be saved), hit the "Esc" key to finish. This will give
+a confirmation prompt to check that you really do want to spend your points as
+you've assigned them. Saying 'y' saves the changes and allows you to use or
+apply your new skills :).
+
+All skills have a maximum level of 50, and as long as you can learn a skill,
+and have enough skill points to pump into it, it is theoretically possible to
+get it to level 50 no matter what your race, class or how you learned it.
+
+Each skill affects your character differently. It may be worth getting one or
+more of your characters skills to 50, but it may not be worth investing [[[[[Bany]
+skill points in some other skills. As general and personal advice, which may
+not work for you, I'd say concentrate on a few skills, and leave the others
+empty. Just because you [[[[[Bcan] learn a skill, it doesn't mean you have to.
+It often pays to have a plan ("I'm going to make this assassin the
+stealthiest, most able-dodging, backstabber around. I'm not going to bother
+with trapping or thieving ability") which you can stick to for the whole game.
+~~~~~57|Skills|List of skills
+#####GThe Skills Themselves
+So you want to know what each of the skills do so that you can decide how to
+spend you're hard-earned points, huh? Well, each skill affects different
+abilities, and not all of them are intuitive - but they don't take all that
+long to learn :).
+If you don't find this informative enough, and would like more detailed spoilers
+on what each skill does, try [[[[[ghttp://www.killerbunnies.org/angband/skill-220.html]
+for some third party help!
+The skills are:
+
+ *****skills.txt*27[Air] *****skills.txt*49[Alchemy] *****skills.txt*50[Antimagic] *****skills.txt*08[Archery]
+ *****skills.txt*05[Axe-mastery] *****skills.txt*18[Backstab] *****skills.txt*13[Barehand-combat] *****skills.txt*61[Bearform-combat]
+ *****skills.txt*12[Boomerang-mastery] *****skills.txt*58[Boulder-throwing] *****skills.txt*10[Bow-mastery] *****skills.txt*01[Combat]
+ *****skills.txt*30[Conveyance] *****skills.txt*44[Corpse-preservation]*****skills.txt*04[Critical-hits] *****skills.txt*11[Crossbow-mastery]
+ *****skills.txt*52[Demonology] *****skills.txt*16[Disarming] *****skills.txt*31[Divination] *****skills.txt*20[Dodging]
+ *****skills.txt*28[Earth] *****skills.txt*25[Fire] *****skills.txt*60[Geomancy] *****skills.txt*06[Hafted-mastery]
+ *****skills.txt*21[Magic] *****skills.txt*54[Magic-device] *****skills.txt*24[Mana] *****skills.txt*29[Meta]
+ *****skills.txt*47[Mimicry] *****skills.txt*33[Mind] *****skills.txt*41[Mindcraft] *****skills.txt*42[Monster-lore]
+ *****skills.txt*59[Music] *****skills.txt*34[Nature] *****skills.txt*35[Necromancy] *****skills.txt*07[Polearm-mastery]
+ *****skills.txt*45[Possession] *****skills.txt*39[Prayer] *****skills.txt*36[Runecraft] *****skills.txt*09[Sling-mastery]
+ *****skills.txt*14[Sneakiness] *****skills.txt*22[Spell-power] *****skills.txt*38[Spirituality] *****skills.txt*23[Sorcery]
+ *****skills.txt*19[Stealing] *****skills.txt*15[Stealth] *****skills.txt*53[Stunning-blows] *****skills.txt*43[Summoning]
+ *****skills.txt*03[Sword-mastery] *****skills.txt*46[Symbiosis] *****skills.txt*32[Temporal] *****skills.txt*37[Thaumaturgy]
+ *****skills.txt*48[Udun] *****skills.txt*26[Water] *****skills.txt*02[Weaponmastery]
+
+
+~~~~~01|Skills|Combat
+[[[[[BCombat]
+The combat skill is used to determine the maximum combined weight of armour
+you can wear before you become encumbered by it. It also affects your general
+fighting ability, although not to as great an extent as Weaponmastery, and
+determines the speed and strength of pseudo-id of weapons and armour.
+
+Investing in the combat skill? You might be interested in the
+*****ability.txt*05[Extra Max Blow(1)] and *****ability.txt*06[(2)] abilities.
+
+Sub-skills of Combat are Weaponmastery, Archery, Barehand-combat,
+Boulder-throwing and Anti-magic.
+~~~~~02|Skills|Weaponmastery
+[[[[[BWeaponmastery]
+This skill is a sub-skill of the Combat skill. It affects your general
+ability to use melee weapons of all sorts. Spending 1 skill point on
+Weaponmastery adds 0.5 bonus skill points to Combat.
+
+Investing in the weaponmastery skill? You might be interested in the
+*****ability.txt*02[Spread Blows] ability.
+
+Sub-skills of Weaponmastery are Sword-mastery, Axe-mastery, Hafted-mastery
+and Polearm-mastery.
+~~~~~03|Skills|Sword-mastery
+[[[[[BSword-mastery]
+This skill is a sub-skill of the Weaponmastery skill. It affects your ability
+to use bladed weapons (e.g. daggers, swords). Spending 1 skill point on your
+Sword-mastery skill adds 0.25 bonus skill points to your Weaponmastery skill
+and adds 0.07 bonus skill points to your Combat skill.
+
+Critical-hits is a sub-skill of Sword-mastery.
+~~~~~04|Skills|Critical-Hits
+[[[[[BCritical-hits]
+This skill is a sub-skill of the Sword-mastery skill. It affects your ability
+to deal critical hits to monsters using a bladed weapon that weighs less than 5
+pounds. Spending one skill point on your Critical-hits skill also increases
+your Sword-mastery skill by 0.05 skill points.
+~~~~~05|Skills|Axe-mastery
+[[[[[BAxe-mastery]
+This skill is a sub-skill of the Weaponmastery skill. It affects your ability
+to use axes. Spending 1 skill point on your Axe-mastery skill adds 0.25 bonus
+skill points to your Weaponmastery skill and adds 0.07 bonus skill points to
+your Combat skill.
+~~~~~06|Skills|Hafted-mastery
+[[[[[BHafted-mastery]
+This skill is a sub-skill of the Weaponmastery skill. It affects your ability
+to use hafted weapons (e.g. whips & maces). Spending 1 skill point on your
+Hafted-mastery skill adds 0.25 bonus skill points to your Weaponmastery skill
+and adds 0.07 bonus skill points to your Combat skill.
+
+Stunning-blows is a sub-skill of Hafted-mastery.
+~~~~~53|Skills|Stunning-blows
+[[[[[BStunning-blows]
+This skill is a sub-skill of the Hafted-mastery skill. It affects your ability
+to stun opponents when doing critical hits with a hafted weapon that weighs
+more than 5 lbs. Spending one skill point on your Stunning-blows skill also
+increases your Hafted-mastery skill by 0.05 skill points.
+~~~~~07|Skills|Polearm-mastery
+[[[[[BPolearm-mastery]
+This skill is a sub-skill of the Weaponmastery skill. It affects your ability
+to use polearms (e.g. pikes & halberds). Spending 1 skill point on your
+Polearm-mastery skill adds 0.25 bonus skill points to your Weaponmastery skill
+and adds 0.07 bonus skill points to your Combat skill.
+
+Investing in the Polearm-mastery skill? You might be interested in the
+*****ability.txt*10[Far reaching attack] ability.
+~~~~~08|Skills|Archery
+[[[[[BArchery]
+This skill is a sub-skill of the Combat skill. It affects your general
+ability to use ranged weapons of all sorts. Spending one skill point on your
+Archery skill adds 0.5 bonus skill points to your Combat skill.
+
+Investing in the Archery skill? You might be interested in the
+*****ability.txt*07[Ammo creation] ability.
+
+Sub-skills of Archery include Sling-mastery, Bow-mastery, Crossbow-mastery
+and Boomerang-mastery.
+~~~~~09|Skills|Sling-mastery
+[[[[[BSling-mastery]
+This skill is a sub-skill of the Archery skill. It affects your ability to
+use Slings. Spending 1 skill point on your Sling-mastery skill adds 0.25
+bonus skill points to your Archery skill and 0.07 bonus skill points to your
+Combat skill.
+~~~~~10|Skills|Bow-mastery
+[[[[[BBow-mastery]
+This skill is a sub-skill of the Archery skill. It affects your ability to
+use both Long and Short Bows. Spending 1 skill point on your Bow-mastery
+skill adds 0.25 bonus skill points to your Archery skill and 0.07 bonus skill
+points to your Combat skill.
+~~~~~11|Skills|Crossbow-mastery
+[[[[[BCrossbow-mastery]
+This skill is a sub-skill of the Archery skill. It affects your ability
+to use both Heavy and Light Crossbows. Spending 1 skill point on your
+Crossbow-mastery skill adds 0.25 bonus skill points to your Archery skill
+and 0.07 bonus skill points to your Combat skill.
+~~~~~12|Skills|Boomerang-mastery
+[[[[[BBoomerang-mastery]
+This skill is a sub-skill of the Archery skill. It affects your ability to
+use all boomerangs. Spending 1 skill point on your Boomerang-mastery skill
+adds 0.25 bonus skill points to your Archery skill and 0.07 bonus skill
+points to your Combat skill.
+~~~~~13|Skills|Barehand-combat
+[[[[[BBarehand-combat]
+This skill is a sub-skill of the Combat skill. It affects your general ability
+to fight using martial arts. In order to utilise it, you must be capable of
+using a weapon in the first place, but choose not to. Spending 1 skill point
+on your Barehand-combat skill adds 0.5 bonus skill points to your Combat skill.
+Barehand-combat fighters develop stronger and faster attacks, and also gain
+speed bonuses, as they advance in skill. However, they cannot use this skill
+whilst wearing heavy armour.
+~~~~~61|Skills|Bearform-combat
+[[[[[BBearform-combat]
+This skill is a sub-skill of the Combat skill. It affects your ability to fight
+while in the form of a bear. In order to utilise it, you must be in bearform.
+*****r_beorn.txt*0[Beornings] are the adventurers most likely to use this form of skill.
+~~~~~58|Skills|Boulder-throwing
+[[[[[BBoulder-throwing]
+This skill is a sub-skill of the Combat skill. It affects your ability to
+throw boulders and make them from granite walls. Spending 1 skill point on
+your Boulder-throwing skill adds 0.4 bonus skill points to your Combat skill.
+~~~~~50|Skills|Antimagic
+[[[[[BAntimagic]
+This skill is a sub-skill of the Combat skill. It generates a field around
+the character within which magic cannot work. As such, it can be very useful
+to prevent monsters from casting offensive spells against you or from
+teleporting away from you just before you kill them - but it will also prevent
+you from casting spells, or teleporting away when they've almost killed you!
+It also inhibits your ability to do magic, affecting *all* the magic and
+spirituality sub-skills.
+
+This skill does not affect your ability to use scrolls and potions, but other
+items that require [Self]Magic-Device are affected. At higher levels you gain
+the ability to detect traps and disrupt all teleportation.
+~~~~~14|Skills|Sneakiness
+[[[[[BSneakiness]
+The sneakiness skill affects your searching and perception abilities.
+
+Sub-skills of Sneakiness include Stealth, Disarming, Trapping, Backstab,
+Stealing and Dodging.
+~~~~~15|Skills|Stealth
+[[[[[BStealth]
+This skill is a sub-skill of the Sneakiness skill. It affects your ability
+to move around the dungeon quietly so that you are not noticed by its
+inhabitants. Spending 1 skill point on your Stealth skill adds 0.15 bonus
+skill points to your Sneakiness skill.
+~~~~~16|Skills|Disarming
+[[[[[BDisarming]
+This skill is a sub-skill of the Sneakiness skill. It affects your ability
+to safely disarm any traps you find. Spending 1 skill point on your Disarming
+skill adds 0.1 bonus skill points to your Sneakiness skill.
+
+Investing in the Disarming skill? You might be interested in the *****ability.txt*11[Trapping]
+ability.
+~~~~~18|Skills|Backstab
+[[[[[BBackstab]
+This skill is a sub-skill of the Sneakiness skill. It affects your ability
+to sneak up on monsters and do extra damage to them before they wake up.
+It also affects monsters who have turned to flee from you. Spending 1 skill
+point on your Backstab skill adds 0.05 bonus skill points to your Sneakiness
+skill.
+~~~~~19|Skills|Stealing
+[[[[[BStealing]
+This skill is a sub-skill of the Sneakiness skill. It affects your ability
+to steal items from monsters and shops. Be careful when stealing from shops;
+if you're caught, the shopkeeper will close his doors to you and not open
+them again. I have heard that shop keepers do not stay in one shop forever
+though. Spending 1 skill point on your Stealing skill adds 0.15 bonus skill
+points to your Sneakiness skill.
+~~~~~20|Skills|Dodging
+[[[[[BDodging]
+This skill is a sub-skill of the Sneakiness skill. It affects your ability
+to dodge out of the way of monster blows and bolts. The less armour you wear
+and the less you carry, the greater your chance of dodging a blow. Rings and
+amulets do not affect your chance to dodge, but full armour will almost render
+the effect of the skill obsolete. Spending 1 skill point on your Dodging skill
+adds 0.1 bonus skill points to your Sneakiness skill.
+~~~~~21|Skills|Magic
+[[[[[BMagic]
+The Magic skill affects your general use of magic items, the amount of mana
+you can handle, and in general your ability to do magic. It can also affect
+the strength of wands and staffs.
+
+Investing in the Magic skill? You might be interested in the *****ability.txt*04[Perfect Casting]
+ability.
+
+Sub-skills include: Magic-device, Spell-power, Sorcery, Mana, Fire, Water, Air,
+Earth, Meta, Conveyance, Divination, Temporal, Mind, Nature, Udun, Demonology,
+Necromancy, Runecraft, Thaumaturgy, and Alchemy.
+~~~~~54|Skills|Magic-device
+[[[[[BMagic-device]
+This skill is a sub-skill of the Magic skill. It eases the use of magical
+devices, such as wands, staves, and rods, and boosts the casting level of spells
+stored in a wand or a staff. *****magic.txt*02[More on this]. It also helps pseudo-id of magic
+objects. Spending 1 skill point on your Magic-device skill adds 0.07 bonus skill
+points to your Magic skill.
+~~~~~22|Skills|Spell-power
+[[[[[BSpell-power]
+This skill is a sub-skill of the Magic skill. It boosts the casting level of
+most spells you are capable of casting. For example, if you have level one in
+the mana school, you could cast "Manathrust". For every 2.5 skill levels of
+Spell-power, Manathrust becomes more powerful, adding +1 casting level to the
+spell. Note that this is not exactly the same as certain magic items which
+boost spell power. Spending 1 skill point on your Spell-power skill adds 0.2
+bonus skill points to your Magic skill.
+
+[[[[[BThis skill only affects the 11 primary schools] (Mana, Earth, Air, Fire,
+Water, Meta, Mind, Temporal, Conveyance, Divination and Nature), as well as
+Geomancy and the spells granted by the Gods.
+~~~~~23|Skills|Sorcery
+[[[[[BSorcery]
+This skill is a sub-skill of the Magic skill. It allows you to access any
+spell in the 11 schools up to the sorcery skill level. For example, if you
+have a sorcery skill of 1, you could cast "Manathrust", which is a level 1
+mana school spell; and "Phase Door", which is a level 1 conveyance school
+spell. Spending 1 skill point on your Sorcery skill adds 0.2 bonus skill
+points to your Magic skill.
+
+[[[[[BThis skill only affects the 11 primary schools] (Mana, Earth, Air, Fire,
+Water, Meta, Mind, Temporal, Conveyance, Divination and Nature).
+
+However, handling that much magic is hazardous to your health, and as such
+reduces both your hit points and your fighting ability. Any ability in sorcery
+affects your Weaponmastery, Archery, Barehand-combat and gives a negative
+percentage modifier to your total hit points, equal to the level of your
+sorcery skill (i.e. if sorcery is 12.500, hit points get modified by -12.5%).
+~~~~~24|Skills|Mana
+[[[[[BMana]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_mana.txt*0[mana] school, and as it increases so does the casting level of
+spells already attained in the school. For example, if you have level 1 in
+the mana school, you could cast "Manathrust" at a casting level of 1. For
+every skill level you add to Mana, Manathrust will become more powerful,
+adding 1 casting level to the spell. Spending 1 skill point on your Mana
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~60|Skills|Geomancy
+[[[[[BGeomancy]
+This skill is a subskill of the Magic skill. It gives access to spells
+within the *****m_geoman.txt*0[Geomancy] school, and as it increases so does the casting level of
+spells already attained in the school. Most spells from this school rely
+on the Fire, Water, Air and Earth skills as well. Spending 1 skill point
+on your Geomancy skill adds 0.45 bonus skill points to your Fire, Water,
+Air and Earth skills.
+~~~~~25|Skills|Fire
+[[[[[BFire]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_fire.txt*0[fire] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Fire
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~26|Skills|Water
+[[[[[BWater]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_water.txt*0[water] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Water
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~27|Skills|Air
+[[[[[BAir]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_air.txt*0[air] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Air
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~28|Skills|Earth
+[[[[[BEarth]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_earth.txt*0[earth] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Earth
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~29|Skills|Meta
+[[[[[BMeta]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_meta.txt*0[meta] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Meta
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~30|Skills|Conveyance
+[[[[[BConveyance]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_convey.txt*0[conveyance] school, and as it increases so does the casting level
+of spells already attained in the school. Spending 1 skill point on your
+Conveyance skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~31|Skills|Divination
+[[[[[BDivination]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_divin.txt*0[divination] school, and as it increases so does the casting level
+of spells already attained in the school. Spending 1 skill point on your
+Divination skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~32|Skills|Temporal
+[[[[[BTemporal]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_tempo.txt*0[temporal] school, and as it increases so does the casting level
+of spells already attained in the school. Spending 1 skill point on your
+Temporal skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~33|Skills|Mind
+[[[[[BMind]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_mind.txt*0[mind] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Mind
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~34|Skills|Nature
+[[[[[BNature]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_nature.txt*0[nature] school, and as it increases so does the casting level of
+spells already attained in the school. Spending 1 skill point on your Nature
+skill adds 0.1 bonus skill points to your Magic skill.
+
+Investing in the Nature skill? You might be interested in the *****ability.txt*03[Tree Walking]
+ability.
+~~~~~48|Skills|Udun
+[[[[[BUdun]
+This skill is a sub-skill of the Magic skill. It gives access to spells
+within the *****m_udun.txt*0[Udun] school, and is available only to worshippers of Melkor.
+As it increases so does the casting level of spells already attained in the
+school. Spending 1 skill point on your Udun skill adds 0.1 bonus skill
+points to your Magic skill.
+~~~~~52|Skills|Demonology
+[[[[[BDemonology]
+This skill is a sub-skill of the Magic skill. *****m_demono.txt*0[Demonology] gives access to spells
+contained within special Demon-blades, -shields and -horns (helms), and as it
+increases so does the casting level of spells already attained in the school.
+This skill is available only to Demonologists, or those sufficiently
+corrupted with demon-like powers. Spending 1 skill point on your Demonology
+skill adds 0.1 bonus skill points to your Magic skill.
+~~~~~35|Skills|Necromancy
+[[[[[BNecromancy]
+This skill is a sub-skill of the Magic skill. It grants access to *****m_necrom.txt*0[necromancy]
+spells. This is the base skill of the Necromancer class. Spending 1 skill
+point on your Necromancy skill adds 0.04 bonus skill points to your Magic skill.
+
+Investing in the Necromancy skill? You might be interested in the
+*****ability.txt*08[Touch of Death] and *****ability.txt*12[Undead Form] abilities.
+~~~~~36|Skills|Runecraft
+[[[[[BRunecraft]
+This skill is a sub-skill of the Magic skill. This is the base skill of the
+Runecrafter class. Spending 1 skill point on your Runecraft skill adds 0.12
+bonus skill points to your Magic skill.
+~~~~~37|Skills|Thaumaturgy
+[[[[[BThaumaturgy]
+This skill is a sub-skill of the Magic skill. Each level of *****m_thaum.txt*0[thaumaturgy] gives
+a few random attack spells that can be cast without the use of spell books of
+any sort. However, once learned these spells do not gain in levels as the
+thaumaturgy skill or the spell-power skills are increased. Spending 1 skill
+point on your Thaumaturgy skill adds 0.06 bonus skill points to your Magic
+skill.
+~~~~~49|Skills|Alchemy
+[[[[[BAlchemy]
+The Alchemy skill affects your ability to extract and use essences to create
+magical items.
+
+Investing in the Alchemy skill? You might be interested in the *****ability.txt*09[Artifact Creation]
+ability.
+~~~~~38|Skills|Spirituality
+[[[[[BSpirituality]
+The spirituality skill influences things which have a "helping hand" from the
+Gods, like your saving throw, and the general spirituality skills.
+
+Sub-skills of Spirituality are Prayer, Mindcraft and Music.
+~~~~~39|Skills|Prayer
+[[[[[BPrayer]
+This skill is a sub-skill of the Spirituality skill. It affects what level of
+your *****gods.txt*0[God's] special magic you can access (and what levels of the additional
+schools that each God also provides). Spending 1 skill point on your Prayer
+skill adds 0.1 bonus skill points to your Spirituality skill and 0.1 bonus
+skill points to your Magic skill.
+~~~~~41|Skills|Mindcraft
+[[[[[BMindcraft]
+This skill is a sub-skill of the Spirituality skill. It affects what level of
+*****m_mindcr.txt*0[Mindcrafter powers] you can access, which is done without books and is
+available under the "m" menu. Spending 1 skill point on your Mindcraft skill
+adds 0.1 bonus skill points to your Spirituality skill and 0.1 bonus skill
+points to your Magic skill.
+~~~~~59|Skills|Music
+[[[[[BMusic]
+This skill is a sub-skill of the Spirituality skill. It affects what level of
+*****m_music.txt*0[Musical songs] you can access through instruments. This power
+is available under the "m" menu. Spending 1 skill point on your Music skill
+adds 0.1 bonus skill points to your Spirituality skill and 0.1 bonus skill
+points to your Magic skill.
+~~~~~42|Skills|Monster-lore
+[[[[[BMonster-lore]
+The monster-lore skill affects your general ability at the monster related
+skills. It determines how much experience you will gain if your *****dungeon.txt*18[pets] kill a
+monster, and how many companions you can have. At skill level 12, it allows
+you to turn a pet into a loyal companion.
+
+Sub-skills of Monster-lore are Summoning, Corpse-preservation, Possession,
+Symbiosis, and Mimicry.
+~~~~~43|Skills|Summoning
+[[[[[BSummoning]
+This skill is a sub-skill of the Monster-lore skill. It affects your ability
+to create "totems" and use them to summon monsters to your aid. Spending 1
+skill point on your Summoning skill adds 0.1 bonus skill points to your
+Monster-lore skill.
+~~~~~44|Skills|Corpse-preservation
+[[[[[BCorpse-preservation]
+This skill is a sub-skill of the Monster-lore skill. It affects your ability
+to kill monsters without destroying their bodies, so the corpses will be
+available to use. Spending 1 skill point on your Corpse-preservation skill
+adds 0.1 bonus skill points to your Monster-lore skill.
+~~~~~45|Skills|Possession
+[[[[[BPossession]
+This skill is a sub-skill of the Monster-lore skill. It affects your ability
+to possess a dead monster's corpse. Spending 1 skill point on your Possession
+skill adds 0.1 bonus skill points to your Monster-lore skill.
+~~~~~46|Skills|Symbiosis
+[[[[[BSymbiosis]
+This skill is a sub-skill of the Monster-lore skill. It affects your ability
+to go into symbiosis with monsters that cannot move, and to cast *****m_symbio.txt*0[Symbiotic]
+spells. Spending 1 skill point on your Symbiosis skill adds 0.1 bonus skill
+points to your Monster-lore skill.
+~~~~~47|Skills|Mimicry
+[[[[[BMimicry]
+This skill is a sub-skill of the Monster-lore skill. It affects your ability
+to use cloaks of mimicry to change form and to cast *****m_mimic.txt*0[Mimicry spells].
+Spending 1 skill point on your Mimicry skill adds 0.1 bonus skill points to
+your Monster-lore skill.
diff --git a/lib/help/spoil_faq.txt b/lib/help/spoil_faq.txt
new file mode 100644
index 00000000..e4a77c15
--- /dev/null
+++ b/lib/help/spoil_faq.txt
@@ -0,0 +1,73 @@
+|||||oy
+~~~~~01|Help|Spoiled FAQ
+~~~~~02|FAQ - contains spoilers
+#####R ToME Spoiler FAQ
+#####R Updated for version 2.3.x
+
+#####G------------------------------------------------------------------------------
+
+This page contains significant spoilers. Don't browse it unless you want some
+parts of the game ruined, but don't expect the spoilers to spoil you completely!
+
+~~~~~06|Spoilers|Merton the lost Hobbit quest
+#####G------------------------------------------------------------------------------
+#####GQ: I'm in the Maze, but I cannot find Merton!
+
+A: Merton appears on a *random* level between dungeon level 26 (1300') and
+36 (1800'). Each maze level is one panel by one panel in size, but *full* of
+passageways - so you may have to do quite a bit of tunnelling to search the
+whole level.
+
+#####G------------------------------------------------------------------------------
+#####GQ: I've found Merton, now what?
+
+A: There is a new command that has been added to ToME that allows you to pass
+objects to creatures. What would you try and do if you had a broken leg and
+needed to get back to the town?
+~~~~~07|Spoilers|Lothlorien Poisoned water quest
+#####G------------------------------------------------------------------------------
+#####GQ: I'm trying to find the Poisoned water quest at Lothlorien, but cannot
+#####G find the quest entrance!
+
+A: This quest is located in the wilderness. To the west of Lothlorien are 4
+water squares in an upside down L shape. One of these squares will contain the
+quest. (Viewed from the Wilderness map). There is no yellow > sign, so don't
+bother looking for one.
+~~~~~20|Spoilers|God Quest - directions
+~~~~~23|Gods|Quest - Spoilers
+#####G------------------------------------------------------------------------------
+#####GQ: I've been given directions to a temple by my God but can't find the
+#####G temple anywhere!
+
+A: It [[[[[BIS] there. However, your god's idea of compass directions that are
+not directly on the 4 main axes are probably slightly less acurate than your
+idea. In other words, if your god says it is South-East, s/he means it is
+somewhere in the quadrant that is between the south and east axes.
+~~~~~21|Spoilers|God Quest - relic
+#####G------------------------------------------------------------------------------
+#####GQ: Where is the relic by god was talking about? I've looked in the lost
+#####G temple and can't find it anywhere!
+
+A: It [[[[[BIS] there. However, when your god told you to look for it VERY
+carefully, s/he meant it. Regardless of your game settings, the relic will only
+be created once in the temple, at a random place. If you have searched the
+whole temple once over, and not located it, then you have missed it, and it is
+lost forever. Each temple has 5 dungeon levels, and the relic might be on
+any of these 5 levels.
+~~~~~22|Spoilers|God Quest - how many?
+#####G------------------------------------------------------------------------------
+#####GQ: Apparently my god has lost another piece of his relic and wants me to go
+#####G find it again. How many of these are there?
+
+A: You can receive up to five god quests, with the final piece yielding an
+extra reward. However you will only receive extra quests if you have
+sucessfully completed all the previous ones.
+~~~~~19|Deathmolds on Mount Doom and other quest areas.
+#####G------------------------------------------------------------------------------
+#####GQ: How do I complete the special no-teleport level quests with a Deathmold?
+
+A: You will need either some morphic oils, some mimicry skill, or the ring of
+Flare. Deathmolds are marked as experimental for a reason you know. Morphic
+oils can also help if you're having trouble communictaing with monsters. A very
+beautiful Elf springs to mind as possibly causing some problems if you are in
+Deathmold form.
diff --git a/lib/help/spoiler.hlp b/lib/help/spoiler.hlp
new file mode 100644
index 00000000..bc229852
--- /dev/null
+++ b/lib/help/spoiler.hlp
@@ -0,0 +1,18 @@
+|||||oy
+~~~~~01|Spoilers
+#####RWelcome to the Angband Online Spoiler System.
+#####R=============================================
+
+Please choose one of the following online spoiler files:
+
+ *****/acorspoil.txt*0[(a) Corruptions]
+ *****/bdunspoil.txt*0[(b) Dungeons]
+ *****/cessences.txt*0[(c) Essence Spoiler]
+ *****/dinscrip.txt*0[(d) Floor Inscriptions]
+ *****/eluckspoi.txt*0[(e) Luck]
+ *****/ffatespoi.txt*0[(f) Fates]
+ *****/gwishing.txt*0[(g) Wishing]
+ *****/hspoil_faq.txt*0[(h) Spoiled FAQ]
+
+
+ *****/zhelp.hlp*0[(z) Main Help menu]
diff --git a/lib/help/tome_faq.txt b/lib/help/tome_faq.txt
new file mode 100644
index 00000000..756ce639
--- /dev/null
+++ b/lib/help/tome_faq.txt
@@ -0,0 +1,381 @@
+|||||oy
+~~~~~01|Help|FAQ - Spoiler free
+~~~~~02|FAQ - Spoiler free
+#####R ToME FAQ
+#####R Updated for version 2.3.x
+
+#####G------------------------------------------------------------------------------
+
+#####R=== Differences Between ToME and Vanilla Angband ===
+
+The first main difference a new player to ToME will need to be aware of is
+that it has implemented a skills based system. Instead of the adventurer
+automatically improving in his abilities as he becomes more experienced,
+he gets 6 skill points to spend on his skills, allowing the player to
+customise what type of character she will play. See the *****skills.txt*0[skills] help file
+for details.
+
+A second major difference is that the main dungeon from Angband has been split
+into 4 "dungeons", each of which covers a different portion of the Angband
+dungeon's levels. Each of these 4 dungeons is located either in or near one of
+the four main towns so that the character can keep stocked up on supplies. As
+the adventurer advances in ability, he will need to travel overland to the next
+town/dungeon, which is most easily carried out using the wilderness map ("<"
+from town level). As well as these main places, there are a number of
+additional dungeons which the character may or may not choose to enter, which
+can have guardians, contain specific artifacts, or just be used as an
+alternative place to enjoy gaining experience. Note that not all of the places
+are actually "dungeons" - some are caves, forests, etc.
+
+ToME also offers the player the ability to undertake a series of quests.
+Random quests can be specified during start-up, and involve rescuing a princess
+from a group of monsters within the dungeon, or recovering a lost sword from
+(you guessed it...) a group of monsters. If you do not wish to play with
+random quests, simply specify "0" when asked how many you want during character
+generation. Other "fixed" quests are also available from the towns (whether
+random quests are enabled or not), usually given by the town leaders upon the
+request of the adventurer. It is not required for any adventurer to undertake
+the fixed quests, but they can result in some nice rewards.
+
+The third main difference between Vanilla Angband and ToME is the difference
+in character classes and races, as well as a very different magic system.
+See the help files on *****birth.txt*0[Creating a character] and the *****magic.txt*0[magic] system.
+Class abilities (generally referred to as skills) are generally accessed
+through the 'm' command. Most racial abilities, or corruptions, are accessed
+through the "U" command.
+
+To balance the expansion in things like player abilities and customisation, the
+list of both monsters and items has also been expanded. Be warned that items
+which were by default safe in Vanilla are not necessarily safe in ToME (a
+certain early artifact comes to mind here...), and picking on defenceless
+creatures is frowned upon....
+
+Happy adventuring!
+~~~~~03|Altars
+~~~~~04|Gods|Altars
+#####G------------------------------------------------------------------------------
+#####GQ: How do I use the altars (the 'O's) I see in the dungeon?
+
+A: ToME introduces a new system of gods.
+
+You can find altars only in Lothlorien and in the dungeon.
+The ones on the surface are dedicated to the good Valar (Eru, Manwe, Tulkas and
+Yavanna), while altars found in the dungeons are "sacred" for Melkor.
+You can use altars to convert yourself to the service of a specific Vala by
+using the "O" command while standing on them. Beware, this works only if you
+don't already have a God, and as a new convert, your God won't like you that
+much. Melkor also uses his altars as a mean of collecting sacrifices from his
+devotees; this function is likewise accomplished by the "O" command.
+
+Read *****gods.txt*0[gods.txt] for more information about Gods.
+~~~~~05|Fountains
+#####G------------------------------------------------------------------------------
+#####GQ: How do I use the fountains (the '_'s) I see in the dungeon?
+
+A: Fountains in ToME act like potions, but can only be identified by
+drinking from them. Each one can hold between 3 and 12 doses of the potion.
+Quaffing from a fountain can be done by using the 'H' command (in the standard
+keyset) and answering 'Q' at the prompt.
+
+You can also fill empty bottles at a fountain (enabling you to identify the
+potion and hence the type of fountain) by using the 'H' command and answering
+'F' at the prompt. The game will then ask you to choose bottles and how many
+bottles you want to fill. You can find empty bottles on the dungeon and
+drinking pints of fine ale/wine will give you emtpy bottles; if you are
+trained in Alchemy, you can reuse bottles after quaffing potions as well.
+
+#####G------------------------------------------------------------------------------
+#####GQ: I got killed by a Great Wyrm of Power at 50'!!! What happened?
+
+A: You killed a defenceless creature. I told you that it was frowned upon!
+~~~~~18|Artifacts that activate but I cannot wear or wield
+~~~~~17|Strange items
+#####G------------------------------------------------------------------------------
+#####GQ: I've found some strange items like a Red Tome, a Voodoo Doll, ...
+#####G What can I do with them?
+
+A: You've found an unusual artifact that cannot be wielded, but always
+has a sometimes-useful activation. It will not be listed in the known
+artifact list and its activation is chosen randomly. It would probably be
+wise for this kind of artifact be *identified* before use, as the
+activation can be something very nasty....
+
+To activate it, use the normal Activation command, but when prompted for which
+item to activate change to the backpack instead of wielded equipment.
+~~~~~10|Essences
+~~~~~11|Runes
+#####G------------------------------------------------------------------------------
+#####GQ: I keep coming across "essences" and "runes". What are they?
+
+Essences are the *****c_alchem.txt*0[Alchemist's] friend, and you can only use them if you
+have access to the *****skills.txt*49[Alchemy] skill.
+Runes are used to cast and store spells of varying types. *****c_runecr.txt*0[Runecrafters] are the
+class who are most proficient at using these. You can only use them if you
+have access to the *****skills.txt*36[Runecrafting] skill.
+~~~~~12|Homes
+#####G------------------------------------------------------------------------------
+#####GQ: Where can I store all my equipment? Theere's not enough room in my
+#####Ginventory? And what happened to the thieves quest in Bree?
+
+Nor is there supposed to be enough room in your backpack. It's not
+bottomless you know! If you go talk to the Mayor in Bree, he might let you know
+about a slight problem that there's been in town. If you can clear up the
+problem, you may find yourself with somewhere extra to keep your stuff. I've
+heard tell that there are similiar problems in other towns in Middle-Earth.
+~~~~~13|Fates|Prophets
+#####G------------------------------------------------------------------------------
+#####GQ: I spent 500 gp at the Prophet but she said nothing. Is that a bug ?
+
+A: No. Nor is it because Prophets are swindlers. She said nothing
+because you have no fate at this moment. You gain fates while playing, and
+will be warned by a message such as "You feel your fate has changed". A fate
+can be useless like finding a broken skull at level 30, deadly like dying at
+level 56, or really useful like never dying by the hand of a mortal.
+~~~~~14|Mathilde
+#####G------------------------------------------------------------------------------
+#####GQ: Who is Mathilde, the Science Student whom I see every so often in the
+#####G town?
+
+A: Most of the time she laughs and giggles. She has no loot on her, and
+she's never done you any harm. So leave her be - even if she should
+happen to shout "Drop dead, creep!"
+~~~~~15|Wrists hurting
+#####G------------------------------------------------------------------------------
+#####GQ: My wrists hurt a lot when playing the game. Should I take precautions?
+
+A: Yes, you should. Repetitive strain on wrists (which results from a badly
+placed keyboard, for example) can lead to serious injury of the wrist ligaments
+called Carpal Tunnel Syndrome. If you feel your wrists are strained, here is
+an exercise posted by Jason Maskell in rec.games.roguelike.angband which might
+help:
+
+Hold your arms out horizontally, make a fist, and then point the fist towards
+the floor, as much as you can. This will stretch one side's tendons. Hold for
+5 seconds. Then make a flat hand and hold it level with your arm, hold for 5
+seconds. Now splay your fingers and attempt to make your hand point toward the
+ceiling (this one is hard, so don't push it too much). You should feel your
+tendons stretching. Repeat this a few times. Take frequent breaks and do this
+if it starts to hurt a little bit. I was sliding very fast towards CTS and this
+corrected it.
+~~~~~16|Void jumpgates
+#####G------------------------------------------------------------------------------
+#####GQ: When I stand on void jumpgates I'm never teleported away, what's wrong?
+
+A: Void jumpgates are not automatic. You must press '>' while standing
+on one to activate it.
+
+#####G------------------------------------------------------------------------------
+#####GQ: When it panic saves it reloads an old savefile !!
+
+A: Now the panic saves use a different file to save, savefile.pnc.
+This file will be loaded first, if present, when the game is started.
+If it loads successfully, save the game immediately. Otherwise, delete
+the panic save, and your old (non-panic) savefile should be safe.
+
+~~~~~08|Monsters|They are talking to me!
+#####G------------------------------------------------------------------------------
+#####GQ: Farmer Maggot / Melinda Proudfoot keep shouting at me, and I cannot
+#####G kill them.
+
+A: Both these people need to talk to you about something. Have a chat with them
+(check the file *****command.txt*96[command.txt] for how to chat).
+~~~~~09|Sentient weapons
+#####G------------------------------------------------------------------------------
+#####GQ: I have found a sentient weapon, it says it has access to the realms
+#####G of Earth and Fire. How do I use these realms?
+
+A: You don't actually 'use' them as such. If a weapon is sentient it means it
+gains experience itself as it delivers killing blows. As it levels up it has
+the chance to gain pluses to hit and to damage, and also powers from any of the
+available 'realms'. For instance, the realm of fire gives the chance to gain
+resistance to fire, or fire branding on your weapon. The realm of earth has a
+chance to confer extra attacks or the power of causing earthquakes and so on.
+~~~~~24|Fumblefingers quests
+~~~~~25|Abbreviations|FF
+#####G------------------------------------------------------------------------------
+#####GQ: What or who is Fumblefingers? How do I get his quests?
+
+A: FF is short for Fumblefingers, the name that some players give to the
+adventurer who keeps having his sword stolen by monsters and asking you to
+find it for him. So named because he often seems to lose it to molds and
+other creatures you wouldn't expect to be able to pickpocket! During birth
+you'll be asked to specify a number of random quests you would like to
+attempt to complete. Some of these quests will take the form of princess
+quests, others will be fumble-finger quests. If you complete the task he
+sets you sucessfully, he'll offer to join you as a companion. If you do not
+want him to join you, he'll offer to teach you some new skills. Quite handy.
+~~~~~26|Random quests are not working.
+#####G------------------------------------------------------------------------------
+#####GQ: Where has the option gone to set the number of random quests?
+#####G Why aren't there any after the Barrow-downs?
+
+A: Turning on either of the options "Allow permanent dungeon levels" or "Always
+create special rooms" will disable random quests. Random quests can only be
+found in the four main dungeons (Barrowdowns, Mirkwood, Mordor and Angband).
+~~~~~27|Weird display
+~~~~~28|Floor tiles displaying incorrectly
+#####G------------------------------------------------------------------------------
+#####GQ: How do I get the dots to show up on floor tiles in Windows XP? I've tried
+#####G changing the tile character to the brighter dot. Toggling 'Bizarre Display'
+#####G mode helped with the trailing @@@@@@@@@@@@@@@@@ problem. Any suggestions? I
+#####G gave up a long time ago and play with graphics tiles now, but I'd like to be
+#####G able to fix this.
+
+A: In the file ./lib/pref/font_win.prf, either remove or comment out with # the
+lines that end in /0x1F, e.g.
+# open floor
+# F:1:0x01/0x1F
+Another possibility is to manually change the symbols used with the '%'
+command, but the previous solution is faster.
+~~~~~29|Dark grey things are difficult to see
+#####G------------------------------------------------------------------------------
+#####GQ: Many things are written in a dark grey color which is next to impossible to
+#####G read against a black background. Also, some monsters appear in dark grey and
+#####G are easy to miss! What can I do to fix this?
+
+A: Fix the gamma control of your display. If your display software does not
+include such a tool, access the 'Interact with Colors' screen in ToME via
+shift+7, type '4', and modify the gamma correction there.
+~~~~~30|Game 'balance'
+#####G------------------------------------------------------------------------------
+#####GQ: Why don't you make X class less powerful or Y class more powerful?
+
+A: In ToME the player determines how hard the game is. Classes, races, and
+subraces are neither meant nor desired to be equal in game difficulty. So no,
+we won't make Axemasters more powerful just to "balance them out", nor will we
+make Sorcerors weaker.
+~~~~~31|I keep dying!
+#####G------------------------------------------------------------------------------
+#####GQ: Why do I always start in a terribly difficult, very deep dungeon instead of
+#####G a town? Is the game really this hard?
+
+A: You have chosen a "Lost soul" character subrace. That's where Lost souls
+start. They tend to die very quickly, so don't choose them if you're new to
+ToME.
+~~~~~32|Invisible character
+#####G------------------------------------------------------------------------------
+#####GQ: My character is invisible. That's great, but how do I know where she is if
+#####G I can't see her?!
+
+A: You could seek for a way to see invisible things.
+You could also go to game options:
+ 1. Type = (game options)
+ 2. Type 4 (efficiency options)
+ 3. Arrow down to 'hilite the player with the cursor'
+ 4. Type y to toggle the option to 'yes'
+~~~~~33|Objects|Piles
+#####G------------------------------------------------------------------------------
+#####GQ: I'm standing on a pile of items. How do I see what's in the pile without
+#####G picking it all up, moving it, or destroying it all?
+A:
+ 1. Stand on the pile in question
+ 2. Type shift + I (inspect)
+ 3. Type - (examine items on floor)
+ 4. Type * (expand list of items on floor)
+ 5. (as needed) Type letter associated with item to look at it more closely.
+
+#####G------------------------------------------------------------------------------
+#####GQ: If I'm standing on a pile of items. Is there a command to see if there is a
+#####G stairway or jumpgate beneath the pile?
+
+A: Stairs/jumpgates obscured by clutter do still function. You are advised to
+take a good hard look at your surroundings before creating lots of dungeon
+clutter.
+ 1. You can pick up, move, or eliminate the pile.
+ 2. Press l (look), then select the square you wish to inquire about. Press
+<enter>; it will scroll through everything on the ground, and eventually it
+ends with "It is in a Void Jumpgate", or whatever.
+~~~~~34|Character choice is too confusing
+~~~~~36|Beginner strategy
+#####G------------------------------------------------------------------------------
+#####GQ: What is a good starting character?
+
+A: Make sure to read the parchment you start the game with!
+
+If you're new to ToME, understand that your characters are going to die a lot.
+Be prepared for that. In fact, take advantage of it by using the various
+characters you run to experiment and learn the game's various facets.
+
+Try a warrior. A Dunadan Swordmaster is an excellent combination of race and
+class.
+
+Try a priest. A Rohan Knight Paladin gives you some magic to go with strong
+combat, but your terrible stealth will give you a tougher time in some respects.
+
+Try an archer. A Wood-elf Archer will have fewer hitpoints than you're used to,
+but lets you use excellent ranged combat instead. Also note how your higher
+stealth wakes up fewer monsters, letting you fight them more on your own terms.
+
+Try a mage. A Dark-elf Mage lets you keep using weapons, while getting a taste
+of the various magic schools. You have even fewer hitpoints, though, so beware.
+
+Try another mage. A Hobbit Sorceror is a fun character, but the hitpoint
+penalties make you need to be very careful. You get high-powered magic, though,
+to more than make up for it.
+
+I would just add that for those who get frustrated in the early levels and want
+to run a more powerful character that I think the three easiest combinations
+are probably the Zombie Rohan Knight Unbeliever, the Thunderlord (or Vampire
+Half-Ogre) Sorceror, and the High-Elf (or Deathmold if you can figure it out)
+Possessor. Also, you might want to try a priest of Eru or Tulkas. Most of these
+have low stealth, but should be pretty easy to play up to around level 30, and
+they offer an attractive range of experiences for the new player.
+~~~~~35|I STILL keep dying!
+#####G------------------------------------------------------------------------------
+#####GQ: I'm getting killed a lot. Can you recommend some starting options to
+#####G make my chances a little better?
+
+A: Realize that getting killed a lot is to be expected. Having said that, try
+this. At character creation:
+ 1. Turn off "always generate very unusual rooms".
+ 2. Turn off joke monsters.
+ 3. Turn off "always make small levels".
+ 4. Regarding the number of random quests: See the Q/A below.
+ 5. Do not choose a Lost Soul character.
+
+Later, set these options:
+ 1. Turn on "expand the power of the look command".
+ 2. Turn on "allow some monsters to carry light".
+ 3. Turn on "map remembers all perma-lit grids".
+ 4. Turn on "map remembers all torch-lit grids".
+ 5. Turn off "monsters learn from their mistakes".
+ 6. Turn off "monsters exploit player weaknesses".
+ 7. Turn on "monsters behave stupidly".
+ 8. Turn off "allow unusually small dungeon levels".
+ 9. Turn off "allow empty 'arena' levels".
+
+~~~~~36|Random quests strategy
+#####G------------------------------------------------------------------------------
+#####GQ: How many random quests should I choose?
+
+A: One big question a beginner is faced with is: How many (random) optional
+quests to choose?
+
+I think this is another area where the beginner should mix it up. The early
+items from princesses are a great benefit to beginners, but coming to rely on
+those can be a problem when it comes time to enter deep dungeons and the real
+nasty quests begin.
+
+Also high counts, especially 98 quests, can be very frustrating for a beginner
+when it puts an especially difficult quest on dungeon level 1 or 2.
+
+~~~~~37|Anti-magic Amulets and the Anti-magic shell
+#####G------------------------------------------------------------------------------
+#####GQ: Are Amulets of Anti-magic and the Anti-magic skill related?
+
+A: No. The Anti-magic shell of the Amulet of Anti-magic has nothing to do with
+the Anti-magic field given off by the skill and Dark Swords.
+
+~~~~~38|Beornings and Bearform-combat
+#####G------------------------------------------------------------------------------
+#####GQ: My Beorning character doesn't have Bearform-combat! Why?
+
+A: You cannot put points into Bearform-combat unless you are transformed
+into a bear. Use the racial power ('U' in the original keyset, 'O' in
+roguelike) to transform first.
+
+#####G------------------------------------------------------------------------------
+#####GQ: The game is so slow...
+
+A: Yeah :(
+Try disabling the various options marked as (slow)
diff --git a/lib/help/version.txt b/lib/help/version.txt
new file mode 100644
index 00000000..501be7f6
--- /dev/null
+++ b/lib/help/version.txt
@@ -0,0 +1,354 @@
+|||||oy
+~~~~~01|Development history
+*****version.txt*01[The origins of ToME]
+*****version.txt*02[Zangband History and Information]
+*****version.txt*03[Brief Version History (of standard Angband)]
+*****version.txt*04[A Posting from the Original Author (of Moria)]
+*****version.txt*05[Previous Versions (outdated)]
+
+
+#####R====== ToME Brief History =======
+
+When Zangband came to its 2.2.0 version I (DarkGod) was an Angband winner and
+I had been a C programmer for a long time, so I decided to take the sources
+and to try to code my own variant. At this time I was reading the Pern
+novels from Anne McCaffrey and I found them *VERY* good, so I decided to
+include some elements of them into my variant from which it takes the name,
+PernAngband.
+
+One hard thing to decide was on which Angband to base it. Although I didn't
+like Zangband because of the Zelazny universe, which I found to be not very
+Tolkienish, I chose it because of all the good things it had (especially
+the race powers that I wasn't able to code at the time). So I removed
+much of the Zelazny stuff and replaced it with Tolkien and Pernish stuff.
+And so the history of PernAngband began with the version 2.9.9a.
+
+Now, in PernAngband 5.x.x, PernAngband is a thriving Angband variant
+with plenty of unique features.
+
+Then came some legal problems with Anne McCaffrey estate and ubisoft and
+I had to remove the Pern stuff, so the game got renamed to ToME,
+the Troubles of Middle Earth.
+~~~~~02
+#####R=== Zangband History and Information ===
+
+The seeds of Zangband lie in an obsolete and long ago vanished PC variant
+(somewhat misleadingly) dubbed Angband--. The variant was written by a
+hopeless Angband addict (previously Moria veteran and winner) who got
+bored with the standard monsters and wanted to introduce some new
+monsters. Angband-- was based on the PC Angband 1.31 sources, and
+it was set in Roger Zelazny's 'Amber' universe.
+
+Later this individual got a better computer and learned to code, and
+produced the PC Zangband, and most Angband-- monsters survived into
+PC Zangband 1.0. PC Zangband 1.0 was the first PC Angband to introduce
+(simple, font-based) graphics, which were also used in the graphical
+PC Angband 1.40.
+
+Yet this individual was still not cured of his addiction... his almost
+as strong addiction to the Civilization style fantasy strategy game
+'Master of Magic' inspired him to write a new magic system. The current
+version of Zangband (2.*) incorporates this magic system, as well as
+the best features from Angband-- and PC Zangband 1.0. It is based on
+the Angband 2.8.1 sources (by Ben Harrison), and is therefore portable
+to other systems (unlike the earlier versions which were for DOS-PC's
+only).
+
+Incidentally, this person (me, Topi Ylinen) also thought that the
+standard Angband monsters were too easy, which led him to introduce
+such monsters as Death swords, Cyberdemons and Great Wyrms of Power...
+
+Special thanks -- The current version of Zangband might not have come into
+existence without the significant help from these excellent Angband
+programmers:
+
+ Ben Harrison, for obvious reasons.
+
+ Greg Wooledge, who pointed out a bug in the dos compiler,
+ which was preventing my progress with the first 2.* version
+ of Zangband and for various patches.
+
+ Julian Lighton, who must have sent me more ideas, patches, and
+ bug reports than all the others together.
+
+ Robert Ruehlmann, whose nice new main-dos.c enables SVGA
+ graphics and even windows in MS-DOS.
+
+ Paul Sexton, who is responsible for about 50% of the new code
+ in 2.1.0.
+
+ Heino Vander Sanden, who created the quest-code and
+ Dean Anderson, whose patch showed me the quickest way to
+ implement the quests.
+
+ Adam Bolt, who created the new ZAngband tiles.
+
+ Scott Bigham, for the S-Lang patch.
+
+ Jeff Duprey for the new mutations.
+
+ Leigh Silas Hanrihan for the new items.
+
+ Benny S. Hofmann, Aram Harrow, Greg Harvey, Keldon Jones,
+ Graham Murray, Remco Gerlich, Tim Baker and many others
+ for bugreports, patches, bugfixes, and ideas.
+
+
+ZAngband 2.1.0c was Topi's last version, he has got a job and
+doesn't have enough time anymore to continue work on ZAngband.
+He asked for a new maintainer and I was the one to take over the task.
+May I introduce myself, my name is Robert Ruehlmann, I'm the creator
+of the graphical Angband versions for DOS and webmaster of
+"Thangorodrim - The Angband Page" ("http://www.thangorodrim.net").
+~~~~~03
+#####R=== Brief Version History (of standard Angband) ===
+
+First came "VMS Moria", by Robert Alan Koeneke (1985).
+
+Then came "Umoria" (Unix Moria), by James E. Wilson (1989).
+
+In 1990, Alex Cutler and Andy Astrand, with the help of other students
+at the University of Warwick, created Angband 1.0, based on the existing
+code for Umoria 5.2.1. They wanted to expand the game, keeping or even
+strengthening the grounding in Tolkien lore, while adding more monsters
+and items, including unique monsters and artifact items, plus activation,
+pseudo-sensing, level feelings, and special dungeon rooms.
+
+Over time, Sean Marsh, Geoff Hill, Charles Teague, and others, worked on
+the source, releasing a copy known as "Angband 2.4.frog_knows" at some
+point, which ran only on Unix systems, but which was ported by various
+people to various other systems.
+
+Then Charles Swiger (cs4w+@andrew.cmu.edu) attempted to clean up the mess,
+resulting in several versions, starting sometime around November, 1993, with
+Angband 2.5.1 (more or less) and leading up to Angband 2.6.2 in late 1994.
+Several people ported (the primarily Unix/NeXT centered) Angband 2.6.1 to
+other platforms, including Keith Randall, who made a Macintosh port that
+added support for color usage. Some of the changes during this period were
+based on suggestions from the "net", PC Angband 1.40, UMoria 5.5, and some
+of the Angband "variations", such as FAngband.
+
+Finally, I (Ben Harrison) took over in late 1994 when Charles Swiger left.
+Initially my intention was simply to clean up what had become, after ten
+years, a rather unholy mess, but the deeper I delved into the code, the
+more it became apparent that drastic changes were needed, so, starting
+with MacAngband 2.6.1, I began a more or less total rewrite, resulting,
+eventually, in Angband 2.7.0, released around January first, 1995.
+
+Angband 2.7.0 was a very clean (but very buggy) rewrite that, among other
+things, allowed extremely simple porting to multiple platforms, starting
+with Unix and Macintosh, and by the time most of the bugs were cleaned up,
+in Angband 2.7.2, including X11, and various IBM machines. Angband 2.7.4
+was released to the "ftp.cis.ksu.edu" site, and quickly gained acceptance,
+perhaps helped by the OS2 and Windows and Amiga and Linux ports. Angband
+2.7.5 and 2.7.6 added important capabilities such as macros and user pref
+files, and continued to clean up the source. Angband 2.7.8 was designed
+to supply another "stable" version that we can all give to our friends,
+with new "help files" and "spoiler files" for the "online help", plus a
+variety of minor tweaks and some new features. Angband 2.7.9 optimized
+a few things, and tweaked a few other things, and cleaned up a few other
+things, and introduced a few minor semantic changes.
+
+It is very hard to pin down, along the way from 2.6.2 to 2.7.0, and thence
+to 2.7.8, exactly what was added exactly when. Most of these steps involved
+so many changes as to make "diff files" not very useful, since often the diff
+files were as long as the code itself. Most of the changes, with the notable
+exception of the creation of the new "main-xxx.c" files for the various new
+platforms, and a few other exceptions generally noted in the source, were
+written by myself, either spontaneously, or, more commonly, as the result of
+a suggestion or comment by an Angband player. So if you have any problems
+with anything that you do not recognize from older versions, you can blame
+them on me. And if you like the new features and such, you can send me a
+brief little "thank you" email (to benh@phial.com) or something...
+
+The Official Angband Home Page ("http://www.phial.com/")
+was created along with Angband 2.7.9 to serve as an up to date description
+of any bugs found in various versions, and to list all of the people whose
+email addresses I kept having to look up.
+
+~~~~~04
+#####R=== A Posting from the Original Author ===
+
+From: koeneke@ionet.net (Robert Alan Koeneke)
+Newsgroups: rec.games.roguelike.angband,rec.games.roguelike.moria
+Subject: Early history of Moria
+Date: Wed, 21 Feb 1996 04:20:51 GMT
+
+I had some email show up asking about the origin of Moria, and its
+relation to Rogue. So I thought I would just post some text on the
+early days of Moria.
+
+First of all, yes, I really am the Robert Koeneke who wrote the first
+Moria. I had a lot of mail accussing me of pulling their leg and
+such. I just recently connected to Internet (yes, I work for a
+company in the dark ages where Internet is concerned) and
+was real surprised to find Moria in the news groups... Angband was an
+even bigger surprise, since I have never seen it. I probably spoke to
+its originator though... I have given permission to lots of people
+through the years to enhance, modify, or whatever as long as they
+freely distributed the results. I have always been a proponent of
+sharing games, not selling them.
+
+Anyway...
+
+Around 1980 or 81 I was enrolled in engineering courses at the
+University of Oklahoma. The engineering lab ran on a PDP 1170 under
+an early version of UNIX. I was always good at computers, so it was
+natural for me to get to know the system administrators. They invited
+me one night to stay and play some games, an early startrek game, The
+Colossal Cave Adventure (later just 'Adventure'), and late one night,
+a new dungeon game called 'Rogue'.
+
+So yes, I was exposed to Rogue before Moria was even a gleam in my
+eye. In fact, Rogue was directly responsible for millions of hours of
+play time wasted on Moria and its descendents...
+
+Soon after playing Rogue (and man, was I HOOKED), I got a job in a
+different department as a student assistant in computers. I worked on
+one of the early VAX 11/780's running VMS, and no games were available
+for it at that time. The engineering lab got a real geek of an
+administrator who thought the only purpose of a computer was WORK!
+Imagine... Soooo, no more games, and no more rogue!
+
+This was intolerable! So I decided to write my own rogue game, Moria
+Beta 1.0. I had three languages available on my VMS system. Fortran
+IV, PASCAL V1.?, and BASIC. Since most of the game was string
+manipulation, I wrote the first attempt at Moria in VMS BASIC, and it
+looked a LOT like Rogue, at least what I could remember of it. Then I
+began getting ideas of how to improve it, how it should work
+differently, and I pretty much didn't touch it for about a year.
+
+Around 1983, two things happened that caused Moria to be born in its
+recognizable form. I was engaged to be married, and the only cure for
+THAT is to work so hard you can't think about it; and I was enrolled
+for fall to take an operating systems class in PASCAL.
+
+So, I investigated the new version of VMS PASCAL and found out it had
+a new feature. Variable length strings! Wow...
+
+That summer I finished Moria 1.0 in VMS PASCAL. I learned more about
+data structures, optimization, and just plain programming that summer
+then in all of my years in school. I soon drew a crowd of devoted
+Moria players... All at OU.
+
+I asked Jimmey Todd, a good friend of mine, to write a better
+character generator for the game, and so the skills and history were
+born. Jimmey helped out on many of the functions in the game as well.
+This would have been about Moria 2.0
+
+In the following two years, I listened a lot to my players and kept
+making enhancements to the game to fix problems, to challenge them,
+and to keep them going. If anyone managed to win, I immediately found
+out how, and 'enhanced' the game to make it harder. I once vowed it
+was 'unbeatable', and a week later a friend of mine beat it! His
+character, 'Iggy', was placed into the game as 'The Evil Iggy', and
+immortalized... And of course, I went in and plugged up the trick he
+used to win...
+
+Around 1985 I started sending out source to other universities. Just
+before a OU / Texas football clash, I was asked to send a copy to the
+Univeristy of Texas... I couldn't resist... I modified it so that
+the begger on the town level was 'An OU football fan' and they moved
+at maximum rate. They also multiplied at maximum rate... So the
+first step you took and woke one up, it crossed the floor increasing
+to hundreds of them and pounded you into oblivion... I soon received
+a call and provided instructions on how to 'de-enhance' the game!
+
+Around 1986 - 87 I released Moria 4.7, my last official release. I
+was working on a Moria 5.0 when I left OU to go to work for American
+Airlines (and yes, I still work there). Moria 5.0 was a complete
+rewrite, and contained many neat enhancements, features, you name it.
+It had water, streams, lakes, pools, with water monsters. It had
+'mysterious orbs' which could be carried like torches for light but
+also gave off magical aura's (like protection from fire, or aggrivate
+monster...). It had new weapons and treasures... I left it with the
+student assistants at OU to be finished, but I guess it soon died on
+the vine. As far as I know, that source was lost...
+
+I gave permission to anyone who asked to work on the game. Several
+people asked if they could convert it to 'C', and I said fine as long
+as a complete credit history was maintained, and that it could NEVER
+be sold, only given. So I guess one or more of them succeeded in
+their efforts to rewrite it in 'C'.
+
+I have since received thousands of letters from all over the world
+from players telling about their exploits, and from administrators
+cursing the day I was born... I received mail from behind the iron
+curtain (while it was still standing) talking about the game on VAX's
+(which supposedly couldn't be there due to export laws). I used to
+have a map with pins for every letter I received, but I gave up on
+that!
+
+I am very happy to learn my creation keeps on going... I plan to
+download it and Angband and play them... Maybe something has been
+added that will surprise me! That would be nice... I never got to
+play Moria and be surprised...
+
+Robert Alan Koeneke
+koeneke@ionet.net
+
+~~~~~05
+#####R=== Previous Versions (outdated) ===
+
+
+ VMS Moria Version 4.8
+Version 0.1 : 03/25/83
+Version 1.0 : 05/01/84
+Version 2.0 : 07/10/84
+Version 3.0 : 11/20/84
+Version 4.0 : 01/20/85
+
+Modules :
+ V1.0 Dungeon Generator - RAK
+ Character Generator - RAK & JWT
+ Moria Module - RAK
+ Miscellaneous - RAK & JWT
+ V2.0 Town Level & Misc - RAK
+ V3.0 Internal Help & Misc - RAK
+ V4.0 Source Release Version - RAK
+
+Robert Alan Koeneke Jimmey Wayne Todd Jr.
+Student/University of Oklahoma Student/University of Oklahoma
+
+
+ Umoria Version 5.2 (formerly UNIX Moria)
+Version 4.83 : 5/14/87
+Version 4.85 : 10/26/87
+Version 4.87 : 5/27/88
+Version 5.0 : 11/2/89
+Version 5.2 : 5/9/90
+
+James E. Wilson, U.C. Berkeley
+ wilson@ernie.Berkeley.EDU
+ ...!ucbvax!ucbernie!wilson
+
+Other contributors:
+D. G. Kneller - MS-DOS Moria port
+Christopher J. Stuart - recall, options, inventory, and running code
+Curtis McCauley - Macintosh Moria port
+Stephen A. Jacobs - Atari ST Moria port
+William Setzer - object naming code
+David J. Grabiner - numerous bug reports, and consistency checking
+Dan Bernstein - UNIX hangup signal fix, many bug fixes
+and many others...
+
+
+
+
+Copyright (c) 1989 James E. Wilson, Robert A. Keoneke
+ This software may be copied and distributed for educational, research, and
+ not for profit purposes provided that this copyright and statement are
+ included in all such copies.
+
+Umoria Version 5.2, patch level 1
+
+Angband Version 2.0 Alex Cutler, Andy Astrand, Sean Marsh, Geoff Hill,
+ Charles Teague.
+
+Angband Version 2.4 : 5/09/93
+
+Angband Version 2.5 : 12/05/93 Charles Swiger.
+
+Angband Version 2.6 : 9/04/94
+
+Angband Version 2.7 : 1/1/95 Ben Harrison
diff --git a/lib/help/whattome.txt b/lib/help/whattome.txt
new file mode 100644
index 00000000..43ebb2e1
--- /dev/null
+++ b/lib/help/whattome.txt
@@ -0,0 +1,30 @@
+|||||oy
+~~~~~01|ToME - a General Description
+#####R /----------------------------------------\
+#####R < What is ToME? >
+#####R \----------------------------------------/
+
+Tales of Middle Earth (ToME) is a fantasy adventure game, based on the works
+of Tolkien. Focusing on game-play rather than fancy graphics that get boring
+after a week, ToME will keep you playing for years.
+
+Explore dozens of different dungeons including hundreds of randomly generated
+levels filled with multitudes of different items and treasures. Fight off
+hundreds of monsters and uniques from the stories in a complex fighting system.
+Gain experience and learn skills; choose from the dozens of races and classes
+available to the player; cast spells from simple teleportation spells to
+advanced spells that can wipe out a whole army at once. It's the only game where
+you can burn spell books by trudging in lava (unless you have gained immunity
+from some armour), dry up rivers to cast mighty spells, strike at orcs with
+blades attuned to slay them specifically, summon armies from a simple totem,
+and even enter a symbiotic relationship with a mold!
+
+Explore dungeons, gain power, and save Middle-earth!
+
+The player will begin his adventure on the town level where he may acquire
+supplies, weapons, armour, and magical devices by bartering with various shop
+owners. After preparing for his adventure, the player can descend into the
+dungeon near Bree where fantastic adventures await his coming!
+
+Make sure you read the parchment you are given when you start the game, and
+read the in-game documentation.
diff --git a/lib/help/wishing.txt b/lib/help/wishing.txt
new file mode 100644
index 00000000..d16f5ae9
--- /dev/null
+++ b/lib/help/wishing.txt
@@ -0,0 +1,24 @@
+~~~~~01|Spoilers|Wishing
+#####R=== Wishes ===
+
+Some items in ToME will grant the user the ability to "wish" for an
+object that they are interested in. As such, these are generally very rare
+and very powerful objects.
+
+#####GRules for Wishes
+Due to the powerful nature of wishes, there are some rules that govern
+what is able to be wished for. These rules are as follows:
+1. You cannot wish for a wish, or any other item which would grant more
+ wishes.
+2. A wish will always generate *one* object. So, never put a number, "a"
+ or "an" in front of the object you are wishing for.
+3. It is not possible to wish for the magical +'s to the object (i.e. you
+ cannot wish for "gloves of slaying (+10,+10)", but you can wish for
+ "gloves of slaying").
+4. You cannot wish for artifacts, but you *can* wish for excellent (ego)
+ items.
+5. You can wish for monsters and ego monsters (e.g. "cave orc", "rogue cave
+ orc").
+6. You cannot wish for unique monsters.
+7. You can wish the monster to have a specific flag - a pet, or a foe.
+ Possible flags include: enemy, neutral, friendly, pet, companion.
diff --git a/lib/info/delete.me b/lib/info/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/info/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/mods/mods_aux.lua b/lib/mods/mods_aux.lua
new file mode 100644
index 00000000..1562a566
--- /dev/null
+++ b/lib/mods/mods_aux.lua
@@ -0,0 +1,185 @@
+-- Ok some functions that we dont need are dangerous
+--[[
+execute = nil
+getenv = nil
+setlocale = nil
+exit = nil
+openfile = nil
+writeto = nil
+readfrom = nil
+appendto = nil
+remove = nil
+rename = nil
+tmpname = nil
+]]
+modules = {}
+
+current_module = nil
+
+function setup_module(mod)
+ -- For standart game, nothing needs to be done
+ if not mod.layout then return end
+
+ for k, e in mod.layout do
+ module_reset_dir(k, e)
+ end
+end
+
+function init_module(i)
+ setup_module(get_module(i))
+end
+
+function max_modules()
+ local i = 0
+ for k, e in modules do
+ if type(k) == "number" and type(e) == "table" then
+ i = i + 1
+ end
+ end
+ return i
+end
+
+function get_module_name(j)
+ local i = 0
+ for k, e in modules do
+ if type(k) == "number" and type(e) == "table" then
+ if i == j then return e.name end
+ i = i + 1
+ end
+ end
+end
+
+function get_module_desc(j)
+ local i = 0
+ for k, e in modules do
+ if type(k) == "number" and type(e) == "table" then
+ if i == j then return e.desc end
+ i = i + 1
+ end
+ end
+end
+
+function get_module(j)
+ local i = 0
+ for k, e in modules do
+ if type(k) == "number" and type(e) == "table" then
+ if i == j then return e end
+ i = i + 1
+ end
+ end
+end
+
+function find_module(name)
+ local i = 0
+ for k, e in modules do
+ if type(k) == "number" and type(e) == "table" then
+ if name == e.name then return i end
+ i = i + 1
+ end
+ end
+end
+
+function assign_current_module(name)
+ current_module = get_module(find_module(name))
+end
+
+function get_module_info(type, subtype)
+ if subtype then
+ return current_module[type][subtype]
+ else
+ return current_module[type]
+ end
+end
+
+function exec_module_info(type, ...)
+ return call(current_module[type], arg)
+end
+
+function module_savefile_loadable(savefile_mod, savefile_death)
+ for _, e in current_module.mod_savefiles do
+ if e[1] == savefile_mod then
+ if e[2] == "all" then
+ return TRUE
+ elseif e[2] == "alive" and savefile_death == FALSE then
+ return TRUE
+ elseif e[2] == "dead" and savefile_death == TRUE then
+ return TRUE
+ end
+ end
+ end
+ return FALSE
+end
+
+function scan_extra_modules()
+ scansubdir(ANGBAND_DIR_MODULES)
+ for i = 0, scansubdir_max - 1 do
+ if (scansubdir_result[i + 1] ~= ".") and (scansubdir_result[i + 1] ~= "..") then
+ local dir = path_build(ANGBAND_DIR_MODULES, scansubdir_result[i + 1])
+ local file = path_build(dir, "module.lua")
+ if file_exist(file) == TRUE then
+ tome_dofile_anywhere(dir, "module.lua")
+ end
+ end
+ end
+end
+
+function add_module(t)
+ assert(t.name, "No module name")
+ assert(type(t.version) == "table", "No module version")
+ assert(t.desc, "No module desc")
+ assert(t.author, "No module author")
+ assert(t.mod_savefiles, "No loadable savefiles module mark")
+
+ for _, e in modules do
+ if type(e) == "table" and e.name == t.name then
+ error("Module name already defined: "..t.name)
+ end
+ end
+
+ if type(t.author) == "string" then
+ t.author = { t.author, "unknown@unknown.net" }
+ end
+
+ for k, e in t.mod_savefiles do
+ if type(e) == "string" then t.mod_savefiles[k] = { e, "all" } end
+ end
+
+ if type(t.desc) == "table" then
+ local d = ""
+ for k, e in t.desc do
+ d = d .. e
+ if k < getn(t.desc) then
+ d = d .. "\n"
+ end
+ end
+ t.desc = d
+ end
+
+ if not t.rand_quest then t.rand_quest = FALSE end
+ if not t.C_quest then t.C_quest = FALSE end
+
+ if not t.base_dungeon then t.base_dungeon = 4 end
+ if not t.death_dungeon then t.death_dungeon = 28 end
+
+ if not t.astral_dungeon then t.astral_dungeon = 8 end
+ if not t.astral_wild_x then t.astral_wild_x = 45 end
+ if not t.astral_wild_y then t.astral_wild_y = 19 end
+
+ if not t.random_artifact_weapon_chance then
+ t.random_artifact_weapon_chance = 30
+ end
+ if not t.random_artifact_armor_chance then
+ t.random_artifact_armor_chance = 20
+ end
+ if not t.random_artifact_jewelry_chance then
+ t.random_artifact_jewelry_chance = 20
+ end
+
+ if not t.max_plev then t.max_plev = 50 end
+ if not t.max_skill_overage then t.max_skill_overage = 4 end
+ if not t.skill_per_level then t.skill_per_level = function() return 6 end end
+
+ if not t.allow_birth then t.allow_birth = TRUE end
+
+ tinsert(modules, t)
+end
diff --git a/lib/mods/modules.lua b/lib/mods/modules.lua
new file mode 100644
index 00000000..5deddef7
--- /dev/null
+++ b/lib/mods/modules.lua
@@ -0,0 +1,5 @@
+-- Load ToME
+tome_dofile_anywhere(ANGBAND_DIR, "module.lua")
+
+-- Look for more modules
+scan_extra_modules()
diff --git a/lib/module.lua b/lib/module.lua
new file mode 100644
index 00000000..895d79e1
--- /dev/null
+++ b/lib/module.lua
@@ -0,0 +1,36 @@
+add_module
+{
+ ["name"] = "ToME",
+ ["version"] = { 2, 3, 5 },
+ ["author"] = { "DarkGod", "darkgod@t-o-m-e.net" },
+ ["desc"] = {
+ "The Tales of Middle-earth, the standard and official game.",
+ "You are set on a quest to investigate the old tower of Dol Guldur.",
+ "But who knows what will happen...",
+ },
+
+ ["rand_quest"] = TRUE,
+ ["C_quest"] = TRUE,
+
+ ["base_dungeon"] = 4,
+ ["death_dungeon"] = 28,
+
+ ["astral_dungeon"] = 8,
+ ["astral_wild_x"] = 45,
+ ["astral_wild_y"] = 19,
+
+ ["random_artifact_weapon_chance"] = 30,
+ ["random_artifact_armor_chance"] = 20,
+ ["random_artifact_jewelry_chance"] = 20,
+
+ ["max_plev"] = 50,
+ ["max_skill_overage"] = 4,
+ ["skill_per_level"] = function()
+ return 6
+ end,
+
+ ["mod_savefiles"]=
+ {
+ "ToME",
+ },
+}
diff --git a/lib/note/delete.me b/lib/note/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/note/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/patch/delete.me b/lib/patch/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/patch/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/pref/422color.prf b/lib/pref/422color.prf
new file mode 100644
index 00000000..309c36b0
--- /dev/null
+++ b/lib/pref/422color.prf
@@ -0,0 +1,909 @@
+# Agent of the black market
+R:14:0x08:0x74
+
+# Mean-looking mercenary
+R:17:0x0c:0x74
+
+# Battle-scarred veteran
+R:18:0x04:0x74
+
+# Small kobold
+R:29:0x0d:0x6b
+
+# Kobold
+R:30:0x05:0x6b
+
+# Floating eye
+R:32:0x06:0x65
+
+# Fruit bat
+R:37:0x0a:0x62
+
+# Blubbering icky thing
+R:41:0x05:0x69
+
+# Metallic green centipede
+R:42:0x0d:0x63
+
+# Novice warrior
+R:43:0x0f:0x70
+
+# Novice rogue
+R:44:0x08:0x70
+
+# Novice priest
+R:45:0x0e:0x70
+
+# Novice mage
+R:46:0x0c:0x70
+
+# Smeagol
+R:63:0x07:0x68
+
+# Metallic blue centipede
+R:67:0x0e:0x63
+
+# Metallic red centipede
+R:77:0x0c:0x63
+
+# Novice ranger
+R:83:0x05:0x70
+
+# Snotling
+R:87:0x0d:0x6f
+
+# Skeleton kobold
+R:91:0x01:0x73
+
+# Novice mage
+R:93:0x0c:0x70
+
+# Giant leech
+R:95:0x0a:0x77
+
+# Large kobold
+R:102:0x01:0x6b
+
+# Novice priest
+R:109:0x0e:0x70
+
+# Novice warrior
+R:110:0x0f:0x70
+
+# Novice rogue
+R:116:0x08:0x70
+
+# Snaga
+R:118:0x0d:0x6f
+
+# Giant pink frog
+R:121:0x0c:0x52
+
+# Cave orc
+R:126:0x0f:0x6f
+
+# Manes
+R:128:0x0c:0x75
+
+# Red naga
+R:130:0x0c:0x6e
+
+# Red jelly
+R:131:0x0c:0x6a
+
+# Green icky thing
+R:132:0x0d:0x69
+
+# Lost soul
+R:133:0x07:0x47
+
+# Mughash, the Kobold Lord
+R:135:0x06:0x6b
+
+# Skeleton orc
+R:136:0x01:0x73
+
+# Wormtongue, Agent of Saruman
+R:137:0x08:0x70
+
+# Nurgling
+R:139:0x0f:0x75
+
+# Lagduf, the Snaga
+R:140:0x07:0x6f
+
+# Novice ranger
+R:142:0x05:0x70
+
+# Giant salamander
+R:143:0x04:0x52
+
+# Lemure
+R:148:0x03:0x75
+
+# Hill orc
+R:149:0x02:0x6f
+
+# Bandit
+R:150:0x08:0x70
+
+# Orc shaman
+R:162:0x06:0x6f
+
+# Baby blue dragon
+R:163:0x0e:0x64
+
+# Baby white dragon
+R:164:0x09:0x64
+
+# Baby green dragon
+R:165:0x0d:0x64
+
+# Baby red dragon
+R:167:0x0c:0x64
+
+# Giant red ant
+R:168:0x0c:0x61
+
+# Brodda, the Easterling
+R:169:0x03:0x70
+
+# Giant spider
+R:175:0x02:0x53
+
+# Dark elven mage
+R:178:0x0a:0x68
+
+# Kamikaze yeek
+R:179:0x07:0x79
+
+# Dark elven warrior
+R:182:0x08:0x68
+
+# Sand-dweller
+R:183:0x0f:0x75
+
+# Clear mushroom patch
+R:184:0x01:0x2c
+
+# Grishnakh, the Hill Orc
+R:186:0x07:0x6f
+
+# Hairy mold
+R:190:0x0f:0x6d
+
+# Grizzly bear
+R:191:0x07:0x71
+
+# Pseudo dragon
+R:193:0x0a:0x64
+
+# Giant fruit fly
+R:197:0x0f:0x49
+
+# Golfimbul, the Hill Orc Chief
+R:215:0x07:0x6f
+
+# Swordsman
+R:216:0x0f:0x70
+
+# Priest
+R:225:0x0e:0x70
+
+# Dark elven priest
+R:226:0x06:0x68
+
+# Skeleton human
+R:228:0x01:0x73
+
+# Moaning spirit
+R:231:0x0f:0x47
+
+# Stegocentipede
+R:232:0x06:0x63
+
+# Spotted jelly
+R:233:0x02:0x6a
+
+# Illusionist
+R:240:0x0a:0x70
+
+# Druid
+R:241:0x05:0x70
+
+# Ochre jelly
+R:245:0x0b:0x6a
+
+# Vlasta
+R:249:0x06:0x52
+
+# Snaga sapper
+R:251:0x0d:0x6f
+
+# Flesh golem
+R:256:0x03:0x67
+
+# Dark naga
+R:265:0x08:0x6e
+
+# Giant tarantula
+R:275:0x0d:0x53
+
+# Giant clear centipede
+R:276:0x01:0x63
+
+# Mirkwood spider
+R:277:0x07:0x53
+
+# Umber hulk
+R:283:0x07:0x58
+
+# Orc captain
+R:285:0x05:0x6f
+
+# Gelatinous cube
+R:286:0x0e:0x6a
+
+# Lizard man
+R:290:0x0c:0x68
+
+# Ulfast, Son of Ulfang
+R:291:0x03:0x70
+
+# Quasit
+R:294:0x09:0x75
+
+# Imp
+R:296:0x04:0x75
+
+# Forest troll
+R:297:0x0d:0x54
+
+# Giant red scorpion
+R:304:0x0c:0x53
+
+# Earth spirit
+R:305:0x0f:0x45
+
+# Fire spirit
+R:306:0x0c:0x45
+
+# Cold hound
+R:308:0x09:0x5a
+
+# Energy hound
+R:309:0x0b:0x5a
+
+# Uruk
+R:313:0x01:0x6f
+
+# Shagrat, the Orc Captain
+R:314:0x07:0x6f
+
+# Gorbag, the Orc Captain
+R:315:0x07:0x6f
+
+# Stone giant
+R:321:0x02:0x50
+
+# Giant black dragon fly
+R:322:0x08:0x46
+
+# Stone golem
+R:323:0x02:0x67
+
+# Ghast
+R:327:0x0f:0x7a
+
+# Bolg, Son of Azog
+R:330:0x0c:0x6f
+
+# Lizard king
+R:332:0x0c:0x68
+
+# Wyvern
+R:334:0x0d:0x64
+
+# Air hound
+R:338:0x0e:0x5a
+
+# Quylthulg
+R:342:0x09:0x51
+
+# Dark elven lord
+R:348:0x08:0x68
+
+# Cloud giant
+R:349:0x0e:0x50
+
+# Ugluk, the Uruk
+R:350:0x07:0x6f
+
+# Blue dragon bat
+R:351:0x0e:0x62
+
+# Water vortex
+R:355:0x06:0x76
+
+# Cold vortex
+R:358:0x09:0x76
+
+# Energy vortex
+R:359:0x0b:0x76
+
+# Mummified orc
+R:362:0x07:0x7a
+
+# Serpent man
+R:364:0x05:0x4a
+
+# Killer stag beetle
+R:366:0x02:0x4b
+
+# Iron golem
+R:367:0x09:0x67
+
+# Azog, King of the Uruk-Hai
+R:373:0x04:0x6f
+
+# Master rogue
+R:376:0x08:0x70
+
+# Angamaite of Umbar
+R:380:0x07:0x70
+
+# Forest wight
+R:381:0x0d:0x57
+
+# 4-headed hydra
+R:387:0x0f:0x4d
+
+# Mummified human
+R:390:0x0f:0x7a
+
+# Sangahyando of Umbar
+R:392:0x03:0x70
+
+# Banshee
+R:394:0x09:0x47
+
+# Pukelman
+R:398:0x07:0x67
+
+# Disenchanter beast
+R:399:0x01:0x71
+
+# Stone troll
+R:401:0x08:0x54
+
+# Troll priest
+R:403:0x06:0x54
+
+# Khufu, the Mummified King
+R:409:0x07:0x7a
+
+# Beast of Nurgle
+R:422:0x03:0x71
+
+# Creeping adamantite coins
+R:423:0x0a:0x24
+
+# Algroth
+R:424:0x02:0x54
+
+# Flamer of Tzeentch
+R:425:0x0c:0x2c
+
+# Vibration hound
+R:428:0x0f:0x5a
+
+# Ogre mage
+R:430:0x03:0x4f
+
+# Lokkak, the Ogre Chieftain
+R:431:0x05:0x4f
+
+# Colbran
+R:435:0x06:0x67
+
+# Spirit naga
+R:436:0x0e:0x6e
+
+# Corpser
+R:437:0x05:0x2c
+
+# Black knight
+R:442:0x08:0x70
+
+# Clairvoyant
+R:445:0x0e:0x70
+
+# Basilisk
+R:453:0x08:0x52
+
+# Aklash
+R:463:0x09:0x54
+
+# Skeleton troll
+R:465:0x01:0x73
+
+# Shadow drake
+R:471:0x08:0x64
+
+# Manticore
+R:472:0x03:0x48
+
+# Killer slicer beetle
+R:474:0x03:0x4b
+
+# Gug
+R:476:0x08:0x50
+
+# Death watch beetle
+R:478:0x0e:0x4b
+
+# Doombat
+R:484:0x06:0x62
+
+# Ninja
+R:485:0x08:0x70
+
+# Storm giant
+R:487:0x06:0x50
+
+# Bokrug
+R:489:0x05:0x52
+
+# Half-troll
+R:491:0x03:0x54
+
+# Bert the Stone Troll
+R:493:0x09:0x54
+
+# Bill the Stone Troll
+R:494:0x09:0x54
+
+# Tom the Stone Troll
+R:495:0x09:0x54
+
+# Barrow wight
+R:499:0x09:0x57
+
+# Law drake
+R:502:0x09:0x64
+
+# Balance drake
+R:503:0x02:0x64
+
+# Groo, the Wanderer
+R:505:0x03:0x70
+
+# Spectre
+R:508:0x0d:0x47
+
+# Cherub
+R:511:0x09:0x41
+
+# Master thief
+R:516:0x08:0x70
+
+# Lich
+R:518:0x01:0x4c
+
+# Master vampire
+R:520:0x02:0x56
+
+# Oriental vampire
+R:521:0x05:0x56
+
+# Greater mummy
+R:522:0x09:0x7a
+
+# Eog golem
+R:530:0x08:0x67
+
+# Dagashi
+R:532:0x0b:0x70
+
+# Smoke elemental
+R:537:0x08:0x45
+
+# Olog
+R:538:0x05:0x54
+
+# Gravity hound
+R:540:0x02:0x5a
+
+# Acidic cytoplasm
+R:541:0x0d:0x6a
+
+# Ooze elemental
+R:545:0x0f:0x45
+
+# Young black dragon
+R:546:0x08:0x64
+
+# Xorn
+R:550:0x03:0x58
+
+# Colossus
+R:558:0x0b:0x67
+
+# Bodak
+R:566:0x08:0x75
+
+# Lorgan, Chief of the Easterlings
+R:573:0x0c:0x70
+
+# Chaos spawn
+R:574:0x07:0x65
+
+# Crypt thing
+R:577:0x0e:0x4c
+
+# Will o' the wisp
+R:582:0x0b:0x45
+
+# Crystal drake
+R:591:0x0e:0x64
+
+# Mature black dragon
+R:592:0x08:0x64
+
+# Draebor, the Imp
+R:595:0x05:0x75
+
+# Mother Hydra
+R:596:0x05:0x75
+
+# Death knight
+R:597:0x02:0x70
+
+# Time vortex
+R:599:0x0d:0x76
+
+# Shimmering vortex
+R:600:0x0a:0x76
+
+# Beholder
+R:603:0x05:0x65
+
+# Emperor wight
+R:604:0x0b:0x57
+
+# Vargo, Tyrant of Fire
+R:606:0x0c:0x45
+
+# Nether wraith
+R:612:0x0c:0x57
+
+# Waldern, King of Water
+R:615:0x02:0x45
+
+# Ettin
+R:621:0x0b:0x54
+
+# Night mare
+R:622:0x0a:0x71
+
+# Ancient black dragon
+R:624:0x08:0x44
+
+# Shadowfax, steed of Gandalf
+R:629:0x09:0x71
+
+# Spirit troll
+R:630:0x0a:0x47
+
+# War troll
+R:631:0x05:0x54
+
+# Rotting quylthulg
+R:633:0x0f:0x51
+
+# 9-headed hydra
+R:635:0x03:0x4d
+
+# Enchantress
+R:636:0x0b:0x70
+
+# Sorcerer
+R:638:0x03:0x70
+
+# Xaren
+R:639:0x0b:0x58
+
+# Medusa, the Gorgon
+R:642:0x03:0x6e
+
+# Death drake
+R:643:0x07:0x44
+
+# Great crystal drake
+R:646:0x0e:0x44
+
+# Wyrd sister
+R:647:0x0b:0x70
+
+# Death quasit
+R:649:0x01:0x75
+
+# Fallen angel
+R:652:0x08:0x41
+
+# Judge Fire
+R:654:0x04:0x73
+
+# Master lich
+R:658:0x09:0x4c
+
+# Eol, the Dark Elf
+R:660:0x02:0x68
+
+# Undead beholder
+R:664:0x02:0x65
+
+# Dark young of Shub-Niggurath
+R:677:0x08:0x55
+
+# Dreadmaster
+R:690:0x03:0x47
+
+# Scatha the Worm
+R:692:0x01:0x44
+
+# Zoth-Ommog
+R:695:0x07:0x52
+
+# Grand master thief
+R:696:0x0e:0x70
+
+# Leprechaun fanatic
+R:700:0x08:0x68
+
+# Dracolich
+R:701:0x0e:0x44
+
+# Greater titan
+R:702:0x0a:0x50
+
+# Dracolisk
+R:703:0x04:0x44
+
+# Itangast the Fire Drake
+R:710:0x04:0x44
+
+# Glaurung, Father of the Dragons
+R:715:0x04:0x44
+
+# Behemoth
+R:716:0x05:0x48
+
+# Garm, Guardian of Hel
+R:717:0x07:0x43
+
+# Maulotaur
+R:723:0x07:0x48
+
+# Nether hound
+R:724:0x08:0x5a
+
+# Time hound
+R:725:0x0d:0x5a
+
+# Demonic quylthulg
+R:727:0x0c:0x51
+
+# Ulik the Troll
+R:729:0x03:0x54
+
+# Baphomet the Minotaur Lord
+R:730:0x08:0x48
+
+# Hell knight
+R:731:0x02:0x70
+
+# Old Sorcerer
+R:738:0x0a:0x70
+
+# Keeper of Secrets
+R:746:0x0a:0x48
+
+# Hand druj
+R:748:0x08:0x73
+
+# Eye druj
+R:749:0x08:0x73
+
+# Skull druj
+R:750:0x08:0x73
+
+# Aether vortex
+R:752:0x0e:0x76
+
+# Draconic quylthulg
+R:759:0x0d:0x51
+
+# Fundin Bluecloak
+R:762:0x0d:0x68
+
+# Gabriel, the Messenger
+R:769:0x0a:0x41
+
+# Artsi, the Champion of Chaos
+R:770:0x0d:0x68
+
+# The Cat Lord
+R:777:0x04:0x66
+
+# Vlad Dracula, Prince of Darkness
+R:780:0x06:0x56
+
+# Great Wyrm of Law
+R:784:0x09:0x44
+
+# Great Wyrm of Balance
+R:785:0x02:0x44
+
+# Glaaki
+R:788:0x05:0x7e
+
+# Greater draconic quylthulg
+R:801:0x05:0x51
+
+# Greater rotting quylthulg
+R:802:0x07:0x51
+
+# Omarax the Eye Tyrant
+R:805:0x0b:0x65
+
+# Greater Balrog
+R:807:0x0c:0x55
+
+# Aether hound
+R:811:0x0e:0x5a
+
+# Yig, Father of Serpents
+R:814:0x05:0x4a
+
+# Hela, Queen of the Dead
+R:817:0x05:0x70
+
+# Master quylthulg
+R:821:0x06:0x51
+
+# Qlzqqlzuup, the Lord of Flesh
+R:822:0x0e:0x51
+
+# F'lar, rider of the Bronze Mnementh
+R:824:0x08:0x44
+
+# Cyaegha
+R:826:0x08:0x65
+
+# Pazuzu, Lord of Air
+R:827:0x01:0x55
+
+# Cantoras, the Skeletal Lord
+R:830:0x0e:0x73
+
+# Godzilla
+R:832:0x05:0x52
+
+# Loki, the Trickster
+R:835:0x05:0x50
+
+# Lungorthin, the Balrog of White Fire
+R:839:0x01:0x55
+
+# Draugluin, Sire of All Werewolves
+R:840:0x07:0x43
+
+# Vecna, the Emperor Lich
+R:844:0x0b:0x4c
+
+# Great Wyrm of Power
+R:847:0x0b:0x44
+
+# Nodens, Lord of the Great Abyss
+R:849:0x01:0x50
+
+# Jormungand the Midgard Serpent
+R:854:0x05:0x4a
+
+# The Destroyer
+R:855:0x09:0x67
+
+# Gothmog, the High Captain of Balrogs
+R:856:0x0c:0x55
+
+# Sauron, the Sorcerer
+R:860:0x03:0x70
+
+# Human Warrior
+R:863:0x01:0x70
+
+# Elven archer
+R:864:0x0d:0x68
+
+# Death
+R:875:0x02:0x47
+
+# Famine
+R:876:0x02:0x47
+
+# Pestilence
+R:877:0x02:0x47
+
+# War
+R:878:0x02:0x47
+
+# Giant piranha
+R:884:0x0d:0x7e
+
+# Octopus
+R:891:0x02:0x7e
+
+# Giant octopus
+R:892:0x0a:0x7e
+
+# Drowned soul
+R:895:0x02:0x47
+
+# Devilfish
+R:920:0x0a:0x7e
+
+# Undead devilfish
+R:921:0x0a:0x7e
+
+# Gandalf the Grey
+R:935:0x0a:0x70
+
+# Brown Firelizard
+R:942:0x0f:0x64
+
+# Bronze Firelizard
+R:943:0x01:0x64
+
+# Elder aranea
+R:964:0x04:0x53
+
+# Bullroarer the Hobbit
+R:985:0x06:0x68
+
+# Hezrou
+R:992:0x0a:0x55
+
+# Glabrezu
+R:993:0x03:0x55
+
+# Lesser Balrog
+R:996:0x0c:0x55
+
+# The Rat King
+R:1006:0x07:0x72
+
+# Vort the Kobold Queen
+R:1007:0x06:0x6b
+
+# Fire Phantom
+R:1009:0x0c:0x47
+
+# Bone golem
+R:1013:0x08:0x67
+
+# Snake of Yig
+R:1014:0x04:0x4a
+
+# Cultist
+R:1017:0x0e:0x70
+
+# Cult leader
+R:1018:0x06:0x70
+
+# Green dragon worm
+R:1025:0x05:0x77
+
+# Red dragon worm
+R:1027:0x04:0x77
+
diff --git a/lib/pref/colors.prf b/lib/pref/colors.prf
new file mode 100644
index 00000000..fa4d3f05
--- /dev/null
+++ b/lib/pref/colors.prf
@@ -0,0 +1,53 @@
+
+
+# Color redefinitions
+
+# Color 'White'
+V:1:0x07:0xFF:0xFF:0xFF
+
+# Color 'Slate'
+V:2:0x03:0x8C:0x8C:0x8C
+
+# Color 'Orange'
+V:3:0x0C:0xFF:0x77:0x00
+
+# Color 'Red'
+V:4:0x04:0xC9:0x00:0x00
+
+# Color 'Green'
+V:5:0x02:0x00:0x86:0x45
+
+# Color 'Blue'
+V:6:0x01:0x00:0x00:0xE3
+
+# Color 'Umber'
+V:7:0x06:0x8E:0x45:0x00
+
+# Color 'Light Dark'
+V:8:0x08:0x50:0x50:0x50
+
+# Color 'Light Slate'
+V:9:0x0B:0xD1:0xD1:0xD1
+
+# Color 'Violet'
+V:10:0x05:0xC0:0x00:0xAF
+
+# Color 'Yellow'
+V:11:0x0E:0xFF:0xFF:0x00
+
+# Color 'Light Red'
+V:12:0x0D:0xFF:0x00:0x68
+
+# Color 'Light Green'
+V:13:0x0A:0x00:0xFF:0x00
+
+# Color 'Light Blue'
+V:14:0x09:0x51:0xDD:0xFF
+
+# Color 'Light Umber'
+V:15:0x06:0xD7:0x8E:0x45
+
+
+
+
+
diff --git a/lib/pref/font-ami.prf b/lib/pref/font-ami.prf
new file mode 100644
index 00000000..1c06dbb3
--- /dev/null
+++ b/lib/pref/font-ami.prf
@@ -0,0 +1,28 @@
+# File: font-ami.prf
+
+#
+# This file contains text remapping data
+# currently used in the Amiga version.
+#
+# Lars Haugseth <larshau@ifi.uio.no>
+#
+
+
+# Color palette - Text
+V:0:0x01:0x00:0x00:0x00
+V:1:0x01:0xFF:0xFF:0xFF
+V:2:0x01:0xC7:0xC7:0xC7
+V:3:0x01:0xFF:0x92:0x00
+V:4:0x01:0xFF:0x00:0x00
+V:5:0x01:0x00:0xCD:0x00
+V:6:0x01:0x00:0x00:0xFE
+V:7:0x01:0xC8:0x64:0x00
+V:8:0x01:0x8A:0x8A:0x8A
+V:9:0x01:0xE0:0xE0:0xE0
+V:10:0x01:0xA5:0x00:0xFF
+V:11:0x01:0xFF:0xFD:0x00
+V:12:0x01:0xFF:0x00:0xBC
+V:13:0x01:0x00:0xFF:0x00
+V:14:0x01:0x00:0xC8:0xFF
+V:15:0x01:0xFF:0xCC:0x80
+
diff --git a/lib/pref/font-dos.prf b/lib/pref/font-dos.prf
new file mode 100644
index 00000000..9cf1866f
--- /dev/null
+++ b/lib/pref/font-dos.prf
@@ -0,0 +1,8 @@
+# File: font-dos.prf
+
+#
+# This file is used by Angband (when it was compiled using "main-dos.c")
+# to specify simple attr/char remappings using a standard font, allowing
+# the use of special pseudo-graphic pictures for walls and such.
+#
+
diff --git a/lib/pref/font-ibm.prf b/lib/pref/font-ibm.prf
new file mode 100644
index 00000000..5111f639
--- /dev/null
+++ b/lib/pref/font-ibm.prf
@@ -0,0 +1,363 @@
+# File: font-ibm.prf
+
+#
+# This file is used by Angband (when it was compiled using "main-ibm.c")
+# to specify simple attr/char remappings using a standard font, allowing
+# the use of the IBM's built in pseudo-graphic pictures for walls and such.
+#
+
+
+##### Feature attr/char definitions #####
+
+# open floor
+F:1:0x01/0xF9
+
+# fountain
+F:2:0x01/0xF4
+
+# fountain
+F:15:0x08/0xF4
+
+# web
+F:16:0x0B/0xB2
+
+# secret door
+F:48:0x01/0xB1
+
+# magma vein
+F:50:0x02/0xB0
+
+# quartz vein
+F:51:0x01/0xB0
+
+# magma vein
+F:52:0x02/0xB0
+
+# quartz vein
+F:53:0x01/0xB0
+
+# magma vein with treasure
+F:54:0x03/0xB0
+
+# quartz vein with treasure
+F:55:0x03/0xB0
+
+# granite wall
+F:56:0x01/0xB1
+
+# granite wall
+F:57:0x01/0xB1
+
+# granite wall
+F:58:0x01/0xB1
+
+# granite wall
+F:59:0x01/0xB1
+
+# permanent wall
+F:60:0x01/0xB1
+
+# permanent wall
+F:61:0x01/0xB1
+
+# permanent wall
+F:62:0x01/0xB1
+
+# permanent wall
+F:63:0x01/0xB1
+
+# Straight Road startpoint
+F:65:0x01/0xB2
+
+# section of the Straight Road
+F:66:0x0E/0xB2
+
+# section of the Straight Road
+F:67:0x06/0xB2
+
+# section of the Straight Road
+F:68:0x0E/0xB2
+
+# section of the Straight Road
+F:69:0x06/0xB2
+
+# section of the Straight Road
+F:70:0x09/0xB2
+
+# section of the Straight Road (discharged)
+F:71:0x09/0xB2
+
+# Straight Road exit
+F:72:0x01/0xB2
+
+# corrupted section of the Straight Road
+F:73:0x08/0xB2
+
+# permanent wall
+F:75:0x01/0xB1
+
+# permanent wall
+F:76:0x01/0xB1
+
+# permanent wall
+F:77:0x01/0xB1
+
+# permanent wall
+F:78:0x01/0xB1
+
+# stream of shallow water
+F:84:0x0E/0xF7
+
+# pool of deep lava
+F:85:0x0C/0xF7
+
+# stream of shallow lava
+F:86:0x04/0xF7
+
+# dark pit
+F:87:0x08/0xDB
+
+# dirt
+F:88:0x0F/0xF9
+
+# patch of grass
+F:89:0x0D/0xF9
+
+# ice
+F:90:0x09/0xDB
+
+# sand
+F:91:0x0B/0xF9
+
+# dead tree
+F:92:0x08/0x9D
+
+# ash
+F:93:0x02/0xF9
+
+# mud
+F:94:0x07/0xF9
+
+# ice wall
+F:95:0x09/0xB1
+
+# tree
+F:96:0x0D/0x9D
+
+# sandwall
+F:98:0x0B/0xB1
+
+# sandwall
+F:99:0x0B/0xB1
+
+# sandwall with treasure
+F:100:0x03/0xB1
+
+# nether mist
+F:102:0x0A/0xB2
+
+# Void Jumpgate
+F:160:0x0A/0xEF
+
+# Altar of Being
+F:161:0x09/0xD2
+
+# Altar of Winds
+F:162:0x0E/0xD2
+
+# Altar of Force
+F:163:0x0C/0xD2
+
+# Altar of Darkness
+F:164:0x08/0xD2
+
+# floor
+F:172:0x01/0xF9
+
+# Underground Tunnel
+F:173:0x02/0xB0
+
+# stream of tainted water
+F:174:0x07/0xF7
+
+# Void Jumpgate
+F:176:0x0A/0xEF
+
+# lava wall
+F:177:0x0C/0xB1
+
+# Great Fire
+F:178:0x0A/0xF7
+
+# field
+F:181:0x05/0xF9
+
+# Ekkaia, the Encircling Sea
+F:182:0x06/0xF7
+
+# pool of deep water
+F:187:0x06/0xF7
+
+# glass wall
+F:188:0x0E/0xDB
+
+# illusion wall
+F:189:0x01/0xB1
+
+# Grass roof
+F:190:0x0B/0xB1
+
+# grass roof top
+F:191:0x0B/0xB1
+
+# grass roof chimney
+F:192:0x0B/0xB1
+
+# brick roof
+F:193:0x04/0xB1
+
+# brick roof top
+F:194:0x04/0xB1
+
+# brick roof chimney
+F:195:0x04/0xB1
+
+# window
+F:196:0x01/0x4F
+
+# small window
+F:197:0x01/0x6F
+
+# rain barrel
+F:198:0x01/0xB1
+
+# cobblestone road
+F:200:0x01/0xF9
+
+# cobblestone with outlet
+F:201:0x01/0xF9
+
+# small tree
+F:202:0x05/0x9D
+
+# Underground Tunnel
+F:204:0x0F/0xB0
+
+# a blazing fire
+F:205:0x0B/0xF7
+
+# rocky ground
+F:207:0x02/0xF9
+
+# cloud-like vapour
+F:208:0x09/0xB2
+
+# condensing water
+F:209:0x0E/0xF7
+
+# dense mist
+F:210:0x01/0xB2
+
+# hail-stone wall
+F:211:0x09/0xB1
+
+
+##### Monster attr/char definitions #####
+
+# Space monster
+R:144:0x00/0xF9
+
+# Old Man Willow
+R:206:0x02/0x9D
+
+# Lurker
+R:247:0x01/0xF9
+
+# Tangleweed
+R:248:0x05/0x9D
+
+# Poison ivy
+R:266:0x05/0x9D
+
+# Giant Venus Flytrap
+R:317:0x05/0x9D
+
+# Stunwall
+R:326:0x09/0xB1
+
+# Huorn
+R:329:0x05/0x9D
+
+# Landmine
+R:333:0x01/0xF9
+
+# Livingstone
+R:336:0x09/0xB1
+
+# Vampiric mist
+R:365:0x08/0xB2
+
+# It
+R:393:0x09/0xF9
+
+# Xiclotlan
+R:396:0x08/0x9D
+
+# Roper
+R:426:0x08/0xB1
+
+# Lesser wall monster
+R:448:0x09/0xB1
+
+# Chaos tile
+R:458:0x0A/0xF9
+
+# Mist giant
+R:552:0x0E/0xB2
+
+# Trapper
+R:565:0x01/0xF9
+
+# Time bomb
+R:567:0x01/0xF9
+
+# Weird fume
+R:625:0x0A/0xB2
+
+# Colour out of space
+R:678:0x0A/0xF9
+
+# Ent
+R:708:0x0D/0x9D
+
+# Quickbeam, the Ent
+R:714:0x0D/0x9D
+
+# Greater wall monster
+R:718:0x09/0xB1
+
+# Ahtu, Avatar of Nyarlathotep
+R:761:0x08/0xB1
+
+# Null, the Living Void
+R:803:0x00/0xF9
+
+# Rocket mine
+R:870:0x0C/0xF9
+
+# Bouncing mine
+R:871:0x0E/0xF9
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0x0D/0x9D
+
+# The Glass Golem
+R:1033:0x09/0xB1
+
+# Golgarach, the Living Rock
+R:1035:0x09/0xB1
+
+# Spirit
+R:1053:0x09/0xF9
+
+
diff --git a/lib/pref/font-mac.new b/lib/pref/font-mac.new
new file mode 100644
index 00000000..0d6dd8e1
--- /dev/null
+++ b/lib/pref/font-mac.new
@@ -0,0 +1,108 @@
+# File: font-mac.prf
+
+#
+# This file is used by Angband (when it was compiled using "main-mac.c")
+# to specify simple attr/char remappings using a standard font, allowing
+# the use of the Macintosh's special characters for some things.
+#
+
+
+##### Feature attr/char definitions #####
+
+# fountain
+F:2:0x01/0xBA
+
+# fountain
+F:15:0x08/0xBA
+
+# stream of shallow water
+F:84:0x0E/0xC5
+
+# pool of deep lava
+F:85:0x0C/0xC5
+
+# stream of shallow lava
+F:86:0x04/0xC5
+
+# dead tree
+F:92:0x08/0xB4
+
+# tree
+F:96:0x0D/0xB4
+
+# mountain chain
+F:97:0x0F/0xC6
+
+# high mountain chain
+F:101:0x09/0xC6
+
+# Void Jumpgate
+F:160:0x0A/0xBD
+
+# Altar of Being
+F:161:0x09/0xB9
+
+# Altar of Winds
+F:162:0x0E/0xB9
+
+# Altar of Force
+F:163:0x0C/0xB9
+
+# Altar of Darkness
+F:164:0x08/0xB9
+
+# stream of tainted water
+F:174:0x07/0xC5
+
+# Void Jumpgate
+F:176:0x0A/0xBD
+
+# Great Fire
+F:178:0x0A/0xC5
+
+# Ekkaia, the Encircling Sea
+F:182:0x06/0xC5
+
+# pool of deep water
+F:187:0x06/0xC5
+
+# small tree
+F:202:0x05/0xB4
+
+# a blazing fire
+F:205:0x0B/0xC5
+
+# condensing water
+F:209:0x0E/0xC5
+
+
+##### Monster attr/char definitions #####
+
+# Old Man Willow
+R:206:0x02/0xB4
+
+# Tangleweed
+R:248:0x05/0xB4
+
+# Poison ivy
+R:266:0x05/0xB4
+
+# Giant Venus Flytrap
+R:317:0x05/0xB4
+
+# Huorn
+R:329:0x05/0xB4
+
+# Xiclotlan
+R:396:0x08/0xB4
+
+# Ent
+R:708:0x0D/0xB4
+
+# Quickbeam, the Ent
+R:714:0x0D/0xB4
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0x0D/0xB4
+
+
diff --git a/lib/pref/font-mac.prf b/lib/pref/font-mac.prf
new file mode 100644
index 00000000..d15c2e47
--- /dev/null
+++ b/lib/pref/font-mac.prf
@@ -0,0 +1,18 @@
+# File: font.prf
+
+#
+# This file defines special attr/char mappings for use in "text" mode
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+## OPTION: Display "veins" (white "%") as "normal walls" (white "#")
+## This replaces the old method of setting "notice_seams" to false,
+## which no longer works as of Angband 2.7.9, for various reasons.
+#
+#F:50:1/35
+#F:51:1/35
+#F:52:1/35
+#F:53:1/35
+
diff --git a/lib/pref/font-win.prf b/lib/pref/font-win.prf
new file mode 100644
index 00000000..00475916
--- /dev/null
+++ b/lib/pref/font-win.prf
@@ -0,0 +1,298 @@
+# File: font-win.prf
+
+#
+# This file is used by Angband (when it was compiled using "main-win.c")
+# to specify simple attr/char remappings using a standard font, allowing
+# the use of special pseudo-graphic pictures for walls and such.
+#
+# Note that this file is extremely similar to the file "font-ibm.prf",
+# but it uses different codes, since it uses the special pseudo-graphic
+# symbols defined in the "lib/xtra/font/*.FON" files, and there is only
+# one special wall type, so we have to use special "colors".
+#
+
+
+##### Feature attr/char definitions #####
+
+# open floor
+F:1:0x01/0x1F
+
+# web
+F:16:0x0B/0x7F
+
+# secret door
+F:48:0x01/0x7F
+
+# magma vein
+F:50:0x02/0x7F
+
+# quartz vein
+F:51:0x09/0x7F
+
+# magma vein
+F:52:0x02/0x7F
+
+# quartz vein
+F:53:0x09/0x7F
+
+# granite wall
+F:56:0x01/0x7F
+
+# granite wall
+F:57:0x01/0x7F
+
+# granite wall
+F:58:0x01/0x7F
+
+# granite wall
+F:59:0x01/0x7F
+
+# permanent wall
+F:60:0x01/0x7F
+
+# permanent wall
+F:61:0x01/0x7F
+
+# permanent wall
+F:62:0x01/0x7F
+
+# permanent wall
+F:63:0x01/0x7F
+
+# permanent wall
+F:75:0x01/0x7F
+
+# permanent wall
+F:76:0x01/0x7F
+
+# permanent wall
+F:77:0x01/0x7F
+
+# permanent wall
+F:78:0x01/0x7F
+
+# pool of deep lava
+F:85:0x0C/0x1F
+
+# stream of shallow lava
+F:86:0x04/0x1F
+
+# dark pit
+F:87:0x08/0x7F
+
+# dirt
+F:88:0x0F/0x1F
+
+# patch of grass
+F:89:0x0D/0x1F
+
+# ice
+F:90:0x09/0x1F
+
+# sand
+F:91:0x0B/0x1F
+
+# dead tree
+F:92:0x08/0x7F
+
+# ash
+F:93:0x02/0x1F
+
+# mud
+F:94:0x07/0x1F
+
+# ice wall
+F:95:0x09/0x7F
+
+# tree
+F:96:0x0D/0x7F
+
+# sandwall
+F:98:0x0B/0x7F
+
+# sandwall
+F:99:0x0B/0x7F
+
+# sandwall with treasure
+F:100:0x03/0x7F
+
+# nether mist
+F:102:0x0A/0x7F
+
+# Underground Tunnel
+F:173:0x02/0x7F
+
+# lava wall
+F:177:0x0C/0x7F
+
+# Great Fire
+F:178:0x0A/0x7F
+
+# field
+F:181:0x05/0x1F
+
+# glass wall
+F:188:0x0E/0x1F
+
+# illusion wall
+F:189:0x01/0x7F
+
+# Grass roof
+F:190:0x0B/0x7F
+
+# grass roof top
+F:191:0x0B/0x7F
+
+# grass roof chimney
+F:192:0x0B/0x7F
+
+# brick roof
+F:193:0x04/0x7F
+
+# brick roof top
+F:194:0x04/0x7F
+
+# brick roof chimney
+F:195:0x04/0x7F
+
+# rain barrel
+F:198:0x01/0x7F
+
+# cobblestone road
+F:200:0x01/0x1F
+
+# cobblestone with outlet
+F:201:0x01/0x1F
+
+# small tree
+F:202:0x05/0x7F
+
+# Underground Tunnel
+F:204:0x0F/0x7F
+
+# a blazing fire
+F:205:0x0B/0x7F
+
+# rocky ground
+F:207:0x02/0x1F
+
+# cloud-like vapour
+F:208:0x09/0x1F
+
+# dense mist
+F:210:0x01/0x7F
+
+# hail-stone wall
+F:211:0x09/0x7F
+
+# copper pillar
+F:213:0x07/0x7F
+
+# ethereal wall
+F:214:0x01/0x1F
+
+# glacial wall
+F:215:0x0E/0x7F
+
+# battlement
+F:216:0x01/0x7F
+
+##### Monster attr/char definitions #####
+
+# Space monster
+R:144:0x00/0x1F
+
+# Old Man Willow
+R:206:0x02/0x7F
+
+# Lurker
+R:247:0x01/0x1F
+
+# Tangleweed
+R:248:0x05/0x7F
+
+# Poison ivy
+R:266:0x05/0x7F
+
+# Giant Venus Flytrap
+R:317:0x05/0x7F
+
+# Stunwall
+R:326:0x09/0x7F
+
+# Huorn
+R:329:0x05/0x7F
+
+# Landmine
+R:333:0x01/0x1F
+
+# Livingstone
+R:336:0x09/0x7F
+
+# Vampiric mist
+R:365:0x08/0x7F
+
+# It
+R:393:0x09/0x1F
+
+# Xiclotlan
+R:396:0x08/0x7F
+
+# Roper
+R:426:0x08/0x7F
+
+# Lesser wall monster
+R:448:0x09/0x7F
+
+# Chaos tile
+R:458:0x0A/0x1F
+
+# Mist giant
+R:552:0x0E/0x7F
+
+# Trapper
+R:565:0x01/0x1F
+
+# Time bomb
+R:567:0x01/0x1F
+
+# Weird fume
+R:625:0x0A/0x7F
+
+# Colour out of space
+R:678:0x0A/0x1F
+
+# Ent
+R:708:0x0D/0x7F
+
+# Quickbeam, the Ent
+R:714:0x0D/0x7F
+
+# Greater wall monster
+R:718:0x09/0x7F
+
+# Ahtu, Avatar of Nyarlathotep
+R:761:0x08/0x7F
+
+# Null, the Living Void
+R:803:0x00/0x1F
+
+# Rocket mine
+R:870:0x0C/0x1F
+
+# Bouncing mine
+R:871:0x0E/0x1F
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0x0D/0x7F
+
+# The Glass Golem
+R:1033:0x09/0x7F
+
+# Golgarach, the Living Rock
+R:1035:0x09/0x7F
+
+# Spirit
+R:1053:0x09/0x1F
+
+
diff --git a/lib/pref/font-x11.prf b/lib/pref/font-x11.prf
new file mode 100644
index 00000000..57828a56
--- /dev/null
+++ b/lib/pref/font-x11.prf
@@ -0,0 +1,22 @@
+# File: font-x11.prf
+
+
+# Color palette - Text
+
+V:0:0x01:0x00:0x00:0x00
+V:1:0x01:0xFF:0xFF:0xFF
+V:2:0x01:0xC7:0xC7:0xC7
+V:3:0x01:0xFF:0x92:0x00
+V:4:0x01:0xC0:0x00:0x00
+V:5:0x01:0x00:0xC0:0x00
+V:6:0x01:0x00:0x00:0xFE
+V:7:0x01:0xC8:0x64:0x00
+V:8:0x01:0x8A:0x8A:0x8A
+V:9:0x01:0xE0:0xE0:0xE0
+V:10:0x01:0xA5:0x00:0xFF
+V:11:0x01:0xFF:0xFD:0x00
+V:12:0x01:0xFF:0x40:0x40
+V:13:0x01:0x00:0xFF:0x00
+V:14:0x01:0x00:0xC8:0xFF
+V:15:0x01:0xFF:0xCC:0x80
+
diff --git a/lib/pref/font-xxx.prf b/lib/pref/font-xxx.prf
new file mode 100644
index 00000000..298a8643
--- /dev/null
+++ b/lib/pref/font-xxx.prf
@@ -0,0 +1,472 @@
+# File: font-xxx.prf
+
+#
+# This file defines special attr/char mappings for use in "text" mode
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+##### Special attr/char values #####
+
+
+## # Unused (@)
+## S:0x00:0x00/0x40
+## S:0x01:0x01/0x40
+## S:0x02:0x02/0x40
+## S:0x03:0x03/0x40
+## S:0x04:0x04/0x40
+## S:0x05:0x05/0x40
+## S:0x06:0x06/0x40
+## S:0x07:0x07/0x40
+## S:0x08:0x08/0x40
+## S:0x09:0x09/0x40
+## S:0x0A:0x0A/0x40
+## S:0x0B:0x0B/0x40
+## S:0x0C:0x0C/0x40
+## S:0x0D:0x0D/0x40
+## S:0x0E:0x0E/0x40
+## S:0x0F:0x0F/0x40
+
+## # Unused (@)
+## S:0x10:0x00/0x40
+## S:0x11:0x01/0x40
+## S:0x12:0x02/0x40
+## S:0x13:0x03/0x40
+## S:0x14:0x04/0x40
+## S:0x15:0x05/0x40
+## S:0x16:0x06/0x40
+## S:0x17:0x07/0x40
+## S:0x18:0x08/0x40
+## S:0x19:0x09/0x40
+## S:0x1A:0x0A/0x40
+## S:0x1B:0x0B/0x40
+## S:0x1C:0x0C/0x40
+## S:0x1D:0x0D/0x40
+## S:0x1E:0x0E/0x40
+## S:0x1F:0x0F/0x40
+
+## # Unused (@)
+## S:0x20:0x00/0x40
+## S:0x21:0x01/0x40
+## S:0x22:0x02/0x40
+## S:0x23:0x03/0x40
+## S:0x24:0x04/0x40
+## S:0x25:0x05/0x40
+## S:0x26:0x06/0x40
+## S:0x27:0x07/0x40
+## S:0x28:0x08/0x40
+## S:0x29:0x09/0x40
+## S:0x2A:0x0A/0x40
+## S:0x2B:0x0B/0x40
+## S:0x2C:0x0C/0x40
+## S:0x2D:0x0D/0x40
+## S:0x2E:0x0E/0x40
+## S:0x2F:0x0F/0x40
+
+# Spells (*)
+S:0x30:0x00/0x2A
+S:0x31:0x01/0x2A
+S:0x32:0x02/0x2A
+S:0x33:0x03/0x2A
+S:0x34:0x04/0x2A
+S:0x35:0x05/0x2A
+S:0x36:0x06/0x2A
+S:0x37:0x07/0x2A
+S:0x38:0x08/0x2A
+S:0x39:0x09/0x2A
+S:0x3A:0x0A/0x2A
+S:0x3B:0x0B/0x2A
+S:0x3C:0x0C/0x2A
+S:0x3D:0x0D/0x2A
+S:0x3E:0x0E/0x2A
+S:0x3F:0x0F/0x2A
+
+# Spells (|)
+S:0x40:0x00/0x7C
+S:0x41:0x01/0x7C
+S:0x42:0x02/0x7C
+S:0x43:0x03/0x7C
+S:0x44:0x04/0x7C
+S:0x45:0x05/0x7C
+S:0x46:0x06/0x7C
+S:0x47:0x07/0x7C
+S:0x48:0x08/0x7C
+S:0x49:0x09/0x7C
+S:0x4A:0x0A/0x7C
+S:0x4B:0x0B/0x7C
+S:0x4C:0x0C/0x7C
+S:0x4D:0x0D/0x7C
+S:0x4E:0x0E/0x7C
+S:0x4F:0x0F/0x7C
+
+# Spells (-)
+S:0x50:0x00/0x2D
+S:0x51:0x01/0x2D
+S:0x52:0x02/0x2D
+S:0x53:0x03/0x2D
+S:0x54:0x04/0x2D
+S:0x55:0x05/0x2D
+S:0x56:0x06/0x2D
+S:0x57:0x07/0x2D
+S:0x58:0x08/0x2D
+S:0x59:0x09/0x2D
+S:0x5A:0x0A/0x2D
+S:0x5B:0x0B/0x2D
+S:0x5C:0x0C/0x2D
+S:0x5D:0x0D/0x2D
+S:0x5E:0x0E/0x2D
+S:0x5F:0x0F/0x2D
+
+# Spells (/)
+S:0x60:0x00/0x2F
+S:0x61:0x01/0x2F
+S:0x62:0x02/0x2F
+S:0x63:0x03/0x2F
+S:0x64:0x04/0x2F
+S:0x65:0x05/0x2F
+S:0x66:0x06/0x2F
+S:0x67:0x07/0x2F
+S:0x68:0x08/0x2F
+S:0x69:0x09/0x2F
+S:0x6A:0x0A/0x2F
+S:0x6B:0x0B/0x2F
+S:0x6C:0x0C/0x2F
+S:0x6D:0x0D/0x2F
+S:0x6E:0x0E/0x2F
+S:0x6F:0x0F/0x2F
+
+# Spells (\)
+S:0x70:0x00/0x5C
+S:0x71:0x01/0x5C
+S:0x72:0x02/0x5C
+S:0x73:0x03/0x5C
+S:0x74:0x04/0x5C
+S:0x75:0x05/0x5C
+S:0x76:0x06/0x5C
+S:0x77:0x07/0x5C
+S:0x78:0x08/0x5C
+S:0x79:0x09/0x5C
+S:0x7A:0x0A/0x5C
+S:0x7B:0x0B/0x5C
+S:0x7C:0x0C/0x5C
+S:0x7D:0x0D/0x5C
+S:0x7E:0x0E/0x5C
+S:0x7F:0x0F/0x5C
+
+# Amulets (")
+S:0x80:0x00/0x22
+S:0x81:0x01/0x22
+S:0x82:0x02/0x22
+S:0x83:0x03/0x22
+S:0x84:0x04/0x22
+S:0x85:0x05/0x22
+S:0x86:0x06/0x22
+S:0x87:0x07/0x22
+S:0x88:0x08/0x22
+S:0x89:0x09/0x22
+S:0x8A:0x0A/0x22
+S:0x8B:0x0B/0x22
+S:0x8C:0x0C/0x22
+S:0x8D:0x0D/0x22
+S:0x8E:0x0E/0x22
+S:0x8F:0x0F/0x22
+
+# Rings (=)
+S:0x90:0x00/0x3D
+S:0x91:0x01/0x3D
+S:0x92:0x02/0x3D
+S:0x93:0x03/0x3D
+S:0x94:0x04/0x3D
+S:0x95:0x05/0x3D
+S:0x96:0x06/0x3D
+S:0x97:0x07/0x3D
+S:0x98:0x08/0x3D
+S:0x99:0x09/0x3D
+S:0x9A:0x0A/0x3D
+S:0x9B:0x0B/0x3D
+S:0x9C:0x0C/0x3D
+S:0x9D:0x0D/0x3D
+S:0x9E:0x0E/0x3D
+S:0x9F:0x0F/0x3D
+
+# Staffs (_)
+S:0xA0:0x00/0x5F
+S:0xA1:0x01/0x5F
+S:0xA2:0x02/0x5F
+S:0xA3:0x03/0x5F
+S:0xA4:0x04/0x5F
+S:0xA5:0x05/0x5F
+S:0xA6:0x06/0x5F
+S:0xA7:0x07/0x5F
+S:0xA8:0x08/0x5F
+S:0xA9:0x09/0x5F
+S:0xAA:0x0A/0x5F
+S:0xAB:0x0B/0x5F
+S:0xAC:0x0C/0x5F
+S:0xAD:0x0D/0x5F
+S:0xAE:0x0E/0x5F
+S:0xAF:0x0F/0x5F
+
+# Wands (-)
+S:0xB0:0x00/0x2D
+S:0xB1:0x01/0x2D
+S:0xB2:0x02/0x2D
+S:0xB3:0x03/0x2D
+S:0xB4:0x04/0x2D
+S:0xB5:0x05/0x2D
+S:0xB6:0x06/0x2D
+S:0xB7:0x07/0x2D
+S:0xB8:0x08/0x2D
+S:0xB9:0x09/0x2D
+S:0xBA:0x0A/0x2D
+S:0xBB:0x0B/0x2D
+S:0xBC:0x0C/0x2D
+S:0xBD:0x0D/0x2D
+S:0xBE:0x0E/0x2D
+S:0xBF:0x0F/0x2D
+
+# Rods (-)
+S:0xC0:0x00/0x2D
+S:0xC1:0x01/0x2D
+S:0xC2:0x02/0x2D
+S:0xC3:0x03/0x2D
+S:0xC4:0x04/0x2D
+S:0xC5:0x05/0x2D
+S:0xC6:0x06/0x2D
+S:0xC7:0x07/0x2D
+S:0xC8:0x08/0x2D
+S:0xC9:0x09/0x2D
+S:0xCA:0x0A/0x2D
+S:0xCB:0x0B/0x2D
+S:0xCC:0x0C/0x2D
+S:0xCD:0x0D/0x2D
+S:0xCE:0x0E/0x2D
+S:0xCF:0x0F/0x2D
+
+# Scrolls (?)
+S:0xD0:0x00/0x3F
+S:0xD1:0x01/0x3F
+S:0xD2:0x02/0x3F
+S:0xD3:0x03/0x3F
+S:0xD4:0x04/0x3F
+S:0xD5:0x05/0x3F
+S:0xD6:0x06/0x3F
+S:0xD7:0x07/0x3F
+S:0xD8:0x08/0x3F
+S:0xD9:0x09/0x3F
+S:0xDA:0x0A/0x3F
+S:0xDB:0x0B/0x3F
+S:0xDC:0x0C/0x3F
+S:0xDD:0x0D/0x3F
+S:0xDE:0x0E/0x3F
+S:0xDF:0x0F/0x3F
+
+# Potions (!)
+S:0xE0:0x00/0x21
+S:0xE1:0x01/0x21
+S:0xE2:0x02/0x21
+S:0xE3:0x03/0x21
+S:0xE4:0x04/0x21
+S:0xE5:0x05/0x21
+S:0xE6:0x06/0x21
+S:0xE7:0x07/0x21
+S:0xE8:0x08/0x21
+S:0xE9:0x09/0x21
+S:0xEA:0x0A/0x21
+S:0xEB:0x0B/0x21
+S:0xEC:0x0C/0x21
+S:0xED:0x0D/0x21
+S:0xEE:0x0E/0x21
+S:0xEF:0x0F/0x21
+
+# Food (,)
+S:0xF0:0x00/0x2C
+S:0xF1:0x01/0x2C
+S:0xF2:0x02/0x2C
+S:0xF3:0x03/0x2C
+S:0xF4:0x04/0x2C
+S:0xF5:0x05/0x2C
+S:0xF6:0x06/0x2C
+S:0xF7:0x07/0x2C
+S:0xF8:0x08/0x2C
+S:0xF9:0x09/0x2C
+S:0xFA:0x0A/0x2C
+S:0xFB:0x0B/0x2C
+S:0xFC:0x0C/0x2C
+S:0xFD:0x0D/0x2C
+S:0xFE:0x0E/0x2C
+S:0xFF:0x0F/0x2C
+
+
+
+##### Default inventory object colors #####
+
+
+# SKELETON
+E:1:0x01
+
+# BOTTLE
+E:2:0x01
+
+# FIRESTONE
+E:3:0x01
+
+# SPIKE
+E:5:0x02
+
+# CHEST
+E:7:0x02
+
+# JUNK
+E:11:0x01
+
+# BOOMERANG
+E:15:0x07
+
+# SHOT
+E:16:0x0F
+
+# ARROW
+E:17:0x0F
+
+# BOLT
+E:18:0x0F
+
+# BOW
+E:19:0x07
+
+# DIGGING
+E:20:0x02
+
+# HAFTED
+E:21:0x01
+
+# POLEARM
+E:22:0x01
+
+# SWORD
+E:23:0x01
+
+# AXE
+E:24:0x01
+
+# BOOTS
+E:30:0x0F
+
+# GLOVES
+E:31:0x0F
+
+# HELM
+E:32:0x0F
+
+# CROWN
+E:33:0x0F
+
+# SHIELD
+E:34:0x0F
+
+# CLOAK
+E:35:0x0F
+
+# SOFT_ARMOR
+E:36:0x02
+
+# HARD_ARMOR
+E:37:0x02
+
+# DRAG_ARMOR
+E:38:0x02
+
+# LITE
+E:39:0x0B
+
+# AMULET
+E:40:0x03
+
+# RING
+E:45:0x04
+
+# STAFF
+E:55:0x0F
+
+# WAND
+E:65:0x05
+
+# ROD
+E:66:0x0A
+
+# ROD TIP
+E:67:0x0A
+
+# SCROLL
+E:70:0x01
+
+# POTION
+E:71:0x0E
+
+# POTION2
+E:72:0x0E
+
+# FLASK
+E:77:0x0B
+
+# FOOD
+E:80:0x0F
+
+# BOOK
+E:111:0x0E
+
+# SYMBIOTIC_BOOK
+E:112:0x05
+
+# MUSIC_BOOK
+E:113:0x02
+
+# DRUID_BOOK
+E:114:0x0D
+
+# DAEMON BOOK
+E:115:0x03
+
+# POWER BATERIES
+E:4:0x0D
+
+# MAGE STAFFS
+E:6:0x0E
+
+# PARCHEMENT
+E:8:0x03
+
+# CORPSE
+E:9:0x0F
+
+# HYPNOS
+E:99:0x0F
+
+# RANDOM ARTIFACT
+E:102:0x0D
+
+# MUSICAL INSTRUMENT
+E:14:0x0A
+
+# EGG
+E:10:0x03
+
+# BASE RUNE
+E:104:0x0A
+
+# SECONDARY RUNE
+E:105:0x03
+
+# GOLD
+E:100:0x0B
+
+# TOOL
+E:12:0x0A
+
+# TRAPPING KIT
+E:46:0x08
+
+# TOTEM
+E:54:0x08
diff --git a/lib/pref/font.prf b/lib/pref/font.prf
new file mode 100644
index 00000000..505964bd
--- /dev/null
+++ b/lib/pref/font.prf
@@ -0,0 +1,60 @@
+# File: font.prf
+
+#
+# This file defines special attr/char mappings for use in "text" mode
+#
+# This file includes, if appropriate, various "sub-files"
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+## #
+## # OPTION: Display "veins" (white "%") as "normal walls" (white "#").
+## #
+## F:50:0x01/0x23
+## F:51:0x01/0x23
+## F:52:0x01/0x23
+## F:53:0x01/0x23
+
+
+##### Standard font file #####
+
+%:font-xxx.prf
+
+
+##### System Specific Subfiles #####
+
+?:[IOR [EQU $SYS xaw] [EQU $SYS x11] [EQU $SYS gtk]]
+%:font-x11.prf
+
+?:[EQU $SYS glu]
+%:font-glu.prf
+
+?:[EQU $SYS gcu]
+%:font-gcu.prf
+
+?:[EQU $SYS ami]
+%:font-ami.prf
+
+?:[EQU $SYS mac]
+%:font-mac.prf
+
+?:[EQU $SYS win]
+%:font-win.prf
+
+?:[EQU $SYS dos]
+%:font-dos.prf
+
+?:[EQU $SYS ibm]
+%:font-ibm.prf
+
+?:[EQU $SYS emx]
+%:font-emx.prf
+
+?:[EQU $SYS acn]
+%:font-acn.prf
+
+?:1
+
+
diff --git a/lib/pref/graf-ami.prf b/lib/pref/graf-ami.prf
new file mode 100644
index 00000000..d9b1b356
--- /dev/null
+++ b/lib/pref/graf-ami.prf
@@ -0,0 +1,64 @@
+# File: graf-ami.prf
+
+#
+# This file contains color definitions and
+# graphics remapping for the Amiga version.
+#
+# Lars Haugseth <larshau@ifi.uio.no>
+#
+
+
+# Color palette - Graphics
+V:0:0x01:0x00:0x00:0x00
+V:1:0x01:0xF0:0xE0:0xD0
+V:2:0x01:0x80:0x80:0x80
+V:3:0x01:0x50:0x50:0x50
+V:4:0x01:0xE0:0xB0:0x00
+V:5:0x01:0xC0:0xA0:0x70
+V:6:0x01:0x80:0x60:0x40
+V:7:0x01:0x40:0x30:0x20
+V:8:0x01:0x00:0xA0:0xF0
+V:9:0x01:0x00:0x00:0xF0
+V:10:0x01:0x00:0x00:0x70
+V:11:0x01:0xF0:0x00:0x00
+V:12:0x01:0x80:0x00:0x00
+V:13:0x01:0x90:0x00:0xB0
+V:14:0x01:0x00:0x60:0x10
+V:15:0x01:0x60:0xF0:0x40
+
+
+# Color palette - Text
+V:16:0x01:0x00:0x00:0x00
+V:17:0x01:0xFF:0xFF:0xFF
+V:18:0x01:0xC7:0xC7:0xC7
+V:19:0x01:0xFF:0x92:0x00
+V:20:0x01:0xFF:0x00:0x00
+V:21:0x01:0x00:0xCD:0x00
+V:22:0x01:0x00:0x00:0xFE
+V:23:0x01:0xC8:0x64:0x00
+V:24:0x01:0x8A:0x8A:0x8A
+V:25:0x01:0xE0:0xE0:0xE0
+V:26:0x01:0xA5:0x00:0xFF
+V:27:0x01:0xFF:0xFD:0x00
+V:28:0x01:0xFF:0x00:0xBC
+V:29:0x01:0x00:0xFF:0x00
+V:30:0x01:0x00:0xC8:0xFF
+V:31:0x01:0xFF:0xCC:0x80
+
+
+# Standard file
+%:graf-xxx.prf
+
+
+### Feature attr/char definitions
+
+# nothing
+F:0:0x01/0x20
+
+# open floor
+F:1:0x81/0x8E
+
+# invis trap
+F:2:0x81/0x8E
+
+
diff --git a/lib/pref/graf-dos.prf b/lib/pref/graf-dos.prf
new file mode 100644
index 00000000..41f38c76
--- /dev/null
+++ b/lib/pref/graf-dos.prf
@@ -0,0 +1,15 @@
+# File: graf-win.prf
+
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+# Standard file
+?:[EQU $GRAF old]
+%:graf-xxx.prf
+
+# New tiles
+?:[EQU $GRAF new]
+%:graf-new.prf
diff --git a/lib/pref/graf-ibm.prf b/lib/pref/graf-ibm.prf
new file mode 100644
index 00000000..eee54a13
--- /dev/null
+++ b/lib/pref/graf-ibm.prf
@@ -0,0 +1,6237 @@
+# File: graf-ibm.prf
+
+# This file defines special attr/char mappings for use in the pseudo
+# graphics mode using character generator font redefinitions. It can
+# also be used with X11/XAW/GTK ports by generating a bdf (then pcf)
+# file from lib/xtra/angband.fnt. How to do so is beyond the scope of
+# this file.
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+##### Special attr/char values #####
+
+## # Unused
+## S:0x00:0x00/0x40
+## S:0x01:0x01/0x40
+## S:0x02:0x02/0x40
+## S:0x03:0x03/0x40
+## S:0x04:0x04/0x40
+## S:0x05:0x05/0x40
+## S:0x06:0x06/0x40
+## S:0x07:0x07/0x40
+## S:0x08:0x08/0x40
+## S:0x09:0x09/0x40
+## S:0x0A:0x0A/0x40
+## S:0x0B:0x0B/0x40
+## S:0x0C:0x0C/0x40
+## S:0x0D:0x0D/0x40
+## S:0x0E:0x0E/0x40
+## S:0x0F:0x0F/0x40
+
+## # Unused
+## S:0x10:0x00/0x40
+## S:0x11:0x01/0x40
+## S:0x12:0x02/0x40
+## S:0x13:0x03/0x40
+## S:0x14:0x04/0x40
+## S:0x15:0x05/0x40
+## S:0x16:0x06/0x40
+## S:0x17:0x07/0x40
+## S:0x18:0x08/0x40
+## S:0x19:0x09/0x40
+## S:0x1A:0x0A/0x40
+## S:0x1B:0x0B/0x40
+## S:0x1C:0x0C/0x40
+## S:0x1D:0x0D/0x40
+## S:0x1E:0x0E/0x40
+## S:0x1F:0x0F/0x40
+
+## # Unused
+## S:0x20:0x00/0x40
+## S:0x21:0x01/0x40
+## S:0x22:0x02/0x40
+## S:0x23:0x03/0x40
+## S:0x24:0x04/0x40
+## S:0x25:0x05/0x40
+## S:0x26:0x06/0x40
+## S:0x27:0x07/0x40
+## S:0x28:0x08/0x40
+## S:0x29:0x09/0x40
+## S:0x2A:0x0A/0x40
+## S:0x2B:0x0B/0x40
+## S:0x2C:0x0C/0x40
+## S:0x2D:0x0D/0x40
+## S:0x2E:0x0E/0x40
+## S:0x2F:0x0F/0x40
+
+# Spells (*)
+S:0x30:0x00/0x2A
+S:0x31:0x01/0x2A
+S:0x32:0x02/0x2A
+S:0x33:0x03/0x2A
+S:0x34:0x04/0x2A
+S:0x35:0x05/0x2A
+S:0x36:0x06/0x2A
+S:0x37:0x07/0x2A
+S:0x38:0x08/0x2A
+S:0x39:0x09/0x2A
+S:0x3A:0x0A/0x2A
+S:0x3B:0x0B/0x2A
+S:0x3C:0x0C/0x2A
+S:0x3D:0x0D/0x2A
+S:0x3E:0x0E/0x2A
+S:0x3F:0x0F/0x2A
+
+# Spells (|)
+S:0x40:0x00/0x7C
+S:0x41:0x01/0x7C
+S:0x42:0x02/0x7C
+S:0x43:0x03/0x7C
+S:0x44:0x04/0x7C
+S:0x45:0x05/0x7C
+S:0x46:0x06/0x7C
+S:0x47:0x07/0x7C
+S:0x48:0x08/0x7C
+S:0x49:0x09/0x7C
+S:0x4A:0x0A/0x7C
+S:0x4B:0x0B/0x7C
+S:0x4C:0x0C/0x7C
+S:0x4D:0x0D/0x7C
+S:0x4E:0x0E/0x7C
+S:0x4F:0x0F/0x7C
+
+# Spells (-)
+S:0x50:0x00/0x2D
+S:0x51:0x01/0x2D
+S:0x52:0x02/0x2D
+S:0x53:0x03/0x2D
+S:0x54:0x04/0x2D
+S:0x55:0x05/0x2D
+S:0x56:0x06/0x2D
+S:0x57:0x07/0x2D
+S:0x58:0x08/0x2D
+S:0x59:0x09/0x2D
+S:0x5A:0x0A/0x2D
+S:0x5B:0x0B/0x2D
+S:0x5C:0x0C/0x2D
+S:0x5D:0x0D/0x2D
+S:0x5E:0x0E/0x2D
+S:0x5F:0x0F/0x2D
+
+# Spells (/)
+S:0x60:0x00/0x2F
+S:0x61:0x01/0x2F
+S:0x62:0x02/0x2F
+S:0x63:0x03/0x2F
+S:0x64:0x04/0x2F
+S:0x65:0x05/0x2F
+S:0x66:0x06/0x2F
+S:0x67:0x07/0x2F
+S:0x68:0x08/0x2F
+S:0x69:0x09/0x2F
+S:0x6A:0x0A/0x2F
+S:0x6B:0x0B/0x2F
+S:0x6C:0x0C/0x2F
+S:0x6D:0x0D/0x2F
+S:0x6E:0x0E/0x2F
+S:0x6F:0x0F/0x2F
+
+# Spells (\)
+S:0x70:0x00/0x5C
+S:0x71:0x01/0x5C
+S:0x72:0x02/0x5C
+S:0x73:0x03/0x5C
+S:0x74:0x04/0x5C
+S:0x75:0x05/0x5C
+S:0x76:0x06/0x5C
+S:0x77:0x07/0x5C
+S:0x78:0x08/0x5C
+S:0x79:0x09/0x5C
+S:0x7A:0x0A/0x5C
+S:0x7B:0x0B/0x5C
+S:0x7C:0x0C/0x5C
+S:0x7D:0x0D/0x5C
+S:0x7E:0x0E/0x5C
+S:0x7F:0x0F/0x5C
+
+# Amulets
+S:0x80:0x00/0xE7
+S:0x81:0x01/0xE7
+S:0x82:0x02/0xE7
+S:0x83:0x03/0xE7
+S:0x84:0x04/0xE7
+S:0x85:0x05/0xE7
+S:0x86:0x06/0xE7
+S:0x87:0x07/0xE7
+S:0x88:0x08/0xE7
+S:0x89:0x09/0xE7
+S:0x8A:0x0A/0xE7
+S:0x8B:0x0B/0xE7
+S:0x8C:0x0C/0xE7
+S:0x8D:0x0D/0xE7
+S:0x8E:0x0E/0xE7
+S:0x8F:0x0F/0xE7
+
+# Rings
+S:0x90:0x00/0xE8
+S:0x91:0x01/0xE8
+S:0x92:0x02/0xE8
+S:0x93:0x03/0xE8
+S:0x94:0x04/0xE8
+S:0x95:0x05/0xE8
+S:0x96:0x06/0xE8
+S:0x97:0x07/0xE8
+S:0x98:0x08/0xE8
+S:0x99:0x09/0xE8
+S:0x9A:0x0A/0xE8
+S:0x9B:0x0B/0xE8
+S:0x9C:0x0C/0xE8
+S:0x9D:0x0D/0xE8
+S:0x9E:0x0E/0xE8
+S:0x9F:0x0F/0xE8
+
+# Staffs
+S:0xA0:0x00/0xE9
+S:0xA1:0x01/0xE9
+S:0xA2:0x02/0xE9
+S:0xA3:0x03/0xE9
+S:0xA4:0x04/0xE9
+S:0xA5:0x05/0xE9
+S:0xA6:0x06/0xE9
+S:0xA7:0x07/0xE9
+S:0xA8:0x08/0xE9
+S:0xA9:0x09/0xE9
+S:0xAA:0x0A/0xE9
+S:0xAB:0x0B/0xE9
+S:0xAC:0x0C/0xE9
+S:0xAD:0x0D/0xE9
+S:0xAE:0x0E/0xE9
+S:0xAF:0x0F/0xE9
+
+# Wands
+S:0xB0:0x00/0xEA
+S:0xB1:0x01/0xEA
+S:0xB2:0x02/0xEA
+S:0xB3:0x03/0xEA
+S:0xB4:0x04/0xEA
+S:0xB5:0x05/0xEA
+S:0xB6:0x06/0xEA
+S:0xB7:0x07/0xEA
+S:0xB8:0x08/0xEA
+S:0xB9:0x09/0xEA
+S:0xBA:0x0A/0xEA
+S:0xBB:0x0B/0xEA
+S:0xBC:0x0C/0xEA
+S:0xBD:0x0D/0xEA
+S:0xBE:0x0E/0xEA
+S:0xBF:0x0F/0xEA
+
+# Rods
+S:0xC0:0x00/0xEB
+S:0xC1:0x01/0xEB
+S:0xC2:0x02/0xEB
+S:0xC3:0x03/0xEB
+S:0xC4:0x04/0xEB
+S:0xC5:0x05/0xEB
+S:0xC6:0x06/0xEB
+S:0xC7:0x07/0xEB
+S:0xC8:0x08/0xEB
+S:0xC9:0x09/0xEB
+S:0xCA:0x0A/0xEB
+S:0xCB:0x0B/0xEB
+S:0xCC:0x0C/0xEB
+S:0xCD:0x0D/0xEB
+S:0xCE:0x0E/0xEB
+S:0xCF:0x0F/0xEB
+
+# Scrolls
+S:0xD0:0x00/0xEC
+S:0xD1:0x01/0xEC
+S:0xD2:0x02/0xEC
+S:0xD3:0x03/0xEC
+S:0xD4:0x04/0xEC
+S:0xD5:0x05/0xEC
+S:0xD6:0x06/0xEC
+S:0xD7:0x07/0xEC
+S:0xD8:0x08/0xEC
+S:0xD9:0x09/0xEC
+S:0xDA:0x0A/0xEC
+S:0xDB:0x0B/0xEC
+S:0xDC:0x0C/0xEC
+S:0xDD:0x0D/0xEC
+S:0xDE:0x0E/0xEC
+S:0xDF:0x0F/0xEC
+
+# Potions
+S:0xE0:0x00/0xED
+S:0xE1:0x01/0xED
+S:0xE2:0x02/0xED
+S:0xE3:0x03/0xED
+S:0xE4:0x04/0xED
+S:0xE5:0x05/0xED
+S:0xE6:0x06/0xED
+S:0xE7:0x07/0xED
+S:0xE8:0x08/0xED
+S:0xE9:0x09/0xED
+S:0xEA:0x0A/0xED
+S:0xEB:0x0B/0xED
+S:0xEC:0x0C/0xED
+S:0xED:0x0D/0xED
+S:0xEE:0x0E/0xED
+S:0xEF:0x0F/0xED
+
+# Food
+S:0xF0:0x00/0xEE
+S:0xF1:0x01/0xEE
+S:0xF2:0x02/0xEE
+S:0xF3:0x03/0xEE
+S:0xF4:0x04/0xEE
+S:0xF5:0x05/0xEE
+S:0xF6:0x06/0xEE
+S:0xF7:0x07/0xEE
+S:0xF8:0x08/0xEE
+S:0xF9:0x09/0xEE
+S:0xFA:0x0A/0xEE
+S:0xFB:0x0B/0xEE
+S:0xFC:0x0C/0xEE
+S:0xFD:0x0D/0xEE
+S:0xFE:0x0E/0xEE
+S:0xFF:0x0F/0xEE
+
+
+##### Feature attr/char definitions #####
+
+# nothing
+# F:0:0x01/0x20
+
+# open floor
+F:1:0x01/0xB5
+
+# fountain
+F:2:0x0E/0xC8
+
+# glyph of warding
+F:3:0x0B/0xC6
+
+# open door
+F:4:0x0F/0xB9
+
+# broken door
+F:5:0x0F/0xBA
+
+# up staircase
+F:6:0x01/0xB6
+
+# down staircase
+F:7:0x01/0xB7
+
+# quest entrance
+F:8:0x0B/0xB7
+
+# quest exit
+F:9:0x0B/0xB6
+
+# quest down level
+F:10:0x04/0xB7
+
+# quest up level
+F:11:0x04/0xB6
+
+# town exit
+F:12:0x05/0xB7
+
+# shaft down
+F:13:0x0F/0xB7
+
+# shaft up
+F:14:0x0F/0xB6
+
+# fountain
+F:15:0x08/0xC8
+
+# web
+F:16:0x0B/0xCA
+
+# trap
+F:17:0x01/0xC7
+
+# door
+F:32:0x0F/0xB8
+
+# locked door
+F:33:0x0F/0xB8
+
+# locked door
+F:34:0x0F/0xB8
+
+# locked door
+F:35:0x0F/0xB8
+
+# locked door
+F:36:0x0F/0xB8
+
+# locked door
+F:37:0x0F/0xB8
+
+# locked door
+F:38:0x0F/0xB8
+
+# locked door
+F:39:0x0F/0xB8
+
+# jammed door
+F:40:0x0F/0xB8
+
+# jammed door
+F:41:0x0F/0xB8
+
+# jammed door
+F:42:0x0F/0xB8
+
+# jammed door
+F:43:0x0F/0xB8
+
+# jammed door
+F:44:0x0F/0xB8
+
+# jammed door
+F:45:0x0F/0xB8
+
+# jammed door
+F:46:0x0F/0xB8
+
+# jammed door
+F:47:0x0F/0xB8
+
+# secret door
+F:48:0x02/0xBC
+
+# pile of rubble
+F:49:0x02/0xBB
+
+# magma vein
+F:50:0x01/0xBC
+
+# quartz vein
+F:51:0x09/0xBC
+
+# magma vein
+F:52:0x01/0xBC
+
+# quartz vein
+F:53:0x09/0xBC
+
+# magma vein with treasure
+F:54:0x03/0xBC
+
+# quartz vein with treasure
+F:55:0x03/0xBC
+
+# granite wall
+F:56:0x02/0xBC
+
+# granite wall
+F:57:0x02/0xBC
+
+# granite wall
+F:58:0x02/0xBC
+
+# granite wall
+F:59:0x02/0xBC
+
+# permanent wall
+F:60:0x02/0xBC
+
+# permanent wall
+F:61:0x02/0xBC
+
+# permanent wall
+F:62:0x02/0xBC
+
+# permanent wall
+F:63:0x02/0xBC
+
+# explosive rune
+F:64:0x0C/0xC6
+
+# Straight Road startpoint
+F:65:0x01/0xCA
+
+# section of the Straight Road
+F:66:0x0E/0xCA
+
+# section of the Straight Road
+F:67:0x06/0xCA
+
+# section of the Straight Road
+F:68:0x0E/0xCA
+
+# section of the Straight Road
+F:69:0x06/0xCA
+
+# section of the Straight Road
+F:70:0x09/0xCA
+
+# section of the Straight Road (discharged)
+F:71:0x09/0xCA
+
+# Straight Road exit
+F:72:0x01/0xCA
+
+# corrupted section of the Straight Road
+F:73:0x08/0xCA
+
+# Building
+F:74:0x0F/0xC5
+
+# permanent wall
+F:75:0x02/0xBC
+
+# permanent wall
+F:76:0x02/0xBC
+
+# permanent wall
+F:77:0x02/0xBC
+
+# permanent wall
+F:78:0x02/0xBC
+
+# stream of shallow water
+F:84:0x0E/0xCB
+
+# pool of deep lava
+F:85:0x04/0xCB
+
+# stream of shallow lava
+F:86:0x0C/0xCB
+
+# dark pit
+F:87:0x08/0xD0
+
+# dirt
+F:88:0x07/0xB5
+
+# patch of grass
+F:89:0x0D/0xC9
+
+# ice
+F:90:0x01/0xD0
+
+# sand
+F:91:0x0F/0xB5
+
+# dead tree
+F:92:0x08/0xCC
+
+# ash
+F:93:0x02/0xB5
+
+# mud
+F:94:0x07/0xB5
+
+# ice wall
+F:95:0x01/0xCF
+
+# tree
+F:96:0x05/0xCC
+
+# mountain chain
+F:97:0x02/0xCE
+
+# sandwall
+F:98:0x0F/0xCF
+
+# sandwall
+F:99:0x0F/0xCF
+
+# sandwall with treasure
+F:100:0x03/0xCF
+
+# high mountain chain
+F:101:0x01/0xCE
+
+# nether mist
+F:102:0x0A/0xCA
+
+# Void Jumpgate
+F:160:0x0A/0xB9
+
+# Altar of Being
+F:161:0x09/0xD1
+
+# Altar of Winds
+F:162:0x0E/0xD1
+
+# Altar of Force
+F:163:0x0C/0xD1
+
+# Altar of Darkness
+F:164:0x08/0xD1
+
+# Altar of Nature
+# F:165:0x05/0x30
+
+# floor
+F:172:0x01/0xB5
+
+# Underground Tunnel
+F:173:0x02/0xB9
+
+# stream of tainted water
+F:174:0x0A/0xCB
+
+# monster trap
+F:175:0x0A/0xC6
+
+# Void Jumpgate
+F:176:0x0A/0xB9
+
+# lava wall
+F:177:0x0C/0xBC
+
+# Great Fire
+F:178:0x0C/0xCB
+
+# path to the next area
+F:179:0x01/0xB7
+
+# path to the previous area
+F:180:0x01/0xB6
+
+# field
+F:181:0x05/0xB5
+
+# Ekkaia, the Encircling Sea
+F:182:0x06/0xCB
+
+# pool of deep water
+F:187:0x06/0xCB
+
+# glass wall
+F:188:0x0E/0xD0
+
+# illusion wall
+F:189:0x02/0xBC
+
+# Grass roof
+F:190:0x0F/0xCA
+
+# grass roof top
+F:191:0x0F/0xCA
+
+# grass roof chimney
+F:192:0x0F/0xCA
+
+# brick roof
+F:193:0x04/0xCF
+
+# brick roof top
+F:194:0x04/0xCF
+
+# brick roof chimney
+F:195:0x04/0xCF
+
+# window
+F:196:0x01/0x4F
+
+# small window
+F:197:0x01/0x6F
+
+# rain barrel
+F:198:0x02/0xBC
+
+# grass with flowers
+F:199:0x04/0xC9
+
+# cobblestone road
+F:200:0x02/0xB5
+
+# cobblestone with outlet
+F:201:0x02/0xB5
+
+# small tree
+F:202:0x0D/0xCD
+
+# town
+F:203:0x01/0xC5
+
+# Underground Tunnel
+F:204:0x02/0xB9
+
+# a blazing fire
+F:205:0x0C/0xCB
+
+# pile of rubble
+F:206:0x02/0xBB
+
+# rocky ground
+F:207:0x02/0xB5
+
+# cloud-like vapour
+F:208:0x09/0xCA
+
+# condensing water
+F:209:0x0E/0xCB
+
+# dense mist
+F:210:0x01/0xCA
+
+# hail-stone wall
+F:211:0x09/0xBC
+
+
+##### Building attr/char definitions #####
+
+# General Store
+B:0:0x0F/0xBD
+
+# Armoury
+B:1:0x02/0xBE
+
+# Weaponsmith
+B:2:0x01/0xBF
+
+# Temple
+B:3:0x05/0xC0
+
+# Alchemy shop
+B:4:0x06/0xC1
+
+# Magic shop
+B:5:0x04/0xC4
+
+# Black Market
+B:6:0x08/0xC3
+
+# Home
+B:7:0x0B/0xB8
+
+# Book Store
+B:8:0x03/0xC2
+
+# Pet Shop
+B:9:0x06/0xB8
+
+# Mayor's Office
+B:10:0x03/0xB8
+
+# Inn
+B:11:0x01/0xB8
+
+# The Soothsayer
+B:12:0x0E/0xB8
+
+# Library
+B:13:0x0F/0xC2
+
+# Castle
+B:14:0x03/0xB8
+
+# Casino
+B:15:0x02/0xB8
+
+# Beastmaster Shanty
+B:16:0x05/0xB8
+
+# Fighters Hall
+B:17:0x02/0xB8
+
+# Tower of Magery
+B:18:0x06/0xB8
+
+# Inner Temple
+B:19:0x0D/0xC0
+
+# Paladins Guild
+B:20:0x05/0xB8
+
+# Rangers Guild
+B:21:0x07/0xB8
+
+# Thunderlords' Nest
+B:22:0x0F/0xB8
+
+# The Mirror
+B:23:0x0F/0xB8
+
+# Seat of Ruling
+B:24:0x0F/0xCC
+
+# Wizards Spire
+B:25:0x0F/0xB8
+
+# Priests Circle
+B:26:0x03/0xB8
+
+# Tower of the King
+B:27:0x0F/0xB8
+
+# Library
+B:28:0x0F/0xC2
+
+# The White Tree
+B:29:0x01/0xCC
+
+# Craftsmaster
+B:30:0x02/0xB8
+
+# Earth-Dome (Nature)
+B:31:0x0F/0xB8
+
+# Minstrels Haven
+B:32:0x0F/0xB8
+
+# Star-Dome
+B:33:0x0F/0xB8
+
+# Valarin Temple
+B:34:0x0F/0xC0
+
+# Sea-Dome
+B:35:0x0F/0xB8
+
+# The Golden Flower
+B:36:0x0F/0xB8
+
+# The Fountain
+B:37:0x0F/0xC8
+
+# Axe Smith
+B:38:0x01/0xBF
+
+# Hafted Smith
+B:39:0x01/0xBF
+
+# Polearm Smith
+B:40:0x01/0xBF
+
+# Sword Smith
+B:41:0x01/0xBF
+
+# Rare Jewelry Shop
+B:42:0x0A/0xC4
+
+# Jewelry Shop
+B:43:0x0B/0xC4
+
+# Footwear Shop
+B:44:0x04/0xBE
+
+# Rare Footwear Shop
+B:45:0x04/0xBE
+
+# Library
+B:46:0x0B/0xC2
+
+# Forbidden Library
+B:47:0x0A/0xC2
+
+# Expensive Black Market
+B:48:0x0A/0xC3
+
+# Common Shop
+B:49:0x0F/0xBD
+
+# Dragon Hunter
+B:50:0x0A/0xBE
+
+# Speed Ring Market
+B:51:0x0D/0xC4
+
+# Scribe
+B:52:0x0E/0xC1
+
+# Potion Store
+B:53:0x0E/0xC1
+
+# Recaller
+B:54:0x06/0xB8
+
+# Master Archer
+B:55:0x05/0xBF
+
+# Merchants Guild
+B:56:0x05/0xB8
+
+# The Mathom-house
+B:57:0x05/0xB8
+
+# The Prancing Pony
+B:58:0x01/0xB8
+
+# Mining Supply store
+B:59:0x02/0xB8
+
+
+##### Object attr/char definitions #####
+
+# something
+# K:0:0x01/0x26
+
+# Blindness
+K:1:0x00/0xEE
+
+# Paranoia
+K:2:0x00/0xEE
+
+# Confusion
+K:3:0x00/0xEE
+
+# Hallucination
+K:4:0x00/0xEE
+
+# Cure Poison
+K:5:0x00/0xEE
+
+# Cure Blindness
+K:6:0x00/0xEE
+
+# Cure Paranoia
+K:7:0x00/0xEE
+
+# Cure Confusion
+K:8:0x00/0xEE
+
+# Weakness
+K:9:0x00/0xEE
+
+# Unhealth
+K:10:0x00/0xEE
+
+# Restore Constitution
+K:11:0x00/0xEE
+
+# Restoring
+K:12:0x00/0xEE
+
+# Stupidity
+K:13:0x00/0xEE
+
+# Naivety
+K:14:0x00/0xEE
+
+# Poison
+K:15:0x00/0xEE
+
+# Sickness
+K:16:0x00/0xEE
+
+# Paralysis
+K:17:0x00/0xEE
+
+# Restore Strength
+K:18:0x00/0xEE
+
+# Disease
+K:19:0x00/0xEE
+
+# Cure Serious Wounds
+K:20:0x00/0xEE
+
+# & Ration~ of Food
+K:21:0x0F/0xF2
+
+# & Hard Biscuit~
+K:22:0x0F/0xF2
+
+# & Strip~ of Venison
+K:23:0x07/0xF2
+
+# & Slime Mold~
+K:24:0x05/0xF2
+
+# & Lembas~
+K:25:0x0E/0xF2
+
+# & Pint~ of Fine Ale
+K:26:0x0B/0xED
+
+# & Pint~ of Fine Wine
+K:27:0x04/0xED
+
+# & Mattock~
+K:28:0x08/0xD9
+
+# & Blue Stone~
+K:29:0x0E/0xE7
+
+# & Broken Dagger~
+K:30:0x08/0xDC
+
+# & Bastard Sword~
+K:31:0x09/0xDC
+
+# & Scimitar~
+K:32:0x09/0xDC
+
+# & Tulwar~
+K:33:0x09/0xDC
+
+# & Broad Sword~
+K:34:0x09/0xDC
+
+# & Short Sword~
+K:35:0x09/0xDC
+
+# & Blade~ of Chaos
+K:36:0x0A/0xDC
+
+# & Two-Handed Sword~
+K:37:0x09/0xDC
+
+# & Main Gauche~
+K:38:0x09/0xDC
+
+# & Cutlass~
+K:39:0x09/0xDC
+
+# & Executioner's Sword~
+K:40:0x04/0xDC
+
+# & Katana~
+K:41:0x09/0xDC
+
+# & Long Sword~
+K:42:0x09/0xDC
+
+# & Dagger~
+K:43:0x09/0xDC
+
+# & Rapier~
+K:44:0x09/0xDC
+
+# & Sabre~
+K:45:0x09/0xDC
+
+# & Small Sword~
+K:46:0x09/0xDC
+
+# & Broken Sword~
+K:47:0x08/0xDC
+
+# & Ball-and-Chain~
+K:48:0x08/0xDA
+
+# & Whip~
+K:49:0x08/0xDA
+
+# & Flail~
+K:50:0x08/0xDA
+
+# & Two-Handed Flail~
+K:51:0x0B/0xDA
+
+# & Morning Star~
+K:52:0x08/0xDA
+
+# & Mace~
+K:53:0x08/0xDA
+
+# & Quarterstaff~
+K:54:0x0F/0xDA
+
+# & War Hammer~
+K:55:0x08/0xDA
+
+# & Lead-Filled Mace~
+K:56:0x08/0xDA
+
+# & Mace~ of Disruption
+K:57:0x0A/0xDA
+
+# & Lucerne Hammer~
+K:58:0x0E/0xDA
+
+# & Beaked Axe~
+K:59:0x02/0xDB
+
+# & Glaive~
+K:60:0x02/0xDB
+
+# & Halberd~
+K:61:0x02/0xDB
+
+# & Awl-Pike~
+K:62:0x02/0xDB
+
+# & Pike~
+K:63:0x02/0xDB
+
+# & Spear~
+K:64:0x02/0xDB
+
+# & Trident~
+K:65:0x0B/0xDB
+
+# & Lance~
+K:66:0x02/0xDB
+
+# & Great Axe~
+K:67:0x02/0xDB
+
+# & Battle Axe~
+K:68:0x02/0xDB
+
+# & Lochaber Axe~
+K:69:0x08/0xDB
+
+# & Broad Axe~
+K:70:0x02/0xDB
+
+# & Scythe~
+K:71:0x02/0xDB
+
+# & Scythe~ of Slicing
+K:72:0x04/0xDB
+
+# & Short Bow~
+K:73:0x0F/0xD8
+
+# & Long Bow~
+K:74:0x0F/0xD8
+
+# & Light Crossbow~
+K:75:0x02/0xD8
+
+# & Heavy Crossbow~
+K:76:0x02/0xD8
+
+# & Sling~
+K:77:0x07/0xD8
+
+# & Arrow~
+K:78:0x0F/0xD7
+
+# & Seeker Arrow~
+K:79:0x0D/0xD7
+
+# & Bolt~
+K:80:0x02/0xD7
+
+# & Seeker Bolt~
+K:81:0x0E/0xD7
+
+# & Rounded Pebble~
+K:82:0x02/0xD6
+
+# & Iron Shot~
+K:83:0x02/0xD6
+
+# & Shovel~
+K:84:0x02/0xD9
+
+# & Gnomish Shovel~
+K:85:0x0D/0xD9
+
+# & Dwarven Shovel~
+K:86:0x0E/0xD9
+
+# & Pick~
+K:87:0x02/0xD9
+
+# & Orcish Pick~
+K:88:0x05/0xD9
+
+# & Dwarven Pick~
+K:89:0x06/0xD9
+
+# & Elven Cloak~
+K:90:0x0D/0xE2
+
+# & Pair~ of Soft Leather Boots
+K:91:0x0F/0xDD
+
+# & Pair~ of Hard Leather Boots
+K:92:0x0F/0xDD
+
+# & Pair~ of Metal Shod Boots
+K:93:0x02/0xDD
+
+# & Hard Leather Cap~
+K:94:0x07/0xDF
+
+# & Metal Cap~
+K:95:0x02/0xDF
+
+# & Iron Helm~
+K:96:0x02/0xDF
+
+# & Steel Helm~
+K:97:0x09/0xDF
+
+# & Iron Crown~
+K:98:0x02/0xE0
+
+# & Golden Crown~
+K:99:0x0B/0xE0
+
+# & Jewel Encrusted Crown~
+K:100:0x0A/0xE0
+
+# & Robe~
+K:101:0x06/0xE3
+
+# & Filthy Rag~
+K:102:0x08/0xE3
+
+# Soft Leather Armour~
+K:103:0x0F/0xE3
+
+# Soft Studded Leather~
+K:104:0x0F/0xE3
+
+# Hard Leather Armour~
+K:105:0x0F/0xE3
+
+# Hard Studded Leather~
+K:106:0x0F/0xE3
+
+# Leather Scale Mail~
+K:107:0x0F/0xE3
+
+# Metal Scale Mail~
+K:108:0x02/0xE4
+
+# Chain Mail~
+K:109:0x02/0xE4
+
+# Rusty Chain Mail~
+K:110:0x04/0xE4
+
+# Augmented Chain Mail~
+K:111:0x02/0xE4
+
+# Bar Chain Mail~
+K:112:0x02/0xE4
+
+# Metal Brigandine Armour~
+K:113:0x02/0xE4
+
+# Partial Plate Armour~
+K:114:0x09/0xE4
+
+# Metal Lamellar Armour~
+K:115:0x09/0xE4
+
+# Full Plate Armour~
+K:116:0x09/0xE4
+
+# Ribbed Plate Armour~
+K:117:0x09/0xE4
+
+# Adamantite Plate Mail~
+K:118:0x0D/0xE4
+
+# Mithril Plate Mail~
+K:119:0x0E/0xE4
+
+# Mithril Chain Mail~
+K:120:0x0E/0xE4
+
+# Double Chain Mail~
+K:121:0x02/0xE4
+
+# & Shield~ of Deflection
+K:122:0x0E/0xE1
+
+# & Cloak~
+K:123:0x05/0xE2
+
+# & Shadow Cloak~
+K:124:0x08/0xE2
+
+# & Set~ of Leather Gloves
+K:125:0x0F/0xDE
+
+# & Set~ of Gauntlets
+K:126:0x0F/0xDE
+
+# & Set~ of Cesti
+K:127:0x09/0xDE
+
+# & Small Leather Shield~
+K:128:0x0F/0xE1
+
+# & Large Leather Shield~
+K:129:0x0F/0xE1
+
+# & Small Metal Shield~
+K:130:0x02/0xE1
+
+# & Large Metal Shield~
+K:131:0x02/0xE1
+
+# Strength
+K:132:0x00/0xE8
+
+# Dexterity
+K:133:0x00/0xE8
+
+# Constitution
+K:134:0x00/0xE8
+
+# Intelligence
+K:135:0x00/0xE8
+
+# Speed
+K:136:0x00/0xE8
+
+# Searching
+K:137:0x00/0xE8
+
+# Teleportation
+K:138:0x00/0xE8
+
+# Slow Digestion
+K:139:0x00/0xE8
+
+# Fire Resistance
+K:140:0x00/0xE8
+
+# Cold Resistance
+K:141:0x00/0xE8
+
+# Levitation
+K:142:0x00/0xE8
+
+# Poison Resistance
+K:143:0x00/0xE8
+
+# Free Action
+K:144:0x00/0xE8
+
+# Weakness
+K:145:0x00/0xE8
+
+# Flames
+K:146:0x00/0xE8
+
+# Acid
+K:147:0x00/0xE8
+
+# Ice
+K:148:0x00/0xE8
+
+# Woe
+K:149:0x00/0xE8
+
+# Stupidity
+K:150:0x00/0xE8
+
+# Damage
+K:151:0x00/0xE8
+
+# Accuracy
+K:152:0x00/0xE8
+
+# Protection
+K:153:0x00/0xE8
+
+# Aggravate Monster
+K:154:0x00/0xE8
+
+# See Invisible
+K:155:0x00/0xE8
+
+# Sustain Strength
+K:156:0x00/0xE8
+
+# Sustain Intelligence
+K:157:0x00/0xE8
+
+# Sustain Wisdom
+K:158:0x00/0xE8
+
+# Sustain Constitution
+K:159:0x00/0xE8
+
+# Sustain Dexterity
+K:160:0x00/0xE8
+
+# Sustain Charisma
+K:161:0x00/0xE8
+
+# Slaying
+K:162:0x00/0xE8
+
+# Brilliance
+K:163:0x00/0xE7
+
+# Charisma
+K:164:0x00/0xE7
+
+# Searching
+K:165:0x00/0xE7
+
+# Teleportation
+K:166:0x00/0xE7
+
+# Slow Digestion
+K:167:0x00/0xE7
+
+# Acid Resistance
+K:168:0x00/0xE7
+
+# Adornment
+K:169:0x00/0xE7
+
+# Double Ring Mail~
+K:170:0x02/0xE4
+
+# the Magi
+K:171:0x00/0xE7
+
+# Doom
+K:172:0x00/0xE7
+
+# Enchant Weapon To-Hit
+K:173:0x00/0xEC
+
+# Enchant Weapon To-Dam
+K:174:0x00/0xEC
+
+# Enchant Armor
+K:175:0x00/0xEC
+
+# Identify
+K:176:0x00/0xEC
+
+# *Identify*
+K:177:0x00/0xEC
+
+# Rumour
+K:178:0x00/0xEC
+
+# Chaos
+K:179:0x00/0xEC
+
+# Remove Curse
+K:180:0x00/0xEC
+
+# Light
+K:181:0x00/0xEC
+
+# Fire
+K:182:0x00/0xEC
+
+# Ice
+K:183:0x00/0xEC
+
+# Summon Monster
+K:184:0x00/0xEC
+
+# Phase Door
+K:185:0x00/0xEC
+
+# Teleportation
+K:186:0x00/0xEC
+
+# Teleport Level
+K:187:0x00/0xEC
+
+# Monster Confusion
+K:188:0x00/0xEC
+
+# Magic Mapping
+K:189:0x00/0xEC
+
+# Rune of Protection
+K:190:0x00/0xEC
+
+# *Remove Curse*
+K:191:0x00/0xEC
+
+# Treasure Detection
+K:192:0x00/0xEC
+
+# Object Detection
+K:193:0x00/0xEC
+
+# Trap Detection
+K:194:0x00/0xEC
+
+# & Sheaf Arrow~
+K:195:0x03/0xD7
+
+# & Mithril Shot~
+K:196:0x0E/0xD6
+
+# Door/Stair Location
+K:197:0x00/0xEC
+
+# Acquirement
+K:198:0x00/0xEC
+
+# *Acquirement*
+K:199:0x00/0xEC
+
+# Mass Genocide
+K:200:0x00/0xEC
+
+# Detect Invisible
+K:201:0x00/0xEC
+
+# Aggravate Monster
+K:202:0x00/0xEC
+
+# Trap Creation
+K:203:0x00/0xEC
+
+# Trap/Door Destruction
+K:204:0x00/0xEC
+
+# Artifact Creation
+K:205:0x00/0xEC
+
+# Recharging
+K:206:0x00/0xEC
+
+# Genocide
+K:207:0x00/0xEC
+
+# Darkness
+K:208:0x00/0xEC
+
+# Protection from Evil
+K:209:0x00/0xEC
+
+# Satisfy Hunger
+K:210:0x00/0xEC
+
+# Dispel Undead
+K:211:0x00/0xEC
+
+# *Enchant Weapon*
+K:212:0x00/0xEC
+
+# Curse Weapon
+K:213:0x00/0xEC
+
+# *Enchant Armor*
+K:214:0x00/0xEC
+
+# Curse Armor
+K:215:0x00/0xEC
+
+# Summon Undead
+K:216:0x00/0xEC
+
+# Blessing
+K:217:0x00/0xEC
+
+# Holy Chant
+K:218:0x00/0xEC
+
+# Holy Prayer
+K:219:0x00/0xEC
+
+# Word of Recall
+K:220:0x00/0xEC
+
+# *Destruction*
+K:221:0x00/0xEC
+
+# Slime Mold Juice
+K:222:0x00/0xED
+
+# Apple Juice
+K:223:0x00/0xED
+
+# Water
+K:224:0x00/0xED
+
+# Strength
+K:225:0x00/0xED
+
+# Weakness
+K:226:0x00/0xED
+
+# Restore Strength
+K:227:0x00/0xED
+
+# Intelligence
+K:228:0x00/0xED
+
+# Stupidity
+K:229:0x00/0xED
+
+# Restore Intelligence
+K:230:0x00/0xED
+
+# Wisdom
+K:231:0x00/0xED
+
+# Naivety
+K:232:0x00/0xED
+
+# Restore Wisdom
+K:233:0x00/0xED
+
+# Charisma
+K:234:0x00/0xED
+
+# Ugliness
+K:235:0x00/0xED
+
+# Restore Charisma
+K:236:0x00/0xED
+
+# Curing
+K:237:0x00/0xED
+
+# Invulnerability
+K:238:0x00/0xED
+
+# New Life
+K:239:0x00/0xED
+
+# Cure Serious Wounds
+K:240:0x00/0xED
+
+# Cure Critical Wounds
+K:241:0x00/0xED
+
+# Healing
+K:242:0x00/0xED
+
+# Constitution
+K:243:0x00/0xED
+
+# Experience
+K:244:0x00/0xED
+
+# Sleep
+K:245:0x00/0xED
+
+# Blindness
+K:246:0x00/0xED
+
+# Booze
+K:247:0x00/0xED
+
+# Poison
+K:248:0x00/0xED
+
+# Speed
+K:249:0x00/0xED
+
+# Slowness
+K:250:0x00/0xED
+
+# Dexterity
+K:251:0x00/0xED
+
+# Restore Dexterity
+K:252:0x00/0xED
+
+# Restore Constitution
+K:253:0x00/0xED
+
+# Lose Memories
+K:254:0x00/0xED
+
+# Salt Water
+K:255:0x00/0xED
+
+# Enlightenment
+K:256:0x00/0xED
+
+# Heroism
+K:257:0x00/0xED
+
+# Berserk Strength
+K:258:0x00/0xED
+
+# Boldness
+K:259:0x00/0xED
+
+# Restore Life Levels
+K:260:0x00/0xED
+
+# Resist Heat
+K:261:0x00/0xED
+
+# Resist Cold
+K:262:0x00/0xED
+
+# Detect Invisible
+K:263:0x00/0xED
+
+# Slow Poison
+K:264:0x00/0xED
+
+# Neutralise Poison
+K:265:0x00/0xED
+
+# Restore Mana
+K:266:0x00/0xED
+
+# Infra-vision
+K:267:0x00/0xED
+
+# Resistance
+K:268:0x00/0xED
+
+# Spell
+K:269:0x00/0xEA
+
+# Manathrust
+K:270:0x00/0xEA
+
+# Fireflash
+K:271:0x00/0xEA
+
+# Firewall
+K:272:0x00/0xEA
+
+# Tidal Wave
+K:273:0x00/0xEA
+
+# Ice Storm
+K:274:0x00/0xEA
+
+# Noxious Cloud
+K:275:0x00/0xEA
+
+# Poison Blood
+K:276:0x00/0xEA
+
+# Thunderstorm
+K:277:0x00/0xEA
+
+# Dig
+K:278:0x00/0xEA
+
+# Stone Prison
+K:279:0x00/0xEA
+
+# Strike
+K:280:0x00/0xEA
+
+# Teleport Away
+K:281:0x00/0xEA
+
+# Summon Animal
+K:282:0x00/0xEA
+
+# Magelock
+K:283:0x00/0xEA
+
+# Slow Monster
+K:284:0x00/0xEA
+
+# Essence of Speed
+K:285:0x00/0xEA
+
+# Banishment
+K:286:0x00/0xEA
+
+# Disperse Magic
+K:287:0x00/0xEA
+
+# Charm
+K:288:0x00/0xEA
+
+# Confuse
+K:289:0x00/0xEA
+
+# Demon Blade
+K:290:0x00/0xEA
+
+# Heal Monster
+K:291:0x00/0xEA
+
+# Haste Monster
+K:292:0x00/0xEA
+
+# & Flight Arrow~
+K:293:0x0B/0xD7
+
+# Spell
+K:300:0x00/0xE9
+
+# Nothing
+K:301:0x00/0xE9
+
+# Globe of Light
+K:302:0x00/0xE9
+
+# Fiery Shield
+K:303:0x00/0xE9
+
+# Remove Curses
+K:304:0x00/0xE9
+
+# Wings of Winds
+K:305:0x00/0xE9
+
+# Shake
+K:306:0x00/0xE9
+
+# Disarm
+K:307:0x00/0xE9
+
+# Teleportation
+K:308:0x00/0xE9
+
+# Probability Travel
+K:309:0x00/0xE9
+
+# Recovery
+K:310:0x00/0xE9
+
+# Healing
+K:311:0x00/0xE9
+
+# Vision
+K:312:0x00/0xE9
+
+# Identify
+K:313:0x00/0xE9
+
+# Sense Hidden
+K:314:0x00/0xE9
+
+# Reveal Ways
+K:315:0x00/0xE9
+
+# Sense Monsters
+K:316:0x00/0xE9
+
+# Genocide
+K:317:0x00/0xE9
+
+# Summon
+K:318:0x00/0xE9
+
+# Wish
+K:320:0x00/0xE9
+
+# Mana
+K:321:0x00/0xE9
+
+# & Tome~ of Magical Energy
+K:330:0x0E/0xEF
+
+# & Tome~ of the Eternal Flame
+K:331:0x0C/0xEF
+
+# & Tome~ of the Blowing Wind
+K:332:0x06/0xEF
+
+# & Tome~ of the Impenetrable Earth
+K:333:0x0F/0xEF
+
+# & Tome~ of the Everrunning Wave
+K:334:0x0E/0xEF
+
+# & Tome~ of Translocation
+K:335:0x0E/0xEF
+
+# & Tome~ of the Tree
+K:336:0x0D/0xEF
+
+# & Tome~ of Knowledge
+K:337:0x08/0xEF
+
+# & Small wooden chest~
+K:338:0x02/0xD5
+
+# & Large wooden chest~
+K:339:0x02/0xD5
+
+# & Small iron chest~
+K:340:0x02/0xD5
+
+# & Large iron chest~
+K:341:0x02/0xD5
+
+# & Small steel chest~
+K:342:0x02/0xD5
+
+# & Large steel chest~
+K:343:0x02/0xD5
+
+# & Ruined chest~
+K:344:0x02/0xD5
+
+# & Iron Spike~
+K:345:0x09/0xD4
+
+# & Wooden Torch~
+K:346:0x07/0xF1
+
+# & Brass Lantern~
+K:347:0x0F/0xE6
+
+# & Flask~ of oil
+K:348:0x0B/0xED
+
+# & Empty Bottle~
+K:349:0x01/0xED
+
+# Havoc
+K:350:0x00/0xEB
+
+# Door/Stair Location
+K:351:0x00/0xEB
+
+# Trap Location
+K:352:0x00/0xEB
+
+# Probing
+K:353:0x00/0xEB
+
+# Recall
+K:354:0x00/0xEB
+
+# Illumination
+K:355:0x00/0xEB
+
+# Light
+K:356:0x00/0xEB
+
+# Lightning Bolts
+K:357:0x00/0xEB
+
+# Frost Bolts
+K:358:0x00/0xEB
+
+# Fire Bolts
+K:359:0x00/0xEB
+
+# Polymorph
+K:360:0x00/0xEB
+
+# Slow Monster
+K:361:0x00/0xEB
+
+# Sleep Monster
+K:362:0x00/0xEB
+
+# Drain Life
+K:363:0x00/0xEB
+
+# Teleport Other
+K:364:0x00/0xEB
+
+# Disarming
+K:365:0x00/0xEB
+
+# Lightning Balls
+K:366:0x00/0xEB
+
+# Cold Balls
+K:367:0x00/0xEB
+
+# Fire Balls
+K:368:0x00/0xEB
+
+# Acid Balls
+K:369:0x00/0xEB
+
+# Acid Bolts
+K:370:0x00/0xEB
+
+# Enlightenment
+K:371:0x00/0xEB
+
+# Perception
+K:372:0x00/0xEB
+
+# Curing
+K:373:0x00/0xEB
+
+# Healing
+K:374:0x00/0xEB
+
+# Detection
+K:375:0x00/0xEB
+
+# Restoration
+K:376:0x00/0xEB
+
+# Speed
+K:377:0x00/0xEB
+
+# Spell
+K:378:0x00/0xE8
+
+# Spell
+K:379:0x00/0xE7
+
+# & Broken Skull~
+K:391:0x01/0xD3
+
+# & Broken Bone~
+K:392:0x01/0xD3
+
+# & Canine Skeleton~
+K:393:0x01/0xD3
+
+# & Rodent Skeleton~
+K:394:0x01/0xD3
+
+# & Human Skeleton~
+K:395:0x01/0xD3
+
+# & Dwarf Skeleton~
+K:396:0x01/0xD3
+
+# & Elf Skeleton~
+K:397:0x01/0xD3
+
+# & Gnome Skeleton~
+K:398:0x01/0xD3
+
+# & Great Hammer~
+K:399:0x08/0xDA
+
+# Black Dragon Scale Mail~
+K:400:0x02/0xE5
+
+# Blue Dragon Scale Mail~
+K:401:0x06/0xE5
+
+# White Dragon Scale Mail~
+K:402:0x01/0xE5
+
+# Red Dragon Scale Mail~
+K:403:0x04/0xE5
+
+# Green Dragon Scale Mail~
+K:404:0x05/0xE5
+
+# Multi-Hued Dragon Scale Mail~
+K:405:0x0A/0xE5
+
+# Pseudo Dragon Scale Mail~
+K:406:0x0A/0xE5
+
+# Law Dragon Scale Mail~
+K:407:0x0E/0xE5
+
+# Bronze Dragon Scale Mail~
+K:408:0x0F/0xE5
+
+# Gold Dragon Scale Mail~
+K:409:0x0B/0xE5
+
+# Chaos Dragon Scale Mail~
+K:410:0x0A/0xE5
+
+# Balance Dragon Scale Mail~
+K:411:0x0A/0xE5
+
+# Power Dragon Scale Mail~
+K:412:0x0A/0xE5
+
+# & Dragon Helm~
+K:413:0x0D/0xDF
+
+# & Dragon Shield~
+K:414:0x0D/0xE1
+
+# Death
+K:415:0x00/0xED
+
+# Ruination
+K:416:0x00/0xED
+
+# Detonations
+K:417:0x00/0xED
+
+# Augmentation
+K:418:0x00/0xED
+
+# *Healing*
+K:419:0x00/0xED
+
+# Life
+K:420:0x00/0xED
+
+# Self Knowledge
+K:421:0x00/0xED
+
+# *Enlightenment*
+K:422:0x00/0xED
+
+# Fear Resistance
+K:425:0x00/0xE8
+
+# Light and Darkness Resistance
+K:426:0x00/0xE8
+
+# Nether Resistance
+K:427:0x00/0xE8
+
+# Nexus Resistance
+K:428:0x00/0xE8
+
+# Sound Resistance
+K:429:0x00/0xE8
+
+# Confusion Resistance
+K:430:0x00/0xE8
+
+# Shard Resistance
+K:431:0x00/0xE8
+
+# Disenchantment Resistance
+K:432:0x00/0xE8
+
+# Chaos Resistance
+K:433:0x00/0xE8
+
+# Blindness Resistance
+K:434:0x00/0xE8
+
+# Lordly Protection
+K:435:0x00/0xE8
+
+# Extra Attacks
+K:436:0x00/0xE8
+
+# Cure Light Wounds
+K:437:0x00/0xED
+
+# Clumsiness
+K:438:0x00/0xED
+
+# Sickliness
+K:439:0x00/0xED
+
+# Map of Bree
+K:440:0x02/0xEC
+
+# Map of Gondolin
+K:441:0x02/0xEC
+
+# Map of Lothlorien
+K:442:0x02/0xEC
+
+# Map of Minas Anor
+K:443:0x02/0xEC
+
+# & Silver Arrow~
+K:465:0x09/0xD7
+
+# & Silver Bolt~
+K:466:0x01/0xD7
+
+# Lightning Resistance
+K:467:0x00/0xE7
+
+# Wisdom
+K:468:0x00/0xE7
+
+# Regeneration
+K:469:0x00/0xE7
+
+# Infravision
+K:470:0x00/0xE7
+
+# Devotion
+K:471:0x00/0xE7
+
+# Weaponmastery
+K:472:0x00/0xE7
+
+# Trickery
+K:473:0x00/0xE7
+
+# Telepathy
+K:474:0x00/0xE7
+
+# Sustenance
+K:475:0x00/0xE7
+
+# & Palantir~
+K:476:0x0B/0xF0
+
+# & Elfstone~
+K:477:0x05/0xE7
+
+# & Jewel~
+K:478:0x01/0xE7
+
+# & Ring~
+K:479:0x00/0xE8
+
+# copper
+K:480:0x07/0xF3
+
+# copper
+K:481:0x07/0xF3
+
+# copper
+K:482:0x07/0xF3
+
+# silver
+K:483:0x02/0xF3
+
+# silver
+K:484:0x02/0xF3
+
+# silver
+K:485:0x02/0xF3
+
+# garnets
+K:486:0x04/0xF4
+
+# garnets
+K:487:0x04/0xF4
+
+# gold
+K:488:0x0B/0xF3
+
+# gold
+K:489:0x0B/0xF3
+
+# gold
+K:490:0x0B/0xF3
+
+# opals
+K:491:0x09/0xF4
+
+# sapphires
+K:492:0x06/0xF4
+
+# rubies
+K:493:0x04/0xF4
+
+# diamonds
+K:494:0x01/0xF4
+
+# emeralds
+K:495:0x05/0xF4
+
+# mithril
+K:496:0x0E/0xF3
+
+# adamantite
+K:497:0x0D/0xF3
+
+# & Mighty Hammer~
+K:498:0x08/0xDA
+
+# & Massive Iron Crown~
+K:499:0x08/0xE0
+
+# & Phial~
+K:500:0x0B/0xF0
+
+# & Star~
+K:501:0x0E/0xF0
+
+# & Arkenstone~
+K:502:0x0C/0xF0
+
+# & Amulet~
+K:503:0x00/0xE7
+
+# & Amulet~
+K:504:0x00/0xE7
+
+# & Necklace~
+K:505:0x00/0xE7
+
+# & Ring~
+K:506:0x00/0xE8
+
+# & Ring~
+K:507:0x00/0xE8
+
+# & Ring~
+K:508:0x00/0xE8
+
+# & Ring~
+K:509:0x00/0xE8
+
+# & Ring~
+K:510:0x00/0xE8
+
+# & Ring~
+K:511:0x0B/0xE8
+
+# Reflection
+K:520:0x00/0xE7
+
+# Anti-Magic
+K:521:0x00/0xE7
+
+# Anti-Teleportation
+K:522:0x00/0xE7
+
+# Resistance
+K:523:0x00/0xE7
+
+# & Zweihander~
+K:524:0x01/0xDC
+
+# & Dwarven Lantern~
+K:525:0x06/0xE6
+
+# Splint Mail~
+K:526:0x08/0xE4
+
+# & Everburning Torch~
+K:527:0x0C/0xF1
+
+# & Trifurcate Spear~
+K:528:0x03/0xDB
+
+# & Three Piece Rod~
+K:529:0x07/0xDA
+
+# & Feanorian Lamp~
+K:530:0x0E/0xE6
+
+# & Fur Cloak~
+K:531:0x09/0xE2
+
+# Water Curing
+K:532:0x00/0xED
+
+# & Hatchet~
+K:533:0x02/0xDB
+
+# Rhino Hide Armour~
+K:535:0x02/0xE3
+
+# Leather Jacket~
+K:536:0x0F/0xE3
+
+# & Sickle~
+K:537:0x02/0xDB
+
+# & Club~
+K:542:0x07/0xDA
+
+# & Broad Spear~
+K:543:0x01/0xDB
+
+# & Khopesh~
+K:544:0x09/0xDC
+
+# & Flamberge~
+K:545:0x09/0xDC
+
+# & Claymore~
+K:546:0x09/0xDC
+
+# & Espadon~
+K:547:0x09/0xDC
+
+# & Great Scimitar~
+K:548:0x09/0xDC
+
+# Arrow
+K:549:0x04/0xF8
+
+# Bolt
+K:550:0x03/0xF8
+
+# & Fauchard~
+K:551:0x02/0xDB
+
+# & Guisarme~
+K:552:0x02/0xDB
+
+# & Heavy Lance~
+K:553:0x02/0xDB
+
+# & Basillard~
+K:554:0x01/0xDC
+
+# Catapult
+K:555:0x0C/0xF8
+
+# Ring Mail~
+K:556:0x02/0xE4
+
+# Cord Armour~
+K:557:0x0B/0xE3
+
+# Paper Armour~
+K:558:0x01/0xE3
+
+# Padded Armour~
+K:559:0x0B/0xE3
+
+# Fumes
+K:560:0x0D/0xF8
+
+# Stone and Hide Armour~
+K:561:0x0F/0xE3
+
+# Magic
+K:562:0x05/0xF8
+
+# Device
+K:563:0x0A/0xF8
+
+# Nothing
+K:564:0x00/0xEC
+
+# Poison
+K:565:0x0D/0xF6
+
+# Nothing
+K:566:0x00/0xEA
+
+# Nothing
+K:567:0x00/0xE8
+
+# Nothing
+K:568:0x00/0xE9
+
+# Nothing
+K:569:0x00/0xEB
+
+# Explosion
+K:570:0x0D/0xF6
+
+# Teleport
+K:571:0x0D/0xF6
+
+# Nothing
+K:572:0x00/0xE7
+
+# & Blood~ of Life
+K:573:0x00/0xED
+
+# Cold
+K:574:0x0D/0xF6
+
+# Fire
+K:575:0x0D/0xF6
+
+# Acid
+K:576:0x0D/0xF6
+
+# & Mage Staff~
+K:577:0x0E/0xE9
+
+# Lightning
+K:578:0x00/0xE8
+
+# Life
+K:579:0x0D/0xF6
+
+# Confusion
+K:580:0x0D/0xF6
+
+# Light
+K:581:0x0D/0xF6
+
+# & Ring~
+K:582:0x0B/0xE8
+
+# Invisibility
+K:583:0x00/0xED
+
+# Chaos
+K:584:0x0D/0xF6
+
+# Corruption
+K:585:0x00/0xED
+
+# Invisibility
+K:586:0x00/0xE8
+
+# Time
+K:587:0x0D/0xF6
+
+# Deep Thoughts
+K:588:0x03/0xEC
+
+# More Deep Thoughts
+K:589:0x03/0xEC
+
+# Compendium of Deep Thoughts
+K:590:0x03/0xEC
+
+# Artifact Lore Vol. I
+K:591:0x03/0xEC
+
+# Artifact Lore Vol. II
+K:592:0x03/0xEC
+
+# Artifact Lore Vol. III
+K:593:0x03/0xEC
+
+# Monstrous Compendium 1
+K:594:0x03/0xEC
+
+# Monstrous Compendium 2
+K:595:0x03/0xEC
+
+# Monstrous Compendium 3
+K:596:0x03/0xEC
+
+# Monstrous Compendium 4
+K:597:0x03/0xEC
+
+# Monstrous Compendium 5
+K:598:0x03/0xEC
+
+# Monstrous Compendium 6
+K:599:0x03/0xEC
+
+# Monstrous Compendium 7
+K:600:0x03/0xEC
+
+# Monstrous Compendium 8
+K:601:0x03/0xEC
+
+# Monstrous Compendium 9
+K:602:0x03/0xEC
+
+# Monstrous Compendium 10
+K:603:0x03/0xEC
+
+# Monstrous Compendium 11
+K:604:0x03/0xEC
+
+# Abomination
+K:605:0x00/0xED
+
+# Shape of Wolf
+K:606:0x00/0xED
+
+# Shape of Ape
+K:607:0x00/0xED
+
+# Shape of Goat
+K:608:0x00/0xED
+
+# Shape of Insect
+K:609:0x00/0xED
+
+# Shape of Sparrow
+K:610:0x00/0xED
+
+# Shape of Ent
+K:611:0x00/0xED
+
+# Shape of Vampire
+K:612:0x00/0xED
+
+# Shape of Spider
+K:613:0x00/0xED
+
+# Shape of Mana ball
+K:614:0x00/0xED
+
+# Shape of Fire cloud
+K:615:0x00/0xED
+
+# Shape of Cold cloud
+K:616:0x00/0xED
+
+# Shape of Chaos cloud
+K:617:0x00/0xED
+
+# [Wolf]
+K:618:0x0B/0xE2
+
+# [Ape]
+K:619:0x0B/0xE2
+
+# [Goat]
+K:620:0x0B/0xE2
+
+# [Insect]
+K:621:0x0B/0xE2
+
+# [Sparrow]
+K:622:0x0B/0xE2
+
+# [Ent]
+K:623:0x0B/0xE2
+
+# [Vampire]
+K:624:0x0B/0xE2
+
+# [Spider]
+K:625:0x0B/0xE2
+
+# [Mana ball]
+K:626:0x0B/0xE2
+
+# [Fire cloud]
+K:627:0x0B/0xE2
+
+# [Cold cloud]
+K:628:0x0B/0xE2
+
+# [Chaos Cloud]
+K:629:0x0B/0xE2
+
+# [Ghost]
+K:630:0x0B/0xE2
+
+# [Kobold]
+K:631:0x0B/0xE2
+
+# [Dragon]
+K:632:0x0B/0xE2
+
+# [Demon]
+K:633:0x0B/0xE2
+
+# [Hound]
+K:634:0x0B/0xE2
+
+# [Quylthulg]
+K:635:0x0B/0xE2
+
+# [Maia]
+K:636:0x0B/0xE2
+
+# [Serpent]
+K:637:0x0B/0xE2
+
+# [Giant]
+K:638:0x0B/0xE2
+
+# [Vala]
+K:639:0x0B/0xE2
+
+# Magic
+K:640:0x0D/0xF6
+
+# corpse
+K:641:0x0F/0xD2
+
+# skeleton
+K:642:0x0F/0xD2
+
+# head
+K:643:0x0F/0xD2
+
+# skull
+K:644:0x0F/0xD2
+
+# raw meat
+K:645:0x0F/0xD2
+
+# & Thunderlord Coat~
+K:646:0x0B/0xE3
+
+# & Stone~
+K:647:0x05/0xF0
+
+# & Small Wooden Boomerang~
+K:648:0x0B/0xD8
+
+# & Wooden Boomerang~
+K:649:0x0B/0xD8
+
+# & Small Metal Boomerang~
+K:650:0x0B/0xD8
+
+# & metal Boomerang~
+K:651:0x0B/0xD8
+
+# & Anchor~
+K:652:0x0A/0xF0
+
+# & ~
+K:653:0x0B/0xD2
+
+# Summon Never-Moving Pet
+K:654:0x00/0xEC
+
+# Cure Light Insanity
+K:657:0x00/0xED
+
+# Cure Serious Insanity
+K:658:0x00/0xED
+
+# Cure Critical Insanity
+K:659:0x00/0xED
+
+# Cure Insanity
+K:660:0x00/0xED
+
+# & Phial~
+K:661:0x0B/0xF0
+
+# Random Artifact
+K:662:0x03/0xD3
+
+# Craftmanship
+K:663:0x00/0xEC
+
+# The One Ring
+K:664:0x02/0xEC
+
+# & Book~ of the Lays of the Heroes
+K:665:0x0B/0xEF
+
+# & Book~ of Sound Patterns
+K:666:0x0B/0xEF
+
+# & Flute~
+K:669:0x09/0xF7
+
+# & Drum~
+K:670:0x09/0xF7
+
+# & Harp~
+K:671:0x09/0xF7
+
+# & Banjo~
+K:672:0x09/0xF7
+
+# & Lute~
+K:673:0x09/0xF7
+
+# & Mandolin~
+K:674:0x09/0xF7
+
+# & Palantir~
+K:675:0x0B/0xF0
+
+# Egg
+K:676:0x09/0xD6
+
+# Reset Recall
+K:677:0x00/0xEC
+
+# Divination
+K:678:0x00/0xEC
+
+# Self
+K:679:0x06/0xF5
+
+# Ray
+K:680:0x06/0xF5
+
+# Sphere
+K:681:0x06/0xF5
+
+# Knowledge
+K:682:0x06/0xF5
+
+# Life
+K:683:0x08/0xF5
+
+# Fire
+K:684:0x04/0xF5
+
+# Cold
+K:685:0x06/0xF5
+
+# Lightning
+K:686:0x09/0xF5
+
+# Acid
+K:687:0x0E/0xF5
+
+# Element
+K:688:0x05/0xF5
+
+# Chaos
+K:689:0x0A/0xF5
+
+# Mind
+K:690:0x08/0xF5
+
+# Holding
+K:691:0x0E/0xF5
+
+# Arrow
+K:692:0x06/0xF5
+
+# Power Surge
+K:693:0x06/0xF5
+
+# Armageddon
+K:694:0x06/0xF5
+
+# Gravity
+K:695:0x0D/0xF5
+
+# Extra Life
+K:696:0x0D/0xF6
+
+# Undeath
+K:697:0x0D/0xF5
+
+# Protection
+K:698:0x0D/0xF5
+
+# & Horn~
+K:699:0x09/0xF7
+
+# & Ring~ of Precognition
+K:700:0x00/0xE8
+
+# & Sprig~ of Athelas
+K:701:0x05/0xF2
+
+# & Old Scroll~ of Deincarnation
+K:720:0x00/0xEC
+
+# & Dark Sword~
+K:721:0x08/0xDC
+
+# Numenorean for Beginners (I)
+K:722:0x02/0xEC
+
+# Numenorean for Beginners (II)
+K:723:0x02/0xEC
+
+# Advanced lessons of Numenorean
+K:724:0x02/0xEC
+
+# Advanced lessons of Sindarin
+K:725:0x02/0xEC
+
+# & Shard~ of Pottery
+K:726:0x04/0xD3
+
+# & Broken Stick~
+K:727:0x04/0xD3
+
+# & Book~ of Beginner Cantrips
+K:738:0x01/0xEF
+
+# & Book~ of Teleportation
+K:739:0x01/0xEF
+
+# & Book~ of Recall
+K:740:0x01/0xEF
+
+# & Book~ of Summoning
+K:741:0x01/0xEF
+
+# & Book~ of Fireflash
+K:742:0x01/0xEF
+
+# & Potion~ of Learning
+K:743:0x00/0xED
+
+# Spell
+K:749:0x00/0xEC
+
+# Khuzdul - The Hidden Tongue of the Dwarves
+K:751:0x02/0xEC
+
+# Nandorin for Dummies
+K:752:0x02/0xEC
+
+# Advanced Lessons of Orcish
+K:753:0x02/0xEC
+
+# Flying
+K:755:0x00/0xE8
+
+# & Tome~ of the Time
+K:756:0x06/0xEF
+
+# & Spellbook~ of #
+K:757:0x01/0xEF
+
+# & Tome~ of Meta Spells
+K:758:0x0A/0xEF
+
+# & Tome~ of the Mind
+K:759:0x0E/0xEF
+
+# & Holy Tome~ of Eru Iluvatar
+K:760:0x0D/0xEF
+
+# & Holy Tome~ of Manwe Sulimo
+K:761:0x0E/0xEF
+
+# & War Tome~ of Tulkas
+K:762:0x0C/0xEF
+
+# & Unholy Tome~ of the Hellflame
+K:763:0x0A/0xEF
+
+# & Corrupted Tome~ of Melkor
+K:764:0x08/0xEF
+
+# & Forest Tome~ of Yavanna
+K:768:0x0D/0xEF
+
+# & Ring~
+K:770:0x00/0xE8
+
+# [Earth]
+K:771:0x0C/0xEF
+
+# [Fire]
+K:772:0x0C/0xEF
+
+# [Air]
+K:773:0x04/0xEF
+
+# [Water]
+K:774:0x04/0xEF
+
+# [Mana]
+K:775:0x04/0xEF
+
+# Home Summoning
+K:776:0x00/0xEB
+
+# & Shadow Blade~
+K:777:0x08/0xDC
+
+# & Bluesteel Blade~
+K:778:0x06/0xDC
+
+# the Serpents
+K:779:0x0D/0xE7
+
+# Darkness
+K:780:0x0D/0xF6
+
+# Knowledge
+K:781:0x0D/0xF6
+
+# Force
+K:782:0x0D/0xF6
+
+# Lightning
+K:783:0x0D/0xF6
+
+# Mana
+K:784:0x0D/0xF6
+
+# Ring~ of Power
+K:785:0x00/0xE8
+
+# Climbing Set~
+K:786:0x0E/0xF8
+
+# Adventurer's Guide to Middle-earth
+K:787:0x03/0xEC
+
+# & Demonblade~
+K:788:0x0C/0xEF
+
+# & Demonshield~
+K:789:0x0C/0xEF
+
+# & Demonhorn~
+K:790:0x0C/0xEF
+
+# & Wooden Rod~ of#
+K:793:0x07/0xEB
+
+# & Copper Rod~ of#
+K:794:0x02/0xEB
+
+# & Iron Rod~ of#
+K:795:0x08/0xEB
+
+# & Moonstone Rod~ of#
+K:796:0x0F/0xEB
+
+# & Silver Rod~ of#
+K:797:0x02/0xEB
+
+# & Golden Rod~ of#
+K:798:0x0B/0xEB
+
+# & Mithril Rod~ of#
+K:799:0x0E/0xEB
+
+# & Adamantite Rod~ of#
+K:800:0x0A/0xEB
+
+# & Greater Ration~ of Health
+K:801:0x05/0xF2
+
+# & Crumpled Scroll~ of Mass Resurrection
+K:802:0x00/0xEC
+
+# & Cleaver~
+K:803:0x02/0xDB
+
+# & Light War Axe~
+K:804:0x02/0xDB
+
+# & Slaughter Axe~
+K:805:0x0D/0xDB
+
+# & Runestone~
+K:806:0x0A/0xF5
+
+# & Fortune cookie~
+K:807:0x0F/0xF2
+
+# Portable hole
+K:808:0x0E/0xF8
+
+# Critical Hits
+K:809:0x00/0xE8
+
+# & Wand~ of Digging of Thrain
+K:810:0x00/0xEA
+
+# & Gnarled Staff~ of Holy Fire of Mithrandir
+K:811:0x00/0xE9
+
+# Partial Totem
+K:812:0x0A/0xF8
+
+# True Totem
+K:813:0x0A/0xF8
+
+# & Piece~ of the Relic of Eru
+K:814:0x0A/0xD3
+
+# & Piece~ of the Relic of Manwe
+K:815:0x0A/0xD3
+
+# & Piece~ of the Relic of Tulkas
+K:816:0x0A/0xD3
+
+# & Piece~ of the Relic of Melkor
+K:817:0x0A/0xD3
+
+# & Piece~ of the Relic of Yavanna
+K:818:0x0A/0xD3
+
+
+##### Monster attr/char definitions #####
+
+# Player
+R:0:0x01/0x80
+
+# Filthy street urchin
+R:1:0x08/0xAE
+
+# Scrawny cat
+R:2:0x0F/0xA0
+
+# Sparrow
+R:3:0x0F/0x82
+
+# Chaffinch
+R:4:0x04/0x82
+
+# Wild rabbit
+R:5:0x0F/0xAC
+
+# Woodsman
+R:6:0x05/0xAE
+
+# Scruffy little dog
+R:7:0x0F/0x83
+
+# Farmer Maggot
+R:8:0x01/0xA2
+
+# Blubbering idiot
+R:9:0x09/0xAE
+
+# Boil-covered wretch
+R:10:0x05/0xAE
+
+# Village idiot
+R:11:0x0D/0xAE
+
+# Pitiful-looking beggar
+R:12:0x0F/0xAE
+
+# Mangy-looking leper
+R:13:0x07/0xAE
+
+# Agent of the black market
+R:14:0x06/0xAE
+
+# Singing, happy drunk
+R:15:0x0B/0xAE
+
+# Aimless-looking merchant
+R:16:0x03/0xAE
+
+# Mean-looking mercenary
+R:17:0x04/0xAE
+
+# Battle-scarred veteran
+R:18:0x0E/0xAE
+
+# Martti Ihrasaari
+R:19:0x01/0x90
+
+# Grey mold
+R:20:0x02/0xA7
+
+# Large white snake
+R:21:0x01/0x8A
+
+# Grey mushroom patch
+R:22:0x02/0xEE
+
+# Newt
+R:23:0x0B/0x92
+
+# Giant white centipede
+R:24:0x01/0x9D
+
+# White icky thing
+R:25:0x01/0xA3
+
+# Clear icky thing
+R:26:0x0E/0xA3
+
+# Giant white mouse
+R:27:0x01/0xAC
+
+# Large brown snake
+R:28:0x07/0x8A
+
+# Small kobold
+R:29:0x0B/0xA5
+
+# Kobold
+R:30:0x0D/0xA5
+
+# White worm mass
+R:31:0x01/0xB1
+
+# Floating eye
+R:32:0x03/0x9F
+
+# Rock lizard
+R:33:0x0F/0x92
+
+# Grid bug
+R:34:0x0A/0x89
+
+# Jackal
+R:35:0x0F/0x83
+
+# Soldier ant
+R:36:0x07/0x9B
+
+# Fruit bat
+R:37:0x03/0x9C
+
+# Insect swarm
+R:38:0x07/0x89
+
+# The Greater hell-beast
+R:39:0x02/0x95
+
+# Shrieker mushroom patch
+R:40:0x0C/0xEE
+
+# Blubbering icky thing
+R:41:0x09/0xA3
+
+# Metallic green centipede
+R:42:0x05/0x9D
+
+# Novice warrior
+R:43:0x07/0xAA
+
+# Novice rogue
+R:44:0x06/0xAA
+
+# Novice priest
+R:45:0x05/0xAA
+
+# Novice mage
+R:46:0x04/0xAA
+
+# Yellow mushroom patch
+R:47:0x0B/0xEE
+
+# White jelly
+R:48:0x01/0xA4
+
+# Giant black ant
+R:49:0x08/0x9B
+
+# Salamander
+R:50:0x03/0x92
+
+# White harpy
+R:51:0x01/0x88
+
+# Blue yeek
+R:52:0x06/0xB3
+
+# Grip, Farmer Maggot's dog
+R:53:0x01/0x83
+
+# Wolf, Farmer Maggot's dog
+R:54:0x01/0x83
+
+# Fang, Farmer Maggot's dog
+R:55:0x01/0x83
+
+# Giant green frog
+R:56:0x05/0x92
+
+# Freesia
+R:57:0x07/0xA0
+
+# Green worm mass
+R:58:0x05/0xB1
+
+# Large yellow snake
+R:59:0x0B/0x8A
+
+# Cave spider
+R:60:0x08/0x93
+
+# Crow
+R:61:0x02/0x82
+
+# Wild cat
+R:62:0x0F/0xA0
+
+# Smeagol
+R:63:0x0E/0xA2
+
+# Green ooze
+R:64:0x05/0xA4
+
+# Poltergeist
+R:65:0x02/0x87
+
+# Yellow jelly
+R:66:0x0B/0xA4
+
+# Metallic blue centipede
+R:67:0x06/0x9D
+
+# Raven
+R:68:0x08/0x82
+
+# Giant white louse
+R:69:0x01/0x89
+
+# Giant yellow centipede
+R:70:0x0B/0x9D
+
+# Black naga
+R:71:0x08/0xA8
+
+# Spotted mushroom patch
+R:72:0x03/0xEE
+
+# Silver jelly
+R:73:0x09/0xA4
+
+# Scruffy-looking hobbit
+R:74:0x02/0xA2
+
+# Giant white ant
+R:75:0x01/0x9B
+
+# Yellow mold
+R:76:0x0B/0xA7
+
+# Metallic red centipede
+R:77:0x04/0x9D
+
+# Yellow worm mass
+R:78:0x0B/0xB1
+
+# Clear worm mass
+R:79:0x0E/0xB1
+
+# Radiation eye
+R:80:0x0C/0x9F
+
+# Yellow light
+R:81:0x0B/0xF0
+
+# Cave lizard
+R:82:0x07/0x92
+
+# Novice ranger
+R:83:0x09/0xAA
+
+# Blue jelly
+R:84:0x06/0xA4
+
+# Creeping copper coins
+R:85:0x07/0xF3
+
+# Giant white rat
+R:86:0x09/0xAC
+
+# Snotling
+R:87:0x0F/0xA9
+
+# Swordfish
+R:88:0x09/0x7E
+
+# Blue worm mass
+R:89:0x06/0xB1
+
+# Large grey snake
+R:90:0x02/0x8A
+
+# Skeleton kobold
+R:91:0x09/0xAD
+
+# Ewok
+R:92:0x0D/0xA2
+
+# Novice mage
+R:93:0x04/0xAA
+
+# Green naga
+R:94:0x05/0xA8
+
+# Giant leech
+R:95:0x07/0xB1
+
+# Barracuda
+R:96:0x0D/0x7E
+
+# Novice paladin
+R:97:0x01/0xAA
+
+# Zog
+R:98:0x06/0xA2
+
+# Blue ooze
+R:99:0x06/0xA4
+
+# Green glutton ghost
+R:100:0x05/0x87
+
+# Green jelly
+R:101:0x05/0xA4
+
+# Large kobold
+R:102:0x06/0xA5
+
+# Grey icky thing
+R:103:0x02/0xA3
+
+# Disenchanter eye
+R:104:0x0A/0x9F
+
+# Red worm mass
+R:105:0x04/0xB1
+
+# Copperhead snake
+R:106:0x03/0x8A
+
+# Death sword
+R:107:0x09/0xDC
+
+# Purple mushroom patch
+R:108:0x0A/0xEE
+
+# Novice priest
+R:109:0x05/0xAA
+
+# Novice warrior
+R:110:0x07/0xAA
+
+# Nibelung
+R:111:0x08/0xA2
+
+# The disembodied hand that strangled people
+R:112:0x05/0xB4
+
+# Brown mold
+R:113:0x07/0xA7
+
+# Giant brown bat
+R:114:0x07/0x9C
+
+# Rat-thing
+R:115:0x0C/0xAC
+
+# Novice rogue
+R:116:0x06/0xAA
+
+# Creeping silver coins
+R:117:0x02/0xF3
+
+# Snaga
+R:118:0x0F/0xA9
+
+# Rattlesnake
+R:119:0x04/0x8A
+
+# Giant slug
+R:120:0x0F/0xB1
+
+# Giant pink frog
+R:121:0x04/0x92
+
+# Dark elf
+R:122:0x08/0xA2
+
+# Zombified kobold
+R:123:0x02/0xB4
+
+# Crypt creep
+R:124:0x08/0xAD
+
+# Rotting corpse
+R:125:0x0C/0xB4
+
+# Cave orc
+R:126:0x0D/0xA9
+
+# Wood spider
+R:127:0x0F/0x93
+
+# Manes
+R:128:0x04/0xAF
+
+# Bloodshot eye
+R:129:0x04/0x9F
+
+# Red naga
+R:130:0x04/0xA8
+
+# Red jelly
+R:131:0x04/0xA4
+
+# Green icky thing
+R:132:0x05/0xA3
+
+# Lost soul
+R:133:0x09/0x87
+
+# Night lizard
+R:134:0x06/0x92
+
+# Mughash, the Kobold Lord
+R:135:0x0A/0xA5
+
+# Skeleton orc
+R:136:0x09/0xAD
+
+# Wormtongue, Agent of Saruman
+R:137:0x0E/0xAA
+
+# Robin Hood, the Outlaw
+R:138:0x0D/0xAA
+
+# Nurgling
+R:139:0x03/0xAF
+
+# Lagduf, the Snaga
+R:140:0x0B/0xA9
+
+# Brown yeek
+R:141:0x07/0xB3
+
+# Novice ranger
+R:142:0x09/0xAA
+
+# Giant salamander
+R:143:0x0C/0x92
+
+# Space monster
+R:144:0x00/0xB5
+
+# Carnivorous flying monkey
+R:145:0x0C/0x88
+
+# Green mold
+R:146:0x05/0xA7
+
+# Novice paladin
+R:147:0x01/0xAA
+
+# Lemure
+R:148:0x0F/0xAF
+
+# Hill orc
+R:149:0x07/0xA9
+
+# Bandit
+R:150:0x06/0xAA
+
+# Hunting hawk
+R:151:0x07/0x82
+
+# Phantom warrior
+R:152:0x0E/0x87
+
+# Gremlin
+R:153:0x07/0xAF
+
+# Yeti
+R:154:0x01/0x99
+
+# Bloodshot icky thing
+R:155:0x04/0xA3
+
+# Giant grey rat
+R:156:0x02/0xAC
+
+# Black harpy
+R:157:0x08/0x88
+
+# Skaven
+R:158:0x0D/0xAC
+
+# The wounded bear
+R:159:0x04/0xAB
+
+# Cave bear
+R:160:0x07/0xAB
+
+# Rock mole
+R:161:0x02/0xAC
+
+# Mindcrafter
+R:162:0x0B/0xAA
+
+# Baby blue dragon
+R:163:0x06/0x9E
+
+# Baby white dragon
+R:164:0x01/0x9E
+
+# Baby green dragon
+R:165:0x05/0x9E
+
+# Baby black dragon
+R:166:0x02/0x9E
+
+# Baby red dragon
+R:167:0x04/0x9E
+
+# Giant red ant
+R:168:0x04/0x9B
+
+# Brodda, the Easterling
+R:169:0x0F/0xAA
+
+# Bloodfang, the Wolf
+R:170:0x0C/0x83
+
+# King cobra
+R:171:0x05/0x8A
+
+# Eagle
+R:172:0x07/0x82
+
+# War bear
+R:173:0x07/0xAB
+
+# Killer bee
+R:174:0x0B/0x89
+
+# Giant spider
+R:175:0x0A/0x93
+
+# Giant white tick
+R:176:0x01/0x93
+
+# The Borshin
+R:177:0x01/0xA1
+
+# Dark elven mage
+R:178:0x04/0xA2
+
+# Kamikaze yeek
+R:179:0x04/0xB3
+
+# Orfax, Son of Boldor
+R:180:0x0E/0xB3
+
+# Servant of Glaaki
+R:181:0x0D/0xB4
+
+# Dark elven warrior
+R:182:0x07/0xA2
+
+# Sand-dweller
+R:183:0x0B/0xAF
+
+# Clear mushroom patch
+R:184:0x0E/0xEE
+
+# Quiver slot
+R:185:0x0F/0xEE
+
+# Grishnakh, the Hill Orc
+R:186:0x0B/0xA9
+
+# Giant tan bat
+R:187:0x0F/0x9C
+
+# Owlbear
+R:188:0x03/0x88
+
+# Blue horror
+R:189:0x0E/0xAF
+
+# Hairy mold
+R:190:0x03/0xA7
+
+# Grizzly bear
+R:191:0x0F/0xAB
+
+# Disenchanter mold
+R:192:0x0A/0xA7
+
+# Pseudo dragon
+R:193:0x03/0x9E
+
+# Tengu
+R:194:0x06/0xAF
+
+# Creeping gold coins
+R:195:0x0B/0xF3
+
+# Wolf
+R:196:0x07/0x83
+
+# Giant fruit fly
+R:197:0x0D/0x89
+
+# Panther
+R:198:0x08/0xA0
+
+# Brigand
+R:199:0x06/0xAA
+
+# Hobbes the Tiger
+R:200:0x0B/0xA0
+
+# Shadow Creature of Fiona
+R:201:0x02/0xA2
+
+# Undead mass
+R:202:0x07/0xA4
+
+# Chaos shapechanger
+R:203:0x0A/0x88
+
+# Baby multi-hued dragon
+R:204:0x0A/0x9E
+
+# Vorpal bunny
+R:205:0x01/0xAC
+
+# Old Man Willow
+R:206:0x02/0xCC
+
+# Hippocampus
+R:207:0x0E/0x88
+
+# Zombified orc
+R:208:0x02/0xB4
+
+# Hippogriff
+R:209:0x0F/0x88
+
+# Black mamba
+R:210:0x08/0x8A
+
+# White wolf
+R:211:0x01/0x83
+
+# Grape jelly
+R:212:0x0A/0xA4
+
+# Nether worm mass
+R:213:0x08/0xB1
+
+# Abyss worm mass
+R:214:0x08/0xB1
+
+# Golfimbul, the Hill Orc Chief
+R:215:0x0B/0xA9
+
+# Swordsman
+R:216:0x07/0xAA
+
+# Skaven shaman
+R:217:0x05/0xAC
+
+# Baby bronze dragon
+R:218:0x0F/0x9E
+
+# Baby gold dragon
+R:219:0x0B/0x9E
+
+# Evil eye
+R:220:0x08/0x9F
+
+# Mine-dog
+R:221:0x07/0x83
+
+# Hellcat
+R:222:0x0C/0xA0
+
+# Moon beast
+R:223:0x09/0xAB
+
+# Master yeek
+R:224:0x05/0xB3
+
+# Priest
+R:225:0x05/0xAA
+
+# Dark elven priest
+R:226:0x05/0xA2
+
+# Air spirit
+R:227:0x0E/0x85
+
+# Skeleton human
+R:228:0x09/0xAD
+
+# Zombified human
+R:229:0x02/0xB4
+
+# Tiger
+R:230:0x03/0xA0
+
+# Moaning spirit
+R:231:0x07/0x87
+
+# Stegocentipede
+R:232:0x07/0x9D
+
+# Spotted jelly
+R:233:0x03/0xA4
+
+# Drider
+R:234:0x06/0x93
+
+# Mongbat
+R:235:0x0F/0x9C
+
+# Killer brown beetle
+R:236:0x07/0x8B
+
+# Boldor, King of the Yeeks
+R:237:0x0A/0xB3
+
+# Ogre
+R:238:0x0F/0x8F
+
+# Creeping mithril coins
+R:239:0x0E/0xF3
+
+# Illusionist
+R:240:0x0C/0xAA
+
+# Druid
+R:241:0x0D/0xAA
+
+# Pink horror
+R:242:0x0C/0xAF
+
+# Cloaker
+R:243:0x05/0xE2
+
+# Black orc
+R:244:0x08/0xA9
+
+# Ochre jelly
+R:245:0x0F/0xA4
+
+# Software bug
+R:246:0x04/0x89
+
+# Lurker
+R:247:0x01/0xB5
+
+# Tangleweed
+R:248:0x05/0xCC
+
+# Vlasta
+R:249:0x0E/0x92
+
+# Giant white dragon fly
+R:250:0x01/0x86
+
+# Snaga sapper
+R:251:0x0F/0xA9
+
+# Blue icky thing
+R:252:0x06/0xA3
+
+# Gibbering mouther
+R:253:0x03/0xA4
+
+# Wolfhound of Flora
+R:254:0x02/0x83
+
+# Hill giant
+R:255:0x0F/0x90
+
+# Flesh golem
+R:256:0x0C/0xA1
+
+# Warg
+R:257:0x08/0x83
+
+# Cheerful leprechaun
+R:258:0x0D/0xA2
+
+# Giant flea
+R:259:0x02/0x89
+
+# Ufthak of Cirith Ungol
+R:260:0x05/0xA9
+
+# Clay golem
+R:261:0x0F/0xA1
+
+# Black ogre
+R:262:0x08/0x8F
+
+# Dweller on the threshold
+R:263:0x02/0x99
+
+# Half-orc
+R:264:0x02/0xA9
+
+# Dark naga
+R:265:0x02/0xA8
+
+# Poison ivy
+R:266:0x05/0xCC
+
+# Magic mushroom patch
+R:267:0x0E/0xEE
+
+# Plaguebearer of Nurgle
+R:268:0x03/0xB4
+
+# Guardian naga
+R:269:0x0B/0xA8
+
+# Wererat
+R:270:0x08/0xAC
+
+# Light hound
+R:271:0x03/0x9A
+
+# Dark hound
+R:272:0x08/0x9A
+
+# Flying skull
+R:273:0x02/0xAD
+
+# Mi-Go
+R:274:0x0C/0x89
+
+# Giant tarantula
+R:275:0x03/0x93
+
+# Giant clear centipede
+R:276:0x0E/0x9D
+
+# Mirkwood spider
+R:277:0x0D/0x93
+
+# Frost giant
+R:278:0x01/0x90
+
+# Griffon
+R:279:0x07/0x88
+
+# Homunculus
+R:280:0x0B/0xAF
+
+# Gnome mage
+R:281:0x0C/0xA2
+
+# Clear hound
+R:282:0x0E/0x9A
+
+# Umber hulk
+R:283:0x0F/0x98
+
+# Rust monster
+R:284:0x03/0xAB
+
+# Ogrillon
+R:285:0x09/0x8F
+
+# Gelatinous cube
+R:286:0x0D/0xA4
+
+# Giant green dragon fly
+R:287:0x0D/0x86
+
+# Fire giant
+R:288:0x04/0x90
+
+# Hummerhorn
+R:289:0x0B/0x89
+
+# Lizard man
+R:290:0x0D/0xA2
+
+# Ulfast, Son of Ulfang
+R:291:0x0F/0xAA
+
+# Crebain
+R:292:0x08/0x82
+
+# Berserker
+R:293:0x07/0xAA
+
+# Quasit
+R:294:0x03/0xAF
+
+# Sphinx
+R:295:0x03/0x88
+
+# Imp
+R:296:0x05/0xAF
+
+# Forest troll
+R:297:0x05/0x94
+
+# Freezing sphere
+R:298:0x01/0xF0
+
+# Jumping fireball
+R:299:0x04/0xF0
+
+# Ball lightning
+R:300:0x0E/0xF0
+
+# 2-headed hydra
+R:301:0x07/0x8D
+
+# Swamp thing
+R:302:0x05/0x88
+
+# Water spirit
+R:303:0x06/0x85
+
+# Giant red scorpion
+R:304:0x04/0x93
+
+# Earth spirit
+R:305:0x07/0x85
+
+# Fire spirit
+R:306:0x04/0x85
+
+# Fire hound
+R:307:0x04/0x9A
+
+# Cold hound
+R:308:0x01/0x9A
+
+# Energy hound
+R:309:0x06/0x9A
+
+# Lesser Mimic
+R:310:0x0B/0xA7
+
+# Door mimic
+R:311:0x0F/0xB8
+
+# Blink dog
+R:312:0x0E/0x83
+
+# Uruk
+R:313:0x0E/0xA9
+
+# Shagrat, the Orc Captain
+R:314:0x05/0xA9
+
+# Gorbag, the Orc Captain
+R:315:0x05/0xA9
+
+# Shambling mound
+R:316:0x05/0xEE
+
+# Giant Venus Flytrap
+R:317:0x05/0xCC
+
+# Chaos beastman
+R:318:0x07/0x88
+
+# Daemonette of Slaanesh
+R:319:0x0C/0xAF
+
+# Giant bronze dragon fly
+R:320:0x0F/0x86
+
+# Stone giant
+R:321:0x09/0x90
+
+# Giant black dragon fly
+R:322:0x02/0x86
+
+# Stone golem
+R:323:0x09/0xA1
+
+# Red mold
+R:324:0x04/0xA7
+
+# Giant gold dragon fly
+R:325:0x0B/0x86
+
+# Stunwall
+R:326:0x09/0xBC
+
+# Ghast
+R:327:0x07/0xB4
+
+# Neekerbreeker
+R:328:0x08/0x89
+
+# Huorn
+R:329:0x05/0xCC
+
+# Bolg, Son of Azog
+R:330:0x0A/0xA9
+
+# Phase spider
+R:331:0x0E/0x93
+
+# Lizard king
+R:332:0x05/0xA2
+
+# Landmine
+R:333:0x01/0xB5
+
+# Wyvern
+R:334:0x05/0x9E
+
+# Great eagle
+R:335:0x07/0x82
+
+# Livingstone
+R:336:0x09/0xBC
+
+# Earth hound
+R:337:0x07/0x9A
+
+# Air hound
+R:338:0x05/0x9A
+
+# Sabre-tooth tiger
+R:339:0x0B/0xA0
+
+# Acid hound
+R:340:0x02/0x9A
+
+# Chimaera
+R:341:0x04/0x88
+
+# Quylthulg
+R:342:0x0B/0x91
+
+# Sasquatch
+R:343:0x09/0x99
+
+# Weir
+R:344:0x09/0x83
+
+# Ranger
+R:345:0x09/0xAA
+
+# Paladin
+R:346:0x01/0xAA
+
+# Werewolf
+R:347:0x08/0x83
+
+# Dark elven lord
+R:348:0x02/0xA2
+
+# Cloud giant
+R:349:0x06/0x90
+
+# Ugluk, the Uruk
+R:350:0x0A/0xA9
+
+# Blue dragon bat
+R:351:0x06/0x9C
+
+# Mimic
+R:352:0x0B/0xA7
+
+# Ultimate Mimic
+R:353:0x0B/0xA7
+
+# Fire vortex
+R:354:0x04/0xB0
+
+# Acid vortex
+R:355:0x02/0xB0
+
+# Lugdush, the Uruk
+R:356:0x0A/0xA9
+
+# Arch-vile
+R:357:0x09/0xAF
+
+# Cold vortex
+R:358:0x01/0xB0
+
+# Energy vortex
+R:359:0x06/0xB0
+
+# Globefish
+R:360:0x01/0x7E
+
+# Giant firefly
+R:361:0x04/0x89
+
+# Mummified orc
+R:362:0x01/0xB4
+
+# Wolf chieftain
+R:363:0x08/0x83
+
+# Serpent man
+R:364:0x0D/0x8A
+
+# Vampiric mist
+R:365:0x08/0xCA
+
+# Killer stag beetle
+R:366:0x05/0x8B
+
+# Iron golem
+R:367:0x02/0xA1
+
+# Auto-roller
+R:368:0x02/0xA1
+
+# Giant yellow scorpion
+R:369:0x0B/0x93
+
+# Jade monk
+R:370:0x0D/0xAA
+
+# Black ooze
+R:371:0x08/0xA4
+
+# Hardened warrior
+R:372:0x07/0xAA
+
+# Azog, King of the Uruk-Hai
+R:373:0x0A/0xA9
+
+# Fleshhound of Khorne
+R:374:0x0C/0x83
+
+# Dark elven warlock
+R:375:0x0A/0xA2
+
+# Master rogue
+R:376:0x06/0xAA
+
+# Red dragon bat
+R:377:0x04/0x9C
+
+# Killer white beetle
+R:378:0x01/0x8B
+
+# Ice skeleton
+R:379:0x01/0xAD
+
+# Angamaite of Umbar
+R:380:0x0F/0xAA
+
+# Forest wight
+R:381:0x05/0x97
+
+# Khim, Son of Mim
+R:382:0x03/0xA2
+
+# Ibun, Son of Mim
+R:383:0x03/0xA2
+
+# Meneldor the Swift
+R:384:0x07/0x82
+
+# Phantom beast
+R:385:0x0E/0x87
+
+# Giant silver ant
+R:386:0x09/0x9B
+
+# 4-headed hydra
+R:387:0x0B/0x8D
+
+# Lesser hell-beast
+R:388:0x02/0x95
+
+# Tyrannosaur
+R:389:0x05/0x92
+
+# Mummified human
+R:390:0x01/0xB4
+
+# Vampire bat
+R:391:0x08/0x9C
+
+# Sangahyando of Umbar
+R:392:0x0F/0xAA
+
+# It
+R:393:0x09/0xB5
+
+# Banshee
+R:394:0x06/0x87
+
+# Carrion crawler
+R:395:0x03/0x9D
+
+# Xiclotlan
+R:396:0x08/0xCC
+
+# Silent watcher
+R:397:0x02/0xA1
+
+# Pukelman
+R:398:0x08/0xA1
+
+# Disenchanter beast
+R:399:0x0A/0xAB
+
+# Dark elven druid
+R:400:0x0D/0xA2
+
+# Stone troll
+R:401:0x09/0x94
+
+# Black
+R:402:0x00/0xA4
+
+# Hill troll
+R:403:0x02/0x94
+
+# Wereworm
+R:404:0x07/0xB1
+
+# Killer red beetle
+R:405:0x04/0x8B
+
+# Disenchanter bat
+R:406:0x0A/0x9C
+
+# Gnoph-Keh
+R:407:0x02/0xAB
+
+# Giant grey ant
+R:408:0x02/0x9B
+
+# Khufu, the Mummified King
+R:409:0x0A/0xB4
+
+# Gwaihir the Windlord
+R:410:0x07/0x82
+
+# Giant fire tick
+R:411:0x0C/0x93
+
+# Displacer beast
+R:412:0x06/0xA0
+
+# Ulwarth, Son of Ulfang
+R:413:0x0F/0xAA
+
+# Werebear
+R:414:0x08/0xAB
+
+# Cave ogre
+R:415:0x07/0x8F
+
+# White wraith
+R:416:0x01/0x97
+
+# Angel
+R:417:0x03/0x81
+
+# Ghoul
+R:418:0x0F/0xB4
+
+# Mim, Betrayer of Turin
+R:419:0x03/0xA2
+
+# Hellblade
+R:420:0x0A/0xDC
+
+# Killer fire beetle
+R:421:0x0C/0x8B
+
+# Beast of Nurgle
+R:422:0x0B/0xAB
+
+# Creeping adamantite coins
+R:423:0x0D/0xF3
+
+# Algroth
+R:424:0x03/0x94
+
+# Flamer of Tzeentch
+R:425:0x04/0xEE
+
+# Roper
+R:426:0x08/0xBC
+
+# Headless
+R:427:0x07/0x88
+
+# Vibration hound
+R:428:0x0B/0x9A
+
+# Nexus hound
+R:429:0x0A/0x9A
+
+# Half-ogre
+R:430:0x03/0x8F
+
+# Lokkak, the Ogre Chieftain
+R:431:0x0A/0x8F
+
+# Vampire
+R:432:0x09/0x96
+
+# Gorgimaera
+R:433:0x03/0x88
+
+# Shantak
+R:434:0x08/0x88
+
+# Colbran
+R:435:0x0B/0xA1
+
+# Spirit naga
+R:436:0x01/0xA8
+
+# Corpser
+R:437:0x08/0xEE
+
+# Fiend of Slaanesh
+R:438:0x0C/0x93
+
+# Stairway to Hell
+R:439:0x09/0xB7
+
+# 5-headed hydra
+R:440:0x05/0x8D
+
+# Barney the Dinosaur
+R:441:0x0A/0x92
+
+# Black knight
+R:442:0x02/0xAA
+
+# Seahorse
+R:443:0x03/0x7E
+
+# Cyclops
+R:444:0x07/0x90
+
+# Clairvoyant
+R:445:0x0B/0xAA
+
+# Purple worm
+R:446:0x0A/0xB1
+
+# Catoblepas
+R:447:0x05/0xAB
+
+# Lesser wall monster
+R:448:0x09/0xBC
+
+# Mage
+R:449:0x04/0xAA
+
+# Mind flayer
+R:450:0x0A/0xA2
+
+# The Ultimate Dungeon Cleaner
+R:451:0x08/0xA1
+
+# Deep one
+R:452:0x05/0xAF
+
+# Basilisk
+R:453:0x02/0x92
+
+# Ice troll
+R:454:0x01/0x94
+
+# Dhole
+R:455:0x02/0xB1
+
+# Archangel
+R:456:0x0E/0x81
+
+# Greater Mimic
+R:457:0x0B/0xA7
+
+# Chaos tile
+R:458:0x0A/0xB5
+
+# Young blue dragon
+R:459:0x06/0x9E
+
+# Young white dragon
+R:460:0x01/0x9E
+
+# Young green dragon
+R:461:0x05/0x9E
+
+# Young bronze dragon
+R:462:0x0F/0x9E
+
+# Aklash
+R:463:0x0C/0x94
+
+# Mithril golem
+R:464:0x0E/0xA1
+
+# Skeleton troll
+R:465:0x09/0xAD
+
+# Skeletal tyrannosaur
+R:466:0x01/0x92
+
+# Beorn, the Shape-Changer
+R:467:0x08/0xAB
+
+# Thorondor, Lord of Eagles
+R:468:0x07/0x82
+
+# Giant blue ant
+R:469:0x06/0x9B
+
+# Grave wight
+R:470:0x06/0x97
+
+# Shadow drake
+R:471:0x0D/0x9E
+
+# Manticore
+R:472:0x0B/0x88
+
+# Giant army ant
+R:473:0x03/0x9B
+
+# Killer slicer beetle
+R:474:0x0B/0x8B
+
+# Gorgon
+R:475:0x06/0x88
+
+# Gug
+R:476:0x0D/0x90
+
+# Ghost
+R:477:0x01/0x87
+
+# Death watch beetle
+R:478:0x08/0x8B
+
+# Mountain ogre
+R:479:0x02/0x8F
+
+# Nexus quylthulg
+R:480:0x0A/0x91
+
+# Shelob, Spider of Darkness
+R:481:0x08/0x93
+
+# Giant squid
+R:482:0x05/0x7E
+
+# Ghoulking
+R:483:0x08/0xB4
+
+# Doombat
+R:484:0x0C/0x9C
+
+# Ninja
+R:485:0x07/0xAA
+
+# Memory moss
+R:486:0x06/0xEE
+
+# Storm giant
+R:487:0x0E/0x90
+
+# Spectator
+R:488:0x0E/0x9F
+
+# Bokrug
+R:489:0x0A/0x92
+
+# Biclops
+R:490:0x07/0x90
+
+# Half-troll
+R:491:0x0F/0x94
+
+# Ivory monk
+R:492:0x01/0xAA
+
+# Bert the Stone Troll
+R:493:0x09/0x94
+
+# Bill the Stone Troll
+R:494:0x09/0x94
+
+# Tom the Stone Troll
+R:495:0x09/0x94
+
+# Cave troll
+R:496:0x07/0x94
+
+# Anti-paladin
+R:497:0x08/0xAA
+
+# Chaos master
+R:498:0x0A/0xAA
+
+# Barrow wight
+R:499:0x0A/0x97
+
+# Skeleton ettin
+R:500:0x09/0xAD
+
+# Chaos drake
+R:501:0x0A/0x9E
+
+# Law drake
+R:502:0x0E/0x9E
+
+# Balance drake
+R:503:0x0A/0x9E
+
+# Ethereal drake
+R:504:0x03/0x9E
+
+# Groo, the Wanderer
+R:505:0x0F/0xAA
+
+# Fasolt the Giant
+R:506:0x07/0x90
+
+# Shade
+R:507:0x08/0x87
+
+# Spectre
+R:508:0x0F/0x87
+
+# Water troll
+R:509:0x0E/0x94
+
+# Fire elemental
+R:510:0x04/0x85
+
+# Cherub
+R:511:0x0D/0x81
+
+# Water elemental
+R:512:0x06/0x85
+
+# Multi-hued hound
+R:513:0x0A/0x9A
+
+# Invisible stalker
+R:514:0x0B/0x85
+
+# Carrion crawler
+R:515:0x03/0x9D
+
+# Master thief
+R:516:0x06/0xAA
+
+# The Watcher in the Water
+R:517:0x0A/0x7E
+
+# Lich
+R:518:0x03/0x8C
+
+# Gas spore
+R:519:0x05/0x9F
+
+# Master vampire
+R:520:0x05/0x96
+
+# Oriental vampire
+R:521:0x02/0x96
+
+# Greater mummy
+R:522:0x0B/0xB4
+
+# Bloodletter of Khorne
+R:523:0x04/0x95
+
+# Giant grey scorpion
+R:524:0x02/0x93
+
+# Earth elemental
+R:525:0x07/0x85
+
+# Air elemental
+R:526:0x0E/0x85
+
+# Shimmering mold
+R:527:0x06/0xA7
+
+# Gargoyle
+R:528:0x02/0xAF
+
+# Malicious leprechaun
+R:529:0x0A/0xA2
+
+# Eog golem
+R:530:0x07/0xA1
+
+# Little Boy
+R:531:0x08/0xD6
+
+# Dagashi
+R:532:0x07/0xAA
+
+# Headless ghost
+R:533:0x07/0x87
+
+# Dread
+R:534:0x03/0x87
+
+# Leng spider
+R:535:0x0A/0x93
+
+# Gauth
+R:536:0x02/0x9F
+
+# Smoke elemental
+R:537:0x0C/0x85
+
+# Olog
+R:538:0x0B/0x94
+
+# Halfling slinger
+R:539:0x0F/0xA2
+
+# Gravity hound
+R:540:0x09/0x9A
+
+# Acidic cytoplasm
+R:541:0x02/0xA4
+
+# Inertia hound
+R:542:0x09/0x9A
+
+# Impact hound
+R:543:0x07/0x9A
+
+# Shardstorm
+R:544:0x07/0xB0
+
+# Ooze elemental
+R:545:0x05/0x85
+
+# Young black dragon
+R:546:0x02/0x9E
+
+# Mumak
+R:547:0x02/0xAB
+
+# Giant fire ant
+R:548:0x0C/0x9B
+
+# Mature white dragon
+R:549:0x01/0x9E
+
+# Xorn
+R:550:0x07/0x98
+
+# Rogrog the Black Troll
+R:551:0x08/0x94
+
+# Mist giant
+R:552:0x0E/0xCA
+
+# Phantom
+R:553:0x0A/0x87
+
+# Grey wraith
+R:554:0x02/0x97
+
+# Revenant
+R:555:0x07/0x97
+
+# Young multi-hued dragon
+R:556:0x0A/0x9E
+
+# Raal's Tome of Destruction
+R:557:0x04/0xEF
+
+# Colossus
+R:558:0x0D/0xA1
+
+# Young gold dragon
+R:559:0x0B/0x9E
+
+# Mature blue dragon
+R:560:0x06/0x9E
+
+# Mature green dragon
+R:561:0x05/0x9E
+
+# Mature bronze dragon
+R:562:0x0F/0x9E
+
+# Young red dragon
+R:563:0x04/0x9E
+
+# Nightblade
+R:564:0x08/0xA2
+
+# Trapper
+R:565:0x01/0xB5
+
+# Bodak
+R:566:0x04/0xAF
+
+# Time bomb
+R:567:0x01/0xB5
+
+# Mezzodaemon
+R:568:0x03/0xAF
+
+# Elder thing
+R:569:0x0D/0xAF
+
+# Ice elemental
+R:570:0x01/0x85
+
+# Necromancer
+R:571:0x0C/0xAA
+
+# The Greater hell magic mushroom were-quylthulg
+R:572:0x02/0x91
+
+# Lorgan, Chief of the Easterlings
+R:573:0x0A/0xAA
+
+# Chaos spawn
+R:574:0x02/0x9F
+
+# Mummified troll
+R:575:0x01/0xB4
+
+# Storm of Unmagic
+R:576:0x0A/0xB0
+
+# Crypt thing
+R:577:0x0D/0x8C
+
+# Chaos butterfly
+R:578:0x0D/0x89
+
+# Time elemental
+R:579:0x0D/0x85
+
+# Flying polyp
+R:580:0x0C/0x7E
+
+# The Queen Ant
+R:581:0x0A/0x9B
+
+# Will o' the wisp
+R:582:0x09/0x85
+
+# Shan
+R:583:0x0E/0x89
+
+# Magma elemental
+R:584:0x03/0x85
+
+# Black pudding
+R:585:0x08/0xA4
+
+# Killer iridescent beetle
+R:586:0x0A/0x8B
+
+# Nexus vortex
+R:587:0x0A/0xB0
+
+# Plasma vortex
+R:588:0x0C/0xB0
+
+# Mature red dragon
+R:589:0x04/0x9E
+
+# Mature gold dragon
+R:590:0x0B/0x9E
+
+# Crystal drake
+R:591:0x07/0x9E
+
+# Mature black dragon
+R:592:0x02/0x9E
+
+# Mature multi-hued dragon
+R:593:0x0A/0x9E
+
+# Sky whale
+R:594:0x0D/0x7E
+
+# Draebor, the Imp
+R:595:0x0A/0xAF
+
+# Mother Hydra
+R:596:0x0A/0xAF
+
+# Death knight
+R:597:0x08/0xAA
+
+# Castamir the Usurper
+R:598:0x0C/0xAA
+
+# Time vortex
+R:599:0x0E/0xB0
+
+# Shimmering vortex
+R:600:0x03/0xB0
+
+# Ancient blue dragon
+R:601:0x06/0x84
+
+# Ancient bronze dragon
+R:602:0x0F/0x84
+
+# Beholder
+R:603:0x0F/0x9F
+
+# Emperor wight
+R:604:0x04/0x97
+
+# Seraph
+R:605:0x04/0x81
+
+# Vargo, Tyrant of Fire
+R:606:0x04/0x85
+
+# Black wraith
+R:607:0x08/0x97
+
+# Nightgaunt
+R:608:0x08/0x95
+
+# Baron of hell
+R:609:0x0F/0x95
+
+# Scylla
+R:610:0x0E/0x8D
+
+# Monastic lich
+R:611:0x07/0x8C
+
+# Nether wraith
+R:612:0x0D/0x97
+
+# Hellhound
+R:613:0x04/0x83
+
+# 7-headed hydra
+R:614:0x0D/0x8D
+
+# Waldern, King of Water
+R:615:0x06/0x85
+
+# Kavlax the Many-Headed
+R:616:0x0A/0x9E
+
+# Ancient white dragon
+R:617:0x01/0x84
+
+# Ancient green dragon
+R:618:0x05/0x84
+
+# Chthonian
+R:619:0x08/0xB1
+
+# Eldrak
+R:620:0x04/0x94
+
+# Ettin
+R:621:0x06/0x94
+
+# Night mare
+R:622:0x0D/0xAB
+
+# Vampire lord
+R:623:0x06/0x96
+
+# Ancient black dragon
+R:624:0x02/0x84
+
+# Weird fume
+R:625:0x0A/0xCA
+
+# Spawn of Ubbo-Sathla
+R:626:0x0A/0xA4
+
+# Fat Man
+R:627:0x08/0xD6
+
+# Malekith the Accursed
+R:628:0x0A/0xA2
+
+# Shadowfax, steed of Gandalf
+R:629:0x0A/0xAB
+
+# Spirit troll
+R:630:0x0D/0x87
+
+# War troll
+R:631:0x06/0x94
+
+# Disenchanter worm mass
+R:632:0x0A/0xB1
+
+# Rotting quylthulg
+R:633:0x07/0x91
+
+# Lesser titan
+R:634:0x0B/0x90
+
+# 9-headed hydra
+R:635:0x04/0x8D
+
+# Enchantress
+R:636:0x0C/0xAA
+
+# Ranger chieftain
+R:637:0x09/0xAA
+
+# Sorcerer
+R:638:0x0C/0xAA
+
+# Xaren
+R:639:0x02/0x98
+
+# Giant roc
+R:640:0x07/0x82
+
+# Minotaur
+R:641:0x0F/0x88
+
+# Medusa, the Gorgon
+R:642:0x0A/0xA8
+
+# Death drake
+R:643:0x0D/0x84
+
+# Ancient red dragon
+R:644:0x04/0x84
+
+# Ancient gold dragon
+R:645:0x0B/0x84
+
+# Great crystal drake
+R:646:0x0F/0x84
+
+# Wyrd sister
+R:647:0x0A/0xAA
+
+# Vrock
+R:648:0x02/0x95
+
+# Death quasit
+R:649:0x08/0xAF
+
+# Giganto, the Gargantuan
+R:650:0x02/0x7E
+
+# Strygalldwir
+R:651:0x09/0x95
+
+# Fallen angel
+R:652:0x02/0x81
+
+# Giant headless
+R:653:0x07/0x88
+
+# Judge Fire
+R:654:0x0C/0xAD
+
+# Ubbo-Sathla, the Unbegotten Source
+R:655:0x09/0xA4
+
+# Judge Mortis
+R:656:0x0D/0xB4
+
+# Dark elven sorcerer
+R:657:0x0C/0xA2
+
+# Master lich
+R:658:0x04/0x8C
+
+# Byakhee
+R:659:0x08/0x95
+
+# Eol, the Dark Elf
+R:660:0x08/0xA2
+
+# Archon
+R:661:0x0B/0x81
+
+# Formless spawn of Tsathoggua
+R:662:0x08/0x95
+
+# Hunting horror
+R:663:0x08/0x95
+
+# Undead beholder
+R:664:0x07/0x9F
+
+# Shadow
+R:665:0x08/0x87
+
+# Iron lich
+R:666:0x02/0x8C
+
+# Dread
+R:667:0x03/0x87
+
+# Greater basilisk
+R:668:0x08/0x92
+
+# Charybdis
+R:669:0x04/0x7E
+
+# Jack of Shadows
+R:670:0x02/0xAA
+
+# Zephyr Lord
+R:671:0x0A/0x97
+
+# Juggernaut of Khorne
+R:672:0x08/0xA1
+
+# Mumak
+R:673:0x02/0xAB
+
+# Judge Fear
+R:674:0x08/0x97
+
+# Ancient multi-hued dragon
+R:675:0x0A/0x84
+
+# Ethereal dragon
+R:676:0x03/0x84
+
+# Dark young of Shub-Niggurath
+R:677:0x05/0x95
+
+# Colour out of space
+R:678:0x0A/0xB5
+
+# Quaker, Master of Earth
+R:679:0x07/0x85
+
+# Death leprechaun
+R:680:0x08/0xA2
+
+# Chaugnar Faugn, Horror from the Hills
+R:681:0x08/0xAB
+
+# Lloigor
+R:682:0x0E/0xB0
+
+# Utgard-Loke
+R:683:0x0A/0x90
+
+# Quachil Uttaus, Treader of the Dust
+R:684:0x08/0xB4
+
+# Shoggoth
+R:685:0x08/0xA4
+
+# Judge Death
+R:686:0x08/0x97
+
+# Ariel, Queen of Air
+R:687:0x0E/0x85
+
+# 11-headed hydra
+R:688:0x0C/0x8D
+
+# Patriarch
+R:689:0x0D/0xAA
+
+# Dreadmaster
+R:690:0x0B/0x87
+
+# Drolem
+R:691:0x05/0xA1
+
+# Scatha the Worm
+R:692:0x09/0x84
+
+# Warrior of the Dawn
+R:693:0x0C/0xAA
+
+# Lesser black reaver
+R:694:0x08/0x8C
+
+# Zoth-Ommog
+R:695:0x0A/0x92
+
+# Grand master thief
+R:696:0x06/0xAA
+
+# Smaug the Golden
+R:697:0x0C/0x84
+
+# The Stormbringer
+R:698:0x08/0xDC
+
+# Knight Templar
+R:699:0x01/0xAA
+
+# Leprechaun fanatic
+R:700:0x04/0xA2
+
+# Dracolich
+R:701:0x0D/0x84
+
+# Greater titan
+R:702:0x03/0x90
+
+# Dracolisk
+R:703:0x0C/0x84
+
+# Winged Horror
+R:704:0x08/0x82
+
+# Spectral tyrannosaur
+R:705:0x0D/0x92
+
+# Yibb-Tstll, the Patient One
+R:706:0x08/0x90
+
+# Ghatanothoa
+R:707:0x08/0xB0
+
+# Ent
+R:708:0x0D/0xCC
+
+# Hru
+R:709:0x02/0x90
+
+# Itangast the Fire Drake
+R:710:0x0C/0x84
+
+# Death mold
+R:711:0x08/0xA7
+
+# Fafner the Dragon
+R:712:0x0D/0x84
+
+# Charon, Boatman of the Styx
+R:713:0x0E/0x97
+
+# Quickbeam, the Ent
+R:714:0x0D/0xCC
+
+# Glaurung, Father of the Dragons
+R:715:0x0C/0x84
+
+# Behemoth
+R:716:0x0E/0x88
+
+# Garm, Guardian of Hel
+R:717:0x06/0x83
+
+# Greater wall monster
+R:718:0x09/0xBC
+
+# Nycadaemon
+R:719:0x03/0x95
+
+# Barbazu
+R:720:0x0D/0x95
+
+# Goat of Mendes
+R:721:0x08/0xAB
+
+# Nightwing
+R:722:0x08/0x97
+
+# Maulotaur
+R:723:0x02/0x88
+
+# Nether hound
+R:724:0x0D/0x9A
+
+# Time hound
+R:725:0x0E/0x9A
+
+# Plasma hound
+R:726:0x0C/0x9A
+
+# Demonic quylthulg
+R:727:0x04/0x91
+
+# Great Storm Wyrm
+R:728:0x06/0x84
+
+# Ulik the Troll
+R:729:0x0A/0x94
+
+# Baphomet the Minotaur Lord
+R:730:0x0A/0x88
+
+# Hell knight
+R:731:0x08/0xAA
+
+# Bull Gates
+R:732:0x08/0xAA
+
+# Santa Claus
+R:733:0x04/0xA2
+
+# Eihort, the Thing in the Labyrinth
+R:734:0x0C/0xA4
+
+# The King in Yellow
+R:735:0x0B/0x8C
+
+# Great unclean one
+R:736:0x05/0x95
+
+# Lord of Chaos
+R:737:0x0A/0xAA
+
+# Old Sorcerer
+R:738:0x0C/0xAA
+
+# Ethereal hound
+R:739:0x0D/0x9A
+
+# Lesser kraken
+R:740:0x0D/0x7E
+
+# Great Ice Wyrm
+R:741:0x01/0x84
+
+# Demilich
+R:742:0x0F/0x8C
+
+# The Phoenix
+R:743:0x04/0x82
+
+# Nightcrawler
+R:744:0x08/0x97
+
+# Lord of Change
+R:745:0x0A/0x95
+
+# Keeper of Secrets
+R:746:0x0D/0x88
+
+# Shudde M'ell
+R:747:0x02/0xB1
+
+# Hand druj
+R:748:0x0B/0xAD
+
+# Eye druj
+R:749:0x04/0xAD
+
+# Skull druj
+R:750:0x03/0xAD
+
+# Chaos vortex
+R:751:0x0A/0xB0
+
+# Aether vortex
+R:752:0x0A/0xB0
+
+# Nidhogg, the Hel-Drake
+R:753:0x08/0x84
+
+# The Lernaean Hydra
+R:754:0x0A/0x8D
+
+# Thuringwethil, the Vampire Messenger
+R:755:0x0A/0x96
+
+# Great Hell Wyrm
+R:756:0x04/0x84
+
+# Hastur the Unspeakable
+R:757:0x06/0x88
+
+# Bloodthirster
+R:758:0x04/0x95
+
+# Draconic quylthulg
+R:759:0x05/0x91
+
+# Nyogtha, the Thing that Should not Be
+R:760:0x08/0xA4
+
+# Ahtu, Avatar of Nyarlathotep
+R:761:0x08/0xBC
+
+# Fundin Bluecloak
+R:762:0x0E/0xA2
+
+# Bile Demon
+R:763:0x0C/0x95
+
+# Uriel, Angel of Fire
+R:764:0x0C/0x81
+
+# Azriel, Angel of Death
+R:765:0x08/0x81
+
+# Ancalagon the Black
+R:766:0x08/0x84
+
+# Daoloth, the Render of the Veils
+R:767:0x02/0x95
+
+# Nightwalker
+R:768:0x08/0x97
+
+# Gabriel, the Messenger
+R:769:0x01/0x81
+
+# Artsi, the Champion of Chaos
+R:770:0x0A/0xA2
+
+# Saruman of Many Colours
+R:771:0x0A/0xAA
+
+# Harowen the Black Hand
+R:772:0x0E/0xAA
+
+# Osyluth
+R:773:0x09/0x95
+
+# Dreadlord
+R:774:0x04/0x87
+
+# Greater kraken
+R:775:0x0D/0x7E
+
+# Archlich
+R:776:0x0E/0x8C
+
+# The Cat Lord
+R:777:0x0A/0xA0
+
+# Jabberwock
+R:778:0x0A/0x88
+
+# Chaos hound
+R:779:0x0A/0x9A
+
+# Vlad Dracula, Prince of Darkness
+R:780:0x08/0x96
+
+# Beholder hive-mother
+R:781:0x0B/0x9F
+
+# Leviathan
+R:782:0x0A/0x7E
+
+# Great Wyrm of Chaos
+R:783:0x0A/0x84
+
+# Great Wyrm of Law
+R:784:0x0E/0x84
+
+# Great Wyrm of Balance
+R:785:0x0A/0x84
+
+# Shambler
+R:786:0x09/0x85
+
+# Gelugon
+R:787:0x01/0x95
+
+# Glaaki
+R:788:0x0A/0x7E
+
+# Trone, the Rebel Thunderlord
+R:789:0x08/0x82
+
+# Great Wyrm of Many Colours
+R:790:0x0A/0x84
+
+# Marda, rider of gold Laronth
+R:791:0x0B/0x82
+
+# Tselakus, the Dreadlord
+R:792:0x0C/0x87
+
+# Sky Drake
+R:793:0x0E/0x84
+
+# Eilinel the Entrapped
+R:794:0x08/0xAA
+
+# Horned Reaper
+R:795:0x0E/0x95
+
+# The Norsa
+R:796:0x0E/0x88
+
+# Rhan-Tegoth
+R:797:0x06/0x93
+
+# Black reaver
+R:798:0x08/0x8C
+
+# Master mindcrafter
+R:799:0x0B/0xAA
+
+# Greater demonic quylthulg
+R:800:0x0C/0x91
+
+# Greater draconic quylthulg
+R:801:0x0D/0x91
+
+# Greater rotting quylthulg
+R:802:0x0F/0x91
+
+# Null, the Living Void
+R:803:0x00/0xB5
+
+# Feagwath, the Undead Sorcerer
+R:804:0x0B/0x8C
+
+# Omarax the Eye Tyrant
+R:805:0x0A/0x9F
+
+# Tsathoggua, the Sleeper of N'kai
+R:806:0x08/0x92
+
+# Greater Balrog
+R:807:0x0A/0x95
+
+# Ungoliant, the Unlight
+R:808:0x08/0x93
+
+# Atlach-Nacha, the Spider God
+R:809:0x08/0x93
+
+# Y'golonac
+R:810:0x0C/0x88
+
+# Aether hound
+R:811:0x0A/0x9A
+
+# Pit Fiend
+R:812:0x03/0x95
+
+# The Serpent of Chaos
+R:813:0x0A/0x8A
+
+# Yig, Father of Serpents
+R:814:0x06/0x8A
+
+# Unmaker
+R:815:0x0A/0x85
+
+# Cyberdemon
+R:816:0x07/0x95
+
+# Hela, Queen of the Dead
+R:817:0x0D/0xAA
+
+# The Mouth of Sauron
+R:818:0x0A/0xAA
+
+# The Necromancer of Dol Guldur
+R:819:0x0A/0xAA
+
+# Lisa, rider of gold Romth
+R:820:0x0B/0x82
+
+# Master quylthulg
+R:821:0x0E/0x91
+
+# Qlzqqlzuup, the Lord of Flesh
+R:822:0x0A/0x91
+
+# Cthugha, the Living Flame
+R:823:0x0C/0x85
+
+# Flare, rider of bronze Moonth
+R:824:0x0F/0x82
+
+# Maeglin, the Traitor of Gondolin
+R:825:0x08/0xA2
+
+# Cyaegha
+R:826:0x0D/0x9F
+
+# Pazuzu, Lord of Air
+R:827:0x06/0x95
+
+# Ithaqua the Windwalker
+R:828:0x0E/0x99
+
+# Greater Hellhound
+R:829:0x04/0x83
+
+# Cantoras, the Skeletal Lord
+R:830:0x0A/0xAD
+
+# Mephistopheles, Lord of Hell
+R:831:0x04/0x95
+
+# Godzilla
+R:832:0x0A/0x92
+
+# Abhoth, Source of Uncleanness
+R:833:0x0D/0xA4
+
+# Ymir, the Ice Giant
+R:834:0x01/0x90
+
+# Loki, the Trickster
+R:835:0x08/0x90
+
+# Star-spawn of Cthulhu
+R:836:0x0D/0x95
+
+# Surtur, the Fire Giant
+R:837:0x04/0x90
+
+# The Tarrasque
+R:838:0x0A/0x92
+
+# Lungorthin, the Balrog of White Fire
+R:839:0x0A/0x95
+
+# Draugluin, Sire of All Werewolves
+R:840:0x0A/0x83
+
+# Shuma-Gorath
+R:841:0x0D/0x9F
+
+# Tulzscha, the Green Flame
+R:842:0x0D/0x85
+
+# Oremorj, the Cyberdemon Lord
+R:843:0x07/0x95
+
+# Vecna, the Emperor Lich
+R:844:0x0A/0x8C
+
+# Yog-Sothoth, the All-in-One
+R:845:0x0A/0xA4
+
+# Fenris Wolf
+R:846:0x08/0x83
+
+# Great Wyrm of Power
+R:847:0x0A/0x84
+
+# Shub-Niggurath, Black Goat of the Woods
+R:848:0x08/0x95
+
+# Nodens, Lord of the Great Abyss
+R:849:0x09/0x90
+
+# Carcharoth, the Jaws of Thirst
+R:850:0x08/0x83
+
+# Nyarlathotep, the Crawling Chaos
+R:851:0x04/0x95
+
+# Azathoth, the Daemon Sultan
+R:852:0x0E/0x85
+
+# Huan, Wolfhound of the Valar
+R:853:0x09/0x83
+
+# Jormungand the Midgard Serpent
+R:854:0x0A/0x8A
+
+# The Destroyer
+R:855:0x0A/0xA1
+
+# Gothmog, the High Captain of Balrogs
+R:856:0x0A/0x95
+
+# Great Cthulhu
+R:857:0x05/0x95
+
+# Sarko, rider of gold Foronth
+R:858:0x0B/0x82
+
+# The Unicorn of Order
+R:859:0x01/0xAB
+
+# Sauron, the Sorcerer
+R:860:0x0A/0xAA
+
+# DarkGod, the Mighty Coder of Hell
+R:861:0x0E/0x90
+
+# Morgoth, Lord of Darkness
+R:862:0x08/0x90
+
+# Human Warrior
+R:863:0x07/0xAA
+
+# Elven archer
+R:864:0x09/0xA2
+
+# Dwarven warrior
+R:865:0x0F/0xA2
+
+# Elite uruk
+R:866:0x01/0xA9
+
+# The Philosophy Teacher
+R:867:0x04/0xAA
+
+# The Variant Maintainer
+R:868:0x0E/0xAA
+
+# Random Number Generator
+R:869:0x06/0x89
+
+# Rocket mine
+R:870:0x0C/0xB5
+
+# Bouncing mine
+R:871:0x0E/0xB5
+
+# Durin's Bane
+R:872:0x0A/0x95
+
+# The Icky Queen
+R:873:0x0A/0xA3
+
+# Rot jelly
+R:874:0x07/0xA4
+
+# Death
+R:875:0x08/0x87
+
+# Famine
+R:876:0x0F/0x87
+
+# Pestilence
+R:877:0x0D/0x87
+
+# War
+R:878:0x04/0x87
+
+# Pike
+R:879:0x02/0x7E
+
+# Electric eel
+R:880:0x0E/0x8A
+
+# Giant crayfish
+R:881:0x0C/0x7E
+
+# Mermaid
+R:882:0x0D/0xA2
+
+# Box jellyfish
+R:883:0x0E/0x7E
+
+# Giant piranha
+R:884:0x05/0x7E
+
+# Piranha
+R:885:0x05/0x7E
+
+# Bullywug
+R:886:0x05/0xA2
+
+# Bullywug warrior
+R:887:0x05/0xA2
+
+# Bullywug shaman
+R:888:0x05/0xA2
+
+# Whale
+R:889:0x0D/0x7E
+
+# Sand mite
+R:890:0x0E/0x7E
+
+# Octopus
+R:891:0x05/0x7E
+
+# Giant octopus
+R:892:0x05/0x7E
+
+# Eye of the deep
+R:893:0x06/0x9F
+
+# Murk dweller
+R:894:0x02/0x93
+
+# Drowned soul
+R:895:0x0E/0x87
+
+# Tiger shark
+R:896:0x05/0x7E
+
+# Hammerhead shark
+R:897:0x05/0x7E
+
+# Great white shark
+R:898:0x01/0x7E
+
+# Aquatic golem
+R:899:0x06/0xA1
+
+# Aquatic kobold
+R:900:0x0E/0xA5
+
+# White shark
+R:901:0x09/0x7E
+
+# Scrag
+R:902:0x0E/0x94
+
+# Jaws
+R:903:0x01/0x7E
+
+# Aquatic elf
+R:904:0x06/0xA2
+
+# Aquatic elven warrior
+R:905:0x06/0xA2
+
+# Aquatic elven shaman
+R:906:0x06/0xA2
+
+# Stargazer
+R:907:0x0B/0x7E
+
+# Elder stargazer
+R:908:0x0B/0x7E
+
+# Flounder
+R:909:0x02/0x7E
+
+# Giant turtle
+R:910:0x0D/0x92
+
+# Baby dragon turtle
+R:911:0x09/0x9E
+
+# Young dragon turtle
+R:912:0x09/0x9E
+
+# Mature dragon turtle
+R:913:0x09/0x9E
+
+# Ancient dragon turtle
+R:914:0x09/0x84
+
+# Fastitocalon
+R:915:0x05/0x84
+
+# Undead stargazer
+R:916:0x0B/0x7E
+
+# Killer whale
+R:917:0x01/0x7E
+
+# Merrow
+R:918:0x0E/0x8F
+
+# Water naga
+R:919:0x0E/0xA8
+
+# Devilfish
+R:920:0x02/0x7E
+
+# Undead devilfish
+R:921:0x08/0x7E
+
+# Moby Dick, the White Whale
+R:922:0x01/0x7E
+
+# Aquatic hound
+R:923:0x0E/0x9A
+
+# Water demon
+R:924:0x0E/0x95
+
+# Ixitxachitl
+R:925:0x02/0x7E
+
+# Ixitxachitl priest
+R:926:0x02/0x7E
+
+# Vampiric ixitxachitl
+R:927:0x08/0x7E
+
+# Mathilde, the Science Student
+R:928:0x0B/0xA2
+
+# Child spirit
+R:929:0x09/0x87
+
+# Young spirit
+R:930:0x09/0x87
+
+# Mature spirit
+R:931:0x09/0x87
+
+# Experienced spirit
+R:932:0x09/0x87
+
+# Wise spirit
+R:933:0x09/0x87
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0x0D/0xCC
+
+# Gandalf the Grey
+R:935:0x02/0xAA
+
+# Nar, the Dwarf
+R:936:0x0B/0xA2
+
+# Novice mindcrafter
+R:937:0x0B/0xAA
+
+# Great Swamp Wyrm
+R:938:0x05/0x84
+
+# Great Bile Wyrm
+R:939:0x02/0x84
+
+# Blue Firebird
+R:940:0x0E/0x82
+
+# Green Firebird
+R:941:0x0D/0x82
+
+# Brown Firebird
+R:942:0x07/0x82
+
+# Bronze Firebird
+R:943:0x0F/0x82
+
+# Gold Firebird
+R:944:0x0B/0x82
+
+# High-elven ranger
+R:945:0x0D/0xA2
+
+# Uvatha the Horseman
+R:946:0x08/0x97
+
+# Adunaphel the Quiet
+R:947:0x08/0x97
+
+# Akhorahil the Blind
+R:948:0x08/0x97
+
+# Ren the Unclean
+R:949:0x08/0x97
+
+# Ji Indur Dawndeath
+R:950:0x08/0x97
+
+# Dwar, Dog Lord of Waw
+R:951:0x08/0x97
+
+# Hoarmurath of Dir
+R:952:0x08/0x97
+
+# Khamul, the Black Easterling
+R:953:0x08/0x97
+
+# The Witch-King of Angmar
+R:954:0x08/0x97
+
+# Green Thunderlord
+R:955:0x05/0x82
+
+# Blue Thunderlord
+R:956:0x06/0x82
+
+# Brown Thunderlord
+R:957:0x07/0x82
+
+# Bronze Thunderlord
+R:958:0x0F/0x82
+
+# Gold Thunderlord
+R:959:0x0B/0x82
+
+# Blood Sprout
+R:960:0x05/0xEE
+
+# Gorlim, Betrayer of Barahir
+R:961:0x02/0xAA
+
+# The Blubbering idiot, agent of black market, Simon the weak
+R:962:0x09/0xAE
+
+# Aranea
+R:963:0x04/0x93
+
+# Elder aranea
+R:964:0x0A/0x93
+
+# Giant brown tick
+R:965:0x07/0x93
+
+# Wavelord
+R:966:0x06/0xAA
+
+# Novice possessor (soul)
+R:967:0x08/0x87
+
+# Bat of Gorgoroth
+R:968:0x05/0x9C
+
+# The Princess
+R:969:0x0B/0xAA
+
+# Merton Proudfoot, the lost hobbit
+R:970:0x0A/0xA2
+
+# The Wight-King of the Barrow-downs
+R:971:0x0A/0x97
+
+# Adventurer
+R:972:0x0F/0x80
+
+# Experienced possessor (soul)
+R:973:0x08/0x87
+
+# Old possessor (soul)
+R:974:0x08/0x87
+
+# Death orb
+R:975:0x08/0x85
+
+# Bronze dragon worm
+R:976:0x0F/0xB1
+
+# Gold dragon worm
+R:977:0x0B/0xB1
+
+# Moldoux, the Defenceless Mold
+R:978:0x0A/0xA7
+
+# The Physics Teacher
+R:979:0x01/0xAA
+
+# Ar-Pharazon the Golden
+R:980:0x0B/0xAA
+
+# Doppelganger
+R:981:0x01/0x80
+
+# Marylene, Heartbreakeress of the Netherworld
+R:982:0x09/0x90
+
+# The Greater Lag Monster
+R:983:0x0A/0x95
+
+# Hrungnir, the Stone Giant
+R:984:0x09/0x90
+
+# Bullroarer the Hobbit
+R:985:0x0F/0xA2
+
+# 3-headed hydra
+R:986:0x03/0x8D
+
+# Uldor the Accursed
+R:987:0x0F/0xAA
+
+# Mystic
+R:988:0x03/0xAA
+
+# Elder vampire
+R:989:0x04/0x96
+
+# Ulfang the Black
+R:990:0x0F/0xAA
+
+# Demonologist
+R:991:0x0C/0xAA
+
+# Hezrou
+R:992:0x05/0x95
+
+# Glabrezu
+R:993:0x0F/0x95
+
+# Nalfeshnee
+R:994:0x04/0x95
+
+# Marilith
+R:995:0x0B/0x95
+
+# Lesser Balrog
+R:996:0x0A/0x95
+
+# Master mystic
+R:997:0x03/0xAA
+
+# Grand master mystic
+R:998:0x03/0xAA
+
+# Erinyes
+R:999:0x07/0x95
+
+# Novice mindcrafter
+R:1000:0x0B/0xAA
+
+# Polyphemus, the Blind Cyclops
+R:1001:0x05/0x90
+
+# Great Wyrm of Perplexity
+R:1002:0x0F/0x84
+
+# Hound of Tindalos
+R:1003:0x02/0x9A
+
+# Great Wyrm of Thunder
+R:1004:0x0B/0x84
+
+# Silver mouse
+R:1005:0x09/0xAC
+
+# The Rat King
+R:1006:0x0A/0xAC
+
+# Vort the Kobold Queen
+R:1007:0x0A/0xA5
+
+# Giant black louse
+R:1008:0x08/0x89
+
+# Fire Phantom
+R:1009:0x04/0x87
+
+# The Insane Player
+R:1010:0x0A/0xAA
+
+# Glaryssa, Succubus Queen
+R:1011:0x09/0x95
+
+# Vermicious Knid
+R:1012:0x02/0xA4
+
+# Bone golem
+R:1013:0x01/0xA1
+
+# Snake of Yig
+R:1014:0x06/0x8A
+
+# Bronze golem
+R:1015:0x03/0xA1
+
+# Dimensional shambler
+R:1016:0x0E/0xA2
+
+# Cultist
+R:1017:0x0D/0xAA
+
+# Cult leader
+R:1018:0x0D/0xAA
+
+# Servitor of the outer gods
+R:1019:0x0B/0x88
+
+# Avatar of Nyarlathotep
+R:1020:0x0C/0xAA
+
+# Thiazi, the Storm Giant
+R:1021:0x0E/0x90
+
+# Hypnos, Lord of Sleep
+R:1022:0x0D/0xAA
+
+# Blue dragon worm
+R:1023:0x0E/0xB1
+
+# White dragon worm
+R:1024:0x09/0xB1
+
+# Green dragon worm
+R:1025:0x0D/0xB1
+
+# Black dragon worm
+R:1026:0x02/0xB1
+
+# Red dragon worm
+R:1027:0x0C/0xB1
+
+# Multi-hued dragon worm
+R:1028:0x0A/0xB1
+
+# The Minotaur of the Labyrinth
+R:1029:0x02/0x88
+
+# The Sandworm Queen
+R:1030:0x0A/0xB1
+
+# Sandworm
+R:1031:0x0B/0xB1
+
+# Tik'srvzllat
+R:1032:0x0A/0x87
+
+# The Glass Golem
+R:1033:0x09/0xBC
+
+# The White Balrog
+R:1034:0x09/0x95
+
+# Golgarach, the Living Rock
+R:1035:0x09/0xBC
+
+# Atlas, the Titan
+R:1036:0x02/0x90
+
+# Kronos, Lord of the Titans
+R:1037:0x0A/0x90
+
+# Water hound
+R:1038:0x04/0x9A
+
+# Improv, the mighty MoLD
+R:1039:0x0A/0xA7
+
+# Emperor Mimic
+R:1040:0x0B/0xA7
+
+# Melinda Proudfoot
+R:1041:0x0A/0xA2
+
+# Thrain, the King Under the Mountain
+R:1042:0x0E/0xA2
+
+# Fire golem
+R:1043:0x04/0xA1
+
+# Melkor, Lord of Darkness
+R:1044:0x0A/0x87
+
+# Spirit
+R:1045:0x0A/0x87
+
+# Spirit
+R:1046:0x0E/0x87
+
+# Spirit
+R:1047:0x0E/0x87
+
+# Spirit
+R:1048:0x0A/0x87
+
+# Spirit
+R:1049:0x0F/0x87
+
+# Spirit
+R:1050:0x0A/0x87
+
+# Spirit
+R:1051:0x05/0x87
+
+# Spirit
+R:1052:0x0A/0x87
+
+# Spirit
+R:1053:0x09/0xB5
+
+# Spirit
+R:1054:0x05/0x87
+
+# Spirit
+R:1055:0x09/0x87
+
+# Spirit
+R:1056:0x00/0x87
+
+# Spirit
+R:1057:0x07/0x87
+
+# Spirit
+R:1058:0x04/0x87
+
+# Spirit
+R:1059:0x0D/0x87
+
+# Spirit
+R:1060:0x09/0x87
+
+# Spirit
+R:1061:0x02/0x87
+
+# Spirit
+R:1062:0x06/0x87
+
+# Spirit
+R:1063:0x04/0x87
+
+# Spirit
+R:1064:0x01/0x87
+
+# Spirit
+R:1065:0x02/0x87
+
+# Spirit
+R:1066:0x06/0x87
+
+# Spirit
+R:1067:0x0A/0x87
+
+# Spirit
+R:1068:0x00/0x87
+
+# Spirit
+R:1069:0x09/0x87
+
+# Spirit
+R:1070:0x0A/0x87
+
+# Spirit
+R:1071:0x0D/0x87
+
+# Spirit
+R:1072:0x0B/0x87
+
+# Spirit
+R:1073:0x08/0x87
+
+# Spirit
+R:1074:0x03/0x87
+
+# Spirit
+R:1075:0x0A/0x87
+
+
diff --git a/lib/pref/graf-iso.prf b/lib/pref/graf-iso.prf
new file mode 100644
index 00000000..86cf8cc8
--- /dev/null
+++ b/lib/pref/graf-iso.prf
@@ -0,0 +1,6875 @@
+%:trap-iso.prf
+
+# General Store
+B:0:0x80:0xB1
+
+# Armoury
+B:1:0x80:0xB2
+
+# Weapon Smiths
+B:2:0x80:0xB3
+
+# Temple
+B:3:0x80:0xB4
+
+# Alchemy Shop
+B:4:0x80:0xB5
+
+# Magic Shop
+B:5:0x80:0xB6
+
+# Black Market
+B:6:0x80:0xB7
+
+# Home
+B:7:0x80:0xB8
+
+# Bookstore
+B:8:0x80:0xB9
+
+# Pet shop
+B:9:0x80:0xAB
+
+# Mayors office
+B:10:0x80:0xAB
+
+# Inn
+B:11:0x80:0xB0
+
+# The Soothsayer
+B:12:0x80:0xAB
+
+# Library
+B:13:0x80:0xAB
+
+# Castle
+B:14:0x80:0xAB
+
+# Casino
+B:15:0x80:0xAB
+
+# Beastmaster Shanty
+B:16:0x80:0xAB
+
+# Fighters Hall
+B:17:0x80:0xAB
+
+# Tower of Magery
+B:18:0x80:0xAB
+
+# Inner Temple
+B:19:0x80:0xAB
+
+# Paladins Guild
+B:20:0x80:0xAB
+
+# Rangers Guild
+B:21:0x80:0xAB
+
+# Thunderlords' Hide
+B:22:0x80:0xAB
+
+# The Mirror
+B:23:0x80:0xAB
+
+# Seat of Ruling
+B:24:0x80:0xAB
+
+# Wizards Spire
+B:25:0x80:0xAB
+
+# Priests Circle
+B:26:0x80:0xAB
+
+# Tower of the King
+B:27:0x80:0xAB
+
+# Library
+B:28:0x80:0xAB
+
+# The White Tree
+B:29:0x80:0xAB
+
+# Craftsmaster
+B:30:0x80:0xAB
+
+# Earth-Dome (Nature)
+B:31:0x80:0xAB
+
+# Minstrels Haven
+B:32:0x80:0xAB
+
+# Star-Dome
+B:33:0x80:0xAB
+
+# Valarin Temple
+B:34:0x80:0xAB
+
+# Sea-Dome
+B:35:0x80:0xAB
+
+# The Golden Flower
+B:36:0x80:0xAB
+
+# The Fountain
+B:37:0x80:0xAB
+
+# Axe Smith
+B:38:0x80:0xAB
+
+# Hafted Smith
+B:39:0x80:0xAB
+
+# Polearm Smith
+B:40:0x80:0xAB
+
+# Sword Smith
+B:41:0x80:0xAB
+
+# Rare Jewelry Shop
+B:42:0x80:0xAB
+
+# Jewelry Shop
+B:43:0x80:0xAB
+
+# Footwear Shop
+B:44:0x80:0xAB
+
+# Rare Footwear Shop
+B:45:0x80:0xAB
+
+# Library
+B:46:0x80:0xAB
+
+# Forbidden Library
+B:47:0x80:0xAB
+
+# Expensive Black Market
+B:48:0x80:0xAB
+
+# Common Shop
+B:49:0x80:0xAB
+
+# Dragon Hunter
+B:50:0x80:0xAB
+
+# Speed Ring Market
+B:51:0x80:0xAB
+
+# Scribe
+B:52:0x80:0xAB
+
+# Potion Store
+B:53:0x80:0xAB
+
+# Recaller
+B:54:0x80:0xAB
+
+# Master Archer
+B:55:0x80:0xAB
+
+# Merchants Guild
+B:56:0x80:0xAB
+
+# The Mathom-house
+B:57:0x80:0xAB
+
+# The Prancing Pony
+B:58:0x80:0xAB
+
+# nothing
+F:0:0x80:0xA0
+
+# open floor
+F:1:0x82:0xBC
+
+# fountain
+F:2:0x81:0x8D
+
+# glyph of warding
+F:3:0x8A:0xE1
+
+# open door
+F:4:0x80:0xA7
+
+# broken door
+F:5:0x80:0xA7
+
+# up staircase
+F:6:0x80:0xBC
+
+# down staircase
+F:7:0x80:0xBE
+
+# quest entrance
+F:8:0x80:0xBE
+
+# quest exit
+F:9:0x80:0xBC
+
+# quest down level
+F:10:0x80:0xBE
+
+# quest up level
+F:11:0x80:0xBC
+
+# town exit
+F:12:0x80:0xBE
+
+# shaft down
+F:13:0x80:0xBE
+
+# shaft up
+F:14:0x80:0xBC
+
+# fountain
+F:15:0x81:0x8D
+
+# web
+F:15:0x81:0x8D
+
+# trap
+F:17:0x8A:0xF9
+
+# visible trap -- spiked pit
+F:18:0x8A:0xF9
+
+# visible trap -- poison pit
+F:19:0x8A:0xF8
+
+# visible trap -- rune -- summon
+F:20:0x8A:0xF8
+
+# visible trap -- rune -- teleport
+F:21:0x8A:0xF8
+
+# visible trap -- spot -- fire
+F:22:0x8A:0xFD
+
+# visible trap -- spot -- acid
+F:23:0x8A:0xFA
+
+# visible trap -- dart -- slow
+F:24:0x8A:0xF3
+
+# visible trap -- dart -- lose str
+F:25:0x8A:0xF3
+
+# visible trap -- dart -- lose dex
+F:26:0x8A:0xF3
+
+# visible trap -- dart -- lose con
+F:27:0x8A:0xF3
+
+# visible trap -- gas -- blind
+F:28:0x8A:0xF6
+
+# visible trap -- gas -- confuse
+F:29:0x8A:0xF6
+
+# visible trap -- gas -- poison
+F:30:0x8A:0xF6
+
+# visible trap -- gas -- sleep
+F:31:0x8A:0xF6
+
+# door
+F:32:0x80:0xAB
+
+# locked door
+F:33:0x80:0xAB
+
+# locked door
+F:34:0x80:0xAB
+
+# locked door
+F:35:0x80:0xAB
+
+# locked door
+F:36:0x80:0xAB
+
+# locked door
+F:37:0x80:0xAB
+
+# locked door
+F:38:0x80:0xAB
+
+# locked door
+F:39:0x80:0xAB
+
+# jammed door
+F:40:0x80:0xAB
+
+# jammed door
+F:41:0x80:0xAB
+
+# jammed door
+F:42:0x80:0xAB
+
+# jammed door
+F:43:0x80:0xAB
+
+# jammed door
+F:44:0x80:0xAB
+
+# jammed door
+F:45:0x80:0xAB
+
+# jammed door
+F:46:0x80:0xAB
+
+# jammed door
+F:47:0x80:0xAB
+
+# secret door
+F:48:0x80:0xA3
+
+# pile of rubble
+F:49:0x8A:0xEF
+
+# magma vein
+F:50:0x80:0xA5
+
+# quartz vein
+F:51:0x81:0xF3
+
+# magma vein
+F:52:0x81:0xF3
+
+# quartz vein
+F:53:0x81:0xF3
+
+# magma vein with treasure
+F:54:0x80:0xAA
+
+# quartz vein with treasure
+F:55:0x80:0xAA
+
+# granite wall
+F:56:0x81:0xF0
+
+# granite wall
+F:57:0x81:0xF0
+
+# granite wall
+F:58:0x81:0xF0
+
+# granite wall
+F:59:0x81:0xF0
+
+# permanent wall
+F:60:0x81:0xF3
+
+# permanent wall
+F:61:0x81:0xF3
+
+# permanent wall
+F:62:0x81:0xF3
+
+# permanent wall
+F:63:0x81:0xF3
+
+# explosive rune
+F:64:0x80:0xAA
+
+# Straight Road startpoint
+F:65:0x80:0xAA
+
+# section of the Straight Road
+F:66:0x80:0xAA
+
+# section of the Straight Road
+F:67:0x80:0xAA
+
+# section of the Straight Road
+F:68:0x80:0xAA
+
+# section of the Straight Road
+F:69:0x80:0xAA
+
+# section of the Straight Road
+F:70:0x80:0xAA
+
+# section of the Straight Road (discharged)
+F:71:0x80:0xAA
+
+# Straight Road exit
+F:72:0x80:0xAA
+
+# corrupted section of the Straight Road
+F:73:0x80:0xAA
+
+# Building
+F:74:0x80:0xB1
+
+# permanent wall
+F:75:0x81:0xF3
+
+# permanent wall
+F:76:0x81:0xF3
+
+# permanent wall
+F:77:0x81:0xF3
+
+# permanent wall
+F:78:0x81:0xF3
+
+# stream of shallow water
+F:84:0x82:0xEB
+
+# pool of deep lava
+F:85:0x82:0xAB
+
+# stream of shallow lava
+F:86:0x82:0xAB
+
+# dark pit
+F:87:0x83:0x8B
+
+# dirt
+F:88:0x82:0xF3
+
+# patch of grass
+F:89:0x82:0xE8
+
+# ice
+F:90:0x80:0xAE
+
+# sand
+F:91:0x80:0xAE
+
+# dead tree
+F:92:0x80:0xA3
+
+# ash
+F:93:0x80:0xAE
+
+# mud
+F:94:0x80:0xAE
+
+# ice wall
+F:95:0x80:0x80
+
+# tree
+F:96:0x83:0x88
+
+# mountain chain
+F:97:0x81:0x9D
+
+# sandwall
+F:98:0x80:0xA3
+
+# sandwall
+F:99:0x80:0xA5
+
+# sandwall with treasure
+F:100:0x80:0xAA
+
+# high mountain chain
+F:101:0x80:0xDE
+
+# nether mist
+F:102:0x80:0x80
+
+# Void Jumpgate
+F:160:0x81:0x8C
+
+# Altar of Being
+F:161:0x81:0x94
+
+# Altar of Winds
+F:162:0x81:0x94
+
+# Altar of Force
+F:163:0x81:0x94
+
+# Altar of Darkness
+F:164:0x81:0x94
+
+# Altar of Nature
+F:165:0x81:0x94
+
+# Altar of Sun
+F:166:0x81:0x94
+
+# Altar of Rage
+F:167:0x81:0x94
+
+# Altar of Winds
+F:168:0x81:0x94
+
+# Altar of Stars
+F:169:0x81:0x94
+
+# Altar of Being
+F:170:0x81:0x94
+
+# Altar of Randomness
+F:171:0x81:0x94
+
+# floor
+F:172:0x80:0x81
+
+# Underground Tunnel
+F:173:0x80:0x82
+
+# stream of tainted water
+F:174:0x80:0x80
+
+# monster trap
+F:175:0x81:0x9C
+
+# Void Jumpgate
+F:176:0x80:0x80
+
+# lava wall
+F:177:0x80:0x80
+
+# Great Fire
+F:178:0x80:0x80
+
+# Path to next area
+F:179:0x80:0xBE
+
+# Path to previous area
+F:180:0x80:0xBC
+
+# field
+F:181:0x80:0x80
+
+# Ekkaia, the Encircling Sea
+F:182:0x80:0x80
+
+# Altar of Energy
+F:183:0x80:0x80
+
+# Altar of Matter
+F:184:0x80:0x80
+
+# Altar of Being
+F:185:0x80:0x80
+
+# Altar of Unbeing
+F:186:0x80:0x80
+
+# pool of deep water
+F:187:0x82:0xF0
+
+# glass wall
+F:188:0x80:0xAE
+
+# illusion wall
+F:189:0x80:0xA3
+
+# Grass roof
+F:190:0x82:0xF6
+
+# grass roof top
+F:191:0x82:0xF6
+
+# grass roof chimney
+F:192:0x82:0xF7
+
+# brick roof
+F:193:0x82:0xEE
+
+# brick roof top
+F:194:0x82:0xEE
+
+# brick roof chimney
+F:195:0x82:0xF7
+
+# window
+F:196:0x80:0xA3
+
+# small window
+F:197:0x80:0xA3
+
+# rain barrel
+F:198:0x80:0xA3
+
+# grass with flowers
+F:199:0x82:0xF8
+
+# cobblestone road
+F:200:0x83:0x83
+
+# cobblestone with outlet
+F:201:0x80:0xAE
+
+# small tree
+F:202:0x83:0x88
+
+# town
+F:203:0x80:0xAA
+
+# Underground Tunnel
+F:204:0x80:0x82
+
+# a blazing fire
+F:205:0x80:0x80
+
+# pile of rubble
+F:206:0x8A:0xEF
+
+# rocky ground
+F:207:0x80:0x80
+
+# cloud-like vapour
+F:208:0x80:0x80
+
+# condensing water
+F:209:0x80:0x80
+
+# dense mist
+F:210:0x80:0x80
+
+# hail-stone wall
+F:211:0x80:0x80
+
+# dead small tree
+F:212:0x80:0x80
+
+# something
+K:0:0x80:0x80
+
+# Blindness
+K:1:0x86:0x8C
+
+# Paranoia
+K:2:0x86:0x8C
+
+# Confusion
+K:3:0x86:0x8C
+
+# Hallucination
+K:4:0x86:0x8C
+
+# Cure Poison
+K:5:0x86:0x8C
+
+# Cure Blindness
+K:6:0x86:0x8C
+
+# Cure Paranoia
+K:7:0xA6:0xBB
+
+# Cure Confusion
+K:8:0x86:0x8C
+
+# Weakness
+K:9:0x86:0x8C
+
+# Unhealth
+K:10:0x86:0x8C
+
+# Restore Constitution
+K:11:0x86:0x8C
+
+# Restoring
+K:12:0x86:0x8C
+
+# Stupidity
+K:13:0x86:0x8C
+
+# Naivety
+K:14:0x86:0x8C
+
+# Poison
+K:15:0x86:0x8C
+
+# Sickness
+K:16:0x86:0x8C
+
+# Paralysis
+K:17:0x86:0x8C
+
+# Restore Strength
+K:18:0x86:0x8C
+
+# Disease
+K:19:0x86:0x8C
+
+# Cure Serious Wounds
+K:20:0x86:0x8C
+
+# & Ration~ of Food
+K:21:0x8A:0xBC
+
+# & Hard Biscuit~
+K:22:0x8A:0xBA
+
+# & Strip~ of Venison
+K:23:0x8A:0xBB
+
+# & Slime Mold~
+K:24:0x8A:0xBD
+
+# & Lembas~
+K:25:0x8A:0xBE
+
+# & Pint~ of Fine Ale
+K:26:0x8A:0xB8
+
+# & Pint~ of Fine Wine
+K:27:0x8A:0xB8
+
+# & Mattock~
+K:28:0x9E:0xC4
+
+# & Blue Stone~
+K:29:0xA6:0x8B
+
+# & Broken Dagger~
+K:30:0x88:0xC5
+
+# & Bastard Sword~
+K:31:0x88:0xC6
+
+# & Scimitar~
+K:32:0x88:0xCF
+
+# & Tulwar~
+K:33:0x88:0xCD
+
+# & Broad Sword~
+K:34:0x88:0xD0
+
+# & Short Sword~
+K:35:0x88:0xCC
+
+# & Blade~ of Chaos
+K:36:0x88:0xD6
+
+# & Two-Handed Sword~
+K:37:0x88:0xD4
+
+# & Main Gauche~
+K:38:0x88:0xC8
+
+# & Cutlass~
+K:39:0x88:0xCE
+
+# & Executioner's Sword~
+K:40:0x88:0xD5
+
+# & Katana~
+K:41:0x88:0xD3
+
+# & Long Sword~
+K:42:0x88:0xD1
+
+# & Dagger~
+K:43:0x88:0xC7
+
+# & Rapier~
+K:44:0x88:0xC9
+
+# & Sabre~
+K:45:0x88:0xCB
+
+# & Small Sword~
+K:46:0x88:0xCA
+
+# & Broken Sword~
+K:47:0x88:0xC6
+
+# & Ball-and-Chain~
+K:48:0x88:0xFE
+
+# & Whip~
+K:49:0x88:0xD7
+
+# & Flail~
+K:50:0x88:0xFB
+
+# & Two-Handed Flail~
+K:51:0x88:0xFF
+
+# & Morning Star~
+K:52:0x88:0xFC
+
+# & Mace~
+K:53:0x88:0xF9
+
+# & Quarterstaff~
+K:54:0x88:0xFA
+
+# & War Hammer~
+K:55:0x88:0xF8
+
+# & Lead-Filled Mace~
+K:56:0x88:0xFD
+
+# & Mace~ of Disruption
+K:57:0x89:0x80
+
+# & Lucerne Hammer~
+K:58:0x89:0x85
+
+# & Beaked Axe~
+K:59:0x89:0x88
+
+# & Glaive~
+K:60:0x89:0x8A
+
+# & Halberd~
+K:61:0x89:0x8B
+
+# & Awl-Pike~
+K:62:0x89:0x83
+
+# & Pike~
+K:63:0x89:0x87
+
+# & Spear~
+K:64:0x89:0x81
+
+# & Trident~
+K:65:0x89:0x82
+
+# & Lance~
+K:66:0x89:0x84
+
+# & Great Axe~
+K:67:0x89:0x8D
+
+# & Battle Axe~
+K:68:0x89:0x86
+
+# & Lochaber Axe~
+K:69:0x89:0x8C
+
+# & Broad Axe~
+K:70:0x89:0x89
+
+# & Scythe~
+K:71:0x89:0x8E
+
+# & Scythe~ of Slicing
+K:72:0x89:0x8F
+
+# & Short Bow~
+K:73:0x89:0x90
+
+# & Long Bow~
+K:74:0x89:0x91
+
+# & Light Crossbow~
+K:75:0x89:0x92
+
+# & Heavy Crossbow~
+K:76:0x89:0x93
+
+# & Sling~
+K:77:0x89:0x94
+
+# & Arrow~
+K:78:0x89:0xB8
+
+# & Seeker Arrow~
+K:79:0x89:0xB9
+
+# & Bolt~
+K:80:0x89:0xBA
+
+# & Seeker Bolt~
+K:81:0x89:0xBB
+
+# & Rounded Pebble~
+K:82:0x89:0xBC
+
+# & Iron Shot~
+K:83:0x89:0xBD
+
+# & Shovel~
+K:84:0x8A:0xC7
+
+# & Gnomish Shovel~
+K:85:0x8A:0xC8
+
+# & Dwarven Shovel~
+K:86:0x8A:0xC9
+
+# & Pick~
+K:87:0x8A:0xC4
+
+# & Orcish Pick~
+K:88:0x8A:0xC5
+
+# & Dwarven Pick~
+K:89:0x8A:0xC9
+
+# & Elven Cloak~
+K:90:0x88:0x81
+
+# & Pair~ of Soft Leather Boots
+K:91:0x87:0xC6
+
+# & Pair~ of Hard Leather Boots
+K:92:0x87:0xC7
+
+# & Pair~ of Metal Shod Boots
+K:93:0x87:0xC8
+
+# & Hard Leather Cap~
+K:94:0x87:0x90
+
+# & Metal Cap~
+K:95:0x87:0x91
+
+# & Iron Helm~
+K:96:0x87:0x92
+
+# & Steel Helm~
+K:97:0x87:0x93
+
+# & Iron Crown~
+K:98:0x87:0x94
+
+# & Golden Crown~
+K:99:0x87:0x95
+
+# & Jewel Encrusted Crown~
+K:100:0x87:0x96
+
+# & Robe~
+K:101:0x88:0x84
+
+# & Filthy Rag~
+K:102:0x88:0x83
+
+# Soft Leather Armour~
+K:103:0x88:0x85
+
+# Soft Studded Leather~
+K:104:0x88:0x86
+
+# Hard Leather Armour~
+K:105:0x88:0x87
+
+# Hard Studded Leather~
+K:106:0x88:0x88
+
+# Leather Scale Mail~
+K:107:0x88:0x89
+
+# Metal Scale Mail~
+K:108:0x88:0x8A
+
+# Chain Mail~
+K:109:0x88:0x8C
+
+# Rusty Chain Mail~
+K:110:0x88:0x8B
+
+# Augmented Chain Mail~
+K:111:0x88:0x8E
+
+# Bar Chain Mail~
+K:112:0x88:0x8F
+
+# Metal Brigandine Armour~
+K:113:0x88:0x90
+
+# Partial Plate Armour~
+K:114:0x88:0x91
+
+# Metal Lamellar Armour~
+K:115:0x88:0x92
+
+# Full Plate Armour~
+K:116:0x88:0x93
+
+# Ribbed Plate Armour~
+K:117:0x88:0x94
+
+# Adamantite Plate Mail~
+K:118:0x88:0x97
+
+# Mithril Plate Mail~
+K:119:0x88:0x96
+
+# Mithril Chain Mail~
+K:120:0x88:0x95
+
+# Double Chain Mail~
+K:121:0x88:0x8D
+
+# & Shield~ of Deflection
+K:122:0x87:0xD0
+
+# & Cloak~
+K:123:0x88:0x80
+
+# & Shadow Cloak~
+K:124:0x88:0x81
+
+# & Set~ of Leather Gloves
+K:125:0x87:0xC9
+
+# & Set~ of Gauntlets
+K:126:0x87:0xCA
+
+# & Set~ of Cesti
+K:127:0x87:0xCB
+
+# & Small Leather Shield~
+K:128:0x87:0xCC
+
+# & Large Leather Shield~
+K:129:0x87:0xCD
+
+# & Small Metal Shield~
+K:130:0x87:0xCE
+
+# & Large Metal Shield~
+K:131:0x87:0xCF
+
+# Strength
+K:132:0x85:0xB9
+
+# Dexterity
+K:133:0x85:0xBB
+
+# Constitution
+K:134:0x85:0xBB
+
+# Intelligence
+K:135:0x85:0xBB
+
+# Speed
+K:136:0x85:0xBB
+
+# Searching
+K:137:0x85:0xBB
+
+# Teleportation
+K:138:0x85:0xBB
+
+# Slow Digestion
+K:139:0x85:0xBB
+
+# Fire Resistance
+K:140:0x85:0xBB
+
+# Cold Resistance
+K:141:0x85:0xBB
+
+# Levitation
+K:142:0x85:0xBB
+
+# Poison Resistance
+K:143:0x85:0xBB
+
+# Free Action
+K:144:0x85:0xBB
+
+# Weakness
+K:145:0x85:0xBB
+
+# Flames
+K:146:0x85:0xBB
+
+# Acid
+K:147:0x85:0xBB
+
+# Ice
+K:148:0x85:0xBB
+
+# Woe
+K:149:0x85:0xBB
+
+# Stupidity
+K:150:0x85:0xBB
+
+# Damage
+K:151:0x85:0xBB
+
+# Accuracy
+K:152:0x85:0xBB
+
+# Protection
+K:153:0x85:0xBB
+
+# Aggravate Monster
+K:154:0x85:0xBB
+
+# See Invisible
+K:155:0x85:0xBB
+
+# Sustain Strength
+K:156:0x85:0xBB
+
+# Sustain Intelligence
+K:157:0x85:0xBB
+
+# Sustain Wisdom
+K:158:0x85:0xBB
+
+# Sustain Constitution
+K:159:0x85:0xBB
+
+# Sustain Dexterity
+K:160:0x85:0xBB
+
+# Sustain Charisma
+K:161:0x85:0xBB
+
+# Slaying
+K:162:0x85:0xBB
+
+# Brilliance
+K:163:0x86:0xFB
+
+# Charisma
+K:164:0x86:0xFB
+
+# Searching
+K:165:0x86:0xFB
+
+# Teleportation
+K:166:0x86:0xFB
+
+# Slow Digestion
+K:167:0x86:0xFB
+
+# Acid Resistance
+K:168:0x86:0xFB
+
+# Adornment
+K:169:0x86:0xFB
+
+# Double Ring Mail~
+K:170:0x88:0x93
+
+# the Magi
+K:171:0x86:0xFB
+
+# Doom
+K:172:0x86:0xFB
+
+# Enchant Weapon To-Hit
+K:173:0x85:0x94
+
+# Enchant Weapon To-Dam
+K:174:0x85:0x94
+
+# Enchant Armor
+K:175:0x85:0x94
+
+# Identify
+K:176:0x85:0x94
+
+# *Identify*
+K:177:0x85:0x94
+
+# Rumour
+K:178:0x85:0x94
+
+# Chaos
+K:179:0x85:0x94
+
+# Remove Curse
+K:180:0x85:0x94
+
+# Light
+K:181:0x85:0x94
+
+# Fire
+K:182:0x85:0x94
+
+# Ice
+K:183:0x85:0x94
+
+# Summon Monster
+K:184:0x85:0x94
+
+# Phase Door
+K:185:0x85:0x94
+
+# Teleportation
+K:186:0x85:0x94
+
+# Teleport Level
+K:187:0x85:0x94
+
+# Monster Confusion
+K:188:0x85:0x94
+
+# Magic Mapping
+K:189:0x85:0x94
+
+# Rune of Protection
+K:190:0x85:0x94
+
+# *Remove Curse*
+K:191:0x85:0x94
+
+# Treasure Detection
+K:192:0x85:0x94
+
+# Object Detection
+K:193:0x85:0x94
+
+# Trap Detection
+K:194:0x85:0x94
+
+# & Sheaf Arrow~
+K:195:0x89:0xB9
+
+# & Mithril Shot~
+K:196:0x89:0xBD
+
+# Door
+K:197:0x85:0x94
+
+# Acquirement
+K:198:0x85:0x94
+
+# *Acquirement*
+K:199:0x85:0x94
+
+# Mass Genocide
+K:200:0x85:0x94
+
+# Detect Invisible
+K:201:0x85:0x94
+
+# Aggravate Monster
+K:202:0x85:0x94
+
+# Trap Creation
+K:203:0x85:0x94
+
+# Trap
+K:204:0x85:0x94
+
+# Artifact Creation
+K:205:0x85:0x94
+
+# Recharging
+K:206:0x85:0x94
+
+# Genocide
+K:207:0x85:0x94
+
+# Darkness
+K:208:0x85:0x94
+
+# Protection from Evil
+K:209:0x85:0x94
+
+# Satisfy Hunger
+K:210:0x85:0x94
+
+# Dispel Undead
+K:211:0x85:0x94
+
+# *Enchant Weapon*
+K:212:0x85:0x94
+
+# Curse Weapon
+K:213:0x85:0x94
+
+# *Enchant Armor*
+K:214:0x85:0x94
+
+# Curse Armor
+K:215:0x85:0x94
+
+# Summon Undead
+K:216:0x85:0x94
+
+# Blessing
+K:217:0x85:0x94
+
+# Holy Chant
+K:218:0x85:0x94
+
+# Holy Prayer
+K:219:0x85:0x94
+
+# Word of Recall
+K:220:0x85:0x94
+
+# *Destruction*
+K:221:0x85:0x94
+
+# Slime Mold Juice
+K:222:0x85:0xFD
+
+# Apple Juice
+K:223:0x85:0xFD
+
+# Water
+K:224:0x85:0xFD
+
+# Strength
+K:225:0x85:0xFD
+
+# Weakness
+K:226:0x85:0xFD
+
+# Restore Strength
+K:227:0x85:0xFD
+
+# Intelligence
+K:228:0x85:0xFD
+
+# Stupidity
+K:229:0x85:0xFD
+
+# Restore Intelligence
+K:230:0x85:0xFD
+
+# Wisdom
+K:231:0x85:0xFD
+
+# Naivety
+K:232:0x85:0xFD
+
+# Restore Wisdom
+K:233:0x85:0xFD
+
+# Charisma
+K:234:0x85:0xFD
+
+# Ugliness
+K:235:0x85:0xFD
+
+# Restore Charisma
+K:236:0x85:0xFD
+
+# Curing
+K:237:0x85:0xFD
+
+# Invulnerability
+K:238:0x85:0xFD
+
+# New Life
+K:239:0x85:0xFD
+
+# Cure Serious Wounds
+K:240:0x85:0xFD
+
+# Cure Critical Wounds
+K:241:0x85:0xFD
+
+# Healing
+K:242:0x85:0xFD
+
+# Constitution
+K:243:0x85:0xFD
+
+# Experience
+K:244:0x85:0xFD
+
+# Sleep
+K:245:0x85:0xFD
+
+# Blindness
+K:246:0x85:0xFD
+
+# Booze
+K:247:0x85:0xFD
+
+# Poison
+K:248:0x85:0xFD
+
+# Speed
+K:249:0x85:0xFD
+
+# Slowness
+K:250:0x85:0xFD
+
+# Dexterity
+K:251:0x85:0xFD
+
+# Restore Dexterity
+K:252:0x85:0xFD
+
+# Restore Constitution
+K:253:0x85:0xFD
+
+# Lose Memories
+K:254:0x85:0xFD
+
+# Salt Water
+K:255:0x85:0xFD
+
+# Enlightenment
+K:256:0x85:0xFD
+
+# Heroism
+K:257:0x85:0xFD
+
+# Berserk Strength
+K:258:0x85:0xFD
+
+# Boldness
+K:259:0x85:0xFD
+
+# Restore Life Levels
+K:260:0x85:0xFD
+
+# Resist Heat
+K:261:0x85:0xFD
+
+# Resist Cold
+K:262:0x85:0xFD
+
+# Detect Invisible
+K:263:0x85:0xFD
+
+# Slow Poison
+K:264:0x85:0xFD
+
+# Neutralise Poison
+K:265:0x85:0xFD
+
+# Restore Mana
+K:266:0x85:0xFD
+
+# Infra-vision
+K:267:0x85:0xFD
+
+# Resistance
+K:268:0x85:0xFD
+
+# Spell
+K:269:0x86:0xCB
+
+# Manathrust
+K:270:0x86:0xCB
+
+# Fireflash
+K:271:0x86:0xCB
+
+# Firewall
+K:272:0x86:0xCB
+
+# Tidal Wave
+K:273:0x86:0xCB
+
+# Ice Storm
+K:274:0x86:0xCB
+
+# Noxious Cloud
+K:275:0x86:0xCB
+
+# Poison Blood
+K:276:0x86:0xCB
+
+# Thunderstorm
+K:277:0x86:0xCB
+
+# Dig
+K:278:0x86:0xCB
+
+# Stone Prison
+K:279:0x86:0xCB
+
+# Strike
+K:280:0x86:0xCB
+
+# Teleport Away
+K:281:0x86:0xCB
+
+# Summon Animal
+K:282:0x86:0xCB
+
+# Magelock
+K:283:0x86:0xCB
+
+# Slow Monster
+K:284:0x86:0xCB
+
+# Essence of Speed
+K:285:0x9F:0x84
+
+# Banishment
+K:286:0x86:0xCB
+
+# Disperse Magic
+K:287:0x86:0xCB
+
+# Charm
+K:288:0x86:0xCB
+
+# Confuse
+K:289:0x86:0xCB
+
+# Demon Blade
+K:290:0x86:0xCB
+
+# Heal Monster
+K:291:0x86:0xCB
+
+# Haste Monster
+K:292:0x86:0xCB
+
+# & Flight Arrow~
+K:293:0x89:0xB9
+
+# Acid Bolts
+K:294:0x86:0xCB
+
+# Dragon's Flame
+K:295:0x86:0xCB
+
+# Dragon's Frost
+K:296:0x86:0xCB
+
+# Dragon's Breath
+K:297:0x86:0xCB
+
+# Annihilation
+K:298:0x86:0xCB
+
+# Rockets
+K:299:0x86:0xCB
+
+# Spell
+K:300:0x87:0x8A
+
+# Nothing
+K:301:0x87:0x8A
+
+# Globe of Light
+K:302:0x87:0x8A
+
+# Fiery Shield
+K:303:0x87:0x8A
+
+# Remove Curses
+K:304:0x87:0x8A
+
+# Wings of Winds
+K:305:0x87:0x8A
+
+# Shake
+K:306:0x87:0x8A
+
+# Disarm
+K:307:0x87:0x8A
+
+# Teleportation
+K:308:0x87:0x8A
+
+# Probability Travel
+K:309:0x87:0x8A
+
+# Recovery
+K:310:0x87:0x8A
+
+# Healing
+K:311:0x87:0x8A
+
+# Vision
+K:312:0x87:0x8A
+
+# Identify
+K:313:0x87:0x8A
+
+# Sense Hidden
+K:314:0x87:0x8A
+
+# Reveal Ways
+K:315:0x87:0x8A
+
+# Sense Monsters
+K:316:0x87:0x8A
+
+# Genocide
+K:317:0x87:0x8A
+
+# Summon
+K:318:0x87:0x8A
+
+# Curing
+K:319:0x87:0x8A
+
+# Wish
+K:320:0x87:0x8A
+
+# Mana
+K:321:0x87:0x8A
+
+# Darkness
+K:322:0x87:0x8A
+
+# Genocide
+K:323:0x87:0x8A
+
+# Power
+K:324:0x87:0x8A
+
+# the Magi
+K:325:0x87:0x8A
+
+# Perception
+K:326:0x87:0x8A
+
+# Holiness
+K:327:0x87:0x8A
+
+# Enlightenment
+K:328:0x87:0x8A
+
+# Healing
+K:329:0x87:0x8A
+
+# & Tome~ of Magical Energy
+K:330:0x8B:0xD8
+
+# & Tome~ of the Eternal Flame
+K:331:0x8B:0xD9
+
+# & Tome~ of the Blowing Wind
+K:332:0x8B:0xDA
+
+# & Tome~ of the Impenetrable Earth
+K:333:0x8B:0xDB
+
+# & Tome~ of the Everrunning Wave
+K:334:0x8B:0xDC
+
+# & Tome~ of Translocation
+K:335:0x8B:0xDD
+
+# & Tome~ of the Tree
+K:336:0x8B:0xDE
+
+# & Tome~ of Knowledge
+K:337:0x8B:0xDF
+
+# & Small wooden chest~
+K:338:0x85:0xD1
+
+# & Large wooden chest~
+K:339:0x85:0xD2
+
+# & Small iron chest~
+K:340:0x85:0xD3
+
+# & Large iron chest~
+K:341:0x85:0xD4
+
+# & Small steel chest~
+K:342:0x85:0xD5
+
+# & Large steel chest~
+K:343:0x85:0xD6
+
+# & Ruined chest~
+K:344:0x85:0xD7
+
+# & Iron Spike~
+K:345:0x8A:0xC1
+
+# & Wooden Torch~
+K:346:0x8A:0xC3
+
+# & Brass Lantern~
+K:347:0x8A:0xC2
+
+# & Flask~ of oil
+K:348:0x8A:0xC0
+
+# & Empty Bottle~
+K:349:0x8A:0xBF
+
+# Havoc
+K:350:0x86:0xBB
+
+# Door
+K:351:0x86:0xBB
+
+# Trap Location
+K:352:0x86:0xBB
+
+# Probing
+K:353:0x86:0xBB
+
+# Recall
+K:354:0x86:0xBB
+
+# Illumination
+K:355:0x86:0xBB
+
+# Light
+K:356:0x86:0xBB
+
+# Lightning Bolts
+K:357:0x86:0xBB
+
+# Frost Bolts
+K:358:0x86:0xBB
+
+# Fire Bolts
+K:359:0x86:0xBB
+
+# Polymorph
+K:360:0x86:0xBB
+
+# Slow Monster
+K:361:0x86:0xBB
+
+# Sleep Monster
+K:362:0x86:0xBB
+
+# Drain Life
+K:363:0x86:0xBB
+
+# Teleport Other
+K:364:0x86:0xBB
+
+# Disarming
+K:365:0x86:0xBB
+
+# Lightning Balls
+K:366:0x86:0xBB
+
+# Cold Balls
+K:367:0x86:0xBB
+
+# Fire Balls
+K:368:0x86:0xBB
+
+# Acid Balls
+K:369:0x86:0xBB
+
+# Acid Bolts
+K:370:0x86:0xBB
+
+# Enlightenment
+K:371:0x86:0xBB
+
+# Perception
+K:372:0x86:0xBB
+
+# Curing
+K:373:0x86:0xBB
+
+# Healing
+K:374:0x86:0xBB
+
+# Detection
+K:375:0x86:0xBB
+
+# Restoration
+K:376:0x86:0xBB
+
+# Speed
+K:377:0x86:0xBB
+
+# Spell
+K:378:0xA3:0xFC
+
+# Spell
+K:379:0x89:0xF8
+
+# [Beings of Darkness]
+K:380:0x89:0xF9
+
+# [Material Shadow]
+K:381:0x89:0xFA
+
+# [Nature's Wrath]
+K:382:0x89:0xFB
+
+# [Sign of Chaos]
+K:383:0x89:0xD0
+
+# [Chaos Mastery]
+K:384:0x89:0xD1
+
+# [Chaos Channels]
+K:385:0x89:0xD2
+
+# [Armageddon Tome]
+K:386:0x89:0xD3
+
+# [Nether Openings]
+K:387:0x8A:0x80
+
+# [Unholy Blessings]
+K:388:0x8A:0x81
+
+# & Firestone~
+K:389:0x8A:0xCA
+
+# & Small Firestone~
+K:390:0x8A:0xCB
+
+# & Broken Skull~
+K:391:0x8A:0xCC
+
+# & Broken Bone~
+K:392:0x8A:0xCD
+
+# & Canine Skeleton~
+K:393:0x8A:0xD2
+
+# & Rodent Skeleton~
+K:394:0x8A:0xD3
+
+# & Human Skeleton~
+K:395:0x8A:0xCE
+
+# & Dwarf Skeleton~
+K:396:0x8A:0xD0
+
+# & Elf Skeleton~
+K:397:0x8A:0xCF
+
+# & Gnome Skeleton~
+K:398:0x8A:0xD1
+
+# & Great Hammer~
+K:399:0x9E:0xC2
+
+# Black Dragon Scale Mail~
+K:400:0x88:0xBA
+
+# Blue Dragon Scale Mail~
+K:401:0x88:0xB8
+
+# White Dragon Scale Mail~
+K:402:0x88:0xB9
+
+# Red Dragon Scale Mail~
+K:403:0x88:0xBB
+
+# Green Dragon Scale Mail~
+K:404:0x88:0xBC
+
+# Multi-Hued Dragon Scale Mail~
+K:405:0x88:0xC3
+
+# Pseudo Dragon Scale Mail~
+K:406:0x88:0xBF
+
+# Law Dragon Scale Mail~
+K:407:0x88:0xC1
+
+# Bronze Dragon Scale Mail~
+K:408:0x88:0xBD
+
+# Gold Dragon Scale Mail~
+K:409:0x88:0xBE
+
+# Chaos Dragon Scale Mail~
+K:410:0x88:0xC0
+
+# Balance Dragon Scale Mail~
+K:411:0x88:0xC2
+
+# Power Dragon Scale Mail~
+K:412:0x88:0xC4
+
+# & Dragon Helm~
+K:413:0x87:0xBA
+
+# & Dragon Shield~
+K:414:0x87:0xD4
+
+# Death
+K:415:0x85:0xFD
+
+# Ruination
+K:416:0x85:0xFD
+
+# Detonations
+K:417:0x85:0xFD
+
+# Augmentation
+K:418:0x85:0xFD
+
+# *Healing*
+K:419:0x85:0xFD
+
+# Life
+K:420:0x85:0xFD
+
+# Self Knowledge
+K:421:0x85:0xFD
+
+# *Enlightenment*
+K:422:0x85:0xFD
+
+# [Necromantic Incantations]
+K:423:0x8A:0x82
+
+# [Curses of Angmar]
+K:424:0x8A:0x83
+
+# Fear Resistance
+K:425:0x85:0xBB
+
+# Light and Darkness Resistance
+K:426:0x85:0xBB
+
+# Nether Resistance
+K:427:0x85:0xBB
+
+# Nexus Resistance
+K:428:0x85:0xBB
+
+# Sound Resistance
+K:429:0x85:0xBB
+
+# Confusion Resistance
+K:430:0x85:0xBB
+
+# Shard Resistance
+K:431:0x85:0xBB
+
+# Disenchantment Resistance
+K:432:0x85:0xBB
+
+# Chaos Resistance
+K:433:0x85:0xBB
+
+# Blindness Resistance
+K:434:0x85:0xBB
+
+# Lordly Protection
+K:435:0x85:0xBB
+
+# Extra Attacks
+K:436:0x85:0xBB
+
+# Cure Light Wounds
+K:437:0x85:0xFD
+
+# Clumsiness
+K:438:0x85:0xFD
+
+# Sickliness
+K:439:0x85:0xFD
+
+# Map of Bree
+K:440:0xA5:0xB8
+
+# Map of Gondolin
+K:441:0xA5:0xB8
+
+# Map of Lothlorien
+K:442:0xA5:0xB8
+
+# Map of Minas Anor
+K:443:0xA5:0xB8
+
+# & Silver Arrow~
+K:465:0xA6:0xB9
+
+# & Silver Bolt~
+K:466:0xA6:0xBA
+
+# Lightning Resistance
+K:467:0x86:0xF8
+
+# Wisdom
+K:468:0x86:0xF8
+
+# Regeneration
+K:469:0x86:0xF8
+
+# Infravision
+K:470:0x86:0xF8
+
+# Devotion
+K:471:0x86:0xF8
+
+# Weaponmastery
+K:472:0x86:0xF8
+
+# Trickery
+K:473:0x86:0xF8
+
+# Telepathy
+K:474:0x86:0xF8
+
+# Sustenance
+K:475:0x86:0xF8
+
+# & Palantir~
+K:476:0xA6:0xBF
+
+# & Elfstone~
+K:477:0xA6:0xBB
+
+# & Jewel~
+K:478:0xA6:0xBC
+
+# & Ring~
+K:479:0xA6:0xBD
+
+# copper
+K:480:0x85:0x89
+
+# copper
+K:481:0x85:0x89
+
+# copper
+K:482:0x85:0x89
+
+# silver
+K:483:0x85:0x8A
+
+# silver
+K:484:0x85:0x8A
+
+# silver
+K:485:0x85:0x8A
+
+# garnets
+K:486:0x85:0x8E
+
+# garnets
+K:487:0x85:0x8E
+
+# gold
+K:488:0x85:0x8B
+
+# gold
+K:489:0x85:0x8B
+
+# gold
+K:490:0x85:0x8B
+
+# opals
+K:491:0x85:0x8F
+
+# sapphires
+K:492:0x85:0x90
+
+# rubies
+K:493:0x85:0x91
+
+# diamonds
+K:494:0x85:0x92
+
+# emeralds
+K:495:0x85:0x93
+
+# mithril
+K:496:0x85:0x8C
+
+# adamantite
+K:497:0x85:0x8D
+
+# & Mighty Hammer~
+K:498:0x9E:0xC2
+
+# & Massive Iron Crown~
+K:499:0x87:0x94
+
+# & Phial~
+K:500:0x8A:0xD5
+
+# & Star~
+K:501:0x8A:0xD6
+
+# & Arkenstone~
+K:502:0x8A:0xD7
+
+# & Amulet~
+K:503:0x85:0xCE
+
+# & Amulet~
+K:504:0x85:0xCF
+
+# & Necklace~
+K:505:0x85:0xD0
+
+# & Ring~
+K:506:0x85:0xC7
+
+# & Ring~
+K:507:0x85:0xC8
+
+# & Ring~
+K:508:0x85:0xCA
+
+# & Ring~
+K:509:0x85:0xCB
+
+# & Ring~
+K:510:0x85:0xCC
+
+# & Ring~
+K:511:0x85:0xCD
+
+# [Rites of Initiation]
+K:512:0x8A:0x88
+
+# [Ways of War]
+K:513:0x8A:0x89
+
+# [Divine Retribution]
+K:514:0x8A:0x8A
+
+# [Essence of Fury]
+K:515:0x8A:0x8B
+
+# [Novice Crafts]
+K:516:0x8A:0x84
+
+# [Arcane Channels]
+K:517:0x8A:0x85
+
+# [Sigils of Wizardry]
+K:518:0x8A:0x86
+
+# [Mana Focus]
+K:519:0x8A:0x87
+
+# Reflection
+K:520:0x86:0xFB
+
+# Anti-Magic
+K:521:0x86:0xFB
+
+# Anti-Teleportation
+K:522:0x86:0xFB
+
+# Resistance
+K:523:0x86:0xFB
+
+# & Zweihander~
+K:524:0x9E:0xC4
+
+# & Dwarven Lantern~
+K:525:0xA6:0x8C
+
+# Splint Mail~
+K:526:0x88:0x94
+
+# & Everburning Torch~
+K:527:0xA6:0x8D
+
+# & Trifurcate Spear~
+K:528:0x9E:0xBD
+
+# & Three Piece Rod~
+K:529:0x9E:0xB8
+
+# & Feanorian Lamp~
+K:530:0xA6:0x8E
+
+# & Fur Cloak~
+K:531:0x88:0x81
+
+# Water Curing
+K:532:0x9E:0xBE
+
+# & Hatchet~
+K:533:0x9E:0xC7
+
+# Rhino Hide Armour~
+K:535:0x88:0x90
+
+# Leather Jacket~
+K:536:0x88:0x87
+
+# & Sickle~
+K:537:0x9E:0xC8
+
+# [Psychoportation]
+K:538:0x9E:0xBF
+
+# [Clairsentience]
+K:539:0x9E:0xC9
+
+# [Telekinesis]
+K:540:0x9E:0xCB
+
+# [Empathy]
+K:541:0x9E:0xCA
+
+# & Club~
+K:542:0x9E:0xCA
+
+# & Broad Spear~
+K:543:0x9E:0xBC
+
+# & Khopesh~
+K:544:0x9E:0xCC
+
+# & Flamberge~
+K:545:0x9E:0xBB
+
+# & Claymore~
+K:546:0x9E:0xC5
+
+# & Espadon~
+K:547:0x9E:0xC6
+
+# & Great Scimitar~
+K:548:0x9E:0xC3
+
+# Arrow
+K:549:0x8A:0xD8
+
+# Bolt
+K:550:0x8A:0xD9
+
+# & Fauchard~
+K:551:0x9E:0xCD
+
+# & Guisarme~
+K:552:0x9E:0xCE
+
+# & Heavy Lance~
+K:553:0x9E:0xBA
+
+# & Basillard~
+K:554:0x9E:0xD1
+
+# Catapult
+K:555:0x8A:0xDA
+
+# Ring Mail~
+K:556:0x88:0x94
+
+# Cord Armour~
+K:557:0x88:0x88
+
+# Paper Armour~
+K:558:0x88:0xB9
+
+# Padded Armour~
+K:559:0x88:0x89
+
+# Fumes
+K:560:0x8A:0xDB
+
+# Stone and Hide Armour~
+K:561:0x88:0x8F
+
+# Magic
+K:562:0x8A:0xDC
+
+# Device
+K:563:0x8A:0xDD
+
+# Nothing
+K:564:0xA6:0xD4
+
+# Poison
+K:565:0x9E:0xF8
+
+# Nothing
+K:566:0xA6:0xD4
+
+# Nothing
+K:567:0xA6:0xD4
+
+# Nothing
+K:568:0xA6:0xD4
+
+# Nothing
+K:569:0xA6:0xD4
+
+# Explosion
+K:570:0x9E:0xF9
+
+# Teleport
+K:571:0x9E:0xFA
+
+# Nothing
+K:572:0xA6:0xD4
+
+# & Blood~ of Life
+K:573:0x85:0xFD
+
+# Cold
+K:574:0x9E:0xFB
+
+# Fire
+K:575:0x9E:0xFC
+
+# Acid
+K:576:0x9E:0xFD
+
+# & Mage Staff~
+K:577:0x9F:0xB8
+
+# Lightning
+K:578:0x85:0xB8
+
+# Life
+K:579:0x9E:0xFE
+
+# Confusion
+K:580:0x9E:0xFF
+
+# Light
+K:581:0x9F:0x80
+
+# & Ring~
+K:582:0x85:0xBD
+
+# Invisibility
+K:583:0x85:0xFD
+
+# Chaos
+K:584:0x9F:0x81
+
+# Corruption
+K:585:0x85:0xFD
+
+# Invisibility
+K:586:0x85:0xFD
+
+# Time
+K:587:0x9F:0x82
+
+# Deep Thoughts
+K:588:0x85:0x94
+
+# More Deep Thoughts
+K:589:0x85:0x95
+
+# Compendium of Deep Thoughts
+K:590:0x85:0x96
+
+# Artifact Lore Vol. I
+K:591:0x85:0x94
+
+# Artifact Lore Vol. II
+K:592:0x85:0x95
+
+# Artifact Lore Vol. III
+K:593:0x85:0x97
+
+# Monstrous Compendium 1
+K:594:0x85:0x97
+
+# Monstrous Compendium 2
+K:595:0x85:0x96
+
+# Monstrous Compendium 3
+K:596:0x85:0x95
+
+# Monstrous Compendium 4
+K:597:0x85:0x94
+
+# Monstrous Compendium 5
+K:598:0x85:0x97
+
+# Monstrous Compendium 6
+K:599:0x85:0x96
+
+# Monstrous Compendium 7
+K:600:0x85:0x95
+
+# Monstrous Compendium 8
+K:601:0x85:0x94
+
+# Monstrous Compendium 9
+K:602:0x85:0x95
+
+# Monstrous Compendium 10
+K:603:0x85:0x96
+
+# Monstrous Compendium 11
+K:604:0x85:0x97
+
+# Abomination
+K:605:0x85:0xFD
+
+# Shape of Wolf
+K:606:0x85:0xFD
+
+# Shape of Ape
+K:607:0x85:0xFD
+
+# Shape of Goat
+K:608:0x85:0xFD
+
+# Shape of Insect
+K:609:0x85:0xFD
+
+# Shape of Sparrow
+K:610:0x85:0xFD
+
+# Shape of Ent
+K:611:0x85:0xFD
+
+# Shape of Vampire
+K:612:0x85:0xFD
+
+# Shape of Spider
+K:613:0x85:0xFD
+
+# Shape of Mana ball
+K:614:0x85:0xFD
+
+# Shape of Fire cloud
+K:615:0x85:0xFD
+
+# Shape of Cold cloud
+K:616:0x85:0xFD
+
+# Shape of Chaos cloud
+K:617:0x85:0xFD
+
+# [Wolf]
+K:618:0x8B:0x98
+
+# [Ape]
+K:619:0x8B:0x99
+
+# [Goat]
+K:620:0x8B:0x9A
+
+# [Insect]
+K:621:0x8B:0x9B
+
+# [Sparrow]
+K:622:0x8B:0x9C
+
+# [Ent]
+K:623:0x8B:0x9D
+
+# [Vampire]
+K:624:0x8B:0x9E
+
+# [Spider]
+K:625:0x8B:0x9F
+
+# [Mana ball]
+K:626:0x8B:0xA0
+
+# [Fire cloud]
+K:627:0x8B:0xA1
+
+# [Cold cloud]
+K:628:0x8B:0xA2
+
+# [Chaos Cloud]
+K:629:0x8B:0xA3
+
+# [Ghost]
+K:630:0x8B:0xA4
+
+# [Kobold]
+K:631:0x8B:0xA5
+
+# [Dragon]
+K:632:0x8B:0xA6
+
+# [Demon]
+K:633:0x8B:0xA7
+
+# [Hound]
+K:634:0x8B:0xA8
+
+# [Quylthulg]
+K:635:0x8B:0xA9
+
+# [Maia]
+K:636:0x8B:0xAA
+
+# [Serpent]
+K:637:0x8B:0xAB
+
+# [Giant]
+K:638:0x8B:0xAC
+
+# [Vala]
+K:639:0x8B:0xAD
+
+# Magic
+K:640:0x9F:0x83
+
+# corpse
+K:641:0x9F:0xB9
+
+# skeleton
+K:642:0x8A:0xCE
+
+# head
+K:643:0x8A:0xCC
+
+# skull
+K:644:0x8A:0xCC
+
+# raw meat
+K:645:0x8A:0xBB
+
+# & Thunderlord Coat~
+K:646:0x88:0xBE
+
+# & Stone~
+K:647:0x8A:0xD4
+
+# & small wooden Boomerang~
+K:648:0x9F:0xBA
+
+# & wooden Boomerang~
+K:649:0x9F:0xBB
+
+# & small metal Boomerang~
+K:650:0x9F:0xBC
+
+# & metal Boomerang~
+K:651:0x9F:0xBD
+
+# & Anchor~
+K:652:0x8A:0x96
+
+# & ~
+K:653:0xA6:0xD4
+
+# Summon never-moving pet
+K:654:0x85:0x95
+
+# [Life in symbiosis]
+K:655:0x9F:0xBE
+
+# [Perfect Symbiosis]
+K:656:0x9F:0xBE
+
+# Cure Light Insanity
+K:657:0x85:0xFD
+
+# Cure Serious Insanity
+K:658:0x85:0xFD
+
+# Cure Critical Insanity
+K:659:0x85:0xFD
+
+# Cure Insanity
+K:660:0x85:0xFD
+
+# & Phial~
+K:661:0x8A:0xD5
+
+# Random Artifact
+K:662:0xA6:0xD4
+
+# Craftmanship
+K:663:0x85:0x97
+
+# The One Ring
+K:664:0x85:0x96
+
+# & Book~ of the Lays of the Heroes
+K:665:0x9F:0xBF
+
+# & Book~ of Sound Patterns
+K:666:0x9F:0xBF
+
+# [Harps of Rivendell]
+K:667:0x9F:0xBF
+
+# [Lays of Beleriand]
+K:668:0x9F:0xBF
+
+# & Flute~
+K:669:0x9F:0xC0
+
+# & Drum~
+K:670:0x9F:0xC1
+
+# & Harp~
+K:671:0x9F:0xC2
+
+# & Banjo~
+K:672:0x9F:0xC4
+
+# & Lute~
+K:673:0x9F:0xC3
+
+# & Mandolin~
+K:674:0x9F:0xC3
+
+# & Palantir~
+K:675:0x8A:0x97
+
+# Egg
+K:676:0x9F:0x85
+
+# Reset Recall
+K:677:0x85:0x95
+
+# Divination
+K:678:0x85:0x95
+
+# Self
+K:679:0x9F:0x86
+
+# Ray
+K:680:0x9F:0x87
+
+# Sphere
+K:681:0x9F:0x88
+
+# Knowledge
+K:682:0x9F:0x8C
+
+# Life
+K:683:0x9F:0x8D
+
+# Fire
+K:684:0x9F:0x8E
+
+# Cold
+K:685:0x9F:0x8F
+
+# Lightning
+K:686:0x9F:0x90
+
+# Acid
+K:687:0x9F:0x91
+
+# Element
+K:688:0x9F:0x92
+
+# Chaos
+K:689:0x9F:0x93
+
+# Mind
+K:690:0x9F:0x94
+
+# Holding
+K:691:0x9F:0x95
+
+# Arrow
+K:692:0x9F:0x89
+
+# Power Surge
+K:693:0x9F:0x8A
+
+# Armageddon
+K:694:0x9F:0x8B
+
+# Gravity
+K:695:0x9F:0x96
+
+# Extra Life
+K:696:0x9F:0x97
+
+# Undeath
+K:697:0x9E:0xD3
+
+# Protection
+K:698:0x9E:0xD4
+
+# & Horn~
+K:699:0x9F:0xC5
+
+# & Ring~ of Precognition
+K:700:0x85:0xBB
+
+# & Sprig~ of Athelas
+K:701:0x9F:0xC6
+
+# [Magic for Beginners]
+K:702:0x9F:0xC7
+
+# [Conjurings and Tricks]
+K:703:0x9F:0xC7
+
+# [Incantations and Illusions]
+K:704:0x9F:0xC7
+
+# [Sorcery and Evocations]
+K:705:0x9F:0xC7
+
+# [Beginners Handbook]
+K:706:0x9F:0xC8
+
+# [Words of Wisdom]
+K:707:0x9F:0xC8
+
+# [Chants and Blessings]
+K:708:0x9F:0xC8
+
+# [Exorcism and Dispelling]
+K:709:0x9F:0xC8
+
+# [Resistance of Scarabtarices]
+K:710:0x9F:0xCA
+
+# [Mordenkainen's Escapes]
+K:711:0x9F:0xCA
+
+# [Kelek's Grimoire of Power]
+K:712:0x9F:0xCA
+
+# [Tenser's Transformations]
+K:713:0x9F:0xCA
+
+# [Raal's Tome of Destruction]
+K:714:0x9F:0xCA
+
+# [Ethereal Openings]
+K:715:0x9F:0xCA
+
+# [Godly Insights]
+K:716:0x9F:0xC9
+
+# [Purifications and Healing]
+K:717:0x9F:0xC9
+
+# [Holy Infusions]
+K:718:0x9F:0xC9
+
+# [Wrath of God]
+K:719:0x9F:0xC9
+
+# & Old Scroll~ of Deincarnation
+K:720:0x85:0x97
+
+# & Dark Sword~
+K:721:0xA5:0xB9
+
+# Numenorean for beginners (I)
+K:722:0xA3:0xF8
+
+# Numenorean for beginners (II)
+K:723:0xA3:0xF9
+
+# Advanced lessons of Numenorean
+K:724:0xA3:0xF8
+
+# Advanced lessons of Sindarin
+K:725:0xA3:0xF9
+
+# & Shard~ of Pottery
+K:726:0x8A:0xCA
+
+# & Broken Stick~
+K:727:0x8A:0xCB
+
+# Wall Creation
+K:728:0x85:0x97
+
+# [Illusions for Beginners]
+K:729:0xA3:0xFA
+
+# [Tricks and Visions]
+K:730:0xA3:0xFA
+
+# [Phantasms and Illusions]
+K:731:0xA3:0xFA
+
+# [Shadows and Prisms]
+K:732:0xA3:0xFA
+
+# [Serten's Immunities]
+K:733:0xA3:0xFB
+
+# [Knowledge of Kenault]
+K:734:0xA3:0xFB
+
+# [Otiluke's Spheres]
+K:735:0xA3:0xFA
+
+# [Boccob's Book of Shadows]
+K:736:0xA3:0xFC
+
+# [Bigby's Handbook]
+K:737:0xA3:0xFC
+
+# & Book~ of Beginner Cantrips
+K:738:0xA3:0xFD
+
+# & Book~ of Teleportation
+K:739:0xA3:0xFE
+
+# & Book~ of Recall
+K:740:0xA3:0xFF
+
+# & Book~ of Summoning
+K:741:0xA3:0xF8
+
+# & Book~ of Fireflash
+K:742:0xA3:0xF9
+
+# & Potion~ of Learning
+K:743:0xA3:0xFA
+
+# [Eye of Sauron]
+K:744:0xA3:0xFB
+
+# [Flame of Udun]
+K:745:0xA3:0xFC
+
+# [Corruptions of Melkor]
+K:746:0xA3:0xFD
+
+# [Crescent of Morgul]
+K:747:0xA3:0xFE
+
+# [Morgoth's Ring]
+K:748:0xA3:0xFF
+
+# Spell
+K:749:0x86:0xC8
+
+# Wishing
+K:750:0x86:0xC8
+
+# Khuzdul - The hidden tongue of the Dwarves
+K:751:0x85:0x95
+
+# Nandorin for dummies
+K:752:0xA3:0xF9
+
+# Advanced lessons of Orcish
+K:753:0xA3:0xFA
+
+# & Ancient Tome~
+K:754:0xA3:0xFE
+
+# Flying
+K:755:0x85:0xC0
+
+# & Tome~ of the Time
+K:756:0xA3:0xF8
+
+# & Spellbook~ of #
+K:757:0x8C:0x9C
+
+# & Tome~ of Meta Spells
+K:758:0xA3:0xF9
+
+# & Tome~ of the Mind
+K:759:0xA3:0xFA
+
+# & Holy Tome~ of Eru Iluvatar
+K:760:0xA3:0xFB
+
+# & Holy Tome~ of Manwe Sulimo
+K:761:0xA3:0xFC
+
+# & War Tome~ of Tulkas
+K:762:0xA3:0xFD
+
+# & Unholy Tome~ of the Hellflame
+K:763:0xA3:0xFE
+
+# & Corrupted Tome~ of Melkor
+K:764:0xA3:0xFF
+
+# [Aiding Shades]
+K:765:0xA3:0xF8
+
+# [Morgoth's Space-Time Warpings]
+K:766:0xA3:0xF9
+
+# [Murazor's Tome of Conjuring & Dispelling]
+K:767:0xA3:0xFA
+
+# & Forest Tome~ of Yavanna
+K:768:0xA3:0xFB
+
+# [Sauron's Forgotten Tome]
+K:769:0xA3:0xFF
+
+# & Ring~
+K:770:0x85:0xBC
+
+# [Earth]
+K:771:0xA4:0x80
+
+# [Fire]
+K:772:0xA4:0x81
+
+# [Air]
+K:773:0xA4:0x82
+
+# [Water]
+K:774:0xA4:0x83
+
+# [Mana]
+K:775:0xA4:0x84
+
+# Home Summoning
+K:776:0x85:0x97
+
+# & Shadow Blade~
+K:777:0xA4:0x89
+
+# & Bluesteel Blade~
+K:778:0xA4:0x8A
+
+# the Serpents
+K:779:0xA5:0xC0
+
+# Darkness
+K:780:0xA5:0xC1
+
+# Knowledge
+K:781:0xA5:0xC2
+
+# Force
+K:782:0xA5:0xC3
+
+# Lightning
+K:783:0xA5:0xC4
+
+# Mana
+K:784:0xA5:0xC5
+
+# Ring~ of Power
+K:785:0xA5:0xC6
+
+# Climbing Set~
+K:786:0xA4:0x8B
+
+# Adventurer's guide to Middle-earth
+K:787:0x85:0x96
+
+# & Demonblade~
+K:788:0x8B:0xE0
+
+# & Demonshield~
+K:789:0x8B:0xE1
+
+# & Demonhorn~
+K:790:0x8B:0xE2
+
+# [Demonthoughts]
+K:791:0xA3:0xFB
+
+# [Hellfire Tome]
+K:792:0xA4:0x8C
+
+# & Wooden Rod~ of#
+K:793:0xA4:0x8D
+
+# & Copper Rod~ of#
+K:794:0xA4:0x8E
+
+# & Iron Rod~ of#
+K:795:0xA4:0x8F
+
+# & Moonstone Rod~ of#
+K:796:0xA4:0x90
+
+# & Silver Rod~ of#
+K:797:0xA4:0x91
+
+# & Golden Rod~ of#
+K:798:0xA4:0x93
+
+# & Mithril Rod~ of#
+K:799:0xA4:0x94
+
+# & Adamantite Rod~ of#
+K:800:0xA4:0x95
+
+# & Greater Ration~ of Health
+K:801:0xA5:0xBF
+
+# & Crumpled Scroll~ of Mass Resurrection
+K:802:0x85:0x96
+
+# & Cleaver~
+K:803:0xA5:0xBA
+
+# & Light War Axe~
+K:804:0xA5:0xBB
+
+# & Slaughter Axe~
+K:805:0xA5:0xBC
+
+# & Runestone~
+K:806:0xA5:0xBD
+
+# & Fortune cookie~
+K:807:0xA6:0xBE
+
+# Portable hole
+K:808:0xA6:0xC1
+
+# Critical Hits
+K:809:0xA6:0xD4
+
+# & Wand~ of Digging of Thrain
+K:810:0xA6:0xD4
+
+# & Gnarled Staff~ of Holy Fire of Mithrandir
+K:811:0xA6:0xD4
+
+# Partial Totem
+K:812:0xA6:0xD5
+
+# True Totem
+K:813:0xA6:0xD6
+
+# & piece~ of a Relic of Eru
+K:814:0x89:0xDA
+
+# & piece~ of a Relic of Manwe
+K:815:0x89:0xDB
+
+# & piece~ of a Relic of Tulkas
+K:816:0x89:0xDC
+
+# & piece~ of a Relic of Melkor
+K:817:0x89:0xDD
+
+# & piece~ of a Relic of Yavanna
+K:818:0x89:0xDE
+
+# Player
+R:0:0x80:0xC0
+
+# Filthy street urchin
+R:1:0x98:0xB8
+
+# Scrawny cat
+R:2:0x96:0xFA
+
+# Sparrow
+R:3:0x9D:0xD6
+
+# Chaffinch
+R:4:0x9D:0xD6
+
+# Wild rabbit
+R:5:0x9D:0xD7
+
+# Woodsman
+R:6:0x98:0xC9
+
+# Scruffy little dog
+R:7:0x92:0x92
+
+# Farmer Maggot
+R:8:0x98:0xB9
+
+# Blubbering idiot
+R:9:0x98:0xBA
+
+# Boil-covered wretch
+R:10:0x98:0xBB
+
+# Village idiot
+R:11:0x98:0xBC
+
+# Pitiful-looking beggar
+R:12:0x98:0xBD
+
+# Mangy-looking leper
+R:13:0x98:0xBE
+
+# Agent of the black market
+R:14:0x98:0xBF
+
+# Singing, happy drunk
+R:15:0x98:0xC0
+
+# Aimless-looking merchant
+R:16:0x98:0xC1
+
+# Mean-looking mercenary
+R:17:0x98:0xC2
+
+# Battle-scarred veteran
+R:18:0x98:0xC3
+
+# Martti Ihrasaari
+R:19:0x9B:0xB8
+
+# Grey mold
+R:20:0x97:0xD7
+
+# Large white snake
+R:21:0x94:0xBD
+
+# Grey mushroom patch
+R:22:0x9B:0xB9
+
+# Newt
+R:23:0x9B:0xBA
+
+# Giant white centipede
+R:24:0x96:0x8D
+
+# White icky thing
+R:25:0x97:0xBB
+
+# Clear icky thing
+R:26:0x97:0xBC
+
+# Giant white mouse
+R:27:0x99:0xBD
+
+# Large brown snake
+R:28:0x94:0xBC
+
+# Small kobold
+R:29:0x97:0xD1
+
+# Kobold
+R:30:0x97:0xD2
+
+# White worm mass
+R:31:0x99:0xD5
+
+# Floating eye
+R:32:0x96:0xD3
+
+# Rock lizard
+R:33:0x94:0xBE
+
+# Grid bug
+R:34:0x9B:0xBC
+
+# Jackal
+R:35:0x92:0x93
+
+# Soldier ant
+R:36:0x95:0xFF
+
+# Fruit bat
+R:37:0x96:0x87
+
+# Insect swarm
+R:38:0x9E:0x96
+
+# The Greater hell-beast
+R:39:0x9B:0xBB
+
+# Shrieker mushroom patch
+R:40:0x91:0xFE
+
+# Blubbering icky thing
+R:41:0x97:0xBD
+
+# Metallic green centipede
+R:42:0x96:0x8E
+
+# Novice warrior
+R:43:0x98:0xC4
+
+# Novice rogue
+R:44:0x98:0xC5
+
+# Novice priest
+R:45:0x98:0xC6
+
+# Novice mage
+R:46:0x98:0xC7
+
+# Yellow mushroom patch
+R:47:0x91:0xFF
+
+# White jelly
+R:48:0x97:0xC2
+
+# Giant black ant
+R:49:0x96:0x80
+
+# Salamander
+R:50:0x94:0xC0
+
+# White harpy
+R:51:0x93:0xC0
+
+# Blue yeek
+R:52:0x99:0xFF
+
+# Grip, Farmer Maggot's dog
+R:53:0x92:0x94
+
+# Wolf, Farmer Maggot's dog
+R:54:0x92:0x95
+
+# Fang, Farmer Maggot's dog
+R:55:0x92:0x95
+
+# Giant green frog
+R:56:0x94:0xBF
+
+# Freesia
+R:57:0x9B:0xBD
+
+# Green worm mass
+R:58:0x99:0xD6
+
+# Large yellow snake
+R:59:0x94:0xC1
+
+# Cave spider
+R:60:0x94:0xD5
+
+# Crow
+R:61:0x9E:0x97
+
+# Wild cat
+R:62:0x96:0xFB
+
+# Smeagol
+R:63:0x98:0xC8
+
+# Green ooze
+R:64:0x97:0xC3
+
+# Poltergeist
+R:65:0x93:0x91
+
+# Yellow jelly
+R:66:0x97:0xC5
+
+# Metallic blue centipede
+R:67:0x96:0x8F
+
+# Raven
+R:68:0x9E:0x97
+
+# Giant white louse
+R:69:0x97:0xD5
+
+# Giant yellow centipede
+R:70:0x96:0x8C
+
+# Black naga
+R:71:0x98:0x80
+
+# Spotted mushroom patch
+R:72:0x92:0x80
+
+# Silver jelly
+R:73:0x97:0xC4
+
+# Scruffy-looking hobbit
+R:74:0x97:0x8B
+
+# Giant white ant
+R:75:0x96:0x81
+
+# Yellow mold
+R:76:0x97:0xF8
+
+# Metallic red centipede
+R:77:0x96:0x90
+
+# Yellow worm mass
+R:78:0x99:0xD7
+
+# Clear worm mass
+R:79:0x99:0xF8
+
+# Radiation eye
+R:80:0x96:0xD4
+
+# Yellow light
+R:81:0x9F:0xCB
+
+# Cave lizard
+R:82:0x94:0xC2
+
+# Novice ranger
+R:83:0x98:0xC9
+
+# Blue jelly
+R:84:0x97:0xC6
+
+# Creeping copper coins
+R:85:0x91:0xF8
+
+# Giant white rat
+R:86:0x99:0xBE
+
+# Snotling
+R:87:0xA0:0x81
+
+# Swordfish
+R:88:0x9E:0xD6
+
+# Blue worm mass
+R:89:0x99:0xF9
+
+# Large grey snake
+R:90:0x94:0xC3
+
+# Skeleton kobold
+R:91:0x99:0xC1
+
+# Ewok
+R:92:0x9B:0xBE
+
+# Novice mage
+R:93:0x98:0xC7
+
+# Green naga
+R:94:0x98:0x81
+
+# Giant leech
+R:95:0x9F:0xCC
+
+# Barracuda
+R:96:0x9E:0xD7
+
+# Novice paladin
+R:97:0x98:0xCA
+
+# Zog
+R:98:0x93:0xF8
+
+# Blue ooze
+R:99:0x97:0xC7
+
+# Green glutton ghost
+R:100:0x93:0x92
+
+# Green jelly
+R:101:0x97:0xC8
+
+# Large kobold
+R:102:0x97:0xD3
+
+# Grey icky thing
+R:103:0x97:0xBE
+
+# Disenchanter eye
+R:104:0x96:0xD5
+
+# Red worm mass
+R:105:0x99:0xFA
+
+# Copperhead snake
+R:106:0x94:0xC4
+
+# Death sword
+R:107:0x9B:0xBF
+
+# Purple mushroom patch
+R:108:0x92:0x81
+
+# Novice priest
+R:109:0x98:0xC6
+
+# Novice warrior
+R:110:0x98:0xC4
+
+# Nibelung
+R:111:0x9B:0xC0
+
+# The disembodied hand that strangled people
+R:112:0x9B:0xC1
+
+# Brown mold
+R:113:0x97:0xF9
+
+# Giant brown bat
+R:114:0x96:0x88
+
+# Rat-thing
+R:115:0x99:0xC0
+
+# Novice rogue
+R:116:0x98:0xBF
+
+# Creeping silver coins
+R:117:0x91:0xF9
+
+# Snaga
+R:118:0x98:0x86
+
+# Rattlesnake
+R:119:0x94:0xC5
+
+# Giant slug
+R:120:0x9F:0xCC
+
+# Giant pink frog
+R:121:0x9F:0xCD
+
+# Dark elf
+R:122:0x8C:0xCC
+
+# Zombified kobold
+R:123:0x99:0xC1
+
+# Crypt creep
+R:124:0x9B:0xC2
+
+# Rotting corpse
+R:125:0x9B:0xC3
+
+# Cave orc
+R:126:0x98:0x87
+
+# Wood spider
+R:127:0x94:0xD6
+
+# Manes
+R:128:0x93:0xC9
+
+# Bloodshot eye
+R:129:0x96:0xD6
+
+# Red naga
+R:130:0x98:0x82
+
+# Red jelly
+R:131:0x97:0xC9
+
+# Green icky thing
+R:132:0x97:0xBF
+
+# Lost soul
+R:133:0x93:0x93
+
+# Night lizard
+R:134:0x94:0xC7
+
+# Mughash, the Kobold Lord
+R:135:0x97:0xD4
+
+# Skeleton orc
+R:136:0x99:0xC2
+
+# Wormtongue, Agent of Saruman
+R:137:0x98:0xD0
+
+# Robin Hood, the Outlaw
+R:138:0x9B:0xC4
+
+# Nurgling
+R:139:0x9F:0xCF
+
+# Lagduf, the Snaga
+R:140:0x98:0x88
+
+# Brown yeek
+R:141:0x9A:0x80
+
+# Novice ranger
+R:142:0x98:0xC9
+
+# Giant salamander
+R:143:0x94:0xC8
+
+# Space monster
+R:144:0x9B:0xC5
+
+# Carnivorous flying monkey
+R:145:0x9F:0xD0
+
+# Green mold
+R:146:0x97:0xFA
+
+# Novice paladin
+R:147:0x98:0xCA
+
+# Lemure
+R:148:0x93:0xCA
+
+# Hill orc
+R:149:0x98:0x89
+
+# Bandit
+R:150:0x98:0xD3
+
+# Hunting hawk
+R:151:0x9B:0xC6
+
+# Phantom warrior
+R:152:0x9B:0xC7
+
+# Gremlin
+R:153:0x9B:0xC8
+
+# Yeti
+R:154:0x95:0xC9
+
+# Bloodshot icky thing
+R:155:0x97:0xC0
+
+# Giant grey rat
+R:156:0x99:0xBF
+
+# Black harpy
+R:157:0x93:0xC1
+
+# Skaven
+R:158:0x9F:0xD1
+
+# The wounded bear
+R:159:0xA0:0xB8
+
+# Cave bear
+R:160:0xA5:0xC7
+
+# Rock mole
+R:161:0xA0:0xBA
+
+# Mindcrafter
+R:162:0x98:0xCB
+
+# Baby blue dragon
+R:163:0x96:0x95
+
+# Baby white dragon
+R:164:0x96:0x96
+
+# Baby green dragon
+R:165:0x96:0x97
+
+# Baby black dragon
+R:166:0x96:0xB8
+
+# Baby red dragon
+R:167:0x96:0xB9
+
+# Giant red ant
+R:168:0x96:0x85
+
+# Brodda, the Easterling
+R:169:0x98:0xD4
+
+# Bloodfang, the Wolf
+R:170:0xA0:0xBB
+
+# King cobra
+R:171:0x94:0xC9
+
+# Eagle
+R:172:0x9D:0xD6
+
+# War bear
+R:173:0x9B:0xC9
+
+# Killer bee
+R:174:0x9B:0xCA
+
+# Giant spider
+R:175:0x94:0xD7
+
+# Giant white tick
+R:176:0x97:0xD5
+
+# The Borshin
+R:177:0xA0:0xBC
+
+# Dark elven mage
+R:178:0x97:0x8E
+
+# Kamikaze yeek
+R:179:0xA0:0xCC
+
+# Orfax, Son of Boldor
+R:180:0x9A:0x81
+
+# Servant of Glaaki
+R:181:0xA0:0xBD
+
+# Dark elven warrior
+R:182:0x97:0x8F
+
+# Sand-dweller
+R:183:0xA0:0xBE
+
+# Clear mushroom patch
+R:184:0x9F:0xCE
+
+# Quiver slot
+R:185:0x9B:0xCB
+
+# Grishnakh, the Hill Orc
+R:186:0x98:0x8B
+
+# Giant tan bat
+R:187:0xA5:0xC8
+
+# Owlbear
+R:188:0xA0:0xBF
+
+# Blue horror
+R:189:0x9F:0xD2
+
+# Hairy mold
+R:190:0x97:0xFB
+
+# Grizzly bear
+R:191:0xA0:0xC0
+
+# Disenchanter mold
+R:192:0x97:0xFC
+
+# Pseudo dragon
+R:193:0x96:0xBA
+
+# Tengu
+R:194:0x93:0xCB
+
+# Creeping gold coins
+R:195:0x91:0xFA
+
+# Wolf
+R:196:0x92:0x96
+
+# Giant fruit fly
+R:197:0x93:0x89
+
+# Panther
+R:198:0x96:0xFC
+
+# Brigand
+R:199:0x9B:0xCC
+
+# Hobbes the Tiger
+R:200:0x9B:0xCD
+
+# Shadow Creature of Fiona
+R:201:0x9B:0xCE
+
+# Undead mass
+R:202:0x9B:0xCF
+
+# Chaos shapechanger
+R:203:0x9B:0xD0
+
+# Baby multi-hued dragon
+R:204:0x96:0xBB
+
+# Vorpal bunny
+R:205:0x9D:0xD7
+
+# Old Man Willow
+R:206:0xA0:0xC1
+
+# Hippocampus
+R:207:0xA0:0xC2
+
+# Zombified orc
+R:208:0x99:0xC4
+
+# Hippogriff
+R:209:0x93:0xC2
+
+# Black mamba
+R:210:0x94:0xCA
+
+# White wolf
+R:211:0x92:0x97
+
+# Grape jelly
+R:212:0x97:0xCA
+
+# Nether worm mass
+R:213:0x99:0xFB
+
+# Abyss worm mass
+R:214:0x9B:0xD1
+
+# Golfimbul, the Hill Orc Chief
+R:215:0x98:0x8C
+
+# Swordsman
+R:216:0x8E:0xF9
+
+# Skaven shaman
+R:217:0x90:0xBC
+
+# Baby bronze dragon
+R:218:0x96:0xBA
+
+# Baby gold dragon
+R:219:0x96:0xBA
+
+# Evil eye
+R:220:0xA5:0xC9
+
+# Mine-dog
+R:221:0xA0:0x83
+
+# Hellcat
+R:222:0x9B:0xD2
+
+# Moon beast
+R:223:0x9B:0xD3
+
+# Master yeek
+R:224:0x9A:0x82
+
+# Priest
+R:225:0x98:0xD6
+
+# Dark elven priest
+R:226:0x97:0x91
+
+# Air spirit
+R:227:0x92:0xD7
+
+# Skeleton human
+R:228:0x99:0xC3
+
+# Zombified human
+R:229:0x9A:0x86
+
+# Tiger
+R:230:0x96:0xFD
+
+# Moaning spirit
+R:231:0x93:0x94
+
+# Stegocentipede
+R:232:0x96:0x91
+
+# Spotted jelly
+R:233:0x97:0xCB
+
+# Drider
+R:234:0x94:0xF8
+
+# Mongbat
+R:235:0x9B:0xD4
+
+# Killer brown beetle
+R:236:0x93:0xD3
+
+# Boldor, King of the Yeeks
+R:237:0x9A:0x83
+
+# Ogre
+R:238:0x94:0x83
+
+# Creeping mithril coins
+R:239:0x91:0xFB
+
+# Illusionist
+R:240:0x98:0xF8
+
+# Druid
+R:241:0x98:0xF9
+
+# Pink horror
+R:242:0x9F:0xD3
+
+# Cloaker
+R:243:0x88:0x80
+
+# Black orc
+R:244:0x98:0x8D
+
+# Ochre jelly
+R:245:0x97:0xCC
+
+# Software bug
+R:246:0x9B:0xD5
+
+# Lurker
+R:247:0x83:0xB9
+
+# Tangleweed
+R:248:0xA5:0xCA
+
+# Vlasta
+R:249:0x95:0xFC
+
+# Giant white dragon fly
+R:250:0x93:0x8B
+
+# Snaga sapper
+R:251:0xA0:0x84
+
+# Blue icky thing
+R:252:0x97:0xC1
+
+# Gibbering mouther
+R:253:0x9B:0xD6
+
+# Wolfhound of Flora
+R:254:0x9B:0xD7
+
+# Hill giant
+R:255:0x94:0x89
+
+# Flesh golem
+R:256:0x97:0x81
+
+# Warg
+R:257:0x92:0xB8
+
+# Cheerful leprechaun
+R:258:0x9B:0xF8
+
+# Giant flea
+R:259:0x93:0x8A
+
+# Ufthak of Cirith Ungol
+R:260:0xA0:0xC4
+
+# Clay golem
+R:261:0x9F:0xD5
+
+# Black ogre
+R:262:0x94:0x84
+
+# Dweller on the threshold
+R:263:0xA0:0x85
+
+# Half-orc
+R:264:0xA0:0xC5
+
+# Dark naga
+R:265:0x9F:0xD6
+
+# Poison ivy
+R:266:0xA5:0xCB
+
+# Magic mushroom patch
+R:267:0x92:0x83
+
+# Plaguebearer of Nurgle
+R:268:0x9A:0x85
+
+# Guardian naga
+R:269:0x98:0x83
+
+# Wererat
+R:270:0xA0:0xC6
+
+# Light hound
+R:271:0x95:0xCB
+
+# Dark hound
+R:272:0x95:0xCC
+
+# Flying skull
+R:273:0x9B:0xF9
+
+# Mi-Go
+R:274:0x9B:0xFA
+
+# Giant tarantula
+R:275:0x94:0xF9
+
+# Giant clear centipede
+R:276:0x96:0x92
+
+# Mirkwood spider
+R:277:0x94:0xFA
+
+# Frost giant
+R:278:0x94:0x8A
+
+# Griffon
+R:279:0x93:0xC3
+
+# Homunculus
+R:280:0x93:0xCC
+
+# Gnome mage
+R:281:0x97:0x90
+
+# Clear hound
+R:282:0x95:0xCD
+
+# Umber hulk
+R:283:0x95:0x91
+
+# Rust monster
+R:284:0xA0:0x87
+
+# Ogrillon
+R:285:0x98:0x90
+
+# Gelatinous cube
+R:286:0x97:0xCD
+
+# Giant green dragon fly
+R:287:0x93:0x8C
+
+# Fire giant
+R:288:0x94:0x8B
+
+# Hummerhorn
+R:289:0xA0:0xC7
+
+# Lizard man
+R:290:0xA0:0x88
+
+# Ulfast, Son of Ulfang
+R:291:0x98:0xFA
+
+# Crebain
+R:292:0xA5:0xCC
+
+# Berserker
+R:293:0x98:0x8F
+
+# Quasit
+R:294:0x93:0xCD
+
+# Sphinx
+R:295:0xA0:0x89
+
+# Imp
+R:296:0x93:0xCE
+
+# Forest troll
+R:297:0x95:0x81
+
+# Freezing sphere
+R:298:0xA0:0xC9
+
+# Jumping fireball
+R:299:0xA0:0x8A
+
+# Ball lightning
+R:300:0xA0:0xCA
+
+# 2-headed hydra
+R:301:0x94:0xCB
+
+# Swamp thing
+R:302:0xA0:0x8B
+
+# Water spirit
+R:303:0x92:0xF8
+
+# Giant red scorpion
+R:304:0x94:0xFB
+
+# Earth spirit
+R:305:0x92:0xF9
+
+# Fire spirit
+R:306:0x92:0xFA
+
+# Fire hound
+R:307:0x95:0xCE
+
+# Cold hound
+R:308:0x95:0xCF
+
+# Energy hound
+R:309:0x95:0xD0
+
+# Lesser Mimic
+R:310:0x92:0x86
+
+# Door mimic
+R:311:0x84:0xBB
+
+# Blink dog
+R:312:0x92:0xB9
+
+# Uruk
+R:313:0x98:0x91
+
+# Shagrat, the Orc Captain
+R:314:0x98:0x92
+
+# Gorbag, the Orc Captain
+R:315:0x98:0x93
+
+# Shambling mound
+R:316:0x92:0x84
+
+# Giant Venus Flytrap
+R:317:0xA5:0xCD
+
+# Chaos beastman
+R:318:0xA0:0x8D
+
+# Daemonette of Slaanesh
+R:319:0xA0:0x8C
+
+# Giant bronze dragon fly
+R:320:0x93:0x90
+
+# Stone giant
+R:321:0x94:0x8C
+
+# Giant black dragon fly
+R:322:0x93:0x8E
+
+# Stone golem
+R:323:0x97:0x83
+
+# Red mold
+R:324:0x97:0xFD
+
+# Giant gold dragon fly
+R:325:0x93:0x8F
+
+# Stunwall
+R:326:0x83:0xCB
+
+# Ghast
+R:327:0xA0:0xCD
+
+# Neekerbreeker
+R:328:0xA5:0xCE
+
+# Huorn
+R:329:0xA0:0xCE
+
+# Bolg, Son of Azog
+R:330:0x98:0x94
+
+# Phase spider
+R:331:0x94:0xFC
+
+# Lizard king
+R:332:0xA0:0x8F
+
+# Landmine
+R:333:0xA0:0xCF
+
+# Wyvern
+R:334:0x9B:0xFB
+
+# Great eagle
+R:335:0xA0:0x90
+
+# Livingstone
+R:336:0x9B:0xFC
+
+# Earth hound
+R:337:0x95:0xD1
+
+# Air hound
+R:338:0x95:0xD2
+
+# Sabre-tooth tiger
+R:339:0x96:0xFE
+
+# Acid hound
+R:340:0x95:0xD3
+
+# Chimaera
+R:341:0x93:0xC4
+
+# Quylthulg
+R:342:0x94:0x92
+
+# Sasquatch
+R:343:0x95:0xCA
+
+# Weir
+R:344:0x9B:0xFD
+
+# Ranger
+R:345:0x98:0xCF
+
+# Paladin
+R:346:0x99:0x8A
+
+# Werewolf
+R:347:0xA0:0xD1
+
+# Dark elven lord
+R:348:0x97:0x94
+
+# Cloud giant
+R:349:0x94:0x8E
+
+# Ugluk, the Uruk
+R:350:0x98:0x95
+
+# Blue dragon bat
+R:351:0x96:0x89
+
+# Mimic
+R:352:0x85:0x95
+
+# Ultimate Mimic
+R:353:0x85:0xD6
+
+# Fire vortex
+R:354:0x99:0xCC
+
+# Acid vortex
+R:355:0x99:0xCD
+
+# Lugdush, the Uruk
+R:356:0xA0:0x92
+
+# Arch-vile
+R:357:0xA0:0xD2
+
+# Cold vortex
+R:358:0x99:0xCE
+
+# Energy vortex
+R:359:0x99:0xCF
+
+# Globefish
+R:360:0xA0:0x93
+
+# Giant firefly
+R:361:0x93:0x8D
+
+# Mummified orc
+R:362:0x94:0x80
+
+# Wolf chieftain
+R:363:0xA5:0xCF
+
+# Serpent man
+R:364:0xA0:0xD4
+
+# Vampiric mist
+R:365:0xA0:0x95
+
+# Killer stag beetle
+R:366:0x93:0xD5
+
+# Iron golem
+R:367:0x97:0x84
+
+# Auto-roller
+R:368:0x9B:0xFE
+
+# Giant yellow scorpion
+R:369:0x94:0xFD
+
+# Jade monk
+R:370:0xA0:0xD5
+
+# Black ooze
+R:371:0x97:0xCE
+
+# Hardened warrior
+R:372:0x98:0xFB
+
+# Azog, King of the Uruk-Hai
+R:373:0x98:0x97
+
+# Fleshhound of Khorne
+R:374:0xA0:0x96
+
+# Dark elven warlock
+R:375:0x9B:0xFF
+
+# Master rogue
+R:376:0x98:0xFC
+
+# Red dragon bat
+R:377:0x96:0x8A
+
+# Killer white beetle
+R:378:0xA0:0xD6
+
+# Ice skeleton
+R:379:0xA0:0x97
+
+# Angamaite of Umbar
+R:380:0xA0:0xF8
+
+# Forest wight
+R:381:0x9C:0x80
+
+# Khim, Son of Mim
+R:382:0x9C:0x81
+
+# Ibun, Son of Mim
+R:383:0x9C:0x82
+
+# Meneldor the Swift
+R:384:0xA0:0xF9
+
+# Phantom beast
+R:385:0x9C:0x83
+
+# Giant silver ant
+R:386:0x93:0xD4
+
+# 4-headed hydra
+R:387:0x94:0xCD
+
+# Lesser hell-beast
+R:388:0xA0:0xFB
+
+# Tyrannosaur
+R:389:0x9C:0x84
+
+# Mummified human
+R:390:0x94:0x81
+
+# Vampire bat
+R:391:0x96:0x8B
+
+# Sangahyando of Umbar
+R:392:0x98:0xFD
+
+# It
+R:393:0x9C:0x85
+
+# Banshee
+R:394:0x93:0x95
+
+# Carrion crawler
+R:395:0x96:0x93
+
+# Xiclotlan
+R:396:0xA0:0xFC
+
+# Silent watcher
+R:397:0x9C:0x86
+
+# Pukelman
+R:398:0x97:0x85
+
+# Disenchanter beast
+R:399:0xA0:0xD7
+
+# Dark elven druid
+R:400:0x97:0x97
+
+# Stone troll
+R:401:0x95:0x82
+
+# Black
+R:402:0x9B:0xC5
+
+# Hill troll
+R:403:0x95:0x83
+
+# Wereworm
+R:404:0x99:0xFC
+
+# Killer red beetle
+R:405:0x93:0xD7
+
+# Disenchanter bat
+R:406:0xA5:0xD0
+
+# Gnoph-Keh
+R:407:0xA0:0xFE
+
+# Giant grey ant
+R:408:0x96:0x84
+
+# Khufu, the Mummified King
+R:409:0x9C:0x87
+
+# Gwaihir the Windlord
+R:410:0xA0:0xF9
+
+# Giant fire tick
+R:411:0xA0:0xFF
+
+# Displacer beast
+R:412:0x96:0xFF
+
+# Ulwarth, Son of Ulfang
+R:413:0x98:0xD1
+
+# Werebear
+R:414:0xA5:0xC7
+
+# Cave ogre
+R:415:0x94:0x85
+
+# White wraith
+R:416:0x95:0x97
+
+# Angel
+R:417:0x92:0x87
+
+# Ghoul
+R:418:0x9D:0xC7
+
+# Mim, Betrayer of Turin
+R:419:0x9C:0x88
+
+# Hellblade
+R:420:0x9C:0x89
+
+# Killer fire beetle
+R:421:0x93:0xF8
+
+# Beast of Nurgle
+R:422:0xA1:0x80
+
+# Creeping adamantite coins
+R:423:0x91:0xFC
+
+# Algroth
+R:424:0x95:0x84
+
+# Flamer of Tzeentch
+R:425:0x9F:0xD7
+
+# Roper
+R:426:0x9F:0xF8
+
+# Headless
+R:427:0x9C:0x8A
+
+# Vibration hound
+R:428:0x95:0xD4
+
+# Nexus hound
+R:429:0x95:0xD5
+
+# Half-ogre
+R:430:0x94:0x86
+
+# Lokkak, the Ogre Chieftain
+R:431:0x94:0x88
+
+# Vampire
+R:432:0x95:0x92
+
+# Gorgimaera
+R:433:0x93:0xC5
+
+# Shantak
+R:434:0x9C:0x8B
+
+# Colbran
+R:435:0x97:0x86
+
+# Spirit naga
+R:436:0x98:0x84
+
+# Corpser
+R:437:0x9F:0xF9
+
+# Fiend of Slaanesh
+R:438:0x94:0xCF
+
+# Stairway to Hell
+R:439:0x9C:0x8C
+
+# 5-headed hydra
+R:440:0x94:0xCE
+
+# Barney the Dinosaur
+R:441:0x9C:0x8D
+
+# Black knight
+R:442:0x99:0x80
+
+# Seahorse
+R:443:0xA1:0x81
+
+# Cyclops
+R:444:0xA1:0x82
+
+# Clairvoyant
+R:445:0x98:0xFE
+
+# Purple worm
+R:446:0x9F:0xFA
+
+# Catoblepas
+R:447:0x99:0xB9
+
+# Lesser wall monster
+R:448:0x9C:0x8E
+
+# Mage
+R:449:0x99:0x82
+
+# Mind flayer
+R:450:0x99:0x83
+
+# The Ultimate Dungeon Cleaner
+R:451:0x9C:0x8F
+
+# Deep one
+R:452:0x95:0xBE
+
+# Basilisk
+R:453:0x94:0xCF
+
+# Ice troll
+R:454:0x95:0x85
+
+# Dhole
+R:455:0x9C:0x91
+
+# Archangel
+R:456:0x92:0x88
+
+# Greater Mimic
+R:457:0x9A:0x96
+
+# Chaos tile
+R:458:0x9C:0x92
+
+# Young blue dragon
+R:459:0x96:0xBC
+
+# Young white dragon
+R:460:0x96:0xBD
+
+# Young green dragon
+R:461:0x96:0xBE
+
+# Young bronze dragon
+R:462:0x96:0xBF
+
+# Aklash
+R:463:0xA4:0x96
+
+# Mithril golem
+R:464:0x97:0x87
+
+# Skeleton troll
+R:465:0x99:0xC5
+
+# Skeletal tyrannosaur
+R:466:0xA1:0x83
+
+# Beorn, the Shape-Changer
+R:467:0xA5:0xD1
+
+# Thorondor, Lord of Eagles
+R:468:0xA0:0xF9
+
+# Giant blue ant
+R:469:0x96:0x83
+
+# Grave wight
+R:470:0x9A:0x94
+
+# Shadow drake
+R:471:0x96:0xC0
+
+# Manticore
+R:472:0x93:0xC6
+
+# Giant army ant
+R:473:0x9A:0xB9
+
+# Killer slicer beetle
+R:474:0x93:0xF9
+
+# Gorgon
+R:475:0xA1:0x85
+
+# Gug
+R:476:0xA1:0x86
+
+# Ghost
+R:477:0x93:0x96
+
+# Death watch beetle
+R:478:0x93:0xFA
+
+# Mountain ogre
+R:479:0x94:0x87
+
+# Nexus quylthulg
+R:480:0x94:0x93
+
+# Shelob, Spider of Darkness
+R:481:0x94:0xFE
+
+# Giant squid
+R:482:0x9F:0xFB
+
+# Ghoulking
+R:483:0x9A:0x84
+
+# Doombat
+R:484:0x9F:0xFC
+
+# Ninja
+R:485:0x99:0x84
+
+# Memory moss
+R:486:0x97:0xFE
+
+# Storm giant
+R:487:0x94:0x8D
+
+# Spectator
+R:488:0x9C:0x93
+
+# Bokrug
+R:489:0xA1:0x87
+
+# Biclops
+R:490:0xA1:0x88
+
+# Half-troll
+R:491:0x98:0x96
+
+# Ivory monk
+R:492:0x98:0xCB
+
+# Bert the Stone Troll
+R:493:0x95:0x88
+
+# Bill the Stone Troll
+R:494:0x95:0x89
+
+# Tom the Stone Troll
+R:495:0x95:0x8A
+
+# Cave troll
+R:496:0x95:0x86
+
+# Anti-paladin
+R:497:0x9C:0x94
+
+# Chaos master
+R:498:0x9C:0x95
+
+# Barrow wight
+R:499:0x95:0xB9
+
+# Skeleton ettin
+R:500:0xA5:0xD2
+
+# Chaos drake
+R:501:0x96:0xC1
+
+# Law drake
+R:502:0x96:0xC2
+
+# Balance drake
+R:503:0x96:0xC3
+
+# Ethereal drake
+R:504:0x96:0xC4
+
+# Groo, the Wanderer
+R:505:0x9C:0x96
+
+# Fasolt the Giant
+R:506:0x9C:0x97
+
+# Shade
+R:507:0x95:0xC1
+
+# Spectre
+R:508:0x93:0xB8
+
+# Water troll
+R:509:0x95:0x8B
+
+# Fire elemental
+R:510:0x92:0xFB
+
+# Cherub
+R:511:0x92:0x89
+
+# Water elemental
+R:512:0x92:0xFC
+
+# Multi-hued hound
+R:513:0x9C:0xB9
+
+# Invisible stalker
+R:514:0x92:0xFD
+
+# Carrion crawler
+R:515:0x96:0x94
+
+# Master thief
+R:516:0x99:0x86
+
+# The Watcher in the Water
+R:517:0x9B:0x8D
+
+# Lich
+R:518:0x93:0xFB
+
+# Gas spore
+R:519:0x9C:0xC6
+
+# Master vampire
+R:520:0x95:0x93
+
+# Oriental vampire
+R:521:0x9C:0xBB
+
+# Greater mummy
+R:522:0x94:0x82
+
+# Bloodletter of Khorne
+R:523:0x93:0xD0
+
+# Giant grey scorpion
+R:524:0x94:0xFF
+
+# Earth elemental
+R:525:0x92:0xFE
+
+# Air elemental
+R:526:0x92:0xFF
+
+# Shimmering mold
+R:527:0x9A:0xF9
+
+# Gargoyle
+R:528:0xA1:0x89
+
+# Malicious leprechaun
+R:529:0x9C:0xBD
+
+# Eog golem
+R:530:0x97:0x88
+
+# Little Boy
+R:531:0x9F:0xFD
+
+# Dagashi
+R:532:0x99:0x88
+
+# Headless ghost
+R:533:0xA1:0x8A
+
+# Dread
+R:534:0x9F:0xFF
+
+# Leng spider
+R:535:0xA1:0x8B
+
+# Gauth
+R:536:0xA5:0xD3
+
+# Smoke elemental
+R:537:0x93:0x88
+
+# Olog
+R:538:0x95:0x8C
+
+# Halfling slinger
+R:539:0x9C:0xBE
+
+# Gravity hound
+R:540:0x95:0xD6
+
+# Acidic cytoplasm
+R:541:0x97:0xCF
+
+# Inertia hound
+R:542:0x95:0xD7
+
+# Impact hound
+R:543:0x95:0xF8
+
+# Shardstorm
+R:544:0xA5:0xD4
+
+# Ooze elemental
+R:545:0x93:0x80
+
+# Young black dragon
+R:546:0x96:0xC5
+
+# Mumak
+R:547:0x99:0xBC
+
+# Giant fire ant
+R:548:0x96:0x82
+
+# Mature white dragon
+R:549:0x96:0xC6
+
+# Xorn
+R:550:0x95:0xC7
+
+# Rogrog the Black Troll
+R:551:0x95:0x87
+
+# Mist giant
+R:552:0x97:0x89
+
+# Phantom
+R:553:0x9C:0xBF
+
+# Grey wraith
+R:554:0x95:0xBA
+
+# Revenant
+R:555:0x95:0xC0
+
+# Young multi-hued dragon
+R:556:0x96:0xC7
+
+# Raal's Tome of Destruction
+R:557:0x9C:0xC0
+
+# Colossus
+R:558:0x9C:0xC1
+
+# Young gold dragon
+R:559:0x96:0xC8
+
+# Mature blue dragon
+R:560:0x96:0xC9
+
+# Mature green dragon
+R:561:0x96:0xCA
+
+# Mature bronze dragon
+R:562:0x96:0xCB
+
+# Young red dragon
+R:563:0x96:0xCC
+
+# Nightblade
+R:564:0x9C:0xC2
+
+# Trapper
+R:565:0x9A:0x97
+
+# Bodak
+R:566:0x93:0xD0
+
+# Time bomb
+R:567:0xA1:0x8E
+
+# Mezzodaemon
+R:568:0x9A:0x88
+
+# Elder thing
+R:569:0x9C:0xC3
+
+# Ice elemental
+R:570:0x93:0x82
+
+# Necromancer
+R:571:0x9C:0xC4
+
+# The Greater hell magic mushroom were-quylthulg
+R:572:0x9F:0xFE
+
+# Lorgan, Chief of the Easterlings
+R:573:0x9C:0xC5
+
+# Chaos spawn
+R:574:0x9C:0xC6
+
+# Mummified troll
+R:575:0xA1:0x8F
+
+# Storm of Unmagic
+R:576:0xA5:0xD5
+
+# Crypt thing
+R:577:0x95:0xB8
+
+# Chaos butterfly
+R:578:0x80:0x80
+
+# Time elemental
+R:579:0x9C:0xC7
+
+# Flying polyp
+R:580:0xA1:0x91
+
+# The Queen Ant
+R:581:0x96:0x86
+
+# Will o' the wisp
+R:582:0x93:0x83
+
+# Shan
+R:583:0xA1:0x92
+
+# Magma elemental
+R:584:0x93:0x84
+
+# Black pudding
+R:585:0x97:0xD0
+
+# Killer iridescent beetle
+R:586:0x9D:0xC8
+
+# Nexus vortex
+R:587:0x9A:0xB8
+
+# Plasma vortex
+R:588:0x99:0xD0
+
+# Mature red dragon
+R:589:0x96:0xCD
+
+# Mature gold dragon
+R:590:0x96:0xCE
+
+# Crystal drake
+R:591:0x96:0xCF
+
+# Mature black dragon
+R:592:0x96:0xD0
+
+# Mature multi-hued dragon
+R:593:0x96:0xD1
+
+# Sky whale
+R:594:0xA1:0x93
+
+# Draebor, the Imp
+R:595:0xA4:0x97
+
+# Mother Hydra
+R:596:0x94:0xD0
+
+# Death knight
+R:597:0x99:0x8C
+
+# Castamir the Usurper
+R:598:0x9C:0xC8
+
+# Time vortex
+R:599:0x99:0xD1
+
+# Shimmering vortex
+R:600:0x99:0xD2
+
+# Ancient blue dragon
+R:601:0x92:0xC0
+
+# Ancient bronze dragon
+R:602:0x92:0xC1
+
+# Beholder
+R:603:0x96:0xD7
+
+# Emperor wight
+R:604:0x95:0xBB
+
+# Seraph
+R:605:0x92:0x8A
+
+# Vargo, Tyrant of Fire
+R:606:0x93:0x85
+
+# Black wraith
+R:607:0x95:0xBC
+
+# Nightgaunt
+R:608:0x9C:0xC9
+
+# Baron of hell
+R:609:0x9C:0xCA
+
+# Scylla
+R:610:0xA1:0x94
+
+# Monastic lich
+R:611:0x93:0xFF
+
+# Nether wraith
+R:612:0x95:0xBD
+
+# Hellhound
+R:613:0x9A:0x8E
+
+# 7-headed hydra
+R:614:0x94:0xD1
+
+# Waldern, King of Water
+R:615:0x93:0x86
+
+# Kavlax the Many-Headed
+R:616:0x96:0xD2
+
+# Ancient white dragon
+R:617:0x92:0xC2
+
+# Ancient green dragon
+R:618:0x92:0xC3
+
+# Chthonian
+R:619:0x9C:0xCB
+
+# Eldrak
+R:620:0x95:0x8F
+
+# Ettin
+R:621:0x95:0x8E
+
+# Night mare
+R:622:0x99:0xBB
+
+# Vampire lord
+R:623:0x95:0x94
+
+# Ancient black dragon
+R:624:0x92:0xC4
+
+# Weird fume
+R:625:0x9A:0xF8
+
+# Spawn of Ubbo-Sathla
+R:626:0xA1:0x95
+
+# Fat Man
+R:627:0x9F:0xFD
+
+# Malekith the Accursed
+R:628:0x97:0x8D
+
+# Shadowfax, steed of Gandalf
+R:629:0xA1:0x96
+
+# Spirit troll
+R:630:0x95:0x90
+
+# War troll
+R:631:0x9C:0xCC
+
+# Disenchanter worm mass
+R:632:0x99:0xFE
+
+# Rotting quylthulg
+R:633:0x94:0x94
+
+# Lesser titan
+R:634:0x94:0x8F
+
+# 9-headed hydra
+R:635:0x94:0xD1
+
+# Enchantress
+R:636:0x99:0x8E
+
+# Ranger chieftain
+R:637:0x99:0x8F
+
+# Sorcerer
+R:638:0x99:0x90
+
+# Xaren
+R:639:0x95:0xC8
+
+# Giant roc
+R:640:0x92:0x8F
+
+# Minotaur
+R:641:0x93:0xC7
+
+# Medusa, the Gorgon
+R:642:0x98:0x85
+
+# Death drake
+R:643:0x92:0xC5
+
+# Ancient red dragon
+R:644:0x92:0xC6
+
+# Ancient gold dragon
+R:645:0x92:0xC7
+
+# Great crystal drake
+R:646:0x92:0xC8
+
+# Wyrd sister
+R:647:0x97:0x95
+
+# Vrock
+R:648:0x9C:0xCD
+
+# Death quasit
+R:649:0x93:0xD1
+
+# Giganto, the Gargantuan
+R:650:0xA1:0x97
+
+# Strygalldwir
+R:651:0x9C:0xCE
+
+# Fallen angel
+R:652:0x98:0xC6
+
+# Giant headless
+R:653:0xA1:0xB8
+
+# Judge Fire
+R:654:0x99:0x8B
+
+# Ubbo-Sathla, the Unbegotten Source
+R:655:0xA1:0xB9
+
+# Judge Mortis
+R:656:0xA1:0xBA
+
+# Dark elven sorcerer
+R:657:0x97:0xB9
+
+# Master lich
+R:658:0x93:0xFC
+
+# Byakhee
+R:659:0x9C:0xCF
+
+# Eol, the Dark Elf
+R:660:0x9C:0xD1
+
+# Archon
+R:661:0x92:0x8B
+
+# Formless spawn of Tsathoggua
+R:662:0x9C:0xD2
+
+# Hunting horror
+R:663:0x9C:0xD3
+
+# Undead beholder
+R:664:0x96:0xF8
+
+# Shadow
+R:665:0x93:0xB9
+
+# Iron lich
+R:666:0x9C:0xD4
+
+# Dread
+R:667:0x9F:0xFF
+
+# Greater basilisk
+R:668:0xA1:0xBB
+
+# Charybdis
+R:669:0xA1:0xBC
+
+# Jack of Shadows
+R:670:0x99:0x94
+
+# Zephyr Lord
+R:671:0x99:0x89
+
+# Juggernaut of Khorne
+R:672:0xA1:0xBD
+
+# Mumak
+R:673:0x99:0xBA
+
+# Judge Fear
+R:674:0x97:0x80
+
+# Ancient multi-hued dragon
+R:675:0x92:0xC9
+
+# Ethereal dragon
+R:676:0x92:0xCA
+
+# Dark young of Shub-Niggurath
+R:677:0x9C:0xD5
+
+# Colour out of space
+R:678:0x8C:0x91
+
+# Quaker, Master of Earth
+R:679:0x93:0x87
+
+# Death leprechaun
+R:680:0x97:0x96
+
+# Chaugnar Faugn, Horror from the Hills
+R:681:0xA1:0xCC
+
+# Lloigor
+R:682:0xA1:0xCD
+
+# Utgard-Loke
+R:683:0xA1:0xCE
+
+# Quachil Uttaus, Treader of the Dust
+R:684:0xA1:0xCF
+
+# Shoggoth
+R:685:0xA1:0xD0
+
+# Judge Death
+R:686:0xA1:0xD1
+
+# Ariel, Queen of Air
+R:687:0x93:0x89
+
+# 11-headed hydra
+R:688:0x94:0xD2
+
+# Patriarch
+R:689:0x99:0x92
+
+# Dreadmaster
+R:690:0x93:0xBD
+
+# Drolem
+R:691:0x97:0x8A
+
+# Scatha the Worm
+R:692:0x9A:0x93
+
+# Warrior of the Dawn
+R:693:0x9C:0xD6
+
+# Lesser black reaver
+R:694:0x95:0xBF
+
+# Zoth-Ommog
+R:695:0xA2:0xC1
+
+# Grand master thief
+R:696:0xA4:0xC2
+
+# Smaug the Golden
+R:697:0x92:0xCB
+
+# The Stormbringer
+R:698:0x9C:0xF8
+
+# Knight Templar
+R:699:0x9C:0xF9
+
+# Leprechaun fanatic
+R:700:0x97:0xB8
+
+# Dracolich
+R:701:0x92:0xCD
+
+# Greater titan
+R:702:0x94:0x90
+
+# Dracolisk
+R:703:0x92:0xCC
+
+# Winged Horror
+R:704:0xA5:0xD6
+
+# Spectral tyrannosaur
+R:705:0x9C:0xFA
+
+# Yibb-Tstll, the Patient One
+R:706:0xA1:0xD3
+
+# Ghatanothoa
+R:707:0xA1:0xD4
+
+# Ent
+R:708:0xA1:0xBE
+
+# Hru
+R:709:0xA1:0xD5
+
+# Itangast the Fire Drake
+R:710:0x92:0xCE
+
+# Death mold
+R:711:0x97:0xFF
+
+# Fafner the Dragon
+R:712:0x9C:0xFB
+
+# Charon, Boatman of the Styx
+R:713:0xA1:0xD6
+
+# Quickbeam, the Ent
+R:714:0xA1:0xD7
+
+# Glaurung, Father of the Dragons
+R:715:0x9A:0x92
+
+# Behemoth
+R:716:0xA1:0xF8
+
+# Garm, Guardian of Hel
+R:717:0x92:0xBC
+
+# Greater wall monster
+R:718:0x9C:0xFC
+
+# Nycadaemon
+R:719:0x9A:0x89
+
+# Barbazu
+R:720:0x9A:0x8D
+
+# Goat of Mendes
+R:721:0x9C:0xFD
+
+# Nightwing
+R:722:0x9A:0x95
+
+# Maulotaur
+R:723:0x9C:0xFE
+
+# Nether hound
+R:724:0x95:0xF9
+
+# Time hound
+R:725:0x95:0xFA
+
+# Plasma hound
+R:726:0x95:0xFB
+
+# Demonic quylthulg
+R:727:0x94:0x95
+
+# Great Storm Wyrm
+R:728:0x92:0xCF
+
+# Ulik the Troll
+R:729:0xA1:0xF9
+
+# Baphomet the Minotaur Lord
+R:730:0x93:0xC8
+
+# Hell knight
+R:731:0xA1:0xFA
+
+# Bull Gates
+R:732:0x9C:0xFF
+
+# Santa Claus
+R:733:0x9D:0x80
+
+# Eihort, the Thing in the Labyrinth
+R:734:0xA2:0xC2
+
+# The King in Yellow
+R:735:0xA2:0xC3
+
+# Great unclean one
+R:736:0xA2:0xC4
+
+# Lord of Chaos
+R:737:0x9D:0x81
+
+# Old Sorcerer
+R:738:0x91:0xC2
+
+# Ethereal hound
+R:739:0x9D:0x82
+
+# Lesser kraken
+R:740:0xA1:0xFB
+
+# Great Ice Wyrm
+R:741:0x92:0xD0
+
+# Demilich
+R:742:0x95:0xC2
+
+# The Phoenix
+R:743:0x92:0x90
+
+# Nightcrawler
+R:744:0x95:0xC4
+
+# Lord of Change
+R:745:0xA1:0xBF
+
+# Keeper of Secrets
+R:746:0xA2:0xC5
+
+# Shudde M'ell
+R:747:0xA2:0xC6
+
+# Hand druj
+R:748:0x99:0xC6
+
+# Eye druj
+R:749:0x99:0xC7
+
+# Skull druj
+R:750:0x99:0xC8
+
+# Chaos vortex
+R:751:0x99:0xD3
+
+# Aether vortex
+R:752:0x99:0xD4
+
+# Nidhogg, the Hel-Drake
+R:753:0xA2:0xC7
+
+# The Lernaean Hydra
+R:754:0x94:0xD3
+
+# Thuringwethil, the Vampire Messenger
+R:755:0x95:0x95
+
+# Great Hell Wyrm
+R:756:0x92:0xD1
+
+# Hastur the Unspeakable
+R:757:0x9D:0x83
+
+# Bloodthirster
+R:758:0xA1:0xFC
+
+# Draconic quylthulg
+R:759:0x94:0x96
+
+# Nyogtha, the Thing that Should not Be
+R:760:0x9D:0x84
+
+# Ahtu, Avatar of Nyarlathotep
+R:761:0xA2:0xC8
+
+# Fundin Bluecloak
+R:762:0xA2:0xC9
+
+# Bile Demon
+R:763:0xA5:0xF8
+
+# Uriel, Angel of Fire
+R:764:0x92:0x8C
+
+# Azriel, Angel of Death
+R:765:0x92:0x8D
+
+# Ancalagon the Black
+R:766:0x92:0xD2
+
+# Daoloth, the Render of the Veils
+R:767:0xA2:0xCA
+
+# Nightwalker
+R:768:0xA1:0xFD
+
+# Gabriel, the Messenger
+R:769:0x92:0x8E
+
+# Artsi, the Champion of Chaos
+R:770:0xA2:0xCB
+
+# Saruman of Many Colours
+R:771:0x99:0x96
+
+# Harowen the Black Hand
+R:772:0xA2:0xCC
+
+# Osyluth
+R:773:0xA5:0xF9
+
+# Dreadlord
+R:774:0x93:0xBE
+
+# Greater kraken
+R:775:0xA1:0xFE
+
+# Archlich
+R:776:0x95:0xC5
+
+# The Cat Lord
+R:777:0x9D:0x87
+
+# Jabberwock
+R:778:0xA5:0xFA
+
+# Chaos hound
+R:779:0x95:0xFD
+
+# Vlad Dracula, Prince of Darkness
+R:780:0xA2:0xCD
+
+# Beholder hive-mother
+R:781:0xA2:0xCE
+
+# Leviathan
+R:782:0xA1:0xFF
+
+# Great Wyrm of Chaos
+R:783:0x92:0xD3
+
+# Great Wyrm of Law
+R:784:0x92:0xD4
+
+# Great Wyrm of Balance
+R:785:0x92:0xD5
+
+# Shambler
+R:786:0x9D:0x89
+
+# Gelugon
+R:787:0xA5:0xFB
+
+# Glaaki
+R:788:0xA0:0x80
+
+# T'ron, the Rebel Dragonrider
+R:789:0x9D:0x8A
+
+# Great Wyrm of Many Colours
+R:790:0x9D:0x8B
+
+# Mardra, rider of the Gold Loranth
+R:791:0x9D:0x8C
+
+# Tselakus, the Dreadlord
+R:792:0x93:0xBF
+
+# Sky Drake
+R:793:0x9D:0x8D
+
+# Eilinel the Entrapped
+R:794:0x93:0xBB
+
+# Horned Reaper
+R:795:0xA5:0xFC
+
+# The Norsa
+R:796:0x9D:0x8F
+
+# Rhan-Tegoth
+R:797:0xA2:0xD1
+
+# Black reaver
+R:798:0x93:0xFD
+
+# Master mindcrafter
+R:799:0x99:0x97
+
+# Greater demonic quylthulg
+R:800:0x94:0x97
+
+# Greater draconic quylthulg
+R:801:0x94:0xB8
+
+# Greater rotting quylthulg
+R:802:0x94:0xB9
+
+# Null, the Living Void
+R:803:0xA1:0xC0
+
+# Feagwath, the Undead Sorcerer
+R:804:0x93:0xFE
+
+# Omarax the Eye Tyrant
+R:805:0x96:0xF9
+
+# Tsathoggua, the Sleeper of N'kai
+R:806:0xA2:0xD2
+
+# Greater Balrog
+R:807:0x9A:0x8B
+
+# Ungoliant, the Unlight
+R:808:0x95:0x80
+
+# Atlach-Nacha, the Spider God
+R:809:0x9D:0x92
+
+# Y'golonac
+R:810:0xA2:0xD3
+
+# Aether hound
+R:811:0x95:0xFE
+
+# Pit Fiend
+R:812:0x9A:0x8A
+
+# The Serpent of Chaos
+R:813:0x9D:0xC5
+
+# Yig, Father of Serpents
+R:814:0xA2:0xD4
+
+# Unmaker
+R:815:0x9D:0x94
+
+# Cyberdemon
+R:816:0x9D:0x95
+
+# Hela, Queen of the Dead
+R:817:0xA2:0xD5
+
+# The Mouth of Sauron
+R:818:0xA2:0xD6
+
+# The Necromancer of Dol Guldur
+R:819:0x9D:0x96
+
+# Lessa, rider of the Gold Ramoth
+R:820:0x9D:0x97
+
+# Master quylthulg
+R:821:0x94:0xBA
+
+# Qlzqqlzuup, the Lord of Flesh
+R:822:0x94:0xBB
+
+# Cthugha, the Living Flame
+R:823:0xA2:0xD7
+
+# F'lar, rider of the Bronze Mnementh
+R:824:0x9D:0xB8
+
+# Maeglin, the Traitor of Gondolin
+R:825:0x95:0xC6
+
+# Cyaegha
+R:826:0xA2:0xF8
+
+# Pazuzu, Lord of Air
+R:827:0xA2:0xF9
+
+# Ithaqua the Windwalker
+R:828:0x9D:0xB9
+
+# Greater Hellhound
+R:829:0x92:0xBB
+
+# Cantoras, the Skeletal Lord
+R:830:0x99:0xC9
+
+# Mephistopheles, Lord of Hell
+R:831:0x9D:0xBA
+
+# Godzilla
+R:832:0x9D:0xBB
+
+# Abhoth, Source of Uncleanness
+R:833:0xA2:0xFA
+
+# Ymir, the Ice Giant
+R:834:0xA2:0xFB
+
+# Loki, the Trickster
+R:835:0xA2:0xFC
+
+# Star-spawn of Cthulhu
+R:836:0x9D:0xBC
+
+# Surtur, the Fire Giant
+R:837:0xA2:0xFD
+
+# The Tarrasque
+R:838:0xA1:0xCB
+
+# Lungorthin, the Balrog of White Fire
+R:839:0xA2:0xFE
+
+# Draugluin, Sire of All Werewolves
+R:840:0xA2:0xFF
+
+# Shuma-Gorath
+R:841:0xA3:0x80
+
+# Tulzscha, the Green Flame
+R:842:0xA1:0xCA
+
+# Oremorj, the Cyberdemon Lord
+R:843:0xA1:0xC9
+
+# Vecna, the Emperor Lich
+R:844:0x9D:0xBD
+
+# Yog-Sothoth, the All-in-One
+R:845:0x9D:0xBE
+
+# Fenris Wolf
+R:846:0xA1:0xC8
+
+# Great Wyrm of Power
+R:847:0x9D:0xBF
+
+# Shub-Niggurath, Black Goat of the Woods
+R:848:0x9D:0xC0
+
+# Nodens, Lord of the Great Abyss
+R:849:0x99:0x85
+
+# Carcharoth, the Jaws of Thirst
+R:850:0x92:0xBE
+
+# Nyarlathotep, the Crawling Chaos
+R:851:0x9D:0xC1
+
+# Azathoth, the Daemon Sultan
+R:852:0x9D:0xC2
+
+# Huan, Wolfhound of the Valar
+R:853:0x92:0xBF
+
+# Jormungand the Midgard Serpent
+R:854:0xA1:0xC7
+
+# The Destroyer
+R:855:0xA1:0xC6
+
+# Gothmog, the High Captain of Balrogs
+R:856:0x9A:0x90
+
+# Great Cthulhu
+R:857:0x9D:0xC3
+
+# Sorka, rider of the Gold Faranth
+R:858:0x9D:0xC4
+
+# The Unicorn of Order
+R:859:0xA1:0xC5
+
+# Sauron, the Sorcerer
+R:860:0x99:0xB8
+
+# DarkGod, the Mighty Coder of Hell
+R:861:0xA3:0xD5
+
+# Morgoth, Lord of Darkness
+R:862:0x9D:0xC6
+
+# Human Warrior
+R:863:0x9D:0xF8
+
+# Elven archer
+R:864:0x9D:0xF9
+
+# Dwarven warrior
+R:865:0x9D:0xFA
+
+# Elite uruk
+R:866:0x9D:0xFB
+
+# The Philosophy Teacher
+R:867:0xA1:0xC4
+
+# The Variant Maintainer
+R:868:0xA1:0xC3
+
+# Random Number Generator
+R:869:0xA1:0xC2
+
+# Rocket mine
+R:870:0xA2:0x80
+
+# Bouncing mine
+R:871:0xA2:0x81
+
+# Durin's Bane
+R:872:0xA3:0x81
+
+# The Icky Queen
+R:873:0xA3:0x82
+
+# Rot jelly
+R:874:0xA2:0x82
+
+# Death
+R:875:0xA2:0x83
+
+# Famine
+R:876:0xA2:0x85
+
+# Pestilence
+R:877:0xA2:0x84
+
+# War
+R:878:0xA2:0x86
+
+# Pike
+R:879:0xA2:0x87
+
+# Electric eel
+R:880:0xA2:0x88
+
+# Giant crayfish
+R:881:0xA2:0x89
+
+# Mermaid
+R:882:0xA2:0x8A
+
+# Box jellyfish
+R:883:0xA0:0xB9
+
+# Giant piranha
+R:884:0x9E:0xD5
+
+# Piranha
+R:885:0x9E:0xD5
+
+# Bullywug
+R:886:0xA2:0x8C
+
+# Bullywug warrior
+R:887:0xA2:0x8D
+
+# Bullywug shaman
+R:888:0xA2:0x8E
+
+# Whale
+R:889:0xA0:0xD0
+
+# Sand mite
+R:890:0xA2:0x90
+
+# Octopus
+R:891:0xA2:0x91
+
+# Giant octopus
+R:892:0xA2:0x92
+
+# Eye of the deep
+R:893:0xA2:0x93
+
+# Murk dweller
+R:894:0xA3:0x83
+
+# Drowned soul
+R:895:0xA3:0x84
+
+# Tiger shark
+R:896:0xA3:0x85
+
+# Hammerhead shark
+R:897:0xA0:0xC8
+
+# Great white shark
+R:898:0xA0:0xFA
+
+# Aquatic golem
+R:899:0xA3:0x86
+
+# Aquatic kobold
+R:900:0xA3:0x87
+
+# White shark
+R:901:0xA0:0xFA
+
+# Scrag
+R:902:0xA3:0x89
+
+# Jaws
+R:903:0xA1:0x84
+
+# Aquatic elf
+R:904:0xA3:0x8B
+
+# Aquatic elven warrior
+R:905:0xA3:0x8C
+
+# Aquatic elven shaman
+R:906:0xA3:0x8D
+
+# Stargazer
+R:907:0xA3:0x8E
+
+# Elder stargazer
+R:908:0xA3:0x8F
+
+# Flounder
+R:909:0xA3:0x90
+
+# Giant turtle
+R:910:0xA3:0x91
+
+# Baby dragon turtle
+R:911:0xA3:0x92
+
+# Young dragon turtle
+R:912:0xA3:0x93
+
+# Mature dragon turtle
+R:913:0xA3:0x94
+
+# Ancient dragon turtle
+R:914:0xA3:0x95
+
+# Fastitocalon
+R:915:0xA3:0x96
+
+# Undead stargazer
+R:916:0xA3:0x97
+
+# Killer whale
+R:917:0xA0:0x94
+
+# Merrow
+R:918:0xA5:0xFD
+
+# Water naga
+R:919:0xA3:0xB9
+
+# Devilfish
+R:920:0xA3:0xBA
+
+# Undead devilfish
+R:921:0xA3:0xBB
+
+# Moby Dick, the White Whale
+R:922:0xA3:0xB8
+
+# Aquatic hound
+R:923:0xA3:0xBD
+
+# Water demon
+R:924:0xA3:0xBE
+
+# Ixitxachitl
+R:925:0x9F:0xD4
+
+# Ixitxachitl priest
+R:926:0xA3:0xC0
+
+# Vampiric ixitxachitl
+R:927:0xA3:0xC1
+
+# Mathilde, the Science Student
+R:928:0xA2:0x94
+
+# Child spirit
+R:929:0xA2:0x95
+
+# Young spirit
+R:930:0xA2:0x96
+
+# Mature spirit
+R:931:0xA2:0x97
+
+# Experienced spirit
+R:932:0xA2:0xB8
+
+# Wise spirit
+R:933:0xA2:0xB9
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0xA3:0xC2
+
+# Gandalf the Grey
+R:935:0xA3:0xC3
+
+# Nar, the Dwarf
+R:936:0xA3:0xC4
+
+# Novice mindcrafter
+R:937:0x98:0xD2
+
+# Great Swamp Wyrm
+R:938:0xA5:0xFE
+
+# Great Bile Wyrm
+R:939:0xA5:0xFF
+
+# Blue Firelizard
+R:940:0xA2:0xBA
+
+# Green Firelizard
+R:941:0xA2:0xBB
+
+# Brown Firelizard
+R:942:0xA2:0xBC
+
+# Bronze Firelizard
+R:943:0xA2:0xBD
+
+# Gold Firelizard
+R:944:0xA2:0xBE
+
+# High-elven ranger
+R:945:0xA2:0xBF
+
+# Uvatha the Horseman
+R:946:0xA3:0xC8
+
+# Adunaphel the Quiet
+R:947:0xA3:0xC9
+
+# Akhorahil the Blind
+R:948:0xA3:0xCA
+
+# Ren the Unclean
+R:949:0xA3:0xCB
+
+# Ji Indur Dawndeath
+R:950:0xA3:0xCC
+
+# Dwar, Dog Lord of Waw
+R:951:0xA3:0xCD
+
+# Hoarmurath of Dir
+R:952:0xA3:0xCE
+
+# Khamul, the Black Easterling
+R:953:0xA3:0xCF
+
+# The Witch-King of Angmar
+R:954:0xA3:0xD0
+
+# Green Dragonrider
+R:955:0x9D:0x8E
+
+# Blue Dragonrider
+R:956:0x9D:0x86
+
+# Brown Dragonrider
+R:957:0x9D:0x90
+
+# Bronze Dragonrider
+R:958:0x9D:0x90
+
+# Gold Dragonrider
+R:959:0x9D:0x8C
+
+# Thread
+R:960:0xA2:0xC0
+
+# Gorlim, Betrayer of Barahir
+R:961:0xA3:0xD1
+
+# The Blubbering idiot, agent of black market, Simon the weak
+R:962:0x98:0xBA
+
+# Aranea
+R:963:0xA4:0x92
+
+# Elder aranea
+R:964:0xA3:0xD2
+
+# Giant brown tick
+R:965:0xA6:0x80
+
+# Dolphiner
+R:966:0xA3:0xD4
+
+# Novice possessor (soul)
+R:967:0xA5:0xBE
+
+# Bat of Gorgoroth
+R:968:0xA6:0x8F
+
+# The Princess
+R:969:0xA6:0x90
+
+# Merton Proudfoot, the lost hobbit
+R:970:0xA6:0x91
+
+# The Wight-King of the Barrow-downs
+R:971:0xA6:0x92
+
+# Adventurer
+R:972:0xA6:0x93
+
+# Experienced possessor (soul)
+R:973:0xA6:0x94
+
+# Old possessor (soul)
+R:974:0xA6:0x95
+
+# Death orb
+R:975:0xA6:0x96
+
+# Bronze dragon worm
+R:976:0xA6:0xB8
+
+# Gold dragon worm
+R:977:0xA6:0x97
+
+# Moldoux, the Defenceless Mold
+R:978:0xA1:0xC1
+
+# The Physics Teacher
+R:979:0xA2:0xCF
+
+# Ar-Pharazon the Golden
+R:980:0xA3:0xD6
+
+# Doppelganger
+R:981:0x8F:0x84
+
+# Marylene, Heartbreakeress of the Netherworld
+R:982:0xA4:0x85
+
+# The Greater Lag Monster
+R:983:0xA4:0xC5
+
+# Hrungnir, the Stone Giant
+R:984:0x97:0x82
+
+# Bullroarer the Hobbit
+R:985:0x97:0x93
+
+# 3-headed hydra
+R:986:0x94:0xCC
+
+# Uldor the Accursed
+R:987:0x99:0x8D
+
+# Mystic
+R:988:0x99:0x93
+
+# Elder vampire
+R:989:0x94:0x91
+
+# Ulfang the Black
+R:990:0x98:0xCE
+
+# Demonologist
+R:991:0x97:0xBA
+
+# Hezrou
+R:992:0x94:0xD4
+
+# Glabrezu
+R:993:0x94:0xC6
+
+# Nalfeshnee
+R:994:0xA4:0xC6
+
+# Marilith
+R:995:0xA4:0xC7
+
+# Lesser Balrog
+R:996:0x9A:0x8D
+
+# Master mystic
+R:997:0x98:0xCC
+
+# Grand master mystic
+R:998:0x99:0x95
+
+# Erinyes
+R:999:0x93:0xBC
+
+# Novice mindcrafter
+R:1000:0x98:0xD2
+
+# Polyphemus, the Blind Cyclops
+R:1001:0xA6:0x81
+
+# Great Wyrm of Perplexity
+R:1002:0xA4:0xCA
+
+# Hound of Tindalos
+R:1003:0xA4:0xCB
+
+# Great Wyrm of Thunder
+R:1004:0xA6:0x82
+
+# Silver mouse
+R:1005:0xA6:0x83
+
+# The Rat King
+R:1006:0xA4:0xCE
+
+# Vort the Kobold Queen
+R:1007:0xA4:0xCF
+
+# Giant black louse
+R:1008:0xA4:0xD0
+
+# Fire Phantom
+R:1009:0xA4:0xD1
+
+# The Insane Player
+R:1010:0x8C:0xB9
+
+# Glaryssa, Succubus Queen
+R:1011:0xA4:0xD2
+
+# Vermicious Knid
+R:1012:0xA4:0xD3
+
+# Bone golem
+R:1013:0xA4:0xD4
+
+# Snake of Yig
+R:1014:0xA4:0xD5
+
+# Bronze golem
+R:1015:0xA6:0x84
+
+# Dimensional shambler
+R:1016:0xA4:0xD7
+
+# Cultist
+R:1017:0x8D:0xD1
+
+# Cult leader
+R:1018:0x90:0x8F
+
+# Servitor of the outer gods
+R:1019:0xA5:0x82
+
+# Avatar of Nyarlathotep
+R:1020:0xA5:0x83
+
+# Thiazi, the Storm Giant
+R:1021:0xA6:0x85
+
+# Hypnos, Lord of Sleep
+R:1022:0xA5:0x85
+
+# Blue dragon worm
+R:1023:0xA5:0x86
+
+# White dragon worm
+R:1024:0xA5:0x87
+
+# Green dragon worm
+R:1025:0xA5:0x8A
+
+# Black dragon worm
+R:1026:0xA5:0x89
+
+# Red dragon worm
+R:1027:0xA5:0x88
+
+# Multi-hued dragon worm
+R:1028:0xA5:0x8B
+
+# The Minotaur of the Labyrinth
+R:1029:0xA5:0x8C
+
+# The Sandworm Queen
+R:1030:0xA5:0x93
+
+# Sandworm
+R:1031:0xA5:0x94
+
+# Tik'srvzllat
+R:1032:0xA5:0x95
+
+# The Glass Golem
+R:1033:0xA6:0x86
+
+# The White Balrog
+R:1034:0xA6:0x87
+
+# Golgarach, the Living Rock
+R:1035:0x83:0xBC
+
+# Atlas, the Titan
+R:1036:0xA6:0x88
+
+# Kronos, Lord of the Titans
+R:1037:0xA6:0x89
+
+# Water hound
+R:1038:0xA6:0xC0
+
+# Improv, the mighty MoLD
+R:1039:0xA6:0xC6
+
+# Emperor Mimic
+R:1040:0xA6:0xD4
+
+# Melinda Proudfoot
+R:1041:0x87:0xE2
+
+# Thrain, the King Under the Mountain
+R:1042:0x87:0xE3
+
+# Fire golem
+R:1043:0x89:0xD8
+
+# Melkor, Lord of Darkness
+R:1044:0x89:0xD9
+
+# Spirit
+R:1045:0x8C:0xD7
+
+# Spirit
+R:1046:0x8C:0xD8
+
+# Spirit
+R:1047:0x8C:0xD9
+
+# Spirit
+R:1048:0x8C:0xDA
+
+# Spirit
+R:1049:0x8C:0xDB
+
+# Spirit
+R:1050:0x8C:0xDC
+
+# Spirit
+R:1051:0x8C:0xDD
+
+# Spirit
+R:1052:0x8C:0xDE
+
+# Spirit
+R:1053:0x8C:0xDF
+
+# Spirit
+R:1054:0x8C:0xE0
+
+# Spirit
+R:1055:0x8C:0xE1
+
+# Spirit
+R:1056:0x8C:0xE2
+
+# Spirit
+R:1057:0x8C:0xDB
+
+# Spirit
+R:1058:0x8C:0xE3
+
+# Spirit
+R:1059:0x8C:0xE4
+
+# Spirit
+R:1060:0x8C:0xE5
+
+# Spirit
+R:1061:0x8C:0xE6
+
+# Spirit
+R:1062:0x8C:0xE7
+
+# Spirit
+R:1063:0x8C:0xE8
+
+# Spirit
+R:1064:0x8C:0xE9
+
+# Spirit
+R:1065:0x8C:0xEA
+
+# Spirit
+R:1066:0x8C:0xEB
+
+# Spirit
+R:1067:0x8C:0xEC
+
+# Spirit
+R:1068:0x8C:0xED
+
+# Spirit
+R:1069:0x8C:0xEE
+
+# Spirit
+R:1070:0x8C:0xEF
+
+# Spirit
+R:1071:0x8C:0xF0
+
+# Spirit
+R:1072:0x8C:0xF1
+
+# Spirit
+R:1073:0x8C:0xF2
+
+# Spirit
+R:1074:0x8C:0xF3
+
+# Spirit
+R:1075:0x8C:0xF4
+
+# Spells (*)
+S:48:0x8C:0x80
+
+# Spells (*)
+S:49:0x8C:0x81
+
+# Spells (*)
+S:50:0x8C:0x82
+
+# Spells (*)
+S:51:0x8C:0x83
+
+# Spells (*)
+S:52:0x8C:0x84
+
+# Spells (*)
+S:53:0x8C:0x85
+
+# Spells (*)
+S:54:0x8C:0x86
+
+# Spells (*)
+S:55:0x8C:0x87
+
+# Spells (*)
+S:56:0x8C:0x88
+
+# Spells (*)
+S:57:0x8C:0x89
+
+# Spells (*)
+S:58:0x8C:0x8A
+
+# Spells (*)
+S:59:0x8C:0x8B
+
+# Spells (*)
+S:60:0x8C:0x8C
+
+# Spells (*)
+S:61:0x8C:0x8D
+
+# Spells (*)
+S:62:0x8C:0x8E
+
+# Spells (*)
+S:63:0x8C:0x8F
+
+# Spells (|)
+S:64:0x8A:0xF8
+
+# Spells (|)
+S:65:0x8A:0xFC
+
+# Spells (|)
+S:66:0x8B:0x80
+
+# Spells (|)
+S:67:0x8B:0x84
+
+# Spells (|)
+S:68:0x8B:0x88
+
+# Spells (|)
+S:69:0x8B:0x8C
+
+# Spells (|)
+S:70:0x8B:0x90
+
+# Spells (|)
+S:71:0x8B:0x94
+
+# Spells (|)
+S:72:0x8B:0xB8
+
+# Spells (|)
+S:73:0x8B:0xBC
+
+# Spells (|)
+S:74:0x8B:0xC0
+
+# Spells (|)
+S:75:0x8B:0xC4
+
+# Spells (|)
+S:76:0x8B:0xC8
+
+# Spells (|)
+S:77:0x8B:0xCC
+
+# Spells (|)
+S:78:0x8B:0xD0
+
+# Spells (|)
+S:79:0x8B:0xD4
+
+# Spells (-)
+S:80:0x8A:0xF9
+
+# Spells (-)
+S:81:0x8A:0xFD
+
+# Spells (-)
+S:82:0x8B:0x81
+
+# Spells (-)
+S:83:0x8B:0x85
+
+# Spells (-)
+S:84:0x8B:0x89
+
+# Spells (-)
+S:85:0x8B:0x8D
+
+# Spells (-)
+S:86:0x8B:0x91
+
+# Spells (-)
+S:87:0x8B:0x95
+
+# Spells (-)
+S:88:0x8B:0xB9
+
+# Spells (-)
+S:89:0x8B:0xBD
+
+# Spells (-)
+S:90:0x8B:0xC1
+
+# Spells (-)
+S:91:0x8B:0xC5
+
+# Spells (-)
+S:92:0x8B:0xC9
+
+# Spells (-)
+S:93:0x8B:0xCD
+
+# Spells (-)
+S:94:0x8B:0xD1
+
+# Spells (-)
+S:95:0x8B:0xD5
+
+# Spells (:)
+S:96:0x8A:0xFA
+
+# Spells (:)
+S:97:0x8A:0xFE
+
+# Spells (:)
+S:98:0x8B:0x82
+
+# Spells (:)
+S:99:0x8B:0x86
+
+# Spells (:)
+S:100:0x8B:0x8A
+
+# Spells (:)
+S:101:0x8B:0x8E
+
+# Spells (:)
+S:102:0x8B:0x92
+
+# Spells (:)
+S:103:0x8B:0x96
+
+# Spells (:)
+S:104:0x8B:0xBA
+
+# Spells (:)
+S:105:0x8B:0xBE
+
+# Spells (:)
+S:106:0x8B:0xC2
+
+# Spells (:)
+S:107:0x8B:0xC6
+
+# Spells (:)
+S:108:0x8B:0xCA
+
+# Spells (:)
+S:109:0x8B:0xCE
+
+# Spells (:)
+S:110:0x8B:0xD2
+
+# Spells (:)
+S:111:0x8B:0xD6
+
+# Spells (\)
+S:112:0x8A:0xFB
+
+# Spells (\)
+S:113:0x8A:0xFF
+
+# Spells (\)
+S:114:0x8B:0x83
+
+# Spells (\)
+S:115:0x8B:0x87
+
+# Spells (\)
+S:116:0x8B:0x8B
+
+# Spells (\)
+S:117:0x8B:0x8F
+
+# Spells (\)
+S:118:0x8B:0x93
+
+# Spells (\)
+S:119:0x8B:0x97
+
+# Spells (\)
+S:120:0x8B:0xBB
+
+# Spells (\)
+S:121:0x8B:0xBF
+
+# Spells (\)
+S:122:0x8B:0xC3
+
+# Spells (\)
+S:123:0x8B:0xC7
+
+# Spells (\)
+S:124:0x8B:0xCB
+
+# Spells (\)
+S:125:0x8B:0xCF
+
+# Spells (\)
+S:126:0x8B:0xD3
+
+# Spells (\)
+S:127:0x8B:0xD7
+
+# Amulets (
+S:128:0x86:0xFF
+
+# Amulets (
+S:129:0x86:0xF8
+
+# Amulets (
+S:130:0x87:0x80
+
+# Amulets (
+S:131:0x86:0xFA
+
+# Amulets (
+S:132:0x86:0xFB
+
+# Amulets (
+S:133:0x86:0xFC
+
+# Amulets (
+S:134:0x86:0xFD
+
+# Amulets (
+S:135:0x86:0xFE
+
+# Amulets (
+S:136:0x86:0xF9
+
+# Amulets (
+S:137:0x86:0xF9
+
+# Amulets (
+S:138:0x87:0x81
+
+# Amulets (
+S:139:0x87:0x82
+
+# Amulets (
+S:140:0x87:0x83
+
+# Amulets (
+S:141:0x87:0x84
+
+# Amulets (
+S:142:0x87:0x85
+
+# Amulets (
+S:143:0x87:0x86
+
+# Rings (=)
+S:144:0x85:0xBF
+
+# Rings (=)
+S:145:0x85:0xB8
+
+# Rings (=)
+S:146:0x85:0xC0
+
+# Rings (=)
+S:147:0x85:0xBA
+
+# Rings (=)
+S:148:0x85:0xBB
+
+# Rings (=)
+S:149:0x85:0xBC
+
+# Rings (=)
+S:150:0x85:0xBD
+
+# Rings (=)
+S:151:0x85:0xBE
+
+# Rings (=)
+S:152:0x85:0xB9
+
+# Rings (=)
+S:153:0x85:0xB9
+
+# Rings (=)
+S:154:0x85:0xC1
+
+# Rings (=)
+S:155:0x85:0xC2
+
+# Rings (=)
+S:156:0x85:0xC3
+
+# Rings (=)
+S:157:0x85:0xC4
+
+# Rings (=)
+S:158:0x85:0xC5
+
+# Rings (=)
+S:159:0x85:0xC6
+
+# Staffs (_)
+S:160:0x87:0x8E
+
+# Staffs (_)
+S:161:0x87:0x8D
+
+# Staffs (_)
+S:162:0x87:0x8D
+
+# Staffs (_)
+S:163:0x87:0x8A
+
+# Staffs (_)
+S:164:0x87:0x8A
+
+# Staffs (_)
+S:165:0x87:0x8B
+
+# Staffs (_)
+S:166:0x87:0x8D
+
+# Staffs (_)
+S:167:0x87:0x88
+
+# Staffs (_)
+S:168:0x87:0x8D
+
+# Staffs (_)
+S:169:0x87:0x8D
+
+# Staffs (_)
+S:170:0x87:0x8A
+
+# Staffs (_)
+S:171:0x87:0x8C
+
+# Staffs (_)
+S:172:0x87:0x8A
+
+# Staffs (_)
+S:173:0x87:0x8B
+
+# Staffs (_)
+S:174:0x87:0x8E
+
+# Staffs (_)
+S:175:0x87:0x88
+
+# Wands (-)
+S:176:0x86:0xCF
+
+# Wands (-)
+S:177:0x86:0xC8
+
+# Wands (-)
+S:178:0x86:0xD0
+
+# Wands (-)
+S:179:0x86:0xCA
+
+# Wands (-)
+S:180:0x86:0xCB
+
+# Wands (-)
+S:181:0x86:0xCC
+
+# Wands (-)
+S:182:0x86:0xCD
+
+# Wands (-)
+S:183:0x86:0xCE
+
+# Wands (-)
+S:184:0x86:0xC9
+
+# Wands (-)
+S:185:0x86:0xC9
+
+# Wands (-)
+S:186:0x86:0xD1
+
+# Wands (-)
+S:187:0x86:0xD2
+
+# Wands (-)
+S:188:0x86:0xD3
+
+# Wands (-)
+S:189:0x86:0xD4
+
+# Wands (-)
+S:190:0x86:0xD5
+
+# Wands (-)
+S:191:0x86:0xD6
+
+# Rods (-)
+S:192:0x86:0xBF
+
+# Rods (-)
+S:193:0x86:0xB8
+
+# Rods (-)
+S:194:0x86:0xC0
+
+# Rods (-)
+S:195:0x86:0xBA
+
+# Rods (-)
+S:196:0x86:0xBB
+
+# Rods (-)
+S:197:0x86:0xBC
+
+# Rods (-)
+S:198:0x86:0xBD
+
+# Rods (-)
+S:199:0x86:0xBE
+
+# Rods (-)
+S:200:0x86:0xB9
+
+# Rods (-)
+S:201:0x86:0xB9
+
+# Rods (-)
+S:202:0x86:0xC1
+
+# Rods (-)
+S:203:0x86:0xC2
+
+# Rods (-)
+S:204:0x86:0xC3
+
+# Rods (-)
+S:205:0x86:0xC4
+
+# Rods (-)
+S:206:0x86:0xC5
+
+# Rods (-)
+S:207:0x86:0xC6
+
+# Scrolls (?)
+S:208:0x85:0x94
+
+# Scrolls (?)
+S:209:0x85:0x95
+
+# Scrolls (?)
+S:210:0x85:0x96
+
+# Scrolls (?)
+S:211:0x85:0x97
+
+# Scrolls (?)
+S:212:0x85:0x94
+
+# Scrolls (?)
+S:213:0x85:0x95
+
+# Scrolls (?)
+S:214:0x85:0x96
+
+# Scrolls (?)
+S:215:0x85:0x97
+
+# Scrolls (?)
+S:216:0x85:0x94
+
+# Scrolls (?)
+S:217:0x85:0x95
+
+# Scrolls (?)
+S:218:0x85:0x96
+
+# Scrolls (?)
+S:219:0x85:0x97
+
+# Scrolls (?)
+S:220:0x85:0x94
+
+# Scrolls (?)
+S:221:0x85:0x95
+
+# Scrolls (?)
+S:222:0x85:0x96
+
+# Scrolls (?)
+S:223:0x85:0x97
+
+# Potions (!)
+S:224:0x85:0xFF
+
+# Potions (!)
+S:225:0x85:0xF8
+
+# Potions (!)
+S:226:0x86:0x80
+
+# Potions (!)
+S:227:0x85:0xFA
+
+# Potions (!)
+S:228:0x85:0xFB
+
+# Potions (!)
+S:229:0x85:0xFC
+
+# Potions (!)
+S:230:0x85:0xFD
+
+# Potions (!)
+S:231:0x85:0xFE
+
+# Potions (!)
+S:232:0x85:0xF9
+
+# Potions (!)
+S:233:0x85:0xF9
+
+# Potions (!)
+S:234:0x86:0x81
+
+# Potions (!)
+S:235:0x86:0x82
+
+# Potions (!)
+S:236:0x86:0x83
+
+# Potions (!)
+S:237:0x86:0x84
+
+# Potions (!)
+S:238:0x86:0x85
+
+# Potions (!)
+S:239:0x86:0x86
+
+# Food (,)
+S:240:0x86:0x8F
+
+# Food (,)
+S:241:0x86:0x88
+
+# Food (,)
+S:242:0x86:0x90
+
+# Food (,)
+S:243:0x86:0x8A
+
+# Food (,)
+S:244:0x86:0x8B
+
+# Food (,)
+S:245:0x86:0x8C
+
+# Food (,)
+S:246:0x86:0x8D
+
+# Food (,)
+S:247:0x86:0x8E
+
+# Food (,)
+S:248:0x86:0x89
+
+# Food (,)
+S:249:0x86:0x89
+
+# Food (,)
+S:250:0x86:0x91
+
+# Food (,)
+S:251:0x86:0x92
+
+# Food (,)
+S:252:0x86:0x93
+
+# Food (,)
+S:253:0x86:0x94
+
+# Food (,)
+S:254:0x86:0x95
+
+# Food (,)
+S:255:0x86:0x96
+
diff --git a/lib/pref/graf-mac.prf b/lib/pref/graf-mac.prf
new file mode 100644
index 00000000..7bb84141
--- /dev/null
+++ b/lib/pref/graf-mac.prf
@@ -0,0 +1,15 @@
+# File: graf-mac.prf
+
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+# Standard file
+?:[EQU $GRAF old]
+%:graf-xxx.prf
+
+# New tiles
+?:[EQU $GRAF new]
+%:graf-new.prf
diff --git a/lib/pref/graf-new.prf b/lib/pref/graf-new.prf
new file mode 100644
index 00000000..3df5caa9
--- /dev/null
+++ b/lib/pref/graf-new.prf
@@ -0,0 +1,6844 @@
+# PRF file generated by Andreas Koch`s Tile Assigner
+# 23/06/2004 Edited manually
+
+# 2460 items
+# 2312 probably mapped correctly
+# 147 imported but not yet defined
+# 1 defined to value(s) lower than 0x80
+# Old header :
+# File: graf-new.prf
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+# with Adam Bolt's 16x16 tiles.
+#
+# By Robert Ruehlmann < rr9@angband.org >
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+# General Store
+B:0:0x82/0x87
+
+# Armoury
+B:1:0x82/0x88
+
+# Weaponsmith
+B:2:0x82/0x89
+
+# Temple
+B:3:0x82/0x8A
+
+# Alchemy shop
+B:4:0x82/0x8B
+
+# Magic shop
+B:5:0x82/0x8C
+
+# Black Market
+B:6:0x82/0x8D
+
+# Home
+B:7:0x82/0x8E
+
+# Book Store
+B:8:0x82/0x8F
+
+# Pet Shop
+B:9:0x82/0x90
+
+# Mayor's Office
+B:10:0x86/0xA0
+
+# Inn
+B:11:0x86/0xA1
+
+# The Soothsayer
+B:12:0x86/0xA2
+
+# Library
+B:13:0x86/0xA3
+
+# Castle
+B:14:0x86/0xA4
+
+# Casino
+B:15:0x86/0xA5
+
+# Beastmaster Shanty
+B:16:0x86/0xA6
+
+# Fighters Hall
+B:17:0x86/0xA7
+
+# Tower of Magery
+B:18:0x86/0xA8
+
+# Inner Temple
+B:19:0x86/0xA9
+
+# Paladins Guild
+B:20:0x86/0xAA
+
+# Rangers Guild
+B:21:0x86/0xAB
+
+# Thunderlords' Hide
+B:22:0x86/0xAC
+
+# The Mirror
+B:23:0x86/0xAD
+
+# Seat of Ruling
+B:24:0x86/0xAE
+
+# Wizards Spire
+B:25:0x86/0xAF
+
+# Priests Circle
+B:26:0x86/0xB0
+
+# Tower of the King
+B:27:0x86/0xB1
+
+# Library
+B:28:0x86/0xA3
+
+# The White Tree
+B:29:0x86/0xB2
+
+# Craftsmaster
+B:30:0x86/0xB3
+
+# Earth-Dome (Nature)
+B:31:0x86/0xB4
+
+# Minstrels Haven
+B:32:0x86/0xB5
+
+# Star-Dome
+B:33:0x86/0xB6
+
+# Valarin Temple
+B:34:0x86/0xB7
+
+# Sea-Dome
+B:35:0x86/0xB8
+
+# The Golden Flower
+B:36:0x86/0xB9
+
+# The Fountain
+B:37:0x86/0xBA
+
+# Axe Smith
+B:38:0x86/0xBB
+
+# Hafted Smith
+B:39:0x86/0xBC
+
+# Polearm Smith
+B:40:0x86/0xBD
+
+# Sword Smith
+B:41:0x86/0xBE
+
+# Rare Jewelry Shop
+B:42:0x86/0xBF
+
+# Jewelry Shop
+B:43:0x87/0xA0
+
+# Footwear Shop
+B:44:0x87/0xA1
+
+# Rare Footwear Shop
+B:45:0x87/0xA2
+
+# Library
+B:46:0x86/0xA3
+
+# Forbidden Library
+B:47:0x87/0xA3
+
+# Expensive Black Market
+B:48:0x87/0xA4
+
+# Common Shop
+B:49:0x87/0xA5
+
+# Dragon Hunter
+B:50:0x87/0xA6
+
+# Speed Ring Market
+B:51:0x87/0xA7
+
+# Scribe
+B:52:0x87/0xA8
+
+# Potion Store
+B:53:0x87/0xA9
+
+# Recaller
+B:54:0x87/0xAA
+
+# Master Archer
+B:55:0x87/0xAB
+
+# Merchants Guild
+B:56:0x87/0xAC
+
+# The Mathom-house
+B:57:0x87/0xAD
+
+# The Prancing Pony
+B:58:0x86/0xA1
+
+# nothing
+F:0:0x80/0x80
+
+# open floor
+F:1:0x80/0x81
+
+# fountain
+F:2:0xC3/0x9A
+
+# glyph of warding
+F:3:0x8D/0x95
+
+# open door
+F:4:0x82/0x84
+
+# broken door
+F:5:0x82/0x85
+
+# up staircase
+F:6:0x80/0x96
+
+# down staircase
+F:7:0x80/0x99
+
+# quest entrance
+F:8:0x80/0x9A
+
+# quest exit
+F:9:0x80/0x97
+
+# quest down level
+F:10:0x80/0x9B
+
+# quest up level
+F:11:0x80/0x98
+
+# town exit
+F:12:0x82/0x84
+
+# shaft down
+F:13:0xC3/0x84
+
+# shaft up
+F:14:0xC3/0x85
+
+# fountain
+F:15:0xC3/0x99
+
+# web
+F:16:0x81/0x8C
+
+# trap
+F:17:0x81/0x89
+
+# visible trap -- spiked pit
+F:18:0x81/0x89
+
+# visible trap -- poison pit
+F:19:0x81/0x89
+
+# visible trap -- rune -- summon
+F:20:0x81/0x8F
+
+# visible trap -- rune -- teleport
+F:21:0x81/0x92
+
+# visible trap -- spot -- fire
+F:22:0x81/0x86
+
+# visible trap -- spot -- acid
+F:23:0x81/0x86
+
+# visible trap -- dart -- slow
+F:24:0x81/0x80
+
+# visible trap -- dart -- lose str
+F:25:0x81/0x80
+
+# visible trap -- dart -- lose dex
+F:26:0x81/0x80
+
+# visible trap -- dart -- lose con
+F:27:0x81/0x80
+
+# visible trap -- gas -- blind
+F:28:0x81/0x83
+
+# visible trap -- gas -- confuse
+F:29:0x81/0x83
+
+# visible trap -- gas -- poison
+F:30:0x81/0x83
+
+# visible trap -- gas -- sleep
+F:31:0x81/0x83
+
+# door
+F:32:0x82/0x83
+
+# locked door
+F:33:0x82/0x83
+F:34:0x82/0x83
+F:35:0x82/0x83
+F:36:0x82/0x83
+F:37:0x82/0x83
+F:38:0x82/0x86
+F:39:0x82/0x86
+
+# jammed door
+F:40:0x82/0x83
+F:41:0x82/0x83
+F:42:0x82/0x83
+F:43:0x82/0x83
+F:44:0x82/0x83
+F:45:0x82/0x86
+F:46:0x82/0x86
+F:47:0x82/0x86
+
+# secret door
+F:48:0x80/0x84
+
+# pile of rubble
+F:49:0x80/0x9C
+
+# magma vein
+F:50:0x80/0x8D
+
+# quartz vein
+F:51:0x80/0x87
+
+# magma vein
+F:52:0x80/0x90
+
+# quartz vein
+F:53:0x80/0x87
+
+# magma vein with treasure
+F:54:0x80/0x90
+
+# quartz vein with treasure
+F:55:0x80/0x8A
+
+# granite wall
+F:56:0x80/0x84
+F:57:0x80/0x84
+F:58:0x80/0x84
+F:59:0x80/0x84
+
+# permanent wall
+F:60:0x80/0x93
+F:61:0x80/0x93
+F:62:0x80/0x93
+F:63:0x80/0x93
+
+# explosive rune
+F:64:0x8D/0x9E
+
+# Straight Road startpoint
+F:65:0x81/0x95
+
+# section of the Straight Road
+F:66:0x81/0x95
+F:67:0x81/0x95
+F:68:0x81/0x95
+F:69:0x81/0x95
+F:70:0x81/0x95
+
+# section of the Straight Road (discharged)
+F:71:0x81/0x98
+
+# Straight Road exit
+F:72:0x81/0x9B
+
+# corrupted section of the Straight Road
+F:73:0x81/0x9E
+
+# Building
+F:74:0x82/0x93
+
+# permanent wall
+F:75:0x82/0x93
+F:76:0x82/0x94
+F:77:0x82/0x95
+F:78:0x82/0x96
+
+# stream of shallow water
+F:84:0xB4/0x97
+
+# pool of deep lava
+F:85:0x83/0x8D
+
+# stream of shallow lava
+F:86:0xB4/0x9A
+
+# dark pit
+F:87:0x80/0x80
+
+# dirt
+F:88:0xB4/0x91
+
+# patch of grass
+F:89:0xB4/0x94
+
+# ice
+F:90:0xC3/0x83
+
+# sand
+F:91:0xC3/0x88
+
+# dead tree
+F:92:0xC3/0x98
+
+# ash
+F:93:0xC3/0x97
+
+# mud
+F:94:0xC3/0x96
+
+# ice wall
+F:95:0xC5/0x92
+
+# tree
+F:96:0x82/0x9A
+
+# mountain chain
+F:97:0x8D/0x98
+
+# sandwall
+F:98:0xC3/0x86
+F:99:0xC3/0x86
+
+# sandwall with treasure
+F:100:0xC3/0x87
+
+# high mountain chain
+F:101:0xC3/0x9E
+
+# nether mist
+F:102:0xC3/0x9F
+
+# Void Jumpgate
+F:160:0x91/0x84
+
+# Altar of Being
+F:161:0xC1/0x8E
+
+# Altar of Winds
+F:162:0xB5/0x8A
+
+# Altar of Force
+F:163:0xB5/0x86
+
+# Altar of Darkness
+F:164:0xB5/0x86
+
+# Altar of Nature
+F:165:0xB5/0x91
+
+# Altar of Sun
+F:166:0xB5/0x8F
+
+# Altar of Rage
+F:167:0xB5/0x8C
+
+# Altar of Winds
+F:168:0xB5/0x92
+
+# Altar of Stars
+F:169:0xC1/0x8F
+
+# Altar of Being
+F:170:0xB5/0x8D
+
+# Altar of Randomness
+F:171:0xB5/0x88
+
+# floor
+F:172:0x80/0x81
+
+# Underground Tunnel
+F:173:0x80/0x82
+
+# stream of tainted water
+F:174:0xAF/0x8E
+
+# monster trap
+F:175:0x81/0x9C
+
+# Void Jumpgate
+F:176:0xAF/0x8C
+
+# lava wall
+F:177:0xC6/0x8C
+
+# Great Fire
+F:178:0xC6/0x8A
+
+# path to the next area
+F:179:0x88/0xA1
+
+# path to the previous area
+F:180:0x88/0xA0
+
+# field
+F:181:0x88/0xA2
+
+# Ekkaia, the Encircling Sea
+F:182:0x88/0xA3
+
+# Altar of Energy
+F:183:0xB5/0x9A
+
+# Altar of Matter
+F:184:0xB5/0x9B
+
+# Altar of Being
+F:185:0xB5/0x9C
+
+# Altar of Unbeing
+F:186:0xB5/0x9D
+
+# pool of deep water
+F:187:0x83/0x80
+
+# glass wall
+F:188:0xC0/0x9F
+
+# illusion wall
+F:189:0x80/0x84
+
+# Grass roof
+F:190:0xC2/0x80
+
+# grass roof top
+F:191:0xC2/0x81
+
+# grass roof chimney
+F:192:0xC2/0x82
+
+# brick roof
+F:193:0xC3/0x80
+
+# brick roof top
+F:194:0xC3/0x81
+
+# brick roof chimney
+F:195:0xC3/0x82
+
+# window
+F:196:0xC2/0x83
+
+# small window
+F:197:0xC2/0x84
+
+# rain barrel
+F:198:0xC2/0x85
+
+# grass with flowers
+F:199:0xC2/0x86
+
+# cobblestone road
+F:200:0xC2/0x87
+
+# cobblestone with outlet
+F:201:0xC2/0x88
+
+# small tree
+F:202:0x82/0x9D
+
+# town
+F:203:0xC3/0x95
+
+# Underground Tunnel
+F:204:0x80/0x82
+
+# a blazing fire
+F:205:0xC6/0x8A
+
+# pile of rubble
+F:206:0xC6/0x8B
+
+# ethereal wall
+F:214:0x80/0x81
+
+# glacial wall
+F:215:0xC5/0x92
+
+# Skeleton
+G:M:1:0xC6/0x91
+
+# Zombie
+G:M:2:0xC6/0x92
+
+# Lich
+G:M:3:0xC6/0x93
+
+# Spectral
+G:M:4:0xC6/0x94
+
+# Captain
+G:M:5:0xC6/0x95
+
+# Chieftain
+G:M:6:0xC6/0x96
+
+# Shaman
+G:M:7:0xC6/0x97
+
+# Priest
+G:M:8:0xC6/0x98
+
+# Mage
+G:M:9:0xC6/0x99
+
+# Archer
+G:M:10:0xC6/0x9A
+
+# Rogue
+G:M:11:0xC6/0x9B
+
+# Vampire
+G:P:1:0x88/0xA4
+
+# Spectre
+G:P:2:0x88/0xA5
+
+# Skeleton
+G:P:3:0xC6/0x91
+
+# Zombie
+G:P:4:0xC6/0x92
+
+# Barbarian
+G:P:5:0x88/0xA6
+
+# Hermit
+G:P:6:0x88/0xA7
+
+# Corrupted
+G:P:7:0x88/0xA8
+
+# LostSoul
+G:P:8:0x88/0xA9
+
+# something
+K:0:0x01/0x20
+
+# Blindness
+K:1:0x85/0x94
+
+# Paranoia
+K:2:0x85/0x94
+
+# Confusion
+K:3:0x85/0x94
+
+# Hallucination
+K:4:0x85/0x94
+
+# Cure Poison
+K:5:0x85/0x94
+
+# Cure Blindness
+K:6:0x85/0x94
+
+# Cure Paranoia
+K:7:0xC6/0x83
+
+# Cure Confusion
+K:8:0x85/0x94
+
+# Weakness
+K:9:0x85/0x94
+
+# Unhealth
+K:10:0x85/0x94
+
+# Restore Constitution
+K:11:0x85/0x94
+
+# Restoring
+K:12:0x85/0x94
+
+# Stupidity
+K:13:0x85/0x94
+
+# Naivety
+K:14:0x85/0x94
+
+# Poison
+K:15:0x85/0x94
+
+# Sickness
+K:16:0x85/0x94
+
+# Paralysis
+K:17:0x85/0x94
+
+# Restore Strength
+K:18:0x85/0x94
+
+# Disease
+K:19:0x85/0x94
+
+# Cure Serious Wounds
+K:20:0x85/0x94
+
+# & Ration~ of Food
+K:21:0x8E/0x84
+
+# & Hard Biscuit~
+K:22:0x8E/0x82
+
+# & Strip~ of Venison
+K:23:0x8E/0x83
+
+# & Slime Mold~
+K:24:0x8E/0x85
+
+# & Lembas~
+K:25:0x8E/0x86
+
+# & Pint~ of Fine Ale
+K:26:0x8E/0x80
+
+# & Pint~ of Fine Wine
+K:27:0x8E/0x80
+
+# & Mattock~
+K:28:0xB6/0x8C
+
+# & Blue Stone~
+K:29:0xC5/0x93
+
+# & Broken Dagger~
+K:30:0x8A/0x8D
+
+# & Bastard Sword~
+K:31:0x8A/0x8E
+
+# & Scimitar~
+K:32:0x8A/0x97
+
+# & Tulwar~
+K:33:0x8A/0x95
+
+# & Broad Sword~
+K:34:0x8A/0x98
+
+# & Short Sword~
+K:35:0x8A/0x94
+
+# & Blade~ of Chaos
+K:36:0x8A/0x9E
+
+# & Two-Handed Sword~
+K:37:0x8A/0x9C
+
+# & Main Gauche~
+K:38:0x8A/0x90
+
+# & Cutlass~
+K:39:0x8A/0x96
+
+# & Executioner's Sword~
+K:40:0x8A/0x9D
+
+# & Katana~
+K:41:0x8A/0x9B
+
+# & Long Sword~
+K:42:0x8A/0x99
+
+# & Dagger~
+K:43:0x8A/0x8F
+
+# & Rapier~
+K:44:0x8A/0x91
+
+# & Sabre~
+K:45:0x8A/0x93
+
+# & Small Sword~
+K:46:0x8A/0x92
+
+# & Broken Sword~
+K:47:0x8A/0x8E
+
+# & Ball-and-Chain~
+K:48:0x8B/0x86
+
+# & Whip~
+K:49:0x8A/0x9F
+
+# & Flail~
+K:50:0x8B/0x83
+
+# & Two-Handed Flail~
+K:51:0x8B/0x87
+
+# & Morning Star~
+K:52:0x8B/0x84
+
+# & Mace~
+K:53:0x8B/0x81
+
+# & Quarterstaff~
+K:54:0x8B/0x82
+
+# & War Hammer~
+K:55:0x8B/0x80
+
+# & Lead-Filled Mace~
+K:56:0x8B/0x85
+
+# & Mace~ of Disruption
+K:57:0x8B/0x88
+
+# & Lucerne Hammer~
+K:58:0x8B/0x8D
+
+# & Beaked Axe~
+K:59:0x8B/0x90
+
+# & Glaive~
+K:60:0x8B/0x92
+
+# & Halberd~
+K:61:0x8B/0x93
+
+# & Awl-Pike~
+K:62:0x8B/0x8B
+
+# & Pike~
+K:63:0x8B/0x8F
+
+# & Spear~
+K:64:0x8B/0x89
+
+# & Trident~
+K:65:0x8B/0x8A
+
+# & Lance~
+K:66:0x8B/0x8C
+
+# & Great Axe~
+K:67:0x8B/0x95
+
+# & Battle Axe~
+K:68:0x8B/0x8E
+
+# & Lochaber Axe~
+K:69:0x8B/0x94
+
+# & Broad Axe~
+K:70:0x8B/0x91
+
+# & Scythe~
+K:71:0x8B/0x96
+
+# & Scythe~ of Slicing
+K:72:0x8B/0x97
+
+# & Short Bow~
+K:73:0x8B/0x98
+
+# & Long Bow~
+K:74:0x8B/0x99
+
+# & Light Crossbow~
+K:75:0x8B/0x9A
+
+# & Heavy Crossbow~
+K:76:0x8B/0x9B
+
+# & Sling~
+K:77:0x8B/0x9C
+
+# & Arrow~
+K:78:0x8C/0x80
+
+# & Seeker Arrow~
+K:79:0x8C/0x81
+
+# & Bolt~
+K:80:0x8C/0x82
+
+# & Seeker Bolt~
+K:81:0x8C/0x83
+
+# & Rounded Pebble~
+K:82:0x8C/0x84
+
+# & Iron Shot~
+K:83:0x8C/0x85
+
+# & Shovel~
+K:84:0x8E/0x8F
+
+# & Gnomish Shovel~
+K:85:0x8E/0x90
+
+# & Dwarven Shovel~
+K:86:0x8E/0x91
+
+# & Pick~
+K:87:0x8E/0x8C
+
+# & Orcish Pick~
+K:88:0x8E/0x8D
+
+# & Dwarven Pick~
+K:89:0x8E/0x91
+
+# & Elven Cloak~
+K:90:0x89/0x89
+
+# & Pair~ of Soft Leather Boots
+K:91:0x88/0x8E
+
+# & Pair~ of Hard Leather Boots
+K:92:0x88/0x8F
+
+# & Pair~ of Metal Shod Boots
+K:93:0x88/0x90
+
+# & Hard Leather Cap~
+K:94:0x87/0x98
+
+# & Metal Cap~
+K:95:0x87/0x99
+
+# & Iron Helm~
+K:96:0x87/0x9A
+
+# & Steel Helm~
+K:97:0x87/0x9B
+
+# & Iron Crown~
+K:98:0x87/0x9C
+
+# & Golden Crown~
+K:99:0x87/0x9D
+
+# & Jewel Encrusted Crown~
+K:100:0x87/0x9E
+
+# & Robe~
+K:101:0x89/0x8C
+
+# & Filthy Rag~
+K:102:0x89/0x8B
+
+# Soft Leather Armour~
+K:103:0x89/0x8D
+
+# Soft Studded Leather~
+K:104:0x89/0x8E
+
+# Hard Leather Armour~
+K:105:0x89/0x8F
+
+# Hard Studded Leather~
+K:106:0x89/0x90
+
+# Leather Scale Mail~
+K:107:0x89/0x91
+
+# Metal Scale Mail~
+K:108:0x89/0x92
+
+# Chain Mail~
+K:109:0x89/0x94
+
+# Rusty Chain Mail~
+K:110:0x89/0x93
+
+# Augmented Chain Mail~
+K:111:0x89/0x96
+
+# Bar Chain Mail~
+K:112:0x89/0x97
+
+# Metal Brigandine Armour~
+K:113:0x89/0x98
+
+# Partial Plate Armour~
+K:114:0x89/0x99
+
+# Metal Lamellar Armour~
+K:115:0x89/0x9A
+
+# Full Plate Armour~
+K:116:0x89/0x9B
+
+# Ribbed Plate Armour~
+K:117:0x89/0x9C
+
+# Adamantite Plate Mail~
+K:118:0x89/0x9F
+
+# Mithril Plate Mail~
+K:119:0x89/0x9E
+
+# Mithril Chain Mail~
+K:120:0x89/0x9D
+
+# Double Chain Mail~
+K:121:0x89/0x95
+
+# & Shield~ of Deflection
+K:122:0x88/0x98
+
+# & Cloak~
+K:123:0x89/0x88
+
+# & Shadow Cloak~
+K:124:0x89/0x89
+
+# & Set~ of Leather Gloves
+K:125:0x88/0x91
+
+# & Set~ of Gauntlets
+K:126:0x88/0x92
+
+# & Set~ of Cesti
+K:127:0x88/0x93
+
+# & Small Leather Shield~
+K:128:0x88/0x94
+
+# & Large Leather Shield~
+K:129:0x88/0x95
+
+# & Small Metal Shield~
+K:130:0x88/0x96
+
+# & Large Metal Shield~
+K:131:0x88/0x97
+
+# Strength
+K:132:0x84/0x81
+
+# Dexterity
+K:133:0x84/0x83
+
+# Constitution
+K:134:0x84/0x83
+
+# Intelligence
+K:135:0x84/0x83
+
+# Speed
+K:136:0x84/0x83
+
+# Searching
+K:137:0x84/0x83
+
+# Teleportation
+K:138:0x84/0x83
+
+# Slow Digestion
+K:139:0x84/0x83
+
+# Fire Resistance
+K:140:0x84/0x83
+
+# Cold Resistance
+K:141:0x84/0x83
+
+# Levitation
+K:142:0x84/0x83
+
+# Poison Resistance
+K:143:0x84/0x83
+
+# Free Action
+K:144:0x84/0x83
+
+# Weakness
+K:145:0x84/0x83
+
+# Flames
+K:146:0x84/0x83
+
+# Acid
+K:147:0x84/0x83
+
+# Ice
+K:148:0x84/0x83
+
+# Woe
+K:149:0x84/0x83
+
+# Stupidity
+K:150:0x84/0x83
+
+# Damage
+K:151:0x84/0x83
+
+# Accuracy
+K:152:0x84/0x83
+
+# Protection
+K:153:0x84/0x83
+
+# Aggravate Monster
+K:154:0x84/0x83
+
+# See Invisible
+K:155:0x84/0x83
+
+# Sustain Strength
+K:156:0x84/0x83
+
+# Sustain Intelligence
+K:157:0x84/0x83
+
+# Sustain Wisdom
+K:158:0x84/0x83
+
+# Sustain Constitution
+K:159:0x84/0x83
+
+# Sustain Dexterity
+K:160:0x84/0x83
+
+# Sustain Charisma
+K:161:0x84/0x83
+
+# Slaying
+K:162:0x84/0x83
+
+# Brilliance
+K:163:0x87/0x83
+
+# Charisma
+K:164:0x87/0x83
+
+# Searching
+K:165:0x87/0x83
+
+# Teleportation
+K:166:0x87/0x83
+
+# Slow Digestion
+K:167:0x87/0x83
+
+# Acid Resistance
+K:168:0x87/0x83
+
+# Adornment
+K:169:0x87/0x83
+
+# Double Ring Mail~
+K:170:0x89/0x9B
+
+# the Magi
+K:171:0x87/0x83
+
+# Doom
+K:172:0x87/0x83
+
+# Enchant Weapon To-Hit
+K:173:0x83/0x9C
+
+# Enchant Weapon To-Dam
+K:174:0x83/0x9C
+
+# Enchant Armor
+K:175:0x83/0x9C
+
+# Identify
+K:176:0x83/0x9C
+
+# *Identify*
+K:177:0x83/0x9C
+
+# Rumour
+K:178:0x83/0x9C
+
+# Chaos
+K:179:0x83/0x9C
+
+# Remove Curse
+K:180:0x83/0x9C
+
+# Light
+K:181:0x83/0x9C
+
+# Fire
+K:182:0x83/0x9C
+
+# Ice
+K:183:0x83/0x9C
+
+# Summon Monster
+K:184:0x83/0x9C
+
+# Phase Door
+K:185:0x83/0x9C
+
+# Teleportation
+K:186:0x83/0x9C
+
+# Teleport Level
+K:187:0x83/0x9C
+
+# Monster Confusion
+K:188:0x83/0x9C
+
+# Magic Mapping
+K:189:0x83/0x9C
+
+# Rune of Protection
+K:190:0x83/0x9C
+
+# *Remove Curse*
+K:191:0x83/0x9C
+
+# Treasure Detection
+K:192:0x83/0x9C
+
+# Object Detection
+K:193:0x83/0x9C
+
+# Trap Detection
+K:194:0x83/0x9C
+
+# & Sheaf Arrow~
+K:195:0x8C/0x81
+
+# & Mithril Shot~
+K:196:0x8C/0x85
+
+# Door
+K:197:0x83/0x9C
+
+# Acquirement
+K:198:0x83/0x9C
+
+# *Acquirement*
+K:199:0x83/0x9C
+
+# Mass Genocide
+K:200:0x83/0x9C
+
+# Detect Invisible
+K:201:0x83/0x9C
+
+# Aggravate Monster
+K:202:0x83/0x9C
+
+# Trap Creation
+K:203:0x83/0x9C
+
+# Trap
+K:204:0x83/0x9C
+
+# Artifact Creation
+K:205:0x83/0x9C
+
+# Recharging
+K:206:0x83/0x9C
+
+# Genocide
+K:207:0x83/0x9C
+
+# Darkness
+K:208:0x83/0x9C
+
+# Protection from Evil
+K:209:0x83/0x9C
+
+# Satisfy Hunger
+K:210:0x83/0x9C
+
+# Dispel Undead
+K:211:0x83/0x9C
+
+# *Enchant Weapon*
+K:212:0x83/0x9C
+
+# Curse Weapon
+K:213:0x83/0x9C
+
+# *Enchant Armor*
+K:214:0x83/0x9C
+
+# Curse Armor
+K:215:0x83/0x9C
+
+# Summon Undead
+K:216:0x83/0x9C
+
+# Blessing
+K:217:0x83/0x9C
+
+# Holy Chant
+K:218:0x83/0x9C
+
+# Holy Prayer
+K:219:0x83/0x9C
+
+# Word of Recall
+K:220:0x83/0x9C
+
+# *Destruction*
+K:221:0x83/0x9C
+
+# Slime Mold Juice
+K:222:0x85/0x85
+
+# Apple Juice
+K:223:0x85/0x85
+
+# Water
+K:224:0x85/0x85
+
+# Strength
+K:225:0x85/0x85
+
+# Weakness
+K:226:0x85/0x85
+
+# Restore Strength
+K:227:0x85/0x85
+
+# Intelligence
+K:228:0x85/0x85
+
+# Stupidity
+K:229:0x85/0x85
+
+# Restore Intelligence
+K:230:0x85/0x85
+
+# Wisdom
+K:231:0x85/0x85
+
+# Naivety
+K:232:0x85/0x85
+
+# Restore Wisdom
+K:233:0x85/0x85
+
+# Charisma
+K:234:0x85/0x85
+
+# Ugliness
+K:235:0x85/0x85
+
+# Restore Charisma
+K:236:0x85/0x85
+
+# Curing
+K:237:0x85/0x85
+
+# Invulnerability
+K:238:0x85/0x85
+
+# New Life
+K:239:0x85/0x85
+
+# Cure Serious Wounds
+K:240:0x85/0x85
+
+# Cure Critical Wounds
+K:241:0x85/0x85
+
+# Healing
+K:242:0x85/0x85
+
+# Constitution
+K:243:0x85/0x85
+
+# Experience
+K:244:0x85/0x85
+
+# Sleep
+K:245:0x85/0x85
+
+# Blindness
+K:246:0x85/0x85
+
+# Booze
+K:247:0x85/0x85
+
+# Poison
+K:248:0x85/0x85
+
+# Speed
+K:249:0x85/0x85
+
+# Slowness
+K:250:0x85/0x85
+
+# Dexterity
+K:251:0x85/0x85
+
+# Restore Dexterity
+K:252:0x85/0x85
+
+# Restore Constitution
+K:253:0x85/0x85
+
+# Lose Memories
+K:254:0x85/0x85
+
+# Salt Water
+K:255:0x85/0x85
+
+# Enlightenment
+K:256:0x85/0x85
+
+# Heroism
+K:257:0x85/0x85
+
+# Berserk Strength
+K:258:0x85/0x85
+
+# Boldness
+K:259:0x85/0x85
+
+# Restore Life Levels
+K:260:0x85/0x85
+
+# Resist Heat
+K:261:0x85/0x85
+
+# Resist Cold
+K:262:0x85/0x85
+
+# Detect Invisible
+K:263:0x85/0x85
+
+# Slow Poison
+K:264:0x85/0x85
+
+# Neutralise Poison
+K:265:0x85/0x85
+
+# Restore Mana
+K:266:0x85/0x85
+
+# Infra-vision
+K:267:0x85/0x85
+
+# Resistance
+K:268:0x85/0x85
+
+# Spell
+K:269:0x86/0x93
+
+# Manathrust
+K:270:0x86/0x93
+
+# Fireflash
+K:271:0x86/0x93
+
+# Firewall
+K:272:0x86/0x93
+
+# Tidal Wave
+K:273:0x86/0x93
+
+# Ice Storm
+K:274:0x86/0x93
+
+# Noxious Cloud
+K:275:0x86/0x93
+
+# Poison Blood
+K:276:0x86/0x93
+
+# Thunderstorm
+K:277:0x86/0x93
+
+# Dig
+K:278:0x86/0x93
+
+# Stone Prison
+K:279:0x86/0x93
+
+# Strike
+K:280:0x86/0x93
+
+# Teleport Away
+K:281:0x86/0x93
+
+# Summon Animal
+K:282:0x86/0x93
+
+# Magelock
+K:283:0x86/0x93
+
+# Slow Monster
+K:284:0x86/0x93
+
+# Essence of Speed
+K:285:0xB7/0x8C
+
+# Banishment
+K:286:0x86/0x93
+
+# Disperse Magic
+K:287:0x86/0x93
+
+# Charm
+K:288:0x86/0x93
+
+# Confuse
+K:289:0x86/0x93
+
+# Demon Blade
+K:290:0x86/0x93
+
+# Heal Monster
+K:291:0x86/0x93
+
+# Haste Monster
+K:292:0x86/0x93
+
+# & Flight Arrow~
+K:293:0x8C/0x81
+
+# Acid Bolts
+K:294:0x86/0x93
+
+# Dragon's Flame
+K:295:0x86/0x93
+
+# Dragon's Frost
+K:296:0x86/0x93
+
+# Dragon's Breath
+K:297:0x86/0x93
+
+# Annihilation
+K:298:0x86/0x93
+
+# Rockets
+K:299:0x86/0x93
+
+# Spell
+K:300:0x87/0x92
+
+# Nothing
+K:301:0x87/0x92
+
+# Globe of Light
+K:302:0x87/0x92
+
+# Fiery Shield
+K:303:0x87/0x92
+
+# Remove Curses
+K:304:0x87/0x92
+
+# Wings of Winds
+K:305:0x87/0x92
+
+# Shake
+K:306:0x87/0x92
+
+# Disarm
+K:307:0x87/0x92
+
+# Teleportation
+K:308:0x87/0x92
+
+# Probability Travel
+K:309:0x87/0x92
+
+# Recovery
+K:310:0x87/0x92
+
+# Healing
+K:311:0x87/0x92
+
+# Vision
+K:312:0x87/0x92
+
+# Identify
+K:313:0x87/0x92
+
+# Sense Hidden
+K:314:0x87/0x92
+
+# Reveal Ways
+K:315:0x87/0x92
+
+# Sense Monsters
+K:316:0x87/0x92
+
+# Genocide
+K:317:0x87/0x92
+
+# Summon
+K:318:0x87/0x92
+
+# Curing
+K:319:0x87/0x92
+
+# Wish
+K:320:0x87/0x92
+
+# Mana
+K:321:0x87/0x92
+
+# Darkness
+K:322:0x87/0x92
+
+# Genocide
+K:323:0x87/0x92
+
+# Power
+K:324:0x87/0x92
+
+# the Magi
+K:325:0x87/0x92
+
+# Perception
+K:326:0x87/0x92
+
+# Holiness
+K:327:0x87/0x92
+
+# Enlightenment
+K:328:0x87/0x92
+
+# Healing
+K:329:0x87/0x92
+
+# & Tome~ of Magical Energy
+K:330:0x90/0xA0
+
+# & Tome~ of the Eternal Flame
+K:331:0x90/0xA1
+
+# & Tome~ of the Blowing Wind
+K:332:0x90/0xA2
+
+# & Tome~ of the Impenetrable Earth
+K:333:0x90/0xA3
+
+# & Tome~ of the Everrunning Wave
+K:334:0x90/0xA4
+
+# & Tome~ of Translocation
+K:335:0x90/0xA5
+
+# & Tome~ of the Tree
+K:336:0x90/0xA6
+
+# & Tome~ of Knowledge
+K:337:0x90/0xA7
+
+# & Small wooden chest~
+K:338:0x84/0x99
+
+# & Large wooden chest~
+K:339:0x84/0x9A
+
+# & Small iron chest~
+K:340:0x84/0x9B
+
+# & Large iron chest~
+K:341:0x84/0x9C
+
+# & Small steel chest~
+K:342:0x84/0x9D
+
+# & Large steel chest~
+K:343:0x84/0x9E
+
+# & Ruined chest~
+K:344:0x84/0x9F
+
+# & Iron Spike~
+K:345:0x8E/0x89
+
+# & Wooden Torch~
+K:346:0x8E/0x8B
+
+# & Brass Lantern~
+K:347:0x8E/0x8A
+
+# & Flask~ of oil
+K:348:0x8E/0x88
+
+# & Empty Bottle~
+K:349:0x8E/0x87
+
+# Havoc
+K:350:0x86/0x83
+
+# Door
+K:351:0x86/0x83
+
+# Trap Location
+K:352:0x86/0x83
+
+# Probing
+K:353:0x86/0x83
+
+# Recall
+K:354:0x86/0x83
+
+# Illumination
+K:355:0x86/0x83
+
+# Light
+K:356:0x86/0x83
+
+# Lightning Bolts
+K:357:0x86/0x83
+
+# Frost Bolts
+K:358:0x86/0x83
+
+# Fire Bolts
+K:359:0x86/0x83
+
+# Polymorph
+K:360:0x86/0x83
+
+# Slow Monster
+K:361:0x86/0x83
+
+# Sleep Monster
+K:362:0x86/0x83
+
+# Drain Life
+K:363:0x86/0x83
+
+# Teleport Other
+K:364:0x86/0x83
+
+# Disarming
+K:365:0x86/0x83
+
+# Lightning Balls
+K:366:0x86/0x83
+
+# Cold Balls
+K:367:0x86/0x83
+
+# Fire Balls
+K:368:0x86/0x83
+
+# Acid Balls
+K:369:0x86/0x83
+
+# Acid Bolts
+K:370:0x86/0x83
+
+# Enlightenment
+K:371:0x86/0x83
+
+# Perception
+K:372:0x86/0x83
+
+# Curing
+K:373:0x86/0x83
+
+# Healing
+K:374:0x86/0x83
+
+# Detection
+K:375:0x86/0x83
+
+# Restoration
+K:376:0x86/0x83
+
+# Speed
+K:377:0x86/0x83
+
+# Spell
+K:378:0xC1/0x84
+K:379:0x8D/0x80
+
+# [Beings of Darkness]
+K:380:0x8D/0x81
+
+# [Material Shadow]
+K:381:0x8D/0x82
+
+# [Nature's Wrath]
+K:382:0x8D/0x83
+
+# [Sign of Chaos]
+K:383:0x8C/0x98
+
+# [Chaos Mastery]
+K:384:0x8C/0x99
+
+# [Chaos Channels]
+K:385:0x8C/0x9A
+
+# [Armageddon Tome]
+K:386:0x8C/0x9B
+
+# [Nether Openings]
+K:387:0x8D/0x88
+
+# [Unholy Blessings]
+K:388:0x8D/0x89
+
+# & Firestone~
+K:389:0x8E/0x92
+
+# & Small Firestone~
+K:390:0x8E/0x93
+
+# & Broken Skull~
+K:391:0x8E/0x94
+
+# & Broken Bone~
+K:392:0x8E/0x95
+
+# & Canine Skeleton~
+K:393:0x8E/0x9A
+
+# & Rodent Skeleton~
+K:394:0x8E/0x9B
+
+# & Human Skeleton~
+K:395:0x8E/0x96
+
+# & Dwarf Skeleton~
+K:396:0x8E/0x98
+
+# & Elf Skeleton~
+K:397:0x8E/0x97
+
+# & Gnome Skeleton~
+K:398:0x8E/0x99
+
+# & Great Hammer~
+K:399:0xB6/0x8A
+
+# Black Dragon Scale Mail~
+K:400:0x8A/0x82
+
+# Blue Dragon Scale Mail~
+K:401:0x8A/0x80
+
+# White Dragon Scale Mail~
+K:402:0x8A/0x81
+
+# Red Dragon Scale Mail~
+K:403:0x8A/0x83
+
+# Green Dragon Scale Mail~
+K:404:0x8A/0x84
+
+# Multi-Hued Dragon Scale Mail~
+K:405:0x8A/0x8B
+
+# Pseudo Dragon Scale Mail~
+K:406:0x8A/0x87
+
+# Law Dragon Scale Mail~
+K:407:0x8A/0x89
+
+# Bronze Dragon Scale Mail~
+K:408:0x8A/0x85
+
+# Gold Dragon Scale Mail~
+K:409:0x8A/0x86
+
+# Chaos Dragon Scale Mail~
+K:410:0x8A/0x88
+
+# Balance Dragon Scale Mail~
+K:411:0x8A/0x8A
+
+# Power Dragon Scale Mail~
+K:412:0x8A/0x8C
+
+# & Dragon Helm~
+K:413:0x88/0x82
+
+# & Dragon Shield~
+K:414:0x88/0x9C
+
+# Death
+K:415:0x85/0x85
+
+# Ruination
+K:416:0x85/0x85
+
+# Detonations
+K:417:0x85/0x85
+
+# Augmentation
+K:418:0x85/0x85
+
+# *Healing*
+K:419:0x85/0x85
+
+# Life
+K:420:0x85/0x85
+
+# Self Knowledge
+K:421:0x85/0x85
+
+# *Enlightenment*
+K:422:0x85/0x85
+
+# [Necromantic Incantations]
+K:423:0x8D/0x8A
+
+# [Curses of Angmar]
+K:424:0x8D/0x8B
+
+# Fear Resistance
+K:425:0x84/0x83
+
+# Light and Darkness Resistance
+K:426:0x84/0x83
+
+# Nether Resistance
+K:427:0x84/0x83
+
+# Nexus Resistance
+K:428:0x84/0x83
+
+# Sound Resistance
+K:429:0x84/0x83
+
+# Confusion Resistance
+K:430:0x84/0x83
+
+# Shard Resistance
+K:431:0x84/0x83
+
+# Disenchantment Resistance
+K:432:0x84/0x83
+
+# Chaos Resistance
+K:433:0x84/0x83
+
+# Blindness Resistance
+K:434:0x84/0x83
+
+# Lordly Protection
+K:435:0x84/0x83
+
+# Extra Attacks
+K:436:0x84/0x83
+
+# Cure Light Wounds
+K:437:0x85/0x85
+
+# Clumsiness
+K:438:0x85/0x85
+
+# Sickliness
+K:439:0x85/0x85
+
+# Map of Bree
+K:440:0xC4/0x80
+
+# Map of Gondolin
+K:441:0xC4/0x80
+
+# Map of Lothlorien
+K:442:0xC4/0x80
+
+# Map of Minas Anor
+K:443:0xC4/0x80
+
+# & Silver Arrow~
+K:465:0xC6/0x81
+
+# & Silver Bolt~
+K:466:0xC6/0x82
+
+# Lightning Resistance
+K:467:0x87/0x80
+
+# Wisdom
+K:468:0x87/0x80
+
+# Regeneration
+K:469:0x87/0x80
+
+# Infravision
+K:470:0x87/0x80
+
+# Devotion
+K:471:0x87/0x80
+
+# Weaponmastery
+K:472:0x87/0x80
+
+# Trickery
+K:473:0x87/0x80
+
+# Telepathy
+K:474:0x87/0x80
+
+# Sustenance
+K:475:0x87/0x80
+
+# & Palantir~
+K:476:0xC6/0x87
+
+# & Elfstone~
+K:477:0xC6/0x83
+
+# & Jewel~
+K:478:0xC6/0x84
+
+# & Ring~
+K:479:0xC6/0x85
+
+# copper
+K:480:0x83/0x91
+K:481:0x83/0x91
+K:482:0x83/0x91
+
+# silver
+K:483:0x83/0x92
+K:484:0x83/0x92
+K:485:0x83/0x92
+
+# garnets
+K:486:0x83/0x96
+K:487:0x83/0x96
+
+# gold
+K:488:0x83/0x93
+K:489:0x83/0x93
+K:490:0x83/0x93
+
+# opals
+K:491:0x83/0x97
+
+# sapphires
+K:492:0x83/0x98
+
+# rubies
+K:493:0x83/0x99
+
+# diamonds
+K:494:0x83/0x9A
+
+# emeralds
+K:495:0x83/0x9B
+
+# mithril
+K:496:0x83/0x94
+
+# adamantite
+K:497:0x83/0x95
+
+# & Mighty Hammer~
+K:498:0xB6/0x8A
+
+# & Massive Iron Crown~
+K:499:0x87/0x9C
+
+# & Phial~
+K:500:0x8E/0x9D
+
+# & Star~
+K:501:0x8E/0x9E
+
+# & Arkenstone~
+K:502:0x8E/0x9F
+
+# & Amulet~
+K:503:0x84/0x96
+K:504:0x84/0x97
+
+# & Necklace~
+K:505:0x84/0x98
+
+# & Ring~
+K:506:0x84/0x8F
+K:507:0x84/0x90
+K:508:0x84/0x92
+K:509:0x84/0x93
+K:510:0x84/0x94
+K:511:0x84/0x95
+
+# [Rites of Initiation]
+K:512:0x8D/0x90
+
+# [Ways of War]
+K:513:0x8D/0x91
+
+# [Divine Retribution]
+K:514:0x8D/0x92
+
+# [Essence of Fury]
+K:515:0x8D/0x93
+
+# [Novice Crafts]
+K:516:0x8D/0x8C
+
+# [Arcane Channels]
+K:517:0x8D/0x8D
+
+# [Sigils of Wizardry]
+K:518:0x8D/0x8E
+
+# [Mana Focus]
+K:519:0x8D/0x8F
+
+# Reflection
+K:520:0x87/0x83
+
+# Anti-Magic
+K:521:0x87/0x83
+
+# Anti-Teleportation
+K:522:0x87/0x83
+
+# Resistance
+K:523:0x87/0x83
+
+# & Zweihander~
+K:524:0xB6/0x8C
+
+# & Dwarven Lantern~
+K:525:0xC5/0x94
+
+# Splint Mail~
+K:526:0x89/0x9C
+
+# & Everburning Torch~
+K:527:0xC5/0x95
+
+# & Trifurcate Spear~
+K:528:0xB6/0x85
+
+# & Three Piece Rod~
+K:529:0xB6/0x80
+
+# & Feanorian Lamp~
+K:530:0xC5/0x96
+
+# & Fur Cloak~
+K:531:0x89/0x89
+
+# Water Curing
+K:532:0xB6/0x86
+
+# & Hatchet~
+K:533:0xB6/0x8F
+
+# Rhino Hide Armour~
+K:535:0x89/0x98
+
+# Leather Jacket~
+K:536:0x89/0x8F
+
+# & Sickle~
+K:537:0xB6/0x90
+
+# [Psychoportation]
+K:538:0xB6/0x87
+
+# [Clairsentience]
+K:539:0xB6/0x91
+
+# [Telekinesis]
+K:540:0xB6/0x93
+
+# [Empathy]
+K:541:0xB6/0x92
+
+# & Club~
+K:542:0xB6/0x92
+
+# & Broad Spear~
+K:543:0xB6/0x84
+
+# & Khopesh~
+K:544:0xB6/0x94
+
+# & Flamberge~
+K:545:0xB6/0x83
+
+# & Claymore~
+K:546:0xB6/0x8D
+
+# & Espadon~
+K:547:0xB6/0x8E
+
+# & Great Scimitar~
+K:548:0xB6/0x8B
+
+# Arrow
+K:549:0x8E/0xA0
+
+# Bolt
+K:550:0x8E/0xA1
+
+# & Fauchard~
+K:551:0xB6/0x95
+
+# & Guisarme~
+K:552:0xB6/0x96
+
+# & Heavy Lance~
+K:553:0xB6/0x82
+
+# & Basillard~
+K:554:0xB6/0x99
+
+# Catapult
+K:555:0x8E/0xA2
+
+# Ring Mail~
+K:556:0x89/0x9C
+
+# Cord Armour~
+K:557:0x89/0x90
+
+# Paper Armour~
+K:558:0x8A/0x81
+
+# Padded Armour~
+K:559:0x89/0x91
+
+# Fumes
+K:560:0x8E/0xA3
+
+# Stone and Hide Armour~
+K:561:0x89/0x97
+
+# Magic
+K:562:0x8E/0xA4
+
+# Device
+K:563:0x8E/0xA5
+
+# Nothing
+K:564:0xC6/0x9C
+
+# Poison
+K:565:0xB7/0x80
+
+# Nothing
+K:566:0xC6/0x9C
+K:567:0xC6/0x9C
+K:568:0xC6/0x9C
+K:569:0xC6/0x9C
+
+# Explosion
+K:570:0xB7/0x81
+
+# Teleport
+K:571:0xB7/0x82
+
+# Nothing
+K:572:0xC6/0x9C
+
+# & Blood~ of Life
+K:573:0x85/0x85
+
+# Cold
+K:574:0xB7/0x83
+
+# Fire
+K:575:0xB7/0x84
+
+# Acid
+K:576:0xB7/0x85
+
+# & Mage Staff~
+K:577:0xB8/0x80
+
+# Lightning
+K:578:0x84/0x80
+
+# Life
+K:579:0xB7/0x86
+
+# Confusion
+K:580:0xB7/0x87
+
+# Light
+K:581:0xB7/0x88
+
+# & Ring~
+K:582:0x84/0x85
+
+# Invisibility
+K:583:0x85/0x85
+
+# Chaos
+K:584:0xB7/0x89
+
+# Corruption
+K:585:0x85/0x85
+
+# Invisibility
+K:586:0x85/0x85
+
+# Time
+K:587:0xB7/0x8A
+
+# Deep Thoughts
+K:588:0x83/0x9C
+
+# More Deep Thoughts
+K:589:0x83/0x9D
+
+# Compendium of Deep Thoughts
+K:590:0x83/0x9E
+
+# Artifact Lore Vol. I
+K:591:0x83/0x9C
+
+# Artifact Lore Vol. II
+K:592:0x83/0x9D
+
+# Artifact Lore Vol. III
+K:593:0x83/0x9F
+
+# Monstrous Compendium 1
+K:594:0x83/0x9F
+
+# Monstrous Compendium 2
+K:595:0x83/0x9E
+
+# Monstrous Compendium 3
+K:596:0x83/0x9D
+
+# Monstrous Compendium 4
+K:597:0x83/0x9C
+
+# Monstrous Compendium 5
+K:598:0x83/0x9F
+
+# Monstrous Compendium 6
+K:599:0x83/0x9E
+
+# Monstrous Compendium 7
+K:600:0x83/0x9D
+
+# Monstrous Compendium 8
+K:601:0x83/0x9C
+
+# Monstrous Compendium 9
+K:602:0x83/0x9D
+
+# Monstrous Compendium 10
+K:603:0x83/0x9E
+
+# Monstrous Compendium 11
+K:604:0x83/0x9F
+
+# Abomination
+K:605:0x85/0x85
+
+# Shape of Wolf
+K:606:0x85/0x85
+
+# Shape of Ape
+K:607:0x85/0x85
+
+# Shape of Goat
+K:608:0x85/0x85
+
+# Shape of Insect
+K:609:0x85/0x85
+
+# Shape of Sparrow
+K:610:0x85/0x85
+
+# Shape of Ent
+K:611:0x85/0x85
+
+# Shape of Vampire
+K:612:0x85/0x85
+
+# Shape of Spider
+K:613:0x85/0x85
+
+# Shape of Mana ball
+K:614:0x85/0x85
+
+# Shape of Fire cloud
+K:615:0x85/0x85
+
+# Shape of Cold cloud
+K:616:0x85/0x85
+
+# Shape of Chaos cloud
+K:617:0x85/0x85
+
+# [Wolf]
+K:618:0x8F/0xA0
+
+# [Ape]
+K:619:0x8F/0xA1
+
+# [Goat]
+K:620:0x8F/0xA2
+
+# [Insect]
+K:621:0x8F/0xA3
+
+# [Sparrow]
+K:622:0x8F/0xA4
+
+# [Ent]
+K:623:0x8F/0xA5
+
+# [Vampire]
+K:624:0x8F/0xA6
+
+# [Spider]
+K:625:0x8F/0xA7
+
+# [Mana ball]
+K:626:0x8F/0xA8
+
+# [Fire cloud]
+K:627:0x8F/0xA9
+
+# [Cold cloud]
+K:628:0x8F/0xAA
+
+# [Chaos Cloud]
+K:629:0x8F/0xAB
+
+# [Ghost]
+K:630:0x8F/0xAC
+
+# [Kobold]
+K:631:0x8F/0xAD
+
+# [Dragon]
+K:632:0x8F/0xAE
+
+# [Demon]
+K:633:0x8F/0xAF
+
+# [Hound]
+K:634:0x8F/0xB0
+
+# [Quylthulg]
+K:635:0x8F/0xB1
+
+# [Maia]
+K:636:0x8F/0xB2
+
+# [Serpent]
+K:637:0x8F/0xB3
+
+# [Giant]
+K:638:0x8F/0xB4
+
+# [Vala]
+K:639:0x8F/0xB5
+
+# Magic
+K:640:0xB7/0x8B
+
+# corpse
+K:641:0xB8/0x81
+
+# skeleton
+K:642:0x8E/0x96
+
+# head
+K:643:0x8E/0x94
+
+# skull
+K:644:0x8E/0x94
+
+# raw meat
+K:645:0x8E/0x83
+
+# & Thunderlord Coat~
+K:646:0x8A/0x86
+
+# & Stone~
+K:647:0x8E/0x9C
+
+# & small wooden Boomerang~
+K:648:0xB8/0x82
+
+# & wooden Boomerang~
+K:649:0xB8/0x83
+
+# & small metal Boomerang~
+K:650:0xB8/0x84
+
+# & metal Boomerang~
+K:651:0xB8/0x85
+
+# & Anchor~
+K:652:0x8D/0x9E
+
+# & ~
+K:653:0xC6/0x9C
+
+# Summon never-moving pet
+K:654:0x83/0x9D
+
+# [Life in symbiosis]
+K:655:0xB8/0x86
+
+# [Perfect Symbiosis]
+K:656:0xB8/0x86
+
+# Cure Light Insanity
+K:657:0x85/0x85
+
+# Cure Serious Insanity
+K:658:0x85/0x85
+
+# Cure Critical Insanity
+K:659:0x85/0x85
+
+# Cure Insanity
+K:660:0x85/0x85
+
+# & Phial~
+K:661:0x8E/0x9D
+
+# Random Artifact
+K:662:0xC6/0x9C
+
+# Craftmanship
+K:663:0x83/0x9F
+
+# The One Ring
+K:664:0x83/0x9E
+
+# & Book~ of the Lays of the Heroes
+K:665:0xB8/0x87
+
+# & Book~ of Sound Patterns
+K:666:0xB8/0x87
+
+# [Harps of Rivendell]
+K:667:0xB8/0x87
+
+# [Lays of Beleriand]
+K:668:0xB8/0x87
+
+# & Flute~
+K:669:0xB8/0x88
+
+# & Drum~
+K:670:0xB8/0x89
+
+# & Harp~
+K:671:0xB8/0x8A
+
+# & Banjo~
+K:672:0xB8/0x8C
+
+# & Lute~
+K:673:0xB8/0x8B
+
+# & Mandolin~
+K:674:0xB8/0x8B
+
+# & Palantir~
+K:675:0x8D/0x9F
+
+# Egg
+K:676:0xB7/0x8D
+
+# Reset Recall
+K:677:0x83/0x9D
+
+# Divination
+K:678:0x83/0x9D
+
+# Self
+K:679:0xB7/0x8E
+
+# Ray
+K:680:0xB7/0x8F
+
+# Sphere
+K:681:0xB7/0x90
+
+# Knowledge
+K:682:0xB7/0x94
+
+# Life
+K:683:0xB7/0x95
+
+# Fire
+K:684:0xB7/0x96
+
+# Cold
+K:685:0xB7/0x97
+
+# Lightning
+K:686:0xB7/0x98
+
+# Acid
+K:687:0xB7/0x99
+
+# Element
+K:688:0xB7/0x9A
+
+# Chaos
+K:689:0xB7/0x9B
+
+# Mind
+K:690:0xB7/0x9C
+
+# Holding
+K:691:0xB7/0x9D
+
+# Arrow
+K:692:0xB7/0x91
+
+# Power Surge
+K:693:0xB7/0x92
+
+# Armageddon
+K:694:0xB7/0x93
+
+# Gravity
+K:695:0xB7/0x9E
+
+# Extra Life
+K:696:0xB7/0x9F
+
+# Undeath
+K:697:0xB6/0x9B
+
+# Protection
+K:698:0xB6/0x9C
+
+# & Horn~
+K:699:0xB8/0x8D
+
+# & Ring~ of Precognition
+K:700:0x84/0x83
+
+# & Sprig~ of Athelas
+K:701:0xB8/0x8E
+
+# [Magic for Beginners]
+K:702:0xB8/0x8F
+
+# [Conjurings and Tricks]
+K:703:0xB8/0x8F
+
+# [Incantations and Illusions]
+K:704:0xB8/0x8F
+
+# [Sorcery and Evocations]
+K:705:0xB8/0x8F
+
+# [Beginners Handbook]
+K:706:0xB8/0x90
+
+# [Words of Wisdom]
+K:707:0xB8/0x90
+
+# [Chants and Blessings]
+K:708:0xB8/0x90
+
+# [Exorcism and Dispelling]
+K:709:0xB8/0x90
+
+# [Resistance of Scarabtarices]
+K:710:0xB8/0x92
+
+# [Mordenkainen's Escapes]
+K:711:0xB8/0x92
+
+# [Kelek's Grimoire of Power]
+K:712:0xB8/0x92
+
+# [Tenser's Transformations]
+K:713:0xB8/0x92
+
+# [Raal's Tome of Destruction]
+K:714:0xB8/0x92
+
+# [Ethereal Openings]
+K:715:0xB8/0x92
+
+# [Godly Insights]
+K:716:0xB8/0x91
+
+# [Purifications and Healing]
+K:717:0xB8/0x91
+
+# [Holy Infusions]
+K:718:0xB8/0x91
+
+# [Wrath of God]
+K:719:0xB8/0x91
+
+# & Old Scroll~ of Deincarnation
+K:720:0x83/0x9F
+
+# & Dark Sword~
+K:721:0xC4/0x81
+
+# Numenorean for beginners (I)
+K:722:0xC1/0x80
+
+# Numenorean for beginners (II)
+K:723:0xC1/0x81
+
+# Advanced lessons of Numenorean
+K:724:0xC1/0x80
+
+# Advanced lessons of Sindarin
+K:725:0xC1/0x81
+
+# & Shard~ of Pottery
+K:726:0x8E/0x92
+
+# & Broken Stick~
+K:727:0x8E/0x93
+
+# Wall Creation
+K:728:0x83/0x9F
+
+# [Illusions for Beginners]
+K:729:0xC1/0x82
+
+# [Tricks and Visions]
+K:730:0xC1/0x82
+
+# [Phantasms and Illusions]
+K:731:0xC1/0x82
+
+# [Shadows and Prisms]
+K:732:0xC1/0x82
+
+# [Serten's Immunities]
+K:733:0xC1/0x83
+
+# [Knowledge of Kenault]
+K:734:0xC1/0x83
+
+# [Otiluke's Spheres]
+K:735:0xC1/0x82
+
+# [Boccob's Book of Shadows]
+K:736:0xC1/0x84
+
+# [Bigby's Handbook]
+K:737:0xC1/0x84
+
+# & Book~ of Beginner Cantrips
+K:738:0xC1/0x85
+
+# & Book~ of Teleportation
+K:739:0xC1/0x86
+
+# & Book~ of Recall
+K:740:0xC1/0x87
+
+# & Book~ of Summoning
+K:741:0xC1/0x80
+
+# & Book~ of Fireflash
+K:742:0xC1/0x81
+
+# & Potion~ of Learning
+K:743:0xC1/0x82
+
+# [Eye of Sauron]
+K:744:0xC1/0x83
+
+# [Flame of Udun]
+K:745:0xC1/0x84
+
+# [Corruptions of Melkor]
+K:746:0xC1/0x85
+
+# [Crescent of Morgul]
+K:747:0xC1/0x86
+
+# [Morgoth's Ring]
+K:748:0xC1/0x87
+
+# Spell
+K:749:0x86/0x90
+
+# Wishing
+K:750:0x86/0x90
+
+# Khuzdul - The hidden tongue of the Dwarves
+K:751:0x83/0x9D
+
+# Nandorin for dummies
+K:752:0xC1/0x81
+
+# Advanced lessons of Orcish
+K:753:0xC1/0x82
+
+# & Ancient Tome~
+K:754:0xC1/0x86
+
+# Flying
+K:755:0x84/0x88
+
+# & Tome~ of the Time
+K:756:0xC1/0x80
+
+# & Tome~ of Meta Spells
+K:758:0xC1/0x81
+
+# & Tome~ of the Mind
+K:759:0xC1/0x82
+
+# & Holy Tome~ of Eru Iluvatar
+K:760:0xC1/0x83
+
+# & Holy Tome~ of Manwe Sulimo
+K:761:0xC1/0x84
+
+# & War Tome~ of Tulkas
+K:762:0xC1/0x85
+
+# & Unholy Tome~ of the Hellflame
+K:763:0xC1/0x86
+
+# & Corrupted Tome~ of Melkor
+K:764:0xC1/0x87
+
+# [Aiding Shades]
+K:765:0xC1/0x80
+
+# [Morgoth's Space-Time Warpings]
+K:766:0xC1/0x81
+
+# [Murazor's Tome of Conjuring & Dispelling]
+K:767:0xC1/0x82
+
+# & Forest Tome~ of Yavanna
+K:768:0xC1/0x83
+
+# [Sauron's Forgotten Tome]
+K:769:0xC1/0x87
+
+# & Ring~
+K:770:0x84/0x84
+
+# [Earth]
+K:771:0xC1/0x88
+
+# [Fire]
+K:772:0xC1/0x89
+
+# [Air]
+K:773:0xC1/0x8A
+
+# [Water]
+K:774:0xC1/0x8B
+
+# [Mana]
+K:775:0xC1/0x8C
+
+# Home Summoning
+K:776:0x83/0x9F
+
+# & Shadow Blade~
+K:777:0xC1/0x91
+
+# & Bluesteel Blade~
+K:778:0xC1/0x92
+
+# the Serpents
+K:779:0xC4/0x88
+
+# Darkness
+K:780:0xC4/0x89
+
+# Knowledge
+K:781:0xC4/0x8A
+
+# Force
+K:782:0xC4/0x8B
+
+# Lightning
+K:783:0xC4/0x8C
+
+# Mana
+K:784:0xC4/0x8D
+
+# Ring~ of Power
+K:785:0xC4/0x8E
+
+# Climbing Set~
+K:786:0xC1/0x93
+
+# Adventurer's guide to Middle-earth
+K:787:0x83/0x9E
+
+# & Demonblade~
+K:788:0x90/0xA8
+
+# & Demonshield~
+K:789:0x90/0xA9
+
+# & Demonhorn~
+K:790:0x90/0xAA
+
+# [Demonthoughts]
+K:791:0xC1/0x83
+
+# [Hellfire Tome]
+K:792:0xC1/0x94
+
+# & Wooden Rod~ of#
+K:793:0xC1/0x95
+
+# & Copper Rod~ of#
+K:794:0xC1/0x96
+
+# & Iron Rod~ of#
+K:795:0xC1/0x97
+
+# & Moonstone Rod~ of#
+K:796:0xC1/0x98
+
+# & Silver Rod~ of#
+K:797:0xC1/0x99
+
+# & Golden Rod~ of#
+K:798:0xC1/0x9B
+
+# & Mithril Rod~ of#
+K:799:0xC1/0x9C
+
+# & Adamantite Rod~ of#
+K:800:0xC1/0x9D
+
+# & Greater Ration~ of Health
+K:801:0xC4/0x87
+
+# & Crumpled Scroll~ of Mass Resurrection
+K:802:0x83/0x9E
+
+# & Cleaver~
+K:803:0xC4/0x82
+
+# & Light War Axe~
+K:804:0xC4/0x83
+
+# & Slaughter Axe~
+K:805:0xC4/0x84
+
+# & Runestone~
+K:806:0xC4/0x85
+
+# & Fortune cookie~
+K:807:0xC6/0x86
+
+# Portable hole
+K:808:0xC6/0x89
+
+# Critical Hits
+K:809:0xC6/0x9C
+
+# & Wand~ of Digging of Thrain
+K:810:0xC6/0x9C
+
+# & Gnarled Staff~ of Holy Fire of Mithrandir
+K:811:0xC6/0x9C
+
+# Partial Totem
+K:812:0xC6/0x9D
+
+# True Totem
+K:813:0xC6/0x9E
+
+# Player
+R:0:0x8E/0x80
+
+# Filthy street urchin
+R:1:0xAA/0x80
+
+# Scrawny cat
+R:2:0xA7/0x82
+
+# Sparrow
+R:3:0xB4/0x9E
+
+# Chaffinch
+R:4:0xB4/0x9E
+
+# Wild rabbit
+R:5:0xB4/0x9F
+
+# Woodsman
+R:6:0xAA/0x91
+
+# Scruffy little dog
+R:7:0x9D/0x9A
+
+# Farmer Maggot
+R:8:0xAA/0x81
+
+# Blubbering idiot
+R:9:0xAA/0x82
+
+# Boil-covered wretch
+R:10:0xAA/0x83
+
+# Village idiot
+R:11:0xAA/0x84
+
+# Pitiful-looking beggar
+R:12:0xAA/0x85
+
+# Mangy-looking leper
+R:13:0xAA/0x86
+
+# Agent of the black market
+R:14:0xAA/0x87
+
+# Singing, happy drunk
+R:15:0xAA/0x88
+
+# Aimless-looking merchant
+R:16:0xAA/0x89
+
+# Mean-looking mercenary
+R:17:0xAA/0x8A
+
+# Battle-scarred veteran
+R:18:0xAA/0x8B
+
+# Martti Ihrasaari
+R:19:0xB0/0x80
+
+# Grey mold
+R:20:0xA8/0x9F
+
+# Large white snake
+R:21:0xA2/0x85
+
+# Grey mushroom patch
+R:22:0xB0/0x81
+
+# Newt
+R:23:0xB0/0x82
+
+# Giant white centipede
+R:24:0xA5/0x95
+
+# White icky thing
+R:25:0xA8/0x83
+
+# Clear icky thing
+R:26:0xA8/0x84
+
+# Giant white mouse
+R:27:0xAC/0x85
+
+# Large brown snake
+R:28:0xA2/0x84
+
+# Small kobold
+R:29:0xA8/0x99
+
+# Kobold
+R:30:0xA8/0x9A
+
+# White worm mass
+R:31:0xAC/0x9D
+
+# Floating eye
+R:32:0xA6/0x9B
+
+# Rock lizard
+R:33:0xA2/0x86
+
+# Grid bug
+R:34:0xB0/0x84
+
+# Jackal
+R:35:0x9D/0x9B
+
+# Soldier ant
+R:36:0xA5/0x87
+
+# Fruit bat
+R:37:0xA5/0x8F
+
+# Insect swarm
+R:38:0xB5/0x9E
+
+# The Greater hell-beast
+R:39:0xB0/0x83
+
+# Shrieker mushroom patch
+R:40:0x9D/0x86
+
+# Blubbering icky thing
+R:41:0xA8/0x85
+
+# Metallic green centipede
+R:42:0xA5/0x96
+
+# Novice warrior
+R:43:0xAA/0x8C
+
+# Novice rogue
+R:44:0xAA/0x8D
+
+# Novice priest
+R:45:0xAA/0x8E
+
+# Novice mage
+R:46:0xAA/0x8F
+
+# Yellow mushroom patch
+R:47:0x9D/0x87
+
+# White jelly
+R:48:0xA8/0x8A
+
+# Giant black ant
+R:49:0xA5/0x88
+
+# Salamander
+R:50:0xA2/0x88
+
+# White harpy
+R:51:0xA0/0x88
+
+# Blue yeek
+R:52:0xAD/0x87
+
+# Grip, Farmer Maggot's dog
+R:53:0x9D/0x9C
+
+# Wolf, Farmer Maggot's dog
+R:54:0x9D/0x9D
+
+# Fang, Farmer Maggot's dog
+R:55:0x9D/0x9D
+
+# Giant green frog
+R:56:0xA2/0x87
+
+# Freesia
+R:57:0xB0/0x85
+
+# Green worm mass
+R:58:0xAC/0x9E
+
+# Large yellow snake
+R:59:0xA2/0x89
+
+# Cave spider
+R:60:0xA2/0x9D
+
+# Crow
+R:61:0xB5/0x9F
+
+# Wild cat
+R:62:0xA7/0x83
+
+# Smeagol
+R:63:0xAA/0x90
+
+# Green ooze
+R:64:0xA8/0x8B
+
+# Poltergeist
+R:65:0x9F/0x99
+
+# Yellow jelly
+R:66:0xA8/0x8D
+
+# Metallic blue centipede
+R:67:0xA5/0x97
+
+# Raven
+R:68:0xB5/0x9F
+
+# Giant white louse
+R:69:0xA8/0x9D
+
+# Giant yellow centipede
+R:70:0xA5/0x94
+
+# Black naga
+R:71:0xA9/0x88
+
+# Spotted mushroom patch
+R:72:0x9D/0x88
+
+# Silver jelly
+R:73:0xA8/0x8C
+
+# Scruffy-looking hobbit
+R:74:0xA7/0x93
+
+# Giant white ant
+R:75:0xA5/0x89
+
+# Yellow mold
+R:76:0xA9/0x80
+
+# Metallic red centipede
+R:77:0xA5/0x98
+
+# Yellow worm mass
+R:78:0xAC/0x9F
+
+# Clear worm mass
+R:79:0xAD/0x80
+
+# Radiation eye
+R:80:0xA6/0x9C
+
+# Yellow light
+R:81:0xB8/0x93
+
+# Cave lizard
+R:82:0xA2/0x8A
+
+# Novice ranger
+R:83:0xAA/0x91
+
+# Blue jelly
+R:84:0xA8/0x8E
+
+# Creeping copper coins
+R:85:0x9D/0x80
+
+# Giant white rat
+R:86:0xAC/0x86
+
+# Snotling
+R:87:0xB9/0x89
+
+# Swordfish
+R:88:0xB6/0x9E
+
+# Blue worm mass
+R:89:0xAD/0x81
+
+# Large grey snake
+R:90:0xA2/0x8B
+
+# Skeleton kobold
+R:91:0xAC/0x89
+
+# Ewok
+R:92:0xB0/0x86
+
+# Novice mage
+R:93:0xAA/0x8F
+
+# Green naga
+R:94:0xA9/0x89
+
+# Giant leech
+R:95:0xB8/0x94
+
+# Barracuda
+R:96:0xB6/0x9F
+
+# Novice paladin
+R:97:0xAA/0x92
+
+# Zog
+R:98:0xA1/0x80
+
+# Blue ooze
+R:99:0xA8/0x8F
+
+# Green glutton ghost
+R:100:0x9F/0x9A
+
+# Green jelly
+R:101:0xA8/0x90
+
+# Large kobold
+R:102:0xA8/0x9B
+
+# Grey icky thing
+R:103:0xA8/0x86
+
+# Disenchanter eye
+R:104:0xA6/0x9D
+
+# Red worm mass
+R:105:0xAD/0x82
+
+# Copperhead snake
+R:106:0xA2/0x8C
+
+# Death sword
+R:107:0xB0/0x87
+
+# Purple mushroom patch
+R:108:0x9D/0x89
+
+# Novice priest
+R:109:0xAA/0x8E
+
+# Novice warrior
+R:110:0xAA/0x8C
+
+# Nibelung
+R:111:0xB0/0x88
+
+# The disembodied hand that strangled people
+R:112:0xB0/0x89
+
+# Brown mold
+R:113:0xA9/0x81
+
+# Giant brown bat
+R:114:0xA5/0x90
+
+# Rat-thing
+R:115:0xAC/0x88
+
+# Novice rogue
+R:116:0xAA/0x87
+
+# Creeping silver coins
+R:117:0x9D/0x81
+
+# Snaga
+R:118:0xA9/0x8E
+
+# Rattlesnake
+R:119:0xA2/0x8D
+
+# Giant slug
+R:120:0xB8/0x94
+
+# Giant pink frog
+R:121:0xB8/0x95
+
+# Dark elf
+R:122:0x92/0x94
+
+# Zombified kobold
+R:123:0xAC/0x89
+
+# Crypt creep
+R:124:0xB0/0x8A
+
+# Rotting corpse
+R:125:0xB0/0x8B
+
+# Cave orc
+R:126:0xA9/0x8F
+
+# Wood spider
+R:127:0xA2/0x9E
+
+# Manes
+R:128:0xA0/0x91
+
+# Bloodshot eye
+R:129:0xA6/0x9E
+
+# Red naga
+R:130:0xA9/0x8A
+
+# Red jelly
+R:131:0xA8/0x91
+
+# Green icky thing
+R:132:0xA8/0x87
+
+# Lost soul
+R:133:0x9F/0x9B
+
+# Night lizard
+R:134:0xA2/0x8F
+
+# Mughash, the Kobold Lord
+R:135:0xA8/0x9C
+
+# Skeleton orc
+R:136:0xAC/0x8A
+
+# Wormtongue, Agent of Saruman
+R:137:0xAA/0x98
+
+# Robin Hood, the Outlaw
+R:138:0xB0/0x8C
+
+# Nurgling
+R:139:0xB8/0x97
+
+# Lagduf, the Snaga
+R:140:0xA9/0x90
+
+# Brown yeek
+R:141:0xAD/0x88
+
+# Novice ranger
+R:142:0xAA/0x91
+
+# Giant salamander
+R:143:0xA2/0x90
+
+# Space monster
+R:144:0xB0/0x8D
+
+# Carnivorous flying monkey
+R:145:0xB8/0x98
+
+# Green mold
+R:146:0xA9/0x82
+
+# Novice paladin
+R:147:0xAA/0x92
+
+# Lemure
+R:148:0xA0/0x92
+
+# Hill orc
+R:149:0xA9/0x91
+
+# Bandit
+R:150:0xAA/0x9B
+
+# Hunting hawk
+R:151:0xB0/0x8E
+
+# Phantom warrior
+R:152:0xB0/0x8F
+
+# Gremlin
+R:153:0xB0/0x90
+
+# Yeti
+R:154:0xA4/0x91
+
+# Bloodshot icky thing
+R:155:0xA8/0x88
+
+# Giant grey rat
+R:156:0xAC/0x87
+
+# Black harpy
+R:157:0xA0/0x89
+
+# Skaven
+R:158:0xB8/0x99
+
+# The wounded bear
+R:159:0xBA/0x80
+
+# Cave bear
+R:160:0xC4/0x8F
+
+# Rock mole
+R:161:0xBA/0x82
+
+# Mindcrafter
+R:162:0xAA/0x93
+
+# Baby blue dragon
+R:163:0xA5/0x9D
+
+# Baby white dragon
+R:164:0xA5/0x9E
+
+# Baby green dragon
+R:165:0xA5/0x9F
+
+# Baby black dragon
+R:166:0xA6/0x80
+
+# Baby red dragon
+R:167:0xA6/0x81
+
+# Giant red ant
+R:168:0xA5/0x8D
+
+# Brodda, the Easterling
+R:169:0xAA/0x9C
+
+# Bloodfang, the Wolf
+R:170:0xBA/0x83
+
+# King cobra
+R:171:0xA2/0x91
+
+# Eagle
+R:172:0xB4/0x9E
+
+# War bear
+R:173:0xB0/0x91
+
+# Killer bee
+R:174:0xB0/0x92
+
+# Giant spider
+R:175:0xA2/0x9F
+
+# Giant white tick
+R:176:0xA8/0x9D
+
+# The Borshin
+R:177:0xBA/0x84
+
+# Dark elven mage
+R:178:0xA7/0x96
+
+# Kamikaze yeek
+R:179:0xBA/0x94
+
+# Orfax, Son of Boldor
+R:180:0xAD/0x89
+
+# Servant of Glaaki
+R:181:0xBA/0x85
+
+# Dark elven warrior
+R:182:0xA7/0x97
+
+# Sand-dweller
+R:183:0xBA/0x86
+
+# Clear mushroom patch
+R:184:0xB8/0x96
+
+# Quiver slot
+R:185:0xB0/0x93
+
+# Grishnakh, the Hill Orc
+R:186:0xA9/0x93
+
+# Giant tan bat
+R:187:0xC4/0x90
+
+# Owlbear
+R:188:0xBA/0x87
+
+# Blue horror
+R:189:0xB8/0x9A
+
+# Hairy mold
+R:190:0xA9/0x83
+
+# Grizzly bear
+R:191:0xBA/0x88
+
+# Disenchanter mold
+R:192:0xA9/0x84
+
+# Pseudo dragon
+R:193:0xA6/0x82
+
+# Tengu
+R:194:0xA0/0x93
+
+# Creeping gold coins
+R:195:0x9D/0x82
+
+# Wolf
+R:196:0x9D/0x9E
+
+# Giant fruit fly
+R:197:0x9F/0x91
+
+# Panther
+R:198:0xA7/0x84
+
+# Brigand
+R:199:0xB0/0x94
+
+# Hobbes the Tiger
+R:200:0xB0/0x95
+
+# Shadow Creature of Fiona
+R:201:0xB0/0x96
+
+# Undead mass
+R:202:0xB0/0x97
+
+# Chaos shapechanger
+R:203:0xB0/0x98
+
+# Baby multi-hued dragon
+R:204:0xA6/0x83
+
+# Vorpal bunny
+R:205:0xB4/0x9F
+
+# Old Man Willow
+R:206:0xBA/0x89
+
+# Hippocampus
+R:207:0xBA/0x8A
+
+# Zombified orc
+R:208:0xAC/0x8C
+
+# Hippogriff
+R:209:0xA0/0x8A
+
+# Black mamba
+R:210:0xA2/0x92
+
+# White wolf
+R:211:0x9D/0x9F
+
+# Grape jelly
+R:212:0xA8/0x92
+
+# Nether worm mass
+R:213:0xAD/0x83
+
+# Abyss worm mass
+R:214:0xB0/0x99
+
+# Golfimbul, the Hill Orc Chief
+R:215:0xA9/0x94
+
+# Swordsman
+R:216:0x97/0x81
+
+# Skaven shaman
+R:217:0x9A/0x84
+
+# Baby bronze dragon
+R:218:0xA6/0x82
+
+# Baby gold dragon
+R:219:0xA6/0x82
+
+# Evil eye
+R:220:0xC4/0x91
+
+# Mine-dog
+R:221:0xB9/0x8B
+
+# Hellcat
+R:222:0xB0/0x9A
+
+# Moon beast
+R:223:0xB0/0x9B
+
+# Master yeek
+R:224:0xAD/0x8A
+
+# Priest
+R:225:0xAA/0x9E
+
+# Dark elven priest
+R:226:0xA7/0x99
+
+# Air spirit
+R:227:0x9E/0x9F
+
+# Skeleton human
+R:228:0xAC/0x8B
+
+# Zombified human
+R:229:0xAD/0x8E
+
+# Tiger
+R:230:0xA7/0x85
+
+# Moaning spirit
+R:231:0x9F/0x9C
+
+# Stegocentipede
+R:232:0xA5/0x99
+
+# Spotted jelly
+R:233:0xA8/0x93
+
+# Drider
+R:234:0xA3/0x80
+
+# Mongbat
+R:235:0xB0/0x9C
+
+# Killer brown beetle
+R:236:0xA0/0x9B
+
+# Boldor, King of the Yeeks
+R:237:0xAD/0x8B
+
+# Ogre
+R:238:0xA1/0x8B
+
+# Creeping mithril coins
+R:239:0x9D/0x83
+
+# Illusionist
+R:240:0xAB/0x80
+
+# Druid
+R:241:0xAB/0x81
+
+# Pink horror
+R:242:0xB8/0x9B
+
+# Cloaker
+R:243:0x89/0x88
+
+# Black orc
+R:244:0xA9/0x95
+
+# Ochre jelly
+R:245:0xA8/0x94
+
+# Software bug
+R:246:0xB0/0x9D
+
+# Lurker
+R:247:0x80/0x81
+
+# Tangleweed
+R:248:0xC4/0x92
+
+# Vlasta
+R:249:0xA5/0x84
+
+# Giant white dragon fly
+R:250:0x9F/0x93
+
+# Snaga sapper
+R:251:0xB9/0x8C
+
+# Blue icky thing
+R:252:0xA8/0x89
+
+# Gibbering mouther
+R:253:0xB0/0x9E
+
+# Wolfhound of Flora
+R:254:0xB0/0x9F
+
+# Hill giant
+R:255:0xA1/0x91
+
+# Flesh golem
+R:256:0xA7/0x89
+
+# Warg
+R:257:0x9E/0x80
+
+# Cheerful leprechaun
+R:258:0xB1/0x80
+
+# Giant flea
+R:259:0x9F/0x92
+
+# Ufthak of Cirith Ungol
+R:260:0xBA/0x8C
+
+# Clay golem
+R:261:0xB8/0x9D
+
+# Black ogre
+R:262:0xA1/0x8C
+
+# Dweller on the threshold
+R:263:0xB9/0x8D
+
+# Half-orc
+R:264:0xBA/0x8D
+
+# Dark naga
+R:265:0xB8/0x9E
+
+# Poison ivy
+R:266:0xC4/0x93
+
+# Magic mushroom patch
+R:267:0x9D/0x8B
+
+# Plaguebearer of Nurgle
+R:268:0xAD/0x8D
+
+# Guardian naga
+R:269:0xA9/0x8B
+
+# Wererat
+R:270:0xBA/0x8E
+
+# Light hound
+R:271:0xA4/0x93
+
+# Dark hound
+R:272:0xA4/0x94
+
+# Flying skull
+R:273:0xB1/0x81
+
+# Mi-Go
+R:274:0xB1/0x82
+
+# Giant tarantula
+R:275:0xA3/0x81
+
+# Giant clear centipede
+R:276:0xA5/0x9A
+
+# Mirkwood spider
+R:277:0xA3/0x82
+
+# Frost giant
+R:278:0xA1/0x92
+
+# Griffon
+R:279:0xA0/0x8B
+
+# Homunculus
+R:280:0xA0/0x94
+
+# Gnome mage
+R:281:0xA7/0x98
+
+# Clear hound
+R:282:0xA4/0x95
+
+# Umber hulk
+R:283:0xA3/0x99
+
+# Rust monster
+R:284:0xB9/0x8F
+
+# Ogrillon
+R:285:0xA9/0x98
+
+# Gelatinous cube
+R:286:0xA8/0x95
+
+# Giant green dragon fly
+R:287:0x9F/0x94
+
+# Fire giant
+R:288:0xA1/0x93
+
+# Hummerhorn
+R:289:0xBA/0x8F
+
+# Lizard man
+R:290:0xB9/0x90
+
+# Ulfast, Son of Ulfang
+R:291:0xAB/0x82
+
+# Crebain
+R:292:0xC4/0x94
+
+# Berserker
+R:293:0xA9/0x97
+
+# Quasit
+R:294:0xA0/0x95
+
+# Sphinx
+R:295:0xB9/0x91
+
+# Imp
+R:296:0xA0/0x96
+
+# Forest troll
+R:297:0xA3/0x89
+
+# Freezing sphere
+R:298:0xBA/0x91
+
+# Jumping fireball
+R:299:0xB9/0x92
+
+# Ball lightning
+R:300:0xBA/0x92
+
+# 2-headed hydra
+R:301:0xA2/0x93
+
+# Swamp thing
+R:302:0xB9/0x93
+
+# Water spirit
+R:303:0x9F/0x80
+
+# Giant red scorpion
+R:304:0xA3/0x83
+
+# Earth spirit
+R:305:0x9F/0x81
+
+# Fire spirit
+R:306:0x9F/0x82
+
+# Fire hound
+R:307:0xA4/0x96
+
+# Cold hound
+R:308:0xA4/0x97
+
+# Energy hound
+R:309:0xA4/0x98
+
+# Lesser Mimic
+R:310:0x9D/0x8E
+
+# Door mimic
+R:311:0x82/0x83
+
+# Blink dog
+R:312:0x9E/0x81
+
+# Uruk
+R:313:0xA9/0x99
+
+# Shagrat, the Orc Captain
+R:314:0xA9/0x9A
+
+# Gorbag, the Orc Captain
+R:315:0xA9/0x9B
+
+# Shambling mound
+R:316:0x9D/0x8C
+
+# Giant Venus Flytrap
+R:317:0xC4/0x95
+
+# Chaos beastman
+R:318:0xB9/0x95
+
+# Daemonette of Slaanesh
+R:319:0xB9/0x94
+
+# Giant bronze dragon fly
+R:320:0x9F/0x98
+
+# Stone giant
+R:321:0xA1/0x94
+
+# Giant black dragon fly
+R:322:0x9F/0x96
+
+# Stone golem
+R:323:0xA7/0x8B
+
+# Red mold
+R:324:0xA9/0x85
+
+# Giant gold dragon fly
+R:325:0x9F/0x97
+
+# Stunwall
+R:326:0x80/0x93
+
+# Ghast
+R:327:0xBA/0x95
+
+# Neekerbreeker
+R:328:0xC4/0x96
+
+# Huorn
+R:329:0xBA/0x96
+
+# Bolg, Son of Azog
+R:330:0xA9/0x9C
+
+# Phase spider
+R:331:0xA3/0x84
+
+# Lizard king
+R:332:0xB9/0x97
+
+# Landmine
+R:333:0xBA/0x97
+
+# Wyvern
+R:334:0xB1/0x83
+
+# Great eagle
+R:335:0xB9/0x98
+
+# Livingstone
+R:336:0xB1/0x84
+
+# Earth hound
+R:337:0xA4/0x99
+
+# Air hound
+R:338:0xA4/0x9A
+
+# Sabre-tooth tiger
+R:339:0xA7/0x86
+
+# Acid hound
+R:340:0xA4/0x9B
+
+# Chimaera
+R:341:0xA0/0x8C
+
+# Quylthulg
+R:342:0xA1/0x9A
+
+# Sasquatch
+R:343:0xA4/0x92
+
+# Weir
+R:344:0xB1/0x85
+
+# Ranger
+R:345:0xAA/0x97
+
+# Paladin
+R:346:0xAB/0x92
+
+# Werewolf
+R:347:0xBA/0x99
+
+# Dark elven lord
+R:348:0xA7/0x9C
+
+# Cloud giant
+R:349:0xA1/0x96
+
+# Ugluk, the Uruk
+R:350:0xA9/0x9D
+
+# Blue dragon bat
+R:351:0xA5/0x91
+
+# Mimic
+R:352:0x83/0x9D
+
+# Ultimate Mimic
+R:353:0x84/0x9E
+
+# Fire vortex
+R:354:0xAC/0x94
+
+# Acid vortex
+R:355:0xAC/0x95
+
+# Lugdush, the Uruk
+R:356:0xB9/0x9A
+
+# Arch-vile
+R:357:0xBA/0x9A
+
+# Cold vortex
+R:358:0xAC/0x96
+
+# Energy vortex
+R:359:0xAC/0x97
+
+# Globefish
+R:360:0xB9/0x9B
+
+# Giant firefly
+R:361:0x9F/0x95
+
+# Mummified orc
+R:362:0xA1/0x88
+
+# Wolf chieftain
+R:363:0xC4/0x97
+
+# Serpent man
+R:364:0xBA/0x9C
+
+# Vampiric mist
+R:365:0xB9/0x9D
+
+# Killer stag beetle
+R:366:0xA0/0x9D
+
+# Iron golem
+R:367:0xA7/0x8C
+
+# Auto-roller
+R:368:0xB1/0x86
+
+# Giant yellow scorpion
+R:369:0xA3/0x85
+
+# Jade monk
+R:370:0xBA/0x9D
+
+# Black ooze
+R:371:0xA8/0x96
+
+# Hardened warrior
+R:372:0xAB/0x83
+
+# Azog, King of the Uruk-Hai
+R:373:0xA9/0x9F
+
+# Fleshhound of Khorne
+R:374:0xB9/0x9E
+
+# Dark elven warlock
+R:375:0xB1/0x87
+
+# Master rogue
+R:376:0xAB/0x84
+
+# Red dragon bat
+R:377:0xA5/0x92
+
+# Killer white beetle
+R:378:0xBA/0x9E
+
+# Ice skeleton
+R:379:0xB9/0x9F
+
+# Angamaite of Umbar
+R:380:0xBB/0x80
+
+# Forest wight
+R:381:0xB1/0x88
+
+# Khim, Son of Mim
+R:382:0xB1/0x89
+
+# Ibun, Son of Mim
+R:383:0xB1/0x8A
+
+# Meneldor the Swift
+R:384:0xBB/0x81
+
+# Phantom beast
+R:385:0xB1/0x8B
+
+# Giant silver ant
+R:386:0xA0/0x9C
+
+# 4-headed hydra
+R:387:0xA2/0x95
+
+# Lesser hell-beast
+R:388:0xBB/0x83
+
+# Tyrannosaur
+R:389:0xB1/0x8C
+
+# Mummified human
+R:390:0xA1/0x89
+
+# Vampire bat
+R:391:0xA5/0x93
+
+# Sangahyando of Umbar
+R:392:0xAB/0x85
+
+# It
+R:393:0xB1/0x8D
+
+# Banshee
+R:394:0x9F/0x9D
+
+# Carrion crawler
+R:395:0xA5/0x9B
+
+# Xiclotlan
+R:396:0xBB/0x84
+
+# Silent watcher
+R:397:0xB1/0x8E
+
+# Pukelman
+R:398:0xA7/0x8D
+
+# Disenchanter beast
+R:399:0xBA/0x9F
+
+# Dark elven druid
+R:400:0xA7/0x9F
+
+# Stone troll
+R:401:0xA3/0x8A
+
+# Black
+R:402:0xB0/0x8D
+
+# Hill troll
+R:403:0xA3/0x8B
+
+# Wereworm
+R:404:0xAD/0x84
+
+# Killer red beetle
+R:405:0xA0/0x9F
+
+# Disenchanter bat
+R:406:0xC4/0x98
+
+# Gnoph-Keh
+R:407:0xBB/0x86
+
+# Giant grey ant
+R:408:0xA5/0x8C
+
+# Khufu, the Mummified King
+R:409:0xB1/0x8F
+
+# Gwaihir the Windlord
+R:410:0xBB/0x81
+
+# Giant fire tick
+R:411:0xBB/0x87
+
+# Displacer beast
+R:412:0xA7/0x87
+
+# Ulwarth, Son of Ulfang
+R:413:0xAA/0x99
+
+# Werebear
+R:414:0xC4/0x8F
+
+# Cave ogre
+R:415:0xA1/0x8D
+
+# White wraith
+R:416:0xA3/0x9F
+
+# Angel
+R:417:0x9D/0x8F
+
+# Ghoul
+R:418:0xB4/0x8F
+
+# Mim, Betrayer of Turin
+R:419:0xB1/0x90
+
+# Hellblade
+R:420:0xB1/0x91
+
+# Killer fire beetle
+R:421:0xA1/0x80
+
+# Beast of Nurgle
+R:422:0xBB/0x88
+
+# Creeping adamantite coins
+R:423:0x9D/0x84
+
+# Algroth
+R:424:0xA3/0x8C
+
+# Flamer of Tzeentch
+R:425:0xB8/0x9F
+
+# Roper
+R:426:0xB9/0x80
+
+# Headless
+R:427:0xB1/0x92
+
+# Vibration hound
+R:428:0xA4/0x9C
+
+# Nexus hound
+R:429:0xA4/0x9D
+
+# Half-ogre
+R:430:0xA1/0x8E
+
+# Lokkak, the Ogre Chieftain
+R:431:0xA1/0x90
+
+# Vampire
+R:432:0xA3/0x9A
+
+# Gorgimaera
+R:433:0xA0/0x8D
+
+# Shantak
+R:434:0xB1/0x93
+
+# Colbran
+R:435:0xA7/0x8E
+
+# Spirit naga
+R:436:0xA9/0x8C
+
+# Corpser
+R:437:0xB9/0x81
+
+# Fiend of Slaanesh
+R:438:0xA2/0x97
+
+# Stairway to Hell
+R:439:0xB1/0x94
+
+# 5-headed hydra
+R:440:0xA2/0x96
+
+# Barney the Dinosaur
+R:441:0xB1/0x95
+
+# Black knight
+R:442:0xAB/0x88
+
+# Seahorse
+R:443:0xBB/0x89
+
+# Cyclops
+R:444:0xBB/0x8A
+
+# Clairvoyant
+R:445:0xAB/0x86
+
+# Purple worm
+R:446:0xB9/0x82
+
+# Catoblepas
+R:447:0xAC/0x81
+
+# Lesser wall monster
+R:448:0xB1/0x96
+
+# Mage
+R:449:0xAB/0x8A
+
+# Mind flayer
+R:450:0xAB/0x8B
+
+# The Ultimate Dungeon Cleaner
+R:451:0xB1/0x97
+
+# Deep one
+R:452:0xA4/0x86
+
+# Basilisk
+R:453:0xA2/0x97
+
+# Ice troll
+R:454:0xA3/0x8D
+
+# Dhole
+R:455:0xB1/0x99
+
+# Archangel
+R:456:0x9D/0x90
+
+# Greater Mimic
+R:457:0xAD/0x9E
+
+# Chaos tile
+R:458:0xB1/0x9A
+
+# Young blue dragon
+R:459:0xA6/0x84
+
+# Young white dragon
+R:460:0xA6/0x85
+
+# Young green dragon
+R:461:0xA6/0x86
+
+# Young bronze dragon
+R:462:0xA6/0x87
+
+# Aklash
+R:463:0xC1/0x9E
+
+# Mithril golem
+R:464:0xA7/0x8F
+
+# Skeleton troll
+R:465:0xAC/0x8D
+
+# Skeletal tyrannosaur
+R:466:0xBB/0x8B
+
+# Beorn, the Shape-Changer
+R:467:0xC4/0x99
+
+# Thorondor, Lord of Eagles
+R:468:0xBB/0x81
+
+# Giant blue ant
+R:469:0xA5/0x8B
+
+# Grave wight
+R:470:0xAD/0x9C
+
+# Shadow drake
+R:471:0xA6/0x88
+
+# Manticore
+R:472:0xA0/0x8E
+
+# Giant army ant
+R:473:0xAE/0x81
+
+# Killer slicer beetle
+R:474:0xA1/0x81
+
+# Gorgon
+R:475:0xBB/0x8D
+
+# Gug
+R:476:0xBB/0x8E
+
+# Ghost
+R:477:0x9F/0x9E
+
+# Death watch beetle
+R:478:0xA1/0x82
+
+# Mountain ogre
+R:479:0xA1/0x8F
+
+# Nexus quylthulg
+R:480:0xA1/0x9B
+
+# Shelob, Spider of Darkness
+R:481:0xA3/0x86
+
+# Giant squid
+R:482:0xB9/0x83
+
+# Ghoulking
+R:483:0xAD/0x8C
+
+# Doombat
+R:484:0xB9/0x84
+
+# Ninja
+R:485:0xAB/0x8C
+
+# Memory moss
+R:486:0xA9/0x86
+
+# Storm giant
+R:487:0xA1/0x95
+
+# Spectator
+R:488:0xB1/0x9B
+
+# Bokrug
+R:489:0xBB/0x8F
+
+# Biclops
+R:490:0xBB/0x90
+
+# Half-troll
+R:491:0xA9/0x9E
+
+# Ivory monk
+R:492:0xAA/0x93
+
+# Bert the Stone Troll
+R:493:0xA3/0x90
+
+# Bill the Stone Troll
+R:494:0xA3/0x91
+
+# Tom the Stone Troll
+R:495:0xA3/0x92
+
+# Cave troll
+R:496:0xA3/0x8E
+
+# Anti-paladin
+R:497:0xB1/0x9C
+
+# Chaos master
+R:498:0xB1/0x9D
+
+# Barrow wight
+R:499:0xA4/0x81
+
+# Skeleton ettin
+R:500:0xC4/0x9A
+
+# Chaos drake
+R:501:0xA6/0x89
+
+# Law drake
+R:502:0xA6/0x8A
+
+# Balance drake
+R:503:0xA6/0x8B
+
+# Ethereal drake
+R:504:0xA6/0x8C
+
+# Groo, the Wanderer
+R:505:0xB1/0x9E
+
+# Fasolt the Giant
+R:506:0xB1/0x9F
+
+# Shade
+R:507:0xA4/0x89
+
+# Spectre
+R:508:0xA0/0x80
+
+# Water troll
+R:509:0xA3/0x93
+
+# Fire elemental
+R:510:0x9F/0x83
+
+# Cherub
+R:511:0x9D/0x91
+
+# Water elemental
+R:512:0x9F/0x84
+
+# Multi-hued hound
+R:513:0xB2/0x81
+
+# Invisible stalker
+R:514:0x9F/0x85
+
+# Carrion crawler
+R:515:0xA5/0x9C
+
+# Master thief
+R:516:0xAB/0x8E
+
+# The Watcher in the Water
+R:517:0xAF/0x95
+
+# Lich
+R:518:0xA1/0x83
+
+# Gas spore
+R:519:0xB2/0x8E
+
+# Master vampire
+R:520:0xA3/0x9B
+
+# Oriental vampire
+R:521:0xB2/0x83
+
+# Greater mummy
+R:522:0xA1/0x8A
+
+# Bloodletter of Khorne
+R:523:0xA0/0x98
+
+# Giant grey scorpion
+R:524:0xA3/0x87
+
+# Earth elemental
+R:525:0x9F/0x86
+
+# Air elemental
+R:526:0x9F/0x87
+
+# Shimmering mold
+R:527:0xAF/0x81
+
+# Gargoyle
+R:528:0xBB/0x91
+
+# Malicious leprechaun
+R:529:0xB2/0x85
+
+# Eog golem
+R:530:0xA7/0x90
+
+# Little Boy
+R:531:0xB9/0x85
+
+# Dagashi
+R:532:0xAB/0x90
+
+# Headless ghost
+R:533:0xBB/0x92
+
+# Dread
+R:534:0xB9/0x87
+
+# Leng spider
+R:535:0xBB/0x93
+
+# Gauth
+R:536:0xC4/0x9B
+
+# Smoke elemental
+R:537:0x9F/0x90
+
+# Olog
+R:538:0xA3/0x94
+
+# Halfling slinger
+R:539:0xB2/0x86
+
+# Gravity hound
+R:540:0xA4/0x9E
+
+# Acidic cytoplasm
+R:541:0xA8/0x97
+
+# Inertia hound
+R:542:0xA4/0x9F
+
+# Impact hound
+R:543:0xA5/0x80
+
+# Shardstorm
+R:544:0xC4/0x9C
+
+# Ooze elemental
+R:545:0x9F/0x88
+
+# Young black dragon
+R:546:0xA6/0x8D
+
+# Mumak
+R:547:0xAC/0x84
+
+# Giant fire ant
+R:548:0xA5/0x8A
+
+# Mature white dragon
+R:549:0xA6/0x8E
+
+# Xorn
+R:550:0xA4/0x8F
+
+# Rogrog the Black Troll
+R:551:0xA3/0x8F
+
+# Mist giant
+R:552:0xA7/0x91
+
+# Phantom
+R:553:0xB2/0x87
+
+# Grey wraith
+R:554:0xA4/0x82
+
+# Revenant
+R:555:0xA4/0x88
+
+# Young multi-hued dragon
+R:556:0xA6/0x8F
+
+# Raal's Tome of Destruction
+R:557:0xB2/0x88
+
+# Colossus
+R:558:0xB2/0x89
+
+# Young gold dragon
+R:559:0xA6/0x90
+
+# Mature blue dragon
+R:560:0xA6/0x91
+
+# Mature green dragon
+R:561:0xA6/0x92
+
+# Mature bronze dragon
+R:562:0xA6/0x93
+
+# Young red dragon
+R:563:0xA6/0x94
+
+# Nightblade
+R:564:0xB2/0x8A
+
+# Trapper
+R:565:0xAD/0x9F
+
+# Bodak
+R:566:0xA0/0x98
+
+# Time bomb
+R:567:0xBB/0x96
+
+# Mezzodaemon
+R:568:0xAD/0x90
+
+# Elder thing
+R:569:0xB2/0x8B
+
+# Ice elemental
+R:570:0x9F/0x8A
+
+# Necromancer
+R:571:0xB2/0x8C
+
+# The Greater hell magic mushroom were-quylthulg
+R:572:0xB9/0x86
+
+# Lorgan, Chief of the Easterlings
+R:573:0xB2/0x8D
+
+# Chaos spawn
+R:574:0xB2/0x8E
+
+# Mummified troll
+R:575:0xBB/0x97
+
+# Storm of Unmagic
+R:576:0xC4/0x9D
+
+# Crypt thing
+R:577:0xA4/0x80
+
+# Chaos butterfly
+R:578:0xBB/0x98
+
+# Time elemental
+R:579:0xB2/0x8F
+
+# Flying polyp
+R:580:0xBB/0x99
+
+# The Queen Ant
+R:581:0xA5/0x8E
+
+# Will o' the wisp
+R:582:0x9F/0x8B
+
+# Shan
+R:583:0xBB/0x9A
+
+# Magma elemental
+R:584:0x9F/0x8C
+
+# Black pudding
+R:585:0xA8/0x98
+
+# Killer iridescent beetle
+R:586:0xB4/0x90
+
+# Nexus vortex
+R:587:0xAE/0x80
+
+# Plasma vortex
+R:588:0xAC/0x98
+
+# Mature red dragon
+R:589:0xA6/0x95
+
+# Mature gold dragon
+R:590:0xA6/0x96
+
+# Crystal drake
+R:591:0xA6/0x97
+
+# Mature black dragon
+R:592:0xA6/0x98
+
+# Mature multi-hued dragon
+R:593:0xA6/0x99
+
+# Sky whale
+R:594:0xBB/0x9B
+
+# Draebor, the Imp
+R:595:0xC1/0x9F
+
+# Mother Hydra
+R:596:0xA2/0x98
+
+# Death knight
+R:597:0xAB/0x94
+
+# Castamir the Usurper
+R:598:0xB2/0x90
+
+# Time vortex
+R:599:0xAC/0x99
+
+# Shimmering vortex
+R:600:0xAC/0x9A
+
+# Ancient blue dragon
+R:601:0x9E/0x88
+
+# Ancient bronze dragon
+R:602:0x9E/0x89
+
+# Beholder
+R:603:0xA6/0x9F
+
+# Emperor wight
+R:604:0xA4/0x83
+
+# Seraph
+R:605:0x9D/0x92
+
+# Vargo, Tyrant of Fire
+R:606:0x9F/0x8D
+
+# Black wraith
+R:607:0xA4/0x84
+
+# Nightgaunt
+R:608:0xB2/0x91
+
+# Baron of hell
+R:609:0xB2/0x92
+
+# Scylla
+R:610:0xBB/0x9C
+
+# Monastic lich
+R:611:0xA1/0x87
+
+# Nether wraith
+R:612:0xA4/0x85
+
+# Hellhound
+R:613:0xAD/0x96
+
+# 7-headed hydra
+R:614:0xA2/0x99
+
+# Waldern, King of Water
+R:615:0x9F/0x8E
+
+# Kavlax the Many-Headed
+R:616:0xA6/0x9A
+
+# Ancient white dragon
+R:617:0x9E/0x8A
+
+# Ancient green dragon
+R:618:0x9E/0x8B
+
+# Chthonian
+R:619:0xB2/0x93
+
+# Eldrak
+R:620:0xA3/0x97
+
+# Ettin
+R:621:0xA3/0x96
+
+# Night mare
+R:622:0xAC/0x83
+
+# Vampire lord
+R:623:0xA3/0x9C
+
+# Ancient black dragon
+R:624:0x9E/0x8C
+
+# Weird fume
+R:625:0xAF/0x80
+
+# Spawn of Ubbo-Sathla
+R:626:0xBB/0x9D
+
+# Fat Man
+R:627:0xB9/0x85
+
+# Malekith the Accursed
+R:628:0xA7/0x95
+
+# Shadowfax, steed of Gandalf
+R:629:0xBB/0x9E
+
+# Spirit troll
+R:630:0xA3/0x98
+
+# War troll
+R:631:0xB2/0x94
+
+# Disenchanter worm mass
+R:632:0xAD/0x86
+
+# Rotting quylthulg
+R:633:0xA1/0x9C
+
+# Lesser titan
+R:634:0xA1/0x97
+
+# 9-headed hydra
+R:635:0xA2/0x99
+
+# Enchantress
+R:636:0xAB/0x96
+
+# Ranger chieftain
+R:637:0xAB/0x97
+
+# Sorcerer
+R:638:0xAB/0x98
+
+# Xaren
+R:639:0xA4/0x90
+
+# Giant roc
+R:640:0x9D/0x97
+
+# Minotaur
+R:641:0xA0/0x8F
+
+# Medusa, the Gorgon
+R:642:0xA9/0x8D
+
+# Death drake
+R:643:0x9E/0x8D
+
+# Ancient red dragon
+R:644:0x9E/0x8E
+
+# Ancient gold dragon
+R:645:0x9E/0x8F
+
+# Great crystal drake
+R:646:0x9E/0x90
+
+# Wyrd sister
+R:647:0xA7/0x9D
+
+# Vrock
+R:648:0xB2/0x95
+
+# Death quasit
+R:649:0xA0/0x99
+
+# Giganto, the Gargantuan
+R:650:0xBB/0x9F
+
+# Strygalldwir
+R:651:0xB2/0x96
+
+# Fallen angel
+R:652:0xAA/0x8E
+
+# Giant headless
+R:653:0xBC/0x80
+
+# Judge Fire
+R:654:0xAB/0x93
+
+# Ubbo-Sathla, the Unbegotten Source
+R:655:0xBC/0x81
+
+# Judge Mortis
+R:656:0xBC/0x82
+
+# Dark elven sorcerer
+R:657:0xA8/0x81
+
+# Master lich
+R:658:0xA1/0x84
+
+# Byakhee
+R:659:0xB2/0x97
+
+# Eol, the Dark Elf
+R:660:0xB2/0x99
+
+# Archon
+R:661:0x9D/0x93
+
+# Formless spawn of Tsathoggua
+R:662:0xB2/0x9A
+
+# Hunting horror
+R:663:0xB2/0x9B
+
+# Undead beholder
+R:664:0xA7/0x80
+
+# Shadow
+R:665:0xA0/0x81
+
+# Iron lich
+R:666:0xB2/0x9C
+
+# Dread
+R:667:0xB9/0x87
+
+# Greater basilisk
+R:668:0xBC/0x83
+
+# Charybdis
+R:669:0xBC/0x84
+
+# Jack of Shadows
+R:670:0xAB/0x9C
+
+# Zephyr Lord
+R:671:0xAB/0x91
+
+# Juggernaut of Khorne
+R:672:0xBC/0x85
+
+# Mumak
+R:673:0xAC/0x82
+
+# Judge Fear
+R:674:0xA7/0x88
+
+# Ancient multi-hued dragon
+R:675:0x9E/0x91
+
+# Ethereal dragon
+R:676:0x9E/0x92
+
+# Dark young of Shub-Niggurath
+R:677:0xB2/0x9D
+
+# Colour out of space
+R:678:0x91/0x99
+
+# Quaker, Master of Earth
+R:679:0x9F/0x8F
+
+# Death leprechaun
+R:680:0xA7/0x9E
+
+# Chaugnar Faugn, Horror from the Hills
+R:681:0xBC/0x94
+
+# Lloigor
+R:682:0xBC/0x95
+
+# Utgard-Loke
+R:683:0xBC/0x96
+
+# Quachil Uttaus, Treader of the Dust
+R:684:0xBC/0x97
+
+# Shoggoth
+R:685:0xBC/0x98
+
+# Judge Death
+R:686:0xBC/0x99
+
+# Ariel, Queen of Air
+R:687:0x9F/0x91
+
+# 11-headed hydra
+R:688:0xA2/0x9A
+
+# Patriarch
+R:689:0xAB/0x9A
+
+# Dreadmaster
+R:690:0xA0/0x85
+
+# Drolem
+R:691:0xA7/0x92
+
+# Scatha the Worm
+R:692:0xAD/0x9B
+
+# Warrior of the Dawn
+R:693:0xB2/0x9E
+
+# Lesser black reaver
+R:694:0xA4/0x87
+
+# Zoth-Ommog
+R:695:0xBE/0x89
+
+# Grand master thief
+R:696:0xC2/0x8A
+
+# Smaug the Golden
+R:697:0x9E/0x93
+
+# The Stormbringer
+R:698:0xB3/0x80
+
+# Knight Templar
+R:699:0xB3/0x81
+
+# Leprechaun fanatic
+R:700:0xA8/0x80
+
+# Dracolich
+R:701:0x9E/0x95
+
+# Greater titan
+R:702:0xA1/0x98
+
+# Dracolisk
+R:703:0x9E/0x94
+
+# Winged Horror
+R:704:0xC4/0x9E
+
+# Spectral tyrannosaur
+R:705:0xB3/0x82
+
+# Yibb-Tstll, the Patient One
+R:706:0xBC/0x9B
+
+# Ghatanothoa
+R:707:0xBC/0x9C
+
+# Ent
+R:708:0xBC/0x86
+
+# Hru
+R:709:0xBC/0x9D
+
+# Itangast the Fire Drake
+R:710:0x9E/0x96
+
+# Death mold
+R:711:0xA9/0x87
+
+# Fafner the Dragon
+R:712:0xB3/0x83
+
+# Charon, Boatman of the Styx
+R:713:0xBC/0x9E
+
+# Quickbeam, the Ent
+R:714:0xBC/0x9F
+
+# Glaurung, Father of the Dragons
+R:715:0xAD/0x9A
+
+# Behemoth
+R:716:0xBD/0x80
+
+# Garm, Guardian of Hel
+R:717:0x9E/0x84
+
+# Greater wall monster
+R:718:0xB3/0x84
+
+# Nycadaemon
+R:719:0xAD/0x91
+
+# Barbazu
+R:720:0xAD/0x95
+
+# Goat of Mendes
+R:721:0xB3/0x85
+
+# Nightwing
+R:722:0xAD/0x9D
+
+# Maulotaur
+R:723:0xB3/0x86
+
+# Nether hound
+R:724:0xA5/0x81
+
+# Time hound
+R:725:0xA5/0x82
+
+# Plasma hound
+R:726:0xA5/0x83
+
+# Demonic quylthulg
+R:727:0xA1/0x9D
+
+# Great Storm Wyrm
+R:728:0x9E/0x97
+
+# Ulik the Troll
+R:729:0xBD/0x81
+
+# Baphomet the Minotaur Lord
+R:730:0xA0/0x90
+
+# Hell knight
+R:731:0xBD/0x82
+
+# Bull Gates
+R:732:0xB3/0x87
+
+# Santa Claus
+R:733:0xB3/0x88
+
+# Eihort, the Thing in the Labyrinth
+R:734:0xBE/0x8A
+
+# The King in Yellow
+R:735:0xBE/0x8B
+
+# Great unclean one
+R:736:0xBE/0x8C
+
+# Lord of Chaos
+R:737:0xB3/0x89
+
+# Old Sorcerer
+R:738:0x9C/0x8A
+
+# Ethereal hound
+R:739:0xB3/0x8A
+
+# Lesser kraken
+R:740:0xBD/0x83
+
+# Great Ice Wyrm
+R:741:0x9E/0x98
+
+# Demilich
+R:742:0xA4/0x8A
+
+# The Phoenix
+R:743:0x9D/0x98
+
+# Nightcrawler
+R:744:0xA4/0x8C
+
+# Lord of Change
+R:745:0xBC/0x87
+
+# Keeper of Secrets
+R:746:0xBE/0x8D
+
+# Shudde M'ell
+R:747:0xBE/0x8E
+
+# Hand druj
+R:748:0xAC/0x8E
+
+# Eye druj
+R:749:0xAC/0x8F
+
+# Skull druj
+R:750:0xAC/0x90
+
+# Chaos vortex
+R:751:0xAC/0x9B
+
+# Aether vortex
+R:752:0xAC/0x9C
+
+# Nidhogg, the Hel-Drake
+R:753:0xBE/0x8F
+
+# The Lernaean Hydra
+R:754:0xA2/0x9B
+
+# Thuringwethil, the Vampire Messenger
+R:755:0xA3/0x9D
+
+# Great Hell Wyrm
+R:756:0x9E/0x99
+
+# Hastur the Unspeakable
+R:757:0xB3/0x8B
+
+# Bloodthirster
+R:758:0xBD/0x84
+
+# Draconic quylthulg
+R:759:0xA1/0x9E
+
+# Nyogtha, the Thing that Should not Be
+R:760:0xB3/0x8C
+
+# Ahtu, Avatar of Nyarlathotep
+R:761:0xBE/0x90
+
+# Fundin Bluecloak
+R:762:0xBE/0x91
+
+# Bile Demon
+R:763:0xC5/0x80
+
+# Uriel, Angel of Fire
+R:764:0x9D/0x94
+
+# Azriel, Angel of Death
+R:765:0x9D/0x95
+
+# Ancalagon the Black
+R:766:0x9E/0x9A
+
+# Daoloth, the Render of the Veils
+R:767:0xBE/0x92
+
+# Nightwalker
+R:768:0xBD/0x85
+
+# Gabriel, the Messenger
+R:769:0x9D/0x96
+
+# Artsi, the Champion of Chaos
+R:770:0xBE/0x93
+
+# Saruman of Many Colours
+R:771:0xAB/0x9E
+
+# Harowen the Black Hand
+R:772:0xBE/0x94
+
+# Osyluth
+R:773:0xC5/0x81
+
+# Dreadlord
+R:774:0xA0/0x86
+
+# Greater kraken
+R:775:0xBD/0x86
+
+# Archlich
+R:776:0xA4/0x8D
+
+# The Cat Lord
+R:777:0xB3/0x8F
+
+# Jabberwock
+R:778:0xC5/0x82
+
+# Chaos hound
+R:779:0xA5/0x85
+
+# Vlad Dracula, Prince of Darkness
+R:780:0xBE/0x95
+
+# Beholder hive-mother
+R:781:0xBE/0x96
+
+# Leviathan
+R:782:0xBD/0x87
+
+# Great Wyrm of Chaos
+R:783:0x9E/0x9B
+
+# Great Wyrm of Law
+R:784:0x9E/0x9C
+
+# Great Wyrm of Balance
+R:785:0x9E/0x9D
+
+# Shambler
+R:786:0xB3/0x91
+
+# Gelugon
+R:787:0xC5/0x83
+
+# Glaaki
+R:788:0xB9/0x88
+
+# T'ron, the Rebel Dragonrider
+R:789:0xB3/0x92
+
+# Great Wyrm of Many Colours
+R:790:0xB3/0x93
+
+# Mardra, rider of the Gold Loranth
+R:791:0xB3/0x94
+
+# Tselakus, the Dreadlord
+R:792:0xA0/0x87
+
+# Sky Drake
+R:793:0xB3/0x95
+
+# Eilinel the Entrapped
+R:794:0xA0/0x83
+
+# Horned Reaper
+R:795:0xC5/0x84
+
+# The Norsa
+R:796:0xB3/0x97
+
+# Rhan-Tegoth
+R:797:0xBE/0x99
+
+# Black reaver
+R:798:0xA1/0x85
+
+# Master mindcrafter
+R:799:0xAB/0x9F
+
+# Greater demonic quylthulg
+R:800:0xA1/0x9F
+
+# Greater draconic quylthulg
+R:801:0xA2/0x80
+
+# Greater rotting quylthulg
+R:802:0xA2/0x81
+
+# Null, the Living Void
+R:803:0xBC/0x88
+
+# Feagwath, the Undead Sorcerer
+R:804:0xA1/0x86
+
+# Omarax the Eye Tyrant
+R:805:0xA7/0x81
+
+# Tsathoggua, the Sleeper of N'kai
+R:806:0xBE/0x9A
+
+# Greater Balrog
+R:807:0xAD/0x93
+
+# Ungoliant, the Unlight
+R:808:0xA3/0x88
+
+# Atlach-Nacha, the Spider God
+R:809:0xB3/0x9A
+
+# Y'golonac
+R:810:0xBE/0x9B
+
+# Aether hound
+R:811:0xA5/0x86
+
+# Pit Fiend
+R:812:0xAD/0x92
+
+# The Serpent of Chaos
+R:813:0xB4/0x8D
+
+# Yig, Father of Serpents
+R:814:0xBE/0x9C
+
+# Unmaker
+R:815:0xB3/0x9C
+
+# Cyberdemon
+R:816:0xB3/0x9D
+
+# Hela, Queen of the Dead
+R:817:0xBE/0x9D
+
+# The Mouth of Sauron
+R:818:0xBE/0x9E
+
+# The Necromancer of Dol Guldur
+R:819:0xB3/0x9E
+
+# Lessa, rider of the Gold Ramoth
+R:820:0xB3/0x9F
+
+# Master quylthulg
+R:821:0xA2/0x82
+
+# Qlzqqlzuup, the Lord of Flesh
+R:822:0xA2/0x83
+
+# Cthugha, the Living Flame
+R:823:0xBE/0x9F
+
+# F'lar, rider of the Bronze Mnementh
+R:824:0xB4/0x80
+
+# Maeglin, the Traitor of Gondolin
+R:825:0xA4/0x8E
+
+# Cyaegha
+R:826:0xBF/0x80
+
+# Pazuzu, Lord of Air
+R:827:0xBF/0x81
+
+# Ithaqua the Windwalker
+R:828:0xB4/0x81
+
+# Greater Hellhound
+R:829:0x9E/0x83
+
+# Cantoras, the Skeletal Lord
+R:830:0xAC/0x91
+
+# Mephistopheles, Lord of Hell
+R:831:0xB4/0x82
+
+# Godzilla
+R:832:0xB4/0x83
+
+# Abhoth, Source of Uncleanness
+R:833:0xBF/0x82
+
+# Ymir, the Ice Giant
+R:834:0xBF/0x83
+
+# Loki, the Trickster
+R:835:0xBF/0x84
+
+# Star-spawn of Cthulhu
+R:836:0xB4/0x84
+
+# Surtur, the Fire Giant
+R:837:0xBF/0x85
+
+# The Tarrasque
+R:838:0xBC/0x93
+
+# Lungorthin, the Balrog of White Fire
+R:839:0xBF/0x86
+
+# Draugluin, Sire of All Werewolves
+R:840:0xBF/0x87
+
+# Shuma-Gorath
+R:841:0xBF/0x88
+
+# Tulzscha, the Green Flame
+R:842:0xBC/0x92
+
+# Oremorj, the Cyberdemon Lord
+R:843:0xBC/0x91
+
+# Vecna, the Emperor Lich
+R:844:0xB4/0x85
+
+# Yog-Sothoth, the All-in-One
+R:845:0xB4/0x86
+
+# Fenris Wolf
+R:846:0xBC/0x90
+
+# Great Wyrm of Power
+R:847:0xB4/0x87
+
+# Shub-Niggurath, Black Goat of the Woods
+R:848:0xB4/0x88
+
+# Nodens, Lord of the Great Abyss
+R:849:0xAB/0x8D
+
+# Carcharoth, the Jaws of Thirst
+R:850:0x9E/0x86
+
+# Nyarlathotep, the Crawling Chaos
+R:851:0xB4/0x89
+
+# Azathoth, the Daemon Sultan
+R:852:0xB4/0x8A
+
+# Huan, Wolfhound of the Valar
+R:853:0x9E/0x87
+
+# Jormungand the Midgard Serpent
+R:854:0xBC/0x8F
+
+# The Destroyer
+R:855:0xBC/0x8E
+
+# Gothmog, the High Captain of Balrogs
+R:856:0xAD/0x98
+
+# Great Cthulhu
+R:857:0xB4/0x8B
+
+# Sorka, rider of the Gold Faranth
+R:858:0xB4/0x8C
+
+# The Unicorn of Order
+R:859:0xBC/0x8D
+
+# Sauron, the Sorcerer
+R:860:0xAC/0x80
+
+# DarkGod, the Mighty Coder of Hell
+R:861:0xC0/0x9D
+
+# Morgoth, Lord of Darkness
+R:862:0xB4/0x8E
+
+# Human Warrior
+R:863:0xB5/0x80
+
+# Elven archer
+R:864:0xB5/0x81
+
+# Dwarven warrior
+R:865:0xB5/0x82
+
+# Elite uruk
+R:866:0xB5/0x83
+
+# The Philosophy Teacher
+R:867:0xBC/0x8C
+
+# The Variant Maintainer
+R:868:0xBC/0x8B
+
+# Random Number Generator
+R:869:0xBC/0x8A
+
+# Rocket mine
+R:870:0xBD/0x88
+
+# Bouncing mine
+R:871:0xBD/0x89
+
+# Durin's Bane
+R:872:0xBF/0x89
+
+# The Icky Queen
+R:873:0xBF/0x8A
+
+# Rot jelly
+R:874:0xBD/0x8A
+
+# Death
+R:875:0xBD/0x8B
+
+# Famine
+R:876:0xBD/0x8D
+
+# Pestilence
+R:877:0xBD/0x8C
+
+# War
+R:878:0xBD/0x8E
+
+# Pike
+R:879:0xBD/0x8F
+
+# Electric eel
+R:880:0xBD/0x90
+
+# Giant crayfish
+R:881:0xBD/0x91
+
+# Mermaid
+R:882:0xBD/0x92
+
+# Box jellyfish
+R:883:0xBA/0x81
+
+# Giant piranha
+R:884:0xB6/0x9D
+
+# Piranha
+R:885:0xB6/0x9D
+
+# Bullywug
+R:886:0xBD/0x94
+
+# Bullywug warrior
+R:887:0xBD/0x95
+
+# Bullywug shaman
+R:888:0xBD/0x96
+
+# Whale
+R:889:0xBA/0x98
+
+# Sand mite
+R:890:0xBD/0x98
+
+# Octopus
+R:891:0xBD/0x99
+
+# Giant octopus
+R:892:0xBD/0x9A
+
+# Eye of the deep
+R:893:0xBD/0x9B
+
+# Murk dweller
+R:894:0xBF/0x8B
+
+# Drowned soul
+R:895:0xBF/0x8C
+
+# Tiger shark
+R:896:0xBF/0x8D
+
+# Hammerhead shark
+R:897:0xBA/0x90
+
+# Great white shark
+R:898:0xBB/0x82
+
+# Aquatic golem
+R:899:0xBF/0x8E
+
+# Aquatic kobold
+R:900:0xBF/0x8F
+
+# White shark
+R:901:0xBB/0x82
+
+# Scrag
+R:902:0xBF/0x91
+
+# Jaws
+R:903:0xBB/0x8C
+
+# Aquatic elf
+R:904:0xBF/0x93
+
+# Aquatic elven warrior
+R:905:0xBF/0x94
+
+# Aquatic elven shaman
+R:906:0xBF/0x95
+
+# Stargazer
+R:907:0xBF/0x96
+
+# Elder stargazer
+R:908:0xBF/0x97
+
+# Flounder
+R:909:0xBF/0x98
+
+# Giant turtle
+R:910:0xBF/0x99
+
+# Baby dragon turtle
+R:911:0xBF/0x9A
+
+# Young dragon turtle
+R:912:0xBF/0x9B
+
+# Mature dragon turtle
+R:913:0xBF/0x9C
+
+# Ancient dragon turtle
+R:914:0xBF/0x9D
+
+# Fastitocalon
+R:915:0xBF/0x9E
+
+# Undead stargazer
+R:916:0xBF/0x9F
+
+# Killer whale
+R:917:0xB9/0x9C
+
+# Merrow
+R:918:0xC5/0x85
+
+# Water naga
+R:919:0xC0/0x81
+
+# Devilfish
+R:920:0xC0/0x82
+
+# Undead devilfish
+R:921:0xC0/0x83
+
+# Moby Dick, the White Whale
+R:922:0xC0/0x80
+
+# Aquatic hound
+R:923:0xC0/0x85
+
+# Water demon
+R:924:0xC0/0x86
+
+# Ixitxachitl
+R:925:0xB8/0x9C
+
+# Ixitxachitl priest
+R:926:0xC0/0x88
+
+# Vampiric ixitxachitl
+R:927:0xC0/0x89
+
+# Mathilde, the Science Student
+R:928:0xBD/0x9C
+
+# Child spirit
+R:929:0xBD/0x9D
+
+# Young spirit
+R:930:0xBD/0x9E
+
+# Mature spirit
+R:931:0xBD/0x9F
+
+# Experienced spirit
+R:932:0xBE/0x80
+
+# Wise spirit
+R:933:0xBE/0x81
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0xC0/0x8A
+
+# Gandalf the Grey
+R:935:0xC0/0x8B
+
+# Nar, the Dwarf
+R:936:0xC0/0x8C
+
+# Novice mindcrafter
+R:937:0xAA/0x9A
+
+# Great Swamp Wyrm
+R:938:0xC5/0x86
+
+# Great Bile Wyrm
+R:939:0xC5/0x87
+
+# Blue Firelizard
+R:940:0xBE/0x82
+
+# Green Firelizard
+R:941:0xBE/0x83
+
+# Brown Firelizard
+R:942:0xBE/0x84
+
+# Bronze Firelizard
+R:943:0xBE/0x85
+
+# Gold Firelizard
+R:944:0xBE/0x86
+
+# High-elven ranger
+R:945:0xBE/0x87
+
+# Uvatha the Horseman
+R:946:0xC0/0x90
+
+# Adunaphel the Quiet
+R:947:0xC0/0x91
+
+# Akhorahil the Blind
+R:948:0xC0/0x92
+
+# Ren the Unclean
+R:949:0xC0/0x93
+
+# Ji Indur Dawndeath
+R:950:0xC0/0x94
+
+# Dwar, Dog Lord of Waw
+R:951:0xC0/0x95
+
+# Hoarmurath of Dir
+R:952:0xC0/0x96
+
+# Khamul, the Black Easterling
+R:953:0xC0/0x97
+
+# The Witch-King of Angmar
+R:954:0xC0/0x98
+
+# Green Dragonrider
+R:955:0xB3/0x96
+
+# Blue Dragonrider
+R:956:0xB3/0x8E
+
+# Brown Dragonrider
+R:957:0xB3/0x98
+
+# Bronze Dragonrider
+R:958:0xB3/0x98
+
+# Gold Dragonrider
+R:959:0xB3/0x94
+
+# Thread
+R:960:0xBE/0x88
+
+# Gorlim, Betrayer of Barahir
+R:961:0xC0/0x99
+
+# The Blubbering idiot, agent of black market, Simon the weak
+R:962:0xAA/0x82
+
+# Aranea
+R:963:0xC1/0x9A
+
+# Elder aranea
+R:964:0xC0/0x9A
+
+# Giant brown tick
+R:965:0xC5/0x88
+
+# Dolphiner
+R:966:0xC0/0x9C
+
+# Novice possessor (soul)
+R:967:0xC4/0x86
+
+# Bat of Gorgoroth
+R:968:0xC5/0x97
+
+# The Princess
+R:969:0xC5/0x98
+
+# Merton Proudfoot, the lost hobbit
+R:970:0xC5/0x99
+
+# The Wight-King of the Barrow-downs
+R:971:0xC5/0x9A
+
+# Adventurer
+R:972:0xC5/0x9B
+
+# Experienced possessor (soul)
+R:973:0xC5/0x9C
+
+# Old possessor (soul)
+R:974:0xC5/0x9D
+
+# Death orb
+R:975:0xC5/0x9E
+
+# Bronze dragon worm
+R:976:0xC6/0x80
+
+# Gold dragon worm
+R:977:0xC5/0x9F
+
+# Moldoux, the Defenceless Mold
+R:978:0xBC/0x89
+
+# The Physics Teacher
+R:979:0xBE/0x97
+
+# Ar-Pharazon the Golden
+R:980:0xC0/0x9E
+
+# Doppelganger
+R:981:0x97/0x8C
+
+# Marylene, Heartbreakeress of the Netherworld
+R:982:0xC1/0x8D
+
+# The Greater Lag Monster
+R:983:0xC2/0x8D
+
+# Hrungnir, the Stone Giant
+R:984:0xA7/0x8A
+
+# Bullroarer the Hobbit
+R:985:0xA7/0x9B
+
+# 3-headed hydra
+R:986:0xA2/0x94
+
+# Uldor the Accursed
+R:987:0xAB/0x95
+
+# Mystic
+R:988:0xAB/0x9B
+
+# Elder vampire
+R:989:0xA1/0x99
+
+# Ulfang the Black
+R:990:0xAA/0x96
+
+# Demonologist
+R:991:0xA8/0x82
+
+# Hezrou
+R:992:0xA2/0x9C
+
+# Glabrezu
+R:993:0xA2/0x8E
+
+# Nalfeshnee
+R:994:0xC2/0x8E
+
+# Marilith
+R:995:0xC2/0x8F
+
+# Lesser Balrog
+R:996:0xAD/0x95
+
+# Master mystic
+R:997:0xAA/0x94
+
+# Grand master mystic
+R:998:0xAB/0x9D
+
+# Erinyes
+R:999:0xA0/0x84
+
+# Novice mindcrafter
+R:1000:0xAA/0x9A
+
+# Polyphemus, the Blind Cyclops
+R:1001:0xC5/0x89
+
+# Great Wyrm of Perplexity
+R:1002:0xC2/0x92
+
+# Hound of Tindalos
+R:1003:0xC2/0x93
+
+# Great Wyrm of Thunder
+R:1004:0xC5/0x8A
+
+# Silver mouse
+R:1005:0xC5/0x8B
+
+# The Rat King
+R:1006:0xC2/0x96
+
+# Vort the Kobold Queen
+R:1007:0xC2/0x97
+
+# Giant black louse
+R:1008:0xC2/0x98
+
+# Fire Phantom
+R:1009:0xC2/0x99
+
+# The Insane Player
+R:1010:0x92/0x81
+
+# Glaryssa, Succubus Queen
+R:1011:0xC2/0x9A
+
+# Vermicious Knid
+R:1012:0xC2/0x9B
+
+# Bone golem
+R:1013:0xC2/0x9C
+
+# Snake of Yig
+R:1014:0xC2/0x9D
+
+# Bronze golem
+R:1015:0xC5/0x8C
+
+# Dimensional shambler
+R:1016:0xC2/0x9F
+
+# Cultist
+R:1017:0x94/0x99
+
+# Cult leader
+R:1018:0x99/0x97
+
+# Servitor of the outer gods
+R:1019:0xC3/0x8A
+
+# Avatar of Nyarlathotep
+R:1020:0xC3/0x8B
+
+# Thiazi, the Storm Giant
+R:1021:0xC5/0x8D
+
+# Hypnos, Lord of Sleep
+R:1022:0xC3/0x8D
+
+# Blue dragon worm
+R:1023:0xC3/0x8E
+
+# White dragon worm
+R:1024:0xC3/0x8F
+
+# Green dragon worm
+R:1025:0xC3/0x92
+
+# Black dragon worm
+R:1026:0xC3/0x91
+
+# Red dragon worm
+R:1027:0xC3/0x90
+
+# Multi-hued dragon worm
+R:1028:0xC3/0x93
+
+# The Minotaur of the Labyrinth
+R:1029:0xC3/0x94
+
+# The Sandworm Queen
+R:1030:0xC3/0x9B
+
+# Sandworm
+R:1031:0xC3/0x9C
+
+# Tik'srvzllat
+R:1032:0xC3/0x9D
+
+# The Glass Golem
+R:1033:0xC5/0x8E
+
+# The White Balrog
+R:1034:0xC5/0x8F
+
+# Golgarach, the Living Rock
+R:1035:0x80/0x84
+
+# Atlas, the Titan
+R:1036:0xC5/0x90
+
+# Kronos, Lord of the Titans
+R:1037:0xC5/0x91
+
+# Water hound
+R:1038:0xC6/0x88
+
+# Improv, the mighty MoLD
+R:1039:0xC6/0x8E
+
+# Emperor Mimic
+R:1040:0xC6/0x9C
+
+# Melinda Proudfoot
+R:1041:0x88/0xAA
+
+# Thrain, the King Under the Mountain
+R:1042:0x88/0xAB
+
+# Spells (*)
+S:48:0x91/0x88
+S:49:0x91/0x89
+S:50:0x91/0x8A
+S:51:0x91/0x8B
+S:52:0x91/0x8C
+S:53:0x91/0x8D
+S:54:0x91/0x8E
+S:55:0x91/0x8F
+S:56:0x91/0x90
+S:57:0x91/0x91
+S:58:0x91/0x92
+S:59:0x91/0x93
+S:60:0x91/0x94
+S:61:0x91/0x95
+S:62:0x91/0x96
+S:63:0x91/0x97
+
+# Spells (|)
+S:64:0x8F/0x80
+S:65:0x8F/0x84
+S:66:0x8F/0x88
+S:67:0x8F/0x8C
+S:68:0x8F/0x90
+S:69:0x8F/0x94
+S:70:0x8F/0x98
+S:71:0x8F/0x9C
+S:72:0x90/0x80
+S:73:0x90/0x84
+S:74:0x90/0x88
+S:75:0x90/0x8C
+S:76:0x90/0x90
+S:77:0x90/0x94
+S:78:0x90/0x98
+S:79:0x90/0x9C
+
+# Spells (-)
+S:80:0x8F/0x81
+S:81:0x8F/0x85
+S:82:0x8F/0x89
+S:83:0x8F/0x8D
+S:84:0x8F/0x91
+S:85:0x8F/0x95
+S:86:0x8F/0x99
+S:87:0x8F/0x9D
+S:88:0x90/0x81
+S:89:0x90/0x85
+S:90:0x90/0x89
+S:91:0x90/0x8D
+S:92:0x90/0x91
+S:93:0x90/0x95
+S:94:0x90/0x99
+S:95:0x90/0x9D
+
+# Spells (/)
+S:96:0x8F/0x82
+S:97:0x8F/0x86
+S:98:0x8F/0x8A
+S:99:0x8F/0x8E
+S:100:0x8F/0x92
+S:101:0x8F/0x96
+S:102:0x8F/0x9A
+S:103:0x8F/0x9E
+S:104:0x90/0x82
+S:105:0x90/0x86
+S:106:0x90/0x8A
+S:107:0x90/0x8E
+S:108:0x90/0x92
+S:109:0x90/0x96
+S:110:0x90/0x9A
+S:111:0x90/0x9E
+
+# Spells (\)
+S:112:0x8F/0x83
+S:113:0x8F/0x87
+S:114:0x8F/0x8B
+S:115:0x8F/0x8F
+S:116:0x8F/0x93
+S:117:0x8F/0x97
+S:118:0x8F/0x9B
+S:119:0x8F/0x9F
+S:120:0x90/0x83
+S:121:0x90/0x87
+S:122:0x90/0x8B
+S:123:0x90/0x8F
+S:124:0x90/0x93
+S:125:0x90/0x97
+S:126:0x90/0x9B
+S:127:0x90/0x9F
+
+# Amulets (")
+S:128:0x87/0x87
+S:129:0x87/0x80
+S:130:0x87/0x88
+S:131:0x87/0x82
+S:132:0x87/0x83
+S:133:0x87/0x84
+S:134:0x87/0x85
+S:135:0x87/0x86
+S:136:0x87/0x81
+S:137:0x87/0x81
+S:138:0x87/0x89
+S:139:0x87/0x8A
+S:140:0x87/0x8B
+S:141:0x87/0x8C
+S:142:0x87/0x8D
+S:143:0x87/0x8E
+
+# Rings (=)
+S:144:0x84/0x87
+S:145:0x84/0x80
+S:146:0x84/0x88
+S:147:0x84/0x82
+S:148:0x84/0x83
+S:149:0x84/0x84
+S:150:0x84/0x85
+S:151:0x84/0x86
+S:152:0x84/0x81
+S:153:0x84/0x81
+S:154:0x84/0x89
+S:155:0x84/0x8A
+S:156:0x84/0x8B
+S:157:0x84/0x8C
+S:158:0x84/0x8D
+S:159:0x84/0x8E
+
+# Staffs (_)
+S:160:0x87/0x96
+S:161:0x87/0x95
+S:162:0x87/0x95
+S:163:0x87/0x92
+S:164:0x87/0x92
+S:165:0x87/0x93
+S:166:0x87/0x95
+S:167:0x87/0x90
+S:168:0x87/0x95
+S:169:0x87/0x95
+S:170:0x87/0x92
+S:171:0x87/0x94
+S:172:0x87/0x92
+S:173:0x87/0x93
+S:174:0x87/0x96
+S:175:0x87/0x90
+
+# Wands (-)
+S:176:0x86/0x97
+S:177:0x86/0x90
+S:178:0x86/0x98
+S:179:0x86/0x92
+S:180:0x86/0x93
+S:181:0x86/0x94
+S:182:0x86/0x95
+S:183:0x86/0x96
+S:184:0x86/0x91
+S:185:0x86/0x91
+S:186:0x86/0x99
+S:187:0x86/0x9A
+S:188:0x86/0x9B
+S:189:0x86/0x9C
+S:190:0x86/0x9D
+S:191:0x86/0x9E
+
+# Rods (-)
+S:192:0x86/0x87
+S:193:0x86/0x80
+S:194:0x86/0x88
+S:195:0x86/0x82
+S:196:0x86/0x83
+S:197:0x86/0x84
+S:198:0x86/0x85
+S:199:0x86/0x86
+S:200:0x86/0x81
+S:201:0x86/0x81
+S:202:0x86/0x89
+S:203:0x86/0x8A
+S:204:0x86/0x8B
+S:205:0x86/0x8C
+S:206:0x86/0x8D
+S:207:0x86/0x8E
+
+# Scrolls (?)
+S:208:0x83/0x9C
+S:209:0x83/0x9D
+S:210:0x83/0x9E
+S:211:0x83/0x9F
+S:212:0x83/0x9C
+S:213:0x83/0x9D
+S:214:0x83/0x9E
+S:215:0x83/0x9F
+S:216:0x83/0x9C
+S:217:0x83/0x9D
+S:218:0x83/0x9E
+S:219:0x83/0x9F
+S:220:0x83/0x9C
+S:221:0x83/0x9D
+S:222:0x83/0x9E
+S:223:0x83/0x9F
+
+# Potions (!)
+S:224:0x85/0x87
+S:225:0x85/0x80
+S:226:0x85/0x88
+S:227:0x85/0x82
+S:228:0x85/0x83
+S:229:0x85/0x84
+S:230:0x85/0x85
+S:231:0x85/0x86
+S:232:0x85/0x81
+S:233:0x85/0x81
+S:234:0x85/0x89
+S:235:0x85/0x8A
+S:236:0x85/0x8B
+S:237:0x85/0x8C
+S:238:0x85/0x8D
+S:239:0x85/0x8E
+
+# Food (,)
+S:240:0x85/0x97
+S:241:0x85/0x90
+S:242:0x85/0x98
+S:243:0x85/0x92
+S:244:0x85/0x93
+S:245:0x85/0x94
+S:246:0x85/0x95
+S:247:0x85/0x96
+S:248:0x85/0x91
+S:249:0x85/0x91
+S:250:0x85/0x99
+S:251:0x85/0x9A
+S:252:0x85/0x9B
+S:253:0x85/0x9C
+S:254:0x85/0x9D
+S:255:0x85/0x9E
+
+# Fire golem
+R:1043:0x8C/0xA0
+
+# Melkor, Lord of Darkness
+R:1044:0x8C/0xA1
+
+# & piece~ of a Relic of Eru
+K:814:0x8C/0xA2
+
+# & piece~ of a Relic of Manwe
+K:815:0x8C/0xA3
+
+# & piece~ of a Relic of Tulkas
+K:816:0x8C/0xA4
+
+# & piece~ of a Relic of Melkor
+K:817:0x8C/0xA5
+
+# rocky ground
+F:207:0x8D/0xA0
+
+# cloud-like vapour
+F:208:0x8D/0xA1
+
+# condensing water
+F:209:0x8D/0xA2
+
+# dense mist
+F:210:0x8D/0xA3
+
+# hail-stone wall
+F:211:0x8D/0xA4
+
+# Mining Supply store
+B:59:0x87/0xAE
+
+# & piece~ of a Relic of Yavanna
+K:818:0x8C/0xA6
+
+# Elven
+G:M:12:0x91/0xA1
+
+# Dwarven
+G:M:13:0x91/0xA0
+
+# Spirit
+R:1045:0x92/0x9F
+R:1046:0x92/0xA0
+R:1047:0x92/0xA1
+R:1048:0x92/0xA2
+R:1049:0x92/0xA3
+R:1050:0x92/0xA4
+R:1051:0x92/0xA5
+R:1052:0x92/0xA6
+R:1053:0x92/0xA7
+R:1054:0x92/0xA8
+R:1055:0x92/0xA9
+R:1056:0x92/0xAA
+R:1057:0x92/0xA3
+R:1058:0x92/0xAB
+R:1059:0x92/0xAC
+R:1060:0x92/0xAD
+R:1061:0x92/0xAE
+R:1062:0x92/0xAF
+R:1063:0x92/0xB0
+R:1064:0x92/0xB1
+R:1065:0x92/0xB2
+R:1066:0x92/0xB3
+R:1067:0x92/0xB4
+R:1068:0x92/0xB5
+R:1069:0x92/0xB6
+R:1070:0x92/0xB7
+R:1071:0x92/0xB8
+R:1072:0x92/0xB9
+R:1073:0x92/0xBA
+R:1074:0x92/0xBB
+R:1075:0x92/0xBC
+
+# & Spellbook~ of #
+K:757:0x91/0xA4
+
+# Weakness Trap
+#G:T:1:0xFF/0xFF
+#G:T:2:0xFF/0xFF
+#G:T:3:0xFF/0xFF
+
+# Intelligence Trap
+#G:T:4:0xFF/0xFF
+#G:T:5:0xFF/0xFF
+#G:T:6:0xFF/0xFF
+
+# Wisdom Trap
+#G:T:7:0xFF/0xFF
+#G:T:8:0xFF/0xFF
+#G:T:9:0xFF/0xFF
+
+# Fumbling Fingers Trap
+#G:T:10:0xFF/0xFF
+#G:T:11:0xFF/0xFF
+#G:T:12:0xFF/0xFF
+
+# Wasting Trap
+#G:T:13:0xFF/0xFF
+#G:T:14:0xFF/0xFF
+#G:T:15:0xFF/0xFF
+
+# Beauty Trap
+#G:T:16:0xFF/0xFF
+#G:T:17:0xFF/0xFF
+#G:T:18:0xFF/0xFF
+
+# Trap of Curse Weapon
+#G:T:20:0xFF/0xFF
+
+# Trap of Curse Armor
+#G:T:21:0xFF/0xFF
+
+# Earthquake Trap
+#G:T:22:0xFF/0xFF
+
+# Poison Needle Trap
+#G:T:23:0xFF/0xFF
+
+# Summon Monster Trap
+#G:T:24:0xFF/0xFF
+
+# Summon Undead Trap
+#G:T:25:0xFF/0xFF
+
+# Summon Greater Undead Trap
+#G:T:26:0xFF/0xFF
+
+# Teleport Trap
+#G:T:27:0xFF/0xFF
+
+# Paralyzing Trap
+#G:T:28:0xFF/0xFF
+
+# Explosive Device
+#G:T:29:0xFF/0xFF
+
+# Teleport Item Trap
+#G:T:30:0xFF/0xFF
+
+# Lose Memory Trap
+#G:T:31:0xFF/0xFF
+
+# Bitter Regret Trap
+#G:T:32:0xFF/0xFF
+
+# Bowel Cramps Trap
+#G:T:33:0xFF/0xFF
+
+# Blindness
+#G:T:34:0xFF/0xFF
+
+# Aggravation Trap
+#G:T:35:0xFF/0xFF
+
+# Multiplication Trap
+#G:T:36:0xFF/0xFF
+
+# Steal Item Trap
+#G:T:37:0xFF/0xFF
+
+# Summon Fast Quylthulgs Trap
+#G:T:38:0xFF/0xFF
+
+# Trap of Sinking
+#G:T:39:0xFF/0xFF
+
+# Trap of Mana Drain
+#G:T:40:0xFF/0xFF
+
+# Trap of Missing Money
+#G:T:41:0xFF/0xFF
+
+# Trap of No Return
+#G:T:42:0xFF/0xFF
+
+# Trap of Silent Switching
+#G:T:43:0xFF/0xFF
+
+# Trap of Walls
+#G:T:44:0xFF/0xFF
+
+# Trap of Calling Out
+#G:T:45:0xFF/0xFF
+
+# Trap of Sliding
+#G:T:46:0xFF/0xFF
+
+# Trap of Charges Drain
+#G:T:47:0xFF/0xFF
+
+# Trap of Stair Movement
+#G:T:48:0xFF/0xFF
+
+# Trap of New Trap
+#G:T:49:0xFF/0xFF
+
+# Trap of Scatter Items
+#G:T:50:0xFF/0xFF
+
+# Trap of Decay
+#G:T:51:0xFF/0xFF
+
+# Trap of Wasting Wands
+#G:T:52:0xFF/0xFF
+
+# Trap of Filling
+#G:T:53:0xFF/0xFF
+
+# Trap of Drain Speed
+#G:T:54:0xFF/0xFF
+
+# Lightning Bolt Trap
+#G:T:60:0xFF/0xFF
+
+# Poison Bolt Trap
+#G:T:61:0xFF/0xFF
+
+# Acid Bolt Trap
+#G:T:62:0xFF/0xFF
+
+# Cold Bolt Trap
+#G:T:63:0xFF/0xFF
+
+# Fire Bolt Trap
+#G:T:64:0xFF/0xFF
+
+# Plasma Bolt Trap
+#G:T:65:0xFF/0xFF
+
+# Water Bolt Trap
+#G:T:66:0xFF/0xFF
+
+# Lite Bolt Trap
+#G:T:67:0xFF/0xFF
+
+# Dark Bolt Trap
+#G:T:68:0xFF/0xFF
+
+# Shards Bolt Trap
+#G:T:69:0xFF/0xFF
+
+# Sound Bolt Trap
+#G:T:70:0xFF/0xFF
+
+# Confusion Bolt Trap
+#G:T:71:0xFF/0xFF
+
+# Force Bolt Trap
+#G:T:72:0xFF/0xFF
+
+# Inertia Bolt Trap
+#G:T:73:0xFF/0xFF
+
+# Mana Bolt Trap
+#G:T:74:0xFF/0xFF
+
+# Ice Bolt Trap
+#G:T:75:0xFF/0xFF
+
+# Chaos Bolt Trap
+#G:T:76:0xFF/0xFF
+
+# Nether Bolt Trap
+#G:T:77:0xFF/0xFF
+
+# Disenchantment Bolt Trap
+#G:T:78:0xFF/0xFF
+
+# Nexus Bolt Trap
+#G:T:79:0xFF/0xFF
+
+# Time Bolt Trap
+#G:T:80:0xFF/0xFF
+
+# Gravity Bolt Trap
+#G:T:81:0xFF/0xFF
+
+# Lightning Ball Trap
+#G:T:82:0xFF/0xFF
+
+# Poison Ball Trap
+#G:T:83:0xFF/0xFF
+
+# Acid Ball Trap
+#G:T:84:0xFF/0xFF
+
+# Cold Ball Trap
+#G:T:85:0xFF/0xFF
+
+# Fire Ball Trap
+#G:T:86:0xFF/0xFF
+
+# Plasma Ball Trap
+#G:T:87:0xFF/0xFF
+
+# Water Ball Trap
+#G:T:88:0xFF/0xFF
+
+# Light Ball Trap
+#G:T:89:0xFF/0xFF
+
+# Darkness Ball Trap
+#G:T:90:0xFF/0xFF
+
+# Shards Ball Trap
+#G:T:91:0xFF/0xFF
+
+# Sound Ball Trap
+#G:T:92:0xFF/0xFF
+
+# Confusion Ball Trap
+#G:T:93:0xFF/0xFF
+
+# Force Ball Trap
+#G:T:94:0xFF/0xFF
+
+# Inertia Ball Trap
+#G:T:95:0x82/0xBF
+
+# Mana Ball Trap
+#G:T:96:0xFF/0xFF
+
+# Ice Ball Trap
+#G:T:97:0xFF/0xFF
+
+# Chaos Ball Trap
+#G:T:98:0xFF/0xFF
+
+# Nether Ball Trap
+#G:T:99:0xFF/0xFF
+
+# Disenchantment Ball Trap
+#G:T:100:0xFF/0xFF
+
+# Nexus Ball Trap
+#G:T:101:0xFF/0xFF
+
+# Time Ball Trap
+#G:T:102:0xFF/0xFF
+
+# Gravity Ball Trap
+#G:T:103:0xFF/0xFF
+
+# Arrow Trap
+#G:T:110:0xFF/0xFF
+
+# Bolt Trap
+#G:T:111:0xFF/0xFF
+
+# Seeker Arrow Trap
+#G:T:112:0xFF/0xFF
+
+# Seeker Bolt Trap
+#G:T:113:0xFF/0xFF
+
+# Poison Arrow Trap
+#G:T:114:0xFF/0xFF
+
+# Poison Bolt Trap
+#G:T:115:0xFF/0xFF
+
+# Poison Seeker Arrow Trap
+#G:T:116:0xFF/0xFF
+
+# Poison Seeker Bolt Trap
+#G:T:117:0xFF/0xFF
+
+# Broken Dagger Trap
+#G:T:118:0xFF/0xFF
+
+# Dagger Trap
+#G:T:119:0xFF/0xFF
+
+# Poison Broken Dagger Trap
+#G:T:120:0xFF/0xFF
+
+# Poison Dagger Trap
+#G:T:121:0xFF/0xFF
+
+# Arrows Trap
+#G:T:122:0xFF/0xFF
+
+# Bolts Trap
+#G:T:123:0xFF/0xFF
+
+# Seeker Arrow Trap
+#G:T:124:0xFF/0xFF
+
+# Seeker Bolt Trap
+#G:T:125:0xFF/0xFF
+
+# Poison Arrows Trap
+#G:T:126:0xFF/0xFF
+
+# Poison Bolt Trap
+#G:T:127:0xFF/0xFF
+
+# Poison Seeker Arrows Trap
+#G:T:128:0xFF/0xFF
+
+# Poison Seeker Bolts Trap
+#G:T:129:0xFF/0xFF
+
+# Broken Daggers Trap
+#G:T:130:0xFF/0xFF
+
+# Dagger Trap
+#G:T:131:0xFF/0xFF
+
+# Poison Broken Daggers Trap
+#G:T:132:0xFF/0xFF
+
+# Poison Daggers Trap
+#G:T:133:0xFF/0xFF
+
+# Trap of Drop Item
+#G:T:140:0xFF/0xFF
+
+# Trap of Drop Items
+#G:T:141:0xFF/0xFF
+
+# Trap of Drop Everything
+#G:T:142:0xFF/0xFF
+
+# Trap of Femininity
+#G:T:150:0xFF/0xFF
+
+# Trap of Masculinity
+#G:T:151:0xFF/0xFF
+
+# Trap of Neutrality
+#G:T:152:0xFF/0xFF
+
+# Trap of Aging
+#G:T:153:0xFF/0xFF
+
+# Trap of Growing
+#G:T:154:0xFF/0xFF
+
+# Trap of Shrinking
+#G:T:155:0xFF/0xFF
+
+# Trap of Tanker Drain
+#G:T:157:0xFF/0xFF
+
+# Trap of Divine Anger
+#G:T:158:0xFF/0xFF
+
+# Trap of Divine Wrath
+#G:T:159:0xFF/0xFF
+
+# Hallucination Trap
+#G:T:160:0xFF/0xFF
+
+# Greater Magic Missile Trap
+#G:T:161:0xFF/0xFF
+
+# Foulness Trap
+#G:T:162:0xFF/0xFF
+
+# Trap of Holy Fire
+#G:T:164:0xFF/0xFF
+
+# Trap of Hell Fire
+#G:T:165:0xFF/0xFF
+
+# Psi Bolt Trap
+#G:T:166:0xFF/0xFF
+
+# Psi Drain Trap
+#G:T:167:0xFF/0xFF
+
+# Plasma Ball Trap
+#G:T:168:0xFF/0xFF
+
+# Psi Ball Trap
+#G:T:169:0xFF/0xFF
+
+# Acquirement Trap
+#G:T:170:0xFF/0xFF
+
+# Greater Lightning Bolt Trap
+#G:T:171:0xFF/0xFF
+
+# Greater Poison Bolt Trap
+#G:T:172:0xFF/0xFF
+
+# Greater Acid Bolt Trap
+#G:T:173:0xFF/0xFF
+
+# Greater Cold Bolt Trap
+#G:T:174:0xFF/0xFF
+
+# Greater Fire Bolt Trap
+#G:T:175:0xFF/0xFF
+# non-defines encountered :
+# Load the special player pictures
+%:xtra-new.prf
diff --git a/lib/pref/graf-sdl.prf b/lib/pref/graf-sdl.prf
new file mode 100644
index 00000000..818f876a
--- /dev/null
+++ b/lib/pref/graf-sdl.prf
@@ -0,0 +1,37 @@
+# File: graf-x11.prf
+
+
+# Font stuff
+%:font-x11.prf
+
+
+# Color palette - Graphics
+
+#V:16:0x01:0x00:0x00:0x00
+#V:17:0x01:0xF0:0xE0:0xD0
+#V:18:0x01:0x80:0x80:0x80
+#V:19:0x01:0x50:0x50:0x50
+#V:20:0x01:0xE0:0xB0:0x00
+#V:21:0x01:0xC0:0xA0:0x70
+#V:22:0x01:0x80:0x60:0x40
+#V:23:0x01:0x50:0x3C:0x28
+#V:24:0x01:0x00:0xA0:0xF0
+#V:25:0x01:0x00:0x00:0xF0
+#V:26:0x01:0x00:0x00:0x70
+#V:27:0x01:0xF0:0x00:0x00
+#V:28:0x01:0x80:0x00:0x00
+#V:29:0x01:0x90:0x00:0xB0
+#V:30:0x01:0x00:0x60:0x10
+#V:31:0x01:0x60:0xF0:0x40
+
+
+# Standard file
+?:[EQU $GRAF old]
+%:graf-xxx.prf
+
+# New tiles
+?:[EQU $GRAF new]
+%:graf-new.prf
+
+?:1
+
diff --git a/lib/pref/graf-win.prf b/lib/pref/graf-win.prf
new file mode 100644
index 00000000..f59edb35
--- /dev/null
+++ b/lib/pref/graf-win.prf
@@ -0,0 +1,16 @@
+# File: graf-win.prf
+
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+# Standard file
+?:[EQU $GRAF old]
+%:graf-xxx.prf
+
+# New tiles
+?:[EQU $GRAF new]
+%:graf-new.prf
diff --git a/lib/pref/graf-x11.prf b/lib/pref/graf-x11.prf
new file mode 100644
index 00000000..818f876a
--- /dev/null
+++ b/lib/pref/graf-x11.prf
@@ -0,0 +1,37 @@
+# File: graf-x11.prf
+
+
+# Font stuff
+%:font-x11.prf
+
+
+# Color palette - Graphics
+
+#V:16:0x01:0x00:0x00:0x00
+#V:17:0x01:0xF0:0xE0:0xD0
+#V:18:0x01:0x80:0x80:0x80
+#V:19:0x01:0x50:0x50:0x50
+#V:20:0x01:0xE0:0xB0:0x00
+#V:21:0x01:0xC0:0xA0:0x70
+#V:22:0x01:0x80:0x60:0x40
+#V:23:0x01:0x50:0x3C:0x28
+#V:24:0x01:0x00:0xA0:0xF0
+#V:25:0x01:0x00:0x00:0xF0
+#V:26:0x01:0x00:0x00:0x70
+#V:27:0x01:0xF0:0x00:0x00
+#V:28:0x01:0x80:0x00:0x00
+#V:29:0x01:0x90:0x00:0xB0
+#V:30:0x01:0x00:0x60:0x10
+#V:31:0x01:0x60:0xF0:0x40
+
+
+# Standard file
+?:[EQU $GRAF old]
+%:graf-xxx.prf
+
+# New tiles
+?:[EQU $GRAF new]
+%:graf-new.prf
+
+?:1
+
diff --git a/lib/pref/graf-xxx.prf b/lib/pref/graf-xxx.prf
new file mode 100644
index 00000000..08661544
--- /dev/null
+++ b/lib/pref/graf-xxx.prf
@@ -0,0 +1,6345 @@
+# PRF file generated by Andreas Koch`s Tile Assigner
+# 23/06/2004 : Edited manually
+
+# 2185 items
+# 2185 probably mapped correctly
+# 0 imported but not yet defined
+# 0 defined to value(s) lower than 0x80
+# Old header :
+### Special attr:char values ###
+# # Unused (@)
+# S:0x00:0x00:0x40
+# S:0x01:0x01:0x40
+# S:0x02:0x02:0x40
+# S:0x03:0x03:0x40
+# S:0x04:0x04:0x40
+# S:0x05:0x05:0x40
+# S:0x06:0x06:0x40
+# S:0x07:0x07:0x40
+# S:0x08:0x08:0x40
+# S:0x09:0x09:0x40
+# S:0x0A:0x0A:0x40
+# S:0x0B:0x0B:0x40
+# S:0x0C:0x0C:0x40
+# S:0x0D:0x0D:0x40
+# S:0x0E:0x0E:0x40
+# S:0x0F:0x0F:0x40
+# # Unused (@)
+# S:0x10:0x00:0x40
+# S:0x11:0x01:0x40
+# S:0x12:0x02:0x40
+# S:0x13:0x03:0x40
+# S:0x14:0x04:0x40
+# S:0x15:0x05:0x40
+# S:0x16:0x06:0x40
+# S:0x17:0x07:0x40
+# S:0x18:0x08:0x40
+# S:0x19:0x09:0x40
+# S:0x1A:0x0A:0x40
+# S:0x1B:0x0B:0x40
+# S:0x1C:0x0C:0x40
+# S:0x1D:0x0D:0x40
+# S:0x1E:0x0E:0x40
+# S:0x1F:0x0F:0x40
+# # Unused (@)
+# S:0x20:0x00:0x40
+# S:0x21:0x01:0x40
+# S:0x22:0x02:0x40
+# S:0x23:0x03:0x40
+# S:0x24:0x04:0x40
+# S:0x25:0x05:0x40
+# S:0x26:0x06:0x40
+# S:0x27:0x07:0x40
+# S:0x28:0x08:0x40
+# S:0x29:0x09:0x40
+# S:0x2A:0x0A:0x40
+# S:0x2B:0x0B:0x40
+# S:0x2C:0x0C:0x40
+# S:0x2D:0x0D:0x40
+# S:0x2E:0x0E:0x40
+# S:0x2F:0x0F:0x40
+
+# General Store
+B:0:0x81/0x91
+
+# Armoury
+B:1:0x81/0x92
+
+# Weapon Smiths
+B:2:0x81/0x93
+
+# Temple
+B:3:0x81/0x94
+
+# Alchemy Shop
+B:4:0x81/0x95
+
+# Magic Shop
+B:5:0x81/0x96
+
+# Black Market
+B:6:0x81/0x97
+
+# Home
+B:7:0x81/0x98
+
+# Bookstore
+B:8:0x82/0x93
+
+# Pet Shop
+B:9:0xCB/0x96
+
+# Mayor's Office
+B:10:0xCB/0x92
+
+# Inn
+B:11:0xCB/0x95
+
+# The Soothsayer
+B:12:0xD4/0x85
+
+# Library
+B:13:0xD4/0x89
+
+# Castle
+B:14:0xCB/0x92
+
+# Casino
+B:15:0xD5/0x81
+
+# Beastmaster Shanty
+B:16:0xD3/0x8B
+
+# Fighters Hall
+B:17:0xD3/0x8C
+
+# Tower of Magery
+B:18:0xD4/0x8B
+
+# Inner Temple
+B:19:0xD4/0x9D
+
+# Paladins Guild
+B:20:0xCB/0x8F
+
+# Rangers Guild
+B:21:0xD3/0x83
+
+# Weyr
+B:22:0xCB/0x93
+
+# The Mirror
+B:23:0xD4/0x89
+
+# Seat of Ruling
+B:24:0xCB/0x92
+
+# Wizards Spire
+B:25:0xD4/0x8A
+
+# Priests Circle
+B:26:0xD4/0x92
+
+# Tower of the King
+B:27:0xCB/0x92
+
+# Library
+B:28:0xD4/0x89
+
+# The White Tree
+B:29:0xCB/0x95
+
+# Craftsmaster
+B:30:0xCB/0x97
+
+# Earth-Dome (Nature)
+B:31:0xCB/0x9A
+
+# Minstrels Haven
+B:32:0xD3/0x9F
+
+# Star-Dome
+B:33:0xD4/0x8C
+
+# Valarin Temple
+B:34:0xD4/0x90
+
+# Sea-Dome
+B:35:0xD4/0x91
+
+# The Golden Flower
+B:36:0xD3/0x83
+
+# The Fountain
+B:37:0xD4/0x9D
+
+# Axe Smith
+B:38:0xCC/0x96
+
+# Hafted Smith
+B:39:0xCC/0x97
+
+# Polearm Smith
+B:40:0xCC/0x98
+
+# Sword Smith
+B:41:0xCC/0x80
+
+# Rare Jewelry Shop
+B:42:0xD3/0x96
+
+# Jewelry Shop
+B:43:0xD3/0x93
+
+# Footwear Shop
+B:44:0xD3/0x9D
+
+# Rare Footwear Shop
+B:45:0xD3/0x9E
+
+# Library
+B:46:0xD3/0x9C
+
+# Forbidden Library
+B:47:0xD4/0x8F
+
+# Expensive Black Market
+B:48:0xD4/0x95
+
+# Common Shop
+B:49:0xD4/0x93
+
+# Dragon Hunter
+B:50:0xCC/0x89
+
+# Speed Ring Market
+B:51:0xD3/0x97
+
+# Scribe
+B:52:0xD4/0x86
+
+# Potion Store
+B:53:0xD4/0x80
+
+# Recaller
+B:54:0xD4/0x88
+
+# Master Archer
+B:55:0xD3/0x85
+
+# Merchants Guild
+B:56:0xD4/0x9B
+
+# The Mathom-house
+B:57:0xCB/0x9B
+
+# The Prancing Pony
+B:58:0xCB/0x95
+
+# nothing
+F:0:0x81/0x80
+
+# open floor
+F:1:0x80/0x80
+
+# fountain - wet
+F:2:0xD1/0x83
+
+# glyph of warding
+F:3:0xA2/0x88
+
+# open door
+F:4:0x81/0x87
+
+# broken door
+F:5:0x81/0x87
+
+# up staircase
+F:6:0x81/0x9C
+
+# down staircase
+F:7:0x81/0x9E
+
+# quest entrance
+F:8:0x82/0x8E
+
+# quest exit
+F:9:0x82/0x8B
+
+# quest down level
+F:10:0x82/0x8F
+
+# quest up level
+F:11:0x82/0x8C
+
+# town exit
+F:12:0x82/0x91
+
+# shaft down
+F:13:0x82/0x90
+
+# shaft up
+F:14:0x82/0x8D
+
+# fountain
+F:15:0xD1/0x82
+
+# web
+F:16:0x82/0x92
+
+# Open pit
+F:17:0xA2/0x96
+
+# Spiked Pit
+F:18:0xA2/0x96
+
+# Poison Pit
+F:19:0xA2/0x96
+
+# Summon Rune
+F:20:0x8A/0x9C
+
+# Teleport Rune
+F:21:0x8A/0x9C
+
+# Fire spot
+F:22:0x8A/0x9B
+
+# Acid spot
+F:23:0x8A/0x9B
+
+# Slow dart trap
+F:24:0x82/0x9E
+
+# Lose str dart
+F:25:0xA2/0x89
+
+# Lose dex dart
+F:26:0xA2/0x8D
+
+# Lose con dart
+F:27:0xA2/0x92
+
+# gas trap - blind
+F:28:0xA2/0x8E
+
+# gas trap - confuse
+F:29:0xA2/0x8F
+
+# gas trap - poison
+F:30:0xA2/0x90
+
+# gas trap - sleep
+F:31:0xA2/0x91
+
+# door
+F:32:0x81/0x8B
+
+# locked door
+F:33:0x81/0x8B
+F:34:0x81/0x8B
+F:35:0x81/0x8B
+F:36:0x81/0x8B
+F:37:0x81/0x8B
+F:38:0x81/0x8B
+F:39:0x81/0x8B
+
+# jammed door
+F:40:0x81/0x8B
+F:41:0x81/0x8B
+F:42:0x81/0x8B
+F:43:0x81/0x8B
+F:44:0x81/0x8B
+F:45:0x81/0x8B
+F:46:0x81/0x8B
+F:47:0x81/0x8B
+
+# secret door
+F:48:0x80/0x82
+
+# pile of rubble
+F:49:0x81/0x9A
+
+# magma vein
+F:50:0x81/0x83
+
+# quartz vein
+F:51:0x80/0x83
+
+# magma vein
+F:52:0x81/0x83
+
+# quartz vein
+F:53:0x80/0x83
+
+# magma vein with treasure
+F:54:0x80/0x84
+
+# quartz vein with treasure
+F:55:0x80/0x84
+
+# granite wall
+F:56:0x80/0x82
+F:57:0x80/0x82
+F:58:0x80/0x82
+F:59:0x80/0x82
+
+# permanent wall
+F:60:0x80/0x95
+F:61:0x80/0x95
+F:62:0x80/0x95
+F:63:0x80/0x95
+
+# explosive rune
+F:64:0xA2/0x87
+
+# Straight Road startpoint
+F:65:0xA3/0x9D
+
+# section of the Straight Road
+F:66:0xA3/0x97
+F:67:0xA3/0x9C
+F:68:0xA3/0x9B
+F:69:0xA3/0x9A
+F:70:0xA3/0x98
+
+# section of the Straight Road (discharged)
+F:71:0xA3/0x98
+
+# Straight Road exit
+F:72:0xA3/0x9D
+
+# corrupted section of the Straight Road
+F:73:0xA3/0x99
+
+# Building
+F:74:0x81/0x91
+
+# permanent wall
+F:75:0x80/0x95
+F:76:0x80/0x95
+F:77:0x80/0x95
+F:78:0x80/0x95
+
+# Deep water
+F:83:0xD2/0x83
+
+# stream of shallow water
+F:84:0xD2/0x81
+
+# pool of deep lava
+F:85:0xCB/0x89
+
+# stream of shallow lava
+F:86:0xCB/0x88
+
+# dark pit
+F:87:0x81/0x80
+
+# dirt
+F:88:0xCB/0x84
+
+# patch of grass
+F:89:0xD0/0x8E
+
+# ice
+F:90:0xCF/0x81
+
+# sand
+F:91:0xCF/0x8E
+
+# dead tree
+F:92:0xCF/0x85
+
+# ash
+F:93:0xCF/0x95
+
+# mud
+F:94:0xCF/0x8D
+
+# ice wall
+F:95:0xD0/0x88
+
+# tree
+F:96:0xCB/0x86
+
+# mountain chain
+F:97:0xCB/0x87
+
+# sandwall
+F:98:0xD0/0x87
+F:99:0xD0/0x87
+
+# sandwall with treasure
+F:100:0xD0/0x8A
+
+# high mountain chain
+F:101:0xCB/0x87
+
+# nether mist
+F:102:0xC5/0x8C
+
+# Between gate
+F:160:0x8A/0x9D
+
+# Altar of Forests
+F:161:0xD1/0x85
+
+# Altar of Water
+F:162:0xD1/0x86
+
+# Altar of Earth
+F:163:0xD1/0x8E
+
+# Altar of Darkness
+F:164:0xD1/0x88
+
+# Altar of Moon
+F:165:0xD1/0x89
+
+# Altar of Sun
+F:166:0xD1/0x8C
+
+# Altar of Rage
+F:167:0xD1/0x8A
+
+# Altar of Winds
+F:168:0xD1/0x8B
+
+# Altar of Stars
+F:169:0xD1/0x8D
+
+# Altar of Being
+F:170:0xD1/0x87
+
+# Altar of Randomness
+F:171:0xD1/0x8F
+
+# floor
+F:172:0x80/0x80
+
+# Underground Tunnel
+F:173:0xCF/0x97
+
+# stream of tainted water
+F:174:0xD2/0x82
+
+# monster trap
+F:175:0x82/0x94
+
+# Between gate
+F:176:0x8A/0x9D
+
+# lava wall
+F:177:0xD0/0x86
+
+# Great Fire
+F:178:0xD1/0x90
+
+# Path to next area
+F:179:0xCF/0x9C
+
+# Path to previous area
+F:180:0xCF/0x9B
+
+# field
+F:181:0xCF/0x8A
+
+# Ekkaia, the Encircling Sea
+F:182:0xD2/0x84
+
+# pool of deep water
+F:187:0xD2/0x80
+
+# glass wall
+F:188:0xD0/0x89
+
+# illusion wall
+F:189:0xD0/0x8C
+
+# Grass roof
+F:190:0xD0/0x8F
+
+# grass roof top
+F:191:0xD0/0x8F
+
+# grass roof chimney
+F:192:0xD0/0x8F
+
+# brick roof
+F:193:0xD0/0x90
+
+# brick roof top
+F:194:0xD0/0x90
+
+# brick roof chimney
+F:195:0xD0/0x90
+
+# window
+F:196:0xD0/0x91
+
+# small window
+F:197:0xD0/0x92
+
+# rain barrel
+F:198:0xD0/0x93
+
+# grass with flowers
+F:199:0xD0/0x8D
+
+# cobblestone road
+F:200:0x82/0x8A
+
+# cobblestone with outlet
+F:201:0x82/0x8A
+
+# small tree
+F:202:0xD0/0x8B
+
+# town
+F:203:0xD0/0x94
+
+# Underground Tunnel
+F:204:0xD0/0x95
+
+# a blazing fire
+F:205:0xD1/0x84
+
+# pile of rubble
+F:206:0x81/0x9A
+
+# ethereal wall
+F:214:0x80/0x80
+
+# glacial wall
+F:215:0xD0/0x88
+
+# something
+K:0:0x80/0x80
+
+# Blindness
+K:1:0xBA/0x81
+
+# Paranoia
+K:2:0xBA/0x81
+
+# Confusion
+K:3:0xBA/0x81
+
+# Hallucination
+K:4:0xBA/0x81
+
+# Cure Poison
+K:5:0xBA/0x81
+
+# Cure Blindness
+K:6:0xBA/0x81
+
+# Cure Paranoia
+K:7:0xBA/0x81
+
+# Cure Confusion
+K:8:0xBA/0x81
+
+# Weakness
+K:9:0xBA/0x81
+
+# Unhealth
+K:10:0xBA/0x81
+
+# Restore Constitution
+K:11:0xBA/0x81
+
+# Restoring
+K:12:0xBA/0x81
+
+# Stupidity
+K:13:0xBA/0x81
+
+# Naivety
+K:14:0xBA/0x81
+
+# Poison
+K:15:0xBA/0x81
+
+# Sickness
+K:16:0xBA/0x81
+
+# Paralysis
+K:17:0xBA/0x81
+
+# Restore Strength
+K:18:0xBA/0x81
+
+# Disease
+K:19:0xBA/0x81
+
+# Cure Serious Wounds
+K:20:0xBA/0x81
+
+# & Ration~ of Food
+K:21:0x8B/0x82
+
+# & Hard Biscuit~
+K:22:0x8B/0x82
+
+# & Strip~ of Venison
+K:23:0x8B/0x82
+
+# & Slime Mold~
+K:24:0x8A/0x9F
+
+# & Piece~ of Elvish Waybread
+K:25:0x8B/0x80
+
+# & Pint~ of Fine Ale
+K:26:0x8A/0x95
+
+# & Pint~ of Fine Wine
+K:27:0x8A/0x96
+
+# & Mattock~
+K:28:0xCD/0x80
+
+# The Blue Stone 'Toris Mejistos'
+K:29:0xB6/0x89
+
+# & Broken Dagger~
+K:30:0x89/0x83
+
+# & Bastard Sword~
+K:31:0x89/0x85
+
+# & Scimitar~
+K:32:0x89/0x85
+
+# & Tulwar~
+K:33:0x89/0x84
+
+# & Broad Sword~
+K:34:0x89/0x85
+
+# & Short Sword~
+K:35:0x89/0x84
+
+# & Blade~ of Chaos
+K:36:0x89/0x87
+
+# & Two-Handed Sword~
+K:37:0x89/0x85
+
+# & Main Gauche~
+K:38:0x89/0x83
+
+# & Cutlass~
+K:39:0x89/0x84
+
+# & Executioner's Sword~
+K:40:0x89/0x86
+
+# & Katana~
+K:41:0x89/0x85
+
+# & Long Sword~
+K:42:0x89/0x85
+
+# & Dagger~
+K:43:0x89/0x83
+
+# & Rapier~
+K:44:0x89/0x84
+
+# & Sabre~
+K:45:0x89/0x84
+
+# & Small Sword~
+K:46:0x89/0x84
+
+# & Broken Sword~
+K:47:0x89/0x83
+
+# & Ball-and-Chain~
+K:48:0x89/0x88
+
+# & Whip~
+K:49:0x89/0x89
+
+# & Flail~
+K:50:0x89/0x8B
+
+# & Two-Handed Flail~
+K:51:0x89/0x8B
+
+# & Morning Star~
+K:52:0x89/0x8B
+
+# & Mace~
+K:53:0x89/0x8C
+
+# & Quarterstaff~
+K:54:0x89/0x8E
+
+# & War Hammer~
+K:55:0x89/0x8F
+
+# & Lead-Filled Mace~
+K:56:0x89/0x8C
+
+# & Mace~ of Disruption
+K:57:0x89/0x8D
+
+# & Lucerne Hammer~
+K:58:0x89/0x90
+
+# & Beaked Axe~
+K:59:0x89/0x90
+
+# & Glaive~
+K:60:0x89/0x90
+
+# & Halberd~
+K:61:0x89/0x90
+
+# & Awl-Pike~
+K:62:0x89/0x91
+
+# & Pike~
+K:63:0x89/0x91
+
+# & Spear~
+K:64:0x89/0x91
+
+# & Trident~
+K:65:0x89/0x92
+
+# & Lance~
+K:66:0x89/0x93
+
+# & Great Axe~
+K:67:0x89/0x90
+
+# & Battle Axe~
+K:68:0x89/0x90
+
+# & Lochaber Axe~
+K:69:0x89/0x90
+
+# & Broad Axe~
+K:70:0x89/0x90
+
+# & Scythe~
+K:71:0x89/0x94
+
+# & Scythe~ of Slicing
+K:72:0x89/0x94
+
+# & Short Bow~
+K:73:0x89/0x95
+
+# & Long Bow~
+K:74:0x89/0x96
+
+# & Light Crossbow~
+K:75:0x89/0x97
+
+# & Heavy Crossbow~
+K:76:0x89/0x98
+
+# & Sling~
+K:77:0x89/0x99
+
+# & Arrow~
+K:78:0x89/0x9A
+
+# & Seeker Arrow~
+K:79:0x89/0x9B
+
+# & Bolt~
+K:80:0x89/0x9C
+
+# & Seeker Bolt~
+K:81:0x89/0x9D
+
+# & Rounded Pebble~
+K:82:0x89/0x9E
+
+# & Iron Shot~
+K:83:0x89/0x9F
+
+# & Shovel~
+K:84:0x8A/0x98
+
+# & Gnomish Shovel~
+K:85:0x8B/0x8F
+
+# & Dwarven Shovel~
+K:86:0x8B/0x90
+
+# & Pick~
+K:87:0x8A/0x97
+
+# & Orcish Pick~
+K:88:0x8B/0x8D
+
+# & Dwarven Pick~
+K:89:0x8B/0x8E
+
+# & Elven Cloak~
+K:90:0x88/0x81
+
+# & Pair~ of Soft Leather Boots
+K:91:0x88/0x89
+
+# & Pair~ of Hard Leather Boots
+K:92:0x88/0x8A
+
+# & Pair~ of Metal Shod Boots
+K:93:0x88/0x8B
+
+# & Hard Leather Cap~
+K:94:0x88/0x82
+
+# & Metal Cap~
+K:95:0x88/0x83
+
+# & Iron Helm~
+K:96:0x88/0x84
+
+# & Steel Helm~
+K:97:0x88/0x85
+
+# & Iron Crown~
+K:98:0x88/0x86
+
+# & Golden Crown~
+K:99:0x88/0x87
+
+# & Jewel Encrusted Crown~
+K:100:0x88/0x88
+
+# & Robe~
+K:101:0x88/0x95
+
+# & Filthy Rag~
+K:102:0x88/0x94
+
+# Soft Leather Armour~
+K:103:0x88/0x96
+
+# Soft Studded Leather~
+K:104:0x88/0x96
+
+# Hard Leather Armour~
+K:105:0x88/0x97
+
+# Hard Studded Leather~
+K:106:0x88/0x97
+
+# Leather Scale Mail~
+K:107:0x88/0x98
+
+# Metal Scale Mail~
+K:108:0x88/0x98
+
+# Chain Mail~
+K:109:0x88/0x99
+
+# Rusty Chain Mail~
+K:110:0x88/0x9A
+
+# Augmented Chain Mail~
+K:111:0x88/0x99
+
+# Bar Chain Mail~
+K:112:0x88/0x99
+
+# Metal Brigandine Armour~
+K:113:0x88/0x99
+
+# Partial Plate Armour~
+K:114:0x88/0x9B
+
+# Metal Lamellar Armour~
+K:115:0x88/0x9B
+
+# Full Plate Armour~
+K:116:0xCD/0x82
+
+# Ribbed Plate Armour~
+K:117:0x88/0x9B
+
+# Adamantite Plate Mail~
+K:118:0xA3/0x96
+
+# Mithril Plate Mail~
+K:119:0x88/0x9C
+
+# Mithril Chain Mail~
+K:120:0x88/0x9C
+
+# Double Chain Mail~
+K:121:0x88/0x99
+
+# & Shield~ of Deflection
+K:122:0x88/0x93
+
+# & Cloak~
+K:123:0x88/0x80
+
+# & Shadow Cloak~
+K:124:0x88/0x81
+
+# & Set~ of Leather Gloves
+K:125:0x88/0x8C
+
+# & Set~ of Gauntlets
+K:126:0x88/0x8D
+
+# & Set~ of Cesti
+K:127:0x88/0x8E
+
+# & Small Leather Shield~
+K:128:0x88/0x8F
+
+# & Large Leather Shield~
+K:129:0x88/0x90
+
+# & Small Metal Shield~
+K:130:0x88/0x91
+
+# & Large Metal Shield~
+K:131:0x88/0x92
+
+# Strength
+K:132:0xB5/0x81
+
+# Dexterity
+K:133:0xB5/0x81
+
+# Constitution
+K:134:0xB5/0x81
+
+# Intelligence
+K:135:0xB5/0x81
+
+# Speed
+K:136:0xB5/0x83
+
+# Searching
+K:137:0xB5/0x80
+
+# Teleportation
+K:138:0xB5/0x80
+
+# Slow Digestion
+K:139:0xB5/0x80
+
+# Fire Resistance
+K:140:0xB5/0x80
+
+# Cold Resistance
+K:141:0xB5/0x80
+
+# Levitation
+K:142:0xB5/0x80
+
+# Poison Resistance
+K:143:0xB5/0x82
+
+# Free Action
+K:144:0xB5/0x80
+
+# Weakness
+K:145:0xB5/0x80
+
+# Flames
+K:146:0xB5/0x82
+
+# Acid
+K:147:0xB5/0x82
+
+# Ice
+K:148:0xB5/0x82
+
+# Woe
+K:149:0xB5/0x82
+
+# Stupidity
+K:150:0xB5/0x80
+
+# Damage
+K:151:0xB5/0x81
+
+# Accuracy
+K:152:0xB5/0x81
+
+# Protection
+K:153:0xB5/0x80
+
+# Aggravate Monster
+K:154:0xB5/0x80
+
+# See Invisible
+K:155:0xB5/0x81
+
+# Sustain Strength
+K:156:0xB5/0x81
+
+# Sustain Intelligence
+K:157:0xB5/0x81
+
+# Sustain Wisdom
+K:158:0xB5/0x81
+
+# Sustain Constitution
+K:159:0xB5/0x81
+
+# Sustain Dexterity
+K:160:0xB5/0x81
+
+# Sustain Charisma
+K:161:0xB5/0x81
+
+# Slaying
+K:162:0xB5/0x81
+
+# Brilliance
+K:163:0xB6/0x9F
+
+# Charisma
+K:164:0xB6/0x9F
+
+# Searching
+K:165:0xB6/0x9E
+
+# Teleportation
+K:166:0xB6/0x9E
+
+# Slow Digestion
+K:167:0xB6/0x9E
+
+# Acid Resistance
+K:168:0xB6/0x9E
+
+# Adornment
+K:169:0xB6/0x9E
+
+# Double Ring Mail~
+K:170:0xCD/0x83
+
+# the Magi
+K:171:0xB6/0x80
+
+# Doom
+K:172:0xB6/0x80
+
+# Enchant Weapon To-Hit
+K:173:0x86/0x80
+
+# Enchant Weapon To-Dam
+K:174:0x86/0x80
+
+# Enchant Armor
+K:175:0x86/0x80
+
+# Identify
+K:176:0x86/0x80
+
+# *Identify*
+K:177:0x86/0x82
+
+# Rumour
+K:178:0x86/0x80
+
+# Chaos
+K:179:0x86/0x80
+
+# Remove Curse
+K:180:0x86/0x80
+
+# Light
+K:181:0x86/0x80
+
+# Fire
+K:182:0x86/0x80
+
+# Ice
+K:183:0x86/0x80
+
+# Summon Monster
+K:184:0x86/0x80
+
+# Phase Door
+K:185:0x86/0x80
+
+# Teleportation
+K:186:0x86/0x80
+
+# Teleport Level
+K:187:0x86/0x80
+
+# Monster Confusion
+K:188:0x86/0x80
+
+# Magic Mapping
+K:189:0x86/0x80
+
+# Rune of Protection
+K:190:0x86/0x82
+
+# *Remove Curse*
+K:191:0x86/0x82
+
+# Treasure Detection
+K:192:0x86/0x80
+
+# Object Detection
+K:193:0x86/0x80
+
+# Trap Detection
+K:194:0x86/0x80
+
+# & Sheaf Arrow~
+K:195:0xCD/0x84
+
+# & Mithril Shot~
+K:196:0xCD/0x85
+
+# Door
+K:197:0x86/0x80
+
+# Acquirement
+K:198:0x86/0x80
+
+# *Acquirement*
+K:199:0x86/0x82
+
+# Mass Genocide
+K:200:0x86/0x82
+
+# Detect Invisible
+K:201:0x86/0x80
+
+# Aggravate Monster
+K:202:0x86/0x80
+
+# Trap Creation
+K:203:0x86/0x80
+
+# Trap
+K:204:0x86/0x80
+
+# Artifact Creation
+K:205:0x86/0x82
+
+# Recharging
+K:206:0x86/0x81
+
+# Genocide
+K:207:0x86/0x81
+
+# Darkness
+K:208:0x86/0x80
+
+# Protection from Evil
+K:209:0x86/0x81
+
+# Satisfy Hunger
+K:210:0x86/0x80
+
+# Dispel Undead
+K:211:0x86/0x81
+
+# *Enchant Weapon*
+K:212:0x86/0x82
+
+# Curse Weapon
+K:213:0x86/0x82
+
+# *Enchant Armor*
+K:214:0x86/0x82
+
+# Curse Armor
+K:215:0x86/0x82
+
+# Summon Undead
+K:216:0x86/0x80
+
+# Blessing
+K:217:0x86/0x80
+
+# Holy Chant
+K:218:0x86/0x80
+
+# Holy Prayer
+K:219:0x86/0x81
+
+# Word of Recall
+K:220:0x86/0x80
+
+# *Destruction*
+K:221:0x86/0x82
+
+# Slime Mold Juice
+K:222:0xBC/0x85
+
+# Apple Juice
+K:223:0xBC/0x85
+
+# Water
+K:224:0xBC/0x85
+
+# Strength
+K:225:0xBC/0x86
+
+# Weakness
+K:226:0xBC/0x85
+
+# Restore Strength
+K:227:0xBC/0x86
+
+# Intelligence
+K:228:0xBC/0x86
+
+# Stupidity
+K:229:0xBC/0x85
+
+# Restore Intelligence
+K:230:0xBC/0x86
+
+# Wisdom
+K:231:0xBC/0x86
+
+# Naivety
+K:232:0xBC/0x85
+
+# Restore Wisdom
+K:233:0xBC/0x86
+
+# Charisma
+K:234:0xBC/0x86
+
+# Ugliness
+K:235:0xBC/0x86
+
+# Restore Charisma
+K:236:0xBC/0x86
+
+# Curing
+K:237:0xBC/0x86
+
+# Invulnerability
+K:238:0xBC/0x86
+
+# New Life
+K:239:0xBC/0x86
+
+# Cure Serious Wounds
+K:240:0xBC/0x85
+
+# Cure Critical Wounds
+K:241:0xBC/0x85
+
+# Healing
+K:242:0xBC/0x85
+
+# Constitution
+K:243:0xBC/0x86
+
+# Experience
+K:244:0xBC/0x87
+
+# Sleep
+K:245:0xBC/0x85
+
+# Blindness
+K:246:0xBC/0x85
+
+# Booze
+K:247:0xBC/0x85
+
+# Poison
+K:248:0xBC/0x85
+
+# Speed
+K:249:0xBC/0x85
+
+# Slowness
+K:250:0xBC/0x85
+
+# Dexterity
+K:251:0xBC/0x86
+
+# Restore Dexterity
+K:252:0xBC/0x86
+
+# Restore Constitution
+K:253:0xBC/0x86
+
+# Lose Memories
+K:254:0xBC/0x85
+
+# Salt Water
+K:255:0xBC/0x85
+
+# Enlightenment
+K:256:0xBC/0x85
+
+# Heroism
+K:257:0xBC/0x85
+
+# Berserk Strength
+K:258:0xBC/0x85
+
+# Boldness
+K:259:0xBC/0x85
+
+# Restore Life Levels
+K:260:0xBC/0x87
+
+# Resist Heat
+K:261:0xBC/0x85
+
+# Resist Cold
+K:262:0xBC/0x85
+
+# Detect Invisible
+K:263:0xBC/0x85
+
+# Slow Poison
+K:264:0xBC/0x85
+
+# Neutralise Poison
+K:265:0xBC/0x85
+
+# Restore Mana
+K:266:0xBC/0x86
+
+# Infra-vision
+K:267:0xBC/0x85
+
+# Resistance
+K:268:0xBC/0x85
+
+# Light
+K:269:0xB7/0x8F
+
+# Tame Monster
+K:270:0xB7/0x8F
+
+# Frost Bolts
+K:271:0xB7/0x8F
+
+# Fire Bolts
+K:272:0xB7/0x90
+
+# Stone to Mud
+K:273:0xB7/0x8F
+
+# Polymorph
+K:274:0xB7/0x8F
+
+# Heal Monster
+K:275:0xB7/0x8F
+
+# Haste Monster
+K:276:0xB7/0x8F
+
+# Slow Monster
+K:277:0xB7/0x8F
+
+# Confuse Monster
+K:278:0xB7/0x8F
+
+# Sleep Monster
+K:279:0xB7/0x8F
+
+# Drain Life
+K:280:0xB7/0x91
+
+# Trap
+K:281:0xB7/0x8F
+
+# Magic Missile
+K:282:0xB7/0x8F
+
+# Clone Monster
+K:283:0xB7/0x90
+
+# Scare Monster
+K:284:0xB7/0x90
+
+# Teleport Other
+K:285:0xB7/0x8F
+
+# Disarming
+K:286:0xB7/0x8F
+
+# Lightning Balls
+K:287:0xB7/0x90
+
+# Cold Balls
+K:288:0xB7/0x90
+
+# Fire Balls
+K:289:0xB7/0x91
+
+# Stinking Cloud
+K:290:0xB7/0x8F
+
+# Acid Balls
+K:291:0xB7/0x91
+
+# Wonder
+K:292:0xB7/0x8F
+
+# & Flight Arrow~
+K:293:0xCD/0x86
+
+# Acid Bolts
+K:294:0xB7/0x90
+
+# Dragon's Flame
+K:295:0xB7/0x91
+
+# Dragon's Frost
+K:296:0xB7/0x91
+
+# Dragon's Breath
+K:297:0xB7/0x91
+
+# Annihilation
+K:298:0xB7/0x91
+
+# Rockets
+K:299:0xB7/0x91
+
+# Trap Location
+K:300:0xB9/0x99
+
+# Treasure Location
+K:301:0xB9/0x99
+
+# Object Location
+K:302:0xB9/0x99
+
+# Teleportation
+K:303:0xB9/0x99
+
+# Earthquakes
+K:304:0xB9/0x9A
+
+# Summoning
+K:305:0xB9/0x99
+
+# Light
+K:306:0xB9/0x99
+
+# *Destruction*
+K:307:0xB9/0x9B
+
+# Starlight
+K:308:0xB9/0x99
+
+# Haste Monsters
+K:309:0xB9/0x99
+
+# Slow Monsters
+K:310:0xB9/0x99
+
+# Sleep Monsters
+K:311:0xB9/0x99
+
+# Cure Light Wounds
+K:312:0xB9/0x99
+
+# Detect Invisible
+K:313:0xB9/0x99
+
+# Speed
+K:314:0xB9/0x9A
+
+# Slowness
+K:315:0xB9/0x99
+
+# Door
+K:316:0xB9/0x99
+
+# Remove Curse
+K:317:0xB9/0x9A
+
+# Detect Evil
+K:318:0xB9/0x99
+
+# Curing
+K:319:0xB9/0x9A
+
+# Dispel Evil
+K:320:0xB9/0x9B
+
+# Probing
+K:321:0xB9/0x9A
+
+# Darkness
+K:322:0xB9/0x99
+
+# Genocide
+K:323:0xB9/0x9B
+
+# Power
+K:324:0xB9/0x9C
+
+# the Magi
+K:325:0xB9/0x9C
+
+# Perception
+K:326:0xB9/0x99
+
+# Holiness
+K:327:0xB9/0x9C
+
+# Enlightenment
+K:328:0xB9/0x9A
+
+# Healing
+K:329:0xB9/0x9C
+
+# [Call of the West]
+K:330:0xA3/0x8A
+
+# [Light of Valinor]
+K:331:0xA3/0x8A
+
+# [Divine Mastery]
+K:332:0xA3/0x8A
+
+# [Words of Power]
+K:333:0xA3/0x8A
+
+# [Apprentice Handbook]
+K:334:0xA3/0x8C
+
+# [Mystical Words]
+K:335:0xA3/0x8C
+
+# [Arcane Chants]
+K:336:0xA3/0x8C
+
+# [Locus of Force]
+K:337:0xA3/0x8C
+
+# & Small wooden chest~
+K:338:0x80/0x96
+
+# & Large wooden chest~
+K:339:0x80/0x97
+
+# & Small iron chest~
+K:340:0x80/0x98
+
+# & Large iron chest~
+K:341:0x80/0x99
+
+# & Small steel chest~
+K:342:0x80/0x9A
+
+# & Large steel chest~
+K:343:0x80/0x9B
+
+# & Ruined chest~
+K:344:0x80/0x9C
+
+# & Iron Spike~
+K:345:0x8B/0x84
+
+# & Wooden Torch~
+K:346:0x8B/0x86
+
+# & Brass Lantern~
+K:347:0x8B/0x85
+
+# & Flask~ of oil
+K:348:0xBC/0x90
+
+# & Empty Bottle~
+K:349:0x8A/0x99
+
+# Havoc
+K:350:0xB8/0x94
+
+# Door
+K:351:0xB8/0x94
+
+# Trap Location
+K:352:0xB8/0x94
+
+# Probing
+K:353:0xB8/0x97
+
+# Recall
+K:354:0xB8/0x96
+
+# Illumination
+K:355:0xB8/0x95
+
+# Light
+K:356:0xB8/0x94
+
+# Lightning Bolts
+K:357:0xB8/0x94
+
+# Frost Bolts
+K:358:0xB8/0x95
+
+# Fire Bolts
+K:359:0xB8/0x95
+
+# Polymorph
+K:360:0xB8/0x95
+
+# Slow Monster
+K:361:0xB8/0x95
+
+# Sleep Monster
+K:362:0xB8/0x95
+
+# Drain Life
+K:363:0xB8/0x97
+
+# Teleport Other
+K:364:0xB8/0x96
+
+# Disarming
+K:365:0xB8/0x95
+
+# Lightning Balls
+K:366:0xB8/0x96
+
+# Cold Balls
+K:367:0xB8/0x96
+
+# Fire Balls
+K:368:0xB8/0x97
+
+# Acid Balls
+K:369:0xB8/0x97
+
+# Acid Bolts
+K:370:0xB8/0x95
+
+# Enlightenment
+K:371:0xB8/0x97
+
+# Perception
+K:372:0xB8/0x96
+
+# Curing
+K:373:0xB8/0x97
+
+# Healing
+K:374:0xB8/0x97
+
+# Detection
+K:375:0xB8/0x95
+
+# Restoration
+K:376:0xB8/0x97
+
+# Speed
+K:377:0xB8/0x97
+
+# [Inner Void]
+K:378:0xA3/0x8E
+
+# [Lurkings of the Night]
+K:379:0xA3/0x8E
+
+# [Beings of Darkness]
+K:380:0xA3/0x8E
+
+# [Material Shadow]
+K:381:0xA3/0x8E
+
+# [Sign of Chaos]
+K:383:0xA3/0x90
+
+# [Chaos Mastery]
+K:384:0xA3/0x90
+
+# [Chaos Channels]
+K:385:0xA3/0x91
+
+# [Armageddon Tome]
+K:386:0xA3/0x91
+
+# [Nether Openings]
+K:387:0xA3/0x92
+
+# [Unholy Blessings]
+K:388:0xA3/0x92
+
+# & Firestone~
+K:389:0x8B/0x88
+
+# & Small Firestone~
+K:390:0x8B/0x89
+
+# & Broken Skull~
+K:391:0x8B/0x8A
+
+# & Broken Bone~
+K:392:0x8B/0x8B
+
+# & Canine Skeleton~
+K:393:0x8B/0x87
+
+# & Rodent Skeleton~
+K:394:0x8B/0x87
+
+# & Human Skeleton~
+K:395:0x8B/0x87
+
+# & Dwarf Skeleton~
+K:396:0x8B/0x87
+
+# & Elf Skeleton~
+K:397:0x8B/0x87
+
+# & Gnome Skeleton~
+K:398:0x8B/0x87
+
+# & Great Hammer~
+K:399:0xCD/0x87
+
+# Black Dragon Scale Mail~
+K:400:0x88/0x9F
+
+# Blue Dragon Scale Mail~
+K:401:0x88/0x9D
+
+# White Dragon Scale Mail~
+K:402:0x88/0x9E
+
+# Red Dragon Scale Mail~
+K:403:0x89/0x81
+
+# Green Dragon Scale Mail~
+K:404:0x89/0x80
+
+# Multi-Hued Dragon Scale Mail~
+K:405:0x89/0x82
+
+# Pseudo Dragon Scale Mail~
+K:406:0xBB/0x9C
+
+# Law Dragon Scale Mail~
+K:407:0x88/0x9F
+
+# Bronze Dragon Scale Mail~
+K:408:0x88/0x96
+
+# Gold Dragon Scale Mail~
+K:409:0x88/0x9C
+
+# Chaos Dragon Scale Mail~
+K:410:0x89/0x80
+
+# Balance Dragon Scale Mail~
+K:411:0x88/0x99
+
+# Power Dragon Scale Mail~
+K:412:0xA2/0x9E
+
+# & Dragon Helm~
+K:413:0xA2/0x9D
+
+# & Dragon Shield~
+K:414:0xA2/0x9C
+
+# Death
+K:415:0xBC/0x88
+
+# Ruination
+K:416:0xBC/0x87
+
+# Detonations
+K:417:0xBC/0x87
+
+# Augmentation
+K:418:0xBC/0x87
+
+# *Healing*
+K:419:0xBC/0x87
+
+# Life
+K:420:0xBC/0x88
+
+# Self Knowledge
+K:421:0xBC/0x87
+
+# *Enlightenment*
+K:422:0xBC/0x88
+
+# [Necromantic Incantations]
+K:423:0xA3/0x92
+
+# [Curses of Angmar]
+K:424:0xA3/0x92
+
+# Fear Resistance
+K:425:0xB5/0x81
+
+# Light and Darkness Resistance
+K:426:0xB5/0x81
+
+# Nether Resistance
+K:427:0xB5/0x81
+
+# Nexus Resistance
+K:428:0xB5/0x81
+
+# Sound Resistance
+K:429:0xB5/0x81
+
+# Confusion Resistance
+K:430:0xB5/0x81
+
+# Shard Resistance
+K:431:0xB5/0x81
+
+# Disenchantment Resistance
+K:432:0xB5/0x81
+
+# Chaos Resistance
+K:433:0xB5/0x81
+
+# Blindness Resistance
+K:434:0xB5/0x81
+
+# Lordly Protection
+K:435:0xB5/0x81
+
+# Extra Attacks
+K:436:0xB5/0x81
+
+# Cure Light Wounds
+K:437:0xBC/0x85
+
+# Clumsiness
+K:438:0xBC/0x85
+
+# Sickliness
+K:439:0xBC/0x85
+
+# Map of Bree
+K:440:0xD8/0x81
+
+# Map of Gondolin
+K:441:0xD8/0x81
+
+# Map of Lothlorien
+K:442:0xD8/0x81
+
+# Map of Minas Anor
+K:443:0xD8/0x81
+
+# & Silver Arrow~
+K:465:0xCE/0x91
+
+# & Silver Bolt~
+K:466:0xCE/0x92
+
+# Lightning Resistance
+K:467:0x87/0x80
+
+# Wisdom
+K:468:0x87/0x80
+
+# Regeneration
+K:469:0x87/0x80
+
+# Infravision
+K:470:0x87/0x80
+
+# Devotion
+K:471:0x87/0x80
+
+# Weaponmastery
+K:472:0x87/0x80
+
+# Trickery
+K:473:0x87/0x80
+
+# ESP
+K:474:0x87/0x80
+
+# Sustenance
+K:475:0x87/0x80
+
+# Palantir
+K:476:0xD8/0x8F
+
+# Elfstone 'Elessar'
+K:477:0xB6/0x8F
+
+# Jewel 'Evenstar'
+K:478:0xB6/0x90
+
+# Ring of Durin
+K:479:0xB5/0x8E
+
+# copper
+K:480:0x80/0x8B
+K:481:0x80/0x8B
+K:482:0x80/0x8B
+
+# silver
+K:483:0x80/0x8C
+K:484:0x80/0x8C
+K:485:0x80/0x8C
+
+# garnets
+K:486:0x80/0x8F
+K:487:0x80/0x8F
+
+# gold
+K:488:0x80/0x8D
+K:489:0x80/0x8D
+K:490:0x80/0x8D
+
+# opals
+K:491:0x80/0x90
+
+# sapphires
+K:492:0x80/0x91
+
+# rubies
+K:493:0x80/0x92
+
+# diamonds
+K:494:0x80/0x93
+
+# emeralds
+K:495:0x80/0x94
+
+# mithril
+K:496:0x80/0x8E
+
+# adamantite
+K:497:0xA3/0x95
+
+# & Mighty Hammer~
+K:498:0x87/0x9A
+
+# & Massive Iron Crown~
+K:499:0x87/0x9B
+
+# & Phial~
+K:500:0x87/0x9D
+
+# & Star~
+K:501:0x87/0x9E
+
+# & Arkenstone~
+K:502:0x87/0x9F
+
+# & Amulet~
+K:503:0xB6/0x82
+K:504:0xB6/0x83
+
+# & Necklace~
+K:505:0xB6/0x84
+
+# & Ring~
+K:506:0xB5/0x83
+K:507:0xB5/0x83
+K:508:0xB5/0x84
+K:509:0xB5/0x85
+K:510:0xB5/0x86
+K:511:0xB5/0x87
+
+# [Rites of Initiation]
+K:512:0xBC/0x91
+
+# [Ways of War]
+K:513:0xBC/0x91
+
+# [Divine Retribution]
+K:514:0xBC/0x92
+
+# [Essence of Fury]
+K:515:0xBC/0x92
+
+# [Novice Crafts]
+K:516:0xBC/0x95
+
+# [Arcane Channels]
+K:517:0xBC/0x95
+
+# [Sigils of Wizardry]
+K:518:0xBC/0x95
+
+# [Mana Focus]
+K:519:0xBC/0x95
+
+# Reflection
+K:520:0xB6/0x80
+
+# Anti-Magic
+K:521:0xB6/0x80
+
+# Anti-Teleportation
+K:522:0xB6/0x80
+
+# Resistance
+K:523:0xB6/0x80
+
+# & Zweihander~
+K:524:0xCD/0x88
+
+# & Dwarven Lantern~
+K:525:0xD8/0x86
+
+# Splint Mail~
+K:526:0xCD/0x8A
+
+# & Everburning Torch~
+K:527:0xD8/0x87
+
+# & Trifurcate Spear~
+K:528:0xCD/0x96
+
+# & Three Piece Rod~
+K:529:0xCD/0x8C
+
+# & Feanorian Lamp~
+K:530:0xD8/0x85
+
+# & Fur Cloak~
+K:531:0xCD/0x8E
+
+# Potion: Water Curing
+K:532:0xBC/0x84
+
+# & Hatchet~
+K:533:0xCD/0x90
+
+# Rhino Hide Armour~
+K:535:0xCD/0x91
+
+# Leather Jacket~
+K:536:0xCD/0x92
+
+# & Sickle~
+K:537:0xCD/0x93
+
+# [Psychoportation]
+K:538:0xA3/0x88
+
+# [Clairsentience]
+K:539:0xA3/0x88
+
+# [Telekinesis]
+K:540:0xA3/0x89
+
+# [Empathy]
+K:541:0xA3/0x89
+
+# & Club~
+K:542:0xCD/0x99
+
+# & Broad Spear~
+K:543:0xCD/0x9A
+
+# & Khopesh~
+K:544:0xCD/0x9B
+
+# & Flamberge~
+K:545:0xCD/0x9C
+
+# & Claymore~
+K:546:0xCD/0x9D
+
+# & Espadon~
+K:547:0xCD/0x9E
+
+# & Great Scimitar~
+K:548:0xCD/0x9F
+
+# Trapping Kit: Arrow
+K:549:0xD7/0x84
+
+# Trapping Kit: Bolt
+K:550:0xD7/0x83
+
+# & Fauchard~
+K:551:0xCE/0x82
+
+# & Guisarme~
+K:552:0xCE/0x83
+
+# & Heavy Lance~
+K:553:0xCE/0x84
+
+# & Basillard~
+K:554:0xCE/0x85
+
+# Trapping Kit: Catapult
+K:555:0xD7/0x82
+
+# Ring Mail~
+K:556:0xCE/0x87
+
+# Cord Armour~
+K:557:0xCE/0x88
+
+# Paper Armour~
+K:558:0xCE/0x89
+
+# Padded Armour~
+K:559:0xCE/0x8A
+
+# Trap Kit: Fumes
+K:560:0xD7/0x80
+
+# Stone and Hide Armour~
+K:561:0xCE/0x8C
+
+# Trap Kit: Magic
+K:562:0xD7/0x81
+
+# Trap Kit: Device
+K:563:0xD7/0x85
+
+# Scroll: Nothing
+K:564:0x86/0x80
+
+# Poison
+K:565:0xD9/0x82
+
+# Wand: Nothing
+K:566:0xB7/0x90
+
+# Ring: Nothing
+K:567:0xB5/0x80
+
+# Staff: Nothing
+K:568:0xB8/0x96
+
+# Rod Tip: Nothing
+K:569:0xB8/0x95
+
+# Explosion
+K:570:0xD9/0x82
+
+# Teleport
+K:571:0xD9/0x82
+
+# Amulet: Nothing
+K:572:0x87/0x80
+
+# & Blood~ of Life
+K:573:0x87/0x88
+
+# Cold
+K:574:0xD9/0x82
+
+# Fire
+K:575:0xD9/0x82
+
+# Acid
+K:576:0xD9/0x82
+
+# Mage Staff
+K:577:0xCE/0x97
+
+# Lightning
+K:578:0xB5/0x81
+
+# Life
+K:579:0xD9/0x82
+
+# Confusion
+K:580:0xD9/0x82
+
+# Light
+K:581:0xD9/0x82
+
+# Ring of F'Lar
+K:582:0xB5/0x8F
+
+# Invisibility
+K:583:0xB8/0x85
+
+# Chaos
+K:584:0xD9/0x82
+
+# Corruption
+K:585:0xB8/0x85
+
+# Invisibility
+K:586:0xB5/0x81
+
+# Time
+K:587:0xD9/0x82
+
+# Deep Thoughts
+K:588:0xD8/0x80
+
+# More Deep Thoughts
+K:589:0xD8/0x80
+
+# Compendium of Deep Thoughts
+K:590:0xD8/0x80
+
+# Artifact Lore Vol. I
+K:591:0xD8/0x80
+
+# Artifact Lore Vol. II
+K:592:0xD8/0x80
+
+# Artifact Lore Vol. III
+K:593:0xD8/0x80
+
+# Monstrous Compendium 1
+K:594:0xD8/0x80
+
+# Monstrous Compendium 2
+K:595:0xD8/0x80
+
+# Monstrous Compendium 3
+K:596:0xD8/0x80
+
+# Monstrous Compendium 4
+K:597:0xD8/0x80
+
+# Monstrous Compendium 5
+K:598:0xD8/0x80
+
+# Monstrous Compendium 6
+K:599:0xD8/0x80
+
+# Monstrous Compendium 7
+K:600:0xD8/0x80
+
+# Monstrous Compendium 8
+K:601:0xD8/0x80
+
+# Monstrous Compendium 9
+K:602:0xD8/0x80
+
+# Monstrous Compendium 10
+K:603:0xD8/0x80
+
+# Monstrous Compendium 11
+K:604:0xD8/0x80
+
+# Abomination
+K:605:0xBC/0x85
+
+# Shape of Wolf
+K:606:0xBC/0x85
+
+# Shape of Ape
+K:607:0xBC/0x85
+
+# Shape of Goat
+K:608:0xBC/0x85
+
+# Shape of Insect
+K:609:0xBC/0x85
+
+# Shape of Sparrow
+K:610:0xBC/0x85
+
+# Shape of Ent
+K:611:0xBC/0x85
+
+# Shape of Vampire
+K:612:0xBC/0x85
+
+# Shape of Spider
+K:613:0xBC/0x85
+
+# Shape of Mana ball
+K:614:0xBC/0x85
+
+# Shape of Fire cloud
+K:615:0xBC/0x85
+
+# Shape of Cold cloud
+K:616:0xBC/0x85
+
+# Shape of Chaos cloud
+K:617:0xBC/0x85
+
+# [Wolf]
+K:618:0xCE/0x93
+
+# [Ape]
+K:619:0xCE/0x93
+
+# [Goat]
+K:620:0xCE/0x93
+
+# [Insect]
+K:621:0xCE/0x93
+
+# [Sparrow]
+K:622:0xCE/0x93
+
+# [Ent]
+K:623:0xCE/0x93
+
+# [Vampire]
+K:624:0xCE/0x93
+
+# [Spider]
+K:625:0xCE/0x93
+
+# [Mana ball]
+K:626:0xCE/0x93
+
+# [Fire cloud]
+K:627:0xCE/0x93
+
+# [Cold cloud]
+K:628:0xCE/0x93
+
+# [Chaos Cloud]
+K:629:0xCE/0x93
+
+# [Ghost]
+K:630:0xCE/0x93
+
+# [Kobold]
+K:631:0xCE/0x93
+
+# [Dragon]
+K:632:0xCE/0x93
+
+# [Demon]
+K:633:0xCE/0x93
+
+# [Hound]
+K:634:0xCE/0x93
+
+# [Quylthulg]
+K:635:0xCE/0x93
+
+# [Maia]
+K:636:0xCE/0x93
+
+# [Serpent]
+K:637:0xCE/0x93
+
+# [Giant]
+K:638:0xCE/0x93
+
+# [Vala]
+K:639:0xCE/0x93
+
+# Magic
+K:640:0xD9/0x82
+
+# corpse
+K:641:0xB4/0x90
+
+# skeleton
+K:642:0xB4/0x8B
+
+# head
+K:643:0xB4/0x8E
+
+# skull
+K:644:0xB4/0x8F
+
+# raw meat
+K:645:0xB4/0x8C
+
+# Dragonrider Coat
+K:646:0xCE/0x98
+
+# Stone of Lore
+K:647:0xD8/0x90
+
+# small wooden boomerang
+K:648:0xCE/0x99
+
+# large wooden boomerang
+K:649:0xCE/0x9A
+
+# small metal boomerang
+K:650:0xCE/0x9B
+
+# large metal boomerang
+K:651:0xCE/0x9C
+
+# The Space-Time Anchor
+K:652:0xD8/0x91
+
+# Summon never-moving pet
+K:654:0x86/0x80
+
+# [Life in symbiosis]
+K:655:0xA3/0x84
+
+# [Perfect Symbiosis]
+K:656:0xA3/0x85
+
+# Cure Light Insanity
+K:657:0xBC/0x85
+
+# Cure Serious Insanity
+K:658:0xBC/0x85
+
+# Cure Critical Insanity
+K:659:0xBC/0x85
+
+# Cure Insanity
+K:660:0xBC/0x85
+
+# & Phial~
+K:661:0x87/0x9D
+
+# Craftmanship
+K:663:0x86/0x82
+
+# The One Ring
+K:664:0xD8/0x81
+
+# [Apprentice Handbook]
+K:665:0xA3/0x81
+
+# [Minstrel's Music]
+K:666:0xA3/0x81
+
+# [Harps of Rivendell]
+K:667:0x8A/0x90
+
+# [Lays of Beleriand]
+K:668:0x8A/0x90
+
+# & Flute~
+K:669:0xD8/0x88
+
+# & Drum~
+K:670:0xD8/0x89
+
+# & Harp~
+K:671:0xD8/0x8A
+
+# & Banjo~
+K:672:0xD8/0x8B
+
+# & Lute~
+K:673:0xD8/0x8C
+
+# & Mandolin~
+K:674:0xD8/0x8D
+
+# Palantir of Orthanc
+K:675:0xD8/0x8F
+
+# Egg
+K:676:0xD8/0x84
+
+# Reset Recall
+K:677:0x86/0x81
+
+# Divination
+K:678:0x86/0x81
+
+# Rune: Self
+K:679:0xDA/0x80
+
+# Rune: Ray
+K:680:0xDA/0x80
+
+# Rune: Sphere
+K:681:0xDA/0x80
+
+# Rune: Knowledge
+K:682:0xDA/0x80
+
+# Rune: Life
+K:683:0xDA/0x84
+
+# Rune: Fire
+K:684:0xDA/0x81
+
+# Rune: Cold
+K:685:0xDA/0x80
+
+# Rune: Lightning
+K:686:0xDA/0x85
+
+# Rune: Acid
+K:687:0xDA/0x88
+
+# Rune: Element
+K:688:0xDA/0x89
+
+# Rune: Chaos
+K:689:0xDA/0x83
+
+# Rune: Mind
+K:690:0xDA/0x84
+
+# Rune: Holding
+K:691:0xDA/0x84
+
+# Rune: Arrow
+K:692:0xDA/0x80
+
+# Rune: Power Surge
+K:693:0xDA/0x80
+
+# Rune: Armageddon
+K:694:0xDA/0x80
+
+# Rune: Gravity
+K:695:0xDA/0x82
+
+# Essence: Extra Life
+K:696:0xD9/0x82
+
+# Rune: Undeath
+K:697:0xDA/0x82
+
+# Rune: Protection
+K:698:0xDA/0x82
+
+# Horn
+K:699:0xD8/0x8E
+
+# The Ring of Precognition
+K:700:0xB5/0x8E
+
+# Sprig of Athelas
+K:701:0xCE/0x96
+
+# [Magic for Beginners]
+K:702:0x8A/0x80
+
+# [Conjurings and Tricks]
+K:703:0x8A/0x80
+
+# [Incantations and Illusions]
+K:704:0x8A/0x80
+
+# [Sorcery and Evocations]
+K:705:0x8A/0x80
+
+# [Beginners Handbook]
+K:706:0x8A/0x89
+
+# [Words of Wisdom]
+K:707:0x8A/0x89
+
+# [Chants and Blessings]
+K:708:0x8A/0x89
+
+# [Exorcism and Dispelling]
+K:709:0x8A/0x89
+
+# [Resistance of Scarabtarices]
+K:710:0x8A/0x88
+
+# [Mordenkainen's Escapes]
+K:711:0x8A/0x88
+
+# [Kelek's Grimoire of Power]
+K:712:0x8A/0x88
+
+# [Tenser's Transformations]
+K:713:0x8A/0x88
+
+# [Raal's Tome of Destruction]
+K:714:0x8A/0x88
+
+# [Ethereal Openings]
+K:715:0x8A/0x85
+
+# [Godly Insights]
+K:716:0x8A/0x85
+
+# [Purifications and Healing]
+K:717:0x8A/0x85
+
+# [Holy Infusions]
+K:718:0x8A/0x85
+
+# [Wrath of God]
+K:719:0x8A/0x85
+
+# & Old Scroll~ of Deincarnation
+K:720:0x85/0x80
+
+# Dark Sword
+K:721:0xCE/0x9D
+
+# Numenorean for beginners (I)
+K:722:0xD8/0x81
+
+# Numenorean for beginners (II)
+K:723:0xD8/0x81
+
+# Advanced lessons of Numenorean
+K:724:0xD8/0x81
+
+# Advanced lessons of Sindarin
+K:725:0xD8/0x81
+
+# & Shard~ of Pottery
+K:726:0x8B/0x88
+
+# & Broken Stick~
+K:727:0x8B/0x89
+
+# Wall Creation
+K:728:0xB5/0x80
+
+# [Illusions for Beginners]
+K:729:0xA3/0x86
+
+# [Tricks and Visions]
+K:730:0xA3/0x86
+
+# [Phantasms and Illusions]
+K:731:0xA3/0x86
+
+# [Shadows and Prisms]
+K:732:0xA3/0x86
+
+# [Serten's Immunities]
+K:733:0xA3/0x87
+
+# [Knowledge of Kenault]
+K:734:0xA3/0x87
+
+# [Otiluke's Spheres]
+K:735:0xA3/0x87
+
+# [Boccob's Book of Shadows]
+K:736:0xA3/0x87
+
+# [Bigby's Handbook]
+K:737:0xA3/0x87
+
+# [Hunt of Orome]
+K:738:0xA3/0x8B
+
+# [Holy Sanctifications]
+K:739:0xA3/0x8B
+
+# [Secrets of the Feanturi]
+K:740:0xA3/0x8B
+
+# [War of Wrath]
+K:741:0xA3/0x8B
+
+# [Gifts of Iluvatar]
+K:742:0xA3/0x8B
+
+# & Potion~ of Learning
+K:743:0x87/0x86
+
+# [Eye of Sauron]
+K:744:0xA3/0x93
+
+# [Flame of Udun]
+K:745:0xA3/0x93
+
+# [Corruptions of Melkor]
+K:746:0xA3/0x93
+
+# [Crescent of Morgul]
+K:747:0xA3/0x93
+
+# [Morgoth's Ring]
+K:748:0xA3/0x93
+
+# Scroll: Spell
+K:749:0x86/0x82
+
+# Staff: Wishing
+K:750:0xB9/0x9B
+
+# Khuzdul - The hidden tongue of the Dwarves
+K:751:0xD8/0x81
+
+# Nandorin for dummies
+K:752:0xD8/0x81
+
+# Advanced lessons of Orcish
+K:753:0xD8/0x81
+
+# Ring: Flying
+K:755:0xB5/0x80
+
+# [Powerful Sigils]
+K:756:0xA3/0x8D
+
+# [Disruptive Forces]
+K:758:0xA3/0x8D
+
+# [Forces of the Mind]
+K:759:0xA3/0x8D
+
+# [Power of Ancient Sorcerors]
+K:760:0xA3/0x8D
+
+# [Tricks of the Wild]
+K:761:0xBC/0x93
+
+# [Mastering the Rituals]
+K:762:0xBC/0x93
+
+# [Rites of Power]
+K:763:0xBC/0x94
+
+# [Tribal Power]
+K:764:0xBC/0x94
+
+# [Aiding Shades]
+K:765:0xA3/0x8F
+
+# [Morgoth's Space-Time Warpings]
+K:766:0xA3/0x8F
+
+# [Murazor's Tome of Conjuring & Dispelling]
+K:767:0xA3/0x8F
+
+# [Channeling the Void]
+K:768:0xA3/0x8F
+
+# [Sauron's Forgotten Tome]
+K:769:0xA3/0x8F
+
+# Ring of Phasing
+K:770:0xB5/0x8E
+
+# [Earth]
+K:771:0xD8/0x82
+
+# [Fire]
+K:772:0xD8/0x82
+
+# [Air]
+K:773:0xD8/0x83
+
+# [Water]
+K:774:0xD8/0x83
+
+# [Mana]
+K:775:0xD8/0x83
+
+# Rod Tip: Home Summoning
+K:776:0xB8/0x84
+
+# Shadow Blade
+K:777:0xCD/0x9C
+
+# Bluesteel Blade
+K:778:0xCE/0x9E
+
+# Amulet: of the Serpents
+K:779:0xB6/0x9F
+
+# Darkness
+K:780:0xD9/0x82
+
+# Knowledge
+K:781:0xD9/0x82
+
+# Force
+K:782:0xD9/0x82
+
+# Lightning
+K:783:0xD9/0x82
+
+# Mana
+K:784:0xD9/0x82
+
+# Nazgul Ring
+K:785:0xB5/0x85
+
+# Climbing Set
+K:786:0xD8/0x92
+
+# Adventurer's guide to Middle-earth
+K:787:0xD8/0x80
+
+# [Dark Incantations]
+K:788:0xCE/0x94
+
+# [Immortal Rituals]
+K:789:0xCE/0x94
+
+# [Minions of Azathoth]
+K:790:0xCE/0x95
+
+# [Demonthoughts]
+K:791:0xCE/0x95
+
+# [Hellfire Tome]
+K:792:0xCE/0x95
+
+# Rod: Wooden
+K:793:0xDB/0x80
+
+# Rod: Copper
+K:794:0xDB/0x81
+
+# Rod: Iron
+K:795:0xDB/0x82
+
+# Rod: Aluminium
+K:796:0xDB/0x83
+
+# Rod: Silver
+K:797:0xDB/0x84
+
+# Rod: Golden
+K:798:0xDB/0x85
+
+# Rod: Mithril
+K:799:0xDB/0x86
+
+# Rod: Adamantite
+K:800:0xDB/0x87
+
+# Greater Ration of Health
+K:801:0x8A/0x9E
+
+# Scroll of Mass Ressurrection
+K:802:0x86/0x82
+
+# Cleaver
+K:803:0xD8/0x93
+
+# Light War Axe
+K:804:0xD8/0x94
+
+# Slaughter Axe
+K:805:0xD8/0x95
+
+# Runestone
+K:806:0xDA/0x83
+
+# Fortune Cookie
+K:807:0x8A/0x93
+
+# Portable Hole
+K:808:0xD8/0x96
+
+# Ring: Critical Hits
+K:809:0xB5/0x82
+
+# Wand of Digging of Thrain
+K:810:0xB8/0x97
+
+# Gnarled Staff of Holy Fire of Mithrandir
+K:811:0xCE/0x9F
+
+# Partial Totem
+K:812:0xB4/0x82
+
+# True Totem
+K:813:0xB4/0x85
+
+# Player
+R:0:0x8C/0x81
+
+# Filthy street urchin
+R:1:0x9B/0x8A
+
+# Scrawny cat
+R:2:0x98/0x8B
+
+# Sparrow
+R:3:0xBD/0x87
+
+# Chaffinch
+R:4:0xBD/0x86
+
+# Wild rabbit
+R:5:0xBF/0x85
+
+# Woodsman
+R:6:0xBD/0x88
+
+# Scruffy little dog
+R:7:0x8E/0x9D
+
+# Farmer Maggot
+R:8:0x9B/0x8B
+
+# Blubbering idiot
+R:9:0x9B/0x8C
+
+# Boil-covered wretch
+R:10:0x9B/0x8D
+
+# Village idiot
+R:11:0x9B/0x8E
+
+# Pitiful-looking beggar
+R:12:0xBB/0x82
+
+# Mangy-looking leper
+R:13:0x9B/0x90
+
+# Agent of the black market
+R:14:0xA2/0x94
+
+# Singing, happy drunk
+R:15:0x9B/0x92
+
+# Aimless-looking merchant
+R:16:0x9B/0x93
+
+# Mean-looking mercenary
+R:17:0x9B/0x94
+
+# Battle-scarred veteran
+R:18:0x9B/0x95
+
+# Martti Ihrasaari
+R:19:0xA1/0x8D
+
+# Grey mold
+R:20:0x9A/0x88
+
+# Large white snake
+R:21:0x93/0x8A
+
+# Grey mushroom patch
+R:22:0x8E/0x85
+
+# Newt
+R:23:0xA0/0x86
+
+# Giant white centipede
+R:24:0x96/0x9E
+
+# White icky thing
+R:25:0x99/0x8C
+
+# Clear icky thing
+R:26:0x99/0x8D
+
+# Giant white mouse
+R:27:0x9D/0x8F
+
+# Large brown snake
+R:28:0x93/0x92
+
+# Small kobold
+R:29:0x9A/0x82
+
+# Kobold
+R:30:0x9A/0x83
+
+# White worm mass
+R:31:0x9E/0x88
+
+# Floating eye
+R:32:0x98/0x84
+
+# Rock lizard
+R:33:0x93/0x8B
+
+# Grid bug
+R:34:0xA2/0x9B
+
+# Jackal
+R:35:0x8E/0x9E
+
+# Soldier ant
+R:36:0x96/0x8F
+
+# Fruit bat
+R:37:0x96/0x98
+
+# Insect swarm
+R:38:0xBD/0x89
+
+# The Greater hell-beast
+R:39:0xA2/0x82
+
+# Shrieker mushroom patch
+R:40:0x8E/0x86
+
+# Blubbering icky thing
+R:41:0x99/0x8E
+
+# Metallic green centipede
+R:42:0x96/0x9F
+
+# Novice warrior
+R:43:0x9B/0x96
+
+# Novice rogue
+R:44:0x9B/0x97
+
+# Novice priest
+R:45:0x9B/0x98
+
+# Novice mage
+R:46:0x9B/0x99
+
+# Yellow mushroom patch
+R:47:0x8E/0x87
+
+# White jelly
+R:48:0x99/0x93
+
+# Giant black ant
+R:49:0x96/0x90
+
+# Salamander
+R:50:0x93/0x8D
+
+# White harpy
+R:51:0x91/0x8C
+
+# Blue yeek
+R:52:0x9E/0x92
+
+# Grip, Farmer Maggot's dog
+R:53:0x8E/0x9F
+
+# Wolf, Farmer Maggot's dog
+R:54:0xBD/0x8A
+
+# Fang, Farmer Maggot's dog
+R:55:0x8F/0x80
+
+# Giant green frog
+R:56:0x93/0x8C
+
+# Freesia
+R:57:0xBC/0x98
+
+# Green worm mass
+R:58:0x9E/0x89
+
+# Large yellow snake
+R:59:0x93/0x91
+
+# Cave spider
+R:60:0x94/0x82
+
+# Crow
+R:61:0xBD/0x8B
+
+# Wild cat
+R:62:0x98/0x8C
+
+# Smeagol
+R:63:0x9B/0x9A
+
+# Green ooze
+R:64:0x99/0x94
+
+# Poltergeist
+R:65:0x90/0x9D
+
+# Yellow jelly
+R:66:0x99/0x96
+
+# Metallic blue centipede
+R:67:0x97/0x80
+
+# Raven
+R:68:0xBD/0x8C
+
+# Giant white louse
+R:69:0x9A/0x86
+
+# Giant yellow centipede
+R:70:0x96/0x9D
+
+# Black naga
+R:71:0x9A/0x91
+
+# Spotted mushroom patch
+R:72:0x8E/0x88
+
+# Silver jelly
+R:73:0x99/0x95
+
+# Scruffy-looking hobbit
+R:74:0x98/0x9C
+
+# Giant white ant
+R:75:0x96/0x91
+
+# Yellow mold
+R:76:0x9A/0x89
+
+# Metallic red centipede
+R:77:0x97/0x81
+
+# Yellow worm mass
+R:78:0x9E/0x8A
+
+# Clear worm mass
+R:79:0x9E/0x8B
+
+# Radiation eye
+R:80:0x98/0x85
+
+# Yellow light
+R:81:0xBD/0x92
+
+# Cave lizard
+R:82:0x93/0x8F
+
+# Novice ranger
+R:83:0x9B/0x9B
+
+# Blue jelly
+R:84:0x99/0x97
+
+# Creeping copper coins
+R:85:0x8E/0x80
+
+# Giant white rat
+R:86:0x9D/0x90
+
+# Snotling
+R:87:0xBD/0x8D
+
+# Swordfish
+R:88:0xBE/0x81
+
+# Blue worm mass
+R:89:0x9E/0x8C
+
+# Large grey snake
+R:90:0x93/0x90
+
+# Skeleton kobold
+R:91:0x9D/0x93
+
+# Ewok
+R:92:0xBB/0x90
+
+# Novice mage
+R:93:0x9B/0x9D
+
+# Green naga
+R:94:0x9A/0x92
+
+# Giant leech
+R:95:0xBD/0x8E
+
+# Barracuda
+R:96:0xBE/0x82
+
+# Novice paladin
+R:97:0x9B/0x9C
+
+# Zog
+R:98:0xBD/0x8F
+
+# Blue ooze
+R:99:0x99/0x98
+
+# Green glutton ghost
+R:100:0x90/0x9E
+
+# Green jelly
+R:101:0x99/0x99
+
+# Large kobold
+R:102:0x9A/0x84
+
+# Grey icky thing
+R:103:0x99/0x8F
+
+# Disenchanter eye
+R:104:0x98/0x86
+
+# Red worm mass
+R:105:0x9E/0x8D
+
+# Copperhead snake
+R:106:0x93/0x91
+
+# Death sword
+R:107:0x89/0x85
+
+# Purple mushroom patch
+R:108:0x8E/0x89
+
+# Novice priest
+R:109:0x9B/0x9E
+
+# Novice warrior
+R:110:0x9B/0x9F
+
+# Nibelung
+R:111:0xBB/0x8E
+
+# The disembodied hand that strangled people
+R:112:0x9F/0x87
+
+# Brown mold
+R:113:0x9A/0x8A
+
+# Giant brown bat
+R:114:0x96/0x99
+
+# Rat-thing
+R:115:0xBD/0x90
+
+# Novice rogue
+R:116:0x9C/0x81
+
+# Creeping silver coins
+R:117:0x8E/0x81
+
+# Snaga
+R:118:0x9A/0x97
+
+# Rattlesnake
+R:119:0x93/0x92
+
+# Giant slug
+R:120:0xBD/0x93
+
+# Giant pink frog
+R:121:0x93/0x93
+
+# Dark elf
+R:122:0x98/0x9E
+
+# Zombified kobold
+R:123:0x9E/0x97
+
+# Crypt creep
+R:124:0x9F/0x93
+
+# Rotting corpse
+R:125:0xBB/0x8B
+
+# Cave orc
+R:126:0x9A/0x98
+
+# Wood spider
+R:127:0x94/0x83
+
+# Manes
+R:128:0x91/0x96
+
+# Bloodshot eye
+R:129:0x98/0x87
+
+# Red naga
+R:130:0x9A/0x93
+
+# Red jelly
+R:131:0x99/0x9A
+
+# Green icky thing
+R:132:0x99/0x90
+
+# Lost soul
+R:133:0x90/0x9F
+
+# Night lizard
+R:134:0x93/0x94
+
+# Mughash, the Kobold Lord
+R:135:0x9A/0x85
+
+# Skeleton orc
+R:136:0x9D/0x94
+
+# Wormtongue, Agent of Saruman
+R:137:0xBC/0x9A
+
+# Robin Hood, the Outlaw
+R:138:0xBB/0x88
+
+# Nurgling
+R:139:0xBD/0x94
+
+# Lagduf, the Snaga
+R:140:0x9A/0x99
+
+# Brown yeek
+R:141:0x9E/0x93
+
+# Novice ranger
+R:142:0x9B/0x9B
+
+# Giant salamander
+R:143:0x93/0x95
+
+# Space monster
+R:144:0x8A/0x9B
+
+# Carnivorous flying monkey
+R:145:0xBD/0x95
+
+# Green mold
+R:146:0x9A/0x8B
+
+# Novice paladin
+R:147:0x9B/0x9C
+
+# Lemure
+R:148:0x91/0x97
+
+# Hill orc
+R:149:0x9A/0x9A
+
+# Bandit
+R:150:0x9C/0x85
+
+# Hunting hawk
+R:151:0x96/0x99
+
+# Phantom warrior
+R:152:0xA0/0x83
+
+# Gremlin
+R:153:0xA1/0x86
+
+# Yeti
+R:154:0x95/0x99
+
+# Bloodshot icky thing
+R:155:0x99/0x91
+
+# Giant grey rat
+R:156:0x9D/0x91
+
+# Black harpy
+R:157:0x91/0x8D
+
+# Skaven
+R:158:0xBD/0x96
+
+# The wounded bear
+R:159:0xBD/0x98
+
+# Cave bear
+R:160:0xB0/0x82
+
+# Rock mole
+R:161:0xBD/0x99
+
+# Mindcrafter
+R:162:0xB0/0x85
+
+# Baby blue dragon
+R:163:0x97/0x86
+
+# Baby white dragon
+R:164:0x97/0x87
+
+# Baby green dragon
+R:165:0x97/0x88
+
+# Baby black dragon
+R:166:0x97/0x89
+
+# Baby red dragon
+R:167:0x97/0x8A
+
+# Giant red ant
+R:168:0x96/0x96
+
+# Brodda, the Easterling
+R:169:0x9C/0x86
+
+# Bloodfang, the Wolf
+R:170:0xBD/0x9A
+
+# King cobra
+R:171:0x93/0x96
+
+# Eagle
+R:172:0xBD/0x9B
+
+# War bear
+R:173:0x9F/0x9D
+
+# Killer bee
+R:174:0xA0/0x88
+
+# Giant spider
+R:175:0x94/0x87
+
+# Giant white tick
+R:176:0x9D/0x9C
+
+# The Borshin
+R:177:0xBD/0x9C
+
+# Dark elven mage
+R:178:0x98/0x9F
+
+# Kamikaze yeek
+R:179:0xBD/0x9D
+
+# Orfax, Son of Boldor
+R:180:0x9E/0x94
+
+# Servant of Glaaki
+R:181:0xBD/0x9E
+
+# Dark elven warrior
+R:182:0x99/0x80
+
+# Sand-dweller
+R:183:0xBF/0x80
+
+# Clear mushroom patch
+R:184:0x8E/0x8A
+
+# Quiver slot
+R:185:0x89/0x9A
+
+# Grishnakh, the Hill Orc
+R:186:0x9A/0x9C
+
+# Giant tan bat
+R:187:0x96/0x99
+
+# Owlbear
+R:188:0xBF/0x81
+
+# Blue horror
+R:189:0xBF/0x82
+
+# Hairy mold
+R:190:0x9A/0x8C
+
+# Grizzly bear
+R:191:0xBF/0x83
+
+# Disenchanter mold
+R:192:0x9A/0x8D
+
+# Pseudo dragon
+R:193:0xBB/0x9B
+
+# Tengu
+R:194:0x91/0x98
+
+# Creeping gold coins
+R:195:0x8E/0x82
+
+# Wolf
+R:196:0x8F/0x81
+
+# Giant fruit fly
+R:197:0x90/0x95
+
+# Panther
+R:198:0x98/0x8D
+
+# Brigand
+R:199:0x9C/0x87
+
+# Hobbes the Tiger
+R:200:0x98/0x8E
+
+# Shadow Creature of Fiona
+R:201:0xBB/0x8F
+
+# Undead mass
+R:202:0xA0/0x89
+
+# Chaos shapechanger
+R:203:0xA0/0x8E
+
+# Baby multi-hued dragon
+R:204:0x97/0x8C
+
+# Vorpal bunny
+R:205:0xBF/0x84
+
+# Old Man Willow
+R:206:0xBF/0x86
+
+# Hippocampus
+R:207:0xBE/0x85
+
+# Zombified orc
+R:208:0x9E/0x98
+
+# Hippogriff
+R:209:0x91/0x8E
+
+# Black mamba
+R:210:0x93/0x97
+
+# White wolf
+R:211:0x8F/0x82
+
+# Grape jelly
+R:212:0x99/0x9B
+
+# Nether worm mass
+R:213:0x9E/0x8E
+
+# Abyss worm mass
+R:214:0xA0/0x8C
+
+# Golfimbul, the Hill Orc Chief
+R:215:0x9A/0x9D
+
+# Swordsman
+R:216:0x9C/0x89
+
+# Skaven shaman
+R:217:0xBD/0x97
+
+# Baby bronze dragon
+R:218:0xB0/0x89
+
+# Baby gold dragon
+R:219:0xB0/0x8A
+
+# Evil eye
+R:220:0xB0/0x8B
+
+# Mine-dog
+R:221:0xBF/0x88
+
+# Hellcat
+R:222:0xBC/0x97
+
+# Moon beast
+R:223:0xBB/0x9E
+
+# Master yeek
+R:224:0x9E/0x95
+
+# Priest
+R:225:0x9C/0x88
+
+# Dark elven priest
+R:226:0x99/0x82
+
+# Air spirit
+R:227:0x90/0x83
+
+# Skeleton human
+R:228:0x9D/0x95
+
+# Zombified human
+R:229:0x9E/0x99
+
+# Tiger
+R:230:0x98/0x8E
+
+# Moaning spirit
+R:231:0x91/0x80
+
+# Stegocentipede
+R:232:0x97/0x82
+
+# Spotted jelly
+R:233:0x99/0x9C
+
+# Drider
+R:234:0x94/0x85
+
+# Mongbat
+R:235:0xC4/0x80
+
+# Killer brown beetle
+R:236:0x92/0x80
+
+# Boldor, King of the Yeeks
+R:237:0x9E/0x96
+
+# Ogre
+R:238:0x92/0x90
+
+# Creeping mithril coins
+R:239:0x8E/0x83
+
+# Illusionist
+R:240:0x9C/0x8A
+
+# Druid
+R:241:0x9C/0x8B
+
+# Pink horror
+R:242:0xBF/0x89
+
+# Cloaker
+R:243:0xBF/0x8A
+
+# Black orc
+R:244:0x9A/0x9E
+
+# Ochre jelly
+R:245:0x99/0x9D
+
+# Software bug
+R:246:0xA0/0x92
+
+# Lurker
+R:247:0x80/0x80
+
+# Tangleweed
+R:248:0xBE/0x9E
+
+# Vlasta
+R:249:0xBF/0x8C
+
+# Giant white dragon fly
+R:250:0x90/0x97
+
+# Snaga sapper
+R:251:0xBF/0x8D
+
+# Blue icky thing
+R:252:0x99/0x92
+
+# Gibbering mouther
+R:253:0xA0/0x8A
+
+# Wolfhound of Flora
+R:254:0xA0/0x91
+
+# Hill giant
+R:255:0x92/0x96
+
+# Flesh golem
+R:256:0x98/0x92
+
+# Warg
+R:257:0x8F/0x83
+
+# Cheerful leprechaun
+R:258:0xA0/0x93
+
+# Giant flea
+R:259:0x90/0x96
+
+# Ufthak of Cirith Ungol
+R:260:0x9A/0x9F
+
+# Clay golem
+R:261:0x98/0x93
+
+# Black ogre
+R:262:0x92/0x91
+
+# Dweller on the threshold
+R:263:0xC4/0x83
+
+# Half-orc
+R:264:0x9B/0x80
+
+# Dark naga
+R:265:0xBF/0x8E
+
+# Poison ivy
+R:266:0xBE/0x9C
+
+# Magic mushroom patch
+R:267:0x8E/0x8B
+
+# Plaguebearer of Nurgle
+R:268:0xBF/0x8F
+
+# Guardian naga
+R:269:0x9A/0x94
+
+# Wererat
+R:270:0x9D/0x92
+
+# Light hound
+R:271:0x95/0x9B
+
+# Dark hound
+R:272:0x95/0x9C
+
+# Flying skull
+R:273:0xA0/0x95
+
+# Mi-Go
+R:274:0x9F/0x9F
+
+# Giant tarantula
+R:275:0x94/0x86
+
+# Giant clear centipede
+R:276:0x97/0x83
+
+# Mirkwood spider
+R:277:0x94/0x84
+
+# Frost giant
+R:278:0x92/0x97
+
+# Griffon
+R:279:0x91/0x8F
+
+# Homunculus
+R:280:0x91/0x99
+
+# Gnome mage
+R:281:0x99/0x83
+
+# Clear hound
+R:282:0x95/0x9D
+
+# Umber hulk
+R:283:0x94/0x9E
+
+# Rust monster
+R:284:0xBF/0x90
+
+# Ogrillon
+R:285:0xB0/0x8C
+
+# Gelatinous cube
+R:286:0x99/0x9E
+
+# Giant green dragon fly
+R:287:0x90/0x98
+
+# Fire giant
+R:288:0x92/0x98
+
+# Hummerhorn
+R:289:0x90/0x99
+
+# Lizard man
+R:290:0xBF/0x91
+
+# Ulfast, Son of Ulfang
+R:291:0x9C/0x8C
+
+# Crebain
+R:292:0xC4/0x94
+
+# Berserker
+R:293:0xBF/0x92
+
+# Quasit
+R:294:0x91/0x9A
+
+# Sphinx
+R:295:0xBF/0x93
+
+# Imp
+R:296:0x91/0x9B
+
+# Forest troll
+R:297:0x94/0x8E
+
+# Freezing sphere
+R:298:0xBF/0x94
+
+# Jumping fireball
+R:299:0xBF/0x95
+
+# Ball lightning
+R:300:0xBF/0x96
+
+# 2-headed hydra
+R:301:0x93/0x98
+
+# Swamp thing
+R:302:0xBF/0x97
+
+# Water spirit
+R:303:0x90/0x84
+
+# Giant red scorpion
+R:304:0x94/0x8C
+
+# Earth spirit
+R:305:0x90/0x85
+
+# Fire spirit
+R:306:0x90/0x86
+
+# Fire hound
+R:307:0x95/0x9E
+
+# Cold hound
+R:308:0x95/0x9F
+
+# Energy hound
+R:309:0x96/0x80
+
+# Lesser Mimic
+R:310:0x8E/0x8F
+
+# Door mimic
+R:311:0xBF/0x98
+
+# Blink dog
+R:312:0x8F/0x84
+
+# Uruk
+R:313:0x9B/0x82
+
+# Shagrat, the Orc Captain
+R:314:0x9B/0x83
+
+# Gorbag, the Orc Captain
+R:315:0x9B/0x84
+
+# Shambling mound
+R:316:0x8E/0x8C
+
+# Giant Venus Flytrap
+R:317:0xBE/0x9D
+
+# Chaos beastman
+R:318:0xBF/0x99
+
+# Daemonette of Slaanesh
+R:319:0xBF/0x9A
+
+# Giant bronze dragon fly
+R:320:0x90/0x9C
+
+# Stone giant
+R:321:0x92/0x99
+
+# Giant black dragon fly
+R:322:0x90/0x9A
+
+# Stone golem
+R:323:0x98/0x94
+
+# Red mold
+R:324:0x9A/0x8E
+
+# Giant gold dragon fly
+R:325:0x90/0x9B
+
+# Stunwall
+R:326:0xBF/0x9B
+
+# Ghast
+R:327:0xBF/0x9C
+
+# Neekerbreeker
+R:328:0xBE/0x86
+
+# Huorn
+R:329:0xBF/0x9D
+
+# Bolg, Son of Azog
+R:330:0x9B/0x85
+
+# Phase spider
+R:331:0x94/0x89
+
+# Lizard king
+R:332:0xBF/0x9E
+
+# Landmine
+R:333:0xBF/0x9F
+
+# Wyvern
+R:334:0xA0/0x97
+
+# Great eagle
+R:335:0xC0/0x80
+
+# Livingstone
+R:336:0x80/0x82
+
+# Earth hound
+R:337:0x96/0x81
+
+# Air hound
+R:338:0x96/0x82
+
+# Sabre-tooth tiger
+R:339:0x98/0x8F
+
+# Acid hound
+R:340:0x96/0x83
+
+# Chimaera
+R:341:0x91/0x90
+
+# Quylthulg
+R:342:0x92/0x9F
+
+# Sasquatch
+R:343:0x95/0x9A
+
+# Weir
+R:344:0xA0/0x96
+
+# Ranger
+R:345:0x9C/0x83
+
+# Paladin
+R:346:0x8D/0x9C
+
+# Werewolf
+R:347:0x8F/0x85
+
+# Dark elven lord
+R:348:0x99/0x85
+
+# Cloud giant
+R:349:0x92/0x9A
+
+# Ugluk, the Uruk
+R:350:0x9B/0x86
+
+# Blue dragon bat
+R:351:0x96/0x9A
+
+# Mimic
+R:352:0x86/0x82
+
+# Ultimate Mimic
+R:353:0xC0/0x83
+
+# Fire vortex
+R:354:0x9D/0x9E
+
+# Acid vortex
+R:355:0x9D/0x9F
+
+# Lugdush, the Uruk
+R:356:0x9B/0x87
+
+# Arch-vile
+R:357:0xC0/0x84
+
+# Cold vortex
+R:358:0x9E/0x80
+
+# Energy vortex
+R:359:0x9E/0x81
+
+# Globefish
+R:360:0xBE/0x8D
+
+# Giant firefly
+R:361:0xB0/0x8D
+
+# Mummified orc
+R:362:0x92/0x8D
+
+# Wolf chieftain
+R:363:0xBE/0x8E
+
+# Serpent man
+R:364:0xC0/0x85
+
+# Vampiric mist
+R:365:0xC0/0x86
+
+# Killer stag beetle
+R:366:0x92/0x81
+
+# Iron golem
+R:367:0x98/0x95
+
+# Auto-roller
+R:368:0xA0/0x98
+
+# Giant yellow scorpion
+R:369:0x94/0x8A
+
+# Jade monk
+R:370:0xC0/0x87
+
+# Black ooze
+R:371:0x99/0x9F
+
+# Hardened warrior
+R:372:0x9C/0x8D
+
+# Azog, King of the Uruk-Hai
+R:373:0x9B/0x88
+
+# Fleshhound of Khorne
+R:374:0xC0/0x89
+
+# Dark elven warlock
+R:375:0xA0/0x81
+
+# Master rogue
+R:376:0x9C/0x8E
+
+# Red dragon bat
+R:377:0x96/0x9B
+
+# Killer white beetle
+R:378:0x96/0x91
+
+# Ice skeleton
+R:379:0xC0/0x8A
+
+# Angamaite of Umbar
+R:380:0x9C/0x90
+
+# Forest wight
+R:381:0x95/0x83
+
+# Khim, Son of Mim
+R:382:0x99/0x87
+
+# Ibun, Son of Mim
+R:383:0x99/0x86
+
+# Meneldor the Swift
+R:384:0xC0/0x8B
+
+# Phantom beast
+R:385:0xA0/0x84
+
+# Giant silver ant
+R:386:0xB0/0x87
+
+# 4-headed hydra
+R:387:0x93/0x9A
+
+# Lesser hell-beast
+R:388:0xC0/0x8C
+
+# Tyrannosaur
+R:389:0x9F/0x94
+
+# Mummified human
+R:390:0x92/0x8E
+
+# Vampire bat
+R:391:0x96/0x9C
+
+# Sangahyando of Umbar
+R:392:0x9C/0x8F
+
+# It
+R:393:0x80/0x80
+
+# Banshee
+R:394:0x91/0x81
+
+# Carrion crawler
+R:395:0x97/0x84
+
+# Xiclotlan
+R:396:0xC0/0x8D
+
+# Silent watcher
+R:397:0xA0/0x9A
+
+# Pukelman
+R:398:0x98/0x96
+
+# Disenchanter beast
+R:399:0xC0/0x8E
+
+# Dark elven druid
+R:400:0x99/0x88
+
+# Stone troll
+R:401:0x94/0x9A
+
+# Black
+R:402:0xC0/0x8F
+
+# Hill troll
+R:403:0xB0/0x8F
+
+# Wereworm
+R:404:0x9E/0x8F
+
+# Killer red beetle
+R:405:0x92/0x83
+
+# Disenchanter bat
+R:406:0xB0/0x9B
+
+# Gnoph-Keh
+R:407:0xC0/0x90
+
+# Giant grey ant
+R:408:0x96/0x95
+
+# Khufu, the Mummified King
+R:409:0xA0/0x9C
+
+# Gwaihir the Windlord
+R:410:0xC0/0x91
+
+# Giant fire tick
+R:411:0x9D/0x9D
+
+# Displacer beast
+R:412:0x98/0x90
+
+# Ulwarth, Son of Ulfang
+R:413:0x9C/0x91
+
+# Werebear
+R:414:0xB1/0x96
+
+# Cave ogre
+R:415:0x92/0x92
+
+# White wraith
+R:416:0x95/0x84
+
+# Angel
+R:417:0x8E/0x92
+
+# Ghoul
+R:418:0xBB/0x8C
+
+# Mim, Betrayer of Turin
+R:419:0x99/0x89
+
+# Hellblade
+R:420:0x89/0x87
+
+# Killer fire beetle
+R:421:0x92/0x84
+
+# Beast of Nurgle
+R:422:0xC0/0x92
+
+# Creeping adamantite coins
+R:423:0x8E/0x84
+
+# Algroth
+R:424:0x94/0x91
+
+# Flamer of Tzeentch
+R:425:0xC0/0x93
+
+# Roper
+R:426:0xC0/0x94
+
+# Headless
+R:427:0x9F/0x86
+
+# Vibration hound
+R:428:0x96/0x84
+
+# Nexus hound
+R:429:0x96/0x8A
+
+# Half-ogre
+R:430:0xB0/0x9E
+
+# Lokkak, the Ogre Chieftain
+R:431:0xA1/0x80
+
+# Vampire
+R:432:0x94/0x9F
+
+# Gorgimaera
+R:433:0x91/0x91
+
+# Shantak
+R:434:0xA0/0x9D
+
+# Colbran
+R:435:0x98/0x97
+
+# Spirit naga
+R:436:0x9A/0x95
+
+# Corpser
+R:437:0xC0/0x95
+
+# Fiend of Slaanesh
+R:438:0xC0/0x96
+
+# Stairway to Hell
+R:439:0x81/0x9E
+
+# 5-headed hydra
+R:440:0x93/0x9B
+
+# Barney the Dinosaur
+R:441:0x9F/0x96
+
+# Black knight
+R:442:0x9C/0x92
+
+# Seahorse
+R:443:0xBE/0x91
+
+# Cyclops
+R:444:0xC0/0x97
+
+# Clairvoyant
+R:445:0xC0/0x98
+
+# Purple worm
+R:446:0x9E/0x90
+
+# Catoblepas
+R:447:0x9D/0x8B
+
+# Lesser wall monster
+R:448:0x80/0x82
+
+# Mage
+R:449:0x9C/0x94
+
+# Mind flayer
+R:450:0x9C/0x95
+
+# The Ultimate Dungeon Cleaner
+R:451:0xA0/0x99
+
+# Deep one
+R:452:0xC0/0x99
+
+# Basilisk
+R:453:0xBB/0x9F
+
+# Ice troll
+R:454:0x94/0x92
+
+# Dhole
+R:455:0xA1/0x82
+
+# Archangel
+R:456:0x8E/0x93
+
+# Greater Mimic
+R:457:0x82/0x81
+
+# Chaos tile
+R:458:0xA2/0x86
+
+# Young blue dragon
+R:459:0x97/0x8D
+
+# Young white dragon
+R:460:0x97/0x8E
+
+# Young green dragon
+R:461:0x97/0x8F
+
+# Young bronze dragon
+R:462:0x97/0x90
+
+# Aklash
+R:463:0xC0/0x9A
+
+# Mithril golem
+R:464:0x98/0x98
+
+# Skeleton troll
+R:465:0x9D/0x96
+
+# Skeletal tyrannosaur
+R:466:0xC0/0x9B
+
+# Beorn, the Shape-Changer
+R:467:0xA1/0x92
+
+# Thorondor, Lord of Eagles
+R:468:0xC0/0x9C
+
+# Giant blue ant
+R:469:0x96/0x94
+
+# Grave wight
+R:470:0x95/0x85
+
+# Shadow drake
+R:471:0x97/0x91
+
+# Manticore
+R:472:0x91/0x92
+
+# Giant army ant
+R:473:0x96/0x95
+
+# Killer slicer beetle
+R:474:0x92/0x85
+
+# Gorgon
+R:475:0xC0/0x9D
+
+# Gug
+R:476:0xC0/0x9E
+
+# Ghost
+R:477:0x91/0x82
+
+# Death watch beetle
+R:478:0x92/0x86
+
+# Mountain ogre
+R:479:0x92/0x95
+
+# Nexus quylthulg
+R:480:0x93/0x80
+
+# Shelob, Spider of Darkness
+R:481:0x94/0x8B
+
+# Giant squid
+R:482:0xBE/0x87
+
+# Ghoulking
+R:483:0xC0/0x9F
+
+# Doombat
+R:484:0xC1/0x80
+
+# Ninja
+R:485:0x9C/0x96
+
+# Memory moss
+R:486:0x9A/0x8F
+
+# Storm giant
+R:487:0x92/0x9B
+
+# Spectator
+R:488:0xA0/0x85
+
+# Bokrug
+R:489:0xC1/0x81
+
+# Biclops
+R:490:0xC1/0x82
+
+# Half-troll
+R:491:0x94/0x94
+
+# Ivory monk
+R:492:0xC0/0x88
+
+# Bert the Stone Troll
+R:493:0x94/0x95
+
+# Bill the Stone Troll
+R:494:0x94/0x96
+
+# Tom the Stone Troll
+R:495:0x94/0x97
+
+# Cave troll
+R:496:0x94/0x93
+
+# Anti-paladin
+R:497:0xA1/0x84
+
+# Chaos master
+R:498:0xBB/0x84
+
+# Barrow wight
+R:499:0x95/0x86
+
+# Skeleton ettin
+R:500:0x9D/0x97
+
+# Chaos drake
+R:501:0xBB/0x9A
+
+# Law drake
+R:502:0x97/0x93
+
+# Balance drake
+R:503:0x97/0x94
+
+# Ethereal drake
+R:504:0x97/0x95
+
+# Groo, the Wanderer
+R:505:0xA1/0x81
+
+# Fasolt the Giant
+R:506:0xBB/0x83
+
+# Shade
+R:507:0x91/0x83
+
+# Spectre
+R:508:0xA2/0x85
+
+# Water troll
+R:509:0x94/0x98
+
+# Fire elemental
+R:510:0x90/0x87
+
+# Cherub
+R:511:0x8E/0x94
+
+# Water elemental
+R:512:0x90/0x88
+
+# Multi-hued hound
+R:513:0xA2/0x83
+
+# Invisible stalker
+R:514:0x90/0x89
+
+# Carrion crawler
+R:515:0x97/0x85
+
+# Master thief
+R:516:0x9C/0x98
+
+# The Watcher in the Water
+R:517:0xBB/0x87
+
+# Lich
+R:518:0x92/0x88
+
+# Gas spore
+R:519:0xC1/0x83
+
+# Master vampire
+R:520:0x95/0x80
+
+# Oriental vampire
+R:521:0xA1/0x88
+
+# Greater mummy
+R:522:0xC1/0x84
+
+# Bloodletter of Khorne
+R:523:0xC1/0x85
+
+# Giant grey scorpion
+R:524:0xBB/0x9D
+
+# Earth elemental
+R:525:0x90/0x8A
+
+# Air elemental
+R:526:0x90/0x8B
+
+# Shimmering mold
+R:527:0xA2/0x93
+
+# Gargoyle
+R:528:0xC1/0x86
+
+# Malicious leprechaun
+R:529:0xA0/0x94
+
+# Eog golem
+R:530:0x98/0x99
+
+# Little Boy
+R:531:0xC0/0x81
+
+# Dagashi
+R:532:0x9C/0x9A
+
+# Headless ghost
+R:533:0xC1/0x87
+
+# Dread
+R:534:0x91/0x85
+
+# Gauth
+R:536:0xC5/0x8D
+
+# Leng spider
+R:535:0xC1/0x88
+
+# Smoke elemental
+R:537:0x90/0x8D
+
+# Olog
+R:538:0x94/0x99
+
+# Halfling slinger
+R:539:0xBB/0x91
+
+# Gravity hound
+R:540:0x96/0x86
+
+# Acidic cytoplasm
+R:541:0x9A/0x80
+
+# Inertia hound
+R:542:0x96/0x87
+
+# Impact hound
+R:543:0x96/0x88
+
+# Shardstorm
+R:544:0xB0/0x9F
+
+# Ooze elemental
+R:545:0x90/0x8C
+
+# Young black dragon
+R:546:0x97/0x96
+
+# Mumak
+R:547:0x9D/0x8C
+
+# Giant fire ant
+R:548:0x96/0x96
+
+# Mature white dragon
+R:549:0x97/0x97
+
+# Xorn
+R:550:0x95/0x97
+
+# Rogrog the Black Troll
+R:551:0x94/0x9A
+
+# Mist giant
+R:552:0xC1/0x8A
+
+# Phantom
+R:553:0x91/0x87
+
+# Grey wraith
+R:554:0x95/0x87
+
+# Revenant
+R:555:0xC1/0x8B
+
+# Young multi-hued dragon
+R:556:0x97/0x98
+
+# Raal's Tome of Destruction
+R:557:0xA3/0x91
+
+# Colossus
+R:558:0xA0/0x80
+
+# Young gold dragon
+R:559:0x97/0x99
+
+# Mature blue dragon
+R:560:0x97/0x9A
+
+# Mature green dragon
+R:561:0x97/0x9B
+
+# Mature bronze dragon
+R:562:0x97/0x9C
+
+# Young red dragon
+R:563:0x97/0x9D
+
+# Nightblade
+R:564:0xBB/0x92
+
+# Trapper
+R:565:0x8E/0x8E
+
+# Bodak
+R:566:0x91/0x9D
+
+# Time bomb
+R:567:0xC1/0x8C
+
+# Mezzodaemon
+R:568:0xC1/0x8D
+
+# Elder thing
+R:569:0x9F/0x9E
+
+# Ice elemental
+R:570:0x90/0x8E
+
+# Necromancer
+R:571:0x9C/0x9B
+
+# The Greater hell magic mushroom were-quylthulg
+R:572:0xB1/0x97
+
+# Lorgan, Chief of the Easterlings
+R:573:0x9C/0x9C
+
+# Chaos spawn
+R:574:0x9F/0x85
+
+# Mummified troll
+R:575:0x92/0x8F
+
+# Storm of Unmagic
+R:576:0xB1/0x98
+
+# Crypt thing
+R:577:0xC1/0x90
+
+# Chaos butterfly
+R:578:0xC1/0x92
+
+# Time elemental
+R:579:0xA2/0x84
+
+# Flying polyp
+R:580:0xC1/0x93
+
+# The Queen Ant
+R:581:0x96/0x97
+
+# Will o' the wisp
+R:582:0x90/0x8F
+
+# Shan
+R:583:0xC1/0x94
+
+# Magma elemental
+R:584:0x90/0x90
+
+# Black pudding
+R:585:0x9A/0x81
+
+# Killer iridescent beetle
+R:586:0xA3/0x94
+
+# Nexus vortex
+R:587:0xA1/0x9D
+
+# Plasma vortex
+R:588:0x9E/0x83
+
+# Mature red dragon
+R:589:0x97/0x9E
+
+# Mature gold dragon
+R:590:0x97/0x9F
+
+# Crystal drake
+R:591:0xBB/0x99
+
+# Mature black dragon
+R:592:0x98/0x81
+
+# Mature multi-hued dragon
+R:593:0x98/0x82
+
+# Sky whale
+R:594:0xC1/0x95
+
+# Draebor, the Imp
+R:595:0x91/0x9C
+
+# Mother Hydra
+R:596:0xC1/0x97
+
+# Death knight
+R:597:0x9C/0x9E
+
+# Castamir the Usurper
+R:598:0x9C/0x9F
+
+# Time vortex
+R:599:0x9E/0x82
+
+# Shimmering vortex
+R:600:0x9E/0x85
+
+# Ancient blue dragon
+R:601:0x8F/0x8B
+
+# Ancient bronze dragon
+R:602:0x8F/0x8C
+
+# Beholder
+R:603:0x98/0x88
+
+# Emperor wight
+R:604:0x95/0x88
+
+# Seraph
+R:605:0x8E/0x95
+
+# Vargo, Tyrant of Fire
+R:606:0x90/0x91
+
+# Black wraith
+R:607:0x95/0x89
+
+# Nightgaunt
+R:608:0xA0/0x9E
+
+# Baron of hell
+R:609:0x9F/0x8F
+
+# Scylla
+R:610:0xC1/0x98
+
+# Monastic lich
+R:611:0xC1/0x91
+
+# Nether wraith
+R:612:0x95/0x8A
+
+# Hellhound
+R:613:0x8F/0x87
+
+# 7-headed hydra
+R:614:0x93/0x9D
+
+# Waldern, King of Water
+R:615:0x90/0x92
+
+# Kavlax the Many-Headed
+R:616:0x98/0x83
+
+# Ancient white dragon
+R:617:0x8F/0x8D
+
+# Ancient green dragon
+R:618:0x8F/0x8E
+
+# Chthonian
+R:619:0xA1/0x83
+
+# Eldrak
+R:620:0x94/0x9B
+
+# Ettin
+R:621:0x94/0x9C
+
+# Night mare
+R:622:0x9D/0x8D
+
+# Vampire lord
+R:623:0x95/0x81
+
+# Ancient black dragon
+R:624:0x8F/0x8F
+
+# Weird fume
+R:625:0xC1/0x9A
+
+# Spawn of Ubbo-Sathla
+R:626:0xC1/0x9B
+
+# Fat Man
+R:627:0xC0/0x82
+
+# Malekith the Accursed
+R:628:0xBD/0x91
+
+# Shadowfax, steed of Gandalf
+R:629:0xC1/0x9C
+
+# Spirit troll
+R:630:0xC4/0x84
+
+# War troll
+R:631:0xA1/0x8C
+
+# Disenchanter worm mass
+R:632:0x9E/0x91
+
+# Rotting quylthulg
+R:633:0x93/0x81
+
+# Lesser titan
+R:634:0x92/0x9C
+
+# 9-headed hydra
+R:635:0x93/0x9E
+
+# Enchantress
+R:636:0xBB/0x81
+
+# Ranger chieftain
+R:637:0xB1/0x99
+
+# Sorcerer
+R:638:0x9D/0x82
+
+# Xaren
+R:639:0x95/0x98
+
+# Giant roc
+R:640:0x8E/0x9A
+
+# Minotaur
+R:641:0x91/0x93
+
+# Medusa, the Gorgon
+R:642:0x9A/0x96
+
+# Death drake
+R:643:0xBB/0x98
+
+# Ancient red dragon
+R:644:0x8F/0x91
+
+# Ancient gold dragon
+R:645:0x8F/0x92
+
+# Great crystal drake
+R:646:0xBB/0x97
+
+# Wyrd sister
+R:647:0xC1/0x9D
+
+# Vrock
+R:648:0x9E/0x9D
+
+# Death quasit
+R:649:0x91/0x9E
+
+# Giganto, the Gargantuan
+R:650:0xBE/0x95
+
+# Strygalldwir
+R:651:0x8E/0x9C
+
+# Fallen angel
+R:652:0xC1/0x9E
+
+# Giant headless
+R:653:0xC1/0x9F
+
+# Judge Fire
+R:654:0xC2/0x80
+
+# Ubbo-Sathla, the Unbegotten Source
+R:655:0xC2/0x81
+
+# Judge Mortis
+R:656:0xC2/0x82
+
+# Dark elven sorcerer
+R:657:0x99/0x8A
+
+# Master lich
+R:658:0x92/0x89
+
+# Byakhee
+R:659:0xA0/0x9F
+
+# Eol, the Dark Elf
+R:660:0xB2/0x8A
+
+# Archon
+R:661:0x8E/0x96
+
+# Formless spawn of Tsathoggua
+R:662:0x9F/0x9C
+
+# Hunting horror
+R:663:0x9F/0x98
+
+# Undead beholder
+R:664:0x98/0x89
+
+# Shadow
+R:665:0x91/0x86
+
+# Iron lich
+R:666:0xA1/0x8B
+
+# Dread
+R:667:0x91/0x85
+
+# Greater basilisk
+R:668:0xC2/0x83
+
+# Charybdis
+R:669:0xBE/0x96
+
+# Jack of Shadows
+R:670:0xC2/0x84
+
+# Zephyr Lord
+R:671:0xC2/0x85
+
+# Juggernaut of Khorne
+R:672:0xC2/0x86
+
+# Mumak
+R:673:0x9D/0x8E
+
+# Judge Fear
+R:674:0xC2/0x87
+
+# Ancient multi-hued dragon
+R:675:0x8F/0x94
+
+# Ethereal dragon
+R:676:0x8F/0x95
+
+# Dark young of Shub-Niggurath
+R:677:0xA1/0x8F
+
+# Colour out of space
+R:678:0xC2/0x88
+
+# Quaker, Master of Earth
+R:679:0x90/0x93
+
+# Death leprechaun
+R:680:0xBD/0x82
+
+# Chaugnar Faugn, Horror from the Hills
+R:681:0xC2/0x89
+
+# Lloigor
+R:682:0xC2/0x8A
+
+# Utgard-Loke
+R:683:0xC2/0x8B
+
+# Quachil Uttaus, Treader of the Dust
+R:684:0xC2/0x8C
+
+# Shoggoth
+R:685:0xA1/0x8E
+
+# Judge Death
+R:686:0xC2/0x8D
+
+# Ariel, Queen of Air
+R:687:0x90/0x94
+
+# 11-headed hydra
+R:688:0x93/0x9F
+
+# Patriarch
+R:689:0x9D/0x84
+
+# Dreadmaster
+R:690:0x91/0x89
+
+# Drolem
+R:691:0x98/0x9B
+
+# Scatha the Worm
+R:692:0x8F/0x93
+
+# Warrior of the Dawn
+R:693:0xA1/0x8A
+
+# Lesser black reaver
+R:694:0xC2/0x8E
+
+# Zoth-Ommog
+R:695:0xC2/0x8F
+
+# Grand master thief
+R:696:0x9C/0x84
+
+# Smaug the Golden
+R:697:0xBB/0x96
+
+# The Stormbringer
+R:698:0xA0/0x9B
+
+# Knight Templar
+R:699:0xB1/0x9A
+
+# Leprechaun fanatic
+R:700:0xC2/0x90
+
+# Dracolich
+R:701:0x8F/0x98
+
+# Greater titan
+R:702:0x92/0x9D
+
+# Dracolisk
+R:703:0x8F/0x9E
+
+# Winged Horror
+R:704:0xC4/0x85
+
+# Spectral tyrannosaur
+R:705:0x9F/0x95
+
+# Yibb-Tstll, the Patient One
+R:706:0xC2/0x91
+
+# Ghatanothoa
+R:707:0xC2/0x92
+
+# Ent
+R:708:0xC2/0x93
+
+# Hru
+R:709:0xC2/0x94
+
+# Itangast the Fire Drake
+R:710:0x8F/0x99
+
+# Death mold
+R:711:0x9A/0x90
+
+# Fafner the Dragon
+R:712:0xA1/0x90
+
+# Charon, Boatman of the Styx
+R:713:0xB1/0x9B
+
+# Quickbeam, the Ent
+R:714:0xB1/0x9C
+
+# Glaurung, Father of the Dragons
+R:715:0x8F/0x99
+
+# Behemoth
+R:716:0xC2/0x97
+
+# Garm, Guardian of Hel
+R:717:0xC2/0x98
+
+# Greater wall monster
+R:718:0x80/0x82
+
+# Nycadaemon
+R:719:0xC2/0x99
+
+# Barbazu
+R:720:0xB2/0x9D
+
+# Goat of Mendes
+R:721:0x9F/0x8D
+
+# Nightwing
+R:722:0x95/0x91
+
+# Maulotaur
+R:723:0xA0/0x8B
+
+# Nether hound
+R:724:0x96/0x89
+
+# Time hound
+R:725:0x96/0x85
+
+# Plasma hound
+R:726:0x96/0x8B
+
+# Demonic quylthulg
+R:727:0x93/0x82
+
+# Great Storm Wyrm
+R:728:0x8F/0x9B
+
+# Ulik the Troll
+R:729:0xC2/0x9A
+
+# Baphomet the Minotaur Lord
+R:730:0x91/0x95
+
+# Hell knight
+R:731:0xC2/0x9B
+
+# Bull Gates
+R:732:0xBC/0x9B
+
+# Santa Claus
+R:733:0x9F/0x89
+
+# Eihort, the Thing in the Labyrinth
+R:734:0xC2/0x9C
+
+# The King in Yellow
+R:735:0xC2/0x9D
+
+# Great unclean one
+R:736:0xC2/0x9E
+
+# Lord of Chaos
+R:737:0xBB/0x85
+
+# Old Sorcerer
+R:738:0xB1/0x9E
+
+# Ethereal hound
+R:739:0x96/0x8C
+
+# Lesser kraken
+R:740:0xBE/0x98
+
+# Great Ice Wyrm
+R:741:0x8F/0x9C
+
+# Demilich
+R:742:0xC2/0x9F
+
+# The Phoenix
+R:743:0x8E/0x9B
+
+# Nightcrawler
+R:744:0x95/0x94
+
+# Lord of Change
+R:745:0xC3/0x80
+
+# Keeper of Secrets
+R:746:0xC3/0x81
+
+# Shudde M'ell
+R:747:0xC3/0x82
+
+# Hand druj
+R:748:0x9D/0x98
+
+# Eye druj
+R:749:0x9D/0x99
+
+# Skull druj
+R:750:0x9D/0x9A
+
+# Chaos vortex
+R:751:0xBB/0x93
+
+# Aether vortex
+R:752:0x9E/0x87
+
+# Nidhogg, the Hel-Drake
+R:753:0xC3/0x83
+
+# The Lernaean Hydra
+R:754:0x94/0x80
+
+# Thuringwethil, the Vampire Messenger
+R:755:0x95/0x82
+
+# Great Hell Wyrm
+R:756:0x8F/0x9D
+
+# Hastur the Unspeakable
+R:757:0x9F/0x8C
+
+# Bloodthirster
+R:758:0xC3/0x84
+
+# Draconic quylthulg
+R:759:0x93/0x83
+
+# Nyogtha, the Thing that Should not Be
+R:760:0xA1/0x91
+
+# Ahtu, Avatar of Nyarlathotep
+R:761:0xC3/0x85
+
+# Fundin Bluecloak
+R:762:0x99/0x8B
+
+# Bile Demon
+R:763:0xB1/0x9F
+
+# Uriel, Angel of Fire
+R:764:0x8E/0x97
+
+# Azriel, Angel of Death
+R:765:0x8E/0x98
+
+# Ancalagon the Black
+R:766:0x8F/0x90
+
+# Daoloth, the Render of the Veils
+R:767:0xC3/0x86
+
+# Nightwalker
+R:768:0x95/0x95
+
+# Gabriel, the Messenger
+R:769:0x8E/0x99
+
+# Artsi, the Champion of Chaos
+R:770:0xC3/0x87
+
+# Saruman of Many Colours
+R:771:0x9D/0x88
+
+# Harowen the Black Hand
+R:772:0x9D/0x86
+
+# Osyluth
+R:773:0xB2/0x8E
+
+# Dreadlord
+R:774:0x91/0x8A
+
+# Greater kraken
+R:775:0xBE/0x99
+
+# Archlich
+R:776:0xC3/0x89
+
+# The Cat Lord
+R:777:0x98/0x91
+
+# Jabberwock
+R:778:0x91/0x9F
+
+# Chaos hound
+R:779:0xBB/0x94
+
+# Vlad Dracula, Prince of Darkness
+R:780:0xC3/0x8B
+
+# Beholder hive-mother
+R:781:0xC3/0x8C
+
+# Leviathan
+R:782:0xBE/0x9A
+
+# Great Wyrm of Chaos
+R:783:0xBB/0x95
+
+# Great Wyrm of Law
+R:784:0x90/0x80
+
+# Great Wyrm of Balance
+R:785:0x90/0x81
+
+# Shambler
+R:786:0x9F/0x92
+
+# Gelugon
+R:787:0xB2/0x8B
+
+# Glaaki
+R:788:0xC3/0x8E
+
+# T'ron, the Rebel Dragonrider
+R:789:0xB2/0x88
+
+# Great Wyrm of Many Colours
+R:790:0xA2/0x95
+
+# Mardra, rider of the Gold Loranth
+R:791:0xB2/0x85
+
+# Tselakus, the Dreadlord
+R:792:0x91/0x8B
+
+# Sky Drake
+R:793:0xA2/0x80
+
+# Eilinel the Entrapped
+R:794:0xB2/0x80
+
+# Horned Reaper
+R:795:0xB2/0x81
+
+# The Norsa
+R:796:0xBB/0x80
+
+# Rhan-Tegoth
+R:797:0xC3/0x8F
+
+# Black reaver
+R:798:0x92/0x8A
+
+# Master mindcrafter
+R:799:0xB0/0x86
+
+# Greater demonic quylthulg
+R:800:0x93/0x87
+
+# Greater draconic quylthulg
+R:801:0x93/0x85
+
+# Greater rotting quylthulg
+R:802:0x93/0x86
+
+# Null, the Living Void
+R:803:0xC3/0x90
+
+# Feagwath, the Undead Sorcerer
+R:804:0x92/0x8C
+
+# Omarax the Eye Tyrant
+R:805:0x98/0x8A
+
+# Tsathoggua, the Sleeper of N'kai
+R:806:0xC3/0x91
+
+# Greater Balrog
+R:807:0xB2/0x9E
+
+# Ungoliant, the Unlight
+R:808:0x94/0x8D
+
+# Atlach-Nacha, the Spider God
+R:809:0x94/0x8D
+
+# Y'golonac
+R:810:0xC3/0x92
+
+# Aether hound
+R:811:0x96/0x8E
+
+# Pit Fiend
+R:812:0xB2/0x9F
+
+# The Serpent of Chaos
+R:813:0x9F/0x8E
+
+# Yig, Father of Serpents
+R:814:0xC3/0x94
+
+# Unmaker
+R:815:0xA1/0x9E
+
+# Cyberdemon
+R:816:0x9F/0x91
+
+# Hela, Queen of the Dead
+R:817:0xC3/0x95
+
+# The Mouth of Sauron
+R:818:0x9D/0x89
+
+# The Necromancer of Dol Guldur
+R:819:0xC5/0x8B
+
+# Lessa, rider of the Gold Ramoth
+R:820:0xB2/0x86
+
+# Master quylthulg
+R:821:0x93/0x84
+
+# Qlzqqlzuup, the Lord of Flesh
+R:822:0x93/0x88
+
+# Cthugha, the Living Flame
+R:823:0xC3/0x96
+
+# F'lar, rider of the Bronze Mnementh
+R:824:0xB2/0x87
+
+# Maeglin, the Traitor of Gondolin
+R:825:0xA1/0x9C
+
+# Cyaegha
+R:826:0xBD/0x81
+
+# Pazuzu, Lord of Air
+R:827:0x8E/0x9C
+
+# Ithaqua the Windwalker
+R:828:0xA0/0x82
+
+# Hellhound
+R:829:0x8F/0x87
+
+# Cantoras, the Skeletal Lord
+R:830:0x9D/0x9B
+
+# Mephistopheles, Lord of Hell
+R:831:0x9F/0x90
+
+# Godzilla
+R:832:0x9F/0x97
+
+# Abhoth, Source of Uncleanness
+R:833:0xC3/0x97
+
+# Ymir, the Ice Giant
+R:834:0xBD/0x84
+
+# Loki, the Trickster
+R:835:0xC3/0x98
+
+# Star-spawn of Cthulhu
+R:836:0x9F/0x8A
+
+# Surtur, the Fire Giant
+R:837:0xBD/0x85
+
+# The Tarrasque
+R:838:0x94/0x81
+
+# Lungorthin, the Balrog of White Fire
+R:839:0x9F/0x82
+
+# Draugluin, Sire of All Werewolves
+R:840:0x8F/0x88
+
+# Shuma-Gorath
+R:841:0xBD/0x80
+
+# Tulzscha, the Green Flame
+R:842:0xC3/0x99
+
+# Oremorj, the Cyberdemon Lord
+R:843:0xC3/0x9A
+
+# Vecna, the Emperor Lich
+R:844:0x92/0x8B
+
+# Yog-Sothoth, the All-in-One
+R:845:0x9F/0x8B
+
+# Fenris Wolf
+R:846:0xC3/0x9B
+
+# Great Wyrm of Power
+R:847:0xA2/0x81
+
+# Shub-Niggurath, Black Goat of the Woods
+R:848:0x9F/0x99
+
+# Nodens, Lord of the Great Abyss
+R:849:0xC3/0x9C
+
+# Carcharoth, the Jaws of Thirst
+R:850:0x8F/0x89
+
+# Nyarlathotep, the Crawling Chaos
+R:851:0x9F/0x9B
+
+# Azathoth, the Daemon Sultan
+R:852:0xC4/0x8C
+
+# Huan, Wolfhound of the Valar
+R:853:0xB2/0x83
+
+# Jormungand the Midgard Serpent
+R:854:0xBE/0x9B
+
+# The Destroyer
+R:855:0xBD/0x83
+
+# Gothmog, the High Captain of Balrogs
+R:856:0x9F/0x81
+
+# Great Cthulhu
+R:857:0x9F/0x84
+
+# Sorka, rider of the Gold Faranth
+R:858:0xB2/0x84
+
+# The Unicorn of Order
+R:859:0xC3/0x9D
+
+# Sauron, the Sorcerer
+R:860:0x9D/0x8A
+
+# DarkGod, the Mighty Coder of Hell
+R:861:0xA1/0x93
+
+# Morgoth, Lord of Darkness
+R:862:0x92/0x9E
+
+# Human Warrior
+R:863:0xB2/0x82
+
+# Elven archer
+R:864:0x9C/0x81
+
+# Dwarven warrior
+R:865:0xB2/0x89
+
+# Elite uruk
+R:866:0x9B/0x81
+
+# The Philosophy Teacher
+R:867:0xC4/0x82
+
+# The Variant Maintainer
+R:868:0xA1/0x99
+
+# Random Number Generator
+R:869:0xBD/0x9F
+
+# Rocket mine
+R:870:0xB2/0x8C
+
+# Bouncing mine
+R:871:0xB2/0x8D
+
+# The Balrog of Moria
+R:872:0xB2/0x8F
+
+# The Icky Queen
+R:873:0xB2/0x90
+
+# Rot jelly
+R:874:0xB1/0x91
+
+# Death
+R:875:0xB2/0x91
+
+# Famine
+R:876:0xB2/0x92
+
+# Pestilence
+R:877:0xB2/0x93
+
+# War
+R:878:0xB2/0x94
+
+# Pike
+R:879:0xB2/0x95
+
+# Electric eel
+R:880:0xBE/0x8C
+
+# Giant crayfish
+R:881:0xB2/0x96
+
+# Mermaid
+R:882:0xB1/0x9D
+
+# Box jellyfish
+R:883:0xBE/0x83
+
+# Giant piranha
+R:884:0xBE/0x84
+
+# Piranha
+R:885:0xBE/0x80
+
+# Bullywug
+R:886:0xB2/0x97
+
+# Bullywug warrior
+R:887:0xB2/0x98
+
+# Bullywug shaman
+R:888:0xB2/0x99
+
+# Whale
+R:889:0xBE/0x8B
+
+# Sand mite
+R:890:0xB0/0x9D
+
+# Octopus
+R:891:0xB2/0x9A
+
+# Giant octopus
+R:892:0xBE/0x93
+
+# Eye of the deep
+R:893:0xB2/0x9B
+
+# Murk dweller
+R:894:0xB2/0x9C
+
+# Drowned soul
+R:895:0xC5/0x8F
+
+# Tiger shark
+R:896:0xC5/0x8E
+
+# Hammerhead shark
+R:897:0xBE/0x88
+
+# Great white shark
+R:898:0xBE/0x8F
+
+# Aquatic golem
+R:899:0xC5/0x95
+
+# Aquatic kobold
+R:900:0xC5/0x96
+
+# White shark
+R:901:0xBE/0x89
+
+# Scrag
+R:902:0xC5/0x97
+
+# Jaws
+R:903:0xBE/0x92
+
+# Aquatic elf
+R:904:0xC5/0x98
+
+# Aquatic elven warrior
+R:905:0xC5/0x99
+
+# Aquatic elven shaman
+R:906:0xC5/0x9A
+
+# Stargazer
+R:907:0xC5/0x9E
+
+# Elder stargazer
+R:908:0xC5/0x9F
+
+# Flounder
+R:909:0xC6/0x81
+
+# Giant turtle
+R:910:0xC6/0x82
+
+# Baby dragon turtle
+R:911:0xC6/0x83
+
+# Young dragon turtle
+R:912:0xC6/0x84
+
+# Mature dragon turtle
+R:913:0xC6/0x85
+
+# Ancient dragon turtle
+R:914:0xC6/0x86
+
+# Fastitocalon
+R:915:0xBE/0x97
+
+# Undead stargazer
+R:916:0xC6/0x80
+
+# Killer whale
+R:917:0xBE/0x8E
+
+# Merrow
+R:918:0xC5/0x9D
+
+# Water naga
+R:919:0xC6/0x87
+
+# Devilfish
+R:920:0xC6/0x88
+
+# Undead devilfish
+R:921:0xC6/0x89
+
+# Moby Dick, the White Whale
+R:922:0xC5/0x9C
+
+# Aquatic hound
+R:923:0xC4/0x9E
+
+# Water demon
+R:924:0xC6/0x8B
+
+# Ixitxachitl
+R:925:0xBE/0x86
+
+# Ixitxachitl priest
+R:926:0xBE/0x8A
+
+# Vampiric ixitxachitl
+R:927:0xBE/0x90
+
+# Mathilde, the Science Student
+R:928:0x9D/0x80
+
+# Child spirit
+R:929:0xC5/0x90
+
+# Young spirit
+R:930:0xC5/0x91
+
+# Mature spirit
+R:931:0xC5/0x92
+
+# Experienced spirit
+R:932:0xC5/0x93
+
+# Wise spirit
+R:933:0xC5/0x94
+
+# Fangorn the Treebeard, Lord of the Ents
+R:934:0xC2/0x95
+
+# Gandalf the Grey
+R:935:0xC3/0x88
+
+# Nar, the Dwarf
+R:936:0x99/0x84
+
+# Novice mindcrafter
+R:937:0xB0/0x84
+
+# Great Swamp Wyrm
+R:938:0x8F/0x9A
+
+# Great Bile Wyrm
+R:939:0x8F/0x9F
+
+# Blue Firelizard
+R:940:0xB0/0x99
+
+# Green Firelizard
+R:941:0xB0/0x98
+
+# Brown Firelizard
+R:942:0xB0/0x97
+
+# Bronze Firelizard
+R:943:0xB0/0x96
+
+# Gold Firelizard
+R:944:0xB0/0x95
+
+# High-elven ranger
+R:945:0xC6/0x93
+
+# Uvatha the Horseman
+R:946:0x95/0x8B
+
+# Adunaphel the Quiet
+R:947:0x95/0x8C
+
+# Akhorahil the Blind
+R:948:0x95/0x8D
+
+# Ren the Unclean
+R:949:0x95/0x82
+
+# Ji Indur Dawndeath
+R:950:0x95/0x8F
+
+# Dwar, Dog Lord of Waw
+R:951:0x95/0x8F
+
+# Hoarmurath of Dir
+R:952:0x95/0x92
+
+# Khamul, the Black Easterling
+R:953:0x95/0x93
+
+# The Witch-King of Angmar
+R:954:0x95/0x96
+
+# Green Dragonrider
+R:955:0xB0/0x91
+
+# Blue Dragonrider
+R:956:0xB0/0x94
+
+# Brown Dragonrider
+R:957:0xB0/0x92
+
+# Bronze Dragonrider
+R:958:0xB0/0x93
+
+# Gold Dragonrider
+R:959:0xB0/0x90
+
+# Thread
+R:960:0xB0/0x9A
+
+# Gorlim, Betrayer of Barahir
+R:961:0x9D/0x83
+
+# The Blubbering idiot, agent of black market, Simon the weak
+R:962:0x9B/0x8F
+
+# Aranea
+R:963:0xB0/0x80
+
+# Elder aranea
+R:964:0xB0/0x81
+
+# Giant brown tick
+R:965:0xC5/0x9B
+
+# Dolphiner
+R:966:0xC6/0x98
+
+# Novice possessor (soul)
+R:967:0xC6/0x8E
+
+# Bat of Gorgoroth
+R:968:0xC6/0x94
+
+# The Princess
+R:969:0xA2/0x9F
+
+# Merton Proudfoot, the lost hobbit
+R:970:0xC6/0x9E
+
+# The Wight-King of the Barrow-downs
+R:971:0xC6/0x91
+
+# Adventurer
+R:972:0xA9/0x86
+
+# Experienced possessor (soul)
+R:973:0xC6/0x8F
+
+# Old possessor (soul)
+R:974:0xC6/0x90
+
+# Death orb
+R:975:0xC6/0x99
+
+# Bronze dragon worm
+R:976:0xB1/0x80
+
+# Gold dragon worm
+R:977:0xB1/0x81
+
+# Moldoux, the Defenceless Mold
+R:978:0xB1/0x8A
+
+# The Physics Teacher
+R:979:0xC6/0x9A
+
+# Ar-Pharazon the Golden
+R:980:0xA1/0x9B
+
+# Doppelganger
+R:981:0x8C/0x81
+
+# Marylene, Heartbreakeress of the Netherworld
+R:982:0xA3/0x9E
+
+# The Greater Lag Monster
+R:983:0xC6/0x96
+
+# Hrungnir, the Stone Giant
+R:984:0xB1/0x8B
+
+# Bullroarer the Hobbit
+R:985:0x98/0x9D
+
+# 3-headed hydra
+R:986:0x93/0x99
+
+# Uldor the Accursed
+R:987:0x9C/0x93
+
+# Mystic
+R:988:0x9C/0x97
+
+# Elder vampire
+R:989:0xA1/0x9F
+
+# Ulfang the Black
+R:990:0x9C/0x99
+
+# Demonologist
+R:991:0x9C/0x9D
+
+# Hezrou
+R:992:0x9E/0x9C
+
+# Glabrezu
+R:993:0x9E/0x9D
+
+# Nalfeshnee
+R:994:0x9E/0x9E
+
+# Marilith
+R:995:0x9E/0x9F
+
+# Lesser Balrog
+R:996:0x9F/0x80
+
+# Master mystic
+R:997:0x9D/0x85
+
+# Grand master mystic
+R:998:0x9D/0x87
+
+# Erinyes
+R:999:0x9E/0x9A
+
+# Novice mindcrafter
+R:1000:0xB0/0x84
+
+# Polyphemus, the Blind Cyclops
+R:1001:0xC6/0x92
+
+# Great Wyrm of Perplexity
+R:1002:0xB1/0x8C
+
+# Hound of Tindalos
+R:1003:0xB0/0x8E
+
+# Great Wyrm of Thunder
+R:1004:0xB1/0x8D
+
+# Silver mouse
+R:1005:0xB0/0x83
+
+# The Rat King
+R:1006:0xB1/0x8E
+
+# Vort the Kobold Queen
+R:1007:0xB0/0x88
+
+# Giant black louse
+R:1008:0x9A/0x87
+
+# Fire Phantom
+R:1009:0xB1/0x8F
+
+# The Insane Player
+R:1010:0xBC/0x99
+
+# Glaryssa, Succubus Queen
+R:1011:0xA3/0x9F
+
+# Vermicious Knid
+R:1012:0xB1/0x90
+
+# Bone golem
+R:1013:0xB1/0x92
+
+# Snake of Yig
+R:1014:0xC6/0x8A
+
+# Bronze golem
+R:1015:0xB1/0x93
+
+# Dimensional shambler
+R:1016:0xB0/0x9C
+
+# Cultist
+R:1017:0xC6/0x8C
+
+# Cult leader
+R:1018:0xC6/0x8D
+
+# Servitor of the outer gods
+R:1019:0xC6/0x95
+
+# Avatar of Nyarlathotep
+R:1020:0xC3/0x85
+
+# Thiazi, the Storm Giant
+R:1021:0xB1/0x94
+
+# Hypnos, Lord of Sleep
+R:1022:0xC3/0x8D
+
+# Blue dragon worm
+R:1023:0xB1/0x82
+
+# White dragon worm
+R:1024:0xB1/0x83
+
+# Green dragon worm
+R:1025:0xB1/0x84
+
+# Black dragon worm
+R:1026:0xB1/0x85
+
+# Red dragon worm
+R:1027:0xB1/0x86
+
+# Multi-hued dragon worm
+R:1028:0xB1/0x87
+
+# The Minotaur of the Labyrinth
+R:1029:0x91/0x95
+
+# The Sandworm Queen
+R:1030:0xB1/0x88
+
+# Sandworm
+R:1031:0xB1/0x89
+
+# Tik'srvzllat
+R:1032:0xC6/0x9B
+
+# The Glass Golem
+R:1033:0xB1/0x95
+
+# The White Balrog
+R:1034:0x9F/0x82
+
+# Golgarach, the Living Rock
+R:1035:0x80/0x95
+
+# Atlas, the Titan
+R:1036:0xC6/0x9C
+
+# Kronos, Lord of the Titans
+R:1037:0xC6/0x9D
+
+# Water hound
+R:1038:0x96/0x83
+
+# Improv, the mighty MoLD
+R:1039:0xB1/0x8A
+
+# Emperor Mimic
+R:1040:0xA3/0x8D
+
+# Melinda Proudfoot
+R:1041:0xC6/0x9F
+
+# Thrain, the King Under the Mountain
+R:1042:0xC6/0x97
+
+## Fire golem
+R:1043:0xBC/0x9C
+
+# Spells (*)
+S:0x30:0x85/0x93
+S:0x31:0x85/0x92
+S:0x32:0x85/0x92
+S:0x33:0x85/0x8D
+S:0x34:0x85/0x8C
+S:0x35:0x85/0x8F
+S:0x36:0x85/0x90
+S:0x37:0x85/0x95
+S:0x38:0x85/0x93
+S:0x39:0x85/0x92
+S:0x3A:0x85/0x91
+S:0x3B:0x85/0x8E
+S:0x3C:0x85/0x8D
+S:0x3D:0x85/0x8F
+S:0x3E:0x85/0x90
+S:0x3F:0x85/0x95
+
+# Spells (|)
+S:0x40:0x84/0x9C
+S:0x41:0x84/0x98
+S:0x42:0x84/0x98
+S:0x43:0x85/0x88
+S:0x44:0x84/0x80
+S:0x45:0x84/0x8C
+S:0x46:0x84/0x90
+S:0x47:0x85/0x84
+S:0x48:0x84/0x9C
+S:0x49:0x84/0x98
+S:0x4A:0x84/0x94
+S:0x4B:0x84/0x88
+S:0x4C:0x85/0x88
+S:0x4D:0x84/0x8C
+S:0x4E:0x84/0x90
+S:0x4F:0x85/0x84
+
+# Spells (-)
+S:0x50:0x84/0x9D
+S:0x51:0x84/0x99
+S:0x52:0x84/0x99
+S:0x53:0x85/0x89
+S:0x54:0x84/0x81
+S:0x55:0x84/0x8D
+S:0x56:0x84/0x91
+S:0x57:0x85/0x85
+S:0x58:0x84/0x9D
+S:0x59:0x84/0x99
+S:0x5A:0x84/0x95
+S:0x5B:0x84/0x89
+S:0x5C:0x85/0x89
+S:0x5D:0x84/0x8D
+S:0x5E:0x84/0x91
+S:0x5F:0x85/0x85
+
+# Spells (:)
+S:0x60:0x84/0x9E
+S:0x61:0x84/0x9A
+S:0x62:0x84/0x9A
+S:0x63:0x85/0x8A
+S:0x64:0x84/0x82
+S:0x65:0x84/0x8E
+S:0x66:0x84/0x92
+S:0x67:0x85/0x86
+S:0x68:0x84/0x9E
+S:0x69:0x84/0x9A
+S:0x6A:0x84/0x96
+S:0x6B:0x84/0x8A
+S:0x6C:0x85/0x8A
+S:0x6D:0x84/0x8E
+S:0x6E:0x84/0x92
+S:0x6F:0x85/0x86
+
+# Spells (\)
+S:0x70:0x84/0x9F
+S:0x71:0x84/0x9B
+S:0x72:0x84/0x9B
+S:0x73:0x85/0x8B
+S:0x74:0x84/0x83
+S:0x75:0x84/0x8F
+S:0x76:0x84/0x93
+S:0x77:0x85/0x87
+S:0x78:0x84/0x9F
+S:0x79:0x84/0x9B
+S:0x7A:0x84/0x97
+S:0x7B:0x84/0x8B
+S:0x7C:0x85/0x8B
+S:0x7D:0x84/0x8F
+S:0x7E:0x84/0x93
+S:0x7F:0x85/0x87
+
+# Amulets (")
+S:0x80:0xB6/0x87
+S:0x81:0xB6/0x88
+S:0x82:0xB6/0x85
+S:0x83:0xB6/0x86
+S:0x84:0xB6/0x81
+S:0x85:0xB6/0x82
+S:0x86:0xB6/0x83
+S:0x87:0xB6/0x84
+S:0x88:0xB6/0x87
+S:0x89:0xB6/0x88
+S:0x8A:0xB6/0x8E
+S:0x8B:0xB6/0x86
+S:0x8C:0xB6/0x81
+S:0x8D:0xB6/0x82
+S:0x8E:0xB6/0x8B
+S:0x8F:0xB6/0x8C
+
+# Rings (=)
+S:0x90:0xB5/0x8B
+S:0x91:0xB5/0x8C
+S:0x92:0xB5/0x89
+S:0x93:0xB5/0x8A
+S:0x94:0xB5/0x81
+S:0x95:0xB5/0x82
+S:0x96:0xB5/0x83
+S:0x97:0xB5/0x88
+S:0x98:0xB5/0x8B
+S:0x99:0xB5/0x8C
+S:0x9A:0xB5/0x80
+S:0x9B:0xB5/0x8A
+S:0x9C:0xB5/0x81
+S:0x9D:0xB5/0x82
+S:0x9E:0xB5/0x83
+S:0x9F:0xB5/0x88
+
+# Staffs (_)
+S:0xA0:0xB9/0x84
+S:0xA1:0xB9/0x85
+S:0xA2:0xB9/0x85
+S:0xA3:0xB9/0x81
+S:0xA4:0xB9/0x81
+S:0xA5:0xB9/0x82
+S:0xA6:0xB9/0x80
+S:0xA7:0xB9/0x87
+S:0xA8:0xB9/0x84
+S:0xA9:0xB9/0x85
+S:0xAA:0xB9/0x83
+S:0xAB:0xB9/0x87
+S:0xAC:0xB9/0x81
+S:0xAD:0xB9/0x82
+S:0xAE:0xB9/0x80
+S:0xAF:0xB9/0x87
+
+# Wands (-)
+S:0xB0:0xB7/0x84
+S:0xB1:0xB7/0x85
+S:0xB2:0xB7/0x85
+S:0xB3:0xB7/0x86
+S:0xB4:0xB7/0x81
+S:0xB5:0xB7/0x82
+S:0xB6:0xB7/0x80
+S:0xB7:0xB7/0x87
+S:0xB8:0xB7/0x84
+S:0xB9:0xB7/0x85
+S:0xBA:0xB7/0x83
+S:0xBB:0xB7/0x86
+S:0xBC:0xB7/0x81
+S:0xBD:0xB7/0x82
+S:0xBE:0xB7/0x80
+S:0xBF:0xB7/0x87
+
+# Rods (-)
+S:0xC0:0xB8/0x84
+S:0xC1:0xB8/0x85
+S:0xC2:0xB8/0x85
+S:0xC3:0xB8/0x86
+S:0xC4:0xB8/0x81
+S:0xC5:0xB8/0x82
+S:0xC6:0xB8/0x80
+S:0xC7:0xB8/0x87
+S:0xC8:0xB8/0x84
+S:0xC9:0xB8/0x85
+S:0xCA:0xB8/0x83
+S:0xCB:0xB8/0x86
+S:0xCC:0xB8/0x81
+S:0xCD:0xB8/0x82
+S:0xCE:0xB8/0x80
+S:0xCF:0xB8/0x87
+
+# Scrolls (?)
+S:0xD0:0x86/0x82
+S:0xD1:0x86/0x82
+S:0xD2:0x86/0x82
+S:0xD3:0x86/0x82
+S:0xD4:0x86/0x82
+S:0xD5:0x86/0x82
+S:0xD6:0x86/0x82
+S:0xD7:0x86/0x82
+S:0xD8:0x86/0x82
+S:0xD9:0x86/0x82
+S:0xDA:0x86/0x82
+S:0xDB:0x86/0x82
+S:0xDC:0x86/0x82
+S:0xDD:0x86/0x82
+S:0xDE:0x86/0x82
+S:0xDF:0x86/0x82
+
+# Potions (!)
+S:0xE0:0xBC/0x84
+S:0xE1:0xBC/0x83
+S:0xE2:0xBC/0x8A
+S:0xE3:0xBC/0x8B
+S:0xE4:0xBC/0x87
+S:0xE5:0xBC/0x86
+S:0xE6:0xBC/0x85
+S:0xE7:0xBC/0x89
+S:0xE8:0xBC/0x84
+S:0xE9:0xBC/0x83
+S:0xEA:0xBC/0x8E
+S:0xEB:0xBC/0x88
+S:0xEC:0xBC/0x8B
+S:0xED:0xBC/0x8C
+S:0xEE:0xBC/0x8D
+S:0xEF:0xBC/0x89
+
+# Food (,)
+S:0xF0:0xBA/0x84
+S:0xF1:0xBA/0x85
+S:0xF2:0xBA/0x85
+S:0xF3:0xBA/0x86
+S:0xF4:0xBA/0x81
+S:0xF5:0xBA/0x82
+S:0xF6:0xBA/0x80
+S:0xF7:0xBA/0x87
+S:0xF8:0xBA/0x84
+S:0xF9:0xBA/0x85
+S:0xFA:0xBA/0x83
+S:0xFB:0xBA/0x86
+S:0xFC:0xBA/0x81
+S:0xFD:0xBA/0x82
+S:0xFE:0xBA/0x80
+S:0xFF:0xBA/0x87
+
+# Unknown Amulet
+U:40:0xB6/0x81
+
+# Unknown Ring
+U:45:0xB5/0x81
+
+# Unknown Staff
+U:55:0xB9/0x81
+
+# Unknown Wand
+U:65:0xB7/0x81
+
+# Unknown Rod
+U:66:0xB8/0x81
+
+# Unknown Scroll
+U:70:0x86/0x82
+
+# Unknown Potion
+U:75:0xBC/0x85
+
+# Unknown Food
+U:80:0x8B/0x81
+# non-defines encountered :
+# Load the special player pictures
+%:xtra-xxx.prf
+# Load the Trap image definitions
+%:trap-xxx.prf
diff --git a/lib/pref/graf.prf b/lib/pref/graf.prf
new file mode 100644
index 00000000..a82ce364
--- /dev/null
+++ b/lib/pref/graf.prf
@@ -0,0 +1,51 @@
+# File: graf.prf
+
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+#
+# This file includes, if appropriate, various "sub-files"
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+##### Standard font file #####
+
+%:font-xxx.prf
+
+
+##### System Specific Subfiles #####
+
+?:[IOR [EQU $SYS xaw] [EQU $SYS x11] [EQU $SYS gtk]]
+%:graf-x11.prf
+
+?:[EQU $SYS gcu]
+%:graf-gcu.prf
+
+?:[EQU $SYS ami]
+%:graf-ami.prf
+
+?:[EQU $SYS mac]
+%:graf-mac.prf
+
+?:[EQU $SYS dos]
+%:graf-dos.prf
+
+?:[EQU $SYS win]
+%:graf-win.prf
+
+?:[EQU $SYS ibm]
+%:graf-ibm.prf
+
+?:[EQU $SYS emx]
+%:graf-emx.prf
+
+?:[EQU $SYS acn]
+%:graf-acn.prf
+
+?:[EQU $SYS sdl]
+%:graf-sdl.prf
+
+?:1
+
+
diff --git a/lib/pref/pref-acn.prf b/lib/pref/pref-acn.prf
new file mode 100644
index 00000000..ae95fe26
--- /dev/null
+++ b/lib/pref/pref-acn.prf
@@ -0,0 +1,24 @@
+# File: pref-acn.prf
+
+# This is a minimal "pref" file for RISC OS
+
+
+# Map cursor keys onto keypad
+
+A:4
+P:^_18C\r
+
+A:6
+P:^_18D\r
+
+A:2
+P:^_18E\r
+
+A:8
+P:^_18F\r
+
+
+# Map F3 to ^S (to be a bit RISC OS-ey)
+
+A:^S
+P:^_183\r
diff --git a/lib/pref/pref-ami.prf b/lib/pref/pref-ami.prf
new file mode 100644
index 00000000..08a1c310
--- /dev/null
+++ b/lib/pref/pref-ami.prf
@@ -0,0 +1,7 @@
+# File: pref-ami.prf
+
+#
+# This file contains various default macros for use with
+# the executable built from "main-ami.c".
+#
+
diff --git a/lib/pref/pref-emx.prf b/lib/pref/pref-emx.prf
new file mode 100644
index 00000000..555c0dbc
--- /dev/null
+++ b/lib/pref/pref-emx.prf
@@ -0,0 +1,19 @@
+# File: pref-emx.prf
+
+#
+# Include "pref-ibm.prf" to get the default macros.
+#
+# Note that, while most key-to-trigger mappings are the same as DOS/Win,
+# some few odd keys will be different or not available at all.
+#
+# Examples: Ctrl-Escape, Alt-PrintScreen
+#
+# What's NOT working: color palette redefinitions.
+#
+
+%:pref-win.prf
+
+
+# shift-5 - rest
+A:R\r
+P:^_Sx4C\r
diff --git a/lib/pref/pref-gcu.prf b/lib/pref/pref-gcu.prf
new file mode 100644
index 00000000..cbc80ada
--- /dev/null
+++ b/lib/pref/pref-gcu.prf
@@ -0,0 +1,70 @@
+# File: pref-gcu.prf
+
+#
+# This file may be included by "pref.prf", when using "main-gcu.prf".
+#
+# It contains macro definitions to allow the VT100 cursor keys to be
+# recognized by Angband. This will also make the "escape" key take a
+# few seconds to recognize, so you may want to use the "`" key instead.
+#
+
+
+### VT100 Keypad ###
+
+
+# Special keypad keys (delete, insert)
+
+A:.
+P:\e[3~
+
+A:0
+P:\e[2~
+
+
+# Numerical keypad keys (map to appropriate number)
+
+A:1
+P:\e[4~
+P:\e[F
+
+A:2
+P:\e[B
+
+A:3
+P:\e[6~
+
+A:4
+P:\e[D
+
+A:5
+P:\e[G
+
+A:6
+P:\e[C
+
+A:7
+P:\e[1~
+P:\e[H
+
+A:8
+P:\e[A
+
+A:9
+P:\e[5~
+
+
+# Basic function keys (F1 - F4)
+
+A:\e
+P:\eOP
+
+A:\e
+P:\eOQ
+
+A:\e
+P:\eOR
+
+A:\e
+P:\eOS
+
+
diff --git a/lib/pref/pref-iso.prf b/lib/pref/pref-iso.prf
new file mode 100644
index 00000000..a6bad2e3
--- /dev/null
+++ b/lib/pref/pref-iso.prf
@@ -0,0 +1,118 @@
+# File: pref-sdl.prf
+
+#
+# This file provides some macros for use with versions of Angband compiled
+# using the "main-x11.c" (or "main-xaw.c") file.
+#
+# Note the use of "\e\e\e\e" (four escapes) to allow the macros to work
+# even if the game is not yet ready for a command.
+#
+# Note the use of "\\." (for "run") and "\\+" (for "alter"), to make sure
+# that the macros will work regardless of the "keymap" being used.
+#
+
+
+
+# Keypad (0-9)
+
+A:0
+P:^__100\r
+
+A:1
+P:^__101\r
+
+A:2
+P:^__112\r
+P:^__102\r
+
+A:3
+P:^__103\r
+
+A:4
+P:^__114\r
+P:^__104\r
+
+A:5
+P:^__105\r
+
+A:6
+P:^__113\r
+P:^__106\r
+
+A:7
+P:^__107\r
+
+A:8
+P:^__111\r
+P:^__108\r
+
+A:9
+P:^__109\r
+
+
+# Shift-Keypad (0-9)
+
+A:\e\e\e\e\\.0
+P:^_S_100\r
+
+A:\e\e\e\e\\.1
+P:^_S_101\r
+
+A:\e\e\e\e\\.2
+P:^_S_102\r
+
+A:\e\e\e\e\\.3
+P:^_S_103\r
+
+A:\e\e\e\e\\.4
+P:^_S_104\r
+
+A:\e\e\e\e\\.5
+P:^_S_105\r
+
+A:\e\e\e\e\\.6
+P:^_S_106\r
+
+A:\e\e\e\e\\.7
+P:^_S_107\r
+
+A:\e\e\e\e\\.8
+P:^_S_108\r
+
+A:\e\e\e\e\\.9
+P:^_S_109\r
+
+
+
+# Control-Keypad (0-9)
+
+A:\e\e\e\e\\+0
+P:^_N_100\r
+
+A:\e\e\e\e\\+1
+P:^_N_101\r
+
+A:\e\e\e\e\\+2
+P:^_N_102\r
+
+A:\e\e\e\e\\+3
+P:^_N_103\r
+
+A:\e\e\e\e\\+4
+P:^_N_104\r
+
+A:\e\e\e\e\\+5
+P:^_N_105\r
+
+A:\e\e\e\e\\+6
+P:^_N_106\r
+
+A:\e\e\e\e\\+7
+P:^_N_107\r
+
+A:\e\e\e\e\\+8
+P:^_N_108\r
+
+A:\e\e\e\e\\+9
+P:^_N_109\r
+
diff --git a/lib/pref/pref-mac.prf b/lib/pref/pref-mac.prf
new file mode 100644
index 00000000..88e5618e
--- /dev/null
+++ b/lib/pref/pref-mac.prf
@@ -0,0 +1,243 @@
+# File: pref-mac.prf
+
+#
+# This file is included by "pref.prf" when "main-mac.c" is used.
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+#
+# Macro Trigger configuration
+#
+# T:<trigger template>:<modifiers>:<modifier name1>:<modifier name2>:....
+# '&' in <trigger template> specifies location of modifier character.
+# '#' in <trigger template> specifies location of key code.
+#
+# If <trigger template> is null string, all trigger difinition will be cleared.
+
+T:&#:CSOX:control-:shift-:option-:command-
+
+# T:<trigger name>:<keycode>:<keycode with shiftkey>
+# '\' in <trigger name> is escape character.
+
+T:KP_Decimal:1
+T:KP_Multiply:3
+T:KP_Add:5
+T:KP_Clear:7
+T:KP_Divide:11
+T:KP_Enter:12
+T:KP_Subtract:14
+T:KP_Equal:17
+T:KP_0:18
+T:KP_1:19
+T:KP_2:20
+T:KP_3:21
+T:KP_4:22
+T:KP_5:23
+T:KP_6:24
+T:KP_7:25
+T:KP_8:27
+T:KP_9:28
+T:F5:32
+T:F6:33
+T:F7:34
+T:F3:35
+T:F8:36
+T:F10:37
+T:F11:39
+T:F13:41
+T:F14:43
+T:F9:45
+T:F12:47
+T:F15:49
+T:Help:50
+T:Home:51
+T:Page_Up:52
+T:Delete:53
+T:F4:54
+T:End:55
+T:F2:56
+T:Page_Down:57
+T:F1:58
+T:Left:59
+T:Right:60
+T:Down:61
+T:Up:62
+
+##### Simple Macros #####
+
+
+#
+# Keypad -- (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.)
+#
+
+A:/
+P:^_K/\r
+
+A:*
+P:^_K*\r
+
+A:7
+P:^_K7\r
+
+A:8
+P:^_K8\r
+
+A:9
+P:^_K9\r
+
+A:-
+P:^_K-\r
+
+A:4
+P:^_K4\r
+
+A:5
+P:^_K5\r
+
+A:6
+P:^_K6\r
+
+A:+
+P:^_K+\r
+
+A:1
+P:^_K1\r
+
+A:2
+P:^_K2\r
+
+A:3
+P:^_K3\r
+
+A:0
+P:^_K0\r
+
+A:.
+P:^_K.\r
+
+
+#
+# Shift-Keypad -- Directed running
+#
+
+A:\e\e\\.1
+P:^_S19\r
+
+A:\e\e\\.2
+P:^_S20\r
+
+A:\e\e\\.3
+P:^_S21\r
+
+A:\e\e\\.4
+P:^_S22\r
+
+A:\e\e\\.5
+P:^_S23\r
+
+A:\e\e\\.6
+P:^_S24\r
+
+A:\e\e\\.7
+P:^_S25\r
+
+A:\e\e\\.8
+P:^_S27\r
+
+A:\e\e\\.9
+P:^_S28\r
+
+
+#
+# Control-Keypad -- Directed tunneling
+#
+
+A:\e\e\\+1
+P:^_C19\r
+
+A:\e\e\\+2
+P:^_C20\r
+
+A:\e\e\\+3
+P:^_C21\r
+
+A:\e\e\\+4
+P:^_C22\r
+
+A:\e\e\\+5
+P:^_C23\r
+
+A:\e\e\\+6
+P:^_C24\r
+
+A:\e\e\\+7
+P:^_C25\r
+
+A:\e\e\\+8
+P:^_C27\r
+
+A:\e\e\\+9
+P:^_C28\r
+
+
+
+#
+# Option-Control-Keypad -- wield {@0} and tunnel
+#
+
+A:\e\ew0\s\s\\+1
+P:^_CO19\r
+
+A:\e\ew0\s\s\\+2
+P:^_CO20\r
+
+A:\e\ew0\s\s\\+3
+P:^_CO21\r
+
+A:\e\ew0\s\s\\+4
+P:^_CO22\r
+
+A:\e\ew0\s\s\\+5
+P:^_CO23\r
+
+A:\e\ew0\s\s\\+6
+P:^_CO24\r
+
+A:\e\ew0\s\s\\+7
+P:^_CO25\r
+
+A:\e\ew0\s\s\\+8
+P:^_CO27\r
+
+A:\e\ew0\s\s\\+9
+P:^_CO28\r
+
+
+#
+# Option-Control-Keypad-Zero -- wield {@0}
+#
+
+A:\e\ew0\s
+P:^_CO18\r
+
+
+
+#
+# Hack -- Arrow-Keys
+#
+
+A:4
+P:^_59\r
+
+A:6
+P:^_60\r
+
+A:2
+P:^_61\r
+
+A:8
+P:^_62\r
+
+
diff --git a/lib/pref/pref-sdl.prf b/lib/pref/pref-sdl.prf
new file mode 100644
index 00000000..3bc0f030
--- /dev/null
+++ b/lib/pref/pref-sdl.prf
@@ -0,0 +1,144 @@
+# File: pref-sdl.prf
+
+# This file implements macros for extended keyboard commands (characters not
+# within the 128 character ASCII set).
+# Basically, if you have to hold down control or alt or it's an arrow key,
+# it will be handled here. This means that we can let SDL worry about figuring
+# out what key is which; all it needs to do is give us the name and we'll map
+# it here.
+#
+# Note the use of "\e\e\e\e" (four escapes) to allow the macros to work
+# even if the game is not yet ready for a command.
+#
+# Note the use of "\\." (for "run") and "\\+" (for "alter"), to make sure
+# that the macros will work regardless of the "keymap" being used.
+#
+
+# Basic Arrow Movement
+
+A:8
+P:\[up]
+
+A:6
+P:\[right]
+
+A:4
+P:\[left]
+
+A:2
+P:\[down]
+
+# Basic Arrows with Shift Down
+
+A:\e\e\e\e\\.8
+P:\[shift-up]
+
+A:\e\e\e\e\\.6
+P:\[shift-right]
+
+A:\e\e\e\e\\.4
+P:\[shift-left]
+
+A:\e\e\e\e\\.2
+P:\[shift-down]
+
+# Basic Arrows with Control Down
+
+A:\e\e\e\e\\+8
+P:\[ctrl-up]
+
+A:\e\e\e\e\\+6
+P:\[ctrl-right]
+
+A:\e\e\e\e\\+4
+P:\[ctrl-left]
+
+A:\e\e\e\e\\+2
+P:\[ctrl-down]
+
+# Keypad
+
+A:1
+P:\[[1]]
+
+A:2
+P:\[[2]]
+
+A:3
+P:\[[3]]
+
+A:4
+P:\[[4]]
+
+A:5
+P:\[[5]]
+
+A:6
+P:\[[6]]
+
+A:7
+P:\[[7]]
+
+A:8
+P:\[[8]]
+
+A:9
+P:\[[9]]
+
+# Keypad With Shift
+
+A:\e\e\e\e\\.1
+P:\[shift-[1]]
+
+A:\e\e\e\e\\.2
+P:\[shift-[2]]
+
+A:\e\e\e\e\\.3
+P:\[shift-[3]]
+
+A:\e\e\e\e\\.4
+P:\[shift-[4]]
+
+A:\e\e\e\e\\.5
+P:\[shift-[5]]
+
+A:\e\e\e\e\\.6
+P:\[shift-[6]]
+
+A:\e\e\e\e\\.7
+P:\[shift-[7]]
+
+A:\e\e\e\e\\.8
+P:\[shift-[8]]
+
+A:\e\e\e\e\\.9
+P:\[shift-[9]]
+
+# Keypad With Control
+
+A:\e\e\e\e\\+1
+P:\[ctrl-[1]]
+
+A:\e\e\e\e\\+2
+P:\[ctrl-[2]]
+
+A:\e\e\e\e\\+3
+P:\[ctrl-[3]]
+
+A:\e\e\e\e\\+4
+P:\[ctrl-[4]]
+
+A:\e\e\e\e\\+5
+P:\[ctrl-[5]]
+
+A:\e\e\e\e\\+6
+P:\[ctrl-[6]]
+
+A:\e\e\e\e\\+7
+P:\[ctrl-[7]]
+
+A:\e\e\e\e\\+8
+P:\[ctrl-[8]]
+
+A:\e\e\e\e\\+9
+P:\[ctrl-[9]]
diff --git a/lib/pref/pref-win.prf b/lib/pref/pref-win.prf
new file mode 100644
index 00000000..7b1501f1
--- /dev/null
+++ b/lib/pref/pref-win.prf
@@ -0,0 +1,534 @@
+# File: pref-ibm.prf
+
+#
+# This file is used by Angband (when it was compiled using "main-ibm.c"
+# or "main-dos.c" or "main-win.c") to specify various "user preferences",
+# including "macros".
+#
+# This file defines some basic macros, which allow the use of the "keypad",
+# alone, and with the shift and/or control modifier keys. All "special"
+# keys are translated by "main-ibm.c" (or "main-win.c") into special "macro
+# triggers" of the encoded form "^_MMMxSS\r", where the "modifier" flags are
+# stored in "MMM", and the two digit hexidecimal scan code of the keypress is
+# stored in "SS".
+#
+# The "main-ibm.prf" and "main-dos.prf" files may not be able to recognize
+# the "/" and "*" keys on the keypad, because it mistakenly classifies the
+# "0x35" and "0x37" codes as the keycodes of "normal" keys.
+#
+# The "main-win.prf" file should not be using the final "control + keypad"
+# section in this file, it was created for "main-ibm.c" and "main-dos.c".
+#
+# The "main-win.prf" file may actually send the "ascii" equivalent of some
+# keypad keys after the keypad key itself, especially if "numlock" is down,
+# which may cause problems. Or it may not, it is hard to tell. This is bad.
+#
+# See "main-ibm.c" and "main-dos.c" and "main-win.c" for more info.
+#
+
+#
+# Macro Trigger configuration
+#
+# T:<trigger template>:<modifiers>:<modifier name1>:<modifier name2>:....
+# '&' in <trigger template> specifies location of modifier character.
+# '#' in <trigger template> specifies location of key code.
+#
+# If <trigger template> is null string, all trigger difinition will be cleared.
+
+T:&x#:CSA:control-:shift-:alt-
+
+# T:<trigger name>:<keycode>:<keycode with shiftkey>
+# '\' in <trigger name> is escape character.
+
+# These keycodes are actually direct keyboard scan code taken from the 'dinput.h'.
+
+?:[EQU $KEYBOARD JAPAN]
+# For Japanese keyboard.
+T:-:0C
+T:^:0D
+T:@:1A
+T:[:1B
+T:;:27
+T:\::28
+T:]:2B
+T:,:33
+T:.:34
+T:\/:35
+T:_:73
+?:1
+
+?:[EQU $KEYBOARD 0]
+# For US keyboard.
+T:-:0C
+T:=:0D
+T:[:1A
+T:]:1B
+T:;:27
+T:\':28
+T:`:29
+T:\\:2B
+T:,:33
+T:.:34
+T:\/:35
+?:1
+
+############
+# Common keycodes (except NEC PC-98x1)
+
+?:[NOT [EQU $KEYBOARD NEC98]]
+T:1:02
+T:2:03
+T:3:04
+T:4:05
+T:5:06
+T:6:07
+T:7:08
+T:8:09
+T:9:0A
+T:0:0B
+T:Backspace:0E
+T:Q:10
+T:W:11
+T:E:12
+T:R:13
+T:T:14
+T:Y:15
+T:U:16
+T:I:17
+T:O:18
+T:P:19
+T:Enter:1C
+T:A:1E
+T:S:1F
+T:D:20
+T:F:21
+T:G:22
+T:H:23
+T:J:24
+T:K:25
+T:L:26
+T:Zenkaku_Hankaku:29
+T:Z:2C
+T:X:2D
+T:C:2E
+T:V:2F
+T:B:30
+T:N:31
+T:M:32
+T:KP_Multiply:37
+T:CapsLock:3A
+T:F1:3B
+T:F2:3C
+T:F3:3D
+T:F4:3E
+T:F5:3F
+T:F6:40
+T:F7:41
+T:F8:42
+T:F9:43
+T:F10:44
+T:Numlock:45
+T:Scroll:46
+#T:KP_7:47
+T:Home:47
+#T:KP_8:48
+T:Up:48
+#T:KP_9:49
+T:Page_Up:49
+T:KP_Subtract:4A
+#T:KP_4:4B
+T:Left:4B
+T:KP_5:4C
+#T:KP_6:4D
+T:Right:4D
+T:KP_Add:4E
+#T:KP_1:4F
+T:End:4F
+#T:KP_2:50
+T:Down:50
+#T:KP_3:51
+T:Page_Down:51
+#T:KP_0:52
+T:Insert:52
+#T:KP_Decimal:53
+T:Delete:53
+T:Oem_102:56
+T:F11:57
+T:F12:58
+T:Menu:5D
+T:F13:64
+T:F14:65
+T:F15:66
+T:Hiragana_Katakana:70
+T:Abnt_C1:73
+T:Henkan:79
+T:Muhenkan:7B
+T:Yen:7D
+T:Abnt_C2:7E
+T:KP_equals:8D
+T:Prevtrack:90
+T:Kanji:94
+T:Stop:95
+T:Ax:96
+T:Unlabeled:97
+T:Nexttrack:99
+T:KP_Enter:9C
+T:Mute:A0
+T:Calculator:A1
+T:Playpause:A2
+T:Mediastop:A4
+T:Volumedown:AE
+T:Volumeup:B0
+T:Webhome:B2
+T:KP_Comma:B3
+T:KP_Divide:B5
+T:Sys_Req:B7
+T:Pause:C5
+#T:Home:C7
+#T:Up:C8
+#T:Prior:C9
+#T:Left:CB
+#T:Right:CD
+#T:End:CF
+#T:Down:D0
+#T:Next:D1
+#T:Insert:D2
+#T:Delete:D3
+T:Lwin:DB
+T:Rwin:DC
+T:Apps:DD
+T:Power:DE
+T:Sleep:DF
+T:Wake:E3
+T:Websearch:E5
+T:Webfavorites:E6
+T:Webrefresh:E7
+T:Webstop:E8
+T:Webforward:E9
+T:Webback:EA
+T:Mycomputer:EB
+T:Mail:EC
+T:Mediaselect:ED
+?:1
+
+
+######################
+# For NEC PC-98x1
+
+?:[EQU $KEYBOARD NEC98]
+T:1:01
+T:2:02
+T:3:03
+T:4:04
+T:5:05
+T:6:06
+T:7:07
+T:8:08
+T:9:09
+T:0:0A
+T:-:0B
+T:^:0C
+T:Yen:0D
+T:Backspace:0E
+T:Q:10
+T:W:11
+T:E:12
+T:R:13
+T:T:14
+T:Y:15
+T:U:16
+T:I:17
+T:O:18
+T:P:19
+T:@:1a
+T:[:1b
+T:Enter:1C
+T:A:1D
+T:S:1E
+T:D:1F
+T:F:20
+T:G:21
+T:H:22
+T:J:23
+T:K:24
+T:L:25
+T:;:26
+T:\::27
+T:[:28
+T:Z:29
+T:X:2A
+T:C:2B
+T:V:2C
+T:B:2D
+T:N:2E
+T:M:2F
+T:,:30
+T:.:31
+T:\/:32
+T:_:33
+T:Henkan:35
+T:Page_Down:36
+T:Page_Up:37
+T:Insert:38
+T:Delete:39
+T:Up:3A
+T:Left:3B
+T:Right:3C
+T:Down:3D
+T:Home:3E
+T:End:3F
+T:KP_Subtract:40
+T:KP_Divide:41
+T:KP_7:42
+T:KP_8:43
+T:KP_9:44
+T:KP_Multiply:45
+T:KP_4:46
+T:KP_5:47
+T:KP_6:48
+T:KP_Add:49
+T:KP_1:4A
+T:KP_2:4B
+T:KP_3:4C
+T:KP_Equal:4D
+T:KP_0:4E
+T:KP_Comma:4F
+T:KP_Decimal:50
+T:Muhenkan:51
+T:F11:52
+T:F12:53
+T:F13:54
+T:F14:55
+T:F15:56
+T:Pause:60
+T:F1:62
+T:F2:63
+T:F3:64
+T:F4:65
+T:F5:66
+T:F6:67
+T:F7:68
+T:F8:69
+T:F9:6A
+T:F10:6B
+T:CapsLock:71
+T:Hiragana_Katakana:72
+T:Menu:79
+
+?:[NOT [EQU $KEYBOARD NEC98]]
+
+
+#
+# Hack -- Some foreign keyboards have a special key on the keyboard, which
+# is used to generate the "<", ">", and "|" keys (alone, shifted, alt-ed).
+#
+
+A:<
+P:^_x56\r
+
+A:>
+P:^_Sx56\r
+
+A:|
+P:^_Ax56\r
+
+
+#
+# Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.)
+#
+
+A:/
+P:^_x35\r
+
+A:*
+P:^_x37\r
+
+A:7
+P:^_x47\r
+
+A:8
+P:^_x48\r
+
+A:9
+P:^_x49\r
+
+A:-
+P:^_x4A\r
+
+A:4
+P:^_x4B\r
+
+A:5
+P:^_x4C\r
+
+A:6
+P:^_x4D\r
+
+A:+
+P:^_x4E\r
+
+A:1
+P:^_x4F\r
+
+A:2
+P:^_x50\r
+
+A:3
+P:^_x51\r
+
+A:0
+P:^_x52\r
+
+A:.
+P:^_x53\r
+
+
+#
+# Shift + Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.)
+#
+
+A:\e\e\e
+P:^_Sx35\r
+
+A:\e\e\e
+P:^_Sx37\r
+
+A:\e\e\\.7
+P:^_Sx47\r
+
+A:\e\e\\.8
+P:^_Sx48\r
+
+A:\e\e\\.9
+P:^_Sx49\r
+
+A:\e\e\e
+P:^_Sx4A\r
+
+A:\e\e\\.4
+P:^_Sx4B\r
+
+A:\e\e\\.5
+P:^_Sx4C\r
+
+A:\e\e\\.6
+P:^_Sx4D\r
+
+A:\e\e\e
+P:^_Sx4E\r
+
+A:\e\e\\.1
+P:^_Sx4F\r
+
+A:\e\e\\.2
+P:^_Sx50\r
+
+A:\e\e\\.3
+P:^_Sx51\r
+
+A:\e\e\e
+P:^_Sx52\r
+
+A:\e\e\e
+P:^_Sx53\r
+
+
+#
+# Control + Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.)
+#
+
+A:\e\e\e
+P:^_Cx35\r
+
+A:\e\e\e
+P:^_Cx37\r
+
+A:\e\e\\+7
+P:^_Cx47\r
+
+A:\e\e\\+8
+P:^_Cx48\r
+
+A:\e\e\\+9
+P:^_Cx49\r
+
+A:\e\e\e
+P:^_Cx4A\r
+
+A:\e\e\\+4
+P:^_Cx4B\r
+
+A:\e\e\\+5
+P:^_Cx4C\r
+
+A:\e\e\\+6
+P:^_Cx4D\r
+
+A:\e\e\e
+P:^_Cx4E\r
+
+A:\e\e\\+1
+P:^_Cx4F\r
+
+A:\e\e\\+2
+P:^_Cx50\r
+
+A:\e\e\\+3
+P:^_Cx51\r
+
+A:\e\e\e
+P:^_Cx52\r
+
+A:\e\e\e
+P:^_Cx53\r
+
+
+#
+# Control + Keypad (/,*,7,8,9,-,4,5,6,+,1,2,3,0,.)
+#
+
+A:\e\e\e
+P:^_Cx95\r
+
+A:\e\e\e
+P:^_Cx96\r
+
+A:\e\e\\+7
+P:^_Cx77\r
+
+A:\e\e\\+8
+P:^_Cx8D\r
+
+A:\e\e\\+9
+P:^_Cx84\r
+
+A:\e\e\e
+P:^_Cx8E\r
+
+A:\e\e\\+4
+P:^_Cx73\r
+
+A:\e\e\\+5
+P:^_Cx8F\r
+
+A:\e\e\\+6
+P:^_Cx74\r
+
+A:\e\e\e
+P:^_Cx90\r
+
+A:\e\e\\+1
+P:^_Cx75\r
+
+A:\e\e\\+2
+P:^_Cx91\r
+
+A:\e\e\\+3
+P:^_Cx76\r
+
+A:\e\e\e
+P:^_Cx92\r
+
+A:\e\e\e
+P:^_Cx93\r
+
diff --git a/lib/pref/pref-x11.prf b/lib/pref/pref-x11.prf
new file mode 100644
index 00000000..f4df9376
--- /dev/null
+++ b/lib/pref/pref-x11.prf
@@ -0,0 +1,413 @@
+# File: pref-x11.prf
+
+#
+# This file provides some macros for use with versions of Angband compiled
+# using the "main-x11.c" (or "main-xaw.c") file.
+#
+# Note the use of "\e\e\e\e" (four escapes) to allow the macros to work
+# even if the game is not yet ready for a command.
+#
+# Note the use of "\\." (for "run") and "\\+" (for "alter"), to make sure
+# that the macros will work regardless of the "keymap" being used.
+#
+
+#
+# Macro Trigger configuration
+#
+# T:<trigger template>:<modifiers>:<modifier name1>:<modifier name2>:....
+# '&' in <trigger template> specifies location of modifier character.
+# '#' in <trigger template> specifies location of key code.
+#
+# If <trigger template> is null string, all trigger difinition will be cleared.
+
+T:&_#:NSOM:control-:shift-:alt-:mod2-
+
+# T:<trigger name>:<keycode>:<keycode with shiftkey>
+# '\' in <trigger name> is escape character.
+
+T:Clear:FF0B
+T:Pause:FF13
+T:Scroll_Lock:FF14
+T:Sys_Req:FF15
+T:Escape:FF1B
+T:Delete:FFFF
+T:Multi_Key:FF20
+T:Codeinput:FF37
+T:SingleCandidate:FF3C
+T:MultipleCandidate:FF3D
+T:PreviousCandidate:FF3E
+T:Kanji:FF21
+T:Muhenkan:FF22
+T:Henkan:FF23
+T:Henkan_Mode:FF23
+T:Romaji:FF24
+T:Hiragana:FF25
+T:Katakana:FF26
+T:Hiragana_Katakana:FF27
+T:Zenkaku:FF28
+T:Hankaku:FF29
+T:Zenkaku_Hankaku:FF2A
+T:Touroku:FF2B
+T:Massyo:FF2C
+T:Kana_Lock:FF2D
+T:Kana_Shift:FF2E
+T:Eisu_Shift:FF2F
+T:Eisu_Toggle:FF30
+T:Kanji_Bangou:FF37
+T:Zen_Koho:FF3D
+T:Mae_Koho:FF3E
+T:Home:FF50
+T:Left:FF51
+T:Up:FF52
+T:Right:FF53
+T:Down:FF54
+T:Page_Up:FF55
+T:Page_Down:FF56
+T:End:FF57
+T:Begin:FF58
+T:Select:FF60
+T:Print:FF61
+T:Execute:FF62
+T:Insert:FF63
+T:Undo:FF65
+T:Redo:FF66
+T:Menu:FF67
+T:Find:FF68
+T:Cancel:FF69
+T:Help:FF6A
+T:Break:FF6B
+T:Mode_Switch:FF7E
+T:Num_Lock:FF7F
+T:KP_Space:FF80
+T:KP_Tab:FF89
+T:KP_Enter:FF8D
+T:KP_F1:FF91
+T:KP_F2:FF92
+T:KP_F3:FF93
+T:KP_F4:FF94
+T:KP_Home:FF95
+T:KP_Left:FF96
+T:KP_Up:FF97
+T:KP_Right:FF98
+T:KP_Down:FF99
+T:KP_Page_Up:FF9A
+T:KP_Page_Down:FF9B
+T:KP_End:FF9C
+T:KP_Begin:FF9D
+T:KP_Insert:FF9E
+T:KP_Delete:FF9F
+T:KP_Equal:FFBD
+T:KP_Multiply:FFAA
+T:KP_Add:FFAB
+T:KP_Comma:FFAC
+T:KP_Subtract:FFAD
+T:KP_Decimal:FFAE
+T:KP_Divide:FFAF
+T:KP_0:FFB0
+T:KP_1:FFB1
+T:KP_2:FFB2
+T:KP_3:FFB3
+T:KP_4:FFB4
+T:KP_5:FFB5
+T:KP_6:FFB6
+T:KP_7:FFB7
+T:KP_8:FFB8
+T:KP_9:FFB9
+T:F1:FFBE
+T:F2:FFBF
+T:F3:FFC0
+T:F4:FFC1
+T:F5:FFC2
+T:F6:FFC3
+T:F7:FFC4
+T:F8:FFC5
+T:F9:FFC6
+T:F10:FFC7
+T:F11:FFC8
+T:F12:FFC9
+T:F13:FFCA
+T:F14:FFCB
+T:F15:FFCC
+T:F16:FFCD
+T:F17:FFCE
+T:F18:FFCF
+T:F19:FFD0
+T:F20:FFD1
+T:F21:FFD2
+T:F22:FFD3
+T:F23:FFD4
+T:F24:FFD5
+T:F25:FFD6
+T:F26:FFD7
+T:F27:FFD8
+T:F28:FFD9
+T:F29:FFDA
+T:F30:FFDB
+T:F31:FFDC
+T:F32:FFDD
+T:F33:FFDE
+T:F34:FFDF
+T:F35:FFE0
+
+T:\::3A:2A
+T:*:3A:2A
+T:;:3B:2B
+T:+:3B:2B
+T:,:2C:3C
+T:<:2C:3C
+T:-:2D:3D
+T:=:2D:3D
+T:.:2E:3E
+T:>:2E:3E
+T:\/:2F:3F
+T:?:2F:3F
+T:0:30:7E
+T:~:30:7E
+T:1:31:21
+T:!:31:21
+T:2:32:22
+T:":32:22
+T:3:33:23
+T:#:33:23
+T:4:34:24
+T:$:34:24
+T:5:35:25
+T:%:35:25
+T:6:36:26
+T:&:36:26
+T:7:37:27
+T:\':37:27
+T:8:38:28
+T:(:38:28
+T:9:39:29
+T:):39:29
+T:@:40:60
+T:`:40:60
+T:A:61:41
+T:B:62:42
+T:C:63:43
+T:D:64:44
+T:E:65:45
+T:F:66:46
+T:G:67:47
+T:H:68:48
+T:I:69:49
+T:J:6A:4A
+T:K:6B:4B
+T:L:6C:4C
+T:M:6D:4D
+T:N:6E:4E
+T:O:6F:4F
+T:P:70:50
+T:Q:71:51
+T:R:72:52
+T:S:73:53
+T:T:74:54
+T:U:75:55
+T:V:76:56
+T:W:77:57
+T:X:78:58
+T:Y:79:59
+T:Z:7A:5A
+T:[:5B:7B
+T:{:5B:7B
+T:\\:5C:5F
+T:_:5C:5F
+T:]:5D:7D
+T:}:5D:7D
+T:^:5E:7E
+T:~:5E:7E
+T:|:A5:7C
+
+
+# Keypad (0-9)
+
+A:0
+P:^__FFB0\r
+P:^__FF63\r
+P:^__????\r
+P:^__FF9E\r
+
+A:1
+P:^__FFB1\r
+P:^__FF57\r
+P:^__FFDE\r
+P:^__FF9C\r
+
+A:2
+P:^__FFB2\r
+P:^__FF54\r
+P:^__FFDF\r
+P:^__FF99\r
+
+A:3
+P:^__FFB3\r
+P:^__FF56\r
+P:^__FFE0\r
+P:^__FF9B\r
+
+A:4
+P:^__FFB4\r
+P:^__FF51\r
+P:^__FFDB\r
+P:^__FF96\r
+
+A:5
+P:^__FFB5\r
+P:^__FF80\r
+P:^__FFDC\r
+P:^__FF9D\r
+
+A:6
+P:^__FFB6\r
+P:^__FF53\r
+P:^__FFDD\r
+P:^__FF98\r
+
+A:7
+P:^__FFB7\r
+P:^__FF50\r
+P:^__FFD8\r
+P:^__FF95\r
+
+A:8
+P:^__FFB8\r
+P:^__FF52\r
+P:^__FFD9\r
+P:^__FF97\r
+
+A:9
+P:^__FFB9\r
+P:^__FF55\r
+P:^__FFDA\r
+P:^__FF9A\r
+
+
+# Shift-Keypad (0-9)
+
+A:\e\e\e\e\\.0
+P:^_S_FFB0\r
+P:^_S_FF63\r
+P:^_S_????\r
+P:^_S_FF9E\r
+
+A:\e\e\e\e\\.1
+P:^_S_FFB1\r
+P:^_S_FF57\r
+P:^_S_FFDE\r
+P:^_S_FF9C\r
+
+A:\e\e\e\e\\.2
+P:^_S_FFB2\r
+P:^_S_FF54\r
+P:^_S_FFDF\r
+P:^_S_FF99\r
+
+A:\e\e\e\e\\.3
+P:^_S_FFB3\r
+P:^_S_FF56\r
+P:^_S_FFE0\r
+P:^_S_FF9B\r
+
+A:\e\e\e\e\\.4
+P:^_S_FFB4\r
+P:^_S_FF51\r
+P:^_S_FFDB\r
+P:^_S_FF96\r
+
+A:\e\e\e\e\\.5
+P:^_S_FFB5\r
+P:^_S_FF80\r
+P:^_S_FFDC\r
+P:^_S_????\r
+
+A:\e\e\e\e\\.6
+P:^_S_FFB6\r
+P:^_S_FF53\r
+P:^_S_FFDD\r
+P:^_S_FF98\r
+
+A:\e\e\e\e\\.7
+P:^_S_FFB7\r
+P:^_S_FF50\r
+P:^_S_FFD8\r
+P:^_S_FF95\r
+
+A:\e\e\e\e\\.8
+P:^_S_FFB8\r
+P:^_S_FF52\r
+P:^_S_FFD9\r
+P:^_S_FF97\r
+
+A:\e\e\e\e\\.9
+P:^_S_FFB9\r
+P:^_S_FF55\r
+P:^_S_FFDA\r
+P:^_S_FF9A\r
+
+
+# Control-Keypad (0-9)
+
+A:\e\e\e\e\\+0
+P:^_N_FFB0\r
+P:^_N_FF63\r
+P:^_N_????\r
+P:^_N_FF9E\r
+
+A:\e\e\e\e\\+1
+P:^_N_FFB1\r
+P:^_N_FF57\r
+P:^_N_FFDE\r
+P:^_N_FF9C\r
+
+A:\e\e\e\e\\+2
+P:^_N_FFB2\r
+P:^_N_FF54\r
+P:^_N_FFDF\r
+P:^_N_FF99\r
+
+A:\e\e\e\e\\+3
+P:^_N_FFB3\r
+P:^_N_FF56\r
+P:^_N_FFE0\r
+P:^_N_FF9B\r
+
+A:\e\e\e\e\\+4
+P:^_N_FFB4\r
+P:^_N_FF51\r
+P:^_N_FFDB\r
+P:^_N_FF96\r
+
+A:\e\e\e\e\\+5
+P:^_N_FFB5\r
+P:^_N_FF80\r
+P:^_N_FFDC\r
+P:^_N_????\r
+
+A:\e\e\e\e\\+6
+P:^_N_FFB6\r
+P:^_N_FF53\r
+P:^_N_FFDD\r
+P:^_N_FF98\r
+
+A:\e\e\e\e\\+7
+P:^_N_FFB7\r
+P:^_N_FF50\r
+P:^_N_FFD8\r
+P:^_N_FF95\r
+
+A:\e\e\e\e\\+8
+P:^_N_FFB8\r
+P:^_N_FF52\r
+P:^_N_FFD9\r
+P:^_N_FF97\r
+
+A:\e\e\e\e\\+9
+P:^_N_FFB9\r
+P:^_N_FF55\r
+P:^_N_FFDA\r
+P:^_N_FF9A\r
+
+
+
+
diff --git a/lib/pref/pref.prf b/lib/pref/pref.prf
new file mode 100644
index 00000000..a6bfdbe1
--- /dev/null
+++ b/lib/pref/pref.prf
@@ -0,0 +1,311 @@
+# File: pref.prf
+
+#
+# This file defines "default" actions of various kinds
+#
+# This file includes, if appropriate, various "sub-files"
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+# Note that the "X" key is mapped in both keysets to the key sequence
+# "w0", which will "swap weapons" as long as both weapons contain the
+# inscription "@0". For example, inscribe your main weapon as "@1@0"
+# and your digger (or secondary weapon) as "@2@0".
+#
+
+
+##### Force certain options #####
+
+## # Option -- Default to original commands
+## X:rogue_like_commands
+
+## # Option -- Default to roguelike commands
+## Y:rogue_like_commands
+
+
+##### Original Keyset Mappings #####
+
+# Stay still
+A:,
+C:0:5
+
+# Movement
+A:;1
+C:0:1
+A:;2
+C:0:2
+A:;3
+C:0:3
+A:;4
+C:0:4
+A:;6
+C:0:6
+A:;7
+C:0:7
+A:;8
+C:0:8
+A:;9
+C:0:9
+
+# Hack -- Return
+A:\r
+C:0:^J
+
+# Hack -- Commit suicide
+A:Q
+C:0:^K
+
+# Hack -- Commit suicide
+A:Q
+C:0:^C
+
+
+##### Roguelike Keyset Mappings #####
+
+# Run
+A:.
+C:1:,
+
+# Stay still
+A:,
+C:1:.
+
+# Stay still
+A:,
+C:1:5
+
+# Movement
+A:;1
+C:1:1
+A:;2
+C:1:2
+A:;3
+C:1:3
+A:;4
+C:1:4
+A:;6
+C:1:6
+A:;7
+C:1:7
+A:;8
+C:1:8
+A:;9
+C:1:9
+
+# Movement (rogue keys)
+A:;1
+C:1:b
+A:;2
+C:1:j
+A:;3
+C:1:n
+A:;4
+C:1:h
+A:;6
+C:1:l
+A:;7
+C:1:y
+A:;8
+C:1:k
+A:;9
+C:1:u
+
+# Running (shift + rogue keys)
+A:.1
+C:1:B
+A:.2
+C:1:J
+A:.3
+C:1:N
+A:.4
+C:1:H
+A:.6
+C:1:L
+A:.7
+C:1:Y
+A:.8
+C:1:K
+A:.9
+C:1:U
+
+# Altering (control + rogue keys)
+A:+1
+C:1:^B
+A:+2
+C:1:^J
+A:+3
+C:1:^N
+A:+4
+C:1:^H
+A:+6
+C:1:^L
+A:+7
+C:1:^Y
+A:+8
+C:1:^K
+A:+9
+C:1:^U
+
+# Allow use of the "tunnel" command
+A:T
+C:1:^T
+
+# Allow use of the "destroy" command
+A:k
+C:1:^D
+
+# Locate player on map
+A:L
+C:1:W
+
+# Browse a book (Peruse)
+A:b
+C:1:P
+
+# Toggle search mode
+A:S
+C:1:#
+
+# Use a staff (Zap)
+A:u
+C:1:Z
+
+# Take off equipment
+A:t
+C:1:T
+
+# Fire an item
+A:f
+C:1:t
+
+# Bash a door (Force)
+A:B
+C:1:f
+
+# Look around (examine)
+A:l
+C:1:x
+
+# Aim a wand (Zap)
+A:a
+C:1:z
+
+# Zap a rod (Activate)
+A:z
+C:1:a
+
+# Hack -- Commit suicide
+A:Q
+C:1:^C
+
+# Hack - race/mutation power activation
+A:U
+C:1:O
+
+# Sacrifice at an altar
+A:O
+C:1:^g
+
+# Hack up a corpse
+A:h
+C:1:$
+
+# Cure meat
+A:K
+C:1:^O
+
+# Pet commands
+A:P
+C:1:X
+
+# Engrave
+A:x
+C:1:]
+
+# Steal
+A:Z
+C:1:[
+
+# Drink from a fountain
+A:H
+C:1:V
+
+# Give item
+A:y
+C:1:'
+
+# Chat
+A:Y
+C:1:(
+
+# Command line
+A:#
+C:1:)
+
+# Record command
+A:$
+C:1:S
+
+
+### Extended macros ###
+
+# Note that : and ' can only be included in triggers if they are
+# within quotes (i.e. as ':' or ''').
+
+L:-8192:help:Show this help
+L:-8192:?
+L:-8192:'''
+L:-8191:irc_on:Connect to IRC
+L:-8191:C
+L:-8190:irc_say:Speak on IRC
+L:-8190:@
+L:-8190:':'
+L:-8189:irc_off:Disconnect from IRC
+L:-8188:time:Get the current game time
+L:-8188:T
+L:-8187:skills:Check skills
+L:-8187:S
+L:-8186:html-dump:Save an html screenshot
+L:-8186:D
+L:-8184:quest:Show quest list
+L:-8184:Q
+L:-8183:blunder:Walk without disarming
+L:-8183:B
+L:-8182:ability:Check abilities
+L:-8182:A
+L:y:give:Give an object to a monster
+L:Y:chat:Talk to a monster
+L:j:jam:Jam a door
+
+##### System Specific Subfiles #####
+
+?:[IOR [EQU $SYS xaw] [EQU $SYS x11] [EQU $SYS gtk2]]
+%:pref-x11.prf
+
+?:[EQU $SYS gcu]
+%:pref-gcu.prf
+
+?:[EQU $SYS ami]
+%:pref-ami.prf
+
+?:[EQU $SYS mac]
+%:pref-mac.prf
+
+?:[IOR [EQU $SYS win] [EQU $SYS dos] [EQU $SYS ibm]]
+%:pref-win.prf
+
+?:[EQU $SYS emx]
+%:pref-emx.prf
+
+?:[EQU $SYS acn]
+%:pref-acn.prf
+
+?:[EQU $SYS sdl]
+%:pref-sdl.prf
+
+?:[EQU $SYS pgu]
+%:pref-pgu.prf
+
+?:1
+
+
diff --git a/lib/pref/trap-iso.prf b/lib/pref/trap-iso.prf
new file mode 100644
index 00000000..a2b77aec
--- /dev/null
+++ b/lib/pref/trap-iso.prf
@@ -0,0 +1,429 @@
+# Prf file for use with the 8x8 tiles to define
+# trap image locations. Separated out from main
+# graphics definitions due to the G:T:num:y:x
+# instead of T:num:y:x (major problems if this
+# file is opened in the TileAssigner)
+
+# Weakness Traps
+G:T:1:0x8A:0xF6
+G:T:2:0x8A:0xF6
+G:T:3:0x8A:0xF6
+
+# Intelligence Traps
+G:T:4:0x8A:0xF6
+G:T:5:0x8A:0xF6
+G:T:6:0x8A:0xF6
+
+# Wisdom Traps
+G:T:7:0x8A:0xF6
+G:T:8:0x8A:0xF6
+G:T:9:0x8A:0xF6
+
+# Fumbling Fingers Traps
+G:T:10:0x8A:0xF6
+G:T:11:0x8A:0xF6
+G:T:12:0x8A:0xF6
+
+# Wasting Traps
+G:T:13:0x8A:0xF6
+G:T:14:0x8A:0xF6
+G:T:15:0x8A:0xF6
+
+# Beauty Traps
+G:T:16:0x8A:0xF6
+G:T:17:0x8A:0xF6
+G:T:18:0x8A:0xF6
+
+# Trap of Curse Weapon
+G:T:20:0x8A:0xF7
+
+# Trap of Curse Armor
+G:T:21:0x8A:0xF7
+
+# Earthquake Trap
+G:T:22:0x8A:0xFC
+
+# Poison Needle Trap
+G:T:23:0x8A:0xF8
+
+# Summon Monster Trap
+G:T:24:0x8A:0xFA
+
+# Summon Undead Trap
+G:T:25:0x8A:0xFA
+
+# Summon Greater Undead Trap
+G:T:26:0x8A:0xFA
+
+# Teleport Trap
+G:T:27:0x8A:0xFA
+
+# Paralyzing Trap
+G:T:28:0x8A:0xF8
+
+# Explosive Device
+G:T:29:0x8A:0xF8
+
+# Teleport Item Trap
+G:T:30:0x8A:0xFA
+
+# Lose Memory Trap
+G:T:31:0x8A:0xF8
+
+# Bitter Regret Trap
+G:T:32:0x8A:0xF8
+
+# Bowel Cramps Trap
+G:T:33:0x8A:0xF8
+
+# Blindness/Confusion Trap
+G:T:34:0x8A:0xF8
+
+# Aggravation Trap
+G:T:35:0x8A:0xFC
+
+# Multiplication Trap
+G:T:36:0x8A:0xFC
+
+# Steal Item Trap
+G:T:37:0x8A:0xF7
+
+# Summon Fast Quylthulgs Trap
+G:T:38:0x8A:0xFA
+
+# Trap of Sinking
+G:T:39:0x8A:0xFA
+
+# Trap of Mana Drain
+G:T:40:0x8A:0xF8
+
+# Trap of Missing Money
+#G:T:41:0x8A:0xF7
+G:T:41:0x8A:0xF9
+
+# Trap of No Return
+G:T:42:0x8A:0xF7
+
+# Trap of Silent Switching
+G:T:43:0x8A:0xF7
+
+# Trap of Walls
+G:T:44:0x8A:0xFC
+
+# Trap of Calling Out
+G:T:45:0x8A:0xFA
+
+# Trap of Sliding
+G:T:46:0x8A:0xF8
+
+# Trap of Charges Drain
+G:T:47:0x8A:0xF7
+
+# Trap of Stair Movement
+G:T:48:0x8A:0xFC
+
+# Trap of New Trap
+G:T:49:0x8A:0xFC
+
+# Trap of Scatter Items
+G:T:50:0x8A:0xFA
+
+# Trap of Decay
+G:T:51:0x8A:0xF8
+
+# Trap of Wasting Wands
+G:T:52:0x8A:0xF7
+
+# Trap of Filling
+G:T:53:0x8A:0xFC
+
+# Trap of Drain Speed
+G:T:54:0x8A:0xF7
+
+# Lightning Bolt Trap
+G:T:60:0x8A:0xF9
+
+# Poison Bolt Trap
+G:T:61:0x8A:0xF9
+
+# Acid Bolt Trap
+G:T:62:0x8A:0xF9
+
+# Cold Bolt Trap
+G:T:63:0x8A:0xF9
+
+# Fire Bolt Trap
+G:T:64:0x8A:0xF9
+
+# Plasma Bolt Trap
+G:T:65:0x8A:0xF9
+
+# Water Bolt Trap
+G:T:66:0x8A:0xF9
+
+# Lite Bolt Trap
+G:T:67:0x8A:0xF9
+
+# Dark Bolt Trap
+G:T:68:0x8A:0xF9
+
+# Shards Bolt Trap
+G:T:69:0x8A:0xF9
+
+# Sound Bolt Trap
+G:T:70:0x8A:0xF9
+
+# Confusion Bolt Trap
+G:T:71:0x8A:0xF9
+
+# Force Bolt Trap
+G:T:72:0x8A:0xF9
+
+# Inertia Bolt Trap
+G:T:73:0x8A:0xF9
+
+# Mana Bolt Trap
+G:T:74:0x8A:0xF9
+
+# Ice Bolt Trap
+G:T:75:0x8A:0xF9
+
+# Chaos Bolt Trap
+G:T:76:0x8A:0xF9
+
+# Nether Bolt Trap
+G:T:77:0x8A:0xF9
+
+# Disenchantment Bolt Trap
+G:T:78:0x8A:0xF9
+
+# Nexus Bolt Trap
+G:T:79:0x8A:0xF9
+
+# Time Bolt Trap
+G:T:80:0x8A:0xF9
+
+# Gravity Bolt Trap
+G:T:81:0x8A:0xF9
+
+# Lightning Ball Trap
+G:T:82:0x8A:0xFA
+
+# Poison Ball Trap
+G:T:83:0x8A:0xFA
+
+# Acid Ball Trap
+G:T:84:0x8A:0xFA
+
+# Cold Ball Trap
+G:T:85:0x8A:0xFA
+
+# Fire Ball Trap
+G:T:86:0x8A:0xFA
+
+# Plasma Ball Trap
+G:T:87:0x8A:0xFA
+
+# Water Ball Trap
+G:T:88:0x8A:0xFA
+
+# Light Ball Trap
+G:T:89:0x8A:0xFA
+
+# Darkness Ball Trap
+G:T:90:0x8A:0xFA
+
+# Shards Ball Trap
+G:T:91:0x8A:0xFA
+
+# Sound Ball Trap
+G:T:92:0x8A:0xFA
+
+# Confusion Ball Trap
+G:T:93:0x8A:0xFA
+
+# Force Ball Trap
+G:T:94:0x8A:0xFA
+
+# Inertia Ball Trap
+G:T:95:0x8A:0xFA
+
+# Mana Ball Trap
+G:T:96:0x8A:0xFA
+
+# Ice Ball Trap
+G:T:97:0x8A:0xFA
+
+# Chaos Ball Trap
+G:T:98:0x8A:0xFA
+
+# Nether Ball Trap
+G:T:99:0x8A:0xFA
+
+# Disenchantment Ball Trap
+G:T:100:0x8A:0xFA
+
+# Nexus Ball Trap
+G:T:101:0x8A:0xFA
+
+# Time Ball Trap
+G:T:102:0x8A:0xFA
+
+# Gravity Ball Trap
+G:T:103:0x8A:0xFA
+
+# Arrow Trap
+G:T:110:0x8A:0xFC
+
+# Bolt Trap
+G:T:111:0x8A:0xFC
+
+# Seeker Arrow Trap
+G:T:112:0x8A:0xFC
+
+# Seeker Bolt Trap
+G:T:113:0x8A:0xFC
+
+# Poison Arrow Trap
+G:T:114:0x8A:0xFC
+
+# Poison Bolt Trap
+G:T:115:0x8A:0xFC
+
+# Poison Seeker Arrow Trap
+G:T:116:0x8A:0xFC
+
+# Poison Seeker Bolt Trap
+G:T:117:0x8A:0xFC
+
+# Broken Dagger Trap
+G:T:118:0x8A:0xFC
+
+# Dagger Trap
+G:T:119:0x8A:0xFC
+
+# Poison Broken Dagger Trap
+G:T:120:0x8A:0xFC
+
+# Poison Dagger Trap
+G:T:121:0x8A:0xFC
+
+# Arrows Trap
+G:T:122:0x8A:0xFC
+
+# Bolts Trap
+G:T:123:0x8A:0xFC
+
+# Seeker Arrow Trap
+G:T:124:0x8A:0xFC
+
+# Seeker Bolt Trap
+G:T:125:0x8A:0xFC
+
+# Poison Arrows Trap
+G:T:126:0x8A:0xFC
+
+# Poison Bolt Trap
+G:T:127:0x8A:0xFC
+
+# Poison Seeker Arrows Trap
+G:T:128:0x8A:0xFC
+
+# Poison Seeker Bolts Trap
+G:T:129:0x8A:0xFC
+
+# Broken Daggers Trap
+G:T:130:0x8A:0xFC
+
+# Dagger Trap
+G:T:131:0x8A:0xFC
+
+# Poison Broken Daggers Trap
+G:T:132:0x8A:0xFC
+
+# Poison Daggers Trap
+G:T:133:0x8A:0xFC
+
+# Trap of Drop Item
+G:T:140:0x8A:0xF7
+
+# Trap of Drop Items
+G:T:141:0x8A:0xF7
+
+# Trap of Drop Everything
+G:T:142:0x8A:0xF7
+
+# Trap of Femininity
+G:T:150:0x8A:0xF8
+
+# Trap of Masculinity
+G:T:151:0x8A:0xF8
+
+# Trap of Neutrality
+G:T:152:0x8A:0xF8
+
+# Trap of Aging
+G:T:153:0x8A:0xF8
+
+# Trap of Growing
+G:T:154:0x8A:0xF8
+
+# Trap of Shrinking
+G:T:155:0x8A:0xF8
+
+# Trap of Tanker Drain
+G:T:157:0x8A:0xF8
+
+# Trap of Divine Anger
+G:T:158:0x8A:0xFB
+
+# Trap of Divine Wrath
+G:T:159:0x8A:0xFB
+
+# Hallucination Trap
+G:T:160:0x8A:0xF8
+
+# Greater Magic Missile Trap
+G:T:161:0x8A:0xF9
+
+# Foulness Trap
+G:T:162:0x8A:0xF9
+
+# Trap of Death Ray
+G:T:163:0x8A:0xF9
+
+# Trap of Holy Fire
+G:T:164:0x8A:0xF9
+
+# Trap of Hell Fire
+G:T:165:0x8A:0xF9
+
+# Psi Bolt Trap
+G:T:166:0x8A:0xF9
+
+# Psi Drain Trap
+G:T:167:0x8A:0xF8
+
+# Plasma (Nuke) Ball Trap
+G:T:168:0x8A:0xFA
+
+# Psi Ball Trap
+G:T:169:0x8A:0xFA
+
+# Aquirement Trap
+G:T:170:0x8A:0xFA
+
+# Greater Lightning Bolt Trap
+G:T:171:0x8A:0xF9
+
+# Greater Poison Bolt Trap
+G:T:172:0x8A:0xF9
+
+# Greater Acid Bolt Trap
+G:T:173:0x8A:0xF9
+
+# Greater Cold Bolt Trap
+G:T:174:0x8A:0xF9
+
+# Greater Fire Bolt Trap
+G:T:175:0x8A:0xF9
diff --git a/lib/pref/trap-xxx.prf b/lib/pref/trap-xxx.prf
new file mode 100644
index 00000000..f4d699a0
--- /dev/null
+++ b/lib/pref/trap-xxx.prf
@@ -0,0 +1,428 @@
+# Prf file for use with the 8x8 tiles to define
+# trap image locations. Separated out from main
+# graphics definitions due to the G:T:num:y:x
+# instead of T:num:y:x (major problems if this
+# file is opened in the TileAssigner)
+
+# Weakness Traps
+G:T:1:0x82:0x9E
+G:T:2:0x82:0x9E
+G:T:3:0x82:0x9E
+
+# Intelligence Traps
+G:T:4:0x82:0x9E
+G:T:5:0x82:0x9E
+G:T:6:0x82:0x9E
+
+# Wisdom Traps
+G:T:7:0x82:0x9E
+G:T:8:0x82:0x9E
+G:T:9:0x82:0x9E
+
+# Fumbling Fingers Traps
+G:T:10:0x82:0x9E
+G:T:11:0x82:0x9E
+G:T:12:0x82:0x9E
+
+# Wasting Traps
+G:T:13:0x82:0x9E
+G:T:14:0x82:0x9E
+G:T:15:0x82:0x9E
+
+# Beauty Traps
+G:T:16:0x82:0x9E
+G:T:17:0x82:0x9E
+G:T:18:0x82:0x9E
+
+# Trap of Curse Weapon
+G:T:20:0xA2:0x92
+
+# Trap of Curse Armor
+G:T:21:0xA2:0x92
+
+# Earthquake Trap
+G:T:22:0xA2:0x87
+
+# Poison Needle Trap
+G:T:23:0xA2:0x8B
+
+# Summon Monster Trap
+G:T:24:0xA2:0x89
+
+# Summon Undead Trap
+G:T:25:0xA2:0x89
+
+# Summon Greater Undead Trap
+G:T:26:0xA2:0x89
+
+# Teleport Trap
+G:T:27:0x8A:0x9C
+
+# Paralyzing Trap
+G:T:28:0xA2:0x8B
+
+# Explosive Device
+G:T:29:0xA2:0x8B
+
+# Teleport Item Trap
+G:T:30:0x8A:0x9C
+
+# Lose Memory Trap
+G:T:31:0xA2:0x8B
+
+# Bitter Regret Trap
+G:T:32:0xA2:0x8B
+
+# Bowel Cramps Trap
+G:T:33:0xA2:0x8B
+
+# Blindness/Confusion Trap
+G:T:34:0xA2:0x8B
+
+# Aggravation Trap
+G:T:35:0xA2:0x87
+
+# Multiplication Trap
+G:T:36:0xA2:0x87
+
+# Steal Item Trap
+G:T:37:0xA2:0x92
+
+# Summon Fast Quylthulgs Trap
+G:T:38:0xA2:0x89
+
+# Trap of Sinking
+G:T:39:0x8A:0x9C
+
+# Trap of Mana Drain
+G:T:40:0xA2:0x8B
+
+# Trap of Missing Money
+G:T:41:0xA2:0x92
+
+# Trap of No Return
+G:T:42:0xA2:0x92
+
+# Trap of Silent Switching
+G:T:43:0xA2:0x92
+
+# Trap of Walls
+G:T:44:0xA2:0x87
+
+# Trap of Calling Out
+G:T:45:0xA2:0x89
+
+# Trap of Sliding
+G:T:46:0xA2:0x8B
+
+# Trap of Charges Drain
+G:T:47:0xA2:0x92
+
+# Trap of Stair Movement
+G:T:48:0xA2:0x87
+
+# Trap of New Trap
+G:T:49:0xA2:0x87
+
+# Trap of Scatter Items
+G:T:50:0x8A:0x9C
+
+# Trap of Decay
+G:T:51:0xA2:0x8B
+
+# Trap of Wasting Wands
+G:T:52:0xA2:0x92
+
+# Trap of Filling
+G:T:53:0xA2:0x87
+
+# Trap of Drain Speed
+G:T:54:0xA2:0x92
+
+# Lightning Bolt Trap
+G:T:60:0xA2:0x8C
+
+# Poison Bolt Trap
+G:T:61:0xA2:0x8C
+
+# Acid Bolt Trap
+G:T:62:0xA2:0x8C
+
+# Cold Bolt Trap
+G:T:63:0xA2:0x8C
+
+# Fire Bolt Trap
+G:T:64:0xA2:0x8C
+
+# Plasma Bolt Trap
+G:T:65:0xA2:0x8C
+
+# Water Bolt Trap
+G:T:66:0xA2:0x8C
+
+# Lite Bolt Trap
+G:T:67:0xA2:0x8C
+
+# Dark Bolt Trap
+G:T:68:0xA2:0x8C
+
+# Shards Bolt Trap
+G:T:69:0xA2:0x8C
+
+# Sound Bolt Trap
+G:T:70:0xA2:0x8C
+
+# Confusion Bolt Trap
+G:T:71:0xA2:0x8C
+
+# Force Bolt Trap
+G:T:72:0xA2:0x8C
+
+# Inertia Bolt Trap
+G:T:73:0xA2:0x8C
+
+# Mana Bolt Trap
+G:T:74:0xA2:0x8C
+
+# Ice Bolt Trap
+G:T:75:0xA2:0x8C
+
+# Chaos Bolt Trap
+G:T:76:0xA2:0x8C
+
+# Nether Bolt Trap
+G:T:77:0xA2:0x8C
+
+# Disenchantment Bolt Trap
+G:T:78:0xA2:0x8C
+
+# Nexus Bolt Trap
+G:T:79:0xA2:0x8C
+
+# Time Bolt Trap
+G:T:80:0xA2:0x8C
+
+# Gravity Bolt Trap
+G:T:81:0xA2:0x8C
+
+# Lightning Ball Trap
+G:T:82:0xA2:0x8D
+
+# Poison Ball Trap
+G:T:83:0xA2:0x8D
+
+# Acid Ball Trap
+G:T:84:0xA2:0x8D
+
+# Cold Ball Trap
+G:T:85:0xA2:0x8D
+
+# Fire Ball Trap
+G:T:86:0xA2:0x8D
+
+# Plasma Ball Trap
+G:T:87:0xA2:0x8D
+
+# Water Ball Trap
+G:T:88:0xA2:0x8D
+
+# Light Ball Trap
+G:T:89:0xA2:0x8D
+
+# Darkness Ball Trap
+G:T:90:0xA2:0x8D
+
+# Shards Ball Trap
+G:T:91:0xA2:0x8D
+
+# Sound Ball Trap
+G:T:92:0xA2:0x8D
+
+# Confusion Ball Trap
+G:T:93:0xA2:0x8D
+
+# Force Ball Trap
+G:T:94:0xA2:0x8D
+
+# Inertia Ball Trap
+G:T:95:0xA2:0x8D
+
+# Mana Ball Trap
+G:T:96:0xA2:0x8D
+
+# Ice Ball Trap
+G:T:97:0xA2:0x8D
+
+# Chaos Ball Trap
+G:T:98:0xA2:0x8D
+
+# Nether Ball Trap
+G:T:99:0xA2:0x8D
+
+# Disenchantment Ball Trap
+G:T:100:0xA2:0x8D
+
+# Nexus Ball Trap
+G:T:101:0xA2:0x8D
+
+# Time Ball Trap
+G:T:102:0xA2:0x8D
+
+# Gravity Ball Trap
+G:T:103:0xA2:0x8D
+
+# Arrow Trap
+G:T:110:0xA2:0x8E
+
+# Bolt Trap
+G:T:111:0xA2:0x8E
+
+# Seeker Arrow Trap
+G:T:112:0xA2:0x8E
+
+# Seeker Bolt Trap
+G:T:113:0xA2:0x8E
+
+# Poison Arrow Trap
+G:T:114:0xA2:0x8E
+
+# Poison Bolt Trap
+G:T:115:0xA2:0x8E
+
+# Poison Seeker Arrow Trap
+G:T:116:0xA2:0x8E
+
+# Poison Seeker Bolt Trap
+G:T:117:0xA2:0x8E
+
+# Broken Dagger Trap
+G:T:118:0xA2:0x8E
+
+# Dagger Trap
+G:T:119:0xA2:0x8E
+
+# Poison Broken Dagger Trap
+G:T:120:0xA2:0x8E
+
+# Poison Dagger Trap
+G:T:121:0xA2:0x8E
+
+# Arrows Trap
+G:T:122:0xA2:0x8E
+
+# Bolts Trap
+G:T:123:0xA2:0x8E
+
+# Seeker Arrow Trap
+G:T:124:0xA2:0x8E
+
+# Seeker Bolt Trap
+G:T:125:0xA2:0x8E
+
+# Poison Arrows Trap
+G:T:126:0xA2:0x8E
+
+# Poison Bolt Trap
+G:T:127:0xA2:0x8E
+
+# Poison Seeker Arrows Trap
+G:T:128:0xA2:0x8E
+
+# Poison Seeker Bolts Trap
+G:T:129:0xA2:0x8E
+
+# Broken Daggers Trap
+G:T:130:0xA2:0x8E
+
+# Dagger Trap
+G:T:131:0xA2:0x8E
+
+# Poison Broken Daggers Trap
+G:T:132:0xA2:0x8E
+
+# Poison Daggers Trap
+G:T:133:0xA2:0x8E
+
+# Trap of Drop Item
+G:T:140:0xA2:0x92
+
+# Trap of Drop Items
+G:T:141:0xA2:0x92
+
+# Trap of Drop Everything
+G:T:142:0xA2:0x92
+
+# Trap of Femininity
+G:T:150:0xA2:0x8B
+
+# Trap of Masculinity
+G:T:151:0xA2:0x8B
+
+# Trap of Neutrality
+G:T:152:0xA2:0x8B
+
+# Trap of Aging
+G:T:153:0xA2:0x8B
+
+# Trap of Growing
+G:T:154:0xA2:0x8B
+
+# Trap of Shrinking
+G:T:155:0xA2:0x8B
+
+# Trap of Tanker Drain
+G:T:157:0xA2:0x8B
+
+# Trap of Divine Anger
+G:T:158:0xA2:0x88
+
+# Trap of Divine Wrath
+G:T:159:0xA2:0x88
+
+# Hallucination Trap
+G:T:160:0xA2:0x8B
+
+# Greater Magic Missile Trap
+G:T:161:0xA2:0x8C
+
+# Foulness Trap
+G:T:162:0xA2:0x8C
+
+# Trap of Death Ray
+G:T:163:0xA2:0x8C
+
+# Trap of Holy Fire
+G:T:164:0xA2:0x8C
+
+# Trap of Hell Fire
+G:T:165:0xA2:0x8C
+
+# Psi Bolt Trap
+G:T:166:0xA2:0x8C
+
+# Psi Drain Trap
+G:T:167:0xA2:0x8B
+
+# Plasma (Nuke) Ball Trap
+G:T:168:0xA2:0x8D
+
+# Psi Ball Trap
+G:T:169:0xA2:0x8D
+
+# Aquirement Trap
+G:T:170:0xA2:0x89
+
+# Greater Lightning Bolt Trap
+G:T:171:0xA2:0x8C
+
+# Greater Poison Bolt Trap
+G:T:172:0xA2:0x8C
+
+# Greater Acid Bolt Trap
+G:T:173:0xA2:0x8C
+
+# Greater Cold Bolt Trap
+G:T:174:0xA2:0x8C
+
+# Greater Fire Bolt Trap
+G:T:175:0xA2:0x8C \ No newline at end of file
diff --git a/lib/pref/user.prf b/lib/pref/user.prf
new file mode 100644
index 00000000..9e10b2ac
--- /dev/null
+++ b/lib/pref/user.prf
@@ -0,0 +1,50 @@
+# File: user.prf
+
+#
+# This file defines "override" actions of various kinds
+#
+# This file includes, if appropriate, various "sub-files"
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+## Option -- Force the use of original commands
+#X:rogue_like_commands
+
+## Option -- Force the use of roguelike commands
+#Y:rogue_like_commands
+
+
+##### System Specific Subfiles #####
+
+?:[IOR [EQU $SYS xaw] [EQU $SYS x11]]
+%:user-x11.prf
+
+?:[EQU $SYS gcu]
+%:user-gcu.prf
+
+?:[EQU $SYS ami]
+%:user-ami.prf
+
+?:[EQU $SYS mac]
+%:user-mac.prf
+
+?:[EQU $SYS dos]
+%:user-dos.prf
+
+?:[EQU $SYS ibm]
+%:user-ibm.prf
+
+?:[EQU $SYS win]
+%:user-win.prf
+
+?:[EQU $SYS emx]
+%:user-emx.prf
+
+?:[EQU $SYS acn]
+%:user-acn.prf
+
+?:1
+
+
diff --git a/lib/pref/xtra-gcu.prf b/lib/pref/xtra-gcu.prf
new file mode 100644
index 00000000..7097658e
--- /dev/null
+++ b/lib/pref/xtra-gcu.prf
@@ -0,0 +1,34 @@
+# File: xtra-gcu.prf
+
+# Rename this file to "pref-gcu.prf" to allow the VT100 cursor keys
+# to be recognized by Angband. This will also make the "escape" key
+# take a few seconds to recognize, so you may want to use the "`" key
+# instead, or make a macro from escape+escape to escape.
+
+### VT100 Keypad ###
+
+# Special keypad keys (delete, insert)
+A:.
+P:\e[3~
+A:0
+P:\e[2~
+
+# Numerical keypad keys (map to appropriate number)
+A:1
+P:\e[4~
+A:2
+P:\e[B
+A:3
+P:\e[6~
+A:4
+P:\e[D
+A:5
+P:\e[G
+A:6
+P:\e[C
+A:7
+P:\e[1~
+A:8
+P:\e[A
+A:9
+P:\e[5~
diff --git a/lib/pref/xtra-new.prf b/lib/pref/xtra-new.prf
new file mode 100644
index 00000000..de82b976
--- /dev/null
+++ b/lib/pref/xtra-new.prf
@@ -0,0 +1,1128 @@
+# File: xtra-new.prf
+
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+#
+# Edited for use with Adam Bolt's new graphics by Robert Ruehlmann < rr9@angband.org >
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+##### AK 20011216 Default pictures for all races that
+# are not handled below, without caring for class
+
+
+?:[EQU $RACE Human]
+R:0:0x89/0xA0
+?:[EQU $RACE Half-Elf]
+R:0:0x89/0xA1
+?:[EQU $RACE Elf]
+R:0:0x89/0xA2
+?:[EQU $RACE Hobbit]
+R:0:0x89/0xA3
+?:[EQU $RACE Gnome]
+R:0:0x89/0xA4
+?:[EQU $RACE Dwarf]
+R:0:0x89/0xA5
+?:[EQU $RACE Orc]
+R:0:0x89/0xA6
+?:[EQU $RACE Troll]
+R:0:0x89/0xA7
+?:[EQU $RACE Dunadan]
+R:0:0x89/0xA8
+?:[EQU $RACE High-Elf]
+R:0:0x89/0xA9
+?:[EQU $RACE Half-Ogre]
+R:0:0x89/0xAA
+?:[EQU $RACE Beorning]
+R:0:0x89/0xAB
+?:[EQU $RACE Kobold]
+R:0:0x89/0xAC
+?:[EQU $RACE Petty-Dwarf]
+R:0:0x89/0xAD
+?:[EQU $RACE Dark-Elf]
+R:0:0x89/0xAE
+?:[EQU $RACE Ent]
+R:0:0x89/0xAF
+?:[EQU $RACE RohanKnight]
+R:0:0x89/0xB0
+?:[EQU $RACE Thunderlord]
+R:0:0x89/0xB1
+?:[EQU $RACE DeathMold]
+R:0:0x89/0xB2
+?:[EQU $RACE Yeek]
+R:0:0x89/0xB3
+?:[EQU $RACE Wood-Elf]
+R:0:0x89/0xB4
+?:[EQU $RACE Maia]
+R:0:0x89/0xB5
+
+##### Remap the player icon #####
+
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Human] ]
+R:0:0x92/0x80
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Elf] ]
+R:0:0x92/0x81
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Elf] ]
+R:0:0x92/0x82
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Hobbit] ]
+R:0:0x92/0x83
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Gnome] ]
+R:0:0x92/0x84
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Dwarf] ]
+R:0:0x92/0x85
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Orc] ]
+R:0:0x92/0x86
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Troll] ]
+R:0:0x92/0x87
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Dunadan] ]
+R:0:0x92/0x88
+?:[AND [EQU $CLASS Warrior] [EQU $RACE High-Elf] ]
+R:0:0x92/0x89
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Dunadan] ]
+R:0:0x92/0x8A
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Barbarian] ]
+R:0:0x92/0x8B
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Ogre] ]
+R:0:0x92/0x8C
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Giant] ]
+R:0:0x92/0x8D
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Titan] ]
+R:0:0x92/0x8E
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Kobold] ]
+R:0:0x92/0x92
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Nibelung] ]
+R:0:0x92/0x93
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Dark Elf] ]
+R:0:0x92/0x94
+?:[AND [EQU $CLASS Warrior] [EQU $RACE RohanKnight] ]
+R:0:0x93/0x91
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Thunderlord] ]
+R:0:0x92/0x95
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Mindflayer] ]
+R:0:0x92/0x96
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Imp] ]
+R:0:0x92/0x97
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Ent] ]
+R:0:0x92/0x98
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Skeleton] ]
+R:0:0x92/0x99
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Zombie] ]
+R:0:0x92/0x9A
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Vampire] ]
+R:0:0x92/0x9B
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Spectre] ]
+R:0:0x92/0x9C
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Sprite] ]
+R:0:0x92/0x9D
+?:[AND [EQU $CLASS Warrior] [EQU $RACE DeathMold] ]
+R:0:0x92/0x9E
+
+?:[AND [EQU $CLASS Mage] [EQU $RACE Human] ]
+R:0:0x93/0x80
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Elf] ]
+R:0:0x93/0x81
+?:[AND [EQU $CLASS Mage] [EQU $RACE Elf] ]
+R:0:0x93/0x82
+?:[AND [EQU $CLASS Mage] [EQU $RACE Hobbit] ]
+R:0:0x93/0x83
+?:[AND [EQU $CLASS Mage] [EQU $RACE Gnome] ]
+R:0:0x93/0x84
+?:[AND [EQU $CLASS Mage] [EQU $RACE Dwarf] ]
+R:0:0x93/0x85
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Orc] ]
+R:0:0x93/0x86
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Troll] ]
+R:0:0x93/0x87
+?:[AND [EQU $CLASS Mage] [EQU $RACE Dunadan] ]
+R:0:0x93/0x88
+?:[AND [EQU $CLASS Mage] [EQU $RACE High-Elf] ]
+R:0:0x93/0x89
+?:[AND [EQU $CLASS Mage] [EQU $RACE Dunadan] ]
+R:0:0x93/0x8A
+?:[AND [EQU $CLASS Mage] [EQU $RACE Barbarian] ]
+R:0:0x93/0x8B
+?:[AND [EQU $CLASS Mage] [EQU $RACE Ogre] ]
+R:0:0x93/0x8C
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Giant] ]
+R:0:0x93/0x8D
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Titan] ]
+R:0:0x93/0x8E
+?:[AND [EQU $CLASS Mage] [EQU $RACE Cyclops] ]
+R:0:0x93/0x8F
+?:[AND [EQU $CLASS Mage] [EQU $RACE Yeek] ]
+R:0:0x93/0x90
+?:[AND [EQU $CLASS Mage] [EQU $RACE RohanKnight] ]
+R:0:0x93/0x91
+?:[AND [EQU $CLASS Mage] [EQU $RACE Kobold] ]
+R:0:0x93/0x92
+?:[AND [EQU $CLASS Mage] [EQU $RACE Nibelung] ]
+R:0:0x93/0x93
+?:[AND [EQU $CLASS Mage] [EQU $RACE Dark Elf] ]
+R:0:0x93/0x94
+?:[AND [EQU $CLASS Mage] [EQU $RACE Thunderlord] ]
+R:0:0x93/0x95
+?:[AND [EQU $CLASS Mage] [EQU $RACE Mindflayer] ]
+R:0:0x93/0x96
+?:[AND [EQU $CLASS Mage] [EQU $RACE Imp] ]
+R:0:0x93/0x97
+?:[AND [EQU $CLASS Mage] [EQU $RACE Ent] ]
+R:0:0x93/0x98
+?:[AND [EQU $CLASS Mage] [EQU $RACE Skeleton] ]
+R:0:0x93/0x99
+?:[AND [EQU $CLASS Mage] [EQU $RACE Zombie] ]
+R:0:0x93/0x9A
+?:[AND [EQU $CLASS Mage] [EQU $RACE Vampire] ]
+R:0:0x93/0x9B
+?:[AND [EQU $CLASS Mage] [EQU $RACE Spectre] ]
+R:0:0x93/0x9C
+?:[AND [EQU $CLASS Mage] [EQU $RACE Sprite] ]
+R:0:0x93/0x9D
+?:[AND [EQU $CLASS Mage] [EQU $RACE DeathMold] ]
+R:0:0x93/0x9E
+
+?:[AND [EQU $CLASS Priest] [EQU $RACE Human] ]
+R:0:0x94/0x80
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Elf] ]
+R:0:0x94/0x81
+?:[AND [EQU $CLASS Priest] [EQU $RACE Elf] ]
+R:0:0x94/0x82
+?:[AND [EQU $CLASS Priest] [EQU $RACE Hobbit] ]
+R:0:0x94/0x83
+?:[AND [EQU $CLASS Priest] [EQU $RACE Gnome] ]
+R:0:0x94/0x84
+?:[AND [EQU $CLASS Priest] [EQU $RACE Dwarf] ]
+R:0:0x94/0x85
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Orc] ]
+R:0:0x94/0x86
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Troll] ]
+R:0:0x94/0x87
+?:[AND [EQU $CLASS Priest] [EQU $RACE Dunadan] ]
+R:0:0x94/0x88
+?:[AND [EQU $CLASS Priest] [EQU $RACE High-Elf] ]
+R:0:0x94/0x89
+?:[AND [EQU $CLASS Priest] [EQU $RACE Dunadan] ]
+R:0:0x94/0x8A
+?:[AND [EQU $CLASS Priest] [EQU $RACE Barbarian] ]
+R:0:0x94/0x8B
+?:[AND [EQU $CLASS Priest] [EQU $RACE Ogre] ]
+R:0:0x94/0x8C
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Giant] ]
+R:0:0x94/0x8D
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Titan] ]
+R:0:0x94/0x8E
+?:[AND [EQU $CLASS Priest] [EQU $RACE Cyclops] ]
+R:0:0x94/0x8F
+?:[AND [EQU $CLASS Priest] [EQU $RACE Yeek] ]
+R:0:0x94/0x90
+?:[AND [EQU $CLASS Priest] [EQU $RACE RohanKnight] ]
+R:0:0x94/0x91
+?:[AND [EQU $CLASS Priest] [EQU $RACE Kobold] ]
+R:0:0x94/0x92
+?:[AND [EQU $CLASS Priest] [EQU $RACE Nibelung] ]
+R:0:0x94/0x93
+?:[AND [EQU $CLASS Priest] [EQU $RACE Dark Elf] ]
+R:0:0x94/0x94
+?:[AND [EQU $CLASS Priest] [EQU $RACE Thunderlord] ]
+R:0:0x94/0x95
+?:[AND [EQU $CLASS Priest] [EQU $RACE Mindflayer] ]
+R:0:0x94/0x96
+?:[AND [EQU $CLASS Priest] [EQU $RACE Imp] ]
+R:0:0x94/0x97
+?:[AND [EQU $CLASS Priest] [EQU $RACE Ent] ]
+R:0:0x94/0x98
+?:[AND [EQU $CLASS Priest] [EQU $RACE Skeleton] ]
+R:0:0x94/0x99
+?:[AND [EQU $CLASS Priest] [EQU $RACE Zombie] ]
+R:0:0x94/0x9A
+?:[AND [EQU $CLASS Priest] [EQU $RACE Vampire] ]
+R:0:0x94/0x9B
+?:[AND [EQU $CLASS Priest] [EQU $RACE Spectre] ]
+R:0:0x94/0x9C
+?:[AND [EQU $CLASS Priest] [EQU $RACE Sprite] ]
+R:0:0x94/0x9D
+?:[AND [EQU $CLASS Priest] [EQU $RACE DeathMold] ]
+R:0:0x94/0x9E
+
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Human] ]
+R:0:0x95/0x80
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Elf] ]
+R:0:0x95/0x81
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Elf] ]
+R:0:0x95/0x82
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Hobbit] ]
+R:0:0x95/0x83
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Gnome] ]
+R:0:0x95/0x84
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Dwarf] ]
+R:0:0x95/0x85
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Orc] ]
+R:0:0x95/0x86
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Troll] ]
+R:0:0x95/0x87
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Dunadan] ]
+R:0:0x95/0x88
+?:[AND [EQU $CLASS Rogue] [EQU $RACE High-Elf] ]
+R:0:0x95/0x89
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Dunadan] ]
+R:0:0x95/0x8A
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Barbarian] ]
+R:0:0x95/0x8B
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Ogre] ]
+R:0:0x95/0x8C
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Giant] ]
+R:0:0x95/0x8D
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Titan] ]
+R:0:0x95/0x8E
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Cyclops] ]
+R:0:0x95/0x8F
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Yeek] ]
+R:0:0x95/0x90
+?:[AND [EQU $CLASS Rogue] [EQU $RACE RohanKnight] ]
+R:0:0x95/0x91
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Kobold] ]
+R:0:0x95/0x92
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Nibelung] ]
+R:0:0x95/0x93
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Dark Elf] ]
+R:0:0x95/0x94
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Thunderlord] ]
+R:0:0x95/0x95
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Mindflayer] ]
+R:0:0x95/0x96
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Imp] ]
+R:0:0x95/0x97
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Ent] ]
+R:0:0x95/0x98
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Skeleton] ]
+R:0:0x95/0x99
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Zombie] ]
+R:0:0x95/0x9A
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Vampire] ]
+R:0:0x95/0x9B
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Spectre] ]
+R:0:0x95/0x9C
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Sprite] ]
+R:0:0x95/0x9D
+?:[AND [EQU $CLASS Rogue] [EQU $RACE DeathMold] ]
+R:0:0x95/0x9E
+
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Human] ]
+R:0:0x96/0x80
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Elf] ]
+R:0:0x96/0x81
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Elf] ]
+R:0:0x96/0x82
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Hobbit] ]
+R:0:0x96/0x83
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Gnome] ]
+R:0:0x96/0x84
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Dwarf] ]
+R:0:0x96/0x85
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Orc] ]
+R:0:0x96/0x86
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Troll] ]
+R:0:0x96/0x87
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Dunadan] ]
+R:0:0x96/0x88
+?:[AND [EQU $CLASS Ranger] [EQU $RACE High-Elf] ]
+R:0:0x96/0x89
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Dunadan] ]
+R:0:0x96/0x8A
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Barbarian] ]
+R:0:0x96/0x8B
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Ogre] ]
+R:0:0x96/0x8C
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Giant] ]
+R:0:0x96/0x8D
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Titan] ]
+R:0:0x96/0x8E
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Cyclops] ]
+R:0:0x96/0x8F
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Yeek] ]
+R:0:0x96/0x90
+?:[AND [EQU $CLASS Ranger] [EQU $RACE RohanKnight] ]
+R:0:0x96/0x91
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Kobold] ]
+R:0:0x96/0x92
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Nibelung] ]
+R:0:0x96/0x93
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Dark Elf] ]
+R:0:0x96/0x94
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Thunderlord] ]
+R:0:0x96/0x95
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Mindflayer] ]
+R:0:0x96/0x96
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Imp] ]
+R:0:0x96/0x97
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Ent] ]
+R:0:0x96/0x98
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Skeleton] ]
+R:0:0x96/0x99
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Zombie] ]
+R:0:0x96/0x9A
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Vampire] ]
+R:0:0x96/0x9B
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Spectre] ]
+R:0:0x96/0x9C
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Sprite] ]
+R:0:0x96/0x9D
+?:[AND [EQU $CLASS Ranger] [EQU $RACE DeathMold] ]
+R:0:0x96/0x9E
+
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Human] ]
+R:0:0x97/0x80
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Elf] ]
+R:0:0x97/0x81
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Elf] ]
+R:0:0x97/0x82
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Hobbit] ]
+R:0:0x97/0x83
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Gnome] ]
+R:0:0x97/0x84
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Dwarf] ]
+R:0:0x97/0x85
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Orc] ]
+R:0:0x97/0x86
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Troll] ]
+R:0:0x97/0x87
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Dunadan] ]
+R:0:0x97/0x88
+?:[AND [EQU $CLASS Paladin] [EQU $RACE High-Elf] ]
+R:0:0x97/0x89
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Dunadan] ]
+R:0:0x97/0x8A
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Barbarian] ]
+R:0:0x97/0x8B
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Ogre] ]
+R:0:0x97/0x8C
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Giant] ]
+R:0:0x97/0x8D
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Titan] ]
+R:0:0x97/0x8E
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Cyclops] ]
+R:0:0x97/0x8F
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Yeek] ]
+R:0:0x97/0x90
+?:[AND [EQU $CLASS Paladin] [EQU $RACE RohanKnight] ]
+R:0:0x97/0x91
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Kobold] ]
+R:0:0x97/0x92
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Nibelung] ]
+R:0:0x97/0x93
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Dark Elf] ]
+R:0:0x97/0x94
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Thunderlord] ]
+R:0:0x97/0x95
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Mindflayer] ]
+R:0:0x97/0x96
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Imp] ]
+R:0:0x97/0x97
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Ent] ]
+R:0:0x97/0x98
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Skeleton] ]
+R:0:0x97/0x99
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Zombie] ]
+R:0:0x97/0x9A
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Vampire] ]
+R:0:0x97/0x9B
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Spectre] ]
+R:0:0x97/0x9C
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Sprite] ]
+R:0:0x97/0x9D
+?:[AND [EQU $CLASS Paladin] [EQU $RACE DeathMold] ]
+R:0:0x97/0x9E
+
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Human] ]
+R:0:0x98/0x80
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Elf] ]
+R:0:0x98/0x81
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Elf] ]
+R:0:0x98/0x82
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Hobbit] ]
+R:0:0x98/0x83
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Gnome] ]
+R:0:0x98/0x84
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dwarf] ]
+R:0:0x98/0x85
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Orc] ]
+R:0:0x98/0x86
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Troll] ]
+R:0:0x98/0x87
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dunadan] ]
+R:0:0x98/0x88
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE High-Elf] ]
+R:0:0x98/0x89
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dunadan] ]
+R:0:0x98/0x8A
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Barbarian] ]
+R:0:0x98/0x8B
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Ogre] ]
+R:0:0x98/0x8C
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Giant] ]
+R:0:0x98/0x8D
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Half-Titan] ]
+R:0:0x98/0x8E
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Cyclops] ]
+R:0:0x98/0x8F
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Yeek] ]
+R:0:0x98/0x90
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE RohanKnight] ]
+R:0:0x98/0x91
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Kobold] ]
+R:0:0x98/0x92
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Nibelung] ]
+R:0:0x98/0x93
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Dark Elf] ]
+R:0:0x98/0x94
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Thunderlord] ]
+R:0:0x98/0x95
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Mindflayer] ]
+R:0:0x98/0x96
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Imp] ]
+R:0:0x98/0x97
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Ent] ]
+R:0:0x98/0x98
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Skeleton] ]
+R:0:0x98/0x99
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Zombie] ]
+R:0:0x98/0x9A
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Vampire] ]
+R:0:0x98/0x9B
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Spectre] ]
+R:0:0x98/0x9C
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE Sprite] ]
+R:0:0x98/0x9D
+?:[AND [EQU $CLASS Warrior-Mage] [EQU $RACE DeathMold] ]
+R:0:0x98/0x9E
+
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Human] ]
+R:0:0x99/0x80
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Elf] ]
+R:0:0x99/0x81
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Elf] ]
+R:0:0x99/0x82
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Hobbit] ]
+R:0:0x99/0x83
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Gnome] ]
+R:0:0x99/0x84
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dwarf] ]
+R:0:0x99/0x85
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Orc] ]
+R:0:0x99/0x86
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Troll] ]
+R:0:0x99/0x87
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dunadan] ]
+R:0:0x99/0x88
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE High-Elf] ]
+R:0:0x99/0x89
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dunadan] ]
+R:0:0x99/0x8A
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Barbarian] ]
+R:0:0x99/0x8B
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Ogre] ]
+R:0:0x99/0x8C
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Giant] ]
+R:0:0x99/0x8D
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Half-Titan] ]
+R:0:0x99/0x8E
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Cyclops] ]
+R:0:0x99/0x8F
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Yeek] ]
+R:0:0x99/0x90
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE RohanKnight] ]
+R:0:0x99/0x91
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Kobold] ]
+R:0:0x99/0x92
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Nibelung] ]
+R:0:0x99/0x93
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Dark Elf] ]
+R:0:0x99/0x94
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Thunderlord] ]
+R:0:0x99/0x95
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Mindflayer] ]
+R:0:0x99/0x96
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Imp] ]
+R:0:0x99/0x97
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Ent] ]
+R:0:0x99/0x98
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Skeleton] ]
+R:0:0x99/0x99
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Zombie] ]
+R:0:0x99/0x9A
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Vampire] ]
+R:0:0x99/0x9B
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Spectre] ]
+R:0:0x99/0x9C
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE Sprite] ]
+R:0:0x99/0x9D
+?:[AND [EQU $CLASS Chaos-Warrior] [EQU $RACE DeathMold] ]
+R:0:0x99/0x9E
+
+?:[AND [EQU $CLASS Monk] [EQU $RACE Human] ]
+R:0:0x9A/0x80
+?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Elf] ]
+R:0:0x9A/0x81
+?:[AND [EQU $CLASS Monk] [EQU $RACE Elf] ]
+R:0:0x9A/0x82
+?:[AND [EQU $CLASS Monk] [EQU $RACE Hobbit] ]
+R:0:0x9A/0x83
+?:[AND [EQU $CLASS Monk] [EQU $RACE Gnome] ]
+R:0:0x9A/0x84
+?:[AND [EQU $CLASS Monk] [EQU $RACE Dwarf] ]
+R:0:0x9A/0x85
+?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Orc] ]
+R:0:0x9A/0x86
+?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Troll] ]
+R:0:0x9A/0x87
+?:[AND [EQU $CLASS Monk] [EQU $RACE Dunadan] ]
+R:0:0x9A/0x88
+?:[AND [EQU $CLASS Monk] [EQU $RACE High-Elf] ]
+R:0:0x9A/0x89
+?:[AND [EQU $CLASS Monk] [EQU $RACE Dunadan] ]
+R:0:0x9A/0x8A
+?:[AND [EQU $CLASS Monk] [EQU $RACE Barbarian] ]
+R:0:0x9A/0x8B
+?:[AND [EQU $CLASS Monk] [EQU $RACE Ogre] ]
+R:0:0x9A/0x8C
+?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Giant] ]
+R:0:0x9A/0x8D
+?:[AND [EQU $CLASS Monk] [EQU $RACE Half-Titan] ]
+R:0:0x9A/0x8E
+?:[AND [EQU $CLASS Monk] [EQU $RACE Cyclops] ]
+R:0:0x9A/0x8F
+?:[AND [EQU $CLASS Monk] [EQU $RACE Yeek] ]
+R:0:0x9A/0x90
+?:[AND [EQU $CLASS Monk] [EQU $RACE RohanKnight] ]
+R:0:0x9A/0x91
+?:[AND [EQU $CLASS Monk] [EQU $RACE Kobold] ]
+R:0:0x9A/0x92
+?:[AND [EQU $CLASS Monk] [EQU $RACE Nibelung] ]
+R:0:0x9A/0x93
+?:[AND [EQU $CLASS Monk] [EQU $RACE Dark Elf] ]
+R:0:0x9A/0x94
+?:[AND [EQU $CLASS Monk] [EQU $RACE Thunderlord] ]
+R:0:0x9A/0x95
+?:[AND [EQU $CLASS Monk] [EQU $RACE Mindflayer] ]
+R:0:0x9A/0x96
+?:[AND [EQU $CLASS Monk] [EQU $RACE Imp] ]
+R:0:0x9A/0x97
+?:[AND [EQU $CLASS Monk] [EQU $RACE Ent] ]
+R:0:0x9A/0x98
+?:[AND [EQU $CLASS Monk] [EQU $RACE Skeleton] ]
+R:0:0x9A/0x99
+?:[AND [EQU $CLASS Monk] [EQU $RACE Zombie] ]
+R:0:0x9A/0x9A
+?:[AND [EQU $CLASS Monk] [EQU $RACE Vampire] ]
+R:0:0x9A/0x9B
+?:[AND [EQU $CLASS Monk] [EQU $RACE Spectre] ]
+R:0:0x9A/0x9C
+?:[AND [EQU $CLASS Monk] [EQU $RACE Sprite] ]
+R:0:0x9A/0x9D
+?:[AND [EQU $CLASS Monk] [EQU $RACE DeathMold] ]
+R:0:0x9A/0x9E
+
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Human] ]
+R:0:0x9B/0x80
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Elf] ]
+R:0:0x9B/0x81
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Elf] ]
+R:0:0x9B/0x82
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Hobbit] ]
+R:0:0x9B/0x83
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Gnome] ]
+R:0:0x9B/0x84
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dwarf] ]
+R:0:0x9B/0x85
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Orc] ]
+R:0:0x9B/0x86
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Troll] ]
+R:0:0x9B/0x87
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dunadan] ]
+R:0:0x9B/0x88
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE High-Elf] ]
+R:0:0x9B/0x89
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dunadan] ]
+R:0:0x9B/0x8A
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Barbarian] ]
+R:0:0x9B/0x8B
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Ogre] ]
+R:0:0x9B/0x8C
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Giant] ]
+R:0:0x9B/0x8D
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Half-Titan] ]
+R:0:0x9B/0x8E
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Cyclops] ]
+R:0:0x9B/0x8F
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Yeek] ]
+R:0:0x9B/0x90
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE RohanKnight] ]
+R:0:0x9B/0x91
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Kobold] ]
+R:0:0x9B/0x92
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Nibelung] ]
+R:0:0x9B/0x93
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Dark Elf] ]
+R:0:0x9B/0x94
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Thunderlord] ]
+R:0:0x9B/0x95
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Mindflayer] ]
+R:0:0x9B/0x96
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Imp] ]
+R:0:0x9B/0x97
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Ent] ]
+R:0:0x9B/0x98
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Skeleton] ]
+R:0:0x9B/0x99
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Zombie] ]
+R:0:0x9B/0x9A
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Vampire] ]
+R:0:0x9B/0x9B
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Spectre] ]
+R:0:0x9B/0x9C
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE Sprite] ]
+R:0:0x9B/0x9D
+?:[AND [EQU $CLASS Mindcrafter] [EQU $RACE DeathMold] ]
+R:0:0x9B/0x9E
+
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Human] ]
+R:0:0x9C/0x80
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Elf] ]
+R:0:0x9C/0x81
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Elf] ]
+R:0:0x9C/0x82
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Hobbit] ]
+R:0:0x9C/0x83
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Gnome] ]
+R:0:0x9C/0x84
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dwarf] ]
+R:0:0x9C/0x85
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Orc] ]
+R:0:0x9C/0x86
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Troll] ]
+R:0:0x9C/0x87
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dunadan] ]
+R:0:0x9C/0x88
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE High-Elf] ]
+R:0:0x9C/0x89
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dunadan] ]
+R:0:0x9C/0x8A
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Barbarian] ]
+R:0:0x9C/0x8B
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Ogre] ]
+R:0:0x9C/0x8C
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Giant] ]
+R:0:0x9C/0x8D
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Half-Titan] ]
+R:0:0x9C/0x8E
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Cyclops] ]
+R:0:0x9C/0x8F
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Yeek] ]
+R:0:0x9C/0x90
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE RohanKnight] ]
+R:0:0x9C/0x91
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Kobold] ]
+R:0:0x9C/0x92
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Nibelung] ]
+R:0:0x9C/0x93
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Dark Elf] ]
+R:0:0x9C/0x94
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Thunderlord] ]
+R:0:0x9C/0x95
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Mindflayer] ]
+R:0:0x9C/0x96
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Imp] ]
+R:0:0x9C/0x97
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Ent] ]
+R:0:0x9C/0x98
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Skeleton] ]
+R:0:0x9C/0x99
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Zombie] ]
+R:0:0x9C/0x9A
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Vampire] ]
+R:0:0x9C/0x9B
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Spectre] ]
+R:0:0x9C/0x9C
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE Sprite] ]
+R:0:0x9C/0x9D
+?:[AND [EQU $CLASS High-Mage] [EQU $RACE DeathMold] ]
+R:0:0x9C/0x9E
+
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Human] ]
+R:0:0x9C/0x80
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Elf] ]
+R:0:0x9C/0x81
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Elf] ]
+R:0:0x9C/0x82
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Hobbit] ]
+R:0:0x9C/0x83
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Gnome] ]
+R:0:0x9C/0x84
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dwarf] ]
+R:0:0x9C/0x85
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Orc] ]
+R:0:0x9C/0x86
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Troll] ]
+R:0:0x9C/0x87
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dunadan] ]
+R:0:0x9C/0x88
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE High-Elf] ]
+R:0:0x9C/0x89
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dunadan] ]
+R:0:0x9C/0x8A
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Barbarian] ]
+R:0:0x9C/0x8B
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Ogre] ]
+R:0:0x9C/0x8C
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Giant] ]
+R:0:0x9C/0x8D
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Titan] ]
+R:0:0x9C/0x8E
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Cyclops] ]
+R:0:0x9C/0x8F
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Yeek] ]
+R:0:0x9C/0x90
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE RohanKnight] ]
+R:0:0x9C/0x91
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Kobold] ]
+R:0:0x9C/0x92
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Nibelung] ]
+R:0:0x9C/0x93
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dark Elf] ]
+R:0:0x9C/0x94
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Thunderlord] ]
+R:0:0x9C/0x95
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Mindflayer] ]
+R:0:0x9C/0x96
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Imp] ]
+R:0:0x9C/0x97
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Ent] ]
+R:0:0x9C/0x98
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Skeleton] ]
+R:0:0x9C/0x99
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Zombie] ]
+R:0:0x9C/0x9A
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Vampire] ]
+R:0:0x9C/0x9B
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Spectre] ]
+R:0:0x9C/0x9C
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Sprite] ]
+R:0:0x9C/0x9D
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE DeathMold] ]
+R:0:0x9C/0x9E
+
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Human] ]
+R:0:0x96/0x80
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Half-Elf] ]
+R:0:0x96/0x81
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Elf] ]
+R:0:0x96/0x82
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Hobbit] ]
+R:0:0x96/0x83
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Gnome] ]
+R:0:0x96/0x84
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Dwarf] ]
+R:0:0x96/0x85
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Half-Orc] ]
+R:0:0x96/0x86
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Half-Troll] ]
+R:0:0x96/0x87
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Dunadan] ]
+R:0:0x96/0x88
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE High-Elf] ]
+R:0:0x96/0x89
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Dunadan] ]
+R:0:0x96/0x8A
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Barbarian] ]
+R:0:0x96/0x8B
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Ogre] ]
+R:0:0x96/0x8C
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Half-Giant] ]
+R:0:0x96/0x8D
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Half-Titan] ]
+R:0:0x96/0x8E
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Cyclops] ]
+R:0:0x96/0x8F
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Yeek] ]
+R:0:0x96/0x90
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE RohanKnight] ]
+R:0:0x96/0x91
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Kobold] ]
+R:0:0x96/0x92
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Nibelung] ]
+R:0:0x96/0x93
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Dark Elf] ]
+R:0:0x96/0x94
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Thunderlord] ]
+R:0:0x96/0x95
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Mindflayer] ]
+R:0:0x96/0x96
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Imp] ]
+R:0:0x96/0x97
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Ent] ]
+R:0:0x96/0x98
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Skeleton] ]
+R:0:0x96/0x99
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Zombie] ]
+R:0:0x96/0x9A
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Vampire] ]
+R:0:0x96/0x9B
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Spectre] ]
+R:0:0x96/0x9C
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE Sprite] ]
+R:0:0x96/0x9D
+?:[AND [EQU $CLASS BeastMaster] [EQU $RACE DeathMold] ]
+R:0:0x96/0x9E
+
+?:[EQU $CLASS Mimic]
+R:0:0x91/0x95
+
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Human] ]
+R:0:0x93/0x80
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Elf] ]
+R:0:0x93/0x81
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Elf] ]
+R:0:0x93/0x82
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Hobbit] ]
+R:0:0x93/0x83
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Gnome] ]
+R:0:0x93/0x84
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dwarf] ]
+R:0:0x93/0x85
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Orc] ]
+R:0:0x93/0x86
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Troll] ]
+R:0:0x93/0x87
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dunadan] ]
+R:0:0x93/0x88
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE High-Elf] ]
+R:0:0x93/0x89
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dunadan] ]
+R:0:0x93/0x8A
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Barbarian] ]
+R:0:0x93/0x8B
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Ogre] ]
+R:0:0x93/0x8C
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Giant] ]
+R:0:0x93/0x8D
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Half-Titan] ]
+R:0:0x93/0x8E
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Cyclops] ]
+R:0:0x93/0x8F
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Yeek] ]
+R:0:0x93/0x90
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE RohanKnight] ]
+R:0:0x93/0x91
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Kobold] ]
+R:0:0x93/0x92
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Nibelung] ]
+R:0:0x93/0x93
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Dark Elf] ]
+R:0:0x93/0x94
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Thunderlord] ]
+R:0:0x93/0x95
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Mindflayer] ]
+R:0:0x93/0x96
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Imp] ]
+R:0:0x93/0x97
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Ent] ]
+R:0:0x93/0x98
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Skeleton] ]
+R:0:0x93/0x99
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Zombie] ]
+R:0:0x93/0x9A
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Vampire] ]
+R:0:0x93/0x9B
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Spectre] ]
+R:0:0x93/0x9C
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE Sprite] ]
+R:0:0x93/0x9D
+?:[AND [EQU $CLASS Alchemist] [EQU $RACE DeathMold] ]
+R:0:0x93/0x9E
+
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Human] ]
+R:0:0x93/0x80
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Half-Elf] ]
+R:0:0x93/0x81
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Elf] ]
+R:0:0x93/0x82
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Hobbit] ]
+R:0:0x93/0x83
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Gnome] ]
+R:0:0x93/0x84
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Dwarf] ]
+R:0:0x93/0x85
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Half-Orc] ]
+R:0:0x93/0x86
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Half-Troll] ]
+R:0:0x93/0x87
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Dunadan] ]
+R:0:0x93/0x88
+?:[AND [EQU $CLASS Wizard] [EQU $RACE High-Elf] ]
+R:0:0x93/0x89
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Dunadan] ]
+R:0:0x93/0x8A
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Barbarian] ]
+R:0:0x93/0x8B
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Ogre] ]
+R:0:0x93/0x8C
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Half-Giant] ]
+R:0:0x93/0x8D
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Half-Titan] ]
+R:0:0x93/0x8E
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Cyclops] ]
+R:0:0x93/0x8F
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Yeek] ]
+R:0:0x93/0x90
+?:[AND [EQU $CLASS Wizard] [EQU $RACE RohanKnight] ]
+R:0:0x93/0x91
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Kobold] ]
+R:0:0x93/0x92
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Nibelung] ]
+R:0:0x93/0x93
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Dark Elf] ]
+R:0:0x93/0x94
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Thunderlord] ]
+R:0:0x93/0x95
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Mindflayer] ]
+R:0:0x93/0x96
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Imp] ]
+R:0:0x93/0x97
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Ent] ]
+R:0:0x93/0x98
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Skeleton] ]
+R:0:0x93/0x99
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Zombie] ]
+R:0:0x93/0x9A
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Vampire] ]
+R:0:0x93/0x9B
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Spectre] ]
+R:0:0x93/0x9C
+?:[AND [EQU $CLASS Wizard] [EQU $RACE Sprite] ]
+R:0:0x93/0x9D
+?:[AND [EQU $CLASS Wizard] [EQU $RACE DeathMold] ]
+R:0:0x93/0x9E
+
+?:[AND [EQU $CLASS Prior] [EQU $RACE Human] ]
+R:0:0x94/0x80
+?:[AND [EQU $CLASS Prior] [EQU $RACE Half-Elf] ]
+R:0:0x94/0x81
+?:[AND [EQU $CLASS Prior] [EQU $RACE Elf] ]
+R:0:0x94/0x82
+?:[AND [EQU $CLASS Prior] [EQU $RACE Hobbit] ]
+R:0:0x94/0x83
+?:[AND [EQU $CLASS Prior] [EQU $RACE Gnome] ]
+R:0:0x94/0x84
+?:[AND [EQU $CLASS Prior] [EQU $RACE Dwarf] ]
+R:0:0x94/0x85
+?:[AND [EQU $CLASS Prior] [EQU $RACE Half-Orc] ]
+R:0:0x94/0x86
+?:[AND [EQU $CLASS Prior] [EQU $RACE Half-Troll] ]
+R:0:0x94/0x87
+?:[AND [EQU $CLASS Prior] [EQU $RACE Dunadan] ]
+R:0:0x94/0x88
+?:[AND [EQU $CLASS Prior] [EQU $RACE High-Elf] ]
+R:0:0x94/0x89
+?:[AND [EQU $CLASS Prior] [EQU $RACE Dunadan] ]
+R:0:0x94/0x8A
+?:[AND [EQU $CLASS Prior] [EQU $RACE Barbarian] ]
+R:0:0x94/0x8B
+?:[AND [EQU $CLASS Prior] [EQU $RACE Ogre] ]
+R:0:0x94/0x8C
+?:[AND [EQU $CLASS Prior] [EQU $RACE Half-Giant] ]
+R:0:0x94/0x8D
+?:[AND [EQU $CLASS Prior] [EQU $RACE Half-Titan] ]
+R:0:0x94/0x8E
+?:[AND [EQU $CLASS Prior] [EQU $RACE Cyclops] ]
+R:0:0x94/0x8F
+?:[AND [EQU $CLASS Prior] [EQU $RACE Yeek] ]
+R:0:0x94/0x90
+?:[AND [EQU $CLASS Prior] [EQU $RACE RohanKnight] ]
+R:0:0x94/0x91
+?:[AND [EQU $CLASS Prior] [EQU $RACE Kobold] ]
+R:0:0x94/0x92
+?:[AND [EQU $CLASS Prior] [EQU $RACE Nibelung] ]
+R:0:0x94/0x93
+?:[AND [EQU $CLASS Prior] [EQU $RACE Dark Elf] ]
+R:0:0x94/0x94
+?:[AND [EQU $CLASS Prior] [EQU $RACE Thunderlord] ]
+R:0:0x94/0x95
+?:[AND [EQU $CLASS Prior] [EQU $RACE Mindflayer] ]
+R:0:0x94/0x96
+?:[AND [EQU $CLASS Prior] [EQU $RACE Imp] ]
+R:0:0x94/0x97
+?:[AND [EQU $CLASS Prior] [EQU $RACE Ent] ]
+R:0:0x94/0x98
+?:[AND [EQU $CLASS Prior] [EQU $RACE Skeleton] ]
+R:0:0x94/0x99
+?:[AND [EQU $CLASS Prior] [EQU $RACE Zombie] ]
+R:0:0x94/0x9A
+?:[AND [EQU $CLASS Prior] [EQU $RACE Vampire] ]
+R:0:0x94/0x9B
+?:[AND [EQU $CLASS Prior] [EQU $RACE Spectre] ]
+R:0:0x94/0x9C
+?:[AND [EQU $CLASS Prior] [EQU $RACE Sprite] ]
+R:0:0x94/0x9D
+?:[AND [EQU $CLASS Prior] [EQU $RACE DeathMold] ]
+R:0:0x94/0x9E
+
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Human] ]
+R:0:0x92/0x80
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Half-Elf] ]
+R:0:0x92/0x81
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Elf] ]
+R:0:0x92/0x82
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Hobbit] ]
+R:0:0x92/0x83
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Gnome] ]
+R:0:0x92/0x84
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Dwarf] ]
+R:0:0x92/0x85
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Half-Orc] ]
+R:0:0x92/0x86
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Half-Troll] ]
+R:0:0x92/0x87
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Dunadan] ]
+R:0:0x92/0x88
+?:[AND [EQU $CLASS Possessor] [EQU $RACE High-Elf] ]
+R:0:0x92/0x89
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Dunadan] ]
+R:0:0x92/0x8A
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Barbarian] ]
+R:0:0x92/0x8B
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Ogre] ]
+R:0:0x92/0x8C
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Half-Giant] ]
+R:0:0x92/0x8D
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Half-Titan] ]
+R:0:0x92/0x8E
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Kobold] ]
+R:0:0x92/0x92
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Nibelung] ]
+R:0:0x92/0x93
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Dark Elf] ]
+R:0:0x92/0x94
+?:[AND [EQU $CLASS Possessor] [EQU $RACE RohanKnight] ]
+R:0:0x93/0x91
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Thunderlord] ]
+R:0:0x92/0x95
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Mindflayer] ]
+R:0:0x92/0x96
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Imp] ]
+R:0:0x92/0x97
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Ent] ]
+R:0:0x92/0x98
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Skeleton] ]
+R:0:0x92/0x99
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Zombie] ]
+R:0:0x92/0x9A
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Vampire] ]
+R:0:0x92/0x9B
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Spectre] ]
+R:0:0x92/0x9C
+?:[AND [EQU $CLASS Possessor] [EQU $RACE Sprite] ]
+R:0:0x92/0x9D
+?:[AND [EQU $CLASS Possessor] [EQU $RACE DeathMold] ]
+R:0:0x92/0x9E
+
diff --git a/lib/pref/xtra-xxx.prf b/lib/pref/xtra-xxx.prf
new file mode 100644
index 00000000..0c6186de
--- /dev/null
+++ b/lib/pref/xtra-xxx.prf
@@ -0,0 +1,137 @@
+# File: xtra-xxx.prf
+
+#
+# This file defines special attr/char mappings for use in "graphics" mode
+#
+# See "lib/help/command.txt" and "src/files.c" for more information.
+#
+
+
+##### Remap the player icon #####
+
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Human] ]
+R:0:0x8C/0x80
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Elf] ]
+R:0:0x8C/0x81
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Elf] ]
+R:0:0x8C/0x82
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Hobbit] ]
+R:0:0x8C/0x83
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Gnome] ]
+R:0:0x8C/0x84
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Dwarf] ]
+R:0:0x8C/0x85
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Orc] ]
+R:0:0x8C/0x86
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Half-Troll] ]
+R:0:0x8C/0x87
+?:[AND [EQU $CLASS Warrior] [EQU $RACE Dunadan] ]
+R:0:0x8C/0x88
+?:[AND [EQU $CLASS Warrior] [EQU $RACE High-Elf] ]
+R:0:0x8C/0x89
+
+?:[AND [EQU $CLASS Mage] [EQU $RACE Human] ]
+R:0:0x8C/0x8A
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Elf] ]
+R:0:0x8C/0x8B
+?:[AND [EQU $CLASS Mage] [EQU $RACE Elf] ]
+R:0:0x8C/0x8C
+?:[AND [EQU $CLASS Mage] [EQU $RACE Hobbit] ]
+R:0:0x8C/0x8D
+?:[AND [EQU $CLASS Mage] [EQU $RACE Gnome] ]
+R:0:0x8C/0x8E
+?:[AND [EQU $CLASS Mage] [EQU $RACE Dwarf] ]
+R:0:0x8C/0x8F
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Orc] ]
+R:0:0x8C/0x90
+?:[AND [EQU $CLASS Mage] [EQU $RACE Half-Troll] ]
+R:0:0x8C/0x91
+?:[AND [EQU $CLASS Mage] [EQU $RACE Dunadan] ]
+R:0:0x8C/0x92
+?:[AND [EQU $CLASS Mage] [EQU $RACE High-Elf] ]
+R:0:0x8C/0x93
+
+?:[AND [EQU $CLASS Priest] [EQU $RACE Human] ]
+R:0:0x8C/0x94
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Elf] ]
+R:0:0x8C/0x95
+?:[AND [EQU $CLASS Priest] [EQU $RACE Elf] ]
+R:0:0x8C/0x96
+?:[AND [EQU $CLASS Priest] [EQU $RACE Hobbit] ]
+R:0:0x8C/0x97
+?:[AND [EQU $CLASS Priest] [EQU $RACE Gnome] ]
+R:0:0x8C/0x98
+?:[AND [EQU $CLASS Priest] [EQU $RACE Dwarf] ]
+R:0:0x8C/0x99
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Orc] ]
+R:0:0x8C/0x9A
+?:[AND [EQU $CLASS Priest] [EQU $RACE Half-Troll] ]
+R:0:0x8C/0x9B
+?:[AND [EQU $CLASS Priest] [EQU $RACE Dunadan] ]
+R:0:0x8C/0x9C
+?:[AND [EQU $CLASS Priest] [EQU $RACE High-Elf] ]
+R:0:0x8C/0x9D
+
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Human] ]
+R:0:0x8C/0x9E
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Elf] ]
+R:0:0x8C/0x9F
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Elf] ]
+R:0:0x8D/0x80
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Hobbit] ]
+R:0:0x8D/0x81
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Gnome] ]
+R:0:0x8D/0x82
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Dwarf] ]
+R:0:0x8D/0x83
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Orc] ]
+R:0:0x8D/0x84
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Half-Troll] ]
+R:0:0x8D/0x85
+?:[AND [EQU $CLASS Rogue] [EQU $RACE Dunadan] ]
+R:0:0x8D/0x86
+?:[AND [EQU $CLASS Rogue] [EQU $RACE High-Elf] ]
+R:0:0x8D/0x87
+
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Human] ]
+R:0:0x8D/0x88
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Elf] ]
+R:0:0x8D/0x89
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Elf] ]
+R:0:0x8D/0x8A
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Hobbit] ]
+R:0:0x8D/0x8B
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Gnome] ]
+R:0:0x8D/0x8C
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Dwarf] ]
+R:0:0x8D/0x8D
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Orc] ]
+R:0:0x8D/0x8E
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Half-Troll] ]
+R:0:0x8D/0x8F
+?:[AND [EQU $CLASS Ranger] [EQU $RACE Dunadan] ]
+R:0:0x8D/0x90
+?:[AND [EQU $CLASS Ranger] [EQU $RACE High-Elf] ]
+R:0:0x8D/0x91
+
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Human] ]
+R:0:0x8D/0x92
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Elf] ]
+R:0:0x8D/0x93
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Elf] ]
+R:0:0x8D/0x94
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Hobbit] ]
+R:0:0x8D/0x95
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Gnome] ]
+R:0:0x8D/0x96
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Dwarf] ]
+R:0:0x8D/0x97
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Orc] ]
+R:0:0x8D/0x98
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Half-Troll] ]
+R:0:0x8D/0x99
+?:[AND [EQU $CLASS Paladin] [EQU $RACE Dunadan] ]
+R:0:0x8D/0x9A
+?:[AND [EQU $CLASS Paladin] [EQU $RACE High-Elf] ]
+R:0:0x8D/0x9B
+
diff --git a/lib/save/delete.me b/lib/save/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/save/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/scpt/bounty.lua b/lib/scpt/bounty.lua
new file mode 100644
index 00000000..94c15598
--- /dev/null
+++ b/lib/scpt/bounty.lua
@@ -0,0 +1,90 @@
+-- The bounty quest! bring back corpses to increase your monster lore skill
+
+add_quest
+{
+ ["global"] = "BOUNTY_QUEST",
+ ["name"] = "Bounty quest",
+ ["desc"] = function()
+ if quest(BOUNTY_QUEST).status == QUEST_STATUS_TAKEN then
+ print_hook("#####yBounty quest!\n")
+ print_hook("You must bring back "..monster_race_desc(bounty_quest_monster, 0).." corpse to the beastmaster.\n")
+ print_hook("\n")
+ end
+ end,
+ ["level"] = -1,
+ ["data"] = {
+ ["bounty_quest_monster"] = 0,
+ },
+ ["hooks"] = {
+ -- Start the game without the quest, need to request it
+ [HOOK_BIRTH_OBJECTS] = function()
+ quest(BOUNTY_QUEST).status = QUEST_STATUS_UNTAKEN
+ end,
+ },
+}
+
+add_building_action
+{
+ -- Index is used in ba_info.txt to set the actions
+ ["index"] = 54,
+ ["action"] = function()
+ if quest(BOUNTY_QUEST).status == QUEST_STATUS_UNTAKEN then
+ quest(BOUNTY_QUEST).status = QUEST_STATUS_TAKEN
+ bounty_quest_monster = get_new_bounty_monster(3 + ((player.lev * 3) / 2))
+
+ msg_print("You must bring me back "..monster_race_desc(bounty_quest_monster, 0).." corpse.")
+ else
+ msg_print("You still must bring me back "..monster_race_desc(bounty_quest_monster, 0).." corpse.")
+ end
+ end
+}
+
+add_building_action
+{
+ -- Index is used in ba_info.txt to set the actions
+ ["index"] = 55,
+ ["action"] = function()
+ if quest(BOUNTY_QUEST).status == QUEST_STATUS_TAKEN then
+ local ret, item
+
+ -- Ask for an item
+ ret, item = get_item("What corpse to return?",
+ "You have no corpse to return.",
+ bor(USE_INVEN),
+ function (obj)
+ if (obj.tval == TV_CORPSE) and (obj.pval2 == bounty_quest_monster) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+
+ -- Ok we got the corpse!
+ if ret == TRUE then
+ -- Take the corpse from the inventory
+ inven_item_increase(item, -1)
+ inven_item_optimize(item)
+
+ msg_print("Ah well done adventurer!")
+ msg_print("As a reward I will teach you a bit of monster lore.")
+
+ if skill(SKILL_LORE).mod == 0 then
+ skill(SKILL_LORE).mod = 900
+ skill(SKILL_LORE).dev = TRUE
+ end
+ skill(SKILL_LORE).value = skill(SKILL_LORE).value + skill(SKILL_LORE).mod
+ if skill(SKILL_PRESERVATION).mod == 0 then
+ skill(SKILL_PRESERVATION).value = 800
+ skill(SKILL_PRESERVATION).mod = 800
+ skill(SKILL_PRESERVATION).dev = TRUE
+ msg_print("I see you don't know the corpse preservation skill, I shall teach you it too.")
+ end
+
+ quest(BOUNTY_QUEST).status = QUEST_STATUS_UNTAKEN
+ bounty_quest_monster = 0
+ end
+ else
+ msg_print("You do not have any bounty quest yet.")
+ end
+ end
+}
diff --git a/lib/scpt/corrupt.lua b/lib/scpt/corrupt.lua
new file mode 100644
index 00000000..6a7ae0e5
--- /dev/null
+++ b/lib/scpt/corrupt.lua
@@ -0,0 +1,440 @@
+-- Definition of the corruptions
+
+-- The Balrog corruptions
+CORRUPT_BALROG_AURA = add_corruption
+{
+ ["color"] = TERM_ORANGE,
+ ["name"] = "Balrog Aura",
+ ["get_text"] = "A corrupted wall of flames surrounds you.",
+ ["lose_text"] = "The wall of corrupted flames abandons you.",
+ ["desc"] =
+ {
+ " Surrounds you with a fiery aura",
+ " But it can burn scrolls when you read them"
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.xtra_f3 = bor(player.xtra_f3, TR3_SH_FIRE)
+ player.xtra_f3 = bor(player.xtra_f3, TR3_LITE1)
+ end,
+ [HOOK_READ] = function(obj)
+ if magik(5) == TRUE then
+ msg_print("Your demon aura burns the scroll before you read it!")
+ return TRUE, TRUE, FALSE
+ else
+ return FALSE
+ end
+ end,
+ },
+}
+
+CORRUPT_BALROG_WINGS = add_corruption
+{
+ ["color"] = TERM_ORANGE,
+ ["name"] = "Balrog Wings",
+ ["get_text"] = "Wings of shadow grow in your back.",
+ ["lose_text"] = "The wings in your back fall apart.",
+ ["desc"] =
+ {
+ " Creates ugly, but working, wings allowing you to fly",
+ " But it reduces charisma by 4 and dexterity by 2"
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.xtra_f4 = bor(player.xtra_f4, TR4_FLY)
+ player.modify_stat(A_CHR, -4)
+ player.modify_stat(A_DEX, -2)
+ end,
+ },
+}
+
+CORRUPT_BALROG_STRENGTH = add_corruption
+{
+ ["color"] = TERM_ORANGE,
+ ["name"] = "Balrog Strength",
+ ["get_text"] = "Your muscles get unnatural strength.",
+ ["lose_text"] = "Your muscles get weaker again.",
+ ["desc"] =
+ {
+ " Provides 3 strength and 1 constitution",
+ " But it reduces charisma by 1 and dexterity by 3"
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.modify_stat(A_STR, 3)
+ player.modify_stat(A_CON, 1)
+ player.modify_stat(A_DEX, -3)
+ player.modify_stat(A_CHR, -1)
+ end,
+ },
+}
+
+CORRUPT_BALROG_FORM = add_corruption
+{
+ ["color"] = TERM_YELLOW,
+ ["name"] = "Balrog Form",
+ ["get_text"] = "You feel the might of a Balrog inside you.",
+ ["lose_text"] = "The presence of the Balrog seems to abandon you.",
+ ["desc"] =
+ {
+ " Allows you to turn into a Balrog at will",
+ " You need Balrog Wings, Balrog Aura and Balrog Strength to activate it"
+ },
+ ["depends"] =
+ {
+ [CORRUPT_BALROG_AURA] = TRUE,
+ [CORRUPT_BALROG_WINGS] = TRUE,
+ [CORRUPT_BALROG_STRENGTH] = TRUE
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.xtra_f2 = bor(player.xtra_f2, TR2_IM_ACID)
+ player.xtra_f2 = bor(player.xtra_f2, TR2_IM_FIRE)
+ player.xtra_f2 = bor(player.xtra_f2, TR2_IM_ELEC)
+ player.xtra_f2 = bor(player.xtra_f2, TR2_RES_DARK)
+ player.xtra_f2 = bor(player.xtra_f2, TR2_RES_CHAOS)
+ end,
+ [HOOK_CALC_POWERS] = function()
+ player.add_power(PWR_BALROG)
+ end,
+ },
+}
+
+
+-- The Demon corruptions
+CORRUPT_DEMON_SPIRIT = add_corruption
+{
+ ["color"] = TERM_RED,
+ ["name"] = "Demon Spirit",
+ ["get_text"] = "Your spirit opens to corrupted thoughts.",
+ ["lose_text"] = "Your spirit closes again to the corrupted thoughts.",
+ ["desc"] =
+ {
+ " Increases your intelligence by 1",
+ " But reduce your charisma by 2",
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.modify_stat(A_INT, 1)
+ player.modify_stat(A_CHR, -2)
+ end,
+ },
+}
+
+CORRUPT_DEMON_HIDE = add_corruption
+{
+ ["color"] = TERM_RED,
+ ["name"] = "Demon Hide",
+ ["get_text"] = "Your skin grows into a thick hide.",
+ ["lose_text"] = "Your skin returns to a natural state.",
+ ["desc"] =
+ {
+ " Increases your armour class by your level",
+ " Provides immunity to fire at level 40",
+ " But reduces speed by your level / 7",
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.to_a = player.to_a + player.lev
+ player.dis_to_a = player.dis_to_a + player.lev
+ player.pspeed = player.pspeed - (player.lev / 7)
+ if player.lev >= 40 then player.xtra_f2 = bor(player.xtra_f2, TR2_IM_FIRE) end
+ end,
+ },
+}
+
+CORRUPT_DEMON_BREATH = add_corruption
+{
+ ["color"] = TERM_RED,
+ ["name"] = "Demon Breath",
+ ["get_text"] = "Your breath becomes mephitic.",
+ ["lose_text"] = "Your breath is once again normal.",
+ ["desc"] =
+ {
+ " Provides fire breath",
+ " But gives a small chance to spoil potions when you quaff them",
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_POWERS] = function()
+ player.add_power(PWR_BR_FIRE)
+ end,
+ [HOOK_QUAFF] = function(obj)
+ if magik(9) == TRUE then
+ msg_print("Your demon breath spoils the potion!")
+ return TRUE, FALSE
+ else
+ return FALSE
+ end
+ end,
+ },
+}
+
+CORRUPT_DEMON_REALM = add_corruption
+{
+ ["color"] = TERM_L_RED,
+ ["name"] = "Demon Realm",
+ ["get_text"] = "You feel more attuned to the demon realm.",
+ ["lose_text"] = "You lose your attunement to the demon realm.",
+ ["desc"] =
+ {
+ " Provides access to the demon school skill and the use of demonic equipment",
+ " You need Demon Spirit, Demon Hide and Demon Breath to activate it"
+ },
+ ["depends"] =
+ {
+ [CORRUPT_DEMON_SPIRIT] = TRUE,
+ [CORRUPT_DEMON_HIDE] = TRUE,
+ [CORRUPT_DEMON_BREATH] = TRUE
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ -- 1500 may seem a lot, but people are rather unlikely to get the corruption very soon
+ -- due to the dependencies
+ if s_info[SKILL_DAEMON + 1].mod == 0 then s_info[SKILL_DAEMON + 1].mod = 1500 end
+ s_info[SKILL_DAEMON + 1].hidden = FALSE;
+ end,
+ },
+}
+
+
+-- Teleportation corruptions
+
+-- Random teleportation will ask for confirmation 70% of the time
+-- But 30% of the time it will teleport, without asking
+CORRUPT_RANDOM_TELEPORT = add_corruption
+{
+ ["color"] = TERM_GREEN,
+ ["name"] = "Random teleportation",
+ ["get_text"] = "Space seems to fizzle around you.",
+ ["lose_text"] = "Space solidify again around you.",
+ ["desc"] =
+ {
+ " Randomly teleports you around",
+ },
+ -- No oppose field, it will be automatically set when we declare the anti-telep corruption to oppose us
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.xtra_f3 = bor(player.xtra_f3, TR3_TELEPORT)
+ end,
+ [HOOK_PROCESS_WORLD] = function()
+ if rand_int(300) == 1 then
+ if magik(70) == TRUE then
+ if get_check("Teleport?") == TRUE then
+ teleport_player(50)
+ end
+ else
+ disturb(0, 0)
+ msg_print("Your corruption takes over you, you teleport!")
+ teleport_player(50)
+ end
+ end
+ end,
+ },
+}
+
+-- Anti-teleportation corruption, can be stopped with this power
+CORRUPT_ANTI_TELEPORT = add_corruption
+{
+ ["color"] = TERM_GREEN,
+ ["name"] = "Anti-teleportation",
+ ["get_text"] = "Space continuum freezes around you.",
+ ["lose_text"] = "Space continuum can once more be altered around you.",
+ ["desc"] =
+ {
+ " Prevents all teleportations, be it of you or monsters",
+ },
+ ["oppose"] =
+ {
+ [CORRUPT_RANDOM_TELEPORT] = TRUE
+ },
+ ["hooks"] =
+ {
+ [HOOK_BIRTH_OBJECTS] = function()
+ player.corrupt_anti_teleport_stopped = FALSE
+ end,
+ [HOOK_CALC_POWERS] = function()
+ player.add_power(POWER_COR_SPACE_TIME)
+ end,
+ [HOOK_CALC_BONUS] = function()
+ if player.corrupt_anti_teleport_stopped == FALSE then
+ player.resist_continuum = TRUE
+ end
+ end,
+ [HOOK_PROCESS_WORLD] = function()
+ if player.corrupt_anti_teleport_stopped == TRUE then
+ local amt = player.msp + player.csp
+ amt = amt / 100
+ if (amt < 1) then amt = 1 end
+ increase_mana(-amt)
+ if player.csp == 0 then
+ player.corrupt_anti_teleport_stopped = FALSE
+ msg_print("You stop controlling your corruption.")
+ player.update = bor(player.update, PU_BONUS)
+ end
+ end
+ end,
+ },
+}
+
+
+-- Troll blood
+CORRUPT_TROLL_BLOOD = add_corruption
+{
+ ["color"] = TERM_GREEN,
+ ["name"] = "Troll Blood",
+ ["get_text"] = "Your blood thickens, you sense corruption in it.",
+ ["lose_text"] = "Your blood returns to a normal state.",
+ ["desc"] =
+ {
+ " Troll blood flows in your veins, granting increased regeneration",
+ " It also enables you to feel the presence of other troll beings",
+ " But it will make your presence more noticeable and aggravating",
+ },
+ ["can_gain"] = function()
+ -- Ok trolls should not get this one. never.
+ if get_race_name() == "Troll" then
+ return nil
+ else
+ return not nil
+ end
+ end,
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ player.xtra_f3 = bor(player.xtra_f3, TR3_REGEN, TR3_AGGRAVATE)
+ player.xtra_esp = bor(player.xtra_esp, ESP_TROLL)
+ end,
+ },
+}
+
+-- The vampire corruption set
+CORRUPT_VAMPIRE_TEETH = add_corruption
+{
+ ["group"] = "Vampire",
+ ["removable"] = FALSE,
+ ["color"] = TERM_L_DARK,
+ ["name"] = "Vampiric Teeth",
+ ["get_text"] = "You grow vampiric teeth!",
+ ["lose_text"] = "BUG! this should not happen",
+ ["desc"] =
+ {
+ " Your teeth allow you to drain blood to feed yourself",
+ " However your stomach now only accepts blood.",
+ },
+ ["allow"] = function()
+ if test_race_flags(1, PR1_NO_SUBRACE_CHANGE) == FALSE then return not nil else return nil end
+ end,
+ ["gain"] = function()
+ switch_subrace(SUBRACE_SAVE, TRUE);
+
+ subrace_add_power(subrace(SUBRACE_SAVE), PWR_VAMPIRISM)
+ subrace(SUBRACE_SAVE).flags1 = bor(subrace(SUBRACE_SAVE).flags1, PR1_VAMPIRE, PR1_UNDEAD, PR1_NO_SUBRACE_CHANGE)
+ end,
+ ["hooks"] =
+ {
+ },
+}
+CORRUPT_VAMPIRE_STRENGTH = add_corruption
+{
+ ["group"] = "Vampire",
+ ["removable"] = FALSE,
+ ["color"] = TERM_L_DARK,
+ ["name"] = "Vampiric Strength",
+ ["get_text"] = "Your body seems more dead than alive.",
+ ["lose_text"] = "BUG! this should not happen",
+ ["desc"] =
+ {
+ " Your body seems somewhat dead",
+ " In this near undead state it has improved strength, constitution and intelligence",
+ " But reduced dexterity, wisdom and charisma.",
+ },
+ ["depends"] =
+ {
+ [CORRUPT_VAMPIRE_TEETH] = TRUE,
+ },
+ ["gain"] = function()
+ -- Apply the bonuses/penalities
+ subrace(SUBRACE_SAVE).r_mhp = subrace(SUBRACE_SAVE).r_mhp + 1
+ subrace(SUBRACE_SAVE).r_exp = subrace(SUBRACE_SAVE).r_exp + 100
+
+ subrace(SUBRACE_SAVE).r_adj[A_STR + 1] = subrace(SUBRACE_SAVE).r_adj[A_STR + 1] + 3
+ subrace(SUBRACE_SAVE).r_adj[A_INT + 1] = subrace(SUBRACE_SAVE).r_adj[A_INT + 1] + 2
+ subrace(SUBRACE_SAVE).r_adj[A_WIS + 1] = subrace(SUBRACE_SAVE).r_adj[A_WIS + 1] - 3
+ subrace(SUBRACE_SAVE).r_adj[A_DEX + 1] = subrace(SUBRACE_SAVE).r_adj[A_DEX + 1] - 2
+ subrace(SUBRACE_SAVE).r_adj[A_CON + 1] = subrace(SUBRACE_SAVE).r_adj[A_CON + 1] + 1
+ subrace(SUBRACE_SAVE).r_adj[A_CHR + 1] = subrace(SUBRACE_SAVE).r_adj[A_CHR + 1] - 4
+
+ -- be reborn!
+ do_rebirth()
+ cmsg_print(TERM_L_DARK, "You feel death slipping inside.")
+ end,
+ ["hooks"] =
+ {
+ },
+}
+CORRUPT_VAMPIRE_VAMPIRE = add_corruption
+{
+ ["group"] = "Vampire",
+ ["removable"] = FALSE,
+ ["color"] = TERM_L_DARK,
+ ["name"] = "Vampire",
+ ["get_text"] = "You die to be reborn in a Vampire form.",
+ ["lose_text"] = "BUG! this should not happen",
+ ["desc"] =
+ {
+ " You are a Vampire. As such you resist cold, poison, darkness and nether.",
+ " Your life is sustained, but you cannot stand the light of the sun."
+ },
+ ["depends"] =
+ {
+ [CORRUPT_VAMPIRE_STRENGTH] = TRUE,
+ },
+ ["gain"] = function()
+ -- Be a Vampire and be proud of it
+ local title = get_subrace_title(SUBRACE_SAVE)
+ if title == " " or title == "Vampire" then
+ title = "Vampire"
+ subrace(SUBRACE_SAVE).place = FALSE
+ else
+ title = "Vampire "..title
+ end
+ set_subrace_title(SUBRACE_SAVE, title)
+
+ -- Bonus/and .. not bonus :)
+ subrace(SUBRACE_SAVE).flags1 = bor(subrace(SUBRACE_SAVE).flags1, PR1_HURT_LITE)
+ subrace(SUBRACE_SAVE).oflags2[2] = bor(subrace(SUBRACE_SAVE).oflags2[2], TR2_RES_POIS, TR2_RES_NETHER, TR2_RES_COLD, TR2_RES_DARK, TR2_HOLD_LIFE)
+ subrace(SUBRACE_SAVE).oflags3[2] = bor(subrace(SUBRACE_SAVE).oflags3[2], TR3_LITE1)
+ end,
+ ["hooks"] =
+ {
+ },
+}
+
+
+--[[
+CORRUPT_ = add_corruption
+{
+ ["color"] = TERM_GREEN,
+ ["name"] = "",
+ ["get_text"] = "",
+ ["lose_text"] = "",
+ ["desc"] =
+ {
+ " ",
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ end,
+ },
+}
+]]
diff --git a/lib/scpt/drunk.lua b/lib/scpt/drunk.lua
new file mode 100644
index 00000000..7d90af8d
--- /dev/null
+++ b/lib/scpt/drunk.lua
@@ -0,0 +1,21 @@
+-- silly function that allows a drunk to take a bottle of wine/ale from the player
+
+function drunk_takes_wine(m_idx, item)
+
+ m_ptr = monster(m_idx)
+ o_ptr = get_object(item)
+
+ if (m_ptr.r_idx == test_monster_name("Singing, happy drunk"))
+ and (o_ptr.tval == TV_FOOD) and ((o_ptr.sval == 38) or (o_ptr.sval == 39)) then
+
+ cmsg_print(TERM_YELLOW, "'Hic!'")
+
+ inven_item_increase(item, -1)
+ inven_item_optimize(item)
+ return TRUE
+ else
+ return FALSE
+ end
+end
+
+add_hook_script(HOOK_GIVE, "drunk_takes_wine", "drunk_takes_wine")
diff --git a/lib/scpt/fireprof.lua b/lib/scpt/fireprof.lua
new file mode 100644
index 00000000..79a0747b
--- /dev/null
+++ b/lib/scpt/fireprof.lua
@@ -0,0 +1,480 @@
+-- The Old Mages/Fireproofing quest: Bring back an essence from a fiery cave and get some books/scrolls/staves fireproofed in return
+
+fireproof_quest = {}
+
+-- The map definition itself
+fireproof_quest.MAP =
+[[#!map
+# Created by fearoffours (fearoffours@moppy.co.uk)
+# Made for ToME 2.1.x on 03/09/02
+
+# Permanent wall
+F:X:63:3
+
+# Floor with dirt
+F:.:88:3
+
+# shallow lava
+F:f:86:3
+
+# Deep lava
+F:F:85:3
+
+### Random Monsters and/or Items
+# Random object (upto 3 levels ood)
+F:!:88:5:0:*21
+
+# red mold
+F:m:88:5:324
+
+# Chimaera
+F:H:88:5:341
+
+# Red dragon bat
+F:b:88:5:377
+
+# Hellhound and
+# Random object (upto 7 levels ood) on normal floor
+F:C:88:5:613:*25
+
+# Quest exit
+F:<:6:3
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X......m.................H.........b...m......X
+D:X.............b...............................X
+D:X.......................m.....m.......H...b.C.X
+D:X...............m.!........b.............FFFf.X
+D:X.........m!............H....!........fffFFFffX
+D:X..................................fffFFFFFFfFX
+D:XFFf..............................fFFFFff..fffX
+D:XFFFff........FFFFFF...........fffFFFfff......X
+D:XfFFFFfff....FFFFFFFf.......fffFFFFFf.........X
+D:X.fFFFFFFff.FFFFFFFFFfF..fffFFFFFFff..........X
+D:X..fFFFFFFFffFFFfffFFFfffFFFFFFFFf............X
+D:X...fFFFFFFFFFFff.ffFFFFFFFFFFFff.............X
+D:X....fffFFFFFFff...ffFFFFFFFFFf...............X
+D:X.......ffFFFf.......ffffFFfff................X
+D:X.........fff.................................X
+D:X.............................................X
+D:X.............................................X
+D:X.............................................X
+D:X..................................<..........X
+D:X.............................................X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:22:26
+]]
+
+
+-- change this constant (and the FOO_POINTS ones) to adjust the no of items fire-proofed as a reward
+fireproof_quest.TOTAL_ITEM_POINTS = 12
+
+-- These constants are how many 'points' each type of item will take up. So currently, you can fireproof 3 books, 4 staves or 12 scrolls.
+fireproof_quest.BOOK_POINTS = 4
+fireproof_quest.STAFF_POINTS = 3
+fireproof_quest.SCROLL_POINTS = 1
+
+add_quest
+{
+ ["global"] = "FIREPROOF_QUEST",
+ ["name"] = "Old Mages quest",
+ ["desc"] = function()
+ local num_books, num_staff, num_scroll
+
+ num_books = fireproof_quest.item_points_remaining / fireproof_quest.BOOK_POINTS
+ num_staff = fireproof_quest.item_points_remaining / fireproof_quest.STAFF_POINTS
+ num_scroll = fireproof_quest.item_points_remaining / fireproof_quest.SCROLL_POINTS
+
+ -- Quest taken
+ if (quest(FIREPROOF_QUEST).status == QUEST_STATUS_TAKEN) then
+ print_hook("#####yAn Old Mages Quest!\n")
+ print_hook("Retrieve the strange essence for the old mage in Lothlorien.\n")
+ print_hook("\n")
+ -- essence retrieved, not taken to mage
+ elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then
+ print_hook("#####yAn Old Mages Quest!\n")
+ print_hook("You have retrieved the essence for the old mage in Lothlorien. Perhaps you \n")
+ print_hook("should see about a reward.\n")
+ print_hook("\n")
+ -- essence returned, not all books fireproofed
+ elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_FINISHED) and (fireproof_quest.item_points_remaining > 0) then
+ print_hook("#####yAn Old Mages Quest!\n")
+ print_hook("You have retrieved the essence for the old mage in Lothlorien. He will still \n")
+ print_hook("fireproof "..num_books.." book(s) or "..num_staff.." staff/staves or "..num_scroll.." scroll(s) for you.\n")
+ print_hook("\n")
+ end
+ end,
+ ["level"] = 20,
+ ["data"] = {
+ -- store some variables
+ ["fireproof_quest.item_points_remaining"] = fireproof_quest.TOTAL_ITEM_POINTS,
+ ["fireproof_quest.essence"] = 0,
+ },
+ ["hooks"] = {
+ -- Start the game without the quest, need to request it
+ [HOOK_BIRTH_OBJECTS] = function()
+ quest(FIREPROOF_QUEST).status = QUEST_STATUS_UNTAKEN
+
+ -- reset some variables on birth
+ fireproof_quest.item_points_remaining = fireproof_quest.TOTAL_ITEM_POINTS
+ fireproof_quest.essence = 0
+ end,
+ [HOOK_GEN_QUEST] = function()
+ local essence, y, x, traps, tries, trap_y, trap_x, grid
+
+ -- Only if player doing this quest
+ if (player.inside_quest ~= FIREPROOF_QUEST) then
+ return FALSE
+ else
+ -- load the map
+ load_map(fireproof_quest.MAP, 2, 2)
+
+ -- no teleport
+ level_flags2 = DF2_NO_TELEPORT
+
+ -- determine type of essence
+ fireproof_quest.essence = randint(18)
+
+ -- create essence
+ essence = create_object(TV_BATERIE, fireproof_quest.essence)
+
+ -- mark essence
+ essence.pval2 = fireproof_quest.essence
+ essence.note = quark_add("quest")
+
+ -- roll for co-ordinates in top half of map
+ y = randint(3) + 2
+ x = randint(45) + 2
+
+ -- drop it
+ drop_near(essence, -1, y, x)
+
+ -- how many traps to generate
+ traps = rand_range(10, 30)
+
+ -- generate the traps
+ while (traps > 0) do
+
+ -- initialise tries variable
+ tries = 0
+
+ -- make sure it's a safe place
+ while (tries == 0) do
+
+ -- get grid coordinates
+ trap_y = randint(19) + 2
+ trap_x = randint(45) + 2
+ grid = cave(trap_y, trap_x)
+
+ -- are the coordinates on a stair, or a wall?
+ if (cave_is(grid, FF1_PERMANENT) ~= 0) or (cave_is(grid, FF1_FLOOR) == 0) then
+
+ -- try again
+ tries = 0
+ else
+ -- not a stair, then stop this 'while'
+ tries = 1
+ end
+ end
+
+ -- randomise level of trap
+ trap_level = rand_range(20, 40)
+
+ -- put the trap there
+ place_trap(trap_y, trap_x, trap_level)
+
+ -- that's one less trap to place
+ traps = traps - 1
+ end
+ return TRUE
+ end
+ end,
+ [HOOK_STAIR] = function()
+ local ret
+
+ -- only ask this if player about to go up stairs of quest and hasn;t retrieved essence
+ if (player.inside_quest ~= FIREPROOF_QUEST) or
+ (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then
+ return FALSE
+ else
+ if cave(player.py, player.px).feat ~= FEAT_LESS then return end
+
+ -- flush all pending input
+ flush()
+
+ -- confirm
+ ret = get_check("Really abandon the quest?")
+
+ -- if yes, then
+ if (ret == TRUE) then
+
+ -- fail the quest
+ quest(FIREPROOF_QUEST).status = QUEST_STATUS_FAILED
+ return FALSE
+ else
+ -- if no, they stay in the quest
+ return TRUE
+ end
+ end
+ end,
+ [HOOK_GET] = function(o_ptr)
+
+ -- if they're in the quest and haven't picked up the essence already, continue
+ if (player.inside_quest ~= FIREPROOF_QUEST) or
+ (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then
+ return FALSE
+ else
+
+ -- check that it's the real essence and not another one generated via the random object placing in fireproof.map
+ if (o_ptr.pval2 == fireproof_quest.essence) then
+
+ -- ok mark the quest 'completed'
+ quest(FIREPROOF_QUEST).status = QUEST_STATUS_COMPLETED
+ msg_print(TERM_YELLOW, "Fine! Looks like you've found it.")
+ end
+ end
+ end,
+
+ },
+}
+
+-- add the bit that determines what happens when the request 'q'uest bit is done in the wizard spire
+add_building_action
+{
+ -- Index is used in ba_info.txt to set the actions
+ ["index"] = 56,
+ ["action"] = function()
+
+ local num_books, num_staff, num_scroll
+
+ num_books = fireproof_quest.item_points_remaining / fireproof_quest.BOOK_POINTS
+ num_staff = fireproof_quest.item_points_remaining / fireproof_quest.STAFF_POINTS
+ num_scroll = fireproof_quest.item_points_remaining / fireproof_quest.SCROLL_POINTS
+
+ -- the quest hasn;t been requested already, right?
+ if quest(FIREPROOF_QUEST).status == QUEST_STATUS_UNTAKEN then
+
+ -- quest has been taken now
+ quest(FIREPROOF_QUEST).status = QUEST_STATUS_TAKEN
+ fireproof_quest.item_points_remaining = fireproof_quest.TOTAL_ITEM_POINTS
+
+ -- issue instructions
+ msg_print("I need a very special essence for a spell I am working on. I am too old to ")
+ msg_print("fetch it myself. Please bring it back to me. You can find it north of here.")
+ msg_print("Be careful with it, it's fragile and might be destroyed easily.")
+
+ return TRUE, FALSE, TRUE
+ -- if quest completed (essence was retrieved)
+ elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then
+
+ -- ask for essence
+ ret, item = get_item("Which essence?",
+ "You have no essences to return",
+ bor(USE_INVEN),
+ function (obj)
+
+ -- check it's the 'marked' essence
+ if (obj.tval == TV_BATERIE) and (obj.sval == fireproof_quest.essence) and (obj.pval2 == fireproof_quest.essence) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+
+ -- didn't get the essence?
+ if (ret == FALSE) then
+ return TRUE
+
+ -- got the essence!
+ else
+
+ -- take essence
+ inven_item_increase(item, -1)
+ inven_item_optimize(item)
+ msg_print("Great! Let me fireproof some of your items in thanks. I can do "..num_books.." books, ")
+ msg_print(num_staff.." staves, or "..num_scroll.." scrolls.")
+
+ -- how many items to proof?
+ local items = fireproof_quest.item_points_remaining
+
+ -- repeat till up to 3 (value defined as TOTAL_ITEM_POINTS constant) books fireproofed
+ while items > 0 do
+ ret = fireproof()
+
+ -- don't loop the fireproof if there's nothing to fireproof
+ if ret == FALSE then
+ break
+ end
+
+ -- subtract item points
+ items = fireproof_quest.item_points_remaining
+ end
+
+ -- have they all been done?
+ if (fireproof_quest.item_points_remaining == 0) then
+ -- mark quest to make sure no more quests are given
+ quest(FIREPROOF_QUEST).status = QUEST_STATUS_REWARDED
+ else
+ -- mark in preparation of anymore books to fireproof
+ quest(FIREPROOF_QUEST).status = QUEST_STATUS_FINISHED
+ end
+
+
+ end
+
+ -- if the player asks for a quest when they already have it, but haven't failed it, give them some extra instructions
+ elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_TAKEN) then
+ msg_print("The essence is in a cave just behind the shop.")
+
+ -- ok not all books have been fireproofed... lets do the rest
+ elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_FINISHED) then
+
+ -- how many books still to proof?
+ local items = fireproof_quest.item_points_remaining
+
+ -- repeat as necessary
+ while items > 0 do
+ ret = fireproof()
+
+ -- don't loop the fireproof if there's nothing to fireproof
+ if ret == FALSE then
+ break
+ else
+ -- have they all been done?
+ if (fireproof_quest.item_points_remaining == 0) then quest(FIREPROOF_QUEST).status = QUEST_STATUS_REWARDED end
+ end
+
+ -- subtract item points
+ items = fireproof_quest.item_points_remaining
+ end
+
+ -- quest failed or completed, then give no more quests
+ elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_FAILED) or (quest(FIREPROOF_QUEST).status == QUEST_STATUS_REWARDED) then
+ msg_print("I have no more quests for you")
+ end
+ return TRUE
+ end,
+}
+
+-- the routine that checks for a book and actually fireproofs it
+function fireproof()
+
+ local ret, item, obj2, stack, obj3, carry_it
+
+ ret, item = get_item("Which item shall I fireproof?",
+ "You have no more items I can fireproof, come back when you have some.",
+ bor(USE_INVEN),
+ function (obj)
+
+ -- get some flags
+ local f1, f2, f3, f4, f5, esp = object_flags(obj)
+
+ -- is it a book/staff/scroll, is it already fireproof?
+ if ((obj.tval == TV_BOOK) or (obj.tval == TV_SCROLL) or (obj.tval == TV_STAFF)) and (band(f3, TR3_IGNORE_FIRE) == 0) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+
+ -- get the object type from the number
+ obj2 = get_object(item)
+
+ -- check we have enough points (if we 'got' an item)
+ if (ret == TRUE) then
+ ret2, stack = enough_points(obj2)
+ end
+
+ -- did either routine fail?
+ if (ret == FALSE) or (ret2 == FALSE) then
+ return FALSE
+ else
+
+ -- are we part of the items from a stack?
+ if (obj2.number ~= stack) then
+
+ -- make a new object to handle
+ object_copy(obj_forge, obj2)
+
+ -- give it the right number of items
+ obj_forge.number = stack
+
+ -- adjust for number of items in pack not to be fireproofed
+ obj2.number = obj2.number - stack
+ obj3 = obj_forge
+
+ -- we'll need to add this to the inventory after fireproofing
+ carry_it = TRUE
+ else
+
+ -- use the whole stack
+ obj3 = obj2
+
+ -- we'll be dealing this while it's still in the inventory
+ carry_it = FALSE
+ end
+
+ -- make it fireproof
+ obj3.name2 = 149
+
+ -- apply it, making sure the pvals don't change with apply_magic (it would change the type of book!)
+ local oldpval = obj3.pval
+ local oldpval2 = obj3.pval2
+ local oldpval3 = obj3.pval3
+ apply_magic(obj3, -1, FALSE, FALSE, FALSE)
+ obj3.pval = oldpval
+ obj3.pval2 = oldpval2
+ obj3.pval3 = oldpval3
+
+ -- put it in the inventory if it's only part of a stack
+ if (carry_it == TRUE) then
+ inven_carry(obj3, TRUE)
+ end
+
+ -- id and notice it
+ set_known(obj3)
+ set_aware(obj3)
+
+ return TRUE
+ end
+end
+
+-- This function makes sure the player has enough 'points' left to fireproof stuff.
+function enough_points(obj)
+ local item_value, stack
+
+ -- are the items in a stack?
+ if (obj.number > 1) then
+
+ -- how many to fireproof?
+ stack = get_quantity("How many would you like fireproofed?", obj.number)
+ else
+ stack = 1
+ end
+
+ -- check for item type and multiply number in the stack by the amount of points per item of that type
+ if (obj.tval == TV_BOOK) then
+ item_value = fireproof_quest.BOOK_POINTS * stack
+ elseif (obj.tval == TV_STAFF) then
+ item_value = fireproof_quest.STAFF_POINTS * stack
+ elseif (obj.tval == TV_SCROLL) then
+ item_value = fireproof_quest.SCROLL_POINTS * stack
+ end
+
+ -- do we have enough points?
+ if (item_value > fireproof_quest.item_points_remaining) then
+ msg_print("I do not have enough fireproofing material for that.")
+ return FALSE
+ else
+ -- if so then subtract those points before we do the fireproofing
+ fireproof_quest.item_points_remaining = fireproof_quest.item_points_remaining - item_value
+ end
+
+ -- Used all the points? the quest is completely rewarded.
+ if fireproof_quest.item_points_remaining == 0 then quest(FIREPROOF_QUEST).status = QUEST_STATUS_REWARDED end
+
+ return TRUE, stack
+end
+
diff --git a/lib/scpt/god.lua b/lib/scpt/god.lua
new file mode 100644
index 00000000..3f32888b
--- /dev/null
+++ b/lib/scpt/god.lua
@@ -0,0 +1,640 @@
+-- The god quest: find randomly placed relic in a randomly placed dungeon!
+
+-- set some global variables (stored in the save file via the ["data"] key)
+god_quest = {}
+
+-- increase this number to make god quests more common, to a max value of 100
+god_quest.CHANCE_OF_GOD_QUEST = 21
+
+-- increase this number to make more quests
+god_quest.MAX_NUM_GOD_QUESTS = 5
+
+-- d_idx of the god_quest (Lost Temple) dungeon
+god_quest.DUNGEON_GOD = 30
+
+add_quest
+{
+ ["global"] = "GOD_QUEST",
+ ["name"] = "God quest",
+ ["desc"] = function()
+
+ if quest(GOD_QUEST).status == QUEST_STATUS_TAKEN then
+
+ -- get the direction that the dungeon lies from lothlorien/angband
+ local home, home_axis, home_distance, home2, home2_axis, home2_distance = get_god_quest_axes()
+
+ print_hook("#####yGod quest "..god_quest.quests_given.."!\n")
+ print_hook("Thou art to find the lost temple of thy God and\n");
+ print_hook("to retrieve the lost part of the relic for thy God! \n")
+ if home_axis ~= "close" then
+ print_hook("The temple lies "..home_distance.." to the "..home_axis.." of "..home..", \n")
+ else
+ print_hook("The temple lies very close to "..home..", \n")
+ end
+ if home2_axis ~= "close" then
+ print_hook( "and "..home2_distance.." to the "..home2_axis.." of "..home2..".\n")
+ else
+ print_hook("and very close to "..home2..".\n")
+ end
+ print_hook("\n")
+ end
+ end,
+ ["level"] = -1,
+ ["data"] = {
+ ["god_quest.relic_num"] = 1,
+ ["god_quest.quests_given"] = 0,
+ ["god_quest.relics_found"] = 0,
+ ["god_quest.dun_mindepth"] = 1,
+ ["god_quest.dun_maxdepth"] = 4,
+ ["god_quest.dun_minplev"] = 0,
+ ["god_quest.relic_gen_tries"] = 0,
+ ["god_quest.relic_generated"] = FALSE,
+ ["god_quest.dung_x"] = 1,
+ ["god_quest.dung_y"] = 1,
+ ["god_quest.player_x"] = 0,
+ ["god_quest.player_y"] = 0,
+ },
+ ["hooks"] = {
+ -- Start the game without the quest, given it by chance
+ [HOOK_BIRTH_OBJECTS] = function()
+ quest(GOD_QUEST).status = QUEST_STATUS_UNTAKEN
+
+ -- initialise save-file stored variables when new character is created
+ god_quest.relic_num = 1
+ god_quest.quests_given = 0
+ god_quest.relics_found = 0
+ god_quest.dun_mindepth = 1
+ god_quest.dun_maxdepth = 4
+ god_quest.dun_minplev = 0
+ god_quest.relic_gen_tries = 0
+ god_quest.relic_generated = FALSE
+ end,
+ [HOOK_PLAYER_LEVEL] = function(gained)
+ local home_axis, home
+
+ if gained > 0 then
+ -- roll for chance of quest
+ local give_god_quest = magik(god_quest.CHANCE_OF_GOD_QUEST)
+
+ -- check player is worshipping a god, not already on a god quest.
+ if (player.astral ~= FALSE) or (player.pgod <= 0)
+ or (quest(GOD_QUEST).status == QUEST_STATUS_TAKEN) or (quest(GOD_QUEST).status == QUEST_STATUS_FAILED)
+ or (god_quest.quests_given >= god_quest.MAX_NUM_GOD_QUESTS) or (give_god_quest == FALSE)
+ or ((current_dungeon_idx == god_quest.DUNGEON_GOD) and (dun_level > 0)) or (player.lev <= god_quest.dun_minplev) then
+ -- Don't let a player get quests with trickery
+ if player.lev > god_quest.dun_minplev then
+ god_quest.dun_minplev = player.lev
+ end
+ return
+ else
+ -- each god has different characteristics, so the quests are differnet depending on your god
+ if player.pgod == GOD_ERU then
+ god_quest.relic_num = 7
+ elseif player.pgod == GOD_MANWE then
+ god_quest.relic_num = 8
+ elseif player.pgod == GOD_TULKAS then
+ god_quest.relic_num = 9
+ elseif player.pgod == GOD_MELKOR then
+ god_quest.relic_num = 10
+ elseif player.pgod == GOD_YAVANNA then
+ god_quest.relic_num =11
+ end
+
+ -- This var will need resetting
+ god_quest.relic_generated = FALSE
+ quest(GOD_QUEST).status = QUEST_STATUS_TAKEN
+ god_quest.quests_given = god_quest.quests_given + 1
+
+ -- actually place the dungeon in a random place
+ place_rand_dung()
+
+ -- store the variables of the coords where the player was given the quest
+ god_quest.player_y, god_quest.player_x = player.get_wild_coord()
+
+ -- establish direction of player and 'home' from dungeon
+ local home, home_axis, home_distance, home2, home2_axis, home2_distance = get_god_quest_axes()
+
+ -- God issues instructions
+ cmsg_print(TERM_L_BLUE, "The voice of "..deity(player.pgod).name.." booms in your head:")
+
+ cmsg_print(TERM_YELLOW, "'I have a task for thee.")
+ cmsg_print(TERM_YELLOW, "Centuries ago an ancient relic of mine was broken apart.")
+ cmsg_print(TERM_YELLOW, "The pieces of it have been lost in fallen temples.")
+ cmsg_print(TERM_YELLOW, "Thou art to find my lost temple and retrieve a piece of the relic.")
+ cmsg_print(TERM_YELLOW, "When thy task is done, thou art to lift it in the air and call upon my name.")
+ cmsg_print(TERM_YELLOW, "I shall then come to reclaim what is mine!")
+ if home_axis ~= "close" then
+ cmsg_print(TERM_YELLOW, "The temple lies "..home_distance.." to the "..home_axis.." of "..home..", ")
+ else
+ cmsg_print(TERM_YELLOW, "The temple lies very close to "..home..",")
+ end
+
+ if home2_axis ~= "close" then
+ cmsg_print(TERM_YELLOW, "and "..home2_distance.." to the "..home2_axis.." of "..home2..", I can feel it.'")
+ else
+ cmsg_print(TERM_YELLOW, "and very close to "..home2..", I can feel it.'")
+ end
+
+ -- Prepare depth of dungeon. If this was generated in set_god_dungeon_attributes(),
+ -- then we'd have trouble if someone levelled up in the dungeon!
+ god_quest.dun_mindepth = player.lev*2/3
+ god_quest.dun_maxdepth = god_quest.dun_mindepth + 4
+ end
+ end
+ end,
+ [HOOK_LEVEL_END_GEN] = function()
+ local chance
+
+ -- Check for dungeon
+ if (current_dungeon_idx ~= god_quest.DUNGEON_GOD) or (quest(GOD_QUEST).status == QUEST_STATUS_UNTAKEN) then
+ return
+ -- if the relic has been created at this point, then it was created on the *PREVIOUS* call of HOOK_LEVEL_END_GEN, and
+ -- therefore the player has caused another level generation in the temple and hence failed the quest.
+ elseif (god_quest.relic_generated == TRUE) and quest(GOD_QUEST).status ~= QUEST_STATUS_FAILED then
+
+ -- fail the quest, don't give another one, don't give this message again
+ quest(GOD_QUEST).status = QUEST_STATUS_FAILED
+ -- God issues instructions
+ cmsg_print(TERM_L_BLUE, "The voice of "..deity(player.pgod).name.." booms in your head:")
+
+ cmsg_print(TERM_YELLOW, "'Thou art a fool!")
+ cmsg_print(TERM_YELLOW, "I told thee to look carefully for the relic. It appears thou hast missed the")
+ cmsg_print(TERM_YELLOW, "opportunity to claim it in my name, as I sense that those monsters who ")
+ cmsg_print(TERM_YELLOW, "have overrun my temple have destroyed it themselves.")
+ cmsg_print(TERM_YELLOW, "I shall not ask thee to do such a thing again, as thou hast failed me in this")
+ cmsg_print(TERM_YELLOW, "simple task!'")
+ else
+ -- Force relic generation on 5th attempt if others have been unsuccessful.
+ if (god_quest.relic_gen_tries == 4) and (god_quest.relic_generated == FALSE) then
+ generate_relic()
+ else
+ -- 1/5 chance of generation
+ chance = randint(5)
+ if (chance == 5) then
+ generate_relic()
+ else
+ god_quest.relic_gen_tries = god_quest.relic_gen_tries + 1
+ end
+ end
+ end
+ end,
+ [HOOK_ENTER_DUNGEON] = function(d_idx)
+ -- call the function to set the dungeon variables (dependant on pgod) the first time we enter the dungeon
+ if d_idx ~= god_quest.DUNGEON_GOD then
+ return
+ else
+ set_god_dungeon_attributes()
+ end
+ end,
+ [HOOK_GEN_LEVEL_BEGIN] = function()
+ -- call the function to set the dungeon variables (dependant on pgod) when we WoR back into the dungeon
+ if current_dungeon_idx ~= god_quest.DUNGEON_GOD then
+ return
+ else
+ set_god_dungeon_attributes()
+ end
+ end,
+ [HOOK_STAIR] = function()
+ -- call the function to set the dungeon variables (dependant on pgod) every time we go down a level
+ if current_dungeon_idx ~= god_quest.DUNGEON_GOD then
+ return
+ else
+ set_god_dungeon_attributes()
+ end
+ end,
+ [HOOK_GET] = function(o_ptr, item)
+ -- Is it the relic, and check to make sure the relic hasn't already been identified
+ if (quest(GOD_QUEST).status == QUEST_STATUS_TAKEN) and (o_ptr.tval == TV_JUNK) and (o_ptr.sval == god_quest.relic_num)
+ and (o_ptr.pval ~= TRUE) and (god_quest.relics_found < god_quest.quests_given) then
+
+ -- more God talky-talky
+ cmsg_print(TERM_L_BLUE, deity(player.pgod).name.." speaks to you:")
+
+ -- Is it the last piece of the relic?
+ if (god_quest.quests_given == god_quest.MAX_NUM_GOD_QUESTS) then
+ cmsg_print(TERM_YELLOW, "'At last! Thou hast found all of the relic pieces.")
+
+ -- reward player by increasing prayer skill
+ cmsg_print(TERM_YELLOW, "Thou hast done exceptionally well! I shall increase thy prayer skill even more!'")
+ skill(SKILL_PRAY).value = skill(SKILL_PRAY).value + (10 * (skill(SKILL_PRAY).mod))
+
+ -- Take the relic piece
+ floor_item_increase(item, -1)
+ floor_item_optimize(item)
+ else
+ cmsg_print(TERM_YELLOW, "'Well done! Thou hast found part of the relic.")
+ cmsg_print(TERM_YELLOW, "I shall surely ask thee to find more of it later!")
+ cmsg_print(TERM_YELLOW, "I will take it from thee for now'")
+
+ -- Take the relic piece
+ floor_item_increase(item, -1)
+ floor_item_optimize(item)
+
+ -- reward player by increasing prayer skill
+ cmsg_print(TERM_YELLOW, "'As a reward, I shall teach thee how to pray better'")
+ skill(SKILL_PRAY).value = skill(SKILL_PRAY).value + (5 * (skill(SKILL_PRAY).mod))
+ end
+
+ -- relic piece has been identified
+ o_ptr.pval = TRUE
+ god_quest.relics_found = god_quest.relics_found + 1
+
+ -- Make sure quests can be given again if neccesary
+ quest(GOD_QUEST).status = QUEST_STATUS_UNTAKEN
+ return TRUE
+ end
+ end,
+ [HOOK_CHAR_DUMP] = function()
+
+ if (god_quest.quests_given > 0) then
+
+ local relics = god_quest.relics_found
+ local append_text = ""
+ if (god_quest.relics_found == god_quest.MAX_NUM_GOD_QUESTS) then
+ relics = "all"
+ append_text = " and pleased your god"
+ else
+ if (god_quest.relics_found == 0) then
+ relics = "none"
+ end
+ if (quest(GOD_QUEST).status == QUEST_STATUS_FAILED) then
+ append_text = " and failed in your quest"
+ end
+ end
+
+ print_hook("\n You found "..(relics).." of the relic pieces"..(append_text)..".")
+
+ end
+ end,
+ },
+}
+
+-- this function places the lost temple at a randomly determined place.
+function place_rand_dung()
+ local tries, grid
+
+ -- erase old dungeon
+ if (god_quest.quests_given > 0) then
+ place_dungeon(god_quest.dung_y, god_quest.dung_x)
+
+ -- erase old recall level
+ max_dlv[god_quest.DUNGEON_GOD + 1] = 0
+ end
+
+ -- initialise tries variable
+ tries = 1000
+
+ while tries > 0 do
+
+ tries = tries - 1
+ -- get grid coordinates, within a range which prevents dungeon being generated at the very edge of the wilderness (would crash the game).
+ god_quest.dung_x = rand_range(1, max_wild_x-2)
+ god_quest.dung_y = rand_range(1, max_wild_y-2)
+
+ -- Is there a town/dungeon/potentially impassable feature there, ?
+ if (wild_map(god_quest.dung_y, god_quest.dung_x).entrance ~= 0)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).entrance ~= 0)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_EDGE)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_DEEP_WATER)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_TREES)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_SHALLOW_LAVA)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_DEEP_LAVA)
+ or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_MOUNTAIN) then
+ -- try again
+ else
+ --neither player, nor wall, then stop this 'while'
+ break
+ end
+ end
+
+ -- Uhuh BAD ! lets use the default location up bree
+ if tries == 0 then
+ god_quest.dung_x = 32
+ god_quest.dung_y = 19
+ end
+
+ -- create god dungeon in that place
+ place_dungeon(god_quest.dung_y, god_quest.dung_x, god_quest.DUNGEON_GOD)
+
+end
+
+-- this function generates the relic at a randomly determined place in the temple.
+function generate_relic()
+ local tries, grid, x, y, relic
+
+ -- initialise tries variable
+ tries = 1000
+
+ while (tries > 0) do
+
+ tries = tries - 1
+ -- get grid coordinates from current height/width, minus one to prevent relic being generated in outside wall. (would crash the game)
+ y = randint(cur_hgt-1)
+ x = randint(cur_wid-1)
+ grid = cave(y, x)
+
+ -- are the coordinates on a floor, not on a permanent feature (eg stairs), and not on a trap ?
+ if (cave_is(grid, FF1_FLOOR) == TRUE) and (cave_is(grid, FF1_PERMANENT) == FALSE) and (grid.t_idx == 0) then break end
+
+ end
+
+ -- create relic
+ relic = create_object(TV_JUNK, god_quest.relic_num)
+
+ -- inscribe it to prevent automatizer 'accidents'
+ relic.note = quark_add("quest")
+
+ -- If no safe co-ords were found, put it in the players backpack
+ if tries == 0 then
+
+ -- explain it
+ msg_print(TERM_L_BLUE, "You luckily stumble across the relic on the stairs!")
+
+ if (inven_carry_okay(relic)) then
+ inven_carry(relic, FALSE)
+ else
+ -- no place found, drop it on the stairs
+ drop_near(relic, -1, player.py, player.px)
+ end
+
+ else
+ -- drop it
+ drop_near(relic, -1, y, x)
+ end
+
+ -- Only generate once!
+ god_quest.relic_generated = TRUE
+
+ -- Reset some variables
+ god_quest.relic_gen_tries = 0
+
+end
+
+
+
+
+function set_god_dungeon_attributes()
+
+ -- dungeon properties altered according to which god player is worshipping,
+ if player.pgod == GOD_ERU then
+
+ -- The Eru temple is based on Meneltarma.
+ -- W: Not too many monsters (they'll be tough though, with big levels)
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 14
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 200
+
+ -- L: Dirt and grass. More dirt at bottom, more grass at top. rocky ground would be nice
+ dungeon(god_quest.DUNGEON_GOD).floor1 = 88
+ dungeon(god_quest.DUNGEON_GOD).floor2 = 89
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 70
+ dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 30
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[2] = 10
+ dungeon(god_quest.DUNGEON_GOD).floor_percent2[2] = 90
+
+ -- A: Outer wall mountain chain. other walls granite
+ dungeon(god_quest.DUNGEON_GOD).fill_type1 = 97
+ dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100
+ dungeon(god_quest.DUNGEON_GOD).outer_wall = 57
+ dungeon(god_quest.DUNGEON_GOD).inner_wall = 97
+ dungeon(god_quest.DUNGEON_GOD).fill_method = 2
+
+ -- O: "At Meneltarma no weapon or tool had ever been borne" (but invaders would have left a small number)
+ dungeon(god_quest.DUNGEON_GOD).objs.treasure = 45
+ dungeon(god_quest.DUNGEON_GOD).objs.combat = 5
+ dungeon(god_quest.DUNGEON_GOD).objs.magic = 45
+ dungeon(god_quest.DUNGEON_GOD).objs.tools = 5
+
+ -- F: A large pillar, with stairs created at edges. (You can't climb a rock through the middle, can you?)
+ dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_BIG, DF1_NO_DOORS, DF1_CIRCULAR_ROOMS, DF1_EMPTY, DF1_TOWER, DF1_FLAT, DF1_ADJUST_LEVEL_2)
+ dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_ADJUST_LEVEL_1_2, DF2_NO_SHAFT, DF2_ADJUST_LEVEL_PLAYER)
+
+ -- R:
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 50
+
+ -- M: We want evil or flying characters
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mflags3 = RF3_EVIL
+
+ dungeon(god_quest.DUNGEON_GOD).rules[2].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[2].percent = 50
+
+ -- M: We want evil or flying characters
+ dungeon(god_quest.DUNGEON_GOD).rules[2].mflags7 = RF7_CAN_FLY
+
+
+ elseif player.pgod == GOD_MANWE then
+
+ -- Manwe's lost temple is high in the clouds
+ -- W: Has average number of monsters.
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 18
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 160
+
+
+ -- L: floor will be 'cloud-like vapour' and pools of 'condensing water'
+ dungeon(god_quest.DUNGEON_GOD).floor1 = 208
+ dungeon(god_quest.DUNGEON_GOD).floor2 = 209
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 85
+ dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 15
+
+ -- A: Outer wall is 'hail stone wall', inner wall 'dense fog'. FIlled at max smoothing, like islands.
+ dungeon(god_quest.DUNGEON_GOD).fill_type1 = 211
+ dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100
+ dungeon(god_quest.DUNGEON_GOD).outer_wall = 210
+ dungeon(god_quest.DUNGEON_GOD).inner_wall = 211
+ dungeon(god_quest.DUNGEON_GOD).fill_method = 4
+
+ -- O: Can't imagine Manwe having much treasure. Little need for tools in a cloud temple. lots of magical stuff though...
+ dungeon(god_quest.DUNGEON_GOD).objs.treasure = 15
+ dungeon(god_quest.DUNGEON_GOD).objs.combat = 25
+ dungeon(god_quest.DUNGEON_GOD).objs.magic = 55
+ dungeon(god_quest.DUNGEON_GOD).objs.tools = 5
+
+ -- F: It's open, goes up like a tower, give it a few interesting rooms, make the monsters hard(ish).
+ dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_NO_DOORS, DF1_TOWER, DF1_CAVERN, DF1_ADJUST_LEVEL_2)
+ dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_NO_SHAFT, DF2_ADJUST_LEVEL_PLAYER)
+
+ -- R:
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 20
+ dungeon(god_quest.DUNGEON_GOD).rules[2].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[2].percent = 20
+ dungeon(god_quest.DUNGEON_GOD).rules[3].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[3].percent = 20
+ dungeon(god_quest.DUNGEON_GOD).rules[4].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[4].percent = 20
+ dungeon(god_quest.DUNGEON_GOD).rules[5].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[5].percent = 20
+
+ -- M: We want air(poison-type) or flying characters. Orcs too. They would have ransacked his elf-loving temple :)
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mflags2 = RF2_INVISIBLE
+ dungeon(god_quest.DUNGEON_GOD).rules[2].mflags3 = bor(RF3_ORC, RF3_IM_POIS)
+ dungeon(god_quest.DUNGEON_GOD).rules[3].mflags4 = bor(RF4_BR_POIS, RF4_BR_GRAV)
+ dungeon(god_quest.DUNGEON_GOD).rules[4].mflags5 = RF5_BA_POIS
+ dungeon(god_quest.DUNGEON_GOD).rules[5].mflags7 = RF7_CAN_FLY
+
+
+ elseif player.pgod == GOD_TULKAS then
+
+ -- Tulkas dungeon is quite normal, possibly a bit boring to be honest. Maybe I should add something radical to it.
+ -- 'The house of Tulkas in the midmost of Valmar was a house of mirth and revelry. It sprang into the air with many storeys,
+ -- and had a tower of bronze and pillars of copper in a wide arcade'
+ -- W: but with lots of monsters
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 20
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 120
+
+ -- L: floor is normal
+ dungeon(god_quest.DUNGEON_GOD).floor1 = 1
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 100
+
+ -- A: Granite walls
+ dungeon(god_quest.DUNGEON_GOD).fill_type1 = 56
+ dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100
+ dungeon(god_quest.DUNGEON_GOD).outer_wall = 58
+ dungeon(god_quest.DUNGEON_GOD).inner_wall = 57
+ dungeon(god_quest.DUNGEON_GOD).fill_method = 0
+
+ -- O: Loads of combat drops
+ dungeon(god_quest.DUNGEON_GOD).objs.treasure = 10
+ dungeon(god_quest.DUNGEON_GOD).objs.combat = 70
+ dungeon(god_quest.DUNGEON_GOD).objs.magic = 5
+ dungeon(god_quest.DUNGEON_GOD).objs.tools = 15
+
+ -- F: fairly standard
+ dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_NO_DESTROY, DF1_ADJUST_LEVEL_2)
+ dungeon(god_quest.DUNGEON_GOD).flags2 = DF2_ADJUST_LEVEL_PLAYER
+
+ -- R:
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 100
+
+ -- M: plenty demons please
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mflags3 = bor(RF3_DEMON, RF3_EVIL)
+
+
+ elseif player.pgod == GOD_MELKOR then
+
+ -- Melkors dungeon will be dark, fiery and stuff
+ -- Many many monsters! (but prob ADJUST_LEVEL_1_2)
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 24
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 80
+
+
+ -- L: floor is dirt/mud/nether
+ dungeon(god_quest.DUNGEON_GOD).floor1 = 88
+ dungeon(god_quest.DUNGEON_GOD).floor2 = 94
+ dungeon(god_quest.DUNGEON_GOD).floor3 = 102
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 45
+ dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 45
+ dungeon(god_quest.DUNGEON_GOD).floor_percent3[1] = 10
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[2] = 35
+ dungeon(god_quest.DUNGEON_GOD).floor_percent2[2] = 35
+ dungeon(god_quest.DUNGEON_GOD).floor_percent3[2] = 30
+
+ -- A: Granite walls to fill but glass walls for room perimeters (you can see the nasty monsters coming)
+ dungeon(god_quest.DUNGEON_GOD).fill_type1 = 188
+ dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100
+ dungeon(god_quest.DUNGEON_GOD).outer_wall = 188
+ dungeon(god_quest.DUNGEON_GOD).inner_wall = 57
+ dungeon(god_quest.DUNGEON_GOD).fill_method = 1
+
+ -- O: Even drops
+ dungeon(god_quest.DUNGEON_GOD).objs.treasure = 25
+ dungeon(god_quest.DUNGEON_GOD).objs.combat = 25
+ dungeon(god_quest.DUNGEON_GOD).objs.magic = 25
+ dungeon(god_quest.DUNGEON_GOD).objs.tools = 25
+
+ -- F: Small, lava rivers, nasty monsters hehehehehe
+ dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_SMALL, DF1_LAVA_RIVERS, DF1_ADJUST_LEVEL_1)
+ dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_ADJUST_LEVEL_1_2, DF2_ADJUST_LEVEL_PLAYER)
+
+ -- R: No restrictions on monsters here
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 0
+ dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 80
+
+ -- R: Apart from making sure we have some GOOD ones
+ dungeon(god_quest.DUNGEON_GOD).rules[2].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[2].percent = 20
+
+ -- M:
+ dungeon(god_quest.DUNGEON_GOD).rules[2].mflags3 = RF3_GOOD
+
+ elseif player.pgod == GOD_YAVANNA then
+
+ -- Yavannas dungeon will be very natural, tress and stuff.
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 22
+ dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 100
+
+ -- L: floor is grass/flowers, plus dirt so not always regenerating quick!
+ dungeon(god_quest.DUNGEON_GOD).floor1 = 89
+ dungeon(god_quest.DUNGEON_GOD).floor2 = 199
+ dungeon(god_quest.DUNGEON_GOD).floor3 = 88
+ dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 40
+ dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 15
+ dungeon(god_quest.DUNGEON_GOD).floor_percent3[1] = 45
+
+ -- A: Tree walls to fill, small trees for inner walls
+ dungeon(god_quest.DUNGEON_GOD).fill_type1 = 96
+ dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100
+ dungeon(god_quest.DUNGEON_GOD).outer_wall = 202
+ dungeon(god_quest.DUNGEON_GOD).inner_wall = 96
+ dungeon(god_quest.DUNGEON_GOD).fill_method = 1
+
+ -- O: nt much combat.. tools where ransackers have tried to chop trees down.
+ dungeon(god_quest.DUNGEON_GOD).objs.treasure = 20
+ dungeon(god_quest.DUNGEON_GOD).objs.combat = 10
+ dungeon(god_quest.DUNGEON_GOD).objs.magic = 30
+ dungeon(god_quest.DUNGEON_GOD).objs.tools = 40
+
+ -- F: Natural looking
+ dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_NO_DOORS, DF1_WATER_RIVERS, DF1_NO_DESTROY, DF1_ADJUST_LEVEL_1, DF1_NO_RECALL)
+ dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_ADJUST_LEVEL_1_2, DF2_NO_SHAFT, DF2_NO_GENO, DF2_ADJUST_LEVEL_PLAYER)
+
+ -- R: Demons, Undead, non-living
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3
+ dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 100
+
+ -- M:
+ dungeon(god_quest.DUNGEON_GOD).rules[1].mflags3 = bor(RF3_DEMON, RF3_UNDEAD, RF3_NONLIVING)
+
+ end
+
+ -- W: All dungeons are 5 levels deep, and created at 2/3 of the player clvl when the quest is given
+ dungeon(god_quest.DUNGEON_GOD).mindepth = god_quest.dun_mindepth
+ dungeon(god_quest.DUNGEON_GOD).maxdepth = god_quest.dun_maxdepth
+ dungeon(god_quest.DUNGEON_GOD).minplev = god_quest.dun_minplev
+
+end
+
+-- Calling this function returns the direction the dungeon is in from the players position at the time
+-- the quest was given, and also the direction from angband (if the player is worshipping Melkor) or lothlorien.
+function get_god_quest_axes()
+ local home, home_y_coord, home_x_coord, home_axis, home2, home2_y_coord, home2_x_coord, home2_axis, mydistance
+
+ -- different values for different gods...
+ if player.pgod ~= GOD_MELKOR then
+
+ -- one of the valar, "home" is lothlorien, home2 is Minas Arnor
+ home = "Bree"
+ home_y_coord = 21
+ home_x_coord = 34
+ home2 = "Minas Anor"
+ home2_y_coord = 56
+ home2_x_coord = 60
+ else
+ -- Melkor, "home" is angband, home2 is Barad-dur
+ home = "the Pits of Angband"
+ home_y_coord = 7
+ home_x_coord = 34
+ home2 = "the Land of Mordor"
+ home2_y_coord = 58
+ home2_x_coord = 65
+ end
+
+ home_axis = compass(home_y_coord, home_x_coord, god_quest.dung_y, god_quest.dung_x)
+ home2_axis = compass(home2_y_coord, home2_x_coord, god_quest.dung_y, god_quest.dung_x)
+
+ home_distance = approximate_distance(home_y_coord, home_x_coord, god_quest.dung_y, god_quest.dung_x)
+ home2_distance = approximate_distance(home2_y_coord, home2_x_coord, god_quest.dung_y, god_quest.dung_x)
+
+ return home, home_axis, home_distance, home2, home2_axis, home2_distance
+end
diff --git a/lib/scpt/gods.lua b/lib/scpt/gods.lua
new file mode 100644
index 00000000..014a4423
--- /dev/null
+++ b/lib/scpt/gods.lua
@@ -0,0 +1,26 @@
+add_hooks
+{
+ [HOOK_FOLLOW_GOD] = function(god, action)
+ if action == "ask" then
+ if not (god == GOD_MELKOR) then
+ local i = INVEN_WIELD
+ while i < INVEN_TOTAL do
+ -- 13 is ART_POWER
+ if player.inventory(i).name1 == 13 then
+ msg_print("The One Ring has corrupted you, and you are rejected.")
+ return TRUE
+ end
+ i = i + 1
+ end
+ end
+ end
+ return FALSE
+ end,
+ [HOOK_RECALC_SKILLS] = function()
+ if not (player.pgod == GOD_NONE) and (get_skill(SKILL_ANTIMAGIC) > 0) then
+ msg_print("You no longer believe.")
+ abandon_god(GOD_ALL)
+ end
+ return FALSE
+ end,
+}
diff --git a/lib/scpt/help.lua b/lib/scpt/help.lua
new file mode 100644
index 00000000..5350fec8
--- /dev/null
+++ b/lib/scpt/help.lua
@@ -0,0 +1,411 @@
+-- Ingame contextual help
+
+-------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
+-----------------------Here comes the definition of help-----------------------
+-------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
+
+ingame_help
+{
+ ["hook"] = HOOK_MOVE,
+ ["event"] = function(y, x) if cave(y, x).feat == FEAT_BETWEEN then return TRUE end end,
+ ["desc"] =
+ {
+ "Void Jumpgates can be entered by pressing the > key. They will transport",
+ "you to another jumpgate, but beware of the cold damage that might kill you.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_MOVE,
+ ["event"] = function(y, x) if cave(y, x).feat == FEAT_FOUNTAIN then return TRUE end end,
+ ["desc"] =
+ {
+ "Fountains are always magical. You can quaff from them by pressing H.",
+ "Beware that unlike potions they cannot be identified.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_MOVE,
+ ["event"] = function(y, x) if cave(y, x).o_idx ~= 0 then return TRUE end end,
+ ["desc"] =
+ {
+ "So you found your first item! Nice, eh? Now when you stumble across",
+ "objects, you can pick them up by pressing g, and if you are wondering",
+ "what they do, press I (then *, then the letter for the item) to get",
+ "some basic information. You may also want to identify them with scrolls,",
+ "staves, rods or spells.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_MOVE,
+ ["event"] = function(y, x) if (cave(y, x).feat >= FEAT_ALTAR_HEAD) and (cave(y, x).feat <= FEAT_ALTAR_TAIL) then return TRUE end end,
+ ["desc"] =
+ {
+ "Altars are the way to reach the Valar, powers of the world,",
+ "usualy called Gods. You can press O to become a follower.",
+ "Beware that once you follow a god, you are not allowed to change.",
+ "For an exact description of what gods do and want, read the documentation."
+ }
+}
+
+-- Beware this one, if Bree is moved from 21, 34 (y, x) on the wilderness map it will break
+ingame_help
+{
+ ["hook"] = HOOK_END_TURN,
+ ["event"] = function(y, x)
+ if ((player.wilderness_x ~= 34) or (player.wilderness_y ~= 21) and (player.astral == FALSE)) then return TRUE end
+ end,
+ ["desc"] =
+ {
+ "Ahh wilderness travel... The overview mode will allow you to travel",
+ "fast, but that comes to the cost of GREATLY increased food consumption.",
+ "So you should bring lots of food and really watch your hunger status.",
+ "To enter the overview mode, press < while in the wilderness.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_PLAYER_LEVEL,
+ ["event"] = function(y, x) if player.lev > 1 then return TRUE end end,
+ ["desc"] =
+ {
+ "Ok, so you now gained a level, and you have skill points to spend.",
+ "To do so simply press G to learn skills. Reading the documentation",
+ "about skills and abilities is also strongly recommended.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_MOVE,
+ ["event"] = function(y, x) if cave(y, x).feat == FEAT_MORE then return TRUE end end,
+ ["desc"] =
+ {
+ "Ah, this is a stair, or a way into something. Press > to enter it.",
+ "But be ready to fight what lies within, for it might not be too friendly.",
+ }
+}
+
+ingame_help
+{
+ ["callback"] = "monster_chat",
+ ["desc"] =
+ {
+ "Somebody is speaking to you it seems. You can talk back with the Y key.",
+ "This can lead to quests. You can also give items to 'monsters' with the y key.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_END_TURN,
+ ["event"] = function(y, x) return TRUE end,
+ ["desc"] =
+ {
+ "Welcome to ToME! I am the spirit of knowledge and my task is to help you",
+ "to get used to how to play. I have prepared a #vparchment#y for you to #vread#y.",
+ "Press r, then space then select it. You can also check the documentation",
+ "by pressing ? at (nearly) any time.",
+ "The first place you can explore is Barrow-downs. Go to the west of town",
+ "and you should see a #v>#y there.",
+ "If you miss any of this you can press ctrl+p to see your message log.",
+ "Now I must reveal your task here. You are on a quest to investigate",
+ "the dreadful tower of Dol Guldur in the Mirkwood forest to see what evil",
+ "lurks there, but beware, you are not yet ready.",
+ "If you do not want me to bother you any more with tips, press = then go",
+ "into the ToME options and deactivate the ingame_help option.",
+ "You can see your quest log by pressing ctrl+q. Now go to your destiny!",
+ }
+}
+
+ingame_help
+{
+ ["no_test"] = TRUE,
+ ["callback"] = "select_context",
+ ["fct"] = function(typ, name)
+ -- list of files for classes, { filename, anchor }
+ local t =
+ {
+ ["race"] =
+ {
+ ["Beorning"] = { "r_beorn.txt", 0 },
+ ["DeathMold"] = { "r_deathm.txt", 0 },
+ ["Dark-Elf"] = { "r_drkelf.txt", 0 },
+ ["Dunadan"] = { "r_dunad.txt", 0 },
+ ["Dwarf"] = { "r_dwarf.txt", 0 },
+ ["Elf"] = { "r_elf.txt", 0 },
+ ["Ent"] = { "r_ent.txt", 0 },
+ ["Gnome"] = { "r_gnome.txt", 0 },
+ ["Half-Elf"] = { "r_hafelf.txt", 0 },
+ ["Half-Ogre"] = { "r_hafogr.txt", 0 },
+ ["High-Elf"] = { "r_hielf.txt", 0 },
+ ["Hobbit"] = { "r_hobbit.txt", 0 },
+ ["Human"] = { "r_human.txt", 0 },
+ ["Kobold"] = { "r_kobold.txt", 0 },
+ ["Maia"] = { "r_maia.txt", 0 },
+ ["Orc"] = { "r_orc.txt", 0 },
+ ["Petty-Dwarf"] = { "r_pettyd.txt", 0 },
+ ["RohanKnight"] = { "r_rohank.txt", 0 },
+ ["Thunderlord"] = { "r_thlord.txt", 0 },
+ ["Troll"] = { "r_troll.txt", 0 },
+ ["Wood-Elf"] = { "r_wodelf.txt", 0 },
+ ["Yeek"] = { "r_yeek.txt", 0 },
+ },
+ ["subrace"] =
+ {
+ ["Barbarian"] = { "rm_barb.txt", 0 },
+ ["Classical"] = { "rm_class.txt", 0 },
+ ["Corrupted"] = { "rm_corru.txt", 0 },
+ ["Hermit"] = { "rm_herm.txt", 0 },
+ ["LostSoul"] = { "rm_lsoul.txt", 0 },
+ ["Skeleton"] = { "rm_skel.txt", 0 },
+ ["Spectre"] = { "rm_spec.txt", 0 },
+ ["Vampire"] = { "rm_vamp.txt", 0 },
+ ["Zombie"] = { "rm_zomb.txt", 0 },
+ },
+ ["class"] =
+ {
+ ["Alchemist"] = { "c_alchem.txt", 0 },
+ ["Archer"] = { "c_archer.txt", 0 },
+ ["Assassin"] = { "c_assass.txt", 0 },
+ ["Axemaster"] = { "c_axemas.txt", 0 },
+ ["Bard"] = { "c_bard.txt", 0 },
+ ["Dark-Priest"] = { "c_pr_drk.txt", 0 },
+ ["Demonologist"] = { "c_demono.txt", 0 },
+ ["Druid"] = { "c_druid.txt", 0 },
+ ["Geomancer"] = { "c_geoman.txt", 0 },
+ ["Haftedmaster"] = { "c_hafted.txt", 0 },
+ ["Loremaster"] = { "c_lorema.txt", 0 },
+ ["Mage"] = { "c_mage.txt", 0 },
+ ["Mimic"] = { "c_mimic.txt", 0 },
+ ["Mindcrafter"] = { "c_mindcr.txt", 0 },
+ ["Monk"] = { "c_monk.txt", 0 },
+ ["Necromancer"] = { "c_necro.txt", 0 },
+ ["Paladin"] = { "c_palad.txt", 0 },
+ ["Polearmmaster"] = { "c_polear.txt", 0 },
+ ["Possessor"] = { "c_posses.txt", 0 },
+ ["Priest"] = { "c_priest.txt", 0 },
+ ["Priest(Eru)"] = { "c_pr_eru.txt", 0 },
+ ["Priest(Manwe)"] = { "c_pr_man.txt", 0 },
+ ["Ranger"] = { "c_ranger.txt", 0 },
+ ["Rogue"] = { "c_rogue.txt", 0 },
+ ["Runecrafter"] = { "c_runecr.txt", 0 },
+ ["Sorceror"] = { "c_sorcer.txt", 0 },
+ ["Summoner"] = { "c_summon.txt", 0 },
+ ["Swordmaster"] = { "c_swordm.txt", 0 },
+ ["Symbiant"] = { "c_symbia.txt", 0 },
+ ["Thaumaturgist"] = { "c_thaum.txt", 0 },
+ ["Unbeliever"] = { "c_unbel.txt", 0 },
+ ["Warper"] = { "c_warper.txt", 0 },
+ ["Warrior"] = { "c_warrio.txt", 0 },
+ },
+ ["god"] =
+ {
+ ["Eru Iluvatar"] = { "g_eru.txt", 0 },
+ ["Manwe Sulimo"] = { "g_manwe.txt", 0 },
+ ["Tulkas"] = { "g_tulkas.txt", 0 },
+ ["Melkor Bauglir"] = { "g_melkor.txt", 0 },
+ ["Yavanna Kementari"] = { "g_yavann.txt", 0 },
+ },
+ ["skill"] =
+ {
+ ["Air"] = { "skills.txt", 27 },
+ ["Alchemy"] = { "skills.txt", 49 },
+ ["Antimagic"] = { "skills.txt", 50 },
+ ["Archery"] = { "skills.txt", 08 },
+ ["Axe-mastery"] = { "skills.txt", 05 },
+ ["Backstab"] = { "skills.txt", 18 },
+ ["Barehand-combat"] = { "skills.txt", 13 },
+ ["Boomerang-mastery"] = { "skills.txt", 12 },
+ ["Boulder-throwing"] = { "skills.txt", 58 },
+ ["Bow-mastery"] = { "skills.txt", 10 },
+ ["Combat"] = { "skills.txt", 01 },
+ ["Conveyance"] = { "skills.txt", 30 },
+ ["Corpse-preservation"] = { "skills.txt", 44 },
+ ["Critical-hits"] = { "skills.txt", 04 },
+ ["Crossbow-mastery"] = { "skills.txt", 11 },
+ ["Demonology"] = { "skills.txt", 52 },
+ ["Disarming"] = { "skills.txt", 16 },
+ ["Divination"] = { "skills.txt", 31 },
+ ["Dodging"] = { "skills.txt", 20 },
+ ["Druidistic"] = { "skills.txt", 40 },
+ ["Earth"] = { "skills.txt", 28 },
+ ["Fire"] = { "skills.txt", 25 },
+ ["Geomancy"] = { "skills.txt", 60 },
+ ["Hafted-mastery"] = { "skills.txt", 06 },
+ ["Magic"] = { "skills.txt", 21 },
+ ["Magic-Device"] = { "skills.txt", 54 },
+ ["Mana"] = { "skills.txt", 24 },
+ ["Meta"] = { "skills.txt", 29 },
+ ["Mimicry"] = { "skills.txt", 47 },
+ ["Mind"] = { "skills.txt", 33 },
+ ["Mindcraft"] = { "skills.txt", 41 },
+ ["Monster-lore"] = { "skills.txt", 42 },
+ ["Music"] = { "skills.txt", 59 },
+ ["Nature"] = { "skills.txt", 34 },
+ ["Necromancy"] = { "skills.txt", 35 },
+ ["Polearm-mastery"] = { "skills.txt", 07 },
+ ["Possession"] = { "skills.txt", 45 },
+ ["Prayer"] = { "skills.txt", 39 },
+ ["Runecraft"] = { "skills.txt", 36 },
+ ["Sling-mastery"] = { "skills.txt", 09 },
+ ["Sneakiness"] = { "skills.txt", 14 },
+ ["Spell-power"] = { "skills.txt", 22 },
+ ["Spirituality"] = { "skills.txt", 38 },
+ ["Sorcery"] = { "skills.txt", 23 },
+ ["Stealing"] = { "skills.txt", 19 },
+ ["Stealth"] = { "skills.txt", 15 },
+ ["Stunning-blows"] = { "skills.txt", 53 },
+ ["Summoning"] = { "skills.txt", 43 },
+ ["Sword-mastery"] = { "skills.txt", 03 },
+ ["Symbiosis"] = { "skills.txt", 46 },
+ ["Temporal"] = { "skills.txt", 32 },
+ ["Thaumaturgy"] = { "skills.txt", 37 },
+ ["Udun"] = { "skills.txt", 48 },
+ ["Weaponmastery"] = { "skills.txt", 02 },
+ ["Water"] = { "skills.txt", 26 },
+ },
+ ["ability"] =
+ {
+ ["Spread blows"] = { "ability.txt", 02 },
+ ["Tree walking"] = { "ability.txt", 03 },
+ ["Perfect casting"] = { "ability.txt", 04 },
+ ["Extra Max Blow(1)"] = { "ability.txt", 05 },
+ ["Extra Max Blow(2)"] = { "ability.txt", 06 },
+ ["Ammo creation"] = { "ability.txt", 07 },
+ ["Touch of death"] = { "ability.txt", 08 },
+ ["Artifact Creation"] = { "ability.txt", 09 },
+ ["Far reaching attack"] = { "ability.txt", 10 },
+ ["Trapping"] = { "ability.txt", 11 },
+ ["Undead Form"] = { "ability.txt", 12 },
+ },
+ }
+
+ if t[typ][name] then ingame_help_doc(t[typ][name][1], t[typ][name][2])
+ else ingame_help_doc("help.hlp", 0)
+ end
+ end,
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_IDENTIFY,
+ ["event"] = function(i, mode)
+ if mode == "full" then
+ local obj = get_object(i)
+ local f1, f2, f3, f4, f5, esp = object_flags(obj)
+ if band(f5, TR5_SPELL_CONTAIN) ~= 0 then return TRUE end
+ end
+ end,
+ ["desc"] =
+ {
+ "Ah, an item that can contain a spell. To use it you must have some levels of",
+ "Magic skill and then you get the option to copy a spell when pressing m.",
+ "Then just select which spell to copy and to which object. Note that doing so",
+ "is permanent; the spell cannot be removed or changed later.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_GET,
+ ["event"] = function(obj, idx) if obj.tval == TV_BATERIE then return TRUE end end,
+ ["desc"] =
+ {
+ "Ah, an essence! Those magical containers stores energies. They are used",
+ "with the Alchemy skill to create or modify the powers of items.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_GET,
+ ["event"] = function(obj, idx) if obj.tval == TV_RUNE1 or obj.tval == TV_RUNE2 then return TRUE end end,
+ ["desc"] =
+ {
+ "Ah, a rune! Runes are used with the Runecraft skill to allow you to",
+ "create spells on your own.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_GET,
+ ["event"] = function(obj, idx) if obj.tval == TV_ROD_MAIN then return TRUE end end,
+ ["desc"] =
+ {
+ "This is a rod. You will need to attach a rod tip to it before you",
+ "can use it. This main part of the rod may give the rod bonuses",
+ "like quicker charging time, or a larger capacity for charges.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_GET,
+ ["event"] = function(obj, idx) if obj.tval == TV_ROD then return TRUE end end,
+ ["desc"] =
+ {
+ "You've found a rod-tip! You will need to attach it to a rod base",
+ "before you can use it. Once it has been attatched (use the 'z' key)",
+ "you cannot unattach it! The rod tip will determine the effect of",
+ "the rod. To use your rod, 'z'ap it once it has been assembled.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_GET,
+ ["event"] = function(obj, idx) if obj.tval == TV_TRAPKIT then return TRUE end end,
+ ["desc"] =
+ {
+ "Ooooh, a trapping kit. If you have ability in the trapping skill,",
+ "you can lay this trap (via the 'm' key) to harm unsuspecting foes.",
+ "You'll generally need either some ammo or magic device depending",
+ "on the exact type of trap kit.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_RECALC_SKILLS,
+ ["event"] = function() if game.started and (get_melee_skills() > 1) then return TRUE end end,
+ ["desc"] =
+ {
+ "Ah, you now possess more than one melee type. To switch between them press m",
+ "and select the switch melee type option.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_GET,
+ ["event"] = function(obj, idx) if obj.tval == TV_WAND or obj.tval == TV_STAFF then return TRUE end end,
+ ["desc"] =
+ {
+ "You've found a magical device, either a staff or a wand. Each staff",
+ "contains a spell, often from one of the primary magic schools. There",
+ "is a lot of information you can find about this object if you identify",
+ "it and 'I'nspect it. Check the help file on Magic for more about these.",
+ }
+}
+
+ingame_help
+{
+ ["hook"] = HOOK_PLAYER_LEVEL,
+ ["event"] = function(y, x) if player.lev >= 20 then return TRUE end end,
+ ["desc"] =
+ {
+ "I see you are now at least level 20. Nice! If you want to gloat about your",
+ "character you could press 'C' then 'f' to make a character dump and post it to",
+ "http://angband.oook.cz/ where it will end up in the ladder.",
+ }
+}
diff --git a/lib/scpt/init.lua b/lib/scpt/init.lua
new file mode 100644
index 00000000..a6f3f8ab
--- /dev/null
+++ b/lib/scpt/init.lua
@@ -0,0 +1,46 @@
+--
+-- This file is loaded at the initialisation of ToME
+--
+
+-- Load the class specific stuff
+tome_dofile("player.lua")
+
+-- Load the ingame contextual help
+tome_dofile("help.lua")
+
+-- let the store specific stuff happen!
+tome_dofile("stores.lua")
+
+-- Add various 'U' powers
+tome_dofile("powers.lua")
+
+-- Add the mimic shapes
+tome_dofile("mimic.lua")
+
+-- Add the corruptions
+tome_dofile("corrupt.lua")
+
+-- Add the mkey activations
+tome_dofile("mkeys.lua")
+
+-- Add the schools of magic
+tome_dofile("spells.lua")
+
+-- Add god stuff
+tome_dofile("gods.lua")
+
+-- Add some quests
+tome_dofile("bounty.lua")
+tome_dofile("god.lua")
+tome_dofile("fireprof.lua")
+tome_dofile("library.lua")
+
+-- Add joke stuff
+tome_dofile("drunk.lua")
+tome_dofile("joke.lua")
+
+-- Some tests, if the file is not present, this is fine
+tome_dofile_anywhere(ANGBAND_DIR_SCPT, "dg_test.lua", FALSE)
+
+-- A nice custom intro :)
+tome_dofile("intro.lua")
diff --git a/lib/scpt/intro.lua b/lib/scpt/intro.lua
new file mode 100644
index 00000000..2c4d3def
--- /dev/null
+++ b/lib/scpt/intro.lua
@@ -0,0 +1,105 @@
+function drop_text_left(c, str, y, o)
+ local i = strlen(str)
+ local x = 39 - (strlen(str) / 2) + o
+ while (i > 0)
+ do
+ local a = 0
+ local time = 0
+
+ if (strbyte(str, i) ~= strbyte(" ", 1)) then
+ while (a < x + i - 1)
+ do
+ Term_putch(a - 1, y, c, 32)
+ Term_putch(a, y, c, strbyte(str, i))
+ time = time + 1
+ if time >= 4 then
+ Term_xtra(TERM_XTRA_DELAY, 1)
+ time = 0
+ end
+ Term_redraw_section(a - 1, y, a, y)
+ a = a + 1
+
+ inkey_scan = TRUE
+ if (inkey() ~= 0) then
+ return TRUE
+ end
+ end
+ end
+
+ i = i - 1
+ end
+ return FALSE
+end
+
+function drop_text_right(c, str, y, o)
+ local x = 39 - (strlen(str) / 2) + o
+ local i = 1
+ while (i <= strlen(str))
+ do
+ local a = 79
+ local time = 0
+
+ if (strbyte(str, i) ~= strbyte(" ", 1)) then
+ while (a >= x + i - 1)
+ do
+ Term_putch(a + 1, y, c, 32)
+ Term_putch(a, y, c, strbyte(str, i))
+ time = time + 1
+ if time >= 4 then
+ Term_xtra(TERM_XTRA_DELAY, 1)
+ time = 0
+ end
+ Term_redraw_section(a, y, a + 1, y)
+ a = a - 1
+
+ inkey_scan = TRUE
+ if (inkey() ~= 0) then
+ return TRUE
+ end
+ end
+ end
+
+ i = i + 1
+ end
+ return FALSE
+end
+
+function tome_intro()
+ screen_save()
+ Term_clear()
+
+ if (TRUE == drop_text_left(TERM_L_BLUE, "Art thou an adventurer,", 10, 0)) then screen_load() return end
+ if (TRUE == drop_text_right(TERM_L_BLUE, "One who passes through the waterfalls we call danger", 11, -1)) then screen_load() return end
+ if (TRUE == drop_text_left(TERM_L_BLUE, "to find the true nature of the legends beyond them?", 12, 0)) then screen_load() return end
+ if (TRUE == drop_text_right(TERM_L_BLUE, "If this is so, then seeketh me.", 13, -1)) then screen_load() return end
+
+ if (TRUE == drop_text_left(TERM_WHITE, "[Press any key to continue]", 23, -1)) then screen_load() return end
+
+ Term_putch(0, 0, TERM_DARK, 32)
+ inkey_scan = FALSE
+ inkey()
+
+ Term_clear()
+
+ if (TRUE == drop_text_left(TERM_L_BLUE, "DarkGod", 8, 0)) then screen_load() return end
+ if (TRUE == drop_text_right(TERM_WHITE, "in collaboration with", 9, -1)) then screen_load() return end
+ if (TRUE == drop_text_left(TERM_L_GREEN, "Eru Iluvatar,", 10, 0)) then screen_load() return end
+ if (TRUE == drop_text_right(TERM_L_GREEN, "Manwe", 11, -1)) then screen_load() return end
+ if (TRUE == drop_text_left(TERM_WHITE, "and", 12, 0)) then screen_load() return end
+ if (TRUE == drop_text_right(TERM_L_GREEN, "All the T.o.M.E. contributors(see credits.txt)", 13, -1)) then screen_load() return end
+
+ if (TRUE == drop_text_left(TERM_WHITE, "present", 15, 1)) then screen_load() return end
+ if (TRUE == drop_text_right(TERM_YELLOW, "T.o.M.E.", 16, 0)) then screen_load() return end
+
+ if (TRUE == drop_text_left(TERM_WHITE, "[Press any key to continue]", 23, -1)) then screen_load() return end
+ Term_putch(0, 0, TERM_DARK, 32)
+
+ inkey_scan = FALSE
+
+ inkey()
+
+ screen_load()
+ return
+end
+
+add_hook_script(HOOK_INIT, "tome_intro", "lua_intro_init")
diff --git a/lib/scpt/joke.lua b/lib/scpt/joke.lua
new file mode 100644
index 00000000..2d87b651
--- /dev/null
+++ b/lib/scpt/joke.lua
@@ -0,0 +1,31 @@
+-- Place a monster in a good spot
+function gen_joke_place_monster(r_idx)
+ local try = 1000
+ local x
+ local y
+ while try > 0 do
+ x = randint(cur_hgt - 4) + 2
+ y = randint(cur_wid - 4) + 2
+ if not (0 == place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY)) then
+ return
+ end
+ try = try - 1
+ end
+end
+
+-- Check if a special joke monster can be generated here
+function gen_joke_monsters()
+ if joke_monsters == FALSE then
+ return
+ end
+
+ -- Neil
+ if (current_dungeon_idx == 20) and (dun_level == 72) then
+ neil = test_monster_name("Neil, the Sorceror")
+ m_allow_special[neil + 1] = TRUE
+ gen_joke_place_monster(neil)
+ m_allow_special[neil + 1] = FALSE
+ end
+end
+
+add_hook_script(HOOK_LEVEL_END_GEN, "gen_joke_monsters", "gen_joke_monsters")
diff --git a/lib/scpt/library.lua b/lib/scpt/library.lua
new file mode 100644
index 00000000..99c2c0f4
--- /dev/null
+++ b/lib/scpt/library.lua
@@ -0,0 +1,513 @@
+-- Library quest in Minas Anor
+
+-- Partially based on Fireproofing quest
+
+library_quest = {}
+
+-- The map definition itself
+library_quest.MAP =
+[[#!map
+
+# Permanent wall
+F:X:63:3
+
+# Granite Wall
+F:#:57:3
+
+# Cobblestone Road
+F:O:200:3
+
+# Floor
+F:.:1:3
+
+# Lich
+F:l:200:3:518
+
+# Master lich
+F:L:200:3:658
+
+# Quest exit
+F:<:6:3
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X###############################################################X
+D:X#<OlOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X
+D:X#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOL#X
+D:X###############################################################X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Starting position
+P:4:4
+]]
+
+-- Map helper
+library_quest.place_random = function(minY, minX, maxY, maxX, monster)
+ y = randint(maxY - minY + 1) + minY
+ x = randint(maxX - minX + 1) + minX
+ return place_monster_one(y, x, monster, 0, TRUE, MSTATUS_ENEMY)
+end
+
+-- Book creation helpers
+library_quest.bookable_spells =
+{
+ MANATHRUST, DELCURSES,
+ GLOBELIGHT, FIREGOLEM, FIREFLASH, FIREWALL,
+ GEYSER, VAPOR, ENTPOTION,
+ NOXIOUSCLOUD, POISONBLOOD,
+ STONESKIN, DIG,
+ RECHARGE, DISPERSEMAGIC,
+ BLINK, DISARM, TELEPORT,
+ SENSEMONSTERS, SENSEHIDDEN, REVEALWAYS, IDENTIFY, VISION,
+ MAGELOCK, SLOWMONSTER, ESSENCESPEED,
+ CHARM, CONFUSE, ARMOROFFEAR, STUN,
+ GROWTREE, HEALING, RECOVERY,
+ ERU_SEE, ERU_LISTEN,
+ MANWE_BLESS, MANWE_SHIELD,
+ YAVANNA_CHARM_ANIMAL, YAVANNA_GROW_GRASS, YAVANNA_TREE_ROOTS,
+ TULKAS_AIM, TULKAS_SPIN,
+ MELKOR_CURSE, MELKOR_CORPSE_EXPLOSION,
+ DRAIN
+}
+
+library_quest.get_term_size = function()
+ local width = 0
+ local height = 0
+ ret, width, height = Term_get_size(width, height)
+ return width, height
+end
+
+library_quest.book_slots_left = function()
+ if school_book[61][1] == -1 then
+ return 3
+ elseif school_book[61][2] == -1 then
+ return 2
+ elseif school_book[61][3] == -1 then
+ return 1
+ else
+ return 0
+ end
+end
+
+library_quest.book_contains_spell = function(spell)
+ if school_book[61][1] == spell then
+ return TRUE
+ elseif school_book[61][2] == spell then
+ return TRUE
+ elseif school_book[61][3] == spell then
+ return TRUE
+ else
+ return FALSE
+ end
+end
+
+library_quest.add_spell = function(spell)
+ if school_book[61][1] == -1 then
+ school_book[61][1] = spell
+ return TRUE
+ elseif school_book[61][2] == -1 then
+ school_book[61][2] = spell
+ return TRUE
+ elseif school_book[61][3] == -1 then
+ school_book[61][3] = spell
+ return TRUE
+ else
+ return FALSE
+ end
+end
+
+library_quest.remove_spell = function(spell)
+ if school_book[61][1] == spell then
+ school_book[61][1] = school_book[61][2]
+ school_book[61][2] = school_book[61][3]
+ school_book[61][3] = -1
+ return TRUE
+ elseif school_book[61][2] == spell then
+ school_book[61][2] = school_book[61][3]
+ school_book[61][3] = -1
+ return TRUE
+ elseif school_book[61][3] == spell then
+ school_book[61][3] = -1
+ return TRUE
+ else
+ return FALSE
+ end
+end
+
+-- Print a spell (taken from s_aux)
+function library_quest.print_spell(color, y, spl)
+ local x, index, sch, size, s
+
+ x = 0
+ size = 0
+ book = 255
+ obj = nil
+
+ -- Hack if the book is 255 it is a random book
+ if book == 255 then
+ school_book[book] = {spl}
+ end
+
+ -- Parse all spells
+ for index, s in school_book[book] do
+ local lvl, na = get_level_school(s, 50, -50)
+ local xx, sch_str
+
+ xx = nil
+ sch_str = ""
+ for index, sch in __spell_school[s] do
+ if xx then
+ sch_str = sch_str.."/"..school(sch).name
+ else
+ xx = 1
+ sch_str = sch_str..school(sch).name
+ end
+ end
+
+ if s == spl then
+ if na then
+ c_prt(color, format("%-20s%-16s %3s %4s %3d%s %s", spell(s).name, sch_str, na, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x)
+ else
+ c_prt(color, format("%-20s%-16s %3d %4s %3d%s %s", spell(s).name, sch_str, lvl, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x)
+ end
+ y = y + 1
+ size = size + 1
+ end
+ end
+ return y
+end
+
+-- spell selection routines inspired by skills.c
+library_quest.print_spells = function(first, current)
+ Term_clear()
+ width, height = library_quest.get_term_size()
+ slots = library_quest.book_slots_left()
+
+ c_prt(TERM_WHITE, "Book Creation Screen", 0, 0);
+ c_prt(TERM_WHITE, "Up/Down to move, Right/Left to modify, I to describe, Esc to Save/Cancel", 1, 0);
+
+ if slots == 0 then
+ c_prt(TERM_L_RED, "The book can hold no more spells.", 2, 0);
+ elseif slots == 1 then
+ c_prt(TERM_L_BLUE, "The book can hold 1 more spell.", 2, 0);
+ else
+ c_prt(TERM_L_BLUE, "The book can hold "..slots.." more spells.", 2, 0);
+ end
+
+ row = 3;
+ for index, spell in library_quest.bookable_spells do
+ if index >= first then
+ if index == current then
+ color = TERM_GREEN
+ elseif library_quest.book_contains_spell(spell) == TRUE then
+ color = TERM_WHITE
+ else
+ color = TERM_ORANGE
+ end
+ library_quest.print_spell(color, row, spell)
+
+ if row == height - 1 then
+ return
+ end
+ row = row + 1
+ end
+ end
+end
+
+library_quest.fill_book = function()
+ -- Always start with a cleared book
+ school_book[61] = {-1, -1, -1}
+
+ screen_save()
+ width, height = library_quest.get_term_size()
+ -- room for legend
+ margin = 3
+
+ first = 1
+ current = 1
+ done = FALSE
+
+ while done == FALSE do
+ library_quest.print_spells(first, current)
+
+ inkey_scan = FALSE
+ inkey_base = TRUE
+ char = inkey()
+ dir = get_keymap_dir(char)
+ if char == ESCAPE then
+ if library_quest.book_slots_left() == 0 then
+ flush()
+ done = get_check("Really create the book?")
+ else
+ done = TRUE
+ end
+ elseif char == strbyte('\r') then
+ -- TODO: make tree of schools
+ elseif char == strbyte('n') then
+ current = current + height
+ elseif char == strbyte('p') then
+ current = current - height
+ elseif char == strbyte('I') then
+ print_spell_desc(library_quest.bookable_spells[current], 0)
+ inkey()
+ elseif dir == 2 then
+ current = current + 1
+ elseif dir == 8 then
+ current = current - 1
+ elseif dir == 6 then
+ if library_quest.book_contains_spell(library_quest.bookable_spells[current]) == FALSE then
+ library_quest.add_spell(library_quest.bookable_spells[current])
+ end
+ elseif dir == 4 then
+ library_quest.remove_spell(library_quest.bookable_spells[current])
+ end
+ total = getn(library_quest.bookable_spells)
+ if current > total then
+ current = total
+ elseif current < 1 then
+ current = 1
+ end
+
+ if current > (first + height - margin - 1) then
+ first = current - height + margin + 1
+ elseif first > current then
+ first = current
+ end
+ end
+
+ screen_load()
+end
+
+-- Quest data and hooks
+add_quest
+{
+ ["global"] = "LIBRARY_QUEST",
+ ["name"] = "Library quest",
+ ["desc"] = function()
+ -- Quest taken
+ if (quest(LIBRARY_QUEST).status == QUEST_STATUS_TAKEN) then
+ print_hook("#####yAn Old Mages Quest! (Danger Level: 35)\n")
+ print_hook("Make the library safe for the old mage in Minas Anor.\n")
+ print_hook("\n")
+ -- Quest done, book not gotten yet
+ elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then
+ print_hook("#####yAn Old Mages Quest!\n")
+ print_hook("You have made the library safe for the old mage in Minas Anor.\n")
+ print_hook("Perhaps you should see about a reward.\n")
+ print_hook("\n")
+ end
+ end,
+ ["level"] = 35,
+ ["data"] =
+ {
+ ["school_book[61][1]"] = -1,
+ ["school_book[61][2]"] = -1,
+ ["school_book[61][3]"] = -1
+ },
+ ["hooks"] =
+ {
+ -- Start the game without the quest, need to request it
+ [HOOK_BIRTH_OBJECTS] = function()
+ quest(LIBRARY_QUEST).status = QUEST_STATUS_UNTAKEN
+ school_book[61] = {-1, -1, -1}
+ end,
+
+ [HOOK_GEN_QUEST] = function()
+ -- Only if player doing this quest
+ if (player.inside_quest ~= LIBRARY_QUEST) then
+ return FALSE
+ end
+
+ load_map(library_quest.MAP, 2, 2)
+ level_flags2 = DF2_NO_GENO
+
+ -- generate the Liches 518
+ liches = damroll(4, 2) -- plus one on the map
+ while(liches > 0) do
+ if 0 < library_quest.place_random(4, 4, 14, 37, 518) then
+ liches = liches - 1
+ end
+ end
+
+ -- generate the Monastic liches 611
+ liches = damroll(1, 2)
+ while(liches > 0) do
+ if 0 < library_quest.place_random(14, 34, 37, 67, 611) then
+ liches = liches - 1
+ end
+ end
+
+ -- generate more Monastic liches 611
+ liches = damroll(1, 2) - 1
+ while(liches > 0) do
+ if 0 < library_quest.place_random(4, 34, 14, 67, 611) then
+ liches = liches - 1
+ end
+ end
+
+ -- generate even more Monastic liches 611
+ liches = damroll(1, 2) - 1
+ while(liches > 0) do
+ if 0 < library_quest.place_random(14, 4, 37, 34, 611) then
+ liches = liches - 1
+ end
+ end
+
+ -- Flesh golem 256
+ golems = 2
+ while(golems > 0) do
+ if 0 < library_quest.place_random(10, 10, 37, 67, 256) then
+ golems = golems - 1
+ end
+ end
+
+ -- Clay golem 261
+ golems = 2
+ while(golems > 0) do
+ if 0 < library_quest.place_random(10, 10, 37, 67, 261) then
+ golems = golems - 1
+ end
+ end
+
+ -- Iron golem 367
+ golems = 2
+ while(golems > 0) do
+ if 0 < library_quest.place_random(10, 10, 37, 67, 367) then
+ golems = golems - 1
+ end
+ end
+
+ -- Mithril Golem 464
+ golems = 1
+ while(golems > 0) do
+ if 0 < library_quest.place_random(10, 10, 37, 67, 464) then
+ golems = golems - 1
+ end
+ end
+
+ -- one Master lich is on the map
+
+ return TRUE
+ end,
+ [HOOK_STAIR] = function()
+ local ret
+
+ -- only ask this if player about to go up stairs of quest and hasn't won yet
+ if (player.inside_quest ~= LIBRARY_QUEST) or (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then
+ return FALSE
+ end
+
+ if cave(player.py, player.px).feat ~= FEAT_LESS then return end
+
+ -- flush all pending input
+ flush()
+
+ -- confirm
+ ret = get_check("Really abandon the quest?")
+
+ -- if yes, then
+ if ret == TRUE then
+ -- fail the quest
+ quest(LIBRARY_QUEST).status = QUEST_STATUS_FAILED
+ return FALSE
+ else
+ -- if no, they stay in the quest
+ return TRUE
+ end
+ end,
+ [HOOK_MONSTER_DEATH] = function()
+ -- if they're in the quest and haven't won, continue
+ if (player.inside_quest ~= LIBRARY_QUEST) or (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then
+ return FALSE
+ end
+
+ i = 1
+ count = -1
+ while i <= m_max do
+ local monster = m_list[i]
+ if (monster.r_idx > 0) and (monster.status <= MSTATUS_ENEMY) then
+ count = count + 1
+ end
+ i = i + 1
+ end
+
+ if count == 0 then
+ quest(LIBRARY_QUEST).status = QUEST_STATUS_COMPLETED
+ msg_print(TERM_YELLOW, "The library is safe now.")
+ end
+ end,
+ },
+}
+
+-- Library store action
+add_building_action
+{
+ ["index"] = 61,
+ ["action"] = function()
+ -- the quest hasn't been requested already, right?
+ if quest(LIBRARY_QUEST).status == QUEST_STATUS_UNTAKEN then
+ -- quest has been taken now
+ quest(LIBRARY_QUEST).status = QUEST_STATUS_TAKEN
+
+ -- issue instructions
+ msg_print("I need get some stock from my main library, but it is infested with monsters!")
+ msg_print("Please use the side entrance and vanquish the intruders for me.")
+
+ return TRUE, FALSE, TRUE
+ -- if quest completed
+ elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then
+ msg_print("Thank you! Let me make a special book for you.")
+ msg_print("Tell me three spells and I will write them in the book.")
+ library_quest.fill_book()
+ if library_quest.book_slots_left() == 0 then
+ quest(LIBRARY_QUEST).status = QUEST_STATUS_REWARDED
+ book = create_object(TV_BOOK, 61)
+ book.art_name = quark_add(player_name())
+ book.found = OBJ_FOUND_REWARD
+ set_aware(book)
+ set_known(book)
+ inven_carry(book, FALSE)
+ end
+
+ -- if the player asks for a quest when they already have it, but haven't failed it, give them some extra instructions
+ elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_TAKEN) then
+ msg_print("Please use the side entrance and vanquish the intruders for me.")
+
+ -- quest failed or completed, then give no more quests
+ elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_FAILED) or (quest(LIBRARY_QUEST).status == QUEST_STATUS_REWARDED) then
+ msg_print("I have no more quests for you.")
+ end
+ return TRUE
+ end,
+}
diff --git a/lib/scpt/mimic.lua b/lib/scpt/mimic.lua
new file mode 100644
index 00000000..be969ad5
--- /dev/null
+++ b/lib/scpt/mimic.lua
@@ -0,0 +1,385 @@
+-- Define the various possible mimic shapes
+
+-- Nature shapes
+add_mimic_shape
+{
+ ["name"] = "Mouse",
+ ["obj_name"] = "Mouse Fur",
+ ["desc"] = "Mice are small, fast and very stealthy",
+ ["realm"] = "nature",
+ ["level"] = 1,
+ ["rarity"] = 10,
+ ["duration"] = {20, 40},
+ ["calc"] = function ()
+ -- Mice run!
+ player.pspeed = player.pspeed + 5 + (player.mimic_level / 7)
+
+ -- They can crtawl under your armor to hit you ;)
+ player.to_h = player.to_h + 10 + (player.mimic_level / 5)
+ player.dis_to_h = player.dis_to_h + 10 + (player.mimic_level / 5)
+
+ -- But they are not very powerfull
+ player.to_d = player.to_d / 5
+ player.dis_to_d = player.dis_to_d / 5
+
+ -- But they are stealthy
+ player.skill_stl = player.skill_stl + 10 + (player.mimic_level / 5)
+
+ -- Stat mods
+ player.modify_stat(A_STR, -5)
+ player.modify_stat(A_DEX, 3)
+ player.modify_stat(A_CON, 1)
+
+ end,
+ ["power"] = function()
+ if player.mimic_level >= 30 then
+ player.add_power(POWER_INVISIBILITY)
+ end
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Eagle",
+ ["obj_name"] = "Feathers Cloak",
+ ["desc"] = "Eagles are master of the air, good hunters with excellent vision.",
+ ["realm"] = "nature",
+ ["level"] = 10,
+ ["rarity"] = 30,
+ ["duration"] = {10, 50},
+ ["calc"] = function ()
+ player.ffall = TRUE
+ player.pspeed = player.pspeed + 2 + (player.mimic_level / 6)
+
+ player.modify_stat(A_STR, -3)
+ player.modify_stat(A_DEX, 2 + (player.mimic_level / 15))
+ player.modify_stat(A_CON, 4 + (player.mimic_level / 20))
+ player.modify_stat(A_INT, -1)
+ player.modify_stat(A_WIS, 1)
+ player.modify_stat(A_CHR, -1)
+
+ if player.mimic_level >= 20 then player.fly = TRUE end
+ if player.mimic_level >= 20 then player.see_inv = TRUE end
+ if player.mimic_level >= 25 then player.free_act = TRUE end
+ if player.mimic_level >= 30 then player.resist_elec = TRUE end
+ if player.mimic_level >= 40 then player.sh_elec = TRUE end
+
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Wolf",
+ ["obj_name"] = "Wolf Pelt",
+ ["desc"] = "Wolves are masters of movement, strong and have excellent eyesight.",
+ ["realm"] = "nature",
+ ["level"] = 20,
+ ["rarity"] = 40,
+ ["duration"] = {10, 50},
+ ["calc"] = function ()
+ player.modify_stat(A_STR, 2 + (player.mimic_level / 20))
+ player.modify_stat(A_DEX, 3 + (player.mimic_level / 20))
+ player.modify_stat(A_INT, -3)
+ player.modify_stat(A_CHR, -2)
+
+ player.pspeed = player.pspeed + 10 + (player.mimic_level / 5)
+
+ player.free_act = TRUE
+ player.resist_fear = TRUE
+
+ if player.mimic_level >= 10 then player.resist_cold = TRUE end
+ if player.mimic_level >= 15 then player.see_inv = TRUE end
+ if player.mimic_level >= 30 then player.resist_dark = TRUE end
+ if player.mimic_level >= 35 then player.resist_conf = TRUE end
+
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Spider",
+ ["obj_name"] = "Spider Web",
+ ["desc"] = "Spiders are clever and become good climbers.",
+ ["realm"] = "nature",
+ ["level"] = 25,
+ ["rarity"] = 50,
+ ["duration"] = {10, 50},
+ ["calc"] = function ()
+ player.modify_stat(A_STR, -4)
+ player.modify_stat(A_DEX, 1 + (player.mimic_level / 8))
+ player.modify_stat(A_INT, 1 + (player.mimic_level / 5))
+ player.modify_stat(A_WIS, 1 + (player.mimic_level / 5))
+ player.modify_stat(A_CON, -5)
+ player.modify_stat(A_CHR, -10)
+
+ player.pspeed = player.pspeed + 5
+
+ player.resist_pois = TRUE
+ player.resist_fear = TRUE
+ player.resist_dark = TRUE
+
+ if player.mimic_level >= 40 then player.climb = TRUE end
+
+ end,
+ ["power"] = function()
+ if player.mimic_level >= 25 then
+ player.add_power(POWER_WEB)
+ end
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Elder Ent",
+ ["obj_name"] = "Entish Bark",
+ ["desc"] = "Ents are powerful tree-like beings dating from the dawn of time.",
+ ["realm"] = "nature",
+ ["level"] = 40,
+ ["rarity"] = 60,
+ ["duration"] = {10, 30},
+ ["limit"] = TRUE,
+ ["calc"] = function ()
+ player.pspeed = player.pspeed - 5 - (player.mimic_level / 10)
+
+ player.to_a = player.to_a + 10 + player.mimic_level
+ player.dis_to_a = player.dis_to_a + 10 + player.mimic_level
+
+ player.resist_pois = TRUE
+ player.resist_cold = TRUE
+ player.free_act = TRUE
+ player.regenerate = TRUE
+ player.see_inv = TRUE
+ player.sensible_fire = TRUE
+
+ player.modify_stat(A_STR, player.mimic_level / 5)
+ player.modify_stat(A_INT, - (player.mimic_level / 7))
+ player.modify_stat(A_WIS, - (player.mimic_level / 7))
+ player.modify_stat(A_DEX, -4)
+ player.modify_stat(A_CON, player.mimic_level / 5)
+ player.modify_stat(A_CHR, -7)
+
+ end,
+ ["power"] = function ()
+ player.add_power(PWR_GROW_TREE)
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Vapour",
+ ["obj_name"] = "Cloak of Mist",
+ ["desc"] = "A sentient cloud, darting around",
+ ["realm"] = "nature",
+ ["level"] = 15,
+ ["rarity"] = 10,
+ ["duration"] = {10, 40},
+ ["calc"] = function ()
+
+ player.pspeed = player.pspeed + 5
+
+ --Try to hit a cloud!
+ player.to_a = player.to_a + 40 + player.mimic_level
+ player.dis_to_a = player.dis_to_a + 40 + player.mimic_level
+
+ --Try to hit WITH a cloud!
+ player.to_h = player.to_h - 40
+ player.dis_to_h = player.dis_to_h -40
+
+ -- But they are stealthy
+ player.skill_stl = player.skill_stl + 10 + (player.mimic_level / 5)
+ player.resist_pois = TRUE
+ player.immune_cold = TRUE
+ player.resist_shard = TRUE
+ player.free_act = TRUE
+ player.regenerate = TRUE
+ player.see_inv = TRUE
+ player.sensible_fire = TRUE
+ player.levitate = TRUE
+
+ -- Stat mods
+ player.modify_stat(A_STR, -4)
+ player.modify_stat(A_DEX, 5)
+ player.modify_stat(A_CON, -4)
+ player.modify_stat(A_CHR, -10)
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Serpent",
+ ["obj_name"] = "Snakeskin Cloak",
+ ["desc"] = "Serpents are fast, lethal predators.",
+ ["realm"] = "nature",
+ ["level"] = 30,
+ ["rarity"] = 25,
+ ["duration"] = {15, 20},
+ ["calc"] = function ()
+ player.pspeed = player.pspeed + 10 + (player.mimic_level / 6)
+ player.to_a = player.to_a + 3 + (player.mimic_level / 8)
+ player.dis_to_a = player.dis_to_a + 3 + (player.mimic_level / 8)
+
+ player.modify_stat(A_STR, player.mimic_level / 8)
+ player.modify_stat(A_INT, -6)
+ player.modify_stat(A_WIS, -6)
+ player.modify_stat(A_DEX, -4)
+ player.modify_stat(A_CON, player.mimic_level / 7)
+ player.modify_stat(A_CHR, -6)
+
+ player.resist_pois = TRUE
+ if player.mimic_level >= 25 then player.free_act = TRUE end
+ end,
+}
+
+add_mimic_shape
+{
+ ["name"] = "Mumak",
+ ["obj_name"] = "Mumak Hide",
+ ["desc"] = "A giant, elaphantine form.",
+ ["realm"] = "nature",
+ ["level"] = 40,
+ ["rarity"] = 40,
+ ["duration"] = {15, 20},
+ ["calc"] = function ()
+ player.pspeed = player.pspeed - 5 - (player.mimic_level / 10)
+ player.to_a = player.to_a + 10 + (player.mimic_level / 6)
+ player.dis_to_a = player.dis_to_a + 10 + (player.mimic_level / 6)
+ player.to_d = player.to_d + 5 + ((player.mimic_level * 2) / 3)
+ player.dis_to_d = player.dis_to_d + 5 + ((player.mimic_level * 2) / 3)
+
+ player.modify_stat(A_STR, player.mimic_level / 4)
+ player.modify_stat(A_INT, -8)
+ player.modify_stat(A_WIS, -4)
+ player.modify_stat(A_DEX, -5)
+ player.modify_stat(A_CON, player.mimic_level / 3)
+ player.modify_stat(A_CHR, -10)
+
+ if player.mimic_level >= 10 then player.resist_fear = TRUE end
+ if player.mimic_level >= 25 then player.resist_conf = TRUE end
+ if player.mimic_level >= 30 then player.free_act = TRUE end
+ if player.mimic_level >= 35 then player.resist_nexus = TRUE end
+ end,
+}
+
+--------- Extra shapes -----------
+
+-- For Beornings
+add_mimic_shape
+{
+ ["name"] = "Bear",
+ ["desc"] = "A fierce, terrible bear.",
+ ["realm"] = nil,
+ ["level"] = 1,
+ ["rarity"] = 101,
+ ["duration"] = {50, 200},
+ ["limit"] = TRUE,
+ ["calc"] = function ()
+ player.pspeed = player.pspeed - 5 + (player.mimic_level / 5)
+
+ player.to_a = player.to_a + 5 + ((player.mimic_level * 2) / 3)
+ player.dis_to_a = player.dis_to_a + 5 + ((player.mimic_level * 2) / 3)
+
+ player.modify_stat(A_STR, player.mimic_level / 11)
+ player.modify_stat(A_INT, player.mimic_level / 11)
+ player.modify_stat(A_WIS, player.mimic_level / 11)
+ player.modify_stat(A_DEX, -1)
+ player.modify_stat(A_CON, player.mimic_level / 11)
+ player.modify_stat(A_CHR, -10)
+
+ if player.mimic_level >= 10 then player.free_act = TRUE end
+ if player.mimic_level >= 20 then player.regenerate = TRUE end
+ if player.mimic_level >= 30 then player.resist_conf = TRUE end
+ if player.mimic_level >= 35 then player.resist_nexus = TRUE end
+
+ -- activate the skill
+ skill(SKILL_BEAR).hidden = FALSE
+ end,
+}
+
+-- For balrog corruptions
+add_mimic_shape
+{
+ ["name"] = "Balrog",
+ ["desc"] = "A corrupted maia.",
+ ["realm"] = nil,
+ ["level"] = 1,
+ ["rarity"] = 101,
+ ["duration"] = {30, 70},
+ ["limit"] = TRUE,
+ ["calc"] = function ()
+ player.modify_stat(A_STR, 5 + player.mimic_level / 5)
+ player.modify_stat(A_INT, player.mimic_level / 10)
+ player.modify_stat(A_WIS, - ( 5 + player.mimic_level / 10))
+ player.modify_stat(A_DEX, player.mimic_level / 10)
+ player.modify_stat(A_CON, 5 + player.mimic_level / 5)
+ player.modify_stat(A_CHR, - ( 5 + player.mimic_level / 10))
+
+ player.immune_fire = TRUE
+ player.immune_elec = TRUE
+ player.immune_acid = TRUE
+ player.resist_pois = TRUE
+ player.resist_dark = TRUE
+ player.resist_chaos = TRUE
+ player.hold_life = TRUE
+ player.ffall = TRUE
+ player.regenerate = TRUE
+ player.sh_fire = TRUE
+ return 1
+ end,
+}
+
+-- For avatar spell
+add_mimic_shape
+{
+ ["name"] = "Maia",
+ ["desc"] = "A near god-like being.",
+ ["realm"] = nil,
+ ["level"] = 1,
+ ["rarity"] = 101,
+ ["duration"] = {30, 70},
+ ["limit"] = TRUE,
+ ["calc"] = function ()
+ player.modify_stat(A_STR, 5 + player.mimic_level / 5)
+ player.modify_stat(A_INT, 5 + player.mimic_level / 5)
+ player.modify_stat(A_WIS, 5 + player.mimic_level / 5)
+ player.modify_stat(A_DEX, 5 + player.mimic_level / 5)
+ player.modify_stat(A_CON, 5 + player.mimic_level / 5)
+ player.modify_stat(A_CHR, 5 + player.mimic_level / 5)
+
+ player.immune_fire = TRUE
+ player.immune_elec = TRUE
+ player.immune_acid = TRUE
+ player.immune_cold = TRUE
+ player.resist_pois = TRUE
+ player.resist_lite = TRUE
+ player.resist_dark = TRUE
+ player.resist_chaos = TRUE
+ player.hold_life = TRUE
+ player.ffall = TRUE
+ player.regenerate = TRUE
+ return 2
+ end,
+}
+
+-- For Geomancy
+add_mimic_shape
+{
+ ["name"] = "Fire Elem.",
+ ["desc"] = "A towering column of flames",
+ ["realm"] = nil,
+ ["level"] = 1,
+ ["rarity"] = 101,
+ ["duration"] = {10, 10},
+ ["limit"] = TRUE,
+ ["calc"] = function ()
+ player.modify_stat(A_STR, 5 + (player.mimic_level / 5))
+ player.modify_stat(A_DEX, 5 + (player.mimic_level / 5))
+ player.modify_stat(A_WIS, -5 - (player.mimic_level / 5))
+
+ player.immune_fire = TRUE
+ -- was immune to poison in the 3.0.0 version
+ player.resist_pois = TRUE
+ player.sh_fire = TRUE
+ player.lite = TRUE
+ return 0
+ end,
+}
diff --git a/lib/scpt/mkeys.lua b/lib/scpt/mkeys.lua
new file mode 100644
index 00000000..15e23546
--- /dev/null
+++ b/lib/scpt/mkeys.lua
@@ -0,0 +1,95 @@
+-- Mkeys for skills & abilities
+
+GF_INSTA_DEATH = add_spell_type
+{
+ ["color"] = { TERM_DARK, 0 },
+ ["angry"] = function() return TRUE, TRUE end,
+ ["monster"] = function(who, dam, rad, y, x, monst)
+ local race = race_info_idx(monst.r_idx, monst.ego)
+ if magik(5) == FALSE or band(race.flags1, RF1_UNIQUE) ~= FALSE or band(race.flags3, RF3_UNDEAD) ~= FALSE or band(race.flags3, RF3_NONLIVING) ~= FALSE then
+ return TRUE, FALSE
+ else
+ -- Reduce the exp gained this way
+ monst.level = monst.level / 3
+ return TRUE, FALSE, 32535, 0, 0, 0, 0, 0, 0, 0, " faints.", " is sucked out of life."
+ end
+ end,
+}
+
+-- Death touch ability
+add_mkey
+{
+ ["mkey"] = 100,
+ ["fct"] = function()
+ if player.csp > 40 then
+ increase_mana(-40)
+ set_project(randint(30) + 10, GF_INSTA_DEATH, 1, 0, bor(PROJECT_STOP, PROJECT_KILL))
+ energy_use = 100
+ else
+ msg_print("You need at least 40 mana.")
+ end
+ end,
+}
+
+
+-- Geomancy skill
+add_mkey
+{
+ ["mkey"] = 101,
+ ["fct"] = function()
+ local s
+
+ -- No magic
+ if (player.antimagic > 0) then
+ msg_print("Your anti-magic field disrupts any magic attempts.")
+ return
+ end
+
+ local obj = get_object(INVEN_WIELD)
+ if (obj.k_idx <= 0) or (obj.tval ~= TV_MSTAFF) then
+ msg_print('You must wield a magestaff to use Geomancy.')
+ return
+ end
+
+ s = get_school_spell("cast", "is_ok_spell", 62);
+
+ -- Actualy cast the choice
+ if (s ~= -1) then
+ cast_school_spell(s, spell(s))
+ end
+ end,
+}
+
+-- Far reaching attack of polearms
+add_mkey
+{
+ ["mkey"] = 102,
+ ["fct"] = function()
+ local weapon = get_object(INVEN_WIELD);
+ if weapon.tval == TV_POLEARM and (weapon.sval == SV_HALBERD or weapon.sval == SV_PIKE or weapon.sval == SV_HEAVY_LANCE or weapon.sval == SV_LANCE) then
+ else
+ msg_print("You will need a long polearm for this!")
+ return
+ end
+
+ ret, dir = get_rep_dir()
+ if ret == FALSE then return end
+
+ local dy, dx = explode_dir(dir)
+ dy = dy * 2
+ dx = dx * 2
+ targety = player.py + dy
+ targetx = player.px + dx
+
+ local max_blows = get_skill_scale(SKILL_POLEARM, player.num_blow / 2)
+ if max_blows == 0 then max_blows = 1 end
+
+ if get_skill(SKILL_POLEARM) >= 40 then
+ energy_use = energy_use + 200
+ return project(0, 0, targety, targetx, max_blows, GF_ATTACK, bor(PROJECT_BEAM, PROJECT_KILL))
+ else
+ energy_use = energy_use + 200
+ return project(0, 0, targety, targetx, max_blows, GF_ATTACK, bor(PROJECT_BEAM, PROJECT_STOP, PROJECT_KILL))
+ end
+ end,
+}
diff --git a/lib/scpt/player.lua b/lib/scpt/player.lua
new file mode 100644
index 00000000..2a617608
--- /dev/null
+++ b/lib/scpt/player.lua
@@ -0,0 +1,76 @@
+------------------------------------------------------------------------------
+----------------------- Hook to create birth objects -------------------------
+------------------------------------------------------------------------------
+function __birth_hook_objects()
+ -- Provide a book of blink to rangers
+ if get_class_name() == "Ranger" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("Phase Door")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+
+ -- Provide a book of Geyser to Geomancers
+ if get_class_name() == "Geomancer" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("Geyser")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+
+ -- Provide a book of prayer to priests
+ if get_class_name() == "Priest(Eru)" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("See the Music")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+ if get_class_name() == "Priest(Manwe)" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("Manwe's Blessing")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+ if get_class_name() == "Druid" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("Charm Animal")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+ if get_class_name() == "Dark-Priest" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("Curse")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+ if get_class_name() == "Paladin" then
+ local obj = create_object(TV_BOOK, 255);
+ obj.pval = find_spell("Divine Aim")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+ if get_class_name() == "Mimic" then
+ local obj = create_object(TV_CLOAK, 100);
+ obj.pval2 = resolve_mimic_name("Mouse")
+ obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN)
+ inven_carry(obj, FALSE)
+ end_object(obj)
+ end
+
+ -- Start the undeads, as undeads with the corruptions
+ if get_subrace_name() == "Vampire" then
+ player.corruption(CORRUPT_VAMPIRE_TEETH, TRUE)
+ player.corruption(CORRUPT_VAMPIRE_STRENGTH, TRUE)
+ player.corruption(CORRUPT_VAMPIRE_VAMPIRE, TRUE)
+ end
+end
+
+-- Register in the hook list
+add_hook_script(HOOK_BIRTH_OBJECTS, "__birth_hook_objects", "__birth_hook_objects")
diff --git a/lib/scpt/powers.lua b/lib/scpt/powers.lua
new file mode 100644
index 00000000..ff4c62c5
--- /dev/null
+++ b/lib/scpt/powers.lua
@@ -0,0 +1,61 @@
+-- Various 'U' powers
+
+-- Invisibility power, for the mouse mimic shape
+POWER_INVISIBILITY = add_power
+{
+ ["name"] = "invisibility",
+ ["desc"] = "You are able melt into the shadows to become invisible.",
+ ["desc_get"] = "You suddenly become able to melt into the shadows.",
+ ["desc_lose"] = "You lose your shadow-melting ability.",
+ ["level"] = 30,
+ ["cost"] = 10,
+ ["stat"] = A_DEX,
+ ["fail"] = 20,
+ ["power"] = function()
+ set_invis(20 + randint(30), 30)
+ end,
+}
+
+-- Web power, for the spider mimic shape
+POWER_WEB = add_power
+{
+ ["name"] = "web",
+ ["desc"] = "You are able throw a thick and very resistant spider web.",
+ ["desc_get"] = "You suddenly become able to weave webs.",
+ ["desc_lose"] = "You lose your web-weaving capability.",
+ ["level"] = 25,
+ ["cost"] = 30,
+ ["stat"] = A_DEX,
+ ["fail"] = 20,
+ ["power"] = function()
+ -- Warning, beware of f_info changes .. I hate to do that ..
+ grow_things(16, 1 + (player.lev / 10))
+ end,
+}
+
+-- Activating/stopping space-continuum
+-- When stopped it will induce constant mana loss
+player.corrupt_anti_teleport_stopped = FALSE
+add_loadsave("player.corrupt_anti_teleport_stopped", FALSE)
+POWER_COR_SPACE_TIME = add_power
+{
+ ["name"] = "control space/time continuum",
+ ["desc"] = "You are able to control the space/time continuum.",
+ ["desc_get"] = "You become able to control the space/time continuum.",
+ ["desc_lose"] = "You are no more able to control the space/time continuum.",
+ ["level"] = 1,
+ ["cost"] = 10,
+ ["stat"] = A_WIS,
+ ["fail"] = 10,
+ ["power"] = function()
+ if player.corrupt_anti_teleport_stopped == TRUE then
+ player.corrupt_anti_teleport_stopped = FALSE
+ msg_print("You stop controlling your corruption.")
+ player.update = bor(player.update, PU_BONUS)
+ else
+ player.corrupt_anti_teleport_stopped = TRUE
+ msg_print("You start controlling your corruption, teleportation works once more.")
+ player.update = bor(player.update, PU_BONUS)
+ end
+ end,
+}
diff --git a/lib/scpt/s_air.lua b/lib/scpt/s_air.lua
new file mode 100644
index 00000000..afd1f584
--- /dev/null
+++ b/lib/scpt/s_air.lua
@@ -0,0 +1,193 @@
+-- handle the air school
+
+NOXIOUSCLOUD = add_spell
+{
+ ["name"] = "Noxious Cloud",
+ ["school"] = {SCHOOL_AIR},
+ ["level"] = 3,
+ ["mana"] = 3,
+ ["mana_max"] = 30,
+ ["fail"] = 20,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 7 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 15,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 25, 50 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir, type
+
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ if get_level(NOXIOUSCLOUD, 50) >= 30 then type = GF_UNBREATH
+ else type = GF_POIS end
+ fire_cloud(type, dir, 7 + get_level(NOXIOUSCLOUD, 150), 3, 5 + get_level(NOXIOUSCLOUD, 40))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dam "..(7 + get_level(NOXIOUSCLOUD, 150)).." rad 3 dur "..(5 + get_level(NOXIOUSCLOUD, 40))
+ end,
+ ["desc"] = {
+ "Creates a cloud of poison",
+ "The cloud will persist for some turns, damaging all monsters passing by",
+ "At spell level 30 it turns into a thick gas attacking all living beings"
+ }
+}
+
+AIRWINGS = add_spell
+{
+ ["name"] = "Wings of Winds",
+ ["school"] = {SCHOOL_AIR, SCHOOL_CONVEYANCE},
+ ["level"] = 22,
+ ["mana"] = 30,
+ ["mana_max"] = 40,
+ ["fail"] = 60,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 5 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 27,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["inertia"] = { 1, 10 },
+ ["spell"] = function()
+ if get_level(AIRWINGS, 50) >= 16 then
+ if player.tim_fly == 0 then return set_tim_fly(randint(10) + 5 + get_level(AIRWINGS, 25)) end
+ else
+ if player.tim_ffall == 0 then return set_tim_ffall(randint(10) + 5 + get_level(AIRWINGS, 25)) end
+ end
+ return FALSE
+ end,
+ ["info"] = function()
+ return "dur "..(5 + get_level(AIRWINGS, 25)).."+d10"
+ end,
+ ["desc"] = {
+ "Grants the power of levitation",
+ "At level 16 it grants the power of controlled flight"
+ }
+}
+
+INVISIBILITY = add_spell
+{
+ ["name"] = "Invisibility",
+ ["school"] = {SCHOOL_AIR},
+ ["level"] = 16,
+ ["mana"] = 10,
+ ["mana_max"] = 20,
+ ["fail"] = 50,
+ ["inertia"] = { 1, 30 },
+ ["spell"] = function()
+ if player.tim_invisible == 0 then return set_invis(randint(20) + 15 + get_level(INVISIBILITY, 50), 20 + get_level(INVISIBILITY, 50)) end
+ end,
+ ["info"] = function()
+ return "dur "..(15 + get_level(INVISIBILITY, 50)).."+d20 power "..(20 + get_level(INVISIBILITY, 50))
+ end,
+ ["desc"] = {
+ "Grants invisibility"
+ }
+}
+
+POISONBLOOD = add_spell
+{
+ ["name"] = "Poison Blood",
+ ["school"] = {SCHOOL_AIR},
+ ["level"] = 12,
+ ["mana"] = 10,
+ ["mana_max"] = 20,
+ ["fail"] = 30,
+ ["stick"] =
+ {
+ ["charge"] = { 10, 15 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 45,
+ ["base_level"] = { 1, 25 },
+ ["max_level"] = { 35, 50 },
+ },
+ },
+ ["inertia"] = { 1, 35 },
+ ["spell"] = function()
+ local obvious = nil
+ if player.oppose_pois == 0 then obvious = set_oppose_pois(randint(30) + 25 + get_level(POISONBLOOD, 25)) end
+ if (player.tim_poison == 0) and (get_level(POISONBLOOD, 50) >= 15) then obvious = is_obvious(set_poison(randint(30) + 25 + get_level(POISONBLOOD, 25)), obvious) end
+ return obvious
+ end,
+ ["info"] = function()
+ return "dur "..(25 + get_level(POISONBLOOD, 25)).."+d30"
+ end,
+ ["desc"] = {
+ "Grants resist poison",
+ "At level 15 it provides poison branding to wielded weapon"
+ }
+}
+
+THUNDERSTORM = add_spell
+{
+ ["name"] = "Thunderstorm",
+ ["school"] = {SCHOOL_AIR, SCHOOL_NATURE},
+ ["level"] = 25,
+ ["mana"] = 40,
+ ["mana_max"] = 60,
+ ["fail"] = 60,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 85,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 25, 50 },
+ },
+ },
+ ["inertia"] = { 2, 15 },
+ ["spell"] = function()
+ if player.tim_thunder == 0 then return set_tim_thunder(randint(10) + 10 + get_level(THUNDERSTORM, 25), 5 + get_level(THUNDERSTORM, 10), 10 + get_level(THUNDERSTORM, 25)) end
+ return FALSE
+ end,
+ ["info"] = function()
+ return "dam "..(5 + get_level(THUNDERSTORM, 10)).."d"..(10 + get_level(THUNDERSTORM, 25)).." dur "..(10 + get_level(THUNDERSTORM, 25)).."+d10"
+ end,
+ ["desc"] = {
+ "Charges up the air around you with electricity",
+ "Each turn it will throw a thunder bolt at a random monster in sight",
+ "The thunder does 3 types of damage, one third of lightning",
+ "one third of sound and one third of light"
+ }
+}
+
+STERILIZE = add_spell
+{
+ ["name"] = "Sterilize",
+ ["school"] = {SCHOOL_AIR},
+ ["level"] = 20,
+ ["mana"] = 10,
+ ["mana_max"] = 100,
+ ["fail"] = 50,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 5 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 20,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["spell"] = function()
+ set_no_breeders((30) + 20 + get_level(STERILIZE, 70))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dur "..(20 + get_level(STERILIZE, 70)).."+d30"
+ end,
+ ["desc"] = {
+ "Prevents explosive breeding for a while."
+ }
+}
diff --git a/lib/scpt/s_convey.lua b/lib/scpt/s_convey.lua
new file mode 100644
index 00000000..e7856c43
--- /dev/null
+++ b/lib/scpt/s_convey.lua
@@ -0,0 +1,227 @@
+-- handle the conveyance school
+
+BLINK = add_spell
+{
+ ["name"] = "Phase Door",
+ ["school"] = {SCHOOL_CONVEYANCE},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 3,
+ ["fail"] = 10,
+ ["inertia"] = { 1, 5 },
+ ["spell"] = function()
+ if get_level(BLINK, 50) >= 30 then
+ local oy, ox = player.py, player.px
+
+ teleport_player(10 + get_level(BLINK, 8))
+ create_between_gate(0, oy, ox)
+ return TRUE
+ else
+ teleport_player(10 + get_level(BLINK, 8))
+ return TRUE
+ end
+ end,
+ ["info"] = function()
+ return "distance "..(10 + get_level(BLINK, 8))
+ end,
+ ["desc"] = {
+ "Teleports you on a small scale range",
+ "At level 30 it creates void jumpgates",
+ }
+}
+
+DISARM = add_spell
+{
+ ["name"] = "Disarm",
+ ["school"] = {SCHOOL_CONVEYANCE},
+ ["level"] = 3,
+ ["mana"] = 2,
+ ["mana_max"] = 4,
+ ["fail"] = 15,
+ ["stick"] =
+ {
+ ["charge"] = { 10, 15 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 4,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 10, 50 },
+ },
+ },
+ ["spell"] = function()
+ local obvious
+ obvious = destroy_doors_touch()
+ if get_level(DISARM, 50) >= 10 then obvious = is_obvious(destroy_traps_touch(), obvious) end
+ return obvious
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Destroys doors and traps",
+ "At level 10 it destroys doors and traps, then reveals and unlocks any secret",
+ "doors"
+ }
+}
+
+TELEPORT = add_spell
+{
+ ["name"] = "Teleportation",
+ ["school"] = {SCHOOL_CONVEYANCE},
+ ["level"] = 10,
+ ["mana"] = 8,
+ ["mana_max"] = 14,
+ ["fail"] = 30,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 7 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 50,
+ ["base_level"] = { 1, 20 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["inertia"] = { 1, 10 },
+ ["spell"] = function()
+ player.energy = player.energy - (25 - get_level(TELEPORT, 50))
+ teleport_player(100 + get_level(TELEPORT, 100))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "distance "..(100 + get_level(TELEPORT, 100))
+ end,
+ ["desc"] = {
+ "Teleports you around the level. The casting time decreases with level",
+ }
+}
+
+TELEAWAY = add_spell
+{
+ ["name"] = "Teleport Away",
+ ["school"] = {SCHOOL_CONVEYANCE},
+ ["level"] = 23,
+ ["mana"] = 15,
+ ["mana_max"] = 40,
+ ["fail"] = 60,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 75,
+ ["base_level"] = { 1, 20 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir
+
+ if get_level(TELEAWAY, 50) >= 20 then
+ return project_los(GF_AWAY_ALL, 100)
+ elseif get_level(TELEAWAY, 50) >= 10 then
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_ball(GF_AWAY_ALL, dir, 100, 3 + get_level(TELEAWAY, 4))
+ else
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return teleport_monster(dir)
+ end
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Teleports a line of monsters away",
+ "At level 10 it turns into a ball",
+ "At level 20 it teleports all monsters in sight"
+ }
+}
+
+RECALL = add_spell
+{
+ ["name"] = "Recall",
+ ["school"] = {SCHOOL_CONVEYANCE},
+ ["level"] = 30,
+ ["mana"] = 25,
+ ["mana_max"] = 25,
+ ["fail"] = 60,
+ ["spell"] = function()
+ local ret, x, y, c_ptr
+ ret, x, y = tgt_pt()
+ if ret == FALSE then return end
+ c_ptr = cave(y, x)
+ if (y == player.py) and (x == player.px) then
+ local d = 21 - get_level(RECALL, 15)
+ if d < 0 then
+ d = 0
+ end
+ local f = 15 - get_level(RECALL, 10)
+ if f < 1 then
+ f = 1
+ end
+ recall_player(d, f)
+ return TRUE
+ elseif c_ptr.m_idx > 0 then
+ swap_position(y, x)
+ return TRUE
+ elseif c_ptr.o_idx > 0 then
+ set_target(y, x)
+ if get_level(RECALL, 50) >= 15 then
+ fetch(5, 10 + get_level(RECALL, 150), FALSE)
+ else
+ fetch(5, 10 + get_level(RECALL, 150), TRUE)
+ end
+ return TRUE
+ end
+ end,
+ ["info"] = function()
+ local d = 21 - get_level(RECALL, 15)
+ if d < 0 then
+ d = 0
+ end
+ local f = 15 - get_level(RECALL, 10)
+ if f < 1 then
+ f = 1
+ end
+ return "dur "..f.."+d"..d.." weight "..(1 + get_level(RECALL, 15)).."lb"
+ end,
+ ["desc"] = {
+ "Cast on yourself it will recall you to the surface/dungeon.",
+ "Cast at a monster you will swap positions with the monster.",
+ "Cast at an object it will fetch the object to you."
+ }
+}
+
+PROBABILITY_TRAVEL = add_spell
+{
+ ["name"] = "Probability Travel",
+ ["school"] = {SCHOOL_CONVEYANCE},
+ ["level"] = 35,
+ ["mana"] = 30,
+ ["mana_max"] = 50,
+ ["fail"] = 90,
+ ["stick"] =
+ {
+ ["charge"] = { 1, 2 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 97,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 8, 25 },
+ },
+ },
+ ["inertia"] = { 6, 40 },
+ ["spell"] = function()
+ return set_prob_travel(randint(20) + get_level(PROBABILITY_TRAVEL, 60))
+ end,
+ ["info"] = function()
+ return "dur "..get_level(PROBABILITY_TRAVEL, 60).."+d20"
+ end,
+ ["desc"] = {
+ "Renders you immaterial, when you hit a wall you travel through it and",
+ "instantly appear on the other side of it. You can also float up and down",
+ "at will"
+ }
+}
diff --git a/lib/scpt/s_demon.lua b/lib/scpt/s_demon.lua
new file mode 100644
index 00000000..ada97310
--- /dev/null
+++ b/lib/scpt/s_demon.lua
@@ -0,0 +1,337 @@
+-- handle the demonology school
+
+-- Demonblade
+DEMON_BLADE = add_spell
+{
+ ["name"] = "Demon Blade",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 1,
+ ["mana"] = 4,
+ ["mana_max"] = 44,
+ ["fail"] = 10,
+ ["random"] = 0,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 7 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 75,
+ ["base_level"] = { 1, 17 },
+ ["max_level"] = { 20, 40 },
+ },
+ },
+ ["spell"] = function()
+ local type, rad
+
+ type = GF_FIRE
+ if get_level(DEMON_BLADE) >= 30 then type = GF_HELL_FIRE end
+
+ rad = 0
+ if get_level(DEMON_BLADE) >= 45 then rad = 1 end
+
+ return set_project(randint(20) + get_level(DEMON_BLADE, 80),
+ type,
+ 4 + get_level(DEMON_BLADE, 40),
+ rad,
+ bor(PROJECT_STOP, PROJECT_KILL))
+ end,
+ ["info"] = function()
+ return "dur "..(get_level(DEMON_BLADE, 80)).."+d20 dam "..(4 + get_level(DEMON_BLADE, 40)).."/blow"
+ end,
+ ["desc"] = {
+ "Imbues your blade with fire to deal more damage",
+ "At level 30 it deals hellfire damage",
+ "At level 45 it spreads over a 1 radius zone around your target",
+ }
+}
+
+DEMON_MADNESS = add_spell
+{
+ ["name"] = "Demon Madness",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 10,
+ ["mana"] = 5,
+ ["mana_max"] = 20,
+ ["fail"] = 25,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, dir, type, y1, x1, y2, x2
+
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ type = GF_CHAOS
+ if magik(33) == TRUE then type = GF_CONFUSION end
+ if magik(33) == TRUE then type = GF_CHARM end
+
+ -- Calc the coordinates of arrival
+ y1, x1 = get_target(dir)
+ y2 = player.py - (y1 - player.py)
+ x2 = player.px - (x1 - player.px)
+
+ local obvious = nil
+ obvious = project(0, 1 + get_level(DEMON_MADNESS, 4, 0),
+ y1, x1,
+ 20 + get_level(DEMON_MADNESS, 200),
+ type, bor(PROJECT_STOP, PROJECT_GRID, PROJECT_ITEM, PROJECT_KILL))
+ obvious = is_obvious(project(0, 1 + get_level(DEMON_MADNESS, 4, 0),
+ y2, x2,
+ 20 + get_level(DEMON_MADNESS, 200),
+ type, bor(PROJECT_STOP, PROJECT_GRID, PROJECT_ITEM, PROJECT_KILL)), obvious)
+ return obvious
+ end,
+ ["info"] = function()
+ return "dam "..(20 + get_level(DEMON_MADNESS, 200)).." rad "..(1 + get_level(DEMON_MADNESS, 4, 0))
+ end,
+ ["desc"] = {
+ "Fire 2 balls in opposite directions of randomly chaos, confusion or charm",
+ }
+}
+
+DEMON_FIELD = add_spell
+{
+ ["name"] = "Demon Field",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 20,
+ ["mana"] = 20,
+ ["mana_max"] = 60,
+ ["fail"] = 60,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, dir
+
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_cloud(GF_NEXUS, dir, 20 + get_level(DEMON_FIELD, 70), 7, 30 + get_level(DEMON_FIELD, 100))
+ end,
+ ["info"] = function()
+ return "dam "..(20 + get_level(DEMON_FIELD, 70)).." dur "..(30 + get_level(DEMON_FIELD, 100))
+ end,
+ ["desc"] = {
+ "Fires a cloud of deadly nexus over a radius of 7",
+ }
+}
+
+-- Demonshield
+
+DOOM_SHIELD = add_spell
+{
+ ["name"] = "Doom Shield",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 1,
+ ["mana"] = 2,
+ ["mana_max"] = 30,
+ ["fail"] = 10,
+ ["random"] = 0,
+ ["spell"] = function()
+ return set_shield(randint(10) + 20 + get_level(DOOM_SHIELD, 100), -300 + get_level(DOOM_SHIELD, 100), SHIELD_COUNTER, 1 + get_level(DOOM_SHIELD, 14), 10 + get_level(DOOM_SHIELD, 15))
+ end,
+ ["info"] = function()
+ return "dur "..(20 + get_level(DOOM_SHIELD, 100)).."+d10 dam "..(1 + get_level(DOOM_SHIELD, 14)).."d"..(10 + get_level(DOOM_SHIELD, 15))
+ end,
+ ["desc"] = {
+ "Raises a mirror of pain around you, doing very high damage to your foes",
+ "that dare hit you, but greatly reduces your armour class",
+ }
+}
+
+UNHOLY_WORD = add_spell
+{
+ ["name"] = "Unholy Word",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 25,
+ ["mana"] = 15,
+ ["mana_max"] = 45,
+ ["fail"] = 55,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, x, y, c_ptr
+ ret, x, y = tgt_pt()
+ if ret == FALSE then return end
+ c_ptr = cave(y, x)
+
+ -- ok that is a monster
+ if c_ptr.m_idx > 0 then
+ local m_ptr = monster(c_ptr.m_idx)
+ if m_ptr.status ~= MSTATUS_PET then
+ msg_print("You can only target a pet.")
+ return
+ end
+
+ -- Oups he is angry now
+ if magik(30 - get_level(UNHOLY_WORD, 25, 0)) == TRUE then
+ local m_name = monster_desc(m_ptr, 0).." turns against you."
+ msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2))
+ else
+ local m_name = monster_desc(m_ptr, 0)
+ msg_print("You consume "..m_name..".")
+
+ local heal = (m_ptr.hp * 100) / m_ptr.maxhp
+ heal = ((30 + get_level(UNHOLY_WORD, 50, 0)) * heal) / 100
+
+ hp_player(heal)
+
+ delete_monster_idx(c_ptr.m_idx)
+ end
+ return TRUE
+ end
+ end,
+ ["info"] = function()
+ return "heal mhp% of "..(30 + get_level(UNHOLY_WORD, 50, 0)).."%"
+ end,
+ ["desc"] = {
+ "Kills a pet to heal you",
+ "There is a chance that the pet won't die but will turn against you",
+ "it will decrease with higher level",
+ }
+}
+
+DEMON_CLOAK = add_spell
+{
+ ["name"] = "Demon Cloak",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 20,
+ ["mana"] = 10,
+ ["mana_max"] = 40,
+ ["fail"] = 70,
+ ["random"] = 0,
+ ["spell"] = function()
+ return set_tim_reflect(randint(5) + 5 + get_level(DEMON_CLOAK, 15, 0))
+ end,
+ ["info"] = function()
+ return "dur "..(5 + get_level(DEMON_CLOAK, 15, 0)).."+d5"
+ end,
+ ["desc"] = {
+ "Raises a mirror that can reflect bolts and arrows for a time",
+ }
+}
+
+
+-- Demonhorn
+DEMON_SUMMON = add_spell
+{
+ ["name"] = "Summon Demon",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 5,
+ ["mana"] = 10,
+ ["mana_max"] = 50,
+ ["fail"] = 30,
+ ["random"] = 0,
+ ["spell"] = function()
+ local type = SUMMON_DEMON
+ local level = dun_level
+ local minlevel = 4
+ if level < minlevel then level=minlevel end
+ summon_specific_level = 5 + get_level(DEMON_SUMMON, 100)
+ if get_level(DEMON_SUMMON) >= 35 then type = SUMMON_HI_DEMON end
+ if summon_monster(player.py, player.px, level, TRUE, type) == TRUE then
+ return TRUE
+ else
+ msg_print("Something blocks your summoning!")
+ return FALSE
+ end
+ end,
+ ["info"] = function()
+ return "level "..(5 + get_level(DEMON_SUMMON, 100))
+ end,
+ ["desc"] = {
+ "Summons a leveled demon to your side",
+ "At level 35 it summons a high demon",
+ }
+}
+
+DISCHARGE_MINION = add_spell
+{
+ ["name"] = "Discharge Minion",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 10,
+ ["mana"] = 20,
+ ["mana_max"] = 50,
+ ["fail"] = 30,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, x, y, c_ptr
+ ret, x, y = tgt_pt()
+ if ret == FALSE then return end
+ c_ptr = cave(y, x)
+
+ -- ok that is a monster
+ if c_ptr.m_idx > 0 then
+ local m_ptr = monster(c_ptr.m_idx)
+ if m_ptr.status ~= MSTATUS_PET then
+ msg_print("You can only target a pet.")
+ return
+ end
+
+ local dam = m_ptr.hp
+ delete_monster_idx(c_ptr.m_idx)
+ dam = (dam * (20 + get_level(DISCHARGE_MINION, 60, 0))) / 100
+ if dam > 100 + get_level(DISCHARGE_MINION, 500, 0) then
+ dam = 100 + get_level(DISCHARGE_MINION, 500, 0)
+ end
+
+ -- We use project instead of fire_ball because we must tell it exactly where to land
+ return project(0, 2,
+ y, x,
+ dam,
+ GF_GRAVITY, bor(PROJECT_STOP, PROJECT_GRID, PROJECT_ITEM, PROJECT_KILL))
+ end
+ end,
+ ["info"] = function()
+ return "dam "..(20 + get_level(DISCHARGE_MINION, 60, 0)).."% max "..(100 + get_level(DISCHARGE_MINION, 500, 0))
+ end,
+ ["desc"] = {
+ "The targeted pet will explode in a burst of gravity",
+ }
+}
+
+CONTROL_DEMON = add_spell
+{
+ ["name"] = "Control Demon",
+ ["school"] = {SCHOOL_DEMON},
+ ["level"] = 25,
+ ["mana"] = 30,
+ ["mana_max"] = 70,
+ ["fail"] = 55,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ return fire_ball(GF_CONTROL_DEMON, dir, 50 + get_level(CONTROL_DEMON, 250), 0)
+ end,
+ ["info"] = function()
+ return "power "..(50 + get_level(CONTROL_DEMON, 250))
+ end,
+ ["desc"] = {
+ "Attempts to control a demon",
+ }
+}
+
+-- ok we need to have different wield slots
+add_hooks
+{
+ [HOOK_WIELD_SLOT] = function (obj, ideal)
+ if (obj.tval == TV_DAEMON_BOOK) then
+ local slot
+ if (obj.sval == SV_DEMONBLADE) then
+ if(ideal == TRUE) then
+ slot = INVEN_WIELD
+ else
+ slot = get_slot(INVEN_WIELD)
+ end
+ elseif (obj.sval == SV_DEMONSHIELD) then
+ if(ideal == TRUE) then
+ slot = INVEN_ARM
+ else
+ slot = get_slot(INVEN_ARM)
+ end
+ elseif (obj.sval == SV_DEMONHORN) then
+ if(ideal == TRUE) then
+ slot = INVEN_HEAD
+ else
+ slot = get_slot(INVEN_HEAD)
+ end
+ end
+ return TRUE, slot
+ end
+ end,
+}
diff --git a/lib/scpt/s_divin.lua b/lib/scpt/s_divin.lua
new file mode 100644
index 00000000..60b0275f
--- /dev/null
+++ b/lib/scpt/s_divin.lua
@@ -0,0 +1,230 @@
+-- Handles thhe divination school
+
+
+STARIDENTIFY = add_spell
+{
+ ["name"] = "Greater Identify",
+ ["school"] = {SCHOOL_DIVINATION},
+ ["level"] = 35,
+ ["mana"] = 30,
+ ["mana_max"] = 30,
+ ["fail"] = 80,
+ ["spell"] = function()
+ if get_check("Cast on yourself?") == TRUE then
+ self_knowledge()
+ else
+ identify_fully()
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Asks for an object and fully identify it, providing the full list of powers",
+ "Cast at yourself it will reveal your powers"
+ }
+}
+
+IDENTIFY = add_spell
+{
+ ["name"] = "Identify",
+ ["school"] = {SCHOOL_DIVINATION},
+ ["level"] = 8,
+ ["mana"] = 10,
+ ["mana_max"] = 50,
+ ["fail"] = 40,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 10 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 45,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 15, 40 },
+ },
+ },
+ ["spell"] = function()
+ if get_level(IDENTIFY, 50) >= 27 then
+ local obvious
+ obvious = identify_pack()
+ obvious = is_obvious(fire_ball(GF_IDENTIFY, 0, 1, get_level(IDENTIFY, 3)), obvious)
+ if obvious == TRUE then
+ player.notice = bor(player.notice, PN_COMBINE, PN_REORDER)
+ end
+ return obvious
+ elseif get_level(IDENTIFY, 50) >= 17 then
+ local obvious
+ obvious = identify_pack()
+ obvious = is_obvious(fire_ball(GF_IDENTIFY, 0, 1, 0), obvious)
+ if obvious == TRUE then
+ player.notice = bor(player.notice, PN_COMBINE, PN_REORDER)
+ end
+ return obvious
+ else
+ if ident_spell() == TRUE then return TRUE else return end
+ end
+ end,
+ ["info"] = function()
+ if get_level(IDENTIFY, 50) >= 27 then
+ return "rad "..(get_level(IDENTIFY, 3))
+ else
+ return ""
+ end
+ end,
+ ["desc"] = {
+ "Asks for an object and identifies it",
+ "At level 17 it identifies all objects in the inventory",
+ "At level 27 it identifies all objects in the inventory and in a",
+ "radius on the floor, as well as probing monsters in that radius"
+ }
+}
+
+VISION = add_spell
+{
+ ["name"] = "Vision",
+ ["school"] = {SCHOOL_DIVINATION},
+ ["level"] = 15,
+ ["mana"] = 7,
+ ["mana_max"] = 55,
+ ["fail"] = 45,
+ ["stick"] =
+ {
+ ["charge"] = { 4, 6 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 60,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 10, 30 },
+ },
+ },
+ ["inertia"] = { 2, 200 },
+ ["spell"] = function()
+ if get_level(VISION, 50) >= 25 then
+ wiz_lite_extra()
+ else
+ map_area()
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Detects the layout of the surrounding area",
+ "At level 25 it maps and lights the whole level",
+ }
+}
+
+SENSEHIDDEN = add_spell
+{
+ ["name"] = "Sense Hidden",
+ ["school"] = {SCHOOL_DIVINATION},
+ ["level"] = 5,
+ ["mana"] = 2,
+ ["mana_max"] = 10,
+ ["fail"] = 25,
+ ["stick"] =
+ {
+ ["charge"] = { 1, 15 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 20,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 10, 50 },
+ },
+ },
+ ["inertia"] = { 1, 10 },
+ ["spell"] = function()
+ local obvious = nil
+ obvious = detect_traps(15 + get_level(SENSEHIDDEN, 40, 0))
+ if get_level(SENSEHIDDEN, 50) >= 15 then
+ obvious = is_obvious(set_tim_invis(10 + randint(20) + get_level(SENSEHIDDEN, 40)), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ if get_level(SENSEHIDDEN, 50) >= 15 then
+ return "rad "..(15 + get_level(SENSEHIDDEN, 40)).." dur "..(10 + get_level(SENSEHIDDEN, 40)).."+d20"
+ else
+ return "rad "..(15 + get_level(SENSEHIDDEN, 40))
+ end
+ end,
+ ["desc"] = {
+ "Detects the traps in a certain radius around you",
+ "At level 15 it allows you to sense invisible for a while"
+ }
+}
+
+REVEALWAYS = add_spell
+{
+ ["name"] = "Reveal Ways",
+ ["school"] = {SCHOOL_DIVINATION},
+ ["level"] = 9,
+ ["mana"] = 3,
+ ["mana_max"] = 15,
+ ["fail"] = 20,
+ ["stick"] =
+ {
+ ["charge"] = { 6, 6 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 35,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 25, 50 },
+ },
+ },
+ ["inertia"] = { 1, 10 },
+ ["spell"] = function()
+ local obvious
+ obvious = detect_doors(10 + get_level(REVEALWAYS, 40, 0))
+ obvious = is_obvious(detect_stairs(10 + get_level(REVEALWAYS, 40, 0)), obvious)
+ return obvious
+ end,
+ ["info"] = function()
+ return "rad "..(10 + get_level(REVEALWAYS, 40))
+ end,
+ ["desc"] = {
+ "Detects the doors/stairs/ways in a certain radius around you",
+ }
+}
+
+SENSEMONSTERS = add_spell
+{
+ ["name"] = "Sense Monsters",
+ ["school"] = {SCHOOL_DIVINATION},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 20,
+ ["fail"] = 10,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 10 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 37,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 15, 40 },
+ },
+ },
+ ["inertia"] = { 1, 10 },
+ ["spell"] = function()
+ local obvious
+ obvious = detect_monsters_normal(10 + get_level(SENSEMONSTERS, 40, 0))
+ if get_level(SENSEMONSTERS, 50) >= 30 then
+ obvious = is_obvious(set_tim_esp(10 + randint(10) + get_level(SENSEMONSTERS, 20)), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ if get_level(SENSEMONSTERS, 50) >= 30 then
+ return "rad "..(10 + get_level(SENSEMONSTERS, 40)).." dur "..(10 + get_level(SENSEMONSTERS, 20)).."+d10"
+ else
+ return "rad "..(10 + get_level(SENSEMONSTERS, 40))
+ end
+ end,
+ ["desc"] = {
+ "Detects all monsters near you",
+ "At level 30 it allows you to sense monster minds for a while"
+ }
+}
diff --git a/lib/scpt/s_earth.lua b/lib/scpt/s_earth.lua
new file mode 100644
index 00000000..23aa001c
--- /dev/null
+++ b/lib/scpt/s_earth.lua
@@ -0,0 +1,184 @@
+-- The earth school
+
+STONESKIN = add_spell
+{
+ ["name"] = "Stone Skin",
+ ["school"] = SCHOOL_EARTH,
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 50,
+ ["fail"] = 10,
+ ["inertia"] = { 2, 50 },
+ ["spell"] = function()
+ local type
+ if get_level(STONESKIN, 50) >= 25 then
+ type = SHIELD_COUNTER
+ else
+ type = 0
+ end
+ return set_shield(randint(10) + 10 + get_level(STONESKIN, 100), 10 + get_level(STONESKIN, 50), type, 2 + get_level(STONESKIN, 5), 3 + get_level(STONESKIN, 5))
+ end,
+ ["info"] = function()
+ if get_level(STONESKIN, 50) >= 25 then
+ return "dam "..(2 + get_level(STONESKIN, 5)).."d"..(3 + get_level(STONESKIN, 5)).." dur "..(10 + get_level(STONESKIN, 100)).."+d10 AC "..(10 + get_level(STONESKIN, 50))
+ else
+ return "dur "..(10 + get_level(STONESKIN, 100)).."+d10 AC "..(10 + get_level(STONESKIN, 50))
+ end
+ end,
+ ["desc"] = {
+ "Creates a shield of earth around you to protect you",
+ "At level 25 it starts dealing damage to attackers"
+ }
+}
+
+DIG = add_spell
+{
+ ["name"] = "Dig",
+ ["school"] = SCHOOL_EARTH,
+ ["level"] = 12,
+ ["mana"] = 14,
+ ["mana_max"] = 14,
+ ["fail"] = 20,
+ ["stick"] =
+ {
+ ["charge"] = { 15, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 25,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 1, 1 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return wall_to_mud(dir)
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Digs a hole in a wall much faster than any shovels",
+ }
+}
+
+STONEPRISON = add_spell
+{
+ ["name"] = "Stone Prison",
+ ["school"] = SCHOOL_EARTH,
+ ["level"] = 25,
+ ["mana"] = 30,
+ ["mana_max"] = 50,
+ ["fail"] = 65,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 3 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 57,
+ ["base_level"] = { 1, 3 },
+ ["max_level"] = { 5, 20 },
+ },
+ },
+ ["spell"] = function()
+ local ret, x, y
+ if get_level(STONEPRISON, 50) >= 10 then
+ ret, x, y = tgt_pt()
+ else
+ y = player.py
+ x = player.px
+ end
+ wall_stone(y, x)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Creates a prison of walls around you",
+ "At level 10 it allows you to target a monster"
+ }
+}
+
+STRIKE = add_spell
+{
+ ["name"] = "Strike",
+ ["school"] = {SCHOOL_EARTH},
+ ["level"] = 30,
+ ["mana"] = 30,
+ ["mana_max"] = 50,
+ ["fail"] = 60,
+ ["stick"] =
+ {
+ ["charge"] = { 2, 6 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 635,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 10, 50 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir, rad
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ if get_level(STRIKE, 50) >= 12 then
+ return fire_ball(GF_FORCE, dir, 50 + get_level(STRIKE, 50), 1)
+ else
+ return fire_ball(GF_FORCE, dir, 50 + get_level(STRIKE, 50), 0)
+ end
+ end,
+ ["info"] = function()
+ if get_level(STRIKE, 50) >= 12 then
+ return "dam "..(50 + get_level(STRIKE, 50)).." rad 1"
+ else
+ return "dam "..(50 + get_level(STRIKE, 50))
+ end
+ end,
+ ["desc"] = {
+ "Creates a micro-ball of force that will push monsters backwards",
+ "If the monster is caught near a wall, it'll be crushed against it",
+ "At level 12 it turns into a ball of radius 1"
+ }
+}
+
+SHAKE = add_spell
+{
+ ["name"] = "Shake",
+ ["school"] = {SCHOOL_EARTH},
+ ["level"] = 27,
+ ["mana"] = 25,
+ ["mana_max"] = 30,
+ ["fail"] = 60,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 10 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 75,
+ ["base_level"] = { 1, 3 },
+ ["max_level"] = { 9, 20 },
+ },
+ },
+ ["inertia"] = { 2, 50 },
+ ["spell"] = function()
+ local ret, x, y
+ if get_level(SHAKE, 50) >= 10 then
+ ret, x, y = tgt_pt()
+ if ret == FALSE then return end
+ else
+ x = player.px
+ y = player.py
+ end
+ earthquake(y, x, 4 + get_level(SHAKE, 10));
+ return TRUE
+ end,
+ ["info"] = function()
+ return "rad "..(4 + get_level(SHAKE, 10))
+ end,
+ ["desc"] = {
+ "Creates a localised earthquake",
+ "At level 10 it can be targeted at any location"
+ }
+}
diff --git a/lib/scpt/s_eru.lua b/lib/scpt/s_eru.lua
new file mode 100644
index 00000000..c0cb0aaf
--- /dev/null
+++ b/lib/scpt/s_eru.lua
@@ -0,0 +1,130 @@
+-- Handle Eru Iluvatar magic school
+
+ERU_SEE = add_spell
+{
+ ["name"] = "See the Music",
+ ["school"] = {SCHOOL_ERU},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 50,
+ ["fail"] = 20,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local obvious
+ obvious = set_tim_invis(randint(20) + 10 + get_level(ERU_SEE, 100))
+ if get_level(ERU_SEE) >= 30 then
+ wiz_lite_extra()
+ obvious = TRUE
+ elseif get_level(ERU_SEE) >= 10 then
+ map_area()
+ obvious = TRUE
+ end
+ if get_level(ERU_SEE) >= 20 then
+ obvious = is_obvious(set_blind(0), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ return "dur "..(10 + get_level(ERU_SEE, 100)).."+d20"
+ end,
+ ["desc"] = {
+ "Allows you to 'see' the Great Music from which the world",
+ "originates, allowing you to see unseen things",
+ "At level 10 it allows you to see your surroundings",
+ "At level 20 it allows you to cure blindness",
+ "At level 30 it allows you to fully see all the level"
+ }
+}
+
+ERU_LISTEN = add_spell
+{
+ ["name"] = "Listen to the Music",
+ ["school"] = {SCHOOL_ERU},
+ ["level"] = 7,
+ ["mana"] = 15,
+ ["mana_max"] = 200,
+ ["fail"] = 25,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ if get_level(ERU_LISTEN) >= 30 then
+ ident_all()
+ identify_pack()
+ return TRUE
+ elseif get_level(ERU_LISTEN) >= 14 then
+ identify_pack()
+ return TRUE
+ else
+ return ident_spell()
+ end
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Allows you to listen to the Great Music from which the world",
+ "originates, allowing you to understand the meaning of things",
+ "At level 14 it allows you to identify all your pack",
+ "At level 30 it allows you to identify all items on the level",
+ }
+}
+
+ERU_UNDERSTAND = add_spell
+{
+ ["name"] = "Know the Music",
+ ["school"] = {SCHOOL_ERU},
+ ["level"] = 30,
+ ["mana"] = 200,
+ ["mana_max"] = 600,
+ ["fail"] = 50,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ if get_level(ERU_UNDERSTAND) >= 10 then
+ identify_pack_fully()
+ return TRUE
+ else
+ return identify_fully()
+ end
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Allows you to understand the Great Music from which the world",
+ "originates, allowing you to know the full abilities of things",
+ "At level 10 it allows you to *identify* all your pack",
+ }
+}
+
+ERU_PROT = add_spell
+{
+ ["name"] = "Lay of Protection",
+ ["school"] = {SCHOOL_ERU},
+ ["level"] = 35,
+ ["mana"] = 400,
+ ["mana_max"] = 400,
+ ["fail"] = 80,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ return fire_ball(GF_MAKE_GLYPH, 0, 1, 1 + get_level(ERU_PROT, 2, 0))
+ end,
+ ["info"] = function()
+ return "rad "..(1 + get_level(ERU_PROT, 2, 0))
+ end,
+ ["desc"] = {
+ "Creates a circle of safety around you",
+ }
+}
diff --git a/lib/scpt/s_fire.lua b/lib/scpt/s_fire.lua
new file mode 100644
index 00000000..dbbf7604
--- /dev/null
+++ b/lib/scpt/s_fire.lua
@@ -0,0 +1,227 @@
+-- handle the fire school
+
+GLOBELIGHT = add_spell
+{
+ ["name"] = "Globe of Light",
+ ["school"] = {SCHOOL_FIRE},
+ ["level"] = 1,
+ ["mana"] = 2,
+ ["mana_max"] = 15,
+ ["fail"] = 10,
+ ["stick"] =
+ {
+ ["charge"] = { 10, 5 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 7,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 10, 45 },
+ },
+ },
+ ["inertia"] = { 1, 40 },
+ ["spell"] = function()
+ local obvious
+ if get_level(GLOBELIGHT, 50) >= 3 then
+ obvious = lite_area(10, 4)
+ else
+ lite_room(player.py, player.px)
+ obvious = TRUE
+ end
+ if get_level(GLOBELIGHT, 50) >= 15 then
+ obvious = is_obvious(fire_ball(GF_LITE, 0, 10 + get_level(GLOBELIGHT, 100), 5 + get_level(GLOBELIGHT, 6)), obvious)
+ player.update = bor(player.update, PU_VIEW)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ if get_level(GLOBELIGHT, 50) >= 15 then
+ return "dam "..(10 + get_level(GLOBELIGHT, 100)).." rad "..(5 + get_level(GLOBELIGHT, 6))
+ else
+ return ""
+ end
+ end,
+ ["desc"] = {
+ "Creates a globe of pure light",
+ "At level 3 it starts damaging monsters",
+ "At level 15 it starts creating a more powerful kind of light",
+ }
+}
+
+FIREFLASH = add_spell
+{
+ ["name"] = "Fireflash",
+ ["school"] = {SCHOOL_FIRE},
+ ["level"] = 10,
+ ["mana"] = 5,
+ ["mana_max"] = 70,
+ ["fail"] = 35,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 35,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 15, 35 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir, type
+ if (get_level(FIREFLASH, 50) >= 20) then
+ type = GF_HOLY_FIRE
+ else
+ type = GF_FIRE
+ end
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_ball(type, dir, 20 + get_level(FIREFLASH, 500), 2 + get_level(FIREFLASH, 5))
+ end,
+ ["info"] = function()
+ return "dam "..(20 + get_level(FIREFLASH, 500)).." rad "..(2 + get_level(FIREFLASH, 5))
+ end,
+ ["desc"] = {
+ "Conjures a ball of fire to burn your foes to ashes",
+ "At level 20 it turns into a ball of holy fire"
+ }
+}
+
+FIERYAURA = add_spell
+{
+ ["name"] = "Fiery Shield",
+ ["school"] = {SCHOOL_FIRE},
+ ["level"] = 20,
+ ["mana"] = 20,
+ ["mana_max"] = 60,
+ ["fail"] = 50,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 5 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 50,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 5, 40 },
+ },
+ },
+ ["inertia"] = { 2, 15 },
+ ["spell"] = function()
+ local type
+ if (get_level(FIERYAURA, 50) >= 8) then
+ type = SHIELD_GREAT_FIRE
+ else
+ type = SHIELD_FIRE
+ end
+ return set_shield(randint(20) + 10 + get_level(FIERYAURA, 70), 10, type, 5 + get_level(FIERYAURA, 10), 5 + get_level(FIERYAURA, 7))
+ end,
+ ["info"] = function()
+ return "dam "..(5 + get_level(FIERYAURA, 15)).."d"..(5 + get_level(FIERYAURA, 7)).." dur "..(10 + get_level(FIERYAURA, 70)).."+d20"
+ end,
+ ["desc"] = {
+ "Creates a shield of fierce flames around you",
+ "At level 8 it turns into a greater kind of flame that can not be resisted"
+ }
+}
+
+FIREWALL = add_spell
+{
+ ["name"] = "Firewall",
+ ["school"] = {SCHOOL_FIRE},
+ ["level"] = 15,
+ ["mana"] = 25,
+ ["mana_max"] = 100,
+ ["fail"] = 40,
+ ["stick"] =
+ {
+ ["charge"] = { 4, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 55,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 5, 40 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir, type
+ if (get_level(FIREWALL, 50) >= 6) then
+ type = GF_HELL_FIRE
+ else
+ type = GF_FIRE
+ end
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ fire_wall(type, dir, 40 + get_level(FIREWALL, 150), 10 + get_level(FIREWALL, 14))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dam "..(40 + get_level(FIREWALL, 150)).." dur "..(10 + get_level(FIREWALL, 14))
+ end,
+ ["desc"] = {
+ "Creates a fiery wall to incinerate monsters stupid enough to attack you",
+ "At level 6 it turns into a wall of hell fire"
+ }
+}
+
+FIREGOLEM = add_spell
+{
+ ["name"] = "Fire Golem",
+ ["school"] = {SCHOOL_FIRE, SCHOOL_MIND},
+ ["level"] = 7,
+ ["mana"] = 16,
+ ["mana_max"] = 70,
+ ["fail"] = 40,
+ ["spell"] = function()
+ local m_idx, y, x, ret, item
+
+ -- Can we reconnect ?
+ if do_control_reconnect() == TRUE then
+ msg_print("Control re-established.")
+ return
+ end
+
+ ret, item = get_item("Which light source do you want to use to create the golem?",
+ "You have no light source for the golem",
+ bor(USE_INVEN, USE_EQUIP),
+ function (obj)
+ if (obj.tval == TV_LITE) and ((obj.sval == SV_LITE_TORCH) or (obj.sval == SV_LITE_LANTERN)) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+ if ret == FALSE then return TRUE end
+ inven_item_increase(item, -1)
+ inven_item_describe(item)
+ inven_item_optimize(item)
+
+ -- Summon it
+ m_allow_special[1043 + 1] = TRUE
+ y, x = find_position(player.py, player.px)
+ m_idx = place_monster_one(y, x, 1043, 0, FALSE, MSTATUS_FRIEND)
+ m_allow_special[1043 + 1] = FALSE
+
+ -- level it
+ if m_idx ~= 0 then
+ monster_set_level(m_idx, 7 + get_level(FIREGOLEM, 70))
+ player.control = m_idx
+ monster(m_idx).mflag = bor(monster(m_idx).mflag, MFLAG_CONTROL)
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return "golem level "..(7 + get_level(FIREGOLEM, 70))
+ end,
+ ["desc"] = {
+ "Creates a fiery golem and controls it",
+ "During the control the available keylist is:",
+ "Movement keys: movement of the golem(depending on its speed",
+ " it can move more than one square)",
+ ", : pickup all items on the floor",
+ "d : drop all carried items",
+ "i : list all carried items",
+ "m : end the possession/use golem powers",
+ "Most of the other keys are disabled, you cannot interact with your",
+ "real body while controlling the golem",
+ "But to cast the spell you will need a lantern or a wooden torch to",
+ "Create the golem from"
+ }
+}
diff --git a/lib/scpt/s_geom.lua b/lib/scpt/s_geom.lua
new file mode 100644
index 00000000..b9730318
--- /dev/null
+++ b/lib/scpt/s_geom.lua
@@ -0,0 +1,656 @@
+-- Geomancy school
+
+function geomancy_random_wall(y, x)
+ local c_ptr = cave(y, x)
+
+ -- Do not destroy permanent things
+ if cave_is(c_ptr, FF1_PERMANENT) ~= FALSE then return end
+
+ local feat = nil
+ local table =
+ {
+ [1] = { SKILL_FIRE, FEAT_SANDWALL, 1},
+
+ [2] = { SKILL_WATER, FEAT_TREES, 1},
+ [3] = { SKILL_WATER, FEAT_ICE_WALL, 12},
+
+ [4] = { SKILL_EARTH, FEAT_WALL_EXTRA, 1},
+ }
+
+ while feat == nil do
+ local t = table[randint(getn(table))]
+
+ -- Do we meet the requirements ?
+ -- And then select the features based on skill proportions
+ if get_skill(t[1]) >= t[3] and magik(get_skill_scale(t[1], 100)) == TRUE then
+ feat = t[2]
+ end
+ end
+
+ cave_set_feat(y, x, feat)
+end
+
+
+GF_ELEMENTAL_WALL = add_spell_type
+{
+ ["color"] = { TERM_GREEN, 0 },
+ ["angry"] = function() return TRUE, FALSE end,
+ ["grid"] = function(who, dam, rad, y, x)
+ if player.py ~= y or player.px ~= x then
+ geomancy_random_wall(y, x)
+ end
+ end,
+}
+
+function geomancy_random_floor(y, x, kill_wall)
+ local c_ptr = cave(y, x)
+
+ -- Do not destroy permanent things
+ if cave_is(c_ptr, FF1_PERMANENT) ~= FALSE then return end
+ if not kill_wall then
+ if cave_is(c_ptr, FF1_FLOOR) ~= TRUE then return end
+ end
+
+ local feat = nil
+ local table =
+ {
+ [1] = { SKILL_FIRE, FEAT_SAND, 1},
+ [2] = { SKILL_FIRE, FEAT_SHAL_LAVA, 8},
+ [3] = { SKILL_FIRE, FEAT_DEEP_LAVA, 18},
+
+ [4] = { SKILL_WATER, FEAT_SHAL_WATER, 1},
+ [5] = { SKILL_WATER, FEAT_DEEP_WATER, 8},
+ [6] = { SKILL_WATER, FEAT_ICE, 18},
+
+ [7] = { SKILL_EARTH, FEAT_GRASS, 1},
+ [8] = { SKILL_EARTH, FEAT_FLOWER, 8},
+ [9] = { SKILL_EARTH, FEAT_DARK_PIT, 18},
+ }
+
+ while feat == nil do
+ local t = table[randint(getn(table))]
+
+ -- Do we meet the requirements ?
+ -- And then select the features based on skill proportions
+ if get_skill(t[1]) >= t[3] and magik(get_skill_scale(t[1], 100)) == TRUE then
+ feat = t[2]
+ end
+ end
+
+ cave_set_feat(y, x, feat)
+end
+
+
+GF_ELEMENTAL_GROWTH = add_spell_type
+{
+ ["color"] = { TERM_GREEN, 0 },
+ ["angry"] = function() return TRUE, FALSE end,
+ ["grid"] = function(who, dam, rad, y, x)
+ geomancy_random_floor(y, x)
+ end,
+}
+
+CALL_THE_ELEMENTS = add_spell
+{
+ ["name"] = "Call the Elements",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 1,
+ ["mana"] = 2,
+ ["mana_max"] = 20,
+ ["fail"] = 10,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, dir = 0, 0
+
+ if get_level(CALL_THE_ELEMENTS) >= 17 then
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ end
+
+ fire_ball(GF_ELEMENTAL_GROWTH, dir, 1, 1 + get_level(CALL_THE_ELEMENTS, 5, 0))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "rad "..(1 + get_level(CALL_THE_ELEMENTS, 5, 0))
+ end,
+ ["desc"] = {
+ "Randomly creates various elements around you",
+ "Each type of element chance is controlled by your level",
+ "in the corresponding skill",
+ "At level 17 it can be targeted",
+ }
+}
+
+-- Seperate function because an other spell needs it
+function channel_the_elements(y, x, level, silent)
+ local t =
+ {
+ -- Earth stuff
+ [FEAT_GRASS] = function()
+ hp_player(player.mhp * (5 + get_skill_scale(SKILL_EARTH, 20)) / 100)
+ end,
+ [FEAT_FLOWER] = function()
+ hp_player(player.mhp * (5 + get_skill_scale(SKILL_EARTH, 30)) / 100)
+ end,
+ [FEAT_DARK_PIT] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ local type = GF_DARK
+ if get_skill(SKILL_EARTH) >= 18 then type = GF_NETHER end
+ fire_bolt(type, dir, damroll(10, get_skill(SKILL_EARTH)))
+ end,
+
+ -- Water stuff
+ [FEAT_SHAL_WATER] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ local type = GF_WATER
+ if get_skill(SKILL_WATER) >= 18 then type = GF_WAVE end
+
+ if get_skill(SKILL_WATER) >= 8 then
+ fire_beam(type, dir, damroll(3, get_skill(SKILL_WATER)))
+ else
+ fire_bolt(type, dir, damroll(3, get_skill(SKILL_WATER)))
+ end
+ end,
+ [FEAT_DEEP_WATER] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ local type = GF_WATER
+ if get_skill(SKILL_WATER) >= 18 then type = GF_WAVE end
+
+ if get_skill(SKILL_WATER) >= 8 then
+ fire_beam(type, dir, damroll(5, get_skill(SKILL_WATER)))
+ else
+ fire_bolt(type, dir, damroll(5, get_skill(SKILL_WATER)))
+ end
+ end,
+ [FEAT_ICE] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ if get_skill(SKILL_WATER) >= 12 then
+ fire_ball(GF_ICE, dir, get_skill_scale(SKILL_WATER, 340), 3)
+ else
+ fire_bolt(GF_ICE, dir, damroll(3, get_skill(SKILL_WATER)))
+ end
+ end,
+
+ -- Fire stuff
+ [FEAT_SAND] = function()
+ local type
+ if (get_level(FIERYAURA, 50) >= 8) then
+ type = SHIELD_GREAT_FIRE
+ else
+ type = SHIELD_FIRE
+ end
+ local dur = randint(20) + %level + get_skill(SKILL_AIR)
+ set_shield(dur, 0, type, 5 + get_skill_scale(SKILL_FIRE, 20), 5 + get_skill_scale(SKILL_FIRE, 14))
+ set_blind(dur)
+ end,
+ [FEAT_SHAL_LAVA] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ if get_skill(SKILL_FIRE) >= 15 then
+ fire_bolt(GF_HELL_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15))
+ else
+ fire_bolt(GF_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15))
+ end
+ end,
+ [FEAT_DEEP_LAVA] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ if get_skill(SKILL_FIRE) >= 15 then
+ fire_ball(GF_HELL_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15), 3)
+ else
+ fire_ball(GF_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15), 3)
+ end
+ end,
+ }
+
+ if t[cave(y, x).feat] then
+ t[cave(y, x).feat]()
+
+ if magik(100 - level) == TRUE then
+ if cave(y, x).feat == FEAT_FLOWER then
+ cave_set_feat(y, x, FEAT_GRASS)
+ else
+ cave_set_feat(y, x, FEAT_FLOOR)
+ end
+ msg_print("The area is drained.")
+ end
+ elseif not silent then
+ msg_print("You cannot channel this area.")
+ end
+end
+
+CHANNEL_ELEMENTS = add_spell
+{
+ ["name"] = "Channel Elements",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 3,
+ ["mana"] = 3,
+ ["mana_max"] = 30,
+ ["fail"] = 20,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = 0,
+ ["spell"] = function()
+ channel_the_elements(player.py, player.px, get_level(CHANNEL_ELEMENTS), nil)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Draws on the caster's immediate environs to form an attack or other effect.",
+ "Grass/Flower heals.",
+ "Water creates water bolt attacks.",
+ "Ice creates ice bolt attacks.",
+ "Sand creates a wall of thick, blinding, burning sand around you.",
+ "Lava creates fire bolt attacks.",
+ "Deep lava creates fire ball attacks.",
+ "Chasm creates darkness bolt attacks.",
+ "At Earth level 18, darkness becomes nether.",
+ "At Water level 8, water attacks become beams with a striking effect.",
+ "At Water level 12, ice attacks become balls of ice shards.",
+ "At Water level 18, water attacks push monsters back.",
+ "At Fire level 15, fire become hellfire.",
+ }
+}
+
+ELEMENTAL_WAVE = add_spell
+{
+ ["name"] = "Elemental Wave",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 15,
+ ["mana"] = 15,
+ ["mana_max"] = 50,
+ ["fail"] = 20,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = 0,
+ ["spell"] = function()
+ local ret, dir = get_rep_dir()
+ if ret == FALSE then return end
+
+ local y, x = explode_dir(dir)
+ y = y + player.py
+ x = x + player.px
+
+ local t =
+ {
+ -- Earth stuff
+ [FEAT_GRASS] = { GF_POIS, GF_POIS, 10 + get_skill_scale(SKILL_EARTH, 200) },
+ [FEAT_FLOWER] = { GF_POIS, GF_POIS, 10 + get_skill_scale(SKILL_EARTH, 300) },
+ -- cannot turn chasm into a wave
+
+ -- Water stuff
+ [FEAT_SHAL_WATER] = { GF_WATER, GF_WATER, 10 + get_skill_scale(SKILL_WATER, 200) },
+ [FEAT_DEEP_WATER] = { GF_WATER, GF_WATER, 10 + get_skill_scale(SKILL_WATER, 300) },
+ [FEAT_ICE] = { GF_ICE, GF_ICE, 10 + get_skill_scale(SKILL_WATER, 200) },
+
+ -- Fire stuff
+ [FEAT_SAND] = { GF_LITE, GF_LITE, 10 + get_skill_scale(SKILL_FIRE, 400) },
+ [FEAT_SHAL_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 10 + get_skill_scale(SKILL_FIRE, 200) },
+ [FEAT_DEEP_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 10 + get_skill_scale(SKILL_FIRE, 300) },
+ }
+
+
+ local effect = t[cave(y, x).feat]
+ if not effect then
+ msg_print("You cannot channel this area.")
+ else
+ local typ = effect[1]
+ if get_level(ELEMENTAL_WAVE) >= 20 then typ = effect[2] end
+
+ cave_set_feat(y, x, FEAT_FLOOR)
+
+ fire_wave(typ, 0, effect[3], 0, 6 + get_level(ELEMENTAL_WAVE, 20), EFF_WAVE + EFF_LAST + getglobal("EFF_DIR"..dir))
+ end
+
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Draws on an adjacent special square to project a slow-moving",
+ "wave of that element in that direction",
+ "Abyss squares cannot be channeled into a wave.",
+ }
+}
+
+VAPORIZE = add_spell
+{
+ ["name"] = "Vaporize",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 4,
+ ["mana"] = 3,
+ ["mana_max"] = 30,
+ ["fail"] = 15,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ -- Must have at least 4 Air
+ ["random"] = 0,
+ ["depend"] = function()
+ if get_skill(SKILL_AIR) >= 4 then return TRUE end
+ end,
+ ["spell"] = function()
+ local t =
+ {
+ -- Earth stuff
+ [FEAT_GRASS] = { GF_POIS, GF_POIS, 5 + get_skill_scale(SKILL_EARTH, 100) },
+ [FEAT_FLOWER] = { GF_POIS, GF_POIS, 5 + get_skill_scale(SKILL_EARTH, 150) },
+ [FEAT_DARK_PIT] = { GF_DARK, GF_DARK, 5 + get_skill_scale(SKILL_EARTH, 200) },
+
+ -- Water stuff
+ [FEAT_SHAL_WATER] = { GF_WATER, GF_WATER, 5 + get_skill_scale(SKILL_WATER, 100) },
+ [FEAT_DEEP_WATER] = { GF_WATER, GF_WATER, 5 + get_skill_scale(SKILL_WATER, 150) },
+ [FEAT_ICE] = { GF_ICE, GF_ICE, 5 + get_skill_scale(SKILL_WATER, 100) },
+
+ -- Fire stuff
+ [FEAT_SAND] = { GF_LITE, GF_LITE, 5 + get_skill_scale(SKILL_FIRE, 200) },
+ [FEAT_SHAL_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 5 + get_skill_scale(SKILL_FIRE, 100) },
+ [FEAT_DEEP_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 5 + get_skill_scale(SKILL_FIRE, 150) },
+ }
+
+ local effect = t[cave(player.py, player.px).feat]
+ if not effect then
+ msg_print("You cannot channel this area.")
+ else
+ local typ = effect[1]
+ if get_level(VAPORIZE) >= 20 then typ = effect[2] end
+
+ cave_set_feat(player.py, player.px, FEAT_FLOOR)
+
+ fire_cloud(typ, 0, effect[3], 1 + get_level(VAPORIZE, 4), 10 + get_level(VAPORIZE, 20))
+ end
+
+ return TRUE
+ end,
+ ["info"] = function()
+ return "rad "..(1 + get_level(VAPORIZE, 4)).." dur "..(10 + get_level(VAPORIZE, 20))
+ end,
+ ["desc"] = {
+ "Draws upon your immediate environs to form a cloud of damaging vapors",
+ }
+}
+
+geomancy_can_tunnel =
+{
+ [FEAT_WALL_EXTRA] = TRUE,
+ [FEAT_WALL_OUTER] = TRUE,
+ [FEAT_WALL_INNER] = TRUE,
+ [FEAT_WALL_SOLID] = TRUE,
+
+ [FEAT_MAGMA] = TRUE,
+ [FEAT_QUARTZ] = TRUE,
+ [FEAT_MAGMA_H] = TRUE,
+ [FEAT_QUARTZ_H] = TRUE,
+ [FEAT_MAGMA_K] = TRUE,
+ [FEAT_QUARTZ_K] = TRUE,
+
+ [FEAT_TREES] = TRUE,
+ [FEAT_DEAD_TREE] = TRUE,
+
+ [FEAT_SANDWALL] = TRUE,
+ [FEAT_SANDWALL_H] = TRUE,
+ [FEAT_SANDWALL_K] = TRUE,
+
+ [FEAT_ICE_WALL] = TRUE,
+}
+
+-- Dig & sprew
+function geomancy_dig(oy, ox, dir, length)
+ local dy, dx = explode_dir(dir)
+ local y = dy + oy
+ local x = dx + ox
+
+ for i = 1, length do
+ local c_ptr = cave(y, x)
+ local ox = x - dx
+ local oy = y - dy
+
+ -- stop at the end of tunnelable things
+ if not geomancy_can_tunnel[c_ptr.feat] then break end
+
+ if geomancy_can_tunnel[cave(y - 1, x - 1).feat] then geomancy_random_wall(y - 1, x - 1) end
+ if geomancy_can_tunnel[cave(y - 1, x).feat] then geomancy_random_wall(y - 1, x) end
+ if geomancy_can_tunnel[cave(y - 1, x + 1).feat] then geomancy_random_wall(y - 1, x + 1) end
+
+ if geomancy_can_tunnel[cave(y, x - 1).feat] then geomancy_random_wall(y, x - 1) end
+ if geomancy_can_tunnel[cave(y, x + 1).feat] then geomancy_random_wall(y, x + 1) end
+
+ if geomancy_can_tunnel[cave(y + 1, x - 1).feat] then geomancy_random_wall(y + 1, x - 1) end
+ if geomancy_can_tunnel[cave(y + 1, x).feat] then geomancy_random_wall(y + 1, x) end
+ if geomancy_can_tunnel[cave(y + 1, x + 1).feat] then geomancy_random_wall(y + 1, x + 1) end
+
+ y = y + dy
+ x = x + dx
+ end
+
+ y = y - dy
+ x = x - dx
+ while (y ~= oy) or (x ~= ox) do
+ geomancy_random_floor(y, x, TRUE)
+
+ -- Should we branch ?
+ if magik(20) == TRUE then
+ local rot = 1
+ if magik(50) == TRUE then rot = -1 end
+ geomancy_dig(y, x, rotate_dir(dir, rot), length / 3)
+ end
+
+ y = y - dy
+ x = x - dx
+ end
+end
+
+GEOLYSIS = add_spell
+{
+ ["name"] = "Geolysis",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 7,
+ ["mana"] = 15,
+ ["mana_max"] = 40,
+ ["fail"] = 15,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = 0,
+ -- Must have at least 7 Earth
+ ["depend"] = function()
+ if get_skill(SKILL_EARTH) >= 7 then return TRUE end
+ end,
+ ["spell"] = function()
+ local ret, dir = get_rep_dir()
+ if ret == FALSE then return end
+
+ msg_print("Elements recombine before you, laying down an open path.")
+ geomancy_dig(player.py, player.px, dir, 5 + get_level(GEOLYSIS, 12))
+
+ return TRUE
+ end,
+ ["info"] = function()
+ return "length "..(5 + get_level(GEOLYSIS, 12))
+ end,
+ ["desc"] = {
+ "Burrows deeply and slightly at random into a wall,",
+ "leaving behind tailings of various different sorts of walls in the passage.",
+ }
+}
+
+player.dripping_tread = 0
+add_loadsave("player.dripping_tread", 0)
+add_hooks
+{
+ [HOOK_MOVED] = function(oy, ox)
+ if player.dripping_tread > 0 then
+ geomancy_random_floor(oy, ox)
+ player.dripping_tread = player.dripping_tread - 1
+ if player.dripping_tread == 0 then
+ msg_print("You stop dripping raw elemental energies.")
+ end
+ end
+ end
+}
+DRIPPING_TREAD = add_spell
+{
+ ["name"] = "Dripping Tread",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 10,
+ ["mana"] = 15,
+ ["mana_max"] = 25,
+ ["fail"] = 15,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = 0,
+ -- Must have at least 10 Water
+ ["depend"] = function()
+ if get_skill(SKILL_WATER) >= 10 then return TRUE end
+ end,
+ ["spell"] = function()
+ if player.dripping_tread == 0 then
+ player.dripping_tread = randint(15) + 10 + get_level(DRIPPING_TREAD)
+ msg_print("You start dripping raw elemental energies.")
+ else
+ player.dripping_tread = 0
+ msg_print("You stop dripping raw elemental energies.")
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dur "..(10 + get_level(DRIPPING_TREAD)).."+d15 movs"
+ end,
+ ["desc"] = {
+ "Causes you to leave random elemental forms behind as you walk",
+ }
+}
+
+GROW_BARRIER = add_spell
+{
+ ["name"] = "Grow Barrier",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 12,
+ ["mana"] = 30,
+ ["mana_max"] = 40,
+ ["fail"] = 15,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ ["random"] = 0,
+ -- Must have at least 12 Earth
+ ["depend"] = function()
+ if get_skill(SKILL_EARTH) >= 12 then return TRUE end
+ end,
+ ["spell"] = function()
+ local ret, dir = 0, 0
+
+ if get_level(GROW_BARRIER) >= 20 then
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ end
+
+ fire_ball(GF_ELEMENTAL_WALL, dir, 1, 1)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Creates impassable terrain (walls, trees, etc.) around you.",
+ "At level 20 it can be projected around another area.",
+ }
+}
+
+ELEMENTAL_MINION = add_spell
+{
+ ["name"] = "Elemental Minion",
+ ["school"] = {SCHOOL_GEOMANCY},
+ ["level"] = 20,
+ ["mana"] = 40,
+ ["mana_max"] = 80,
+ ["fail"] = 25,
+ -- Unnafected by blindness
+ ["random"] = 0,
+ -- Must have at least 12 Earth
+ ["spell"] = function()
+ local ret, dir = 0, 0
+
+ ret, dir = get_rep_dir()
+ if ret == FALSE then return end
+
+ local t =
+ {
+ [FEAT_WALL_EXTRA] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_WALL_OUTER] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_WALL_INNER] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_WALL_SOLID] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_MAGMA] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_QUARTZ] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_MAGMA_H] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_QUARTZ_H] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_MAGMA_K] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+ [FEAT_QUARTZ_K] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } },
+
+ [FEAT_DARK_PIT] = { SKILL_AIR, { "Air elemental", "Ancient blue dragon", "Great Storm Wyrm", "Sky Drake" } },
+
+ [FEAT_SANDWALL] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } },
+ [FEAT_SANDWALL_H] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } },
+ [FEAT_SANDWALL_K] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } },
+ [FEAT_SHAL_LAVA] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } },
+ [FEAT_DEEP_LAVA] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } },
+
+ [FEAT_ICE_WALL] = { SKILL_WATER, { "Water elemental", "Water troll", "Water demon" } },
+ [FEAT_SHAL_WATER] = { SKILL_WATER, { "Water elemental", "Water troll", "Water demon" } },
+ [FEAT_DEEP_WATER] = { SKILL_WATER, { "Water elemental", "Water troll", "Water demon" } },
+ }
+
+ local y, x = explode_dir(dir)
+ y = y + player.py
+ x = x + player.px
+
+ local effect = t[cave(y, x).feat]
+ if not effect then
+ msg_print("You cannot summon from this area.")
+ else
+ local skill = effect[1]
+ local types = effect[2]
+
+ local max = get_skill_scale(skill, getn(types))
+ if max == 0 then max = 1 end
+
+ local r_idx = test_monster_name(types[rand_range(1, max)])
+
+ -- Summon it
+ local my, mx = find_position(y, x)
+ local m_idx = place_monster_one(my, mx, r_idx, 0, FALSE, MSTATUS_FRIEND)
+
+ -- level it
+ if m_idx ~= 0 then
+ monster_set_level(m_idx, 10 + get_level(ELEMENTAL_MINION, 120))
+ end
+
+ cave_set_feat(y, x, FEAT_FLOOR)
+ end
+
+ return TRUE
+ end,
+ ["info"] = function()
+ return "min level "..(10 + get_level(ELEMENTAL_MINION, 120))
+ end,
+ ["desc"] = {
+ "Summons a minion from a nearby element.",
+ "Walls can summon Earth elmentals, Xorns and Xarens",
+ "Dark Pits can summon Air elementals, Ancient blue dragons, Great Storm Wyrms",
+ "and Sky Drakes",
+ "Sandwalls and lava can summon Fire elementals and Ancient red dragons",
+ "Icewall, and water can summon Water elementals, Water trolls and Water demons",
+ }
+}
diff --git a/lib/scpt/s_mana.lua b/lib/scpt/s_mana.lua
new file mode 100644
index 00000000..736b06b0
--- /dev/null
+++ b/lib/scpt/s_mana.lua
@@ -0,0 +1,132 @@
+-- The mana school
+
+function get_manathrust_dam()
+ return 3 + get_level(MANATHRUST, 50), 1 + get_level(MANATHRUST, 20)
+end
+
+MANATHRUST = add_spell
+{
+ ["name"] = "Manathrust",
+ ["school"] = SCHOOL_MANA,
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 25,
+ ["fail"] = 10,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 10 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 5,
+ ["base_level"] = { 1, 20 },
+ ["max_level"] = { 15, 33 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir
+
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_bolt(GF_MANA, dir, damroll(get_manathrust_dam()))
+ end,
+ ["info"] = function()
+ local x, y
+
+ x, y = get_manathrust_dam()
+ return "dam "..x.."d"..y
+ end,
+ ["desc"] = {
+ "Conjures up mana into a powerful bolt",
+ "The damage is irresistible and will increase with level"
+ }
+}
+
+DELCURSES = add_spell
+{
+ ["name"] = "Remove Curses",
+ ["school"] = SCHOOL_MANA,
+ ["level"] = 10,
+ ["mana"] = 20,
+ ["mana_max"] = 40,
+ ["fail"] = 30,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 8 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 70,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 15, 50 },
+ },
+ },
+ ["inertia"] = { 1, 10 },
+ ["spell"] = function()
+ local done
+
+ if get_level(DELCURSES, 50) >= 20 then done = remove_all_curse()
+ else done = remove_curse() end
+ if done == TRUE then msg_print("The curse is broken!") end
+ return done
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Remove curses of worn objects",
+ "At level 20 switches to *remove curses*"
+ }
+}
+
+RESISTS = add_spell
+{
+ ["name"] = "Elemental Shield",
+ ["school"] = SCHOOL_MANA,
+ ["level"] = 20,
+ ["mana"] = 17,
+ ["mana_max"] = 20,
+ ["fail"] = 40,
+ ["inertia"] = { 2, 25 },
+ ["spell"] = function()
+ local obvious
+ if player.oppose_fire == 0 then obvious = set_oppose_fire(randint(10) + 15 + get_level(RESISTS, 50)) end
+ if player.oppose_cold == 0 then obvious = is_obvious(set_oppose_cold(randint(10) + 15 + get_level(RESISTS, 50)), obvious) end
+ if player.oppose_elec == 0 then obvious = is_obvious(set_oppose_elec(randint(10) + 15 + get_level(RESISTS, 50)), obvious) end
+ if player.oppose_acid == 0 then obvious = is_obvious(set_oppose_acid(randint(10) + 15 + get_level(RESISTS, 50)), obvious) end
+ return obvious
+ end,
+ ["info"] = function()
+ return "dur "..(15 + get_level(RESISTS, 50)).."+d10"
+ end,
+ ["desc"] = {
+ "Provide resistances to the four basic elements",
+ }
+}
+
+MANASHIELD = add_spell
+{
+ ["name"] = "Disruption Shield",
+ ["school"] = SCHOOL_MANA,
+ ["level"] = 45,
+ ["mana"] = 50,
+ ["mana_max"] = 50,
+ ["fail"] = 90,
+ ["inertia"] = { 9, 10},
+ ["spell"] = function()
+ if get_level(MANASHIELD, 50) >= 5 then
+ if (player.invuln == 0) then
+ return set_invuln(randint(5) + 3 + get_level(MANASHIELD, 10))
+ end
+ else
+ if (player.disrupt_shield == 0) then return set_disrupt_shield(randint(5) + 3 + get_level(MANASHIELD, 10)) end
+ end
+ end,
+ ["info"] = function()
+ return "dur "..(3 + get_level(MANASHIELD, 10)).."+d5"
+ end,
+ ["desc"] = {
+ "Uses mana instead of hp to take damage",
+ "At level 5 switches to Globe of Invulnerability.",
+ "The spell breaks as soon as a melee, shooting, throwing or magical",
+ "skill action is attempted, and lasts only a short time."
+ }
+}
diff --git a/lib/scpt/s_manwe.lua b/lib/scpt/s_manwe.lua
new file mode 100644
index 00000000..6f0f9661
--- /dev/null
+++ b/lib/scpt/s_manwe.lua
@@ -0,0 +1,144 @@
+-- Handle Manwe Sulimo magic school
+
+MANWE_SHIELD = add_spell
+{
+ ["name"] = "Wind Shield",
+ ["school"] = {SCHOOL_MANWE},
+ ["level"] = 10,
+ ["mana"] = 100,
+ ["mana_max"] = 500,
+ ["fail"] = 30,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local dur = get_level(MANWE_SHIELD, 50) + 10 + randint(20)
+ local obvious
+
+ obvious = set_protevil(dur)
+ if get_level(MANWE_SHIELD) >= 10 then
+ local type
+
+ type = 0
+ if get_level(MANWE_SHIELD) >= 20 then
+ type = SHIELD_COUNTER
+ end
+ obvious = is_obvious(set_shield(dur, get_level(MANWE_SHIELD, 30), type, 1 + get_level(MANWE_SHIELD, 2), 1 + get_level(MANWE_SHIELD, 6)), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ local desc = "dur "..(get_level(MANWE_SHIELD, 50) + 10).."+d20"
+
+ if get_level(MANWE_SHIELD) >= 10 then
+ desc = desc.." AC "..(get_level(MANWE_SHIELD, 30))
+ end
+ if get_level(MANWE_SHIELD) >= 20 then
+ desc = desc.." dam "..(1 + get_level(MANWE_SHIELD, 2)).."d"..(1 + get_level(MANWE_SHIELD, 6))
+ end
+ return desc
+ end,
+ ["desc"] = {
+ "It surrounds you with a shield of wind that deflects blows from evil monsters",
+ "At level 10 it increases your armour rating",
+ "At level 20 it retaliates against monsters that melee you",
+ }
+}
+
+MANWE_AVATAR = add_spell
+{
+ ["name"] = "Avatar",
+ ["school"] = {SCHOOL_MANWE},
+ ["level"] = 35,
+ ["mana"] = 1000,
+ ["mana_max"] = 1000,
+ ["fail"] = 80,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ return set_mimic(get_level(MANWE_AVATAR, 20) + randint(10), resolve_mimic_name("Maia"), player.lev)
+ end,
+ ["info"] = function()
+ return "dur "..(get_level(MANWE_AVATAR, 20)).."+d10"
+ end,
+ ["desc"] = {
+ "It turns you into a full grown Maia",
+ }
+}
+
+MANWE_BLESS = add_spell
+{
+ ["name"] = "Manwe's Blessing",
+ ["school"] = {SCHOOL_MANWE},
+ ["level"] = 1,
+ ["mana"] = 10,
+ ["mana_max"] = 100,
+ ["fail"] = 20,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local dur = get_level(MANWE_BLESS, 70) + 30 + randint(40)
+ local obvious
+
+ obvious = set_blessed(dur)
+ obvious = is_obvious(set_afraid(0), obvious)
+ obvious = is_obvious(set_lite(0), obvious)
+ if get_level(MANWE_BLESS) >= 10 then
+ obvious = is_obvious(set_hero(dur), obvious)
+ end
+ if get_level(MANWE_BLESS) >= 20 then
+ obvious = is_obvious(set_shero(dur), obvious)
+ end
+ if get_level(MANWE_BLESS) >= 30 then
+ obvious = is_obvious(set_holy(dur), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ return "dur "..(get_level(MANWE_BLESS, 70) + 30).."+d40"
+ end,
+ ["desc"] = {
+ "Manwe's Blessing removes your fears, blesses you and surrounds you with",
+ "holy light",
+ "At level 10 it also grants heroism",
+ "At level 20 it also grants super heroism",
+ "At level 30 it also grants holy luck and life protection",
+ }
+}
+
+MANWE_CALL = add_spell
+{
+ ["name"] = "Manwe's Call",
+ ["school"] = {SCHOOL_MANWE},
+ ["level"] = 20,
+ ["mana"] = 200,
+ ["mana_max"] = 500,
+ ["fail"] = 40,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local y, x, m_idx
+
+ y, x = find_position(player.py, player.px)
+ m_idx = place_monster_one(y, x, test_monster_name("Great eagle"), 0, FALSE, MSTATUS_FRIEND)
+
+ if m_idx ~= 0 then
+ monster_set_level(m_idx, 20 + get_level(MANWE_CALL, 70, 0))
+ return TRUE
+ end
+ end,
+ ["info"] = function()
+ return "level "..(get_level(MANWE_CALL, 70) + 20)
+ end,
+ ["desc"] = {
+ "Manwe's Call summons a Great Eagle to help you battle the forces",
+ "of Morgoth"
+ }
+}
diff --git a/lib/scpt/s_melkor.lua b/lib/scpt/s_melkor.lua
new file mode 100644
index 00000000..b2c693dd
--- /dev/null
+++ b/lib/scpt/s_melkor.lua
@@ -0,0 +1,154 @@
+-- handle the melkor school
+
+-- Not included in the spell code directly because I need to call it from somewhere else too
+function do_melkor_curse(who)
+ local m_ptr = monster(who)
+
+ if get_level(MELKOR_CURSE) >= 35 then
+ local r_ptr = race_info_idx(m_ptr.r_idx, m_ptr.ego)
+
+ m_ptr.maxhp = m_ptr.maxhp - r_ptr.hside;
+ if m_ptr.maxhp < 1 then m_ptr.maxhp = 1 end
+ if m_ptr.hp > m_ptr.maxhp then m_ptr.hp = m_ptr.maxhp end
+ player.redraw = bor(player.redraw, PR_HEALTH)
+ end
+ if get_level(MELKOR_CURSE) >= 25 then
+ m_ptr.speed = m_ptr.speed - get_level(MELKOR_CURSE, 7)
+ m_ptr.mspeed = m_ptr.mspeed - get_level(MELKOR_CURSE, 7)
+
+ if m_ptr.speed < 70 then m_ptr.speed = 70 end
+ if m_ptr.mspeed < 70 then m_ptr.mspeed = 70 end
+ end
+ if get_level(MELKOR_CURSE) >= 15 then
+ m_ptr.ac = m_ptr.ac - get_level(MELKOR_CURSE, 50)
+
+ if m_ptr.ac < -70 then m_ptr.ac = -70 end
+ end
+
+ local i, pow
+ i = 1
+ pow = get_level(MELKOR_CURSE, 2)
+ while (i <= 4) do
+ if m_ptr.blow[i].d_dice > 0 then
+ if m_ptr.blow[i].d_dice < pow then
+ pow = m_ptr.blow[i].d_dice
+ end
+ if m_ptr.blow[i].d_side < pow then
+ pow = m_ptr.blow[i].d_side
+ end
+ m_ptr.blow[i].d_dice = m_ptr.blow[i].d_dice - pow
+ end
+ i = i + 1
+ end
+
+ local m_name = monster_desc(m_ptr, 0).." looks weaker."
+ msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2))
+
+ -- wake it
+ m_ptr.csleep = 0;
+end
+
+MELKOR_CURSE = add_spell
+{
+ ["name"] = "Curse",
+ ["school"] = {SCHOOL_MELKOR},
+ ["level"] = 1,
+ ["mana"] = 50,
+ ["mana_max"] = 300,
+ ["fail"] = 20,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ if target_who == -1 then
+ msg_print("You must target a monster.")
+ else
+ do_melkor_curse(target_who)
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "It curses a monster, reducing its melee power",
+ "At level 5 it can be auto-casted (with no piety cost) while fighting",
+ "At level 15 it also reduces armor",
+ "At level 25 it also reduces speed",
+ "At level 35 it also reduces max life (but it is never fatal)",
+ }
+}
+
+MELKOR_CORPSE_EXPLOSION = add_spell
+{
+ ["name"] = "Corpse Explosion",
+ ["school"] = {SCHOOL_MELKOR},
+ ["level"] = 10,
+ ["mana"] = 100,
+ ["mana_max"] = 500,
+ ["fail"] = 45,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ return fire_ball(GF_CORPSE_EXPL, 0, 20 + get_level(MELKOR_CORPSE_EXPLOSION, 70), 2 + get_level(MELKOR_CORPSE_EXPLOSION, 5))
+ end,
+ ["info"] = function()
+ return "dam "..(20 + get_level(MELKOR_CORPSE_EXPLOSION, 70)).."%"
+ end,
+ ["desc"] = {
+ "It makes corpses in an area around you explode for a percent of their",
+ "hit points as damage",
+ }
+}
+
+MELKOR_MIND_STEAL = add_spell
+{
+ ["name"] = "Mind Steal",
+ ["school"] = {SCHOOL_MELKOR},
+ ["level"] = 20,
+ ["mana"] = 1000,
+ ["mana_max"] = 3000,
+ ["fail"] = 90,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ if target_who == -1 then
+ msg_print("You must target a monster.")
+ else
+ local chance, m_ptr, r_ptr
+
+ m_ptr = monster(target_who)
+ r_ptr = race_info_idx(m_ptr.r_idx, m_ptr.ego)
+ chance = get_level(MELKOR_MIND_STEAL)
+ if (randint(m_ptr.level) < chance) and (band(r_ptr.flags1, RF1_UNIQUE) == 0) then
+ player.control = target_who
+ m_ptr.mflag = bor(m_ptr.mflag, MFLAG_CONTROL)
+
+ local m_name = monster_desc(m_ptr, 0).." falls under your control."
+ msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2))
+ else
+ local m_name = monster_desc(m_ptr, 0).." resists."
+ msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2))
+ end
+ return TRUE
+ end
+ end,
+ ["info"] = function()
+ return "chance 1d(mlvl)<"..(get_level(MELKOR_MIND_STEAL))
+ end,
+ ["desc"] = {
+ "It allows your spirit to temporarily leave your own body, which will",
+ "be vulnerable, to control one of your enemies body."
+ }
+}
diff --git a/lib/scpt/s_meta.lua b/lib/scpt/s_meta.lua
new file mode 100644
index 00000000..eab691d8
--- /dev/null
+++ b/lib/scpt/s_meta.lua
@@ -0,0 +1,287 @@
+-- handle the meta school
+
+RECHARGE = add_spell
+{
+ ["name"] = "Recharge",
+ ["school"] = {SCHOOL_META},
+ ["level"] = 5,
+ ["mana"] = 10,
+ ["mana_max"] = 100,
+ ["fail"] = 20,
+ ["spell"] = function()
+ return recharge(60 + get_level(RECHARGE, 140))
+ end,
+ ["info"] = function()
+ return "power "..(60 + get_level(RECHARGE, 140))
+ end,
+ ["desc"] = {
+ "Taps on the ambient mana to recharge an object's power (charges or mana)",
+ }
+}
+
+function get_spellbinder_max()
+ local i
+
+ i = get_level(SPELLBINDER, 4)
+ if i > 4 then i = 4 end
+ return i
+end
+--'
+SPELLBINDER = add_spell
+{
+ ["name"] = "Spellbinder",
+ ["school"] = {SCHOOL_META},
+ ["level"] = 20,
+ ["mana"] = 100,
+ ["mana_max"] = 300,
+ ["fail"] = 85,
+ ["spell"] = function()
+ local i, ret, c
+
+ if player.spellbinder_num ~= 0 then
+ local t =
+ {
+ [SPELLBINDER_HP75] = "75% HP",
+ [SPELLBINDER_HP50] = "50% HP",
+ [SPELLBINDER_HP25] = "25% HP",
+ }
+ msg_print("The spellbinder is already active.")
+ msg_print("It will trigger at "..t[player.spellbinder_trigger]..".")
+ msg_print("With the spells: ")
+ for i = 1, player.spellbinder_num do
+ msg_print(spell(player.spellbinder[i]).name)
+ end
+ return TRUE
+ end
+
+ ret, c = get_com("Trigger at [a]75% hp [b]50% hp [c]25% hp?", strbyte("a"))
+ if ret == FALSE then return TRUE end
+
+ if c == strbyte("a") then
+ player.spellbinder_trigger = SPELLBINDER_HP75
+ elseif c == strbyte("b") then
+ player.spellbinder_trigger = SPELLBINDER_HP50
+ elseif c == strbyte("c") then
+ player.spellbinder_trigger = SPELLBINDER_HP25
+ else
+ return
+ end
+ player.spellbinder_num = get_spellbinder_max()
+ i = player.spellbinder_num
+ while i > 0 do
+ local s
+
+ s = get_school_spell("bind", "is_ok_spell", 0)
+ if s == -1 then
+ player.spellbinder_trigger = 0
+ player.spellbinder_num = 0
+ return TRUE
+ else
+ if spell(s).skill_level > 7 + get_level(SPELLBINDER, 35) then
+ msg_print("You are only allowed spells with a base level of "..(7 + get_level(SPELLBINDER, 35))..".");
+ return TRUE
+ end
+ end
+ player.spellbinder[i] = s
+ i = i - 1
+ end
+ player.energy = player.energy - 3100;
+ msg_print("Spellbinder ready.")
+ return TRUE
+ end,
+ ["info"] = function()
+ return "number "..(get_spellbinder_max()).." max level "..(7 + get_level(SPELLBINDER, 35))
+ end,
+ ["desc"] = {
+ "Stores spells in a trigger.",
+ "When the condition is met all spells fire off at the same time",
+ "This spell takes a long time to cast so you are advised to prepare it",
+ "in a safe area.",
+ "Also it will use the mana for the Spellbinder and the mana for the",
+ "selected spells"
+ }
+}
+
+DISPERSEMAGIC = add_spell
+{
+ ["name"] = "Disperse Magic",
+ ["school"] = {SCHOOL_META},
+ ["level"] = 15,
+ ["mana"] = 30,
+ ["mana_max"] = 60,
+ ["fail"] = 40,
+ -- Unnafected by blindness
+ ["blind"] = FALSE,
+ -- Unnafected by confusion
+ ["confusion"] = FALSE,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 25,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 5, 40 },
+ },
+ },
+ ["inertia"] = { 1, 5 },
+ ["spell"] = function()
+ local obvious
+ obvious = set_blind(0)
+ obvious = is_obvious(set_lite(0), obvious)
+ if get_level(DISPERSEMAGIC, 50) >= 5 then
+ obvious = is_obvious(set_confused(0), obvious)
+ obvious = is_obvious(set_image(0), obvious)
+ end
+ if get_level(DISPERSEMAGIC, 50) >= 10 then
+ obvious = is_obvious(set_slow(0), obvious)
+ obvious = is_obvious(set_fast(0, 0), obvious)
+ obvious = is_obvious(set_light_speed(0), obvious)
+ end
+ if get_level(DISPERSEMAGIC, 50) >= 15 then
+ obvious = is_obvious(set_stun(0), obvious)
+ obvious = is_obvious(set_meditation(0), obvious)
+ obvious = is_obvious(set_cut(0), obvious)
+ end
+ if get_level(DISPERSEMAGIC, 50) >= 20 then
+ obvious = is_obvious(set_hero(0), obvious)
+ obvious = is_obvious(set_shero(0), obvious)
+ obvious = is_obvious(set_blessed(0), obvious)
+ obvious = is_obvious(set_shield(0, 0, 0, 0, 0), obvious)
+ obvious = is_obvious(set_afraid(0), obvious)
+ obvious = is_obvious(set_parasite(0, 0), obvious)
+ obvious = is_obvious(set_mimic(0, 0, 0), obvious)
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Dispels a lot of magic that can affect you, be it good or bad",
+ "Level 1: blindness and light",
+ "Level 5: confusion and hallucination",
+ "Level 10: speed (both bad or good) and light speed",
+ "Level 15: stunning, meditation, cuts",
+ "Level 20: hero, super hero, bless, shields, afraid, parasites, mimicry",
+ }
+}
+
+TRACKER = add_spell
+{
+ ["name"] = "Tracker",
+ ["school"] = {SCHOOL_META, SCHOOL_CONVEYANCE},
+ ["level"] = 30,
+ ["mana"] = 50,
+ ["mana_max"] = 50,
+ ["fail"] = 95,
+ ["spell"] = function()
+ if last_teleportation_y == -1 then
+ msg_print("There has not been any teleporatation here.")
+ return TRUE
+ end
+ teleport_player_to(last_teleportation_y, last_teleportation_x)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Tracks down the last teleportation that happened on the level and teleports",
+ "you to it",
+ }
+}
+
+-- Saves the values of the timer
+save_timer("TIMER_INERTIA_CONTROL")
+add_loadsave("player.inertia_controlled_spell", -1)
+player.inertia_controlled_spell = -1
+
+-- Automatically cast the inertia controlled spells
+TIMER_INERTIA_CONTROL = new_timer
+{
+ ["enabled"] = FALSE,
+ ["delay"] = 10,
+ ["callback"] = function()
+ -- Don't cast a controlled spell in wilderness mode
+ if player.antimagic then
+ msg_print("Your anti-magic field disrupts any magic attempts.")
+ elseif player.anti_magic then
+ msg_print("Your anti-magic shell disrupts any magic attempts.")
+ elseif (player.inertia_controlled_spell ~= -1) and (player.wild_mode == FALSE) then
+ __spell_spell[player.inertia_controlled_spell]()
+ end
+ end,
+}
+
+stop_inertia_controlled_spell = function()
+ player.inertia_controlled_spell = -1
+ TIMER_INERTIA_CONTROL.enabled = FALSE
+ player.update = bor(player.update, PU_MANA)
+ return TRUE
+end
+
+add_hooks
+{
+ -- Reduce the mana by four times the cost of the spell
+ [HOOK_CALC_MANA] = function(msp)
+ if player.inertia_controlled_spell ~= -1 then
+ msp = msp - (get_mana(player.inertia_controlled_spell) * 4)
+ if msp < 0 then msp = 0 end
+ return TRUE, msp
+ end
+ end,
+
+ -- Stop a previous spell at birth
+ [HOOK_BIRTH_OBJECTS] = function()
+ stop_inertia_controlled_spell()
+ end,
+}
+
+INERTIA_CONTROL = add_spell
+{
+ ["name"] = "Inertia Control",
+ ["school"] = {SCHOOL_META},
+ ["level"] = 37,
+ ["mana"] = 300,
+ ["mana_max"] = 700,
+ ["fail"] = 95,
+ ["spell"] = function()
+ if player.inertia_controlled_spell ~= -1 then
+ msg_print("You cancel your inertia flow control.")
+ return stop_inertia_controlled_spell()
+ end
+
+ local s = get_school_spell("control", "is_ok_spell", 0)
+ if s == -1 then
+ return stop_inertia_controlled_spell()
+ end
+
+ local inertia = __tmp_spells[s].inertia
+
+ if inertia == nil then
+ msg_print("This spell inertia flow can not be controlled.")
+ return stop_inertia_controlled_spell()
+ end
+ if inertia[1] > get_level(INERTIA_CONTROL, 10) then
+ msg_print("This spell inertia flow("..inertia[1]..") is too strong to be controlled by your current spell.")
+ return stop_inertia_controlled_spell()
+ end
+
+ player.inertia_controlled_spell = s
+ TIMER_INERTIA_CONTROL.enabled = TRUE
+ TIMER_INERTIA_CONTROL.delay = inertia[2]
+ TIMER_INERTIA_CONTROL.countdown = TIMER_INERTIA_CONTROL.delay
+ player.update = bor(player.update, PU_MANA)
+ msg_print("Inertia flow controlling spell "..spell(s).name..".")
+ return TRUE
+ end,
+ ["info"] = function()
+ return "level "..get_level(INERTIA_CONTROL, 10)
+ end,
+ ["desc"] = {
+ "Changes the energy flow of a spell to be continuously recasted",
+ "at a given interval. The inertia controlled spell reduces your",
+ "maximum mana by four times its cost.",
+ }
+}
diff --git a/lib/scpt/s_mind.lua b/lib/scpt/s_mind.lua
new file mode 100644
index 00000000..d1b25e9e
--- /dev/null
+++ b/lib/scpt/s_mind.lua
@@ -0,0 +1,132 @@
+-- handle the mind school
+
+CHARM = add_spell
+{
+ ["name"] = "Charm",
+ ["school"] = {SCHOOL_MIND},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 20,
+ ["fail"] = 10,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 35,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 20, 40 },
+ },
+ },
+ ["spell"] = function()
+ if get_level(CHARM, 50) >= 35 then
+ return project_los(GF_CHARM, 10 + get_level(CHARM, 150))
+ elseif get_level(CHARM, 50) >= 15 then
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_ball(GF_CHARM, dir, 10 + get_level(CHARM, 150), 3)
+ else
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_bolt(GF_CHARM, dir, 10 + get_level(CHARM, 150))
+ end
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(CHARM, 150))
+ end,
+ ["desc"] = {
+ "Tries to manipulate the mind of a monster to make it friendly",
+ "At level 15 it turns into a ball",
+ "At level 35 it affects all monsters in sight"
+ }
+}
+
+CONFUSE = add_spell
+{
+ ["name"] = "Confuse",
+ ["school"] = {SCHOOL_MIND},
+ ["level"] = 5,
+ ["mana"] = 5,
+ ["mana_max"] = 30,
+ ["fail"] = 20,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 4 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 45,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 20, 40 },
+ },
+ },
+ ["spell"] = function()
+ if get_level(CONFUSE, 50) >= 35 then
+ return project_los(GF_OLD_CONF, 10 + get_level(CONFUSE, 150))
+ elseif get_level(CONFUSE, 50) >= 15 then
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_ball(GF_OLD_CONF, dir, 10 + get_level(CONFUSE, 150), 3)
+ else
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_bolt(GF_OLD_CONF, dir, 10 + get_level(CONFUSE, 150))
+ end
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(CONFUSE, 150))
+ end,
+ ["desc"] = {
+ "Tries to manipulate the mind of a monster to confuse it",
+ "At level 15 it turns into a ball",
+ "At level 35 it affects all monsters in sight"
+ }
+}
+
+ARMOROFFEAR = add_spell
+{
+ ["name"] = "Armor of Fear",
+ ["school"] = SCHOOL_MIND,
+ ["level"] = 10,
+ ["mana"] = 10,
+ ["mana_max"] = 50,
+ ["fail"] = 35,
+ ["inertia"] = { 2, 20 },
+ ["spell"] = function()
+ return set_shield(randint(10) + 10 + get_level(ARMOROFFEAR, 100), 10, SHIELD_FEAR, 1 + get_level(ARMOROFFEAR, 7), 5 + get_level(ARMOROFFEAR, 20))
+ end,
+ ["info"] = function()
+ return "dur "..(10 + get_level(ARMOROFFEAR, 100)).." power "..(1 + get_level(ARMOROFFEAR, 7)).."d"..(5 + get_level(ARMOROFFEAR, 20))
+ end,
+ ["desc"] = {
+ "Creates a shield of pure fear around you. Any monster attempting to hit you",
+ "must save or flee",
+ }
+}
+
+STUN = add_spell
+{
+ ["name"] = "Stun",
+ ["school"] = {SCHOOL_MIND},
+ ["level"] = 15,
+ ["mana"] = 10,
+ ["mana_max"] = 90,
+ ["fail"] = 45,
+ ["spell"] = function()
+ if get_level(STUN, 50) >= 20 then
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_ball(GF_STUN, dir, 10 + get_level(STUN, 150), 3)
+ else
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_bolt(GF_STUN, dir, 10 + get_level(STUN, 150))
+ end
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(STUN, 150))
+ end,
+ ["desc"] = {
+ "Tries to manipulate the mind of a monster to stun it",
+ "At level 20 it turns into a ball",
+ }
+}
diff --git a/lib/scpt/s_music.lua b/lib/scpt/s_music.lua
new file mode 100644
index 00000000..9da6393a
--- /dev/null
+++ b/lib/scpt/s_music.lua
@@ -0,0 +1,443 @@
+-- handle the music school
+-- *ALL* lasting spell must return the mana cost in the lasting function
+
+MUSIC_STOP = add_spell
+{
+ ["name"] = "Stop singing(I)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = -400,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 1,
+ ["blind"] = FALSE,
+ ["spell"] = function()
+ player.start_lasting_spell(0)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Stops the current song, if any."
+ }
+}
+
+--- Drums
+MUSIC_HOLD = add_spell
+{
+ ["name"] = "Holding Pattern(I)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 10,
+ ["fail"] = 20,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 1,
+ ["blind"] = FALSE,
+ ["lasting"] = function()
+ project_los(GF_OLD_SLOW, 10 + get_level(MUSIC_HOLD, 100))
+ return get_mana(MUSIC_HOLD)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_HOLD)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(MUSIC_HOLD, 100))
+ end,
+ ["desc"] = {
+ "Slows down all monsters listening the song.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+MUSIC_CONF = add_spell
+{
+ ["name"] = "Illusion Pattern(II)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 5,
+ ["mana"] = 2,
+ ["mana_max"] = 15,
+ ["fail"] = 30,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 2,
+ ["blind"] = FALSE,
+ ["lasting"] = function()
+ project_los(GF_OLD_CONF, 10 + get_level(MUSIC_CONF, 100))
+ return get_mana(MUSIC_CONF)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_CONF)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(MUSIC_CONF, 100))
+ end,
+ ["desc"] = {
+ "Tries to confuse all monsters listening the song.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+MUSIC_STUN = add_spell
+{
+ ["name"] = "Stun Pattern(IV)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 10,
+ ["mana"] = 3,
+ ["mana_max"] = 25,
+ ["fail"] = 45,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 4,
+ ["blind"] = FALSE,
+ ["lasting"] = function()
+ project_los(GF_STUN, 10 + get_level(MUSIC_STUN, 90))
+ return get_mana(MUSIC_STUN)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_STUN)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(MUSIC_STUN, 90))
+ end,
+ ["desc"] = {
+ "Stuns all monsters listening the song.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+--- Harps
+MUSIC_LITE = add_spell
+{
+ ["name"] = "Song of the Sun(I)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 1,
+ ["fail"] = 20,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["blind"] = FALSE,
+ ["pval"] = 1,
+ ["lasting"] = function()
+ set_lite(5)
+ return 1
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_LITE)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Provides light as long as you sing.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+MUSIC_HEAL = add_spell
+{
+ ["name"] = "Flow of Life(II)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 7,
+ ["mana"] = 5,
+ ["mana_max"] = 30,
+ ["fail"] = 35,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 2,
+ ["lasting"] = function()
+ hp_player(7 + get_level(MUSIC_HEAL, 100))
+ return get_mana(MUSIC_HEAL)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_HEAL)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "heal "..(7 + get_level(MUSIC_HEAL, 100)).."/turn"
+ end,
+ ["desc"] = {
+ "Heals you as long as you sing.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+MUSIC_HERO = add_spell
+{
+ ["name"] = "Heroic Ballad(II)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 10,
+ ["mana"] = 4,
+ ["mana_max"] = 14,
+ ["fail"] = 45,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 2,
+ ["lasting"] = function()
+ set_hero(5)
+ if get_level(MUSIC_HERO) >= 10 then
+ set_shero(5)
+ end
+ if get_level(MUSIC_HERO) >= 20 then
+ set_strike(5)
+ end
+ if get_level(MUSIC_HERO) >= 25 then
+ set_oppose_cc(5)
+ end
+ return get_mana(MUSIC_HERO)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_HERO)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Increases melee accuracy",
+ "At level 10 it increases it even more and reduces armour a bit",
+ "At level 20 it increases it again",
+ "At level 25 it grants protection against chaos and confusion",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+MUSIC_TIME = add_spell
+{
+ ["name"] = "Hobbit Melodies(III)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 20,
+ ["mana"] = 10,
+ ["mana_max"] = 30,
+ ["fail"] = 70,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 3,
+ ["lasting"] = function()
+ set_shield(5, 10 + get_level(MUSIC_TIME, 50), 0, 0, 0)
+ if get_level(MUSIC_TIME) >= 15 then
+ set_fast(5, 7 + get_level(MUSIC_TIME, 10))
+ end
+ return get_mana(MUSIC_TIME)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_TIME)
+ return TRUE
+ end,
+ ["info"] = function()
+ if get_level(MUSIC_TIME) >= 15 then
+ return "AC "..(10 + get_level(MUSIC_TIME, 50)).." speed "..(7 + get_level(MUSIC_TIME, 10))
+ else
+ return "AC "..(10 + get_level(MUSIC_TIME, 50))
+ end
+ end,
+ ["desc"] = {
+ "Greatly increases your reflexes allowing you to block more melee blows.",
+ "At level 15 it also makes you faster.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+MUSIC_MIND = add_spell
+{
+ ["name"] = "Clairaudience(IV)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 25,
+ ["mana"] = 15,
+ ["mana_max"] = 30,
+ ["fail"] = 75,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 4,
+ ["lasting"] = function()
+ set_tim_esp(5)
+ if get_level(MUSIC_MIND) >= 10 then
+ fire_ball(GF_IDENTIFY, 0, 1, 1 + get_level(MUSIC_MIND, 3, 0))
+ end
+ return get_mana(MUSIC_MIND)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_MIND)
+ return TRUE
+ end,
+ ["info"] = function()
+ if get_level(MUSIC_MIND) >= 10 then
+ return "rad "..(1 + get_level(MUSIC_MIND, 3, 0))
+ else
+ return ""
+ end
+ end,
+ ["desc"] = {
+ "Allows you to sense monster minds as long as you sing.",
+ "At level 10 it identifies all objects in a radius on the floor,",
+ "as well as probing monsters in that radius.",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+--- Horns
+
+MUSIC_BLOW = add_spell
+{
+ ["name"] = "Blow(I)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 4,
+ ["mana"] = 3,
+ ["mana_max"] = 30,
+ ["fail"] = 20,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 1,
+ ["spell"] = function()
+ fire_ball(GF_SOUND,
+ 0,
+ damroll(2 + get_level(MUSIC_BLOW, 10, 0), 4 + get_level(MUSIC_BLOW, 40, 0)),
+ 1 + get_level(MUSIC_BLOW, 12, 0)
+ )
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dam "..(2 + get_level(MUSIC_BLOW, 10, 0)).."d"..(4 + get_level(MUSIC_BLOW, 40, 0)).." rad "..(1 + get_level(MUSIC_BLOW, 12, 0))
+ end,
+ ["desc"] = {
+ "Produces a powerful, blowing, sound all around you.",
+ }
+}
+
+MUSIC_WIND = add_spell
+{
+ ["name"] = "Gush of Wind(II)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 14,
+ ["mana"] = 15,
+ ["mana_max"] = 45,
+ ["fail"] = 30,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 2,
+ ["spell"] = function()
+ fire_ball(GF_AWAY_ALL,
+ 0,
+ 10 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0)
+ )
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dist "..(10 + get_level(MUSIC_BLOW, 40, 0)).." rad "..(1 + get_level(MUSIC_BLOW, 12, 0))
+ end,
+ ["desc"] = {
+ "Produces a outgoing gush of wind that sends monsters away.",
+ }
+}
+
+MUSIC_YLMIR = add_spell
+{
+ ["name"] = "Horns of Ylmir(III)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 20,
+ ["mana"] = 25,
+ ["mana_max"] = 30,
+ ["fail"] = 20,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 3,
+ ["spell"] = function()
+ earthquake(player.py, player.px, 2 + get_level(SHAKE, 10))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "rad "..(2 + get_level(SHAKE, 10))
+ end,
+ ["desc"] = {
+ "Produces an earth shaking sound.",
+ }
+}
+
+MUSIC_AMBARKANTA = add_spell
+{
+ ["name"] = "Ambarkanta(IV)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 25,
+ ["mana"] = 70,
+ ["mana_max"] = 70,
+ ["fail"] = 60,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 4,
+ ["spell"] = function()
+ alter_reality()
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Produces a reality shaking sound that transports you to a nearly",
+ "identical reality.",
+ }
+}
+
+
+--[[
+MUSIC_ = add_spell
+{
+ ["name"] = "(I)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 20,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 1,
+ ["lasting"] = function()
+ return get_mana(MUSIC_)
+ end,
+ ["spell"] = function()
+ player.start_lasting_spell(MUSIC_)
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "",
+ "Consumes the amount of mana each turn.",
+ }
+}
+
+or
+
+MUSIC_ = add_spell
+{
+ ["name"] = "(I)",
+ ["school"] = {SCHOOL_MUSIC},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 20,
+ ["stat"] = A_CHR,
+ ["random"] = SKILL_MUSIC,
+ ["pval"] = 1,
+ ["spell"] = function()
+
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "",
+ }
+}
+]]
diff --git a/lib/scpt/s_nature.lua b/lib/scpt/s_nature.lua
new file mode 100644
index 00000000..e71a89bf
--- /dev/null
+++ b/lib/scpt/s_nature.lua
@@ -0,0 +1,152 @@
+-- handle the nature school
+
+GROWTREE = add_spell
+{
+ ["name"] = "Grow Trees",
+ ["school"] = {SCHOOL_NATURE, SCHOOL_TEMPORAL},
+ ["level"] = 6,
+ ["mana"] = 6,
+ ["mana_max"] = 30,
+ ["fail"] = 35,
+ ["inertia"] = { 5, 50 },
+ ["spell"] = function()
+ grow_trees(2 + get_level(GROWTREE, 7))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "rad "..(2 + get_level(GROWTREE, 7))
+ end,
+ ["desc"] = {
+ "Makes trees grow extremely quickly around you",
+ }
+}
+
+HEALING = add_spell
+{
+ ["name"] = "Healing",
+ ["school"] = {SCHOOL_NATURE},
+ ["level"] = 10,
+ ["mana"] = 15,
+ ["mana_max"] = 50,
+ ["fail"] = 45,
+ ["stick"] =
+ {
+ ["charge"] = { 2, 3 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 90,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 20, 40 },
+ },
+ },
+ ["spell"] = function()
+ return hp_player(player.mhp * (15 + get_level(HEALING, 35)) / 100)
+ end,
+ ["info"] = function()
+ return "heal "..(15 + get_level(HEALING, 35)).."% = "..(player.mhp * (15 + get_level(HEALING, 35)) / 100).."hp"
+ end,
+ ["desc"] = {
+ "Heals a percent of hitpoints",
+ }
+}
+
+RECOVERY = add_spell
+{
+ ["name"] = "Recovery",
+ ["school"] = {SCHOOL_NATURE},
+ ["level"] = 15,
+ ["mana"] = 10,
+ ["mana_max"] = 25,
+ ["fail"] = 60,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 10 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 50,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 10, 30 },
+ },
+ },
+ ["inertia"] = { 2, 100 },
+ ["spell"] = function()
+ local obvious
+ obvious = set_poisoned(player.poisoned / 2)
+ if get_level(RECOVERY, 50) >= 5 then
+ obvious = is_obvious(set_poisoned(0), obvious)
+ obvious = is_obvious(set_cut(0), obvious)
+ end
+ if get_level(RECOVERY, 50) >= 10 then
+ obvious = is_obvious(do_res_stat(A_STR, TRUE), obvious)
+ obvious = is_obvious(do_res_stat(A_CON, TRUE), obvious)
+ obvious = is_obvious(do_res_stat(A_DEX, TRUE), obvious)
+ obvious = is_obvious(do_res_stat(A_WIS, TRUE), obvious)
+ obvious = is_obvious(do_res_stat(A_INT, TRUE), obvious)
+ obvious = is_obvious(do_res_stat(A_CHR, TRUE), obvious)
+ end
+ if get_level(RECOVERY, 50) >= 15 then
+ obvious = is_obvious(restore_level(), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Reduces the length of time that you are poisoned",
+ "At level 5 it cures poison and cuts",
+ "At level 10 it restores drained stats",
+ "At level 15 it restores lost experience"
+ }
+}
+
+REGENERATION = add_spell
+{
+ ["name"] = "Regeneration",
+ ["school"] = {SCHOOL_NATURE},
+ ["level"] = 20,
+ ["mana"] = 30,
+ ["mana_max"] = 55,
+ ["fail"] = 70,
+ ["inertia"] = { 4, 40 },
+ ["spell"] = function()
+ if player.tim_regen == 0 then return set_tim_regen(randint(10) + 5 + get_level(REGENERATION, 50), 300 + get_level(REGENERATION, 700)) end
+ end,
+ ["info"] = function()
+ return "dur "..(5 + get_level(REGENERATION, 50)).."+d10 power "..(300 + get_level(REGENERATION, 700))
+ end,
+ ["desc"] = {
+ "Increases your body's regeneration rate",
+ }
+}
+
+
+SUMMONANNIMAL = add_spell
+{
+ ["name"] = "Summon Animal",
+ ["school"] = {SCHOOL_NATURE},
+ ["level"] = 25,
+ ["mana"] = 25,
+ ["mana_max"] = 50,
+ ["fail"] = 90,
+ ["stick"] =
+ {
+ ["charge"] = { 1, 3 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 85,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 15, 45 },
+ },
+ },
+ ["spell"] = function()
+ summon_specific_level = 25 + get_level(SUMMONANNIMAL, 50)
+ return summon_monster(player.py, player.px, dun_level, TRUE, SUMMON_ANIMAL)
+ end,
+ ["info"] = function()
+ return "level "..(25 + get_level(SUMMONANNIMAL, 50))
+ end,
+ ["desc"] = {
+ "Summons a leveled animal to your aid",
+ }
+}
diff --git a/lib/scpt/s_stick.lua b/lib/scpt/s_stick.lua
new file mode 100644
index 00000000..e9d994e9
--- /dev/null
+++ b/lib/scpt/s_stick.lua
@@ -0,0 +1,444 @@
+-- Spells that are stick or artifacts/... only
+
+DEVICE_HEAL_MONSTER = add_spell
+{
+ ["name"] = "Heal Monster",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 3,
+ ["mana"] = 5,
+ ["mana_max"] = 20,
+ ["fail"] = 15,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 10, 10 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 17,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ return fire_ball(GF_OLD_HEAL, dir, 20 + get_level(DEVICE_HEAL_MONSTER, 380), 0)
+ end,
+ ["info"] = function()
+ return "heal "..(20 + get_level(DEVICE_HEAL_MONSTER, 380))
+ end,
+ ["desc"] = {
+ "Heals a monster",
+ }
+}
+
+DEVICE_SPEED_MONSTER = add_spell
+{
+ ["name"] = "Haste Monster",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 10,
+ ["mana"] = 10,
+ ["mana_max"] = 10,
+ ["fail"] = 30,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 10, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 7,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ return fire_ball(GF_OLD_SPEED, dir, 1, 0)
+ end,
+ ["info"] = function()
+ return "speed +10"
+ end,
+ ["desc"] = {
+ "Haste a monster",
+ }
+}
+
+DEVICE_WISH = add_spell
+{
+ ["name"] = "Wish",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 50,
+ ["mana"] = 400,
+ ["mana_max"] = 400,
+ ["fail"] = 99,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 1, 2 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 98,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 1, 1 },
+ },
+ },
+ ["spell"] = function()
+ make_wish()
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "This grants you a wish, beware of what you ask for!",
+ }
+}
+
+DEVICE_SUMMON = add_spell
+{
+ ["name"] = "Summon",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 5,
+ ["mana"] = 5,
+ ["mana_max"] = 25,
+ ["fail"] = 20,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 1, 20 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 13,
+ ["base_level"] = { 1, 40 },
+ ["max_level"] = { 25, 50 },
+ },
+ },
+ ["spell"] = function()
+ local i, obvious
+ obvious = nil
+ for i = 1, 4 + get_level(DEVICE_SUMMON, 30) do
+ obvious = is_obvious(summon_specific(player.py, player.px, dun_level, 0), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Summons hostile monsters near you",
+ }
+}
+
+DEVICE_MANA = add_spell
+{
+ ["name"] = "Mana",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 30,
+ ["mana"] = 1,
+ ["mana_max"] = 1,
+ ["fail"] = 80,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 2, 3 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 78,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 20, 35 },
+ },
+ },
+ ["spell"] = function()
+ increase_mana((player.msp * (20 + get_level(DEVICE_MANA, 50))) / 100)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "restore "..(20 + get_level(DEVICE_MANA, 50)).."%"
+ end,
+ ["desc"] = {
+ "Restores a part(or all) of your mana",
+ }
+}
+
+DEVICE_NOTHING = add_spell
+{
+ ["name"] = "Nothing",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 0,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 0, 0 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 3,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 1, 1 },
+ },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 3,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 1, 1},
+ },
+ },
+ ["spell"] = function()
+ return FALSE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "It does nothing.",
+ }
+}
+
+DEVICE_LEBOHAUM = add_spell
+{
+ ["name"] = "Artifact Lebauhaum",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 0,
+ ["random"] = -1,
+ ["activate"] = 3,
+ ["spell"] = function()
+ msg_print("You hear a little song in your head in some unknown tongue:")
+ msg_print("'Avec le casque Lebohaum y a jamais d'anicroches, je parcours les dongeons,")
+ msg_print("j'en prend plein la caboche. Avec le casque Lebohaum, tout ces monstres a la")
+ msg_print("con, je leur met bien profond: c'est moi le maitre du dongeon!'")
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "sing a cheerful song",
+ }
+}
+
+DEVICE_MAGGOT = add_spell
+{
+ ["name"] = "Artifact Maggot",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 7,
+ ["mana_max"] = 7,
+ ["fail"] = 20,
+ ["random"] = -1,
+ ["activate"] = { 10, 50 },
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_ball(GF_TURN_ALL, dir, 40, 2)
+ end,
+ ["info"] = function()
+ return "power 40 rad 2"
+ end,
+ ["desc"] = {
+ "terrify",
+ }
+}
+
+DEVICE_HOLY_FIRE = add_spell
+{
+ ["name"] = "Holy Fire of Mithrandir",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 30,
+ ["mana"] = 50,
+ ["mana_max"] = 150,
+ ["fail"] = 75,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 2, 5 },
+ [TV_STAFF] =
+ {
+ -- Rarity higher than 100 to be sure to not have it generated randomly
+ ["rarity"] = 999,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 35, 35 },
+ },
+ },
+ ["spell"] = function()
+ return project_los(GF_HOLY_FIRE, 50 + get_level(DEVICE_HOLY_FIRE, 300))
+ end,
+ ["info"] = function()
+ return "dam "..(50 + get_level(DEVICE_HOLY_FIRE, 250))
+ end,
+ ["desc"] = {
+ "The Holy Fire created by this staff will deeply(double damage) burn",
+ "all that is evil.",
+ }
+}
+
+-- Ok the Eternal Flame, to craete one of the 4 ultimate arts
+-- needed to enter the last level of the Void
+DEVICE_ETERNAL_FLAME = add_spell
+{
+ ["name"] = "Artifact Eternal Flame",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 0,
+ ["random"] = -1,
+ ["activate"] = { 0, 0 },
+ ["spell"] = function(flame_item)
+ local ret, item, obj
+
+ ret, item = get_item("Which object do you want to imbue?",
+ "You have no objects to imbue.",
+ bor(USE_INVEN),
+ function (obj)
+ if obj.name1 > 0 or obj.name2 > 0 then return FALSE end
+ if (obj.tval == TV_SWORD) and (obj.sval == SV_LONG_SWORD) then
+ return TRUE
+ elseif (obj.tval == TV_MSTAFF) and (obj.sval == SV_MSTAFF) then
+ return TRUE
+ elseif (obj.tval == TV_BOW) and (obj.sval == SV_HEAVY_XBOW) then
+ return TRUE
+ elseif (obj.tval == TV_DRAG_ARMOR) and (obj.sval == SV_DRAGON_POWER) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+ if ret == FALSE then return FALSE end
+
+ obj = get_object(item)
+
+ if (obj.tval == TV_SWORD) and (obj.sval == SV_LONG_SWORD) then
+ obj.name1 = 147
+ elseif (obj.tval == TV_MSTAFF) and (obj.sval == SV_MSTAFF) then
+ obj.name1 = 127
+ elseif (obj.tval == TV_BOW) and (obj.sval == SV_HEAVY_XBOW) then
+ obj.name1 = 152
+ elseif (obj.tval == TV_DRAG_ARMOR) and (obj.sval == SV_DRAGON_POWER) then
+ obj.name1 = 17
+ end
+ apply_magic(obj, -1, TRUE, TRUE, TRUE)
+
+ obj.found = OBJ_FOUND_SELFMADE
+
+ inven_item_increase(flame_item, -1)
+ inven_item_describe(flame_item)
+ inven_item_optimize(flame_item)
+
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "imbuing an object with the eternal fire",
+ }
+}
+
+-- And one more silly activation :)
+DEVICE_DURANDIL = add_spell
+{
+ ["name"] = "Artifact Durandil",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 0,
+ ["random"] = -1,
+ ["activate"] = 3,
+ ["spell"] = function()
+ msg_print("You hear a little song in your head in some unknown tongue:")
+ msg_print("'Les epees Durandils sont forgees dans les mines par des nains.")
+ msg_print("Avec ca c'est facile de tuer un troll avec une seule main. Pas besoin")
+ msg_print("de super entrainement nis de niveau 28. Quand tu sors l'instrument")
+ msg_print("c'est l'ennemi qui prend la fuite! Avec ton epee Durandil quand tu")
+ msg_print("parcours les chemins, tu massacre sans peine les brigands et les gobelins,")
+ msg_print("les rats geants, les ogres mutants, les zombies et les liches, tu les")
+ msg_print("decoupe en tranches comme si c'etait des parts de quiches.")
+ msg_print("Les epees Durandil! Les epees Durandil!")
+ msg_print("Quand tu la sort dans un dongeon au moins t'as pas l'air debile.")
+ msg_print("C'est l'arme des bourins qui savent etre subtils.")
+ msg_print("Ne partez pas a l'aventure sans votre epee Durandil!'")
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "sing a cheerful song",
+ }
+}
+
+DEVICE_THUNDERLORDS = add_spell
+{
+ ["name"] = "Artifact Thunderlords",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 1,
+ ["fail"] = 20,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 3 },
+ [TV_STAFF] =
+ {
+ -- Rarity higher than 100 to be sure to not have it generated randomly
+ ["rarity"] = 999,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 1, 1 },
+ },
+ },
+ ["spell"] = function()
+ if dun_level > 0 then
+ msg_print("As you blow the horn a thunderlord pops out of nowhere and grabs you.")
+ recall_player(0, 1)
+ else
+ msg_print("You cannot use it there.")
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "A thunderlord will appear to transport you quickly to the surface.",
+ }
+}
+
+--[[ Template
+DEVICE_ = add_spell
+{
+ ["name"] = "",
+ ["school"] = {SCHOOL_DEVICE},
+ ["level"] = 1,
+ ["mana"] = 2,
+ ["mana_max"] = 15,
+ ["fail"] = 10,
+ ["random"] = -1,
+ ["stick"] =
+ {
+ ["charge"] = { 10, 5 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 7,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 25, 50 },
+ },
+ },
+ ["spell"] = function()
+ return FALSE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "",
+ }
+}
+]]
diff --git a/lib/scpt/s_tempo.lua b/lib/scpt/s_tempo.lua
new file mode 100644
index 00000000..d3d2fbb5
--- /dev/null
+++ b/lib/scpt/s_tempo.lua
@@ -0,0 +1,162 @@
+-- Handles thhe temporal school
+
+
+MAGELOCK = add_spell
+{
+ ["name"] = "Magelock",
+ ["school"] = {SCHOOL_TEMPORAL},
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 35,
+ ["fail"] = 10,
+ ["stick"] =
+ {
+ ["charge"] = { 7, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 30,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 15, 45 },
+ },
+ },
+ ["spell"] = function()
+ if get_level(MAGELOCK, 50) >= 30 then
+ local ret, x, y, c_ptr
+
+ if get_level(MAGELOCK, 50) >= 40 then
+ ret, x, y = tgt_pt()
+ if ret == FALSE then return end
+ if cave_is(cave(y, x), FF1_FLOOR) == FALSE or cave_is(cave(y, x), FF1_PERMANENT) == TRUE or los(player.py, player.px, y, x) == FALSE then
+ msg_print("You cannot place it there.")
+ return TRUE
+ end
+ else
+ y = player.py
+ x = player.px
+ end
+ cave_set_feat(y, x, 3)
+ return TRUE
+ else
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return wizard_lock(dir)
+ end
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Magically locks a door",
+ "At level 30 it creates a glyph of warding",
+ "At level 40 the glyph can be placed anywhere in the field of vision"
+ }
+}
+
+SLOWMONSTER = add_spell
+{
+ ["name"] = "Slow Monster",
+ ["school"] = {SCHOOL_TEMPORAL},
+ ["level"] = 10,
+ ["mana"] = 10,
+ ["mana_max"] = 15,
+ ["fail"] = 35,
+ ["stick"] =
+ {
+ ["charge"] = { 5, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 23,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["spell"] = function()
+ local ret, dir
+
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ if get_level(SLOWMONSTER, 50) >= 20 then
+ return fire_ball(GF_OLD_SLOW, dir, 40 + get_level(SLOWMONSTER, 160), 1)
+ else
+ return fire_bolt(GF_OLD_SLOW, dir, 40 + get_level(SLOWMONSTER, 160))
+ end
+ end,
+ ["info"] = function()
+ if get_level(SLOWMONSTER, 50) >= 20 then
+ return "power "..(40 + get_level(SLOWMONSTER, 160)).." rad 1"
+ else
+ return "power "..(40 + get_level(SLOWMONSTER, 160))
+ end
+ end,
+ ["desc"] = {
+ "Magically slows down the passing of time around a monster",
+ "At level 20 it affects a zone"
+ }
+}
+
+ESSENCESPEED = add_spell
+{
+ ["name"] = "Essence of Speed",
+ ["school"] = {SCHOOL_TEMPORAL},
+ ["level"] = 15,
+ ["mana"] = 20,
+ ["mana_max"] = 40,
+ ["fail"] = 50,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 3 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 80,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 10, 39 },
+ },
+ },
+ ["inertia"] = { 5, 20 },
+ ["spell"] = function()
+ if player.fast == 0 then return set_fast(10 + randint(10) + get_level(ESSENCESPEED, 50), 5 + get_level(ESSENCESPEED, 20)) end
+ end,
+ ["info"] = function()
+ return "dur "..(10 + get_level(ESSENCESPEED, 50)).."+d10 speed "..(5 + get_level(ESSENCESPEED, 20))
+ end,
+ ["desc"] = {
+ "Magically decreases the passing of time around you, making you move faster with",
+ "respect to the rest of the universe."
+ }
+}
+
+BANISHMENT = add_spell
+{
+ ["name"] = "Banishment",
+ ["school"] = {SCHOOL_TEMPORAL, SCHOOL_CONVEYANCE},
+ ["level"] = 30,
+ ["mana"] = 30,
+ ["mana_max"] = 40,
+ ["fail"] = 95,
+ ["stick"] =
+ {
+ ["charge"] = { 1, 3 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 98,
+ ["base_level"] = { 1, 15 },
+ ["max_level"] = { 10, 36 },
+ },
+ },
+ ["inertia"] = { 5, 50 },
+ ["spell"] = function()
+ local obvious
+ obvious = project_los(GF_AWAY_ALL, 40 + get_level(BANISHMENT, 160))
+ if get_level(BANISHMENT, 50) >= 15 then
+ obvious = is_obvious(project_los(GF_STASIS, 20 + get_level(BANISHMENT, 120)), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ return "power "..(40 + get_level(BANISHMENT, 160))
+ end,
+ ["desc"] = {
+ "Disrupts the space/time continuum in your area and teleports all monsters away.",
+ "At level 15 it may also lock them in a time bubble for a while."
+ }
+}
diff --git a/lib/scpt/s_tulkas.lua b/lib/scpt/s_tulkas.lua
new file mode 100644
index 00000000..4afa8082
--- /dev/null
+++ b/lib/scpt/s_tulkas.lua
@@ -0,0 +1,81 @@
+-- Handle Tulkas magic school
+
+TULKAS_AIM = add_spell
+{
+ ["name"] = "Divine Aim",
+ ["school"] = {SCHOOL_TULKAS},
+ ["level"] = 1,
+ ["mana"] = 30,
+ ["mana_max"] = 500,
+ ["fail"] = 20,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local dur = get_level(TULKAS_AIM, 50) + randint(10)
+ local obvious
+
+ obvious = set_strike(dur)
+ if get_level(TULKAS_AIM) >= 20 then
+ obvious = is_obvious(set_tim_deadly(dur), obvious)
+ end
+ return obvious
+ end,
+ ["info"] = function()
+ return "dur "..(get_level(TULKAS_AIM, 50)).."+d10"
+ end,
+ ["desc"] = {
+ "It makes you more accurate in combat",
+ "At level 20 all your blows are critical hits",
+ }
+}
+
+TULKAS_WAVE = add_spell
+{
+ ["name"] = "Wave of Power",
+ ["school"] = {SCHOOL_TULKAS},
+ ["level"] = 20,
+ ["mana"] = 200,
+ ["mana_max"] = 200,
+ ["fail"] = 75,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ return fire_bolt(GF_ATTACK, dir, get_level(TULKAS_WAVE, player.num_blow))
+ end,
+ ["info"] = function()
+ return "blows "..(get_level(TULKAS_WAVE, player.num_blow))
+ end,
+ ["desc"] = {
+ "It allows you to project a number of melee blows across a distance",
+ }
+}
+
+TULKAS_SPIN = add_spell
+{
+ ["name"] = "Whirlwind",
+ ["school"] = {SCHOOL_TULKAS},
+ ["level"] = 10,
+ ["mana"] = 100,
+ ["mana_max"] = 100,
+ ["fail"] = 45,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ return fire_ball(GF_ATTACK, 0, 1, 1)
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "It allows you to spin around and hit all monsters nearby",
+ }
+}
diff --git a/lib/scpt/s_udun.lua b/lib/scpt/s_udun.lua
new file mode 100644
index 00000000..c4266239
--- /dev/null
+++ b/lib/scpt/s_udun.lua
@@ -0,0 +1,180 @@
+-- handle the udun school
+
+DRAIN = add_spell
+{
+ ["name"] = "Drain",
+ ["school"] = {SCHOOL_UDUN, SCHOOL_MANA},
+ ["level"] = 1,
+ ["mana"] = 0,
+ ["mana_max"] = 0,
+ ["fail"] = 20,
+ ["spell"] = function()
+ local ret, item, obj, o_name, add
+
+ -- Ask for an item
+ ret, item = get_item("What item to drain?", "You have nothing you can drain", USE_INVEN,
+ function (obj)
+ if (obj.tval == TV_WAND) or (obj.tval == TV_ROD_MAIN) or (obj.tval == TV_STAFF) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+
+ if ret == TRUE then
+ -- get the item
+ obj = get_object(item)
+
+ add = 0
+ if (obj.tval == TV_STAFF) or (obj.tval == TV_WAND) then
+ local kind = get_kind(obj)
+
+ add = kind.level * obj.pval * obj.number
+
+ -- Destroy it!
+ inven_item_increase(item, -99)
+ inven_item_describe(item)
+ inven_item_optimize(item)
+ end
+ if obj.tval == TV_ROD_MAIN then
+ add = obj.timeout
+ obj.timeout = 0;
+
+ --Combine / Reorder the pack (later)
+ player.notice = bor(player.notice, PN_COMBINE, PN_REORDER)
+ player.window = bor(player.window, PW_INVEN, PW_EQUIP, PW_PLAYER)
+ end
+ increase_mana(add)
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Drains the mana contained in wands, staves and rods to increase yours",
+ }
+}
+
+GENOCIDE = add_spell
+{
+ ["name"] = "Genocide",
+ ["school"] = {SCHOOL_UDUN, SCHOOL_NATURE},
+ ["level"] = 25,
+ ["mana"] = 50,
+ ["mana_max"] = 50,
+ ["fail"] = 90,
+ ["stick"] =
+ {
+ ["charge"] = { 2, 2 },
+ [TV_STAFF] =
+ {
+ ["rarity"] = 85,
+ ["base_level"] = { 1, 1 },
+ ["max_level"] = { 5, 15 },
+ },
+ },
+ ["spell"] = function()
+ local type
+
+ type = 0
+ if get_level(GENOCIDE) >= 10 then type = 1 end
+ if type == 0 then
+ genocide(TRUE)
+ return TRUE
+ else
+ if get_check("Genocide all monsters near you? ") == TRUE then
+ mass_genocide(TRUE)
+ else
+ genocide(TRUE)
+ end
+ return TRUE
+ end
+ end,
+ ["info"] = function()
+ return ""
+ end,
+ ["desc"] = {
+ "Genocides all monsters of a race on the level",
+ "At level 10 it can genocide all monsters near you"
+ }
+}
+
+WRAITHFORM = add_spell
+{
+ ["name"] = "Wraithform",
+ ["school"] = {SCHOOL_UDUN, SCHOOL_CONVEYANCE},
+ ["level"] = 30,
+ ["mana"] = 20,
+ ["mana_max"] = 40,
+ ["fail"] = 95,
+ ["inertia"] = { 4, 30 },
+ ["spell"] = function()
+ return set_shadow(randint(30) + 20 + get_level(WRAITHFORM, 40))
+ end,
+ ["info"] = function()
+ return "dur "..(20 + get_level(WRAITHFORM, 40)).."+d30"
+ end,
+ ["desc"] = {
+ "Turns you into an immaterial being",
+ }
+}
+
+FLAMEOFUDUN = add_spell
+{
+ ["name"] = "Flame of Udun",
+ ["school"] = {SCHOOL_UDUN, SCHOOL_FIRE},
+ ["level"] = 35,
+ ["mana"] = 70,
+ ["mana_max"] = 100,
+ ["fail"] = 95,
+ ["inertia"] = { 7, 15 },
+ ["spell"] = function()
+ return set_mimic(randint(15) + 5 + get_level(FLAMEOFUDUN, 30), resolve_mimic_name("Balrog"), get_level(FLAMEOFUDUN))
+ end,
+ ["info"] = function()
+ return "dur "..(5 + get_level(FLAMEOFUDUN, 30)).."+d15"
+ end,
+ ["desc"] = {
+ "Turns you into a powerful Balrog",
+ }
+}
+
+
+-- Return the number of Udun/Melkor spells in a given book
+function udun_in_book(sval, pval)
+ local i, y, index, sch, s
+
+ i = 0
+
+ -- Hack if the book is 255 it is a random book
+ if sval == 255 then
+ school_book[sval] = {pval}
+ end
+ -- Parse all spells
+ for index, s in school_book[sval] do
+ for index, sch in __spell_school[s] do
+ if sch == SCHOOL_UDUN then i = i + 1 end
+ if sch == SCHOOL_MELKOR then i = i + 1 end
+ end
+ end
+ return i
+end
+
+-- Return the total level of spells
+function levels_in_book(sval, pval)
+ local i, y, index, sch, s
+
+ i = 0
+
+ -- Hack if the book is 255 it is a random book
+ if sval == 255 then
+ school_book[sval] = {pval}
+ end
+
+ -- Parse all spells
+ for index, s in school_book[sval] do
+ i = i + __tmp_spells[s].level
+ end
+ return i
+end
diff --git a/lib/scpt/s_water.lua b/lib/scpt/s_water.lua
new file mode 100644
index 00000000..88305201
--- /dev/null
+++ b/lib/scpt/s_water.lua
@@ -0,0 +1,154 @@
+-- handle the water school
+
+TIDALWAVE = add_spell
+{
+ ["name"] = "Tidal Wave",
+ ["school"] = {SCHOOL_WATER},
+ ["level"] = 16,
+ ["mana"] = 16,
+ ["mana_max"] = 40,
+ ["fail"] = 65,
+ ["stick"] =
+ {
+ ["charge"] = { 6, 5 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 54,
+ ["base_level"] = { 1, 10 },
+ ["max_level"] = { 20, 50 },
+ },
+ },
+ ["inertia"] = { 4, 100 },
+ ["spell"] = function()
+ fire_wave(GF_WAVE, 0, 40 + get_level(TIDALWAVE, 200), 0, 6 + get_level(TIDALWAVE, 10), EFF_WAVE)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dam "..(40 + get_level(TIDALWAVE, 200)).." rad "..(6 + get_level(TIDALWAVE, 10))
+ end,
+ ["desc"] = {
+ "Summons a monstrous tidal wave that will expand and crush the",
+ "monsters under its mighty waves."
+ }
+}
+
+ICESTORM = add_spell
+{
+ ["name"] = "Ice Storm",
+ ["school"] = {SCHOOL_WATER},
+ ["level"] = 22,
+ ["mana"] = 30,
+ ["mana_max"] = 60,
+ ["fail"] = 80,
+ ["stick"] =
+ {
+ ["charge"] = { 3, 7 },
+ [TV_WAND] =
+ {
+ ["rarity"] = 65,
+ ["base_level"] = { 1, 5 },
+ ["max_level"] = { 25, 45 },
+ },
+ },
+ ["inertia"] = { 3, 40 },
+ ["spell"] = function()
+ local type
+
+ if get_level(ICESTORM, 50) >= 10 then type = GF_ICE
+ else type = GF_COLD end
+ fire_wave(type, 0, 80 + get_level(ICESTORM, 200), 1 + get_level(ICESTORM, 3, 0), 20 + get_level(ICESTORM, 70), EFF_STORM)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dam "..(80 + get_level(ICESTORM, 200)).." rad "..(1 + get_level(ICESTORM, 3, 0)).." dur "..(20 + get_level(ICESTORM, 70))
+ end,
+ ["desc"] = {
+ "Engulfs you in a storm of roaring cold that strikes your foes.",
+ "At level 10 it turns into shards of ice."
+ }
+}
+
+ENTPOTION = add_spell
+{
+ ["name"] = "Ent's Potion",
+ ["school"] = {SCHOOL_WATER},
+ ["level"] = 6,
+ ["mana"] = 7,
+ ["mana_max"] = 15,
+ ["fail"] = 35,
+ ["inertia"] = { 1, 30 },
+ ["spell"] = function()
+ set_food(PY_FOOD_MAX - 1)
+ msg_print("The Ent's Potion fills your stomach.")
+ if get_level(ENTPOTION, 50) >= 5 then
+ set_afraid(0)
+ end
+ if get_level(ENTPOTION, 50) >= 12 then
+ set_hero(player.hero + randint(25) + 25 + get_level(ENTPOTION, 40))
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ if get_level(ENTPOTION, 50) >= 12 then
+ return "dur "..(25 + get_level(ENTPOTION, 40)).."+d25"
+ else
+ return ""
+ end
+ end,
+ ["desc"] = {
+ "Fills up your stomach.",
+ "At level 5 it boldens your heart.",
+ "At level 12 it make you heroic."
+ }
+}
+
+VAPOR = add_spell
+{
+ ["name"] = "Vapor",
+ ["school"] = {SCHOOL_WATER},
+ ["level"] = 2,
+ ["mana"] = 2,
+ ["mana_max"] = 12,
+ ["fail"] = 20,
+ ["inertia"] = { 1, 30 },
+ ["spell"] = function()
+ fire_cloud(GF_WATER, 0, 3 + get_level(VAPOR, 20), 3 + get_level(VAPOR, 9, 0), 5)
+ return TRUE
+ end,
+ ["info"] = function()
+ return "dam "..(3 + get_level(VAPOR, 20)).." rad "..(3 + get_level(VAPOR, 9, 0)).." dur 5"
+ end,
+ ["desc"] = {
+ "Fills the air with toxic moisture to eradicate annoying critters."
+ }
+}
+
+function get_geyser_damage()
+ return get_level(GEYSER, 10), 3 + get_level(GEYSER, 35)
+end
+
+GEYSER = add_spell
+{
+ ["name"] = "Geyser",
+ ["school"] = SCHOOL_WATER,
+ ["level"] = 1,
+ ["mana"] = 1,
+ ["mana_max"] = 35,
+ ["fail"] = 5,
+ ["spell"] = function()
+ local ret, dir
+ ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+ return fire_bolt_or_beam(2 * get_level(GEYSER, 85), GF_WATER, dir, damroll(get_geyser_damage()))
+ end,
+ ["info"] = function()
+ local n, d
+ n, d = get_geyser_damage()
+ return "dam "..n.."d"..d
+ end,
+ ["desc"] =
+ {
+ "Shoots a geyser of water from your fingertips.",
+ "Sometimes it can blast through its first target."
+ },
+}
diff --git a/lib/scpt/s_yavann.lua b/lib/scpt/s_yavann.lua
new file mode 100644
index 00000000..2f594e85
--- /dev/null
+++ b/lib/scpt/s_yavann.lua
@@ -0,0 +1,157 @@
+-- Handle Yavanna kementari magic school
+
+YAVANNA_CHARM_ANIMAL = add_spell
+{
+ ["name"] = "Charm Animal",
+ ["school"] = {SCHOOL_YAVANNA},
+ ["level"] = 1,
+ ["mana"] = 10,
+ ["mana_max"] = 100,
+ ["fail"] = 30,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local ret, dir = get_aim_dir()
+ if ret == FALSE then return end
+
+ return fire_ball(GF_CONTROL_ANIMAL, dir, 10 + get_level(YAVANNA_CHARM_ANIMAL, 170), get_level(YAVANNA_CHARM_ANIMAL, 2))
+ end,
+ ["info"] = function()
+ return "power "..(10 + get_level(YAVANNA_CHARM_ANIMAL, 170)).." rad "..(get_level(YAVANNA_CHARM_ANIMAL, 2))
+ end,
+ ["desc"] = {
+ "It tries to tame an animal",
+ }
+}
+
+YAVANNA_GROW_GRASS = add_spell
+{
+ ["name"] = "Grow Grass",
+ ["school"] = {SCHOOL_YAVANNA},
+ ["level"] = 10,
+ ["mana"] = 70,
+ ["mana_max"] = 150,
+ ["fail"] = 65,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ grow_grass(get_level(YAVANNA_GROW_GRASS, 4))
+ return TRUE
+ end,
+ ["info"] = function()
+ return "rad "..(get_level(YAVANNA_GROW_GRASS, 4))
+ end,
+ ["desc"] = {
+ "Create a floor of grass around you. While on grass and praying",
+ "a worshipper of Yavanna will know a greater regeneration rate"
+ }
+}
+
+YAVANNA_TREE_ROOTS = add_spell
+{
+ ["name"] = "Tree Roots",
+ ["school"] = {SCHOOL_YAVANNA},
+ ["level"] = 15,
+ ["mana"] = 50,
+ ["mana_max"] = 1000,
+ ["fail"] = 70,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ return set_roots(10 + get_level(YAVANNA_TREE_ROOTS, 30), 10 + get_level(YAVANNA_TREE_ROOTS, 60), 10 + get_level(YAVANNA_TREE_ROOTS, 20))
+ end,
+ ["info"] = function()
+ return "dur "..(10 + get_level(YAVANNA_TREE_ROOTS, 30)).." AC "..(10 + get_level(YAVANNA_TREE_ROOTS, 60)).." dam "..(10 + get_level(YAVANNA_TREE_ROOTS, 20))
+ end,
+ ["desc"] = {
+ "Creates roots deep in the floor from your feet, making you more stable and able",
+ "to make stronger attacks, but prevents any movement (even teleportation).",
+ "It also makes you recover from stunning almost immediately."
+ }
+}
+
+YAVANNA_WATER_BITE = add_spell
+{
+ ["name"] = "Water Bite",
+ ["school"] = {SCHOOL_YAVANNA},
+ ["level"] = 20,
+ ["mana"] = 150,
+ ["mana_max"] = 300,
+ ["fail"] = 90,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local rad
+
+ rad = 0
+ if get_level(YAVANNA_WATER_BITE) >= 25 then rad = 1 end
+
+ return set_project(randint(30) + 30 + get_level(YAVANNA_WATER_BITE, 150),
+ GF_WATER,
+ 10 + get_level(YAVANNA_WATER_BITE),
+ rad,
+ bor(PROJECT_STOP, PROJECT_KILL))
+ end,
+ ["info"] = function()
+ return "dur "..(30 + get_level(YAVANNA_WATER_BITE, 150)).."+d30 dam "..(10 + get_level(YAVANNA_WATER_BITE)).."/blow"
+ end,
+ ["desc"] = {
+ "Imbues your melee weapon with a natural stream of water",
+ "At level 25, it spreads over a 1 radius zone around your target"
+ }
+}
+
+YAVANNA_UPROOT = add_spell
+{
+ ["name"] = "Uproot",
+ ["school"] = {SCHOOL_YAVANNA},
+ ["level"] = 35,
+ ["mana"] = 250,
+ ["mana_max"] = 350,
+ ["fail"] = 95,
+ -- Uses piety to cast
+ ["piety"] = TRUE,
+ ["stat"] = A_WIS,
+ ["random"] = SKILL_SPIRITUALITY,
+ ["spell"] = function()
+ local m_idx, x, y, c_ptr, ret, dir
+
+ ret, dir = get_rep_dir()
+ if ret == FALSE then return end
+ y, x = explode_dir(dir)
+ y, x = y + player.py, x + player.px
+ c_ptr = cave(y, x)
+
+ if c_ptr.feat == FEAT_TREES then
+ cave_set_feat(y, x, FEAT_GRASS);
+
+ -- Summon it
+ y, x = find_position(y, x)
+ m_idx = place_monster_one(y, x, test_monster_name("Ent"), 0, FALSE, MSTATUS_FRIEND)
+
+ -- level it
+ if m_idx ~= 0 then
+ monster_set_level(m_idx, 30 + get_level(YAVANNA_UPROOT, 70))
+ end
+
+ msg_print("The tree awakes!");
+ else
+ msg_print("There is no tree there.")
+ end
+ return TRUE
+ end,
+ ["info"] = function()
+ return "lev "..(30 + get_level(YAVANNA_UPROOT, 70))
+ end,
+ ["desc"] = {
+ "Awakes a tree to help you battle the forces of Morgoth",
+ }
+}
diff --git a/lib/scpt/spells.lua b/lib/scpt/spells.lua
new file mode 100644
index 00000000..8727d282
--- /dev/null
+++ b/lib/scpt/spells.lua
@@ -0,0 +1,475 @@
+--
+-- This file takes care of the schools of magic
+--
+
+-- Create the schools
+SCHOOL_MANA = add_school
+{
+ ["name"] = "Mana",
+ ["skill"] = SKILL_MANA,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Eru Iluvatar provides the Mana school at half the prayer skill
+ [GOD_ERU] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 2,
+ },
+ },
+ ["hooks"] =
+ {
+ [HOOK_CALC_MANA] = function(msp)
+ if get_skill(SKILL_MANA) >= 35 then
+ msp = msp + (msp * ((get_skill(SKILL_MANA) - 34)) / 100)
+ return TRUE, msp
+ end
+ end
+ },
+}
+SCHOOL_FIRE = add_school
+{
+ ["name"] = "Fire",
+ ["skill"] = SKILL_FIRE,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+}
+SCHOOL_AIR = add_school
+{
+ ["name"] = "Air",
+ ["skill"] = SKILL_AIR,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ if get_skill(SKILL_AIR) >= 50 then
+ player.magical_breath = TRUE
+ end
+ end,
+ },
+ ["gods"] =
+ {
+ -- Manwe Sulimo provides the Air school at 2/3 the prayer skill
+ [GOD_MANWE] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 2,
+ ["div"] = 3,
+ },
+ },
+}
+SCHOOL_WATER = add_school
+{
+ ["name"] = "Water",
+ ["skill"] = SKILL_WATER,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["hooks"] =
+ {
+ [HOOK_CALC_BONUS] = function()
+ if get_skill(SKILL_WATER) >= 30 then
+ player.water_breath = TRUE
+ end
+ end,
+ },
+ ["gods"] =
+ {
+ -- Yavanna Kementari provides the Water school at 1/2 the prayer skill
+ [GOD_YAVANNA] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 2,
+ },
+ },
+}
+SCHOOL_EARTH = add_school
+{
+ ["name"] = "Earth",
+ ["skill"] = SKILL_EARTH,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Tulkas provides the Earth school at 4/5 the prayer skill
+ [GOD_TULKAS] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 4,
+ ["div"] = 5,
+ },
+ -- Yavanna Kementari provides the Earth school at 1/2 the prayer skill
+ [GOD_YAVANNA] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 2,
+ },
+ },
+}
+SCHOOL_CONVEYANCE = add_school
+{
+ ["name"] = "Conveyance",
+ ["skill"] = SKILL_CONVEYANCE,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Manwe Sulimo provides the Conveyance school at 1/2 the prayer skill
+ [GOD_MANWE] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 2,
+ },
+ },
+}
+SCHOOL_GEOMANCY = add_school
+{
+ ["name"] = "Geomancy",
+ ["skill"] = SKILL_GEOMANCY,
+ ["spell_power"] = TRUE,
+ -- Require to wield a Mage Staff, as the spells requries the caster to stomp the floor with it
+ ["depend"] = function()
+ -- Require at least one point in each school
+ if get_skill(SKILL_FIRE) == 0 then return end
+ if get_skill(SKILL_AIR) == 0 then return end
+ if get_skill(SKILL_EARTH) == 0 then return end
+ if get_skill(SKILL_WATER) == 0 then return end
+
+ local obj = get_object(INVEN_WIELD)
+ if (obj.k_idx > 0) and (obj.tval == TV_MSTAFF) then return TRUE end
+ end,
+}
+SCHOOL_DIVINATION = add_school
+{
+ ["name"] = "Divination",
+ ["skill"] = SKILL_DIVINATION,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Eru Iluvatar provides the Divination school at 2/3 the prayer skill
+ [GOD_ERU] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 2,
+ ["div"] = 3,
+ },
+ },
+}
+SCHOOL_TEMPORAL = add_school
+{
+ ["name"] = "Temporal",
+ ["skill"] = SKILL_TEMPORAL,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Yavanna Kementari provides the Temoral school at 1/6 the prayer skill
+ [GOD_YAVANNA] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 6,
+ },
+ },
+}
+SCHOOL_NATURE = add_school
+{
+ ["name"] = "Nature",
+ ["skill"] = SKILL_NATURE,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Yavanna Kementari provides the Nature school at 1/2 the prayer skill
+ [GOD_YAVANNA] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 2,
+ },
+ },
+}
+SCHOOL_META = add_school
+{
+ ["name"] = "Meta",
+ ["skill"] = SKILL_META,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Manwe Sulimo provides the Meta school at 1/3 the prayer skill
+ [GOD_MANWE] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 3,
+ },
+ },
+}
+SCHOOL_MIND = add_school
+{
+ ["name"] = "Mind",
+ ["skill"] = SKILL_MIND,
+ ["spell_power"] = TRUE,
+ ["sorcery"] = TRUE,
+ ["gods"] =
+ {
+ -- Eru Iluvatar provides the Mind school at 1/3 the prayer skill
+ [GOD_ERU] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 3,
+ },
+ -- Melkor Bauglir provides the Mind school at 1/3 the prayer skill
+ [GOD_MELKOR] =
+ {
+ ["skill"] = SKILL_PRAY,
+ ["mul"] = 1,
+ ["div"] = 3,
+ },
+ },
+}
+SCHOOL_UDUN = add_school
+{
+ ["name"] = "Udun",
+ ["skill"] = SKILL_UDUN,
+ ["bonus_level"] = function()
+ return ((player.lev * 2) / 3)
+ end,
+}
+SCHOOL_DEMON = add_school
+{
+ ["name"] = "Demon",
+ ["skill"] = SKILL_DAEMON,
+ ["no_random"] = TRUE,
+}
+
+-- The God specific schools, all tied to the prayer skill
+SCHOOL_ERU = add_school
+{
+ ["name"] = "Eru Iluvatar",
+ ["skill"] = SKILL_PRAY,
+ ["spell_power"] = TRUE,
+ ["god"] = GOD_ERU,
+}
+SCHOOL_MANWE = add_school
+{
+ ["name"] = "Manwe Sulimo",
+ ["skill"] = SKILL_PRAY,
+ ["spell_power"] = TRUE,
+ ["god"] = GOD_MANWE,
+}
+SCHOOL_TULKAS = add_school
+{
+ ["name"] = "Tulkas",
+ ["skill"] = SKILL_PRAY,
+ ["spell_power"] = TRUE,
+ ["god"] = GOD_TULKAS,
+}
+SCHOOL_MELKOR = add_school
+{
+ ["name"] = "Melkor Bauglir",
+ ["skill"] = SKILL_PRAY,
+ ["spell_power"] = TRUE,
+ ["god"] = GOD_MELKOR,
+}
+SCHOOL_YAVANNA = add_school
+{
+ ["name"] = "Yavanna Kementari",
+ ["skill"] = SKILL_PRAY,
+ ["spell_power"] = TRUE,
+ ["god"] = GOD_YAVANNA,
+}
+
+-- Not a real school, rather a palcehodler for stick only spells
+SCHOOL_DEVICE = add_school
+{
+ ["name"] = "Device",
+ ["skill"] = SKILL_DEVICE,
+}
+
+-- Music "spells"
+SCHOOL_MUSIC = add_school
+{
+ ["name"] = "Music",
+ ["skill"] = SKILL_MUSIC,
+}
+
+-- Put some spells
+tome_dofile("s_fire.lua")
+tome_dofile("s_mana.lua")
+tome_dofile("s_water.lua")
+tome_dofile("s_air.lua")
+tome_dofile("s_earth.lua")
+tome_dofile("s_convey.lua")
+tome_dofile("s_nature.lua")
+tome_dofile("s_divin.lua")
+tome_dofile("s_tempo.lua")
+tome_dofile("s_meta.lua")
+tome_dofile("s_mind.lua")
+tome_dofile("s_udun.lua")
+tome_dofile("s_geom.lua")
+
+-- God's specific spells
+tome_dofile("s_eru.lua")
+tome_dofile("s_manwe.lua")
+tome_dofile("s_tulkas.lua")
+tome_dofile("s_melkor.lua")
+tome_dofile("s_yavann.lua")
+
+-- Specific schools
+tome_dofile("s_demon.lua")
+
+-- Device spells
+tome_dofile("s_stick.lua")
+
+-- Musics
+tome_dofile("s_music.lua")
+
+-- List of spellbooks
+
+-- Create the crystal of mana
+school_book[0] = {
+ MANATHRUST, DELCURSES, RESISTS, MANASHIELD,
+}
+
+-- The book of the eternal flame
+school_book[1] = {
+ GLOBELIGHT, FIREGOLEM, FIREFLASH, FIREWALL, FIERYAURA,
+}
+
+-- The book of the blowing winds
+school_book[2] = {
+ NOXIOUSCLOUD, POISONBLOOD, INVISIBILITY, STERILIZE, AIRWINGS, THUNDERSTORM,
+}
+
+-- The book of the impenetrable earth
+school_book[3] = {
+ STONESKIN, DIG, STONEPRISON, SHAKE, STRIKE,
+}
+
+-- The book of the unstopable wave
+school_book[4] = {
+ GEYSER, VAPOR, ENTPOTION, TIDALWAVE, ICESTORM
+}
+
+-- Create the book of translocation
+school_book[5] = {
+ BLINK, DISARM, TELEPORT, TELEAWAY, RECALL, PROBABILITY_TRAVEL,
+}
+
+-- Create the book of the tree
+school_book[6] = {
+ GROWTREE, HEALING, RECOVERY, REGENERATION, SUMMONANNIMAL,
+}
+
+-- Create the book of Knowledge
+school_book[7] = {
+ SENSEMONSTERS, SENSEHIDDEN, REVEALWAYS, IDENTIFY, VISION, STARIDENTIFY,
+}
+
+-- Create the book of the Time
+school_book[8] = {
+ MAGELOCK, SLOWMONSTER, ESSENCESPEED, BANISHMENT,
+}
+
+-- Create the book of meta spells
+school_book[9] = {
+ RECHARGE, DISPERSEMAGIC, SPELLBINDER, TRACKER, INERTIA_CONTROL,
+}
+
+-- Create the book of the mind
+school_book[10] = {
+ CHARM, CONFUSE, ARMOROFFEAR, STUN,
+}
+
+-- Create the book of hellflame
+school_book[11] = {
+ DRAIN, GENOCIDE, WRAITHFORM, FLAMEOFUDUN,
+}
+
+-- Create the book of eru
+school_book[20] = {
+ ERU_SEE, ERU_LISTEN, ERU_UNDERSTAND, ERU_PROT,
+}
+
+-- Create the book of manwe
+school_book[21] = {
+ MANWE_BLESS, MANWE_SHIELD, MANWE_CALL, MANWE_AVATAR,
+}
+
+-- Create the book of tulkas
+school_book[22] = {
+ TULKAS_AIM, TULKAS_SPIN, TULKAS_WAVE,
+}
+
+-- Create the book of melkor
+school_book[23] = {
+ MELKOR_CURSE, MELKOR_CORPSE_EXPLOSION, MELKOR_MIND_STEAL,
+}
+
+-- Create the book of yavanna
+school_book[24] = {
+ YAVANNA_CHARM_ANIMAL, YAVANNA_GROW_GRASS, YAVANNA_TREE_ROOTS, YAVANNA_WATER_BITE, YAVANNA_UPROOT,
+}
+
+-- Create the book of beginner's cantrip
+school_book[50] = {
+ MANATHRUST, GLOBELIGHT, ENTPOTION, BLINK, SENSEMONSTERS, SENSEHIDDEN,
+}
+
+-- Create the book of teleporatation
+school_book[51] = {
+ BLINK, TELEPORT, TELEAWAY
+}
+
+-- Create the book of summoning
+school_book[52] = {
+ FIREGOLEM, SUMMONANNIMAL
+}
+
+
+-- Create the Armageddon Demonblade
+school_book[55] = {
+ DEMON_BLADE, DEMON_MADNESS, DEMON_FIELD,
+}
+
+-- Create the Shield Demonblade
+school_book[56] = {
+ DOOM_SHIELD, DEMON_CLOAK, UNHOLY_WORD,
+}
+
+-- Create the Control Demonblade
+school_book[57] = {
+ DEMON_SUMMON, DISCHARGE_MINION, CONTROL_DEMON,
+}
+
+-- Create the Drums
+school_book[58] = {
+ MUSIC_STOP, MUSIC_HOLD, MUSIC_CONF, MUSIC_STUN,
+}
+
+-- Create the Harps
+school_book[59] = {
+ MUSIC_STOP, MUSIC_LITE, MUSIC_HERO, MUSIC_HEAL, MUSIC_TIME, MUSIC_MIND,
+}
+
+-- Create the Horns
+school_book[60] = {
+ MUSIC_STOP, MUSIC_BLOW, MUSIC_WIND, MUSIC_YLMIR, MUSIC_AMBARKANTA,
+}
+
+-- Book of the Player, filled in by the Library Quest
+school_book[61] = { }
+
+-- Geomancy spells, not a real book
+school_book[62] = {
+ CALL_THE_ELEMENTS, CHANNEL_ELEMENTS, ELEMENTAL_WAVE, VAPORIZE, GEOLYSIS, DRIPPING_TREAD, GROW_BARRIER, ELEMENTAL_MINION
+}
diff --git a/lib/scpt/stores.lua b/lib/scpt/stores.lua
new file mode 100644
index 00000000..713b3c01
--- /dev/null
+++ b/lib/scpt/stores.lua
@@ -0,0 +1,151 @@
+-- Whats shops can buy what
+store_buy_list
+{
+ ["General Store"] =
+ {
+ TV_CORPSE,
+ TV_FOOD,
+ TV_LITE,
+ TV_FLASK,
+ TV_SPIKE,
+ TV_SHOT,
+ TV_ARROW,
+ TV_BOLT,
+ TV_DIGGING,
+ TV_CLOAK,
+ TV_BOTTLE,
+ },
+ ["Armoury"] =
+ {
+ TV_BOOTS,
+ TV_GLOVES,
+ TV_CROWN,
+ TV_HELM,
+ TV_SHIELD,
+ TV_CLOAK,
+ TV_SOFT_ARMOR,
+ TV_HARD_ARMOR,
+ TV_DRAG_ARMOR,
+ },
+ ["Weaponsmith"] =
+ {
+ TV_SHOT,
+ TV_BOLT,
+ TV_ARROW,
+ TV_BOOMERANG,
+ TV_BOW,
+ TV_DIGGING,
+ TV_HAFTED,
+ TV_POLEARM,
+ TV_SWORD,
+ TV_AXE,
+ TV_MSTAFF,
+ },
+ -- We use a function because we want to restrict to blessed weapons and god spells
+ ["Temple"] = function (obj)
+ if obj.tval == TV_DRUID_BOOK then return TRUE
+ elseif obj.tval == TV_BOOK and obj.sval == 255 and (can_spell_random(obj.pval) == SKILL_SPIRITUALITY) then return TRUE
+ elseif obj.tval == TV_SCROLL then return TRUE
+ elseif obj.tval == TV_POTION2 then return TRUE
+ elseif obj.tval == TV_POTION then return TRUE
+ elseif obj.tval == TV_HAFTED then return TRUE
+ elseif obj.tval == TV_POLEARM and is_blessed(obj) == TRUE then return TRUE
+ elseif obj.tval == TV_SWORD and is_blessed(obj) == TRUE then return TRUE
+ elseif obj.tval == TV_AXE and is_blessed(obj) == TRUE then return TRUE
+ elseif obj.tval == TV_BOOMERANG and is_blessed(obj) == TRUE then return TRUE
+ end
+ end,
+ ["Alchemy shop"] =
+ {
+ TV_SCROLL,
+ TV_POTION2,
+ TV_POTION,
+ TV_BATERIE,
+ TV_BOTTLE,
+ },
+ -- We use a function because we dont want god spells
+ ["Magic shop"] = function (obj)
+ local buy =
+ {
+ [TV_SYMBIOTIC_BOOK] = TRUE,
+ [TV_AMULET] = TRUE,
+ [TV_RING] = TRUE,
+ [TV_STAFF] = TRUE,
+ [TV_WAND] = TRUE,
+ [TV_ROD] = TRUE,
+ [TV_ROD_MAIN] = TRUE,
+ [TV_SCROLL] = TRUE,
+ [TV_POTION2] = TRUE,
+ [TV_POTION] = TRUE,
+ [TV_MSTAFF] = TRUE,
+ [TV_RANDART] = TRUE,
+ }
+
+ if obj.tval == TV_BOOK and obj.sval == 255 and (can_spell_random(obj.pval) == SKILL_MAGIC) then return TRUE
+ elseif obj.tval == TV_BOOK and obj.sval ~= 255 then return TRUE
+ elseif buy[obj.tval] == TRUE then return TRUE
+ end
+ end,
+ -- Black markets wants ALL!
+ ["Black Market"] = function (obj)
+ return TRUE
+ end,
+ ["Book Store"] =
+ {
+ TV_BOOK,
+ TV_SYMBIOTIC_BOOK,
+ TV_MUSIC_BOOK,
+ TV_DAEMON_BOOK,
+ TV_DRUID_BOOK,
+ },
+ ["Pet Shop"] =
+ {
+ TV_EGG,
+ },
+}
+
+-- Test only
+function out_sticks()
+ local i
+ for i = 0, __tmp_spells_num - 1 do
+ if __tmp_spells[i].stick then
+ if __tmp_spells[i].stick[TV_WAND] then
+ print("Wand: " .. __tmp_spells[i].name)
+ end
+ end
+ end
+ for i = 0, __tmp_spells_num - 1 do
+ if __tmp_spells[i].stick then
+ if __tmp_spells[i].stick[TV_STAFF] then
+ print("Staff: " .. __tmp_spells[i].name)
+ end
+ end
+ end
+end
+
+-- Take care to have Magic shop/Temple have specific spells only
+add_hooks
+{
+ [HOOK_STORE_STOCK] = function (index, name, level)
+ if name == "Magic shop" then
+ -- Books
+ if magik(20) == TRUE then
+ object_prep(obj_forge, lookup_kind(TV_BOOK, 255))
+ local spell = get_random_spell(SKILL_MAGIC, 20)
+ if spell > -1 then
+ obj_forge.pval = spell
+ return TRUE, obj_forge
+ end
+ end
+ elseif name == "Temple" then
+ if magik(20) == TRUE then
+ object_prep(obj_forge, lookup_kind(TV_BOOK, 255))
+ local spell = get_random_spell(SKILL_SPIRITUALITY, 20)
+ if spell > -1 then
+ obj_forge.pval = spell
+ return TRUE, obj_forge
+ end
+ end
+ end
+ end,
+}
diff --git a/lib/scpt/test.lua b/lib/scpt/test.lua
new file mode 100644
index 00000000..81dd5c03
--- /dev/null
+++ b/lib/scpt/test.lua
@@ -0,0 +1,364 @@
+--
+-- This file takes care of providing the Shiny-Test class
+-- with .. erm .. powerful spells
+--
+
+------------------------------ MAGICTYPE -- M KEY ---------------------------------
+
+zog_magic = add_magic
+{
+ ["fail"] = function(chance)
+ msg_print("So bad, we had "..chance.." chances to succeed.")
+ msg_print("Hooo bad luck the spell backfires !.")
+ take_hit("stupidity", 5)
+ end,
+ ["stat"] = A_CON,
+ -- Must return a number between 0 and 50 representing a level
+ ["get_level"] = function()
+ return get_skill_scale(SKILL_MAGIC, 25) + get_skill_scale(SKILL_SPIRITUALITY, 25)
+ end,
+ ["spell_list"] =
+ {
+ {
+ ["name"] = "Zog1",
+ ["desc"] = "dessssc zog1",
+ ["mana"] = 1,
+ ["level"] = 1,
+ ["fail"] = 10,
+ ["spell"] = function()
+ local ret, dir
+ -- Get a direction
+ ret, dir = get_aim_dir();
+ if (ret == FALSE) then return end
+ fire_ball(GF_MANA, dir, 2000, 10)
+ end,
+ ["info"] = function()
+ return " dam 2000"
+ end,
+ },
+ {
+ ["name"] = "Zog2",
+ ["desc"] = "dessssc zog2",
+ ["mana"] = 3,
+ ["level"] = 3,
+ ["fail"] = 30,
+ ["spell"] = function()
+ local ret, item, obj, o_name
+
+ -- Ask for an item
+ ret, item = get_item("What to uber-ize?",
+ "You have nothing you can uber-ize",
+ bor(USE_INVEN, USE_EQUIP),
+ function (obj)
+ if (obj.tval == TV_HAFTED) or (obj.tval == TV_SWORD) or (obj.tval == TV_POLEARM) or (obj.tval == TV_AXE) then
+ return TRUE
+ end
+ return FALSE
+ end
+ )
+
+ if ret == TRUE then
+ -- get the item
+ obj = get_object(item)
+ -- modify it
+ obj.dd = 255
+ obj.ds = 255
+ obj.to_d = 1000
+ obj.to_h = 1000
+
+ -- get the name
+ o_name = object_desc(obj, FALSE, 0);
+ msg_print("Your "..o_name.." is hit by a pure wave of uber-ification!")
+ end
+ end,
+ ["info"] = function()
+ return " cooool"
+ end,
+ },
+ {
+ ["name"] = "Zog3",
+ ["desc"] = "dessssc zog3",
+ ["mana"] = 4,
+ ["level"] = 5,
+ ["fail"] = 50,
+ ["spell"] = function()
+ local list = {[1] = "Novice Warrior", [2] = "Novice Mage"}
+ local x, y, num, max
+
+ num = rand_range(1, 2)
+ max = damroll(1, 2)
+ while (max > 0) do
+ y, x = find_position(player.py, player.px)
+ place_monster_one(y, x, test_monster_name(list[num]), 0, FALSE, MSTATUS_FRIEND)
+ max = max - 1
+ end
+ end,
+ ["info"] = function()
+ return " summons 1d2 monsters"
+ end,
+ },
+ },
+}
+
+-- Register a new magic type
+MKEY_SHINY_TEST = 1000
+add_mkey
+{
+ ["mkey"] = MKEY_SHINY_TEST,
+ ["fct"] = function()
+ execute_magic(zog_magic)
+
+ -- use up some energy
+ energy_use = energy_use + 100;
+ end
+}
+
+
+------------------------------ EXTRA POWERS ---------------------------------
+
+
+-- Register a new power (the 'U' menu)
+POWER_TEST = add_power
+{
+ ["name"] = "Test power",
+ ["desc"] = "You are a shinny test",
+ ["desc_get"] = "You become a shinny test",
+ ["desc_lose"] = "You are no more a shinny test",
+ ["level"] = 1,
+ ["cost"] = 5,
+ ["stat"] = A_CON,
+ ["fail"] = 6,
+ ["power"] = function()
+ msg_print("Zogzog !")
+ end,
+}
+
+
+---- tests
+
+function test_write()
+ local conn = zsock:new_connection()
+ zsock:setup(conn, "192.168.0.200", 2262, ZSOCK_TYPE_TCP, FALSE)
+ zsock:open(conn)
+
+ local res, len = zsock:write(conn, "footest", FALSE)
+ msg_print("res "..res.." :: len "..len)
+
+ zsock:close(conn)
+ zsock:unsetup(conn)
+ zsock:free_connection(conn);
+end
+
+function test_read()
+ local conn = zsock:new_connection()
+ zsock:setup(conn, "192.168.0.200", 2262, ZSOCK_TYPE_TCP, FALSE)
+ zsock:open(conn)
+
+
+ zsock:wait(conn, 50)
+ local res, str, len = zsock:read(conn, 9, TRUE)
+ msg_print("res "..res.." :: len "..len.." '"..str.."'")
+
+ zsock:close(conn)
+ zsock:unsetup(conn)
+ zsock:free_connection(conn);
+end
+
+
+-- A level generator being tested
+
+CORRIDOR = 1
+ROOM = 2
+
+possible_walls = {}
+
+function level_generator_dungeon2_room(feat, y, x, h, w)
+ if feat == CORRIDOR then
+ -- Add the possible walls
+ for i = x, x + w - 1 do
+ tinsert(possible_walls, {y, i})
+ tinsert(possible_walls, {y + h - 1, i})
+ end
+ for i = x, x + w - 1 do
+ tinsert(possible_walls, {x, i})
+ tinsert(possible_walls, {x, i + h - 1})
+ end
+
+ for i = x + 1, x + w - 2 do
+ for j = y + 1, y + h - 2 do
+ place_floor(j, i)
+ end
+ end
+ else
+ -- Add the possible walls
+ for i = x, x + w - 1 do
+ tinsert(possible_walls, {y, i})
+ tinsert(possible_walls, {y + h - 1, i})
+ end
+ for i = x, x + w - 1 do
+ tinsert(possible_walls, {x, i})
+ tinsert(possible_walls, {x, i + h - 1})
+ end
+
+ for i = x, x + w - 1 do
+ for j = y, y + h - 1 do
+ cave(j, i).feat = 56
+ end
+ end
+ for i = x + 1, x + w - 2 do
+ for j = y + 1, y + h - 2 do
+ place_floor(j, i)
+ end
+ end
+ end
+end
+
+function select_feature(dir)
+ if magik(30) == TRUE then
+ return ROOM, rand_range(5, 12), rand_range(7, 17)
+
+ -- Corridor selection
+ elseif dir == "up" or dir == "down" then
+ return CORRIDOR, rand_range(5, 17), 3
+ else
+ return CORRIDOR, 3, rand_range(3, 15)
+ end
+end
+
+function put_feature(feat, y, x, h, w)
+ level_generator_dungeon2_room(feat, y, x, h, w)
+end
+
+function can_feature(y, x, h, w)
+ for i = x, x + w - 1 do
+ for j = y, y + h - 1 do
+ if j <= 0 or i <= 0 or i >= cur_wid - 1 or j >= cur_hgt - 1 then return nil end
+
+ if cave_is(cave(j, i), FF1_WALL) == FALSE then return nil end
+ end
+ end
+ return not nil
+end
+
+function is_near_wall(y, x)
+ if y <= 0 or x <= 0 or x >= cur_wid - 1 or y >= cur_hgt - 1 then return nil end
+
+ if cave_is(cave(y, x), FF1_WALL) == FALSE then return nil end
+ if cave_is(cave(y - 1, x), FF1_FLOOR) == TRUE then return "down" end
+ if cave_is(cave(y + 1, x), FF1_FLOOR) == TRUE then return "up" end
+ if cave_is(cave(y, x - 1), FF1_FLOOR) == TRUE then return "right" end
+ if cave_is(cave(y, x + 1), FF1_FLOOR) == TRUE then return "left" end
+ return nil
+end
+
+function find_spot()
+--[[ local y, x = 1, 1
+
+ while not is_near_wall(y, x) do
+ y, x = rand_range(1, cur_hgt - 2), rand_range(1, cur_wid - 2)
+ end]]
+ local i = rand_range(1, getn(possible_walls))
+ local y, x = possible_walls[i][1], possible_walls[i][2]
+
+ while not is_near_wall(y, x) do
+ i = rand_range(1, getn(possible_walls))
+ y, x = possible_walls[i][1], possible_walls[i][2]
+ end
+
+ tremove(possible_walls, i)
+ return is_near_wall(y, x), y, x
+end
+
+function adjust_dir(dir, y, x, h, w)
+ if dir == "up" then
+ y = y - (h - 1)
+ x = x - (w / 2)
+ elseif dir == "down" then
+ x = x - (w / 2)
+ elseif dir == "left" then
+ y = y - (h / 2)
+ x = x - (w - 1)
+ elseif dir == "up" then
+ y = y - (h / 2)
+ end
+ return y, x
+end
+
+level_generator
+{
+ ["name"] = "dungeon2",
+ ["stairs"] = FALSE,
+ ["monsters"] = FALSE,
+ ["objects"] = FALSE,
+ ["miscs"] = FALSE,
+ ["gen"] = function()
+ for i = 1, cur_wid - 1 do
+ for j = 1, cur_hgt - 1 do
+ place_filler(j, i)
+ end
+ end
+
+
+ -- the first room
+ level_generator_dungeon2_room(ROOM, cur_hgt / 2, cur_wid / 2, rand_range(3, 10), rand_range(5, 15))
+
+
+ -- Place 10 features
+ for nb = 1, 0 do
+ -- Find a spot near an empty space
+ local dir, y, x = find_spot()
+
+ local feat, h, w = select_feature(dir)
+
+ local sy, sx = adjust_dir(dir, y, x, h, w)
+
+ if can_feature(sy, sx, h, w) then
+ put_feature(feat, sy, sx, h, w)
+ cave(y, x).feat = 32
+ end
+ end
+
+ player.py = cur_hgt / 2
+ player.px = cur_wid / 2
+ return TRUE
+ end,
+}
+
+
+-- exmaple of display_list
+function input_list_example()
+ local list = { "a", "b", "c", "d", "e", "f", "h", "g" }
+ local sel = 1
+ local begin = 1
+ local res = nil
+
+ screen_save()
+
+ while not nil do
+ display_list(1, 0, 5, 9, "select", list, begin, sel, TERM_L_BLUE)
+
+ local key = inkey()
+
+ if key == ESCAPE then break
+ elseif key == strbyte("8") then
+ sel = sel - 1
+ if sel < 1 then sel = 1 end
+ if sel < begin then
+ begin = begin - 1
+ end
+ elseif key == strbyte("2") then
+ sel = sel + 1
+ if sel > getn(list) then sel = getn(list) end
+ if sel >= begin + 4 then
+ begin = begin + 1
+ end
+ elseif key == strbyte("\r") then
+ res = list[sel]
+ break
+ end
+ end
+
+ screen_load()
+
+ if res then msg_print("Selected: " .. res) end
+end
diff --git a/lib/user/automat.atm b/lib/user/automat.atm
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/lib/user/automat.atm
diff --git a/lib/user/delete.me b/lib/user/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/user/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/xtra/ang16.bdf b/lib/xtra/ang16.bdf
new file mode 100644
index 00000000..f59a2eca
--- /dev/null
+++ b/lib/xtra/ang16.bdf
@@ -0,0 +1,5017 @@
+STARTFONT 2.1
+FONT -Angband-Fixed-Medium-R-Normal--16-120-100-100-C-80-Misc-Fontspecific
+SIZE 16 100 100
+FONTBOUNDINGBOX 8 16 0 -2
+STARTPROPERTIES 18
+FONTNAME_REGISTRY ""
+FOUNDRY "Angband"
+FAMILY_NAME "Fixed"
+WEIGHT_NAME "Medium"
+SLANT "R"
+SETWIDTH_NAME "Normal"
+ADD_STYLE_NAME ""
+PIXEL_SIZE 16
+POINT_SIZE 120
+RESOLUTION_X 100
+RESOLUTION_Y 100
+SPACING "C"
+AVERAGE_WIDTH 80
+CHARSET_REGISTRY "Misc"
+CHARSET_ENCODING "Fontspecific"
+DEFAULT_CHAR 32
+FONT_DESCENT 2
+FONT_ASCENT 14
+ENDPROPERTIES
+CHARS 217
+STARTCHAR C20
+ENCODING 32
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C21
+ENCODING 33
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+30
+30
+30
+30
+30
+30
+00
+00
+30
+30
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C22
+ENCODING 34
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+66
+42
+24
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C23
+ENCODING 35
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+6c
+6c
+6c
+fe
+6c
+6c
+6c
+fe
+6c
+6c
+6c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C24
+ENCODING 36
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+10
+7c
+d2
+d0
+f0
+7c
+3e
+1e
+16
+96
+7c
+10
+00
+00
+00
+ENDCHAR
+STARTCHAR C25
+ENCODING 37
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+62
+66
+0e
+1c
+38
+70
+e0
+cc
+8c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C26
+ENCODING 38
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+30
+68
+58
+38
+70
+f2
+d4
+c8
+d4
+76
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C27
+ENCODING 39
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+18
+08
+10
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C28
+ENCODING 40
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+08
+10
+30
+30
+30
+30
+30
+30
+30
+10
+08
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C29
+ENCODING 41
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+10
+08
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+08
+10
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C2A
+ENCODING 42
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+d6
+fe
+7c
+ee
+7c
+fe
+d6
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C2B
+ENCODING 43
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+18
+18
+18
+7e
+18
+18
+18
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C2C
+ENCODING 44
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+38
+38
+10
+20
+00
+00
+ENDCHAR
+STARTCHAR C2D
+ENCODING 45
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+7e
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C2E
+ENCODING 46
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+18
+18
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C2F
+ENCODING 47
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+02
+06
+0e
+1c
+38
+70
+e0
+c0
+80
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C30
+ENCODING 48
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+1c
+26
+66
+66
+6e
+76
+66
+66
+64
+38
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C31
+ENCODING 49
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+18
+38
+78
+18
+18
+18
+18
+18
+18
+7e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C32
+ENCODING 50
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+46
+66
+06
+0c
+18
+30
+62
+7e
+7e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C33
+ENCODING 51
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+66
+66
+06
+3c
+06
+06
+66
+66
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C34
+ENCODING 52
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+02
+06
+0e
+16
+26
+46
+ff
+06
+06
+0f
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C35
+ENCODING 53
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7e
+7e
+40
+5c
+66
+46
+06
+66
+46
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C36
+ENCODING 54
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+1c
+26
+66
+60
+7c
+66
+66
+66
+66
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C37
+ENCODING 55
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7e
+7e
+42
+06
+0c
+18
+30
+30
+30
+30
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C38
+ENCODING 56
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+66
+62
+74
+3c
+2e
+46
+46
+66
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C39
+ENCODING 57
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+66
+66
+66
+66
+3e
+06
+66
+64
+38
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C3A
+ENCODING 58
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+18
+00
+00
+18
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C3B
+ENCODING 59
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+30
+30
+00
+00
+00
+38
+18
+10
+20
+00
+00
+ENDCHAR
+STARTCHAR C3C
+ENCODING 60
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+04
+0c
+18
+30
+60
+30
+18
+0c
+04
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C3D
+ENCODING 61
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+7e
+00
+00
+7e
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C3E
+ENCODING 62
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+20
+30
+18
+0c
+06
+0c
+18
+30
+20
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C3F
+ENCODING 63
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+c6
+86
+06
+0c
+18
+30
+00
+30
+30
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C40
+ENCODING 64
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+46
+9a
+b6
+ba
+b6
+9c
+80
+c2
+7c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C41
+ENCODING 65
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+06
+0e
+0e
+16
+16
+26
+3e
+46
+46
+ef
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C42
+ENCODING 66
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+66
+62
+66
+7c
+66
+62
+62
+66
+fc
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C43
+ENCODING 67
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3e
+46
+c2
+c0
+c0
+c0
+c0
+c2
+e6
+7c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C44
+ENCODING 68
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+6e
+66
+66
+66
+66
+66
+66
+64
+f8
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C45
+ENCODING 69
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fe
+62
+60
+64
+7c
+64
+60
+60
+62
+fe
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C46
+ENCODING 70
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fe
+62
+60
+64
+7c
+64
+60
+60
+60
+f0
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C47
+ENCODING 71
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+66
+c2
+c0
+ce
+c6
+c6
+c6
+e4
+78
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C48
+ENCODING 72
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e7
+66
+66
+66
+7e
+66
+66
+66
+66
+e7
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C49
+ENCODING 73
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+18
+18
+18
+18
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C4A
+ENCODING 74
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+18
+18
+18
+18
+18
+18
+18
+18
+18
+d8
+70
+00
+00
+ENDCHAR
+STARTCHAR C4B
+ENCODING 75
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e7
+62
+62
+64
+68
+7c
+6e
+66
+66
+e7
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C4C
+ENCODING 76
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+f0
+60
+60
+60
+60
+60
+60
+60
+62
+fe
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C4D
+ENCODING 77
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e7
+66
+76
+7e
+7e
+56
+46
+46
+46
+ef
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C4E
+ENCODING 78
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e7
+62
+72
+72
+5a
+5a
+4e
+4e
+46
+e6
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C4F
+ENCODING 79
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+4e
+c6
+c6
+c6
+c6
+c6
+c6
+e4
+78
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C50
+ENCODING 80
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+66
+66
+66
+64
+78
+60
+60
+60
+f0
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C51
+ENCODING 81
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+4e
+c6
+c6
+c6
+c6
+d6
+d6
+f4
+78
+0e
+00
+00
+00
+ENDCHAR
+STARTCHAR C52
+ENCODING 82
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+66
+66
+66
+6c
+78
+64
+66
+66
+e7
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C53
+ENCODING 83
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7a
+c6
+c2
+e0
+70
+1c
+0e
+86
+ce
+bc
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C54
+ENCODING 84
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+ff
+db
+99
+18
+18
+18
+18
+18
+18
+7e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C55
+ENCODING 85
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e3
+62
+62
+62
+62
+62
+62
+62
+72
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C56
+ENCODING 86
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e3
+62
+64
+64
+68
+68
+70
+70
+60
+60
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C57
+ENCODING 87
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e3
+62
+62
+6a
+6a
+7e
+7e
+7e
+6c
+68
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C58
+ENCODING 88
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+f7
+62
+62
+74
+38
+1c
+2e
+46
+46
+ef
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C59
+ENCODING 89
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e3
+62
+74
+34
+38
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C5A
+ENCODING 90
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fe
+86
+0e
+1c
+38
+70
+e0
+c0
+c2
+fe
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C5B
+ENCODING 91
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+30
+30
+30
+30
+30
+30
+30
+30
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C5C
+ENCODING 92
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+80
+c0
+e0
+70
+38
+1c
+0e
+06
+02
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C5D
+ENCODING 93
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+0c
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C5E
+ENCODING 94
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+10
+38
+6c
+c6
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C5F
+ENCODING 95
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+7e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C60
+ENCODING 96
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+10
+20
+30
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C61
+ENCODING 97
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+78
+8c
+1c
+6c
+ce
+dc
+68
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C62
+ENCODING 98
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+20
+60
+e0
+60
+6c
+76
+66
+66
+66
+64
+58
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C63
+ENCODING 99
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+1c
+26
+60
+60
+62
+64
+38
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C64
+ENCODING 100
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+04
+0c
+1c
+0c
+3c
+4c
+cc
+cc
+cc
+dc
+6e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C65
+ENCODING 101
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+1c
+22
+64
+78
+72
+64
+38
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C66
+ENCODING 102
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+06
+0f
+16
+30
+7c
+30
+30
+30
+30
+30
+78
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C67
+ENCODING 103
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+06
+3a
+4c
+64
+38
+7c
+8e
+66
+62
+3c
+00
+00
+ENDCHAR
+STARTCHAR C68
+ENCODING 104
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+20
+60
+e0
+60
+6c
+76
+66
+66
+66
+66
+e7
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C69
+ENCODING 105
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+0c
+00
+00
+04
+0c
+1c
+0c
+0c
+0c
+1e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C6A
+ENCODING 106
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+0c
+00
+00
+04
+0c
+1c
+0c
+0c
+0c
+0c
+68
+70
+00
+00
+ENDCHAR
+STARTCHAR C6B
+ENCODING 107
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+20
+60
+e0
+60
+6e
+64
+68
+70
+78
+6c
+ee
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C6C
+ENCODING 108
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+08
+18
+38
+18
+18
+18
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C6D
+ENCODING 109
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+2a
+7f
+eb
+6b
+6b
+6b
+eb
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C6E
+ENCODING 110
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+2c
+76
+e6
+66
+66
+66
+e7
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C6F
+ENCODING 111
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+1c
+26
+66
+66
+66
+64
+38
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C70
+ENCODING 112
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+2c
+76
+e6
+66
+64
+78
+60
+60
+f0
+00
+00
+ENDCHAR
+STARTCHAR C71
+ENCODING 113
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+34
+4c
+cc
+cc
+cc
+7c
+0c
+0c
+1e
+00
+00
+ENDCHAR
+STARTCHAR C72
+ENCODING 114
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+06
+4f
+f6
+60
+60
+60
+60
+f0
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C73
+ENCODING 115
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+1e
+32
+38
+1c
+0e
+26
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C74
+ENCODING 116
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+10
+30
+7e
+30
+30
+30
+32
+34
+18
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C75
+ENCODING 117
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+ee
+66
+66
+66
+67
+6e
+34
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C76
+ENCODING 118
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+e6
+64
+68
+68
+70
+70
+60
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C77
+ENCODING 119
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+e3
+62
+6a
+6e
+7e
+6c
+48
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C78
+ENCODING 120
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+ee
+64
+38
+38
+38
+4c
+ee
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C79
+ENCODING 121
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+f3
+62
+36
+3c
+1c
+08
+10
+70
+60
+00
+00
+ENDCHAR
+STARTCHAR C7A
+ENCODING 122
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+7e
+46
+0c
+18
+30
+62
+7e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C7B
+ENCODING 123
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+0e
+18
+18
+18
+18
+70
+18
+18
+18
+18
+0e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C7C
+ENCODING 124
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+30
+30
+30
+30
+00
+30
+30
+30
+30
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C7D
+ENCODING 125
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+70
+18
+18
+18
+18
+0e
+18
+18
+18
+18
+70
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C7E
+ENCODING 126
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+32
+7e
+4c
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C7F
+ENCODING 127
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+10
+38
+6c
+c6
+82
+fe
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C80
+ENCODING 128
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+c6
+c6
+de
+de
+de
+dc
+c0
+7e
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C81
+ENCODING 129
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+38
+6c
+c6
+c6
+c6
+fe
+c6
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C82
+ENCODING 130
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+66
+66
+66
+7c
+66
+66
+66
+fc
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C83
+ENCODING 131
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+66
+c0
+c0
+c0
+c0
+c0
+66
+3c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C84
+ENCODING 132
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+f8
+6c
+66
+66
+66
+66
+66
+6c
+f8
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C85
+ENCODING 133
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fe
+66
+60
+60
+7c
+60
+60
+66
+fe
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C86
+ENCODING 134
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fe
+66
+60
+60
+7c
+60
+60
+60
+f0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C87
+ENCODING 135
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+c6
+c6
+c0
+c0
+ce
+c6
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C88
+ENCODING 136
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+c6
+c6
+fe
+c6
+c6
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C89
+ENCODING 137
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+18
+18
+18
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C8A
+ENCODING 138
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3c
+18
+18
+18
+18
+18
+d8
+d8
+70
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C8B
+ENCODING 139
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+cc
+d8
+f0
+f0
+d8
+cc
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C8C
+ENCODING 140
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+f0
+60
+60
+60
+60
+60
+62
+66
+fe
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C8D
+ENCODING 141
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+ee
+fe
+d6
+d6
+d6
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C8E
+ENCODING 142
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+e6
+e6
+f6
+de
+ce
+ce
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C8F
+ENCODING 143
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+c6
+c6
+c6
+c6
+c6
+c6
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C90
+ENCODING 144
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+66
+66
+66
+7c
+60
+60
+60
+f0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C91
+ENCODING 145
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+c6
+c6
+c6
+c6
+c6
+c6
+d6
+7c
+06
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C92
+ENCODING 146
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fc
+66
+66
+66
+7c
+78
+6c
+66
+e6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C93
+ENCODING 147
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+c6
+c0
+60
+38
+0c
+06
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C94
+ENCODING 148
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7e
+5a
+18
+18
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C95
+ENCODING 149
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+c6
+c6
+c6
+c6
+c6
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C96
+ENCODING 150
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+c6
+c6
+c6
+c6
+6c
+38
+10
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C97
+ENCODING 151
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+d6
+d6
+d6
+fe
+ee
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C98
+ENCODING 152
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+c6
+c6
+6c
+38
+38
+38
+6c
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C99
+ENCODING 153
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+66
+66
+66
+66
+3c
+18
+18
+18
+3c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C9A
+ENCODING 154
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+fe
+c6
+8c
+18
+30
+60
+c2
+c6
+fe
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C9B
+ENCODING 155
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+78
+0c
+7c
+cc
+dc
+76
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C9C
+ENCODING 156
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e0
+60
+60
+7c
+66
+66
+66
+66
+fc
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C9D
+ENCODING 157
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+7c
+c6
+c0
+c0
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C9E
+ENCODING 158
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+1c
+0c
+0c
+7c
+cc
+cc
+cc
+cc
+7e
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR C9F
+ENCODING 159
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+7c
+c6
+fe
+c0
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA0
+ENCODING 160
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+1c
+36
+30
+30
+fc
+30
+30
+30
+78
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA1
+ENCODING 161
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+76
+ce
+c6
+c6
+7e
+06
+c6
+7c
+00
+00
+00
+ENDCHAR
+STARTCHAR CA2
+ENCODING 162
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e0
+60
+60
+6c
+76
+66
+66
+66
+e6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA3
+ENCODING 163
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+18
+18
+00
+38
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA4
+ENCODING 164
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+0c
+0c
+00
+1c
+0c
+0c
+0c
+0c
+cc
+cc
+78
+00
+00
+00
+ENDCHAR
+STARTCHAR CA5
+ENCODING 165
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+e0
+60
+60
+66
+6c
+78
+6c
+66
+e6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA6
+ENCODING 166
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+38
+18
+18
+18
+18
+18
+18
+18
+3c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA7
+ENCODING 167
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+6c
+fe
+d6
+d6
+c6
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA8
+ENCODING 168
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+dc
+66
+66
+66
+66
+66
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CA9
+ENCODING 169
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+7c
+c6
+c6
+c6
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CAA
+ENCODING 170
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+dc
+66
+66
+66
+7c
+60
+60
+f0
+00
+00
+00
+ENDCHAR
+STARTCHAR CAB
+ENCODING 171
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+76
+cc
+cc
+cc
+7c
+0c
+0c
+1e
+00
+00
+00
+ENDCHAR
+STARTCHAR CAC
+ENCODING 172
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+dc
+66
+60
+60
+60
+f0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CAD
+ENCODING 173
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+7c
+c6
+70
+1c
+c6
+7c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CAE
+ENCODING 174
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+30
+30
+30
+fc
+30
+30
+30
+36
+1c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CAF
+ENCODING 175
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+cc
+cc
+cc
+cc
+cc
+76
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CB0
+ENCODING 176
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+c6
+c6
+c6
+6c
+38
+10
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CB1
+ENCODING 177
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+c6
+c6
+d6
+d6
+fe
+6c
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CB2
+ENCODING 178
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+c6
+6c
+38
+38
+6c
+c6
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CB3
+ENCODING 179
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+c6
+c6
+c6
+ce
+76
+06
+c6
+7c
+00
+00
+00
+ENDCHAR
+STARTCHAR CB4
+ENCODING 180
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+fe
+8c
+18
+30
+62
+fe
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CB5
+ENCODING 181
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+88
+00
+00
+00
+22
+00
+00
+00
+88
+00
+00
+00
+22
+00
+00
+00
+ENDCHAR
+STARTCHAR CB6
+ENCODING 182
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+ff
+42
+42
+7e
+42
+42
+7e
+42
+42
+7e
+42
+42
+7e
+42
+42
+ENDCHAR
+STARTCHAR CB7
+ENCODING 183
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+42
+42
+7e
+42
+42
+7e
+42
+42
+7e
+42
+42
+7e
+42
+42
+ff
+ff
+ENDCHAR
+STARTCHAR CB8
+ENCODING 184
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+ff
+95
+d5
+95
+d5
+95
+d5
+95
+d5
+95
+d5
+95
+d5
+ff
+ff
+ENDCHAR
+STARTCHAR CB9
+ENCODING 185
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+c3
+81
+81
+81
+81
+81
+81
+81
+81
+81
+81
+81
+81
+81
+81
+ENDCHAR
+STARTCHAR CBA
+ENCODING 186
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+7e
+52
+52
+52
+50
+02
+12
+00
+82
+82
+92
+92
+f6
+fe
+ff
+ENDCHAR
+STARTCHAR CBB
+ENCODING 187
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+1c
+2e
+53
+ff
+00
+00
+ENDCHAR
+STARTCHAR CBC
+ENCODING 188
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+20
+20
+20
+ff
+02
+02
+02
+ff
+20
+20
+20
+ff
+02
+02
+02
+ENDCHAR
+STARTCHAR CBD
+ENCODING 189
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+81
+81
+81
+9d
+bd
+ad
+ad
+ad
+b9
+81
+81
+42
+24
+18
+00
+ENDCHAR
+STARTCHAR CBE
+ENCODING 190
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+81
+81
+81
+81
+bd
+bd
+bd
+bd
+99
+81
+81
+42
+24
+18
+00
+ENDCHAR
+STARTCHAR CBF
+ENCODING 191
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+81
+95
+9d
+bd
+95
+91
+91
+91
+91
+91
+81
+42
+24
+18
+00
+ENDCHAR
+STARTCHAR CC0
+ENCODING 192
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+e7
+db
+db
+e7
+81
+81
+e7
+e7
+e7
+e7
+e7
+7e
+3c
+18
+00
+ENDCHAR
+STARTCHAR CC1
+ENCODING 193
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+e7
+c3
+e7
+e7
+e7
+c3
+a1
+81
+81
+c3
+e7
+7e
+3c
+18
+00
+ENDCHAR
+STARTCHAR CC2
+ENCODING 194
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+87
+83
+b5
+85
+85
+85
+85
+85
+85
+dd
+e1
+7e
+3c
+18
+00
+ENDCHAR
+STARTCHAR CC3
+ENCODING 195
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+81
+b9
+a9
+b9
+91
+91
+91
+95
+9d
+95
+81
+42
+24
+18
+00
+ENDCHAR
+STARTCHAR CC4
+ENCODING 196
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+ff
+ef
+bb
+ef
+c7
+ef
+c7
+bb
+bb
+bb
+c7
+7e
+3c
+18
+00
+ENDCHAR
+STARTCHAR CC5
+ENCODING 197
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+18
+3c
+7e
+ff
+7e
+66
+66
+66
+66
+66
+66
+66
+ff
+00
+00
+ENDCHAR
+STARTCHAR CC6
+ENCODING 198
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+04
+08
+00
+7e
+90
+20
+40
+80
+84
+82
+82
+42
+3c
+00
+00
+ENDCHAR
+STARTCHAR CC7
+ENCODING 199
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+00
+00
+00
+7e
+ff
+7e
+00
+00
+00
+ENDCHAR
+STARTCHAR CC8
+ENCODING 200
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+6c
+92
+10
+10
+10
+10
+54
+92
+7c
+00
+00
+00
+ENDCHAR
+STARTCHAR CC9
+ENCODING 201
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+c0
+c0
+00
+00
+0c
+0c
+00
+00
+c0
+c0
+00
+00
+0c
+0c
+00
+00
+ENDCHAR
+STARTCHAR CCA
+ENCODING 202
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+55
+aa
+55
+aa
+55
+aa
+55
+aa
+55
+aa
+55
+aa
+55
+aa
+55
+aa
+ENDCHAR
+STARTCHAR CCB
+ENCODING 203
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+44
+aa
+11
+00
+44
+aa
+11
+00
+44
+aa
+11
+00
+44
+aa
+11
+ENDCHAR
+STARTCHAR CCC
+ENCODING 204
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+3c
+6e
+df
+ff
+bf
+ff
+ff
+ff
+7e
+18
+18
+3c
+7e
+00
+00
+ENDCHAR
+STARTCHAR CCD
+ENCODING 205
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+18
+3c
+7e
+7e
+7e
+7e
+7e
+3c
+18
+18
+3c
+00
+00
+ENDCHAR
+STARTCHAR CCE
+ENCODING 206
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+24
+5a
+91
+21
+42
+94
+69
+46
+82
+11
+aa
+44
+00
+00
+00
+ENDCHAR
+STARTCHAR CCF
+ENCODING 207
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+df
+df
+df
+00
+fd
+fd
+fd
+00
+df
+df
+df
+00
+fd
+fd
+fd
+ENDCHAR
+STARTCHAR CD0
+ENCODING 208
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ff
+ENDCHAR
+STARTCHAR CD1
+ENCODING 209
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+42
+00
+42
+42
+42
+ff
+7e
+7e
+7e
+7e
+ff
+00
+00
+ENDCHAR
+STARTCHAR CD2
+ENCODING 210
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+1c
+1c
+08
+3e
+49
+3e
+49
+3e
+49
+1c
+2a
+22
+22
+46
+00
+ENDCHAR
+STARTCHAR CD3
+ENCODING 211
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+48
+84
+02
+25
+18
+98
+64
+62
+14
+08
+12
+21
+42
+04
+08
+ENDCHAR
+STARTCHAR CD4
+ENCODING 212
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+38
+38
+10
+10
+10
+10
+10
+10
+10
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CD5
+ENCODING 213
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+00
+3e
+43
+7d
+45
+45
+7e
+00
+00
+00
+ENDCHAR
+STARTCHAR CD6
+ENCODING 214
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+1c
+26
+5f
+5f
+5f
+2e
+1c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CD7
+ENCODING 215
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+10
+38
+54
+10
+10
+10
+10
+10
+10
+10
+28
+00
+00
+ENDCHAR
+STARTCHAR CD8
+ENCODING 216
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+60
+10
+48
+0c
+4c
+0c
+4c
+0c
+4c
+08
+50
+20
+40
+00
+00
+ENDCHAR
+STARTCHAR CD9
+ENCODING 217
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+7c
+fe
+92
+10
+10
+10
+10
+10
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CDA
+ENCODING 218
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+0f
+0f
+0f
+0f
+10
+a0
+40
+a0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CDB
+ENCODING 219
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+48
+4c
+5e
+7e
+5e
+4c
+48
+40
+40
+40
+40
+40
+40
+00
+00
+ENDCHAR
+STARTCHAR CDC
+ENCODING 220
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+03
+07
+0e
+1c
+38
+b0
+40
+a0
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CDD
+ENCODING 221
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+60
+60
+60
+60
+70
+7c
+5e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CDE
+ENCODING 222
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+28
+aa
+aa
+be
+fc
+7d
+7d
+3e
+38
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CDF
+ENCODING 223
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+1c
+3e
+7f
+41
+41
+41
+00
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CE0
+ENCODING 224
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+18
+3c
+18
+7e
+db
+db
+7e
+7e
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CE1
+ENCODING 225
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+7e
+7e
+7e
+5a
+66
+3c
+3c
+18
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CE2
+ENCODING 226
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+36
+77
+7f
+77
+7f
+77
+5d
+6b
+77
+7f
+00
+00
+00
+ENDCHAR
+STARTCHAR CE3
+ENCODING 227
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+66
+db
+b5
+2c
+56
+6a
+56
+6a
+7e
+00
+00
+00
+ENDCHAR
+STARTCHAR CE4
+ENCODING 228
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+66
+db
+b5
+2c
+56
+6a
+56
+6a
+7e
+00
+00
+00
+ENDCHAR
+STARTCHAR CE5
+ENCODING 229
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+66
+db
+bd
+3c
+7e
+7e
+7e
+7e
+7e
+00
+00
+00
+ENDCHAR
+STARTCHAR CE6
+ENCODING 230
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+10
+10
+10
+7c
+82
+82
+92
+82
+92
+ba
+fe
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CE7
+ENCODING 231
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+1c
+22
+42
+44
+84
+82
+82
+62
+1c
+70
+d8
+a8
+d8
+70
+ENDCHAR
+STARTCHAR CE8
+ENCODING 232
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+28
+82
+10
+38
+10
+38
+44
+44
+44
+38
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CE9
+ENCODING 233
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+01
+02
+02
+00
+00
+06
+06
+0c
+0c
+18
+18
+30
+30
+60
+60
+00
+ENDCHAR
+STARTCHAR CEA
+ENCODING 234
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+22
+24
+28
+80
+50
+10
+08
+08
+04
+04
+02
+02
+00
+00
+00
+ENDCHAR
+STARTCHAR CEB
+ENCODING 235
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+08
+10
+42
+24
+00
+18
+18
+18
+18
+18
+18
+18
+00
+00
+00
+ENDCHAR
+STARTCHAR CEC
+ENCODING 236
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+3f
+7d
+46
+7c
+44
+7c
+4c
+7c
+54
+7c
+fc
+00
+00
+00
+ENDCHAR
+STARTCHAR CED
+ENCODING 237
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+3c
+00
+18
+18
+18
+3c
+6e
+5e
+5e
+5e
+7e
+7e
+7e
+00
+00
+ENDCHAR
+STARTCHAR CEE
+ENCODING 238
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+38
+7c
+fe
+fe
+10
+10
+38
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CEF
+ENCODING 239
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+7c
+7e
+7d
+55
+7d
+4d
+7d
+7d
+7d
+7d
+7d
+43
+3f
+00
+ENDCHAR
+STARTCHAR CF0
+ENCODING 240
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+92
+54
+01
+98
+3d
+bc
+19
+40
+82
+29
+48
+00
+00
+00
+ENDCHAR
+STARTCHAR CF1
+ENCODING 241
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+01
+02
+08
+7b
+78
+08
+02
+01
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CF2
+ENCODING 242
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+00
+00
+10
+38
+7c
+fe
+fe
+7c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CF3
+ENCODING 243
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+3c
+76
+c1
+d7
+c1
+f5
+c1
+76
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CF4
+ENCODING 244
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+28
+28
+82
+44
+00
+7c
+c6
+7c
+38
+10
+00
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CF5
+ENCODING 245
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+38
+10
+10
+10
+10
+38
+54
+92
+38
+54
+92
+30
+00
+00
+ENDCHAR
+STARTCHAR CF6
+ENCODING 246
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+3c
+18
+24
+42
+42
+7e
+7e
+3c
+00
+00
+00
+00
+ENDCHAR
+STARTCHAR CF7
+ENCODING 247
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+08
+0c
+0e
+0a
+0a
+0c
+08
+78
+f8
+70
+00
+00
+00
+ENDCHAR
+STARTCHAR CF8
+ENCODING 248
+SWIDTH 480 0
+DWIDTH 8 0
+BBX 8 16 0 -2
+BITMAP
+00
+00
+00
+00
+3c
+66
+66
+0c
+18
+18
+00
+18
+18
+00
+00
+00
+ENDCHAR
+ENDFONT
diff --git a/lib/xtra/angband.fnt b/lib/xtra/angband.fnt
new file mode 100644
index 00000000..d4db7bf2
--- /dev/null
+++ b/lib/xtra/angband.fnt
Binary files differ
diff --git a/lib/xtra/font/10X20.FON b/lib/xtra/font/10X20.FON
new file mode 100644
index 00000000..5a51ee9d
--- /dev/null
+++ b/lib/xtra/font/10X20.FON
Binary files differ
diff --git a/lib/xtra/font/12X24.FON b/lib/xtra/font/12X24.FON
new file mode 100644
index 00000000..7542c934
--- /dev/null
+++ b/lib/xtra/font/12X24.FON
Binary files differ
diff --git a/lib/xtra/font/5X8.FON b/lib/xtra/font/5X8.FON
new file mode 100644
index 00000000..53cdb63b
--- /dev/null
+++ b/lib/xtra/font/5X8.FON
Binary files differ
diff --git a/lib/xtra/font/6X10.FON b/lib/xtra/font/6X10.FON
new file mode 100644
index 00000000..cb93c326
--- /dev/null
+++ b/lib/xtra/font/6X10.FON
Binary files differ
diff --git a/lib/xtra/font/6X12.FON b/lib/xtra/font/6X12.FON
new file mode 100644
index 00000000..304af0b0
--- /dev/null
+++ b/lib/xtra/font/6X12.FON
Binary files differ
diff --git a/lib/xtra/font/6X13.FON b/lib/xtra/font/6X13.FON
new file mode 100644
index 00000000..9e59e40d
--- /dev/null
+++ b/lib/xtra/font/6X13.FON
Binary files differ
diff --git a/lib/xtra/font/6X13B.FON b/lib/xtra/font/6X13B.FON
new file mode 100644
index 00000000..07fa4932
--- /dev/null
+++ b/lib/xtra/font/6X13B.FON
Binary files differ
diff --git a/lib/xtra/font/6X9.FON b/lib/xtra/font/6X9.FON
new file mode 100644
index 00000000..c0913ac7
--- /dev/null
+++ b/lib/xtra/font/6X9.FON
Binary files differ
diff --git a/lib/xtra/font/7X13.FON b/lib/xtra/font/7X13.FON
new file mode 100644
index 00000000..d7f24c64
--- /dev/null
+++ b/lib/xtra/font/7X13.FON
Binary files differ
diff --git a/lib/xtra/font/7X13B.FON b/lib/xtra/font/7X13B.FON
new file mode 100644
index 00000000..6031925e
--- /dev/null
+++ b/lib/xtra/font/7X13B.FON
Binary files differ
diff --git a/lib/xtra/font/8X13.FON b/lib/xtra/font/8X13.FON
new file mode 100644
index 00000000..adf8ffaf
--- /dev/null
+++ b/lib/xtra/font/8X13.FON
Binary files differ
diff --git a/lib/xtra/font/8X13B.FON b/lib/xtra/font/8X13B.FON
new file mode 100644
index 00000000..e1d35106
--- /dev/null
+++ b/lib/xtra/font/8X13B.FON
Binary files differ
diff --git a/lib/xtra/font/9X15.FON b/lib/xtra/font/9X15.FON
new file mode 100644
index 00000000..7963de76
--- /dev/null
+++ b/lib/xtra/font/9X15.FON
Binary files differ
diff --git a/lib/xtra/font/9X15B.FON b/lib/xtra/font/9X15B.FON
new file mode 100644
index 00000000..ff5ab215
--- /dev/null
+++ b/lib/xtra/font/9X15B.FON
Binary files differ
diff --git a/lib/xtra/font/VeraMono.ttf b/lib/xtra/font/VeraMono.ttf
new file mode 100644
index 00000000..139f0b43
--- /dev/null
+++ b/lib/xtra/font/VeraMono.ttf
Binary files differ
diff --git a/lib/xtra/font/XM10X17.FNT b/lib/xtra/font/XM10X17.FNT
new file mode 100644
index 00000000..bf96f2c1
--- /dev/null
+++ b/lib/xtra/font/XM10X17.FNT
Binary files differ
diff --git a/lib/xtra/font/XM10X17B.FNT b/lib/xtra/font/XM10X17B.FNT
new file mode 100644
index 00000000..4df07431
--- /dev/null
+++ b/lib/xtra/font/XM10X17B.FNT
Binary files differ
diff --git a/lib/xtra/font/XM12X20.FNT b/lib/xtra/font/XM12X20.FNT
new file mode 100644
index 00000000..7d4b3ab9
--- /dev/null
+++ b/lib/xtra/font/XM12X20.FNT
Binary files differ
diff --git a/lib/xtra/font/XM12X20B.FNT b/lib/xtra/font/XM12X20B.FNT
new file mode 100644
index 00000000..d43c199d
--- /dev/null
+++ b/lib/xtra/font/XM12X20B.FNT
Binary files differ
diff --git a/lib/xtra/font/XM16X25.FNT b/lib/xtra/font/XM16X25.FNT
new file mode 100644
index 00000000..abe43d44
--- /dev/null
+++ b/lib/xtra/font/XM16X25.FNT
Binary files differ
diff --git a/lib/xtra/font/XM16X25B.FNT b/lib/xtra/font/XM16X25B.FNT
new file mode 100644
index 00000000..e676a37f
--- /dev/null
+++ b/lib/xtra/font/XM16X25B.FNT
Binary files differ
diff --git a/lib/xtra/font/XM5X8.FNT b/lib/xtra/font/XM5X8.FNT
new file mode 100644
index 00000000..1d3f5977
--- /dev/null
+++ b/lib/xtra/font/XM5X8.FNT
Binary files differ
diff --git a/lib/xtra/font/XM6X12.FNT b/lib/xtra/font/XM6X12.FNT
new file mode 100644
index 00000000..173a687f
--- /dev/null
+++ b/lib/xtra/font/XM6X12.FNT
Binary files differ
diff --git a/lib/xtra/font/XM6X12B.FNT b/lib/xtra/font/XM6X12B.FNT
new file mode 100644
index 00000000..81c2da41
--- /dev/null
+++ b/lib/xtra/font/XM6X12B.FNT
Binary files differ
diff --git a/lib/xtra/font/XM8X16B.FNT b/lib/xtra/font/XM8X16B.FNT
new file mode 100644
index 00000000..54b4e493
--- /dev/null
+++ b/lib/xtra/font/XM8X16B.FNT
Binary files differ
diff --git a/lib/xtra/font/xm4x6.fnt b/lib/xtra/font/xm4x6.fnt
new file mode 100644
index 00000000..f879fe42
--- /dev/null
+++ b/lib/xtra/font/xm4x6.fnt
Binary files differ
diff --git a/lib/xtra/font/xm8x13.fnt b/lib/xtra/font/xm8x13.fnt
new file mode 100644
index 00000000..57d2dd3c
--- /dev/null
+++ b/lib/xtra/font/xm8x13.fnt
Binary files differ
diff --git a/lib/xtra/font/xm8x13b.fnt b/lib/xtra/font/xm8x13b.fnt
new file mode 100644
index 00000000..00c25d6b
--- /dev/null
+++ b/lib/xtra/font/xm8x13b.fnt
Binary files differ
diff --git a/lib/xtra/font/xm8x16.fnt b/lib/xtra/font/xm8x16.fnt
new file mode 100644
index 00000000..0f7de641
--- /dev/null
+++ b/lib/xtra/font/xm8x16.fnt
Binary files differ
diff --git a/lib/xtra/graf/16x16.bmp b/lib/xtra/graf/16x16.bmp
new file mode 100644
index 00000000..2e6b0ebf
--- /dev/null
+++ b/lib/xtra/graf/16x16.bmp
Binary files differ
diff --git a/lib/xtra/graf/16x16.png b/lib/xtra/graf/16x16.png
new file mode 100644
index 00000000..9fc1681a
--- /dev/null
+++ b/lib/xtra/graf/16x16.png
Binary files differ
diff --git a/lib/xtra/graf/8x8.bmp b/lib/xtra/graf/8x8.bmp
new file mode 100644
index 00000000..02d2d1a9
--- /dev/null
+++ b/lib/xtra/graf/8x8.bmp
Binary files differ
diff --git a/lib/xtra/graf/8x8.png b/lib/xtra/graf/8x8.png
new file mode 100644
index 00000000..d56e9d6a
--- /dev/null
+++ b/lib/xtra/graf/8x8.png
Binary files differ
diff --git a/lib/xtra/graf/mask.bmp b/lib/xtra/graf/mask.bmp
new file mode 100644
index 00000000..fced8b05
--- /dev/null
+++ b/lib/xtra/graf/mask.bmp
Binary files differ
diff --git a/lib/xtra/graf/tome-128.png b/lib/xtra/graf/tome-128.png
new file mode 100644
index 00000000..31b79c31
--- /dev/null
+++ b/lib/xtra/graf/tome-128.png
Binary files differ
diff --git a/lib/xtra/music/delete.me b/lib/xtra/music/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/xtra/music/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/xtra/sound/Sound.cfg b/lib/xtra/sound/Sound.cfg
new file mode 100644
index 00000000..eb3a7f39
--- /dev/null
+++ b/lib/xtra/sound/Sound.cfg
@@ -0,0 +1,79 @@
+# sound.cfg
+#
+# Configuration file for the Angband sound events
+#
+# The format is:
+# name of the Angband event = list of the available sample-names seperated by spaces.
+#
+# Example:
+# hit = hit.wav hit1.wav
+#
+# Look at the definition of "angband_sound_name" in variable.c for a complete list of
+# all the available event names.
+
+[Sound]
+hit = hit.wav drop.wav hit1.wav
+miss = miss.wav miss1.wav
+flee = flee.wav
+drop = clunk.wav
+kill = kill.wav destroy.wav kill1.wav
+level = level.wav
+death = death.wav
+study =
+teleport =
+shoot =
+quaff =
+zap =
+walk =
+tpother =
+hitwall =
+eat = eat.wav
+store1 = money.wav
+store2 = money.wav
+store3 = money.wav
+store4 = money.wav
+dig = thump.wav
+opendoor = opendoor.wav
+shutdoor = shutdoor.wav
+tplevel = teleport.wav
+scroll =
+buy =
+sell =
+warn =
+rocket =
+n_kill =
+u_kill =
+quest =
+heal =
+x_heal =
+bite =
+claw =
+m_spell =
+summon =
+breath =
+ball =
+m_heal =
+atkspell =
+evil =
+touch =
+sting =
+crush =
+slime =
+wail =
+winner =
+fire =
+acid =
+elec =
+cold =
+illegal =
+fail =
+wakeup =
+invuln =
+fall =
+pain =
+destitem =
+moan =
+show =
+unused =
+explode =
+
diff --git a/lib/xtra/sound/readme.txt b/lib/xtra/sound/readme.txt
new file mode 100644
index 00000000..49bb86a1
--- /dev/null
+++ b/lib/xtra/sound/readme.txt
@@ -0,0 +1,33 @@
+New Sound Effect For Angband 2.8.1 Windows 95 (beta release)
+
+Changes since Alpha release:
+
+Altered sounds: breath.wav
+ death.wav
+ flee.wav
+ hit.wav
+ kill.wav
+
+
+New sounds: None (no new events yet)
+
+
+Useage:
+
+Unzip files into your angband 2.8.1 sounds directory EG:
+
+C:\angband-281\lib\xtra\sound
+
+That's it!
+
+If you wish to use the sounds for any other purpose or wish to include them as the default sounds
+in your variant of Angband, contact me at either:
+
+tim.haywood@ocean.co.uk or timothy.haywood@virgin.net
+
+Legal Stuff:
+
+This is a patch for Angband 2.8.1 (Windows95 Version).
+Copyright @ 1997 Tim Haywood
+Not for resale.
+Not for release with original version of Angband without permission.
diff --git a/src/.#dungeon.c.1.279.2.4 b/src/.#dungeon.c.1.279.2.4
new file mode 100644
index 00000000..1056358b
--- /dev/null
+++ b/src/.#dungeon.c.1.279.2.4
@@ -0,0 +1,6140 @@
+/* File: dungeon.c */
+
+/* Purpose: Angband game engine */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+extern lua_State* L;
+
+#define TY_CURSE_CHANCE 100
+#define DG_CURSE_CHANCE 50
+#define AUTO_CURSE_CHANCE 15
+#define CHAINSWORD_NOISE 100
+
+/*
+ * I created this when a bug misplaced my character and the game wasn't able
+ * to load it again.. very frustrating.
+ * So this hack will generate a new level without calling dungeon(), and
+ * then the normal behavior will apply.
+ */
+/* #define SAVE_HACK */
+#ifdef SAVE_HACK
+bool save_hack = TRUE;
+#endif
+
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 1 (Heavy).
+ */
+byte value_check_aux1(object_type *o_ptr)
+{
+ /* Artifacts */
+ if (artifact_p(o_ptr))
+ {
+ /* Cursed/Broken */
+ if (cursed_p(o_ptr)) return (SENSE_TERRIBLE);
+
+ /* Normal */
+ return (SENSE_SPECIAL);
+ }
+
+ /* Ego-Items */
+ if (ego_item_p(o_ptr))
+ {
+ /* Cursed/Broken */
+ if (cursed_p(o_ptr)) return (SENSE_WORTHLESS);
+
+ /* Normal */
+ return (SENSE_EXCELLENT);
+ }
+
+ /* Cursed items */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Good "armor" bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_HEAVY);
+
+ /* Good "weapon" bonus */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_HEAVY);
+
+ /* Default to "average" */
+ return (SENSE_AVERAGE);
+}
+
+byte value_check_aux1_magic(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+
+ switch (o_ptr->tval)
+ {
+ /* Scrolls, Potions, Wands, Staves and Rods */
+ case TV_SCROLL:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ /* "Cursed" scrolls/potions have a cost of 0 */
+ if (k_ptr->cost == 0) return (SENSE_TERRIBLE);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_SPECIAL);
+
+ /* Scroll of Nothing, Apple Juice, etc. */
+ if (k_ptr->cost < 3) return (SENSE_WORTHLESS);
+
+ /*
+ * Identify, Phase Door, Cure Light Wounds, etc. are
+ * just average
+ */
+ if (k_ptr->cost < 100) return (SENSE_AVERAGE);
+
+ /* Enchant Armor, *Identify*, Restore Stat, etc. */
+ if (k_ptr->cost < 10000) return (SENSE_GOOD_HEAVY);
+
+ /* Acquirement, Deincarnation, Strength, Blood of Life, ... */
+ if (k_ptr->cost >= 10000) return (SENSE_EXCELLENT);
+
+ break;
+ }
+
+ /* Food */
+ case TV_FOOD:
+ {
+ /* "Cursed" food */
+ if (k_ptr->cost == 0) return (SENSE_TERRIBLE);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_SPECIAL);
+
+ /* Normal food (no magical properties) */
+ if (k_ptr->cost <= 10) return (SENSE_AVERAGE);
+
+ /* Everything else is good */
+ if (k_ptr->cost > 10) return (SENSE_GOOD_HEAVY);
+
+ break;
+ }
+ }
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 2 (Light).
+ */
+byte value_check_aux2(object_type *o_ptr)
+{
+ /* Cursed items (all of them) */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Artifacts -- except cursed/broken ones */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Ego-Items -- except cursed/broken ones */
+ if (ego_item_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Good armor bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_LIGHT);
+
+ /* Good weapon bonuses */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_LIGHT);
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+byte value_check_aux2_magic(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+
+ switch (o_ptr->tval)
+ {
+ /* Scrolls, Potions, Wands, Staves and Rods */
+ case TV_SCROLL:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ /* "Cursed" scrolls/potions have a cost of 0 */
+ if (k_ptr->cost == 0) return (SENSE_CURSED);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Scroll of Nothing, Apple Juice, etc. */
+ if (k_ptr->cost < 3) return (SENSE_AVERAGE);
+
+ /*
+ * Identify, Phase Door, Cure Light Wounds, etc. are
+ * just average
+ */
+ if (k_ptr->cost < 100) return (SENSE_AVERAGE);
+
+ /* Enchant Armor, *Identify*, Restore Stat, etc. */
+ if (k_ptr->cost < 10000) return (SENSE_GOOD_LIGHT);
+
+ /* Acquirement, Deincarnation, Strength, Blood of Life, ... */
+ if (k_ptr->cost >= 10000) return (SENSE_GOOD_LIGHT);
+
+ break;
+ }
+
+ /* Food */
+ case TV_FOOD:
+ {
+ /* "Cursed" food */
+ if (k_ptr->cost == 0) return (SENSE_CURSED);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Normal food (no magical properties) */
+ if (k_ptr->cost <= 10) return (SENSE_AVERAGE);
+
+ /* Everything else is good */
+ if (k_ptr->cost > 10) return (SENSE_GOOD_LIGHT);
+
+ break;
+ }
+ }
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+/*
+ * Can a player be resurrected?
+ */
+static bool granted_resurrection(void)
+{
+ PRAY_GOD(GOD_ERU)
+ {
+ if (p_ptr->grace > 100000)
+ {
+ if (magik(70)) return (TRUE);
+ else return (FALSE);
+ }
+ }
+ return (FALSE);
+}
+
+byte select_sense(object_type *o_ptr, bool ok_combat, bool ok_magic)
+{
+ /* Valid "tval" codes */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_MSTAFF:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_BOOMERANG:
+ case TV_TRAPKIT:
+ {
+ if (ok_combat) return 1;
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ if (ok_magic) return 2;
+ break;
+ }
+
+ /* Dual use? */
+ case TV_DAEMON_BOOK:
+ {
+ if (ok_combat || ok_magic) return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Sense the inventory
+ *
+ * Combat items (weapons and armour) - Fast, weak if combat skill < 10, strong
+ * otherwise.
+ *
+ * Magic items (scrolls, staffs, wands, potions etc) - Slow, weak if
+ * magic skill < 10, strong otherwise.
+ *
+ * It shouldn't matter a lot to discriminate against magic users, because
+ * they learn one form of ID or another, and because most magic items are
+ * easy_know.
+ */
+static void sense_inventory(void)
+{
+ int i, combat_lev, magic_lev;
+
+ bool heavy_combat, heavy_magic;
+ bool ok_combat, ok_magic;
+
+ byte feel;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /*** Check for "sensing" ***/
+
+ /* No sensing when confused */
+ if (p_ptr->confused) return;
+
+ /* Can we pseudo id */
+#if 0
+
+ if (0 == rand_int(133 - get_skill_scale(SKILL_COMBAT, 130))) ok_combat = TRUE;
+ if (0 == rand_int(133 - get_skill_scale(SKILL_MAGIC, 130))) ok_magic = TRUE;
+
+#endif
+
+ /*
+ * In Angband, the chance of pseudo-id uses two different formulae:
+ *
+ * (1) Fast. 0 == rand_int(BASE / (plev * plev + 40)
+ * (2) Slow. 0 == rand_int(BASE / (plev + 5)
+ *
+ * Warriors: Fase with BASE == 9000
+ * Magi: Slow with BASE == 240000
+ * Priests: Fast with BASE == 10000
+ * Rogues: Fase with BASE == 20000
+ * Rangers: Slow with BASE == 120000
+ * Paladins: Fast with BASE == 80000
+ *
+ * In other words, those who have identify spells are penalised.
+ * The problems with Pern/Tome since it externalised player classes
+ * is that it uses the same and slow formula for spellcasters and
+ * fighters.
+ *
+ * In the following code, combat item pseudo-ID improves exponentially,
+ * (fast with BASE 9000) and magic one linear (slow with base 60000 --
+ * twice faster than V rangers).
+ *
+ * I hope this makes it closer to the original model -- pelpel
+ */
+
+ /* The combat skill affects weapon/armour pseudo-ID */
+ combat_lev = get_skill(SKILL_COMBAT);
+
+ /* Use the fast formula */
+ ok_combat = (0 == rand_int(9000L / (combat_lev * combat_lev + 40)));
+
+ /* The magic skill affects magic item pseudo-ID */
+ magic_lev = get_skill(SKILL_MAGIC);
+
+ /*
+ * Use the slow formula, because spellcasters have id spells
+ *
+ * Lowered the base value because V rangers are known to have
+ * pretty useless pseudo-ID. This should make it ten times more often.
+ */
+ ok_magic = (0 == rand_int(12000L / (magic_lev + 5)));
+
+ /* Both ID rolls failed */
+ if (!ok_combat && !ok_magic) return;
+
+ /* Higher skill levels give the player better sense of items */
+ heavy_combat = (combat_lev > 10) ? TRUE : FALSE;
+ heavy_magic = (magic_lev > 10) ? TRUE : FALSE;
+
+
+ /*** Sense everything ***/
+
+ /* Check everything */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ byte okay = 0;
+
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Valid "tval" codes */
+ okay = select_sense(o_ptr, ok_combat, ok_magic);
+
+ /* Skip non-sense machines */
+ if (!okay) continue;
+
+ /* We know about it already, do not tell us again */
+ if (o_ptr->ident & (IDENT_SENSE)) continue;
+
+ /* It is fully known, no information needed */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Occasional failure on inventory items */
+ if ((i < INVEN_WIELD) && (0 != rand_int(5))) continue;
+
+ /* Check for a feeling */
+ if (okay == 1)
+ {
+ feel = (heavy_combat ? value_check_aux1(o_ptr) : value_check_aux2(o_ptr));
+ }
+ else
+ {
+ feel = (heavy_magic ? value_check_aux1_magic(o_ptr) : value_check_aux2_magic(o_ptr));
+ }
+
+ /* Skip non-feelings */
+ if (feel == SENSE_NONE) continue;
+
+ /* Stop everything */
+ if (disturb_minor) disturb(0, 0);
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Message (equipment) */
+ if (i >= INVEN_WIELD)
+ {
+ msg_format("You feel the %s (%c) you are %s %s %s...",
+ o_name, index_to_label(i), describe_use(i),
+ ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+ }
+
+ /* Message (inventory) */
+ else
+ {
+ msg_format("You feel the %s (%c) in your pack %s %s...",
+ o_name, index_to_label(i),
+ ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+ }
+
+ /* We have "felt" it */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Set sense property */
+ o_ptr->sense = feel;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Squelch ! */
+ squeltch_inventory();
+}
+
+
+/*
+ * Go to any level (ripped off from wiz_jump)
+ */
+static void pattern_teleport(void)
+{
+ /* Ask for level */
+ if (get_check("Teleport level? "))
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ sprintf(ppp, "Teleport to level (0-%d): ", 99);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+ else if (get_check("Normal teleport? "))
+ {
+ teleport_player(200);
+ return;
+ }
+ else
+ {
+ return;
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > 99) command_arg = 99;
+
+ /* Accept request */
+ msg_format("You teleport to dungeon level %d.", command_arg);
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Change level */
+ dun_level = command_arg;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Returns TRUE if we are on the Straight Road...
+ */
+static bool pattern_effect(void)
+{
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_START) ||
+ (cave[p_ptr->py][p_ptr->px].feat > FEAT_PATTERN_XTRA2)) return (FALSE);
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_END)
+ {
+ (void)set_poisoned(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_blind(0);
+ (void)set_afraid(0);
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ (void)restore_level();
+ (void)hp_player(1000);
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_PATTERN_OLD);
+ msg_print("This section of the Straight Road looks less powerful.");
+ }
+
+
+ /*
+ * We could make the healing effect of the
+ * Pattern center one-time only to avoid various kinds
+ * of abuse, like luring the win monster into fighting you
+ * in the middle of the pattern...
+ */
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_OLD)
+ {
+ /* No effect */
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA1)
+ {
+ pattern_teleport();
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA2)
+ {
+ if (!(p_ptr->invuln))
+ take_hit(200, "walking the corrupted Straight Road");
+ }
+
+ else
+ {
+ if (!(p_ptr->invuln))
+ take_hit(damroll(1, 3), "walking the Straight Road");
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * If player has inscribed the object with "!!", let him know when it's
+ * recharged. -LM-
+ */
+static void recharged_notice(object_type *o_ptr)
+{
+ char o_name[80];
+
+ cptr s;
+
+
+ /* No inscription */
+ if (!o_ptr->note) return;
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process notification request. */
+ while (s)
+ {
+ /* Find another '!' */
+ if (s[1] == '!')
+ {
+ /* Describe (briefly) */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Notify the player */
+ if (o_ptr->number > 1)
+ {
+ msg_format("Your %s are recharged.", o_name);
+ }
+ else
+ {
+ msg_format("Your %s is recharged.", o_name);
+ }
+
+ /* Done. */
+ return;
+ }
+
+ /* Keep looking for '!'s */
+ s = strchr(s + 1, '!');
+ }
+}
+
+
+
+/*
+ * Regenerate hit points -RAK-
+ */
+static void regenhp(int percent)
+{
+ s32b new_chp, new_chp_frac;
+
+ int old_chp;
+
+
+ /* Only if alive */
+ if (!(p_ptr->necro_extra & CLASS_UNDEAD))
+ {
+ /* Save the old hitpoints */
+ old_chp = p_ptr->chp;
+
+ /* Extract the new hitpoints */
+ new_chp = ((long)p_ptr->mhp) * percent + PY_REGEN_HPBASE;
+
+ /* div 65536 */
+ p_ptr->chp += new_chp >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT;
+
+ /* mod 65536 */
+ new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac;
+
+ if (new_chp_frac >= 0x10000L)
+ {
+ p_ptr->chp_frac = new_chp_frac - 0x10000L;
+ p_ptr->chp++;
+ }
+ else
+ {
+ p_ptr->chp_frac = new_chp_frac;
+ }
+
+ /* Fully healed */
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Notice changes */
+ if (old_chp != p_ptr->chp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+}
+
+
+/*
+ * Regenerate mana points -RAK-
+ */
+static void regenmana(int percent)
+{
+ s32b new_mana, new_mana_frac;
+
+ int old_csp;
+
+ /* Incraese regen with int */
+ percent += adj_str_blow[p_ptr->stat_ind[A_INT]] * 3;
+
+ old_csp = p_ptr->csp;
+ new_mana = ((long)p_ptr->msp) * percent + PY_REGEN_MNBASE;
+ printf("percent(%d) regen(%d) new(%d)\n", percent, PY_REGEN_MNBASE, new_mana);
+
+ /* div 65536 */
+ p_ptr->csp += new_mana >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->csp < 0) && (old_csp > 0))
+ {
+ p_ptr->csp = MAX_SHORT;
+ }
+
+ /* mod 65536 */
+ new_mana_frac = (new_mana & 0xFFFF) + p_ptr->csp_frac;
+
+ if (new_mana_frac >= 0x10000L)
+ {
+ p_ptr->csp_frac = new_mana_frac - 0x10000L;
+ p_ptr->csp++;
+ }
+ else
+ {
+ p_ptr->csp_frac = new_mana_frac;
+ }
+
+ /* Must set frac to zero even if equal */
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Redraw mana */
+ if (old_csp != p_ptr->csp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+
+
+
+
+/*
+ * Regenerate the monsters (once per 100 game turns)
+ *
+ * XXX XXX XXX Should probably be done during monster turns.
+ */
+static void regen_monsters(void)
+{
+ int i, frac;
+
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ /* Allow regeneration (if needed) */
+ if (o_ptr->pval2 < o_ptr->pval3)
+ {
+ /* Hack -- Base regeneration */
+ frac = o_ptr->pval3 / 100;
+
+ /* Hack -- Minimal regeneration rate */
+ if (!frac) frac = 1;
+
+ /* Hack -- Some monsters regenerate quickly */
+ if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2;
+
+
+ /* Hack -- Regenerate */
+ o_ptr->pval2 += frac;
+
+ /* Do not over-regenerate */
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ /* Redraw (later) */
+ p_ptr->redraw |= (PR_MH);
+ }
+ }
+
+ /* Regenerate everyone */
+ for (i = 1; i < m_max; i++)
+ {
+ /* Check the i'th monster */
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Dont regen bleeding/poisonned monsters */
+ if (m_ptr->bleeding || m_ptr->poisoned) continue;
+
+ /* Allow regeneration (if needed) */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ /* Hack -- Base regeneration */
+ frac = m_ptr->maxhp / 100;
+
+ /* Hack -- Minimal regeneration rate */
+ if (!frac) frac = 1;
+
+ /* Hack -- Some monsters regenerate quickly */
+ if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2;
+
+
+ /* Hack -- Regenerate */
+ m_ptr->hp += frac;
+
+ /* Do not over-regenerate */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == i) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+}
+
+
+/*
+ * Forcibly pseudo-identify an object in the inventory
+ * (or on the floor)
+ */
+bool psychometry(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ byte feel;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Meditate on which item? ";
+ s = "You have nothing appropriate.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* It is fully known, no information needed */
+ if ((object_known_p(o_ptr)) || (o_ptr->ident & IDENT_SENSE))
+ {
+ msg_print("You cannot find out anything more about that.");
+ return (TRUE);
+ }
+
+ /* Check for a feeling */
+ feel = value_check_aux1_magic(o_ptr);
+ if (feel == SENSE_NONE) feel = value_check_aux1(o_ptr);
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Skip non-feelings */
+ if (!feel)
+ {
+ msg_format("You do not perceive anything unusual about the %s.", o_name);
+ return (TRUE);
+ }
+
+ msg_format("You feel that the %s %s %s...",
+ o_name, ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+
+ /* We have "felt" it */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Set sense property */
+ o_ptr->sense = feel;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+/*
+ * Does an object decay?
+ *
+ * Should belong to object1.c, renamed to object_decays() -- pelpel
+ */
+bool decays(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f3 & TR3_DECAY) return (TRUE);
+
+ return (FALSE);
+}
+
+
+static int process_lasting_spell(s16b music)
+{
+ int oldtop, use_mana;
+
+ if (music > 0) return FALSE;
+
+ oldtop = lua_gettop(L);
+
+ music = -music;
+
+ /* Push the function */
+ lua_getglobal(L, "exec_lasting_spell");
+
+ /* Push the spell */
+ tolua_pushnumber(L, music);
+
+ /* Call the function */
+ if (lua_call(L, 1, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling lasting spell");
+ return 0;
+ }
+
+ use_mana = tolua_getnumber(L, -(lua_gettop(L) - oldtop), 0);
+ lua_settop(L, oldtop);
+ return use_mana;
+}
+
+static void gere_class_special()
+{
+ switch (p_ptr->druid_extra2)
+ {
+ /* Lay a path of mana on the floor */
+ case CLASS_MANA_PATH:
+ {
+ /* Does the player have enought mana ? */
+ if (p_ptr->csp < (p_ptr->druid_extra & 255))
+ {
+ p_ptr->druid_extra = 0;
+ p_ptr->druid_extra2 = CLASS_NONE;
+ msg_print("You stop laying a mana path.");
+ }
+ else
+ {
+ /* Use some mana */
+ p_ptr->csp -= (p_ptr->druid_extra & 255);
+
+ if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE)
+ {
+ /* Absorb some of the mana of the grid */
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 50;
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ /* Set the new grid mana */
+ cave[p_ptr->py][p_ptr->px].mana = p_ptr->druid_extra & 255;
+ }
+ else
+ {
+ int m = cave[p_ptr->py][p_ptr->px].mana;
+
+ if (m + (p_ptr->druid_extra & 255) > 255)
+ {
+ cave[p_ptr->py][p_ptr->px].mana = 255;
+ }
+ else
+ {
+ cave[p_ptr->py][p_ptr->px].mana += p_ptr->druid_extra & 255;
+ }
+ }
+ }
+
+ break;
+ }
+
+ /* Lay a path of mana on the floor */
+ case CLASS_WINDS_MANA:
+ {
+ /* Does the player have enought mana ? */
+ if (p_ptr->csp < (p_ptr->druid_extra & 255))
+ {
+ p_ptr->druid_extra = CLASS_NONE;
+ msg_print("You stop expulsing mana winds.");
+ }
+ else
+ {
+ int dam = 0;
+
+ /* Use some mana */
+ p_ptr->csp -= (p_ptr->druid_extra & 255);
+
+ if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE)
+ {
+ dam = (p_ptr->druid_extra & 255) + 256;
+ }
+ else
+ {
+ dam = (p_ptr->druid_extra & 255);
+ }
+
+ fire_explosion(p_ptr->py, p_ptr->px, GF_WINDS_MANA, 2, dam);
+ }
+
+ break;
+ }
+
+ case CLASS_CANALIZE_MANA:
+ {
+ if (p_ptr->druid_extra & CLASS_CANALIZE_MANA_EXTRA)
+ {
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 10;
+ }
+ else
+ {
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 20;
+ }
+
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ cave[p_ptr->py][p_ptr->px].mana = 0;
+
+ break;
+ }
+
+ /* CLASS_NONE, possibly others? */
+ default:
+ {
+ /* No mana update */
+ return;
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static void check_music()
+{
+ int use_mana;
+
+ /* Music sung by player */
+ if (!p_ptr->music_extra) return;
+
+ use_mana = process_lasting_spell(p_ptr->music_extra);
+
+ if (p_ptr->csp < use_mana)
+ {
+ msg_print("You stop your spell.");
+ p_ptr->music_extra = MUSIC_NONE;
+ p_ptr->music_extra2 = MUSIC_NONE;
+ }
+ else
+ {
+ p_ptr->csp -= use_mana;
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * Generate the feature effect
+ */
+void apply_effect(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ feature_type *f_ptr = &f_info[c_ptr->feat];
+
+
+ if (f_ptr->d_frequency[0] != 0)
+ {
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (f_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % f_ptr->d_frequency[i]) == 0) &&
+ ((f_ptr->d_side[i] != 0) || (f_ptr->d_dice[i] != 0)))
+ {
+ int l, dam = 0;
+ int d = f_ptr->d_dice[i], s = f_ptr->d_side[i];
+
+ if (d == -1) d = p_ptr->lev;
+ if (s == -1) s = p_ptr->lev;
+
+ /* Roll damage */
+ for (l = 0; l < d; l++)
+ {
+ dam += randint(s);
+ }
+
+ /* Apply damage */
+ project( -100, 0, y, x, dam, f_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_HIDE);
+
+ /* Hack -- notice death */
+ if (!alive || death) return;
+ }
+ }
+ }
+}
+
+
+#if 0
+/*
+ * Activate corruptions' effects on player
+ *
+ * All the rolls against arbitrarily chosen numbers are normalised
+ * (i.e. zero). They might have some cabalistic(?) significance,
+ * but I seriously doubt if processors take care of the Judeo-Christian
+ * tradition :) -- pelpel
+ */
+static void process_corruption_effects(void)
+{}
+
+#endif
+
+
+
+#ifdef pelpel
+
+/*
+ * Handle staying spell effects once every 10 game turns
+ */
+static void process_effects(void)
+{
+ int i, j;
+
+ /* Every 10 game turns */
+ if (turn % 10) return;
+
+ /* Not in the small-scale wilderness map */
+ if (p_ptr->wild_mode) return;
+
+
+ /* Handle spell effects */
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (i = 0; i < cur_wid - 1; i++)
+ {
+ int e = cave[j][i].effect;
+
+ if (e)
+ {
+ effect_type *e_ptr = &effects[e];
+
+ if (e_ptr->time)
+ {
+ /* Apply damage */
+ project(0, 0, j, i, e_ptr->dam, e_ptr->type,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ else
+ {
+ cave[j][i].effect = 0;
+ }
+
+ /* Hack -- notice death */
+ if (!alive || death) return;
+
+ if (((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST)) || ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST)))
+ {
+ if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1)
+ cave[j][i].effect = 0;
+ }
+ }
+ }
+ }
+
+
+ /* Reduce & handle effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ /* Skip empty slots */
+ if (effects[i].time == 0) continue;
+
+ /* Reduce duration */
+ effects[i].time--;
+
+ /* Creates a "wave" effect*/
+ if (effects[i].flags & EFF_WAVE)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->rad++;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ /* Creates a "storm" effect*/
+ else if (effects[i].flags & EFF_STORM)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->cy = p_ptr->py;
+ e_ptr->cx = p_ptr->px;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ }
+
+ /* Apply sustained effect in the player grid, if any */
+ apply_effect(p_ptr->py, p_ptr->px);
+}
+
+#endif /* pelpel */
+
+
+/* XXX XXX XXX */
+bool is_recall = FALSE;
+
+
+/*
+ * Handle certain things once every 10 game turns
+ *
+ * Note that a single movement in the overhead wilderness mode
+ * consumes 132 times as much energy as a normal one...
+ */
+static void process_world(void)
+{
+ timer_type *t_ptr;
+
+ int x, y, i, j;
+
+ int regen_amount;
+ bool cave_no_regen = FALSE;
+ int upkeep_factor = 0;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+ object_kind *k_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /*
+ * Every 10 game turns -- which means this section is invoked once
+ * in a player turn under the normal speed, and 132 times in a move
+ * in the reduced map mode.
+ */
+ if (turn % 10) return;
+
+ /*
+ * I don't know if this is the right thing to do because I'm totally
+ * ignorant (yes, I must admit that...) about the scripting part of
+ * the game, but since there have been complaints telling us it
+ * runs terribly slow in the reduced map mode... -- pelpel
+ *
+ * Note to coders: if it is desirable to make this active in the
+ * reduced map mode, remove the if condition surrounding the line
+ * and move the code inside into every 1000 game turns section.
+ */
+ if (dun_level || (!p_ptr->wild_mode))
+ {
+ /* Let the script live! */
+ process_hooks(HOOK_PROCESS_WORLD, "()");
+
+ /* Handle the player song */
+ check_music();
+ }
+
+ /* Handle the timers */
+ for (t_ptr = gl_timers; t_ptr != NULL; t_ptr = t_ptr->next)
+ {
+ if (!t_ptr->enabled) continue;
+
+ t_ptr->countdown--;
+ if (!t_ptr->countdown)
+ {
+ t_ptr->countdown = t_ptr->delay;
+ call_lua(t_ptr->callback, "()", "");
+ }
+ }
+
+ /* Handle class special actions */
+ gere_class_special();
+
+ /* Check the fate */
+ if (fate_option && (p_ptr->lev > 10))
+ {
+ /*
+ * WAS: == 666 against randint(50000).
+ * Since CPU's don't know Judeo-Christian / Cabalistic traditions,
+ * and since comparisons with zero is more efficient in many
+ * architectures...
+ */
+ if (rand_int(50000) == 0) gain_fate(0);
+ }
+
+ /*** Is the wielded monsters still hypnotised ***/
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ if ((randint(1000) < r_ptr->level - ((p_ptr->lev * 2) + get_skill(SKILL_SYMBIOTIC))))
+ {
+ msg_format("%s thinks you are not enough in symbiosis.",
+ symbiote_name(TRUE));
+ carried_make_attack_normal(o_ptr->pval);
+ }
+ }
+
+ /*** Check the Time and Load ***/
+
+ /*
+ * Every 1000 game turns -- which means this section is invoked every
+ * 100 player turns under the normal speed, and slightly more than
+ * one per move in the reduced map.
+ */
+ if ((turn % 1000) == 0)
+ {
+ /* Check time and load */
+ if ((0 != check_time()) || (0 != check_load()))
+ {
+ /* Warning */
+ if (closing_flag <= 2)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Count warnings */
+ closing_flag++;
+
+ /* Message */
+ msg_print("The gates to ANGBAND are closing...");
+ msg_print("Please finish up and/or save your game.");
+ }
+
+ /* Slam the gate */
+ else
+ {
+ /* Message */
+ msg_print("The gates to ANGBAND are now closed.");
+
+ /* Stop playing */
+ alive = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ }
+ }
+
+ /*** Attempt timed autosave ***/
+ if (autosave_t && autosave_freq)
+ {
+ if ((turn % ((s32b)autosave_freq * 10)) == 0)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+ }
+
+
+ /*** Handle the wilderness/town (sunshine) ***/
+
+ /* While in town/wilderness and not in the overworld mode */
+ if (!dun_level && !p_ptr->wild_mode)
+ {
+ /* Hack -- Daybreak/Nighfall in town */
+ if ((turn % ((10L * DAY) / 2)) == 0)
+ {
+ bool dawn;
+
+ /* Check for dawn */
+ dawn = ((turn % (10L * DAY)) == 0);
+
+ /* Day breaks */
+ if (dawn)
+ {
+ /* Message */
+ msg_print("The sun has risen.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Assume lit */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Hack -- Memorize lit grids if allowed */
+ if (view_perma_grids) c_ptr->info |= (CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+
+ /* Night falls */
+ else
+ {
+ /* Message */
+ msg_print("The sun has fallen.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Darken "boring" features */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+ }
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /* Tell a day passed */
+ if (((turn + (DAY_START * 10L)) % (10L * DAY)) == 0)
+ {
+ char buf[20];
+
+ sprintf(buf, get_day(bst(YEAR, turn) + START_YEAR));
+ cmsg_format(TERM_L_GREEN,
+ "Today it is %s of the %s year of the third age.",
+ get_month_name(bst(DAY, turn), wizard, FALSE), buf);
+ }
+
+ /* Set back the rewards once a day */
+ if ((turn % (10L * STORE_TURNS)) == 0)
+ {
+ /* Select new bounties. */
+ if (magik(20)) select_bounties();
+ }
+
+ /* Modify loan */
+ if (p_ptr->loan)
+ {
+ if (p_ptr->loan_time) p_ptr->loan_time--;
+
+ if (((turn % 5000) == 0) && !p_ptr->loan_time)
+ {
+ cmsg_print(TERM_RED, "You should pay your loan...");
+
+ p_ptr->loan += p_ptr->loan / 12;
+
+ if (p_ptr->loan > PY_MAX_GOLD) p_ptr->loan = PY_MAX_GOLD;
+
+ /* Do a nasty stuff */
+ if (p_ptr->wild_mode && rand_int(2))
+ {
+ /* Discount player items */
+ int z = 0, tries = 200;
+ object_type *o_ptr = NULL;
+
+ while (tries--)
+ {
+ z = rand_int(INVEN_TOTAL);
+ o_ptr = &p_ptr->inventory[z];
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->discount >= 100) continue;
+
+ break;
+ }
+
+ if (tries)
+ {
+ o_ptr->discount += 70;
+ if (o_ptr->discount >= 100) o_ptr->discount = 100;
+
+ inven_item_optimize(z);
+ inven_item_describe(z);
+
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ }
+
+ else
+ {
+ int merc = test_monster_name("Mean-looking mercenary");
+ int agent = test_monster_name("Agent of the black market");
+ int num = 5 + (p_ptr->lev / 3), z;
+
+ for (z = 0; z < num; z++)
+ {
+ int yy, xx, attempts = 200, m_idx;
+
+ /* Summon */
+ while (1)
+ {
+ scatter(&yy, &xx, p_ptr->py, p_ptr->px, 6, 0);
+
+ /* Accept an empty grid within the boundary */
+ if (in_bounds(yy, xx) && cave_floor_bold(yy, xx)) break;
+
+ /* Max number of retries reached */
+ if (--attempts == 0) break;
+ }
+
+ /* All the attempts failed */
+ if (attempts == 0) continue;
+
+ /* Summon a monster */
+ m_idx = place_monster_one(yy, xx, magik(80) ? merc : agent,
+ 0, FALSE, MSTATUS_ENEMY);
+
+ /* Level it */
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+
+ m_ptr->exp = MONSTER_EXP(p_ptr->lev * 2);
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+ }
+ }
+
+ /*** Process the monsters ***/
+
+ /* Check for creature generation. */
+ if (!p_ptr->wild_mode &&
+ !p_ptr->inside_arena &&
+ !p_ptr->inside_quest &&
+ (rand_int(d_info[(dun_level) ? dungeon_type : DUNGEON_WILDERNESS].max_m_alloc_chance) == 0))
+ {
+ /* Make a new monster */
+ if (!(dungeon_flags2 & DF2_NO_NEW_MONSTER))
+ {
+ (void)alloc_monster(MAX_SIGHT + 5, FALSE);
+ }
+ }
+
+ /* Hack -- Check for creature regeneration */
+ if (!p_ptr->wild_mode && ((turn % 100) == 0)) regen_monsters();
+
+
+ /*** Damage over Time ***/
+
+ /* Take damage from poison */
+ if (p_ptr->poisoned && !p_ptr->invuln)
+ {
+ /* Take damage */
+ take_hit(1, "poison");
+ }
+
+
+ /* Vampires take damage from sunlight */
+ if (p_ptr->sensible_lite)
+ {
+ if ((!dun_level) && (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ if (cave[p_ptr->py][p_ptr->px].info & (CAVE_GLOW))
+ {
+ /* Take damage */
+ msg_print("The sun's rays scorch your undead flesh!");
+ take_hit(1, "sunlight");
+ cave_no_regen = TRUE;
+ drop_from_wild();
+ }
+ }
+
+ if ((p_ptr->inventory[INVEN_LITE].tval != 0) &&
+ (p_ptr->inventory[INVEN_LITE].sval >= SV_LITE_GALADRIEL) &&
+ (p_ptr->inventory[INVEN_LITE].sval <= SV_STONE_LORE) &&
+ (p_ptr->inventory[INVEN_LITE].sval != SV_LITE_UNDEATH))
+ {
+ object_type * o_ptr = &p_ptr->inventory[INVEN_LITE];
+ char o_name [80];
+ char ouch [80];
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ msg_format("The %s scorches your undead flesh!", o_name);
+
+ cave_no_regen = TRUE;
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, TRUE, 0);
+
+ sprintf(ouch, "wielding %s", o_name);
+ take_hit(1, ouch);
+ }
+ }
+
+ /* Drown in deep water unless the player have levitation, water walking
+ water breathing, or magic breathing.*/
+ if (!p_ptr->ffall && !p_ptr->walk_water && !p_ptr->magical_breath &&
+ !p_ptr->water_breath &&
+ (cave[p_ptr->py][p_ptr->px].feat == FEAT_DEEP_WATER))
+ {
+ if (calc_total_weight() > ((weight_limit()) / 2))
+ {
+ /* Take damage */
+ msg_print("You are drowning!");
+ take_hit(randint(p_ptr->lev), "drowning");
+ cave_no_regen = TRUE;
+ }
+ }
+
+
+ /* Spectres -- take damage when moving through walls */
+
+ /*
+ * Added: ANYBODY takes damage if inside through walls
+ * without wraith form -- NOTE: Spectres will never be
+ * reduced below 0 hp by being inside a stone wall; others
+ * WILL BE!
+ */
+ if (!cave_floor_bold(p_ptr->py, p_ptr->px))
+ {
+ int feature = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Player can walk through or fly over trees */
+ if ((has_ability(AB_TREE_WALK) || p_ptr->fly) && (feature == FEAT_TREES))
+ {
+ /* Do nothing */
+ }
+ /* Player can climb over mountains */
+ else if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB))
+ {
+ /* Do nothing */
+ }
+ else if (PRACE_FLAG(PR1_SEMI_WRAITH) && (!p_ptr->wraith_form) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_CAN_PASS))
+ {
+ int amt = 1 + ((p_ptr->lev) / 5);
+
+ cave_no_regen = TRUE;
+ if (amt > p_ptr->chp - 1) amt = p_ptr->chp - 1;
+ take_hit(amt, " walls ...");
+ }
+ }
+
+
+ /* Take damage from cuts */
+ if ((p_ptr->cut) && !(p_ptr->invuln))
+ {
+ /* Mortal wound or Deep Gash */
+ if (p_ptr->cut > 200)
+ {
+ i = 3;
+ }
+
+ /* Severe cut */
+ else if (p_ptr->cut > 100)
+ {
+ i = 2;
+ }
+
+ /* Other cuts */
+ else
+ {
+ i = 1;
+ }
+
+ /* Take damage */
+ take_hit(i, "a fatal wound");
+ }
+
+
+ /*** Check the Food, and Regenerate ***/
+
+ /* Digest normally */
+ if (p_ptr->food < PY_FOOD_MAX)
+ {
+ /* Every 100 game turns */
+ if ((turn % 100) == 0)
+ {
+ int speed_use = p_ptr->pspeed;
+
+ /* Maximum */
+ if (speed_use > 199)
+ {
+ speed_use = 199;
+ }
+
+ /* Minimum */
+ else if (speed_use < 0)
+ {
+ speed_use = 0;
+ }
+
+ /* Basic digestion rate based on speed */
+ i = extract_energy[speed_use] * 2;
+
+ /* Regeneration takes more food */
+ if (p_ptr->regenerate) i += 30;
+
+ /* Regeneration takes more food */
+ if (p_ptr->tim_regen) i += p_ptr->tim_regen_pow / 10;
+
+ /* Invisibility consume a lot of food */
+ i += p_ptr->invis / 2;
+
+ /* Invulnerability consume a lot of food */
+ if (p_ptr->invuln) i += 40;
+
+ /* Wraith Form consume a lot of food */
+ if (p_ptr->wraith_form) i += 30;
+
+ /* Get the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Examine the sword */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hitpoints multiplier consume a lot of food */
+ if (o_ptr->k_idx && (f2 & (TR2_LIFE))) i += o_ptr->pval * 5;
+
+ /* Slow digestion takes less food */
+ if (p_ptr->slow_digest) i -= 10;
+
+ /* Minimal digestion */
+ if (i < 1) i = 1;
+
+ /* Digest some food */
+ (void)set_food(p_ptr->food - i);
+ }
+ }
+
+ /* Digest quickly when gorged */
+ else
+ {
+ /* Digest a lot of food */
+ (void)set_food(p_ptr->food - 100);
+ }
+
+ /* Starve to death (slowly) */
+ if (p_ptr->food < PY_FOOD_STARVE)
+ {
+ /* Calculate damage */
+ i = (PY_FOOD_STARVE - p_ptr->food) / 10;
+
+ /* Take damage */
+ if (!(p_ptr->invuln)) take_hit(i, "starvation");
+ }
+
+ /* Default regeneration */
+ regen_amount = PY_REGEN_NORMAL;
+
+ /* Getting Weak */
+ if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ /* Lower regeneration */
+ if (p_ptr->food < PY_FOOD_STARVE)
+ {
+ regen_amount = 0;
+ }
+ else if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ regen_amount = PY_REGEN_FAINT;
+ }
+ else
+ {
+ regen_amount = PY_REGEN_WEAK;
+ }
+
+ /* Getting Faint */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ /* Faint occasionally */
+ if (!p_ptr->paralyzed && (rand_int(100) < 10))
+ {
+ /* Message */
+ msg_print("You faint from the lack of food.");
+ disturb(1, 0);
+
+ /* Hack -- faint (bypass free action) */
+ (void)set_paralyzed(p_ptr->paralyzed + 1 + rand_int(5));
+ }
+ }
+ }
+
+ /* Are we walking the pattern? */
+ if (!p_ptr->wild_mode && pattern_effect())
+ {
+ cave_no_regen = TRUE;
+ }
+ else
+ {
+ /* Regeneration ability */
+ if (p_ptr->regenerate)
+ {
+ regen_amount = regen_amount * 2;
+ }
+ }
+
+
+ /* Searching or Resting */
+ if (p_ptr->searching || resting)
+ {
+ regen_amount = regen_amount * 2;
+ }
+
+ if (total_friends)
+ {
+ int upkeep_divider = 20;
+
+ if (has_ability(AB_PERFECT_CASTING))
+ upkeep_divider = 15;
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard) msg_format("Total friends: %d.", total_friends);
+
+#endif /* TRACK_FRIENDS */
+
+ if (total_friends > 1 + (p_ptr->lev / (upkeep_divider)))
+ {
+ upkeep_factor = (total_friend_levels);
+
+ if (upkeep_factor > 100) upkeep_factor = 100;
+ else if (upkeep_factor < 10) upkeep_factor = 10;
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard) msg_format("Levels %d, upkeep %d", total_friend_levels,
+ upkeep_factor);
+
+#endif /* TRACK_FRIENDS */
+ }
+ }
+
+ /* Regenerate the mana */
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ if (upkeep_factor)
+ {
+ s16b upkeep_regen = (((100 - upkeep_factor) * regen_amount) / 100);
+ regenmana(upkeep_regen);
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard)
+ {
+ msg_format("Regen: %d/%d", upkeep_regen, regen_amount);
+ }
+
+#endif /* TRACK_FRIENDS */
+ }
+ else
+ {
+ regenmana(regen_amount);
+ }
+ }
+
+ /* Eru piety incraese with time */
+ if (((turn % 100) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode))
+ {
+ NOT_PRAY_GOD(GOD_ERU)
+ {
+ int inc = wisdom_scale(10);
+
+ /* Increase by wisdom/4 */
+ if (!inc) inc = 1;
+ inc_piety(GOD_ERU, inc);
+ }
+ }
+ /* Most gods piety decrease with time */
+ if (((turn % 300) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ GOD(GOD_MANWE)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ PRAY_GOD(GOD_MANWE)
+ dec++;
+ if (PRACE_FLAG(PR1_ELF))
+ dec -= wisdom_scale(2);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_MANWE, -dec);
+ }
+ GOD(GOD_MELKOR)
+ {
+ int dec = 8 - wisdom_scale(6);
+
+ PRAY_GOD(GOD_MELKOR)
+ dec++;
+ if (PRACE_FLAG(PR1_ELF))
+ dec += 5 - wisdom_scale(4);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_MELKOR, -dec);
+ }
+ PRAY_GOD(GOD_TULKAS)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_TULKAS, -dec);
+ }
+ }
+ /* Yavanna piety decrease with time */
+ if (((turn % 400) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ GOD(GOD_YAVANNA)
+ {
+ int dec = 5 - wisdom_scale(3);
+
+ /* Blech what an hideous hack */
+ if (!strcmp(rp_ptr->title + rp_name, "Ent"))
+ dec -= wisdom_scale(2);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_YAVANNA, -dec);
+ }
+ }
+ p_ptr->did_nothing = FALSE;
+
+ /* Increase regen by tim regen */
+ if (p_ptr->tim_regen) regen_amount += p_ptr->tim_regen_pow;
+
+ /* Poisoned or cut yields no healing */
+ if (p_ptr->poisoned) regen_amount = 0;
+ if (p_ptr->cut) regen_amount = 0;
+
+ /* Special floor -- Pattern, in a wall -- yields no healing */
+ if (cave_no_regen) regen_amount = 0;
+
+ /* Being over grass allows Yavanna to regen you */
+ PRAY_GOD(GOD_YAVANNA)
+ {
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_GRASS)
+ {
+ regen_amount += 200 + wisdom_scale(800);
+ }
+ }
+
+ /* Regenerate Hit Points if needed */
+ if ((p_ptr->chp < p_ptr->mhp) && !cave_no_regen)
+ {
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_END) &&
+ (cave[p_ptr->py][p_ptr->px].feat >= FEAT_PATTERN_START))
+ {
+ /* Hmmm. this should never happen? */
+ regenhp(regen_amount / 5);
+ }
+ else
+ {
+ regenhp(regen_amount);
+ }
+ }
+
+
+ /*** Timeout Various Things ***/
+
+ /* Handle temporary stat drains */
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0)
+ {
+ p_ptr->stat_cnt[i]--;
+ if (p_ptr->stat_cnt[i] == 0)
+ {
+ do_res_stat(i, FALSE);
+ }
+ }
+ }
+
+ /* Hack -- Hallucinating */
+ if (p_ptr->image)
+ {
+ (void)set_image(p_ptr->image - 1);
+ }
+
+ /* Holy Aura */
+ if (p_ptr->holy)
+ {
+ (void)set_holy(p_ptr->holy - 1);
+ }
+
+ /* Soul absorbtion */
+ if (p_ptr->absorb_soul)
+ {
+ (void)set_absorb_soul(p_ptr->absorb_soul - 1);
+ }
+
+ /* Undead loose Death Points */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ int old_chp = p_ptr->chp;
+ int warning = (p_ptr->mhp * hitpoint_warn / 10);
+
+ /* Bypass invulnerability and wraithform */
+ p_ptr->chp--;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Dead player */
+ if (p_ptr->chp < 0)
+ {
+ bool old_quick = quick_messages;
+
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ if (!last_words)
+ {
+ msg_print("You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ char death_message[80];
+
+ (void)get_rnd_line("death.txt", death_message);
+ msg_print(death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, "being undead too long");
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* No longer a winner */
+ total_winner = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Note death */
+ death = TRUE;
+
+ quick_messages = FALSE;
+ if (get_check("Make a last screenshot? "))
+ {
+ do_cmd_html_dump();
+ }
+ quick_messages = old_quick;
+
+ /* Dead */
+ return;
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->chp < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_chp > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ msg_print("*** LOW DEATHPOINT WARNING! ***");
+ msg_print(NULL);
+ }
+ }
+
+ /* Walk water */
+ if (p_ptr->walk_water)
+ {
+ (void)set_walk_water(p_ptr->walk_water - 1);
+ }
+
+ /* True Strike */
+ if (p_ptr->strike)
+ {
+ (void)set_strike(p_ptr->strike - 1);
+ }
+
+ /* Meditation */
+ if (p_ptr->meditation)
+ {
+ (void)set_meditation(p_ptr->meditation - 1);
+ }
+
+ /* Timed project */
+ if (p_ptr->tim_project)
+ {
+ (void)set_project(p_ptr->tim_project - 1, p_ptr->tim_project_gf, p_ptr->tim_project_dam, p_ptr->tim_project_rad, p_ptr->tim_project_flag);
+ }
+
+ /* Timed roots */
+ if (p_ptr->tim_roots)
+ {
+ (void)set_roots(p_ptr->tim_roots - 1, p_ptr->tim_roots_ac, p_ptr->tim_roots_dam);
+ }
+
+ /* Timed breath */
+ if (p_ptr->tim_water_breath)
+ {
+ (void)set_tim_breath(p_ptr->tim_water_breath - 1, FALSE);
+ }
+ if (p_ptr->tim_magic_breath)
+ {
+ (void)set_tim_breath(p_ptr->tim_magic_breath - 1, TRUE);
+ }
+
+ /* Timed regen */
+ if (p_ptr->tim_regen)
+ {
+ (void)set_tim_regen(p_ptr->tim_regen - 1, p_ptr->tim_regen_pow);
+ }
+
+ /* Timed Disrupt shield */
+ if (p_ptr->disrupt_shield)
+ {
+ (void)set_disrupt_shield(p_ptr->disrupt_shield - 1);
+ }
+
+ /* Timed Parasite */
+ if (p_ptr->parasite)
+ {
+ (void)set_parasite(p_ptr->parasite - 1, p_ptr->parasite_r_idx);
+ }
+
+ /* Timed Reflection */
+ if (p_ptr->tim_reflect)
+ {
+ (void)set_tim_reflect(p_ptr->tim_reflect - 1);
+ }
+
+ /* Timed Prob Travel */
+ if (p_ptr->prob_travel)
+ {
+ (void)set_prob_travel(p_ptr->prob_travel - 1);
+ }
+
+ /* Timed Time Resistance */
+ if (p_ptr->tim_res_time)
+ {
+ (void)set_tim_res_time(p_ptr->tim_res_time - 1);
+ }
+
+ /* Timed Levitation */
+ if (p_ptr->tim_ffall)
+ {
+ (void)set_tim_ffall(p_ptr->tim_ffall - 1);
+ }
+ if (p_ptr->tim_fly)
+ {
+ (void)set_tim_fly(p_ptr->tim_fly - 1);
+ }
+
+ /* Thunderstorm */
+ if (p_ptr->tim_thunder)
+ {
+ int dam = damroll(p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2);
+ int i, tries = 600;
+ monster_type *m_ptr = NULL;
+
+ while (tries)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i = rand_range(1, m_max - 1)];
+
+ tries--;
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Cant see ? cant hit */
+ if (!los(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) continue;
+
+ /* Do not hurt friends! */
+ if (is_friend(m_ptr) >= 0) continue;
+ break;
+ }
+
+ if (tries)
+ {
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0);
+ msg_format("Lightning strikes %s.", m_name);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_ELEC,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_LITE,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_SOUND,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+
+ (void)set_tim_thunder(p_ptr->tim_thunder - 1, p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2);
+ }
+
+ /* Poisonned hands */
+ if (p_ptr->tim_poison)
+ {
+ (void)set_poison(p_ptr->tim_poison - 1);
+ }
+
+ /* Timed Fire Aura */
+ if (p_ptr->tim_fire_aura)
+ {
+ (void)set_tim_fire_aura(p_ptr->tim_fire_aura - 1);
+ }
+
+ /* Brightness */
+ if (p_ptr->tim_lite)
+ {
+ (void)set_lite(p_ptr->tim_lite - 1);
+ }
+
+ /* Blindness */
+ if (p_ptr->blind)
+ {
+ (void)set_blind(p_ptr->blind - 1);
+ }
+
+ /* Timed no_breeds */
+ if (no_breeds)
+ {
+ (void)set_no_breeders(no_breeds - 1);
+ }
+
+ /* Timed mimic */
+ if (p_ptr->tim_mimic)
+ {
+ (void)set_mimic(p_ptr->tim_mimic - 1, p_ptr->mimic_form, p_ptr->mimic_level);
+ }
+
+ /* Timed special move commands */
+ if (p_ptr->immov_cntr)
+ {
+ p_ptr->immov_cntr--;
+ }
+
+ /* Timed invisibility */
+ if (p_ptr->tim_invisible)
+ {
+ (void)set_invis(p_ptr->tim_invisible - 1, p_ptr->tim_inv_pow);
+ }
+
+ /* Times see-invisible */
+ if (p_ptr->tim_invis)
+ {
+ (void)set_tim_invis(p_ptr->tim_invis - 1);
+ }
+
+ if (multi_rew)
+ {
+ multi_rew = FALSE;
+ }
+
+ /* Timed esp */
+ if (p_ptr->tim_esp)
+ {
+ (void)set_tim_esp(p_ptr->tim_esp - 1);
+ }
+
+ /* Timed infra-vision */
+ if (p_ptr->tim_infra)
+ {
+ (void)set_tim_infra(p_ptr->tim_infra - 1);
+ }
+
+ /* Paralysis */
+ if (p_ptr->paralyzed)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed - 1);
+ }
+
+ /* Confusion */
+ if (p_ptr->confused)
+ {
+ (void)set_confused(p_ptr->confused - 1);
+ }
+
+ /* Afraid */
+ if (p_ptr->afraid)
+ {
+ (void)set_afraid(p_ptr->afraid - 1);
+ }
+
+ /* Fast */
+ if (p_ptr->fast)
+ {
+ (void)set_fast(p_ptr->fast - 1, p_ptr->speed_factor);
+ }
+
+ /* Light speed */
+ if (p_ptr->lightspeed)
+ {
+ (void)set_light_speed(p_ptr->lightspeed - 1);
+ }
+
+ /* Slow */
+ if (p_ptr->slow)
+ {
+ (void)set_slow(p_ptr->slow - 1);
+ }
+
+ /* Protection from evil */
+ if (p_ptr->protevil)
+ {
+ (void)set_protevil(p_ptr->protevil - 1);
+ }
+
+ /* Protection from good */
+ if (p_ptr->protgood)
+ {
+ (void)set_protgood(p_ptr->protgood - 1);
+ }
+
+ /* Protection from undead */
+ if (p_ptr->protundead)
+ {
+ (void)set_protundead(p_ptr->protundead - 1);
+ }
+
+ /* Invulnerability */
+ if (p_ptr->invuln)
+ {
+ (void)set_invuln(p_ptr->invuln - 1);
+ }
+
+ /* Wraith form */
+ if (p_ptr->tim_wraith)
+ {
+ (void)set_shadow(p_ptr->tim_wraith - 1);
+ }
+
+ /* Heroism */
+ if (p_ptr->hero)
+ {
+ (void)set_hero(p_ptr->hero - 1);
+ }
+
+ /* Super Heroism */
+ if (p_ptr->shero)
+ {
+ (void)set_shero(p_ptr->shero - 1);
+ }
+
+ /* Blessed */
+ if (p_ptr->blessed)
+ {
+ (void)set_blessed(p_ptr->blessed - 1);
+ }
+
+ /* Shield */
+ if (p_ptr->shield)
+ {
+ (void)set_shield(p_ptr->shield - 1, p_ptr->shield_power, p_ptr->shield_opt, p_ptr->shield_power_opt, p_ptr->shield_power_opt2);
+ }
+
+ /* Oppose Acid */
+ if (p_ptr->oppose_acid)
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid - 1);
+ }
+
+ /* Oppose Lightning */
+ if (p_ptr->oppose_elec)
+ {
+ (void)set_oppose_elec(p_ptr->oppose_elec - 1);
+ }
+
+ /* Oppose Fire */
+ if (p_ptr->oppose_fire)
+ {
+ (void)set_oppose_fire(p_ptr->oppose_fire - 1);
+ }
+
+ /* Oppose Cold */
+ if (p_ptr->oppose_cold)
+ {
+ (void)set_oppose_cold(p_ptr->oppose_cold - 1);
+ }
+
+ /* Oppose Poison */
+ if (p_ptr->oppose_pois)
+ {
+ (void)set_oppose_pois(p_ptr->oppose_pois - 1);
+ }
+
+ /* Oppose Light & Dark */
+ if (p_ptr->oppose_ld)
+ {
+ (void)set_oppose_ld(p_ptr->oppose_ld - 1);
+ }
+
+ /* Oppose Chaos & Confusion */
+ if (p_ptr->oppose_cc)
+ {
+ (void)set_oppose_cc(p_ptr->oppose_cc - 1);
+ }
+
+ /* Oppose Sound & Shards */
+ if (p_ptr->oppose_ss)
+ {
+ (void)set_oppose_ss(p_ptr->oppose_ss - 1);
+ }
+
+ /* Oppose Nexus */
+ if (p_ptr->oppose_nex)
+ {
+ (void)set_oppose_nex(p_ptr->oppose_nex - 1);
+ }
+
+ /* Mental Barrier */
+ if (p_ptr->tim_mental_barrier)
+ {
+ (void)set_mental_barrier(p_ptr->tim_mental_barrier - 1);
+ }
+
+ /* The rush */
+ if (p_ptr->rush)
+ {
+ (void)set_rush(p_ptr->rush - 1);
+ }
+
+
+ /* Timed mimicry */
+ if (get_skill(SKILL_MIMICRY))
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ if ((att & CLASS_LEGS) || (att & CLASS_WALL) || (att & CLASS_ARMS))
+ {
+ value--;
+
+ if (!value)
+ {
+ if (att & CLASS_LEGS) msg_print("You lose your extra pair of legs.");
+ if (att & CLASS_ARMS) msg_print("You lose your extra pair of arms.");
+ if (att & CLASS_WALL) msg_print("You lose your affinity for walls.");
+
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_LEGS);
+ att &= ~(CLASS_WALL);
+
+ if (disturb_state) disturb(0, 0);
+ }
+
+ p_ptr->update |= (PU_BODY);
+ p_ptr->mimic_extra = att + (value << 16);
+ }
+ }
+
+
+ /*** Poison and Stun and Cut ***/
+
+ /* Poison */
+ if (p_ptr->poisoned)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Apply some healing */
+ (void)set_poisoned(p_ptr->poisoned - adjust);
+ }
+
+ /* Stun */
+ if (p_ptr->stun)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Apply some healing */
+ (void)set_stun(p_ptr->stun - adjust);
+ }
+
+ /* Cut */
+ if (p_ptr->cut)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Hack -- Truly "mortal" wound */
+ if (p_ptr->cut > 1000) adjust = 0;
+
+ /* Apply some healing */
+ (void)set_cut(p_ptr->cut - adjust);
+ }
+
+ /* Hack - damage done by the dungeon -SC- */
+ if ((dun_level != 0) && (d_ptr->d_frequency[0] != 0))
+ {
+ int i, j, k;
+
+ /* Apply damage to every grid in the dungeon */
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (d_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % d_ptr->d_frequency[i]) == 0) &&
+ ((d_ptr->d_side[i] != 0) || (d_ptr->d_dice[i] != 0)))
+ {
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (k = 0; k < cur_wid - 1; k++)
+ {
+ int l, dam = 0;
+
+ if (!(dungeon_flags1 & DF1_DAMAGE_FEAT))
+ {
+ /* If the grid is empty, skip it */
+ if ((cave[j][k].o_idx == 0) &&
+ ((j != p_ptr->py) && (i != p_ptr->px))) continue;
+ }
+
+ /* Let's not hurt poor monsters */
+ if (cave[j][k].m_idx) continue;
+
+ /* Roll damage */
+ for (l = 0; l < d_ptr->d_dice[i]; l++)
+ {
+ dam += randint(d_ptr->d_side[i]);
+ }
+
+ /* Apply damage */
+ project( -100, 0, j, k, dam, d_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ }
+ }
+ }
+ }
+
+#ifndef pelpel
+
+ /* handle spell effects */
+ if (!p_ptr->wild_mode)
+ {
+ /*
+ * I noticed significant performance degrade after the introduction
+ * of staying spell effects. I believe serious optimisation effort
+ * is required before another release.
+ *
+ * More important is to fix that display weirdness...
+ *
+ * It seems that the game never expects that monster deaths and
+ * terrain feature changes should happen here... Moving these
+ * to process_player() [before resting code, with "every 10 game turn"
+ * 'if'] may or may not fix the problem... -- pelpel to DG
+ */
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (i = 0; i < cur_wid - 1; i++)
+ {
+ int e = cave[j][i].effect;
+
+ if (e)
+ {
+ effect_type *e_ptr = &effects[e];
+
+ if (e_ptr->time)
+ {
+ /* Apply damage */
+ project(0, 0, j, i, e_ptr->dam, e_ptr->type,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ else
+ {
+ cave[j][i].effect = 0;
+ }
+
+ if ((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST))
+ {
+ if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1)
+ cave[j][i].effect = 0;
+ }
+ else if ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST))
+ {
+ cave[j][i].effect = 0;
+ }
+
+ lite_spot(j, i);
+ }
+ }
+ }
+
+ /* Reduce & handle effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ /* Skip empty slots */
+ if (effects[i].time == 0) continue;
+
+ /* Reduce duration */
+ effects[i].time--;
+
+ /* Creates a "wave" effect*/
+ if (effects[i].flags & EFF_WAVE)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y, z;
+
+ e_ptr->rad++;
+
+ /* What a frelling ugly line of ifs ... */
+ if (effects[i].flags & EFF_DIR8)
+ for (y = e_ptr->cy - e_ptr->rad, z = 0; y <= e_ptr->cy; y++, z++)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR2)
+ for (y = e_ptr->cy, z = e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++, z--)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR6)
+ for (x = e_ptr->cx, z = e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++, z--)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR4)
+ for (x = e_ptr->cx - e_ptr->rad, z = 0; x <= e_ptr->cx; x++, z++)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR9)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR1)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR7)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR3)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ /* This is *slow* -- pelpel */
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ /* Creates a "storm" effect*/
+ else if (effects[i].flags & EFF_STORM)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->cy = p_ptr->py;
+ e_ptr->cx = p_ptr->px;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) <= e_ptr->rad))
+ {
+ cave[y][x].effect = i;
+ lite_spot(y, x);
+ }
+ }
+ }
+ }
+ }
+
+ apply_effect(p_ptr->py, p_ptr->px);
+ }
+
+#endif /* !pelpel */
+
+ /* Arg cannot breath? */
+ if ((dungeon_flags2 & DF2_WATER_BREATH) && (!p_ptr->water_breath))
+ {
+ cmsg_print(TERM_L_RED, "You cannot breathe water, you suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+ if ((dungeon_flags2 & DF2_NO_BREATH) && (!p_ptr->magical_breath))
+ {
+ cmsg_print(TERM_L_RED, "There is no air there! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+
+ /*
+ * Every 1500 turns, warn about any Black Breath not gotten from
+ * an equipped object, and stop any resting. -LM-
+ *
+ * It's apparent that someone has halved the frequency... -- pelpel
+ */
+ if (((turn % 3000) == 0) && p_ptr->black_breath)
+ {
+ u32b f1, f2, f3, f4, f5;
+
+ bool be_silent = FALSE;
+
+ /* check all equipment for the Black Breath flag. */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* No messages if object has the flag, to avoid annoyance. */
+ if (f4 & (TR4_BLACK_BREATH)) be_silent = TRUE;
+
+ }
+ /* If we are allowed to speak, warn and disturb. */
+
+ if (!be_silent)
+ {
+ cmsg_print(TERM_L_DARK, "The Black Breath saps your soul!");
+ disturb(0, 0);
+ }
+ }
+
+
+ /*** Process Light ***/
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ {
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- Use some fuel */
+ if ((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0))
+ {
+ /* Decrease life-span */
+ o_ptr->timeout--;
+
+ /* Hack -- notice interesting fuel steps */
+ if ((o_ptr->timeout < 100) || ((o_ptr->timeout % 100) == 0))
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Hack -- Special treatment when blind */
+ if (p_ptr->blind)
+ {
+ /* Hack -- save some light for later */
+ if (o_ptr->timeout == 0) o_ptr->timeout++;
+ }
+
+ /* The light is now out */
+ else if (o_ptr->timeout < 1)
+ {
+ disturb(0, 0);
+ cmsg_print(TERM_YELLOW, "Your light has gone out!");
+ }
+
+ /* The light is getting dim */
+ else if (o_ptr->timeout < 100)
+ {
+ if (disturb_minor) disturb(0, 0);
+ cmsg_print(TERM_YELLOW, "Your light is growing faint.");
+ drop_from_wild();
+ }
+ }
+ }
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+
+ /*** Process Inventory ***/
+
+ /*
+ * Handle experience draining. In Oangband, the effect is worse,
+ * especially for high-level characters. As per Tolkien, hobbits
+ * are resistant.
+ */
+ if (p_ptr->black_breath)
+ {
+ byte chance = 0;
+ int plev = p_ptr->lev;
+
+ if (PRACE_FLAG(PR1_RESIST_BLACK_BREATH)) chance = 2;
+ else chance = 5;
+
+ if ((rand_int(100) < chance) && (p_ptr->exp > 0))
+ {
+ p_ptr->exp -= 1 + plev / 5;
+ p_ptr->max_exp -= 1 + plev / 5;
+ (void)do_dec_stat(rand_int(6), STAT_DEC_NORMAL);
+ check_experience();
+ }
+ }
+
+ /* Drain Mana */
+ if (p_ptr->drain_mana && p_ptr->csp)
+ {
+ p_ptr->csp -= p_ptr->drain_mana;
+ if (magik(30)) p_ptr->csp -= p_ptr->drain_mana;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ /* Partial summons drain mana */
+ if (p_ptr->maintain_sum)
+ {
+ p_ptr->csp -= p_ptr->maintain_sum / 100;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Drain Hitpoints */
+ if (p_ptr->drain_life)
+ {
+ int drain = p_ptr->drain_life + rand_int(p_ptr->mhp / 100);
+
+ p_ptr->chp -= (drain < p_ptr->chp ? drain : p_ptr->chp);
+
+ if (p_ptr->chp == 0)
+ {
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Handle experience draining */
+ if (p_ptr->exp_drain)
+ {
+ if ((rand_int(100) < 10) && (p_ptr->exp > 0))
+ {
+ p_ptr->exp--;
+ p_ptr->max_exp--;
+ check_experience();
+ }
+ }
+
+ /* Process equipment */
+ for (j = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Get the object */
+ o_ptr = &p_ptr->inventory[i];
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ /* TY Curse */
+ if ((f3 & TR3_TY_CURSE) && (rand_int(TY_CURSE_CHANCE) == 0))
+ {
+ activate_ty_curse();
+ }
+
+ /* DG Curse */
+ if ((f4 & TR4_DG_CURSE) && (rand_int(DG_CURSE_CHANCE) == 0))
+ {
+ activate_dg_curse();
+
+ /* The object recurse itself ! */
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ /* Auto Curse */
+ if ((f3 & TR3_AUTO_CURSE) && (rand_int(AUTO_CURSE_CHANCE) == 0))
+ {
+ /* The object recurse itself ! */
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ /*
+ * Hack: Uncursed teleporting items (e.g. Dragon Weapons)
+ * can actually be useful!
+ */
+ if ((f3 & TR3_TELEPORT) && (rand_int(100) < 1))
+ {
+ if ((o_ptr->ident & IDENT_CURSED) && !p_ptr->anti_tele)
+ {
+ disturb(0, 0);
+
+ /* Teleport player */
+ teleport_player(40);
+ }
+ else
+ {
+ if (p_ptr->wild_mode ||
+ (o_ptr->note && strchr(quark_str(o_ptr->note), '.')))
+ {
+ /* Do nothing */
+ /* msg_print("Teleport aborted.") */;
+ }
+ else if (get_check("Teleport? "))
+ {
+ disturb(0, 0);
+ teleport_player(50);
+ }
+ }
+ }
+
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack: Skip wielded lights that need fuel (already handled above) */
+ if ((i == INVEN_LITE) && (o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE)) continue;
+
+ /* Recharge activatable objects */
+ if (o_ptr->timeout > 0)
+ {
+ /* Recharge */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ recharged_notice(o_ptr);
+ j++;
+ }
+ }
+
+ /* Recharge second spell in Mage Staffs of Spells */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && (o_ptr->xtra2 > 0))
+ {
+ /* Recharge */
+ o_ptr->xtra2--;
+
+ /* Notice changes */
+ if (o_ptr->xtra2 == 0) j++;
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Recharge rods */
+ for (j = 0, i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+ k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ inven_item_increase(i, -99);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Examine all charging rods or stacks of charging rods. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Always notice */
+ j++;
+
+ /* Notice changes, provide message if object is inscribed. */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Examine all charging random artifacts */
+ if ((f5 & TR5_ACTIVATE_NO_WIELD) && (o_ptr->timeout > 0))
+ {
+ /* Charge it */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ j++;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Decay objects in pack */
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ pack_decay(i);
+ j++;
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+
+ if (o_ptr->timeout == 0)
+ {
+ o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ mx = p_ptr->px;
+ my = p_ptr->py + 1;
+ get_pos_player(5, &my, &mx);
+ msg_print("Your egg hatches!");
+ place_monster_aux(my, mx, o_ptr->pval2, FALSE, FALSE, MSTATUS_PET);
+
+ m_ptr = &m_list[cave[my][mx].m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if ((r_ptr->flags9 & RF9_IMPRESED) && can_create_companion())
+ {
+ msg_format("And you have given the imprint to your %s!",
+ r_name + r_ptr->name);
+ m_ptr->status = MSTATUS_COMPANION;
+ }
+
+ inven_item_increase(i, -1);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+ j++;
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Combine pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* Feel the p_ptr->inventory */
+ if (dun_level || (!p_ptr->wild_mode))
+ {
+ sense_inventory();
+ }
+
+ /*** Process Objects ***/
+
+ /* Process objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Access object */
+ o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ floor_item_increase(i, -99);
+ floor_item_optimize(i);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Recharge rods on the ground. No messages. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Do not overflow */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+ /* Decay objects on the ground*/
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Turn it into a skeleton */
+ if (o_ptr->pval <= 0)
+ {
+ floor_decay(i);
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+ if (o_ptr->timeout > 0) o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ mx = o_ptr->ix;
+ my = o_ptr->iy;
+ get_pos_player(5, &my, &mx);
+ msg_print("An egg hatches!");
+ place_monster_one(my, mx, o_ptr->pval2, 0, FALSE, MSTATUS_ENEMY);
+ floor_item_increase(i, -1);
+ floor_item_describe(i);
+ floor_item_optimize(i);
+ }
+ }
+ }
+
+
+ /*** Involuntary Movement ***/
+
+ /* Delayed Word-of-Recall */
+ if (p_ptr->word_recall)
+ {
+ /* Can we ? */
+ if (process_hooks(HOOK_RECALL, "()", ""))
+ {
+ p_ptr->word_recall = 0;
+ }
+
+ /* No recall. sorry */
+ else if (dungeon_flags2 & DF2_NO_RECALL_OUT)
+ {
+ cmsg_print(TERM_L_DARK, "You cannot recall from here.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Cannot WoR out of death fate levels */
+ else if (dungeon_type == DUNGEON_DEATH)
+ {
+ cmsg_print(TERM_L_DARK, "You are fated to die here, FIGHT for your life!");
+ p_ptr->word_recall = 0;
+ }
+
+ /* I think the 'inside_quest' code belongs here -- pelpel */
+
+ /* They cannot use word of recall until reaching surface */
+ else if (p_ptr->astral)
+ {
+ msg_print("As an astral being you can't recall.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Normal WoR */
+ else
+ {
+ /*
+ * HACK: Autosave BEFORE resetting the recall counter (rr9)
+ * The player is yanked up/down as soon as
+ * he loads the autosaved game.
+ */
+ if (autosave_l && (p_ptr->word_recall == 1))
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Make SURE that persistent levels are saved
+ * I don't know if this is needed, but I'm getting reports,
+ * so I'm adding this extra save -- Neil
+ */
+ save_dungeon();
+
+ /* Count down towards recall */
+ p_ptr->word_recall--;
+
+ /* Activate the recall */
+ if (p_ptr->word_recall == 0)
+ {
+ /* Disturbing! */
+ disturb(0, 0);
+
+ /* Determine the level */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("The recall is cancelled by a powerful magic force!");
+ }
+ else if (dun_level)
+ {
+ msg_print("You feel yourself yanked upwards!");
+
+ p_ptr->recall_dungeon = dungeon_type;
+ dungeon_type = DUNGEON_WILDERNESS;
+ dun_level = 0;
+
+ is_recall = TRUE;
+
+ p_ptr->inside_quest = 0;
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You feel yourself yanked downwards!");
+
+ /* New depth */
+ dungeon_type = p_ptr->recall_dungeon;
+ dun_level = max_dlv[dungeon_type];
+ if (dun_level < 1) dun_level = 1;
+
+ /* Reset player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ /* Leaving */
+ is_recall = TRUE;
+
+ p_ptr->leaving = TRUE;
+ p_ptr->wild_mode = FALSE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+ }
+ }
+ }
+}
+
+
+/*
+ * Verify use of "wizard" mode
+ */
+static bool enter_wizard_mode(void)
+{
+#if 0
+ /* Ask first time */
+ if (!(noscore & 0x0002))
+#else
+ /* Ask first time, but not while loading a dead char with the -w option */
+ if (!noscore && !(p_ptr->chp < 0))
+#endif
+ {
+ /* Mention effects */
+ msg_print("Wizard mode is for debugging and experimenting.");
+ msg_print("The game will not be scored if you enter wizard mode.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to enter wizard mode? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0002;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+#ifdef ALLOW_WIZARD
+
+/*
+ * Verify use of "debug" commands
+ */
+static bool enter_debug_mode(void)
+{
+ /* Ask first time */
+#if 0
+ if (!(noscore & 0x0008))
+#else
+if (!noscore && !wizard)
+#endif
+ {
+ /* Mention effects */
+ msg_print("The debug commands are for debugging and experimenting.");
+ msg_print("The game will not be scored if you use debug commands.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to use debug commands? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0008;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Declare the Debug Routines
+ */
+extern void do_cmd_debug(void);
+
+#endif /* ALLOW_WIZARD */
+
+
+#ifdef ALLOW_BORG
+
+/*
+ * Verify use of "borg" commands
+ */
+static bool enter_borg_mode(void)
+{
+ /* Ask first time */
+ if (!(noscore & 0x0010))
+ {
+ /* Mention effects */
+ msg_print("The borg commands are for debugging and experimenting.");
+ msg_print("The game will not be scored if you use borg commands.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to use borg commands? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0010;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Declare the Ben Borg
+ */
+extern void do_cmd_borg(void);
+
+#endif /* ALLOW_BORG */
+
+
+
+/*
+ * Parse and execute the current command
+ * Give "Warning" on illegal commands.
+ *
+ * XXX XXX XXX Make some "blocks"
+ */
+static void process_command(void)
+{
+ char error_m[80];
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Process the appropriate hooks */
+ if (process_hooks(HOOK_KEYPRESS, "(d)", command_cmd)) return;
+
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Ignore */
+ case ESCAPE:
+ case ' ':
+ case 0:
+ {
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+#ifdef ALLOW_QUITTING
+
+ case KTRL('L'):
+ {
+ quit("CHEATER");
+ break;
+ }
+
+#endif
+
+#ifdef ALLOW_WIZARD
+
+ /*** Wizard Commands ***/
+
+ /* Toggle Wizard Mode */
+ case KTRL('W'):
+ {
+ if (wizard)
+ {
+ wizard = FALSE;
+ msg_print("Wizard mode off.");
+ }
+ else if (enter_wizard_mode())
+ {
+ wizard = TRUE;
+ msg_print("Wizard mode on.");
+ }
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ break;
+ }
+
+ /* Special "debug" commands */
+ case KTRL('A'):
+ {
+ /* Enter debug mode */
+ if (enter_debug_mode())
+ {
+ do_cmd_debug();
+ }
+ break;
+ }
+
+#endif /* ALLOW_WIZARD */
+
+
+#ifdef ALLOW_BORG
+
+ /* Special "borg" commands */
+ case KTRL('Z'):
+ {
+ /* Enter borg mode */
+ if (enter_borg_mode())
+ {
+ if (!p_ptr->wild_mode) do_cmd_borg();
+ }
+
+ break;
+ }
+
+#endif /* ALLOW_BORG */
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_takeoff();
+ p_ptr->redraw |= (PR_MH);
+ break;
+ }
+
+ /* Drop an item */
+ case 'd':
+ {
+ if (do_control_drop()) break;
+ if (!p_ptr->wild_mode) do_cmd_drop();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ if (p_ptr->control) break;
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ if (p_ptr->control) break;
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ if (do_control_inven()) break;
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+ /*** Standard "Movement" Commands ***/
+
+ /* Alter a grid */
+ case '+':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_alter();
+ break;
+ }
+
+ /* Dig a tunnel */
+ case 'T':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_tunnel();
+ break;
+ }
+
+ /* Move (usually pick up things) */
+ case ';':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(always_pickup, TRUE);
+
+ break;
+ }
+
+ /* Move (usually do not pick up) */
+ case '-':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(!always_pickup, TRUE);
+
+ break;
+ }
+
+
+ /*** Running, Resting, Searching, Staying */
+
+ /* Begin Running -- Arg is Max Distance */
+ case '.':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_run();
+ break;
+ }
+
+ /* Stay still (usually pick things up) */
+ case ',':
+ {
+ if (do_control_pickup()) break;
+ do_cmd_stay(always_pickup);
+ break;
+ }
+
+ /* Stay still (usually do not pick up) */
+ case 'g':
+ {
+ if (p_ptr->control) break;
+ do_cmd_stay(!always_pickup);
+ break;
+ }
+
+ /* Rest -- Arg is time */
+ case 'R':
+ {
+ if (p_ptr->control) break;
+ do_cmd_rest();
+ break;
+ }
+
+ /* Search for traps/doors */
+ case 's':
+ {
+ if (p_ptr->control) break;
+ do_cmd_search();
+ break;
+ }
+
+ /* Toggle search mode */
+ case 'S':
+ {
+ if (p_ptr->control) break;
+ do_cmd_toggle_search();
+ break;
+ }
+
+
+ /*** Stairs and Doors and Chests and Traps ***/
+
+ /* Enter store */
+ case '_':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_store();
+ break;
+ }
+
+#if 0 /* Merged with the '>' command -- pelpel */
+
+ /* Enter quest level -KMW- */
+ case '[':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_quest();
+ break;
+ }
+
+#endif /* 0 */
+
+ /* Go up staircase */
+ case '<':
+ {
+ object_type *o_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (p_ptr->wild_mode || dun_level || is_quest(dun_level))
+ {
+ do_cmd_go_up();
+ }
+ else if (vanilla_town)
+ {
+ /* Do nothing */
+ }
+ /* Don't let the player < when he'd just drop right back down */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ msg_print("You are too hungry to travel.");
+ }
+ else if (p_ptr->sensible_lite &&
+ (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ /* Burn vampires! burn! */
+ msg_print("You can't travel during the day!");
+ }
+ else if (p_ptr->sensible_lite &&
+ (o_ptr->tval != 0) &&
+ (o_ptr->sval >= SV_LITE_GALADRIEL) &&
+ (o_ptr->sval <= SV_STONE_LORE) &&
+ (o_ptr->sval != SV_LITE_UNDEATH))
+ {
+ msg_print("Travel with your present light would be unsafe.");
+ }
+ else if (p_ptr->cut || p_ptr->poisoned)
+ {
+ /* I actually died this way once -- neil */
+ msg_print("You are too injured to travel.");
+ }
+ else if (ambush_flag)
+ {
+ msg_print("To flee the ambush you have to reach the edge of the map.");
+ }
+ else if (o_ptr->tval && (f4 & TR4_FUEL_LITE) && (o_ptr->timeout < 100))
+ {
+ msg_print("Your light is too low on fuel for you to travel with it.");
+ }
+ /* TODO: make the above stuff use this hook */
+ else if (!process_hooks(HOOK_FORBID_TRAVEL, "()"))
+ {
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ change_wild_mode();
+
+ /* Update the known wilderness */
+ reveal_wilderness_around_player(p_ptr->wilderness_y,
+ p_ptr->wilderness_x,
+ 0, WILDERNESS_SEE_RADIUS);
+ }
+
+ break;
+ }
+
+ /* Go down staircase */
+ case '>':
+ {
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (!p_ptr->wild_mode)
+ {
+ do_cmd_go_down();
+ }
+
+ /* Special cases */
+ else
+ {
+ if ((wf_info[wild_map[p_ptr->py][p_ptr->px].feat].entrance >= 1000) ||
+ (wild_map[p_ptr->py][p_ptr->px].entrance > 1000))
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+ do_cmd_go_down();
+
+ if (dun_level == 0)
+ {
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+ }
+ else
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ }
+ }
+ else
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ }
+ }
+
+ break;
+ }
+
+ /* Open a door or chest */
+ case 'o':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_open();
+ break;
+ }
+
+ /* Close a door */
+ case 'c':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_close();
+ break;
+ }
+
+ /* Give an item */
+ case 'y':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_give();
+ break;
+ }
+
+ /* Chat */
+ case 'Y':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_chat();
+ break;
+ }
+
+ /* Jam a door with spikes */
+ case 'j':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_spike();
+ break;
+ }
+
+ /* Bash a door */
+ case 'B':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_bash();
+ break;
+ }
+
+ /* Disarm a trap or chest */
+ case 'D':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_disarm();
+ break;
+ }
+
+
+ /*** Magic and Prayers ***/
+
+ /* Interact with skills */
+ case 'G':
+ {
+ if (p_ptr->control) break;
+ do_cmd_skill();
+ break;
+ }
+
+ /* Interact with abilities */
+ case 'N':
+ {
+ if (p_ptr->control) break;
+ do_cmd_ability();
+ break;
+ }
+
+ /* Browse a book */
+ case 'b':
+ {
+ if (p_ptr->control) break;
+ do_cmd_browse();
+ break;
+ }
+
+ /* Cast a spell */
+ case 'm':
+ {
+ if (do_control_magic()) break;
+
+ /* No magic in the overworld map */
+ if (p_ptr->wild_mode) break;
+
+ /* Neither in the Arena */
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+
+ break;
+ }
+ do_cmd_activate_skill();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Pray a prayer */
+ case 'p':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_pray();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Issue commands to pets */
+ case 'P':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_pet();
+ break;
+ }
+
+ /* Cut up a corpse */
+ case 'h':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_cut_corpse();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Cure some meat */
+ case 'K':
+ {
+ if (p_ptr->control) break;
+ do_cmd_cure_meat();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Steal an item form a monster */
+ case 'Z':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_steal();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /*** Use various objects ***/
+
+ /* Inscribe an object */
+ case '{':
+ {
+ if (p_ptr->control) break;
+ do_cmd_inscribe();
+ break;
+ }
+
+ /* Uninscribe an object */
+ case '}':
+ {
+ if (p_ptr->control) break;
+ do_cmd_uninscribe();
+ break;
+ }
+
+ /* Activate an artifact */
+ case 'A':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_activate();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Eat some food */
+ case 'E':
+ {
+ if (p_ptr->control) break;
+ do_cmd_eat_food();
+ break;
+ }
+
+ /* Fuel your lantern/torch */
+ case 'F':
+ {
+ if (p_ptr->control) break;
+ do_cmd_refill();
+ break;
+ }
+
+ /* Fire an item */
+ case 'f':
+ {
+ object_type *j_ptr;
+
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("You're in the arena now. This is hand-to-hand!");
+ msg_print(NULL);
+ break;
+ }
+
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ if (process_hooks(HOOK_FIRE, "(O)", j_ptr))
+ break;
+
+ if (j_ptr->tval == TV_BOOMERANG)
+ {
+ do_cmd_boomerang();
+ }
+ else
+ {
+ do_cmd_fire();
+ }
+
+ break;
+ }
+
+ /* Throw an item */
+ case 'v':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("You're in the arena now. This is hand-to-hand!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_throw();
+ break;
+ }
+
+ /* Aim a wand */
+ case 'a':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_aim_wand();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Zap a rod */
+ case 'z':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_zap_rod();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Quaff a potion */
+ case 'q':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_quaff_potion();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Drink from a fountain -SC- */
+ case 'H':
+ {
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ if (p_ptr->control) break;
+ if ((c_ptr->feat == FEAT_FOUNTAIN) ||
+ (c_ptr->feat == FEAT_EMPTY_FOUNTAIN))
+ {
+ do_cmd_drink_fountain();
+ squeltch_inventory();
+ squeltch_grid();
+ }
+ else
+ {
+ msg_print("You see no fountain here.");
+ }
+
+ break;
+ }
+
+ /* Read a scroll */
+ case 'r':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_read_scroll();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Use a staff */
+ case 'u':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_use_staff();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Use racial power */
+ case 'U':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_power();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Sacrifice at an altar */
+ case 'O':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (PRACE_FLAG(PR1_NO_GOD))
+ {
+ msg_print("You cannot worship gods.");
+ }
+ else
+ {
+ do_cmd_sacrifice();
+ }
+
+ break;
+ }
+
+ /*** Looking at Things (nearby or on map) ***/
+
+ /* Full dungeon map */
+ case 'M':
+ {
+ if (!p_ptr->wild_mode) do_cmd_view_map();
+ break;
+ }
+
+ /* Locate player on map */
+ case 'L':
+ {
+ do_cmd_locate();
+ break;
+ }
+
+ /* Look around */
+ case 'l':
+ {
+ do_cmd_look();
+ break;
+ }
+
+ /* Target monster or location */
+ case '*':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_target();
+ break;
+ }
+
+ /* Engrave the floor */
+ case 'x':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ /* No point in engraving if there isn't any mana on this grid. */
+ /* DG - actualy there is, it doesnt break macros */
+ do_cmd_sense_grid_mana();
+ do_cmd_engrave();
+
+ break;
+ }
+
+ /*** Help and Such ***/
+
+ /* Help */
+ case '?':
+ {
+ do_cmd_help();
+ break;
+ }
+
+ /* Identify symbol */
+ case '/':
+ {
+ do_cmd_query_symbol();
+ break;
+ }
+
+ /* Character description */
+ case 'C':
+ {
+ do_cmd_change_name();
+ break;
+ }
+
+
+ /*** System Commands ***/
+
+ /* Hack -- User interface */
+ case '!':
+ {
+ (void)Term_user(0);
+ break;
+ }
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ if (!p_ptr->wild_mode)
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Show quest status -KMW- */
+ case KTRL('Q'):
+ case CMD_QUEST:
+{
+ do_cmd_checkquest();
+ break;
+ }
+
+ /* Redraw the screen */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ break;
+ }
+
+#ifndef VERIFY_SAVEFILE
+
+ /* Hack -- Save and don't quit */
+ case KTRL('S'):
+ {
+ is_autosave = FALSE;
+ do_cmd_save_game();
+ break;
+ }
+
+#endif /* VERIFY_SAVEFILE */
+
+ case KTRL('T'):
+ {
+ do_cmd_time();
+ }
+ break;
+
+ /* Save and quit */
+ case KTRL('X'):
+ {
+ alive = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ break;
+ }
+
+ /* Quit (commit suicide) */
+ case 'Q':
+ {
+ do_cmd_suicide();
+ break;
+ }
+
+ /* Activate cmovie */
+ case '|':
+ {
+ /* Stop ? */
+ if (do_movies == 1)
+ {
+ do_stop_cmovie();
+ msg_print("Cmovie recording stopped.");
+ }
+ else
+ {
+ do_start_cmovie();
+ }
+ break;
+ }
+
+ /* Extended command */
+ case '#':
+ {
+ do_cmd_cli();
+ break;
+ }
+
+ /* Check artifacts, uniques, objects */
+ case '~':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Commands only available as extended commands: */
+
+ /* Extended command help. */
+ case CMD_CLI_HELP:
+ {
+ do_cmd_cli_help();
+ break;
+ }
+
+ /* Connect to IRC. */
+ case CMD_IRC_CONNECT:
+ {
+ irc_connect();
+ break;
+ }
+
+ /* Speak on IRC. */
+ case CMD_IRC_CHAT:
+ {
+ irc_chat();
+ break;
+ }
+
+ /* Disconnect from IRC. */
+ case CMD_IRC_DISCON:
+ {
+ irc_disconnect();
+ break;
+ }
+
+ /* Game time. */
+ case CMD_SHOW_TIME:
+ {
+ do_cmd_time();
+ break;
+ }
+
+ /* Check skills. */
+ case CMD_SHOW_SKILL:
+ {
+ do_cmd_skill();
+ break;
+ }
+
+ /* Check abilities. */
+ case CMD_SHOW_ABILITY:
+ {
+ do_cmd_ability();
+ break;
+ }
+
+ /* Save a html screenshot. */
+ case CMD_DUMP_HTML:
+ {
+ do_cmd_html_dump();
+ break;
+ }
+
+ /* Record a macro. */
+ case '$':
+ case CMD_MACRO:
+ {
+ do_cmd_macro_recorder();
+ break;
+ }
+ case CMD_BLUNDER:
+ {
+ if (do_control_walk()) break;
+ do_cmd_walk(always_pickup, FALSE);
+ break;
+ }
+ /* Hack -- Unknown command */
+ default:
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+
+ /* Would like to have an option disabling this -- pelpel */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("error.txt", error_m);
+ sound(SOUND_ILLEGAL);
+ msg_print(error_m);
+ }
+ else
+ {
+ prt("Type '?' for help.", 0, 0);
+ }
+
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Process the player
+ *
+ * Notice the annoying code to handle "pack overflow", which
+ * must come first just in case somebody manages to corrupt
+ * the savefiles by clever use of menu commands or something.
+ */
+void process_player(void)
+{
+ int i, j;
+
+ int speed_use;
+
+
+ /*** Apply energy ***/
+
+ if (hack_corruption)
+ {
+ msg_print("You feel different!");
+ (void)gain_random_corruption(0);
+ hack_corruption = FALSE;
+ }
+
+ /* Obtain current speed */
+ speed_use = p_ptr->pspeed;
+
+ /* Maximum value */
+ if (speed_use > 199)
+ {
+ speed_use = 199;
+ }
+
+ /* Minimum value */
+ else if (speed_use < 0)
+ {
+ speed_use = 0;
+ }
+
+ /* Give the player some energy */
+ p_ptr->energy += extract_energy[speed_use];
+
+ /* No turn yet */
+ if (p_ptr->energy < 100) return;
+
+
+ /*** Check for interupts ***/
+
+ /* Complete resting */
+ if (resting < 0)
+ {
+ /* Basic resting */
+ if (resting == -1)
+ {
+ /* Stop resting */
+ if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp >= p_ptr->msp))
+ {
+ disturb(0, 0);
+ }
+ }
+
+ /* Complete resting */
+ else if (resting == -2)
+ {
+ bool stop = TRUE;
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ r_ptr = &r_info[o_ptr->pval];
+
+ /* Stop resting */
+ if ((!p_ptr->drain_life) && (p_ptr->chp != p_ptr->mhp)) stop = FALSE;
+ if ((!p_ptr->drain_mana) && (p_ptr->csp != p_ptr->msp)) stop = FALSE;
+ if (o_ptr->pval2 < o_ptr->pval3) stop = FALSE;
+ if (p_ptr->blind || p_ptr->confused) stop = FALSE;
+ if (p_ptr->poisoned || p_ptr->afraid) stop = FALSE;
+ if (p_ptr->stun || p_ptr->cut) stop = FALSE;
+ if (p_ptr->slow || p_ptr->paralyzed) stop = FALSE;
+ if (p_ptr->image || p_ptr->word_recall) stop = FALSE;
+ if (p_ptr->immov_cntr != 0) stop = FALSE;
+
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0) stop = FALSE;
+ }
+
+ if (stop)
+ {
+ disturb(0, 0);
+ }
+ p_ptr->redraw |= (PR_STATE);
+ }
+ }
+
+ /* Handle "abort" */
+ if (!avoid_abort)
+ {
+ /* Check for "player abort" (semi-efficiently for resting) */
+ if (running || command_rep || (resting && !(resting & 0x0F)))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+
+ /* Check for a key */
+ if (inkey())
+ {
+ /* Flush input */
+ flush();
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Show a Message */
+ msg_print("Cancelled.");
+ }
+ }
+ }
+
+
+ /*** Handle actual user input ***/
+
+ /* Repeat until out of energy */
+ while (p_ptr->energy >= 100)
+ {
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- mark current wilderness location as known */
+ if (!p_ptr->wild_mode && dun_level == 0)
+ wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].known = TRUE;
+
+
+ /* Place the cursor on the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Refresh (optional) */
+ if (fresh_before) Term_fresh();
+
+ /* Hack -- Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ char o_name[80];
+
+ object_type *o_ptr;
+
+ /* Access the slot to be dropped */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Disturbing */
+ disturb(0, 0);
+
+ /* Warning */
+ msg_print("Your pack overflows!");
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it (carefully) near the player */
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* Modify, Describe, Optimize */
+ inven_item_increase(item, -255);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+ }
+
+
+ /* Hack -- cancel "lurking browse mode" */
+ if (!command_new) command_see = FALSE;
+
+
+ /* Assume free turn */
+ energy_use = 0;
+
+
+ /* Paralyzed or Knocked Out */
+ if ((p_ptr->paralyzed) || (p_ptr->stun >= 100))
+ {
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Resting */
+ else if (resting)
+ {
+ /* Timed rest */
+ if (resting > 0)
+ {
+ /* Reduce rest count */
+ resting--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ p_ptr->did_nothing = TRUE;
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Running */
+ else if (running)
+ {
+ /* Take a step */
+ run_step(0);
+
+ /*
+ * Commented out because it doesn't make any sense
+ * to require a player holding down direction keys
+ * instead of using running commands when s/he follows
+ * Eru and do the opposite for the other deities -- pelpel
+ */
+ /* p_ptr->did_nothing = TRUE; */
+ }
+
+ /* Repeated command */
+ else if (command_rep)
+ {
+ /* Count this execution */
+ command_rep--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Hack -- Assume messages were seen */
+ msg_flag = FALSE;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Process the command */
+ process_command();
+
+ p_ptr->did_nothing = TRUE;
+ }
+
+ /* Normal command */
+ else
+ {
+ /* Place the cursor on the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Get a command (normal) */
+ request_command(FALSE);
+
+ /* Process the command */
+ process_command();
+ }
+
+
+ /*** Clean up ***/
+
+ /* Significant */
+ if (energy_use)
+ {
+ /* Use some energy */
+ p_ptr->energy -= energy_use;
+
+
+ /* Hack -- constant hallucination */
+ if (p_ptr->image) p_ptr->redraw |= (PR_MAP);
+
+
+ /* Shimmer monsters if needed */
+ if (!avoid_other && !use_graphics && shimmer_monsters)
+ {
+ /* Clear the flag */
+ shimmer_monsters = FALSE;
+
+ /* Shimmer multi-hued monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ /* Access monster */
+ m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Access the monster race */
+ r_ptr = race_inf(m_ptr);
+
+ /* Skip non-multi-hued monsters */
+ if (!(r_ptr->flags1 & (RF1_ATTR_MULTI))) continue;
+
+ /* Reset the flag */
+ shimmer_monsters = TRUE;
+
+ /* Redraw regardless */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+ }
+ }
+
+ /* Shimmer objects if needed and requested */
+ if (!avoid_other && !avoid_shimmer && !use_graphics &&
+ shimmer_objects)
+ {
+ /* Clear the flag */
+ shimmer_objects = FALSE;
+
+ /* Shimmer multi-hued objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object -- for speed only base items are allowed to shimmer */
+ object_type *o_ptr = &o_list[i];
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip dead or carried objects */
+ if ((!o_ptr->k_idx) || (!o_ptr->ix)) continue;
+
+ /* Skip non-multi-hued monsters */
+ if (!(k_ptr->flags5 & (TR5_ATTR_MULTI))) continue;
+
+ /* Reset the flag */
+ shimmer_objects = TRUE;
+
+ /* Redraw regardless */
+ lite_spot(o_ptr->iy, o_ptr->ix);
+ }
+ }
+
+ /*
+ * Shimmer features if needed and requested
+ *
+ * Note: this can be unbearably slow when a player chooses
+ * to use a REALLY big screen in levels filled with shallow
+ * water. I believe this also hurts a lot on multiuser systems.
+ * However fast modern processors are, I/O cannot be made that
+ * fast, and that's why shimmering has been limited to small
+ * number of monsters -- pelpel
+ */
+ if (!avoid_other && !avoid_shimmer && !use_graphics &&
+ !resting && !running)
+ {
+ for (j = panel_row_min; j <= panel_row_max; j++)
+ {
+ for (i = panel_col_min; i <= panel_col_max; i++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+ feature_type *f_ptr;
+
+ /* Apply terrain feature mimics */
+ if (c_ptr->mimic)
+ {
+ f_ptr = &f_info[c_ptr->mimic];
+ }
+ else
+ {
+ f_ptr = &f_info[f_info[c_ptr->feat].mimic];
+ }
+
+ /* Skip normal features */
+ if (!(f_ptr->flags1 & (FF1_ATTR_MULTI))) continue;
+
+ /* Redraw a shimmering spot */
+ lite_spot(j, i);
+ }
+ }
+ }
+
+
+ /* Handle monster detection */
+ if (repair_monsters)
+ {
+ /* Reset the flag */
+ repair_monsters = FALSE;
+
+ /* Rotate detection flags */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Access monster */
+ m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Nice monsters get mean */
+ if (m_ptr->mflag & (MFLAG_NICE))
+ {
+ /* Nice monsters get mean */
+ m_ptr->mflag &= ~(MFLAG_NICE);
+ }
+
+ /* Handle memorized monsters */
+ if (m_ptr->mflag & (MFLAG_MARK))
+ {
+ /* Maintain detection */
+ if (m_ptr->mflag & (MFLAG_SHOW))
+ {
+ /* Forget flag */
+ m_ptr->mflag &= ~(MFLAG_SHOW);
+
+ /* Still need repairs */
+ repair_monsters = TRUE;
+ }
+
+ /* Remove detection */
+ else
+ {
+ /* Forget flag */
+ m_ptr->mflag &= ~(MFLAG_MARK);
+
+ /* Assume invisible */
+ m_ptr->ml = FALSE;
+
+ /* Update the monster */
+ update_mon(i, FALSE);
+
+ /* Redraw regardless */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+ }
+ }
+ }
+ }
+
+ /*
+ * Moved from dungeon() -- It'll get called whenever player
+ * spends energy, so that maze isn't incredibly easy for
+ * Sorcerors and alike any longer -- pelpel
+ *
+ * Forget everything when requested hehe I'm *NASTY*
+ */
+ if (dun_level && (dungeon_flags1 & DF1_FORGET))
+ {
+ wiz_dark();
+ }
+ }
+
+
+ /* Hack -- notice death */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+ }
+}
+
+
+
+/*
+ * Interact with the current dungeon level.
+ *
+ * This function will not exit until the level is completed,
+ * the user dies, or the game is terminated.
+ */
+static void dungeon(void)
+{
+ /* Reset various flags */
+ hack_mind = FALSE;
+
+ /* Not leaving */
+ p_ptr->leaving = FALSE;
+
+ /* Reset the "command" vars */
+ command_cmd = 0;
+ command_new = 0;
+ command_rep = 0;
+ command_arg = 0;
+ command_dir = 0;
+
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Check visual effects */
+ shimmer_monsters = TRUE;
+ shimmer_objects = TRUE;
+ repair_monsters = TRUE;
+ repair_objects = TRUE;
+
+
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Track maximum player level */
+ if (p_ptr->max_plv < p_ptr->lev)
+ {
+ p_ptr->max_plv = p_ptr->lev;
+ }
+
+ /* Track maximum dungeon level (if not in quest -KMW-) */
+ if ((max_dlv[dungeon_type] < dun_level) && !p_ptr->inside_quest)
+ {
+ max_dlv[dungeon_type] = dun_level;
+ }
+
+ /* No stairs down from Quest */
+ if (is_quest(dun_level) && !p_ptr->astral)
+ {
+ create_down_stair = FALSE;
+ create_down_shaft = FALSE;
+ }
+
+ /* Paranoia -- no stairs from town or wilderness */
+ if (!dun_level) create_down_stair = create_up_stair = FALSE;
+ if (!dun_level) create_down_shaft = create_up_shaft = FALSE;
+
+ /* Option -- no connected stairs */
+ if (!dungeon_stair) create_down_stair = create_up_stair = FALSE;
+ if (!dungeon_stair) create_down_shaft = create_up_shaft = FALSE;
+
+ /* no connecting stairs on special levels */
+ if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_stair = create_up_stair = FALSE;
+ if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_shaft = create_up_shaft = FALSE;
+
+ /* Make a stairway. */
+ if ((create_up_stair || create_down_stair ||
+ create_up_shaft || create_down_shaft) &&
+ !get_fbranch())
+ {
+ /* Place a stairway */
+ if (cave_valid_bold(p_ptr->py, p_ptr->px))
+ {
+ /* XXX XXX XXX */
+ delete_object(p_ptr->py, p_ptr->px);
+
+ /* Make stairs */
+ if (create_down_stair)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE);
+ }
+ else if (create_down_shaft)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN);
+ }
+ else if (create_up_shaft)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP);
+ }
+ else
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS);
+ }
+ }
+
+ /* Cancel the stair request */
+ create_down_stair = create_up_stair = FALSE;
+ create_down_shaft = create_up_shaft = FALSE;
+ }
+
+ /* Hack - Assume invalid panel */
+ panel_row_min = cur_hgt;
+ panel_row_max = 0;
+ panel_col_min = cur_wid;
+ panel_col_max = 0;
+
+ /* Center the panel */
+ verify_panel();
+
+ /* Flush messages */
+ msg_print(NULL);
+
+
+ /* Enter "xtra" mode */
+ character_xtra = TRUE;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+
+ /* Redraw dungeon */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_SANITY | PU_BODY);
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Redraw stuff */
+ window_stuff();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Leave "xtra" mode */
+ character_xtra = FALSE;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_BODY);
+
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Window stuff */
+ window_stuff();
+
+ /* Refresh */
+ Term_fresh();
+
+
+ /* Announce (or repeat) the feeling */
+ if (dun_level) do_cmd_feeling();
+
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) return;
+
+ /*** Process this dungeon level ***/
+
+ /* Reset the monster generation level */
+ monster_level = dun_level;
+
+ /* Reset the object generation level */
+ object_level = dun_level;
+
+ hack_mind = TRUE;
+
+ /* Mega Hack, if needed wipe all stairs */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ int i, j;
+
+ for (i = 0; i < cur_wid; i++)
+ {
+ for (j = 0; j < cur_hgt; j++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+
+ switch (c_ptr->feat)
+ {
+ case FEAT_MORE:
+ case FEAT_LESS:
+ case FEAT_SHAFT_UP:
+ case FEAT_SHAFT_DOWN:
+ {
+ cave_set_feat(j, i, FEAT_FLOOR);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Reset the monster generation level */
+ monster_level = 127;
+
+ /* Reset the object generation level */
+ object_level = 0;
+ }
+
+ /* Main loop */
+ while (TRUE)
+ {
+ /* Hack -- Compact the monster list occasionally */
+ if (m_cnt + 32 > max_m_idx) compact_monsters(64);
+
+ /* Hack -- Compress the monster list occasionally */
+ if (m_cnt + 32 < m_max) compact_monsters(0);
+
+
+ /* Hack -- Compact the object list occasionally */
+ if (o_cnt + 32 > max_o_idx) compact_objects(64);
+
+ /* Hack -- Compress the object list occasionally */
+ if (o_cnt + 32 < o_max) compact_objects(0);
+
+
+
+ /* Process the player */
+ process_player();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+#ifdef pelpel
+
+ /* Process spell effects */
+ process_effects();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+#endif /* pelpel */
+
+
+ total_friends = 0;
+ total_friend_levels = 0;
+
+ /* Process all of the monsters */
+ process_monsters();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+ /* Process the world */
+ process_world();
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_END_TURN, "(d)", is_quest(dun_level));
+
+ /* Make it pulsate and live !!!! */
+ if ((dungeon_flags1 & DF1_EVOLVE) && dun_level)
+ {
+ if (!(turn % 10)) evolve_level(TRUE);
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Count game turns */
+ turn++;
+ }
+
+ /* Did we leave a dungeon ? */
+ if ((dun_level < d_info[dungeon_type].mindepth) && !is_recall)
+ {
+ dun_level = 0;
+
+ if (d_info[dungeon_type].ix > -1)
+ {
+ p_ptr->wilderness_x = d_info[dungeon_type].ix;
+ p_ptr->wilderness_y = d_info[dungeon_type].iy;
+ }
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ }
+
+ if (dun_level > d_info[dungeon_type].maxdepth)
+ {
+ dun_level = 0;
+
+ if (d_info[dungeon_type].ox > -1)
+ {
+ p_ptr->wilderness_x = d_info[dungeon_type].ox;
+ p_ptr->wilderness_y = d_info[dungeon_type].oy;
+ }
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ }
+
+ is_recall = FALSE;
+}
+
+
+
+
+/*
+ * Load some "user pref files"
+ */
+static void load_all_pref_files(void)
+{
+ char buf[1024];
+
+
+ /* Access the "race" pref file */
+ sprintf(buf, "%s.prf", rp_ptr->title + rp_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "class" pref file */
+ sprintf(buf, "%s.prf", spp_ptr->title + c_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "character" pref file */
+ sprintf(buf, "%s.prf", player_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Process player specific automatizer sets */
+ tome_dofile_anywhere(ANGBAND_DIR_USER, format("%s.atm", player_name), FALSE);
+}
+
+/*
+ * Actually play a game
+ *
+ * If the "new_game" parameter is true, then, after loading the
+ * savefile, we will commit suicide, if necessary, to allow the
+ * player to start a new game.
+ */
+void play_game(bool new_game)
+{
+ int i, tmp_dun;
+
+ bool cheat_death = FALSE;
+
+ hack_corruption = FALSE;
+
+ /* Hack -- Character is "icky" */
+ character_icky = TRUE;
+
+
+ /* Make sure main term is active */
+ Term_activate(angband_term[0]);
+
+ /* Initialise the resize hook XXX XXX XXX */
+ angband_term[0]->resize_hook = resize_map;
+
+ /* XXX XXX XXX hardcoded number of terms */
+ for (i = 1; i < 8; i++)
+ {
+ if (angband_term[i])
+ {
+ /* Add redraw hook */
+ angband_term[i]->resize_hook = resize_window;
+ }
+ }
+
+
+ /* Hack -- turn off the cursor */
+ (void)Term_set_cursor(0);
+
+ /* Character list */
+ if (!new_game && !no_begin_screen) new_game = begin_screen();
+ no_begin_screen = FALSE;
+
+ /* Attempt to load */
+ if (!load_player())
+ {
+ /* Oops */
+ quit("broken savefile");
+ }
+
+ /* Nothing loaded */
+ if (!character_loaded)
+ {
+ /* Make new player */
+ new_game = TRUE;
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+ }
+ else
+ {
+ int i;
+
+ /* Init new skills to their defaults */
+ for (i = old_max_s_idx; i < max_s_idx; i++)
+ {
+ s32b value = 0, mod = 0;
+
+ compute_skills(&value, &mod, i);
+
+ init_skill(value, mod, i);
+ }
+ }
+
+#if 1
+
+ /* Process old character */
+ if (!new_game)
+ {
+ /* Process the player name */
+ process_player_name(FALSE);
+ }
+
+#endif
+
+ /* Init the RNG */
+ if (Rand_quick)
+ {
+ u32b seed;
+
+ /* Basic seed */
+ seed = (time(NULL));
+
+#ifdef SET_UID
+
+ /* Mutate the seed on Unix machines */
+ seed = ((seed >> 3) * (getpid() << 1));
+
+#endif
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ /* Seed the "complex" RNG */
+ Rand_state_init(seed);
+ }
+
+ /* Extract the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Set the "default" options */
+ if (option_info[i].o_var)
+ {
+ /* Set */
+ if (option_flag[os] & (1L << ob))
+ {
+ /* Set */
+ (*option_info[i].o_var) = TRUE;
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ (*option_info[i].o_var) = FALSE;
+ }
+ }
+ }
+
+ /* Roll new character */
+ if (new_game)
+ {
+ s32b ret;
+
+ /* Are we authorized to create new chars? */
+ call_lua("get_module_info", "(s)", "d", "allow_birth", &ret);
+
+ if (!ret)
+ {
+ msg_box("Sorry, this module does not allow character creation.", -1, -1);
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+ }
+
+ process_hooks(HOOK_INIT, "()");
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+
+ /* Hack -- seed for flavors */
+ seed_flavor = rand_int(0x10000000);
+
+ /* Hack -- seed for town layout */
+ seed_town = rand_int(0x10000000);
+
+ /* Roll up a new character */
+ player_birth();
+
+ /* Start in town, or not */
+ if (p_ptr->astral) dun_level = 98;
+ else dun_level = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->inside_arena = 0;
+
+ /* Hack -- enter the world */
+ /* Mega-hack Vampires and Spectres start in the dungeon */
+ if (PRACE_FLAG(PR1_UNDEAD))
+ {
+ turn = (10L * DAY / 2) + (START_DAY * 10) + 1;
+ }
+ else
+ {
+ turn = (START_DAY * 10) + 1;
+ }
+ }
+
+ /* Flash a message */
+ prt("Please wait...", 0, 0);
+
+ /* Flush the message */
+ Term_fresh();
+
+ /* Be sure to not bother the player */
+ calc_powers_silent = TRUE;
+
+ /* Hack -- Enter wizard mode */
+ if (arg_wizard && enter_wizard_mode()) wizard = TRUE;
+
+ /* Flavor the objects */
+ flavor_init();
+
+ /* Reset the visual mappings */
+ reset_visuals();
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+
+ /* Window stuff */
+ window_stuff();
+
+ /* load user file */
+ process_pref_file("user.prf");
+
+ /* Load the "pref" files */
+ load_all_pref_files();
+
+ /* Set or clear "rogue_like_commands" if requested */
+ if (arg_force_original) rogue_like_commands = FALSE;
+ if (arg_force_roguelike) rogue_like_commands = TRUE;
+
+ /* Initialize vault info */
+ if (init_v_info()) quit("Cannot initialize vaults");
+
+ /* Initialize hooks */
+ init_hooks();
+ ingame_help(p_ptr->help.enabled);
+
+ /* React to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Mega hack, prevent lots of bugs */
+ if ((p_ptr->px == 0) || (p_ptr->py == 0))
+ {
+ p_ptr->px = 1;
+ p_ptr->py = 1;
+ };
+
+ /* Hack - if note file exists, load it */
+ if (!new_game && take_notes)
+ {
+ add_note_type(NOTE_ENTER_DUNGEON);
+ }
+
+ /* Generate a dungeon level if needed */
+ if (!character_dungeon) generate_cave();
+
+ /* Ok tell the scripts that the game is about to start */
+ process_hooks(HOOK_GAME_START, "()");
+
+ /* Character is now "complete" */
+ character_generated = TRUE;
+
+
+ /* Hack -- Character is no longer "icky" */
+ character_icky = FALSE;
+
+
+ /* Start game */
+ alive = TRUE;
+
+ /* Hack -- Enforce "delayed death" */
+ if (p_ptr->chp < 0) death = TRUE;
+
+ /* Should we use old colors */
+ if (autoload_old_colors)
+ {
+ process_pref_file("422color.prf");
+ }
+
+
+ /* Process */
+ while (TRUE)
+ {
+ /* Save the level */
+ old_dun_level = dun_level;
+ p_ptr->old_wild_mode = p_ptr->wild_mode;
+
+ /* We reached surface ? good, lets go down again !! */
+ if (p_ptr->astral && !dun_level)
+ {
+ p_ptr->astral = FALSE;
+ cmsg_print(TERM_L_GREEN,
+ "Well done ! You reached the town ! "
+ "You can now go down again.");
+ }
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+#ifdef SAVE_HACK
+
+ /* Process the level */
+ if (!save_hack)
+ {
+ dungeon();
+ }
+ else
+ {
+ generate_cave();
+ }
+
+ save_hack = FALSE;
+
+ p_ptr->leaving = TRUE;
+
+#else
+
+ /* Process the level */
+ dungeon();
+
+#endif
+
+ /* Save the current level if in a persistent level */
+ tmp_dun = dun_level;
+ dun_level = old_dun_level;
+ save_dungeon();
+ dun_level = tmp_dun;
+
+ /* A death fate affects level generation */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ /* Ignore empty slots */
+ if (!fates[i].fate) continue;
+
+ /* Ignore non-applicable fates */
+ if (fates[i].level != dun_level) continue;
+
+ /* Non-serious fate fails to fire 50% of time */
+ if (!fates[i].serious && (rand_int(2) == 0)) continue;
+
+ /* Analyse fate */
+ switch (fates[i].fate)
+ {
+ /* You are doomed */
+ case FATE_DIE:
+ {
+ cmsg_print(TERM_L_DARK, "You were fated to die here, DIE!");
+
+ /* You shall perish there */
+ dungeon_type = DUNGEON_DEATH;
+ dun_level = d_info[dungeon_type].mindepth; /* was 1 */
+
+ fates[i].fate = FATE_NONE;
+ break;
+ }
+ }
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Forget the lite */
+ forget_mon_lite();
+
+ /* Forget the view */
+ forget_view();
+
+ /* Handle "quit and save" */
+ if (!alive && !death) break;
+
+
+ /* Erase the old cave */
+ wipe_o_list();
+
+
+ /* XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Accidental Death */
+ if (alive && death)
+ {
+ cheat_death = FALSE;
+
+ /* Can we die ? please let us die ! */
+ if (process_hooks(HOOK_DIE, "()"))
+ {
+ cheat_death = TRUE;
+ }
+
+ /* Deus ex machina */
+ else if (granted_resurrection())
+ {
+ cheat_death = TRUE;
+ p_ptr->grace = -200000;
+ cmsg_format(TERM_L_GREEN,
+ "The power of %s raises you back from the grave!",
+ deity_info[p_ptr->pgod].name);
+ msg_print(NULL);
+ }
+
+ /* Blood of life */
+ else if (p_ptr->allow_one_death > 0)
+ {
+ cheat_death = TRUE;
+
+ /* Lose one extra life */
+ p_ptr->allow_one_death--;
+
+ cmsg_print(TERM_L_GREEN,
+ "You have been saved by the Blood of Life!");
+ msg_print(NULL);
+ }
+
+ /* Cheat death option */
+ else if ((wizard || cheat_live) && !get_check("Die? "))
+ {
+ cheat_death = TRUE;
+
+ /* Mark social class, reset age, if needed */
+ if (p_ptr->sc) p_ptr->sc = p_ptr->age = 0;
+
+ /* Increase age */
+ p_ptr->age++;
+
+ /* Mark savefile */
+ noscore |= 0x0001;
+ msg_print("You invoke wizard mode and cheat death.");
+ msg_print(NULL);
+ }
+
+ if (cheat_death)
+ {
+ /* Restore the winner status */
+ total_winner = has_won;
+
+ /* One more life spent */
+ p_ptr->lives++;
+
+ /* Restore hit points */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Heal sanity */
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+
+ /* Restore spell points */
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+
+ /* Hack -- Healing */
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_poisoned(0);
+ (void)set_afraid(0);
+ (void)set_paralyzed(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+
+ /* accounting for a new ailment. -LM- */
+ p_ptr->black_breath = FALSE;
+
+ /* Hack -- don't go to undead form */
+ p_ptr->necro_extra &= ~CLASS_UNDEAD;
+
+ /* Hack -- Prevent starvation */
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Hack -- cancel recall */
+ if (p_ptr->word_recall)
+ {
+ /* Message */
+ msg_print("A tension leaves the air around you...");
+ msg_print(NULL);
+
+ /* Hack -- Prevent recall */
+ p_ptr->word_recall = 0;
+ }
+
+ /* Note cause of death XXX XXX XXX */
+ (void)strcpy(died_from, "Cheating death");
+
+ /* Do not die */
+ death = FALSE;
+
+ /* New depth -KMW- */
+ /* dun_level = 0; */
+ p_ptr->inside_arena = 0;
+ leaving_quest = 0;
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ }
+
+ /* Handle "death" */
+ if (death)
+ {
+ break;
+ }
+
+ /* Mega hack */
+ if (dun_level) p_ptr->wild_mode = FALSE;
+
+ /* Make a new level */
+ process_hooks(HOOK_NEW_LEVEL, "(d)", is_quest(dun_level));
+ generate_cave();
+ }
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+}
+
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..be576d7b
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,3 @@
+/tolua
+/tome
+w_*.c
diff --git a/src/A-mac-h.pch b/src/A-mac-h.pch
new file mode 100644
index 00000000..5f97bdc0
--- /dev/null
+++ b/src/A-mac-h.pch
@@ -0,0 +1,104 @@
+/* File: A-mac-h.pch */
+
+/*
+ * This file creates pre-compiled header files which are used to
+ * compile Macintosh Angband using the Code Warrior Pro compiler.
+ *
+ * It can also be used with the later versions of CodeWarrior.
+ * Both PPC and 68K binaries have been built successfully by
+ * CodeWarrior Version 6 for Macintosh:
+ */
+
+#if defined(__MWERKS__)
+# if defined(powerc) || defined(__powerc)
+# pragma precompile_target "A-mac-h-PPC"
+# else
+# pragma precompile_target "A-mac-h-68K"
+# endif
+#endif
+
+/*
+ * Activate the Macintosh-specific code
+ */
+#define MACINTOSH
+
+/*
+ * Activate Lua scripting
+ */
+#define USE_LUA
+
+/*
+ * PernAngband specific main-xxx.c features
+ */
+/* We have in-game savefile chooser */
+#define SAVEFILE_SCREEN
+/* We have Angband 2.8.1-style reset_visuals() */
+#define ANG281_RESET_VISUALS
+/* We now have big screen support */
+#define ALLOW_BIG_SCREEN
+/* We support transparency effect, but don't have use_transparency variable */
+#define NO_USE_TRANSPARENCY_VAR
+/* Creator code */
+#define ANGBAND_CREATOR 'PrnA'
+/* Preferences file name */
+#define ANGBAND_PREFERENCES "ToME Preferences"
+
+/*
+ * Activate transparency effect for 16x16 tileset.
+ */
+#define USE_TRANSPARENCY
+
+/*
+ * Activate support for graphics overlay.
+ */
+#define USE_EGO_GRAPHICS
+
+/*
+ * Active the code to support Mogami's double width tile patch
+ */
+#define USE_DOUBLE_TILES
+
+/*
+ * OPTION: Allow "Wizards" to yield "high scores"
+ */
+/* #define SCORE_WIZARDS */
+
+/*
+ * OPTION: Allow "Cheaters" to yield "high scores"
+ */
+/* #define SCORE_CHEATERS */
+
+/*
+ * OPTION: Allow player to know his / her starting life rate
+ */
+/* #define SHOW_LIFE_RATE */
+
+/*
+ * OPTION: Unallow the using of every race/class combination
+ */
+/* #define FORBID_BAD_COMBINAISON */
+
+/*
+ * OPTION: Enable the CTRL + L command to quit without saving
+ * as well as activate the Exit menu that serves for the same purpose
+ */
+/* #define ALLOW_QUITING */
+
+/*
+ * Precompile the header files
+ */
+#include "angband.h"
+
+/*
+ * These should go into h-system.h XXX XXX XXX
+ */
+# include <unistd.h>
+# include <fcntl.h>
+
+/*
+ * Include the standard ansi_prefix.mac.h file
+ */
+#define MSL_USE_PRECOMPILED_HEADERS 0
+#include "ansi_prefix.mac.h"
+
+
diff --git a/src/ENGLISH.txt b/src/ENGLISH.txt
new file mode 100644
index 00000000..354aa129
--- /dev/null
+++ b/src/ENGLISH.txt
@@ -0,0 +1,35 @@
+ English Text Style
+
+* Use British spelling in preference to American spelling. Quotations,
+ however, should be left intact.
+
+* Capitalize the name of spells and special powers as you would the
+ title of a book.
+
+* Avoid unnecessary modernisms. This helps preserve the mood -- an
+ item "allows", rather than "enables", you to do something.
+
+* When describing a person possessing an item, use "its wielder" or
+ "its wearer" in preference to "the wielder" or "the wearer".
+
+* When describing a gendered person of indeterminate gender, use
+ masculine terms. "This icy sword increases its wielder's statistics
+ and sustains his strength."
+
+* Use the British Imperial system for measurements. This means feet
+ and inches, not meters and centimeters.
+
+
+ Specific Issues
+
+* Use "hit points", not "hitpoints".
+
+* Use "Long Sword", not "Long-sword" or "Longsword".
+
+* Use "Middle-earth", not "middle-earth" or "Middle Earth".
+
+* Use "staves", not "staffs".
+
+* Use "dwarves", not "dwarfs".
+
+-- markrax (Mark Schreiber) <mark7@andrew.cmu.edu>
diff --git a/src/angband.h b/src/angband.h
new file mode 100644
index 00000000..8e0ad1f9
--- /dev/null
+++ b/src/angband.h
@@ -0,0 +1,102 @@
+/* File: angband.h */
+
+/* Main "Angband" header file */
+
+#ifndef INCLUDED_ANGBAND_H
+#define INCLUDED_ANGBAND_H
+
+/*
+ * Copyright (c) 1989 James E. Wilson
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+
+/*
+ * First, include the low-level includes. Be sure to edit "h-config.h"
+ * to reflect any hardware, operating system, or compiler nuances.
+ */
+#include "h-basic.h"
+
+
+/*
+ * Then, include the header files for the low-level code
+ */
+#include "z-util.h"
+#include "z-virt.h"
+#include "z-form.h"
+#include "z-rand.h"
+#include "z-term.h"
+#include "z-sock.h"
+
+
+/*
+ * Include the "Angband" configuration header
+ */
+#include "config.h"
+
+
+/*
+ * Now, include the define's, the type's, and the extern's
+ */
+#include "defines.h"
+#include "types.h"
+#include "externs.h"
+#include "plots.h"
+
+/***** Some copyright messages follow below *****/
+
+/*
+ * Note that these copyright messages apply to an ancient version
+ * of Angband, as in, from pre-2.4.frog-knows days, and thus the
+ * reference to "5.0" is rather misleading...
+ */
+
+/*
+ * UNIX ANGBAND Version 5.0
+ */
+
+
+/* Original copyright message follows. */
+
+/*
+ * ANGBAND Version 4.8 COPYRIGHT (c) Robert Alan Koeneke
+ *
+ * I lovingly dedicate this game to hackers and adventurers
+ * everywhere...
+ *
+ * Designer and Programmer:
+ * Robert Alan Koeneke
+ * University of Oklahoma
+ *
+ * Assistant Programmer:
+ * Jimmey Wayne Todd
+ * University of Oklahoma
+ *
+ * Assistant Programmer:
+ * Gary D. McAdoo
+ * University of Oklahoma
+ *
+ * UNIX Port:
+ * James E. Wilson
+ * UC Berkeley
+ * wilson@ernie.Berkeley.EDU
+ * ucbvax!ucbernie!wilson
+ */
+
+
+/*
+ * ANGBAND may be copied and modified freely as long as the above
+ * credits are retained. No one who-so-ever may sell or market
+ * this software in any form without the expressed written consent
+ * of the author Robert Alan Koeneke.
+ */
+
+
+#endif
+
+
+
diff --git a/src/angband.ico b/src/angband.ico
new file mode 100644
index 00000000..d20d4c7f
--- /dev/null
+++ b/src/angband.ico
Binary files differ
diff --git a/src/angband.rc b/src/angband.rc
new file mode 100644
index 00000000..00e16516
--- /dev/null
+++ b/src/angband.rc
@@ -0,0 +1,132 @@
+/* File: angband.rc */
+
+/*
+ * If the windows command to compile this file understands #ifdef's, please
+ * say #ifdef ALLOW_QUITTING to select from "A&bort" and "Sh&ow scores"
+ */
+
+ANGBAND MENU
+{
+ POPUP "&File"
+ {
+ MENUITEM "&Save", 110
+ MENUITEM SEPARATOR
+ MENUITEM "S&how score", 120
+ /*MENUITEM "A&bort", 120*/
+ MENUITEM "E&xit", 121
+ }
+
+ POPUP "&Window"
+ {
+ POPUP "&Visibility"
+ {
+ MENUITEM "ToME window", 200
+ MENUITEM "Mirror window", 201
+ MENUITEM "Recall window", 202
+ MENUITEM "Choice window", 203
+ MENUITEM "Term-4 window", 204
+ MENUITEM "Term-5 window", 205
+ MENUITEM "Term-6 window", 206
+ MENUITEM "Term-7 window", 207
+ }
+
+ POPUP "&Font"
+ {
+ MENUITEM "ToME window", 210
+ MENUITEM "Mirror window", 211
+ MENUITEM "Recall window", 212
+ MENUITEM "Choice window", 213
+ MENUITEM "Term-4 window", 214
+ MENUITEM "Term-5 window", 215
+ MENUITEM "Term-6 window", 216
+ MENUITEM "Term-7 window", 217
+ }
+
+ MENUITEM SEPARATOR
+
+ POPUP "Bizarre Display"
+ {
+ MENUITEM "ToME window", 230
+ MENUITEM "Mirror window", 231
+ MENUITEM "Recall window", 232
+ MENUITEM "Choice window", 233
+ MENUITEM "Term-4 window", 234
+ MENUITEM "Term-5 window", 235
+ MENUITEM "Term-6 window", 236
+ MENUITEM "Term-7 window", 237
+ }
+
+ POPUP "Increase Tile Width"
+ {
+ MENUITEM "ToME window", 240
+ MENUITEM "Mirror window", 241
+ MENUITEM "Recall window", 242
+ MENUITEM "Choice window", 243
+ MENUITEM "Term-4 window", 244
+ MENUITEM "Term-5 window", 245
+ MENUITEM "Term-6 window", 246
+ MENUITEM "Term-7 window", 247
+ }
+
+ POPUP "Decrease Tile Width"
+ {
+ MENUITEM "ToME window", 250
+ MENUITEM "Mirror window", 251
+ MENUITEM "Recall window", 252
+ MENUITEM "Choice window", 253
+ MENUITEM "Term-4 window", 254
+ MENUITEM "Term-5 window", 255
+ MENUITEM "Term-6 window", 256
+ MENUITEM "Term-7 window", 257
+ }
+
+ POPUP "Increase Tile Height"
+ {
+ MENUITEM "ToME window", 260
+ MENUITEM "Mirror window", 261
+ MENUITEM "Recall window", 262
+ MENUITEM "Choice window", 263
+ MENUITEM "Term-4 window", 264
+ MENUITEM "Term-5 window", 265
+ MENUITEM "Term-6 window", 266
+ MENUITEM "Term-7 window", 267
+ }
+
+ POPUP "Decrease Tile Height"
+ {
+ MENUITEM "ToME window", 270
+ MENUITEM "Mirror window", 271
+ MENUITEM "Recall window", 272
+ MENUITEM "Choice window", 273
+ MENUITEM "Term-4 window", 274
+ MENUITEM "Term-5 window", 275
+ MENUITEM "Term-6 window", 276
+ MENUITEM "Term-7 window", 277
+ }
+ }
+
+ POPUP "&Options"
+ {
+ POPUP "&Graphics"
+ {
+ MENUITEM "&Old tiles", 400
+ MENUITEM "&New tiles", 401
+ MENUITEM "ASCII &Text", 403
+ MENUITEM "&Bigtile mode", 409
+ }
+
+ MENUITEM "&Sound", 402
+ MENUITEM SEPARATOR
+ MENUITEM "Unused menu option", 410
+ MENUITEM "Activate Screensaver", 411
+ }
+
+ POPUP "&Help"
+ {
+ MENUITEM "&General", 901
+ MENUITEM "&Spoilers", 902
+ }
+}
+
+ANGBAND ICON "angband.ico"
+
diff --git a/src/birth.c b/src/birth.c
new file mode 100644
index 00000000..b3b61e3c
--- /dev/null
+++ b/src/birth.c
@@ -0,0 +1,3920 @@
+/* File: birth.c */
+
+/* Purpose: create a player character */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * How often the autoroller will update the display and pause
+ * to check for user interuptions.
+ * Bigger values will make the autoroller faster, but slower
+ * system may have problems because the user can't stop the
+ * autoroller for this number of rolls.
+ */
+#define AUTOROLLER_STEP 25L
+
+/*
+ * Maximum number of tries for selection of a proper quest monster
+ */
+#define MAX_TRIES 100
+
+/* Max quests */
+static byte max_quests = 0;
+
+/*
+ * Current stats
+ */
+static s16b stat_use[6];
+
+/*
+ * Autoroll limit
+ */
+static s16b stat_limit[6];
+
+/*
+ * Autoroll matches
+ */
+static s32b stat_match[6];
+
+/*
+ * Autoroll round
+ */
+static s32b auto_round;
+
+/*
+ * Last round
+ */
+static s32b last_round;
+
+/* Human */
+static char *human_syllable1[] =
+{
+ "Ab", "Ac", "Ad", "Af", "Agr", "Ast", "As", "Al", "Adw", "Adr", "Ar",
+ "B", "Br", "C", "Cr", "Ch", "Cad", "D", "Dr", "Dw", "Ed", "Eth", "Et",
+ "Er", "El", "Eow", "F", "Fr", "G", "Gr", "Gw", "Gal", "Gl", "H", "Ha",
+ "Ib", "Jer", "K", "Ka", "Ked", "L", "Loth", "Lar", "Leg", "M", "Mir",
+ "N", "Nyd", "Ol", "Oc", "On", "P", "Pr", "R", "Rh", "S", "Sev", "T",
+ "Tr", "Th", "V", "Y", "Z", "W", "Wic",
+};
+
+static char *human_syllable2[] =
+{
+ "a", "ae", "au", "ao", "are", "ale", "ali", "ay", "ardo", "e", "ei",
+ "ea", "eri", "era", "ela", "eli", "enda", "erra", "i", "ia", "ie",
+ "ire", "ira", "ila", "ili", "ira", "igo", "o", "oa", "oi", "oe",
+ "ore", "u", "y",
+};
+
+static char *human_syllable3[] =
+{
+ "a", "and", "b", "bwyn", "baen", "bard", "c", "ctred", "cred", "ch",
+ "can", "d", "dan", "don", "der", "dric", "dfrid", "dus", "f", "g",
+ "gord", "gan", "l", "li", "lgrin", "lin", "lith", "lath", "loth",
+ "ld", "ldric", "ldan", "m", "mas", "mos", "mar", "mond", "n",
+ "nydd", "nidd", "nnon", "nwan", "nyth", "nad", "nn", "nnor", "nd",
+ "p", "r", "ron", "rd", "s", "sh", "seth", "sean", "t", "th", "tha",
+ "tlan", "trem", "tram", "v", "vudd", "w", "wan", "win", "wyn", "wyr",
+ "wyr", "wyth",
+};
+
+/*
+ * Random Name Generator
+ * based on a Javascript by Michael Hensley
+ * "http://geocities.com/timessquare/castle/6274/"
+ */
+static void create_random_name(int race, char *name)
+{
+ char *syl1, *syl2, *syl3;
+
+ int idx;
+
+
+ /* Paranoia */
+ if (!name) return;
+
+ /* Select the monster type */
+ switch (race)
+ {
+ /* Create the monster name */
+
+ /* Use human ones */
+ default:
+ {
+ idx = rand_int(sizeof(human_syllable1) / sizeof(char *));
+ syl1 = human_syllable1[idx];
+ idx = rand_int(sizeof(human_syllable2) / sizeof(char *));
+ syl2 = human_syllable2[idx];
+ idx = rand_int(sizeof(human_syllable3) / sizeof(char *));
+ syl3 = human_syllable3[idx];
+
+ break;
+ }
+ }
+
+ /* Concatenate selected syllables */
+ strnfmt(name, 32, "%s%s%s", syl1, syl2, syl3);
+}
+
+
+void print_desc_aux(cptr txt, int y, int xx)
+{
+ int i = -1, x = xx;
+
+
+ while (txt[++i] != 0)
+ {
+ if (txt[i] == '\n')
+ {
+ x = xx;
+ y++;
+ }
+ else
+ {
+ Term_putch(x++, y, TERM_YELLOW, txt[i]);
+ }
+ }
+}
+
+void print_desc(cptr txt)
+{
+ print_desc_aux(txt, 12, 1);
+}
+
+/*
+ * Save the current data for later
+ */
+static void save_prev_data(void)
+{
+ int i;
+
+
+ /*** Save the current data ***/
+
+ /* Save the data */
+ previous_char.sex = p_ptr->psex;
+ previous_char.race = p_ptr->prace;
+ previous_char.rmod = p_ptr->pracem;
+ previous_char.pclass = p_ptr->pclass;
+ previous_char.spec = p_ptr->pspec;
+
+ previous_char.quests = max_quests;
+
+ previous_char.god = p_ptr->pgod;
+ previous_char.grace = p_ptr->grace;
+
+ previous_char.age = p_ptr->age;
+ previous_char.wt = p_ptr->wt;
+ previous_char.ht = p_ptr->ht;
+ previous_char.sc = p_ptr->sc;
+ previous_char.au = p_ptr->au;
+
+ /* Save the stats */
+ for (i = 0; i < 6; i++)
+ {
+ previous_char.stat[i] = p_ptr->stat_max[i];
+ }
+ previous_char.luck = p_ptr->luck_base;
+
+ /* Save the chaos patron */
+ previous_char.chaos_patron = p_ptr->chaos_patron;
+
+ /* Save the weapon specialty */
+ previous_char.weapon = 0;
+
+ /* Save the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(previous_char.history[i], history[i]);
+ }
+}
+
+
+/*
+ * Load the previous data
+ */
+static void load_prev_data(bool save)
+{
+ int i;
+
+ birther temp;
+
+
+ /*** Save the current data ***/
+
+ /* Save the data */
+ temp.age = p_ptr->age;
+ temp.wt = p_ptr->wt;
+ temp.ht = p_ptr->ht;
+ temp.sc = p_ptr->sc;
+ temp.au = p_ptr->au;
+
+ /* Save the stats */
+ for (i = 0; i < 6; i++)
+ {
+ temp.stat[i] = p_ptr->stat_max[i];
+ }
+ temp.luck = p_ptr->luck_base;
+
+ /* Save the chaos patron */
+ temp.chaos_patron = p_ptr->chaos_patron;
+
+ /* Save the weapon specialty */
+ temp.weapon = 0;
+
+ /* Save the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(temp.history[i], history[i]);
+ }
+
+
+ /*** Load the previous data ***/
+
+ /* Load the data */
+ p_ptr->age = previous_char.age;
+ p_ptr->wt = previous_char.wt;
+ p_ptr->ht = previous_char.ht;
+ p_ptr->sc = previous_char.sc;
+ p_ptr->au = previous_char.au;
+
+ /* Load the stats */
+ for (i = 0; i < 6; i++)
+ {
+ p_ptr->stat_max[i] = previous_char.stat[i];
+ p_ptr->stat_cur[i] = previous_char.stat[i];
+ }
+ p_ptr->luck_base = previous_char.luck;
+ p_ptr->luck_max = previous_char.luck;
+
+ /* Load the chaos patron */
+ p_ptr->chaos_patron = previous_char.chaos_patron;
+
+ /* Load the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(history[i], previous_char.history[i]);
+ }
+
+
+ /*** Save the current data ***/
+ if (!save) return;
+
+ /* Save the data */
+ previous_char.age = temp.age;
+ previous_char.wt = temp.wt;
+ previous_char.ht = temp.ht;
+ previous_char.sc = temp.sc;
+ previous_char.au = temp.au;
+
+ /* Save the stats */
+ for (i = 0; i < 6; i++)
+ {
+ previous_char.stat[i] = temp.stat[i];
+ }
+ previous_char.luck = temp.luck;
+
+ /* Save the chaos patron */
+ previous_char.chaos_patron = temp.chaos_patron;
+
+ /* Save the weapon specialty */
+ previous_char.weapon = temp.weapon;
+
+ /* Save the history */
+ for (i = 0; i < 4; i++)
+ {
+ strcpy(previous_char.history[i], temp.history[i]);
+ }
+}
+
+
+
+
+/*
+ * Returns adjusted stat -JK- Algorithm by -JWT-
+ *
+ * auto_roll is boolean and states maximum changes should be used rather
+ * than random ones to allow specification of higher values to wait for
+ *
+ * The "p_ptr->maximize" code is important -BEN-
+ */
+static int adjust_stat(int value, int amount, int auto_roll)
+{
+ int i;
+
+
+ /* Negative amounts */
+ if (amount < 0)
+ {
+ /* Apply penalty */
+ for (i = 0; i < (0 - amount); i++)
+ {
+ if (value >= 18 + 10)
+ {
+ value -= 10;
+ }
+ else if (value > 18)
+ {
+ value = 18;
+ }
+ else if (value > 3)
+ {
+ value--;
+ }
+ }
+ }
+
+ /* Positive amounts */
+ else if (amount > 0)
+ {
+ /* Apply reward */
+ for (i = 0; i < amount; i++)
+ {
+ if (value < 18)
+ {
+ value++;
+ }
+ else if (p_ptr->maximize)
+ {
+ value += 10;
+ }
+ else if (value < 18 + 70)
+ {
+ value += ((auto_roll ? 15 : randint(15)) + 5);
+ }
+ else if (value < 18 + 90)
+ {
+ value += ((auto_roll ? 6 : randint(6)) + 2);
+ }
+ else if (value < 18 + 100)
+ {
+ value++;
+ }
+ }
+ }
+
+ /* Return the result */
+ return (value);
+}
+
+
+
+
+/*
+ * Roll for a characters stats
+ *
+ * For efficiency, we include a chunk of "calc_bonuses()".
+ */
+static void get_stats(void)
+{
+ int i, j;
+
+ int bonus;
+
+ int dice[18];
+
+
+ /* Roll and verify some stats */
+ while (TRUE)
+ {
+ /* Roll some dice */
+ for (j = i = 0; i < 18; i++)
+ {
+ /* Roll the dice */
+ dice[i] = randint(3 + i % 3);
+
+ /* Collect the maximum */
+ j += dice[i];
+ }
+
+ /*
+ * Verify totals
+ *
+ * 57 was 54... I hate 'magic numbers' :< TY
+ *
+ * (d3 + d4 + d5) ~= 7.5 (+- 4.5)
+ * with 5 makes avg. stat value of 12.5 (min 8, max 17)
+ *
+ * (d3 + d4 + d5) x 6 ~= 45 (+- 18)
+ *
+ * So the original value (still used by Vanilla as of 2.9.3)
+ * allows (avg - 2)..(avg + 8), while this Z version
+ * (avg - 2)..(avg + 11). I don't understand what TY meant
+ * by "magic numbers", but I like big stats :) -- pelpel
+ *
+ */
+ if ((j > 42) && (j < 57)) break;
+ }
+
+ /* Acquire the stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Extract 5 + 1d3 + 1d4 + 1d5 */
+ j = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2];
+
+ /* Save that value */
+ p_ptr->stat_max[i] = j;
+
+ /* Obtain a "bonus" for "race" and "class" */
+ bonus = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i];
+
+ /* Variable stat maxes */
+ if (p_ptr->maximize)
+ {
+ /* Start fully healed */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i];
+
+ /* Efficiency -- Apply the racial/class bonuses */
+ stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus);
+ }
+
+ /* Fixed stat maxes */
+ else
+ {
+ /* Apply the bonus to the stat (somewhat randomly) */
+ stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE);
+
+ /* Save the resulting stat maximum */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i];
+ }
+
+ /* No temporary drain (yet...) */
+ p_ptr->stat_cnt[i] = 0;
+ p_ptr->stat_los[i] = 0;
+ }
+
+ /* Get luck */
+ p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5);
+ p_ptr->luck_max = p_ptr->luck_base;
+}
+
+
+/*
+ * Roll for some info that the auto-roller ignores
+ */
+static void get_extra(void)
+{
+ int i, j, min_value, max_value;
+
+#ifdef SHOW_LIFE_RATE
+
+ int percent;
+
+#endif
+
+
+ /* Level one */
+ p_ptr->max_plv = p_ptr->lev = 1;
+
+ /* Experience factor */
+ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp;
+
+ /* Initialize arena and rewards information -KMW- */
+ p_ptr->arena_number = 0;
+ p_ptr->inside_arena = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->exit_bldg = TRUE; /* only used for arena now -KMW- */
+
+ /* Hitdice */
+ p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp;
+
+ /* Initial hitpoints */
+ p_ptr->mhp = p_ptr->hitdie;
+
+ /* Minimum hitpoints at highest level */
+ min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8;
+ min_value += PY_MAX_LEVEL;
+
+ /* Maximum hitpoints at highest level */
+ max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8;
+ max_value += PY_MAX_LEVEL;
+
+ /* Pre-calculate level 1 hitdice */
+ player_hp[0] = p_ptr->hitdie;
+
+ /* Roll out the hitpoints */
+ while (TRUE)
+ {
+ /* Roll the hitpoint values */
+ for (i = 1; i < PY_MAX_LEVEL; i++)
+ {
+ j = randint(p_ptr->hitdie);
+ player_hp[i] = player_hp[i - 1] + j;
+ }
+
+ /* XXX Could also require acceptable "mid-level" hitpoints */
+
+ /* Require "valid" hitpoints at highest level */
+ if (player_hp[PY_MAX_LEVEL - 1] < min_value) continue;
+ if (player_hp[PY_MAX_LEVEL - 1] > max_value) continue;
+
+ /* Acceptable */
+ break;
+ }
+
+ p_ptr->tactic = 4;
+ p_ptr->movement = 4;
+
+#ifdef SHOW_LIFE_RATE
+
+ percent = (int)(((long)player_hp[PY_MAX_LEVEL - 1] * 200L) /
+ (p_ptr->hitdie + ((PY_MAX_LEVEL - 1) * p_ptr->hitdie)));
+
+ msg_format("Current Life Rating is %d/100.", percent);
+ msg_print(NULL);
+
+#endif /* SHOW_LIFE_RATE */
+}
+
+
+/*
+ * Get the racial history, and social class, using the "history charts".
+ */
+static void get_history(void)
+{
+ int i, n, chart, roll, social_class;
+
+ char *s, *t;
+
+ char buf[240];
+
+
+ /* Clear the previous history strings */
+ for (i = 0; i < 4; i++) history[i][0] = '\0';
+
+ /* Clear the history text */
+ buf[0] = '\0';
+
+ /* Initial social class */
+ social_class = randint(4);
+
+ /* Starting place */
+ chart = rp_ptr->chart;
+
+ /* Process the history */
+ while (chart)
+ {
+ /* Start over */
+ i = 0;
+
+ /* Roll for nobility */
+ roll = randint(100);
+
+
+ /* Access the proper entry in the table */
+ while ((chart != bg[i].chart) || (roll > bg[i].roll)) i++;
+
+ /* Acquire the textual history */
+ (void)strcat(buf, bg[i].info + rp_text);
+
+ /* Add in the social class */
+ social_class += (int)(bg[i].bonus) - 50;
+
+ /* Enter the next chart */
+ chart = bg[i].next;
+ }
+
+
+
+ /* Verify social class */
+ if (social_class > 100) social_class = 100;
+ else if (social_class < 1) social_class = 1;
+
+ /* Save the social class */
+ p_ptr->sc = social_class;
+
+
+ /* Skip leading spaces */
+ for (s = buf; *s == ' '; s++) /* loop */;
+
+ /* Get apparent length */
+ n = strlen(s);
+
+ /* Kill trailing spaces */
+ while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0';
+
+
+ /* Start at first line */
+ i = 0;
+
+ /* Collect the history */
+ while (TRUE)
+ {
+ /* Extract remaining length */
+ n = strlen(s);
+
+ /* All done */
+ if (n < 60)
+ {
+ /* Save one line of history */
+ strcpy(history[i++], s);
+
+ /* All done */
+ break;
+ }
+
+ /* Find a reasonable break-point */
+ for (n = 60; ((n > 0) && (s[n - 1] != ' ')); n--) /* loop */;
+
+ /* Save next location */
+ t = s + n;
+
+ /* Wipe trailing spaces */
+ while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0';
+
+ /* Save one line of history */
+ strcpy(history[i++], s);
+
+ /* Start next line */
+ for (s = t; *s == ' '; s++) /* loop */;
+ }
+}
+
+
+/*
+ * Fill the random_artifacts array with relevant info.
+ */
+errr init_randart(void)
+{
+ int i;
+
+ long cost;
+
+ random_artifact* ra_ptr;
+
+ char buf[80];
+
+
+ for (i = 0; i < MAX_RANDARTS; i++)
+ {
+ ra_ptr = &random_artifacts[i];
+
+ strcpy(ra_ptr->name_short,
+ get_line("rart_s.txt", ANGBAND_DIR_FILE, buf, i));
+ strcpy(ra_ptr->name_full,
+ get_line("rart_f.txt", ANGBAND_DIR_FILE, buf, i));
+
+ ra_ptr->attr = randint(15);
+ ra_ptr->activation = rand_int(MAX_T_ACT);
+ ra_ptr->generated = FALSE;
+
+ cost = randnor(0, 250);
+
+ if (cost < 0) cost = 0;
+
+ ra_ptr->cost = cost;
+ }
+
+ return 0;
+}
+
+
+/*
+ * A helper function for get_ahw(), also called by polymorph code
+ */
+void get_height_weight(void)
+{
+ int h_mean, h_stddev;
+
+ int w_mean, w_stddev;
+
+
+ /* Extract mean and standard deviation -- Male */
+ if (p_ptr->psex == SEX_MALE)
+ {
+ h_mean = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+ h_stddev = rp_ptr->m_m_ht + rmp_ptr->m_m_ht;
+
+ w_mean = rp_ptr->m_b_wt + rmp_ptr->m_b_wt;
+ w_stddev = rp_ptr->m_m_wt + rmp_ptr->m_m_wt;
+ }
+
+ /* Female */
+ else if (p_ptr->psex == SEX_FEMALE)
+ {
+ h_mean = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ h_stddev = rp_ptr->f_m_ht + rmp_ptr->f_m_ht;
+
+ w_mean = rp_ptr->f_b_wt + rmp_ptr->f_b_wt;
+ w_stddev = rp_ptr->f_m_wt + rmp_ptr->f_m_wt;
+ }
+
+ /* Neuter XXX */
+ else
+ {
+ h_mean = (rp_ptr->m_b_ht + rmp_ptr->m_b_ht +
+ rp_ptr->f_b_ht + rmp_ptr->f_b_ht) / 2,
+ h_stddev = (rp_ptr->m_m_ht + rmp_ptr->m_m_ht +
+ rp_ptr->f_m_ht + rmp_ptr->f_m_ht) / 2;
+
+ w_mean = (rp_ptr->m_b_wt + rmp_ptr->m_b_wt +
+ rp_ptr->f_b_wt + rmp_ptr->f_b_wt) / 2,
+ w_stddev = (rp_ptr->m_m_wt + rmp_ptr->m_m_wt +
+ rp_ptr->f_m_wt + rmp_ptr->f_m_wt) / 2;
+ }
+
+ /* Calculate height/weight */
+ p_ptr->ht = randnor(h_mean, h_stddev);
+ p_ptr->wt = randnor(w_mean, w_stddev);
+
+ /* Weight/height shouldn't be negative */
+ if (p_ptr->ht < 1) p_ptr->ht = 1;
+ if (p_ptr->wt < 1) p_ptr->wt = 1;
+}
+
+
+/*
+ * Computes character's age, height, and weight
+ */
+static void get_ahw(void)
+{
+ /* Calculate the age */
+ p_ptr->age = rp_ptr->b_age + rmp_ptr->b_age +
+ randint(rp_ptr->m_age + rmp_ptr->m_age);
+
+ /* Calculate the height/weight */
+ get_height_weight();
+}
+
+
+
+
+/*
+ * Get the player's starting money
+ */
+static void get_money(void)
+{
+ int i, gold;
+
+
+ /* Social Class determines starting gold */
+ gold = (p_ptr->sc * 6) + randint(100) + 300;
+
+ /* Process the stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Mega-Hack -- reduce gold for high stats */
+ if (stat_use[i] >= 18 + 50) gold -= 300;
+ else if (stat_use[i] >= 18 + 20) gold -= 200;
+ else if (stat_use[i] > 18) gold -= 150;
+ else gold -= (stat_use[i] - 8) * 10;
+ }
+
+ /* Minimum 100 gold */
+ if (gold < 100) gold = 100;
+
+ /* Save the gold */
+ p_ptr->au = gold;
+}
+
+
+
+/*
+ * Display stat values, subset of "put_stats()"
+ *
+ * See 'display_player()' for basic method.
+ */
+static void birth_put_stats(void)
+{
+ int i, p;
+
+ byte attr;
+
+ char buf[80];
+
+
+ /* Put the stats (and percents) */
+ for (i = 0; i < 6; i++)
+ {
+ /* Put the stat */
+ cnv_stat(p_ptr->stat_use[i], buf);
+ c_put_str(TERM_L_GREEN, buf, 2 + i, 66);
+
+ /* Put the percent */
+ if (stat_match[i])
+ {
+ p = 1000L * stat_match[i] / auto_round;
+ attr = (p < 100) ? TERM_YELLOW : TERM_L_GREEN;
+ strnfmt(buf, 80, "%3d.%d%%", p / 10, p % 10);
+ c_put_str(attr, buf, 2 + i, 73);
+ }
+
+ /* Never happened */
+ else
+ {
+ c_put_str(TERM_RED, "(NONE)", 2 + i, 73);
+ }
+ }
+}
+
+
+/*
+ * Clear all the global "character" data
+ */
+static void player_wipe(void)
+{
+ int i, j;
+
+ bool *powers;
+ bool *corruptions;
+
+
+ /* Wipe special levels */
+ wipe_saved();
+
+ /* Save the powers & corruptions */
+ powers = p_ptr->powers;
+ corruptions = p_ptr->corruptions;
+
+ /* Hack -- zero the struct */
+ p_ptr = WIPE(p_ptr, player_type);
+
+ /* Restore the powers & corruptions */
+ p_ptr->powers = powers;
+ p_ptr->corruptions = corruptions;
+
+ /* Not dead yet */
+ p_ptr->lives = 0;
+
+ /* Wipe the corruptions */
+ (void)C_WIPE(p_ptr->corruptions, max_corruptions, bool);
+
+ /* Wipe the history */
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 60; j++)
+ {
+ if (j < 59) history[i][j] = ' ';
+ else history[i][j] = '\0';
+ }
+ }
+
+ /* Wipe the towns */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ for (j = 0; j < MAX_DUNGEON_DEPTH; j++)
+ {
+ special_lvl[j][i] = 0;
+ }
+ }
+
+ /* Wipe the towns */
+ for (i = max_real_towns + 1; i < max_towns; i++)
+ {
+ town_info[i].flags = 0;
+ }
+
+ /* Wipe the quests */
+ for (i = 0; i < MAX_Q_IDX_INIT; i++)
+ {
+ quest[i].status = QUEST_STATUS_UNTAKEN;
+ for (j = 0; j < 4; j++)
+ {
+ quest[i].data[j] = 0;
+ }
+ }
+
+ /* Wipe the rune spells */
+ rune_num = 0;
+ for (i = 0; i < MAX_RUNES; i++)
+ {
+ strcpy(rune_spells[i].name, "");
+ rune_spells[i].type = 0;
+ rune_spells[i].rune2 = 0;
+ rune_spells[i].mana = 0;
+ }
+
+ /* No items */
+ inven_cnt = 0;
+ equip_cnt = 0;
+
+ /* Clear the inventory */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_wipe(&p_ptr->inventory[i]);
+ }
+
+ /* Generate random artifacts */
+ init_randart();
+
+ /* Start with no artifacts made yet */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+ a_ptr->cur_num = 0;
+ }
+
+ /* Reset the "objects" */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Reset "tried" */
+ k_ptr->tried = FALSE;
+
+ /* Reset "aware" */
+ k_ptr->aware = FALSE;
+
+ /* Reset "know" */
+ k_ptr->know = FALSE;
+
+ /* Reset "artifact" */
+ k_ptr->artifact = 0;
+ }
+
+
+ /* Reset the "monsters" */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Hack -- Reset the counter */
+ r_ptr->cur_num = 0;
+
+ /* Hack -- Reset the max counter */
+ r_ptr->max_num = 100;
+
+ /* Hack -- Reset the max counter */
+ if (r_ptr->flags1 & RF1_UNIQUE) r_ptr->max_num = 1;
+ if (r_ptr->flags3 & RF3_UNIQUE_4) r_ptr->max_num = 4;
+
+ /* Clear player kills */
+ r_ptr->r_pkills = 0;
+
+ /* Clear saved flag */
+ r_ptr->on_saved = FALSE;
+ }
+
+
+ /* Hack -- Well fed player */
+ p_ptr->food = PY_FOOD_FULL - 1;
+
+ /* Wipe the alchemists' recipes */
+ for ( i = 0 ; i < 32 ; i++)
+ alchemist_known_egos[i] = 0;
+ for ( i = 0 ; i < 6 ; i++)
+ alchemist_known_artifacts[i] = 0;
+ alchemist_gained = 0;
+
+ /* Clear "cheat" options */
+ cheat_peek = FALSE;
+ cheat_hear = FALSE;
+ cheat_room = FALSE;
+ cheat_xtra = FALSE;
+ cheat_know = FALSE;
+ cheat_live = FALSE;
+
+ /* Assume no winning game */
+ total_winner = 0;
+ has_won = FALSE;
+
+ /* Assume no panic save */
+ panic_save = 0;
+
+ /* Assume no cheating */
+ noscore = 0;
+ wizard = 0;
+
+ /* Assume no innate spells */
+ spell_num = 0;
+
+ /* Clear the fate */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ fates[i].fate = 0;
+ }
+ p_ptr->no_mortal = FALSE;
+
+ /* Player don't have the black breath from the beginning !*/
+ p_ptr->black_breath = FALSE;
+
+ /* Default pet command settings */
+ p_ptr->pet_follow_distance = 6;
+ p_ptr->pet_open_doors = FALSE;
+ p_ptr->pet_pickup_items = FALSE;
+
+ /* Body changing initialisation */
+ p_ptr->body_monster = 0;
+ p_ptr->disembodied = FALSE;
+
+ /* Wipe the bounties */
+ total_bounties = 0;
+
+ /* Wipe spells */
+ p_ptr->xtra_spells = 0;
+
+ /* Wipe xtra hp */
+ p_ptr->hp_mod = 0;
+
+ /* Wipe the monsters */
+ wipe_m_list();
+
+ /* Wipe the doppleganger */
+ doppleganger = 0;
+
+ /* Wipe the recall depths */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ max_dlv[i] = 0;
+ }
+
+ /* Wipe the known inscription list */
+ for (i = 0; i < MAX_INSCRIPTIONS; i++)
+ {
+ inscription_info[i].know = FALSE;
+ }
+
+ /* Wipe the known traps list */
+ for (i = 0; i < max_t_idx; i++)
+ {
+ t_info[i].known = 0;
+ t_info[i].ident = FALSE;
+ }
+
+ /* Reset wild_mode to FALSE */
+ p_ptr->wild_mode = FALSE;
+ p_ptr->old_wild_mode = FALSE;
+
+ /* Initialize allow_one_death */
+ p_ptr->allow_one_death = 0;
+
+ p_ptr->loan = p_ptr->loan_time = 0;
+
+ /* Wipe the power list */
+ for (i = 0; i < POWER_MAX_INIT; i++)
+ {
+ p_ptr->powers_mod[i] = 0;
+ }
+
+ /* No companions killed */
+ p_ptr->companion_killed = 0;
+}
+
+
+/* Create an object */
+void outfit_obj(int tv, int sv, int pval, int dd, int ds)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+ q_ptr->pval = 0;
+ q_ptr->pval2 = 0;
+
+ /* Hack -- Give the player an object */
+ object_prep(q_ptr, lookup_kind(tv, sv));
+
+ if (pval)
+ q_ptr->pval = pval;
+
+ /* Merchants get a chest which is currently empty */
+#if 0 /* DGDGDGDG -- use a skill */
+ if ((tv == TV_CHEST) && (cp_ptr->magic_key == MKEY_TELEKINESIS))
+ {
+ /* Put items into the chest */
+ q_ptr->pval = -5;
+
+ /* Set the number of items in the chest */
+ q_ptr->pval2 = 6;
+ }
+#endif
+ /* These objects are "storebought" */
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->number = damroll(dd, ds);
+
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+}
+
+
+/*
+ * Init players with some belongings
+ *
+ * Having an item makes the player "aware" of its purpose.
+ */
+static void player_outfit(void)
+{
+ int i;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+
+ /*
+ * Get an adventurer guide describing a bit of the
+ * wilderness(useless for vanilla town)
+ */
+ if (!vanilla_town)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player an adventurer guide */
+ object_prep(q_ptr, lookup_kind(TV_PARCHMENT, 20));
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ process_hooks(HOOK_BIRTH_OBJECTS, "()");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ {
+ /* Hack -- Give the player some food */
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_RATION));
+ q_ptr->number = (byte)rand_range(3, 7);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ {
+ /* Hack -- Give the player some torches */
+ object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH));
+ q_ptr->number = (byte)rand_range(3, 7);
+ q_ptr->timeout = rand_range(3, 7) * 500;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Rogues have a better knowledge of traps */
+ if (has_ability(AB_TRAPPING))
+ {
+ t_info[TRAP_OF_DAGGER_I].known = randint(50) + 50;
+ t_info[TRAP_OF_POISON_NEEDLE].known = randint(50) + 50;
+ t_info[TRAP_OF_FIRE_BOLT].known = randint(50) + 50;
+ t_info[TRAP_OF_DAGGER_I].ident = TRUE;
+ t_info[TRAP_OF_POISON_NEEDLE].ident = TRUE;
+ t_info[TRAP_OF_FIRE_BOLT].ident = TRUE;
+
+ /* Hack -- Give the player a some ammo for the traps */
+ object_prep(q_ptr, lookup_kind(TV_SHOT, SV_AMMO_NORMAL));
+ q_ptr->number = (byte)rand_range(5, 15);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* These objects are "storebought" */
+ q_ptr->ident |= IDENT_MENTAL;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Hack -- Give the player some useful objects */
+ for (i = 0; i < rp_ptr->obj_num; i++)
+ outfit_obj(rp_ptr->obj_tval[i], rp_ptr->obj_sval[i], rp_ptr->obj_pval[i], rp_ptr->obj_dd[i], rp_ptr->obj_ds[i]);
+ for (i = 0; i < rmp_ptr->obj_num; i++)
+ outfit_obj(rmp_ptr->obj_tval[i], rmp_ptr->obj_sval[i], rmp_ptr->obj_pval[i], rmp_ptr->obj_dd[i], rmp_ptr->obj_ds[i]);
+ for (i = 0; i < cp_ptr->obj_num; i++)
+ outfit_obj(cp_ptr->obj_tval[i], cp_ptr->obj_sval[i], cp_ptr->obj_pval[i], cp_ptr->obj_dd[i], cp_ptr->obj_ds[i]);
+ for (i = 0; i < cp_ptr->spec[p_ptr->pspec].obj_num; i++)
+ outfit_obj(cp_ptr->spec[p_ptr->pspec].obj_tval[i], cp_ptr->spec[p_ptr->pspec].obj_sval[i], cp_ptr->spec[p_ptr->pspec].obj_pval[i], cp_ptr->spec[p_ptr->pspec].obj_dd[i], cp_ptr->spec[p_ptr->pspec].obj_ds[i]);
+}
+
+
+/* Possible number(and layout) or random quests */
+#define MAX_RANDOM_QUESTS_TYPES ((8 * 3) + (8 * 1))
+int random_quests_types[MAX_RANDOM_QUESTS_TYPES] =
+{
+ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */
+ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */
+ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */
+ 20, 13, 15, 16, 9, 17, 18, 8, /* Hero Sword Quest */
+};
+
+/* Enforce OoD monsters until this level */
+#define RQ_LEVEL_CAP 49
+
+static void gen_random_quests(int n)
+{
+ int step, lvl, i, k;
+ int old_type = dungeon_type;
+
+ /* Factor dlev value by 1000 to keep precision */
+ step = (98 * 1000) / n;
+
+ lvl = step / 2;
+
+ quest[QUEST_RANDOM].status = QUEST_STATUS_TAKEN;
+
+ for (i = 0; i < n; i++)
+ {
+ monster_race *r_ptr = &r_info[2];
+
+ int rl = (lvl / 1000) + 1;
+
+ int min_level;
+
+ int tries = 5000;
+
+ random_quest *q_ptr = &random_quests[rl];
+
+ int j;
+
+ /* Find the appropriate dungeon */
+ for (j = 0; j < max_d_idx; j++)
+ {
+ dungeon_info_type *d_ptr = &d_info[j];
+
+ if (!(d_ptr->flags1 & DF1_PRINCIPAL)) continue;
+
+ if ((d_ptr->mindepth <= rl) && (rl <= d_ptr->maxdepth))
+ {
+ dungeon_type = j;
+ break;
+ }
+ }
+
+ q_ptr->type = random_quests_types[rand_int(MAX_RANDOM_QUESTS_TYPES)];
+
+ /* XXX XXX XXX Try until valid choice is found */
+ while (tries)
+ {
+ bool ok;
+
+ tries--;
+
+ /* Random monster 5 - 10 levels out of depth */
+ q_ptr->r_idx = get_mon_num(rl + 4 + randint(6));
+
+ if (!q_ptr->r_idx) continue;
+
+ r_ptr = &r_info[q_ptr->r_idx];
+
+ /* Accept only monsters that can be generated */
+ if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue;
+ if (r_ptr->flags9 & RF9_NEVER_GENE) continue;
+
+ /* Accept only monsters that are not breeders */
+ if (r_ptr->flags4 & RF4_MULTIPLY) continue;
+
+ /* Forbid joke monsters */
+ if (r_ptr->flags8 & RF8_JOKEANGBAND) continue;
+
+ /* Accept only monsters that are not friends */
+ if (r_ptr->flags7 & RF7_PET) continue;
+
+ /* Refuse nazguls */
+ if (r_ptr->flags7 & RF7_NAZGUL) continue;
+
+ /* Accept only monsters that are not good */
+ if (r_ptr->flags3 & RF3_GOOD) continue;
+
+ /* Assume no explosion attacks */
+ ok = TRUE;
+
+ /* Reject monsters with exploding attacks */
+ for (k = 0; k < 4; k++)
+ {
+ if (r_ptr->blow[k].method == RBM_EXPLODE) ok = FALSE;
+ }
+ if (!ok) continue;
+
+ /* No mutliple uniques */
+ if ((r_ptr->flags1 & RF1_UNIQUE) &&
+ ((q_ptr->type != 1) || (r_ptr->max_num == -1))) continue;
+
+ /* No single non uniques */
+ if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (q_ptr->type == 1)) continue;
+
+ /* Level restriction */
+ min_level = (rl > RQ_LEVEL_CAP) ? RQ_LEVEL_CAP : rl;
+
+ /* Accept monsters matching the level restriction */
+ if (r_ptr->level > min_level) break;
+ }
+
+ /* Arg could not find anything ??? */
+ if (!tries)
+ {
+ if (wizard) message_add(MESSAGE_MSG, format("Could not find quest monster on lvl %d", rl), TERM_RED);
+ q_ptr->type = 0;
+ }
+ else
+ {
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ r_ptr->max_num = -1;
+ }
+
+ q_ptr->done = FALSE;
+
+ if (wizard) message_add(MESSAGE_MSG,
+ format("Quest for %d on lvl %d",
+ q_ptr->r_idx, rl), TERM_RED);
+ }
+
+ lvl += step;
+ }
+
+ dungeon_type = old_type;
+}
+
+int dump_classes(s16b *classes, int sel, u32b *restrictions)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, c_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ while (classes[n] != -1)
+ {
+ cptr mod = "";
+ char p2 = ')', p1 = ' ';
+
+ /* Analyze */
+ p_ptr->pclass = classes[n];
+ cp_ptr = &class_info[p_ptr->pclass];
+ str = cp_ptr->title + c_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s%s", p1,
+ (n <= 25) ? I2A(n) : I2D(n - 26), p2, str, mod);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, c_head->text_size, "%s%s", cp_ptr->desc + c_text,
+ cp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (!(restrictions[classes[n] / 32] & BIT(classes[n])) ||
+ cp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ else
+ {
+ if (!(restrictions[classes[n] / 32] & BIT(classes[n])) ||
+ cp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ n++;
+ }
+
+ C_FREE(desc, c_head->text_size, char);
+
+ return (n);
+}
+
+int dump_specs(int sel)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, c_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ for (n = 0; n < MAX_SPEC; n++)
+ {
+ char p2 = ')', p1 = ' ';
+
+ /* Found the last one ? */
+ if (!class_info[p_ptr->pclass].spec[n].title) break;
+
+ /* Analyze */
+ p_ptr->pspec = n;
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+ str = spp_ptr->title + c_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, c_head->text_size, "%s%s", spp_ptr->desc + c_text,
+ spp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (spp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ else
+ {
+ if (spp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ else
+ put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4));
+ }
+ }
+
+ C_FREE(desc, c_head->text_size, char);
+
+ return (n);
+}
+
+int dump_races(int sel)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, rp_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ for (n = 0; n < max_rp_idx; n++)
+ {
+ char p2 = ')', p1 = ' ';
+
+ /* Analyze */
+ p_ptr->prace = n;
+ rp_ptr = &race_info[p_ptr->prace];
+ str = rp_ptr->title + rp_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, rp_head->text_size, "%s%s", rp_ptr->desc + rp_text,
+ rp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (rp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ else
+ {
+ if (rp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ }
+
+ C_FREE(desc, rp_head->text_size, char);
+
+ return (n);
+}
+
+
+int dump_rmods(int sel, int *racem, int max)
+{
+ int n = 0;
+
+ char buf[80];
+ char *desc;
+
+ cptr str;
+
+ C_MAKE(desc, rmp_head->text_size, char);
+
+ /* Clean up */
+ clear_from(12);
+
+ /* Dump races */
+ for (n = 0; n < max; n++)
+ {
+ char p2 = ')', p1 = ' ';
+
+ /* Analyze */
+ p_ptr->pracem = racem[n];
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+ str = rmp_ptr->title + rmp_name;
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ if (racem[n])
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str);
+ else
+ strnfmt(buf, 80, "%c%c%c Classical", p1, I2A(n), p2);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ strnfmt(desc, rmp_head->text_size, "%s%s", rmp_ptr->desc + rmp_text,
+ rmp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : "");
+ print_desc(desc);
+
+ if (rmp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ else
+ {
+ if (rmp_ptr->flags1 & PR1_EXPERIMENTAL)
+ c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ else
+ put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5));
+ }
+ }
+
+ C_FREE(desc, rmp_head->text_size, char);
+
+ return (n);
+}
+
+int dump_gods(int sel, int *choice, int max)
+{
+ int i, j;
+ char buf[80];
+ cptr str;
+
+ /* Clean up */
+ clear_from(12);
+
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ "You can choose to worship a god, some class must start with a god.");
+
+ for (i = 0; i < max; i++)
+ {
+ char p2 = ')', p1 = ' ';
+ int n = choice[i];
+ deity_type *g_ptr = &deity_info[0];
+
+ if (!n) str = "No God";
+ else
+ {
+ g_ptr = &deity_info[n];
+ str = g_ptr->name;
+ }
+
+ if (sel == i)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(i), p2, str);
+
+ /* Print some more info */
+ if (sel == i)
+ {
+ if (n)
+ {
+ /* Display the first four lines of the god description */
+ for (j = 0; j < 4; j++)
+ if (strcmp(g_ptr->desc[j], ""))
+ print_desc_aux(g_ptr->desc[j], 12 + j, 1);
+ }
+ else print_desc("You can begin as an atheist and still convert to a god later.");
+
+ c_put_str(TERM_L_BLUE, buf, 20 + (i / 4), 1 + 20 * (i % 4));
+ }
+ else
+ {
+ put_str(buf, 20 + (i / 4), 1 + 20 * (i % 4));
+ }
+ }
+
+ return (max);
+}
+
+
+/* Ask questions */
+static bool do_quick_start = FALSE;
+
+static bool player_birth_aux_ask()
+{
+ int i, k, n, v, sel;
+
+ s32b tmp;
+
+ int racem[100], max_racem = 0;
+
+ u32b restrictions[2];
+
+ cptr str;
+
+ char c;
+
+ char p2 = ')';
+
+ char buf[200];
+ char inp[200];
+
+ s16b *class_types;
+
+ s32b allow_quest;
+
+ /*** Intro ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Title everything */
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class :", 5, 1);
+
+ /* Dump the default name */
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+
+
+ /*** Instructions ***/
+
+ /* Display some helpful information */
+ Term_putstr(5, 8, -1, TERM_WHITE,
+ "Please answer the following questions. Most of the questions");
+ Term_putstr(5, 9, -1, TERM_WHITE,
+ "display a set of standard answers, and many will also accept");
+ Term_putstr(5, 10, -1, TERM_WHITE,
+ "some special responses, including 'Q' to quit, 'S' to restart,");
+ Term_putstr(5, 11, -1, TERM_WHITE,
+ "and '?' for help. Note that 'Q' and 'S' must be capitalized.");
+
+
+ /*** Quick Start ***/
+
+ if (previous_char.quick_ok)
+ {
+ /* Extra info */
+ Term_putstr(1, 15, -1, TERM_WHITE,
+ "Do you want to use the quick start function(same character as your last one).");
+
+ /* Choose */
+ while (1)
+ {
+ put_str("Use quick start (y/n)?", 20, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ else if (c == 'S') return (FALSE);
+ else if ((c == 'y') || (c == 'Y'))
+ {
+ do_quick_start = TRUE;
+ break;
+ }
+ else
+ {
+ do_quick_start = FALSE;
+ break;
+ }
+ }
+ }
+
+ /* Clean up */
+ clear_from(15);
+
+ /*** Player sex ***/
+
+ if (do_quick_start)
+ {
+ k = previous_char.sex;
+ }
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 15, -1, TERM_WHITE,
+ "Your 'sex' does not have any significant gameplay effects.");
+
+ /* Prompt for "Sex" */
+ for (n = 0; n < MAX_SEXES; n++)
+ {
+ /* Analyze */
+ p_ptr->psex = n;
+ sp_ptr = &sex_info[p_ptr->psex];
+ str = sp_ptr->title;
+
+ /* Display */
+ strnfmt(buf, 200, "%c%c %s", I2A(n), p2, str);
+ put_str(buf, 21 + (n / 5), 2 + 15 * (n % 5));
+ }
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a sex (%c-%c), * for random, = for options: ", I2A(0), I2A(n - 1));
+ put_str(buf, 20, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = rand_int(MAX_SEXES);
+ break;
+ }
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') do_cmd_help();
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else bell();
+ }
+ }
+
+ /* Set sex */
+ p_ptr->psex = k;
+ sp_ptr = &sex_info[p_ptr->psex];
+ str = sp_ptr->title;
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, str, 3, 9);
+
+ /* Clean up */
+ clear_from(15);
+
+
+ /*** Player race ***/
+
+ if (do_quick_start)
+ {
+ k = previous_char.race;
+ }
+ else
+ {
+ /* Only one choice = instant choice */
+ if (max_rp_idx == 1)
+ k = 0;
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 16, -1, TERM_WHITE,
+ "Your 'race' determines various intrinsic factors and bonuses.");
+ hack_corruption = FALSE;
+
+ /* Dump races */
+ sel = 0;
+ n = dump_races(sel);
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a race (%c-%c), * for a random choice, = for options, 8/2/4/6 for movement: ",
+ I2A(0), I2A(max_rp_idx - 1));
+ put_str(buf, 17, 2);
+
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = rand_int(max_rp_idx);
+ break;
+ }
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'race', '%s')", race_info[sel].title + rp_name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 5;
+ if (sel >= n) sel %= 5;
+ dump_races(sel);
+ }
+ else if (c == '8')
+ {
+ sel -= 5;
+ if (sel < 0) sel = n - 1 -( ( -sel) % 5);
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ dump_races(sel);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_races(sel);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_races(sel);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+ }
+ /* Set race */
+ p_ptr->prace = k;
+ rp_ptr = &race_info[p_ptr->prace];
+ str = rp_ptr->title + rp_name;
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, str, 4, 9);
+
+ /* Get a random name */
+ if (!do_quick_start) create_random_name(p_ptr->prace, player_name);
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+
+ /* Clean up */
+ clear_from(12);
+
+
+ /*** Player race mod ***/
+ if (do_quick_start)
+ {
+ k = previous_char.rmod;
+ p_ptr->pracem = k;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+ }
+ else
+ {
+ /* Only one choice = instant choice */
+ if (max_rmp_idx == 1)
+ k = 0;
+ else
+ {
+ for (n = 0; n < 100; n++) racem[n] = 0;
+
+ max_racem = 0;
+ for (n = 0; n < max_rmp_idx; n++)
+ {
+ /* Analyze */
+ p_ptr->pracem = n;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+
+ /* Must be an ok choice */
+ if (!(BIT(p_ptr->prace) & rmp_ptr->choice[p_ptr->prace / 32])) continue;
+
+ /* Ok thats a possibility */
+ racem[max_racem++] = n;
+ }
+
+ /* Ah ! nothing found, lets use the default */
+ if (!max_racem) p_ptr->pracem = 0;
+ /* Only one ? use it */
+ else if (max_racem == 1) p_ptr->pracem = racem[0];
+ /* We got to ask the player */
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 15, -1, TERM_WHITE,
+ "Your 'race modifier' determines various intrinsic factors and bonuses.");
+
+ /* Dump races */
+ sel = 0;
+ n = dump_rmods(sel, racem, max_racem);
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a race modifier (%c-%c), * for a random choice, = for options: ",
+ I2A(0), I2A(max_racem - 1));
+ put_str(buf, 17, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ do
+ {
+ k = rand_int(max_racem);
+ }
+ while (!(BIT(racem[k]) & rmp_ptr->choice[racem[k] / 32]));
+ break;
+ }
+ else if (c == '?') exec_lua(format("ingame_help('select_context', 'subrace', '%s')", race_mod_info[racem[sel]].title + rmp_name));
+
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < max_racem) &&
+ (BIT(p_ptr->prace) & race_mod_info[racem[k]].choice[p_ptr->prace / 32])) break;
+
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 5;
+ if (sel >= n) sel = sel - n + 1;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '8')
+ {
+ sel -= 5;
+ if (sel < 0) sel = n - 1 + sel;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_rmods(sel, racem, max_racem);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+
+ /* Set race */
+ p_ptr->pracem = racem[k];
+ }
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, get_player_race_name(p_ptr->prace, p_ptr->pracem), 4, 9);
+ }
+ }
+
+ /* Clean up */
+ clear_from(12);
+
+
+ /*** Player class ***/
+ if (do_quick_start)
+ {
+ k = previous_char.pclass;
+ p_ptr->pclass = k;
+ cp_ptr = &class_info[p_ptr->pclass];
+ k = previous_char.spec;
+ p_ptr->pspec = k;
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+ }
+ else
+ {
+ int z;
+
+ for (z = 0; z < 2; z++)
+ restrictions[z] = (rp_ptr->choice[z] | rmp_ptr->pclass[z]) & (~rmp_ptr->mclass[z]);
+
+ if (max_mc_idx > 1)
+ {
+ /* Extra info */
+ Term_putstr(5, 13, -1, TERM_WHITE,
+ "Your 'class' determines various intrinsic abilities and bonuses.");
+
+ /* Get a class type */
+ for (i = 0; i < max_mc_idx; i++)
+ c_put_str(meta_class_info[i].color, format("%c) %s", I2A(i), meta_class_info[i].name), 16 + i, 2);
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class type (a-%c), * for random, = for options: ", I2A(max_mc_idx - 1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = rand_int(max_mc_idx);
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < max_mc_idx)) break;
+ if (c == '?') do_cmd_help();
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else bell();
+ }
+ }
+ else
+ {
+ k = 0;
+ }
+ class_types = meta_class_info[k].classes;
+ clear_from(15);
+
+ /* Count classes */
+ n = 0;
+ while (class_types[n] != -1) n++;
+
+ /* Only one choice = instant choice */
+ if (n == 1)
+ k = 0;
+ else
+ {
+ /* Dump classes */
+ sel = 0;
+ n = dump_classes(class_types, sel, restrictions);
+
+ /* Get a class */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class (%c-%c), * for random, = for options, 8/2/4 for up/down/back: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = randint(n) - 1;
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'class', '%s')", class_info[class_types[sel]].title + c_name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel %= 4;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = n - 1 -( ( -sel) % 4);
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+
+ /* Set class */
+#ifdef RESTRICT_COMBINATIONS
+ if (!(restrictions & BIT(k)))
+ {
+ noscore |= 0x0020;
+ message_add(MESSAGE_MSG, " ", TERM_VIOLET);
+ message_add(MESSAGE_MSG, " ", TERM_VIOLET);
+ message_add(MESSAGE_MSG, " ", TERM_VIOLET);
+ message_add(MESSAGE_MSG, "***************************", TERM_VIOLET);
+ message_add(MESSAGE_MSG, "***************************", TERM_VIOLET);
+ message_add(MESSAGE_MSG, "********* Cheater *********", TERM_VIOLET);
+ message_add(MESSAGE_MSG, "***************************", TERM_VIOLET);
+ message_add(MESSAGE_MSG, "***************************", TERM_VIOLET);
+ }
+#endif
+ p_ptr->pclass = class_types[k];
+
+ /* Choose class spec */
+ clear_from(15);
+
+ /* Count choices */
+ for (n = 0; n < MAX_SPEC; n++)
+ {
+ /* Found the last one ? */
+ if (!class_info[p_ptr->pclass].spec[n].title) break;
+ }
+
+ /* Only one choice = auto choice */
+ if (n == 1)
+ k = 0;
+ else
+ {
+ /* Dump classes spec */
+ sel = 0;
+ n = dump_specs(sel);
+
+ /* Get a class */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class specialisation (%c-%c), * for random, = for options, 8/2/4/6 for up/down/left/right: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = randint(n) - 1;
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'class', '%s')", class_info[p_ptr->pclass].spec[sel].title + c_name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel = sel - n + 1;
+ dump_specs(sel);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = n - 1 + sel;
+ dump_specs(sel);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_specs(sel);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_specs(sel);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+
+ /* Set class spec */
+ p_ptr->pspec = k;
+ }
+ cp_ptr = &class_info[p_ptr->pclass];
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+ str = spp_ptr->title + c_name;
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, str, 5, 9);
+
+ /* Clean up */
+ clear_from(15);
+
+ /*** Player god ***/
+ if (do_quick_start)
+ {
+ k = previous_char.god;
+ p_ptr->pgod = k;
+ set_grace(previous_char.grace);
+ }
+ else if (PRACE_FLAG(PR1_NO_GOD))
+ {
+ p_ptr->pgod = GOD_NONE;
+ }
+ else
+ {
+ int *choice;
+ int max = 0;
+
+ C_MAKE(choice, max_gods, int);
+
+ /* Get the list of possible gods */
+ for (n = 0; n < max_gods; n++)
+ {
+ if ((cp_ptr->gods | spp_ptr->gods) & BIT(n))
+ choice[max++] = n;
+ }
+
+ if (!max)
+ {
+ p_ptr->pgod = GOD_NONE;
+ }
+ else if (max == 1)
+ {
+ p_ptr->pgod = choice[0];
+ }
+ else if (max > 1)
+ {
+ sel = 0;
+ n = dump_gods(sel, choice, max);
+
+ /* Choose */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a god (%c-%c), * for a random choice, "
+ "= for options, 8/2/4/6 for movement: ",
+ I2A(0), I2A(max - 1));
+ put_str(buf, 19, 2);
+
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S')
+ {
+ C_FREE(choice, max_gods, int);
+
+ return (FALSE);
+ }
+ if (c == '*')
+ {
+ k = choice[randint(max) - 1];
+ break;
+ }
+ k = (islower(c) ? A2I(c) : -1);
+ if ((k >= 0) && (k < max))
+ {
+ k = choice[k];
+ break;
+ }
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'god', '%s')", deity_info[choice[sel]].name));
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel %= 4;
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ if (sel < 0) sel = n - 1 -( ( -sel) % 4);
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_gods(sel, choice, max);
+ }
+ else if (c == '\r')
+ {
+ k = choice[sel];
+ break;
+ }
+ else bell();
+ }
+
+ C_FREE(choice, max_gods, int);
+
+ /* Set god */
+ p_ptr->pgod = k;
+ p_ptr->grace = 0;
+ }
+
+ /* A god that like us ? more grace ! */
+ if (PRACE_FLAGS(PR1_GOD_FRIEND))
+ {
+ set_grace(200);
+ }
+ else
+ {
+ set_grace(100);
+ }
+ }
+
+ /* Clean up */
+ clear_from(12);
+
+ if (!do_quick_start)
+ {
+ /* Clear */
+ clear_from(15);
+
+ /* */
+ if (get_check("Do you want to modify the options"))
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ }
+
+ /* Set birth options: maximize, preserve, sepcial levels and astral */
+ p_ptr->maximize = maximize;
+ p_ptr->preserve = preserve;
+ p_ptr->special = special_lvls;
+ p_ptr->astral = (PRACE_FLAG2(PR2_ASTRAL)) ? TRUE : FALSE;
+
+ /*
+ * A note by pelpel. (remove this please)
+ * Be it the new Vanilla way (adult vs. birth options) or
+ * the old one (player_type members), it would be less confusing
+ * to handle birth-only options in a uniform fashion,the above and
+ * the following:
+ * permanent_levels,
+ * ironman_rooms,
+ * joke_monsters,
+ * always_small_level, and
+ * fate_option
+ */
+
+
+ /* Set dungeon seed */
+ if (permanent_levels)
+ {
+ seed_dungeon = randint(0x10000000);
+ }
+ else
+ {
+ seed_dungeon = 0;
+ }
+
+ /* Set the recall dungeon accordingly */
+ call_lua("get_module_info", "(s)", "d", "base_dungeon", &tmp);
+ dungeon_type = tmp;
+ p_ptr->recall_dungeon = dungeon_type;
+ max_dlv[dungeon_type] = d_info[dungeon_type].mindepth;
+
+ if (p_ptr->astral)
+ {
+ s32b x, y, astral_dun;
+
+ call_lua("get_module_info", "(s)", "d", "astral_dungeon", &astral_dun);
+ dungeon_type = astral_dun;
+
+ /* Somewhere in the misty mountains */
+ call_lua("get_module_info", "(s)", "d", "astral_wild_x", &x);
+ call_lua("get_module_info", "(s)", "d", "astral_wild_y", &y);
+ p_ptr->wilderness_x = x;
+ p_ptr->wilderness_y = y;
+ }
+
+ /* Clean up */
+ clear_from(10);
+
+ /*** User enters number of quests ***/
+ /* Heino Vander Sanden and Jimmy De Laet */
+
+ call_lua("get_module_info", "(s)", "d", "rand_quest", &allow_quest);
+ if (!ironman_rooms && !permanent_levels && allow_quest)
+ {
+ if (do_quick_start)
+ {
+ v = previous_char.quests;
+ }
+ else
+ {
+ /* Extra info */
+ Term_putstr(5, 15, -1, TERM_WHITE,
+ "Select the number of optional random quests you'd like to receive.");
+ Term_putstr(5, 16, -1, TERM_WHITE,
+ "If you do not want any optional quests, enter 0.");
+
+ /* Ask the number of additional quests */
+ while (TRUE)
+ {
+ put_str(format("Number of quests? (0-%u) ",
+ MAX_RANDOM_QUEST - 1), 20, 2);
+
+ /* Get a the number of additional quest */
+ while (TRUE)
+ {
+ /* Move the cursor */
+ put_str("", 20, 27);
+
+ /* Default */
+ strcpy(inp, "20");
+
+ /* Get a response (or escape) */
+ if (!askfor_aux(inp, 2)) inp[0] = '\0';
+ if (inp[0] == '*') v = rand_int(MAX_RANDOM_QUEST);
+ else v = atoi(inp);
+
+ /* Break on valid input */
+ if ((v < MAX_RANDOM_QUEST) && ( v >= 0 )) break;
+ }
+ break;
+ }
+
+ /* Clear */
+ clear_from(15);
+ }
+ }
+ else
+ {
+ /* NO quests for ironman rooms or persistent levels, since they
+ don't work */
+ v = 0;
+ }
+
+ /* Set the quest monster hook */
+ get_mon_num_hook = monster_quest;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Generate quests */
+ for (i = 0; i < MAX_RANDOM_QUEST; i++) random_quests[i].type = 0;
+ if (v) gen_random_quests(v);
+ max_quests = v;
+
+ p_ptr->inside_quest = 0;
+
+ /* Init the plots */
+ call_lua("get_module_info", "(s)", "d", "C_quest", &allow_quest);
+ if (allow_quest)
+ {
+ plots[PLOT_MAIN] = QUEST_NECRO;
+ quest[plots[PLOT_MAIN]].status = QUEST_STATUS_TAKEN;
+
+ plots[PLOT_BREE] = QUEST_THIEVES;
+ quest[plots[PLOT_BREE]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_LORIEN] = QUEST_WOLVES;
+ quest[plots[PLOT_LORIEN]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_GONDOLIN] = QUEST_DRAGONS;
+ quest[plots[PLOT_GONDOLIN]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_MINAS] = QUEST_HAUNTED;
+ quest[plots[PLOT_MINAS]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_KHAZAD] = QUEST_EVIL;
+ quest[plots[PLOT_KHAZAD]].status = QUEST_STATUS_UNTAKEN;
+
+ plots[PLOT_OTHER] = QUEST_NULL;
+ }
+
+ quest_random_init_hook(QUEST_RANDOM);
+
+ /* Ok */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Initial stat costs (initial stats always range from 10 to 18 inclusive).
+ */
+static const int birth_stat_costs[(18-10) + 1] =
+{
+ 0, 1, 2, 4, 7, 11, 16, 22, 30
+};
+
+
+/*
+ * Helper function for 'player_birth()'.
+ *
+ * This function handles "point-based" character creation.
+ *
+ * The player selects, for each stat, a value from 10 to 18 (inclusive),
+ * each costing a certain amount of points (as above), from a pool of 48
+ * available points, to which race/class modifiers are then applied.
+ *
+ * Each unused point is converted into 100 gold pieces, with a maximum of
+ * 600 gp at birth.
+ *
+ * Taken from V 2.9.0
+ */
+static bool player_birth_aux_point(void)
+{
+ int i;
+
+ int row = 3;
+
+ int col = 42;
+
+ int stat = 0;
+
+ int stats[6];
+
+ int cost;
+
+ char ch;
+
+ char buf[80];
+
+ int mode = 0;
+
+
+ /* Initialize stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Initial stats */
+ stats[i] = 10;
+ }
+
+
+ /* Roll for base hitpoints */
+ get_extra();
+
+ /* Roll for age/height/weight */
+ get_ahw();
+
+ /* Roll for social class */
+ get_history();
+
+ /*** Generate ***/
+ process_hooks(HOOK_BIRTH, "()");
+
+ /* Hack -- get a chaos patron even if you are not a chaos warrior */
+ p_ptr->chaos_patron = (randint(MAX_PATRON)) - 1;
+
+ /* Get luck */
+ p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5);
+ p_ptr->luck_max = p_ptr->luck_base;
+
+ /* Interact */
+ while (1)
+ {
+ /* Reset cost */
+ cost = 0;
+
+ /* Process stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Variable stat maxes */
+ if (p_ptr->maximize)
+ {
+ /* Reset stats */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i];
+
+ }
+
+ /* Fixed stat maxes */
+ else
+ {
+ /* Obtain a "bonus" for "race" and "class" */
+ int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
+
+ /* Apply the racial/class bonuses */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] =
+ modify_stat_value(stats[i], bonus);
+ }
+
+ /* Total cost */
+ cost += birth_stat_costs[stats[i] - 10];
+ }
+
+ /* Restrict cost */
+ if (cost > 48)
+ {
+ /* Warning */
+ bell();
+
+ /* Reduce stat */
+ stats[stat]--;
+
+ /* Recompute costs */
+ continue;
+ }
+
+ /* Gold is inversely proportional to cost */
+ p_ptr->au = (100 * (48 - cost)) + 100;
+
+ /* Maximum of 600 gold */
+ if (p_ptr->au > 600) p_ptr->au = 600;
+
+ /* Calculate the bonuses and hitpoints */
+ p_ptr->update |= (PU_BONUS | PU_HP);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Fully healed */
+ p_ptr->chp = p_ptr->mhp;
+
+ /* Fully rested */
+ p_ptr->csp = p_ptr->msp;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Display the costs header */
+ put_str("Cost", row - 2, col + 32);
+
+ /* Display the costs */
+ for (i = 0; i < 6; i++)
+ {
+ /* Display cost */
+ strnfmt(buf, 80, "%4d", birth_stat_costs[stats[i] - 10]);
+ put_str(buf, row + (i - 1), col + 32);
+ }
+
+
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80, "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, ESC to accept.", cost);
+ prt(buf, 0, 0);
+
+ /* Place cursor just after cost of current stat */
+ Term_gotoxy(col + 36, row + stat - 1);
+
+ /* Get key */
+ ch = inkey();
+
+ /* Quit */
+ if (ch == 'Q') quit(NULL);
+
+ /* Start over */
+ if (ch == 'S') return (FALSE);
+
+ /* Done */
+ if (ch == ESCAPE) break;
+
+ /* Prev stat */
+ if (ch == '8')
+ {
+ stat = (stat + 6 - 1) % 6;
+ }
+
+ /* Next stat */
+ if (ch == '2')
+ {
+ stat = (stat + 1) % 6;
+ }
+
+ /* Decrease stat */
+ if ((ch == '4') && (stats[stat] > 10))
+ {
+ stats[stat]--;
+ }
+
+ /* Increase stat */
+ if ((ch == '6') && (stats[stat] < 18))
+ {
+ stats[stat]++;
+ }
+ }
+
+
+ /* Done */
+ return (TRUE);
+}
+
+/*
+ * Use the autoroller or not to generate a char
+ */
+static bool player_birth_aux_auto()
+{
+ int i, j, m, v;
+
+ int mode = 0;
+
+ bool flag = FALSE;
+
+ bool prev = FALSE;
+
+ char c;
+
+ char b1 = '[';
+
+ char b2 = ']';
+
+ char buf[80];
+
+ char inp[80];
+
+
+#ifdef ALLOW_AUTOROLLER
+
+ /* Initialize */
+ if (autoroll)
+ {
+ int mval[6];
+
+
+ /* Clear fields */
+ auto_round = 0L;
+ last_round = 0L;
+
+ /* Clean up */
+ clear_from(10);
+
+ /* Prompt for the minimum stats */
+ put_str("Enter minimum attribute for: ", 15, 2);
+
+ /* Output the maximum stats */
+ for (i = 0; i < 6; i++)
+ {
+ char stat_buf[15];
+
+ /* Reset the "success" counter */
+ stat_match[i] = 0;
+
+ /* Race/Class bonus */
+ j = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i];
+
+ /* Obtain the "maximal" stat */
+ m = adjust_stat(17, j, TRUE);
+
+
+ /* Save the maximum */
+ mval[i] = m;
+
+ /* Extract a textual format */
+ cnv_stat(m, stat_buf);
+
+ strnfmt(inp, 80, "(Max of %s):", stat_buf);
+
+ /* Prepare a prompt */
+ strnfmt(buf, 80, "%-5s: %-20s", stat_names[i], inp);
+
+ /* Dump the prompt */
+ put_str(buf, 16 + i, 5);
+ }
+
+ /* Input the minimum stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Get a minimum stat */
+ while (TRUE)
+ {
+ char *s;
+
+ /* Move the cursor */
+ put_str("", 16 + i, 30);
+
+ /* Default */
+ strcpy(inp, "");
+
+ /* Get a response (or escape) */
+ if (!askfor_aux(inp, 8)) inp[0] = '\0';
+
+ /* Weirdos stat display .. erm .. I mean, original stat display */
+ if (!linear_stats)
+ {
+ /* Hack -- add a fake slash */
+ strcat(inp, "/");
+
+ /* Hack -- look for the "slash" */
+ s = strchr(inp, '/');
+
+ /* Hack -- Nuke the slash */
+ *s++ = '\0';
+
+ /* Hack -- Extract an input */
+ v = atoi(inp) + atoi(s);
+ }
+ else
+ {
+ int z = atoi(inp);
+
+ if (z <= 18)
+ v = z;
+ else
+ {
+ int extra = z - 18;
+ v = 18 + (extra * 10);
+ }
+ }
+
+ /* Break on valid input */
+ if (v <= mval[i]) break;
+ }
+
+ /* Save the minimum stat */
+ stat_limit[i] = (v > 0) ? v : 0;
+ }
+ }
+
+#endif /* ALLOW_AUTOROLLER */
+
+ /* Roll */
+ while (TRUE)
+ {
+ /* Feedback */
+ if (autoroll)
+ {
+ Term_clear();
+
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class:", 5, 1);
+
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+ c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9);
+ strnfmt(buf, 80, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem));
+ c_put_str(TERM_L_BLUE, buf, 4, 9);
+ c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9);
+
+ /* Label stats */
+ put_str("STR:", 2 + A_STR, 61);
+ put_str("INT:", 2 + A_INT, 61);
+ put_str("WIS:", 2 + A_WIS, 61);
+ put_str("DEX:", 2 + A_DEX, 61);
+ put_str("CON:", 2 + A_CON, 61);
+ put_str("CHR:", 2 + A_CHR, 61);
+
+ /* Note when we started */
+ last_round = auto_round;
+
+ /* Indicate the state */
+ put_str("(Hit ESC to abort)", 11, 61);
+
+ /* Label count */
+ put_str("Round:", 9, 61);
+ }
+
+ /* Otherwise just get a character */
+ else
+ {
+ /* Get a new character */
+ get_stats();
+ }
+
+ /* Auto-roll */
+ while (autoroll)
+ {
+ bool accept = TRUE;
+
+ /* Get a new character */
+ get_stats();
+
+ /* Advance the round */
+ auto_round++;
+
+ /* Hack -- Prevent overflow */
+ if (auto_round >= 1000000L) break;
+
+ /* Check and count acceptable stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* This stat is okay */
+ if (stat_use[i] >= stat_limit[i])
+ {
+ stat_match[i]++;
+ }
+
+ /* This stat is not okay */
+ else
+ {
+ accept = FALSE;
+ }
+ }
+
+ /* Break if "happy" */
+ if (accept) break;
+
+ /* Take note every 25 rolls */
+ flag = (!(auto_round % AUTOROLLER_STEP));
+
+ /* Update display occasionally */
+ if (flag || (auto_round < last_round + 100))
+ {
+ /* Dump data */
+ birth_put_stats();
+
+ /* Dump round */
+ put_str(format("%6ld", auto_round), 9, 73);
+
+ /* Make sure they see everything */
+ Term_fresh();
+
+#ifndef USE_FAST_AUTOROLLER
+
+ /* Delay 1/10 second */
+ if (fast_autoroller && flag) Term_xtra(TERM_XTRA_DELAY, 100);
+
+#endif
+ /* Do not wait for a key */
+ inkey_scan = TRUE;
+
+ /* Check for a keypress */
+ if (inkey()) break;
+ }
+ }
+
+ /* Flush input */
+ flush();
+
+
+ /*** Display ***/
+
+ /* Mode */
+ mode = 0;
+
+ /* Roll for base hitpoints */
+ get_extra();
+
+ /* Roll for age/height/weight */
+ get_ahw();
+
+ /* Roll for social class */
+ get_history();
+
+ /* Roll for gold */
+ get_money();
+
+ /*** Generate ***/
+ process_hooks(HOOK_BIRTH, "()");
+
+ /* Hack -- get a chaos patron even if you are not a chaos warrior */
+ p_ptr->chaos_patron = (randint(MAX_PATRON)) - 1;
+
+ /* Input loop */
+ while (TRUE)
+ {
+ /* Calculate the bonuses and hitpoints */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Fully healed */
+ p_ptr->chp = p_ptr->mhp;
+
+ /* Fully rested */
+ p_ptr->csp = p_ptr->msp;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Prepare a prompt (must squeeze everything in) */
+ Term_gotoxy(2, 23);
+ Term_addch(TERM_WHITE, b1);
+ Term_addstr( -1, TERM_WHITE, "'r' to reroll");
+ if (prev) Term_addstr( -1, TERM_WHITE, ", 'p' for prev");
+ if (mode) Term_addstr( -1, TERM_WHITE, ", 'h' for Misc.");
+ else Term_addstr( -1, TERM_WHITE, ", 'h' for History");
+ Term_addstr( -1, TERM_WHITE, ", or ESC to accept");
+ Term_addch(TERM_WHITE, b2);
+
+ /* Prompt and get a command */
+ c = inkey();
+
+ /* Quit */
+ if (c == 'Q') quit(NULL);
+
+ /* Start over */
+ if (c == 'S') return (FALSE);
+
+ /* Escape accepts the roll */
+ if (c == ESCAPE) break;
+
+ /* Reroll this character */
+ if ((c == ' ') || (c == 'r')) break;
+
+ /* Previous character */
+ if (prev && (c == 'p'))
+ {
+ load_prev_data(TRUE);
+ continue;
+ }
+
+ /* Toggle the display */
+ if ((c == 'H') || (c == 'h'))
+ {
+ mode = ((mode != 0) ? 0 : 1);
+ continue;
+ }
+
+ /* Help */
+ if (c == '?')
+ {
+ do_cmd_help();
+ continue;
+ }
+
+ /* Warning */
+ bell();
+ }
+
+ /* Are we done? */
+ if (c == ESCAPE) break;
+
+ /* Save this for the "previous" character */
+ save_prev_data();
+
+ /* Note that a previous roll exists */
+ prev = TRUE;
+ }
+
+ /* Clear prompt */
+ clear_from(23);
+
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for 'player_birth()'
+ *
+ * The delay may be reduced, but is recommended to keep players
+ * from continuously rolling up characters, which can be VERY
+ * expensive CPU wise. And it cuts down on player stupidity.
+ */
+static bool player_birth_aux()
+{
+ char c;
+
+ int i, j;
+
+ int y = 0, x = 0;
+
+ char old_history[4][60];
+
+ /* Ask */
+ if (!player_birth_aux_ask()) return (FALSE);
+
+ for (i = 1; i < max_s_idx; i++)
+ s_info[i].dev = FALSE;
+ for (i = 1; i < max_s_idx; i++)
+ {
+ s32b value = 0, mod = 0;
+
+ compute_skills(&value, &mod, i);
+
+ init_skill(value, mod, i);
+
+ /* Develop only revelant branches */
+ if (s_info[i].value || s_info[i].mod)
+ {
+ int z = s_info[i].father;
+
+ while (z != -1)
+ {
+ s_info[z].dev = TRUE;
+ z = s_info[z].father;
+ if (z == 0)
+ break;
+ }
+ }
+ }
+
+ if (do_quick_start)
+ {
+ load_prev_data(FALSE);
+
+ /* Roll for base hitpoints */
+ get_extra();
+
+ /* Calculate the bonuses and hitpoints */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Fully healed */
+ p_ptr->chp = p_ptr->mhp;
+
+ /* Fully rested */
+ p_ptr->csp = p_ptr->msp;
+ }
+ else
+ {
+ /* Point based */
+ if (point_based)
+ {
+ if (!player_birth_aux_point()) return FALSE;
+ }
+ /* Auto-roll */
+ else
+ {
+ if (!player_birth_aux_auto()) return FALSE;
+ }
+
+ /* Edit character background */
+ for (i = 0; i < 4; i++)
+ {
+ strnfmt(old_history[i], 60, "%s", history[i]);
+ }
+ /* Turn 0 to space */
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; history[i][j]; j++) /* loop */;
+
+ for (; j < 59; j++) history[i][j] = ' ';
+ }
+ display_player(1);
+ c_put_str(TERM_L_GREEN, "(Character Background - Edit Mode)", 15, 20);
+ while (TRUE)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ put_str(history[i], i + 16, 10);
+ }
+ c_put_str(TERM_L_BLUE, format("%c", history[y][x]), y + 16, x + 10);
+
+ /* Place cursor just after cost of current stat */
+ Term_gotoxy(x + 10, y + 16);
+
+ c = inkey();
+
+ if (c == '8')
+ {
+ y--;
+ if (y < 0) y = 3;
+ }
+ else if (c == '2')
+ {
+ y++;
+ if (y > 3) y = 0;
+ }
+ else if (c == '6')
+ {
+ x++;
+ if (x > 59) x = 0;
+ }
+ else if (c == '4')
+ {
+ x--;
+ if (x < 0) x = 59;
+ }
+ else if (c == '\r')
+ {
+ break;
+ }
+ else if (c == ESCAPE)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ strnfmt(history[i], 60, "%s", old_history[i]);
+ put_str(history[i], i + 16, 10);
+ }
+ break;
+ }
+ else
+ {
+ history[y][x++] = c;
+ if (x > 58)
+ {
+ x = 0;
+ y++;
+ if (y > 3) y = 0;
+ }
+ }
+ }
+
+
+ /*** Finish up ***/
+
+ /* Get a name, recolor it, prepare savefile */
+
+ get_name();
+
+
+ /* Prompt for it */
+ prt("['Q' to suicide, 'S' to start over, or ESC to continue]", 23, 10);
+
+ /* Get a key */
+ c = inkey();
+
+ /* Quit */
+ if (c == 'Q') quit(NULL);
+
+ /* Start over */
+ if (c == 'S') return (FALSE);
+ }
+
+ /* Save this for the next character */
+ previous_char.quick_ok = TRUE;
+ save_prev_data();
+
+ /* Accept */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for validate_bg().
+ */
+static void validate_bg_aux(int chart, bool chart_checked[], char *buf)
+{
+ char *s;
+
+ int i;
+
+
+ /* Assume the chart does not exist */
+ bool chart_exists = FALSE;
+
+ /* Assume the chart is not complete */
+ bool chart_complete = FALSE;
+
+ int bg_max = max_bg_idx;
+
+ /* No chart */
+ if (!chart) return;
+
+ /* Already saw this chart */
+ if (chart_checked[chart]) return;
+
+ /* Build a debug message */
+ s = buf + strlen(buf);
+
+ /* XXX XXX XXX */
+ (void) strnfmt(s, -1, "%d --> ", chart);
+
+ /* Check each chart */
+ for (i = 0; i < bg_max; i++)
+ {
+ /* Require same chart */
+ if (bg[i].chart != chart) continue;
+
+ /* The chart exists */
+ chart_exists = TRUE;
+
+ /* Validate the "next" chart recursively */
+ validate_bg_aux(bg[i].next, chart_checked, buf);
+
+ /* Require a terminator */
+ if (bg[i].roll != 100) continue;
+
+ /* The chart is complete */
+ chart_complete = TRUE;
+ }
+
+ /* Failed: The chart does not exist */
+ if (!chart_exists)
+ {
+ quit_fmt("birth.c: bg[] chart %d does not exist\n%s", chart, buf);
+ }
+
+ /* Failed: The chart is not complete */
+ if (!chart_complete)
+ {
+ quit_fmt("birth.c: bg[] chart %d is not complete", chart);
+ }
+
+ /* Remember we saw this chart */
+ chart_checked[chart] = TRUE;
+
+ /* Build a debug message */
+ *s = 0;
+}
+
+
+/*
+ * Verify that the bg[] table is valid.
+ */
+static void validate_bg(void)
+{
+ int i, race;
+
+ bool chart_checked[512];
+
+ char buf[1024];
+
+
+ for (i = 0; i < 512; i++) chart_checked[i] = FALSE;
+
+ /* Check each race */
+ for (race = 0; race < max_rp_idx; race++)
+ {
+ /* Get the first chart for this race */
+ int chart = race_info[race].chart;
+
+ (void) strcpy(buf, "");
+
+ /* Validate the chart recursively */
+ validate_bg_aux(chart, chart_checked, buf);
+ }
+}
+
+/*
+ * Initialize a random town
+ */
+void init_town(int t_idx, int level)
+{
+ town_type *t_ptr = &town_info[t_idx];
+
+ /* Mark it as existent */
+ t_ptr->flags |= (TOWN_REAL);
+
+ /* Mark it as not found */
+ t_ptr->flags &= ~(TOWN_KNOWN);
+
+ /* Generation seed for the town */
+ t_ptr->seed = randint(0x10000000);
+
+ /* Total hack and not even used */
+ t_ptr->numstores = 8;
+}
+
+/*
+ * Create a new character.
+ *
+ * Note that we may be called with "junk" leftover in the various
+ * fields, so we must be sure to clear them first.
+ */
+void player_birth(void)
+{
+ int i, j, rtown = TOWN_RANDOM;
+
+ /* Validate the bg[] table */
+ validate_bg();
+
+ /* Create a new character */
+ while (1)
+ {
+ /* Wipe the player */
+ player_wipe();
+
+ /* Roll up a new character */
+ if (player_birth_aux()) break;
+ }
+
+ /* Finish skills */
+ p_ptr->skill_points = 0;
+ p_ptr->skill_last_level = 1;
+
+ recalc_skills(FALSE);
+
+ /* grab level 1 abilities */
+ for (i = 0; i < max_ab_idx; i++)
+ ab_info[i].acquired = FALSE;
+ apply_level_abilities(1);
+
+ /* Complete the god */
+ i = p_ptr->pgod;
+ p_ptr->pgod = 0;
+ follow_god(i, TRUE);
+
+ /* Select the default melee type */
+ select_default_melee();
+
+ /* Make a note file if that option is set */
+ if (take_notes)
+ {
+ add_note_type(NOTE_BIRTH);
+ }
+
+ /* Note player birth in the message recall */
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, "====================", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+ message_add(MESSAGE_MSG, " ", TERM_L_BLUE);
+
+ /* Verify autoskiller */
+#if 0
+ if (validate_autoskiller(spp_ptr->skill_ideal) < 0)
+ {
+ message_add(MESSAGE_MSG, "WARNING: Bad autoskill chart", TERM_VIOLET);
+ }
+#endif
+
+ /* Hack -- outfit the player */
+ player_outfit();
+
+ /* Initialize random towns in the dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+ int num = 0, z;
+
+ d_ptr->t_num = 0;
+ for (z = 0; z < TOWN_DUNGEON; z++)
+ {
+ d_ptr->t_idx[z] = 0;
+ d_ptr->t_level[z] = 0;
+ }
+ if (!(d_ptr->flags1 & DF1_RANDOM_TOWNS)) continue;
+
+ /* Can we add a town ? */
+ while (magik(TOWN_CHANCE - (num * 10)))
+ {
+ int lev;
+
+ d_ptr->t_idx[num] = rtown;
+ rtown++;
+
+ while (TRUE)
+ {
+ int j;
+ bool ok = TRUE;
+
+ lev = rand_range(d_ptr->mindepth, d_ptr->maxdepth - 1);
+
+ /* Be sure it wasnt already used */
+ for (j = 0; j < num; j++)
+ {
+ if (d_ptr->t_level[j] == lev) ok = FALSE;
+ }
+
+ /* Ok found one */
+ if (ok) break;
+ }
+ d_ptr->t_level[num] = lev;
+
+ if (wizard) message_add(MESSAGE_MSG, format("Random dungeon town: d_idx:%d, lev:%d", i, lev), TERM_WHITE);
+
+ /* Create the town */
+ init_town(d_ptr->t_idx[num], d_ptr->t_level[num]);
+
+ num++;
+
+ /* No free slots left */
+ if (num >= TOWN_DUNGEON) break;
+ }
+
+ d_ptr->t_num = num;
+ }
+
+ /* Init the towns */
+ for (i = 1; i < max_towns; i++)
+ {
+ /* Not destroyed ! yet .. ;) */
+ town_info[i].destroyed = FALSE;
+
+ /* Ignore non-existent towns */
+ if (!(town_info[i].flags & (TOWN_REAL))) continue;
+
+ create_stores_stock(i);
+
+ /* Init the stores */
+ for (j = 0; j < max_st_idx; j++)
+ {
+ /* Initialize */
+ store_init(i, j);
+ }
+ }
+
+ /* Init wilderness seeds */
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ wild_map[j][i].seed = rand_int(0x10000000);
+ wild_map[j][i].entrance = 0;
+ wild_map[j][i].known = FALSE;
+ }
+ }
+
+ /* Select bounty monsters. */
+ select_bounties();
+}
+
+
+
+
+char savefile_module[46][80];
+char savefile_names[46][30];
+char savefile_desc[46][80];
+bool savefile_alive[46];
+int savefile_idx[46];
+
+/*
+ * Grab all the names from an index
+ */
+int load_savefile_names()
+{
+ FILE *fff;
+ char buf[1024];
+ char tmp[50];
+ char player_base_save[32];
+ int max = 0, fd;
+
+
+ /* Build the filename */
+#ifdef SAVEFILE_USE_UID
+ strnfmt(tmp, 50, "user.%d.svg", player_uid);
+#else
+ strcpy(tmp, "global.svg");
+#endif /* SAVEFILE_USE_UID */
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Read the file */
+ fff = my_fopen(buf, "r");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Failure */
+ if (!fff) return (0);
+
+
+ /* Save the current 'player_base' */
+ strncpy(player_base_save, player_base, 32);
+
+
+ /*
+ * Parse, use '@' intead of ':' as a separator because it cannot exists
+ * in savefiles
+ */
+ while (0 == my_fgets(fff, buf, 1024))
+ {
+ int i = 0, start, count;
+
+ /* Check for pre-ToME 2.1.2 file */
+ count = 0;
+ i = 0;
+ while (buf[i] && buf[i] != '\n')
+ {
+ if (buf[i] == '@')
+ ++count;
+ ++i;
+ }
+
+ /* Check module if a current svg file */
+ start = 0;
+ i = 0;
+ if (count > 1)
+ {
+ while (buf[i] != '@')
+ {
+ savefile_module[max][i - start] = buf[i];
+ i++;
+ }
+ savefile_module[max][i] = '\0';
+ i++;
+ }
+ /* Default to ToME for old files */
+ else
+ {
+ savefile_module[max][0] = 'T';
+ savefile_module[max][1] = 'o';
+ savefile_module[max][2] = 'M';
+ savefile_module[max][3] = 'E';
+ savefile_module[max][4] = '\0';
+ }
+
+ if (buf[i] == '0') savefile_alive[max] = FALSE;
+ else if (buf[i] == '1') savefile_alive[max] = TRUE;
+
+ i++;
+ start = i;
+ while (buf[i] != '@')
+ {
+ savefile_names[max][i - start] = buf[i];
+ i++;
+ }
+ savefile_names[max][i - start] = '\0';
+ i++;
+ strcpy(savefile_desc[max], buf + i);
+
+ /* Build platform-dependent savefile name */
+ strncpy(player_base, savefile_names[max], 32);
+ process_player_name(TRUE);
+
+ /* File type is 'SAVE' */
+ FILE_TYPE(FILE_TYPE_SAVE);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Try to open the savefile */
+ fd = fd_open(savefile, O_RDONLY);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Still existing ? */
+ if (fd >= 0)
+ {
+ fd_close(fd);
+ max++;
+ }
+ }
+
+ my_fclose(fff);
+
+ /* Restore the values of 'player_base' and 'savefile' */
+ strncpy(player_base, player_base_save, 32);
+ process_player_name(TRUE);
+
+ return (max);
+}
+
+
+/*
+ * Save all the names from an index
+ */
+void save_savefile_names()
+{
+ FILE *fff;
+ char buf[1024];
+ char tmp[50];
+ int max = load_savefile_names(), i;
+
+
+ /* Build the filename */
+#ifdef SAVEFILE_USE_UID
+ strnfmt(tmp, 50, "user.%d.svg", player_uid);
+#else
+ strcpy(tmp, "global.svg");
+#endif /* SAVEFILE_USE_UID */
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Read the file */
+ fff = my_fopen(buf, "w");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Failure */
+ if (!fff) return;
+
+ /*
+ * Save, use '@' intead of ':' as a separator because it cannot exists
+ * in savefiles
+ */
+ fprintf(fff, "%s@%c%s@%s, the %s %s is %s\n", game_module,
+ (death) ? '0' : '1', player_base, player_name,
+ get_player_race_name(p_ptr->prace, p_ptr->pracem),
+ spp_ptr->title + c_name,
+ (!death) ? "alive" : "dead");
+
+ for (i = 0; i < max; i++)
+ {
+ if (!strcmp(savefile_names[i], player_base)) continue;
+ fprintf(fff, "%s@%c%s@%s\n", savefile_module[i],
+ (savefile_alive[i]) ? '1' : '0', savefile_names[i], savefile_desc[i]);
+ }
+
+ my_fclose(fff);
+}
+
+
+static void dump_savefiles(int sel, int max)
+{
+ int i;
+
+ char buf[40], pre = ' ', post = ')';
+
+ char ind;
+
+
+ for (i = 0; i < max; i++)
+ {
+ ind = I2A(i % 26);
+ if (i >= 26) ind = toupper(ind);
+
+ if (sel == i)
+ {
+ pre = '[';
+ post = ']';
+ }
+ else
+ {
+ pre = ' ';
+ post = ')';
+ }
+
+ if (i == 0) strnfmt(buf, 40, "%c%c%c New Character", pre, ind, post);
+ else if (i == 1) strnfmt(buf, 40, "%c%c%c Load Savefile", pre, ind, post);
+ else strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, savefile_names[savefile_idx[i - 2]]);
+
+ if (sel == i)
+ {
+ if (i >= 2)
+ {
+ if (savefile_alive[i - 2]) c_put_str(TERM_L_GREEN, savefile_desc[savefile_idx[i - 2]], 5, 0);
+ else c_put_str(TERM_L_RED, savefile_desc[savefile_idx[i - 2]], 5, 0);
+ }
+ else if (i == 1) c_put_str(TERM_YELLOW, "Load an existing savefile that is not in the list", 5, 0);
+ else c_put_str(TERM_YELLOW, "Create a new character", 5, 0);
+ c_put_str(TERM_L_BLUE, buf, 6 + (i / 4), 20 * (i % 4));
+ }
+ else
+ put_str(buf, 6 + (i / 4), 20 * (i % 4));
+ }
+}
+
+
+/* Asks for new game or load game */
+bool no_begin_screen = FALSE;
+
+bool begin_screen()
+{
+ int m, k, sel, max;
+
+savefile_try_again:
+ sel = 0;
+
+ /* Hack */
+ use_color = TRUE;
+
+ /* Grab the savefiles */
+ max = load_savefile_names();
+
+ /* Get only the usable savefiles */
+ for (k = 0, m = 0; k < max; k++)
+ {
+ s32b can_use;
+
+ call_lua("module_savefile_loadable", "(s)", "d", savefile_module[k], &can_use);
+ if (can_use)
+ {
+ savefile_idx[m++] = k;
+ }
+ }
+ max = m + 2;
+ if (max > 2) sel = 2;
+
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Let the user choose */
+ c_put_str(TERM_YELLOW, format("Welcome to %s! To play you will need a character.", game_module), 1, 10);
+ put_str("Press 8/2/4/6 to move, Return to select, Backspace to delete a savefile.", 3, 3);
+ put_str("and Esc to quit.", 4, 32);
+
+ dump_savefiles(sel, max);
+
+ k = inkey();
+
+ if (k == ESCAPE)
+ {
+ quit(NULL);
+ }
+ if (k == '6')
+ {
+ sel++;
+ if (sel >= max) sel = 0;
+ continue;
+ }
+ else if (k == '4')
+ {
+ sel--;
+ if (sel < 0) sel = max - 1;
+ continue;
+ }
+ else if (k == '2')
+ {
+ sel += 4;
+ if (sel >= max) sel = sel % max;
+ continue;
+ }
+ else if (k == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = (sel + max - 1) % max;
+ continue;
+ }
+ else if (k == '\r')
+ {
+ if (sel < 26) k = I2A(sel);
+ else k = toupper(I2A(sel));
+ }
+ else if (((k == 0x7F) || (k == '\010')) && (sel >= 2))
+ {
+ char player_base_save[32];
+
+ if (!get_check(format("Really delete '%s'?", savefile_names[savefile_idx[sel - 2]]))) continue;
+
+ /* Save current 'player_base' */
+ strncpy(player_base_save, player_base, 32);
+
+ /* Build platform-dependent save file name */
+ strncpy(player_base, savefile_names[savefile_idx[sel - 2]], 32);
+ process_player_name(TRUE);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove the savefile */
+ fd_kill(savefile);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Restore 'player_base' and 'savefile' */
+ strncpy(player_base, player_base_save, 32);
+ process_player_name(TRUE);
+
+ /* Reload, gods I hate using goto .. */
+ goto savefile_try_again;
+
+ continue;
+ }
+
+ if (k == 'a')
+ {
+ /* Display prompt */
+ prt("Enter the name of the savefile that will hold this character: ", 23, 0);
+
+ /* Ask the user for a string */
+ if (!askfor_aux(player_base, 15)) continue;
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (TRUE);
+ }
+ if (k == 'b')
+ {
+ /* Display prompt */
+ prt("Enter the name of a savefile: ", 23, 0);
+
+ /* Ask the user for a string */
+ if (!askfor_aux(player_base, 15)) continue;
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (FALSE);
+ }
+ else
+ {
+ int x;
+
+ if (islower(k)) x = A2I(k);
+ else x = A2I(tolower(k)) + 26;
+
+ if ((x < 2) || (x >= max)) continue;
+
+ strnfmt(player_base, 32, "%s", savefile_names[savefile_idx[x - 2]]);
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (FALSE);
+ }
+ }
+
+ /* Shouldnt happen */
+ return (FALSE);
+}
diff --git a/src/bldg.c b/src/bldg.c
new file mode 100644
index 00000000..6a10a0f2
--- /dev/null
+++ b/src/bldg.c
@@ -0,0 +1,2273 @@
+/* File: bldg.c */
+
+/*
+ * Purpose: Building commands
+ * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3
+ * -KMW-
+ *
+ * Rewritten for Kangband 2.8.3i using Kamband's version of
+ * bldg.c as written by Ivan Tkatchev
+ *
+ * Changed for ZAngband by Robert Ruehlmann
+ *
+ * Heavily modified for ToME by DarkGod
+ */
+
+#include "angband.h"
+
+/* hack as in leave_store in store.c */
+static bool leave_bldg = FALSE;
+
+/* remember building location */
+static int building_loc = 0;
+
+
+/*
+ * A helper function for is_state
+ */
+bool is_state_aux(store_type *s_ptr, int state)
+{
+ owner_type *ow_ptr = &ow_info[s_ptr->owner];
+
+
+ /* Check race */
+ if (ow_ptr->races[state][p_ptr->prace / 32] & (1 << p_ptr->prace))
+ {
+ return (TRUE);
+ }
+
+ /* Check class */
+ if (ow_ptr->classes[state][p_ptr->prace / 32] & (1 << p_ptr->pclass))
+ {
+ return (TRUE);
+ }
+
+ /* All failed */
+ return (FALSE);
+}
+
+
+/*
+ * Test if the state accords with the player
+ */
+bool is_state(store_type *s_ptr, int state)
+{
+ if (state == STORE_NORMAL)
+ {
+ if (is_state_aux(s_ptr, STORE_LIKED)) return (FALSE);
+ if (is_state_aux(s_ptr, STORE_HATED)) return (FALSE);
+ return (TRUE);
+ }
+
+ else
+ {
+ return (is_state_aux(s_ptr, state));
+ }
+}
+
+
+/*
+ * Clear the building information
+ */
+static void clear_bldg(int min_row, int max_row)
+{
+ int i;
+
+
+ for (i = min_row; i <= max_row; i++)
+ {
+ prt("", i, 0);
+ }
+}
+
+
+/*
+ * Display a building.
+ */
+void show_building(store_type *s_ptr)
+{
+ char buff[20];
+
+ int i;
+
+ byte action_color;
+
+ char tmp_str[80];
+
+ store_info_type *st_ptr = &st_info[s_ptr->st_idx];
+
+ store_action_type *ba_ptr;
+
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_ptr->actions[i]];
+
+ if (ba_ptr->letter != '.')
+ {
+ if (ba_ptr->action_restr == 0)
+ {
+ if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) ||
+ (is_state(s_ptr, STORE_HATED) && (ba_ptr->costs[STORE_HATED] == 0)) ||
+ (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0)))
+ {
+ action_color = TERM_WHITE;
+ buff[0] = '\0';
+ }
+ else if (is_state(s_ptr, STORE_LIKED))
+ {
+ action_color = TERM_L_GREEN;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
+ }
+ else if (is_state(s_ptr, STORE_HATED))
+ {
+ action_color = TERM_RED;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_HATED]);
+ }
+ else
+ {
+ action_color = TERM_YELLOW;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]);
+ }
+ }
+ else if (ba_ptr->action_restr == 1)
+ {
+ if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) ||
+ (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0)))
+ {
+ action_color = TERM_WHITE;
+ buff[0] = '\0';
+ }
+ else if (is_state(s_ptr, STORE_LIKED))
+ {
+ action_color = TERM_L_GREEN;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
+ }
+ else if (is_state(s_ptr, STORE_HATED))
+ {
+ action_color = TERM_L_DARK;
+ strnfmt(buff, 20, "(closed)");
+ }
+ else
+ {
+ action_color = TERM_YELLOW;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]);
+ }
+ }
+ else
+ {
+ if (is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0))
+ {
+ action_color = TERM_WHITE;
+ buff[0] = '\0';
+ }
+ else if (is_state(s_ptr, STORE_LIKED))
+ {
+ action_color = TERM_L_GREEN;
+ strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
+ }
+ else
+ {
+ action_color = TERM_L_DARK;
+ strnfmt(buff, 20, "(closed)");
+ }
+ }
+
+ strnfmt(tmp_str, 80, " %c", ba_ptr->letter);
+ c_put_str(TERM_YELLOW, tmp_str, 21 + (i / 2), 17 + (30 * (i % 2)));
+
+ strnfmt(tmp_str, 80, ") %s %s", ba_ptr->name + ba_name, buff);
+ c_put_str(action_color, tmp_str, 21 + (i / 2), 2 + 17 + (30 * (i % 2)));
+ }
+ }
+}
+
+
+/* reset timed flags */
+static void reset_tim_flags()
+{
+ p_ptr->fast = 0; /* Timed -- Fast */
+ p_ptr->slow = 0; /* Timed -- Slow */
+ p_ptr->blind = 0; /* Timed -- Blindness */
+ p_ptr->paralyzed = 0; /* Timed -- Paralysis */
+ p_ptr->confused = 0; /* Timed -- Confusion */
+ p_ptr->afraid = 0; /* Timed -- Fear */
+ p_ptr->image = 0; /* Timed -- Hallucination */
+ p_ptr->poisoned = 0; /* Timed -- Poisoned */
+ p_ptr->cut = 0; /* Timed -- Cut */
+ p_ptr->stun = 0; /* Timed -- Stun */
+
+ p_ptr->protevil = 0; /* Timed -- Protection */
+ p_ptr->protgood = 0; /* Timed -- Protection */
+ p_ptr->invuln = 0; /* Timed -- Invulnerable */
+ p_ptr->hero = 0; /* Timed -- Heroism */
+ p_ptr->shero = 0; /* Timed -- Super Heroism */
+ p_ptr->shield = 0; /* Timed -- Shield Spell */
+ p_ptr->blessed = 0; /* Timed -- Blessed */
+ p_ptr->tim_invis = 0; /* Timed -- Invisibility */
+ p_ptr->tim_infra = 0; /* Timed -- Infra Vision */
+
+ p_ptr->oppose_acid = 0; /* Timed -- oppose acid */
+ p_ptr->oppose_elec = 0; /* Timed -- oppose lightning */
+ p_ptr->oppose_fire = 0; /* Timed -- oppose heat */
+ p_ptr->oppose_cold = 0; /* Timed -- oppose cold */
+ p_ptr->oppose_pois = 0; /* Timed -- oppose poison */
+
+ p_ptr->confusing = 0; /* Touch of Confusion */
+}
+
+
+/*
+ * arena commands
+ */
+static void arena_comm(int cmd)
+{
+ char tmp_str[80];
+
+ monster_race *r_ptr;
+
+ cptr name;
+
+
+ switch (cmd)
+ {
+ case BACT_ARENA:
+ {
+ if (p_ptr->arena_number == MAX_ARENA_MONS)
+ {
+ clear_bldg(5, 19);
+ prt(" Arena Victor!", 5, 0);
+ prt("Congratulations! You have defeated all before you.", 7, 0);
+ prt("For that, receive the prize: 10,000 gold pieces", 8, 0);
+ prt("", 10, 0);
+ prt("", 11, 0);
+ p_ptr->au += 10000;
+ msg_print("Press the space bar to continue");
+ msg_print(NULL);
+ p_ptr->arena_number++;
+ }
+ else if (p_ptr->arena_number > MAX_ARENA_MONS)
+ {
+ msg_print("You enter the arena briefly and bask in your glory.");
+ msg_print(NULL);
+ }
+ else
+ {
+ p_ptr->inside_arena = TRUE;
+ p_ptr->exit_bldg = FALSE;
+ reset_tim_flags();
+ p_ptr->leaving = TRUE;
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ leave_bldg = TRUE;
+ }
+
+ break;
+ }
+
+ case BACT_POSTER:
+ {
+ if (p_ptr->arena_number == MAX_ARENA_MONS)
+ msg_print("You are victorious. Enter the arena for the ceremony.");
+ else if (p_ptr->arena_number > MAX_ARENA_MONS)
+ msg_print("You have won against all foes.");
+ else
+ {
+ r_ptr = &r_info[arena_monsters[p_ptr->arena_number]];
+ name = (r_name + r_ptr->name);
+ strnfmt(tmp_str, 80, "Do I hear any challenges against: %s", name);
+ msg_print(tmp_str);
+ msg_print(NULL);
+ }
+
+ break;
+ }
+
+ case BACT_ARENA_RULES:
+ {
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the arena help file */
+ (void)show_file("arena.txt", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * display fruit for dice slots
+ */
+static void display_fruit(int row, int col, int fruit)
+{
+ switch (fruit)
+ {
+ case 0: /* lemon */
+ {
+ c_put_str(TERM_YELLOW, " ####.", row, col);
+ c_put_str(TERM_YELLOW, " # #", row + 1, col);
+ c_put_str(TERM_YELLOW, " # #", row + 2, col);
+ c_put_str(TERM_YELLOW, "# #", row + 3, col);
+ c_put_str(TERM_YELLOW, "# #", row + 4, col);
+ c_put_str(TERM_YELLOW, "# # ", row + 5, col);
+ c_put_str(TERM_YELLOW, "# # ", row + 6, col);
+ c_put_str(TERM_YELLOW, ".#### ", row + 7, col);
+ prt(" Lemon ", row + 8, col);
+
+ break;
+ }
+
+ case 1: /* orange */
+ {
+ c_put_str(TERM_ORANGE, " ## ", row, col);
+ c_put_str(TERM_ORANGE, " #..# ", row + 1, col);
+ c_put_str(TERM_ORANGE, " #....# ", row + 2, col);
+ c_put_str(TERM_ORANGE, "#......#", row + 3, col);
+ c_put_str(TERM_ORANGE, "#......#", row + 4, col);
+ c_put_str(TERM_ORANGE, " #....# ", row + 5, col);
+ c_put_str(TERM_ORANGE, " #..# ", row + 6, col);
+ c_put_str(TERM_ORANGE, " ## ", row + 7, col);
+ prt(" Orange ", row + 8, col);
+
+ break;
+ }
+
+ case 2: /* sword */
+ {
+ c_put_str(TERM_SLATE, " /\\ ", row, col);
+ c_put_str(TERM_SLATE, " ## ", row + 1, col);
+ c_put_str(TERM_SLATE, " ## ", row + 2, col);
+ c_put_str(TERM_SLATE, " ## ", row + 3, col);
+ c_put_str(TERM_SLATE, " ## ", row + 4, col);
+ c_put_str(TERM_SLATE, " ## ", row + 5, col);
+ c_put_str(TERM_UMBER, " ###### ", row + 6, col);
+ c_put_str(TERM_UMBER, " ## ", row + 7, col);
+ prt(" Sword ", row + 8, col);
+
+ break;
+ }
+
+ case 3: /* shield */
+ {
+ c_put_str(TERM_SLATE, " ###### ", row, col);
+ c_put_str(TERM_SLATE, "# #", row + 1, col);
+ c_put_str(TERM_SLATE, "# ++++ #", row + 2, col);
+ c_put_str(TERM_SLATE, "# +==+ #", row + 3, col);
+ c_put_str(TERM_SLATE, "# ++ #", row + 4, col);
+ c_put_str(TERM_SLATE, " # # ", row + 5, col);
+ c_put_str(TERM_SLATE, " # # ", row + 6, col);
+ c_put_str(TERM_SLATE, " ## ", row + 7, col);
+ prt(" Shield ", row + 8, col);
+
+ break;
+ }
+
+ case 4: /* plum */
+ {
+ c_put_str(TERM_VIOLET, " ## ", row, col);
+ c_put_str(TERM_VIOLET, " ###### ", row + 1, col);
+ c_put_str(TERM_VIOLET, "########", row + 2, col);
+ c_put_str(TERM_VIOLET, "########", row + 3, col);
+ c_put_str(TERM_VIOLET, "########", row + 4, col);
+ c_put_str(TERM_VIOLET, " ###### ", row + 5, col);
+ c_put_str(TERM_VIOLET, " #### ", row + 6, col);
+ c_put_str(TERM_VIOLET, " ## ", row + 7, col);
+ prt(" Plum ", row + 8, col);
+
+ break;
+ }
+
+ case 5: /* cherry */
+ {
+ c_put_str(TERM_RED, " ##", row, col);
+ c_put_str(TERM_RED, " ### ", row + 1, col);
+ c_put_str(TERM_RED, " #..# ", row + 2, col);
+ c_put_str(TERM_RED, " #..# ", row + 3, col);
+ c_put_str(TERM_RED, " ###### ", row + 4, col);
+ c_put_str(TERM_RED, "#..##..#", row + 5, col);
+ c_put_str(TERM_RED, "#..##..#", row + 6, col);
+ c_put_str(TERM_RED, " ## ## ", row + 7, col);
+ prt(" Cherry ", row + 8, col);
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * gamble_comm
+ */
+static bool gamble_comm(int cmd)
+{
+ int roll1, roll2, roll3, choice, odds, win;
+
+ s32b wager;
+
+ s32b maxbet;
+
+ s32b oldgold;
+
+ static const char *fruit[6] =
+ {"Lemon", "Orange", "Sword", "Shield", "Plum", "Cherry"
+ };
+
+ char out_val[160], tmp_str[80], again;
+
+ cptr p;
+
+
+ screen_save();
+
+ if (cmd == BACT_GAMBLE_RULES)
+ {
+ /* Peruse the gambling help file */
+ (void)show_file("gambling.txt", NULL, 0, 0);
+ }
+ else
+ {
+ clear_bldg(5, 23);
+
+ /* Set maximum bet */
+ if (p_ptr->lev < 10)
+ maxbet = (p_ptr->lev * 100);
+ else
+ maxbet = (p_ptr->lev * 1000);
+
+ /* Get the wager */
+ strcpy(out_val, "");
+ strnfmt(tmp_str, 80, "Your wager (1-%ld) ? ", maxbet);
+ get_string(tmp_str, out_val, 32);
+
+ /* Strip spaces */
+ for (p = out_val; *p == ' '; p++);
+
+ wager = atol(p);
+
+ if (wager > p_ptr->au)
+ {
+ msg_print("Hey! You don't have the gold - get out of here!");
+ msg_print(NULL);
+ screen_load();
+ return (FALSE);
+ }
+ else if (wager > maxbet)
+ {
+ msg_format("I'll take $%ld of that. Keep the rest.", maxbet);
+ wager = maxbet;
+ }
+ else if (wager < 1)
+ {
+ msg_print("Ok, we'll start with $1.");
+
+ wager = 1;
+ }
+ msg_print(NULL);
+ win = FALSE;
+ odds = 0;
+ oldgold = p_ptr->au;
+
+ strnfmt(tmp_str, 80, "Gold before game: %9ld", oldgold);
+ prt(tmp_str, 20, 2);
+
+ strnfmt(tmp_str, 80, "Current Wager: %9ld", wager);
+ prt(tmp_str, 21, 2);
+
+ do
+ {
+ switch (cmd)
+ {
+ case BACT_IN_BETWEEN: /* Game of In-Between */
+ {
+ c_put_str(TERM_GREEN, "In Between", 5, 2);
+ odds = 3;
+ win = FALSE;
+ roll1 = randint(10);
+ roll2 = randint(10);
+ choice = randint(10);
+ strnfmt(tmp_str, 80, "Black die: %d Black Die: %d",
+ roll1, roll2);
+ prt(tmp_str, 8, 3);
+ strnfmt(tmp_str, 80, "Red die: %d", choice);
+ prt(tmp_str, 11, 14);
+ if (((choice > roll1) && (choice < roll2)) ||
+ ((choice < roll1) && (choice > roll2)))
+ win = TRUE;
+
+ break;
+ }
+ case BACT_CRAPS: /* Game of Craps */
+ {
+ c_put_str(TERM_GREEN, "Craps", 5, 2);
+ win = 3;
+ odds = 1;
+ roll1 = randint(6);
+ roll2 = randint(6);
+ roll3 = roll1 + roll2;
+ choice = roll3;
+ strnfmt(tmp_str, 80, "First roll: %d %d Total: %d", roll1,
+ roll2, roll3);
+ prt(tmp_str, 7, 5);
+ if ((roll3 == 7) || (roll3 == 11))
+ win = TRUE;
+ else if ((roll3 == 2) || (roll3 == 3) || (roll3 == 12))
+ win = FALSE;
+ else
+ {
+ do
+ {
+ msg_print("Hit any key to roll again");
+ msg_print(NULL);
+ roll1 = randint(6);
+ roll2 = randint(6);
+ roll3 = roll1 + roll2;
+
+ strnfmt(tmp_str, 80, "Roll result: %d %d Total: %d",
+ roll1, roll2, roll3);
+ prt(tmp_str, 8, 5);
+ if (roll3 == choice)
+ win = TRUE;
+ else if (roll3 == 7)
+ win = FALSE;
+ }
+ while ((win != TRUE) && (win != FALSE));
+ }
+
+ break;
+ }
+
+ case BACT_SPIN_WHEEL: /* Spin the Wheel Game */
+ {
+ win = FALSE;
+ odds = 10;
+ c_put_str(TERM_GREEN, "Wheel", 5, 2);
+ prt("0 1 2 3 4 5 6 7 8 9", 7, 5);
+ prt("--------------------------------", 8, 3);
+ strcpy(out_val, "");
+ get_string ("Pick a number (1-9): ", out_val, 32);
+ for (p = out_val; *p == ' '; p++);
+ choice = atol(p);
+ if (choice < 0)
+ {
+ msg_print("I'll put you down for 0.");
+ choice = 0;
+ }
+ else if (choice > 9)
+ {
+ msg_print("Ok, I'll put you down for 9.");
+ choice = 9;
+ }
+ msg_print(NULL);
+ roll1 = randint(10) - 1;
+ strnfmt(tmp_str, 80, "The wheel spins to a stop and the winner is %d",
+ roll1);
+ prt(tmp_str, 13, 3);
+ prt("", 9, 0);
+ prt("*", 9, (3 * roll1 + 5));
+ if (roll1 == choice)
+ win = TRUE;
+
+ break;
+ }
+
+ case BACT_DICE_SLOTS: /* The Dice Slots */
+ {
+ c_put_str(TERM_GREEN, "Dice Slots", 5, 2);
+ win = FALSE;
+ roll1 = randint(6);
+ roll2 = randint(6);
+ choice = randint(6);
+ strnfmt(tmp_str, 80, "%s %s %s",
+ fruit[roll1 - 1], fruit[roll2 - 1],
+ fruit[choice - 1]);
+ prt(tmp_str, 15, 37);
+ prt("/--------------------------\\", 7, 2);
+ prt("\\--------------------------/", 17, 2);
+ display_fruit(8, 3, roll1 - 1);
+ display_fruit(8, 12, roll2 - 1);
+ display_fruit(8, 21, choice - 1);
+ if ((roll1 == roll2) && (roll2 == choice))
+ {
+ win = TRUE;
+ if (roll1 == 1)
+ odds = 4;
+ else if (roll1 == 2)
+ odds = 6;
+ else
+ odds = roll1 * roll1;
+ }
+ else if ((roll1 == 6) && (roll2 == 6))
+ {
+ win = TRUE;
+ odds = choice + 1;
+ }
+
+ break;
+ }
+ }
+
+ if (win)
+ {
+ prt("YOU WON", 16, 37);
+ p_ptr->au = p_ptr->au + (odds * wager);
+ strnfmt(tmp_str, 80, "Payoff: %d", odds);
+ prt(tmp_str, 17, 37);
+ }
+ else
+ {
+ prt("You Lost", 16, 37);
+ p_ptr->au = p_ptr->au - wager;
+ prt("", 17, 37);
+ }
+ strnfmt(tmp_str, 80, "Current Gold: %9ld", p_ptr->au);
+ prt(tmp_str, 22, 2);
+ prt("Again(Y/N)?", 18, 37);
+ move_cursor(18, 49);
+ again = inkey();
+ if (wager > p_ptr->au)
+ {
+ msg_print("Hey! You don't have the gold - get out of here!");
+ msg_print(NULL);
+ screen_load();
+ return (FALSE);
+ /* strnfmt(tmp_str, 80, "Current Wager: %9ld",wager);
+ prt(tmp_str, 17, 2); */
+ }
+ }
+ while ((again == 'y') || (again == 'Y'));
+
+ prt("", 18, 37);
+ if (p_ptr->au >= oldgold)
+ msg_print("You came out a winner! We'll win next time, I'm sure.");
+ else
+ msg_print("You lost gold! Haha, better head home.");
+ msg_print(NULL);
+ }
+
+ screen_load();
+
+ return (TRUE);
+}
+
+
+/*
+ * inn commands
+ * Note that resting for the night was a perfect way to avoid player
+ * ghosts in the town *if* you could only make it to the inn in time (-:
+ * Now that the ghosts are temporarily disabled in 2.8.X, this function
+ * will not be that useful. I will keep it in the hopes the player
+ * ghost code does become a reality again. Does help to avoid filthy urchins.
+ * Resting at night is also a quick way to restock stores -KMW-
+ */
+static bool inn_comm(int cmd)
+{
+ bool vampire;
+
+
+ /* Extract race info */
+ vampire = ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")));
+
+ switch (cmd)
+ {
+ case BACT_FOOD: /* Buy food & drink */
+ {
+ if (!vampire)
+ {
+ msg_print("The barkeep gives you some gruel and a beer.");
+ msg_print(NULL);
+ (void) set_food(PY_FOOD_MAX - 1);
+ }
+ else
+ msg_print("You're a vampire and I don't have any food for you!");
+
+ break;
+ }
+
+ /*
+ * I revamped this... Don't know why normal races didn't get
+ * mana regenerated. It is the grand tradition of p&p games -- pelpel
+ */
+ case BACT_REST: /* Rest for the night */
+ {
+ bool nighttime;
+
+ /* Extract the current time */
+ nighttime = ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18));
+
+ /* Normal races rest at night */
+ if (!vampire && !nighttime)
+ {
+ msg_print("The rooms are available only at night.");
+ msg_print(NULL);
+ return (FALSE);
+ }
+
+ /* Vampires rest during daytime */
+ if (vampire && nighttime)
+ {
+ msg_print("The rooms are available only during daylight for your kind.");
+ msg_print(NULL);
+ return (FALSE);
+ }
+
+ /* Must cure HP draining status first */
+ if ((p_ptr->poisoned > 0) || (p_ptr->cut > 0))
+ {
+ msg_print("You need a healer, not a room.");
+ msg_print(NULL);
+ msg_print("Sorry, but I don't want anyone dying in here.");
+ return (FALSE);
+ }
+
+ /* Let the time pass XXX XXX XXX */
+ if (vampire)
+ {
+ /* Wait for sunset */
+ while ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18))
+ {
+ turn += (10L * MINUTE);
+ }
+ }
+ else
+ {
+ /* Wait for sunrise */
+ while ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18))
+ {
+ turn += (10L * MINUTE);
+ }
+ }
+
+ /* Regen */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->csp = p_ptr->msp;
+
+ /* Restore status */
+ set_blind(0);
+ set_confused(0);
+ p_ptr->stun = 0;
+
+ /* Message */
+ if (vampire) msg_print("You awake refreshed for the new night.");
+ else msg_print("You awake refreshed for the new day.");
+
+ /* Dungeon stuff */
+ p_ptr->leaving = TRUE;
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ /* Select new bounties. */
+ select_bounties();
+
+ break;
+ }
+
+ case BACT_RUMORS: /* Listen for rumors */
+ {
+ char rumor[80];
+
+ get_rnd_line("rumors.txt", rumor);
+ msg_format("%s", rumor);
+ msg_print(NULL);
+
+ break;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+#if 0 /* removes compiler warning */
+
+/*
+ * share gold for thieves
+ */
+static void share_gold(void)
+{
+ int i;
+
+
+ i = (p_ptr->lev * 2) * 10;
+ msg_format("You collect %d gold pieces", i);
+ msg_print(NULL);
+ p_ptr->au += i;
+}
+
+#endif
+
+/*
+ * Display quest information
+ */
+static void get_questinfo(int questnum)
+{
+ int i;
+
+
+ /* Print the quest info */
+ prt(format("Quest Information (Danger level: %d)", quest[questnum].level), 5, 0);
+
+ prt(quest[questnum].name, 7, 0);
+
+ i = 0;
+ while ((i < 10) && (quest[questnum].desc[i][0] != '\0'))
+ {
+ c_put_str(TERM_YELLOW, quest[questnum].desc[i], i + 8, 0);
+ i++;
+ }
+}
+
+
+/*
+ * Request a quest from the Lord.
+ */
+static bool castle_quest(int y, int x)
+{
+ int plot = 0;
+
+ quest_type *q_ptr;
+
+
+ clear_bldg(7, 18);
+
+ /* Current plot of the building */
+ plot = cave[y][x].special;
+
+ /* Is there a quest available at the building? */
+ if ((!plot) || (plots[plot] == QUEST_NULL))
+ {
+ put_str("I don't have a quest for you at the moment.", 8, 0);
+ return FALSE;
+ }
+
+ q_ptr = &quest[plots[plot]];
+
+ /* Quest is completed */
+ if (q_ptr->status == QUEST_STATUS_COMPLETED)
+ {
+ /* Rewarded quest */
+ q_ptr->status = QUEST_STATUS_FINISHED;
+
+ process_hooks(HOOK_QUEST_FINISH, "(d)", plots[plot]);
+
+ return (TRUE);
+ }
+
+ /* Quest is still unfinished */
+ else if (q_ptr->status == QUEST_STATUS_TAKEN)
+ {
+ put_str("You have not completed your current quest yet!", 8, 0);
+ put_str("Use CTRL-Q to check the status of your quest.", 9, 0);
+ put_str("Return when you have completed your quest.", 12, 0);
+
+ return (FALSE);
+ }
+ /* Failed quest */
+ else if (q_ptr->status == QUEST_STATUS_FAILED)
+ {
+ /* Mark quest as done (but failed) */
+ q_ptr->status = QUEST_STATUS_FAILED_DONE;
+
+ process_hooks(HOOK_QUEST_FAIL, "(d)", plots[plot]);
+
+ return (FALSE);
+ }
+ /* No quest yet */
+ else if (q_ptr->status == QUEST_STATUS_UNTAKEN)
+ {
+ if (process_hooks(HOOK_INIT_QUEST, "(d)", plots[plot])) return (FALSE);
+
+ q_ptr->status = QUEST_STATUS_TAKEN;
+
+ /* Assign a new quest */
+ get_questinfo(plots[plot]);
+
+ /* Add the hooks */
+ if (quest[plots[plot]].type == HOOK_TYPE_C) quest[plots[plot]].init(plots[plot]);
+
+ return (TRUE);
+ }
+
+ return FALSE;
+}
+
+/*
+ * Displaying town history -KMW-
+ */
+static void town_history(void)
+{
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the building help file */
+ (void)show_file("bldg.txt", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+}
+
+
+/*
+ * compare_weapon_aux2 -KMW-
+ */
+static void compare_weapon_aux2(object_type *o_ptr, int numblows, int r, int c, int mult, char attr[80], u32b f1, u32b f2, u32b f3, byte color)
+{
+ char tmp_str[80];
+
+ c_put_str(color, attr, r, c);
+ strnfmt(tmp_str, 80, "Attack: %d-%d damage",
+ numblows * ((o_ptr->dd * mult) + o_ptr->to_d),
+ numblows * ((o_ptr->ds * o_ptr->dd * mult) + o_ptr->to_d));
+ put_str(tmp_str, r, c + 8);
+ r++;
+}
+
+
+/*
+ * compare_weapon_aux1 -KMW-
+ */
+static void compare_weapon_aux1(object_type *o_ptr, int col, int r)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ if (f1 & (TR1_SLAY_ANIMAL))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Animals:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_EVIL))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Evil:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_UNDEAD))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Undead:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_DEMON))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Demons:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_ORC))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Orcs:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_TROLL))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Trolls:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_GIANT))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Giants:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_SLAY_DRAGON))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Dragons:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_KILL_DRAGON))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 5, "Dragons:",
+ f1, f2, f3, TERM_YELLOW);
+ }
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Acid:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Elec:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Fire:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Cold:",
+ f1, f2, f3, TERM_RED);
+ }
+ if (f1 & (TR1_BRAND_POIS))
+ {
+ compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Poison:",
+ f1, f2, f3, TERM_RED);
+ }
+}
+
+
+/*
+ * list_weapon -KMW-
+ */
+static void list_weapon(object_type *o_ptr, int row, int col)
+{
+ char o_name[80];
+
+ char tmp_str[80];
+
+
+ object_desc(o_name, o_ptr, TRUE, 0);
+ c_put_str(TERM_YELLOW, o_name, row, col);
+ strnfmt(tmp_str, 80, "To Hit: %d To Damage: %d", o_ptr->to_h, o_ptr->to_d);
+ put_str(tmp_str, row + 1, col);
+ strnfmt(tmp_str, 80, "Dice: %d Sides: %d", o_ptr->dd, o_ptr->ds);
+ put_str(tmp_str, row + 2, col);
+ strnfmt(tmp_str, 80, "Number of Blows: %d", p_ptr->num_blow);
+ put_str(tmp_str, row + 3, col);
+ c_put_str(TERM_YELLOW, "Possible Damage:", row + 5, col);
+ strnfmt(tmp_str, 80, "One Strike: %d-%d damage", o_ptr->dd + o_ptr->to_d,
+ (o_ptr->ds*o_ptr->dd) + o_ptr->to_d);
+ put_str(tmp_str, row + 6, col + 1);
+ strnfmt(tmp_str, 80, "One Attack: %d-%d damage", p_ptr->num_blow*(o_ptr->dd + o_ptr->to_d),
+ p_ptr->num_blow*(o_ptr->ds*o_ptr->dd + o_ptr->to_d));
+ put_str(tmp_str, row + 7, col + 1);
+}
+
+
+/*
+ * Select melee weapons
+ */
+static bool item_tester_hook_melee_weapon(object_type *o_ptr)
+{
+ return (wield_slot(o_ptr) == INVEN_WIELD);
+}
+
+/*
+ * compare_weapons -KMW-
+ */
+static bool compare_weapons(void)
+{
+ int item, item2, i;
+
+ object_type *o1_ptr, *o2_ptr, *orig_ptr;
+
+ object_type *i_ptr;
+
+ cptr q, s;
+
+
+ clear_bldg(6, 18);
+
+ o1_ptr = NULL;
+ o2_ptr = NULL;
+ i_ptr = NULL;
+
+ /* Store copy of original wielded weapon in pack slot */
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ orig_ptr = &p_ptr->inventory[INVEN_PACK];
+ object_copy(orig_ptr, i_ptr);
+
+ i = 6;
+ /* Get first weapon */
+ /* Restrict choices to meele weapons */
+ item_tester_hook = item_tester_hook_melee_weapon;
+
+ q = "What is your first melee weapon? ";
+ s = "You have nothing to compare.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN)))
+ {
+ object_wipe(orig_ptr);
+ return (FALSE);
+ }
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ o1_ptr = &p_ptr->inventory[item];
+
+ /* Get second weapon */
+ /* Restrict choices to melee weapons */
+ item_tester_hook = item_tester_hook_melee_weapon;
+
+ q = "What is your second melee weapon? ";
+ s = "You have nothing to compare.";
+ if (!get_item(&item2, q, s, (USE_EQUIP | USE_INVEN)))
+ {
+ object_wipe(orig_ptr);
+ return (FALSE);
+ }
+
+ /* Get the item (in the pack) */
+ if (item2 >= 0) o2_ptr = &p_ptr->inventory[item2];
+
+ put_str("Based on your current abilities, here is what your weapons will do", 4, 2);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_copy(i_ptr, o1_ptr);
+ calc_bonuses(TRUE);
+
+ list_weapon(o1_ptr, i, 2);
+ compare_weapon_aux1(o1_ptr, 2, i + 8);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ if (item2 == INVEN_WIELD)
+ object_copy(i_ptr, orig_ptr);
+ else
+ object_copy(i_ptr, o2_ptr);
+ calc_bonuses(TRUE);
+
+ list_weapon(o2_ptr, i, 40);
+ compare_weapon_aux1(o2_ptr, 40, i + 8);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_copy(i_ptr, orig_ptr);
+ calc_bonuses(TRUE);
+
+ object_wipe(orig_ptr);
+
+ put_str("(Only highest damage applies per monster. Special damage not cumulative)", 20, 0);
+
+ return (TRUE);
+}
+
+
+/*
+ * general all-purpose fixing routine for items from building personnel
+ * sharpen arrows, repair armor, repair weapon
+ * -KMW-
+ */
+static bool fix_item(int istart, int iend, int ispecific, bool iac,
+ int ireward, bool set_reward)
+{
+ int i;
+
+ int j = 9;
+
+ int maxenchant = (p_ptr->lev / 5);
+
+ object_type *o_ptr;
+
+ char out_val[80], tmp_str[80];
+
+ bool repaired = FALSE;
+
+#if 0
+ if (set_reward && p_ptr->rewards[ireward])
+ {
+ msg_print("You already have been rewarded today.");
+ msg_print(NULL);
+
+ return (FALSE);
+ }
+#endif
+ clear_bldg(5, 18);
+ strnfmt(tmp_str, 80, " Based on your skill, we can improve up to +%d", maxenchant);
+ prt(tmp_str, 5, 0);
+ prt("Status", 7, 30);
+
+ for (i = istart; i <= iend; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+ if (ispecific > 0)
+ {
+ if (o_ptr->tval != ispecific)
+ continue;
+ }
+
+ if (o_ptr->tval)
+ {
+ object_desc(tmp_str, o_ptr, FALSE, 1);
+
+ if ((o_ptr->name1 && (o_ptr->ident & 0x08)))
+ strnfmt(out_val, 80, "%-40s: beyond our skills!", tmp_str);
+ else if (o_ptr->name1)
+ strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
+ else
+ {
+ if ((iac) && (o_ptr->to_a <= -3))
+ {
+ strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
+ }
+ else if ((iac) && (o_ptr->to_a < maxenchant))
+ {
+ o_ptr->to_a++;
+ strnfmt(out_val, 80, "%-40s: polished -> (%d)", tmp_str, o_ptr->to_a);
+ repaired = TRUE;
+ }
+ else if ((!iac) && ((o_ptr->to_h <= -3) || (o_ptr->to_d <= -3)))
+ {
+ strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
+ }
+ /* Sharpen a weapon */
+ else if ((!iac) && ((o_ptr->to_h < maxenchant) ||
+ (o_ptr->to_d < maxenchant)))
+ {
+ if (o_ptr->to_h < maxenchant)
+ o_ptr->to_h++;
+ if (o_ptr->to_d < maxenchant)
+ o_ptr->to_d++;
+ strnfmt(out_val, 80, "%-40s: sharpened -> (%d,%d)", tmp_str,
+ o_ptr->to_h, o_ptr->to_d);
+ repaired = TRUE;
+ }
+ else
+ strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
+ }
+ prt(out_val, j++, 0);
+ }
+ }
+
+ if (!repaired)
+ {
+ msg_print("You don't have anything appropriate.");
+ msg_print(NULL);
+ }
+ else
+ {
+#if 0
+ if (set_reward)
+ p_ptr->rewards[ireward] = TRUE;
+#endif
+ msg_print("Press the spacebar to continue");
+ msg_print(NULL);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ p_ptr->update |= (PU_BONUS);
+ }
+ clear_bldg(5, 18);
+
+ return (repaired);
+}
+
+
+/*
+ * Research Item
+ */
+static bool research_item(void)
+{
+ clear_bldg(5, 18);
+ return (identify_fully());
+}
+
+
+/*
+ * Show the current quest monster.
+ */
+static void show_quest_monster(void)
+{
+ monster_race* r_ptr = &r_info[bounties[0][0]];
+
+
+ msg_format("Quest monster: %s. "
+ "Need to turn in %d corpse%s to receive reward.",
+ r_name + r_ptr->name, bounties[0][1],
+ (bounties[0][1] > 1 ? "s" : ""));
+ msg_print(NULL);
+}
+
+
+/*
+ * Show the current bounties.
+ */
+static void show_bounties(void)
+{
+ int i, j = 6;
+
+ monster_race* r_ptr;
+
+ char buff[80];
+
+
+ clear_bldg(7, 18);
+
+ c_prt(TERM_YELLOW, "Currently active bounties:", 4, 2);
+
+ for (i = 1; i < MAX_BOUNTIES; i++, j++)
+ {
+ r_ptr = &r_info[bounties[i][0]];
+
+ strnfmt(buff, 80, "%-30s (%d gp)", r_name + r_ptr->name, bounties[i][1]);
+
+ prt(buff, j, 2);
+
+ if (j >= 17)
+ {
+ msg_print("Press space for more.");
+ msg_print(NULL);
+
+ clear_bldg(7, 18);
+ j = 5;
+ }
+ }
+}
+
+
+/*
+ * Filter for corpses that currently have a bounty on them.
+ */
+static bool item_tester_hook_bounty(object_type* o_ptr)
+{
+ int i;
+
+
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ for (i = 1; i < MAX_BOUNTIES; i++)
+ {
+ if (bounties[i][0] == o_ptr->pval2) return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+/* Filter to match the quest monster's corpse. */
+static bool item_tester_hook_quest_monster(object_type* o_ptr)
+{
+ if ((o_ptr->tval == TV_CORPSE) &&
+ (o_ptr->pval2 == bounties[0][0])) return (TRUE);
+ return (FALSE);
+}
+
+
+/*
+ * Return the boost in the corpse's value depending on how rare the body
+ * part is.
+ */
+static int corpse_value_boost(int sval)
+{
+ switch (sval)
+ {
+ case SV_CORPSE_HEAD:
+ case SV_CORPSE_SKULL:
+ {
+ return (1);
+ }
+
+ /* Default to no boost. */
+ default:
+ {
+ return (0);
+ }
+ }
+}
+
+/*
+ * Sell a corpse, if there's currently a bounty on it.
+ */
+static void sell_corpses(void)
+{
+ object_type* o_ptr;
+
+ int i, boost = 0;
+
+ s16b value;
+
+ int item;
+
+
+ /* Set the hook. */
+ item_tester_hook = item_tester_hook_bounty;
+
+ /* Select a corpse to sell. */
+ if (!get_item(&item, "Sell which corpse",
+ "You have no corpses you can sell.", USE_INVEN)) return;
+
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Exotic body parts are worth more. */
+ boost = corpse_value_boost(o_ptr->sval);
+
+ /* Try to find a match. */
+ for (i = 1; i < MAX_BOUNTIES; i++)
+ {
+ if (o_ptr->pval2 == bounties[i][0])
+ {
+ value = bounties[i][1] + boost * (r_info[o_ptr->pval2].level);
+
+ msg_format("Sold for %ld gold pieces.", value);
+ msg_print(NULL);
+ p_ptr->au += value;
+
+ /* Increase the number of collected bounties */
+ total_bounties++;
+
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ return;
+ }
+ }
+
+ msg_print("Sorry, but that monster does not have a bounty on it.");
+ msg_print(NULL);
+}
+
+
+
+/*
+ * Hook for bounty monster selection.
+ */
+static bool mon_hook_bounty(int r_idx)
+{
+ monster_race* r_ptr = &r_info[r_idx];
+
+
+ /* Reject uniques */
+ if (r_ptr->flags1 & RF1_UNIQUE) return (FALSE);
+
+ /* Reject those who cannot leave anything */
+ if (!(r_ptr->flags9 & RF9_DROP_CORPSE) &&
+ !(r_ptr->flags9 & RF9_DROP_SKELETON)) return (FALSE);
+
+ /* Reject pets */
+ if (r_ptr->flags7 & RF7_PET) return (FALSE);
+
+ /* Reject friendly creatures */
+ if (r_ptr->flags7 & RF7_FRIENDLY) return (FALSE);
+
+ /* The rest are acceptable */
+ return (TRUE);
+}
+
+
+static void select_quest_monster(void)
+{
+ monster_race* r_ptr;
+
+ int amt;
+
+
+ /*
+ * Set up the hooks -- no bounties on uniques or monsters
+ * with no corpses
+ */
+ get_mon_num_hook = mon_hook_bounty;
+ get_mon_num_prep();
+
+ /* Set up the quest monster. */
+ bounties[0][0] = get_mon_num(p_ptr->lev);
+
+ r_ptr = &r_info[bounties[0][0]];
+
+ /*
+ * Select the number of monsters needed to kill. Groups and
+ * breeders require more
+ */
+ amt = randnor(5, 3);
+
+ if (amt < 2) amt = 2;
+
+ if (r_ptr->flags1 & RF1_FRIEND) amt *= 3; amt /= 2;
+ if (r_ptr->flags1 & RF1_FRIENDS) amt *= 2;
+ if (r_ptr->flags4 & RF4_MULTIPLY) amt *= 3;
+
+ if (r_ptr->flags7 & RF7_AQUATIC) amt /= 2;
+
+ bounties[0][1] = amt;
+
+ /* Undo the filters */
+ get_mon_num_hook = NULL;
+ get_mon_num_prep();
+}
+
+
+
+/*
+ * Sell a corpse for a reward.
+ */
+static void sell_quest_monster(void)
+{
+ object_type* o_ptr;
+
+ int item;
+
+
+ /* Set the hook. */
+ item_tester_hook = item_tester_hook_quest_monster;
+
+ /* Select a corpse to sell. */
+ if (!get_item(&item, "Sell which corpse",
+ "You have no corpses you can sell.", USE_INVEN)) return;
+
+ o_ptr = &p_ptr->inventory[item];
+
+ bounties[0][1] -= o_ptr->number;
+
+ /* Completed the quest. */
+ if (bounties[0][1] <= 0)
+ {
+ int m;
+ monster_race *r_ptr;
+
+ cmsg_print(TERM_YELLOW, "You have completed your quest!");
+ msg_print(NULL);
+
+ /* Give full knowledge */
+
+ /* Hack -- Maximal info */
+ r_ptr = &r_info[bounties[0][0]];
+
+ msg_print(format("Well done! As a reward I'll teach you everything "
+ "about the %s, (check your recall)",
+ r_name + r_ptr->name));
+
+ r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR;
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags4 = r_ptr->flags7;
+ r_ptr->r_flags5 = r_ptr->flags8;
+ r_ptr->r_flags6 = r_ptr->flags9;
+
+ msg_print(NULL);
+
+ select_quest_monster();
+
+ }
+ else
+ {
+ msg_format("Well done, only %d more to go.", bounties[0][1]);
+ msg_print(NULL);
+ }
+
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+}
+
+
+
+/*
+ * Fill the bounty list with monsters.
+ */
+void select_bounties(void)
+{
+ int i, j;
+
+
+ select_quest_monster();
+
+ /*
+ * Set up the hooks -- no bounties on uniques or monsters
+ * with no corpses
+ */
+ get_mon_num_hook = mon_hook_bounty;
+ get_mon_num_prep();
+
+ for (i = 1; i < MAX_BOUNTIES; i++)
+ {
+ int lev = i * 5 + randnor(0, 2);
+ monster_race* r_ptr;
+ s16b r_idx;
+ s16b val;
+
+ if (lev < 1) lev = 1;
+
+ if (lev >= MAX_DEPTH) lev = MAX_DEPTH - 1;
+
+ /* We don't want to duplicate entries in the list */
+ while (TRUE)
+ {
+ r_idx = get_mon_num(lev);
+
+ for (j = 0; j < i; j++)
+ {
+ if (bounties[j][0] == r_idx) continue;
+ }
+
+ break;
+ }
+
+ bounties[i][0] = r_idx;
+
+ r_ptr = &r_info[r_idx];
+
+ val = r_ptr->mexp + r_ptr->level * 20 + randnor(0, r_ptr->level * 2);
+
+ if (val < 1) val = 1;
+
+ bounties[i][1] = val;
+ }
+
+ /* Undo the filters. */
+ get_mon_num_hook = NULL;
+ get_mon_num_prep();
+}
+
+/*
+ * Execute a building command
+ */
+bool bldg_process_command(store_type *s_ptr, int i)
+{
+ store_action_type *ba_ptr = &ba_info[st_info[s_ptr->st_idx].actions[i]];
+
+ int bact = ba_ptr->action;
+
+ int bcost;
+
+ bool paid = FALSE;
+
+ bool set_reward = FALSE;
+
+ bool recreate = FALSE;
+
+
+ if (is_state(s_ptr, STORE_LIKED))
+ {
+ bcost = ba_ptr->costs[STORE_LIKED];
+ }
+ else if (is_state(s_ptr, STORE_HATED))
+ {
+ bcost = ba_ptr->costs[STORE_HATED];
+ }
+ else
+ {
+ bcost = ba_ptr->costs[STORE_NORMAL];
+ }
+
+ /* action restrictions */
+ if (((ba_ptr->action_restr == 1) && (is_state(s_ptr, STORE_LIKED))) ||
+ ((ba_ptr->action_restr == 2) && (!is_state(s_ptr, STORE_LIKED))))
+ {
+ msg_print("You have no right to choose that!");
+ msg_print(NULL);
+ return FALSE;
+ }
+
+ /* If player has loan and the time is out, few things work in stores */
+ if (p_ptr->loan && !p_ptr->loan_time)
+ {
+ if ((bact != BACT_SELL) && (bact != BACT_VIEW_BOUNTIES) &&
+ (bact != BACT_SELL_CORPSES) &&
+ (bact != BACT_VIEW_QUEST_MON) &&
+ (bact != BACT_SELL_QUEST_MON) &&
+ (bact != BACT_EXAMINE) && (bact != BACT_STEAL) &&
+ (bact != BACT_PAY_BACK_LOAN))
+ {
+ msg_print("You are not allowed to do that until you have paid back your loan.");
+ msg_print(NULL);
+ return FALSE;
+ }
+ }
+
+ /* check gold */
+ if (bcost > p_ptr->au)
+ {
+ msg_print("You do not have the gold!");
+ msg_print(NULL);
+ return FALSE;
+ }
+
+ if (!bcost) set_reward = TRUE;
+
+ switch (bact)
+ {
+ case BACT_RESEARCH_ITEM:
+ {
+ paid = research_item();
+ break;
+ }
+
+ case BACT_TOWN_HISTORY:
+ {
+ town_history();
+ break;
+ }
+
+ case BACT_RACE_LEGENDS:
+ {
+ race_legends();
+ break;
+ }
+
+#if 0
+ case BACT_GREET_KING:
+ {
+ castle_greet();
+ break;
+ }
+
+#endif
+
+ case BACT_QUEST1:
+ case BACT_QUEST2:
+ case BACT_QUEST3:
+ case BACT_QUEST4:
+ {
+ int y = 1, x = 1;
+ bool ok = FALSE;
+
+ while ((x < cur_wid - 1) && !ok)
+ {
+ y = 1;
+ while ((y < cur_hgt - 1) && !ok)
+ {
+ /* Found the location of the quest info ? */
+ if (bact - BACT_QUEST1 + FEAT_QUEST1 == cave[y][x].feat)
+ {
+ /* Stop the loop */
+ ok = TRUE;
+ }
+ y++;
+ }
+ x++;
+ }
+
+ if (ok)
+ {
+ recreate = castle_quest(y - 1, x - 1);
+ ;
+ }
+ else
+ {
+ msg_format("ERROR: no quest info feature found: %d", bact - BACT_QUEST1 + FEAT_QUEST1);
+ }
+ break;
+ }
+
+ case BACT_KING_LEGENDS:
+ case BACT_ARENA_LEGENDS:
+ case BACT_LEGENDS:
+ {
+ show_highclass(building_loc);
+ break;
+ }
+
+ case BACT_POSTER:
+ case BACT_ARENA_RULES:
+ case BACT_ARENA:
+ {
+ arena_comm(bact);
+ break;
+ }
+
+ case BACT_IN_BETWEEN:
+ case BACT_CRAPS:
+ case BACT_SPIN_WHEEL:
+ case BACT_DICE_SLOTS:
+ case BACT_GAMBLE_RULES:
+ {
+ gamble_comm(bact);
+ break;
+ }
+
+ case BACT_REST:
+ case BACT_RUMORS:
+ case BACT_FOOD:
+ {
+ paid = inn_comm(bact);
+ break;
+ }
+
+ case BACT_RESEARCH_MONSTER:
+ {
+ paid = !research_mon();
+ break;
+ }
+
+ case BACT_COMPARE_WEAPONS:
+ {
+ paid = compare_weapons();
+ break;
+ }
+
+#if 0
+
+ case BACT_GREET:
+ {
+ greet_char();
+ break;
+ }
+
+#endif
+
+ case BACT_ENCHANT_WEAPON:
+ {
+ paid = fix_item(INVEN_WIELD, INVEN_WIELD, 0, FALSE,
+ BACT_ENCHANT_WEAPON, set_reward);
+ break;
+ }
+
+ case BACT_ENCHANT_ARMOR:
+ {
+ paid = fix_item(INVEN_BODY, INVEN_FEET, 0, TRUE,
+ BACT_ENCHANT_ARMOR, set_reward);
+ break;
+ }
+
+ /* needs work */
+ case BACT_RECHARGE:
+ {
+ if (recharge(80)) paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_IDENTS:
+ {
+ identify_pack();
+ msg_print("Your possessions have been identified.");
+ msg_print(NULL);
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_STAR_HEAL:
+ {
+ hp_player(200);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_cut(0);
+ set_stun(0);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ }
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_HEALING:
+ {
+ hp_player(200);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_cut(0);
+ set_stun(0);
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_RESTORE:
+ {
+ if (do_res_stat(A_STR, TRUE)) paid = TRUE;
+ if (do_res_stat(A_INT, TRUE)) paid = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) paid = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) paid = TRUE;
+ if (do_res_stat(A_CON, TRUE)) paid = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) paid = TRUE;
+ break;
+ }
+
+ /* set timed reward flag */
+ case BACT_GOLD:
+ {
+#if 0
+ if (!p_ptr->rewards[BACT_GOLD])
+ {
+ share_gold();
+ p_ptr->rewards[BACT_GOLD] = TRUE;
+ }
+ else
+ {
+ msg_print("You just had your daily allowance!");
+ msg_print(NULL);
+ }
+#endif
+ break;
+ }
+
+ case BACT_ENCHANT_ARROWS:
+ {
+ paid = fix_item(0, INVEN_WIELD, TV_ARROW, FALSE,
+ BACT_ENCHANT_ARROWS, set_reward);
+ break;
+ }
+
+ case BACT_ENCHANT_BOW:
+ {
+ paid = fix_item(INVEN_BOW, INVEN_BOW, TV_BOW, FALSE,
+ BACT_ENCHANT_BOW, set_reward);
+ break;
+ }
+
+ case BACT_RECALL:
+ {
+ p_ptr->word_recall = 1;
+ msg_print("The air about you becomes charged...");
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_TELEPORT_LEVEL:
+ {
+ if (reset_recall(FALSE))
+ {
+ p_ptr->word_recall = 1;
+ msg_print("The air about you becomes charged...");
+ paid = TRUE;
+ }
+ break;
+ }
+
+ case BACT_MIMIC_NORMAL:
+ {
+ set_mimic(0, 0, 0);
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_VIEW_BOUNTIES:
+ {
+ show_bounties();
+ break;
+ }
+
+ case BACT_VIEW_QUEST_MON:
+ {
+ show_quest_monster();
+ break;
+ }
+
+ case BACT_SELL_QUEST_MON:
+ {
+ sell_quest_monster();
+ break;
+ }
+
+ case BACT_SELL_CORPSES:
+ {
+ sell_corpses();
+ break;
+ }
+
+ case BACT_DIVINATION:
+ {
+ int i, count = 0;
+ bool something = FALSE;
+
+ while (count < 1000)
+ {
+ count++;
+ i = rand_int(MAX_FATES);
+ if (!fates[i].fate) continue;
+ if (fates[i].know) continue;
+ msg_print("You know a little more of your fate.");
+
+ fates[i].know = TRUE;
+ something = TRUE;
+ break;
+ }
+
+ if (!something) msg_print("Well, you have no fate, but I'll keep your money anyway!");
+
+ paid = TRUE;
+ break;
+
+ }
+
+ case BACT_BUY:
+ {
+ store_purchase();
+ break;
+ }
+
+ case BACT_SELL:
+ {
+ store_sell();
+ break;
+ }
+
+ case BACT_EXAMINE:
+ {
+ store_examine();
+ break;
+ }
+
+ case BACT_STEAL:
+ {
+ store_stole();
+ break;
+ }
+
+ case BACT_REQUEST_ITEM:
+ {
+ store_request_item();
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_GET_LOAN:
+ {
+ s32b i, req;
+ char prompt[80];
+
+ if (p_ptr->loan)
+ {
+ msg_print("You already have a loan!");
+ break;
+ }
+
+ req = p_ptr->au;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ req += object_value_real(&p_ptr->inventory[i]);
+
+ if (req > 100000) req = 100000;
+ if ((req + p_ptr->au) > PY_MAX_GOLD) req = PY_MAX_GOLD - p_ptr->au;
+
+ strnfmt(prompt, sizeof (prompt),
+ "How much would you like to get (0-%ld) ?", req);
+
+ req = get_quantity(prompt, req);
+
+ if (req)
+ {
+ p_ptr->loan += req;
+ p_ptr->au += req;
+ p_ptr->loan_time += req;
+
+ msg_format("You receive %i gold pieces", req);
+
+ paid = TRUE;
+ }
+ else
+ msg_format("You did not request any money!");
+
+ break;
+ }
+
+ case BACT_PAY_BACK_LOAN:
+ {
+ s32b req;
+ char prompt[80];
+
+ if (p_ptr->loan)
+ {
+ msg_format("You have nothing to payback!");
+ break;
+ }
+
+ msg_format("You have a loan of %i.", p_ptr->loan);
+
+ req = ((p_ptr->loan + bcost) > p_ptr->au) ? p_ptr->au - bcost : p_ptr->loan;
+
+ strnfmt(prompt, sizeof (prompt),
+ "How much would you like to pay back (0-%ld) ?", req);
+
+ req = get_quantity(prompt, req);
+
+ p_ptr->loan -= req;
+ p_ptr->au -= req;
+
+ if (!p_ptr->loan) p_ptr->loan_time = 0;
+
+ msg_format("You pay back %i gold pieces", req);
+ paid = TRUE;
+ break;
+ }
+
+ default:
+ {
+ if (process_hooks_ret(HOOK_BUILDING_ACTION, "dd", "(d)", bact))
+ {
+ paid = process_hooks_return[0].num;
+ recreate = process_hooks_return[1].num;
+ }
+ break;
+ }
+ }
+
+ if (paid)
+ {
+ p_ptr->au -= bcost;
+
+ /* Display the current gold */
+ store_prt_gold();
+ }
+
+ return (recreate);
+}
+
+
+/*
+ * Enter quest level
+ */
+void enter_quest(void)
+{
+ if (!(cave[p_ptr->py][p_ptr->px].feat == FEAT_QUEST_ENTER))
+ {
+ msg_print("You see no quest level here.");
+ return;
+ }
+ else
+ {
+ /* Player enters a new quest */
+ p_ptr->oldpy = p_ptr->py;
+ p_ptr->oldpx = p_ptr->px;
+
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = cave[p_ptr->py][p_ptr->px].special;
+ dun_level = 1;
+ p_ptr->leaving = TRUE;
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+}
+
+
+/*
+ * Do building commands
+ */
+void do_cmd_bldg(void)
+{
+ int i, which, x = p_ptr->px, y = p_ptr->py;
+
+ char command;
+
+ bool validcmd;
+
+ store_type *s_ptr;
+
+ store_info_type *st_ptr;
+
+ store_action_type *ba_ptr;
+
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_SHOP)
+ {
+ msg_print("You see no building here.");
+ return;
+ }
+
+ which = cave[p_ptr->py][p_ptr->px].special;
+ building_loc = which;
+
+ s_ptr = &town_info[p_ptr->town_num].store[which];
+ st_ptr = &st_info[which];
+
+ p_ptr->oldpy = p_ptr->py;
+ p_ptr->oldpx = p_ptr->px;
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+ /* Hack -- Increase "icky" depth */
+ character_icky++;
+
+ command_arg = 0;
+ command_rep = 0;
+ command_new = 0;
+
+ show_building(s_ptr);
+ leave_bldg = FALSE;
+
+ while (!leave_bldg)
+ {
+ validcmd = FALSE;
+ prt("", 1, 0);
+ command = inkey();
+
+ if (command == ESCAPE)
+ {
+ leave_bldg = TRUE;
+ p_ptr->inside_arena = FALSE;
+ break;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_info->actions[i]];
+
+ if (ba_ptr->letter)
+ {
+ if (ba_ptr->letter == command)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ if (ba_ptr->letter_aux)
+ {
+ if (ba_ptr->letter_aux == command)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (validcmd)
+ bldg_process_command(s_ptr, i);
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Reinit wilderness to activate quests ... */
+ wilderness_gen(TRUE);
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ /* Hack -- Decrease "icky" depth */
+ character_icky--;
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Update the visuals */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE | PU_MONSTERS | PU_BONUS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
diff --git a/src/carbon/Angband.icns b/src/carbon/Angband.icns
new file mode 100644
index 00000000..3d775739
--- /dev/null
+++ b/src/carbon/Angband.icns
Binary files differ
diff --git a/src/carbon/Carbon.r b/src/carbon/Carbon.r
new file mode 100644
index 00000000..e3194dd2
--- /dev/null
+++ b/src/carbon/Carbon.r
@@ -0,0 +1,1568 @@
+/*
+ * Minimal Resources for T.o.M.E.
+ *
+ * Turned into human-readable and programmer-friendly format by pelpel
+ *
+ *
+ * This might help mac/non-mac coders to play, modify, hack and do
+ * whatever they like with this file.
+ *
+ *
+ * Header files and reasons for their inclusion:
+ *
+ * MacTypes.r - 'STR ' and 'STR#'
+ * Finder.r - 'BNDL' and 'FREF'
+ * Dialogs.r - 'ALRT', 'DITL' and 'DLOG'
+ * Menus.r - 'MENU' and 'MBAR'
+ * Processes.r - 'SIZE'
+ */
+
+#include <MacTypes.r>
+#include <Finder.r>
+#include <Dialogs.r>
+#include <Menus.r>
+#include <Processes.r>
+
+
+#ifndef MACH_O
+
+/*
+ * Signature - Who am I?
+ * Vanilla uses 'A271'
+ * ID should always be 0.
+ */
+#define AngbandSignature 'PrnA'
+
+type AngbandSignature as 'STR ';
+
+resource AngbandSignature (0, "Owner resource", purgeable)
+{
+ "T.o.M.E. 2.3.4"
+};
+
+
+/* OS X Finder requires this to recognise a Carbon-compatible PEF binary */
+data 'plst' (0)
+{
+ "$00";
+};
+
+
+/*
+ * Inform system of program's characteristics
+ * ID should always be -1.
+ */
+resource 'SIZE' (-1)
+{
+ /*
+ * Flags dumped = 0101 1000 1100 0000
+ */
+ reserved,
+
+ /* accepts/ignores suspend&resume events? */
+ acceptSuspendResumeEvents,
+
+ reserved,
+
+ /* can use background null events */
+ canBackground,
+
+ /* activates own windows in response to OS events */
+ doesActivateOnFGSwitch,
+
+ /* app has a user interface */
+ backgroundAndForeground,
+
+ /* don't return mouse events in front window on resume */
+ dontGetFrontClicks,
+
+ /* applications use this */
+ ignoreAppDiedEvents,
+
+ /* works with 24- or 32-bit addr */
+ is32BitCompatible,
+
+ /* can use high-level events */
+ isHighLevelEventAware,
+
+ /* only local high-level events */
+ onlyLocalHLEvents,
+
+ /* can't use stationery documents */
+ notStationeryAware,
+
+ /* can't use inline services */
+ dontUseTextEditServices,
+
+ /* all windows redrawn when monitor(s) change */
+ notDisplayManagerAware,
+
+ reserved,
+ reserved,
+
+ /* preferred memory size */
+ 16 * (1024 * 1024),
+
+ /* minimum memory size */
+ 4 * (1024 * 1024)
+};
+
+
+/*
+ * File types used by Angband
+ */
+resource 'FREF' (128, purgeable)
+{
+ /* file type */
+ 'APPL',
+
+ /* maps to icon list resource w/ local ID 0 in bundle resource */
+ 0,
+
+ /* leave empty string for name */
+ ""
+};
+
+resource 'FREF' (129, purgeable)
+{
+ /* file type */
+ 'SAVE',
+
+ /* maps to icon list resource w/ local ID 1 in bundle resource */
+ 1,
+
+ /* leave empty string for name */
+ ""
+};
+
+resource 'FREF' (130, purgeable)
+{
+ /* file type */
+ 'TEXT',
+
+ /* maps to icon list resource w/ local ID 2 in bundle resource */
+ 2,
+
+ /* leave empty string for name */
+ ""
+};
+
+resource 'FREF' (131, purgeable)
+{
+ /* file type */
+ 'DATA',
+
+ /* maps to icon list resource w/ local ID 3 in bundle resource */
+ 3,
+
+ /* leave empty string for name */
+ ""
+};
+
+
+/*
+ * Bundle information
+ */
+resource 'BNDL' (128, purgeable)
+{
+ /* Our signature */
+ AngbandSignature,
+
+ /* resource ID of signature resource: should always be 0 */
+ 0,
+
+ {
+ /* mapping local IDs in 'FREF's to 'ICN#' IDs */
+ 'ICN#',
+ {
+ /* local ID 0 -> ICN# 128 */
+ 0, 128,
+
+ /* local ID 1 -> ICN# 129 */
+ 1, 129,
+
+ /* local ID 2 -> ICN# 130 */
+ 2, 130,
+
+ /* local ID 3 -> ICN# 131 */
+ 3, 131
+ },
+
+ /* local res IDs for 'FREF's: no duplicates */
+ 'FREF',
+ {
+ /* local ID 0 -> FREF 128 */
+ 0, 128,
+
+ /* local ID 1 -> FREF 129 */
+ 1, 129,
+
+ /* local ID 2 -> FREF 130 */
+ 2, 130,
+
+ /* local ID 3 -> FREF 131 */
+ 3, 131
+ }
+ }
+};
+
+#endif /* !MACH_O */
+
+/*
+ * Menu definitions
+ */
+resource 'MENU' (128, preload)
+{
+ /* menu ID */
+ 128,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* everything but the divider is enabled */
+ 0b11111111111111111111111111111101,
+ /* or we can use 0... */
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ apple,
+
+ /* its contents */
+ {
+ /* First item */
+ "About T.o.M.E. ...", noicon, nokey, nomark, plain;
+
+ /* Second item - divider */
+ "-", noicon, nokey, nomark, plain;
+ }
+};
+
+resource 'MENU' (129, preload)
+{
+ /* menu ID */
+ 129,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000011011,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "File",
+
+ /* its contents */
+ {
+#if 0
+ /* item #1 */
+ "New", noicon, "N", nomark, plain;
+
+ /* item #2 */
+ "Open", noicon, "O", nomark, plain;
+
+ /* item #3 */
+ "Import", noicon, "I", nomark, plain;
+#endif
+ /* item #1 (was #4) */
+ "Close", noicon, "W", nomark, plain;
+
+ /* item #2 (was #5) */
+ "Save", noicon, "S", nomark, plain;
+
+ /* item #3 (was #6) */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #4 (was #7) */
+ "Score", noicon, "H", nomark, plain;
+
+ /* item #4 (was #7) */
+ "Quit", noicon, "Q", nomark, plain;
+ }
+};
+
+
+resource 'MENU' (130, preload)
+{
+ /* menu ID */
+ 130,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000111101,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Edit",
+
+ /* its contents */
+ {
+ /* item #1 */
+ "Undo", noicon, "Z", nomark, plain;
+
+ /* item #2 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #3 */
+ "Cut", noicon, "X", nomark, plain;
+
+ /* item #4 */
+ "Copy", noicon, "C", nomark, plain;
+
+ /* item #5 */
+ "Paste", noicon, "V", nomark, plain;
+
+ /* item #6 */
+ "Clear", noicon, nokey, nomark, plain;
+ }
+};
+
+resource 'MENU' (131, preload)
+{
+ /* menu ID */
+ 131,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000011,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Font",
+
+ /* its contents */
+ {
+ /* item #1 */
+ "Bold", noicon, nokey, nomark, plain;
+
+ /* item #2 */
+ "Wide", noicon, nokey, nomark, plain;
+
+ /* item #3 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* the rest are supplied by the program */
+ }
+};
+
+resource 'MENU' (132, preload)
+{
+ /* menu ID */
+ 132,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Size",
+
+ /* its contents */
+ {
+ /* Let the program fill it in */
+ }
+};
+
+resource 'MENU' (133, preload)
+{
+ /* menu ID */
+ 133,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Windows",
+
+ /* its contents */
+ {
+ /* Let the program create them for us */
+ }
+};
+
+resource 'MENU' (134, preload)
+{
+ /* menu ID */
+ 134,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000011011,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "Special",
+
+ /* its contents */
+ {
+ /* item #1 */
+ "Sound", noicon, nokey, nomark, plain;
+
+ /* item #2 - 0x90 = 144 */
+ "Graphics", noicon, hierarchicalMenu, "\0x90", plain;
+
+ /* item #3 - 0x91 = 145 */
+ "TileWidth", noicon, hierarchicalMenu, "\0x91", plain;
+
+ /* item #4 - 0x92 = 146 */
+ "TileHeight", noicon, hierarchicalMenu, "\0x92", plain;
+
+ /* item #5 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #6 */
+ "Fiddle", noicon, nokey, nomark, plain;
+
+ /* item #7 */
+ "Wizard", noicon, nokey, nomark, plain;
+ }
+};
+
+/* Graphics submenu */
+resource 'MENU' (144, preload)
+{
+ /* menu ID */
+ 144,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000111,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title (ignored) */
+ "Graphics",
+
+ /* menu items */
+ {
+ /* item #1 */
+ "None", noicon, nokey, nomark, plain;
+
+ /* item #2 */
+ "8x8", noicon, nokey, nomark, plain;
+
+ /* item #3 */
+ "16x16", noicon, nokey, nomark, plain;
+
+ /* item #4 */
+ "32x32", noicon, nokey, nomark, plain;
+
+ /* item #5 */
+ "-", noicon, nokey, nomark, plain;
+
+ /* item #6 */
+ "Enlarge tiles", noicon, nokey, nomark, plain;
+ }
+};
+
+/* Tilewidth submenu */
+resource 'MENU' (145, preload)
+{
+ /* menu ID */
+ 145,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "TileWidth",
+
+ /* its contents */
+ {
+ /* Let the program create them for us */
+ }
+};
+
+/* TileHeight submenu */
+resource 'MENU' (146, preload)
+{
+ /* menu ID */
+ 146,
+
+ /* use standard definition proc */
+ textMenuProc,
+
+ /* let the program enable/disable them */
+ 0b00000000000000000000000000000000,
+
+ /* enable the title */
+ enabled,
+
+ /* menu title */
+ "TileHeight",
+
+ /* its contents */
+ {
+ /* Let the program create them for us */
+ }
+};
+
+/* Menu bar definition */
+resource 'MBAR' (128, preload)
+{
+ { 128, 129, 130, 131, 132, 133, 134 }
+};
+
+
+/*
+ * Dialogue item lists
+ */
+resource 'DITL' (129, purgeable)
+{
+ {
+ /** item #1 **/
+
+ /* bounding rect */
+ { 45, 353, 65, 411 },
+
+ /* type */
+ Button
+ {
+ /* enable flag */
+ enabled,
+
+ /* title */
+ "OK"
+ },
+
+ /** item #2 **/
+
+ /* bounding rect */
+ { 19, 68, 90, 339 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "^0"
+ },
+
+ /** item #3 **/
+
+ /* bounding rect */
+ { 38, 21, 70, 53 },
+
+ /* type */
+ Icon
+ {
+ /* enable flag */
+ disabled,
+
+ /* 'ICON' ID */
+ 128
+ }
+ }
+};
+
+
+resource 'DITL' (128, purgeable)
+{
+ {
+ /** item #1 **/
+
+ /* bounding rect */
+ { -4, 0, 225, 308 },
+
+ /* type */
+ UserItem
+ {
+ /* enable flag */
+ enabled
+ },
+
+ /** item #2 **/
+
+ /* bounding rect */
+ { 7, 108, 24, 235 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "T.o.M.E. 2.3.3"
+ },
+
+ /** item #3 **/
+
+ /* bounding rect */
+ { 36, 80, 53, 275 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Copyright (c) 1998-2003"
+ },
+
+ /** item #4 **/
+ { 53, 122, 70, 220 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "DarkGod"
+ },
+
+ /** item #5 **/
+
+ /* bounding rect */
+ { 70, 81, 87, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "(darkgod@ifrance.com)"
+ },
+
+ /** item #6 **/
+
+ /* bounding rect */
+ { 99, 88, 116, 266 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Original Copyright by"
+ },
+
+ /** item #7 **/
+
+ /* bounding rect */
+ { 135, 92, 151, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Robert A. Koeneke"
+ },
+
+ /** item #8 **/
+
+ /* bounding rect */
+ { 119, 103, 135, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "James E. Wilson"
+ },
+
+ /** item #9 **/
+
+ /* bounding rect */
+ { 150, 112, 166, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ "Ben Harrison"
+ },
+
+ /** item #10 */
+
+ /* bounding rect */
+ { 166, 62, 182, 145 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Topi Ylinen"
+ },
+
+ /** item #11 **/
+
+ /* bounding rect */
+ { 166, 148, 182, 294 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Robert Ruehlmann"
+ },
+
+ /** item #12 **/
+
+ /* bounding rect */
+ { 190, 96, 207, 255 },
+
+ /* type */
+ StaticText
+ {
+ /* enable flag */
+ disabled,
+
+ /* title */
+ "Macintosh Version"
+ }
+ }
+};
+
+resource 'ALRT' (130, purgeable)
+{
+ /* bounding rect */
+ { 144, 154, 283, 384 },
+
+ /* 'DITL' ID */
+ 130,
+
+ /* bold outline, draw alert and beeps */
+ {
+ /* stage 4 */
+ OK, visible, sound1;
+
+ /* stage 3 */
+ OK, visible, sound1;
+
+ /* stage 2 */
+ OK, visible, sound1;
+
+ /* stage 1 */
+ OK, visible, sound1;
+ },
+
+#if ALRT_RezTemplateVersion == 1
+
+ /* centered to parent window */
+ centerParentWindow
+
+#endif /* ALRT_RezTemplateVersion == 1 */
+};
+
+resource 'ALRT' (129, purgeable)
+{
+ /* bounding rect */
+ { 40, 40, 150, 471 },
+
+ /* 'DITL' ID */
+ 129,
+
+ /* bold outline, draw alert and beeps */
+ {
+ /* stage 4 */
+ OK, visible, sound1;
+
+ /* stage 3 */
+ OK, visible, sound1;
+
+ /* stage 2 */
+ OK, visible, sound1;
+
+ /* stage 1 */
+ OK, visible, sound1;
+ },
+
+#if ALRT_RezTemplateVersion == 1
+
+ /* centered to parent window */
+ centerParentWindow
+
+#endif /* ALRT_RezTemplateVersion == 1 */
+};
+
+resource 'ALRT' (128, purgeable)
+{
+ /* bounding rect */
+ { 40, 40, 150, 471 },
+
+ /* 'DITL' ID */
+ 129,
+
+ /* bold outline, draw alert and beeps */
+ {
+ /* stage 4 */
+ OK, visible, sound1;
+
+ /* stage 3 */
+ OK, visible, sound1;
+
+ /* stage 2 */
+ OK, visible, sound1;
+
+ /* stage 1 */
+ OK, visible, sound1;
+ },
+
+#if ALRT_RezTemplateVersion == 1
+
+ /* centered to parent window */
+ centerParentWindow
+
+#endif /* ALRT_RezTemplateVersion == 1 */
+};
+
+resource 'DLOG' (128, purgeable)
+{
+ /* bounding rect */
+ { 112, 202, 341, 512 },
+
+ /* procID */
+ dBoxProc,
+
+ /* visibility */
+ invisible,
+
+ /* has closebox? */
+ noGoAway,
+
+ /* refCon */
+ 0x0,
+
+ /* 'DITL' ID */
+ 128,
+
+ /* title */
+ "",
+
+#if DLOG_RezTemplateVersion == 1
+
+ /* position */
+ centerMainScreen
+
+#endif /* DLOG_RezTemplateVersion == 1 */
+};
+
+
+/*
+ * Additional resources for Carbon
+ */
+resource 'STR#' (128, purgeable)
+{
+ {
+ /* item #1 */
+ "Please select the \"lib\" folder"
+ }
+};
+
+
+/*
+ * Warning Icon (The ! one)
+ */
+data 'ICON' (128, purgeable) {
+ $"0001 8000 0003 C000 0003 C000 0006 6000"
+ $"0006 6000 000C 3000 000C 3000 0018 1800"
+ $"0019 9800 0033 CC00 0033 CC00 0063 C600"
+ $"0063 C600 00C3 C300 00C3 C300 0183 C180"
+ $"0183 C180 0303 C0C0 0303 C0C0 0603 C060"
+ $"0601 8060 0C01 8030 0C00 0030 1800 0018"
+ $"1801 8018 3003 C00C 3003 C00C 6001 8006"
+ $"6000 0006 C000 0003 FFFF FFFF 7FFF FFFE"
+};
+
+
+#ifndef MACH_O
+
+/*
+ * The JRRT icons we all know and love: you are not expected to change these,
+ * unless you are afraid of Tolkien Estate solicitors...
+ */
+
+data 'icl4' (129, purgeable) {
+ $"000F FFFF FFFF FFFF FFFF FFF0 0000 0000"
+ $"000F 0000 0000 0000 0000 0CFF 0000 0000"
+ $"000F 000F FFFF FFFF 0000 0CF0 F000 0000"
+ $"000F 000F 0F0F 0F0F 0000 0CF0 0F00 0000"
+ $"000F 0FFF FFFF FFFF FFFF FCF0 00F0 0000"
+ $"000F 0F0F 0F0F 0F0F 0F0F 0CF0 000F 0000"
+ $"000F 0FFF FFFF FFFF FFFF FCFF FFFF F000"
+ $"000F 000F 0F0F 0F0F 0000 00CC CCCC FC00"
+ $"000F 000F FFFF FFFF 0000 0000 0FF0 FC00"
+ $"000F 0000 000F 0F00 0000 0000 0F00 FC00"
+ $"000F 0000 000F FF00 0FFF FFFF FFF0 FC00"
+ $"000F 0000 000F 0F00 0F0F 0F0F 0F00 FC00"
+ $"000F 0FFF FFFF FFFF FFFF FFFF FFF0 FC00"
+ $"000F 0F0F 0F0F 0F0F 0F00 0000 0F00 FC00"
+ $"000F 0FFF FFFF FFFF F000 FFC0 0000 FC00"
+ $"000F 0F0F 0000 0000 00FF FC0F C000 FC00"
+ $"000F 0FFF 0000 00FC 000F FC0C 0FC0 FC00"
+ $"000F 0F0F 0000 00FF FFFF FFFF FFC0 FC00"
+ $"000F 0FFF FFFF 0FFC CFFF FFFC CFFC FC00"
+ $"000F 0F0F 0F0F 0CC0 FC0F FC0F CCC0 FC00"
+ $"000F 0FFF FFFF 0000 FC0F FC0F C000 FC00"
+ $"000F 0F0F 0F0F 0F00 0FFF FFFC 0000 FC00"
+ $"000F 0FFF FFFF FFF0 00FF FFC0 0000 FC00"
+ $"000F 0F0F 0F0F 0F00 0FCF FCF0 0000 FC00"
+ $"000F 0FFF FFFF FF0F FC0F FC0F FC00 FC00"
+ $"000F 0F0F 0F0F 0F0C C00F FC0C C000 FC00"
+ $"000F 0FFF FFFF FF00 0FCF FC00 0000 FC00"
+ $"000F 0F0F 0F0F 0F0F 0C0F FC00 0000 FC00"
+ $"000F 0FFF FFFF FFFF 000F CFFC 0000 FC00"
+ $"000F 0F0F 0F0F 0F0F 0FFC 0CC0 0000 FC00"
+ $"000F 0000 0000 0000 0CC0 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+};
+
+data 'icl4' (130, purgeable) {
+ $"000F FFFF FFFF FFFF FFFF FFF0 0000 0000"
+ $"000F 0000 0000 0000 0000 0CFF 0000 0000"
+ $"000F 0000 0000 0000 0000 0CF0 F000 0000"
+ $"000F 0000 FFFF FFFF FFFF CCF0 0F00 0000"
+ $"000F 0000 CCCC CCCC CCCC CCF0 00F0 0000"
+ $"000F 0000 0000 0000 0000 0CF0 000F 0000"
+ $"000F 00FF FFFF FFFF FFFF CCFF FFFF F000"
+ $"000F 00CC CCCC CCCC CCCC C0CC CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 00FF FFFF FFFF FFFF FFFF FFFC FC00"
+ $"000F 00CC CCCC CCCC CCCC CCCC CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 00FF FFFF FFFF FFFF FFFF FFFC FC00"
+ $"000F 00CC CCCC CCCC CCCC CCCC CCCC FC00"
+ $"000F 0000 0000 0000 0000 FFC0 0000 FC00"
+ $"000F 00FF FFFF C000 00FF FC0F C000 FC00"
+ $"000F 00CC CCCC C0FC 000F FC0C 0FC0 FC00"
+ $"000F 0000 0000 00FF FFFF FFFF FFC0 FC00"
+ $"000F 00FF FFFC 0FFC CFFF FFFC CFFC FC00"
+ $"000F 00CC CCCC 0CC0 FC0F FC0F CCC0 FC00"
+ $"000F 0000 0000 0000 FC0F FC0F C000 FC00"
+ $"000F 0000 FFFF FC00 0FFF FFFC 0000 FC00"
+ $"000F 0000 CCCC CC00 00FF FFC0 0000 FC00"
+ $"000F 0000 0000 0000 0FCF FCF0 0000 FC00"
+ $"000F 00FF FFFF FC0F FC0F FC0F FC00 FC00"
+ $"000F 00CC CCCC CC0C C00F FC0C C000 FC00"
+ $"000F 0000 0000 0000 0FCF FC00 0000 FC00"
+ $"000F 00FF FFFF FFC0 0C0F FC00 0000 FC00"
+ $"000F 00CC CCCC CCC0 000F CFFC 0000 FC00"
+ $"000F 0000 0000 0000 0FFC 0CC0 0000 FC00"
+ $"000F 0000 0000 0000 0CC0 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+};
+
+data 'icl4' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"F333 3333 3FFF 000F ED00 FFF3 3333 333F"
+ $"F333 333F F000 00FE D0ED 00FF F333 333F"
+ $"F333 33FF 0000 00DD 0FED 0FDF FF33 333F"
+ $"F333 3F00 0000 00FF 0ED0 00FD 00F3 333F"
+ $"F333 F000 0000 00CF FED0 00D0 000F 333F"
+ $"F33F 000F 0000 000F FED0 0000 0E00 F33F"
+ $"F3FF 00FF FFFF FFFF FFFF FFFF FFE0 FF3F"
+ $"F3F0 0FFF FFFF FFFF FFFF FFFF FFFE 0F3F"
+ $"FF00 FEED DDDD DDDF FEDD DDDD DDFF EDFF"
+ $"FF00 FDD0 000F FFFF FEFF FF00 0000 EDFF"
+ $"FF00 D000 00FF DDDF FEDD DFF0 0000 D0FF"
+ $"F000 0000 0FFD 000F FED0 00FE D000 000F"
+ $"F000 0000 0FFD 000F FED0 00FE D000 000F"
+ $"F000 0000 0FFD 000F FED0 00FE D000 000F"
+ $"F000 0000 00FF D00F FED0 0FFD 0000 000F"
+ $"F000 0000 000F FFFF FEFF FFD0 0000 000F"
+ $"F000 0000 000D DDFF FEFD DD00 0000 000F"
+ $"F000 0000 0000 0FFF FEFF 0000 0000 000F"
+ $"F000 0000 0000 FFDF FEDF F000 0000 000F"
+ $"FF00 0000 000F FD0F FED0 FF00 0000 00FF"
+ $"FF00 000F FFFF D00F FED0 0FFF FFD0 00FF"
+ $"FF00 0000 FFFD 000F FED0 00FF FD00 00FF"
+ $"F3F0 0000 DDD0 000F FED0 00DD D000 0F3F"
+ $"F3FF 0000 0000 F00F FED0 0000 0000 FF3F"
+ $"F33F 0000 000F DFDF FED0 0000 0000 F33F"
+ $"F333 F000 0000 FD0F FFD0 0000 000F 333F"
+ $"F333 3F00 0000 D00F FFF0 FFD0 00F3 333F"
+ $"F333 33FF 00F0 00FF FDFF FD00 FF33 333F"
+ $"F333 333F F00F FFFF D00D D00F F333 333F"
+ $"F333 3333 3FFF FFDD 0000 FFF3 3333 333F"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'icl4' (131, purgeable) {
+ $"000F FFFF FFFF FFFF FFFF FFF0 0000 0000"
+ $"000F 0000 0000 0000 0000 00FF 0000 0000"
+ $"000F 0F00 0F00 FF00 0FF0 00FC F000 0000"
+ $"000F 0FF0 FF0F 00F0 F00F 00FC 0F00 0000"
+ $"000F 0F0F 0F0F 00F0 F000 00FC 00F0 0000"
+ $"000F 0F00 0F0F FFF0 F000 00FC 000F 0000"
+ $"000F 0F00 0F0F 00F0 F00F 00FF FFFF F000"
+ $"000F 0F00 0F0F 00F0 0FF0 000C CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+ $"000F CCCC CCCC CCCC CCCC CCCC CCCC FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 0000 0000 0000 0000 0000 0000 FC00"
+ $"000F 0000 0000 0000 0000 FFC0 0000 FC00"
+ $"000F 0000 0000 0000 00FF FC0F C000 FC00"
+ $"000F 0000 0000 00FC 000F FC0C 0FC0 FC00"
+ $"000F 0000 0000 00FF FFFF FFFF FFC0 FC00"
+ $"000F 0000 0000 0FFC CFFF FFFC CFFC FC00"
+ $"000F 0000 0000 0CC0 FC0F FC0F CCC0 FC00"
+ $"000F 0000 0000 0000 FC0F FC0F C000 FC00"
+ $"000F 0000 0000 0000 0FFF FFFC 0000 FC00"
+ $"000F 0000 0000 0000 00FF FFC0 0000 FC00"
+ $"000F 0000 0000 0000 0FCF FCF0 0000 FC00"
+ $"000F 0000 0000 000F FC0F FC0F FC00 FC00"
+ $"000F 0000 0000 000C C00F FC0C C000 FC00"
+ $"000F 0000 0000 0000 0FCF FC00 0000 FC00"
+ $"000F 0000 0000 0000 0C0F FC00 0000 FC00"
+ $"000F 0000 0000 0000 000F CFFC 0000 FC00"
+ $"000F 0000 0000 0000 0FFC 0CC0 0000 FC00"
+ $"000F 0000 0000 0000 0CC0 0000 0000 FC00"
+ $"000F FFFF FFFF FFFF FFFF FFFF FFFF FC00"
+};
+
+data 'ICN#' (128, purgeable) {
+ $"FFFF DFFF FFF1 8FFF FF83 23FF FF00 657F"
+ $"FC03 423F F801 C01F F101 C04F F3FF FFEF"
+ $"E7FF FFF7 CE01 C03B C81F FC0B C031 C603"
+ $"8061 C301 8061 C301 8061 C301 8031 C601"
+ $"801F FC01 8003 E001 8007 F001 800D D801"
+ $"C019 CC03 C1F1 C7C3 C0E1 C383 E001 C007"
+ $"F009 C00F F015 C00F F809 C01F FC01 EC3F"
+ $"FF23 B8FF FF9F 01FF FFFC 0FFF FFF3 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'ICN#' (129, purgeable) {
+ $"1FFF FE00 1000 0300 11FF 0280 1155 0240"
+ $"17FF FA20 1555 5210 17FF FBF8 1155 0008"
+ $"11FF 0068 1014 0048 101C 7FE8 1014 5548"
+ $"17FF FFE8 1555 4008 17FF 8C08 1500 3908"
+ $"1702 1848 1503 FFC8 17F6 7E68 1550 9908"
+ $"17F0 9908 1554 7E08 17FE 3C08 1554 5A08"
+ $"17FD 9988 1554 1808 17FC 5808 1555 1808"
+ $"17FF 1608 1555 6008 1000 0008 1FFF FFF8"
+ $"1FFF FE00 1FFF FF00 1FFF FF80 1FFF FFC0"
+ $"1FFF FFE0 1FFF FFF0 1FFF FFF8 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+};
+
+data 'ICN#' (130, purgeable) {
+ $"1FFF FE00 1000 0300 1000 0280 10FF F240"
+ $"1000 0220 1000 0210 13FF F3F8 1000 0008"
+ $"1000 0008 13FF FFE8 1000 0008 1000 0008"
+ $"13FF FFE8 1000 0008 1000 0C08 13F0 3908"
+ $"1002 1848 1003 FFC8 13E6 7E68 1000 9908"
+ $"1000 9908 10F8 7E08 1000 3C08 1000 5A48"
+ $"13F9 9988 1000 1808 1000 5808 13FC 1808"
+ $"1000 1608 1000 6008 1000 0008 1FFF FFF8"
+ $"1FFF FE00 1FFF FF00 1FFF FF80 1FFF FFC0"
+ $"1FFF FFE0 1FFF FFF0 1FFF FFF8 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+};
+
+data 'ICN#' (131, purgeable) {
+ $"1FFF FE00 1000 0300 144C 6280 16D2 9240"
+ $"1552 8220 145E 8210 1452 93F8 1452 6008"
+ $"1000 0008 1FFF FFF8 1000 0008 1000 0008"
+ $"1000 0008 1000 0008 1000 0C08 1000 3908"
+ $"1002 1848 1003 FFC8 1006 7E68 1000 9908"
+ $"1000 9908 1000 7E08 1000 3C08 1000 5A48"
+ $"1001 9988 1000 1808 1000 5808 1000 1808"
+ $"1000 1608 1000 6008 1000 0008 1FFF FFF8"
+ $"1FFF FE00 1FFF FF00 1FFF FF80 1FFF FFC0"
+ $"1FFF FFE0 1FFF FFF0 1FFF FFF8 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFFC 1FFF FFFC"
+};
+
+data 'ics#' (128, purgeable) {
+ $"FFFF F99F F18F FFFF E7E3 8991 8991 87E1"
+ $"83C1 85A1 9999 C183 E587 F18F F97F FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'ics#' (129, purgeable) {
+ $"7FF0 4018 5FD4 555E 5FC2 4412 5FFA 5502"
+ $"5F12 557E 5F3A 5556 5F3A 551A 4022 7FFE"
+ $"7FF0 7FF8 7FFC 7FFE 7FFE 7FFE 7FFE 7FFE"
+ $"7FFE 7FFE 7FFE 7FFE 7FFE 7FFE 7FFE 7FFE"
+};
+
+data 'ics#' (130, purgeable) {
+ $"7FF0 4018 5FD4 401E 5FC2 4002 5FFA 4002"
+ $"5F12 407E 5F3A 4056 5F3A 401A 4022 7FFE"
+ $"7FF0 7FF8 7FFC 7FFE 7FFF 7FFF 7FFF 7FFF"
+ $"7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF"
+};
+
+data 'ics#' (131, purgeable) {
+ $"7FF0 4018 5FD4 5F9E 57C2 4002 7FFE 4002"
+ $"4012 407E 403A 4056 403A 401A 4022 7FFE"
+ $"7FF0 7FF8 7FFC 7FFE 7FFF 7FFF 7FFF 7FFF"
+ $"7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF 7FFF"
+};
+
+data 'icl8' (129, purgeable) {
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F7 FFFF 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5FF FFFF FFFF FFFF FFFF"
+ $"F5F5 F5F5 F5F7 FFF5 FF00 0000 0000 0000"
+ $"0000 00FF F5F5 F5FF F5FF F5FF F5FF F5FF"
+ $"F5F5 F5F5 F5F7 FFF5 F5FF 0000 0000 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFF7 FFF5 F5F5 FF00 0000 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5FF F5FF F5F7 FFF5 F5F5 F5FF 0000 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFF7 FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF F5F5 F5FF F5FF F5FF F5FF F5FF"
+ $"F5F5 F5F5 F5F5 F7F7 F7F7 F7F7 FFF7 0000"
+ $"0000 00FF F5F5 F5FF FFFF FFFF FFFF FFFF"
+ $"F5F5 F5F5 F5F5 F5F5 F5FF FFF5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5FF F5FF F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5FF F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5FF FFFF F5F5"
+ $"F5FF FFFF FFFF FFFF FFFF FFF5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5FF F5FF F5F5"
+ $"F5FF F5FF F5FF F5FF F5FF F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFF5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5FF F5F5 F5F5 F5F5 F5FF F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFF5 F5F5 FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 FFFF FFF8 F7FF F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF F5F5 F5F5 F5F5 FFF8"
+ $"F6F5 F5FF FFF8 F6F8 F6FF F8F6 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5F5 F5F5 F5F5 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F8F6 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF F5FF FFF8"
+ $"F8FF FFFF FFFF FFF8 F6FF FFF8 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5F8 F8F6"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F6F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF F5F6 F6F5"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5F5"
+ $"F5FF FFFF FFFF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFF5"
+ $"F5F5 FFFF FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5F5"
+ $"F5FF F8FF FFF8 FFF5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF F5FF"
+ $"FFF8 F6FF FFF8 F6FF FFF8 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5F8"
+ $"F8F6 F5FF FFF8 F6F8 F8F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF F5F6"
+ $"F6FF F8FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5F8 F6FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"F5F6 F5FF F8FF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5FF F5FF"
+ $"F5FF FFF8 F7F8 F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F8F7 F6F6 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+};
+
+data 'icl8' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF F9FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFD7 D7D7 D7D7 D7D7 D7FF FFFF 0000 00FF"
+ $"FCF9 F7F7 FFFF FFD7 D7D7 D7D7 D7D7 6BFF"
+ $"FFD7 4747 4747 4747 FF00 0000 F5F5 FFFC"
+ $"F9F7 FCF8 F7F5 FFFF FF47 4747 4747 6BFF"
+ $"FFD7 4747 4747 FFFF 00F5 F5F5 F5F5 F9F9"
+ $"F7FF FCF8 F7FF F7FF F9FF 4747 4747 6BFF"
+ $"FFD7 4747 47FF 0000 F5F5 F5F5 F5F5 FFFF"
+ $"F7FC F9F7 F5F5 FFF9 F7F5 FF47 4747 6BFF"
+ $"FFD7 4747 FF00 F5F5 F5F5 F5F5 F5F5 F7FF"
+ $"FFFC F9F7 F5F5 F9F7 F5F5 F5FF 4747 6BFF"
+ $"FFD7 47FF 00F5 F5FF F5F5 F5F5 F5F5 F5FF"
+ $"FFFC F9F7 F5F5 F5F5 F5FC F5F5 FF47 6BFF"
+ $"FFD7 47FF 00F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FCF5 FF47 6BFF"
+ $"FFD7 FF00 F5FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFC F5FF 6BFF"
+ $"FFFF 00F7 FFFC FCF9 F9F9 F9F9 F9F9 F9FF"
+ $"FFFC F9F9 F9F9 F9F9 F9F9 FFFF FCF9 FFFF"
+ $"FFFF 00F7 FCF9 F9F7 F7F7 F7FF FFFF FFFF"
+ $"FFFC FFFF FFFF F7F7 F7F7 F7F7 FCF9 FFFF"
+ $"FFFF 00F7 F9F7 F7F5 F5F5 FFFF F8F8 F8FF"
+ $"FFFC F9F9 F9FF FFF7 F7F5 F5F7 F9F7 FFFF"
+ $"FFF5 F5F5 F7F5 F5F5 F5FF FFF9 F7F7 F7FF"
+ $"FFFC F9F7 F7F7 FFFC F8F7 F5F5 F7F7 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5FF FFF9 F7F5 F5FF"
+ $"FFFC F9F7 F5F5 FFFC F8F7 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5FF FFF9 F7F5 F5FF"
+ $"FFFC F9F7 F5F5 FFFC F8F7 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 FFFF F9F7 F5FF"
+ $"FFFC F9F7 F5FF FFF8 F7F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5FF FFFF FFFF"
+ $"FFFC FFFF FFFF F8F7 F5F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5F9 F9F9 FFFF"
+ $"FFFC FFF9 F9F9 F7F5 F5F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5F5 F5FF FFFF"
+ $"FFFC FFFF F7F7 F5F5 F5F5 F5F5 F5F5 F8FF"
+ $"FF00 F5F5 F5F5 F5F5 F5F5 F5F5 FFFF F9FF"
+ $"FFFC F9FF FFF5 F5F5 F5F5 F5F5 F5F5 F8FF"
+ $"FFFF F5F5 F5F5 F5F5 F5F5 F5FF FFF9 F7FF"
+ $"FFFC F9F7 FFFF F5F5 F5F5 F5F5 F5F8 FFFF"
+ $"FFFF F5F5 F5F5 F7FF FFFF FFFF F9F7 F5FF"
+ $"FFFC F9F7 F7FF FFFF FFFF F9F5 F5F8 FFFF"
+ $"FFFF F5F5 F5F5 F5F7 FFFF FFF9 F7F5 F5FF"
+ $"FFFC F9F7 F5F7 FFFF FFF9 F7F5 F5F8 FFFF"
+ $"FFD7 FFF5 F5F5 F5F7 F9F9 F9F7 F5F5 F5FF"
+ $"FFFC F9F7 F5F5 F9F9 F9F7 F5F5 F8FF 6BFF"
+ $"FFD7 47FF F5F5 F5F5 F7F7 F7F5 FFF6 F5FF"
+ $"FFFC F9F7 F5F5 F5F7 F7F5 F5F8 FF47 6BFF"
+ $"FFD7 47FF F5F5 F5F5 F5F5 F5FF F7FF F8FF"
+ $"FFFC F9F7 F5F5 F5F5 F5F5 F5F8 FF47 6BFF"
+ $"FFD7 4747 FFF5 F5F5 F5F5 F5F5 FFF8 F7FF"
+ $"FFFF F9F7 F5F5 F5F5 F5F5 F8FF 4747 6BFF"
+ $"FFD7 4747 47FF F5F5 F5F5 F5F5 F8F7 F5FF"
+ $"FFFF FFF7 FFFF F9F7 F8F8 FF47 4747 6BFF"
+ $"FFD7 4747 4747 FFFF F5F5 FFF5 F7F5 FFFF"
+ $"FFF8 FFFF FFF9 F7F8 FFFF 4747 4747 6BFF"
+ $"FFD7 4747 4747 4747 FFF5 F5FF FFFF FFFF"
+ $"F8F6 F5F9 F9F7 F5FF 4747 4747 4747 6BFF"
+ $"FFD7 6B6B 6B6B 6B6B 6BFF FFFF FFFF F8F8"
+ $"F8F8 F8F5 FFFF FF6B 6B6B 6B6B 6B6B 6BFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF F8F8 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'icl8' (130, purgeable) {
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F8 FFFF 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F8 FFF6 FF00 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF F8F8 FFF6 F6FF 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 FFF6 F6F6 FF00 0000 0000"
+ $"0000 00FF F5F5 F5F5 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 F6F8 FFF6 F6F6 F6FF 0000 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF F8F8 FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F5 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F6F6 F6F6 FFFF F8F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF F8F5 F5F5"
+ $"F5F5 FFFF FFF8 F6FF F8F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F5 FFF8"
+ $"F5F5 F5FF FFF8 F5F8 F5FF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F5 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFF8 F6FF FFF8"
+ $"F8FF FFFF FFFF FFF8 F8FF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F6F8 F8F5"
+ $"FFF8 F6FF FFF8 F6FF F8F8 F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F5 F5F5"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 FFFF FFFF FFF8 F6F5"
+ $"F5FF FFFF FFFF FFF8 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F8F8 F8F8 F8F8 F6F5"
+ $"F5F5 FFFF FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F6F6 F6F6 F6F6 F6F5"
+ $"F5FF F8FF FFF8 FFF6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFF8 F6FF"
+ $"FFF8 F6FF FFF8 F6FF FFF8 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F6F8"
+ $"F8F6 F5FF FFF8 F6F8 F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F5"
+ $"F6FF F8FF FFF8 F6F6 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 FFFF FFFF FFFF FFFF F8F6"
+ $"F5F8 F6FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F8F8 F8F8 F8F8 F8F8 F8F6"
+ $"F5F6 F5FF F8FF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F6F6 F6F6 F6F6 F6F6 F6F6"
+ $"F5FF FFF8 F6F8 F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F8F6 F5F5 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+};
+
+data 'icl8' (131, purgeable) {
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 FFFF 0000 0000 0000 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5F5 FFFF F5F5"
+ $"F5FF FFF5 F5F5 FFF8 FF00 0000 0000 0000"
+ $"0000 00FF F5FF FFF5 FFFF F5FF 00F5 FFF5"
+ $"FF00 00FF F5F5 FFF8 F6FF 0000 0000 0000"
+ $"0000 00FF F5FF F5FF F5FF F5FF F5F5 FFF5"
+ $"FF00 0000 F5F5 FFF8 F6F6 FF00 0000 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5FF FFFF FFF5"
+ $"FF00 F5F5 F5F5 FFF8 F6F6 F6FF 0000 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5FF F5F5 FFF5"
+ $"FF00 F5FF 00F5 FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF F5FF F5F5 F5FF F5FF F5F5 FFF5"
+ $"00FF FF00 0000 F5F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F500 00F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+ $"0000 00FF F8F8 F8F8 F8F8 F8F8 F8F8 F8F8"
+ $"F8F8 F8F8 F8F8 F8F8 F8F8 F8F8 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 F500 FFFF F8F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 FFFF FFF8 F6FF F8F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 FFF8"
+ $"F5F5 F5FF FFF8 F5F8 F5FF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5FF FFF8"
+ $"F8FF FFFF FFFF FFF8 F8FF FFF8 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F8 F8F5"
+ $"FFF8 F6FF FFF8 F6FF F8F8 F8F6 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"FFF8 F6FF FFF8 F6FF F8F6 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5FF FFFF FFFF FFF8 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F5 FFFF FFFF F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5FF F8FF FFF8 FFF6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5FF"
+ $"FFF8 F6FF FFF8 F6FF FFF8 F6F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F8"
+ $"F8F6 F5FF FFF8 F6F8 F8F6 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F6FF F8FF FFF8 F6F6 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F6FF FFF8 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F6 F5FF F8FF FFF8 F6F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5FF FFF8 F6F8 F8F6 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5"
+ $"F5F8 F8F6 F5F5 F6F5 F5F5 F5F5 FFF7 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFF7 0000"
+};
+
+data 'ics8' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF47 4747 FFFF F6FF FFF8 FFFF 4747 47FF"
+ $"FF47 47FF F6F6 F6FF FFF8 F6F6 FF47 47FF"
+ $"FF47 FFFF FFFF FFFF FFFF FFFF FFFF 47FF"
+ $"FF47 FFF8 F8FF FFFF FFFF FFF8 F8F8 47FF"
+ $"FFFF F8F6 FFF8 F8FF FFF8 F8FF F8F6 FFFF"
+ $"FFF6 F6F6 FFF8 F6FF FFF8 F6FF F8F6 F6FF"
+ $"FFF6 F6F6 F6FF FFFF FFFF FFF8 F6F6 F6FF"
+ $"FFF6 F6F6 F6F6 FFFF FFFF F8F6 F6F6 F6FF"
+ $"FFF6 F6F6 F6FF F8FF FFF8 FFF6 F6F6 F6FF"
+ $"FFFF F6FF FFF8 F6FF FFF8 F6FF FFF8 FFFF"
+ $"FFFF F6F8 F8F6 F6FF FFF8 F6F8 F8F8 47FF"
+ $"FF47 FFF6 F6FF F8FF FFF8 F6F6 F6FF 47FF"
+ $"FF47 47FF F6F8 F6FF FFF8 F6F6 FF47 47FF"
+ $"FF47 4747 FFFF F6FF F8FF FFFF 4747 47FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+};
+
+data 'ics8' (129, purgeable) {
+ $"00FF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FF00 0000"
+ $"00FF F5FF FFFF FFFF FFFF F5FF F5FF 0000"
+ $"00FF F5FF F5FF F5FF F5FF F5FF FFFF FF00"
+ $"00FF F5FF FFFF FFFF FFFF F5F5 F5F5 FF00"
+ $"00FF F5F5 F5FF F5F5 F5F5 F5FF F5F5 FF00"
+ $"00FF F5FF FFFF FFFF FFFF FFFF FFF5 FF00"
+ $"00FF F5FF F5FF F5FF F5F5 F5F5 F5F5 FF00"
+ $"00FF F5FF FFFF FFFF F5F5 F5FF F5F5 FF00"
+ $"00FF F5FF F5FF F5FF F5FF FFFF FFFF FF00"
+ $"00FF F5FF FFFF FFFF F5F5 FFFF FF00 FF00"
+ $"00FF F5FF F5FF F5FF F5FF 00FF 00FF FF00"
+ $"00FF F5FF FFFF FFFF F5F5 FFFF FFF5 FF00"
+ $"00FF F5FF F5FF F5FF F5F5 F5FF FFF5 FF00"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFF5 F5F5 FF00"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+};
+
+data 'ics8' (130, purgeable) {
+ $"00FF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FF00 0000"
+ $"00FF F5FF FFFF FFFF FFFF F7FF F5FF 0000"
+ $"00FF F5F7 F7F7 F7F7 F7F7 F7FF FFFF FF00"
+ $"00FF F5FF FFFF FFFF FFFF F7F5 F5F5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7F7 F7F5 F5F5 FFF7"
+ $"00FF F5FF FFFF FFFF FFFF FFFF FFF7 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7F7 F7F7 F7F7 FFF7"
+ $"00FF F5FF FFFF FFFF F7F5 F5FF F5F5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7FF FFFF FFFF FFF7"
+ $"00FF F5FF FFFF FFFF F7F5 FFFF FFF5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7FF F5FF F5FF FFF7"
+ $"00FF F5FF FFFF FFFF F7F5 FFFF FFF5 FFF7"
+ $"00FF F5F7 F7F7 F7F7 F7F5 F5FF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFF5 F5F5 FFF7"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFF7"
+};
+
+data 'ics8' (131, purgeable) {
+ $"00FF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FF00 0000"
+ $"00FF F5FF FFFF FFFF FFFF F5FF F5FF 0000"
+ $"00FF F5FF FFFF FFFF FF00 F5FF FFFF FF00"
+ $"00FF F5FF 00FF FFFF FFFF F5F5 F7F7 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5F5 F5F5 FFF7"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFF7"
+ $"00FF F7F7 F7F7 F7F7 F7F7 F7F7 F7F7 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF F5F5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5FF FFFF FFFF FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFFF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5FF F5FF F5FF FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFFF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 F5FF FFF5 FFF7"
+ $"00FF F5F5 F5F5 F5F5 F5F5 FFF5 F5F5 FFF7"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFF7"
+};
+
+data 'ics4' (128, purgeable) {
+ $"FFFF FFFF FFFF FFFF F333 FFCF FCFF 333F"
+ $"F33F CCCF FCCC F33F F3FF FFFF FFFF FF3F"
+ $"FFFC CFFF FFFC CCFF FFCC FCCF FCCF CCFF"
+ $"FCCC FCCF FCCF CCCF FCCC CFFF FFFC CCCF"
+ $"FCCC CCFF FFCC CCCF FCCC CFCF FCFC CCCF"
+ $"FFCF FCCF FCCF FCFF FFCC CCCF FCCC CCFF"
+ $"F3FC CFCF FCCC CF3F F33F CCCF FCCC F33F"
+ $"F333 FFCF CFFF 333F FFFF FFFF FFFF FFFF"
+};
+
+data 'ics4' (129, purgeable) {
+ $"0FFF FFFF FFFF 0000 0F00 0000 000F F000"
+ $"0F0F FFFF FF0F 0F00 0F0F 0F0F 0F0F FFF0"
+ $"0F0F FFFF FF00 00F0 0F00 0F00 000F 00F0"
+ $"0F0F FFFF FFFF F0F0 0F0F 0F0F 0000 00F0"
+ $"0F0F FFFF 000F 00F0 0F0F 0F0F 0FFF FFF0"
+ $"0F0F FFFF 00FF F0F0 0F0F 0F0F 0F0F 0FF0"
+ $"0F0F FFFF 00FF F0F0 0F0F 0F0F 000F F0F0"
+ $"0F00 0000 00F0 00F0 0FFF FFFF FFFF FFF0"
+};
+
+data 'ics4' (130, purgeable) {
+ $"0FFF FFFF FFFF 0000 0FCC CCCC CCCF F000"
+ $"0FCF FFFF FFCF CF00 0FCC CCCC CCCF FFF0"
+ $"0FCF FFFF FFCC CCFD 0FCC CCCC CCCC CCFD"
+ $"0FCF FFFF FFFF FCFD 0FCC CCCC CCCC CCFD"
+ $"0FCF FFFF CCCF CCFD 0FCC CCCC CFFF FFFD"
+ $"0FCF FFFF CCFF FCFD 0FCC CCCC CFCF CFFD"
+ $"0FCF FFFF CCFF FCFD 0FCC CCCC CCCF FCFD"
+ $"0FCC CCCC CCFC CCFD 0FFF FFFF FFFF FFFD"
+};
+
+data 'ics4' (131, purgeable) {
+ $"0FFF FFFF FFFF 0000 0FCC CCCC CCCF F000"
+ $"0FCF FFFF FFCF CF00 0FCF FFFF FCCF FFF0"
+ $"0FCF CFFF FFCC DDFD 0FCC CCCC CCCC CCFD"
+ $"0FFF FFFF FFFF FFFD 0FCC CCCC CCCC CCFD"
+ $"0FCC CCCC CCCF CCFD 0FCC CCCC CFFF FFFD"
+ $"0FCC CCCC CCFF FCFD 0FCC CCCC CFCF CFFD"
+ $"0FCC CCCC CCFF FCFD 0FCC CCCC CCCF FCFD"
+ $"0FCC CCCC CCFC CCFD 0FFF FFFF FFFF FFFD"
+};
+
+#endif /* !MACH_O */
+
diff --git a/src/carbon/Data.icns b/src/carbon/Data.icns
new file mode 100644
index 00000000..dbaec173
--- /dev/null
+++ b/src/carbon/Data.icns
Binary files differ
diff --git a/src/carbon/Edit.icns b/src/carbon/Edit.icns
new file mode 100644
index 00000000..5e55c272
--- /dev/null
+++ b/src/carbon/Edit.icns
Binary files differ
diff --git a/src/carbon/Image-DS_Store b/src/carbon/Image-DS_Store
new file mode 100644
index 00000000..efd42e72
--- /dev/null
+++ b/src/carbon/Image-DS_Store
Binary files differ
diff --git a/src/carbon/Info.plist b/src/carbon/Info.plist
new file mode 100644
index 00000000..fff3fb15
--- /dev/null
+++ b/src/carbon/Info.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plist version="1.0">
+ <dict>
+ <key>CFBundleName</key><string>T.o.M.E.</string>
+ <key>CFBundleDisplayName</key><string>T.o.M.E. (OS X)</string>
+ <key>CFBundleExecutable</key><string>tome</string>
+ <key>CFBundlePackageType</key><string>APPL</string>
+ <key>CFBundleSignature</key><string>PrnA</string>
+ <key>CFBundleVersion</key><string>2.3.4</string>
+ <key>CFBundleShortVersionString</key><string>234</string>
+ <key>CFBundleIconFile</key><string>Angband</string>
+ <key>CFBundleIdentifier</key><string>net.t-o-m-e.tome</string>
+ <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ <key>CFBundleTypeIconFile</key><string>Save</string>
+ <key>CFBundleTypeName</key><string>Angband game data</string>
+ <key>CFBundleTypeOSTypes</key><array><string>SAVE</string></array>
+ <key>CFBundleTypeRole</key><string>Editor</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ <key>CFBundleTypeIconFile</key><string>Edit</string>
+ <key>CFBundleTypeName</key><string>Angband game data</string>
+ <key>CFBundleTypeOSTypes</key><array><string>TEXT</string></array>
+ <key>CFBundleTypeRole</key><string>Editor</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ <key>CFBundleTypeIconFile</key><string>Data</string>
+ <key>CFBundleTypeName</key><string>Angband game data</string>
+ <key>CFBundleTypeOSTypes</key><array><string>DATA</string></array>
+ <key>CFBundleTypeRole</key><string>Editor</string>
+ </dict>
+ </array>
+ </dict>
+</plist>
diff --git a/src/carbon/Save.icns b/src/carbon/Save.icns
new file mode 100644
index 00000000..362f80f6
--- /dev/null
+++ b/src/carbon/Save.icns
Binary files differ
diff --git a/src/carbon/getversion b/src/carbon/getversion
new file mode 100755
index 00000000..786fd14b
--- /dev/null
+++ b/src/carbon/getversion
@@ -0,0 +1,2 @@
+#! /bin/sh
+grep CFBundleVersion carbon/Info.plist | perl -pe "s,.*<string>([^<]*)<.*,\\1,"
diff --git a/src/cave.c b/src/cave.c
new file mode 100644
index 00000000..fa65b321
--- /dev/null
+++ b/src/cave.c
@@ -0,0 +1,5266 @@
+/* File: cave.c */
+
+/* Purpose: low level dungeon routines -BEN- */
+
+
+#include "angband.h"
+
+
+/*
+ * Support for Adam Bolt's tileset, lighting and transparency effects
+ * by Robert Ruehlmann (rr9@angband.org)
+ */
+
+
+/*
+ * Approximate Distance between two points.
+ *
+ * When either the X or Y component dwarfs the other component,
+ * this function is almost perfect, and otherwise, it tends to
+ * over-estimate about one grid per fifteen grids of distance.
+ *
+ * Algorithm: hypot(dy,dx) = max(dy,dx) + min(dy,dx) / 2
+ */
+int distance(int y1, int x1, int y2, int x2)
+{
+ int dy, dx, d;
+
+
+ /* Find the absolute y/x distance components */
+ dy = (y1 > y2) ? (y1 - y2) : (y2 - y1);
+ dx = (x1 > x2) ? (x1 - x2) : (x2 - x1);
+
+ /* Hack -- approximate the distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+
+ /* Return the distance */
+ return (d);
+}
+
+
+/*
+ * Returns TRUE if a grid is considered to be a wall for the purpose
+ * of magic mapping / clairvoyance
+ */
+static bool is_wall(cave_type *c_ptr)
+{
+ byte feat;
+
+
+ /* Handle feature mimics */
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else feat = c_ptr->feat;
+
+ /* Paranoia */
+ if (feat >= max_f_idx) return FALSE;
+
+ /* Vanilla floors and doors aren't considered to be walls */
+ if (feat < FEAT_SECRET) return FALSE;
+
+ /* Exception #1: a glass wall is a wall but doesn't prevent LOS */
+ if (feat == FEAT_GLASS_WALL) return FALSE;
+
+ /* Exception #2: an illusion wall is not a wall but obstructs view */
+ if (feat == FEAT_ILLUS_WALL) return TRUE;
+
+ /* Exception #3: a small tree is a floor but obstructs view */
+ if (feat == FEAT_SMALL_TREES) return TRUE;
+
+ /* Normal cases: use the WALL flag in f_info.txt */
+ return (f_info[feat].flags1 & FF1_WALL) ? TRUE : FALSE;
+}
+
+
+/*
+ * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall,
+ * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu.
+ *
+ * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2).
+ *
+ * The LOS begins at the center of the tile (x1,y1) and ends at the center of
+ * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line
+ * passes through must be floor tiles, except for (x1,y1) and (x2,y2).
+ *
+ * We assume that the "mathematical corner" of a non-floor tile does not
+ * block line of sight.
+ *
+ * Because this function uses (short) ints for all calculations, overflow may
+ * occur if dx and dy exceed 90.
+ *
+ * Once all the degenerate cases are eliminated, the values "qx", "qy", and
+ * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that
+ * we can use integer arithmetic.
+ *
+ * We travel from start to finish along the longer axis, starting at the border
+ * between the first and second tiles, where the y offset = .5 * slope, taking
+ * into account the scale factor. See below.
+ *
+ * Also note that this function and the "move towards target" code do NOT
+ * share the same properties. Thus, you can see someone, target them, and
+ * then fire a bolt at them, but the bolt may hit a wall, not them. However,
+ * by clever choice of target locations, you can sometimes throw a "curve".
+ *
+ * Note that "line of sight" is not "reflexive" in all cases.
+ *
+ * Use the "projectable()" routine to test "spell/missile line of sight".
+ *
+ * Use the "update_view()" function to determine player line-of-sight.
+ */
+bool los(int y1, int x1, int y2, int x2)
+{
+ /* Delta */
+ int dx, dy;
+
+ /* Absolute */
+ int ax, ay;
+
+ /* Signs */
+ int sx, sy;
+
+ /* Fractions */
+ int qx, qy;
+
+ /* Scanners */
+ int tx, ty;
+
+ /* Scale factors */
+ int f1, f2;
+
+ /* Slope, or 1/Slope, of LOS */
+ int m;
+
+
+ /* Extract the offset */
+ dy = y2 - y1;
+ dx = x2 - x1;
+
+ /* Extract the absolute offset */
+ ay = ABS(dy);
+ ax = ABS(dx);
+
+
+ /* Handle adjacent (or identical) grids */
+ if ((ax < 2) && (ay < 2)) return (TRUE);
+
+
+ /* Paranoia -- require "safe" origin */
+ /* if (!in_bounds(y1, x1)) return (FALSE); */
+
+
+ /* Directly South/North */
+ if (!dx)
+ {
+ /* South -- check for walls */
+ if (dy > 0)
+ {
+ for (ty = y1 + 1; ty < y2; ty++)
+ {
+ if (!cave_sight_bold(ty, x1)) return (FALSE);
+ }
+ }
+
+ /* North -- check for walls */
+ else
+ {
+ for (ty = y1 - 1; ty > y2; ty--)
+ {
+ if (!cave_sight_bold(ty, x1)) return (FALSE);
+ }
+ }
+
+ /* Assume los */
+ return (TRUE);
+ }
+
+ /* Directly East/West */
+ if (!dy)
+ {
+ /* East -- check for walls */
+ if (dx > 0)
+ {
+ for (tx = x1 + 1; tx < x2; tx++)
+ {
+ if (!cave_sight_bold(y1, tx)) return (FALSE);
+ }
+ }
+
+ /* West -- check for walls */
+ else
+ {
+ for (tx = x1 - 1; tx > x2; tx--)
+ {
+ if (!cave_sight_bold(y1, tx)) return (FALSE);
+ }
+ }
+
+ /* Assume los */
+ return (TRUE);
+ }
+
+
+ /* Extract some signs */
+ sx = (dx < 0) ? -1 : 1;
+ sy = (dy < 0) ? -1 : 1;
+
+
+ /* Vertical "knights" */
+ if (ax == 1)
+ {
+ if (ay == 2)
+ {
+ if (cave_sight_bold(y1 + sy, x1)) return (TRUE);
+ }
+ }
+
+ /* Horizontal "knights" */
+ else if (ay == 1)
+ {
+ if (ax == 2)
+ {
+ if (cave_sight_bold(y1, x1 + sx)) return (TRUE);
+ }
+ }
+
+
+ /* Calculate scale factor div 2 */
+ f2 = (ax * ay);
+
+ /* Calculate scale factor */
+ f1 = f2 << 1;
+
+
+ /* Travel horizontally */
+ if (ax >= ay)
+ {
+ /* Let m = dy / dx * 2 * (dy * dx) = 2 * dy * dy */
+ qy = ay * ay;
+ m = qy << 1;
+
+ tx = x1 + sx;
+
+ /* Consider the special case where slope == 1. */
+ if (qy == f2)
+ {
+ ty = y1 + sy;
+ qy -= f1;
+ }
+ else
+ {
+ ty = y1;
+ }
+
+ /* Note (below) the case (qy == f2), where */
+ /* the LOS exactly meets the corner of a tile. */
+ while (x2 - tx)
+ {
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+
+ qy += m;
+
+ if (qy < f2)
+ {
+ tx += sx;
+ }
+ else if (qy > f2)
+ {
+ ty += sy;
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+ qy -= f1;
+ tx += sx;
+ }
+ else
+ {
+ ty += sy;
+ qy -= f1;
+ tx += sx;
+ }
+ }
+ }
+
+ /* Travel vertically */
+ else
+ {
+ /* Let m = dx / dy * 2 * (dx * dy) = 2 * dx * dx */
+ qx = ax * ax;
+ m = qx << 1;
+
+ ty = y1 + sy;
+
+ if (qx == f2)
+ {
+ tx = x1 + sx;
+ qx -= f1;
+ }
+ else
+ {
+ tx = x1;
+ }
+
+ /* Note (below) the case (qx == f2), where */
+ /* the LOS exactly meets the corner of a tile. */
+ while (y2 - ty)
+ {
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+
+ qx += m;
+
+ if (qx < f2)
+ {
+ ty += sy;
+ }
+ else if (qx > f2)
+ {
+ tx += sx;
+ if (!cave_sight_bold(ty, tx)) return (FALSE);
+ qx -= f1;
+ ty += sy;
+ }
+ else
+ {
+ tx += sx;
+ qx -= f1;
+ ty += sy;
+ }
+ }
+ }
+
+ /* Assume los */
+ return (TRUE);
+}
+
+
+
+/*
+ * Returns true if the player's grid is dark
+ */
+bool no_lite(void)
+{
+ return (!player_can_see_bold(p_ptr->py, p_ptr->px));
+}
+
+
+
+/*
+ * Determine if a given location may be "destroyed"
+ *
+ * Used by destruction spells, and for placing stairs, etc.
+ */
+bool cave_valid_bold(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Forbid perma-grids */
+ if (cave_perma_grid(c_ptr)) return (FALSE);
+
+ /* Check objects */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Forbid artifact grids */
+ if ((o_ptr->art_name) || artifact_p(o_ptr)) return (FALSE);
+ }
+
+ /* Accept */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Hack -- Legal monster codes
+ */
+static cptr image_monster_hack = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/*
+ * Hack -- Legal monster codes for IBM pseudo-graphics
+ *
+ * Dropped. Although this option has long left unmaintained, hardcoding
+ * code points makes it impossible to update the font and prf files
+ * flexibly. And the normal graphics code still works with it -- pelpel
+ */
+
+/*
+ * Mega-Hack -- Hallucinatory monster
+ */
+static void image_monster(byte *ap, char *cp)
+{
+ int n;
+
+ switch (graphics_mode)
+ {
+ /* Text mode */
+ case GRAPHICS_NONE:
+ {
+ n = strlen(image_monster_hack);
+
+ /* Random symbol from set above */
+ *cp = (image_monster_hack[rand_int(n)]);
+
+ /* Random color */
+ *ap = randint(15);
+
+ break;
+ }
+
+ /* Normal graphics */
+ default:
+ {
+ /* Avoid player ghost */
+ n = randint(max_r_idx);
+
+ *cp = r_info[n].x_char;
+
+ *ap = r_info[n].x_attr;
+
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Hack -- Legal object codes
+ */
+static cptr image_object_hack = "?/|\\\"!$()_-=[]{},~";
+
+/*
+ * Hardcoded IBM pseudo-graphics code points have been removed
+ * for the same reason as stated above -- pelpel
+ */
+
+/*
+ * Mega-Hack -- Hallucinatory object
+ */
+static void image_object(byte *ap, char *cp)
+{
+ int n;
+
+ switch (graphics_mode)
+ {
+ /* Text mode */
+ case GRAPHICS_NONE:
+ {
+ n = strlen(image_object_hack);
+
+ /* Random symbol from set above */
+ *cp = (image_object_hack[rand_int(n)]);
+
+ /* Random color */
+ *ap = randint(15);
+
+ /* Done */
+ break;
+ }
+
+ /* Normal graphics */
+ default:
+ {
+ n = randint(max_k_idx - 1);
+
+ *cp = k_info[n].x_char;
+ *ap = k_info[n].x_attr;
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Hack -- Random hallucination
+ */
+static void image_random(byte *ap, char *cp)
+{
+ /* Normally, assume monsters */
+ if (rand_int(100) < 75)
+ {
+ image_monster(ap, cp);
+ }
+
+ /* Otherwise, assume objects */
+ else
+ {
+ image_object(ap, cp);
+ }
+}
+
+
+/*
+ * The 16x16 tile of the terrain supports lighting
+ */
+#if 1
+
+#define feat_supports_lighting(F) \
+((f_info[F].flags1 & FF1_SUPPORT_LIGHT) != 0)
+
+#else
+
+static bool feat_supports_lighting(byte feat)
+{
+ if (f_info[feat].flags1 & FF1_SUPPORT_LIGHT) return TRUE;
+ else return FALSE;
+}
+
+#endif
+
+
+char get_shimmer_color()
+{
+ switch (randint(7))
+ {
+ case 1:
+ return (TERM_RED);
+ case 2:
+ return (TERM_L_RED);
+ case 3:
+ return (TERM_WHITE);
+ case 4:
+ return (TERM_L_GREEN);
+ case 5:
+ return (TERM_BLUE);
+ case 6:
+ return (TERM_L_DARK);
+ case 7:
+ return (TERM_GREEN);
+ }
+
+ return (TERM_VIOLET);
+}
+
+
+/*
+ * Table of breath colors. Must match listings in a single set of
+ * monster spell flags.
+ *
+ * The value "255" is special. Monsters with that kind of breath
+ * may be any color.
+ */
+static byte breath_to_attr[32][2] =
+{
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { TERM_SLATE, TERM_L_DARK }, /* RF4_BRTH_ACID */
+ { TERM_BLUE, TERM_L_BLUE }, /* RF4_BRTH_ELEC */
+ { TERM_RED, TERM_L_RED }, /* RF4_BRTH_FIRE */
+ { TERM_WHITE, TERM_L_WHITE }, /* RF4_BRTH_COLD */
+ { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_POIS */
+ { TERM_L_GREEN, TERM_GREEN }, /* RF4_BRTH_NETHR */
+ { TERM_YELLOW, TERM_ORANGE }, /* RF4_BRTH_LITE */
+ { TERM_L_DARK, TERM_SLATE }, /* RF4_BRTH_DARK */
+ { TERM_L_UMBER, TERM_UMBER }, /* RF4_BRTH_CONFU */
+ { TERM_YELLOW, TERM_L_UMBER }, /* RF4_BRTH_SOUND */
+ { 255, 255 }, /* (any color) */ /* RF4_BRTH_CHAOS */
+ { TERM_VIOLET, TERM_VIOLET }, /* RF4_BRTH_DISEN */
+ { TERM_L_RED, TERM_VIOLET }, /* RF4_BRTH_NEXUS */
+ { TERM_L_BLUE, TERM_L_BLUE }, /* RF4_BRTH_TIME */
+ { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_INER */
+ { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_GRAV */
+ { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_SHARD */
+ { TERM_ORANGE, TERM_RED }, /* RF4_BRTH_PLAS */
+ { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_FORCE */
+ { TERM_L_BLUE, TERM_WHITE }, /* RF4_BRTH_MANA */
+ { 0, 0 }, /* */
+ { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_NUKE */
+ { 0, 0 }, /* */
+ { TERM_WHITE, TERM_L_RED }, /* RF4_BRTH_DISINT */
+};
+
+
+/*
+ * Multi-hued monsters shimmer acording to their breaths.
+ *
+ * If a monster has only one kind of breath, it uses both colors
+ * associated with that breath. Otherwise, it just uses the first
+ * color for any of its breaths.
+ *
+ * If a monster does not breath anything, it can be any color.
+ */
+static byte multi_hued_attr(monster_race *r_ptr)
+{
+ byte allowed_attrs[15];
+
+ int i, j;
+
+ int stored_colors = 0;
+
+ int breaths = 0;
+
+ int first_color = 0;
+
+ int second_color = 0;
+
+
+ /* Monsters with no ranged attacks can be any color */
+ if (!r_ptr->freq_inate) return (get_shimmer_color());
+
+ /* Check breaths */
+ for (i = 0; i < 32; i++)
+ {
+ bool stored = FALSE;
+
+ /* Don't have that breath */
+ if (!(r_ptr->flags4 & (1L << i))) continue;
+
+ /* Get the first color of this breath */
+ first_color = breath_to_attr[i][0];
+
+ /* Breath has no color associated with it */
+ if (first_color == 0) continue;
+
+ /* Monster can be of any color */
+ if (first_color == 255) return (randint(15));
+
+
+ /* Increment the number of breaths */
+ breaths++;
+
+ /* Monsters with lots of breaths may be any color. */
+ if (breaths == 6) return (randint(15));
+
+
+ /* Always store the first color */
+ for (j = 0; j < stored_colors; j++)
+ {
+ /* Already stored */
+ if (allowed_attrs[j] == first_color) stored = TRUE;
+ }
+ if (!stored)
+ {
+ allowed_attrs[stored_colors] = first_color;
+ stored_colors++;
+ }
+
+ /*
+ * Remember (but do not immediately store) the second color
+ * of the first breath.
+ */
+ if (breaths == 1)
+ {
+ second_color = breath_to_attr[i][1];
+ }
+ }
+
+ /* Monsters with no breaths may be of any color. */
+ if (breaths == 0) return (get_shimmer_color());
+
+ /* If monster has one breath, store the second color too. */
+ if (breaths == 1)
+ {
+ allowed_attrs[stored_colors] = second_color;
+ stored_colors++;
+ }
+
+ /* Pick a color at random */
+ return (allowed_attrs[rand_int(stored_colors)]);
+}
+
+
+/*
+ * Extract the attr/char to display at the given (legal) map location
+ *
+ * Note that this function, since it is called by "lite_spot()" which
+ * is called by "update_view()", is a major efficiency concern.
+ *
+ * Basically, we examine each "layer" of the world (terrain, objects,
+ * monsters/players), from the bottom up, extracting a new attr/char
+ * if necessary at each layer, and defaulting to "darkness". This is
+ * not the fastest method, but it is very simple, and it is about as
+ * fast as it could be for grids which contain no "marked" objects or
+ * "visible" monsters.
+ *
+ * We apply the effects of hallucination during each layer. Objects will
+ * always appear as random "objects", monsters will always appear as random
+ * "monsters", and normal grids occasionally appear as random "monsters" or
+ * "objects", but note that these random "monsters" and "objects" are really
+ * just "colored ascii symbols" (which may look silly on some machines).
+ *
+ * The hallucination functions avoid taking any pointers to local variables
+ * because some compilers refuse to use registers for any local variables
+ * whose address is taken anywhere in the function.
+ *
+ * As an optimization, we can handle the "player" grid as a special case.
+ *
+ * Note that the memorization of "objects" and "monsters" is not related
+ * to the memorization of "terrain". This allows the player to memorize
+ * the terrain of a grid without memorizing any objects in that grid, and
+ * to detect monsters without detecting anything about the terrain of the
+ * grid containing the monster.
+ *
+ * The fact that all interesting "objects" and "terrain features" are
+ * memorized as soon as they become visible for the first time means
+ * that we only have to check the "CAVE_SEEN" flag for "boring" grids.
+ *
+ * Note that bizarre things must be done when the "attr" and/or "char"
+ * codes have the "high-bit" set, since these values are used to encode
+ * various "special" pictures in some versions, and certain situations,
+ * such as "multi-hued" or "clear" monsters, cause the attr/char codes
+ * to be "scrambled" in various ways.
+ *
+ * Note that the "zero" entry in the feature/object/monster arrays are
+ * used to provide "special" attr/char codes, with "monster zero" being
+ * used for the player attr/char, "object zero" being used for the "stack"
+ * attr/char, and "feature zero" being used for the "nothing" attr/char.
+ *
+ * Note that eventually we may want to use the "&" symbol for embedded
+ * treasure, and use the "*" symbol to indicate multiple objects, but
+ * currently, we simply use the attr/char of the first "marked" object
+ * in the stack, if any, and so "object zero" is unused. XXX XXX XXX
+ *
+ * Note the assumption that doing "x_ptr = &x_info[x]" plus a few of
+ * "x_ptr->xxx", is quicker than "x_info[x].xxx", even if "x" is a fixed
+ * constant. If this is incorrect then a lot of code should be changed.
+ *
+ *
+ * Some comments on the "terrain" layer...
+ *
+ * Note that "boring" grids (floors, invisible traps, and any illegal grids)
+ * are very different from "interesting" grids (all other terrain features),
+ * and the two types of grids are handled completely separately. The most
+ * important distinction is that "boring" grids may or may not be memorized
+ * when they are first encountered, and so we must use the "CAVE_SEEN" flag
+ * to see if they are "see-able".
+ *
+ *
+ * Some comments on the "terrain" layer (boring grids)...
+ *
+ * Note that "boring" grids are always drawn using the picture for "empty
+ * floors", which is stored in "f_info[FEAT_FLOOR]". Sometimes, special
+ * lighting effects may cause this picture to be modified.
+ *
+ * Note that "invisible traps" are always displayes exactly like "empty
+ * floors", which prevents various forms of "cheating", with no loss of
+ * efficiency. There are still a few ways to "guess" where traps may be
+ * located, for example, objects will never fall into a grid containing
+ * an invisible trap. XXX XXX
+ *
+ * To determine if a "boring" grid should be displayed, we simply check to
+ * see if it is either memorized ("CAVE_MARK"), or currently "see-able" by
+ * the player ("CAVE_SEEN"). Note that "CAVE_SEEN" is now maintained by the
+ * "update_view()" function.
+ *
+ * Note the "special lighting effects" which can be activated for "boring"
+ * grids using the "view_special_lite" option, causing certain such grids
+ * to be displayed using special colors. If the grid is "see-able" by
+ * the player, we will use the normal (except that, if the "view_yellow_lite"
+ * option is set, and the grid is *only* "see-able" because of the player's
+ * torch, then we will use "yellow"), else if the player is "blind", we will
+ * use greyscale, else if the grid is not "illuminated", we will use "dark
+ * gray", if the "view_bright_lite" option is set, we will use "darker" colour
+ * else we will use the normal colour.
+ *
+ *
+ * Some comments on the "terrain" layer (non-boring grids)...
+ *
+ * Note the use of the "mimic" field in the "terrain feature" processing,
+ * which allows any feature to "pretend" to be another feature. This is
+ * used to "hide" secret doors, and to make all "doors" appear the same,
+ * and all "walls" appear the same, and "hidden" treasure stay hidden.
+ * Note that it is possible to use this field to make a feature "look"
+ * like a floor, but the "view_special_lite" flag only affects actual
+ * "boring" grids.
+ *
+ * Since "interesting" grids are always memorized as soon as they become
+ * "see-able" by the player ("CAVE_SEEN"), such a grid only needs to be
+ * displayed if it is memorized ("CAVE_MARK"). Most "interesting" grids
+ * are in fact non-memorized, non-see-able, wall grids, so the fact that
+ * we do not have to check the "CAVE_SEEN" flag adds some efficiency, at
+ * the cost of *forcing* the memorization of all "interesting" grids when
+ * they are first seen. Since the "CAVE_SEEN" flag is now maintained by
+ * the "update_view()" function, this efficiency is not as significant as
+ * it was in previous versions, and could perhaps be removed.
+ * (so I removed this to simplify the terrain feature handling -- pelpel)
+ *
+ * Note the "special lighting effects" which can be activated for "wall"
+ * grids using the "view_granite_lite" option, causing certain such grids
+ * to be displayed using special colors.
+ * If the grid is "see-able" by the player, we will use the normal colour
+ * else if the player is "blind", we will use grey scale, else if the
+ * "view_bright_lite" option is set, we will use reduced colour, else we
+ * will use the normal one.
+ *
+ * Note that "wall" grids are more complicated than "boring" grids, due to
+ * the fact that "CAVE_GLOW" for a "wall" grid means that the grid *might*
+ * be glowing, depending on where the player is standing in relation to the
+ * wall. In particular, the wall of an illuminated room should look just
+ * like any other (dark) wall unless the player is actually inside the room.
+ *
+ * Thus, we do not support as many visual special effects for "wall" grids
+ * as we do for "boring" grids, since many of them would give the player
+ * information about the "CAVE_GLOW" flag of the wall grid, in particular,
+ * it would allow the player to notice the walls of illuminated rooms from
+ * a dark hallway that happened to run beside the room.
+ *
+ *
+ * Some comments on the "object" layer...
+ *
+ * Currently, we do nothing with multi-hued objects, because there are
+ * not any. If there were, they would have to set "shimmer_objects"
+ * when they were created, and then new "shimmer" code in "dungeon.c"
+ * would have to be created handle the "shimmer" effect, and the code
+ * in "cave.c" would have to be updated to create the shimmer effect.
+ * This did not seem worth the effort. XXX XXX
+ *
+ *
+ * Some comments on the "monster"/"player" layer...
+ *
+ * Note that monsters can have some "special" flags, including "ATTR_MULTI",
+ * which means their color changes, and "ATTR_CLEAR", which means they take
+ * the color of whatever is under them, and "CHAR_CLEAR", which means that
+ * they take the symbol of whatever is under them. Technically, the flag
+ * "CHAR_MULTI" is supposed to indicate that a monster looks strange when
+ * examined, but this flag is currently ignored. All of these flags are
+ * ignored if the "avoid_other" option is set, since checking for these
+ * conditions is expensive (and annoying) on some systems.
+ *
+ * Normally, players could be handled just like monsters, except that the
+ * concept of the "torch lite" of others player would add complications.
+ * For efficiency, however, we handle the (only) player first, since the
+ * "player" symbol always "pre-empts" any other facts about the grid.
+ *
+ * The "hidden_player" efficiency option, which only makes sense with a
+ * single player, allows the player symbol to be hidden while running.
+ */
+
+/*
+ * Alternative colours for unseen grids
+ *
+ * Reduced colours - remembered interesting grids and perma-lit floors
+ * B&W - currently only used by blindness effect
+ */
+
+/* Colour */
+static byte dark_attrs[16] =
+{
+ TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE,
+ TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER,
+ TERM_L_DARK, TERM_SLATE, TERM_VIOLET, TERM_YELLOW,
+ TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER
+};
+
+/* B&W */
+static byte darker_attrs[16] =
+{
+ TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_SLATE,
+ TERM_L_DARK, TERM_L_DARK, TERM_L_DARK, TERM_L_DARK,
+ TERM_L_DARK, TERM_SLATE, TERM_L_DARK, TERM_SLATE,
+ TERM_SLATE, TERM_SLATE, TERM_SLATE, TERM_SLATE
+};
+
+
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp,
+ byte *eap, char *ecp)
+#else /* USE_EGO_GRAPHICS */
+void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp)
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+void map_info(int y, int x, byte *ap, char *cp)
+#endif /* USE_TRANSPARENCY */
+{
+ cave_type *c_ptr;
+
+ feature_type *f_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ u16b info;
+
+ s16b t_idx;
+
+ byte feat;
+
+ byte a;
+
+ byte c;
+
+ /*
+ * This means that a port supports graphics overlay as well as lighting
+ * effects. See the step 3 below for the detailed information about
+ * lighting. Basically, it requires "darker" tiles for those terrain
+ * features with SUPPORT_LIGHT flag set, and they must be arranged
+ * this way:
+ * col col+1 col+2
+ * row base darker brighter
+ */
+ bool graf_new = ((graphics_mode == GRAPHICS_ISO) ||
+ (graphics_mode == GRAPHICS_NEW));
+
+ /*
+ * I never understand why some coders like shimmering so much.
+ * It just serves to hurt my eyes, IMHO. If one feels like to show off,
+ * go for better graphics support... Anyway this means a port allows
+ * changing attr independently from its char -- pelpel
+ */
+ bool attr_mutable = (!use_graphics ||
+ (graphics_mode == GRAPHICS_IBM));
+
+
+ /**** Preparation ****/
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+
+ /* Cache some frequently used values */
+
+ /* Grid info */
+ info = c_ptr->info;
+
+ /* Feature code */
+ feat = c_ptr->feat;
+
+ /* Apply "mimic" field */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[feat].mimic;
+ }
+
+ /* Access floor */
+ f_ptr = &f_info[feat];
+
+
+#ifdef USE_EGO_GRAPHICS
+
+ /* Reset attr/char */
+ *eap = 0;
+ *ecp = 0;
+
+#endif /* USE_EGO_GRAPHICS */
+
+
+ /**** Layer 1 -- Terrain feature ****/
+
+ /* Only memorised or visible grids are displayed */
+ if (info & (CAVE_MARK | CAVE_SEEN))
+ {
+ /**** Step 1 -- Retrieve base attr/char ****/
+
+ /* 'Sane' terrain features */
+ if (feat != FEAT_SHOP)
+ {
+ /* Normal char */
+ c = f_ptr->x_char;
+
+ /* Normal attr */
+ a = f_ptr->x_attr;
+ }
+
+ /* Mega-Hack 1 -- Building don't conform to f_info */
+ else
+ {
+ c = st_info[c_ptr->special].x_char;
+ a = st_info[c_ptr->special].x_attr;
+ }
+
+ /* Mega-Hack 2 -- stair to dungeon branch are purple */
+ if (c_ptr->special && attr_mutable &&
+ ((feat == FEAT_MORE) || (feat == FEAT_LESS)))
+ {
+ a = TERM_VIOLET;
+ }
+
+ /* Mega-Hack 3 -- Traps don't have f_info entries either */
+ if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL))
+ {
+ /* Trap index */
+ t_idx = c_ptr->t_idx;
+
+ if (use_graphics &&
+ (t_info[t_idx].g_attr != 0) &&
+ (t_info[t_idx].g_char != 0))
+ {
+
+#ifdef USE_EGO_GRAPHICS
+
+ if (graf_new)
+ {
+ *eap = t_info[t_idx].g_attr;
+ *ecp = t_info[t_idx].g_char;
+ }
+ else
+ {
+ a = t_info[t_idx].g_attr;
+ c = t_info[t_idx].g_char;
+ }
+
+#else /* USE_EGO_GRAPHICS */
+
+ a = t_info[t_idx].g_attr;
+ c = t_info[t_idx].g_char;
+
+#endif /* USE_EGO_GRAPHICS */
+
+ }
+ else
+ {
+ /*
+ * If trap is set on a floor grid that is not
+ * one of "interesting" features, use a special
+ * symbol to display it. Check for doors is no longer
+ * necessary because they have REMEMBER flag now.
+ *
+ * Cave macros cannot be used safely here, because of
+ * c_ptr->mimic XXX XXX
+ */
+ if (!attr_mutable)
+ {
+ a = f_info[FEAT_TRAP].x_attr;
+ c = f_info[FEAT_TRAP].x_char;
+ }
+ else
+ {
+ if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)
+ {
+ c = f_info[FEAT_TRAP].x_char;
+ }
+
+ /* Add attr XXX XXX XXX */
+ a = t_info[t_idx].color;
+
+ /* Get a new color with a strange formula :) XXX XXX XXX */
+ if (t_info[t_idx].flags & FTRAP_CHANGE)
+ {
+ s32b tmp;
+
+ tmp = dun_level + dungeon_type + feat;
+
+ a = tmp % 16;
+ }
+ }
+ }
+ }
+
+
+ /**** Step 2 -- Apply special random effects ****/
+ if (!avoid_other && !avoid_shimmer && attr_mutable)
+ {
+ /* Special terrain effect */
+ if (c_ptr->effect)
+ {
+ a = spell_color(effects[c_ptr->effect].type);
+ }
+
+ /* Multi-hued attr */
+ else if (f_ptr->flags1 & FF1_ATTR_MULTI)
+ {
+ a = f_ptr->shimmer[rand_int(7)];
+ }
+ }
+
+
+ /*
+ * Step 3
+ *
+ * Special lighting effects, if specified and applicable
+ * This will never happen for
+ * - any grids in the overhead map
+ * - traps
+ * - (graphics modes) terrain features without corresponding
+ * "darker" tiles.
+ *
+ * Note the use of f_ptr->flags1 to avoid problems with
+ * c_ptr->mimic.
+ */
+
+ /* view_special_lite: lighting effects for boring features */
+ if (view_special_lite &&
+ ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) &&
+ (attr_mutable || (graf_new && feat_supports_lighting(feat))))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Only lit by "torch" light */
+ if (view_yellow_lite && !(info & (CAVE_GLOW)))
+ {
+ if (graf_new)
+ {
+ /* Use a brightly lit tile */
+ c += 2;
+ }
+ else
+ {
+ /* Use "yellow" */
+ a = TERM_YELLOW;
+ }
+ }
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+ }
+
+ /* Handle "dark" grids */
+ else if (!(info & (CAVE_GLOW)))
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darkest colour */
+ a = TERM_L_DARK;
+ }
+ }
+
+ /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+ }
+ }
+
+ /* view_granite_lite: lighting effects for walls and doors */
+ else if (view_granite_lite &&
+ (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR)))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) &&
+ (attr_mutable || (graf_new && feat_supports_lighting(feat))))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Do nothing */
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+ }
+
+ /* Handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ if (graf_new)
+ {
+ /* Use a dark tile */
+ c++;
+ }
+ else
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+
+ else
+ {
+ if (graf_new)
+ {
+ /* Use a brightly lit tile */
+ c += 2;
+ }
+ else
+ {
+ /* Use normal colour */
+ }
+ }
+ }
+ }
+ }
+
+ /* Unknown grids */
+ else
+ {
+ /* Access darkness */
+ f_ptr = &f_info[FEAT_NONE];
+
+ /* Normal attr */
+ a = f_ptr->x_attr;
+
+ /* Normal char */
+ c = f_ptr->x_char;
+ }
+
+ /*
+ * Hack -- rare random hallucination
+ * Because we cannot be sure which is outer dungeon walls,
+ * the check for 'feat' has been removed
+ */
+ if (p_ptr->image && (rand_int(256) == 0))
+ {
+ /* Hallucinate */
+ image_random(ap, cp);
+ }
+
+#ifdef USE_TRANSPARENCY
+
+ /* Save the terrain info for the transparency effects */
+ *tap = a;
+ *tcp = c;
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+
+ /**** Layer 2 -- Objects ****/
+
+ if (feat != FEAT_MON_TRAP)
+ {
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other && attr_mutable &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+
+ /**** Layer 3 -- Handle monsters ****/
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other && attr_mutable &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+ }
+ }
+ else
+ {
+ /* Visible monster */
+ if (m_ptr->ml)
+ {
+ monster_race *r_ptr = race_inf(m_ptr);
+
+#ifdef USE_EGO_GRAPHICS
+
+ /* Reset attr/char */
+ *eap = 0;
+ *ecp = 0;
+
+#endif /* USE_EGO_GRAPHICS */
+
+ if (use_graphics)
+ {
+
+#ifdef USE_EGO_GRAPHICS
+
+ if (graf_new)
+ {
+ monster_ego *re_ptr = &re_info[m_ptr->ego];
+
+ /* Desired attr */
+ *eap = re_ptr->g_attr;
+
+ /* Desired char */
+ *ecp = re_ptr->g_char;
+ }
+
+#endif /* USE_EGO_GRAPHICS */
+
+ /* Use base monster */
+ r_ptr = &r_info[m_ptr->r_idx];
+ }
+
+ /* Desired attr/char */
+ c = r_ptr->x_char;
+ a = r_ptr->x_attr;
+
+ /* Ignore weird codes */
+ if (avoid_other)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Special attr/char codes */
+ else if (!attr_mutable)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Multi-hued monster */
+ else if (r_ptr->flags1 & (RF1_ATTR_MULTI))
+ {
+ /* Is it a shapechanger? */
+ if (r_ptr->flags2 & (RF2_SHAPECHANGER))
+ {
+ image_random(ap, cp);
+ }
+ else
+ *cp = c;
+
+ /* Multi-hued attr */
+ if (r_ptr->flags2 & (RF2_ATTR_ANY))
+ {
+ *ap = randint(15);
+ }
+ else
+ {
+ *ap = multi_hued_attr(r_ptr);
+ }
+ }
+
+ /* Normal monster (not "clear" in any way) */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR)))
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /*
+ * Hack -- Bizarre grid under monster
+ * WAS: else if (*ap & 0x80) || (*cp & 0x80) -- pelpel
+ */
+ else if (*ap & 0x80)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Normal */
+ else
+ {
+ /* Normal (non-clear char) monster */
+ if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR)))
+ {
+ /* Normal char */
+ *cp = c;
+ }
+
+ /* Normal (non-clear attr) monster */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR)))
+ {
+ /* Normal attr */
+ *ap = a;
+ }
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image)
+ {
+ /* Hallucinatory monster */
+ image_monster(ap, cp);
+ }
+ }
+ }
+ }
+
+ /* Handle "player" */
+ if ((y == p_ptr->py) && (x == p_ptr->px) &&
+ (!p_ptr->invis || p_ptr->see_inv))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+#ifdef USE_EGO_GRAPHICS
+
+ /* Reset attr/char */
+ *eap = 0;
+ *ecp = 0;
+
+#endif /* USE_EGO_GRAPHICS */
+
+ /* Get the "player" attr */
+ if (!avoid_other && attr_mutable && (r_ptr->flags1 & RF1_ATTR_MULTI))
+ {
+ a = get_shimmer_color();
+ }
+ else
+ {
+ a = r_ptr->x_attr;
+ }
+
+ /* Get the "player" char */
+ c = r_ptr->x_char;
+
+
+ /* Mega-Hack -- Apply modifications to player graphics XXX XXX XXX */
+ switch (graphics_mode)
+ {
+ case GRAPHICS_NONE:
+ case GRAPHICS_IBM:
+ {
+ if (player_char_health)
+ {
+ int percent = p_ptr->chp * 10 / p_ptr->mhp;
+
+ if (percent < 7)
+ {
+ c = I2D(percent);
+ if (percent < 3) a = TERM_L_RED;
+ }
+ }
+
+ break;
+ }
+
+#ifdef VARIABLE_PLAYER_GRAPH
+
+ case GRAPHICS_OLD:
+ {
+ if (player_symbols)
+ {
+ a = BMP_FIRST_PC_CLASS + p_ptr->pclass;
+ c = BMP_FIRST_PC_RACE + p_ptr->prace;
+ }
+
+ break;
+ }
+
+#endif /* VARIABLE_PLAYER_GRAPH */
+
+#ifdef USE_EGO_GRAPHICS
+
+ case GRAPHICS_ISO:
+ case GRAPHICS_NEW:
+ {
+ if (p_ptr->pracem)
+ {
+ player_race_mod *rmp_ptr = &race_mod_info[p_ptr->pracem];
+
+ /* Desired attr */
+ *eap = rmp_ptr->g_attr;
+
+ /* Desired char */
+ *ecp = rmp_ptr->g_char;
+ }
+
+ /* +AKH 20020421 - Health dispay for graphics, too */
+ if (player_char_health && (graphics_mode == GRAPHICS_NEW))
+ {
+ int percent = p_ptr->chp * 14 / p_ptr->mhp;
+
+ if (percent < 10)
+ {
+ *eap = 10;
+ *ecp = 32 + 14 - percent;
+ }
+ }
+
+ break;
+ }
+
+#endif /* USE_EGO_GRAPHICS */
+
+ }
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+ }
+}
+
+
+/*
+ * Special version of map_info, for use by cmovie and HTML converter
+ * to obtain pure-ASCII image of dungeon map
+ */
+void map_info_default(int y, int x, byte *ap, char *cp)
+{
+ cave_type *c_ptr;
+
+ feature_type *f_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ u16b info;
+
+ s16b t_idx;
+
+ byte feat;
+
+ byte a;
+
+ byte c;
+
+ bool use_graphics_hack = use_graphics;
+ byte graphics_mode_hack = graphics_mode;
+
+
+ /* Temporarily disable graphics mode -- for some random effects XXX */
+ use_graphics = FALSE;
+ graphics_mode = GRAPHICS_NONE;
+
+ /**** Preparation ****/
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+
+ /* Cache some frequently used values */
+
+ /* Grid info */
+ info = c_ptr->info;
+
+ /* Feature code */
+ feat = c_ptr->feat;
+
+ /* Apply "mimic" field */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[feat].mimic;
+ }
+
+ /* Access floor */
+ f_ptr = &f_info[feat];
+
+
+ /**** Layer 1 -- Terrain feature ****/
+
+ /* Only memorised or visible grids are displayed */
+ if (info & (CAVE_MARK | CAVE_SEEN))
+ {
+ /**** Step 1 -- Retrieve base attr/char ****/
+
+ /* 'Sane' terrain features */
+ if (feat != FEAT_SHOP)
+ {
+ /* Default char */
+ c = f_ptr->d_char;
+
+ /* Default attr */
+ a = f_ptr->d_attr;
+ }
+
+ /* Mega-Hack 1 -- Building don't conform to f_info */
+ else
+ {
+ c = st_info[c_ptr->special].d_char;
+ a = st_info[c_ptr->special].d_attr;
+ }
+
+ /* Mega-Hack 2 -- stair to dungeon branch are purple */
+ if (c_ptr->special &&
+ ((feat == FEAT_MORE) || (feat == FEAT_LESS)))
+ {
+ a = TERM_VIOLET;
+ }
+
+ /* Mega-Hack 3 -- Traps don't have f_info entries either */
+ if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL))
+ {
+ /* Trap index */
+ t_idx = c_ptr->t_idx;
+
+ /*
+ * If trap is set on a floor grid that is not
+ * one of "interesting" features, use a special
+ * symbol to display it. Check for doors is no longer
+ * necessary because they have REMEMBER flag now.
+ *
+ * Cave macros cannot be used safely here, because of
+ * c_ptr->mimic XXX XXX
+ */
+ if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)
+ {
+ c = f_info[FEAT_TRAP].d_char;
+ }
+
+ /* Add attr */
+ a = t_info[t_idx].color;
+
+ /* Get a new color with a strange formula :) */
+ if (t_info[t_idx].flags & FTRAP_CHANGE)
+ {
+ s32b tmp;
+
+ tmp = dun_level + dungeon_type + feat;
+
+ a = tmp % 16;
+ }
+ }
+
+
+ /**** Step 2 -- Apply special random effects ****/
+ if (!avoid_other)
+ {
+ /* Special terrain effect */
+ if (c_ptr->effect)
+ {
+ a = spell_color(effects[c_ptr->effect].type);
+ }
+
+ /* Multi-hued attr */
+ else if (f_ptr->flags1 & FF1_ATTR_MULTI)
+ {
+ a = f_ptr->shimmer[rand_int(7)];
+ }
+ }
+
+
+ /*
+ * Step 3
+ *
+ * Special lighting effects, if specified and applicable
+ * This will never happen for
+ * - any grids in the overhead map
+ * - traps
+ * - (graphics modes) terrain features without corresponding
+ * "darker" tiles.
+ *
+ * All the if's here are flag checks, so changed order shouldn't
+ * affect performance a lot, I hope...
+ */
+
+ /* view_special_lite: lighting effects for boring features */
+ if (view_special_lite &&
+ ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Only lit by "torch" light */
+ if (view_yellow_lite && !(info & (CAVE_GLOW)))
+ {
+ /* Use "yellow" */
+ a = TERM_YELLOW;
+ }
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+
+ /* Handle "dark" grids */
+ else if (!(info & (CAVE_GLOW)))
+ {
+ /* Use darkest colour */
+ a = TERM_L_DARK;
+ }
+
+ /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+ }
+
+ /* view_granite_lite: lighting effects for walls and doors */
+ else if (view_granite_lite &&
+ (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR)))
+ {
+ if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)))
+ {
+ /* Handle "seen" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Do nothing */
+ }
+
+ /* Handle "blind" */
+ else if (p_ptr->blind)
+ {
+ /* Use darker colour */
+ a = darker_attrs[a & 0xF];
+ }
+
+ /* Handle "view_bright_lite" */
+ else if (view_bright_lite)
+ {
+ /* Use darker colour */
+ a = dark_attrs[a & 0xF];
+ }
+ }
+ }
+ }
+
+ /* Unknown grids */
+ else
+ {
+ /* Access darkness */
+ f_ptr = &f_info[FEAT_NONE];
+
+ /* Default attr */
+ a = f_ptr->d_attr;
+
+ /* Default char */
+ c = f_ptr->d_char;
+ }
+
+ /*
+ * Hack -- rare random hallucination
+ * Because we cannot be sure which is outer dungeon walls,
+ * the check for 'feat' has been removed
+ */
+ if (p_ptr->image && (rand_int(256) == 0))
+ {
+ /* Hallucinate */
+ image_random(ap, cp);
+ }
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+
+ /**** Layer 2 -- Objects ****/
+
+ if (feat != FEAT_MON_TRAP)
+ {
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char_default(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr_default(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+
+ /**** Layer 3 -- Handle monsters ****/
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ /* Memorized objects */
+ if (o_ptr->marked)
+ {
+ /* Normal char */
+ *cp = object_char_default(o_ptr);
+
+ /* Normal attr */
+ *ap = object_attr_default(o_ptr);
+
+ /* Multi-hued attr */
+ if (!avoid_other && !use_graphics &&
+ (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI))
+ {
+ *ap = get_shimmer_color();
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image) image_object(ap, cp);
+ }
+ }
+ else
+ {
+ /* Visible monster */
+ if (m_ptr->ml)
+ {
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Default attr/char */
+ c = r_ptr->d_char;
+ a = r_ptr->d_attr;
+
+ /* Ignore weird codes */
+ if (avoid_other)
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Multi-hued monster */
+ else if (r_ptr->flags1 & (RF1_ATTR_MULTI))
+ {
+ /* Is it a shapechanger? */
+ if (r_ptr->flags2 & (RF2_SHAPECHANGER))
+ {
+ image_random(ap, cp);
+ }
+ else
+ *cp = c;
+
+ /* Multi-hued attr */
+ if (r_ptr->flags2 & (RF2_ATTR_ANY))
+ {
+ *ap = randint(15);
+ }
+ else
+ {
+ *ap = multi_hued_attr(r_ptr);
+ }
+ }
+
+ /* Normal monster (not "clear" in any way) */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR)))
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Hack -- Bizarre grid under monster */
+ else if ((*ap & 0x80) || (*cp & 0x80))
+ {
+ /* Use char */
+ *cp = c;
+
+ /* Use attr */
+ *ap = a;
+ }
+
+ /* Normal */
+ else
+ {
+ /* Normal (non-clear char) monster */
+ if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR)))
+ {
+ /* Normal char */
+ *cp = c;
+ }
+
+ /* Normal (non-clear attr) monster */
+ else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR)))
+ {
+ /* Normal attr */
+ *ap = a;
+ }
+ }
+
+ /* Hack -- hallucination */
+ if (p_ptr->image)
+ {
+ /* Hallucinatory monster */
+ image_monster(ap, cp);
+ }
+ }
+ }
+ }
+
+
+ /* Handle "player" */
+ if ((y == p_ptr->py) && (x == p_ptr->px) &&
+ (!p_ptr->invis ||
+ (p_ptr->invis && p_ptr->see_inv)))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Get the "player" attr */
+ if (!avoid_other && (r_ptr->flags1 & RF1_ATTR_MULTI))
+ {
+ a = get_shimmer_color();
+ }
+ else
+ {
+ a = r_ptr->d_attr;
+ }
+
+ /* Get the "player" char */
+ c = r_ptr->d_char;
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+ }
+
+ /* XXX Restore the graphics mode */
+ use_graphics = use_graphics_hack;
+ graphics_mode = graphics_mode_hack;
+}
+
+
+/*
+ * Calculate panel colum of a location in the map
+ */
+static int panel_col_of(int col)
+{
+ col -= panel_col_min;
+ if (use_bigtile) col *= 2;
+ return col + COL_MAP;
+}
+
+
+
+/*
+ * Moves the cursor to a given MAP (y,x) location
+ */
+void move_cursor_relative(int row, int col)
+{
+ /* Real co-ords convert to screen positions */
+ row -= panel_row_prt;
+
+ /* Go there */
+ Term_gotoxy(panel_col_of(col), row);
+}
+
+
+
+/*
+ * Place an attr/char pair at the given map coordinate, if legal.
+ */
+void print_rel(char c, byte a, int y, int x)
+{
+ /* Paranoia -- Only do "legal" locations */
+ if (!panel_contains(y, x)) return;
+
+ /* Draw the char using the attr */
+ Term_draw(panel_col_of(x), y - panel_row_prt, a, c);
+
+ if (use_bigtile)
+ {
+ char c2;
+ byte a2;
+
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_draw(panel_col_of(x) + 1, y - panel_row_prt, a2, c2);
+ }
+}
+
+
+
+
+
+/*
+ * Memorize interesting viewable object/features in the given grid
+ *
+ * This function should only be called on "legal" grids.
+ *
+ * This function will memorize the object and/or feature in the given
+ * grid, if they are (1) viewable and (2) interesting. Note that all
+ * objects are interesting, all terrain features except floors (and
+ * invisible traps) are interesting, and floors (and invisible traps)
+ * are interesting sometimes (depending on various options involving
+ * the illumination of floor grids).
+ *
+ * The automatic memorization of all objects and non-floor terrain
+ * features as soon as they are displayed allows incredible amounts
+ * of optimization in various places, especially "map_info()".
+ *
+ * Note that the memorization of objects is completely separate from
+ * the memorization of terrain features, preventing annoying floor
+ * memorization when a detected object is picked up from a dark floor,
+ * and object memorization when an object is dropped into a floor grid
+ * which is memorized but out-of-sight.
+ *
+ * This function should be called every time the "memorization" of
+ * a grid (or the object in a grid) is called into question, such
+ * as when an object is created in a grid, when a terrain feature
+ * "changes" from "floor" to "non-floor", when any grid becomes
+ * "illuminated" or "viewable", and when a "floor" grid becomes
+ * "torch-lit".
+ *
+ * Note the relatively efficient use of this function by the various
+ * "update_view()" and "update_lite()" calls, to allow objects and
+ * terrain features to be memorized (and drawn) whenever they become
+ * viewable or illuminated in any way, but not when they "maintain"
+ * or "lose" their previous viewability or illumination.
+ *
+ * Note the butchered "internal" version of "player_can_see_bold()",
+ * optimized primarily for the most common cases, that is, for the
+ * non-marked floor grids.
+ */
+void note_spot(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ u16b info = c_ptr->info;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Require "seen" flag */
+ if (!(info & (CAVE_SEEN))) return;
+
+
+ /* Hack -- memorize objects */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorize objects */
+ o_ptr->marked = TRUE;
+ }
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ o_ptr->marked = TRUE;
+ }
+ }
+
+
+ /* Hack -- memorize grids */
+ if (!(info & (CAVE_MARK)))
+ {
+ /* Memorise some "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Option -- memorise certain floors */
+ if ((info & (CAVE_TRDT)) ||
+ ((info & (CAVE_GLOW)) && view_perma_grids ) ||
+ view_torch_grids)
+ {
+ /* Memorize */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+
+ /* Memorise all "interesting" grids */
+ else
+ {
+ /* Memorize */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+}
+
+
+/*
+ * Redraw (on the screen) a given MAP location
+ *
+ * This function should only be called on "legal" grids
+ */
+void lite_spot(int y, int x)
+{
+ byte a, a2;
+ byte c, c2;
+
+#ifdef USE_TRANSPARENCY
+ byte ta;
+ char tc;
+
+# ifdef USE_EGO_GRAPHICS
+ byte ea;
+ char ec;
+
+# endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Redraw if on screen */
+ if (panel_contains(y, x))
+ {
+#ifdef USE_TRANSPARENCY
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Examine the grid */
+ map_info(y, x, &a, (char*)&c, &ta, &tc, &ea, &ec);
+
+ /* Hack -- Queue it */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0);
+ }
+
+# else /* USE_EGO_GRAPHICS */
+
+ /* Examine the grid */
+ map_info(y, x, &a, &c, &ta, &tc);
+
+ /* Hack -- Queue it */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0);
+ }
+
+# endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Examine the grid */
+ map_info(y, x, &a, (char *) &c);
+
+ /* Hack -- Queue it */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2);
+ }
+
+#endif /* USE_TRANSPARENCY */
+
+ }
+}
+
+
+
+
+/*
+ * Prints the map of the dungeon
+ *
+ * Note that, for efficiency, we contain an "optimized" version
+ * of both "lite_spot()" and "print_rel()", and that we use the
+ * "lite_spot()" function to display the player grid, if needed.
+ */
+void prt_map(void)
+{
+ int x, y;
+
+ int v;
+
+ /* Access the cursor state */
+ (void)Term_get_cursor(&v);
+
+ /* Hide the cursor */
+ (void)Term_set_cursor(0);
+
+ /* Dump the map */
+ for (y = panel_row_min; y <= panel_row_max; y++)
+ {
+ /* Scan the columns of row "y" */
+ for (x = panel_col_min; x <= panel_col_max; x++)
+ {
+ byte a, a2;
+ char c, c2;
+
+#ifdef USE_TRANSPARENCY
+ byte ta;
+ char tc;
+
+#ifdef USE_EGO_GRAPHICS
+ byte ea;
+ char ec;
+
+ /* Determine what is there */
+ map_info(y, x, &a, &c, &ta, &tc, &ea, &ec);
+
+ /* Efficiency -- Redraw that grid of the map */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0);
+ }
+#else /* USE_EGO_GRAPHICS */
+/* Determine what is there */
+ map_info(y, x, &a, &c, &ta, &tc);
+ /* Efficiency -- Redraw that grid of the map */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0);
+ }
+
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ /* Determine what is there */
+ map_info(y, x, &a, &c);
+
+ /* Efficiency -- Redraw that grid of the map */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c);
+ if (use_bigtile)
+ {
+ if (a & 0x80)
+ {
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+ Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2);
+ }
+#endif /* USE_TRANSPARENCY */
+ }
+ }
+
+ /* Display player */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Restore the cursor */
+ (void)Term_set_cursor(v);
+}
+
+
+
+
+
+/*
+ * Display highest priority object in the RATIO by RATIO area
+ */
+
+/*
+ * Display the entire map
+ */
+#define MAP_HGT (MAX_HGT / RATIO)
+#define MAP_WID (MAX_WID / RATIO)
+
+/*
+ * Hack -- priority array (see below)
+ *
+ * Note that all "walls" always look like "secret doors" (see "map_info()").
+ */
+static byte priority_table[][2] =
+{
+ /* Dark */
+ { FEAT_NONE, 2 },
+
+ /* Floors */
+ { FEAT_FLOOR, 5 },
+
+ /* Walls */
+ { FEAT_SECRET, 10 },
+
+ /* Quartz */
+ { FEAT_QUARTZ, 11 },
+
+ /* Magma */
+ { FEAT_MAGMA, 12 },
+
+ /* Rubble */
+ { FEAT_RUBBLE, 13 },
+
+ /* Sandwall */
+ { FEAT_SANDWALL, 14 },
+
+ /* Open doors */
+ { FEAT_OPEN, 15 },
+ { FEAT_BROKEN, 15 },
+
+ /* Closed doors */
+ { FEAT_DOOR_HEAD + 0x00, 17 },
+
+ /* Hidden gold */
+ { FEAT_QUARTZ_K, 19 },
+ { FEAT_MAGMA_K, 19 },
+ { FEAT_SANDWALL_K, 19 },
+
+ /* water, lava, & trees oh my! -KMW- */
+ { FEAT_DEEP_WATER, 20 },
+ { FEAT_SHAL_WATER, 20 },
+ { FEAT_DEEP_LAVA, 20 },
+ { FEAT_SHAL_LAVA, 20 },
+ { FEAT_DIRT, 20 },
+ { FEAT_GRASS, 20 },
+ { FEAT_DARK_PIT, 20 },
+ { FEAT_TREES, 20 },
+ { FEAT_MOUNTAIN, 20 },
+ { FEAT_ICE, 20},
+ { FEAT_SAND, 20},
+ { FEAT_DEAD_TREE, 20},
+ { FEAT_ASH, 20},
+ { FEAT_MUD, 20},
+
+ /* Fountain */
+ { FEAT_FOUNTAIN, 22 },
+ { FEAT_EMPTY_FOUNTAIN, 22 },
+
+ /* Stairs */
+ { FEAT_LESS, 25 },
+ { FEAT_MORE, 25 },
+
+ /* Stairs */
+ { FEAT_WAY_LESS, 25 },
+ { FEAT_WAY_MORE, 25 },
+
+ { FEAT_SHAFT_UP, 25 },
+ { FEAT_SHAFT_DOWN, 25 },
+
+ /* End */
+ { 0, 0 }
+};
+
+
+/*
+ * Hack -- a priority function (see below)
+ */
+static byte priority(byte a, char c)
+{
+ int i, p0, p1;
+
+ feature_type *f_ptr;
+
+ /* Scan the table */
+ for (i = 0; TRUE; i++)
+ {
+ /* Priority level */
+ p1 = priority_table[i][1];
+
+ /* End of table */
+ if (!p1) break;
+
+ /* Feature index */
+ p0 = priority_table[i][0];
+
+ /* Access the feature */
+ f_ptr = &f_info[p0];
+
+ /* Check character and attribute, accept matches */
+ if ((f_ptr->x_char == c) && (f_ptr->x_attr == a)) return (p1);
+ }
+
+ /* Default */
+ return (20);
+}
+
+
+/*
+ * Display a "small-scale" map of the dungeon in the active Term
+ *
+ * Note that the "map_info()" function must return fully colorized
+ * data or this function will not work correctly.
+ *
+ * Note that this function must "disable" the special lighting
+ * effects so that the "priority" function will work.
+ *
+ * Note the use of a specialized "priority" function to allow this
+ * function to work with any graphic attr/char mappings, and the
+ * attempts to optimize this function where possible.
+ */
+void display_map(int *cy, int *cx)
+{
+ int i, j, x, y;
+
+ byte ta;
+ char tc;
+
+ byte tp;
+
+ byte **ma;
+ char **mc;
+
+ byte **mp;
+
+ bool old_view_special_lite;
+ bool old_view_granite_lite;
+
+ int hgt, wid, yrat, xrat, yfactor, xfactor;
+
+
+ /* Obtain current size of the Angband window */
+ Term_get_size(&wid, &hgt);
+
+ /* Use two characters as one tile in Bigtile mode */
+ if (use_bigtile) wid /= 2;
+
+ /*
+ * Calculate the size of the dungeon map area
+ */
+ hgt -= ROW_MAP + 2;
+ wid -= COL_MAP + 1;
+
+ /* Paranoia */
+ if ((hgt < 3) || (wid < 3))
+ {
+ /* Map is too small, but place the player anyway */
+ *cy = ROW_MAP;
+ *cx = COL_MAP;
+
+ return;
+ }
+
+
+ /* Save lighting effects */
+ old_view_special_lite = view_special_lite;
+ old_view_granite_lite = view_granite_lite;
+
+ /* Disable lighting effects */
+ view_special_lite = FALSE;
+ view_granite_lite = FALSE;
+
+
+ /* Allocate temporary memory for the maps */
+ C_MAKE(ma, hgt + 2, byte *);
+ C_MAKE(mc, hgt + 2, char *);
+ C_MAKE(mp, hgt + 2, byte *);
+
+ /* Allocate each line in the maps */
+ for (i = 0; i < hgt + 2; i++)
+ {
+ C_MAKE(ma[i], wid + 2, byte);
+ C_MAKE(mc[i], wid + 2, char);
+ C_MAKE(mp[i], wid + 2, byte);
+ }
+
+ /* Clear the chars and attributes */
+ for (y = 0; y < hgt + 2; ++y)
+ {
+ for (x = 0; x < wid + 2; ++x)
+ {
+ /* Nothing here */
+ ma[y][x] = TERM_WHITE;
+ mc[y][x] = ' ';
+
+ /* No priority */
+ mp[y][x] = 0;
+ }
+ }
+
+ /* Calculate scaling factors */
+ yfactor = ((cur_hgt / hgt < 4) && (cur_hgt > hgt)) ? 10 : 1;
+ xfactor = ((cur_wid / wid < 4) && (cur_wid > wid)) ? 10 : 1;
+
+ yrat = (cur_hgt * yfactor + (hgt - 1)) / hgt;
+ xrat = (cur_wid * xfactor + (wid - 1)) / wid;
+
+ /* Fill in the map */
+ for (j = 0; j < cur_hgt; ++j)
+ {
+ for (i = 0; i < cur_wid; ++i)
+ {
+ /* Location */
+ y = j * yfactor / yrat + 1;
+ x = i * xfactor / xrat + 1;
+
+ /* Extract the current attr/char at that map location */
+#ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+ map_info(j, i, &ta, &tc, &ta, &tc, &ta, &tc);
+# else /* USE_EGO_GRAPHICS */
+ map_info(j, i, &ta, &tc, &ta, &tc);
+# endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ map_info(j, i, &ta, &tc);
+#endif /* USE_TRANSPARENCY */
+
+ /* Extract the priority of that attr/char */
+ tp = priority(ta, tc);
+
+ /* Player location has the highest priority */
+ if ((p_ptr->py == j) && (p_ptr->px == i)) tp = 255;
+
+ /* Save "best" */
+ if (mp[y][x] < tp)
+ {
+ /* Save the char */
+ mc[y][x] = tc;
+
+ /* Save the attr */
+ ma[y][x] = ta;
+
+ /* Save priority */
+ mp[y][x] = tp;
+ }
+ }
+ }
+
+
+ /* Corners */
+ y = hgt + 1;
+ x = wid + 1;
+
+ /* Draw the corners */
+ mc[0][0] = mc[0][x] = mc[y][0] = mc[y][x] = '+';
+
+ /* Draw the horizontal edges */
+ for (x = 1; x <= wid; x++) mc[0][x] = mc[y][x] = '-';
+
+ /* Draw the vertical edges */
+ for (y = 1; y <= hgt; y++) mc[y][0] = mc[y][x] = '|';
+
+
+ /* Display each map line in order */
+ for (y = 0; y < hgt + 2; ++y)
+ {
+ /* Start a new line */
+ Term_gotoxy(COL_MAP - 1, y);
+
+ /* Display the line */
+ for (x = 0; x < wid + 2; ++x)
+ {
+ ta = ma[y][x];
+ tc = mc[y][x];
+
+ /* Add the character */
+ Term_addch(ta, tc);
+
+ /* Double width tile mode requires filler */
+ if (use_bigtile)
+ {
+ byte a2;
+ char c2;
+
+ if (ta & 0x80)
+ {
+ /* Mega-Hack */
+ a2 = 255;
+ c2 = 255;
+ }
+ else
+ {
+ a2 = TERM_WHITE;
+ c2 = ' ';
+ }
+
+ Term_addch(a2, c2);
+ }
+ }
+ }
+
+ /* Player location in dungeon */
+ *cy = p_ptr->py * yfactor / yrat + ROW_MAP;
+ if (!use_bigtile)
+ {
+ *cx = p_ptr->px * xfactor / xrat + COL_MAP;
+ }
+ else
+ {
+ *cx = (p_ptr->px * xfactor / xrat + 1) * 2 - 1 + COL_MAP;
+ }
+
+ /* Free each line in the maps */
+ for (i = 0; i < hgt + 2; i++)
+ {
+ C_FREE(ma[i], wid + 2, byte);
+ C_FREE(mc[i], wid + 2, char);
+ C_FREE(mp[i], wid + 2, byte);
+ }
+
+ /* Allocate temporary memory for the maps */
+ C_FREE(ma, hgt + 2, byte *);
+ C_FREE(mc, hgt + 2, char *);
+ C_FREE(mp, hgt + 2, byte *);
+
+
+ /* Restore lighting effects */
+ view_special_lite = old_view_special_lite;
+ view_granite_lite = old_view_granite_lite;
+}
+
+
+/*
+ * Display a "small-scale" map of the dungeon for the player
+ *
+ * Currently, the "player" is displayed on the map. XXX XXX XXX
+ */
+void do_cmd_view_map(void)
+{
+ int cy, cx;
+ int wid, hgt;
+
+ /* Retrive current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Note */
+ prt("Please wait...", 0, 0);
+
+ /* Flush */
+ Term_fresh();
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Display the map */
+ display_map(&cy, &cx);
+
+ /* Wait for it */
+ put_str("Hit any key to continue", hgt - 1, (wid - COL_MAP) / 2);
+
+ /* Hilite the player */
+ move_cursor(cy, cx);
+
+ /* Get any key */
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+
+
+
+/*
+ * Some comments on the dungeon related data structures and functions...
+ *
+ * Angband is primarily a dungeon exploration game, and it should come as
+ * no surprise that the internal representation of the dungeon has evolved
+ * over time in much the same way as the game itself, to provide semantic
+ * changes to the game itself, to make the code simpler to understand, and
+ * to make the executable itself faster or more efficient in various ways.
+ *
+ * There are a variety of dungeon related data structures, and associated
+ * functions, which store information about the dungeon, and provide methods
+ * by which this information can be accessed or modified.
+ *
+ * Some of this information applies to the dungeon as a whole, such as the
+ * list of unique monsters which are still alive. Some of this information
+ * only applies to the current dungeon level, such as the current depth, or
+ * the list of monsters currently inhabiting the level. And some of the
+ * information only applies to a single grid of the current dungeon level,
+ * such as whether the grid is illuminated, or whether the grid contains a
+ * monster, or whether the grid can be seen by the player. If Angband was
+ * to be turned into a multi-player game, some of the information currently
+ * associated with the dungeon should really be associated with the player,
+ * such as whether a given grid is viewable by a given player.
+ *
+ * One of the major bottlenecks in ancient versions of Angband was in the
+ * calculation of "line of sight" from the player to various grids, such
+ * as those containing monsters, using the relatively expensive "los()"
+ * function. This was such a nasty bottleneck that a lot of silly things
+ * were done to reduce the dependancy on "line of sight", for example, you
+ * could not "see" any grids in a lit room until you actually entered the
+ * room, at which point every grid in the room became "illuminated" and
+ * all of the grids in the room were "memorized" forever. Other major
+ * bottlenecks involved the determination of whether a grid was lit by the
+ * player's torch, and whether a grid blocked the player's line of sight.
+ * These bottlenecks led to the development of special new functions to
+ * optimize issues involved with "line of sight" and "torch lit grids".
+ * These optimizations led to entirely new additions to the game, such as
+ * the ability to display the player's entire field of view using different
+ * colors than were used for the "memorized" portions of the dungeon, and
+ * the ability to memorize dark floor grids, but to indicate by the way in
+ * which they are displayed that they are not actually illuminated. And
+ * of course many of them simply made the game itself faster or more fun.
+ * Also, over time, the definition of "line of sight" has been relaxed to
+ * allow the player to see a wider "field of view", which is slightly more
+ * realistic, and only slightly more expensive to maintain.
+ *
+ * Currently, a lot of the information about the dungeon is stored in ways
+ * that make it very efficient to access or modify the information, while
+ * still attempting to be relatively conservative about memory usage, even
+ * if this means that some information is stored in multiple places, or in
+ * ways which require the use of special code idioms. For example, each
+ * monster record in the monster array contains the location of the monster,
+ * and each cave grid has an index into the monster array, or a zero if no
+ * monster is in the grid. This allows the monster code to efficiently see
+ * where the monster is located, while allowing the dungeon code to quickly
+ * determine not only if a monster is present in a given grid, but also to
+ * find out which monster. The extra space used to store the information
+ * twice is inconsequential compared to the speed increase.
+ *
+ * Some of the information about the dungeon is used by functions which can
+ * constitute the "critical efficiency path" of the game itself, and so the
+ * way in which they are stored and accessed has been optimized in order to
+ * optimize the game itself. For example, the "update_view()" function was
+ * originally created to speed up the game itself (when the player was not
+ * running), but then it took on extra responsibility as the provider of the
+ * new "special effects lighting code", and became one of the most important
+ * bottlenecks when the player was running. So many rounds of optimization
+ * were performed on both the function itself, and the data structures which
+ * it uses, resulting eventually in a function which not only made the game
+ * faster than before, but which was responsible for even more calculations
+ * (including the determination of which grids are "viewable" by the player,
+ * which grids are illuminated by the player's torch, and which grids can be
+ * "seen" in some way by the player), as well as for providing the guts of
+ * the special effects lighting code, and for the efficient redisplay of any
+ * grids whose visual representation may have changed.
+ *
+ * Several pieces of information about each cave grid are stored in various
+ * two dimensional arrays, with one unit of information for each grid in the
+ * dungeon. Some of these arrays have been intentionally expanded by a small
+ * factor to make the two dimensional array accesses faster by allowing the
+ * use of shifting instead of multiplication.
+ *
+ * Several pieces of information about each cave grid are stored in the
+ * "cave_info" array, which is a special two dimensional array of bytes,
+ * one for each cave grid, each containing eight separate "flags" which
+ * describe some property of the cave grid. These flags can be checked and
+ * modified extremely quickly, especially when special idioms are used to
+ * force the compiler to keep a local register pointing to the base of the
+ * array. Special location offset macros can be used to minimize the number
+ * of computations which must be performed at runtime. Note that using a
+ * byte for each flag set may be slightly more efficient than using a larger
+ * unit, so if another flag (or two) is needed later, and it must be fast,
+ * then the two existing flags which do not have to be fast should be moved
+ * out into some other data structure and the new flags should take their
+ * place. This may require a few minor changes in the savefile code.
+ *
+ * The "CAVE_ROOM" flag is saved in the savefile and is used to determine
+ * which grids are part of "rooms", and thus which grids are affected by
+ * "illumination" spells. This flag does not have to be very fast.
+ *
+ * The "CAVE_ICKY" flag is saved in the savefile and is used to determine
+ * which grids are part of "vaults", and thus which grids cannot serve as
+ * the destinations of player teleportation. This flag does not have to
+ * be very fast.
+ *
+ * The "CAVE_MARK" flag is saved in the savefile and is used to determine
+ * which grids have been "memorized" by the player. This flag is used by
+ * the "map_info()" function to determine if a grid should be displayed.
+ * This flag is used in a few other places to determine if the player can
+ * "know" about a given grid. This flag must be very fast.
+ *
+ * The "CAVE_GLOW" flag is saved in the savefile and is used to determine
+ * which grids are "permanently illuminated". This flag is used by the
+ * "update_view()" function to help determine which viewable flags may
+ * be "seen" by the player. This flag is used by the "map_info" function
+ * to determine if a grid is only lit by the player's torch. This flag
+ * has special semantics for wall grids (see "update_view()"). This flag
+ * must be very fast.
+ *
+ * The "CAVE_WALL" flag is used to determine which grids block the player's
+ * line of sight. This flag is used by the "update_view()" function to
+ * determine which grids block line of sight, and to help determine which
+ * grids can be "seen" by the player. This flag must be very fast.
+ *
+ * The "CAVE_VIEW" flag is used to determine which grids are currently in
+ * line of sight of the player. This flag is set by (and used by) the
+ * "update_view()" function. This flag is used by any code which needs to
+ * know if the player can "view" a given grid. This flag is used by the
+ * "map_info()" function for some optional special lighting effects. The
+ * "player_has_los_bold()" macro wraps an abstraction around this flag, but
+ * certain code idioms are much more efficient. This flag is used to check
+ * if a modification to a terrain feature might affect the player's field of
+ * view. This flag is used to see if certain monsters are "visible" to the
+ * player. This flag is used to allow any monster in the player's field of
+ * view to "sense" the presence of the player. This flag must be very fast.
+ *
+ * The "CAVE_SEEN" flag is used to determine which grids are currently in
+ * line of sight of the player and also illuminated in some way. This flag
+ * is set by the "update_view()" function, using computations based on the
+ * "CAVE_VIEW" and "CAVE_WALL" and "CAVE_GLOW" flags of various grids. This
+ * flag is used by any code which needs to know if the player can "see" a
+ * given grid. This flag is used by the "map_info()" function both to see
+ * if a given "boring" grid can be seen by the player, and for some optional
+ * special lighting effects. The "player_can_see_bold()" macro wraps an
+ * abstraction around this flag, but certain code idioms are much more
+ * efficient. This flag is used to see if certain monsters are "visible" to
+ * the player. This flag is never set for a grid unless "CAVE_VIEW" is also
+ * set for the grid. Whenever the "CAVE_WALL" or "CAVE_GLOW" flag changes
+ * for a grid which has the "CAVE_VIEW" flag set, the "CAVE_SEEN" flag must
+ * be recalculated. The simplest way to do this is to call "forget_view()"
+ * and "update_view()" whenever the "CAVE_WALL" or "CAVE_GLOW" flags change
+ * for a grid which has "CAVE_VIEW" set. This flag must be very fast.
+ *
+ * The "CAVE_TEMP" flag is used for a variety of temporary purposes. This
+ * flag is used to determine if the "CAVE_SEEN" flag for a grid has changed
+ * during the "update_view()" function. This flag is used to "spread" light
+ * or darkness through a room. This flag is used by the "monster flow code".
+ * This flag must always be cleared by any code which sets it, often, this
+ * can be optimized by the use of the special "temp_g", "temp_y", "temp_x"
+ * arrays (and the special "temp_n" global). This flag must be very fast.
+ *
+ * Note that the "CAVE_MARK" flag is used for many reasons, some of which
+ * are strictly for optimization purposes. The "CAVE_MARK" flag means that
+ * even if the player cannot "see" the grid, he "knows" about the terrain in
+ * that grid. This is used to "memorize" grids when they are first "seen" by
+ * the player, and to allow certain grids to be "detected" by certain magic.
+ * Note that most grids are always memorized when they are first "seen", but
+ * "boring" grids (floor grids) are only memorized if the "view_torch_grids"
+ * option is set, or if the "view_perma_grids" option is set, and the grid
+ * in question has the "CAVE_GLOW" flag set.
+ *
+ * Objects are "memorized" in a different way, using a special "marked" flag
+ * on the object itself, which is set when an object is observed or detected.
+ * This allows objects to be "memorized" independant of the terrain features.
+ *
+ * The "update_view()" function is an extremely important function. It is
+ * called only when the player moves, significant terrain changes, or the
+ * player's blindness or torch radius changes. Note that when the player
+ * is resting, or performing any repeated actions (like digging, disarming,
+ * farming, etc), there is no need to call the "update_view()" function, so
+ * even if it was not very efficient, this would really only matter when the
+ * player was "running" through the dungeon. It sets the "CAVE_VIEW" flag
+ * on every cave grid in the player's field of view, and maintains an array
+ * of all such grids in the global "view_g" array. It also checks the torch
+ * radius of the player, and sets the "CAVE_SEEN" flag for every grid which
+ * is in the "field of view" of the player and which is also "illuminated",
+ * either by the players torch (if any) or by any permanent light source.
+ * It could use and help maintain information about multiple light sources,
+ * which would be helpful in a multi-player version of Angband.
+ *
+ * The "update_view()" function maintains the special "view_g" array, which
+ * contains exactly those grids which have the "CAVE_VIEW" flag set. This
+ * array is used by "update_view()" to (only) memorize grids which become
+ * newly "seen", and to (only) redraw grids whose "seen" value changes, which
+ * allows the use of some interesting (and very efficient) "special lighting
+ * effects". In addition, this array could be used elsewhere to quickly scan
+ * through all the grids which are in the player's field of view.
+ *
+ * Note that the "update_view()" function allows, among other things, a room
+ * to be "partially" seen as the player approaches it, with a growing cone
+ * of floor appearing as the player gets closer to the door. Also, by not
+ * turning on the "memorize perma-lit grids" option, the player will only
+ * "see" those floor grids which are actually in line of sight. And best
+ * of all, you can now activate the special lighting effects to indicate
+ * which grids are actually in the player's field of view by using dimmer
+ * colors for grids which are not in the player's field of view, and/or to
+ * indicate which grids are illuminated only by the player's torch by using
+ * the color yellow for those grids.
+ *
+ * The old "update_view()" algorithm uses the special "CAVE_EASY" flag as a
+ * temporary internal flag to mark those grids which are not only in view,
+ * but which are also "easily" in line of sight of the player. This flag
+ * is actually just the "CAVE_SEEN" flag, and the "update_view()" function
+ * makes sure to clear it for all old "CAVE_SEEN" grids, and then use it in
+ * the algorithm as "CAVE_EASY", and then clear it for all "CAVE_EASY" grids,
+ * and then reset it as appropriate for all new "CAVE_SEEN" grids. This is
+ * kind of messy, but it works. The old algorithm may disappear eventually.
+ *
+ * The new "update_view()" algorithm uses a faster and more mathematically
+ * correct algorithm, assisted by a large machine generated static array, to
+ * determine the "CAVE_VIEW" and "CAVE_SEEN" flags simultaneously. See below.
+ *
+ * It seems as though slight modifications to the "update_view()" functions
+ * would allow us to determine "reverse" line-of-sight as well as "normal"
+ * line-of-sight", which would allow monsters to have a more "correct" way
+ * to determine if they can "see" the player, since right now, they "cheat"
+ * somewhat and assume that if the player has "line of sight" to them, then
+ * they can "pretend" that they have "line of sight" to the player. But if
+ * such a change was attempted, the monsters would actually start to exhibit
+ * some undesirable behavior, such as "freezing" near the entrances to long
+ * hallways containing the player, and code would have to be added to make
+ * the monsters move around even if the player was not detectable, and to
+ * "remember" where the player was last seen, to avoid looking stupid.
+ *
+ * Note that the "CAVE_GLOW" flag means that a grid is permanently lit in
+ * some way. However, for the player to "see" the grid, as determined by
+ * the "CAVE_SEEN" flag, the player must not be blind, the grid must have
+ * the "CAVE_VIEW" flag set, and if the grid is a "wall" grid, and it is
+ * not lit by the player's torch, then it must touch a grid which does not
+ * have the "CAVE_WALL" flag set, but which does have both the "CAVE_GLOW"
+ * and "CAVE_VIEW" flags set. This last part about wall grids is induced
+ * by the semantics of "CAVE_GLOW" as applied to wall grids, and checking
+ * the technical requirements can be very expensive, especially since the
+ * grid may be touching some "illegal" grids. Luckily, it is more or less
+ * correct to restrict the "touching" grids from the eight "possible" grids
+ * to the (at most) three grids which are touching the grid, and which are
+ * closer to the player than the grid itself, which eliminates more than
+ * half of the work, including all of the potentially "illegal" grids, if
+ * at most one of the three grids is a "diagonal" grid. In addition, in
+ * almost every situation, it is possible to ignore the "CAVE_VIEW" flag
+ * on these three "touching" grids, for a variety of technical reasons.
+ * Finally, note that in most situations, it is only necessary to check
+ * a single "touching" grid, in fact, the grid which is strictly closest
+ * to the player of all the touching grids, and in fact, it is normally
+ * only necessary to check the "CAVE_GLOW" flag of that grid, again, for
+ * various technical reasons. However, one of the situations which does
+ * not work with this last reduction is the very common one in which the
+ * player approaches an illuminated room from a dark hallway, in which the
+ * two wall grids which form the "entrance" to the room would not be marked
+ * as "CAVE_SEEN", since of the three "touching" grids nearer to the player
+ * than each wall grid, only the farthest of these grids is itself marked
+ * "CAVE_GLOW".
+ *
+ *
+ * Here are some pictures of the legal "light source" radius values, in
+ * which the numbers indicate the "order" in which the grids could have
+ * been calculated, if desired. Note that the code will work with larger
+ * radiuses, though currently yields such a radius, and the game would
+ * become slower in some situations if it did.
+ *
+ * Rad=0 Rad=1 Rad=2 Rad=3
+ * No-Lite Torch,etc Lantern Artifacts
+ *
+ * 333
+ * 333 43334
+ * 212 32123 3321233
+ * @ 1@1 31@13 331@133
+ * 212 32123 3321233
+ * 333 43334
+ * 333
+ *
+ *
+ * Here is an illustration of the two different "update_view()" algorithms,
+ * in which the grids marked "%" are pillars, and the grids marked "?" are
+ * not in line of sight of the player.
+ *
+ *
+ * Sample situation
+ *
+ * #####################
+ * ############.%.%.%.%#
+ * #...@..#####........#
+ * #............%.%.%.%#
+ * #......#####........#
+ * ############........#
+ * #####################
+ *
+ *
+ * New Algorithm Old Algorithm
+ *
+ * ########????????????? ########?????????????
+ * #...@..#????????????? #...@..#?????????????
+ * #...........????????? #.........???????????
+ * #......#####.....???? #......####??????????
+ * ########?????????...# ########?????????????
+ *
+ * ########????????????? ########?????????????
+ * #.@....#????????????? #.@....#?????????????
+ * #............%??????? #...........?????????
+ * #......#####........? #......#####?????????
+ * ########??????????..# ########?????????????
+ *
+ * ########????????????? ########?????%???????
+ * #......#####........# #......#####..???????
+ * #.@..........%??????? #.@..........%???????
+ * #......#####........# #......#####..???????
+ * ########????????????? ########?????????????
+ *
+ * ########??????????..# ########?????????????
+ * #......#####........? #......#####?????????
+ * #............%??????? #...........?????????
+ * #.@....#????????????? #.@....#?????????????
+ * ########????????????? ########?????????????
+ *
+ * ########?????????%??? ########?????????????
+ * #......#####.....???? #......####??????????
+ * #...........????????? #.........???????????
+ * #...@..#????????????? #...@..#?????????????
+ * ########????????????? ########?????????????
+ */
+
+
+
+
+/*
+ * Maximum number of grids in a single octant
+ */
+#define VINFO_MAX_GRIDS 161
+
+
+/*
+ * Maximum number of slopes in a single octant
+ */
+#define VINFO_MAX_SLOPES 126
+
+
+/*
+ * Mask of bits used in a single octant
+ */
+#define VINFO_BITS_3 0x3FFFFFFF
+#define VINFO_BITS_2 0xFFFFFFFF
+#define VINFO_BITS_1 0xFFFFFFFF
+#define VINFO_BITS_0 0xFFFFFFFF
+
+
+/*
+ * Forward declare
+ */
+typedef struct vinfo_type vinfo_type;
+
+
+/*
+ * The 'vinfo_type' structure
+ */
+struct vinfo_type
+{
+ s16b grid_y[8];
+ s16b grid_x[8];
+
+ u32b bits_3;
+ u32b bits_2;
+ u32b bits_1;
+ u32b bits_0;
+
+ vinfo_type *next_0;
+ vinfo_type *next_1;
+
+ byte y;
+ byte x;
+ byte d;
+ byte r;
+};
+
+
+
+/*
+ * The array of "vinfo" objects, initialized by "vinfo_init()"
+ */
+static vinfo_type vinfo[VINFO_MAX_GRIDS];
+
+
+
+
+/*
+ * Slope scale factor
+ */
+#define SCALE 100000L
+
+
+/*
+ * The actual slopes (for reference)
+ */
+
+/* Bit : Slope Grids */
+/* --- : ----- ----- */
+/* 0 : 2439 21 */
+/* 1 : 2564 21 */
+/* 2 : 2702 21 */
+/* 3 : 2857 21 */
+/* 4 : 3030 21 */
+/* 5 : 3225 21 */
+/* 6 : 3448 21 */
+/* 7 : 3703 21 */
+/* 8 : 4000 21 */
+/* 9 : 4347 21 */
+/* 10 : 4761 21 */
+/* 11 : 5263 21 */
+/* 12 : 5882 21 */
+/* 13 : 6666 21 */
+/* 14 : 7317 22 */
+/* 15 : 7692 20 */
+/* 16 : 8108 21 */
+/* 17 : 8571 21 */
+/* 18 : 9090 20 */
+/* 19 : 9677 21 */
+/* 20 : 10344 21 */
+/* 21 : 11111 20 */
+/* 22 : 12000 21 */
+/* 23 : 12820 22 */
+/* 24 : 13043 22 */
+/* 25 : 13513 22 */
+/* 26 : 14285 20 */
+/* 27 : 15151 22 */
+/* 28 : 15789 22 */
+/* 29 : 16129 22 */
+/* 30 : 17241 22 */
+/* 31 : 17647 22 */
+/* 32 : 17948 23 */
+/* 33 : 18518 22 */
+/* 34 : 18918 22 */
+/* 35 : 20000 19 */
+/* 36 : 21212 22 */
+/* 37 : 21739 22 */
+/* 38 : 22580 22 */
+/* 39 : 23076 22 */
+/* 40 : 23809 22 */
+/* 41 : 24137 22 */
+/* 42 : 24324 23 */
+/* 43 : 25714 23 */
+/* 44 : 25925 23 */
+/* 45 : 26315 23 */
+/* 46 : 27272 22 */
+/* 47 : 28000 23 */
+/* 48 : 29032 23 */
+/* 49 : 29411 23 */
+/* 50 : 29729 24 */
+/* 51 : 30434 23 */
+/* 52 : 31034 23 */
+/* 53 : 31428 23 */
+/* 54 : 33333 18 */
+/* 55 : 35483 23 */
+/* 56 : 36000 23 */
+/* 57 : 36842 23 */
+/* 58 : 37142 24 */
+/* 59 : 37931 24 */
+/* 60 : 38461 24 */
+/* 61 : 39130 24 */
+/* 62 : 39393 24 */
+/* 63 : 40740 24 */
+/* 64 : 41176 24 */
+/* 65 : 41935 24 */
+/* 66 : 42857 23 */
+/* 67 : 44000 24 */
+/* 68 : 44827 24 */
+/* 69 : 45454 23 */
+/* 70 : 46666 24 */
+/* 71 : 47368 24 */
+/* 72 : 47826 24 */
+/* 73 : 48148 24 */
+/* 74 : 48387 24 */
+/* 75 : 51515 25 */
+/* 76 : 51724 25 */
+/* 77 : 52000 25 */
+/* 78 : 52380 25 */
+/* 79 : 52941 25 */
+/* 80 : 53846 25 */
+/* 81 : 54838 25 */
+/* 82 : 55555 24 */
+/* 83 : 56521 25 */
+/* 84 : 57575 26 */
+/* 85 : 57894 25 */
+/* 86 : 58620 25 */
+/* 87 : 60000 23 */
+/* 88 : 61290 25 */
+/* 89 : 61904 25 */
+/* 90 : 62962 25 */
+/* 91 : 63636 25 */
+/* 92 : 64705 25 */
+/* 93 : 65217 25 */
+/* 94 : 65517 25 */
+/* 95 : 67741 26 */
+/* 96 : 68000 26 */
+/* 97 : 68421 26 */
+/* 98 : 69230 26 */
+/* 99 : 70370 26 */
+/* 100 : 71428 25 */
+/* 101 : 72413 26 */
+/* 102 : 73333 26 */
+/* 103 : 73913 26 */
+/* 104 : 74193 27 */
+/* 105 : 76000 26 */
+/* 106 : 76470 26 */
+/* 107 : 77777 25 */
+/* 108 : 78947 26 */
+/* 109 : 79310 26 */
+/* 110 : 80952 26 */
+/* 111 : 81818 26 */
+/* 112 : 82608 26 */
+/* 113 : 84000 26 */
+/* 114 : 84615 26 */
+/* 115 : 85185 26 */
+/* 116 : 86206 27 */
+/* 117 : 86666 27 */
+/* 118 : 88235 27 */
+/* 119 : 89473 27 */
+/* 120 : 90476 27 */
+/* 121 : 91304 27 */
+/* 122 : 92000 27 */
+/* 123 : 92592 27 */
+/* 124 : 93103 28 */
+/* 125 : 100000 13 */
+
+
+
+/*
+ * Forward declare
+ */
+typedef struct vinfo_hack vinfo_hack;
+
+
+/*
+ * Temporary data used by "vinfo_init()"
+ *
+ * - Number of grids
+ *
+ * - Number of slopes
+ *
+ * - Slope values
+ *
+ * - Slope range per grid
+ */
+struct vinfo_hack
+{
+
+ int num_slopes;
+
+ long slopes[VINFO_MAX_SLOPES];
+
+ long slopes_min[MAX_SIGHT + 1][MAX_SIGHT + 1];
+ long slopes_max[MAX_SIGHT + 1][MAX_SIGHT + 1];
+};
+
+
+
+/*
+ * Sorting hook -- comp function -- array of long's (see below)
+ *
+ * We use "u" to point to an array of long integers.
+ */
+static bool ang_sort_comp_hook_longs(vptr u, vptr v, int a, int b)
+{
+ long *x = (long*)(u);
+
+ return (x[a] <= x[b]);
+}
+
+
+/*
+ * Sorting hook -- comp function -- array of long's (see below)
+ *
+ * We use "u" to point to an array of long integers.
+ */
+static void ang_sort_swap_hook_longs(vptr u, vptr v, int a, int b)
+{
+ long *x = (long*)(u);
+
+ long temp;
+
+ /* Swap */
+ temp = x[a];
+ x[a] = x[b];
+ x[b] = temp;
+}
+
+
+
+/*
+ * Save a slope
+ */
+static void vinfo_init_aux(vinfo_hack *hack, int y, int x, long m)
+{
+ int i;
+
+ /* Handle "legal" slopes */
+ if ((m > 0) && (m <= SCALE))
+ {
+ /* Look for that slope */
+ for (i = 0; i < hack->num_slopes; i++)
+ {
+ if (hack->slopes[i] == m) break;
+ }
+
+ /* New slope */
+ if (i == hack->num_slopes)
+ {
+ /* Paranoia */
+ if (hack->num_slopes >= VINFO_MAX_SLOPES)
+ {
+ quit_fmt("Too many slopes (%d)!",
+ VINFO_MAX_SLOPES);
+ }
+
+ /* Save the slope, and advance */
+ hack->slopes[hack->num_slopes++] = m;
+ }
+ }
+
+ /* Track slope range */
+ if (hack->slopes_min[y][x] > m) hack->slopes_min[y][x] = m;
+ if (hack->slopes_max[y][x] < m) hack->slopes_max[y][x] = m;
+}
+
+
+
+/*
+ * Initialize the "vinfo" array
+ *
+ * Full Octagon (radius 20), Grids=1149
+ *
+ * Quadrant (south east), Grids=308, Slopes=251
+ *
+ * Octant (east then south), Grids=161, Slopes=126
+ *
+ * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES
+ * have the correct values, which can be derived by setting them to
+ * a number which is too high, running this function, and using the
+ * error messages to obtain the correct values.
+ */
+errr vinfo_init(void)
+{
+ int i, y, x;
+
+ long m;
+
+ vinfo_hack *hack;
+
+ int num_grids = 0;
+
+ int queue_head = 0;
+ int queue_tail = 0;
+ vinfo_type *queue[VINFO_MAX_GRIDS*2];
+
+
+ /* Make hack */
+ MAKE(hack, vinfo_hack);
+
+
+ /* Analyze grids */
+ for (y = 0; y <= MAX_SIGHT; ++y)
+ {
+ for (x = y; x <= MAX_SIGHT; ++x)
+ {
+ /* Skip grids which are out of sight range */
+ if (distance(0, 0, y, x) > MAX_SIGHT) continue;
+
+ /* Default slope range */
+ hack->slopes_min[y][x] = 999999999;
+ hack->slopes_max[y][x] = 0;
+
+ /* Paranoia */
+ if (num_grids >= VINFO_MAX_GRIDS)
+ {
+ quit_fmt("Too many grids (%d >= %d)!",
+ num_grids, VINFO_MAX_GRIDS);
+ }
+
+ /* Count grids */
+ num_grids++;
+
+ /* Slope to the top right corner */
+ m = SCALE * (1000L * y - 500) / (1000L * x + 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+
+ /* Slope to top left corner */
+ m = SCALE * (1000L * y - 500) / (1000L * x - 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+
+ /* Slope to bottom right corner */
+ m = SCALE * (1000L * y + 500) / (1000L * x + 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+
+ /* Slope to bottom left corner */
+ m = SCALE * (1000L * y + 500) / (1000L * x - 500);
+
+ /* Handle "legal" slopes */
+ vinfo_init_aux(hack, y, x, m);
+ }
+ }
+
+
+ /* Enforce maximal efficiency */
+ if (num_grids < VINFO_MAX_GRIDS)
+ {
+ quit_fmt("Too few grids (%d < %d)!",
+ num_grids, VINFO_MAX_GRIDS);
+ }
+
+ /* Enforce maximal efficiency */
+ if (hack->num_slopes < VINFO_MAX_SLOPES)
+ {
+ quit_fmt("Too few slopes (%d < %d)!",
+ hack->num_slopes, VINFO_MAX_SLOPES);
+ }
+
+
+ /* Sort slopes numerically */
+ ang_sort_comp = ang_sort_comp_hook_longs;
+
+ /* Sort slopes numerically */
+ ang_sort_swap = ang_sort_swap_hook_longs;
+
+ /* Sort the (unique) slopes */
+ ang_sort(hack->slopes, NULL, hack->num_slopes);
+
+
+
+ /* Enqueue player grid */
+ queue[queue_tail++] = &vinfo[0];
+
+ /* Process queue */
+ while (queue_head < queue_tail)
+ {
+ int e;
+
+ vinfo_type *p;
+
+
+ /* Index */
+ e = queue_head;
+
+ /* Dequeue next grid */
+ p = queue[queue_head++];
+
+ /* Location of main grid */
+ y = vinfo[e].grid_y[0];
+ x = vinfo[e].grid_x[0];
+
+
+ /* Compute grid offsets */
+ vinfo[e].grid_y[0] = + y;
+ vinfo[e].grid_x[0] = + x;
+ vinfo[e].grid_y[1] = + x;
+ vinfo[e].grid_x[1] = + y;
+ vinfo[e].grid_y[2] = + x;
+ vinfo[e].grid_x[2] = -y;
+ vinfo[e].grid_y[3] = + y;
+ vinfo[e].grid_x[3] = -x;
+ vinfo[e].grid_y[4] = -y;
+ vinfo[e].grid_x[4] = -x;
+ vinfo[e].grid_y[5] = -x;
+ vinfo[e].grid_x[5] = -y;
+ vinfo[e].grid_y[6] = -x;
+ vinfo[e].grid_x[6] = + y;
+ vinfo[e].grid_y[7] = -y;
+ vinfo[e].grid_x[7] = + x;
+
+
+ /* Analyze slopes */
+ for (i = 0; i < hack->num_slopes; ++i)
+ {
+ m = hack->slopes[i];
+
+ /* Memorize intersection slopes (for non-player-grids) */
+ if ((e > 0) &&
+ (hack->slopes_min[y][x] < m) &&
+ (m < hack->slopes_max[y][x]))
+ {
+ switch (i / 32)
+ {
+ case 3:
+ vinfo[e].bits_3 |= (1L << (i % 32));
+ break;
+ case 2:
+ vinfo[e].bits_2 |= (1L << (i % 32));
+ break;
+ case 1:
+ vinfo[e].bits_1 |= (1L << (i % 32));
+ break;
+ case 0:
+ vinfo[e].bits_0 |= (1L << (i % 32));
+ break;
+ }
+ }
+ }
+
+
+ /* Default */
+ vinfo[e].next_0 = &vinfo[0];
+
+ /* Grid next child */
+ if (distance(0, 0, y, x + 1) <= MAX_SIGHT)
+ {
+ if ((queue[queue_tail - 1]->grid_y[0] != y) ||
+ (queue[queue_tail - 1]->grid_x[0] != x + 1))
+ {
+ vinfo[queue_tail].grid_y[0] = y;
+ vinfo[queue_tail].grid_x[0] = x + 1;
+ queue[queue_tail] = &vinfo[queue_tail];
+ queue_tail++;
+ }
+
+ vinfo[e].next_0 = &vinfo[queue_tail - 1];
+ }
+
+
+ /* Default */
+ vinfo[e].next_1 = &vinfo[0];
+
+ /* Grid diag child */
+ if (distance(0, 0, y + 1, x + 1) <= MAX_SIGHT)
+ {
+ if ((queue[queue_tail - 1]->grid_y[0] != y + 1) ||
+ (queue[queue_tail - 1]->grid_x[0] != x + 1))
+ {
+ vinfo[queue_tail].grid_y[0] = y + 1;
+ vinfo[queue_tail].grid_x[0] = x + 1;
+ queue[queue_tail] = &vinfo[queue_tail];
+ queue_tail++;
+ }
+
+ vinfo[e].next_1 = &vinfo[queue_tail - 1];
+ }
+
+
+ /* Hack -- main diagonal has special children */
+ if (y == x) vinfo[e].next_0 = vinfo[e].next_1;
+
+
+ /* Extra values */
+ vinfo[e].y = y;
+ vinfo[e].x = x;
+ vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2));
+ vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0);
+ }
+
+
+ /* Verify maximal bits XXX XXX XXX */
+ if (((vinfo[1].bits_3 | vinfo[2].bits_3) != VINFO_BITS_3) ||
+ ((vinfo[1].bits_2 | vinfo[2].bits_2) != VINFO_BITS_2) ||
+ ((vinfo[1].bits_1 | vinfo[2].bits_1) != VINFO_BITS_1) ||
+ ((vinfo[1].bits_0 | vinfo[2].bits_0) != VINFO_BITS_0))
+ {
+ quit("Incorrect bit masks!");
+ }
+
+
+ /* Kill hack */
+ KILL(hack, vinfo_hack);
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Forget the "CAVE_VIEW" grids, redrawing as needed
+ */
+void forget_view(void)
+{
+ int i;
+
+ int fast_view_n = view_n;
+
+ cave_type *c_ptr;
+
+
+ /* None to forget */
+ if (!fast_view_n) return;
+
+ /* Clear them all */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ int y = view_y[i];
+ int x = view_x[i];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */
+ c_ptr->info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* None left */
+ view_n = 0;
+}
+
+
+
+/*
+ * Calculate the complete field of view using a new algorithm
+ *
+ * If "view_y/x" and "temp_y/x" were global pointers to arrays of grids, as
+ * opposed to actual arrays of grids, then we could be more efficient by
+ * using "pointer swapping".
+ *
+ * Normally, vision along the major axes is more likely than vision
+ * along the diagonal axes, so we check the bits corresponding to
+ * the lines of sight near the major axes first.
+ *
+ * We use the "temp_y/x" array (and the "CAVE_TEMP" flag) to keep track of
+ * which grids were previously marked "CAVE_SEEN", since only those grids
+ * whose "CAVE_SEEN" value changes during this routine must be redrawn.
+ *
+ * This function is now responsible for maintaining the "CAVE_SEEN"
+ * flags as well as the "CAVE_VIEW" flags, which is good, because
+ * the only grids which normally need to be memorized and/or redrawn
+ * are the ones whose "CAVE_SEEN" flag changes during this routine.
+ *
+ * Basically, this function divides the "octagon of view" into octants of
+ * grids (where grids on the main axes and diagonal axes are "shared" by
+ * two octants), and processes each octant one at a time, processing each
+ * octant one grid at a time, processing only those grids which "might" be
+ * viewable, and setting the "CAVE_VIEW" flag for each grid for which there
+ * is an (unobstructed) line of sight from the center of the player grid to
+ * any internal point in the grid (and collecting these "CAVE_VIEW" grids
+ * into the "view_y/x" array), and setting the "CAVE_SEEN" flag for the grid
+ * if, in addition, the grid is "illuminated" in some way.
+ *
+ * This function relies on a theorem (suggested and proven by Mat Hostetter)
+ * which states that in each octant of a field of view, a given grid will
+ * be "intersected" by one or more unobstructed "lines of sight" from the
+ * center of the player grid if and only if it is "intersected" by at least
+ * one such unobstructed "line of sight" which passes directly through some
+ * corner of some grid in the octant which is not shared by any other octant.
+ * The proof is based on the fact that there are at least three significant
+ * lines of sight involving any non-shared grid in any octant, one which
+ * intersects the grid and passes though the corner of the grid closest to
+ * the player, and two which "brush" the grid, passing through the "outer"
+ * corners of the grid, and that any line of sight which intersects a grid
+ * without passing through the corner of a grid in the octant can be "slid"
+ * slowly towards the corner of the grid closest to the player, until it
+ * either reaches it or until it brushes the corner of another grid which
+ * is closer to the player, and in either case, the existanc of a suitable
+ * line of sight is thus demonstrated.
+ *
+ * It turns out that in each octant of the radius 20 "octagon of view",
+ * there are 161 grids (with 128 not shared by any other octant), and there
+ * are exactly 126 distinct "lines of sight" passing from the center of the
+ * player grid through any corner of any non-shared grid in the octant. To
+ * determine if a grid is "viewable" by the player, therefore, you need to
+ * simply show that one of these 126 lines of sight intersects the grid but
+ * does not intersect any wall grid closer to the player. So we simply use
+ * a bit vector with 126 bits to represent the set of interesting lines of
+ * sight which have not yet been obstructed by wall grids, and then we scan
+ * all the grids in the octant, moving outwards from the player grid. For
+ * each grid, if any of the lines of sight which intersect that grid have not
+ * yet been obstructed, then the grid is viewable. Furthermore, if the grid
+ * is a wall grid, then all of the lines of sight which intersect the grid
+ * should be marked as obstructed for future reference. Also, we only need
+ * to check those grids for whom at least one of the "parents" was a viewable
+ * non-wall grid, where the parents include the two grids touching the grid
+ * but closer to the player grid (one adjacent, and one diagonal). For the
+ * bit vector, we simply use 4 32-bit integers. All of the static values
+ * which are needed by this function are stored in the large "vinfo" array
+ * (above), which is machine generated by another program. XXX XXX XXX
+ *
+ * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids
+ * because the grids at the edge of the field of view use "grid zero" as
+ * their children, and the queue must be able to hold several of these
+ * special grids. Because the actual number of required grids is bizarre,
+ * we simply allocate twice as many as we would normally need. XXX XXX XXX
+ */
+void update_view(void)
+{
+ int i, o;
+ int y, x;
+
+ int radius;
+
+ int fast_view_n = view_n;
+
+ int fast_temp_n = 0;
+
+ cave_type *c_ptr;
+
+ u16b info;
+
+
+ /*** Step 0 -- Begin ***/
+
+ /* Save the old "view" grids for later */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ /* Location */
+ y = view_y[i];
+ x = view_x[i];
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Get grid info */
+ info = c_ptr->info;
+ ;
+
+ /* Save "CAVE_SEEN" grids */
+ if (info & (CAVE_SEEN))
+ {
+ /* Set "CAVE_TEMP" flag */
+ info |= (CAVE_TEMP);
+
+ /* Save grid for later */
+ temp_y[fast_temp_n] = y;
+ temp_x[fast_temp_n++] = x;
+ }
+
+ /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */
+ info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT);
+
+ /* Save cave info */
+ c_ptr->info = info;
+ }
+
+ /* Reset the "view" array */
+ fast_view_n = 0;
+
+ /* Extract "radius" value */
+ radius = p_ptr->cur_lite;
+
+ /* Handle real light */
+ if (radius > 0) ++radius;
+
+
+ /*** Step 1 -- player grid ***/
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Get grid info */
+ info = c_ptr->info;
+
+ /* Assume viewable */
+ info |= (CAVE_VIEW);
+
+ /* Torch-lit grid */
+ if (0 < radius)
+ {
+ /* Mark as "CAVE_SEEN" and torch-lit */
+ info |= (CAVE_SEEN | CAVE_PLIT);
+ }
+
+
+ /* Perma-lit grid */
+ else if (info & (CAVE_GLOW))
+ {
+ /* Mark as "CAVE_SEEN" */
+ info |= (CAVE_SEEN);
+ }
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Save in array */
+ view_y[fast_view_n] = p_ptr->py;
+ view_x[fast_view_n++] = p_ptr->px;
+
+
+ /*** Step 2 -- octants ***/
+
+ /* Scan each octant */
+ for (o = 0; o < 8; o++)
+ {
+ vinfo_type *p;
+
+ /* Last added */
+ vinfo_type *last = &vinfo[0];
+
+ /* Grid queue */
+ int queue_head = 0;
+ int queue_tail = 0;
+ vinfo_type *queue[VINFO_MAX_GRIDS*2];
+
+ /* Slope bit vector */
+ u32b bits0 = VINFO_BITS_0;
+ u32b bits1 = VINFO_BITS_1;
+ u32b bits2 = VINFO_BITS_2;
+ u32b bits3 = VINFO_BITS_3;
+
+ /* Reset queue */
+ queue_head = queue_tail = 0;
+
+ /* Initial grids */
+ queue[queue_tail++] = &vinfo[1];
+ queue[queue_tail++] = &vinfo[2];
+
+ /* Process queue */
+ while (queue_head < queue_tail)
+ {
+ /* Dequeue next grid */
+ p = queue[queue_head++];
+
+ /* Check bits */
+ if ((bits0 & (p->bits_0)) ||
+ (bits1 & (p->bits_1)) ||
+ (bits2 & (p->bits_2)) ||
+ (bits3 & (p->bits_3)))
+ {
+ /* Extract coordinate value */
+ y = p_ptr->py + p->grid_y[o];
+ x = p_ptr->px + p->grid_x[o];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Get grid info */
+ info = c_ptr->info;
+
+ /* Handle wall */
+ if (info & (CAVE_WALL))
+ {
+ /* Clear bits */
+ bits0 &= ~(p->bits_0);
+ bits1 &= ~(p->bits_1);
+ bits2 &= ~(p->bits_2);
+ bits3 &= ~(p->bits_3);
+
+ /* Newly viewable wall */
+ if (!(info & (CAVE_VIEW)))
+ {
+ /* Mark as viewable */
+ info |= (CAVE_VIEW);
+
+ /* Torch-lit grids */
+ if (p->d < radius)
+ {
+ /* Mark as "CAVE_SEEN" and torch-lit */
+ info |= (CAVE_SEEN | CAVE_PLIT);
+ }
+
+ /* Monster-lit grids */
+ else if (info & (CAVE_MLIT))
+ {
+ /* Mark as "CAVE_SEEN" */
+ info |= (CAVE_SEEN);
+ }
+
+ /* Perma-lit grids */
+ else if (info & (CAVE_GLOW))
+ {
+ /* Hack -- move towards player */
+ int yy = (y < p_ptr->py) ? (y + 1) : (y > p_ptr->py) ? (y - 1) : y;
+ int xx = (x < p_ptr->px) ? (x + 1) : (x > p_ptr->px) ? (x - 1) : x;
+
+#ifdef UPDATE_VIEW_COMPLEX_WALL_ILLUMINATION
+
+ /* Check for "complex" illumination */
+ if ((!(cave[yy][xx].info & (CAVE_WALL)) &&
+ (cave[yy][xx].info & (CAVE_GLOW))) ||
+ (!(cave[y][xx].info & (CAVE_WALL)) &&
+ (cave[y][xx].info & (CAVE_GLOW))) ||
+ (!(cave[yy][x].info & (CAVE_WALL)) &&
+ (cave[yy][x].info & (CAVE_GLOW))))
+ {
+ /* Mark as seen */
+ info |= (CAVE_SEEN);
+ }
+
+#else /* UPDATE_VIEW_COMPLEX_WALL_ILLUMINATION */
+
+ /* Check for "simple" illumination */
+ if (cave[yy][xx].info & (CAVE_GLOW))
+ {
+ /* Mark as seen */
+ info |= (CAVE_SEEN);
+ }
+
+#endif /* UPDATE_VIEW_COMPLEX_WALL_ILLUMINATION */
+
+ }
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Save in array */
+ view_y[fast_view_n] = y;
+ view_x[fast_view_n++] = x;
+ }
+ }
+
+ /* Handle non-wall */
+ else
+ {
+ /* Enqueue child */
+ if (last != p->next_0)
+ {
+ queue[queue_tail++] = last = p->next_0;
+ }
+
+ /* Enqueue child */
+ if (last != p->next_1)
+ {
+ queue[queue_tail++] = last = p->next_1;
+ }
+
+ /* Newly viewable non-wall */
+ if (!(info & (CAVE_VIEW)))
+ {
+ /* Mark as "viewable" */
+ info |= (CAVE_VIEW);
+
+ /* Torch-lit grids */
+ if (p->d < radius)
+ {
+ /* Mark as "CAVE_SEEN" and torch-lit */
+ info |= (CAVE_SEEN | CAVE_PLIT);
+ }
+
+ /* Perma-lit or monster-lit grids */
+ else if (info & (CAVE_GLOW | CAVE_MLIT))
+ {
+ /* Mark as "CAVE_SEEN" */
+ info |= (CAVE_SEEN);
+ }
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Save in array */
+ view_y[fast_view_n] = y;
+ view_x[fast_view_n++] = x;
+ }
+ }
+ }
+ }
+ }
+
+
+ /*** Step 3 -- Complete the algorithm ***/
+
+ /* Handle blindness */
+ if (p_ptr->blind)
+ {
+ /* Process "new" grids */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ /* Location */
+ y = view_y[i];
+ x = view_x[i];
+
+ /* Grid cannot be "CAVE_SEEN" */
+ cave[y][x].info &= ~(CAVE_SEEN);
+ }
+ }
+
+ /* Process "new" grids */
+ for (i = 0; i < fast_view_n; i++)
+ {
+ /* Location */
+ y = view_y[i];
+ x = view_x[i];
+
+ /* Get grid info */
+ info = cave[y][x].info;
+
+ /* Was not "CAVE_SEEN", is now "CAVE_SEEN" */
+ if ((info & (CAVE_SEEN)) && !(info & (CAVE_TEMP)))
+ {
+ /* Note */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+
+ /* Process "old" grids */
+ for (i = 0; i < fast_temp_n; i++)
+ {
+ /* Location */
+ y = temp_y[i];
+ x = temp_x[i];
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Get grid info */
+ info = c_ptr->info;
+
+ /* Clear "CAVE_TEMP" flag */
+ info &= ~(CAVE_TEMP);
+
+ /* Save cave info */
+ c_ptr->info = info;
+
+ /* Was "CAVE_SEEN", is now not "CAVE_SEEN" */
+ if (!(info & (CAVE_SEEN)))
+ {
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+
+
+ /* Save 'view_n' */
+ view_n = fast_view_n;
+}
+
+
+/*
+ * Clear monster light
+ */
+void forget_mon_lite(void)
+{
+ int i, y, x;
+
+ /* Process all the monster-lit grids */
+ for (i = 0; i < lite_n; i++)
+ {
+ /* Access location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Clear monster light flag */
+ cave[y][x].info &= ~(CAVE_MLIT);
+ }
+
+ /* Forget light array */
+ lite_n = 0;
+}
+
+
+/*
+ * Update squares illuminated by monsters
+ *
+ * Code taken from Steven Fuerst's work for ZAngband, without support
+ * for multiple lite radii, and with necessary modifications for different
+ * internal representation of dungeon/wilderness. Other minor changes
+ * are mine...
+ *
+ * I'm not sure if I can handle wide radius well. Consider the following
+ * example, with p carrying a radius 3 light source:
+ *
+ * ##%#
+ * .x..
+ * p##@
+ *
+ * % should be illuminated, although the beam path is entirely out of
+ * player's los (because of grid-based nature of cave representation)...
+ * And I'm extremely reluctant to introduce symmetrical los. The current
+ * asymmetrical system has its own merit, and all the rules of games are
+ * asymmetrical, in some way or another...
+ *
+ * The code below exploits special characteristics of radius one light
+ * where one can fairly safely use light source's visibility (in terms of los)
+ * to determine if we can illuminate walls XXX
+ *
+ * This function works within the current player's field of view
+ * calculated by update_view(), so it should normally be called
+ * whenever FoV is updated (== PU_VIEW | PU_MON_LITE). The other
+ * case is when RF9_HAS_LITE monsters have moved or dead. Monster
+ * creation occurs out of LoS, so I chose not to take this into
+ * consideration.
+ *
+ * The CAVE_TEMP flag is used by the function to remember "old" monster-lit
+ * grids so that it can only redraw squares whose visibility has changed.
+ *
+ * Doing this in the update_view() order (update "new" grids, then "old")
+ * would result in bizarre lighting effects XXX XXX
+ *
+ * It has been made possible again to draw torch/monster-lit grids in
+ * different colours, even when they are in permanently lit locations
+ * by using (CAVE_PLIT|CAVE_MLIT) as if it were old CAVE_LITE, but I don't
+ * think it's appropriate for torch lights to be visible under the Sun :)
+ * or brighter light, and it doesn't work well with PernAngband's already
+ * colourful terrain features in aesthetically pleasing ways... -- pelpel
+ */
+void update_mon_lite(void)
+{
+ int i, y, x, d;
+ int fy, fx;
+
+ cave_type *c_ptr;
+ u16b info;
+
+ bool invis;
+
+ s16b fast_lite_n = lite_n;
+ s16b fast_temp_n;
+
+
+ /* Mega-Hack -- It's unnecessary there */
+ if (p_ptr->wild_mode) return;
+
+ /* Handle special case -- Blindness */
+ if (p_ptr->blind)
+ {
+ for (i = 0; i < fast_lite_n; i++)
+ {
+ /* Light location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Forget monster light and view */
+ cave[y][x].info &= ~(CAVE_MLIT | CAVE_SEEN);
+
+ /* Redraw spot */
+ /* lite_spot(y, x); */
+ }
+
+ /* Clear the light list */
+ lite_n = 0;
+
+ /* Done */
+ return;
+ }
+
+
+ /* Remember and clear all monster-lit grids */
+ for (i = 0; i < fast_lite_n; i++)
+ {
+ /* Lit location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Access cave info of the grid */
+ info = c_ptr->info;
+
+ /* Remember it, by setting the CAVE_TEMP flag */
+ info |= (CAVE_TEMP);
+
+ /* Forget monster light */
+ info &= ~(CAVE_MLIT);
+
+ /* Unseen unless it's glowing or illuminated by player light source */
+ if (!(info & (CAVE_GLOW | CAVE_PLIT)))
+ {
+ info &= ~(CAVE_SEEN);
+ }
+
+ /* Save cave info flags */
+ c_ptr->info = info;
+ }
+
+
+ /* Clear the temp list */
+ fast_temp_n = 0;
+
+ /* Loop through monsters, adding newly lit grids to changes list */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr;
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Skip out-of-sight monsters (MAX_SIGHT + max radius) */
+ if (m_ptr->cdis > MAX_SIGHT + 1) continue;
+
+ /* Access monster race info (with possible ego mods) */
+ r_ptr = race_info_idx(m_ptr->r_idx, m_ptr->ego);
+
+ /* Skip monsters not carrying light source */
+ if (!(r_ptr->flags9 & RF9_HAS_LITE)) continue;
+
+ /* Access the location */
+ fy = m_ptr->fy;
+ fx = m_ptr->fx;
+
+ /* Extract monster grid visibility */
+ invis = !player_has_los_bold(fy, fx);
+
+ /* Nested loops may be a bad idea here XXX */
+ for (d = 0; d < 9; d++)
+ {
+ y = fy + ddy_ddd[d];
+ x = fx + ddx_ddd[d];
+
+ /* Paranoia */
+ /* if (!in_bounds(y, x)) continue; */
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Access cave info flags */
+ info = c_ptr->info;
+
+ /* Don't care grids out of player's los */
+ if (!(info & (CAVE_VIEW))) continue;
+
+ /*
+ * Avoid processing already monster-lit grids,
+ * for efficiency and to avoid temp array overflow
+ */
+ if (info & (CAVE_MLIT)) continue;
+
+ /*
+ * Hack XXX XXX -- light shouldn't penetrate walls
+ *
+ * OK NG
+ * .#. p#. | p. .p. p..
+ * p.@ ..@ | .# .#. .#.
+ * | .@ .@. ..@
+ *
+ * So if a monster carrying light source is out of player LoS,
+ * walls aren't illuminated.
+ *
+ * CAVEAT: % will be illuminated in cases like this:
+ *
+ * #%..@
+ * p....
+ *
+ * We don't have four sides for a wall grid, so...
+ */
+ if (invis && (f_info[c_ptr->feat].flags1 & FF1_NO_VISION)) continue;
+
+ /* Give monster light to the location */
+ c_ptr->info |= (CAVE_MLIT | CAVE_SEEN);
+
+ /* Save the location */
+ temp_y[fast_temp_n] = y;
+ temp_x[fast_temp_n] = x;
+ fast_temp_n++;
+ }
+ }
+
+ /* Process old grids */
+ for (i = 0; i < fast_lite_n; i++)
+ {
+ /* Access location */
+ y = lite_y[i];
+ x = lite_x[i];
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Was lit, is no longer lit */
+ if (!(c_ptr->info & (CAVE_MLIT)))
+ {
+ /* Clear the temp flag */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* See if there was a visible monster */
+ if (player_has_los_bold(y, x) && c_ptr->m_idx)
+ {
+ /* Hide the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+ else
+ {
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ /* Copy the temp array into the light array */
+ for (i = 0; i < fast_temp_n; i++)
+ {
+ /* Access location */
+ y = temp_y[i];
+ x = temp_x[i];
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+
+ /* No changes in illumination */
+ if (c_ptr->info & (CAVE_TEMP))
+ {
+ /* Clear the temp flag */
+ c_ptr->info &= ~(CAVE_TEMP);
+ }
+
+ /* Was not lit, is now lit */
+ else
+ {
+ /* Remember the location, if appropriate */
+ note_spot(y, x);
+
+ /* See if there is a monster */
+ if (c_ptr->m_idx)
+ {
+ /* Show it */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+ else
+ {
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+
+
+ /* Save the location */
+ lite_y[i] = y;
+ lite_x[i] = x;
+ }
+
+ /* Save lite_n */
+ lite_n = fast_temp_n;
+
+ /* Forget temp */
+ temp_n = 0;
+}
+
+
+
+
+
+
+/*
+ * Hack -- provide some "speed" for the "flow" code
+ * This entry is the "current index" for the "when" field
+ * Note that a "when" value of "zero" means "not used".
+ *
+ * Note that the "cost" indexes from 1 to 127 are for
+ * "old" data, and from 128 to 255 are for "new" data.
+ *
+ * This means that as long as the player does not "teleport",
+ * then any monster up to 128 + MONSTER_FLOW_DEPTH will be
+ * able to track down the player, and in general, will be
+ * able to track down either the player or a position recently
+ * occupied by the player.
+ */
+static int flow_n = 0;
+
+
+/*
+ * Hack -- forget the "flow" information
+ */
+void forget_flow(void)
+{
+
+#ifdef MONSTER_FLOW
+
+ int x, y;
+
+ /* Nothing to forget */
+ if (!flow_n) return;
+
+ /* Check the entire dungeon */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Forget the old data */
+ cave[y][x].cost = 0;
+ cave[y][x].when = 0;
+ }
+ }
+
+ /* Start over */
+ flow_n = 0;
+
+#endif
+
+}
+
+
+#ifdef MONSTER_FLOW
+
+/*
+ * Hack -- Allow us to treat the "seen" array as a queue
+ */
+static int flow_head = 0;
+static int flow_tail = 0;
+
+
+/*
+ * Take note of a reachable grid. Assume grid is legal.
+ */
+static void update_flow_aux(int y, int x, int n)
+{
+ cave_type *c_ptr;
+
+ int old_head = flow_head;
+
+
+ /* Get the grid */
+ c_ptr = &cave[y][x];
+
+ /* Ignore "pre-stamped" entries */
+ if (c_ptr->when == flow_n) return;
+
+ /* Ignore "walls" and "rubble" */
+ if (c_ptr->feat >= FEAT_RUBBLE) return;
+
+ /* Save the time-stamp */
+ c_ptr->when = flow_n;
+
+ /* Save the flow cost */
+ c_ptr->cost = n;
+
+ /* Hack -- limit flow depth */
+ if (n == MONSTER_FLOW_DEPTH) return;
+
+ /* Enqueue that entry */
+ temp_y[flow_head] = y;
+ temp_x[flow_head] = x;
+
+ /* Advance the queue */
+ if (++flow_head == TEMP_MAX) flow_head = 0;
+
+ /* Hack -- notice overflow by forgetting new entry */
+ if (flow_head == flow_tail) flow_head = old_head;
+}
+
+#endif
+
+
+/*
+ * Hack -- fill in the "cost" field of every grid that the player
+ * can "reach" with the number of steps needed to reach that grid.
+ * This also yields the "distance" of the player from every grid.
+ *
+ * In addition, mark the "when" of the grids that can reach
+ * the player with the incremented value of "flow_n".
+ *
+ * Hack -- use the "seen" array as a "circular queue".
+ *
+ * We do not need a priority queue because the cost from grid
+ * to grid is always "one" and we process them in order.
+ */
+void update_flow(void)
+{
+
+#ifdef MONSTER_FLOW
+
+ int x, y, d;
+
+ /* Hack -- disabled */
+ if (!flow_by_sound) return;
+
+ /* Paranoia -- make sure the array is empty */
+ if (temp_n) return;
+
+ /* Cycle the old entries (once per 128 updates) */
+ if (flow_n == 255)
+ {
+ /* Rotate the time-stamps */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ int w = cave[y][x].when;
+ cave[y][x].when = (w > 128) ? (w - 128) : 0;
+ }
+ }
+
+ /* Restart */
+ flow_n = 127;
+ }
+
+ /* Start a new flow (never use "zero") */
+ flow_n++;
+
+
+ /* Reset the "queue" */
+ flow_head = flow_tail = 0;
+
+ /* Add the player's grid to the queue */
+ update_flow_aux(p_ptr->py, p_ptr->px, 0);
+
+ /* Now process the queue */
+ while (flow_head != flow_tail)
+ {
+ /* Extract the next entry */
+ y = temp_y[flow_tail];
+ x = temp_x[flow_tail];
+
+ /* Forget that entry */
+ if (++flow_tail == TEMP_MAX) flow_tail = 0;
+
+ /* Add the "children" */
+ for (d = 0; d < 8; d++)
+ {
+ /* Add that child if "legal" */
+ update_flow_aux(y + ddy_ddd[d], x + ddx_ddd[d], cave[y][x].cost + 1);
+ }
+ }
+
+ /* Forget the flow info */
+ flow_head = flow_tail = 0;
+
+#endif
+
+}
+
+
+
+
+
+
+
+/*
+ * Hack -- map the current panel (plus some) ala "magic mapping"
+ */
+void map_area(void)
+{
+ int i, x, y, y1, y2, x1, x2;
+
+ cave_type *c_ptr;
+
+
+ /* Pick an area to map */
+ y1 = panel_row_min - randint(10);
+ y2 = panel_row_max + randint(10);
+ x1 = panel_col_min - randint(20);
+ x2 = panel_col_max + randint(20);
+
+ /* Speed -- shrink to fit legal bounds */
+ if (y1 < 1) y1 = 1;
+ if (y2 > cur_hgt - 2) y2 = cur_hgt - 2;
+ if (x1 < 1) x1 = 1;
+ if (x2 > cur_wid - 2) x2 = cur_wid - 2;
+
+ /* Scan that area */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ c_ptr = &cave[y][x];
+
+ /* All non-walls are "checked" */
+ if (!is_wall(c_ptr))
+ {
+ /* Memorize normal features */
+ if (!cave_plain_floor_grid(c_ptr))
+ {
+ /* Memorize the object */
+ c_ptr->info |= (CAVE_MARK);
+ }
+
+ /* Memorize known walls */
+ for (i = 0; i < 8; i++)
+ {
+ c_ptr = &cave[y + ddy_ddd[i]][x + ddx_ddd[i]];
+
+ /* Memorize walls (etc) */
+ if (is_wall(c_ptr))
+ {
+ /* Memorize the walls */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+ }
+ }
+ }
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * Light up the dungeon using "clairvoyance"
+ *
+ * This function "illuminates" every grid in the dungeon, memorizes all
+ * "objects", memorizes all grids as with magic mapping, and, under the
+ * standard option settings (view_perma_grids but not view_torch_grids)
+ * memorizes all floor grids too.
+ *
+ * Note that if "view_perma_grids" is not set, we do not memorize floor
+ * grids, since this would defeat the purpose of "view_perma_grids", not
+ * that anyone seems to play without this option.
+ *
+ * Note that if "view_torch_grids" is set, we do not memorize floor grids,
+ * since this would prevent the use of "view_torch_grids" as a method to
+ * keep track of what grids have been observed directly.
+ */
+void wiz_lite(void)
+{
+ int i, y, x;
+
+
+ /* Memorize objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx) continue;
+
+ /* Memorize */
+ o_ptr->marked = TRUE;
+ }
+
+ /* Scan all normal grids */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* Scan all normal grids */
+ for (x = 1; x < cur_wid - 1; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ o_ptr->marked = TRUE;
+ }
+ }
+
+ /* Process all non-walls */
+ /* if (c_ptr->feat < FEAT_SECRET) */
+ {
+ /* Scan all neighbors */
+ for (i = 0; i < 9; i++)
+ {
+ int yy = y + ddy_ddd[i];
+ int xx = x + ddx_ddd[i];
+
+ /* Get the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Perma-lite the grid */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Memorize normal features */
+ if (!cave_plain_floor_grid(c_ptr))
+ {
+ /* Memorize the grid */
+ c_ptr->info |= (CAVE_MARK);
+ }
+
+ /* Normally, memorize floors (see above) */
+ if (view_perma_grids && !view_torch_grids)
+ {
+ /* Memorize the grid */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+ }
+ }
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+void wiz_lite_extra(void)
+{
+ int y, x;
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ wiz_lite();
+}
+
+/*
+ * Forget the dungeon map (ala "Thinking of Maud...").
+ */
+void wiz_dark(void)
+{
+ int i, y, x;
+
+
+ /* Forget every grid */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Process the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+ }
+ }
+
+ /* Forget all objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx) continue;
+
+ /* Forget the object */
+ o_ptr->marked = FALSE;
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+
+
+/*
+ * Change the "feat" flag for a grid, and notice/redraw the grid
+ */
+void cave_set_feat(int y, int x, int feat)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Change the feature */
+ c_ptr->feat = feat;
+
+ /*
+ * Handle "wall/door" grids
+ *
+ * XXX XXX XXX This assumes c_ptr->mimic doesn't mimic terrain
+ * features whose LoS behaviour is different from its own, in
+ * most cases. Level boundaries are the most notable exception,
+ * where "real" terrain is always FEAT_PERM_SOLID, and the fact
+ * is (ab)used to prevent out-of-range access to the cave array.
+ * If we were going to implement an evil dungeon type in which
+ * everything is mimicked, then this function, los(), projectable(),
+ * project_path() and maybe some functions in melee2.c might
+ * better use c_ptr->mimic when it's set -- pelpel
+ */
+ if (!cave_sight_grid(c_ptr))
+ {
+ c_ptr->info |= (CAVE_WALL);
+ }
+
+ /* Handle "floor"/etc grids */
+ else
+ {
+ c_ptr->info &= ~(CAVE_WALL);
+ }
+
+ /* Notice & Redraw */
+ if (character_dungeon)
+ {
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+}
+
+
+/*
+ * Place floor terrain at (y, x) according to dungeon info
+ */
+void place_floor(int y, int x)
+{
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+}
+
+/*
+ * Place a cave filler at (y, x)
+ */
+void place_filler(int y, int x)
+{
+ cave_set_feat(y, x, fill_type[rand_int(100)]);
+}
+
+
+/*
+ * Calculate "incremental motion". Used by project() and shoot().
+ * Assumes that (*y,*x) lies on the path from (y1,x1) to (y2,x2).
+ */
+void mmove2(int *y, int *x, int y1, int x1, int y2, int x2)
+{
+ int dy, dx, dist, shift;
+
+ /* Extract the distance travelled */
+ dy = (*y < y1) ? y1 - *y : *y - y1;
+ dx = (*x < x1) ? x1 - *x : *x - x1;
+
+ /* Number of steps */
+ dist = (dy > dx) ? dy : dx;
+
+ /* We are calculating the next location */
+ dist++;
+
+
+ /* Calculate the total distance along each axis */
+ dy = (y2 < y1) ? (y1 - y2) : (y2 - y1);
+ dx = (x2 < x1) ? (x1 - x2) : (x2 - x1);
+
+ /* Paranoia -- Hack -- no motion */
+ if (!dy && !dx) return;
+
+
+ /* Move mostly vertically */
+ if (dy > dx)
+ {
+
+#if 0
+
+ int k;
+
+ /* Starting shift factor */
+ shift = dy >> 1;
+
+ /* Extract a shift factor */
+ for (k = 0; k < dist; k++)
+ {
+ if (shift <= 0) shift += dy;
+ shift -= dx;
+ }
+
+ /* Sometimes move along minor axis */
+ if (shift <= 0) (*x) = (x2 < x1) ? (*x - 1) : (*x + 1);
+
+ /* Always move along major axis */
+ (*y) = (y2 < y1) ? (*y - 1) : (*y + 1);
+
+#endif
+
+ /* Extract a shift factor */
+ shift = (dist * dx + (dy - 1) / 2) / dy;
+
+ /* Sometimes move along the minor axis */
+ (*x) = (x2 < x1) ? (x1 - shift) : (x1 + shift);
+
+ /* Always move along major axis */
+ (*y) = (y2 < y1) ? (y1 - dist) : (y1 + dist);
+ }
+
+ /* Move mostly horizontally */
+ else
+ {
+
+#if 0
+
+ int k;
+
+ /* Starting shift factor */
+ shift = dx >> 1;
+
+ /* Extract a shift factor */
+ for (k = 0; k < dist; k++)
+ {
+ if (shift <= 0) shift += dx;
+ shift -= dy;
+ }
+
+ /* Sometimes move along minor axis */
+ if (shift <= 0) (*y) = (y2 < y1) ? (*y - 1) : (*y + 1);
+
+ /* Always move along major axis */
+ (*x) = (x2 < x1) ? (*x - 1) : (*x + 1);
+
+#endif
+
+ /* Extract a shift factor */
+ shift = (dist * dy + (dx - 1) / 2) / dx;
+
+ /* Sometimes move along the minor axis */
+ (*y) = (y2 < y1) ? (y1 - shift) : (y1 + shift);
+
+ /* Always move along major axis */
+ (*x) = (x2 < x1) ? (x1 - dist) : (x1 + dist);
+ }
+}
+
+
+
+/*
+ * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive
+ * at the final destination, assuming no monster gets in the way.
+ *
+ * This is slightly (but significantly) different from "los(y1,x1,y2,x2)".
+ */
+bool projectable(int y1, int x1, int y2, int x2)
+{
+ int dist, y, x;
+
+ /* Start at the initial location */
+ y = y1, x = x1;
+
+ /* See "project()" */
+ for (dist = 0; dist <= MAX_RANGE; dist++)
+ {
+ /* Check for arrival at "final target" */
+ /*
+ * NB: this check was AFTER the 'never pass
+ * thru walls' clause, below. Switching them
+ * lets monsters shoot a the player if s/he is
+ * visible but in a wall
+ */
+ if ((x == x2) && (y == y2)) return (TRUE);
+
+ /* Never pass through walls */
+ if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break;
+
+ /* Calculate the new location */
+ mmove2(&y, &x, y1, x1, y2, x2);
+ }
+
+
+ /* Assume obstruction */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Standard "find me a location" function
+ *
+ * Obtains a legal location within the given distance of the initial
+ * location, and with "los()" from the source to destination location.
+ *
+ * This function is often called from inside a loop which searches for
+ * locations while increasing the "d" distance.
+ *
+ * Currently the "m" parameter is unused.
+ */
+void scatter(int *yp, int *xp, int y, int x, int d, int m)
+{
+ int nx, ny;
+ int attempts_left = 5000;
+
+ /* Unused */
+ m = m;
+
+
+ /* Pick a location */
+ while (--attempts_left)
+ {
+ /* Pick a new location */
+ ny = rand_spread(y, d);
+ nx = rand_spread(x, d);
+
+ /* Ignore illegal locations and outer walls */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Ignore "excessively distant" locations */
+ if ((d > 1) && (distance(y, x, ny, nx) > d)) continue;
+
+ /* Require "line of sight" */
+ if (los(y, x, ny, nx)) break;
+ }
+
+ if (attempts_left > 0)
+ {
+ /* Save the location */
+ (*yp) = ny;
+ (*xp) = nx;
+ }
+}
+
+
+
+
+/*
+ * Track a new monster
+ */
+void health_track(int m_idx)
+{
+ /* Track a new guy */
+ health_who = m_idx;
+
+ /* Redraw (later) */
+ p_ptr->redraw |= (PR_HEALTH);
+}
+
+
+
+/*
+ * Hack -- track the given monster race
+ */
+void monster_race_track(int r_idx, int ego)
+{
+ /* Save this monster ID */
+ monster_race_idx = r_idx;
+ monster_ego_idx = ego;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+}
+
+
+
+/*
+ * Hack -- track the given object kind
+ */
+void object_track(object_type *o_ptr)
+{
+ /* Save this monster ID */
+ tracked_object = o_ptr;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OBJECT);
+}
+
+
+
+/*
+ * Something has happened to disturb the player.
+ *
+ * The first arg indicates a major disturbance, which affects search.
+ *
+ * The second arg is currently unused, but could induce output flush.
+ *
+ * All disturbance cancels repeated commands, resting, and running.
+ */
+void disturb(int stop_search, int unused_flag)
+{
+ /* Unused */
+ unused_flag = unused_flag;
+
+ /* Cancel auto-commands */
+ /* command_new = 0; */
+
+ /* Cancel repeated commands */
+ if (command_rep)
+ {
+ /* Cancel */
+ command_rep = 0;
+
+ /* Redraw the state (later) */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Cancel Resting */
+ if (resting)
+ {
+ /* Cancel */
+ resting = 0;
+
+ /* Redraw the state (later) */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Cancel running */
+ if (running)
+ {
+ /* Cancel */
+ running = 0;
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+ }
+
+ /* Cancel searching if requested */
+ if (stop_search && p_ptr->searching)
+ {
+ /* Cancel */
+ p_ptr->searching = FALSE;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Flush the input if requested */
+ if (flush_disturb) flush();
+}
+
+
+/*
+ * Hack -- Check if a level is a "quest" level
+ */
+int is_quest(int level)
+{
+ int i = random_quest_number();
+
+ /* Check quests */
+ if (p_ptr->inside_quest)
+ return (p_ptr->inside_quest);
+
+ if (i) return (QUEST_RANDOM);
+
+ /* Nope */
+ return (0);
+}
+
+
+/*
+ * Return the index of the random quest on this level
+ * (or zero)
+ */
+int random_quest_number()
+{
+ if ((dun_level >= 1) && (dun_level < MAX_RANDOM_QUEST) &&
+ (dungeon_flags1 & DF1_PRINCIPAL) &&
+ (random_quests[dun_level].type) &&
+ (!random_quests[dun_level].done) &&
+ (!is_randhero(dun_level)))
+ {
+ return dun_level;
+ }
+
+ /* Nope */
+ return 0;
+}
+
+
+/*
+ * handle spell effects
+ */
+int effect_pop()
+{
+ int i;
+
+ for (i = 1; i < MAX_EFFECTS; i++)
+ if (!effects[i].time)
+ return i;
+ return -1;
+}
+
+int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags)
+{
+ int i;
+
+ if ((i = effect_pop()) == -1) return -1;
+
+ effects[i].type = type;
+ effects[i].dam = dam;
+ effects[i].time = time;
+ effects[i].flags = flags;
+ effects[i].cx = cx;
+ effects[i].cy = cy;
+ effects[i].rad = rad;
+ return i;
+}
diff --git a/src/cmd1.c b/src/cmd1.c
new file mode 100644
index 00000000..12a30f48
--- /dev/null
+++ b/src/cmd1.c
@@ -0,0 +1,5501 @@
+/* File: cmd1.c */
+
+/* Purpose: Movement commands (part 1) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#define MAX_VAMPIRIC_DRAIN 100
+
+
+/*
+ * Determine if the player "hits" a monster (normal combat).
+ * Note -- Always miss 5%, always hit 5%, otherwise random.
+ */
+bool test_hit_fire(int chance, int ac, int vis)
+{
+ int k;
+
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Instant miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Never hit */
+ if (chance <= 0) return (FALSE);
+
+ /* Invisible monsters are harder to hit */
+ if (!vis) chance = (chance + 1) / 2;
+
+ /* Power competes against armor */
+ if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE);
+
+ /* Assume hit */
+ return (TRUE);
+}
+
+
+
+/*
+ * Determine if the player "hits" a monster (normal combat).
+ *
+ * Note -- Always miss 5%, always hit 5%, otherwise random.
+ */
+bool test_hit_norm(int chance, int ac, int vis)
+{
+ int k;
+
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Instant miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Wimpy attack never hits */
+ if (chance <= 0) return (FALSE);
+
+ /* Penalize invisible targets */
+ if (!vis) chance = (chance + 1) / 2;
+
+ /* Power must defeat armor */
+ if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE);
+
+ /* Assume hit */
+ return (TRUE);
+}
+
+
+
+/*
+ * Critical hits (from objects thrown by player)
+ * Factor in item weight, total plusses, and player level.
+ */
+s16b critical_shot(int weight, int plus, int dam, int skill)
+{
+ int i, k;
+
+
+ /* Extract "shot" power */
+ i = (weight + ((p_ptr->to_h + plus) * 4) +
+ get_skill_scale(skill, 100));
+ i += 50 * p_ptr->xtra_crit;
+ i += luck( -100, 100);
+
+ /* Critical hit */
+ if (randint(5000) <= i)
+ {
+ k = weight + randint(500);
+
+ if (k < 500)
+ {
+ msg_print("It was a good hit!");
+ dam = 2 * dam + 5;
+ }
+ else if (k < 1000)
+ {
+ msg_print("It was a great hit!");
+ dam = 2 * dam + 10;
+ }
+ else
+ {
+ msg_print("It was a superb hit!");
+ dam = 3 * dam + 15;
+ }
+ }
+
+ return (dam);
+}
+
+/*
+ * Critical hits (by player)
+ *
+ * Factor in weapon weight, total plusses, player level.
+ */
+s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool *done_crit)
+{
+ int i, k, num = randint(5000);
+
+ *done_crit = FALSE;
+
+ /* Extract "blow" power */
+ i = (weight + ((p_ptr->to_h + plus) * 5) +
+ get_skill_scale(p_ptr->melee_style, 150));
+ i += 50 * p_ptr->xtra_crit;
+ if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS))
+ {
+ i += get_skill_scale(SKILL_CRITS, 40 * 50);
+ }
+ i += luck( -100, 100);
+
+ /* Force good strikes */
+ if (p_ptr->tim_deadly)
+ {
+ set_tim_deadly(p_ptr->tim_deadly - 1);
+ msg_print("It was a *GREAT* hit!");
+ dam = 3 * dam + 20;
+ *done_crit = TRUE;
+ }
+
+ /* Chance */
+ else if (num <= i)
+ {
+ k = weight + randint(650);
+ if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS))
+ {
+ k += get_skill_scale(SKILL_CRITS, 400);
+ }
+
+ if (k < 400)
+ {
+ msg_print("It was a good hit!");
+ dam = 2 * dam + 5;
+ }
+ else if (k < 700)
+ {
+ msg_print("It was a great hit!");
+ dam = 2 * dam + 10;
+ }
+ else if (k < 900)
+ {
+ msg_print("It was a superb hit!");
+ dam = 3 * dam + 15;
+ }
+ else if (k < 1300)
+ {
+ msg_print("It was a *GREAT* hit!");
+ dam = 3 * dam + 20;
+ }
+ else
+ {
+ msg_print("It was a *SUPERB* hit!");
+ dam = ((7 * dam) / 2) + 25;
+ }
+ *done_crit = TRUE;
+ }
+
+ return (dam);
+}
+
+
+
+/*
+ * Extract the "total damage" from a given object hitting a given monster.
+ *
+ * Note that "flasks of oil" do NOT do fire damage, although they
+ * certainly could be made to do so. XXX XXX
+ *
+ * Note that most brands and slays are x3, except Slay Animal (x2),
+ * Slay Evil (x2), and Kill dragon (x5).
+ */
+s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr,
+ s32b *special)
+{
+ int mult = 1;
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Some "weapons" and "ammo" do extra damage */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOOMERANG:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_DIGGING:
+ {
+ /* Slay Animal */
+ if ((f1 & (TR1_SLAY_ANIMAL)) && (r_ptr->flags3 & (RF3_ANIMAL)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_ANIMAL);
+ }
+
+ if (mult < 2) mult = 2;
+ }
+
+ /* Slay Evil */
+ if ((f1 & (TR1_SLAY_EVIL)) && (r_ptr->flags3 & (RF3_EVIL)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+
+ if (mult < 2) mult = 2;
+ }
+
+ /* Slay Undead */
+ if ((f1 & (TR1_SLAY_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Demon */
+ if ((f1 & (TR1_SLAY_DEMON)) && (r_ptr->flags3 & (RF3_DEMON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Orc */
+ if ((f1 & (TR1_SLAY_ORC)) && (r_ptr->flags3 & (RF3_ORC)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_ORC);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Troll */
+ if ((f1 & (TR1_SLAY_TROLL)) && (r_ptr->flags3 & (RF3_TROLL)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_TROLL);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Giant */
+ if ((f1 & (TR1_SLAY_GIANT)) && (r_ptr->flags3 & (RF3_GIANT)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_GIANT);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Slay Dragon */
+ if ((f1 & (TR1_SLAY_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DRAGON);
+ }
+
+ if (mult < 3) mult = 3;
+ }
+
+ /* Execute Dragon */
+ if ((f1 & (TR1_KILL_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DRAGON);
+ }
+
+ if (mult < 5) mult = 5;
+ }
+
+ /* Execute Undead */
+ if ((f5 & (TR5_KILL_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ if (mult < 5) mult = 5;
+ }
+
+ /* Execute Demon */
+ if ((f5 & (TR5_KILL_DEMON)) && (r_ptr->flags3 & (RF3_DEMON)))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ if (mult < 5) mult = 5;
+ }
+
+
+ /* Brand (Acid) */
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_ACID))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_ACID);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_ACID))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_ACID);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Elec) */
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_ELEC))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_ELEC);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_ELEC))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Fire) */
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_FIRE))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_FIRE);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags3 & (RF3_SUSCEP_FIRE))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Cold) */
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (mult < 6) mult = 6;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ }
+ }
+
+ /* Brand (Poison) */
+ if (f1 & (TR1_BRAND_POIS) || (p_ptr->tim_poison))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ }
+
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_POIS);
+ }
+ if (mult < 6) mult = 6;
+ if (magik(95)) *special |= SPEC_POIS;
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (mult < 3) mult = 3;
+ if (magik(50)) *special |= SPEC_POIS;
+ }
+ }
+
+ /* Wounding */
+ if (f5 & (TR5_WOUNDING))
+ {
+ /* Notice immunity */
+ if (r_ptr->flags8 & (RF8_NO_CUT))
+ {
+ if (m_ptr->ml)
+ {
+ r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT);
+ }
+ }
+
+ /* Otherwise, take the damage */
+ else
+ {
+ if (magik(50)) *special |= SPEC_CUT;
+ }
+ }
+ break;
+ }
+ }
+
+
+ /* Return the total damage */
+ return (tdam * mult);
+}
+
+
+/*
+ * Search for hidden things
+ */
+void search(void)
+{
+ int y, x, chance;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ cave_type *c_ptr;
+
+
+ /* Start with base search ability */
+ chance = p_ptr->skill_srh;
+
+ /* Penalize various conditions */
+ if (p_ptr->blind || no_lite()) chance = chance / 10;
+ if (p_ptr->confused || p_ptr->image) chance = chance / 10;
+
+ /* Search the nearby grids, which are always in bounds */
+ for (y = (p_ptr->py - 1); y <= (p_ptr->py + 1); y++)
+ {
+ for (x = (p_ptr->px - 1); x <= (p_ptr->px + 1); x++)
+ {
+ /* Sometimes, notice things */
+ if (rand_int(100) < chance)
+ {
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Invisible trap */
+ if ((c_ptr->t_idx != 0) && !(c_ptr->info & CAVE_TRDT))
+ {
+ /* Pick a trap */
+ pick_trap(y, x);
+
+ /* Message */
+ msg_print("You have found a trap.");
+
+ /* Disturb */
+ disturb(0, 0);
+ }
+
+ /* Secret door */
+ if (c_ptr->feat == FEAT_SECRET)
+ {
+ /* Message */
+ msg_print("You have found a secret door.");
+
+ /* Pick a door XXX XXX XXX */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ cave[y][x].mimic = 0;
+ lite_spot(y, x);
+
+ /* Disturb */
+ disturb(0, 0);
+ }
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx;
+ this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip non-chests */
+ if (o_ptr->tval != TV_CHEST) continue;
+
+ /* Skip non-trapped chests */
+ if (!o_ptr->pval) continue;
+
+ /* Identify once */
+ if (!object_known_p(o_ptr))
+ {
+ /* Message */
+ msg_print("You have discovered a trap on the chest!");
+
+ /* Know the trap */
+ object_known(o_ptr);
+
+ /* Notice it */
+ disturb(0, 0);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * Player "wants" to pick up an object or gold.
+ * Note that we ONLY handle things that can be picked up.
+ * See "move_player()" for handling of other things.
+ */
+void carry(int pickup)
+{
+ if (!p_ptr->disembodied)
+ {
+ py_pickup_floor(pickup);
+ }
+}
+
+
+/*
+ * Handle player hitting a real trap
+ */
+static void hit_trap(void)
+{
+ bool ident = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Disturb the player */
+ disturb(0, 0);
+
+ /* Get the cave grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+ if (c_ptr->t_idx != 0)
+ {
+ ident = player_activate_trap_type(p_ptr->py, p_ptr->px, NULL, -1);
+ if (ident)
+ {
+ t_info[c_ptr->t_idx].ident = TRUE;
+ msg_format("You identified the trap as %s.",
+ t_name + t_info[c_ptr->t_idx].name);
+ }
+ }
+}
+
+
+void touch_zap_player(monster_type *m_ptr)
+{
+ int aura_damage = 0;
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+
+ if (r_ptr->flags2 & (RF2_AURA_FIRE))
+ {
+ if (!(p_ptr->immune_fire))
+ {
+ char aura_dam[80];
+
+ aura_damage =
+ damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17));
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(aura_dam, m_ptr, 0x88);
+
+ msg_print("You are suddenly very hot!");
+
+ if (p_ptr->oppose_fire) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->resist_fire) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->sensible_fire) aura_damage = (aura_damage + 2) * 2;
+
+ take_hit(aura_damage, aura_dam);
+ r_ptr->r_flags2 |= RF2_AURA_FIRE;
+ handle_stuff();
+ }
+ }
+
+
+ if (r_ptr->flags2 & (RF2_AURA_ELEC))
+ {
+ if (!(p_ptr->immune_elec))
+ {
+ char aura_dam[80];
+
+ aura_damage =
+ damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17));
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(aura_dam, m_ptr, 0x88);
+
+ if (p_ptr->oppose_elec) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->resist_elec) aura_damage = (aura_damage + 2) / 3;
+
+ msg_print("You get zapped!");
+ take_hit(aura_damage, aura_dam);
+ r_ptr->r_flags2 |= RF2_AURA_ELEC;
+ handle_stuff();
+ }
+ }
+}
+
+#if 0 /* not used, eliminates a compiler warning */
+static void natural_attack(s16b m_idx, int attack, bool *fear, bool *mdeath)
+{
+ int k, bonus, chance;
+
+ int n_weight = 0;
+ bool done_crit;
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ char m_name[80];
+
+ int dss, ddd;
+
+ char *atk_desc;
+
+
+ switch (attack)
+ {
+ default:
+ {
+ dss = ddd = n_weight = 1;
+ atk_desc = "undefined body part";
+
+ break;
+ }
+ }
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+
+ /* Calculate the "attack quality" */
+ bonus = p_ptr->to_h;
+ chance = (p_ptr->skill_thn + (bonus * BTH_PLUS_ADJ));
+
+ /* Test for hit */
+ if (test_hit_norm(chance, m_ptr->ac, m_ptr->ml))
+ {
+ /* Sound */
+ sound(SOUND_HIT);
+
+ msg_format("You hit %s with your %s.", m_name, atk_desc);
+
+ k = damroll(ddd, dss);
+ k = critical_norm(n_weight, p_ptr->to_h, k, -1, &done_crit);
+
+ /* Apply the player damage bonuses */
+ k += p_ptr->to_d;
+
+ /* No negative damage */
+ if (k < 0) k = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.", k, m_ptr->hp);
+ }
+
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ {
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+
+ break;
+ }
+
+ case 0:
+ {
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+
+ break;
+ }
+ }
+
+ /* Damage, check for fear and mdeath */
+ switch (attack)
+ {
+ default:
+ {
+ *mdeath = mon_take_hit(m_idx, k, fear, NULL);
+ break;
+ }
+ }
+
+ touch_zap_player(m_ptr);
+ }
+
+ /* Player misses */
+ else
+ {
+ /* Sound */
+ sound(SOUND_MISS);
+
+ /* Message */
+ msg_format("You miss %s.", m_name);
+ }
+}
+
+#endif
+
+/*
+ * Carried monster can attack too.
+ * Based on monst_attack_monst.
+ */
+static void carried_monster_attack(s16b m_idx, bool *fear, bool *mdeath,
+ int x, int y)
+{
+ monster_type *t_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr;
+
+ monster_race *tr_ptr = race_inf(t_ptr);
+
+ cave_type *c_ptr;
+
+ int ap_cnt;
+
+ int ac, rlev, pt;
+
+ char t_name[80];
+
+ cptr sym_name = symbiote_name(TRUE);
+
+ char temp[80];
+
+ bool blinked = FALSE, touched = FALSE;
+
+ byte y_saver = t_ptr->fy;
+
+ byte x_saver = t_ptr->fx;
+
+ object_type *o_ptr;
+
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ if (!o_ptr->k_idx) return;
+
+ c_ptr = &cave[y][x];
+
+ r_ptr = &r_info[o_ptr->pval];
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((o_ptr->elevel >= 1) ? o_ptr->elevel : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!t_ptr->ml)
+ {
+ msg_print("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool visible = FALSE;
+ bool obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+#if 0
+
+ /* Extract visibility from carrying lite */
+ if (r_ptr->flags9 & RF9_HAS_LITE) visible = TRUE;
+
+#endif /* 0 */
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ {
+ power = 60;
+ break;
+ }
+ case RBE_POISON:
+ {
+ power = 5;
+ break;
+ }
+ case RBE_UN_BONUS:
+ {
+ power = 20;
+ break;
+ }
+ case RBE_UN_POWER:
+ {
+ power = 15;
+ break;
+ }
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ case RBE_ABOMINATION:
+ power = 20;
+ break;
+ }
+
+
+ /* Monster hits */
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ disturb(1, 0);
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's %s.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs %s for money.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = "insults %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = "moans at %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ act = "sings to %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+ }
+
+ /* Message */
+ if (act)
+ {
+ strfmt(temp, act, t_name);
+ if (t_ptr->ml)
+ msg_format("%s %s", sym_name, temp);
+
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ pt = GF_MISSILE;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ damage = 0;
+ pt = 0;
+ break;
+ }
+
+ case RBE_HURT:
+ case RBE_SANITY:
+ {
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+ break;
+ }
+
+ case RBE_POISON:
+ case RBE_DISEASE:
+ {
+ pt = GF_POIS;
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ case RBE_ABOMINATION:
+ {
+ pt = GF_DISENCHANT;
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ {
+ pt = damage = 0;
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ case RBE_EAT_GOLD:
+ {
+ pt = damage = 0;
+ if (randint(2) == 1) blinked = TRUE;
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ pt = GF_ACID;
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ pt = GF_ELEC;
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ pt = GF_FIRE;
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ pt = GF_COLD;
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ break;
+ }
+
+ case RBE_CONFUSE:
+ case RBE_HALLU:
+ {
+ pt = GF_CONFUSION;
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ pt = GF_TURN_ALL;
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ pt = GF_OLD_SLEEP; /* sort of close... */
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ case RBE_LOSE_INT:
+ case RBE_LOSE_WIS:
+ case RBE_LOSE_DEX:
+ case RBE_LOSE_CON:
+ case RBE_LOSE_CHR:
+ case RBE_LOSE_ALL:
+ case RBE_PARASITE:
+ {
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 8);
+ }
+ break;
+ }
+
+ case RBE_EXP_10:
+ case RBE_EXP_20:
+ case RBE_EXP_40:
+ case RBE_EXP_80:
+ {
+ pt = GF_NETHER;
+ break;
+ }
+
+ case RBE_TIME:
+ {
+ pt = GF_TIME;
+ break;
+ }
+
+ default:
+ {
+ pt = 0;
+ break;
+ }
+ }
+
+ if (pt)
+ {
+ /* Do damage if not exploding */
+ project(0, 0, t_ptr->fy, t_ptr->fx,
+ (pt == GF_OLD_SLEEP ? r_ptr->level : damage), pt,
+ PROJECT_KILL | PROJECT_STOP);
+
+ if (touched)
+ {
+ /* Aura fire */
+ if ((tr_ptr->flags2 & RF2_AURA_FIRE) &&
+ !(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You are suddenly very hot!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_FIRE;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_FIRE, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ /* Aura elec */
+ if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) &&
+ !(r_ptr->flags3 & (RF3_IM_ELEC)))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You get zapped!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_ELEC;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_ELEC, PROJECT_KILL | PROJECT_STOP);
+ }
+ }
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+ {
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("%s misses %s.", sym_name, t_name);
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+ /* Blink away */
+ if (blinked)
+ {
+ msg_format("You and %s flee laughing!", symbiote_name(FALSE));
+
+ teleport_player(MAX_SIGHT * 2 + 5);
+ }
+}
+
+/*
+ * Carried monster can attack too.
+ * Based on monst_attack_monst.
+ */
+static void incarnate_monster_attack(s16b m_idx, bool *fear, bool *mdeath,
+ int x, int y)
+{
+ monster_type *t_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr;
+
+ monster_race *tr_ptr = race_inf(t_ptr);
+
+ cave_type *c_ptr;
+
+ int ap_cnt;
+
+ int ac, rlev, pt;
+
+ char t_name[80];
+
+ char temp[80];
+
+ bool blinked = FALSE, touched = FALSE;
+
+ byte y_saver = t_ptr->fy;
+
+ byte x_saver = t_ptr->fx;
+
+
+ if (!p_ptr->body_monster) return;
+
+ c_ptr = &cave[y][x];
+
+ r_ptr = race_info_idx(p_ptr->body_monster, 0);
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!t_ptr->ml)
+ {
+ msg_print("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < (p_ptr->num_blow > 4) ? 4 : p_ptr->num_blow;
+ ap_cnt++)
+ {
+ bool visible = FALSE;
+ bool obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+#if 0
+
+ /* Extract visibility from carrying lite */
+ if (r_ptr->flags9 & RF9_HAS_LITE) visible = TRUE;
+
+#endif /* 0 */
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ power = 60;
+ break;
+ case RBE_POISON:
+ power = 5;
+ break;
+ case RBE_UN_BONUS:
+ power = 20;
+ break;
+ case RBE_UN_POWER:
+ power = 15;
+ break;
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ }
+
+
+ /* Monster hits */
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ disturb(1, 0);
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hit %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touch %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punch %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kick %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claw %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bite %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "sting %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's %s.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butt %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crush %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulf %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charge %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawl on %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drool on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spit on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gaze at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wail at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "release spores at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "project XXX4's at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "beg %s for money.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = "insult %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = "moan at %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ act = "sing to %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+ }
+
+ /* Message */
+ if (act)
+ {
+ strfmt(temp, act, t_name);
+ if (t_ptr->ml)
+ msg_format("You %s", temp);
+
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side) + p_ptr->to_d;
+
+ pt = GF_MISSILE;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ damage = 0;
+ pt = 0;
+ break;
+ }
+
+ case RBE_HURT:
+ case RBE_SANITY:
+ {
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+ break;
+ }
+
+ case RBE_POISON:
+ case RBE_DISEASE:
+ {
+ pt = GF_POIS;
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ {
+ pt = GF_DISENCHANT;
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ {
+ pt = damage = 0;
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ case RBE_EAT_GOLD:
+ {
+ pt = damage = 0;
+ if (randint(2) == 1) blinked = TRUE;
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ pt = GF_ACID;
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ pt = GF_ELEC;
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ pt = GF_FIRE;
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ pt = GF_COLD;
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ break;
+ }
+
+ case RBE_HALLU:
+ case RBE_CONFUSE:
+ {
+ pt = GF_CONFUSION;
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ pt = GF_TURN_ALL;
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ pt = GF_OLD_SLEEP; /* sort of close... */
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ case RBE_LOSE_INT:
+ case RBE_LOSE_WIS:
+ case RBE_LOSE_DEX:
+ case RBE_LOSE_CON:
+ case RBE_LOSE_CHR:
+ case RBE_LOSE_ALL:
+ case RBE_PARASITE:
+ {
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 8);
+ }
+ break;
+ }
+
+ case RBE_EXP_10:
+ case RBE_EXP_20:
+ case RBE_EXP_40:
+ case RBE_EXP_80:
+ {
+ pt = GF_NETHER;
+ break;
+ }
+
+ case RBE_TIME:
+ {
+ pt = GF_TIME;
+ break;
+ }
+
+ default:
+ {
+ pt = 0;
+ break;
+ }
+ }
+
+ if (pt)
+ {
+ /* Do damage if not exploding */
+ project(0, 0, t_ptr->fy, t_ptr->fx,
+ (pt == GF_OLD_SLEEP ? p_ptr->lev * 2 : damage), pt,
+ PROJECT_KILL | PROJECT_STOP);
+
+ if (touched)
+ {
+ /* Aura fire */
+ if ((tr_ptr->flags2 & RF2_AURA_FIRE) &&
+ !(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You are suddenly very hot!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_FIRE;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_FIRE, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ /* Aura elec */
+ if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) &&
+ !(r_ptr->flags3 & (RF3_IM_ELEC)))
+ {
+ if (t_ptr->ml)
+ {
+ blinked = FALSE;
+ msg_format("You get zapped!");
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_ELEC;
+ }
+ project(m_idx, 0, p_ptr->py, p_ptr->px,
+ damroll(1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_ELEC, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ }
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+ {
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("You miss %s.", t_name);
+
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+ /* Blink away */
+ if (blinked)
+ {
+ msg_print("You flee laughing!");
+
+ teleport_player(MAX_SIGHT * 2 + 5);
+ }
+}
+
+
+/*
+ * Fetch an attack description from dam_*.txt files.
+ */
+
+static void flavored_attack(int percent, char *output)
+{
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ bool insane = (rand_int(100) < insanity);
+
+ if (percent < 5)
+ {
+ if (!insane)
+ strcpy(output, "You scratch %s.");
+ else
+ get_rnd_line("dam_none.txt", output);
+
+ }
+ else if (percent < 30)
+ {
+ if (!insane)
+ strcpy(output, "You hit %s.");
+ else
+ get_rnd_line("dam_med.txt", output);
+ }
+ else if (percent < 60)
+ {
+ if (!insane)
+ strcpy(output, "You wound %s.");
+ else
+ get_rnd_line("dam_lots.txt", output);
+ }
+ else if (percent < 95)
+ {
+ if (!insane)
+ strcpy(output, "You cripple %s.");
+ else
+ get_rnd_line("dam_huge.txt", output);
+
+ }
+ else
+ {
+ if (!insane)
+ strcpy(output, "You demolish %s.");
+ else
+ get_rnd_line("dam_xxx.txt", output);
+ }
+}
+
+
+/*
+ * Apply the special effects of an attack
+ */
+void attack_special(monster_type *m_ptr, s32b special, int dam)
+{
+ char m_name[80];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Special - Cut monster */
+ if (special & SPEC_CUT)
+ {
+ /* Cut the monster */
+ if (r_ptr->flags8 & (RF8_NO_CUT))
+ {
+ if (m_ptr->ml)
+ {
+ r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT);
+ }
+ }
+ else if (rand_int(100) >= r_ptr->level)
+ {
+ /* Already partially poisoned */
+ if (m_ptr->bleeding) msg_format("%^s is bleeding more strongly.",
+ m_name);
+ /* Was not poisoned */
+ else
+ msg_format("%^s is bleeding.", m_name);
+
+ m_ptr->bleeding += dam * 2;
+ }
+ }
+
+ /* Special - Poison monster */
+ if (special & SPEC_POIS)
+ {
+ /* Poison the monster */
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ }
+ /* Notice susceptibility */
+ else if (r_ptr->flags9 & (RF9_SUSCEP_POIS))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags9 |= (RF9_SUSCEP_POIS);
+ }
+ /* Already partially poisoned */
+ if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name);
+ /* Was not poisoned */
+ else
+ msg_format("%^s is poisoned.", m_name);
+
+ m_ptr->poisoned += dam * 2;
+ }
+ else if (rand_int(100) >= r_ptr->level)
+ {
+ /* Already partially poisoned */
+ if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name);
+ /* Was not poisoned */
+ else
+ msg_format("%^s is poisoned.", m_name);
+
+ m_ptr->poisoned += dam;
+ }
+ }
+}
+
+
+/*
+ * Bare handed attacks
+ */
+static void py_attack_hand(int *k, monster_type *m_ptr, s32b *special)
+{
+ s16b special_effect = 0, stun_effect = 0, times = 0;
+ martial_arts *ma_ptr, *old_ptr, *blow_table = ma_blows;
+ int resist_stun = 0, max = MAX_MA;
+ monster_race *r_ptr = race_inf(m_ptr);
+ char m_name[80];
+ bool desc = FALSE;
+ bool done_crit;
+ int plev = p_ptr->lev;
+
+ if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) &&
+ (p_ptr->melee_style == SKILL_BEAR))
+ {
+ blow_table = bear_blows;
+ max = MAX_BEAR;
+ plev = get_skill(SKILL_BEAR);
+ }
+ if (p_ptr->melee_style == SKILL_HAND)
+ {
+ blow_table = ma_blows;
+ max = MAX_MA;
+ plev = get_skill(SKILL_HAND);
+ }
+ ma_ptr = &blow_table[0];
+ old_ptr = &blow_table[0];
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ if (r_ptr->flags1 & RF1_UNIQUE) resist_stun += 88;
+ if (r_ptr->flags3 & RF3_NO_CONF) resist_stun += 44;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) resist_stun += 44;
+ if ((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_NONLIVING)) resist_stun += 88;
+
+ if (plev)
+ {
+ for (times = 0; times < (plev < 7 ? 1 : plev / 7); times++)
+ {
+ do
+ {
+ ma_ptr = &blow_table[(randint(max)) - 1];
+ }
+ while ((ma_ptr->min_level > plev) || (randint(plev) < ma_ptr->chance));
+
+ /* keep the highest level attack available we found */
+ if ((ma_ptr->min_level > old_ptr->min_level) &&
+ !(p_ptr->stun || p_ptr->confused))
+ {
+ old_ptr = ma_ptr;
+
+ if (wizard && cheat_xtra)
+ {
+ msg_print("Attack re-selected.");
+ }
+ }
+ else
+ {
+ ma_ptr = old_ptr;
+ }
+ }
+ }
+
+ *k = damroll(ma_ptr->dd, ma_ptr->ds);
+
+ if (ma_ptr->effect & MA_KNEE)
+ {
+ if (r_ptr->flags1 & RF1_MALE)
+ {
+ if (!desc) msg_format("You hit %s in the groin with your knee!",
+ m_name);
+ sound(SOUND_PAIN);
+ special_effect = MA_KNEE;
+ }
+ else if (!desc) msg_format(ma_ptr->desc, m_name);
+
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_FULL_SLOW)
+ {
+ special_effect = MA_SLOW;
+ if (!desc) msg_format(ma_ptr->desc, m_name);
+
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_SLOW)
+ {
+ if (!
+ ((r_ptr->flags1 & RF1_NEVER_MOVE) ||
+ strchr("UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char)))
+ {
+ if (!desc) msg_format("You kick %s in the ankle.", m_name);
+ special_effect = MA_SLOW;
+ }
+ else if (!desc) msg_format(ma_ptr->desc, m_name);
+
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_STUN)
+ {
+ if (ma_ptr->power)
+ {
+ stun_effect = (ma_ptr->power / 2) + randint(ma_ptr->power / 2);
+ }
+
+ if (!desc) msg_format(ma_ptr->desc, m_name);
+ desc = TRUE;
+ }
+ if (ma_ptr->effect & MA_WOUND)
+ {
+ if (magik(ma_ptr->power))
+ {
+ *special |= SPEC_CUT;
+ }
+ if (!desc) msg_format(ma_ptr->desc, m_name);
+ desc = TRUE;
+ }
+
+ *k = critical_norm(plev * (randint(10)), ma_ptr->min_level, *k, -1, &done_crit);
+
+ if ((special_effect & MA_KNEE) && ((*k + p_ptr->to_d) < m_ptr->hp))
+ {
+ msg_format("%^s moans in agony!", m_name);
+ stun_effect = 7 + randint(13);
+ resist_stun /= 3;
+ }
+ if (((special_effect & MA_FULL_SLOW) || (special_effect & MA_SLOW)) &&
+ ((*k + p_ptr->to_d) < m_ptr->hp))
+ {
+ if (!(r_ptr->flags1 & RF1_UNIQUE) &&
+ (randint(plev) > m_ptr->level) && m_ptr->mspeed > 60)
+ {
+ msg_format("%^s starts limping slower.", m_name);
+ m_ptr->mspeed -= 10;
+ }
+ }
+
+ if (stun_effect && ((*k + p_ptr->to_d) < m_ptr->hp))
+ {
+ if (plev > randint(m_ptr->level + resist_stun + 10))
+ {
+ if (m_ptr->stunned)
+ msg_format("%^s is still stunned.", m_name);
+ else
+ msg_format("%^s is stunned.", m_name);
+
+ m_ptr->stunned += (stun_effect);
+ }
+ }
+}
+
+
+/*
+ * Apply nazgul effects
+ */
+void do_nazgul(int *k, int *num, int num_blow, int weap, monster_race *r_ptr,
+ object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ bool mundane;
+ bool allow_shatter = TRUE;
+
+ /* Extract mundane-ness of the current weapon */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* It should be Slay Evil, Slay Undead, or *Slay Undead* */
+ mundane = !(f1 & TR1_SLAY_EVIL) && !(f1 & TR1_SLAY_UNDEAD) &&
+ !(f5 & TR5_KILL_UNDEAD);
+
+ /* Some blades can resist shattering */
+ if (f5 & TR5_RES_MORGUL)
+ allow_shatter = FALSE;
+
+ /* Mega Hack -- Hitting Nazgul is REALY dangerous (ideas from Akhronath) */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ if ((!o_ptr->name2) && (!artifact_p(o_ptr)) && allow_shatter)
+ {
+ msg_print("Your weapon *DISINTEGRATES*!");
+ *k = 0;
+ inven_item_increase(INVEN_WIELD + weap, -1);
+ inven_item_optimize(INVEN_WIELD + weap);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ else if (o_ptr->name2)
+ {
+ if (mundane)
+ {
+ msg_print
+ ("The Ringwraith is IMPERVIOUS to the mundane weapon.");
+ *k = 0;
+ }
+
+ /* 25% chance of getting destroyed */
+ if (magik(25) && allow_shatter)
+ {
+ msg_print("Your weapon is destroyed!");
+ inven_item_increase(INVEN_WIELD + weap, -1);
+ inven_item_optimize(INVEN_WIELD + weap);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ }
+ else if (artifact_p(o_ptr))
+ {
+ if (mundane)
+ {
+ msg_print
+ ("The Ringwraith is IMPERVIOUS to the mundane weapon.");
+ *k = 0;
+ }
+
+ apply_disenchant(INVEN_WIELD + weap);
+
+ /* 1/1000 chance of getting destroyed */
+ if (!rand_int(1000) && allow_shatter)
+ {
+ msg_print("Your weapon is destroyed!");
+ inven_item_increase(INVEN_WIELD + weap, -1);
+ inven_item_optimize(INVEN_WIELD + weap);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ }
+
+ /* If any damage is done, then 25% chance of getting the Black Breath */
+ if (*k)
+ {
+ if (magik(25))
+ {
+ msg_print("Your foe calls upon your soul!");
+ msg_print
+ ("You feel the Black Breath slowly draining you of life...");
+ p_ptr->black_breath = TRUE;
+ }
+ }
+ }
+}
+
+
+/*
+ * Player attacks a (poor, defenseless) creature -RAK-
+ *
+ * If no "weapon" is available, then "punch" the monster one time.
+ */
+void py_attack(int y, int x, int max_blow)
+{
+ int num = 0, k, bonus, chance;
+
+ s32b special = 0;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ object_type *o_ptr;
+
+ char m_name[80];
+
+ bool fear = FALSE;
+
+ bool mdeath = FALSE;
+
+ bool backstab = FALSE;
+
+ bool vorpal_cut = FALSE;
+
+ int chaos_effect = 0;
+
+ bool stab_fleeing = FALSE;
+
+ bool do_quake = FALSE;
+
+ bool done_crit = FALSE;
+
+ bool drain_msg = TRUE;
+
+ int drain_result = 0, drain_heal = 0;
+
+ int drain_left = MAX_VAMPIRIC_DRAIN;
+
+ /* A massive hack -- life-draining weapons */
+ u32b f1, f2, f3, f4, f5, esp;
+
+ bool no_extra = FALSE;
+
+ int weap;
+
+ /* Disturb the player */
+ disturb(0, 0);
+
+ if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW)
+ {
+ msg_print("You cannot attack in this form!");
+ return;
+ }
+
+ if (get_skill(SKILL_BACKSTAB))
+ {
+ if ((m_ptr->csleep) && (m_ptr->ml))
+ {
+ /* Can't backstab creatures that we can't see, right? */
+ backstab = TRUE;
+ }
+ else if ((m_ptr->monfear) && (m_ptr->ml))
+ {
+ stab_fleeing = TRUE;
+ }
+ }
+
+ /* Disturb the monster */
+ m_ptr->csleep = 0;
+
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dont even bother */
+ if (r_ptr->flags7 & RF7_IM_MELEE)
+ {
+ msg_format("%^s is immune to melee attacks.");
+ return;
+ }
+
+ /* Auto-Recall if possible and visible */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Track a new monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+
+ /* Stop if friendly */
+ if ((is_friend(m_ptr) >= 0) &&
+ !(p_ptr->stun || p_ptr->confused || p_ptr->image ||
+ !(m_ptr->ml)))
+ {
+ if (!(p_ptr->inventory[INVEN_WIELD].art_name))
+ {
+ msg_format("You stop to avoid hitting %s.", m_name);
+ return;
+ }
+
+ if (!
+ (streq
+ (quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'")))
+ {
+ msg_format("You stop to avoid hitting %s.", m_name);
+ return;
+ }
+
+ msg_format("Your black blade greedily attacks %s!", m_name);
+ }
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ /* Handle player fear */
+ if (p_ptr->afraid)
+ {
+ /* Message */
+ if (m_ptr->ml)
+ msg_format("You are too afraid to attack %s!", m_name);
+ else
+ msg_format("There is something scary in your way!");
+
+ /* Done */
+ return;
+ }
+
+ /* Monsters can use barehanded combat, but not weapon combat */
+ if ((p_ptr->body_monster) &&
+ (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]) &&
+ !(p_ptr->melee_style == SKILL_HAND))
+ {
+ incarnate_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x);
+ }
+ /* Otherwise use your weapon(s) */
+ else
+ {
+ int weapons;
+ if (p_ptr->melee_style == SKILL_MASTERY)
+ weapons = r_info[p_ptr->body_monster].body_parts[BODY_WEAPON];
+ else /* SKILL_HAND */
+ weapons = 1;
+
+ /* Attack with ALL the weapons !!!!! -- ooh that's gonna hurt YOU */
+ for (weap = 0; weap < weapons; ++weap)
+ {
+ /* Monster is already dead ? oh :( */
+ if (mdeath) break;
+
+ /* Reset the blows counter */
+ num = 0;
+
+ /* Access the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + weap];
+
+ /* Calculate the "attack quality" */
+ bonus = p_ptr->to_h + p_ptr->to_h_melee + o_ptr->to_h;
+ chance = p_ptr->skill_thn + (bonus * BTH_PLUS_ADJ);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!(f4 & TR4_NEVER_BLOW))
+ {
+ int num_blow = p_ptr->num_blow;
+
+ /* Restrict to max_blow(if max_blow >= 0) */
+ if ((max_blow >= 0) &&
+ (num_blow > max_blow)) num_blow = max_blow;
+
+ /* Attack once for each legal blow */
+ while (num++ < num_blow)
+ {
+ /* Test for hit */
+ if (test_hit_norm(chance, m_ptr->ac, m_ptr->ml))
+ {
+ /* Sound */
+ sound(SOUND_HIT);
+
+ /* Hack -- bare hands do one damage */
+ k = 1;
+
+ /* Select a chaotic effect (50% chance) */
+ if ((f1 & TR1_CHAOTIC) && (rand_int(2) == 0))
+ {
+ if (randint(5) < 3)
+ {
+ /* Vampiric (20%) */
+ chaos_effect = 1;
+ }
+ else if (rand_int(250) == 0)
+ {
+ /* Quake (0.12%) */
+ chaos_effect = 2;
+ }
+ else if (rand_int(10))
+ {
+ /* Confusion (26.892%) */
+ chaos_effect = 3;
+ }
+ else if (rand_int(2) == 0)
+ {
+ /* Teleport away (1.494%) */
+ chaos_effect = 4;
+ }
+ else
+ {
+ /* Polymorph (1.494%) */
+ chaos_effect = 5;
+ }
+ }
+
+ /* Vampiric drain */
+ if ((f1 & TR1_VAMPIRIC) || (chaos_effect == 1))
+ {
+ if (!
+ ((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_NONLIVING)))
+ drain_result = m_ptr->hp;
+ else
+ drain_result = 0;
+ }
+
+ if (f1 & TR1_VORPAL && (randint(6) == 1))
+ vorpal_cut = TRUE;
+ else
+ vorpal_cut = FALSE;
+
+ /* Should we attack with hands or not ? */
+ if (p_ptr->melee_style != SKILL_MASTERY)
+ {
+ py_attack_hand(&k, m_ptr, &special);
+ }
+ /* Handle normal weapon */
+ else if (o_ptr->k_idx)
+ {
+ k = damroll(o_ptr->dd, o_ptr->ds);
+ k = tot_dam_aux(o_ptr, k, m_ptr, &special);
+
+ if (backstab)
+ {
+ k += (k *
+ get_skill_scale(SKILL_BACKSTAB,
+ 100)) / 100;
+ }
+ else if (stab_fleeing)
+ {
+ k += (k * get_skill_scale(SKILL_BACKSTAB, 70)) /
+ 100;
+ }
+
+ if ((p_ptr->impact && ((k > 50) || randint(7) == 1))
+ || (chaos_effect == 2))
+ {
+ do_quake = TRUE;
+ }
+
+ k = critical_norm(o_ptr->weight, o_ptr->to_h, k, o_ptr->tval, &done_crit);
+
+ /* Stunning blow */
+ if (magik(get_skill(SKILL_STUN)) && (o_ptr->tval == TV_HAFTED) && (o_ptr->weight > 50) && done_crit)
+ {
+ if (!(r_ptr->flags4 & (RF4_BR_SOUN)) && !(r_ptr->flags4 & (RF4_BR_WALL)) && k)
+ {
+ int tmp;
+
+ /* Get stunned */
+ if (m_ptr->stunned)
+ {
+ msg_format("%^s is more dazed.", m_name);
+ tmp = m_ptr->stunned + get_skill_scale(SKILL_STUN, 30) + 10;
+ }
+ else
+ {
+ msg_format("%^s is dazed.", m_name);
+ tmp = get_skill_scale(SKILL_STUN, 60) + 20;
+ }
+
+ /* Apply stun */
+ m_ptr->stunned = (tmp < 200) ? tmp : 200;
+ }
+ }
+
+ if (vorpal_cut)
+ {
+ int step_k = k;
+
+ msg_format("Your weapon cuts deep into %s!",
+ m_name);
+ do
+ {
+ k += step_k;
+ }
+ while (randint(4) == 1);
+ }
+
+ PRAY_GOD(GOD_TULKAS)
+ {
+ if (magik(wisdom_scale(130) - m_ptr->level) && (p_ptr->grace > 1000))
+ {
+ msg_print("You feel the hand of Tulkas helping your blow.");
+ k += (o_ptr->to_d + p_ptr->to_d_melee) * 2;
+ }
+ else k += o_ptr->to_d + p_ptr->to_d_melee;
+ }
+ else k += o_ptr->to_d;
+
+ /* Project some more nasty stuff? */
+ if (p_ptr->tim_project)
+ {
+ project(0, p_ptr->tim_project_rad, y, x, p_ptr->tim_project_dam, p_ptr->tim_project_gf, p_ptr->tim_project_flag | PROJECT_JUMP);
+ if (!c_ptr->m_idx)
+ {
+ mdeath = TRUE;
+ break;
+ }
+ }
+
+ do_nazgul(&k, &num, num_blow, weap, r_ptr, o_ptr);
+
+ }
+
+ /* Melkor can cast curse for you*/
+ PRAY_GOD(GOD_MELKOR)
+ {
+ int lv = exec_lua("return get_level(MELKOR_CURSE, 100)");
+
+ if (lv >= 10)
+ {
+ int chance = (wisdom_scale(30) * lv) / ((m_ptr->level < 1) ? 1 : m_ptr->level);
+
+ if (chance < 1) chance = 1;
+ if ((p_ptr->grace > 5000) && magik(chance))
+ {
+ exec_lua(format("do_melkor_curse(%d)", c_ptr->m_idx));
+ }
+ }
+ }
+
+ /* May it clone the monster ? */
+ if ((f4 & TR4_CLONE) && magik(30))
+ {
+ msg_format("Oh no! Your weapon clones %^s!",
+ m_name);
+ multiply_monster(c_ptr->m_idx, FALSE, TRUE);
+ }
+
+ /* Apply the player damage bonuses */
+ k += p_ptr->to_d + p_ptr->to_d_melee;
+
+ /* No negative damage */
+ if (k < 0) k = 0;
+
+ /* Message */
+ if (!(backstab || stab_fleeing))
+ {
+ /* These monsters never have flavoured combat msgs */
+ if (strchr("vwjmelX,.*", r_ptr->d_char))
+ {
+ msg_format("You hit %s.", m_name);
+ }
+
+ /* Print flavoured messages if requested */
+ else
+ {
+ char buff[255];
+
+ flavored_attack((100 * k) / m_ptr->maxhp, buff);
+ msg_format(buff, m_name);
+ }
+ }
+ else if (backstab)
+ {
+ char buf[80];
+
+ monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego);
+
+ backstab = FALSE;
+
+ msg_format
+ ("You cruelly stab the helpless, sleeping %s!",
+ buf);
+ }
+ else
+ {
+ char buf[80];
+
+ monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego);
+
+ msg_format("You backstab the fleeing %s!", buf);
+ }
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.", k,
+ m_ptr->hp);
+ }
+
+ if (special) attack_special(m_ptr, special, k);
+
+ /* Damage, check for fear and death */
+ if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL))
+ {
+ /* Hack -- High-level warriors can spread their attacks out
+ * among weaker foes.
+ */
+ if ((has_ability(AB_SPREAD_BLOWS)) && (num < num_blow) &&
+ (energy_use))
+ {
+ energy_use = energy_use * num / num_blow;
+ }
+ mdeath = TRUE;
+ break;
+ }
+
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+
+ touch_zap_player(m_ptr);
+
+ /* Are we draining it? A little note: If the monster is
+ dead, the drain does not work... */
+
+ if (drain_result)
+ {
+ drain_result -= m_ptr->hp; /* Calculate the difference */
+
+ if (drain_result > 0) /* Did we really hurt it? */
+ {
+ drain_heal = damroll(4, (drain_result / 6));
+
+ if (cheat_xtra)
+ {
+ msg_format("Draining left: %d", drain_left);
+ }
+
+ if (drain_left)
+ {
+ if (drain_heal < drain_left)
+ {
+ drain_left -= drain_heal;
+ }
+ else
+ {
+ drain_heal = drain_left;
+ drain_left = 0;
+ }
+
+ if (drain_msg)
+ {
+ msg_format
+ ("Your weapon drains life from %s!",
+ m_name);
+ drain_msg = FALSE;
+ }
+
+ hp_player(drain_heal);
+ /* We get to keep some of it! */
+ }
+ }
+ }
+
+ /* Confusion attack */
+ if ((p_ptr->confusing) || (chaos_effect == 3))
+ {
+ /* Cancel glowing hands */
+ if (p_ptr->confusing)
+ {
+ p_ptr->confusing = FALSE;
+ msg_print("Your hands stop glowing.");
+ }
+
+ /* Confuse the monster */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ msg_format("%^s is unaffected.", m_name);
+ }
+ else if (rand_int(100) < m_ptr->level)
+ {
+ msg_format("%^s is unaffected.", m_name);
+ }
+ else
+ {
+ msg_format("%^s appears confused.", m_name);
+ m_ptr->confused +=
+ 10 + rand_int(get_skill(SKILL_COMBAT)) / 5;
+ }
+ }
+
+ else if (chaos_effect == 4)
+ {
+ msg_format("%^s disappears!", m_name);
+ teleport_away(c_ptr->m_idx, 50);
+ num = num_blow + 1; /* Can't hit it anymore! */
+ no_extra = TRUE;
+ }
+
+ else if ((chaos_effect == 5) && cave_floor_bold(y, x) &&
+ (randint(90) > m_ptr->level))
+ {
+ if (!((r_ptr->flags1 & RF1_UNIQUE) ||
+ (r_ptr->flags4 & RF4_BR_CHAO) ||
+ (m_ptr->mflag & MFLAG_QUEST)))
+ {
+ /* Handle polymorph */
+ if (do_poly_monster(y, x))
+ {
+ /* Polymorph succeeded */
+ msg_format("%^s changes!", m_name);
+
+ /* Hack -- Get new monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Oops, we need a different name... */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Hack -- Get new race */
+ r_ptr = race_inf(m_ptr);
+
+ fear = FALSE;
+ }
+ else
+ {
+ msg_format("%^s resists.", m_name);
+ }
+ }
+ else
+ {
+ msg_format("%^s is unaffected.", m_name);
+ }
+ }
+ }
+
+ /* Player misses */
+ else
+ {
+ /* Sound */
+ sound(SOUND_MISS);
+
+ backstab = FALSE; /* Clumsy! */
+
+ /* Message */
+ msg_format("You miss %s.", m_name);
+ }
+ }
+ }
+ else
+ {
+ msg_print("You can't attack with that weapon.");
+ }
+ }
+ }
+
+ /* Carried monster can attack too */
+ if ((!mdeath) && m_list[c_ptr->m_idx].hp)
+ carried_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x);
+
+ /* Hack -- delay fear messages */
+ if (fear && m_ptr->ml)
+ {
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+
+ /* Mega-Hack -- apply earthquake brand */
+ if (do_quake)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ }
+}
+
+
+
+static bool pattern_tile(int y, int x)
+{
+ return ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) &&
+ (cave[y][x].feat >= FEAT_PATTERN_START));
+}
+
+
+static bool pattern_seq(int c_y, int c_x, int n_y, int n_x)
+{
+ if (!(pattern_tile(c_y, c_x)) && !(pattern_tile(n_y, n_x)))
+ return TRUE;
+
+ if (cave[n_y][n_x].feat == FEAT_PATTERN_START)
+ {
+ if ((!(pattern_tile(c_y, c_x))) &&
+ !(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ if (get_check
+ ("If you start walking the Straight Road, you must walk the whole way. Ok? "))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+ }
+ else if ((cave[n_y][n_x].feat == FEAT_PATTERN_OLD) ||
+ (cave[n_y][n_x].feat == FEAT_PATTERN_END) ||
+ (cave[n_y][n_x].feat == FEAT_PATTERN_XTRA2))
+ {
+ if (pattern_tile(c_y, c_x))
+ {
+ return TRUE;
+ }
+ else
+ {
+ msg_print
+ ("You must start walking the Straight Road from the startpoint.");
+ return FALSE;
+ }
+ }
+ else if ((cave[n_y][n_x].feat == FEAT_PATTERN_XTRA1) ||
+ (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA1))
+ {
+ return TRUE;
+ }
+ else if (cave[c_y][c_x].feat == FEAT_PATTERN_START)
+ {
+ if (pattern_tile(n_y, n_x))
+ return TRUE;
+ else
+ {
+ msg_print("You must walk the Straight Road in correct order.");
+ return FALSE;
+ }
+ }
+ else if ((cave[c_y][c_x].feat == FEAT_PATTERN_OLD) ||
+ (cave[c_y][c_x].feat == FEAT_PATTERN_END) ||
+ (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA2))
+ {
+ if (!pattern_tile(n_y, n_x))
+ {
+ msg_print("You may not step off from the Straight Road.");
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (!pattern_tile(c_y, c_x))
+ {
+ msg_print
+ ("You must start walking the Straight Road from the startpoint.");
+ return FALSE;
+ }
+ else
+ {
+ byte ok_move = FEAT_PATTERN_START;
+ switch (cave[c_y][c_x].feat)
+ {
+ case FEAT_PATTERN_1:
+ ok_move = FEAT_PATTERN_2;
+ break;
+ case FEAT_PATTERN_2:
+ ok_move = FEAT_PATTERN_3;
+ break;
+ case FEAT_PATTERN_3:
+ ok_move = FEAT_PATTERN_4;
+ break;
+ case FEAT_PATTERN_4:
+ ok_move = FEAT_PATTERN_1;
+ break;
+ default:
+ if (wizard)
+ msg_format("Funny Straight Road walking, %d.",
+ cave[c_y][c_x]);
+ return TRUE; /* Goof-up */
+ }
+
+ if ((cave[n_y][n_x].feat == ok_move) ||
+ (cave[n_y][n_x].feat == cave[c_y][c_x].feat))
+ return TRUE;
+ else
+ {
+ if (!pattern_tile(n_y, n_x))
+ msg_print("You may not step off from the Straight Road.");
+ else
+ msg_print
+ ("You must walk the Straight Road in correct order.");
+
+ return FALSE;
+ }
+ }
+ }
+}
+
+
+
+bool player_can_enter(byte feature)
+{
+ bool pass_wall;
+
+ bool only_wall = FALSE;
+
+
+ /* Player can not walk through "walls" unless in Shadow Form */
+ if (p_ptr->wraith_form || (PRACE_FLAG(PR1_SEMI_WRAITH)))
+ pass_wall = TRUE;
+ else
+ pass_wall = FALSE;
+
+ /* Wall mimicry force the player to stay in walls */
+ if (p_ptr->mimic_extra & CLASS_WALL)
+ {
+ only_wall = TRUE;
+ }
+
+ /* Don't let the player kill himself with one keystroke */
+ if (p_ptr->wild_mode)
+ {
+ if (feature == FEAT_DEEP_WATER)
+ {
+ int wt = weight_limit() / 2;
+
+ if ((calc_total_weight() >= wt) && !(p_ptr->ffall))
+ return (FALSE);
+ }
+ else if (feature == FEAT_SHAL_LAVA ||
+ feature == FEAT_DEEP_LAVA)
+ {
+ if (!(p_ptr->resist_fire ||
+ p_ptr->immune_fire ||
+ p_ptr->oppose_fire ||
+ p_ptr->ffall))
+ return (FALSE);
+ }
+ }
+
+ if (feature == FEAT_TREES)
+ {
+ if (p_ptr->fly ||
+ pass_wall ||
+ (has_ability(AB_TREE_WALK)) ||
+ (p_ptr->mimic_form == resolve_mimic_name("Ent")) ||
+ ((p_ptr->grace >= 9000) && (p_ptr->praying) && (p_ptr->pgod == GOD_YAVANNA)))
+ return (TRUE);
+ }
+
+ if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB))
+ return (TRUE);
+ if ((p_ptr->fly) &&
+ ((f_info[feature].flags1 & FF1_CAN_FLY) ||
+ (f_info[feature].flags1 & FF1_CAN_LEVITATE)))
+ return (TRUE);
+ else if (only_wall && (f_info[feature].flags1 & FF1_FLOOR))
+ return (FALSE);
+ else if ((p_ptr->ffall) &&
+ (f_info[feature].flags1 & FF1_CAN_LEVITATE))
+ return (TRUE);
+ else if ((pass_wall || only_wall) &&
+ (f_info[feature].flags1 & FF1_CAN_PASS))
+ return (TRUE);
+ else if (f_info[feature].flags1 & FF1_NO_WALK)
+ return (FALSE);
+ else if ((f_info[feature].flags1 & FF1_WEB) &&
+ ((!(r_info[p_ptr->body_monster].flags7 & RF7_SPIDER)) && (p_ptr->mimic_form != resolve_mimic_name("Spider"))))
+ return (FALSE);
+
+ return (TRUE);
+}
+
+/*
+ * Move player in the given direction, with the given "pickup" flag.
+ *
+ * This routine should (probably) always induce energy expenditure.
+ *
+ * Note that moving will *always* take a turn, and will *always* hit
+ * any monster which might be in the destination grid. Previously,
+ * moving into walls was "free" and did NOT hit invisible monsters.
+ */
+void move_player_aux(int dir, int do_pickup, int run, bool disarm)
+{
+ int y, x, tmp;
+
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ monster_type *m_ptr;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster], *mr_ptr;
+
+ char m_name[80];
+
+ bool stormbringer = FALSE;
+
+ bool old_dtrap, new_dtrap;
+
+ bool oktomove = TRUE;
+
+
+ /* Hack - random movement */
+ if (p_ptr->disembodied)
+ tmp = dir;
+ else if ((r_ptr->flags1 & RF1_RAND_25) && (r_ptr->flags1 & RF1_RAND_50))
+ {
+ if (randint(100) < 75)
+ tmp = randint(9);
+ else
+ tmp = dir;
+ }
+ else if (r_ptr->flags1 & RF1_RAND_50)
+ {
+ if (randint(100) < 50)
+ tmp = randint(9);
+ else
+ tmp = dir;
+ }
+ else if (r_ptr->flags1 & RF1_RAND_25)
+ {
+ if (randint(100) < 25)
+ tmp = randint(9);
+ else
+ tmp = dir;
+ }
+ else
+ {
+ tmp = dir;
+ }
+
+ if ((c_ptr->feat == FEAT_ICE) && (!p_ptr->ffall && !p_ptr->fly))
+ {
+ if (magik(70 - p_ptr->lev))
+ {
+ tmp = randint(9);
+ msg_print("You slip on the icy floor.");
+ }
+ else
+ tmp = dir;
+ }
+
+ /* Find the result of moving */
+ y = p_ptr->py + ddy[tmp];
+ x = p_ptr->px + ddx[tmp];
+
+ /* Examine the destination */
+ c_ptr = &cave[y][x];
+
+ /* Change oldpx and oldpy to place the player well when going back to big mode */
+ if (p_ptr->wild_mode)
+ {
+ if (ddy[tmp] > 0) p_ptr->oldpy = 1;
+ if (ddy[tmp] < 0) p_ptr->oldpy = MAX_HGT - 2;
+ if (ddy[tmp] == 0) p_ptr->oldpy = MAX_HGT / 2;
+ if (ddx[tmp] > 0) p_ptr->oldpx = 1;
+ if (ddx[tmp] < 0) p_ptr->oldpx = MAX_WID - 2;
+ if (ddx[tmp] == 0) p_ptr->oldpx = MAX_WID / 2;
+ }
+
+ /* Exit the area */
+ if (!dun_level && !p_ptr->wild_mode && !is_quest(dun_level) &&
+ ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1)))
+ {
+ /* Can the player enter the grid? */
+ if (player_can_enter(c_ptr->mimic))
+ {
+ /* Hack: move to new area */
+ if ((y == 0) && (x == 0))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == 0) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == 0))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == 0)
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == cur_hgt - 1)
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == 0)
+ {
+ p_ptr->wilderness_x--;
+ p_ptr->oldpx = cur_wid - 2;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == cur_wid - 1)
+ {
+ p_ptr->wilderness_x++;
+ p_ptr->oldpx = 1;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+ }
+
+ /* Some hooks */
+ if (process_hooks(HOOK_MOVE, "(d,d)", y, x)) return;
+
+ /* Get the monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+ mr_ptr = race_inf(m_ptr);
+
+ if (p_ptr->inventory[INVEN_WIELD].art_name)
+ {
+ if (streq(quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'"))
+ stormbringer = TRUE;
+ }
+
+ /* Hack -- attack monsters */
+ if (c_ptr->m_idx && (m_ptr->ml || player_can_enter(c_ptr->feat)))
+ {
+
+ /* Attack -- only if we can see it OR it is not in a wall */
+ if ((is_friend(m_ptr) > 0) &&
+ !(p_ptr->confused || p_ptr->image || !(m_ptr->ml) || p_ptr->stun) &&
+ (pattern_seq(p_ptr->py, p_ptr->px, y, x)) &&
+ ((player_can_enter(cave[y][x].feat))))
+ {
+ m_ptr->csleep = 0;
+
+ /* Extract monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Auto-Recall if possible and visible */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Track a new monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+
+ /* displace? */
+ if (stormbringer && (randint(1000) > 666))
+ {
+ py_attack(y, x, -1);
+ }
+ else if (cave_floor_bold(p_ptr->py, p_ptr->px) ||
+ (mr_ptr->flags2 & RF2_PASS_WALL))
+ {
+ msg_format("You push past %s.", m_name);
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+ c_ptr->m_idx = 0;
+ update_mon(cave[p_ptr->py][p_ptr->px].m_idx, TRUE);
+ }
+ else
+ {
+ msg_format("%^s is in your way!", m_name);
+ energy_use = 0;
+ oktomove = FALSE;
+ }
+
+ /* now continue on to 'movement' */
+ }
+ else
+ {
+ py_attack(y, x, -1);
+ oktomove = FALSE;
+ }
+ }
+
+ else if ((c_ptr->feat == FEAT_DARK_PIT) && !p_ptr->ffall)
+ {
+ msg_print("You can't cross the chasm.");
+ running = 0;
+ oktomove = FALSE;
+ }
+
+#ifdef ALLOW_EASY_DISARM /* TNB */
+
+ /* Disarm a visible trap */
+ else if (easy_disarm && disarm && (c_ptr->info & (CAVE_TRDT)))
+ {
+ (void)do_cmd_disarm_aux(y, x, tmp, do_pickup);
+ return;
+ }
+
+ /* Don't step on known traps. */
+ else if (disarm && (c_ptr->info & (CAVE_TRDT)) && !(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ msg_print("You stop to avoid triggering the trap.");
+ energy_use = 0;
+ oktomove = FALSE;
+ }
+
+#endif /* ALLOW_EASY_DISARM -- TNB */
+
+ /* Player can't enter ? soo bad for him/her ... */
+ else if (!player_can_enter(c_ptr->feat))
+ {
+ oktomove = FALSE;
+
+ /* Disturb the player */
+ disturb(0, 0);
+
+ if (p_ptr->prob_travel)
+ {
+ if (passwall(tmp, TRUE)) return;
+ }
+
+ /* Notice things in the dark */
+ if (!(c_ptr->info & (CAVE_MARK)) && !(c_ptr->info & (CAVE_SEEN)))
+ {
+ /* Rubble */
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ msg_print("You feel some rubble blocking your way.");
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+
+ /* Closed door */
+ else if (c_ptr->feat < FEAT_SECRET)
+ {
+ msg_print("You feel a closed door blocking your way.");
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+
+ /* Wall (or secret door) */
+ else
+ {
+ int feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else
+ feat = f_info[c_ptr->feat].mimic;
+
+ msg_format("You feel %s.", f_text + f_info[feat].block);
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+ }
+
+ /* Notice things */
+ else
+ {
+ /* Rubble */
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ if (!easy_tunnel)
+ {
+ msg_print("There is rubble blocking your way.");
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ /*
+ * Well, it makes sense that you lose time bumping into
+ * a wall _if_ you are confused, stunned or blind; but
+ * typing mistakes should not cost you a turn...
+ */
+ }
+ else
+ {
+ do_cmd_tunnel_aux(y, x, dir);
+ return;
+ }
+ }
+ /* Closed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+#ifdef ALLOW_EASY_OPEN
+
+ if (easy_open)
+ {
+ if (easy_open_door(y, x)) return;
+ }
+ else
+#endif /* ALLOW_EASY_OPEN */
+
+ {
+ msg_print("There is a closed door blocking your way.");
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ }
+ }
+
+ /* Wall (or secret door) */
+ else
+ {
+ if (!easy_tunnel)
+ {
+ int feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else
+ feat = f_info[c_ptr->feat].mimic;
+
+ msg_format("There is %s.", f_text + f_info[feat].block);
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ }
+ else
+ {
+ do_cmd_tunnel_aux(y, x, dir);
+ return;
+ }
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_HITWALL);
+ }
+
+ /* Normal movement */
+ if (!pattern_seq(p_ptr->py, p_ptr->px, y, x))
+ {
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ energy_use = 0;
+ }
+
+ disturb(0, 0); /* To avoid a loop with running */
+
+ oktomove = FALSE;
+ }
+
+
+ /*
+ * Check trap detection status -- retrieve them here
+ * because they are used by the movement code as well
+ */
+ old_dtrap = ((cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT) != 0);
+ new_dtrap = ((cave[y][x].info & CAVE_DETECT) != 0);
+
+ /* Normal movement */
+ if (oktomove && running && disturb_detect)
+ {
+ /*
+ * Disturb the player when about to leave the trap detected
+ * area
+ */
+ if (old_dtrap && !new_dtrap)
+ {
+ /* Disturb player */
+ disturb(0, 0);
+
+ /* but don't take a turn */
+ energy_use = 0;
+
+ /* Tell player why */
+ cmsg_print(TERM_VIOLET, "You are about to leave a trap detected zone.");
+ /* Flush */
+ /* msg_print(NULL); */
+
+ oktomove = FALSE;
+ }
+ }
+
+ /* Normal movement */
+ if (oktomove)
+ {
+ int oy, ox;
+ int feat;
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ /* Save old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ if (cave[p_ptr->py][p_ptr->px].mimic) feat = cave[p_ptr->py][p_ptr->px].mimic;
+ else
+ feat = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Some hooks */
+ if (process_hooks(HOOK_MOVED, "(d,d)", oy, ox)) return;
+
+ /* Redraw new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Redraw old spot */
+ lite_spot(oy, ox);
+
+ /* Sound */
+ /* sound(SOUND_WALK); */
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Check detection status */
+ if (old_dtrap && !new_dtrap)
+ {
+ cmsg_print(TERM_VIOLET, "You leave a trap detected zone.");
+ if (running) msg_print(NULL);
+ p_ptr->redraw |= (PR_DTRAP);
+ }
+ else if (!old_dtrap && new_dtrap)
+ {
+ cmsg_print(TERM_L_BLUE, "You enter a trap detected zone.");
+ if (running) msg_print(NULL);
+ p_ptr->redraw |= (PR_DTRAP);
+ }
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Window stuff */
+ if (!run) p_ptr->window |= (PW_OVERHEAD);
+
+ /* Some feature descs */
+ if (f_info[cave[p_ptr->py][p_ptr->px].feat].text > 1)
+ {
+ /* Mega-hack for dungeon branches */
+ if ((feat == FEAT_MORE) && c_ptr->special)
+ {
+ msg_format("There is %s", d_text + d_info[c_ptr->special].text);
+ }
+ else
+ {
+ msg_print(f_text + f_info[feat].text);
+ }
+
+ /* Flush message while running */
+ if (running) msg_print(NULL);
+ }
+
+ /* Spontaneous Searching */
+ if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos)))
+ {
+ search();
+ }
+
+ /* Continuous Searching */
+ if (p_ptr->searching)
+ {
+ search();
+ }
+
+ /* Handle "objects" */
+ carry(do_pickup);
+
+ /* Handle "store doors" */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Enter store */
+ command_new = '_';
+ }
+
+#if 0 /* These are noxious -- pelpel */
+
+ /* Handle quest areas -KMW- */
+ else if (cave[y][x].feat == FEAT_QUEST_ENTER)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Enter quest level */
+ command_new = '[';
+ }
+
+ else if (cave[y][x].feat == FEAT_QUEST_EXIT)
+ {
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = cave[y][x].special;
+ dun_level = 0;
+ p_ptr->oldpx = 0;
+ p_ptr->oldpy = 0;
+ p_ptr->leaving = TRUE;
+ }
+
+#endif /* 0 */
+
+ else if (cave[y][x].feat >= FEAT_ALTAR_HEAD &&
+ cave[y][x].feat <= FEAT_ALTAR_TAIL)
+ {
+ cptr name = f_name + f_info[cave[y][x].feat].name;
+ cptr pref = (is_a_vowel(name[0])) ? "an" : "a";
+
+ msg_format("You see %s %s.", pref, name);
+
+ /* Flush message while running */
+ if (running) msg_print(NULL);
+ }
+
+ /* Discover invisible traps */
+ else if ((c_ptr->t_idx != 0) &&
+ !(f_info[cave[y][x].feat].flags1 & FF1_DOOR))
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ if (!(c_ptr->info & (CAVE_TRDT)))
+ {
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Pick a trap */
+ pick_trap(p_ptr->py, p_ptr->px);
+ }
+
+ /* Hit the trap */
+ hit_trap();
+ }
+
+ /* Execute the inscription */
+ else if (c_ptr->inscription)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ msg_format("There is an inscription here: %s",
+ inscription_info[c_ptr->inscription].text);
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK)
+ {
+ execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px);
+ }
+ }
+ }
+
+ /* Update wilderness knowledge */
+ if (p_ptr->wild_mode)
+ {
+ if (wizard) msg_format("y:%d, x:%d", p_ptr->py, p_ptr->px);
+
+ /* Update the known wilderness */
+ reveal_wilderness_around_player(p_ptr->py, p_ptr->px, 0, WILDERNESS_SEE_RADIUS);
+
+ /* Walking the wild isnt meaningfull */
+ p_ptr->did_nothing = TRUE;
+ }
+}
+
+void move_player(int dir, int do_pickup, bool disarm)
+{
+ move_player_aux(dir, do_pickup, 0, disarm);
+}
+
+
+/*
+ * Hack -- Grid-based version of see_obstacle
+ */
+static int see_obstacle_grid(cave_type *c_ptr)
+{
+ /*
+ * Hack -- Avoid hitting detected traps, because we cannot rely on
+ * the CAVE_MARK check below, and traps can be set to nearly
+ * everything the player can move on to XXX XXX XXX
+ */
+ if (c_ptr->info & (CAVE_TRDT)) return (TRUE);
+
+
+ /* Hack -- Handle special cases XXX XXX */
+ switch (c_ptr->feat)
+ {
+ /* Require levitation */
+ case FEAT_DARK_PIT:
+ case FEAT_DEEP_WATER:
+ case FEAT_ICE:
+ {
+ if (p_ptr->ffall || p_ptr->fly) return (FALSE);
+ }
+
+ /* Require immunity */
+ case FEAT_DEEP_LAVA:
+ case FEAT_SHAL_LAVA:
+ {
+ if (p_ptr->invuln || p_ptr->immune_fire) return (FALSE);
+ }
+ }
+
+
+ /* "Safe" floor grids aren't obstacles */
+ if (f_info[c_ptr->feat].flags1 & FF1_CAN_RUN) return (FALSE);
+
+ /* Must be known to the player */
+ if (!(c_ptr->info & (CAVE_MARK))) return (FALSE);
+
+ /* Default */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Check for a "known wall" or "dangerous" feature (see below)
+ */
+static int see_obstacle(int dir, int y, int x)
+{
+ /* Get the new location */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Illegal grids are not known walls */
+ if (!in_bounds2(y, x)) return (FALSE);
+
+ /* Analyse the grid */
+ return (see_obstacle_grid(&cave[y][x]));
+}
+
+
+/*
+ * Hack -- Check for an "unknown corner" (see below)
+ */
+static int see_nothing(int dir, int y, int x)
+{
+ /* Get the new location */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Illegal grids are unknown */
+ if (!in_bounds2(y, x)) return (TRUE);
+
+ /* Memorized grids are always known */
+ if (cave[y][x].info & (CAVE_MARK)) return (FALSE);
+
+ /* Non-floor grids are unknown */
+ if (!cave_floor_bold(y, x)) return (TRUE);
+
+ /* Viewable door/wall grids are known */
+ if (player_can_see_bold(y, x)) return (FALSE);
+
+ /* Default */
+ return (TRUE);
+}
+
+
+
+
+
+/*
+ * The running algorithm: -CJS-
+ *
+ * In the diagrams below, the player has just arrived in the
+ * grid marked as '@', and he has just come from a grid marked
+ * as 'o', and he is about to enter the grid marked as 'x'.
+ *
+ * Of course, if the "requested" move was impossible, then you
+ * will of course be blocked, and will stop.
+ *
+ * Overview: You keep moving until something interesting happens.
+ * If you are in an enclosed space, you follow corners. This is
+ * the usual corridor scheme. If you are in an open space, you go
+ * straight, but stop before entering enclosed space. This is
+ * analogous to reaching doorways. If you have enclosed space on
+ * one side only (that is, running along side a wall) stop if
+ * your wall opens out, or your open space closes in. Either case
+ * corresponds to a doorway.
+ *
+ * What happens depends on what you can really SEE. (i.e. if you
+ * have no light, then running along a dark corridor is JUST like
+ * running in a dark room.) The algorithm works equally well in
+ * corridors, rooms, mine tailings, earthquake rubble, etc, etc.
+ *
+ * These conditions are kept in static memory:
+ * find_openarea You are in the open on at least one
+ * side.
+ * find_breakleft You have a wall on the left, and will
+ * stop if it opens
+ * find_breakright You have a wall on the right, and will
+ * stop if it opens
+ *
+ * To initialize these conditions, we examine the grids adjacent
+ * to the grid marked 'x', two on each side (marked 'L' and 'R').
+ * If either one of the two grids on a given side is seen to be
+ * closed, then that side is considered to be closed. If both
+ * sides are closed, then it is an enclosed (corridor) run.
+ *
+ * LL L
+ * @x LxR
+ * RR @R
+ *
+ * Looking at more than just the immediate squares is
+ * significant. Consider the following case. A run along the
+ * corridor will stop just before entering the center point,
+ * because a choice is clearly established. Running in any of
+ * three available directions will be defined as a corridor run.
+ * Note that a minor hack is inserted to make the angled corridor
+ * entry (with one side blocked near and the other side blocked
+ * further away from the runner) work correctly. The runner moves
+ * diagonally, but then saves the previous direction as being
+ * straight into the gap. Otherwise, the tail end of the other
+ * entry would be perceived as an alternative on the next move.
+ *
+ * #.#
+ * ##.##
+ * .@x..
+ * ##.##
+ * #.#
+ *
+ * Likewise, a run along a wall, and then into a doorway (two
+ * runs) will work correctly. A single run rightwards from @ will
+ * stop at 1. Another run right and down will enter the corridor
+ * and make the corner, stopping at the 2.
+ *
+ * #@x 1
+ * ########### ######
+ * 2 #
+ * #############
+ * #
+ *
+ * After any move, the function area_affect is called to
+ * determine the new surroundings, and the direction of
+ * subsequent moves. It examines the current player location
+ * (at which the runner has just arrived) and the previous
+ * direction (from which the runner is considered to have come).
+ *
+ * Moving one square in some direction places you adjacent to
+ * three or five new squares (for straight and diagonal moves
+ * respectively) to which you were not previously adjacent,
+ * marked as '!' in the diagrams below.
+ *
+ * ...! ...
+ * .o@! .o.!
+ * ...! ..@!
+ * !!!
+ *
+ * You STOP if any of the new squares are interesting in any way:
+ * for example, if they contain visible monsters or treasure.
+ *
+ * You STOP if any of the newly adjacent squares seem to be open,
+ * and you are also looking for a break on that side. (that is,
+ * find_openarea AND find_break).
+ *
+ * You STOP if any of the newly adjacent squares do NOT seem to be
+ * open and you are in an open area, and that side was previously
+ * entirely open.
+ *
+ * Corners: If you are not in the open (i.e. you are in a corridor)
+ * and there is only one way to go in the new squares, then turn in
+ * that direction. If there are more than two new ways to go, STOP.
+ * If there are two ways to go, and those ways are separated by a
+ * square which does not seem to be open, then STOP.
+ *
+ * Otherwise, we have a potential corner. There are two new open
+ * squares, which are also adjacent. One of the new squares is
+ * diagonally located, the other is straight on (as in the diagram).
+ * We consider two more squares further out (marked below as ?).
+ *
+ * We assign "option" to the straight-on grid, and "option2" to the
+ * diagonal grid, and "check_dir" to the grid marked 's'.
+ *
+ * .s
+ * @x?
+ * #?
+ *
+ * If they are both seen to be closed, then it is seen that no
+ * benefit is gained from moving straight. It is a known corner.
+ * To cut the corner, go diagonally, otherwise go straight, but
+ * pretend you stepped diagonally into that next location for a
+ * full view next time. Conversely, if one of the ? squares is
+ * not seen to be closed, then there is a potential choice. We check
+ * to see whether it is a potential corner or an intersection/room entrance.
+ * If the square two spaces straight ahead, and the space marked with 's'
+ * are both blank, then it is a potential corner and enter if find_examine
+ * is set, otherwise must stop because it is not a corner.
+ */
+
+
+
+
+/*
+ * Hack -- allow quick "cycling" through the legal directions
+ */
+static byte cycle[] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 };
+
+/*
+ * Hack -- map each direction into the "middle" of the "cycle[]" array
+ */
+static byte chome[] = { 0, 8, 9, 10, 7, 0, 11, 6, 5, 4 };
+
+/*
+ * The direction we are running
+ */
+static byte find_current;
+
+/*
+ * The direction we came from
+ */
+static byte find_prevdir;
+
+/*
+ * We are looking for open area
+ */
+static bool find_openarea;
+
+/*
+ * We are looking for a break
+ */
+static bool find_breakright;
+static bool find_breakleft;
+
+
+
+/*
+ * Initialize the running algorithm for a new direction.
+ *
+ * Diagonal Corridor -- allow diaginal entry into corridors.
+ *
+ * Blunt Corridor -- If there is a wall two spaces ahead and
+ * we seem to be in a corridor, then force a turn into the side
+ * corridor, must be moving straight into a corridor here. ???
+ *
+ * Diagonal Corridor Blunt Corridor (?)
+ * # # #
+ * #x# @x#
+ * @p. p
+ */
+static void run_init(int dir)
+{
+ int row, col, deepleft, deepright;
+
+ int i, shortleft, shortright;
+
+
+ /* Save the direction */
+ find_current = dir;
+
+ /* Assume running straight */
+ find_prevdir = dir;
+
+ /* Assume looking for open area */
+ find_openarea = TRUE;
+
+ /* Assume not looking for breaks */
+ find_breakright = find_breakleft = FALSE;
+
+ /* Assume no nearby walls */
+ deepleft = deepright = FALSE;
+ shortright = shortleft = FALSE;
+
+ /* Find the destination grid */
+ row = p_ptr->py + ddy[dir];
+ col = p_ptr->px + ddx[dir];
+
+ /* Extract cycle index */
+ i = chome[dir];
+
+ /* Check for walls */
+ if (see_obstacle(cycle[i + 1], p_ptr->py, p_ptr->px))
+ {
+ find_breakleft = TRUE;
+ shortleft = TRUE;
+ }
+ else if (see_obstacle(cycle[i + 1], row, col))
+ {
+ find_breakleft = TRUE;
+ deepleft = TRUE;
+ }
+
+ /* Check for walls */
+ if (see_obstacle(cycle[i - 1], p_ptr->py, p_ptr->px))
+ {
+ find_breakright = TRUE;
+ shortright = TRUE;
+ }
+ else if (see_obstacle(cycle[i - 1], row, col))
+ {
+ find_breakright = TRUE;
+ deepright = TRUE;
+ }
+
+ /* Looking for a break */
+ if (find_breakleft && find_breakright)
+ {
+ /* Not looking for open area */
+ find_openarea = FALSE;
+
+ /* Hack -- allow angled corridor entry */
+ if (dir & 0x01)
+ {
+ if (deepleft && !deepright)
+ {
+ find_prevdir = cycle[i - 1];
+ }
+ else if (deepright && !deepleft)
+ {
+ find_prevdir = cycle[i + 1];
+ }
+ }
+
+ /* Hack -- allow blunt corridor entry */
+ else if (see_obstacle(cycle[i], row, col))
+ {
+ if (shortleft && !shortright)
+ {
+ find_prevdir = cycle[i - 2];
+ }
+ else if (shortright && !shortleft)
+ {
+ find_prevdir = cycle[i + 2];
+ }
+ }
+ }
+}
+
+
+/*
+ * Update the current "run" path
+ *
+ * Return TRUE if the running should be stopped
+ */
+static bool run_test(void)
+{
+ int prev_dir, new_dir, check_dir = 0;
+
+ int row, col;
+
+ int i, max, inv;
+
+ int option = 0, option2 = 0;
+
+ cave_type *c_ptr;
+
+
+ /* Where we came from */
+ prev_dir = find_prevdir;
+
+
+ /* Range of newly adjacent grids */
+ max = (prev_dir & 0x01) + 1;
+
+
+ /* Look at every newly adjacent square. */
+ for (i = -max; i <= max; i++)
+ {
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* New direction */
+ new_dir = cycle[chome[prev_dir] + i];
+
+ /* New location */
+ row = p_ptr->py + ddy[new_dir];
+ col = p_ptr->px + ddx[new_dir];
+
+ /* Access grid */
+ c_ptr = &cave[row][col];
+
+
+ /* Visible monsters abort running */
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Visible monster */
+ if (m_ptr->ml) return (TRUE);
+ }
+
+ /* Visible objects abort running */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Visible object */
+ if (o_ptr->marked) return (TRUE);
+ }
+
+
+ /* Assume unknown */
+ inv = TRUE;
+
+ /* Check memorized grids */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ bool notice = TRUE;
+
+ /*
+ * Examine the terrain -- conditional disturbance
+ * If we had more flags, we could make these customisable too
+ */
+ switch (c_ptr->feat)
+ {
+ case FEAT_DEEP_LAVA:
+ case FEAT_SHAL_LAVA:
+ {
+ /* Ignore */
+ if (p_ptr->invuln || p_ptr->immune_fire) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+
+ case FEAT_DEEP_WATER:
+ case FEAT_ICE:
+ {
+ /* Ignore */
+ if (p_ptr->ffall || p_ptr->fly) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+
+ /* Open doors */
+ case FEAT_OPEN:
+ case FEAT_BROKEN:
+ {
+ /* Option -- ignore */
+ if (find_ignore_doors) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+
+ /*
+ * Stairs - too many of them, should find better ways to
+ * handle them (not scripting!, because it can be called
+ * from within the running algo) XXX XXX XXX
+ */
+ case FEAT_LESS:
+ case FEAT_MORE:
+ case FEAT_QUEST_ENTER:
+ case FEAT_QUEST_EXIT:
+ case FEAT_QUEST_DOWN:
+ case FEAT_QUEST_UP:
+ case FEAT_SHAFT_UP:
+ case FEAT_SHAFT_DOWN:
+ case FEAT_WAY_LESS:
+ case FEAT_WAY_MORE:
+ /* XXX */
+ case FEAT_BETWEEN:
+ case FEAT_BETWEEN2:
+ {
+ /* Option -- ignore */
+ if (find_ignore_stairs) notice = FALSE;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Check the "don't notice running" flag */
+ if (f_info[c_ptr->feat].flags1 & FF1_DONT_NOTICE_RUNNING)
+ {
+ notice = FALSE;
+ }
+
+ /* A detected trap is interesting */
+ if (c_ptr->info & (CAVE_TRDT)) notice = TRUE;
+
+ /* Interesting feature */
+ if (notice) return (TRUE);
+
+ /* The grid is "visible" */
+ inv = FALSE;
+ }
+
+ /* Mega-Hack -- Maze code removes CAVE_MARK XXX XXX XXX */
+ if (c_ptr->info & (CAVE_TRDT)) return (TRUE);
+
+ /* Analyze unknown grids and floors */
+ if (inv || cave_floor_bold(row, col))
+ {
+ /* Looking for open area */
+ if (find_openarea)
+ {
+ /* Nothing */
+ }
+
+ /* The first new direction. */
+ else if (!option)
+ {
+ option = new_dir;
+ }
+
+ /* Three new directions. Stop running. */
+ else if (option2)
+ {
+ return (TRUE);
+ }
+
+ /* Two non-adjacent new directions. Stop running. */
+ else if (option != cycle[chome[prev_dir] + i - 1])
+ {
+ return (TRUE);
+ }
+
+ /* Two new (adjacent) directions (case 1) */
+ else if (new_dir & 0x01)
+ {
+ check_dir = cycle[chome[prev_dir] + i - 2];
+ option2 = new_dir;
+ }
+
+ /* Two new (adjacent) directions (case 2) */
+ else
+ {
+ check_dir = cycle[chome[prev_dir] + i + 1];
+ option2 = option;
+ option = new_dir;
+ }
+ }
+
+ /* Obstacle, while looking for open area */
+ else
+ {
+ if (find_openarea)
+ {
+ if (i < 0)
+ {
+ /* Break to the right */
+ find_breakright = TRUE;
+ }
+
+ else if (i > 0)
+ {
+ /* Break to the left */
+ find_breakleft = TRUE;
+ }
+ }
+ }
+ }
+
+
+ /* Looking for open area */
+ if (find_openarea)
+ {
+ /* Hack -- look again */
+ for (i = -max; i < 0; i++)
+ {
+ new_dir = cycle[chome[prev_dir] + i];
+
+ row = p_ptr->py + ddy[new_dir];
+ col = p_ptr->px + ddx[new_dir];
+
+ /* Access grid */
+ c_ptr = &cave[row][col];
+
+ /* Unknown grids or non-obstacle */
+ if (!see_obstacle_grid(c_ptr))
+ {
+ /* Looking to break right */
+ if (find_breakright)
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Obstacle */
+ else
+ {
+ /* Looking to break left */
+ if (find_breakleft)
+ {
+ return (TRUE);
+ }
+ }
+ }
+
+ /* Hack -- look again */
+ for (i = max; i > 0; i--)
+ {
+ new_dir = cycle[chome[prev_dir] + i];
+
+ row = p_ptr->py + ddy[new_dir];
+ col = p_ptr->px + ddx[new_dir];
+
+ /* Access grid */
+ c_ptr = &cave[row][col];
+
+ /* Unknown grid or non-obstacle */
+ if (!see_obstacle_grid(c_ptr))
+ {
+ /* Looking to break left */
+ if (find_breakleft)
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Obstacle */
+ else
+ {
+ /* Looking to break right */
+ if (find_breakright)
+ {
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+
+ /* Not looking for open area */
+ else
+ {
+ /* No options */
+ if (!option)
+ {
+ return (TRUE);
+ }
+
+ /* One option */
+ else if (!option2)
+ {
+ /* Primary option */
+ find_current = option;
+
+ /* No other options */
+ find_prevdir = option;
+ }
+
+ /* Two options, examining corners */
+ else if (find_examine && !find_cut)
+ {
+ /* Primary option */
+ find_current = option;
+
+ /* Hack -- allow curving */
+ find_prevdir = option2;
+ }
+
+ /* Two options, pick one */
+ else
+ {
+ /* Get next location */
+ row = p_ptr->py + ddy[option];
+ col = p_ptr->px + ddx[option];
+
+ /* Don't see that it is closed off. */
+ /* This could be a potential corner or an intersection. */
+ if (!see_obstacle(option, row, col) || !see_obstacle(check_dir, row, col))
+ {
+ /* Can not see anything ahead and in the direction we */
+ /* are turning, assume that it is a potential corner. */
+ if (find_examine &&
+ see_nothing(option, row, col) &&
+ see_nothing(option2, row, col))
+ {
+ find_current = option;
+ find_prevdir = option2;
+ }
+
+ /* STOP: we are next to an intersection or a room */
+ else
+ {
+ return (TRUE);
+ }
+ }
+
+ /* This corner is seen to be enclosed; we cut the corner. */
+ else if (find_cut)
+ {
+ find_current = option2;
+ find_prevdir = option2;
+ }
+
+ /* This corner is seen to be enclosed, and we */
+ /* deliberately go the long way. */
+ else
+ {
+ find_current = option;
+ find_prevdir = option2;
+ }
+ }
+ }
+
+
+ /* About to hit a known wall, stop */
+ if (see_obstacle(find_current, p_ptr->py, p_ptr->px))
+ {
+ return (TRUE);
+ }
+
+
+ /* Failure */
+ return (FALSE);
+}
+
+
+
+/*
+ * Take one step along the current "run" path
+ */
+void run_step(int dir)
+{
+ /* Start running */
+ if (dir)
+ {
+ /* Hack -- do not start silly run */
+ if (see_obstacle(dir, p_ptr->py, p_ptr->px) &&
+ (cave[p_ptr->py + ddy[dir]][p_ptr->px + ddx[dir]].feat != FEAT_TREES))
+ {
+ /* Message */
+ msg_print("You cannot run in that direction.");
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Done */
+ return;
+ }
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Initialize */
+ run_init(dir);
+ }
+
+ /* Keep running */
+ else
+ {
+ /* Update run */
+ if (run_test())
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Done */
+ return;
+ }
+ }
+
+ /* Decrease the run counter */
+ if (--running <= 0) return;
+
+ /* Take time */
+ energy_use = 100;
+
+
+ /* Move the player, using the "pickup" flag */
+ move_player_aux(find_current, always_pickup, 1, TRUE);
+}
+
+
+/*
+ * Take care of the various things that can happen when you step
+ * into a space. (Objects, traps, and stores.)
+ */
+void step_effects(int y, int x, int do_pickup)
+{
+ /* Handle "objects" */
+ py_pickup_floor(do_pickup);
+
+ /* Handle "store doors" */
+ if (cave[y][x].feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Enter store */
+ command_new = KTRL('V');
+ }
+
+ /* Discover/set off traps */
+ else if (cave[y][x].t_idx != 0)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ if (!(cave[y][x].info & CAVE_TRDT))
+ {
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Pick a trap */
+ pick_trap(y, x);
+ }
+
+ /* Hit the trap */
+ hit_trap();
+ }
+}
+
+/*
+ * Issue a pet command
+ */
+void do_cmd_pet(void)
+{
+ int i = 0;
+
+ int num = 0;
+
+ int powers[36];
+
+ char power_desc[36][80];
+
+ bool flag, redraw;
+
+ int ask;
+
+ char choice;
+
+ char out_val[160];
+
+ int pets = 0, pet_ctr = 0;
+
+ bool all_pets = FALSE;
+
+ monster_type *m_ptr;
+
+
+ for (num = 0; num < 36; num++)
+ {
+ powers[num] = 0;
+ strcpy(power_desc[num], "");
+ }
+
+ num = 0;
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused to command your pets.");
+ energy_use = 0;
+ return;
+ }
+
+ /* Calculate pets */
+ /* Process the monsters (backwards) */
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+
+ if (m_ptr->status >= MSTATUS_FRIEND) pets++;
+ }
+
+ if (pets == 0)
+ {
+ msg_print("You have no pets/companions.");
+ energy_use = 0;
+ return;
+ }
+ else
+ {
+ strcpy(power_desc[num], "dismiss pets");
+ powers[num++] = 1;
+ strcpy(power_desc[num], "dismiss companions");
+ powers[num++] = 10;
+ strcpy(power_desc[num], "call pets");
+ powers[num++] = 2;
+ strcpy(power_desc[num], "follow me");
+ powers[num++] = 6;
+ strcpy(power_desc[num], "seek and destroy");
+ powers[num++] = 3;
+ if (p_ptr->pet_open_doors)
+ strcpy(power_desc[num], "disallow open doors");
+ else
+ strcpy(power_desc[num], "allow open doors");
+ powers[num++] = 4;
+ if (p_ptr->pet_pickup_items)
+ strcpy(power_desc[num], "disallow pickup items");
+ else
+ strcpy(power_desc[num], "allow pickup items");
+ powers[num++] = 5;
+ strcpy(power_desc[num], "give target to a friend");
+ powers[num++] = 7;
+ strcpy(power_desc[num], "give target to all friends");
+ powers[num++] = 8;
+ strcpy(power_desc[num], "friend forget target");
+ powers[num++] = 9;
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Build a prompt (accept all spells) */
+ if (num <= 26)
+ {
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78,
+ "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0),
+ I2A(num - 1));
+ }
+ else
+ {
+ strnfmt(out_val, 78,
+ "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0),
+ '0' + num - 27);
+ }
+
+ /* Get a command from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt("", y++, x);
+
+ while (ctr < num)
+ {
+ strnfmt(dummy, 80, "%c) %s", I2A(ctr), power_desc[ctr]);
+ prt(dummy, y + ctr, x);
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt("", y + ctr, x);
+ }
+ else
+ {
+ prt("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ ask = FALSE; /* Can't uppercase digits */
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", power_desc[i]);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Abort if needed */
+ if (!flag)
+ {
+ energy_use = 0;
+ return;
+ }
+
+ switch (powers[i])
+ {
+ /* forget target */
+ case 9:
+ {
+ monster_type *m_ptr;
+ int ii, jj;
+
+ msg_print("Select the friendly monster:");
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ if (m_ptr->status < MSTATUS_PET)
+ {
+ msg_print("You cannot give orders to this monster.");
+ return;
+ }
+
+ m_ptr->target = -1;
+ }
+ break;
+ }
+ /* Give target to all */
+ case 8:
+ {
+ monster_type *m_ptr;
+ int ii, jj, i;
+
+
+ msg_print("Select the target monster:");
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ msg_print("Target selected.");
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status < MSTATUS_PET) continue;
+
+ m_ptr->target = cave[jj][ii].m_idx;
+ }
+ }
+ else
+ {
+ msg_print("This is not a correct target.");
+ return;
+ }
+ break;
+ }
+ case 1: /* Dismiss pets */
+ {
+ int Dismissed = 0;
+
+ if (get_check("Dismiss all pets? ")) all_pets = TRUE;
+
+ /* Process the monsters (backwards) */
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ monster_race *r_ptr;
+
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_PET) || (m_ptr->status == MSTATUS_FRIEND))) /* Get rid of it! */
+ {
+ bool checked = FALSE;
+ char command;
+ bool delete_this = FALSE;
+
+ if (all_pets)
+ {
+ delete_this = TRUE;
+ }
+ else
+ {
+ char friend_name[80], check_friend[80];
+ monster_desc(friend_name, m_ptr, 0x80);
+ strnfmt(check_friend, 80, "Dismiss %s? (Escape to cancel)", friend_name);
+
+ while (!checked)
+ {
+ if (!get_com(check_friend, &command))
+ {
+ /* get out of loop */
+ checked = TRUE;
+ pet_ctr = 0;
+ }
+ else switch (command)
+ {
+ case 'Y':
+ case 'y':
+ delete_this = TRUE;
+ checked = TRUE;
+ break;
+ case 'n':
+ case 'N':
+ checked = TRUE;
+ break;
+ default:
+ bell();
+ break;
+ }
+ }
+ }
+
+ if (delete_this)
+ {
+ delete_monster_idx(pet_ctr);
+ Dismissed++;
+ }
+ }
+ }
+
+ msg_format("You have dismissed %d pet%s.", Dismissed,
+ (Dismissed == 1 ? "" : "s"));
+ break;
+ }
+ case 10: /* Dismiss companions */
+ {
+ int Dismissed = 0;
+
+ if (get_check("Dismiss all companions? ")) all_pets = TRUE;
+
+ /* Process the monsters (backwards) */
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ monster_race *r_ptr;
+
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_COMPANION))) /* Get rid of it! */
+ {
+ bool delete_this = FALSE;
+
+ if (all_pets)
+ delete_this = TRUE;
+ else
+ {
+ char friend_name[80], check_friend[80];
+ monster_desc(friend_name, m_ptr, 0x80);
+ strnfmt(check_friend, 80, "Dismiss %s? ", friend_name);
+
+ if (get_check(check_friend))
+ delete_this = TRUE;
+ }
+
+ if (delete_this)
+ {
+ delete_monster_idx(pet_ctr);
+ Dismissed++;
+ }
+ }
+ }
+
+ msg_format("You have dismissed %d companion%s.", Dismissed,
+ (Dismissed == 1 ? "" : "s"));
+ break;
+ }
+ /* Call pets */
+ case 2:
+ {
+ p_ptr->pet_follow_distance = 1;
+ break;
+ }
+ /* "Seek and destroy" */
+ case 3:
+ {
+ p_ptr->pet_follow_distance = 255;
+ break;
+ }
+ /* flag - allow pets to open doors */
+ case 4:
+ {
+ p_ptr->pet_open_doors = !p_ptr->pet_open_doors;
+ break;
+ }
+ /* flag - allow pets to pickup items */
+ case 5:
+ {
+ p_ptr->pet_pickup_items = !p_ptr->pet_pickup_items;
+
+ /* Drop objects being carried by pets */
+ if (!p_ptr->pet_pickup_items)
+ {
+ for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[pet_ctr];
+
+ if (m_ptr->status >= MSTATUS_PET)
+ {
+ monster_drop_carried_objects(m_ptr);
+ }
+ }
+ }
+
+ break;
+ }
+ /* "Follow Me" */
+ case 6:
+ {
+ p_ptr->pet_follow_distance = 6;
+ break;
+ }
+ }
+}
+
+/*
+ * Incarnate into a body
+ */
+bool do_cmd_integrate_body()
+{
+ cptr q, s;
+
+ int item;
+
+ object_type *o_ptr;
+
+
+ if (!p_ptr->disembodied)
+ {
+ msg_print("You are already in a body.");
+ return FALSE;
+ }
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_CORPSE;
+
+ /* Get an item */
+ q = "Incarnate in which body? ";
+ s = "You have no corpse to incarnate in.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return FALSE;
+
+ o_ptr = &o_list[0 - item];
+
+ if (o_ptr->sval != SV_CORPSE_CORPSE)
+ {
+ msg_print("You must select a corpse.");
+ return FALSE;
+ }
+
+ p_ptr->body_monster = o_ptr->pval2;
+ p_ptr->chp = o_ptr->pval3;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+
+ msg_print("Your spirit is incarnated in your new body.");
+ p_ptr->wraith_form = FALSE;
+ p_ptr->disembodied = FALSE;
+ do_cmd_redraw();
+
+ return TRUE;
+}
+
+/*
+ * Leave a body
+ */
+bool do_cmd_leave_body(bool drop_body)
+{
+ object_type *o_ptr, forge;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ int i;
+
+
+ if (p_ptr->disembodied)
+ {
+ msg_print("You are already disembodied.");
+ return FALSE;
+ }
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->body_parts[i - INVEN_WIELD] && p_ptr->inventory[i].k_idx &&
+ cursed_p(&p_ptr->inventory[i]))
+ {
+ msg_print("A cursed object is preventing you from leaving your body.");
+ return FALSE;
+ }
+ }
+
+ if (drop_body)
+ {
+ if (magik(25 + get_skill_scale(SKILL_POSSESSION, 25) + get_skill(SKILL_PRESERVATION)))
+ {
+ o_ptr = &forge;
+ object_prep(o_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+ o_ptr->number = 1;
+ o_ptr->pval = 0;
+ o_ptr->pval2 = p_ptr->body_monster;
+ o_ptr->pval3 = p_ptr->chp;
+ o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= IDENT_STOREB;
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ o_ptr->name1 = 201;
+ }
+
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+ }
+ else
+ msg_print
+ ("You do not manage to keep the corpse from rotting away.");
+ }
+
+ msg_print("Your spirit leaves your body.");
+ p_ptr->disembodied = TRUE;
+
+ /* Turn into a lost soul(just for the picture) */
+ p_ptr->body_monster = test_monster_name("Lost soul");
+ do_cmd_redraw();
+
+ return (TRUE);
+}
+
+
+bool execute_inscription(byte i, byte y, byte x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Not enough mana in the current grid */
+ if (c_ptr->mana < inscription_info[i].mana) return (TRUE);
+
+
+ /* Reduce the grid mana -- note: it can't be restored */
+ c_ptr->mana -= inscription_info[i].mana;
+
+ /* Analyse inscription type */
+ switch (i)
+ {
+ case INSCRIP_LIGHT:
+ {
+ msg_print("The inscription shines in a bright light!");
+ lite_room(y, x);
+
+ break;
+ }
+
+ case INSCRIP_DARK:
+ {
+ msg_print("The inscription is enveloped in a dark aura!");
+ unlite_room(y, x);
+
+ break;
+ }
+
+ case INSCRIP_STORM:
+ {
+ msg_print("The inscription releases a powerful storm!");
+ project(0, 3, y, x, damroll(10, 10),
+ GF_ELEC, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM |
+ PROJECT_KILL | PROJECT_JUMP);
+
+ break;
+ }
+
+ case INSCRIP_PROTECTION:
+ {
+ return (FALSE);
+
+ break;
+ }
+
+ case INSCRIP_DWARF_SUMMON:
+ {
+ int yy = y, xx = x;
+
+ scatter(&yy, &xx, y, x, 3, 0);
+ place_monster_one(yy, xx, test_monster_name("Dwarven Warrior"),
+ 0, FALSE, MSTATUS_FRIEND);
+
+ break;
+ }
+
+ case INSCRIP_CHASM:
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ cave_type *c_ptr;
+ int ii = x, ij = y;
+
+ cave_set_feat(ij, ii, FEAT_DARK_PIT);
+ msg_print("A chasm appears in the floor!");
+
+ if (cave[ij][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[ij][ii].m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ {
+ msg_print("The monster simply flies over the chasm.");
+ }
+ else
+ {
+ if (!(r_ptr->flags1 & RF1_UNIQUE))
+ {
+ msg_print("The monster falls in the chasm!");
+ delete_monster_idx(cave[ij][ii].m_idx);
+ }
+ }
+ }
+
+ if (cave[ij][ii].o_idx)
+ {
+ s16b this_o_idx, next_o_idx = 0;
+
+ c_ptr = &cave[ij][ii];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx;
+ this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+ bool plural = FALSE;
+
+ char o_name[80];
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ if (o_ptr->number > 1) plural = TRUE;
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Effect "observed" */
+ if (o_ptr->marked)
+ {
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+
+ /* Artifacts get to resist */
+ if (o_ptr->name1)
+ {
+ /* Observe the resist */
+ if (o_ptr->marked)
+ {
+ msg_format("The %s %s simply fly over the chasm!",
+ o_name, (plural ? "are" : "is"));
+ }
+ }
+
+ /* Kill it */
+ else
+ {
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Redraw */
+ lite_spot(ij, ii);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case INSCRIP_BLACK_FIRE:
+ {
+ msg_print("The inscription releases a blast of hellfire!");
+ project(0, 3, y, x, 200,
+ GF_HELL_FIRE, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM |
+ PROJECT_KILL | PROJECT_JUMP);
+
+ break;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Choose an inscription and engrave it
+ */
+void do_cmd_engrave()
+{
+ char buf[41] = "";
+
+ byte i;
+
+ strnfmt(buf, 41, "%s", inscription_info[cave[p_ptr->py][p_ptr->px].inscription].text);
+
+ get_string("Engrave what? ", buf, 40);
+
+ /* Silently do nothing when player his escape or enters an empty string */
+ if (!buf[0]) return;
+
+ for (i = 0; i < MAX_INSCRIPTIONS; i++)
+ {
+ if (!strcmp(inscription_info[i].text, buf))
+ {
+ if (inscription_info[i].know)
+ {
+ /* Save the inscription */
+ cave[p_ptr->py][p_ptr->px].inscription = i;
+ }
+ else
+ msg_print("You can't use this inscription for now.");
+ }
+ }
+
+ /* Execute the inscription */
+ if (inscription_info[cave[p_ptr->py][p_ptr->px].inscription].when & INSCRIP_EXEC_ENGRAVE)
+ {
+ execute_inscription(cave[p_ptr->py][p_ptr->px].inscription, p_ptr->py, p_ptr->px);
+ }
+
+ energy_use += 300;
+}
+
+
+/*
+ * Let's do a spinning around attack: -- DG --
+ * aDb
+ * y@k
+ * ooT
+ * Ah ... all of those will get hit.
+ */
+void do_spin()
+{
+ int i, j;
+
+
+ msg_print("You start spinning around...");
+
+ for (j = p_ptr->py - 1; j <= p_ptr->py + 1; j++)
+ {
+ for (i = p_ptr->px - 1; i <= p_ptr->px + 1; i++)
+ {
+ /* Avoid stupid bugs */
+ if (in_bounds(j, i) && cave[j][i].m_idx)
+ py_attack(j, i, 1);
+ }
+ }
+}
diff --git a/src/cmd2.c b/src/cmd2.c
new file mode 100644
index 00000000..27c64ed7
--- /dev/null
+++ b/src/cmd2.c
@@ -0,0 +1,5266 @@
+/* File: cmd2.c */
+
+/* Purpose: Movement commands (part 2) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+void do_cmd_immovable_special(void);
+
+/*
+ * Try to bash an altar
+ */
+static bool do_cmd_bash_altar(int y, int x)
+{
+ msg_print("Are you mad? You want to anger the gods?");
+ return (FALSE);
+}
+
+
+/*
+ * Try to bash a fountain
+ */
+static bool do_cmd_bash_fountain(int y, int x)
+{
+ int bash, temp;
+
+ cave_type *c_ptr;
+
+ bool more = TRUE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Message */
+ msg_print("You smash into the fountain!");
+
+ /* Hack -- Bash power based on strength */
+ /* (Ranges from 3 to 20 to 100 to 200) */
+ bash = adj_str_blow[p_ptr->stat_ind[A_STR]];
+
+ /* Compare bash power to door power XXX XXX XXX */
+ temp = (bash - 50);
+
+ /* Hack -- always have a chance */
+ if (temp < 1) temp = 1;
+
+ /* Hack -- attempt to bash down the door */
+ if (rand_int(200) < temp)
+ {
+ /* Message */
+ msg_print("The fountain breaks!");
+
+ fire_ball(GF_WATER, 5, damroll(6, 8), 2);
+
+ cave_set_feat(y, x, FEAT_DEEP_WATER);
+ more = FALSE;
+ }
+
+ return (more);
+}
+
+
+/*
+ * Go up one level
+ */
+void do_cmd_go_up(void)
+{
+ bool go_up = FALSE, go_up_many = FALSE, prob_traveling = FALSE;
+
+ cave_type *c_ptr;
+
+ int oldl = dun_level;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Can we ? */
+ if (process_hooks(HOOK_STAIR, "(s)", "up")) return;
+
+ /* Normal up stairs */
+ if ((c_ptr->feat == FEAT_LESS) || (c_ptr->feat == FEAT_WAY_LESS))
+ {
+ if (!dun_level)
+ {
+ go_up = TRUE;
+ }
+ else if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ go_up = get_check("Leave this unique level forever? ");
+ }
+ else if (confirm_stairs)
+ {
+ go_up = get_check("Really leave the level? ");
+ }
+ else
+ {
+ go_up = TRUE;
+ }
+ }
+
+ /* Shaft up */
+ else if (c_ptr->feat == FEAT_SHAFT_UP)
+ {
+ if (dun_level == 1)
+ {
+ go_up = TRUE;
+ }
+ else if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ go_up = get_check("Leave this unique level forever? ");
+ }
+ else if (confirm_stairs)
+ {
+ go_up_many = get_check("Really leave the level? ");
+ }
+ else
+ {
+ go_up_many = TRUE;
+ }
+ }
+
+ /* Quest exit */
+ else if (c_ptr->feat == FEAT_QUEST_EXIT)
+ {
+ leaving_quest = p_ptr->inside_quest;
+
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) &&
+ !get_check("Leave this unique level forever? "))
+ return;
+
+ p_ptr->inside_quest = c_ptr->special;
+ dun_level = 0;
+ p_ptr->oldpx = 0;
+ p_ptr->oldpy = 0;
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+
+ /* Exits to previous area in flat terrains */
+ else if (!(dungeon_flags1 & DF1_FLAT) &&
+ p_ptr->prob_travel && !p_ptr->inside_quest)
+ {
+ if (d_ptr->mindepth == dun_level) return;
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerful force prevents your from teleporting.");
+ return;
+ }
+
+ prob_traveling = TRUE;
+
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_up = TRUE;
+ }
+ else
+ {
+ go_up = TRUE;
+ }
+ }
+ else
+ {
+ msg_print("I see no up staircase here.");
+ return;
+ }
+
+ if (go_up || go_up_many)
+ {
+
+#if 0
+ /*
+ * I'm experimenting without this... otherwise the monsters get to
+ * act first when we go up stairs, theoretically resulting in a possible
+ * insta-death.
+ */
+ /* Hack -- take a turn */
+ energy_use = 100;
+#else
+energy_use = 0;
+#endif
+
+ /* Success */
+ if (c_ptr->feat == FEAT_WAY_LESS)
+ msg_print("You enter the previous area.");
+ else
+ msg_print("You enter a maze of up staircases.");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ if (p_ptr->inside_quest)
+ {
+ dun_level = 1;
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = c_ptr->special;
+ }
+
+ /* Create a way back */
+ if (go_up_many)
+ create_down_shaft = TRUE;
+ else
+ create_down_stair = TRUE;
+
+ /* New depth */
+ if (go_up)
+ dun_level--;
+ else
+ {
+ dun_level -= randint(3) + 1;
+ if (dun_level <= 0) dun_level = 0;
+ }
+
+ if (c_ptr->special && (!prob_traveling))
+ {
+ dun_level = oldl;
+ dun_level = get_flevel();
+ dungeon_type = c_ptr->special;
+ dun_level += d_info[dungeon_type].mindepth;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+}
+
+
+/*
+ * Returns TRUE if we are in the Between...
+ */
+static bool between_effect(void)
+{
+ byte bx, by;
+
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN)
+ {
+#if 0 /* The Between is out of the space-time continuum anyway */
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return (TRUE);
+ }
+
+#endif
+
+ bx = cave[p_ptr->py][p_ptr->px].special & 255;
+ by = cave[p_ptr->py][p_ptr->px].special >> 8;
+
+ msg_print("You fall into the void.");
+ msg_print("Brrrr! It's deadly cold.");
+
+ swap_position(by, bx);
+
+ /* To avoid being teleported back */
+ energy_use = 100;
+
+ return (TRUE);
+ }
+
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN2)
+ {
+ between_exit *be_ptr = &between_exits[cave[p_ptr->py][p_ptr->px].special];
+
+ p_ptr->wild_mode = FALSE;
+ p_ptr->wilderness_x = be_ptr->wild_x;
+ p_ptr->wilderness_y = be_ptr->wild_y;
+ p_ptr->oldpx = p_ptr->px = be_ptr->px;
+ p_ptr->oldpy = p_ptr->py = be_ptr->py;
+ dungeon_type = be_ptr->d_idx;
+ dun_level = be_ptr->level;
+ p_ptr->leaving = TRUE;
+
+ return (TRUE);
+ }
+ else
+ return (FALSE);
+}
+
+/*
+ * Go down one level
+ */
+void do_cmd_go_down(void)
+{
+ cave_type *c_ptr;
+
+ bool go_down = FALSE, go_down_many = FALSE, prob_traveling = FALSE;
+
+ bool fall_trap = FALSE;
+
+ char i;
+
+ int old_dun = dun_level;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+
+ /* MUST be actived now */
+ if (between_effect()) return;
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ if (p_ptr->astral && (dun_level == 98)) return;
+
+ if (c_ptr->t_idx == TRAP_OF_SINKING) fall_trap = TRUE;
+
+ /* test if on special level */
+ if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ prt("Leave this unique level forever (y/n) ? ", 0, 0);
+ flush();
+ i = inkey();
+ prt("", 0, 0);
+ if (i != 'y') return;
+ }
+
+ /* Can we ? */
+ if (process_hooks(HOOK_STAIR, "(s)", "down")) return;
+
+ /* Normal up stairs */
+ if (c_ptr->feat == FEAT_SHAFT_DOWN)
+ {
+ if (!dun_level)
+ {
+ go_down = TRUE;
+
+ /* Save old player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+ else
+ {
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down_many = TRUE;
+ }
+ else
+ {
+ go_down_many = TRUE;
+ }
+ }
+ }
+
+ /* Normal stairs */
+ else if ((c_ptr->feat == FEAT_MORE) || (c_ptr->feat == FEAT_WAY_MORE))
+ {
+ if (p_ptr->prob_travel)
+ {
+ if (d_ptr->maxdepth == dun_level) return;
+ }
+ if (!dun_level)
+ {
+ go_down = TRUE;
+
+ /* Save old player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+ else
+ {
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down = TRUE;
+ }
+ else
+ {
+ go_down = TRUE;
+ }
+ }
+ }
+
+ /* Handle quest areas -KMW- */
+ else if (c_ptr->feat == FEAT_QUEST_ENTER)
+ {
+ /* Enter quest level */
+ enter_quest();
+
+ return;
+ }
+
+ else if (!(dungeon_flags1 & DF1_FLAT) &&
+ p_ptr->prob_travel && !p_ptr->inside_quest)
+ {
+ if (d_ptr->maxdepth == dun_level) return;
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerfull force prevents your from teleporting.");
+ return;
+ }
+
+ prob_traveling = TRUE;
+
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down = TRUE;
+ }
+ else
+ {
+ go_down = TRUE;
+ }
+ }
+
+ else if (!(fall_trap))
+ {
+ msg_print("I see no down staircase here.");
+ return;
+ }
+
+ if (go_down || go_down_many)
+ {
+
+#if 0
+ /* Hack -- take a turn */
+ energy_use = 100;
+#else
+ energy_use = 0;
+#endif
+
+ if (fall_trap)
+ msg_print("You deliberately jump through the trap door.");
+ else
+ {
+ if (c_ptr->feat == FEAT_WAY_MORE)
+ msg_print("You enter the next area.");
+ else
+ msg_print("You enter a maze of down staircases.");
+ }
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Go down */
+ if (go_down)
+ {
+ dun_level++;
+ }
+ else if (go_down_many)
+ {
+ int i = randint(3) + 1, j;
+
+ for (j = 1; j < i; j++)
+ {
+ dun_level++;
+ if (is_quest(dun_level + i - 1)) break;
+ if (d_ptr->maxdepth == dun_level) break;
+ }
+ }
+
+ /* We change place */
+ if (c_ptr->special && (!prob_traveling))
+ {
+ if (d_info[c_ptr->special].min_plev <= p_ptr->lev)
+ {
+ dungeon_info_type *d_ptr = &d_info[c_ptr->special];
+
+ /* Do the lua scripts refuse ? ;) */
+ if (process_hooks(HOOK_ENTER_DUNGEON, "(d)", c_ptr->special))
+ {
+ dun_level = old_dun;
+ return;
+ }
+
+ /* Ok go in the new dungeon */
+ dungeon_type = c_ptr->special;
+ d_ptr = &d_info[dungeon_type];
+
+ if ((p_ptr->wilderness_x == d_ptr->ix) &&
+ (p_ptr->wilderness_y == d_ptr->iy))
+ {
+ dun_level = d_ptr->mindepth;
+ }
+ else if ((p_ptr->wilderness_x == d_ptr->ox) &&
+ (p_ptr->wilderness_y == d_ptr->oy))
+ {
+ dun_level = d_ptr->maxdepth;
+ }
+ else
+ {
+ dun_level = d_ptr->mindepth;
+ }
+
+ msg_format("You go into %s",
+ d_text + d_info[dungeon_type].text);
+ }
+ else
+ {
+ msg_print
+ ("You don't feel yourself experienced enough to go there...");
+ dun_level = old_dun;
+ return;
+ }
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ if (!fall_trap)
+ {
+ /* Create a way back */
+ if (go_down_many)
+ create_up_shaft = TRUE;
+ else
+ create_up_stair = TRUE;
+ }
+ }
+}
+
+
+
+/*
+ * Simple command to "search" for one turn
+ */
+void do_cmd_search(void)
+{
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Search */
+ search();
+}
+
+
+/*
+ * Hack -- toggle search mode
+ */
+void do_cmd_toggle_search(void)
+{
+ /* Stop searching */
+ if (p_ptr->searching)
+ {
+ /* Clear the searching flag */
+ p_ptr->searching = FALSE;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ /* Start searching */
+ else
+ {
+ /* Set the searching flag */
+ p_ptr->searching = TRUE;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw stuff */
+ p_ptr->redraw |= (PR_STATE | PR_SPEED);
+ }
+}
+
+
+
+/*
+ * Determine if a grid contains a chest
+ */
+static s16b chest_check(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip unknown chests XXX XXX */
+ /* if (!o_ptr->marked) continue; */
+
+ /* Check for chest */
+ if (o_ptr->tval == TV_CHEST) return (this_o_idx);
+ }
+
+ /* No chest */
+ return (0);
+}
+
+
+/*
+ * Allocates objects upon opening a chest -BEN-
+ *
+ * Disperse treasures from the given chest, centered at (x,y).
+ *
+ * Small chests often contain "gold", while Large chests always contain
+ * items. Wooden chests contain 2 items, Iron chests contain 4 items,
+ * and Steel chests contain 6 items. The "value" of the items in a
+ * chest is based on the "power" of the chest, which is in turn based
+ * on the level on which the chest is generated.
+ */
+static void chest_death(int y, int x, s16b o_idx)
+{
+ int number;
+
+ bool small;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+
+ /* Small chests often hold "gold" */
+ small = (o_ptr->sval < SV_CHEST_MIN_LARGE);
+
+ /* Determine how much to drop (see above) */
+ number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2;
+
+ /* Zero pval means empty chest */
+ if (!o_ptr->pval) number = 0;
+
+ /* Opening a chest */
+ opening_chest = TRUE;
+
+ /* Determine the "value" of the items */
+ object_level = ABS(o_ptr->pval) + 10;
+
+ /* Drop some objects (non-chests) */
+ for (; number > 0; --number)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Small chests often drop gold */
+ if (small && (rand_int(100) < 75))
+ {
+ /* Make some gold */
+ if (!make_gold(q_ptr)) continue;
+ }
+
+ /* Otherwise drop an item */
+ else
+ {
+ /* Make an object */
+ if (!make_object(q_ptr, FALSE, FALSE, d_info[dungeon_type].objs))
+ continue;
+ }
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+
+ /* Reset the object level */
+ object_level = dun_level;
+
+ /* No longer opening a chest */
+ opening_chest = FALSE;
+
+ /* Empty */
+ o_ptr->pval = 0;
+ o_ptr->pval2 = 0;
+
+ /* Known */
+ object_known(o_ptr);
+}
+
+
+/*
+ * Chests have traps too.
+ *
+ * Exploding chest destroys contents (and traps).
+ * Note that the chest itself is never destroyed.
+ */
+static void chest_trap(int y, int x, s16b o_idx)
+{
+ int trap;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ bool ident = FALSE;
+
+
+ /* Ignore disarmed chests */
+ if (o_ptr->pval <= 0) return;
+
+ /* Obtain the trap */
+ trap = o_ptr->pval;
+
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Set off trap */
+ ident = player_activate_trap_type(y, x, o_ptr, o_idx);
+ if (ident)
+ {
+ t_info[o_ptr->pval].ident = TRUE;
+ msg_format("You identified the trap as %s.",
+ t_name + t_info[trap].name);
+ }
+}
+
+
+/*
+ * Attempt to open the given chest at the given location
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool do_cmd_open_chest(int y, int x, s16b o_idx)
+{
+ int i, j;
+
+ bool flag = TRUE;
+
+ bool more = FALSE;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open chests.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Attempt to unlock it */
+ if (o_ptr->pval > 0)
+ {
+ /* Assume locked, and thus not open */
+ flag = FALSE;
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the difficulty */
+ j = i - o_ptr->pval;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success -- May still have traps */
+ if (rand_int(100) < j)
+ {
+ msg_print("You have picked the lock.");
+ gain_exp(1);
+ flag = TRUE;
+ }
+
+ /* Failure -- Keep trying */
+ else
+ {
+ /* We may continue repeating */
+ more = TRUE;
+
+ if (flush_failure) flush();
+
+ msg_print("You failed to pick the lock.");
+ }
+ }
+
+ /* Allowed to open */
+ if (flag)
+ {
+ /* Apply chest traps, if any */
+ chest_trap(y, x, o_idx);
+
+ /* Let the Chest drop items */
+ chest_death(y, x, o_idx);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+#if defined(ALLOW_EASY_OPEN) || defined(ALLOW_EASY_DISARM)
+
+/*
+ * Original code by TNB, improvement for Angband 2.9.3 by rr9
+ * Slightly modified for ToME because of its trap implementation
+ */
+
+/*
+ * Return TRUE if the given grid is an open door
+ */
+static bool is_open(cave_type *c_ptr)
+{
+ return (c_ptr->feat == FEAT_OPEN);
+}
+
+
+/*
+ * Return TRUE if the given grid is a closed door
+ */
+static bool is_closed(cave_type *c_ptr)
+{
+ byte feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else feat = c_ptr->feat;
+
+ return ((feat >= FEAT_DOOR_HEAD) && (feat <= FEAT_DOOR_TAIL));
+}
+
+
+/*
+ * Return TRUE if the given grid has a trap
+ */
+static bool is_trap(cave_type *c_ptr)
+{
+ return ((c_ptr->info & (CAVE_TRDT)) != 0);
+}
+
+
+/*
+ * Return the number of doors/traps around (or under)
+ * the character using the filter function 'test'
+ */
+static int count_feats(int *y, int *x, bool (*test) (cave_type *c_ptr),
+ bool under)
+{
+ int d;
+
+ int xx, yy;
+
+ int count;
+
+
+ /* Clear match counter */
+ count = 0;
+
+ /* Check around (and under) the character */
+ for (d = 0; d < 9; d++)
+ {
+ /* Ignore current grid if told so -- See tables.c */
+ if ((d == 8) && !under) continue;
+
+ /* Extract adjacent (legal) location */
+ yy = p_ptr->py + ddy_ddd[d];
+ xx = p_ptr->px + ddx_ddd[d];
+
+ /* Paranoia */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Must have knowledge */
+ if (!(cave[yy][xx].info & (CAVE_MARK))) continue;
+
+ /* Not looking for this feature */
+ if (!(*test) (&cave[yy][xx])) continue;
+
+ /* Count it */
+ count++;
+
+ /* Remember the location. Only meaningful if there's
+ exactly one match */
+ *y = yy;
+ *x = xx;
+ }
+
+ /* All done */
+ return (count);
+}
+
+
+/*
+ * Return the number of chests around (or under) the character.
+ * If requested, count only trapped chests.
+ */
+static int count_chests(int *y, int *x, bool trapped)
+{
+ int d, count, o_idx;
+
+ object_type *o_ptr;
+
+
+ /* Count how many matches */
+ count = 0;
+
+ /* Check around (and under) the character */
+ for (d = 0; d < 9; d++)
+ {
+
+ /* Extract adjacent (legal) location */
+ int yy = p_ptr->py + ddy_ddd[d];
+ int xx = p_ptr->px + ddx_ddd[d];
+
+ /* No (visible) chest is there */
+ if ((o_idx = chest_check(yy, xx)) == 0) continue;
+
+ /* Grab the object */
+ o_ptr = &o_list[o_idx];
+
+ /* Already open */
+ if (o_ptr->pval == 0) continue;
+
+ /* No (known) traps here */
+ if (trapped && (!object_known_p(o_ptr) || !o_ptr->pval)) continue;
+
+ /* OK */
+ ++count;
+
+ /* Remember the location. Only useful if only one match */
+ *y = yy;
+ *x = xx;
+ }
+
+ /* All done */
+ return (count);
+}
+
+
+/*
+ * Convert an adjacent location to a direction.
+ */
+static int coords_to_dir(int y, int x)
+{
+ int d[3][3] =
+ {
+ {7, 4, 1},
+ {8, 5, 2},
+ {9, 6, 3} };
+
+ int dy, dx;
+
+
+ dy = y - p_ptr->py;
+ dx = x - p_ptr->px;
+
+ /* Paranoia */
+ if (ABS(dx) > 1 || ABS(dy) > 1) return (0);
+
+ return d[dx + 1][dy + 1];
+}
+
+#endif /* defined(ALLOW_EASY_OPEN) || defined(ALLOW_EASY_DISARM) -- TNB */
+
+
+/*
+ * Perform the basic "open" command on doors
+ *
+ * Assume destination is a closed/locked/jammed door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool do_cmd_open_aux(int y, int x, int dir)
+{
+ int i, j;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* Jammed door */
+ if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08)
+ {
+ /* Stuck */
+ msg_print("The door appears to be stuck.");
+ }
+
+ /* Locked door */
+ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01)
+ {
+ /* Disarm factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the lock power */
+ j = c_ptr->feat - FEAT_DOOR_HEAD;
+
+ /* Extract the difficulty XXX XXX XXX */
+ j = i - (j * 4);
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_print("You have picked the lock.");
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Experience */
+ gain_exp(1);
+ }
+
+ /* Failure */
+ else
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to pick the lock.");
+
+ /* We may keep trying */
+ more = TRUE;
+ }
+ }
+
+ /* Closed door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+
+/*
+ * Open a closed/locked/jammed door or a closed/locked chest.
+ *
+ * Unlocking a locked door/chest is worth one experience point.
+ */
+void do_cmd_open(void)
+{
+ int y, x, dir;
+
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return;
+ }
+
+#ifdef ALLOW_EASY_OPEN /* TNB */
+
+ /* Option: Pick a direction */
+ if (easy_open)
+ {
+ int num_doors, num_chests;
+
+ /* Count closed doors (locked or jammed) */
+ num_doors = count_feats(&y, &x, is_closed, FALSE);
+
+ /* Count chests (locked) */
+ num_chests = count_chests(&y, &x, FALSE);
+
+ /* There is nothing the player can open */
+ if ((num_doors + num_chests) == 0)
+ {
+ /* Message */
+ msg_print("You see nothing there to open.");
+
+ /* Done */
+ return;
+ }
+
+ /* Set direction if there is only one target */
+ else if ((num_doors + num_chests) == 1)
+ {
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+#endif /* ALLOW_EASY_OPEN -- TNB */
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* Check for chest */
+ o_idx = chest_check(y, x);
+
+ /* Nothing useful */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) && !o_idx)
+ {
+ /* Message */
+ msg_print("You see nothing there to open.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Handle chests */
+ else if (o_idx)
+ {
+ /* Open the chest */
+ more = do_cmd_open_chest(y, x, o_idx);
+ }
+
+ /* Handle doors */
+ else
+ {
+ /* Open the door */
+ more = do_cmd_open_aux(y, x, dir);
+ }
+ }
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level));
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(0, 0);
+}
+
+
+
+/*
+ * Perform the basic "close" command
+ *
+ * Assume destination is an open/broken door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool do_cmd_close_aux(int y, int x, int dir)
+{
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot close doors.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Broken door */
+ if (c_ptr->feat == FEAT_BROKEN)
+ {
+ /* Message */
+ msg_print("The door appears to be broken.");
+ }
+
+ /* Open door */
+ else
+ {
+ /* Close the door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_SHUTDOOR);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Close an open door.
+ */
+void do_cmd_close(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+
+#ifdef ALLOW_EASY_OPEN /* TNB */
+
+ /* Option: Pick a direction */
+ if (easy_open)
+ {
+ int num_doors;
+
+ /* Count open doors */
+ num_doors = count_feats(&y, &x, is_open, FALSE);
+
+ /* There are no doors the player can close */
+ if (num_doors == 0)
+ {
+ /* Message */
+ msg_print("You see nothing there to close.");
+
+ /* Done */
+ return;
+ }
+
+ /* Exactly one closeable door */
+ else if (num_doors == 1)
+ {
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+#endif /* ALLOW_EASY_OPEN -- TNB */
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Require open/broken door */
+ if ((c_ptr->feat != FEAT_OPEN) && (c_ptr->feat != FEAT_BROKEN))
+ {
+ /* Message */
+ msg_print("You see nothing there to close.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Close the door */
+ else
+ {
+ /* Close the door */
+ more = do_cmd_close_aux(y, x, dir);
+ }
+ }
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Determine if a given grid may be "tunneled"
+ */
+static bool do_cmd_tunnel_test(int y, int x)
+{
+ /* Must have knowledge(execpt on "forget" levels) */
+ if (!(cave[y][x].info & (CAVE_MARK)))
+ {
+ /* Message */
+ msg_print("You see nothing there.");
+
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Must be a wall/door/etc */
+ if (cave_floor_bold(y, x))
+ {
+ /* Message */
+ msg_print("You see nothing there to tunnel.");
+
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Must be tunnelable */
+ if (!(f_info[cave[y][x].feat].flags1 & FF1_TUNNELABLE))
+ {
+ /* Message */
+ msg_print(f_text + f_info[cave[y][x].feat].tunnel);
+
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Tunnel through wall. Assumes valid location.
+ *
+ * Note that it is impossible to "extend" rooms past their
+ * outer walls (which are actually part of the room).
+ *
+ * This will, however, produce grids which are NOT illuminated
+ * (or darkened) along with the rest of the room.
+ */
+static bool twall(int y, int x, byte feat)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Paranoia -- Require a wall or door or some such */
+ if (cave_floor_bold(y, x)) return (FALSE);
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Remove the feature */
+ cave_set_feat(y, x, feat);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Result */
+ return (TRUE);
+}
+
+
+
+/*
+ * Perform the basic "tunnel" command
+ *
+ * Assumes that the destination is a wall, a vein, a secret
+ * door, or rubble.
+ *
+ * Assumes that no monster is blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+bool do_cmd_tunnel_aux(int y, int x, int dir)
+{
+ int skill_req = 0, skill_req_1pct = 0;
+ cave_type *c_ptr = &cave[y][x];
+
+ feature_type *f_ptr = &f_info[c_ptr->feat];
+
+ bool more = FALSE;
+
+
+ /* Must be have something to dig with (except for sandwalls) */
+ if ((c_ptr->feat < FEAT_SANDWALL) || (c_ptr->feat > FEAT_SANDWALL_K))
+ {
+ if (!p_ptr->inventory[INVEN_TOOL].k_idx ||
+ (p_ptr->inventory[INVEN_TOOL].tval != TV_DIGGING))
+ {
+ msg_print("You need to have a shovel or pick in your tool slot.");
+
+ return (FALSE);
+ }
+ }
+
+ /* Verify legality */
+ if (!do_cmd_tunnel_test(y, x)) return (FALSE);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Sound */
+ sound(SOUND_DIG);
+
+ /* Titanium */
+ if (f_ptr->flags1 & FF1_PERMANENT)
+ {
+ msg_print(f_text + f_ptr->tunnel);
+ }
+
+ else if ((c_ptr->feat == FEAT_TREES) || (c_ptr->feat == FEAT_DEAD_TREE))
+ {
+ /* Chop Down */
+ skill_req = 10;
+ skill_req_1pct = 14;
+ if ((p_ptr->skill_dig > 10 + rand_int(400)) && twall(y, x, FEAT_GRASS))
+ {
+ msg_print("You have cleared away the trees.");
+ }
+
+ /* Keep trying */
+ else
+ {
+ /* We may continue chopping */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+
+ /* Occasional Search XXX XXX */
+ if (rand_int(100) < 25) search();
+ }
+ }
+
+
+ /* Granite */
+ else if ((c_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (c_ptr->feat <= FEAT_WALL_SOLID))
+ {
+ /* Tunnel */
+ skill_req = 40;
+ skill_req_1pct = 56;
+ if ((p_ptr->skill_dig > 40 + rand_int(1600)) && twall(y, x, FEAT_FLOOR))
+ {
+ msg_print("You have finished the tunnel.");
+ }
+
+ /* Keep trying */
+ else
+ {
+ /* We may continue tunelling */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+
+ /* Quartz / Magma / Sandwall */
+ else if (((c_ptr->feat >= FEAT_MAGMA) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ ((c_ptr->feat >= FEAT_SANDWALL) &&
+ (c_ptr->feat <= FEAT_SANDWALL_K)))
+ {
+ bool okay = FALSE;
+ bool gold = FALSE;
+ bool hard = FALSE;
+ bool soft = FALSE;
+
+ /* Found gold */
+ if ((c_ptr->feat >= FEAT_MAGMA_H) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) gold = TRUE;
+
+ if ((c_ptr->feat == FEAT_SANDWALL_H) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ gold = TRUE;
+ soft = TRUE;
+ }
+ else
+ /* Extract "quartz" flag XXX XXX XXX */
+ if ((c_ptr->feat - FEAT_MAGMA) & 0x01) hard = TRUE;
+
+ /* Quartz */
+ if (hard)
+ {
+ skill_req = 20;
+ skill_req_1pct = 28;
+ okay = (p_ptr->skill_dig > 20 + rand_int(800));
+ }
+
+ /* Sandwall */
+ else if (soft)
+ {
+ skill_req = 5;
+ skill_req_1pct = 8;
+ okay = (p_ptr->skill_dig > 5 + rand_int(250));
+ }
+
+ /* Magma */
+ else
+ {
+ skill_req = 10;
+ skill_req_1pct = 14;
+ okay = (p_ptr->skill_dig > 10 + rand_int(400));
+ }
+
+ /* Success */
+ if (okay && twall(y, x, FEAT_FLOOR))
+ {
+ /* Found treasure */
+ if (gold)
+ {
+ /* Place some gold */
+ place_gold(y, x);
+
+ /* Message */
+ msg_print("You have found something!");
+ }
+
+ /* Found nothing */
+ else
+ {
+ /* Message */
+ msg_print("You have finished the tunnel.");
+ }
+ }
+
+ /* Failure */
+ else
+ {
+ /* Message, continue digging */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+ /* Rubble */
+ else if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Remove the rubble */
+ skill_req = 0;
+ skill_req_1pct = 2;
+ if ((p_ptr->skill_dig > rand_int(200)) &&
+ twall(y, x, d_info[dungeon_type].floor1))
+ {
+ /* Message */
+ msg_print("You have removed the rubble.");
+
+ /* Hack -- place an object */
+ if (rand_int(100) < 10)
+ {
+ /* Create a simple object */
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE);
+
+ /* Observe new object */
+ if (player_can_see_bold(y, x))
+ {
+ msg_print("You have found something!");
+ }
+ }
+ }
+
+ else
+ {
+ /* Message, keep digging */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+ /* Secret doors */
+ else if (c_ptr->feat >= FEAT_SECRET)
+ {
+ /* Tunnel */
+ skill_req = 30;
+ skill_req_1pct = 42;
+ if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR))
+ {
+ msg_print("You have finished the tunnel.");
+ c_ptr->mimic = 0;
+ lite_spot(y, x);
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+ }
+
+ /* Keep trying */
+ else
+ {
+ int feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else
+ feat = c_ptr->feat;
+
+ /* We may continue tunelling */
+ msg_print(f_text + f_info[feat].tunnel);
+ more = TRUE;
+
+ /* Occasional Search XXX XXX */
+ if (rand_int(100) < 25) search();
+ }
+ }
+
+ /* Doors */
+ else
+ {
+ /* Tunnel */
+ skill_req = 30;
+ skill_req_1pct = 42;
+ if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR))
+ {
+ msg_print("You have finished the tunnel.");
+ }
+
+ /* Keep trying */
+ else
+ {
+ /* We may continue tunelling */
+ msg_print(f_text + f_ptr->tunnel);
+ more = TRUE;
+ }
+ }
+
+ if (more && magik(2))
+ {
+ if (p_ptr->skill_dig < skill_req)
+ {
+ msg_print("You fail to make even the slightest of progress.");
+ more = FALSE;
+ }
+ else if (p_ptr->skill_dig < skill_req_1pct)
+ {
+ msg_print("This will take some time.");
+ }
+ }
+
+ /* Notice new floor grids */
+ if (!cave_floor_bold(y, x))
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Tunnels through "walls" (including rubble and closed doors)
+ *
+ * Note that you must tunnel in order to hit invisible monsters
+ * in walls, though moving into walls still takes a turn anyway.
+ *
+ * Digging is very difficult without a "digger" weapon, but can be
+ * accomplished by strong players using heavy weapons.
+ */
+void do_cmd_tunnel(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+
+ if (p_ptr->wild_mode) return;
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction to tunnel, or Abort */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* No tunnelling through doors */
+ if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) || (c_ptr->feat == FEAT_SHOP))
+ {
+ /* Message */
+ msg_print("You cannot tunnel through doors.");
+ }
+
+ /* No tunnelling through air */
+ else if (cave_floor_grid(c_ptr))
+ {
+ /* Message */
+ msg_print("You cannot tunnel through air.");
+ }
+
+ /* A monster is in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Try digging */
+ else
+ {
+ /* Tunnel through walls */
+ more = do_cmd_tunnel_aux(y, x, dir);
+ }
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0, 0);
+}
+
+
+#ifdef ALLOW_EASY_OPEN /* TNB */
+
+/*
+ * easy_open_door --
+ *
+ * If there is a jammed/closed/locked door at the given location,
+ * then attempt to unlock/open it. Return TRUE if an attempt was
+ * made (successful or not), otherwise return FALSE.
+ *
+ * The code here should be nearly identical to that in
+ * do_cmd_open_test() and do_cmd_open_aux().
+ */
+
+bool easy_open_door(int y, int x)
+{
+ int i, j;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return (FALSE);
+ }
+
+ /* Must be a closed door */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Nope */
+ return (FALSE);
+ }
+
+ /* Jammed door */
+ if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08)
+ {
+ /* Stuck */
+ msg_print("The door appears to be stuck.");
+ }
+
+ /* Locked door */
+ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01)
+ {
+ /* Disarm factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the lock power */
+ j = c_ptr->feat - FEAT_DOOR_HEAD;
+
+ /* Extract the difficulty XXX XXX XXX */
+ j = i - (j * 4);
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_print("You have picked the lock.");
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level));
+
+ /* Experience */
+ gain_exp(1);
+ }
+
+ /* Failure */
+ else
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to pick the lock.");
+ }
+ }
+
+ /* Closed door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+ }
+
+ /* Result */
+ return (TRUE);
+}
+
+#endif /* ALLOW_EASY_OPEN -- TNB */
+
+
+/*
+ * Perform the basic "disarm" command
+ *
+ * Assume destination is a visible trap
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool do_cmd_disarm_chest(int y, int x, s16b o_idx)
+{
+ int i, j;
+
+ bool more = FALSE;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ trap_type *t_ptr = &t_info[o_ptr->pval];
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the difficulty */
+ j = i - t_ptr->difficulty * 3;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Must find the trap first. */
+ if (!object_known_p(o_ptr))
+ {
+ msg_print("I don't see any traps.");
+ }
+
+ /* Already disarmed/unlocked */
+ else if (o_ptr->pval <= 0)
+ {
+ msg_print("The chest is not trapped.");
+ }
+
+ /* Success (get a lot of experience) */
+ else if (rand_int(100) < j)
+ {
+ msg_print("You have disarmed the chest.");
+ gain_exp(t_ptr->difficulty * 3);
+ o_ptr->pval = (0 - o_ptr->pval);
+ }
+
+ /* Failure -- Keep trying */
+ else if ((i > 5) && (randint(i) > 5))
+ {
+ /* We may keep trying */
+ more = TRUE;
+ if (flush_failure) flush();
+ msg_print("You failed to disarm the chest.");
+ }
+
+ /* Failure -- Set off the trap */
+ else
+ {
+ msg_print("You set off a trap!");
+ sound(SOUND_FAIL);
+ chest_trap(y, x, o_idx);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Perform the basic "disarm" command
+ *
+ * Assume destination is a visible trap
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+#ifdef ALLOW_EASY_DISARM /* TNB */
+bool do_cmd_disarm_aux(int y, int x, int dir, int do_pickup)
+#else /* ALLOW_EASY_DISARM -- TNB */
+static bool do_cmd_disarm_aux(int y, int x, int dir, int do_pickup)
+#endif /* ALLOW_EASY_DISARM -- TNB */
+{
+ int i, j, power;
+
+ cave_type *c_ptr;
+
+ cptr name;
+
+ bool more = FALSE;
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Access trap name */
+ if (t_info[c_ptr->t_idx].ident)
+ name = (t_name + t_info[c_ptr->t_idx].name);
+ else
+ name = "unknown trap";
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* XXX XXX XXX Variable power? */
+
+ /* Extract trap "power" */
+ power = t_info[c_ptr->t_idx].difficulty;
+
+ /* Extract the difficulty */
+ j = i - power;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_format("You have disarmed the %s.", name);
+
+ /* Reward */
+ gain_exp(power);
+
+ /* Forget the trap */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Remove the trap */
+ c_ptr->t_idx = 0;
+
+ /* Move the player onto the trap */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))
+ move_player_aux(dir, do_pickup, 0, TRUE);
+
+ /* Remove trap attr from grid */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ /* Failure -- Keep trying */
+ else if ((i > 5) && (randint(i) > 5))
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_format("You failed to disarm the %s.", name);
+
+ /* We may keep trying */
+ more = TRUE;
+ }
+
+ /* Failure -- Set off the trap */
+ else
+ {
+ /* Message */
+ msg_format("You set off the %s!", name);
+
+ /* Move the player onto the trap */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))
+ move_player_aux(dir, do_pickup, 0, FALSE);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Disamrs the monster traps(no failure)
+ */
+void do_cmd_disarm_mon_trap(int y, int x)
+{
+ msg_print("You disarm the monster trap.");
+
+ place_floor(y, x);
+ cave[p_ptr->py][p_ptr->px].special = cave[p_ptr->py][p_ptr->px].special2 = 0;
+}
+
+
+/*
+ * Disarms a trap, or chest
+ */
+void do_cmd_disarm(void)
+{
+ int y, x, dir;
+
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+
+#ifdef ALLOW_EASY_DISARM /* TNB */
+
+ /* Option: Pick a direction */
+ if (easy_disarm)
+ {
+ int num_traps, num_chests;
+
+ /* Count visible traps */
+ num_traps = count_feats(&y, &x, is_trap, TRUE);
+
+ /* Count chests (trapped) */
+ num_chests = count_chests(&y, &x, TRUE);
+
+ /* See if only one target */
+ if (num_traps || num_chests)
+ {
+ if (num_traps + num_chests <= 1)
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+#endif /* ALLOW_EASY_DISARM -- TNB */
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction (or abort) */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Check for chests */
+ o_idx = chest_check(y, x);
+
+ /* Disarm a trap */
+ if (((c_ptr->t_idx == 0) || (!(c_ptr->info & CAVE_TRDT))) &&
+ !o_idx && (c_ptr->feat != FEAT_MON_TRAP))
+ {
+ /* Message */
+ msg_print("You see nothing there to disarm.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Disarm chest */
+ else if (o_idx)
+ {
+ /* Disarm the chest */
+ more = do_cmd_disarm_chest(y, x, o_idx);
+ }
+
+ /* Disarm trap */
+ else
+ {
+ /* Disarm the trap */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ do_cmd_disarm_mon_trap(y, x);
+ more = FALSE;
+ }
+ else
+ more = do_cmd_disarm_aux(y, x, dir, always_pickup);
+ }
+ }
+
+ /* Cancel repeat unless told not to */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Perform the basic "bash" command
+ *
+ * Assume destination is a closed/locked/jammed door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool do_cmd_bash_aux(int y, int x, int dir)
+{
+ int bash, temp;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Message */
+ msg_print("You smash into the door!");
+
+ /* Hack -- Bash power based on strength */
+ /* (Ranges from 3 to 20 to 100 to 200) */
+ bash = adj_str_blow[p_ptr->stat_ind[A_STR]];
+
+ /* Extract door power */
+ temp = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+ /* Compare bash power to door power XXX XXX XXX */
+ temp = (bash - (temp * 10));
+
+ /* Hack -- always have a chance */
+ if (temp < 1) temp = 1;
+
+ /* Hack -- attempt to bash down the door */
+ if (rand_int(100) < temp)
+ {
+ /* Message */
+ msg_print("The door crashes open!");
+
+ /* Break down the door */
+ if (rand_int(100) < 50)
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ cave_set_feat(y, x, FEAT_BROKEN);
+ }
+
+ /* Open the door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ cave_set_feat(y, x, FEAT_OPEN);
+ }
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Hack -- Fall through the door. Can't disarm while falling. */
+ move_player_aux(dir, always_pickup, 0, FALSE);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_DISTANCE);
+ }
+
+ /* Saving throw against stun */
+ else if (rand_int(100) < adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev)
+ {
+ /* Message */
+ msg_print("The door holds firm.");
+
+ /* Allow repeated bashing */
+ more = TRUE;
+ }
+
+ /* High dexterity yields coolness */
+ else
+ {
+ /* Message */
+ msg_print("You are off-balance.");
+
+ /* Hack -- Lose balance ala paralysis */
+ (void)set_paralyzed(p_ptr->paralyzed + 2 + rand_int(2));
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Bash open a door, success based on character strength
+ *
+ * For a closed door, pval is positive if locked; negative if stuck.
+ *
+ * For an open door, pval is positive for a broken door.
+ *
+ * A closed door can be opened - harder if locked. Any door might be
+ * bashed open (and thereby broken). Bashing a door is (potentially)
+ * faster! You move into the door way. To open a stuck door, it must
+ * be bashed. A closed door can be jammed (see do_cmd_spike()).
+ *
+ * Creatures can also open or bash doors, see elsewhere.
+ */
+void do_cmd_bash(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return;
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Bash location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Nothing useful */
+ if ((c_ptr->feat < FEAT_DOOR_HEAD ||
+ c_ptr->feat > FEAT_DOOR_TAIL) &&
+ (c_ptr->feat < FEAT_ALTAR_HEAD ||
+ c_ptr->feat > FEAT_ALTAR_TAIL) && (c_ptr->feat != FEAT_FOUNTAIN))
+ {
+ /* Message */
+ msg_print("You see nothing there to bash.");
+ }
+
+ /* Monster in the way */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ else if (c_ptr->feat >= FEAT_ALTAR_HEAD &&
+ c_ptr->feat <= FEAT_ALTAR_TAIL)
+ {
+ more = do_cmd_bash_altar(y, x);
+ }
+ /* Bash a closed door */
+ else if (c_ptr->feat == FEAT_FOUNTAIN)
+ {
+ more = do_cmd_bash_fountain(y, x);
+ }
+ else
+ {
+ /* Bash the door */
+ more = do_cmd_bash_aux(y, x, dir);
+ }
+ }
+
+ /* Unless valid action taken, cancel bash */
+ if (!more) disturb(0, 0);
+}
+
+
+
+/*
+ * Manipulate an adjacent grid in some way
+ *
+ * Attack monsters, tunnel through walls, disarm traps, open doors.
+ *
+ * Consider confusion XXX XXX XXX
+ *
+ * This command must always take a turn, to prevent free detection
+ * of invisible monsters.
+ */
+void do_cmd_alter(void)
+{
+ int y, x, dir;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Attack monsters */
+ if (c_ptr->m_idx)
+ {
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+#if 0
+ /* Bash jammed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Tunnel */
+ more = do_cmd_bash_aux(y, x, dir);
+ }
+#endif
+ /* Open closed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Tunnel */
+ more = do_cmd_open_aux(y, x, dir);
+ }
+
+ /* Tunnel through walls */
+ else if (f_info[c_ptr->feat].flags1 & FF1_TUNNELABLE)
+ {
+ /* Tunnel */
+ more = do_cmd_tunnel_aux(y, x, dir);
+ }
+
+ /* Disarm traps */
+ else if (c_ptr->t_idx != 0)
+ {
+ /* Tunnel */
+ more = do_cmd_disarm_aux(y, x, dir, always_pickup);
+ }
+
+ /* Oops */
+ else
+ {
+ /* Oops */
+ msg_print("You attack the empty air.");
+ }
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Find the index of some "spikes", if possible.
+ *
+ * XXX XXX XXX Let user choose a pile of spikes, perhaps?
+ */
+static bool get_spike(int *ip)
+{
+ int i;
+
+
+ /* Check every item in the pack */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Check the "tval" code */
+ if (o_ptr->tval == TV_SPIKE)
+ {
+ /* Save the spike index */
+ (*ip) = i;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+
+/*
+ * Jam a closed door with a spike
+ *
+ * This command may NOT be repeated
+ */
+void do_cmd_spike(void)
+{
+ int y, x, dir, item;
+
+ cave_type *c_ptr;
+
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Require closed door */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Message */
+ msg_print("You see nothing there to spike.");
+ }
+
+ /* Get a spike */
+ else if (!get_spike(&item))
+ {
+ /* Message */
+ msg_print("You have no spikes!");
+ }
+
+ /* Is a monster in the way? */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Go for it */
+ else
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Successful jamming */
+ msg_print("You jam the door with a spike.");
+
+ /* Convert "locked" to "stuck" XXX XXX XXX */
+ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08;
+
+ /* Add one spike to the door */
+ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++;
+
+ /* Use up, and describe, a single spike, from the bottom */
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ }
+}
+
+
+static void do_cmd_walk_jump(int pickup, bool disarm)
+{
+ int dir;
+
+ bool more = FALSE;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Actually move the character */
+ move_player(dir, pickup, disarm);
+
+ /* Allow more walking */
+ more = TRUE;
+ }
+
+ /* Hack -- In small scale wilderness it takes MUCH more time to move */
+ energy_use *= (p_ptr->wild_mode) ? ((MAX_HGT + MAX_WID) / 2) : 1;
+
+ /* Hack again -- Is there a special encounter ??? */
+ if (p_ptr->wild_mode &&
+ magik(wf_info[wild_map[p_ptr->py][p_ptr->px].feat].level - (p_ptr->lev * 2)))
+ {
+ /* Go into large wilderness view */
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ energy_use = 100;
+ change_wild_mode();
+
+ /* HACk -- set the encouter flag for the wilderness generation */
+ generate_encounter = TRUE;
+ p_ptr->oldpx = MAX_WID / 2;
+ p_ptr->oldpy = MAX_HGT / 2;
+
+ /* Inform the player of his horrible fate :=) */
+ msg_print("You are ambushed!");
+ }
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(0, 0);
+}
+
+
+/*
+ * Support code for the "Walk" and "Jump" commands
+ */
+void do_cmd_walk(int pickup, bool disarm)
+{
+ /* Move (usually pickup) */
+
+ if (p_ptr->immovable)
+ {
+ do_cmd_unwalk();
+ }
+ else
+ {
+ do_cmd_walk_jump(pickup, disarm);
+ }
+}
+
+
+void do_cmd_run_run()
+{
+ int dir;
+
+
+ /* Hack -- no running when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Hack -- Set the run counter */
+ running = (command_arg ? command_arg : 1000);
+
+ /* First step */
+ run_step(dir);
+ }
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Start running.
+ */
+void do_cmd_run(void)
+{
+ if (p_ptr->immovable)
+ {
+ return;
+ }
+ else
+ {
+ do_cmd_run_run();
+ }
+}
+
+
+
+/*
+ * Stay still. Search. Enter stores.
+ * Pick up treasure if "pickup" is true.
+ */
+void do_cmd_stay(int pickup)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Spontaneous Searching */
+ if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos)))
+ {
+ search();
+ }
+
+ /* Continuous Searching */
+ if (p_ptr->searching)
+ {
+ search();
+ }
+
+
+ /* Handle "objects" */
+ carry(pickup);
+
+
+ /* Hack -- enter a store if we are on one */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- enter store */
+ command_new = '_';
+ }
+}
+
+/*
+ * Resting allows a player to safely restore his hp -RAK-
+ */
+void do_cmd_rest(void)
+{
+ /* Can't rest on a Void Jumpgate -- too dangerous */
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN)
+ {
+ /* 'R&\n' is one of our favourite macros, so we have to do this */
+ if (flush_failure) flush();
+
+ /* Tell the player why */
+ msg_print(format("Resting on a %s is too dangerous!",
+ f_name + f_info[cave[p_ptr->py][p_ptr->px].feat].name));
+
+ /* Done */
+ return;
+ }
+
+ /* Can't rest while undead, it would mean dying */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ /* 'R&\n' is one of our favourite macros, so we have to do this */
+ if (flush_failure) flush();
+
+ /* Tell the player why */
+ msg_print("Resting is impossible while undead!");
+
+ /* Done */
+ return;
+ }
+
+ /* Prompt for time if needed */
+ if (command_arg <= 0)
+ {
+ cptr p = "Rest (0-9999, '*' for HP/SP, '&' as needed): ";
+
+ char out_val[80];
+
+ /* Default */
+ strcpy(out_val, "&");
+
+ /* Ask for duration */
+ if (!get_string(p, out_val, 4)) return;
+
+ /* Rest until done */
+ if (out_val[0] == '&')
+ {
+ command_arg = ( -2);
+ }
+
+ /* Rest a lot */
+ else if (out_val[0] == '*')
+ {
+ command_arg = ( -1);
+ }
+
+ /* Rest some */
+ else
+ {
+ command_arg = atoi(out_val);
+ if (command_arg <= 0) return;
+ }
+ }
+
+
+ /* Paranoia */
+ if (command_arg > 9999) command_arg = 9999;
+
+
+ /* Take a turn XXX XXX XXX (?) */
+ energy_use = 100;
+
+ /* Save the rest code */
+ resting = command_arg;
+
+ /* Cancel searching */
+ p_ptr->searching = FALSE;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+
+
+
+
+/*
+ * Determines the odds of an object breaking when thrown at a monster
+ *
+ * Note that artifacts never break, see the "drop_near()" function.
+ */
+int breakage_chance(object_type *o_ptr)
+{
+ int reducer =
+ 1 + ((get_skill(SKILL_ARCHERY)) ? (get_skill_scale(SKILL_ARCHERY, 10)) : 0);
+
+ /* Examine the item type */
+ switch (o_ptr->tval)
+ {
+ /* Always break */
+ case TV_FLASK:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_BOTTLE:
+ case TV_FOOD:
+ {
+ return (100);
+ }
+
+ /* Often break */
+ case TV_LITE:
+ case TV_SCROLL:
+ case TV_SKELETON:
+ {
+ return (50);
+ }
+
+ case TV_ARROW:
+ {
+ return (50 / reducer);
+ }
+
+ /* Sometimes break */
+ case TV_WAND:
+ case TV_SPIKE:
+ {
+ return (25);
+ }
+
+ case TV_SHOT:
+ case TV_BOLT:
+ {
+ return (25 / reducer);
+ }
+ case TV_BOOMERANG:
+ {
+ return 1;
+ }
+ }
+
+ /* Rarely break */
+ return (10);
+}
+
+/*
+ * Return multiplier of an object
+ */
+int get_shooter_mult(object_type *o_ptr)
+{
+ /* Assume a base multiplier */
+ int tmul = 1;
+
+ /* Analyze the launcher */
+ switch (o_ptr->sval)
+ {
+ case SV_SLING:
+ {
+ /* Sling and ammo */
+ tmul = 2;
+ break;
+ }
+
+ case SV_SHORT_BOW:
+ {
+ /* Short Bow and Arrow */
+ tmul = 2;
+ break;
+ }
+
+ case SV_LONG_BOW:
+ {
+ /* Long Bow and Arrow */
+ tmul = 3;
+ break;
+ }
+
+ /* Light Crossbow and Bolt */
+ case SV_LIGHT_XBOW:
+ {
+ tmul = 3;
+ break;
+ }
+
+ /* Heavy Crossbow and Bolt */
+ case SV_HEAVY_XBOW:
+ {
+ tmul = 4;
+ break;
+ }
+ }
+ return tmul;
+}
+
+
+/*
+ * Fire an object from the pack or floor.
+ *
+ * You may only fire items that "match" your missile launcher.
+ *
+ * You must use slings + pebbles/shots, bows + arrows, xbows + bolts.
+ *
+ * See "calc_bonuses()" for more calculations and such.
+ *
+ * Note that "firing" a missile is MUCH better than "throwing" it.
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Objects are more likely to break if they "attempt" to hit a monster.
+ *
+ * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots.
+ *
+ * The "extra shot" code works by decreasing the amount of energy
+ * required to make each shot, spreading the shots out over time.
+ *
+ * Note that when firing missiles, the launcher multiplier is applied
+ * after all the bonuses are added in, making multipliers very useful.
+ *
+ * Note that Bows of "Extra Might" get extra range and an extra bonus
+ * for the damage multiplier.
+ *
+ * Note that Bows of "Extra Shots" give an extra shot.
+ */
+void do_cmd_fire(void)
+{
+ int dir, item;
+
+ int j, y, x, ny, nx, ty, tx, by, bx;
+
+ int oldtdam, tdam, tdis, thits, tmul;
+
+ int bonus, chance;
+
+ int cur_dis, visible;
+
+ int breakage = -1, num_pierce = 0;
+
+ s32b special = 0;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ object_type *j_ptr;
+
+ bool hit_body = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ cptr q, s;
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+
+ /* Get the "bow" (if any) */
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ /* Require a launcher */
+ if (!j_ptr->tval)
+ {
+ msg_print("You have nothing with which to fire.");
+ return;
+ }
+
+ /* XXX HACK */
+ if (j_ptr->tval == TV_INSTRUMENT)
+ {
+ msg_print("You cannot fire with an instrument.");
+ return;
+ }
+
+#if 0 /* Old code without the quiver slot */
+
+ /* Require proper missile */
+ item_tester_tval = p_ptr->tval_ammo;
+
+ /* Get an item */
+ q = "Fire which item? ";
+ s = "You have nothing to fire.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+
+ /* Access the item (if in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+#else /* New code with the quiver slot */
+
+ /* Get the "ammo" (if any) */
+ o_ptr = &p_ptr->inventory[INVEN_AMMO];
+
+ item = INVEN_AMMO;
+
+ /* If nothing correct try to choose from the backpack */
+ if ((p_ptr->tval_ammo != o_ptr->tval) || (!o_ptr->k_idx))
+ {
+ /* Require proper missile */
+ item_tester_tval = p_ptr->tval_ammo;
+
+ /* Get an item */
+ q = "Your quiver is empty. Fire which item? ";
+ s = "You have nothing to fire.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+
+ /* Access the item (if in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ }
+
+#endif
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Reduce and describe p_ptr->inventory */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Reduce and describe floor item */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_optimize(0 - item);
+ }
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+
+ /* Sound */
+ sound(SOUND_SHOOT);
+
+
+ /* Describe the object */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+
+ /* Use the proper number of shots */
+ thits = p_ptr->num_fire;
+
+ /* Use a base distance */
+ tdis = 10;
+
+ /* Base damage from thrown object plus launcher bonus */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + j_ptr->to_d;
+
+ /* Actually "fire" the object */
+ bonus = (p_ptr->to_h + p_ptr->to_h_ranged + q_ptr->to_h + j_ptr->to_h);
+
+ chance = (p_ptr->skill_thb + (bonus * BTH_PLUS_ADJ));
+ if (chance < 5) chance = 5;
+
+ tmul = get_shooter_mult(j_ptr);
+
+ /* Get extra "power" from "extra might" */
+ tmul += p_ptr->xtra_might;
+
+ /* Boost the damage */
+ tdam *= tmul;
+
+ /* Add in the player damage */
+ tdam += p_ptr->to_d_ranged;
+
+ /* Base range */
+ tdis = 10 + 5 * tmul;
+
+
+ /* Take a (partial) turn */
+ energy_use = (100 / thits);
+
+ /* piercing shots ? */
+ if (p_ptr->use_piercing_shots &&
+ ((get_skill(SKILL_BOW) > 25) || (get_skill(SKILL_XBOW) > 25) ||
+ (get_skill(SKILL_SLING) > 25)))
+ {
+ num_pierce = (get_skill(SKILL_COMBAT) / 10) - 1;
+ num_pierce = (num_pierce < 0) ? 0 : num_pierce;
+ }
+
+ /* Start at the player */
+ by = p_ptr->py;
+ bx = p_ptr->px;
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ oldtdam = tdam;
+ while (TRUE)
+ {
+ /* Reset after a piercing shot */
+ tdam = oldtdam;
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, by, bx, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx)) break;
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+
+
+ /* Monster here, Try to hit it */
+ if (cave[y][x].m_idx)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx,
+ m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+
+ /* Anger friends */
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ {
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ }
+ case 0:
+ {
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Exploding arrow ? */
+ if (q_ptr->pval2 != 0)
+ {
+ int rad = 0, dam =
+ (damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d) * 2;
+ int flag =
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL |
+ PROJECT_JUMP;
+ switch (q_ptr->sval)
+ {
+ case SV_AMMO_LIGHT:
+ rad = 2;
+ dam /= 2;
+ break;
+ case SV_AMMO_NORMAL:
+ rad = 3;
+ break;
+ case SV_AMMO_HEAVY:
+ rad = 4;
+ dam *= 2;
+ break;
+ }
+
+ project(0, rad, y, x, dam, q_ptr->pval2, flag);
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(q_ptr) : 0);
+
+ /* Break ? */
+ if ((q_ptr->pval2 != 0) || (rand_int(100) < j))
+ {
+ breakage = 100;
+ break;
+ }
+
+ /* If the ammo doesn't break, it can pierce through */
+ if ((num_pierce) && (hit_body) &&
+ (magik(45 + get_skill(SKILL_ARCHERY))))
+ {
+ num_pierce--;
+ hit_body = FALSE;
+
+ /* If target isn't reached, continue moving to target */
+ if ( !((tx < x && x < bx) || (bx < x && x < tx)) &&
+ !((ty < y && y < by) || (by < y && y < ty)))
+ {
+ /* Continue moving in same direction if we reached the target */
+ int dx = tx - bx;
+ int dy = ty - by;
+ tx = x + 99 * dx;
+ ty = y + 99 * dy;
+
+ /* New base location */
+ by = y;
+ bx = x;
+ }
+
+ msg_format("The %s pierces through!", o_name);
+ }
+ else
+ break;
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(q_ptr, breakage, y, x);
+}
+
+
+/*
+ * Why is this here? even if it's temporary boost...
+ * Moved into player_type, hoping it might be useful in future extensions
+ * -- pelpel
+ */
+/* int throw_mult = 1; */
+
+/*
+ * Throw an object from the pack or floor.
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ */
+void do_cmd_throw(void)
+{
+ int dir, item;
+
+ s32b special = 0;
+
+ int j, y, x, ny, nx, ty, tx;
+
+ int chance, tdam, tdis;
+
+ int mul, div;
+
+ int boulder_add = 0;
+ int boulder_mult = 0;
+
+ int cur_dis, visible;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ bool hit_body = FALSE;
+
+ bool hit_wall = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Get an item */
+ q = "Throw which item? ";
+ s = "You have nothing to throw.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Access the item (if in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack - Cannot throw away 'no drop' cursed items */
+ if (cursed_p(o_ptr) && (f4 & TR4_CURSE_NO_DROP))
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to throw it.");
+
+ /* Nope */
+ return;
+ }
+
+ /* Boulder throwing */
+ if ((o_ptr->tval == TV_JUNK) && (o_ptr->sval == SV_BOULDER) && (get_skill(SKILL_BOULDER)))
+ {
+ boulder_add = get_skill_scale(SKILL_BOULDER, 80);
+ boulder_mult = get_skill_scale(SKILL_BOULDER, 6);
+ }
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /*
+ * Hack -- If rods or wands are thrown, the total maximum timeout or
+ * charges need to be allocated between the two stacks.
+ */
+ if ((o_ptr->tval == TV_WAND))
+ {
+ q_ptr->pval = o_ptr->pval / o_ptr->number;
+
+ if (o_ptr->number > 1) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Reduce and describe p_ptr->inventory */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Reduce and describe floor item */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_optimize(0 - item);
+ }
+
+
+ /* Description */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+ /* Extract a "distance multiplier" */
+ /* Changed for 'launcher' corruption */
+ mul = 10 + (2 * (p_ptr->throw_mult - 1)) + (2 * boulder_mult);
+
+ /* Enforce a minimum "weight" of one pound */
+ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10);
+
+ /* Hack -- Distance -- Reward strength, penalize weight */
+ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div;
+
+ /* Max distance of 10-18 */
+ if (tdis > mul) tdis = mul;
+
+ /* Hack -- Base damage from thrown object */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + boulder_add;
+ tdam *= p_ptr->throw_mult + boulder_mult;
+
+ /* Chance of hitting - adjusted for Weaponmasters -- Gumby */
+ chance = (p_ptr->skill_tht + (p_ptr->to_h * BTH_PLUS_ADJ));
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Start at the player */
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx))
+ {
+ hit_wall = TRUE;
+ break;
+ }
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+
+
+ /* Monster here, Try to hit it */
+ if (cave[y][x].m_idx)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, o_ptr->sval == SV_BOULDER ? SKILL_BOULDER : SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Anger friends */
+ if (!(k_info[q_ptr->k_idx].tval == TV_POTION))
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(q_ptr) : 0);
+
+ /* Potions smash open */
+ if (k_info[q_ptr->k_idx].tval == TV_POTION)
+ {
+ if ((hit_body) || (hit_wall) || (randint(100) < j))
+ {
+ /* Message */
+ msg_format("The %s shatters!", o_name);
+
+ if (potion_smash_effect(0, y, x, q_ptr->sval))
+ {
+ if (cave[y][x].m_idx)
+ {
+ char m_name[80];
+ monster_desc(m_name, &m_list[cave[y][x].m_idx], 0);
+ switch (is_friend(&m_list[cave[y][x].m_idx]))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(&m_list[cave[y][x].m_idx]);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_list[cave[y][x].m_idx].status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ else
+ {
+ j = 0;
+ }
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(q_ptr, j, y, x);
+}
+
+
+/*
+ * Throw a boomerang object from the equipement(bow).
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ */
+void do_cmd_boomerang(void)
+{
+ int dir;
+
+ int j, y, x, ny, nx, ty, tx;
+
+ int chance, tdam, tdis;
+
+ int mul, div;
+
+ int cur_dis, visible;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ bool hit_body = FALSE;
+
+ bool hit_wall = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ s32b special = 0;
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+
+ /* Get the "bow" (if any) */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Description */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+ /* Extract a "distance multiplier" */
+ /* Changed for 'launcher' corruption */
+ mul = 10 + 2 * (p_ptr->throw_mult - 1);
+
+ /* Enforce a minimum "weight" of one pound */
+ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10);
+
+ /* Hack -- Distance -- Reward strength, penalize weight */
+ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div;
+
+ /* Max distance of 10-18 */
+ if (tdis > mul) tdis = mul;
+
+ /* Hack -- Base damage from thrown object */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d;
+ tdam *= p_ptr->throw_mult;
+
+ /* Chance of hitting */
+ chance =
+ (p_ptr->skill_tht +
+ ((p_ptr->to_h + p_ptr->to_h_ranged) * BTH_PLUS_ADJ));
+
+ chance += get_skill(SKILL_BOOMERANG);
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Start at the player */
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx))
+ {
+ hit_wall = TRUE;
+ break;
+ }
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+
+
+ /* Monster here, Try to hit it */
+ if (cave[y][x].m_idx)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Anger friends */
+ if (!(k_info[q_ptr->k_idx].tval == TV_POTION))
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(o_ptr) : 0);
+
+ /* Break the boomerang */
+ if (!(o_ptr->art_name || artifact_p(o_ptr)) &&
+ (rand_int(100) < j))
+ {
+ msg_print(format("Your %s is destroyed.", o_name));
+ inven_item_increase(INVEN_BOW, -1);
+ inven_item_optimize(INVEN_BOW);
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Travel back to the player */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == p_ptr->py) && (x == p_ptr->px)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, ty, tx, p_ptr->py, p_ptr->px);
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ }
+}
+
+
+/*
+ * Try to ``walk'' using phase door.
+ */
+void do_cmd_unwalk()
+{
+ int dir, y, x, feat;
+
+ cave_type *c_ptr;
+
+ bool more = FALSE;
+
+
+ if (!get_rep_dir(&dir)) return;
+
+#if 0 /* No more, but there are penalities */
+
+ /* A mold can't blink in small scale mode */
+ if (p_ptr->wild_mode)
+ {
+ msg_print("You cannot move in the overview display.");
+ return;
+ }
+
+#endif
+
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ c_ptr = &cave[y][x];
+ feat = c_ptr->feat;
+
+ /* Must have knowledge to know feature XXX XXX */
+ if (!(c_ptr->info & (CAVE_MARK))) feat = FEAT_NONE;
+
+ /* Take a turn */
+ energy_use = 100;
+ energy_use *= (p_ptr->wild_mode) ? (5 * (MAX_HGT + MAX_WID) / 2) : 1;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+
+ /* Attack monsters */
+ if (c_ptr->m_idx > 0)
+ {
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Exit the area */
+ else if ((!dun_level) && (!p_ptr->wild_mode) &&
+ ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1)))
+ {
+ /* Can the player enter the grid? */
+ if (player_can_enter(c_ptr->mimic))
+ {
+ /* Hack: move to new area */
+ if ((y == 0) && (x == 0))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == 0) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == 0))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x--;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = cur_wid - 2;
+ ambush_flag = FALSE;
+ }
+
+ else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1))
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->wilderness_x++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = 1;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == 0)
+ {
+ p_ptr->wilderness_y--;
+ p_ptr->oldpy = cur_hgt - 2;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (y == cur_hgt - 1)
+ {
+ p_ptr->wilderness_y++;
+ p_ptr->oldpy = 1;
+ p_ptr->oldpx = x;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == 0)
+ {
+ p_ptr->wilderness_x--;
+ p_ptr->oldpx = cur_wid - 2;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ else if (x == cur_wid - 1)
+ {
+ p_ptr->wilderness_x++;
+ p_ptr->oldpx = 1;
+ p_ptr->oldpy = y;
+ ambush_flag = FALSE;
+ }
+
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+ }
+
+ /* Hack -- Ignore weird terrain types. */
+ else if (!cave_floor_grid(c_ptr))
+ {
+ teleport_player(10);
+ }
+
+ /* Enter quests */
+ else if (((feat >= FEAT_QUEST_ENTER) && (feat <= FEAT_QUEST_UP)) ||
+ ((feat >= FEAT_LESS) && (feat <= FEAT_MORE)))
+ {
+ move_player(dir, always_pickup, TRUE);
+ more = FALSE;
+ }
+
+ /* Hack -- Ignore wilderness mofe. */
+ else if (p_ptr->wild_mode)
+ {
+ /* Chance to not blink right */
+ if (magik(15))
+ {
+ do
+ {
+ dir = rand_range(1, 9);
+ }
+ while (dir == 5);
+ }
+
+ move_player(dir, always_pickup, TRUE);
+ }
+
+ /* Walking semantics */
+ else
+ {
+ teleport_player_directed(10, dir);
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0, 0);
+}
+
+
+static bool tport_vertically(bool how)
+{
+ /* arena or quest -KMW- */
+ if ((p_ptr->inside_arena) || (p_ptr->inside_quest))
+ {
+ msg_print("There is no effect.");
+ return (FALSE);
+ }
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerful force prevents you from teleporting.");
+ return FALSE;
+ }
+
+ /* Go down */
+ if (how)
+ {
+ if (dun_level >= d_info[dungeon_type].maxdepth)
+ {
+ msg_print("The floor is impermeable.");
+ return (FALSE);
+ }
+
+ msg_print("You sink through the floor.");
+ dun_level++;
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ if (dun_level < d_info[dungeon_type].mindepth)
+ {
+ msg_print("There is nothing above you but air.");
+ return (FALSE);
+ }
+
+ msg_print("You rise through the ceiling.");
+ dun_level--;
+ p_ptr->leaving = TRUE;
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Do a special ``movement'' action. Meant to be used for ``immovable''
+ * characters.
+ */
+void do_cmd_immovable_special(void)
+{
+ int i, ii, ij, dir;
+
+ int foo = p_ptr->immov_cntr;
+
+ int lose_sp = 0;
+
+ int lose_hp = 0;
+
+ bool did_act = FALSE;
+
+ bool did_load = FALSE;
+
+
+ if (foo > 1)
+ {
+ if (p_ptr->csp > foo / 2)
+ {
+
+ msg_format("This will drain %d mana points!", foo / 2);
+ if (!get_check("Proceed? ")) return;
+
+ lose_sp = foo / 2;
+
+ }
+ else if (p_ptr->chp > foo / 2)
+ {
+
+ msg_format("Warning: This will drain %d hit points!", foo / 2);
+ if (!get_check("Proceed? ")) return;
+
+ lose_hp = foo / 2;
+
+ }
+ else
+ {
+ msg_print("You can't use your powers yet.");
+ return;
+ }
+ }
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Do what special action:", 2, 0);
+
+ /* Give some choices */
+ prt("(a) Teleport to a specific place.", 4, 5);
+ prt("(b) Fetch an item.", 5, 5);
+ prt("(c) Go up 50'", 6, 5);
+ prt("(d) Go down 50'", 7, 5);
+
+ /* Prompt */
+ prt("Command: ", 9, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Tele-to */
+ if (i == 'a')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!tgt_pt(&ii, &ij)) break;
+
+ /* Teleport to the target */
+ teleport_player_to(ij, ii);
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Fetch item */
+ else if (i == 'b')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!get_aim_dir(&dir)) return;
+ fetch(dir, p_ptr->lev * 15, FALSE);
+ py_pickup_floor(always_pickup);
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Move up */
+ else if (i == 'c')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!tport_vertically(FALSE)) return;
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Move down */
+ else if (i == 'd')
+ {
+ Term_load();
+ character_icky = FALSE;
+ did_load = TRUE;
+
+ if (!tport_vertically(TRUE)) return;
+
+ did_act = TRUE;
+ break;
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ }
+
+ /* Check if screen was restored before */
+ if (!did_load)
+ {
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+ }
+
+ /* Apply stat losses if something was done */
+ if (did_act)
+ {
+ p_ptr->immov_cntr += 101 - (p_ptr->lev * 2);
+
+ if (lose_sp)
+ {
+ p_ptr->csp -= lose_sp;
+ p_ptr->redraw |= (PR_MANA);
+ }
+
+ if (lose_hp)
+ {
+ p_ptr->chp -= lose_hp;
+ p_ptr->redraw |= (PR_HP);
+ }
+
+ energy_use = 100;
+ }
+}
+
+/* Can we sacrifice it ? */
+static bool item_tester_hook_sacrifiable(object_type *o_ptr)
+{
+ GOD(GOD_MELKOR)
+ {
+ /* Corpses are */
+ if (o_ptr->tval == TV_CORPSE && o_ptr->sval == SV_CORPSE_CORPSE)
+ return (TRUE);
+
+ /* Books without any udun spells */
+ if ((o_ptr->tval == TV_BOOK) && (exec_lua(format("return udun_in_book(%d, %d)", o_ptr->sval, o_ptr->pval)) == 0))
+ return TRUE;
+ }
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * Handle sacrifices.
+ * Grace is increased by value of sacrifice.
+ */
+void do_cmd_sacrifice(void)
+{
+ byte on_what = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Check valididty */
+ if ((on_what < FEAT_ALTAR_HEAD) || (on_what > FEAT_ALTAR_TAIL))
+ {
+ show_god_info(FALSE);
+ return;
+ }
+ else
+ {
+ int agod = on_what - FEAT_ALTAR_HEAD + 1;
+
+ /* Not worshipping a god ? ahhhh! */
+ GOD(GOD_NONE)
+ {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ if (deity_info[agod].desc[i] != NULL)
+ msg_print(deity_info[agod].desc[i]);
+ }
+ if (get_check(format("Do you want to worship %s? ", deity_info[agod].name)))
+ {
+ follow_god(agod, FALSE);
+ p_ptr->grace = -200;
+ inc_piety(p_ptr->pgod, 0);
+ }
+ }
+ else if (p_ptr->pgod == agod)
+ {
+ GOD(GOD_MELKOR)
+ {
+ /* One can sacrifice some HP for piety or damage */
+ if ((p_ptr->mhp > 10) && (p_ptr->chp > 10) && get_check("Do you want to sacrifice a part of yourself? "))
+ {
+ /* 10 HP = 300 * wis piety */
+ if (get_check("Do you want to sacrifice for more piety instead of damage? "))
+ {
+ int x = wisdom_scale(6);
+ if (x < 1) x = 1;
+
+ p_ptr->hp_mod -= 10;
+ take_hit(10, "self sacrifice to Melkor");
+ msg_print("Your life slips away, and Melkor seems happier.");
+ inc_piety(GOD_MELKOR, x * 300);
+ p_ptr->update |= (PU_HP);
+ }
+ /* 10 HP = +wis damage */
+ else
+ {
+ take_hit(10, "self sacrifice to Melkor");
+ msg_print("Your life slips away, and your arms grow stronger.");
+ p_ptr->melkor_sacrifice++;
+ p_ptr->update |= (PU_BONUS | PU_HP);
+ }
+ }
+ else
+ {
+ int item;
+ object_type *o_ptr;
+
+ /* Restrict choices to food */
+ item_tester_hook = item_tester_hook_sacrifiable;
+
+ /* Get an item */
+ if (!get_item(&item, "Sacrifice which item? ", "You have nothing to sacrifice.", (USE_INVEN))) return;
+ o_ptr = get_object(item);
+
+ /* Piety for corpses is based on monster level */
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ inc_piety(GOD_MELKOR, 2 * r_info[o_ptr->pval2].level);
+ }
+
+ /* In books it depends of the spell levels*/
+ if (o_ptr->tval == TV_BOOK)
+ {
+ int x = exec_lua(format("return levels_in_book(%d, %d)", o_ptr->sval, o_ptr->pval));
+
+ inc_piety(GOD_MELKOR, 2 * x);
+ }
+
+ /* Remove the item */
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ }
+ else
+ {
+ process_hooks(HOOK_SACRIFICE_GOD, "()", "");
+ }
+ }
+ }
+}
+
+
+/*
+ * scan_monst --
+ *
+ * Return a list of o_list[] indexes of items of the given monster
+ */
+bool scan_monst(int *items, int *item_num, int m_idx)
+{
+ int this_o_idx, next_o_idx;
+
+ int num = 0;
+
+
+ (*item_num) = 0;
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = m_list[m_idx].hold_o_idx; this_o_idx;
+ this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Accept this item */
+ items[num++] = this_o_idx;
+
+ /* XXX Hack -- Enforce limit */
+ if (num == 23) break;
+ }
+
+ /* Number of items */
+ (*item_num) = num;
+
+ /* Result */
+ return (num != 0);
+}
+
+
+/*
+ * Display a list of the items that the given monster carries.
+ */
+byte show_monster_inven(int m_idx, int *monst_list)
+{
+ int i, j, k, l;
+
+ int col, len, lim;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char tmp_val[80];
+
+ int out_index[23];
+
+ byte out_color[23];
+
+ char out_desc[23][80];
+
+ int monst_num;
+
+
+ /* Default length */
+ len = 79 - 50;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ /* Scan for objects on the monster */
+ (void)scan_monst(monst_list, &monst_num, m_idx);
+
+ /* Display the p_ptr->inventory */
+ for (k = 0, i = 0; i < monst_num; i++)
+ {
+ o_ptr = &o_list[monst_list[i]];
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Save the index */
+ out_index[k] = i;
+
+ /* Acquire p_ptr->inventory color */
+ out_color[k] = tval_to_attr[o_ptr->tval & 0x7F];
+
+ /* Save the object description */
+ strcpy(out_desc[k], o_name);
+
+ /* Find the predicted "line length" */
+ l = strlen(out_desc[k]) + 5;
+
+ /* Be sure to account for the weight */
+ if (show_weights) l += 9;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+
+ /* Advance to next "line" */
+ k++;
+ }
+
+ /* Find the column to start in */
+ col = (len > 76) ? 0 : (79 - len);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ /* Get the index */
+ i = monst_list[out_index[j]];
+
+ /* Get the item */
+ o_ptr = &o_list[i];
+
+ /* Clear the line */
+ prt("", j + 1, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ strnfmt(tmp_val, 80, "%c)", index_to_label(j));
+
+ /* Clear the line with the (possibly indented) index */
+ put_str(tmp_val, j + 1, col);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], j + 1, col + 3);
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ strnfmt(tmp_val, 80, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, j + 1, 71);
+ }
+ }
+
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", j + 1, col ? col - 2 : col);
+
+ return monst_num;
+}
+
+
+/*
+ * Steal an object from a monster
+ */
+void do_cmd_steal()
+{
+ int x, y, dir = 0, item = -1, k = -1;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr;
+
+ object_type *o_ptr, forge;
+
+ byte num = 0;
+
+ bool done = FALSE;
+
+ int monst_list[23];
+
+
+ /* Only works on adjacent monsters */
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("There is no monster there!");
+ return;
+ }
+
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* There were no non-gold items */
+ if (!m_ptr->hold_o_idx)
+ {
+ msg_print("That monster has no objects!");
+ return;
+ }
+
+ /* The monster is immune */
+ if (r_info[m_ptr->r_idx].flags7 & (RF7_NO_THEFT))
+ {
+ msg_print("The monster is guarding the treasures.");
+ return;
+ }
+
+ screen_save();
+
+ num = show_monster_inven(c_ptr->m_idx, monst_list);
+
+ /* Repeat until done */
+ while (!done)
+ {
+ char tmp_val[80];
+ char which = ' ';
+
+ /* Build the prompt */
+ strnfmt(tmp_val, 80, "Choose an item to steal (a-%c) or ESC:",
+ 'a' - 1 + num);
+
+ /* Show the prompt */
+ prt(tmp_val, 0, 0);
+
+ /* Get a key */
+ which = inkey();
+
+ /* Parse it */
+ switch (which)
+ {
+ case ESCAPE:
+ {
+ done = TRUE;
+
+ break;
+ }
+
+ default:
+ {
+ int ver;
+
+ /* Extract "query" setting */
+ ver = isupper(which);
+ which = tolower(which);
+
+ k = islower(which) ? A2I(which) : -1;
+ if (k < 0 || k >= num)
+ {
+ bell();
+
+ break;
+ }
+
+ /* Verify the item */
+ if (ver && !verify("Try", 0 - monst_list[k]))
+ {
+ done = TRUE;
+
+ break;
+ }
+
+ /* Accept that choice */
+ item = monst_list[k];
+ done = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ if (item != -1)
+ {
+ int chance;
+
+ chance = 40 - p_ptr->stat_ind[A_DEX];
+ chance +=
+ o_list[item].weight / (get_skill_scale(SKILL_STEALING, 19) + 1);
+ chance += get_skill_scale(SKILL_STEALING, 29) + 1;
+ chance -= (m_ptr->csleep) ? 10 : 0;
+ chance += m_ptr->level;
+
+ /* Failure check */
+ if (rand_int(chance) > 1 + get_skill_scale(SKILL_STEALING, 25))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Speed up because monsters are ANGRY when you try to thief them */
+ m_ptr->mspeed += 5;
+
+ screen_load();
+
+ msg_print("Oops! The monster is now really *ANGRY*!");
+
+ return;
+ }
+
+ /* Reconnect the objects list */
+ if (num == 1) m_ptr->hold_o_idx = 0;
+ else
+ {
+ if (k > 0) o_list[monst_list[k - 1]].next_o_idx = monst_list[k + 1];
+ if (k + 1 >= num) o_list[monst_list[k - 1]].next_o_idx = 0;
+ if (k == 0) m_ptr->hold_o_idx = monst_list[k + 1];
+ }
+
+ /* Rogues gain some xp */
+ if (PRACE_FLAGS(PR1_EASE_STEAL))
+ {
+ s32b max_point;
+
+ /* Max XP gained from stealing */
+ max_point = (o_list[item].weight / 2) + (m_ptr->level * 10);
+
+ /* Randomise it a bit, with half a max guaranteed */
+ gain_exp((max_point / 2) + (randint(max_point) / 2));
+
+ /* Allow escape */
+ if (get_check("Phase door?")) teleport_player(10);
+ }
+
+ /* Get the item */
+ o_ptr = &forge;
+
+ /* Special handling for gold */
+ if (o_list[item].tval == TV_GOLD)
+ {
+ /* Collect the gold */
+ p_ptr->au += o_list[item].pval;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ object_copy(o_ptr, &o_list[item]);
+
+ inven_carry(o_ptr, FALSE);
+ }
+
+ /* Delete it */
+ o_list[item].k_idx = 0;
+ }
+
+ screen_load();
+
+ /* Take a turn */
+ energy_use = 100;
+}
+
+
+/*
+ * Give an item to a monster
+ */
+void do_cmd_give()
+{
+ int dir, x, y;
+
+ cave_type *c_ptr;
+
+ cptr q, s;
+
+ int item;
+
+
+ /* Get a "repeated" direction */
+ if (!get_rep_dir(&dir)) return;
+
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* No monster in the way */
+ if (c_ptr->m_idx == 0)
+ {
+ msg_print("There is no monster there.");
+ return;
+ }
+
+ /* Get an item */
+ q = "What item do you want to offer? ";
+ s = "You have nothing to offer.";
+ if (!get_item(&item, q, s, USE_INVEN)) return;
+
+ /* Process hooks if there are any */
+ if (!process_hooks(HOOK_GIVE, "(d,d)", c_ptr->m_idx, item))
+ {
+ msg_print("The monster does not want your item.");
+ }
+
+ /* Take a turn, even if the offer is declined */
+ energy_use = 100;
+}
+
+
+/*
+ * Chat with a monster
+ */
+void do_cmd_chat()
+{
+ int dir, x, y;
+
+ cave_type *c_ptr;
+
+
+ /* Get a "repeated" direction */
+ if (!get_rep_dir(&dir)) return;
+
+ /* Get requested location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* No monster in the way */
+ if (c_ptr->m_idx == 0)
+ {
+ msg_print("There is no monster there.");
+ return;
+ }
+
+ /* Process hook if there are any */
+ if (!process_hooks(HOOK_CHAT, "(d)", c_ptr->m_idx))
+ {
+ msg_print("The monster does not want to chat.");
+ }
+
+ /* No energy spent */
+}
diff --git a/src/cmd3.c b/src/cmd3.c
new file mode 100644
index 00000000..65f431f1
--- /dev/null
+++ b/src/cmd3.c
@@ -0,0 +1,2531 @@
+/* File: cmd3.c */
+
+/* Purpose: Inventory commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Display p_ptr->inventory
+ */
+void do_cmd_inven(void)
+{
+#if 0
+ /* Broken */
+
+ int capacity_tester = 0;
+
+ int i = 0, j = 0;
+
+#endif
+
+ char out_val[160];
+
+
+ /* Note that we are in "p_ptr->inventory" mode */
+ command_wrk = FALSE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Hack -- show empty slots */
+ item_tester_full = TRUE;
+
+ /* Display the p_ptr->inventory */
+ show_inven(FALSE);
+
+ /* Hack -- hide empty slots */
+ item_tester_full = FALSE;
+
+#if 0
+
+ /* Broken */
+
+ /* Extract the current weight (in tenth pounds) */
+ j = calc_total_weight();
+
+ /* Extract the "weight limit" (in tenth pounds) */
+ i = weight_limit();
+
+ capacity_tester = i + (i / 10) - 1;
+
+ strnfmt(out_val, 160,
+ "Inventory: carrying %d.%d pounds (%d%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((capacity_tester) / 2));
+
+#else
+
+ {
+ s32b total_weight = calc_total_weight();
+
+ strnfmt(out_val, 160,
+ "Inventory: carrying %ld.%ld pounds (%ld%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((weight_limit()) / 2));
+ }
+
+#endif
+
+ /* Get a command */
+ prt(out_val, 0, 0);
+
+ /* Get a new command */
+ command_new = inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Process "Escape" */
+ if (command_new == ESCAPE)
+ {
+ /* Reset stuff */
+ command_new = 0;
+ }
+
+ /* Process normal keys */
+ else
+ {
+ /* Hack -- Use "display" mode */
+ command_see = TRUE;
+
+ /* Mega-Hack -- Don't disable keymaps for this key */
+ request_command_inven_mode = TRUE;
+ }
+}
+
+
+/*
+ * Display equipment
+ */
+void do_cmd_equip(void)
+{
+ char out_val[160];
+
+
+ /* Note that we are in "equipment" mode */
+ command_wrk = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Hack -- show empty slots */
+ item_tester_full = TRUE;
+
+ /* Display the equipment */
+ show_equip(FALSE);
+
+ /* Hack -- undo the hack above */
+ item_tester_full = FALSE;
+
+ /* Build a prompt */
+ {
+ s32b total_weight = calc_total_weight();
+
+ /* Build a prompt */
+ strnfmt(out_val, 160,
+ "Equipment: carrying %ld.%ld pounds (%ld%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((weight_limit()) / 2));
+ }
+
+ /* Get a command */
+ prt(out_val, 0, 0);
+
+ /* Get a new command */
+ command_new = inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Process "Escape" */
+ if (command_new == ESCAPE)
+ {
+ /* Reset stuff */
+ command_new = 0;
+ }
+
+ /* Process normal keys */
+ else
+ {
+ /* Enter "display" mode */
+ command_see = TRUE;
+
+ /* Mega-Hack -- Don't disable keymaps for this key */
+ request_command_inven_mode = TRUE;
+ }
+}
+
+
+/*
+ * The "wearable" tester
+ */
+static bool item_tester_hook_wear(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ int slot = wield_slot(o_ptr);
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Only one ultimate at a time */
+ if (f4 & TR4_ULTIMATE)
+ {
+ int i;
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *q_ptr = &p_ptr->inventory[i];
+
+ /* Extract the flags */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!q_ptr->k_idx) continue;
+
+ if (f4 & TR4_ULTIMATE) return (FALSE);
+ }
+ }
+
+ if ((slot < INVEN_WIELD) || ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_WIELD) && (p_ptr->melee_style != SKILL_MASTERY)))
+ return (FALSE);
+
+ /* Check for a usable slot */
+ if (slot >= INVEN_WIELD) return (TRUE);
+
+ /* Assume not wearable */
+ return (FALSE);
+}
+
+
+bool is_slot_ok(int slot)
+{
+ if ((slot >= INVEN_WIELD) && (slot < INVEN_TOTAL))
+ {
+ return (TRUE);
+ }
+ else
+ {
+ return (FALSE);
+ }
+}
+
+
+/*
+ * Wield or wear a single item from the pack or floor
+ */
+void do_cmd_wield(void)
+{
+ int item, slot, num = 1;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr, *i_ptr;
+
+ cptr act;
+
+ char o_name[80];
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Restrict the choices */
+ item_tester_hook = item_tester_hook_wear;
+
+ /* Get an item */
+ q = "Wear/Wield which item? ";
+ s = "You have nothing you can wear or wield.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Check the slot */
+ slot = wield_slot(o_ptr);
+
+ /* Prevent wielding into a cursed slot */
+ if (cursed_p(&p_ptr->inventory[slot]))
+ {
+ /* Describe it */
+ object_desc(o_name, &p_ptr->inventory[slot], FALSE, 0);
+
+ /* Message */
+ msg_format("The %s you are %s appears to be cursed.",
+ o_name, describe_use(slot));
+
+ /* Cancel the command */
+ return;
+ }
+
+ if ((cursed_p(o_ptr)) && (wear_confirm)
+ && (object_known_p(o_ptr) || (o_ptr->ident & (IDENT_SENSE))))
+ {
+ char dummy[512];
+
+ /* Describe it */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ strnfmt(dummy, 512, "Really use the %s {cursed}? ", o_name);
+ if (!(get_check(dummy)))
+ return;
+ }
+
+ /* Can we wield */
+ if (process_hooks(HOOK_WIELD, "(d)", item)) return;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Two handed weapons can't be wielded with a shield */
+ if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) &&
+ (f4 & TR4_MUST2H) &&
+ (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0))
+ {
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("You cannot wield your %s with a shield.", o_name);
+ return;
+ }
+
+ if (is_slot_ok(slot - INVEN_ARM + INVEN_WIELD))
+ {
+ i_ptr = &p_ptr->inventory[slot - INVEN_ARM + INVEN_WIELD];
+
+ /* Extract the flags */
+ object_flags(i_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Prevent shield from being put on if wielding 2H */
+ if ((f4 & TR4_MUST2H) && (i_ptr->k_idx) &&
+ (p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM))
+ {
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("You cannot wield your %s with a two-handed weapon.", o_name);
+ return;
+ }
+
+ if ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM) &&
+ (f4 & TR4_COULD2H))
+ {
+ if (!get_check("Are you sure you want to restrict your fighting? "))
+ {
+ return;
+ }
+ }
+ }
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) &&
+ (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0) &&
+ (f4 & TR4_COULD2H))
+ {
+ if (!get_check("Are you sure you want to use this weapon with a shield?"))
+ {
+ return;
+ }
+ }
+
+ /* Can we take off existing item */
+ if (slot != INVEN_AMMO)
+ {
+ if (p_ptr->inventory[slot].k_idx)
+ if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return;
+ }
+ else
+ {
+ if (p_ptr->inventory[slot].k_idx)
+ if (!object_similar(&p_ptr->inventory[slot], o_ptr))
+ if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ if (slot == INVEN_AMMO) num = o_ptr->number;
+
+ /* Modify quantity */
+ q_ptr->number = num;
+
+ /* Decrease the item (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -num);
+ inven_item_optimize(item);
+ }
+
+ /* Decrease the item (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -num);
+ floor_item_optimize(0 - item);
+ }
+
+ /* Access the wield slot */
+ o_ptr = &p_ptr->inventory[slot];
+
+ /* Take off existing item */
+ if (slot != INVEN_AMMO)
+ {
+ if (o_ptr->k_idx)
+ {
+ /* Take off existing item */
+ (void)inven_takeoff(slot, 255, FALSE);
+ }
+ }
+ else
+ {
+ if (o_ptr->k_idx)
+ {
+ if (!object_similar(o_ptr, q_ptr))
+ {
+ /* Take off existing item */
+ (void)inven_takeoff(slot, 255, FALSE);
+ }
+ else
+ {
+ q_ptr->number += o_ptr->number;
+ }
+ }
+ }
+
+
+ /* Wear the new stuff */
+ object_copy(o_ptr, q_ptr);
+
+ /* Increment the equip counter by hand */
+ equip_cnt++;
+
+ /* Where is the item now */
+ if (slot == INVEN_WIELD)
+ {
+ act = "You are wielding";
+ }
+ else if (( slot == INVEN_BOW ) && (o_ptr->tval == TV_INSTRUMENT))
+ {
+ act = "You are holding";
+ }
+ else if (slot == INVEN_BOW)
+ {
+ act = "You are shooting with";
+ }
+ else if (slot == INVEN_LITE)
+ {
+ act = "Your light source is";
+ }
+ else if (slot == INVEN_AMMO)
+ {
+ act = "In your quiver you have";
+ }
+ else if (slot == INVEN_TOOL)
+ {
+ act = "You are using";
+ }
+ else
+ {
+ act = "You are wearing";
+ }
+
+ /* Describe the result */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
+
+ /* Cursed! */
+ if (cursed_p(o_ptr))
+ {
+ /* Warn the player */
+ msg_print("Oops! It feels deathly cold!");
+
+ /* Note the curse */
+ o_ptr->ident |= (IDENT_SENSE);
+ o_ptr->sense = SENSE_CURSED;
+ }
+
+ /* Take care of item sets */
+ if (o_ptr->name1)
+ {
+ wield_set(o_ptr->name1, a_info[o_ptr->name1].set, FALSE);
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA | PU_SPELLS);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_MH);
+
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+
+
+/*
+ * Take off an item
+ */
+void do_cmd_takeoff(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Take off which item? ";
+ s = "You are not wearing anything to take off.";
+ if (!get_item(&item, q, s, (USE_EQUIP))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Can we take it off */
+ if (process_hooks(HOOK_TAKEOFF, "(d)", item)) return;
+
+ /* Item is cursed */
+ if (cursed_p(o_ptr) && (!wizard))
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Take off the item */
+ (void)inven_takeoff(item, 255, FALSE);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ p_ptr->redraw |= (PR_MH);
+}
+
+
+/*
+ * Drop an item
+ */
+void do_cmd_drop(void)
+{
+ int item, amt = 1;
+
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Drop which item? ";
+ s = "You have nothing to drop.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can we drop */
+ if (process_hooks(HOOK_DROP, "(d)", item)) return;
+
+ /* Hack -- Cannot remove cursed items */
+ if (cursed_p(o_ptr))
+ {
+ if (item >= INVEN_WIELD)
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+ else
+ {
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to drop it.");
+
+ /* Nope */
+ return;
+ }
+ }
+ }
+
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Drop (some of) the item */
+ inven_drop(item, amt, p_ptr->py, p_ptr->px, FALSE);
+}
+
+
+/*
+ * Destroy an item
+ */
+void do_cmd_destroy(void)
+{
+ int item, amt = 1;
+
+ int old_number;
+
+ bool force = FALSE;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Hack -- force destruction */
+ if (command_arg > 0) force = TRUE;
+
+
+ /* Get an item */
+ q = "Destroy which item? ";
+ s = "You have nothing to destroy.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_AUTO))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+
+ /* Describe the object */
+ old_number = o_ptr->number;
+ o_ptr->number = amt;
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_ptr->number = old_number;
+
+ /* Verify unless quantity given */
+ if (!force)
+ {
+ if (!((auto_destroy) && (object_value(o_ptr) < 1)))
+ {
+ /* Make a verification */
+ strnfmt(out_val, 160, "Really destroy %s? ", o_name);
+ if (!get_check(out_val)) return;
+ }
+ }
+
+ /* Take no time, just like the automatizer */
+ energy_use = 0;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f4 & TR4_CURSE_NO_DROP) && cursed_p(o_ptr))
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to destroy it.");
+
+ /* Nope */
+ return;
+ }
+
+
+ /* Artifacts cannot be destroyed */
+ if (artifact_p(o_ptr) || o_ptr->art_name)
+ {
+ byte feel = SENSE_SPECIAL;
+
+ energy_use = 0;
+
+ /* Message */
+ msg_format("You cannot destroy %s.", o_name);
+
+ /* Hack -- Handle icky artifacts */
+ if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE;
+
+ /* Hack -- inscribe the artifact */
+ o_ptr->sense = feel;
+
+ /* We have "felt" it (again) */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ return;
+ }
+
+ /* Message */
+ msg_format("You destroy %s.", o_name);
+ sound(SOUND_DESTITEM);
+
+ /* Create an automatizer rule */
+ if (automatizer_create)
+ {
+ automatizer_add_rule(o_ptr, TRUE);
+ }
+
+#if 0 /* DGDGDGDG -- use a skill */
+ if (cp_ptr->magic_key == MKEY_TELEKINESIS)
+ {
+ /* Good merchants don't break anything... */
+ s32b value = object_value_real(o_ptr);
+
+ if (value < 0) value = -value;
+
+ /* ... otherwise they lose some experience */
+ value = value * amt / 10;
+ if (value == 0) value = 1;
+
+ lose_exp(value);
+ msg_print("Good merchants should not break anything...");
+ }
+#endif
+ /*
+ * Hack -- If rods or wand are destroyed, the total maximum timeout or
+ * charges of the stack needs to be reduced, unless all the items are
+ * being destroyed. -LM-
+ */
+ if ((o_ptr->tval == TV_WAND) && (amt < o_ptr->number))
+ {
+ o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Eru wont be happy */
+ if (f3 & TR3_BLESSED)
+ inc_piety(GOD_ERU, -10 * k_info[o_ptr->k_idx].level);
+
+ /* Eliminate the item (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Eliminate the item (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -amt);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+}
+
+
+/*
+ * Observe an item which has been *identify*-ed
+ */
+void do_cmd_observe(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Examine which item? ";
+ s = "You have nothing to examine.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ cmsg_format(TERM_L_BLUE, "%s", o_name);
+
+ /* Describe it fully */
+ if (!object_out_desc(o_ptr, NULL, FALSE, TRUE)) msg_print("You see nothing special.");
+}
+
+
+
+/*
+ * Remove the inscription from an object
+ * XXX Mention item (when done)?
+ */
+void do_cmd_uninscribe(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Un-inscribe which item? ";
+ s = "You have nothing to un-inscribe.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Nothing to remove */
+ if (!o_ptr->note)
+ {
+ msg_print("That item had no inscription to remove.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Inscription removed.");
+
+ /* Remove the incription */
+ o_ptr->note = 0;
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+}
+
+
+/*
+ * Inscribe an object with a comment
+ */
+void do_cmd_inscribe(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[80];
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Inscribe which item? ";
+ s = "You have nothing to inscribe.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Describe the activity */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("Inscribing %s.", o_name);
+ msg_print(NULL);
+
+ /* Start with nothing */
+ strcpy(out_val, "");
+
+ /* Use old inscription */
+ if (o_ptr->note)
+ {
+ /* Start with the old inscription */
+ strcpy(out_val, quark_str(o_ptr->note));
+ }
+
+ /* Get a new inscription (possibly empty) */
+ if (get_string("Inscription: ", out_val, 80))
+ {
+ /* Save the inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+}
+
+
+
+/*
+ * An "item_tester_hook" for refilling lanterns
+ */
+static bool item_tester_refill_lantern(object_type *o_ptr)
+{
+ /* Flasks of oil are okay */
+ if (o_ptr->tval == TV_FLASK) return (TRUE);
+
+ /* Lanterns are okay */
+ if ((o_ptr->tval == TV_LITE) &&
+ (o_ptr->sval == SV_LITE_LANTERN)) return (TRUE);
+
+ /* Assume not okay */
+ return (FALSE);
+}
+
+
+/*
+ * Refill the players lamp (from the pack or floor)
+ */
+static void do_cmd_refill_lamp(void)
+{
+ int item;
+
+ object_type *o_ptr;
+ object_type *j_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict the choices */
+ item_tester_hook = item_tester_refill_lantern;
+
+ /* Get an item */
+ q = "Refill with which flask? ";
+ s = "You have no flasks of oil.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Access the lantern */
+ j_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Refuel */
+ if (o_ptr->tval == TV_FLASK)
+ j_ptr->timeout += o_ptr->pval;
+ else
+ j_ptr->timeout += o_ptr->timeout;
+
+ /* Message */
+ msg_print("You fuel your lamp.");
+
+ /* Comment */
+ if (j_ptr->timeout >= FUEL_LAMP)
+ {
+ j_ptr->timeout = FUEL_LAMP;
+ msg_print("Your lamp is full.");
+ }
+
+ /* Decrease the item (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Decrease the item (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+}
+
+
+/*
+ * An "item_tester_hook" for refilling torches
+ */
+static bool item_tester_refill_torch(object_type *o_ptr)
+{
+ /* Torches are okay */
+ if ((o_ptr->tval == TV_LITE) &&
+ (o_ptr->sval == SV_LITE_TORCH)) return (TRUE);
+
+ /* Assume not okay */
+ return (FALSE);
+}
+
+
+/*
+ * Refuel the players torch (from the pack or floor)
+ */
+static void do_cmd_refill_torch(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ object_type *j_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict the choices */
+ item_tester_hook = item_tester_refill_torch;
+
+ /* Get an item */
+ q = "Refuel with which torch? ";
+ s = "You have no extra torches.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Access the primary torch */
+ j_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Refuel */
+ j_ptr->timeout += o_ptr->timeout + 5;
+
+ /* Message */
+ msg_print("You combine the torches.");
+
+ /* Over-fuel message */
+ if (j_ptr->timeout >= FUEL_TORCH)
+ {
+ j_ptr->timeout = FUEL_TORCH;
+ msg_print("Your torch is fully fueled.");
+ }
+
+ /* Refuel message */
+ else
+ {
+ msg_print("Your torch glows more brightly.");
+ }
+
+ /* Decrease the item (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Decrease the item (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+}
+
+
+/*
+ * Refill the players lamp, or restock his torches
+ */
+void do_cmd_refill(void)
+{
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Get the light */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* It is nothing */
+ if (o_ptr->tval != TV_LITE)
+ {
+ msg_print("You are not wielding a light.");
+ return;
+ }
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ /* It's a torch */
+ if (o_ptr->sval == SV_LITE_TORCH ||
+ o_ptr->sval == SV_LITE_TORCH_EVER)
+ {
+ do_cmd_refill_torch();
+ }
+
+ /* It's a lamp */
+ else if (o_ptr->sval == SV_LITE_LANTERN ||
+ o_ptr->sval == SV_LITE_DWARVEN ||
+ o_ptr->sval == SV_LITE_FEANORIAN)
+ {
+ do_cmd_refill_lamp();
+ }
+ }
+
+ /* No torch to refill */
+ else
+ {
+ msg_print("Your light cannot be refilled.");
+ }
+}
+
+
+/*
+ * Target command
+ */
+void do_cmd_target(void)
+{
+ /* Target set */
+ if (target_set(TARGET_KILL))
+ {
+ msg_print("Target Selected.");
+ }
+
+ /* Target aborted */
+ else
+ {
+ msg_print("Target Aborted.");
+ }
+}
+
+
+
+/*
+ * Look command
+ */
+void do_cmd_look(void)
+{
+ /* Look around */
+ if (target_set(TARGET_LOOK))
+ {
+ msg_print("Target Selected.");
+ }
+}
+
+
+
+/*
+ * Allow the player to examine other sectors on the map
+ */
+void do_cmd_locate(void)
+{
+ int dir, y1, x1, y2, x2;
+ int panel_hgt, panel_wid;
+ char tmp_val[80];
+ char out_val[160];
+
+
+ /* Retrieve size of the Angband window */
+ Term_get_size(&panel_wid, &panel_hgt);
+
+ /* Calcurate size of the dungeon map area */
+ panel_hgt = (panel_hgt - (ROW_MAP + 1)) / 2;
+ panel_wid = (panel_wid - (COL_MAP + 1)) / 2;
+
+ /* Start at current panel */
+ y2 = y1 = panel_row_min;
+ x2 = x1 = panel_col_min;
+
+ /* Show panels until done */
+ while (1)
+ {
+ /* Describe the location */
+ if ((y2 == y1) && (x2 == x1))
+ {
+ tmp_val[0] = '\0';
+ }
+ else
+ {
+ strnfmt(tmp_val, 80, "%s%s of",
+ ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""),
+ ((x2 < x1) ? " West" : (x2 > x1) ? " East" : ""));
+ }
+
+ /* Prepare to ask which way to look */
+ if ((panel_hgt == PANEL_HGT) && (panel_wid == PANEL_WID))
+ {
+ /* Avoid surprising the standard screen users */
+ strnfmt(out_val, 160,
+ "Map sector [%d,%d], which is%s your sector. Direction?",
+ y2 / panel_hgt, x2 / panel_wid, tmp_val);
+ }
+
+ /* Big screen */
+ else
+ {
+ /* Panels are measured by current map area size */
+ strnfmt(out_val, 160,
+ "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?",
+ y2 / panel_hgt, y2 % panel_hgt,
+ x2 / panel_wid, x2 % panel_wid, tmp_val);
+ }
+
+ /* Assume no direction */
+ dir = 0;
+
+ /* Get a direction */
+ while (!dir)
+ {
+ char ch;
+
+ /* Get a command (or cancel) */
+ if (!get_com(out_val, &ch)) break;
+
+ /* Extract the action (if any) */
+ dir = get_keymap_dir(ch);
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) break;
+
+ /* Apply the motion */
+ if (change_panel(ddy[dir], ddx[dir]))
+ {
+ y2 = panel_row_min;
+ x2 = panel_col_min;
+ }
+ }
+
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+}
+
+
+
+
+
+
+/*
+ * The table of "symbol info" -- each entry is a string of the form
+ * "X:desc" where "X" is the trigger, and "desc" is the "info".
+ */
+static cptr ident_info[] =
+{
+ " :A dark grid",
+ "!:A potion (or oil)",
+ "\":An amulet (or necklace)",
+ "#:A wall (or secret door)",
+ "$:Treasure (gold or gems)",
+ "%:A vein (magma or quartz)",
+ /* "&:unused", */
+ "':An open door",
+ "(:Soft armor",
+ "):A shield",
+ "*:A vein with treasure",
+ "+:A closed door",
+ ",:Food (or mushroom patch)",
+ "-:A wand (or rod)",
+ ".:Floor",
+ "/:A polearm (Axe/Pike/etc)",
+ "0:An altar",
+ "1:Entrance to General Store",
+ "2:Entrance to Armory",
+ "3:Entrance to Weaponsmith",
+ "4:Entrance to Temple",
+ "5:Entrance to Alchemy shop",
+ "6:Entrance to Magic store",
+ "7:Entrance to Black Market",
+ "8:Entrance to your home",
+ "9:Entrance to Bookstore",
+ "::Rubble",
+ ";:A glyph of warding / explosive rune",
+ "<:An up staircase",
+ "=:A ring",
+ ">:A down staircase",
+ "?:A scroll",
+ "@:You",
+ "A:Angel",
+ "B:Bird",
+ "C:Canine",
+ "D:Ancient Dragon/Wyrm",
+ "E:Elemental",
+ "F:Dragon Fly",
+ "G:Ghost",
+ "H:Hybrid",
+ "I:Insect",
+ "J:Snake",
+ "K:Killer Beetle",
+ "L:Lich",
+ "M:Multi-Headed Reptile",
+ /* "N:unused", */
+ "O:Ogre",
+ "P:Giant Humanoid",
+ "Q:Quylthulg (Pulsing Flesh Mound)",
+ "R:Reptile/Amphibian",
+ "S:Spider/Scorpion/Tick",
+ "T:Troll",
+ "U:Major Demon",
+ "V:Vampire",
+ "W:Wight/Wraith/etc",
+ "X:Xorn/Xaren/etc",
+ "Y:Yeti",
+ "Z:Zephyr Hound",
+ "[:Hard armor",
+ "\\:A hafted weapon (mace/whip/etc)",
+ "]:Misc. armor",
+ "^:A trap",
+ "_:A staff",
+ /* "`:unused", */
+ "a:Ant",
+ "b:Bat",
+ "c:Centipede",
+ "d:Dragon",
+ "e:Floating Eye",
+ "f:Feline",
+ "g:Golem",
+ "h:Hobbit/Elf/Dwarf",
+ "i:Icky Thing",
+ "j:Jelly",
+ "k:Kobold",
+ "l:Louse",
+ "m:Mold",
+ "n:Naga",
+ "o:Orc",
+ "p:Person/Human",
+ "q:Quadruped",
+ "r:Rodent",
+ "s:Skeleton",
+ "t:Townsperson",
+ "u:Minor Demon",
+ "v:Vortex",
+ "w:Worm/Worm-Mass",
+ /* "x:unused", */
+ "y:Yeek",
+ "z:Zombie/Mummy",
+ "{:A missile (arrow/bolt/shot)",
+ "|:An edged weapon (sword/dagger/etc)",
+ "}:A launcher (bow/crossbow/sling)",
+ "~:A tool (or miscellaneous item)",
+ NULL
+};
+
+
+
+/*
+ * Sorting hook -- Comp function -- see below
+ *
+ * We use "u" to point to array of monster indexes,
+ * and "v" to select the type of sorting to perform on "u".
+ */
+static bool ang_sort_comp_hook(vptr u, vptr v, int a, int b)
+{
+ u16b *who = (u16b*)(u);
+
+ u16b *why = (u16b*)(v);
+
+ int w1 = who[a];
+
+ int w2 = who[b];
+
+ int z1, z2;
+
+
+ /* Sort by player kills */
+ if (*why >= 4)
+ {
+ /* Extract player kills */
+ z1 = r_info[w1].r_pkills;
+ z2 = r_info[w2].r_pkills;
+
+ /* Compare player kills */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Sort by total kills */
+ if (*why >= 3)
+ {
+ /* Extract total kills */
+ z1 = r_info[w1].r_tkills;
+ z2 = r_info[w2].r_tkills;
+
+ /* Compare total kills */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Sort by monster level */
+ if (*why >= 2)
+ {
+ /* Extract levels */
+ z1 = r_info[w1].level;
+ z2 = r_info[w2].level;
+
+ /* Compare levels */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Sort by monster experience */
+ if (*why >= 1)
+ {
+ /* Extract experience */
+ z1 = r_info[w1].mexp;
+ z2 = r_info[w2].mexp;
+
+ /* Compare experience */
+ if (z1 < z2) return (TRUE);
+ if (z1 > z2) return (FALSE);
+ }
+
+
+ /* Compare indexes */
+ return (w1 <= w2);
+}
+
+
+/*
+ * Sorting hook -- Swap function -- see below
+ *
+ * We use "u" to point to array of monster indexes,
+ * and "v" to select the type of sorting to perform.
+ */
+static void ang_sort_swap_hook(vptr u, vptr v, int a, int b)
+{
+ u16b *who = (u16b*)(u);
+
+ u16b holder;
+
+
+ /* XXX XXX */
+ v = v ? v : 0;
+
+ /* Swap */
+ holder = who[a];
+ who[a] = who[b];
+ who[b] = holder;
+}
+
+
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race
+ */
+static void roff_top(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ byte a1, a2;
+
+ char c1, c2;
+
+
+ /* Access the chars */
+ c1 = r_ptr->d_char;
+ c2 = r_ptr->x_char;
+
+ /* Access the attrs */
+ a1 = r_ptr->d_attr;
+ a2 = r_ptr->x_attr;
+
+ /* Hack -- fake monochrome */
+ if (!use_color) a1 = TERM_WHITE;
+ if (!use_color) a2 = TERM_WHITE;
+
+
+ /* Clear the top line */
+ Term_erase(0, 0, 255);
+
+ /* Reset the cursor */
+ Term_gotoxy(0, 0);
+
+ /* A title (use "The" for non-uniques) */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ Term_addstr( -1, TERM_WHITE, "The ");
+ }
+
+ /* Dump the name */
+ Term_addstr( -1, TERM_WHITE, (r_name + r_ptr->name));
+
+ /* Append the "standard" attr/char info */
+ Term_addstr( -1, TERM_WHITE, " ('");
+ Term_addch(a1, c1);
+ if (use_bigtile && (a1 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "')");
+
+ /* Append the "optional" attr/char info */
+ Term_addstr( -1, TERM_WHITE, "/('");
+ Term_addch(a2, c2);
+ if (use_bigtile && (a2 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "'):");
+}
+
+
+/*
+ * Identify a character, allow recall of monsters
+ *
+ * Several "special" responses recall "multiple" monsters:
+ * ^A (all monsters)
+ * ^U (all unique monsters)
+ * ^N (all non-unique monsters)
+ * ^M (case insensitive name search)
+ *
+ * The responses may be sorted in several ways, see below.
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+void do_cmd_query_symbol(void)
+{
+ int i, n, r_idx;
+
+ char sym, query;
+
+ char buf[128];
+
+
+ bool all = FALSE;
+
+ bool uniq = FALSE;
+
+ bool norm = FALSE;
+
+
+ bool name = FALSE;
+
+ char temp[80] = "";
+
+
+ bool recall = FALSE;
+
+
+ u16b why = 0;
+
+ u16b *who;
+
+
+ /* Get a character, or abort */
+ if (!get_com("Enter character to be identified, "
+ "or (Ctrl-A, Ctrl-U, Ctrl-N, Ctrl-M):", &sym)) return;
+
+ /* Find that character info, and describe it */
+ for (i = 0; ident_info[i]; ++i)
+ {
+ if (sym == ident_info[i][0]) break;
+ }
+
+ /* Describe */
+ if (sym == KTRL('A'))
+ {
+ all = TRUE;
+ strcpy(buf, "Full monster list.");
+ }
+ else if (sym == KTRL('U'))
+ {
+ all = uniq = TRUE;
+ strcpy(buf, "Unique monster list.");
+ }
+ else if (sym == KTRL('N'))
+ {
+ all = norm = TRUE;
+ strcpy(buf, "Non-unique monster list.");
+ }
+ else if (sym == KTRL('M'))
+ {
+ all = name = TRUE;
+ if (!get_string("Name:", temp, 70)) return;
+ strnfmt(buf, 128, "Monsters with a name \"%s\"", temp);
+ strlower(temp);
+ }
+ else if (ident_info[i])
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2);
+ }
+ else
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol");
+ }
+
+ /* Display the result */
+ prt(buf, 0, 0);
+
+ /* Allocate the "who" array */
+ C_MAKE(who, max_r_idx, u16b);
+
+ /* Collect matching monsters */
+ for (n = 0, i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Nothing to recall */
+ if (!cheat_know && !r_ptr->r_sights) continue;
+
+ /* Require non-unique monsters if needed */
+ if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Require unique monsters if needed */
+ if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Require monsters with the name requested if needed */
+ if (name)
+ {
+ char mon_name[80];
+
+ strcpy(mon_name, r_name + r_ptr->name);
+ strlower(mon_name);
+
+ if (!strstr(mon_name, temp)) continue;
+ }
+
+ /* Collect "appropriate" monsters */
+ if (all || (r_ptr->d_char == sym)) who[n++] = i;
+ }
+
+ /* Nothing to recall */
+ if (!n)
+ {
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ return;
+ }
+
+
+ /* Prompt XXX XXX XXX */
+ put_str("Recall details? (k/p/y/n): ", 0, 40);
+
+ /* Query */
+ query = inkey();
+
+ /* Restore */
+ prt(buf, 0, 0);
+
+
+ /* Sort by kills (and level) */
+ if (query == 'k')
+ {
+ why = 4;
+ query = 'y';
+ }
+
+ /* Sort by level */
+ if (query == 'p')
+ {
+ why = 2;
+ query = 'y';
+ }
+
+ /* Catch "escape" */
+ if (query != 'y')
+ {
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ return;
+ }
+
+
+ /* Sort if needed */
+ if (why)
+ {
+ /* Select the sort method */
+ ang_sort_comp = ang_sort_comp_hook;
+ ang_sort_swap = ang_sort_swap_hook;
+
+ /* Sort the array */
+ ang_sort(who, &why, n);
+ }
+
+
+ /* Start at the end */
+ i = n - 1;
+
+ /* Scan the monster memory */
+ while (1)
+ {
+ /* Extract a race */
+ r_idx = who[i];
+
+ /* Hack -- Auto-recall */
+ monster_race_track(r_idx, 0);
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Hack -- Begin the prompt */
+ roff_top(r_idx);
+
+ /* Hack -- Complete the prompt */
+ Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]");
+
+ /* Interact */
+ while (1)
+ {
+ /* Recall */
+ if (recall)
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Recall on screen */
+ screen_roff(who[i], 0, 0);
+
+ /* Hack -- Complete the prompt (again) */
+ Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]");
+ }
+
+ /* Command */
+ query = inkey();
+
+ /* Unrecall */
+ if (recall)
+ {
+ /* Restore */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Normal commands */
+ if (query != 'r') break;
+
+ /* Toggle recall */
+ recall = !recall;
+ }
+
+ /* Stop scanning */
+ if (query == ESCAPE) break;
+
+ /* Move to "prev" monster */
+ if (query == '-')
+ {
+ if (++i == n)
+ {
+ i = 0;
+ if (!expand_list) break;
+ }
+ }
+
+ /* Move to "next" monster */
+ else
+ {
+ if (i-- == 0)
+ {
+ i = n - 1;
+ if (!expand_list) break;
+ }
+ }
+ }
+
+ /* Re-display the identity */
+ prt(buf, 0, 0);
+
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+}
+
+
+/*
+ * research_mon
+ * -KMW-
+ */
+bool research_mon()
+{
+ int i, n, r_idx;
+
+ char sym, query;
+
+ char buf[128];
+
+
+ s16b oldkills;
+
+ byte oldwake;
+
+ bool oldcheat;
+
+
+ bool all = FALSE;
+
+ bool uniq = FALSE;
+
+ bool norm = FALSE;
+
+ bool notpicked;
+
+
+ bool recall = FALSE;
+
+ u16b why = 0;
+
+ monster_race *r2_ptr;
+
+ u16b *who;
+
+
+ /* Hack -- Remember "cheat_know" flag */
+ oldcheat = cheat_know;
+
+
+ /* Get a character, or abort */
+ if (!get_com("Enter character of monster: ", &sym)) return (TRUE);
+
+ /* Allocate the "who" array */
+ C_MAKE(who, max_r_idx, u16b);
+
+ /* Find that character info, and describe it */
+ for (i = 0; ident_info[i]; ++i)
+ {
+ if (sym == ident_info[i][0]) break;
+ }
+
+ if (ident_info[i])
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2);
+ }
+ else
+ {
+ strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol");
+ }
+
+ /* Display the result */
+ prt(buf, 16, 10);
+
+
+ /* Collect matching monsters */
+ for (n = 0, i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Hack -- Force "cheat_know" */
+ cheat_know = TRUE;
+
+ /* Nothing to recall */
+ if (!cheat_know && !r_ptr->r_sights) continue;
+
+ /* Require non-unique monsters if needed */
+ if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Require unique monsters if needed */
+ if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue;
+
+ /* Collect "appropriate" monsters */
+ if (all || (r_ptr->d_char == sym)) who[n++] = i;
+ }
+
+ /* Nothing to recall */
+ if (!n)
+ {
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ /* Restore the "cheat_know" flag */
+ cheat_know = oldcheat;
+
+ return (TRUE);
+ }
+
+
+ /* Sort by level */
+ why = 2;
+ query = 'y';
+
+ /* Sort if needed */
+ if (why)
+ {
+ /* Select the sort method */
+ ang_sort_comp = ang_sort_comp_hook;
+ ang_sort_swap = ang_sort_swap_hook;
+
+ /* Sort the array */
+ ang_sort(who, &why, n);
+ }
+
+
+ /* Start at the end */
+ i = n - 1;
+
+ notpicked = TRUE;
+
+ /* Scan the monster memory */
+ while (notpicked)
+ {
+ /* Extract a race */
+ r_idx = who[i];
+
+ /* Hack -- Auto-recall */
+ monster_race_track(r_idx, 0);
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Hack -- Begin the prompt */
+ roff_top(r_idx);
+
+ /* Hack -- Complete the prompt */
+ Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC, space to continue]");
+
+ /* Interact */
+ while (1)
+ {
+ /* Recall */
+ if (recall)
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Recall on screen */
+ r2_ptr = &r_info[r_idx];
+
+ oldkills = r2_ptr->r_tkills;
+ oldwake = r2_ptr->r_wake;
+ screen_roff(who[i], 0, 1);
+ r2_ptr->r_tkills = oldkills;
+ r2_ptr->r_wake = oldwake;
+ r2_ptr->r_sights = 1;
+ cheat_know = oldcheat;
+ notpicked = FALSE;
+ break;
+
+ }
+
+ /* Command */
+ query = inkey();
+
+ /* Unrecall */
+ if (recall)
+ {
+ /* Restore */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Normal commands */
+ if (query != 'r') break;
+
+ /* Toggle recall */
+ recall = !recall;
+ }
+
+ /* Stop scanning */
+ if (query == ESCAPE) break;
+
+ /* Move to "prev" monster */
+ if (query == '-')
+ {
+ if (++i == n)
+ {
+ i = 0;
+ if (!expand_list) break;
+ }
+ }
+
+ /* Move to "next" monster */
+ else
+ {
+ if (i-- == 0)
+ {
+ i = n - 1;
+ if (!expand_list) break;
+ }
+ }
+ }
+
+
+ /* Re-display the identity */
+ /* prt(buf, 5, 5);*/
+
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, u16b);
+
+ /* Restore the "cheat_know" flag */
+ cheat_know = oldcheat;
+
+ return (notpicked);
+}
+
+
+/*
+ * Try to "sense" the grid's mana
+ */
+bool do_cmd_sense_grid_mana()
+{
+ int chance, i;
+
+
+ /* Take (a lot of) time */
+ energy_use = 200;
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* Hight mana grids are harder */
+ chance = chance - (cave[p_ptr->py][p_ptr->px].mana / 10);
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to sense the grid's mana.");
+ sound(SOUND_FAIL);
+ return FALSE;
+ }
+
+ /* Try to give an "average" value */
+ i = (101 - p_ptr->skill_dev) / 2;
+ i = (i < 1) ? 1 : (i > 50) ? 50 : i;
+
+ if (wizard)
+ {
+ msg_format("Grid's mana: %d.", cave[p_ptr->py][p_ptr->px].mana);
+ msg_format("Average grid's mana: %d.", (cave[p_ptr->py][p_ptr->px].mana / i) * i);
+ }
+ else
+ {
+ msg_format("Average Area's mana: %d", (cave[p_ptr->py][p_ptr->px].mana / i) * i);
+ }
+ return TRUE;
+}
+
+
+/*
+ * Calculate the weight of the portable holes
+ */
+s32b portable_hole_weight(void)
+{
+ s32b weight, i;
+
+ store_type *st_ptr = &town_info[TOWN_RANDOM].store[STORE_HOME];
+
+
+ /* Sum the objects in the appropriate home */
+ for (i = 0, weight = 0; i < st_ptr->stock_num; i++)
+ {
+ object_type *o_ptr = &st_ptr->stock[i];
+
+ weight += (o_ptr->weight * o_ptr->number);
+ }
+
+ /* Multiply the sum with 1.5 */
+ weight = (weight * 3) / 2 + 2;
+
+ return (weight);
+}
+
+
+/*
+ * Calculate and set the weight of the portable holes
+ */
+void set_portable_hole_weight(void)
+{
+ s32b weight, i, j;
+
+
+ /* Portable holes can be used only by merchants */
+#if 0 /* DGDGDGDG -- use a skill */
+ if (cp_ptr->magic_key == MKEY_TELEKINESIS) return;
+#endif
+
+ /* Calculate the weight of items in home */
+ weight = portable_hole_weight();
+
+ /* Set the weight of portable holes in the shops, ... */
+ for (i = 1; i < max_towns; i++)
+ {
+ for (j = 0; j < max_st_idx; j++)
+ {
+ store_type *st_ptr = &town_info[i].store[j];
+ int k;
+
+ for (k = 0; k < st_ptr->stock_num; k++)
+ {
+ object_type *o_ptr = &st_ptr->stock[k];
+
+ if ((o_ptr->tval == TV_TOOL) &&
+ (o_ptr->sval == SV_PORTABLE_HOLE))
+ o_ptr->weight = weight;
+ }
+ }
+ }
+
+ /* ... in the object list, ... */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ if ((o_ptr->tval == TV_TOOL) &&
+ (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight;
+ }
+
+ /* ... and in the p_ptr->inventory to the appropriate value */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if ((o_ptr->tval == TV_TOOL) &&
+ (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight;
+ }
+}
+
+
+/*
+ * Use a portable hole
+ */
+void do_cmd_portable_hole(void)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ int feat, special, town_num;
+
+
+ /* Portable holes can be used only by merchants */
+#if 0 /* DGDGDGDG -- use a skill */
+ if (cp_ptr->magic_key != MKEY_TELEKINESIS) return;
+#endif
+
+ /* Is it currently wielded? */
+ if (!p_ptr->inventory[INVEN_TOOL].k_idx ||
+ (p_ptr->inventory[INVEN_TOOL].tval != TV_TOOL) ||
+ (p_ptr->inventory[INVEN_TOOL].sval != SV_PORTABLE_HOLE))
+ {
+ /* No, it isn't */
+ msg_print("You have to wield a portable hole to use your abilities");
+ return;
+ }
+
+ /* Mega-hack: Saving the old values, and then... */
+ feat = c_ptr->feat;
+ special = c_ptr->special;
+ town_num = p_ptr->town_num;
+
+ /* ... change the current grid to the home in town #1 */
+ /* DG -- use the first random town, since random towns cannot have houses */
+ /*
+ * pelpel -- This doesn't affect LoS, so we can manipulate
+ * terrain feature without calling cave_set_feat()
+ */
+ c_ptr->feat = FEAT_SHOP;
+ c_ptr->special = STORE_HOME;
+ p_ptr->town_num = TOWN_RANDOM;
+
+ /* Now use the portable hole */
+ do_cmd_store();
+
+ /* Mega-hack part II: change the current grid to the original value */
+ c_ptr->feat = feat;
+ c_ptr->special = special;
+ p_ptr->town_num = town_num;
+
+ set_portable_hole_weight();
+
+ /* Recalculate bonuses */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Try to add a CLI action.
+ */
+void cli_add(cptr active, cptr trigger, cptr descr)
+{
+ s16b num;
+ cli_comm *cli_ptr, *old_ptr;
+
+ /* Too many macros. */
+ if (cli_total >= CLI_MAX) return;
+
+ /* First try to read active as a number. */
+ if (strtol(active, 0, 0))
+ {
+ num = strtol(active, 0, 0);
+ }
+ /* Then try to read it as a character. */
+ else if (strlen(active) == 1)
+ {
+ num = active[0];
+ }
+ /* Give up if it doesn't work. */
+ else
+ {
+ return;
+ }
+
+ /* Dump the macro. */
+ cli_ptr = cli_info + cli_total;
+ old_ptr = cli_info + cli_total - 1;
+
+ /*
+ * Trim 's from the ends of a token. This turns '@' into @ and
+ * ''' into '. This may be the intent of the code in tokenize(),
+ * but I've left it for lack of comments to back me up.
+ */
+ if (strchr(trigger, '\''))
+ {
+ char temp[80], *t;
+ cptr s;
+ for (s = trigger, t = temp; ; s++, t++)
+ {
+ /* tokenize() causes each ' to be followed by another character,
+ * and then another '. Trim the 's here. */
+ if (*s == '\'')
+ {
+ *t = *(++s);
+ s++;
+ }
+ else
+ {
+ *t = *s;
+ }
+ if (*t == '\0') break;
+ }
+ cli_ptr->comm = string_make(temp);
+ }
+ else
+ {
+ cli_ptr->comm = string_make(trigger);
+ }
+
+ /* First try copying everything across. */
+ cli_ptr->key = num;
+ cli_ptr->descrip = string_make(descr);
+
+ /* Take description for the previous record if appropriate. */
+ if ((cli_total > 0) && (old_ptr->key == cli_ptr->key) && (cli_ptr->descrip == 0))
+ {
+ cli_ptr->descrip = old_ptr->descrip;
+ }
+
+ /* Accept the macro. */
+ if (cli_ptr->key && cli_ptr->comm && cli_ptr->descrip) cli_total++;
+}
+
+
+
+/*
+ * Get a string using CLI completion.
+ */
+bool get_string_cli(cptr prompt, char *buf, int len)
+{
+ bool res;
+
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display prompt */
+ prt(prompt, 0, 0);
+
+ /* Ask the user for a string */
+ askfor_aux_complete = TRUE;
+ res = askfor_aux(buf, len);
+ askfor_aux_complete = FALSE;
+
+ /* Clear prompt */
+ prt("", 0, 0);
+
+ /* Result */
+ return (res);
+}
+
+
+/*
+ * Do a command line command
+ *
+ * This is a wrapper around process command to provide a "reverse keymap"
+ * whereby a set of keypresses is mapped to one.
+ *
+ * This is useful because command_cmd is a s16b, and so allows each command a
+ * unique representation.
+ *
+ * See defines.h for a list of the codes used.
+ */
+void do_cmd_cli(void)
+{
+ char buff[80];
+
+ cli_comm *cli_ptr;
+
+ /* Clear the input buffer */
+ strcpy(buff, "");
+
+ /* Accept command */
+ if (!get_string_cli("Command: ", buff, 30)) return;
+
+
+ /* Analyse the input */
+ for (cli_ptr = cli_info; cli_ptr->comm; cli_ptr++)
+ {
+ if (!strcmp(buff, cli_ptr->comm))
+ {
+ /* Process the command without keymaps or macros. */
+ command_new = cli_ptr->key;
+ return;
+ }
+ }
+
+ msg_format("No such command: %s", buff);
+}
+
+
+/*
+ * Display on-line help for the CLI commands
+ */
+void do_cmd_cli_help()
+{
+ int i, j;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ for (i = 0, j = -1; i < cli_total; i++)
+ {
+ if (j < i - 1) fprintf(fff, "/");
+ fprintf(fff, "[[[[[G%s]", cli_info[i].comm);
+ if (cli_info[i].descrip != cli_info[i + 1].descrip)
+ {
+ fprintf(fff, " %s\n", cli_info[i].descrip);
+ j = i;
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Display the file contents */
+ show_file(file_name, "Command line help", 0, 0);
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Dump screen shot in HTML
+ */
+void do_cmd_html_dump()
+{
+ char tmp_val[81];
+ bool html = TRUE;
+ term_win *save;
+
+ /* Save the screen */
+ save = Term_save_to();
+
+ if (wizard && get_check("WIZARD MODE: Do an help file dump?"))
+ html = FALSE;
+
+ /* Ask for a file */
+ if (html)
+ {
+ strcpy(tmp_val, "dummy.htm");
+ if (!get_string("File(you can post it to http://angband.oook.cz/): ", tmp_val, 80))
+ {
+ /* Now restore the screen to initial state */
+ Term_load_from(save, TRUE);
+ Term_fresh();
+ return;
+ }
+ }
+ else
+ {
+ strcpy(tmp_val, "dummy.txt");
+ if (!get_string("File: ", tmp_val, 80))
+ {
+ /* Now restore the screen to initial state */
+ Term_load_from(save, TRUE);
+ Term_fresh();
+ return;
+ }
+ }
+
+ /* Now restore the screen to dump it */
+ Term_load_from(save, TRUE);
+
+ if (html)
+ html_screenshot(tmp_val);
+ else
+ help_file_screenshot(tmp_val);
+
+ Term_erase(0, 0, 255);
+ msg_print("Dump saved.");
+ Term_fresh();
+ fix_message();
+}
diff --git a/src/cmd4.c b/src/cmd4.c
new file mode 100644
index 00000000..919adddc
--- /dev/null
+++ b/src/cmd4.c
@@ -0,0 +1,4808 @@
+/* File: cmd4.c */
+
+/* Purpose: Interface commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Hack -- redraw the screen
+ *
+ * This command performs various low level updates, clears all the "extra"
+ * windows, does a total redraw of the main window, and requests all of the
+ * interesting updates and redraws that I can think of.
+ *
+ * This command is also used to "instantiate" the results of the user
+ * selecting various things, such as graphics mode, so it must call
+ * the "TERM_XTRA_REACT" hook before redrawing the windows.
+ */
+void do_cmd_redraw(void)
+{
+ int j;
+
+ term *old = Term;
+
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+
+ /* Combine and Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+
+ /* Update torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Forget view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update view */
+ p_ptr->update |= (PU_VIEW);
+
+ /* Update monster light */
+ p_ptr->update |= (PU_MON_LITE);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER | PW_M_LIST);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE | PW_IRC | PW_OVERHEAD | PW_MONSTER | PW_OBJECT);
+
+ /* Hack -- update */
+ handle_stuff();
+
+
+ /* Redraw every window */
+ for (j = 0; j < 8; j++)
+ {
+ /* Dead window */
+ if (!angband_term[j]) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Redraw */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- change name
+ */
+void do_cmd_change_name(void)
+{
+ char c;
+
+ int mode = 0;
+
+ char tmp[160];
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Forever */
+ while (1)
+ {
+ /* keep mode below 7 */
+ mode = (mode + 6) % 6;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Prompt */
+ if (mode == 0)
+ {
+ Term_putstr(14, 22, -1, TERM_WHITE,
+ "['t/T' to change tactics, 'e/E' to change movement]");
+ }
+
+ Term_putstr(4, 23, -1, TERM_WHITE,
+ "['c' to change name, 'f' to file, 'p' for previous, 'n' for next, or ESC]");
+
+ /* Query */
+ c = inkey();
+
+ /* Exit */
+ if (c == ESCAPE) break;
+
+ /* Change name */
+ if (c == 'c')
+ {
+ get_name();
+ }
+
+ /* File dump */
+ else if (c == 'f')
+ {
+ strnfmt(tmp, 160, "%s.txt", player_name);
+ if (get_string("Filename(you can post it to http://angband.oook.cz/): ", tmp, 80))
+ {
+ if (tmp[0] && (tmp[0] != ' '))
+ {
+ file_character(tmp, FALSE);
+ }
+ }
+ }
+
+ /* Toggle mode */
+ else if (c == 'n')
+ {
+ mode++;
+ }
+ else if (c == 'p')
+ {
+ mode--;
+ }
+
+ else if (mode == 0)
+ {
+ /* Change tactic */
+ if (c == 't')
+ {
+ (void)do_cmd_change_tactic( -1);
+ }
+ else if (c == 'T')
+ {
+ (void)do_cmd_change_tactic(1);
+ }
+
+ /* Change movement */
+ else if (c == 'e')
+ {
+ do_cmd_change_movement( -1);
+ }
+ else if (c == 'E')
+ {
+ do_cmd_change_movement(1);
+ }
+ else
+ {
+ bell();
+ }
+ }
+ /* Oops */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+
+ handle_stuff();
+}
+
+
+/*
+ * Recall the most recent message
+ */
+void do_cmd_message_one(void)
+{
+ cptr msg = format("> %s", message_str(0));
+
+ /* Recall one message XXX XXX XXX */
+ display_message(0, 0, strlen(msg), message_color(0), msg);
+}
+
+
+/*
+ * Show previous messages to the user -BEN-
+ *
+ * The screen format uses line 0 and (Term->hgt - 1) for headers and prompts,
+ * skips line 1 and (Term->hgt - 2), and uses line 2 thru (Term->hgt - 3) for
+ * old messages.
+ *
+ * This command shows you which commands you are viewing, and allows
+ * you to "search" for strings in the recall.
+ *
+ * Note that messages may be longer than 80 characters, but they are
+ * displayed using "infinite" length, with a special sub-command to
+ * "slide" the virtual display to the left or right.
+ *
+ * Attempt to only hilite the matching portions of the string.
+ *
+ * Now taking advantages of big-screen. -pav-
+ */
+void do_cmd_messages(void)
+{
+ int i, j, k, n;
+ u32b q;
+ int wid, hgt;
+
+ char shower[80];
+ char finder[80];
+
+ /* Wipe finder */
+ strcpy(finder, "");
+
+ /* Wipe shower */
+ strcpy(shower, "");
+
+
+ /* Total messages */
+ n = message_num();
+
+ /* Start on first message */
+ i = 0;
+
+ /* Start at leftmost edge */
+ q = 0;
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Process requests until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Dump up to 20 (or more in bigscreen) lines of messages */
+ for (j = 0; (j < (hgt - 4)) && (i + j < n); j++)
+ {
+ cptr msg = message_str(i + j);
+ byte color = message_color(i + j);
+
+ /* Apply horizontal scroll */
+ msg = (strlen(msg) >= q) ? (msg + q) : "";
+
+ /* Dump the messages, bottom to top */
+ display_message(0, (hgt - 3) - j, strlen(msg), color, msg);
+
+ /* Hilite "shower" */
+ if (shower[0])
+ {
+ cptr str = msg;
+
+ /* Display matches */
+ while ((str = strstr(str, shower)) != NULL)
+ {
+ int len = strlen(shower);
+
+ /* Display the match */
+ Term_putstr(str - msg, (hgt - 3) - j, len, TERM_YELLOW, shower);
+
+ /* Advance */
+ str += len;
+ }
+ }
+ }
+
+ /* Display header XXX XXX XXX */
+ prt(format("Message Recall (%d-%d of %d), Offset %d",
+ i, i + j - 1, n, q), 0, 0);
+
+ /* Display prompt (not very informative) */
+ prt("[Press 'p' for older, 'n' for newer, ..., or ESCAPE]", hgt - 1, 0);
+
+ /* Get a command */
+ k = inkey();
+
+ /* Exit on Escape */
+ if (k == ESCAPE) break;
+
+ /* Hack -- Save the old index */
+ j = i;
+
+ /* Horizontal scroll */
+ if (k == '4')
+ {
+ /* Scroll left */
+ q = (q >= ((u32b)wid / 2)) ? (q - wid / 2) : 0;
+
+ /* Success */
+ continue;
+ }
+
+ /* Horizontal scroll */
+ if (k == '6')
+ {
+ /* Scroll right */
+ q = q + wid / 2;
+
+ /* Success */
+ continue;
+ }
+
+ /* Hack -- handle show */
+ if (k == '=')
+ {
+ /* Prompt */
+ prt("Show: ", hgt - 1, 0);
+
+ /* Get a "shower" string, or continue */
+ if (!askfor_aux(shower, 80)) continue;
+
+ /* Okay */
+ continue;
+ }
+
+ /* Hack -- handle find */
+ if (k == '/')
+ {
+ s16b z;
+
+ /* Prompt */
+ prt("Find: ", hgt - 1, 0);
+
+ /* Get a "finder" string, or continue */
+ if (!askfor_aux(finder, 80)) continue;
+
+ /* Show it */
+ strcpy(shower, finder);
+
+ /* Scan messages */
+ for (z = i + 1; z < n; z++)
+ {
+ cptr msg = message_str(z);
+
+ /* Search for it */
+ if (strstr(msg, finder))
+ {
+ /* New location */
+ i = z;
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Recall 1 older message */
+ if ((k == '8') || (k == '\n') || (k == '\r'))
+ {
+ /* Go newer if legal */
+ if (i + 1 < n) i += 1;
+ }
+
+ /* Recall 10 older messages */
+ if (k == '+')
+ {
+ /* Go older if legal */
+ if (i + 10 < n) i += 10;
+ }
+
+ /* Recall one screen of older messages */
+ if ((k == 'p') || (k == KTRL('P')) || (k == ' '))
+ {
+ /* Go older if legal */
+ if (i + (hgt - 4) < n) i += (hgt - 4);
+ }
+
+ /* Recall one screen of newer messages */
+ if ((k == 'n') || (k == KTRL('N')))
+ {
+ /* Go newer (if able) */
+ i = (i >= (hgt - 4)) ? (i - (hgt - 4)) : 0;
+ }
+
+ /* Recall 10 newer messages */
+ if (k == '-')
+ {
+ /* Go newer (if able) */
+ i = (i >= 10) ? (i - 10) : 0;
+ }
+
+ /* Recall 1 newer messages */
+ if (k == '2')
+ {
+ /* Go newer (if able) */
+ i = (i >= 1) ? (i - 1) : 0;
+ }
+
+ /* Hack -- Error of some kind */
+ if (i == j) bell();
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Number of cheating options
+ */
+#define CHEAT_MAX 6
+
+/*
+ * Cheating options
+ */
+static option_type cheat_info[CHEAT_MAX] =
+{
+ { &cheat_peek, FALSE, 0, 0, "cheat_peek", "Peek into object creation" },
+ { &cheat_hear, FALSE, 0, 1, "cheat_hear", "Peek into monster creation" },
+ { &cheat_room, FALSE, 0, 2, "cheat_room", "Peek into dungeon creation" },
+ { &cheat_xtra, FALSE, 0, 3, "cheat_xtra", "Peek into something else" },
+ { &cheat_know, FALSE, 0, 4, "cheat_know", "Know complete monster info" },
+ { &cheat_live, FALSE, 0, 5, "cheat_live", "Allow player to avoid death" }
+};
+
+/*
+ * Interact with some options for cheating
+ */
+static void do_cmd_options_cheat(cptr info)
+{
+ char ch;
+
+ int i, k = 0, n = CHEAT_MAX;
+
+ int dir;
+
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (i = 0; i < n; i++)
+ {
+ byte a = TERM_WHITE;
+
+ /* Color current option */
+ if (i == k) a = TERM_L_BLUE;
+
+ /* Display the option text */
+ strnfmt(buf, 80, "%-48s: %s (%s)",
+ cheat_info[i].o_desc,
+ (*cheat_info[i].o_var ? "yes" : "no "),
+ cheat_info[i].o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ dir = get_keymap_dir(ch);
+ if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir);
+
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ return;
+ }
+
+ case '-':
+ case '8':
+ {
+ k = (n + k - 1) % n;
+
+ break;
+ }
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '2':
+ {
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'y':
+ case 'Y':
+ case '6':
+ {
+ noscore |= (cheat_info[k].o_page * 256 + cheat_info[k].o_bit);
+ (*cheat_info[k].o_var) = TRUE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ (*cheat_info[k].o_var) = FALSE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+}
+
+
+static option_type autosave_info[2] =
+{
+ { &autosave_l, FALSE, 0, 6, "autosave_l", "Autosave when entering new levels" },
+ { &autosave_t, FALSE, 0, 7, "autosave_t", "Timed autosave" },
+};
+
+s16b toggle_frequency(s16b current)
+{
+ if (current == 0) return (50);
+ if (current == 50) return (100);
+ if (current == 100) return (250);
+ if (current == 250) return (500);
+ if (current == 500) return (1000);
+ if (current == 1000) return (2500);
+ if (current == 2500) return (5000);
+ if (current == 5000) return (10000);
+ if (current == 10000) return (25000);
+
+ return (0);
+}
+
+
+/*
+ * Interact with some options for cheating
+ */
+static void do_cmd_options_autosave(cptr info)
+{
+ char ch;
+
+ int i, k = 0, n = 2;
+
+ int dir;
+
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80,
+ "%s (RET to advance, y/n to set, 'F' for frequency, ESC to accept) ",
+ info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (i = 0; i < n; i++)
+ {
+ byte a = TERM_WHITE;
+
+ /* Color current option */
+ if (i == k) a = TERM_L_BLUE;
+
+ /* Display the option text */
+ strnfmt(buf, 80, "%-48s: %s (%s)",
+ autosave_info[i].o_desc,
+ (*autosave_info[i].o_var ? "yes" : "no "),
+ autosave_info[i].o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ prt(format("Timed autosave frequency: every %d turns", autosave_freq), 5, 0);
+
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ dir = get_keymap_dir(ch);
+ if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir);
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ return;
+ }
+
+ case '-':
+ case '8':
+ {
+ k = (n + k - 1) % n;
+
+ break;
+ }
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '2':
+ {
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'y':
+ case 'Y':
+ case '6':
+ {
+
+ (*autosave_info[k].o_var) = TRUE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ (*autosave_info[k].o_var) = FALSE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'f':
+ case 'F':
+ {
+ autosave_freq = toggle_frequency(autosave_freq);
+ prt(format("Timed autosave frequency: every %d turns",
+ autosave_freq), 5, 0);
+
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+}
+
+/* Switch an option by only knowing its name */
+bool change_option(cptr name, bool value)
+{
+ int i;
+
+ /* Scan the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ if (!strcmp(option_info[i].o_text, name))
+ {
+ bool old = (*option_info[i].o_var);
+
+ (*option_info[i].o_var) = value;
+
+ return old;
+ }
+ }
+
+ cmsg_format(TERM_VIOLET, "Warning, change_option couldn't find option '%s'.", name);
+ return FALSE;
+}
+
+/*
+ * Interact with some options
+ */
+void do_cmd_options_aux(int page, cptr info, bool read_only)
+{
+ char ch;
+
+ int i, k = 0, n = 0;
+
+ int dir;
+
+ int opt[24];
+
+ char buf[80];
+
+
+ /* Lookup the options */
+ for (i = 0; i < 24; i++) opt[i] = 0;
+
+ /* Scan the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ /* Notice options on this "page" */
+ if (option_info[i].o_page == page) opt[n++] = i;
+ }
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (i = 0; i < n; i++)
+ {
+ byte a = TERM_WHITE;
+
+ /* Color current option */
+ if (i == k) a = TERM_L_BLUE;
+
+ /* Display the option text */
+ strnfmt(buf, 80, "%-48s: %s (%s)",
+ option_info[opt[i]].o_desc,
+ (*option_info[opt[i]].o_var ? "yes" : "no "),
+ option_info[opt[i]].o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ dir = get_keymap_dir(ch);
+ if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir);
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ return;
+ }
+
+ case '-':
+ case '8':
+ {
+ k = (n + k - 1) % n;
+
+ break;
+ }
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '2':
+ {
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'y':
+ case 'Y':
+ case '6':
+ {
+ if (read_only) break;
+
+ (*option_info[opt[k]].o_var) = TRUE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ if (read_only) break;
+
+ (*option_info[opt[k]].o_var) = FALSE;
+ k = (k + 1) % n;
+
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Modify the "window" options
+ */
+static void do_cmd_options_win(void)
+{
+ int i, j, d;
+
+ int y = 0;
+
+ int x = 0;
+
+ char ch;
+
+ bool go = TRUE;
+
+ u32b old_flag[8];
+
+
+ /* Memorize old flags */
+ for (j = 0; j < 8; j++)
+ {
+ /* Acquire current flags */
+ old_flag[j] = window_flag[j];
+ }
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact */
+ while (go)
+ {
+ /* Prompt XXX XXX XXX */
+ prt("Window Flags (<dir>, t, y, n, ESC) ", 0, 0);
+
+ /* Display the windows */
+ for (j = 0; j < 8; j++)
+ {
+ byte a = TERM_WHITE;
+
+ cptr s = angband_term_name[j];
+
+ /* Use color */
+ if (use_color && (j == x)) a = TERM_L_BLUE;
+
+ /* Window name, staggered, centered */
+ Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
+ }
+
+ /* Display the options */
+ for (i = 0; i < 16; i++)
+ {
+ byte a = TERM_WHITE;
+
+ cptr str = window_flag_desc[i];
+
+ /* Use color */
+ if (use_color && (i == y)) a = TERM_L_BLUE;
+
+ /* Unused option */
+ if (!str) str = "(Unused option)";
+
+ /* Flag name */
+ Term_putstr(0, i + 5, -1, a, str);
+
+ /* Display the windows */
+ for (j = 0; j < 8; j++)
+ {
+ byte a = TERM_WHITE;
+
+ char c = '.';
+
+ /* Use color */
+ if (use_color && (i == y) && (j == x)) a = TERM_L_BLUE;
+
+ /* Active flag */
+ if (window_flag[j] & (1L << i)) c = 'X';
+
+ /* Flag value */
+ Term_putch(35 + j * 5, i + 5, a, c);
+ }
+ }
+
+ /* Place Cursor */
+ Term_gotoxy(35 + x * 5, y + 5);
+
+ /* Get key */
+ ch = inkey();
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ go = FALSE;
+
+ break;
+ }
+
+ case 'T':
+ case 't':
+ {
+ /* Clear windows */
+ for (j = 0; j < 8; j++)
+ {
+ window_flag[j] &= ~(1L << y);
+ }
+
+ /* Clear flags */
+ for (i = 0; i < 16; i++)
+ {
+ window_flag[x] &= ~(1L << i);
+ }
+
+ /* Fall through */
+ }
+
+ case 'y':
+ case 'Y':
+ {
+ /* Ignore screen */
+ if (x == 0) break;
+
+ /* Set flag */
+ window_flag[x] |= (1L << y);
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ {
+ /* Clear flag */
+ window_flag[x] &= ~(1L << y);
+
+ break;
+ }
+
+ default:
+ {
+ d = get_keymap_dir(ch);
+
+ x = (x + ddx[d] + 8) % 8;
+ y = (y + ddy[d] + 16) % 16;
+
+ if (!d) bell();
+
+ break;
+ }
+ }
+ }
+
+ /* Notice changes */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* Dead window */
+ if (!angband_term[j]) continue;
+
+ /* Ignore non-changes */
+ if (window_flag[j] == old_flag[j]) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Erase */
+ Term_clear();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Write all current options to the given preference file in the
+ * lib/user directory. Modified from KAmband 1.8.
+ */
+static errr option_dump(cptr fname)
+{
+ int i, j;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip some lines */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic option dump\n\n");
+
+ /* Dump options (skip cheat, adult, score) */
+ for (i = 0; option_info[i].o_var != NULL; i++)
+ {
+ /* Require a real option */
+ if (!option_info[i].o_text) continue;
+
+ /* No birth options */
+ if (option_info[i].o_page == 6) continue;
+
+ /* Comment */
+ fprintf(fff, "# Option '%s'\n", option_info[i].o_desc);
+
+ /* Dump the option */
+ if ((*option_info[i].o_var))
+ {
+ fprintf(fff, "Y:%s\n", option_info[i].o_text);
+ }
+ else
+ {
+ fprintf(fff, "X:%s\n", option_info[i].o_text);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+ }
+
+ /* Dump window flags */
+ for (i = 1; i < ANGBAND_TERM_MAX; i++)
+ {
+ /* Require a real window */
+ if (!angband_term[i]) continue;
+
+ /* Check each flag */
+ for (j = 0; j < 32; j++)
+ {
+ /* Require a real flag */
+ if (!window_flag_desc[j]) continue;
+
+ /* Comment */
+ fprintf(fff, "# Window '%s', Flag '%s'\n",
+ angband_term_name[i], window_flag_desc[j]);
+
+ /* Dump the flag */
+ if (window_flag[i] & (1L << j))
+ {
+ fprintf(fff, "W:%d:%d:1\n", i, j);
+ }
+ else
+ {
+ fprintf(fff, "W:%d:%d:0\n", i, j);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+ }
+ }
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Ask for a "user pref file" and process it.
+ *
+ * This function should only be used by standard interaction commands,
+ * in which a standard "Command:" prompt is present on the given row.
+ *
+ * Allow absolute file names? XXX XXX XXX
+ */
+static void do_cmd_pref_file_hack(int row)
+{
+ char ftmp[80];
+
+
+ /* Prompt */
+ prt("Command: Load a user pref file", row, 0);
+
+ /* Prompt */
+ prt("File: ", row + 2, 0);
+
+ /* Default filename */
+ strnfmt(ftmp, 80, "%s.prf", player_base);
+
+ /* Ask for a file (or cancel) */
+ if (!askfor_aux(ftmp, 80)) return;
+
+ /* Process the given filename */
+ if (process_pref_file(ftmp))
+ {
+ /* Mention failure */
+ msg_format("Failed to load '%s'!", ftmp);
+ }
+ else
+ {
+ /* Mention success */
+ msg_format("Loaded '%s'.", ftmp);
+ }
+}
+
+
+/*
+ * Set or unset various options.
+ *
+ * The user must use the "Ctrl-R" command to "adapt" to changes
+ * in any options which control "visual" aspects of the game.
+ */
+void do_cmd_options(void)
+{
+ int k;
+
+
+ /* Save the screen */
+ screen_save();
+
+ /* Interact */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Why are we here */
+ prt("Options", 2, 0);
+
+ /* Give some choices */
+ prt("(1) User Interface Options", 4, 5);
+ prt("(2) Disturbance Options", 5, 5);
+ prt("(3) Game-Play Options", 6, 5);
+ prt("(4) Efficiency Options", 7, 5);
+ prt("(5) ToME Options", 8, 5);
+ prt("(6) Birth Options(read only)", 9, 5);
+
+ /* Special choices */
+ prt("(D) Base Delay Factor", 10, 5);
+ prt("(H) Hitpoint Warning", 11, 5);
+ prt("(A) Autosave Options", 12, 5);
+
+ /* Automatizer */
+ prt("(T) Automatizer", 14, 5);
+
+
+ /* Window flags */
+ prt("(W) Window Flags", 16, 5);
+
+ /* Cheating */
+ prt("(C) Cheating Options", 18, 5);
+
+ /* Dump */
+ prt("(U) Dump Options setting", 20, 5);
+ prt("(O) Load Options setting", 21, 5);
+
+ /* Prompt */
+ prt("Command: ", 22, 0);
+
+ /* Get command */
+ k = inkey();
+
+ /* Exit */
+ if (k == ESCAPE) break;
+
+ /* Analyze */
+ switch (k)
+ {
+ /* Load a user pref file */
+ case 'o':
+ case 'O':
+ {
+ /* Ask for and load a user pref file */
+ do_cmd_pref_file_hack(21);
+
+ break;
+ }
+
+ /* Append options to a file */
+ case 'u':
+ case 'U':
+ {
+ char ftmp[80];
+
+ /* Prompt */
+ prt("Command: Append options to a file", 21, 0);
+
+ /* Prompt */
+ prt("File: ", 21, 0);
+
+ /* Default filename */
+ strnfmt(ftmp, 80, "%s.prf", player_base);
+
+ /* Ask for a file */
+ if (!askfor_aux(ftmp, 80)) continue;
+
+ /* Dump the options */
+ if (option_dump(ftmp))
+ {
+ /* Failure */
+ msg_print("Failed!");
+ }
+ else
+ {
+ /* Success */
+ msg_print("Done.");
+ }
+
+ break;
+ }
+
+ /* General Options */
+ case '1':
+ {
+ /* Process the general options */
+ do_cmd_options_aux(1, "User Interface Options", FALSE);
+
+ break;
+ }
+
+ /* Disturbance Options */
+ case '2':
+ {
+ /* Spawn */
+ do_cmd_options_aux(2, "Disturbance Options", FALSE);
+
+ break;
+ }
+
+ /* Inventory Options */
+ case '3':
+ {
+ /* Spawn */
+ do_cmd_options_aux(3, "Game-Play Options", FALSE);
+
+ break;
+ }
+
+ /* Efficiency Options */
+ case '4':
+ {
+ /* Spawn */
+ do_cmd_options_aux(4, "Efficiency Options", FALSE);
+
+ break;
+ }
+
+ /* ToME Options */
+ case '5':
+ {
+ do_cmd_options_aux(5, "ToME Options", FALSE);
+
+ break;
+ }
+
+ /* Birth Options - read only */
+ case '6':
+ {
+ do_cmd_options_aux(6, "Birth Options(read only)", TRUE);
+
+ break;
+ }
+#if 0 /* DGDGDG -- has if anyone used those */
+ /* Stacking Options */
+ case 'S':
+ case 's':
+ {
+ /* Spawn */
+ do_cmd_options_aux(7, "Stacking Options", FALSE);
+
+ break;
+ }
+#endif
+ /* Cheating Options */
+ case 'C':
+ {
+ /* Spawn */
+ do_cmd_options_cheat("Cheaters never win");
+
+ break;
+ }
+
+ case 't':
+ case 'T':
+ {
+ do_cmd_automatizer();
+ break;
+ }
+
+ case 'a':
+ case 'A':
+ {
+ do_cmd_options_autosave("Autosave");
+
+ break;
+ }
+
+ /* Window flags */
+ case 'W':
+ case 'w':
+ {
+ /* Spawn */
+ do_cmd_options_win();
+
+ break;
+ }
+
+ /* Hack -- Delay Speed */
+ case 'D':
+ case 'd':
+ {
+ /* Prompt */
+ prt("Command: Base Delay Factor", 21, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ int msec = delay_factor * delay_factor * delay_factor;
+ prt(format("Current base delay factor: %d (%d msec)",
+ delay_factor, msec), 22, 0);
+ prt("Delay Factor (0-9 or ESC to accept): ", 23, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) delay_factor = D2I(k);
+ else bell();
+ }
+
+ break;
+ }
+
+ /* Hack -- hitpoint warning factor */
+ case 'H':
+ case 'h':
+ {
+ /* Prompt */
+ prt("Command: Hitpoint Warning", 18, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ prt(format("Current hitpoint warning: %d0%%",
+ hitpoint_warn), 22, 0);
+ prt("Hitpoint Warning (0-9 or ESC to accept): ", 20, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) hitpoint_warn = D2I(k);
+ else bell();
+ }
+
+ break;
+ }
+
+ /* Unknown option */
+ default:
+ {
+ /* Oops */
+ bell();
+
+ break;
+ }
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ screen_load();
+
+ /* Set the ingame help */
+ ingame_help(p_ptr->help.enabled);
+}
+
+
+
+/*
+ * Ask for a "user pref line" and process it
+ *
+ * XXX XXX XXX Allow absolute file names?
+ */
+void do_cmd_pref(void)
+{
+ char buf[80];
+
+
+ /* Default */
+ strcpy(buf, "");
+
+ /* Ask for a "user pref command" */
+ if (!get_string("Pref: ", buf, 80)) return;
+
+ /* Process that pref command */
+ (void)process_pref_file_aux(buf);
+}
+
+
+#ifdef ALLOW_MACROS
+
+/*
+ * Hack -- append all current macros to the given file
+ */
+static errr macro_dump(cptr fname)
+{
+ int i;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip space */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic macro dump\n\n");
+
+ /* Dump them */
+ for (i = 0; i < macro__num; i++)
+ {
+ /* Start the macro */
+ fprintf(fff, "# Macro '%d'\n\n", i);
+
+ /* Extract the action */
+ ascii_to_text(buf, macro__act[i]);
+
+ /* Dump the macro */
+ fprintf(fff, "A:%s\n", buf);
+
+ /* Extract the action */
+ ascii_to_text(buf, macro__pat[i]);
+
+ /* Dump normal macros */
+ fprintf(fff, "P:%s\n", buf);
+
+ /* End the macro */
+ fprintf(fff, "\n\n");
+ }
+
+ /* Start dumping */
+ fprintf(fff, "\n\n\n\n");
+
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Hack -- ask for a "trigger" (see below)
+ *
+ * Note the complex use of the "inkey()" function from "util.c".
+ *
+ * Note that both "flush()" calls are extremely important.
+ */
+static void do_cmd_macro_aux(char *buf, bool macro_screen)
+{
+ int i, n = 0;
+
+ char tmp[1024];
+
+
+ /* Flush */
+ flush();
+
+ /* Do not process macros */
+ inkey_base = TRUE;
+
+ /* First key */
+ i = inkey();
+
+ /* Read the pattern */
+ while (i)
+ {
+ /* Save the key */
+ buf[n++] = i;
+
+ /* Do not process macros */
+ inkey_base = TRUE;
+
+ /* Do not wait for keys */
+ inkey_scan = TRUE;
+
+ /* Attempt to read a key */
+ i = inkey();
+ }
+
+ /* Terminate */
+ buf[n] = '\0';
+
+ /* Flush */
+ flush();
+
+
+ if (macro_screen)
+ {
+ /* Convert the trigger */
+ ascii_to_text(tmp, buf);
+
+ /* Hack -- display the trigger */
+ Term_addstr( -1, TERM_WHITE, tmp);
+ }
+}
+
+#endif
+
+
+/*
+ * Hack -- ask for a keymap "trigger" (see below)
+ *
+ * Note that both "flush()" calls are extremely important. This may
+ * no longer be true, since "util.c" is much simpler now. XXX XXX XXX
+ */
+static void do_cmd_macro_aux_keymap(char *buf)
+{
+ char tmp[1024];
+
+
+ /* Flush */
+ flush();
+
+
+ /* Get a key */
+ buf[0] = inkey();
+ buf[1] = '\0';
+
+
+ /* Convert to ascii */
+ ascii_to_text(tmp, buf);
+
+ /* Hack -- display the trigger */
+ Term_addstr( -1, TERM_WHITE, tmp);
+
+
+ /* Flush */
+ flush();
+}
+
+
+/*
+ * Hack -- append all keymaps to the given file
+ */
+static errr keymap_dump(cptr fname)
+{
+ int i;
+
+ FILE *fff;
+
+ char key[1024];
+ char buf[1024];
+
+ int mode;
+
+
+ /* Roguelike */
+ if (rogue_like_commands)
+ {
+ mode = KEYMAP_MODE_ROGUE;
+ }
+
+ /* Original */
+ else
+ {
+ mode = KEYMAP_MODE_ORIG;
+ }
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip space */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic keymap dump\n\n");
+
+ /* Dump them */
+ for (i = 0; i < 256; i++)
+ {
+ cptr act;
+
+ /* Loop up the keymap */
+ act = keymap_act[mode][i];
+
+ /* Skip empty keymaps */
+ if (!act) continue;
+
+ /* Encode the key */
+ buf[0] = i;
+ buf[1] = '\0';
+ ascii_to_text(key, buf);
+
+ /* Encode the action */
+ ascii_to_text(buf, act);
+
+ /* Dump the macro */
+ fprintf(fff, "A:%s\n", buf);
+ fprintf(fff, "C:%d:%s\n", mode, key);
+ }
+
+ /* Start dumping */
+ fprintf(fff, "\n\n\n");
+
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Interact with "macros"
+ *
+ * Note that the macro "action" must be defined before the trigger.
+ *
+ * Could use some helpful instructions on this page. XXX XXX XXX
+ */
+void do_cmd_macros(void)
+{
+ int i;
+
+ char tmp[1024];
+
+ char buf[1024];
+
+ int mode;
+
+
+ /* Roguelike */
+ if (rogue_like_commands)
+ {
+ mode = KEYMAP_MODE_ROGUE;
+ }
+
+ /* Original */
+ else
+ {
+ mode = KEYMAP_MODE_ORIG;
+ }
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save screen */
+ Term_save();
+
+
+ /* Process requests until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Describe */
+ prt("Interact with Macros", 2, 0);
+
+
+ /* Describe that action */
+ prt("Current action (if any) shown below:", 20, 0);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+
+ /* Selections */
+ prt("(1) Load a user pref file", 4, 5);
+#ifdef ALLOW_MACROS
+ prt("(2) Append macros to a file", 5, 5);
+ prt("(3) Query a macro", 6, 5);
+ prt("(4) Create a macro", 7, 5);
+ prt("(5) Remove a macro", 8, 5);
+ prt("(6) Append keymaps to a file", 9, 5);
+ prt("(7) Query a keymap", 10, 5);
+ prt("(8) Create a keymap", 11, 5);
+ prt("(9) Remove a keymap", 12, 5);
+ prt("(0) Enter a new action", 13, 5);
+#endif /* ALLOW_MACROS */
+
+ /* Prompt */
+ prt("Command: ", 16, 0);
+
+ /* Get a command */
+ i = inkey();
+
+ /* Leave */
+ if (i == ESCAPE) break;
+
+ /* Load a 'macro' file */
+ else if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Process the given filename */
+ if (0 != process_pref_file(tmp))
+ {
+ /* Prompt */
+ msg_print("Could not load file!");
+ }
+ }
+
+#ifdef ALLOW_MACROS
+
+ /* Save macros */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Append macros to a file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Dump the macros */
+ (void)macro_dump(tmp);
+
+ /* Prompt */
+ msg_print("Appended macros.");
+ }
+
+ /* Query a macro */
+ else if (i == '3')
+ {
+ int k;
+
+ /* Prompt */
+ prt("Command: Query a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Acquire action */
+ k = macro_find_exact(buf);
+
+ /* Nothing found */
+ if (k < 0)
+ {
+ /* Prompt */
+ msg_print("Found no macro.");
+ }
+
+ /* Found one */
+ else
+ {
+ /* Obtain the action */
+ strcpy(macro__buf, macro__act[k]);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+ /* Prompt */
+ msg_print("Found a macro.");
+ }
+ }
+
+ /* Create a macro */
+ else if (i == '4')
+ {
+ /* Prompt */
+ prt("Command: Create a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Clear */
+ clear_from(20);
+
+ /* Prompt */
+ prt("Action: ", 20, 0);
+
+ /* Convert to text */
+ ascii_to_text(tmp, macro__buf);
+
+ /* Get an encoded action */
+ if (askfor_aux(tmp, 80))
+ {
+ /* Convert to ascii */
+ text_to_ascii(macro__buf, tmp);
+
+ /* Link the macro */
+ macro_add(buf, macro__buf);
+
+ /* Prompt */
+ msg_print("Added a macro.");
+ }
+ }
+
+ /* Remove a macro */
+ else if (i == '5')
+ {
+ /* Prompt */
+ prt("Command: Remove a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Link the macro */
+ macro_add(buf, buf);
+
+ /* Prompt */
+ msg_print("Removed a macro.");
+ }
+
+ /* Save keymaps */
+ else if (i == '6')
+ {
+ /* Prompt */
+ prt("Command: Append keymaps to a file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Dump the macros */
+ (void)keymap_dump(tmp);
+
+ /* Prompt */
+ msg_print("Appended keymaps.");
+ }
+
+ /* Query a keymap */
+ else if (i == '7')
+ {
+ cptr act;
+
+ /* Prompt */
+ prt("Command: Query a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Look up the keymap */
+ act = keymap_act[mode][(byte)(buf[0])];
+
+ /* Nothing found */
+ if (!act)
+ {
+ /* Prompt */
+ msg_print("Found no keymap.");
+ }
+
+ /* Found one */
+ else
+ {
+ /* Obtain the action */
+ strcpy(macro__buf, act);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+ /* Prompt */
+ msg_print("Found a keymap.");
+ }
+ }
+
+ /* Create a keymap */
+ else if (i == '8')
+ {
+ /* Prompt */
+ prt("Command: Create a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Clear */
+ clear_from(20);
+
+ /* Prompt */
+ prt("Action: ", 20, 0);
+
+ /* Convert to text */
+ ascii_to_text(tmp, macro__buf);
+
+ /* Get an encoded action */
+ if (askfor_aux(tmp, 80))
+ {
+ /* Convert to ascii */
+ text_to_ascii(macro__buf, tmp);
+
+ /* Free old keymap */
+ string_free(keymap_act[mode][(byte)(buf[0])]);
+
+ /* Make new keymap */
+ keymap_act[mode][(byte)(buf[0])] = string_make(macro__buf);
+
+ /* Prompt */
+ msg_print("Added a keymap.");
+ }
+ }
+
+ /* Remove a keymap */
+ else if (i == '9')
+ {
+ /* Prompt */
+ prt("Command: Remove a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Free old keymap */
+ string_free(keymap_act[mode][(byte)(buf[0])]);
+
+ /* Make new keymap */
+ keymap_act[mode][(byte)(buf[0])] = NULL;
+
+ /* Prompt */
+ msg_print("Removed a keymap.");
+ }
+
+ /* Enter a new action */
+ else if (i == '0')
+ {
+ /* Prompt */
+ prt("Command: Enter a new action", 16, 0);
+
+ /* Go to the correct location */
+ Term_gotoxy(0, 22);
+
+ /* Hack -- limit the value */
+ tmp[80] = '\0';
+
+ /* Get an encoded action */
+ if (!askfor_aux(buf, 80)) continue;
+
+ /* Extract an action */
+ text_to_ascii(macro__buf, buf);
+ }
+
+#endif /* ALLOW_MACROS */
+
+ /* Oops */
+ else
+ {
+ /* Oops */
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Load screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Interact with "visuals"
+ */
+void do_cmd_visuals(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char tmp[160];
+
+ char buf[1024];
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Interact with Visuals", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Load a user pref file", 4, 5);
+#ifdef ALLOW_VISUALS
+ prt("(2) Dump monster attr/chars", 5, 5);
+ prt("(3) Dump object attr/chars", 6, 5);
+ prt("(4) Dump feature attr/chars", 7, 5);
+ prt("(5) (unused)", 8, 5);
+ prt("(6) Change monster attr/chars", 9, 5);
+ prt("(7) Change object attr/chars", 10, 5);
+ prt("(8) Change feature attr/chars", 11, 5);
+ prt("(9) (unused)", 12, 5);
+#endif
+ prt("(0) Reset visuals", 13, 5);
+
+ /* Prompt */
+ prt("Command: ", 15, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Load a 'pref' file */
+ else if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Query */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Process the given filename */
+ (void)process_pref_file(tmp);
+ }
+
+#ifdef ALLOW_VISUALS
+
+ /* Dump monster attr/chars */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Dump monster attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Monster attr/char definitions\n\n");
+
+ /* Dump monsters */
+ for (i = 0; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Skip non-entries */
+ if (!r_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", (r_name + r_ptr->name));
+
+ /* Dump the monster attr/char info */
+ fprintf(fff, "R:%d:0x%02X:0x%02X\n\n", i,
+ (byte)(r_ptr->x_attr), (byte)(r_ptr->x_char));
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped monster attr/chars.");
+ }
+
+ /* Dump object attr/chars */
+ else if (i == '3')
+ {
+ /* Prompt */
+ prt("Command: Dump object attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Object attr/char definitions\n\n");
+
+ /* Dump objects */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Skip non-entries */
+ if (!k_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", (k_name + k_ptr->name));
+
+ /* Dump the object attr/char info */
+ fprintf(fff, "K:%d:0x%02X:0x%02X\n\n", i,
+ (byte)(k_ptr->x_attr), (byte)(k_ptr->x_char));
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped object attr/chars.");
+ }
+
+ /* Dump feature attr/chars */
+ else if (i == '4')
+ {
+ /* Prompt */
+ prt("Command: Dump feature attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Feature attr/char definitions\n\n");
+
+ /* Dump features */
+ for (i = 0; i < max_f_idx; i++)
+ {
+ feature_type *f_ptr = &f_info[i];
+
+ /* Skip non-entries */
+ if (!f_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", (f_name + f_ptr->name));
+
+ /* Dump the feature attr/char info */
+ fprintf(fff, "F:%d:0x%02X:0x%02X\n\n", i,
+ (byte)(f_ptr->x_attr), (byte)(f_ptr->x_char));
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped feature attr/chars.");
+ }
+
+ /* Modify monster attr/chars */
+ else if (i == '6')
+ {
+ static int r = 0;
+
+ /* Prompt */
+ prt("Command: Change monster attr/chars", 15, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ monster_race *r_ptr = &r_info[r];
+
+ byte da = (r_ptr->d_attr);
+ char dc = (r_ptr->d_char);
+ byte ca = (r_ptr->x_attr);
+ char cc = (r_ptr->x_char);
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Monster = %d, Name = %-40.40s",
+ r, (r_name + r_ptr->name)));
+
+ /* Label the Default values */
+ Term_putstr(10, 19, -1, TERM_WHITE,
+ format("Default attr/char = %3u / %3u", da, (dc & 0xFF)));
+ Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 19, da, dc);
+ if (use_bigtile)
+ {
+ if (da & 0x80)
+ Term_putch(44, 19, 255, 255);
+ else
+ Term_putch(44, 19, 0, ' ');
+ }
+
+ /* Label the Current values */
+ Term_putstr(10, 20, -1, TERM_WHITE,
+ format("Current attr/char = %3u / %3u", ca, (cc & 0xFF)));
+ Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 20, ca, cc);
+ if (use_bigtile)
+ {
+ if (ca & 0x80)
+ Term_putch(44, 20, 255, 255);
+ else
+ Term_putch(44, 20, 0, ' ');
+ }
+
+ /* Prompt */
+ Term_putstr(0, 22, -1, TERM_WHITE,
+ "Command (n/N/a/A/c/C): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') r = (r + max_r_idx + 1) % max_r_idx;
+ if (i == 'N') r = (r + max_r_idx - 1) % max_r_idx;
+ if (i == 'a') r_ptr->x_attr = (byte)(ca + 1);
+ if (i == 'A') r_ptr->x_attr = (byte)(ca - 1);
+ if (i == 'c') r_ptr->x_char = (byte)(cc + 1);
+ if (i == 'C') r_ptr->x_char = (byte)(cc - 1);
+ }
+ }
+
+ /* Modify object attr/chars */
+ else if (i == '7')
+ {
+ static int k = 0;
+
+ /* Prompt */
+ prt("Command: Change object attr/chars", 15, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ byte da = (byte)k_ptr->d_attr;
+ char dc = (byte)k_ptr->d_char;
+ byte ca = (byte)k_ptr->x_attr;
+ char cc = (byte)k_ptr->x_char;
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Object = %d, Name = %-40.40s",
+ k, (k_name + k_ptr->name)));
+
+ /* Label the Default values */
+ Term_putstr(10, 19, -1, TERM_WHITE,
+ format("Default attr/char = %3u / %3u", da, (dc & 0xFF)));
+ Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 19, da, dc);
+ if (use_bigtile)
+ {
+ if (da & 0x80)
+ Term_putch(44, 19, 255, 255);
+ else
+ Term_putch(44, 19, 0, ' ');
+ }
+
+ /* Label the Current values */
+ Term_putstr(10, 20, -1, TERM_WHITE,
+ format("Current attr/char = %3u / %3u", ca, (cc & 0xFF)));
+ Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 20, ca, cc);
+ if (use_bigtile)
+ {
+ if (ca & 0x80)
+ Term_putch(44, 20, 255, 255);
+ else
+ Term_putch(44, 20, 0, ' ');
+ }
+
+ /* Prompt */
+ Term_putstr(0, 22, -1, TERM_WHITE,
+ "Command (n/N/a/A/c/C): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') k = (k + max_k_idx + 1) % max_k_idx;
+ if (i == 'N') k = (k + max_k_idx - 1) % max_k_idx;
+ if (i == 'a') k_info[k].x_attr = (byte)(ca + 1);
+ if (i == 'A') k_info[k].x_attr = (byte)(ca - 1);
+ if (i == 'c') k_info[k].x_char = (byte)(cc + 1);
+ if (i == 'C') k_info[k].x_char = (byte)(cc - 1);
+ }
+ }
+
+ /* Modify feature attr/chars */
+ else if (i == '8')
+ {
+ static int f = 0;
+
+ /* Prompt */
+ prt("Command: Change feature attr/chars", 15, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ feature_type *f_ptr = &f_info[f];
+
+ byte da = (byte)f_ptr->d_attr;
+ char dc = (byte)f_ptr->d_char;
+ byte ca = (byte)f_ptr->x_attr;
+ char cc = (byte)f_ptr->x_char;
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Terrain = %d, Name = %-40.40s",
+ f, (f_name + f_ptr->name)));
+
+ /* Label the Default values */
+ Term_putstr(10, 19, -1, TERM_WHITE,
+ format("Default attr/char = %3u / %3u", da, (dc & 0xFF)));
+ Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 19, da, dc);
+ if (use_bigtile)
+ {
+ if (da & 0x80)
+ Term_putch(44, 19, 255, 255);
+ else
+ Term_putch(44, 19, 0, ' ');
+ }
+
+ /* Label the Current values */
+ Term_putstr(10, 20, -1, TERM_WHITE,
+ format("Current attr/char = %3u / %3u", ca, (cc & 0xFF)));
+ Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
+ Term_putch(43, 20, ca, cc);
+ if (use_bigtile)
+ {
+ if (ca & 0x80)
+ Term_putch(44, 20, 255, 255);
+ else
+ Term_putch(44, 20, 0, ' ');
+ }
+
+ /* Prompt */
+ Term_putstr(0, 22, -1, TERM_WHITE,
+ "Command (n/N/a/A/c/C/d): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') f = (f + max_f_idx + 1) % max_f_idx;
+ if (i == 'N') f = (f + max_f_idx - 1) % max_f_idx;
+ if (i == 'a') f_info[f].x_attr = (byte)(ca + 1);
+ if (i == 'A') f_info[f].x_attr = (byte)(ca - 1);
+ if (i == 'c') f_info[f].x_char = (byte)(cc + 1);
+ if (i == 'C') f_info[f].x_char = (byte)(cc - 1);
+ if (i == 'd')
+ {
+ f_info[f].x_char = f_ptr->d_char;
+ f_info[f].x_attr = f_ptr->d_attr;
+ }
+ }
+ }
+
+#endif
+
+ /* Reset visuals */
+ else if (i == '0')
+ {
+ /* Reset */
+ reset_visuals();
+
+ /* Message */
+ msg_print("Visual attr/char tables reset.");
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Interact with "colors"
+ */
+void do_cmd_colors(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char tmp[160];
+
+ char buf[1024];
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Interact with Colors", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Load a user pref file", 4, 5);
+#ifdef ALLOW_COLORS
+ prt("(2) Dump colors", 5, 5);
+ prt("(3) Modify colors", 6, 5);
+# ifdef SUPPORT_GAMMA
+ prt("(4) Gamma correction", 7, 5);
+# endif /* SUPPORT_GAMMA */
+#endif
+
+ /* Prompt */
+ prt("Command: ", 8, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Load a 'pref' file */
+ if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 8, 0);
+
+ /* Prompt */
+ prt("File: ", 10, 0);
+
+ /* Default file */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Query */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Process the given filename */
+ (void)process_pref_file(tmp);
+
+ /* Mega-Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Mega-Hack -- redraw */
+ Term_redraw();
+ }
+
+#ifdef ALLOW_COLORS
+
+ /* Dump colors */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Dump colors", 8, 0);
+
+ /* Prompt */
+ prt("File: ", 10, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Color redefinitions\n\n");
+
+ /* Dump colors */
+ for (i = 0; i < 256; i++)
+ {
+ int kv = angband_color_table[i][0];
+ int rv = angband_color_table[i][1];
+ int gv = angband_color_table[i][2];
+ int bv = angband_color_table[i][3];
+
+ cptr name = "unknown";
+
+ /* Skip non-entries */
+ if (!kv && !rv && !gv && !bv) continue;
+
+ /* Extract the color name */
+ if (i < 16) name = color_names[i];
+
+ /* Dump a comment */
+ fprintf(fff, "# Color '%s'\n", name);
+
+ /* Dump the monster attr/char info */
+ fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n",
+ i, kv, rv, gv, bv);
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped color redefinitions.");
+ }
+
+ /* Edit colors */
+ else if (i == '3')
+ {
+ static byte a = 0;
+
+ /* Prompt */
+ prt("Command: Modify colors", 8, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ cptr name;
+
+ /* Clear */
+ clear_from(10);
+
+ /* Exhibit the normal colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Exhibit this color */
+ Term_putstr(i*4, 20, -1, a, "###");
+
+ /* Exhibit all colors */
+ Term_putstr(i*4, 22, -1, i, format("%3d", i));
+ }
+
+ /* Describe the color */
+ name = ((a < 16) ? color_names[a] : "undefined");
+
+ /* Describe the color */
+ Term_putstr(5, 10, -1, TERM_WHITE,
+ format("Color = %d, Name = %s", a, name));
+
+ /* Label the Current values */
+ Term_putstr(5, 12, -1, TERM_WHITE,
+ format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x",
+ angband_color_table[a][0],
+ angband_color_table[a][1],
+ angband_color_table[a][2],
+ angband_color_table[a][3]));
+
+ /* Prompt */
+ Term_putstr(0, 14, -1, TERM_WHITE,
+ "Command (n/N/k/K/r/R/g/G/b/B): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') a = (byte)(a + 1);
+ if (i == 'N') a = (byte)(a - 1);
+ if (i == 'k') angband_color_table[a][0] = (byte)(angband_color_table[a][0] + 1);
+ if (i == 'K') angband_color_table[a][0] = (byte)(angband_color_table[a][0] - 1);
+ if (i == 'r') angband_color_table[a][1] = (byte)(angband_color_table[a][1] + 1);
+ if (i == 'R') angband_color_table[a][1] = (byte)(angband_color_table[a][1] - 1);
+ if (i == 'g') angband_color_table[a][2] = (byte)(angband_color_table[a][2] + 1);
+ if (i == 'G') angband_color_table[a][2] = (byte)(angband_color_table[a][2] - 1);
+ if (i == 'b') angband_color_table[a][3] = (byte)(angband_color_table[a][3] + 1);
+ if (i == 'B') angband_color_table[a][3] = (byte)(angband_color_table[a][3] - 1);
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Hack -- redraw */
+ Term_redraw();
+ }
+ }
+
+# ifdef SUPPORT_GAMMA
+
+ /* Gamma correction */
+ else if (i == '4')
+ {
+ int gamma;
+
+ /* Prompt */
+ prt("Command: Gamma correction", 8, 0);
+
+ /* gamma_val isn't set - assume 1.0 */
+ if (gamma_val == 0) gamma = 10;
+
+ /* It's set - convert to usual notation (times 10) */
+ else gamma = 2560 / gamma_val;
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ /* Clear */
+ clear_from(10);
+
+ /* Exhibit the normal colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Exhibit all colors */
+ Term_putstr(i*4, 22, -1, i, format("%3d", i));
+ }
+
+ /* Describe the gamma */
+ Term_putstr(5, 10, -1, TERM_WHITE,
+ format("Gamma = %d.%d", gamma / 10, gamma % 10));
+
+ /* Prompt */
+ Term_putstr(0, 12, -1, TERM_WHITE, "Command (g/G): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'g') gamma = (byte)(gamma + 1);
+ else if (i == 'G') gamma = (byte)(gamma - 1);
+ else continue;
+
+ /* Force limits ([1.0, 2.5]) */
+ if (gamma < 10) gamma = 10;
+ if (gamma > 25) gamma = 25;
+
+ /* Hack - 1.0 means no correction */
+ if (gamma == 10) gamma_val = 0;
+
+ /* otherwise, calculate gamma_val */
+ else gamma_val = 2560 / gamma;
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Hack -- redraw */
+ Term_redraw();
+ }
+ }
+
+# endif /* SUPPORT_GAMMA */
+
+#endif
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Take notes. There are two ways this can happen, either in the message
+ * recall or a file.
+ */
+void do_cmd_note(void)
+{
+ char buf[80];
+
+
+ /* Default */
+ strcpy(buf, "");
+
+ if (!get_string("Note: ", buf, 60)) return;
+
+ /* Ignore empty notes */
+ if (!buf[0] || (buf[0] == ' ')) return;
+
+ if (take_notes)
+ {
+ /* Add note to file */
+ add_note(buf, ' ');
+ }
+ else
+ {
+ /* Add note to message recall */
+ msg_format("Note: %s", buf);
+ }
+}
+
+
+/*
+ * Mention the current version
+ */
+void do_cmd_version(void)
+{
+ cptr author, email;
+
+ call_lua("get_module_info", "(s,d)", "s", "author", 1, &author);
+ call_lua("get_module_info", "(s,d)", "s", "author", 2, &email);
+
+ /* Silly message */
+ msg_format("You are playing %s %d.%d.%d%s made by %s (%s).",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS,
+ author, email);
+ call_lua("patchs_display", "()", "");
+}
+
+
+
+/*
+ * Array of feeling strings
+ */
+static cptr do_cmd_feeling_text[11] =
+{
+ "Looks like any other level.",
+ "You feel there is something special about this level.",
+ "You have a superb feeling about this level.",
+ "You have an excellent feeling...",
+ "You have a very good feeling...",
+ "You have a good feeling...",
+ "You feel strangely lucky...",
+ "You feel your luck is turning...",
+ "You like the look of this place...",
+ "This level can't be all bad...",
+ "What a boring place..."
+};
+
+
+/*
+ * Note that "feeling" is set to zero unless some time has passed.
+ * Note that this is done when the level is GENERATED, not entered.
+ */
+void do_cmd_feeling(void)
+{
+ /* Verify the feeling */
+ if (feeling < 0) feeling = 0;
+ if (feeling > 10) feeling = 10;
+
+ /* Feeling of the fate */
+ if (fate_flag && !(dungeon_flags2 & DF2_SPECIAL) && !p_ptr->inside_quest)
+ {
+ msg_print("You feel that you will meet your fate here.");
+ }
+
+ /* Hooked feelings ? */
+ if (process_hooks(HOOK_FEELING, "(d)", is_quest(dun_level)))
+ {
+ return;
+ }
+
+ /* No useful feeling in special levels */
+ if (dungeon_flags2 & DF2_DESC)
+ {
+ char buf[1024];
+
+ if ((get_dungeon_save(buf)) || (generate_special_feeling) || (dungeon_flags2 & DF2_DESC_ALWAYS))
+ {
+ if (!get_level_desc(buf)) msg_print("Someone forgot to describe this level!");
+ else msg_print(buf);
+ return;
+ }
+ }
+
+ /* No useful feeling in quests */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("Looks like a typical quest level.");
+ return;
+ }
+
+ /* Display feelings in the dungeon, nothing on the surface */
+ if (dun_level)
+ {
+ /* This could be simplified with a correct p_ptr->town_num */
+ int i, town_level = 0;
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ if (town_level)
+ msg_print("You hear the sound of a market.");
+ else
+ msg_print(do_cmd_feeling_text[feeling]);
+ }
+ return;
+}
+
+
+
+/*
+ * Encode the screen colors
+ */
+static char hack[17] = "dwsorgbuDWvyRGBU";
+
+
+/*
+ * Hack -- load a screen dump from a file
+ */
+void do_cmd_load_screen(void)
+{
+ int i, y, x;
+
+ int wid, hgt;
+ int len;
+
+ byte a = 0;
+ char c = ' ';
+
+ bool okay = TRUE;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt");
+
+ /* Append to the file */
+ fff = my_fopen(buf, "r");
+
+ /* Oops */
+ if (!fff) return;
+
+
+ /* Retrieve the current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Load the screen */
+ for (y = 0; okay; y++)
+ {
+ /* Get a line of data */
+ if (my_fgets(fff, buf, 1024)) okay = FALSE;
+
+ /* Stop on blank line */
+ if (!buf[0]) break;
+
+ /* Ignore off screen lines */
+ if (y >= hgt) continue;
+
+ /* Get width */
+ len = strlen(buf);
+
+ /* Truncate if it's longer than current screen width */
+ if (len > wid) len = wid;
+
+ /* Show each row */
+ for (x = 0; x < len; x++)
+ {
+ /* Put the attr/char */
+ Term_draw(x, y, TERM_WHITE, buf[x]);
+ }
+ }
+
+ /* Dump the screen */
+ for (y = 0; okay; y++)
+ {
+ /* Get a line of data */
+ if (my_fgets(fff, buf, 1024)) okay = FALSE;
+
+ /* Stop on blank line */
+ if (!buf[0]) break;
+
+ /* Ignore off screen lines */
+ if (y >= hgt) continue;
+
+ /* Get width */
+ len = strlen(buf);
+
+ /* Truncate if it's longer than current screen width */
+ if (len > wid) len = wid;
+
+ /* Dump each row */
+ for (x = 0; x < len; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Look up the attr */
+ for (i = 0; i < 16; i++)
+ {
+ /* Use attr matches */
+ if (hack[i] == buf[x]) a = i;
+ }
+
+ /* Hack -- fake monochrome */
+ if (!use_color) a = TERM_WHITE;
+
+ /* Put the attr/char */
+ Term_draw(x, y, a, c);
+ }
+ }
+
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Screen dump loaded.");
+ msg_print(NULL);
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Redefinable "save_screen" action
+ */
+void (*screendump_aux)(void) = NULL;
+
+
+
+
+
+
+/*
+ * Hack -- save a screen dump to a file
+ */
+void do_cmd_save_screen(void)
+{
+ /* Do we use a special screendump function ? */
+ if (screendump_aux)
+ {
+ /* Dump the screen to a graphics file */
+ (*screendump_aux)();
+ }
+
+ /* Dump the screen as text */
+ else
+ {
+ int y, x;
+ int wid, hgt;
+
+ byte a = 0;
+ char c = ' ';
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt");
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff) return;
+
+
+ /* Retrieve the current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = hack[a & 0x0F];
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Screen dump saved.");
+ msg_print(NULL);
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+ }
+}
+
+
+/*
+ * Check the status of "artifacts"
+ */
+void do_cmd_knowledge_artifacts(void)
+{
+ int i, k, z, x, y;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ char base_name[80];
+
+ bool *okay, *okayk;
+
+
+ /* Allocate the "okay" array */
+ C_MAKE(okay, max_a_idx, bool);
+ C_MAKE(okayk, max_k_idx, bool);
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the artifacts */
+ for (k = 0; k < max_a_idx; k++)
+ {
+ artifact_type *a_ptr = &a_info[k];
+
+ /* Default */
+ okay[k] = FALSE;
+
+ /* Skip "empty" artifacts */
+ if (!a_ptr->name) continue;
+
+ /* Skip "uncreated" artifacts */
+ if (!a_ptr->cur_num) continue;
+
+ /* Assume okay */
+ okay[k] = TRUE;
+ }
+
+ for (k = 0; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Default */
+ okayk[k] = FALSE;
+
+ /* Skip "empty" artifacts */
+ if (!(k_ptr->flags3 & TR3_NORM_ART)) continue;
+
+ /* Skip "uncreated" artifacts */
+ if (!k_ptr->artifact) continue;
+
+ /* Assume okay */
+ okayk[k] = TRUE;
+ }
+
+ /* Check the dungeon */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Ignore random artifacts */
+ if (o_ptr->tval == TV_RANDART) continue;
+
+ /* Ignore non-artifacts */
+ if (!artifact_p(o_ptr)) continue;
+
+ /* Ignore known items */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Note the artifact */
+ if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ okayk[o_ptr->k_idx] = FALSE;
+ }
+ else
+ {
+ okay[o_ptr->name1] = FALSE;
+ }
+ }
+ }
+ }
+
+ /* Check monsters in the dungeon */
+ for (i = 0; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ /* Scan all objects the monster carries */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Ignore random artifacts */
+ if (o_ptr->tval == TV_RANDART) continue;
+
+ /* Ignore non-artifacts */
+ if (!artifact_p(o_ptr)) continue;
+
+ /* Ignore known items */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Note the artifact */
+ if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ okayk[o_ptr->k_idx] = FALSE;
+ }
+ else
+ {
+ okay[o_ptr->name1] = FALSE;
+ }
+ }
+ }
+
+ /* Check the p_ptr->inventory and equipment */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Ignore non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Ignore random artifacts */
+ if (o_ptr->tval == TV_RANDART) continue;
+
+ /* Ignore non-artifacts */
+ if (!artifact_p(o_ptr)) continue;
+
+ /* Ignore known items */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Note the artifact */
+ if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ okayk[o_ptr->k_idx] = FALSE;
+ }
+ else
+ {
+ okay[o_ptr->name1] = FALSE;
+ }
+ }
+
+ /* Scan the artifacts */
+ for (k = 0; k < max_a_idx; k++)
+ {
+ artifact_type *a_ptr = &a_info[k];
+
+ /* List "dead" ones */
+ if (!okay[k]) continue;
+
+ /* Paranoia */
+ strcpy(base_name, "Unknown Artifact");
+
+ /* Obtain the base object type */
+ z = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Real object */
+ if (z)
+ {
+ object_type forge;
+ object_type *q_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create fake object */
+ object_prep(q_ptr, z);
+
+ /* Make it an artifact */
+ q_ptr->name1 = k;
+
+ /* Spell in it ? no ! */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f5 & TR5_SPELL_CONTAIN)
+ q_ptr->pval2 = -1;
+
+ /* Describe the artifact */
+ object_desc_store(base_name, q_ptr, FALSE, 0);
+ }
+
+ /* Hack -- Build the artifact name */
+ fprintf(fff, " The %s\n", base_name);
+ }
+
+ for (k = 0; k < max_k_idx; k++)
+ {
+ /* List "dead" ones */
+ if (!okayk[k]) continue;
+
+ /* Paranoia */
+ strcpy(base_name, "Unknown Artifact");
+
+ /* Real object */
+ if (k)
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create fake object */
+ object_prep(q_ptr, k);
+
+ /* Describe the artifact */
+ object_desc_store(base_name, q_ptr, FALSE, 0);
+ }
+
+ /* Hack -- Build the artifact name */
+ fprintf(fff, " The %s\n", base_name);
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Artifacts Seen", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+
+ C_FREE(okay, max_a_idx, bool);
+ C_FREE(okayk, max_k_idx, bool);
+}
+
+
+/*
+ * Check the status of traps
+ */
+void do_cmd_knowledge_traps(void)
+{
+ int k;
+
+ FILE *fff;
+
+ trap_type *t_ptr;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the traps */
+ for (k = 0; k < max_t_idx; k++)
+ {
+ /* Get the trap */
+ t_ptr = &t_info[k];
+
+ /* Skip "empty" traps */
+ if (!t_ptr->name) continue;
+
+ /* Skip unidentified traps */
+ if (!t_ptr->ident) continue;
+
+ /* Hack -- Build the trap name */
+ fprintf(fff, " %s\n", t_name + t_ptr->name);
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Traps known", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Display known uniques
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+static void insert_sort_unique(int *sort_uniques, int *num, int r_idx)
+{
+ int i, j;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int level = r_ptr->level;
+
+
+ /* Hack -- Morgoth is always at the bottom of the list */
+ if (r_idx == 862) level = 20000;
+
+ /* Find the place */
+ for (i = 0; i < *num; i++)
+ {
+ monster_race *r2_ptr = &r_info[sort_uniques[i]];
+ int level2 = r2_ptr->level;
+
+ if (sort_uniques[i] == 862) level2 = 20000;
+
+ if (level < level2) break;
+ }
+
+ /* Move the remaining items */
+ for (j = *num - 1; j >= i; j--)
+ {
+ sort_uniques[j + 1] = sort_uniques[j];
+ }
+
+ /* Insert it */
+ sort_uniques[i] = r_idx;
+ (*num)++;
+}
+
+
+static void do_cmd_knowledge_uniques(void)
+{
+ int k;
+
+ int *sort_uniques;
+
+ int num = 0;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ C_MAKE(sort_uniques, max_r_idx, int);
+
+ /* Sort the monster races */
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ /* Only print Uniques */
+ if (r_ptr->flags1 & (RF1_UNIQUE) &&
+ !(r_ptr->flags7 & RF7_PET) &&
+ !(r_ptr->flags7 & RF7_NEUTRAL))
+ {
+ insert_sort_unique(sort_uniques, &num, k);
+ }
+ }
+
+ /* Scan the monster races -- sorted */
+ for (k = 0; k < num; k++)
+ {
+ monster_race *r_ptr = &r_info[sort_uniques[k]];
+
+ /* Only print Uniques */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool dead = (r_ptr->max_num == 0);
+
+ /* Only display "known" uniques */
+ if (dead || cheat_know || r_ptr->r_sights)
+ {
+ /* Print a message */
+ if (dead)
+ {
+ /* Don't print the unique's ASCII symbol
+ * if use_graphics is on. */
+ if (use_graphics)
+ {
+ fprintf(fff, "[[[[[R%-70s is dead]\n",
+ (r_name + r_ptr->name));
+ }
+ else
+ {
+ fprintf(fff, "[[[[[%c%c] [[[[[R%-68s is dead]\n",
+ conv_color[r_ptr->d_attr],
+ r_ptr->d_char,
+ (r_name + r_ptr->name));
+ }
+ }
+ else
+ {
+ /* Don't print the unique's ASCII symbol
+ * if use_graphics is on. */
+ if (use_graphics)
+ {
+ fprintf(fff, "[[[[[w%-70s is alive]\n",
+ (r_name + r_ptr->name));
+ }
+ else
+ {
+ fprintf(fff, "[[[[[%c%c] [[[[[w%-68s is alive]\n",
+ conv_color[r_ptr->d_attr],
+ r_ptr->d_char,
+ (r_name + r_ptr->name));
+ }
+ }
+ }
+ }
+ }
+
+ C_FREE(sort_uniques, max_r_idx, int);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Known Uniques", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+void plural_aux(char *name)
+{
+ int name_len = strlen(name);
+
+ /* Hack -- Precedent must be pluralised for this one */
+ if (strstr(name, "Disembodied hand"))
+ {
+ strcpy(name, "Disembodied hands that strangled people");
+ }
+
+ /* "someone of something" */
+ else if (strstr(name, " of "))
+ {
+ cptr aider = strstr(name, " of ");
+ char dummy[80];
+ int i = 0;
+ cptr ctr = name;
+
+ while (ctr < aider)
+ {
+ dummy[i] = *ctr;
+ ctr++;
+ i++;
+ }
+
+ if (dummy[i - 1] == 's')
+ {
+ strcpy(&dummy[i], "es");
+ i++;
+ }
+ else
+ {
+ strcpy(&dummy[i], "s");
+ }
+
+ strcpy(&dummy[i + 1], aider);
+ strcpy(name, dummy);
+ }
+
+ /* Creeping coins */
+ else if (strstr(name, "coins"))
+ {
+ char dummy[80];
+ strcpy(dummy, "piles of ");
+ strcat(dummy, name);
+ strcpy(name, dummy);
+ return;
+ }
+
+ /* Manes stay manes */
+ else if (strstr(name, "Manes"))
+ {
+ return;
+ }
+
+ /* Broken plurals are, well, broken */
+ else if (name[name_len - 1] == 'y')
+ {
+ strcpy(&name[name_len - 1], "ies");
+ }
+ else if (streq(&name[name_len - 4], "ouse"))
+ {
+ strcpy(&name[name_len - 4], "ice");
+ }
+ else if (streq(&name[name_len - 6], "kelman"))
+ {
+ strcpy(&name[name_len - 6], "kelmen");
+ }
+ else if (streq(&name[name_len - 2], "ex"))
+ {
+ strcpy(&name[name_len - 2], "ices");
+ }
+ else if (streq(&name[name_len - 3], "olf"))
+ {
+ strcpy(&name[name_len - 3], "olves");
+ }
+
+ /* Now begins sane cases */
+ else if ((streq(&name[name_len - 2], "ch")) || (name[name_len - 1] == 's'))
+ {
+ strcpy(&name[name_len], "es");
+ }
+ else
+ {
+ strcpy(&name[name_len], "s");
+ }
+}
+
+
+/*
+ * Display current pets
+ */
+static void do_cmd_knowledge_pets(void)
+{
+ int i;
+
+ FILE *fff;
+
+ monster_type *m_ptr;
+
+ monster_race *r_ptr;
+
+ int t_friends = 0;
+
+ int t_levels = 0;
+
+ int show_upkeep = 0;
+
+ int upkeep_divider = 20;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ if (has_ability(AB_PERFECT_CASTING)) upkeep_divider = 15;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Calculate "upkeep" for friendly monsters */
+ if (m_ptr->status >= MSTATUS_PET)
+ {
+ char pet_name[80];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ t_friends++;
+ t_levels += m_ptr->level;
+ monster_desc(pet_name, m_ptr, 0x88);
+ fprintf(fff, "%s%s (%s)\n",
+ (r_ptr->flags1 & RF1_UNIQUE) ? "#####G" : "",
+ pet_name,
+ (m_ptr->status < MSTATUS_COMPANION) ? "pet" : "companion");
+ }
+ }
+
+ if (t_friends > 1 + (p_ptr->lev / (upkeep_divider)))
+ {
+ show_upkeep = (t_levels);
+
+ if (show_upkeep > 100) show_upkeep = 100;
+ else if (show_upkeep < 10) show_upkeep = 10;
+ }
+
+
+ fprintf(fff, "----------------------------------------------\n");
+ fprintf(fff, " Total: %d pet%s.\n", t_friends, (t_friends == 1 ? "" : "s"));
+ fprintf(fff, " Upkeep: %d%% mana.\n", show_upkeep);
+
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Current Pets", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+
+/*
+ * Total kill count
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+static void do_cmd_knowledge_kill_count(void)
+{
+ int k;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ s32b Total = 0;
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ {
+ /* Monsters slain */
+ int kk;
+
+ /* For all monsters */
+ for (kk = 1; kk < max_r_idx; kk++)
+ {
+ monster_race *r_ptr = &r_info[kk];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool dead = (r_ptr->max_num == 0);
+
+ if (dead)
+ {
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+
+ if (Total < 1)
+ {
+ fprintf(fff, "You have defeated no enemies yet.\n\n");
+ }
+ else if (Total == 1)
+ {
+ fprintf(fff, "You have defeated one enemy.\n\n");
+ }
+ else
+ {
+ fprintf(fff, "You have defeated %lu enemies.\n\n", Total);
+ }
+ }
+
+ Total = 0;
+
+ /* Scan the monster races */
+ for (k = 0; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool dead = (r_ptr->max_num == 0);
+
+ if (dead)
+ {
+ /* Print a message */
+ fprintf(fff, " %s\n",
+ (r_name + r_ptr->name));
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ if (This < 2)
+ {
+ if (strstr(r_name + r_ptr->name, "coins"))
+ {
+ fprintf(fff, " 1 pile of %s\n", (r_name + r_ptr->name));
+ }
+ else
+ {
+ fprintf(fff, " 1 %s\n", (r_name + r_ptr->name));
+ }
+ }
+ else
+ {
+ char to_plural[80];
+ strcpy(to_plural, (r_name + r_ptr->name));
+ plural_aux(to_plural);
+ fprintf(fff, " %d %s\n", This, to_plural);
+ }
+
+ Total += This;
+ }
+ }
+ }
+
+ fprintf(fff, "----------------------------------------------\n");
+ fprintf(fff, " Total: %lu creature%s killed.\n", Total, (Total == 1 ? "" : "s"));
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Kill Count", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Display known objects
+ */
+static void do_cmd_knowledge_objects(void)
+{
+ int k;
+
+ FILE *fff;
+
+ char o_name[80];
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the object kinds */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Hack -- skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* List known flavored objects */
+ if (k_ptr->flavor && k_ptr->aware)
+ {
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Create fake object */
+ object_prep(i_ptr, k);
+
+ /* Describe the object */
+ object_desc_store(o_name, i_ptr, FALSE, 0);
+
+ /* Print a message */
+ fprintf(fff, " %s\n", o_name);
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Known Objects", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * List recall depths
+ */
+static void do_cmd_knowledge_dungeons(void)
+{
+ int y;
+ char file_name[1024];
+ FILE *fff;
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Oops */
+ if (fff == NULL) return;
+
+ /* Scan all dungeons */
+ for (y = 1; y < max_d_idx; y++)
+ {
+ /* The dungeon has a valid recall depth set */
+ if (max_dlv[y])
+ {
+ /* Describe the recall depth */
+ fprintf(fff, " %c%s: Level %d (%d')\n",
+ (p_ptr->recall_dungeon == y) ? '*' : ' ',
+ d_name + d_info[y].name,
+ max_dlv[y], 50 * (max_dlv[y]));
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Recall Depths", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * List known towns
+ */
+void do_cmd_knowledge_towns(void)
+{
+ int i, j;
+ char file_name[1024];
+ FILE *fff;
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Oops */
+ if (fff == NULL) return;
+
+ /* Scan all dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+
+ /* Scan all dungeon town slots */
+ for (j = 0; j < TOWN_DUNGEON; j++)
+ {
+ int town_idx = d_ptr->t_idx[j];
+
+ /* Ignore non-existent towns */
+ if (!(town_info[town_idx].flags & (TOWN_REAL))) continue;
+
+ /* Ignore unknown towns */
+ if (!(town_info[town_idx].flags & (TOWN_KNOWN))) continue;
+
+ /* Describe the dungeon town */
+ fprintf(fff, " %s: Level %d (%d')\n",
+ d_name + d_ptr->name,
+ d_ptr->t_level[j],
+ 50 * d_ptr->t_level[j]);
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Dungeon Towns", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * List corruptions
+ */
+void do_cmd_knowledge_corruptions(void)
+{
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Dump the corruptions to file */
+ if (fff) dump_corruptions(fff, TRUE);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Corruptions", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Helper function for do_cmd_knowledge_quests
+ */
+static void insert_sort_quest(int *order, int *num, int q_idx)
+{
+ int i, j;
+
+ quest_type *q_ptr = &quest[q_idx];
+
+ int level = q_ptr->level;
+
+
+ /* Find the place */
+ for (i = 0; i < *num; i++)
+ {
+ quest_type *q2_ptr = &quest[order[i]];
+ int level2 = q2_ptr->level;
+
+ if (level < level2) break;
+ }
+
+ /* Move the remaining items */
+ for (j = *num - 1; j >= i; j--)
+ {
+ order[j + 1] = order[j];
+ }
+
+ /* Insert it */
+ order[i] = q_idx;
+ (*num)++;
+}
+
+
+/*
+ * Print quest status of all active quests
+ */
+static void do_cmd_knowledge_quests(void)
+{
+ FILE *fff;
+
+ char file_name[1024];
+
+ int *order;
+
+ int num = 0;
+
+ int i, j, z;
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ C_MAKE(order, max_q_idx, int);
+
+ for (i = 0; i < max_q_idx; i++)
+ {
+ insert_sort_quest(order, &num, i);
+ }
+
+ for (z = 0; z < max_q_idx; z++)
+ {
+ i = order[z];
+
+ /* Dynamic quests */
+ if (quest[i].dynamic_desc)
+ {
+ /* Random quests */
+ if (i == QUEST_RANDOM)
+ {
+ /**/
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) continue;
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) continue;
+ if (!random_quests[dun_level].type) continue;
+ if (random_quests[dun_level].done) continue;
+ if (p_ptr->inside_quest) continue;
+ if (!dun_level) continue;
+
+ if (!is_randhero(dun_level))
+ {
+ fprintf(fff, "#####yCaptured princess!\n");
+ fprintf(fff, "A princess is being held prisoner and tortured here!\n");
+ fprintf(fff, "Save her from the horrible %s.\n",
+ r_info[random_quests[dun_level].r_idx].name + r_name);
+ }
+ else
+ {
+ fprintf(fff, "#####yLost sword!\n");
+ fprintf(fff, "An adventurer lost his sword to a bunch of %s!\n",
+ r_info[random_quests[dun_level].r_idx].name + r_name);
+ fprintf(fff, "Kill them all to get it back.\n");
+ }
+ fprintf(fff, "Number: %d, Killed: %ld.\n",
+ random_quests[dun_level].type, quest[QUEST_RANDOM].data[0]);
+ fprintf(fff, "\n");
+ }
+ /* MUST be a lua quest */
+ else
+ {
+ hook_file = fff;
+ exec_lua(format("__quest_dynamic_desc[%d]()", i));
+ }
+ }
+
+ /* Fixed quests (only known ones) */
+ else if (!quest[i].silent)
+ {
+ if (quest[i].status == QUEST_STATUS_TAKEN)
+ {
+ /* Print the quest info */
+ fprintf(fff, "#####y%s (Danger level: %d)\n",
+ quest[i].name, quest[i].level);
+
+ j = 0;
+ while ((j < 10) && (quest[i].desc[j][0] != '\0'))
+ {
+ fprintf(fff, "%s\n", quest[i].desc[j++]);
+ }
+ fprintf(fff, "\n");
+ }
+ else if (quest[i].status == QUEST_STATUS_COMPLETED)
+ {
+ fprintf(fff , "#####G%s Completed - Unrewarded\n", quest[i].name);
+ fprintf(fff, "\n");
+ }
+ }
+ }
+
+ C_FREE(order, max_q_idx, int);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Quest status", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Print fate status
+ */
+static void do_cmd_knowledge_fates(void)
+{
+ FILE *fff;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ dump_fates(fff);
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Fate status", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
+
+
+/*
+ * Print the note file
+ */
+void do_cmd_knowledge_notes(void)
+{
+ /* Spawn */
+ show_notes_file();
+
+ /* Done */
+ return;
+}
+
+
+/*
+ * Interact with "knowledge"
+ */
+void do_cmd_knowledge(void)
+{
+ int i;
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Display current knowledge", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Display known artifacts", 4, 5);
+ prt("(2) Display known uniques", 5, 5);
+ prt("(3) Display known objects", 6, 5);
+ prt("(4) Display kill count", 7, 5);
+ prt("(5) Display recall depths", 8, 5);
+ prt("(6) Display corruptions", 9, 5);
+ prt("(7) Display current pets", 10, 5);
+ prt("(8) Display current quests", 11, 5);
+ prt("(9) Display current fates", 12, 5);
+ prt("(0) Display known traps", 13, 5);
+ prt("(A) Display known dungeon towns", 14, 5);
+ if (take_notes) prt("(B) Display notes", 15, 5);
+
+ /* Prompt */
+ prt("Command: ", 17, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ switch (i)
+ {
+ /* Artifacts */
+ case '1':
+ {
+ do_cmd_knowledge_artifacts();
+
+ break;
+ }
+
+ /* Uniques */
+ case '2':
+ {
+ do_cmd_knowledge_uniques();
+
+ break;
+ }
+
+ /* Objects */
+ case '3':
+ {
+ do_cmd_knowledge_objects();
+
+ break;
+ }
+
+ /* Kill count */
+ case '4':
+ {
+ do_cmd_knowledge_kill_count();
+
+ break;
+ }
+
+ /* Recall depths */
+ case '5':
+ {
+ do_cmd_knowledge_dungeons();
+
+ break;
+ }
+
+ /* corruptions */
+ case '6':
+ {
+ do_cmd_knowledge_corruptions();
+
+ break;
+ }
+
+ /* Pets */
+ case '7':
+ {
+ do_cmd_knowledge_pets();
+
+ break;
+ }
+
+ /* Quests */
+ case '8':
+ {
+ do_cmd_knowledge_quests();
+
+ break;
+ }
+
+ /* Fates */
+ case '9':
+ {
+ do_cmd_knowledge_fates();
+
+ break;
+ }
+
+ /* Traps */
+ case '0':
+ {
+ do_cmd_knowledge_traps();
+
+ break;
+ }
+
+ /* Dungeon towns */
+ case 'A':
+ case 'a':
+ {
+ do_cmd_knowledge_towns();
+
+ break;
+ }
+
+ /* Notes */
+ case 'B':
+ case 'b':
+ {
+ if (take_notes) do_cmd_knowledge_notes();
+ else bell();
+
+ break;
+ }
+
+ /* Unknown option */
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Check on the status of an active quest -KMW-
+ * TODO: Spill out status when not a simple kill # monster.
+ */
+void do_cmd_checkquest(void)
+{
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Quest info */
+ do_cmd_knowledge_quests();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Change player's "tactic" setting
+ */
+void do_cmd_change_tactic(int i)
+{
+ p_ptr->tactic += i;
+ if (p_ptr->tactic > 8) p_ptr->tactic = 0;
+ if (p_ptr->tactic < 0) p_ptr->tactic = 8;
+
+ p_ptr->update |= (PU_BONUS);
+ update_stuff();
+ prt("", 0, 0);
+}
+
+
+/*
+ * Change player's "movement" setting
+ */
+void do_cmd_change_movement(int i)
+{
+ p_ptr->movement += i;
+ if (p_ptr->movement > 8) p_ptr->movement = 0;
+ if (p_ptr->movement < 0) p_ptr->movement = 8;
+
+ p_ptr->update |= (PU_BONUS);
+ update_stuff();
+ prt("", 0, 0);
+}
+
+
+/*
+ * Display the time and date
+ */
+void do_cmd_time()
+{
+ int day = bst(DAY, turn);
+
+ int hour = bst(HOUR, turn);
+
+ int min = bst(MINUTE, turn);
+
+ int full = hour * 100 + min;
+
+ char buf2[20];
+
+ int start = 9999;
+
+ int end = -9999;
+
+ int num = 0;
+
+ char desc[1024];
+
+ char buf[1024];
+
+ FILE *fff;
+
+
+ /* Note */
+ strcpy(desc, "It is a strange time.");
+
+ /* Format time of the day */
+ strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR));
+
+ /* Display current date in the Elvish calendar */
+ msg_format("This is %s of the %s year of the third age.",
+ get_month_name(day, wizard, FALSE), buf2);
+
+ /* Message */
+ msg_format("The time is %d:%02d %s.",
+ (hour % 12 == 0) ? 12 : (hour % 12),
+ min, (hour < 12) ? "AM" : "PM");
+
+ /* Find the path */
+ if (!rand_int(10) || p_ptr->image)
+ {
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "timefun.txt");
+ }
+ else
+ {
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "timenorm.txt");
+ }
+
+ /* Open this file */
+ fff = my_fopen(buf, "rt");
+
+ /* Oops */
+ if (!fff) return;
+
+ /* Find this time */
+ while (!my_fgets(fff, buf, 1024))
+ {
+ /* Ignore comments */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Ignore invalid lines */
+ if (buf[1] != ':') continue;
+
+ /* Process 'Start' */
+ if (buf[0] == 'S')
+ {
+ /* Extract the starting time */
+ start = atoi(buf + 2);
+
+ /* Assume valid for an hour */
+ end = start + 59;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'End' */
+ if (buf[0] == 'E')
+ {
+ /* Extract the ending time */
+ end = atoi(buf + 2);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Ignore incorrect range */
+ if ((start > full) || (full > end)) continue;
+
+ /* Process 'Description' */
+ if (buf[0] == 'D')
+ {
+ num++;
+
+ /* Apply the randomizer */
+ if (!rand_int(num)) strcpy(desc, buf + 2);
+
+ /* Next... */
+ continue;
+ }
+ }
+
+ /* Message */
+ msg_print(desc);
+
+ /* Close the file */
+ my_fclose(fff);
+}
+
+/*
+ * Macro recorder!
+ * It records all keypresses and then put them in a macro
+ * Not as powerful as the macro screen, but much easier for newbies
+ */
+char *macro_recorder_current = NULL;
+void macro_recorder_start()
+{
+ msg_print("Starting macro recording, press this key again to stop. Note that if the action you want to record accepts the @ key, use it; it will remove your the need to inscribe stuff.");
+ C_MAKE(macro_recorder_current, 1, char);
+ macro_recorder_current[0] = '\0';
+}
+
+void macro_recorder_add(char c)
+{
+ char *old_macro_recorder_current = macro_recorder_current;
+
+ if (macro_recorder_current == NULL) return;
+
+ C_MAKE(macro_recorder_current, strlen(macro_recorder_current) + 1 + 1, char);
+ sprintf(macro_recorder_current, "%s%c", old_macro_recorder_current, c);
+ C_FREE(old_macro_recorder_current, strlen(old_macro_recorder_current) + 1, char);
+}
+
+void macro_recorder_stop()
+{
+ char *str, *macro;
+ char buf[1024];
+
+ /* Ok we remove the last key, because it is the key to stop recording */
+ macro_recorder_current[strlen(macro_recorder_current) - 1] = '\0';
+
+ /* Stop the recording */
+ macro = macro_recorder_current;
+ macro_recorder_current = NULL;
+
+ /* Add it */
+ if (get_check("Are you satisfied and want to create the macro? "))
+ {
+ prt("Trigger: ", 0, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, FALSE);
+
+ /* Link the macro */
+ macro_add(buf, macro);
+
+ /* Prompt */
+ C_MAKE(str, (strlen(macro) + 1) * 3, char);
+ ascii_to_text(str, macro);
+ msg_format("Added a macro '%s'. If you want it to stay permanently, press @ now and dump macros to a file.", str);
+ C_FREE(str, (strlen(macro) + 1) * 3, char);
+ }
+
+ /* Ok now rid of useless stuff */
+ C_FREE(macro, strlen(macro) + 1, char);
+}
+
+void do_cmd_macro_recorder()
+{
+ if (macro_recorder_current == NULL)
+ macro_recorder_start();
+ else
+ macro_recorder_stop();
+}
diff --git a/src/cmd5.c b/src/cmd5.c
new file mode 100644
index 00000000..0e683486
--- /dev/null
+++ b/src/cmd5.c
@@ -0,0 +1,2583 @@
+/* File: cmd5.c */
+
+/* Purpose: Class commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+
+extern lua_State *L;
+
+
+/* Maximum number of tries for teleporting */
+#define MAX_TRIES 300
+
+bool is_school_book(object_type *o_ptr)
+{
+ if (o_ptr->tval == TV_BOOK)
+ {
+ return TRUE;
+ }
+ else if (o_ptr->tval == TV_DAEMON_BOOK)
+ {
+ return TRUE;
+ }
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* Does it contains a schooled spell ? */
+static bool hook_school_spellable(object_type *o_ptr)
+{
+ if (is_school_book(o_ptr))
+ return TRUE;
+ else
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Is it a book */
+bool item_tester_hook_browsable(object_type *o_ptr)
+{
+ if (hook_school_spellable(o_ptr)) return TRUE;
+ if (o_ptr->tval >= TV_BOOK) return TRUE;
+ return FALSE;
+}
+
+/*
+ * Are we using a mage staff
+ */
+bool is_magestaff()
+{
+ int i;
+
+
+ i = 0;
+
+ while (p_ptr->body_parts[i] == INVEN_WIELD)
+ {
+ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ /* Wielding a mage staff */
+ if ((o_ptr->k_idx) && (o_ptr->tval == TV_MSTAFF)) return (TRUE);
+
+ /* Next slot */
+ i++;
+
+ /* Paranoia */
+ if (i >= (INVEN_TOTAL - INVEN_WIELD)) break;
+ }
+
+ /* Not wielding a mage staff */
+ return (FALSE);
+}
+
+/*
+ * Peruse the spells/prayers in a book
+ *
+ * Note that *all* spells in the book are listed
+ *
+ * Note that browsing is allowed while confused or blind,
+ * and in the dark, primarily to allow browsing in stores.
+ */
+
+extern void do_cmd_browse_aux(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (is_school_book(o_ptr))
+ browse_school_spell(o_ptr->sval, o_ptr->pval, o_ptr);
+ else if (f5 & TR5_SPELL_CONTAIN && o_ptr->pval2 != -1)
+ browse_school_spell(255, o_ptr->pval2, o_ptr);
+}
+
+void do_cmd_browse(void)
+{
+ int item;
+
+ cptr q, s;
+
+ object_type *o_ptr;
+
+ /* Restrict choices to "useful" books */
+ item_tester_hook = item_tester_hook_browsable;
+
+ /* Get an item */
+ q = "Browse which book? ";
+ s = "You have no books that you can read.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_EQUIP | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ do_cmd_browse_aux(o_ptr);
+}
+
+void do_poly_wounds(void)
+{
+ /* Changed to always provide at least _some_ healing */
+ s16b wounds = p_ptr->cut;
+
+ s16b hit_p = (p_ptr->mhp - p_ptr->chp);
+
+ s16b change = damroll(p_ptr->lev, 5);
+
+ bool Nasty_effect = (randint(5) == 1);
+
+
+ if (!(wounds || hit_p || Nasty_effect)) return;
+
+ msg_print("Your wounds are polymorphed into less serious ones.");
+ hp_player(change);
+ if (Nasty_effect)
+ {
+ msg_print("A new wound was created!");
+ take_hit(change / 2, "a polymorphed wound");
+ set_cut(change);
+ }
+ else
+ {
+ set_cut((p_ptr->cut) - (change / 2));
+ }
+}
+
+void do_poly_self(void)
+{
+ int power = p_ptr->lev;
+ int poly_power;
+
+ msg_print("You feel a change coming over you...");
+
+ if ((power > rand_int(20)) && (rand_int(3) == 0))
+ {
+ char effect_msg[80] = "";
+ int new_race, expfact, goalexpfact;
+
+ /* Some form of racial polymorph... */
+ power -= 10;
+
+ if ((power > rand_int(5)) && (rand_int(4) == 0))
+ {
+ /* sex change */
+ power -= 2;
+
+ if (p_ptr->psex == SEX_MALE)
+ {
+ p_ptr->psex = SEX_FEMALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ strcpy(effect_msg, "female");
+ }
+ else
+ {
+ p_ptr->psex = SEX_MALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ strcpy(effect_msg, "male");
+ }
+ }
+
+ if ((power > rand_int(30)) && (rand_int(5) == 0))
+ {
+ int tmp = 0;
+
+ /* Harmful deformity */
+ power -= 15;
+
+ while (tmp < 6)
+ {
+ if ( rand_int(2) == 0)
+ {
+ (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0));
+ power -= 1;
+ }
+ tmp++;
+ }
+
+ /* Deformities are discriminated against! */
+ (void)dec_stat(A_CHR, randint(6), TRUE);
+
+ if (effect_msg[0])
+ {
+ char tmp_msg[10];
+ strnfmt(tmp_msg, 10, "%s", effect_msg);
+ strnfmt(effect_msg, 80, "deformed %s", tmp_msg);
+ }
+ else
+ {
+ strcpy(effect_msg, "deformed");
+ }
+ }
+
+ while ((power > rand_int(20)) && (rand_int(10) == 0))
+ {
+ /* Polymorph into a less corrupted form */
+ power -= 10;
+
+ lose_corruption(0);
+ }
+
+ /*
+ * I'm not sure 'power' is always positive, with *so* many minuses.
+ * Also, passing zero / negative numbers to randint/rand_int can
+ * cause a zero divide exception, IIRC, not to speak of its absurdity
+ * -- pelpel
+ */
+ poly_power = (power > 1) ? power : 1;
+
+ /*
+ * Restrict the race choices by exp penalty so weak polymorph
+ * always means weak race
+ */
+ goalexpfact = 100 + 3 * rand_int(poly_power);
+
+ /* Roll until an appropriate selection is made */
+ while (1)
+ {
+ new_race = rand_int(max_rp_idx);
+ expfact = race_info[new_race].r_exp;
+
+ if ((new_race != p_ptr->prace) && (expfact <= goalexpfact)) break;
+ }
+
+ if (effect_msg[0])
+ {
+ msg_format("You turn into a%s %s!",
+ ((is_a_vowel(rp_name[race_info[new_race].title])) ? "n" : ""),
+ race_info[new_race].title + rp_name);
+ }
+ else
+ {
+ msg_format("You turn into a %s %s!", effect_msg,
+ race_info[new_race].title);
+ }
+
+ p_ptr->prace = new_race;
+ rp_ptr = &race_info[p_ptr->prace];
+
+ /* Experience factor */
+ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp;
+
+ /* Calculate the height/weight */
+ get_height_weight();
+
+
+ check_experience();
+ p_ptr->max_plv = p_ptr->lev;
+
+ p_ptr->redraw |= (PR_BASIC);
+
+ p_ptr->update |= (PU_BONUS);
+
+ handle_stuff();
+ lite_spot(p_ptr->py, p_ptr->px);
+ }
+
+ if ((power > rand_int(30)) && (rand_int(6) == 0))
+ {
+ int tmp = 0;
+
+ /* Abomination! */
+ power -= 20;
+
+ msg_print("Your internal organs are rearranged!");
+ while (tmp < 6)
+ {
+ (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0));
+ tmp++;
+ }
+ if (rand_int(6) == 0)
+ {
+ msg_print("You find living difficult in your present form!");
+ take_hit(damroll(randint(10), p_ptr->lev), "a lethal corruption");
+ power -= 10;
+ }
+ }
+
+ if ((power > rand_int(20)) && (rand_int(4) == 0))
+ {
+ power -= 10;
+
+ do_cmd_rerate();
+ }
+
+ while ((power > rand_int(15)) && (rand_int(3) == 0))
+ {
+ power -= 7;
+ (void) gain_random_corruption(0);
+ }
+
+ if (power > rand_int(5))
+ {
+ power -= 5;
+ do_poly_wounds();
+ }
+
+ /* Note: earlier deductions may have left power < 0 already. */
+ while (power > 0)
+ {
+ corrupt_player();
+ power--;
+ }
+}
+
+
+/*
+ * Brand the current weapon
+ */
+void brand_weapon(int brand_type)
+{
+ object_type *o_ptr;
+
+ cptr act = NULL;
+
+ char o_name[80];
+
+
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /*
+ * You can never modify artifacts / ego-items
+ * You can never modify cursed items
+ *
+ * TY: You _can_ modify broken items (if you're silly enough)
+ */
+ if (!o_ptr->k_idx || artifact_p(o_ptr) || ego_item_p(o_ptr) ||
+ o_ptr->art_name || cursed_p(o_ptr))
+ {
+ if (flush_failure) flush();
+
+ msg_print("The Branding failed.");
+
+ return;
+ }
+
+
+ /* Save the old name */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ switch (brand_type)
+ {
+ case 6:
+ {
+ act = "glows with godly power.";
+ o_ptr->name2 = EGO_BLESS_BLADE;
+ o_ptr->pval = randint(4);
+
+ break;
+ }
+ case 5:
+ {
+ act = "seems very powerful.";
+ o_ptr->name2 = EGO_EARTHQUAKES;
+ o_ptr->pval = randint(3);
+
+ break;
+ }
+ case 4:
+ {
+ act = "seems very unstable now.";
+ o_ptr->name2 = EGO_DRAGON;
+ o_ptr->pval = randint(2);
+
+ break;
+ }
+ case 3:
+ {
+ act = "thirsts for blood!";
+ o_ptr->name2 = EGO_VAMPIRIC;
+
+ break;
+ }
+ case 2:
+ {
+ act = "is coated with poison.";
+ o_ptr->name2 = EGO_BRAND_POIS;
+
+ break;
+ }
+ case 1:
+ {
+ act = "is engulfed in raw chaos!";
+ o_ptr->name2 = EGO_CHAOTIC;
+
+ break;
+ }
+ default:
+ {
+ if (rand_int(100) < 25)
+ {
+ act = "is covered in a fiery shield!";
+ o_ptr->name2 = EGO_BRAND_FIRE;
+ }
+ else
+ {
+ act = "glows deep, icy blue!";
+ o_ptr->name2 = EGO_BRAND_COLD;
+ }
+ }
+ }
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ o_ptr->discount = 100;
+
+ msg_format("Your %s %s", o_name, act);
+
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+}
+
+/*
+ * Fetch an item (teleport it right underneath the caster)
+ */
+void fetch(int dir, int wgt, bool require_los)
+{
+ int ty, tx, i;
+
+ bool flag;
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Check to see if an object is already there */
+ if (cave[p_ptr->py][p_ptr->px].o_idx)
+ {
+ msg_print("You can't fetch when you're already standing on something.");
+ return;
+ }
+
+ /* Use a target */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+
+ if (distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE)
+ {
+ msg_print("You can't fetch something that far away!");
+ return;
+ }
+
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->o_idx)
+ {
+ msg_print("There is no object at this place.");
+ return;
+ }
+
+ if (require_los && (!player_has_los_bold(ty, tx)))
+ {
+ msg_print("You have no direct line of sight to that location.");
+ return;
+ }
+ }
+ else
+ {
+ /* Use a direction */
+ ty = p_ptr->py; /* Where to drop the item */
+ tx = p_ptr->px;
+ flag = FALSE;
+
+ while (1)
+ {
+ ty += ddy[dir];
+ tx += ddx[dir];
+ c_ptr = &cave[ty][tx];
+
+ if ((distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) ||
+ !cave_floor_bold(ty, tx)) return;
+
+ if (c_ptr->o_idx) break;
+ }
+ }
+
+ o_ptr = &o_list[c_ptr->o_idx];
+
+ if (o_ptr->weight > wgt)
+ {
+ /* Too heavy to 'fetch' */
+ msg_print("The object is too heavy.");
+ return;
+ }
+
+ i = c_ptr->o_idx;
+ c_ptr->o_idx = o_ptr->next_o_idx;
+ cave[p_ptr->py][p_ptr->px].o_idx = i; /* 'move' it */
+ o_ptr->next_o_idx = 0;
+ o_ptr->iy = p_ptr->py;
+ o_ptr->ix = p_ptr->px;
+
+ object_desc(o_name, o_ptr, TRUE, 0);
+ msg_format("%^s flies through the air to your feet.", o_name);
+
+ note_spot(p_ptr->py, p_ptr->px);
+ p_ptr->redraw |= PR_MAP;
+}
+
+
+/*
+ * Handle random effects of player shrieking
+ */
+void shriek_effect()
+{
+ switch (randint(9))
+ {
+ case 1:
+ case 5:
+ case 8:
+ case 9:
+ {
+ msg_print("You make a high-pitched shriek!");
+ aggravate_monsters(1);
+
+ break;
+ }
+ case 2:
+ case 6:
+ {
+ msg_print("Oops! You call a monster.");
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0);
+
+ break;
+ }
+ case 3:
+ case 7:
+ {
+ msg_print("The dungeon collapses!");
+ earthquake(p_ptr->py, p_ptr->px, 5);
+
+ break;
+ }
+ case 4:
+ {
+ msg_print("Your shriek is so horrible that you damage your health!");
+ take_hit(damroll(p_ptr->lev / 5, 8), "inner hemorrhaging");
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Like all the random effect codes, this is *ugly*,
+ * and there is not a single line of comment, so I can't tell
+ * some fall throughs are really intended. Well, I know it's
+ * intended to be bizarre :) -- pelpel
+ */
+void wild_magic(int spell)
+{
+ int counter = 0;
+ int type = SUMMON_BIZARRE1 - 1 + randint(6);
+
+ if (type < SUMMON_BIZARRE1) type = SUMMON_BIZARRE1;
+ else if (type > SUMMON_BIZARRE6) type = SUMMON_BIZARRE6;
+
+ switch (randint(spell) + randint(8) + 1)
+ {
+ case 1:
+ case 2:
+ case 3:
+ {
+ teleport_player(10);
+
+ break;
+ }
+
+ case 4:
+ case 5:
+ case 6:
+ {
+ teleport_player(100);
+
+ break;
+ }
+
+ case 7:
+ case 8:
+ {
+ teleport_player(200);
+
+ break;
+ }
+
+ case 9:
+ case 10:
+ case 11:
+ {
+ unlite_area(10, 3);
+
+ break;
+ }
+
+ case 12:
+ case 13:
+ case 14:
+ {
+ lite_area(damroll(2, 3), 2);
+
+ break;
+ }
+
+ case 15:
+ {
+ destroy_doors_touch();
+
+ break;
+ }
+
+ case 16:
+ case 17:
+ {
+ wall_breaker();
+
+ /* I don't think this is a fall through -- pelpel */
+ break;
+ }
+
+ case 18:
+ {
+ sleep_monsters_touch();
+
+ break;
+ }
+
+ case 19:
+ case 20:
+ {
+ trap_creation();
+
+ break;
+ }
+
+ case 21:
+ case 22:
+ {
+ door_creation();
+
+ break;
+ }
+
+ case 23:
+ case 24:
+ case 25:
+ {
+ aggravate_monsters(1);
+
+ break;
+ }
+
+ case 26:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 5);
+
+ break;
+ }
+
+ case 27:
+ case 28:
+ {
+ break;
+ }
+
+ case 29:
+ case 30:
+ {
+ apply_disenchant(0);
+
+ break;
+ }
+
+ case 31:
+ {
+ lose_all_info();
+
+ break;
+ }
+
+ case 32:
+ {
+ fire_ball(GF_CHAOS, 0, spell + 5, 1 + (spell / 10));
+
+ break;
+ }
+
+ case 33:
+ {
+ wall_stone(p_ptr->py, p_ptr->px);
+
+ break;
+ }
+
+ case 34:
+ case 35:
+ {
+ while (counter++ < 8)
+ {
+ (void) summon_specific(p_ptr->py, p_ptr->px, (dun_level * 3) / 2, type);
+ }
+
+ break;
+ }
+
+ case 36:
+ case 37:
+ {
+ activate_hi_summon();
+
+ break;
+ }
+
+ case 38:
+ {
+ summon_cyber();
+
+ /* I don't think this is a fall through -- pelpel */
+ break;
+ }
+
+ default:
+ {
+ activate_ty_curse();
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * Hack -- Determine if the player is wearing an artefact ring
+ * specified by art_type, that should be an index into a_info
+ */
+bool check_ring(int art_type)
+{
+ int i;
+
+
+ /* We are only interested in ring slots */
+ i = INVEN_RING;
+
+ /* Scan the list of rings until we reach the end */
+ while (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_RING)
+ {
+ /* Found the ring we were looking for */
+ if (p_ptr->inventory[i].k_idx && (p_ptr->inventory[i].name1 == art_type))
+ {
+ return (TRUE);
+ }
+
+ /* Next item */
+ i++;
+ }
+
+ /* Found nothing */
+ return (FALSE);
+}
+
+/*
+ * Return the symbiote's name or description.
+ */
+cptr symbiote_name(bool capitalize)
+{
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ static char buf[80];
+
+ /* Make sure there actually is a symbiote there... */
+ if (!o_ptr->k_idx)
+ {
+ strcpy(buf, "A non-existent symbiote");
+ }
+ else
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+ cptr s = NULL;
+
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ /* Unique monster; no preceding "your", and ignore our name. */
+ strncpy(buf, r_name + r_ptr->name, sizeof(buf));
+ }
+ else if (o_ptr->note &&
+ (s = strstr(quark_str(o_ptr->note), "#named ")) != NULL)
+ {
+ /* We've named it. */
+ strncpy(buf, s + 7, sizeof(buf));
+ }
+ else
+ {
+ /* No special cases, just return "Your <monster type>". */
+ strcpy(buf, "your ");
+ strncpy(buf + 5, r_name + r_ptr->name, sizeof(buf) - 5);
+ }
+ }
+
+ /* Just in case... */
+ buf[sizeof(buf) - 1] = '\0';
+ if (capitalize) buf[0] = toupper(buf[0]);
+ return buf;
+}
+
+/*
+ * Use a power of the monster in symbiosis
+ */
+int use_symbiotic_power(int r_idx, bool great, bool only_number, bool no_cost)
+{
+ int power = -1;
+
+ int num = 0, dir = 0 , i;
+
+ int powers[96];
+
+ bool flag, redraw;
+
+ int ask, plev = p_ptr->lev;
+
+ char choice;
+
+ char out_val[160];
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ int x = p_ptr->px, y = p_ptr->py, k;
+
+ int rad;
+
+ int label;
+
+
+ /* List the monster powers -- RF4_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags4 & BIT(i))
+ {
+ if (monster_powers[i].great && (!great)) continue;
+ if (!monster_powers[i].power) continue;
+ powers[num++] = i;
+ }
+ }
+
+ /* List the monster powers -- RF5_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags5 & BIT(i))
+ {
+ if (monster_powers[i + 32].great && (!great)) continue;
+ if (!monster_powers[i + 32].power) continue;
+ powers[num++] = i + 32;
+ }
+ }
+
+ /* List the monster powers -- RF6_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags6 & BIT(i))
+ {
+ if (monster_powers[i + 64].great && (!great)) continue;
+ if (!monster_powers[i + 64].power) continue;
+ powers[num++] = i + 64;
+ }
+ }
+
+ if (!num)
+ {
+ msg_print("You have no powers you can use.");
+ return (0);
+ }
+
+ if (only_number) return (num);
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Get the last label */
+ label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26);
+
+ /* Build a prompt (accept all spells) */
+ /* Mega Hack -- if no_cost is false, we're actually a Possessor -dsb */
+ strnfmt(out_val, 78,
+ "(Powers a-%c, *=List, ESC=exit) Use which power of your %s? ",
+ label, (no_cost ? "symbiote" : "body"));
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt ("", y++, x);
+
+ while (ctr < num)
+ {
+ monster_power *mp_ptr = &monster_powers[powers[ctr]];
+ int mana = mp_ptr->mana / 10;
+
+ if (mana > p_ptr->msp) mana = p_ptr->msp;
+
+ if (!mana) mana = 1;
+
+ label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26);
+
+ if (!no_cost)
+ {
+ strnfmt(dummy, 80, " %c) %2d %s",
+ label, mana, mp_ptr->name);
+ }
+ else
+ {
+ strnfmt(dummy, 80, " %c) %s",
+ label, mp_ptr->name);
+ }
+
+ if (ctr < 17)
+ {
+ prt(dummy, y + ctr, x);
+ }
+ else
+ {
+ prt(dummy, y + ctr - 17, x + 40);
+ }
+
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt ("", y + ctr, x);
+ }
+ else
+ {
+ prt ("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ /* Can't uppercase digits XXX XXX XXX */
+ ask = FALSE;
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ power = powers[i];
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Abort if needed */
+ if (!flag)
+ {
+ energy_use = 0;
+ return -1;
+ }
+
+ /* 'Powerful' monsters have wider radii */
+ if (r_ptr->flags2 & RF2_POWERFUL)
+ {
+ rad = 1 + (p_ptr->lev / 15);
+ }
+ else
+ {
+ rad = 1 + (p_ptr->lev / 20);
+ }
+
+
+ /* Analyse power */
+ switch (power)
+ {
+ /**** RF4 (bit position) ****/
+
+ /* SHRIEK */
+ case 0:
+ {
+ aggravate_monsters( -1);
+
+ break;
+ }
+
+ /* MULTIPLY */
+ case 1:
+ {
+ do_cmd_wiz_named_friendly(p_ptr->body_monster, FALSE);
+
+ break;
+ }
+
+ /* S_ANIMAL */
+ case 2:
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+
+ break;
+ }
+
+ /* ROCKET */
+ case 3:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ROCKET, dir, p_ptr->lev * 12, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+ /* ARROW_1 */
+ case 4:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(1, 6));
+
+ break;
+ }
+
+ /* ARROW_2 */
+ case 5:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(3, 6));
+
+ break;
+ }
+
+ /* ARROW_3 */
+ case 6:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(5, 6));
+
+ break;
+ }
+
+ /* ARROW_4 */
+ case 7:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ARROW, dir, damroll(7, 6));
+
+ break;
+ }
+
+ /* BR_ACID */
+ case 8:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_ELEC */
+ case 9:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_FIRE */
+ case 10:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_COLD */
+ case 11:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_POIS */
+ case 12:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_POIS, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_NETH */
+ case 13:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NETHER, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_LITE */
+ case 14:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_LITE, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_DARK */
+ case 15:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DARK, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_CONF */
+ case 16:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_CONFUSION, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_SOUN */
+ case 17:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_SOUND, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_CHAO */
+ case 18:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 7, rad);
+
+ break;
+ }
+
+ /* BR_DISE */
+ case 19:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DISENCHANT, dir, p_ptr->lev * 7, rad);
+
+ break;
+ }
+
+ /* BR_NEXU */
+ case 20:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NEXUS, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BR_TIME */
+ case 21:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_TIME, dir, p_ptr->lev * 3, rad);
+
+ break;
+ }
+
+ /* BR_INER */
+ case 22:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_INERTIA, dir, p_ptr->lev * 4, rad);
+
+ break;
+ }
+
+ /* BR_GRAV */
+ case 23:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_GRAVITY, dir, p_ptr->lev * 4, rad);
+
+ break;
+ }
+
+ /* BR_SHAR */
+ case 24:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_SHARDS, dir, p_ptr->lev * 8, rad);
+
+ break;
+ }
+
+ /* BR_PLAS */
+ case 25:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_PLASMA, dir, p_ptr->lev * 3, rad);
+
+ break;
+ }
+
+ /* BR_WALL */
+ case 26:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FORCE, dir, p_ptr->lev * 4, rad);
+
+ break;
+ }
+
+ /* BR_MANA */
+ case 27:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_MANA, dir, p_ptr->lev * 5, rad);
+
+ break;
+ }
+
+ /* BA_NUKE */
+ case 28:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+ /* BR_NUKE */
+ case 29:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+ /* BA_CHAO */
+ case 30:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 4, 2);
+
+ break;
+ }
+
+ /* BR_DISI */
+ case 31:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DISINTEGRATE, dir, p_ptr->lev * 5, 1 + (p_ptr->lev / 20));
+
+ break;
+ }
+
+
+ /**** RF5 (bit position + 32) ****/
+
+ /* BA_ACID */
+ case 32:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, randint(p_ptr->lev * 6) + 20, 2);
+
+ break;
+ }
+
+ /* BA_ELEC */
+ case 33:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* BA_FIRE */
+ case 34:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, randint(p_ptr->lev * 7) + 20, 2);
+
+ break;
+ }
+
+ /* BA_COLD */
+ case 35:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* BA_POIS */
+ case 36:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_POIS, dir, damroll(12, 2), 2);
+
+ break;
+ }
+
+ /* BA_NETH */
+ case 37:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_NETHER, dir, randint(p_ptr->lev * 4) + 20, 2);
+
+ break;
+ }
+
+ /* BA_WATE */
+ case 38:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_WATER, dir, randint(p_ptr->lev * 4) + 20, 2);
+
+ break;
+ }
+
+ /* BA_MANA */
+ case 39:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_MANA, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* BA_DARK */
+ case 40:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_DARK, dir, randint(p_ptr->lev * 3) + 20, 2);
+
+ break;
+ }
+
+ /* 41 DRAIN_MANA -- Not available */
+
+ /* 42 MIND_BLAST -- Not available */
+
+ /* 43 BRAIN_SMASH -- Not available */
+
+ /* CAUSE_1 */
+ case 44:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(3, 8));
+
+ break;
+ }
+
+ /* CAUSE_2 */
+ case 45:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(8, 8));
+
+ break;
+ }
+
+ /* CAUSE_3 */
+ case 46:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(10, 15));
+
+ break;
+ }
+
+ /* CAUSE_4 */
+ case 47:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(15, 15));
+
+ break;
+ }
+
+ /* BO_ACID */
+ case 48:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ACID, dir, damroll(7, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_ELEC */
+ case 49:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ELEC, dir, damroll(4, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_FIRE */
+ case 50:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_FIRE, dir, damroll(9, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_COLD */
+ case 51:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_COLD, dir, damroll(6, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_POIS */
+ case 52:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_POIS, dir, damroll(7, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_NETH */
+ case 53:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_NETHER, dir, damroll(5, 5) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_WATE */
+ case 54:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_WATER, dir, damroll(10, 10) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_MANA */
+ case 55:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(3, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_PLAS */
+ case 56:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_PLASMA, dir, damroll(8, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* BO_ICEE */
+ case 57:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_ICE, dir, damroll(6, 6) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* MISSILE */
+ case 58:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MISSILE, dir, damroll(2, 6) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* SCARE */
+ case 59:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fear_monster(dir, plev);
+
+ break;
+ }
+
+ /* BLIND */
+ case 60:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_CONFUSION, dir, damroll(1, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* CONF */
+ case 61:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_CONFUSION, dir, damroll(7, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* SLOW */
+ case 62:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_OLD_SLOW, dir, damroll(6, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+ /* HOLD */
+ case 63:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_OLD_SLEEP, dir, damroll(5, 8) + (p_ptr->lev / 3));
+
+ break;
+ }
+
+
+ /**** RF6 (bit position + 64) ****/
+
+ /* HASTE */
+ case 64:
+ {
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(20 + (plev) ) + plev, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + randint(5), 10);
+ }
+
+ break;
+ }
+
+ /* HAND_DOOM */
+ case 65:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(GF_MANA, dir, damroll(10, 8) + (p_ptr->lev));
+
+ break;
+ }
+
+ /* HEAL */
+ case 66:
+ {
+ hp_player(damroll(8, 5));
+
+ break;
+ }
+
+ /* S_ANIMALS */
+ case 67:
+ {
+ for (k = 0; k < 4; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+ }
+
+ break;
+ }
+
+ /* BLINK */
+ case 68:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ teleport_player(10);
+
+ break;
+ }
+
+ /* TPORT */
+ case 69:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ teleport_player(plev * 5);
+
+ break;
+ }
+
+ /* TELE_TO */
+ case 70:
+ {
+ int ii, ij;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ msg_print("You go between.");
+
+ if (!tgt_pt(&ii, &ij)) break;
+
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev * 20 + 2))
+ {
+ msg_print("You fail to show the destination correctly!");
+ p_ptr->energy -= 100;
+ teleport_player(10);
+ }
+ else teleport_player_to(ij, ii);
+
+ break;
+ }
+
+ /* TELE_AWAY */
+ case 71:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ if (!get_aim_dir(&dir)) break;
+
+ (void)fire_beam(GF_AWAY_ALL, dir, plev);
+
+ break;
+ }
+
+ /* TELE_LEVEL */
+ case 72:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ break;
+ }
+
+ teleport_player_level();
+
+ break;
+ }
+
+ /* DARKNESS */
+ case 73:
+ {
+ (void)project( -1, 3, p_ptr->py, p_ptr->px, 0, GF_DARK_WEAK,
+ PROJECT_GRID | PROJECT_KILL);
+
+ /* Unlite the room */
+ unlite_room(p_ptr->py, p_ptr->px);
+
+ break;
+ }
+
+ /* TRAPS */
+ case 74:
+ {
+ trap_creation();
+
+ break;
+ }
+
+ /* 75 FORGET -- Not available */
+
+ /* ANIM_DEAD -- Use the same code as the nether spell */
+ case 76:
+ {
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_RAISE, dir, 1, 0);
+
+ break;
+ }
+
+ /* 77 S_BUG -- Not available, well we do that anyway ;) */
+
+ /* 78 S_RNG -- Not available, who dares? */
+
+ /* S_THUNDERLORD */
+ case 79:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_THUNDERLORD, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_KIN -- Summon Kin, because we code bugs :) */
+ case 80:
+ {
+ /* Big hack */
+ summon_kin_type = r_ptr->d_char;
+
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_KIN, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HI_DEMON */
+ case 81:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_DEMON, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_MONSTER */
+ case 82:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, 0, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_MONSTERS */
+ case 83:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, 0, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_ANT */
+ case 84:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANT, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_SPIDER */
+ case 85:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_SPIDER, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HOUND */
+ case 86:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HOUND, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HYDRA */
+ case 87:
+ {
+ for (k = 0; k < 6; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HYDRA, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_ANGEL */
+ case 88:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_ANGEL, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_DEMON */
+ case 89:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_DEMON, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_UNDEAD */
+ case 90:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_UNDEAD, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_DRAGON */
+ case 91:
+ {
+ for (k = 0; k < 1; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_DRAGON, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HI_UNDEAD */
+ case 92:
+ {
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_HI_DRAGON */
+ case 93:
+ {
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_DRAGON_NO_UNIQUES, TRUE);
+ }
+
+ break;
+ }
+
+ /* S_WRAITH */
+ case 94:
+ {
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(y, x, rlev, SUMMON_WRAITH, TRUE);
+ }
+
+ break;
+ }
+
+ /* 95 S_UNIQUE -- Not available */
+ }
+
+ /* Take some SP */
+ if (!no_cost)
+ {
+ int chance, pchance;
+
+ chance = (monster_powers[power].mana + r_ptr->level);
+ pchance = adj_str_wgt[p_ptr->stat_ind[A_WIS]] / 2 + get_skill(SKILL_POSSESSION);
+
+ if (rand_int(chance) >= pchance)
+ {
+ int m = monster_powers[power].mana / 10;
+
+ if (m > p_ptr->msp) m = p_ptr->msp;
+ if (!m) m = 1;
+
+ p_ptr->csp -= m;
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ return (num);
+}
+
+/*
+ * Schooled magic
+ */
+
+/*
+ * Find a spell in any books/objects
+ */
+static int hack_force_spell = -1;
+static object_type *hack_force_spell_obj = NULL;
+bool get_item_hook_find_spell(int *item)
+{
+ int i, spell;
+ char buf[80];
+ char buf2[100];
+
+ strcpy(buf, "Manathrust");
+ if (!get_string("Spell name? ", buf, 79))
+ return FALSE;
+ sprintf(buf2, "return find_spell(\"%s\")", buf);
+ spell = exec_lua(buf2);
+ if (spell == -1) return FALSE;
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Must we wield it ? */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if ((wield_slot(o_ptr) != -1) && (i < INVEN_WIELD) && (f5 & TR5_WIELD_CAST)) continue;
+
+ /* Is it a non-book? */
+ if (!is_school_book(o_ptr))
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == spell))
+ {
+ *item = i;
+ hack_force_spell = spell;
+ hack_force_spell_obj = o_ptr;
+ return TRUE;
+ }
+ }
+ /* A random book ? */
+ else if ((o_ptr->sval == 255) && (o_ptr->pval == spell))
+ {
+ *item = i;
+ hack_force_spell = spell;
+ hack_force_spell_obj = o_ptr;
+ return TRUE;
+ }
+ /* A normal book */
+ else if (o_ptr->sval != 255)
+ {
+ sprintf(buf2, "return spell_in_book(%d, %d)", o_ptr->sval, spell);
+ if (exec_lua(buf2))
+ {
+ *item = i;
+ hack_force_spell = spell;
+ hack_force_spell_obj = o_ptr;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Get a spell from a book
+ */
+u32b get_school_spell(cptr do_what, cptr check_fct, s16b force_book)
+{
+ int i, item;
+ u32b spell = -1;
+ int num = 0;
+ s32b where = 1;
+ int ask;
+ bool flag, redraw;
+ char choice;
+ char out_val[160];
+ char buf2[40];
+ char buf3[40];
+ object_type *o_ptr, forge;
+ int tmp;
+ int sval, pval;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ hack_force_spell = -1;
+ hack_force_spell_obj = NULL;
+
+ /* Ok do we need to ask for a book ? */
+ if (!force_book)
+ {
+ get_item_extra_hook = get_item_hook_find_spell;
+ item_tester_hook = hook_school_spellable;
+ sprintf(buf2, "You have no book to %s from", do_what);
+ sprintf(buf3, "%s from which book?", do_what);
+ if (!get_item(&item, buf3, buf2, USE_INVEN | USE_EQUIP | USE_EXTRA )) return -1;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* If it can be wielded, it must */
+ if ((wield_slot(o_ptr) != -1) && (item < INVEN_WIELD) && (f5 & TR5_WIELD_CAST))
+ {
+ msg_format("You cannot %s from that object; it must be wielded first.", do_what);
+ return -1;
+ }
+ }
+ else
+ {
+ o_ptr = &forge;
+ o_ptr->tval = TV_BOOK;
+ o_ptr->sval = force_book;
+ o_ptr->pval = 0;
+ }
+
+ if (repeat_pull(&tmp))
+ {
+ return tmp;
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+
+ /* No spell to cast by default */
+ spell = -1;
+
+ /* Is it a random book, or something else ? */
+ if (is_school_book(o_ptr))
+ {
+ sval = o_ptr->sval;
+ pval = o_ptr->pval;
+ }
+ else
+ {
+ sval = 255;
+ pval = o_ptr->pval2;
+ }
+
+ if (hack_force_spell == -1)
+ {
+ num = exec_lua(format("return book_spells_num(%d)", sval));
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(Spells %c-%c, Descs %c-%c, *=List, ESC=exit) %^s which spell? ",
+ I2A(0), I2A(num - 1), I2A(0) - 'a' + 'A', I2A(num - 1) - 'a' + 'A', do_what);
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if (((choice == ' ') || (choice == '*') || (choice == '?')))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", sval, pval, o_ptr, &where);
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+ where = 1;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Verify it */
+ if (ask)
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_load();
+ Term_save();
+
+ }
+ /* Rstore the screen */
+ else
+ {
+ /* Restore the screen */
+ Term_load();
+ }
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", sval, pval, o_ptr, &where);
+ exec_lua(format("print_spell_desc(spell_x(%d, %d, %d), %d)", sval, pval, i, where));
+ }
+ else
+ {
+ s32b ok;
+
+ /* Save the spell index */
+ spell = exec_lua(format("return spell_x(%d, %d, %d)", sval, pval, i));
+
+ /* Do we need to do some pre test */
+ call_lua(check_fct, "(d,O)", "d", spell, o_ptr, &ok);
+
+ /* Require "okay" spells */
+ if (!ok)
+ {
+ bell();
+ msg_format("You may not %s that spell.", do_what);
+ spell = -1;
+ continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+ }
+ }
+ else
+ {
+ s32b ok;
+
+ /* Require "okay" spells */
+ call_lua(check_fct, "(d, O)", "d", hack_force_spell, hack_force_spell_obj, &ok);
+ if (ok)
+ {
+ flag = TRUE;
+ spell = hack_force_spell;
+ }
+ else
+ {
+ bell();
+ msg_format("You may not %s that spell.", do_what);
+ spell = -1;
+ }
+ }
+
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+
+
+ /* Abort if needed */
+ if (!flag) return -1;
+
+ tmp = spell;
+ repeat_push(tmp);
+ return spell;
+}
+
+void cast_school_spell()
+{
+ int spell;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ spell = get_school_spell("cast", "is_ok_spell", 0);
+
+ /* Actualy cast the choice */
+ if (spell != -1)
+ {
+ exec_lua(format("cast_school_spell(%d, spell(%d))", spell, spell));
+ }
+}
+
+void browse_school_spell(int book, int pval, object_type *o_ptr)
+{
+ int i;
+ int num = 0, where = 1;
+ int ask;
+ char choice;
+ char out_val[160];
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+
+ num = exec_lua(format("return book_spells_num(%d)", book));
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(Spells %c-%c, ESC=exit) cast which spell? ",
+ I2A(0), I2A(num - 1));
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", book, pval, o_ptr, &where);
+
+ /* Get a spell from the user */
+ while (get_com(out_val, &choice))
+ {
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", book, pval, o_ptr, &where);
+
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Display a list of spells */
+ call_lua("print_book", "(d,d,O)", "d", book, pval, o_ptr, &where);
+ exec_lua(format("print_spell_desc(spell_x(%d, %d, %d), %d)", book, pval, i, where));
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Show choices */
+ if (show_choices)
+ {
+ /* Window stuff */
+ window_stuff();
+ }
+}
+
+/* Can it contains a schooled spell ? */
+static bool hook_school_can_spellable(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == -1))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Copy a spell from a bok to an object
+ */
+void do_cmd_copy_spell()
+{
+ int spell = get_school_spell("copy", "is_ok_spell", 0);
+ int item;
+ object_type *o_ptr;
+
+ if (spell == -1) return;
+
+ /* Spells that cannot be randomly created cannot be copied */
+ if (exec_lua(format("return can_spell_random(%d)", spell)) == FALSE)
+ {
+ msg_print("This spell cannot be copied.");
+ return;
+ }
+
+ item_tester_hook = hook_school_can_spellable;
+ if (!get_item(&item, "Copy to which object? ", "You have no object to copy to.", (USE_INVEN | USE_EQUIP))) return;
+ o_ptr = get_object(item);
+
+ msg_print("You copy the spell!");
+ o_ptr->pval2 = spell;
+ inven_item_describe(item);
+}
+
+/*
+ * Finds a spell by name, optimized for speed
+ */
+int find_spell(char *name)
+{
+ int oldtop, spell;
+ oldtop = lua_gettop(L);
+
+ lua_getglobal(L, "find_spell");
+ tolua_pushstring(L, name);
+
+ /* Call the function */
+ if (lua_call(L, 1, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'find_spell'.");
+ lua_settop(L, oldtop);
+ return -1;
+ }
+
+ spell = tolua_getnumber(L, -(lua_gettop(L) - oldtop), -1);
+
+ lua_settop(L, oldtop);
+
+ return spell;
+}
diff --git a/src/cmd6.c b/src/cmd6.c
new file mode 100644
index 00000000..5511f6db
--- /dev/null
+++ b/src/cmd6.c
@@ -0,0 +1,7948 @@
+/* File: cmd6.c */
+
+/* Purpose: Object commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Forward declare
+ */
+static bool activate_spell(object_type * o_ptr, byte choice);
+
+
+/*
+ * General function to find an item by its name
+ */
+cptr get_item_hook_find_obj_what;
+bool get_item_hook_find_obj(int *item)
+{
+ int i;
+ char buf[80];
+ char buf2[100];
+
+ strcpy(buf, "");
+ if (!get_string(get_item_hook_find_obj_what, buf, 79))
+ return FALSE;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!item_tester_okay(o_ptr)) continue;
+
+ object_desc(buf2, o_ptr, -1, 0);
+ if (!strcmp(buf, buf2))
+ {
+ *item = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ * This file includes code for eating food, drinking potions,
+ * reading scrolls, aiming wands, using staffs, zapping rods,
+ * and activating artifacts.
+ *
+ * In all cases, if the player becomes "aware" of the item's use
+ * by testing it, mark it as "aware" and reward some experience
+ * based on the object's level, always rounding up. If the player
+ * remains "unaware", mark that object "kind" as "tried".
+ *
+ * This code now correctly handles the unstacking of wands, staffs,
+ * and rods. Note the overly paranoid warning about potential pack
+ * overflow, which allows the player to use and drop a stacked item.
+ *
+ * In all "unstacking" scenarios, the "used" object is "carried" as if
+ * the player had just picked it up. In particular, this means that if
+ * the use of an item induces pack overflow, that item will be dropped.
+ *
+ * For simplicity, these routines induce a full "pack reorganization"
+ * which not only combines similar items, but also reorganizes various
+ * items to obey the current "sorting" method. This may require about
+ * 400 item comparisons, but only occasionally.
+ *
+ * There may be a BIG problem with any "effect" that can cause "changes"
+ * to the p_ptr->inventory. For example, a "scroll of recharging" can cause
+ * a wand/staff to "disappear", moving the p_ptr->inventory up. Luckily, the
+ * scrolls all appear BEFORE the staffs/wands, so this is not a problem.
+ * But, for example, a "staff of recharging" could cause MAJOR problems.
+ * In such a case, it will be best to either (1) "postpone" the effect
+ * until the end of the function, or (2) "change" the effect, say, into
+ * giving a staff "negative" charges, or "turning a staff into a stick".
+ * It seems as though a "rod of recharging" might in fact cause problems.
+ * The basic problem is that the act of recharging (and destroying) an
+ * item causes the inducer of that action to "move", causing "o_ptr" to
+ * no longer point at the correct item, with horrifying results.
+ *
+ * Note that food/potions/scrolls no longer use bit-flags for effects,
+ * but instead use the "sval" (which is also used to sort the objects).
+ */
+
+
+/*
+ * Determine the effects of eating a corpse. A corpse can be
+ * eaten whole or cut into pieces for later.
+ */
+static void corpse_effect(object_type *o_ptr, bool cutting)
+{
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Assume no bad effects */
+ bool harmful = FALSE;
+
+ byte method, effect, d_dice, d_side;
+
+ int i, dam, idam = 0, mdam, brpow, brdam = 0;
+
+
+ /* How much of the monster's breath attack remains */
+ if (o_ptr->pval <= r_ptr->weight)
+ {
+ brpow = 0;
+ }
+ else
+ {
+ brpow = (o_ptr->pval - r_ptr->weight) / 5;
+ if (brpow > (r_ptr->weight / 5)) brpow = r_ptr->weight / 5;
+ }
+
+ if (o_ptr->weight <= 0) o_ptr->weight = 1;
+ if (o_ptr->pval <= 0) o_ptr->pval = 1;
+
+ /*
+ * The breath is only discharged by accident or by slicing off pieces
+ * of meat, and only by corpses.
+ */
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) ||
+ (rand_int(o_ptr->weight / 5) && !cutting)) brpow = 0;
+
+ /* Immediate effects - poison, acid, fire, etc. */
+ if (!cutting)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ /* skip empty blow slot */
+ if (!r_ptr->blow[i].method) continue;
+
+ method = r_ptr->blow[i].method;
+ effect = r_ptr->blow[i].effect;
+ d_dice = r_ptr->blow[i].d_dice;
+ d_side = r_ptr->blow[i].d_side;
+ dam = damroll(d_dice, d_side) * o_ptr->pval / o_ptr->weight / 2;
+ idam = damroll(d_dice, d_side) *
+ ((o_ptr->weight / o_ptr->pval > 2) ?
+ o_ptr->weight / o_ptr->pval : 2);
+ mdam = maxroll(d_dice, d_side) * 2;
+
+ /* Analyse method */
+ switch (method)
+ {
+ /* Methods that are meaningless after death */
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_ENGULF:
+ case RBM_DROOL:
+ case RBM_SPIT:
+ case RBM_GAZE:
+ case RBM_WAIL:
+ case RBM_BEG:
+ case RBM_INSULT:
+ case RBM_MOAN:
+ {
+ continue;
+ }
+ }
+
+ /* Analyse effect */
+ switch (effect)
+ {
+ /* Effects that are meaningless after death */
+ case RBE_HURT:
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ case RBE_EAT_GOLD:
+ case RBE_EAT_ITEM:
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ case RBE_ELEC:
+ case RBE_COLD:
+ case RBE_SHATTER:
+ {
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + dam + idam + 10);
+ harmful = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Total Immunity */
+ if (!(p_ptr->immune_acid || (dam <= 0)))
+ {
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ /* Take damage */
+ take_hit(dam, "acidic food");
+ harmful = TRUE;
+ }
+ else
+ {
+ set_oppose_acid(p_ptr->oppose_acid + idam);
+ }
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Totally immune */
+ if (p_ptr->immune_fire || (dam <= 0))
+ {
+ /* Resist the damage */
+ if (p_ptr->resist_fire) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_fire) dam = (dam + 2) / 3;
+
+ /* Take damage */
+ take_hit(dam, "a fiery meal");
+ harmful = TRUE;
+ }
+ else
+ {
+ set_oppose_fire(p_ptr->oppose_fire + idam);
+ }
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ if (!p_ptr->resist_blind)
+ {
+ set_blind(p_ptr->blind + dam * 2 + idam * 2 + 20);
+ }
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + dam + idam + 10);
+ }
+ if (!p_ptr->resist_chaos && rand_int(mdam - dam))
+ {
+ set_image(p_ptr->image + dam * 10 + idam * 10 + 100);
+ }
+
+ break;
+ }
+
+ case RBE_HALLU:
+ {
+ if (!p_ptr->resist_chaos && rand_int(mdam - dam))
+ {
+ set_image(p_ptr->image + dam * 10 + idam * 10 + 50);
+ }
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ if (!p_ptr->resist_fear)
+ {
+ set_afraid(p_ptr->afraid + dam + idam + 10);
+ }
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ if (!p_ptr->free_act)
+ {
+ set_paralyzed(p_ptr->paralyzed + dam + idam + 10);
+ }
+
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ {
+ do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_INT:
+ {
+ do_dec_stat(A_INT, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_WIS:
+ {
+ do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_DEX:
+ {
+ do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_CON:
+ {
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ case RBE_LOSE_CHR:
+ {
+ do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+
+ break;
+ }
+
+ /* Don't eat Morgoth's corpse :) */
+ case RBE_LOSE_ALL:
+ {
+ do_dec_stat(A_STR, STAT_DEC_NORMAL);
+ do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ msg_print("You feel your sanity slipping away!");
+ take_sanity_hit(dam, "eating an insane monster");
+
+ break;
+ }
+
+ /* Unlife is bad to eat */
+ case RBE_EXP_10:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(10, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_EXP_20:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(20, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_EXP_40:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(40, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+
+ case RBE_EXP_80:
+ {
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(80, 6) +
+ (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+
+ o_ptr->pval = 1;
+
+ break;
+ }
+ }
+ }
+ } /* if (!cutting) */
+
+
+ /*
+ * The organ that supplies breath attacks is not
+ * immediately emptied upon death, although some types
+ * of breath have no effect.
+ * AMHD's make rather risky meals, and deadly snacks.
+ */
+
+ /* Acid */
+ if (r_ptr->flags4 & RF4_BR_ACID && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("You are hit by a gush of acid!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_acid || (brdam <= 0)))
+ {
+ /* Take damage */
+ acid_dam(brdam, "a gush of acid");
+ harmful = TRUE;
+ }
+ o_ptr->pval = 1;
+ }
+ else if (r_ptr->flags4 & RF4_BR_ACID)
+ {
+ set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10);
+ }
+
+ /* Electricity */
+ if (r_ptr->flags4 & RF4_BR_ELEC && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("You receive a heavy shock!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_elec || (brdam <= 0)))
+ {
+ /* Take damage */
+ elec_dam(brdam, "an electric shock");
+ harmful = TRUE;
+ }
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+ else if (r_ptr->flags4 & RF4_BR_ELEC)
+ {
+ set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10);
+ }
+
+ /* Fire */
+ if (r_ptr->flags4 & RF4_BR_FIRE && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("Roaring flames engulf you!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_fire || (brdam <= 0)))
+ {
+ /* Take damage */
+ fire_dam(brdam, "an explosion");
+ harmful = TRUE;
+ }
+ o_ptr->pval = 1;
+ }
+ else if (r_ptr->flags4 & RF4_BR_FIRE)
+ {
+ set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10);
+ }
+
+ /* Cold */
+ if (r_ptr->flags4 & RF4_BR_COLD && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));
+
+ msg_print("You are caught in a freezing liquid!");
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_cold || (brdam <= 0)))
+ {
+ /* Take damage */
+ cold_dam(brdam, "a chilling blast");
+ harmful = TRUE;
+ }
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+ else if (r_ptr->flags4 & RF4_BR_COLD)
+ {
+ set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10);
+ }
+
+ /* Poison */
+ if (r_ptr->flags4 & RF4_BR_POIS && brpow > 0)
+ {
+ brdam = ((brpow / 3) > 800 ? 800 : (brpow / 3));
+
+ msg_print("You are surrounded by toxic gases!");
+
+ /* Resist the damage */
+ if (p_ptr->resist_pois) brdam = (brdam + 2) / 3;
+ if (p_ptr->oppose_pois) brdam = (brdam + 2) / 3;
+
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ (void)set_poisoned(p_ptr->poisoned + rand_int(brdam) + 10);
+ }
+
+ /* Take damage */
+ take_hit(brdam, "toxic gases");
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ harmful = TRUE;
+ }
+
+ /* Nether */
+ if (r_ptr->flags4 & RF4_BR_NETH && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 550 ? 550 : (brpow / 6));
+
+ msg_print("A black aura surrounds the corpse!");
+
+ if (p_ptr->resist_neth)
+ {
+ brdam *= 6;
+ brdam /= (randint(6) + 6);
+ }
+ else
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ /* Take damage */
+ take_hit(brdam, "an unholy blast");
+ harmful = TRUE;
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+
+ /* Confusion */
+ if (r_ptr->flags4 & RF4_BR_CONF && brpow > 0)
+ {
+ msg_print("A strange liquid splashes on you!");
+
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + brdam + idam + 10);
+ }
+ o_ptr->weight = o_ptr->weight - brpow;
+ o_ptr->pval = o_ptr->weight;
+ }
+
+ /* Chaos */
+ if (r_ptr->flags4 & RF4_BR_CHAO && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 600 ? 600 : (brpow / 6));
+
+ msg_print("A swirling cloud surrounds you!");
+
+ if (p_ptr->resist_chaos)
+ {
+ brdam *= 6;
+ brdam /= (randint(6) + 6);
+ }
+
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(20) + 10);
+ }
+
+ if (!p_ptr->resist_chaos)
+ {
+ (void)set_image(p_ptr->image + randint(10));
+ }
+
+ if (!p_ptr->resist_neth && !p_ptr->resist_chaos)
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ /* Take damage */
+ take_hit(brdam, "chaotic forces");
+ o_ptr->pval = 1;
+ }
+
+ /* Disenchantment */
+ if (r_ptr->flags4 & RF4_BR_DISE && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 500 ? 500 : (brpow / 6));
+
+ msg_print("You are blasted by raw mana!");
+
+ if (p_ptr->resist_disen)
+ {
+ brdam *= 6;
+ brdam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)apply_disenchant(0);
+ }
+
+ /* Take damage */
+ take_hit(brdam, "raw mana");
+ o_ptr->pval = 1;
+ }
+
+ /* Plasma */
+ if (r_ptr->flags4 & RF4_BR_PLAS && brpow > 0)
+ {
+ brdam = ((brpow / 6) > 150 ? 150 : (brpow / 6));
+
+ msg_print("Searing flames engulf the corpse!");
+
+ /* Resist the damage */
+ if (p_ptr->resist_fire || p_ptr->oppose_fire) brdam = (brdam + 2) / 3;
+
+ if (!p_ptr->resist_sound)
+ {
+ int k = (randint((brdam > 40) ? 35 : (brdam * 3 / 4 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ /* Take damage */
+ take_hit(brdam, "an explosion");
+ harmful = TRUE;
+ o_ptr->pval = 1;
+ }
+
+ /* Hack -- Jellies are immune to acid only if they are already acidic */
+ if (strchr("j", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_ACID))
+ {
+ dam = damroll(8, 8);
+
+ /* Total Immunity */
+ if (!(p_ptr->immune_acid || (dam <= 0)))
+ {
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ /* Take damage */
+ take_hit(dam, "acidic food");
+ }
+ harmful = TRUE;
+ }
+
+ /*
+ * Hack -- Jellies, kobolds, spiders, icky things, molds, and mushrooms
+ * are immune to poison because their body already contains
+ * poisonous chemicals.
+ */
+ if (strchr("ijkmS,", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_POIS))
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(15) + 10);
+ }
+ harmful = TRUE;
+ }
+
+ /*
+ * Bad effects override good effects
+ * and hacked-up corpses lose intrinsics.
+ */
+ if (!harmful && !cutting && (o_ptr->sval != SV_CORPSE_MEAT))
+ {
+ if (r_ptr->flags3 & RF3_IM_ACID)
+ {
+ set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_ELEC)
+ {
+ set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_FIRE)
+ {
+ set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_COLD)
+ {
+ set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_IM_POIS)
+ {
+ set_oppose_pois(p_ptr->oppose_pois + rand_int(10) + 10);
+ }
+ if (r_ptr->flags3 & RF3_RES_NETH)
+ {
+ set_protevil(p_ptr->protevil + rand_int(25) + 3 * r_ptr->level);
+ }
+ if (r_ptr->flags3 & RF3_RES_PLAS)
+ {
+ set_oppose_fire(p_ptr->oppose_fire + rand_int(20) + 20);
+ }
+ if (r_ptr->flags2 & RF2_SHAPECHANGER)
+ {
+ // DGDGDG (void)set_mimic(20 , rand_int(MIMIC_VALAR));
+ }
+
+ if (r_ptr->flags3 & RF3_DEMON)
+ {
+ // DGDGDG (void)set_mimic(30 , MIMIC_DEMON);
+ }
+
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ {
+ // DGDGDG (void)set_mimic(30 , MIMIC_VAMPIRE);
+ }
+
+ if (r_ptr->flags3 & RF3_NO_FEAR)
+ {
+ (void)set_afraid(0);
+ }
+ if (r_ptr->flags3 & RF3_NO_STUN)
+ {
+ (void)set_stun(0);
+ }
+ if (r_ptr->flags3 & RF3_NO_CONF)
+ {
+ (void)set_confused(0);
+ }
+ if (r_ptr->flags6 & RF6_S_THUNDERLORD)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_THUNDERLORD, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_DEMON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_KIN)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_KIN, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HI_DEMON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DEMON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_MONSTER)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_MONSTERS)
+ {
+ int k;
+ for (k = 0; k < 8; k++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE);
+ }
+ }
+ if (r_ptr->flags6 & RF6_S_UNDEAD)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_DRAGON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_ANT)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_SPIDER)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HOUND)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HYDRA)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_ANGEL)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HI_DRAGON)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_HI_UNDEAD)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_WRAITH)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH, FALSE);
+ }
+ if (r_ptr->flags6 & RF6_S_UNIQUE)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE, FALSE);
+ }
+ }
+}
+
+
+/*
+ * Hook to determine if an object is eatable
+ */
+static bool item_tester_hook_eatable(object_type *o_ptr)
+{
+ /* Foods and, well, corpses are edible */
+ if ((o_ptr->tval == TV_FOOD) || (o_ptr->tval == TV_CORPSE)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Eat some food (from the pack or floor)
+ */
+void do_cmd_eat_food(void)
+{
+ int item, ident, lev, fval = 0;
+
+ object_type *o_ptr;
+ object_type *q_ptr, forge;
+
+ monster_race *r_ptr;
+
+ cptr q, s;
+
+ bool destroy = TRUE;
+
+
+ /* Restrict choices to food */
+ item_tester_hook = item_tester_hook_eatable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Food full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Eat which item? ";
+ s = "You have nothing to eat.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Sound */
+ sound(SOUND_EAT);
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Identity not known yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Scripted foods */
+ if (process_hooks_ret(HOOK_EAT, "d", "(O)", o_ptr))
+ {
+ ident = process_hooks_return[0].num;
+ }
+ /* (not quite) Normal foods */
+ else if (o_ptr->tval == TV_FOOD)
+ {
+ /* Analyze the food */
+ switch (o_ptr->sval)
+ {
+ case SV_FOOD_GREAT_HEALTH:
+ {
+ p_ptr->hp_mod += 70;
+ msg_print("As you eat it you begin to feel your life flow getting stronger.");
+ ident = TRUE;
+ p_ptr->update |= (PU_HP);
+
+ break;
+ }
+
+ case SV_FOOD_POISON:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_BLINDNESS:
+ {
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + rand_int(200) + 200))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_PARANOIA:
+ {
+ if (!p_ptr->resist_fear)
+ {
+ if (set_afraid(p_ptr->afraid + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_CONFUSION:
+ {
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_HALLUCINATION:
+ {
+ if (!p_ptr->resist_chaos)
+ {
+ if (set_image(p_ptr->image + rand_int(250) + 250))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_PARALYSIS:
+ {
+ if (!p_ptr->free_act)
+ {
+ if (set_paralyzed(p_ptr->paralyzed + rand_int(10) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_FOOD_WEAKNESS:
+ {
+ take_hit(damroll(6, 6), "poisonous food");
+ (void)do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_SICKNESS:
+ {
+ take_hit(damroll(6, 6), "poisonous food");
+ (void)do_dec_stat(A_CON, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_STUPIDITY:
+ {
+ take_hit(damroll(8, 8), "poisonous food");
+ (void)do_dec_stat(A_INT, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_NAIVETY:
+ {
+ take_hit(damroll(8, 8), "poisonous food");
+ (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_UNHEALTH:
+ {
+ take_hit(damroll(10, 10), "poisonous food");
+ (void)do_dec_stat(A_CON, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_DISEASE:
+ {
+ take_hit(damroll(10, 10), "poisonous food");
+ (void)do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_POISON:
+ {
+ if (set_poisoned(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_BLINDNESS:
+ {
+ if (set_blind(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_PARANOIA:
+ {
+ if (set_afraid(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_CONFUSION:
+ {
+ if (set_confused(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_CURE_SERIOUS:
+ {
+ if (hp_player(damroll(4, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_RESTORE_STR:
+ {
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_RESTORE_CON:
+ {
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_RESTORING:
+ {
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_FORTUNE_COOKIE:
+ {
+ char rumour[80];
+
+ msg_print("That tastes good.");
+ msg_print("There is message in the cookie. It says:");
+ msg_print(NULL);
+
+ switch (randint(20))
+ {
+ case 1:
+ {
+ get_rnd_line("chainswd.txt", rumour);
+ break;
+ }
+
+ case 2:
+ {
+ get_rnd_line("error.txt", rumour);
+ break;
+ }
+
+ case 3:
+ case 4:
+ case 5:
+ {
+ get_rnd_line("death.txt", rumour);
+ break;
+ }
+
+ default:
+ {
+ get_rnd_line("rumors.txt", rumour);
+ break;
+ }
+ }
+
+ msg_format("%s", rumour);
+ msg_print(NULL);
+
+ ident = TRUE;
+
+ break;
+ }
+
+
+ case SV_FOOD_RATION:
+ case SV_FOOD_BISCUIT:
+ case SV_FOOD_JERKY:
+ {
+ msg_print("That tastes good.");
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_SLIME_MOLD:
+ {
+ msg_print("That tastes good.");
+
+ /* 2% chance of getting the mold power */
+ if (magik(2))
+ {
+ ADD_POWER(p_ptr->powers_mod, PWR_GROW_MOLD);
+ p_ptr->update |= PU_POWERS;
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_WAYBREAD:
+ {
+ msg_print("That tastes very good.");
+ (void)set_poisoned(0);
+ (void)hp_player(damroll(4, 8));
+ set_food(PY_FOOD_MAX - 1);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_FOOD_PINT_OF_ALE:
+ case SV_FOOD_PINT_OF_WINE:
+ {
+ msg_print("That tastes good.");
+
+ ident = TRUE;
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1));
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ break;
+ }
+
+ case SV_FOOD_ATHELAS:
+ {
+ msg_print("A fresh, clean essence rises, driving away wounds and poison.");
+
+ (void)set_poisoned(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+ }
+ }
+
+ /* Corpses... */
+ else
+ {
+ r_ptr = &r_info[o_ptr->pval2];
+
+ /* Analyse the corpse */
+ switch (o_ptr->sval)
+ {
+ case SV_CORPSE_CORPSE:
+ {
+ bool no_meat = FALSE;
+
+ /* Not all is edible. Apologies if messy. */
+
+ /* Check weight -- they have to have some meat left */
+ if (r_ptr->flags9 & RF9_DROP_SKELETON)
+ {
+ if (o_ptr->weight <= (r_ptr->weight * 3) / 5)
+ {
+ no_meat = TRUE;
+ }
+ }
+
+ /* Non-skeletons are naturally have more allowances */
+ else
+ {
+ if (o_ptr->weight <= (r_ptr->weight * 7) / 20)
+ {
+ no_meat = TRUE;
+ }
+ }
+
+ /* Nothing left to eat */
+ if (no_meat)
+ {
+ msg_print("There is not enough meat.");
+ return;
+ }
+
+
+ /* Check freshness */
+ if (!o_ptr->timeout) msg_print("Ugh! Raw meat!");
+ else msg_print("That tastes good.");
+
+
+ /* A pound of raw meat */
+ o_ptr->pval -= 10;
+ o_ptr->weight -= 10;
+
+ /* Corpses still have meat on them */
+ destroy = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_CORPSE_HEAD:
+ {
+ msg_print("You feel rather sick.");
+
+ /* A pound of raw meat */
+ o_ptr->pval -= 10;
+ o_ptr->weight -= 10;
+
+ /* Corpses still have meat on them */
+ destroy = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_CORPSE_MEAT:
+ {
+ /* Just meat */
+ if (!o_ptr->timeout) msg_print("You quickly swallow the meat.");
+ else msg_print("That tastes good.");
+
+ ident = TRUE;
+
+ /* Those darn microorganisms */
+ if (!o_ptr->timeout && (o_ptr->weight > o_ptr->pval) &&
+ !(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) +
+ (o_ptr->weight - o_ptr->pval));
+ }
+
+ break;
+ }
+ }
+
+ corpse_effect(o_ptr, FALSE);
+
+ /* Less nutritious than food rations, but much more of it. */
+ fval = (o_ptr->timeout) ? 2000 : 2500;
+
+ /* Those darn microorganisms */
+ if (!o_ptr->timeout && (o_ptr->weight - o_ptr->pval > 10) &&
+ !(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) +
+ (o_ptr->weight - o_ptr->pval));
+ }
+
+ /* Partially cured */
+ if (o_ptr->weight > o_ptr->timeout)
+ {
+ /* Adjust the "timeout" without overflowing */
+ o_ptr->timeout = (o_ptr->timeout * ((100 * o_ptr->timeout) / o_ptr->weight)) / 100;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* We have tried it */
+ object_tried(o_ptr);
+
+ /* The player is now aware of the object */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ if (!fval) fval = o_ptr->pval;
+
+ /* Food can feed the player, in a different ways */
+
+ /* Vampires */
+ if ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")))
+ {
+ /* Reduced nutritional benefit */
+ /* (void)set_food(p_ptr->food + (fval / 10)); -- No more */
+ msg_print("Mere victuals hold scant sustenance for a being such as yourself.");
+
+ /* Hungry */
+ if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ msg_print("Your hunger can only be satisfied with fresh blood!");
+ }
+ }
+
+ else if (PRACE_FLAG(PR1_NO_FOOD))
+ {
+ if (PRACE_FLAG(PR1_UNDEAD))
+ {
+ msg_print("The food of mortals is poor sustenance for you.");
+ }
+ else
+ {
+ msg_print("Food is poor sustenance for you.");
+ }
+ set_food(p_ptr->food + ((fval) / 40));
+ }
+
+ /* Those living in fresh */
+ else
+ {
+ (void)set_food(p_ptr->food + fval);
+ }
+
+
+ /* Destroy a food in the pack */
+ if (destroy)
+ {
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Destroy a food on the floor */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+}
+
+
+/*
+ * Cut a corpse up for convenient storage
+ */
+void do_cmd_cut_corpse(void)
+{
+ int item, meat = 0, not_meat = 0;
+
+ object_type *o_ptr;
+
+ object_type *i_ptr;
+
+ object_type object_type_body;
+
+ monster_race *r_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict choices to corpses */
+ item_tester_tval = TV_CORPSE;
+
+ /* Get an item */
+ q = "Hack up which corpse? ";
+ s = "You have no corpses.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ r_ptr = &r_info[o_ptr->pval2];
+
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_HEAD))
+ {
+ msg_print ("You cannot split that.");
+ return;
+ }
+
+ switch (o_ptr->sval)
+ {
+ case SV_CORPSE_CORPSE:
+ {
+ if (r_ptr->flags9 & RF9_DROP_SKELETON)
+ {
+ not_meat = (r_ptr->weight * 3) / 5;
+ }
+ else
+ {
+ not_meat = (r_ptr->weight * 7) / 20;
+ }
+ meat = r_ptr->weight + r_ptr->weight / 10 - not_meat;
+
+ break;
+ }
+
+ case SV_CORPSE_HEAD:
+ {
+ not_meat = r_ptr->weight / 150;
+ meat = r_ptr->weight / 30 + r_ptr->weight / 300 - not_meat;
+
+ break;
+ }
+ }
+
+ if ((o_ptr->weight <= not_meat) || (meat < 10))
+ {
+ msg_print("There is not enough meat.");
+ return;
+ }
+
+ /* Hacking 10 pounds off */
+ if (meat > 100) meat = 100;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ o_ptr->pval -= meat;
+ o_ptr->weight -= meat;
+
+ msg_print("You hack some meat off the corpse.");
+
+ corpse_effect(o_ptr, TRUE);
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Make some meat */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_MEAT));
+
+ i_ptr->number = meat / 10;
+ i_ptr->pval2 = o_ptr->pval2;
+
+ /* Length of time before decay */
+ i_ptr->pval = 1000 + rand_int(1000);
+
+ if (inven_carry_okay(i_ptr))
+ {
+ inven_carry(i_ptr, TRUE);
+ }
+ else
+ {
+ drop_near(i_ptr, 0, p_ptr->py, p_ptr->px);
+ }
+}
+
+
+/*
+ * Use a potion to cure some meat
+ *
+ * Salt water works well.
+ */
+void do_cmd_cure_meat(void)
+{
+ int item, num, cure;
+
+ object_type *o_ptr;
+
+ object_type *i_ptr;
+
+ cptr q, s;
+
+
+ /* Restrict choices to corpses */
+ item_tester_tval = TV_CORPSE;
+ item_tester_hook = item_tester_hook_eatable;
+
+ /* Get some meat */
+ q = "Cure which meat? ";
+ s = "You have no meat to cure.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Restrict choices to potions */
+ item_tester_tval = TV_POTION;
+
+ /* Get a potion */
+ q = "Use which potion? ";
+ s = "You have no potions to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ i_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ i_ptr = &o_list[0 - item];
+ }
+
+ if (i_ptr->number > 1)
+ {
+ /* Get a number */
+ get_count(1, i_ptr->number);
+
+ /* Save it */
+ num = command_arg;
+ }
+ else
+ {
+ num = 1;
+ }
+
+ if (num == 0) return;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ q = "You soak the meat.";
+ s = "You soak the meat.";
+
+ switch (i_ptr->sval)
+ {
+ case SV_POTION_SALT_WATER:
+ {
+ q = "You salt the meat.";
+ cure = 200 * num;
+
+ break;
+ }
+
+ case SV_POTION_POISON:
+ {
+ q = "You poison the meat.";
+ cure = 0;
+ o_ptr->pval /= 2;
+ if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight;
+
+ break;
+ }
+
+ case SV_POTION_CONFUSION:
+ {
+ cure = 80 * num;
+
+ break;
+ }
+
+ case SV_POTION_SLOW_POISON:
+ {
+ cure = 20 * num;
+
+ break;
+ }
+
+ case SV_POTION_CURE_POISON:
+ {
+ cure = 45 * num;
+
+ break;
+ }
+
+ case SV_POTION_DEATH:
+ {
+ q = "You ruin the meat.";
+ cure = 0;
+ o_ptr->pval /= 10;
+ if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight / 2;
+
+ break;
+ }
+
+ default:
+ {
+ cure = 0;
+
+ break;
+ }
+ }
+
+ /* Message */
+ if (object_known_p(i_ptr)) msg_print(q);
+ else msg_print(s);
+
+ /* The meat is already spoiling */
+ if (((o_ptr->sval == SV_CORPSE_MEAT) && (o_ptr->weight > o_ptr->pval)) ||
+ (o_ptr->weight - o_ptr->pval > 10))
+ {
+ cure = (cure * o_ptr->pval) / (o_ptr->weight * 20);
+ }
+
+ /* Cure the meat */
+ o_ptr->timeout += cure / o_ptr->number;
+
+ if (o_ptr->timeout > o_ptr->pval) o_ptr->timeout = o_ptr->pval;
+
+ /* Use up the potions in the pack */
+ if (item >= 0)
+ {
+ inven_item_increase(item, 0 - num);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Use up the potions on the floor */
+ else
+ {
+ floor_item_increase(0 - item, 0 - num);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+}
+
+
+/*
+ * Hook to determine if an object is quaffable
+ */
+static bool item_tester_hook_quaffable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_POTION) || (o_ptr->tval == TV_POTION2)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+static bool quaff_potion(int tval, int sval, int pval, int pval2)
+{
+ int ident = FALSE;
+
+
+ /* "Traditional" potions */
+ if (tval == TV_POTION)
+ {
+ switch (sval)
+ {
+ case SV_POTION_WATER:
+ case SV_POTION_APPLE_JUICE:
+ case SV_POTION_SLIME_MOLD:
+ {
+ msg_print("You feel less thirsty.");
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SLOWNESS:
+ {
+ if (set_slow(p_ptr->slow + randint(25) + 15)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SALT_WATER:
+ {
+ msg_print("The potion makes you vomit!");
+ (void)set_food(PY_FOOD_STARVE - 1);
+ (void)set_poisoned(0);
+ (void)set_paralyzed(p_ptr->paralyzed + 4);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_POISON:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + rand_int(15) + 10))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_POTION_BLINDNESS:
+ {
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + rand_int(100) + 100))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ /* Booze */
+ case SV_POTION_CONFUSION:
+ {
+ if (!((p_ptr->resist_conf) || (p_ptr->resist_chaos)))
+ {
+ if (set_confused(p_ptr->confused + rand_int(20) + 15))
+ {
+ ident = TRUE;
+ }
+ if (randint(2) == 1)
+ {
+ if (set_image(p_ptr->image + rand_int(150) + 150))
+ {
+ ident = TRUE;
+ }
+ }
+ if (randint(13) == 1)
+ {
+ ident = TRUE;
+ if (randint(3) == 1) lose_all_info();
+ else wiz_dark();
+ teleport_player(100);
+ wiz_dark();
+ msg_print("You wake up elsewhere with a sore head...");
+ msg_print("You can't remember a thing, or how you got here!");
+ }
+ }
+
+ break;
+ }
+
+ case SV_POTION_SLEEP:
+ {
+ if (!p_ptr->free_act)
+ {
+ if (set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_POTION_LOSE_MEMORIES:
+ {
+ if (!p_ptr->hold_life && (p_ptr->exp > 0))
+ {
+ msg_print("You feel your memories fade.");
+ lose_exp(p_ptr->exp / 4);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RUINATION:
+ {
+ msg_print("Your nerves and muscles feel weak and lifeless!");
+ take_hit(damroll(10, 10), "a potion of Ruination");
+ (void)dec_stat(A_DEX, 25, TRUE);
+ (void)dec_stat(A_WIS, 25, TRUE);
+ (void)dec_stat(A_CON, 25, TRUE);
+ (void)dec_stat(A_STR, 25, TRUE);
+ (void)dec_stat(A_CHR, 25, TRUE);
+ (void)dec_stat(A_INT, 25, TRUE);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_STR:
+ {
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_INT:
+ {
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_WIS:
+ {
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_DEX:
+ {
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_CON:
+ {
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEC_CHR:
+ {
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DETONATIONS:
+ {
+ msg_print("Massive explosions rupture your body!");
+ take_hit(damroll(50, 20), "a potion of Detonation");
+ (void)set_stun(p_ptr->stun + 75);
+ (void)set_cut(p_ptr->cut + 5000);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_DEATH:
+ {
+ msg_print("A feeling of Death flows through your body.");
+ take_hit(5000, "a potion of Death");
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INFRAVISION:
+ {
+ if (set_tim_infra(p_ptr->tim_infra + 100 + randint(100)))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_DETECT_INVIS:
+ {
+ if (set_tim_invis(p_ptr->tim_invis + 12 + randint(12)))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_SLOW_POISON:
+ {
+ if (set_poisoned(p_ptr->poisoned / 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_POISON:
+ {
+ if (set_poisoned(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BOLDNESS:
+ {
+ if (set_afraid(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SPEED:
+ {
+ if (!p_ptr->fast)
+ {
+ if (set_fast(randint(25) + 15, 10)) ident = TRUE;
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESIST_HEAT:
+ {
+ if (set_oppose_fire(p_ptr->oppose_fire + randint(10) + 10))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESIST_COLD:
+ {
+ if (set_oppose_cold(p_ptr->oppose_cold + randint(10) + 10))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_HEROISM:
+ {
+ if (set_afraid(0)) ident = TRUE;
+ if (set_hero(p_ptr->hero + randint(25) + 25)) ident = TRUE;
+ if (hp_player(10)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BESERK_STRENGTH:
+ {
+ if (set_afraid(0)) ident = TRUE;
+ if (set_shero(p_ptr->shero + randint(25) + 25)) ident = TRUE;
+ if (hp_player(30)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_LIGHT:
+ {
+ if (hp_player(damroll(2, 8))) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_cut(p_ptr->cut - 10)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_SERIOUS:
+ {
+ if (hp_player(damroll(4, 8))) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_cut((p_ptr->cut / 2) - 50)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURE_CRITICAL:
+ {
+ if (hp_player(damroll(6, 8))) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_HEALING:
+ {
+ if (hp_player(300)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_STAR_HEALING:
+ {
+ if (hp_player(1200)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_LIFE:
+ {
+ msg_print("You feel life flow through your body!");
+ restore_level();
+ hp_player(5000);
+ (void)set_poisoned(0);
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+ p_ptr->black_breath = FALSE;
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RESTORE_MANA:
+ {
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ msg_print("Your feel your head clear.");
+ p_ptr->redraw |= (PR_MANA);
+ p_ptr->window |= (PW_PLAYER);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESTORE_EXP:
+ {
+ if (restore_level()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_STR:
+ {
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_INT:
+ {
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_WIS:
+ {
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_DEX:
+ {
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_CON:
+ {
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_RES_CHR:
+ {
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_STR:
+ {
+ if (do_inc_stat(A_STR)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_INT:
+ {
+ if (do_inc_stat(A_INT)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_WIS:
+ {
+ if (do_inc_stat(A_WIS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_DEX:
+ {
+ if (do_inc_stat(A_DEX)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_CON:
+ {
+ if (do_inc_stat(A_CON)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INC_CHR:
+ {
+ if (do_inc_stat(A_CHR)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_AUGMENTATION:
+ {
+ if (do_inc_stat(A_STR)) ident = TRUE;
+ if (do_inc_stat(A_INT)) ident = TRUE;
+ if (do_inc_stat(A_WIS)) ident = TRUE;
+ if (do_inc_stat(A_DEX)) ident = TRUE;
+ if (do_inc_stat(A_CON)) ident = TRUE;
+ if (do_inc_stat(A_CHR)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_ENLIGHTENMENT:
+ {
+ msg_print("An image of your surroundings forms in your mind...");
+ wiz_lite();
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ {
+ msg_print("You begin to feel more enlightened...");
+ msg_print(NULL);
+ wiz_lite_extra();
+ (void)do_inc_stat(A_INT);
+ (void)do_inc_stat(A_WIS);
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+ (void)detect_treasure(DEFAULT_RADIUS);
+ (void)detect_objects_gold(DEFAULT_RADIUS);
+ (void)detect_objects_normal(DEFAULT_RADIUS);
+ identify_pack();
+ self_knowledge(NULL);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_SELF_KNOWLEDGE:
+ {
+ msg_print("You begin to know yourself a little better...");
+ msg_print(NULL);
+ self_knowledge(NULL);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_EXPERIENCE:
+ {
+ if (p_ptr->exp < PY_MAX_EXP)
+ {
+ s32b ee = (p_ptr->exp / 2) + 10;
+ if (ee > 100000L) ee = 100000L;
+ msg_print("You feel more experienced.");
+ gain_exp(ee);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESISTANCE:
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURING:
+ {
+ if (hp_player(50)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+ if (set_image(0)) ident = TRUE;
+ if (heal_insanity(50)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INVULNERABILITY:
+ {
+ (void)set_invuln(p_ptr->invuln + randint(7) + 7);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_NEW_LIFE:
+ {
+ do_cmd_rerate();
+#if 0 /* DGDGDGDG -- No, losing corruption should be near impossible, maybe a quest to do it once but thats it */
+ lose_all_corruptions();
+#endif
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BLOOD:
+ {
+ msg_print("You feel the blood of life running through your veins!");
+ ident = TRUE;
+ p_ptr->allow_one_death++;
+
+ break;
+ }
+
+ case SV_POTION_MUTATION:
+ {
+ msg_print("You feel the dark corruptions of Morgoth coming over you!");
+ gain_random_corruption(0);
+ ident = TRUE;
+ break;
+ }
+
+ case SV_POTION_INVIS:
+ {
+ int t = 30 + randint(30);
+
+ if (set_invis(p_ptr->tim_invis + t, 35))
+ {
+ ident = TRUE;
+ }
+ set_tim_invis(p_ptr->tim_invis + t);
+
+ break;
+ }
+
+ case SV_POTION_LEARNING:
+ {
+ p_ptr->skill_points += rand_range(4, 10 + luck( -4, 4));
+ cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ /* "Duplicate" potions */
+ else
+ {
+ switch (sval)
+ {
+ case SV_POTION2_MIMIC:
+ {
+ if (!p_ptr->mimic_form)
+ {
+ s32b time;
+
+ call_lua("get_mimic_rand_dur", "(d)", "d", pval2, &time);
+
+ set_mimic(time, pval2, (p_ptr->lev * 2) / 3);
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION2_CURE_LIGHT_SANITY:
+ {
+ if (heal_insanity(damroll(4, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION2_CURE_SERIOUS_SANITY:
+ {
+ if (heal_insanity(damroll(8, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION2_CURE_CRITICAL_SANITY:
+ {
+ if (heal_insanity(damroll(12, 8))) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION2_CURE_SANITY:
+ {
+ if (heal_insanity(damroll(10, 100))) ident = TRUE;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ return (ident);
+}
+
+
+/*
+ * Quaff a potion (from the pack or the floor)
+ */
+void do_cmd_quaff_potion(void)
+{
+ int item, ident, lev;
+
+ object_type *o_ptr;
+
+ object_type *q_ptr, forge;
+
+ cptr q, s;
+
+
+ /* Restrict choices to potions */
+ item_tester_hook = item_tester_hook_quaffable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Potion full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Quaff which potion? ";
+ s = "You have no potions to quaff.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Sound */
+ sound(SOUND_QUAFF);
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Analyze the potion */
+ if (process_hooks_ret(HOOK_QUAFF, "d", "(O)", o_ptr))
+ {
+ ident = process_hooks_return[0].num;
+ }
+ else
+ {
+ ident = quaff_potion(o_ptr->tval, o_ptr->sval, o_ptr->pval, o_ptr->pval2);
+ }
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* The item has been tried */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ if (get_skill(SKILL_ALCHEMY))
+ {
+ if (item >= 0)
+ {
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1));
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Potions can feed the player */
+ (void)set_food(p_ptr->food + o_ptr->pval);
+
+
+ /* Destroy a potion in the pack */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Destroy a potion on the floor */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+}
+
+
+/*
+ * Drink from a fountain
+ */
+void do_cmd_drink_fountain(void)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ bool ident;
+
+ int tval, sval, pval = 0;
+
+ int i;
+
+ char ch;
+
+
+ /* Is the fountain empty? */
+ if (c_ptr->special2 <= 0)
+ {
+ msg_print("The fountain is dried out.");
+ return;
+ }
+
+ /* We quaff or we fill ? */
+ if (!get_com("Do you want to [Q]uaff or [F]ill from the fountain? ", &ch))
+ {
+ return;
+ }
+
+ if ((ch == 'F') || (ch == 'f'))
+ {
+ do_cmd_fill_bottle();
+
+ return;
+ }
+
+ else if ((ch == 'Q') || (ch == 'q'))
+ {
+ if (c_ptr->special <= SV_POTION_LAST)
+ {
+ tval = TV_POTION;
+ sval = c_ptr->special;
+ }
+ else
+ {
+ tval = TV_POTION2;
+ sval = c_ptr->special - SV_POTION_LAST;
+ }
+
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ if (k_ptr->tval != tval) continue;
+ if (k_ptr->sval != sval) continue;
+
+ pval = k_ptr->pval;
+
+ break;
+ }
+
+ ident = quaff_potion(tval, sval, pval, 0);
+
+ c_ptr->special2--;
+
+ if (c_ptr->special2 <= 0)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
+ }
+
+ if (ident) c_ptr->info |= CAVE_IDNT;
+ }
+}
+
+
+/*
+ * Fill an empty bottle
+ */
+void do_cmd_fill_bottle(void)
+{
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ int tval, sval, item, amt = 1;
+
+ object_type *q_ptr, *o_ptr, forge;
+
+ cptr q, s;
+
+ /* Is the fountain empty? */
+ /*
+ * This check is redundant as it is done in do_cmd_drink_fountain()
+ * but I keep this because someone might want to call this directly.
+ * -- Kusunose
+ */
+ if (c_ptr->special2 <= 0)
+ {
+ msg_print("The fountain has dried up.");
+ return;
+ }
+
+ /* Determine the tval/sval of the potion */
+ if (c_ptr->special <= SV_POTION_LAST)
+ {
+ tval = TV_POTION;
+ sval = c_ptr->special;
+ }
+ else
+ {
+ tval = TV_POTION2;
+ sval = c_ptr->special - SV_POTION_LAST;
+ }
+
+ /* Restrict choices to bottles */
+ item_tester_tval = TV_BOTTLE;
+
+ /* Get an item */
+ q = "Fill which bottle? ";
+ s = "You have no bottles to fill.";
+ if (!get_item(&item, q, s, (USE_INVEN))) return;
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ if (amt > c_ptr->special2) amt = c_ptr->special2;
+
+ /* Destroy bottles in the pack */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Destroy bottles on the floor */
+ else
+ {
+ floor_item_increase(0 - item, -amt);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ /* Create the potion */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(tval, sval));
+ q_ptr->number = amt;
+
+ if (c_ptr->info & CAVE_IDNT)
+ {
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ }
+
+ inven_carry(q_ptr, TRUE);
+
+ c_ptr->special2 -= amt;
+
+ if (c_ptr->special2 <= 0)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
+ }
+
+ return;
+}
+
+
+/*
+ * Curse the players armor
+ */
+bool curse_armor(void)
+{
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Curse the body armor */
+ o_ptr = &p_ptr->inventory[INVEN_BODY];
+
+ /* Nothing to curse */
+ if (!o_ptr->k_idx) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Attempt a saving throw for artifacts */
+ if (((o_ptr->art_name) || artifact_p(o_ptr)) && (rand_int(100) < 50))
+ {
+ /* Cool */
+ msg_format("A terrible black aura tries to surround your armour, "
+ "but your %s resists the effects!", o_name);
+ }
+
+ /* not artifact or failed save... */
+ else
+ {
+ /* Oops */
+ msg_format("A terrible black aura blasts your %s!", o_name);
+
+ /* Blast the armor */
+ o_ptr->name1 = 0;
+ o_ptr->name2 = EGO_BLASTED;
+ o_ptr->to_a = 0 - randint(5) - randint(5);
+ o_ptr->to_h = 0;
+ o_ptr->to_d = 0;
+ o_ptr->ac = 0;
+ o_ptr->dd = 0;
+ o_ptr->ds = 0;
+ o_ptr->art_flags1 = 0;
+ o_ptr->art_flags2 = 0;
+ o_ptr->art_flags3 = 0;
+ o_ptr->art_flags4 = 0;
+
+ /* Curse it */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Curse the players weapon
+ */
+bool curse_weapon(void)
+{
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Curse the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Nothing to curse */
+ if (!o_ptr->k_idx) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Attempt a saving throw */
+ if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 50))
+ {
+ /* Cool */
+ msg_format("A terrible black aura tries to surround your weapon, "
+ "but your %s resists the effects!", o_name);
+ }
+
+ /* not artifact or failed save... */
+ else
+ {
+ /* Oops */
+ msg_format("A terrible black aura blasts your %s!", o_name);
+
+ /* Shatter the weapon */
+ o_ptr->name1 = 0;
+ o_ptr->name2 = EGO_SHATTERED;
+ o_ptr->to_h = 0 - randint(5) - randint(5);
+ o_ptr->to_d = 0 - randint(5) - randint(5);
+ o_ptr->to_a = 0;
+ o_ptr->ac = 0;
+ o_ptr->dd = 0;
+ o_ptr->ds = 0;
+ o_ptr->art_flags1 = 0;
+ o_ptr->art_flags2 = 0;
+ o_ptr->art_flags3 = 0;
+ o_ptr->art_flags4 = 0;
+
+
+ /* Curse it */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is readable
+ */
+static bool item_tester_hook_readable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_SCROLL) || (o_ptr->tval == TV_PARCHMENT)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Read a scroll (from the pack or floor).
+ *
+ * Certain scrolls can be "aborted" without losing the scroll. These
+ * include scrolls with no effects but recharge or identify, which are
+ * cancelled before use. XXX Reading them still takes a turn, though.
+ */
+void do_cmd_read_scroll(void)
+{
+ int item, k, used_up, ident, lev;
+
+ object_type *o_ptr;
+
+ object_type *q_ptr, forge;
+
+ cptr q, s;
+
+
+ /* Check some conditions */
+ if (p_ptr->blind)
+ {
+ msg_print("You can't see anything.");
+ return;
+ }
+
+ if (no_lite())
+ {
+ msg_print("You have no light by which to read.");
+ return;
+ }
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ /* Restrict choices to scrolls */
+ item_tester_hook = item_tester_hook_readable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Scroll full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Read which scroll? ";
+ s = "You have no scrolls to read.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Assume the scroll will get used up */
+ used_up = TRUE;
+
+ /* New scripts, can override the ingame code */
+ if (process_hooks_ret(HOOK_READ, "dd", "(O)", o_ptr))
+ {
+ used_up = process_hooks_return[0].num;
+ ident = process_hooks_return[1].num;
+ }
+ /* Traditional scrolls */
+ else if (o_ptr->tval == TV_SCROLL)
+ {
+ /* Analyze the scroll */
+ switch (o_ptr->sval)
+ {
+ case SV_SCROLL_MASS_RESURECTION:
+ {
+ int k;
+
+ ident = TRUE;
+ msg_print("You feel the souls of the dead coming back "
+ "from the Halls of Mandos.");
+
+ for (k = 0; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & RF1_UNIQUE &&
+ !(r_ptr->flags9 & RF9_SPECIAL_GENE))
+ {
+ r_ptr->max_num = 1;
+ }
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_DEINCARNATION:
+ {
+ if (!get_check("Do you really want to leave your body? "
+ "(beware, it'll be destroyed!) "))
+ {
+ used_up = FALSE;
+ break;
+ }
+
+ do_cmd_leave_body(FALSE);
+
+ ident = TRUE;
+ used_up = TRUE;
+
+ break;
+ }
+
+ /* original didn't set used_up flag ??? -- pelpel */
+ case SV_SCROLL_RESET_RECALL:
+ {
+ if (!reset_recall(TRUE))
+ {
+ used_up = FALSE;
+ break;
+ }
+
+ msg_format("Recall reset to %s at level %d.",
+ d_info[p_ptr->recall_dungeon].name + d_name,
+ max_dlv[p_ptr->recall_dungeon]);
+
+ ident = TRUE;
+ used_up = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DIVINATION:
+ {
+ int i, count = 0;
+ char buf[120];
+
+ while (count < 1000)
+ {
+ count++;
+ i = rand_int(MAX_FATES);
+ if (!fates[i].fate) continue;
+ if (fates[i].know) continue;
+
+ msg_print("A message appears on the scroll. It says:");
+ msg_print(NULL);
+
+ fate_desc(buf, i);
+ msg_format("%s", buf);
+
+ msg_print(NULL);
+ msg_print("The scroll disappears in a puff of smoke!");
+
+ fates[i].know = TRUE;
+ ident = TRUE;
+
+ break;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_DARKNESS:
+ {
+ if (!(p_ptr->resist_blind) && !(p_ptr->resist_dark))
+ {
+ (void)set_blind(p_ptr->blind + 3 + randint(5));
+ }
+ if (unlite_area(10, 3)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_AGGRAVATE_MONSTER:
+ {
+ msg_print("There is a high-pitched humming noise.");
+ aggravate_monsters(1);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_CURSE_ARMOR:
+ {
+ if (curse_armor()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_CURSE_WEAPON:
+ {
+ if (curse_weapon()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_SUMMON_MONSTER:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, dun_level, 0))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_SUMMON_MINE:
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE))
+ {
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_SUMMON_UNDEAD:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD))
+ {
+ ident = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_TRAP_CREATION:
+ {
+ if (trap_creation()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_PHASE_DOOR:
+ {
+ teleport_player(10);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_TELEPORT:
+ {
+ teleport_player(100);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_TELEPORT_LEVEL:
+ {
+ (void)teleport_player_level();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_WORD_OF_RECALL:
+ {
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
+ {
+ used_up = FALSE;
+ }
+ else
+ {
+ recall_player(21, 15);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!ident_spell()) used_up = FALSE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!identify_fully()) used_up = FALSE;
+
+ break;
+ }
+
+ case SV_SCROLL_REMOVE_CURSE:
+ {
+ if (remove_curse())
+ {
+ msg_print("You feel as if someone is watching over you.");
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_REMOVE_CURSE:
+ {
+ remove_all_curse();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_ARMOR:
+ {
+ ident = TRUE;
+
+ if (!enchant_spell(0, 0, 1, 0)) used_up = FALSE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_WEAPON_TO_HIT:
+ {
+ if (!enchant_spell(1, 0, 0, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_WEAPON_TO_DAM:
+ {
+ if (!enchant_spell(0, 1, 0, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ENCHANT_WEAPON_PVAL:
+ {
+ if (!enchant_spell(0, 0, 0, 1)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_ENCHANT_ARMOR:
+ {
+ if (!enchant_spell(0, 0, randint(3) + 2, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_ENCHANT_WEAPON:
+ {
+ if (!enchant_spell(randint(3), randint(3), 0, 0)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_RECHARGING:
+ {
+ if (!recharge(60)) used_up = FALSE;
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_LIGHT:
+ {
+ if (lite_area(damroll(2, 8), 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_MAPPING:
+ {
+ map_area();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_GOLD:
+ {
+ if (detect_treasure(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_objects_gold(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_ITEM:
+ {
+ if (detect_objects_normal(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_TRAP:
+ {
+ if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_DOOR:
+ {
+ if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DETECT_INVIS:
+ {
+ if (detect_monsters_invis(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_SATISFY_HUNGER:
+ {
+ if (set_food(PY_FOOD_MAX - 1)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_BLESSING:
+ {
+ if (set_blessed(p_ptr->blessed + randint(12) + 6)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_HOLY_CHANT:
+ {
+ if (set_blessed(p_ptr->blessed + randint(24) + 12)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_HOLY_PRAYER:
+ {
+ if (set_blessed(p_ptr->blessed + randint(48) + 24)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_MONSTER_CONFUSION:
+ {
+ if (p_ptr->confusing == 0)
+ {
+ msg_print("Your hands begin to glow.");
+ p_ptr->confusing = TRUE;
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_SCROLL_PROTECTION_FROM_EVIL:
+ {
+ k = 3 * p_ptr->lev;
+ if (set_protevil(p_ptr->protevil + randint(25) + k)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_RUNE_OF_PROTECTION:
+ {
+ warding_glyph();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_TRAP_DOOR_DESTRUCTION:
+ {
+ if (destroy_doors_touch()) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_DESTRUCTION:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE);
+ }
+ else
+ {
+ msg_print("The dungeon trembles...");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_DISPEL_UNDEAD:
+ {
+ if (dispel_undead(60)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_GENOCIDE:
+ {
+ (void)genocide(TRUE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_MASS_GENOCIDE:
+ {
+ (void)mass_genocide(TRUE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ACQUIREMENT:
+ {
+ acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_STAR_ACQUIREMENT:
+ {
+ acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ /* ZAngband scrolls */
+ case SV_SCROLL_FIRE:
+ {
+ fire_ball(GF_FIRE, 0, 150, 4);
+
+ /*
+ * Note: "Double" damage since it is centered on
+ * the player ...
+ */
+ if (!p_ptr->oppose_fire && !p_ptr->resist_fire &&
+ !p_ptr->immune_fire)
+ {
+ take_hit(50 + randint(50) + (p_ptr->sensible_fire) ? 20 : 0,
+ "a Scroll of Fire");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+
+ case SV_SCROLL_ICE:
+ {
+ fire_ball(GF_ICE, 0, 175, 4);
+
+ if (!p_ptr->oppose_cold && !p_ptr->resist_cold &&
+ !p_ptr->immune_cold)
+ {
+ take_hit(100 + randint(100), "a Scroll of Ice");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_CHAOS:
+ {
+ fire_ball(GF_CHAOS, 0, 222, 4);
+
+ if (!p_ptr->resist_chaos)
+ {
+ take_hit(111 + randint(111), "a Scroll of Chaos");
+ }
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_RUMOR:
+ {
+ char rumour[80];
+
+ msg_print("There is message on the scroll. It says:");
+ msg_print(NULL);
+
+ /* Pick random text */
+ switch (randint(20))
+ {
+ case 1:
+ {
+ get_rnd_line("chainswd.txt", rumour);
+
+ break;
+ }
+
+ case 2:
+ {
+ get_rnd_line("error.txt", rumour);
+
+ break;
+ }
+
+ case 3:
+ case 4:
+ case 5:
+ {
+ get_rnd_line("death.txt", rumour);
+
+ break;
+ }
+
+ default:
+ {
+ get_rnd_line("rumors.txt", rumour);
+
+ break;
+ }
+ }
+
+ msg_format("%s", rumour);
+ msg_print(NULL);
+
+ msg_print("The scroll disappears in a puff of smoke!");
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_SCROLL_ARTIFACT:
+ {
+ ident = TRUE;
+
+ if (!artifact_scroll()) used_up = FALSE;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ /* Other readable items */
+ else
+ {
+ /* Maps */
+ if (o_ptr->sval >= 200)
+ {
+ int i, n;
+ char buf[80], fil[20];
+
+ strnfmt(fil, 20, "book-%d.txt", o_ptr->sval);
+
+ n = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, -1));
+
+ /* Parse all the fields */
+ for (i = 0; i < n; i += 4)
+ {
+ /* Grab the fields */
+ int x = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 0));
+ int y = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 1));
+ int w = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 2));
+ int h = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 3));
+
+ reveal_wilderness_around_player(y, x, h, w);
+ }
+ }
+
+ /* Normal parchements */
+ else
+ {
+ /* Save screen */
+ screen_save();
+
+ /* Get the filename */
+ q = format("book-%d.txt", o_ptr->sval);
+
+ /* Peruse the help file */
+ (void)show_file(q, NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+
+ if (o_ptr->sval >= 100)
+ {
+ inscription_info[o_ptr->sval - 100].know = TRUE;
+ }
+
+ used_up = FALSE;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* The item was tried */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Hack -- allow certain scrolls to be "preserved" */
+ if (!used_up) return;
+
+ sound(SOUND_SCROLL);
+
+ /* Destroy a scroll in the pack */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Destroy a scroll on the floor */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ if (get_skill(SKILL_ALCHEMY))
+ {
+ if (item >= 0)
+ {
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_SCROLL, SV_SCROLL_NOTHING));
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+ }
+}
+
+
+
+/* Set the 'stick mode' on */
+void set_stick_mode(object_type *o_ptr)
+{
+ s32b bonus = o_ptr->pval3 & 0xFFFF;
+ s32b max = o_ptr->pval3 >> 16;
+
+ exec_lua(format("get_level_use_stick = %d; get_level_max_stick = %d", bonus, max));
+}
+/* Remove 'stick mode' */
+void unset_stick_mode()
+{
+ exec_lua("get_level_use_stick = -1; get_level_max_stick = -1");
+}
+
+
+/*
+ * Use a staff. -RAK-
+ *
+ * One charge of one staff disappears.
+ *
+ * Hack -- staffs of identify can be "cancelled".
+ */
+void do_cmd_use_staff(void)
+{
+ int item, ident, chance;
+
+ s32b obvious, use_charge;
+
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to wands */
+ item_tester_tval = TV_STAFF;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Staff full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Use which staff? ";
+ s = "You have no staff to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Mega-Hack -- refuse to use a pile from the ground */
+ if ((item < 0) && (o_ptr->number > 1))
+ {
+ msg_print("You must first pick up the staffs.");
+ return;
+ }
+
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* get the chance */
+ chance = exec_lua(format("return spell_chance(%d)", o_ptr->pval2));
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance /= 3;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if (magik(chance))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to use the staff properly.");
+ sound(SOUND_FAIL);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+ /* Notice empty staffs */
+ if (o_ptr->pval <= 0)
+ {
+ if (flush_failure) flush();
+ msg_print("The staff has no charges left.");
+ o_ptr->ident |= (IDENT_EMPTY);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+
+ /* Analyze the staff */
+ call_lua("activate_stick", "(d)", "dd", o_ptr->pval2, &obvious, &use_charge);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Tried the item */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Hack -- some uses are "free" */
+ if (!use_charge)
+ {
+ /* Leave device mode */
+ unset_stick_mode();
+
+ return;
+ }
+
+ /* An identification was made */
+ if (obvious)
+ {
+ object_aware(o_ptr);
+ }
+
+ /* Use a single charge */
+ o_ptr->pval--;
+
+ /* XXX Hack -- unstack if necessary */
+ if ((item >= 0) && (o_ptr->number > 1))
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Restore the charges */
+ o_ptr->pval++;
+
+ /* Unstack the used item */
+ o_ptr->number--;
+ item = inven_carry(q_ptr, FALSE);
+
+ /* Message */
+ msg_print("You unstack your staff.");
+ }
+
+ /* Describe charges in the pack */
+ if (item >= 0)
+ {
+ inven_item_charges(item);
+ }
+
+ /* Describe charges on the floor */
+ else
+ {
+ floor_item_charges(0 - item);
+ }
+
+ /* Leave device mode */
+ unset_stick_mode();
+}
+
+
+/*
+ * Aim a wand (from the pack or floor).
+ *
+ * Use a single charge from a single item.
+ * Handle "unstacking" in a logical manner.
+ *
+ * For simplicity, you cannot use a stack of items from the
+ * ground. This would require too much nasty code.
+ *
+ * There are no wands which can "destroy" themselves, in the p_ptr->inventory
+ * or on the ground, so we can ignore this possibility. Note that this
+ * required giving "wand of wonder" the ability to ignore destruction
+ * by electric balls.
+ *
+ * All wands can be "cancelled" at the "Direction?" prompt for free.
+ *
+ * Note that the basic "bolt" wands do slightly less damage than the
+ * basic "bolt" rods, but the basic "ball" wands do the same damage
+ * as the basic "ball" rods.
+ */
+void do_cmd_aim_wand(void)
+{
+ s32b obvious, use_charge;
+
+ int item, ident, chance, sval;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to wands */
+ item_tester_tval = TV_WAND;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Wand full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Aim which wand? ";
+ s = "You have no wand to aim.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Mega-Hack -- refuse to aim a pile from the ground */
+ if ((item < 0) && (o_ptr->number > 1))
+ {
+ msg_print("You must first pick up the wands.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ /* get the chance */
+ chance = exec_lua(format("return spell_chance(%d)", o_ptr->pval2));
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance /= 3;
+ }
+
+ /* Roll for usage */
+ if (magik(chance))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to use the wand properly.");
+ sound(SOUND_FAIL);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+ /* The wand is already empty! */
+ if (o_ptr->pval <= 0)
+ {
+ if (flush_failure) flush();
+ msg_print("The wand has no charges left.");
+ o_ptr->ident |= (IDENT_EMPTY);
+
+ /* Leave device mode */
+ unset_stick_mode();
+ return;
+ }
+
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+
+ /* XXX Hack -- Extract the "sval" effect */
+ sval = o_ptr->sval;
+
+ /* Analyze the wand */
+ call_lua("activate_stick", "(d)", "dd", o_ptr->pval2, &obvious, &use_charge);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Mark it as tried */
+ object_tried(o_ptr);
+
+ /* Hack -- some uses are "free" */
+ if (!use_charge)
+ {
+ /* Leave device mode */
+ unset_stick_mode();
+
+ return;
+ }
+
+ /* An identification was made */
+ if (obvious)
+ {
+ object_aware(o_ptr);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Use a single charge */
+ o_ptr->pval--;
+
+ /* Describe the charges in the pack */
+ if (item >= 0)
+ {
+ inven_item_charges(item);
+ }
+
+ /* Describe the charges on the floor */
+ else
+ {
+ floor_item_charges(0 - item);
+ }
+
+ /* Leave device mode */
+ unset_stick_mode();
+}
+
+
+
+
+
+
+/*
+ * Activate (zap) a Rod
+ *
+ * Unstack fully charged rods as needed.
+ *
+ * Hack -- rods of perception/genocide can be "cancelled"
+ * All rods can be cancelled at the "Direction?" prompt
+ */
+
+
+/*
+ * Hook to determine if an object is zapable
+ */
+static bool item_tester_hook_zapable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_ROD_MAIN)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Hook to determine if an object is attachable
+ */
+static bool item_tester_hook_attachable(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_ROD_MAIN) &&
+ (o_ptr->pval == SV_ROD_NOTHING)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Combine a rod and a rod tip
+ */
+void zap_combine_rod_tip(object_type *q_ptr, int tip_item)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ object_kind *k_ptr;
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+ s32b cost;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to rods */
+ item_tester_hook = item_tester_hook_attachable;
+
+ /* Get an item */
+ q = "Attach the rod tip with which rod? ";
+ s = "You have no rod to attach to.";
+ if (!get_item(&item, q, s, (USE_INVEN))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ k_ptr = &k_info[o_ptr->k_idx];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ k_ptr = &k_info[o_ptr->k_idx];
+ }
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Calculate rod tip's mana cost */
+ cost = q_ptr->pval;
+
+ if (f4 & TR4_CHEAPNESS)
+ {
+ cost /= 2;
+ }
+
+ /*
+ * The rod must have at least the same mana capacity as the
+ * rod tip spell needs
+ */
+ if (o_ptr->pval2 < cost)
+ {
+ msg_print("This rod doesn't have enough mana for the rod tip.");
+ return;
+ }
+
+ /* Attach the tip to the rod */
+ o_ptr->pval = q_ptr->sval;
+
+ /* Destroy a rod tip in the pack */
+ if (tip_item >= 0)
+ {
+ inven_item_increase(tip_item, -1);
+ inven_item_describe(tip_item);
+ inven_item_optimize(tip_item);
+ }
+ /* Destroy a rod tip on the floor */
+ else
+ {
+ floor_item_increase(0 - tip_item, -1);
+ floor_item_describe(0 - tip_item);
+ floor_item_optimize(0 - tip_item);
+ }
+}
+
+
+/*
+ * Zap a rod, or attack a rod tip to a rod
+ */
+void do_cmd_zap_rod(void)
+{
+ int item, ident, chance, dir, lev;
+
+ int cost;
+
+ bool require_dir;
+
+ object_type *o_ptr;
+
+ object_kind *k_ptr;
+
+ object_kind *tip_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+ /* Hack -- let perception get aborted */
+ bool use_charge = TRUE;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* Restrict choices to rods */
+ item_tester_hook = item_tester_hook_zapable;
+
+ /* Set up the extra finder */
+ get_item_hook_find_obj_what = "Rod full name? ";
+ get_item_extra_hook = get_item_hook_find_obj;
+
+ /* Get an item */
+ q = "Zap which rod? ";
+ s = "You have no rod to zap.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ k_ptr = &k_info[o_ptr->k_idx];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ k_ptr = &k_info[o_ptr->k_idx];
+ }
+
+
+ /* "Zapping" a Rod Tip on rod of nothing will attach it */
+ if (o_ptr->tval == TV_ROD)
+ {
+ if (item >= 0)
+ {
+ zap_combine_rod_tip(o_ptr, item);
+ return;
+ }
+ else
+ {
+ msg_print("You can't zap a rod tip that's on the floor.");
+ return;
+ }
+ }
+
+
+ /* Non-directed rods */
+ if (o_ptr->pval < SV_ROD_MIN_DIRECTION)
+ {
+ require_dir = FALSE;
+ }
+
+ /* Some rods always require direction */
+ else
+ {
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_DETECT_TRAP:
+ case SV_ROD_HAVOC:
+ case SV_ROD_HOME:
+ {
+ require_dir = FALSE;
+ break;
+ }
+
+ default:
+ {
+ require_dir = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Get a direction (unless KNOWN not to need it) */
+ if (!object_aware_p(o_ptr) || require_dir)
+ {
+ /* Get a direction, allow cancel */
+ if (!get_aim_dir(&dir)) return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FAST_CAST) energy_use /= 2;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Extract the item level */
+ tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)];
+ lev = k_info[lookup_kind(TV_ROD, o_ptr->pval)].level;
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* High level objects are harder */
+ chance = chance - ((lev > 50) ? 50 : lev);
+
+ if (chance <= 0)
+ {
+ chance = 1;
+ }
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance *= 10;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ /* Flush input if necessary */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to use the rod properly.");
+
+ sound(SOUND_FAIL);
+
+ return;
+ }
+
+ /* Extract mana cost */
+ cost = tip_ptr->pval;
+
+ /* "Cheapness" ego halven the cost */
+ if (f4 & TR4_CHEAPNESS) cost = cost / 2;
+
+ /* A single rod is still charging */
+ if (o_ptr->timeout < cost)
+ {
+ /* Flush input if necessary */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The rod does not have enough mana yet.");
+
+ return;
+ }
+
+ /* Increase the timeout by the rod kind's pval. */
+ o_ptr->timeout -= cost;
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Analyze the rod */
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_HOME:
+ {
+ ident = TRUE;
+
+ do_cmd_home_trump();
+
+ break;
+ }
+
+ case SV_ROD_DETECT_TRAP:
+ {
+ if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DETECT_DOOR:
+ {
+ if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!ident_spell()) use_charge = FALSE;
+
+ break;
+ }
+
+ case SV_ROD_RECALL:
+ {
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
+ {
+ use_charge = FALSE;
+ }
+ else
+ {
+ recall_player(21, 15);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_ROD_ILLUMINATION:
+ {
+ if (lite_area(damroll(2, 8), 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_MAPPING:
+ {
+ map_area();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DETECTION:
+ {
+ detect_all(DEFAULT_RADIUS);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_PROBING:
+ {
+ probing();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_CURING:
+ {
+ if (set_blind(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+ if (set_image(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_HEALING:
+ {
+ if (hp_player(500)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_RESTORATION:
+ {
+ if (restore_level()) ident = TRUE;
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SPEED:
+ {
+ if (!p_ptr->fast)
+ {
+ if (set_fast(randint(30) + 15, 10)) ident = TRUE;
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ break;
+ }
+
+ case SV_ROD_TELEPORT_AWAY:
+ {
+ if (teleport_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DISARMING:
+ {
+ if (disarm_trap(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_LITE:
+ {
+ msg_print("A line of blue shimmering light appears.");
+ lite_line(dir);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SLEEP_MONSTER:
+ {
+ if (sleep_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SLOW_MONSTER:
+ {
+ if (slow_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DRAIN_LIFE:
+ {
+ if (drain_life(dir, 75)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_POLYMORPH:
+ {
+ if (poly_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ACID_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ELEC_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_ELEC, dir, damroll(3, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_FIRE_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_FIRE, dir, damroll(8, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_COLD_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_COLD, dir, damroll(5, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ACID_BALL:
+ {
+ fire_ball(GF_ACID, dir, 60, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ELEC_BALL:
+ {
+ fire_ball(GF_ELEC, dir, 32, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_FIRE_BALL:
+ {
+ fire_ball(GF_FIRE, dir, 72, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_COLD_BALL:
+ {
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_HAVOC:
+ {
+ call_chaos();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ default:
+ {
+ process_hooks(HOOK_ZAP, "(d,d)", o_ptr->tval, o_ptr->sval);
+
+ break;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Tried the object */
+ object_tried(o_ptr);
+
+ /* Successfully determined the object function */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Hack -- deal with cancelled zap */
+ if (!use_charge)
+ {
+ o_ptr->timeout += cost;
+
+ return;
+ }
+}
+
+
+
+
+/*
+ * Hook to determine if an object is activable
+ */
+static bool item_tester_hook_activate(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Not known */
+ if (!object_known_p(o_ptr)) return (FALSE);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Check activation flag */
+ if (f3 & (TR3_ACTIVATE)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+
+/*
+ * Hack -- activate the ring of power
+ */
+int ring_of_power()
+{
+ char ch = 0, p = 0;
+
+ int plev = p_ptr->lev;
+
+ int timeout = 0;
+
+
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("[S]ummon a wraith, [R]ule the world or "
+ "[C]ast a powerful attack? ", &ch))
+ {
+ return (0);
+ }
+
+ if (ch == 'S' || ch == 's')
+ {
+ p = 1;
+ break;
+ }
+ if (ch == 'R' || ch == 'r')
+ {
+ p = 2;
+ break;
+ }
+ if (ch == 'C' || ch == 'c')
+ {
+ p = 3;
+ break;
+ }
+ }
+
+ /* Summon a Wraith */
+ if (p == 1)
+ {
+ /* Rewrite this -- pelpel */
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD),
+ (bool)(((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
+ {
+ msg_print("Cold winds begin to blow around you, "
+ "carrying with them the stench of decay...");
+ msg_print("Ancient, long-dead forms arise from the ground "
+ "to serve you!");
+ }
+ timeout = 200 + rand_int(200);
+ }
+
+ /* Rule the World -- only if we can really do so */
+ else if (p == 2)
+ {
+ msg_print("The power of the ring destroys the world!");
+ msg_print("The world changes!");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ timeout = 250 + rand_int(250);
+ }
+
+ /* Cast a powerful spell */
+ else if (p == 3)
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return (0);
+
+ if (rand_int(3) == 0)
+ {
+ msg_print("You call the fire of Mount Doom!");
+ fire_ball(GF_METEOR, dir, 600, 4);
+ }
+ else
+ {
+ msg_print("Your ring tries to take possession of your enemy's mind!");
+ fire_bolt(GF_CHARM, dir, 600);
+ }
+ timeout = 300 + rand_int(300);
+ }
+
+ return (timeout);
+}
+
+
+
+
+/*
+ * Enchant some bolts
+ */
+bool brand_bolts(void)
+{
+ int i;
+
+
+ /* Use the first acceptable bolts */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-bolts */
+ if (o_ptr->tval != TV_BOLT) continue;
+
+ /* Skip artifacts and ego-items */
+ if (o_ptr->art_name || artifact_p(o_ptr) || ego_item_p(o_ptr)) continue;
+
+ /* Skip cursed/broken items */
+ if (cursed_p(o_ptr)) continue;
+
+ /* Randomize */
+ if (rand_int(100) < 75) continue;
+
+ /* Message */
+ msg_print("Your bolts are covered in a fiery aura!");
+
+ /* Ego-item */
+ o_ptr->name2 = EGO_FLAME;
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Enchant */
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Fail */
+ msg_print("The fiery enchantment failed.");
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+/*
+ * Objects in the p_ptr->inventory can now be activated, and
+ * SOME of those may be able to stack (ego wands or something)
+ * in any case, we can't know that it's impossible. *BUT* we'll
+ * ignore it for now, and the timeout will be set on the entire stack
+ * of objects. Reduces their utility, but oh well.
+ *
+ * Note that it always takes a turn to activate an object, even if
+ * the user hits "escape" at the "direction" prompt.
+ */
+void do_cmd_activate(void)
+{
+ int item, lev, chance;
+
+ char ch, spell_choice;
+
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr q, s;
+
+
+ /* Prepare the hook */
+ item_tester_hook = item_tester_hook_activate;
+
+ /* Get an item */
+ command_see = TRUE;
+ command_wrk = USE_EQUIP;
+ q = "Activate which item? ";
+ s = "You have nothing to activate.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Wearable items have to be worn */
+ if (!(f5 & TR5_ACTIVATE_NO_WIELD))
+ {
+ if (item < INVEN_WIELD)
+ {
+ msg_print("You must wear it to activate it.");
+ return;
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Extract the item level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Hack -- Use artifact level instead */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ lev = random_artifacts[o_ptr->sval].level;
+ }
+ else
+ {
+ lev = a_info[o_ptr->name1].level;
+ }
+ }
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* Hight level objects are harder */
+ chance = chance - ((lev > 50) ? 50 : lev);
+
+ if (chance <= 0)
+ {
+ chance = 1;
+ }
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance *= 10;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to activate it properly.");
+ sound(SOUND_FAIL);
+ return;
+ }
+
+ /* Check the recharge */
+ if (o_ptr->timeout)
+ {
+ /* Mage Staff of Spells -- Have another timeout in xtra2 */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && o_ptr->xtra2)
+ {
+ msg_print("It whines, glows and fades...");
+ return;
+ }
+
+ /* Monster eggs */
+ else if (o_ptr->tval == TV_EGG)
+ {
+ msg_print("You resume the development of the egg.");
+ o_ptr->timeout = 0;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Normal activatable items */
+ else
+ {
+ msg_print("It whines, glows and fades...");
+ return;
+ }
+ }
+
+
+ /* Activate the item */
+ msg_print("You activate it...");
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Lua hook ? -- go first to allow lua to override */
+ if (process_hooks(HOOK_ACTIVATE, "(d)", item))
+ {
+ return;
+ }
+
+ /* New mostly unified activation code
+ This has to be early to allow artifacts to override normal items -- neil */
+
+ if ( activation_aux(o_ptr, TRUE, item) == NULL )
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Mage Staff of Spells */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ while (TRUE)
+ {
+ if (!get_com("Use Spell [1] or [2]?", &ch))
+ {
+ return;
+ }
+
+ if (ch == '1')
+ {
+ spell_choice = 1;
+ break;
+ }
+
+ if (ch == '2')
+ {
+ spell_choice = 2;
+ break;
+ }
+ }
+
+ if (spell_choice == 1)
+ {
+ /* Still need to check timeouts because there is another counter */
+ if (o_ptr->timeout)
+ {
+ msg_print("The first spell is still charging!");
+ return;
+ }
+
+ /* Cast spell 1 */
+ activate_spell(o_ptr, spell_choice);
+ }
+ else if (spell_choice == 2)
+ {
+ /* Still need to check timeouts because there is another counter */
+ if (o_ptr->xtra2)
+ {
+ msg_print("The second spell is still charging!");
+ return;
+ }
+
+ /* Cast spell 2 */
+ activate_spell(o_ptr, spell_choice);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Monster eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ msg_print("You stop the development of the egg.");
+ o_ptr->timeout = -1;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Musical instruments */
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ /* Horns */
+ if (o_ptr->sval == SV_HORN)
+ {
+ msg_format("Your instrument emits a loud sound!");
+
+ aggravate_monsters(1);
+
+ o_ptr->timeout = 100;
+ }
+
+ /* Success */
+ return;
+ }
+
+ /* Mistake */
+ msg_print("Oops. That object cannot be activated.");
+}
+
+
+
+const char *activation_aux(object_type * o_ptr, bool doit, int item)
+{
+ int plev = get_skill(SKILL_DEVICE);
+
+ int i = 0, ii = 0, ij = 0, k, dir, dummy = 0;
+ int chance;
+ bool is_junkart = (o_ptr->tval == TV_RANDART);
+
+ int spell = 0;
+
+ /* Junkarts */
+ if (is_junkart)
+ spell = activation_info[o_ptr->pval2].spell;
+
+ /* True Actifacts */
+ if (!spell && o_ptr->name1)
+ spell = a_info[o_ptr->name1].activate;
+
+ /* Random and Alchemist Artifacts */
+ if (!spell && o_ptr->art_name)
+ spell = o_ptr->xtra2;
+
+ /* Ego Items */
+ if (!spell && o_ptr->name2)
+ spell = e_info[o_ptr->name2].activate;
+
+ /* Dual egos with the second ego having the activation */
+ if (!spell && o_ptr->name2b)
+ spell = e_info[o_ptr->name2b].activate;
+
+ /* Intrinsic to item type (rings of Ice, etc) */
+ if (!spell)
+ spell = k_info[o_ptr->k_idx].activate;
+
+ /* Complain about mis-configured .txt files? */
+ if (!spell)
+ return "Unknown!";
+
+ /* Negative means a unified spell index */
+ if (spell < 0)
+ {
+ if (doit)
+ {
+ call_lua("activate_activation", "(d,d)", "", -spell, item);
+ o_ptr->timeout = exec_lua(format("return get_activation_timeout(%d)", -spell));
+ }
+ else
+ {
+ return string_exec_lua(format("return get_activation_desc(%d)", -spell));
+ }
+ }
+ else
+ {
+ /* Activate for attack */
+ switch (spell)
+ {
+ case ACT_GILGALAD:
+ {
+ if (!doit) return "starlight (75) every 75+d75 turns";
+ for (k = 1; k < 10; k++)
+ {
+ if (k - 5) fire_beam(GF_LITE, k, 75);
+ }
+
+ o_ptr->timeout = rand_int(75) + 75;
+
+ break;
+ }
+
+ case ACT_CELEBRIMBOR:
+ {
+ if (!doit) return "temporary ESP (dur 20+d20) every 20+d50 turns";
+ set_tim_esp(p_ptr->tim_esp + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 20;
+
+ break;
+ }
+
+ case ACT_SKULLCLEAVER:
+ {
+ if (!doit) return "destruction every 200+d200 turns";
+ destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE);
+
+ o_ptr->timeout = rand_int(200) + 200;
+
+ break;
+ }
+
+ case ACT_HARADRIM:
+ {
+ if (!doit) return "berserk strength every 50+d50 turns";
+ set_afraid(0);
+ set_shero(p_ptr->shero + randint(25) + 25);
+ hp_player(30);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_FUNDIN:
+ {
+ if (!doit) return "dispel evil (x4) every 100+d100 turns";
+ dispel_evil(p_ptr->lev * 4);
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_EOL:
+ {
+ if (!doit) return "mana bolt (9d8) 7+d7 turns";
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MANA, dir, damroll(9, 8));
+
+ o_ptr->timeout = rand_int(7) + 7;
+
+ break;
+ }
+
+ case ACT_UMBAR:
+ {
+ if (!doit) return "magic arrow (10d10) every 20+d20 turns";
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MISSILE, dir, damroll(10, 10));
+
+ o_ptr->timeout = rand_int(20) + 20;
+
+ break;
+ }
+
+ case ACT_NUMENOR:
+ {
+ /* Give full knowledge */
+ /* Hack -- Maximal info */
+ monster_race *r_ptr;
+ cave_type *c_ptr;
+ int x, y, m;
+
+ if (!doit) return "analyze monster every 500+d200 turns";
+
+ if (!tgt_pt(&x, &y)) break;
+
+ c_ptr = &cave[y][x];
+ if (!c_ptr->m_idx) break;
+
+ r_ptr = &r_info[c_ptr->m_idx];
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags7 = r_ptr->flags7;
+ r_ptr->r_flags8 = r_ptr->flags8;
+ r_ptr->r_flags9 = r_ptr->flags9;
+
+ o_ptr->timeout = rand_int(200) + 500;
+
+ break;
+ }
+
+ case ACT_KNOWLEDGE:
+ {
+ if (!doit) return "whispers from beyond(sanity drain) 100+d200 turns";
+ identify_fully();
+ take_sanity_hit(damroll(10, 7), "the sounds of the dead");
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_UNDEATH:
+ {
+ if (!doit) return "ruination every 10+d10 turns";
+ msg_print("The phial wells with dark light...");
+ unlite_area(damroll(2, 15), 3);
+ take_hit(damroll(10, 10), "activating The Phial of Undeath");
+ (void)dec_stat(A_DEX, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_WIS, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_CON, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_STR, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_CHR, 25, STAT_DEC_PERMANENT);
+ (void)dec_stat(A_INT, 25, STAT_DEC_PERMANENT);
+
+ o_ptr->timeout = rand_int(10) + 10;
+
+ break;
+ }
+
+ case ACT_THRAIN:
+ {
+ if (!doit) return "detection every 30+d30 turns";
+ msg_print("The stone glows a deep green...");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(30) + 30;
+
+ break;
+ }
+
+ case ACT_BARAHIR:
+ {
+ if (!doit) return "dispel small life every 55+d55 turns";
+ msg_print("You exterminate small life.");
+ (void)dispel_monsters(4);
+
+ o_ptr->timeout = rand_int(55) + 55;
+
+ break;
+ }
+
+ case ACT_TULKAS:
+ {
+ if (!doit) return "haste self (75+d75 turns) every 150+d150 turns";
+ msg_print("The ring glows brightly...");
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(75) + 75, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ o_ptr->timeout = rand_int(150) + 150;
+
+ break;
+ }
+
+ case ACT_NARYA:
+ {
+ if (!doit) return "healing (500) every 200+d100 turns";
+ msg_print("The ring glows deep red...");
+ hp_player(500);
+ set_blind(0);
+ set_confused(0);
+ set_poisoned(0);
+ set_stun(0);
+ set_cut(0);
+
+ o_ptr->timeout = rand_int(100) + 200;
+
+ break;
+ }
+
+ case ACT_NENYA:
+ {
+ if (!doit) return "healing (800) every 100+d200 turns";
+ msg_print("The ring glows bright white...");
+ hp_player(800);
+ set_blind(0);
+ set_confused(0);
+ set_poisoned(0);
+ set_stun(0);
+ set_cut(0);
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_VILYA:
+ {
+ if (!doit) return "greater healing (900) every 200+d200 turns";
+ msg_print("The ring glows deep blue...");
+ hp_player(900);
+ set_blind(0);
+ set_confused(0);
+ set_poisoned(0);
+ set_stun(0);
+ set_cut(0);
+ if (p_ptr->black_breath)
+ {
+ p_ptr->black_breath = FALSE;
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+
+ o_ptr->timeout = rand_int(200) + 200;
+
+ break;
+ }
+
+ case ACT_POWER:
+ {
+ if (!doit) return "powerful things";
+ msg_print("The ring glows intensely black...");
+
+ o_ptr->timeout = ring_of_power();
+
+ break;
+ }
+
+
+ /* The Stone of Lore is perilous, for the sake of game balance. */
+ case ACT_STONE_LORE:
+ {
+ if (!doit) return "perilous identify every turn";
+ msg_print("The stone reveals hidden mysteries...");
+ if (!ident_spell()) break;
+
+ if (has_ability(AB_PERFECT_CASTING))
+ {
+ /* Sufficient mana */
+ if (20 <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= 20;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = 20 - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You are too weak to control the stone!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed +
+ randint(5 * oops + 1));
+
+ /* Confusing. */
+ (void)set_confused(p_ptr->confused +
+ randint(5 * oops + 1));
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+ }
+
+ take_hit(damroll(1, 12), "perilous secrets");
+
+ /* Confusing. */
+ if (rand_int(5) == 0)
+ {
+ (void)set_confused(p_ptr->confused + randint(10));
+ }
+
+ /* Exercise a little care... */
+ if (rand_int(20) == 0)
+ {
+ take_hit(damroll(4, 10), "perilous secrets");
+ }
+
+ o_ptr->timeout = 1;
+
+ break;
+ }
+
+ case ACT_RAZORBACK:
+ {
+ if (!doit) return "star ball (150) every 1000 turns";
+ msg_print("Your armor is surrounded by lightning...");
+ for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], 150, 3);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_BLADETURNER:
+ {
+ if (!doit) return "invulnerability (4+d8) every 800 turns";
+ set_invuln(p_ptr->invuln + randint(8) + 4);
+
+ o_ptr->timeout = 800;
+
+ break;
+ }
+
+ case ACT_MEDIATOR:
+ {
+ if (!doit) return "breathe elements (300), berserk rage, bless, and resistance every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 4);
+ msg_print("Your armor glows many colours...");
+ (void)set_afraid(0);
+ (void)set_shero(p_ptr->shero + randint(50) + 50);
+ (void)hp_player(30);
+ (void)set_blessed(p_ptr->blessed + randint(50) + 50);
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(50) + 50);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(50) + 50);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(50) + 50);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(50) + 50);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(50) + 50);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BELEGENNON:
+ {
+ if (!doit) return ("heal (777), curing and heroism every 300 turns");
+ msg_print("A heavenly choir sings...");
+ (void)set_poisoned(0);
+ (void)set_cut(0);
+ (void)set_stun(0);
+ (void)set_confused(0);
+ (void)set_blind(0);
+ (void)set_hero(p_ptr->hero + randint(25) + 25);
+ (void)hp_player(777);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_GORLIM:
+ {
+ if (!doit) return "rays of fear in every direction";
+ turn_monsters(40 + p_ptr->lev);
+
+ o_ptr->timeout = 3 * (p_ptr->lev + 10);
+
+ break;
+ }
+
+ case ACT_COLLUIN:
+ {
+ if (!doit) return "resistance (20+d20 turns) every 111 turns";
+ msg_print("Your cloak glows many colours...");
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
+
+ o_ptr->timeout = 111;
+
+ break;
+ }
+
+
+ case ACT_BELANGIL:
+ {
+ if (!doit) return "frost ball (48) every 5+d5 turns";
+ msg_print("Your dagger is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ o_ptr->timeout = rand_int(5) + 5;
+
+ break;
+ }
+
+ case ACT_ANGUIREL:
+ {
+ if (!doit) return "a getaway every 35 turns";
+ switch (randint(13))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ teleport_player(10);
+
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ {
+ teleport_player(222);
+
+ break;
+ }
+
+ case 11:
+ case 12:
+ {
+ (void)stair_creation();
+
+ break;
+ }
+
+ default:
+ {
+ if (get_check("Leave this level? "))
+ {
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ o_ptr->timeout = 35;
+
+ break;
+ }
+
+ case ACT_ERU:
+ {
+ if (!doit) return "healing(7000), curing every 500 turns";
+ msg_print("Your sword glows an intense white...");
+ hp_player(7000);
+ heal_insanity(50);
+ set_blind(0);
+ set_poisoned(0);
+ set_confused(0);
+ set_stun(0);
+ set_cut(0);
+ set_image(0);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DAWN:
+ {
+ if (!doit) return "summon the Legion of the Dawn every 500+d500 turns";
+ msg_print("You summon the Legion of the Dawn.");
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DAWN, TRUE);
+
+ o_ptr->timeout = 500 + randint(500);
+
+ break;
+ }
+
+ case ACT_FIRESTAR:
+ {
+ if (!doit) return "large fire ball (72) every 100 turns";
+ msg_print("Your morning star rages in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 72, 3);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_TURMIL:
+ {
+ if (!doit) return "drain life (90) every 70 turns";
+ msg_print("Your hammer glows white...");
+ if (!get_aim_dir(&dir)) break;
+ drain_life(dir, 90);
+
+ o_ptr->timeout = 70;
+
+ break;
+ }
+
+ case ACT_CUBRAGOL:
+ {
+ if (!doit) return "fire branding of bolts every 999 turns";
+ msg_print("Your crossbow glows deep red...");
+ (void)brand_bolts();
+
+ o_ptr->timeout = 999;
+
+ break;
+ }
+
+ case ACT_ELESSAR:
+ {
+ if (!doit) return "heal and cure black breath every 200 turns";
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+ p_ptr->black_breath = FALSE;
+ hp_player(100);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_GANDALF:
+ {
+ if (!doit) return "restore mana every 666 turns";
+ msg_print("Your mage staff glows deep blue...");
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ msg_print("Your feel your head clear.");
+ p_ptr->redraw |= (PR_MANA);
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ o_ptr->timeout = 666;
+
+ break;
+ }
+
+ case ACT_MARDA:
+ {
+ if (!doit) return "summon a thunderlord every 1000 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_THUNDERLORD))
+ {
+ msg_print("A Thunderlord comes from thin air!");
+ msg_print("'I will burn you!'");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_THUNDERLORD, (bool)(plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("A Thunderlord comes from thin air!");
+ msg_print("'I will help you in your difficult task.'");
+ }
+ }
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_PALANTIR:
+ {
+ if (!doit) return "clairvoyance every 100+d100 turns";
+ msg_print("The stone glows a deep green...");
+ wiz_lite_extra();
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_EREBOR:
+ {
+ if (!doit) return "open a secret passage every 75 turns";
+ msg_print("Your pick twists in your hands.");
+
+ if (!get_aim_dir(&dir)) break;
+ if (passwall(dir, TRUE))
+ {
+ msg_print("A passage opens, and you step through.");
+ }
+ else
+ {
+ msg_print("There is no wall there!");
+ }
+
+ o_ptr->timeout = 75;
+
+ break;
+ }
+
+ case ACT_DRUEDAIN:
+ {
+ if (!doit) return "detection every 99 turns";
+ msg_print("Your drum shows you the world.");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = 99;
+
+ break;
+ }
+
+ case ACT_ROHAN:
+ {
+ if (!doit) return "heroism, berserker, and haste every 250 turns";
+ msg_print("Your horn glows deep red.");
+ set_afraid(0);
+ set_shero(p_ptr->shero + damroll(5, 10) + 30);
+ set_afraid(0);
+ set_hero(p_ptr->hero + damroll(5, 10) + 30);
+ set_fast(p_ptr->fast + damroll(5, 10) + 30, 10);
+ hp_player(30);
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_HELM:
+ {
+ if (!doit) return "sound ball (300) every 300 turns";
+ msg_print("Your horn emits a loud sound.");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_SOUND, dir, 300, 6);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_BOROMIR:
+ {
+ if (!doit) return "mass human summoning every 1000 turns";
+ msg_print("Your horn calls for help.");
+ for (i = 0; i < 15; i++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_HUMAN, TRUE);
+ }
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_HURIN:
+ {
+ if (!doit) return "berserker and +10 to speed (50) every 100+d200 turns";
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(50) + 50, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+ hp_player(30);
+ set_afraid(0);
+ set_shero(p_ptr->shero + randint(50) + 50);
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_AXE_GOTHMOG:
+ {
+ if (!doit) return "fire ball (300) every 200+d200 turns";
+ msg_print("Your lochaber axe erupts in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 300, 4);
+
+ o_ptr->timeout = 200 + rand_int(200);
+
+ break;
+ }
+
+ case ACT_MELKOR:
+ {
+ if (!doit) return "darkness ball (150) every 100 turns";
+ msg_print("Your spear is covered of darkness...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_DARK, dir, 150, 3);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_GROND:
+ {
+ if (!doit) return "alter reality every 100 turns";
+ msg_print("Your hammer hits the floor...");
+ alter_reality();
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_NATUREBANE:
+ {
+ if (!doit) return "dispel monsters (300) every 200+d200 turns";
+ msg_print("Your axe glows blood red...");
+ dispel_monsters(300);
+
+ o_ptr->timeout = 200 + randint(200);
+
+ break;
+ }
+
+ case ACT_NIGHT:
+ {
+ if (!doit) return "vampiric drain (3*100) every 250 turns";
+ msg_print("Your axe emits a black aura...");
+ if (!get_aim_dir(&dir)) break;
+ for (i = 0; i < 3; i++)
+ {
+ if (drain_life(dir, 100)) hp_player(100);
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_ORCHAST:
+ {
+ if (!doit) return "detect orcs every 10 turns";
+ msg_print("Your weapon glows brightly...");
+ (void)detect_monsters_xxx(RF3_ORC, DEFAULT_RADIUS);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+ case ACT_SUNLIGHT:
+ {
+ if (!doit) return "beam of sunlight every 10 turns";
+
+ if (!get_aim_dir(&dir)) break;
+ msg_print("A line of sunlight appears.");
+ lite_line(dir);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_BO_MISS_1:
+ {
+ if (!doit) return "magic missile (2d6) every 2 turns";
+ msg_print("It glows extremely brightly...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MISSILE, dir, damroll(2, 6));
+
+ o_ptr->timeout = 2;
+
+ break;
+ }
+
+ case ACT_BA_POIS_1:
+ {
+ if (!doit) return "stinking cloud (12), rad. 3, every 4+d4 turns";
+ msg_print("It throbs deep green...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_POIS, dir, 12, 3);
+
+ o_ptr->timeout = rand_int(4) + 4;
+
+ break;
+ }
+
+ case ACT_BO_ELEC_1:
+ {
+ if (!doit) return "lightning bolt (4d8) every 6+d6 turns";
+ msg_print("It is covered in sparks...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ELEC, dir, damroll(4, 8));
+
+ o_ptr->timeout = rand_int(6) + 6;
+
+ break;
+ }
+
+ case ACT_BO_ACID_1:
+ {
+ if (!doit) return "acid bolt (5d8) every 5+d5 turns";
+ msg_print("It is covered in acid...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ACID, dir, damroll(5, 8));
+
+ o_ptr->timeout = rand_int(5) + 5;
+
+ break;
+ }
+
+ case ACT_BO_COLD_1:
+ {
+ if (!doit) return "frost bolt (6d8) every 7+d7 turns";
+ msg_print("It is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_COLD, dir, damroll(6, 8));
+
+ o_ptr->timeout = rand_int(7) + 7;
+
+ break;
+ }
+
+ case ACT_BO_FIRE_1:
+ {
+ if (!doit) return "fire bolt (9d8) every 8+d8 turns";
+ msg_print("It is covered in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_FIRE, dir, damroll(9, 8));
+
+ o_ptr->timeout = rand_int(8) + 8;
+
+ break;
+ }
+
+ case ACT_BA_COLD_1:
+ {
+ if (!doit) return "ball of cold (48) every 400 turns";
+ msg_print("It is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_1:
+ {
+ if (!doit) return "ball of fire (72) every 400 turns";
+ msg_print("It glows an intense red...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 72, 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_DRAIN_1:
+ {
+ if (!doit) return "drain life (100) every 100+d100 turns";
+ msg_print("It glows black...");
+ if (!get_aim_dir(&dir)) break;
+ if (drain_life(dir, 100))
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_BA_COLD_2:
+ {
+ if (!doit) return "ball of cold (100) every 300 turns";
+ msg_print("It glows an intense blue...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 100, 2);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_2:
+ {
+ if (!doit) return "ball of lightning (100) every 500 turns";
+ msg_print("It crackles with electricity...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_ELEC, dir, 100, 3);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DRAIN_2:
+ {
+ if (!doit) return "drain life (120) every 400 turns";
+ msg_print("It glows black...");
+ if (!get_aim_dir(&dir)) break;
+ drain_life(dir, 120);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_VAMPIRE_1:
+ {
+ if (!doit) return "vampiric drain (3*50) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ for (dummy = 0; dummy < 3; dummy++)
+ {
+ if (drain_life(dir, 50))
+ hp_player(50);
+ }
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BO_MISS_2:
+ {
+ if (!doit) return "arrows (150) every 90+d90 turns";
+ msg_print("It grows magical spikes...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ARROW, dir, 150);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_2:
+ {
+ if (!doit) return "fire ball (120) every 225+d225 turns";
+ msg_print("It glows deep red...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 120, 3);
+
+ o_ptr->timeout = rand_int(225) + 225;
+
+ break;
+ }
+
+ case ACT_BA_COLD_3:
+ {
+ if (!doit) return "ball of cold (200) every 325+d325 turns";
+ msg_print("It glows bright white...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 200, 3);
+
+ o_ptr->timeout = rand_int(325) + 325;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_3:
+ {
+ if (!doit) return "Lightning Ball (250) every 425+d425 turns";
+ msg_print("It glows deep blue...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_ELEC, dir, 250, 3);
+
+ o_ptr->timeout = rand_int(425) + 425;
+
+ break;
+ }
+
+ case ACT_WHIRLWIND:
+ {
+ int y = 0, x = 0;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ if (!doit) return "whirlwind attack every 250 turns";
+
+ for (dir = 0; dir <= 9; dir++)
+ {
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Get the monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Hack -- attack monsters */
+ if (c_ptr->m_idx && (m_ptr->ml || cave_floor_bold(y, x)))
+ {
+ py_attack(y, x, -1);
+ }
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_VAMPIRE_2:
+ {
+ if (!doit) return "vampiric drain (3*100) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ for (dummy = 0; dummy < 3; dummy++)
+ {
+ if (drain_life(dir, 100))
+ hp_player(100);
+ }
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+
+ case ACT_CALL_CHAOS:
+ {
+ if (!doit) return "call chaos every 350 turns";
+ msg_print("It glows in scintillating colours...");
+ call_chaos();
+
+ o_ptr->timeout = 350;
+
+ break;
+ }
+
+ case ACT_ROCKET:
+ {
+ if (!doit) return "launch rocket (120+level) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You launch a rocket!");
+ fire_ball(GF_ROCKET, dir, 120 + (plev), 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_DISP_EVIL:
+ {
+ if (!doit) return "dispel evil (level*5) every 300+d300 turns";
+ msg_print("It floods the area with goodness...");
+ dispel_evil(p_ptr->lev * 5);
+
+ o_ptr->timeout = rand_int(300) + 300;
+
+ break;
+ }
+
+ case ACT_DISP_GOOD:
+ {
+ if (!doit) return "dispel good (level*5) every 300+d300 turns";
+ msg_print("It floods the area with evil...");
+ dispel_good(p_ptr->lev * 5);
+
+ o_ptr->timeout = rand_int(300) + 300;
+
+ break;
+ }
+
+ case ACT_BA_MISS_3:
+ {
+ if (!doit) return "elemental breath (300) every 500 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 4);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ /* Activate for other offensive action */
+
+ case ACT_CONFUSE:
+ {
+ if (!doit) return "confuse monster every 15 turns";
+ msg_print("It glows in scintillating colours...");
+ if (!get_aim_dir(&dir)) break;
+ confuse_monster(dir, 20);
+
+ o_ptr->timeout = 15;
+
+ break;
+ }
+
+ case ACT_SLEEP:
+ {
+ if (!doit) return "sleep nearby monsters every 55 turns";
+ msg_print("It glows deep blue...");
+ sleep_monsters_touch();
+
+ o_ptr->timeout = 55;
+
+ break;
+ }
+
+ case ACT_QUAKE:
+ {
+ if (!doit) return "earthquake (rad 10) every 50 turns";
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ o_ptr->timeout = 50;
+ }
+
+ break;
+ }
+
+ case ACT_TERROR:
+ {
+ if (!doit) return "terror every 3 * (level+10) turns";
+#if 0
+ for (i = 0; i < 8; i++) fear_monster(ddd[i], (p_ptr->lev) + 10);
+#else
+turn_monsters(40 + p_ptr->lev);
+#endif
+
+ o_ptr->timeout = 3 * (p_ptr->lev + 10);
+
+ break;
+ }
+
+ case ACT_TELE_AWAY:
+ {
+ if (!doit) return "teleport away every 200 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void)fire_beam(GF_AWAY_ALL, dir, plev);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_BANISH_EVIL:
+ {
+ if (!doit) return "banish evil every 250+d250 turns";
+ if (banish_evil(100))
+ {
+ msg_print("The power of the artifact banishes evil!");
+ }
+
+ o_ptr->timeout = 250 + randint(250);
+
+ break;
+ }
+
+ case ACT_GENOCIDE:
+ {
+ if (!doit) return "genocide every 500 turns";
+ msg_print("It glows deep blue...");
+ (void)genocide(TRUE);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_MASS_GENO:
+ {
+ if (!doit) return "mass genocide every 1000 turns";
+ msg_print("It lets out a long, shrill note...");
+ (void)mass_genocide(TRUE);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ /* Activate for summoning / charming */
+
+ case ACT_CHARM_ANIMAL:
+ {
+ if (!doit) return "charm animal every 300 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void) charm_animal(dir, plev);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_CHARM_UNDEAD:
+ {
+ if (!doit) return "enslave undead every 333 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void)control_one_undead(dir, plev);
+
+ o_ptr->timeout = 333;
+
+ break;
+ }
+
+ case ACT_CHARM_OTHER:
+ {
+ if (!doit) return "charm monster every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void) charm_monster(dir, plev);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_CHARM_ANIMALS:
+ {
+ if (!doit) return "animal friendship every 500 turns";
+ (void) charm_animals(plev * 2);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_CHARM_OTHERS:
+ {
+ if (!doit) return "mass charm every 750 turns";
+ charm_monsters(plev * 2);
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_SUMMON_ANIMAL:
+ {
+ if (!doit) return "summon animal every 200+d300 turns";
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, plev, SUMMON_ANIMAL_RANGER, TRUE);
+
+ o_ptr->timeout = 200 + randint(300);
+
+ break;
+ }
+
+ case ACT_SUMMON_PHANTOM:
+ {
+ if (!doit) return "summon phantasmal servant every 200+d200 turns";
+ msg_print("You summon a phantasmal servant.");
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_PHANTOM, TRUE);
+
+ o_ptr->timeout = 200 + randint(200);
+
+ break;
+ }
+
+ case ACT_SUMMON_ELEMENTAL:
+ {
+ if (!doit) return "summon elemental every 750 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_ELEMENTAL))
+ {
+ msg_print("An elemental materialises...");
+ msg_print("You fail to control it!");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_ELEMENTAL, (bool)(plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("An elemental materialises...");
+ msg_print("It seems obedient to you.");
+ }
+ }
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_SUMMON_DEMON:
+ {
+ if (!doit) return "summon demon every 666+d333 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_DEMON))
+ {
+ msg_print("The area fills with a stench of sulphur and brimstone.");
+ msg_print("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_DEMON, (bool)(plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("The area fills with a stench of sulphur and brimstone.");
+ msg_print("'What is thy bidding... Master?'");
+ }
+ }
+
+ o_ptr->timeout = 666 + randint(333);
+
+ break;
+ }
+
+ case ACT_SUMMON_UNDEAD:
+ {
+ if (!doit) return "summon undead every 666+d333 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ (plev > 47 ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD)))
+ {
+ msg_print("Cold winds begin to blow around you, carrying with them the stench of decay...");
+ msg_print("'The dead arise... to punish you for disturbing them!'");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD),
+ (bool)(((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
+ {
+ msg_print("Cold winds begin to blow around you, carrying with them the stench of decay...");
+ msg_print("Ancient, long-dead forms arise from the ground to serve you!");
+ }
+ }
+
+ o_ptr->timeout = 666 + randint(333);
+
+ break;
+ }
+
+ /* Activate for healing */
+
+ case ACT_CURE_LW:
+ {
+ if (!doit) return format("cure light wounds every %d turns", (is_junkart ? 50 : 10));
+ (void)set_afraid(0);
+ (void)hp_player(30);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_CURE_MW:
+ {
+ if (!doit) return format("cure serious wounds every %s turns", (is_junkart? "75" : "3+d3"));
+ msg_print("It radiates deep purple...");
+ hp_player(damroll(4, 8));
+ (void)set_cut((p_ptr->cut / 2) - 50);
+
+ o_ptr->timeout = rand_int(3) + 3;
+
+ break;
+ }
+
+ case ACT_CURE_POISON:
+ {
+ if (!doit) return "remove fear and cure poison every 5 turns";
+ msg_print("It glows deep blue...");
+ (void)set_afraid(0);
+ (void)set_poisoned(0);
+
+ o_ptr->timeout = 5;
+
+ break;
+ }
+
+ case ACT_REST_LIFE:
+ {
+ if (!doit) return "restore life levels every 450 turns";
+ msg_print("It glows a deep red...");
+ restore_level();
+
+ o_ptr->timeout = 450;
+
+ break;
+ }
+
+ case ACT_REST_ALL:
+ {
+ if (!doit) return format("restore stats and life levels every %d turns", (is_junkart ? 200 : 750));
+ msg_print("It glows a deep green...");
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ (void)restore_level();
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_CURE_700:
+ {
+ if (!doit) return format("heal 700 hit points every %d turns", (is_junkart ? 100 : 250));
+ msg_print("It glows deep blue...");
+ msg_print("You feel a warm tingling inside...");
+ (void)hp_player(700);
+ (void)set_cut(0);
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_CURE_1000:
+ {
+ if (!doit) return "heal 1000 hit points every 888 turns";
+ msg_print("It glows a bright white...");
+ msg_print("You feel much better...");
+ (void)hp_player(1000);
+ (void)set_cut(0);
+
+ o_ptr->timeout = 888;
+
+ break;
+ }
+
+ case ACT_ESP:
+ {
+ if (!doit) return "temporary ESP (dur 25+d30) every 200 turns";
+ (void)set_tim_esp(p_ptr->tim_esp + randint(30) + 25);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_BERSERK:
+ {
+ if (!doit) return "heroism and berserk (dur 50+d50) every 100+d100 turns";
+ (void)set_shero(p_ptr->shero + randint(50) + 50);
+ (void)set_blessed(p_ptr->blessed + randint(50) + 50);
+
+ o_ptr->timeout = 100 + randint(100);
+
+ break;
+ }
+
+ case ACT_PROT_EVIL:
+ {
+ if (!doit) return "protection from evil (dur level*3 + d25) every 225+d225 turns";
+ msg_print("It lets out a shrill wail...");
+ k = 3 * p_ptr->lev;
+ (void)set_protevil(p_ptr->protevil + randint(25) + k);
+
+ o_ptr->timeout = rand_int(225) + 225;
+
+ break;
+ }
+
+ case ACT_RESIST_ALL:
+ {
+ if (!doit) return "resist elements (dur 40+d40) every 200 turns";
+ msg_print("It glows many colours...");
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(40) + 40);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(40) + 40);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(40) + 40);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(40) + 40);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(40) + 40);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_SPEED:
+ {
+ if (!doit) return "speed (dur 20+d20) every 250 turns";
+ msg_print("It glows bright green...");
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(20) + 20, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_XTRA_SPEED:
+ {
+ if (!doit) return "speed (dur 75+d75) every 200+d200 turns";
+ msg_print("It glows brightly...");
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(75) + 75, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ o_ptr->timeout = rand_int(200) + 200;
+
+ break;
+ }
+
+ case ACT_WRAITH:
+ {
+ if (!doit) return "wraith form (level/2 + d(level/2)) every 1000 turns";
+ set_shadow(p_ptr->tim_wraith + randint(plev / 2) + (plev / 2));
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_INVULN:
+ {
+ if (!doit) return "invulnerability (dur 8+d8) every 1000 turns";
+ (void)set_invuln(p_ptr->invuln + randint(8) + 8);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ /* Activate for general purpose effect (detection etc.) */
+
+ case ACT_LIGHT:
+ {
+ if (!doit) return format("light area (dam 2d15) every %s turns", (is_junkart ? "100" : "10+d10"));
+ msg_print("It wells with clear light...");
+ lite_area(damroll(2, 15), 3);
+
+ o_ptr->timeout = rand_int(10) + 10;
+
+ break;
+ }
+
+ case ACT_MAP_LIGHT:
+ {
+ if (!doit) return "light (dam 2d15) & map area every 50+d50 turns";
+ msg_print("It shines brightly...");
+ map_area();
+ lite_area(damroll(2, 15), 3);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_DETECT_ALL:
+ {
+ if (!doit) return "detection every 55+d55 turns";
+ msg_print("It glows bright white...");
+ msg_print("An image forms in your mind...");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(55) + 55;
+
+ break;
+ }
+
+ case ACT_DETECT_XTRA:
+ {
+ if (!doit) return "detection, probing and identify true every 1000 turns";
+ msg_print("It glows brightly...");
+ detect_all(DEFAULT_RADIUS);
+ probing();
+ identify_fully();
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_ID_FULL:
+ {
+ if (!doit) return "identify true every 750 turns";
+ msg_print("It glows yellow...");
+ identify_fully();
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_ID_PLAIN:
+ {
+ if (!doit) return "identify spell every 10 turns";
+ if (!ident_spell()) break;
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_RUNE_EXPLO:
+ {
+ if (!doit) return "explosive rune every 200 turns";
+ msg_print("It glows bright red...");
+ explosive_rune();
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_RUNE_PROT:
+ {
+ if (!doit) return "rune of protection every 400 turns";
+ msg_print("It glows light blue...");
+ warding_glyph();
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_SATIATE:
+ {
+ if (!doit) return "satisfy hunger every 200 turns";
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_DEST_DOOR:
+ {
+ if (!doit) return "destroy doors and traps every 10 turns";
+ msg_print("It glows bright red...");
+ destroy_doors_touch();
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_STONE_MUD:
+ {
+ if (!doit) return "stone to mud every 5 turns";
+ msg_print("It pulsates...");
+ if (!get_aim_dir(&dir)) break;
+ wall_to_mud(dir);
+
+ o_ptr->timeout = 5;
+
+ break;
+ }
+
+ case ACT_RECHARGE:
+ {
+ if (!doit) return "recharging every 70 turns";
+ recharge(60);
+
+ o_ptr->timeout = 70;
+
+ break;
+ }
+
+ case ACT_ALCHEMY:
+ {
+ if (!doit) return "alchemy every 500 turns";
+ msg_print("It glows bright yellow...");
+ (void) alchemy();
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DIM_DOOR:
+ {
+ if (!doit) return "dimension door every 100 turns";
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ break;
+ }
+
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+ if (!tgt_pt(&ii, &ij)) break;
+
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2) ||
+ (!rand_int(plev * plev / 2)))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10, &ij, &ii);
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_TELEPORT:
+ {
+ if (!doit) return format("teleport (range 100) every %d turns", (is_junkart? 100 : 45));
+ msg_print("It twists space around you...");
+ teleport_player(100);
+
+ o_ptr->timeout = 45;
+
+ break;
+ }
+
+ case ACT_RECALL:
+ {
+ if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")))
+ {
+ if (!doit) return "word of recall every 200 turns";
+ msg_print("It glows soft white...");
+ recall_player(20,15);
+
+ o_ptr->timeout = 200;
+ }
+
+ break;
+ }
+
+ case ACT_DEATH:
+ {
+ if (!doit) return "death";
+ take_hit(5000, "activating a death spell");
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_RUINATION:
+ {
+ if (!doit) return "Ruination";
+ msg_print("Your nerves and muscles feel weak and lifeless!");
+
+ take_hit(damroll(10, 10), "activating Ruination");
+ (void)dec_stat(A_DEX, 25, TRUE);
+ (void)dec_stat(A_WIS, 25, TRUE);
+ (void)dec_stat(A_CON, 25, TRUE);
+ (void)dec_stat(A_STR, 25, TRUE);
+ (void)dec_stat(A_CHR, 25, TRUE);
+ (void)dec_stat(A_INT, 25, TRUE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_DESTRUC:
+ {
+ if (!doit) return "Destruction every 100 turns";
+ earthquake(p_ptr->py, p_ptr->px, 12);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNINT:
+ {
+ if (!doit) return "decreasing Intelligence";
+ (void)dec_stat(A_INT, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNSTR:
+ {
+ if (!doit) return "decreasing Strength";
+ (void)dec_stat(A_STR, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNCON:
+ {
+ if (!doit) return "decreasing Constitution";
+ (void)dec_stat(A_CON, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNCHR:
+ {
+ if (!doit) return "decreasing Charisma";
+ (void)dec_stat(A_CHR, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNDEX:
+ {
+ if (!doit) return "decreasing Dexterity";
+ (void)dec_stat(A_DEX, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_UNWIS:
+ {
+ if (!doit) return "decreasing Wisdom";
+ (void)dec_stat(A_WIS, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_STATLOSS:
+ {
+ if (!doit) return "stat loss";
+ (void)dec_stat(A_STR, 15, FALSE);
+ (void)dec_stat(A_INT, 15, FALSE);
+ (void)dec_stat(A_WIS, 15, FALSE);
+ (void)dec_stat(A_DEX, 15, FALSE);
+ (void)dec_stat(A_CON, 15, FALSE);
+ (void)dec_stat(A_CHR, 15, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HISTATLOSS:
+ {
+ if (!doit) return "high stat loss";
+ (void)dec_stat(A_STR, 25, FALSE);
+ (void)dec_stat(A_INT, 25, FALSE);
+ (void)dec_stat(A_WIS, 25, FALSE);
+ (void)dec_stat(A_DEX, 25, FALSE);
+ (void)dec_stat(A_CON, 25, FALSE);
+ (void)dec_stat(A_CHR, 25, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_EXPLOSS:
+ {
+ if (!doit) return "experience loss";
+ lose_exp(p_ptr->exp / 20);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HIEXPLOSS:
+ {
+ if (!doit) return "high experience loss";
+ lose_exp(p_ptr->exp / 10);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_SUMMON_MONST:
+ {
+ if (!doit) return "summon monster";
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_PARALYZE:
+ {
+ if (!doit) return "paralyze";
+ set_paralyzed(p_ptr->paralyzed + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HALLU:
+ {
+ if (!doit) return "hallucination every 10 turns";
+ set_image(p_ptr->image + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_POISON:
+ {
+ if (!doit) return "poison";
+ set_poisoned(p_ptr->poisoned + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_HUNGER:
+ {
+ if (!doit) return "create hunger";
+ (void)set_food(PY_FOOD_WEAK);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_STUN:
+ {
+ if (!doit) return "stun";
+ set_stun(p_ptr->stun + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CUTS:
+ {
+ if (!doit) return "cuts";
+ set_cut(p_ptr->cut + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_PARANO:
+ {
+ if (!doit) return "confusion";
+ set_confused(p_ptr->confused + 30 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CONFUSION:
+ {
+ if (!doit) return "confusion";
+ set_confused(p_ptr->confused + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_BLIND:
+ {
+ if (!doit) return "blindness";
+ set_blind(p_ptr->blind + 20 + randint(10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_PET_SUMMON:
+ {
+ if (!doit) return "summon pet every 101 turns";
+ summon_specific_friendly(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0, FALSE);
+
+ /* Timeout is set before return */
+ /*FINDME*/
+
+ break;
+ }
+
+ case ACT_CURE_PARA:
+ {
+ if (!doit) return "cure confusion every 500 turns";
+ set_confused(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_HALLU:
+ {
+ if (!doit) return "cure hallucination every 100 turns";
+ set_image(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_POIS:
+ {
+ if (!doit) return "cure poison every 100 turns";
+ set_poisoned(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_HUNGER:
+ {
+ if (!doit) return "satisfy hunger every 100 turns";
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_STUN:
+ {
+ if (!doit) return "cure stun every 100 turns";
+ set_stun(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_CUTS:
+ {
+ if (!doit) return "cure cuts every 100 turns";
+ set_cut(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_FEAR:
+ {
+ if (!doit) return "cure fear every 100 turns";
+ set_afraid(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_CONF:
+ {
+ if (!doit) return "cure confusion every 100 turns";
+ set_confused(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_BLIND:
+ {
+ if (!doit) return "cure blindness every 100 turns";
+ set_blind(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURING:
+ {
+ if (!doit) return "curing every 110 turns";
+ set_blind(0);
+ set_poisoned(0);
+ set_confused(0);
+ set_stun(0);
+ set_cut(0);
+ set_image(0);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_DARKNESS:
+ {
+ if (!doit) return "darkness";
+ unlite_area(damroll(2, 10), 10);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_LEV_TELE:
+ {
+ if (!doit) return "teleport level every 50 turns";
+ teleport_player_level();
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_ACQUIREMENT:
+ {
+ if (!doit) return "acquirement every 3000 turns";
+ acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_WEIRD:
+ {
+ if (!doit) return "something weird every 5 turns";
+ /* It doesn't do anything */
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_AGGRAVATE:
+ {
+ if (!doit) return "aggravate";
+ aggravate_monsters(1);
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_MUT:
+ {
+ if (!doit) return "gain corruption every 10 turns";
+ gain_random_corruption(0);
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_INSANITY:
+ {
+ if (!doit) return "cure insanity every 200 turns";
+ heal_insanity(damroll(10, 10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_MUT:
+ {
+#if 0 // DGDGDGD
+#else
+ msg_print("Ahah, you wish.");
+#endif
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_LIGHT_ABSORBTION:
+ {
+ int y, x, light = 0, dir;
+ cave_type *c_ptr;
+
+ if (!doit) return "light absorption every 80 turns";
+
+ for (y = p_ptr->py - 6; y <= p_ptr->py + 6; y++)
+ {
+ for (x = p_ptr->px - 6; x <= p_ptr->px + 6; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ c_ptr = &cave[y][x];
+
+ if (distance(y, x, p_ptr->py, p_ptr->px) > 6) continue;
+
+ if (c_ptr->info & CAVE_GLOW)
+ {
+ light++;
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Darken the grid */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr) &&
+ !(c_ptr->info & (CAVE_TRDT)))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ note_spot(y, x);
+ }
+
+ /* Process affected monsters */
+ if (c_ptr->m_idx)
+ {
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ if (!get_aim_dir(&dir)) return (FALSE);
+
+ msg_print("The light around you is absorbed... "
+ "and released in a powerful bolt!");
+ fire_bolt(GF_LITE, dir, damroll(light, p_ptr->lev));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+ /* Horns of DragonKind (Note that these are new egos)*/
+ case ACT_BA_FIRE_H:
+ {
+ if (!doit) return "large fire ball (300) every 100 turns";
+ fire_ball(GF_FIRE, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_COLD_H:
+ {
+ if (!doit) return "large cold ball (300) every 100 turns";
+ fire_ball(GF_COLD, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_ELEC_H:
+ {
+ if (!doit) return "large lightning ball (300) every 100 turns";
+ fire_ball(GF_ELEC, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_ACID_H:
+ {
+ if (!doit) return "large acid ball (300) every 100 turns";
+ fire_ball(GF_ACID, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+
+ case ACT_SPIN:
+ {
+ if (!doit) return "spinning around every 50+d25 turns";
+ do_spin();
+
+ o_ptr->timeout = 50 + randint(25);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+ case ACT_NOLDOR:
+ {
+ if (!doit) return "detect treasure every 10+d20 turns";
+ detect_treasure(DEFAULT_RADIUS);
+
+ o_ptr->timeout = 10 + randint(20);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+ case ACT_SPECTRAL:
+ {
+ if (!doit) return "wraith-form every 50+d50 turns";
+ if (!p_ptr->wraith_form)
+ {
+ set_shadow(20 + randint(20));
+ }
+ else
+ {
+ set_shadow(p_ptr->tim_wraith + randint(20));
+ }
+
+ o_ptr->timeout = 50 + randint(50);
+
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN | PW_EQUIP;
+
+ /* Done */
+ break;
+ }
+ case ACT_JUMP:
+ {
+ if (!doit) return "phasing every 10+d10 turns";
+ teleport_player(10);
+ o_ptr->timeout = 10 + randint(10);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+
+ case ACT_DEST_TELE:
+ {
+ if (!doit) return "teleportation and destruction of the ring";
+ if (!item)
+ {
+ msg_print("You can't activate this when it's there!");
+ }
+ if (get_check("This will destroy the ring. Do you wish to continue? "))
+ {
+ msg_print("The ring explodes into a space distortion.");
+ teleport_player(200);
+
+ /* It explodes, doesn't it ? */
+ take_hit(damroll(2, 10), "an exploding ring");
+ if ( item > 0)
+ {
+ inven_item_increase(item, -255);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase( -item, -255);
+ floor_item_optimize( -item);
+ }
+ }
+
+ break;
+ }
+ /*amulet of serpents dam 100, rad 2 timeout 40+d60 */
+ case ACT_BA_POIS_4:
+ {
+ if (!doit) return "venom breathing every 40+d60 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ msg_print("You breathe venom...");
+ fire_ball(GF_POIS, dir, 100, 2);
+
+ o_ptr->timeout = rand_int(60) + 40;
+
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN | PW_EQUIP;
+
+ /* Done */
+ break;
+ }
+ /*rings of X 50,50+d50 dur 20+d20 */
+ case ACT_BA_COLD_4:
+ {
+ if (!doit) return "ball of cold and resist cold every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, 50, 2);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_4:
+ {
+ if (!doit) return "ball of fire and resist fire every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, 50, 2);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+ case ACT_BA_ACID_4:
+ {
+ if (!doit) return "ball of acid and resist acid every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, 50, 2);
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_4:
+ {
+ if (!doit) return "ball of lightning and resist lightning every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, 50, 2);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BR_ELEC:
+ {
+ if (!doit) return "breathe lightning (100) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe lightning.");
+ fire_ball(GF_ELEC, dir, 100, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_COLD:
+ {
+ if (!doit) return "breathe frost (110) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe frost.");
+ fire_ball(GF_COLD, dir, 110, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_FIRE:
+ {
+ if (!doit) return "breathe fire (200) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe fire.");
+ fire_ball(GF_FIRE, dir, 200, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_ACID:
+ {
+ if (!doit) return "breathe acid (130) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe acid.");
+ fire_ball(GF_ACID, dir, 130, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_POIS:
+ {
+ if (!doit) return "breathe poison gas (150) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe poison gas.");
+ fire_ball(GF_POIS, dir, 150, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_MANY:
+ {
+ if (!doit) return "breathe multi-hued (250) every 60+d60 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(5);
+ msg_format("You breathe %s.",
+ ((chance == 1) ? "lightning" :
+ ((chance == 2) ? "frost" :
+ ((chance == 3) ? "acid" :
+ ((chance == 4) ? "poison gas" : "fire")))));
+ fire_ball(((chance == 1) ? GF_ELEC :
+ ((chance == 2) ? GF_COLD :
+ ((chance == 3) ? GF_ACID :
+ ((chance == 4) ? GF_POIS : GF_FIRE)))),
+ dir, 250, 2);
+
+ o_ptr->timeout = rand_int(60) + 60;
+
+ break;
+ }
+
+ case ACT_BR_CONF:
+ {
+ if (!doit) return "breathe confusion (120) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe confusion.");
+ fire_ball(GF_CONFUSION, dir, 120, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_SOUND:
+ {
+ if (!doit) return "breathe sound (130) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe sound.");
+ fire_ball(GF_SOUND, dir, 130, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_CHAOS:
+ {
+ if (!doit) return "breathe chaos/disenchant (220) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 1 ? "chaos" : "disenchantment")));
+ fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT),
+ dir, 220, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_SHARD:
+ {
+ if (!doit) return "breathe sound/shards (230) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 1 ? "sound" : "shards")));
+ fire_ball((chance == 1 ? GF_SOUND : GF_SHARDS),
+ dir, 230, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_BALANCE:
+ {
+ if (!doit) return "breathe balance (250) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(4);
+ msg_format("You breathe %s.",
+ ((chance == 1) ? "chaos" :
+ ((chance == 2) ? "disenchantment" :
+ ((chance == 3) ? "sound" : "shards"))));
+ fire_ball(((chance == 1) ? GF_CHAOS :
+ ((chance == 2) ? GF_DISENCHANT :
+ ((chance == 3) ? GF_SOUND : GF_SHARDS))),
+ dir, 250, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_LIGHT:
+ {
+ if (!doit) return "breathe light/darkness (200) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 0 ? "light" : "darkness")));
+ fire_ball((chance == 0 ? GF_LITE : GF_DARK), dir, 200, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+ case ACT_BR_POWER:
+ {
+ if (!doit) return "breathe the elements (300) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 3);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+ case ACT_GROW_MOLD:
+ {
+ if (!doit) return "grow mushrooms every 50+d50 turns";
+ msg_print("You twirl and spores fly everywhere!");
+ for (i = 0; i < 8; i++)
+ summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);
+
+ o_ptr->timeout = randint(50) + 50;
+
+ break;
+ }
+ case ACT_MUSIC:
+ /*
+ fall through to unknown, as music should be
+ handled by calling procedure.
+ */
+
+ default:
+ {
+ msg_format("Unknown activation effect: %d.", spell);
+ if ( !doit ) return "Unknown Activation";
+ return NULL;
+ }
+ }
+ }
+
+ /* Set timeout for junkarts
+ * Note that I still need to set the timeouts for other
+ * (non-random) artifacts above
+ */
+ if (is_junkart && doit)
+ o_ptr->timeout = activation_info[o_ptr->pval2].cost / 10;
+
+ return NULL;
+}
+
+
+static bool activate_spell(object_type * o_ptr, byte choice)
+{
+ int mana = 0, gf = 0, mod = 0;
+
+ rune_spell s_ptr;
+
+
+ if (choice == 1)
+ {
+ gf = o_ptr->pval & 0xFFFF;
+ mod = o_ptr->pval3 & 0xFFFF;
+ mana = o_ptr->pval2 & 0xFF;
+ }
+ else if (choice == 2)
+ {
+ gf = o_ptr->pval >> 16;
+ mod = o_ptr->pval3 >> 16;
+ mana = o_ptr->pval2 >> 8;
+ }
+
+ s_ptr.type = gf;
+ s_ptr.rune2 = 1 << mod;
+ s_ptr.mana = mana;
+
+ /* Execute */
+ rune_exec(&s_ptr, 0);
+
+ if (choice == 1) o_ptr->timeout = mana * 5;
+ if (choice == 2) o_ptr->xtra2 = mana * 5;
+
+ return (TRUE);
+}
diff --git a/src/cmd7.c b/src/cmd7.c
new file mode 100644
index 00000000..123822a6
--- /dev/null
+++ b/src/cmd7.c
@@ -0,0 +1,7984 @@
+/* File: cmd7.c */
+
+/* Purpose: More Class commands */
+
+/*
+ * Copyright (c) 1999 Dark God
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+#include "angband.h"
+
+
+/*
+ * Describe class powers of Mindcrafters
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mindcraft_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+
+ /* Clear buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " rad %d", DEFAULT_RADIUS);
+ break;
+ case 1:
+ strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15);
+ break;
+ case 2:
+ strnfmt(p, 80, " range %d", (plev < 25 ? 10 : plev + 2 + p_ptr->to_s * 3));
+ break;
+ case 3:
+ strnfmt(p, 80, " range %d", plev * 5);
+ break;
+ case 4:
+ strnfmt(p, 80, " power %d", plev * (plev < 30 ? 1 : 2));
+ break;
+ case 5:
+ if (plev > 20)
+ strnfmt(p, 80, " dam %dd8 rad %d", 8 + ((plev - 5) / 4), (plev - 20)/8 + 1);
+ else
+ strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4));
+ break;
+ case 6:
+ strnfmt(p, 80, " dur %d", plev);
+ break;
+ case 7:
+ break;
+ case 8:
+ if (plev < 25)
+ strnfmt(p, 80, " dam %d rad %d", (3 * plev) / 2, 2 + (plev / 10));
+ else
+ strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1));
+ break;
+ case 9:
+ strnfmt(p, 80, " dur 11-%d", 10 + plev + plev / 2);
+ break;
+ case 10:
+ strnfmt(p, 80, " dam %dd6 rad %d", plev / 2, 0 + (plev - 25) / 10);
+ break;
+ case 11:
+ strnfmt(p, 80, " dam %d rad %d", plev * (plev > 39 ? 4 : 3), 3 + plev / 10);
+ break;
+ }
+}
+
+
+/*
+ * Describe class powers of Mimics
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mimic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MIMICRY);
+ object_type *o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ /* Clear the buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " dur %d", k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 70));
+ break;
+ case 1:
+ strnfmt(p, 80, " dur %d+d20", 10 + plev);
+ break;
+ case 2:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 3:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 4:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ }
+}
+
+
+/*
+ * Allow user to choose a magic power.
+ *
+ * If a valid spell is chosen, saves it in '*sn' and returns TRUE
+ * If the user hits escape, returns FALSE, and set '*sn' to -1
+ * If there are no legal choices, returns FALSE, and sets '*sn' to -2
+ *
+ * The "prompt" should be "cast", "recite", or "study"
+ * The "known" should be TRUE for cast/pray, FALSE for study
+ *
+ * nb: This function has a (trivial) display bug which will be obvious
+ * when you run it. It's probably easy to fix but I haven't tried,
+ * sorry.
+ */
+bool get_magic_power(int *sn, magic_power *powers, int max_powers,
+ void (*power_info)(char *p, int power), int plev, int cast_stat)
+{
+ int i;
+
+ int num = 0;
+
+ int y = 2;
+
+ int x = 18;
+
+ int minfail = 0;
+
+ int chance = 0;
+
+ int info;
+
+ char choice;
+
+ char out_val[160];
+
+ char comment[80];
+
+ cptr p = "power";
+
+ magic_power spell;
+
+ bool flag, redraw;
+
+
+ /* Assume cancelled */
+ *sn = ( -1);
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Get the spell, if available */
+ if (repeat_pull(sn))
+ {
+ /* Verify the spell */
+ if (powers[*sn].min_lev <= plev)
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Count number of powers that satisfies minimum plev requirement */
+ for (i = 0; i < max_powers; i++)
+ {
+ if (powers[i].min_lev <= plev)
+ {
+ num++;
+ }
+ }
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(%^ss %c-%c, *=List, ESC=exit, %c-%c=Info) Use which %s? ",
+ p, I2A(0), I2A(num - 1), toupper(I2A(0)), toupper(I2A(num - 1)), p);
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ char psi_desc[80];
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Display a list of spells */
+ prt("", 1, x);
+ prt("", y, x);
+ put_str("Name", y, x + 5);
+ put_str("Lv Mana Fail Info", y, x + 35);
+
+ /* Dump the spells */
+ for (i = 0; i < max_powers; i++)
+ {
+ /* Access the spell */
+ spell = powers[i];
+ if (spell.min_lev > plev) break;
+
+ chance = spell.fail;
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[cast_stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[cast_stat]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Get info */
+ power_info(comment, i);
+
+ /* Dump the spell --(-- */
+ strnfmt(psi_desc, 80, " %c) %-30s%2d %4d %3d%%%s",
+ I2A(i), spell.name,
+ spell.min_lev, spell.mana_cost, chance, comment);
+ prt(psi_desc, y + i + 1, x);
+ }
+
+ /* Clear the bottom line */
+ prt("", y + i + 1, x);
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ /* Note verify */
+ info = (isupper(choice));
+
+ /* Lowercase */
+ if (info) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ spell = powers[i];
+
+ /* Provides info */
+ if (info)
+ {
+ c_prt(TERM_L_BLUE, spell.desc, 1, 0);
+
+ /* Restore the screen */
+ inkey();
+ Term_load();
+ character_icky = FALSE;
+ continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ }
+ character_icky = FALSE;
+
+ /* Abort if needed */
+ if (!flag) return (FALSE);
+
+ /* Save the choice */
+ (*sn) = i;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ repeat_push(*sn);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mindcrafter'.
+ */
+void do_cmd_mindcraft(void)
+{
+ int n = 0, b = 0;
+
+ int chance;
+
+ int dir;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+ magic_power spell;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mindcraft_powers, MAX_MINDCRAFT_POWERS,
+ mindcraft_info, plev, A_WIS)) return;
+
+ spell = mindcraft_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_MINDCRAFT) - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_WIS]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_WIS]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else if (b < 90)
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ else
+ {
+ /* Mana storm */
+ msg_print("Your mind unleashes its power in an uncontrollable storm!");
+ project(1, 2 + plev / 10, p_ptr->py, p_ptr->px, plev * 2,
+ GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM);
+ p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Precog */
+ case 0:
+ {
+ /* Magic mapping */
+ if (plev > 44)
+ {
+ wiz_lite();
+ }
+ else if (plev > 19)
+ {
+ map_area();
+ }
+
+ /* Detection */
+ if (plev < 30)
+ {
+ b = detect_monsters_normal(DEFAULT_RADIUS);
+ if (plev > 14) b |= detect_monsters_invis(DEFAULT_RADIUS);
+ if (plev > 4) b |= detect_traps(DEFAULT_RADIUS);
+ }
+ else
+ {
+ b = detect_all(DEFAULT_RADIUS);
+ }
+
+ /* Telepathy */
+ if (plev > 24)
+ {
+ set_tim_esp(p_ptr->tim_esp + plev);
+
+ /* If plvl >= 40, we should have permanent ESP */
+ }
+
+ if (!b) msg_print("You feel safe.");
+
+ break;
+ }
+
+ /* Mindblast */
+ case 1:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ if (randint(100) < plev * 2)
+ {
+ fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)));
+ }
+ else
+ {
+ fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0);
+ }
+
+ break;
+ }
+
+ /* Minor displace */
+ case 2:
+ {
+ if (plev < 25)
+ {
+ teleport_player(10);
+ }
+ else
+ {
+ int ii, ij;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ break;
+ }
+
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2 + (p_ptr->to_s*3)) ||
+ (rand_int(plev * plev / 2) == 0))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10 + p_ptr->to_s / 2, &ij, &ii);
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+ }
+
+ break;
+ }
+
+ /* Major displace */
+ case 3:
+ {
+ if (plev > 29) banish_monsters(plev);
+ teleport_player(plev * 5);
+
+ break;
+ }
+
+ /* Domination */
+ case 4:
+ {
+ if (plev < 30)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_DOMINATION, dir, plev, 0);
+ }
+ else
+ {
+ charm_monsters(plev * 2);
+ }
+
+ break;
+ }
+
+ /* Fist of Force --- not 'true' TK */
+ case 5:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8),
+ (plev > 20 ? (plev - 20) / 8 + 1 : 0));
+
+ break;
+ }
+
+ /* Character Armour */
+ case 6:
+ {
+ set_shield(p_ptr->shield + plev, 50, 0, 0, 0);
+ if (plev > 14) set_oppose_acid(p_ptr->oppose_acid + plev);
+ if (plev > 19) set_oppose_fire(p_ptr->oppose_fire + plev);
+ if (plev > 24) set_oppose_cold(p_ptr->oppose_cold + plev);
+ if (plev > 29) set_oppose_elec(p_ptr->oppose_elec + plev);
+ if (plev > 34) set_oppose_pois(p_ptr->oppose_pois + plev);
+
+ break;
+ }
+
+ /* Psychometry */
+ case 7:
+ {
+ if (plev < 40)
+ {
+ psychometry();
+ }
+ else
+ {
+ ident_spell();
+ }
+
+ break;
+ }
+
+ /* Mindwave */
+ case 8:
+ {
+ msg_print("Mind-warping forces emanate from your brain!");
+ if (plev < 25)
+ {
+ project(0, 2 + plev / 10, p_ptr->py, p_ptr->px,
+ (plev*3) / 2, GF_PSI, PROJECT_KILL);
+ }
+ else
+ {
+ (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1));
+ }
+
+ break;
+ }
+
+ /* Adrenaline */
+ case 9:
+ {
+ set_afraid(0);
+ set_stun(0);
+ hp_player(plev);
+
+ b = 10 + randint((plev * 3) / 2);
+
+ if (plev < 35)
+ {
+ set_hero(p_ptr->hero + b);
+ }
+ else
+ {
+ set_shero(p_ptr->shero + b);
+ }
+
+ if (!p_ptr->fast)
+ {
+ /* Haste */
+ (void)set_fast(b, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + b, 10);
+ }
+
+ break;
+ }
+
+ /* Psychic Drain */
+ case 10:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ b = damroll(plev / 2, 6);
+
+ if (fire_ball(GF_PSI_DRAIN, dir, b, 0 + (plev - 25) / 10))
+ {
+ p_ptr->energy -= randint(150);
+ }
+
+ break;
+ }
+
+ /* Telekinesis */
+ case 11:
+ {
+ msg_print("A wave of pure physical force radiates out from your body!");
+ project(0, 3 + plev / 10, p_ptr->py, p_ptr->px,
+ plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage WIS (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your mind!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_WIS, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static int get_mimic_chance(int mimic)
+{
+ s32b chance;
+
+ call_lua("get_mimic_info", "(d,s)", "d", mimic, "level", &chance);
+ chance *= 3;
+
+ chance -= get_skill_scale(SKILL_MIMICRY, 150);
+ chance -= 3 * adj_mag_stat[p_ptr->stat_ind[A_DEX]];
+
+ if (chance < 2) chance = 2;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+
+void do_cmd_mimic_lore()
+{
+ int fail;
+
+ object_type *o_ptr;
+
+
+ /* Player has to be able to see */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* No transformations when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ /* Already in a mimic form -- Allow cancelling */
+ if (p_ptr->mimic_form)
+ {
+ msg_print("You morph back to your natural form!");
+
+ set_mimic(0, 0, 0);
+ }
+
+ /* Not in mimic forms -- Allow transformations */
+ else
+ {
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ if ((o_ptr->tval != TV_CLOAK) || (o_ptr->sval != SV_MIMIC_CLOAK))
+ {
+ msg_print("You are not wearing any cloaks of mimicry.");
+ return;
+ }
+
+ /* Calculate failure rate */
+ fail = get_mimic_chance(o_ptr->pval2);
+
+ if (fail > 75)
+ {
+ msg_print("You feel uneasy with this shape-change.");
+
+ if (!get_check("Try it anyway? ")) return;
+ }
+
+ /* Fumble */
+ if (randint(100) < fail)
+ {
+ msg_print("Your shape-change goes horribly wrong!");
+
+ if (randint(100) < p_ptr->skill_sav)
+ {
+ msg_print("You manage to wrest your body back under control.");
+ return;
+ }
+
+ set_mimic(30, resolve_mimic_name("Abomination"), get_skill(SKILL_MIMICRY));
+ }
+
+ /* Success */
+ else
+ {
+ set_mimic(k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000), o_ptr->pval2, get_skill(SKILL_MIMICRY));
+ }
+ }
+
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+static bool mimic_forbid_travel(char *fmt)
+{
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ if(value > 0 && (att & CLASS_ARMS || att & CLASS_LEGS))
+ {
+ msg_print("You had best not travel with your extra limbs.");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mimic'.
+ */
+void do_cmd_mimic(void)
+{
+ int n = 0, b = 0;
+
+ int fail;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MIMICRY);
+
+ magic_power spell;
+
+ static bool added_hooks = FALSE;
+ if(!added_hooks)
+ {
+ add_hook(HOOK_FORBID_TRAVEL, mimic_forbid_travel, "mimic_forbid_travel");
+ added_hooks = TRUE;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mimic_powers, MAX_MIMIC_POWERS, mimic_info,
+ plev, A_DEX)) return;
+
+ spell = mimic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ fail = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ fail -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ fail -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ fail += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Minimum failure rate */
+ if (fail < minfail) fail = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) fail += 25;
+ else if (p_ptr->stun) fail += 15;
+
+ /* Always a 5 percent chance of working */
+ if (fail > 95) fail = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < fail)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (fail / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Mimic */
+ case 0:
+ {
+ do_cmd_mimic_lore();
+
+ break;
+ }
+
+ /* Invisibility */
+ case 1:
+ {
+ int ii = 10 + plev + randint(20) + p_ptr->to_s;
+
+ set_invis(p_ptr->tim_invisible + ii, 50);
+ set_tim_invis(p_ptr->tim_invisible + ii);
+
+ break;
+ }
+
+ /* Legs Mimicry */
+ case 2:
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_WALL);
+
+ if (att & CLASS_LEGS)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You mimic a new pair of legs.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_LEGS);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ /* Wall Mimicry */
+ case 3:
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_LEGS);
+
+ if (att & CLASS_WALL)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You grow an affinity for walls.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_WALL);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ case 4: /* Arms Mimicry */
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_LEGS);
+ att &= ~(CLASS_WALL);
+
+ if (att & CLASS_ARMS)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You mimic a new pair of arms.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_ARMS);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage WIS (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your mind!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_DEX, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'beastmaster'.
+ */
+void do_cmd_beastmaster(void)
+{
+ int plev = p_ptr->lev, i, num;
+
+ monster_type *m_ptr;
+
+
+ /* Process the monsters (backwards) */
+ num = 0;
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ num++;
+ }
+ }
+
+ if (num < plev * 2)
+ {
+ /* XXX XXX */
+ if (rand_int(80-(plev) - p_ptr->stat_use[5]-p_ptr->to_s) < 20)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, plev, rand_int(plev / 2), FALSE);
+ }
+ }
+ else msg_print("You can't summon more pets");
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+/*
+ * Set of variables and functions to create an artifact
+ */
+
+
+/* LOG2 is a constant (compile-time) method of converting a single
+ * set bit into a number. Works well, but for variable (runtime)
+ * expressions, use a loop instead.. much smaller code*/
+#define LOG2(x) ( (x) & 0xFFFF? BLOG16(x) : BLOG16((x)>>16) + 16 )
+#define BLOG16(x) ( (x) & 0xFF ? BLOG8(x) : BLOG8 ((x)>>8 ) + 8 )
+#define BLOG8(x) ( (x) & 0xF ? BLOG4(x) : BLOG4 ((x)>>4 ) + 4 )
+#define BLOG4(x) ( (x) & 0x3 ? BLOG2(x) : BLOG2 ((x)>>2 ) + 2 )
+#define BLOG2(x) ( (x) & 0x1 ? 0 : 1 )
+
+int flags_select[32*5];
+int activation_select;
+
+/* Return true if the player is wielding the philosopher's stone
+ */
+bool alchemist_has_stone(void)
+{
+ if (p_ptr->inventory[INVEN_LITE].name1 == 209)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ Display a group of flags from a_select flags, and return
+ the number of flags displayed (even invisible ones)
+ */
+int show_flags(byte group, int pval)
+{
+ int i, x, color = TERM_WHITE;
+ int items = 0;
+
+ char ttt[80];
+
+ Term_clear();
+
+ group++; /* Adjust - no zero group */
+
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ {
+ if (a_select_flags[i].group != group)
+ continue;
+
+ if (a_select_flags[i].xp == 0)
+ break;
+ else
+ {
+ sprintf(ttt, "%c) %s",
+ (items < 26) ? I2A(items) : ('0' + items - 26),
+ al_name + a_select_flags[i].desc);
+ if ( wizard || alchemist_has_stone())
+ sprintf(ttt, "%c) %s (exp %ld)",
+ (items < 26) ? I2A(items) : ('0' + items - 26),
+ al_name + a_select_flags[i].desc,
+ a_select_flags[i].xp);
+
+ /* Note: Somebody is VERY clever, and it wasn't me. Text printed as
+ * TERM_DARK is actually printed as TERM_BLUE *SPACES* to prevent the
+ * player from using a 'cut-and-paste' enabled terminal to see
+ * what he shouldn't. Thus, simply setting the color to TERM_DARK
+ * will entirely prevent the unspoiled player from knowing that it's
+ * even possible. */
+
+ switch (flags_select[i])
+ {
+ case 1:
+ color = TERM_YELLOW;
+ break; /* Flag was set by the player (just now)*/
+ case 0:
+ color = TERM_WHITE;
+ break; /* This flag can be set, player is 'aware' of it*/
+ case - 1:
+ color = TERM_L_GREEN;
+ break; /* Flag is already set*/
+ case - 2:
+ color = TERM_DARK;
+ break; /* Invisible option */
+ case - 3:
+ color = TERM_RED;
+ break; /* Flag is set, but player isn't 'aware' of it */
+ case - 4:
+ color = TERM_L_DARK;
+ break; /* Flag is not set, player is 'aware', but it's beyond thier skill */
+ default:
+ color = TERM_DARK;
+ break; /* Just in Case*/
+ }
+ }
+ /* For alchemists who have the stone, at least show all the flags... */
+ if ((alchemist_has_stone() || wizard) && color == TERM_DARK)
+ color = TERM_BLUE;
+
+ if (items < 16) x = 5;
+ else x = 45;
+ c_prt(color, ttt, ((items < 16) ? items : items - 16) + 5, x);
+ items++;
+
+ }
+ return items;
+}
+
+void show_levels(void)
+{
+ Term_clear();
+ c_prt(TERM_WHITE, "[a] Stats, sustains, luck, speed, vision, etc. ", 3, 10);
+ c_prt(TERM_WHITE, "[b] Misc. (Auras, light, see invis, etc) ", 4, 10);
+ c_prt(TERM_WHITE, "[c] Weapon Branding ", 5, 10);
+ c_prt(TERM_WHITE, "[d] Resistances and Immunities ", 6, 10);
+ c_prt(TERM_WHITE, "[e] ESP and Curses ", 7, 10);
+ c_prt(TERM_WHITE, "[f] Activation ", 8, 10);
+ c_prt(TERM_DARK , "[g] Abilities Gained ", 9, 10);
+ c_prt(TERM_WHITE, "[h] Display Required Essences and items ", 10, 10);
+ c_prt(TERM_WHITE, "[i] Done! Finalize and commit changes. ", 11, 10);
+ /*No need to return anything - if the valid selections change, it'll be a code level change.*/
+}
+
+s32b get_flags_exp(int pval, int oldpval)
+{
+ int i;
+ s32b exp = 0;
+
+ for (i = 0 ; a_select_flags[i].group ; i++ )
+ {
+ if (a_select_flags[i].xp == 0)
+ break;
+ else
+ {
+ if ( a_select_flags[i].group <= 5 && flags_select[i] )
+ {
+ s32b xp = a_select_flags[i].xp;
+ int factor = 1, oldfactor = 0;
+
+ /* don't even look at flags which the user can't set
+ * because they also can't change the pval when a pval-
+ * dependant flag is set, flags which they can't set
+ * cannot effect the exp in any way, whether their set or not
+ */
+ if ( flags_select[i] < -1 )
+ continue;
+ if ( flags_select[i] == -1 )
+ oldfactor = 1;
+
+ if (a_select_flags[i].pval)
+ {
+ /* (1/4)x^2 + x
+ * I wanted something smaller than x^2 or x^x
+ * this is because although a ring of speed +10 is
+ * more than 10 times better than a ring of speed +1,
+ * I don't think it's 100 times better. More like 30.
+ * this function yields:
+ * 1=1 * 2=3 * 3=5 * 4=8 * 5=11 * 6=15 * 7=21
+ * 8=24 * 9=29 * 10=35 * 11=41 * 12=48 * 13=55
+ * 14=63 * 15=71 * 20=120 * 25=181 * 30=255
+ * which I think is acceptable.
+ * briefly, to get a +30 speed ring, it would be:
+ * 255*50000 or over 12 million experience
+ * points. For reference, a level 50 human requires
+ * 5 million xp. I'm sure it's doable, but it'd be
+ * *HARD*
+ * a speed+10 artifact would require 1.75 million.
+ * much more doable, but not too easily.
+ */
+ factor = (pval * pval / 4 + pval);
+ if ( flags_select[i] == -1 )
+ {
+ oldfactor = oldpval * oldpval / 4 + oldpval;
+ }
+ }
+ exp += xp * factor - xp * oldfactor;
+ }
+ if ( a_select_flags[i].group == 88 && a_select_flags[i].flag == -activation_select )
+ {
+ exp += a_select_flags[i].xp;
+ }
+ }
+ }
+ if ( alchemist_has_stone() ) exp = exp / 4;
+ return exp;
+}
+
+/* returns the 'real quantity' of items needed to empower
+ * a particular flag to a particular pval.
+ * Note that this routine returns zero for any flag that
+ * doesn't require some sort of action.
+ */
+int calc_rqty(int i, int pval, int oldpval)
+{
+ /* return 0 if flag is greater than size of flags_select && ! activation */
+ if ( a_select_flags[i].group > 5 )
+ {
+ if ( activation_select == a_select_flags[i].flag)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* return 0 if the flag wasn't set */
+ if ( flags_select[i] < -1 || flags_select[i] == 0 )
+ return 0;
+
+ /* Return change in pval if the flag was already set */
+ if ( flags_select[i] == -1 && a_select_flags[i].pval)
+ return pval - oldpval;
+
+ /* Return pval if the flag will be set this time */
+ else if ( a_select_flags[i].pval )
+ return pval;
+
+ /* Return 0 if the flag is unknown */
+ else if ( flags_select[i] == -1 )
+ return 0;
+ return 1;
+}
+
+/* Handle the various items that creating artifacts requires.
+ * Mode = 0 to print a description,
+ * 1 to use up the items
+ * -1 to check to see if the items exist
+ * Note that this function is called ONLY from the
+ * other artifact item helper function.
+ */
+
+
+int check_artifact_items(int pval, int oldpval, int mode)
+{
+ int i, j, k, row = 1 , col = 15, rqty, orqty, trqty;
+ bool good = TRUE;
+ int temporary = -1;
+ char ch;
+
+ /* For temporary items, waive the item requirements,
+ * except for the corpse... */
+ for ( j = 0 ; a_select_flags[j].group ; j++)
+ if (a_select_flags[j].flag == 4*32 && flags_select[j] == 1 )
+ temporary = j;
+ /* Check for enough items */
+ for (i = 0; a_select_flags[i].group ; i++)
+ {
+ /* For temporary items, ignore
+ everything except the one item
+ */
+ if (temporary != -1 && i != temporary)
+ continue;
+
+ /* Calc quantity is done per flag, because
+ some have a pval, some don't, some where already
+ set at pval=2, etc
+ */
+ rqty = orqty = calc_rqty(i, pval, oldpval);
+
+ /* If no item is associated with this flag,
+ or this flag wasn't set or didn't change */
+ if ( !a_select_flags[i].rtval || !rqty)
+ continue;
+
+ for ( k = 0 ; k < INVEN_WIELD ; k++ )
+ {
+ object_type *o_ptr = &p_ptr->inventory[k];
+
+ /* Note here that an rsval of -1 (which is read is 0xff
+ for a byte..) matches anything. */
+ if (o_ptr->tval == a_select_flags[i].rtval
+ && (o_ptr->sval == a_select_flags[i].rsval
+ || a_select_flags[i].rsval == (byte) - 1 ) )
+ {
+ /* Corpse validation is COMPLICATED!
+ * But at least we don't have to do this twice.
+ */
+ if ( a_select_flags[i].rtval == TV_CORPSE )
+ {
+ bool itemgood = TRUE;
+
+ /*Specified race not this one */
+ if ( o_ptr->pval2 != a_select_flags[i].rpval && a_select_flags[i].rpval)
+ continue;
+
+ /* Race flag (any monster who...)*/
+ for ( j = 0 ; !a_select_flags[i].rpval && a_select_flags[i].rflag[j] && j < 6 && itemgood ; j++)
+ {
+ int flag = a_select_flags[i].rflag[j] / 32;
+ u32b mask = 1 << (a_select_flags[i].rflag[j] % 32);
+
+ switch (flag)
+ {
+ case 0:
+ if ( !(r_info[o_ptr->pval2].flags1 & mask) ) itemgood = FALSE;
+ break;
+ case 1:
+ if ( !(r_info[o_ptr->pval2].flags2 & mask) ) itemgood = FALSE;
+ break;
+ case 2:
+ if ( !(r_info[o_ptr->pval2].flags3 & mask) ) itemgood = FALSE;
+ break;
+ case 3:
+ if ( !(r_info[o_ptr->pval2].flags4 & mask) ) itemgood = FALSE;
+ break;
+ case 4:
+ if ( !(r_info[o_ptr->pval2].flags5 & mask) ) itemgood = FALSE;
+ break;
+ case 5:
+ if ( !(r_info[o_ptr->pval2].flags6 & mask) ) itemgood = FALSE;
+ break;
+ case 6:
+ if ( !(r_info[o_ptr->pval2].flags7 & mask) ) itemgood = FALSE;
+ break;
+ case 7:
+ if ( !(r_info[o_ptr->pval2].flags8 & mask) ) itemgood = FALSE;
+ break;
+ case 8:
+ if ( !(r_info[o_ptr->pval2].flags9 & mask) ) itemgood = FALSE;
+ break;
+ default:
+ msg_print("This code should never be hit!");
+ }
+ }
+ if ( ! itemgood )
+ continue;
+
+ }
+ /* Validate pval of good item */
+ else if ( a_select_flags[i].rpval)
+ {
+ /* Must have matching signs */
+ if ( (o_ptr->pval < 0) != (a_select_flags[i].rpval < 0))
+ continue;
+ /* Must be greater than */
+ if ( abs(o_ptr->pval) < abs(a_select_flags[i].rpval))
+ continue;
+ }
+
+ trqty = MIN(o_ptr->number, rqty);
+ rqty -= trqty;
+
+ if ( mode == 1 )
+ {
+ inven_item_increase(k, -trqty);
+ inven_item_describe(k);
+ /*
+ if we optimize this now, it moves everything after it
+ in the p_ptr->inventory up one, and the pointer to the item
+ being artifactized now points to something else.
+ DON'T DO IT!
+ inven_item_optimize(k);
+ */
+ }
+ }/* if p_ptr->inventory item is acceptable */
+
+ } /*end of looping through the p_ptr->inventory*/
+
+ if (rqty)
+ {
+ good = FALSE;
+ /* Oops, we didn't have enough of this object
+ when actually creating the artifact.
+ unset this flag
+ */
+ if ( mode == 1 )
+ {
+ flags_select[i] = -4;
+ }
+ /* we only return false for mode -1,
+ * for mode 0 we display stuff, and for
+ * mode 1 we want to continue destroying things
+ * even if the player is missing one small item,
+ * because there's no way to change things now.
+ * We may have already destroyed a unique corpse,
+ * or some other hard-to-find item.
+ */
+ if ( mode == -1 )
+ return FALSE;
+ }
+
+ /* Display a description of the required object, if needed */
+ /* Note that the tests for good items HAVE to be in a different
+ place, because otherwise we don't know how many the player
+ has, as opposed to how many they need.
+ */
+ if ( mode == 0 )
+ {
+ char *o_name = al_name + a_select_flags[i].item_desc;
+ if (orqty > 1 && a_select_flags[i].pval && a_select_flags[i].item_descp)
+ o_name = al_name + a_select_flags[i].item_descp;
+
+ if ( rqty )
+ {
+ if ( orqty > 1 )
+ c_prt(TERM_RED, format(" you are missing %d of the %d %s", rqty, orqty, o_name), row++, col);
+ else if ( is_a_vowel(o_name[0]))
+ c_prt(TERM_RED, format(" you are missing an %s", o_name), row++, col);
+ else
+ c_prt(TERM_RED, format(" you are missing a %s", o_name), row++, col);
+ }
+ else
+ {
+ if ( orqty > 1 )
+ c_prt(TERM_GREEN, format(" you have the %d %s", orqty, o_name), row++, col);
+ else if ( is_a_vowel(o_name[0]))
+ c_prt(TERM_GREEN, format(" you have an %s", o_name), row++, col);
+ else
+ c_prt(TERM_GREEN, format(" you have a %s", o_name), row++, col);
+ }
+
+ if ( row > 21 )
+ {
+ row = 1;
+ if (!good)
+ (void)get_com("You are missing some items:", &ch);
+ else
+ (void)get_com("You have these needed items on hand:", &ch);
+ }
+
+ }
+
+ } /* End of group associated with this a_select_flags entry */
+
+ if ( mode == 0 )
+ {
+ while ( row < 22 )
+ c_prt(TERM_GREEN, " ", row++, col);
+ if (!good)
+ (void)get_com("You are missing some items:", &ch);
+ else
+ (void)get_com("You have these needed items on hand:", &ch);
+ }
+ return good;
+}
+
+/* Display a list of required essences,
+ * and/or use up the essences. */
+bool artifact_display_or_use(int pval, int oldpval, bool use)
+{
+ int essence[MAX_BATERIE_SVAL];
+ int essenceh[MAX_BATERIE_SVAL];
+ int al_idx, i, j, k;
+ bool enough;
+
+ /* Temporary Items require only one item, and no essences. */
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ if ( a_select_flags[i].flag == 32*4)
+ {
+ if ( use )
+ return check_artifact_items(pval, oldpval, 1);
+ else
+ return check_artifact_items(pval, oldpval, 0);
+ }
+
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++ )
+ essence[i] = essenceh[i] = 0;
+
+ /* Accumulate a list of required essences */
+ for ( al_idx = 0; al_idx < max_al_idx ; al_idx++ )
+ if ( alchemist_recipes[al_idx].tval == 0 )
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ {
+ int rqty = calc_rqty(i, pval, oldpval);
+
+ /* If the flag isn't being set, rqty will be zero */
+ if ( !rqty)
+ continue;
+
+ if ( alchemist_recipes[al_idx].sval == a_select_flags[i].flag )
+ essence[alchemist_recipes[al_idx].sval_essence] +=
+ alchemist_recipes[al_idx].qty * rqty;
+ }
+
+ /* The essence array now contains a list of all essences
+ * that will be consumed in the creation of this artifact */
+
+ /* Check for existence of required quatities of essences. */
+ for ( i = 0 ; i < INVEN_WIELD ; i++ )
+ {
+ for ( j = 0 ; j < MAX_BATERIE_SVAL ; j++)
+ if ( p_ptr->inventory[i].tval == TV_BATERIE && p_ptr->inventory[i].sval == j + 1)
+ {
+ essenceh[j] += p_ptr->inventory[i].number;
+ }
+ }
+
+ /* Check for enough essences */
+ enough = TRUE;
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ if ( essenceh[i] < essence[i] )
+ {
+ enough = FALSE;
+ break;
+ }
+
+ /* Check for items */
+ if ( enough )
+ enough = check_artifact_items(pval, oldpval, -1);
+
+
+ /* Display recipe list if they don't have enough, or not enough exp */
+ if (!enough || !use )
+ {
+ int row = 1 , col = 15;
+ bool good = FALSE;
+ char ch;
+
+ /* display of list of required essences */
+ /* Note: there are only 12 or so essences, so this list
+ * will ALWAYS fit on the screen */
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ if ( essence[i] )
+ {
+ int missing = -MIN(essenceh[i] - essence[i], 0);
+ good = TRUE;
+ if ( missing )
+ c_prt(TERM_RED, format("%d of the required %d essences of %s",
+ missing, essence[i],
+ k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ),
+ row++, col);
+ else
+ c_prt(TERM_GREEN, format("you have the needed %d essences of %s",
+ essence[i],
+ k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ),
+ row++, col);
+ }
+
+ if (good)
+ {
+ /* blank the bottom row */
+ c_prt(TERM_WHITE, " ", row++, col);
+
+ /* and wait for a key */
+ (void)get_com("You are currently missing:", &ch);
+ }
+
+ /* Display a list of needed items as well */
+ check_artifact_items(pval, oldpval, 0);
+
+ return FALSE;
+ }
+
+ /* If we get to this point in the code, then the player
+ * has the required essences and items in their p_ptr->inventory */
+
+ /* If they do have enough, and they have enough exp, consume them */
+ for (i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ for ( k = 0 ; k < INVEN_WIELD && essence[i] > 0 ; k++)
+ if (p_ptr->inventory[k].tval == TV_BATERIE
+ && p_ptr->inventory[k].sval == i + 1
+ && essence[i])
+ {
+ int num = p_ptr->inventory[k].number;
+
+ inven_item_increase(k, MAX( -essence[i], -num));
+ inven_item_describe(k);
+ /*
+ messy bug, don't optimize here because it
+ rearanges the p_ptr->inventory.
+ inven_item_optimize(k);
+ */
+ essence[i] -= MIN(num, essence[i]);
+ }
+
+ /* Destroy the items needed */
+ check_artifact_items(pval, oldpval, 1);
+
+ return TRUE;
+}
+
+
+void display_activation_info(int num)
+{
+ object_type forge;
+ int i;
+
+
+ /* find the a_select_flags number of this activation type... */
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ if (a_select_flags[i].group == 88 && a_select_flags[i].flag == -num )
+ break;
+
+ object_wipe(&forge);
+ forge.xtra2 = num;
+ /* Print out various information about this activation... */
+ /* min level, experience, required items (and essences)
+ full description (from activation_aux) */
+ if (wizard)
+ c_prt(TERM_WHITE, format(" number:%d ", num), 5, 5);
+ else
+ c_prt(TERM_WHITE, " ", 5, 5);
+ c_prt(TERM_WHITE, format(" Level:%d ", a_select_flags[i].level), 6, 5);
+ c_prt(TERM_WHITE, format(" Exp :%d ", a_select_flags[i].xp), 7, 5);
+ c_prt(TERM_WHITE, format(" Item :%s ", al_name + a_select_flags[i].item_desc), 8, 5);
+ c_prt(TERM_WHITE, " ", 9, 5);
+ c_prt(TERM_WHITE, format(" %s ", activation_aux(&forge, 0, 0)), 9, 5);
+ c_prt(TERM_WHITE, " ", 10, 5);
+ inkey();
+}
+
+void select_an_activation(void)
+{
+ int i, lev, wid, hgt, begin = 0, sel = 0;
+ u32b max;
+ cptr act_list[150]; /* currently, ~127 hardcoded activations */
+ int act_ref[150];
+ char c;
+ /* How do we want to do this? */
+ /* Ideally, we let them select from a list, which includes all the activations that they've ecountered in any form.
+ Problems with this idea include mainly the lack of any (current) place to store which activations they've seen, and
+ that they'll not get credit for any seen before we start tracking it.
+
+ So - list is everything. If they select one which they're to low-level for
+ or if the explicitly request it, we'll display info about this item.
+ We'll also get our descriptions from the activation_aux(ACT_CONSTANT)
+ function, because they are more complete, and include even lua-scripted ones.
+ msg_print("Since the code to actually let you select one isn't here");
+ msg_print("You will automatically get the activation 'Dawn'");
+ activation_select = ACT_DAWN;
+ */
+
+ /* Build a list of available activations at the player's level */
+ lev = get_skill(SKILL_ALCHEMY);
+ for ( i = max = 0 ; max < (sizeof(act_list) / sizeof(cptr)) && a_select_flags[i].group ; i++)
+ if (a_select_flags[i].group == 88 && a_select_flags[i].level <= lev )
+ {
+ act_ref[max] = -a_select_flags[i].flag; /* Activation number */
+ act_list[max++] = al_name + a_select_flags[i].desc; /* Description */
+ }
+
+ /* Select from that list, using the util.c function display_list to display the scrolled list */
+ /* Note: I think that there is only one other place that uses this function. Should be more! */
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, "Enter to select, ? for more information, 2 and 8 to scroll ", 0, 0);
+ display_list(1, 0, hgt - 2, wid - 2, "Select an Activation", act_list, max, begin, sel, TERM_L_GREEN);
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ else if (c == '8')
+ {
+ sel--;
+ if (sel < 0)
+ {
+ sel = max - 1;
+ begin = max - hgt;
+ if (begin < 0) begin = 0;
+ }
+ if (sel < begin) begin = sel;
+ }
+ else if (c == '2')
+ {
+ sel++;
+ if (sel >= (s32b)max)
+ {
+ sel = 0;
+ begin = 0;
+ }
+ if (sel >= begin + hgt - 1) begin++;
+ }
+ else if (c == '?')
+ {
+ display_activation_info(act_ref[sel]);
+ }
+ else if (c == '\r')
+ {
+ display_activation_info(act_ref[sel]);
+ activation_select = act_ref[sel];
+ return;
+ }
+ }
+ activation_select = 0;
+}
+
+
+/* Consume 'num' magic essences and return true.
+ * If there aren't enough essences, return false */
+
+bool magic_essence(int num)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Count the magic essences */
+ if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) j += o_ptr->number;
+ }
+
+ /* Abort if not enough essences. */
+ if (j < num) return FALSE;
+
+ /* Consume them */
+ i = 0;
+ j = num;
+ while (i < INVEN_WIELD)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC))
+ {
+ /* This can lead to invalid object pointer for objects
+ * that come after the magic essences. Therefore, every
+ * artifactable object should come before the essences.
+ */
+ j -= o_ptr->number;
+ inven_item_increase(i, -num);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+ num = j;
+ if (num <= 0) break;
+ /* Stay on this slot; do not increment i. */
+ }
+ else
+ {
+ /* Move on to the next slot. */
+ i++;
+ }
+ }
+
+ /* Sanity check. */
+ if (num > 0)
+ {
+ msg_format("ERROR: Couldn't destroy %d essences!", num);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void do_cmd_create_artifact(object_type *q_ptr)
+{
+ int max, i = 0, j, cur_set = 0, abord = FALSE, done = FALSE;
+ int skill;
+ s32b exp = 0;
+
+ char out_val[160];
+ char choice = 0;
+ bool lockpval = FALSE;
+ int pval;
+ int oldpval;
+ energy_use = 100;
+
+ pval = q_ptr->pval;
+ oldpval = pval;
+ skill = get_skill(SKILL_ALCHEMY);
+
+ if ( !pval )
+ pval = 1;
+ /* No activation added on this round */
+ activation_select = 0;
+
+ /* Save the current flags */
+ for (i = 0 ; a_select_flags[i].group ; i++)
+ {
+ if ( a_select_flags[i].flag < 0 || a_select_flags[i].group > 5)
+ continue;
+
+ flags_select[i] = 0;
+
+ switch (a_select_flags[i].flag / 32)
+ {
+ case 0:
+ if (q_ptr->art_flags1 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 1:
+ if (q_ptr->art_flags2 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 2:
+ if (q_ptr->art_flags3 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 3:
+ if (q_ptr->art_flags4 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 4:
+ if (q_ptr->art_flags5 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 5:
+ if (q_ptr->art_esp & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ default:
+ /*This will not be hit, inspite of activations, because of the <= 5 above...*/
+ break;
+ }
+ /*
+ this would learn about ALL flags....
+ if(wizard)
+ alchemist_known_artifacts[a_select_flags[i].flag/32] = 0xffffffffL;
+ */
+
+ /* Set various flags if they haven't *ID*'d an artifact with this flag set.*/
+ if ( !(alchemist_known_artifacts[a_select_flags[i].flag / 32] & (1 << (a_select_flags[i].flag % 32)) ))
+ {
+ /* If this item has an ability that depends on pval which the player
+ * cannot set, don't allow them to change the pval either. */
+ if ( a_select_flags[i].pval && flags_select[i])
+ lockpval = TRUE;
+
+ /* Set the color and set-ablitity of this flag */
+ if ( flags_select[i] )
+ flags_select[i] = -3;
+ else
+ flags_select[i] = -2;
+ continue;
+ }
+ else if ( skill < a_select_flags[i].level )
+ {
+ /* If the alchemist has not passed the skill level for this flag,
+ Set this flag as unsettable.
+ */
+ if ( flags_select[i])
+ lockpval = TRUE;
+ else
+ flags_select[i] = -4;
+ }
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+
+ /* Everlasting love ... ... nevermind :) */
+ while ( !done && !abord)
+ {
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ /* Display the menu, but don't display it if we just
+ * displayed a message (it erases the screen, creating a blink message */
+ if ( cur_set < 6 || cur_set == 7 )
+ show_levels();
+
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ prt("Enter to accept, Escape to abort", 1, 0);
+
+ abord = !get_com("Play around with which group of powers?[a-g]", &choice);
+
+ if ( choice == ESCAPE)
+ abord = TRUE;
+
+ if ( abord )
+ continue; /*or break, same diff */
+
+ if ( isalpha(choice))
+ {
+ if (isupper(choice))
+ choice = tolower(choice);
+ cur_set = A2I(choice);
+ }
+ else
+ {
+ bell();
+ continue;
+ }
+
+ if ( cur_set == 5 )
+ {
+ if (q_ptr->xtra2 && !activation_select
+ && !get_check("This item already activates! Choose a different activation?")) continue;
+ select_an_activation();
+ exp = get_flags_exp(pval, oldpval);
+ continue;
+ }
+ if ( cur_set == 6 )
+ {
+ msg_print("This option is not available");
+ continue;
+ }
+ if ( cur_set == 7 )
+ {
+ artifact_display_or_use(pval, oldpval, FALSE);
+ continue;
+ }
+ if ( cur_set == 8 )
+ {
+ if (q_ptr->exp - exp < 0)
+ msg_print("Not enough experience for the flags you've selected.");
+ else
+ done = TRUE;
+ continue;
+ }
+
+ if (cur_set < 0 || cur_set > 4 )
+ {
+ bell();
+ continue;
+ }
+
+
+ while (!done && !abord)
+ {
+ /* Chose the flags */
+ exp = 0;
+ max = show_flags(cur_set, pval);
+ exp = get_flags_exp(pval, oldpval);
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ /* Build a prompt (accept all flags) */
+ if (max <= 26)
+ {
+ /* Build a prompt (accept all flags) */
+ strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ",
+ I2A(0), I2A(max - 1));
+ }
+ else
+ {
+ strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ",
+ I2A(0), '0' + max - 27);
+ }
+ c_prt(TERM_L_BLUE, format("Power(I/D to increase/decrease): %d", pval), 3, 0);
+
+ /* Get a spell from the user */
+ while (!(done = !get_com(out_val, &choice)))
+ {
+ if (choice == 'I')
+ {
+ if ( lockpval )
+ {
+ msg_print("You cannot do that - you don't know how!");
+ continue;
+ }
+ if (q_ptr->exp - exp < 0)
+ {
+ msg_print("Not enough experience. Decrease power or deselect flags.");
+ continue;
+ }
+ pval++;
+ break;
+ }
+ else if (choice == 'D')
+ {
+ if ( lockpval )
+ {
+ msg_print("You cannot do that - you don't know how!");
+ continue;
+ }
+ pval--;
+ if (pval < oldpval) pval = oldpval;
+ break;
+ }
+ else if (choice == '\r' || choice == ESCAPE || choice == ' ')
+ {
+ done = TRUE;
+ break;
+ }
+ else if (isalpha(choice))
+ {
+ /* Lowercase */
+ if (isupper(choice)) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ i = D2I(choice) + 26;
+
+ /* Illegal */
+ if (i < 26) i = -1;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= max))
+ {
+ bell();
+ continue;
+ }
+ else
+ {
+ /*Find the i'th flag in group cur_set...*/
+ for ( j = 0 ; a_select_flags[j].group ; j++)
+ if (a_select_flags[j].group == cur_set + 1)
+ if (!i--) break;
+
+ if ( flags_select[j] == -4 )
+ {
+ msg_format("You need at least %d skill in alchemy.",
+ a_select_flags[j].level);
+ continue;
+ }
+ if ( flags_select[j] != 0 && flags_select[j] != 1)
+ {
+ bell();
+ continue;
+ }
+ if (flags_select[j]) flags_select[j] = 0;
+ else if (!flags_select[j])
+ {
+ if (q_ptr->exp - exp < 0)
+ {
+ msg_print("Not enough experience. Decrease power or deselect flags.");
+ continue;
+ }
+ flags_select[j] = 1;
+ }
+ break;
+ }
+ }
+ }/*sub-screen select and redraw loop*/
+ done = FALSE;
+ Term_clear();
+ }/* main screen (flag select screen) select and redraw loop*/
+
+ /* Abort if not enough experience, or no flags added */
+ if ( q_ptr->exp - exp < 0 || exp == 0 )
+ abord = TRUE;
+
+ /* Display the recipe, or use up the essences.
+ * Note that this has to be done before the screen
+ * is restored. This is because it's also called from
+ * within the loop to display the required items. */
+ if ( !abord )
+ if (!artifact_display_or_use(pval, oldpval, TRUE))
+ abord = TRUE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Return if abort, or missing ingredients */
+ if ( abord )
+ return;
+
+ /* Actually create the artifact */
+ q_ptr->exp -= exp;
+ q_ptr->art_flags4 &= ~TR4_ART_EXP;
+ q_ptr->pval = pval;
+
+ /* Just to be sure */
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD );
+
+ {
+ int now = 0, before = 0;
+ char dummy_name[80];
+ char new_name[80];
+
+ /* Apply the flags */
+ for (i = 0; a_select_flags[i].group ; i++)
+ {
+ if (flags_select[i] < 0)
+ before++;
+ else if ( flags_select[i] == 1)
+ {
+ now++;
+ switch (a_select_flags[i].flag / 32)
+ {
+ case 0:
+ q_ptr->art_flags1 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 1:
+ q_ptr->art_flags2 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 2:
+ q_ptr->art_flags3 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 3:
+ q_ptr->art_flags4 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 4:
+ q_ptr->art_flags5 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 5:
+ q_ptr->art_esp |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ default:
+ msg_print("error: this code can't ever be hit!");
+ }
+ }
+ }
+
+ if ( activation_select )
+ {
+ q_ptr->art_flags3 |= TR3_ACTIVATE;
+ q_ptr->xtra2 = activation_select;
+ }
+
+
+ /* Set the 'show modifier' flag */
+ q_ptr->art_flags3 |= TR3_SHOW_MODS;
+
+ /* For temporary items, set a timeout.
+ * alchemist_skill^2 for now */
+ if ( q_ptr->art_flags5 & TR5_TEMPORARY )
+ {
+ int lev = get_skill(SKILL_ALCHEMY);
+ q_ptr->timeout = lev * lev * 3;
+ }
+
+ /* Describe the new artifact */
+ object_out_desc(q_ptr, NULL, FALSE, TRUE);
+
+
+ /* Name the new artifact */
+ strcpy(dummy_name, "of an Alchemist");
+ if (!(get_string("What do you want to call the artifact? ", dummy_name, 80)))
+ strcpy(new_name, "of an Alchemist");
+ else
+ {
+ if ((strncmp(dummy_name, "of ", 3) == 0) ||
+ (strncmp(dummy_name, "Of ", 3) == 0) ||
+ ((dummy_name[0] == '\'') &&
+ (dummy_name[strlen(dummy_name) - 1] == '\'')))
+ {
+ strcpy(new_name, dummy_name);
+ }
+ else
+ {
+ strcpy(new_name, "called '");
+ strcat(new_name, dummy_name);
+ strcat(new_name, "'");
+ }
+ }
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+ q_ptr->ident |= IDENT_STOREB; /* This will be used later on... */
+
+ /* Save the inscription */
+ q_ptr->art_name = quark_add(new_name);
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ done = FALSE;
+ while (!done && get_com("Do you want to let this item continue to gain experience?", &choice))
+ {
+ switch (choice)
+ {
+ case 'y':
+ case 'Y':
+ if (magic_essence(get_skill(SKILL_ALCHEMY)))
+ q_ptr->art_flags4 |= TR4_ART_EXP;
+ else
+ msg_format("Oh, NO! You don't have enough magic essences. You needed %d.", get_skill(SKILL_ALCHEMY));
+ done = TRUE;
+ break;
+ case 'n':
+ case 'N':
+ q_ptr->exp = 0;
+ done = TRUE;
+ break;
+ }
+ }
+
+ /* Cycle through the p_ptr->inventory, and optimize everything.
+ * This wasn't done earlier, because if we had, then
+ * things in the p_ptr->inventory would shift around, and q_ptr
+ * wouldn't point to the right thing. BUT, at this point
+ * we don't need q_ptr anymore, so optimizing the p_ptr->inventory
+ * becomes sane. Sticky bug to figure out, let me tell you.
+ * Note also that this is cycling backwards - this is so
+ * that the same effect doesn't cause us to skip items. */
+ for ( i = INVEN_WIELD - 1 ; i >= 0 ; i-- )
+ inven_item_optimize(i);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+}
+
+/*
+ * Test to see if this tval/sval combo is in the alchemists'
+ * recipes as a createable item. Used to determine if we
+ * should extract from it.
+ */
+bool alchemist_exists(int tval, int sval, int ego, int artifact)
+{
+ int al_idx;
+
+ /* To prevent conflicts with recipes for ego-items.
+ * artifact not used, simplifies the loop below. */
+ if ((tval == 1) || artifact)
+ return FALSE;
+
+ /*Search for recipes with this tval/sval combo as the final result*/
+ for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++)
+ {
+ int rtval = alchemist_recipes[al_idx].tval;
+ int rsval = alchemist_recipes[al_idx].sval;
+
+ /* Accept ego wands and staves since ego is extracted last */
+ if (((!ego || tval == TV_WAND || tval == TV_STAFF) && rtval == tval && rsval == sval) ||
+ ( ego && rtval == 1 && rsval == ego))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ * Hook to determine if an object can have things extracted from it.
+ */
+bool item_tester_hook_extractable(object_type *o_ptr)
+{
+
+ /* No artifacts */
+ if (artifact_p(o_ptr)) return (FALSE);
+
+ /* No cursed things */
+ if (cursed_p(o_ptr)) return (FALSE);
+
+ /* If we REALLY wanted to rebalance alchemists,
+ * we'd test for 'fully identified this object kind' here.
+ */
+
+ return ((o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != 0)
+ || alchemist_exists(o_ptr->tval, o_ptr->sval, o_ptr->name2, o_ptr->name1));
+}
+
+/*
+ * Hook to determine if an object is empowerable (NOT rechargeable)
+ */
+bool item_tester_hook_empower(object_type *o_ptr)
+{
+ int sval = -1;
+ int lev = get_skill(SKILL_ALCHEMY);
+ /* after level 25, can empower ego items to create artifacts
+ * and double ego items.
+ * after level 50, can empower artifacts to create powerful artifacts
+ */
+
+ /* Never Empower a cursed item */
+ if ( cursed_p(o_ptr))
+ {
+ return FALSE;
+ }
+
+ /* Allow finalizing a self created artifact */
+ if (artifact_p(o_ptr)
+ && (o_ptr->art_flags4 & TR4_ART_EXP)
+ && !(o_ptr->art_flags4 & TR4_ULTIMATE))
+ return TRUE;
+
+ switch ( o_ptr->tval)
+ {
+ /* Empowerable objects: Traditional alchemist stuff */
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ case TV_ROD_MAIN:
+ sval = -1;
+ break;
+ case TV_BOOK:
+ sval = -1;
+ break;
+
+ /* Ego item stuff */
+ /* Disallow ego dragon armour before you can create artifacts.*/
+ case TV_DRAG_ARMOR:
+ if ( lev < 25)
+ return FALSE;
+ /* FALL THROUGH! no break here. */
+
+ /* weapons */
+
+ case TV_DAEMON_BOOK:
+ case TV_SWORD:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_AXE:
+ case TV_MSTAFF:
+
+ /* misc other items */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_INSTRUMENT:
+ case TV_DIGGING:
+ case TV_LITE:
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+
+ /* Armor of various sorts */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+
+ /* Disallow ANY creation of ego items below level 5*/
+ if ( lev < 5)
+ return FALSE;
+
+ /* empowering an ego item creates an artifact or a
+ * double ego item, disallow below level 25 */
+ if ( lev < 25 && o_ptr->name2)
+ return FALSE;
+
+ /* Disallow double-ego and artifact unless the character has
+ * the artifact creation ability. */
+ if (!has_ability(AB_CREATE_ART) &&
+ (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b)))
+ return FALSE;
+
+ /* Otherwise... */
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ /* Return to the traditional alchemist objects.
+ * All ego items and artifacts returning TRUE are accepted as artifactable
+ * at level 25. If we want double ego non wieldable items (Fireproof Staff
+ * of Plenty) the artifactable test in do_cmd_alchemist() must be changed,
+ * e.g. checking if the item is wearable.
+ * For now, we disallow non-wearable ego-items and artifacts here.
+ */
+
+ if ((o_ptr->name2 || artifact_p(o_ptr)) &&
+ o_ptr->tval != TV_RING && o_ptr->tval != TV_AMULET)
+ return FALSE;
+
+ /* return true if it's a 'of nothing' item;
+ * does nothing for TV_ROD_MAIN and TV_BOOK
+ */
+ return (sval == o_ptr->sval
+
+ /* or if it's artifactable */
+ || ((lev >= 50 || (lev >= 25 && !artifact_p(o_ptr))) &&
+ (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET))
+
+ /* or if it's egoable (note that normal egos start at level 5, wands and such start at 15) */
+ || (!o_ptr->name2 && lev >= 15));
+}
+
+/* Extract a rod tip from a rod */
+void rod_tip_extract(object_type *o_ptr)
+{
+ object_type *q_ptr;
+ object_type forge;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Paranoia, return if it's a rod of nothing */
+ if (o_ptr->pval == SV_ROD_NOTHING)
+ return;
+
+ /* Extract the rod tip */
+ object_prep(q_ptr, lookup_kind(TV_ROD, o_ptr->pval));
+
+ q_ptr->number = o_ptr->number;
+
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Remove it from the rod */
+ o_ptr->pval = SV_ROD_NOTHING;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+}
+
+
+/* Begin & finish an art */
+void do_cmd_toggle_artifact(object_type *o_ptr)
+{
+ char o_name[80];
+
+ if (!(o_ptr->art_flags4 & TR4_ART_EXP))
+ {
+ bool okay = TRUE;
+
+ if ( !alchemist_has_stone())
+ {
+ msg_print("Creating an artifact will result into a permanent loss of 10 hp.");
+ if (!get_check("Are you sure you want to do that?")) return;
+ }
+
+ if (!magic_essence(get_skill(SKILL_ALCHEMY)))
+ {
+ msg_format("You need %d magic essences.", get_skill(SKILL_ALCHEMY));
+ return;
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ if (o_ptr->number > 1)
+ {
+ msg_print("Not enough energy to enchant more than one object!");
+ msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was"));
+ o_ptr->number = 1;
+ }
+ okay = TRUE;
+
+ if (!okay) return;
+
+ /* he/she got warned */
+ p_ptr->hp_mod -= 10;
+
+ /* Ok toggle it */
+ o_ptr->art_flags4 |= TR4_ART_EXP;
+ o_ptr->name2 = 0;
+ o_ptr->name2b = 0;
+ o_ptr->art_name = quark_add("Becoming");
+
+ /* Copy the object_kind flags to the artifact flags.
+ * Note that this is only needed so that flags set in the
+ * 'kind' area are visible when finalizing the artifact.
+ */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ o_ptr->art_flags1 |= f1;
+ o_ptr->art_flags2 |= f2;
+ o_ptr->art_flags3 |= f3;
+ o_ptr->art_flags4 |= f4;
+ o_ptr->art_flags5 |= f5;
+ o_ptr->art_esp |= esp;
+ }
+
+ p_ptr->update |= (PU_HP);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ else
+ {
+ do_cmd_create_artifact(o_ptr);
+ }
+}
+
+/*
+ * Test to see if they have all the ingredients to create an item.
+ * (doesn't count base item)
+ * creates 'tocreate' items (may be -1, but no more than that!)
+ * if tocreate=0, will return true if the player has enough
+ * in their p_ptr->inventory to empower that item.
+ */
+bool alchemist_items_check(int tval, int sval, int ego, int tocreate, bool message)
+{
+ int al_idx, j;
+ bool exists = FALSE;
+
+
+ for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ )
+ if ((ego && alchemist_recipes[al_idx].sval == ego
+ && alchemist_recipes[al_idx].tval == 1 )
+ || (!ego && alchemist_recipes[al_idx].sval == sval
+ && alchemist_recipes[al_idx].tval == tval))
+ {
+ exists = TRUE;
+ /* Create the essences */
+ if (tocreate > 0)
+ {
+ object_type forge;
+ object_type *o_ptr = &forge;
+
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence));
+ o_ptr->number = alchemist_recipes[al_idx].qty * tocreate;
+ /* Don't bother with apply_magic */
+
+ /* Randomly decrease the number of essences created */
+ if ( randint(3) == 1
+ && randint(52) > get_skill(SKILL_ALCHEMY)
+ && !alchemist_has_stone())
+ o_ptr->number /= randint(2) + 1;
+ if ( o_ptr->number == 0)
+ continue;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ if (inven_carry_okay(o_ptr))
+ {
+ int i;
+ inven_carry(o_ptr, FALSE);
+ for (i = 0; i < INVEN_WIELD ; i++)
+ if (p_ptr->inventory[i].tval == o_ptr->tval && p_ptr->inventory[i].sval == o_ptr->sval)
+ {
+ if ( message )
+ inven_item_describe(i);
+ break;
+ }
+
+ }
+ else
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ o_ptr->ident |= IDENT_STOREB;
+ }
+ else if ( tocreate < -1)
+ {
+ /*It's not valid to create more than one
+ * thing at a time, so if it's less than -1,
+ * it must be time to display a recipe
+ */
+ msg_format("%d essences of %d",
+ alchemist_recipes[al_idx].qty,
+ al_idx);
+ }
+ else /* Destroy the essences (tocreate == -1)
+ * or check for existence(tocreate == 0)*/
+ {
+ int rqty = alchemist_recipes[al_idx].qty;
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[j];
+ if (o_ptr->k_idx
+ && (o_ptr->tval == TV_BATERIE )
+ && (o_ptr->sval == alchemist_recipes[al_idx].sval_essence )
+ && (o_ptr->number >= rqty ))
+ {
+ /* At this point, the item is required, destroy it. */
+ if ( tocreate )
+ {
+ inven_item_increase(j, 0 - rqty);
+ if ( message)
+ inven_item_describe(j);
+ inven_item_optimize(j);
+ }
+
+ /* When we find enough of the item, break out of the
+ * 'search through the p_ptr->inventory' loop */
+ break;
+ }
+ }
+ if ( j == INVEN_WIELD)
+ /* This ingredient was not found, cannot do recipe */
+ return FALSE;
+ }/*destroying items, or just checking for existence */
+ }
+ return exists;
+}
+
+/* This function lists all the ingredients
+ * needed to create something.
+ */
+void alchemist_display_recipe(int tval, int sval, int ego)
+{
+ int al_idx;
+ int row = 1, col = 15;
+ char o_name[80];
+ char ch;
+ object_type *o_ptr, forge;
+
+ /* Display the ingredients for a recipe */
+ for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ )
+ if ((ego && alchemist_recipes[al_idx].sval == ego
+ && alchemist_recipes[al_idx].tval == 1 )
+ || (!ego && alchemist_recipes[al_idx].sval == sval
+ && alchemist_recipes[al_idx].tval == tval))
+ {
+ int qty = alchemist_recipes[al_idx].qty;
+ c_prt(TERM_GREEN,
+ format(" %d essence%s %s ", qty,
+ qty > 1 ? "s" : "",
+ k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)].name ),
+ row++, col);
+ }
+
+ c_prt(TERM_WHITE, " ", row++, col);
+
+ if (!ego)
+ {
+ /* Find the name of that object */
+ o_ptr = &forge;
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ o_ptr->name2 = ego;
+ hack_apply_magic_power = -99;
+ apply_magic(o_ptr, get_skill(SKILL_ALCHEMY) * 2, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ /* the 0 mode means only the text, leaving off any numbers */
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+ else
+ {
+ /* Display the ego item name */
+ strcpy(o_name, e_name + e_info[ego].name);
+ }
+
+ /* Display a short message about it, and wait for a key. */
+ (void)get_com(format("ingredients needed to create a %s", o_name), &ch);
+
+}
+
+/*
+ *
+ * The alchemist_recipe_select was copied from
+ * wiz_create_itemtype
+ * and then changed quite a bit.
+ *
+ */
+
+/*
+ The select array is a simple array of 'use this char to select item x'
+ It has 88 items (three columns of 20 each)
+ selectitem is initilized with the reverse mappings:
+ selectitem[selectchar[x]] == x is always true.
+ */
+char selectchar[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*():;,.<=>[]{}/=?+'~";
+byte selectitem[256];
+
+void strip_and_print(char *str, int color, int num)
+{
+ int row = 2 + (num % 20), col = 40 * (num / 20);
+ int ch, max_len = 0;
+ char buf[80];
+ char *string;
+
+ if (num > 60)
+ {
+ msg_print("Attempting to display too many items!");
+ return;
+ }
+ ch = selectchar[num];
+ if (selectitem[ch] != num)
+ {
+ int i;
+ for ( i = 0 ; i < 256 ; i++)
+ selectitem[i] = 0xff;
+ for ( i = 0 ; selectchar[i] ; i++)
+ selectitem[(byte)selectchar[i]] = i;
+ }
+
+ /* Skip past leading characters */
+ while ((*str == ' ') || (*str == '&')) str++;
+
+ /* Copy useful chars */
+ for (string = buf; *str; str++)
+ if (*str != '~') *string++ = *str;
+
+ /* Terminate the new name */
+ *string = '\0';
+
+ /* strip the name down to size
+ if (76-col < (signed)max_len)
+ max_len = 76-col;
+ else
+ max_len = 30-6;*/
+ max_len = 39;
+
+ string = buf;
+ if (strlen(string) > (unsigned)max_len)
+ string = string + (strlen(string) - max_len);
+
+ /* Print it */
+ c_prt(color, format("[%c] %s", ch, string), row, col);
+}
+
+/* Display a list of recipes that need a particular essence.
+ * Note that we display a list of essences first,
+ * so in effect, this is the alchemist's recipe book.
+ */
+void alchemist_recipe_book(void)
+{
+ int num, max_num, i, al_idx, bat, kidx;
+ int choice[61], choice2[61];
+ int mod40;
+ bool essence[MAX_BATERIE_SVAL + 1];
+ char ch;
+
+ /* Save and clear the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ while ( TRUE )
+ {
+ Term_clear();
+
+ num = 0;
+
+ /* Display bateries */
+
+ /* start with assumption that the alchemist knows about no recipes */
+ for (i = 0; i < MAX_BATERIE_SVAL + 1 ; i++)
+ essence[i] = FALSE;
+
+ /* cycle through all alchemist recipes */
+ for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++)
+ /* if we aren't already going to display this essence */
+ if (!essence[alchemist_recipes[al_idx].sval_essence])
+ {
+
+ /*Note that we don't display artifact recipes here...*/
+ /*This is partially because artifacts often require exotic
+ ingredients as well */
+
+ if (!alchemist_recipes[al_idx].tval)
+ continue;
+
+ if (alchemist_recipes[al_idx].tval == 1)
+ {
+ if (alchemist_known_egos[alchemist_recipes[al_idx].sval / 32]
+ & (1 << (alchemist_recipes[al_idx].sval % 32)) )
+ essence[alchemist_recipes[al_idx].sval_essence] = TRUE;
+ continue;
+ }
+
+ kidx = lookup_kind(alchemist_recipes[al_idx].tval, alchemist_recipes[al_idx].sval);
+ if (alchemist_recipes[al_idx].tval != 1 && k_info[kidx].know)
+ essence[alchemist_recipes[al_idx].sval_essence] = TRUE;
+
+ }
+ for (num = 0, i = 0; i < MAX_BATERIE_SVAL + 7 ; i++)
+ if (essence[i] || i > MAX_BATERIE_SVAL)
+ {
+ int kidx = lookup_kind(TV_BATERIE, i);
+ if (i > MAX_BATERIE_SVAL)
+ {
+ switch (i)
+ {
+ case (MAX_BATERIE_SVAL + 1): strip_and_print("Scrolls", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 2): strip_and_print("Potions", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 3): strip_and_print("Wands", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 4): strip_and_print("Rings", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 5): strip_and_print("Staves", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 6): strip_and_print("Amulets", TERM_WHITE, num);
+ break;
+ default:
+ continue;
+ }
+ }
+ else
+ /* add this essence to the list*/
+ strip_and_print(k_name + k_info[kidx].name, TERM_WHITE, num);
+
+ choice[num++] = i;
+ }
+ max_num = num;
+ if ( max_num == 0)
+ {
+ /*Note that this should never actually happen, as any skill
+ at alchemy automatically gets you some recipes, and this
+ procedure shouldn't be called for players without alchemist skill
+ */
+ msg_print("You don't know any recipes!");
+ msg_print("You can't be an alchemist without recipes!");
+ break;
+ }
+
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 ||
+ !get_com(format("Which Type of Recipe?[a-%c]", selectchar[max_num - 1]), &ch))
+ break;
+
+ /* Analyze choice - note that the cast to byte prevents overflow*/
+ num = selectitem[(byte)ch];
+
+ }
+ /* This break, and the break for no recipes above,
+ are the only exits from this procedure.
+ */
+ if ( num == 0xff || num >= max_num)
+ break;
+
+ /* Save the baterie index */
+ bat = choice[num];
+ num = 0;
+
+ /*Display the 'type of object' recipe screen*/
+ if (bat > MAX_BATERIE_SVAL)
+ {
+ int tval;
+ switch (bat)
+ {
+ case MAX_BATERIE_SVAL + 1:
+ tval = TV_SCROLL;
+ break;
+ case MAX_BATERIE_SVAL + 2:
+ tval = TV_POTION;
+ break;
+ case MAX_BATERIE_SVAL + 3:
+ tval = TV_WAND;
+ break;
+ case MAX_BATERIE_SVAL + 4:
+ tval = TV_RING;
+ break;
+ case MAX_BATERIE_SVAL + 5:
+ tval = TV_STAFF;
+ break;
+ case MAX_BATERIE_SVAL + 6:
+ tval = TV_AMULET;
+ break;
+ }
+ Term_load();
+ alchemist_recipe_select(&tval, 0, FALSE, TRUE);
+ Term_save();
+ continue;
+ }
+ mod40 = 0;
+ while ( TRUE )
+ {
+ int skipped;
+
+ Term_clear();
+ num = 0;
+
+ if (mod40)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ choice[num] = -2;
+ choice2[num++] = 0;
+ }
+
+ /* Display all items made with this essence */
+ for ( al_idx = 0 , skipped = 0 ; al_idx < max_al_idx ; al_idx++)
+ if ( alchemist_recipes[al_idx].sval_essence == bat)
+ {
+ int sval = alchemist_recipes[al_idx].sval;
+ int tval = alchemist_recipes[al_idx].tval;
+ char names[200] = "";
+
+ if (alchemist_recipes[al_idx].tval == 1)
+ {
+ /* Ego items */
+ ego_item_type *e_ptr = &e_info[sval];
+ int j, k;
+
+ if ( !(alchemist_known_egos[sval / 32] & (1 << (sval % 32))))
+ continue;
+
+ for ( j = 0 ; j < 6 && e_ptr->tval[j] ; j ++ )
+ {
+ if ( j > 0 && e_ptr->tval[j] == e_ptr->tval[j - 1])
+ continue;
+ for ( k = 0; tvals[k].tval; k++)
+ if (tvals[k].tval == e_ptr->tval[j])
+ {
+ strcat(names, tvals[k].desc);
+ strcat(names, ", ");
+ break;
+ }
+ }
+ strcat(names, e_name + e_ptr->name);
+ }
+ else
+ {
+ /* Normal Items */
+ int kidx = lookup_kind(tval, sval);
+ int k;
+ if ( !k_info[kidx].know )
+ continue;
+
+ for ( k = 0; tvals[k].tval; k++)
+ if (tvals[k].tval == tval)
+ {
+ strcat(names, tvals[k].desc);
+ break;
+ }
+ strcat(names, " of ");
+ strcat(names, k_name + k_info[kidx].name);
+
+ }
+
+ /*Skip the first mod40 pages of recipes*/
+ if (skipped++ < mod40*38)
+ continue;
+
+ /* add this object kind to the list*/
+ strip_and_print(names, TERM_WHITE, num);
+ choice[num] = tval;
+ choice2[num++] = sval;
+ if (num > 38)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ choice[num] = -1;
+ choice2[num++] = 0;
+ break;
+ }
+
+ }/*Loop through tidx/sidx*/
+
+ max_num = num;
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 || !get_com(
+ format("Examine which recipe?[%c-%c]", selectchar[0], ch)
+ , &ch))
+ {
+ break;
+ }
+
+ /* Analyze choice */
+ num = selectitem[(byte)ch];
+ }
+
+ if ( choice[num] < 0)
+ {
+ if (choice[num] < -1)
+ mod40--;
+ else
+ mod40++;
+ continue;
+ }
+
+ if ( num == 0xff || num >= max_num)
+ break;
+
+ /* Display the recipe */
+ if (choice[num] == 1)
+ alchemist_display_recipe(0, 0, choice2[num]);
+ else
+ alchemist_display_recipe(choice[num], choice2[num], 0);
+ }
+ /*
+ break is at top of loop, after essence list
+ if( num < 0 || num >= max_num)
+ break;
+ */
+
+ }/*show recipes*/
+
+ /* Restore screen contents */
+ Term_load();
+ character_icky = FALSE;
+}
+
+/* Display a list of known recipies that can be made with
+ * materials on hand (including the passed tval). Also
+ * calls the recipe_display function, if requested by the
+ * player or there aren't enough essences to make the
+ * requested object.
+ *
+ * Note: sval is ignored if !ego, tval is the only determinant
+ * of what recipies are available otherwise.
+ *
+ * This function needs to be able to scroll a list, because
+ * there are SO MANY potions. :)
+ */
+int alchemist_recipe_select(int *tval, int sval, int ego, bool recipe)
+{
+ int i, mod40 = 0, num, max_num = 0;
+
+ cptr tval_desc2 = "";
+ char ch;
+ bool done = FALSE;
+
+ int choice[60];
+ int validc[60];
+
+ char *string;
+
+
+ /* Save and clear the screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ /* Base object type chosen, fill in tval */
+ for ( num = 0 ; num < 40 ; num ++)
+ if (tvals[num].tval == *tval)
+ {
+ tval_desc2 = tvals[num].desc;
+ }
+
+ while (!done)
+ {
+ Term_clear();
+ if (ego)
+ {
+ /* Find matching ego items */
+ for (num = 0, i = 1; (num < 40) && (i < max_e_idx) ; i++)
+ {
+ int j;
+ ego_item_type *e_ptr = &e_info[i];
+
+ /* Skip if unknown ego type */
+ if ( !(alchemist_known_egos[i / 32] & (1 << (i % 32))))
+ continue;
+
+ /* search in permitted tvals/svals for allowed egos */
+ for ( j = 0 ; j < 6 ; j ++ )
+ if ( e_ptr->tval[j] == *tval
+ && sval >= e_ptr->min_sval[j]
+ && sval <= e_ptr->max_sval[j])
+ {
+ int color = TERM_GREEN;
+
+ /*Reject if not opposite end of name
+ prefixes only on postfix egos,
+ postfixes only on prefix egos.
+ */
+ if (ego != -1 && e_ptr->before == e_info[ego].before)
+ continue;
+
+ /*Color it red of the alchemist doesn't have the essences to create it*/
+ if (!alchemist_items_check(*tval, 0, i, 0, TRUE))
+ color = TERM_RED;
+
+ /* add this ego to the list*/
+ strip_and_print(e_name + e_info[i].name, color, num);
+ validc[num] = color;
+ choice[num++] = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ char skipped = 0;
+ num = 0;
+ if (mod40 != 0)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ validc[num] = TERM_WHITE;
+ choice[num++] = -1;
+ }
+
+ for (i = 1; (num < 39) && (i < max_k_idx); i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze matching items */
+ if (k_ptr->tval == *tval || (k_ptr->tval == TV_POTION2 && *tval == TV_POTION))
+ {
+ char color = TERM_GREEN;
+ /* Hack -- Skip instant artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /*Don't display recipes that the alchemist doesn't know about*/
+ if (!k_ptr->know && !wizard) continue;
+
+ /*Skip recipes that are somehow known, but don't exist*/
+ if (!alchemist_exists(k_ptr->tval, k_ptr->sval, 0, 0))
+ continue;
+
+ /* Skip the first 39 if they hit 'more' */
+ if (skipped++ < mod40*39)
+ continue;
+
+ /* Color 'unable to create' items different */
+ if (!alchemist_items_check(k_ptr->tval, k_ptr->sval, 0, 0, TRUE))
+ color = TERM_RED;
+
+ /* Acquire the "name" of object "i" */
+ /* and print it in it's place */
+ strip_and_print(k_name + k_ptr->name, color, num);
+
+ /* Remember the object index */
+ validc[num] = color;
+ choice[num++] = i;
+ }
+ }
+ if (num == 39)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ validc[num] = TERM_WHITE;
+ choice[num++] = -1;
+ }
+ }
+
+ /* We need to know the maximal possible remembered object_index */
+ max_num = num;
+ string = "What Kind of %s? (* to see recipe) [%c-%c,*]";
+ num = 0xff;
+
+ /* Pretend they're all undoable if we where called to display recipes */
+ if (recipe)
+ {
+ for ( num = 0 ; num < max_num ; num++)
+ if (validc[num] != TERM_WHITE) validc[num] = TERM_RED;
+ string = "show which %s recipe? [%c-%c]";
+ }
+
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 || !get_com(format(string, tval_desc2, selectchar[0], ch), &ch))
+ {
+ break;
+ }
+
+ /* Extra breaks for recipe */
+ if (recipe && (ch == '\r' || ch == ' ' || ch == ESCAPE ))
+ break;
+
+ /* Analyze choice */
+ num = selectitem[(byte)ch];
+
+ /* Pretend that we don't have enough essences for anything */
+ if (ch == '*' )
+ {
+ for ( num = 0 ; num < max_num ; num++)
+ if (validc[num] != TERM_WHITE) validc[num] = TERM_RED;
+ string = "Show which %s recipe? [%c-%c]";
+ }
+ }
+ if ( num == 0xff || max_num == 0 || num >= max_num)
+ break;
+
+ if ( validc[num] == TERM_WHITE )
+ {
+ if (num == 0)
+ mod40--;
+ else
+ mod40++;
+ if ( mod40 < 0)
+ mod40 = 0;
+ continue;
+ }
+
+ /* If we don't have enough essences, or user asked for recipes */
+ if ( validc[num] != TERM_GREEN )
+ {
+ /* Display the recipe */
+ if (ego)
+ alchemist_display_recipe(*tval, sval, choice[num]);
+ else
+ alchemist_display_recipe(k_info[choice[num]].tval, k_info[choice[num]].sval, 0);
+ }
+ else
+ done = TRUE;
+
+ }/*while(!done)*/
+
+ /* Restore screen contents */
+ Term_load();
+ character_icky = FALSE;
+
+ /* User abort, or no choices */
+ if (max_num == 0 || num == 0xff || num >= max_num)
+ {
+ if (max_num == 0)
+ msg_print("You don't know of anything you can make using that.");
+ return ( -1);
+ }
+ if ( validc[num] != TERM_GREEN )
+ return ( -1);
+
+ /* And return successful */
+ if ( ego )
+ return choice[num];
+
+ /* Set the tval, should be the same unless they selected a potion2 */
+ if (*tval != k_info[choice[num]].tval && *tval != TV_POTION)
+ msg_print("Coding error: tval != TV_POTION");
+ *tval = k_info[choice[num]].tval;
+ return ( k_info[choice[num]].sval );
+}
+
+/* Set the 'known' flags for all objects with a level <= lev
+ * This lets the budding alchemist create basic items.
+ */
+void alchemist_learn_all(int lev)
+{
+ int i;
+
+ if ( !get_skill(SKILL_ALCHEMY) )
+ return;
+
+ /* msg_format("You learn about level %d items",lev); */
+
+ for ( i = 0 ; i < max_k_idx ; i++ )
+ if ( k_info[i].level <= lev )
+ if (alchemist_exists(k_info[i].tval, k_info[i].sval, 0, 0))
+ k_info[i].know = TRUE;
+}
+
+void alchemist_learn_ego(int ego)
+{
+ char *name;
+ int i;
+
+ /* some Paranoia*/
+ if ( !ego || ego >= max_e_idx )
+ return;
+
+ /* Get the ego items name */
+ name = e_name + e_info[ego].name;
+ while (strchr(name, ' '))
+ name = strchr(name, ' ') + 1;
+
+ /* Don't learn about egos without recipes, and
+ * always learn about the passed ego item. */
+ if (alchemist_exists(0, 0, ego, 0))
+ {
+ alchemist_known_egos[ego / 32] |= (1 << (ego % 32));
+ /* msg_format("You learn about '%s' ego items.",e_name+e_info[ego].name); */
+ }
+ else
+ {
+ return;
+ }
+
+ /* Don't mass learn about egos that have no name. */
+ if ( name[0] == 0 )
+ {
+ return;
+ }
+
+ /* Look through all ego's for matching name */
+ /* Note that the original ego is marked here too */
+ for ( i = 0 ; i < max_e_idx ; i++ )
+ if ( strstr(e_name + e_info[i].name, name) != NULL /*Last word of name exists in this ego's name*/
+ && alchemist_exists(0, 0, i, 0) /*There exists a recipe for this*/
+ && !(alchemist_known_egos[i / 32] & (1 << (i % 32)) ) ) /*Not already known*/
+ /*&& (e_name+e_info[i].name)[0])non-blank name*/
+ {
+ alchemist_known_egos[i / 32] |= (1 << (i % 32));
+ /* msg_format("You learn about '%s' ego items.",e_name+e_info[i].name); */
+ }
+
+ return;
+}
+
+/* Alchemist has learned about a new item.
+ * Learn about not only it, but ALL egos with the
+ * same name.
+ */
+int alchemist_learn_object(object_type *o_ptr)
+{
+
+ /* Allow alchemist to create this item,
+ and.. learn about it even if the player
+ doesn't currently have the alchemy skill
+ */
+ k_info[o_ptr->k_idx].know = TRUE;
+
+ /* Not Paranoia, identify_fully calls this always */
+ if ( !get_skill(SKILL_ALCHEMY) )
+ return FALSE;
+
+ if ( artifact_p(o_ptr) )
+ {
+ char o_name[80];
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Randarts and normal artifacts both*/
+ alchemist_known_artifacts[0] |= f1;
+ alchemist_known_artifacts[1] |= f2;
+ alchemist_known_artifacts[2] |= f3;
+ alchemist_known_artifacts[3] |= f4;
+ alchemist_known_artifacts[4] |= f5;
+ alchemist_known_artifacts[5] |= esp;
+
+ object_desc(o_name, o_ptr, 1, 0);
+ msg_format("You learn all about the abilities of %s!", o_name);
+ }
+ if (o_ptr->name2)
+ alchemist_learn_ego(o_ptr->name2);
+
+ if (o_ptr->name2b)
+ alchemist_learn_ego(o_ptr->name2b);
+
+ return (TRUE);
+}
+
+/* Alchemist has gained a level - set the ego flags
+ * for all egos <= lev/4.
+ */
+void alchemist_gain_level(int lev)
+{
+ object_type forge;
+ object_type *o_ptr = &forge;
+
+ if ( lev == 0)
+ {
+ /* Learn about potions of Detonation */
+ k_info[417].know = TRUE;
+ }
+ if ( lev == 5)
+ {
+ int ego;
+ int egos[] = {
+ 7/*armor of resist fire*/
+ , 18/*shield of resist fire*/
+ , 74/*shocking weapon*/
+ , 75/*fiery weapon*/
+ , 76/*frozen weapon*/
+ , 77/*Venomous weapon*/
+ , 78/*Chaotic weapon*/
+ , 115/*projectile of venom*/
+ , 116/*projectile of Acid*/
+ , 122/*projectile of flame*/
+ , 123/*projectile of frost*/
+ , 137/*Lite of fearlessness*/
+ , 0 /*terminator*/
+ };
+ object_wipe(o_ptr);
+ /* learn about some basic ego items */
+ /* Note that this is just to get you started. */
+ for ( ego = 0 ; egos[ego] ; ego++)
+ {
+ o_ptr->name2 = egos[ego];
+ alchemist_learn_object(o_ptr);
+ }
+ msg_print("You recall your old master teaching you about elemental item infusing.");
+ }
+ if ( lev == 10)
+ {
+ /*For 'hard rooms' Players only, learn about diggers.*/
+ if (ironman_rooms)
+ {
+ msg_print("There's gotta be an easier way to get into all these vaults!");
+ object_wipe(o_ptr);
+ o_ptr->name2 = 101; /* Ego item, 'of digging' */
+ alchemist_learn_object(o_ptr);
+ }
+ }
+ if ( lev == 25)
+ {
+ msg_print("You recall your old master reminiscing about legendary infusings");
+ msg_print("and the Philosophers' stone.");
+
+ /* No auto-learn on artifacts - by this level, you'll have *ID*'d several */
+ }
+ if ( lev == 25)
+ {
+ msg_print("You wonder about shocking daggers of slay evil.");
+ }
+ if ( lev == 50)
+ {
+ /* learn about Temporary item creation */
+ /* Note that this is the ONLY way to learn this,
+ because spells which create a temporary item
+ also fully ID it. */
+ alchemist_known_artifacts[4] |= TR5_TEMPORARY;
+ msg_print("It suddenly occurs to you that artifacts don't *HAVE* to be permanent...");
+ }
+
+ /* Every Four Levels, learn about items that are
+ * less than that.
+ * Note that this isn't a significant effect after the
+ * first few levels, as the level at which you are learning
+ * things here quickly drops behind the level at which you
+ * are finding items.
+ */
+ if ( (lev & 0x3) != 0 )
+ return;
+ lev = (lev >> 2) + 1;
+ alchemist_learn_all(lev);
+
+}
+
+/* This, in combination with some code in loadsave.c,
+ insures that alchemist_gain_level is called EXACTLY
+ once with each possible value during the characters
+ lifetime.
+ */
+void alchemist_check_level()
+{
+ u32b lev = get_skill(SKILL_ALCHEMY);
+ if ( alchemist_gained > lev )
+ return;
+ /*Paranoia*/
+ if ( !lev )
+ return;
+ while ( alchemist_gained <= lev )
+ alchemist_gain_level(alchemist_gained++);
+}
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'alchemist'.
+ */
+void do_cmd_alchemist(void)
+{
+ int item, ext = 0;
+ int value, basechance;
+ int askill;
+ bool repeat = 0;
+ char ch;
+
+ object_type *o_ptr, *q_ptr;
+ object_type forge, forge2;
+ byte carry_o_ptr = FALSE;
+
+ cptr q, s;
+
+ /* With the new skill system, we can no longer depend on
+ * check_exp to handle the changes and learning involved in
+ * gaining levels.
+ * So we'll have to check for it here.
+ */
+ alchemist_check_level();
+ askill = get_skill(SKILL_ALCHEMY);
+
+
+ q_ptr = &forge;
+
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+ if ((o_ptr->tval != TV_GLOVES) || (o_ptr->sval != SV_SET_OF_LEATHER_GLOVES))
+ {
+ msg_print("You must wear gloves in order to do alchemy.");
+ return;
+ }
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ while (TRUE)
+ {
+ if (!get_com("[P]ower, [R]echarge or [L]eech an item, [E]xtract essences, or recipe [B]ook?", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if (ch == ' ' )
+ {
+ ext = 0;
+ break;
+ }
+ if (ch == 'P' || ch == 'p')
+ {
+ ext = 1;
+ break;
+ }
+ if (ch == 'E' || ch == 'e')
+ {
+ ext = 2;
+ break;
+ }
+ if (ch == 'R' || ch == 'r')
+ {
+ ext = 3;
+ break;
+ }
+ if (ch == 'L' || ch == 'l')
+ {
+ ext = 2;
+ repeat = 1;
+ break;
+ }
+ if (ch == 'B' || ch == 'b')
+ {
+ ext = 4;
+ break;
+ }
+ }
+
+ /**********Add a power*********/
+ if (ext == 1)
+ {
+ int i, qty, tval, sval = 0, ego = 0;
+ char o_name[200];
+
+ /* Get an item */
+ q = "Empower which item? ";
+ s = "You have no empowerable items.";
+ item_tester_hook = item_tester_hook_empower;
+
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ /* Create an artifact from an ego or double ego item,
+ * from a previous artifact, or finish an artifact
+ */
+ if ((askill >= 25) && (artifact_p(o_ptr) || o_ptr->name2) && has_ability(AB_CREATE_ART))
+ {
+ if (get_check("Create an artifact?"))
+ {
+ do_cmd_toggle_artifact(o_ptr);
+ return;
+ }
+ /* Don't change artifacts or double ego items further */
+ else if (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b))
+ return;
+ }
+ /*Ok, now we have the item, so we can now pick recipes.
+ Note: No recipe is known unless we have 'extracted' from
+ that object type. I.E. the 'know' flag (also greater identify)
+ is set.
+ */
+
+ /* Here we're not setting what kind of ego item it IS,
+ * where' just deciding that it CAN be an ego item */
+ if ( o_ptr->name2 ) /* creating a DUAL ego */
+ ego = TRUE;
+ if ( o_ptr->tval < 40 && o_ptr->tval != TV_BOTTLE)
+ ego = TRUE;
+ if ( o_ptr->tval == TV_ROD_MAIN || o_ptr->tval == TV_DAEMON_BOOK || o_ptr->tval == TV_BOOK)
+ ego = TRUE;
+
+ sval = o_ptr->sval;
+ if (!ego)
+ {
+ switch ( o_ptr->tval)
+ {
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ }
+ }
+ if ( o_ptr->sval != sval )
+ ego = TRUE;
+
+ tval = o_ptr->tval;
+ sval = o_ptr->sval;
+
+ /*HACK - bottles don't have the same tval as potions*/
+ /*Everything else will have the same tval after empowering*/
+ if (tval == TV_BOTTLE) tval = TV_POTION;
+ if (ego)
+ if (o_ptr->name2)
+ ego = alchemist_recipe_select(&tval, sval, o_ptr->name2, FALSE);
+ else
+ ego = alchemist_recipe_select(&tval, sval, -1, FALSE);
+ else
+ sval = alchemist_recipe_select(&tval, 0, 0, FALSE);
+
+ if ( sval < 0 || ego < 0)
+ return;
+
+ /* Check to make sure we have enough essences */
+ /* theoretically this is taken care of by recipe_select*/
+ /* but we'll double check just for paranoia. */
+ if (!alchemist_items_check(tval, sval, ego, 0, TRUE))
+ {
+ msg_print("You do not have enough essences.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Use up the essences */
+ (void)alchemist_items_check(tval, sval, ego, -1, TRUE);
+
+ /* Enchant stacks of ammunition at a time */
+ if ( o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW || o_ptr->tval == TV_BOLT )
+ {
+ qty = 1;
+ while (qty < o_ptr->number && alchemist_items_check(tval, sval, ego, -1, FALSE))
+ qty++;
+ }
+ else
+ qty = 1;
+
+ /* Copy the object */
+ q_ptr = &forge;
+ object_copy(q_ptr, o_ptr);
+
+ if ( o_ptr->tval == TV_WAND)
+ {
+ /* distribute charges on wands */
+ q_ptr->pval = o_ptr->pval / o_ptr->number;
+ o_ptr->pval -= q_ptr->pval;
+ }
+
+ o_ptr = q_ptr;
+ o_ptr->number = qty;
+ carry_o_ptr = TRUE;
+
+ /* Destroy the initial object */
+ if (item >= 0)
+ {
+ /* Destroy an item in the pack */
+ inven_item_increase(item, -qty);
+ inven_item_describe(item);
+ }
+ else
+ {
+ /* Destroy an item on the floor */
+ floor_item_increase(0 - item, -qty);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+
+ if ( ego )
+ {
+ int pval, pval2;
+ s32b pval3;
+
+ pval = o_ptr->pval;
+ pval2 = o_ptr->pval2;
+ pval3 = o_ptr->pval3;
+
+ if (o_ptr->name2)
+ o_ptr->name2b = ego;
+ else
+ o_ptr->name2 = ego;
+ o_ptr->pval = randint(e_info[ego].max_pval - 1) + 1;
+ /* dilemma - how to prevent creation of cursed items,
+ * without allowing the creation of artifacts?
+ * We can't, unless we want to finalize the ego flags ourselves.
+ */
+ apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE);
+ /* Remember what the old pval was, so that we can re-apply it. */
+ if ( o_ptr->tval == TV_WAND
+ || o_ptr->tval == TV_RING
+ || o_ptr->tval == TV_AMULET
+ || o_ptr->tval == TV_STAFF)
+ {
+ o_ptr->pval = pval;
+ o_ptr->pval2 = pval2;
+ o_ptr->pval3 = pval3;
+ }
+ else if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ o_ptr->pval = pval;
+ }
+ else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255))
+ {
+ o_ptr->pval = pval;
+ }
+ else if (o_ptr->tval == TV_SHOT
+ || o_ptr->tval == TV_ARROW
+ || o_ptr->tval == TV_BOLT)
+ {
+ o_ptr->pval2 = pval2;
+ }
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ o_ptr->pval2 = pval2;
+ }
+
+ /* Calculate failure rate, lev=val/2500+5 */
+ value = MIN(e_info[o_ptr->name2].cost, 50000);
+ if (o_ptr->name2b) value += MIN(e_info[o_ptr->name2b].cost, 50000);
+ basechance = (value / 1000 + 5 - get_skill_scale(SKILL_ALCHEMY, 100) ) * 10;
+ if ( basechance < 0) basechance = 0;
+ if ( basechance > 100) basechance = 100;
+
+ value = object_value_real(o_ptr);
+
+ }
+ else /* not an ego item */
+ {
+ o_ptr = &forge;
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ hack_apply_magic_power = -99;
+ apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE);
+ if ( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF)
+ o_ptr->pval = 0;
+ value = object_value_real(o_ptr);
+
+ basechance = k_info[o_ptr->k_idx].level - askill * 2;
+ basechance *= 10;
+
+ /* Can't fail more that 100% of the time... */
+ if (basechance > 100)
+ basechance = 100;
+ /* Always success in creation of potion of detonations */
+ if (o_ptr->tval == TV_POTION && o_ptr->sval == SV_POTION_DETONATIONS)
+ {
+ basechance /= 10;
+#if 0 /* Let's see how it works */
+ o_ptr->discount = 100;
+#endif
+ }
+ }
+
+ /* Use up gold to create items */
+ /* this has the effect of making the alchemist
+ chronically short of funds, unless he finds the
+ philosopher's stone. It also means the easiest
+ things to make are 'bad', like a potion of
+ detonations...
+ */
+ /* Problem - to restrictive. We need something
+ which requires less money. But at the same time,
+ we don't want an 'easy cash' situation. Maybe something
+ like '10% * level difference', meaning at skill level 5,
+ level one items are free? But egos are frequently level
+ zero! Maybe egos are forced to level 25? with a cost ceiling?
+ I mean, Potions and scrolls are really the problem causing the
+ 'easy cash' situation, it's ego items. Ego items require
+ relatively few essences, and the rewards are HUGE. Most powerful
+ potions and scrolls require rare essences. Maybe force all egos
+ to require a magic essence? But then you'd get lots of magic
+ from distilling them. Maybe consumed in the creation? then when
+ you got a powerful item, you could make one ego item...
+ But if making things doesn't take gold, what about the cash
+ does the Philosopher's stone do?
+ Time*/
+
+ /* 0% failure if you have the stone */
+ if ( alchemist_has_stone())
+ basechance = 0;
+
+ if (basechance > 0 && value)
+ {
+ char string[80];
+ string[0] = '0';
+ string[1] = 0;
+
+ msg_format("The chance of success is only %d%%!", 100-basechance);
+ get_string("How much gold do you want to add?", string, 50);
+ i = atoi(string);
+ /* Note: don't trust the user to enter a positive number... */
+ if ( i < 0)
+ i = 0;
+ if ( i > p_ptr->au)
+ i = p_ptr->au;
+
+ if (i)
+ {
+ basechance = basechance - (i * 20) / value;
+ msg_format("The chance of success improved to %d%%.", 100-basechance);
+ }
+
+ if (randint(100) < basechance )
+ /*creation failed, even with the extra gold...*/
+ carry_o_ptr = FALSE;
+
+ /* Redraw gold */
+ p_ptr->au -= i;
+ p_ptr->redraw |= (PR_GOLD);
+ }
+
+ /* Set fully identified
+ * After all, the player just made it...
+ */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= IDENT_MENTAL;
+ o_ptr->found = OBJ_FOUND_SELFMADE;
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ if ( carry_o_ptr)
+ {
+ msg_format("You have successfully created %s %s",
+ (o_ptr->number > 1 ? "some" : (is_a_vowel(o_name[0]) ? "an" : "a")),
+ o_name);
+
+ if (inven_carry_okay(o_ptr))
+ inven_carry(o_ptr, FALSE);
+ else
+ {
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+ msg_format("You drop the %s", o_name);
+ }
+ carry_o_ptr = FALSE;
+ }
+ else /* don't carry, or in other words... */
+ {
+ int level = k_info[o_ptr->k_idx].level;
+ if (o_ptr->name1) /* created ego item */
+ level += e_info[o_ptr->name2].level;
+
+ msg_format("Your attempt backfires! Your %s explodes!", o_name);
+ take_hit(damroll(3, level - askill ) , "Alchemical Explosion");
+ p_ptr->redraw |= (PR_HP);
+ }
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Optimize the entire p_ptr->inventory - needed because we
+ don't know how many essences where used, and we may
+ have 'used up' a wielded item as well.
+ */
+ for ( item = 0 ; item < INVEN_TOTAL ; item++ )
+ inven_item_optimize(item);
+
+ /**********Extract a power*********/
+ }
+ else if (ext == 2)
+ {
+ int ego;
+ bool discharge_stick = FALSE;
+
+ /* s_ptr holds the empty items */
+ object_type *s_ptr = NULL;
+ bool carry_s_ptr = FALSE;
+
+ item_tester_hook = item_tester_hook_extractable;
+
+ /* Get an item */
+ q = "Extract from which item? ";
+ s = "You have no item to extract power from.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* This is to prevent creating magic essences by extracting
+ * from a recharged wand of dragon breath or something.
+ */
+ if (( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF )
+ && o_ptr->art_flags4 & TR4_RECHARGED)
+ {
+ msg_print("You cannot extract essences after it's been magically recharged.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Handle Rods before the loop, since they don't stack */
+ if (o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != SV_ROD_NOTHING)
+ {
+ rod_tip_extract(o_ptr);
+ return;
+ }
+
+ do
+ { /* Repeat (for leech command) */
+
+ /* Create the items.
+ * we don't care if they drop to the ground,
+ * and if no action was taken, return
+ */
+ ego = 0;
+ if ( o_ptr->name2)
+ ego = o_ptr->name2;
+
+ /* For ego staves and wands (not of nothing), discharge before extracting the ego */
+ discharge_stick = (o_ptr->pval > 0 &&
+ ((o_ptr->tval == TV_STAFF && o_ptr->sval != SV_STAFF_NOTHING) ||
+ (o_ptr->tval == TV_WAND && o_ptr->sval != SV_WAND_NOTHING)));
+ if (discharge_stick)
+ ego = 0;
+
+ if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, ego, 1, TRUE))
+ {
+ msg_print("You cannot extract anything from that item.");
+ return;
+ }
+
+ if (o_ptr->name2b && !alchemist_items_check(o_ptr->tval, o_ptr->sval, o_ptr->name2b, 1, TRUE))
+ {
+ /* do nothing - if the second ego can't be extracted
+ because there is no recipe for it, simply destroy it
+ */
+ }
+
+ /* Once in three times, learn how to make the item */
+ /* Sorry for the complicated if! Basically, if it's an
+ * unknown regular item or an unknown ego item, there's
+ * a one in 3 chance that it'll be id'd */
+ if (((!ego && !k_info[o_ptr->k_idx].know)
+ || (ego && !(alchemist_known_egos[ego / 32] & (1 << (ego % 32)))))
+ && randint(3) == 1)
+ {
+ msg_print("While destroying it, you gain insight into this item.");
+ /* If over level 10, the player has a chance of 'greater ID'
+ * on extracted items
+ */
+ if (askill > 9)
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+ alchemist_learn_object(o_ptr);
+ }
+
+ /* Always learn what kind of thing it is */
+ object_known(o_ptr);
+ object_aware(o_ptr);
+
+ /* If it's a wand or staff with charges (but not of nothing),
+ * decrease number of charges, unstacking if needed.
+ * Otherwise, create the 'of nothing' item and destroy the old one.
+ */
+ if (discharge_stick)
+ {
+ /* Unstack staves */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ /* Create one local copy of the staff */
+ q_ptr = &forge2;
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Unstack the copied staff */
+ o_ptr->number--;
+
+ /* Use the local copy of the staff */
+ o_ptr = q_ptr;
+ carry_o_ptr = TRUE;
+ }
+ /* remove one charge */
+ o_ptr->pval--;
+ }
+ else
+ {
+ /* Create the empty, plain item */
+ /* If the item was already created, increase the number */
+ if (carry_s_ptr)
+ {
+ s_ptr->number++;
+ }
+ else
+ {
+ /* Otherwise we must create a local copy of the empty item */
+ int tval, sval;
+ bool create_item = TRUE;
+
+ tval = o_ptr->tval;
+ if ( !ego && (tval == TV_POTION || tval == TV_POTION2))
+ tval = TV_BOTTLE;
+
+ sval = o_ptr->sval;
+
+ if (!ego)
+ {
+ switch ( tval)
+ {
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ default:
+ create_item = FALSE;
+ }
+ }
+
+ if (create_item)
+ {
+ /* Create the empty item */
+ s_ptr = &forge;
+ object_wipe(s_ptr);
+ object_prep(s_ptr, lookup_kind(tval, sval));
+ s_ptr->number = 1;
+
+ /* Force creation of non ego non cursed */
+ hack_apply_magic_power = -99;
+ apply_magic(s_ptr, 0, FALSE, FALSE, FALSE);
+
+ /* Hack -- remove possible curse */
+ if (cursed_p(s_ptr))
+ {
+ s_ptr->art_flags3 &= ~(TR3_CURSED | TR3_HEAVY_CURSE);
+ s_ptr->ident &= ~(IDENT_CURSED);
+ }
+
+ /* Restore pvals (e.g. charges ==0) of the item */
+ if (ego && ((tval == TV_WAND) || (tval == TV_STAFF) ||
+ (tval == TV_RING) || (tval == TV_AMULET)))
+ {
+ s_ptr->pval = o_ptr->pval;
+ s_ptr->pval2 = o_ptr->pval2;
+ s_ptr->pval3 = o_ptr->pval3;
+ }
+ /* Restore the spell stored in a random book */
+ else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255))
+ {
+ s_ptr->pval = o_ptr->pval;
+ }
+ /* Restore the type of explosive ammo */
+ else if (o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW
+ || o_ptr->tval == TV_BOLT)
+ {
+ s_ptr->pval2 = o_ptr->pval2;
+ }
+ /* Restore the music stored in an instrument */
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ s_ptr->pval2 = o_ptr->pval2;
+ }
+
+ object_aware(s_ptr);
+ object_known(s_ptr);
+ s_ptr->ident |= IDENT_STOREB;
+
+ /* The empty item will be added to the p_ptr->inventory later */
+ carry_s_ptr = TRUE;
+ }
+ }
+
+ /* Now, we can delete the original (leeched) object.
+ * Is o_ptr an p_ptr->inventory / floor item or a local copy?
+ */
+ if (!carry_o_ptr)
+ {
+ /* Break the leech-loop if it was the last item */
+ if (o_ptr->number == 1)
+ repeat = 0;
+
+ if (item >= 0)
+ {
+ inven_item_increase(item, ( -1));
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase(0 - item, ( -1));
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+ else
+ {
+ /* Forget the local object */
+ carry_o_ptr = FALSE;
+
+ /* reset o_ptr to the original stack,
+ * which contains at least another item */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ }
+ }
+ }
+ while ( repeat == 1);
+
+ /* If we carry empty items, add them to the p_ptr->inventory */
+ if (carry_s_ptr)
+ inven_carry(s_ptr, TRUE);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /******* Recharge an item *******/
+ }
+ else if (ext == 3)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Recharge which item? ";
+ s = "You have no rechargable items.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR ))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Make sure we have enough essences to recharge this */
+ if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, 0, TRUE))
+ {
+ msg_print("You don't have the essences to recharge this item.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Destroy the essences */
+ (void)alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, -1, TRUE);
+
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ /* Unstack staves */
+ /* Get local object */
+ q_ptr = &forge2;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Unstack the used item */
+ o_ptr->number--;
+
+ o_ptr = q_ptr;
+ carry_o_ptr = TRUE;
+ }
+ o_ptr->pval++;
+ }
+ else if ( ext == 4)
+ {
+ alchemist_recipe_book();
+ }
+ /* Just in case - */
+ if (carry_o_ptr)
+ {
+ /* the o_ptr item was probably an unstacked staff
+ * Anyway, we need to add it to the p_ptr->inventory */
+ if (inven_carry_okay(o_ptr))
+ inven_carry(o_ptr, TRUE);
+ else
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+ }
+}
+
+
+/*
+ * Command to ask favors from your god.
+ */
+void do_cmd_pray(void)
+{
+ if (p_ptr->pgod == GOD_NONE)
+ {
+ msg_print("Pray hard enough and your prayers might be answered.");
+ return;
+ }
+ else
+ {
+ if (!p_ptr->praying)
+ msg_format("You start praying to %s.", deity_info[p_ptr->pgod].name);
+ else
+ msg_format("You stop praying to %s.", deity_info[p_ptr->pgod].name);
+ p_ptr->praying = !p_ptr->praying;
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ p_ptr->redraw |= PR_PIETY | PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP;
+ energy_use = 100;
+ }
+}
+
+
+/*
+ * Return percentage chance of spell failure.
+ */
+int spell_chance_random(random_spell* rspell)
+{
+ int chance, minfail;
+
+
+ /* Extract the base spell failure rate */
+ chance = rspell->level + 25;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_THAUMATURGY) - rspell->level);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (rspell->mana > p_ptr->csp)
+ {
+ chance += 5 * (rspell->mana - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+
+
+
+/*
+ * Print a batch of spells.
+ */
+static void print_spell_batch(int batch, int max)
+{
+ char buff[80];
+
+ random_spell* rspell;
+
+ int i;
+
+
+ prt(format(" %-30s Lev Fail Mana Damage ", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ rspell = &random_spells[batch * 10 + i];
+
+ if (rspell->untried)
+ {
+ strnfmt(buff, 80, " %c) %-30s (Spell untried) ",
+ I2A(i), rspell->name);
+
+ }
+ else
+ {
+ strnfmt(buff, 80, " %c) %-30s %3d %4d%% %3d %3dd%d ",
+ I2A(i), rspell->name,
+ rspell->level, spell_chance_random(rspell), rspell->mana,
+ rspell->dam_dice, rspell->dam_sides);
+ }
+
+ prt(buff, 2 + i, 20);
+ }
+
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+static random_spell* select_spell_from_batch(int batch, bool quick)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ random_spell* ret;
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ if (spell_num < (batch + 1) * 10)
+ {
+ mut_max = spell_num - batch * 10;
+ }
+
+ strnfmt(tmp, 160, "(a-%c, * to list, A-%cto browse, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1), I2A(mut_max - 1) - 'a' + 'A');
+
+ prt(tmp, 0, 0);
+
+ if (quick)
+ {
+ print_spell_batch(batch, mut_max);
+ }
+
+ while (1)
+ {
+ /* Get a command */
+ which = inkey();
+
+ /* Abort */
+ if (which == ESCAPE)
+ {
+ /* No selection */
+ ret = NULL;
+
+ /* Leave the command loop */
+ break;
+
+ }
+
+ /* List */
+ if (which == '*' || which == '?' || which == ' ')
+ {
+ /* Print power list */
+ print_spell_batch(batch, mut_max);
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Accept default */
+ if (which == '\r')
+ {
+ /* There are no other choices */
+ if (mut_max == 1)
+ {
+ ret = &random_spells[batch * 10];
+
+ /* Leave the command loop */
+ break;
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Rename */
+ if (which == '/')
+ {
+ prt("Rename which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, random_spells[batch*10 + A2I(which)].name);
+ if (get_string("Name this power: ", out_val, 29))
+ {
+ strcpy(random_spells[batch*10 + A2I(which)].name, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Comment */
+ if (which == '-')
+ {
+ prt("Comment which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, random_spells[batch*10 + A2I(which)].desc);
+ if (get_string("Comment this power: ", out_val, 29))
+ {
+ strcpy(random_spells[batch*10 + A2I(which)].desc, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ if (isalpha(which) && isupper(which))
+ {
+ which = tolower(which);
+ c_prt(TERM_L_BLUE, format("%s : %s", random_spells[batch*10 + A2I(which)].name, random_spells[batch*10 + A2I(which)].desc), 0, 0);
+ inkey();
+ prt(tmp, 0, 0);
+ continue;
+ }
+ else if (isalpha(which) && (A2I(which) < mut_max))
+ {
+ /* Pick the power */
+ ret = &random_spells[batch * 10 + A2I(which)];
+
+ /* Leave the command loop */
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ /* Return selection */
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+random_spell* select_spell(bool quick)
+{
+ char tmp[160];
+
+ char which;
+
+ int batch_max = (spell_num - 1) / 10;
+
+ random_spell *ret;
+
+
+ /* Too confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You can't use your powers while confused!");
+ return NULL;
+ }
+
+ /* No spells available */
+ if (spell_num == 0)
+ {
+ msg_print("There are no spells you can cast.");
+ return NULL;
+ }
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max));
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = NULL;
+
+ break;
+ }
+
+ if (which == '\r')
+ {
+ if (batch_max == 0)
+ {
+ ret = select_spell_from_batch(0, quick);
+
+ break;
+ }
+
+ continue;
+ }
+
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ ret = select_spell_from_batch(A2I(which), quick);
+
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+void do_cmd_powermage(void)
+{
+ random_spell *s_ptr;
+
+ u32b proj_flags;
+
+ int dir, chance;
+
+ int ty = 0, tx = 0;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ s_ptr = select_spell(FALSE);
+
+ if (s_ptr == NULL) return;
+
+ if (p_ptr->csp < s_ptr->mana)
+ {
+ msg_print("You do not have enough mana.");
+ return;
+ }
+
+ /* Spell failure chance */
+ chance = spell_chance_random(s_ptr);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ /* Let time pass */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Mana is spent anyway */
+ p_ptr->csp -= s_ptr->mana;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+
+ return;
+ }
+
+
+ p_ptr->csp -= s_ptr->mana;
+
+ s_ptr->untried = FALSE;
+ proj_flags = s_ptr->proj_flags;
+
+ /* Hack -- Spell needs a target */
+ if ((s_ptr->proj_flags & PROJECT_BEAM) ||
+ (s_ptr->proj_flags & PROJECT_STOP))
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+
+ /* Mega-Hack -- Beam spells should continue through
+ * the target; bolt spells should stop at the
+ * target. --dsb */
+ if (s_ptr->proj_flags & PROJECT_BEAM)
+ proj_flags |= PROJECT_THRU;
+ }
+ else
+ {
+ /* Use the given direction */
+ ty = p_ptr->py + ddy[dir];
+ tx = p_ptr->px + ddx[dir];
+
+ /* Mega-Hack -- Both beam and bolt spells should
+ * continue through this fake target. --dsb */
+ proj_flags |= PROJECT_THRU;
+ }
+ }
+
+ if (s_ptr->proj_flags & PROJECT_BLAST)
+ {
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (s_ptr->proj_flags & PROJECT_VIEWABLE)
+ {
+ project_hack(s_ptr->GF, damroll(s_ptr->dam_dice, s_ptr->dam_sides));
+ }
+ else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER)
+ {
+ project_meteor(s_ptr->radius, s_ptr->GF,
+ damroll(s_ptr->dam_dice, s_ptr->dam_sides),
+ s_ptr->proj_flags);
+ }
+ else
+ {
+ project(0, s_ptr->radius, ty, tx,
+ damroll(s_ptr->dam_dice, s_ptr->dam_sides),
+ s_ptr->GF, proj_flags);
+ }
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+
+#if 0
+
+/*
+ * Incremental sleep spell -KMW-
+ */
+static void do_sleep_monster(void)
+{
+ int dir;
+
+ if (p_ptr->lev < 15)
+ {
+ if (!get_aim_dir(&dir)) return;
+ sleep_monster(dir);
+ }
+ else if (p_ptr->lev < 30)
+ {
+ sleep_monsters_touch();
+ }
+ else
+ {
+ sleep_monsters();
+ }
+}
+
+#endif /* 0 */
+
+
+#if 0
+
+/*
+ * Multiple Monster Fear -KMW-
+ */
+static bool fear_monsters(void)
+{
+ return (project_hack(GF_TURN_ALL, p_ptr->lev));
+}
+
+
+/*
+ * Close to Player Monster Fear -KMW-
+ */
+static bool fear_monsters_touch(void)
+{
+ int flg = PROJECT_KILL | PROJECT_HIDE;
+
+ return (project(0, 1, p_ptr->py, p_ptr->px, p_ptr->lev,
+ GF_TURN_ALL, flg));
+}
+
+
+/*
+ * Incremental fear spell -KMW-
+ */
+static void do_fear_monster(void)
+{
+ int dir;
+
+ if (p_ptr->lev < 15)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fear_monster(dir, p_ptr->lev);
+ }
+ else if (p_ptr->lev < 30)
+ {
+ fear_monsters_touch();
+ }
+ else
+ {
+ fear_monsters();
+ }
+}
+
+#endif /* 0 */
+
+
+/*
+ * Brand some ammunition. Used by Cubragol and a mage spell. The spell was
+ * moved here from cmd6.c where it used to be for Cubragol only. I've also
+ * expanded it to do either frost, fire or venom, at random. -GJW -KMW-
+ */
+void brand_ammo(int brand_type, int bolts_only)
+{
+ int a;
+
+ int allowable;
+
+
+ if (bolts_only)
+ {
+ allowable = TV_BOLT;
+ }
+ else
+ {
+ allowable = TV_BOLT | TV_ARROW | TV_SHOT;
+ }
+
+ for (a = 0; a < INVEN_PACK; a++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+
+ if (bolts_only && (o_ptr->tval != TV_BOLT)) continue;
+
+ if (!bolts_only && (o_ptr->tval != TV_BOLT) &&
+ (o_ptr->tval != TV_ARROW) && (o_ptr->tval != TV_SHOT))
+ continue;
+
+ if (!artifact_p(o_ptr) && !ego_item_p(o_ptr) &&
+ !cursed_p(o_ptr))
+ break;
+ }
+
+ /* Enchant the ammo (or fail) */
+ if ((a < INVEN_PACK) && (rand_int(100) < 50))
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+ char *ammo_name;
+ char *aura_name;
+ char msg[48];
+ int aura_type, r;
+
+ /* fire only */
+ if (brand_type == 1) r = 0;
+
+ /* cold only */
+ else if (brand_type == 2) r = 99;
+
+ /* No bias */
+ else r = rand_int(100);
+
+ if (r < 50)
+ {
+ aura_name = "fiery";
+ aura_type = EGO_FLAME;
+ }
+ else
+ {
+ aura_name = "frosty";
+ aura_type = EGO_FROST;
+ }
+
+ if (o_ptr->tval == TV_BOLT)
+ {
+ ammo_name = "bolts";
+ }
+ else if (o_ptr->tval == TV_ARROW)
+ {
+ ammo_name = "arrows";
+ }
+ else
+ {
+ ammo_name = "shots";
+ }
+
+ strnfmt(msg, 48, "Your %s are covered in a %s aura!",
+ ammo_name, aura_name);
+ msg_print(msg);
+
+ o_ptr->name2 = aura_type;
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ o_ptr->discount = 100;
+
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+ }
+ else
+ {
+ if (flush_failure) flush();
+ msg_print("The enchantment failed.");
+ }
+}
+
+
+/*
+ * From Kamband by Ivan Tkatchev
+ */
+void summon_monster(int sumtype)
+{
+ /* Take a turn */
+ energy_use = 100;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("This place seems devoid of life.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level + randint(5), sumtype, TRUE))
+ {
+ msg_print("You summon some help.");
+ }
+ else
+ {
+ msg_print("You called, but no help came.");
+ }
+}
+
+
+
+/*
+ * Use a class power of Possessor
+ */
+void do_cmd_possessor()
+{
+ char ch, ext;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ while (TRUE)
+ {
+ if (!get_com("Use your [R]ace powers or your [I]ncarnating powers?", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'I') || (ch == 'i'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ if (ext == 1)
+ {
+ bool use_great = FALSE;
+
+ if (p_ptr->disembodied)
+ {
+ msg_print("You don't currently own a body to use.");
+ return;
+ }
+
+ /* Do we have access to all the powers ? */
+ if (get_skill_scale(SKILL_POSSESSION, 100) >= r_info[p_ptr->body_monster].level)
+ use_great = TRUE;
+
+ use_symbiotic_power(p_ptr->body_monster, use_great, FALSE, FALSE);
+
+ if (p_ptr->csp < 0)
+ {
+ msg_print("You lose control of your body!");
+ if (!do_cmd_leave_body(FALSE))
+ {
+ cmsg_print(TERM_VIOLET,
+ "You are forced back into your body by your cursed items, "
+ "you suffer a system shock!");
+
+ p_ptr->chp = 1;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+ }
+ }
+ }
+ else if (ext == 2)
+ {
+ if (p_ptr->disembodied)
+ {
+ do_cmd_integrate_body();
+ }
+ else
+ {
+ do_cmd_leave_body(TRUE);
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+}
+
+
+/*
+ * Hook to determine if an object is contertible in an arrow/bolt
+ */
+static bool item_tester_hook_convertible(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_JUNK) || (o_ptr->tval == TV_SKELETON)) return TRUE;
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'archer'.
+ */
+void do_cmd_archer(void)
+{
+ int ext = 0;
+ char ch;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char com[80];
+
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ if (p_ptr->blind)
+ {
+ msg_print("You are blind!");
+ return;
+ }
+
+
+ if (get_skill(SKILL_ARCHERY) >= 20)
+ {
+ strnfmt(com, 80, "Create [S]hots, [A]rrows or [B]olts? ");
+ }
+ else if (get_skill(SKILL_ARCHERY) >= 10)
+ {
+ strnfmt(com, 80, "Create [S]hots or [A]rrows? ");
+ }
+ else
+ {
+ strnfmt(com, 80, "Create [S]hots? ");
+ }
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'S') || (ch == 's'))
+ {
+ ext = 1;
+ break;
+ }
+ if (((ch == 'A') || (ch == 'a')) && (get_skill(SKILL_ARCHERY) >= 10))
+ {
+ ext = 2;
+ break;
+ }
+ if (((ch == 'B') || (ch == 'b')) && (get_skill(SKILL_ARCHERY) >= 20))
+ {
+ ext = 3;
+ break;
+ }
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /**********Create shots*********/
+ if (ext == 1)
+ {
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_SHOT, m_bonus(2, dun_level)));
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some ammo.");
+
+ (void)wall_to_mud(dir);
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /**********Create arrows*********/
+ else if (ext == 2)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_convertible;
+
+ /* Get an item */
+ q = "Convert which item? ";
+ s = "You have no item to convert.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ q_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ q_ptr = &o_list[0 - item];
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some arrows */
+ object_prep(q_ptr, lookup_kind(TV_ARROW, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /**********Create bolts*********/
+ else if (ext == 3)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_convertible;
+
+ /* Get an item */
+ q = "Convert which item? ";
+ s = "You have no item to convert.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ q_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ q_ptr = &o_list[0 - item];
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some bolts */
+ object_prep(q_ptr, lookup_kind(TV_BOLT, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+}
+
+/*
+ * Control whether shots are allowed to pierce
+ */
+void do_cmd_set_piercing(void)
+{
+ int ext = 0;
+ char ch;
+ char com[80];
+
+ strnfmt(com, 80, "Allow shots to pierce? ");
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'Y') || (ch == 'y'))
+ {
+ p_ptr->use_piercing_shots = 1;
+ break;
+ }
+ if ((ch == 'N') || (ch == 'n'))
+ {
+ p_ptr->use_piercing_shots = 0;
+ break;
+ }
+ }
+}
+/*
+ * Helper function to describe necro powers
+ */
+void necro_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_NECROMANCY);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 0:
+ {
+ if (p_ptr->to_s)
+ strnfmt(p, 80, " power %dd%d+%d", 2 + (plev * 2 / 3), 4, (p_ptr->to_s * 2));
+ else
+ strnfmt(p, 80, " power %dd%d", 2 + (plev * 2 / 3), 4);
+ break;
+ }
+ case 2:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 100 + (plev * 4), 200 + (plev * 3));
+ break;
+ }
+ case 3:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 30 + (plev * 2), 50 + plev);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a Necromancy spell
+ */
+void do_cmd_necromancer(void)
+{
+ int n = 0, b = 0;
+ int chance;
+ int dir;
+ int minfail = 0;
+ int plev = get_skill(SKILL_NECROMANCY);
+ magic_power spell;
+ int to_s2 = p_ptr->to_s / 2;
+ int mto_s2 = p_ptr->to_s / 2;
+
+
+ if (mto_s2 == 0) mto_s2 = 1;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, necro_powers, MAX_NECRO_POWERS, necro_info,
+ get_skill(SKILL_NECROMANCY), A_CON)) return;
+
+ spell = necro_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_CON]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_CON]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 10)
+ {
+ msg_print("Oh, no! You become undead!");
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = 2 * plev;
+ msg_format("You have to kill %d monster%s to be brought back to life.",
+ p_ptr->necro_extra2,
+ (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce maximum */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else if (b < 40)
+ {
+ msg_print("Suddenly you feel that you're in a bad situation...");
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type],
+ (plev >= 30) ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD);
+ }
+ else
+ {
+ msg_print("Your body is damaged by the horrible forces of the spell!");
+ take_hit(damroll(5, plev), "using necromancy unwisely");
+ }
+ }
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Horrify */
+ case 0:
+ {
+ int dam = damroll(2 + (plev * 2 / 3), 4) + (p_ptr->to_s * 2);
+
+ if (plev > 45)
+ {
+ project_hack(GF_STUN, dam);
+ project_hack(GF_TURN_ALL, dam);
+ }
+ else if (plev > 35)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_STUN, dir, dam, 3 + (plev / 10));
+ fire_ball(GF_TURN_ALL, dir, dam, 3 + (plev / 10));
+ }
+ else if (plev > 20)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_beam(GF_STUN, dir, dam);
+ fire_beam(GF_TURN_ALL, dir, dam);
+ }
+ else
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_STUN, dir, dam);
+ fire_bolt(GF_TURN_ALL, dir, dam);
+ }
+
+ break;
+ }
+
+ /* Raise Death */
+ case 1:
+ {
+ fire_ball(GF_RAISE, 0, plev * 3, 1 + to_s2 + (plev / 10));
+
+ break;
+ }
+
+ /* Conjures temporary weapon */
+ case 2:
+ {
+ int dur = randint(100 + (plev * 4)) + 200 + (plev * 3);
+ object_type forge, *o_ptr = &forge;
+ int k_idx = test_item_name("& Necromantic Teeth~");
+
+ k_allow_special[k_idx] = TRUE;
+
+ object_prep(o_ptr, k_idx);
+ apply_magic(o_ptr, plev * 2, TRUE, TRUE, TRUE);
+
+ o_ptr->art_flags5 |= TR5_TEMPORARY;
+ o_ptr->timeout = dur;
+
+ /* These objects are "storebought" */
+ o_ptr->ident |= IDENT_MENTAL;
+ o_ptr->number = 1;
+
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ (void)inven_carry(o_ptr, FALSE);
+
+ k_allow_special[k_idx] = FALSE;
+
+ break;
+ }
+
+ /* Absorb souls */
+ case 3:
+ {
+ set_absorb_soul(randint(30 + (plev * 2)) + 50 + plev);
+ break;
+ }
+
+ /* Vampirism */
+ case 4:
+ {
+ int i;
+ if (!get_aim_dir(&dir)) return;
+ for (i = 0; i < 1 + to_s2 + (plev / 15); i++)
+ {
+ if (drain_life(dir, 100))
+ hp_player(100);
+ }
+
+ break;
+ }
+
+ /* Death */
+ case 5:
+ {
+ if (get_check("Using the Death word will leave you undead, with 1 DP. Do you *really* want to use it? "))
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_DEATH, dir, 1);
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = plev + (rand_int(plev / 2) - (plev / 4));
+ msg_format("You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce 1 DP */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage CON (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your body!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_CON, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/* Runecrafters -- Move this into variable.c XXX XXX XXX */
+static s32b rune_combine = 0;
+
+/*
+ * Hook to determine if an object is "runestone"
+ */
+static bool item_tester_hook_runestone(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval != RUNE_STONE) return (FALSE);
+
+ if (o_ptr->pval != 0) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+static bool item_tester_hook_runestone_full(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval != RUNE_STONE) return (FALSE);
+
+ if (o_ptr->pval == 0) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is "rune-able"
+ */
+static bool item_tester_hook_runeable1(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE1) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is "rune-able"
+ */
+static bool item_tester_hook_runeable2(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval == RUNE_STONE) return (FALSE);
+
+ if (rune_combine & BIT(o_ptr->sval)) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * math.h(sqrt) is banned of angband so ... :)
+ */
+s32b sroot(s32b n)
+{
+ s32b i = n / 2;
+
+ if (n < 2) return (n);
+
+ while (1)
+ {
+ s32b err = (i - n / (i + 1)) / 2;
+
+ if (!err) break;
+
+ i -= err;
+ }
+
+ return ((n / i < i) ? (i - 1) : i);
+}
+
+
+/*
+ * Damage formula, for runes
+ */
+void rune_calc_power(s32b *power, s32b *powerdiv)
+{
+ /* Not too weak power(paranoia) */
+ *power = (*power < 1) ? 1 : *power;
+ *power += 3;
+
+ *power = 37 * sroot(*power) / 10;
+
+ /* To reduce the high level power, while increasing the low levels */
+ *powerdiv = *power / 3;
+ if (*powerdiv < 1) *powerdiv = 1;
+
+ /* Use the spell multiplicator */
+ *power *= (p_ptr->to_s / 2) ? (p_ptr->to_s / 2) : 1;
+}
+
+
+/*
+ * Return percentage chance of runespell failure.
+ */
+int spell_chance_rune(rune_spell* spell)
+{
+ int chance, minfail;
+
+ s32b power = spell->mana, power_rune = 0, powerdiv = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ chance = (5 * power_rune) + (power);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+
+/*
+ * Combine the Runes
+ */
+int rune_exec(rune_spell *spell, int cost)
+{
+ int dir, power_rune = 0, mana_used, plev = get_skill(SKILL_RUNECRAFT);
+
+ int chance;
+
+ s32b power, powerdiv;
+
+ int rad = 0, ty = -1, tx = -1, dam = 0, flg = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+
+ power = spell->mana;
+
+ if (cost && ((power * cost / 100) > p_ptr->csp - (power_rune * (plev / 5))))
+ {
+ power = p_ptr->csp - (power_rune * (plev / 5));
+ mana_used = power + (power_rune * (plev / 5));
+ }
+ else
+ {
+ mana_used = (power * cost / 100) + (power_rune * (plev / 5));
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ dam = damroll(powerdiv, power);
+
+ if (wizard) msg_format("Rune %dd%d = dam %d", powerdiv, power, dam);
+
+ /* Extract the base spell failure rate */
+ chance = spell_chance_rune(spell);
+
+ /* Failure ? */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+ return (mana_used);
+ }
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ flg |= (PROJECT_VIEWABLE);
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_ITEM);
+ flg |= (PROJECT_GRID);
+ flg |= (PROJECT_METEOR_SHOWER);
+ rad = (power / 8 == 0) ? 1 : power / 8;
+ rad = (rad > 10) ? 10 : rad;
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_ITEM);
+ flg |= (PROJECT_GRID);
+ rad = (power / 8 == 0) ? 1 : power / 8;
+ rad = (rad > 10) ? 10 : rad;
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_RAY)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_BEAM);
+ ty = -1;
+ tx = -1;
+ }
+ if (spell->rune2 & RUNE_ARROW)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_STOP);
+ flg |= (PROJECT_KILL);
+ ty = -1;
+ tx = -1;
+ }
+ if (spell->rune2 & RUNE_SELF)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_STOP);
+ flg |= (PROJECT_KILL);
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ unsafe = TRUE;
+ }
+
+ if ((ty == -1) && (tx == -1))
+ {
+ if (!get_aim_dir(&dir)) return (mana_used);
+
+ /* Use the given direction */
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ }
+
+ if (flg & PROJECT_VIEWABLE)
+ {
+ project_hack(spell->type, dam);
+ }
+ else if (flg & PROJECT_METEOR_SHOWER)
+ {
+ project_meteor(rad, spell->type, dam, flg);
+ }
+ else project(0, rad, ty, tx, dam, spell->type, flg);
+
+ if (unsafe) unsafe = FALSE;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+
+ return (mana_used);
+}
+
+
+/*
+ * Test if all runes needed at in the player p_ptr->inventory
+ */
+bool test_runespell(rune_spell *spell)
+{
+ int i;
+
+ object_type *o_ptr;
+
+ bool typeok = FALSE;
+
+ int rune2 = 0;
+
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ /* Does the rune1(type) match ? */
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == spell->type))
+ {
+ typeok = TRUE;
+ }
+
+ if ((o_ptr->tval == TV_RUNE2) && (o_ptr->sval != RUNE_STONE))
+ {
+ /* Add it to the list */
+ rune2 |= 1 << o_ptr->sval;
+ }
+ }
+
+ /* Need all runes to be present */
+ return (typeok && ((rune2 & spell->rune2) == spell->rune2));
+}
+
+
+/*
+ * Ask for rune, rune2 and mana
+ */
+bool get_runespell(rune_spell *spell)
+{
+ int item, power_rune = 0, rune2 = 0, plev = get_skill(SKILL_RUNECRAFT);
+
+ s32b power;
+
+ int type = 0;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ bool OK = FALSE;
+
+
+ rune_combine = 0;
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runeable1;
+
+ /* Get an item */
+ q = "Use which rune? ";
+ s = "You have no rune to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return FALSE;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ type = o_ptr->sval;
+
+ while (1)
+ {
+ /* Restrict choices to unused secondary runes */
+ item_tester_hook = item_tester_hook_runeable2;
+
+ OK = !get_item(&item, q, s, (USE_INVEN | USE_FLOOR));
+
+ if (OK) break;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ rune_combine |= 1 << o_ptr->sval;
+ rune2 |= 1 << o_ptr->sval;
+ }
+
+ if (!rune2)
+ {
+ msg_print("You have not selected a second rune!");
+ return (FALSE);
+ }
+
+ power = get_quantity("Which amount of Mana?",
+ p_ptr->csp - (power_rune * (plev / 5)));
+ if (power < 1) power = 1;
+
+ spell->mana = power;
+ spell->type = type;
+ spell->rune2 = rune2;
+
+ return (TRUE);
+}
+
+
+void do_cmd_rune(void)
+{
+ rune_spell spell;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ if (!get_runespell(&spell)) return;
+
+ /* Execute at normal mana cost */
+ p_ptr->csp -= rune_exec(&spell, 100);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Print a batch of runespells.
+ */
+static void print_runespell_batch(int batch, int max)
+{
+ char buff[80];
+
+ rune_spell* spell;
+
+ int i;
+
+ s32b power, powerdiv;
+
+ int p, dp;
+
+
+ prt(format(" %-30s Fail Mana Power", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ spell = &rune_spells[batch * 10 + i];
+
+ power = spell->mana;
+ rune_calc_power(&power, &powerdiv);
+ p = power;
+ dp = powerdiv;
+
+ strnfmt(buff, 80, " %c) %-30s %4d%% %4d %dd%d ", I2A(i), spell->name,
+ spell_chance_rune(spell), spell->mana, dp, p);
+
+ prt(buff, 2 + i, 20);
+ }
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+
+static rune_spell* select_runespell_from_batch(int batch, bool quick,
+ int *s_idx)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ rune_spell* ret;
+
+
+ character_icky = TRUE;
+ Term_save();
+
+ if (rune_num < (batch + 1) * 10)
+ {
+ mut_max = rune_num - batch * 10;
+ }
+
+ strnfmt(tmp, 160, "(a-%c, * to list, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1));
+
+ prt(tmp, 0, 0);
+
+ if (quick)
+ {
+ print_runespell_batch(batch, mut_max);
+ }
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ *s_idx = -1;
+ ret = NULL;
+ break;
+ }
+ else if ((which == '*') || (which == '?') || (which == ' '))
+ {
+ print_runespell_batch(batch, mut_max);
+ }
+ else if ((which == '\r') && (mut_max == 1))
+ {
+ *s_idx = batch * 10;
+ ret = &rune_spells[batch * 10];
+ break;
+ }
+ else if (which == '/')
+ {
+ prt("Rename which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, rune_spells[batch*10 + A2I(which)].name);
+ if (get_string("Name this power: ", out_val, 29))
+ {
+ strcpy(rune_spells[batch*10 + A2I(which)].name, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+ }
+ else
+ {
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) < mut_max))
+ {
+ *s_idx = batch * 10 + A2I(which);
+ ret = &rune_spells[batch * 10 + A2I(which)];
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+ }
+
+ Term_load();
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+
+rune_spell* select_runespell(bool quick, int *s_idx)
+{
+ char tmp[160];
+
+ char which;
+
+ int batch_max = (rune_num - 1) / 10;
+
+ if (rune_num == 0)
+ {
+ msg_print("There are no runespells you can cast.");
+ return (NULL);
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max));
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (NULL);
+ }
+ else if ((which == '\r') && (batch_max == 0))
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (select_runespell_from_batch(0, quick, s_idx));
+
+ }
+ else
+ {
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (select_runespell_from_batch(A2I(which), quick, s_idx));
+ }
+ else
+ {
+ bell();
+ }
+ }
+ }
+}
+
+
+/*
+ * Cast a memorized runespell
+ * Note that the only limits are antimagic & conf, NOT blind
+ */
+void do_cmd_rune_cast()
+{
+ rune_spell *s_ptr;
+
+ int s_idx;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ s_ptr = select_runespell(FALSE, &s_idx);
+
+ if (s_ptr == NULL) return;
+
+ /* Need the runes */
+ if (!test_runespell(s_ptr))
+ {
+ msg_print("You lack some essential rune(s) for this runespell!");
+ return;
+ }
+
+ /* Execute at normal mana cost */
+ p_ptr->csp -= rune_exec(s_ptr, 100);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Cast a runespell from a carved runestone
+ */
+void do_cmd_runestone()
+{
+ rune_spell s_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ int item;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runestone_full;
+
+ /* Get an item */
+ q = "Cast from which runestone? ";
+ s = "You have no runestone to cast from.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ s_ptr.type = o_ptr->pval;
+ s_ptr.rune2 = o_ptr->pval2;
+ s_ptr.mana = o_ptr->pval3;
+
+ /* Execute less mana */
+ p_ptr->csp -= rune_exec(&s_ptr, 75);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Add a runespell to the list
+ */
+void do_cmd_rune_add_mem()
+{
+ rune_spell s_ptr;
+
+ rune_spell *ds_ptr = &rune_spells[rune_num];
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ if (rune_num >= MAX_RUNES)
+ {
+ msg_print("You have already learn the maximun number of runespells!");
+ return;
+ }
+
+ if (!get_runespell(&s_ptr)) return;
+
+ ds_ptr->type = s_ptr.type;
+ ds_ptr->rune2 = s_ptr.rune2;
+ ds_ptr->mana = s_ptr.mana;
+ strcpy(ds_ptr->name, "Unnamed Runespell");
+
+ get_string("Name this runespell: ", ds_ptr->name, 29);
+
+ rune_num++;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Carve a runespell onto a Runestone
+ */
+void do_cmd_rune_carve()
+{
+ rune_spell s_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ int item, i;
+
+ char out_val[80];
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ if (!get_check("Beware, this will destroy the involved runes, continue?"))
+ {
+ return;
+ }
+
+ if (!get_runespell(&s_ptr)) return;
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runestone;
+
+ /* Get an item */
+ q = "Use which runestone? ";
+ s = "You have no runestone to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ o_ptr->pval = s_ptr.type;
+ o_ptr->pval2 = s_ptr.rune2;
+ o_ptr->pval3 = s_ptr.mana;
+
+ /* Start with nothing */
+ strcpy(out_val, "");
+
+ /* Use old inscription */
+ if (o_ptr->note)
+ {
+ /* Start with the old inscription */
+ strcpy(out_val, quark_str(o_ptr->note));
+ }
+
+ /* Get a new inscription (possibly empty) */
+ if (get_string("Name this runestone: ", out_val, 80))
+ {
+ /* Save the inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Delete the runes */
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx)
+ {
+ bool do_del = FALSE;
+
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == s_ptr.type)) do_del = TRUE;
+ if ((o_ptr->tval == TV_RUNE2) && (BIT(o_ptr->sval) & s_ptr.rune2)) do_del = TRUE;
+
+ if (do_del)
+ {
+ inven_item_increase(i, -1);
+ inven_item_optimize(i);
+ }
+ }
+ }
+
+ /* Take a turn -- Carving takes a LONG time */
+ energy_use = 400;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Remove a runespell
+ */
+void do_cmd_rune_del()
+{
+ rune_spell *s_ptr;
+
+ int s_idx;
+
+ int i;
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ s_ptr = select_runespell(FALSE, &s_idx);
+
+ if (s_ptr == NULL) return;
+
+ /* Delete and move */
+ for (i = s_idx + 1; i < rune_num; i++)
+ {
+ rune_spells[i - 1].type = rune_spells[i].type;
+ rune_spells[i - 1].rune2 = rune_spells[i].rune2;
+ rune_spells[i - 1].mana = rune_spells[i].mana;
+ strcpy(rune_spells[i - 1].name, rune_spells[i].name);
+ }
+ rune_num--;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+void do_cmd_rune_add()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Add to [M]emory(need runes to cast) or "
+ "Carve a [R]unestone(less mana to cast)", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'M') || (ch == 'm'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Create a Spell in memory */
+ case 1:
+ {
+ do_cmd_rune_add_mem();
+ break;
+ }
+
+ /* Carve a Runestone */
+ case 2:
+ {
+ do_cmd_rune_carve();
+ break;
+ }
+ }
+}
+
+
+void do_cmd_runecrafter()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Rune Spell:[C]reate, [D]elete, C[a]st, D[i]rectly Cast "
+ "or Use [R]unestone", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'C') || (ch == 'c'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'D') || (ch == 'd'))
+ {
+ ext = 2;
+ break;
+ }
+ if ((ch == 'A') || (ch == 'a'))
+ {
+ ext = 3;
+ break;
+ }
+ if ((ch == 'I') || (ch == 'i'))
+ {
+ ext = 4;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 5;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Create a Spell */
+ case 1:
+ {
+ do_cmd_rune_add();
+ break;
+ }
+
+ /* Delete a Spell */
+ case 2:
+ {
+ do_cmd_rune_del();
+ break;
+ }
+
+ /* Cast a Spell */
+ case 3:
+ {
+ do_cmd_rune_cast();
+ break;
+ }
+
+ /* Directly Cast a Spell */
+ case 4:
+ {
+ do_cmd_rune();
+ break;
+ }
+
+ /* Cast a Runestone */
+ case 5:
+ {
+ do_cmd_runestone();
+ break;
+ }
+ }
+}
+
+
+void do_cmd_unbeliever_antimagic()
+{
+ if (get_skill(SKILL_ANTIMAGIC) < 20)
+ {
+ msg_print("You must have at least a level 20 antimagic skill "
+ "to be able to disrupt the magic continuum.");
+ return;
+ }
+
+ if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC)
+ {
+ p_ptr->antimagic_extra &= ~CLASS_ANTIMAGIC;
+ msg_print("You stop disrupting the magic continuum.");
+ }
+ else
+ {
+ p_ptr->antimagic_extra |= CLASS_ANTIMAGIC;
+ msg_print("You start disrupting the magic continuum.");
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Detect traps + kill traps
+ */
+void do_cmd_unbeliever()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Disrupt [C]ontinuum or [D]etect Traps", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'C') || (ch == 'c'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'D') || (ch == 'd'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Disrupt Continuum */
+ case 1:
+ {
+ do_cmd_unbeliever_antimagic();
+ break;
+ }
+
+ /* Detect Traps */
+ case 2:
+ {
+ s16b skill = get_skill(SKILL_ANTIMAGIC);
+
+ if (skill < 25)
+ {
+ msg_print("You cannot use your detection abilities yet.");
+ break;
+ }
+
+ detect_traps(DEFAULT_RADIUS);
+
+ if (skill >= 35) destroy_doors_touch();
+
+ break;
+ }
+ }
+}
+
+/*
+ * Hook to determine if an object is totemable
+ */
+static bool item_tester_hook_totemable(object_type *o_ptr)
+{
+ /* Only full corpse */
+ if ((o_ptr->tval == TV_CORPSE) &&
+ ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON)))
+ {
+ return (TRUE);
+ }
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Summoners
+ */
+void do_cmd_summoner_extract()
+{
+ object_type *o_ptr, forge, *q_ptr;
+
+ cptr q, s;
+
+ int item, r, e;
+
+ bool partial;
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ item_tester_hook = item_tester_hook_totemable;
+
+ /* Get an item */
+ q = "Use which corpse? ";
+ s = "You have no corpse to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ if (r_info[o_ptr->pval2].flags1 & RF1_UNIQUE)
+ {
+ partial = FALSE;
+ }
+ else
+ {
+ partial = get_check("Do you want to create a partial totem?");
+ }
+
+ r = o_ptr->pval2;
+ e = o_ptr->pval3;
+
+ if (item > 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase( -item, -1);
+ floor_item_describe( -item);
+ floor_item_optimize( -item);
+ }
+
+ if (magik(r_info[o_ptr->pval2].level - get_skill(SKILL_SUMMON)))
+ {
+ msg_print("You failed to extract a totem.");
+ energy_use += 100;
+ return;
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /* Create the object */
+ object_prep(q_ptr, lookup_kind(TV_TOTEM, partial ? 1 : 2));
+ q_ptr->pval = r;
+ q_ptr->pval2 = 0;
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You extract a totem from the dead corpse.");
+ energy_use += 100;
+}
+
+
+void summon_true(int r_idx, int item)
+{
+ int i, status, x = 1, y = 1, rx, ry = 0, chance;
+
+ bool used;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+
+ /* Uniques are less likely to be nice */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Because it's unique, it will always be destroyed */
+ used = TRUE;
+
+ /* About twice as hard as non-uniques */
+ chance = (get_skill(SKILL_SUMMON) * 70 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Non-uniques are easier to handle */
+ else
+ {
+ if (get_skill(SKILL_SUMMON) == 0)
+ {
+ used = TRUE;
+ }
+ else
+ {
+ /* It can be used multiple times */
+ used = FALSE;
+
+ /* But it is not 100% sure (note: skill > 0) */
+ chance = (r_ptr->level * 25 / get_skill(SKILL_SUMMON));
+ if (magik(chance)) used = TRUE;
+ }
+
+ chance = (get_skill(SKILL_SUMMON) * 130 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ if (!(i = place_monster_one (y, x, r_idx, 0, 0, status)))
+ {
+ msg_print("The summoning fails.");
+ }
+ else
+ {
+ m_list[i].status = status;
+ m_list[i].mflag |= MFLAG_NO_DROP;
+ }
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Destroy the totem if the used flag is set */
+ if (used)
+ {
+ /* Eliminate the totem (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Eliminate the totem (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+
+ /* Done */
+ return;
+}
+
+
+void do_cmd_summoner_summon()
+{
+ int item, x = 1, y = 1, rx, ry, m_idx = 0, i;
+
+ cptr q, s;
+
+ object_type *o_ptr;
+
+ monster_type *m_ptr;
+
+
+ /* Which Totem? */
+ item_tester_tval = TV_TOTEM;
+
+ q = "Summon from which Totem?";
+ s = "There are no totems to summon from!";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Access the item */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* True Totems have their own function. */
+ if (o_ptr->sval == 2)
+ {
+ summon_true(o_ptr->pval, item);
+ return;
+ }
+
+ /* Handle partial totems */
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ place_monster_one_no_drop = TRUE;
+ m_idx = place_monster_one(y, x, o_ptr->pval, 0, 0, MSTATUS_PET);
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Failure. */
+ if (!m_idx)
+ {
+ msg_print("The summoning fails.");
+ }
+
+ /* Mark the monster as a "partial" ally */
+ m_ptr = &m_list[m_idx];
+ m_ptr->mflag |= MFLAG_PARTIAL | MFLAG_NO_DROP;
+}
+
+
+void do_cmd_summoner(void)
+{
+ int ext = 0;
+
+ char ch;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* not if blind */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("[E]xtract a totem, [S]ummon", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'E') || (ch == 'e'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 's') || (ch == 'S'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ case 1:
+ {
+ do_cmd_summoner_extract();
+ break;
+ }
+
+ case 2:
+ {
+ do_cmd_summoner_summon();
+ break;
+ }
+ }
+}
+
+
+/*
+ * Fighters may invoke The Rush.
+ */
+void do_cmd_blade(void)
+{
+ /* Are we already Rushed? */
+ if (p_ptr->rush)
+ {
+ msg_format("You have %d turns of The Rush remaining", p_ptr->rush);
+ return;
+ }
+
+ /* Are you sure? */
+ if (!get_check("Are you sure you want to invoke The Rush?")) return;
+
+ /* Let's Rush! */
+ set_rush(2 + p_ptr->lev / 2 + randint(p_ptr->lev / 2));
+}
+
+
+/*
+ * Dodge Chance Feedback.
+ */
+void use_ability_blade(void)
+{
+ int chance = p_ptr->dodge_chance - ((dun_level * 5) / 6);
+
+ if (chance < 0) chance = 0;
+ if (wizard)
+ {
+ msg_format("You have exactly %d chances of dodging a level %d monster.", chance, dun_level);
+ }
+
+ if (chance < 5)
+ {
+ msg_format("You have almost no chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 10)
+ {
+ msg_format("You have a slight chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 20)
+ {
+ msg_format("You have a significant chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 40)
+ {
+ msg_format("You have a large chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 70)
+ {
+ msg_format("You have a high chance of dodging a level %d monster.", dun_level);
+ }
+ else
+ {
+ msg_format("You will usually dodge successfully a level %d monster.", dun_level);
+ }
+
+ return;
+}
+
+/*
+ * Helper function to describe symbiotic powers
+ */
+void symbiotic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_SYMBIOTIC);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 2:
+ {
+ strnfmt(p, 80, " power %d", plev * 3);
+ break;
+ }
+ case 5:
+ {
+ strnfmt(p, 80, " heal %d%%", 15 + get_skill_scale(SKILL_SYMBIOTIC, 35));
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a symbiotic spell
+ */
+void do_cmd_symbiotic(void)
+{
+ int n = 0;
+ int chance;
+ int minfail = 0;
+ int plev = get_skill(SKILL_SYMBIOTIC);
+ magic_power spell;
+
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info,
+ get_skill(SKILL_SYMBIOTIC), A_INT)) return;
+
+ spell = symbiotic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ case 0:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ object_type *q_ptr;
+ object_type forge;
+
+ msg_print("Hypnotise which pet?");
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx)
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ msg_print("You can only hypnotise monsters that cannot move.");
+ }
+ else if (m_ptr->status < MSTATUS_PET)
+ {
+ msg_print("You can only hypnotise pets and companions.");
+ }
+ else if (r_ptr->flags9 & RF9_SPECIAL_GENE)
+ {
+ msg_print("You cannot hypnotise this monster.");
+ }
+ else
+ {
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1));
+ q_ptr->number = 1;
+ q_ptr->pval = m_ptr->r_idx;
+ q_ptr->pval2 = m_ptr->hp;
+ q_ptr->pval3 = m_ptr->maxhp;
+ /* overflow alert */
+ q_ptr->exp = m_ptr->exp;
+ q_ptr->elevel = m_ptr->level;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ drop_near(q_ptr, 0, y, x);
+
+ delete_monster(y, x);
+ health_who = 0;
+ }
+ }
+ else
+ {
+ msg_print("There is no pet here !");
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ monster_type *m_ptr;
+ int m_idx;
+ int item, x, y, d;
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_HYPNOS;
+
+ /* Get an item */
+ q = "Awaken which monster? ";
+ s = "You have no monster to awaken.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return;
+
+ o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d, 0);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ /* Have to be careful here; releasing the symbiote into a
+ * dungeon with leveled monsters will level the symbiote
+ * before we can get hold of it. We'll be nice and use the
+ * larger of the saved exp and the exp that the newly-generated
+ * monster starts with. */
+ m_ptr = &m_list[m_idx];
+ if (m_ptr->exp < o_ptr->exp)
+ {
+ m_ptr->exp = o_ptr->exp;
+ monster_check_experience(m_idx, TRUE);
+ if (m_ptr->level != o_ptr->elevel)
+ cmsg_format(TERM_VIOLET, "ERROR: level-%d HYPNOS becomes level-%d symbiote", o_ptr->elevel, m_ptr->level);
+ }
+ m_ptr->hp = o_ptr->pval2;
+ m_ptr->maxhp = o_ptr->pval3;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ break;
+ }
+
+ /* Charm */
+ case 2:
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return;
+
+ fire_bolt(GF_CHARM_UNMOVING, dir, plev * 3);
+
+ break;
+ }
+
+ /* Life Share */
+ case 3:
+ {
+ s32b percent1, percent2;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ r_ptr = &r_info[o_ptr->pval];
+
+ percent1 = p_ptr->chp;
+ percent1 = (percent1 * 100) / p_ptr->mhp;
+
+ percent2 = o_ptr->pval2;
+ percent2 = (percent2 * 100) / o_ptr->pval3;
+
+ /* Now get the average */
+ percent1 = (percent1 + percent2) / 2;
+
+ /* And set the hp of monster & player to it */
+ p_ptr->chp = (percent1 * p_ptr->mhp) / 100;
+ o_ptr->pval2 = (percent1 * o_ptr->pval3) / 100;
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+
+ break;
+ }
+
+ /* Minor Symbiotic Powers */
+ case 4:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if (0 > use_symbiotic_power(o_ptr->pval, FALSE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Heal Symbiote */
+ case 5:
+ {
+ int hp;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ r_ptr = &r_info[o_ptr->pval];
+ hp = o_ptr->pval3 * (15 + get_skill_scale(SKILL_SYMBIOTIC, 35)) / 100;
+ o_ptr->pval2 += hp;
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ msg_format("%s is healed.", symbiote_name(TRUE));
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+
+ break;
+ }
+
+
+ /* Major Symbiotic Powers */
+ case 6:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if(0 > use_symbiotic_power(o_ptr->pval, TRUE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Summon never-moving pet */
+ case 7:
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE);
+
+ break;
+ }
+
+ /* Force Symbiosis */
+ case 8:
+ {
+ int y, x;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+
+ if (!tgt_pt(&x, &y)) return;
+
+ c_ptr = &cave[y][x];
+
+ if (!c_ptr->m_idx) break;
+
+ m_ptr = &m_list[c_ptr->m_idx];
+ use_symbiotic_power(m_ptr->r_idx, TRUE, FALSE, TRUE);
+
+ break;
+ }
+
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage CON (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your body!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_CHR, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Boulder creation .. sorry :)
+ */
+void do_cmd_create_boulder()
+{
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Granite -- How about other wall types? */
+ if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) ||
+ ((c_ptr->feat >= FEAT_MAGMA_H) && (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ)))
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ (void)wall_to_mud(dir);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_JUNK, SV_BOULDER));
+ q_ptr->number = (byte)rand_range(2, 5);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some boulders.");
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+}
diff --git a/src/cmovie.c b/src/cmovie.c
new file mode 100644
index 00000000..da4dee63
--- /dev/null
+++ b/src/cmovie.c
@@ -0,0 +1,514 @@
+/* File: cmovie.c */
+
+/* Purpose: play cmovie files -DarkGod-Improv- */
+
+#include "angband.h"
+
+/*
+ * Play a given cmovie
+ */
+s16b do_play_cmovie(cptr cmov_file)
+{
+ FILE *fff;
+
+ int y, line = 0, x;
+ int delay;
+
+ char *s;
+
+ char buf[1024];
+ char cbuf[90];
+ char ch;
+
+ char mode = 0;
+
+
+ /* Cmovie files are moved to the user directory on the multiuser systems */
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_CMOV, cmov_file);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Grab permission */
+ /* safe_setuid_grab(); */
+
+ /* Read the file */
+ fff = my_fopen(buf, "r");
+
+ /* Drop permission */
+ /* safe_setuid_drop(); */
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+ /* Save screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ /* Give some usefull info */
+ prt("While viewing the movie you can press Escape to exit, t/Space to switch between", 0, 0);
+ prt("fluid more and step by step mode and any other key to step a frame in step by", 1, 0);
+ prt("step mode.", 2, 0);
+ prt("You can press D to do an html screenshot of the current frame.", 3, 0);
+ prt("You can also use + and - to speed up/down the playing speed.", 5, 0);
+ prt("Press any key when ready.", 8, 0);
+
+ inkey();
+
+ Term_clear();
+
+ line = -1;
+
+ delay = 1;
+
+ /* Init to white */
+ for (x = 0; x < 80; x++)
+ {
+ cbuf[x] = 'w';
+ }
+
+ /* Parse */
+ while (0 == my_fgets(fff, buf, 1024))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+ ch = inkey();
+
+ /* Stop */
+ if (ch == ESCAPE) break;
+
+ /* Change mode */
+ else if (ch == 't')
+ {
+ mode = FALSE;
+ }
+ else if (ch == ' ')
+ {
+ mode = TRUE;
+ }
+
+ /* Change speed */
+ else if (ch == '+')
+ {
+ delay--;
+ if (delay < 0) delay = 0;
+ }
+ else if (ch == '-')
+ {
+ delay++;
+ if (delay > 5) delay = 5;
+ }
+ else if (ch == 'D')
+ {
+ do_cmd_html_dump();
+ }
+
+ line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') break;
+
+ /* Clean screen */
+ if (buf[0] == 'C')
+ {
+ Term_clear();
+
+ /* Next */
+ continue;
+ }
+
+ /* Displays a textbox */
+ if (buf[0] == 'B')
+ {
+ int len = strlen(buf + 2);
+
+ /* Clear the line */
+ Term_erase(0, 0, 255);
+
+ /* Display the message */
+ c_put_str(TERM_VIOLET, "###", 0, 0);
+ c_put_str(TERM_ORANGE, buf + 2, 0, 3);
+ c_put_str(TERM_VIOLET, "###", 0, 3 + len);
+ c_put_str(TERM_WHITE, "(more)", 0, 6 + len);
+
+ /* Next */
+ continue;
+ }
+
+ /* Wait a key */
+ if (buf[0] == 'W')
+ {
+ inkey();
+
+ /* Next */
+ continue;
+ }
+
+ /* Sleep */
+ if (buf[0] == 'S')
+ {
+ long msec;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%ld:", &msec))
+ {
+ return ( -2);
+ }
+
+ if (!mode)
+ {
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ else
+ {
+ bool stop = FALSE;
+
+ while (TRUE)
+ {
+ ch = inkey();
+
+ /* Stop */
+ if (ch == ESCAPE)
+ {
+ stop = TRUE;
+ break;
+ }
+ /* Change mode */
+ else if (ch == 't')
+ {
+ mode = FALSE;
+ break;
+ }
+ /* Change mode */
+ else if (ch == ' ')
+ {
+ if (mode) continue;
+ mode = TRUE;
+ break;
+ }
+ /* Change speed */
+ else if (ch == '+')
+ {
+ delay--;
+ if (delay < 0) delay = 0;
+ }
+ else if (ch == '-')
+ {
+ delay++;
+ if (delay > 5) delay = 5;
+ }
+ else if (ch == 'D')
+ {
+ do_cmd_html_dump();
+ }
+ else break;
+ }
+ if (stop) break;
+ }
+
+ /* Next */
+ continue;
+ }
+
+ /* Get color for the NEXT L line */
+ if (buf[0] == 'E')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return ( -2);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return ( -2);
+
+ /* Get the index */
+ y = atoi(buf + 2);
+
+ C_COPY(cbuf, s, 80, char);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Print a line */
+ if (buf[0] == 'L')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return ( -2);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return ( -2);
+
+ /* Get the index */
+ y = atoi(buf + 2);
+
+ for (x = 0; x < 80; x++)
+ {
+ Term_putch(x, y, color_char_to_attr(cbuf[x]), s[x]);
+
+ /* Reinit to white */
+ cbuf[x] = 'w';
+ }
+ Term_redraw_section(0, y, 79, y);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Update 1 char */
+ if (buf[0] == 'P')
+ {
+ int x, y, a, c;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &x, &y, &c, &a))
+ {
+ a = 'w';
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &x, &y, &c)) return ( -2);
+ }
+
+ Term_putch(x, y, color_char_to_attr(cbuf[x]), c);
+ Term_redraw_section(x, y, x + 1, y + 1);
+
+ /* Next... */
+ continue;
+ }
+ }
+
+ /* Load screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Close */
+ my_fclose(fff);
+
+ return (0);
+}
+
+
+/*
+ * Start the recording of a cmovie
+ */
+void do_record_cmovie(cptr cmovie)
+{
+ char buf[1024];
+ int fd = -1;
+ int y;
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_CMOV, cmovie);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Grab permission */
+ /* safe_setuid_grab(); */
+
+ /* Check for existing file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ /* safe_setuid_drop(); */
+
+ /* Existing file */
+ if (fd >= 0)
+ {
+ char out_val[160];
+
+ /* Close the file */
+ (void)fd_close(fd);
+
+ /* Build query */
+ (void)sprintf(out_val, "Replace existing file %s? ", cmovie);
+
+ /* Ask */
+ if (get_check(out_val)) fd = -1;
+ }
+
+ /* Be sure */
+ if (!get_check("Ready to record(Press ctrl+D to enter a textual note while recording)?")) return;
+
+ /* Grab privs */
+ /* safe_setuid_grab(); */
+
+ /* Open the non-existing file */
+ if (fd < 0) movfile = my_fopen(buf, "w");
+
+ /* And drop them */
+ /* safe_setuid_drop(); */
+
+ /* Invalid file */
+ if (movfile == NULL)
+ {
+ msg_format("Cmovie recording failed!");
+
+ return;
+ }
+
+ /* First thing: Record clear screen then enable the recording */
+ fprintf(movfile, "# Generated by %s %ld.%ld.%ld\n",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
+ fprintf(movfile, "C:\n");
+ last_paused = 0;
+ do_movies = 1;
+ cmovie_init_second();
+
+ /* Mega Hack, get the screen */
+ for (y = 0; y < Term->hgt; y++)
+ {
+ cmovie_record_line(y);
+ }
+}
+
+
+/*
+ * Stop the recording
+ */
+void do_stop_cmovie()
+{
+ if (do_movies == 1)
+ {
+ do_movies = 0;
+ my_fclose(movfile);
+ }
+}
+
+
+/*
+ * Start a cmovie
+ */
+void do_start_cmovie()
+{
+ char name[90], rname[94];
+
+
+ /* Should never happen */
+ if (do_movies == 1) return;
+
+ /* Default */
+ sprintf(name, "%s", player_base);
+
+ if (get_string("Cmovie name: ", name, 80))
+ {
+ if (name[0] && (name[0] != ' '))
+ {
+ sprintf(rname, "%s.cmv", name);
+
+ if (get_check("Record(y), Play(n)?")) do_record_cmovie(rname);
+ else do_play_cmovie(rname);
+ }
+ }
+}
+
+
+void cmovie_clean_line(int y, char *abuf, char *cbuf)
+{
+ const byte *ap = Term->scr->a[y];
+ const char *cp = Term->scr->c[y];
+
+ byte a;
+ char c;
+
+ int x;
+ int wid, hgt;
+ int screen_wid, screen_hgt;
+
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Calculate the size of dungeon map area */
+ screen_wid = wid - (COL_MAP + 1);
+ screen_hgt = hgt - (ROW_MAP + 1);
+
+ /* For the time being, assume 80 column display XXX XXX XXX */
+ for (x = 0; x < wid; x++)
+ {
+ /* Convert dungeon map into default attr/chars */
+ if (!character_icky &&
+ ((x - COL_MAP) >= 0) &&
+ ((x - COL_MAP) < screen_wid) &&
+ ((y - ROW_MAP) >= 0) &&
+ ((y - ROW_MAP) < screen_hgt))
+ {
+ /* Retrieve default attr/char */
+ map_info_default(y + panel_row_prt, x + panel_col_prt, &a, &c);
+
+ abuf[x] = conv_color[a & 0xf];
+
+ if (c == '\0') cbuf[x] = ' ';
+ else cbuf[x] = c;
+ }
+
+ else
+ {
+ abuf[x] = conv_color[ap[x] & 0xf];
+ cbuf[x] = cp[x];
+ }
+ }
+
+ /* Null-terminate the prepared strings */
+ abuf[x] = '\0';
+ cbuf[x] = '\0';
+}
+
+
+/*
+ * Write a record of a screen row into a cmovie file
+ */
+void cmovie_record_line(int y)
+{
+ char abuf[256];
+ char cbuf[256];
+
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Write a colour record */
+ fprintf(movfile, "E:%d:%.80s\n", y, abuf);
+
+ /* Write a char record */
+ fprintf(movfile, "L:%d:%.80s\n", y, cbuf);
+}
+
+
+/*
+ * Record a "text box"
+ */
+void do_cmovie_insert()
+{
+ char buf[81] = "";
+
+ /* Dont record */
+ do_movies = 2;
+
+ while (get_string("Textbox(ESC to end): ", buf, 80))
+ {
+ fprintf(movfile, "B:%s\nW:\n", buf);
+ buf[0] = '\0';
+ }
+
+ /* We reinit the time as to not count the time the user needed ot enter the text */
+ cmovie_init_second();
+
+ /* Continue recording */
+ do_movies = 1;
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 00000000..d626062a
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,584 @@
+/* File: config.h */
+
+/* Purpose: Angband specific configuration stuff */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+/*
+ * Look through the following lines, and where a comment includes the
+ * tag "OPTION:", examine the associated "#define" statements, and decide
+ * whether you wish to keep, comment, or uncomment them. You should not
+ * have to modify any lines not indicated by "OPTION".
+ *
+ * Note: Also examine the "system" configuration file "h-config.h"
+ * and the variable initialization file "variable.c". If you change
+ * anything in "variable.c", you only need to recompile that file.
+ *
+ * And finally, remember that the "Makefile" will specify some rather
+ * important compile time options, like what visual module to use.
+ */
+
+
+/*
+ * OPTION: See the Makefile(s), where several options may be declared.
+ *
+ * Some popular options include "USE_GCU" (allow use with Unix "curses"),
+ * "USE_X11" (allow basic use with Unix X11), "USE_XAW" (allow use with
+ * Unix X11 plus the Athena Widget set), and "USE_CAP" (allow use with
+ * the "termcap" library, or with hard-coded vt100 terminals).
+ *
+ * The old "USE_NCU" option has been replaced with "USE_GCU".
+ *
+ * Several other such options are available for non-unix machines,
+ * such as "MACINTOSH", "WINDOWS", "USE_IBM", "USE_EMX".
+ *
+ * You may also need to specify the "system", using defines such as
+ * "SOLARIS" (for Solaris), etc, see "h-config.h" for more info.
+ */
+
+
+/*
+ * OPTION: define "SPECIAL_BSD" for using certain versions of UNIX
+ * that use the 4.4BSD Lite version of Curses in "main-gcu.c"
+ */
+/* #define SPECIAL_BSD */
+
+
+/*
+ * OPTION: Use the POSIX "termios" methods in "main-gcu.c"
+ */
+/* #define USE_TPOSIX */
+
+/*
+ * OPTION: Use the "termio" methods in "main-gcu.c"
+ */
+/* #define USE_TERMIO */
+
+/*
+ * OPTION: Use the icky BSD "tchars" methods in "main-gcu.c"
+ */
+/* #define USE_TCHARS */
+
+
+/*
+ * OPTION: Use "blocking getch() calls" in "main-gcu.c".
+ * Hack -- Note that this option will NOT work on many BSD machines
+ * Currently used whenever available, if you get a warning about
+ * "nodelay()" undefined, then make sure to undefine this.
+ */
+#if defined(SYS_V) || defined(AMIGA)
+# define USE_GETCH
+#endif
+
+
+/*
+ * OPTION: Use the "curs_set()" call in "main-gcu.c".
+ * Hack -- This option will not work on most BSD machines
+ */
+#ifdef SYS_V
+# define USE_CURS_SET
+#endif
+
+
+/*
+ * OPTION: Include "ncurses.h" instead of "curses.h" in "main-gcu.c"
+ */
+/* #define USE_NCURSES */
+
+
+/*
+ * OPTION: for multi-user machines running the game setuid to some other
+ * user (like 'games') this SAFE_SETUID option allows the program to drop
+ * its privileges when saving files that allow for user specified pathnames.
+ * This lets the game be installed system wide without major security
+ * concerns. There should not be any side effects on any machines.
+ *
+ * This will handle "gids" correctly once the permissions are set right.
+ */
+#define SAFE_SETUID
+
+
+/*
+ * This flag enables the "POSIX" methods for "SAFE_SETUID".
+ */
+#if defined(_POSIX_SAVED_IDS) && !(defined(SUNOS) && !defined(SOLARIS)) && !defined(__APPLE__)
+# define SAFE_SETUID_POSIX
+#endif
+
+
+/*
+ * OPTION: Verify savefile Checksums (Angband 2.7.0 and up)
+ * This option can help prevent "corruption" of savefiles, and also
+ * stop intentional modification by amateur users.
+ */
+#define VERIFY_CHECKSUMS
+
+
+/*
+ * OPTION: Forbid the use of "fiddled" savefiles. As far as I can tell,
+ * a fiddled savefile is one with an internal timestamp different from
+ * the actual timestamp. Thus, turning this option on forbids one from
+ * copying a savefile to a different name. Combined with disabling the
+ * ability to save the game without quitting, and with some method of
+ * stopping the user from killing the process at the tombstone screen,
+ * this should prevent the use of backup savefiles. It may also stop
+ * the use of savefiles from other platforms, so be careful.
+ */
+/* #define VERIFY_TIMESTAMP */
+
+
+/*
+ * OPTION: Forbid the "savefile over-write" cheat, in which you simply
+ * run another copy of the game, loading a previously saved savefile,
+ * and let that copy over-write the "dead" savefile later. This option
+ * either locks the savefile, or creates a fake "xxx.lok" file to prevent
+ * the use of the savefile until the file is deleted. Not ready yet.
+ */
+/* #define VERIFY_SAVEFILE */
+
+
+/*
+ * OPTION: Check version stamp when loading files in lib/data directory,
+ * or their source in lib/edit.
+ */
+/* # #define VERIFY_VERSION_STAMP */
+
+
+
+/*
+ * OPTION: Hack -- Compile in support for "Cyborg" mode
+ */
+/*#define ALLOW_BORG*/
+
+/*
+ * OPTION: Hack -- Compile in support for "Wizard Commands"
+ */
+#define ALLOW_WIZARD
+
+/*
+ * OPTION: Hack -- Compile in support for "Spoiler Generation"
+ */
+#define ALLOW_SPOILERS
+
+/*
+ * OPTION: Allow "do_cmd_colors" at run-time
+ */
+#define ALLOW_COLORS
+
+/*
+ * OPTION: Allow "do_cmd_visuals" at run-time
+ */
+#define ALLOW_VISUALS
+
+/*
+ * OPTION: Allow "do_cmd_macros" at run-time
+ */
+#define ALLOW_MACROS
+
+/*
+ * OPTION: Allow characteres to be "auto-rolled"
+ */
+#define ALLOW_AUTOROLLER
+
+
+/*
+ * OPTION: Allow monsters to "flee" when hit hard
+ */
+#define ALLOW_FEAR
+
+/*
+ * OPTION: Allow monsters to "flee" from strong players
+ */
+#define ALLOW_TERROR
+
+
+/*
+ * OPTION: Allow parsing of the ascii template files in "init.c".
+ * This must be defined if you do not have valid binary image files.
+ * It should be usually be defined anyway to allow easy "updating".
+ */
+#define ALLOW_TEMPLATES
+
+/*
+ * OPTION: Allow loading of pre-2.7.0 savefiles. Note that it takes
+ * about 15K of code in "save-old.c" to parse the old savefile format.
+ * Angband 2.8.0 will ignore a lot of info from pre-2.7.0 savefiles.
+ */
+#define ALLOW_OLD_SAVEFILES
+
+
+/*
+ * OPTION: Delay the loading of the "f_text" array until it is actually
+ * needed, saving ~1K, since "feature" descriptions are unused.
+ */
+/* #define DELAY_LOAD_F_TEXT */
+
+/*
+ * OPTION: Delay the loading of the "k_text" array until it is actually
+ * needed, saving ~1K, since "object" descriptions are unused.
+ */
+/* #define DELAY_LOAD_K_TEXT */
+
+/*
+ * OPTION: Delay the loading of the "a_text" array until it is actually
+ * needed, saving ~1K, since "artifact" descriptions are unused.
+ */
+/* #define DELAY_LOAD_A_TEXT */
+
+/*
+ * OPTION: Delay the loading of the "e_text" array until it is actually
+ * needed, saving ~1K, since "ego-item" descriptions are unused.
+ */
+#define DELAY_LOAD_E_TEXT
+
+/*
+ * OPTION: Delay the loading of the "r_text" array until it is actually
+ * needed, saving ~60K, but "simplifying" the "monster" descriptions.
+ */
+/* #define DELAY_LOAD_R_TEXT */
+
+/*
+ * OPTION: Delay the loading of the "v_text" array until it is actually
+ * needed, saving ~1K, but "destroying" the "vault" generation.
+ */
+/* #define DELAY_LOAD_V_TEXT */
+
+
+/*
+ * OPTION: Handle signals
+ */
+#define HANDLE_SIGNALS
+
+
+/*
+ * Allow "Wizards" to yield "high scores"
+ */
+/* #define SCORE_WIZARDS */
+
+/*
+ * Allow "Borgs" to yield "high scores"
+ */
+/*#define SCORE_BORGS*/
+
+/*
+ * Allow "Cheaters" to yield "high scores"
+ */
+/* #define SCORE_CHEATERS */
+
+
+
+/*
+ * OPTION: Allow use of the "flow_by_smell" and "flow_by_sound"
+ * software options, which enable "monster flowing".
+ */
+#define MONSTER_FLOW
+
+
+/*
+ * OPTION: Maximum flow depth when using "MONSTER_FLOW"
+ */
+#define MONSTER_FLOW_DEPTH 32
+
+
+
+/*
+ * OPTION: Allow use of extended spell info -DRS-
+ */
+#define DRS_SHOW_SPELL_INFO
+
+/*
+ * OPTION: Allow use of the monster health bar -DRS-
+ */
+#define DRS_SHOW_HEALTH_BAR
+
+
+
+/*
+ * OPTION: Enable the "track_follow" and "track_target" options.
+ * They let monsters follow the player's foot-prints, or remember
+ * the player's recent locations. This code has been removed from
+ * the current version because it is being rewritten by Billy, and
+ * until it is ready, it will not work. Do not define this option.
+ */
+/* #define WDT_TRACK_OPTIONS */
+
+
+
+/*
+ * OPTION: Allow the use of "sound" in various places.
+ */
+#define USE_SOUND
+
+/*
+ * OPTION: Allow the use of "graphics" in various places
+ */
+#define USE_GRAPHICS
+
+
+/*
+ * OPTION: Hack -- Macintosh stuff
+ */
+#ifdef MACINTOSH
+
+/* Do not handle signals */
+# undef HANDLE_SIGNALS
+
+#endif
+
+
+/*
+ * OPTION: Hack -- Windows stuff
+ */
+#ifdef WINDOWS
+
+/* Do not handle signals */
+# undef HANDLE_SIGNALS
+
+#endif
+
+
+/*
+ * OPTION: Hack -- EMX stuff
+ */
+#ifdef USE_EMX
+
+/* Do not handle signals */
+# undef HANDLE_SIGNALS
+
+#endif
+
+
+/*
+ * OPTION: Set the "default" path to the angband "lib" directory.
+ *
+ * See "main.c" for usage, and note that this value is only used on
+ * certain machines, primarily Unix machines. If this value is used,
+ * it will be over-ridden by the "ANGBAND_PATH" environment variable,
+ * if that variable is defined and accessable. The final slash is
+ * optional, but it may eventually be required.
+ *
+ * Using the value "./lib/" below tells Angband that, by default,
+ * the user will run "angband" from the same directory that contains
+ * the "lib" directory. This is a reasonable (but imperfect) default.
+ *
+ * If at all possible, you should change this value to refer to the
+ * actual location of the "lib" folder, for example, "/tmp/angband/lib/"
+ * or "/usr/games/lib/angband/", or "/pkg/angband/lib".
+ *
+ * Additional note -- if you are planning to use makefile.org, don't bother
+ * setting this variable, as it is overridden by a value set near the top of
+ * that file.
+ */
+#ifndef DEFAULT_PATH
+# define DEFAULT_PATH "./lib/"
+#endif
+
+
+/*
+ * OPTION: Create and use a hidden directory in the user's home directory
+ * for storing pref-files and character-dumps.
+ */
+#if defined(SET_UID) && !defined(MACH_O_CARBON) && !defined(NO_HOME_TOME)
+#define PRIVATE_USER_PATH "~/.tome"
+#endif /* SET_UID && !MACH_O_CARBON */
+
+/*
+ * Where to put the user's files on the Mac
+ */
+#if defined(MACH_O_CARBON)
+#define PRIVATE_USER_PATH "~/Library/Application Support/ToME"
+#define PRIVATE_USER_PATH_DATA
+#define PRIVATE_USER_PATH_APEX
+#define PRIVATE_USER_PATH_MODULES
+#endif
+
+/*
+ * On multiuser systems, add the "uid" to savefile names
+ */
+#if defined(SET_UID) && !defined(MACH_O_CARBON)
+# define SAVEFILE_USE_UID
+#endif /* SET_UID && !MACH_O_CARBON */
+
+
+/*
+ * OPTION: Check the "time" against "lib/file/hours.txt"
+ */
+/* #define CHECK_TIME */
+
+/*
+ * OPTION: Check the "load" against "lib/file/load.txt"
+ * This may require the 'rpcsvs' library
+ */
+/* #define CHECK_LOAD */
+
+
+/*
+ * OPTION: For some brain-dead computers with no command line interface,
+ * namely Macintosh, there has to be some way of "naming" your savefiles.
+ * The current "Macintosh" hack is to make it so whenever the character
+ * name changes, the savefile is renamed accordingly. But on normal
+ * machines, once you manage to "load" a savefile, it stays that way.
+ * Macintosh is particularly weird because you can load savefiles that
+ * are not contained in the "lib:save:" folder, and if you change the
+ * player's name, it will then save the savefile elsewhere. Note that
+ * this also gives a method of "bypassing" the "VERIFY_TIMESTAMP" code.
+ */
+#if defined(MACINTOSH) || defined(WINDOWS) || defined(AMIGA)
+/* #define SAVEFILE_MUTABLE */
+#endif
+
+
+/*
+ * OPTION: Capitalize the "user_name" (for "default" player name)
+ * This option is only relevant on SET_UID machines.
+ */
+#define CAPITALIZE_USER_NAME
+
+
+
+/*
+ * OPTION: Person to bother if something goes wrong.
+ */
+#define MAINTAINER "darkgod@t-o-m-e.net"
+
+
+/*
+ * OPTION: Default font (when using X11).
+ */
+#define DEFAULT_X11_FONT "9x15"
+
+/*
+ * OPTION: Default fonts (when using X11)
+ */
+#define DEFAULT_X11_FONT_SCREEN DEFAULT_X11_FONT
+#define DEFAULT_X11_FONT_MIRROR DEFAULT_X11_FONT
+#define DEFAULT_X11_FONT_RECALL DEFAULT_X11_FONT
+#define DEFAULT_X11_FONT_CHOICE DEFAULT_X11_FONT
+
+
+
+/*
+ * Hack -- Special "ancient machine" versions
+ */
+#if defined(USE_286) || defined(ANGBAND_LITE_MAC)
+# ifndef ANGBAND_LITE
+# define ANGBAND_LITE
+# endif
+#endif
+
+/*
+ * OPTION: Attempt to minimize the size of the game
+ */
+#ifndef ANGBAND_LITE
+/* #define ANGBAND_LITE */
+#endif
+
+/*
+ * Hack -- React to the "ANGBAND_LITE" flag
+ */
+#ifdef ANGBAND_LITE
+# undef ALLOW_COLORS
+# undef ALLOW_VISUALS
+# undef ALLOW_MACROS
+# undef MONSTER_FLOW
+# undef WDT_TRACK_OPTIONS
+# undef ALLOW_OLD_SAVEFILES
+# undef ALLOW_BORG
+# undef ALLOW_WIZARD
+# undef ALLOW_SPOILERS
+# undef ALLOW_TEMPLATES
+# undef DELAY_LOAD_R_TEXT
+# define DELAY_LOAD_R_TEXT
+#endif
+
+
+
+/*
+ * OPTION: Attempt to prevent all "cheating"
+ */
+/* #define VERIFY_HONOR */
+
+
+/*
+ * React to the "VERIFY_HONOR" flag
+ */
+#ifdef VERIFY_HONOR
+# define VERIFY_SAVEFILE
+# define VERIFY_CHECKSUMS
+# define VERIFY_TIMESTAMPS
+#endif
+
+/* ToME options: */
+
+/* Should the player know his / her starting life rate? */
+/* #define SHOW_LIFE_RATE */
+
+/* Do we want different characters for different races? */
+#define VARIABLE_PLAYER_GRAPH
+
+/* Allow hordes of 'similar' monsters */
+#define MONSTER_HORDES
+
+/* Wizard mode testing options: */
+
+/* For testing the vaults */
+/* # define FORCE_V_IDX 20 */
+
+/* Testing upkeep */
+/* # define TRACK_FRIENDS */
+
+/*
+ * OPTION: Repeat last command -- TNB
+ */
+#define ALLOW_REPEAT
+
+/*
+ * OPTION: Make opening and closing things easy -- TNB
+ */
+#define ALLOW_EASY_OPEN
+
+/*
+ * OPTION: Make disarming traps easy -- TNB
+ */
+#define ALLOW_EASY_DISARM
+
+/*
+ * Check the modification time of *_info.raw files
+ * (by Keldon Jones)
+ */
+#define CHECK_MODIFICATION_TIME
+
+/*
+ * Using the fast autoroller can be considered as cheating
+ */
+#define USE_FAST_AUTOROLLER
+
+/*
+ * Forbid the use of some race/class combinations
+ */
+/* #define RESTRICT_COMBINATIONS */
+
+/*
+ * Enable the CTRL + L command to quit without saving
+ * Only use for debugging purpose, otherwise you are a CHEATER
+ */
+/* #define ALLOW_QUITTING */
+
+/*
+ * Panic saves have a different name
+ */
+#define SAFER_PANICS
+
+/*
+ * Allow makefiles to override the default file mode
+ */
+#ifndef FILE_MODE
+#define FILE_MODE 0644
+#endif
diff --git a/src/defines.h b/src/defines.h
new file mode 100644
index 00000000..96510671
--- /dev/null
+++ b/src/defines.h
@@ -0,0 +1,4722 @@
+/* File: defines.h */
+
+/* Purpose: global constants and macro definitions */
+
+
+/*
+ * Do not edit this file unless you know *exactly* what you are doing.
+ *
+ * Some of the values in this file were chosen to preserve game balance,
+ * while others are hard-coded based on the format of old save-files, the
+ * definition of arrays in various places, mathematical properties, fast
+ * computation, storage limits, or the format of external text files.
+ *
+ * Changing some of these values will induce crashes or memory errors or
+ * savefile mis-reads. Most of the comments in this file are meant as
+ * reminders, not complete descriptions, and even a complete knowledge
+ * of the source may not be sufficient to fully understand the effects
+ * of changing certain definitions.
+ *
+ * Lastly, note that the code does not always use the symbolic constants
+ * below, and sometimes uses various hard-coded values that may not even
+ * be defined in this file, but which may be related to definitions here.
+ * This is of course bad programming practice, but nobody is perfect...
+ *
+ * For example, there are MANY things that depend on the screen being
+ * 80x24, with the top line used for messages, the bottom line being
+ * used for status, and exactly 22 lines used to show the dungeon.
+ * Just because your screen can hold 46 lines does not mean that the
+ * game will work if you try to use 44 lines to show the dungeon.
+ *
+ * You have been warned.
+ */
+
+
+/*
+ * Release state, CVS or not, remember to switch it when making releases
+ */
+
+#ifndef IS_CVS
+#define IS_CVS ""
+/*#define IS_CVS ""*/
+#endif
+
+#define USER_PATH_VERSION "/2.3"
+
+#define ANGBAND_2_8_1
+
+#define SAVEFILE_VERSION 104
+
+/*
+ * This value is not currently used
+ */
+#define VERSION_EXTRA 0
+
+/*
+ * Maximum amount of Angband windows.
+ */
+#define ANGBAND_TERM_MAX 8
+
+/*
+ * Number of grids in each block (vertically)
+ * Probably hard-coded to 11, see "generate.c"
+ */
+#define BLOCK_HGT 11
+
+/*
+ * Number of grids in each block (horizontally)
+ * Probably hard-coded to 11, see "generate.c"
+ */
+#define BLOCK_WID 11
+
+
+/*
+ * Number of grids in each panel (vertically)
+ * Must be a multiple of BLOCK_HGT
+ */
+#define PANEL_HGT 11
+
+/*
+ * Number of grids in each panel (horizontally)
+ * Must be a multiple of BLOCK_WID
+ */
+#define PANEL_WID 33
+
+
+/*
+ * Number of grids used to display the dungeon (vertically).
+ * Must be a multiple of 11, probably hard-coded to 22.
+ */
+#define SCREEN_HGT 22
+
+/*
+ * Number of grids used to display the dungeon (horizontally).
+ * Must be a multiple of 33, probably hard-coded to 66.
+ */
+#define SCREEN_WID 66
+
+
+/*
+ * Maximum dungeon height in grids, must be a multiple of SCREEN_HGT,
+ * probably hard-coded to SCREEN_HGT * 3.
+ */
+#define MAX_HGT 66
+
+/*
+ * Maximum dungeon width in grids, must be a multiple of SCREEN_WID,
+ * probably hard-coded to SCREEN_WID * 3.
+ */
+#define MAX_WID 198
+
+/* Used only in object3.c / trap effects */
+#if ((MAX_HGT / SCREEN_HGT) < (MAX_WID / SCREEN_WID))
+ #define RATIO (MAX_WID / SCREEN_WID)
+#else
+ #define RATIO (MAX_HGT / SCREEN_HGT)
+#endif
+
+/*
+ * Default radius of detection spells
+ * Its area must be at least as large as SCREEN_WID * SCREEN_HGT
+ */
+#define DEFAULT_RADIUS 25
+
+
+#define CHANCE_TRAP_JAMMED_DOOR 2500
+#define CHANCE_TRAP_SECRET_DOOR 1500
+#define CHANCE_TRAP_LOCKED_DOOR 1000
+#define CHANCE_TRAP_DOOR 500 /* in 10000 */
+#define CHANCE_TRAP_FLOOR 4 /* in 10000 chance of placing a trap */
+
+#define MAX_BOUNTIES 24
+
+#define MAX_SPELLS 100
+#define MAX_RUNES 100
+
+/*
+ * Arena constants
+ */
+#define MAX_ARENA_MONS 29 /* -KMW- */
+
+/*
+ * Total number of stores (see "store.c", etc)
+ */
+#define STORE_GENERAL 0
+#define STORE_ARMOURY 1
+#define STORE_WEAPON 2
+#define STORE_TEMPLE 3
+#define STORE_ALCHEMIST 4
+#define STORE_MAGIC 5
+#define STORE_BLACK 6
+#define STORE_HOME 7
+#define STORE_BOOK 8
+#define STORE_PET 9
+
+/*
+ * Maximum number of player "sex" types (see "table.c", etc)
+ */
+#define MAX_SEXES 3
+
+/* The number of "patrons" available (for Chaos Warriors) */
+#define MAX_PATRON 16
+
+/* Number of Random Artifacts */
+#define MAX_RANDARTS 84
+#define MAX_T_ACT 51
+
+/* Chaos Warrior: Reward types: */
+#define REW_POLY_SLF 0
+#define REW_GAIN_EXP 1
+#define REW_LOSE_EXP 2
+#define REW_GOOD_OBJ 3
+#define REW_GREA_OBJ 4
+#define REW_CHAOS_WP 5
+#define REW_GOOD_OBS 6
+#define REW_GREA_OBS 7
+#define REW_TY_CURSE 8
+#define REW_SUMMON_M 9
+#define REW_H_SUMMON 10
+#define REW_DO_HAVOC 11
+#define REW_GAIN_ABL 12
+#define REW_LOSE_ABL 13
+#define REW_RUIN_ABL 14
+#define REW_AUGM_ABL 15
+#define REW_POLY_WND 16
+#define REW_HEAL_FUL 17
+#define REW_HURT_LOT 18
+#define REW_CURSE_WP 19
+#define REW_CURSE_AR 20
+#define REW_PISS_OFF 21
+#define REW_WRATH 22
+#define REW_DESTRUCT 23
+#define REW_GENOCIDE 24
+#define REW_MASS_GEN 25
+#define REW_DISPEL_C 26
+#define REW_UNUSED_1 27
+#define REW_UNUSED_2 28
+#define REW_UNUSED_3 29
+#define REW_UNUSED_4 30
+#define REW_UNUSED_5 31
+#define REW_IGNORE 32
+#define REW_SER_UNDE 33
+#define REW_SER_DEMO 34
+#define REW_SER_MONS 35
+
+
+/* bear barehanded attacks ... */
+#define MAX_BEAR 8
+
+/* Monk martial arts... */
+#define MAX_MA 17
+
+#define MA_KNEE 0x0001
+#define MA_SLOW 0x0002
+#define MA_WOUND 0x0004
+#define MA_STUN 0x0008
+#define MA_FULL_SLOW 0x0010
+
+/* Mindcraft */
+#define MAX_MINDCRAFT_POWERS 12
+
+/* Necromancy */
+#define MAX_NECRO_POWERS 6
+
+/* Mimicry */
+#define MAX_MIMIC_POWERS 5
+
+/* Symbiosis */
+#define MAX_SYMBIOTIC_POWERS 9
+
+
+/* A hack for cave.c */
+#define BMP_FIRST_PC_CLASS 164
+#define BMP_FIRST_PC_RACE 128
+
+
+/*
+ * Size of memory reserved for initialization of some arrays
+ */
+#define FAKE_NAME_SIZE 40 * 1024L
+#define FAKE_TEXT_SIZE 120 * 1024L
+
+
+/*
+ * Maximum number of high scores in the high score file
+ */
+#define MAX_HISCORES 100
+
+
+/*
+ * Maximum dungeon level. The player can never reach this level
+ * in the dungeon, and this value is used for various calculations
+ * involving object and monster creation. It must be at least 100.
+ * Setting it below 128 may prevent the creation of some objects.
+ */
+#define MAX_DEPTH 128
+#define MAX_DEPTH_MONSTER 200
+
+
+/*
+ * Maximum size of the "lite" array (see "cave.c")
+ * Note that the "lite radius" will NEVER exceed 5, and even if the "lite"
+ * was rectangular, we would never require more than 128 entries in the array.
+ */
+#define LITE_MAX 1536
+
+/*
+ * Maximum size of the "view" array (see "cave.c")
+ * Note that the "view radius" will NEVER exceed 20, and even if the "view"
+ * was octagonal, we would never require more than 1520 entries in the array.
+ */
+#define VIEW_MAX 1536
+
+/*
+ * Maximum size of the "temp" array (see "cave.c")
+ * We must be as large as "VIEW_MAX" and "LITE_MAX" for proper functioning
+ * of "update_view()" and "update_lite()". We must also be as large as the
+ * largest illuminatable room, but no room is larger than 800 grids. We
+ * must also be large enough to allow "good enough" use as a circular queue,
+ * to calculate monster flow, but note that the flow code is "paranoid".
+ */
+#define TEMP_MAX 16384
+
+
+/*
+ * Number of keymap modes
+ */
+#define KEYMAP_MODES 2
+
+/*
+ * Mode for original keyset commands
+ */
+#define KEYMAP_MODE_ORIG 0
+
+/*
+ * Mode for roguelike keyset commands
+ */
+#define KEYMAP_MODE_ROGUE 1
+
+
+/*
+ * OPTION: Maximum number of macros (see "io.c")
+ * Default: assume at most 256 macros are used
+ */
+#define MACRO_MAX 256
+
+/*
+ * OPTION: Maximum number of "quarks" (see "io.c")
+ * Default: assume at most 512 different inscriptions are used
+ */
+#define QUARK_MAX 768
+ /* Was 512... 256 quarks added for random artifacts */
+
+/*
+ * OPTION: Maximum number of messages to remember (see "io.c")
+ * Default: assume maximal memorization of 2048 total messages
+ */
+#define MESSAGE_MAX 2048
+
+#define MESSAGE_NONE 0
+#define MESSAGE_MSG 1
+#define MESSAGE_IRC 2
+
+/*
+ * OPTION: Maximum space for the message text buffer (see "io.c")
+ * Default: assume that each of the 2048 messages is repeated an
+ * average of three times, and has an average length of 48
+ */
+#define MESSAGE_BUF 32768
+
+
+/*
+ * Maximum value storable in a "byte" (hard-coded)
+ */
+#define MAX_UCHAR 255
+
+/*
+ * Maximum value storable in a "s16b" (hard-coded)
+ */
+#define MAX_SHORT 32767
+
+
+/*
+ * Store constants
+ */
+#define STORE_INVEN_MAX 255 /* Max number of discrete objs in inven */
+#define STORE_CHOICES 56 /* Number of items to choose stock from */
+#define STORE_OBJ_LEVEL 5 /* Magic Level for normal stores */
+#define STORE_TURNOVER 9 /* Normal shop turnover, per day */
+#define STORE_MIN_KEEP 6 /* Min slots to "always" keep full */
+#define STORE_MAX_KEEP 18 /* Max slots to "always" keep full */
+#define STORE_SHUFFLE 21 /* 1/Chance (per day) of an owner changing */
+#define STORE_TURNS 1000 /* Number of turns between turnovers */
+
+/*
+ * Misc constants
+ */
+#define DAY 11520 /* Number of turns per day */
+#define YEAR (DAY * 365) /* Number of turns per year */
+#define HOUR (DAY / 24) /* Number of turns per hour */
+#define MINUTE (HOUR / 60) /* Number of turns per minute */
+#define DAY_START (HOUR * 6) /* Sunrise */
+#define START_YEAR 2890 /* Bilbo birthday year */
+#define START_DAY (DAY * (42 + 127)) /* Bilbo birthday */
+
+#define BREAK_GLYPH 550 /* Rune of protection resistance */
+#define BREAK_MINOR_GLYPH 99 /* For explosive runes */
+#define BTH_PLUS_ADJ 3 /* Adjust BTH per plus-to-hit */
+#define MON_MULT_ADJ 10 /* High value slows multiplication */
+#define MON_SUMMON_ADJ 2 /* Adjust level of summoned creatures */
+#define MON_DRAIN_LIFE 2 /* Percent of player exp drained per hit */
+#define USE_DEVICE 3 /* x> Harder devices x< Easier devices */
+
+#define BIAS_ELEC 1 /* "Biases" for random artifact gen */
+#define BIAS_POIS 2
+#define BIAS_FIRE 3
+#define BIAS_COLD 4
+#define BIAS_ACID 5
+#define BIAS_STR 6
+#define BIAS_INT 7
+#define BIAS_WIS 8
+#define BIAS_DEX 9
+#define BIAS_CON 10
+#define BIAS_CHR 11
+#define BIAS_CHAOS 12
+#define BIAS_PRIESTLY 13
+#define BIAS_NECROMANTIC 14
+#define BIAS_LAW 15
+#define BIAS_ROGUE 16
+#define BIAS_MAGE 17
+#define BIAS_WARRIOR 18
+#define BIAS_RANGER 19
+
+/*
+ * Location of objects when they were found
+ */
+#define OBJ_FOUND_MONSTER 1
+#define OBJ_FOUND_FLOOR 2
+#define OBJ_FOUND_VAULT 3
+#define OBJ_FOUND_SPECIAL 4
+#define OBJ_FOUND_RUBBLE 5
+#define OBJ_FOUND_REWARD 6
+#define OBJ_FOUND_STORE 7
+#define OBJ_FOUND_STOLEN 8
+#define OBJ_FOUND_SELFMADE 9
+/*
+ * There is a 1/20 (5%) chance of inflating the requested object_level
+ * during the creation of an object (see "get_obj_num()" in "object.c").
+ * Lower values yield better objects more often.
+ */
+#define GREAT_OBJ 20
+
+#define GREAT_EGO 20
+
+/*
+ * There is a 1/50 (2%) chance of inflating the requested monster_level
+ * during the creation of a monsters (see "get_mon_num()" in "monster.c").
+ * Lower values yield harder monsters more often.
+ */
+#define NASTY_MON 50 /* 1/chance of inflated monster level */
+
+
+
+/*
+ * Refueling constants
+ */
+#define FUEL_TORCH 5000 /* Maximum amount of fuel in a torch */
+#define FUEL_LAMP 15000 /* Maximum amount of fuel in a lantern */
+
+
+/*
+ * More maximum values
+ */
+#define MAX_SIGHT 20 /* Maximum view distance */
+#define MAX_RANGE 18 /* Maximum range (spells, etc) */
+
+
+
+/*
+ * The town starts out with 4 residents during the day
+ */
+#define MIN_M_ALLOC_TD 4
+
+/*
+ * The town starts out with 8 residents during the night
+ */
+#define MIN_M_ALLOC_TN 8
+
+
+/*
+ * A monster can only "multiply" (reproduce) if there are fewer than 100
+ * monsters on the level capable of such spontaneous reproduction. This
+ * is a hack which prevents the "m_list[]" array from exploding due to
+ * reproducing monsters. Messy, but necessary.
+ */
+#define MAX_REPRO 100
+
+
+/*
+ * Player constants
+ */
+#define SUBRACE_SAVE 9 /* Ugly hack, should be in foo-info, the subrace saved to the savefile */
+#define PY_MAX_EXP 99999999L /* Maximum exp */
+#define PY_MAX_GOLD 999999999L /* Maximum gold */
+#define PY_MAX_LEVEL 50 /* Maximum level */
+
+/*
+ * Player "food" crucial values
+ */
+#define PY_FOOD_MAX 15000 /* Food value (Bloated) */
+#define PY_FOOD_FULL 10000 /* Food value (Normal) */
+#define PY_FOOD_ALERT 2000 /* Food value (Hungry) */
+#define PY_FOOD_WEAK 1000 /* Food value (Weak) */
+#define PY_FOOD_FAINT 500 /* Food value (Fainting) */
+#define PY_FOOD_STARVE 100 /* Food value (Starving) */
+
+/*
+ * Player regeneration constants
+ */
+#define PY_REGEN_NORMAL 197 /* Regen factor*2^16 when full */
+#define PY_REGEN_WEAK 98 /* Regen factor*2^16 when weak */
+#define PY_REGEN_FAINT 33 /* Regen factor*2^16 when fainting */
+#define PY_REGEN_HPBASE 1442 /* Min amount hp regen*2^16 */
+#define PY_REGEN_MNBASE 524 /* Min amount mana regen*2^16 */
+
+/*
+ * Maximum number of "normal" pack slots, and the index of the "overflow"
+ * slot, which can hold an item, but only temporarily, since it causes the
+ * pack to "overflow", dropping the "last" item onto the ground. Since this
+ * value is used as an actual slot, it must be less than "INVEN_WIELD" (below).
+ * Note that "INVEN_PACK" is probably hard-coded by its use in savefiles, and
+ * by the fact that the screen can only show 23 items plus a one-line prompt.
+ */
+#define INVEN_PACK 23
+
+/*
+ * Body parts
+ */
+#define BODY_WEAPON 0
+#define BODY_TORSO 1
+#define BODY_ARMS 2
+#define BODY_FINGER 3
+#define BODY_HEAD 4
+#define BODY_LEGS 5
+#define BODY_MAX 6
+
+/*
+ * Indexes used for various "equipment" slots (hard-coded by savefiles, etc).
+ */
+#define INVEN_WIELD 24 /* 3 weapons -- WEAPONS */
+#define INVEN_BOW 27 /* 1 bow -- WEAPON */
+#define INVEN_RING 28 /* 6 rings -- FINGER */
+#define INVEN_NECK 34 /* 2 amulets -- HEAD */
+#define INVEN_LITE 36 /* 1 lite -- TORSO */
+#define INVEN_BODY 37 /* 1 body -- TORSO */
+#define INVEN_OUTER 38 /* 1 cloak -- TORSO */
+#define INVEN_ARM 39 /* 3 arms -- ARMS */
+#define INVEN_HEAD 42 /* 2 heads -- HEAD */
+#define INVEN_HANDS 44 /* 3 hands -- ARMS */
+#define INVEN_FEET 47 /* 2 feets -- LEGS */
+#define INVEN_CARRY 49 /* 1 carried monster -- TORSO */
+#define INVEN_AMMO 50 /* 1 quiver -- TORSO */
+#define INVEN_TOOL 51 /* 1 tool -- ARMS */
+
+/*
+ * Total number of inventory slots (hard-coded).
+ */
+#define INVEN_TOTAL 52
+#define INVEN_EQ (INVEN_TOTAL - INVEN_WIELD)
+
+/*
+ * A "stack" of items is limited to less than 100 items (hard-coded).
+ */
+#define MAX_STACK_SIZE 100
+
+
+
+/*
+ * Indexes of the various "stats" (hard-coded by savefiles, etc).
+ */
+#define A_STR 0
+#define A_INT 1
+#define A_WIS 2
+#define A_DEX 3
+#define A_CON 4
+#define A_CHR 5
+
+/*
+ * Player sex constants (hard-coded by save-files, arrays, etc)
+ */
+#define SEX_FEMALE 0
+#define SEX_MALE 1
+#define SEX_NEUTER 2
+
+
+/* Race flags */
+#define PR1_EXPERIMENTAL 0x00000001L /* Is still under developemnt */
+/* XXX */
+#define PR1_RESIST_BLACK_BREATH 0x00000004L /* Resist black breath */
+#define PR1_NO_STUN 0x00000008L /* Never stunned */
+#define PR1_XTRA_MIGHT_BOW 0x00000010L /* Xtra might with bows */
+#define PR1_XTRA_MIGHT_XBOW 0x00000020L /* Xtra might with xbows */
+#define PR1_XTRA_MIGHT_SLING 0x00000040L /* Xtra might with slings */
+#define PR1_AC_LEVEL 0x00000080L /* More AC with levels */
+#define PR1_HURT_LITE 0x00000100L /* Hurt by light */
+#define PR1_VAMPIRE 0x00000200L /* Vampire */
+#define PR1_UNDEAD 0x00000400L /* Undead */
+#define PR1_NO_CUT 0x00000800L /* no cuts */
+#define PR1_CORRUPT 0x00001000L /* hack-- corrupted */
+#define PR1_NO_FOOD 0x00002000L /* little gain from food */
+#define PR1_NO_GOD 0x00004000L /* cannot worship */
+/* XXX */
+#define PR1_ELF 0x00010000L /* Is an elf */
+#define PR1_SEMI_WRAITH 0x00020000L /* Takes damage when going in walls */
+#define PR1_NO_SUBRACE_CHANGE 0x00040000L /* Impossible to change subrace */
+/* XXX */
+#define PR1_ANTIMAGIC 0x00100000L /* antimagic ... hack */
+#define PR1_MOLD_FRIEND 0x00200000L /* Not attacked by molds wielded */
+#define PR1_GOD_FRIEND 0x00400000L /* Better grace */
+/* XXX */
+#define PR1_INNATE_SPELLS 0x01000000L /* KNown all spells, only need books */
+/* XXX */
+/* XXX */
+#define PR1_EASE_STEAL 0x08000000L /* Gain xp by stealing */
+/* XXX */
+/* XXX */
+/* XXX */
+/* XXX */
+
+/* XXX */
+#define PR2_ASTRAL 0x00000002L /* Is it an astral being coming from th halls of mandos ? */
+/* XXX */
+
+#define PRACE_FLAG2(f) ((rp_ptr->flags2 | rmp_ptr->flags2 | cp_ptr->flags2 | spp_ptr->flags2) & (f))
+#define PRACE_FLAG(f) ((rp_ptr->flags1 | rmp_ptr->flags1 | cp_ptr->flags1 | spp_ptr->flags1) & (f))
+#define PRACE_FLAGS(f) PRACE_FLAG(f)
+
+/* XXX */
+#define MKEY_MINDCRAFT 2
+#define MKEY_ANTIMAGIC 3
+#define MKEY_BLADE 4
+#define MKEY_ALCHEMY 5
+#define MKEY_MIMIC 6
+#define MKEY_NECRO 7
+#define MKEY_POWER_MAGE 8
+#define MKEY_RUNE 9
+#define MKEY_FORGING 10
+#define MKEY_INCARNATION 11
+#define MKEY_TELEKINESIS 12
+#define MKEY_SUMMON 13
+#define MKEY_TRAP 14
+#define MKEY_STEAL 15
+#define MKEY_DODGE 16
+#define MKEY_SCHOOL 17
+#define MKEY_LEARN 18
+#define MKEY_COPY 19
+#define MKEY_SYMBIOTIC 20
+#define MKEY_BOULDER 21
+#define MKEY_COMPANION 22
+#define MKEY_PIERCING 23
+
+
+/*** Screen Locations ***/
+
+/*
+ * Some screen locations for various display routines
+ * Currently, row 8 and 15 are the only "blank" rows.
+ * That leaves a "border" around the "stat" values.
+ */
+
+#define ROW_MAP 1
+#define COL_MAP 13 /* Dungeon map */
+
+#define ROW_RACE 1
+#define COL_RACE 0 /* <race name> */
+
+#define ROW_CLASS 2
+#define COL_CLASS 0 /* <class name> */
+
+#define ROW_TITLE 3
+#define COL_TITLE 0 /* <title> or <mode> */
+
+#define ROW_LEVEL 4
+#define COL_LEVEL 0 /* "LEVEL xxxxxx" */
+
+#define ROW_EXP 5
+#define COL_EXP 0 /* "EXP xxxxxxxx" */
+
+#define ROW_GOLD 6
+#define COL_GOLD 0 /* "AU xxxxxxxxx" */
+
+#define ROW_STAT 8
+#define COL_STAT 0 /* "xxx xxxxxx" */
+
+#define ROW_AC 14
+#define COL_AC 0 /* "Cur AC xxxxx" */
+
+#define ROW_HP 15
+#define COL_HP 0 /* "HP xxxxx/xxxxx" */
+
+#define ROW_SANITY 16 /* "Sanity 100%" */
+#define COL_SANITY 0
+
+#define ROW_SP 17
+#define COL_SP 0 /* "SP xxxxx/xxxxx" */
+
+#define ROW_PIETY 18
+#define COL_PIETY 0 /* "Pt xxxxx/xxxxx" */
+
+#define ROW_MH 19
+#define COL_MH 0 /* "MH xxxxx/xxxxx" */
+
+#define ROW_INFO (Term->hgt - 4)
+#define COL_INFO 0 /* "xxxxxxxxxxxx" */
+
+#define ROW_CUT (Term->hgt - 3)
+#define COL_CUT 0 /* <cut> */
+
+#define ROW_STUN (Term->hgt - 2)
+#define COL_STUN 0 /* <stun> */
+
+#define ROW_HUNGRY (Term->hgt - 1)
+#define COL_HUNGRY 0 /* "Weak" / "Hungry" / "Full" / "Gorged" */
+
+#define ROW_BLIND (Term->hgt - 1)
+#define COL_BLIND 7 /* "Blind" */
+
+#define ROW_CONFUSED (Term->hgt - 1)
+#define COL_CONFUSED 13 /* "Conf" */
+
+#define ROW_AFRAID (Term->hgt - 1)
+#define COL_AFRAID 18 /* "Afraid" */
+
+#define ROW_POISONED (Term->hgt - 1)
+#define COL_POISONED 25 /* "Poison" */
+
+#define ROW_DTRAP (Term->hgt - 1)
+#define COL_DTRAP 32 /* "DTrap" */
+
+#define ROW_STATE (Term->hgt - 1)
+#define COL_STATE 38 /* <state> */
+
+#define ROW_SPEED (Term->hgt - 1)
+#define COL_SPEED 49 /* "Slow (-NN)" or "Fast (+NN)" */
+
+#define ROW_STUDY (Term->hgt - 1)
+#define COL_STUDY 60 /* "Study" */
+
+#define ROW_DEPTH (Term->hgt - 1)
+#define COL_DEPTH (Term->wid - 14) /* "Lev NNN" / "NNNN ft" */
+
+
+
+/*** Terrain Feature Indexes (see "lib/edit/f_info.txt") ***/
+
+/* Nothing */
+#define FEAT_NONE 0x00
+
+/* Basic features */
+#define FEAT_FLOOR 0x01
+#define FEAT_FOUNTAIN 0x02
+#define FEAT_GLYPH 0x03
+#define FEAT_OPEN 0x04
+#define FEAT_BROKEN 0x05
+#define FEAT_LESS 0x06
+#define FEAT_MORE 0x07
+
+/* Quest features -KMW- */
+#define FEAT_QUEST_ENTER 0x08
+#define FEAT_QUEST_EXIT 0x09
+#define FEAT_QUEST_DOWN 0x0A
+#define FEAT_QUEST_UP 0x0B
+
+/* Shafts -GSN- */
+#define FEAT_SHAFT_DOWN 0x0D
+#define FEAT_SHAFT_UP 0x0E
+
+/* Basic feature */
+#define FEAT_EMPTY_FOUNTAIN 0x0F
+
+/* Feature 0x10 -- web */
+
+/* Traps */
+#define FEAT_TRAP 0x11
+
+/* Features 0x12 - 0x1F -- unused */
+
+/* Doors */
+#define FEAT_DOOR_HEAD 0x20
+#define FEAT_DOOR_TAIL 0x2F
+
+/* Extra */
+#define FEAT_SECRET 0x30
+#define FEAT_RUBBLE 0x31
+
+/* Seams */
+#define FEAT_MAGMA 0x32
+#define FEAT_QUARTZ 0x33
+#define FEAT_MAGMA_H 0x34
+#define FEAT_QUARTZ_H 0x35
+#define FEAT_MAGMA_K 0x36
+#define FEAT_QUARTZ_K 0x37
+
+/* Walls */
+#define FEAT_WALL_EXTRA 0x38
+#define FEAT_WALL_INNER 0x39
+#define FEAT_WALL_OUTER 0x3A
+#define FEAT_WALL_SOLID 0x3B
+#define FEAT_PERM_EXTRA 0x3C
+#define FEAT_PERM_INNER 0x3D
+#define FEAT_PERM_OUTER 0x3E
+#define FEAT_PERM_SOLID 0x3F
+
+/* Explosive rune */
+#define FEAT_MINOR_GLYPH 0x40
+
+/* Pattern */
+#define FEAT_PATTERN_START 0x41
+#define FEAT_PATTERN_1 0x42
+#define FEAT_PATTERN_2 0x43
+#define FEAT_PATTERN_3 0x44
+#define FEAT_PATTERN_4 0x45
+#define FEAT_PATTERN_END 0x46
+#define FEAT_PATTERN_OLD 0x47
+#define FEAT_PATTERN_XTRA1 0x48
+#define FEAT_PATTERN_XTRA2 0x49
+
+/* Shops */
+#define FEAT_SHOP 0x4A
+
+/* Permanent walls for quests */
+#define FEAT_QUEST1 0x4B
+#define FEAT_QUEST2 0x4C
+#define FEAT_QUEST3 0x4D
+#define FEAT_QUEST4 0x4E
+
+/* Features 0x4F - 0x53 -- unused */
+
+/* Additional terrains */
+#define FEAT_SHAL_WATER 0x54
+#define FEAT_DEEP_LAVA 0x55
+#define FEAT_SHAL_LAVA 0x56
+#define FEAT_DARK_PIT 0x57
+#define FEAT_DIRT 0x58
+#define FEAT_GRASS 0x59
+#define FEAT_ICE 0x5A
+#define FEAT_SAND 0x5B
+#define FEAT_DEAD_TREE 0x5C
+#define FEAT_DEAD_SMALL_TREE 212
+#define FEAT_ASH 0x5D
+#define FEAT_MUD 0x5E
+#define FEAT_ICE_WALL 0x5F
+#define FEAT_TREES 0x60
+#define FEAT_MOUNTAIN 0x61
+#define FEAT_SANDWALL 0x62
+#define FEAT_SANDWALL_H 0x63
+#define FEAT_SANDWALL_K 0x64
+/* Feature 0x65 -- high mountain chain */
+/* Feature 0x66 -- nether mist */
+
+/* Features 0x67 - 0x9F -- unused */
+
+#define FEAT_BETWEEN 0xA0 /* 160 */
+
+/* Altars */
+#define FEAT_ALTAR_HEAD 0xA1 /* 161 */
+#define FEAT_ALTAR_TAIL 0xAB /* 171 */
+
+#define FEAT_MARKER 0xAC /* 172 */
+/* Feature 0xAD -- Underground Tunnel */
+#define FEAT_TAINTED_WATER 0xAE /* 174 */
+#define FEAT_MON_TRAP 0xAF /* 175 */
+#define FEAT_BETWEEN2 0xB0 /* 176 */
+#define FEAT_LAVA_WALL 0xB1 /* 177 */
+#define FEAT_GREAT_FIRE 0xB2 /* 178 */
+#define FEAT_WAY_MORE 0xB3 /* 179 */
+#define FEAT_WAY_LESS 0xB4 /* 180 */
+/* Feature 0xB5 -- field */
+#define FEAT_EKKAIA 0xB6 /* 182 */
+
+/* Features 0xB7 - 0xBA -- unused */
+
+#define FEAT_DEEP_WATER 0xBB /* 187 */
+#define FEAT_GLASS_WALL 0xBC /* 188 */
+#define FEAT_ILLUS_WALL 0xBD /* 189 */
+/* Feature 0xBE -- grass roof */
+/* Feature 0xBF -- grass roof top */
+/* Feature 0xC0 -- grass roof chimney */
+/* Feature 0xC1 -- brick roof */
+/* Feature 0xC2 -- brick roof top */
+/* Feature 0xC3 -- brick roof chimney */
+/* Feature 0xC4 -- window */
+/* Feature 0xC5 -- small window */
+/* Feature 0xC6 -- rain barrel */
+#define FEAT_FLOWER 0xC7 /* 199 */
+/* Feature 0xC8 -- cobblestone road */
+/* Feature 0xC9 -- cobblestone with outlet */
+#define FEAT_SMALL_TREES 0xCA /* 202 */
+#define FEAT_TOWN 0xCB /* 203 */
+/* Feature 0xCC -- Underground Tunnel */
+#define FEAT_FIRE 0xCD /* 205 */
+/* Feature 0xCE -- pile of rubble (permanent) */
+
+/* Features 0xCF - 0xFF -- unused */
+
+
+#define MAX_BETWEEN_EXITS 2
+
+/*
+ * Number of effects
+ */
+#define MAX_EFFECTS 128
+#define EFF_WAVE 0x00000001 /* A circle whose radius increase */
+#define EFF_LAST 0x00000002 /* The wave lasts */
+#define EFF_STORM 0x00000004 /* The area follows the player */
+#define EFF_DIR1 0x00000008 /* Directed effect */
+#define EFF_DIR2 0x00000010 /* Directed effect */
+#define EFF_DIR3 0x00000020 /* Directed effect */
+#define EFF_DIR4 0x00000040 /* Directed effect */
+#define EFF_DIR6 0x00000080 /* Directed effect */
+#define EFF_DIR7 0x00000100 /* Directed effect */
+#define EFF_DIR8 0x00000200 /* Directed effect */
+#define EFF_DIR9 0x00000400 /* Directed effect */
+
+/*
+ * Wilderness terrains
+ */
+#define TERRAIN_EDGE 0 /* Edge of the World */
+#define TERRAIN_TOWN 1 /* Town */
+#define TERRAIN_DEEP_WATER 2 /* Deep water */
+#define TERRAIN_SHALLOW_WATER 3 /* Shallow water */
+#define TERRAIN_SWAMP 4 /* Swamp */
+#define TERRAIN_DIRT 5 /* Dirt */
+#define TERRAIN_GRASS 6 /* Grass */
+#define TERRAIN_TREES 7 /* Trees */
+#define TERRAIN_DESERT 8 /* Desert */
+#define TERRAIN_SHALLOW_LAVA 9 /* Shallow lava */
+#define TERRAIN_DEEP_LAVA 10 /* Deep lava */
+#define TERRAIN_MOUNTAIN 11 /* Mountain */
+
+#define MAX_WILD_TERRAIN 18
+
+/*** Artifact indexes (see "lib/edit/a_info.txt") ***/
+
+
+/* Lites */
+#define ART_GALADRIEL 1
+#define ART_ELENDIL 2
+#define ART_THRAIN 3
+#define ART_PALANTIR 202
+#define ART_UNDEATH 200
+#define ART_STONE_LORE 15
+#define ART_PALANTIR_ITHIL 208
+
+/* Amulets */
+#define ART_CARLAMMAS 4
+#define ART_INGWE 5
+#define ART_DWARVES 6
+#define ART_ANCHOR 14
+#define ART_ELESSAR 206
+#define ART_EVENSTAR 207
+
+/* Rings */
+#define ART_FLAR 7
+#define ART_BARAHIR 8
+#define ART_TULKAS 9
+#define ART_NARYA 10
+#define ART_NENYA 11
+#define ART_VILYA 12
+#define ART_POWER 13
+/* 14 used by the anchor of space-time */
+/* 15 used by the stone of lore */
+
+/* Dragon Scale */
+#define ART_RAZORBACK 16
+#define ART_BLADETURNER 17
+#define ART_MEDIATOR 166
+
+/* Hard Armour */
+#define ART_HIMRING 167
+#define ART_SOULKEEPER 19
+#define ART_ISILDUR 20
+#define ART_ROHIRRIM 21
+#define ART_BELEGENNON 22
+#define ART_CELEBORN 23
+#define ART_ARVEDUI 24
+#define ART_CASPANION 25
+
+/* Thunderlord flying suit */
+#define ART_MARDA 26
+#define ART_TRON 27
+
+/* Soft Armour */
+#define ART_THALKETTOTH 28
+
+/* Shields */
+#define ART_THORIN 30
+#define ART_CELEGORM 31
+#define ART_ANARION 32
+#define ART_GILGALAD 169
+#define ART_HARADRIM 176
+
+/* Helms and Crowns */
+#define ART_MORGOTH 34
+#define ART_BERUTHIEL 35
+#define ART_THRANDUIL 36
+#define ART_THENGEL 37
+#define ART_HAMMERHAND 38
+#define ART_DOR 39
+#define ART_HOLHENNETH 40
+#define ART_GORLIM 41
+#define ART_GONDOR 42
+#define ART_KNOWLEDGE 160
+#define ART_NUMENOR 43
+#define ART_CELEBRIMBOR 170
+
+/* Cloaks */
+#define ART_COLLUIN 44
+#define ART_HOLCOLLETH 45
+#define ART_THINGOL 46
+#define ART_THORONGIL 47
+#define ART_COLANNON 48
+#define ART_LUTHIEN 49
+#define ART_TUOR 50
+
+/* Gloves */
+#define ART_CAMBELEG 52
+#define ART_CAMMITHRIM 53
+#define ART_PAURHACH 54
+#define ART_PAURNIMMEN 55
+#define ART_PAURAEGEN 56
+#define ART_PAURNEN 57
+#define ART_CAMLOST 58
+#define ART_FINGOLFIN 59
+#define ART_EOL 178
+
+/* Boots */
+#define ART_FEANOR 60
+#define ART_DAL 61
+#define ART_THROR 62
+
+/* Swords */
+#define ART_NARSIL 164
+#define ART_MAEDHROS 64
+#define ART_ANGRIST 65
+#define ART_NARTHANC 66
+#define ART_NIMTHANC 67
+#define ART_DETHANC 68
+#define ART_RILIA 69
+#define ART_BELANGIL 70
+#define ART_CALRIS 71
+#define ART_ARUNRUTH 72
+#define ART_GLAMDRING 73
+#define ART_AEGLIN 74
+#define ART_ORCRIST 75
+#define ART_GURTHANG 76
+#define ART_ZARCUTHRA 77
+#define ART_MORMEGIL 78
+#define ART_GONDRICAM 79
+#define ART_CRISDURIAN 80
+#define ART_AGLARANG 81
+#define ART_RINGIL 82
+#define ART_ANDURIL 83
+#define ART_ANGUIREL 84
+#define ART_ELVAGIL 85
+#define ART_FORASGIL 86
+#define ART_CARETH 87
+#define ART_STING 88
+#define ART_HARADEKKET 89
+#define ART_GILETTAR 90
+#define ART_DOOMCALLER 91
+#define ART_VORPAL_BLADE 92
+#define ART_ERU 147
+
+/* Polearms */
+#define ART_THEODEN 93
+#define ART_PAIN 94
+#define ART_OSONDIR 95
+#define ART_TIL 96
+#define ART_AEGLOS 97
+#define ART_OROME 98
+#define ART_NIMLOTH 99
+#define ART_EORLINGAS 100
+#define ART_DURIN 101
+#define ART_EONWE 102
+#define ART_BALLI 103
+#define ART_LOTHARANG 104
+#define ART_MUNDWINE 105
+#define ART_BARUKKHELED 106
+#define ART_WRATH 107
+#define ART_ULMO 108
+#define ART_AVAVIR 109
+#define ART_FUNDIN 175
+
+/* The sword of the Dawn */
+#define ART_DAWN 110
+
+/* Hafted */
+#define ART_MELKOR 18
+#define ART_HURIN 33
+#define ART_GROND 111
+#define ART_TOTILA 112
+#define ART_THUNDERFIST 113
+#define ART_BLOODSPIKE 114
+#define ART_FIRESTAR 115
+#define ART_TARATOL 116
+#define ART_AULE 117
+#define ART_NAR 118
+#define ART_ERIRIL 119
+#define ART_OLORIN 120
+#define ART_DEATHWREAKER 121
+#define ART_TURMIL 122
+#define ART_GOTHMOG 123
+#define ART_AXE_GOTHMOG 145
+#define ART_SKULLCLEAVER 177
+
+#define ART_NAIN 174
+
+/* Bows */
+#define ART_BELTHRONDING 124
+#define ART_BARD 125
+#define ART_CUBRAGOL 126
+#define ART_UMBAR 171
+
+/* Mage Staffs */
+#define ART_GANDALF 127
+
+/* Boomerangs */
+#define ART_BEOR 128
+#define ART_GLIMDRIR 129
+
+/* Musical Instrument */
+#define ART_MAGLOR 137
+#define ART_SKY 138
+#define ART_DAERON 139
+#define ART_DRUEDAIN 141
+#define ART_ROHAN 142
+#define ART_HELM 143
+#define ART_BOROMIR 144
+
+/* Diggers */
+#define ART_EREBOR 140
+
+#define ART_ORCHAST 156
+#define ART_NIGHT 157
+#define ART_NATUREBANE 158
+
+/* Spell for various object */
+#define SPELL_ID_PLAIN 1
+#define SPELL_BO_FIRE 2
+#define SPELL_BO_COLD 3
+#define SPELL_BO_ELEC 4
+#define SPELL_BO_POIS 5
+#define SPELL_BO_ACID 6
+#define SPELL_MAPPING 7
+#define SPELL_CURE 8
+#define SPELL_ID_FULL 9
+#define SPELL_WRAITH 10
+#define SPELL_INVIS 11
+
+
+/*** Ego-Item indexes (see "lib/edit/e_info.txt") ***/
+
+#define EGO_MANA 1
+#define EGO_POWER 2
+#define EGO_MANA_POWER 3
+#define EGO_MSTAFF_SPELL 4
+#define EGO_BRAND_POIS 77
+#define EGO_BRAND_ELEC 74
+#define EGO_BRAND_FIRE 75
+#define EGO_BRAND_COLD 76
+#define EGO_BRAND_ACID 73
+#define EGO_EARTHQUAKES 80
+#define EGO_BLESS_BLADE 67
+#define EGO_VAMPIRIC 97
+#define EGO_CHAOTIC 78
+#define EGO_BLASTED 127
+#define EGO_SHATTERED 126
+#define EGO_FLAME 122
+#define EGO_FROST 123
+#define EGO_LIGHTNING_BOLT 121
+#define EGO_FREE_ACTION 49
+#define EGO_DF 66
+#define EGO_MOTION 59
+#define EGO_SPECTRAL 102
+#define EGO_NOLDOR 23
+#define EGO_JUMP 15
+#define EGO_SPINING 72
+#define EGO_DRAGON 99
+#define EGO_INST_DRAGONKIND 130
+#define EGO_ENDURE_ELEC 17
+#define EGO_ENDURE_ACID 16
+#define EGO_ENDURE_FIRE 18
+#define EGO_ENDURE_COLD 19
+#define EGO_QUIET 58
+#define EGO_WISDOM 25
+#define EGO_RESIST_ELEC 6
+#define EGO_RESIST_ACID 5
+#define EGO_RESIST_FIRE 7
+#define EGO_RESIST_COLD 8
+#define EGO_LIFE 68
+#define EGO_STEALTH 42
+#define EGO_PROTECTION 41
+#define EGO_MAGI 27
+#define EGO_SPEED 60
+#define EGO_RESISTANCE 9
+#define EGO_LITE 32
+#define EGO_AURA_FIRE 44
+#define EGO_SLAYING_WEAPON 71
+#define EGO_SLAYING 50
+#define EGO_TELEPORTATION 35
+#define EGO_ELVENKIND 10
+#define EGO_LITE_MAGI 163
+#define EGO_HA 65
+
+
+/* Activation effects for random artifacts */
+#define ACT_SUNLIGHT 1
+#define ACT_BO_MISS_1 2
+#define ACT_BA_POIS_1 3
+#define ACT_BO_ELEC_1 4
+#define ACT_BO_ACID_1 5
+#define ACT_BO_COLD_1 6
+#define ACT_BO_FIRE_1 7
+#define ACT_BA_COLD_1 8
+#define ACT_BA_FIRE_1 9
+#define ACT_DRAIN_1 10
+#define ACT_BA_COLD_2 11
+#define ACT_BA_ELEC_2 12
+#define ACT_DRAIN_2 13
+#define ACT_VAMPIRE_1 14
+#define ACT_BO_MISS_2 15
+#define ACT_BA_FIRE_2 16
+#define ACT_BA_COLD_3 17
+#define ACT_BA_ELEC_3 18
+#define ACT_WHIRLWIND 19
+#define ACT_VAMPIRE_2 20
+#define ACT_CALL_CHAOS 21
+#define ACT_ROCKET 22
+#define ACT_DISP_EVIL 23
+#define ACT_BA_MISS_3 24
+#define ACT_DISP_GOOD 25
+#define ACT_GILGALAD 26
+#define ACT_CELEBRIMBOR 27
+#define ACT_SKULLCLEAVER 28
+#define ACT_HARADRIM 29
+#define ACT_FUNDIN 30
+#define ACT_EOL 31
+#define ACT_UMBAR 32
+#define ACT_NUMENOR 33
+#define ACT_KNOWLEDGE 34
+#define ACT_UNDEATH 35
+#define ACT_THRAIN 36
+#define ACT_BARAHIR 37
+#define ACT_TULKAS 38
+#define ACT_NARYA 39
+#define ACT_NENYA 40
+#define ACT_VILYA 41
+#define ACT_POWER 42
+#define ACT_STONE_LORE 43
+#define ACT_RAZORBACK 44
+#define ACT_BLADETURNER 45
+#define ACT_MEDIATOR 46
+#define ACT_BELEGENNON 47
+#define ACT_GORLIM 48
+#define ACT_COLLUIN 49
+#define ACT_BELANGIL 50
+#define ACT_CONFUSE 51
+#define ACT_SLEEP 52
+#define ACT_QUAKE 53
+#define ACT_TERROR 54
+#define ACT_TELE_AWAY 55
+#define ACT_BANISH_EVIL 56
+#define ACT_GENOCIDE 57
+#define ACT_MASS_GENO 58
+#define ACT_ANGUIREL 59
+#define ACT_ERU 60
+#define ACT_DAWN 61
+#define ACT_FIRESTAR 62
+#define ACT_TURMIL 63
+#define ACT_CUBRAGOL 64
+#define ACT_CHARM_ANIMAL 65
+#define ACT_CHARM_UNDEAD 66
+#define ACT_CHARM_OTHER 67
+#define ACT_CHARM_ANIMALS 68
+#define ACT_CHARM_OTHERS 69
+#define ACT_SUMMON_ANIMAL 70
+#define ACT_SUMMON_PHANTOM 71
+#define ACT_SUMMON_ELEMENTAL 72
+#define ACT_SUMMON_DEMON 73
+#define ACT_SUMMON_UNDEAD 74
+#define ACT_ELESSAR 75
+#define ACT_GANDALF 76
+#define ACT_MARDA 77
+#define ACT_PALANTIR 78
+/*
+ 79
+ 80
+*/
+#define ACT_CURE_LW 81
+#define ACT_CURE_MW 82
+#define ACT_CURE_POISON 83
+#define ACT_REST_LIFE 84
+#define ACT_REST_ALL 85
+#define ACT_CURE_700 86
+#define ACT_CURE_1000 87
+/*
+ 88
+*/
+#define ACT_EREBOR 89
+#define ACT_DRUEDAIN 90
+#define ACT_ESP 91
+#define ACT_BERSERK 92
+#define ACT_PROT_EVIL 93
+#define ACT_RESIST_ALL 94
+#define ACT_SPEED 95
+#define ACT_XTRA_SPEED 96
+#define ACT_WRAITH 97
+#define ACT_INVULN 98
+#define ACT_ROHAN 99
+#define ACT_HELM 100
+#define ACT_BOROMIR 101
+#define ACT_HURIN 102
+#define ACT_AXE_GOTHMOG 103
+#define ACT_MELKOR 104
+#define ACT_GROND 105
+#define ACT_NATUREBANE 106
+#define ACT_NIGHT 107
+#define ACT_ORCHAST 108
+#define ACT_LIGHT 111
+#define ACT_MAP_LIGHT 112
+#define ACT_DETECT_ALL 113
+#define ACT_DETECT_XTRA 114
+#define ACT_ID_FULL 115
+#define ACT_ID_PLAIN 116
+#define ACT_RUNE_EXPLO 117
+#define ACT_RUNE_PROT 118
+#define ACT_SATIATE 119
+#define ACT_DEST_DOOR 120
+#define ACT_STONE_MUD 121
+#define ACT_RECHARGE 122
+#define ACT_ALCHEMY 123
+#define ACT_DIM_DOOR 124
+#define ACT_TELEPORT 125
+#define ACT_RECALL 126
+
+#define ACT_DEATH 127
+#define ACT_RUINATION 128
+#define ACT_DESTRUC 129
+#define ACT_UNINT 130
+#define ACT_UNSTR 131
+#define ACT_UNCON 132
+#define ACT_UNCHR 133
+#define ACT_UNDEX 134
+#define ACT_UNWIS 135
+#define ACT_STATLOSS 136
+#define ACT_HISTATLOSS 137
+#define ACT_EXPLOSS 138
+#define ACT_HIEXPLOSS 139
+#define ACT_SUMMON_MONST 140
+#define ACT_PARALYZE 141
+#define ACT_HALLU 142
+#define ACT_POISON 143
+#define ACT_HUNGER 144
+#define ACT_STUN 145
+#define ACT_CUTS 146
+#define ACT_PARANO 147
+#define ACT_CONFUSION 148
+#define ACT_BLIND 149
+#define ACT_PET_SUMMON 150
+#define ACT_CURE_PARA 151
+#define ACT_CURE_HALLU 152
+#define ACT_CURE_POIS 153
+#define ACT_CURE_HUNGER 154
+#define ACT_CURE_STUN 155
+#define ACT_CURE_CUTS 156
+#define ACT_CURE_FEAR 157
+#define ACT_CURE_CONF 158
+#define ACT_CURE_BLIND 159
+#define ACT_CURING 160
+#define ACT_DARKNESS 161
+#define ACT_LEV_TELE 162
+#define ACT_ACQUIREMENT 163
+#define ACT_WEIRD 164
+#define ACT_AGGRAVATE 165
+#define ACT_MUT 166
+#define ACT_CURE_INSANITY 167
+#define ACT_CURE_MUT 168
+#define ACT_LIGHT_ABSORBTION 169
+/* of dragonkind?*/
+#define ACT_BA_FIRE_H 170
+#define ACT_BA_COLD_H 171
+#define ACT_BA_ELEC_H 172
+#define ACT_BA_ACID_H 173
+#define ACT_SPIN 174
+#define ACT_NOLDOR 175
+#define ACT_SPECTRAL 176
+#define ACT_JUMP 177
+#define ACT_DEST_TELE 178
+/*amulet of serpents dam 100, rad 2 timout 40+d60 */
+#define ACT_BA_POIS_4 179
+/*rings of X 50,50+d50 dur 20+d20 */
+#define ACT_BA_COLD_4 180
+#define ACT_BA_FIRE_4 181
+#define ACT_BA_ACID_4 182
+#define ACT_BA_ELEC_4 183
+#define ACT_BR_ELEC 184
+#define ACT_BR_COLD 185
+#define ACT_BR_FIRE 186
+#define ACT_BR_ACID 187
+#define ACT_BR_POIS 188
+#define ACT_BR_MANY 189
+#define ACT_BR_CONF 190
+#define ACT_BR_SOUND 191
+#define ACT_BR_CHAOS 192
+#define ACT_BR_SHARD 193
+#define ACT_BR_BALANCE 194
+#define ACT_BR_LIGHT 195
+#define ACT_BR_POWER 196
+#define ACT_GROW_MOLD 197
+#define ACT_MUSIC 200
+/* 170 -> unused */
+
+/*** Object "tval" and "sval" codes ***/
+
+
+/*
+ * The values for the "tval" field of various objects.
+ *
+ * This value is the primary means by which items are sorted in the
+ * player inventory, followed by "sval" and "cost".
+ *
+ * Note that a "BOW" with tval = 19 and sval S = 10*N+P takes a missile
+ * weapon with tval = 16+N, and does (xP) damage when so combined. This
+ * fact is not actually used in the source, but it kind of interesting.
+ *
+ * Note that as of 2.7.8, the "item flags" apply to all items, though
+ * only armor and weapons and a few other items use any of these flags.
+ */
+
+#define TV_SKELETON 1 /* Skeletons ('s') */
+#define TV_BOTTLE 2 /* Empty bottles ('!') */
+/* XXX */
+#define TV_BATERIE 4 /* For the Alchemists */
+#define TV_SPIKE 5 /* Spikes ('~') */
+#define TV_MSTAFF 6 /* Mage Staffs */
+#define TV_CHEST 7 /* Chests ('~') */
+#define TV_PARCHMENT 8 /* Parchments from Kamband */
+#define TV_PARCHEMENT 8 /* compatibility define */
+#define TV_CORPSE 9 /* Monster corpses */
+#define TV_EGG 10 /* Monster Eggs */
+#define TV_JUNK 11 /* Sticks, Pottery, etc ('~') */
+#define TV_TOOL 12 /* Tools */
+#define TV_INSTRUMENT 14 /* Musical instruments */
+#define TV_BOOMERANG 15 /* Boomerangs */
+#define TV_SHOT 16 /* Ammo for slings */
+#define TV_ARROW 17 /* Ammo for bows */
+#define TV_BOLT 18 /* Ammo for x-bows */
+#define TV_BOW 19 /* Slings/Bows/Xbows */
+#define TV_DIGGING 20 /* Shovels/Picks */
+#define TV_HAFTED 21 /* Priest Weapons */
+#define TV_POLEARM 22 /* Pikes/Glaives/Spears/etc. */
+#define TV_SWORD 23 /* Edged Weapons */
+#define TV_AXE 24 /* Axes/Cleavers */
+#define TV_BOOTS 30 /* Boots */
+#define TV_GLOVES 31 /* Gloves */
+#define TV_HELM 32 /* Helms */
+#define TV_CROWN 33 /* Crowns */
+#define TV_SHIELD 34 /* Shields */
+#define TV_CLOAK 35 /* Cloaks */
+#define TV_SOFT_ARMOR 36 /* Soft Armor */
+#define TV_HARD_ARMOR 37 /* Hard Armor */
+#define TV_DRAG_ARMOR 38 /* Dragon Scale Mail */
+#define TV_LITE 39 /* Lites (including Specials) */
+#define TV_AMULET 40 /* Amulets (including Specials) */
+#define TV_RING 45 /* Rings (including Specials) */
+#define TV_TRAPKIT 46 /* Trapkits */
+#define TV_TOTEM 54 /* Summoner totems */
+#define TV_STAFF 55
+#define TV_WAND 65
+#define TV_ROD 66
+#define TV_ROD_MAIN 67
+#define TV_SCROLL 70
+#define TV_POTION 71
+#define TV_POTION2 72 /* Second set of potion */
+#define TV_FLASK 77
+#define TV_FOOD 80
+#define TV_HYPNOS 99 /* To wield monsters !:) */
+#define TV_GOLD 100 /* Gold can only be picked up by players */
+#define TV_RANDART 102 /* Random Artifacts */
+#define TV_RUNE1 104 /* Base runes */
+#define TV_RUNE2 105 /* Modifier runes */
+
+#define TV_BOOK 111
+#define TV_SYMBIOTIC_BOOK 112
+#define TV_MUSIC_BOOK 113
+#define TV_DRUID_BOOK 114
+#define TV_DAEMON_BOOK 115
+
+
+/* The "sval" codes for TV_JUNK */
+#define SV_BOULDER 1
+
+
+/* The "sval" codes for TV_TOOL */
+#define SV_TOOL_CLIMB 0
+#define SV_PORTABLE_HOLE 1
+
+/* The "sval" codes for TV_MSTAFF */
+#define SV_MSTAFF 1
+
+/* The "sval" codes for TV_SHOT/TV_ARROW/TV_BOLT */
+#define SV_AMMO_LIGHT 0 /* pebbles */
+#define SV_AMMO_NORMAL 1 /* shots, arrows, bolts */
+#define SV_AMMO_HEAVY 2 /* seeker arrows and bolts, mithril shots */
+
+/* The "sval" codes for TV_INSTRUMENT */
+#define SV_DRUM 58
+#define SV_HARP 59
+#define SV_HORN 60
+
+/* The "sval" codes for TV_TRAPKIT */
+#define SV_TRAPKIT_SLING 1
+#define SV_TRAPKIT_BOW 2
+#define SV_TRAPKIT_XBOW 3
+#define SV_TRAPKIT_POTION 4
+#define SV_TRAPKIT_SCROLL 5
+#define SV_TRAPKIT_DEVICE 6
+
+/* The "sval" codes for TV_BOOMERANG */
+#define SV_BOOM_S_WOOD 1 /* 1d4 */
+#define SV_BOOM_WOOD 2 /* 1d8 */
+#define SV_BOOM_S_METAL 3 /* 3d4 */
+#define SV_BOOM_METAL 4 /* 4d5 */
+
+/* The "sval" codes for TV_BOW (note information in "sval") */
+#define SV_SLING 2 /* (x2) */
+#define SV_SHORT_BOW 12 /* (x2) */
+#define SV_LONG_BOW 13 /* (x3) */
+#define SV_LIGHT_XBOW 23 /* (x3) */
+#define SV_HEAVY_XBOW 24 /* (x4) */
+
+/* The "sval" codes for TV_DIGGING */
+#define SV_SHOVEL 1
+#define SV_GNOMISH_SHOVEL 2
+#define SV_DWARVEN_SHOVEL 3
+#define SV_PICK 4
+#define SV_ORCISH_PICK 5
+#define SV_DWARVEN_PICK 6
+#define SV_MATTOCK 7
+
+/* The "sval" values for TV_HAFTED */
+#define SV_CLUB 1 /* 1d4 */
+#define SV_WHIP 2 /* 1d6 */
+#define SV_QUARTERSTAFF 3 /* 1d9 */
+#define SV_NUNCHAKU 4 /* 2d3 */
+#define SV_MACE 5 /* 2d4 */
+#define SV_BALL_AND_CHAIN 6 /* 2d4 */
+#define SV_WAR_HAMMER 8 /* 3d3 */
+#define SV_LUCERN_HAMMER 10 /* 2d5 */
+#define SV_THREE_PIECE_ROD 11 /* 3d3 */
+#define SV_MORNING_STAR 12 /* 2d6 */
+#define SV_FLAIL 13 /* 2d6 */
+#define SV_LEAD_FILLED_MACE 15 /* 3d4 */
+#define SV_TWO_HANDED_FLAIL 18 /* 3d6 */
+#define SV_GREAT_HAMMER 19 /* 4d6 */
+#define SV_MACE_OF_DISRUPTION 20 /* 5d8 */
+#define SV_GROND 50 /* 3d4 */
+
+/* The "sval" values for TV_AXE */
+#define SV_HATCHET 1 /* 1d5 */
+#define SV_CLEAVER 2 /* 2d4 */
+#define SV_LIGHT_WAR_AXE 8 /* 2d5 */
+#define SV_BEAKED_AXE 10 /* 2d6 */
+#define SV_BROAD_AXE 11 /* 2d6 */
+#define SV_BATTLE_AXE 22 /* 2d8 */
+#define SV_GREAT_AXE 25 /* 4d4 */
+#define SV_LOCHABER_AXE 28 /* 3d8 */
+#define SV_SLAUGHTER_AXE 30 /* 5d7 */
+
+/* The "sval" values for TV_POLEARM */
+#define SV_SPEAR 2 /* 1d6 */
+#define SV_SICKLE 3 /* 2d3 */
+#define SV_AWL_PIKE 4 /* 1d8 */
+#define SV_TRIDENT 5 /* 1d9 */
+#define SV_FAUCHARD 6 /* 1d10 */
+#define SV_BROAD_SPEAR 7 /* 1d9 */
+#define SV_PIKE 8 /* 2d5 */
+#define SV_GLAIVE 13 /* 2d6 */
+#define SV_HALBERD 15 /* 3d4 */
+#define SV_GUISARME 16 /* 2d5 */
+#define SV_SCYTHE 17 /* 5d3 */
+#define SV_LANCE 20 /* 2d8 */
+#define SV_TRIFURCATE_SPEAR 26 /* 2d9 */
+#define SV_HEAVY_LANCE 29 /* 4d8 */
+#define SV_SCYTHE_OF_SLICING 30 /* 8d4 */
+
+/* The "sval" codes for TV_SWORD */
+#define SV_BROKEN_DAGGER 1 /* 1d1 */
+#define SV_BROKEN_SWORD 2 /* 1d2 */
+#define SV_DAGGER 4 /* 1d4 */
+#define SV_MAIN_GAUCHE 5 /* 1d5 */
+#define SV_RAPIER 7 /* 1d6 */
+#define SV_SMALL_SWORD 8 /* 1d6 */
+#define SV_BASILLARD 9 /* 1d8 */
+#define SV_SHORT_SWORD 10 /* 1d7 */
+#define SV_SABRE 11 /* 1d7 */
+#define SV_CUTLASS 12 /* 1d7 */
+#define SV_KHOPESH 14 /* 2d4 */
+#define SV_TULWAR 15 /* 2d4 */
+#define SV_BROAD_SWORD 16 /* 2d5 */
+#define SV_LONG_SWORD 17 /* 2d5 */
+#define SV_SCIMITAR 18 /* 2d5 */
+#define SV_KATANA 20 /* 3d4 */
+#define SV_BASTARD_SWORD 21 /* 3d4 */
+#define SV_GREAT_SCIMITAR 22 /* 4d5 */
+#define SV_CLAYMORE 23 /* 2d8 */
+#define SV_ESPADON 24 /* 2d9 */
+#define SV_TWO_HANDED_SWORD 25 /* 3d6 */
+#define SV_FLAMBERGE 26 /* 3d7 */
+#define SV_EXECUTIONERS_SWORD 28 /* 4d5 */
+#define SV_ZWEIHANDER 29 /* 4d6 */
+#define SV_BLADE_OF_CHAOS 30 /* 6d5 */
+#define SV_BLUESTEEL_BLADE 31 /* 3d9 */
+#define SV_SHADOW_BLADE 32 /* 4d4 */
+#define SV_DARK_SWORD 33 /* 3d7 */
+
+/* The "sval" codes for TV_SHIELD */
+#define SV_SMALL_LEATHER_SHIELD 2
+#define SV_SMALL_METAL_SHIELD 3
+#define SV_LARGE_LEATHER_SHIELD 4
+#define SV_LARGE_METAL_SHIELD 5
+#define SV_DRAGON_SHIELD 6
+#define SV_SHIELD_OF_DEFLECTION 10
+
+/* The "sval" codes for TV_HELM */
+#define SV_HARD_LEATHER_CAP 2
+#define SV_METAL_CAP 3
+#define SV_IRON_HELM 5
+#define SV_STEEL_HELM 6
+#define SV_DRAGON_HELM 7
+#define SV_IRON_CROWN 10
+#define SV_GOLDEN_CROWN 11
+#define SV_JEWELED_CROWN 12
+#define SV_MORGOTH 50
+
+/* The "sval" codes for TV_BOOTS */
+#define SV_PAIR_OF_SOFT_LEATHER_BOOTS 2
+#define SV_PAIR_OF_HARD_LEATHER_BOOTS 3
+#define SV_PAIR_OF_METAL_SHOD_BOOTS 6
+
+/* The "sval" codes for TV_CLOAK */
+#define SV_CLOAK 1
+#define SV_ELVEN_CLOAK 2
+#define SV_FUR_CLOAK 3
+#define SV_SHADOW_CLOAK 6
+#define SV_MIMIC_CLOAK 100
+
+/* The "sval" codes for TV_GLOVES */
+#define SV_SET_OF_LEATHER_GLOVES 1
+#define SV_SET_OF_GAUNTLETS 2
+#define SV_SET_OF_CESTI 5
+
+/* The "sval" codes for TV_SOFT_ARMOR */
+#define SV_FILTHY_RAG 1
+#define SV_ROBE 2
+#define SV_PAPER_ARMOR 3 /* 4 */
+#define SV_SOFT_LEATHER_ARMOR 4
+#define SV_SOFT_STUDDED_LEATHER 5
+#define SV_HARD_LEATHER_ARMOR 6
+#define SV_HARD_STUDDED_LEATHER 7
+#define SV_RHINO_HIDE_ARMOR 8
+#define SV_CORD_ARMOR 9 /* 6 */
+#define SV_PADDED_ARMOR 10 /* 4 */
+#define SV_LEATHER_SCALE_MAIL 11
+#define SV_LEATHER_JACK 12
+#define SV_STONE_AND_HIDE_ARMOR 15 /* 15 */
+#define SV_THUNDERLORD_SUIT 16
+
+/* The "sval" codes for TV_HARD_ARMOR */
+#define SV_RUSTY_CHAIN_MAIL 1 /* 14- */
+#define SV_RING_MAIL 2 /* 12 */
+#define SV_METAL_SCALE_MAIL 3 /* 13 */
+#define SV_CHAIN_MAIL 4 /* 14 */
+#define SV_DOUBLE_RING_MAIL 5 /* 15 */
+#define SV_AUGMENTED_CHAIN_MAIL 6 /* 16 */
+#define SV_DOUBLE_CHAIN_MAIL 7 /* 16 */
+#define SV_BAR_CHAIN_MAIL 8 /* 18 */
+#define SV_METAL_BRIGANDINE_ARMOUR 9 /* 19 */
+#define SV_SPLINT_MAIL 10 /* 19 */
+#define SV_PARTIAL_PLATE_ARMOUR 12 /* 22 */
+#define SV_METAL_LAMELLAR_ARMOUR 13 /* 23 */
+#define SV_FULL_PLATE_ARMOUR 15 /* 25 */
+#define SV_RIBBED_PLATE_ARMOUR 18 /* 28 */
+#define SV_MITHRIL_CHAIN_MAIL 20 /* 28+ */
+#define SV_MITHRIL_PLATE_MAIL 25 /* 35+ */
+#define SV_ADAMANTITE_PLATE_MAIL 30 /* 40+ */
+
+/* The "sval" codes for TV_DRAG_ARMOR */
+#define SV_DRAGON_BLACK 1
+#define SV_DRAGON_BLUE 2
+#define SV_DRAGON_WHITE 3
+#define SV_DRAGON_RED 4
+#define SV_DRAGON_GREEN 5
+#define SV_DRAGON_MULTIHUED 6
+#define SV_DRAGON_SHINING 10
+#define SV_DRAGON_LAW 12
+#define SV_DRAGON_BRONZE 14
+#define SV_DRAGON_GOLD 16
+#define SV_DRAGON_CHAOS 18
+#define SV_DRAGON_BALANCE 20
+#define SV_DRAGON_POWER 30
+
+/* The sval codes for TV_LITE */
+#define SV_LITE_TORCH 0
+#define SV_LITE_LANTERN 1
+#define SV_LITE_TORCH_EVER 2
+#define SV_LITE_DWARVEN 3
+#define SV_LITE_FEANORIAN 4
+#define SV_LITE_GALADRIEL 100
+#define SV_LITE_ELENDIL 101
+#define SV_LITE_THRAIN 102
+#define SV_LITE_UNDEATH 103
+#define SV_LITE_PALANTIR 104
+#define SV_ANCHOR_SPACETIME 105
+#define SV_STONE_LORE 106
+
+
+/* The "sval" codes for TV_AMULET */
+#define SV_AMULET_DOOM 0
+#define SV_AMULET_TELEPORT 1
+#define SV_AMULET_ADORNMENT 2
+#define SV_AMULET_SLOW_DIGEST 3
+#define SV_AMULET_RESIST_ACID 4
+#define SV_AMULET_SEARCHING 5
+#define SV_AMULET_BRILLANCE 6
+#define SV_AMULET_CHARISMA 7
+#define SV_AMULET_THE_MAGI 8
+#define SV_AMULET_REFLECTION 9
+#define SV_AMULET_CARLAMMAS 10
+#define SV_AMULET_INGWE 11
+#define SV_AMULET_DWARVES 12
+#define SV_AMULET_NO_MAGIC 13
+#define SV_AMULET_NO_TELE 14
+#define SV_AMULET_RESISTANCE 15
+#define SV_AMULET_NOTHING 16
+#define SV_AMULET_SERPENT 17
+#define SV_AMULET_TORIS_MEJISTOS 18
+#define SV_AMULET_ELESSAR 19
+#define SV_AMULET_EVENSTAR 20
+#define SV_AMULET_SUSTENANCE 21
+#define SV_AMULET_TELEPATHY 22
+#define SV_AMULET_TRICKERY 23
+#define SV_AMULET_WEAPONMASTERY 24
+#define SV_AMULET_DEVOTION 25
+#define SV_AMULET_INFRA 26
+#define SV_AMULET_SPELL 27
+#define SV_AMULET_WISDOM 28
+#define SV_AMULET_RESIST_ELEC 29
+#define SV_AMULET_REGEN 30
+
+/* The sval codes for TV_RING */
+#define SV_RING_WOE 0
+#define SV_RING_AGGRAVATION 1
+#define SV_RING_WEAKNESS 2
+#define SV_RING_STUPIDITY 3
+#define SV_RING_TELEPORTATION 4
+#define SV_RING_SPECIAL 5
+#define SV_RING_SLOW_DIGESTION 6
+#define SV_RING_FEATHER_FALL 7
+#define SV_RING_RESIST_FIRE 8
+#define SV_RING_RESIST_COLD 9
+#define SV_RING_SUSTAIN_STR 10
+#define SV_RING_SUSTAIN_INT 11
+#define SV_RING_SUSTAIN_WIS 12
+#define SV_RING_SUSTAIN_CON 13
+#define SV_RING_SUSTAIN_DEX 14
+#define SV_RING_SUSTAIN_CHR 15
+#define SV_RING_PROTECTION 16
+#define SV_RING_ACID 17
+#define SV_RING_FLAMES 18
+#define SV_RING_ICE 19
+#define SV_RING_RESIST_POIS 20
+#define SV_RING_FREE_ACTION 21
+#define SV_RING_SEE_INVIS 22
+#define SV_RING_SEARCHING 23
+#define SV_RING_STR 24
+#define SV_RING_INT 25
+#define SV_RING_DEX 26
+#define SV_RING_CON 27
+#define SV_RING_ACCURACY 28
+#define SV_RING_DAMAGE 29
+#define SV_RING_SLAYING 30
+#define SV_RING_SPEED 31
+#define SV_RING_BARAHIR 32
+#define SV_RING_TULKAS 33
+#define SV_RING_NARYA 34
+#define SV_RING_NENYA 35
+#define SV_RING_VILYA 36
+#define SV_RING_POWER 37
+#define SV_RING_RES_FEAR 38
+#define SV_RING_RES_LD 39
+#define SV_RING_RES_NETHER 40
+#define SV_RING_RES_NEXUS 41
+#define SV_RING_RES_SOUND 42
+#define SV_RING_RES_CONFUSION 43
+#define SV_RING_RES_SHARDS 44
+#define SV_RING_RES_DISENCHANT 45
+#define SV_RING_RES_CHAOS 46
+#define SV_RING_RES_BLINDNESS 47
+#define SV_RING_LORDLY 48
+#define SV_RING_ATTACKS 49
+#define SV_RING_NOTHING 50
+#define SV_RING_PRECONITION 51
+#define SV_RING_FLAR 52
+#define SV_RING_INVIS 53
+#define SV_RING_FLYING 54
+#define SV_RING_WRAITH 55
+#define SV_RING_ELEC 56
+#define SV_RING_DURIN 57
+#define SV_RING_SPELL 58
+#define SV_RING_CRIT 59
+
+/* The "sval" codes for TV_STAFF */
+#define SV_STAFF_SCHOOL 1
+#define SV_STAFF_NOTHING 2
+
+/* needed for monster traps */
+#define SV_STAFF_LIGHT 3
+#define SV_STAFF_FIERY_SHIELD 4
+#define SV_STAFF_REMOVE_CURSES 5
+#define SV_STAFF_WINGS_WIND 6
+#define SV_STAFF_SHAKE 7
+#define SV_STAFF_DISARM 8
+#define SV_STAFF_TELEPORTATION 9
+#define SV_STAFF_PROBABILITY_TRAVEL 10
+#define SV_STAFF_RECOVERY 11
+#define SV_STAFF_HEALING 12
+#define SV_STAFF_VISION 13
+#define SV_STAFF_IDENTIFY 14
+#define SV_STAFF_SENSE_HIDDEN 15
+#define SV_STAFF_REVEAL_WAYS 16
+#define SV_STAFF_SENSE_MONSTER 17
+#define SV_STAFF_GENOCIDE 18
+#define SV_STAFF_SUMMON 19
+#define SV_STAFF_WISH 20
+#define SV_STAFF_MANA 21
+#define SV_STAFF_MITHRANDIR 22
+
+/* The "sval" codes for TV_WAND */
+#define SV_WAND_SCHOOL 1
+#define SV_WAND_NOTHING 2
+
+/* needed for monster traps */
+#define SV_WAND_MANATHRUST 3
+#define SV_WAND_FIREFLASH 4
+#define SV_WAND_FIREWALL 5
+#define SV_WAND_TIDAL_WAVE 6
+#define SV_WAND_ICE_STORM 7
+#define SV_WAND_NOXIOUS_CLOUD 8
+#define SV_WAND_POISON_BLOOD 9
+#define SV_WAND_THUNDERSTORM 10
+#define SV_WAND_DIG 11
+#define SV_WAND_STONE_PRISON 12
+#define SV_WAND_STRIKE 13
+#define SV_WAND_TELEPORT_AWAY 14
+#define SV_WAND_SUMMON_ANIMAL 15
+#define SV_WAND_MAGELOCK 16
+#define SV_WAND_SLOW_MONSTER 17
+#define SV_WAND_SPEED 18
+#define SV_WAND_BANISHMENT 19
+#define SV_WAND_DISPERSE_MAGIC 20
+#define SV_WAND_CHARM 21
+#define SV_WAND_CONFUSE 22
+#define SV_WAND_DEMON_BLADE 23
+#define SV_WAND_HEAL_MONSTER 24
+#define SV_WAND_HASTE_MONSTER 25
+#define SV_WAND_THRAIN 26
+
+/* The "sval" codes for TV_ROD(Rod Tips) */
+#define SV_ROD_NOTHING 0
+#define SV_ROD_DETECT_DOOR 1
+#define SV_ROD_IDENTIFY 2
+#define SV_ROD_RECALL 3
+#define SV_ROD_ILLUMINATION 4
+#define SV_ROD_MAPPING 5
+#define SV_ROD_DETECTION 6
+#define SV_ROD_PROBING 7
+#define SV_ROD_CURING 8
+#define SV_ROD_HEALING 9
+#define SV_ROD_RESTORATION 10
+#define SV_ROD_SPEED 11
+/* xxx (aimed) */
+#define SV_ROD_TELEPORT_AWAY 13
+#define SV_ROD_DISARMING 14
+#define SV_ROD_LITE 15
+#define SV_ROD_SLEEP_MONSTER 16
+#define SV_ROD_SLOW_MONSTER 17
+#define SV_ROD_DRAIN_LIFE 18
+#define SV_ROD_POLYMORPH 19
+#define SV_ROD_ACID_BOLT 20
+#define SV_ROD_ELEC_BOLT 21
+#define SV_ROD_FIRE_BOLT 22
+#define SV_ROD_COLD_BOLT 23
+#define SV_ROD_ACID_BALL 24
+#define SV_ROD_ELEC_BALL 25
+#define SV_ROD_FIRE_BALL 26
+#define SV_ROD_COLD_BALL 27
+#define SV_ROD_HAVOC 28
+#define SV_ROD_DETECT_TRAP 29
+#define SV_ROD_HOME 30
+
+
+/* The "sval" codes for TV_ROD_MAIN(Rods) */
+/* Note that the sval is the max mana capacity of the rod */
+
+#define SV_ROD_WOODEN 10
+#define SV_ROD_COPPER 20
+#define SV_ROD_IRON 50
+#define SV_ROD_ALUMINIUM 75
+#define SV_ROD_SILVER 100
+#define SV_ROD_GOLDEN 125
+#define SV_ROD_MITHRIL 160
+#define SV_ROD_ADMANTITE 200
+
+
+/* The "sval" codes for TV_SCROLL */
+
+#define SV_SCROLL_DARKNESS 0
+#define SV_SCROLL_AGGRAVATE_MONSTER 1
+#define SV_SCROLL_CURSE_ARMOR 2
+#define SV_SCROLL_CURSE_WEAPON 3
+#define SV_SCROLL_SUMMON_MONSTER 4
+#define SV_SCROLL_SUMMON_UNDEAD 5
+#define SV_SCROLL_SUMMON_MINE 6
+#define SV_SCROLL_TRAP_CREATION 7
+#define SV_SCROLL_PHASE_DOOR 8
+#define SV_SCROLL_TELEPORT 9
+#define SV_SCROLL_TELEPORT_LEVEL 10
+#define SV_SCROLL_WORD_OF_RECALL 11
+#define SV_SCROLL_IDENTIFY 12
+#define SV_SCROLL_STAR_IDENTIFY 13
+#define SV_SCROLL_REMOVE_CURSE 14
+#define SV_SCROLL_STAR_REMOVE_CURSE 15
+#define SV_SCROLL_ENCHANT_ARMOR 16
+#define SV_SCROLL_ENCHANT_WEAPON_TO_HIT 17
+#define SV_SCROLL_ENCHANT_WEAPON_TO_DAM 18
+#define SV_SCROLL_ENCHANT_WEAPON_PVAL 19
+#define SV_SCROLL_STAR_ENCHANT_ARMOR 20
+#define SV_SCROLL_STAR_ENCHANT_WEAPON 21
+#define SV_SCROLL_RECHARGING 22
+#define SV_SCROLL_RESET_RECALL 23
+#define SV_SCROLL_LIGHT 24
+#define SV_SCROLL_MAPPING 25
+#define SV_SCROLL_DETECT_GOLD 26
+#define SV_SCROLL_DETECT_ITEM 27
+#define SV_SCROLL_DETECT_TRAP 28
+#define SV_SCROLL_DETECT_DOOR 29
+#define SV_SCROLL_DETECT_INVIS 30
+#define SV_SCROLL_DIVINATION 31
+#define SV_SCROLL_SATISFY_HUNGER 32
+#define SV_SCROLL_BLESSING 33
+#define SV_SCROLL_HOLY_CHANT 34
+#define SV_SCROLL_HOLY_PRAYER 35
+#define SV_SCROLL_MONSTER_CONFUSION 36
+#define SV_SCROLL_PROTECTION_FROM_EVIL 37
+#define SV_SCROLL_RUNE_OF_PROTECTION 38
+#define SV_SCROLL_TRAP_DOOR_DESTRUCTION 39
+#define SV_SCROLL_DEINCARNATION 40
+#define SV_SCROLL_STAR_DESTRUCTION 41
+#define SV_SCROLL_DISPEL_UNDEAD 42
+#define SV_SCROLL_MASS_RESURECTION 43
+#define SV_SCROLL_GENOCIDE 44
+#define SV_SCROLL_MASS_GENOCIDE 45
+#define SV_SCROLL_ACQUIREMENT 46
+#define SV_SCROLL_STAR_ACQUIREMENT 47
+#define SV_SCROLL_FIRE 48
+#define SV_SCROLL_ICE 49
+#define SV_SCROLL_CHAOS 50
+#define SV_SCROLL_RUMOR 51
+#define SV_SCROLL_ARTIFACT 52
+#define SV_SCROLL_NOTHING 53
+
+/* The "sval" codes for TV_POTION */
+#define SV_POTION_WATER 0
+#define SV_POTION_APPLE_JUICE 1
+#define SV_POTION_SLIME_MOLD 2
+#define SV_POTION_BLOOD 3
+#define SV_POTION_SLOWNESS 4
+#define SV_POTION_SALT_WATER 5
+#define SV_POTION_POISON 6
+#define SV_POTION_BLINDNESS 7
+#define SV_POTION_INVIS 8
+#define SV_POTION_CONFUSION 9
+#define SV_POTION_MUTATION 10
+#define SV_POTION_SLEEP 11
+#define SV_POTION_LEARNING 12
+#define SV_POTION_LOSE_MEMORIES 13
+/* xxx */
+#define SV_POTION_RUINATION 15
+#define SV_POTION_DEC_STR 16
+#define SV_POTION_DEC_INT 17
+#define SV_POTION_DEC_WIS 18
+#define SV_POTION_DEC_DEX 19
+#define SV_POTION_DEC_CON 20
+#define SV_POTION_DEC_CHR 21
+#define SV_POTION_DETONATIONS 22
+#define SV_POTION_DEATH 23
+#define SV_POTION_INFRAVISION 24
+#define SV_POTION_DETECT_INVIS 25
+#define SV_POTION_SLOW_POISON 26
+#define SV_POTION_CURE_POISON 27
+#define SV_POTION_BOLDNESS 28
+#define SV_POTION_SPEED 29
+#define SV_POTION_RESIST_HEAT 30
+#define SV_POTION_RESIST_COLD 31
+#define SV_POTION_HEROISM 32
+#define SV_POTION_BESERK_STRENGTH 33
+#define SV_POTION_CURE_LIGHT 34
+#define SV_POTION_CURE_SERIOUS 35
+#define SV_POTION_CURE_CRITICAL 36
+#define SV_POTION_HEALING 37
+#define SV_POTION_STAR_HEALING 38
+#define SV_POTION_LIFE 39
+#define SV_POTION_RESTORE_MANA 40
+#define SV_POTION_RESTORE_EXP 41
+#define SV_POTION_RES_STR 42
+#define SV_POTION_RES_INT 43
+#define SV_POTION_RES_WIS 44
+#define SV_POTION_RES_DEX 45
+#define SV_POTION_RES_CON 46
+#define SV_POTION_RES_CHR 47
+#define SV_POTION_INC_STR 48
+#define SV_POTION_INC_INT 49
+#define SV_POTION_INC_WIS 50
+#define SV_POTION_INC_DEX 51
+#define SV_POTION_INC_CON 52
+#define SV_POTION_INC_CHR 53
+/* xxx */
+#define SV_POTION_AUGMENTATION 55
+#define SV_POTION_ENLIGHTENMENT 56
+#define SV_POTION_STAR_ENLIGHTENMENT 57
+#define SV_POTION_SELF_KNOWLEDGE 58
+#define SV_POTION_EXPERIENCE 59
+#define SV_POTION_RESISTANCE 60
+#define SV_POTION_CURING 61
+#define SV_POTION_INVULNERABILITY 62
+#define SV_POTION_NEW_LIFE 63
+
+#define SV_POTION_LAST 63
+
+/* The "sval" codes for TV_POTION2 */
+#define SV_POTION2_MIMIC 1
+#define SV_POTION2_CURE_LIGHT_SANITY 14
+#define SV_POTION2_CURE_SERIOUS_SANITY 15
+#define SV_POTION2_CURE_CRITICAL_SANITY 16
+#define SV_POTION2_CURE_SANITY 17
+#define SV_POTION2_CURE_WATER 18
+
+#define SV_POTION2_LAST 18
+
+/* The "sval" codes for TV_FOOD */
+#define SV_FOOD_POISON 0
+#define SV_FOOD_BLINDNESS 1
+#define SV_FOOD_PARANOIA 2
+#define SV_FOOD_CONFUSION 3
+#define SV_FOOD_HALLUCINATION 4
+#define SV_FOOD_PARALYSIS 5
+#define SV_FOOD_WEAKNESS 6
+#define SV_FOOD_SICKNESS 7
+#define SV_FOOD_STUPIDITY 8
+#define SV_FOOD_NAIVETY 9
+#define SV_FOOD_UNHEALTH 10
+#define SV_FOOD_DISEASE 11
+#define SV_FOOD_CURE_POISON 12
+#define SV_FOOD_CURE_BLINDNESS 13
+#define SV_FOOD_CURE_PARANOIA 14
+#define SV_FOOD_CURE_CONFUSION 15
+#define SV_FOOD_CURE_SERIOUS 16
+#define SV_FOOD_RESTORE_STR 17
+#define SV_FOOD_RESTORE_CON 18
+#define SV_FOOD_RESTORING 19
+/* many missing mushrooms */
+#define SV_FOOD_BISCUIT 32
+#define SV_FOOD_JERKY 33
+#define SV_FOOD_RATION 35
+#define SV_FOOD_SLIME_MOLD 36
+#define SV_FOOD_WAYBREAD 37
+#define SV_FOOD_PINT_OF_ALE 38
+#define SV_FOOD_PINT_OF_WINE 39
+#define SV_FOOD_ATHELAS 40
+#define SV_FOOD_GREAT_HEALTH 41
+#define SV_FOOD_FORTUNE_COOKIE 42
+
+/* The "sval" codes for TV_BATERIE */
+#define SV_BATERIE_POISON 1
+#define SV_BATERIE_EXPLOSION 2
+#define SV_BATERIE_TELEPORT 3
+#define SV_BATERIE_COLD 4
+#define SV_BATERIE_FIRE 5
+#define SV_BATERIE_ACID 6
+#define SV_BATERIE_LIFE 7
+#define SV_BATERIE_CONFUSION 8
+#define SV_BATERIE_LITE 9
+#define SV_BATERIE_CHAOS 10
+#define SV_BATERIE_TIME 11
+#define SV_BATERIE_MAGIC 12
+#define SV_BATERIE_XTRA_LIFE 13
+#define SV_BATERIE_DARKNESS 14
+#define SV_BATERIE_KNOWLEDGE 15
+#define SV_BATERIE_FORCE 16
+#define SV_BATERIE_LIGHTNING 17
+#define SV_BATERIE_MANA 18
+#define MAX_BATERIE_SVAL 18
+
+/* The "sval" codes for TV_CORPSE */
+#define SV_CORPSE_CORPSE 1
+#define SV_CORPSE_SKELETON 2
+#define SV_CORPSE_HEAD 3
+#define SV_CORPSE_SKULL 4
+#define SV_CORPSE_MEAT 5
+
+/* The "sval" codes for TV_DAEMON_BOOK */
+#define SV_DEMONBLADE 55
+#define SV_DEMONSHIELD 56
+#define SV_DEMONHORN 57
+
+/*
+ * Special "sval" limit -- first "normal" food
+ */
+#define SV_FOOD_MIN_FOOD 32
+
+/*
+ * Special "sval" limit -- first "aimed" rod
+ */
+#define SV_ROD_MIN_DIRECTION 12
+
+/*
+ * Special "sval" limit -- first "large" chest
+ */
+#define SV_CHEST_MIN_LARGE 4
+
+/*
+ * Special "sval" limit -- last "good" magic/prayer book
+ */
+#define SV_BOOK_MAX_GOOD 49
+
+/* flags for operation in get_random_trap in object3.c */
+
+#define TRAP_EXISTS 0x00000001L
+#define TRAP_FOUND 0x00000002L
+#define TRAP_NOTFOUND 0x00000004L
+#define TRAP_IDENTIFIED 0x00000008L
+
+/*** General flag values ***/
+
+
+/*
+ * Special cave grid flags
+ */
+#define CAVE_MARK 0x0001 /* memorized feature */
+#define CAVE_GLOW 0x0002 /* self-illuminating */
+#define CAVE_ICKY 0x0004 /* part of a vault */
+#define CAVE_ROOM 0x0008 /* part of a room */
+#define CAVE_SEEN 0x0010 /* seen flag */
+#define CAVE_VIEW 0x0020 /* view flag */
+#define CAVE_TEMP 0x0040 /* temp flag */
+#define CAVE_WALL 0x0080 /* wall flag */
+#define CAVE_TRDT 0x0100 /* trap detected */
+#define CAVE_IDNT 0x0200 /* grid identified (fountains) */
+#define CAVE_SPEC 0x0400 /* special mark(quests) */
+#define CAVE_FREE 0x0800 /* no random generation on it */
+#define CAVE_DETECT 0x1000 /* Traps detected here */
+#define CAVE_PLIT 0x2000 /* Player lit grid */
+#define CAVE_MLIT 0x4000 /* Monster lit grid */
+
+/*
+ * Bit flags for the "project()" function
+ *
+ * JUMP: Jump directly to the target location (this is a hack)
+ * BEAM: Work as a beam weapon (affect every grid passed through)
+ * THRU: Continue "through" the target (used for "bolts"/"beams")
+ * WALL: Continue "through" the walls
+ * STOP: Stop as soon as we hit a monster (used for "bolts")
+ * GRID: Affect each grid in the "blast area" in some way
+ * ITEM: Affect each object in the "blast area" in some way
+ * KILL: Affect each monster in the "blast area" in some way
+ * HIDE: Hack -- disable "visual" feedback from projection
+ */
+#define PROJECT_JUMP 0x00000001
+#define PROJECT_BEAM 0x00000002
+#define PROJECT_THRU 0x00000004
+#define PROJECT_STOP 0x00000008
+#define PROJECT_GRID 0x00000010
+#define PROJECT_ITEM 0x00000020
+#define PROJECT_KILL 0x00000040
+#define PROJECT_HIDE 0x00000080
+#define PROJECT_VIEWABLE 0x00000100 /* Affect monsters in LOS */
+#define PROJECT_METEOR_SHOWER 0x00000200 /* Affect random grids */
+#define PROJECT_BLAST 0x00000400 /* Like Mega_blast, but will only affect viewable grids */
+#define PROJECT_PANEL 0x00000800 /* Affect everything in the panel. */
+#define PROJECT_ALL 0x00001000 /* Affect every single grid. */
+#define PROJECT_WALL 0x00002000
+#define PROJECT_MANA_PATH 0x00004000 /* Follow a mana path. */
+#define PROJECT_ABSORB_MANA 0x00008000 /* The spell increase in power as it absord grid's mana. */
+#define PROJECT_STAY 0x00010000
+
+/*
+ * Bit flags for the "enchant()" function
+ */
+#define ENCH_TOHIT 0x01
+#define ENCH_TODAM 0x02
+#define ENCH_TOAC 0x04
+#define ENCH_PVAL 0x08
+
+/*
+ * Bit flags for the "target_set" function XXX XXX XXX
+ *
+ * KILL: Target monsters
+ * LOOK: Describe grid fully
+ * XTRA: Currently unused flag
+ * GRID: Select from all grids
+ */
+#define TARGET_KILL 0x01
+#define TARGET_LOOK 0x02
+#define TARGET_XTRA 0x04
+#define TARGET_GRID 0x08
+
+
+/*
+ * Some bit-flags for the "smart" field
+ */
+#define SM_RES_ACID 0x00000001
+#define SM_RES_ELEC 0x00000002
+#define SM_RES_FIRE 0x00000004
+#define SM_RES_COLD 0x00000008
+#define SM_RES_POIS 0x00000010
+#define SM_RES_NETH 0x00000020
+#define SM_RES_LITE 0x00000040
+#define SM_RES_DARK 0x00000080
+#define SM_RES_FEAR 0x00000100
+#define SM_RES_CONF 0x00000200
+#define SM_RES_CHAOS 0x00000400
+#define SM_RES_DISEN 0x00000800
+#define SM_RES_BLIND 0x00001000
+#define SM_RES_NEXUS 0x00002000
+#define SM_RES_SOUND 0x00004000
+#define SM_RES_SHARD 0x00008000
+#define SM_OPP_ACID 0x00010000
+#define SM_OPP_ELEC 0x00020000
+#define SM_OPP_FIRE 0x00040000
+#define SM_OPP_COLD 0x00080000
+#define SM_OPP_POIS 0x00100000
+#define SM_OPP_XXX1 0x00200000
+#define SM_CLONED 0x00400000
+#define SM_NOTE_TRAP 0x00800000
+#define SM_IMM_ACID 0x01000000
+#define SM_IMM_ELEC 0x02000000
+#define SM_IMM_FIRE 0x04000000
+#define SM_IMM_COLD 0x08000000
+#define SM_IMM_XXX5 0x10000000
+#define SM_IMM_REFLECT 0x20000000
+#define SM_IMM_FREE 0x40000000
+#define SM_IMM_MANA 0x80000000
+
+/*
+ * Monster status(Player POV)
+ */
+#define MSTATUS_ENEMY -2
+#define MSTATUS_NEUTRAL_M -1
+#define MSTATUS_NEUTRAL 0
+#define MSTATUS_NEUTRAL_P 1
+#define MSTATUS_FRIEND 2
+#define MSTATUS_PET 3
+#define MSTATUS_COMPANION 4
+
+/*
+ * Bit flags for the "get_item" function
+ */
+#define USE_EQUIP 0x01 /* Allow equip items */
+#define USE_INVEN 0x02 /* Allow inven items */
+#define USE_FLOOR 0x04 /* Allow floor items */
+#define USE_EXTRA 0x08 /* Allow extra items */
+#define USE_AUTO 0x10 /* Allow creation of automatizer rule */
+/*
+ * Bit flags for the "p_ptr->notice" variable
+ */
+#define PN_COMBINE 0x00000001L /* Combine the pack */
+#define PN_REORDER 0x00000002L /* Reorder the pack */
+/* xxx (many) */
+
+
+/*
+ * Bit flags for the "p_ptr->update" variable
+ */
+#define PU_BONUS 0x00000001L /* Calculate bonuses */
+#define PU_TORCH 0x00000002L /* Calculate torch radius */
+#define PU_BODY 0x00000004L /* Calculate body parts */
+#define PU_SANITY 0x00000008L /* Calculate csan and msan */
+#define PU_HP 0x00000010L /* Calculate chp and mhp */
+#define PU_MANA 0x00000020L /* Calculate csp and msp */
+#define PU_SPELLS 0x00000040L /* Calculate spells */
+#define PU_POWERS 0x00000080L /* Calculate powers */
+/* xxx (many) */
+#define PU_UN_VIEW 0x00010000L /* Forget view */
+/* xxx (many) */
+#define PU_VIEW 0x00100000L /* Update view */
+#define PU_MON_LITE 0x00200000L /* Update monster light */
+/* xxx */
+#define PU_MONSTERS 0x01000000L /* Update monsters */
+#define PU_DISTANCE 0x02000000L /* Update distances */
+/* xxx */
+#define PU_FLOW 0x10000000L /* Update flow */
+/* xxx (many) */
+
+
+/*
+ * Bit flags for the "p_ptr->redraw" variable
+ */
+#define PR_MISC 0x00000001L /* Display Race/Class */
+#define PR_TITLE 0x00000002L /* Display Title */
+#define PR_LEV 0x00000004L /* Display Level */
+#define PR_EXP 0x00000008L /* Display Experience */
+#define PR_STATS 0x00000010L /* Display Stats */
+#define PR_ARMOR 0x00000020L /* Display Armor */
+#define PR_HP 0x00000040L /* Display Hitpoints */
+#define PR_MANA 0x00000080L /* Display Mana */
+#define PR_GOLD 0x00000100L /* Display Gold */
+#define PR_DEPTH 0x00000200L /* Display Depth */
+/****/
+#define PR_HEALTH 0x00000800L /* Display Health Bar */
+#define PR_CUT 0x00001000L /* Display Extra (Cut) */
+#define PR_STUN 0x00002000L /* Display Extra (Stun) */
+#define PR_HUNGER 0x00004000L /* Display Extra (Hunger) */
+#define PR_PIETY 0x00008000L /* Display Piety */
+#define PR_BLIND 0x00010000L /* Display Extra (Blind) */
+#define PR_CONFUSED 0x00020000L /* Display Extra (Confused) */
+#define PR_AFRAID 0x00040000L /* Display Extra (Afraid) */
+#define PR_POISONED 0x00080000L /* Display Extra (Poisoned) */
+#define PR_STATE 0x00100000L /* Display Extra (State) */
+#define PR_SPEED 0x00200000L /* Display Extra (Speed) */
+#define PR_STUDY 0x00400000L /* Display Extra (Study) */
+#define PR_SANITY 0x00800000L /* Display Sanity */
+#define PR_EXTRA 0x01000000L /* Display Extra Info */
+#define PR_BASIC 0x02000000L /* Display Basic Info */
+#define PR_MAP 0x04000000L /* Display Map */
+#define PR_WIPE 0x08000000L /* Hack -- Total Redraw */
+#define PR_MH 0x10000000L /* Display Monster hitpoints */
+#define PR_DTRAP 0x20000000L /* Display Extra (DTrap) */
+/* xxx */
+/* xxx */
+
+/*
+ * Bit flags for the "p_ptr->window" variable (etc)
+ */
+#define PW_INVEN 0x00000001L /* Display inven/equip */
+#define PW_EQUIP 0x00000002L /* Display equip/inven */
+/* xxx */
+#define PW_PLAYER 0x00000008L /* Display character */
+#define PW_M_LIST 0x00000010L /* Show monster list */
+#define PW_IRC 0x00000020L /* Display irc messages */
+#define PW_MESSAGE 0x00000040L /* Display messages */
+#define PW_OVERHEAD 0x00000080L /* Display overhead view */
+#define PW_MONSTER 0x00000100L /* Display monster recall */
+#define PW_OBJECT 0x00000200L /* Display object recall */
+/* xxx */
+#define PW_SNAPSHOT 0x00000800L /* Display snap-shot */
+/* xxx */
+/* xxx */
+#define PW_BORG_1 0x00004000L /* Display borg messages */
+#define PW_BORG_2 0x00008000L /* Display borg status */
+
+
+/* jk */
+#define FTRAP_CHEST 0x000000001 /* may appear on chests */
+#define FTRAP_DOOR 0x000000002 /* may appear on doors/floors */
+#define FTRAP_FLOOR 0x000000004 /* may appear on floor */
+#define FTRAP_CHANGE 0x000000008 /* Color changing */
+#define FTRAP_XXX5 0x000000010
+#define FTRAP_XXX6 0x000000020
+#define FTRAP_XXX7 0x000000040
+#define FTRAP_XXX8 0x000000080
+#define FTRAP_XXX9 0x000000100
+#define FTRAP_XXX10 0x000000200
+#define FTRAP_XXX11 0x000000400
+#define FTRAP_XXX12 0x000000800
+#define FTRAP_XXX13 0x000001000
+#define FTRAP_XXX14 0x000002000
+#define FTRAP_XXX15 0x000004000
+#define FTRAP_XXX16 0x000008000
+#define FTRAP_LEVEL1 0x000010000 /* low level ball/bolt trap */
+#define FTRAP_LEVEL2 0x000020000 /* medium level ball/bolt trap */
+#define FTRAP_LEVEL3 0x000040000 /* high level ball/bolt trap */
+#define FTRAP_LEVEL4 0x000080000 /* oops level ball/bolt trap */
+#define FTRAP_XXX21 0x000100000
+#define FTRAP_XXX22 0x000200000
+#define FTRAP_XXX23 0x000400000
+#define FTRAP_XXX24 0x000800000
+#define FTRAP_XXX25 0x001000000
+#define FTRAP_XXX26 0x002000000
+#define FTRAP_XXX27 0x004000000
+#define FTRAP_XXX28 0x008000000
+#define FTRAP_XXX29 0x010000000
+#define FTRAP_XXX30 0x020000000
+#define FTRAP_XXX31 0x040000000
+#define FTRAP_XXX32 0x080000000
+
+/* jk */
+#define STAT_DEC_TEMPORARY 1
+#define STAT_DEC_NORMAL 2
+#define STAT_DEC_PERMANENT 3
+
+/* jk - which trap is which number */
+#define TRAP_OF_WEAKNESS_I 1
+#define TRAP_OF_WEAKNESS_II 2
+#define TRAP_OF_WEAKNESS_III 3
+#define TRAP_OF_INTELLIGENCE_I 4
+#define TRAP_OF_INTELLIGENCE_II 5
+#define TRAP_OF_INTELLIGENCE_III 6
+#define TRAP_OF_WISDOM_I 7
+#define TRAP_OF_WISDOM_II 8
+#define TRAP_OF_WISDOM_III 9
+#define TRAP_OF_FUMBLING_I 10
+#define TRAP_OF_FUMBLING_II 11
+#define TRAP_OF_FUMBLING_III 12
+#define TRAP_OF_WASTING_I 13
+#define TRAP_OF_WASTING_II 14
+#define TRAP_OF_WASTING_III 15
+#define TRAP_OF_BEAUTY_I 16
+#define TRAP_OF_BEAUTY_II 17
+#define TRAP_OF_BEAUTY_III 18
+
+#define TRAP_OF_CURSE_WEAPON 20
+#define TRAP_OF_CURSE_ARMOR 21
+#define TRAP_OF_EARTHQUAKE 22
+#define TRAP_OF_POISON_NEEDLE 23
+#define TRAP_OF_SUMMON_MONSTER 24
+#define TRAP_OF_SUMMON_UNDEAD 25
+#define TRAP_OF_SUMMON_GREATER_UNDEAD 26
+#define TRAP_OF_TELEPORT 27
+#define TRAP_OF_PARALYZING 28
+#define TRAP_OF_EXPLOSIVE_DEVICE 29
+#define TRAP_OF_TELEPORT_AWAY 30
+#define TRAP_OF_LOSE_MEMORY 31
+#define TRAP_OF_BITTER_REGRET 32
+#define TRAP_OF_BOWEL_CRAMPS 33
+#define TRAP_OF_BLINDNESS_CONFUSION 34
+#define TRAP_OF_AGGRAVATION 35
+#define TRAP_OF_MULTIPLICATION 36
+#define TRAP_OF_STEAL_ITEM 37
+#define TRAP_OF_SUMMON_FAST_QUYLTHULGS 38
+#define TRAP_OF_SINKING 39
+#define TRAP_OF_MANA_DRAIN 40
+#define TRAP_OF_MISSING_MONEY 41
+#define TRAP_OF_NO_RETURN 42
+#define TRAP_OF_SILENT_SWITCHING 43
+#define TRAP_OF_WALLS 44
+#define TRAP_OF_CALLING_OUT 45
+#define TRAP_OF_SLIDING 46
+#define TRAP_OF_CHARGES_DRAIN 47
+#define TRAP_OF_STAIR_MOVEMENT 48
+#define TRAP_OF_NEW 49
+#define TRAP_OF_SCATTER_ITEMS 50
+#define TRAP_OF_DECAY 51
+#define TRAP_OF_WASTING_WANDS 52
+#define TRAP_OF_FILLING 53
+#define TRAP_OF_DRAIN_SPEED 54
+
+#define TRAP_OF_ELEC_BOLT 60
+#define TRAP_OF_POIS_BOLT 61
+#define TRAP_OF_ACID_BOLT 62
+#define TRAP_OF_COLD_BOLT 63
+#define TRAP_OF_FIRE_BOLT 64
+#define TRAP_OF_PLASMA_BOLT 65
+#define TRAP_OF_WATER_BOLT 66
+#define TRAP_OF_LITE_BOLT 67
+#define TRAP_OF_DARK_BOLT 68
+#define TRAP_OF_SHARDS_BOLT 69
+#define TRAP_OF_SOUND_BOLT 70
+#define TRAP_OF_CONFUSION_BOLT 71
+#define TRAP_OF_FORCE_BOLT 72
+#define TRAP_OF_INERTIA_BOLT 73
+#define TRAP_OF_MANA_BOLT 74
+#define TRAP_OF_ICE_BOLT 75
+#define TRAP_OF_CHAOS_BOLT 76
+#define TRAP_OF_NETHER_BOLT 77
+#define TRAP_OF_DISENCHANT_BOLT 78
+#define TRAP_OF_NEXUS_BOLT 79
+#define TRAP_OF_TIME_BOLT 80
+#define TRAP_OF_GRAVITY_BOLT 81
+
+#define TRAP_OF_ELEC_BALL 82
+#define TRAP_OF_POIS_BALL 83
+#define TRAP_OF_ACID_BALL 84
+#define TRAP_OF_COLD_BALL 85
+#define TRAP_OF_FIRE_BALL 86
+#define TRAP_OF_PLASMA_BALL 87
+#define TRAP_OF_WATER_BALL 88
+#define TRAP_OF_LITE_BALL 89
+#define TRAP_OF_DARK_BALL 90
+#define TRAP_OF_SHARDS_BALL 91
+#define TRAP_OF_SOUND_BALL 92
+#define TRAP_OF_CONFUSION_BALL 93
+#define TRAP_OF_FORCE_BALL 94
+#define TRAP_OF_INERTIA_BALL 95
+#define TRAP_OF_MANA_BALL 96
+#define TRAP_OF_ICE_BALL 97
+#define TRAP_OF_CHAOS_BALL 98
+#define TRAP_OF_NETHER_BALL 99
+#define TRAP_OF_DISENCHANT_BALL 100
+#define TRAP_OF_NEXUS_BALL 101
+#define TRAP_OF_TIME_BALL 102
+#define TRAP_OF_GRAVITY_BALL 103
+
+#define TRAP_OF_ARROW_I 110
+#define TRAP_OF_ARROW_II 111
+#define TRAP_OF_ARROW_III 112
+#define TRAP_OF_ARROW_IV 113
+#define TRAP_OF_POISON_ARROW_I 114
+#define TRAP_OF_POISON_ARROW_II 115
+#define TRAP_OF_POISON_ARROW_III 116
+#define TRAP_OF_POISON_ARROW_IV 117
+#define TRAP_OF_DAGGER_I 118
+#define TRAP_OF_DAGGER_II 119
+#define TRAP_OF_POISON_DAGGER_I 120
+#define TRAP_OF_POISON_DAGGER_II 121
+#define TRAP_OF_ARROWS_I 122
+#define TRAP_OF_ARROWS_II 123
+#define TRAP_OF_ARROWS_III 124
+#define TRAP_OF_ARROWS_IV 125
+#define TRAP_OF_POISON_ARROWS_I 126
+#define TRAP_OF_POISON_ARROWS_II 127
+#define TRAP_OF_POISON_ARROWS_III 128
+#define TRAP_OF_POISON_ARROWS_IV 129
+#define TRAP_OF_DAGGERS_I 130
+#define TRAP_OF_DAGGERS_II 131
+#define TRAP_OF_POISON_DAGGERS_I 132
+#define TRAP_OF_POISON_DAGGERS_II 133
+
+#define TRAP_OF_DROP_ITEMS 140
+#define TRAP_OF_DROP_ALL_ITEMS 141
+#define TRAP_OF_DROP_EVERYTHING 142
+
+/* -SC- */
+#define TRAP_OF_FEMINITY 150
+#define TRAP_OF_MASCULINITY 151
+#define TRAP_OF_NEUTRALITY 152
+#define TRAP_OF_AGING 153
+#define TRAP_OF_GROWING 154
+#define TRAP_OF_SHRINKING 155
+#define TRAP_OF_ELDRITCH_HORROR 156
+/* XXX */
+#define TRAP_OF_DIVINE_ANGER 158
+#define TRAP_OF_DIVINE_WRATH 159
+#define TRAP_OF_HALLUCINATION 160
+
+#define TRAP_OF_ROCKET 161
+#define TRAP_OF_NUKE_BOLT 162
+#define TRAP_OF_DEATH_RAY 163
+#define TRAP_OF_HOLY_FIRE 164
+#define TRAP_OF_HELL_FIRE 165
+#define TRAP_OF_PSI_BOLT 166
+#define TRAP_OF_PSI_DRAIN 167
+#define TRAP_OF_NUKE_BALL 168
+#define TRAP_OF_PSI_BALL 169
+
+/* DG */
+#define TRAP_OF_ACQUIREMENT 170
+
+/* Runescrye */
+#define TRAP_G_ELEC_BOLT 171
+#define TRAP_G_POIS_BOLT 172
+#define TRAP_G_ACID_BOLT 173
+#define TRAP_G_COLD_BOLT 174
+#define TRAP_G_FIRE_BOLT 175
+
+/*** General index values ***/
+
+
+/*
+ * Legal restrictions for "summon_specific()"
+ */
+#define SUMMON_ANT 11
+#define SUMMON_SPIDER 12
+#define SUMMON_HOUND 13
+#define SUMMON_HYDRA 14
+#define SUMMON_ANGEL 15
+#define SUMMON_DEMON 16
+#define SUMMON_UNDEAD 17
+#define SUMMON_DRAGON 18
+#define SUMMON_HI_UNDEAD 21
+#define SUMMON_HI_DRAGON 22
+#define SUMMON_WRAITH 31
+#define SUMMON_UNIQUE 32
+#define SUMMON_BIZARRE1 33
+#define SUMMON_BIZARRE2 34
+#define SUMMON_BIZARRE3 35
+#define SUMMON_BIZARRE4 36
+#define SUMMON_BIZARRE5 37
+#define SUMMON_BIZARRE6 38
+#define SUMMON_HI_DEMON 39
+#define SUMMON_KIN 40
+#define SUMMON_DAWN 41
+#define SUMMON_ANIMAL 42
+#define SUMMON_ANIMAL_RANGER 43
+#define SUMMON_HI_UNDEAD_NO_UNIQUES 44
+#define SUMMON_HI_DRAGON_NO_UNIQUES 45
+#define SUMMON_NO_UNIQUES 46
+#define SUMMON_PHANTOM 47
+#define SUMMON_ELEMENTAL 48
+#define SUMMON_THUNDERLORD 49
+#define SUMMON_BLUE_HORROR 50
+#define SUMMON_BUG 51
+#define SUMMON_RNG 52
+#define SUMMON_MINE 53
+#define SUMMON_HUMAN 54
+#define SUMMON_SHADOWS 55
+#define SUMMON_GHOST 56
+#define SUMMON_QUYLTHULG 57
+#define SUMMON_LUA 58
+
+
+/*
+ * Spell types used by project(), and related functions.
+ */
+#define GF_ELEC 1
+#define GF_POIS 2
+#define GF_ACID 3
+#define GF_COLD 4
+#define GF_FIRE 5
+#define GF_UNBREATH 6
+#define GF_CORPSE_EXPL 7
+#define GF_MISSILE 10
+#define GF_ARROW 11
+#define GF_PLASMA 12
+#define GF_WAVE 13
+#define GF_WATER 14
+#define GF_LITE 15
+#define GF_DARK 16
+#define GF_LITE_WEAK 17
+#define GF_DARK_WEAK 18
+#define GF_SHARDS 20
+#define GF_SOUND 21
+#define GF_CONFUSION 22
+#define GF_FORCE 23
+#define GF_INERTIA 24
+#define GF_MANA 26
+#define GF_METEOR 27
+#define GF_ICE 28
+#define GF_CHAOS 30
+#define GF_NETHER 31
+#define GF_DISENCHANT 32
+#define GF_NEXUS 33
+#define GF_TIME 34
+#define GF_GRAVITY 35
+#define GF_KILL_WALL 40
+#define GF_KILL_DOOR 41
+#define GF_KILL_TRAP 42
+#define GF_MAKE_WALL 45
+#define GF_MAKE_DOOR 46
+#define GF_MAKE_TRAP 47
+#define GF_OLD_CLONE 51
+#define GF_OLD_POLY 52
+#define GF_OLD_HEAL 53
+#define GF_OLD_SPEED 54
+#define GF_OLD_SLOW 55
+#define GF_OLD_CONF 56
+#define GF_OLD_SLEEP 57
+#define GF_OLD_DRAIN 58
+#define GF_AWAY_UNDEAD 61
+#define GF_AWAY_EVIL 62
+#define GF_AWAY_ALL 63
+#define GF_TURN_UNDEAD 64
+#define GF_TURN_EVIL 65
+#define GF_TURN_ALL 66
+#define GF_DISP_UNDEAD 67
+#define GF_DISP_EVIL 68
+#define GF_DISP_ALL 69
+#define GF_DISP_DEMON 70 /* New types for Zangband begin here... */
+#define GF_DISP_LIVING 71
+#define GF_ROCKET 72
+#define GF_NUKE 73
+#define GF_MAKE_GLYPH 74
+#define GF_STASIS 75
+#define GF_STONE_WALL 76
+#define GF_DEATH_RAY 77
+#define GF_STUN 78
+#define GF_HOLY_FIRE 79
+#define GF_HELL_FIRE 80
+#define GF_DISINTEGRATE 81
+#define GF_CHARM 82
+#define GF_CONTROL_UNDEAD 83
+#define GF_CONTROL_ANIMAL 84
+#define GF_PSI 85
+#define GF_PSI_DRAIN 86
+#define GF_TELEKINESIS 87
+#define GF_JAM_DOOR 88
+#define GF_DOMINATION 89
+#define GF_DISP_GOOD 90
+#define GF_IDENTIFY 91
+#define GF_RAISE 92
+#define GF_STAR_IDENTIFY 93
+#define GF_DESTRUCTION 94
+#define GF_STUN_CONF 95
+#define GF_STUN_DAM 96
+#define GF_CONF_DAM 98
+#define GF_STAR_CHARM 99
+#define GF_IMPLOSION 100
+#define GF_LAVA_FLOW 101
+#define GF_FEAR 102
+#define GF_BETWEEN_GATE 103
+#define GF_WINDS_MANA 104
+#define GF_DEATH 105
+#define GF_CONTROL_DEMON 106
+#define GF_RAISE_DEMON 107
+#define GF_TRAP_DEMONSOUL 108
+#define GF_ATTACK 109
+#define GF_CHARM_UNMOVING 110
+#define MAX_GF 111
+
+/*
+ * Some things which induce learning
+ */
+#define DRS_ACID 1
+#define DRS_ELEC 2
+#define DRS_FIRE 3
+#define DRS_COLD 4
+#define DRS_POIS 5
+#define DRS_NETH 6
+#define DRS_LITE 7
+#define DRS_DARK 8
+#define DRS_FEAR 9
+#define DRS_CONF 10
+#define DRS_CHAOS 11
+#define DRS_DISEN 12
+#define DRS_BLIND 13
+#define DRS_NEXUS 14
+#define DRS_SOUND 15
+#define DRS_SHARD 16
+#define DRS_FREE 30
+#define DRS_MANA 31
+#define DRS_REFLECT 32
+
+
+
+/*
+ * Hack -- first "normal" artifact in the artifact list. All of
+ * the artifacts with indexes from 1 to 15 are "special" (lights,
+ * rings, amulets), and the ones from 16 to 127 are "normal".
+ */
+#define ART_MIN_NORMAL 16
+#define ART_MIN_SPECIAL 200
+
+
+/*
+ * Hack -- special "xtra" object powers
+ */
+
+/* Sustain one stat */
+#define EGO_XTRA_SUSTAIN 1
+
+/* High resist */
+#define EGO_XTRA_POWER 2
+
+/* Special ability */
+#define EGO_XTRA_ABILITY 3
+
+/*** Object flag values ***/
+
+
+/*
+ * Special Object Flags
+ */
+#define IDENT_SENSE 0x01 /* Item has been "sensed" */
+#define IDENT_FIXED 0x02 /* Item has been "haggled" */
+#define IDENT_EMPTY 0x04 /* Item charges are known */
+#define IDENT_KNOWN 0x08 /* Item abilities are known */
+#define IDENT_STOREB 0x10 /* Item is storebought !!!! */
+#define IDENT_MENTAL 0x20 /* Item information is known */
+#define IDENT_CURSED 0x40 /* Item is temporarily cursed */
+
+
+
+/*
+ * Special Monster Flags
+ */
+#define MFLAG_VIEW 0x00000001 /* Monster is in line of sight */
+#define MFLAG_QUEST 0x00000002 /* Monster is subject to a quest */
+#define MFLAG_PARTIAL 0x00000004 /* Monster is a partial summon */
+#define MFLAG_CONTROL 0x00000008 /* Monster is controlled */
+#define MFLAG_BORN 0x00000010 /* Monster is still being born */
+#define MFLAG_NICE 0x00000020 /* Monster is still being nice */
+#define MFLAG_SHOW 0x00000040 /* Monster is recently memorized */
+#define MFLAG_MARK 0x00000080 /* Monster is currently memorized */
+#define MFLAG_NO_DROP 0x00000100 /* Monster wont drop obj/corpse */
+#define MFLAG_QUEST2 0x00000200 /* Monster is subject to a quest */
+#define PERM_MFLAG_MASK ( \
+ MFLAG_QUEST | MFLAG_QUEST2 | \
+ MFLAG_PARTIAL | MFLAG_CONTROL | MFLAG_NO_DROP \
+ )
+
+
+/*
+ * As of 2.7.8, the "object flags" are valid for all objects, and as
+ * of 2.7.9, these flags are not actually stored with the object.
+ *
+ * Note that "flags1" contains all flags dependant on "pval" (including
+ * stat bonuses, but NOT stat sustainers), plus all "extra attack damage"
+ * flags (SLAY_XXX and BRAND_XXX).
+ *
+ * Note that "flags2" contains all "resistances" (including "Stat Sustainers",
+ * actual immunities, and resistances). Note that "Hold Life" is really an
+ * "immunity" to ExpLoss, and "Free Action" is "immunity to paralysis".
+ *
+ * Note that "flags3" contains everything else -- including the three "CURSED"
+ * flags, and the "BLESSED" flag, several "item display" parameters, some new
+ * flags for powerful Bows, and flags which affect the player in a "general"
+ * way (LITE, TELEPATHY, SEE_INVIS, SLOW_DIGEST, REGEN, FEATHER), including
+ * all the "general" curses (TELEPORT, AGGRAVATE, EXP_DRAIN). It also has
+ * four new flags called "ITEM_IGNORE_XXX" which lets an item specify that
+ * it can not be affected by various forms of destruction. This is NOT as
+ * powerful as actually granting resistance/immunity to the wearer.
+ */
+
+#define TR1_STR 0x00000001L /* STR += "pval" */
+#define TR1_INT 0x00000002L /* INT += "pval" */
+#define TR1_WIS 0x00000004L /* WIS += "pval" */
+#define TR1_DEX 0x00000008L /* DEX += "pval" */
+#define TR1_CON 0x00000010L /* CON += "pval" */
+#define TR1_CHR 0x00000020L /* CHR += "pval" */
+#define TR1_MANA 0x00000040L /* Mana multipler */
+#define TR1_SPELL 0x00000080L /* Spell power increase */
+#define TR1_STEALTH 0x00000100L /* Stealth += "pval" */
+#define TR1_SEARCH 0x00000200L /* Search += "pval" */
+#define TR1_INFRA 0x00000400L /* Infra += "pval" */
+#define TR1_TUNNEL 0x00000800L /* Tunnel += "pval" */
+#define TR1_SPEED 0x00001000L /* Speed += "pval" */
+#define TR1_BLOWS 0x00002000L /* Blows += "pval" */
+#define TR1_CHAOTIC 0x00004000L
+#define TR1_VAMPIRIC 0x00008000L
+#define TR1_SLAY_ANIMAL 0x00010000L
+#define TR1_SLAY_EVIL 0x00020000L
+#define TR1_SLAY_UNDEAD 0x00040000L
+#define TR1_SLAY_DEMON 0x00080000L
+#define TR1_SLAY_ORC 0x00100000L
+#define TR1_SLAY_TROLL 0x00200000L
+#define TR1_SLAY_GIANT 0x00400000L
+#define TR1_SLAY_DRAGON 0x00800000L
+#define TR1_KILL_DRAGON 0x01000000L /* Execute Dragon */
+#define TR1_VORPAL 0x02000000L /* Later */
+#define TR1_IMPACT 0x04000000L /* Cause Earthquakes */
+#define TR1_BRAND_POIS 0x08000000L
+#define TR1_BRAND_ACID 0x10000000L
+#define TR1_BRAND_ELEC 0x20000000L
+#define TR1_BRAND_FIRE 0x40000000L
+#define TR1_BRAND_COLD 0x80000000L
+#define TR1_NULL_MASK 0x00000000L
+
+#define TRAP2_AUTOMATIC_5 0x00000001L /* Trap automatically rearms itself, 1 in 5 failure */
+#define TRAP2_AUTOMATIC_99 0x00000002L /* Trap automatically rearms itself */
+#define TRAP2_KILL_GHOST 0x00000004L /* Trap also affects PASS_WALL creatures */
+#define TRAP2_TELEPORT_TO 0x00000008L /* After everything else, teleport to player */
+#define TRAP2_ONLY_DRAGON 0x00000010L /* Affect only dragons & other AFFECTed creatures */
+#define TRAP2_ONLY_DEMON 0x00000020L /* Affect only demons & other AFFECTed creatures */
+#define TRAP2_ONLY_ANIMAL 0x00000100L /* Affect only animals & other AFFECTed creatures */
+#define TRAP2_ONLY_UNDEAD 0x00000200L /* Affect only undead & others */
+#define TRAP2_ONLY_EVIL 0x00000400L /* Affect only evil creatures &c. */
+
+#define TRAP2_ONLY_MASK (TRAP2_ONLY_DRAGON | TRAP2_ONLY_DEMON | TRAP2_ONLY_ANIMAL | \
+ TRAP2_ONLY_UNDEAD | TRAP2_ONLY_EVIL )
+
+#define TR2_SUST_STR 0x00000001L
+#define TR2_SUST_INT 0x00000002L
+#define TR2_SUST_WIS 0x00000004L
+#define TR2_SUST_DEX 0x00000008L
+#define TR2_SUST_CON 0x00000010L
+#define TR2_SUST_CHR 0x00000020L
+#define TR2_INVIS 0x00000040L /* Invisibility */
+#define TR2_LIFE 0x00000080L /* Life multiplier */
+#define TR2_IM_ACID 0x00000100L
+#define TR2_IM_ELEC 0x00000200L
+#define TR2_IM_FIRE 0x00000400L
+#define TR2_IM_COLD 0x00000800L
+#define TR2_SENS_FIRE 0x00001000L /* Sensibility to fire */
+#define TR2_REFLECT 0x00002000L /* Reflect 'bolts' */
+#define TR2_FREE_ACT 0x00004000L /* Free Action */
+#define TR2_HOLD_LIFE 0x00008000L /* Hold Life */
+#define TR2_RES_ACID 0x00010000L
+#define TR2_RES_ELEC 0x00020000L
+#define TR2_RES_FIRE 0x00040000L
+#define TR2_RES_COLD 0x00080000L
+#define TR2_RES_POIS 0x00100000L
+#define TR2_RES_FEAR 0x00200000L
+#define TR2_RES_LITE 0x00400000L
+#define TR2_RES_DARK 0x00800000L
+#define TR2_RES_BLIND 0x01000000L
+#define TR2_RES_CONF 0x02000000L
+#define TR2_RES_SOUND 0x04000000L
+#define TR2_RES_SHARDS 0x08000000L
+#define TR2_RES_NETHER 0x10000000L
+#define TR2_RES_NEXUS 0x20000000L
+#define TR2_RES_CHAOS 0x40000000L
+#define TR2_RES_DISEN 0x80000000L
+#define TR2_NULL_MASK 0x00000000L
+
+#define TR3_SH_FIRE 0x00000001L /* Immolation (Fire) */
+#define TR3_SH_ELEC 0x00000002L /* Electric Sheath */
+#define TR3_AUTO_CURSE 0x00000004L /* The obj will recurse itself */
+#define TR3_DECAY 0x00000008L /* Decay */
+#define TR3_NO_TELE 0x00000010L /* Anti-teleportation */
+#define TR3_NO_MAGIC 0x00000020L /* Anti-magic */
+#define TR3_WRAITH 0x00000040L /* Wraithform */
+#define TR3_TY_CURSE 0x00000080L /* The Ancient Curse */
+#define TR3_EASY_KNOW 0x00000100L /* Aware -> Known */
+#define TR3_HIDE_TYPE 0x00000200L /* Hide "pval" description */
+#define TR3_SHOW_MODS 0x00000400L /* Always show Tohit/Todam */
+#define TR3_INSTA_ART 0x00000800L /* Item must be an artifact */
+#define TR3_FEATHER 0x00001000L /* Feather Falling */
+#define TR3_LITE1 0x00002000L /* lite radius 1 */
+#define TR3_SEE_INVIS 0x00004000L /* See Invisible */
+#define TR3_NORM_ART 0x00008000L /* Artifact in k_info */
+#define TR3_SLOW_DIGEST 0x00010000L /* Item slows down digestion */
+#define TR3_REGEN 0x00020000L /* Item induces regeneration */
+#define TR3_XTRA_MIGHT 0x00040000L /* Bows get extra multiplier */
+#define TR3_XTRA_SHOTS 0x00080000L /* Bows get extra shots */
+#define TR3_IGNORE_ACID 0x00100000L /* Item ignores Acid Damage */
+#define TR3_IGNORE_ELEC 0x00200000L /* Item ignores Elec Damage */
+#define TR3_IGNORE_FIRE 0x00400000L /* Item ignores Fire Damage */
+#define TR3_IGNORE_COLD 0x00800000L /* Item ignores Cold Damage */
+#define TR3_ACTIVATE 0x01000000L /* Item can be activated */
+#define TR3_DRAIN_EXP 0x02000000L /* Item drains Experience */
+#define TR3_TELEPORT 0x04000000L /* Item teleports player */
+#define TR3_AGGRAVATE 0x08000000L /* Item aggravates monsters */
+#define TR3_BLESSED 0x10000000L /* Item is Blessed */
+#define TR3_CURSED 0x20000000L /* Item is Cursed */
+#define TR3_HEAVY_CURSE 0x40000000L /* Item is Heavily Cursed */
+#define TR3_PERMA_CURSE 0x80000000L /* Item is Perma Cursed */
+#define TR3_NULL_MASK 0x00000000L
+
+
+#define TR4_NEVER_BLOW 0x00000001L /* Weapon can't attack */
+#define TR4_PRECOGNITION 0x00000002L /* Like activating the cheat mode */
+#define TR4_BLACK_BREATH 0x00000004L /* Tolkien's Black Breath */
+#define TR4_RECHARGE 0x00000008L /* For artifact Wands and Staffs */
+#define TR4_FLY 0x00000010L /* This one and ONLY this one allow you to fly over trees */
+#define TR4_DG_CURSE 0x00000020L /* The Ancient Morgothian Curse */
+#define TR4_COULD2H 0x00000040L /* Can wield it 2 Handed */
+#define TR4_MUST2H 0x00000080L /* Must wield it 2 Handed */
+#define TR4_LEVELS 0x00000100L /* Can gain exp/exp levels !! */
+#define TR4_CLONE 0x00000200L /* Can clone monsters */
+#define TR4_SPECIAL_GENE 0x00000400L /* The object can only be generated in special conditions like quests, special dungeons, ... */
+#define TR4_CLIMB 0x00000800L /* Allow climbing mountains */
+#define TR4_FAST_CAST 0x00001000L /* Rod is x2 time faster to use */
+#define TR4_CAPACITY 0x00002000L /* Rod can take x2 mana */
+#define TR4_CHARGING 0x00004000L /* Rod recharge faster */
+#define TR4_CHEAPNESS 0x00008000L /* Rod spells are cheaper(in mana cost) to cast */
+#define TR4_FOUNTAIN 0x00010000L /* Available as fountain (for potions) */
+#define TR4_ANTIMAGIC_50 0x00020000L /* Forbid magic */
+#define TR4_ANTIMAGIC_30 0x00040000L /* Forbid magic */
+#define TR4_ANTIMAGIC_20 0x00080000L /* Forbid magic */
+#define TR4_ANTIMAGIC_10 0x00100000L /* Forbid magic */
+#define TR4_EASY_USE 0x00200000L /* Easily activable */
+#define TR4_IM_NETHER 0x00400000L /* Immunity to nether */
+#define TR4_RECHARGED 0x00800000L /* Object has been recharged once */
+#define TR4_ULTIMATE 0x01000000L /* ULTIMATE artifact */
+#define TR4_AUTO_ID 0x02000000L /* Id stuff on floor */
+#define TR4_LITE2 0x04000000L /* lite radius 2 */
+#define TR4_LITE3 0x08000000L /* lite radius 3 */
+#define TR4_FUEL_LITE 0x10000000L /* fuelable lite */
+#define TR4_ART_EXP 0x20000000L /* Will accumulate xp */
+#define TR4_CURSE_NO_DROP 0x40000000L /* The obj wont be dropped */
+#define TR4_NO_RECHARGE 0x80000000L /* Object Cannot be recharged */
+#define TR4_NULL_MASK 0xFFFFFFFCL
+
+#define TR5_TEMPORARY 0x00000001L /* In timeout turns it is destroyed */
+#define TR5_DRAIN_MANA 0x00000002L /* Drains mana */
+#define TR5_DRAIN_HP 0x00000004L /* Drains hp */
+#define TR5_KILL_DEMON 0x00000008L /* Execute Demon */
+#define TR5_KILL_UNDEAD 0x00000010L /* Execute Undead */
+#define TR5_CRIT 0x00000020L /* More critical hits */
+#define TR5_ATTR_MULTI 0x00000040L /* Object shimmer -- only allowed in k_info */
+#define TR5_WOUNDING 0x00000080L /* Wounds monsters */
+#define TR5_FULL_NAME 0x00000100L /* Uses direct name from k_info */
+#define TR5_LUCK 0x00000200L /* Luck += pval */
+#define TR5_IMMOVABLE 0x00000400L /* Cannot move */
+#define TR5_SPELL_CONTAIN 0x00000800L /* Can contain a spell */
+#define TR5_RES_MORGUL 0x00001000L /* Is not shattered by morgul fiends(nazguls) */
+#define TR5_ACTIVATE_NO_WIELD 0x00002000L /* Can be 'A'ctivated without being wielded */
+#define TR5_MAGIC_BREATH 0x00004000L /* Can breath anywere */
+#define TR5_WATER_BREATH 0x00008000L /* Can breath underwater */
+#define TR5_WIELD_CAST 0x00010000L /* Need to be wielded to cast spelsl fomr it(if it can be wiekded) */
+
+/* ESP defines */
+#define ESP_ORC 0x00000001L
+#define ESP_TROLL 0x00000002L
+#define ESP_DRAGON 0x00000004L
+#define ESP_GIANT 0x00000008L
+#define ESP_DEMON 0x00000010L
+#define ESP_UNDEAD 0x00000020L
+#define ESP_EVIL 0x00000040L
+#define ESP_ANIMAL 0x00000080L
+#define ESP_THUNDERLORD 0x00000100L
+#define ESP_GOOD 0x00000200L
+#define ESP_NONLIVING 0x00000400L
+#define ESP_UNIQUE 0x00000800L
+#define ESP_SPIDER 0x00001000L
+#define ESP_ALL 0x80000000L
+
+/* Number of group of flags to choose from */
+#define MAX_FLAG_GROUP 12
+#define NEW_GROUP_CHANCE 40 /* Chance to get a new group */
+
+/*
+ * Hack masks for "pval-dependant" flags.
+ */
+#define TR1_PVAL_MASK \
+ (TR1_STR | TR1_INT | TR1_WIS | TR1_DEX | \
+ TR1_CON | TR1_CHR | \
+ TR1_STEALTH | TR1_SEARCH | TR1_INFRA | TR1_TUNNEL | \
+ TR1_SPEED | TR1_BLOWS | TR1_SPELL)
+
+#define TR5_PVAL_MASK \
+ (TR5_CRIT | TR5_LUCK)
+
+
+/*** Ego flags ***/
+#define ETR4_SUSTAIN 0x00000001L /* Ego-Item gives a Random Sustain */
+#define ETR4_OLD_RESIST 0x00000002L /* The old "extra power" random high resist */
+#define ETR4_ABILITY 0x00000004L /* Ego-Item has a random Sustain */
+#define ETR4_R_ELEM 0x00000008L /* Item resists Acid/Fire/Cold/Elec or Poison */
+#define ETR4_R_LOW 0x00000010L /* Item has a random low resist */
+#define ETR4_R_HIGH 0x00000020L /* Item has a random high resist */
+#define ETR4_R_ANY 0x00000040L /* Item has one additional resist */
+#define ETR4_R_DRAGON 0x00000080L /* Item gets "Dragon" Resist */
+#define ETR4_SLAY_WEAP 0x00000100L /* Special 'Slaying' bonus */
+#define ETR4_DAM_DIE 0x00000200L /* Item has an additional dam die */
+#define ETR4_DAM_SIZE 0x00000400L /* Item has greater damage dice */
+#define ETR4_PVAL_M1 0x00000800L /* Item has +1 to pval */
+#define ETR4_PVAL_M2 0x00001000L /* Item has +(up to 2) to pval */
+#define ETR4_PVAL_M3 0x00002000L /* Item has +(up to 3) to pval */
+#define ETR4_PVAL_M5 0x00004000L /* Item has +(up to 5) to pval */
+#define ETR4_AC_M1 0x00008000L /* Item has +1 to AC */
+#define ETR4_AC_M2 0x00010000L /* Item has +(up to 2) to AC */
+#define ETR4_AC_M3 0x00020000L /* Item has +(up to 3) to AC */
+#define ETR4_AC_M5 0x00040000L /* Item has +(up to 5) to AC */
+#define ETR4_TH_M1 0x00080000L /* Item has +1 to hit */
+#define ETR4_TH_M2 0x00100000L /* Item has +(up to 2) to hit */
+#define ETR4_TH_M3 0x00200000L /* Item has +(up to 3) to hit */
+#define ETR4_TH_M5 0x00400000L /* Item has +(up to 5) to hit */
+#define ETR4_TD_M1 0x00800000L /* Item has +1 to dam */
+#define ETR4_TD_M2 0x01000000L /* Item has +(up to 2) to dam */
+#define ETR4_TD_M3 0x02000000L /* Item has +(up to 3) to dam */
+#define ETR4_TD_M5 0x04000000L /* Item has +(up to 5) to dam */
+#define ETR4_R_P_ABILITY 0x08000000L /* Item has a random pval-affected ability */
+#define ETR4_R_STAT 0x10000000L /* Item affects a random stat */
+#define ETR4_R_STAT_SUST 0x20000000L /* Item affects a random stat & sustains it */
+#define ETR4_R_IMMUNITY 0x40000000L /* Item gives a random immunity */
+#define ETR4_LIMIT_BLOWS 0x80000000L /* switch the "limit blows" feature */
+
+/*** Features flags -- DG ***/
+#define FF1_NO_WALK 0x00000001L
+#define FF1_NO_VISION 0x00000002L
+#define FF1_CAN_LEVITATE 0x00000004L
+#define FF1_CAN_PASS 0x00000008L
+#define FF1_FLOOR 0x00000010L
+#define FF1_WALL 0x00000020L
+#define FF1_PERMANENT 0x00000040L
+#define FF1_CAN_FLY 0x00000080L
+#define FF1_REMEMBER 0x00000100L
+#define FF1_NOTICE 0x00000200L
+#define FF1_DONT_NOTICE_RUNNING 0x00000400L
+#define FF1_CAN_RUN 0x00000800L
+#define FF1_DOOR 0x00001000L
+#define FF1_SUPPORT_LIGHT 0x00002000L
+#define FF1_CAN_CLIMB 0x00004000L
+#define FF1_TUNNELABLE 0x00008000L
+#define FF1_WEB 0x00010000L
+#define FF1_ATTR_MULTI 0x00020000L
+#define FF1_SUPPORT_GROWTH 0x00040000L
+
+/*** Dungeon type flags -- DG ***/
+#define DF1_PRINCIPAL 0x00000001L /* Is a principal dungeon */
+#define DF1_MAZE 0x00000002L /* Is a maze-type dungeon */
+#define DF1_SMALLEST 0x00000004L /* Creates VERY small levels like The Maze */
+#define DF1_SMALL 0x00000008L /* Creates small levels like Dol Goldor */
+#define DF1_BIG 0x00000010L /* Creates big levels like Moria, and Angband dungeons */
+#define DF1_NO_DOORS 0x00000020L /* No doors on rooms, like Barrowdowns, Old Forest etc) */
+#define DF1_WATER_RIVER 0x00000040L /* Allow a single water streamer on a level */
+#define DF1_LAVA_RIVER 0x00000080L /* Allow a single lava streamer on a level */
+#define DF1_WATER_RIVERS 0x00000100L /* Allow multiple water streamers on a level */
+#define DF1_LAVA_RIVERS 0x00000200L /* Allow multiple lava streamers on a level */
+#define DF1_CAVE 0x00000400L /* Allow rooms */
+#define DF1_CAVERN 0x00000800L /* Allow cavern rooms */
+#define DF1_NO_UP 0x00001000L /* Disallow up stairs */
+#define DF1_HOT 0x00002000L /* Corpses on ground and in pack decay quicker through heat */
+#define DF1_COLD 0x00004000L /* Corpses on ground and in pack decay quicker through cold */
+#define DF1_FORCE_DOWN 0x00008000L /* No up stairs generated */
+#define DF1_FORGET 0x00010000L /* Features are forgotten, like the Maze and Illusory Castle */
+#define DF1_NO_DESTROY 0x00020000L /* No destroyed levels in dungeon */
+#define DF1_SAND_VEIN 0x00040000L /* Like in the sandworm lair */
+#define DF1_CIRCULAR_ROOMS 0x00080000L /* Allow circular rooms */
+#define DF1_EMPTY 0x00100000L /* Allow arena levels */
+#define DF1_DAMAGE_FEAT 0x00200000L
+#define DF1_FLAT 0x00400000L /* Creates paths to next areas at edge of level, like Barrowdowns */
+#define DF1_TOWER 0x00800000L /* You start at bottom and go up rather than the reverse */
+#define DF1_RANDOM_TOWNS 0x01000000L /* Allow random towns */
+#define DF1_DOUBLE 0x02000000L /* Creates double-walled dungeon like Helcaraxe and Erebor */
+#define DF1_LIFE_LEVEL 0x04000000L /* Creates dungeon level on modified 'game of life' algorithm */
+#define DF1_EVOLVE 0x08000000L /* Evolving, pulsing levels like Heart of the Earth */
+#define DF1_ADJUST_LEVEL_1 0x10000000L /* Minimum monster level will be equal to dungeon level */
+#define DF1_ADJUST_LEVEL_2 0x20000000L /* Minimum monster level will be double the dungeon level */
+#define DF1_NO_RECALL 0x40000000L /* No recall allowed */
+#define DF1_NO_STREAMERS 0x80000000L /* No streamers */
+
+#define DF2_ADJUST_LEVEL_1_2 0x00000001L /* Minimum monster level will be half the dungeon level */
+#define DF2_NO_SHAFT 0x00000002L /* No shafts */
+#define DF2_ADJUST_LEVEL_PLAYER 0x00000004L /* Uses player level*2 instead of dungeon level for other ADJUST_LEVEL flags */
+#define DF2_NO_TELEPORT 0x00000008L
+#define DF2_ASK_LEAVE 0x00000010L
+#define DF2_NO_STAIR 0x00000020L
+#define DF2_SPECIAL 0x00000040L
+#define DF2_NO_NEW_MONSTER 0x00000080L
+#define DF2_DESC 0x00000100L
+#define DF2_NO_GENO 0x00000200L
+#define DF2_NO_BREATH 0x00000400L /* Oups, cannot breath here */
+#define DF2_WATER_BREATH 0x00000800L /* Oups, cannot breath here, need water breathing */
+#define DF2_ELVEN 0x00001000L /* Try to create elven monster ego */
+#define DF2_DWARVEN 0x00002000L /* Try to create dwarven monster ego */
+#define DF2_NO_EASY_MOVE 0x00004000L /* Forbid stuff like teleport level, probability travel, ... */
+#define DF2_NO_RECALL_OUT 0x00008000L /* Cannot recall out of the place */
+#define DF2_DESC_ALWAYS 0x00010000L /* Always shows the desc */
+
+/*** Town flags ***/
+#define TOWN_REAL 0x01 /* Town is really present */
+#define TOWN_KNOWN 0x02 /* Town is found by the player */
+
+
+
+/*** Monster blow constants ***/
+
+#define MODIFY_AUX(o, n) ((o) = modify_aux((o), (n) >> 2, (n) & 3))
+#define MODIFY(o, n, min) MODIFY_AUX(o, n); (o) = ((o) < (min))?(min):(o)
+
+/*
+ * New monster blow methods
+ */
+#define RBM_ANY 0
+#define RBM_HIT 1
+#define RBM_TOUCH 2
+#define RBM_PUNCH 3
+#define RBM_KICK 4
+#define RBM_CLAW 5
+#define RBM_BITE 6
+#define RBM_STING 7
+#define RBM_XXX1 8
+#define RBM_BUTT 9
+#define RBM_CRUSH 10
+#define RBM_ENGULF 11
+#define RBM_CHARGE 12
+#define RBM_CRAWL 13
+#define RBM_DROOL 14
+#define RBM_SPIT 15
+#define RBM_EXPLODE 16
+#define RBM_GAZE 17
+#define RBM_WAIL 18
+#define RBM_SPORE 19
+#define RBM_XXX4 20
+#define RBM_BEG 21
+#define RBM_INSULT 22
+#define RBM_MOAN 23
+#define RBM_SHOW 24
+
+
+/*
+ * New monster blow effects
+ */
+#define RBE_ANY 0
+#define RBE_HURT 1
+#define RBE_POISON 2
+#define RBE_UN_BONUS 3
+#define RBE_UN_POWER 4
+#define RBE_EAT_GOLD 5
+#define RBE_EAT_ITEM 6
+#define RBE_EAT_FOOD 7
+#define RBE_EAT_LITE 8
+#define RBE_ACID 9
+#define RBE_ELEC 10
+#define RBE_FIRE 11
+#define RBE_COLD 12
+#define RBE_BLIND 13
+#define RBE_CONFUSE 14
+#define RBE_TERRIFY 15
+#define RBE_PARALYZE 16
+#define RBE_LOSE_STR 17
+#define RBE_LOSE_INT 18
+#define RBE_LOSE_WIS 19
+#define RBE_LOSE_DEX 20
+#define RBE_LOSE_CON 21
+#define RBE_LOSE_CHR 22
+#define RBE_LOSE_ALL 23
+#define RBE_SHATTER 24
+#define RBE_EXP_10 25
+#define RBE_EXP_20 26
+#define RBE_EXP_40 27
+#define RBE_EXP_80 28
+#define RBE_DISEASE 29
+#define RBE_TIME 30
+#define RBE_SANITY 31
+#define RBE_HALLU 32
+#define RBE_PARASITE 33
+#define RBE_ABOMINATION 34
+
+
+/*** Monster flag values (hard-coded) ***/
+
+#define MONSTER_LEVEL_MAX 150
+#define MONSTER_EXP(level) ((((level) > MONSTER_LEVEL_MAX)?MONSTER_LEVEL_MAX:(level)) * (((level) > MONSTER_LEVEL_MAX)?MONSTER_LEVEL_MAX:(level)) * (((level) > MONSTER_LEVEL_MAX)?MONSTER_LEVEL_MAX:(level)) * 6)
+
+/*
+ * New monster race bit flags
+ */
+#define RF1_UNIQUE 0x00000001 /* Unique Monster */
+#define RF1_QUESTOR 0x00000002 /* Quest Monster */
+#define RF1_MALE 0x00000004 /* Male gender */
+#define RF1_FEMALE 0x00000008 /* Female gender */
+#define RF1_CHAR_CLEAR 0x00000010 /* Absorbs symbol */
+#define RF1_CHAR_MULTI 0x00000020 /* Changes symbol */
+#define RF1_ATTR_CLEAR 0x00000040 /* Absorbs color */
+#define RF1_ATTR_MULTI 0x00000080 /* Changes color */
+#define RF1_FORCE_DEPTH 0x00000100 /* Start at "correct" depth */
+#define RF1_FORCE_MAXHP 0x00000200 /* Start with max hitpoints */
+#define RF1_FORCE_SLEEP 0x00000400 /* Start out sleeping */
+#define RF1_FORCE_EXTRA 0x00000800 /* Start out something */
+#define RF1_FRIEND 0x00001000 /* Arrive with a friend */
+#define RF1_FRIENDS 0x00002000 /* Arrive with some friends */
+#define RF1_ESCORT 0x00004000 /* Arrive with an escort */
+#define RF1_ESCORTS 0x00008000 /* Arrive with some escorts */
+#define RF1_NEVER_BLOW 0x00010000 /* Never make physical blow */
+#define RF1_NEVER_MOVE 0x00020000 /* Never make physical move */
+#define RF1_RAND_25 0x00040000 /* Moves randomly (25%) */
+#define RF1_RAND_50 0x00080000 /* Moves randomly (50%) */
+#define RF1_ONLY_GOLD 0x00100000 /* Drop only gold */
+#define RF1_ONLY_ITEM 0x00200000 /* Drop only items */
+#define RF1_DROP_60 0x00400000 /* Drop an item/gold (60%) */
+#define RF1_DROP_90 0x00800000 /* Drop an item/gold (90%) */
+#define RF1_DROP_1D2 0x01000000 /* Drop 1d2 items/gold */
+#define RF1_DROP_2D2 0x02000000 /* Drop 2d2 items/gold */
+#define RF1_DROP_3D2 0x04000000 /* Drop 3d2 items/gold */
+#define RF1_DROP_4D2 0x08000000 /* Drop 4d2 items/gold */
+#define RF1_DROP_GOOD 0x10000000 /* Drop good items */
+#define RF1_DROP_GREAT 0x20000000 /* Drop great items */
+#define RF1_DROP_USEFUL 0x40000000 /* Drop "useful" items */
+#define RF1_DROP_CHOSEN 0x80000000 /* Drop "chosen" items */
+
+/*
+ * New monster race bit flags
+ */
+#define RF2_STUPID 0x00000001 /* Monster is stupid */
+#define RF2_SMART 0x00000002 /* Monster is smart */
+#define RF2_CAN_SPEAK 0x00000004 /* TY: can speak */
+#define RF2_REFLECTING 0x00000008 /* Reflects bolts */
+#define RF2_INVISIBLE 0x00000010 /* Monster avoids vision */
+#define RF2_COLD_BLOOD 0x00000020 /* Monster avoids infra */
+#define RF2_EMPTY_MIND 0x00000040 /* Monster avoids telepathy */
+#define RF2_WEIRD_MIND 0x00000080 /* Monster avoids telepathy? */
+#define RF2_DEATH_ORB 0x00000100 /* Death Orb */
+#define RF2_REGENERATE 0x00000200 /* Monster regenerates */
+#define RF2_SHAPECHANGER 0x00000400 /* TY: shapechanger */
+#define RF2_ATTR_ANY 0x00000800 /* TY: Attr_any */
+#define RF2_POWERFUL 0x00001000 /* Monster has strong breath */
+#define RF2_ELDRITCH_HORROR 0x00002000 /* Sanity-blasting horror */
+#define RF2_AURA_FIRE 0x00004000 /* Burns in melee */
+#define RF2_AURA_ELEC 0x00008000 /* Shocks in melee */
+#define RF2_OPEN_DOOR 0x00010000 /* Monster can open doors */
+#define RF2_BASH_DOOR 0x00020000 /* Monster can bash doors */
+#define RF2_PASS_WALL 0x00040000 /* Monster can pass walls */
+#define RF2_KILL_WALL 0x00080000 /* Monster can destroy walls */
+#define RF2_MOVE_BODY 0x00100000 /* Monster can move monsters */
+#define RF2_KILL_BODY 0x00200000 /* Monster can kill monsters */
+#define RF2_TAKE_ITEM 0x00400000 /* Monster can pick up items */
+#define RF2_KILL_ITEM 0x00800000 /* Monster can crush items */
+#define RF2_BRAIN_1 0x01000000
+#define RF2_BRAIN_2 0x02000000
+#define RF2_BRAIN_3 0x04000000
+#define RF2_BRAIN_4 0x08000000
+#define RF2_BRAIN_5 0x10000000
+#define RF2_BRAIN_6 0x20000000
+#define RF2_BRAIN_7 0x40000000
+#define RF2_BRAIN_8 0x80000000
+
+/*
+ * New monster race bit flags
+ */
+#define RF3_ORC 0x00000001 /* Orc */
+#define RF3_TROLL 0x00000002 /* Troll */
+#define RF3_GIANT 0x00000004 /* Giant */
+#define RF3_DRAGON 0x00000008 /* Dragon */
+#define RF3_DEMON 0x00000010 /* Demon */
+#define RF3_UNDEAD 0x00000020 /* Undead */
+#define RF3_EVIL 0x00000040 /* Evil */
+#define RF3_ANIMAL 0x00000080 /* Animal */
+#define RF3_THUNDERLORD 0x00000100 /* DG: Thunderlord */
+#define RF3_GOOD 0x00000200 /* Good */
+#define RF3_AURA_COLD 0x00000400 /* Freezes in melee */
+#define RF3_NONLIVING 0x00000800 /* TY: Non-Living (?) */
+#define RF3_HURT_LITE 0x00001000 /* Hurt by lite */
+#define RF3_HURT_ROCK 0x00002000 /* Hurt by rock remover */
+#define RF3_SUSCEP_FIRE 0x00004000 /* Hurt badly by fire */
+#define RF3_SUSCEP_COLD 0x00008000 /* Hurt badly by cold */
+#define RF3_IM_ACID 0x00010000 /* Resist acid a lot */
+#define RF3_IM_ELEC 0x00020000 /* Resist elec a lot */
+#define RF3_IM_FIRE 0x00040000 /* Resist fire a lot */
+#define RF3_IM_COLD 0x00080000 /* Resist cold a lot */
+#define RF3_IM_POIS 0x00100000 /* Resist poison a lot */
+#define RF3_RES_TELE 0x00200000 /* Resist teleportation */
+#define RF3_RES_NETH 0x00400000 /* Resist nether a lot */
+#define RF3_RES_WATE 0x00800000 /* Resist water */
+#define RF3_RES_PLAS 0x01000000 /* Resist plasma */
+#define RF3_RES_NEXU 0x02000000 /* Resist nexus */
+#define RF3_RES_DISE 0x04000000 /* Resist disenchantment */
+#define RF3_UNIQUE_4 0x08000000 /* Is a "Nazgul" unique */
+#define RF3_NO_FEAR 0x10000000 /* Cannot be scared */
+#define RF3_NO_STUN 0x20000000 /* Cannot be stunned */
+#define RF3_NO_CONF 0x40000000 /* Cannot be confused */
+#define RF3_NO_SLEEP 0x80000000 /* Cannot be slept */
+
+/*
+ * New monster race bit flags
+ */
+#define RF4_SHRIEK 0x00000001 /* Shriek for help */
+#define RF4_MULTIPLY 0x00000002 /* Monster reproduces */
+#define RF4_S_ANIMAL 0x00000004 /* Summon animals */
+#define RF4_ROCKET 0x00000008 /* TY: Rocket */
+#define RF4_ARROW_1 0x00000010 /* Fire an arrow (light) */
+#define RF4_ARROW_2 0x00000020 /* Fire an arrow (heavy) */
+#define RF4_ARROW_3 0x00000040 /* Fire missiles (light) */
+#define RF4_ARROW_4 0x00000080 /* Fire missiles (heavy) */
+#define RF4_BR_ACID 0x00000100 /* Breathe Acid */
+#define RF4_BR_ELEC 0x00000200 /* Breathe Elec */
+#define RF4_BR_FIRE 0x00000400 /* Breathe Fire */
+#define RF4_BR_COLD 0x00000800 /* Breathe Cold */
+#define RF4_BR_POIS 0x00001000 /* Breathe Poison */
+#define RF4_BR_NETH 0x00002000 /* Breathe Nether */
+#define RF4_BR_LITE 0x00004000 /* Breathe Lite */
+#define RF4_BR_DARK 0x00008000 /* Breathe Dark */
+#define RF4_BR_CONF 0x00010000 /* Breathe Confusion */
+#define RF4_BR_SOUN 0x00020000 /* Breathe Sound */
+#define RF4_BR_CHAO 0x00040000 /* Breathe Chaos */
+#define RF4_BR_DISE 0x00080000 /* Breathe Disenchant */
+#define RF4_BR_NEXU 0x00100000 /* Breathe Nexus */
+#define RF4_BR_TIME 0x00200000 /* Breathe Time */
+#define RF4_BR_INER 0x00400000 /* Breathe Inertia */
+#define RF4_BR_GRAV 0x00800000 /* Breathe Gravity */
+#define RF4_BR_SHAR 0x01000000 /* Breathe Shards */
+#define RF4_BR_PLAS 0x02000000 /* Breathe Plasma */
+#define RF4_BR_WALL 0x04000000 /* Breathe Force */
+#define RF4_BR_MANA 0x08000000 /* Breathe Mana */
+#define RF4_BA_NUKE 0x10000000 /* TY: Nuke Ball */
+#define RF4_BR_NUKE 0x20000000 /* TY: Toxic Breath */
+#define RF4_BA_CHAO 0x40000000 /* Chaos Ball */
+#define RF4_BR_DISI 0x80000000 /* Breathe Disintegration */
+
+/*
+ * New monster race bit flags
+ */
+#define RF5_BA_ACID 0x00000001 /* Acid Ball */
+#define RF5_BA_ELEC 0x00000002 /* Elec Ball */
+#define RF5_BA_FIRE 0x00000004 /* Fire Ball */
+#define RF5_BA_COLD 0x00000008 /* Cold Ball */
+#define RF5_BA_POIS 0x00000010 /* Poison Ball */
+#define RF5_BA_NETH 0x00000020 /* Nether Ball */
+#define RF5_BA_WATE 0x00000040 /* Water Ball */
+#define RF5_BA_MANA 0x00000080 /* Mana Storm */
+#define RF5_BA_DARK 0x00000100 /* Darkness Storm */
+#define RF5_DRAIN_MANA 0x00000200 /* Drain Mana */
+#define RF5_MIND_BLAST 0x00000400 /* Blast Mind */
+#define RF5_BRAIN_SMASH 0x00000800 /* Smash Brain */
+#define RF5_CAUSE_1 0x00001000 /* Cause Light Wound */
+#define RF5_CAUSE_2 0x00002000 /* Cause Serious Wound */
+#define RF5_CAUSE_3 0x00004000 /* Cause Critical Wound */
+#define RF5_CAUSE_4 0x00008000 /* Cause Mortal Wound */
+#define RF5_BO_ACID 0x00010000 /* Acid Bolt */
+#define RF5_BO_ELEC 0x00020000 /* Elec Bolt (unused) */
+#define RF5_BO_FIRE 0x00040000 /* Fire Bolt */
+#define RF5_BO_COLD 0x00080000 /* Cold Bolt */
+#define RF5_BO_POIS 0x00100000 /* Poison Bolt (unused) */
+#define RF5_BO_NETH 0x00200000 /* Nether Bolt */
+#define RF5_BO_WATE 0x00400000 /* Water Bolt */
+#define RF5_BO_MANA 0x00800000 /* Mana Bolt */
+#define RF5_BO_PLAS 0x01000000 /* Plasma Bolt */
+#define RF5_BO_ICEE 0x02000000 /* Ice Bolt */
+#define RF5_MISSILE 0x04000000 /* Magic Missile */
+#define RF5_SCARE 0x08000000 /* Frighten Player */
+#define RF5_BLIND 0x10000000 /* Blind Player */
+#define RF5_CONF 0x20000000 /* Confuse Player */
+#define RF5_SLOW 0x40000000 /* Slow Player */
+#define RF5_HOLD 0x80000000 /* Paralyze Player */
+
+/*
+ * New monster race bit flags
+ */
+#define RF6_HASTE 0x00000001 /* Speed self */
+#define RF6_HAND_DOOM 0x00000002 /* Hand of Doom */
+#define RF6_HEAL 0x00000004 /* Heal self */
+#define RF6_S_ANIMALS 0x00000008 /* Summon animals */
+#define RF6_BLINK 0x00000010 /* Teleport Short */
+#define RF6_TPORT 0x00000020 /* Teleport Long */
+#define RF6_TELE_TO 0x00000040 /* Move player to monster */
+#define RF6_TELE_AWAY 0x00000080 /* Move player far away */
+#define RF6_TELE_LEVEL 0x00000100 /* Move player vertically */
+#define RF6_DARKNESS 0x00000200 /* Create Darkness */
+#define RF6_TRAPS 0x00000400 /* Create Traps */
+#define RF6_FORGET 0x00000800 /* Cause amnesia */
+#define RF6_RAISE_DEAD 0x00001000 /* Raise Dead */
+#define RF6_S_BUG 0x00002000 /* Summon Software bug */
+#define RF6_S_RNG 0x00004000 /* Summon RNG */
+#define RF6_S_THUNDERLORD 0x00008000 /* Summon Thunderlords */
+#define RF6_S_KIN 0x00010000 /* Summon "kin" */
+#define RF6_S_HI_DEMON 0x00020000 /* Summon greater demons! */
+#define RF6_S_MONSTER 0x00040000 /* Summon Monster */
+#define RF6_S_MONSTERS 0x00080000 /* Summon Monsters */
+#define RF6_S_ANT 0x00100000 /* Summon Ants */
+#define RF6_S_SPIDER 0x00200000 /* Summon Spiders */
+#define RF6_S_HOUND 0x00400000 /* Summon Hounds */
+#define RF6_S_HYDRA 0x00800000 /* Summon Hydras */
+#define RF6_S_ANGEL 0x01000000 /* Summon Angel */
+#define RF6_S_DEMON 0x02000000 /* Summon Demon */
+#define RF6_S_UNDEAD 0x04000000 /* Summon Undead */
+#define RF6_S_DRAGON 0x08000000 /* Summon Dragon */
+#define RF6_S_HI_UNDEAD 0x10000000 /* Summon Greater Undead */
+#define RF6_S_HI_DRAGON 0x20000000 /* Summon Ancient Dragon */
+#define RF6_S_WRAITH 0x40000000 /* Summon Unique Wraith */
+#define RF6_S_UNIQUE 0x80000000 /* Summon Unique Monster */
+
+/*
+ * New monster race bit flags
+ */
+#define RF7_AQUATIC 0x00000001 /* Aquatic monster */
+#define RF7_CAN_SWIM 0x00000002 /* Monster can swim */
+#define RF7_CAN_FLY 0x00000004 /* Monster can fly */
+#define RF7_FRIENDLY 0x00000008 /* Monster is friendly */
+#define RF7_PET 0x00000010 /* Monster is a pet */
+#define RF7_MORTAL 0x00000020 /* Monster is a mortal being */
+#define RF7_SPIDER 0x00000040 /* Monster is a spider (can pass webs) */
+#define RF7_NAZGUL 0x00000080 /* Monster is a Nazgul */
+#define RF7_DG_CURSE 0x00000100 /* If killed the monster grant a DG Curse to the player */
+#define RF7_POSSESSOR 0x00000200 /* Is it a dreaded possessor monster ? */
+#define RF7_NO_DEATH 0x00000400 /* Cannot be killed */
+#define RF7_NO_TARGET 0x00000800 /* Cannot be targeted */
+#define RF7_AI_ANNOY 0x00001000 /* Try to tease the player */
+#define RF7_AI_SPECIAL 0x00002000 /* For quests */
+#define RF7_NEUTRAL 0x00004000 /* Monster is neutral */
+#define RF7_DROP_ART 0x00008000 /* Monster drop one art */
+#define RF7_DROP_RANDART 0x00010000 /* Monster drop one randart */
+#define RF7_AI_PLAYER 0x00020000 /* Controlled by the player */
+#define RF7_NO_THEFT 0x00040000 /* Monster is immune to theft */
+#define RF7_SPIRIT 0x00080000 /* This is a Spirit, coming from the Void */
+#define RF7_IM_MELEE 0x00100000 /* IM melee */
+
+
+/*
+ * Monster race flags
+ */
+#define RF8_DUNGEON 0x00000001
+#define RF8_WILD_TOWN 0x00000002
+#define RF8_XXX8X02 0x00000004
+#define RF8_WILD_SHORE 0x00000008
+#define RF8_WILD_OCEAN 0x00000010
+#define RF8_WILD_WASTE 0x00000020
+#define RF8_WILD_WOOD 0x00000040
+#define RF8_WILD_VOLCANO 0x00000080
+#define RF8_XXX8X08 0x00000100
+#define RF8_WILD_MOUNTAIN 0x00000200
+#define RF8_WILD_GRASS 0x00000400
+#define RF8_NO_CUT 0x00000800
+#define RF8_CTHANGBAND 0x00001000 /* Not used in ToME */
+/* XXX */
+#define RF8_ZANGBAND 0x00004000 /* Not used in ToME */
+#define RF8_JOKEANGBAND 0x00008000
+#define RF8_ANGBAND 0x00010000
+
+#define RF8_WILD_TOO 0x80000000
+
+
+/*
+ * Monster race flags
+ */
+#define RF9_DROP_CORPSE 0x00000001
+#define RF9_DROP_SKELETON 0x00000002
+#define RF9_HAS_LITE 0x00000004 /* Carries a lite */
+#define RF9_MIMIC 0x00000008 /* *REALLY* looks like an object ... only nastier */
+#define RF9_HAS_EGG 0x00000010 /* Can be monster's eggs */
+#define RF9_IMPRESED 0x00000020 /* The monster can follow you on each level until he dies */
+#define RF9_SUSCEP_ACID 0x00000040 /* Susceptible to acid */
+#define RF9_SUSCEP_ELEC 0x00000080 /* Susceptible to lightning */
+#define RF9_SUSCEP_POIS 0x00000100 /* Susceptible to poison */
+#define RF9_KILL_TREES 0x00000200 /* Monster can eat trees */
+#define RF9_WYRM_PROTECT 0x00000400 /* The monster is protected by great wyrms of power: They'll be summoned if it's killed */
+#define RF9_DOPPLEGANGER 0x00000800 /* The monster looks like you */
+#define RF9_ONLY_DEPTH 0x00001000 /* The monster can only be generated at the GIVEN depth */
+#define RF9_SPECIAL_GENE 0x00002000 /* The monster can only be generated in special conditions like quests, special dungeons, ... */
+#define RF9_NEVER_GENE 0x00004000 /* The monster cannot be normaly generated */
+
+
+/*
+ * Hack -- choose "intelligent" spells when desperate
+ */
+
+#define RF4_INT_MASK \
+ (RF4_S_ANIMAL)
+
+#define RF5_INT_MASK \
+ (RF5_HOLD | RF5_SLOW | RF5_CONF | RF5_BLIND | RF5_SCARE)
+
+#define RF6_INT_MASK \
+ (RF6_BLINK | RF6_TPORT | RF6_TELE_LEVEL | RF6_TELE_AWAY | \
+ RF6_HEAL | RF6_HASTE | RF6_TRAPS | \
+ RF6_S_KIN | RF6_S_HI_DEMON | RF6_S_MONSTER | RF6_S_MONSTERS | \
+ RF6_S_ANT | RF6_S_SPIDER | RF6_S_HOUND | RF6_S_HYDRA | \
+ RF6_S_ANGEL | RF6_S_DRAGON | RF6_S_UNDEAD | RF6_S_DEMON | \
+ RF6_S_HI_DRAGON | RF6_S_HI_UNDEAD | RF6_S_WRAITH | RF6_S_UNIQUE | \
+ RF6_S_THUNDERLORD | RF6_S_BUG | RF6_S_RNG | RF6_S_ANIMALS)
+
+
+/*
+ * Hack -- "bolt" spells that may hurt fellow monsters
+ */
+#define RF4_BOLT_MASK \
+ (RF4_ARROW_1 | RF4_ARROW_2 | RF4_ARROW_3 | RF4_ARROW_4)
+
+#define RF5_BOLT_MASK \
+ (RF5_BO_ACID | RF5_BO_ELEC | RF5_BO_FIRE | RF5_BO_COLD | \
+ RF5_BO_POIS | RF5_BO_NETH | RF5_BO_WATE | RF5_BO_MANA | \
+ RF5_BO_PLAS | RF5_BO_ICEE | RF5_MISSILE)
+
+#define RF6_BOLT_MASK \
+ 0L
+
+
+/* Hack -- summon spells */
+
+#define RF4_SUMMON_MASK \
+ (RF4_S_ANIMAL)
+
+#define RF5_SUMMON_MASK \
+ 0L
+
+#define RF6_SUMMON_MASK \
+ (RF6_S_KIN | RF6_S_HI_DEMON | RF6_S_MONSTER | RF6_S_MONSTERS | RF6_S_ANT | \
+ RF6_S_SPIDER | RF6_S_HOUND | RF6_S_HYDRA | RF6_S_ANGEL | RF6_S_DEMON | \
+ RF6_S_UNDEAD | RF6_S_DRAGON | RF6_S_HI_UNDEAD | RF6_S_HI_DRAGON | \
+ RF6_S_WRAITH | RF6_S_UNIQUE | RF6_S_THUNDERLORD | RF6_S_BUG | RF6_S_RNG | \
+ RF6_S_ANIMALS)
+
+
+/*** Macro Definitions ***/
+
+
+/*
+ * Hack -- The main "screen"
+ */
+#define term_screen (angband_term[0])
+
+
+/*
+ * Determine if a given inventory item is "aware"
+ */
+#define object_aware_p(T) \
+ (k_info[(T)->k_idx].aware)
+
+/*
+ * Determine if a given inventory item is "tried"
+ */
+#define object_tried_p(T) \
+ (k_info[(T)->k_idx].tried)
+
+
+/*
+ * Determine if a given inventory item is "known"
+ * Test One -- Check for special "known" tag
+ * Test Two -- Check for "Easy Know" + "Aware"
+ */
+#define object_known_p(T) \
+ (((T)->ident & (IDENT_KNOWN)) || \
+ (k_info[(T)->k_idx].easy_know && k_info[(T)->k_idx].aware))
+
+
+/*
+ * Return the "attr" for a given item.
+ * Use "flavor" if available.
+ * Default to user definitions.
+ */
+#define object_attr(T) \
+ (((T)->tval == TV_RANDART) ? \
+ random_artifacts[(T)->sval].attr : \
+ (k_info[(T)->k_idx].flavor) ? \
+ misc_to_attr[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].x_attr)
+
+#define object_attr_default(T) \
+ (((T)->tval == TV_RANDART) ? \
+ random_artifacts[(T)->sval].attr : \
+ (k_info[(T)->k_idx].flavor) ? \
+ misc_to_attr[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].d_attr)
+
+/*
+ * Return the "char" for a given item.
+ * Use "flavor" if available.
+ * Default to user definitions.
+ */
+#define object_char(T) \
+ ((k_info[(T)->k_idx].flavor) ? \
+ misc_to_char[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].x_char)
+
+#define object_char_default(T) \
+ ((k_info[(T)->k_idx].flavor) ? \
+ misc_to_char[k_info[(T)->k_idx].flavor] : \
+ k_info[(T)->k_idx].d_char)
+
+
+
+/*
+ * Artifacts use the "name1" field
+ */
+#define artifact_p(T) \
+ ( \
+ ((T)->tval == TV_RANDART || \
+ ((T)->name1 ? TRUE : FALSE) || \
+ ((T)->art_name ? TRUE : FALSE) || \
+ ((k_info[(T)->k_idx].flags3 & TR3_NORM_ART)? TRUE : FALSE)) \
+ )
+
+/*
+ * Ego-Items use the "name2" field
+ */
+#define ego_item_p(T) \
+ ((T)->name2 || (T)->name2b ? TRUE : FALSE)
+
+/*
+ * Ego-Items use the "name2" field
+ */
+#define is_ego_p(T, e) \
+ (((T)->name2 == (e)) || ((T)->name2b == (e)))
+
+
+
+/*
+ * Cursed items.
+ */
+#define cursed_p(T) \
+ ((T)->ident & (IDENT_CURSED))
+
+
+/*
+ * Convert an "attr"/"char" pair into a "pict" (P)
+ */
+#define PICT(A,C) \
+ ((((u16b)(A)) << 8) | ((byte)(C)))
+
+/*
+ * Convert a "pict" (P) into an "attr" (A)
+ */
+#define PICT_A(P) \
+ ((byte)((P) >> 8))
+
+/*
+ * Convert a "pict" (P) into an "char" (C)
+ */
+#define PICT_C(P) \
+ ((char)((byte)(P)))
+
+
+/*
+ * Convert a "location" (Y,X) into a "grid" (G)
+ */
+#define GRID(Y,X) \
+ (256 * (Y) + (X))
+
+/*
+ * Convert a "grid" (G) into a "location" (Y)
+ */
+#define GRID_Y(G) \
+ ((int)((G) / 256U))
+
+/*
+ * Convert a "grid" (G) into a "location" (X)
+ */
+#define GRID_X(G) \
+ ((int)((G) % 256U))
+
+
+/*
+ * Determines if a map location is fully inside the outer walls
+ */
+#define in_bounds(Y,X) \
+ (((Y) > 0) && ((X) > 0) && ((Y) < cur_hgt-1) && ((X) < cur_wid-1))
+
+/*
+ * Determines if a map location is on or inside the outer walls
+ */
+#define in_bounds2(Y,X) \
+ (((Y) >= 0) && ((X) >= 0) && ((Y) < cur_hgt) && ((X) < cur_wid))
+
+
+/*
+ * Determines if a map location is currently "on screen" -RAK-
+ * Note that "panel_contains(Y,X)" always implies "in_bounds2(Y,X)".
+ */
+#define panel_contains(Y,X) \
+ (((Y) >= panel_row_min) && ((Y) <= panel_row_max) && \
+ ((X) >= panel_col_min) && ((X) <= panel_col_max))
+
+
+
+/*
+ * Determine if a "legal" grid is a "floor" grid
+ *
+ * Line 1 -- forbid doors, rubble, seams, walls
+ *
+ * Note that the terrain features are split by a one bit test
+ * into those features which block line of sight and those that
+ * do not, allowing an extremely fast single bit check below.
+ *
+ * Add in the fact that some new terrain (water & lava) do NOT block sight
+ * -KMW-
+ */
+#define cave_floor_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP))
+
+
+/*
+ * Determine if a "legal" grid is floor without the REMEMBER flag set
+ * Sometimes called "boring" grid
+ */
+#define cave_plain_floor_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ !(f_info[cave[Y][X].feat].flags1 & FF1_REMEMBER))
+
+
+/*
+ * Determine if a "legal" grid isn't a "blocking line of sight" grid
+ *
+ * Line 1 -- forbid doors, rubble, seams, walls
+ *
+ * Note that the terrain features are split by a one bit test
+ * into those features which block line of sight and those that
+ * do not, allowing an extremely fast single bit check below.
+ *
+ * Add in the fact that some new terrain (water & lava) do NOT block sight
+ * -KMW-
+ */
+#define cave_sight_bold(Y,X) \
+ (!(f_info[cave[Y][X].feat].flags1 & FF1_NO_VISION))
+
+
+/*
+ * Determine if a "legal" grid is a "clean" floor grid
+ *
+ * Line 1 -- forbid non-floors
+ * Line 2 -- forbid deep water -KMW-
+ * Line 3 -- forbid deep lava -KMW-
+ * Line 4 -- forbid normal objects
+ */
+#define cave_clean_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP) && \
+ (cave[Y][X].o_idx == 0) && \
+ !(f_info[cave[Y][X].feat].flags1 & FF1_PERMANENT))
+
+
+/*
+ * Determine if a "legal" grid is an "empty" floor grid
+ *
+ * Line 1 -- forbid doors, rubble, seams, walls
+ * Line 2 -- forbid normal monsters
+ * Line 3 -- forbid the player
+ */
+#define cave_empty_bold(Y,X) \
+ (cave_floor_bold(Y,X) && \
+ !(cave[Y][X].m_idx) && \
+ !(((Y) == p_ptr->py) && ((X) == p_ptr->px)))
+
+
+/*
+ * Determine if a "legal" grid is an "naked" floor grid
+ *
+ * Line 1 -- forbid non-floors, non-shallow water & lava -KMW-
+ * Line 2 -- forbid normal objects
+ * Line 3 -- forbid player/monsters
+ */
+#define cave_naked_bold(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP) && \
+ !(f_info[cave[Y][X].feat].flags1 & FF1_PERMANENT) && \
+ (cave[Y][X].o_idx == 0) && \
+ (cave[Y][X].m_idx == 0))
+
+#define cave_naked_bold2(Y,X) \
+ ((f_info[cave[Y][X].feat].flags1 & FF1_FLOOR) && \
+ (cave[Y][X].feat != FEAT_MON_TRAP) && \
+ (cave[Y][X].o_idx == 0) && \
+ (cave[Y][X].m_idx == 0))
+
+
+
+/*
+ * Determine if a "legal" grid is "permanent"
+ *
+ * Line 1 -- perma-walls
+ * Line 2-3 -- stairs
+ * Line 4-5 -- building doors -KMW-
+ * Line 6-7 -- shop doors
+ */
+#define cave_perma_bold(Y,X) \
+ (f_info[cave[Y][X].feat].flags1 & FF1_PERMANENT)
+
+
+/*
+ * Grid based version of "cave_floor_bold()"
+ */
+#define cave_floor_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && ((C)->feat != FEAT_MON_TRAP))
+
+
+/*
+ * Grid based version of "cave_plain_floor_bold()"
+ */
+#define cave_plain_floor_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && \
+ !(f_info[(C)->feat].flags1 & FF1_REMEMBER))
+
+
+/*
+ * Grid based version of "cave_clean_bold()"
+ */
+#define cave_clean_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && ((C)->feat != FEAT_MON_TRAP) && \
+ (!(C)->o_idx))
+
+/*
+ * Grid based version of "cave_sight_bold()"
+ */
+#define cave_sight_grid(C) \
+ (!(f_info[(C)->feat].flags1 & FF1_NO_VISION))
+
+/*
+ * Grid based version of "cave_empty_bold()"
+ */
+#define cave_empty_grid(C) \
+ (cave_floor_grid(C) && \
+ !((C)->m_idx) && \
+ !((C) == &cave[p_ptr->py][p_ptr->px]))
+
+/*
+ * Grid based version of "cave_empty_bold()"
+ */
+#define cave_naked_grid(C) \
+ ((f_info[(C)->feat].flags1 & FF1_FLOOR) && ((C)->feat != FEAT_MON_TRAP) && \
+ !((C)->o_idx) && \
+ !((C)->m_idx) && \
+ !((C) == &cave[p_ptr->py][p_ptr->px]))
+
+
+/*
+ * Grid based version of "cave_perma_bold()"
+ */
+#define cave_perma_grid(C) \
+ (f_info[(C)->feat].flags1 & FF1_PERMANENT)
+
+
+
+/*
+ * Determine if a "legal" grid is within "los" of the player
+ *
+ * Note the use of comparison to zero to force a "boolean" result
+ */
+#define player_has_los_bold(Y,X) \
+ ((cave[Y][X].info & (CAVE_VIEW)) != 0)
+
+
+
+/*
+ * Determine if a "legal" grid can be "seen" by the player
+ *
+ * Note the use of comparison to zero to force a "boolean" result
+ */
+#define player_can_see_bold(Y,X) \
+ ((cave[Y][X].info & (CAVE_SEEN)) != 0)
+
+
+
+/*
+ * Hack -- Prepare to use the "Secure" routines
+ */
+#if defined(SET_UID) && defined(SECURE)
+extern int PlayerUID;
+# define getuid() PlayerUID
+# define geteuid() PlayerUID
+#endif
+
+
+
+/*** Color constants ***/
+
+
+/*
+ * Angband "attributes" (with symbols, and base (R,G,B) codes)
+ *
+ * The "(R,G,B)" codes are given in "fourths" of the "maximal" value,
+ * and should "gamma corrected" on most (non-Macintosh) machines.
+ */
+#define TERM_DARK 0 /* 'd' */ /* 0,0,0 */
+#define TERM_WHITE 1 /* 'w' */ /* 4,4,4 */
+#define TERM_SLATE 2 /* 's' */ /* 2,2,2 */
+#define TERM_ORANGE 3 /* 'o' */ /* 4,2,0 */
+#define TERM_RED 4 /* 'r' */ /* 3,0,0 */
+#define TERM_GREEN 5 /* 'g' */ /* 0,2,1 */
+#define TERM_BLUE 6 /* 'b' */ /* 0,0,4 */
+#define TERM_UMBER 7 /* 'u' */ /* 2,1,0 */
+#define TERM_L_DARK 8 /* 'D' */ /* 1,1,1 */
+#define TERM_L_WHITE 9 /* 'W' */ /* 3,3,3 */
+#define TERM_VIOLET 10 /* 'v' */ /* 4,0,4 */
+#define TERM_YELLOW 11 /* 'y' */ /* 4,4,0 */
+#define TERM_L_RED 12 /* 'R' */ /* 4,0,0 */
+#define TERM_L_GREEN 13 /* 'G' */ /* 0,4,0 */
+#define TERM_L_BLUE 14 /* 'B' */ /* 0,4,4 */
+#define TERM_L_UMBER 15 /* 'U' */ /* 3,2,1 */
+
+
+/*** Graphics constants ***/
+
+/*
+ * Possible values of graphics_mode
+ * Good only when use_graphics is set to TRUE
+ * Set by reset_visuals() and used by map_info()
+ */
+#define GRAPHICS_NONE 0
+#define GRAPHICS_UNKNOWN 1
+#define GRAPHICS_IBM 2
+#define GRAPHICS_OLD 3
+#define GRAPHICS_NEW 4
+#define GRAPHICS_ISO 5
+
+
+/*** Sound constants ***/
+
+
+/*
+ * Mega-Hack -- some primitive sound support (see "main-win.c")
+ *
+ * Some "sound" constants for "Term_xtra(TERM_XTRA_SOUND, val)"
+ */
+#define SOUND_HIT 1
+#define SOUND_MISS 2
+#define SOUND_FLEE 3
+#define SOUND_DROP 4
+#define SOUND_KILL 5
+#define SOUND_LEVEL 6
+#define SOUND_DEATH 7
+#define SOUND_STUDY 8
+#define SOUND_TELEPORT 9
+#define SOUND_SHOOT 10
+#define SOUND_QUAFF 11
+#define SOUND_ZAP 12
+#define SOUND_WALK 13
+#define SOUND_TPOTHER 14
+#define SOUND_HITWALL 15
+#define SOUND_EAT 16
+#define SOUND_STORE1 17
+#define SOUND_STORE2 18
+#define SOUND_STORE3 19
+#define SOUND_STORE4 20
+#define SOUND_DIG 21
+#define SOUND_OPENDOOR 22
+#define SOUND_SHUTDOOR 23
+#define SOUND_TPLEVEL 24
+#define SOUND_SCROLL 25
+#define SOUND_BUY 26
+#define SOUND_SELL 27
+#define SOUND_WARN 28
+#define SOUND_ROCKET 29 /* Somebody's shooting rockets */
+#define SOUND_N_KILL 30 /* The player kills a non-living/undead monster */
+#define SOUND_U_KILL 31 /* The player kills a unique */
+#define SOUND_QUEST 32 /* The player has just completed a quest */
+#define SOUND_HEAL 33 /* The player was healed a little bit */
+#define SOUND_X_HEAL 34 /* The player was healed full health */
+#define SOUND_BITE 35 /* A monster bites you */
+#define SOUND_CLAW 36 /* A monster claws you */
+#define SOUND_M_SPELL 37 /* A monster casts a miscellaneous spell */
+#define SOUND_SUMMON 38 /* A monster casts a summoning spell */
+#define SOUND_BREATH 39 /* A monster breathes */
+#define SOUND_BALL 40 /* A monster casts a ball / bolt spell */
+#define SOUND_M_HEAL 41 /* A monster heals itself somehow */
+#define SOUND_ATK_SPELL 42 /* A monster casts a misc. offensive spell */
+#define SOUND_EVIL 43 /* Something nasty has just happened! */
+#define SOUND_TOUCH 44 /* A monster touches you */
+#define SOUND_STING 45 /* A monster stings you */
+#define SOUND_CRUSH 46 /* A monster crushes / envelopes you */
+#define SOUND_SLIME 47 /* A monster drools/spits/etc on you */
+#define SOUND_WAIL 48 /* A monster wails */
+#define SOUND_WINNER 49 /* Just won the game! */
+#define SOUND_FIRE 50 /* An item was burned */
+#define SOUND_ACID 51 /* An item was destroyed by acid */
+#define SOUND_ELEC 52 /* An item was destroyed by electricity */
+#define SOUND_COLD 53 /* An item was shattered */
+#define SOUND_ILLEGAL 54 /* Illegal command attempted */
+#define SOUND_FAIL 55 /* Fail to get a spell off / activate an item */
+#define SOUND_WAKEUP 56 /* A monster wakes up */
+#define SOUND_INVULN 57 /* Invulnerability! */
+#define SOUND_FALL 58 /* Falling through a trapdoor... */
+#define SOUND_PAIN 59 /* A monster is in pain! */
+#define SOUND_DESTITEM 60 /* An item was destroyed by misc. means */
+#define SOUND_MOAN 61 /* A monster makes a moan/beg/insult attack */
+#define SOUND_SHOW 62 /* A monster makes a "show" attack */
+#define SOUND_UNUSED 63 /* (no sound for gaze attacks) */
+#define SOUND_EXPLODE 64 /* Something (or somebody) explodes */
+
+/*
+ * Mega-Hack -- maximum known sounds
+ */
+#define SOUND_MAX 65
+
+
+
+/*** Hack ***/
+
+
+/*
+ * Hack -- attempt to reduce various values
+ */
+#ifdef ANGBAND_LITE
+# undef MACRO_MAX
+# define MACRO_MAX 128
+# undef QUARK_MAX
+# define QUARK_MAX 128
+# undef MESSAGE_MAX
+# define MESSAGE_MAX 128
+# undef MESSAGE_BUF
+# define MESSAGE_BUF 4096
+#endif
+
+/*
+ * Road flags
+ */
+#define ROAD_NORTH 1
+#define ROAD_SOUTH 2
+#define ROAD_EAST 4
+#define ROAD_WEST 8
+
+
+/*
+ * Buildings actions
+ */
+#define BACT_RESEARCH_ITEM 1
+#define BACT_TOWN_HISTORY 2
+#define BACT_RACE_LEGENDS 3
+#define BACT_GREET_KING 4
+#define BACT_KING_LEGENDS 5
+#define BACT_QUEST1 6
+#define BACT_GOLD 7
+#define BACT_POSTER 8
+#define BACT_ARENA_RULES 9
+#define BACT_ARENA 10
+#define BACT_ARENA_LEGENDS 11
+#define BACT_IN_BETWEEN 12
+#define BACT_GAMBLE_RULES 13
+#define BACT_CRAPS 14
+#define BACT_SPIN_WHEEL 15
+#define BACT_DICE_SLOTS 16
+#define BACT_REST 17
+#define BACT_FOOD 18
+#define BACT_RUMORS 19
+#define BACT_RESEARCH_MONSTER 20
+#define BACT_COMPARE_WEAPONS 21
+#define BACT_LEGENDS 22
+#define BACT_ENCHANT_WEAPON 23
+#define BACT_ENCHANT_ARMOR 24
+#define BACT_RECHARGE 25
+#define BACT_IDENTS 26
+#define BACT_LEARN 27
+#define BACT_HEALING 28
+#define BACT_RESTORE 29
+#define BACT_ENCHANT_ARROWS 30
+#define BACT_ENCHANT_BOW 31
+#define BACT_GREET 32
+#define BACT_RECALL 33
+#define BACT_TELEPORT_LEVEL 34
+/* XXX */
+/* XXX */
+#define BACT_MIMIC_NORMAL 37
+#define BACT_VIEW_BOUNTIES 38
+#define BACT_SELL_CORPSES 39
+#define BACT_VIEW_QUEST_MON 40
+#define BACT_SELL_QUEST_MON 41
+#define BACT_DIVINATION 42
+#define BACT_SELL 43
+#define BACT_BUY 44
+#define BACT_EXAMINE 45
+#define BACT_STEAL 46
+#define BACT_QUEST2 47
+#define BACT_QUEST3 48
+#define BACT_QUEST4 49
+#define BACT_STAR_HEAL 50
+#define BACT_REQUEST_ITEM 51
+#define BACT_GET_LOAN 52
+#define BACT_PAY_BACK_LOAN 53
+// If one adds new BACT_ do NOT forget to increase max_bact in variables.c
+
+
+/*
+ * Quest status
+ */
+#define QUEST_STATUS_IGNORED -1
+#define QUEST_STATUS_UNTAKEN 0
+#define QUEST_STATUS_TAKEN 1
+#define QUEST_STATUS_COMPLETED 2
+#define QUEST_STATUS_REWARDED 3
+#define QUEST_STATUS_FAILED 4
+#define QUEST_STATUS_FINISHED 5
+#define QUEST_STATUS_FAILED_DONE 6
+
+/*
+ * Quest flags
+ */
+#define QUEST_FLAG_SILENT 0x01 /* no messages for completion */
+#define QUEST_FLAG_PRESET 0x02 /* quest is outside the main dungeon */
+#define QUEST_FLAG_ONCE 0x04 /* quest is marked finished after leaving */
+
+/*
+ * Initialization flags
+ */
+#define INIT_SHOW_TEXT 0x01
+#define INIT_ASSIGN 0x02
+#define INIT_CREATE_DUNGEON 0x04
+#define INIT_GET_SIZE 0x08
+#define INIT_POSITION 0x10
+
+/*
+ * Alchemists defines
+ */
+#define MAX_ALCHEMIST_RECIPES 20
+#define ALCHEMIST_ENCHANT_DAM 0x01
+#define ALCHEMIST_ENCHANT_PVAL 0x02
+#define ALCHEMIST_ENCHANT_AC 0x04
+
+/*
+ * Music songs
+ */
+#define MUSIC_NONE 0
+#define MUSIC_SLOW 1
+#define MUSIC_CONF 2
+#define MUSIC_STUN 3
+#define MUSIC_LIFE 4
+#define MUSIC_MIND 5
+#define MUSIC_LITE 6
+#define MUSIC_FURY 7
+#define MUSIC_AWARE 8
+#define MUSIC_ID 9
+#define MUSIC_ILLUSION 10
+#define MUSIC_WALL 11
+#define MUSIC_RESIST 12
+#define MUSIC_TIME 13
+#define MUSIC_BETWEEN 14
+#define MUSIC_CHARME 15
+#define MUSIC_VIBRA 16
+#define MUSIC_HOLY 17
+#define MUSIC_HIDE 18
+#define MUSIC_LIBERTY 19
+#define MUSIC_RAISE 20
+#define MUSIC_SHADOW 21
+#define MUSIC_STAR_ID 22
+
+#define MAX_MUSIC 23
+#define MAX_MUSICS 11
+
+/*
+ * Fate
+ */
+#define MAX_FATES 200
+
+#define FATE_NONE 0
+#define FATE_FIND_O 1
+#define FATE_NO_DIE_MORTAL 2
+#define FATE_FIND_A 3
+#define FATE_FIND_R 4
+#define FATE_FIND_V 5
+#define FATE_DIE 6
+
+/*
+ * Runes definition
+ */
+#define RUNE_SELF 0x00000001
+#define RUNE_ARROW 0x00000002
+#define RUNE_RAY 0x00000004
+#define RUNE_SPHERE 0x00000008
+#define RUNE_POWER_SURGE 0x00000010
+#define RUNE_ARMAGEDDON 0x00000020
+#define RUNE_MOD_MAX 6
+#define RUNE_STONE 0x000000FF
+
+
+/*
+ * Defines of the different dungeon types
+ */
+#define DUNGEON_WILDERNESS 0
+#define DUNGEON_MIRKWOOD 1
+#define DUNGEON_MORDOR 2
+#define DUNGEON_ANGBAND 3
+#define DUNGEON_BARROW_DOWNS 4
+#define DUNGEON_MOUNT_DOOM 5
+#define DUNGEON_NETHER_REALM 6
+#define DUNGEON_NUMENOR 7
+#define DUNGEON_MANDOS 8
+#define DUNGEON_VOID 11
+#define DUNGEON_MAZE 18
+#define DUNGEON_DOL_GULDUR 23
+
+/* Max depth of each dungeon(max_depth - min_depth) */
+#define MAX_DUNGEON_DEPTH 128
+
+#define DUNGEON_MODE_NONE 0
+#define DUNGEON_MODE_AND 1
+#define DUNGEON_MODE_NAND 2
+#define DUNGEON_MODE_OR 3
+#define DUNGEON_MODE_NOR 4
+
+
+/*
+ * Returns the dungeon level or the feat,
+ * if the player is not in a dungeon
+ */
+#define level_or_feat(DTYPE, DLEVEL) \
+ ((DTYPE) == DUNGEON_WILDERNESS ? \
+ wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat : \
+ (DLEVEL) )
+
+
+/*
+ * Defines for the inscriptions
+ */
+#define INSCRIP_EXEC_ENGRAVE 0x01
+#define INSCRIP_EXEC_WALK 0x02
+#define INSCRIP_EXEC_MONST_WALK 0x04
+
+#define INSCRIP_NONE 0
+#define INSCRIP_LIGHT 1
+#define INSCRIP_DARK 2
+#define INSCRIP_STORM 3
+#define INSCRIP_PROTECTION 4
+#define INSCRIP_DWARF_SUMMON 5
+#define INSCRIP_CHASM 6
+#define INSCRIP_BLACK_FIRE 7
+#define MAX_INSCRIPTIONS 8
+
+/*
+ * Various class dependant defines
+ */
+#define CLASS_NONE 0
+#define CLASS_MANA_PATH 1
+#define CLASS_CANALIZE_MANA 2
+#define CLASS_WINDS_MANA 3
+
+#define CLASS_MANA_PATH_ERASE 0x0001
+#define CLASS_FLOOD_LEVEL 0x0002
+#define CLASS_CANALIZE_MANA_EXTRA 0x0004
+#define CLASS_UNDEAD 0x0008
+#define CLASS_ANTIMAGIC 0x0010
+#define CLASS_LEGS 0x0020
+#define CLASS_ARMS 0x0040
+#define CLASS_WALL 0x0080
+
+/*
+ * Types of birth presents
+ */
+#define BIRTH_NONE 0
+#define BIRTH_RING 1
+#define BIRTH_AMULET 2
+
+/*
+ * Automatic note taking types
+ */
+#define NOTE_BIRTH 1
+#define NOTE_WINNER 2
+#define NOTE_SAVE_GAME 3
+#define NOTE_ENTER_DUNGEON 4
+
+/*
+ * Player monsters & ghost defines
+ * NO MORE USED but for savefile compatibility
+ */
+#define GHOST_R_IDX_HEAD 967
+#define GHOST_R_IDX_TAIL 977
+#define MAX_GHOSTS (GHOST_R_IDX_TAIL - GHOST_R_IDX_HEAD)
+
+/* Stores/buildings defines */
+#define STORE_HATED 0
+#define STORE_LIKED 1
+#define STORE_NORMAL 2
+
+/* Pseudo-id defines */
+#define SENSE_NONE 0
+#define SENSE_CURSED 1
+#define SENSE_AVERAGE 2
+#define SENSE_GOOD_LIGHT 3
+#define SENSE_GOOD_HEAVY 4
+#define SENSE_EXCELLENT 5
+#define SENSE_WORTHLESS 6
+#define SENSE_TERRIBLE 7
+#define SENSE_SPECIAL 8
+#define SENSE_BROKEN 9
+#define SENSE_UNCURSED 10
+
+/* Wilderness map related */
+#define WILDERNESS_SEE_RADIUS 3 /* The amount of wilderness seen around the player */
+
+/* Ego monsters defines */
+#define MEGO_CHAR_ANY 127
+#define MEGO_ADD 0
+#define MEGO_SUB 1
+#define MEGO_FIX 2
+#define MEGO_PRC 3
+
+#define MEGO_CHANCE 18 /* % chances of getting ego monsters */
+
+#define race_inf(m_ptr) (((m_ptr)->sr_ptr) ? (m_ptr)->sr_ptr : race_info_idx((m_ptr)->r_idx, (m_ptr)->ego))
+
+/* Object generation */
+#define OBJ_GENE_TREASURE 20
+#define OBJ_GENE_COMBAT 20
+#define OBJ_GENE_MAGIC 20
+#define OBJ_GENE_TOOL 20
+
+/*
+ * Used (or should be) by various functions and tables needing access to
+ * single bits
+ */
+#define BIT(x) (1L << (x))
+
+/* Town defines */
+#define TOWN_RANDOM 20 /* First random town */
+#define TOWN_DUNGEON 4 /* Maximun number of towns per dungeon */
+#define TOWN_CHANCE 50 /* Chance of 1 town */
+
+
+/*
+ * Store flags
+ */
+#define SF1_DEPEND_LEVEL 0x00000001L
+#define SF1_SHALLOW_LEVEL 0x00000002L
+#define SF1_MEDIUM_LEVEL 0x00000004L
+#define SF1_DEEP_LEVEL 0x00000008L
+#define SF1_RARE 0x00000010L
+#define SF1_VERY_RARE 0x00000020L
+#define SF1_COMMON 0x00000040L
+#define SF1_ALL_ITEM 0x00000080L /* Works as the BM */
+#define SF1_RANDOM 0x00000100L
+#define SF1_FORCE_LEVEL 0x00000200L
+#define SF1_MUSEUM 0x00000400L
+
+/*
+ * Powers (mutation, activations, ...)
+ */
+#define POWER_MAX_INIT 62
+
+#define PWR_SPIT_ACID 0
+#define PWR_BR_FIRE 1
+#define PWR_HYPN_GAZE 2
+#define PWR_TELEKINES 3
+#define PWR_VTELEPORT 4
+#define PWR_MIND_BLST 5
+#define PWR_RADIATION 6
+#define PWR_VAMPIRISM 7
+#define PWR_SMELL_MET 8
+#define PWR_SMELL_MON 9
+#define PWR_BLINK 10
+#define PWR_EAT_ROCK 11
+#define PWR_SWAP_POS 12
+#define PWR_SHRIEK 13
+#define PWR_ILLUMINE 14
+#define PWR_DET_CURSE 15
+#define PWR_BERSERK 16
+#define PWR_POLYMORPH 17
+#define PWR_MIDAS_TCH 18
+#define PWR_GROW_MOLD 19
+#define PWR_RESIST 20
+#define PWR_EARTHQUAKE 21
+#define PWR_EAT_MAGIC 22
+#define PWR_WEIGH_MAG 23
+#define PWR_STERILITY 24
+#define PWR_PANIC_HIT 25
+#define PWR_DAZZLE 26
+#define PWR_DARKRAY 27
+#define PWR_RECALL 28
+#define PWR_BANISH 29
+#define PWR_COLD_TOUCH 30
+#define PWR_LAUNCHER 31
+
+#define PWR_PASSWALL 32
+#define PWR_DETECT_TD 33
+#define PWR_COOK_FOOD 34
+#define PWR_UNFEAR 35
+#define PWR_EXPL_RUNE 36
+#define PWR_STM 37
+#define PWR_POIS_DART 38
+#define PWR_MAGIC_MISSILE 39
+#define PWR_GROW_TREE 40
+#define PWR_BR_COLD 41
+#define PWR_BR_CHAOS 42
+#define PWR_BR_ELEM 43
+#define PWR_WRECK_WORLD 44
+#define PWR_SCARE 45
+#define PWR_REST_LIFE 46
+#define PWR_SUMMON_MONSTER 47
+#define PWR_NECRO 48
+#define PWR_ROHAN 49
+#define PWR_THUNDER 50
+#define PWR_DEATHMOLD 51
+#define PWR_HYPNO 52
+#define PWR_UNHYPNO 53
+#define PWR_INCARNATE 54
+#define PWR_MAGIC_MAP 55
+#define PWR_LAY_TRAP 56
+#define PWR_MERCHANT 57
+#define PWR_COMPANION 58
+#define PWR_BEAR 59
+#define PWR_DODGE 60
+#define PWR_BALROG 61
+
+#define ADD_POWER(pow, p) ((pow)[(p)] = TRUE)
+
+/*
+ * Shield effect options
+ */
+#define SHIELD_NONE 0x0000
+#define SHIELD_COUNTER 0x0001
+#define SHIELD_FIRE 0x0002
+#define SHIELD_GREAT_FIRE 0x0004
+#define SHIELD_FEAR 0x0008
+
+/*
+ * Quest constants
+ */
+#define MAX_MON_QUEST 10
+#define MAX_ITEM_QUEST 5
+
+#define MAX_RANDOM_QUEST 99
+
+#define QUEST_NULL 0
+#define QUEST_NECRO 1
+#define QUEST_SAURON 2
+#define QUEST_MORGOTH 3
+#define QUEST_THIEVES 4
+#define QUEST_RANDOM 5
+#define QUEST_HOBBIT 6
+#define QUEST_NAZGUL 7
+#define QUEST_TROLL 8
+#define QUEST_WIGHT 9
+#define QUEST_SPIDER 10
+#define QUEST_POISON 11
+#define QUEST_NARSIL 12
+#define QUEST_EOL 13
+#define QUEST_NIRNAETH 14
+#define QUEST_INVASION 15
+#define QUEST_BETWEEN 16
+#define QUEST_ONE 17
+#define QUEST_SHROOM 18
+#define QUEST_THRAIN 19
+#define QUEST_ULTRA_GOOD 20
+#define QUEST_ULTRA_EVIL 21
+#define QUEST_WOLVES 22
+#define QUEST_DRAGONS 23
+#define QUEST_HAUNTED 24
+#define QUEST_EVIL 25
+#define MAX_Q_IDX_INIT 26
+
+#define PLOT_MAIN 0
+#define PLOT_BREE 1
+#define PLOT_LORIEN 2
+#define PLOT_OTHER 3
+#define PLOT_GONDOLIN 4
+#define PLOT_MINAS 5
+#define PLOT_KHAZAD 6
+#define MAX_PLOTS 7
+
+/*
+ * Hooks
+ */
+#define HOOK_MONSTER_DEATH 0
+#define HOOK_OPEN 1
+#define HOOK_GEN_QUEST 2
+#define HOOK_END_TURN 3
+#define HOOK_FEELING 4
+#define HOOK_NEW_MONSTER 5
+#define HOOK_GEN_LEVEL 6
+#define HOOK_BUILD_ROOM1 7
+#define HOOK_NEW_LEVEL 8
+#define HOOK_QUEST_FINISH 9
+#define HOOK_QUEST_FAIL 10
+#define HOOK_GIVE 11
+#define HOOK_CHAR_DUMP 12
+#define HOOK_INIT_QUEST 13
+#define HOOK_WILD_GEN 14
+#define HOOK_DROP 15
+#define HOOK_IDENTIFY 16
+#define HOOK_MOVE 17
+#define HOOK_STAIR 18
+#define HOOK_MONSTER_AI 19
+#define HOOK_PLAYER_LEVEL 20
+#define HOOK_WIELD 21
+#define HOOK_INIT 22
+#define HOOK_QUAFF 23
+#define HOOK_AIM 24
+#define HOOK_USE 25
+#define HOOK_ACTIVATE 26
+#define HOOK_ZAP 27
+#define HOOK_READ 28
+#define HOOK_CALC_BONUS 29
+#define HOOK_CALC_POWERS 30
+#define HOOK_KEYPRESS 31
+#define HOOK_CHAT 32
+#define HOOK_MON_SPEAK 33
+#define HOOK_MKEY 34
+#define HOOK_BIRTH_OBJECTS 35
+#define HOOK_ACTIVATE_DESC 36
+#define HOOK_INIT_GAME 37
+#define HOOK_ACTIVATE_POWER 38
+#define HOOK_ITEM_NAME 39
+#define HOOK_SAVE_GAME 40
+#define HOOK_LOAD_GAME 41
+#define HOOK_LEVEL_REGEN 42
+#define HOOK_LEVEL_END_GEN 43
+#define HOOK_BUILDING_ACTION 44
+#define HOOK_PROCESS_WORLD 45
+#define HOOK_WIELD_SLOT 46
+#define HOOK_STORE_STOCK 47
+#define HOOK_STORE_BUY 48
+#define HOOK_GEN_LEVEL_BEGIN 49
+#define HOOK_GET 50
+#define HOOK_REDRAW 51
+#define HOOK_RECALC_SKILLS 52
+#define HOOK_ENTER_DUNGEON 53
+#define HOOK_FIRE 54
+#define HOOK_EAT 55
+#define HOOK_DIE 56
+#define HOOK_CALC_HP 57
+#define HOOK_GF_COLOR 58
+#define HOOK_GF_EXEC 59
+#define HOOK_CALC_MANA 60
+#define HOOK_LOAD_END 61
+#define HOOK_RECALL 62
+#define HOOK_FOLLOW_GOD 63
+#define HOOK_SACRIFICE_GOD 64
+#define HOOK_BODY_PARTS 65
+#define HOOK_APPLY_MAGIC 66
+#define HOOK_PLAYER_EXP 67
+#define HOOK_BIRTH 68
+#define HOOK_CALC_LITE 69
+#define HOOK_LEARN_ABILITY 70
+#define HOOK_MOVED 71
+#define HOOK_GAME_START 72
+#define HOOK_TAKEOFF 73
+#define HOOK_CALC_WEIGHT 74
+#define HOOK_FORBID_TRAVEL 75
+#define HOOK_DEBUG_COMMAND 76
+#define HOOK_CALC_BONUS_END 77
+#define MAX_HOOKS 78
+
+#define HOOK_TYPE_C 0
+#define HOOK_TYPE_LUA 1
+
+/*
+ * Defines for loadsave.c
+ * Why 3 and 7? So if it's uninitialized, the code will be able to catch it, as
+ * 0 is an invalid flag. Also, having them apart means that it being accidentally
+ * modified will also result in an invalid value -- Improv
+ */
+#define LS_LOAD 3
+#define LS_SAVE 7
+
+/*
+ * In game help
+ */
+#define HELP1_BETWEEN 0x00000001
+#define HELP1_ALTAR 0x00000002
+#define HELP1_FOUNTAIN 0x00000004
+#define HELP1_IDENTIFY 0x00000008
+#define HELP1_WILD_MODE 0x00000010
+
+/*
+ * Special weapon effects
+ */
+#define SPEC_POIS 0x00000001L
+#define SPEC_CUT 0x00000002L
+
+/*
+ * Ambushes in the wild
+ */
+#define AMBUSH_RACE 1
+#define AMBUSH_MIX 2
+
+/*
+ * Macro trigger
+ */
+#define MAX_MACRO_MOD 12
+#define MAX_MACRO_TRIG 200
+
+
+/*
+ * Skills !
+ */
+#define SKILL_MAX 50000 /* Maximun skill value */
+#define SKILL_STEP 1000 /* 1 skill point */
+
+#define SKILL_EXCLUSIVE 9999 /* Flag to tell exclusive skills */
+
+#define SKILL_CONVEYANCE 1
+#define SKILL_MANA 2
+#define SKILL_FIRE 3
+#define SKILL_AIR 4
+#define SKILL_WATER 5
+#define SKILL_NATURE 6
+#define SKILL_EARTH 7
+#define SKILL_SYMBIOTIC 8
+#define SKILL_MUSIC 9
+#define SKILL_DIVINATION 10
+#define SKILL_TEMPORAL 11
+#define SKILL_DRUID 12
+#define SKILL_DAEMON 13
+#define SKILL_META 14
+#define SKILL_MAGIC 15
+#define SKILL_COMBAT 16
+#define SKILL_MASTERY 17
+#define SKILL_SWORD 18
+#define SKILL_AXE 19
+#define SKILL_POLEARM 20
+#define SKILL_HAFTED 21
+#define SKILL_BACKSTAB 22
+#define SKILL_ARCHERY 23
+#define SKILL_SLING 24
+#define SKILL_BOW 25
+#define SKILL_XBOW 26
+#define SKILL_BOOMERANG 27
+#define SKILL_SPIRITUALITY 28
+#define SKILL_MINDCRAFT 29
+#define SKILL_MISC 30
+#define SKILL_NECROMANCY 31
+#define SKILL_MIMICRY 32
+#define SKILL_ANTIMAGIC 33
+#define SKILL_RUNECRAFT 34
+#define SKILL_SNEAK 35
+#define SKILL_STEALTH 36
+#define SKILL_DISARMING 37
+/* XXX */
+#define SKILL_ALCHEMY 39
+#define SKILL_STEALING 40
+#define SKILL_SORCERY 41
+#define SKILL_HAND 42
+#define SKILL_THAUMATURGY 43
+#define SKILL_SUMMON 44
+#define SKILL_SPELL 45
+#define SKILL_DODGE 46
+#define SKILL_BEAR 47
+#define SKILL_LORE 48
+#define SKILL_PRESERVATION 49
+#define SKILL_POSSESSION 50
+#define SKILL_MIND 51
+#define SKILL_CRITS 52
+#define SKILL_PRAY 53
+#define SKILL_LEARN 54
+#define SKILL_UDUN 55
+#define SKILL_DEVICE 56
+#define SKILL_STUN 57
+#define SKILL_BOULDER 58
+#define SKILL_GEOMANCY 59
+
+/* Ugly but needed */
+#define MAX_SKILLS 200
+
+/* SKill flags */
+#define SKF1_HIDDEN 0x00000001 /* Starts hidden */
+#define SKF1_AUTO_HIDE 0x00000002 /* Tries to rehide at calc_bonus */
+#define SKF1_RANDOM_GAIN 0x00000004 /* Can be randomly gained by certain quests & such */
+
+#define MAX_MELEE 3
+
+/*
+ * Player specialities, should be external but ti would be a mess
+ */
+#define MAX_SPEC 20
+
+
+/*
+ * Spellbinder triggers
+ */
+#define SPELLBINDER_HP75 1
+#define SPELLBINDER_HP50 2
+#define SPELLBINDER_HP25 3
+
+/*
+ * God's defines
+ */
+#define GOD_ALL -1
+#define GOD_NONE 0
+#define GOD_ERU 1
+#define GOD_MANWE 2
+#define GOD_TULKAS 3
+#define GOD_MELKOR 4
+#define GOD_YAVANNA 5
+#define MAX_GODS_INIT 6
+
+#define GOD(g) if (p_ptr->pgod == (g))
+#define PRAY_GOD(g) if ((p_ptr->pgod == (g)) && (p_ptr->praying))
+#define NOT_PRAY_GOD(g) if ((p_ptr->pgod == (g)) && (!p_ptr->praying))
+
+/*
+ * Command numbers for do_cmd_cli().
+ *
+ * As the user is not intended to have a way to enter these codes directly
+ * (doing so isn't harmful, but these codes are not intended as mnemonics),
+ * only codes in the range 0xE000 - 0xF8FF (the private area of Unicode 3.0)
+ * should be used.
+ *
+ * In addition, values at the lower end of this range are preferred as the upper
+ * end may have a system-specific encoding
+ */
+#define CMD_CLI_HELP -8192
+#define CMD_IRC_CONNECT -8191
+#define CMD_IRC_CHAT -8190
+#define CMD_IRC_DISCON -8189
+#define CMD_SHOW_TIME -8188
+#define CMD_SHOW_SKILL -8187
+#define CMD_DUMP_HTML -8186
+#define CMD_MACRO -8185
+#define CMD_QUEST -8184
+#define CMD_BLUNDER -8183
+#define CMD_SHOW_ABILITY -8182
+
+#define CLI_MAX 128
+
+
+/*
+ * The various winner state
+ */
+#define WINNER_NORMAL 1
+#define WINNER_ULTRA 2
+
+/*
+ * The abilities
+ */
+#define AB_SPREAD_BLOWS 0
+#define AB_TREE_WALK 1
+#define AB_PERFECT_CASTING 2
+#define AB_MAX_BLOW1 3
+#define AB_MAX_BLOW2 4
+#define AB_AMMO_CREATION 5
+#define AB_DEATH_TOUCH 6
+#define AB_CREATE_ART 7
+#define AB_FAR_REACHING 8
+#define AB_TRAPPING 9
+#define AB_UNDEAD_FORM 10
diff --git a/src/dungeon.c b/src/dungeon.c
new file mode 100644
index 00000000..39f1b0ce
--- /dev/null
+++ b/src/dungeon.c
@@ -0,0 +1,6149 @@
+/* File: dungeon.c */
+
+/* Purpose: Angband game engine */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+extern lua_State* L;
+
+#define TY_CURSE_CHANCE 100
+#define DG_CURSE_CHANCE 50
+#define AUTO_CURSE_CHANCE 15
+#define CHAINSWORD_NOISE 100
+
+/*
+ * I created this when a bug misplaced my character and the game wasn't able
+ * to load it again.. very frustrating.
+ * So this hack will generate a new level without calling dungeon(), and
+ * then the normal behavior will apply.
+ */
+/* #define SAVE_HACK */
+#ifdef SAVE_HACK
+bool save_hack = TRUE;
+#endif
+
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 1 (Heavy).
+ */
+byte value_check_aux1(object_type *o_ptr)
+{
+ /* Artifacts */
+ if (artifact_p(o_ptr))
+ {
+ /* Cursed/Broken */
+ if (cursed_p(o_ptr)) return (SENSE_TERRIBLE);
+
+ /* Normal */
+ return (SENSE_SPECIAL);
+ }
+
+ /* Ego-Items */
+ if (ego_item_p(o_ptr))
+ {
+ /* Cursed/Broken */
+ if (cursed_p(o_ptr)) return (SENSE_WORTHLESS);
+
+ /* Normal */
+ return (SENSE_EXCELLENT);
+ }
+
+ /* Cursed items */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Good "armor" bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_HEAVY);
+
+ /* Good "weapon" bonus */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_HEAVY);
+
+ /* Default to "average" */
+ return (SENSE_AVERAGE);
+}
+
+byte value_check_aux1_magic(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+
+ switch (o_ptr->tval)
+ {
+ /* Scrolls, Potions, Wands, Staves and Rods */
+ case TV_SCROLL:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ /* "Cursed" scrolls/potions have a cost of 0 */
+ if (k_ptr->cost == 0) return (SENSE_TERRIBLE);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_SPECIAL);
+
+ /* Scroll of Nothing, Apple Juice, etc. */
+ if (k_ptr->cost < 3) return (SENSE_WORTHLESS);
+
+ /*
+ * Identify, Phase Door, Cure Light Wounds, etc. are
+ * just average
+ */
+ if (k_ptr->cost < 100) return (SENSE_AVERAGE);
+
+ /* Enchant Armor, *Identify*, Restore Stat, etc. */
+ if (k_ptr->cost < 10000) return (SENSE_GOOD_HEAVY);
+
+ /* Acquirement, Deincarnation, Strength, Blood of Life, ... */
+ if (k_ptr->cost >= 10000) return (SENSE_EXCELLENT);
+
+ break;
+ }
+
+ /* Food */
+ case TV_FOOD:
+ {
+ /* "Cursed" food */
+ if (k_ptr->cost == 0) return (SENSE_TERRIBLE);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_SPECIAL);
+
+ /* Normal food (no magical properties) */
+ if (k_ptr->cost <= 10) return (SENSE_AVERAGE);
+
+ /* Everything else is good */
+ if (k_ptr->cost > 10) return (SENSE_GOOD_HEAVY);
+
+ break;
+ }
+ }
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 2 (Light).
+ */
+byte value_check_aux2(object_type *o_ptr)
+{
+ /* Cursed items (all of them) */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Artifacts -- except cursed/broken ones */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Ego-Items -- except cursed/broken ones */
+ if (ego_item_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Good armor bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_LIGHT);
+
+ /* Good weapon bonuses */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_LIGHT);
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+byte value_check_aux2_magic(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+
+ switch (o_ptr->tval)
+ {
+ /* Scrolls, Potions, Wands, Staves and Rods */
+ case TV_SCROLL:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ /* "Cursed" scrolls/potions have a cost of 0 */
+ if (k_ptr->cost == 0) return (SENSE_CURSED);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Scroll of Nothing, Apple Juice, etc. */
+ if (k_ptr->cost < 3) return (SENSE_AVERAGE);
+
+ /*
+ * Identify, Phase Door, Cure Light Wounds, etc. are
+ * just average
+ */
+ if (k_ptr->cost < 100) return (SENSE_AVERAGE);
+
+ /* Enchant Armor, *Identify*, Restore Stat, etc. */
+ if (k_ptr->cost < 10000) return (SENSE_GOOD_LIGHT);
+
+ /* Acquirement, Deincarnation, Strength, Blood of Life, ... */
+ if (k_ptr->cost >= 10000) return (SENSE_GOOD_LIGHT);
+
+ break;
+ }
+
+ /* Food */
+ case TV_FOOD:
+ {
+ /* "Cursed" food */
+ if (k_ptr->cost == 0) return (SENSE_CURSED);
+
+ /* Artifacts */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Normal food (no magical properties) */
+ if (k_ptr->cost <= 10) return (SENSE_AVERAGE);
+
+ /* Everything else is good */
+ if (k_ptr->cost > 10) return (SENSE_GOOD_LIGHT);
+
+ break;
+ }
+ }
+
+ /* No feeling */
+ return (SENSE_NONE);
+}
+
+
+/*
+ * Can a player be resurrected?
+ */
+static bool granted_resurrection(void)
+{
+ PRAY_GOD(GOD_ERU)
+ {
+ if (p_ptr->grace > 100000)
+ {
+ if (magik(70)) return (TRUE);
+ else return (FALSE);
+ }
+ }
+ return (FALSE);
+}
+
+byte select_sense(object_type *o_ptr, bool ok_combat, bool ok_magic)
+{
+ /* Valid "tval" codes */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_MSTAFF:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_BOOMERANG:
+ case TV_TRAPKIT:
+ {
+ if (ok_combat) return 1;
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ if (ok_magic) return 2;
+ break;
+ }
+
+ /* Dual use? */
+ case TV_DAEMON_BOOK:
+ {
+ if (ok_combat || ok_magic) return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Sense the inventory
+ *
+ * Combat items (weapons and armour) - Fast, weak if combat skill < 10, strong
+ * otherwise.
+ *
+ * Magic items (scrolls, staffs, wands, potions etc) - Slow, weak if
+ * magic skill < 10, strong otherwise.
+ *
+ * It shouldn't matter a lot to discriminate against magic users, because
+ * they learn one form of ID or another, and because most magic items are
+ * easy_know.
+ */
+static void sense_inventory(void)
+{
+ int i, combat_lev, magic_lev;
+
+ bool heavy_combat, heavy_magic;
+ bool ok_combat, ok_magic;
+
+ byte feel;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /*** Check for "sensing" ***/
+
+ /* No sensing when confused */
+ if (p_ptr->confused) return;
+
+ /* Can we pseudo id */
+#if 0
+
+ if (0 == rand_int(133 - get_skill_scale(SKILL_COMBAT, 130))) ok_combat = TRUE;
+ if (0 == rand_int(133 - get_skill_scale(SKILL_MAGIC, 130))) ok_magic = TRUE;
+
+#endif
+
+ /*
+ * In Angband, the chance of pseudo-id uses two different formulae:
+ *
+ * (1) Fast. 0 == rand_int(BASE / (plev * plev + 40)
+ * (2) Slow. 0 == rand_int(BASE / (plev + 5)
+ *
+ * Warriors: Fase with BASE == 9000
+ * Magi: Slow with BASE == 240000
+ * Priests: Fast with BASE == 10000
+ * Rogues: Fase with BASE == 20000
+ * Rangers: Slow with BASE == 120000
+ * Paladins: Fast with BASE == 80000
+ *
+ * In other words, those who have identify spells are penalised.
+ * The problems with Pern/Tome since it externalised player classes
+ * is that it uses the same and slow formula for spellcasters and
+ * fighters.
+ *
+ * In the following code, combat item pseudo-ID improves exponentially,
+ * (fast with BASE 9000) and magic one linear (slow with base 60000 --
+ * twice faster than V rangers).
+ *
+ * I hope this makes it closer to the original model -- pelpel
+ */
+
+ /* The combat skill affects weapon/armour pseudo-ID */
+ combat_lev = get_skill(SKILL_COMBAT);
+
+ /* Use the fast formula */
+ ok_combat = (0 == rand_int(9000L / (combat_lev * combat_lev + 40)));
+
+ /* The magic skill affects magic item pseudo-ID */
+ magic_lev = get_skill(SKILL_MAGIC);
+
+ /*
+ * Use the slow formula, because spellcasters have id spells
+ *
+ * Lowered the base value because V rangers are known to have
+ * pretty useless pseudo-ID. This should make it ten times more often.
+ */
+ ok_magic = (0 == rand_int(12000L / (magic_lev + 5)));
+
+ /* Both ID rolls failed */
+ if (!ok_combat && !ok_magic) return;
+
+ /* Higher skill levels give the player better sense of items */
+ heavy_combat = (combat_lev > 10) ? TRUE : FALSE;
+ heavy_magic = (magic_lev > 10) ? TRUE : FALSE;
+
+
+ /*** Sense everything ***/
+
+ /* Check everything */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ byte okay = 0;
+
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Valid "tval" codes */
+ okay = select_sense(o_ptr, ok_combat, ok_magic);
+
+ /* Skip non-sense machines */
+ if (!okay) continue;
+
+ /* We know about it already, do not tell us again */
+ if (o_ptr->ident & (IDENT_SENSE)) continue;
+
+ /* It is fully known, no information needed */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Occasional failure on inventory items */
+ if ((i < INVEN_WIELD) && (0 != rand_int(5))) continue;
+
+ /* Check for a feeling */
+ if (okay == 1)
+ {
+ feel = (heavy_combat ? value_check_aux1(o_ptr) : value_check_aux2(o_ptr));
+ }
+ else
+ {
+ feel = (heavy_magic ? value_check_aux1_magic(o_ptr) : value_check_aux2_magic(o_ptr));
+ }
+
+ /* Skip non-feelings */
+ if (feel == SENSE_NONE) continue;
+
+ /* Stop everything */
+ if (disturb_minor) disturb(0, 0);
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Message (equipment) */
+ if (i >= INVEN_WIELD)
+ {
+ msg_format("You feel the %s (%c) you are %s %s %s...",
+ o_name, index_to_label(i), describe_use(i),
+ ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+ }
+
+ /* Message (inventory) */
+ else
+ {
+ msg_format("You feel the %s (%c) in your pack %s %s...",
+ o_name, index_to_label(i),
+ ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+ }
+
+ /* We have "felt" it */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Set sense property */
+ o_ptr->sense = feel;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Squelch ! */
+ squeltch_inventory();
+}
+
+
+/*
+ * Go to any level (ripped off from wiz_jump)
+ */
+static void pattern_teleport(void)
+{
+ /* Ask for level */
+ if (get_check("Teleport level? "))
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ sprintf(ppp, "Teleport to level (0-%d): ", 99);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+ else if (get_check("Normal teleport? "))
+ {
+ teleport_player(200);
+ return;
+ }
+ else
+ {
+ return;
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > 99) command_arg = 99;
+
+ /* Accept request */
+ msg_format("You teleport to dungeon level %d.", command_arg);
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Change level */
+ dun_level = command_arg;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Returns TRUE if we are on the Straight Road...
+ */
+static bool pattern_effect(void)
+{
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_START) ||
+ (cave[p_ptr->py][p_ptr->px].feat > FEAT_PATTERN_XTRA2)) return (FALSE);
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_END)
+ {
+ (void)set_poisoned(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_blind(0);
+ (void)set_afraid(0);
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ (void)restore_level();
+ (void)hp_player(1000);
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_PATTERN_OLD);
+ msg_print("This section of the Straight Road looks less powerful.");
+ }
+
+
+ /*
+ * We could make the healing effect of the
+ * Pattern center one-time only to avoid various kinds
+ * of abuse, like luring the win monster into fighting you
+ * in the middle of the pattern...
+ */
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_OLD)
+ {
+ /* No effect */
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA1)
+ {
+ pattern_teleport();
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA2)
+ {
+ if (!(p_ptr->invuln))
+ take_hit(200, "walking the corrupted Straight Road");
+ }
+
+ else
+ {
+ if (!(p_ptr->invuln))
+ take_hit(damroll(1, 3), "walking the Straight Road");
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * If player has inscribed the object with "!!", let him know when it's
+ * recharged. -LM-
+ */
+static void recharged_notice(object_type *o_ptr)
+{
+ char o_name[80];
+
+ cptr s;
+
+
+ /* No inscription */
+ if (!o_ptr->note) return;
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process notification request. */
+ while (s)
+ {
+ /* Find another '!' */
+ if (s[1] == '!')
+ {
+ /* Describe (briefly) */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Notify the player */
+ if (o_ptr->number > 1)
+ {
+ msg_format("Your %s are recharged.", o_name);
+ }
+ else
+ {
+ msg_format("Your %s is recharged.", o_name);
+ }
+
+ /* Done. */
+ return;
+ }
+
+ /* Keep looking for '!'s */
+ s = strchr(s + 1, '!');
+ }
+}
+
+
+
+/*
+ * Regenerate hit points -RAK-
+ */
+static void regenhp(int percent)
+{
+ s32b new_chp, new_chp_frac;
+
+ int old_chp;
+
+
+ /* Only if alive */
+ if (!(p_ptr->necro_extra & CLASS_UNDEAD))
+ {
+ /* Save the old hitpoints */
+ old_chp = p_ptr->chp;
+
+ /* Extract the new hitpoints */
+ new_chp = ((long)p_ptr->mhp) * percent + PY_REGEN_HPBASE;
+
+ /* div 65536 */
+ p_ptr->chp += new_chp >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT;
+
+ /* mod 65536 */
+ new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac;
+
+ if (new_chp_frac >= 0x10000L)
+ {
+ p_ptr->chp_frac = new_chp_frac - 0x10000L;
+ p_ptr->chp++;
+ }
+ else
+ {
+ p_ptr->chp_frac = new_chp_frac;
+ }
+
+ /* Fully healed */
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Notice changes */
+ if (old_chp != p_ptr->chp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+}
+
+
+/*
+ * Regenerate mana points -RAK-
+ */
+static void regenmana(int percent)
+{
+ s32b new_mana, new_mana_frac;
+
+ int old_csp;
+
+ /* Incraese regen with int */
+ percent += adj_str_blow[p_ptr->stat_ind[A_INT]] * 3;
+
+ old_csp = p_ptr->csp;
+ new_mana = ((long)p_ptr->msp) * percent + PY_REGEN_MNBASE;
+
+ /* div 65536 */
+ p_ptr->csp += new_mana >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->csp < 0) && (old_csp > 0))
+ {
+ p_ptr->csp = MAX_SHORT;
+ }
+
+ /* mod 65536 */
+ new_mana_frac = (new_mana & 0xFFFF) + p_ptr->csp_frac;
+
+ if (new_mana_frac >= 0x10000L)
+ {
+ p_ptr->csp_frac = new_mana_frac - 0x10000L;
+ p_ptr->csp++;
+ }
+ else
+ {
+ p_ptr->csp_frac = new_mana_frac;
+ }
+
+ /* Must set frac to zero even if equal */
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Redraw mana */
+ if (old_csp != p_ptr->csp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+
+
+
+
+/*
+ * Regenerate the monsters (once per 100 game turns)
+ *
+ * XXX XXX XXX Should probably be done during monster turns.
+ */
+static void regen_monsters(void)
+{
+ int i, frac;
+
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ /* Allow regeneration (if needed) */
+ if (o_ptr->pval2 < o_ptr->pval3)
+ {
+ /* Hack -- Base regeneration */
+ frac = o_ptr->pval3 / 100;
+
+ /* Hack -- Minimal regeneration rate */
+ if (!frac) frac = 1;
+
+ /* Hack -- Some monsters regenerate quickly */
+ if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2;
+
+
+ /* Hack -- Regenerate */
+ o_ptr->pval2 += frac;
+
+ /* Do not over-regenerate */
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ /* Redraw (later) */
+ p_ptr->redraw |= (PR_MH);
+ }
+ }
+
+ /* Regenerate everyone */
+ for (i = 1; i < m_max; i++)
+ {
+ /* Check the i'th monster */
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Dont regen bleeding/poisonned monsters */
+ if (m_ptr->bleeding || m_ptr->poisoned) continue;
+
+ /* Allow regeneration (if needed) */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ /* Hack -- Base regeneration */
+ frac = m_ptr->maxhp / 100;
+
+ /* Hack -- Minimal regeneration rate */
+ if (!frac) frac = 1;
+
+ /* Hack -- Some monsters regenerate quickly */
+ if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2;
+
+
+ /* Hack -- Regenerate */
+ m_ptr->hp += frac;
+
+ /* Do not over-regenerate */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == i) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+}
+
+
+/*
+ * Forcibly pseudo-identify an object in the inventory
+ * (or on the floor)
+ */
+bool psychometry(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ byte feel;
+
+ cptr q, s;
+
+
+ /* Get an item */
+ q = "Meditate on which item? ";
+ s = "You have nothing appropriate.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* It is fully known, no information needed */
+ if ((object_known_p(o_ptr)) || (o_ptr->ident & IDENT_SENSE))
+ {
+ msg_print("You cannot find out anything more about that.");
+ return (TRUE);
+ }
+
+ /* Check for a feeling */
+ feel = value_check_aux1_magic(o_ptr);
+ if (feel == SENSE_NONE) feel = value_check_aux1(o_ptr);
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Skip non-feelings */
+ if (!feel)
+ {
+ msg_format("You do not perceive anything unusual about the %s.", o_name);
+ return (TRUE);
+ }
+
+ msg_format("You feel that the %s %s %s...",
+ o_name, ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]);
+
+ /* We have "felt" it */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Set sense property */
+ o_ptr->sense = feel;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+/*
+ * Does an object decay?
+ *
+ * Should belong to object1.c, renamed to object_decays() -- pelpel
+ */
+bool decays(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f3 & TR3_DECAY) return (TRUE);
+
+ return (FALSE);
+}
+
+
+static int process_lasting_spell(s16b music)
+{
+ int oldtop, use_mana;
+
+ if (music > 0) return FALSE;
+
+ oldtop = lua_gettop(L);
+
+ music = -music;
+
+ /* Push the function */
+ lua_getglobal(L, "exec_lasting_spell");
+
+ /* Push the spell */
+ tolua_pushnumber(L, music);
+
+ /* Call the function */
+ if (lua_call(L, 1, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling lasting spell");
+ return 0;
+ }
+
+ use_mana = tolua_getnumber(L, -(lua_gettop(L) - oldtop), 0);
+ lua_settop(L, oldtop);
+ return use_mana;
+}
+
+static void gere_class_special()
+{
+ switch (p_ptr->druid_extra2)
+ {
+ /* Lay a path of mana on the floor */
+ case CLASS_MANA_PATH:
+ {
+ /* Does the player have enought mana ? */
+ if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255))
+ {
+ p_ptr->druid_extra = 0;
+ p_ptr->druid_extra2 = CLASS_NONE;
+ msg_print("You stop laying a mana path.");
+ }
+ else
+ {
+ /* Use some mana */
+ p_ptr->csp -= (p_ptr->druid_extra & 255);
+
+ if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE)
+ {
+ /* Absorb some of the mana of the grid */
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 50;
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ /* Set the new grid mana */
+ cave[p_ptr->py][p_ptr->px].mana = p_ptr->druid_extra & 255;
+ }
+ else
+ {
+ int m = cave[p_ptr->py][p_ptr->px].mana;
+
+ if (m + (p_ptr->druid_extra & 255) > 255)
+ {
+ cave[p_ptr->py][p_ptr->px].mana = 255;
+ }
+ else
+ {
+ cave[p_ptr->py][p_ptr->px].mana += p_ptr->druid_extra & 255;
+ }
+ }
+ }
+
+ break;
+ }
+
+ /* Lay a path of mana on the floor */
+ case CLASS_WINDS_MANA:
+ {
+ /* Does the player have enought mana ? */
+ if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255))
+ {
+ p_ptr->druid_extra = CLASS_NONE;
+ msg_print("You stop expulsing mana winds.");
+ }
+ else
+ {
+ int dam = 0;
+
+ /* Use some mana */
+ p_ptr->csp -= (p_ptr->druid_extra & 255);
+
+ if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE)
+ {
+ dam = (p_ptr->druid_extra & 255) + 256;
+ }
+ else
+ {
+ dam = (p_ptr->druid_extra & 255);
+ }
+
+ fire_explosion(p_ptr->py, p_ptr->px, GF_WINDS_MANA, 2, dam);
+ }
+
+ break;
+ }
+
+ case CLASS_CANALIZE_MANA:
+ {
+ if (p_ptr->druid_extra & CLASS_CANALIZE_MANA_EXTRA)
+ {
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 10;
+ }
+ else
+ {
+ p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 20;
+ }
+
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ cave[p_ptr->py][p_ptr->px].mana = 0;
+
+ break;
+ }
+
+ /* CLASS_NONE, possibly others? */
+ default:
+ {
+ /* No mana update */
+ return;
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static void check_music()
+{
+ int use_mana;
+
+ /* Music sung by player */
+ if (!p_ptr->music_extra) return;
+
+ use_mana = process_lasting_spell(p_ptr->music_extra);
+
+ if (p_ptr->csp < use_mana)
+ {
+ msg_print("You stop your spell.");
+ p_ptr->music_extra = MUSIC_NONE;
+ p_ptr->music_extra2 = MUSIC_NONE;
+ }
+ else
+ {
+ p_ptr->csp -= use_mana;
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * Generate the feature effect
+ */
+void apply_effect(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ feature_type *f_ptr = &f_info[c_ptr->feat];
+
+
+ if (f_ptr->d_frequency[0] != 0)
+ {
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (f_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % f_ptr->d_frequency[i]) == 0) &&
+ ((f_ptr->d_side[i] != 0) || (f_ptr->d_dice[i] != 0)))
+ {
+ int l, dam = 0;
+ int d = f_ptr->d_dice[i], s = f_ptr->d_side[i];
+
+ if (d == -1) d = p_ptr->lev;
+ if (s == -1) s = p_ptr->lev;
+
+ /* Roll damage */
+ for (l = 0; l < d; l++)
+ {
+ dam += randint(s);
+ }
+
+ /* Apply damage */
+ project( -100, 0, y, x, dam, f_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_HIDE);
+
+ /* Hack -- notice death */
+ if (!alive || death) return;
+ }
+ }
+ }
+}
+
+
+#if 0
+/*
+ * Activate corruptions' effects on player
+ *
+ * All the rolls against arbitrarily chosen numbers are normalised
+ * (i.e. zero). They might have some cabalistic(?) significance,
+ * but I seriously doubt if processors take care of the Judeo-Christian
+ * tradition :) -- pelpel
+ */
+static void process_corruption_effects(void)
+{}
+
+#endif
+
+
+
+#ifdef pelpel
+
+/*
+ * Handle staying spell effects once every 10 game turns
+ */
+static void process_effects(void)
+{
+ int i, j;
+
+ /* Every 10 game turns */
+ if (turn % 10) return;
+
+ /* Not in the small-scale wilderness map */
+ if (p_ptr->wild_mode) return;
+
+
+ /* Handle spell effects */
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (i = 0; i < cur_wid - 1; i++)
+ {
+ int e = cave[j][i].effect;
+
+ if (e)
+ {
+ effect_type *e_ptr = &effects[e];
+
+ if (e_ptr->time)
+ {
+ /* Apply damage */
+ project(0, 0, j, i, e_ptr->dam, e_ptr->type,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ else
+ {
+ cave[j][i].effect = 0;
+ }
+
+ /* Hack -- notice death */
+ if (!alive || death) return;
+
+ if (((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST)) || ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST)))
+ {
+ if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1)
+ cave[j][i].effect = 0;
+ }
+ }
+ }
+ }
+
+
+ /* Reduce & handle effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ /* Skip empty slots */
+ if (effects[i].time == 0) continue;
+
+ /* Reduce duration */
+ effects[i].time--;
+
+ /* Creates a "wave" effect*/
+ if (effects[i].flags & EFF_WAVE)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->rad++;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ /* Creates a "storm" effect*/
+ else if (effects[i].flags & EFF_STORM)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->cy = p_ptr->py;
+ e_ptr->cx = p_ptr->px;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ }
+
+ /* Apply sustained effect in the player grid, if any */
+ apply_effect(p_ptr->py, p_ptr->px);
+}
+
+#endif /* pelpel */
+
+
+/* XXX XXX XXX */
+bool is_recall = FALSE;
+
+
+/*
+ * Handle certain things once every 10 game turns
+ *
+ * Note that a single movement in the overhead wilderness mode
+ * consumes 132 times as much energy as a normal one...
+ */
+static void process_world(void)
+{
+ timer_type *t_ptr;
+
+ int x, y, i, j;
+
+ int regen_amount;
+ bool cave_no_regen = FALSE;
+ int upkeep_factor = 0;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+ object_kind *k_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /*
+ * Every 10 game turns -- which means this section is invoked once
+ * in a player turn under the normal speed, and 132 times in a move
+ * in the reduced map mode.
+ */
+ if (turn % 10) return;
+
+ /*
+ * I don't know if this is the right thing to do because I'm totally
+ * ignorant (yes, I must admit that...) about the scripting part of
+ * the game, but since there have been complaints telling us it
+ * runs terribly slow in the reduced map mode... -- pelpel
+ *
+ * Note to coders: if it is desirable to make this active in the
+ * reduced map mode, remove the if condition surrounding the line
+ * and move the code inside into every 1000 game turns section.
+ */
+ if (dun_level || (!p_ptr->wild_mode))
+ {
+ /* Let the script live! */
+ process_hooks(HOOK_PROCESS_WORLD, "()");
+
+ /* Handle the player song */
+ check_music();
+ }
+
+ /* Handle the timers */
+ for (t_ptr = gl_timers; t_ptr != NULL; t_ptr = t_ptr->next)
+ {
+ if (!t_ptr->enabled) continue;
+
+ t_ptr->countdown--;
+ if (!t_ptr->countdown)
+ {
+ t_ptr->countdown = t_ptr->delay;
+ call_lua(t_ptr->callback, "()", "");
+ }
+ }
+
+ /* Handle class special actions */
+ gere_class_special();
+
+ /* Check the fate */
+ if (fate_option && (p_ptr->lev > 10))
+ {
+ /*
+ * WAS: == 666 against randint(50000).
+ * Since CPU's don't know Judeo-Christian / Cabalistic traditions,
+ * and since comparisons with zero is more efficient in many
+ * architectures...
+ */
+ if (rand_int(50000) == 0) gain_fate(0);
+ }
+
+ /*** Is the wielded monsters still hypnotised ***/
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ if ((randint(1000) < r_ptr->level - ((p_ptr->lev * 2) + get_skill(SKILL_SYMBIOTIC))))
+ {
+ msg_format("%s thinks you are not enough in symbiosis.",
+ symbiote_name(TRUE));
+ carried_make_attack_normal(o_ptr->pval);
+ }
+ }
+
+ /*** Check the Time and Load ***/
+
+ /*
+ * Every 1000 game turns -- which means this section is invoked every
+ * 100 player turns under the normal speed, and slightly more than
+ * one per move in the reduced map.
+ */
+ if ((turn % 1000) == 0)
+ {
+ /* Check time and load */
+ if ((0 != check_time()) || (0 != check_load()))
+ {
+ /* Warning */
+ if (closing_flag <= 2)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Count warnings */
+ closing_flag++;
+
+ /* Message */
+ msg_print("The gates to Middle Earth are closing...");
+ msg_print("Please finish up and/or save your game.");
+ }
+
+ /* Slam the gate */
+ else
+ {
+ /* Message */
+ msg_print("The gates to Middle Earth are now closed.");
+
+ /* Stop playing */
+ alive = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ }
+ }
+
+ /*** Attempt timed autosave ***/
+ if (autosave_t && autosave_freq)
+ {
+ if ((turn % ((s32b)autosave_freq * 10)) == 0)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+ }
+
+
+ /*** Handle the wilderness/town (sunshine) ***/
+
+ /* While in town/wilderness and not in the overworld mode */
+ if (!dun_level && !p_ptr->wild_mode)
+ {
+ /* Hack -- Daybreak/Nighfall in town */
+ if ((turn % ((10L * DAY) / 2)) == 0)
+ {
+ bool dawn;
+
+ /* Check for dawn */
+ dawn = ((turn % (10L * DAY)) == 0);
+
+ /* Day breaks */
+ if (dawn)
+ {
+ /* Message */
+ msg_print("The sun has risen.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Assume lit */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Hack -- Memorize lit grids if allowed */
+ if (view_perma_grids) c_ptr->info |= (CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+
+ /* Night falls */
+ else
+ {
+ /* Message */
+ msg_print("The sun has set.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Darken "boring" features */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+ }
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /* Tell a day passed */
+ if (((turn + (DAY_START * 10L)) % (10L * DAY)) == 0)
+ {
+ char buf[20];
+
+ sprintf(buf, get_day(bst(YEAR, turn) + START_YEAR));
+ cmsg_format(TERM_L_GREEN,
+ "Today it is %s of the %s year of the third age.",
+ get_month_name(bst(DAY, turn), wizard, FALSE), buf);
+ }
+
+ /* Set back the rewards once a day */
+ if ((turn % (10L * STORE_TURNS)) == 0)
+ {
+ /* Select new bounties. */
+ if (magik(20)) select_bounties();
+ }
+
+ /* Modify loan */
+ if (p_ptr->loan)
+ {
+ if (p_ptr->loan_time) p_ptr->loan_time--;
+
+ if (((turn % 5000) == 0) && !p_ptr->loan_time)
+ {
+ cmsg_print(TERM_RED, "You should pay your loan...");
+
+ p_ptr->loan += p_ptr->loan / 12;
+
+ if (p_ptr->loan > PY_MAX_GOLD) p_ptr->loan = PY_MAX_GOLD;
+
+ /* Do a nasty stuff */
+ if (p_ptr->wild_mode && rand_int(2))
+ {
+ /* Discount player items */
+ int z = 0, tries = 200;
+ object_type *o_ptr = NULL;
+
+ while (tries--)
+ {
+ z = rand_int(INVEN_TOTAL);
+ o_ptr = &p_ptr->inventory[z];
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->discount >= 100) continue;
+
+ break;
+ }
+
+ if (tries)
+ {
+ o_ptr->discount += 70;
+ if (o_ptr->discount >= 100) o_ptr->discount = 100;
+
+ inven_item_optimize(z);
+ inven_item_describe(z);
+
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ }
+
+ else
+ {
+ int merc = test_monster_name("Mean-looking mercenary");
+ int agent = test_monster_name("Agent of the black market");
+ int num = 5 + (p_ptr->lev / 3), z;
+
+ for (z = 0; z < num; z++)
+ {
+ int yy, xx, attempts = 200, m_idx;
+
+ /* Summon */
+ while (1)
+ {
+ scatter(&yy, &xx, p_ptr->py, p_ptr->px, 6, 0);
+
+ /* Accept an empty grid within the boundary */
+ if (in_bounds(yy, xx) && cave_floor_bold(yy, xx)) break;
+
+ /* Max number of retries reached */
+ if (--attempts == 0) break;
+ }
+
+ /* All the attempts failed */
+ if (attempts == 0) continue;
+
+ /* Summon a monster */
+ m_idx = place_monster_one(yy, xx, magik(80) ? merc : agent,
+ 0, FALSE, MSTATUS_ENEMY);
+
+ /* Level it */
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+
+ m_ptr->exp = MONSTER_EXP(p_ptr->lev * 2);
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+ }
+ }
+
+ /*** Process the monsters ***/
+
+ /* Check for creature generation. */
+ if (!p_ptr->wild_mode &&
+ !p_ptr->inside_arena &&
+ !p_ptr->inside_quest &&
+ (rand_int(d_info[(dun_level) ? dungeon_type : DUNGEON_WILDERNESS].max_m_alloc_chance) == 0))
+ {
+ /* Make a new monster */
+ if (!(dungeon_flags2 & DF2_NO_NEW_MONSTER))
+ {
+ (void)alloc_monster(MAX_SIGHT + 5, FALSE);
+ }
+ }
+
+ /* Hack -- Check for creature regeneration */
+ if (!p_ptr->wild_mode && ((turn % 100) == 0)) regen_monsters();
+
+
+ /*** Damage over Time ***/
+
+ /* Take damage from poison */
+ if (p_ptr->poisoned && !p_ptr->invuln)
+ {
+ /* Take damage */
+ take_hit(1, "poison");
+ }
+
+
+ /* Vampires take damage from sunlight */
+ if (p_ptr->sensible_lite)
+ {
+ if ((!dun_level) && (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ if (cave[p_ptr->py][p_ptr->px].info & (CAVE_GLOW))
+ {
+ /* Take damage */
+ msg_print("The sun's rays scorch your undead flesh!");
+ take_hit(1, "sunlight");
+ cave_no_regen = TRUE;
+ drop_from_wild();
+ }
+ }
+
+ if ((p_ptr->inventory[INVEN_LITE].tval != 0) &&
+ (p_ptr->inventory[INVEN_LITE].sval >= SV_LITE_GALADRIEL) &&
+ (p_ptr->inventory[INVEN_LITE].sval <= SV_STONE_LORE) &&
+ (p_ptr->inventory[INVEN_LITE].sval != SV_LITE_UNDEATH))
+ {
+ object_type * o_ptr = &p_ptr->inventory[INVEN_LITE];
+ char o_name [80];
+ char ouch [80];
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ msg_format("The %s scorches your undead flesh!", o_name);
+
+ cave_no_regen = TRUE;
+
+ /* Get an object description */
+ object_desc(o_name, o_ptr, TRUE, 0);
+
+ sprintf(ouch, "wielding %s", o_name);
+ take_hit(1, ouch);
+ }
+ }
+
+ /* Drown in deep water unless the player have levitation, water walking
+ water breathing, or magic breathing.*/
+ if (!p_ptr->ffall && !p_ptr->walk_water && !p_ptr->magical_breath &&
+ !p_ptr->water_breath &&
+ (cave[p_ptr->py][p_ptr->px].feat == FEAT_DEEP_WATER))
+ {
+ if (calc_total_weight() > ((weight_limit()) / 2))
+ {
+ /* Take damage */
+ msg_print("You are drowning!");
+ take_hit(randint(p_ptr->lev), "drowning");
+ cave_no_regen = TRUE;
+ }
+ }
+
+
+ /* Spectres -- take damage when moving through walls */
+
+ /*
+ * Added: ANYBODY takes damage if inside through walls
+ * without wraith form -- NOTE: Spectres will never be
+ * reduced below 0 hp by being inside a stone wall; others
+ * WILL BE!
+ */
+ if (!cave_floor_bold(p_ptr->py, p_ptr->px))
+ {
+ int feature = cave[p_ptr->py][p_ptr->px].feat;
+
+ /* Player can walk through or fly over trees */
+ if ((has_ability(AB_TREE_WALK) || p_ptr->fly) && (feature == FEAT_TREES))
+ {
+ /* Do nothing */
+ }
+ /* Player can climb over mountains */
+ else if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB))
+ {
+ /* Do nothing */
+ }
+ else if (PRACE_FLAG(PR1_SEMI_WRAITH) && (!p_ptr->wraith_form) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_CAN_PASS))
+ {
+ int amt = 1 + ((p_ptr->lev) / 5);
+
+ cave_no_regen = TRUE;
+ if (amt > p_ptr->chp - 1) amt = p_ptr->chp - 1;
+ take_hit(amt, " walls ...");
+ }
+ }
+
+
+ /* Take damage from cuts */
+ if ((p_ptr->cut) && !(p_ptr->invuln))
+ {
+ /* Mortal wound or Deep Gash */
+ if (p_ptr->cut > 200)
+ {
+ i = 3;
+ }
+
+ /* Severe cut */
+ else if (p_ptr->cut > 100)
+ {
+ i = 2;
+ }
+
+ /* Other cuts */
+ else
+ {
+ i = 1;
+ }
+
+ /* Take damage */
+ take_hit(i, "a fatal wound");
+ }
+
+
+ /*** Check the Food, and Regenerate ***/
+
+ /* Digest normally */
+ if (p_ptr->food < PY_FOOD_MAX)
+ {
+ /* Every 100 game turns */
+ if ((turn % 100) == 0)
+ {
+ int speed_use = p_ptr->pspeed;
+
+ /* Maximum */
+ if (speed_use > 199)
+ {
+ speed_use = 199;
+ }
+
+ /* Minimum */
+ else if (speed_use < 0)
+ {
+ speed_use = 0;
+ }
+
+ /* Basic digestion rate based on speed */
+ i = extract_energy[speed_use] * 2;
+
+ /* Regeneration takes more food */
+ if (p_ptr->regenerate) i += 30;
+
+ /* Regeneration takes more food */
+ if (p_ptr->tim_regen) i += p_ptr->tim_regen_pow / 10;
+
+ /* Invisibility consume a lot of food */
+ i += p_ptr->invis / 2;
+
+ /* Invulnerability consume a lot of food */
+ if (p_ptr->invuln) i += 40;
+
+ /* Wraith Form consume a lot of food */
+ if (p_ptr->wraith_form) i += 30;
+
+ /* Get the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Examine the sword */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hitpoints multiplier consume a lot of food */
+ if (o_ptr->k_idx && (f2 & (TR2_LIFE))) i += o_ptr->pval * 5;
+
+ /* Slow digestion takes less food */
+ if (p_ptr->slow_digest) i -= 10;
+
+ /* Minimal digestion */
+ if (i < 1) i = 1;
+
+ /* Digest some food */
+ (void)set_food(p_ptr->food - i);
+ }
+ }
+
+ /* Digest quickly when gorged */
+ else
+ {
+ /* Digest a lot of food */
+ (void)set_food(p_ptr->food - 100);
+ }
+
+ /* Starve to death (slowly) */
+ if (p_ptr->food < PY_FOOD_STARVE)
+ {
+ /* Calculate damage */
+ i = (PY_FOOD_STARVE - p_ptr->food) / 10;
+
+ /* Take damage */
+ if (!(p_ptr->invuln)) take_hit(i, "starvation");
+ }
+
+ /* Default regeneration */
+ regen_amount = PY_REGEN_NORMAL;
+
+ /* Getting Weak */
+ if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ /* Lower regeneration */
+ if (p_ptr->food < PY_FOOD_STARVE)
+ {
+ regen_amount = 0;
+ }
+ else if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ regen_amount = PY_REGEN_FAINT;
+ }
+ else
+ {
+ regen_amount = PY_REGEN_WEAK;
+ }
+
+ /* Getting Faint */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ /* Faint occasionally */
+ if (!p_ptr->paralyzed && (rand_int(100) < 10))
+ {
+ /* Message */
+ msg_print("You faint from the lack of food.");
+ disturb(1, 0);
+
+ /* Hack -- faint (bypass free action) */
+ (void)set_paralyzed(p_ptr->paralyzed + 1 + rand_int(5));
+ }
+ }
+ }
+
+ /* Are we walking the pattern? */
+ if (!p_ptr->wild_mode && pattern_effect())
+ {
+ cave_no_regen = TRUE;
+ }
+ else
+ {
+ /* Regeneration ability */
+ if (p_ptr->regenerate)
+ {
+ regen_amount = regen_amount * 2;
+ }
+ }
+
+
+ /* Searching or Resting */
+ if (p_ptr->searching || resting)
+ {
+ regen_amount = regen_amount * 2;
+ }
+
+ if (total_friends)
+ {
+ int upkeep_divider = 20;
+
+ if (has_ability(AB_PERFECT_CASTING))
+ upkeep_divider = 15;
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard) msg_format("Total friends: %d.", total_friends);
+
+#endif /* TRACK_FRIENDS */
+
+ if (total_friends > 1 + (p_ptr->lev / (upkeep_divider)))
+ {
+ upkeep_factor = (total_friend_levels);
+
+ if (upkeep_factor > 100) upkeep_factor = 100;
+ else if (upkeep_factor < 10) upkeep_factor = 10;
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard) msg_format("Levels %d, upkeep %d", total_friend_levels,
+ upkeep_factor);
+
+#endif /* TRACK_FRIENDS */
+ }
+ }
+
+ /* Regenerate the mana */
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ if (upkeep_factor)
+ {
+ s16b upkeep_regen = (((100 - upkeep_factor) * regen_amount) / 100);
+ regenmana(upkeep_regen);
+
+#ifdef TRACK_FRIENDS
+
+ if (wizard)
+ {
+ msg_format("Regen: %d/%d", upkeep_regen, regen_amount);
+ }
+
+#endif /* TRACK_FRIENDS */
+ }
+ else
+ {
+ regenmana(regen_amount);
+ }
+ }
+
+ /* Eru piety incraese with time */
+ if (((turn % 100) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode))
+ {
+ NOT_PRAY_GOD(GOD_ERU)
+ {
+ int inc = wisdom_scale(10);
+
+ /* Increase by wisdom/4 */
+ if (!inc) inc = 1;
+ inc_piety(GOD_ERU, inc);
+ }
+ }
+ /* Most gods piety decrease with time */
+ if (((turn % 300) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ GOD(GOD_MANWE)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ PRAY_GOD(GOD_MANWE)
+ dec++;
+ if (PRACE_FLAG(PR1_ELF))
+ dec -= wisdom_scale(2);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_MANWE, -dec);
+ }
+ GOD(GOD_MELKOR)
+ {
+ int dec = 8 - wisdom_scale(6);
+
+ PRAY_GOD(GOD_MELKOR)
+ dec++;
+ if (PRACE_FLAG(PR1_ELF))
+ dec += 5 - wisdom_scale(4);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_MELKOR, -dec);
+ }
+ PRAY_GOD(GOD_TULKAS)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_TULKAS, -dec);
+ }
+ }
+ /* Yavanna piety decrease with time */
+ if (((turn % 400) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ GOD(GOD_YAVANNA)
+ {
+ int dec = 5 - wisdom_scale(3);
+
+ /* Blech what an hideous hack */
+ if (!strcmp(rp_ptr->title + rp_name, "Ent"))
+ dec -= wisdom_scale(2);
+ if (dec < 1) dec = 1;
+ inc_piety(GOD_YAVANNA, -dec);
+ }
+ }
+ p_ptr->did_nothing = FALSE;
+
+ /* Increase regen by tim regen */
+ if (p_ptr->tim_regen) regen_amount += p_ptr->tim_regen_pow;
+
+ /* Poisoned or cut yields no healing */
+ if (p_ptr->poisoned) regen_amount = 0;
+ if (p_ptr->cut) regen_amount = 0;
+
+ /* Special floor -- Pattern, in a wall -- yields no healing */
+ if (cave_no_regen) regen_amount = 0;
+
+ /* Being over grass allows Yavanna to regen you */
+ PRAY_GOD(GOD_YAVANNA)
+ {
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_GRASS)
+ {
+ regen_amount += 200 + wisdom_scale(800);
+ }
+ }
+
+ /* Regenerate Hit Points if needed */
+ if ((p_ptr->chp < p_ptr->mhp) && !cave_no_regen)
+ {
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_END) &&
+ (cave[p_ptr->py][p_ptr->px].feat >= FEAT_PATTERN_START))
+ {
+ /* Hmmm. this should never happen? */
+ regenhp(regen_amount / 5);
+ }
+ else
+ {
+ regenhp(regen_amount);
+ }
+ }
+
+
+ /*** Timeout Various Things ***/
+
+ /* Handle temporary stat drains */
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0)
+ {
+ p_ptr->stat_cnt[i]--;
+ if (p_ptr->stat_cnt[i] == 0)
+ {
+ do_res_stat(i, FALSE);
+ }
+ }
+ }
+
+ /* Hack -- Hallucinating */
+ if (p_ptr->image)
+ {
+ (void)set_image(p_ptr->image - 1);
+ }
+
+ /* Holy Aura */
+ if (p_ptr->holy)
+ {
+ (void)set_holy(p_ptr->holy - 1);
+ }
+
+ /* Soul absorbtion */
+ if (p_ptr->absorb_soul)
+ {
+ (void)set_absorb_soul(p_ptr->absorb_soul - 1);
+ }
+
+ /* Undead loose Death Points */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ int old_chp = p_ptr->chp;
+ int warning = (p_ptr->mhp * hitpoint_warn / 10);
+
+ /* Bypass invulnerability and wraithform */
+ p_ptr->chp--;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Dead player */
+ if (p_ptr->chp < 0)
+ {
+ bool old_quick = quick_messages;
+
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ if (!last_words)
+ {
+ msg_print("You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ char death_message[80];
+
+ (void)get_rnd_line("death.txt", death_message);
+ msg_print(death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, "being undead too long");
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* No longer a winner */
+ total_winner = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Note death */
+ death = TRUE;
+
+ quick_messages = FALSE;
+ if (get_check("Make a last screenshot? "))
+ {
+ do_cmd_html_dump();
+ }
+ quick_messages = old_quick;
+
+ /* Dead */
+ return;
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->chp < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_chp > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ msg_print("*** LOW DEATHPOINT WARNING! ***");
+ msg_print(NULL);
+ }
+ }
+
+ /* Walk water */
+ if (p_ptr->walk_water)
+ {
+ (void)set_walk_water(p_ptr->walk_water - 1);
+ }
+
+ /* True Strike */
+ if (p_ptr->strike)
+ {
+ (void)set_strike(p_ptr->strike - 1);
+ }
+
+ /* Meditation */
+ if (p_ptr->meditation)
+ {
+ (void)set_meditation(p_ptr->meditation - 1);
+ }
+
+ /* Timed project */
+ if (p_ptr->tim_project)
+ {
+ (void)set_project(p_ptr->tim_project - 1, p_ptr->tim_project_gf, p_ptr->tim_project_dam, p_ptr->tim_project_rad, p_ptr->tim_project_flag);
+ }
+
+ /* Timed roots */
+ if (p_ptr->tim_roots)
+ {
+ (void)set_roots(p_ptr->tim_roots - 1, p_ptr->tim_roots_ac, p_ptr->tim_roots_dam);
+ }
+
+ /* Timed breath */
+ if (p_ptr->tim_water_breath)
+ {
+ (void)set_tim_breath(p_ptr->tim_water_breath - 1, FALSE);
+ }
+ if (p_ptr->tim_magic_breath)
+ {
+ (void)set_tim_breath(p_ptr->tim_magic_breath - 1, TRUE);
+ }
+
+ /* Timed regen */
+ if (p_ptr->tim_regen)
+ {
+ (void)set_tim_regen(p_ptr->tim_regen - 1, p_ptr->tim_regen_pow);
+ }
+
+ /* Timed Disrupt shield */
+ if (p_ptr->disrupt_shield)
+ {
+ (void)set_disrupt_shield(p_ptr->disrupt_shield - 1);
+ }
+
+ /* Timed Parasite */
+ if (p_ptr->parasite)
+ {
+ (void)set_parasite(p_ptr->parasite - 1, p_ptr->parasite_r_idx);
+ }
+
+ /* Timed Reflection */
+ if (p_ptr->tim_reflect)
+ {
+ (void)set_tim_reflect(p_ptr->tim_reflect - 1);
+ }
+
+ /* Timed Prob Travel */
+ if (p_ptr->prob_travel)
+ {
+ (void)set_prob_travel(p_ptr->prob_travel - 1);
+ }
+
+ /* Timed Time Resistance */
+ if (p_ptr->tim_res_time)
+ {
+ (void)set_tim_res_time(p_ptr->tim_res_time - 1);
+ }
+
+ /* Timed Levitation */
+ if (p_ptr->tim_ffall)
+ {
+ (void)set_tim_ffall(p_ptr->tim_ffall - 1);
+ }
+ if (p_ptr->tim_fly)
+ {
+ (void)set_tim_fly(p_ptr->tim_fly - 1);
+ }
+
+ /* Thunderstorm */
+ if (p_ptr->tim_thunder)
+ {
+ int dam = damroll(p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2);
+ int i, tries = 600;
+ monster_type *m_ptr = NULL;
+
+ while (tries)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i = rand_range(1, m_max - 1)];
+
+ tries--;
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Cant see ? cant hit */
+ if (!los(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) continue;
+
+ /* Do not hurt friends! */
+ if (is_friend(m_ptr) >= 0) continue;
+ break;
+ }
+
+ if (tries)
+ {
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0);
+ msg_format("Lightning strikes %s.", m_name);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_ELEC,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_LITE,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_SOUND,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+
+ (void)set_tim_thunder(p_ptr->tim_thunder - 1, p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2);
+ }
+
+ /* Poisonned hands */
+ if (p_ptr->tim_poison)
+ {
+ (void)set_poison(p_ptr->tim_poison - 1);
+ }
+
+ /* Timed Fire Aura */
+ if (p_ptr->tim_fire_aura)
+ {
+ (void)set_tim_fire_aura(p_ptr->tim_fire_aura - 1);
+ }
+
+ /* Brightness */
+ if (p_ptr->tim_lite)
+ {
+ (void)set_lite(p_ptr->tim_lite - 1);
+ }
+
+ /* Blindness */
+ if (p_ptr->blind)
+ {
+ (void)set_blind(p_ptr->blind - 1);
+ }
+
+ /* Timed no_breeds */
+ if (no_breeds)
+ {
+ (void)set_no_breeders(no_breeds - 1);
+ }
+
+ /* Timed mimic */
+ if (p_ptr->tim_mimic)
+ {
+ (void)set_mimic(p_ptr->tim_mimic - 1, p_ptr->mimic_form, p_ptr->mimic_level);
+ }
+
+ /* Timed special move commands */
+ if (p_ptr->immov_cntr)
+ {
+ p_ptr->immov_cntr--;
+ }
+
+ /* Timed invisibility */
+ if (p_ptr->tim_invisible)
+ {
+ (void)set_invis(p_ptr->tim_invisible - 1, p_ptr->tim_inv_pow);
+ }
+
+ /* Times see-invisible */
+ if (p_ptr->tim_invis)
+ {
+ (void)set_tim_invis(p_ptr->tim_invis - 1);
+ }
+
+ if (multi_rew)
+ {
+ multi_rew = FALSE;
+ }
+
+ /* Timed esp */
+ if (p_ptr->tim_esp)
+ {
+ (void)set_tim_esp(p_ptr->tim_esp - 1);
+ }
+
+ /* Timed infra-vision */
+ if (p_ptr->tim_infra)
+ {
+ (void)set_tim_infra(p_ptr->tim_infra - 1);
+ }
+
+ /* Paralysis */
+ if (p_ptr->paralyzed)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed - 1);
+ }
+
+ /* Confusion */
+ if (p_ptr->confused)
+ {
+ (void)set_confused(p_ptr->confused - 1);
+ }
+
+ /* Afraid */
+ if (p_ptr->afraid)
+ {
+ (void)set_afraid(p_ptr->afraid - 1);
+ }
+
+ /* Fast */
+ if (p_ptr->fast)
+ {
+ (void)set_fast(p_ptr->fast - 1, p_ptr->speed_factor);
+ }
+
+ /* Light speed */
+ if (p_ptr->lightspeed)
+ {
+ (void)set_light_speed(p_ptr->lightspeed - 1);
+ }
+
+ /* Slow */
+ if (p_ptr->slow)
+ {
+ (void)set_slow(p_ptr->slow - 1);
+ }
+
+ /* Protection from evil */
+ if (p_ptr->protevil)
+ {
+ (void)set_protevil(p_ptr->protevil - 1);
+ }
+
+ /* Protection from good */
+ if (p_ptr->protgood)
+ {
+ (void)set_protgood(p_ptr->protgood - 1);
+ }
+
+ /* Protection from undead */
+ if (p_ptr->protundead)
+ {
+ (void)set_protundead(p_ptr->protundead - 1);
+ }
+
+ /* Invulnerability */
+ if (p_ptr->invuln)
+ {
+ (void)set_invuln(p_ptr->invuln - 1);
+ }
+
+ /* Wraith form */
+ if (p_ptr->tim_wraith)
+ {
+ (void)set_shadow(p_ptr->tim_wraith - 1);
+ }
+
+ /* Heroism */
+ if (p_ptr->hero)
+ {
+ (void)set_hero(p_ptr->hero - 1);
+ }
+
+ /* Super Heroism */
+ if (p_ptr->shero)
+ {
+ (void)set_shero(p_ptr->shero - 1);
+ }
+
+ /* Blessed */
+ if (p_ptr->blessed)
+ {
+ (void)set_blessed(p_ptr->blessed - 1);
+ }
+
+ /* Shield */
+ if (p_ptr->shield)
+ {
+ (void)set_shield(p_ptr->shield - 1, p_ptr->shield_power, p_ptr->shield_opt, p_ptr->shield_power_opt, p_ptr->shield_power_opt2);
+ }
+
+ /* Oppose Acid */
+ if (p_ptr->oppose_acid)
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid - 1);
+ }
+
+ /* Oppose Lightning */
+ if (p_ptr->oppose_elec)
+ {
+ (void)set_oppose_elec(p_ptr->oppose_elec - 1);
+ }
+
+ /* Oppose Fire */
+ if (p_ptr->oppose_fire)
+ {
+ (void)set_oppose_fire(p_ptr->oppose_fire - 1);
+ }
+
+ /* Oppose Cold */
+ if (p_ptr->oppose_cold)
+ {
+ (void)set_oppose_cold(p_ptr->oppose_cold - 1);
+ }
+
+ /* Oppose Poison */
+ if (p_ptr->oppose_pois)
+ {
+ (void)set_oppose_pois(p_ptr->oppose_pois - 1);
+ }
+
+ /* Oppose Light & Dark */
+ if (p_ptr->oppose_ld)
+ {
+ (void)set_oppose_ld(p_ptr->oppose_ld - 1);
+ }
+
+ /* Oppose Chaos & Confusion */
+ if (p_ptr->oppose_cc)
+ {
+ (void)set_oppose_cc(p_ptr->oppose_cc - 1);
+ }
+
+ /* Oppose Sound & Shards */
+ if (p_ptr->oppose_ss)
+ {
+ (void)set_oppose_ss(p_ptr->oppose_ss - 1);
+ }
+
+ /* Oppose Nexus */
+ if (p_ptr->oppose_nex)
+ {
+ (void)set_oppose_nex(p_ptr->oppose_nex - 1);
+ }
+
+ /* Mental Barrier */
+ if (p_ptr->tim_mental_barrier)
+ {
+ (void)set_mental_barrier(p_ptr->tim_mental_barrier - 1);
+ }
+
+ /* The rush */
+ if (p_ptr->rush)
+ {
+ (void)set_rush(p_ptr->rush - 1);
+ }
+
+
+ /* Timed mimicry */
+ if (get_skill(SKILL_MIMICRY))
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ if ((att & CLASS_LEGS) || (att & CLASS_WALL) || (att & CLASS_ARMS))
+ {
+ value--;
+
+ if (!value)
+ {
+ if (att & CLASS_LEGS) msg_print("You lose your extra pair of legs.");
+ if (att & CLASS_ARMS) msg_print("You lose your extra pair of arms.");
+ if (att & CLASS_WALL) msg_print("You lose your affinity for walls.");
+
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_LEGS);
+ att &= ~(CLASS_WALL);
+
+ if (disturb_state) disturb(0, 0);
+ }
+
+ p_ptr->update |= (PU_BODY);
+ p_ptr->mimic_extra = att + (value << 16);
+ }
+ }
+
+
+ /*** Poison and Stun and Cut ***/
+
+ /* Poison */
+ if (p_ptr->poisoned)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Apply some healing */
+ (void)set_poisoned(p_ptr->poisoned - adjust);
+ }
+
+ /* Stun */
+ if (p_ptr->stun)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Apply some healing */
+ (void)set_stun(p_ptr->stun - adjust);
+ }
+
+ /* Cut */
+ if (p_ptr->cut)
+ {
+ int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1);
+
+ /* Hack -- Truly "mortal" wound */
+ if (p_ptr->cut > 1000) adjust = 0;
+
+ /* Apply some healing */
+ (void)set_cut(p_ptr->cut - adjust);
+ }
+
+ /* Hack - damage done by the dungeon -SC- */
+ if ((dun_level != 0) && (d_ptr->d_frequency[0] != 0))
+ {
+ int i, j, k;
+
+ /* Apply damage to every grid in the dungeon */
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (d_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % d_ptr->d_frequency[i]) == 0) &&
+ ((d_ptr->d_side[i] != 0) || (d_ptr->d_dice[i] != 0)))
+ {
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (k = 0; k < cur_wid - 1; k++)
+ {
+ int l, dam = 0;
+
+ if (!(dungeon_flags1 & DF1_DAMAGE_FEAT))
+ {
+ /* If the grid is empty, skip it */
+ if ((cave[j][k].o_idx == 0) &&
+ ((j != p_ptr->py) && (i != p_ptr->px))) continue;
+ }
+
+ /* Let's not hurt poor monsters */
+ if (cave[j][k].m_idx) continue;
+
+ /* Roll damage */
+ for (l = 0; l < d_ptr->d_dice[i]; l++)
+ {
+ dam += randint(d_ptr->d_side[i]);
+ }
+
+ /* Apply damage */
+ project( -100, 0, j, k, dam, d_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ }
+ }
+ }
+ }
+
+#ifndef pelpel
+
+ /* handle spell effects */
+ if (!p_ptr->wild_mode)
+ {
+ /*
+ * I noticed significant performance degrade after the introduction
+ * of staying spell effects. I believe serious optimisation effort
+ * is required before another release.
+ *
+ * More important is to fix that display weirdness...
+ *
+ * It seems that the game never expects that monster deaths and
+ * terrain feature changes should happen here... Moving these
+ * to process_player() [before resting code, with "every 10 game turn"
+ * 'if'] may or may not fix the problem... -- pelpel to DG
+ */
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (i = 0; i < cur_wid - 1; i++)
+ {
+ int e = cave[j][i].effect;
+
+ if (e)
+ {
+ effect_type *e_ptr = &effects[e];
+
+ if (e_ptr->time)
+ {
+ /* Apply damage */
+ project(0, 0, j, i, e_ptr->dam, e_ptr->type,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ else
+ {
+ cave[j][i].effect = 0;
+ }
+
+ if ((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST))
+ {
+ if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1)
+ cave[j][i].effect = 0;
+ }
+ else if ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST))
+ {
+ cave[j][i].effect = 0;
+ }
+
+ lite_spot(j, i);
+ }
+ }
+ }
+
+ /* Reduce & handle effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ /* Skip empty slots */
+ if (effects[i].time == 0) continue;
+
+ /* Reduce duration */
+ effects[i].time--;
+
+ /* Creates a "wave" effect*/
+ if (effects[i].flags & EFF_WAVE)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y, z;
+
+ e_ptr->rad++;
+
+ /* What a frelling ugly line of ifs ... */
+ if (effects[i].flags & EFF_DIR8)
+ for (y = e_ptr->cy - e_ptr->rad, z = 0; y <= e_ptr->cy; y++, z++)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR2)
+ for (y = e_ptr->cy, z = e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++, z--)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR6)
+ for (x = e_ptr->cx, z = e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++, z--)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR4)
+ for (x = e_ptr->cx - e_ptr->rad, z = 0; x <= e_ptr->cx; x++, z++)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR9)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR1)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR7)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR3)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ /* This is *slow* -- pelpel */
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ /* Creates a "storm" effect*/
+ else if (effects[i].flags & EFF_STORM)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->cy = p_ptr->py;
+ e_ptr->cx = p_ptr->px;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) <= e_ptr->rad))
+ {
+ cave[y][x].effect = i;
+ lite_spot(y, x);
+ }
+ }
+ }
+ }
+ }
+
+ apply_effect(p_ptr->py, p_ptr->px);
+ }
+
+#endif /* !pelpel */
+
+ /* Arg cannot breath? */
+ if ((dungeon_flags2 & DF2_WATER_BREATH) && (!p_ptr->water_breath))
+ {
+ cmsg_print(TERM_L_RED, "You cannot breathe water! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+ if ((dungeon_flags2 & DF2_NO_BREATH) && (!p_ptr->magical_breath))
+ {
+ cmsg_print(TERM_L_RED, "There is no air there! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+
+ /*
+ * Every 1500 turns, warn about any Black Breath not gotten from
+ * an equipped object, and stop any resting. -LM-
+ *
+ * It's apparent that someone has halved the frequency... -- pelpel
+ */
+ if (((turn % 3000) == 0) && p_ptr->black_breath)
+ {
+ u32b f1, f2, f3, f4, f5;
+
+ bool be_silent = FALSE;
+
+ /* check all equipment for the Black Breath flag. */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* No messages if object has the flag, to avoid annoyance. */
+ if (f4 & (TR4_BLACK_BREATH)) be_silent = TRUE;
+
+ }
+ /* If we are allowed to speak, warn and disturb. */
+
+ if (!be_silent)
+ {
+ cmsg_print(TERM_L_DARK, "The Black Breath saps your soul!");
+ disturb(0, 0);
+ }
+ }
+
+
+ /*** Process Light ***/
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ {
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- Use some fuel */
+ if ((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0))
+ {
+ /* Decrease life-span */
+ o_ptr->timeout--;
+
+ /* Hack -- notice interesting fuel steps */
+ if ((o_ptr->timeout < 100) || ((o_ptr->timeout % 100) == 0))
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Hack -- Special treatment when blind */
+ if (p_ptr->blind)
+ {
+ /* Hack -- save some light for later */
+ if (o_ptr->timeout == 0) o_ptr->timeout++;
+ }
+
+ /* The light is now out */
+ else if (o_ptr->timeout < 1)
+ {
+ disturb(0, 0);
+ cmsg_print(TERM_YELLOW, "Your light has gone out!");
+ }
+
+ /* The light is getting dim */
+ else if (o_ptr->timeout < 100)
+ {
+ if (disturb_minor) disturb(0, 0);
+ cmsg_print(TERM_YELLOW, "Your light is growing faint.");
+ drop_from_wild();
+ }
+ }
+ }
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+
+ /*** Process Inventory ***/
+
+ /*
+ * Handle experience draining. In Oangband, the effect is worse,
+ * especially for high-level characters. As per Tolkien, hobbits
+ * are resistant.
+ */
+ if (p_ptr->black_breath)
+ {
+ byte chance = 0;
+ int plev = p_ptr->lev;
+
+ if (PRACE_FLAG(PR1_RESIST_BLACK_BREATH)) chance = 2;
+ else chance = 5;
+
+ if ((rand_int(100) < chance) && (p_ptr->exp > 0))
+ {
+ p_ptr->exp -= 1 + plev / 5;
+ p_ptr->max_exp -= 1 + plev / 5;
+ (void)do_dec_stat(rand_int(6), STAT_DEC_NORMAL);
+ check_experience();
+ }
+ }
+
+ /* Drain Mana */
+ if (p_ptr->drain_mana && p_ptr->csp)
+ {
+ p_ptr->csp -= p_ptr->drain_mana;
+ if (magik(30)) p_ptr->csp -= p_ptr->drain_mana;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ /* Partial summons drain mana */
+ if (p_ptr->maintain_sum)
+ {
+ u32b oldcsp = p_ptr->csp;
+ p_ptr->csp -= p_ptr->maintain_sum / 10000;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ disturb(0, 0);
+
+ p_ptr->maintain_sum = 0;
+ }
+ else
+ {
+ /* Leave behind any fractional sp */
+ p_ptr->maintain_sum -= (oldcsp - p_ptr->csp) * 10000;
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Drain Hitpoints */
+ if (p_ptr->drain_life)
+ {
+ int drain = p_ptr->drain_life + rand_int(p_ptr->mhp / 100);
+
+ p_ptr->chp -= (drain < p_ptr->chp ? drain : p_ptr->chp);
+
+ if (p_ptr->chp == 0)
+ {
+ disturb(0, 0);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Handle experience draining */
+ if (p_ptr->exp_drain)
+ {
+ if ((rand_int(100) < 10) && (p_ptr->exp > 0))
+ {
+ p_ptr->exp--;
+ p_ptr->max_exp--;
+ check_experience();
+ }
+ }
+
+ /* Process equipment */
+ for (j = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Get the object */
+ o_ptr = &p_ptr->inventory[i];
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ /* TY Curse */
+ if ((f3 & TR3_TY_CURSE) && (rand_int(TY_CURSE_CHANCE) == 0))
+ {
+ activate_ty_curse();
+ }
+
+ /* DG Curse */
+ if ((f4 & TR4_DG_CURSE) && (rand_int(DG_CURSE_CHANCE) == 0))
+ {
+ activate_dg_curse();
+
+ /* The object recurse itself ! */
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ /* Auto Curse */
+ if ((f3 & TR3_AUTO_CURSE) && (rand_int(AUTO_CURSE_CHANCE) == 0))
+ {
+ /* The object recurse itself ! */
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ /*
+ * Hack: Uncursed teleporting items (e.g. Dragon Weapons)
+ * can actually be useful!
+ */
+ if ((f3 & TR3_TELEPORT) && (rand_int(100) < 1))
+ {
+ if ((o_ptr->ident & IDENT_CURSED) && !p_ptr->anti_tele)
+ {
+ disturb(0, 0);
+
+ /* Teleport player */
+ teleport_player(40);
+ }
+ else
+ {
+ if (p_ptr->wild_mode ||
+ (o_ptr->note && strchr(quark_str(o_ptr->note), '.')))
+ {
+ /* Do nothing */
+ /* msg_print("Teleport aborted.") */;
+ }
+ else if (get_check("Teleport? "))
+ {
+ disturb(0, 0);
+ teleport_player(50);
+ }
+ }
+ }
+
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack: Skip wielded lights that need fuel (already handled above) */
+ if ((i == INVEN_LITE) && (o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE)) continue;
+
+ /* Recharge activatable objects */
+ if (o_ptr->timeout > 0)
+ {
+ /* Recharge */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ recharged_notice(o_ptr);
+ j++;
+ }
+ }
+
+ /* Recharge second spell in Mage Staffs of Spells */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && (o_ptr->xtra2 > 0))
+ {
+ /* Recharge */
+ o_ptr->xtra2--;
+
+ /* Notice changes */
+ if (o_ptr->xtra2 == 0) j++;
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Recharge rods */
+ for (j = 0, i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+ k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ inven_item_increase(i, -99);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Examine all charging rods or stacks of charging rods. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Always notice */
+ j++;
+
+ /* Notice changes, provide message if object is inscribed. */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Examine all charging random artifacts */
+ if ((f5 & TR5_ACTIVATE_NO_WIELD) && (o_ptr->timeout > 0))
+ {
+ /* Charge it */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ j++;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Decay objects in pack */
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ pack_decay(i);
+ j++;
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+
+ if (o_ptr->timeout == 0)
+ {
+ o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ mx = p_ptr->px;
+ my = p_ptr->py + 1;
+ get_pos_player(5, &my, &mx);
+ msg_print("Your egg hatches!");
+ place_monster_aux(my, mx, o_ptr->pval2, FALSE, FALSE, MSTATUS_PET);
+
+ m_ptr = &m_list[cave[my][mx].m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if ((r_ptr->flags9 & RF9_IMPRESED) && can_create_companion())
+ {
+ msg_format("And you have given the imprint to your %s!",
+ r_name + r_ptr->name);
+ m_ptr->status = MSTATUS_COMPANION;
+ }
+
+ inven_item_increase(i, -1);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+ j++;
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Combine pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* Feel the p_ptr->inventory */
+ if (dun_level || (!p_ptr->wild_mode))
+ {
+ sense_inventory();
+ }
+
+ /*** Process Objects ***/
+
+ /* Process objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Access object */
+ o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ floor_item_increase(i, -99);
+ floor_item_optimize(i);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Recharge rods on the ground. No messages. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Do not overflow */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+ /* Decay objects on the ground*/
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Turn it into a skeleton */
+ if (o_ptr->pval <= 0)
+ {
+ floor_decay(i);
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+ if (o_ptr->timeout > 0) o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ mx = o_ptr->ix;
+ my = o_ptr->iy;
+ get_pos_player(5, &my, &mx);
+ msg_print("An egg hatches!");
+ place_monster_one(my, mx, o_ptr->pval2, 0, FALSE, MSTATUS_ENEMY);
+ floor_item_increase(i, -1);
+ floor_item_describe(i);
+ floor_item_optimize(i);
+ }
+ }
+ }
+
+
+ /*** Involuntary Movement ***/
+
+ /* Delayed Word-of-Recall */
+ if (p_ptr->word_recall)
+ {
+ /* Can we ? */
+ if (process_hooks(HOOK_RECALL, "()", ""))
+ {
+ p_ptr->word_recall = 0;
+ }
+
+ /* No recall. sorry */
+ else if (dungeon_flags2 & DF2_NO_RECALL_OUT)
+ {
+ cmsg_print(TERM_L_DARK, "You cannot recall from here.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Cannot WoR out of death fate levels */
+ else if (dungeon_type == DUNGEON_DEATH)
+ {
+ cmsg_print(TERM_L_DARK, "You are fated to die here. FIGHT for your life!");
+ p_ptr->word_recall = 0;
+ }
+
+ /* I think the 'inside_quest' code belongs here -- pelpel */
+
+ /* They cannot use word of recall until reaching surface */
+ else if (p_ptr->astral)
+ {
+ msg_print("As an astral being you can't recall.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Normal WoR */
+ else
+ {
+ /*
+ * HACK: Autosave BEFORE resetting the recall counter (rr9)
+ * The player is yanked up/down as soon as
+ * he loads the autosaved game.
+ */
+ if (autosave_l && (p_ptr->word_recall == 1))
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Make SURE that persistent levels are saved
+ * I don't know if this is needed, but I'm getting reports,
+ * so I'm adding this extra save -- Neil
+ */
+ save_dungeon();
+
+ /* Count down towards recall */
+ p_ptr->word_recall--;
+
+ /* Activate the recall */
+ if (p_ptr->word_recall == 0)
+ {
+ /* Disturbing! */
+ disturb(0, 0);
+
+ /* Determine the level */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("The recall is cancelled by a powerful magic force!");
+ }
+ else if (dun_level)
+ {
+ msg_print("You feel yourself yanked upwards!");
+
+ p_ptr->recall_dungeon = dungeon_type;
+ dungeon_type = DUNGEON_WILDERNESS;
+ dun_level = 0;
+
+ is_recall = TRUE;
+
+ p_ptr->inside_quest = 0;
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You feel yourself yanked downwards!");
+
+ /* New depth */
+ dungeon_type = p_ptr->recall_dungeon;
+ dun_level = max_dlv[dungeon_type];
+ if (dun_level < 1) dun_level = 1;
+
+ /* Reset player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ /* Leaving */
+ is_recall = TRUE;
+
+ p_ptr->leaving = TRUE;
+ p_ptr->wild_mode = FALSE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+ }
+ }
+ }
+}
+
+
+/*
+ * Verify use of "wizard" mode
+ */
+static bool enter_wizard_mode(void)
+{
+#if 0
+ /* Ask first time */
+ if (!(noscore & 0x0002))
+#else
+ /* Ask first time, but not while loading a dead char with the -w option */
+ if (!noscore && !(p_ptr->chp < 0))
+#endif
+ {
+ /* Mention effects */
+ msg_print("Wizard mode is for debugging and experimenting.");
+ msg_print("The game will not be scored if you enter wizard mode.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to enter wizard mode? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0002;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+#ifdef ALLOW_WIZARD
+
+/*
+ * Verify use of "debug" commands
+ */
+static bool enter_debug_mode(void)
+{
+ /* Ask first time */
+#if 0
+ if (!(noscore & 0x0008))
+#else
+if (!noscore && !wizard)
+#endif
+ {
+ /* Mention effects */
+ msg_print("The debug commands are for debugging and experimenting.");
+ msg_print("The game will not be scored if you use debug commands.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to use debug commands? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0008;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Declare the Debug Routines
+ */
+extern void do_cmd_debug(void);
+
+#endif /* ALLOW_WIZARD */
+
+
+#ifdef ALLOW_BORG
+
+/*
+ * Verify use of "borg" commands
+ */
+static bool enter_borg_mode(void)
+{
+ /* Ask first time */
+ if (!(noscore & 0x0010))
+ {
+ /* Mention effects */
+ msg_print("The borg commands are for debugging and experimenting.");
+ msg_print("The game will not be scored if you use borg commands.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to use borg commands? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0010;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- Declare the Ben Borg
+ */
+extern void do_cmd_borg(void);
+
+#endif /* ALLOW_BORG */
+
+
+
+/*
+ * Parse and execute the current command
+ * Give "Warning" on illegal commands.
+ *
+ * XXX XXX XXX Make some "blocks"
+ */
+static void process_command(void)
+{
+ char error_m[80];
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Process the appropriate hooks */
+ if (process_hooks(HOOK_KEYPRESS, "(d)", command_cmd)) return;
+
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Ignore */
+ case ESCAPE:
+ case ' ':
+ case 0:
+ {
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+#ifdef ALLOW_QUITTING
+
+ case KTRL('L'):
+ {
+ quit("CHEATER");
+ break;
+ }
+
+#endif
+
+#ifdef ALLOW_WIZARD
+
+ /*** Wizard Commands ***/
+
+ /* Toggle Wizard Mode */
+ case KTRL('W'):
+ {
+ if (wizard)
+ {
+ wizard = FALSE;
+ msg_print("Wizard mode off.");
+ }
+ else if (enter_wizard_mode())
+ {
+ wizard = TRUE;
+ msg_print("Wizard mode on.");
+ }
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ break;
+ }
+
+ /* Special "debug" commands */
+ case KTRL('A'):
+ {
+ /* Enter debug mode */
+ if (enter_debug_mode())
+ {
+ do_cmd_debug();
+ }
+ break;
+ }
+
+#endif /* ALLOW_WIZARD */
+
+
+#ifdef ALLOW_BORG
+
+ /* Special "borg" commands */
+ case KTRL('Z'):
+ {
+ /* Enter borg mode */
+ if (enter_borg_mode())
+ {
+ if (!p_ptr->wild_mode) do_cmd_borg();
+ }
+
+ break;
+ }
+
+#endif /* ALLOW_BORG */
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_takeoff();
+ p_ptr->redraw |= (PR_MH);
+ break;
+ }
+
+ /* Drop an item */
+ case 'd':
+ {
+ if (do_control_drop()) break;
+ if (!p_ptr->wild_mode) do_cmd_drop();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ if (p_ptr->control) break;
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ if (p_ptr->control) break;
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ if (do_control_inven()) break;
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+ /*** Standard "Movement" Commands ***/
+
+ /* Alter a grid */
+ case '+':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_alter();
+ break;
+ }
+
+ /* Dig a tunnel */
+ case 'T':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_tunnel();
+ break;
+ }
+
+ /* Move (usually pick up things) */
+ case ';':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(always_pickup, TRUE);
+
+ break;
+ }
+
+ /* Move (usually do not pick up) */
+ case '-':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(!always_pickup, TRUE);
+
+ break;
+ }
+
+
+ /*** Running, Resting, Searching, Staying */
+
+ /* Begin Running -- Arg is Max Distance */
+ case '.':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_run();
+ break;
+ }
+
+ /* Stay still (usually pick things up) */
+ case ',':
+ {
+ if (do_control_pickup()) break;
+ do_cmd_stay(always_pickup);
+ break;
+ }
+
+ /* Stay still (usually do not pick up) */
+ case 'g':
+ {
+ if (p_ptr->control) break;
+ do_cmd_stay(!always_pickup);
+ break;
+ }
+
+ /* Rest -- Arg is time */
+ case 'R':
+ {
+ if (p_ptr->control) break;
+ do_cmd_rest();
+ break;
+ }
+
+ /* Search for traps/doors */
+ case 's':
+ {
+ if (p_ptr->control) break;
+ do_cmd_search();
+ break;
+ }
+
+ /* Toggle search mode */
+ case 'S':
+ {
+ if (p_ptr->control) break;
+ do_cmd_toggle_search();
+ break;
+ }
+
+
+ /*** Stairs and Doors and Chests and Traps ***/
+
+ /* Enter store */
+ case '_':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_store();
+ break;
+ }
+
+#if 0 /* Merged with the '>' command -- pelpel */
+
+ /* Enter quest level -KMW- */
+ case '[':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_quest();
+ break;
+ }
+
+#endif /* 0 */
+
+ /* Go up staircase */
+ case '<':
+ {
+ object_type *o_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (p_ptr->wild_mode || dun_level || is_quest(dun_level))
+ {
+ do_cmd_go_up();
+ }
+ else if (vanilla_town)
+ {
+ /* Do nothing */
+ }
+ /* Don't let the player < when he'd just drop right back down */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ msg_print("You are too hungry to travel.");
+ }
+ else if (p_ptr->sensible_lite &&
+ (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ /* Burn vampires! burn! */
+ msg_print("You can't travel during the day!");
+ }
+ else if (p_ptr->sensible_lite &&
+ (o_ptr->tval != 0) &&
+ (o_ptr->sval >= SV_LITE_GALADRIEL) &&
+ (o_ptr->sval <= SV_STONE_LORE) &&
+ (o_ptr->sval != SV_LITE_UNDEATH))
+ {
+ msg_print("Travel with your present light would be unsafe.");
+ }
+ else if (p_ptr->cut || p_ptr->poisoned)
+ {
+ /* I actually died this way once -- neil */
+ msg_print("You are too injured to travel.");
+ }
+ else if (ambush_flag)
+ {
+ msg_print("To flee the ambush you have to reach the edge of the map.");
+ }
+ else if (o_ptr->tval && (f4 & TR4_FUEL_LITE) && (o_ptr->timeout < 100))
+ {
+ msg_print("Your light is too low on fuel for you to travel with it.");
+ }
+ /* TODO: make the above stuff use this hook */
+ else if (!process_hooks(HOOK_FORBID_TRAVEL, "()"))
+ {
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ change_wild_mode();
+
+ /* Update the known wilderness */
+ reveal_wilderness_around_player(p_ptr->wilderness_y,
+ p_ptr->wilderness_x,
+ 0, WILDERNESS_SEE_RADIUS);
+ }
+
+ break;
+ }
+
+ /* Go down staircase */
+ case '>':
+ {
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (!p_ptr->wild_mode)
+ {
+ do_cmd_go_down();
+ }
+
+ /* Special cases */
+ else
+ {
+ if ((wf_info[wild_map[p_ptr->py][p_ptr->px].feat].entrance >= 1000) ||
+ (wild_map[p_ptr->py][p_ptr->px].entrance > 1000))
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+ do_cmd_go_down();
+
+ if (dun_level == 0)
+ {
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+ }
+ else
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ }
+ }
+ else
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ }
+ }
+
+ break;
+ }
+
+ /* Open a door or chest */
+ case 'o':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_open();
+ break;
+ }
+
+ /* Close a door */
+ case 'c':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_close();
+ break;
+ }
+
+ /* Give an item */
+ case 'y':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_give();
+ break;
+ }
+
+ /* Chat */
+ case 'Y':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_chat();
+ break;
+ }
+
+ /* Jam a door with spikes */
+ case 'j':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_spike();
+ break;
+ }
+
+ /* Bash a door */
+ case 'B':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_bash();
+ break;
+ }
+
+ /* Disarm a trap or chest */
+ case 'D':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_disarm();
+ break;
+ }
+
+
+ /*** Magic and Prayers ***/
+
+ /* Interact with skills */
+ case 'G':
+ {
+ if (p_ptr->control) break;
+ do_cmd_skill();
+ break;
+ }
+
+ /* Interact with abilities */
+ case 'N':
+ {
+ if (p_ptr->control) break;
+ do_cmd_ability();
+ break;
+ }
+
+ /* Browse a book */
+ case 'b':
+ {
+ if (p_ptr->control) break;
+ do_cmd_browse();
+ break;
+ }
+
+ /* Cast a spell */
+ case 'm':
+ {
+ if (do_control_magic()) break;
+
+ /* No magic in the overworld map */
+ if (p_ptr->wild_mode) break;
+
+ /* Neither in the Arena */
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+
+ break;
+ }
+ do_cmd_activate_skill();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Pray a prayer */
+ case 'p':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_pray();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Issue commands to pets */
+ case 'P':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_pet();
+ break;
+ }
+
+ /* Cut up a corpse */
+ case 'h':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_cut_corpse();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Cure some meat */
+ case 'K':
+ {
+ if (p_ptr->control) break;
+ do_cmd_cure_meat();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Steal an item form a monster */
+ case 'Z':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_steal();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /*** Use various objects ***/
+
+ /* Inscribe an object */
+ case '{':
+ {
+ if (p_ptr->control) break;
+ do_cmd_inscribe();
+ break;
+ }
+
+ /* Uninscribe an object */
+ case '}':
+ {
+ if (p_ptr->control) break;
+ do_cmd_uninscribe();
+ break;
+ }
+
+ /* Activate an artifact */
+ case 'A':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_activate();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Eat some food */
+ case 'E':
+ {
+ if (p_ptr->control) break;
+ do_cmd_eat_food();
+ break;
+ }
+
+ /* Fuel your lantern/torch */
+ case 'F':
+ {
+ if (p_ptr->control) break;
+ do_cmd_refill();
+ break;
+ }
+
+ /* Fire an item */
+ case 'f':
+ {
+ object_type *j_ptr;
+
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("You're in the arena now. This is hand-to-hand!");
+ msg_print(NULL);
+ break;
+ }
+
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ if (process_hooks(HOOK_FIRE, "(O)", j_ptr))
+ break;
+
+ if (j_ptr->tval == TV_BOOMERANG)
+ {
+ do_cmd_boomerang();
+ }
+ else
+ {
+ do_cmd_fire();
+ }
+
+ break;
+ }
+
+ /* Throw an item */
+ case 'v':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("You're in the arena now. This is hand-to-hand!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_throw();
+ break;
+ }
+
+ /* Aim a wand */
+ case 'a':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_aim_wand();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Zap a rod */
+ case 'z':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_zap_rod();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Quaff a potion */
+ case 'q':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_quaff_potion();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Drink from a fountain -SC- */
+ case 'H':
+ {
+ cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ if (p_ptr->control) break;
+ if ((c_ptr->feat == FEAT_FOUNTAIN) ||
+ (c_ptr->feat == FEAT_EMPTY_FOUNTAIN))
+ {
+ do_cmd_drink_fountain();
+ squeltch_inventory();
+ squeltch_grid();
+ }
+ else
+ {
+ msg_print("You see no fountain here.");
+ }
+
+ break;
+ }
+
+ /* Read a scroll */
+ case 'r':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_read_scroll();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Use a staff */
+ case 'u':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_use_staff();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Use racial power */
+ case 'U':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("The arena absorbs all attempted magic!");
+ msg_print(NULL);
+ break;
+ }
+
+ do_cmd_power();
+ squeltch_inventory();
+ squeltch_grid();
+ break;
+ }
+
+ /* Sacrifice at an altar */
+ case 'O':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ if (PRACE_FLAG(PR1_NO_GOD))
+ {
+ msg_print("You cannot worship gods.");
+ }
+ else
+ {
+ do_cmd_sacrifice();
+ }
+
+ break;
+ }
+
+ /*** Looking at Things (nearby or on map) ***/
+
+ /* Full dungeon map */
+ case 'M':
+ {
+ if (!p_ptr->wild_mode) do_cmd_view_map();
+ break;
+ }
+
+ /* Locate player on map */
+ case 'L':
+ {
+ do_cmd_locate();
+ break;
+ }
+
+ /* Look around */
+ case 'l':
+ {
+ do_cmd_look();
+ break;
+ }
+
+ /* Target monster or location */
+ case '*':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_target();
+ break;
+ }
+
+ /* Engrave the floor */
+ case 'x':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) break;
+
+ /* No point in engraving if there isn't any mana on this grid. */
+ /* DG - actualy there is, it doesnt break macros */
+ do_cmd_sense_grid_mana();
+ do_cmd_engrave();
+
+ break;
+ }
+
+ /*** Help and Such ***/
+
+ /* Help */
+ case '?':
+ {
+ do_cmd_help();
+ break;
+ }
+
+ /* Identify symbol */
+ case '/':
+ {
+ do_cmd_query_symbol();
+ break;
+ }
+
+ /* Character description */
+ case 'C':
+ {
+ do_cmd_change_name();
+ break;
+ }
+
+
+ /*** System Commands ***/
+
+ /* Hack -- User interface */
+ case '!':
+ {
+ (void)Term_user(0);
+ break;
+ }
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ if (!p_ptr->wild_mode)
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Show quest status -KMW- */
+ case KTRL('Q'):
+ case CMD_QUEST:
+{
+ do_cmd_checkquest();
+ break;
+ }
+
+ /* Redraw the screen */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ break;
+ }
+
+#ifndef VERIFY_SAVEFILE
+
+ /* Hack -- Save and don't quit */
+ case KTRL('S'):
+ {
+ is_autosave = FALSE;
+ do_cmd_save_game();
+ break;
+ }
+
+#endif /* VERIFY_SAVEFILE */
+
+ case KTRL('T'):
+ {
+ do_cmd_time();
+ }
+ break;
+
+ /* Save and quit */
+ case KTRL('X'):
+ {
+ alive = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ break;
+ }
+
+ /* Quit (commit suicide) */
+ case 'Q':
+ {
+ do_cmd_suicide();
+ break;
+ }
+
+ /* Activate cmovie */
+ case '|':
+ {
+ /* Stop ? */
+ if (do_movies == 1)
+ {
+ do_stop_cmovie();
+ msg_print("Cmovie recording stopped.");
+ }
+ else
+ {
+ do_start_cmovie();
+ }
+ break;
+ }
+
+ /* Extended command */
+ case '#':
+ {
+ do_cmd_cli();
+ break;
+ }
+
+ /* Check artifacts, uniques, objects */
+ case '~':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Commands only available as extended commands: */
+
+ /* Extended command help. */
+ case CMD_CLI_HELP:
+ {
+ do_cmd_cli_help();
+ break;
+ }
+
+ /* Connect to IRC. */
+ case CMD_IRC_CONNECT:
+ {
+ irc_connect();
+ break;
+ }
+
+ /* Speak on IRC. */
+ case CMD_IRC_CHAT:
+ {
+ irc_chat();
+ break;
+ }
+
+ /* Disconnect from IRC. */
+ case CMD_IRC_DISCON:
+ {
+ irc_disconnect();
+ break;
+ }
+
+ /* Game time. */
+ case CMD_SHOW_TIME:
+ {
+ do_cmd_time();
+ break;
+ }
+
+ /* Check skills. */
+ case CMD_SHOW_SKILL:
+ {
+ do_cmd_skill();
+ break;
+ }
+
+ /* Check abilities. */
+ case CMD_SHOW_ABILITY:
+ {
+ do_cmd_ability();
+ break;
+ }
+
+ /* Save a html screenshot. */
+ case CMD_DUMP_HTML:
+ {
+ do_cmd_html_dump();
+ break;
+ }
+
+ /* Record a macro. */
+ case '$':
+ case CMD_MACRO:
+ {
+ do_cmd_macro_recorder();
+ break;
+ }
+ case CMD_BLUNDER:
+ {
+ if (do_control_walk()) break;
+ do_cmd_walk(always_pickup, FALSE);
+ break;
+ }
+ /* Hack -- Unknown command */
+ default:
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+
+ /* Would like to have an option disabling this -- pelpel */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("error.txt", error_m);
+ sound(SOUND_ILLEGAL);
+ msg_print(error_m);
+ }
+ else
+ {
+ prt("Type '?' for help.", 0, 0);
+ }
+
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Process the player
+ *
+ * Notice the annoying code to handle "pack overflow", which
+ * must come first just in case somebody manages to corrupt
+ * the savefiles by clever use of menu commands or something.
+ */
+void process_player(void)
+{
+ int i, j;
+
+ int speed_use;
+
+
+ /*** Apply energy ***/
+
+ if (hack_corruption)
+ {
+ msg_print("You feel different!");
+ (void)gain_random_corruption(0);
+ hack_corruption = FALSE;
+ }
+
+ /* Obtain current speed */
+ speed_use = p_ptr->pspeed;
+
+ /* Maximum value */
+ if (speed_use > 199)
+ {
+ speed_use = 199;
+ }
+
+ /* Minimum value */
+ else if (speed_use < 0)
+ {
+ speed_use = 0;
+ }
+
+ /* Give the player some energy */
+ p_ptr->energy += extract_energy[speed_use];
+
+ /* No turn yet */
+ if (p_ptr->energy < 100) return;
+
+
+ /*** Check for interupts ***/
+
+ /* Complete resting */
+ if (resting < 0)
+ {
+ /* Basic resting */
+ if (resting == -1)
+ {
+ /* Stop resting */
+ if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp >= p_ptr->msp))
+ {
+ disturb(0, 0);
+ }
+ }
+
+ /* Complete resting */
+ else if (resting == -2)
+ {
+ bool stop = TRUE;
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ r_ptr = &r_info[o_ptr->pval];
+
+ /* Stop resting */
+ if ((!p_ptr->drain_life) && (p_ptr->chp != p_ptr->mhp)) stop = FALSE;
+ if ((!p_ptr->drain_mana) && (p_ptr->csp != p_ptr->msp)) stop = FALSE;
+ if (o_ptr->pval2 < o_ptr->pval3) stop = FALSE;
+ if (p_ptr->blind || p_ptr->confused) stop = FALSE;
+ if (p_ptr->poisoned || p_ptr->afraid) stop = FALSE;
+ if (p_ptr->stun || p_ptr->cut) stop = FALSE;
+ if (p_ptr->slow || p_ptr->paralyzed) stop = FALSE;
+ if (p_ptr->image || p_ptr->word_recall) stop = FALSE;
+ if (p_ptr->immov_cntr != 0) stop = FALSE;
+
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0) stop = FALSE;
+ }
+
+ if (stop)
+ {
+ disturb(0, 0);
+ }
+ p_ptr->redraw |= (PR_STATE);
+ }
+ }
+
+ /* Handle "abort" */
+ if (!avoid_abort)
+ {
+ /* Check for "player abort" (semi-efficiently for resting) */
+ if (running || command_rep || (resting && !(resting & 0x0F)))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+
+ /* Check for a key */
+ if (inkey())
+ {
+ /* Flush input */
+ flush();
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- Show a Message */
+ msg_print("Cancelled.");
+ }
+ }
+ }
+
+
+ /*** Handle actual user input ***/
+
+ /* Repeat until out of energy */
+ while (p_ptr->energy >= 100)
+ {
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- mark current wilderness location as known */
+ if (!p_ptr->wild_mode && dun_level == 0)
+ wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].known = TRUE;
+
+
+ /* Place the cursor on the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Refresh (optional) */
+ if (fresh_before) Term_fresh();
+
+ /* Hack -- Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ char o_name[80];
+
+ object_type *o_ptr;
+
+ /* Access the slot to be dropped */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Disturbing */
+ disturb(0, 0);
+
+ /* Warning */
+ msg_print("Your pack overflows!");
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it (carefully) near the player */
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* Modify, Describe, Optimize */
+ inven_item_increase(item, -255);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+ }
+
+
+ /* Hack -- cancel "lurking browse mode" */
+ if (!command_new) command_see = FALSE;
+
+
+ /* Assume free turn */
+ energy_use = 0;
+
+
+ /* Paralyzed or Knocked Out */
+ if ((p_ptr->paralyzed) || (p_ptr->stun >= 100))
+ {
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Resting */
+ else if (resting)
+ {
+ /* Timed rest */
+ if (resting > 0)
+ {
+ /* Reduce rest count */
+ resting--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+ }
+
+ p_ptr->did_nothing = TRUE;
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Running */
+ else if (running)
+ {
+ /* Take a step */
+ run_step(0);
+
+ /*
+ * Commented out because it doesn't make any sense
+ * to require a player holding down direction keys
+ * instead of using running commands when s/he follows
+ * Eru and do the opposite for the other deities -- pelpel
+ */
+ /* p_ptr->did_nothing = TRUE; */
+ }
+
+ /* Repeated command */
+ else if (command_rep)
+ {
+ /* Count this execution */
+ command_rep--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Hack -- Assume messages were seen */
+ msg_flag = FALSE;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Process the command */
+ process_command();
+
+ p_ptr->did_nothing = TRUE;
+ }
+
+ /* Normal command */
+ else
+ {
+ /* Place the cursor on the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Get a command (normal) */
+ request_command(FALSE);
+
+ /* Process the command */
+ process_command();
+ }
+
+
+ /*** Clean up ***/
+
+ /* Significant */
+ if (energy_use)
+ {
+ /* Use some energy */
+ p_ptr->energy -= energy_use;
+
+
+ /* Hack -- constant hallucination */
+ if (p_ptr->image) p_ptr->redraw |= (PR_MAP);
+
+
+ /* Shimmer monsters if needed */
+ if (!avoid_other && !use_graphics && shimmer_monsters)
+ {
+ /* Clear the flag */
+ shimmer_monsters = FALSE;
+
+ /* Shimmer multi-hued monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ /* Access monster */
+ m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Access the monster race */
+ r_ptr = race_inf(m_ptr);
+
+ /* Skip non-multi-hued monsters */
+ if (!(r_ptr->flags1 & (RF1_ATTR_MULTI))) continue;
+
+ /* Reset the flag */
+ shimmer_monsters = TRUE;
+
+ /* Redraw regardless */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+ }
+ }
+
+ /* Shimmer objects if needed and requested */
+ if (!avoid_other && !avoid_shimmer && !use_graphics &&
+ shimmer_objects)
+ {
+ /* Clear the flag */
+ shimmer_objects = FALSE;
+
+ /* Shimmer multi-hued objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object -- for speed only base items are allowed to shimmer */
+ object_type *o_ptr = &o_list[i];
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip dead or carried objects */
+ if ((!o_ptr->k_idx) || (!o_ptr->ix)) continue;
+
+ /* Skip non-multi-hued monsters */
+ if (!(k_ptr->flags5 & (TR5_ATTR_MULTI))) continue;
+
+ /* Reset the flag */
+ shimmer_objects = TRUE;
+
+ /* Redraw regardless */
+ lite_spot(o_ptr->iy, o_ptr->ix);
+ }
+ }
+
+ /*
+ * Shimmer features if needed and requested
+ *
+ * Note: this can be unbearably slow when a player chooses
+ * to use a REALLY big screen in levels filled with shallow
+ * water. I believe this also hurts a lot on multiuser systems.
+ * However fast modern processors are, I/O cannot be made that
+ * fast, and that's why shimmering has been limited to small
+ * number of monsters -- pelpel
+ */
+ if (!avoid_other && !avoid_shimmer && !use_graphics &&
+ !resting && !running)
+ {
+ for (j = panel_row_min; j <= panel_row_max; j++)
+ {
+ for (i = panel_col_min; i <= panel_col_max; i++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+ feature_type *f_ptr;
+
+ /* Apply terrain feature mimics */
+ if (c_ptr->mimic)
+ {
+ f_ptr = &f_info[c_ptr->mimic];
+ }
+ else
+ {
+ f_ptr = &f_info[f_info[c_ptr->feat].mimic];
+ }
+
+ /* Skip normal features */
+ if (!(f_ptr->flags1 & (FF1_ATTR_MULTI))) continue;
+
+ /* Redraw a shimmering spot */
+ lite_spot(j, i);
+ }
+ }
+ }
+
+
+ /* Handle monster detection */
+ if (repair_monsters)
+ {
+ /* Reset the flag */
+ repair_monsters = FALSE;
+
+ /* Rotate detection flags */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Access monster */
+ m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Nice monsters get mean */
+ if (m_ptr->mflag & (MFLAG_NICE))
+ {
+ /* Nice monsters get mean */
+ m_ptr->mflag &= ~(MFLAG_NICE);
+ }
+
+ /* Handle memorized monsters */
+ if (m_ptr->mflag & (MFLAG_MARK))
+ {
+ /* Maintain detection */
+ if (m_ptr->mflag & (MFLAG_SHOW))
+ {
+ /* Forget flag */
+ m_ptr->mflag &= ~(MFLAG_SHOW);
+
+ /* Still need repairs */
+ repair_monsters = TRUE;
+ }
+
+ /* Remove detection */
+ else
+ {
+ /* Forget flag */
+ m_ptr->mflag &= ~(MFLAG_MARK);
+
+ /* Assume invisible */
+ m_ptr->ml = FALSE;
+
+ /* Update the monster */
+ update_mon(i, FALSE);
+
+ /* Redraw regardless */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+ }
+ }
+ }
+ }
+
+ /*
+ * Moved from dungeon() -- It'll get called whenever player
+ * spends energy, so that maze isn't incredibly easy for
+ * Sorcerors and alike any longer -- pelpel
+ *
+ * Forget everything when requested hehe I'm *NASTY*
+ */
+ if (dun_level && (dungeon_flags1 & DF1_FORGET))
+ {
+ wiz_dark();
+ }
+ }
+
+
+ /* Hack -- notice death */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+ }
+}
+
+
+
+/*
+ * Interact with the current dungeon level.
+ *
+ * This function will not exit until the level is completed,
+ * the user dies, or the game is terminated.
+ */
+static void dungeon(void)
+{
+ /* Reset various flags */
+ hack_mind = FALSE;
+
+ /* Not leaving */
+ p_ptr->leaving = FALSE;
+
+ /* Reset the "command" vars */
+ command_cmd = 0;
+ command_new = 0;
+ command_rep = 0;
+ command_arg = 0;
+ command_dir = 0;
+
+ /* Make sure partial summoning counter is initialized. */
+ p_ptr->maintain_sum = 0;
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Check visual effects */
+ shimmer_monsters = TRUE;
+ shimmer_objects = TRUE;
+ repair_monsters = TRUE;
+ repair_objects = TRUE;
+
+
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Track maximum player level */
+ if (p_ptr->max_plv < p_ptr->lev)
+ {
+ p_ptr->max_plv = p_ptr->lev;
+ }
+
+ /* Track maximum dungeon level (if not in quest -KMW-) */
+ if ((max_dlv[dungeon_type] < dun_level) && !p_ptr->inside_quest)
+ {
+ max_dlv[dungeon_type] = dun_level;
+ }
+
+ /* No stairs down from Quest */
+ if (is_quest(dun_level) && !p_ptr->astral)
+ {
+ create_down_stair = FALSE;
+ create_down_shaft = FALSE;
+ }
+
+ /* Paranoia -- no stairs from town or wilderness */
+ if (!dun_level) create_down_stair = create_up_stair = FALSE;
+ if (!dun_level) create_down_shaft = create_up_shaft = FALSE;
+
+ /* Option -- no connected stairs */
+ if (!dungeon_stair) create_down_stair = create_up_stair = FALSE;
+ if (!dungeon_stair) create_down_shaft = create_up_shaft = FALSE;
+
+ /* no connecting stairs on special levels */
+ if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_stair = create_up_stair = FALSE;
+ if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_shaft = create_up_shaft = FALSE;
+
+ /* Make a stairway. */
+ if ((create_up_stair || create_down_stair ||
+ create_up_shaft || create_down_shaft) &&
+ !get_fbranch())
+ {
+ /* Place a stairway */
+ if (cave_valid_bold(p_ptr->py, p_ptr->px))
+ {
+ /* XXX XXX XXX */
+ delete_object(p_ptr->py, p_ptr->px);
+
+ /* Make stairs */
+ if (create_down_stair)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE);
+ }
+ else if (create_down_shaft)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN);
+ }
+ else if (create_up_shaft)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP);
+ }
+ else
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS);
+ }
+ }
+
+ /* Cancel the stair request */
+ create_down_stair = create_up_stair = FALSE;
+ create_down_shaft = create_up_shaft = FALSE;
+ }
+
+ /* Hack - Assume invalid panel */
+ panel_row_min = cur_hgt;
+ panel_row_max = 0;
+ panel_col_min = cur_wid;
+ panel_col_max = 0;
+
+ /* Center the panel */
+ verify_panel();
+
+ /* Flush messages */
+ msg_print(NULL);
+
+
+ /* Enter "xtra" mode */
+ character_xtra = TRUE;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+
+ /* Redraw dungeon */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_SANITY | PU_BODY);
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Redraw stuff */
+ window_stuff();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Leave "xtra" mode */
+ character_xtra = FALSE;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_BODY);
+
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Window stuff */
+ window_stuff();
+
+ /* Refresh */
+ Term_fresh();
+
+
+ /* Announce (or repeat) the feeling */
+ if (dun_level) do_cmd_feeling();
+
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) return;
+
+ /*** Process this dungeon level ***/
+
+ /* Reset the monster generation level */
+ monster_level = dun_level;
+
+ /* Reset the object generation level */
+ object_level = dun_level;
+
+ hack_mind = TRUE;
+
+ /* Mega Hack, if needed wipe all stairs */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ int i, j;
+
+ for (i = 0; i < cur_wid; i++)
+ {
+ for (j = 0; j < cur_hgt; j++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+
+ switch (c_ptr->feat)
+ {
+ case FEAT_MORE:
+ case FEAT_LESS:
+ case FEAT_SHAFT_UP:
+ case FEAT_SHAFT_DOWN:
+ {
+ cave_set_feat(j, i, FEAT_FLOOR);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Reset the monster generation level */
+ monster_level = 127;
+
+ /* Reset the object generation level */
+ object_level = 0;
+ }
+
+ /* Main loop */
+ while (TRUE)
+ {
+ /* Hack -- Compact the monster list occasionally */
+ if (m_cnt + 32 > max_m_idx) compact_monsters(64);
+
+ /* Hack -- Compress the monster list occasionally */
+ if (m_cnt + 32 < m_max) compact_monsters(0);
+
+
+ /* Hack -- Compact the object list occasionally */
+ if (o_cnt + 32 > max_o_idx) compact_objects(64);
+
+ /* Hack -- Compress the object list occasionally */
+ if (o_cnt + 32 < o_max) compact_objects(0);
+
+
+
+ /* Process the player */
+ process_player();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+#ifdef pelpel
+
+ /* Process spell effects */
+ process_effects();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+#endif /* pelpel */
+
+
+ total_friends = 0;
+ total_friend_levels = 0;
+
+ /* Process all of the monsters */
+ process_monsters();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+ /* Process the world */
+ process_world();
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_END_TURN, "(d)", is_quest(dun_level));
+
+ /* Make it pulsate and live !!!! */
+ if ((dungeon_flags1 & DF1_EVOLVE) && dun_level)
+ {
+ if (!(turn % 10)) evolve_level(TRUE);
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Count game turns */
+ turn++;
+ }
+
+ /* Did we leave a dungeon ? */
+ if ((dun_level < d_info[dungeon_type].mindepth) && !is_recall)
+ {
+ dun_level = 0;
+
+ if (d_info[dungeon_type].ix > -1)
+ {
+ p_ptr->wilderness_x = d_info[dungeon_type].ix;
+ p_ptr->wilderness_y = d_info[dungeon_type].iy;
+ }
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ }
+
+ if (dun_level > d_info[dungeon_type].maxdepth)
+ {
+ dun_level = 0;
+
+ if (d_info[dungeon_type].ox > -1)
+ {
+ p_ptr->wilderness_x = d_info[dungeon_type].ox;
+ p_ptr->wilderness_y = d_info[dungeon_type].oy;
+ }
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ }
+
+ is_recall = FALSE;
+}
+
+
+
+
+/*
+ * Load some "user pref files"
+ */
+static void load_all_pref_files(void)
+{
+ char buf[1024];
+
+
+ /* Access the "race" pref file */
+ sprintf(buf, "%s.prf", rp_ptr->title + rp_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "class" pref file */
+ sprintf(buf, "%s.prf", spp_ptr->title + c_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "character" pref file */
+ sprintf(buf, "%s.prf", player_name);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Process player specific automatizer sets */
+ tome_dofile_anywhere(ANGBAND_DIR_USER, format("%s.atm", player_name), FALSE);
+}
+
+/*
+ * Actually play a game
+ *
+ * If the "new_game" parameter is true, then, after loading the
+ * savefile, we will commit suicide, if necessary, to allow the
+ * player to start a new game.
+ */
+void play_game(bool new_game)
+{
+ int i, tmp_dun;
+
+ bool cheat_death = FALSE;
+
+ hack_corruption = FALSE;
+
+ /* Hack -- Character is "icky" */
+ character_icky = TRUE;
+
+
+ /* Make sure main term is active */
+ Term_activate(angband_term[0]);
+
+ /* Initialise the resize hook XXX XXX XXX */
+ angband_term[0]->resize_hook = resize_map;
+
+ /* XXX XXX XXX hardcoded number of terms */
+ for (i = 1; i < 8; i++)
+ {
+ if (angband_term[i])
+ {
+ /* Add redraw hook */
+ angband_term[i]->resize_hook = resize_window;
+ }
+ }
+
+
+ /* Hack -- turn off the cursor */
+ (void)Term_set_cursor(0);
+
+ /* Character list */
+ if (!new_game && !no_begin_screen) new_game = begin_screen();
+ no_begin_screen = FALSE;
+
+ /* Attempt to load */
+ if (!load_player())
+ {
+ /* Oops */
+ quit("broken savefile");
+ }
+
+ /* Nothing loaded */
+ if (!character_loaded)
+ {
+ /* Make new player */
+ new_game = TRUE;
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+ }
+ else
+ {
+ int i;
+
+ /* Init new skills to their defaults */
+ for (i = old_max_s_idx; i < max_s_idx; i++)
+ {
+ s32b value = 0, mod = 0;
+
+ compute_skills(&value, &mod, i);
+
+ init_skill(value, mod, i);
+ }
+ }
+
+#if 1
+
+ /* Process old character */
+ if (!new_game)
+ {
+ /* Process the player name */
+ process_player_name(FALSE);
+ }
+
+#endif
+
+ /* Init the RNG */
+ if (Rand_quick)
+ {
+ u32b seed;
+
+ /* Basic seed */
+ seed = (time(NULL));
+
+#ifdef SET_UID
+
+ /* Mutate the seed on Unix machines */
+ seed = ((seed >> 3) * (getpid() << 1));
+
+#endif
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ /* Seed the "complex" RNG */
+ Rand_state_init(seed);
+ }
+
+ /* Extract the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Set the "default" options */
+ if (option_info[i].o_var)
+ {
+ /* Set */
+ if (option_flag[os] & (1L << ob))
+ {
+ /* Set */
+ (*option_info[i].o_var) = TRUE;
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ (*option_info[i].o_var) = FALSE;
+ }
+ }
+ }
+
+ /* Roll new character */
+ if (new_game)
+ {
+ s32b ret;
+
+ /* Are we authorized to create new chars? */
+ call_lua("get_module_info", "(s)", "d", "allow_birth", &ret);
+
+ if (!ret)
+ {
+ msg_box("Sorry, this module does not allow character creation.", -1, -1);
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+ }
+
+ process_hooks(HOOK_INIT, "()");
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+
+ /* Hack -- seed for flavors */
+ seed_flavor = rand_int(0x10000000);
+
+ /* Hack -- seed for town layout */
+ seed_town = rand_int(0x10000000);
+
+ /* Roll up a new character */
+ player_birth();
+
+ /* Start in town, or not */
+ if (p_ptr->astral) dun_level = 98;
+ else dun_level = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->inside_arena = 0;
+
+ /* Hack -- enter the world */
+ /* Mega-hack Vampires and Spectres start in the dungeon */
+ if (PRACE_FLAG(PR1_UNDEAD))
+ {
+ turn = (10L * DAY / 2) + (START_DAY * 10) + 1;
+ }
+ else
+ {
+ turn = (START_DAY * 10) + 1;
+ }
+ }
+
+ /* Flash a message */
+ prt("Please wait...", 0, 0);
+
+ /* Flush the message */
+ Term_fresh();
+
+ /* Be sure to not bother the player */
+ calc_powers_silent = TRUE;
+
+ /* Hack -- Enter wizard mode */
+ if (arg_wizard && enter_wizard_mode()) wizard = TRUE;
+
+ /* Flavor the objects */
+ flavor_init();
+
+ /* Reset the visual mappings */
+ reset_visuals();
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+
+ /* Window stuff */
+ window_stuff();
+
+ /* load user file */
+ process_pref_file("user.prf");
+
+ /* Load the "pref" files */
+ load_all_pref_files();
+
+ /* Set or clear "rogue_like_commands" if requested */
+ if (arg_force_original) rogue_like_commands = FALSE;
+ if (arg_force_roguelike) rogue_like_commands = TRUE;
+
+ /* Initialize vault info */
+ if (init_v_info()) quit("Cannot initialize vaults");
+
+ /* Initialize hooks */
+ init_hooks();
+ ingame_help(p_ptr->help.enabled);
+
+ /* React to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Mega hack, prevent lots of bugs */
+ if ((p_ptr->px == 0) || (p_ptr->py == 0))
+ {
+ p_ptr->px = 1;
+ p_ptr->py = 1;
+ };
+
+ /* Hack - if note file exists, load it */
+ if (!new_game && take_notes)
+ {
+ add_note_type(NOTE_ENTER_DUNGEON);
+ }
+
+ /* Generate a dungeon level if needed */
+ if (!character_dungeon) generate_cave();
+
+ /* Ok tell the scripts that the game is about to start */
+ process_hooks(HOOK_GAME_START, "()");
+
+ /* Character is now "complete" */
+ character_generated = TRUE;
+
+
+ /* Hack -- Character is no longer "icky" */
+ character_icky = FALSE;
+
+
+ /* Start game */
+ alive = TRUE;
+
+ /* Hack -- Enforce "delayed death" */
+ if (p_ptr->chp < 0) death = TRUE;
+
+ /* Should we use old colors */
+ if (autoload_old_colors)
+ {
+ process_pref_file("422color.prf");
+ }
+
+
+ /* Process */
+ while (TRUE)
+ {
+ /* Save the level */
+ old_dun_level = dun_level;
+ p_ptr->old_wild_mode = p_ptr->wild_mode;
+
+ /* We reached surface ? good, lets go down again !! */
+ if (p_ptr->astral && !dun_level)
+ {
+ p_ptr->astral = FALSE;
+ cmsg_print(TERM_L_GREEN,
+ "Well done ! You reached the town ! "
+ "You can now go down again.");
+ }
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+#ifdef SAVE_HACK
+
+ /* Process the level */
+ if (!save_hack)
+ {
+ dungeon();
+ }
+ else
+ {
+ generate_cave();
+ }
+
+ save_hack = FALSE;
+
+ p_ptr->leaving = TRUE;
+
+#else
+
+ /* Process the level */
+ dungeon();
+
+#endif
+
+ /* Save the current level if in a persistent level */
+ tmp_dun = dun_level;
+ dun_level = old_dun_level;
+ save_dungeon();
+ dun_level = tmp_dun;
+
+ /* A death fate affects level generation */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ /* Ignore empty slots */
+ if (!fates[i].fate) continue;
+
+ /* Ignore non-applicable fates */
+ if (fates[i].level != dun_level) continue;
+
+ /* Non-serious fate fails to fire 50% of time */
+ if (!fates[i].serious && (rand_int(2) == 0)) continue;
+
+ /* Analyse fate */
+ switch (fates[i].fate)
+ {
+ /* You are doomed */
+ case FATE_DIE:
+ {
+ cmsg_print(TERM_L_DARK, "You were fated to die here. DIE!");
+
+ /* You shall perish there */
+ dungeon_type = DUNGEON_DEATH;
+ dun_level = d_info[dungeon_type].mindepth; /* was 1 */
+
+ fates[i].fate = FATE_NONE;
+ break;
+ }
+ }
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Forget the lite */
+ forget_mon_lite();
+
+ /* Forget the view */
+ forget_view();
+
+ /* Handle "quit and save" */
+ if (!alive && !death) break;
+
+
+ /* Erase the old cave */
+ wipe_o_list();
+
+
+ /* XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Accidental Death */
+ if (alive && death)
+ {
+ cheat_death = FALSE;
+
+ /* Can we die ? please let us die ! */
+ if (process_hooks(HOOK_DIE, "()"))
+ {
+ cheat_death = TRUE;
+ }
+
+ /* Deus ex machina */
+ else if (granted_resurrection())
+ {
+ cheat_death = TRUE;
+ p_ptr->grace = -200000;
+ cmsg_format(TERM_L_GREEN,
+ "The power of %s raises you back from the grave!",
+ deity_info[p_ptr->pgod].name);
+ msg_print(NULL);
+ }
+
+ /* Blood of life */
+ else if (p_ptr->allow_one_death > 0)
+ {
+ cheat_death = TRUE;
+
+ /* Lose one extra life */
+ p_ptr->allow_one_death--;
+
+ cmsg_print(TERM_L_GREEN,
+ "You have been saved by the Blood of Life!");
+ msg_print(NULL);
+ }
+
+ /* Cheat death option */
+ else if ((wizard || cheat_live) && !get_check("Die? "))
+ {
+ cheat_death = TRUE;
+
+ /* Mark social class, reset age, if needed */
+ if (p_ptr->sc) p_ptr->sc = p_ptr->age = 0;
+
+ /* Increase age */
+ p_ptr->age++;
+
+ /* Mark savefile */
+ noscore |= 0x0001;
+ msg_print("You invoke wizard mode and cheat death.");
+ msg_print(NULL);
+ }
+
+ if (cheat_death)
+ {
+ /* Restore the winner status */
+ total_winner = has_won;
+
+ /* One more life spent */
+ p_ptr->lives++;
+
+ /* Restore hit points */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Heal sanity */
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+
+ /* Restore spell points */
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+
+ /* Hack -- Healing */
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_poisoned(0);
+ (void)set_afraid(0);
+ (void)set_paralyzed(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+
+ /* accounting for a new ailment. -LM- */
+ p_ptr->black_breath = FALSE;
+
+ /* Hack -- don't go to undead form */
+ p_ptr->necro_extra &= ~CLASS_UNDEAD;
+
+ /* Hack -- Prevent starvation */
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Hack -- cancel recall */
+ if (p_ptr->word_recall)
+ {
+ /* Message */
+ msg_print("A tension leaves the air around you...");
+ msg_print(NULL);
+
+ /* Hack -- Prevent recall */
+ p_ptr->word_recall = 0;
+ }
+
+ /* Note cause of death XXX XXX XXX */
+ (void)strcpy(died_from, "Cheating death");
+
+ /* Do not die */
+ death = FALSE;
+
+ /* New depth -KMW- */
+ /* dun_level = 0; */
+ p_ptr->inside_arena = 0;
+ leaving_quest = 0;
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ }
+
+ /* Handle "death" */
+ if (death)
+ {
+ break;
+ }
+
+ /* Mega hack */
+ if (dun_level) p_ptr->wild_mode = FALSE;
+
+ /* Make a new level */
+ process_hooks(HOOK_NEW_LEVEL, "(d)", is_quest(dun_level));
+ generate_cave();
+ }
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+}
+
diff --git a/src/dungeon.pkg b/src/dungeon.pkg
new file mode 100644
index 00000000..3e11338d
--- /dev/null
+++ b/src/dungeon.pkg
@@ -0,0 +1,1618 @@
+/* File: dungeon.pkg */
+
+/*
+ * Purpose: Lua interface defitions for dungeon routines.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @name Cave Grid
+ * @note Special cave grid flags
+ * @{
+ */
+
+/** @def CAVE_MARK
+ * @note memorized feature
+ */
+#define CAVE_MARK 0x0001
+
+/** @def CAVE_GLOW
+ * @note self-illuminating
+ */
+#define CAVE_GLOW 0x0002
+
+/** @def CAVE_ICKY
+ * @note part of a vault
+ */
+#define CAVE_ICKY 0x0004
+
+/** @def CAVE_ROOM
+ * @note part of a room
+ */
+#define CAVE_ROOM 0x0008
+
+/** @def CAVE_SEEN
+ * @note seen flag
+ */
+#define CAVE_SEEN 0x0010
+
+/** @def CAVE_VIEW
+ * @note view flag
+ */
+#define CAVE_VIEW 0x0020
+
+/** @def CAVE_TEMP
+ * @note temp flag
+ */
+#define CAVE_TEMP 0x0040
+
+/** @def CAVE_WALL
+ * @note wall flag
+ */
+#define CAVE_WALL 0x0080
+
+/** @def CAVE_TRDT
+ * @note trap detected
+ */
+#define CAVE_TRDT 0x0100
+
+/** @def CAVE_IDNT
+ * @note grid identified (fountains)
+ */
+#define CAVE_IDNT 0x0200
+
+/** @def CAVE_SPEC
+ * @note special mark(quests)
+ */
+#define CAVE_SPEC 0x0400
+
+/** @def CAVE_FREE
+ * @note no random generation on it
+ */
+#define CAVE_FREE 0x0800
+
+/** @def CAVE_DETECT
+ * @note Traps detected here
+ */
+#define CAVE_DETECT 0x1000
+
+/** @def CAVE_PLIT
+ * @note Player lit grid
+ */
+#define CAVE_PLIT 0x2000
+
+/** @def CAVE_MLIT
+ * @note Monster lit grid
+ */
+#define CAVE_MLIT 0x4000
+/** @} */
+
+/** @name Terrain Feature Indexes
+ * @note (see "lib/edit/f_info.txt")
+ * @{
+ */
+
+/* Nothing */
+/** @def FEAT_NONE */
+#define FEAT_NONE 0x00
+
+
+/* Basic features */
+/** @def FEAT_FLOOR */
+#define FEAT_FLOOR 0x01
+
+/** @def FEAT_FOUNTAIN */
+#define FEAT_FOUNTAIN 0x02
+
+/** @def FEAT_GLYPH */
+#define FEAT_GLYPH 0x03
+
+/** @def FEAT_OPEN */
+#define FEAT_OPEN 0x04
+
+/** @def FEAT_BROKEN */
+#define FEAT_BROKEN 0x05
+
+/** @def FEAT_LESS */
+#define FEAT_LESS 0x06
+
+/** @def FEAT_MORE */
+#define FEAT_MORE 0x07
+
+
+/* Quest features -KMW- */
+/** @def FEAT_QUEST_ENTER */
+#define FEAT_QUEST_ENTER 0x08
+
+/** @def FEAT_QUEST_EXIT */
+#define FEAT_QUEST_EXIT 0x09
+
+/** @def FEAT_QUEST_DOWN */
+#define FEAT_QUEST_DOWN 0x0A
+
+/** @def FEAT_QUEST_UP */
+#define FEAT_QUEST_UP 0x0B
+
+
+/* Shafts -GSN- */
+/** @def FEAT_SHAFT_DOWN */
+#define FEAT_SHAFT_DOWN 0x0D
+
+/** @def FEAT_SHAFT_UP */
+#define FEAT_SHAFT_UP 0x0E
+
+
+/* Basic feature */
+/** @def FEAT_EMPTY_FOUNTAIN */
+#define FEAT_EMPTY_FOUNTAIN 0x0F
+
+
+/* Feature 0x10 -- web */
+
+/* Traps */
+/** @def FEAT_TRAP */
+#define FEAT_TRAP 0x11
+
+
+/* Features 0x12 - 0x1F -- unused */
+
+/* Doors */
+/** @def FEAT_DOOR_HEAD */
+#define FEAT_DOOR_HEAD 0x20
+
+/** @def FEAT_DOOR_TAIL */
+#define FEAT_DOOR_TAIL 0x2F
+
+
+/* Extra */
+/** @def FEAT_SECRET */
+#define FEAT_SECRET 0x30
+
+/** @def FEAT_RUBBLE */
+#define FEAT_RUBBLE 0x31
+
+
+/* Seams */
+/** @def FEAT_MAGMA */
+#define FEAT_MAGMA 0x32
+
+/** @def FEAT_QUARTZ */
+#define FEAT_QUARTZ 0x33
+
+/** @def FEAT_MAGMA_H */
+#define FEAT_MAGMA_H 0x34
+
+/** @def FEAT_QUARTZ_H */
+#define FEAT_QUARTZ_H 0x35
+
+/** @def FEAT_MAGMA_K */
+#define FEAT_MAGMA_K 0x36
+
+/** @def FEAT_QUARTZ_K */
+#define FEAT_QUARTZ_K 0x37
+
+
+/* Walls */
+/** @def FEAT_WALL_EXTRA */
+#define FEAT_WALL_EXTRA 0x38
+
+/** @def FEAT_WALL_INNER */
+#define FEAT_WALL_INNER 0x39
+
+/** @def FEAT_WALL_OUTER */
+#define FEAT_WALL_OUTER 0x3A
+
+/** @def FEAT_WALL_SOLID */
+#define FEAT_WALL_SOLID 0x3B
+
+/** @def FEAT_PERM_EXTRA */
+#define FEAT_PERM_EXTRA 0x3C
+
+/** @def FEAT_PERM_INNER */
+#define FEAT_PERM_INNER 0x3D
+
+/** @def FEAT_PERM_OUTER */
+#define FEAT_PERM_OUTER 0x3E
+
+/** @def FEAT_PERM_SOLID */
+#define FEAT_PERM_SOLID 0x3F
+
+
+/* Explosive rune */
+/** @def FEAT_MINOR_GLYPH */
+#define FEAT_MINOR_GLYPH 0x40
+
+
+/* Pattern */
+/** @def FEAT_PATTERN_START */
+#define FEAT_PATTERN_START 0x41
+
+/** @def FEAT_PATTERN_1 */
+#define FEAT_PATTERN_1 0x42
+
+/** @def FEAT_PATTERN_2 */
+#define FEAT_PATTERN_2 0x43
+
+/** @def FEAT_PATTERN_3 */
+#define FEAT_PATTERN_3 0x44
+
+/** @def FEAT_PATTERN_4 */
+#define FEAT_PATTERN_4 0x45
+
+/** @def FEAT_PATTERN_END */
+#define FEAT_PATTERN_END 0x46
+
+/** @def FEAT_PATTERN_OLD */
+#define FEAT_PATTERN_OLD 0x47
+
+/** @def FEAT_PATTERN_XTRA1 */
+#define FEAT_PATTERN_XTRA1 0x48
+
+/** @def FEAT_PATTERN_XTRA2 */
+#define FEAT_PATTERN_XTRA2 0x49
+
+
+/* Shops */
+/** @def FEAT_SHOP */
+#define FEAT_SHOP 0x4A
+
+
+/* Permanent walls for quests */
+/** @def FEAT_QUEST1 */
+#define FEAT_QUEST1 0x4B
+
+/** @def FEAT_QUEST2 */
+#define FEAT_QUEST2 0x4C
+
+/** @def FEAT_QUEST3 */
+#define FEAT_QUEST3 0x4D
+
+/** @def FEAT_QUEST4 */
+#define FEAT_QUEST4 0x4E
+
+
+/* Features 0x4F - 0x53 -- unused */
+
+/* Additional terrains */
+/** @def FEAT_SHAL_WATER */
+#define FEAT_SHAL_WATER 0x54
+
+/** @def FEAT_DEEP_LAVA */
+#define FEAT_DEEP_LAVA 0x55
+
+/** @def FEAT_SHAL_LAVA */
+#define FEAT_SHAL_LAVA 0x56
+
+/** @def FEAT_DARK_PIT */
+#define FEAT_DARK_PIT 0x57
+
+/** @def FEAT_DIRT */
+#define FEAT_DIRT 0x58
+
+/** @def FEAT_GRASS */
+#define FEAT_GRASS 0x59
+
+/** @def FEAT_ICE */
+#define FEAT_ICE 0x5A
+
+/** @def FEAT_SAND */
+#define FEAT_SAND 0x5B
+
+/** @def FEAT_DEAD_TREE */
+#define FEAT_DEAD_TREE 0x5C
+
+/** @def FEAT_ASH */
+#define FEAT_ASH 0x5D
+
+/** @def FEAT_MUD */
+#define FEAT_MUD 0x5E
+
+/** @def FEAT_ICE_WALL */
+#define FEAT_ICE_WALL 0x5F
+
+/** @def FEAT_TREES */
+#define FEAT_TREES 0x60
+
+/** @def FEAT_MOUNTAIN */
+#define FEAT_MOUNTAIN 0x61
+
+/** @def FEAT_SANDWALL */
+#define FEAT_SANDWALL 0x62
+
+/** @def FEAT_SANDWALL_H */
+#define FEAT_SANDWALL_H 0x63
+
+/** @def FEAT_SANDWALL_K */
+#define FEAT_SANDWALL_K 0x64
+
+/* Feature 0x65 -- high mountain chain */
+/* Feature 0x66 -- nether mist */
+
+/* Features 0x67 - 0x9F -- unused */
+
+/** @def FEAT_BETWEEN
+ * @note 160
+ */
+#define FEAT_BETWEEN 0xA0
+
+/* Altars */
+/** @def FEAT_ALTAR_HEAD
+ * @note 161
+ */
+#define FEAT_ALTAR_HEAD 0xA1
+
+/** @def FEAT_ALTAR_TAIL
+ * @note 171
+ */
+#define FEAT_ALTAR_TAIL 0xAB
+
+/** @def FEAT_MARKER
+ * @note 172
+ */
+#define FEAT_MARKER 0xAC
+
+/* Feature 0xAD -- Underground Tunnel */
+/** @def FEAT_TAINTED_WATER
+ * @note 174
+ */
+#define FEAT_TAINTED_WATER 0xAE
+
+/** @def FEAT_MON_TRAP
+ * @note 175
+ */
+#define FEAT_MON_TRAP 0xAF
+
+/** @def FEAT_BETWEEN2
+ * @note 176
+ */
+#define FEAT_BETWEEN2 0xB0
+
+/** @def FEAT_LAVA_WALL
+ * @note 177
+ */
+#define FEAT_LAVA_WALL 0xB1
+
+/** @def FEAT_GREAT_FIRE
+ * @note 178
+ */
+#define FEAT_GREAT_FIRE 0xB2
+
+/** @def FEAT_WAY_MORE
+ * @note 179
+ */
+#define FEAT_WAY_MORE 0xB3
+
+/** @def FEAT_WAY_LESS
+ * @note 180
+ */
+#define FEAT_WAY_LESS 0xB4
+
+/* Feature 0xB5 -- field */
+
+/** @def FEAT_EKKAIA
+ * @note 182
+ */
+#define FEAT_EKKAIA 0xB6
+
+/* Features 0xB7 - 0xBA -- unused */
+
+/** @def FEAT_DEEP_WATER
+ * @note 187
+ */
+#define FEAT_DEEP_WATER 0xBB
+
+/** @def FEAT_GLASS_WALL
+ * @note 188
+ */
+#define FEAT_GLASS_WALL 0xBC
+
+/** @def FEAT_ILLUS_WALL
+ * @note 189
+ */
+#define FEAT_ILLUS_WALL 0xBD
+
+/* Feature 0xBE -- grass roof */
+/* Feature 0xBF -- grass roof top */
+/* Feature 0xC0 -- grass roof chimney */
+/* Feature 0xC1 -- brick roof */
+/* Feature 0xC2 -- brick roof top */
+/* Feature 0xC3 -- brick roof chimney */
+/* Feature 0xC4 -- window */
+/* Feature 0xC5 -- small window */
+/* Feature 0xC6 -- rain barrel */
+
+/** @def FEAT_FLOWER
+ * @note 199
+ */
+#define FEAT_FLOWER 0xC7
+
+/* Feature 0xC8 -- cobblestone road */
+/* Feature 0xC9 -- cobblestone with outlet */
+
+/** @def FEAT_SMALL_TREES
+ * @note 202
+ */
+#define FEAT_SMALL_TREES 0xCA
+
+/** @def FEAT_TOWN
+ * @note 203
+ */
+#define FEAT_TOWN 0xCB
+
+/* Feature 0xCC -- Underground Tunnel */
+
+/** @def FEAT_FIRE
+ * @note 205
+ */
+#define FEAT_FIRE 0xCD
+
+/* Feature 0xCE -- pile of rubble (permanent) */
+/* Features 0xCF - 0xFF -- unused */
+/** @} */
+
+/** @name Dungeon Type Flags (part 1)
+ * @{ */
+
+/** @def DF1_PRINCIPAL
+ * @note Is a principal dungeon
+ */
+#define DF1_PRINCIPAL 0x00000001L
+/** @def DF1_MAZE
+ * @note Is a maze-type dungeon
+ */
+#define DF1_MAZE 0x00000002L
+/** @def DF1_SMALLEST
+ * @note Creates VERY small levels like The Maze
+ */
+#define DF1_SMALLEST 0x00000004L
+/** @def DF1_SMALL
+ * @note Creates small levels like Dol Goldor
+ */
+#define DF1_SMALL 0x00000008L
+/** @def DF1_BIG
+ * @note Creates big levels like Moria, and Angband dungeons
+ */
+#define DF1_BIG 0x00000010L
+/** @def DF1_NO_DOORS
+ * @note No doors on rooms, like Barrowdowns, Old Forest etc)
+ */
+#define DF1_NO_DOORS 0x00000020L
+/** @def DF1_WATER_RIVER
+ * @note Allow a single water streamer on a level
+ */
+#define DF1_WATER_RIVER 0x00000040L
+/** @def DF1_LAVA_RIVER
+ * @note Allow a single lava streamer on a level
+ */
+#define DF1_LAVA_RIVER 0x00000080L
+/** @def DF1_WATER_RIVERS
+ * @note Allow multiple water streamers on a level
+ */
+#define DF1_WATER_RIVERS 0x00000100L
+/** @def DF1_LAVA_RIVERS
+ * @note Allow multiple lava streamers on a level
+ */
+#define DF1_LAVA_RIVERS 0x00000200L
+/** @def DF1_CAVE
+ * @note Allow orc-cave like 'fractal' rooms
+ */
+#define DF1_CAVE 0x00000400L
+/** @def DF1_CAVERN
+ * @note Allow cavern rooms
+ */
+#define DF1_CAVERN 0x00000800L
+/** @def DF1_NO_UP
+ * @note Disallow up stairs
+ */
+#define DF1_NO_UP 0x00001000L
+/** @def DF1_HOT
+ * @note Corpses on ground and in pack decay quicker through heat
+ */
+#define DF1_HOT 0x00002000L
+/** @def DF1_COLD
+ * @note Corpses on ground and in pack decay quicker through cold
+ */
+#define DF1_COLD 0x00004000L
+/** @def DF1_FORCE_DOWN
+ * @note No up stairs generated
+ */
+#define DF1_FORCE_DOWN 0x00008000L
+/** @def DF1_FORGET
+ * @note Features are forgotten, like the Maze and Illusory Castle
+ */
+#define DF1_FORGET 0x00010000L
+/** @def DF1_NO_DESTROY
+ * @note No destroyed levels in dungeon
+ */
+#define DF1_NO_DESTROY 0x00020000L
+/** @def DF1_SAND_VEIN
+ * @note Like in the sandworm lair
+ */
+#define DF1_SAND_VEIN 0x00040000L
+/** @def DF1_CIRCULAR_ROOMS
+ * @note Allow circular rooms
+ */
+#define DF1_CIRCULAR_ROOMS 0x00080000L
+/** @def DF1_EMPTY
+ * @note Allow arena levels
+ */
+#define DF1_EMPTY 0x00100000L
+/** @def DF1_DAMAGE_FEAT
+ * @note Effect specified in will affect all grids incl. terrain and monsters
+ */
+#define DF1_DAMAGE_FEAT 0x00200000L
+/** @def DF1_FLAT
+ * @note Creates paths to next areas at edge of level, like Barrowdowns
+ */
+#define DF1_FLAT 0x00400000L
+/** @def DF1_TOWER
+ * @note You start at bottom and go up rather than the reverse
+ */
+#define DF1_TOWER 0x00800000L
+/** @def DF1_RANDOM_TOWNS
+ * @note Allow random towns
+ */
+#define DF1_RANDOM_TOWNS 0x01000000L
+/** @def DF1_DOUBLE
+ * @note Generates everything at double size like Helcaraxe and Erebor
+ */
+#define DF1_DOUBLE 0x02000000L
+/** @def DF1_LIFE_LEVEL
+ * @note Creates dungeon level on modified 'game of life' algorithm
+ */
+#define DF1_LIFE_LEVEL 0x04000000L
+/** @def DF1_EVOLVE
+ * @note Evolving, pulsing levels like Heart of the Earth
+ */
+#define DF1_EVOLVE 0x08000000L
+/** @def DF1_ADJUST_LEVEL_1
+ * @note Minimum monster level will be equal to dungeon level
+ */
+#define DF1_ADJUST_LEVEL_1 0x10000000L
+/** @def DF1_ADJUST_LEVEL_2
+ * @note Minimum monster level will be double the dungeon level
+ */
+#define DF1_ADJUST_LEVEL_2 0x20000000L
+/** @def DF1_NO_RECALL
+ * @note No recall allowed
+ */
+#define DF1_NO_RECALL 0x40000000L
+/** @def DF1_NO_STREAMERS
+ * @note No streamers
+ */
+#define DF1_NO_STREAMERS 0x80000000L
+/** @} */
+
+/** @name Dungeon Type Flags (part 2)
+ * @{ */
+
+/** @def DF2_ADJUST_LEVEL_1_2
+ * @note Minimum monster level will be half the dungeon level
+ */
+#define DF2_ADJUST_LEVEL_1_2 0x00000001L
+
+/** @def DF2_NO_SHAFT
+ * @note No shafts
+ */
+#define DF2_NO_SHAFT 0x00000002L
+
+/** @def DF2_ADJUST_LEVEL_PLAYER
+ * @note Uses player level*2 instead of dungeon level for other ADJUST_LEVEL flags
+ */
+#define DF2_ADJUST_LEVEL_PLAYER 0x00000004L
+
+/** @def DF2_NO_TELEPORT */
+#define DF2_NO_TELEPORT 0x00000008L
+
+/** @def DF2_ASK_LEAVE */
+#define DF2_ASK_LEAVE 0x00000010L
+
+/** @def DF2_NO_STAIR */
+#define DF2_NO_STAIR 0x00000020L
+
+/** @def DF2_SPECIAL */
+#define DF2_SPECIAL 0x00000040L
+
+/** @def DF2_NO_NEW_MONSTER */
+#define DF2_NO_NEW_MONSTER 0x00000080L
+
+/** @def DF2_DESC */
+#define DF2_DESC 0x00000100L
+
+/** @def DF2_NO_GENO */
+#define DF2_NO_GENO 0x00000200L
+
+/** @def DF2_NO_BREATH
+ * @note Oups, cannot breath here
+ */
+#define DF2_NO_BREATH 0x00000400L
+
+/** @def DF2_WATER_BREATH
+ * @note Oups, cannot breath here, need water breathing
+ */
+#define DF2_WATER_BREATH 0x00000800L
+
+/** @def DF2_ELVEN
+ * @note Try to create elven monster ego
+ */
+#define DF2_ELVEN 0x00001000L
+
+/** @def DF2_DWARVEN
+ * @note Try to create dwarven monster ego
+ */
+#define DF2_DWARVEN 0x00002000L
+
+/** @def DF2_NO_EASY_MOVE
+ * @note Forbid stuff like teleport level, probability travel, ...
+ */
+#define DF2_NO_EASY_MOVE 0x00004000L
+
+/** @def DF2_NO_RECALL_OUT
+ * @note Cannot recall out of the place
+ */
+#define DF2_NO_RECALL_OUT 0x00008000L
+
+/** @def DF2_DESC_ALWAYS
+ * @note Always shows the desc
+ */
+#define DF2_DESC_ALWAYS 0x00010000L
+/** @} */
+
+/** @var level_flags1;
+ * @brief Number
+ */
+extern u32b dungeon_flags1@level_flags1;
+
+/** @var level_flags2;
+ * @brief Number
+ */
+extern u32b dungeon_flags2@level_flags2;
+
+/** @def MAX_HGT
+ * @note Maximum dungeon height in grids, must be a multiple of SCREEN_HGT,
+ * probably hard-coded to SCREEN_HGT * 3.
+ */
+#define MAX_HGT 66
+
+
+/** @def MAX_WID
+ * @note Maximum dungeon width in grids, must be a multiple of SCREEN_WID,
+ * probably hard-coded to SCREEN_WID * 3.
+ */
+#define MAX_WID 198
+
+
+/** @name Town Defines
+ * @{ */
+
+/** @def TOWN_RANDOM
+ * @note First random town
+ */
+#define TOWN_RANDOM 20
+
+/** @def TOWN_DUNGEON
+ * @note Maximun number of towns per dungeon
+ */
+#define TOWN_DUNGEON 4
+
+/** @def TOWN_CHANCE
+ * @note Chance of 1 town
+ */
+#define TOWN_CHANCE 50
+
+/** @} */
+
+/** @name Wilderness Terrains
+ * @{
+ */
+
+/** @def TERRAIN_EDGE
+ * @note Edge of the World
+ */
+#define TERRAIN_EDGE 0
+
+/** @def TERRAIN_TOWN
+ * @note Town
+ */
+#define TERRAIN_TOWN 1
+
+/** @def TERRAIN_DEEP_WATER
+ * @note Deep water
+ */
+#define TERRAIN_DEEP_WATER 2
+
+/** @def TERRAIN_SHALLOW_WATER
+ * @note Shallow water
+ */
+#define TERRAIN_SHALLOW_WATER 3
+
+/** @def TERRAIN_SWAMP
+ * @note Swamp
+ */
+#define TERRAIN_SWAMP 4
+
+/** @def TERRAIN_DIRT
+ * @note Dirt
+ */
+#define TERRAIN_DIRT 5
+
+/** @def TERRAIN_GRASS
+ * @note Grass
+ */
+#define TERRAIN_GRASS 6
+
+/** @def TERRAIN_TREES
+ * @note Trees
+ */
+#define TERRAIN_TREES 7
+
+/** @def TERRAIN_DESERT
+ * @note Desert
+ */
+#define TERRAIN_DESERT 8
+
+/** @def TERRAIN_SHALLOW_LAVA
+ * @note Shallow lava
+ */
+#define TERRAIN_SHALLOW_LAVA 9
+
+/** @def TERRAIN_DEEP_LAVA
+ * @note Deep lava
+ */
+#define TERRAIN_DEEP_LAVA 10
+
+/** @def TERRAIN_MOUNTAIN
+ * @note Mountain
+ */
+#define TERRAIN_MOUNTAIN 11
+
+/** @def MAX_WILD_TERRAIN */
+#define MAX_WILD_TERRAIN 18
+/** @} */
+
+/** @struct border_type
+ * @note Border
+ */
+struct border_type
+{
+ /** @structvar north[MAX_WID]
+ * @brief Number
+ */
+ byte north[MAX_WID];
+
+ /** @structvar south[MAX_WID]
+ * @brief Number
+ */
+ byte south[MAX_WID];
+
+ /** @structvar east[MAX_HGT]
+ * @brief Number
+ */
+ byte east[MAX_HGT];
+
+ /** @structvar west[MAX_HGT]
+ * @brief Number
+ */
+ byte west[MAX_HGT];
+
+ /** @structvar north_west
+ * @brief Number
+ */
+ byte north_west;
+
+ /** @structvar north_east
+ * @brief Number
+ */
+ byte north_east;
+
+ /** @structvar south_west
+ * @brief Number
+ */
+ byte south_west;
+
+ /** @structvar south_east
+ * @brief Number
+ */
+ byte south_east;
+};
+
+
+/** @struct wilderness_type_info
+ * @note A structure describing a wilderness area
+ * with a terrain, a town or a dungeon entrance
+ */
+struct wilderness_type_info
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name (offset)
+ */
+ u32b name;
+
+ /** @structvar text
+ * @brief Number
+ * @note Text (offset)
+ */
+ u32b text;
+
+ /** @structvar entrance
+ * @brief Number
+ * @note Which town is there(<1000 i's a town, >=1000 it a dungeon)
+ */
+ u16b entrance;
+
+ /** @structvar road
+ * @brief Number
+ * @note Flags of road
+ */
+ byte road;
+
+ /** @structvar level
+ * @brief Number
+ * @note Difficulty level
+ */
+ int level;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note Some flags
+ */
+ u32b flags1;
+
+ /** @structvar feat
+ * @brief Number
+ * @note The feature of f_info.txt that is used to allow passing, ... and to get a char/color/graph
+ */
+ byte feat;
+
+ /** @structvar terrain_idx
+ * @brief Number
+ * @note Terrain index(defined in defines.h)
+ */
+ byte terrain_idx;
+
+ /** @structvar terrain[MAX_WILD_TERRAIN]
+ * @brief Number
+ * @note Feature types for the plasma generator
+ */
+ byte terrain[MAX_WILD_TERRAIN];
+};
+
+/** @struct wilderness_map
+ * @note A structure describing a wilderness map
+ */
+struct wilderness_map
+{
+ /** @structvar feat
+ * @brief Number
+ * @note Wilderness feature
+ */
+ int feat;
+
+ /** @structvar seed
+ * @brief Number
+ * @note Seed for the RNG
+ */
+ u32b seed;
+
+ /** @structvar entrance
+ * @brief Number
+ * @note Entrance for dungeons
+ */
+ u16b entrance;
+
+ /** @structvar known
+ * @brief Boolean
+ * @note Is it seen by the player ?
+ */
+ bool known;
+};
+
+
+/** @struct town_type
+ * @note A structure describing a town with
+ * stores and buildings
+ */
+struct town_type
+{
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+
+ /** @structvar seed
+ * @brief Number
+ * @note Seed for RNG
+ */
+ u32b seed;
+
+ /** @structvar *store
+ * @brief store_type
+ * @note The stores [max_st_idx]
+ */
+ store_type store[max_st_idx];
+
+ /** @structvar numstores
+ * @brief Number
+ */
+ byte numstores;
+
+ /** @structvar flags
+ * @brief Number
+ * @note Town flags
+ */
+ byte flags;
+
+ /** @structvar stocked
+ * @brief Boolean
+ * @note Is the town actually stocked ?
+ * Left this for the sake of compatibility
+ */
+ bool stocked;
+
+ /** @structvar destroyed
+ * @brief Boolean
+ * @note Is the town destroyed?
+ */
+ bool destroyed;
+};
+
+/** @var max_towns
+ * @brief Number
+ */
+extern u16b max_towns;
+
+/** @var town_info[max_towns]
+ * @brief town_type
+ */
+extern town_type town_info[max_towns];
+
+/** @struct rule_type
+ * Define monster generation rules
+ */
+struct rule_type
+{
+ /** @structvar mode
+ * @brief Number
+ * @note Mode of combination of the monster flags
+ */
+ byte mode;
+
+ /** @structvar percent
+ * @brief Number
+ * @note Percent of monsters affected by the rule
+ */
+ byte percent;
+
+ /** @structvar mflags1
+ * @brief Number
+ * @note The monster flags that are allowed
+ */
+ u32b mflags1;
+
+ /** @structvar mflags2
+ * @brief Number
+ */
+ u32b mflags2;
+
+ /** @structvar mflags3
+ * @brief Number
+ */
+ u32b mflags3;
+
+ /** @structvar mflags4
+ * @brief Number
+ */
+ u32b mflags4;
+
+ /** @structvar mflags5
+ * @brief Number
+ */
+ u32b mflags5;
+
+ /** @structvar mflags6
+ * @brief Number
+ */
+ u32b mflags6;
+
+ /** @structvar mflags7
+ * @brief Number
+ */
+ u32b mflags7;
+
+ /** @structvar mflags8
+ * @brief Number
+ */
+ u32b mflags8;
+
+ /** @structvar mflags9
+ * @brief Number
+ */
+ u32b mflags9;
+
+ /** @structvar r_char[5]
+ * @brief String
+ * @note Monster race allowed
+ */
+ char r_char[5];
+};
+
+/** @struct obj_theme
+ * @brief "Themed" objects.
+ * @note Probability in percent for each class of objects to be dropped.
+ * This could perhaps be an array - but that wouldn't be as clear.
+ */
+struct obj_theme
+{
+ /** @structvar treasure
+ * @brief Number
+ */
+ byte treasure;
+
+ /** @structvar combat
+ * @brief Number
+ */
+ byte combat;
+
+ /** @structvar magic
+ * @brief Number
+ */
+ byte magic;
+
+ /** @structvar tools
+ * @brief Number
+ */
+ byte tools;
+};
+
+/** @struct dungeon_info_type
+ * A structure for the != dungeon types
+ */
+struct dungeon_info_type
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name
+ */
+ u32b name;
+
+ /** @structvar text
+ * @brief Number
+ * @note Description
+ */
+ u32b text;
+
+ /** @structvar short_name[3]
+ * @brief String
+ * @note Short name
+ */
+ char short_name[3];
+
+ /** @structvar floor1
+ * @brief Number
+ * @note Floor tile 1
+ */
+ s16b floor1;
+
+ /** @structvar floor_percent1[2]
+ * @brief Number
+ * @note Chance of type 1
+ */
+ byte floor_percent1[2];
+
+ /** @structvar floor2
+ * @brief Number
+ * @note Floor tile 2
+ */
+ s16b floor2;
+
+ /** @structvar floor_percent2[2]
+ * @brief Number
+ * @note Chance of type 2
+ */
+ byte floor_percent2[2];
+
+ /** @structvar floor3
+ * @brief Number
+ * @note Floor tile 3
+ */
+ s16b floor3;
+
+ /** @structvar floor_percent3[2]
+ * @brief Number
+ * @note Chance of type 3
+ */
+ byte floor_percent3[2];
+
+ /** @structvar outer_wall
+ * @brief Number
+ * @note Outer wall tile
+ */
+ s16b outer_wall;
+
+ /** @structvar inner_wall
+ * @brief Number
+ * @note Inner wall tile
+ */
+ s16b inner_wall;
+
+ /** @structvar fill_type1
+ * @brief Number
+ * @note Cave tile 1
+ */
+ s16b fill_type1;
+
+ /** @structvar fill_percent1[2]
+ * @brief Number
+ * @note Chance of type 1
+ */
+ byte fill_percent1[2];
+
+ /** @structvar fill_type2
+ * @brief Number
+ * @note Cave tile 2
+ */
+ s16b fill_type2;
+
+ /** @structvar fill_percent2[2]
+ * @brief Number
+ * @note Chance of type 2
+ */
+ byte fill_percent2[2];
+
+ /** @structvar fill_type3
+ * @brief Number
+ * @note Cave tile 3
+ */
+ s16b fill_type3;
+
+ /** @structvar fill_percent3[2]
+ * @brief Number
+ * @note Chance of type 3
+ */
+ byte fill_percent3[2];
+
+ /** @structvar fill_method
+ * @brief Number
+ * @note Smoothing parameter for the above
+ */
+ byte fill_method;
+
+ /** @structvar mindepth
+ * @brief Number
+ * @note Minimal depth
+ */
+ s16b mindepth;
+
+ /** @structvar maxdepth
+ * @brief Number
+ * @note Maximal depth
+ */
+ s16b maxdepth;
+
+ /** @structvar principal
+ * @brief Boolean
+ * @note If it's a part of the main dungeon
+ */
+ bool principal;
+
+ /** @structvar next
+ * @brief Number
+ * @note The next part of the main dungeon
+ */
+ byte next;
+
+ /** @structvar min_plev
+ * @brief Number
+ * @note Minimal plev needed to enter -- it's an anti-cheating mesure
+ */
+ byte min_plev;
+
+ /** @structvar min_m_alloc_level
+ * @brief Number
+ * @note Minimal number of monsters per level
+ */
+ int min_m_alloc_level;
+
+ /** @structvar max_m_alloc_chance
+ * @brief Number
+ * @note There is a 1/max_m_alloc_chance chance per round of creating a new monster
+ */
+ int max_m_alloc_chance;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note Flags 1
+ */
+ u32b flags1;
+
+ /** @structvar flags2
+ * @brief Number
+ * @note Flags 1
+ */
+ u32b flags2;
+
+ /** @structvar size_x
+ * @brief Number
+ */
+ int size_x;
+
+ /** @structvar size_y
+ * @brief Number
+ * @note Desired numers of panels
+ */
+ int size_y;
+
+ /** @structvar rule_percents[100]
+ * @brief Number
+ * @note Flat rule percents
+ */
+ byte rule_percents[100];
+
+ /** @structvar rules[5]
+ * @brief rule_type
+ * @note Monster generation rules
+ */
+ rule_type rules[5];
+
+ /** @structvar final_object
+ * @brief Number
+ * @note The object you'll find at the bottom
+ */
+ int final_object;
+
+ /** @structvar final_artifact
+ * @brief Number
+ * @note The artifact you'll find at the bottom
+ */
+ int final_artifact;
+
+ /** @structvar final_guardian
+ * @brief Number
+ * @note The artifact's guardian. If an artifact is specified, then it's NEEDED
+ */
+ int final_guardian;
+
+ /** @structvar ix
+ * @brief Number
+ */
+ int ix;
+
+ /** @structvar iy
+ * @brief Number
+ */
+ int iy;
+
+ /** @structvar ox
+ * @brief Number
+ */
+ int ox;
+
+ /** @structvar oy
+ * @brief Number
+ * @note Wilderness coordinates of the entrance/output of the dungeon
+ */
+ int oy;
+
+ /** @structvar objs
+ * @brief obj_theme
+ * @note The drops type
+ */
+ obj_theme objs;
+
+ /** @structvar d_dice[4]
+ * @brief Number
+ * @note Number of dices
+ */
+ int d_dice[4];
+
+ /** @structvar d_side[4]
+ * @brief Number
+ * @note Number of sides
+ */
+ int d_side[4];
+
+ /** @structvar d_frequency[4]
+ * @brief Number
+ * @note Frequency of damage (1 is the minimum)
+ */
+ int d_frequency[4];
+
+ /** @structvar d_type[4]
+ * @brief Number
+ * @note Type of damage
+ */
+ int d_type[4];
+
+ /** @structvar t_idx[TOWN_DUNGEON]
+ * @brief Number
+ * @note The towns
+ */
+ s16b t_idx[TOWN_DUNGEON];
+
+ /** @structvar t_level[TOWN_DUNGEON]
+ * @brief Number
+ * @note The towns levels
+ */
+ s16b t_level[TOWN_DUNGEON];
+
+ /** @structvar t_num
+ * @brief Number
+ * @note Number of towns
+ */
+ s16b t_num;
+};
+
+/** @var max_d_idx
+ * @brief Number
+ */
+extern u16b max_d_idx;
+
+/** @var d_info[max_d_idx]
+ * @brief dungeon_info_type
+ */
+extern dungeon_info_type d_info[max_d_idx];
+
+/** @var *d_name
+ * @brief String
+ */
+extern char *d_name;
+
+/** @var *d_text
+ * @brief String
+ */
+extern char *d_text;
+
+/** @var max_wild_x
+ * @brief Number
+ */
+extern u16b max_wild_x;
+
+/** @var max_wild_y
+ * @brief Number
+ */
+extern u16b max_wild_y;
+
+/** @var max_wf_idx
+ * @brief Number
+ */
+extern u16b max_wf_idx;
+
+/** @var wf_info[max_wf_idx]
+ * @brief wilderness_type_info
+ */
+extern wilderness_type_info wf_info[max_wf_idx];
+
+/** @var *wf_name
+ * @brief String
+ */
+extern char *wf_name;
+
+/** @var *wf_text
+ * @brief String
+ */
+extern char *wf_text;
+
+/** @var DUNGEON_DEATH
+ * @brief Number
+ */
+extern s32b DUNGEON_DEATH;
+
+/** @var current_dungeon_idx;
+ * @brief Number
+ */
+extern byte dungeon_type@current_dungeon_idx;
+
+/*
+ * tolua doesnt like wierd arraysn, I'll use accessing functions
+ * extern wilderness_map wild_map[max_wild_y][max_wild_x];
+ */
+$static wilderness_map* lua_get_wild_map(int y, int x) { return &wild_map[y][x]; }
+
+/** @fn wild_map(int y, int x);
+ * @brief Return a map of the wilderness at coordinate (y,x).\n
+ * @param y Number \n y coordinate of wilderness map
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of wilderness map
+ * @brief X-coordinate
+ * @return wilderness_map \n map of wilderness at coordinate (y,x)
+ * @note (see file w_dun.c)
+ */
+wilderness_map* lua_get_wild_map@wild_map(int y, int x);
+
+/** @fn place_trap(int y, int x)
+ * @brief Place a random trap at the given location.\n
+ * @param y Number \n y coordinate of dungeon
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of dungeon
+ * @brief X-coordinate
+ * @note
+ * Places a random trap at the given location.\n
+ * The location must be a valid, empty, clean, floor grid.
+ * @note (see file traps.c)
+ */
+extern void place_trap(int y, int x);
+
+/** @fn place_floor(int y, int x)
+ * @brief Place floor terrain at (y, x).\n
+ * @param y Number \n y coordinate of dungeon
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of dungeon
+ * @brief X-coordinate
+ * @note
+ * Place floor terrain at (y, x) according to dungeon info.
+ * @note (see file cave.c)
+ */
+extern void place_floor(int y, int x);
+
+/** @fn place_filler(int y, int x)
+ * @brief Place a cave filler at (y, x).\n
+ * @param y Number \n y coordinate of dungeon
+ * @brief Y-coordinate
+ * @param x Number \n x coordinate of dungeon
+ * @brief X-coordinate
+ * @note (see file generate.c)
+ */
+extern void place_filler(int y, int x);
+
+/** @fn add_scripted_generator(cptr name, bool stairs, bool monsters, bool objects, bool miscs)
+ * @dgonly
+ * @param name String \n Name of generator
+ * @param stairs Boolean \n TRUE if stairs can be generated, otherwise FALSE
+ * @param monsters Boolean \n TRUE if monsters can be generated, otherwise FALSE
+ * @param objects Boolean \n TRUE if objects can be generated, otherwise FALSE
+ * @param miscs Boolean \n TRUE if other stuff can be generated, otherwise FALSE
+ * @note (defined in file lua_bind.c)
+ */
+extern void add_scripted_generator(cptr name, bool stairs, bool monsters, bool objects, bool miscs);
+
+/** @fn new_player_spot(int branch)
+ * @brief Places player in a new location.\n
+ * @param branch Number \n branch is the dungeon branch (if any).
+ * @brief Dungeon branch
+ * @return Boolean \n TRUE if player was placed successfully, otherwise FALSE.
+ * The global values py and px are updated.
+ * @note
+ * Up to 5000 attempts are made to place the player in the dungeon. The grid
+ * must be a naked floor and not an anti-teleport grid. In some circumstances
+ * stairs or ways in/out may be created under the player.
+ * @note (see file generate.c)
+ */
+extern bool new_player_spot(int branch);
+
+/** @fn get_level_desc(char *buf)
+ * @brief Return the special level desc.\n
+ * @param *buf String
+ * @brief Description
+ * @return *buf String \n The level description
+ * @return Boolean \n TRUE if a level description was returned, otherwise FALSE
+ * @note
+ * This is the 'D' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_level_desc(char *buf);
+
+/** @fn get_level_flags()
+ * These are the 'F' lines in the dngn files.
+ * @note (see file levels.c)
+ */
+extern void get_level_flags();
+
+/** @fn get_dungeon_name(char *buf)
+ * @brief Return the special level name.\n
+ * @param *buf String
+ * @brief Name
+ * @return *buf String \n The level name
+ * @return Boolean \n TRUE if a level name was returned, otherwise FALSE
+ * @note
+ * This is the 'N' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_dungeon_name(char *buf);
+
+/** @fn get_dungeon_special(char *buf)
+ * @brief Return the map filename.\n
+ * @param *buf String
+ * @brief Map filename
+ * @return *buf String \n The map filename
+ * @return Boolean \n TRUE if a map filename was returned, otherwise FALSE
+ * @note
+ * This is the 'S' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_dungeon_special(char *buf);
+
+/** @fn get_command(const char *file, char comm, char *param)
+ * @brief Return the parameter of command "comm" in file "*file".\n
+ * @param *file String \n name of the dungeon file.
+ * @brief Dungeon file
+ * @param comm String \n The command \n
+ * 'A' = father branch, 'B' = branch, 'D' = desccription, 'L' = father level,
+ * 'N' = name, 'S' = savefile extension, 'U' = map filename
+ * @brief Command
+ * @param *param String
+ * @brief Parameter
+ * @return *param String \n The result of the command
+ * @return Boolean \n TRUE if a result is returned, otherwise FALSE
+ * @note (see file levels.c)
+ */
+extern bool get_command(const char *file, char comm, char *param);
+
+/** @fn get_branch()
+ * @brief return the dungeon branch starting form the current dungeon/level.
+ * @return Number \n The branch
+ * @note
+ * This is the 'B' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern int get_branch();
+
+/** @fn get_fbranch()
+ * @brief Return the father dungeon branch.
+ * @return Number \n The father branch
+ * @note
+ * This is the 'A' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern int get_fbranch();
+
+/** @fn get_flevel()
+ * @brief Return the father dungeon level.
+ * @return Number \n The father level
+ * @note
+ * This is the 'L' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern int get_flevel();
+
+/** @fn get_dungeon_save(char *buf)
+ * @brief Return the extension of the savefile for the level.\n
+ * @param *buf String
+ * @brief Savefile extension
+ * @return *buf String \n The savefile extension
+ * @return Boolean \n TRUE if a savefile extension was returned, otherwise FALSE
+ * This is the 'S' line in the dngn files.
+ * @note (see file levels.c)
+ */
+extern bool get_dungeon_save(char *buf);
diff --git a/src/externs.h b/src/externs.h
new file mode 100644
index 00000000..50ca716b
--- /dev/null
+++ b/src/externs.h
@@ -0,0 +1,1920 @@
+/* File: externs.h */
+
+/* Purpose: extern declarations (variables and functions) */
+
+/*
+ * Note that some files have their own header files
+ * (z-virt.h, z-util.h, z-form.h, term.h, random.h)
+ */
+
+
+/*
+ * Automatically generated "variable" declarations
+ */
+
+extern int max_macrotrigger;
+extern char *macro_template;
+extern char *macro_modifier_chr;
+extern char *macro_modifier_name[MAX_MACRO_MOD];
+extern char *macro_trigger_name[MAX_MACRO_TRIG];
+extern char *macro_trigger_keycode[2][MAX_MACRO_TRIG];
+
+/* tables.c */
+extern s16b ddd[9];
+extern s16b ddx[10];
+extern s16b ddy[10];
+extern s16b ddx_ddd[9];
+extern s16b ddy_ddd[9];
+extern char hexsym[16];
+extern byte adj_val_min[];
+extern byte adj_val_max[];
+extern byte adj_mag_study[];
+extern byte adj_mag_mana[];
+extern byte adj_mag_fail[];
+extern byte adj_mag_stat[];
+extern byte adj_chr_gold[];
+extern byte adj_int_dev[];
+extern byte adj_wis_sav[];
+extern byte adj_dex_dis[];
+extern byte adj_int_dis[];
+extern byte adj_dex_ta[];
+extern byte adj_str_td[];
+extern byte adj_dex_th[];
+extern byte adj_str_th[];
+extern byte adj_str_wgt[];
+extern byte adj_str_hold[];
+extern byte adj_str_dig[];
+extern byte adj_str_blow[];
+extern byte adj_dex_blow[];
+extern byte adj_dex_safe[];
+extern byte adj_con_fix[];
+extern byte adj_con_mhp[];
+extern byte blows_table[12][12];
+extern s16b arena_monsters[MAX_ARENA_MONS];
+extern byte extract_energy[300];
+extern s32b player_exp[PY_MAX_LEVEL];
+extern player_sex sex_info[MAX_SEXES];
+extern deity_type deity_info_init[MAX_GODS_INIT];
+extern cptr color_names[16];
+extern cptr stat_names[6];
+extern cptr stat_names_reduced[6];
+extern cptr window_flag_desc[32];
+extern option_type option_info[];
+extern cptr chaos_patrons[MAX_PATRON];
+extern int chaos_stats[MAX_PATRON];
+extern int chaos_rewards[MAX_PATRON][20];
+extern martial_arts bear_blows[MAX_BEAR];
+extern martial_arts ma_blows[MAX_MA];
+extern magic_power mindcraft_powers[MAX_MINDCRAFT_POWERS];
+extern magic_power necro_powers[MAX_NECRO_POWERS];
+extern magic_power mimic_powers[MAX_MIMIC_POWERS];
+extern magic_power symbiotic_powers[MAX_SYMBIOTIC_POWERS];
+extern cptr deity_rarity[2];
+extern cptr deity_niceness[10];
+extern cptr deity_standing[11];
+extern move_info_type move_info[9];
+extern tactic_info_type tactic_info[9];
+extern activation activation_info[MAX_T_ACT];
+extern inscription_info_type inscription_info[MAX_INSCRIPTIONS];
+extern cptr sense_desc[];
+extern flags_group flags_groups[MAX_FLAG_GROUP];
+extern power_type powers_type_init[POWER_MAX_INIT];
+extern quest_type quest_info[MAX_Q_IDX_INIT];
+extern cptr artifact_names_list;
+extern monster_power monster_powers[96];
+extern tval_desc tval_descs[];
+extern between_exit between_exits[MAX_BETWEEN_EXITS];
+extern int month_day[9];
+extern cptr month_name[9];
+extern cli_comm *cli_info;
+extern int cli_total;
+extern quest_type quest_init_tome[MAX_Q_IDX_INIT];
+extern int max_body_part[BODY_MAX];
+extern gf_name_type gf_names[];
+
+
+/* variable.c */
+extern cptr copyright[5];
+extern byte version_major;
+extern byte version_minor;
+extern byte version_patch;
+extern byte version_extra;
+extern byte sf_major;
+extern byte sf_minor;
+extern byte sf_patch;
+extern byte sf_extra;
+extern u32b sf_xtra;
+extern u32b sf_when;
+extern u16b sf_lives;
+extern u16b sf_saves;
+extern u32b vernum; /* Version flag */
+extern bool arg_fiddle;
+extern bool arg_wizard;
+extern bool arg_sound;
+extern bool arg_graphics;
+extern bool arg_force_original;
+extern bool arg_force_roguelike;
+extern bool arg_bigtile;
+extern bool character_generated;
+extern bool character_dungeon;
+extern bool character_loaded;
+extern bool character_saved;
+extern bool character_icky;
+extern bool character_xtra;
+extern u32b seed_flavor;
+extern u32b seed_town;
+extern u32b seed_dungeon;
+extern s16b command_cmd;
+extern s16b command_arg;
+extern s16b command_rep;
+extern s16b command_dir;
+extern s16b command_see;
+extern s16b command_wrk;
+extern s16b command_new;
+extern s32b energy_use;
+extern s16b choose_default;
+extern bool create_up_stair;
+extern bool create_down_stair;
+extern bool create_up_shaft;
+extern bool create_down_shaft;
+extern bool msg_flag;
+extern bool alive;
+extern bool death;
+extern s16b running;
+extern s16b resting;
+extern s16b cur_hgt;
+extern s16b cur_wid;
+extern s16b dun_level;
+extern s16b old_dun_level;
+extern s16b num_repro;
+extern s16b object_level;
+extern s16b monster_level;
+extern s32b turn;
+extern s32b old_turn;
+extern bool wizard;
+extern bool use_sound;
+extern bool use_graphics;
+extern bool use_bigtile;
+extern byte graphics_mode;
+extern u16b total_winner;
+extern u16b has_won;
+extern u16b panic_save;
+extern u16b noscore;
+extern s16b signal_count;
+extern bool inkey_base;
+extern bool inkey_xtra;
+extern bool inkey_scan;
+extern bool inkey_flag;
+extern s16b coin_type;
+extern bool opening_chest;
+extern bool shimmer_monsters;
+extern bool shimmer_objects;
+extern bool repair_monsters;
+extern bool repair_objects;
+extern s16b inven_nxt;
+extern s16b inven_cnt;
+extern s16b equip_cnt;
+extern s16b o_max;
+extern s16b o_cnt;
+extern s16b m_max;
+extern s16b m_cnt;
+extern s16b hack_m_idx;
+extern s16b hack_m_idx_ii;
+extern int total_friends;
+extern s32b total_friend_levels;
+extern int leaving_quest;
+extern bool multi_rew;
+extern char summon_kin_type;
+extern bool hack_mind;
+extern bool hack_corruption;
+extern bool is_autosave;
+extern int artifact_bias;
+extern FILE *text_out_file;
+extern void (*text_out_hook)(byte a, cptr str);
+extern int text_out_wrap;
+extern int text_out_indent;
+extern int highscore_fd;
+extern bool show_inven_graph;
+extern bool show_store_graph;
+extern bool show_equip_graph;
+extern bool rogue_like_commands;
+extern bool quick_messages;
+extern bool other_query_flag;
+extern bool carry_query_flag;
+extern bool always_pickup;
+extern bool prompt_pickup_heavy;
+extern bool always_repeat;
+extern bool use_old_target;
+extern bool depth_in_feet;
+extern bool use_color;
+extern bool compress_savefile;
+extern bool hilite_player;
+extern bool ring_bell;
+extern bool find_ignore_stairs;
+extern bool find_ignore_doors;
+extern bool find_cut;
+extern bool find_examine;
+extern bool disturb_near;
+extern bool disturb_move;
+extern bool disturb_panel;
+extern bool disturb_detect;
+extern bool disturb_state;
+extern bool disturb_minor;
+extern bool disturb_other;
+extern bool avoid_abort;
+extern bool avoid_shimmer;
+extern bool avoid_other;
+extern bool flush_disturb;
+extern bool flush_failure;
+extern bool flush_command;
+extern bool fresh_before;
+extern bool fresh_after;
+extern bool fresh_message;
+extern bool alert_hitpoint;
+extern bool alert_failure;
+extern bool view_yellow_lite;
+extern bool view_bright_lite;
+extern bool view_granite_lite;
+extern bool view_special_lite;
+extern bool plain_descriptions;
+extern bool stupid_monsters;
+extern bool auto_destroy;
+extern bool wear_confirm;
+extern bool confirm_stairs;
+extern bool disturb_pets;
+extern bool view_perma_grids;
+extern bool view_torch_grids;
+extern bool monster_lite;
+extern bool flow_by_sound;
+extern bool flow_by_smell;
+extern bool track_follow;
+extern bool track_target;
+extern bool stack_allow_items;
+extern bool stack_allow_wands;
+extern bool stack_force_notes;
+extern bool stack_force_costs;
+extern bool view_reduce_lite;
+extern bool view_reduce_view;
+extern bool auto_haggle;
+extern bool auto_scum;
+extern bool expand_look;
+extern bool expand_list;
+extern bool dungeon_align;
+extern bool dungeon_stair;
+extern bool smart_learn;
+extern bool smart_cheat;
+extern bool show_labels;
+extern bool show_weights;
+extern bool show_choices;
+extern bool show_details;
+extern bool testing_stack;
+extern bool testing_carry;
+extern bool cheat_peek;
+extern bool cheat_hear;
+extern bool cheat_room;
+extern bool cheat_xtra;
+extern bool cheat_know;
+extern bool cheat_live;
+extern bool last_words; /* Zangband options */
+extern bool speak_unique;
+extern bool small_levels;
+extern bool empty_levels;
+extern bool always_small_level;
+#if 0 /* It's controlled by insanity -- pelpel */
+extern bool flavored_attacks;
+#endif
+extern bool player_symbols;
+extern byte hitpoint_warn;
+extern byte delay_factor;
+extern s16b autosave_freq;
+extern bool autosave_t;
+extern bool autosave_l;
+extern s16b feeling;
+extern s16b rating;
+extern bool good_item_flag;
+extern bool closing_flag;
+extern s16b max_panel_rows, max_panel_cols;
+extern s16b panel_row_min, panel_row_max;
+extern s16b panel_col_min, panel_col_max;
+extern s16b panel_col_prt, panel_row_prt;
+extern byte feat_wall_outer;
+extern byte feat_wall_inner;
+extern s16b floor_type[100];
+extern s16b fill_type[100];
+extern s16b py;
+extern s16b px;
+extern s16b target_who;
+extern s16b target_col;
+extern s16b target_row;
+extern s16b health_who;
+extern s16b monster_race_idx;
+extern s16b monster_ego_idx;
+extern object_type *tracked_object;
+extern int player_uid;
+extern int player_euid;
+extern int player_egid;
+extern char player_name[32];
+extern char player_base[32];
+extern char died_from[80];
+extern char history[4][60];
+extern char savefile[1024];
+extern bool savefile_setuid;
+extern s16b lite_n;
+extern s16b lite_y[LITE_MAX];
+extern s16b lite_x[LITE_MAX];
+extern s16b view_n;
+extern byte view_y[VIEW_MAX];
+extern byte view_x[VIEW_MAX];
+extern s16b temp_n;
+extern byte temp_y[TEMP_MAX];
+extern byte temp_x[TEMP_MAX];
+extern s16b macro__num;
+extern cptr *macro__pat;
+extern cptr *macro__act;
+extern bool *macro__cmd;
+extern char *macro__buf;
+extern s16b quark__num;
+extern cptr *quark__str;
+extern u16b message__next;
+extern u16b message__last;
+extern u16b message__head;
+extern u16b message__tail;
+extern u16b *message__ptr;
+extern byte *message__color;
+extern byte *message__type;
+extern u16b *message__count;
+extern char *message__buf;
+extern u32b option_flag[8];
+extern u32b option_mask[8];
+extern u32b window_flag[ANGBAND_TERM_MAX];
+extern u32b window_mask[ANGBAND_TERM_MAX];
+extern term *angband_term[ANGBAND_TERM_MAX];
+extern char angband_term_name[ANGBAND_TERM_MAX][80];
+extern byte angband_color_table[256][4];
+extern char angband_sound_name[SOUND_MAX][16];
+extern cave_type *cave[MAX_HGT];
+extern object_type *o_list;
+extern monster_type *m_list;
+extern monster_type *km_list;
+extern u16b max_real_towns;
+extern u16b max_towns;
+extern town_type *town_info;
+extern s16b alloc_kind_size;
+extern alloc_entry *alloc_kind_table;
+extern bool alloc_kind_table_valid;
+extern s16b alloc_race_size;
+extern alloc_entry *alloc_race_table;
+extern byte misc_to_attr[256];
+extern char misc_to_char[256];
+extern byte tval_to_attr[128];
+extern char tval_to_char[128];
+extern cptr keymap_act[KEYMAP_MODES][256];
+extern player_type p_body;
+extern player_type *p_ptr;
+extern player_sex *sp_ptr;
+extern player_race *rp_ptr;
+extern player_race_mod *rmp_ptr;
+extern player_class *cp_ptr;
+extern player_spec *spp_ptr;
+extern u32b alchemist_known_egos[32];
+extern alchemist_recipe *alchemist_recipes;
+extern u32b alchemist_known_artifacts[6];
+extern u32b alchemist_gained;
+extern s16b player_hp[PY_MAX_LEVEL];
+extern header *al_head;
+extern char *al_name;
+extern artifact_select_flag *a_select_flags;
+
+extern header *ab_head;
+extern ability_type *ab_info;
+extern char *ab_name;
+extern char *ab_text;
+
+extern header *s_head;
+extern skill_type *s_info;
+extern char *s_name;
+extern char *s_text;
+
+extern header *v_head;
+extern vault_type *v_info;
+extern char *v_name;
+extern char *v_text;
+extern header *f_head;
+extern feature_type *f_info;
+extern char *f_name;
+extern char *f_text;
+extern header *k_head;
+extern object_kind *k_info;
+extern char *k_name;
+extern char *k_text;
+extern header *a_head;
+extern artifact_type *a_info;
+extern char *a_name;
+extern char *a_text;
+extern header *e_head;
+extern ego_item_type *e_info;
+extern char *e_name;
+extern char *e_text;
+extern header *ra_head;
+extern randart_part_type *ra_info;
+extern randart_gen_type ra_gen[30];
+extern header *r_head;
+extern monster_race *r_info;
+extern char *r_name;
+extern char *r_text;
+extern header *re_head;
+extern monster_ego *re_info;
+extern char *re_name;
+extern header *d_head;
+extern dungeon_info_type *d_info;
+extern char *d_name;
+extern char *d_text;
+extern header *c_head;
+extern player_class *class_info;
+extern char *c_name;
+extern char *c_text;
+extern meta_class_type *meta_class_info;
+extern header *rp_head;
+extern player_race *race_info;
+extern char *rp_name;
+extern char *rp_text;
+extern header *rmp_head;
+extern player_race_mod *race_mod_info;
+extern char *rmp_name;
+extern char *rmp_text;
+extern header *t_head;
+extern trap_type *t_info;
+extern char *t_name;
+extern char *t_text;
+extern header *wf_head;
+extern wilderness_type_info *wf_info;
+extern char *wf_name;
+extern char *wf_text;
+extern int wildc2i[256];
+extern header *st_head;
+extern store_info_type *st_info;
+extern char *st_name;
+extern header *ba_head;
+extern store_action_type *ba_info;
+extern char *ba_name;
+extern header *ow_head;
+extern owner_type *ow_info;
+extern char *ow_name;
+extern header *set_head;
+extern set_type *set_info;
+extern char *set_name;
+extern char *set_text;
+extern cptr ANGBAND_SYS;
+extern cptr ANGBAND_KEYBOARD;
+extern cptr ANGBAND_GRAF;
+extern cptr ANGBAND_DIR;
+extern cptr ANGBAND_DIR_APEX;
+extern cptr ANGBAND_DIR_BONE;
+extern cptr ANGBAND_DIR_CORE;
+extern cptr ANGBAND_DIR_DNGN;
+extern cptr ANGBAND_DIR_DATA;
+extern cptr ANGBAND_DIR_EDIT;
+extern cptr ANGBAND_DIR_FILE;
+extern cptr ANGBAND_DIR_HELP;
+extern cptr ANGBAND_DIR_INFO;
+extern cptr ANGBAND_DIR_MODULES;
+extern cptr ANGBAND_DIR_NOTE;
+extern cptr ANGBAND_DIR_SAVE;
+extern cptr ANGBAND_DIR_SCPT;
+extern cptr ANGBAND_DIR_PATCH;
+extern cptr ANGBAND_DIR_PREF;
+extern cptr ANGBAND_DIR_USER;
+extern cptr ANGBAND_DIR_XTRA;
+extern cptr ANGBAND_DIR_CMOV;
+extern char pref_tmp_value[8];
+extern bool item_tester_full;
+extern byte item_tester_tval;
+extern bool (*item_tester_hook)(object_type *o_ptr);
+extern bool (*ang_sort_comp)(vptr u, vptr v, int a, int b);
+extern void (*ang_sort_swap)(vptr u, vptr v, int a, int b);
+extern bool (*get_mon_num_hook)(int r_idx);
+extern bool (*get_mon_num2_hook)(int r_idx);
+extern bool (*get_obj_num_hook)(int k_idx);
+extern bool monk_armour_aux;
+extern bool monk_notify_aux;
+extern u16b max_wild_x;
+extern u16b max_wild_y;
+extern wilderness_map **wild_map;
+extern u16b old_max_s_idx;
+extern u16b max_ab_idx;
+extern u16b max_s_idx;
+extern u16b max_al_idx;
+extern u16b max_r_idx;
+extern u16b max_re_idx;
+extern u16b max_k_idx;
+extern u16b max_v_idx;
+extern u16b max_f_idx;
+extern u16b max_a_idx;
+extern u16b max_e_idx;
+extern u16b max_ra_idx;
+extern u16b max_d_idx;
+extern u16b max_o_idx;
+extern u16b max_m_idx;
+extern u16b max_t_idx;
+extern u16b max_rp_idx;
+extern u16b max_c_idx;
+extern u16b max_mc_idx;
+extern u16b max_rmp_idx;
+extern u16b max_st_idx;
+extern u16b max_ba_idx;
+extern u16b max_ow_idx;
+extern u16b max_wf_idx;
+extern s16b max_set_idx;
+extern int init_flags;
+extern bool ambush_flag;
+extern bool fate_flag;
+extern u16b no_breeds;
+extern bool carried_monster_hit;
+extern random_artifact random_artifacts[MAX_RANDARTS];
+extern s32b RANDART_WEAPON;
+extern s32b RANDART_ARMOR;
+extern s32b RANDART_JEWEL;
+extern s16b bounties[MAX_BOUNTIES][2];
+extern random_spell random_spells[MAX_SPELLS];
+extern s16b spell_num;
+extern rune_spell rune_spells[MAX_RUNES];
+extern s16b rune_num;
+extern fate fates[MAX_FATES];
+extern byte vanilla_town;
+extern byte dungeon_type;
+extern s16b *max_dlv;
+extern u32b total_bounties;
+extern s16b doppleganger;
+extern bool generate_encounter;
+extern bool permanent_levels;
+extern bool autoroll;
+extern bool point_based;
+extern bool maximize, preserve, special_lvls, ironman_rooms;
+extern bool inventory_no_move;
+extern bool take_notes, auto_notes;
+extern bool *m_allow_special;
+extern bool *k_allow_special;
+extern bool *a_allow_special;
+extern bool rand_birth;
+extern bool fast_autoroller;
+extern bool joke_monsters;
+extern bool munchkin_multipliers;
+extern bool center_player;
+#ifdef SAFER_PANICS
+extern bool panicload;
+#endif
+extern s16b plots[MAX_PLOTS];
+extern random_quest random_quests[MAX_RANDOM_QUEST];
+extern bool exp_need;
+extern bool autoload_old_colors;
+extern bool fate_option;
+extern bool *special_lvl[MAX_DUNGEON_DEPTH];
+extern bool generate_special_feeling;
+extern bool auto_more;
+extern u32b dungeon_flags1;
+extern u32b dungeon_flags2;
+extern birther previous_char;
+extern hist_type *bg;
+extern int max_bg_idx;
+extern power_type *powers_type;
+extern s16b power_max;
+extern s32b extra_savefile_parts;
+extern s16b max_q_idx;
+extern quest_type *quest;
+extern bool player_char_health;
+extern s16b max_spells;
+extern spell_type *school_spells;
+extern s16b max_schools;
+extern school_type *schools;
+extern int project_time;
+extern s32b project_time_effect;
+extern effect_type effects[MAX_EFFECTS];
+extern char gen_skill_basem[MAX_SKILLS];
+extern u32b gen_skill_base[MAX_SKILLS];
+extern char gen_skill_modm[MAX_SKILLS];
+extern s16b gen_skill_mod[MAX_SKILLS];
+extern bool linear_stats;
+extern int max_bact;
+extern s16b max_corruptions;
+extern bool option_ingame_help;
+extern bool automatizer_enabled;
+extern s16b last_teleportation_y;
+extern s16b last_teleportation_x;
+extern cptr game_module;
+extern s32b VERSION_MAJOR;
+extern s32b VERSION_MINOR;
+extern s32b VERSION_PATCH;
+extern s32b max_plev;
+extern s32b DUNGEON_DEATH;
+extern deity_type *deity_info;
+extern s32b max_gods;
+extern timer_type *gl_timers;
+
+/* plots.c */
+extern FILE *hook_file;
+extern bool check_hook(int h_idx);
+extern void wipe_hooks(void);
+extern void dump_hooks(int h_idx);
+extern void init_hooks(void);
+extern hooks_chain* add_hook(int h_idx, hook_type hook, cptr name);
+extern void add_hook_script(int h_idx, char *script, cptr name);
+extern void del_hook(int h_idx, hook_type hook);
+extern void del_hook_name(int h_idx, cptr name);
+extern s32b get_next_arg(char *fmt);
+extern int process_hooks_restart;
+extern hook_return process_hooks_return[20];
+extern bool process_hooks_ret(int h_idx, char *ret, char *fmt, ...);
+extern bool process_hooks(int h_idx, char *fmt, ...);
+
+/* help.c */
+extern void ingame_help(bool enable);
+
+/* birth.c */
+extern void print_desc_aux(cptr txt, int y, int x);
+extern void save_savefile_names(void);
+extern bool no_begin_screen;
+extern bool begin_screen(void);
+extern errr init_randart(void);
+extern void get_height_weight(void);
+extern void player_birth(void);
+
+/* cave.c */
+extern int distance(int y1, int x1, int y2, int x2);
+extern bool los(int y1, int x1, int y2, int x2);
+extern bool cave_valid_bold(int y, int x);
+extern bool no_lite(void);
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+extern void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp, byte *eap, char *ecp);
+#else /* USE_EGO_GRAPHICS */
+extern void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp);
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+extern void map_info(int y, int x, byte *ap, char *cp);
+#endif /* USE_TRANSPARENCY */
+extern void map_info_default(int y, int x, byte *ap, char *cp);
+extern void move_cursor_relative(int row, int col);
+extern void print_rel(char c, byte a, int y, int x);
+extern void note_spot(int y, int x);
+extern void lite_spot(int y, int x);
+extern void prt_map(void);
+extern void display_map(int *cy, int *cx);
+extern void do_cmd_view_map(void);
+extern errr vinfo_init(void);
+extern void forget_view(void);
+extern void update_view(void);
+extern void forget_mon_lite(void);
+extern void update_mon_lite(void);
+extern void forget_flow(void);
+extern void update_flow(void);
+extern void map_area(void);
+extern void wiz_lite(void);
+extern void wiz_lite_extra(void);
+extern void wiz_dark(void);
+extern void cave_set_feat(int y, int x, int feat);
+extern void place_floor(int y, int x);
+extern void place_filler(int y, int x);
+extern void mmove2(int *y, int *x, int y1, int x1, int y2, int x2);
+extern bool projectable(int y1, int x1, int y2, int x2);
+extern void scatter(int *yp, int *xp, int y, int x, int d, int m);
+extern void health_track(int m_idx);
+extern void monster_race_track(int r_idx, int ego);
+extern void object_track(object_type *o_ptr);
+extern void disturb(int stop_search, int flush_output);
+extern int is_quest(int level);
+extern int random_quest_number(void);
+extern int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags);
+
+/* cmovie.c */
+extern void cmovie_init_second(void);
+extern s16b do_play_cmovie(cptr cmov_file);
+extern void do_record_cmovie(cptr cmovie);
+extern void do_stop_cmovie(void);
+extern void do_start_cmovie(void);
+extern void cmovie_clean_line(int y, char *abuf, char *cbuf);
+extern void cmovie_record_line(int y);
+extern void do_cmovie_insert(void);
+
+/* cmd1.c */
+extern void attack_special(monster_type *m_ptr, s32b special, int dam);
+extern bool test_hit_fire(int chance, int ac, int vis);
+extern bool test_hit_norm(int chance, int ac, int vis);
+extern s16b critical_shot(int weight, int plus, int dam, int skill);
+extern s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool *done_crit);
+extern s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr, s32b *special);
+extern void search(void);
+extern void carry(int pickup);
+extern void py_attack(int y, int x, int max_blow);
+extern bool player_can_enter(byte feature);
+extern void move_player(int dir, int do_pickup, bool disarm);
+extern void move_player_aux(int dir, int do_pickup, int run, bool disarm);
+extern void run_step(int dir);
+extern void step_effects(int y, int x, int do_pickup);
+extern void do_cmd_pet(void);
+extern bool do_cmd_integrate_body(void);
+extern bool do_cmd_leave_body(bool drop_body);
+extern bool execute_inscription(byte i, byte y, byte x);
+extern void do_cmd_engrave(void);
+extern void do_spin(void);
+
+/* cmd2.c */
+extern byte show_monster_inven(int m_idx, int *monst_list);
+extern int breakage_chance(object_type *o_ptr);
+extern void do_cmd_go_up(void);
+extern void do_cmd_go_down(void);
+extern void do_cmd_search(void);
+extern void do_cmd_toggle_search(void);
+extern void do_cmd_open(void);
+extern void do_cmd_close(void);
+extern void do_cmd_chat(void);
+extern void do_cmd_give(void);
+extern bool do_cmd_tunnel_aux(int y, int x, int dir);
+extern void do_cmd_tunnel(void);
+extern void do_cmd_disarm(void);
+extern void do_cmd_disarm(void);
+extern void do_cmd_bash(void);
+extern void do_cmd_alter(void);
+extern void do_cmd_spike(void);
+extern void do_cmd_walk(int pickup, bool disarm);
+extern void do_cmd_stay(int pickup);
+extern void do_cmd_run(void);
+extern void do_cmd_rest(void);
+extern int get_shooter_mult(object_type *o_ptr);
+extern void do_cmd_fire(void);
+extern void do_cmd_throw(void);
+extern void do_cmd_boomerang(void);
+extern void do_cmd_unwalk(void);
+extern void do_cmd_immovable_special(void);
+extern void fetch(int dir, int wgt, bool require_los);
+extern void do_cmd_sacrifice(void);
+extern void do_cmd_create_artifact(object_type *q_ptr);
+extern void do_cmd_steal(void);
+extern void do_cmd_racial_power(void);
+
+/* cmd3.c */
+extern void do_cmd_html_dump(void);
+extern void cli_add(cptr active, cptr trigger, cptr descr);
+extern void do_cmd_cli(void);
+extern void do_cmd_cli_help(void);
+extern void do_cmd_inven(void);
+extern void do_cmd_equip(void);
+extern void do_cmd_wield(void);
+extern void do_cmd_takeoff(void);
+extern void do_cmd_drop(void);
+extern void do_cmd_destroy(void);
+extern void do_cmd_observe(void);
+extern void do_cmd_uninscribe(void);
+extern void do_cmd_inscribe(void);
+extern void do_cmd_refill(void);
+extern void do_cmd_target(void);
+extern void do_cmd_look(void);
+extern void do_cmd_locate(void);
+extern void do_cmd_query_symbol(void);
+extern bool do_cmd_sense_grid_mana(void);
+extern bool research_mon(void);
+extern s32b portable_hole_weight(void);
+extern void set_portable_hole_weight(void);
+extern void do_cmd_portable_hole(void);
+
+/* cmd4.c */
+extern bool change_option(cptr name, bool value);
+extern void macro_recorder_start(void);
+extern void macro_recorder_add(char c);
+extern void macro_recorder_stop(void);
+extern void do_cmd_macro_recorder(void);
+extern void do_cmd_redraw(void);
+extern void do_cmd_change_name(void);
+extern void do_cmd_message_one(void);
+extern void do_cmd_messages(void);
+extern void do_cmd_options(void);
+extern void do_cmd_pref(void);
+extern void do_cmd_macros(void);
+extern void do_cmd_visuals(void);
+extern void do_cmd_colors(void);
+extern void do_cmd_note(void);
+extern void do_cmd_version(void);
+extern void do_cmd_feeling(void);
+extern void do_cmd_load_screen(void);
+extern void do_cmd_save_screen(void);
+extern void do_cmd_knowledge(void);
+extern void plural_aux(char * Name);
+extern void do_cmd_checkquest(void);
+extern void do_cmd_change_tactic(int i);
+extern void do_cmd_change_movement(int i);
+extern void do_cmd_time(void);
+extern void do_cmd_options_aux(int page, cptr info, bool read_only);
+
+
+/* cmd5.c */
+extern bool is_magestaff(void);
+extern void calc_magic_bonus(void);
+extern void do_cmd_browse_aux(object_type *o_ptr);
+extern void do_cmd_browse(void);
+extern void do_cmd_cast(void);
+extern void do_cmd_pray(void);
+extern void do_cmd_rerate(void);
+extern void corrupt_player(void);
+extern bool item_tester_hook_armour(object_type *o_ptr);
+extern void fetch(int dir, int wgt, bool require_los);
+extern void do_poly_self(void);
+extern void brand_weapon(int brand_type);
+extern cptr symbiote_name(bool capitalize);
+extern int use_symbiotic_power(int r_idx, bool great, bool only_number, bool no_cost);
+extern u32b get_school_spell(cptr do_what, cptr check_fct, s16b force_book);
+extern void do_cmd_copy_spell(void);
+extern void cast_school_spell(void);
+extern void browse_school_spell(int book, int pval, object_type *o_ptr);
+extern int find_spell(char *name);
+extern bool is_school_book(object_type *o_ptr);
+
+/* cmd6.c */
+extern void set_stick_mode(object_type *o_ptr);
+extern void unset_stick_mode(void);
+extern void do_cmd_eat_food(void);
+extern void do_cmd_quaff_potion(void);
+extern void do_cmd_read_scroll(void);
+extern void do_cmd_aim_wand(void);
+extern void do_cmd_use_staff(void);
+extern void do_cmd_zap_rod(void);
+extern const char *activation_aux(object_type *o_ptr, bool desc, int item);
+extern void do_cmd_activate(void);
+extern void do_cmd_rerate(void);
+extern void do_cmd_cut_corpse(void);
+extern void do_cmd_cure_meat(void);
+extern void do_cmd_drink_fountain(void);
+extern void do_cmd_fill_bottle(void);
+
+/* cmd7.c */
+extern void do_cmd_create_boulder(void);
+extern int rune_exec(rune_spell *spell, int cost);
+extern void necro_info(char *p, int power);
+extern void mindcraft_info(char *p, int power);
+extern void symbiotic_info(char *p, int power);
+extern void mimic_info(char *p, int power);
+extern random_spell* select_spell(bool quick);
+extern void cast_magic_spell(int spell, byte level);
+extern void do_cmd_summoner(void);
+extern void do_cmd_mindcraft(void);
+extern void do_cmd_mimic(void);
+extern void do_cmd_blade(void);
+extern void use_ability_blade(void);
+extern bool alchemist_exists(int tval, int sval, int ego, int artifact);
+extern void rod_tip_extract(object_type *o_ptr);
+extern void do_cmd_toggle_artifact(object_type *o_ptr);
+extern bool alchemist_items_check(int tval, int sval, int ego, int tocreate, bool message);
+extern void alchemist_display_recipe(int tval, int sval, int ego);
+extern void alchemist_recipe_book(void);
+extern int alchemist_recipe_select(int *tval, int sval, int ego, bool recipe);
+extern int alchemist_learn_object(object_type *o_ptr);
+extern void alchemist_gain_level(int lev);
+extern void do_cmd_alchemist(void);
+extern void do_cmd_beastmaster(void);
+extern void do_cmd_powermage(void);
+extern void do_cmd_possessor(void);
+extern void do_cmd_archer(void);
+extern void do_cmd_set_piercing(void);
+extern void do_cmd_necromancer(void);
+extern void do_cmd_unbeliever(void);
+extern void cast_daemon_spell(int spell, byte level);
+extern void do_cmd_unbeliever(void);
+extern void do_cmd_runecrafter(void);
+extern void do_cmd_symbiotic(void);
+extern s32b sroot(s32b n);
+
+/* dungeon.c */
+extern byte value_check_aux1(object_type *o_ptr);
+extern byte value_check_aux1_magic(object_type *o_ptr);
+extern byte value_check_aux2(object_type *o_ptr);
+extern byte value_check_aux2_magic(object_type *o_ptr);
+extern byte select_sense(object_type *o_ptr, bool ok_combat, bool ok_magic);
+extern void play_game(bool new_game);
+extern bool psychometry(void);
+
+/* files.c */
+extern void html_screenshot(cptr name);
+extern void help_file_screenshot(cptr name);
+extern void player_flags(u32b* f1, u32b* f2, u32b* f3, u32b* f4, u32b* f5, u32b* esp);
+extern void wipe_saved(void);
+extern void safe_setuid_drop(void);
+extern void safe_setuid_grab(void);
+extern s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2);
+extern void display_player(int mode);
+extern cptr describe_player_location(void);
+extern errr file_character(cptr name, bool full);
+extern errr process_pref_file_aux(char *buf);
+extern errr process_pref_file(cptr name);
+extern errr check_time_init(void);
+extern errr check_load_init(void);
+extern errr check_time(void);
+extern errr check_load(void);
+extern void read_times(void);
+extern bool txt_to_html(cptr head, cptr food, cptr base, cptr ext, bool force, bool recur);
+extern bool show_file(cptr name, cptr what, int line, int mode);
+extern void do_cmd_help(void);
+extern void process_player_base(void);
+extern void process_player_name(bool sf);
+extern void get_name(void);
+extern void do_cmd_suicide(void);
+extern void do_cmd_save_game(void);
+extern long total_points(void);
+extern void display_scores(int from, int to);
+extern errr predict_score(void);
+extern void close_game(void);
+extern void exit_game_panic(void);
+extern void signals_ignore_tstp(void);
+extern void signals_handle_tstp(void);
+extern void signals_init(void);
+extern errr get_rnd_line(char * file_name, char * output);
+extern char *get_line(char* fname, cptr fdir, char *linbuf, int line);
+extern void do_cmd_knowledge_corruptions(void);
+extern void race_legends(void);
+extern void show_highclass(int building);
+extern errr get_xtra_line(char * file_name, monster_type *m_ptr, char * output);
+
+/* gen_maze.c */
+extern bool level_generate_maze(cptr name);
+
+/* gen_life.c */
+extern bool level_generate_life(cptr name);
+extern void evolve_level(bool noise);
+
+/* generate.c */
+extern bool new_player_spot(int branch);
+extern void add_level_generator(cptr name, bool (*generator)(cptr), bool stairs, bool monsters, bool objects, bool miscs);
+extern bool level_generate_dungeon(cptr name);
+extern bool generate_fracave(int y0, int x0,int xsize,int ysize,int cutoff,bool light,bool room);
+extern void generate_hmap(int y0, int x0,int xsiz,int ysiz,int grd,int roug,int cutoff);
+extern bool room_alloc(int x,int y,bool crowded,int by0,int bx0,int *xx,int *yy);
+extern void generate_grid_mana(void);
+extern byte calc_dungeon_type(void);
+extern void generate_cave(void);
+extern void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info);
+
+
+/* wild.c */
+extern int generate_area(int y, int x, bool border, bool corner, bool refresh);
+extern void wilderness_gen(int refresh);
+extern void wilderness_gen_small(void);
+extern void reveal_wilderness_around_player(int y, int x, int h, int w);
+extern void town_gen(int t_idx);
+
+
+/* init1.c */
+extern int color_char_to_attr(char c);
+extern byte conv_color[16];
+extern errr init_player_info_txt(FILE *fp, char *buf);
+extern errr init_ab_info_txt(FILE *fp, char *buf);
+extern errr init_s_info_txt(FILE *fp, char *buf);
+extern errr init_set_info_txt(FILE *fp, char *buf);
+extern errr init_v_info_txt(FILE *fp, char *buf, bool start);
+extern errr init_f_info_txt(FILE *fp, char *buf);
+extern errr init_k_info_txt(FILE *fp, char *buf);
+extern errr init_a_info_txt(FILE *fp, char *buf);
+extern errr init_al_info_txt(FILE *fp, char *buf);
+extern errr init_ra_info_txt(FILE *fp, char *buf);
+extern errr init_e_info_txt(FILE *fp, char *buf);
+extern errr init_r_info_txt(FILE *fp, char *buf);
+extern errr init_re_info_txt(FILE *fp, char *buf);
+extern errr grab_one_dungeon_flag(u32b *flags1, u32b *flags2, cptr what);
+extern errr init_d_info_txt(FILE *fp, char *buf);
+extern errr init_t_info_txt(FILE *fp, char *buf);
+extern errr init_ba_info_txt(FILE *fp, char *buf);
+extern errr init_st_info_txt(FILE *fp, char *buf);
+extern errr init_ow_info_txt(FILE *fp, char *buf);
+extern errr init_wf_info_txt(FILE *fp, char *buf);
+extern bool process_dungeon_file_full;
+extern errr process_dungeon_file(cptr full_text, cptr name, int *yval, int *xval, int ymax, int xmax, bool init);
+
+/* init2.c */
+extern void init_corruptions(s16b new_size);
+extern void init_spells(s16b new_size);
+extern void init_schools(s16b new_size);
+extern void reinit_gods(s16b new_size);
+extern void reinit_quests(s16b new_size);
+extern void reinit_powers_type(s16b new_size);
+extern void create_stores_stock(int t);
+extern errr init_v_info(void);
+extern void init_file_paths(char *path);
+extern void init_angband(void);
+extern errr init_buildings(void);
+#ifdef ALLOW_TEMPLATES
+extern s16b error_idx;
+extern s16b error_line;
+extern u32b fake_name_size;
+extern u32b fake_text_size;
+#endif /* ALLOW_TEMPLATES */
+
+/* loadsave.c */
+extern void register_savefile(int num);
+extern bool file_exist(char *buf);
+extern s16b rd_variable(void);
+extern void wr_variable(s16b *var);
+extern void wr_scripts(void);
+extern bool load_dungeon(char *ext);
+extern void save_dungeon(void);
+extern bool save_player(void);
+extern bool load_player(void);
+extern errr rd_savefile_new(void);
+extern void load_number_key(char *key, u32b *val);
+extern void save_number_key(char *key, u32b val);
+
+/* melee1.c */
+/* melee2.c */
+extern int monst_spell_monst_spell;
+extern bool mon_take_hit_mon(int s_idx, int m_idx, int dam, bool *fear, cptr note);
+extern int check_hit2(int power, int level, int ac);
+extern bool carried_make_attack_normal(int r_idx);
+extern bool make_attack_normal(int m_idx, byte divis);
+extern bool make_attack_spell(int m_idx);
+extern void process_monsters(void);
+extern void curse_equipment(int chance, int heavy_chance);
+extern void curse_equipment_dg(int chance, int heavy_chance);
+
+/* monster1.c */
+extern void screen_roff(int r_idx, int ego, int remember);
+extern void display_roff(int r_idx, int ego);
+extern void monster_description_out(int r_idx, int ego);
+
+/* monster2.c */
+extern void monster_set_level(int m_idx, int level);
+extern s32b modify_aux(s32b a, s32b b, char mod);
+extern void monster_msg(cptr fmt, ...);
+extern void cmonster_msg(char a, cptr fmt, ...);
+extern bool mego_ok(int r_idx, int ego);
+extern void monster_check_experience(int m_idx, bool silent);
+extern void monster_gain_exp(int m_idx, u32b exp, bool silent);
+extern monster_race* race_info_idx(int r_idx, int ego);
+extern int get_wilderness_flag(void);
+extern void sanity_blast(monster_type * m_ptr, bool necro);
+extern void delete_monster_idx(int i);
+extern void delete_monster(int y, int x);
+extern void compact_monsters(int size);
+extern void wipe_m_list(void);
+extern s16b m_pop(void);
+extern errr get_mon_num_prep(void);
+extern s16b get_mon_num(int level);
+extern void monster_desc(char *desc, monster_type *m_ptr, int mode);
+extern void monster_race_desc(char *desc, int r_idx, int ego);
+extern void lore_do_probe(int m_idx);
+extern void lore_treasure(int m_idx, int num_item, int num_gold);
+extern void update_mon(int m_idx, bool full);
+extern void update_monsters(bool full);
+extern void monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr);
+extern bool bypass_r_ptr_max_num ;
+extern bool place_monster_aux(int y, int x, int r_idx, bool slp, bool grp, int status);
+extern bool place_monster(int y, int x, bool slp, bool grp);
+extern bool alloc_horde(int y, int x);
+extern bool alloc_monster(int dis, bool slp);
+extern bool summon_specific_okay(int r_idx);
+extern int summon_specific_level;
+extern bool summon_specific(int y1, int x1, int lev, int type);
+extern void monster_swap(int y1, int x1, int y2, int x2);
+extern bool multiply_monster(int m_idx, bool charm, bool clone);
+extern bool hack_message_pain_may_silent;
+extern void message_pain(int m_idx, int dam);
+extern void update_smart_learn(int m_idx, int what);
+extern bool summon_specific_friendly(int y1, int x1, int lev, int type, bool Group_ok);
+extern bool place_monster_one_no_drop;
+extern monster_race *place_monster_one_race;
+extern s16b place_monster_one(int y, int x, int r_idx, int ego, bool slp, int status);
+extern s16b player_place(int y, int x);
+extern void monster_drop_carried_objects(monster_type *m_ptr);
+extern bool monster_dungeon(int r_idx);
+extern bool monster_quest(int r_idx);
+extern bool monster_ocean(int r_idx);
+extern bool monster_shore(int r_idx);
+extern bool monster_town(int r_idx);
+extern bool monster_wood(int r_idx);
+extern bool monster_volcano(int r_idx);
+extern bool monster_mountain(int r_idx);
+extern bool monster_grass(int r_idx);
+extern bool monster_deep_water(int r_idx);
+extern bool monster_shallow_water(int r_idx);
+extern bool monster_lava(int r_idx);
+extern void set_mon_num_hook(void);
+extern void set_mon_num2_hook(int y, int x);
+extern bool monster_can_cross_terrain(byte feat, monster_race *r_ptr);
+extern void corrupt_corrupted(void);
+
+/* monster3.c */
+extern void do_cmd_companion(void);
+extern bool do_control_reconnect(void);
+extern bool do_control_drop(void);
+extern bool do_control_magic(void);
+extern bool do_control_pickup(void);
+extern bool do_control_inven(void);
+extern bool do_control_walk(void);
+extern bool can_create_companion(void);
+extern void ai_deincarnate(int m_idx);
+extern bool ai_possessor(int m_idx, int o_idx);
+extern bool ai_multiply(int m_idx);
+extern bool change_side(monster_type *m_ptr);
+extern int is_friend(monster_type *m_ptr);
+extern bool is_enemy(monster_type *m_ptr, monster_type *t_ptr);
+
+/* object1.c */
+/* object2.c */
+extern byte get_item_letter_color(object_type *o_ptr);
+extern void describe_device(object_type *o_ptr);
+extern void object_pickup(int this_o_idx);
+extern int get_slot(int slot);
+extern bool apply_flags_set(s16b a_idx, s16b set_idx, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern bool apply_set(s16b a_idx, s16b set_idx);
+extern bool takeoff_set(s16b a_idx, s16b set_idx);
+extern bool wield_set(s16b a_idx, s16b set_idx, bool silent);
+extern object_type *get_object(int item);
+extern s32b calc_total_weight(void);
+extern void add_random_ego_flag(object_type *o_ptr, int fego, bool *limit_blows);
+extern bool info_spell;
+extern char spell_txt[50];
+extern bool grab_tval_desc(int tval);
+extern void init_match_theme(obj_theme theme);
+extern bool kind_is_artifactable(int k_idx);
+extern bool kind_is_good(int k_idx);
+extern int kind_is_legal_special;
+extern bool kind_is_legal(int k_idx);
+extern bool verify(cptr prompt, int item);
+extern void flavor_init(void);
+extern void reset_visuals(void);
+extern int object_power(object_type *o_ptr);
+extern bool object_flags_no_set;
+extern void object_flags(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern void object_flags_known(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern void object_desc(char *buf, object_type *o_ptr, int pref, int mode);
+extern void object_desc_store(char *buf, object_type *o_ptr, int pref, int mode);
+extern bool object_out_desc(object_type *o_ptr, FILE *fff, bool trim_down, bool wait_for_it);
+extern char index_to_label(int i);
+extern s16b label_to_inven(int c);
+extern s16b label_to_equip(int c);
+extern s16b wield_slot_ideal(object_type *o_ptr, bool ideal);
+extern s16b wield_slot(object_type *o_ptr);
+extern cptr mention_use(int i);
+extern cptr describe_use(int i);
+extern void inven_item_charges(int item);
+extern void inven_item_describe(int item);
+extern void inven_item_increase(int item, int num);
+extern bool inven_item_optimize(int item);
+extern void floor_item_charges(int item);
+extern void floor_item_describe(int item);
+extern void floor_item_increase(int item, int num);
+extern void floor_item_optimize(int item);
+extern bool inven_carry_okay(object_type *o_ptr);
+extern s16b inven_carry(object_type *o_ptr, bool final);
+extern s16b inven_takeoff(int item, int amt, bool force_drop);
+extern void inven_drop(int item, int amt, int dy, int dx, bool silent);
+extern bool item_tester_okay(object_type *o_ptr);
+extern void display_inven(void);
+extern void display_equip(void);
+extern void show_inven(bool mirror);
+extern void show_equip(bool mirror);
+extern void toggle_inven_equip(void);
+extern bool (*get_item_extra_hook)(int *cp);
+extern bool get_item(int *cp, cptr pmt, cptr str, int mode);
+extern void excise_object_idx(int o_idx);
+extern void delete_object_idx(int o_idx);
+extern void delete_object(int y, int x);
+extern void compact_objects(int size);
+extern void wipe_o_list(void);
+extern s16b o_pop(void);
+extern errr get_obj_num_prep(void);
+extern s16b get_obj_num(int level);
+extern void object_known(object_type *o_ptr);
+extern void object_aware(object_type *o_ptr);
+extern void object_tried(object_type *o_ptr);
+extern s32b object_value(object_type *o_ptr);
+extern s32b object_value_real(object_type *o_ptr);
+extern bool object_similar(object_type *o_ptr, object_type *j_ptr);
+extern void object_absorb(object_type *o_ptr, object_type *j_ptr);
+extern s16b lookup_kind(int tval, int sval);
+extern void object_wipe(object_type *o_ptr);
+extern void object_prep(object_type *o_ptr, int k_idx);
+extern void object_copy(object_type *o_ptr, object_type *j_ptr);
+extern int hack_apply_magic_power;
+extern void apply_magic(object_type *o_ptr, int lev, bool okay, bool good, bool great);
+extern bool make_object(object_type *j_ptr, bool good, bool great, obj_theme theme);
+extern void place_object(int y, int x, bool good, bool great, int where);
+extern bool make_gold(object_type *j_ptr);
+extern void place_gold(int y, int x);
+extern void process_objects(void);
+extern s16b drop_near(object_type *o_ptr, int chance, int y, int x);
+extern void acquirement(int y1, int x1, int num, bool great, bool known);
+extern void pick_trap(int y, int x);
+extern cptr item_activation(object_type *o_ptr,byte num);
+extern void combine_pack(void);
+extern void reorder_pack(void);
+extern void display_koff(int k_idx);
+extern void random_artifact_resistance (object_type * o_ptr);
+extern void random_resistance (object_type * o_ptr, bool is_scroll, int specific);
+extern s16b floor_carry(int y, int x, object_type *j_ptr);
+extern void pack_decay(int item);
+extern void floor_decay(int item);
+extern bool scan_floor(int *items, int *item_num, int y, int x, int mode);
+extern void show_floor(int y, int x);
+extern bool get_item_floor(int *cp, cptr pmt, cptr str, int mode);
+extern void py_pickup_floor(int pickup);
+extern s16b m_bonus(int max, int level);
+extern void object_gain_level(object_type *o_ptr);
+extern void gain_flag_group_flag(object_type *o_ptr, bool silent);
+extern void gain_flag_group(object_type *o_ptr, bool silent);
+extern void get_table_name(char * out_string);
+extern s32b flag_cost(object_type * o_ptr, int plusses);
+
+/* powers.c */
+extern void do_cmd_power(void);
+
+/* traps.c */
+extern bool player_activate_trap_type(s16b y, s16b x, object_type *i_ptr, s16b item);
+extern void player_activate_door_trap(s16b y, s16b x);
+extern void place_trap(int y, int x);
+extern void place_trap_object(object_type *o_ptr);
+extern void wiz_place_trap(int y, int x, int idx);
+extern void do_cmd_set_trap(void);
+extern bool mon_hit_trap(int);
+
+/* spells1.c */
+extern byte spell_color(int type);
+extern s16b poly_r_idx(int r_idx);
+extern void get_pos_player(int dis, int *ny, int *nx);
+extern bool teleport_player_bypass;
+extern void teleport_to_player(int m_idx);
+extern void teleport_player_directed(int rad, int dir);
+extern void teleport_away(int m_idx, int dis);
+extern void teleport_player(int dis);
+extern void teleport_player_to(int ny, int nx);
+extern void teleport_monster_to(int m_idx, int ny, int nx);
+extern void teleport_player_level(void);
+extern void recall_player(int d, int f);
+extern void take_hit(int damage, cptr kb_str);
+extern void take_sanity_hit(int damage, cptr hit_from);
+extern void acid_dam(int dam, cptr kb_str);
+extern void elec_dam(int dam, cptr kb_str);
+extern void fire_dam(int dam, cptr kb_str);
+extern void cold_dam(int dam, cptr kb_str);
+extern bool inc_stat(int stat);
+extern bool dec_stat(int stat, int amount, int mode);
+extern bool res_stat(int stat, bool full);
+extern bool apply_disenchant(int mode);
+extern bool project_m(int who, int r, int y, int x, int dam, int typ);
+extern sint project_path(u16b *gp, int range, int y1, int x1, int y2, int x2, int flg);
+extern bool project(int who, int rad, int y, int x, int dam, int typ, int flg);
+extern bool potion_smash_effect(int who, int y, int x, int o_sval);
+extern void do_poly_self(void);
+extern void corrupt_player(void);
+extern void generate_spell(int plev);
+extern bool unsafe;
+extern void describe_attack_fully(int type, char* r);
+extern s16b do_poly_monster(int y, int x);
+
+
+/* spells2.c */
+extern bool remove_curse_object(object_type *o_ptr, bool all);
+extern void curse_artifact(object_type * o_ptr);
+extern void grow_things(s16b type, int rad);
+extern void grow_grass(int rad);
+extern void grow_trees(int rad);
+extern bool hp_player(int num);
+extern bool heal_insanity(int val);
+extern void warding_glyph(void);
+extern void explosive_rune(void);
+extern bool do_dec_stat(int stat, int mode);
+extern bool do_res_stat(int stat, bool full);
+extern bool do_inc_stat(int stat);
+extern void identify_pack(void);
+extern void identify_pack_fully(void);
+extern bool remove_curse(void);
+extern bool remove_all_curse(void);
+extern bool restore_level(void);
+extern void self_knowledge(FILE *fff);
+extern bool lose_all_info(void);
+extern bool detect_traps(int rad);
+extern bool detect_doors(int rad);
+extern bool detect_stairs(int rad);
+extern bool detect_treasure(int rad);
+extern bool hack_no_detect_message;
+extern bool detect_objects_gold(int rad);
+extern bool detect_objects_normal(int rad);
+extern bool detect_objects_magic(int rad);
+extern bool detect_monsters_normal(int rad);
+extern bool detect_monsters_invis(int rad);
+extern bool detect_monsters_evil(int rad);
+extern bool detect_monsters_good(int rad);
+extern bool detect_monsters_xxx(u32b match_flag, int rad);
+extern bool detect_monsters_string(cptr chars, int rad);
+extern bool detect_monsters_nonliving(int rad);
+extern bool detect_all(int rad);
+extern void stair_creation(void);
+extern bool wall_stone(int y, int x);
+extern bool enchant(object_type *o_ptr, int n, int eflag);
+extern bool enchant_spell(int num_hit, int num_dam, int num_ac, int num_pval);
+extern bool ident_spell(void);
+extern bool ident_all(void);
+extern bool identify_fully(void);
+extern bool recharge(int num);
+extern bool speed_monsters(void);
+extern bool slow_monsters(void);
+extern bool sleep_monsters(void);
+extern bool conf_monsters(void);
+extern void aggravate_monsters(int who);
+extern bool genocide_aux(bool player_cast, char typ);
+extern bool genocide(bool player_cast);
+extern bool mass_genocide(bool player_cast);
+extern void do_probe(int m_idx);
+extern bool probing(void);
+extern void change_wild_mode(void);
+extern bool banish_evil(int dist);
+extern bool dispel_evil(int dam);
+extern bool dispel_good(int dam);
+extern bool dispel_undead(int dam);
+extern bool dispel_monsters(int dam);
+extern bool dispel_living(int dam);
+extern bool dispel_demons(int dam);
+extern bool turn_undead(void);
+extern void wipe(int y1, int x1, int r);
+extern void destroy_area(int y1, int x1, int r, bool full, bool bypass);
+extern void earthquake(int cy, int cx, int r);
+extern void lite_room(int y1, int x1);
+extern void unlite_room(int y1, int x1);
+extern bool lite_area(int dam, int rad);
+extern bool unlite_area(int dam, int rad);
+extern bool fire_ball_beam(int typ, int dir, int dam, int rad);
+extern bool fire_cloud(int typ, int dir, int dam, int rad, int time);
+extern bool fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff);
+extern bool fire_wall(int typ, int dir, int dam, int time);
+extern bool fire_ball(int typ, int dir, int dam, int rad);
+extern bool fire_bolt(int typ, int dir, int dam);
+extern bool fire_beam(int typ, int dir, int dam);
+extern bool fire_druid_ball(int typ, int dir, int dam, int rad);
+extern bool fire_druid_bolt(int typ, int dir, int dam);
+extern bool fire_druid_beam(int typ, int dir, int dam);
+extern void call_chaos(void);
+extern bool fire_bolt_or_beam(int prob, int typ, int dir, int dam);
+extern bool lite_line(int dir);
+extern bool drain_life(int dir, int dam);
+extern bool death_ray(int dir, int plev);
+extern bool wall_to_mud(int dir);
+extern bool destroy_door(int dir);
+extern bool disarm_trap(int dir);
+extern bool wizard_lock(int dir);
+extern bool heal_monster(int dir);
+extern bool speed_monster(int dir);
+extern bool slow_monster(int dir);
+extern bool sleep_monster(int dir);
+extern bool stasis_monster(int dir);
+extern bool confuse_monster(int dir, int plev);
+extern bool stun_monster(int dir, int plev);
+extern bool fear_monster(int dir, int plev);
+extern bool scare_monsters(void);
+extern bool poly_monster(int dir);
+extern bool clone_monster(int dir);
+extern bool teleport_monster(int dir);
+extern bool door_creation(void);
+extern bool trap_creation(void);
+extern bool glyph_creation(void);
+extern bool destroy_doors_touch(void);
+extern bool destroy_traps_touch(void);
+extern bool sleep_monsters_touch(void);
+extern bool alchemy(void);
+extern void activate_ty_curse(void);
+extern void activate_dg_curse(void);
+extern void activate_hi_summon(void);
+extern void summon_cyber(void);
+extern void wall_breaker(void);
+extern void bless_weapon(void);
+extern bool confuse_monsters(int dam);
+extern bool charm_monsters(int dam);
+extern bool charm_animals(int dam);
+extern bool charm_demons(int dam);
+extern bool stun_monsters(int dam);
+extern bool stasis_monsters(int dam);
+extern bool banish_monsters(int dist);
+extern bool turn_monsters(int dam);
+extern bool turn_evil(int dam);
+extern bool deathray_monsters(void);
+extern bool charm_monster(int dir, int plev);
+extern bool star_charm_monster(int dir, int plev);
+extern bool control_one_undead(int dir, int plev);
+extern bool charm_animal(int dir, int plev);
+extern bool mindblast_monsters(int dam);
+extern void alter_reality(void);
+extern void report_magics(void);
+extern void teleport_swap(int dir);
+extern void swap_position(int lty, int ltx);
+extern bool item_tester_hook_recharge(object_type *o_ptr);
+extern bool fire_explosion(int y, int x, int typ, int rad, int dam);
+extern bool fire_godly_wrath(int y, int x, int typ, int dir, int dam);
+extern bool invoke(int dam, int typ);
+extern bool project_hack(int typ, int dam);
+extern void project_meteor(int radius, int typ, int dam, u32b flg);
+extern bool item_tester_hook_artifactable(object_type *o_ptr);
+extern bool passwall(int dir, bool safe);
+extern bool project_hook(int typ, int dir, int dam, int flg);
+extern void random_misc (object_type * o_ptr, bool is_scroll);
+extern void random_plus(object_type * o_ptr, bool is_scroll);
+extern bool reset_recall(bool no_trepas_max_depth);
+extern void remove_dg_curse(void);
+
+/* randart.c */
+extern int get_activation_power(void);
+extern void build_prob(cptr learn);
+extern bool create_artifact(object_type *o_ptr, bool a_scroll, bool get_name);
+extern bool artifact_scroll(void);
+
+/* store.c */
+extern bool is_blessed(object_type *o_ptr);
+extern void do_cmd_store(void);
+extern void store_shuffle(int which);
+extern void store_maint(int town_num, int store_num);
+extern void store_init(int town_num, int store_num);
+extern void move_to_black_market(object_type * o_ptr);
+extern void do_cmd_home_trump(void);
+extern void store_sell(void);
+extern void store_purchase(void);
+extern void store_examine(void);
+extern void store_stole(void);
+extern void store_prt_gold(void);
+extern void store_request_item(void);
+
+/* bldg.c -KMW- */
+extern bool bldg_process_command(store_type *s_ptr, int i);
+extern void show_building(store_type *s_ptr);
+extern bool is_state(store_type *s_ptr, int state);
+extern void do_cmd_bldg(void);
+extern bool show_god_info(bool ext);
+extern void enter_quest(void);
+extern void select_bounties(void);
+
+/* util.c */
+extern void scansubdir(cptr dir);
+extern s32b rescale(s32b x, s32b max, s32b new_max);
+extern bool input_box(cptr text, int y, int x, char *buf, int max);
+extern void draw_box(int y, int x, int h, int w);
+extern void display_list(int y, int x, int h, int w, cptr title, cptr *list, int max, int begin, int sel, byte sel_color);
+extern s32b value_scale(int value, int vmax, int max, int min);
+extern int ask_menu(cptr ask, char **items, int max);
+extern cptr get_player_race_name(int pr, int ps);
+extern cptr get_month_name(int month, bool full, bool compact);
+extern cptr get_day(int day);
+extern s32b bst(s32b what, s32b t);
+extern errr path_parse(char *buf, int max, cptr file);
+extern errr path_temp(char *buf, int max);
+extern errr path_build(char *buf, int max, cptr path, cptr file);
+extern FILE *my_fopen(cptr file, cptr mode);
+extern errr my_str_fgets(cptr full_text, char *buf, huge n);
+extern errr my_fgets(FILE *fff, char *buf, huge n);
+extern errr my_fputs(FILE *fff, cptr buf, huge n);
+extern errr my_fclose(FILE *fff);
+extern errr fd_kill(cptr file);
+extern errr fd_move(cptr file, cptr what);
+extern errr fd_copy(cptr file, cptr what);
+extern int fd_make(cptr file, int mode);
+extern int fd_open(cptr file, int flags);
+extern errr fd_lock(int fd, int what);
+extern errr fd_seek(int fd, huge n);
+extern errr fd_chop(int fd, huge n);
+extern errr fd_read(int fd, char *buf, huge n);
+extern errr fd_write(int fd, cptr buf, huge n);
+extern errr fd_close(int fd);
+extern void flush(void);
+extern void bell(void);
+extern void sound(int num);
+extern void move_cursor(int row, int col);
+extern void text_to_ascii(char *buf, cptr str);
+extern void ascii_to_text(char *buf, cptr str);
+extern void keymap_init(void);
+extern errr macro_add(cptr pat, cptr act);
+extern sint macro_find_exact(cptr pat);
+extern char inkey(void);
+extern cptr quark_str(s16b num);
+extern s16b quark_add(cptr str);
+extern s16b message_num(void);
+extern cptr message_str(int age);
+extern byte message_color(int age);
+extern byte message_type(int age);
+extern void message_add(byte type, cptr msg, byte color);
+extern void display_message(int x, int y, int split, byte color, cptr t);
+extern void cmsg_print(byte color, cptr msg);
+extern void msg_print(cptr msg);
+extern void cmsg_format(byte color, cptr fmt, ...);
+extern void msg_format(cptr fmt, ...);
+extern void screen_save(void);
+extern void screen_load(void);
+extern void c_put_str(byte attr, cptr str, int row, int col);
+extern void put_str(cptr str, int row, int col);
+extern void c_prt(byte attr, cptr str, int row, int col);
+extern void prt(cptr str, int row, int col);
+extern void text_out_to_screen(byte a, cptr str);
+extern void text_out_to_file(byte a, cptr str);
+extern void text_out(cptr str);
+extern void text_out_c(byte a, cptr str);
+extern void clear_screen(void);
+extern void clear_from(int row);
+extern bool askfor_aux_complete;
+extern bool askfor_aux(char *buf, int len);
+extern bool get_string(cptr prompt, char *buf, int len);
+extern bool get_check(cptr prompt);
+extern bool get_com(cptr prompt, char *command);
+extern s32b get_quantity(cptr prompt, s32b max);
+extern void pause_line(int row);
+extern char request_command_ignore_keymaps[];
+extern bool request_command_inven_mode;
+extern void request_command(int shopping);
+extern bool is_a_vowel(int ch);
+extern int get_keymap_dir(char ch);
+extern byte count_bits(u32b array);
+extern void strlower(char *buf);
+extern int test_monster_name(cptr name);
+extern int test_mego_name(cptr name);
+extern int test_item_name(cptr name);
+extern char msg_box(cptr text, int y, int x);
+extern timer_type *new_timer(cptr callback, s32b delay);
+extern void del_timer(timer_type *t_ptr);
+
+/* main.c */
+#ifdef PRIVATE_USER_PATH
+extern bool private_check_user_directory(cptr dirpath);
+#endif
+
+/* xtra1.c */
+extern void fix_irc_message(void);
+extern void fix_message(void);
+extern void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval, s16b tval, s16b to_h, s16b to_d, s16b to_a);
+extern int luck(int min, int max);
+extern int weight_limit(void);
+extern bool calc_powers_silent;
+extern void cnv_stat(int i, char *out_val);
+extern s16b modify_stat_value(int value, int amount);
+extern void calc_hitpoints(void);
+extern void notice_stuff(void);
+extern void update_stuff(void);
+extern void redraw_stuff(void);
+extern void window_stuff(void);
+extern void handle_stuff(void);
+extern bool monk_empty_hands(void);
+extern bool monk_heavy_armor(void);
+extern bool beastmaster_whip(void);
+extern void calc_bonuses(bool silent);
+extern void calc_sanity(void);
+extern void gain_fate(byte fate);
+extern void fate_desc(char *desc, int fate);
+extern void dump_fates(FILE *OutFile);
+
+/* xtra2.c */
+extern int resolve_mimic_name(cptr name);
+extern void do_rebirth(void);
+extern cptr get_subrace_title(int racem);
+extern void set_subrace_title(int racem, cptr name);
+extern void switch_subrace(int racem, bool copy_old);
+extern void switch_class(int sclass);
+extern void switch_subclass(int sclass);
+extern void drop_from_wild(void);
+extern void clean_wish_name(char *buf, char *name);
+extern bool test_object_wish(char *name, object_type *o_ptr, object_type *forge, char *what);
+extern bool set_roots(int v, s16b ac, s16b dam);
+extern bool set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag);
+extern bool set_rush(int v);
+extern bool set_parasite(int v, int r);
+extern bool set_disrupt_shield(int v);
+extern bool set_prob_travel(int v);
+extern bool set_absorb_soul(int v);
+extern bool set_tim_breath(int v, bool magical);
+extern bool set_tim_deadly(int v);
+extern bool set_tim_res_time(int v);
+extern bool set_tim_reflect(int v);
+extern bool set_tim_thunder(int v, int p1, int p2);
+extern bool set_meditation(int v);
+extern bool set_strike(int v);
+extern bool set_walk_water(int v);
+extern bool set_tim_regen(int v, int p);
+extern bool set_tim_ffall(int v);
+extern bool set_tim_fly(int v);
+extern bool set_poison(int v);
+extern bool set_tim_fire_aura(int v);
+extern bool set_holy(int v);
+extern void set_grace(s32b v);
+extern bool set_mimic(int v, int p, int level);
+extern bool set_no_breeders(int v);
+extern bool set_invis(int v,int p);
+extern bool set_lite(int v);
+extern bool set_blind(int v);
+extern bool set_confused(int v);
+extern bool set_poisoned(int v);
+extern bool set_afraid(int v);
+extern bool set_paralyzed(int v);
+extern bool set_image(int v);
+extern bool set_fast(int v, int p);
+extern bool set_light_speed(int v);
+extern bool set_slow(int v);
+extern bool set_shield(int v, int p, s16b o, s16b d1, s16b d2);
+extern bool set_blessed(int v);
+extern bool set_hero(int v);
+extern bool set_shero(int v);
+extern bool set_protevil(int v);
+extern bool set_protgood(int v);
+extern bool set_protundead(int v);
+extern bool set_invuln(int v);
+extern bool set_tim_invis(int v);
+extern bool set_tim_infra(int v);
+extern bool set_mental_barrier(int v);
+extern bool set_oppose_acid(int v);
+extern bool set_oppose_elec(int v);
+extern bool set_oppose_fire(int v);
+extern bool set_oppose_cold(int v);
+extern bool set_oppose_pois(int v);
+extern bool set_oppose_ld(int v);
+extern bool set_oppose_cc(int v);
+extern bool set_oppose_ss(int v);
+extern bool set_oppose_nex(int v);
+extern bool set_stun(int v);
+extern bool set_cut(int v);
+extern bool set_food(int v);
+extern void check_experience(void);
+extern void check_experience_obj(object_type *o_ptr);
+extern void gain_exp(s32b amount);
+extern void lose_exp(s32b amount);
+extern int get_coin_type(monster_race *r_ptr);
+extern void monster_death(int m_idx);
+extern bool mon_take_hit(int m_idx, int dam, bool *fear, cptr note);
+extern bool change_panel(int dy, int dx);
+extern void verify_panel(void);
+extern void resize_map(void);
+extern void resize_window(void);
+extern cptr look_mon_desc(int m_idx);
+extern void ang_sort_aux(vptr u, vptr v, int p, int q);
+extern void ang_sort(vptr u, vptr v, int n);
+extern bool target_able(int m_idx);
+extern bool target_okay(void);
+extern bool target_set(int mode);
+extern bool get_aim_dir(int *dp);
+extern bool get_hack_dir(int *dp);
+extern bool get_rep_dir(int *dp);
+extern int get_chaos_patron(void);
+extern void gain_level_reward(int chosen_reward);
+extern bool set_shadow(int v);
+extern bool set_tim_esp(int v);
+extern bool tgp_pt(int *x, int * y);
+extern bool tgt_pt (int *x, int *y);
+extern bool gain_random_corruption(int choose_mut);
+extern bool got_corruptions(void);
+extern void dump_corruptions(FILE *OutFile, bool color);
+extern void do_poly_self(void);
+extern void do_poly_wounds(void);
+extern bool curse_weapon(void);
+extern bool curse_armor(void);
+extern void random_resistance(object_type * q_ptr, bool is_scroll, int specific);
+extern bool lose_corruption(int choose_mut);
+extern bool lose_all_corruptions(void);
+extern void great_side_effect(void);
+extern void nasty_side_effect(void);
+extern void deadly_side_effect(bool god);
+extern void godly_wrath_blast(void);
+extern int interpret_grace(void);
+extern int interpret_favor(void);
+extern void make_wish(void);
+extern bool set_sliding(s16b v);
+extern void create_between_gate(int dist, int y, int x);
+
+/* levels.c */
+extern bool get_dungeon_generator(char *buf);
+extern bool get_level_desc(char *buf);
+extern void get_level_flags(void);
+extern bool get_dungeon_name(char *buf);
+extern bool get_dungeon_special(char *buf);
+extern bool get_command(const char *file, char comm, char *param);
+extern int get_branch(void);
+extern int get_fbranch(void);
+extern int get_flevel(void);
+extern bool get_dungeon_save(char *buf);
+
+/* ghost.c */
+extern s16b place_ghost(void);
+extern void make_bones(void);
+
+
+/* wizard2.c */
+extern void do_cmd_wiz_cure_all(void);
+extern void do_cmd_wiz_named_friendly(int r_idx, bool slp);
+extern tval_desc2 tvals[];
+
+/* notes.c */
+extern void show_notes_file(void);
+extern void output_note(char *final_note);
+extern void add_note(char *note, char code);
+extern void add_note_type(int note_number);
+
+/* squeltch.c */
+extern void squeltch_inventory(void);
+extern void squeltch_grid(void);
+extern void do_cmd_automatizer(void);
+extern void automatizer_add_rule(object_type *o_ptr, bool destroy);
+extern bool automatizer_create;
+
+
+
+/*
+ * Hack -- conditional (or "bizarre") externs
+ */
+
+#ifdef SET_UID
+/* util.c */
+extern void user_name(char *buf, int id);
+#endif
+
+#ifndef HAS_MEMSET
+/* util.c */
+extern char *memset(char*, int, huge);
+#endif
+
+#ifndef HAS_STRICMP
+/* util.c */
+extern int stricmp(cptr a, cptr b);
+#endif
+
+#ifndef HAS_USLEEP
+/* util.c */
+extern int usleep(huge usecs);
+#endif
+
+#ifdef SUPPORT_GAMMA
+/* util.c */
+extern void build_gamma_table(int gamma);
+extern byte gamma_table[256];
+
+/* variable.c */
+extern u16b gamma_val;
+#endif
+
+#ifdef MACINTOSH
+/* main-mac.c */
+/* extern void main(void); */
+#endif
+
+#ifdef MACH_O_CARBON
+/* main-crb.c */
+extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype);
+extern u32b _fcreator;
+extern u32b _ftype;
+#endif /* MACH_O_CARBON */
+
+#ifdef WINDOWS
+/* main-win.c */
+/* extern int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, ...); */
+#endif
+
+#if !defined(WINDOWS) && !defined(MACINTOSH) && !defined(ACORN)
+/* files.c */
+extern bool chg_to_txt(cptr base, cptr newname);
+#endif /* !WINDOWS && !MACINTOSH && !ACORN */
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+/* util.c */
+extern void repeat_push(int what);
+extern bool repeat_pull(int *what);
+extern void repeat_check(void);
+extern void get_count(int number, int max);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+#ifdef ALLOW_EASY_OPEN /* TNB */
+
+/* variable.c */
+extern bool easy_open;
+extern bool easy_tunnel;
+
+/* cmd2.c */
+extern bool easy_open_door(int y, int x);
+
+#endif /* ALLOW_EASY_OPEN -- TNB */
+
+#ifdef ALLOW_EASY_DISARM /* TNB */
+
+/* variable.c */
+extern bool easy_disarm;
+
+/* cmd2.c */
+extern bool do_cmd_disarm_aux(int y, int x, int dir, int do_pickup);
+
+#endif /* ALLOW_EASY_DISARM -- TNB */
+
+extern bool easy_floor;
+
+
+extern void irc_poll(void);
+extern void irc_connect(void);
+extern void irc_emote(char *buf);
+extern void irc_chat(void);
+extern void irc_disconnect(void);
+extern void irc_disconnect_aux(char *str, bool message);
+extern void irc_quit(char *str);
+
+
+/* script.c */
+extern void init_lua(void);
+extern void init_lua_init(void);
+extern int exec_lua(char *file);
+extern cptr string_exec_lua(char *file);
+extern bool tome_dofile(char *file);
+extern bool tome_dofile_anywhere(cptr dir, char *file, bool test_exist);
+extern void dump_lua_stack(int min, int max);
+extern bool call_lua(cptr function, cptr args, cptr ret, ...);
+extern bool get_lua_var(cptr name, char type, void *arg);
+
+/* modules.c */
+extern void module_reset_dir(cptr dir, cptr new_path);
+extern cptr force_module;
+extern bool select_module(void);
+
+
+/* lua_bind.c */
+extern magic_power *grab_magic_power(magic_power *m_ptr, int num);
+extern bool get_magic_power(int *sn, magic_power *powers, int max_powers, void (*power_info)(char *p, int power), int plev, int cast_stat);
+extern magic_power *new_magic_power(int num);
+extern bool get_magic_power_lua(int *sn, magic_power *powers, int max_powers, char *info_fct, int plev, int cast_stat);
+extern bool lua_spell_success(magic_power *spell, int stat, char *oups_fct);
+
+extern object_type *new_object(void);
+extern void end_object(object_type *o_ptr);
+extern void lua_set_item_tester(int tval, char *fct);
+extern char *lua_object_desc(object_type *o_ptr, int pref, int mode);
+
+extern s16b add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff);
+
+extern void find_position(int y, int x, int *yy, int *xx);
+extern bool summon_lua_okay(int r_idx);
+extern bool lua_summon_monster(int y, int x, int lev, bool ffriend, char *fct);
+
+extern s16b add_new_quest(char *name);
+extern void desc_quest(int q_idx, int d, char *desc);
+
+extern s16b add_new_gods(char *name);
+extern void desc_god(int g_idx, int d, char *desc);
+
+extern bool get_com_lua(cptr promtp, int *com);
+
+extern s16b new_school(int i, cptr name, s16b skill);
+extern s16b new_spell(int i, cptr name);
+extern spell_type *grab_spell_type(s16b num);
+extern school_type *grab_school_type(s16b num);
+extern s32b lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus);
+extern s32b lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat);
+extern s32b lua_spell_device_chance(s32b chance, int level, int base_level);
+
+extern cave_type *lua_get_cave(int y, int x);
+extern void set_target(int y, int x);
+extern void get_target(int dir, int *y, int *x);
+
+extern void get_map_size(bool full_text, char *name, int *ysize, int *xsize);
+extern void load_map(bool full_text, char *name, int *y, int *x);
+extern bool alloc_room(int by0, int bx0, int ysize, int xsize, int *y1, int *x1, int *y2, int *x2);
+
+extern void lua_print_hook(cptr str);
+
+extern int lua_get_new_bounty_monster(int lev);
+
+extern char *lua_input_box(cptr title, int max);
+extern char lua_msg_box(cptr title);
+
+extern list_type *lua_create_list(int size);
+extern void lua_delete_list(list_type *, int size);
+extern void lua_add_to_list(list_type *, int idx, cptr str);
+extern void lua_display_list(int y, int x, int h, int w, cptr title, list_type *list, int max, int begin, int sel, byte sel_color);
+
+extern void lua_make_temp_file(void);
+extern void lua_close_temp_file(void);
+extern void lua_end_temp_file(void);
+extern cptr lua_get_temp_name(void);
+
+extern void add_scripted_generator(cptr name, bool stairs, bool monsters, bool objects, bool miscs);
+
+/* skills.c */
+extern void dump_skills(FILE *fff);
+extern s16b find_skill(cptr name);
+extern s16b find_skill_i(cptr name);
+extern s16b get_skill(int skill);
+extern s16b get_skill_scale(int skill, u32b scale);
+extern void do_cmd_skill(void);
+extern void do_cmd_activate_skill(void);
+extern s16b melee_skills[MAX_MELEE];
+extern char *melee_names[MAX_MELEE];
+extern s16b get_melee_skills(void);
+extern s16b get_melee_skill(void);
+extern bool forbid_gloves(void);
+extern bool forbid_non_blessed(void);
+extern int validate_autoskiller(s32b *ideal);
+extern void compute_skills(s32b *v, s32b *m, int i);
+extern void select_default_melee(void);
+extern void do_get_new_skill(void);
+extern void init_skill(s32b value, s32b mod, int i);
+extern s16b find_ability(cptr name);
+extern void dump_abilities(FILE *fff);
+extern void do_cmd_ability(void);
+extern bool has_ability(int ab);
+extern void apply_level_abilities(int level);
+extern void recalc_skills(bool init);
+
+/* gods.c */
+extern void inc_piety(int god, s32b amt);
+extern void abandon_god(int god);
+extern int wisdom_scale(int max);
+extern int find_god(cptr name);
+extern void follow_god(int god, bool silent);
diff --git a/src/files.c b/src/files.c
new file mode 100644
index 00000000..8c934c8c
--- /dev/null
+++ b/src/files.c
@@ -0,0 +1,7219 @@
+/* File: files.c */
+
+/* Purpose: code dealing with files (and death) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+static bool setuid_grabbed = TRUE;
+
+
+/*
+ * You may or may not want to use the following "#undef".
+ */
+/* #undef _POSIX_SAVED_IDS */
+
+
+/*
+ * Hack -- drop permissions
+ */
+void safe_setuid_drop(void)
+{
+ if (setuid_grabbed)
+ {
+ setuid_grabbed = FALSE;
+#ifdef SET_UID
+
+# ifdef SAFE_SETUID
+
+# ifdef SAFE_SETUID_POSIX
+
+ if (setuid(getuid()) != 0)
+ {
+ quit("setuid(): cannot set permissions correctly!");
+ }
+ if (setgid(getgid()) != 0)
+ {
+ quit("setgid(): cannot set permissions correctly!");
+ }
+
+# else
+
+ if (setreuid(geteuid(), getuid()) != 0)
+ {
+ quit("setreuid(): cannot set permissions correctly!");
+ }
+ if (setregid(getegid(), getgid()) != 0)
+ {
+ quit("setregid(): cannot set permissions correctly!");
+ }
+
+# endif
+
+# endif
+
+#endif
+ }
+
+}
+
+
+/*
+ * Hack -- grab permissions
+ */
+void safe_setuid_grab(void)
+{
+ if (!setuid_grabbed)
+ {
+ setuid_grabbed = TRUE;
+#ifdef SET_UID
+
+# ifdef SAFE_SETUID
+
+# ifdef SAFE_SETUID_POSIX
+
+ if (setuid(player_euid) != 0)
+ {
+ quit("setuid(): cannot set permissions correctly!");
+ }
+ if (setgid(player_egid) != 0)
+ {
+ quit("setgid(): cannot set permissions correctly!");
+ }
+
+# else
+
+ if (setreuid(geteuid(), getuid()) != 0)
+ {
+ quit("setreuid(): cannot set permissions correctly!");
+ }
+ if (setregid(getegid(), getgid()) != 0)
+ {
+ quit("setregid(): cannot set permissions correctly!");
+ }
+
+# endif /* SAFE_SETUID_POSIX */
+
+# endif /* SAFE_SETUID */
+
+#endif /* SET_UID */
+ }
+
+}
+
+
+/*
+ * Extract the first few "tokens" from a buffer
+ *
+ * This function uses "colon" and "slash" and delim arg as the delimeter characters.
+ *
+ * We never extract more than "num" tokens. The "last" token may include
+ * "delimeter" characters, allowing the buffer to include a "string" token.
+ *
+ * We save pointers to the tokens in "tokens", and return the number found.
+ *
+ * Hack -- Attempt to handle the 'c' character formalism
+ *
+ * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
+ *
+ * Hack -- We will always extract at least one token
+ */
+s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2)
+{
+ int i = 0;
+
+ char *s = buf;
+
+
+ /* Process */
+ while (i < num - 1)
+ {
+ char *t;
+
+ /* Scan the string */
+ for (t = s; *t; t++)
+ {
+ /* Found a delimiter */
+ if ((*t == delim1) || (*t == delim2)) break;
+
+ /* Handle single quotes */
+ if (*t == '\'')
+ {
+ /* Advance */
+ t++;
+
+ /* Handle backslash */
+ if (*t == '\\') t++;
+
+ /* Require a character */
+ if (!*t) break;
+
+ /* Advance */
+ t++;
+
+ /* Hack -- Require a close quote */
+ if (*t != '\'') *t = '\'';
+ }
+
+ /* Handle back-slash */
+ if (*t == '\\') t++;
+ }
+
+ /* Nothing left */
+ if (!*t) break;
+
+ /* Nuke and advance */
+ *t++ = '\0';
+
+ /* Save the token */
+ tokens[i++] = s;
+
+ /* Advance */
+ s = t;
+ }
+
+ /* Save the token */
+ tokens[i++] = s;
+
+ /* Number found */
+ return (i);
+}
+
+
+
+/*
+ * Parse a sub-file of the "extra info" (format shown below)
+ *
+ * Each "action" line has an "action symbol" in the first column,
+ * followed by a colon, followed by some command specific info,
+ * usually in the form of "tokens" separated by colons or slashes.
+ *
+ * Blank lines, lines starting with white space, and lines starting
+ * with pound signs ("#") are ignored (as comments).
+ *
+ * Note the use of "tokenize()" to allow the use of both colons and
+ * slashes as delimeters, while still allowing final tokens which
+ * may contain any characters including "delimiters".
+ *
+ * Note the use of "strtol()" to allow all "integers" to be encoded
+ * in decimal, hexidecimal, or octal form.
+ *
+ * Note that "monster zero" is used for the "player" attr/char, "object
+ * zero" will be used for the "stack" attr/char, and "feature zero" is
+ * used for the "nothing" attr/char.
+ *
+ * Parse another file recursively, see below for details
+ * %:<filename>
+ *
+ * Specify the attr/char values for "monsters" by race index
+ * R:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for "objects" by kind index
+ * K:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for "features" by feature index
+ * F:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for "stores" by store index
+ * B:<num>:<a>:<c>
+ *
+ * Specify the attr/char values for unaware "objects" by kind tval
+ * U:<tv>:<a>:<c>
+ *
+ * Specify the attr/char values for inventory "objects" by kind tval
+ * E:<tv>:<a>:<c>
+ *
+ * Define a macro action, given an encoded macro action
+ * A:<str>
+ *
+ * Create a normal macro, given an encoded macro trigger
+ * P:<str>
+ *
+ * Create a command macro, given an encoded macro trigger
+ * C:<str>
+ *
+ * Create a keyset mapping
+ * S:<key>:<key>:<dir>
+ *
+ * Turn an option off, given its name
+ * X:<str>
+ *
+ * Turn an option on, given its name
+ * Y:<str>
+ *
+ * Specify visual information, given an index, and some data
+ * V:<num>:<kv>:<rv>:<gv>:<bv>
+ *
+ * Specify squelch settings
+ * Q:<num>:<squelch>
+ */
+errr process_pref_file_aux(char *buf)
+{
+ int i, j, n1, n2;
+
+ char *zz[16];
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) return (0);
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) return (0);
+
+ /* Skip comments */
+ if (buf[0] == '#') return (0);
+
+ /* Require "?:*" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Process "%:<fname>" */
+ if (buf[0] == '%')
+ {
+ /* Attempt to Process the given file */
+ return (process_pref_file(buf + 2));
+ }
+
+
+ /* Process "R:<num>:<a>/<c>" -- attr/char for monster races */
+ if (buf[0] == 'R')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ monster_race *r_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_r_idx) return (1);
+ r_ptr = &r_info[i];
+ if (n1) r_ptr->x_attr = n1;
+ if (n2)
+ {
+ r_ptr->x_char = n2;
+ }
+ return (0);
+ }
+ }
+
+
+ /* Process "G:<type>:<num>:<a>/<c>" -- attr/char for overlay graphics */
+ if (buf[0] == 'G')
+ {
+ /* Process "G:M:<num>:<a>/<c>" -- attr/char for ego monsters */
+ if (buf[2] == 'M')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ monster_ego *re_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_re_idx) return (1);
+ re_ptr = &re_info[i];
+ if (n1) re_ptr->g_attr = n1;
+ if (n2)
+ {
+ re_ptr->g_char = n2;
+ }
+ return (0);
+ }
+ }
+
+ /* Process "G:P:<num>:<a>/<c>" -- attr/char for race modifiers */
+ if (buf[2] == 'P')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ player_race_mod *rmp_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_rmp_idx) return (1);
+ rmp_ptr = &race_mod_info[i];
+ if (n1) rmp_ptr->g_attr = n1;
+ if (n2)
+ {
+ rmp_ptr->g_char = n2;
+ }
+ return (0);
+ }
+ }
+
+ /* Process "G:T:<num>:<a>/<c>" -- attr/char for traps */
+ if (buf[2] == 'T')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ trap_type *t_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_t_idx) return (1);
+ t_ptr = &t_info[i];
+ if (n1) t_ptr->g_attr = n1;
+ if (n2)
+ {
+ t_ptr->g_char = n2;
+ }
+ return (0);
+ }
+ }
+ }
+
+
+ /* Process "K:<num>:<a>/<c>" -- attr/char for object kinds */
+ else if (buf[0] == 'K')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ object_kind *k_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_k_idx) return (1);
+ k_ptr = &k_info[i];
+ if (n1) k_ptr->x_attr = n1;
+ if (n2)
+ {
+ k_ptr->x_char = n2;
+ }
+ return (0);
+ }
+ }
+
+
+ /* Process "F:<num>:<a>/<c>" -- attr/char for terrain features */
+ else if (buf[0] == 'F')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ feature_type *f_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_f_idx) return (1);
+ f_ptr = &f_info[i];
+ if (n1) f_ptr->x_attr = n1;
+ if (n2)
+ {
+ f_ptr->x_char = n2;
+ }
+ return (0);
+ }
+ }
+
+ /* Process "B:<num>:<a>/<c>" -- attr/char for stores */
+ else if (buf[0] == 'B')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ store_info_type *st_ptr;
+ i = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ if (i >= max_st_idx) return (1);
+ st_ptr = &st_info[i];
+ if (n1) st_ptr->x_attr = n1;
+ if (n2) st_ptr->x_char = n2;
+ return (0);
+ }
+ }
+
+ /* Process "S:<num>:<a>/<c>" -- attr/char for special things */
+ else if (buf[0] == 'S')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ j = (byte)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ misc_to_attr[j] = n1;
+ misc_to_char[j] = n2;
+ return (0);
+ }
+ }
+
+ /* Process "U:<tv>:<a>/<c>" -- attr/char for unaware items */
+ else if (buf[0] == 'U')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ j = (huge)strtol(zz[0], NULL, 0);
+ n1 = strtol(zz[1], NULL, 0);
+ n2 = strtol(zz[2], NULL, 0);
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+ if (k_ptr->tval == j)
+ {
+ if (n1) k_ptr->d_attr = n1;
+ if (n2) k_ptr->d_char = n2;
+ }
+ }
+ return (0);
+ }
+ }
+
+
+ /* Process "E:<tv>:<a>" -- attribute for inventory objects */
+ else if (buf[0] == 'E')
+ {
+ if (tokenize(buf + 2, 2, zz, ':', '/') == 2)
+ {
+ j = (byte)strtol(zz[0], NULL, 0) % 128;
+ n1 = strtol(zz[1], NULL, 0);
+ if (n1) tval_to_attr[j] = n1;
+ return (0);
+ }
+ }
+
+
+ /* Process "A:<str>" -- save an "action" for later */
+ else if (buf[0] == 'A')
+ {
+ text_to_ascii(macro__buf, buf + 2);
+ return (0);
+ }
+
+ /* Process "P:<str>" -- normal macro */
+ else if (buf[0] == 'P')
+ {
+ char tmp[1024];
+ text_to_ascii(tmp, buf + 2);
+ macro_add(tmp, macro__buf);
+ return (0);
+ }
+
+ /* Process "L:<num>:<trigger>:<descr> -- extended command macro */
+ else if (buf[0] == 'L')
+ {
+ switch (tokenize(buf + 2, 3, zz, ':', 0))
+ {
+ case 3:
+ cli_add(zz[0], zz[1], zz[2]);
+ return 0;
+ case 2:
+ cli_add(zz[0], zz[1], 0);
+ return 0;
+ default:
+ return 1;
+ }
+ }
+
+ /* Process "C:<str>" -- create keymap */
+ else if (buf[0] == 'C')
+ {
+ int mode;
+
+ char tmp[1024];
+
+ if (tokenize(buf + 2, 2, zz, ':', '/') != 2) return (1);
+
+ mode = strtol(zz[0], NULL, 0);
+ if ((mode < 0) || (mode >= KEYMAP_MODES)) return (1);
+
+ text_to_ascii(tmp, zz[1]);
+ if (!tmp[0] || tmp[1]) return (1);
+ i = (byte)(tmp[0]);
+
+ string_free(keymap_act[mode][i]);
+
+ keymap_act[mode][i] = string_make(macro__buf);
+
+ return (0);
+ }
+
+
+ /* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- visual info */
+ else if (buf[0] == 'V')
+ {
+ if (tokenize(buf + 2, 5, zz, ':', '/') == 5)
+ {
+ i = (byte)strtol(zz[0], NULL, 0);
+ angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0);
+ angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0);
+ angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0);
+ angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0);
+ return (0);
+ }
+ }
+ /* set macro trigger names and a template */
+ /* Process "T:<trigger>:<keycode>:<shift-keycode>" */
+ /* Process "T:<template>:<modifier chr>:<modifier name>:..." */
+ else if (buf[0] == 'T')
+ {
+ int len, tok;
+ tok = tokenize(buf + 2, 2 + MAX_MACRO_MOD, zz, ':', '/');
+ if (tok >= 4)
+ {
+ int i;
+ int num;
+
+ if (macro_template != NULL)
+ {
+ free(macro_template);
+ macro_template = NULL;
+ for (i = 0; i < max_macrotrigger; i++)
+ free(macro_trigger_name[i]);
+ max_macrotrigger = 0;
+ }
+
+ if (*zz[0] == '\0') return 0; /* clear template */
+ num = strlen(zz[1]);
+ if (2 + num != tok) return 1; /* error */
+
+ len = strlen(zz[0]) + 1 + num + 1;
+ for (i = 0; i < num; i++)
+ len += strlen(zz[2 + i]) + 1;
+ macro_template = malloc(len);
+
+ strcpy(macro_template, zz[0]);
+ macro_modifier_chr =
+ macro_template + strlen(macro_template) + 1;
+ strcpy(macro_modifier_chr, zz[1]);
+ macro_modifier_name[0] =
+ macro_modifier_chr + strlen(macro_modifier_chr) + 1;
+ for (i = 0; i < num; i++)
+ {
+ strcpy(macro_modifier_name[i], zz[2 + i]);
+ macro_modifier_name[i + 1] = macro_modifier_name[i] +
+ strlen(macro_modifier_name[i]) + 1;
+ }
+ }
+ else if (tok >= 2)
+ {
+ int m;
+ char *t, *s;
+ if (max_macrotrigger >= MAX_MACRO_TRIG)
+ {
+ msg_print("Too many macro triggers!");
+ return 1;
+ }
+ m = max_macrotrigger;
+ max_macrotrigger++;
+
+ len = strlen(zz[0]) + 1 + strlen(zz[1]) + 1;
+ if (tok == 3)
+ len += strlen(zz[2]) + 1;
+ macro_trigger_name[m] = malloc(len);
+
+ t = macro_trigger_name[m];
+ s = zz[0];
+ while (*s)
+ {
+ if ('\\' == *s) s++;
+ *t++ = *s++;
+ }
+ *t = '\0';
+
+ macro_trigger_keycode[0][m] = macro_trigger_name[m] +
+ strlen(macro_trigger_name[m]) + 1;
+ strcpy(macro_trigger_keycode[0][m], zz[1]);
+ if (tok == 3)
+ {
+ macro_trigger_keycode[1][m] = macro_trigger_keycode[0][m] +
+ strlen(macro_trigger_keycode[0][m]) + 1;
+ strcpy(macro_trigger_keycode[1][m], zz[2]);
+ }
+ else
+ {
+ macro_trigger_keycode[1][m] = macro_trigger_keycode[0][m];
+ }
+ }
+ return 0;
+ }
+
+ /* Process "X:<str>" -- turn option off */
+ else if (buf[0] == 'X')
+ {
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ if (option_info[i].o_var &&
+ option_info[i].o_text &&
+ streq(option_info[i].o_text, buf + 2))
+ {
+ (*option_info[i].o_var) = FALSE;
+ return (0);
+ }
+ }
+ }
+
+ /* Process "Y:<str>" -- turn option on */
+ else if (buf[0] == 'Y')
+ {
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ if (option_info[i].o_var &&
+ option_info[i].o_text &&
+ streq(option_info[i].o_text, buf + 2))
+ {
+ (*option_info[i].o_var) = TRUE;
+ return (0);
+ }
+ }
+ }
+
+ /* Process "W:<win>:<flag>:<value>" -- window flags */
+ else if (buf[0] == 'W')
+ {
+ int win, flag, value;
+
+ if (tokenize(buf + 2, 3, zz, ':', '/') == 3)
+ {
+ win = strtol(zz[0], NULL, 0);
+ flag = strtol(zz[1], NULL, 0);
+ value = strtol(zz[2], NULL, 0);
+
+ /* Ignore illegal windows */
+ /* Hack -- Ignore the main window */
+ if ((win <= 0) || (win >= ANGBAND_TERM_MAX)) return (1);
+
+ /* Ignore illegal flags */
+ if ((flag < 0) || (flag >= 32)) return (1);
+
+ /* Require a real flag */
+ if (window_flag_desc[flag])
+ {
+ if (value)
+ {
+ /* Turn flag on */
+ window_flag[win] |= (1L << flag);
+ }
+ else
+ {
+ /* Turn flag off */
+ window_flag[win] &= ~(1L << flag);
+ }
+ }
+
+ /* Success */
+ return (0);
+ }
+ }
+
+ /* Process "Q:<num>:<squelch>" -- item squelch flags */
+ else if (buf[0] == 'Q')
+ {
+ /* This option isn't used anymore */
+ return (0);
+ }
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Helper function for "process_pref_file()"
+ *
+ * Input:
+ * v: output buffer array
+ * f: final character
+ *
+ * Output:
+ * result
+ */
+static cptr process_pref_file_expr(char **sp, char *fp)
+{
+ cptr v;
+
+ char *b;
+ char *s;
+
+ char b1 = '[';
+ char b2 = ']';
+
+ char f = ' ';
+
+ /* Initial */
+ s = (*sp);
+
+ /* Skip spaces */
+ while (isspace(*s)) s++;
+
+ /* Save start */
+ b = s;
+
+ /* Default */
+ v = "?o?o?";
+
+ /* Analyze */
+ if (*s == b1)
+ {
+ const char *p;
+ const char *t;
+
+ /* Skip b1 */
+ s++;
+
+ /* First */
+ t = process_pref_file_expr(&s, &f);
+
+ /* Oops */
+ if (!*t)
+ {
+ /* Nothing */
+ }
+
+ /* Function: IOR */
+ else if (streq(t, "IOR"))
+ {
+ v = "0";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t && !streq(t, "0")) v = "1";
+ }
+ }
+
+ /* Function: AND */
+ else if (streq(t, "AND"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t && streq(t, "0")) v = "0";
+ }
+ }
+
+ /* Function: NOT */
+ else if (streq(t, "NOT"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t && !streq(t, "0")) v = "0";
+ }
+ }
+
+ /* Function: EQU */
+ else if (streq(t, "EQU"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && !streq(p, t)) v = "0";
+ }
+ }
+
+ /* Function: LEQ */
+ else if (streq(t, "LEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) > 0)) v = "0";
+ }
+ }
+
+ /* Function: GEQ */
+ else if (streq(t, "GEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) < 0)) v = "0";
+ }
+ }
+
+ /* Function: LEQN */
+ else if (streq(t, "LEQN"))
+ {
+ int n = 0;
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ n = atoi(t);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (atoi(t) < n)) v = "0";
+ }
+ }
+
+ /* Function: GEQN */
+ else if (streq(t, "GEQN"))
+ {
+ int n = 0;
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ n = atoi(t);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_pref_file_expr(&s, &f);
+ if (*t && (atoi(t) > n)) v = "0";
+ }
+ }
+
+ /* Function SKILL */
+ else if (streq(t, "SKILL"))
+ {
+ static char skill_val[4*sizeof(int) + 1];
+ s16b skill = -1;
+ v = "0";
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ if (*t) skill = find_skill_i(t);
+ }
+ if (skill > 0)
+ {
+ sprintf(skill_val, "%d", (int)get_skill(skill));
+ v = skill_val;
+ }
+ }
+
+ /* Oops */
+ else
+ {
+ while (*s && (f != b2))
+ {
+ t = process_pref_file_expr(&s, &f);
+ }
+ }
+
+ /* Verify ending */
+ if (f != b2) v = "?x?x?";
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+ }
+
+ /* Other */
+ else
+ {
+ /* Accept all printables except spaces and brackets */
+ while (isprint(*s) && !strchr(" []", *s)) ++s;
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+
+ /* Variable */
+ if (*b == '$')
+ {
+ /* System */
+ if (streq(b + 1, "SYS"))
+ {
+ v = ANGBAND_SYS;
+ }
+
+ else if (streq(b + 1, "KEYBOARD"))
+ {
+ v = ANGBAND_KEYBOARD;
+ }
+
+ /* Graphics */
+ if (streq(b + 1, "GRAF"))
+ {
+ v = ANGBAND_GRAF;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACE"))
+ {
+ v = rp_ptr->title + rp_name;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACEMOD"))
+ {
+ v = rmp_ptr->title + rmp_name;
+ }
+
+ /* Class */
+ else if (streq(b + 1, "CLASS"))
+ {
+ v = spp_ptr->title + c_name;
+ }
+
+ /* Player */
+ else if (streq(b + 1, "PLAYER"))
+ {
+ v = player_base;
+ }
+ }
+
+ /* Constant */
+ else
+ {
+ v = b;
+ }
+ }
+
+ /* Save */
+ (*fp) = f;
+
+ /* Save */
+ (*sp) = s;
+
+ /* Result */
+ return (v);
+}
+
+
+
+
+/*
+ * Process the "user pref file" with the given name
+ *
+ * See the function above for a list of legal "commands".
+ *
+ * We also accept the special "?" and "%" directives, which
+ * allow conditional evaluation and filename inclusion.
+ */
+errr process_pref_file(cptr name)
+{
+ FILE *fp;
+
+ char buf[1024];
+
+ int num = -1;
+
+ errr err = 0;
+
+ bool bypass = FALSE;
+
+ /* Build the filename -- Allow users to override system pref files */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* No such file -- Try system pref file */
+ if (!fp)
+ {
+ /* Build the pathname, this time using the system pref directory */
+ path_build(buf, 1024, ANGBAND_DIR_PREF, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failed again */
+ if (!fp) return ( -1);
+ }
+
+
+ /* Process the file */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Count lines */
+ num++;
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) continue;
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) continue;
+
+ /* Skip comments */
+ if (buf[0] == '#') continue;
+
+
+ /* Process "?:<expr>" */
+ if ((buf[0] == '?') && (buf[1] == ':'))
+ {
+ char f;
+ cptr v;
+ char *s;
+
+ /* Start */
+ s = buf + 2;
+
+ /* Parse the expr */
+ v = process_pref_file_expr(&s, &f);
+
+ /* Set flag */
+ bypass = (streq(v, "0") ? TRUE : FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+ /* Apply conditionals */
+ if (bypass) continue;
+
+
+ /* Process "%:<file>" */
+ if (buf[0] == '%')
+ {
+ /* Process that file if allowed */
+ (void)process_pref_file(buf + 2);
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Process the line */
+ err = process_pref_file_aux(buf);
+
+ /* Oops */
+ if (err) break;
+ }
+
+
+ /* Error */
+ if (err)
+ {
+ /* Useful error message */
+ msg_format("Error %d in line %d of file '%s'.", err, num, name);
+ msg_format("Parsing '%s'", buf);
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Result */
+ return (err);
+}
+
+
+
+
+
+
+
+#ifdef CHECK_TIME
+
+/*
+ * Operating hours for ANGBAND (defaults to non-work hours)
+ */
+static char days[7][29] =
+ {
+ "SUN:XXXXXXXXXXXXXXXXXXXXXXXX",
+ "MON:XXXXXXXX.........XXXXXXX",
+ "TUE:XXXXXXXX.........XXXXXXX",
+ "WED:XXXXXXXX.........XXXXXXX",
+ "THU:XXXXXXXX.........XXXXXXX",
+ "FRI:XXXXXXXX.........XXXXXXX",
+ "SAT:XXXXXXXXXXXXXXXXXXXXXXXX"
+ };
+
+/*
+ * Restict usage (defaults to no restrictions)
+ */
+static bool check_time_flag = FALSE;
+
+#endif
+
+
+/*
+ * Handle CHECK_TIME
+ */
+errr check_time(void)
+{
+
+#ifdef CHECK_TIME
+
+ time_t c;
+
+ struct tm *tp;
+
+
+ /* No restrictions */
+ if (!check_time_flag) return (0);
+
+ /* Check for time violation */
+ c = time((time_t *)0);
+ tp = localtime(&c);
+
+ /* Violation */
+ if (days[tp->tm_wday][tp->tm_hour + 4] != 'X') return (1);
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * !Ran under the game's permission!
+ *
+ * Initialize CHECK_TIME
+ */
+errr check_time_init(void)
+{
+
+#ifdef CHECK_TIME
+
+ FILE *fp;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "time.txt");
+
+ /*
+ * XXX No need to grab permission here because this function is called
+ * only once before the game drops "game" permission
+ */
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* No file, no restrictions */
+ if (!fp) return (0);
+
+ /* Assume restrictions */
+ check_time_flag = TRUE;
+
+ /* Parse the file */
+ while (0 == my_fgets(fp, buf, 80))
+ {
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Chop the buffer */
+ buf[29] = '\0';
+
+ /* Extract the info */
+ if (prefix(buf, "SUN:")) strcpy(days[0], buf);
+ if (prefix(buf, "MON:")) strcpy(days[1], buf);
+ if (prefix(buf, "TUE:")) strcpy(days[2], buf);
+ if (prefix(buf, "WED:")) strcpy(days[3], buf);
+ if (prefix(buf, "THU:")) strcpy(days[4], buf);
+ if (prefix(buf, "FRI:")) strcpy(days[5], buf);
+ if (prefix(buf, "SAT:")) strcpy(days[6], buf);
+ }
+
+ /* Close it */
+ my_fclose(fp);
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+
+#ifdef CHECK_LOAD
+
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 64
+#endif
+
+typedef struct statstime statstime;
+
+struct statstime
+{
+ int cp_time[4];
+ int dk_xfer[4];
+ unsigned int v_pgpgin;
+ unsigned int v_pgpgout;
+ unsigned int v_pswpin;
+ unsigned int v_pswpout;
+ unsigned int v_intr;
+ int if_ipackets;
+ int if_ierrors;
+ int if_opackets;
+ int if_oerrors;
+ int if_collisions;
+ unsigned int v_swtch;
+ long avenrun[3];
+ struct timeval boottime;
+ struct timeval curtime;
+};
+
+/*
+ * Maximal load (if any).
+ */
+static int check_load_value = 0;
+
+#endif
+
+
+/*
+ * Handle CHECK_LOAD
+ */
+errr check_load(void)
+{
+
+#ifdef CHECK_LOAD
+
+ struct statstime st;
+
+
+ /* Success if not checking */
+ if (!check_load_value) return (0);
+
+ /* Check the load */
+ if (0 == rstat("localhost", &st))
+ {
+ long val1 = (long)(st.avenrun[2]);
+ long val2 = (long)(check_load_value) * FSCALE;
+
+ /* Check for violation */
+ if (val1 >= val2) return (1);
+ }
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * !Ran under the game's permission!
+ *
+ * Initialize CHECK_LOAD
+ */
+errr check_load_init(void)
+{
+
+#ifdef CHECK_LOAD
+
+ FILE *fp;
+
+ char buf[1024];
+
+ char temphost[MAXHOSTNAMELEN + 1];
+ char thishost[MAXHOSTNAMELEN + 1];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "load.txt");
+
+ /*
+ * XXX No need to grab permission here because this function is called
+ * only once before the game drops "game" permission
+ */
+
+ /* Open the "load" file */
+ fp = my_fopen(buf, "r");
+
+ /* No file, no restrictions */
+ if (!fp) return (0);
+
+ /* Default load */
+ check_load_value = 100;
+
+ /* Get the host name */
+ (void)gethostname(thishost, (sizeof thishost) - 1);
+
+ /* Parse it */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ int value;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Parse, or ignore */
+ if (sscanf(buf, "%s%d", temphost, &value) != 2) continue;
+
+ /* Skip other hosts */
+ if (!streq(temphost, thishost) &&
+ !streq(temphost, "localhost")) continue;
+
+ /* Use that value */
+ check_load_value = value;
+
+ /* Done */
+ break;
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Print long number with header at given row, column
+ * Use the color for the number, not the header
+ */
+static void prt_lnum(cptr header, s32b num, int row, int col, byte color)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ (void)sprintf(out_val, "%9ld", (long)num);
+ c_put_str(color, out_val, row, col + len);
+}
+
+
+/*
+ * Print number with header at given row, column
+ */
+static void prt_num(cptr header, int num, int row, int col, byte color,
+ char *space)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ put_str(space, row, col + len);
+ (void)sprintf(out_val, "%6ld", (long)num);
+ c_put_str(color, out_val, row, col + len + strlen(space));
+}
+
+
+/*
+ * Print str with header at given row, column
+ */
+static void prt_str(cptr header, cptr str, int row, int col, byte color)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ put_str(" ", row, col + len);
+ (void)sprintf(out_val, "%6s", str);
+ c_put_str(color, out_val, row, col + len + 3);
+}
+
+
+/*
+ * Prints the following information on the screen.
+ *
+ * For this to look right, the following should be spaced the
+ * same as in the prt_lnum code... -CFT
+ */
+static void display_player_middle(void)
+{
+ int show_tohit = p_ptr->dis_to_h;
+ int show_todam = p_ptr->dis_to_d;
+
+ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ char num[7];
+ byte color;
+ int speed;
+
+
+ /* Hack -- add in weapon info if known */
+ if (object_known_p(o_ptr)) show_tohit = p_ptr->dis_to_h + p_ptr->to_h_melee + o_ptr->to_h;
+ else show_tohit = p_ptr->dis_to_h + p_ptr->to_h_melee;
+ if (object_known_p(o_ptr)) show_todam = p_ptr->dis_to_d + p_ptr->to_d_melee + o_ptr->to_d;
+ else show_todam = p_ptr->dis_to_d + p_ptr->to_d_melee;
+
+ /* Dump the bonuses to hit/dam */
+ prt_num("+ To Melee Hit ", show_tohit, 9, 1, TERM_L_BLUE, " ");
+ prt_num("+ To Melee Damage", show_todam, 10, 1, TERM_L_BLUE, " ");
+
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ /* Hack -- add in weapon info if known */
+ if (object_known_p(o_ptr)) show_tohit = p_ptr->dis_to_h + p_ptr->to_h_ranged + o_ptr->to_h;
+ else show_tohit = p_ptr->dis_to_h + p_ptr->to_h_ranged;
+ if (object_known_p(o_ptr)) show_todam = p_ptr->to_d_ranged + o_ptr->to_d;
+ else show_todam = p_ptr->to_d_ranged;
+
+ prt_num("+ To Ranged Hit ", show_tohit, 11, 1, TERM_L_BLUE, " ");
+ prt_num("+ To Ranged Damage", show_todam, 12, 1, TERM_L_BLUE, " ");
+
+ /* Dump the total armor class */
+ prt_str(" AC ", format("%d+%d", p_ptr->ac, p_ptr->dis_to_a), 13, 1, TERM_L_BLUE);
+
+ prt_num("Level ", (int)p_ptr->lev, 9, 28, TERM_L_GREEN, " ");
+
+ if (p_ptr->exp >= p_ptr->max_exp)
+ {
+ prt_lnum("Experience ", p_ptr->exp, 10, 28, TERM_L_GREEN);
+ }
+ else
+ {
+ prt_lnum("Experience ", p_ptr->exp, 10, 28, TERM_YELLOW);
+ }
+
+ prt_lnum("Max Exp ", p_ptr->max_exp, 11, 28, TERM_L_GREEN);
+
+ if ((p_ptr->lev >= PY_MAX_LEVEL) || (p_ptr->lev >= max_plev))
+ {
+ put_str("Exp to Adv.", 12, 28);
+ c_put_str(TERM_L_GREEN, " *****", 12, 28 + 11);
+ }
+ else
+ {
+ prt_lnum("Exp to Adv.",
+ (s32b)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L),
+ 12, 28, TERM_L_GREEN);
+ }
+
+ prt_lnum("Gold ", p_ptr->au, 13, 28, TERM_L_GREEN);
+
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ put_str("Death Points ", 9, 52);
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_BLUE;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_VIOLET;
+ }
+ else
+ {
+ color = TERM_L_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->chp);
+ c_put_str(color, num, 9, 65);
+ put_str("/", 9, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->mhp);
+ c_put_str(TERM_L_BLUE, num, 9, 72);
+ }
+ else
+ {
+ put_str("Hit Points ", 9, 52);
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->chp);
+ c_put_str(color, num, 9, 65);
+ put_str("/", 9, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->mhp);
+ c_put_str(TERM_L_GREEN, num, 9, 72);
+ }
+
+ put_str("Spell Points ", 10, 52);
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->csp);
+ c_put_str(color, num, 10, 65);
+ put_str("/", 10, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->msp);
+ c_put_str(TERM_L_GREEN, num, 10, 72);
+
+ put_str("Sanity ", 11, 52);
+ if (p_ptr->csane >= p_ptr->msane)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csane > (p_ptr->msane * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ (void)sprintf(num, "%6ld", (long)p_ptr->csane);
+ c_put_str(color, num, 11, 65);
+ put_str("/", 11, 71);
+ (void)sprintf(num, "%6ld", (long)p_ptr->msane);
+ c_put_str(TERM_L_GREEN, num, 11, 72);
+
+ if (p_ptr->pgod != GOD_NONE)
+ {
+ prt_num("Piety ", p_ptr->grace, 12, 52, TERM_L_GREEN, " ");
+ }
+
+ put_str("Speed ", 13, 52);
+ speed = p_ptr->pspeed;
+ /* Hack -- Visually "undo" the Search Mode Slowdown */
+ if (p_ptr->searching) speed += 10;
+ if (speed > 110)
+ {
+ char s[11];
+ (void)sprintf(s, "Fast (+%d)", speed - 110);
+ c_put_str(TERM_L_GREEN, s, 13, (speed >= 120) ? 68 : 69);
+ }
+ else if (speed < 110)
+ {
+ char s[11];
+ (void)sprintf(s, "Slow (-%d)", 110 - speed);
+ c_put_str(TERM_L_UMBER, s, 13, (speed <= 100) ? 68 : 69);
+ }
+ else
+ {
+ put_str("Normal", 13, 72);
+ }
+}
+
+
+
+
+/*
+ * Hack -- pass color info around this file
+ */
+static byte likert_color = TERM_WHITE;
+
+
+/*
+ * Returns a "rating" of x depending on y
+ */
+static cptr likert(int x, int y)
+{
+ static char dummy[20] = "";
+
+ /* Paranoia */
+ if (y <= 0) y = 1;
+
+ /* Negative value */
+ if (x < 0)
+ {
+ likert_color = TERM_L_DARK;
+ return ("Very Bad");
+ }
+
+ /* Analyze the value */
+ switch ((x / y))
+ {
+ case 0:
+ case 1:
+ {
+ likert_color = TERM_RED;
+ return ("Bad");
+ }
+ case 2:
+ {
+ likert_color = TERM_L_RED;
+ return ("Poor");
+ }
+ case 3:
+ case 4:
+ {
+ likert_color = TERM_ORANGE;
+ return ("Fair");
+ }
+ case 5:
+ {
+ likert_color = TERM_YELLOW;
+ return ("Good");
+ }
+ case 6:
+ {
+ likert_color = TERM_YELLOW;
+ return ("Very Good");
+ }
+ case 7:
+ case 8:
+ {
+ likert_color = TERM_L_GREEN;
+ return ("Excellent");
+ }
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ {
+ likert_color = TERM_GREEN;
+ return ("Superb");
+ }
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ {
+ likert_color = TERM_L_GREEN;
+ return ("Heroic");
+ }
+ default:
+ {
+ likert_color = TERM_L_GREEN;
+ sprintf(dummy, "Legendary[%d]", (int)((((x / y) - 17) * 5) / 2));
+ return dummy;
+ }
+ }
+}
+
+
+/*
+ * Prints ratings on certain abilities
+ *
+ * This code is "imitated" elsewhere to "dump" a character sheet.
+ */
+static void display_player_various(void)
+{
+ int tmp, tmp2, damdice, damsides, dambonus, blows;
+ int xthn, xthb, xfos, xsrh;
+ int xdis, xdev, xsav, xstl;
+ cptr desc;
+ int i;
+
+ object_type *o_ptr;
+
+
+ /* Fighting Skill (with current weapon) */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ tmp = p_ptr->to_h + o_ptr->to_h + p_ptr->to_h_melee;
+ xthn = p_ptr->skill_thn + (tmp * BTH_PLUS_ADJ);
+
+ /* Shooting Skill (with current bow and normal missile) */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+ tmp = p_ptr->to_h + o_ptr->to_h + p_ptr->to_h_ranged;
+ xthb = p_ptr->skill_thb + (tmp * BTH_PLUS_ADJ);
+
+ /* variables for all types of melee damage */
+ dambonus = p_ptr->dis_to_d;
+ blows = p_ptr->num_blow;
+
+ /* Basic abilities */
+ xdis = p_ptr->skill_dis;
+ xdev = p_ptr->skill_dev;
+ xsav = p_ptr->skill_sav;
+ xstl = p_ptr->skill_stl;
+ xsrh = p_ptr->skill_srh;
+ xfos = p_ptr->skill_fos;
+
+
+ put_str("Fighting :", 16, 1);
+ desc = likert(xthn, 12);
+ c_put_str(likert_color, desc, 16, 15);
+
+ put_str("Bows/Throw :", 17, 1);
+ desc = likert(xthb, 12);
+ c_put_str(likert_color, desc, 17, 15);
+
+ put_str("Saving Throw:", 18, 1);
+ desc = likert(xsav, 6);
+ c_put_str(likert_color, desc, 18, 15);
+
+ put_str("Stealth :", 19, 1);
+ desc = likert(xstl, 1);
+ c_put_str(likert_color, desc, 19, 15);
+
+
+ put_str("Perception :", 16, 28);
+ desc = likert(xfos, 6);
+ c_put_str(likert_color, desc, 16, 42);
+
+ put_str("Searching :", 17, 28);
+ desc = likert(xsrh, 6);
+ c_put_str(likert_color, desc, 17, 42);
+
+ put_str("Disarming :", 18, 28);
+ desc = likert(xdis, 8);
+ c_put_str(likert_color, desc, 18, 42);
+
+ put_str("Magic Device:", 19, 28);
+ desc = likert(xdev, 6);
+ c_put_str(likert_color, desc, 19, 42);
+
+
+ put_str("Blows/Round:", 16, 55);
+ put_str(format("%d", p_ptr->num_blow), 16, 69);
+
+ put_str("Shots/Round:", 17, 55);
+ put_str(format("%d", p_ptr->num_fire), 17, 69);
+
+ put_str("Mel.dmg/Rnd:", 18, 55); /* From PsiAngband */
+
+ if (p_ptr->melee_style == SKILL_HAND || p_ptr->melee_style == SKILL_BEAR)
+ {
+ /* This is all based on py_attack_hand */
+ martial_arts *blow_table, *min_attack, *max_attack;
+ int max_blow, plev, i;
+
+ if (p_ptr->melee_style == SKILL_HAND)
+ {
+ blow_table = ma_blows;
+ max_blow = MAX_MA;
+ plev = get_skill(SKILL_HAND);
+ }
+ else /* SKILL_BEAR */
+ {
+ blow_table = bear_blows;
+ max_blow = MAX_BEAR;
+ plev = get_skill(SKILL_BEAR);
+ }
+ min_attack = blow_table;
+ i = max_blow - 1;
+ while (blow_table[i].min_level > plev && i != 0)
+ --i;
+ max_attack = &blow_table[i];
+
+ dambonus += p_ptr->to_d_melee;
+ tmp = min_attack->dd + dambonus;
+ if (tmp < 0) tmp = 0;
+ tmp2 = maxroll(max_attack->dd, max_attack->ds) + dambonus;
+ if (tmp2 < 0) tmp2 = 0;
+ if (!tmp && !tmp2)
+ desc = "0";
+ else
+ desc = format("%d-%d", blows * tmp, blows * tmp2);
+ }
+ else if (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON])
+ {
+ if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW)
+ desc = "nil!";
+ else
+ {
+ tmp = tmp2 = 0;
+ for (i = 0; i < blows; i++)
+ {
+ tmp += r_info[p_ptr->body_monster].blow[i].d_dice;
+ tmp2 += maxroll(r_info[p_ptr->body_monster].blow[i].d_dice,
+ r_info[p_ptr->body_monster].blow[i].d_side);
+ }
+ if (dambonus > 0)
+ {
+ tmp += dambonus;
+ tmp2 += dambonus;
+ }
+ desc = format("%d-%d", tmp, tmp2);
+ }
+ }
+ else
+ {
+ /* Increase the bonus to damage for weapon combat */
+ dambonus += p_ptr->to_d_melee;
+
+ /* Access the first weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ if (object_known_p(o_ptr)) dambonus += o_ptr->to_d;
+
+ damdice = o_ptr->dd;
+ damsides = o_ptr->ds;
+
+ if ((damdice == 0) || (damsides == 0))
+ {
+ if (dambonus <= 0)
+ desc = "nil!";
+ else
+ desc = format("%d", blows * dambonus);
+ }
+ else
+ {
+ if (dambonus == 0)
+ desc = format("%dd%d", blows * damdice, damsides);
+ else
+ desc = format("%dd%d%c%d", blows * damdice, damsides,
+ ( dambonus > 0 ? '+' : '\0' ), blows * dambonus );
+ }
+ }
+ put_str(desc, 18, 69);
+
+
+ put_str("Infra-Vision:", 19, 55);
+ put_str(format("%d feet", p_ptr->see_infra * 10), 19, 69);
+
+ /* jk - add tactic */
+ put_str("Tactic:", 20, 55);
+ c_put_str(TERM_L_BLUE, tactic_info[(byte)p_ptr->tactic].name, 20, 69);
+
+ /* jk - add movement */
+ put_str("Explor:", 21, 55);
+ c_put_str(TERM_L_BLUE, move_info[(byte)p_ptr->movement].name, 21, 69);
+}
+
+
+
+/*
+ * Obtain the "flags" of the wielded symbiote
+ */
+
+void wield_monster_flags(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Clear */
+ (*f1) = (*f2) = (*f3) = (*f4) = (*f5) = (*esp) = 0L;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ r_ptr = &r_info[o_ptr->pval];
+
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ (*f2) |= TR2_INVIS;
+ if (r_ptr->flags2 & RF2_REFLECTING)
+ (*f2) |= TR2_REFLECT;
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ (*f3) |= TR3_FEATHER;
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ (*f5) |= TR5_WATER_BREATH;
+ }
+}
+
+
+/*
+ * Obtain the "flags" for the player as if he was an item
+ */
+void player_flags(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ int i;
+
+ /* Clear */
+ (*f1) = (*f2) = (*f3) = (*f4) = (*f5) = (*esp) = 0L;
+
+ /* Astral chars */
+ if (p_ptr->astral)
+ {
+ (*f3) |= TR3_WRAITH;
+ }
+
+/* Skills */
+ if (get_skill(SKILL_DAEMON) > 20) (*f2) |= TR2_RES_CONF;
+ if (get_skill(SKILL_DAEMON) > 30) (*f2) |= TR2_RES_FEAR;
+ if (get_skill(SKILL_MINDCRAFT) >= 40) (*esp) |= ESP_ALL;
+ if (p_ptr->melee_style == SKILL_HAND && get_skill(SKILL_HAND) > 24 && !monk_heavy_armor())
+ (*f2) |= TR2_FREE_ACT;
+/* Hack - from Lua */
+ if (get_skill(SKILL_MANA) >= 35) (*f1) |= TR1_MANA;
+ if (get_skill(SKILL_AIR) >= 50) (*f5) |= (TR5_MAGIC_BREATH | TR5_WATER_BREATH);
+ if (get_skill(SKILL_WATER) >= 30) (*f5) |= TR5_WATER_BREATH;
+
+/* Gods */
+ GOD(GOD_ERU)
+ {
+ if ((p_ptr->grace >= 100) || (p_ptr->grace <= -100)) (*f1) |= TR1_MANA;
+ if (p_ptr->grace > 10000) (*f1) |= TR1_WIS;
+ }
+
+ GOD(GOD_MELKOR)
+ {
+ (*f2) |= TR2_RES_FIRE;
+ if (p_ptr->melkor_sacrifice > 0) (*f2) |= TR2_LIFE;
+ if (p_ptr->grace > 10000) (*f1) |= (TR1_STR | TR1_CON | TR1_INT | TR1_WIS | TR1_CHR);
+ PRAY_GOD(GOD_MELKOR)
+ {
+ if (p_ptr->grace > 5000) (*f2) |= TR2_INVIS;
+ if (p_ptr->grace > 15000) (*f2) |= TR2_IM_FIRE;
+ }
+ }
+
+ GOD(GOD_MANWE)
+ {
+ if (p_ptr->grace >= 2000) (*f3) |= TR3_FEATHER;
+ PRAY_GOD(GOD_MANWE)
+ {
+ if (p_ptr->grace >= 7000) (*f2) |= TR2_FREE_ACT;
+ if (p_ptr->grace >= 15000) (*f4) |= TR4_FLY;
+ if ((p_ptr->grace >= 5000) || (p_ptr->grace <= -5000)) (*f1) |= TR1_SPEED;
+ }
+ }
+
+ GOD(GOD_TULKAS)
+ {
+ if (p_ptr->grace > 5000) (*f1) |= TR1_CON;
+ if (p_ptr->grace > 10000) (*f1) |= TR1_STR;
+ }
+
+ /* Classes */
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ (*f1) |= cp_ptr->oflags1[i];
+ (*f2) |= cp_ptr->oflags2[i];
+ (*f3) |= cp_ptr->oflags3[i];
+ (*f4) |= cp_ptr->oflags4[i];
+ (*f5) |= cp_ptr->oflags5[i];
+ (*esp) |= cp_ptr->oesp[i];
+ }
+
+ /* Races */
+ if ((!p_ptr->mimic_form) && (!p_ptr->body_monster))
+ {
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ (*f1) |= rp_ptr->oflags1[i];
+ (*f2) |= rp_ptr->oflags2[i];
+ (*f3) |= rp_ptr->oflags3[i];
+ (*f4) |= rp_ptr->oflags4[i];
+ (*f5) |= rp_ptr->oflags5[i];
+ (*esp) |= rp_ptr->oesp[i];
+
+ (*f1) |= rmp_ptr->oflags1[i];
+ (*f2) |= rmp_ptr->oflags2[i];
+ (*f3) |= rmp_ptr->oflags3[i];
+ (*f4) |= rmp_ptr->oflags4[i];
+ (*f5) |= rmp_ptr->oflags5[i];
+ (*esp) |= rmp_ptr->oesp[i];
+ }
+ }
+#if 0 /* DGDGDG? */
+ else if (p_ptr->mimic_form)
+ {
+ switch (p_ptr->mimic_form)
+ {
+ case MIMIC_GOAT:
+ {
+ (*f3) |= (TR3_SLOW_DIGEST);
+ break;
+ }
+ case MIMIC_INSECT:
+ {
+ (*esp) |= (ESP_ANIMAL);
+ break;
+ }
+ case MIMIC_SPARROW:
+ {
+ (*f3) |= (TR3_FEATHER);
+ break;
+ }
+ case MIMIC_VAMPIRE:
+ {
+ (*f3) |= (TR2_HOLD_LIFE);
+ (*f3) |= (TR2_RES_DARK);
+ (*f3) |= (TR2_RES_NEXUS);
+ (*f3) |= (TR2_RES_BLIND);
+ (*f3) |= (TR3_LITE1);
+ break;
+ }
+ case MIMIC_SPIDER:
+ {
+ (*f3) |= (TR2_RES_FEAR);
+ (*f3) |= (TR2_RES_POIS);
+ break;
+ }
+ case MIMIC_MANA_BALL:
+ {
+ (*f3) |= (TR3_FEATHER);
+ (*f2) |= (TR2_INVIS);
+ (*f3) |= (TR3_TELEPORT);
+ (*f3) |= (TR2_RES_DISEN);
+ break;
+ }
+ case MIMIC_FIRE_CLOUD:
+ {
+ (*f3) |= (TR2_RES_LITE);
+ (*f3) |= (TR2_IM_FIRE);
+ (*f3) |= (TR3_SH_FIRE);
+ break;
+ }
+ case MIMIC_COLD_CLOUD:
+ {
+ (*f3) |= (TR2_RES_LITE);
+ (*f3) |= (TR2_IM_COLD);
+ (*f3) |= (TR3_SH_ELEC);
+ (*esp) |= (ESP_EVIL);
+ (*f3) |= (TR3_REGEN);
+ break;
+ }
+ case MIMIC_CHAOS_CLOUD:
+ {
+ (*f3) |= (TR2_RES_DISEN);
+ (*f3) |= (TR2_RES_CHAOS);
+ (*f3) |= (TR2_RES_LITE);
+ (*f3) |= (TR2_IM_FIRE);
+ (*f3) |= (TR2_IM_COLD);
+ (*f3) |= (TR3_SH_FIRE);
+ (*f3) |= (TR3_SH_ELEC);
+ break;
+ }
+ case MIMIC_GOST:
+ {
+ (*f3) |= (TR3_WRAITH);
+ (*f2) |= TR2_HOLD_LIFE;
+ break;
+ }
+ case MIMIC_ENT:
+ {
+ (*f2) |= TR2_SENS_FIRE;
+ break;
+ }
+ case MIMIC_KOBOLD:
+ {
+ (*f2) |= TR2_RES_POIS;
+ break;
+ }
+ case MIMIC_DRAGON:
+ {
+ (*f3) |= TR3_FEATHER;
+ (*f2) |= TR2_RES_FIRE;
+ (*f2) |= TR2_RES_COLD;
+ (*f2) |= TR2_RES_ELEC;
+ (*f2) |= TR2_RES_DARK;
+ break;
+ }
+ case MIMIC_DEMON:
+ {
+ (*f2) |= TR2_RES_CHAOS;
+ (*f2) |= TR2_RES_NETHER;
+ (*f2) |= TR2_HOLD_LIFE;
+ break;
+ }
+ case MIMIC_HOUND:
+ {
+ (*f1) |= TR1_SPEED;
+ (*f2) |= TR2_RES_LITE;
+ (*f2) |= TR2_RES_DARK;
+ break;
+ }
+ case MIMIC_QUYLTHULG:
+ {
+ (*f3) |= TR3_SEE_INVIS;
+ break;
+ }
+ case MIMIC_MAIAR:
+ {
+ (*f2) |= TR2_IM_ACID;
+ (*f2) |= TR2_IM_ELEC;
+ (*f2) |= TR2_IM_FIRE;
+ (*f2) |= TR2_IM_COLD;
+ (*f2) |= TR2_RES_POIS;
+ (*f2) |= TR2_RES_LITE;
+ (*f2) |= TR2_RES_DARK;
+ (*f2) |= TR2_RES_CHAOS;
+ (*f2) |= TR2_HOLD_LIFE;
+ (*f3) |= TR3_FEATHER;
+ (*f3) |= TR3_REGEN;
+ break;
+ }
+ case MIMIC_SERPENT:
+ {
+ (*f1) |= TR1_SPEED;
+ break;
+ }
+ case MIMIC_GIANT:
+ {
+ (*f2) |= TR2_RES_ACID;
+ (*f2) |= TR2_RES_ELEC;
+ (*f2) |= TR2_RES_FIRE;
+ (*f2) |= TR2_RES_COLD;
+ (*f2) |= TR2_RES_POIS;
+ (*f2) |= TR2_RES_CONF;
+ (*f2) |= TR2_RES_SOUND;
+ (*f2) |= TR2_RES_LITE;
+ (*f2) |= TR2_RES_DARK;
+ (*f2) |= TR2_RES_NEXUS;
+ (*f2) |= TR2_RES_FEAR;
+ (*f2) |= TR2_REFLECT;
+ break;
+ }
+ case MIMIC_VALAR:
+ {
+ (*f3) |= TR3_SEE_INVIS;
+ (*f2) |= TR2_FREE_ACT;
+ (*f3) |= TR3_SLOW_DIGEST;
+ (*f3) |= TR3_REGEN;
+ (*f3) |= TR3_FEATHER;
+ (*f2) |= TR2_HOLD_LIFE;
+ (*esp) |= ESP_ALL;
+ (*f3) |= TR3_LITE1;
+ (*f2) |= TR2_SUST_STR;
+ (*f1) |= TR1_INT;
+ (*f1) |= TR1_WIS;
+ (*f1) |= TR1_DEX;
+ (*f1) |= TR1_CON;
+ (*f1) |= TR1_CHR;
+ (*f2) |= TR2_RES_ACID;
+ (*f2) |= TR2_RES_ELEC;
+ (*f2) |= TR2_RES_FIRE;
+ (*f2) |= TR2_RES_COLD;
+ (*f2) |= TR2_RES_POIS;
+ (*f2) |= TR2_RES_CONF;
+ (*f2) |= TR2_RES_SOUND;
+ (*f2) |= TR2_RES_LITE;
+ (*f2) |= TR2_RES_DARK;
+ (*f2) |= TR2_RES_CHAOS;
+ (*f2) |= TR2_RES_DISEN;
+ (*f2) |= TR2_RES_SHARDS;
+ (*f2) |= TR2_RES_NEXUS;
+ (*f2) |= TR2_RES_BLIND;
+ (*f2) |= TR2_RES_NETHER;
+ (*f2) |= TR2_RES_FEAR;
+ (*f2) |= TR2_REFLECT;
+ (*f3) |= TR3_SH_FIRE;
+ (*f3) |= TR3_SH_ELEC;
+ (*f2) |= TR2_IM_FIRE;
+ (*f2) |= TR2_IM_COLD;
+ (*f2) |= TR2_IM_ELEC;
+ (*f2) |= TR2_IM_ACID;
+ break;
+ }
+
+ case MIMIC_WEREWOLF:
+ {
+ (*f3) |= TR3_REGEN;
+ (*f3) |= TR3_AGGRAVATE;
+ break;
+ }
+ case MIMIC_BALROG:
+ {
+ (*f2) |= TR2_IM_ACID;
+ (*f2) |= TR2_IM_ELEC;
+ (*f2) |= TR2_IM_FIRE;
+ (*f2) |= TR2_RES_POIS;
+ (*f2) |= TR2_RES_DARK;
+ (*f2) |= TR2_RES_CHAOS;
+ (*f2) |= TR2_HOLD_LIFE;
+ (*f3) |= TR3_FEATHER;
+ (*f3) |= TR3_REGEN;
+ break;
+ }
+ case MIMIC_DEMON_LORD:
+ {
+ (*f3) |= TR3_SEE_INVIS;
+ (*f2) |= TR2_FREE_ACT;
+ (*f3) |= TR3_REGEN;
+ (*f3) |= TR3_FEATHER;
+ (*f2) |= TR2_HOLD_LIFE;
+ (*esp) |= ESP_EVIL | ESP_GOOD | ESP_DEMON;
+ (*f2) |= TR2_RES_ACID;
+ (*f2) |= TR2_RES_ELEC;
+ (*f2) |= TR2_RES_FIRE;
+ (*f2) |= TR2_RES_POIS;
+ (*f2) |= TR2_RES_CONF;
+ (*f2) |= TR2_RES_SOUND;
+ (*f2) |= TR2_RES_LITE;
+ (*f2) |= TR2_RES_DARK;
+ (*f2) |= TR2_RES_CHAOS;
+ (*f2) |= TR2_RES_DISEN;
+ (*f2) |= TR2_RES_SHARDS;
+ (*f2) |= TR2_RES_NEXUS;
+ (*f2) |= TR2_RES_BLIND;
+ (*f2) |= TR2_RES_NETHER;
+ (*f2) |= TR2_RES_FEAR;
+ (*f2) |= TR2_REFLECT;
+ (*f3) |= TR3_SH_FIRE;
+ (*f2) |= TR2_IM_FIRE;
+ (*f2) |= TR2_IM_ELEC;
+ (*f2) |= TR2_IM_ACID;
+ break;
+ }
+ }
+ }
+#endif
+ else
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ if (r_ptr->flags2 & RF2_REFLECTING) (*f2) |= TR2_REFLECT;
+ if (r_ptr->flags2 & RF2_REGENERATE) (*f3) |= TR3_REGEN;
+ if (r_ptr->flags2 & RF2_AURA_FIRE) (*f3) |= TR3_SH_FIRE;
+ if (r_ptr->flags2 & RF2_AURA_ELEC) (*f3) |= TR3_SH_ELEC;
+ if (r_ptr->flags2 & RF2_PASS_WALL) (*f3) |= TR3_WRAITH;
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE) (*f2) |= TR2_SENS_FIRE;
+ if (r_ptr->flags3 & RF3_IM_ACID) (*f2) |= TR2_RES_ACID;
+ if (r_ptr->flags3 & RF3_IM_ELEC) (*f2) |= TR2_RES_ELEC;
+ if (r_ptr->flags3 & RF3_IM_FIRE) (*f2) |= TR2_RES_FIRE;
+ if (r_ptr->flags3 & RF3_IM_POIS) (*f2) |= TR2_RES_POIS;
+ if (r_ptr->flags3 & RF3_IM_COLD) (*f2) |= TR2_RES_COLD;
+ if (r_ptr->flags3 & RF3_RES_NETH) (*f2) |= TR2_RES_NETHER;
+ if (r_ptr->flags3 & RF3_RES_NEXU) (*f2) |= TR2_RES_NEXUS;
+ if (r_ptr->flags3 & RF3_RES_DISE) (*f2) |= TR2_RES_DISEN;
+ if (r_ptr->flags3 & RF3_NO_FEAR) (*f2) |= TR2_RES_FEAR;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) (*f2) |= TR2_FREE_ACT;
+ if (r_ptr->flags3 & RF3_NO_CONF) (*f2) |= TR2_RES_CONF;
+ if (r_ptr->flags7 & RF7_CAN_FLY) (*f3) |= TR3_FEATHER;
+ }
+
+ (*f1) |= p_ptr->xtra_f1;
+ (*f2) |= p_ptr->xtra_f2;
+ (*f3) |= p_ptr->xtra_f3;
+ (*f4) |= p_ptr->xtra_f4;
+ (*f5) |= p_ptr->xtra_f5;
+ (*esp) |= p_ptr->xtra_esp;
+
+ if (p_ptr->black_breath)
+ {
+ (*f4) |= TR4_BLACK_BREATH;
+ }
+
+ if (p_ptr->hp_mod != 0)
+ {
+ (*f2) |= TR2_LIFE;
+ }
+}
+
+/*
+ * Object flag names
+ */
+static cptr object_flag_names[192] =
+{
+ "Add Str",
+ "Add Int",
+ "Add Wis",
+ "Add Dex",
+ "Add Con",
+ "Add Chr",
+ "Mul Mana",
+ "Mul SPower",
+ "Add Stea.",
+ "Add Sear.",
+ "Add Infra",
+ "Add Tun..",
+ "Add Speed",
+ "Add Blows",
+ "Chaotic",
+ "Vampiric",
+ "Slay Anim.",
+ "Slay Evil",
+ "Slay Und.",
+ "Slay Demon",
+ "Slay Orc",
+ "Slay Troll",
+ "Slay Giant",
+ "Slay Drag.",
+ "Kill Drag.",
+ "Sharpness",
+ "Impact",
+ "Poison Brd",
+ "Acid Brand",
+ "Elec Brand",
+ "Fire Brand",
+ "Cold Brand",
+
+ "Sust Str",
+ "Sust Int",
+ "Sust Wis",
+ "Sust Dex",
+ "Sust Con",
+ "Sust Chr",
+ "Invisible",
+ "Mul life",
+ "Imm Acid",
+ "Imm Elec",
+ "Imm Fire",
+ "Imm Cold",
+ "Sens Fire",
+ "Reflect",
+ "Free Act",
+ "Hold Life",
+ "Res Acid",
+ "Res Elec",
+ "Res Fire",
+ "Res Cold",
+ "Res Pois",
+ "Res Fear",
+ "Res Light",
+ "Res Dark",
+ "Res Blind",
+ "Res Conf",
+ "Res Sound",
+ "Res Shard",
+ "Res Neth",
+ "Res Nexus",
+ "Res Chaos",
+ "Res Disen",
+
+
+
+ "Aura Fire",
+ "Aura Elec",
+ "Auto Curse",
+ NULL,
+ "NoTeleport",
+ "AntiMagic",
+ "WraithForm",
+ "EvilCurse",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Levitate",
+ "Lite",
+ "See Invis",
+ NULL,
+ "Digestion",
+ "Regen",
+ "Xtra Might",
+ "Xtra Shots",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Activate",
+ "Drain Exp",
+ "Teleport",
+ "Aggravate",
+ "Blessed",
+ "Cursed",
+ "Hvy Curse",
+ "Prm Curse",
+
+ "No blows",
+ "Precogn.",
+ "B.Breath",
+ "Recharge",
+ "Fly",
+ "Mrg.Curse",
+ NULL,
+ NULL,
+ "Sentient",
+ "Clone",
+ NULL,
+ "Climb",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Imm Neth",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ "Orc.ESP",
+ "Troll.ESP",
+ "Dragon.ESP",
+ "Giant.ESP",
+ "Demon.ESP",
+ "Undead.ESP",
+ "Evil.ESP",
+ "Animal.ESP",
+ "TLord.ESP",
+ "Good.ESP",
+ "Nlive.ESP",
+ "Unique.ESP",
+ "Spider ESP",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Full ESP",
+};
+
+/*
+ * Summarize resistances
+ */
+static void display_player_ben_one(int mode)
+{
+ int i, n, x, y, z, dispx, modetemp, xtemp;
+
+ object_type *o_ptr;
+
+ char dummy[80], c;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ u16b b[INVEN_TOTAL - INVEN_WIELD + 1][10];
+
+ int d[INVEN_TOTAL - INVEN_WIELD + 1];
+
+ bool got;
+
+ byte a;
+
+ cptr name;
+
+ /* Scan equipment */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Index */
+ n = (i - INVEN_WIELD);
+
+ /* Object */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Known object flags */
+ object_flags_known(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+ d[n] = o_ptr->pval;
+ }
+
+ /* Carried symbiote */
+ n = INVEN_CARRY - INVEN_WIELD;
+
+ /* Player flags */
+ wield_monster_flags(&f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+
+ /* Index */
+ n = INVEN_TOTAL - INVEN_WIELD;
+
+ /* Player flags */
+ player_flags(&f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+
+ /* Generate the equip chars */
+ sprintf(dummy, " ");
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ {
+ /* If you have that body part then show it */
+ if (p_ptr->body_parts[i])
+ {
+ strcat(dummy, format("%c", i + 'a'));
+ }
+ }
+ strcat(dummy, "@");
+
+ /* Scan cols */
+ for (x = 1; x > -1; x--)
+ {
+ /* Label */
+ Term_putstr(x * 40 + 11, 3, -1, TERM_WHITE, dummy);
+
+ /* Scan rows */
+ for (y = 0; y < 16; y++)
+ {
+ if (mode == 3 && x == 1)
+ {
+ modetemp = 4;
+ xtemp = 0;
+ }
+ else
+ {
+ modetemp = mode;
+ xtemp = x;
+ }
+
+ for (z = mode; z <= modetemp; z++)
+ {
+ if (mode == 3 && x == 1 && z == modetemp) xtemp = 1;
+ name = object_flag_names[32 * modetemp + 16 * xtemp + y];
+ got = FALSE;
+
+ /* No name */
+ if (!name) continue;
+
+ /* Dump colon */
+ if (!(modetemp == 1 && x == 0 && y > 7 && y < 12))
+ {
+ Term_putch(x * 40 + 10, y + 4, TERM_WHITE, ':');
+ }
+
+ /* Check flags */
+ dispx = 0;
+ for (n = 0; n < INVEN_TOTAL - INVEN_WIELD + 1; n++)
+ {
+ /* Change colour every two columns */
+ bool is_green = (dispx & 0x02);
+ a = (is_green ? TERM_GREEN : TERM_SLATE);
+ c = '.';
+
+ /* If the body part doesn't exists then skip it :) */
+ if ((n < INVEN_TOTAL - INVEN_WIELD) && (!p_ptr->body_parts[n])) continue;
+
+ /* Increment the drawing coordinates */
+ dispx++;
+
+ /* Check flag */
+ if (b[n][2 * modetemp + xtemp] & (1 << y))
+ {
+ a = (is_green ? TERM_L_GREEN : TERM_WHITE);
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ c = '*';
+ }
+ else if (modetemp == 0 && x == 0 && y < 14 && (y < 6 || y > 7))
+ {
+ if (n == INVEN_TOTAL - INVEN_WIELD)
+ {
+ c = '+';
+ }
+ else
+ {
+ c = d[n];
+ if (c < 0)
+ {
+ c = -c;
+ a = TERM_RED;
+ }
+ c = (c > 9 ? '*' : I2D(c));
+ }
+ }
+ else
+ {
+ c = '+';
+ }
+ got = TRUE;
+ }
+
+ /* HACK - Check for nether immunity and
+ apply to Res Neth line */
+ if (modetemp == 1 && x == 1 && y == 12)
+ {
+ if (b[n][7] & (1 << 6))
+ {
+ a = (is_green ? TERM_L_GREEN : TERM_WHITE);
+ c = '*';
+ got = TRUE;
+ }
+ }
+
+ /* Monochrome */
+ if (!use_color) a = TERM_WHITE;
+
+ /* Dump flag */
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ if (c == '*') Term_putch(40 + 11 + dispx, y - 4, a, c);
+ }
+ else
+ {
+ Term_putch(x * 40 + 11 + dispx, y + 4, a, c);
+ }
+ }
+
+ a = TERM_WHITE;
+ if (use_color && got)
+ {
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ a = TERM_L_GREEN;
+ }
+ else if (modetemp != 0)
+ {
+ a = TERM_GREEN;
+ }
+ }
+
+ /* HACK - Check for nether immunity and change "Res Neth" */
+ if (modetemp == 1 && x == 1 && y == 12 && p_ptr->immune_neth)
+ {
+ name = "Imm Neth";
+ a = TERM_L_GREEN;
+ }
+
+ /* Dump name */
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ if (got) Term_putstr(40, y - 4, -1, a, name);
+ }
+ else
+ {
+ Term_putstr(x * 40, y + 4, -1, a, name);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Display the character on the screen (various modes)
+ *
+ * The top two and bottom two lines are left blank.
+ *
+ * Mode 0 = standard display with skills
+ * Mode 1 = standard display with history
+ * Mode 2 = current flags (part 1)
+ * Mode 3 = current flags (part 2)
+ * Mode 4 = current flags (part 3)
+ * Mode 5 = current flags (part 4)
+ * Mode 6 = current flags (part 5 -- esp)
+ */
+void display_player(int mode)
+{
+ int i;
+
+ char buf[80];
+
+
+ /* Erase screen */
+ clear_from(0);
+
+ /* Standard */
+ if ((mode == 0) || (mode == 1))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Name, Sex, Race, Class */
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class :", 5, 1);
+ put_str("Body :", 6, 1);
+ put_str("God :", 7, 1);
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+ if (p_ptr->body_monster != 0)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ char tmp[12];
+
+ if ((r_ptr->flags1 & RF1_MALE) != 0)
+ strcpy(tmp, "Male");
+ else if ((r_ptr->flags1 & RF1_FEMALE) != 0)
+ strcpy(tmp, "Female");
+ else
+ strcpy(tmp, "Neuter");
+ c_put_str(TERM_L_BLUE, tmp, 3, 9);
+ }
+ else
+ c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9);
+ sprintf(buf, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem));
+ c_put_str(TERM_L_BLUE, buf, 4, 9);
+ c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9);
+ c_put_str(TERM_L_BLUE, r_name + r_ptr->name, 6, 9);
+ c_put_str(TERM_L_BLUE, deity_info[p_ptr->pgod].name, 7, 9);
+
+ /* Age, Height, Weight, Social */
+ prt_num("Age ", (int)p_ptr->age + bst(YEAR, turn - (START_DAY * 10)), 2, 32, TERM_L_BLUE, " ");
+ prt_num("Height ", (int)p_ptr->ht, 3, 32, TERM_L_BLUE, " ");
+ prt_num("Weight ", (int)p_ptr->wt, 4, 32, TERM_L_BLUE, " ");
+ prt_num("Social Class ", (int)p_ptr->sc, 5, 32, TERM_L_BLUE, " ");
+
+ /* Display the stats */
+ for (i = 0; i < 6; i++)
+ {
+ char punctuation = p_ptr->stat_max[i] == 18 + 100 ? '!' : ':';
+ /* Special treatment of "injured" stats */
+ if (p_ptr->stat_cur[i] < p_ptr->stat_max[i])
+ {
+ int value;
+ int colour;
+
+ if (p_ptr->stat_cnt[i])
+ colour = TERM_ORANGE;
+ else
+ colour = TERM_YELLOW;
+
+ /* Use lowercase stat name */
+ put_str(format("%s%c ", stat_names_reduced[i], punctuation), 2 + i, 61);
+
+ /* Get the current stat */
+ value = p_ptr->stat_use[i];
+
+ /* Obtain the current stat (modified) */
+ cnv_stat(value, buf);
+
+ /* Display the current stat (modified) */
+ c_put_str(colour, buf, 2 + i, 66);
+
+ /* Acquire the max stat */
+ value = p_ptr->stat_top[i];
+
+ /* Obtain the maximum stat (modified) */
+ cnv_stat(value, buf);
+
+ /* Display the maximum stat (modified) */
+ c_put_str(TERM_L_GREEN, buf, 2 + i, 73);
+ }
+
+ /* Normal treatment of "normal" stats */
+ else
+ {
+ /* Assume uppercase stat name */
+ put_str(format("%s%c ", stat_names[i], punctuation), 2 + i, 61);
+
+ /* Obtain the current stat (modified) */
+ cnv_stat(p_ptr->stat_use[i], buf);
+
+ /* Display the current stat (modified) */
+ c_put_str(TERM_L_GREEN, buf, 2 + i, 66);
+ }
+ }
+
+ /* Extra info */
+ display_player_middle();
+
+ /* Display "history" info */
+ if (mode == 1)
+ {
+ put_str("(Character Background)", 15, 25);
+
+ for (i = 0; i < 4; i++)
+ {
+ put_str(history[i], i + 16, 10);
+ }
+ }
+
+ /* Display "various" info */
+ else
+ {
+ put_str("(Miscellaneous Abilities)", 15, 25);
+
+ display_player_various();
+ }
+ }
+
+ /* Special */
+ else
+ {
+ display_player_ben_one(mode - 2);
+ }
+}
+
+/*
+ * Utility function; should probably be in some other file...
+ *
+ * Describe the player's location -- either by dungeon level, town, or in
+ * wilderness with landmark reference.
+ */
+cptr describe_player_location()
+{
+ int i;
+ static char desc[80];
+ int pwx = (p_ptr->wild_mode ? p_ptr->px : p_ptr->wilderness_x);
+ int pwy = (p_ptr->wild_mode ? p_ptr->py : p_ptr->wilderness_y);
+ int feat = wild_map[pwy][pwx].feat;
+
+ if (dungeon_type != DUNGEON_WILDERNESS && dun_level > 0)
+ sprintf(desc, "on level %d of %s", dun_level, d_info[dungeon_type].name + d_name);
+ else if (wf_info[feat].terrain_idx == TERRAIN_TOWN)
+ sprintf(desc, "in the town of %s", wf_info[feat].name + wf_name);
+ else if (wf_info[feat].entrance)
+ sprintf(desc, "near %s", wf_info[feat].name + wf_name);
+ else
+ {
+ /*
+ * The complicated case. Find the nearest known landmark,
+ * and describe our position relative to that. Note that
+ * we may not even have any known landmarks (for instance,
+ * a Lost Soul character just after escaping the Halls of
+ * Mandos).
+ */
+ int landmark = 0, lwx = 0, lwy = 0;
+ int l_dist = -1;
+ int i;
+
+ for (i = 0; i < max_wf_idx; i++)
+ {
+ int wx = wf_info[i].wild_x;
+ int wy = wf_info[i].wild_y;
+ int dist;
+
+ /* Skip if not a landmark */
+ if (!wf_info[i].entrance) continue;
+
+ /* Skip if we haven't seen it */
+ if (!wild_map[wy][wx].known) continue;
+
+ dist = distance(wy, wx, pwy, pwx);
+ if (dist < l_dist || l_dist < 0)
+ {
+ landmark = i;
+ l_dist = dist;
+ lwx = wx;
+ lwy = wy;
+ }
+ }
+
+ if (!landmark)
+ sprintf(desc, "in %s", wf_info[feat].text + wf_text);
+ else if (pwx == lwx && pwy == lwy)
+ /* Paranoia; this should have been caught above */
+ sprintf(desc, "near %s", wf_info[feat].name + wf_name);
+ else
+ {
+ /*
+ * We split the circle into eight equal octants of
+ * size pi/4 radians; the "east" octant, for
+ * instance, is defined as due east plus or minus
+ * pi/8 radians. Now sin(pi/8) ~= 0.3826 ~= 31/81,
+ * so we check |dx|/|dy| and |dy|/|dx| against that
+ * ratio to determine which octant we're in.
+ */
+ int dx = pwx - lwx;
+ int dy = pwy - lwy;
+ cptr ns = (dy > 0 ? "south" : "north");
+ cptr ew = (dx > 0 ? "east" : "west");
+
+ dx = (dx < 0 ? -dx : dx);
+ dy = (dy < 0 ? -dy : dy);
+ if (dy * 81 < dx * 31) ns = "";
+ if (dx * 81 < dy * 31) ew = "";
+
+ sprintf(desc, "in %s %s%s of %s",
+ wf_info[feat].text + wf_text, ns, ew,
+ wf_info[landmark].name + wf_name);
+ }
+ }
+
+ /* strip trailing whitespace */
+ for (i = 0; desc[i]; ++i);
+ while (desc[--i] == ' ')
+ desc[i] = 0;
+
+ return desc;
+}
+
+/*
+ * Helper function or file_character_print_grid
+ *
+ * Figure out if a row on the grid is empty
+ */
+static bool file_character_print_grid_check_row(const char *buf)
+{
+ if (strstr(buf + 12, "+")) return TRUE;
+ if (strstr(buf + 12, "*")) return TRUE;
+ if (strstr(buf + 12, "1")) return TRUE;
+ if (strstr(buf + 12, "2")) return TRUE;
+ if (strstr(buf + 12, "3")) return TRUE;
+ if (strstr(buf + 12, "4")) return TRUE;
+ if (strstr(buf + 12, "5")) return TRUE;
+ if (strstr(buf + 12, "6")) return TRUE;
+ if (strstr(buf + 12, "7")) return TRUE;
+ if (strstr(buf + 12, "8")) return TRUE;
+ if (strstr(buf + 12, "9")) return TRUE;
+ return FALSE;
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Prints the big ugly grid
+ */
+static void file_character_print_grid(FILE *fff, bool show_gaps, bool show_legend)
+{
+ static cptr blank_line = " ";
+ static char buf[1024];
+ byte a;
+ char c;
+ int x, y;
+
+ y = show_legend ? 3 : 4;
+ for (; y < 23; y++)
+ {
+ for (x = 0; x < 40; x++)
+ {
+ (void)(Term_what(x, y, &a, &c));
+ buf[x] = c;
+ }
+
+ buf[x] = '\0';
+ if (strcmp(buf, blank_line) &&
+ (y == 3 || show_gaps || file_character_print_grid_check_row(buf)))
+ fprintf (fff, " %s\n", buf);
+ }
+ for (y = 4; y < 23; y++)
+ {
+ for (x = 40; x < 80; x++)
+ {
+ (void)(Term_what(x, y, &a, &c));
+ buf[x - 40] = c;
+ }
+
+ buf[x] = '\0';
+ if (strcmp(buf, blank_line) &&
+ (show_gaps || file_character_print_grid_check_row(buf)))
+ fprintf (fff, " %s\n", buf);
+ }
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Outputs one item (for Inventory, Equipment, Home, and Mathom-house)
+ */
+void file_character_print_item(FILE *fff, char label, object_type *obj, bool full)
+{
+ static char o_name[80];
+ static cptr paren = ")";
+ object_desc(o_name, obj, TRUE, 3);
+ fprintf(fff, "%c%s %s\n", label, paren, o_name);
+
+ if ((artifact_p(obj) || ego_item_p(obj) || obj->tval == TV_RING || obj->tval == TV_AMULET || full) &&
+ (obj->ident & IDENT_MENTAL))
+ {
+ object_out_desc(obj, fff, TRUE, TRUE);
+ }
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Prints out one "store" (for Home and Mathom-house)
+ */
+void file_character_print_store(FILE *fff, wilderness_type_info *place, int store, bool full)
+{
+ int i;
+ town_type *town = &town_info[place->entrance];
+ store_type *st_ptr = &town->store[store];
+
+ if (st_ptr->stock_num)
+ {
+ /* Header with name of the town */
+ fprintf(fff, " [%s Inventory - %s]\n\n", st_name + st_info[store].name, wf_name + place->name);
+
+ /* Dump all available items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ file_character_print_item(fff, I2A(i%24), &st_ptr->stock[i], full);
+ }
+
+ /* Add an empty line */
+ fprintf(fff, "\n\n");
+ }
+}
+
+/*
+ * Helper function for file_character
+ *
+ * Checks if the store hasn't been added to the list yet, and then adds it if it
+ * was not already there. XXX This is an ugly workaround for the double Gondolin
+ * problem.
+ *
+ * Beware of the ugly pointer gymnastics.
+ */
+bool file_character_check_stores(store_type ***store_list, int *store_list_count, wilderness_type_info *place, int store)
+{
+ town_type *town = &town_info[place->entrance];
+ store_type *st_ptr = &town->store[store];
+ store_type **head = *store_list;
+ int i;
+
+ /* check the list for this store */
+ for (i = 0; i < *store_list_count; ++i)
+ {
+ if (*head == st_ptr) return FALSE;
+ ++head;
+ }
+
+ /* make room for the new one in the array */
+ if (*store_list)
+ {
+ head = *store_list;
+ *store_list = C_RNEW(*store_list_count + 1, store_type *);
+ C_COPY(*store_list, head, *store_list_count, store_type *);
+ C_FREE(head, *store_list_count, store_type *);
+ }
+ else
+ {
+ *store_list = RNEW(store_type *);
+ }
+
+ /* update data */
+ (*store_list)[*store_list_count] = st_ptr;
+ ++*store_list_count;
+
+ return TRUE;
+}
+
+/*
+ * Hack -- Dump a character description file
+ *
+ * XXX XXX XXX Allow the "full" flag to dump additional info,
+ * and trigger its usage from various places in the code.
+ */
+errr file_character(cptr name, bool full)
+{
+ int i, j, x, y;
+ byte a;
+ char c;
+ int fd = -1;
+ FILE *fff = NULL;
+ char buf[1024];
+ store_type **store_list = NULL;
+ int store_list_count = 0;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Check for existing file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Existing file */
+ if (fd >= 0)
+ {
+ char out_val[160];
+
+ /* Close the file */
+ (void)fd_close(fd);
+
+ /* Build query */
+ (void)sprintf(out_val, "Replace existing file %s? ", buf);
+
+ /* Ask */
+ if (get_check(out_val)) fd = -1;
+ }
+
+ /* Open the non-existing file */
+ if (fd < 0) fff = my_fopen(buf, "w");
+
+ /* Invalid file */
+ if (!fff)
+ {
+ /* Message */
+ msg_format("Character sheet creation failed!");
+ msg_print(NULL);
+
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Begin dump */
+ fprintf(fff, " [%s %ld.%ld.%ld%s Character Sheet]\n\n",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+
+
+ /* Display player */
+ display_player(0);
+
+ /* Dump part of the screen */
+ for (y = 2; y < 22; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < 79; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Display history */
+ display_player(1);
+
+ /* Dump part of the screen */
+ for (y = 15; y < 20; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < 79; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* List the patches */
+ hook_file = fff;
+ exec_lua("patchs_list()");
+
+ fprintf(fff, "\n\n [Miscellaneous information]\n");
+ if (joke_monsters)
+ fprintf(fff, "\n Joke monsters: ON");
+ else
+ fprintf(fff, "\n Joke monsters: OFF");
+
+ if (p_ptr->maximize)
+ fprintf(fff, "\n Maximize mode: ON");
+ else
+ fprintf(fff, "\n Maximize mode: OFF");
+
+ if (p_ptr->preserve)
+ fprintf(fff, "\n Preserve Mode: ON");
+ else
+ fprintf(fff, "\n Preserve Mode: OFF");
+
+ if (auto_scum)
+ fprintf(fff, "\n Autoscum: ON");
+ else
+ fprintf(fff, "\n Autoscum: OFF");
+
+ if (always_small_level)
+ fprintf(fff, "\n Small Levels: ALWAYS");
+ else if (small_levels)
+ fprintf(fff, "\n Small Levels: ON");
+ else
+ fprintf(fff, "\n Small Levels: OFF");
+
+ if (empty_levels)
+ fprintf(fff, "\n Arena Levels: ON");
+ else
+ fprintf(fff, "\n Arena Levels: OFF");
+
+ if (ironman_rooms)
+ fprintf(fff, "\n Always unusual rooms: ON");
+ else
+ fprintf(fff, "\n Always unusual rooms: OFF");
+
+ if (seed_dungeon)
+ fprintf(fff, "\n Persistent Dungeons: ON");
+ else
+ fprintf(fff, "\n Persistent Dungeons: OFF");
+
+ fprintf(fff, "\n\n Recall Depth:");
+ for (y = 1; y < max_d_idx; y++)
+ {
+ if (max_dlv[y])
+ fprintf(fff, "\n %s: Level %d (%d')",
+ d_name + d_info[y].name,
+ max_dlv[y], 50 * (max_dlv[y]));
+ }
+ fprintf(fff, "\n");
+
+ if (noscore)
+ fprintf(fff, "\n You have done something illegal.");
+
+ if (PRACE_FLAGS(PR1_EXPERIMENTAL) || seed_dungeon)
+ fprintf(fff, "\n You have done something experimental.");
+
+ if (stupid_monsters)
+ fprintf(fff, "\n Your opponents are behaving stupidly.");
+
+ {
+ char desc[80];
+ cptr mimic;
+
+ monster_race_desc(desc, p_ptr->body_monster, 0);
+ fprintf(fff, "\n Your body %s %s.", (death ? "was" : "is"), desc);
+
+ if (p_ptr->tim_mimic)
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", p_ptr->mimic_form, "name", &mimic);
+ fprintf(fff, "\n You %s disguised as a %s.", (death ? "were" : "are"), mimic);
+ }
+ }
+
+ /* Where we are, if we're alive */
+ if (!death) fprintf(fff, "\n You are currently %s.", describe_player_location());
+
+ /* Monsters slain */
+ {
+ int k;
+ s32b Total = 0;
+
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool dead = (r_ptr->max_num == 0);
+ if (dead)
+ {
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+
+ if (Total < 1)
+ fprintf(fff, "\n You have defeated no enemies yet.");
+ else if (Total == 1)
+ fprintf(fff, "\n You have defeated one enemy.");
+ else
+ fprintf(fff, "\n You have defeated %lu enemies.", Total);
+ }
+
+ hook_file = fff;
+ process_hooks(HOOK_CHAR_DUMP, "()");
+
+ /* Date */
+ {
+ char buf2[20];
+ u32b days = bst(DAY, turn - (START_DAY * 10));
+
+ strnfmt(buf2, 20, get_day(bst(YEAR, START_DAY * 10) + START_YEAR));
+ fprintf(fff, "\n\n You started your adventure the %s of the %s year of the third age.",
+ get_month_name(bst(DAY, START_DAY * 10), wizard, FALSE), buf2);
+
+ strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR));
+ fprintf(fff, "\n %s the %s of the %s year of the third age.",
+ (death ? "You ended your adventure" : "It is currently"),
+ get_month_name(bst(DAY, turn), wizard, FALSE), buf2);
+ fprintf(fff,
+ (death ? "\n Your adventure lasted %ld day%s." : "\n You have been adventuring for %ld day%s."),
+ days, (days == 1) ? "" : "s");
+ }
+
+ fprintf (fff, "\n\n");
+
+ /* Emit the self-knowledge lines, even though they duplicate the
+ information in the grids (below), because they contain information
+ that's not in the grids (racial abilities, luck, etc.). */
+ if (full)
+ {
+ self_knowledge(fff);
+ fprintf(fff, "\n\n");
+ }
+
+ /* adds and slays */
+ display_player (2);
+ file_character_print_grid(fff, FALSE, TRUE);
+
+ /* sustains and resistances */
+ display_player (3);
+ file_character_print_grid(fff, TRUE, FALSE);
+
+ /* stuff */
+ display_player (4);
+ file_character_print_grid(fff, FALSE, FALSE);
+
+ /* a little bit of stuff */
+ display_player (5);
+ file_character_print_grid(fff, FALSE, FALSE);
+
+ /* Dump corruptions */
+ if (got_corruptions())
+ {
+ fprintf(fff, "\n Corruption list:\n");
+ dump_corruptions(fff, FALSE);
+ }
+
+ /* Dump skills */
+ dump_skills(fff);
+ dump_abilities(fff);
+
+ if (p_ptr->companion_killed)
+ {
+ if (p_ptr->companion_killed == 1)
+ fprintf(fff, "\n One of your companion(s) has been killed.");
+ else
+ fprintf(fff, "\n %d of your companions have been killed.", p_ptr->companion_killed);
+ }
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if ((fates[i].fate) && (fates[i].know))
+ {
+ fprintf(fff, "\n\n [Fates]\n\n");
+ dump_fates(fff);
+ break;
+ }
+ }
+
+ /* Skip some lines */
+ fprintf(fff, "\n\n");
+
+
+ /* Dump the equipment */
+ text_out_indent = 4;
+ if (equip_cnt)
+ {
+ fprintf(fff, " [Character Equipment]\n\n");
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (!p_ptr->body_parts[i - INVEN_WIELD]) continue;
+
+ file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full);
+ }
+ fprintf(fff, "\n\n");
+ }
+
+ /* Dump the inventory */
+ fprintf(fff, " [Character Inventory]\n\n");
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full);
+ }
+ fprintf(fff, "\n\n");
+
+ /* Print all homes in the different towns */
+ for (j = 0; j < max_wf_idx; j++)
+ {
+ if (wf_info[j].feat == FEAT_TOWN &&
+ file_character_check_stores(&store_list, &store_list_count, &wf_info[j], 7))
+ file_character_print_store(fff, &wf_info[j], 7, full);
+ }
+ store_list = C_FREE(store_list, store_list_count, store_type *);
+ store_list_count = 0;
+
+ /* Print all Mathom-houses in the different towns */
+ for (j = 0; j < max_wf_idx; j++)
+ {
+ if (wf_info[j].feat == FEAT_TOWN &&
+ file_character_check_stores(&store_list, &store_list_count, &wf_info[j], 57))
+ file_character_print_store(fff, &wf_info[j], 57, full);
+ }
+ store_list = C_FREE(store_list, store_list_count, store_type *);
+ store_list_count = 0;
+
+ text_out_indent = 0;
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Character sheet creation successful.");
+ msg_print(NULL);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Recursive file perusal.
+ *
+ * Return FALSE on "ESCAPE", otherwise TRUE.
+ *
+ * Process various special text in the input file, including
+ * the "menu" structures used by the "help file" system.
+ *
+ * XXX XXX XXX Consider using a temporary file.
+ *
+ * XXX XXX XXX Allow the user to "save" the current file.
+ */
+
+/*
+ * A structure to hold (some of == XXX) the hyperlink information.
+ * This prevents excessive use of stack.
+ */
+#define MAX_LINKS 1024
+struct hyperlink
+{
+ /* Path buffer */
+ char path[1024];
+
+ /* General buffer */
+ char rbuf[1024];
+
+ /* Hold a string to find */
+ char finder[81];
+
+ /* Hold a string to show */
+ char shower[81];
+
+ /* Describe this thing */
+ char caption[128];
+
+ /* Hypertext info */
+ char link[MAX_LINKS][32], link_key[MAX_LINKS];
+ int link_x[MAX_LINKS], link_y[MAX_LINKS], link_line[MAX_LINKS];
+};
+
+typedef struct hyperlink hyperlink_type;
+
+bool show_file(cptr name, cptr what, int line, int mode)
+{
+ int i, k, x;
+
+ byte link_color = TERM_ORANGE, link_color_sel = TERM_YELLOW;
+
+ /* Number of "real" lines passed by */
+ int next = 0;
+
+ /* Number of "real" lines in the file */
+ int size = 0;
+
+ /* Backup value for "line" */
+ int back = 0;
+
+ /* Color of the next line */
+ byte color = TERM_WHITE;
+
+ /* This screen has sub-screens */
+ bool menu = FALSE;
+
+ /* Current help file */
+ FILE *fff = NULL;
+
+ /* Find this string (if any) */
+ cptr find = NULL;
+
+ /* Char array type of hyperlink info */
+ hyperlink_type *h_ptr;
+
+ /* Pointer to general buffer in the above */
+ char *buf;
+
+ int cur_link = 0, max_link = 0;
+
+ /* Read size of screen for big-screen stuff -pav- */
+ int wid, hgt;
+
+ /* Allocate hyperlink data */
+ MAKE(h_ptr, hyperlink_type);
+
+ /* Setup buffer pointer */
+ buf = h_ptr->rbuf;
+
+ /* Wipe the links */
+ for (i = 0; i < MAX_LINKS; i++)
+ {
+ h_ptr->link_x[i] = -1;
+ }
+
+ /* Hack XXX XXX XXX */
+ if (what)
+ {
+ /* h_ptr->caption */
+ strcpy(h_ptr->caption, what);
+
+ /* Access the "file" */
+ strcpy(h_ptr->path, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+ }
+
+ /* Look in "help" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Help file '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+ }
+
+ /* Look in "info" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Info file '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_INFO, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+ }
+
+ /* Look in "file" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "File '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_FILE, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+ }
+
+ /* Oops */
+ if (!fff)
+ {
+ /* Message */
+ msg_format("Cannot open '%s'.", name);
+ msg_print(NULL);
+
+ /* Free hyperlink info */
+ KILL(h_ptr, hyperlink_type);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+
+ /* Pre-Parse the file */
+ while (TRUE)
+ {
+ /* Read a line or stop */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Get the link colors */
+ if (prefix(buf, "|||||"))
+ {
+ link_color = color_char_to_attr(buf[5]);
+ link_color_sel = color_char_to_attr(buf[6]);
+ }
+
+ /* Tag ? */
+ if (prefix(buf, "~~~~~"))
+ {
+ if (line < 0)
+ {
+ int i;
+ char old_c;
+
+ for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++)
+ ;
+ old_c = buf[i];
+ buf[i] = '\0';
+
+ if (atoi(buf + 5) == -line)
+ {
+ line = next + 1;
+ }
+ buf[i] = old_c;
+ }
+ }
+
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5, stmp, xdeb = x + 5, z;
+ char tmp[20];
+
+ for (z = 0; z < 20; z++) tmp[z] = '\0';
+
+ h_ptr->link_x[max_link] = x;
+ h_ptr->link_y[max_link] = next;
+
+ if (buf[xx] == '/')
+ {
+ xx++;
+ h_ptr->link_key[max_link] = buf[xx];
+ xx++;
+ xdeb += 2;
+ }
+ else
+ {
+ h_ptr->link_key[max_link] = 0;
+ }
+
+ /* Zap the link info */
+ while (buf[xx] != '*')
+ {
+ h_ptr->link[max_link][xx - xdeb] = buf[xx];
+ xx++;
+ }
+ h_ptr->link[max_link][xx - xdeb] = '\0';
+ xx++;
+ stmp = xx;
+ while (buf[xx] != '[')
+ {
+ tmp[xx - stmp] = buf[xx];
+ xx++;
+ }
+ xx++;
+ tmp[xx - stmp] = '\0';
+ h_ptr->link_line[max_link] = -atoi(tmp);
+ max_link++;
+ }
+ x++;
+ }
+
+ /* Count the "real" lines */
+ next++;
+ }
+
+ /* Save the number of "real" lines */
+ size = next;
+
+
+
+ /* Display the file */
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ Term_get_size(&wid, &hgt);
+
+ /* Restart when necessary */
+ if (line >= size) line = 0;
+
+
+ /* Re-open the file if needed */
+ if (next > line)
+ {
+ /* Close it */
+ my_fclose(fff);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Hack -- Re-Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+
+ /* Oops */
+ if (!fff)
+ {
+ /* Free hyperlink info */
+ KILL(h_ptr, hyperlink_type);
+
+ return (FALSE);
+ }
+
+ /* File has been restarted */
+ next = 0;
+ }
+
+ /* Skip lines if needed */
+ for (; next < line; next++)
+ {
+ /* Skip a line */
+ if (my_fgets(fff, buf, 1024)) break;
+ }
+
+
+ /* Dump the next 20 (or more in bigscreen) lines of the file */
+ for (i = 0; i < (hgt - 4); )
+ {
+ int print_x;
+
+ /* Hack -- track the "first" line */
+ if (!i) line = next;
+
+ /* Get a line of the file or stop */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ color = color_char_to_attr(h_ptr->rbuf[5]);
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Count the "real" lines */
+ next++;
+
+ /* Skip link colors */
+ if (prefix(buf, "|||||")) continue;
+
+ /* Skip tags */
+ if (prefix(buf, "~~~~~"))
+ {
+ i++;
+ continue;
+ }
+
+ /* Hack -- keep searching */
+ if (find && !i && !strstr(buf, find)) continue;
+
+ /* Hack -- stop searching */
+ find = NULL;
+
+ /* Be sure to get a correct cur_link */
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4))
+ {
+ while ((cur_link > 0) && (h_ptr->link_y[cur_link] >= line + (hgt - 4)))
+ {
+ cur_link--;
+ }
+ }
+ if (h_ptr->link_y[cur_link] < line)
+ {
+ while ((cur_link < max_link) && (h_ptr->link_y[cur_link] < line))
+ {
+ cur_link++;
+ }
+ }
+
+ /* Dump the line */
+ print_x = 0;
+ if (!prefix(buf, "&&&&&"))
+ {
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5;
+
+ /* Zap the link info */
+ while (buf[xx] != '[')
+ {
+ xx++;
+ }
+ xx++;
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ byte color = link_color;
+
+ if ((h_ptr->link_x[cur_link] == x) && (h_ptr->link_y[cur_link] == line + i))
+ color = link_color_sel;
+
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+
+ Term_putch(print_x, i + 2, color, buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+ }
+ /* Color ? */
+ else if (prefix(buf + x, "[[[[["))
+ {
+ int xx = x + 6;
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ Term_putch(print_x, i + 2, color_char_to_attr(buf[x + 5]), buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+ }
+ /* Remove HTML ? */
+ else if (prefix(buf + x, "{{{{{"))
+ {
+ int xx = x + 6;
+
+ /* Ok remove this section */
+ while (buf[xx] != '}')
+ {
+ xx++;
+ }
+ x = xx;
+ }
+ else
+ {
+ Term_putch(print_x, i + 2, color, buf[x]);
+ print_x++;
+ }
+
+ x++;
+ }
+ }
+ /* Verbatim mode: i.e: acacacac */
+ else
+ {
+ x = 5;
+ while (buf[x])
+ {
+ Term_putch(print_x, i + 2, color_char_to_attr(buf[x]), buf[x + 1]);
+ print_x++;
+ x += 2;
+ }
+ }
+ color = TERM_WHITE;
+
+ /* Hilite "h_ptr->shower" */
+ if (h_ptr->shower[0])
+ {
+ cptr str = buf;
+
+ /* Display matches */
+ while ((str = strstr(str, h_ptr->shower)) != NULL)
+ {
+ int len = strlen(h_ptr->shower);
+
+ /* Display the match */
+ Term_putstr(str - buf, i + 2, len, TERM_YELLOW, h_ptr->shower);
+
+ /* Advance */
+ str += len;
+ }
+ }
+
+ /* Count the printed lines */
+ i++;
+ }
+
+ /* Hack -- failed search */
+ if (find)
+ {
+ bell();
+ line = back;
+ find = NULL;
+ continue;
+ }
+
+
+ /* Show a general "title" */
+ prt(format("[%s %ld.%ld.%ld, %s, Line %d/%d]", game_module,
+ VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH,
+ h_ptr->caption, line, size), 0, 0);
+
+ /* Prompt -- menu screen */
+ if (menu)
+ {
+ /* Wait for it */
+ prt("[Press a Number, or ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Prompt -- small files */
+ else if (size <= (hgt - 4))
+ {
+ /* Wait for it */
+ prt("[Press ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Prompt -- large files */
+ else
+ {
+ /* Wait for it */
+ prt("[Press 2, 8, 4, 6, /, =, #, %, backspace, or ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Get a keypress */
+ k = inkey();
+
+ /* Hack -- return to last screen */
+ if ((k == '?') || (k == 0x7F) || (k == '\010')) break;
+
+ /* Hack -- try showing */
+ if (k == '=')
+ {
+ /* Get "h_ptr->shower" */
+ prt("Show: ", hgt - 1, 0);
+ (void)askfor_aux(h_ptr->shower, 80);
+ }
+
+ /* Hack -- try finding */
+ if (k == '/')
+ {
+ /* Get "h_ptr->finder" */
+ prt("Find: ", hgt - 1, 0);
+ if (askfor_aux(h_ptr->finder, 80))
+ {
+ /* Find it */
+ find = h_ptr->finder;
+ back = line;
+ line = line + 1;
+
+ /* Show it */
+ strcpy(h_ptr->shower, h_ptr->finder);
+ }
+ }
+
+ /* Hack -- go to a specific line */
+ if (k == '#')
+ {
+ char tmp[81];
+ prt("Goto Line: ", hgt - 1, 0);
+ strcpy(tmp, "0");
+ if (askfor_aux(tmp, 80))
+ {
+ line = atoi(tmp);
+ }
+ }
+
+ /* Hack -- go to a specific file */
+ if (k == '%')
+ {
+ char tmp[81];
+ prt("Goto File: ", hgt - 1, 0);
+ strcpy(tmp, "help.hlp");
+ if (askfor_aux(tmp, 80))
+ {
+ if (!show_file(tmp, NULL, 0, mode)) k = ESCAPE;
+ }
+ }
+
+ /* Hack -- Allow backing up */
+ if (k == '-')
+ {
+ line = line - (hgt - 4);
+ if (line < 0) line = 0;
+ }
+
+ if (k == '8')
+ {
+ line--;
+ if (line < 0) line = 0;
+ }
+
+ /* Hack -- Advance a single line */
+ if (k == '2')
+ {
+ line = line + 1;
+ }
+
+ /* Advance one page */
+ if (k == ' ')
+ {
+ line = line + (hgt - 4);
+ }
+
+ /* Advance one link */
+ if ((k == '6') || (k == '\t'))
+ {
+ cur_link++;
+ if (cur_link >= max_link) cur_link = max_link - 1;
+
+ if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link];
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4);
+ }
+ /* Return one link */
+ if (k == '4')
+ {
+ cur_link--;
+ if (cur_link < 0) cur_link = 0;
+
+ if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link];
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4);
+ }
+
+ /* Recurse on numbers */
+ if (k == '\r')
+ {
+ if (h_ptr->link_x[cur_link] != -1)
+ {
+ /* Recurse on that file */
+ if (!show_file(h_ptr->link[cur_link], NULL, h_ptr->link_line[cur_link], mode)) k = ESCAPE;
+ }
+ }
+
+ /* Exit on escape */
+ if (k == ESCAPE) break;
+
+ /* No other key ? lets look for a shortcut */
+ for (i = 0; i < max_link; i++)
+ {
+ if (h_ptr->link_key[i] == k)
+ {
+ /* Recurse on that file */
+ if (!show_file(h_ptr->link[i], NULL, h_ptr->link_line[i], mode)) k = ESCAPE;
+ break;
+ }
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Free hyperlink buffers */
+ KILL(h_ptr, hyperlink_type);
+
+ /* Escape */
+ if (k == ESCAPE) return (FALSE);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+bool txt_to_html(cptr head, cptr foot, cptr base, cptr ext, bool force, bool recur)
+{
+ int i, x;
+
+ /* Number of "real" lines passed by */
+ int next = 0;
+
+ /* Number of "real" lines in the file */
+ int size = 0;
+
+ char buf_name[80];
+
+ /* Color of the next line */
+ byte color = TERM_WHITE;
+
+ /* Current help file */
+ FILE *fff = NULL;
+
+ /* Current aux file */
+ FILE *aux = NULL;
+
+ /* Current html file */
+ FILE *htm = NULL;
+
+ /* Char array type of hyperlink info */
+ hyperlink_type *h_ptr;
+
+ cptr file_ext;
+ cptr link_prefix;
+ cptr link_suffix;
+
+ /* Pointer to general buffer in the above */
+ char *buf;
+
+ /* Allocate hyperlink data */
+ MAKE(h_ptr, hyperlink_type);
+
+ /* Setup buffer pointer */
+ buf = h_ptr->rbuf;
+
+ /* Wipe the links */
+ for (i = 0; i < MAX_LINKS; i++)
+ {
+ h_ptr->link_x[i] = -1;
+ }
+
+ /* Parse it(yeah lua is neat :) */
+ tome_dofile_anywhere(ANGBAND_DIR_HELP, "def.aux", TRUE);
+
+ /* Ok now get the parameters */
+ file_ext = string_exec_lua("return file_ext");
+ link_prefix = string_exec_lua("return link_prefix");
+ link_suffix = string_exec_lua("return link_suffix");
+
+ sprintf(buf_name, "%s.%s", base, file_ext);
+
+ if ((!force) && file_exist(buf_name)) return FALSE;
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ htm = my_fopen(h_ptr->path, "w");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ sprintf(buf_name, "%s.%s", base, ext);
+
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Help file '%s'", buf_name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Oops */
+ if (!fff || !htm)
+ {
+ /* Free hyperlink info */
+ KILL(h_ptr, hyperlink_type);
+
+ my_fclose(fff);
+ my_fclose(htm);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+ /* Save the number of "real" lines */
+ size = next;
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, head);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ aux = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Copy the header */
+ if (aux)
+ {
+ while (TRUE)
+ {
+ char *find;
+
+ if (my_fgets(aux, h_ptr->rbuf, 1024)) break;
+ find = strstr(h_ptr->rbuf, "%t");
+ if (find != NULL)
+ {
+ *find = '\0';
+ find += 2;
+ fprintf(htm, "%s", h_ptr->rbuf);
+ fprintf(htm, "%s", base);
+ fprintf(htm, "%s\n", find);
+ }
+ else
+ fprintf(htm, "%s\n", h_ptr->rbuf);
+ }
+ my_fclose(aux);
+ }
+
+ /* Display the file */
+ while (TRUE)
+ {
+ bool do_color = FALSE;
+
+ /* Skip a line */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ color = TERM_WHITE;
+
+ {
+ int print_x;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ color = color_char_to_attr(h_ptr->rbuf[5]);
+ do_color = TRUE;
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Count the "real" lines */
+ next++;
+
+ /* Skip link colors */
+ if (prefix(buf, "|||||")) continue;
+
+ /* Skip tags */
+ if (prefix(buf, "~~~~~"))
+ {
+ int i;
+
+ for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++)
+ ;
+ buf[i] = '\0';
+ fprintf(htm, "<A NAME=\"%s\"></A>", buf + 5);
+ continue;
+ }
+
+ /* Dump the line */
+ print_x = 0;
+ if (!prefix(buf, "&&&&&"))
+ {
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5, z = 0;
+ char buff[80];
+ char link_line[80], *s;
+
+ if (buf[xx] == '/') xx += 2;
+
+ /* Zap the link info */
+ while (buf[xx] != '*')
+ {
+ buff[z++] = buf[xx];
+ xx++;
+ }
+ xx++;
+ buff[z] = '\0';
+
+ /* Zap the link info */
+ z = 0;
+ while (buf[xx] != '[')
+ {
+ link_line[z++] = buf[xx];
+ xx++;
+ }
+ xx++;
+ link_line[z] = '\0';
+
+ /* parse it */
+ s = buff;
+ while (*s != '.') s++;
+ *s = '\0';
+ s++;
+ if (recur) txt_to_html(head, foot, buff, s, FALSE, recur);
+
+ if (atoi(link_line)) fprintf(htm, "<A HREF=\"%s%s.%s%s#%d\">", link_prefix, buff, file_ext, link_suffix, atoi(link_line));
+ else fprintf(htm, "<A HREF=\"%s%s.%s%s\">", link_prefix, buff, file_ext, link_suffix);
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+
+ fprintf(htm, "</A>");
+ }
+ /* Color ? */
+ else if (prefix(buf + x, "[[[[["))
+ {
+ int xx = x + 6;
+
+ color = color_char_to_attr(buf[x + 5]);
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x++;
+ x = xx;
+
+ fprintf(htm, "</FONT>");
+ }
+ /* Hidden HTML tag? */
+ else if (prefix(buf + x, "{{{{{"))
+ {
+ int xx = x + 5;
+
+ /* Ok output the tag inside */
+ while (buf[xx] != '}')
+ {
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ }
+ x++;
+ x = xx;
+ }
+ else
+ {
+ fprintf(htm, "%c", buf[x]);
+ print_x++;
+ }
+
+ x++;
+ }
+ }
+ /* Verbatim mode: i.e: acacacac */
+ else
+ {
+ byte old_color;
+
+ x = 5;
+ old_color = color_char_to_attr(buf[x]);
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+ while (buf[x])
+ {
+ color = color_char_to_attr(buf[x]);
+ if (color != old_color)
+ fprintf(htm, "</FONT><FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+
+ fprintf(htm, "%c", buf[x + 1]);
+ print_x++;
+ x += 2;
+ }
+ fprintf(htm, "</FONT>");
+ }
+ }
+ if (do_color)
+ {
+ fprintf(htm, "</FONT>");
+ }
+ fprintf(htm, "\n");
+ }
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, foot);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ aux = my_fopen(h_ptr->path, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Copy the footer */
+ if (aux)
+ {
+ while (TRUE)
+ {
+ if (my_fgets(aux, h_ptr->rbuf, 1024)) break;
+ fprintf(htm, "%s\n", h_ptr->rbuf);
+ }
+ my_fclose(aux);
+ }
+
+ /* Close the file */
+ my_fclose(htm);
+ my_fclose(fff);
+
+ /* Free hyperlink buffers */
+ KILL(h_ptr, hyperlink_type);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+/* Take an help file screenshot(yes yes I know..) */
+void help_file_screenshot(cptr name)
+{
+ int y, x;
+ int wid, hgt;
+
+ byte a = 0;
+ char c = ' ';
+
+ FILE *htm;
+
+ char buf[1024];
+
+ /* The terms package supports up to 255x255 screen size */
+ char abuf[256];
+ char cbuf[256];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ htm = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!htm) return;
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Dump each row */
+ fprintf(htm, "&&&&&");
+ for (x = 0; x < wid; x++)
+ {
+ a = abuf[x];
+ c = cbuf[x];
+
+ fprintf(htm, "%c%c", a, c);
+ }
+
+ /* End the row */
+ fprintf(htm, "\n");
+ }
+
+ /* Close it */
+ my_fclose(htm);
+}
+
+/* Take an html screenshot */
+void html_screenshot(cptr name)
+{
+ int y, x;
+ int wid, hgt;
+
+ byte a = 0, oa = TERM_WHITE;
+ char c = ' ';
+
+ FILE *htm;
+
+ char buf[1024];
+
+ /* The terms package supports up to 255x255 screen size */
+ char abuf[256];
+ char cbuf[256];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Append to the file */
+ htm = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!htm) return;
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ fprintf(htm, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ "<head>\n");
+ fprintf(htm, "<meta name=\"GENERATOR\" content=\"%s %ld.%ld.%ld\"/>\n",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
+ fprintf(htm, "<title>%s</title>\n", name);
+ fprintf(htm, "</head>\n"
+ "<body>\n"
+ "<pre style=\"color: #ffffff; background-color: #000000; font-family: monospace\">\n");
+ fprintf(htm, "<span style=\"color: #%02X%02X%02X\">\n",
+ angband_color_table[TERM_WHITE][1],
+ angband_color_table[TERM_WHITE][2],
+ angband_color_table[TERM_WHITE][3]);
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ a = color_char_to_attr(abuf[x]);
+ c = cbuf[x];
+
+ if (oa != a)
+ {
+ fprintf(htm, "</span><span style=\"color: #%02X%02X%02X\">", angband_color_table[a][1], angband_color_table[a][2], angband_color_table[a][3]);
+ oa = a;
+ }
+ if (c == '<')
+ fprintf(htm, "&lt;");
+ else if (c == '>')
+ fprintf(htm, "&gt;");
+ else if (c == '&')
+ fprintf(htm, "&amp;");
+ else
+ fprintf(htm, "%c", c);
+ }
+
+ /* End the row */
+ fprintf(htm, "\n");
+ }
+ fprintf(htm, "</span>\n"
+ "</pre>\n"
+ "</body>\n"
+ "</html>\n");
+
+ /* Close it */
+ my_fclose(htm);
+}
+
+/*
+ * Because this is dead code and hardly anyone but DG needs it.
+ * IMHO this should never been included in the game code -- pelpel
+ */
+#if !defined(WINDOWS) && !defined(MACINTOSH) && !defined(ACORN)
+
+#define KEY_NUM 9
+static int keys_tab[KEY_NUM] =
+{
+ 'I', 'G', 'M', 'O', 'P', 'm', 'D', 'B', 'V',
+};
+
+static cptr keys_desc[KEY_NUM] =
+{
+ "Interface changes:",
+ "Gameplay changes:",
+ "Monster changes:",
+ "Object changes:",
+ "Player changes:",
+ "Misc changes:",
+ "Dungeon changes:",
+ "Bug fixes:",
+ "Version:",
+};
+
+static int get_key(char c)
+{
+ int i;
+
+ i = 0;
+ while (keys_tab[i] != c)
+ i++;
+ return ((i > KEY_NUM) ? KEY_NUM : i);
+}
+
+/*
+ * Some ports don't like huge stacks
+ */
+typedef char chg_type[500][100];
+
+bool chg_to_txt(cptr base, cptr newname)
+{
+ int i, j, key = 0;
+
+ char buf[1024];
+
+ int lens[KEY_NUM] = {0, 0, 0, 0, 0, 0, 0, 0};
+ chg_type *strs;
+
+ /* Current chg file */
+ FILE *fff = NULL;
+
+ /* Current txt file */
+ FILE *txt = NULL;
+
+ /* Open the file */
+ fff = my_fopen(base, "r");
+
+ /* Oops */
+ if (!fff)
+ {
+ my_fclose(fff);
+ my_fclose(txt);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+ /* Count the file */
+ while (TRUE)
+ {
+ /* Skip a line */
+ if (my_fgets(fff, buf, 1024)) break;
+
+ if ((!(*buf)) ||
+ ((buf[0] >= '0') && (buf[0] <= '9')) || (buf[0] == '#')) continue;
+
+ if (buf[1] != ' ')
+ lens[get_key(buf[1])]++;
+ }
+
+ /* Open the file */
+ txt = my_fopen(newname, "w");
+
+ /* Open the file */
+ fff = my_fopen(base, "r");
+
+ /* Oops */
+ if (!fff || !txt)
+ {
+ my_fclose(fff);
+ my_fclose(txt);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+ for (i = 0; i < KEY_NUM; i++) lens[i] = 0;
+
+ /* Allocate big amount of temporary storage */
+ C_MAKE(strs, KEY_NUM, chg_type);
+
+ /* Display the file */
+ while (TRUE)
+ {
+ /* Skip a line */
+ if (my_fgets(fff, buf, 1024)) break;
+
+ if ((!(*buf)) ||
+ ((buf[0] >= '0') && (buf[0] <= '9')) || (buf[0] == '#')) continue;
+
+ if (buf[1] != ' ') key = get_key(buf[1]);
+
+ if (key == KEY_NUM - 1)
+ strcpy(strs[key][lens[key]++], buf + 5);
+ else
+ strcpy(strs[key][lens[key]++], buf + 3);
+ }
+
+ fprintf(txt, "%s changes\n", strs[KEY_NUM - 1][0]);
+
+ for (i = 0; i < KEY_NUM - 1; i++)
+ {
+ if (lens[i]) fprintf(txt, "\n%s\n", keys_desc[i]);
+
+ for (j = 0; j < lens[i]; j++)
+ {
+ fprintf(txt, "%s\n", strs[i][j]);
+ }
+ }
+
+ /* Close the file */
+ my_fclose(txt);
+ my_fclose(fff);
+
+ /* Free temporary memory */
+ C_FREE(strs, KEY_NUM, chg_type);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+#endif /* !WINDOWS && !MACINTOSH && !ACORN */
+
+/*
+ * Peruse the On-Line-Help
+ */
+void do_cmd_help(void)
+{
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the main help file */
+ (void)show_file("help.hlp", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+}
+
+
+
+
+/*
+ * Process the player name.
+ * Extract a clean "base name".
+ * Build the savefile name if needed.
+ */
+void process_player_base()
+{
+ char temp[128];
+
+#if defined(SAVEFILE_USE_UID) && !defined(PRIVATE_USER_PATH)
+ /* Rename the savefile, using the player_uid and player_base */
+ (void)sprintf(temp, "%d.%s", player_uid, player_base);
+#else
+ /* Rename the savefile, using the player_base */
+ (void)sprintf(temp, "%s", player_base);
+#endif
+
+#ifdef VM
+ /* Hack -- support "flat directory" usage on VM/ESA */
+ (void)sprintf(temp, "%s.sv", player_base);
+#endif /* VM */
+
+ /* Build the filename */
+ path_build(savefile, 1024, ANGBAND_DIR_SAVE, temp);
+}
+
+void process_player_name(bool sf)
+{
+ int i, k = 0;
+ char tmp[50];
+
+ /* Cannot be too long */
+ if (strlen(player_base) > 15)
+ {
+ /* Name too long */
+ quit_fmt("The name '%s' is too long!", player_base);
+ }
+
+ /* Cannot contain "icky" characters */
+ for (i = 0; player_base[i]; i++)
+ {
+ /* No control characters */
+ if (iscntrl(player_base[i]))
+ {
+ /* Illegal characters */
+ quit_fmt("The name '%s' contains control chars!", player_base);
+ }
+ }
+
+
+#ifdef MACINTOSH
+
+ /* Extract "useful" letters */
+ for (i = 0; player_base[i]; i++)
+ {
+ char c = player_base[i];
+
+ /* Convert "dot" to "underscore" */
+ if (c == '@.') c = '_';
+
+ /* Accept all the letters */
+ tmp[k++] = c;
+ }
+
+#else
+
+ /* Extract "useful" letters */
+ for (i = 0; player_base[i]; i++)
+ {
+ char c = player_base[i];
+
+ /* Accept some letters */
+ if (isalpha(c) || isdigit(c)) tmp[k++] = c;
+
+ /* Convert space, dot, and underscore to underscore */
+ else if (strchr("@. _", c)) tmp[k++] = '_';
+ }
+
+#endif
+
+
+#if defined(WINDOWS) || defined(MSDOS)
+
+ /* Hack -- max length */
+ if (k > 8) k = 8;
+
+#endif
+
+ /* Terminate */
+ tmp[k] = '\0';
+ sprintf(player_base, tmp);
+
+ /* Require a "base" name */
+ if (!player_base[0]) strcpy(player_base, "PLAYER");
+
+
+#ifdef SAVEFILE_MUTABLE
+
+ /* Accept */
+ sf = TRUE;
+
+#endif
+
+ /* Change the savefile name */
+ if (sf)
+ {
+ process_player_base();
+ }
+}
+
+
+/*
+ * Gets a name for the character, reacting to name changes.
+ *
+ * Assumes that "display_player(0)" has just been called
+ *
+ * Perhaps we should NOT ask for a name (at "birth()") on
+ * Unix machines? XXX XXX
+ *
+ * What a horrible name for a global function. XXX XXX XXX
+ */
+void get_name(void)
+{
+ char tmp[32];
+
+ /* Clear last line */
+ clear_from(22);
+
+ /* Prompt and ask */
+ prt("[Enter your player's name above, or hit ESCAPE]", 23, 2);
+
+ /* Ask until happy */
+ while (1)
+ {
+ /* Go to the "name" field */
+ move_cursor(2, 9);
+
+ /* Save the player name */
+ strcpy(tmp, player_name);
+
+ /* Get an input, ignore "Escape" */
+ if (askfor_aux(tmp, 31)) strcpy(player_name, tmp);
+
+ /* Process the player name */
+ process_player_name(FALSE);
+
+ /* All done */
+ break;
+ }
+
+ /* Pad the name (to clear junk) */
+ sprintf(tmp, "%-31.31s", player_name);
+
+ /* Re-Draw the name (in light blue) */
+ c_put_str(TERM_L_BLUE, tmp, 2, 9);
+
+ /* Erase the prompt, etc */
+ clear_from(22);
+}
+
+
+
+/*
+ * Hack -- commit suicide
+ */
+void do_cmd_suicide(void)
+{
+ int i;
+
+ /* Flush input */
+ flush();
+
+ /* Verify Retirement */
+ if (total_winner)
+ {
+ /* Verify */
+ if (!get_check("Do you want to retire? ")) return;
+ }
+
+ /* Verify Suicide */
+ else
+ {
+ /* Verify */
+ if (!get_check("Do you really want to quit? ")) return;
+
+ if (!noscore)
+ {
+ /* Special Verification for suicide */
+ prt("Please verify QUITTING by typing the '@' sign: ", 0, 0);
+ flush();
+ i = inkey();
+ prt("", 0, 0);
+ if (i != '@') return;
+ }
+ }
+
+ /* Stop playing */
+ alive = FALSE;
+
+ /* Kill the player */
+ death = TRUE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Cause of death */
+ (void)strcpy(died_from, "Quitting");
+}
+
+
+ /* HACK - Remove / set the CAVE_VIEW flag, since view_x / view_y
+ * is not saved, and the visible locations are not lighted correctly
+ * when the game is loaded again
+ * Alternatively forget_view() and update_view() can be used
+ */
+void remove_cave_view(bool remove)
+{
+ int i;
+ cave_type *c_ptr;
+
+ if (view_n)
+ {
+ /* Clear them all */
+ for (i = 0; i < view_n; i++)
+ {
+ int y = view_y[i];
+ int x = view_x[i];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ if (remove)
+ c_ptr->info &= ~(CAVE_VIEW);
+ else
+ c_ptr->info |= (CAVE_VIEW);
+ }
+ }
+}
+
+/*
+ * Save the game
+ */
+void do_cmd_save_game(void)
+{
+ panic_save = 0; /* Fixes an apparently long-lived bug */
+
+ remove_cave_view(TRUE);
+
+ /* Save the current level if in a persistent level */
+ save_dungeon();
+
+ /* Autosaves do not disturb */
+ if (!is_autosave)
+ {
+ /* Disturb the player */
+ disturb(1, 0);
+ }
+
+ /* Clear messages */
+ msg_print(NULL);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Message */
+ prt("Saving game...", 0, 0);
+
+ /* Refresh */
+ Term_fresh();
+
+ /* The player is not dead */
+ (void)strcpy(died_from, "(saved)");
+
+ /* Forbid suspend */
+ signals_ignore_tstp();
+
+ /* Save the player */
+ if (save_player())
+ {
+ prt("Saving game... done.", 0, 0);
+ }
+
+ /* Save failed (oops) */
+ else
+ {
+ prt("Saving game... failed!", 0, 0);
+ }
+
+ remove_cave_view(FALSE);
+
+ /* Allow suspend again */
+ signals_handle_tstp();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Note that the player is not dead */
+ (void)strcpy(died_from, "(alive and well)");
+}
+
+
+
+/*
+ * Hack -- Calculates the total number of points earned -JWT-
+ */
+long total_points(void)
+{
+#if 0 /* Old calculation */
+ s16b max_dl = 0, i;
+
+ for (i = 0; i < max_d_idx; i++)
+ if (max_dlv[i] > max_dl)
+ max_dl = max_dlv[i];
+
+ return (p_ptr->max_exp + (100 * max_dl));
+#else /* New calculation */
+ s16b max_dl = 0, i, k;
+ long temp, Total = 0;
+ long mult = 20; /* was 100. Divided values by 5 because of an overflow error */
+ long comp_death = (p_ptr->companion_killed * 2 / 5);
+
+ if (!comp_death) comp_death = 1;
+
+ if (p_ptr->preserve) mult -= 1; /* Penalize preserve, maximize modes */
+ if (p_ptr->maximize) mult -= 1;
+ if (auto_scum) mult -= 4;
+ if (stupid_monsters) mult -= 10;
+ if (small_levels) mult += ((always_small_level) ? 4 : 10);
+ if (empty_levels) mult += 2;
+ if (smart_learn) mult += 4;
+ if (smart_cheat) mult += 4;
+
+ if (mult < 2) mult = 2; /* At least 10% of the original score */
+ /* mult is now between 2 and 40, i.e. 10% and 200% */
+
+ for (i = 0; i < max_d_idx; i++)
+ if (max_dlv[i] > max_dl)
+ max_dl = max_dlv[i];
+
+ temp = p_ptr->lev * p_ptr->lev * p_ptr->lev * p_ptr->lev + (100 * max_dl);
+
+ temp += p_ptr->max_exp / 5;
+
+ temp = (temp * mult / 20);
+
+ /* Gold increases score */
+ temp += p_ptr->au / 5;
+
+ /* Completing quest increase score */
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (quest[i].status >= QUEST_STATUS_COMPLETED)
+ {
+ temp += 2000;
+ temp += quest[i].level * 100;
+ }
+ }
+
+ /* Death of a companion is BAD */
+ temp /= comp_death;
+
+ /* The know objects increase the score */
+ /* Scan the object kinds */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Hack -- skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* List known flavored objects */
+ if (k_ptr->flavor && k_ptr->aware)
+ {
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Create fake object */
+ object_prep(i_ptr, k);
+
+ temp += object_value_real(i_ptr);
+ }
+ }
+
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool dead = (r_ptr->max_num == 0);
+
+ if (dead)
+ {
+ /* Uniques are supposed to be harder */
+ Total += 50;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+ temp += Total * 50;
+
+ temp += total_bounties * 100;
+
+ if (total_winner) temp += 1000000;
+
+
+
+ return (temp);
+#endif
+}
+
+
+
+/*
+ * Centers a string within a 31 character string -JWT-
+ */
+static void center_string(char *buf, cptr str)
+{
+ int i, j;
+
+ /* Total length */
+ i = strlen(str);
+
+ /* Necessary border */
+ j = 15 - i / 2;
+
+ /* Mega-Hack */
+ (void)sprintf(buf, "%*s%s%*s", j, "", str, 31 - i - j, "");
+}
+
+
+/*
+ * Redefinable "print_tombstone" action
+ */
+bool (*tombstone_aux)(void) = NULL;
+
+
+/*
+ * Display a "tomb-stone"
+ */
+static void print_tomb(void)
+{
+ bool done = FALSE;
+
+ /* Do we use a special tombstone ? */
+ if (tombstone_aux)
+ {
+ /* Use tombstone hook */
+ done = (*tombstone_aux)();
+ }
+
+ /* Print the text-tombstone */
+ if (!done)
+ {
+ cptr p;
+
+ char tmp[160];
+
+ char buf[1024];
+ char dummy[80];
+
+ FILE *fp;
+
+ time_t ct = time((time_t)0);
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "dead.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the News file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump */
+ if (fp)
+ {
+ int i = 0;
+
+ /* Dump the file to the screen */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Display and advance */
+ display_message(0, i++, strlen(buf), TERM_WHITE, buf);
+ }
+
+ /* Close */
+ my_fclose(fp);
+ }
+
+
+ /* King or Queen */
+ if (total_winner || (p_ptr->lev > PY_MAX_LEVEL))
+ {
+ p = "Magnificent";
+ }
+
+ /* Normal */
+ else
+ {
+ p = cp_ptr->titles[(p_ptr->lev - 1) / 5] + c_text;
+ }
+
+ center_string(buf, player_name);
+ put_str(buf, 6, 11);
+
+ center_string(buf, "the");
+ put_str(buf, 7, 11);
+
+ center_string(buf, p);
+ put_str(buf, 8, 11);
+
+
+ center_string(buf, spp_ptr->title + c_name);
+ put_str(buf, 10, 11);
+
+ (void)sprintf(tmp, "Level: %d", (int)p_ptr->lev);
+ center_string(buf, tmp);
+ put_str(buf, 11, 11);
+
+ (void)sprintf(tmp, "Exp: %ld", (long)p_ptr->exp);
+ center_string(buf, tmp);
+ put_str(buf, 12, 11);
+
+ (void)sprintf(tmp, "AU: %ld", (long)p_ptr->au);
+ center_string(buf, tmp);
+ put_str(buf, 13, 11);
+
+ (void)sprintf(tmp, "Killed on Level %d", dun_level);
+ center_string(buf, tmp);
+ put_str(buf, 14, 11);
+
+
+ if (strlen(died_from) > 24)
+ {
+ strncpy(dummy, died_from, 24);
+ dummy[24] = '\0';
+ (void)sprintf(tmp, "by %s.", dummy);
+ }
+ else
+ (void)sprintf(tmp, "by %s.", died_from);
+
+ center_string(buf, tmp);
+ put_str(buf, 15, 11);
+
+
+ (void)sprintf(tmp, "%-.24s", ctime(&ct));
+ center_string(buf, tmp);
+ put_str(buf, 17, 11);
+ }
+}
+
+
+/*
+ * Display some character info
+ */
+static void show_info(void)
+{
+ int i, j, k;
+ object_type *o_ptr;
+ store_type *st_ptr;
+
+ /* Hack -- Know everything in the inven/equip */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Aware and Known */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+
+ for (i = 1; i < max_towns; i++)
+ {
+ st_ptr = &town_info[i].store[7];
+
+ /* Hack -- Know everything in the home */
+ for (j = 0; j < st_ptr->stock_num; j++)
+ {
+ o_ptr = &st_ptr->stock[j];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Aware and Known */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+ }
+
+ /* Hack -- Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Flush all input keys */
+ flush();
+
+ /* Flush messages */
+ msg_print(NULL);
+
+
+ /* Describe options */
+ prt("You may now dump a character record to one or more files.", 21, 0);
+ prt("Then, hit RETURN to see the character, or ESC to abort.", 22, 0);
+
+ /* Dump character records as requested */
+ while (TRUE)
+ {
+ char out_val[160];
+
+ /* Prompt */
+ put_str("Filename(you can post it to http://angband.oook.cz/): ", 23, 0);
+
+ /* Default */
+ strcpy(out_val, "");
+
+ /* Ask for filename (or abort) */
+ if (!askfor_aux(out_val, 60)) return;
+
+ /* Return means "show on screen" */
+ if (!out_val[0]) break;
+
+ /* Save screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Dump a character file */
+ (void)file_character(out_val, TRUE);
+
+ /* Load screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+
+ /* Display player */
+ display_player(0);
+
+ /* Prompt for p_ptr->inventory */
+ prt("Hit any key to see more information (ESC to abort): ", 23, 0);
+
+ /* Allow abort at this point */
+ if (inkey() == ESCAPE) return;
+
+
+ /* Show equipment and inventory */
+
+ /* Equipment -- if any */
+ if (equip_cnt)
+ {
+ Term_clear();
+ item_tester_full = TRUE;
+ show_equip(FALSE);
+ prt("You are using: -more-", 0, 0);
+ if (inkey() == ESCAPE) return;
+ }
+
+ /* Inventory -- if any */
+ if (inven_cnt)
+ {
+ Term_clear();
+ item_tester_full = TRUE;
+ show_inven(FALSE);
+ prt("You are carrying: -more-", 0, 0);
+ if (inkey() == ESCAPE) return;
+ }
+
+ /* Homes in the different towns */
+ for (k = 1; k < max_towns; k++)
+ {
+ st_ptr = &town_info[k].store[7];
+
+#if 0 /* TODO -- actualy somewhere set the real variable */
+ /* Step to the real towns */
+ if (!(town_info[k].flags & (TOWN_REAL))) continue;
+#endif
+
+ /* Home -- if anything there */
+ if (st_ptr->stock_num)
+ {
+ /* Display contents of the home */
+ for (k = 0, i = 0; i < st_ptr->stock_num; k++)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Show 12 items */
+ for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++)
+ {
+ char o_name[80];
+ char tmp_val[80];
+
+ /* Acquire item */
+ o_ptr = &st_ptr->stock[i];
+
+ /* Print header, clear line */
+ sprintf(tmp_val, "%c) ", I2A(j));
+ prt(tmp_val, j + 2, 4);
+
+ /* Display object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, j + 2, 7);
+ }
+
+ /* h_ptr->caption */
+ prt(format("Your home contains (page %d): -more-", k + 1), 0, 0);
+
+ /* Wait for it */
+ if (inkey() == ESCAPE) return;
+ }
+ }
+ }
+}
+
+
+
+
+
+/*
+ * Semi-Portable High Score List Entry (128 bytes) -- BEN
+ *
+ * All fields listed below are null terminated ascii strings.
+ *
+ * In addition, the "number" fields are right justified, and
+ * space padded, to the full available length (minus the "null").
+ *
+ * Note that "string comparisons" are thus valid on "pts".
+ */
+
+typedef struct high_score high_score;
+
+struct high_score
+{
+ char what[8]; /* Version info (string) */
+
+ char pts[10]; /* Total Score (number) */
+
+ char gold[10]; /* Total Gold (number) */
+
+ char turns[10]; /* Turns Taken (number) */
+
+ char day[10]; /* Time stamp (string) */
+
+ char who[16]; /* Player Name (string) */
+
+ char uid[8]; /* Player UID (number) */
+
+ char sex[2]; /* Player Sex (string) */
+ char p_r[3]; /* Player Race (number) */
+ char p_s[3]; /* Player Subrace (number) */
+ char p_c[3]; /* Player Class (number) */
+ char p_cs[3]; /* Player Class spec (number) */
+
+ char cur_lev[4]; /* Current Player Level (number) */
+ char cur_dun[4]; /* Current Dungeon Level (number) */
+ char max_lev[4]; /* Max Player Level (number) */
+ char max_dun[4]; /* Max Dungeon Level (number) */
+
+ char arena_number[4]; /* Arena level attained -KMW- */
+ char inside_arena[4]; /* Did the player die in the arena? */
+ char inside_quest[4]; /* Did the player die in a quest? */
+ char exit_bldg[4]; /* Can the player exit arena? Goal obtained? -KMW- */
+
+ char how[32]; /* Method of death (string) */
+};
+
+
+
+/*
+ * Seek score 'i' in the highscore file
+ */
+static int highscore_seek(int i)
+{
+ /* Seek for the requested record */
+ return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score)));
+}
+
+
+/*
+ * Read one score from the highscore file
+ */
+static errr highscore_read(high_score *score)
+{
+ /* Read the record, note failure */
+ return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
+}
+
+
+/*
+ * Write one score to the highscore file
+ */
+static int highscore_write(high_score *score)
+{
+ /* Write the record, note failure */
+ return (fd_write(highscore_fd, (char*)(score), sizeof(high_score)));
+}
+
+
+
+
+/*
+ * Just determine where a new score *would* be placed
+ * Return the location (0 is best) or -1 on failure
+ */
+static int highscore_where(high_score *score)
+{
+ int i;
+
+ high_score the_score;
+
+ /* Paranoia -- it may not have opened */
+ if (highscore_fd < 0) return ( -1);
+
+ /* Go to the start of the highscore file */
+ if (highscore_seek(0)) return ( -1);
+
+ /* Read until we get to a higher score */
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(&the_score)) return (i);
+ if (strcmp(the_score.pts, score->pts) < 0) return (i);
+ }
+
+ /* The "last" entry is always usable */
+ return (MAX_HISCORES - 1);
+}
+
+
+/*
+ * Actually place an entry into the high score file
+ * Return the location (0 is best) or -1 on "failure"
+ */
+static int highscore_add(high_score *score)
+{
+ int i, slot;
+ bool done = FALSE;
+
+ high_score the_score, tmpscore;
+
+
+ /* Paranoia -- it may not have opened */
+ if (highscore_fd < 0) return ( -1);
+
+ /* Determine where the score should go */
+ slot = highscore_where(score);
+
+ /* Hack -- Not on the list */
+ if (slot < 0) return ( -1);
+
+ /* Hack -- prepare to dump the new score */
+ the_score = (*score);
+
+ /* Slide all the scores down one */
+ for (i = slot; !done && (i < MAX_HISCORES); i++)
+ {
+ /* Read the old guy, note errors */
+ if (highscore_seek(i)) return ( -1);
+ if (highscore_read(&tmpscore)) done = TRUE;
+
+ /* Back up and dump the score we were holding */
+ if (highscore_seek(i)) return ( -1);
+ if (highscore_write(&the_score)) return ( -1);
+
+ /* Hack -- Save the old score, for the next pass */
+ the_score = tmpscore;
+ }
+
+ /* Return location used */
+ return (slot);
+}
+
+
+
+/*
+ * Display the scores in a given range.
+ * Assumes the high score list is already open.
+ * Only five entries per line, too much info.
+ *
+ * Mega-Hack -- allow "fake" entry at the given position.
+ */
+static void display_scores_aux(int from, int to, int note, high_score *score)
+{
+ int i, j, k, n, place;
+ byte attr;
+ char out_val[256];
+ char tmp_val[160];
+ high_score the_score;
+
+
+ /* Paranoia -- it may not have opened */
+ if (highscore_fd < 0) return;
+
+
+ /* Assume we will show the first 10 */
+ if (from < 0) from = 0;
+ if (to < 0) to = 10;
+ if (to > MAX_HISCORES) to = MAX_HISCORES;
+
+
+ /* Seek to the beginning */
+ if (highscore_seek(0)) return;
+
+ /* Hack -- Count the high scores */
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(&the_score)) break;
+ }
+
+ /* Hack -- allow "fake" entry to be last */
+ if ((note == i) && score) i++;
+
+ /* Forget about the last entries */
+ if (i > to) i = to;
+
+
+ /* Show 5 per page, until "done" */
+ for (k = from, place = k + 1; k < i; k += 5)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Title */
+ put_str(format(" %s Hall of Fame", game_module), 0, 0);
+
+ /* Indicate non-top scores */
+ if (k > 0)
+ {
+ sprintf(tmp_val, "(from position %d)", k + 1);
+ put_str(tmp_val, 0, 40);
+ }
+
+ /* Dump 5 entries */
+ for (j = k, n = 0; j < i && n < 5; place++, j++, n++)
+ {
+ int pcs, pr, ps, pc, clev, mlev, cdun, mdun;
+
+ cptr user, gold, when, aged;
+
+ int in_arena, in_quest;
+
+ /* Hack -- indicate death in yellow */
+ attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
+
+
+ /* Mega-Hack -- insert a "fake" record */
+ if ((note == j) && score)
+ {
+ the_score = (*score);
+ attr = TERM_L_GREEN;
+ score = NULL;
+ note = -1;
+ j--;
+ }
+
+ /* Read a normal record */
+ else
+ {
+ /* Read the proper record */
+ if (highscore_seek(j)) break;
+ if (highscore_read(&the_score)) break;
+ }
+
+ /* Extract the race/class */
+ pr = atoi(the_score.p_r);
+ ps = atoi(the_score.p_s);
+ pc = atoi(the_score.p_c);
+ pcs = atoi(the_score.p_cs);
+
+ /* Extract the level info */
+ clev = atoi(the_score.cur_lev);
+ mlev = atoi(the_score.max_lev);
+ cdun = atoi(the_score.cur_dun);
+ mdun = atoi(the_score.max_dun);
+
+ in_arena = atoi(the_score.inside_arena);
+ in_quest = atoi(the_score.inside_quest);
+
+ /* Hack -- extract the gold and such */
+ for (user = the_score.uid; isspace(*user); user++) /* loop */;
+ for (when = the_score.day; isspace(*when); when++) /* loop */;
+ for (gold = the_score.gold; isspace(*gold); gold++) /* loop */;
+ for (aged = the_score.turns; isspace(*aged); aged++) /* loop */;
+
+ /* Dump some info */
+ sprintf(out_val, "%3d.%9s %s the %s %s, Level %d",
+ place, the_score.pts, the_score.who,
+ get_player_race_name(pr, ps), class_info[pc].spec[pcs].title + c_name,
+ clev);
+
+ /* Append a "maximum level" */
+ if (mlev > clev) strcat(out_val, format(" (Max %d)", mlev));
+
+ /* Dump the first line */
+ c_put_str(attr, out_val, n*4 + 2, 0);
+
+ /* Another line of info */
+ if (in_arena)
+ {
+ sprintf(out_val, " Killed by %s in the Arena",
+ the_score.how);
+ }
+ else if (in_quest)
+ {
+ sprintf(out_val, " Killed by %s while questing",
+ the_score.how);
+ }
+ /* Hack -- some people die in the town */
+ else if (!cdun)
+ {
+ sprintf(out_val, " Killed by %s in the Town",
+ the_score.how);
+ }
+ else
+ {
+ sprintf(out_val, " Killed by %s on %s %d",
+ the_score.how, "Dungeon Level", cdun);
+ }
+
+ /* Append a "maximum level" */
+ if (mdun > cdun) strcat(out_val, format(" (Max %d)", mdun));
+
+ /* Dump the info */
+ c_put_str(attr, out_val, n*4 + 3, 0);
+
+ /* And still another line of info */
+ sprintf(out_val,
+ " (User %s, Date %s, Gold %s, Turn %s).",
+ user, when, gold, aged);
+ c_put_str(attr, out_val, n*4 + 4, 0);
+ }
+
+
+ /* Wait for response */
+ prt("[Press ESC to quit, any other key to continue.]", 23, 17);
+ j = inkey();
+ prt("", 23, 0);
+
+ /* Hack -- notice Escape */
+ if (j == ESCAPE) break;
+ }
+}
+
+
+/*
+ * Hack -- Display the scores in a given range and quit.
+ *
+ * This function is only called from "main.c" when the user asks
+ * to see the "high scores".
+ */
+void display_scores(int from, int to)
+{
+ char buf[1024];
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the binary high score file, for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Paranoia -- No score file */
+ if (highscore_fd < 0) quit("Score file unavailable.");
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Display the scores */
+ display_scores_aux(from, to, -1, NULL);
+
+ /* Shut the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the high score fd */
+ highscore_fd = -1;
+
+ /* Quit */
+ quit(NULL);
+}
+
+
+/*
+ * show_highclass - selectively list highscores based on class
+ * -KMW-
+ */
+void show_highclass(int building)
+{
+
+ register int i = 0, j, m = 0;
+ int pcs, pr, ps, pc, clev, al;
+ high_score the_score;
+ char buf[1024], out_val[256];
+
+ switch (building)
+ {
+ case 1:
+ prt(" Busts of Greatest Kings", 5, 0);
+ break;
+ case 2:
+ prt(" Plaque - Greatest Arena Champions", 5, 0);
+ break;
+ case 10:
+ prt(" Plaque - Greatest Fighters", 5, 0);
+ break;
+ case 11:
+ prt(" Spires of the Greatest Magic-Users", 5, 0);
+ break;
+ case 12:
+ prt(" Busts of Greatest Priests", 5, 0);
+ break;
+ case 13:
+ prt(" Wall Inscriptions - Greatest Thieves", 5, 0);
+ break;
+ case 14:
+ prt(" Plaque - Greatest Rangers", 5, 0);
+ break;
+ case 15:
+ prt(" Plaque - Greatest Paladins", 5, 0);
+ break;
+ case 16:
+ prt(" Spires of the Greatest Illusionists", 5, 0);
+ break;
+ default:
+ bell();
+ break;
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (highscore_seek(0)) return;
+
+ for (i = 0; i < MAX_HISCORES; i++)
+ if (highscore_read(&the_score)) break;
+
+ m = 0;
+ j = 0;
+ clev = 0;
+
+ while ((m < 9) || (j < MAX_HISCORES))
+ {
+ if (highscore_seek(j)) break;
+ if (highscore_read(&the_score)) break;
+ pr = atoi(the_score.p_r);
+ ps = atoi(the_score.p_s);
+ pc = atoi(the_score.p_c);
+ pcs = atoi(the_score.p_cs);
+ clev = atoi(the_score.cur_lev);
+ al = atoi(the_score.arena_number);
+ if (((pc == (building - 10)) && (building != 1) && (building != 2)) ||
+ ((building == 1) && (clev >= PY_MAX_LEVEL)) ||
+ ((building == 2) && (al > MAX_ARENA_MONS)))
+ {
+ sprintf(out_val, "%3d) %s the %s (Level %2d)",
+ (m + 1), the_score.who, rp_name + race_info[pr].title, clev);
+ prt(out_val, (m + 7), 0);
+ m++;
+ }
+ j++;
+ }
+
+ /* Now, list the active player if they qualify */
+ if ((building == 1) && (p_ptr->lev >= PY_MAX_LEVEL))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ else if ((building == 2) && (p_ptr->arena_number > MAX_ARENA_MONS))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ else if ((building != 1) && (building != 2))
+ {
+ if ((p_ptr->lev > clev) && (p_ptr->pclass == (building - 10)))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ }
+
+ (void)fd_close(highscore_fd);
+ highscore_fd = -1;
+ msg_print("Hit any key to continue");
+ msg_print(NULL);
+ for (j = 5; j < 18; j++)
+ prt("", j, 0);
+}
+
+
+/*
+ * Race Legends
+ * -KMW-
+ */
+void race_score(int race_num)
+{
+ register int i = 0, j, m = 0;
+ int pr, ps, pc, clev, pcs, al, lastlev;
+ high_score the_score;
+ char buf[1024], out_val[256], tmp_str[80];
+
+ lastlev = 0;
+
+ /* rr9: TODO - pluralize the race */
+ sprintf(tmp_str, "The Greatest of all the %s", rp_name + race_info[race_num].title);
+ prt(tmp_str, 5, 3);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the highscore file */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (highscore_seek(0)) return;
+
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(&the_score)) break;
+ }
+
+ m = 0;
+ j = 0;
+
+ while ((m < 10) && (j < i))
+ {
+ if (highscore_seek(j)) break;
+ if (highscore_read(&the_score)) break;
+ pr = atoi(the_score.p_r);
+ ps = atoi(the_score.p_s);
+ pc = atoi(the_score.p_c);
+ pcs = atoi(the_score.p_cs);
+ clev = atoi(the_score.cur_lev);
+ al = atoi(the_score.arena_number);
+ if (pr == race_num)
+ {
+ sprintf(out_val, "%3d) %s the %s (Level %3d)",
+ (m + 1), the_score.who,
+ rp_name + race_info[pr].title, clev);
+ prt(out_val, (m + 7), 0);
+ m++;
+ lastlev = clev;
+ }
+ j++;
+ }
+
+ /* add player if qualified */
+ if ((p_ptr->prace == race_num) && (p_ptr->lev >= lastlev))
+ {
+ sprintf(out_val, "You) %s the %s (Level %3d)",
+ player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+
+ (void)fd_close(highscore_fd);
+ highscore_fd = -1;
+}
+
+
+/*
+ * Race Legends
+ * -KMW-
+ */
+void race_legends(void)
+{
+ int i, j;
+
+ for (i = 0; i < max_rp_idx; i++)
+ {
+ race_score(i);
+ msg_print("Hit any key to continue");
+ msg_print(NULL);
+ for (j = 5; j < 19; j++)
+ prt("", j, 0);
+ }
+}
+
+
+
+
+/*
+ * Enters a players name on a hi-score table, if "legal", and in any
+ * case, displays some relevant portion of the high score list.
+ *
+ * Assumes "signals_ignore_tstp()" has been called.
+ */
+static errr top_twenty(void)
+{
+ int j;
+
+ high_score the_score, *tmp;
+
+ time_t ct = time((time_t*)0);
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return (0);
+ }
+
+#ifndef SCORE_WIZARDS
+ /* Wizard-mode pre-empts scoring */
+ if (noscore & 0x000F)
+ {
+ msg_print("Score not registered for wizards.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+#endif
+
+#ifndef SCORE_BORGS
+ /* Borg-mode pre-empts scoring */
+ if (noscore & 0x00F0)
+ {
+ msg_print("Score not registered for borgs.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+#endif
+
+#ifndef SCORE_CHEATERS
+ /* Cheaters are not scored */
+ if (noscore & 0xFF00)
+ {
+ msg_print("Score not registered for cheaters.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+#endif
+
+ /* Interupted */
+ if (!total_winner && streq(died_from, "Interrupting"))
+ {
+ msg_print("Score not registered due to interruption.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+
+ /* Quitter */
+ if (!total_winner && streq(died_from, "Quitting"))
+ {
+ msg_print("Score not registered due to quitting.");
+ msg_print(NULL);
+ display_scores_aux(0, 10, -1, NULL);
+ return (0);
+ }
+
+
+ /* Clear the record */
+ tmp = WIPE(&the_score, high_score);
+
+ /* Save the version */
+ sprintf(the_score.what, "%lu.%lu.%lu",
+ VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
+
+ /* Calculate and save the points */
+ sprintf(the_score.pts, "%9lu", (long)total_points());
+ the_score.pts[9] = '\0';
+
+ /* Save the current gold */
+ sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
+ the_score.gold[9] = '\0';
+
+ /* Save the current turn */
+ sprintf(the_score.turns, "%9lu", (long)turn - (START_DAY * 10L));
+ the_score.turns[9] = '\0';
+
+#ifdef HIGHSCORE_DATE_HACK
+ /* Save the date in a hacked up form (9 chars) */
+ sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22);
+#else
+ /* Save the date in standard form (8 chars) */
+ strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
+#endif
+
+ /* Save the player name (15 chars) */
+ sprintf(the_score.who, "%-.15s", player_name);
+
+ /* Save the player info XXX XXX XXX */
+ sprintf(the_score.uid, "%7u", player_uid);
+ sprintf(the_score.sex, "%c", (p_ptr->psex ? 'm' : 'f'));
+ sprintf(the_score.p_r, "%2d", p_ptr->prace);
+ sprintf(the_score.p_s, "%2d", p_ptr->pracem);
+ sprintf(the_score.p_c, "%2d", p_ptr->pclass);
+ sprintf(the_score.p_cs, "%2d", p_ptr->pspec);
+
+ /* Save the level and such */
+ sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
+ sprintf(the_score.cur_dun, "%3d", dun_level);
+ sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
+ sprintf(the_score.max_dun, "%3d", max_dlv[dungeon_type]);
+
+ sprintf(the_score.arena_number, "%3d", p_ptr->arena_number); /* -KMW- */
+ sprintf(the_score.inside_arena, "%3d", p_ptr->inside_arena);
+ sprintf(the_score.inside_quest, "%3d", p_ptr->inside_quest);
+ sprintf(the_score.exit_bldg, "%3d", p_ptr->exit_bldg); /* -KMW- */
+
+ /* Save the cause of death (31 chars) */
+ sprintf(the_score.how, "%-.31s", died_from);
+
+
+ /* Lock (for writing) the highscore file, or fail */
+ if (fd_lock(highscore_fd, F_WRLCK)) return (1);
+
+ /* Add a new entry to the score list, see where it went */
+ j = highscore_add(&the_score);
+
+ /* Unlock the highscore file, or fail */
+ if (fd_lock(highscore_fd, F_UNLCK)) return (1);
+
+
+ /* Hack -- Display the top fifteen scores */
+ if (j < 10)
+ {
+ display_scores_aux(0, 15, j, NULL);
+ }
+
+ /* Display the scores surrounding the player */
+ else
+ {
+ display_scores_aux(0, 5, j, NULL);
+ display_scores_aux(j - 2, j + 7, j, NULL);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Predict the players location, and display it.
+ */
+errr predict_score(void)
+{
+ int j;
+
+ high_score the_score;
+
+
+ /* No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return (0);
+ }
+
+
+ /* Save the version */
+ sprintf(the_score.what, "%lu.%lu.%lu",
+ VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
+
+ /* Calculate and save the points */
+ sprintf(the_score.pts, "%9lu", (long)total_points());
+ the_score.pts[9] = '\0';
+
+ /* Save the current gold */
+ sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
+ the_score.gold[9] = '\0';
+
+ /* Save the current turn */
+ sprintf(the_score.turns, "%9lu", (long)turn - (START_DAY * 10L));
+ the_score.turns[9] = '\0';
+
+ /* Hack -- no time needed */
+ strcpy(the_score.day, "TODAY");
+
+ /* Save the player name (15 chars) */
+ sprintf(the_score.who, "%-.15s", player_name);
+
+ /* Save the player info XXX XXX XXX */
+ sprintf(the_score.uid, "%7u", player_uid);
+ sprintf(the_score.sex, "%c", (p_ptr->psex ? 'm' : 'f'));
+ sprintf(the_score.p_r, "%2d", p_ptr->prace);
+ sprintf(the_score.p_s, "%2d", p_ptr->pracem);
+ sprintf(the_score.p_c, "%2d", p_ptr->pclass);
+ sprintf(the_score.p_cs, "%2d", p_ptr->pspec);
+
+ /* Save the level and such */
+ sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
+ sprintf(the_score.cur_dun, "%3d", dun_level);
+ sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
+ sprintf(the_score.max_dun, "%3d", max_dlv[dungeon_type]);
+
+ sprintf(the_score.arena_number, "%3d", p_ptr->arena_number); /* -KMW- */
+ sprintf(the_score.inside_arena, "%3d", p_ptr->inside_arena);
+ sprintf(the_score.inside_quest, "%3d", p_ptr->inside_quest);
+ sprintf(the_score.exit_bldg, "%3d", p_ptr->exit_bldg); /* -KMW- */
+
+ /* Hack -- no cause of death */
+ strcpy(the_score.how, "nobody (yet!)");
+
+
+ /* See where the entry would be placed */
+ j = highscore_where(&the_score);
+
+
+ /* Hack -- Display the top fifteen scores */
+ if (j < 10)
+ {
+ display_scores_aux(0, 15, j, &the_score);
+ }
+
+ /* Display some "useful" scores */
+ else
+ {
+ display_scores_aux(0, 5, -1, NULL);
+ display_scores_aux(j - 2, j + 7, j, &the_score);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Change the player into a King! -RAK-
+ */
+static void kingly(void)
+{
+ /* Hack -- retire in town */
+ dun_level = 0;
+
+ /* Fake death */
+ (void)strcpy(died_from, "Ripe Old Age");
+
+ /* Restore the experience */
+ p_ptr->exp = p_ptr->max_exp;
+
+ /* Restore the level */
+ p_ptr->lev = p_ptr->max_plv;
+
+ /* Hack -- Instant Gold */
+ p_ptr->au += 10000000L;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Would like to see something more Tolkienian here... */
+
+ /* Display a crown */
+ put_str("#", 1, 34);
+ put_str("#####", 2, 32);
+ put_str("#", 3, 34);
+ put_str(",,, $$$ ,,,", 4, 28);
+ put_str(",,=$ \"$$$$$\" $=,,", 5, 24);
+ put_str(",$$ $$$ $$,", 6, 22);
+ put_str("*> <*> <*", 7, 22);
+ put_str("$$ $$$ $$", 8, 22);
+ put_str("\"$$ $$$ $$\"", 9, 22);
+ put_str("\"$$ $$$ $$\"", 10, 23);
+ put_str("*#########*#########*", 11, 24);
+ put_str("*#########*#########*", 12, 24);
+
+ /* Display a message */
+ put_str("Veni, Vidi, Vici!", 15, 26);
+ put_str("I came, I saw, I conquered!", 16, 21);
+ put_str(format("All Hail the Mighty %s!", sp_ptr->winner), 17, 22);
+
+ /* Flush input */
+ flush();
+
+ /* Wait for response */
+ pause_line(23);
+}
+
+
+/*
+ * Wipe the saved levels
+ */
+void wipe_saved()
+{
+ int d, l, od = dungeon_type, ol = dun_level;
+
+ for (d = 0; d < max_d_idx; d++)
+ {
+ dungeon_info_type *d_ptr = &d_info[d];
+
+ for (l = d_ptr->mindepth; l <= d_ptr->maxdepth; l++)
+ {
+ char buf[10];
+
+ dun_level = l;
+ dungeon_type = d;
+ if (get_dungeon_save(buf))
+ {
+ char tmp[80], name[1024];
+
+ sprintf(tmp, "%s.%s", player_base, buf);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Remove the dungeon save file */
+ fd_kill(name);
+
+ /* Drop permission */
+ safe_setuid_drop();
+ }
+ }
+ }
+
+ dungeon_type = od;
+ dun_level = ol;
+}
+
+
+/*
+ * Close up the current game (player may or may not be dead)
+ *
+ * This function is called only from "main.c" and "signals.c".
+ */
+void close_game(void)
+{
+ char buf[1024];
+
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Flush the messages */
+ msg_print(NULL);
+
+ /* Flush the input */
+ flush();
+
+
+ /* No suspending now */
+ signals_ignore_tstp();
+
+ /* Hack -- Character is now "icky" */
+ character_icky = TRUE;
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the high score file, for reading/writing */
+ highscore_fd = fd_open(buf, O_RDWR);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Handle death */
+ if (death)
+ {
+ /* Handle retirement */
+ if (total_winner)
+ {
+ /* Write a note, if that option is on */
+ if (take_notes)
+ {
+ add_note_type(NOTE_WINNER);
+ }
+
+ irc_disconnect_aux(format("Retired; %s %ld.%ld.%ld rules",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), FALSE);
+ kingly();
+ }
+ else
+ {
+ irc_disconnect_aux(format("Killed by %s; %s %ld.%ld.%ld rules",
+ died_from, game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH),
+ FALSE);
+ }
+
+ /* Wipe the saved levels */
+ wipe_saved();
+
+ /* Save memories */
+ if (!save_player()) msg_print("Death save failed!");
+
+ /* You are dead */
+ print_tomb();
+
+ /* Show more info */
+ show_info();
+
+ /* Write a note */
+ if (take_notes)
+ {
+ char long_day[30];
+ char buf[80];
+ time_t ct = time((time_t*)NULL);
+
+ /* Get the date */
+ strftime(long_day, 30,
+ "%Y-%m-%d at %H:%M:%S", localtime(&ct));
+
+ /* Create string */
+ sprintf(buf, "\n%s was killed by %s on %s\n", player_name,
+ died_from, long_day);
+
+ /* Output to the notes file */
+ output_note(buf);
+ }
+
+ /* Dump bones file */
+ make_bones();
+
+ /* Handle score, show Top scores */
+ top_twenty();
+ }
+
+ /* Still alive */
+ else
+ {
+ is_autosave = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+
+ /* If note-taking enabled, write session end to notes file */
+ if (take_notes)
+ {
+ add_note_type(NOTE_SAVE_GAME);
+ }
+
+ irc_disconnect_aux(format("Alive... for the time being; %s %ld.%ld.%ld rules",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH),
+ FALSE);
+
+ /* Prompt for scores XXX XXX XXX */
+ prt("Press Return (or Escape).", 0, 40);
+
+ /* Predict score (or ESCAPE) */
+ if (inkey() != ESCAPE) predict_score();
+ }
+
+
+ /* Shut the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the high score fd */
+ highscore_fd = -1;
+
+ /* Allow suspending now */
+ signals_handle_tstp();
+}
+
+
+/*
+ * Handle abrupt death of the visual system
+ *
+ * This routine is called only in very rare situations, and only
+ * by certain visual systems, when they experience fatal errors.
+ *
+ * XXX XXX Hack -- clear the death flag when creating a HANGUP
+ * save file so that player can see tombstone when restart.
+ */
+void exit_game_panic(void)
+{
+ /* If nothing important has happened, just quit */
+ if (!character_generated || character_saved) quit("panic");
+
+ /* Mega-Hack -- see "msg_print()" */
+ msg_flag = FALSE;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Hack -- turn off some things */
+ disturb(1, 0);
+
+ /* Mega-Hack -- Delay death */
+ if (p_ptr->chp < 0) death = FALSE;
+
+ /* Hardcode panic save */
+ panic_save = 1;
+
+ /* Forbid suspend */
+ signals_ignore_tstp();
+
+ /* Indicate panic save */
+ (void)strcpy(died_from, "(panic save)");
+
+ /* Panic save, or get worried */
+ if (!save_player()) quit("panic save failed!");
+
+ /* Successful panic save */
+ quit("panic save succeeded!");
+}
+
+
+/*
+ * Grab a randomly selected line in lib/file/file_name
+ */
+errr get_rnd_line(char *file_name, char *output)
+{
+ FILE *fp;
+
+ char buf[1024];
+
+ int lines = 0;
+
+ int line;
+
+ int i;
+
+
+ /* Clear the output buffer */
+ strcpy(output, "");
+
+ /* test hack */
+ if (wizard && cheat_xtra) msg_print(file_name);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, file_name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failed */
+ if (!fp) return ( -1);
+
+ /* Read the first line */
+ if (0 != my_fgets(fp, buf, 80))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Retrieve number of valid lines in the file */
+ lines = atoi(buf);
+
+ /* Pick a line in the file */
+ line = randint(lines);
+
+ /*
+ * Scan through the file XXX XXX XXX
+ * Seemingly wrong use of the counter is justified by the
+ * stupid 'buffer' lines in the random text files.
+ */
+ for (i = 0; i <= line; i++)
+ {
+ if (0 != my_fgets(fp, buf, 80))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Found the line */
+ if (i == line) break;
+ }
+
+ /* Copy the line to the output buffer */
+ strcpy(output, buf);
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Read line'th line file the file
+ * and return pointer to it, or NULL if it fails.
+ *
+ * Nuked the static buffer. Caller should provide one. -- pelpel
+ *
+ * Caution: 'linbuf' should be at least 80 byte long.
+ */
+char *get_line(char* fname, cptr fdir, char *linbuf, int line)
+{
+ FILE* fp;
+ int i;
+ char buf[1024];
+
+
+ /* Don't count the first line in the file, which is a comment line */
+ line++;
+
+ /* Build the filename */
+ path_build(buf, 1024, fdir, fname);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failed */
+ if (!fp) return (NULL);
+
+ /* Read past specified number of lines */
+ for (i = 0; i <= line; i++)
+ {
+ /* Oops */
+ if (my_fgets(fp, linbuf, 80) != 0)
+ {
+ my_fclose(fp);
+ return (NULL);
+ }
+ }
+
+ my_fclose(fp);
+
+ return (linbuf);
+}
+
+
+/*
+ * Return a line for a speaking unique, by Matt G.
+ *
+ * XXX XXX XXX Opening a file and scanning it through whenever a unique
+ * tries to say something? Something like DELAY_LOAD_?_TEXT would be
+ * much better -- pelpel
+ *
+ * XXX XXX XXX I must say the original is an extremely poor and unreliable
+ * implementation... I removed noxious flag -- I'm too stupid to
+ * understand such complexities -- and added extra error checkings
+ * and made sure fd is always closed -- pelpel
+ */
+errr get_xtra_line(char *file_name, monster_type *m_ptr, char *output)
+{
+ FILE *fp;
+ char buf[1024];
+ int line;
+ int num_entries;
+ int i;
+ int mnum;
+
+
+ /* Clear the message buffer */
+ strcpy(output, "");
+
+ /* test and DEBUG hack */
+ if (wizard && cheat_xtra)
+ {
+ msg_print(file_name);
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, file_name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failed */
+ if (!fp) return ( -1);
+
+ /* Monster number we are looking for */
+ mnum = m_ptr->r_idx;
+
+ /* Find matching N: line */
+ while (1)
+ {
+ int n;
+
+ /* Read a line */
+ if (my_fgets(fp, buf, 90) != 0)
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Not a N: line */
+ if (buf[0] != 'N') continue;
+
+ /* Skip "N:" and parse off a number */
+ sscanf(buf + 2, "%d", &n);
+
+ /* Match found */
+ if (n == mnum) break;
+ }
+
+ /* Retrieve number of normal messages */
+ while (1)
+ {
+ /* Read next line */
+ if (my_fgets(fp, buf, 90) != 0)
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* The first line not beginning with 'N:' holds number of lines */
+ if (buf[0] != 'N')
+ {
+ num_entries = atoi(buf);
+ break;
+ }
+ }
+
+ /* The monster is afraid */
+ if (m_ptr->monfear)
+ {
+ /* Read past normal lines */
+ for (line = 0; line < num_entries + 1; line++)
+ {
+ if (my_fgets(fp, buf, 90))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+ }
+
+ /* Retrieve number of 'afraid' lines */
+ num_entries = atoi(buf);
+ }
+
+
+ /* Pick a random line */
+ line = rand_int(num_entries);
+
+ /* test and DEBUG hack */
+ if (wizard && cheat_xtra)
+ {
+ sprintf(buf, "Line number %d", line);
+ msg_print(buf);
+ }
+
+ /* Find the selected line */
+ for (i = 0; i <= line; i++)
+ {
+ /* Oops */
+ if (0 != my_fgets(fp, buf, 90))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Found it */
+ if (i == line) break;
+ }
+
+ /* Copy it to the output buffer */
+ strcpy(output, buf);
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef HANDLE_SIGNALS
+
+
+#include <signal.h>
+
+
+/*
+ * Handle signals -- suspend
+ *
+ * Actually suspend the game, and then resume cleanly
+ */
+static void handle_signal_suspend(int sig)
+{
+ /* Disable handler */
+ (void)signal(sig, SIG_IGN);
+
+#ifdef SIGSTOP
+
+ /* Flush output */
+ Term_fresh();
+
+ /* Suspend the "Term" */
+ Term_xtra(TERM_XTRA_ALIVE, 0);
+
+ /* Suspend ourself */
+ (void)kill(0, SIGSTOP);
+
+ /* Resume the "Term" */
+ Term_xtra(TERM_XTRA_ALIVE, 1);
+
+ /* Redraw the term */
+ Term_redraw();
+
+ /* Flush the term */
+ Term_fresh();
+
+#endif
+
+ /* Restore handler */
+ (void)signal(sig, handle_signal_suspend);
+}
+
+
+/*
+ * Handle signals -- simple (interrupt and quit)
+ *
+ * This function was causing a *huge* number of problems, so it has
+ * been simplified greatly. We keep a global variable which counts
+ * the number of times the user attempts to kill the process, and
+ * we commit suicide if the user does this a certain number of times.
+ *
+ * We attempt to give "feedback" to the user as he approaches the
+ * suicide thresh-hold, but without penalizing accidental keypresses.
+ *
+ * To prevent messy accidents, we should reset this global variable
+ * whenever the user enters a keypress, or something like that.
+ */
+static void handle_signal_simple(int sig)
+{
+ /* Disable handler */
+ (void)signal(sig, SIG_IGN);
+
+
+ /* Nothing to save, just quit */
+ if (!character_generated || character_saved) quit(NULL);
+
+
+ /* Count the signals */
+ signal_count++;
+
+
+ /* Terminate dead characters */
+ if (death)
+ {
+ /* Mark the savefile */
+ (void)strcpy(died_from, "Abortion");
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit("interrupt");
+ }
+
+ /* Allow suicide (after 5) */
+ else if (signal_count >= 5)
+ {
+ /* Cause of "death" */
+ (void)strcpy(died_from, "Interrupting");
+
+ /* Stop playing */
+ alive = FALSE;
+
+ /* Suicide */
+ death = TRUE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit("interrupt");
+ }
+
+ /* Give warning (after 4) */
+ else if (signal_count >= 4)
+ {
+ /* Make a noise */
+ Term_xtra(TERM_XTRA_NOISE, 0);
+
+ /* Clear the top line */
+ Term_erase(0, 0, 255);
+
+ /* Display the cause */
+ Term_putstr(0, 0, -1, TERM_WHITE, "Contemplating suicide!");
+
+ /* Flush */
+ Term_fresh();
+ }
+
+ /* Give warning (after 2) */
+ else if (signal_count >= 2)
+ {
+ /* Make a noise */
+ Term_xtra(TERM_XTRA_NOISE, 0);
+ }
+
+ /* Restore handler */
+ (void)signal(sig, handle_signal_simple);
+}
+
+
+/*
+ * Handle signal -- abort, kill, etc
+ */
+static void handle_signal_abort(int sig)
+{
+ /* Disable handler */
+ (void)signal(sig, SIG_IGN);
+
+
+ /* Nothing to save, just quit */
+ if (!character_generated || character_saved) quit(NULL);
+
+
+ /* Clear the bottom line */
+ Term_erase(0, 23, 255);
+
+ /* Give a warning */
+ Term_putstr(0, 23, -1, TERM_RED,
+ "A gruesome software bug LEAPS out at you!");
+
+ /* Message */
+ Term_putstr(45, 23, -1, TERM_RED, "Panic save...");
+
+ /* Flush output */
+ Term_fresh();
+
+ /* Panic Save */
+ panic_save = 1;
+
+ /* Panic save */
+ (void)strcpy(died_from, "(panic save)");
+
+ /* Forbid suspend */
+ signals_ignore_tstp();
+
+ /* Attempt to save */
+ if (save_player())
+ {
+ Term_putstr(45, 23, -1, TERM_RED, "Panic save succeeded!");
+ }
+
+ /* Save failed */
+ else
+ {
+ Term_putstr(45, 23, -1, TERM_RED, "Panic save failed!");
+ }
+
+ /* Flush output */
+ Term_fresh();
+
+ /* Quit */
+ quit(format("software bug %d %d", p_ptr->px, p_ptr->py));
+}
+
+
+
+
+/*
+ * Ignore SIGTSTP signals (keyboard suspend)
+ */
+void signals_ignore_tstp(void)
+{
+
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, SIG_IGN);
+#endif
+
+}
+
+/*
+ * Handle SIGTSTP signals (keyboard suspend)
+ */
+void signals_handle_tstp(void)
+{
+
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, handle_signal_suspend);
+#endif
+
+}
+
+
+/*
+ * Prepare to handle the relevant signals
+ */
+void signals_init(void)
+{
+
+#ifdef SIGHUP
+ (void)signal(SIGHUP, SIG_IGN);
+#endif
+
+
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, handle_signal_suspend);
+#endif
+
+
+#ifdef SIGINT
+ (void)signal(SIGINT, handle_signal_simple);
+#endif
+
+#ifdef SIGQUIT
+ (void)signal(SIGQUIT, handle_signal_simple);
+#endif
+
+
+#ifdef SIGFPE
+ (void)signal(SIGFPE, handle_signal_abort);
+#endif
+
+#ifdef SIGILL
+ (void)signal(SIGILL, handle_signal_abort);
+#endif
+
+#ifdef SIGTRAP
+ (void)signal(SIGTRAP, handle_signal_abort);
+#endif
+
+#ifdef SIGIOT
+ (void)signal(SIGIOT, handle_signal_abort);
+#endif
+
+#ifdef SIGKILL
+ (void)signal(SIGKILL, handle_signal_abort);
+#endif
+
+#ifdef SIGBUS
+ (void)signal(SIGBUS, handle_signal_abort);
+#endif
+
+#ifdef SIGSEGV
+ (void)signal(SIGSEGV, handle_signal_abort);
+#endif
+
+#ifdef SIGTERM
+ (void)signal(SIGTERM, handle_signal_abort);
+#endif
+
+#ifdef SIGPIPE
+ (void)signal(SIGPIPE, handle_signal_abort);
+#endif
+
+#ifdef SIGEMT
+ (void)signal(SIGEMT, handle_signal_abort);
+#endif
+
+#ifdef SIGDANGER
+ (void)signal(SIGDANGER, handle_signal_abort);
+#endif
+
+#ifdef SIGSYS
+ (void)signal(SIGSYS, handle_signal_abort);
+#endif
+
+#ifdef SIGXCPU
+ (void)signal(SIGXCPU, handle_signal_abort);
+#endif
+
+#ifdef SIGPWR
+ (void)signal(SIGPWR, handle_signal_abort);
+#endif
+
+}
+
+
+#else /* HANDLE_SIGNALS */
+
+
+/*
+* Do nothing
+*/
+void signals_ignore_tstp(void)
+{}
+
+/*
+* Do nothing
+*/
+void signals_handle_tstp(void)
+{}
+
+/*
+* Do nothing
+*/
+void signals_init(void)
+{}
+
+
+#endif /* HANDLE_SIGNALS */
diff --git a/src/gen_evol.c b/src/gen_evol.c
new file mode 100644
index 00000000..8779f22f
--- /dev/null
+++ b/src/gen_evol.c
@@ -0,0 +1,159 @@
+/*
+ * Generate a game of life level and make it evolve
+ */
+
+/*
+ * Copyright (c) 2003 DarkGod.
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Generate a game of life level :) and make it evolve
+ */
+void evolve_level(bool noise)
+{
+ int i, j;
+
+ int cw = 0, cf = 0;
+
+
+ /* Add a bit of noise */
+ if (noise)
+ {
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ if (f_info[cave[j][i].feat].flags1 & FF1_WALL) cw++;
+ if (f_info[cave[j][i].feat].flags1 & FF1_FLOOR) cf++;
+ }
+ }
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[j][i];
+
+ /* Permanent features should stay */
+ if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Avoid evolving grids with object or monster */
+ if (c_ptr->o_idx || c_ptr->m_idx) continue;
+
+ /* Avoid evolving player grid */
+ if ((j == p_ptr->py) && (i == p_ptr->px)) continue;
+
+ if (magik(7))
+ {
+ if (cw > cf)
+ {
+ place_floor(j, i);
+ }
+ else
+ {
+ place_filler(j, i);
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ int x, y, c;
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[j][i];
+
+ /* Permanent features should stay */
+ if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Avoid evolving grids with object or monster */
+ if (c_ptr->o_idx || c_ptr->m_idx) continue;
+
+ /* Avoid evolving player grid */
+ if ((j == p_ptr->py) && (i == p_ptr->px)) continue;
+
+
+ /* Reset tally */
+ c = 0;
+
+ /* Count number of surrounding walls */
+ for (x = i - 1; x <= i + 1; x++)
+ {
+ for (y = j - 1; y <= j + 1; y++)
+ {
+ if ((x == i) && (y == j)) continue;
+ if (f_info[cave[y][x].feat].flags1 & FF1_WALL) c++;
+ }
+ }
+
+ /*
+ * Changed these parameters a bit, so that it doesn't produce
+ * too open or too narrow levels -- pelpel
+ */
+ /* Starved or suffocated */
+ if ((c < 4) || (c >= 7))
+ {
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL)
+ {
+ place_floor(j, i);
+ }
+ }
+
+ /* Spawned */
+ else if ((c == 4) || (c == 5))
+ {
+ if (!(f_info[c_ptr->feat].flags1 & FF1_WALL))
+ {
+ place_filler(j, i);
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_FLOW | PU_MON_LITE);
+}
+
+
+bool level_generate_life(cptr name)
+{
+ int i, j;
+
+ /* unused */
+ name = name;
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ cave[j][i].info = (CAVE_ROOM | CAVE_GLOW | CAVE_MARK);
+ if (magik(45)) place_floor(j, i);
+ else place_filler(j, i);
+ }
+ }
+
+ evolve_level(FALSE);
+ evolve_level(FALSE);
+ evolve_level(FALSE);
+
+ /* Determine the character location */
+ if (!new_player_spot(get_branch()))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/gen_maze.c b/src/gen_maze.c
new file mode 100644
index 00000000..03941f01
--- /dev/null
+++ b/src/gen_maze.c
@@ -0,0 +1,297 @@
+/*
+ * Maze dungeon generator
+ */
+
+/*
+ * Copyright (c) 2003 DarkGod. And somebody who posted the algorith on
+ * rec.games.roguelike.development. I can't remember teh name :( please mail me
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * If we wasted static memory for this, it would look like:
+ *
+ * static char maze[(MAX_HGT / 2) + 2][(MAX_WID / 2) + 2];
+ */
+typedef signed char maze_row[(MAX_WID / 2) + 2];
+
+void dig(maze_row *maze, int y, int x, int d)
+{
+ int k;
+ int dy = 0, dx = 0;
+
+ /*
+ * first, open the wall of the new cell
+ * in the direction we come from.
+ */
+ switch (d)
+ {
+ case 0:
+ {
+ maze[y][x] |= 4;
+ break;
+ }
+
+ case 1:
+ {
+ maze[y][x] |= 8;
+ break;
+ }
+
+ case 2:
+ {
+ maze[y][x] |= 1;
+ break;
+ }
+
+ case 3:
+ {
+ maze[y][x] |= 2;
+ break;
+ }
+ }
+
+ /*
+ * try to chage direction, here 50% times.
+ * with smaller values (say 25%) the maze
+ * is made of long straight corridors. with
+ * greaters values (say 75%) the maze is
+ * very "turny".
+ */
+ if (rand_range(1, 100) < 50) d = rand_range(0, 3);
+
+ for (k = 1; k <= 4; k++)
+ {
+ switch (d)
+ {
+ case 0:
+ {
+ dy = 0;
+ dx = 1;
+ break;
+ }
+
+ case 1:
+ {
+ dy = -1;
+ dx = 0;
+ break;
+ }
+
+ case 2:
+ {
+ dy = 0;
+ dx = -1;
+ break;
+ }
+
+ case 3:
+ {
+ dy = 1;
+ dx = 0;
+ break;
+ }
+ }
+
+ if (maze[y + dy][x + dx] == 0)
+ {
+ /*
+ * now, open the wall of the new cell
+ * in the direction we go to.
+ */
+ switch (d)
+ {
+ case 0:
+ {
+ maze[y][x] |= 1;
+ break;
+ }
+
+ case 1:
+ {
+ maze[y][x] |= 2;
+ break;
+ }
+
+ case 2:
+ {
+ maze[y][x] |= 4;
+ break;
+ }
+
+ case 3:
+ {
+ maze[y][x] |= 8;
+ break;
+ }
+ }
+
+ dig(maze, y + dy, x + dx, d);
+ }
+
+ d = (d + 1) % 4;
+ }
+}
+
+
+bool level_generate_maze(cptr name)
+{
+ int i, j, d;
+ int y, dy = 0;
+ int x, dx = 0;
+ int m_1 = 0, m_2 = 0;
+ maze_row *maze;
+
+ /* unused */
+ name = name;
+
+ /* Allocate temporary memory */
+ C_MAKE(maze, (MAX_HGT / 2) + 2, maze_row);
+
+ /*
+ * the empty maze is:
+ *
+ * -1 -1 ... -1 -1
+ * -1 0 0 -1
+ * . .
+ * . .
+ * -1 0 0 -1
+ * -1 -1 ... -1 -1
+ *
+ * -1 are so-called "sentinel value".
+ * 0 are empty cells.
+ *
+ * walls are not represented, only cells.
+ * at the end of the algorithm each cell
+ * contains a value that is bit mask
+ * representing surrounding walls:
+ *
+ * bit #1
+ *
+ * +------+
+ * | |
+ * bit #2 | | bit #0
+ * | |
+ * +------+
+ *
+ * bit #3
+ *
+ * d is the direction you are digging
+ * to. d value is the bit number:
+ * d=0 --> go east
+ * d=1 --> go north
+ * etc
+ *
+ * you need only 4 bits per cell.
+ * this gives you a very compact
+ * maze representation.
+ *
+ */
+ for (j = 0; j <= (cur_hgt / 2) + 1; j++)
+ {
+ for (i = 0; i <= (cur_wid / 2) + 1; i++)
+ {
+ maze[j][i] = -1;
+ }
+ }
+
+ for (j = 1; j <= (cur_hgt / 2); j++)
+ {
+ for (i = 1; i <= (cur_wid / 2); i++)
+ {
+ maze[j][i] = 0;
+ }
+ }
+
+ y = rand_range(1, (cur_hgt / 2));
+ x = rand_range(1, (cur_wid / 2));
+ d = rand_range(0, 3);
+
+ dig(maze, y, x, d);
+
+ maze[y][x] = 0;
+
+ for (d = 0; d <= 3; d++)
+ {
+ switch (d)
+ {
+ case 0:
+ {
+ dy = 0;
+ dx = 1;
+ m_1 = 1;
+ m_2 = 4;
+ break;
+ }
+
+ case 1:
+ {
+ dy = -1;
+ dx = 0;
+ m_1 = 2;
+ m_2 = 8;
+ break;
+ }
+
+ case 2:
+ {
+ dy = 0;
+ dx = -1;
+ m_1 = 4;
+ m_2 = 1;
+ break;
+ }
+
+ case 3:
+ {
+ dy = 1;
+ dx = 0;
+ m_1 = 8;
+ m_2 = 2;
+ break;
+ }
+ }
+
+ if ((maze[y + dy][x + dx] != -1) &&
+ ((maze[y + dy][x + dx] & m_2) != 0))
+ {
+ maze[y][x] |= m_1;
+ }
+ }
+
+ /* Translate the maze bit array into a real dungeon map -- DG */
+ for (j = 1; j <= (cur_hgt / 2) - 2; j++)
+ {
+ for (i = 1; i <= (cur_wid / 2) - 2; i++)
+ {
+ if (maze[j][i])
+ {
+ place_floor(j * 2, i * 2);
+ }
+
+ if (maze[j][i] & 1)
+ {
+ place_floor(j * 2, i * 2 + 1);
+ }
+
+ if (maze[j][i] & 8)
+ {
+ place_floor(j * 2 + 1, i * 2);
+ }
+ }
+ }
+
+ /* Free temporary memory */
+ C_FREE(maze, (MAX_HGT / 2) + 2, maze_row);
+
+ /* Determine the character location */
+ if (!new_player_spot(get_branch()))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/generate.c b/src/generate.c
new file mode 100644
index 00000000..b00dcec4
--- /dev/null
+++ b/src/generate.c
@@ -0,0 +1,9024 @@
+/* File: generate.c */
+
+/* Purpose: Dungeon generation */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#define SAFE_MAX_ATTEMPTS 5000
+
+/*
+ * Note that Level generation is *not* an important bottleneck,
+ * though it can be annoyingly slow on older machines... Thus
+ * we emphasize "simplicity" and "correctness" over "speed".
+ *
+ * This entire file is only needed for generating levels.
+ * This may allow smart compilers to only load it when needed.
+ *
+ * Consider the "v_info.txt" file for vault generation.
+ *
+ * In this file, we use the "special" granite and perma-wall sub-types,
+ * where "basic" is normal, "inner" is inside a room, "outer" is the
+ * outer wall of a room, and "solid" is the outer wall of the dungeon
+ * or any walls that may not be pierced by corridors. Thus the only
+ * wall type that may be pierced by a corridor is the "outer granite"
+ * type. The "basic granite" type yields the "actual" corridors.
+ *
+ * Note that we use the special "solid" granite wall type to prevent
+ * multiple corridors from piercing a wall in two adjacent locations,
+ * which would be messy, and we use the special "outer" granite wall
+ * to indicate which walls "surround" rooms, and may thus be "pierced"
+ * by corridors entering or leaving the room.
+ *
+ * Note that a tunnel which attempts to leave a room near the "edge"
+ * of the dungeon in a direction toward that edge will cause "silly"
+ * wall piercings, but will have no permanently incorrect effects,
+ * as long as the tunnel can *eventually* exit from another side.
+ * And note that the wall may not come back into the room by the
+ * hole it left through, so it must bend to the left or right and
+ * then optionally re-enter the room (at least 2 grids away). This
+ * is not a problem since every room that is large enough to block
+ * the passage of tunnels is also large enough to allow the tunnel
+ * to pierce the room itself several times.
+ *
+ * Note that no two corridors may enter a room through adjacent grids,
+ * they must either share an entryway or else use entryways at least
+ * two grids apart. This prevents "large" (or "silly") doorways.
+ *
+ * To create rooms in the dungeon, we first divide the dungeon up
+ * into "blocks" of 11x11 grids each, and require that all rooms
+ * occupy a rectangular group of blocks. As long as each room type
+ * reserves a sufficient number of blocks, the room building routines
+ * will not need to check bounds. Note that most of the normal rooms
+ * actually only use 23x11 grids, and so reserve 33x11 grids.
+ *
+ * Note that the use of 11x11 blocks (instead of the old 33x11 blocks)
+ * allows more variability in the horizontal placement of rooms, and
+ * at the same time has the disadvantage that some rooms (two thirds
+ * of the normal rooms) may be "split" by panel boundaries. This can
+ * induce a situation where a player is in a room and part of the room
+ * is off the screen. It may be annoying enough to go back to 33x11
+ * blocks to prevent this visual situation.
+ *
+ * Note that the dungeon generation routines are much different (2.7.5)
+ * and perhaps "DUN_ROOMS" should be less than 50.
+ *
+ * XXX XXX XXX Note that it is possible to create a room which is only
+ * connected to itself, because the "tunnel generation" code allows a
+ * tunnel to leave a room, wander around, and then re-enter the room.
+ *
+ * XXX XXX XXX Note that it is possible to create a set of rooms which
+ * are only connected to other rooms in that set, since there is nothing
+ * explicit in the code to prevent this from happening. But this is less
+ * likely than the "isolated room" problem, because each room attempts to
+ * connect to another room, in a giant cycle, thus requiring at least two
+ * bizarre occurances to create an isolated section of the dungeon.
+ *
+ * Note that (2.7.9) monster pits have been split into monster "nests"
+ * and monster "pits". The "nests" have a collection of monsters of a
+ * given type strewn randomly around the room (jelly, animal, or undead),
+ * while the "pits" have a collection of monsters of a given type placed
+ * around the room in an organized manner (orc, troll, giant, dragon, or
+ * demon). Note that both "nests" and "pits" are now "level dependant",
+ * and both make 16 "expensive" calls to the "get_mon_num()" function.
+ *
+ * Note that the cave grid flags changed in a rather drastic manner
+ * for Angband 2.8.0 (and 2.7.9+), in particular, dungeon terrain
+ * features, such as doors and stairs and traps and rubble and walls,
+ * are all handled as a set of 64 possible "terrain features", and
+ * not as "fake" objects (440-479) as in pre-2.8.0 versions.
+ *
+ * The 64 new "dungeon features" will also be used for "visual display"
+ * but we must be careful not to allow, for example, the user to display
+ * hidden traps in a different way from floors, or secret doors in a way
+ * different from granite walls, or even permanent granite in a different
+ * way from granite. XXX XXX XXX
+ */
+
+
+/*
+ * Dungeon generation values
+ */
+#define DUN_ROOMS 50 /* Number of rooms to attempt */
+#define DUN_UNUSUAL 194 /* Level/chance of unusual room (was 200) */
+#define DUN_DEST 18 /* 1/chance of having a destroyed level */
+#define SMALL_LEVEL 6 /* 1/chance of smaller size (3->6) */
+#define EMPTY_LEVEL 15 /* 1/chance of being 'empty' (15)*/
+#define DARK_EMPTY 5 /* 1/chance of arena level NOT being lit (2)*/
+#define XTRA_MAGIC 10 /* 1/chance of having a level with more magic (10)*/
+#define DUN_WILD_VAULT 50 /* Chance of finding a wilderness vault. */
+#define DUN_WAT_RNG 2 /* Width of rivers */
+#define DUN_WAT_CHG 50 /* 1 in 50 chance of junction in river */
+#define DUN_CAVERN 30 /* 1/chance of having a cavern level */
+
+/*
+ * Dungeon tunnel generation values
+ */
+#define DUN_TUN_RND 10 /* Chance of random direction */
+#define DUN_TUN_CHG 30 /* Chance of changing direction */
+#define DUN_TUN_CON 15 /* Chance of extra tunneling */
+#define DUN_TUN_PEN 25 /* Chance of doors at room entrances */
+#define DUN_TUN_JCT 90 /* Chance of doors at tunnel junctions */
+
+/*
+ * Dungeon streamer generation values
+ */
+#define DUN_STR_DEN 5 /* Density of streamers */
+#define DUN_STR_RNG 2 /* Width of streamers */
+#define DUN_STR_MAG 3 /* Number of magma streamers */
+#define DUN_STR_MC 90 /* 1/chance of treasure per magma */
+#define DUN_STR_QUA 2 /* Number of quartz streamers */
+#define DUN_STR_QC 40 /* 1/chance of treasure per quartz */
+#define DUN_STR_SAN 1 /* Number of sand streamers */
+#define DUN_STR_SC 10 /* 1/chance of treasure per sandwall */
+#define DUN_STR_WLW 1 /* Width of lava & water streamers -KMW- */
+#define DUN_STR_DWLW 8 /* Density of water & lava streams -KMW- */
+
+
+/*
+ * Dungeon treausre allocation values
+ */
+#define DUN_AMT_ROOM 9 /* Amount of objects for rooms */
+#define DUN_AMT_ITEM 3 /* Amount of objects for rooms/corridors */
+#define DUN_AMT_GOLD 3 /* Amount of treasure for rooms/corridors */
+#define DUN_AMT_ALTAR 1 /* Amount of altars */
+#define DUN_AMT_BETWEEN 2 /* Amount of between gates */
+#define DUN_AMT_FOUNTAIN 1 /* Amount of fountains */
+
+/*
+ * Hack -- Dungeon allocation "places"
+ */
+#define ALLOC_SET_CORR 1 /* Hallway */
+#define ALLOC_SET_ROOM 2 /* Room */
+#define ALLOC_SET_BOTH 3 /* Anywhere */
+
+/*
+ * Hack -- Dungeon allocation "types"
+ */
+#define ALLOC_TYP_RUBBLE 1 /* Rubble */
+#define ALLOC_TYP_TRAP 3 /* Trap */
+#define ALLOC_TYP_GOLD 4 /* Gold */
+#define ALLOC_TYP_OBJECT 5 /* Object */
+#define ALLOC_TYP_ALTAR 6 /* Altar */
+#define ALLOC_TYP_BETWEEN 7 /* Between */
+#define ALLOC_TYP_FOUNTAIN 8 /* Fountain */
+
+
+/*
+ * The "size" of a "generation block" in grids
+ */
+#define BLOCK_HGT 11
+#define BLOCK_WID 11
+
+/*
+ * Maximum numbers of rooms along each axis (currently 6x6)
+ */
+#define MAX_ROOMS_ROW (MAX_HGT / BLOCK_HGT)
+#define MAX_ROOMS_COL (MAX_WID / BLOCK_WID)
+
+
+/*
+ * Bounds on some arrays used in the "dun_data" structure.
+ * These bounds are checked, though usually this is a formality.
+ */
+#define CENT_MAX 100
+#define DOOR_MAX 200
+#define WALL_MAX 500
+#define TUNN_MAX 900
+
+
+/*
+ * Maximal number of room types
+ */
+#define ROOM_MAX 12
+
+
+
+/*
+ * Simple structure to hold a map location
+ */
+
+
+typedef struct coord coord;
+
+struct coord
+{
+ byte y;
+ byte x;
+};
+
+
+/*
+ * Room type information
+ */
+
+typedef struct room_data room_data;
+
+struct room_data
+{
+ /* Required size in blocks */
+ s16b dy1, dy2, dx1, dx2;
+
+ /* Hack -- minimum level */
+ s16b level;
+};
+
+
+/*
+ * Structure to hold all "dungeon generation" data
+ */
+
+typedef struct dun_data dun_data;
+
+struct dun_data
+{
+ /* Array of centers of rooms */
+ int cent_n;
+ coord cent[CENT_MAX];
+
+ /* Array of possible door locations */
+ int door_n;
+ coord door[DOOR_MAX];
+
+ /* Array of wall piercing locations */
+ int wall_n;
+ coord wall[WALL_MAX];
+
+ /* Array of tunnel grids */
+ int tunn_n;
+ coord tunn[TUNN_MAX];
+
+ /* Number of blocks along each axis */
+ int row_rooms;
+ int col_rooms;
+
+ /* Array of which blocks are used */
+ bool room_map[MAX_ROOMS_ROW][MAX_ROOMS_COL];
+
+ /* Hack -- there is a pit/nest on this level */
+ bool crowded;
+};
+
+/*
+ * Level generator type
+ */
+
+typedef struct level_generator_type level_generator_type;
+struct level_generator_type
+{
+ cptr name;
+ bool (*generator)(cptr);
+
+ bool default_stairs;
+ bool default_monsters;
+ bool default_objects;
+ bool default_miscs;
+
+ struct level_generator_type *next;
+};
+
+static level_generator_type *level_generators = NULL;
+
+/*
+ * Add a new generator
+ */
+void add_level_generator(cptr name, bool (*generator)(cptr name), bool stairs, bool monsters, bool objects, bool miscs)
+{
+ level_generator_type *g;
+
+ MAKE(g, level_generator_type);
+ g->name = string_make(name);
+ g->generator = generator;
+
+ g->default_stairs = stairs;
+ g->default_monsters = monsters;
+ g->default_objects = objects;
+ g->default_miscs = miscs;
+
+ g->next = level_generators;
+ level_generators = g;
+}
+
+
+/*
+ * Dungeon generation data -- see "cave_gen()"
+ */
+static dun_data *dun;
+
+/*
+ * ???
+ */
+static int template_race;
+
+
+
+/*
+ * Array of room types depths
+ */
+static s16b roomdep[] =
+{
+ 0, /* 0 = Nothing */
+ 1, /* 1 = Simple (33x11) */
+ 1, /* 2 = Overlapping (33x11) */
+ 3, /* 3 = Crossed (33x11) */
+ 3, /* 4 = Large (33x11) */
+ 5, /* 5 = Monster nest (33x11) */
+ 5, /* 6 = Monster pit (33x11) */
+ 5, /* 7 = Lesser vault (33x22) */
+ 10, /* 8 = Greater vault (66x44) */
+ 1, /* 9 = Circular rooms (22x22) */
+ 3, /* 10 = Fractal cave (42x24) */
+ 10, /* 11 = Random vault (44x22) */
+ 10, /* 12 = Crypts (22x22) */
+};
+
+
+/*
+ * Always picks a correct direction
+ */
+static void correct_dir(int *rdir, int *cdir, int y1, int x1, int y2, int x2)
+{
+ /* Extract vertical and horizontal directions */
+ *rdir = (y1 == y2) ? 0 : (y1 < y2) ? 1 : -1;
+ *cdir = (x1 == x2) ? 0 : (x1 < x2) ? 1 : -1;
+
+ /* Never move diagonally */
+ if (*rdir && *cdir)
+ {
+ if (rand_int(100) < 50)
+ {
+ *rdir = 0;
+ }
+ else
+ {
+ *cdir = 0;
+ }
+ }
+}
+
+
+/*
+ * Pick a random direction
+ */
+static void rand_dir(int *rdir, int *cdir)
+{
+ /* Pick a random direction */
+ int i = rand_int(4);
+
+ /* Extract the dy/dx components */
+ *rdir = ddy_ddd[i];
+ *cdir = ddx_ddd[i];
+}
+
+
+/*
+ * Convert existing terrain type to "up stairs"
+ */
+static void place_up_stairs(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Create up stairs */
+ if ((rand_int(3) != 0) || (dungeon_flags2 & DF2_NO_SHAFT))
+ {
+ cave_set_feat(y, x, FEAT_LESS);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_UP);
+ }
+
+ c_ptr->special = 0;
+}
+
+
+/*
+ * Convert existing terrain type to "down stairs" with dungeon changing.
+ */
+static void place_magical_stairs(int y, int x, byte next)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Create up stairs */
+ cave_set_feat(y, x, FEAT_MORE);
+ c_ptr->special = next;
+}
+
+
+/*
+ * Convert existing terrain type to "down stairs"
+ */
+static void place_down_stairs(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /*
+ * Create down stairs
+ * All thoses tests are necesary because a shaft can jump up to 4 levels
+ */
+ if ((dun_level + 4 > d_info[dungeon_type].maxdepth) ||
+ (rand_int(3) != 0) || (dungeon_flags2 & DF2_NO_SHAFT))
+ {
+ cave_set_feat(y, x, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_DOWN);
+ }
+
+ c_ptr->special = 0;
+}
+
+
+/*
+ * Helper function for place_new_way. Determine if y, x is one of
+ * floor features of the current dungeon
+ */
+static bool is_safe_floor(int y, int x)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ byte feat = cave[y][x].feat;
+
+ /* One of the legal floor types */
+ if (feat == d_ptr->floor1) return (TRUE);
+ if (feat == d_ptr->floor2) return (TRUE);
+ if (feat == d_ptr->floor3) return (TRUE);
+
+ /* Assume non-floor */
+ return (FALSE);
+}
+
+
+/*
+ * Place a way to next / previoous level on flat places
+ */
+void place_new_way(int *y, int *x)
+{
+ int xx, yy;
+ int x0, x1, x2;
+ int y0, y1, y2;
+ cave_type *c_ptr;
+ bool ok;
+ int i, way_n;
+ byte way_x[MAX_WID], way_y[MAX_WID];
+
+
+ /* Find valid location XXX XXX XXX */
+ while (TRUE)
+ {
+ /* A way on vertical edge */
+ if (rand_int(cur_hgt + cur_wid) < cur_hgt)
+ {
+ /* Pick a random grid */
+ yy = *y = rand_int(cur_hgt - 2) + 1;
+ xx = *x = 1 + (rand_int(2) * (cur_wid - 3));
+
+ /* Pick a direction */
+ if (xx == 1)
+ {
+ /* Left */
+ x0 = + 1;
+ y0 = 0;
+
+ /* Sides */
+ x1 = 0;
+ y1 = -1;
+ x2 = 0;
+ y2 = + 1;
+ }
+ else
+ {
+ /* Right */
+ x0 = -1;
+ y0 = 0;
+
+ /* Sides */
+ x1 = 0;
+ y1 = -1;
+ x2 = 0;
+ y2 = + 1;
+ }
+ }
+
+ /* A way on horizontal edge */
+ else
+ {
+ /* Pick a random grid */
+ xx = *x = rand_int(cur_wid - 2) + 1;
+ yy = *y = 1 + (rand_int(2) * (cur_hgt - 3));
+
+ /* Pick a direction */
+ if (yy == 1)
+ {
+ /* Down */
+ x0 = 0;
+ y0 = + 1;
+
+ /* Sides */
+ x1 = -1;
+ y1 = 0;
+ x2 = + 1;
+ y2 = 0;
+ }
+ else
+ {
+ /* Up */
+ x0 = 0;
+ y0 = -1;
+
+ /* Sides */
+ x1 = -1;
+ y1 = 0;
+ x2 = + 1;
+ y2 = 0;
+ }
+ }
+
+
+ /* Look at the starting location */
+ c_ptr = &cave[yy][xx];
+
+ /* Reject locations inside vaults */
+ if (c_ptr->info & (CAVE_ICKY)) continue;
+
+ /* Reject permanent features */
+ if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) &&
+ (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue;
+
+ /* Reject room walls */
+ if ((c_ptr->info & (CAVE_ROOM)) &&
+ (c_ptr->feat == feat_wall_outer)) continue;
+
+ /* Look at a neighbouring edge */
+ c_ptr = &cave[yy + y1][xx + x1];
+
+ /* Reject two adjacent ways */
+ if ((c_ptr->feat == FEAT_WAY_MORE) ||
+ (c_ptr->feat == FEAT_WAY_LESS)) continue;
+
+ /* Look at the other neighbouring edge */
+ c_ptr = &cave[yy + y2][xx + x2];
+
+ /* Reject two adjacent ways */
+ if ((c_ptr->feat == FEAT_WAY_MORE) ||
+ (c_ptr->feat == FEAT_WAY_LESS)) continue;
+
+ /* Look ahead */
+ c_ptr = &cave[yy + y0][xx + x0];
+
+ /* Reject two adjacent ways -- relatively rare, but this can happen */
+ if ((c_ptr->feat == FEAT_WAY_MORE) ||
+ (c_ptr->feat == FEAT_WAY_LESS)) continue;
+
+
+ /* Reset counter */
+ way_n = 0;
+
+ /* Assume bad location */
+ ok = FALSE;
+
+ /* Check if it connects to current dungeon */
+ while (in_bounds(yy, xx))
+ {
+#if 1
+
+ /* Check grids ahead */
+ if (is_safe_floor(yy + y0, xx + x0)) ok = TRUE;
+
+ /* Check side grids */
+ if (is_safe_floor(yy + y1, xx + x1)) ok = TRUE;
+ if (is_safe_floor(yy + y2, xx + x2)) ok = TRUE;
+
+#else
+
+ /*
+ * This can create unconnected sections if it bumps into a
+ * non-penetrating streamer
+ */
+ /* Check grids ahead */
+ if (cave_floor_bold(yy + y0, xx + x0)) ok = TRUE;
+
+ /* Check side grids */
+ if (cave_floor_bold(yy + y1, xx + x1)) ok = TRUE;
+ if (cave_floor_bold(yy + y2, xx + x2)) ok = TRUE;
+
+#endif
+
+ /* Connected */
+ if (ok) break;
+
+ /* Access grid (ahead) */
+ c_ptr = &cave[yy + y0][xx + x0];
+
+ /* Avoid opening vaults */
+ if (c_ptr->feat == FEAT_PERM_OUTER)
+ {
+ /* Comment this out if you find any problems... */
+ ok = TRUE;
+
+ break;
+ }
+
+ /* Paranoia */
+ if (c_ptr->feat == FEAT_PERM_SOLID) break;
+
+ /*
+ * Hack -- Avoid digging room corner
+ *
+ * CAVEAT: Can't handle situations like this:
+ *
+ * .....########
+ * .....########
+ * ######.....>#
+ * #############
+ * .....#
+ * .....#
+ */
+ if (c_ptr->info & (CAVE_ROOM))
+ {
+ cave_type *c1_ptr = &cave[yy + y0 + y1][xx + x0 + x1];
+ cave_type *c2_ptr = &cave[yy + y0 + y2][xx + x0 + x2];
+
+ /* Bend the way */
+ if ((c1_ptr->info & (CAVE_ROOM)) &&
+ !(c2_ptr->info & (CAVE_ROOM)))
+ {
+ way_x[way_n] = xx + x1;
+ way_y[way_n] = yy + y1;
+ way_n++;
+ way_x[way_n] = xx + x1 + x0;
+ way_y[way_n] = yy + y1 + y0;
+ way_n++;
+ }
+
+ /* Bend the way -- the other direction */
+ else if ((c2_ptr->info & (CAVE_ROOM)) &&
+ !(c1_ptr->info & (CAVE_ROOM)))
+ {
+ way_x[way_n] = xx + x2;
+ way_y[way_n] = yy + y2;
+ way_n++;
+ way_x[way_n] = xx + x2 + x0;
+ way_y[way_n] = yy + y2 + y0;
+ way_n++;
+ }
+
+ else
+ {
+ way_x[way_n] = xx + x0;
+ way_y[way_n] = yy + y0;
+ way_n++;
+ }
+
+ ok = TRUE;
+ break;
+ }
+
+ /* Remember the location */
+ way_x[way_n] = xx + x0;
+ way_y[way_n] = yy + y0;
+ way_n++;
+
+ /* Advance */
+ xx += x0;
+ yy += y0;
+ }
+
+ /* Accept connected corridor */
+ if (ok) break;
+
+ /* Try again, until valid location is found */
+ }
+
+
+ /* Actually dig a corridor connecting the way to dungeon */
+ for (i = 0; i < way_n; i++)
+ {
+ /* Dig */
+ place_floor(way_y[i], way_x[i]);
+ }
+}
+
+
+/*
+ * Returns random co-ordinates for player/monster/object
+ */
+bool new_player_spot(int branch)
+{
+ int y, x;
+ int max_attempts = 5000;
+
+ /* Place the player */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ place_new_way(&y, &x);
+ }
+ else
+ {
+ while (max_attempts--)
+ {
+ /* Pick a legal spot */
+ y = rand_range(1, cur_hgt - 2);
+ x = rand_range(1, cur_wid - 2);
+
+ /* Must be a "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Refuse to start on anti-teleport grids */
+ if (cave[y][x].info & (CAVE_ICKY)) continue;
+
+ /* Done */
+ break;
+
+ }
+ }
+
+ /* Should be -1, actually if we failed... */
+ if (max_attempts < 1) return (FALSE);
+
+
+ /* Save the new player grid */
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ /* XXX XXX XXX */
+ if (dungeon_stair && !(dungeon_flags2 & DF2_NO_STAIR) && dun_level &&
+ (!is_quest(dun_level) || (old_dun_level < dun_level)) && !branch)
+ {
+ if (old_dun_level < dun_level)
+ {
+ place_up_stairs(p_ptr->py , p_ptr->px);
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_WAY_LESS);
+ }
+ }
+ else
+ {
+ place_down_stairs(p_ptr->py , p_ptr->px);
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_WAY_MORE);
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+
+
+/*
+ * Count the number of walls adjacent to the given grid.
+ *
+ * Note -- Assumes "in_bounds(y, x)"
+ *
+ * We count only granite walls and permanent walls.
+ */
+static int next_to_walls(int y, int x)
+{
+ int k = 0;
+
+ if (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL) k++;
+ if (f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) k++;
+ if (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL) k++;
+ if (f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) k++;
+
+ return (k);
+}
+
+
+
+/*
+ * Convert existing terrain type to rubble
+ */
+static void place_rubble(int y, int x)
+{
+ /* Create rubble */
+ cave_set_feat(y, x, FEAT_RUBBLE);
+}
+
+
+/*
+ * Place an altar at the given location
+ */
+static void place_altar(int y, int x)
+{
+ if (magik(10))
+ cave_set_feat(y, x, 164);
+}
+
+
+/*
+ * Place a fountain at the given location
+ */
+static void place_fountain(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+ int svals[SV_POTION_LAST + SV_POTION2_LAST + 1], maxsval = 0, k;
+
+ /* List of usable svals */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ if (((k_ptr->tval == TV_POTION) || (k_ptr->tval == TV_POTION2)) &&
+ (k_ptr->level <= dun_level) && (k_ptr->flags4 & TR4_FOUNTAIN))
+ {
+ if (k_ptr->tval == TV_POTION2) svals[maxsval] = k_ptr->sval + SV_POTION_LAST;
+ else svals[maxsval] = k_ptr->sval;
+ maxsval++;
+ }
+ }
+
+ if (maxsval == 0) return;
+
+ /* Place the fountain */
+ if (randint(100) < 30)
+ {
+ cave_set_feat(y, x, FEAT_EMPTY_FOUNTAIN);
+ c_ptr->special2 = 0;
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_FOUNTAIN);
+ c_ptr->special2 = damroll(3, 4);
+ }
+
+ c_ptr->special = svals[rand_int(maxsval)];
+}
+
+
+/*
+ * Place a between gate at the given location
+ */
+static void place_between(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x], *c1_ptr;
+ int gx, gy;
+
+ while (TRUE)
+ {
+ /* Location */
+ gy = rand_int(cur_hgt);
+ gx = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (cave_naked_bold(gy, gx)) break;
+ }
+
+ /* Access the target grid */
+ c1_ptr = &cave[gy][gx];
+
+ /* Place a pair of between gates */
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ c_ptr->special = gx + (gy << 8);
+ cave_set_feat(gy, gx, FEAT_BETWEEN);
+ c1_ptr->special = x + (y << 8);
+}
+
+
+/*
+ * Place an up/down staircase at given location
+ */
+static void place_random_stairs(int y, int x)
+{
+ /* Paranoia */
+ if (!cave_clean_bold(y, x)) return;
+
+ /* Choose a staircase */
+ if (!dun_level)
+ {
+ place_down_stairs(y, x);
+ }
+ else if (is_quest(dun_level) && (dun_level > 1))
+ {
+ place_up_stairs(y, x);
+ }
+ else if (dun_level >= d_info[dungeon_type].maxdepth)
+ {
+ if (d_info[dungeon_type].next)
+ {
+ place_magical_stairs(y, x, d_info[dungeon_type].next);
+ }
+ else
+ {
+ place_up_stairs(y, x);
+ }
+ }
+ else if (rand_int(100) < 50)
+ {
+ place_down_stairs(y, x);
+ }
+ else
+ {
+ place_up_stairs(y, x);
+ }
+}
+
+
+/*
+ * Place a locked door at the given location
+ */
+static void place_locked_door(int y, int x)
+{
+ /* Create locked door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + randint(7));
+}
+
+
+/*
+ * Place a secret door at the given location
+ */
+static void place_secret_door(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Vaults */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ c_ptr->mimic = FEAT_WALL_INNER;
+ }
+
+ /* Ordinary room -- use current outer or inner wall */
+ else if (c_ptr->info & CAVE_ROOM)
+ {
+ /* Determine if it's inner or outer XXX XXX XXX */
+ if ((cave[y - 1][x].info & CAVE_ROOM) &&
+ (cave[y + 1][x].info & CAVE_ROOM) &&
+ (cave[y][x - 1].info & CAVE_ROOM) &&
+ (cave[y][x + 1].info & CAVE_ROOM))
+ {
+ c_ptr->mimic = feat_wall_inner;
+ }
+ else
+ {
+ c_ptr->mimic = feat_wall_outer;
+ }
+ }
+ else
+ {
+ c_ptr->mimic = fill_type[rand_int(100)];
+ }
+
+ /* Create secret door */
+ cave_set_feat(y, x, FEAT_SECRET);
+}
+
+
+/*
+ * Place a random type of door at the given location
+ */
+static void place_random_door(int y, int x)
+{
+ int tmp;
+
+ /* Choose an object */
+ tmp = rand_int(1000);
+
+ /* Open doors (300/1000) */
+ if (tmp < 300)
+ {
+ /* Create open door */
+ cave_set_feat(y, x, FEAT_OPEN);
+ }
+
+ /* Broken doors (100/1000) */
+ else if (tmp < 400)
+ {
+ /* Create broken door */
+ cave_set_feat(y, x, FEAT_BROKEN);
+ }
+
+ /* Secret doors (200/1000) */
+ else if (tmp < 600)
+ {
+ /* Create secret door */
+ place_secret_door(y, x);
+ }
+
+ /* Closed doors (300/1000) */
+ else if (tmp < 900)
+ {
+ /* Create closed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ /* Locked doors (99/1000) */
+ else if (tmp < 999)
+ {
+ /* Create locked door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + randint(7));
+ }
+
+ /* Stuck doors (1/1000) */
+ else
+ {
+ /* Create jammed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x08 + (byte)rand_int(8));
+ }
+}
+
+
+
+/*
+ * Places some staircases near walls
+ */
+static void alloc_stairs(int feat, int num, int walls, int branch)
+{
+ int y, x, i, j, cnt;
+
+ /* Place "num" stairs */
+ for (cnt = 0, i = 0; i < num || (cnt < 1 && num > 1); i++)
+ {
+ /* Try several times, then decrease "walls" */
+ for (j = 0; j <= SAFE_MAX_ATTEMPTS; j++)
+ {
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ place_new_way(&y, &x);
+ }
+ else
+ {
+ /* Pick a random grid */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Require a certain number of adjacent walls */
+ if (next_to_walls(y, x) < walls) continue;
+ }
+
+ /* Town -- must go down */
+ if (!dun_level)
+ {
+ /* Clear previous contents, add down stairs */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(y, x, FEAT_WAY_MORE);
+ }
+ else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT)))
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_DOWN);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_MORE);
+ }
+ }
+
+ /* Quest -- must go up */
+ else if ((is_quest(dun_level) && (dun_level >= 1)) ||
+ ((dun_level >= d_info[dungeon_type].maxdepth) &&
+ (!dungeon_flags1 & DF1_FORCE_DOWN)))
+ {
+ /* Clear previous contents, add up stairs */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(y, x, FEAT_WAY_LESS);
+ }
+ else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT)))
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_UP);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_LESS);
+ }
+ }
+
+ /* Requested type */
+ else
+ {
+ /* Clear previous contents, add stairs */
+ cave_set_feat(y, x, feat);
+ }
+
+ cave[y][x].special = branch;
+
+ /* Count the number of stairs we've actually managed to place. */
+ cnt++;
+
+ /* All done */
+ break;
+ }
+
+ /* Require fewer walls */
+ if (walls) walls--;
+ }
+}
+
+
+
+
+/*
+ * Allocates some objects (using "place" and "type")
+ */
+static void alloc_object(int set, int typ, int num)
+{
+ int y = 1, x = 1, k;
+ int dummy = 0;
+
+ /* Place some objects */
+ for (k = 0; k < num; k++)
+ {
+ /* Pick a "legal" spot */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ bool room;
+
+ dummy++;
+
+ /* Location */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Check for "room" */
+ room = (cave[y][x].info & (CAVE_ROOM)) ? TRUE : FALSE;
+
+ /* Require corridor? */
+ if ((set == ALLOC_SET_CORR) && room) continue;
+
+ /* Require room? */
+ if ((set == ALLOC_SET_ROOM) && !room) continue;
+
+ /* Accept it */
+ break;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_format("Warning! Could not place object, type : %d!", typ);
+ }
+
+ return;
+ }
+
+
+ /* Place something */
+ switch (typ)
+ {
+ case ALLOC_TYP_RUBBLE:
+ {
+ place_rubble(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_TRAP:
+ {
+ place_trap(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_GOLD:
+ {
+ place_gold(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_OBJECT:
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ break;
+ }
+
+ case ALLOC_TYP_ALTAR:
+ {
+ place_altar(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_BETWEEN:
+ {
+ place_between(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_FOUNTAIN:
+ {
+ place_fountain(y, x);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * The following functions create a rectangle (e.g. outer wall of rooms)
+ */
+void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info)
+{
+ int y, x;
+
+ /* Top and bottom boundaries */
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y1, x, feat);
+ cave[y1][x].info |= (info);
+
+ cave_set_feat(y2, x, feat);
+ cave[y2][x].info |= (info);
+ }
+
+ /* Top and bottom boundaries */
+ for (y = y1; y <= y2; y++)
+ {
+ cave_set_feat(y, x1, feat);
+ cave[y][x1].info |= (info);
+
+ cave_set_feat(y, x2, feat);
+ cave[y][x2].info |= (info);
+ }
+}
+
+
+/*
+ * Place water through the dungeon using recursive fractal algorithm
+ *
+ * Why do those good at math and/or algorithms tend *not* to
+ * place any spaces around binary operators? I've been always
+ * wondering. This seems almost a unversal phenomenon...
+ * Tried to make those conform to the rule, but there may still
+ * some left untouched...
+ */
+static void recursive_river(int x1, int y1, int x2, int y2,
+ int feat1, int feat2, int width)
+{
+ int dx, dy, length, l, x, y;
+ int changex, changey;
+ int ty, tx;
+
+
+ length = distance(x1, y1, x2, y2);
+
+ if (length > 4)
+ {
+ /*
+ * Divide path in half and call routine twice.
+ * There is a small chance of splitting the river
+ */
+ dx = (x2 - x1) / 2;
+ dy = (y2 - y1) / 2;
+
+ if (dy != 0)
+ {
+ /* perturbation perpendicular to path */
+ changex = randint(abs(dy)) * 2 - abs(dy);
+ }
+ else
+ {
+ changex = 0;
+ }
+
+ if (dx != 0)
+ {
+ /* perturbation perpendicular to path */
+ changey = randint(abs(dx)) * 2 - abs(dx);
+ }
+ else
+ {
+ changey = 0;
+ }
+
+
+
+ /* construct river out of two smaller ones */
+ recursive_river(x1, y1, x1 + dx + changex, y1 + dy + changey,
+ feat1, feat2, width);
+ recursive_river(x1 + dx + changex, y1 + dy + changey, x2, y2,
+ feat1, feat2, width);
+
+ /* Split the river some of the time -junctions look cool */
+ if ((width > 0) && (rand_int(DUN_WAT_CHG) == 0))
+ {
+ recursive_river(x1 + dx + changex, y1 + dy + changey,
+ x1 + 8 * (dx + changex), y1 + 8 * (dy + changey),
+ feat1, feat2, width - 1);
+ }
+ }
+
+ /* Actually build the river */
+ else
+ {
+ for (l = 0; l < length; l++)
+ {
+ x = x1 + l * (x2 - x1) / length;
+ y = y1 + l * (y2 - y1) / length;
+
+ for (ty = y - width - 1; ty <= y + width + 1; ty++)
+ {
+ for (tx = x - width - 1; tx <= x + width + 1; tx++)
+ {
+ if (!in_bounds(ty, tx)) continue;
+
+ if (cave[ty][tx].feat == feat1) continue;
+ if (cave[ty][tx].feat == feat2) continue;
+
+ if (distance(ty, tx, y, x) > rand_spread(width, 1)) continue;
+
+ /* Do not convert permanent features */
+ if (cave_perma_bold(ty, tx)) continue;
+
+ /*
+ * Clear previous contents, add feature
+ * The border mainly gets feat2, while the center
+ * gets feat1
+ */
+ if (distance(ty, tx, y, x) > width)
+ {
+ cave_set_feat(ty, tx, feat2);
+ }
+ else
+ {
+ cave_set_feat(ty, tx, feat1);
+ }
+
+ /* Lava terrain glows */
+ if ((feat1 == FEAT_DEEP_LAVA) ||
+ (feat1 == FEAT_SHAL_LAVA))
+ {
+ cave[ty][tx].info |= CAVE_GLOW;
+ }
+
+ /* Hack -- don't teleport here */
+ cave[ty][tx].info |= CAVE_ICKY;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Places water through dungeon.
+ */
+static void add_river(int feat1, int feat2)
+{
+ int y2, x2;
+ int y1 = 0, x1 = 0, wid;
+
+
+ /* Hack -- Choose starting point */
+ y2 = randint(cur_hgt / 2 - 2) + cur_hgt / 2;
+ x2 = randint(cur_wid / 2 - 2) + cur_wid / 2;
+
+ /* Hack -- Choose ending point somewhere on boundary */
+ switch (randint(4))
+ {
+ case 1:
+ {
+ /* top boundary */
+ x1 = randint(cur_wid - 2) + 1;
+ y1 = 1;
+ break;
+ }
+ case 2:
+ {
+ /* left boundary */
+ x1 = 1;
+ y1 = randint(cur_hgt - 2) + 1;
+ break;
+ }
+ case 3:
+ {
+ /* right boundary */
+ x1 = cur_wid - 1;
+ y1 = randint(cur_hgt - 2) + 1;
+ break;
+ }
+ case 4:
+ {
+ /* bottom boundary */
+ x1 = randint(cur_wid - 2) + 1;
+ y1 = cur_hgt - 1;
+ break;
+ }
+ }
+ wid = randint(DUN_WAT_RNG);
+ recursive_river(x1, y1, x2, y2, feat1, feat2, wid);
+}
+
+
+/*
+ * Places "streamers" of rock through dungeon
+ *
+ * Note that their are actually six different terrain features used
+ * to represent streamers. Three each of magma and quartz, one for
+ * basic vein, one with hidden gold, and one with known gold. The
+ * hidden gold types are currently unused.
+ */
+static void build_streamer(int feat, int chance)
+{
+ int i, tx, ty;
+ int y, x, dir;
+ int dummy = 0;
+ cave_type *c_ptr;
+
+
+ /* Hack -- Choose starting point */
+ y = rand_spread(cur_hgt / 2, 10);
+ x = rand_spread(cur_wid / 2, 15);
+
+ /* Choose a random compass direction */
+ dir = ddd[rand_int(8)];
+
+ /* Place streamer into dungeon */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* One grid per density */
+ for (i = 0; i < DUN_STR_DEN; i++)
+ {
+ int d = DUN_STR_RNG;
+
+ /* Pick a nearby grid */
+ while (1)
+ {
+ ty = rand_spread(y, d);
+ tx = rand_spread(x, d);
+ if (!in_bounds2(ty, tx)) continue;
+ break;
+ }
+
+ /* Access the grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Only convert "granite" walls */
+ if ((c_ptr->feat != feat_wall_inner) &&
+ (c_ptr->feat != feat_wall_outer) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type1) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type2) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type3)) continue;
+
+ /* Clear mimic feature to avoid nasty consequences */
+ c_ptr->mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+
+ /* Hack -- Add some (known) treasure */
+ if (rand_int(chance) == 0)
+ {
+ cave_set_feat(ty, tx, c_ptr->feat + 0x04);
+ }
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place streamer!");
+ }
+ return;
+ }
+
+
+ /* Advance the streamer */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Quit before leaving the dungeon */
+ if (!in_bounds(y, x)) break;
+ }
+}
+
+
+
+/*
+ * Place streams of water, lava, & trees -KMW-
+ * This routine varies the placement based on dungeon level
+ * otherwise is similar to build_streamer
+ */
+static void build_streamer2(int feat, int killwall)
+{
+ int i, j, mid, tx, ty;
+ int y, x, dir;
+ int poolchance;
+ int poolsize;
+ cave_type *c_ptr;
+
+ poolchance = randint(10);
+
+ /* Hack -- Choose starting point */
+ y = rand_spread(cur_hgt / 2, 10);
+ x = rand_spread(cur_wid / 2, 15);
+
+ /* Choose a random compass direction */
+ dir = ddd[rand_int(8)];
+
+ /* Place streamer into dungeon */
+ if (poolchance > 2)
+ {
+ while (TRUE)
+ {
+ /* One grid per density */
+ for (i = 0; i < (DUN_STR_DWLW + 1); i++)
+ {
+ int d = DUN_STR_WLW;
+
+ /* Pick a nearby grid */
+ while (1)
+ {
+ ty = rand_spread(y, d);
+ tx = rand_spread(x, d);
+ if (in_bounds(ty, tx)) break;
+ }
+
+ /* Access grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Never convert vaults */
+ if (c_ptr->info & (CAVE_ICKY)) continue;
+
+ /* Reject permanent features */
+ if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) &&
+ (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue;
+
+ /* Avoid converting walls when told so */
+ if (killwall == 0)
+ {
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue;
+ }
+
+ /* Clear mimic feature to avoid nasty consequences */
+ c_ptr->mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+ }
+
+ /* Advance the streamer */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Change direction */
+ if (rand_int(20) == 0) dir = ddd[rand_int(8)];
+
+ /* Stop at dungeon edge */
+ if (!in_bounds(y, x)) break;
+ }
+ }
+
+ /* Create pool */
+ else if ((feat == FEAT_DEEP_WATER) || (feat == FEAT_DEEP_LAVA))
+ {
+ poolsize = 5 + randint(10);
+ mid = poolsize / 2;
+
+ /* One grid per density */
+ for (i = 0; i < poolsize; i++)
+ {
+ for (j = 0; j < poolsize; j++)
+ {
+ tx = x + j;
+ ty = y + i;
+
+ if (!in_bounds(ty, tx)) continue;
+
+ if (i < mid)
+ {
+ if (j < mid)
+ {
+ if ((i + j + 1) < mid)
+ continue;
+ }
+ else if (j > (mid + i))
+ continue;
+ }
+ else if (j < mid)
+ {
+ if (i > (mid + j))
+ continue;
+ }
+ else if ((i + j) > ((mid * 3)-1))
+ continue;
+
+ /* Only convert non-permanent features */
+ if (f_info[cave[ty][tx].feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Clear mimic feature to avoid nasty consequences */
+ cave[ty][tx].mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Build a destroyed level
+ */
+static void destroy_level(void)
+{
+ int y1, x1, y, x, k, t, n;
+
+ cave_type *c_ptr;
+
+ /* Note destroyed levels */
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("Destroyed Level");
+
+ /* Drop a few epi-centers (usually about two) */
+ for (n = 0; n < randint(5); n++)
+ {
+ /* Pick an epi-center */
+ x1 = rand_range(5, cur_wid - 1 - 5);
+ y1 = rand_range(5, cur_hgt - 1 - 5);
+
+ /* Big area of affect */
+ for (y = (y1 - 15); y <= (y1 + 15); y++)
+ {
+ for (x = (x1 - 15); x <= (x1 + 15); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k >= 16) continue;
+
+ /* Delete the monster (if any) */
+ delete_monster(y, x);
+
+ /* Destroy valid grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 60)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 90)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Sand */
+ else if (t < 110)
+ {
+ /* Create sand vein */
+ cave_set_feat(y, x, FEAT_SANDWALL);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ place_floor(y, x);
+ }
+
+ /* No longer part of a room or vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* No longer illuminated or known */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Function that sees if a square is a floor (Includes range checking)
+ */
+static bool get_is_floor(int x, int y)
+{
+ /* Out of bounds */
+ if (!in_bounds(y, x)) return (FALSE);
+
+ /* Do the real check: */
+ if (f_info[cave[y][x].feat].flags1 & FF1_FLOOR) return (TRUE);
+
+ return (FALSE);
+}
+
+
+/*
+ * Tunnel around a room if it will cut off part of a cave system
+ */
+static void check_room_boundary(int x1, int y1, int x2, int y2)
+{
+ int count, x, y;
+ bool old_is_floor, new_is_floor;
+
+ /* Avoid doing this in irrelevant places -- pelpel */
+ if (!(dungeon_flags1 & DF1_CAVERN)) return;
+
+ /* Initialize */
+ count = 0;
+
+ old_is_floor = get_is_floor(x1 - 1, y1);
+
+ /*
+ * Count the number of floor-wall boundaries around the room
+ * Note: diagonal squares are ignored since the player can move diagonally
+ * to bypass these if needed.
+ */
+
+ /* Above the top boundary */
+ for (x = x1; x <= x2; x++)
+ {
+ new_is_floor = get_is_floor(x, y1 - 1);
+
+ /* increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Right boundary */
+ for (y = y1; y <= y2; y++)
+ {
+ new_is_floor = get_is_floor(x2 + 1, y);
+
+ /* increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Bottom boundary*/
+ for (x = x2; x >= x1; x--)
+ {
+ new_is_floor = get_is_floor(x, y2 + 1);
+
+ /* Increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Left boundary */
+ for (y = y2; y >= y1; y--)
+ {
+ new_is_floor = get_is_floor(x1 - 1, y);
+
+ /* Increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+
+ /* If all the same, or only one connection exit */
+ if ((count == 0) || (count == 2)) return;
+
+
+ /* Tunnel around the room so to prevent problems with caves */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ if (in_bounds(y, x)) place_floor(y, x);
+ }
+ }
+}
+
+
+/*
+ * Create up to "num" objects near the given coordinates
+ * Only really called by some of the "vault" routines.
+ */
+static void vault_objects(int y, int x, int num)
+{
+ int dummy = 0;
+ int i = 0, j = y, k = x;
+
+
+ /* Attempt to place 'num' objects */
+ for (; num > 0; --num)
+ {
+ /* Try up to 11 spots looking for empty space */
+ for (i = 0; i < 11; ++i)
+ {
+ /* Pick a random location */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ j = rand_spread(y, 2);
+ k = rand_spread(x, 3);
+ dummy++;
+ if (in_bounds(j, k)) break;
+ }
+
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place vault object!");
+ }
+ }
+
+
+ /* Require "clean" floor space */
+ if (!cave_clean_bold(j, k)) continue;
+
+ /* Place an item */
+ if (rand_int(100) < 75)
+ {
+ place_object(j, k, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ /* Place gold */
+ else
+ {
+ place_gold(j, k);
+ }
+
+ /* Placement accomplished */
+ break;
+ }
+ }
+}
+
+
+/*
+ * Place a trap with a given displacement of point
+ */
+static void vault_trap_aux(int y, int x, int yd, int xd)
+{
+ int count = 0, y1 = y, x1 = x;
+ int dummy = 0;
+
+ /* Place traps */
+ for (count = 0; count <= 5; count++)
+ {
+ /* Get a location */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ y1 = rand_spread(y, yd);
+ x1 = rand_spread(x, xd);
+ dummy++;
+ if (in_bounds(y1, x1)) break;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place vault trap!");
+ }
+ }
+
+
+ /* Require "naked" floor grids */
+ if (!cave_naked_bold(y1, x1)) continue;
+
+ /* Place the trap */
+ place_trap(y1, x1);
+
+ /* Done */
+ break;
+ }
+}
+
+
+/*
+ * Place some traps with a given displacement of given location
+ */
+static void vault_traps(int y, int x, int yd, int xd, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ vault_trap_aux(y, x, yd, xd);
+ }
+}
+
+
+/*
+ * Hack -- Place some sleeping monsters near the given location
+ */
+static void vault_monsters(int y1, int x1, int num)
+{
+ int k, i, y, x;
+
+ /* Try to summon "num" monsters "near" the given location */
+ for (k = 0; k < num; k++)
+ {
+ /* Try nine locations */
+ for (i = 0; i < 9; i++)
+ {
+ int d = 1;
+
+ /* Pick a nearby location */
+ scatter(&y, &x, y1, x1, d, 0);
+
+ /* Require "empty" floor grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place the monster (allow groups) */
+ monster_level = dun_level + 2;
+ (void)place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ }
+}
+
+/*
+ * Allocate the space needed by a room in the room_map array.
+ *
+ * width, height represent the size of the room (0...x-1) by (0...y-1).
+ * crowded is used to denote a monset nest.
+ * by0, bx0 are the positions in the room_map array given to the build_type'x'
+ * function.
+ * cx, cy are the returned center of the allocated room in coordinates for
+ * cave.feat and cave.info etc.
+ */
+bool room_alloc(int width, int height, bool crowded, int by0, int bx0, int *cx, int *cy)
+{
+ int temp, eby, ebx, by, bx;
+
+ /* Calculate number of room_map squares to allocate */
+
+ /* Total number along width */
+ temp = ((width - 1) / BLOCK_WID) + 1;
+
+ for (ebx = bx0 + temp; bx0 > 0 && ebx > dun->col_rooms; bx0--, ebx--);
+
+ if (ebx >= dun->col_rooms) return (FALSE);
+
+ /* Total number along height */
+ temp = ((height - 1) / BLOCK_HGT) + 1;
+
+ for (eby = by0 + temp; by0 > 0 && eby > dun->row_rooms; by0--, eby--);
+
+ /* Never run off the screen */
+ if (eby > dun->row_rooms) return (FALSE);
+
+ /* Verify open space */
+ for (by = by0; by < eby; by++)
+ {
+ for (bx = bx0; bx < ebx; bx++)
+ {
+ if (dun->room_map[by][bx]) return (FALSE);
+ }
+ }
+
+ /*
+ * It is *extremely* important that the following calculation
+ * be *exactly* correct to prevent memory errors XXX XXX XXX
+ */
+
+ /* Acquire the location of the room */
+ *cy = ((by0 + eby) * BLOCK_HGT) / 2;
+ *cx = ((bx0 + ebx) * BLOCK_WID) / 2;
+
+ /* Save the room location */
+ if (dun->cent_n < CENT_MAX)
+ {
+ dun->cent[dun->cent_n].y = *cy;
+ dun->cent[dun->cent_n].x = *cx;
+ dun->cent_n++;
+ }
+
+ /* Reserve some blocks */
+ for (by = by0; by < eby; by++)
+ {
+ for (bx = bx0; bx < ebx; bx++)
+ {
+ dun->room_map[by][bx] = TRUE;
+ }
+ }
+
+ /* Count "crowded" rooms */
+ if (crowded) dun->crowded = TRUE;
+
+ /*
+ * Hack -- See if room will cut off a cavern.
+ * If so, fix by tunneling outside the room in such a way as
+ * to conect the caves.
+ */
+ check_room_boundary(*cx - width / 2 - 1, *cy - height / 2 - 1,
+ *cx + width / 2 + 1, *cy + height / 2 + 1);
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Room building routines.
+ *
+ * Room types:
+ * 1 -- normal
+ * 2 -- overlapping
+ * 3 -- cross shaped
+ * 4 -- large room with features
+ * 5 -- monster nests
+ * 6 -- monster pits
+ * 7 -- simple vaults
+ * 8 -- greater vaults
+ * 9 -- circular rooms
+ */
+
+/*
+ * Type 1 -- normal rectangular rooms
+ */
+static void build_type1(int by0, int bx0)
+{
+ u16b info;
+ int y, x = 1, y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ /* Pick a room size */
+ y1 = rand_range(1, 4);
+ x1 = rand_range(1, 10);
+ y2 = rand_range(1, 3);
+ x2 = rand_range(1, 9);
+
+ xsize = x1 + x2;
+ ysize = y1 + y2;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Walls around the room */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info);
+
+ /* Hack -- Occasional pillar room */
+ if (ysize > 2 && xsize > 2)
+ {
+ if (rand_int(20) == 0)
+ {
+ for (y = y1; y <= y2; y += 2)
+ {
+ for (x = x1; x <= x2; x += 2)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Hack -- Occasional ragged-edge room */
+ else if (rand_int(50) == 0)
+ {
+ for (y = y1 + 2; y <= y2 - 2; y += 2)
+ {
+ cave_set_feat(y, x1, feat_wall_inner);
+ cave_set_feat(y, x2, feat_wall_inner);
+ }
+ for (x = x1 + 2; x <= x2 - 2; x += 2)
+ {
+ cave_set_feat(y1, x, feat_wall_inner);
+ cave_set_feat(y2, x, feat_wall_inner);
+ }
+ }
+ }
+}
+
+/*
+ * Type 2 -- Overlapping rectangular rooms
+ */
+static void build_type2(int by0, int bx0)
+{
+ u16b info;
+ int y, x, yval, xval;
+ int y1a, x1a, y2a, x2a;
+ int y1b, x1b, y2b, x2b;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Determine extents of the first room */
+ y1a = yval - randint(4);
+ y2a = yval + randint(3);
+ x1a = xval - randint(14);
+ x2a = xval + randint(6);
+
+ /* Determine extents of the second room */
+ y1b = yval - randint(3);
+ y2b = yval + randint(4);
+ x1b = xval - randint(6);
+ x2b = xval + randint(14);
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info);
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info);
+
+ /* Replace the floor for room "a" */
+ for (y = y1a; y <= y2a; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Replace the floor for room "b" */
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1b; x <= x2b; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+}
+
+/*
+ * Type 3 -- Cross shaped rooms
+ *
+ * Builds a room at a row, column coordinate
+ *
+ * Room "a" runs north/south, and Room "b" runs east/east
+ * So the "central pillar" runs from x1a,y1b to x2a,y2b.
+ *
+ * Note that currently, the "center" is always 3x3, but I think that
+ * the code below will work (with "bounds checking") for 5x5, or even
+ * for unsymetric values like 4x3 or 5x3 or 3x4 or 3x5, or even larger.
+ */
+static void build_type3(int by0, int bx0)
+{
+ u16b info;
+ int y, x, dy, dx, wy, wx;
+ int y1a, x1a, y2a, x2a;
+ int y1b, x1b, y2b, x2b;
+ int yval, xval;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* For now, always 3x3 */
+ wx = wy = 1;
+
+ /* Pick max vertical size (at most 4) */
+ dy = rand_range(3, 4);
+
+ /* Pick max horizontal size (at most 11) */
+ dx = rand_range(3, 11);
+
+ /* Determine extents of the north/south room */
+ y1a = yval - dy;
+ y2a = yval + dy;
+ x1a = xval - wx;
+ x2a = xval + wx;
+
+ /* Determine extents of the east/west room */
+ y1b = yval - wy;
+ y2b = yval + wy;
+ x1b = xval - dx;
+ x2b = xval + dx;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info);
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info);
+
+ /* Replace the floor for room "a" */
+ for (y = y1a; y <= y2a; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Replace the floor for room "b" */
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1b; x <= x2b; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Special features (3/4) */
+ switch (rand_int(4))
+ {
+ /* Large solid middle pillar */
+ case 1:
+ {
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ break;
+ }
+
+ /* Inner treasure vault */
+ case 2:
+ {
+ /* Build the vault */
+ build_rectangle(y1b, x1a, y2b, x2a, feat_wall_inner, info);
+
+ /* Place a secret door on the inner room */
+ switch (rand_int(4))
+ {
+ case 0:
+ place_secret_door(y1b, xval);
+ break;
+ case 1:
+ place_secret_door(y2b, xval);
+ break;
+ case 2:
+ place_secret_door(yval, x1a);
+ break;
+ case 3:
+ place_secret_door(yval, x2a);
+ break;
+ }
+
+ /* Place a treasure in the vault */
+ place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR);
+
+ /* Let's guard the treasure well */
+ vault_monsters(yval, xval, rand_int(2) + 3);
+
+ /* Traps naturally */
+ vault_traps(yval, xval, 4, 4, rand_int(3) + 2);
+
+ break;
+ }
+
+ /* Something else */
+ case 3:
+ {
+ /* Occasionally pinch the center shut */
+ if (rand_int(3) == 0)
+ {
+ /* Pinch the east/west sides */
+ for (y = y1b; y <= y2b; y++)
+ {
+ if (y == yval) continue;
+ cave_set_feat(y, x1a - 1, feat_wall_inner);
+ cave_set_feat(y, x2a + 1, feat_wall_inner);
+ }
+
+ /* Pinch the north/south sides */
+ for (x = x1a; x <= x2a; x++)
+ {
+ if (x == xval) continue;
+ cave_set_feat(y1b - 1, x, feat_wall_inner);
+ cave_set_feat(y2b + 1, x, feat_wall_inner);
+ }
+
+ /* Sometimes shut using secret doors */
+ if (rand_int(3) == 0)
+ {
+ place_secret_door(yval, x1a - 1);
+ place_secret_door(yval, x2a + 1);
+ place_secret_door(y1b - 1, xval);
+ place_secret_door(y2b + 1, xval);
+ }
+ }
+
+ /* Occasionally put a "plus" in the center */
+ else if (rand_int(3) == 0)
+ {
+ cave_set_feat(yval, xval, feat_wall_inner);
+ cave_set_feat(y1b, xval, feat_wall_inner);
+ cave_set_feat(y2b, xval, feat_wall_inner);
+ cave_set_feat(yval, x1a, feat_wall_inner);
+ cave_set_feat(yval, x2a, feat_wall_inner);
+ }
+
+ /* Occasionally put a pillar in the center */
+ else if (rand_int(3) == 0)
+ {
+ cave_set_feat(yval, xval, feat_wall_inner);
+ }
+
+ break;
+ }
+ }
+}
+
+/*
+ * Type 4 -- Large room with inner features
+ *
+ * Possible sub-types:
+ * 1 - Just an inner room with one door
+ * 2 - An inner room within an inner room
+ * 3 - An inner room with pillar(s)
+ * 4 - Inner room has a maze
+ * 5 - A set of four inner rooms
+ */
+static void build_type4(int by0, int bx0)
+{
+ u16b info;
+ int y, x, y1, x1;
+ int y2, x2, tmp, yval, xval;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place a full floor under the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Outer Walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info);
+
+ /* The inner room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, info);
+
+ /* Inner room variations */
+ switch (randint(5))
+ {
+ /* Just an inner room with a monster */
+ case 1:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Place a monster in the room */
+ vault_monsters(yval, xval, 1);
+
+ break;
+ }
+
+ /* Treasure Vault (with a door) */
+ case 2:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Place another inner room */
+ build_rectangle(yval - 1, xval - 1, yval + 1, xval + 1,
+ feat_wall_inner, info);
+
+ /* Place a locked door on the inner room */
+ switch (randint(4))
+ {
+ case 1:
+ place_locked_door(yval - 1, xval);
+ break;
+ case 2:
+ place_locked_door(yval + 1, xval);
+ break;
+ case 3:
+ place_locked_door(yval, xval - 1);
+ break;
+ case 4:
+ place_locked_door(yval, xval + 1);
+ break;
+ }
+
+ /* Monsters to guard the "treasure" */
+ vault_monsters(yval, xval, randint(3) + 2);
+
+ /* Object (80%) */
+ if (rand_int(100) < 80)
+ {
+ place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ /* Stairs (20%) */
+ else
+ {
+ place_random_stairs(yval, xval);
+ }
+
+ /* Traps to protect the treasure */
+ vault_traps(yval, xval, 4, 10, 2 + randint(3));
+
+ break;
+ }
+
+ /* Inner pillar(s). */
+ case 3:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Large Inner Pillar */
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ for (x = xval - 1; x <= xval + 1; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+
+ /* Occasionally, two more Large Inner Pillars */
+ if (rand_int(2) == 0)
+ {
+ tmp = randint(2);
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ for (x = xval - 5 - tmp; x <= xval - 3 - tmp; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ for (x = xval + 3 + tmp; x <= xval + 5 + tmp; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Occasionally, some Inner rooms */
+ if (rand_int(3) == 0)
+ {
+ /* Long horizontal walls */
+ for (x = xval - 5; x <= xval + 5; x++)
+ {
+ cave_set_feat(yval - 1, x, feat_wall_inner);
+ cave_set_feat(yval + 1, x, feat_wall_inner);
+ }
+
+ /* Close off the left/right edges */
+ cave_set_feat(yval, xval - 5, feat_wall_inner);
+ cave_set_feat(yval, xval + 5, feat_wall_inner);
+
+ /* Secret doors (random top/bottom) */
+ place_secret_door(yval - 3 + (randint(2) * 2), xval - 3);
+ place_secret_door(yval - 3 + (randint(2) * 2), xval + 3);
+
+ /* Monsters */
+ vault_monsters(yval, xval - 2, randint(2));
+ vault_monsters(yval, xval + 2, randint(2));
+
+ /* Objects */
+ if (rand_int(3) == 0) place_object(yval, xval - 2, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ if (rand_int(3) == 0) place_object(yval, xval + 2, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ break;
+ }
+
+ /* Maze inside. */
+ case 4:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Maze (really a checkerboard) */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ if (0x1 & (x + y))
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Monsters just love mazes. */
+ vault_monsters(yval, xval - 5, randint(3));
+ vault_monsters(yval, xval + 5, randint(3));
+
+ /* Traps make them entertaining. */
+ vault_traps(yval, xval - 3, 2, 8, randint(3));
+ vault_traps(yval, xval + 3, 2, 8, randint(3));
+
+ /* Mazes should have some treasure too. */
+ vault_objects(yval, xval, 3);
+
+ break;
+ }
+
+ /* Four small rooms. */
+ case 5:
+ {
+ /* Inner "cross" */
+ for (y = y1; y <= y2; y++)
+ {
+ cave_set_feat(y, xval, feat_wall_inner);
+ }
+
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(yval, x, feat_wall_inner);
+ }
+
+ /* Doors into the rooms */
+ if (rand_int(100) < 50)
+ {
+ int i = randint(10);
+ place_secret_door(y1 - 1, xval - i);
+ place_secret_door(y1 - 1, xval + i);
+ place_secret_door(y2 + 1, xval - i);
+ place_secret_door(y2 + 1, xval + i);
+ }
+ else
+ {
+ int i = randint(3);
+ place_secret_door(yval + i, x1 - 1);
+ place_secret_door(yval - i, x1 - 1);
+ place_secret_door(yval + i, x2 + 1);
+ place_secret_door(yval - i, x2 + 1);
+ }
+
+ /* Treasure, centered at the center of the cross */
+ vault_objects(yval, xval, 2 + randint(2));
+
+ /* Gotta have some monsters. */
+ vault_monsters(yval + 1, xval - 4, randint(4));
+ vault_monsters(yval + 1, xval + 4, randint(4));
+ vault_monsters(yval - 1, xval - 4, randint(4));
+ vault_monsters(yval - 1, xval + 4, randint(4));
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Determine if the given monster is appropriate for inclusion in
+ * a monster nest or monster pit or the given type.
+ *
+ * None of the pits/nests are allowed to include "unique" monsters,
+ * or monsters which can "multiply".
+ *
+ * Some of the pits/nests are asked to avoid monsters which can blink
+ * away or which are invisible. This is probably a hack.
+ *
+ * The old method made direct use of monster "names", which is bad.
+ *
+ * Note the use of Angband 2.7.9 monster race pictures in various places.
+ */
+
+
+/*
+ * Helper function for "monster nest (jelly)"
+ */
+static bool vault_aux_jelly(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Also decline evil jellies (like death molds and shoggoths) */
+ if (r_ptr->flags3 & (RF3_EVIL)) return (FALSE);
+
+ /* Require icky thing, jelly, mold, or mushroom */
+ if (!strchr("ijm,", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (animal)"
+ */
+static bool vault_aux_animal(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "animal" flag */
+ if (!(r_ptr->flags3 & (RF3_ANIMAL))) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (undead)"
+ */
+static bool vault_aux_undead(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require Undead */
+ if (!(r_ptr->flags3 & (RF3_UNDEAD))) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (chapel)"
+ */
+static bool vault_aux_chapel(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "priest" or Angel */
+ if (!((r_ptr->d_char == 'A') ||
+ strstr((r_name + r_ptr->name), "riest")))
+ {
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (kennel)"
+ */
+static bool vault_aux_kennel(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require a Zephyr Hound or a dog */
+ return ((r_ptr->d_char == 'Z') || (r_ptr->d_char == 'C'));
+
+}
+
+
+/*
+ * Helper function for "monster nest (treasure)"
+ */
+static bool vault_aux_treasure(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "priest" or Angel */
+ if (!((r_ptr->d_char == '!') || (r_ptr->d_char == '|') ||
+ (r_ptr->d_char == '$') || (r_ptr->d_char == '?') ||
+ (r_ptr->d_char == '=')))
+ {
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (clone)"
+ */
+static bool vault_aux_clone(int r_idx)
+{
+ return (r_idx == template_race);
+}
+
+
+/*
+ * Helper function for "monster nest (symbol clone)"
+ */
+static bool vault_aux_symbol(int r_idx)
+{
+ return ((r_info[r_idx].d_char == (r_info[template_race].d_char))
+ && !(r_info[r_idx].flags1 & RF1_UNIQUE));
+}
+
+
+/*
+ * Helper function for "monster pit (orc)"
+ */
+static bool vault_aux_orc(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "o" monsters */
+ if (!strchr("o", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Helper function for "monster pit (troll)"
+ */
+static bool vault_aux_troll(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "T" monsters */
+ if (!strchr("T", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster pit (giant)"
+ */
+static bool vault_aux_giant(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "P" monsters */
+ if (!strchr("P", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- breath type for "vault_aux_dragon()"
+ */
+static u32b vault_aux_dragon_mask4;
+
+
+/*
+ * Helper function for "monster pit (dragon)"
+ */
+static bool vault_aux_dragon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "d" or "D" monsters */
+ if (!strchr("Dd", r_ptr->d_char)) return (FALSE);
+
+ /* Hack -- Require correct "breath attack" */
+ if (r_ptr->flags4 != vault_aux_dragon_mask4) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster pit (demon)"
+ */
+static bool vault_aux_demon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "U" monsters */
+ if (!strchr("U", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Type 5 -- Monster nests
+ *
+ * A monster nest is a "big" room, with an "inner" room, containing
+ * a "collection" of monsters of a given type strewn about the room.
+ *
+ * The monsters are chosen from a set of 64 randomly selected monster
+ * races, to allow the nest creation to fail instead of having "holes".
+ *
+ * Note the use of the "get_mon_num_prep()" function, and the special
+ * "get_mon_num_hook()" restriction function, to prepare the "monster
+ * allocation table" in such a way as to optimize the selection of
+ * "appropriate" non-unique monsters for the nest.
+ *
+ * Currently, a monster nest is one of
+ * a nest of "jelly" monsters (Dungeon level 5 and deeper)
+ * a nest of "animal" monsters (Dungeon level 30 and deeper)
+ * a nest of "undead" monsters (Dungeon level 50 and deeper)
+ *
+ * Note that the "get_mon_num()" function may (rarely) fail, in which
+ * case the nest will be empty, and will not affect the level rating.
+ *
+ * Note that "monster nests" will never contain "unique" monsters.
+ */
+static void build_type5(int by0, int bx0)
+{
+ int y, x, y1, x1, y2, x2, xval, yval;
+ int tmp, i;
+ cptr name;
+ bool empty = FALSE;
+ bool (*old_get_mon_num_hook)(int r_idx);
+ s16b what[64];
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ if (seed_dungeon) Rand_quick = FALSE;
+
+ /* Place the floor area */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= (CAVE_ROOM);
+ }
+ }
+
+ /* Place the outer walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Advance to the center room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, CAVE_ROOM);
+
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Hack -- Choose a nest type */
+ tmp = randint(dun_level);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ if ((tmp < 25) && (rand_int(2) != 0))
+ {
+ while (1)
+ {
+ template_race = randint(max_r_idx - 2);
+
+ /* Reject uniques */
+ if (r_info[template_race].flags1 & RF1_UNIQUE) continue;
+
+ /* Reject OoD monsters in a loose fashion */
+ if (((r_info[template_race].level) + randint(5)) >
+ (dun_level + randint(5))) continue;
+
+ /* Don't like 'break's like this, but this cannot be made better */
+ break;
+ }
+
+ if ((dun_level >= (25 + randint(15))) && (rand_int(2) != 0))
+ {
+ name = "symbol clone";
+ get_mon_num_hook = vault_aux_symbol;
+ }
+ else
+ {
+ name = "clone";
+ get_mon_num_hook = vault_aux_clone;
+ }
+ }
+ else if (tmp < 25)
+ /* Monster nest (jelly) */
+ {
+ /* Describe */
+ name = "jelly";
+
+ /* Restrict to jelly */
+ get_mon_num_hook = vault_aux_jelly;
+ }
+
+ else if (tmp < 50)
+ {
+ name = "treasure";
+ get_mon_num_hook = vault_aux_treasure;
+ }
+
+ /* Monster nest (animal) */
+ else if (tmp < 65)
+ {
+ if (rand_int(3) == 0)
+ {
+ name = "kennel";
+ get_mon_num_hook = vault_aux_kennel;
+ }
+ else
+ {
+ /* Describe */
+ name = "animal";
+
+ /* Restrict to animal */
+ get_mon_num_hook = vault_aux_animal;
+ }
+ }
+
+ /* Monster nest (undead) */
+ else
+ {
+ if (rand_int(3) == 0)
+ {
+ name = "chapel";
+ get_mon_num_hook = vault_aux_chapel;
+ }
+ else
+ {
+ /* Describe */
+ name = "undead";
+
+ /* Restrict to undead */
+ get_mon_num_hook = vault_aux_undead;
+ }
+ }
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick some monster types */
+ for (i = 0; i < 64; i++)
+ {
+ /* Get a (hard) monster type */
+ what[i] = get_mon_num(dun_level + 10);
+
+ /* Notice failure */
+ if (!what[i]) empty = TRUE;
+ }
+
+ /* Remove restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Oops */
+ if (empty) return;
+
+ /* Describe */
+ if (cheat_room || p_ptr->precognition)
+ {
+ /* Room type */
+ msg_format("Monster nest (%s)", name);
+ }
+
+ /* Increase the level rating */
+ rating += 10;
+
+ /* (Sometimes) Cause a "special feeling" (for "Monster Nests") */
+ if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Place some monsters */
+ for (y = yval - 2; y <= yval + 2; y++)
+ {
+ for (x = xval - 9; x <= xval + 9; x++)
+ {
+ int r_idx = what[rand_int(64)];
+
+ /* Place that "random" monster (no groups) */
+ (void)place_monster_aux(y, x, r_idx, FALSE, FALSE, MSTATUS_ENEMY);
+ }
+ }
+
+ if (seed_dungeon) Rand_quick = TRUE;
+}
+
+
+
+/*
+ * Type 6 -- Monster pits
+ *
+ * A monster pit is a "big" room, with an "inner" room, containing
+ * a "collection" of monsters of a given type organized in the room.
+ *
+ * Monster types in the pit (list out of date...)
+ * orc pit (Dungeon Level 5 and deeper)
+ * troll pit (Dungeon Level 20 and deeper)
+ * giant pit (Dungeon Level 40 and deeper)
+ * dragon pit (Dungeon Level 60 and deeper)
+ * demon pit (Dungeon Level 80 and deeper)
+ *
+ * The inside room in a monster pit appears as shown below, where the
+ * actual monsters in each location depend on the type of the pit
+ *
+ * #####################
+ * #0000000000000000000#
+ * #0112233455543322110#
+ * #0112233467643322110#
+ * #0112233455543322110#
+ * #0000000000000000000#
+ * #####################
+ *
+ * Note that the monsters in the pit are now chosen by using "get_mon_num()"
+ * to request 16 "appropriate" monsters, sorting them by level, and using
+ * the "even" entries in this sorted list for the contents of the pit.
+ *
+ * Hack -- all of the "dragons" in a "dragon" pit must be the same "color",
+ * which is handled by requiring a specific "breath" attack for all of the
+ * dragons. This may include "multi-hued" breath. Note that "wyrms" may
+ * be present in many of the dragon pits, if they have the proper breath.
+ *
+ * Note the use of the "get_mon_num_prep()" function, and the special
+ * "get_mon_num_hook()" restriction function, to prepare the "monster
+ * allocation table" in such a way as to optimize the selection of
+ * "appropriate" non-unique monsters for the pit.
+ *
+ * Note that the "get_mon_num()" function may (rarely) fail, in which case
+ * the pit will be empty, and will not effect the level rating.
+ *
+ * Note that "monster pits" will never contain "unique" monsters.
+ */
+static void build_type6(int by0, int bx0)
+{
+ int tmp, what[16];
+ int i, j, y, x, y1, x1, y2, x2, xval, yval;
+ bool empty = FALSE;
+ cptr name;
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ if (seed_dungeon) Rand_quick = FALSE;
+
+ /* Place the floor area */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= (CAVE_ROOM);
+ }
+ }
+
+ /* Place the outer walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Advance to the center room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Choose a pit type */
+ tmp = randint(dun_level);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Orc pit */
+ if (tmp < 20)
+ {
+ /* Message */
+ name = "orc";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_orc;
+ }
+
+ /* Troll pit */
+ else if (tmp < 40)
+ {
+ /* Message */
+ name = "troll";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_troll;
+ }
+
+ /* Giant pit */
+ else if (tmp < 55)
+ {
+ /* Message */
+ name = "giant";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_giant;
+ }
+
+ else if (tmp < 70)
+ {
+ if (randint(4) != 1)
+ {
+ /* Message */
+ name = "ordered clones";
+
+ do
+ {
+ template_race = randint(max_r_idx - 2);
+ }
+ while ((r_info[template_race].flags1 & RF1_UNIQUE)
+ || (((r_info[template_race].level) + randint(5)) >
+ (dun_level + randint(5))));
+
+ /* Restrict selection */
+ get_mon_num_hook = vault_aux_symbol;
+ }
+ else
+ {
+ name = "ordered chapel";
+ get_mon_num_hook = vault_aux_chapel;
+ }
+
+ }
+
+ /* Dragon pit */
+ else if (tmp < 80)
+ {
+ /* Pick dragon type */
+ switch (rand_int(6))
+ {
+ /* Black */
+ case 0:
+ {
+ /* Message */
+ name = "acid dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_ACID;
+
+ /* Done */
+ break;
+ }
+
+ /* Blue */
+ case 1:
+ {
+ /* Message */
+ name = "electric dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_ELEC;
+
+ /* Done */
+ break;
+ }
+
+ /* Red */
+ case 2:
+ {
+ /* Message */
+ name = "fire dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_FIRE;
+
+ /* Done */
+ break;
+ }
+
+ /* White */
+ case 3:
+ {
+ /* Message */
+ name = "cold dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_COLD;
+
+ /* Done */
+ break;
+ }
+
+ /* Green */
+ case 4:
+ {
+ /* Message */
+ name = "poison dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_POIS;
+
+ /* Done */
+ break;
+ }
+
+ /* Multi-hued */
+ default:
+ {
+ /* Message */
+ name = "multi-hued dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = (RF4_BR_ACID | RF4_BR_ELEC |
+ RF4_BR_FIRE | RF4_BR_COLD |
+ RF4_BR_POIS);
+
+ /* Done */
+ break;
+ }
+
+ }
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_dragon;
+ }
+
+ /* Demon pit */
+ else
+ {
+ /* Message */
+ name = "demon";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_demon;
+ }
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick some monster types */
+ for (i = 0; i < 16; i++)
+ {
+ /* Get a (hard) monster type */
+ what[i] = get_mon_num(dun_level + 10);
+
+ /* Notice failure */
+ if (!what[i]) empty = TRUE;
+ }
+
+ /* Remove restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Oops */
+ if (empty) return;
+
+ /* XXX XXX XXX */
+ /* Sort the entries */
+ for (i = 0; i < 16 - 1; i++)
+ {
+ /* Sort the entries */
+ for (j = 0; j < 16 - 1; j++)
+ {
+ int i1 = j;
+ int i2 = j + 1;
+
+ int p1 = r_info[what[i1]].level;
+ int p2 = r_info[what[i2]].level;
+
+ /* Bubble */
+ if (p1 > p2)
+ {
+ int tmp = what[i1];
+ what[i1] = what[i2];
+ what[i2] = tmp;
+ }
+ }
+ }
+
+ /* Select the entries */
+ for (i = 0; i < 8; i++)
+ {
+ /* Every other entry */
+ what[i] = what[i * 2];
+ }
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition)
+ {
+ /* Room type */
+ msg_format("Monster pit (%s)", name);
+
+ if (cheat_hear || p_ptr->precognition)
+ {
+ /* Contents */
+ for (i = 0; i < 8; i++)
+ {
+ /* Message */
+ msg_print(r_name + r_info[what[i]].name);
+ }
+ }
+ }
+
+ /* Increase the level rating */
+ rating += 10;
+
+ /* (Sometimes) Cause a "special feeling" (for "Monster Pits") */
+ if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Top and bottom rows */
+ for (x = xval - 9; x <= xval + 9; x++)
+ {
+ place_monster_aux(yval - 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval + 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Middle columns */
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ place_monster_aux(y, xval - 9, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 9, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 8, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 8, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 7, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 7, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 6, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 6, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 5, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 5, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 4, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 4, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 3, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 3, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 2, what[4], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 2, what[4], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Above/Below the center monster */
+ for (x = xval - 1; x <= xval + 1; x++)
+ {
+ place_monster_aux(yval + 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval - 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Next to the center monster */
+ place_monster_aux(yval, xval + 1, what[6], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval, xval - 1, what[6], FALSE, FALSE, MSTATUS_ENEMY);
+
+ /* Center monster */
+ place_monster_aux(yval, xval, what[7], FALSE, FALSE, MSTATUS_ENEMY);
+
+ if (seed_dungeon)
+ {
+ Rand_quick = TRUE;
+ }
+}
+
+/*
+ * Hack -- fill in "vault" rooms
+ */
+static void build_vault(int yval, int xval, int ymax, int xmax, cptr data)
+{
+ int dx, dy, x, y, bwy[8], bwx[8], i;
+
+ cptr t;
+
+ cave_type *c_ptr;
+
+ /* Vaults are different even in persistent dungeons. */
+ if (seed_dungeon)
+ {
+ Rand_quick = FALSE;
+ }
+
+ /* Clean the between gates arrays */
+ for (i = 0; i < 8; i++)
+ {
+ bwy[i] = bwx[i] = 9999;
+ }
+
+ /* Place dungeon features and objects */
+ for (t = data, dy = 0; dy < ymax; dy++)
+ {
+ for (dx = 0; dx < xmax; dx++, t++)
+ {
+ /* Extract the location */
+ x = xval - (xmax / 2) + dx;
+ y = yval - (ymax / 2) + dy;
+
+ /* Hack -- skip "non-grids" */
+ if (*t == ' ') continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lay down a floor */
+ place_floor(y, x);
+
+ /* Part of a vault */
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Analyze the grid */
+ switch (*t)
+ {
+ /* Granite wall (outer) */
+ case '%':
+ {
+ cave_set_feat(y, x, FEAT_WALL_OUTER);
+ break;
+ }
+
+ /* Granite wall (inner) */
+ case '#':
+ {
+ cave_set_feat(y, x, FEAT_WALL_INNER);
+ break;
+ }
+
+ /* Permanent wall (inner) */
+ case 'X':
+ {
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ break;
+ }
+
+ /* Treasure/trap */
+ case '*':
+ {
+ if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+ break;
+ }
+
+ /* Secret doors */
+ case '+':
+ {
+ place_secret_door(y, x);
+ break;
+ }
+
+ /* Trap */
+ case '^':
+ {
+ place_trap(y, x);
+ break;
+ }
+
+ /* Glass wall */
+ case 'G':
+ {
+ cave_set_feat(y, x, FEAT_GLASS_WALL);
+ break;
+ }
+
+ /* Illusion wall */
+ case 'I':
+ {
+ cave_set_feat(y, x, FEAT_ILLUS_WALL);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Place dungeon monsters and objects */
+ for (t = data, dy = 0; dy < ymax; dy++)
+ {
+ for (dx = 0; dx < xmax; dx++, t++)
+ {
+ /* Extract the grid */
+ x = xval - (xmax / 2) + dx;
+ y = yval - (ymax / 2) + dy;
+
+ /* Hack -- skip "non-grids" */
+ if (*t == ' ') continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Analyze the symbol */
+ switch (*t)
+ {
+ /* Monster */
+ case '&':
+ {
+ monster_level = dun_level + 5;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ break;
+ }
+
+ /* Meaner monster */
+ case '@':
+ {
+ monster_level = dun_level + 11;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ break;
+ }
+
+ /* Meaner monster, plus treasure */
+ case '9':
+ {
+ monster_level = dun_level + 9;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 7;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+ /* Nasty monster and treasure */
+ case '8':
+ {
+ monster_level = dun_level + 40;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 20;
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+ /* Monster and/or object */
+ case ',':
+ {
+ if (rand_int(100) < 50)
+ {
+ monster_level = dun_level + 3;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ if (rand_int(100) < 50)
+ {
+ object_level = dun_level + 7;
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_START);
+ break;
+ }
+
+ case 'a':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_1);
+ break;
+ }
+
+ case 'b':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_2);
+ break;
+ }
+
+ case 'c':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_3);
+ break;
+ }
+
+ case 'd':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_4);
+ break;
+ }
+
+ case 'P':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_END);
+ break;
+ }
+
+ case 'B':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_XTRA1);
+ break;
+ }
+
+ case 'A':
+ {
+ object_level = dun_level + 12;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+
+ /* Between gates */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ /* Not found before */
+ if (bwy[*t - '0'] == 9999)
+ {
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ bwy[*t - '0'] = y;
+ bwx[*t - '0'] = x;
+ }
+ /* The second time */
+ else
+ {
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ c_ptr->special = bwx[*t - '0'] + (bwy[*t - '0'] << 8);
+ cave[bwy[*t - '0']][bwx[*t - '0']].special = x + (y << 8);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (seed_dungeon)
+ {
+ Rand_quick = TRUE;
+ }
+}
+
+/*
+ * Type 7 -- simple vaults (see "v_info.txt")
+ */
+static void build_type7(int by0, int bx0)
+{
+ vault_type *v_ptr = NULL;
+ int dummy = 0, xval, yval;
+
+ /* Pick a lesser vault */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* Access a random vault record */
+ v_ptr = &v_info[rand_int(max_v_idx)];
+
+ /* Accept the first lesser vault */
+ if (v_ptr->typ == 7) break;
+ }
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval))
+ {
+ if (cheat_room) msg_print("Could not allocate this vault here");
+ return;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place lesser vault!");
+ }
+ return;
+ }
+
+
+#ifdef FORCE_V_IDX
+ v_ptr = &v_info[FORCE_V_IDX];
+#endif
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition) msg_print("Lesser Vault");
+
+ /* Boost the rating */
+ rating += v_ptr->rat;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 50) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Hack -- Build the vault */
+ build_vault(yval, xval, v_ptr->hgt, v_ptr->wid, v_text + v_ptr->text);
+}
+
+
+
+/*
+ * Type 8 -- greater vaults (see "v_info.txt")
+ */
+static void build_type8(int by0, int bx0)
+{
+ vault_type *v_ptr = NULL;
+ int dummy = 0, xval, yval;
+
+ /* Pick a lesser vault */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* Access a random vault record */
+ v_ptr = &v_info[rand_int(max_v_idx)];
+
+ /* Accept the first greater vault */
+ if (v_ptr->typ == 8) break;
+ }
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval))
+ {
+ if (cheat_room) msg_print("Could not allocate this vault here");
+ return;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place greater vault!");
+ }
+ return;
+ }
+
+
+#ifdef FORCE_V_IDX
+ v_ptr = &v_info[FORCE_V_IDX];
+#endif
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition) msg_print("Greater Vault");
+
+ /* Boost the rating */
+ rating += v_ptr->rat;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 50) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Hack -- Build the vault */
+ build_vault(yval, xval, v_ptr->hgt, v_ptr->wid, v_text + v_ptr->text);
+}
+
+/*
+ * DAG:
+ * Build an vertical oval room.
+ * For every grid in the possible square, check the distance.
+ * If it's less than or == than the radius, make it a room square.
+ * If its less, make it a normal grid. If it's == make it an outer
+ * wall.
+ */
+static void build_type9(int by0, int bx0)
+{
+ u16b info;
+ int rad, x, y, x0, y0;
+
+ rad = 2 + rand_int(8);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(rad*2 + 1, rad*2 + 1, FALSE, by0, bx0, &x0, &y0)) return;
+
+ info = (randint(dun_level) <= 5) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ if (distance(y0, x0, y, x) == rad)
+ {
+ cave[y][x].info |= info;
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+
+ if (distance(y0, x0, y, x) < rad)
+ {
+ cave[y][x].info |= info;
+ place_floor(y, x);
+ }
+ }
+ }
+}
+
+
+/*
+ * Store routine for the fractal cave generator
+ * this routine probably should be an inline function or a macro
+ */
+static void store_height(int x, int y, int x0, int y0, byte val,
+ int xhsize, int yhsize, int cutoff)
+{
+ /* Only write to points that are "blank" */
+ if (cave[y + y0 - yhsize][x + x0 - xhsize].feat != 255) return;
+
+ /* If on boundary set val > cutoff so walls are not as square */
+ if (((x == 0) || (y == 0) || (x == xhsize * 2) || (y == yhsize * 2)) &&
+ (val <= cutoff)) val = cutoff + 1;
+
+ /* Store the value in height-map format */
+ /* Meant to be temporary, hence no cave_set_feat */
+ cave[y + y0 - yhsize][x + x0 - xhsize].feat = val;
+
+ return;
+}
+
+
+
+/*
+ * Explanation of the plasma fractal algorithm:
+ *
+ * A grid of points is created with the properties of a 'height-map'
+ * This is done by making the corners of the grid have a random value.
+ * The grid is then subdivided into one with twice the resolution.
+ * The new points midway between two 'known' points can be calculated
+ * by taking the average value of the 'known' ones and randomly adding
+ * or subtracting an amount proportional to the distance between those
+ * points. The final 'middle' points of the grid are then calculated
+ * by averaging all four of the originally 'known' corner points. An
+ * random amount is added or subtracted from this to get a value of the
+ * height at that point. The scaling factor here is adjusted to the
+ * slightly larger distance diagonally as compared to orthogonally.
+ *
+ * This is then repeated recursively to fill an entire 'height-map'
+ * A rectangular map is done the same way, except there are different
+ * scaling factors along the x and y directions.
+ *
+ * A hack to change the amount of correlation between points is done using
+ * the grd variable. If the current step size is greater than grd then
+ * the point will be random, otherwise it will be calculated by the
+ * above algorithm. This makes a maximum distance at which two points on
+ * the height map can affect each other.
+ *
+ * How fractal caves are made:
+ *
+ * When the map is complete, a cut-off value is used to create a cave.
+ * Heights below this value are "floor", and heights above are "wall".
+ * This also can be used to create lakes, by adding more height levels
+ * representing shallow and deep water/ lava etc.
+ *
+ * The grd variable affects the width of passages.
+ * The roug variable affects the roughness of those passages
+ *
+ * The tricky part is making sure the created cave is connected. This
+ * is done by 'filling' from the inside and only keeping the 'filled'
+ * floor. Walls bounding the 'filled' floor are also kept. Everything
+ * else is converted to the normal granite FEAT_WALL_EXTRA.
+ */
+
+
+/*
+ * Note that this uses the cave.feat array in a very hackish way
+ * the values are first set to zero, and then each array location
+ * is used as a "heightmap"
+ * The heightmap then needs to be converted back into the "feat" format.
+ *
+ * grd=level at which fractal turns on. smaller gives more mazelike caves
+ * roug=roughness level. 16=normal. higher values make things more
+ * convoluted small values are good for smooth walls.
+ * size=length of the side of the square cave system.
+ */
+
+void generate_hmap(int y0, int x0, int xsiz, int ysiz, int grd,
+ int roug, int cutoff)
+{
+ int xhsize, yhsize, xsize, ysize, maxsize;
+
+ /*
+ * fixed point variables- these are stored as 256 x normal value
+ * this gives 8 binary places of fractional part + 8 places of normal part
+ */
+ u16b xstep, xhstep, ystep, yhstep, i, j, diagsize, xxsize, yysize;
+
+
+ /* Redefine size so can change the value if out of range */
+ xsize = xsiz;
+ ysize = ysiz;
+
+ /* Paranoia about size of the system of caves*/
+ if (xsize > 254) xsize = 254;
+ if (xsize < 4) xsize = 4;
+ if (ysize > 254) ysize = 254;
+ if (ysize < 4) ysize = 4;
+
+ /* Get offsets to middle of array */
+ xhsize = xsize / 2;
+ yhsize = ysize / 2;
+
+ /* Fix rounding problem */
+ xsize = xhsize * 2;
+ ysize = yhsize * 2;
+
+ /*
+ * Scale factor for middle points:
+ * About sqrt(2)*256 - correct for a square lattice
+ * approximately correct for everything else.
+ */
+ diagsize = 362;
+
+ /* Maximum of xsize and ysize */
+ maxsize = (xsize > ysize) ? xsize : ysize;
+
+ /* Clear the section */
+ for (i = 0; i <= xsize; i++)
+ {
+ for (j = 0; j <= ysize; j++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[j + y0 - yhsize][i + x0 - xhsize];
+
+ /* 255 is a flag for "not done yet" */
+ c_ptr->feat = 255;
+
+ /* Clear icky flag because may be redoing the cave */
+ c_ptr->info &= ~(CAVE_ICKY);
+ }
+ }
+
+ /* Set the corner values just in case grd>size. */
+ store_height(0, 0, x0, y0, maxsize, xhsize, yhsize, cutoff);
+ store_height(0, ysize, x0, y0, maxsize, xhsize, yhsize, cutoff);
+ store_height(xsize, 0, x0, y0, maxsize, xhsize, yhsize, cutoff);
+ store_height(xsize, ysize, x0, y0, maxsize, xhsize, yhsize, cutoff);
+
+ /* Set the middle square to be an open area. */
+ store_height(xhsize, yhsize, x0, y0, 0, xhsize, yhsize, cutoff);
+
+
+ /* Initialise the step sizes */
+ xstep = xhstep = xsize * 256;
+ ystep = yhstep = ysize * 256;
+ xxsize = xsize * 256;
+ yysize = ysize * 256;
+
+ /*
+ * Fill in the rectangle with fractal height data - like the
+ * 'plasma fractal' in fractint
+ */
+ while ((xstep / 256 > 1) || (ystep / 256 > 1))
+ {
+ /* Halve the step sizes */
+ xstep = xhstep;
+ xhstep /= 2;
+ ystep = yhstep;
+ yhstep /= 2;
+
+ /* Middle top to bottom */
+ for (i = xhstep; i <= xxsize - xhstep; i += xstep)
+ {
+ for (j = 0; j <= yysize; j += ystep)
+ {
+ /* If greater than 'grid' level then is random */
+ if (xhstep / 256 > grd)
+ {
+ store_height(i / 256, j / 256, x0, y0, randint(maxsize),
+ xhsize, yhsize, cutoff);
+ }
+ else
+ {
+ cave_type *l, *r;
+ byte val;
+
+ /* Left point */
+ l = &cave[j / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize];
+
+ /* Right point */
+ r = &cave[j / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize];
+
+ /* Average of left and right points + random bit */
+ val = (l->feat + r->feat) / 2 +
+ (randint(xstep / 256) - xhstep / 256) * roug / 16;
+
+ store_height(i / 256, j / 256, x0, y0, val,
+ xhsize, yhsize, cutoff);
+ }
+ }
+ }
+
+
+ /* Middle left to right */
+ for (j = yhstep; j <= yysize - yhstep; j += ystep)
+ {
+ for (i = 0; i <= xxsize; i += xstep)
+ {
+ /* If greater than 'grid' level then is random */
+ if (xhstep / 256 > grd)
+ {
+ store_height(i / 256, j / 256, x0, y0, randint(maxsize),
+ xhsize, yhsize, cutoff);
+ }
+ else
+ {
+ cave_type *u, *d;
+ byte val;
+
+ /* Up point */
+ u = &cave[(j - yhstep) / 256 + y0 - yhsize][i / 256 + x0 - xhsize];
+
+ /* Down point */
+ d = &cave[(j + yhstep) / 256 + y0 - yhsize][i / 256 + x0 - xhsize];
+
+ /* Average of up and down points + random bit */
+ val = (u->feat + d->feat) / 2 +
+ (randint(ystep / 256) - yhstep / 256) * roug / 16;
+
+ store_height(i / 256, j / 256, x0, y0, val,
+ xhsize, yhsize, cutoff);
+ }
+ }
+ }
+
+ /* Center */
+ for (i = xhstep; i <= xxsize - xhstep; i += xstep)
+ {
+ for (j = yhstep; j <= yysize - yhstep; j += ystep)
+ {
+ /* If greater than 'grid' level then is random */
+ if (xhstep / 256 > grd)
+ {
+ store_height(i / 256, j / 256, x0, y0, randint(maxsize),
+ xhsize, yhsize, cutoff);
+ }
+ else
+ {
+ cave_type *ul, *dl, *ur, *dr;
+ byte val;
+
+ /* Up-left point */
+ ul = &cave[(j - yhstep) / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize];
+
+ /* Down-left point */
+ dl = &cave[(j + yhstep) / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize];
+
+ /* Up-right point */
+ ur = &cave[(j - yhstep) / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize];
+
+ /* Down-right point */
+ dr = &cave[(j + yhstep) / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize];
+
+ /*
+ * average over all four corners + scale by diagsize to
+ * reduce the effect of the square grid on the shape
+ * of the fractal
+ */
+ val = (ul->feat + dl->feat + ur->feat + dr->feat) / 4 +
+ (randint(xstep / 256) - xhstep / 256) *
+ (diagsize / 16) / 256 * roug;
+
+ store_height(i / 256, j / 256, x0, y0, val,
+ xhsize, yhsize , cutoff);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Convert from height-map back to the normal Angband cave format
+ */
+static bool hack_isnt_wall(int y, int x, int cutoff)
+{
+ /* Already done */
+ if (cave[y][x].info & CAVE_ICKY)
+ {
+ return (FALSE);
+ }
+
+ else
+ {
+ /* Show that have looked at this square */
+ cave[y][x].info |= (CAVE_ICKY);
+
+ /* If less than cutoff then is a floor */
+ if (cave[y][x].feat <= cutoff)
+ {
+ place_floor(y, x);
+ return (TRUE);
+ }
+
+ /* If greater than cutoff then is a wall */
+ else
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ return (FALSE);
+ }
+ }
+}
+
+
+/*
+ * Quick and nasty fill routine used to find the connected region
+ * of floor in the middle of the cave
+ */
+static void fill_hack(int y0, int x0, int y, int x, int xsize, int ysize,
+ int cutoff, int *amount)
+{
+ int i, j;
+
+ /* check 8 neighbours +self (self is caught in the isnt_wall function) */
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ /* If within bounds */
+ if ((x + i > 0) && (x + i < xsize) &&
+ (y + j > 0) && (y + j < ysize))
+ {
+ /* If not a wall or floor done before */
+ if (hack_isnt_wall(y + j + y0 - ysize / 2,
+ x + i + x0 - xsize / 2, cutoff))
+ {
+ /* then fill from the new point*/
+ fill_hack(y0, x0, y + j, x + i, xsize, ysize,
+ cutoff, amount);
+
+ /* keep tally of size of cave system */
+ (*amount)++;
+ }
+ }
+
+ /* Affect boundary */
+ else
+ {
+ cave[y0 + y + j - ysize / 2][x0 + x + i - xsize / 2].info |= (CAVE_ICKY);
+ }
+ }
+ }
+}
+
+
+bool generate_fracave(int y0, int x0, int xsize, int ysize,
+ int cutoff, bool light, bool room)
+{
+ int x, y, i, amount, xhsize, yhsize;
+ cave_type *c_ptr;
+
+ /* Offsets to middle from corner */
+ xhsize = xsize / 2;
+ yhsize = ysize / 2;
+
+ /* Reset tally */
+ amount = 0;
+
+ /*
+ * Select region connected to center of cave system
+ * this gets rid of alot of isolated one-sqaures that
+ * can make teleport traps instadeaths...
+ */
+ fill_hack(y0, x0, yhsize, xhsize, xsize, ysize, cutoff, &amount);
+
+ /* If tally too small, try again */
+ if (amount < 10)
+ {
+ /* Too small -- clear area and try again later */
+ for (x = 0; x <= xsize; ++x)
+ {
+ for (y = 0; y < ysize; ++y)
+ {
+ place_filler(y0 + y - yhsize, x0 + x - xhsize);
+ cave[y0 + y - yhsize][x0 + x - xhsize].info &= ~(CAVE_ICKY | CAVE_ROOM);
+ }
+ }
+ return FALSE;
+ }
+
+
+ /*
+ * Do boundaries -- check to see if they are next to a filled region
+ * If not then they are set to normal granite
+ * If so then they are marked as room walls
+ */
+ for (i = 0; i <= xsize; ++i)
+ {
+ /* Access top boundary grid */
+ c_ptr = &cave[0 + y0 - yhsize][i + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(0 + y0 - yhsize, i + x0 - xhsize, feat_wall_outer);
+
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(0 + y0 - yhsize, i + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(0 + y0 - yhsize, i + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+
+ /* Access bottom boundary grid */
+ c_ptr = &cave[ysize + y0 - yhsize][i + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(ysize + y0 - yhsize, i + x0 - xhsize, feat_wall_outer);
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(ysize + y0 - yhsize, i + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(ysize + y0 - yhsize, i + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+ }
+
+
+ /* Do the left and right boundaries minus the corners (done above) */
+ for (i = 1; i < ysize; ++i)
+ {
+ /* Access left boundary grid */
+ c_ptr = &cave[i + y0 - yhsize][0 + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(i + y0 - yhsize, 0 + x0 - xhsize, feat_wall_outer);
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(i + y0 - yhsize, 0 + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(i + y0 - yhsize, 0 + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+
+ /* Access left boundary grid */
+ c_ptr = &cave[i + y0 - yhsize][xsize + x0 - xhsize];
+
+ /* Next to a 'filled' region? -- set to be room walls */
+ if (c_ptr->info & CAVE_ICKY)
+ {
+ cave_set_feat(i + y0 - yhsize, xsize + x0 - xhsize, feat_wall_outer);
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(i + y0 - yhsize, xsize + x0 - xhsize);
+ }
+ }
+
+ /* Outside of the room -- set to be normal granite */
+ else
+ {
+ place_filler(i + y0 - yhsize, xsize + x0 - xhsize);
+ }
+
+ /* Clear the icky flag -- don't need it any more */
+ c_ptr->info &= ~(CAVE_ICKY);
+ }
+
+
+ /*
+ * Do the rest: convert back to the normal format
+ * In other variants, may want to check to see if cave.feat< some value
+ * if so, set to be water:- this will make interesting pools etc.
+ * (I don't do this for standard Angband.)
+ */
+ for (x = 1; x < xsize; ++x)
+ {
+ for (y = 1; y < ysize; ++y)
+ {
+ /* Access the grid */
+ c_ptr = &cave[y + y0 - yhsize][x + x0 - xhsize];
+
+ /* A floor grid to be converted */
+ if ((f_info[c_ptr->feat].flags1 & FF1_FLOOR) &&
+ (c_ptr->info & CAVE_ICKY))
+
+ {
+ /* Clear the icky flag in the filled region */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+ /* Set appropriate flags */
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room) c_ptr->info |= (CAVE_ROOM);
+ }
+
+ /* A wall grid to be convereted */
+ else if ((c_ptr->feat == feat_wall_outer) &&
+ (c_ptr->info & CAVE_ICKY))
+ {
+ /* Clear the icky flag in the filled region */
+ c_ptr->info &= ~(CAVE_ICKY);
+
+ /* Set appropriate flags */
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ if (room)
+ {
+ c_ptr->info |= (CAVE_ROOM);
+ }
+ else
+ {
+ place_filler(y + y0 - yhsize, x + x0 - xhsize);
+ }
+ }
+
+ /* None of the above -- clear the unconnected regions */
+ else
+ {
+ place_filler(y + y0 - yhsize, x + x0 - xhsize);
+ c_ptr->info &= ~(CAVE_ICKY | CAVE_ROOM);
+ }
+ }
+ }
+
+ /*
+ * XXX XXX XXX There is a slight problem when tunnels pierce the caves:
+ * Extra doors appear inside the system. (Its not very noticeable though.)
+ * This can be removed by "filling" from the outside in. This allows
+ * a separation from FEAT_WALL_OUTER with FEAT_WALL_INNER. (Internal
+ * walls are F.W.OUTER instead.)
+ * The extra effort for what seems to be only a minor thing (even
+ * non-existant if you think of the caves not as normal rooms, but as
+ * holes in the dungeon), doesn't seem worth it.
+ */
+
+ return (TRUE);
+}
+
+
+/*
+ * Makes a cave system in the center of the dungeon
+ */
+static void build_cavern(void)
+{
+ int grd, roug, cutoff, xsize, ysize, x0, y0;
+ bool done, light, room;
+
+ light = done = room = FALSE;
+ if (dun_level <= randint(25)) light = TRUE;
+
+ /* Make a cave the size of the dungeon */
+ xsize = cur_wid - 1;
+ ysize = cur_hgt - 1;
+ x0 = xsize / 2;
+ y0 = ysize / 2;
+
+ /* Paranoia: make size even */
+ xsize = x0 * 2;
+ ysize = y0 * 2;
+
+ while (!done)
+ {
+ /* Testing values for these parameters: feel free to adjust */
+ grd = 1 << (randint(4) + 4);
+
+ /* Want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* About size/2 */
+ cutoff = xsize / 2;
+
+ /* Make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format+ clean up*/
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room);
+ }
+}
+
+/*
+ * Driver routine to create fractal cave system
+ */
+static void build_type10(int by0, int bx0)
+{
+ int grd, roug, cutoff, xsize, ysize, y0, x0;
+
+ bool done, light, room;
+
+ /* Get size: note 'Evenness'*/
+ xsize = randint(22) * 2 + 6;
+ ysize = randint(15) * 2 + 6;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 1, ysize + 1, FALSE, by0, bx0, &x0, &y0)) return;
+
+ light = done = FALSE;
+ room = TRUE;
+
+ if (dun_level <= randint(25)) light = TRUE;
+
+ while (!done)
+ {
+ /*
+ * Note: size must be even or there are rounding problems
+ * This causes the tunnels not to connect properly to the room
+ */
+
+ /* Testing values for these parameters feel free to adjust */
+ grd = 1 << (randint(4));
+
+ /* Want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* About size/2 */
+ cutoff = randint(xsize / 4) + randint(ysize / 4) +
+ randint(xsize / 4) + randint(ysize / 4);
+
+ /* Make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format + clean up*/
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room);
+ }
+}
+
+
+/*
+ * Random vault generation from Z 2.5.1
+ */
+
+/*
+ * Make a very small room centred at (x0, y0)
+ *
+ * This is used in crypts, and random elemental vaults.
+ *
+ * Note - this should be used only on allocated regions
+ * within another room.
+ */
+static void build_small_room(int x0, int y0)
+{
+ build_rectangle(y0 - 1, x0 - 1, y0 + 1, x0 + 1, feat_wall_inner, CAVE_ROOM);
+
+ /* Place a secret door on one side */
+ switch (rand_int(4))
+ {
+ case 0:
+ {
+ place_secret_door(y0, x0 - 1);
+ break;
+ }
+
+ case 1:
+ {
+ place_secret_door(y0, x0 + 1);
+ break;
+ }
+
+ case 2:
+ {
+ place_secret_door(y0 - 1, x0);
+ break;
+ }
+
+ case 3:
+ {
+ place_secret_door(y0 + 1, x0);
+ break;
+ }
+ }
+
+ /* Add inner open space */
+ place_floor(y0, x0);
+}
+
+
+/*
+ * Add a door to a location in a random vault
+ *
+ * Note that range checking has to be done in the calling routine.
+ *
+ * The doors must be INSIDE the allocated region.
+ */
+static void add_door(int x, int y)
+{
+ /* Need to have a wall in the center square */
+ if (cave[y][x].feat != feat_wall_outer) return;
+
+ /*
+ * Look at:
+ * x#x
+ * .#.
+ * x#x
+ *
+ * where x=don't care
+ * .=floor, #=wall
+ */
+
+ if (get_is_floor(x, y - 1) && get_is_floor(x, y + 1) &&
+ (cave[y][x - 1].feat == feat_wall_outer) &&
+ (cave[y][x + 1].feat == feat_wall_outer))
+ {
+ /* secret door */
+ place_secret_door(y, x);
+
+ /* set boundarys so don't get wide doors */
+ place_filler(y, x - 1);
+ place_filler(y, x + 1);
+ }
+
+
+ /*
+ * Look at:
+ * x#x
+ * .#.
+ * x#x
+ *
+ * where x = don't care
+ * .=floor, #=wall
+ */
+ if ((cave[y - 1][x].feat == feat_wall_outer) &&
+ (cave[y + 1][x].feat == feat_wall_outer) &&
+ get_is_floor(x - 1, y) && get_is_floor(x + 1, y))
+ {
+ /* secret door */
+ place_secret_door(y, x);
+
+ /* set boundarys so don't get wide doors */
+ place_filler(y - 1, x);
+ place_filler(y + 1, x);
+ }
+}
+
+
+/*
+ * Fill the empty areas of a room with treasure and monsters.
+ */
+static void fill_treasure(int x1, int x2, int y1, int y2, int difficulty)
+{
+ int x, y, cx, cy, size;
+ s32b value;
+
+ /* center of room:*/
+ cx = (x1 + x2) / 2;
+ cy = (y1 + y2) / 2;
+
+ /* Rough measure of size of vault= sum of lengths of sides */
+ size = abs(x2 - x1) + abs(y2 - y1);
+
+ for (x = x1; x <= x2; x++)
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ /*
+ * Thing added based on distance to center of vault
+ * Difficulty is 1-easy to 10-hard
+ */
+ value = (((s32b)distance(cx, cy, x, y) * 100) / size) +
+ randint(10) - difficulty;
+
+ /* Hack -- Empty square part of the time */
+ if ((randint(100) - difficulty * 3) > 50) value = 20;
+
+ /* If floor, shallow water or lava */
+ if (get_is_floor(x, y) ||
+ (cave[y][x].feat == FEAT_SHAL_WATER) ||
+ (cave[y][x].feat == FEAT_SHAL_LAVA))
+ {
+ /* The smaller 'value' is, the better the stuff */
+ if (value < 0)
+ {
+ /* Meanest monster + treasure */
+ monster_level = dun_level + 40;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 20;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_FLOOR);
+ object_level = dun_level;
+ }
+ else if (value < 5)
+ {
+ /* Mean monster +treasure */
+ monster_level = dun_level + 20;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 10;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_FLOOR);
+ object_level = dun_level;
+ }
+ else if (value < 10)
+ {
+ /* Monster */
+ monster_level = dun_level + 9;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ else if (value < 17)
+ {
+ /* Intentional Blank space */
+
+ /*
+ * (Want some of the vault to be empty
+ * so have room for group monsters.
+ * This is used in the hack above to lower
+ * the density of stuff in the vault.)
+ */
+ }
+ else if (value < 23)
+ {
+ /* Object or trap */
+ if (rand_int(100) < 25)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+ }
+ else if (value < 30)
+ {
+ /* Monster and trap */
+ monster_level = dun_level + 5;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ place_trap(y, x);
+ }
+ else if (value < 40)
+ {
+ /* Monster or object */
+ if (rand_int(100) < 50)
+ {
+ monster_level = dun_level + 3;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ if (rand_int(100) < 50)
+ {
+ object_level = dun_level + 7;
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ object_level = dun_level;
+ }
+ }
+ else if (value < 50)
+ {
+ /* Trap */
+ place_trap(y, x);
+ }
+ else
+ {
+ /* Various Stuff */
+
+ /* 20% monster, 40% trap, 20% object, 20% blank space */
+ if (rand_int(100) < 20)
+ {
+ place_monster(y, x, TRUE, TRUE);
+ }
+ else if (rand_int(100) < 50)
+ {
+ place_trap(y, x);
+ }
+ else if (rand_int(100) < 50)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+ }
+
+ }
+ }
+ }
+}
+
+
+/*
+ * Creates a random vault that looks like a collection of bubbles
+ *
+ * It works by getting a set of coordinates that represent the center of
+ * each bubble. The entire room is made by seeing which bubble center is
+ * closest. If two centers are equidistant then the square is a wall,
+ * otherwise it is a floor. The only exception is for squares really
+ * near a center, these are always floor.
+ * (It looks better than without this check.)
+ *
+ * Note: If two centers are on the same point then this algorithm will create a
+ * blank bubble filled with walls. - This is prevented from happening.
+ */
+
+#define BUBBLENUM 10 /* number of bubbles */
+
+static void build_bubble_vault(int x0, int y0, int xsize, int ysize)
+{
+ /* array of center points of bubbles */
+ coord center[BUBBLENUM];
+
+ int i, j, k, x = 0, y = 0;
+ u16b min1, min2, temp;
+ bool done;
+
+ /* Offset from center to top left hand corner */
+ int xhsize = xsize / 2;
+ int yhsize = ysize / 2;
+
+ if (cheat_room) msg_print("Bubble Vault");
+
+ /* Allocate center of bubbles */
+ center[0].x = randint(xsize - 3) + 1;
+ center[0].y = randint(ysize - 3) + 1;
+
+ for (i = 1; i < BUBBLENUM; i++)
+ {
+ done = FALSE;
+
+ /* Get center and check to see if it is unique */
+ for (k = 0; !done && (k < 2000); k++)
+ {
+ done = TRUE;
+
+ x = randint(xsize - 3) + 1;
+ y = randint(ysize - 3) + 1;
+
+ for (j = 0; j < i; j++)
+ {
+ /* Rough test to see if there is an overlap */
+ if ((x == center[j].x) || (y == center[j].y)) done = FALSE;
+ }
+ }
+
+ /* Too many failures */
+ if (k >= 2000) return;
+
+ center[i].x = x;
+ center[i].y = y;
+ }
+
+ build_rectangle(y0 - yhsize, x0 - xhsize,
+ y0 - yhsize + ysize - 1, x0 - xhsize + xsize - 1,
+ feat_wall_outer, CAVE_ROOM | CAVE_ICKY);
+
+ /* Fill in middle with bubbles */
+ for (x = 1; x < xsize - 1; x++)
+ {
+ for (y = 1; y < ysize - 1; y++)
+ {
+ cave_type *c_ptr;
+
+ /* Get distances to two closest centers */
+
+ /* Initialise */
+ min1 = distance(x, y, center[0].x, center[0].y);
+ min2 = distance(x, y, center[1].x, center[1].y);
+
+ if (min1 > min2)
+ {
+ /* Swap if in wrong order */
+ temp = min1;
+ min1 = min2;
+ min2 = temp;
+ }
+
+ /* Scan the rest */
+ for (i = 2; i < BUBBLENUM; i++)
+ {
+ temp = distance(x, y, center[i].x, center[i].y);
+
+ if (temp < min1)
+ {
+ /* Smallest */
+ min2 = min1;
+ min1 = temp;
+ }
+ else if (temp < min2)
+ {
+ /* Second smallest */
+ min2 = temp;
+ }
+ }
+
+ /* Access the grid */
+ c_ptr = &cave[y + y0 - yhsize][x + x0 - xhsize];
+
+ /*
+ * Boundary at midpoint+ not at inner region of bubble
+ *
+ * SCSCSC: was feat_wall_outer
+ */
+ if (((min2 - min1) <= 2) && (!(min1 < 3)))
+ {
+ place_filler(y + y0 - yhsize, x + x0 - xhsize);
+ }
+
+ /* Middle of a bubble */
+ else
+ {
+ place_floor(y + y0 - yhsize, x + x0 - xhsize);
+ }
+
+ /* Clean up rest of flags */
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+ }
+ }
+
+ /* Try to add some random doors */
+ for (i = 0; i < 500; i++)
+ {
+ x = randint(xsize - 3) - xhsize + x0 + 1;
+ y = randint(ysize - 3) - yhsize + y0 + 1;
+ add_door(x, y);
+ }
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 2,
+ y0 - yhsize + 1, y0 - yhsize + ysize - 2, randint(5));
+}
+
+
+/*
+ * Convert FEAT_WALL_EXTRA (used by random vaults) to normal dungeon wall
+ */
+static void convert_extra(int y1, int x1, int y2, int x2)
+{
+ int x, y;
+
+ for (x = x1; x <= x2; x++)
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ if (cave[y][x].feat == FEAT_WALL_OUTER)
+ {
+ place_filler(y, x);
+ }
+ }
+ }
+}
+
+
+/*
+ * Overlay a rectangular room given its bounds
+ *
+ * This routine is used by build_room_vault (hence FEAT_WALL_OUTER)
+ * The area inside the walls is not touched: only granite is removed
+ * and normal walls stay
+ */
+static void build_room(int x1, int x2, int y1, int y2)
+{
+ int x, y, xsize, ysize, temp;
+
+ /* Check if rectangle has no width */
+ if ((x1 == x2) || (y1 == y2)) return;
+
+ /* initialize */
+ if (x1 > x2)
+ {
+ /* Swap boundaries if in wrong order */
+ temp = x1;
+ x1 = x2;
+ x2 = temp;
+ }
+
+ if (y1 > y2)
+ {
+ /* Swap boundaries if in wrong order */
+ temp = y1;
+ y1 = y2;
+ y2 = temp;
+ }
+
+ /* Get total widths */
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+
+ build_rectangle(y1, x1, y2, x2, feat_wall_outer, CAVE_ROOM | CAVE_ICKY);
+
+ /* Middle */
+ for (x = 1; x < xsize; x++)
+ {
+ for (y = 1; y < ysize; y++)
+ {
+ if (cave[y1 + y][x1 + x].feat == FEAT_WALL_OUTER)
+ {
+ /* Clear the untouched region */
+ place_floor(y1 + y, x1 + x);
+ cave[y1 + y][x1 + x].info |= (CAVE_ROOM | CAVE_ICKY);
+ }
+ else
+ {
+ /* Make it a room -- but don't touch */
+ cave[y1 + y][x1 + x].info |= (CAVE_ROOM | CAVE_ICKY);
+ }
+ }
+ }
+}
+
+
+/*
+ * Create a random vault that looks like a collection of overlapping rooms
+ */
+static void build_room_vault(int x0, int y0, int xsize, int ysize)
+{
+ int i, x1, x2, y1, y2, xhsize, yhsize;
+
+ /* Get offset from center */
+ xhsize = xsize / 2;
+ yhsize = ysize / 2;
+
+ if (cheat_room) msg_print("Room Vault");
+
+ /* Fill area so don't get problems with arena levels */
+ for (x1 = 0; x1 <= xsize; x1++)
+ {
+ int x = x0 - xhsize + x1;
+
+ for (y1 = 0; y1 <= ysize; y1++)
+ {
+ int y = y0 - yhsize + y1;
+
+ cave_set_feat(y, x, FEAT_WALL_OUTER);
+ cave[y][x].info &= ~(CAVE_ICKY);
+ }
+ }
+
+ /* Add ten random rooms */
+ for (i = 0; i < 10; i++)
+ {
+ x1 = randint(xhsize) * 2 + x0 - xhsize;
+ x2 = randint(xhsize) * 2 + x0 - xhsize;
+ y1 = randint(yhsize) * 2 + y0 - yhsize;
+ y2 = randint(yhsize) * 2 + y0 - yhsize;
+
+ build_room(x1, x2, y1, y2);
+ }
+
+ convert_extra(y0 - yhsize, x0 - xhsize, y0 - yhsize + ysize,
+ x0 - xhsize + xsize);
+
+ /* Add some random doors */
+ for (i = 0; i < 500; i++)
+ {
+ x1 = randint(xsize - 2) - xhsize + x0 + 1;
+ y1 = randint(ysize - 2) - yhsize + y0 + 1;
+ add_door(x1, y1);
+ }
+
+ /* Fill with monsters and treasure, high difficulty */
+ fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 1,
+ y0 - yhsize + 1, y0 - yhsize + ysize - 1, randint(5) + 5);
+}
+
+
+/*
+ * Create a random vault out of a fractal cave
+ */
+static void build_cave_vault(int x0, int y0, int xsiz, int ysiz)
+{
+ int grd, roug, cutoff, xhsize, yhsize, xsize, ysize, x, y;
+ bool done, light, room;
+
+ /* Round to make sizes even */
+ xhsize = xsiz / 2;
+ yhsize = ysiz / 2;
+ xsize = xhsize * 2;
+ ysize = yhsize * 2;
+
+ if (cheat_room) msg_print("Cave Vault");
+
+ light = done = FALSE;
+ room = TRUE;
+
+ while (!done)
+ {
+ /* Testing values for these parameters feel free to adjust */
+ grd = 1 << rand_int(4);
+
+ /* Want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* About size/2 */
+ cutoff = randint(xsize / 4) + randint(ysize / 4) +
+ randint(xsize / 4) + randint(ysize / 4);
+
+ /* Make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format + clean up */
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room);
+ }
+
+ /* Set icky flag because is a vault */
+ for (x = 0; x <= xsize; x++)
+ {
+ for (y = 0; y <= ysize; y++)
+ {
+ cave[y0 - yhsize + y][x0 - xhsize + x].info |= CAVE_ICKY;
+ }
+ }
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 1,
+ y0 - yhsize + 1, y0 - yhsize + ysize - 1, randint(5));
+}
+
+
+/*
+ * Maze vault -- rectangular labyrinthine rooms
+ *
+ * maze vault uses two routines:
+ * r_visit - a recursive routine that builds the labyrinth
+ * build_maze_vault - a driver routine that calls r_visit and adds
+ * monsters, traps and treasure
+ *
+ * The labyrinth is built by creating a spanning tree of a graph.
+ * The graph vertices are at
+ * (x, y) = (2j + x1, 2k + y1) j = 0,...,m-1 k = 0,...,n-1
+ * and the edges are the vertical and horizontal nearest neighbors.
+ *
+ * The spanning tree is created by performing a suitably randomized
+ * depth-first traversal of the graph. The only adjustable parameter
+ * is the rand_int(3) below; it governs the relative density of
+ * twists and turns in the labyrinth: smaller number, more twists.
+ */
+static void r_visit(int y1, int x1, int y2, int x2,
+ int node, int dir, int *visited)
+{
+ int i, j, m, n, temp, x, y, adj[4];
+
+ /* Dimensions of vertex array */
+ m = (x2 - x1) / 2 + 1;
+ n = (y2 - y1) / 2 + 1;
+
+ /* Mark node visited and set it to a floor */
+ visited[node] = 1;
+ x = 2 * (node % m) + x1;
+ y = 2 * (node / m) + y1;
+ place_floor(y, x);
+
+ /* Setup order of adjacent node visits */
+ if (rand_int(3) == 0)
+ {
+ /* Pick a random ordering */
+ for (i = 0; i < 4; i++)
+ {
+ adj[i] = i;
+ }
+ for (i = 0; i < 4; i++)
+ {
+ j = rand_int(4);
+ temp = adj[i];
+ adj[i] = adj[j];
+ adj[j] = temp;
+ }
+ dir = adj[0];
+ }
+ else
+ {
+ /* Pick a random ordering with dir first */
+ adj[0] = dir;
+ for (i = 1; i < 4; i++)
+ {
+ adj[i] = i;
+ }
+ for (i = 1; i < 4; i++)
+ {
+ j = 1 + rand_int(3);
+ temp = adj[i];
+ adj[i] = adj[j];
+ adj[j] = temp;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ switch (adj[i])
+ {
+ /* (0,+) - check for bottom boundary */
+ case 0:
+ {
+ if ((node / m < n - 1) && (visited[node + m] == 0))
+ {
+ place_floor(y + 1, x);
+ r_visit(y1, x1, y2, x2, node + m, dir, visited);
+ }
+ break;
+ }
+
+ /* (0,-) - check for top boundary */
+ case 1:
+ {
+ if ((node / m > 0) && (visited[node - m] == 0))
+ {
+ place_floor(y - 1, x);
+ r_visit(y1, x1, y2, x2, node - m, dir, visited);
+ }
+ break;
+ }
+
+ /* (+,0) - check for right boundary */
+ case 2:
+ {
+ if ((node % m < m - 1) && (visited[node + 1] == 0))
+ {
+ place_floor(y, x + 1);
+ r_visit(y1, x1, y2, x2, node + 1, dir, visited);
+ }
+ break;
+ }
+
+ /* (-,0) - check for left boundary */
+ case 3:
+ {
+ if ((node % m > 0) && (visited[node - 1] == 0))
+ {
+ place_floor(y, x - 1);
+ r_visit(y1, x1, y2, x2, node - 1, dir, visited);
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+static void build_maze_vault(int x0, int y0, int xsize, int ysize)
+{
+ int y, x, dy, dx;
+ int y1, x1, y2, x2;
+ int i, m, n, num_vertices, *visited;
+ bool light;
+ cave_type *c_ptr;
+
+
+ if (cheat_room) msg_print("Maze Vault");
+
+ /* Choose lite or dark */
+ light = (dun_level <= randint(25));
+
+ /* Pick a random room size - randomized by calling routine */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ c_ptr = &cave[y][x];
+
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+
+ if ((x == x1 - 1) || (x == x2 + 1) ||
+ (y == y1 - 1) || (y == y2 + 1))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ else
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ if (light) c_ptr->info |= (CAVE_GLOW);
+ }
+ }
+
+ /* Dimensions of vertex array */
+ m = dx + 1;
+ n = dy + 1;
+ num_vertices = m * n;
+
+ /* Allocate an array for visited vertices */
+ C_MAKE(visited, num_vertices, int);
+
+ /* Initialise array of visited vertices */
+ for (i = 0; i < num_vertices; i++)
+ {
+ visited[i] = 0;
+ }
+
+ /* Traverse the graph to create a spaning tree, pick a random root */
+ r_visit(y1, x1, y2, x2, rand_int(num_vertices), 0, visited);
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x1, x2, y1, y2, randint(5));
+
+ /* Free the array for visited vertices */
+ C_FREE(visited, num_vertices, int);
+}
+
+
+/*
+ * Build a "mini" checkerboard vault
+ *
+ * This is done by making a permanent wall maze and setting
+ * the diagonal sqaures of the checker board to be granite.
+ * The vault has two entrances on opposite sides to guarantee
+ * a way to get in even if the vault abuts a side of the dungeon.
+ */
+static void build_mini_c_vault(int x0, int y0, int xsize, int ysize)
+{
+ int dy, dx;
+ int y1, x1, y2, x2, y, x, total;
+ int i, m, n, num_vertices;
+ int *visited;
+
+ if (cheat_room) msg_print("Mini Checker Board Vault");
+
+ /* Pick a random room size */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Permanent walls */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ }
+ }
+
+
+ /* Dimensions of vertex array */
+ m = dx + 1;
+ n = dy + 1;
+ num_vertices = m * n;
+
+ /* Allocate an array for visited vertices */
+ C_MAKE(visited, num_vertices, int);
+
+ /* Initialise array of visited vertices */
+ for (i = 0; i < num_vertices; i++)
+ {
+ visited[i] = 0;
+ }
+
+ /* Traverse the graph to create a spannng tree, pick a random root */
+ r_visit(y1, x1, y2, x2, rand_int(num_vertices), 0, visited);
+
+ /* Make it look like a checker board vault */
+ for (x = x1; x <= x2; x++)
+ {
+ for (y = y1; y <= y2; y++)
+ {
+ total = x - x1 + y - y1;
+
+ /* If total is odd and is a floor, then make a wall */
+ if ((total % 2 == 1) && get_is_floor(x, y))
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Make a couple of entrances */
+ if (rand_int(2) == 0)
+ {
+ /* Left and right */
+ y = randint(dy) + dy / 2;
+ cave_set_feat(y1 + y, x1 - 1, feat_wall_outer);
+ cave_set_feat(y1 + y, x2 + 1, feat_wall_outer);
+ }
+ else
+ {
+ /* Top and bottom */
+ x = randint(dx) + dx / 2;
+ cave_set_feat(y1 - 1, x1 + x, feat_wall_outer);
+ cave_set_feat(y2 + 1, x1 + x, feat_wall_outer);
+ }
+
+ /* Fill with monsters and treasure, highest difficulty */
+ fill_treasure(x1, x2, y1, y2, 10);
+
+ /* Free the array for visited vertices */
+ C_FREE(visited, num_vertices, int);
+}
+
+
+/*
+ * Build a town/ castle by using a recursive algorithm.
+ * Basically divide each region in a probalistic way to create
+ * smaller regions. When the regions get too small stop.
+ *
+ * The power variable is a measure of how well defended a region is.
+ * This alters the possible choices.
+ */
+static void build_recursive_room(int x1, int y1, int x2, int y2, int power)
+{
+ int xsize, ysize;
+ int x, y;
+ int choice;
+
+ /* Temp variables */
+ int t1, t2, t3, t4;
+
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+
+ if ((power < 3) && (xsize > 12) && (ysize > 12))
+ {
+ /* Need outside wall +keep */
+ choice = 1;
+ }
+ else
+ {
+ if (power < 10)
+ {
+ /* Make rooms + subdivide */
+ if ((randint(10) > 2) && (xsize < 8) && (ysize < 8))
+ {
+ choice = 4;
+ }
+ else
+ {
+ choice = randint(2) + 1;
+ }
+ }
+ else
+ {
+ /* Mostly subdivide */
+ choice = randint(3) + 1;
+ }
+ }
+
+ /* Based on the choice made above, do something */
+ switch (choice)
+ {
+ /* Outer walls */
+ case 1:
+ {
+ /* Top and bottom */
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y1, x, feat_wall_outer);
+ cave_set_feat(y2, x, feat_wall_outer);
+ }
+
+ /* Left and right */
+ for (y = y1 + 1; y < y2; y++)
+ {
+ cave_set_feat(y, x1, feat_wall_outer);
+ cave_set_feat(y, x2, feat_wall_outer);
+ }
+
+ /* Make a couple of entrances */
+ if (rand_int(2) == 0)
+ {
+ /* Left and right */
+ y = randint(ysize) + y1;
+ place_floor(y, x1);
+ place_floor(y, x2);
+ }
+ else
+ {
+ /* Top and bottom */
+ x = randint(xsize) + x1;
+ place_floor(y1, x);
+ place_floor(y2, x);
+ }
+
+ /* Select size of keep */
+ t1 = randint(ysize / 3) + y1;
+ t2 = y2 - randint(ysize / 3);
+ t3 = randint(xsize / 3) + x1;
+ t4 = x2 - randint(xsize / 3);
+
+ /* Do outside areas */
+
+ /* Above and below keep */
+ build_recursive_room(x1 + 1, y1 + 1, x2 - 1, t1, power + 1);
+ build_recursive_room(x1 + 1, t2, x2 - 1, y2, power + 1);
+
+ /* Left and right of keep */
+ build_recursive_room(x1 + 1, t1 + 1, t3, t2 - 1, power + 3);
+ build_recursive_room(t4, t1 + 1, x2 - 1, t2 - 1, power + 3);
+
+ /* Make the keep itself: */
+ x1 = t3;
+ x2 = t4;
+ y1 = t1;
+ y2 = t2;
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+ power += 2;
+
+ /* Fall through */
+ }
+
+ /* Try to build a room */
+ case 4:
+ {
+ if ((xsize < 3) || (ysize < 3))
+ {
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+
+ /* Too small */
+ return;
+ }
+
+ /* Make outside walls */
+
+ /* Top and bottom */
+ for (x = x1 + 1; x <= x2 - 1; x++)
+ {
+ cave_set_feat(y1 + 1, x, feat_wall_inner);
+ cave_set_feat(y2 - 1, x, feat_wall_inner);
+ }
+
+ /* Left and right */
+ for (y = y1 + 1; y <= y2 - 1; y++)
+ {
+ cave_set_feat(y, x1 + 1, feat_wall_inner);
+ cave_set_feat(y, x2 - 1, feat_wall_inner);
+ }
+
+ /* Make a door */
+ y = randint(ysize - 3) + y1 + 1;
+
+ if (rand_int(2) == 0)
+ {
+ /* Left */
+ place_floor(y, x1 + 1);
+ }
+ else
+ {
+ /* Right */
+ place_floor(y, x2 - 1);
+ }
+
+ /* Build the room */
+ build_recursive_room(x1 + 2, y1 + 2, x2 - 2, y2 - 2, power + 3);
+
+ break;
+ }
+
+ /* Try and divide vertically */
+ case 2:
+ {
+ if (xsize < 3)
+ {
+ /* Too small */
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ return;
+ }
+
+ t1 = randint(xsize - 2) + x1 + 1;
+ build_recursive_room(x1, y1, t1, y2, power - 2);
+ build_recursive_room(t1 + 1, y1, x2, y2, power - 2);
+
+ break;
+ }
+
+ /* Try and divide horizontally */
+ case 3:
+ {
+ if (ysize < 3)
+ {
+ /* Too small */
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ return;
+ }
+
+ t1 = randint(ysize - 2) + y1 + 1;
+ build_recursive_room(x1, y1, x2, t1, power - 2);
+ build_recursive_room(x1, t1 + 1, x2, y2, power - 2);
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Build a castle
+ *
+ * Clear the region and call the recursive room routine.
+ *
+ * This makes a vault that looks like a castle or city in the dungeon.
+ */
+static void build_castle_vault(int x0, int y0, int xsize, int ysize)
+{
+ int dy, dx;
+ int y1, x1, y2, x2;
+ int y, x;
+
+ /* Pick a random room size */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+ if (cheat_room) msg_print("Castle Vault");
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Make everything a floor */
+ place_floor(y, x);
+ }
+ }
+
+ /* Make the castle */
+ build_recursive_room(x1, y1, x2, y2, randint(5));
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x1, x2, y1, y2, randint(3));
+}
+
+
+/*
+ * Add outer wall to a floored region
+ *
+ * Note: no range checking is done so must be inside dungeon
+ * This routine also stomps on doors
+ */
+static void add_outer_wall(int x, int y, int light, int x1, int y1,
+ int x2, int y2)
+{
+ int i, j;
+
+ if (!in_bounds(y, x)) return;
+
+ /*
+ * Hack -- Check to see if square has been visited before
+ * if so, then exit (use room flag to do this)
+ */
+ if (cave[y][x].info & CAVE_ROOM) return;
+
+ /* Set room flag */
+ cave[y][x].info |= (CAVE_ROOM);
+
+ if (get_is_floor(x, y))
+ {
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ if ((x + i >= x1) && (x + i <= x2) &&
+ (y + j >= y1) && (y + j <= y2))
+ {
+ add_outer_wall(x + i, y + j, light, x1, y1, x2, y2);
+ if (light) cave[y][x].info |= CAVE_GLOW;
+ }
+ }
+ }
+ }
+
+ /* Set bounding walls */
+ else if (cave[y][x].feat == FEAT_WALL_EXTRA)
+ {
+ cave[y][x].feat = feat_wall_outer;
+ if (light == TRUE) cave[y][x].info |= CAVE_GLOW;
+ }
+
+ /* Set bounding walls */
+ else if (cave[y][x].feat == FEAT_PERM_OUTER)
+ {
+ if (light == TRUE) cave[y][x].info |= CAVE_GLOW;
+ }
+}
+
+
+/*
+ * Hacked distance formula - gives the 'wrong' answer
+ *
+ * Used to build crypts
+ */
+static int dist2(int x1, int y1, int x2, int y2,
+ int h1, int h2, int h3, int h4)
+{
+ int dx, dy;
+ dx = abs(x2 - x1);
+ dy = abs(y2 - y1);
+
+ /*
+ * Basically this works by taking the normal pythagorean formula
+ * and using an expansion to express this in a way without the
+ * square root. This approximate formula is then perturbed to give
+ * the distorted results. (I found this by making a mistake when I was
+ * trying to fix the circular rooms.)
+ */
+
+ /* h1-h4 are constants that describe the metric */
+ if (dx >= 2 * dy) return (dx + (dy * h1) / h2);
+ if (dy >= 2 * dx) return (dy + (dx * h1) / h2);
+
+ /* 128/181 is approx. 1/sqrt(2) */
+ return (((dx + dy) * 128) / 181 +
+ (dx * dx / (dy * h3) + dy * dy / (dx * h3)) * h4);
+}
+
+
+/*
+ * Build target vault
+ *
+ * This is made by two concentric "crypts" with perpendicular
+ * walls creating the cross-hairs.
+ */
+static void build_target_vault(int x0, int y0, int xsize, int ysize)
+{
+ int rad, x, y;
+
+ int h1, h2, h3, h4;
+
+
+ /* Make a random metric */
+ h1 = randint(32) - 16;
+ h2 = randint(16);
+ h3 = randint(32);
+ h4 = randint(32) - 16;
+
+ if (cheat_room) msg_print("Target Vault");
+
+ /* Work out outer radius */
+ if (xsize > ysize)
+ {
+ rad = ysize / 2;
+ }
+ else
+ {
+ rad = xsize / 2;
+ }
+
+ /* Make floor */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear room flag */
+ c_ptr->info &= ~(CAVE_ROOM);
+
+ /* Grids in vaults are required to be "icky" */
+ c_ptr->info |= (CAVE_ICKY);
+
+ /* Inside -- floor */
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1)
+ {
+ place_floor(y, x);
+ }
+
+ /* Outside -- make it granite so that arena works */
+ else
+ {
+ c_ptr->feat = FEAT_WALL_EXTRA;
+ }
+
+ /* Proper boundary for arena */
+ if (((y + rad) == y0) || ((y - rad) == y0) ||
+ ((x + rad) == x0) || ((x - rad) == x0))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Find visible outer walls and set to be FEAT_OUTER */
+ add_outer_wall(x0, y0, FALSE, x0 - rad - 1, y0 - rad - 1,
+ x0 + rad + 1, y0 + rad + 1);
+
+ /* Add inner wall */
+ for (x = x0 - rad / 2; x <= x0 + rad / 2; x++)
+ {
+ for (y = y0 - rad / 2; y <= y0 + rad / 2; y++)
+ {
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) == rad / 2)
+ {
+ /* Make an internal wall */
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Add perpendicular walls */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ cave_set_feat(y0, x, feat_wall_inner);
+ }
+
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ cave_set_feat(y, x0, feat_wall_inner);
+ }
+
+ /* Make inner vault */
+ for (y = y0 - 1; y <= y0 + 1; y++)
+ {
+ cave_set_feat(y, x0 - 1, feat_wall_inner);
+ cave_set_feat(y, x0 + 1, feat_wall_inner);
+ }
+ for (x = x0 - 1; x <= x0 + 1; x++)
+ {
+ cave_set_feat(y0 - 1, x, feat_wall_inner);
+ cave_set_feat(y0 + 1, x, feat_wall_inner);
+ }
+
+ place_floor(y0, x0);
+
+
+ /*
+ * Add doors to vault
+ *
+ * Get two distances so can place doors relative to centre
+ */
+ x = (rad - 2) / 4 + 1;
+ y = rad / 2 + x;
+
+ add_door(x0 + x, y0);
+ add_door(x0 + y, y0);
+ add_door(x0 - x, y0);
+ add_door(x0 - y, y0);
+ add_door(x0, y0 + x);
+ add_door(x0, y0 + y);
+ add_door(x0, y0 - x);
+ add_door(x0, y0 - y);
+
+ /* Fill with stuff - medium difficulty */
+ fill_treasure(x0 - rad, x0 + rad, y0 - rad, y0 + rad, randint(3) + 3);
+}
+
+
+/*
+ * Random vaults
+ */
+static void build_type11(int by0, int bx0)
+{
+ int y0, x0, xsize, ysize, vtype;
+
+ /* Get size -- gig enough to look good, small enough to be fairly common */
+ xsize = randint(22) + 22;
+ ysize = randint(11) + 11;
+
+ /* Allocate in room_map. If will not fit, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &x0, &y0)) return;
+
+ /*
+ * Boost the rating -- Higher than lesser vaults and lower than
+ * greater vaults
+ */
+ rating += 10;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 1) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Select type of vault */
+ vtype = randint(8);
+
+ switch (vtype)
+ {
+ /* Build an appropriate room */
+ case 1:
+ {
+ build_bubble_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 2:
+ {
+ build_room_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 3:
+ {
+ build_cave_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 4:
+ {
+ build_maze_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 5:
+ {
+ build_mini_c_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 6:
+ {
+ build_castle_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 7:
+ {
+ build_target_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ /* I know how to add a few more... give me some time. */
+
+ /* Paranoia */
+ default:
+ {
+ return;
+ }
+ }
+}
+
+/*
+ * Crypt room generation from Z 2.5.1
+ */
+
+/*
+ * Build crypt room.
+ * For every grid in the possible square, check the (fake) distance.
+ * If it's less than the radius, make it a room square.
+ *
+ * When done fill from the inside to find the walls,
+ */
+static void build_type12(int by0, int bx0)
+{
+ int light, rad, x, y, x0, y0;
+ bool emptyflag = TRUE;
+ int h1, h2, h3, h4;
+
+ /* Make a random metric */
+ h1 = randint(32) - 16;
+ h2 = randint(16);
+ h3 = randint(32);
+ h4 = randint(32) - 16;
+
+ /* Occasional light */
+ light = (randint(dun_level) <= 5) ? TRUE : FALSE;
+
+ rad = randint(9);
+
+ /* Allocate in room_map. If will not fit, exit */
+ if (!room_alloc(rad * 2 + 3, rad * 2 + 3, FALSE, by0, bx0, &x0, &y0)) return;
+
+ /* Make floor */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ /* Clear room flag */
+ cave[y][x].info &= ~(CAVE_ROOM);
+
+ /* Inside -- floor */
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1)
+ {
+ place_floor(y, x);
+ }
+ else if (distance(y0, x0, y, x) < 3)
+ {
+ place_floor(y, x);
+ }
+
+ /* Outside -- make it granite so that arena works */
+ else
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+
+ /* Proper boundary for arena */
+ if (((y + rad) == y0) || ((y - rad) == y0) ||
+ ((x + rad) == x0) || ((x - rad) == x0))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Find visible outer walls and set to be FEAT_OUTER */
+ add_outer_wall(x0, y0, light, x0 - rad - 1, y0 - rad - 1,
+ x0 + rad + 1, y0 + rad + 1);
+
+ /* Check to see if there is room for an inner vault */
+ for (x = x0 - 2; x <= x0 + 2; x++)
+ {
+ for (y = y0 - 2; y <= y0 + 2; y++)
+ {
+ if (!get_is_floor(x, y))
+ {
+ /* Wall in the way */
+ emptyflag = FALSE;
+ }
+ }
+ }
+
+ if (emptyflag && (rand_int(2) == 0))
+ {
+ /* Build the vault */
+ build_small_room(x0, y0);
+
+ /* Place a treasure in the vault */
+ place_object(y0, x0, FALSE, FALSE, OBJ_FOUND_FLOOR);
+
+ /* Let's guard the treasure well */
+ vault_monsters(y0, x0, rand_int(2) + 3);
+
+ /* Traps naturally */
+ vault_traps(y0, x0, 4, 4, rand_int(3) + 2);
+ }
+}
+
+
+/*
+ * Constructs a tunnel between two points
+ *
+ * This function must be called BEFORE any streamers are created,
+ * since we use the special "granite wall" sub-types to keep track
+ * of legal places for corridors to pierce rooms.
+ *
+ * We use "door_flag" to prevent excessive construction of doors
+ * along overlapping corridors.
+ *
+ * We queue the tunnel grids to prevent door creation along a corridor
+ * which intersects itself.
+ *
+ * We queue the wall piercing grids to prevent a corridor from leaving
+ * a room and then coming back in through the same entrance.
+ *
+ * We "pierce" grids which are "outer" walls of rooms, and when we
+ * do so, we change all adjacent "outer" walls of rooms into "solid"
+ * walls so that no two corridors may use adjacent grids for exits.
+ *
+ * The "solid" wall check prevents corridors from "chopping" the
+ * corners of rooms off, as well as "silly" door placement, and
+ * "excessively wide" room entrances.
+ *
+ * Useful "feat" values:
+ * FEAT_WALL_EXTRA -- granite walls
+ * FEAT_WALL_INNER -- inner room walls
+ * FEAT_WALL_OUTER -- outer room walls
+ * FEAT_WALL_SOLID -- solid room walls
+ * FEAT_PERM_EXTRA -- shop walls (perma)
+ * FEAT_PERM_INNER -- inner room walls (perma)
+ * FEAT_PERM_OUTER -- outer room walls (perma)
+ * FEAT_PERM_SOLID -- dungeon border (perma)
+ */
+static void build_tunnel(int row1, int col1, int row2, int col2, bool water)
+{
+ int i, y, x;
+ int tmp_row, tmp_col;
+ int row_dir, col_dir;
+ int start_row, start_col;
+ int main_loop_count = 0;
+
+ bool door_flag = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Reset the arrays */
+ dun->tunn_n = 0;
+ dun->wall_n = 0;
+
+ /* Save the starting location */
+ start_row = row1;
+ start_col = col1;
+
+ /* Start out in the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Keep going until done (or bored) */
+ while ((row1 != row2) || (col1 != col2))
+ {
+ /* Mega-Hack -- Paranoia -- prevent infinite loops */
+ if (main_loop_count++ > 2000) break;
+
+ /* Allow bends in the tunnel */
+ if (rand_int(100) < DUN_TUN_CHG)
+ {
+ /* Acquire the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Random direction */
+ if (rand_int(100) < DUN_TUN_RND)
+ {
+ rand_dir(&row_dir, &col_dir);
+ }
+ }
+
+ /* Get the next location */
+ tmp_row = row1 + row_dir;
+ tmp_col = col1 + col_dir;
+
+
+ /* Extremely Important -- do not leave the dungeon */
+ while (!in_bounds(tmp_row, tmp_col))
+ {
+ /* Acquire the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Random direction */
+ if (rand_int(100) < DUN_TUN_RND)
+ {
+ rand_dir(&row_dir, &col_dir);
+ }
+
+ /* Get the next location */
+ tmp_row = row1 + row_dir;
+ tmp_col = col1 + col_dir;
+ }
+
+
+ /* Access the location */
+ c_ptr = &cave[tmp_row][tmp_col];
+
+
+ /* Avoid the edge of the dungeon */
+ if (c_ptr->feat == FEAT_PERM_SOLID) continue;
+
+ /* Avoid the edge of vaults */
+ if (c_ptr->feat == FEAT_PERM_OUTER) continue;
+
+ /* Avoid "solid" granite walls */
+ if (c_ptr->feat == FEAT_WALL_SOLID) continue;
+
+ /*
+ * Pierce "outer" walls of rooms
+ * Cannot trust feat code any longer...
+ */
+ if ((c_ptr->feat == feat_wall_outer) &&
+ (c_ptr->info & CAVE_ROOM))
+ {
+ /* Acquire the "next" location */
+ y = tmp_row + row_dir;
+ x = tmp_col + col_dir;
+
+ /* Hack -- Avoid outer/solid permanent walls */
+ if (cave[y][x].feat == FEAT_PERM_SOLID) continue;
+ if (cave[y][x].feat == FEAT_PERM_OUTER) continue;
+
+ /* Hack -- Avoid outer/solid granite walls */
+ if ((cave[y][x].feat == feat_wall_outer) &&
+ (cave[y][x].info & CAVE_ROOM)) continue;
+ if (cave[y][x].feat == FEAT_WALL_SOLID) continue;
+
+ /* Accept this location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Save the wall location */
+ if (dun->wall_n < WALL_MAX)
+ {
+ dun->wall[dun->wall_n].y = row1;
+ dun->wall[dun->wall_n].x = col1;
+ dun->wall_n++;
+ }
+
+ /* Forbid re-entry near this piercing */
+ for (y = row1 - 1; y <= row1 + 1; y++)
+ {
+ for (x = col1 - 1; x <= col1 + 1; x++)
+ {
+ /* Convert adjacent "outer" walls as "solid" walls */
+ if ((cave[y][x].feat == feat_wall_outer) &&
+ (cave[y][x].info & CAVE_ROOM))
+ {
+ /* Change the wall to a "solid" wall */
+ /* Mega-Hack -- to be brought back later... */
+ cave_set_feat(y, x, FEAT_WALL_SOLID);
+ }
+ }
+ }
+ }
+
+ /* Travel quickly through rooms */
+ else if (c_ptr->info & (CAVE_ROOM))
+ {
+ /* Accept the location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+ }
+
+ /* Tunnel through all other walls */
+ else if ((c_ptr->feat == d_info[dungeon_type].fill_type1) ||
+ (c_ptr->feat == d_info[dungeon_type].fill_type2) ||
+ (c_ptr->feat == d_info[dungeon_type].fill_type3))
+ {
+ /* Accept this location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Save the tunnel location */
+ if (dun->tunn_n < TUNN_MAX)
+ {
+ dun->tunn[dun->tunn_n].y = row1;
+ dun->tunn[dun->tunn_n].x = col1;
+ dun->tunn_n++;
+ }
+
+ /* Allow door in next grid */
+ door_flag = FALSE;
+ }
+
+ /* Handle corridor intersections or overlaps */
+ else
+ {
+ /* Accept the location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Collect legal door locations */
+ if (!door_flag)
+ {
+ /* Save the door location */
+ if (dun->door_n < DOOR_MAX)
+ {
+ dun->door[dun->door_n].y = row1;
+ dun->door[dun->door_n].x = col1;
+ dun->door_n++;
+ }
+
+ /* No door in next grid */
+ door_flag = TRUE;
+ }
+
+ /* Hack -- allow pre-emptive tunnel termination */
+ if (rand_int(100) >= DUN_TUN_CON)
+ {
+ /* Distance between row1 and start_row */
+ tmp_row = row1 - start_row;
+ if (tmp_row < 0) tmp_row = ( -tmp_row);
+
+ /* Distance between col1 and start_col */
+ tmp_col = col1 - start_col;
+ if (tmp_col < 0) tmp_col = ( -tmp_col);
+
+ /* Terminate the tunnel */
+ if ((tmp_row > 10) || (tmp_col > 10)) break;
+ }
+ }
+ }
+
+
+ /* Turn the tunnel into corridor */
+ for (i = 0; i < dun->tunn_n; i++)
+ {
+ /* Access the grid */
+ y = dun->tunn[i].y;
+ x = dun->tunn[i].x;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear previous contents, add a floor */
+ if (!water)
+ {
+ place_floor(y, x);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAL_WATER);
+ }
+ }
+
+
+ /* Apply the piercings that we found */
+ for (i = 0; i < dun->wall_n; i++)
+ {
+ /* Access the grid */
+ y = dun->wall[i].y;
+ x = dun->wall[i].x;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear previous contents, add up floor */
+ place_floor(y, x);
+
+ /* Occasional doorway */
+ if (!(dungeon_flags1 & DF1_NO_DOORS) &&
+ (rand_int(100) < DUN_TUN_PEN))
+ {
+ /* Place a random door */
+ place_random_door(y, x);
+ }
+ }
+}
+
+
+
+
+/*
+ * Count the number of "corridor" grids adjacent to the given grid.
+ *
+ * Note -- Assumes "in_bounds(y1, x1)"
+ *
+ * XXX XXX This routine currently only counts actual "empty floor"
+ * grids which are not in rooms. We might want to also count stairs,
+ * open doors, closed doors, etc.
+ */
+static int next_to_corr(int y1, int x1)
+{
+ int i, y, x, k = 0;
+
+ cave_type *c_ptr;
+
+ /* Scan adjacent grids */
+ for (i = 0; i < 4; i++)
+ {
+ /* Extract the location */
+ y = y1 + ddy_ddd[i];
+ x = x1 + ddx_ddd[i];
+
+ /* Skip non floors */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Skip non "empty floor" grids */
+ if ((c_ptr->feat != d_info[dungeon_type].floor1) &&
+ (c_ptr->feat != d_info[dungeon_type].floor2) &&
+ (c_ptr->feat != d_info[dungeon_type].floor3))
+ {
+ continue;
+ }
+
+ /* Skip grids inside rooms */
+ if (c_ptr->info & (CAVE_ROOM)) continue;
+
+ /* Count these grids */
+ k++;
+ }
+
+ /* Return the number of corridors */
+ return (k);
+}
+
+
+/*
+ * Determine if the given location is "between" two walls,
+ * and "next to" two corridor spaces. XXX XXX XXX
+ *
+ * Assumes "in_bounds(y,x)"
+ */
+static bool possible_doorway(int y, int x)
+{
+ /* Count the adjacent corridors */
+ if (next_to_corr(y, x) >= 2)
+ {
+ /* Check Vertical */
+ if ((f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) &&
+ (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL))
+ {
+ return (TRUE);
+ }
+
+ /* Check Horizontal */
+ if ((f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) &&
+ (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL))
+ {
+ return (TRUE);
+ }
+ }
+
+ /* No doorway */
+ return (FALSE);
+}
+
+
+#if 0
+
+/*
+ * Places door at y, x position if at least 2 walls found
+ */
+static void try_door(int y, int x)
+{
+ /* Paranoia */
+ if (!in_bounds(y, x)) return;
+
+ /* Some dungeons don't have doors at all */
+ if (dungeon_flags1 & (DF1_NO_DOORS)) return;
+
+ /* Ignore walls */
+ if (f_info[cave[y][x].feat].flags1 & FF1_WALL) return;
+
+ /* Ignore room grids */
+ if (cave[y][x].info & (CAVE_ROOM)) return;
+
+ /* Occasional door (if allowed) */
+ if (possible_doorway(y, x) && (rand_int(100) < DUN_TUN_JCT))
+ {
+ /* Place a door */
+ place_random_door(y, x);
+ }
+}
+
+#endif /* 0 */
+
+
+/*
+ * Places doors around y, x position
+ */
+static void try_doors(int y, int x)
+{
+ bool dir_ok[4];
+ int i, k, n;
+ int yy, xx;
+
+ /* Paranoia */
+ /* if (!in_bounds(y, x)) return; */
+
+ /* Some dungeons don't have doors at all */
+ if (dungeon_flags1 & (DF1_NO_DOORS)) return;
+
+ /* Reset tally */
+ n = 0;
+
+ /* Look four cardinal directions */
+ for (i = 0; i < 4; i++)
+ {
+ /* Assume NG */
+ dir_ok[i] = FALSE;
+
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Out of level boundary */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Ignore walls */
+ if (f_info[cave[yy][xx].feat].flags1 & (FF1_WALL)) continue;
+
+ /* Ignore room grids */
+ if (cave[yy][xx].info & (CAVE_ROOM)) continue;
+
+ /* Not a doorway */
+ if (!possible_doorway(yy, xx)) continue;
+
+ /* Accept the direction */
+ dir_ok[i] = TRUE;
+
+ /* Count good spots */
+ n++;
+ }
+
+ /* Use the traditional method 75% of time */
+ if (rand_int(100) < 75)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ /* Bad locations */
+ if (!dir_ok[i]) continue;
+
+ /* Place one of various kinds of doors */
+ if (rand_int(100) < DUN_TUN_JCT)
+ {
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Place a door */
+ place_random_door(yy, xx);
+ }
+ }
+ }
+
+ /* Use alternative method */
+ else
+ {
+ /* A crossroad */
+ if (n == 4)
+ {
+ /* Clear OK flags XXX */
+ for (i = 0; i < 4; i++) dir_ok[i] = FALSE;
+
+ /* Put one or two secret doors */
+ dir_ok[rand_int(4)] = TRUE;
+ dir_ok[rand_int(4)] = TRUE;
+ }
+
+ /* A T-shaped intersection or two possible doorways */
+ else if ((n == 3) || (n == 2))
+ {
+ /* Pick one random location from the list */
+ k = rand_int(n);
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Reject all but k'th OK direction */
+ if (dir_ok[i] && (k-- != 0)) dir_ok[i] = FALSE;
+ }
+ }
+
+ /* Place secret door(s) */
+ for (i = 0; i < 4; i++)
+ {
+ /* Bad location */
+ if (!dir_ok[i]) continue;
+
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Place a secret door */
+ place_secret_door(yy, xx);
+ }
+ }
+}
+
+
+/*
+ * Attempt to build a room of the given type at the given block
+ *
+ * Note that we restrict the number of "crowded" rooms to reduce
+ * the chance of overflowing the monster list during level creation.
+ */
+static bool room_build(int y, int x, int typ)
+{
+ /* Restrict level */
+ if ((dun_level < roomdep[typ]) && !ironman_rooms) return (FALSE);
+
+ /* Restrict "crowded" rooms */
+ if (dun->crowded && ((typ == 5) || (typ == 6))) return (FALSE);
+
+ /* Build a room */
+ switch (typ)
+ {
+ /* Build an appropriate room */
+ case 12:
+ build_type12(y, x);
+ break;
+ case 11:
+ build_type11(y, x);
+ break;
+ case 10:
+ build_type10(y, x);
+ break;
+ case 9:
+ build_type9 (y, x);
+ break;
+ case 8:
+ build_type8 (y, x);
+ break;
+ case 7:
+ build_type7 (y, x);
+ break;
+ case 6:
+ build_type6 (y, x);
+ break;
+ case 5:
+ build_type5 (y, x);
+ break;
+ case 4:
+ build_type4 (y, x);
+ break;
+ case 3:
+ build_type3 (y, x);
+ break;
+ case 2:
+ build_type2 (y, x);
+ break;
+ case 1:
+ build_type1 (y, x);
+ break;
+
+ /* Paranoia */
+ default:
+ return (FALSE);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Set level boundaries
+ */
+void set_bounders(bool empty_level)
+{
+ int y, x;
+
+ /* Special boundary walls -- Top */
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[0][x].mimic = fill_type[rand_int(100)];
+ else cave[0][x].mimic = cave[0][x].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(0, x, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Bottom */
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[cur_hgt - 1][x].mimic = fill_type[rand_int(100)];
+ else cave[cur_hgt - 1][x].mimic = cave[cur_hgt - 1][x].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(cur_hgt - 1, x, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Left */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[y][0].mimic = fill_type[rand_int(100)];
+ else cave[y][0].mimic = cave[y][0].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(y, 0, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Right */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[y][cur_wid - 1].mimic = fill_type[rand_int(100)];
+ else cave[y][cur_wid - 1].mimic = cave[y][cur_wid - 1].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(y, cur_wid - 1, FEAT_PERM_SOLID);
+ }
+}
+
+/* Needed to refill empty levels */
+static void fill_level(bool use_floor, byte smooth);
+
+/*
+ * Generate a normal dungeon level
+ */
+bool level_generate_dungeon(cptr name)
+{
+ int i, k, y, x, y1, x1, branch = get_branch();
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ int max_vault_ok = 2;
+
+ bool destroyed = FALSE;
+ bool empty_level = FALSE;
+ bool cavern = FALSE;
+ s16b town_level = 0;
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ /* unused */
+ name = name;
+
+ /* Check for arena level */
+ if ((dungeon_flags1 & (DF1_EMPTY)) ||
+ (empty_levels && (rand_int(EMPTY_LEVEL) == 0)))
+ {
+ empty_level = TRUE;
+
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print("Arena level.");
+ }
+
+ /* Refill the level with floor tiles */
+ fill_level(empty_level, d_ptr->fill_method);
+ }
+
+ /* Possible cavern */
+ if ((dungeon_flags1 & DF1_CAVERN) && (rand_int(dun_level / 2) > DUN_CAVERN))
+ {
+ cavern = TRUE;
+
+ /* Make a large fractal cave in the middle of the dungeon */
+ if (cheat_room)
+ {
+ msg_print("Cavern on level.");
+ }
+
+ build_cavern();
+ }
+
+ /* Possible "destroyed" level */
+ if ((dun_level > 10) && (rand_int(DUN_DEST) == 0))
+ {
+ destroyed = TRUE;
+ }
+
+ /* Hack -- No destroyed "quest" levels */
+ if (is_quest(dun_level)) destroyed = FALSE;
+
+ /* Hack -- No destroyed "small" levels */
+ if ((cur_wid != MAX_WID) || (cur_hgt != MAX_HGT)) destroyed = FALSE;
+
+ /* Hack -- No destroyed levels */
+ if (dungeon_flags1 & DF1_NO_DESTROY) destroyed = FALSE;
+
+ /* Actual maximum number of rooms on this level */
+ dun->row_rooms = cur_hgt / BLOCK_HGT;
+ dun->col_rooms = cur_wid / BLOCK_WID;
+
+
+ /* Initialize the room table */
+ for (y = 0; y < dun->row_rooms; y++)
+ {
+ for (x = 0; x < dun->col_rooms; x++)
+ {
+ dun->room_map[y][x] = FALSE;
+ }
+ }
+
+ /* No "crowded" rooms yet */
+ dun->crowded = FALSE;
+
+ /* No rooms yet */
+ dun->cent_n = 0;
+
+ /* Pick a block for the room */
+ y = rand_int(dun->row_rooms);
+ x = rand_int(dun->col_rooms);
+
+ /* Align dungeon rooms */
+ if (dungeon_align)
+ {
+ /* Slide some rooms right */
+ if ((x % 3) == 0) x++;
+
+ /* Slide some rooms left */
+ if ((x % 3) == 2) x--;
+ }
+
+ /* Ugly */
+ process_hooks(HOOK_BUILD_ROOM1, "(d,d)", y, x);
+
+ /* Build some rooms */
+ for (i = 0; i < DUN_ROOMS; i++)
+ {
+ /* Pick a block for the room */
+ y = rand_int(dun->row_rooms);
+ x = rand_int(dun->col_rooms);
+
+ /* Align dungeon rooms */
+ if (dungeon_align)
+ {
+ /* Slide some rooms right */
+ if ((x % 3) == 0) x++;
+
+ /* Slide some rooms left */
+ if ((x % 3) == 2) x--;
+ }
+
+ /* Destroyed levels are boring */
+ if (destroyed)
+ {
+ /* The deeper you are, the more cavelike the rooms are */
+
+ /* no caves when cavern exists: they look bad */
+ k = randint(100);
+
+ if (!cavern && (k < dun_level))
+ {
+ /* Type 10 -- Fractal cave */
+ if (room_build(y, x, 10)) continue;
+ }
+ else
+ {
+ /* Attempt a "trivial" room */
+ if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) &&
+ room_build(y, x, 9))
+ {
+ continue;
+ }
+ else if (room_build(y, x, 1)) continue;
+ }
+
+ /* Never mind */
+ continue;
+ }
+
+ /* Attempt an "unusual" room -- no vaults on town levels */
+ if (!town_level &&
+ (ironman_rooms || (rand_int(DUN_UNUSUAL) < dun_level)))
+ {
+ /* Roll for room type */
+ k = (ironman_rooms ? 0 : rand_int(100));
+
+ /* Attempt a very unusual room */ /* test hack */
+ if (ironman_rooms || (rand_int(DUN_UNUSUAL) < dun_level))
+ {
+#ifdef FORCE_V_IDX
+ if (room_build(y, x, 8)) continue;
+#else
+/* Type 8 -- Greater vault (10%) */
+ if (k < 10)
+ {
+ if (max_vault_ok > 1)
+ {
+ if (room_build(y, x, 8)) continue;
+ }
+ else
+ {
+ if (cheat_room) msg_print("Refusing a greater vault.");
+ }
+ }
+
+ /* Type 7 -- Lesser vault (15%) */
+ if (k < 25)
+ {
+ if (max_vault_ok > 0)
+ {
+ if (room_build(y, x, 7)) continue;
+ }
+ else
+ {
+ if (cheat_room) msg_print("Refusing a lesser vault.");
+ }
+ }
+
+
+ /* Type 5 -- Monster nest (15%) */
+ if ((k < 40) && room_build(y, x, 5)) continue;
+
+ /* Type 6 -- Monster pit (15%) */
+ if ((k < 55) && room_build(y, x, 6)) continue;
+
+ /* Type 11 -- Random vault (5%) */
+ if ((k < 60) && room_build(y, x, 11)) continue;
+#endif
+ }
+
+ /* Type 4 -- Large room (25%) */
+ if ((k < 25) && room_build(y, x, 4)) continue;
+
+ /* Type 3 -- Cross room (20%) */
+ if ((k < 45) && room_build(y, x, 3)) continue;
+
+ /* Type 2 -- Overlapping (20%) */
+ if ((k < 65) && room_build(y, x, 2)) continue;
+
+ /* Type 10 -- Fractal cave (15%) */
+ if ((k < 80) && room_build(y, x, 10)) continue;
+
+ /* Type 9 -- Circular (10%) */
+ /* Hack - build standard rectangular rooms if needed */
+ if (k < 90)
+ {
+ if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && room_build(y, x, 1)) continue;
+ else if (room_build(y, x, 9)) continue;
+ }
+
+ /* Type 12 -- Crypt (10%) */
+ if ((k < 100) && room_build(y, x, 12)) continue;
+ }
+
+ /* Attempt a trivial room */
+ if (dungeon_flags1 & DF1_CAVE)
+ {
+ if (room_build(y, x, 10)) continue;
+ }
+ else
+ {
+ if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && room_build(y, x, 9)) continue;
+ else if (room_build(y, x, 1)) continue;
+ }
+ }
+
+ /* If no rooms are allocated... */
+ while (dun->cent_n == 0)
+ {
+ /* ...force the creation of a small rectangular room */
+ (void)room_build(0, 0, 1);
+ }
+
+ /* Hack -- Scramble the room order */
+ for (i = 0; i < dun->cent_n; i++)
+ {
+ int pick1 = rand_int(dun->cent_n);
+ int pick2 = rand_int(dun->cent_n);
+ y1 = dun->cent[pick1].y;
+ x1 = dun->cent[pick1].x;
+ dun->cent[pick1].y = dun->cent[pick2].y;
+ dun->cent[pick1].x = dun->cent[pick2].x;
+ dun->cent[pick2].y = y1;
+ dun->cent[pick2].x = x1;
+ }
+
+ /* Start with no tunnel doors */
+ dun->door_n = 0;
+
+ /* Hack -- connect the first room to the last room */
+ y = dun->cent[dun->cent_n - 1].y;
+ x = dun->cent[dun->cent_n - 1].x;
+
+ /* Connect all the rooms together */
+ for (i = 0; i < dun->cent_n; i++)
+ {
+ /* Connect the room to the previous room */
+ build_tunnel(dun->cent[i].y, dun->cent[i].x, y, x, FALSE);
+
+ /* Remember the "previous" room */
+ y = dun->cent[i].y;
+ x = dun->cent[i].x;
+ }
+
+ /* Mega-Hack -- Convert FEAT_WALL_SOLID back into outer walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ if (cave[y][x].feat == FEAT_WALL_SOLID)
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Place intersection doors */
+ for (i = 0; i < dun->door_n; i++)
+ {
+ /* Extract junction location */
+ y = dun->door[i].y;
+ x = dun->door[i].x;
+
+ /* Try placing doors */
+ try_doors(y, x);
+ }
+
+ if (strcmp(game_module, "ToME") == 0)
+ {
+ /* Hack -- Add some magma streamers */
+ if ((dungeon_type == DUNGEON_MORDOR) || (dungeon_type == DUNGEON_ANGBAND))
+ for (i = 0; i < DUN_STR_MAG; i++)
+ {
+ build_streamer(FEAT_MAGMA, DUN_STR_MC);
+ }
+
+ /* Hack -- Add some quartz streamers */
+ if ((dungeon_type == DUNGEON_MORDOR) || (dungeon_type == DUNGEON_ANGBAND))
+ for (i = 0; i < DUN_STR_QUA; i++)
+ {
+ build_streamer(FEAT_QUARTZ, DUN_STR_QC);
+ }
+ }
+
+ /* Add some sand streamers */
+ if ((dungeon_flags1 & DF1_SAND_VEIN) && !rand_int(4))
+ {
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("Sand vein.");
+ build_streamer(FEAT_SANDWALL, DUN_STR_SC);
+ }
+
+ /* Destroy the level if necessary */
+ if (destroyed) destroy_level();
+
+ /* Create the town if needed */
+ if (town_level)
+ {
+ town_gen(town_level);
+ }
+
+ /* Hack -- Add some rivers if requested */
+ if ((dungeon_flags1 & DF1_WATER_RIVER) && !rand_int(4))
+ {
+ if (cheat_room || p_ptr->precognition) msg_print("River of water.");
+ add_river(FEAT_DEEP_WATER, FEAT_SHAL_WATER);
+ }
+ if ((dungeon_flags1 & DF1_LAVA_RIVER) && !rand_int(4))
+ {
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("River of lava.");
+ add_river(FEAT_DEEP_LAVA, FEAT_SHAL_LAVA);
+ }
+
+ if (dungeon_flags1 & DF1_WATER_RIVERS)
+ {
+ int max = 3 + rand_int(2);
+ bool said = FALSE;
+
+ for (i = 0; i < max; i++)
+ {
+ if (rand_int(3) == 0)
+ {
+ add_river(FEAT_DEEP_WATER, FEAT_SHAL_WATER);
+ if (!said && ((cheat_room) || (p_ptr->precognition))) msg_print("Rivers of water.");
+ said = TRUE;
+ }
+ }
+ }
+
+ if (dungeon_flags1 & DF1_LAVA_RIVERS)
+ {
+ int max = 2 + rand_int(2);
+ bool said = FALSE;
+
+ for (i = 0; i < max; i++)
+ {
+ if (rand_int(3) == 0)
+ {
+ add_river(FEAT_DEEP_LAVA, FEAT_SHAL_LAVA);
+ if (!said && ((cheat_room) || (p_ptr->precognition))) msg_print("Rivers of lava.");
+ said = TRUE;
+ }
+ }
+ }
+
+ /* Add streamers of trees, water, or lava -KMW- */
+ if (!(dungeon_flags1 & DF1_NO_STREAMERS))
+ {
+ int num;
+
+ /*
+ * Flat levels (was: levels 1--2)
+ *
+ * Small trees (penetrate walls)
+ */
+ if ((dungeon_flags1 & DF1_FLAT) && (randint(20) > 15))
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SMALL_TREES, 1);
+ }
+ }
+
+ /*
+ * Levels 1 -- 33 (was: 1 -- 19)
+ *
+ * Shallow water (preserve walls)
+ * Deep water (penetrate walls)
+ */
+ if (!(dun_level <= 33) && (randint(20) > 15))
+ {
+ num = randint(DUN_STR_QUA - 1);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SHAL_WATER, 0);
+ }
+
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_DEEP_WATER, 1);
+ }
+ }
+ }
+
+ /*
+ * Levels 34 -- (was: 20 --)
+ */
+ else if (dun_level > 33)
+ {
+ /*
+ * Shallow lava (preserve walls)
+ * Deep lava (penetrate walls)
+ */
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SHAL_LAVA, 0);
+ }
+
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA - 1);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_DEEP_LAVA, 1);
+ }
+ }
+ }
+
+ /*
+ * Shallow water (preserve walls)
+ * Deep water (penetrate walls)
+ */
+ else if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA - 1);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_SHAL_WATER, 0);
+ }
+
+ if (randint(20) > 15)
+ {
+ num = randint(DUN_STR_QUA);
+
+ for (i = 0; i < num; i++)
+ {
+ build_streamer2(FEAT_DEEP_WATER, 1);
+ }
+ }
+ }
+ }
+ }
+
+ /* Hack, seems like once a room overrode the level boundaries, this is BAD */
+ set_bounders(empty_level);
+
+ /* Determine the character location */
+ if (!new_player_spot(branch))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Bring the imprinted pets from the old level
+ */
+void replace_all_friends()
+{
+ int i;
+
+ if (p_ptr->wild_mode) return;
+
+ /* Scan every saved pet */
+ for (i = 0; i < max_m_idx; i++)
+ {
+ if ((km_list[i].r_idx) && (km_list[i].status == MSTATUS_COMPANION))
+ {
+ int y = p_ptr->py, x = p_ptr->px;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+
+ /* Find a suitable location */
+ get_pos_player(5, &y, &x);
+ c_ptr = &cave[y][x];
+
+ /* Get a m_idx to use */
+ c_ptr->m_idx = m_pop();
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Actualy place the monster */
+ m_list[c_ptr->m_idx] = km_list[i];
+ m_ptr->fy = y;
+ m_ptr->fx = x;
+ m_ptr->hold_o_idx = 0;
+ }
+ }
+}
+
+/*
+ * Save the imprinted pets from the old level
+ */
+void save_all_friends()
+{
+ if (p_ptr->old_wild_mode) return;
+
+ C_COPY(km_list, m_list, max_m_idx, monster_type);
+}
+
+
+
+/*
+ * Return the dungeon type of the current level(it can only return the
+ * principal dungeons)
+ */
+byte calc_dungeon_type()
+{
+ int i;
+
+ for (i = 0; i < max_d_idx; i++)
+ {
+ if ((dun_level >= d_info[i].mindepth) &&
+ (dun_level <= d_info[i].maxdepth) &&
+ (d_info[i].flags1 & DF1_PRINCIPAL))
+ return (i);
+ }
+ return (0);
+}
+
+
+/*
+ * Build probability tables for walls and floors and set feat_wall_outer
+ * and feat_wall_inner according to the current information in d_info.txt
+ *
+ * *hint* *hint* with this made extern, and we no longer have to
+ * store fill_type and floor_type in the savefile...
+ */
+static void init_feat_info(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ int i;
+ int cur_depth, max_depth;
+ int p1, p2;
+ int floor_lim1, floor_lim2, floor_lim3;
+ int fill_lim1, fill_lim2, fill_lim3;
+
+
+ /* Retrieve dungeon depth info (base 1, to avoid zero divide errors) */
+ cur_depth = (dun_level - d_ptr->mindepth) + 1;
+ max_depth = (d_ptr->maxdepth - d_ptr->mindepth) + 1;
+
+
+ /* Set room wall types */
+ feat_wall_outer = d_ptr->outer_wall;
+ feat_wall_inner = d_ptr->inner_wall;
+
+
+ /* Setup probability info -- Floors */
+ p1 = d_ptr->floor_percent1[0];
+ p2 = d_ptr->floor_percent1[1];
+ floor_lim1 = p1 + (p2 - p1) * cur_depth / max_depth;
+
+ p1 = d_ptr->floor_percent2[0];
+ p2 = d_ptr->floor_percent2[1];
+ floor_lim2 = floor_lim1 + p1 + (p2 - p1) * cur_depth / max_depth;
+
+ floor_lim3 = 100;
+
+ /* Setup probability info -- Fillers */
+ p1 = d_ptr->fill_percent1[0];
+ p2 = d_ptr->fill_percent1[1];
+ fill_lim1 = p1 + (p2 - p1) * cur_depth / max_depth;
+
+ p1 = d_ptr->fill_percent2[0];
+ p2 = d_ptr->fill_percent2[1];
+ fill_lim2 = fill_lim1 + p1 + (p2 - p1) * cur_depth / max_depth;
+
+ fill_lim3 = 100;
+
+
+ /* Fill the arrays of floors and walls in the good proportions */
+ for (i = 0; i < 100; i++)
+ {
+ if (i < floor_lim1)
+ {
+ floor_type[i] = d_ptr->floor1;
+ }
+ else if (i < floor_lim2)
+ {
+ floor_type[i] = d_ptr->floor2;
+ }
+ else
+ {
+ floor_type[i] = d_ptr->floor3;
+ }
+
+ if (i < fill_lim1)
+ {
+ fill_type[i] = d_ptr->fill_type1;
+ }
+ else if (i < fill_lim2)
+ {
+ fill_type[i] = d_ptr->fill_type2;
+ }
+ else
+ {
+ fill_type[i] = d_ptr->fill_type3;
+ }
+ }
+}
+
+
+/*
+ * Fill a level with wall type specified in A: or L: line of d_info.txt
+ *
+ * 'use_floor', when it is TRUE, tells the function to use floor type
+ * terrains (L:) instead of walls (A:).
+ *
+ * Filling behaviour can be controlled by the second parameter 'smooth',
+ * with the following options available:
+ *
+ * smooth behaviour
+ * ------ ------------------------------------------------------------
+ * 0 Fill the entire level with fill_type1 / floor1
+ * 1 All the grids are randomly selected (== --P5.1.2)
+ * 2 Slightly smoothed -- look like scattered patches
+ * 3 More smoothed -- tend to look like caverns / small scale map
+ * 4-- Max smoothing -- tend to look like landscape/island/
+ * continent etc.
+ *
+ * I put it here, because there's another filler generator in
+ * wild.c, but it works better there, in fact...
+ *
+ * CAVEAT: smoothness of 3 or greater doesn't work well with the
+ * current secret door implementation. Outer walls also need some
+ * rethinking.
+ *
+ * -- pelpel
+ */
+
+/*
+ * Thou shalt not invoke the name of thy RNG in vain.
+ * The Angband RNG generates 28 bit pseudo-random number, hence
+ * 28 / 2 = 14
+ */
+#define MAX_SHIFTS 14
+
+static void fill_level(bool use_floor, byte smooth)
+{
+ int y, x;
+ int step;
+ int shift;
+
+
+ /* Convert smoothness to initial step */
+ if (smooth == 0) step = 0;
+ else if (smooth == 1) step = 1;
+ else if (smooth == 2) step = 2;
+ else if (smooth == 3) step = 4;
+ else step = 8;
+
+ /*
+ * Paranoia -- step must be less than or equal to a half of
+ * width or height, whichever shorter
+ */
+ if ((cur_hgt < 16) && (step > 4)) step = 4;
+ if ((cur_wid < 16) && (step > 4)) step = 4;
+
+
+ /* Special case -- simple fill */
+ if (step == 0)
+ {
+ byte filler;
+
+ /* Pick a filler XXX XXX XXX */
+ if (use_floor) filler = d_info[dungeon_type].floor1;
+ else filler = d_info[dungeon_type].fill_type1;
+
+ /* Fill the level with the filler without calling RNG */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, filler);
+ }
+ }
+
+ /* Done */
+ return;
+ }
+
+
+ /*
+ * Fill starting positions -- every 'step' grids horizontally and
+ * vertically
+ */
+ for (y = 0; y < cur_hgt; y += step)
+ {
+ for (x = 0; x < cur_wid; x += step)
+ {
+ /*
+ * Place randomly selected terrain feature using the prebuilt
+ * probability table
+ *
+ * By slightly modifying this, you can build streamers as
+ * well as normal fillers all at once, but this calls for
+ * modifications to the other part of the dungeon generator.
+ */
+ if (use_floor) place_floor(y, x);
+ else place_filler(y, x);
+ }
+ }
+
+
+ /*
+ * Fill spaces between, randomly picking one of their neighbours
+ *
+ * This simple yet powerful algorithm was described by Mike Anderson:
+ *
+ * A B A | B A a B
+ * -> --+-- -> d e b
+ * D C D | C D c C
+ *
+ * a can be either A or B, b B or C, c C or D and d D or A.
+ * e is chosen from A, B, C and D.
+ * Subdivide and repeat the process as many times as you like.
+ *
+ * All the nasty tricks that obscure this simplicity are mine (^ ^;)
+ */
+
+ /* Initialise bit shift counter */
+ shift = MAX_SHIFTS;
+
+ /* Repeat subdivision until all the grids are filled in */
+ while ((step = step >> 1) > 0)
+ {
+ bool y_even, x_even;
+ s16b y_wrap, x_wrap;
+ s16b y_sel, x_sel;
+ u32b selector = 0;
+
+ /* Hacklette -- Calculate wrap-around locations */
+ y_wrap = ((cur_hgt - 1) / (step * 2)) * (step * 2);
+ x_wrap = ((cur_wid - 1) / (step * 2)) * (step * 2);
+
+ /* Initialise vertical phase */
+ y_even = 0;
+
+ for (y = 0; y < cur_hgt; y += step)
+ {
+ /* Flip vertical phase */
+ y_even = !y_even;
+
+ /* Initialise horizontal phase */
+ x_even = 0;
+
+ for (x = 0; x < cur_wid; x += step)
+ {
+ /* Flip horizontal phase */
+ x_even = !x_even;
+
+ /* Already filled in by previous iterations */
+ if (y_even && x_even) continue;
+
+ /*
+ * Retrieve next two bits from pseudo-random bit sequence
+ *
+ * You can do well not caring so much about their randomness.
+ *
+ * This is not really necessary, but I don't like to invoke
+ * relatively expensive RNG when we can do with much smaller
+ * number of calls.
+ */
+ if (shift >= MAX_SHIFTS)
+ {
+ selector = rand_int(0x10000000L);
+ shift = 0;
+ }
+ else
+ {
+ selector >>= 2;
+ shift++;
+ }
+
+ /* Vertically in sync */
+ if (y_even) y_sel = y;
+
+ /* Bit 1 selects neighbouring y */
+ else y_sel = (selector & 2) ? y + step : y - step;
+
+ /* Horizontally in sync */
+ if (x_even) x_sel = x;
+
+ /* Bit 0 selects neighbouring x */
+ else x_sel = (selector & 1) ? x + step : x - step;
+
+ /* Hacklette -- Fix out of range indices by wrapping around */
+ if (y_sel >= cur_hgt) y_sel = 0;
+ else if (y_sel < 0) y_sel = y_wrap;
+ if (x_sel >= cur_wid) x_sel = 0;
+ else if (x_sel < 0) x_sel = x_wrap;
+
+ /*
+ * Fill the grid with terrain feature of the randomly
+ * picked up neighbour
+ */
+ cave_set_feat(y, x, cave[y_sel][x_sel].feat);
+ }
+ }
+ }
+}
+
+
+/*
+ * Generate a new dungeon level
+ *
+ * Note that "dun_body" adds about 4000 bytes of memory to the stack.
+ */
+static bool cave_gen(void)
+{
+ int i, k, y, x, y1, x1, branch;
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ int max_vault_ok = 2;
+
+ bool destroyed = FALSE;
+ bool empty_level = FALSE;
+ s16b town_level = 0;
+
+ level_generator_type *generator;
+
+ dun_data dun_body;
+
+ char generator_name[100];
+
+ if (!get_dungeon_generator(generator_name))
+ strnfmt(generator_name, 99, "%s", d_ptr->generator);
+
+ /*
+ * We generate a double dungeon. First we should halve the desired
+ * width/height, generate the dungeon normally, then double it
+ * in both directions
+ */
+ if (dungeon_flags1 & DF1_DOUBLE)
+ {
+ cur_wid /= 2;
+ cur_hgt /= 2;
+ }
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+
+ /* Fill the arrays of floors and walls in the good proportions */
+ init_feat_info();
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Global data */
+ dun = &dun_body;
+
+ if (!(max_panel_rows)) max_vault_ok--;
+ if (!(max_panel_cols)) max_vault_ok--;
+
+ /*
+ * Hack -- Start with fill_type's
+ *
+ * Need a way to know appropriate smoothing factor for the current
+ * dungeon. Maybe we need another d_info flag/value.
+ */
+ fill_level(empty_level, d_ptr->fill_method);
+
+ set_bounders(empty_level);
+
+ /*
+ * Call the good level generator
+ */
+ generator = level_generators;
+ while (generator)
+ {
+ if (!strcmp(generator->name, generator_name))
+ {
+ if (!generator->generator(generator->name))
+ return FALSE;
+ break;
+ }
+
+ generator = generator->next;
+ }
+
+ /* Only if requested */
+ if (generator->default_stairs)
+ {
+ /* Is there a dungeon branch ? */
+ if ((branch = get_branch()))
+ {
+ /* Place 5 down stair some walls */
+ alloc_stairs(FEAT_MORE, 5, 3, branch);
+ }
+
+ /* Is there a father dungeon branch ? */
+ if ((branch = get_fbranch()))
+ {
+ /* Place 1 down stair some walls */
+ alloc_stairs(FEAT_LESS, 5, 3, branch);
+ }
+
+ if ((dun_level < d_ptr->maxdepth) || ((dun_level == d_ptr->maxdepth) && (dungeon_flags1 & DF1_FORCE_DOWN)))
+ {
+ /* Place 3 or 4 down stairs near some walls */
+ alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE, rand_range(3, 4), 3, 0);
+
+ /* Place 0 or 1 down shafts near some walls */
+ if (!(dungeon_flags2 & DF2_NO_SHAFT)) alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN, rand_range(0, 1), 3, 0);
+ }
+
+ if ((dun_level > d_ptr->mindepth) || ((dun_level == d_ptr->mindepth) && (!(dungeon_flags1 & DF1_NO_UP))))
+ {
+ /* Place 1 or 2 up stairs near some walls */
+ alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS, rand_range(1, 2), 3, 0);
+
+ /* Place 0 or 1 up shafts near some walls */
+ if (!(dungeon_flags2 & DF2_NO_SHAFT)) alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP, rand_range(0, 1), 3, 0);
+ }
+ }
+
+ process_hooks(HOOK_GEN_LEVEL, "(d)", is_quest(dun_level));
+
+ /* Monsters and objects change even in persistent dungeons. */
+ if (seed_dungeon) Rand_quick = FALSE;
+
+ /* Basic "amount" */
+ k = (dun_level / 3);
+ if (k > 10) k = 10;
+ if (k < 2) k = 2;
+
+ /* Only if requested */
+ if (generator->default_monsters)
+ {
+
+ /*
+ * Pick a base number of monsters
+ */
+ i = d_ptr->min_m_alloc_level;
+
+ /* To make small levels a bit more playable */
+ if ((cur_hgt < MAX_HGT) || (cur_wid < MAX_WID))
+ {
+ int small_tester = i;
+
+ i = (i * cur_hgt) / MAX_HGT;
+ i = (i * cur_wid) / MAX_WID;
+ i += 1;
+
+ if (i > small_tester) i = small_tester;
+ else if (cheat_hear)
+ {
+ msg_format("Reduced monsters base from %d to %d", small_tester, i);
+ }
+ }
+
+ i += randint(8);
+
+ /* Put some monsters in the dungeon */
+ for (i = i + k; i > 0; i--)
+ {
+ (void)alloc_monster(0, TRUE);
+ }
+ }
+
+ /* Check fates */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ /* Ignore empty slots */
+ if (fates[i].fate == FATE_NONE) continue;
+
+ /* Check dungeon depth */
+ if (fates[i].level != dun_level) continue;
+
+ /* Non-serious fates don't always fire */
+ if ((!fates[i].serious) && (randint(2) != 1)) continue;
+
+ /* Player meets his/her fate now... */
+ fate_flag = TRUE;
+
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_O:
+ {
+ int oy = p_ptr->py + 1;
+ int ox = p_ptr->px;
+ object_type *q_ptr, forge;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Mega-Hack */
+ object_prep(q_ptr, fates[i].o_idx);
+
+ /* Mega-Hack */
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, fates[i].serious);
+
+ get_pos_player(10, &oy, &ox);
+
+ /* Drop it from the heaven */
+ drop_near(q_ptr, -1, oy, ox);
+
+ /* Make it icky */
+ fates[i].icky = TRUE;
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ int oy = p_ptr->py + 1;
+ int ox = p_ptr->px;
+
+ get_pos_player(10, &oy, &ox);
+
+ place_monster_one(oy, ox, fates[i].r_idx, 0, fates[i].serious, MSTATUS_ENEMY);
+
+ fates[i].icky = TRUE;
+ break;
+ }
+ case FATE_FIND_A:
+ {
+ int oy = p_ptr->py + 1;
+ int ox = p_ptr->px;
+ object_type *q_ptr = NULL, forge;
+
+ get_pos_player(10, &oy, &ox);
+
+ /* XXX XXX XXX Grant a randart */
+ if (fates[i].a_idx == 0)
+ {
+ int obj_lev;
+ s16b k_idx;
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_artifactable;
+
+ /* Object level a la find object fates */
+ obj_lev = max_dlv[dungeon_type] + randint(10);
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ /* Roll for an object */
+ k_idx = get_obj_num(obj_lev);
+
+ /* Reset restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Invalidate the allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ /* Get a local object */
+ q_ptr = &forge;
+
+ /* Wipe it */
+ object_wipe(q_ptr);
+
+ /* Create the object */
+ object_prep(q_ptr, k_idx);
+
+ /* SoAC it */
+ create_artifact(q_ptr, FALSE, TRUE);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, oy, ox);
+ }
+
+ /* Grant a normal artefact */
+ else if (a_info[fates[i].a_idx].cur_num == 0)
+ {
+ artifact_type *a_ptr = &a_info[fates[i].a_idx];
+ s16b I_kind;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = fates[i].a_idx;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, oy, ox);
+ }
+
+ fates[i].icky = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Re scan the list to eliminate the inutile fate */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_A:
+ {
+ if (a_info[fates[i].a_idx].cur_num == 1) fates[i].icky = TRUE;
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ if ((r_info[fates[i].r_idx].cur_num == 1) && (r_info[fates[i].r_idx].flags1 & RF1_UNIQUE)) fates[i].icky = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Only if requested */
+ if (generator->default_miscs)
+ {
+ /* Place some traps in the dungeon */
+ alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_TRAP, randint(k * 2));
+
+ /* Put some rubble in corridors */
+ alloc_object(ALLOC_SET_CORR, ALLOC_TYP_RUBBLE, randint(k));
+ }
+
+ /* Only if requested */
+ if (generator->default_objects)
+ {
+ /* Put some objects in rooms */
+ if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ROOM, 3));
+
+ /* Put some objects/gold in the dungeon */
+ if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ITEM, 3));
+ if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_GOLD, randnor(DUN_AMT_GOLD, 3));
+ }
+
+ /* Only if requested */
+ if (generator->default_miscs)
+ {
+ /* Put some altars */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_ALTAR, randnor(DUN_AMT_ALTAR, 3));
+
+ /* Put some between gates */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_BETWEEN, randnor(DUN_AMT_BETWEEN, 3));
+
+ /* Put some fountains */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_FOUNTAIN, randnor(DUN_AMT_FOUNTAIN, 3));
+ }
+
+ /* Put an Artifact and Artifact Guardian is requested */
+ if (d_ptr->final_guardian && (d_ptr->maxdepth == dun_level))
+ {
+ int oy;
+ int ox;
+ int m_idx, try = 10000;
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ oy = randint(cur_hgt - 4) + 2;
+ ox = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(oy, ox)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ /* Place the guardian */
+ m_allow_special[d_ptr->final_guardian] = TRUE;
+ place_monster_one(oy, ox, d_ptr->final_guardian, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[d_ptr->final_guardian] = FALSE;
+
+
+ m_idx = cave[oy][ox].m_idx;
+
+ if (!m_idx && wizard) cmsg_print(TERM_L_RED, "WARNING: Could not place guardian.");
+
+ /*
+ * If guardian is successfully created and his/her/its
+ * treasure hasn't been found, let him/her/it own that
+ */
+ if (m_idx && d_ptr->final_artifact &&
+ (a_info[d_ptr->final_artifact].cur_num == 0))
+ {
+ artifact_type *a_ptr = &a_info[d_ptr->final_artifact];
+ object_type *q_ptr, forge, *o_ptr;
+ int I_kind, o_idx;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ /* Proceed only if there's an object slot available */
+ if (o_idx)
+ {
+ a_allow_special[d_ptr->final_artifact] = TRUE;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = d_ptr->final_artifact;
+
+ /* Actually create it */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Where it is found ? */
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = d_ptr->final_guardian;
+ q_ptr->found_aux2 = 0;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ a_allow_special[d_ptr->final_artifact] = FALSE;
+
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ }
+
+ if (m_idx && d_ptr->final_object &&
+ (k_info[d_ptr->final_object].artifact == FALSE))
+ {
+ object_type *q_ptr, forge, *o_ptr;
+ int o_idx;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ /* Proceed only if there's an object slot available */
+ if (o_idx)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ k_allow_special[d_ptr->final_object] = TRUE;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Create the final object */
+ object_prep(q_ptr, d_ptr->final_object);
+ apply_magic(q_ptr, 1, FALSE, FALSE, FALSE);
+
+ /* Where it is found ? */
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = d_ptr->final_guardian;
+ q_ptr->found_aux2 = 0;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ k_allow_special[d_ptr->final_object] = FALSE;
+
+ k_info[d_ptr->final_object].artifact = TRUE;
+
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ }
+ }
+
+ if ((empty_level) && (randint(DARK_EMPTY) != 1 || (randint(100) > dun_level)))
+ wiz_lite();
+
+ /* Ghosts love to inhabit destroyed levels, but will live elsewhere */
+ i = (destroyed) ? 11 : 1;
+#if 0 /* DGDGDG -- implement ghost in a good & ncie way */
+ /* Try to place the ghost */
+ while (i-- > 0)
+ {
+
+ /* Attempt to place a ghost */
+ if (place_ghost())
+ {
+ /* Hack -- increase the rating */
+ rating += 10;
+
+ /* A ghost makes the level special */
+ good_item_flag = TRUE;
+
+ /* Make cheaters and precog aware of the ghost */
+ if (cheat_hear || p_ptr->precognition) msg_print("Player Ghost.");
+
+ /* Stop trying to place the ghost */
+ break;
+ }
+ }
+#endif
+ /* Now double the generated dungeon */
+ if (dungeon_flags1 & DF1_DOUBLE)
+ {
+ /* We begin at the bottom-right corner and from there move
+ * up/left (this way we don't need another array for the
+ * dungeon data) */
+ /* Note: we double the border permanent walls, too. It is
+ * easier this way and I think it isn't too ugly */
+ for (y = cur_hgt - 1, y1 = y * 2; y >= 0; y--, y1 -= 2)
+ for (x = cur_wid - 1, x1 = x * 2; x >= 0; x--, x1 -= 2)
+ {
+ int disp[4][2] = {{0, 0}, {0, + 1}, { + 1, 0}, { + 1, + 1}};
+
+ cave_type *c_ptr[4], *cc_ptr = &cave[y][x];
+ object_type *o_ptr = &o_list[cc_ptr->o_idx];
+ monster_type *m_ptr = &m_list[cc_ptr->m_idx];
+
+ /*
+ * Now we copy the generated data to the
+ * appropriate grids
+ */
+ for (i = 0; i < 4; i++)
+ {
+ c_ptr[i] = &cave[y1 + disp[i][0]][x1 + disp[i][1]];
+ *c_ptr[i] = *cc_ptr;
+ c_ptr[i]->o_idx = 0;
+ c_ptr[i]->m_idx = 0;
+
+ if (cc_ptr->feat == FEAT_BETWEEN)
+ {
+ int xxx = cc_ptr->special & 0xFF;
+ int yyy = cc_ptr->special >> 8;
+
+ xxx *= 2;
+ yyy *= 2;
+ xxx += disp[i][1];
+ yyy += disp[i][0];
+ c_ptr[i]->special = xxx + (yyy << 8);
+ }
+ }
+
+ /* Objects should be put only in 1 of the
+ * new grids (otherwise we would segfault
+ * a lot) ... */
+ if (cc_ptr->o_idx != 0)
+ {
+ i = rand_int(4);
+ c_ptr[i]->o_idx = cc_ptr->o_idx;
+ o_ptr->iy = y1 + disp[i][0];
+ o_ptr->ix = x1 + disp[i][1];
+ }
+
+ /* ..just like monsters */
+ if (cc_ptr->m_idx != 0)
+ {
+ i = rand_int(4);
+ c_ptr[i]->m_idx = cc_ptr->m_idx;
+ m_ptr->fy = y1 + disp[i][0];
+ m_ptr->fx = x1 + disp[i][1];
+ }
+ }
+
+ /* Set the width/height ... */
+ cur_wid *= 2;
+ cur_hgt *= 2;
+
+ /* ... and player position to the right place */
+ p_ptr->py *= 2;
+ p_ptr->px *= 2;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Builds the arena after it is entered -KMW-
+ */
+static void build_arena(void)
+{
+ int yval, y_height, y_depth, xval, x_left, x_right;
+ register int i, j;
+
+ yval = SCREEN_HGT / 2;
+ xval = SCREEN_WID / 2;
+ y_height = yval - 10 + SCREEN_HGT;
+ y_depth = yval + 10 + SCREEN_HGT;
+ x_left = xval - 32 + SCREEN_WID;
+ x_right = xval + 32 + SCREEN_WID;
+
+ for (i = y_height; i <= y_height + 5; i++)
+ {
+ for (j = x_left; j <= x_right; j++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ for (i = y_depth; i >= y_depth - 5; i--)
+ {
+ for (j = x_left; j <= x_right; j++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ for (j = x_left; j <= x_left + 17; j++)
+ {
+ for (i = y_height; i <= y_depth; i++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ for (j = x_right; j >= x_right - 17; j--)
+ {
+ for (i = y_height; i <= y_depth; i++)
+ {
+ cave_set_feat(i, j, FEAT_PERM_EXTRA);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+
+ cave_set_feat(y_height + 6, x_left + 18, FEAT_PERM_EXTRA);
+ cave[y_height + 6][x_left + 18].info |= (CAVE_GLOW | CAVE_MARK);
+ cave_set_feat(y_depth - 6, x_left + 18, FEAT_PERM_EXTRA);
+ cave[y_depth - 6][x_left + 18].info |= (CAVE_GLOW | CAVE_MARK);
+ cave_set_feat(y_height + 6, x_right - 18, FEAT_PERM_EXTRA);
+ cave[y_height + 6][x_right - 18].info |= (CAVE_GLOW | CAVE_MARK);
+ cave_set_feat(y_depth - 6, x_right - 18, FEAT_PERM_EXTRA);
+ cave[y_depth - 6][x_right - 18].info |= (CAVE_GLOW | CAVE_MARK);
+
+ i = y_height + 5;
+ j = xval + SCREEN_WID;
+ cave_set_feat(i, j, FEAT_SHOP);
+ cave[i][j].info |= (CAVE_GLOW | CAVE_MARK);
+ player_place(i + 1, j);
+}
+
+
+/*
+ * Town logic flow for generation of arena -KMW-
+ */
+static void arena_gen(void)
+{
+ int y, x;
+ int qy = SCREEN_HGT;
+ int qx = SCREEN_WID;
+ bool daytime;
+
+ /* Day time */
+ if ((turn % (10L * DAY)) < ((10L * DAY) / 2))
+ daytime = TRUE;
+
+ /* Night time */
+ else
+ daytime = FALSE;
+
+ /* Start with solid walls */
+ for (y = 0; y < MAX_HGT; y++)
+ {
+ for (x = 0; x < MAX_WID; x++)
+ {
+ /* Create "solid" perma-wall */
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+
+ /* Illuminate and memorize the walls */
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+
+ /* Then place some floors */
+ for (y = qy + 1; y < qy + SCREEN_HGT - 1; y++)
+ {
+ for (x = qx + 1; x < qx + SCREEN_WID - 1; x++)
+ {
+ /* Create empty floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Darken and forget the floors */
+ cave[y][x].info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Day time */
+ if (daytime)
+ {
+ /* Perma-Lite */
+ cave[y][x].info |= (CAVE_GLOW);
+
+ /* Memorize */
+ if (view_perma_grids) cave[y][x].info |= (CAVE_MARK);
+ }
+ }
+ }
+
+ build_arena();
+
+ place_monster_aux(p_ptr->py + 5, p_ptr->px, arena_monsters[p_ptr->arena_number],
+ FALSE, FALSE, MSTATUS_ENEMY);
+}
+
+
+/*
+ * Generate a quest level
+ */
+static void quest_gen(void)
+{
+ process_hooks(HOOK_GEN_QUEST, "(d)", is_quest(dun_level));
+}
+
+/*
+ * Creates a special level
+ */
+
+/* Mega-Hack */
+#define REGEN_HACK 0x02
+
+bool build_special_level(void)
+{
+ char buf[80];
+ int y, x, ystart = 2, xstart = 2;
+ s16b level;
+
+ /* No special levels on the surface */
+ if (!dun_level) return FALSE;
+
+ level = dun_level - d_info[dungeon_type].mindepth;
+ if ((!get_dungeon_save(buf)) && (special_lvl[level][dungeon_type])) return FALSE;
+ if (!get_dungeon_special(buf)) return FALSE;
+
+ /* Big town */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON | INIT_POSITION;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, buf, &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ special_lvl[level][dungeon_type] = REGEN_HACK;
+ generate_special_feeling = TRUE;
+
+ /* Special feeling because it's special */
+ good_item_flag = TRUE;
+
+ /*
+ * Hack -- It's better/more dangerous than a greater vault.
+ * Better to have a rating field in special level description.
+ */
+ rating += 40;
+
+ return TRUE;
+}
+
+/*
+ * Prepare regeneration of a special level, which should not happen,
+ * but just in case...
+ */
+static void wipe_special_level(void)
+{
+ s16b level;
+ char buf[80];
+
+ /* No special levels on the surface */
+ if (!dun_level) return;
+
+ process_hooks(HOOK_LEVEL_REGEN, "()");
+
+ /* Calculate relative depth */
+ level = dun_level - d_info[dungeon_type].mindepth;
+
+ /* No special level at this depth */
+ if ((!get_dungeon_save(buf)) &&
+ special_lvl[level][dungeon_type]) return;
+ if (!get_dungeon_special(buf)) return;
+
+ /* Clear the Mega-Hack flag */
+ if (special_lvl[level][dungeon_type] == REGEN_HACK)
+ special_lvl[level][dungeon_type] = FALSE;
+}
+
+/*
+ * Finalise generation of a special level
+ */
+static void finalise_special_level(void)
+{
+ s16b level;
+ char buf[80];
+
+ /* No special levels on the surface */
+ if (!dun_level) return;
+
+ process_hooks(HOOK_LEVEL_END_GEN, "()");
+
+ /* Calculate relative depth */
+ level = dun_level - d_info[dungeon_type].mindepth;
+
+ /* No special level at this depth */
+ if ((!get_dungeon_save(buf)) &&
+ special_lvl[level][dungeon_type]) return;
+ if (!get_dungeon_special(buf)) return;
+
+ /* Set the "generated" flag */
+ if (special_lvl[level][dungeon_type] == REGEN_HACK)
+ special_lvl[level][dungeon_type] = TRUE;
+}
+
+/*
+ * Give some magical energy to the each grid of the level
+ */
+void generate_grid_mana()
+{
+ int y, x, mana, mult;
+ bool xtra_magic = FALSE;
+
+ if (randint(XTRA_MAGIC) == 1)
+ {
+ xtra_magic = TRUE;
+
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print("Magical level");
+ }
+ }
+
+ mult = ((xtra_magic) ? 3 : 2);
+
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Calculate the amount of mana in each grid */
+ mana = mult * m_bonus(255, dun_level) / 2;
+ if (xtra_magic) mana += 10 + rand_int(10);
+
+ /* Never more than 255 or less than 0(parano‹a) */
+ if (mana < 0) mana = 0;
+ if (mana > 255) mana = 255;
+
+ c_ptr->mana = mana;
+ }
+ }
+}
+
+
+/*
+ * Generates a random dungeon level -RAK-
+ *
+ * Hack -- regenerate any "overflow" levels
+ *
+ * Hack -- allow auto-scumming via a gameplay option.
+ */
+void generate_cave(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ int tester_1, tester_2;
+ int y, x, num, i;
+ bool loaded = FALSE;
+ char buf[80];
+ s16b town_level = 0;
+ s32b old_seed_dungeon = seed_dungeon;
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+ generate_special_feeling = FALSE;
+
+ /* Initialize the flags with the basic dungeon flags */
+ if (!dun_level)
+ {
+ dungeon_flags1 = d_info[DUNGEON_WILDERNESS].flags1;
+ dungeon_flags2 = d_info[DUNGEON_WILDERNESS].flags2;
+ }
+ else
+ {
+ dungeon_flags1 = d_ptr->flags1;
+ dungeon_flags2 = d_ptr->flags2;
+ }
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ /* Save the imprinted monsters */
+ save_all_friends();
+ wipe_m_list();
+
+ /* Seed the RNG if appropriate */
+ if (seed_dungeon)
+ {
+ Rand_quick = TRUE;
+ Rand_value = seed_dungeon + dun_level;
+ }
+
+ if (town_level)
+ {
+ Rand_quick = TRUE;
+ seed_dungeon = town_info[town_level].seed;
+ Rand_value = seed_dungeon;
+ }
+
+ process_hooks(HOOK_GEN_LEVEL_BEGIN, "");
+
+ /* Try to load a saved level */
+ if (get_dungeon_save(buf))
+ {
+ /* No effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ effects[i].time = 0;
+ }
+
+ /* Start with a blank cave */
+ for (y = 0; y < MAX_HGT; y++)
+ {
+ for (x = 0; x < MAX_WID; x++)
+ {
+ /* No flags */
+ cave[y][x].info = 0;
+
+ /* No features */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+
+ /* No objects */
+ cave[y][x].o_idx = 0;
+
+ /* No monsters */
+ cave[y][x].m_idx = 0;
+
+ /* No traps */
+ cave[y][x].t_idx = 0;
+
+ /* No mimic */
+ cave[y][x].mimic = 0;
+
+ /* No effects */
+ cave[y][x].effect = 0;
+
+ /* No inscription */
+ cave[y][x].inscription = 0;
+
+#ifdef MONSTER_FLOW
+
+ /* No flow */
+ cave[y][x].cost = 0;
+ cave[y][x].when = 0;
+
+#endif /* MONSTER_FLOW */
+
+ }
+ }
+
+ loaded = load_dungeon(buf);
+ }
+
+ /* No saved level -- generate new one */
+ if (!loaded)
+ {
+ if (!get_dungeon_special(buf) ||
+ !special_lvl[dun_level - d_info[dungeon_type].mindepth][dungeon_type])
+ {
+ get_level_flags();
+ }
+
+ /* Generate */
+ for (num = 0; TRUE; num++)
+ {
+ bool okay = TRUE;
+
+ cptr why = NULL;
+
+ /* No effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ effects[i].time = 0;
+ }
+
+ /* Start with a blank cave */
+ for (y = 0; y < MAX_HGT; y++)
+ {
+ for (x = 0; x < MAX_WID; x++)
+ {
+ /* No flags */
+ cave[y][x].info = 0;
+
+ /* No features */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+
+ /* No objects */
+ cave[y][x].o_idx = 0;
+
+ /* No monsters */
+ cave[y][x].m_idx = 0;
+
+ /* No traps */
+ cave[y][x].t_idx = 0;
+
+ /* No mimic */
+ cave[y][x].mimic = 0;
+
+ /* No effect */
+ cave[y][x].effect = 0;
+
+ /* No inscription */
+ cave[y][x].inscription = 0;
+
+#ifdef MONSTER_FLOW
+
+ /* No flow */
+ cave[y][x].cost = 0;
+ cave[y][x].when = 0;
+
+#endif /* MONSTER_FLOW */
+ }
+ }
+
+
+ /* XXX XXX XXX XXX */
+ o_max = 1;
+
+
+ /* Mega-Hack -- no player yet */
+ p_ptr->px = p_ptr->py = 0;
+
+
+ /* Mega-Hack -- no panel yet */
+ panel_row_min = 0;
+ panel_row_max = 0;
+ panel_col_min = 0;
+ panel_col_max = 0;
+
+
+ /* Reset the monster generation level */
+ if (dungeon_type != DUNGEON_DEATH) monster_level = dun_level;
+ else monster_level = (p_ptr->lev * 2) + 10 + rand_int(40);
+
+ /* Reset the object generation level */
+ object_level = dun_level;
+
+ /* Nothing special here yet */
+ good_item_flag = FALSE;
+
+ /* Nothing good here yet */
+ rating = 0;
+
+ /* No ambush here yet */
+ ambush_flag = FALSE;
+
+ /* No fated level here yet */
+ fate_flag = FALSE;
+
+ /* Build the arena -KMW- */
+ if (p_ptr->inside_arena)
+ {
+ /* Small arena */
+ arena_gen();
+ }
+
+ /* Quest levels -KMW- */
+ else if (p_ptr->inside_quest)
+ {
+ quest_gen();
+ }
+
+ /* Special levels */
+ else if (build_special_level())
+ {
+ /* nothing */
+ }
+
+ /* Build the town */
+ else if (!dun_level)
+ {
+ /* Big wilderness mode */
+ if (!p_ptr->wild_mode)
+ {
+ /* Big town */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Make the wilderness */
+ wilderness_gen(0);
+
+ okay = TRUE;
+ }
+
+ /* Small wilderness mode */
+ else
+ {
+ /* Big screen */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Make the wilderness */
+ wilderness_gen_small();
+
+ okay = TRUE;
+ }
+ }
+
+ /* Build a dungeon level */
+ else
+ {
+ /* Requested size level */
+ if (d_ptr->size_x != -1)
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'size' dungeon level.");
+ }
+
+ cur_hgt = d_ptr->size_y * SCREEN_HGT;
+ cur_wid = d_ptr->size_x * SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows);
+ }
+ }
+ /* Very small (1 x 1 panel) level */
+ else if (!(dungeon_flags1 & DF1_BIG) &&
+ (dungeon_flags1 & DF1_SMALLEST))
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'small' dungeon level.");
+ }
+
+ cur_hgt = SCREEN_HGT;
+ cur_wid = SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = 1;
+ max_panel_cols = 1;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:1, Y:1.");
+ }
+ }
+
+ /* Small level */
+ else if (!(dungeon_flags1 & DF1_BIG) &&
+ (always_small_level ||
+ (dungeon_flags1 & DF1_SMALL) ||
+ (small_levels && rand_int(SMALL_LEVEL) == 0)))
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'small' dungeon level.");
+ }
+
+ tester_1 = rand_range(1, (MAX_HGT / SCREEN_HGT));
+ tester_2 = rand_range(1, (MAX_WID / SCREEN_WID) - 1);
+
+ cur_hgt = tester_1 * SCREEN_HGT;
+ cur_wid = tester_2 * SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows);
+ }
+ }
+
+ /* Normal level */
+ else
+ {
+ /* Use full panels */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+ }
+
+ /* Generate a level */
+ if (!cave_gen())
+ {
+ why = "could not place player";
+ okay = FALSE;
+ }
+ }
+
+ /* Extract the feeling */
+ if (rating > 100) feeling = 2;
+ else if (rating > 80) feeling = 3;
+ else if (rating > 60) feeling = 4;
+ else if (rating > 40) feeling = 5;
+ else if (rating > 30) feeling = 6;
+ else if (rating > 20) feeling = 7;
+ else if (rating > 10) feeling = 8;
+ else if (rating > 0) feeling = 9;
+ else feeling = 10;
+
+ /* Hack -- Have a special feeling sometimes */
+ if (good_item_flag && !p_ptr->preserve) feeling = 1;
+
+ /* It takes 1000 game turns for "feelings" to recharge */
+ if ((turn - old_turn) < 1000) feeling = 0;
+
+ /* Hack -- no feeling in the town */
+ if (!dun_level) feeling = 0;
+
+
+ /* Prevent object over-flow */
+ if (o_max >= max_o_idx)
+ {
+ /* Message */
+ why = "too many objects";
+
+ /* Message */
+ okay = FALSE;
+ }
+
+ /* Prevent monster over-flow */
+ if (m_max >= max_m_idx)
+ {
+ /* Message */
+ why = "too many monsters";
+
+ /* Message */
+ okay = FALSE;
+ }
+
+ /* Mega-Hack -- "auto-scum" */
+ if (auto_scum && (num < 100) && !p_ptr->inside_quest && dun_level)
+ {
+ /* Require "goodness" */
+ if ((feeling > 9) ||
+ ((dun_level >= 5) && (feeling > 8)) ||
+ ((dun_level >= 10) && (feeling > 7)) ||
+ ((dun_level >= 20) && (feeling > 6)) ||
+ ((dun_level >= 40) && (feeling > 5)))
+ {
+ /* Give message to cheaters */
+ if (cheat_room || cheat_hear ||
+ cheat_peek || cheat_xtra || p_ptr->precognition)
+ {
+ /* Message */
+ why = "boring level";
+ }
+
+ /* Try again */
+ okay = FALSE;
+ }
+ }
+
+ /* Accept */
+ if (okay || town_level) break;
+
+ /* Message */
+ if (why) msg_format("Generation restarted (%s)", why);
+
+ /* Wipe the objects */
+ wipe_o_list();
+
+ /* Wipe the monsters */
+ wipe_m_list();
+
+ /* Clear the fate icky flags */
+ for (i = 0; i < MAX_FATES; i++) fates[i].icky = FALSE;
+
+ /*
+ * Mega-Hack -- Reset special level flag if necessary
+ * XXX XXX XXX
+ */
+ wipe_special_level();
+ }
+
+ /* Give some mana to each grid -- DG */
+ generate_grid_mana();
+ }
+
+ /* Put the kept monsters -- DG */
+ if (!p_ptr->wild_mode) replace_all_friends();
+
+ /* Hack -- Clear used up fates */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if (fates[i].icky)
+ {
+ /* Mark the artefact as generated */
+ if ((fates[i].fate == FATE_FIND_A) && fates[i].a_idx)
+ {
+ a_info[fates[i].a_idx].cur_num = 1;
+ }
+ fates[i].fate = FATE_NONE;
+ fates[i].icky = FALSE;
+ }
+ }
+
+ /* Set special level generated flag if applicable */
+ finalise_special_level();
+
+ /* No teleporatations yet */
+ last_teleportation_y = -1;
+ last_teleportation_x = -1;
+
+ /* Mark the dungeon town as found */
+ if (town_level)
+ {
+ /* Set the known flag */
+ town_info[town_level].flags |= (TOWN_KNOWN);
+ }
+
+ /* The dungeon is ready */
+ character_dungeon = TRUE;
+
+ /* Remember when this level was "created" */
+ old_turn = turn;
+
+ if (seed_dungeon)
+ {
+ Rand_quick = FALSE;
+
+ seed_dungeon = old_seed_dungeon;
+ }
+
+ /* Provide astral chars with the full map */
+ if (p_ptr->astral && dun_level)
+ {
+ wiz_lite_extra();
+ }
+
+ /* Player should get the first move upon entering the dungeon */
+ p_ptr->energy = 100;
+}
diff --git a/src/ghost.c b/src/ghost.c
new file mode 100644
index 00000000..30a8f81a
--- /dev/null
+++ b/src/ghost.c
@@ -0,0 +1,1140 @@
+/* File: ghost.c */
+
+/*
+ * Purpose: ghost functions
+ *
+ * Created by DarkGod for PernAngband 4.1.0
+ * Lot of code from Drangband
+ */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Save a "bones" file for a dead character
+ *
+ * Should probably attempt some form of locking...
+ */
+void make_bones(void)
+{
+#if 0 /* DGDGDGDG */
+ FILE *fp;
+
+ int i;
+
+ char str[1024];
+
+
+ /* Ignore wizards and borgs */
+ if (!(noscore & 0x00FF))
+ {
+ /* Ignore people who die in town */
+ if (dun_level)
+ {
+ int level;
+ char tmp[128];
+
+ /* Slightly more tenacious saving routine. */
+ for (i = 0; i < 5; i++)
+ {
+ /* Ghost hovers near level of death. */
+ if (i == 0) level = dun_level;
+ else level = dun_level + 5 - damroll(2, 4);
+ if (level < 1) level = randint(4);
+
+ /* XXX XXX XXX "Bones" name */
+ sprintf(tmp, "bone%03d.%03d", dungeon_type, level);
+
+ /* Build the filename */
+ path_build(str, 1024, ANGBAND_DIR_BONE, tmp);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the bones file */
+ fp = my_fopen(str, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Close it right away */
+ if (fp) my_fclose(fp);
+
+ /* Do not over-write a previous ghost */
+ if (fp) continue;
+
+ /* If no file by that name exists, we can make a new one. */
+ if (!(fp)) break;
+ }
+
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Try to write a new "Bones File" */
+ fp = my_fopen(str, "w");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Not allowed to write it? Weird. */
+ if (!fp) return;
+
+ /* Save the info */
+ fprintf(fp, "%s\n", player_name);
+ fprintf(fp, "%d\n", p_ptr->mhp);
+ fprintf(fp, "%d\n", p_ptr->prace);
+ fprintf(fp, "%d\n", p_ptr->pclass);
+
+ /* Close and save the Bones file */
+ my_fclose(fp);
+ }
+ }
+#endif
+}
+
+#if 0 /* DGDGDGDG */
+/*
+ * Ghost generation info
+ */
+
+static int ghost_race;
+static int ghost_class;
+
+static char gb_name[32];
+
+
+/*
+ * Set a "blow" record for the ghost
+ */
+static void ghost_blow(int i, int m, int e, int d, int s)
+{
+ monster_race *g = &r_info[max_r_idx - 1];
+
+ /* Save the data */
+ g->blow[i].method = m;
+ g->blow[i].effect = e;
+ g->blow[i].d_dice = d;
+ g->blow[i].d_side = s;
+}
+
+
+/*
+ * Prepare the "ghost" race (method 1)
+ */
+static void set_ghost_aux_1(void)
+{
+ monster_race *r_ptr = &r_info[max_r_idx - 1];
+
+ int i, d1, d2;
+
+ int attack1, attack2;
+
+ int lev = r_ptr->level;
+
+ int grace = ghost_race;
+ int gclass = ghost_class;
+
+ cptr gr_name = rp_name + race_info[grace].title;
+ cptr gc_name = class_info[grace].title;
+
+
+ /* A wanderer from the town */
+ sprintf(r_name + r_ptr->name, "%s, the skeletal %s %s",
+ gb_name, gr_name, gc_name);
+
+
+ /* Use a "player" symbol */
+ r_ptr->d_char = 's';
+
+ /* Open doors, bash doors */
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+
+
+ /* Treasure drops */
+ r_ptr->flags1 |= (RF1_DROP_60 | RF1_DROP_90);
+
+ /* Treasure drops */
+ if (lev >= 10) r_ptr->flags1 |= (RF1_DROP_1D2);
+ if (lev >= 20) r_ptr->flags1 |= (RF1_DROP_2D2);
+ if (lev >= 30) r_ptr->flags1 |= (RF1_DROP_4D2);
+
+ /* Treasure drops */
+ if (lev >= 40) r_ptr->flags1 &= ~(RF1_DROP_4D2);
+ if (lev >= 40) r_ptr->flags1 |= (RF1_DROP_GREAT);
+
+
+ /* Extract an "immunity power" */
+ i = (lev / 5) + randint(5);
+
+ /* Immunity (by level) */
+ switch ((i > 12) ? 12 : i)
+ {
+ case 12 :
+ {
+ r_ptr->flags3 |= (RF3_IM_POIS);
+ }
+
+ case 11:
+ case 10:
+ {
+ r_ptr->flags3 |= (RF3_IM_ACID);
+ }
+
+ case 9:
+ case 8:
+ case 7:
+ {
+ r_ptr->flags3 |= (RF3_IM_FIRE);
+ }
+
+ case 6:
+ case 5:
+ case 4:
+ {
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ }
+
+ case 3:
+ case 2:
+ case 1:
+ {
+ r_ptr->flags3 |= (RF3_IM_ELEC);
+ }
+ }
+
+
+ /* Extract some spells */
+ switch (gclass)
+ {
+ /* Warrior */
+ case CLASS_WARRIOR:
+ case CLASS_UNBELIEVER:
+ case CLASS_ARCHER:
+ case CLASS_MONK:
+ case CLASS_SYMBIANT:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 8;
+ r_ptr->flags4 |= (RF4_ARROW_1);
+ if (lev > 15) r_ptr->flags4 |= (RF4_ARROW_2);
+ if (lev > 30) r_ptr->flags4 |= (RF4_ARROW_3);
+ if (lev > 45) r_ptr->flags4 |= (RF4_ARROW_4);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_UMBER;
+ break;
+ }
+
+ /* Mage */
+ case CLASS_MAGE:
+ case CLASS_HIGH_MAGE:
+ case CLASS_POWERMAGE:
+ case CLASS_RUNECRAFTER:
+ case CLASS_HARPER:
+ case CLASS_SORCERER:
+ case CLASS_ILLUSIONIST:
+ case CLASS_DRUID:
+ case CLASS_NECRO:
+ case CLASS_ALCHEMIST:
+ case CLASS_CHAOS_WARRIOR:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 2;
+ r_ptr->flags4 |= (RF4_ARROW_1);
+ r_ptr->flags5 |= (RF5_SLOW | RF5_CONF);
+ r_ptr->flags6 |= (RF6_BLINK);
+ if (lev > 5) r_ptr->flags5 |= (RF5_BA_POIS);
+ if (lev > 7) r_ptr->flags5 |= (RF5_BO_ELEC);
+ if (lev > 10) r_ptr->flags5 |= (RF5_BO_COLD);
+ if (lev > 12) r_ptr->flags6 |= (RF6_TPORT);
+ if (lev > 15) r_ptr->flags5 |= (RF5_BO_ACID);
+ if (lev > 20) r_ptr->flags5 |= (RF5_BO_FIRE);
+ if (lev > 25) r_ptr->flags5 |= (RF5_BA_COLD);
+ if (lev > 25) r_ptr->flags6 |= (RF6_HASTE);
+ if (lev > 30) r_ptr->flags5 |= (RF5_BA_FIRE);
+ if (lev > 40) r_ptr->flags5 |= (RF5_BO_MANA);
+ if (lev > 50) r_ptr->flags6 |= (RF6_S_DRAGON);
+ if (lev > 60) r_ptr->flags5 |= (RF5_BA_MANA);
+ if (lev > 70) r_ptr->flags6 |= (RF6_S_HI_UNDEAD);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_RED;
+ break;
+ }
+
+ /* Priest */
+ case CLASS_PRIEST:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 4;
+ r_ptr->flags5 |= (RF5_CAUSE_1 | RF5_SCARE);
+ if (lev > 5) r_ptr->flags6 |= (RF6_HEAL);
+ if (lev > 10) r_ptr->flags5 |= (RF5_BLIND);
+ if (lev > 12) r_ptr->flags5 |= (RF5_CAUSE_2);
+ if (lev > 18) r_ptr->flags5 |= (RF5_HOLD);
+ if (lev > 25) r_ptr->flags5 |= (RF5_CONF);
+ if (lev > 30) r_ptr->flags5 |= (RF5_CAUSE_3);
+ if (lev > 35) r_ptr->flags5 |= (RF5_DRAIN_MANA);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_L_BLUE;
+ break;
+ }
+
+ /* Rogue */
+ case CLASS_ROGUE:
+ case CLASS_MERCHANT:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 6;
+ r_ptr->flags6 |= (RF6_BLINK);
+ if (lev > 10) r_ptr->flags5 |= (RF5_CONF);
+ if (lev > 18) r_ptr->flags5 |= (RF5_SLOW);
+ if (lev > 25) r_ptr->flags6 |= (RF6_TPORT);
+ if (lev > 30) r_ptr->flags5 |= (RF5_HOLD);
+ if (lev > 35) r_ptr->flags6 |= (RF6_TELE_TO);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_BLUE;
+ break;
+ }
+
+ /* Ranger */
+ case CLASS_RANGER:
+ case CLASS_WARLOCK:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 6;
+ r_ptr->flags4 |= (RF4_ARROW_1);
+ if (lev > 5) r_ptr->flags5 |= (RF5_BA_POIS);
+ if (lev > 7) r_ptr->flags5 |= (RF5_BO_ELEC);
+ if (lev > 10) r_ptr->flags5 |= (RF5_BO_COLD);
+ if (lev > 18) r_ptr->flags5 |= (RF5_BO_ACID);
+ if (lev > 25) r_ptr->flags5 |= (RF5_BO_FIRE);
+ if (lev > 30) r_ptr->flags5 |= (RF5_BA_COLD);
+ if (lev > 35) r_ptr->flags5 |= (RF5_BA_FIRE);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_GREEN;
+ break;
+ }
+
+ /* Paladin */
+ case CLASS_PALADIN:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 8;
+ r_ptr->flags5 |= (RF5_CAUSE_1 | RF5_SCARE);
+ if (lev > 5) r_ptr->flags6 |= (RF6_HEAL);
+ if (lev > 10) r_ptr->flags5 |= (RF5_BLIND);
+ if (lev > 12) r_ptr->flags5 |= (RF5_CAUSE_2);
+ if (lev > 18) r_ptr->flags5 |= (RF5_HOLD);
+ if (lev > 25) r_ptr->flags5 |= (RF5_CONF);
+ if (lev > 30) r_ptr->flags5 |= (RF5_CAUSE_3);
+ if (lev > 35) r_ptr->flags5 |= (RF5_DRAIN_MANA);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_WHITE;
+ break;
+ }
+
+ /* Beastmaster */
+ case CLASS_BEASTMASTER:
+ case CLASS_DAEMONOLOGIST:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 3;
+ r_ptr->flags6 |= (RF6_S_KIN);
+ if (lev > 10) r_ptr->flags6 |= (RF6_S_SPIDER);
+ if (lev > 15) r_ptr->flags6 |= (RF6_S_MONSTERS);
+ if (lev > 20) r_ptr->flags6 |= (RF6_S_HOUND);
+ if (lev > 35) r_ptr->flags6 |= (RF6_S_DEMON);
+ if (lev > 30) r_ptr->flags6 |= (RF6_S_UNDEAD);
+ if (lev > 35) r_ptr->flags6 |= (RF6_S_DRAGON);
+ if (lev > 40) r_ptr->flags6 |= (RF6_S_HI_UNDEAD);
+ if (lev > 45) r_ptr->flags6 |= (RF6_S_HI_DRAGON);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_UMBER;
+ break;
+ }
+
+ /* Mindcrafter */
+ case CLASS_MINDCRAFTER:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 4;
+ r_ptr->flags5 |= (RF5_CAUSE_1 | RF5_SCARE);
+ if (lev > 5) r_ptr->flags6 |= (RF6_HEAL);
+ if (lev > 10) r_ptr->flags5 |= (RF5_MIND_BLAST);
+ if (lev > 12) r_ptr->flags6 |= (RF6_FORGET);
+ if (lev > 18) r_ptr->flags5 |= (RF5_BRAIN_SMASH);
+ if (lev > 25) r_ptr->flags5 |= (RF5_CONF);
+ if (lev > 30) r_ptr->flags5 |= (RF5_CAUSE_3);
+ if (lev > 35) r_ptr->flags5 |= (RF5_DRAIN_MANA);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_L_BLUE;
+ break;
+ }
+
+ /* Possessor */
+ case CLASS_POSSESSOR:
+ case CLASS_MIMIC:
+ {
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 4;
+ if (lev > 7) r_ptr->flags4 |= (RF4_BR_ELEC);
+ if (lev > 10) r_ptr->flags4 |= (RF4_BR_COLD);
+ if (lev > 18) r_ptr->flags4 |= (RF4_BR_ACID);
+ if (lev > 25) r_ptr->flags4 |= (RF4_BR_FIRE);
+ if (lev > 35) r_ptr->flags4 |= (RF4_BR_CHAO);
+ if (lev > 45) r_ptr->flags4 |= (RF4_BR_TIME);
+ r_ptr->flags1 |= (RF1_CHAR_MULTI);
+ r_ptr->flags1 |= (RF1_ATTR_MULTI);
+
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_UMBER;
+ break;
+ }
+
+ default:
+ {
+ /* Use a "player" color */
+ r_ptr->d_attr = TERM_WHITE;
+ break;
+ }
+ }
+
+
+ /* Racial properties */
+ if (grace == RACE_HALF_ORC) r_ptr->flags3 |= (RF3_ORC);
+ if (grace == RACE_KOBOLD) r_ptr->flags3 |= (RF3_ORC);
+ if (grace == RACE_HALF_TROLL) r_ptr->flags3 |= (RF3_TROLL);
+ if (grace == RACE_THUNDERLORD) r_ptr->flags3 |= (RF3_THUNDERLORD);
+
+
+ /* Armor class */
+ r_ptr->ac = 15 + randint(15);
+
+ /* Non mage/priest gets extra armor */
+ if ((gclass != CLASS_MAGE) && (gclass != CLASS_PRIEST))
+ {
+ r_ptr->ac += randint(60);
+ }
+
+
+ /* Default speed (normal) */
+ r_ptr->speed = 110;
+
+ /* Higher level they are faster */
+ if (lev >= 50) r_ptr->speed += 10;
+
+ /* High level mages are fast... */
+ if ((gclass == CLASS_MAGE) && (lev >= 20)) r_ptr->speed += 10;
+
+ /* High level rogues are fast... */
+ if ((gclass == CLASS_ROGUE) && (lev >= 30)) r_ptr->speed += 10;
+
+
+ /* Base damage */
+ d1 = 1;
+ d2 = 2 * (lev + 5);
+
+ /* Break up the damage */
+ while ((d1 * 8) < d2)
+ {
+ d1 = d1 * 2;
+ d2 = d2 / 2;
+ }
+
+ attack1 = attack2 = RBM_HIT;
+
+ /* Extract attacks */
+ switch (gclass)
+ {
+
+ /* Warrior */
+ case CLASS_WARRIOR:
+ case CLASS_UNBELIEVER:
+ case CLASS_ARCHER:
+ case CLASS_MONK:
+ case CLASS_SYMBIANT:
+ case CLASS_BEASTMASTER:
+ case CLASS_DAEMONOLOGIST:
+ case CLASS_POSSESSOR:
+ case CLASS_MIMIC:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 2;
+
+ /* Normal attacks (four) */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+ ghost_blow(1, attack2, RBE_HURT, d1, d2);
+ ghost_blow(2, attack1, RBE_HURT, d1, d2);
+ ghost_blow(3, attack2, RBE_HURT, d1, d2);
+
+ break;
+ }
+
+ /* Mage */
+ case CLASS_MAGE:
+ case CLASS_HIGH_MAGE:
+ case CLASS_POWERMAGE:
+ case CLASS_RUNECRAFTER:
+ case CLASS_HARPER:
+ case CLASS_SORCERER:
+ case CLASS_ILLUSIONIST:
+ case CLASS_DRUID:
+ case CLASS_NECRO:
+ case CLASS_ALCHEMIST:
+ case CLASS_CHAOS_WARRIOR:
+ case CLASS_MERCHANT:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 3 / 2;
+
+ /* Normal attacks (one) */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+
+ break;
+ }
+
+ /* Priest */
+ case CLASS_PRIEST:
+ case CLASS_MINDCRAFTER:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 3 / 2;
+
+ /* Normal attacks (two) */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+ ghost_blow(0, attack2, RBE_HURT, d1, d2);
+
+ break;
+ }
+
+ /* Rogue */
+ case CLASS_ROGUE:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 2;
+
+ /* Normal attacks */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+ ghost_blow(1, attack2, RBE_HURT, d1, d2);
+
+ /* Special attacks -- Touch to steal */
+ ghost_blow(2, RBM_TOUCH, RBE_EAT_ITEM, 0, 0);
+ ghost_blow(3, RBM_TOUCH, RBE_EAT_ITEM, 0, 0);
+
+ break;
+ }
+
+ /* Ranger */
+ case CLASS_RANGER:
+ case CLASS_WARLOCK:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 2;
+
+ /* Normal attacks (three) */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+ ghost_blow(1, attack2, RBE_HURT, d1, d2);
+ ghost_blow(2, attack1, RBE_HURT, d1, d2);
+
+ break;
+ }
+
+ /* Paladin */
+ case CLASS_PALADIN:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 2;
+
+ /* Normal attacks (three) */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+ ghost_blow(1, attack2, RBE_HURT, d1, d2);
+ ghost_blow(2, attack1, RBE_HURT, d1, d2);
+ break;
+ }
+
+ default:
+ {
+ /* Sometimes increase damage */
+ if (lev >= 30) d2 = d2 * 2;
+
+ /* Normal attacks (four) */
+ ghost_blow(0, attack1, RBE_HURT, d1, d2);
+ ghost_blow(1, attack2, RBE_HURT, d1, d2);
+ ghost_blow(2, attack1, RBE_HURT, d1, d2);
+ ghost_blow(3, attack2, RBE_HURT, d1, d2);
+
+ break;
+ }
+ }
+}
+
+
+
+/*
+ * Prepare the ghost -- method 2
+ */
+static void set_ghost_aux_2(void)
+{
+ monster_race *r_ptr = &r_info[max_r_idx - 1];
+
+ int lev = r_ptr->level;
+
+ int grace = ghost_race;
+
+ cptr gr_name = rp_name + race_info[grace].title;
+
+
+ /* The ghost is cold blooded */
+ r_ptr->flags2 |= (RF2_COLD_BLOOD);
+
+ /* The ghost is undead */
+ r_ptr->flags3 |= (RF3_UNDEAD);
+
+ /* The ghost is immune to poison */
+ r_ptr->flags3 |= (RF3_IM_POIS);
+
+
+ switch ((lev / 4) + randint(3))
+ {
+ case 1:
+ case 2:
+ case 3:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Skeleton %s", gb_name, gr_name);
+ r_ptr->d_char = 's';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ if (grace == RACE_HALF_ORC) r_ptr->flags3 |= (RF3_ORC);
+ if (grace == RACE_HALF_TROLL) r_ptr->flags3 |= (RF3_TROLL);
+ if (grace == RACE_THUNDERLORD) r_ptr->flags3 |= (RF3_THUNDERLORD);
+ r_ptr->ac = 26;
+ r_ptr->speed = 110;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 2, 6);
+ ghost_blow(1, RBM_HIT, RBE_HURT, 2, 6);
+
+ break;
+ }
+
+ case 4:
+ case 5:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Zombified %s", gb_name, gr_name);
+ r_ptr->d_char = 'z';
+ r_ptr->d_attr = TERM_L_DARK;
+ r_ptr->flags1 |= (RF1_DROP_60 | RF1_DROP_90);
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ if (grace == RACE_HALF_ORC) r_ptr->flags3 |= (RF3_ORC);
+ if (grace == RACE_HALF_TROLL) r_ptr->flags3 |= (RF3_TROLL);
+ if (grace == RACE_THUNDERLORD) r_ptr->flags3 |= (RF3_THUNDERLORD);
+ r_ptr->ac = 30;
+ r_ptr->speed = 110;
+ r_ptr->hside *= 2;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 2, 9);
+
+ break;
+ }
+
+ case 6:
+ case 7:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Mummified %s", gb_name, gr_name);
+ r_ptr->d_char = 'z';
+ r_ptr->d_attr = TERM_L_DARK;
+ r_ptr->flags1 |= (RF1_DROP_1D2);
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ if (grace == RACE_HALF_ORC) r_ptr->flags3 |= (RF3_ORC);
+ if (grace == RACE_HALF_TROLL) r_ptr->flags3 |= (RF3_TROLL);
+ if (grace == RACE_THUNDERLORD) r_ptr->flags3 |= (RF3_THUNDERLORD);
+ r_ptr->ac = 35;
+ r_ptr->speed = 110;
+ r_ptr->hside *= 2;
+ r_ptr->mexp = (r_ptr->mexp * 3) / 2;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 3, 8);
+ ghost_blow(1, RBM_HIT, RBE_HURT, 3, 8);
+ ghost_blow(2, RBM_HIT, RBE_HURT, 3, 8);
+
+ break;
+ }
+
+ case 8:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Poltergeist", gb_name);
+ r_ptr->d_char = 'G';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags1 |= (RF1_RAND_50 | RF1_RAND_25 | RF1_DROP_1D2);
+ r_ptr->flags2 |= (RF2_INVISIBLE | RF2_PASS_WALL);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ r_ptr->ac = 20;
+ r_ptr->speed = 130;
+ r_ptr->mexp = (r_ptr->mexp * 3) / 2;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 2, 6);
+ ghost_blow(1, RBM_HIT, RBE_HURT, 2, 6);
+ ghost_blow(2, RBM_TOUCH, RBE_TERRIFY, 0, 0);
+ ghost_blow(3, RBM_TOUCH, RBE_TERRIFY, 0, 0);
+
+ break;
+ }
+
+ case 9:
+ case 10:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Spirit", gb_name);
+ r_ptr->d_char = 'G';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags1 |= (RF1_DROP_1D2);
+ r_ptr->flags2 |= (RF2_INVISIBLE | RF2_PASS_WALL);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ r_ptr->ac = 20;
+ r_ptr->speed = 110;
+ r_ptr->hside *= 2;
+ r_ptr->mexp = r_ptr->mexp * 3;
+
+ ghost_blow(0, RBM_TOUCH, RBE_LOSE_WIS, 2, 6);
+ ghost_blow(1, RBM_TOUCH, RBE_LOSE_DEX, 2, 6);
+ ghost_blow(2, RBM_HIT, RBE_HURT, 4, 6);
+ ghost_blow(3, RBM_WAIL, RBE_TERRIFY, 0, 0);
+
+ break;
+ }
+
+ case 11:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Ghost", gb_name);
+ r_ptr->d_char = 'G';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags1 |= (RF1_DROP_1D2);
+ r_ptr->flags2 |= (RF2_INVISIBLE | RF2_PASS_WALL);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ r_ptr->flags5 |= (RF5_BLIND | RF5_HOLD | RF5_DRAIN_MANA);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 15;
+ r_ptr->ac = 40;
+ r_ptr->speed = 120;
+ r_ptr->hside *= 2;
+ r_ptr->mexp = (r_ptr->mexp * 7) / 2;
+
+ ghost_blow(0, RBM_WAIL, RBE_TERRIFY, 0, 0);
+ ghost_blow(1, RBM_TOUCH, RBE_EXP_20, 0, 0);
+ ghost_blow(2, RBM_CLAW, RBE_LOSE_INT, 2, 6);
+ ghost_blow(3, RBM_CLAW, RBE_LOSE_WIS, 2, 6);
+
+ break;
+ }
+
+ case 12:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Vampire", gb_name);
+ r_ptr->d_char = 'V';
+ r_ptr->d_attr = TERM_VIOLET;
+ r_ptr->flags1 |= (RF1_DROP_2D2);
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ r_ptr->flags3 |= (RF3_HURT_LITE);
+ r_ptr->flags5 |= (RF5_SCARE | RF5_HOLD | RF5_CAUSE_2);
+ r_ptr->flags6 |= (RF6_TELE_TO);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 8;
+ r_ptr->ac = 40;
+ r_ptr->speed = 110;
+ r_ptr->hside *= 3;
+ r_ptr->mexp = r_ptr->mexp * 3;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 5, 8);
+ ghost_blow(1, RBM_HIT, RBE_HURT, 5, 8);
+ ghost_blow(2, RBM_BITE, RBE_EXP_40, 0, 0);
+
+ break;
+ }
+
+ case 13:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Wraith", gb_name);
+ r_ptr->d_char = 'W';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags1 |= (RF1_DROP_2D2 | RF1_DROP_4D2);
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ r_ptr->flags3 |= (RF3_IM_COLD | RF3_HURT_LITE);
+ r_ptr->flags5 |= (RF5_BLIND | RF5_SCARE | RF5_HOLD);
+ r_ptr->flags5 |= (RF5_CAUSE_3 | RF5_BO_NETH);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 7;
+ r_ptr->ac = 60;
+ r_ptr->speed = 120;
+ r_ptr->hside *= 3;
+ r_ptr->mexp = r_ptr->mexp * 5;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 6, 8);
+ ghost_blow(1, RBM_HIT, RBE_HURT, 6, 8);
+ ghost_blow(2, RBM_TOUCH, RBE_EXP_20, 0, 0);
+
+ break;
+ }
+
+ case 14:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Vampire Lord", gb_name);
+ r_ptr->d_char = 'V';
+ r_ptr->d_attr = TERM_BLUE;
+ r_ptr->flags1 |= (RF1_DROP_1D2 | RF1_DROP_GREAT);
+ r_ptr->flags2 |= (RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ r_ptr->flags3 |= (RF3_HURT_LITE);
+ r_ptr->flags5 |= (RF5_SCARE | RF5_HOLD | RF5_CAUSE_3 | RF5_BO_NETH);
+ r_ptr->flags6 |= (RF6_TELE_TO);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 8;
+ r_ptr->ac = 80;
+ r_ptr->speed = 110;
+ r_ptr->hside *= 2;
+ r_ptr->hdice *= 2;
+ r_ptr->mexp = r_ptr->mexp * 20;
+
+ ghost_blow(0, RBM_HIT, RBE_HURT, 6, 8);
+ ghost_blow(1, RBM_HIT, RBE_HURT, 6, 8);
+ ghost_blow(2, RBM_HIT, RBE_HURT, 6, 8);
+ ghost_blow(3, RBM_BITE, RBE_EXP_80, 0, 0);
+
+ break;
+ }
+
+ case 15:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Ghost", gb_name);
+ r_ptr->d_char = 'G';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags1 |= (RF1_DROP_2D2 | RF1_DROP_GREAT);
+ r_ptr->flags2 |= (RF2_INVISIBLE | RF2_PASS_WALL);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ r_ptr->flags5 |= (RF5_BLIND | RF5_CONF | RF5_HOLD | RF5_DRAIN_MANA);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 5;
+ r_ptr->ac = 90;
+ r_ptr->speed = 130;
+ r_ptr->hside *= 3;
+ r_ptr->mexp = r_ptr->mexp * 20;
+
+ ghost_blow(0, RBM_WAIL, RBE_TERRIFY, 0, 0);
+ ghost_blow(1, RBM_TOUCH, RBE_EXP_20, 0, 0);
+ ghost_blow(2, RBM_CLAW, RBE_LOSE_INT, 2, 6);
+ ghost_blow(3, RBM_CLAW, RBE_LOSE_WIS, 2, 6);
+
+ break;
+ }
+
+ case 17:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Lich", gb_name);
+ r_ptr->d_char = 'L';
+ r_ptr->d_attr = TERM_ORANGE;
+ r_ptr->flags1 |= (RF1_DROP_2D2 | RF1_DROP_1D2 | RF1_DROP_GREAT);
+ r_ptr->flags2 |= (RF2_SMART | RF2_OPEN_DOOR | RF2_BASH_DOOR);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ r_ptr->flags5 |= (RF5_BLIND | RF5_SCARE | RF5_CONF | RF5_HOLD);
+ r_ptr->flags5 |= (RF5_DRAIN_MANA | RF5_BA_FIRE | RF5_BA_COLD);
+ r_ptr->flags5 |= (RF5_CAUSE_3 | RF5_CAUSE_4 | RF5_BRAIN_SMASH);
+ r_ptr->flags6 |= (RF6_BLINK | RF6_TPORT | RF6_TELE_TO | RF6_S_UNDEAD);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 3;
+ r_ptr->ac = 120;
+ r_ptr->speed = 120;
+ r_ptr->hside *= 3;
+ r_ptr->hdice *= 2;
+ r_ptr->mexp = r_ptr->mexp * 50;
+
+ ghost_blow(0, RBM_TOUCH, RBE_LOSE_DEX, 4, 12);
+ ghost_blow(1, RBM_TOUCH, RBE_LOSE_DEX, 4, 12);
+ ghost_blow(2, RBM_TOUCH, RBE_UN_POWER, 0, 0);
+ ghost_blow(3, RBM_TOUCH, RBE_EXP_40, 0, 0);
+
+ break;
+ }
+
+ default:
+ {
+ sprintf(r_name + r_ptr->name, "%s, the Ghost", gb_name);
+ r_ptr->d_char = 'G';
+ r_ptr->d_attr = TERM_WHITE;
+ r_ptr->flags1 |= (RF1_DROP_1D2 | RF1_DROP_2D2 | RF1_DROP_GREAT);
+ r_ptr->flags2 |= (RF2_SMART | RF2_INVISIBLE | RF2_PASS_WALL);
+ r_ptr->flags3 |= (RF3_IM_COLD);
+ r_ptr->flags5 |= (RF5_BLIND | RF5_CONF | RF5_HOLD | RF5_BRAIN_SMASH);
+ r_ptr->flags5 |= (RF5_DRAIN_MANA | RF5_BA_NETH | RF5_BO_NETH);
+ r_ptr->flags6 |= (RF6_TELE_TO | RF6_TELE_LEVEL);
+ r_ptr->freq_inate = r_ptr->freq_spell = 100 / 2;
+ r_ptr->ac = 130;
+ r_ptr->speed = 130;
+ r_ptr->hside *= 2;
+ r_ptr->hdice *= 2;
+ r_ptr->mexp = r_ptr->mexp * 30;
+
+ ghost_blow(0, RBM_WAIL, RBE_TERRIFY, 0, 0);
+ ghost_blow(1, RBM_TOUCH, RBE_EXP_20, 0, 0);
+ ghost_blow(2, RBM_CLAW, RBE_LOSE_INT, 2, 6);
+ ghost_blow(3, RBM_CLAW, RBE_LOSE_WIS, 2, 6);
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Hack -- Prepare the "ghost" race
+ *
+ * We are given a "name" of the form "Bob" (or "Bob, the xxx"), and
+ * a race/class (by index), and a level (usually the dungeon level),
+ * and a special "town" flag (which chooses the major ghost "type").
+ *
+ * Note that "town" ghosts are always level 1 to 50, and other ghosts
+ * are always level 1 to 100 (or deeper?)
+ *
+ * Currently we save the current "ghost race info" in the savefile.
+ * Note that ghosts from pre-2.7.7 savefiles are always ignored.
+ *
+ * Eventually we should probably save the ghost in such a way as
+ * to allow it to be "re-extracted" from a small amount of info,
+ * such as the "base name", the "race", the "class", the base "hp",
+ * the "level", the "town" flag, and the "random seed". This would
+ * make the savefile impervious to changes in the race format.
+ *
+ * Thus we would need to save "pn", "hp", "gr", "gc", and "lev",
+ * plus the "town" flag, plus a random seed of some form. Note that
+ * we already save the "pn" value, followed by a "comma" and "title",
+ * and we have the "lev" field as the actual ghost level. But it is
+ * probably best to ignore this storage method for a few versions.
+ *
+ * We "could" extract the "hp" from the ghost name and current hp's.
+ * We "could" extract the "town" flag from the ghost race symbol.
+ *
+ * Note that each new ghost needs a new "random seed". And actually,
+ * we do not really need a "full" random seed, we could just use a
+ * random value from which random numbers can be extracted. (?)
+ */
+static void set_ghost(cptr pname, int hp, int grace, int gclass, int lev, bool town)
+{
+ int i;
+
+ monster_race *r_ptr = &r_info[max_r_idx - 1];
+
+ /* Ghosts are too weak otherwise */
+ hp *= 2;
+
+ /* Extract the basic ghost name */
+ strcpy(gb_name, pname);
+
+ /* Find the first comma, or end of string */
+ for (i = 0; (i < 16) && (gb_name[i]) && (gb_name[i] != ','); i++);
+
+ /* Terminate the name */
+ gb_name[i] = '\0';
+
+ /* Force a name */
+ if (!gb_name[1]) strcpy(gb_name, "Nobody");
+
+ /* Capitalize the name */
+ if (islower(gb_name[0])) gb_name[0] = toupper(gb_name[0]);
+
+
+ /* Clear the normal flags */
+ r_ptr->flags1 = r_ptr->flags2 = r_ptr->flags3 = r_ptr->flags7 = r_ptr->flags8 = r_ptr->flags9 = 0L;
+
+ /* Clear the spell flags */
+ r_ptr->flags4 = r_ptr->flags5 = r_ptr->flags6 = 0L;
+
+
+ /* Clear the attacks */
+ ghost_blow(0, 0, 0, 0, 0);
+ ghost_blow(1, 0, 0, 0, 0);
+ ghost_blow(2, 0, 0, 0, 0);
+ ghost_blow(3, 0, 0, 0, 0);
+
+
+ /* The ghost never sleeps */
+ r_ptr->sleep = 0;
+
+ /* The ghost is very attentive */
+ r_ptr->aaf = 100;
+
+
+ /* Save the level */
+ r_ptr->level = lev;
+
+ /* Extract the default experience */
+ r_ptr->mexp = lev * 5 + 5;
+
+
+ /* Hack -- Break up the hitpoints */
+ for (i = 1; i * i < hp; i++);
+
+ /* Extract the basic hit dice and sides */
+ r_ptr->hdice = r_ptr->hside = i;
+
+
+ /* Unique monster */
+ r_ptr->flags1 |= (RF1_UNIQUE);
+
+ /* Only carry good items */
+ r_ptr->flags1 |= (RF1_ONLY_ITEM | RF1_DROP_GOOD);
+
+ /* The ghost is always evil */
+ r_ptr->flags3 |= (RF3_EVIL);
+
+ /* Cannot be slept or confused */
+ r_ptr->flags3 |= (RF3_NO_SLEEP | RF3_NO_CONF);
+
+ /* All ghosts are undeads */
+ r_ptr->flags3 |= RF3_UNDEAD;
+
+
+ /* Save the race and class */
+ ghost_race = grace;
+ ghost_class = gclass;
+
+
+ /* Prepare the ghost (method 1) */
+ if (town)
+ {
+ /* Method 1 */
+ set_ghost_aux_1();
+ }
+
+ /* Prepare the ghost (method 2) */
+ else
+ {
+ /* Method 2 */
+ set_ghost_aux_2();
+ }
+}
+#endif
+
+
+/*
+ * Places a ghost somewhere.
+ */
+s16b place_ghost(void)
+{
+#if 0 /* DGDGDGDG */
+ int y, x, hp, level, grace, gclass;
+
+ monster_race *r_ptr = &r_info[max_r_idx - 1];
+
+ FILE *fp;
+
+ bool err = FALSE;
+ bool town = FALSE;
+
+ char name[100];
+ char tmp[1024];
+
+ /* Hack -- no ghosts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Already have a ghost */
+ if (r_ptr->cur_num >= r_ptr->max_num)
+ {
+ return (FALSE);
+ }
+
+ /* Dungeon -- Use Dungeon Level */
+ else
+ {
+ /* And even then, it only happens sometimes */
+ if (14 > randint((dun_level / 2) + 11)) return (FALSE);
+
+ /* Only a 45% chance */
+ if (magik(45)) return (FALSE);
+
+ /* Level is dungeon level */
+ level = dun_level;
+ }
+
+
+ /* Choose a bones file */
+ sprintf(tmp, "%s%sbone%03d.%03d", ANGBAND_DIR_BONE, PATH_SEP, dungeon_type, level);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the bones file */
+ fp = my_fopen(tmp, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* No bones file to use */
+ if (!fp) return (FALSE);
+
+ /* Scan the file */
+ err = (fscanf(fp, "%[^\n]\n%d\n%d\n%d", name, &hp, &grace, &gclass) != 4);
+
+
+ /* Close the file */
+ fclose(fp);
+
+ /* Previously, the bone file would now be deleted. The new
+ * method is to only remove the bone file when the ghost is
+ * destroyed. This means that failing to kill a ghost will
+ * not lose it permenently -TM-
+ * fd_kill(tmp); */
+
+ /* Catch errors */
+ if (err)
+ {
+ msg_print("Warning -- deleted corrupt 'ghost' file!");
+ return (FALSE);
+ }
+
+ /* Create "town" flag */
+ /* TM- What is this? Previously, if the player and dungeon levels
+ * were equal then a 'town' ghost was created. I can't see why
+ * 'town'. They are simply ghosts with abilities determined by
+ * previous class. Currently we just pick between the two.
+ * WAS: if (level == p_ptr->lev) town = TRUE;
+ */
+ if (!rand_int(2)) town = TRUE;
+
+ /* Set up the ghost */
+ set_ghost(name, hp, grace, gclass, level, town);
+
+
+ /* Hack -- pick a nice (far away) location */
+ while (1)
+ {
+
+ /* Pick a location */
+ y = randint(cur_hgt - 2);
+ x = randint(cur_wid - 2);
+
+ /* Require "naked" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Accept far away grids */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > MAX_SIGHT + 5) break;
+ }
+
+
+ /*** Place the Ghost by Hand (so no-one else does it accidentally) ***/
+
+ r_ptr->cur_num = 0;
+ r_ptr->max_num = 1;
+
+ if (!place_monster_one(y, x, max_r_idx - 1, 0, FALSE, MSTATUS_ENEMY))
+ {
+ return FALSE;
+ }
+
+ /* Make sure it looks right */
+ r_ptr->x_attr = r_ptr->d_attr;
+ r_ptr->x_char = r_ptr->d_char;
+ return TRUE;
+#else
+ return (FALSE);
+#endif
+}
diff --git a/src/gods.c b/src/gods.c
new file mode 100644
index 00000000..8d0b1791
--- /dev/null
+++ b/src/gods.c
@@ -0,0 +1,140 @@
+/* File: gods.c */
+
+/* Purpose: Deities code */
+
+/*
+ * Copyright (c) 2002 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Add amt piety is god is god
+ */
+void inc_piety(int god, s32b amt)
+{
+ s32b old = p_ptr->grace;
+
+ if ((god == GOD_ALL) || (god == p_ptr->pgod))
+ {
+ set_grace(p_ptr->grace + amt);
+
+ if(amt > 0 && p_ptr->grace <= old)
+ set_grace(300000);
+
+ if(amt < 0 && p_ptr->grace >= old)
+ set_grace(-300000);
+ }
+}
+
+/*
+ * Renounce to religion
+ */
+void abandon_god(int god)
+{
+ if ((god == GOD_ALL) || (god == p_ptr->pgod))
+ {
+ p_ptr->pgod = GOD_NONE;
+ set_grace(0);
+ }
+}
+
+/*
+ * Get a religion
+ */
+void follow_god(int god, bool silent)
+{
+ /* Poor unbelievers, i'm so mean ... BOUHAHAHA */
+ if (get_skill(SKILL_ANTIMAGIC))
+ {
+ msg_print("Don't be silly; you don't believe in gods.");
+ return;
+ }
+
+ /* Are we allowed ? */
+ if (process_hooks(HOOK_FOLLOW_GOD, "(d,s)", god, "ask"))
+ return;
+
+ if (p_ptr->pgod == GOD_NONE)
+ {
+ p_ptr->pgod = god;
+
+ /* Melkor offer Udun magic */
+ GOD(GOD_MELKOR)
+ {
+ s_info[SKILL_UDUN].hidden = FALSE;
+ if (!silent) msg_print("You feel the dark powers of Melkor in you. You can now use the Udun skill.");
+ }
+
+ /* Anything to be done? */
+ process_hooks(HOOK_FOLLOW_GOD, "(d,s)", god, "done");
+ }
+}
+
+/*
+ * Show religious info.
+ */
+bool show_god_info(bool ext)
+{
+ int pgod = p_ptr->pgod;
+ int tmp;
+
+ deity_type *d_ptr;
+
+ if (pgod < 0)
+ {
+ msg_print("You don't worship anyone.");
+ msg_print(NULL);
+ return FALSE;
+ }
+ else
+ {
+ int i;
+
+ d_ptr = &deity_info[pgod];
+
+ msg_print(NULL);
+
+ character_icky = TRUE;
+ Term_save();
+
+ text_out(format("You worship %s. ", d_ptr->name));
+ for (i = 0; (i < 10) && (strcmp(d_ptr->desc[i], "")); i++)
+ text_out(d_ptr->desc[i]);
+ text_out("\n");
+
+ tmp = inkey();
+
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Rescale the wisdom value to a 0 <-> max range
+ */
+int wisdom_scale(int max)
+{
+ int i = p_ptr->stat_ind[A_WIS];
+
+ return (i * max) / 37;
+}
+
+/* Find a god by name */
+int find_god(cptr name)
+{
+ int i;
+
+ for (i = 0; i < max_gods; i++)
+ {
+ /* The name matches */
+ if (streq(deity_info[i].name, name)) return (i);
+ }
+ return -1;
+}
diff --git a/src/h-basic.h b/src/h-basic.h
new file mode 100644
index 00000000..b5ea5d87
--- /dev/null
+++ b/src/h-basic.h
@@ -0,0 +1,25 @@
+/* File: h-basic.h */
+
+#ifndef INCLUDED_H_BASIC_H
+#define INCLUDED_H_BASIC_H
+
+/*
+ * The most basic "include" file.
+ *
+ * This file simply includes other low level header files.
+ */
+
+/* System Configuration */
+#include "h-config.h"
+
+/* System includes/externs */
+#include "h-system.h"
+
+/* Basic types */
+#include "h-type.h"
+
+/* Basic constants and macros */
+#include "h-define.h"
+
+#endif
+
diff --git a/src/h-config.h b/src/h-config.h
new file mode 100644
index 00000000..d18d0746
--- /dev/null
+++ b/src/h-config.h
@@ -0,0 +1,318 @@
+/* File: h-config.h */
+
+#ifndef INCLUDED_H_CONFIG_H
+#define INCLUDED_H_CONFIG_H
+
+/*
+ * Choose the hardware, operating system, and compiler.
+ * Also, choose various "system level" compilation options.
+ * A lot of these definitions take effect in "h-system.h"
+ *
+ * Note that you may find it simpler to define some of these
+ * options in the "Makefile", especially any options describing
+ * what "system" is being used.
+ */
+
+
+/*
+ * no system definitions are needed for 4.3BSD, SUN OS, DG/UX
+ */
+
+/*
+ * OPTION: Compile on a Macintosh (see "A-mac-h" or "A-mac-pch")
+ */
+#ifndef MACINTOSH
+/* #define MACINTOSH */
+#endif
+
+/*
+ * OPTION: Compile on Windows (automatic)
+ */
+#ifndef WINDOWS
+/* #define WINDOWS */
+#endif
+
+#ifdef USE_IBM
+
+ /*
+ * OPTION: Compile on an IBM (automatic)
+ */
+ #ifndef MSDOS
+ #define MSDOS
+ #endif
+
+
+ /* Use the new SVGA code */
+ #ifndef USE_IBM_SVGA
+ #define USE_IBM_SVGA
+ #endif
+
+
+#endif
+
+/*
+ * OPTION: Compile on a SYS III version of UNIX
+ */
+#ifndef SYS_III
+/* #define SYS_III */
+#endif
+
+/*
+ * OPTION: Compile on a SYS V version of UNIX (not Solaris)
+ */
+#ifndef SYS_V
+/* #define SYS_V */
+#endif
+
+/*
+ * OPTION: Compile on a HPUX version of UNIX
+ */
+#ifndef HPUX
+/* #define HPUX */
+#endif
+
+/*
+ * OPTION: Compile on an SGI running IRIX
+ */
+#ifndef SGI
+/* #define SGI */
+#endif
+
+/*
+ * OPTION: Compile on a SunOS machine
+ */
+#ifndef SUNOS
+/* #define SUNOS */
+#endif
+
+/*
+ * OPTION: Compile on a Solaris machine
+ */
+#ifndef SOLARIS
+/* #define SOLARIS */
+#endif
+
+/*
+ * OPTION: Compile on an ultrix/4.2BSD/Dynix/etc. version of UNIX,
+ * Do not define this if you are on any kind of SunOS.
+ */
+#ifndef ULTRIX
+/* #define ULTRIX */
+#endif
+
+
+
+/*
+ * Extract the "SUNOS" flag from the compiler
+ */
+#if defined(sun)
+# ifndef SUNOS
+# define SUNOS
+# endif
+#endif
+
+/*
+ * Extract the "ULTRIX" flag from the compiler
+ */
+#if defined(ultrix) || defined(Pyramid)
+# ifndef ULTRIX
+# define ULTRIX
+# endif
+#endif
+
+/*
+ * Extract the "ATARI" flag from the compiler [cjh]
+ */
+#if defined(__atarist) || defined(__atarist__)
+# ifndef ATARI
+# define ATARI
+# endif
+#endif
+
+/*
+ * Extract the "ACORN" flag from the compiler
+ */
+#ifdef __riscos
+# ifndef ACORN
+# define ACORN
+# endif
+#endif
+
+/*
+ * Extract the "SGI" flag from the compiler
+ */
+#ifdef sgi
+# ifndef SGI
+# define SGI
+# endif
+#endif
+
+/*
+ * Extract the "MSDOS" flag from the compiler
+ */
+#ifdef __MSDOS__
+# ifndef MSDOS
+# define MSDOS
+# endif
+#endif
+
+/*
+ * Extract the "WINDOWS" flag from the compiler
+ */
+#if defined(_Windows) || defined(__WINDOWS__) || \
+ defined(__WIN32__) || defined(WIN32) || \
+ defined(__WINNT__) || defined(__NT__)
+# ifndef WINDOWS
+# define WINDOWS
+# endif
+#endif
+
+
+
+/*
+ * OPTION: Define "L64" if a "long" is 64-bits. See "h-types.h".
+ * The only such platform that angband is ported to is currently
+ * DEC Alpha AXP running OSF/1 (OpenVMS uses 32-bit longs).
+ */
+#if defined(__alpha) && defined(__osf__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__ia64) || defined(__ia64__) || defined(__mips64) || defined(__ppc64__) | defined(__PPC64__) | defined(__powerpc64__) || defined(__64BIT__) || defined(__sparc64__) || defined(__LP64__)
+# define L64
+#endif
+
+
+
+/*
+ * OPTION: set "SET_UID" if the machine is a "multi-user" machine.
+ * This option is used to verify the use of "uids" and "gids" for
+ * various "Unix" calls, and of "pids" for getting a random seed,
+ * and of the "umask()" call for various reasons, and to guess if
+ * the "kill()" function is available, and for permission to use
+ * functions to extract user names and expand "tildes" in filenames.
+ * It is also used for "locking" and "unlocking" the score file.
+ * Basically, SET_UID should *only* be set for "Unix" machines,
+ * or for the "Atari" platform which is Unix-like, apparently
+ */
+#if !defined(MACINTOSH) && !defined(WINDOWS) && \
+ !defined(MSDOS) && !defined(USE_EMX) && \
+ !defined(AMIGA) && !defined(ACORN) && !defined(VM)
+# define SET_UID
+#endif
+
+
+/*
+ * OPTION: Set "USG" for "System V" versions of Unix
+ * This is used to choose a "lock()" function, and to choose
+ * which header files ("string.h" vs "strings.h") to include.
+ * It is also used to allow certain other options, such as options
+ * involving userid's, or multiple users on a single machine, etc.
+ */
+#ifdef SET_UID
+# if defined(SYS_III) || defined(SYS_V) || defined(SOLARIS) || \
+ defined(HPUX) || defined(SGI) || defined(ATARI)
+# ifndef USG
+# define USG
+# endif
+# endif
+#endif
+
+
+/*
+ * Every system seems to use its own symbol as a path separator.
+ * Default to the standard Unix slash, but attempt to change this
+ * for various other systems. Note that any system that uses the
+ * "period" as a separator (i.e. ACORN) will have to pretend that
+ * it uses the slash, and do its own mapping of period <-> slash.
+ * Note that the VM system uses a "flat" directory, and thus uses
+ * the empty string for "PATH_SEP".
+ */
+#undef PATH_SEP
+#define PATH_SEP "/"
+#ifdef MACINTOSH
+# undef PATH_SEP
+# define PATH_SEP ":"
+#endif
+#if defined(WINDOWS) || defined(WINNT)
+# undef PATH_SEP
+# define PATH_SEP "\\"
+#endif
+#if defined(MSDOS) || defined(OS2) || defined(USE_EMX)
+# undef PATH_SEP
+# define PATH_SEP "\\"
+#endif
+#ifdef AMIGA
+# undef PATH_SEP
+# define PATH_SEP "/"
+#endif
+#ifdef __GO32__
+# undef PATH_SEP
+# define PATH_SEP "/"
+#endif
+#ifdef VM
+# undef PATH_SEP
+# define PATH_SEP ""
+#endif
+
+
+/*
+ * The Macintosh allows the use of a "file type" when creating a file
+ */
+#if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON)
+# define FILE_TYPE_TEXT 'TEXT'
+# define FILE_TYPE_DATA 'DATA'
+# define FILE_TYPE_SAVE 'SAVE'
+# define FILE_TYPE(X) (_ftype = (X))
+#else
+# define FILE_TYPE(X) ((void)0)
+#endif
+
+
+/*
+ * OPTION: Hack -- Make sure "strchr()" and "strrchr()" will work
+ */
+#if defined(SYS_III) || defined(SYS_V) || defined(MSDOS)
+# if !defined(__TURBOC__) && !defined(__WATCOMC__)
+# define strchr index
+# define strrchr rindex
+# endif
+#endif
+
+
+/*
+ * OPTION: Define "HAS_STRICMP" only if "stricmp()" exists.
+ * Note that "stricmp()" is not actually used by Angband.
+ */
+/* #define HAS_STRICMP */
+
+/*
+ * Linux has "stricmp()" with a different name
+ */
+#if defined(linux)
+# define HAS_STRICMP
+# define stricmp strcasecmp
+#endif
+
+
+/*
+ * OPTION: Define "HAS_MEMSET" only if "memset()" exists.
+ * Note that the "memset()" routines are used in "z-virt.h"
+ */
+#define HAS_MEMSET
+
+
+/*
+ * OPTION: Define "HAS_USLEEP" only if "usleep()" exists.
+ * Note that this is only relevant for "SET_UID" machines
+ */
+#ifdef SET_UID
+# if !defined(HPUX) && !defined(ULTRIX) && !defined(SOLARIS) && \
+ !defined(SGI) && !defined(ISC)
+# define HAS_USLEEP
+# endif
+#endif
+
+#ifdef USE_IBM
+ #ifndef HAS_USLEEP
+ #define HAS_USLEEP /* Set for gcc (djgpp-v2), TY */
+ #endif
+#endif
+
+#endif
diff --git a/src/h-define.h b/src/h-define.h
new file mode 100644
index 00000000..777a5b8d
--- /dev/null
+++ b/src/h-define.h
@@ -0,0 +1,133 @@
+/* File: h-define.h */
+
+#ifndef INCLUDED_H_DEFINE_H
+#define INCLUDED_H_DEFINE_H
+
+/*
+ * Define some simple constants
+ */
+
+
+/*
+ * Hack -- Define NULL
+ */
+#ifndef NULL
+# ifdef __STDC__
+# define NULL ((void*)0)
+# else
+# define NULL ((char*)0)
+# endif /* __STDC__ */
+#endif /* NULL */
+
+
+/*
+ * Hack -- assist "main-acn.c" XXX XXX XXX
+ */
+#ifdef ACORN
+# define O_RDONLY 0
+# define O_WRONLY 1
+# define O_RDWR 2
+#endif
+
+
+/*
+ * Hack -- force definitions -- see fd_seek()
+ */
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+/*
+ * Hack -- force definitions -- see fd_lock() XXX XXX XXX
+ */
+#ifndef F_UNLCK
+# define F_UNLCK 0
+#endif
+#ifndef F_RDLCK
+# define F_RDLCK 1
+#endif
+#ifndef F_WRLCK
+# define F_WRLCK 2
+#endif
+
+
+/*
+ * The constants "TRUE" and "FALSE"
+ */
+
+#undef TRUE
+#define TRUE 1
+
+#undef FALSE
+#define FALSE 0
+
+
+
+
+/**** Simple "Macros" ****/
+
+/*
+ * Force a character to lowercase/uppercase
+ */
+#define FORCELOWER(A) ((isupper((A))) ? tolower((A)) : (A))
+#define FORCEUPPER(A) ((islower((A))) ? toupper((A)) : (A))
+
+
+/*
+ * Non-typed minimum value macro
+ */
+#undef MIN
+#define MIN(a,b) (((a) > (b)) ? (b) : (a))
+
+/*
+ * Non-typed maximum value macro
+ */
+#undef MAX
+#define MAX(a,b) (((a) < (b)) ? (b) : (a))
+
+/*
+ * Non-typed absolute value macro
+ */
+#undef ABS
+#define ABS(a) (((a) < 0) ? (-(a)) : (a))
+
+/*
+ * Non-typed sign extractor macro
+ */
+#undef SGN
+#define SGN(a) (((a) < 0) ? (-1) : ((a) != 0))
+
+
+/*
+ * Hack -- allow use of "ASCII" and "EBCDIC" for "indexes", "digits",
+ * and "Control-Characters".
+ *
+ * Note that all "index" values must be "lowercase letters", while
+ * all "digits" must be "digits". Control characters can be made
+ * from any legal characters. XXX XXX XXX
+ */
+#ifdef VM
+# define A2I(X) alphatoindex(X)
+# define I2A(X) indextoalpha(X)
+# define D2I(X) ((X) - '0')
+# define I2D(X) ((X) + '0')
+# define KTRL(X) ((X) & 0x1F)
+# define ESCAPE '\033'
+#else
+# define A2I(X) ((X) - 'a')
+# define I2A(X) ((X) + 'a')
+# define D2I(X) ((X) - '0')
+# define I2D(X) ((X) + '0')
+# define KTRL(X) ((X) & 0x1F)
+# define ESCAPE '\033'
+#endif
+
+
+#endif
+
diff --git a/src/h-system.h b/src/h-system.h
new file mode 100644
index 00000000..2d28e45d
--- /dev/null
+++ b/src/h-system.h
@@ -0,0 +1,141 @@
+/* File: h-system.h */
+
+#ifndef INCLUDED_H_SYSTEM_H
+#define INCLUDED_H_SYSTEM_H
+
+/*
+ * Include the basic "system" files.
+ *
+ * Make sure all "system" constants/macros are defined.
+ * Make sure all "system" functions have "extern" declarations.
+ *
+ * This file is a big hack to make other files less of a hack.
+ * This file has been rebuilt -- it may need a little more work.
+ *
+ * It is (very) unlikely that VMS will work without help, primarily
+ * because VMS does not use the "ASCII" character set.
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#if defined(NeXT)
+# include <libc.h>
+#else
+# include <stdlib.h>
+#endif
+
+
+#ifdef SET_UID
+
+# include <sys/types.h>
+
+# if defined(Pyramid) || defined(NeXT) || defined(SUNOS) || \
+ defined(NCR3K) || defined(SUNOS) || defined(ibm032) || \
+ defined(__osf__) || defined(ISC) || defined(SGI) || \
+ defined(linux)
+# include <sys/time.h>
+# endif
+
+# if !defined(SGI) && !defined(ULTRIX)
+# include <sys/timeb.h>
+# endif
+
+#endif
+
+
+#include <time.h>
+
+
+
+#ifdef MACINTOSH
+# include <unix.h>
+#endif
+
+#if defined(WINDOWS) || defined(MSDOS) || defined(USE_EMX)
+# include <io.h>
+#endif
+
+#if !defined(MACINTOSH) && !defined(AMIGA) && \
+ !defined(ACORN) && !defined(VM) && !defined(__MWERKS__)
+# if defined(__TURBOC__) || defined(__WATCOMC__)
+# include <mem.h>
+# else
+# include <memory.h>
+# endif
+#endif
+
+
+#if !defined(NeXT) && !defined(__MWERKS__) && !defined(ACORN)
+# include <fcntl.h>
+#endif
+
+
+#ifdef SET_UID
+
+# ifndef USG
+# include <sys/param.h>
+# include <sys/file.h>
+# endif
+
+# ifdef linux
+# include <sys/file.h>
+# endif
+
+# include <pwd.h>
+
+# include <unistd.h>
+
+# include <sys/stat.h>
+
+# if defined(SOLARIS)
+# include <netdb.h>
+# endif
+
+#endif
+
+#ifdef __DJGPP__
+#include <unistd.h>
+#endif /* __DJGPP__ */
+
+#ifdef SET_UID
+
+#ifdef USG
+# include <string.h>
+#else
+# include <strings.h>
+# ifndef strstr
+extern char *strstr();
+# endif
+# ifndef strchr
+extern char *strchr();
+# endif
+# ifndef strrchr
+extern char *strrchr();
+# endif
+#endif
+
+#else
+
+# include <string.h>
+
+#endif
+
+
+
+#if !defined(linux) && !defined(__MWERKS__) && !defined(ACORN)
+extern long atol();
+#endif
+
+
+#include <stdarg.h>
+
+
+#endif
+
+/* There was a bug introduced in 10.4.11; working around it */
+#ifdef __APPLE__
+#define GETLOGIN_BROKEN
+#endif
diff --git a/src/h-type.h b/src/h-type.h
new file mode 100644
index 00000000..eb865892
--- /dev/null
+++ b/src/h-type.h
@@ -0,0 +1,185 @@
+/* File: h-type.h */
+
+#ifndef INCLUDED_H_TYPE_H
+#define INCLUDED_H_TYPE_H
+
+/*
+ * Basic "types".
+ *
+ * Note the attempt to make all basic types have 4 letters.
+ * This improves readibility and standardizes the code.
+ *
+ * Likewise, all complex types are at least 4 letters.
+ * Thus, almost every three letter word is a legal variable.
+ * But beware of certain reserved words ('for' and 'if' and 'do').
+ *
+ * Note that the type used in structures for bit flags should be uint.
+ * As long as these bit flags are sequential, they will be space smart.
+ *
+ * Note that on some machines, apparently "signed char" is illegal.
+ *
+ * It must be true that char/byte takes exactly 1 byte
+ * It must be true that sind/uind takes exactly 2 bytes
+ * It must be true that sbig/ubig takes exactly 4 bytes
+ *
+ * On Sparc's, a sint takes 4 bytes (2 is legal)
+ * On Sparc's, a uint takes 4 bytes (2 is legal)
+ * On Sparc's, a long takes 4 bytes (8 is legal)
+ * On Sparc's, a huge takes 4 bytes (8 is legal)
+ * On Sparc's, a vptr takes 4 bytes (8 is legal)
+ * On Sparc's, a real takes 8 bytes (4 is legal)
+ *
+ * Note that some files have already been included by "h-include.h"
+ * These include <stdio.h> and <sys/types>, which define some types
+ * In particular, uint is defined so we do not have to define it
+ *
+ * Also, see <limits.h> for min/max values for sind, uind, long, huge
+ * (SHRT_MIN, SHRT_MAX, USHRT_MAX, LONG_MIN, LONG_MAX, ULONG_MAX)
+ * These limits should be verified and coded into "h-constant.h".
+ */
+
+
+
+/*** Special 4 letter names for some standard types ***/
+
+
+/* A standard pointer (to "void" because ANSI C says so) */
+typedef void *vptr;
+
+/* A simple pointer (to unmodifiable strings) */
+typedef const char *cptr;
+typedef char *mcptr;
+
+
+/* Since float's are silly, hard code real numbers as doubles */
+typedef double real;
+
+
+/* Error codes for function return values */
+/* Success = 0, Failure = -N, Problem = +N */
+typedef int errr;
+
+
+/*
+ * Hack -- prevent problems with non-MACINTOSH
+ */
+#undef uint
+#define uint uint_hack
+
+/*
+ * Hack -- prevent problems with MSDOS and WINDOWS
+ */
+#undef huge
+#define huge huge_hack
+
+/*
+ * Hack -- prevent problems with AMIGA
+ */
+#undef byte
+#define byte byte_hack
+
+/*
+ * Hack -- prevent problems with C++
+ */
+#undef bool
+#define bool bool_hack
+
+
+/* Note that "signed char" is not always "defined" */
+/* So always use "s16b" to hold small signed values */
+/* A signed byte of memory */
+/* typedef signed char syte; */
+
+/* Note that unsigned values can cause math problems */
+/* An unsigned byte of memory */
+typedef unsigned char byte;
+
+/* Note that a bool is smaller than a full "int" */
+/* Simple True/False type */
+typedef char bool;
+
+
+/* A signed, standard integer (at least 2 bytes) */
+typedef int sint;
+
+/* An unsigned, "standard" integer (often pre-defined) */
+typedef unsigned int uint;
+
+
+/* The largest possible signed integer (pre-defined) */
+/* typedef long long; */
+
+/* The largest possible unsigned integer */
+typedef unsigned long huge;
+
+
+/* Signed/Unsigned 16 bit value */
+typedef signed short s16b;
+typedef unsigned short u16b;
+
+/* Signed/Unsigned 32 bit value */
+#ifdef L64 /* 64 bit longs */
+typedef signed int s32b;
+typedef unsigned int u32b;
+#else
+typedef signed long s32b;
+typedef unsigned long u32b;
+#endif
+
+
+/*** Pointers to all the basic types defined above ***/
+
+typedef real *real_ptr;
+typedef errr *errr_ptr;
+typedef char *char_ptr;
+typedef byte *byte_ptr;
+typedef bool *bool_ptr;
+typedef sint *sint_ptr;
+typedef uint *uint_ptr;
+typedef long *long_ptr;
+typedef huge *huge_ptr;
+typedef s16b *s16b_ptr;
+typedef u16b *u16b_ptr;
+typedef s32b *s32b_ptr;
+typedef u32b *u32b_ptr;
+typedef vptr *vptr_ptr;
+typedef cptr *cptr_ptr;
+
+
+
+/*** Pointers to Functions with simple return types and any args ***/
+
+typedef void (*func_void)();
+typedef errr (*func_errr)();
+typedef char (*func_char)();
+typedef byte (*func_byte)();
+typedef bool (*func_bool)();
+typedef sint (*func_sint)();
+typedef uint (*func_uint)();
+typedef real (*func_real)();
+typedef vptr (*func_vptr)();
+typedef cptr (*func_cptr)();
+
+
+
+/*** Pointers to Functions of special types (for various purposes) ***/
+
+/* A generic function takes a user data and a special data */
+typedef errr (*func_gen)(vptr, vptr);
+
+/* An equality testing function takes two things to compare (bool) */
+typedef bool (*func_eql)(vptr, vptr);
+
+/* A comparison function takes two things and to compare (-1,0,+1) */
+typedef sint (*func_cmp)(vptr, vptr);
+
+/* A hasher takes a thing (and a max hash size) to hash (0 to siz - 1) */
+typedef uint (*func_hsh)(vptr, uint);
+
+/* A key extractor takes a thing and returns (a pointer to) some key */
+typedef vptr (*func_key)(vptr);
+
+
+
+#endif
+
diff --git a/src/help.c b/src/help.c
new file mode 100644
index 00000000..73475387
--- /dev/null
+++ b/src/help.c
@@ -0,0 +1,23 @@
+/* File: help.c */
+
+/* Purpose: ingame help */
+/*
+ * Actually this is now handled by lua,
+ * I'll remove this file when I feel un-lazy
+ */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Driver for the context-sensitive help system
+ */
+void ingame_help(bool enable)
+{}
diff --git a/src/init1.c b/src/init1.c
new file mode 100644
index 00000000..d7d90672
--- /dev/null
+++ b/src/init1.c
@@ -0,0 +1,12170 @@
+/* File: init1.c */
+
+/* Purpose: Initialization (part 1) -BEN- */
+
+#include "angband.h"
+
+
+/*
+ * This file is used to initialize various variables and arrays for the
+ * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass
+ * the common limitation of "read()" and "write()" to only 32767 bytes
+ * at a time.
+ *
+ * Several of the arrays for Angband are built from "template" files in
+ * the "lib/file" directory, from which quick-load binary "image" files
+ * are constructed whenever they are not present in the "lib/data"
+ * directory, or if those files become obsolete, if we are allowed.
+ *
+ * Warning -- the "ascii" file parsers use a minor hack to collect the
+ * name and text information in a single pass. Thus, the game will not
+ * be able to load any template file with more than 20K of names or 60K
+ * of text, even though technically, up to 64K should be legal.
+ *
+ * Note that if "ALLOW_TEMPLATES" is not defined, then a lot of the code
+ * in this file is compiled out, and the game will not run unless valid
+ * "binary template files" already exist in "lib/data". Thus, one can
+ * compile Angband with ALLOW_TEMPLATES defined, run once to create the
+ * "*.raw" files in "lib/data", and then quit, and recompile without
+ * defining ALLOW_TEMPLATES, which will both save 20K and prevent people
+ * from changing the ascii template files in potentially dangerous ways.
+ *
+ * The code could actually be removed and placed into a "stand-alone"
+ * program, but that feels a little silly, especially considering some
+ * of the platforms that we currently support.
+ */
+
+
+#ifdef ALLOW_TEMPLATES
+
+
+/*** Helper arrays for parsing ascii template files ***/
+
+/*
+ * Monster Blow Methods
+ */
+static cptr r_info_blow_method[] =
+{
+ "*",
+ "HIT",
+ "TOUCH",
+ "PUNCH",
+ "KICK",
+ "CLAW",
+ "BITE",
+ "STING",
+ "XXX1",
+ "BUTT",
+ "CRUSH",
+ "ENGULF",
+ "CHARGE",
+ "CRAWL",
+ "DROOL",
+ "SPIT",
+ "EXPLODE",
+ "GAZE",
+ "WAIL",
+ "SPORE",
+ "XXX4",
+ "BEG",
+ "INSULT",
+ "MOAN",
+ "SHOW",
+ NULL
+};
+
+
+/*
+ * Monster Blow Effects
+ */
+static cptr r_info_blow_effect[] =
+{
+ "*",
+ "HURT",
+ "POISON",
+ "UN_BONUS",
+ "UN_POWER",
+ "EAT_GOLD",
+ "EAT_ITEM",
+ "EAT_FOOD",
+ "EAT_LITE",
+ "ACID",
+ "ELEC",
+ "FIRE",
+ "COLD",
+ "BLIND",
+ "CONFUSE",
+ "TERRIFY",
+ "PARALYZE",
+ "LOSE_STR",
+ "LOSE_INT",
+ "LOSE_WIS",
+ "LOSE_DEX",
+ "LOSE_CON",
+ "LOSE_CHR",
+ "LOSE_ALL",
+ "SHATTER",
+ "EXP_10",
+ "EXP_20",
+ "EXP_40",
+ "EXP_80",
+ "DISEASE",
+ "TIME",
+ "INSANITY",
+ "HALLU",
+ "PARASITE",
+ "ABOMINATION",
+ NULL
+};
+
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags1[] =
+{
+ "UNIQUE",
+ "QUESTOR",
+ "MALE",
+ "FEMALE",
+ "CHAR_CLEAR",
+ "CHAR_MULTI",
+ "ATTR_CLEAR",
+ "ATTR_MULTI",
+ "FORCE_DEPTH",
+ "FORCE_MAXHP",
+ "FORCE_SLEEP",
+ "FORCE_EXTRA",
+ "FRIEND",
+ "FRIENDS",
+ "ESCORT",
+ "ESCORTS",
+ "NEVER_BLOW",
+ "NEVER_MOVE",
+ "RAND_25",
+ "RAND_50",
+ "ONLY_GOLD",
+ "ONLY_ITEM",
+ "DROP_60",
+ "DROP_90",
+ "DROP_1D2",
+ "DROP_2D2",
+ "DROP_3D2",
+ "DROP_4D2",
+ "DROP_GOOD",
+ "DROP_GREAT",
+ "DROP_USEFUL",
+ "DROP_CHOSEN"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags2[] =
+{
+ "STUPID",
+ "SMART",
+ "CAN_SPEAK",
+ "REFLECTING",
+ "INVISIBLE",
+ "COLD_BLOOD",
+ "EMPTY_MIND",
+ "WEIRD_MIND",
+ "DEATH_ORB",
+ "REGENERATE",
+ "SHAPECHANGER",
+ "ATTR_ANY",
+ "POWERFUL",
+ "ELDRITCH_HORROR",
+ "AURA_FIRE",
+ "AURA_ELEC",
+ "OPEN_DOOR",
+ "BASH_DOOR",
+ "PASS_WALL",
+ "KILL_WALL",
+ "MOVE_BODY",
+ "KILL_BODY",
+ "TAKE_ITEM",
+ "KILL_ITEM",
+ "BRAIN_1",
+ "BRAIN_2",
+ "BRAIN_3",
+ "BRAIN_4",
+ "BRAIN_5",
+ "BRAIN_6",
+ "BRAIN_7",
+ "BRAIN_8"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags3[] =
+{
+ "ORC",
+ "TROLL",
+ "GIANT",
+ "DRAGON",
+ "DEMON",
+ "UNDEAD",
+ "EVIL",
+ "ANIMAL",
+ "THUNDERLORD",
+ "GOOD",
+ "AURA_COLD", /* TODO: Implement aura_cold */
+ "NONLIVING",
+ "HURT_LITE",
+ "HURT_ROCK",
+ "SUSCEP_FIRE",
+ "SUSCEP_COLD",
+ "IM_ACID",
+ "IM_ELEC",
+ "IM_FIRE",
+ "IM_COLD",
+ "IM_POIS",
+ "RES_TELE",
+ "RES_NETH",
+ "RES_WATE",
+ "RES_PLAS",
+ "RES_NEXU",
+ "RES_DISE",
+ "UNIQUE_4",
+ "NO_FEAR",
+ "NO_STUN",
+ "NO_CONF",
+ "NO_SLEEP"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags4[] =
+{
+ "SHRIEK",
+ "MULTIPLY",
+ "S_ANIMAL",
+ "ROCKET",
+ "ARROW_1",
+ "ARROW_2",
+ "ARROW_3",
+ "ARROW_4",
+ "BR_ACID",
+ "BR_ELEC",
+ "BR_FIRE",
+ "BR_COLD",
+ "BR_POIS",
+ "BR_NETH",
+ "BR_LITE",
+ "BR_DARK",
+ "BR_CONF",
+ "BR_SOUN",
+ "BR_CHAO",
+ "BR_DISE",
+ "BR_NEXU",
+ "BR_TIME",
+ "BR_INER",
+ "BR_GRAV",
+ "BR_SHAR",
+ "BR_PLAS",
+ "BR_WALL",
+ "BR_MANA",
+ "BA_NUKE",
+ "BR_NUKE",
+ "BA_CHAO",
+ "BR_DISI",
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags5[] =
+{
+ "BA_ACID",
+ "BA_ELEC",
+ "BA_FIRE",
+ "BA_COLD",
+ "BA_POIS",
+ "BA_NETH",
+ "BA_WATE",
+ "BA_MANA",
+ "BA_DARK",
+ "DRAIN_MANA",
+ "MIND_BLAST",
+ "BRAIN_SMASH",
+ "CAUSE_1",
+ "CAUSE_2",
+ "CAUSE_3",
+ "CAUSE_4",
+ "BO_ACID",
+ "BO_ELEC",
+ "BO_FIRE",
+ "BO_COLD",
+ "BO_POIS",
+ "BO_NETH",
+ "BO_WATE",
+ "BO_MANA",
+ "BO_PLAS",
+ "BO_ICEE",
+ "MISSILE",
+ "SCARE",
+ "BLIND",
+ "CONF",
+ "SLOW",
+ "HOLD"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags6[] =
+{
+ "HASTE",
+ "HAND_DOOM",
+ "HEAL",
+ "S_ANIMALS",
+ "BLINK",
+ "TPORT",
+ "TELE_TO",
+ "TELE_AWAY",
+ "TELE_LEVEL",
+ "DARKNESS",
+ "TRAPS",
+ "FORGET",
+ "ANIM_DEAD", /* ToDo: Implement ANIM_DEAD */
+ "S_BUG",
+ "S_RNG",
+ "S_THUNDERLORD", /* DG : Summon Thunderlord */
+ "S_KIN",
+ "S_HI_DEMON",
+ "S_MONSTER",
+ "S_MONSTERS",
+ "S_ANT",
+ "S_SPIDER",
+ "S_HOUND",
+ "S_HYDRA",
+ "S_ANGEL",
+ "S_DEMON",
+ "S_UNDEAD",
+ "S_DRAGON",
+ "S_HI_UNDEAD",
+ "S_HI_DRAGON",
+ "S_WRAITH",
+ "S_UNIQUE"
+};
+
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags7[] =
+{
+ "AQUATIC",
+ "CAN_SWIM",
+ "CAN_FLY",
+ "FRIENDLY",
+ "PET",
+ "MORTAL",
+ "SPIDER",
+ "NAZGUL",
+ "DG_CURSE",
+ "POSSESSOR",
+ "NO_DEATH",
+ "NO_TARGET",
+ "AI_ANNOY",
+ "AI_SPECIAL",
+ "NEUTRAL",
+ "DROP_ART",
+ "DROP_RANDART",
+ "AI_PLAYER",
+ "NO_THEFT",
+ "SPIRIT",
+ "IM_MELEE",
+ "XXX7X21",
+ "XXX7X22",
+ "XXX7X23",
+ "XXX7X24",
+ "XXX7X25",
+ "XXX7X26",
+ "XXX7X27",
+ "XXX7X28",
+ "XXX7X29",
+ "XXX7X30",
+ "XXX7X31",
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags8[] =
+{
+ "WILD_ONLY",
+ "WILD_TOWN",
+ "XXX8X02",
+ "WILD_SHORE",
+ "WILD_OCEAN",
+ "WILD_WASTE",
+ "WILD_WOOD",
+ "WILD_VOLCANO",
+ "XXX8X08",
+ "WILD_MOUNTAIN",
+ "WILD_GRASS",
+ "NO_CUT",
+ "CTHANGBAND",
+ "XXX8X13",
+ "ZANGBAND",
+ "JOKEANGBAND",
+ "BASEANGBAND",
+ "XXX8X17",
+ "XXX8X18",
+ "XXX8X19",
+ "XXX8X20",
+ "XXX8X21",
+ "XXX8X22",
+ "XXX8X23",
+ "XXX8X24",
+ "XXX8X25",
+ "XXX8X26",
+ "XXX8X27",
+ "XXX8X28",
+ "XXX8X29",
+ "WILD_SWAMP", /* ToDo: Implement Swamp */
+ "WILD_TOO",
+};
+
+
+/*
+ * Monster race flags - Drops
+ */
+static cptr r_info_flags9[] =
+{
+ "DROP_CORPSE",
+ "DROP_SKELETON",
+ "HAS_LITE",
+ "MIMIC",
+ "HAS_EGG",
+ "IMPRESED",
+ "SUSCEP_ACID",
+ "SUSCEP_ELEC",
+ "SUSCEP_POIS",
+ "KILL_TREES",
+ "WYRM_PROTECT",
+ "DOPPLEGANGER",
+ "ONLY_DEPTH",
+ "SPECIAL_GENE",
+ "NEVER_GENE",
+ "XXX9X15",
+ "XXX9X16",
+ "XXX9X17",
+ "XXX9X18",
+ "XXX9X19",
+ "XXX9X20",
+ "XXX9X21",
+ "XXX9X22",
+ "XXX9X23",
+ "XXX9X24",
+ "XXX9X25",
+ "XXX9X26",
+ "XXX9X27",
+ "XXX9X28",
+ "XXX9X29",
+ "XXX9X30",
+ "XXX9X31",
+};
+
+
+/*
+ * Object flags
+ */
+cptr k_info_flags1[] =
+{
+ "STR",
+ "INT",
+ "WIS",
+ "DEX",
+ "CON",
+ "CHR",
+ "MANA",
+ "SPELL",
+ "STEALTH",
+ "SEARCH",
+ "INFRA",
+ "TUNNEL",
+ "SPEED",
+ "BLOWS",
+ "CHAOTIC",
+ "VAMPIRIC",
+ "SLAY_ANIMAL",
+ "SLAY_EVIL",
+ "SLAY_UNDEAD",
+ "SLAY_DEMON",
+ "SLAY_ORC",
+ "SLAY_TROLL",
+ "SLAY_GIANT",
+ "SLAY_DRAGON",
+ "KILL_DRAGON",
+ "VORPAL",
+ "IMPACT",
+ "BRAND_POIS",
+ "BRAND_ACID",
+ "BRAND_ELEC",
+ "BRAND_FIRE",
+ "BRAND_COLD"
+};
+
+/*
+ * Object flags
+ */
+cptr k_info_flags2[] =
+{
+ "SUST_STR",
+ "SUST_INT",
+ "SUST_WIS",
+ "SUST_DEX",
+ "SUST_CON",
+ "SUST_CHR",
+ "INVIS",
+ "LIFE",
+ "IM_ACID",
+ "IM_ELEC",
+ "IM_FIRE",
+ "IM_COLD",
+ "SENS_FIRE",
+ "REFLECT",
+ "FREE_ACT",
+ "HOLD_LIFE",
+ "RES_ACID",
+ "RES_ELEC",
+ "RES_FIRE",
+ "RES_COLD",
+ "RES_POIS",
+ "RES_FEAR",
+ "RES_LITE",
+ "RES_DARK",
+ "RES_BLIND",
+ "RES_CONF",
+ "RES_SOUND",
+ "RES_SHARDS",
+ "RES_NETHER",
+ "RES_NEXUS",
+ "RES_CHAOS",
+ "RES_DISEN"
+};
+
+/*
+ * Trap flags
+ */
+cptr k_info_flags2_trap[] =
+{
+ "AUTOMATIC_5",
+ "AUTOMATIC_99",
+ "KILL_GHOST",
+ "TELEPORT_TO",
+ "ONLY_DRAGON",
+ "ONLY_DEMON",
+ "XXX3",
+ "XXX3",
+ "ONLY_ANIMAL",
+ "ONLY_UNDEAD",
+ "ONLY_EVIL",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+ "XXX3",
+};
+
+
+/*
+ * Object flags
+ */
+cptr k_info_flags3[] =
+{
+ "SH_FIRE",
+ "SH_ELEC",
+ "AUTO_CURSE",
+ "DECAY",
+ "NO_TELE",
+ "NO_MAGIC",
+ "WRAITH",
+ "TY_CURSE",
+ "EASY_KNOW",
+ "HIDE_TYPE",
+ "SHOW_MODS",
+ "INSTA_ART",
+ "FEATHER",
+ "LITE1",
+ "SEE_INVIS",
+ "NORM_ART",
+ "SLOW_DIGEST",
+ "REGEN",
+ "XTRA_MIGHT",
+ "XTRA_SHOTS",
+ "IGNORE_ACID",
+ "IGNORE_ELEC",
+ "IGNORE_FIRE",
+ "IGNORE_COLD",
+ "ACTIVATE",
+ "DRAIN_EXP",
+ "TELEPORT",
+ "AGGRAVATE",
+ "BLESSED",
+ "CURSED",
+ "HEAVY_CURSE",
+ "PERMA_CURSE"
+};
+
+/*
+ * Object flags
+ */
+cptr k_info_flags4[] =
+{
+ "NEVER_BLOW",
+ "PRECOGNITION",
+ "BLACK_BREATH",
+ "RECHARGE",
+ "FLY",
+ "DG_CURSE",
+ "COULD2H",
+ "MUST2H",
+ "LEVELS",
+ "CLONE",
+ "SPECIAL_GENE",
+ "CLIMB",
+ "FAST_CAST",
+ "CAPACITY",
+ "CHARGING",
+ "CHEAPNESS",
+ "FOUNTAIN",
+ "ANTIMAGIC_50",
+ "ANTIMAGIC_30",
+ "ANTIMAGIC_20",
+ "ANTIMAGIC_10",
+ "EASY_USE",
+ "IM_NETHER",
+ "RECHARGED",
+ "ULTIMATE",
+ "AUTO_ID",
+ "LITE2",
+ "LITE3",
+ "FUEL_LITE",
+ "ART_EXP",
+ "CURSE_NO_DROP",
+ "NO_RECHARGE"
+};
+
+/*
+ * Object flags
+ */
+cptr k_info_flags5[] =
+{
+ "TEMPORARY",
+ "DRAIN_MANA",
+ "DRAIN_HP",
+ "KILL_DEMON",
+ "KILL_UNDEAD",
+ "CRIT",
+ "ATTR_MULTI",
+ "WOUNDING",
+ "FULL_NAME",
+ "LUCK",
+ "IMMOVABLE",
+ "SPELL_CONTAIN",
+ "RES_MORGUL",
+ "ACTIVATE_NO_WIELD",
+ "MAGIC_BREATH",
+ "WATER_BREATH",
+ "WIELD_CAST",
+ "XXX8X17",
+ "XXX8X18",
+ "XXX8X19",
+ "XXX8X20",
+ "XXX8X21",
+ "XXX8X22",
+ "XXX8X23",
+ "XXX8X24",
+ "XXX8X25",
+ "XXX8X26",
+ "XXX8X27",
+ "XXX8X28",
+ "XXX8X29",
+ "XXX8X02",
+ "XXX8X22",
+};
+
+/*
+ * ESP flags
+ */
+cptr esp_flags[] =
+{
+ "ESP_ORC",
+ "ESP_TROLL",
+ "ESP_DRAGON",
+ "ESP_GIANT",
+ "ESP_DEMON",
+ "ESP_UNDEAD",
+ "ESP_EVIL",
+ "ESP_ANIMAL",
+ "ESP_THUNDERLORD",
+ "ESP_GOOD",
+ "ESP_NONLIVING",
+ "ESP_UNIQUE",
+ "ESP_SPIDER",
+ "XXX8X02",
+ "XXX8X02",
+ "XXX8X02",
+ "XXX8X02",
+ "XXX8X17",
+ "XXX8X18",
+ "XXX8X19",
+ "XXX8X20",
+ "XXX8X21",
+ "XXX8X22",
+ "XXX8X23",
+ "XXX8X24",
+ "XXX8X25",
+ "XXX8X26",
+ "XXX8X27",
+ "XXX8X28",
+ "XXX8X29",
+ "XXX8X02",
+ "ESP_ALL",
+};
+
+/* Specially handled properties for ego-items */
+
+static cptr ego_flags[] =
+{
+ "SUSTAIN",
+ "OLD_RESIST",
+ "ABILITY",
+ "R_ELEM",
+ "R_LOW",
+ "R_HIGH",
+ "R_ANY",
+ "R_DRAGON",
+ "SLAY_WEAP",
+ "DAM_DIE",
+ "DAM_SIZE",
+ "PVAL_M1",
+ "PVAL_M2",
+ "PVAL_M3",
+ "PVAL_M5",
+ "AC_M1",
+ "AC_M2",
+ "AC_M3",
+ "AC_M5",
+ "TH_M1",
+ "TH_M2",
+ "TH_M3",
+ "TH_M5",
+ "TD_M1",
+ "TD_M2",
+ "TD_M3",
+ "TD_M5",
+ "R_P_ABILITY",
+ "R_STAT",
+ "R_STAT_SUST",
+ "R_IMMUNITY",
+ "LIMIT_BLOWS"
+};
+
+/*
+ * Feature flags
+ */
+static cptr f_info_flags1[] =
+{
+ "NO_WALK",
+ "NO_VISION",
+ "CAN_LEVITATE",
+ "CAN_PASS",
+ "FLOOR",
+ "WALL",
+ "PERMANENT",
+ "CAN_FLY",
+ "REMEMBER",
+ "NOTICE",
+ "DONT_NOTICE_RUNNING",
+ "CAN_RUN",
+ "DOOR",
+ "SUPPORT_LIGHT",
+ "CAN_CLIMB",
+ "TUNNELABLE",
+ "WEB",
+ "ATTR_MULTI",
+ "SUPPORT_GROWTH",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Dungeon flags
+ */
+static cptr d_info_flags1[] =
+{
+ "PRINCIPAL",
+ "MAZE",
+ "SMALLEST",
+ "SMALL",
+ "BIG",
+ "NO_DOORS",
+ "WATER_RIVER",
+ "LAVA_RIVER",
+ "WATER_RIVERS",
+ "LAVA_RIVERS",
+ "CAVE",
+ "CAVERN",
+ "NO_UP",
+ "HOT",
+ "COLD",
+ "FORCE_DOWN",
+ "FORGET",
+ "NO_DESTROY",
+ "SAND_VEIN",
+ "CIRCULAR_ROOMS",
+ "EMPTY",
+ "DAMAGE_FEAT",
+ "FLAT",
+ "TOWER",
+ "RANDOM_TOWNS",
+ "DOUBLE",
+ "LIFE_LEVEL",
+ "EVOLVE",
+ "ADJUST_LEVEL_1",
+ "ADJUST_LEVEL_2",
+ "NO_RECALL",
+ "NO_STREAMERS"
+};
+
+static cptr d_info_flags2[] =
+{
+ "ADJUST_LEVEL_1_2",
+ "NO_SHAFT",
+ "ADJUST_LEVEL_PLAYER",
+ "NO_TELEPORT",
+ "ASK_LEAVE",
+ "NO_STAIR",
+ "SPECIAL",
+ "NO_NEW_MONSTER",
+ "DESC",
+ "NO_GENO",
+ "NO_BREATH",
+ "WATER_BREATH",
+ "ELVEN",
+ "DWARVEN",
+ "NO_EASY_MOVE",
+ "NO_RECALL_OUT",
+ "DESC_ALWAYS",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Trap flags
+ */
+static cptr t_info_flags[] =
+{
+ "CHEST",
+ "DOOR",
+ "FLOOR",
+ "XXX4",
+ "XXX5",
+ "XXX6",
+ "XXX7",
+ "XXX8",
+ "XXX9",
+ "XXX10",
+ "XXX11",
+ "XXX12",
+ "XXX13",
+ "XXX14",
+ "XXX15",
+ "XXX16",
+ "LEVEL1",
+ "LEVEL2",
+ "LEVEL3",
+ "LEVEL4",
+ "XXX21",
+ "XXX22",
+ "XXX23",
+ "XXX24",
+ "XXX25",
+ "XXX26",
+ "XXX27",
+ "XXX28",
+ "XXX29",
+ "XXX30",
+ "XXX31",
+ "XXX32"
+};
+
+/*
+ * Wilderness feature flags
+ */
+static cptr wf_info_flags1[] =
+{
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Stores flags
+ */
+static cptr st_info_flags1[] =
+{
+ "DEPEND_LEVEL",
+ "SHALLOW_LEVEL",
+ "MEDIUM_LEVEL",
+ "DEEP_LEVEL",
+ "RARE",
+ "VERY_RARE",
+ "COMMON",
+ "ALL_ITEM",
+ "RANDOM",
+ "FORCE_LEVEL",
+ "MUSEUM",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Race flags
+ */
+cptr rp_info_flags1[] =
+{
+ "EXPERIMENTAL",
+ "XXX",
+ "RESIST_BLACK_BREATH",
+ "NO_STUN",
+ "XTRA_MIGHT_BOW",
+ "XTRA_MIGHT_XBOW",
+ "XTRA_MIGHT_SLING",
+ "AC_LEVEL",
+ "HURT_LITE",
+ "VAMPIRE",
+ "UNDEAD",
+ "NO_CUT",
+ "CORRUPT",
+ "NO_FOOD",
+ "NO_GOD",
+ "XXX",
+ "ELF",
+ "SEMI_WRAITH",
+ "NO_SUBRACE_CHANGE",
+ "XXX",
+ "XXX",
+ "MOLD_FRIEND",
+ "GOD_FRIEND",
+ "XXX",
+ "INNATE_SPELLS",
+ "XXX",
+ "XXX",
+ "EASE_STEAL",
+ "XXX",
+ "XXX",
+ "XXX",
+ "XXX"
+};
+
+/*
+ * Race flags
+ */
+cptr rp_info_flags2[] =
+{
+ "XXX",
+ "ASTRAL",
+ "XXX",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/* Skill flags */
+static cptr s_info_flags1[] =
+{
+ "HIDDEN",
+ "AUTO_HIDE",
+ "RANDOM_GAIN",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1",
+ "XXX1"
+};
+
+/*
+ * Dungeon effect types (used in E:damage:frequency:type entry in d_info.txt)
+ */
+static struct
+{
+cptr name;
+int feat;
+}
+d_info_dtypes[] =
+{
+ {"ELEC", GF_ELEC},
+ {"POISON", GF_POIS},
+ {"ACID", GF_ACID},
+ {"COLD", GF_COLD},
+ {"FIRE", GF_FIRE},
+ {"MISSILE", GF_MISSILE},
+ {"ARROW", GF_ARROW},
+ {"PLASMA", GF_PLASMA},
+ {"WATER", GF_WATER},
+ {"LITE", GF_LITE},
+ {"DARK", GF_DARK},
+ {"LITE_WEAK", GF_LITE_WEAK},
+ {"LITE_DARK", GF_DARK_WEAK},
+ {"SHARDS", GF_SHARDS},
+ {"SOUND", GF_SOUND},
+ {"CONFUSION", GF_CONFUSION},
+ {"FORCE", GF_FORCE},
+ {"INERTIA", GF_INERTIA},
+ {"MANA", GF_MANA},
+ {"METEOR", GF_METEOR},
+ {"ICE", GF_ICE},
+ {"CHAOS", GF_CHAOS},
+ {"NETHER", GF_NETHER},
+ {"DISENCHANT", GF_DISENCHANT},
+ {"NEXUS", GF_NEXUS},
+ {"TIME", GF_TIME},
+ {"GRAVITY", GF_GRAVITY},
+ {"ROCKET", GF_ROCKET},
+ {"NUKE", GF_NUKE},
+ {"HOLY_FIRE", GF_HOLY_FIRE},
+ {"HELL_FIRE", GF_HELL_FIRE},
+ {"DISINTEGRATE", GF_DISINTEGRATE},
+ {"DESTRUCTION", GF_DESTRUCTION},
+ {"RAISE", GF_RAISE},
+ {NULL, 0}
+};
+
+/* Essence names for al_info.txt */
+static const char *essence_names[] =
+{
+ "No name here", /* can't be matched, sscanf stops at spaces */
+ "POISON",
+ "EXPLOSION",
+ "TELEPORT",
+ "COLD",
+ "FIRE",
+ "ACID",
+ "LIFE",
+ "CONFUSION",
+ "LITE",
+ "CHAOS",
+ "TIME",
+ "MAGIC",
+ "EXTRALIFE",
+ "DARKNESS",
+ "KNOWLEDGE",
+ "FORCE",
+ "LIGHTNING",
+ "MANA",
+ ""
+};
+static const char *activation_names[] =
+{
+ "NO_ACTIVATION", /* 0*/
+ "SUNLIGHT", /* 1*/
+ "BO_MISS_1", /* 2*/
+ "BA_POIS_1", /* 3*/
+ "BO_ELEC_1", /* 4*/
+ "BO_ACID_1", /* 5*/
+ "BO_COLD_1", /* 6*/
+ "BO_FIRE_1", /* 7*/
+ "BA_COLD_1", /* 8*/
+ "BA_FIRE_1", /* 9*/
+ "DRAIN_1", /* 10*/
+ "BA_COLD_2", /* 11*/
+ "BA_ELEC_2", /* 12*/
+ "DRAIN_2", /* 13*/
+ "VAMPIRE_1", /* 14*/
+ "BO_MISS_2", /* 15*/
+ "BA_FIRE_2", /* 16*/
+ "BA_COLD_3", /* 17*/
+ "BA_ELEC_3", /* 18*/
+ "WHIRLWIND", /* 19*/
+ "VAMPIRE_2", /* 20*/
+ "CALL_CHAOS", /* 21*/
+ "ROCKET", /* 22*/
+ "DISP_EVIL", /* 23*/
+ "BA_MISS_3", /* 24*/
+ "DISP_GOOD", /* 25*/
+ "GILGALAD", /* 26*/
+ "CELEBRIMBOR", /* 27*/
+ "SKULLCLEAVER", /* 28*/
+ "HARADRIM", /* 29*/
+ "FUNDIN", /* 30*/
+ "EOL", /* 31*/
+ "UMBAR", /* 32*/
+ "NUMENOR", /* 33*/
+ "KNOWLEDGE", /* 34*/
+ "UNDEATH", /* 35*/
+ "THRAIN", /* 36*/
+ "BARAHIR", /* 37*/
+ "TULKAS", /* 38*/
+ "NARYA", /* 39*/
+ "NENYA", /* 40*/
+ "VILYA", /* 41*/
+ "POWER", /* 42*/
+ "STONE_LORE", /* 43*/
+ "RAZORBACK", /* 44*/
+ "BLADETURNER", /* 45*/
+ "MEDIATOR", /* 46*/
+ "BELEGENNON", /* 47*/
+ "GORLIM", /* 48*/
+ "COLLUIN", /* 49*/
+ "BELANGIL", /* 50*/
+ "CONFUSE", /* 51*/
+ "SLEEP", /* 52*/
+ "QUAKE", /* 53*/
+ "TERROR", /* 54*/
+ "TELE_AWAY", /* 55*/
+ "BANISH_EVIL", /* 56*/
+ "GENOCIDE", /* 57*/
+ "MASS_GENO", /* 58*/
+ "ANGUIREL", /* 59*/
+ "ERU", /* 60*/
+ "DAWN", /* 61*/
+ "FIRESTAR", /* 62*/
+ "TURMIL", /* 63*/
+ "CUBRAGOL", /* 64*/
+ "CHARM_ANIMAL", /* 65*/
+ "CHARM_UNDEAD", /* 66*/
+ "CHARM_OTHER", /* 67*/
+ "CHARM_ANIMALS", /* 68*/
+ "CHARM_OTHERS", /* 69*/
+ "SUMMON_ANIMAL", /* 70*/
+ "SUMMON_PHANTOM", /* 71*/
+ "SUMMON_ELEMENTAL", /* 72*/
+ "SUMMON_DEMON", /* 73*/
+ "SUMMON_UNDEAD", /* 74*/
+ "ELESSAR", /* 75*/
+ "GANDALF", /* 76*/
+ "MARDA", /* 77*/
+ "PALANTIR", /* 78*/
+ "XXX79",
+ "XXX80",
+ "CURE_LW", /* 81*/
+ "CURE_MW", /* 82*/
+ "CURE_POISON", /* 83*/
+ "REST_LIFE", /* 84*/
+ "REST_ALL", /* 85*/
+ "CURE_700", /* 86*/
+ "CURE_1000", /* 87*/
+ "XXX88",
+ "EREBOR", /* 89*/
+ "DRUEDAIN", /* 90*/
+ "ESP", /* 91*/
+ "BERSERK", /* 92*/
+ "PROT_EVIL", /* 93*/
+ "RESIST_ALL", /* 94*/
+ "SPEED", /* 95*/
+ "XTRA_SPEED", /* 96*/
+ "WRAITH", /* 97*/
+ "INVULN", /* 98*/
+ "ROHAN", /* 99*/
+ "HELM", /* 100*/
+ "BOROMIR", /* 101*/
+ "HURIN", /* 102*/
+ "AXE_GOTHMOG", /* 103*/
+ "MELKOR", /* 104*/
+ "GROND", /* 105*/
+ "NATUREBANE", /* 106*/
+ "NIGHT", /* 107*/
+ "ORCHAST", /* 108*/
+ "XXX109",
+ "XXX110",
+ "LIGHT", /* 111*/
+ "MAP_LIGHT", /* 112*/
+ "DETECT_ALL", /* 113*/
+ "DETECT_XTRA", /* 114*/
+ "ID_FULL", /* 115*/
+ "ID_PLAIN", /* 116*/
+ "RUNE_EXPLO", /* 117*/
+ "RUNE_PROT", /* 118*/
+ "SATIATE", /* 119*/
+ "DEST_DOOR", /* 120*/
+ "STONE_MUD", /* 121*/
+ "RECHARGE", /* 122*/
+ "ALCHEMY", /* 123*/
+ "DIM_DOOR", /* 124*/
+ "TELEPORT", /* 125*/
+ "RECALL", /* 126*/
+ "DEATH", /* 127*/
+ "RUINATION", /* 128*/
+ "DESTRUC", /* 129*/
+ "UNINT", /* 130*/
+ "UNSTR", /* 131*/
+ "UNCON", /* 132*/
+ "UNCHR", /* 133*/
+ "UNDEX", /* 134*/
+ "UNWIS", /* 135*/
+ "STATLOSS", /* 136*/
+ "HISTATLOSS", /* 137*/
+ "EXPLOSS", /* 138*/
+ "HIEXPLOSS", /* 139*/
+ "SUMMON_MONST", /* 140*/
+ "PARALYZE", /* 141*/
+ "HALLU", /* 142*/
+ "POISON", /* 143*/
+ "HUNGER", /* 144*/
+ "STUN", /* 145*/
+ "CUTS", /* 146*/
+ "PARANO", /* 147*/
+ "CONFUSION", /* 148*/
+ "BLIND", /* 149*/
+ "PET_SUMMON", /* 150*/
+ "CURE_PARA", /* 151*/
+ "CURE_HALLU", /* 152*/
+ "CURE_POIS", /* 153*/
+ "CURE_HUNGER", /* 154*/
+ "CURE_STUN", /* 155*/
+ "CURE_CUTS", /* 156*/
+ "CURE_FEAR", /* 157*/
+ "CURE_CONF", /* 158*/
+ "CURE_BLIND", /* 159*/
+ "CURING", /* 160*/
+ "DARKNESS", /* 161*/
+ "LEV_TELE", /* 162*/
+ "ACQUIREMENT", /* 163*/
+ "WEIRD", /* 164*/
+ "AGGRAVATE", /* 165*/
+ "MUT", /* 166*/
+ "CURE_INSANITY", /* 167*/
+ "CURE_MUT", /* 168*/
+ "LIGHT_ABSORBTION", /* 169*/
+ "BA_FIRE_H", /* 170*/
+ "BA_COLD_H", /* 171*/
+ "BA_ELEC_H", /* 172*/
+ "BA_ACID_H", /* 173*/
+ "SPIN", /* 174*/
+ "NOLDOR", /* 175*/
+ "SPECTRAL", /* 176*/
+ "JUMP", /* 177*/
+ "DEST_TELE", /* 178*/
+ "BA_POIS_4", /* 179*/
+ "BA_COLD_4", /* 180*/
+ "BA_FIRE_4", /* 181*/
+ "BA_ACID_4", /* 182*/
+ "BA_ELEC_4", /* 183*/
+ "BR_ELEC", /* 184*/
+ "BR_COLD", /* 185*/
+ "BR_FIRE", /* 186*/
+ "BR_ACID", /* 187*/
+ "BR_POIS", /* 188*/
+ "BR_MANY", /* 189*/
+ "BR_CONF", /* 190*/
+ "BR_SOUND", /* 191*/
+ "BR_CHAOS", /* 192*/
+ "BR_SHARD", /* 193*/
+ "BR_BALANCE", /* 194*/
+ "BR_LIGHT", /* 195*/
+ "BR_POWER", /* 196*/
+ "GROW_MOLD", /* 197*/
+ "XXX198",
+ "XXX199",
+ "MUSIC", /* 200*/
+ ""
+};
+
+/*
+ * Convert a "color letter" into an "actual" color
+ * The colors are: dwsorgbuDWvyRGBU, as shown below
+ */
+int color_char_to_attr(char c)
+{
+ switch (c)
+ {
+ case 'd':
+ return (TERM_DARK);
+ case 'w':
+ return (TERM_WHITE);
+ case 's':
+ return (TERM_SLATE);
+ case 'o':
+ return (TERM_ORANGE);
+ case 'r':
+ return (TERM_RED);
+ case 'g':
+ return (TERM_GREEN);
+ case 'b':
+ return (TERM_BLUE);
+ case 'u':
+ return (TERM_UMBER);
+
+ case 'D':
+ return (TERM_L_DARK);
+ case 'W':
+ return (TERM_L_WHITE);
+ case 'v':
+ return (TERM_VIOLET);
+ case 'y':
+ return (TERM_YELLOW);
+ case 'R':
+ return (TERM_L_RED);
+ case 'G':
+ return (TERM_L_GREEN);
+ case 'B':
+ return (TERM_L_BLUE);
+ case 'U':
+ return (TERM_L_UMBER);
+ }
+
+ return ( -1);
+}
+
+/*
+ * Attr value-to-char convertion table
+ */
+byte conv_color[16] =
+{
+ 'd',
+ 'w',
+ 's',
+ 'o',
+ 'r',
+ 'g',
+ 'b',
+ 'u',
+ 'D',
+ 'W',
+ 'v',
+ 'y',
+ 'R',
+ 'G',
+ 'B',
+ 'U',
+};
+
+
+/* Values in re_info can be fixed, added, substracted or percented */
+static byte monster_ego_modify(char c)
+{
+ switch (c)
+ {
+ case '+':
+ return MEGO_ADD;
+ case '-':
+ return MEGO_SUB;
+ case '=':
+ return MEGO_FIX;
+ case '%':
+ return MEGO_PRC;
+ default:
+ {
+ msg_format("Unknown mego value modifier %c.", c);
+ return MEGO_ADD;
+ }
+ }
+}
+
+/*
+ * Implements fp stacks, for included files
+ */
+static FILE *fp_stack[10];
+static int fp_stack_idx = 0;
+
+/*
+ * Must be caleld before the main loop
+ */
+static void fp_stack_init(FILE *fp)
+{
+ fp_stack[0] = fp;
+ fp_stack_idx = 0;
+}
+
+static void fp_stack_push(cptr name)
+{
+ if (fp_stack_idx < 9)
+ {
+ char buf[1024];
+ FILE *fp;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit(format("Cannot open '%s' file.", name));
+
+ printf("ibncluding %s\n", name);
+
+ fp_stack[++fp_stack_idx] = fp;
+ }
+}
+
+static bool fp_stack_pop()
+{
+ if (fp_stack_idx > 0)
+ {
+ FILE *fp = fp_stack[fp_stack_idx--];
+ my_fclose(fp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/*
+ * Must be used instead of my_fgets for teh main loop
+ */
+static int my_fgets_dostack(char *buf, int len)
+{
+ // End of a file
+ if (0 != my_fgets(fp_stack[fp_stack_idx], buf, len))
+ {
+ // If any left, use them
+ if (fp_stack_pop())
+ return my_fgets_dostack(buf, len);
+ // If not, this is the end
+ else
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/*** Initialize from ascii template files ***/
+
+/*
+ * Grab one race flag from a textual string
+ */
+static bool unknown_shut_up = FALSE;
+static errr grab_one_class_flag(u32b *choice, cptr what)
+{
+ int i;
+ cptr s;
+
+ /* Scan classes flags */
+ for (i = 0; i < max_c_idx && (s = class_info[i].title + c_name); i++)
+ {
+ if (streq(what, s))
+ {
+ (choice[i / 32]) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ if (!unknown_shut_up) msg_format("Unknown class flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+static errr grab_one_race_allow_flag(u32b *choice, cptr what)
+{
+ int i;
+ cptr s;
+
+ /* Scan classes flags */
+ for (i = 0; i < max_rp_idx && (s = race_info[i].title + rp_name); i++)
+ {
+ if (streq(what, s))
+ {
+ (choice[i / 32]) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ if (!unknown_shut_up) msg_format("(1)Unknown race flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one flag from a textual string
+ */
+static errr grab_one_skill_flag(u32b *f1, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, s_info_flags1[i]))
+ {
+ (*f1) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("(2)Unknown skill flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+/*
+ * Grab one flag from a textual string
+ */
+static errr grab_one_player_race_flag(u32b *f1, u32b *f2, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, rp_info_flags1[i]))
+ {
+ (*f1) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, rp_info_flags2[i]))
+ {
+ (*f2) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("(2)Unknown race flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+/* Get an activation number (good for artifacts, recipes, egos, and object kinds) */
+int get_activation(char *activation)
+{
+ int i;
+ for ( i = 0 ; activation_names[i][0] ; i++)
+ if (!strncmp(activation_names[i], activation, 19))
+ {
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * Grab one flag in an object_kind from a textual string
+ */
+static errr grab_one_race_kind_flag(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ (*f1) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ (*f2) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps*/
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ (*f3) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ (*f3) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ (*f4) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ (*f5) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ (*esp) |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+/*
+ * Initialize the "player" arrays, by parsing an ascii "template" file
+ */
+errr init_player_info_txt(FILE *fp, char *buf)
+{
+ int i = 0, z;
+ int powers = 0;
+ int lev = 1;
+ int tit_idx = 0;
+ int spec_idx = 0;
+ int cur_ab = -1;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ player_race *rp_ptr = NULL;
+ player_race_mod *rmp_ptr = NULL;
+ player_class *c_ptr = NULL;
+ player_spec *s_ptr = NULL;
+ meta_class_type *mc_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ rp_head->name_size = 0;
+ rp_head->text_size = 0;
+ rmp_head->name_size = 0;
+ rmp_head->text_size = 0;
+ c_head->name_size = 0;
+ c_head->text_size = 0;
+
+ /* Init general skills */
+ for (z = 0; z < MAX_SKILLS; z++)
+ {
+ gen_skill_basem[z] = 0;
+ gen_skill_base[z] = 0;
+ gen_skill_modm[z] = 0;
+ gen_skill_mod[z] = 0;
+ }
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != rp_head->v_major) ||
+ (v2 != rp_head->v_minor) ||
+ (v3 != rp_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+/* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Reinit error_idx */
+ if (buf[0] == 'I')
+ {
+ error_idx = -1;
+ continue;
+ }
+
+ /* Process 'H' for "History" */
+ if (buf[0] == 'H')
+ {
+ int idx;
+ char *zz[6];
+
+ /* Scan for the values */
+ if (tokenize(buf + 2, 6, zz, ':', ':') != 6) return (1);
+
+ idx = atoi(zz[0]);
+ bg[idx].roll = atoi(zz[1]);
+ bg[idx].chart = atoi(zz[2]);
+ bg[idx].next = atoi(zz[3]);
+ bg[idx].bonus = atoi(zz[4]);
+
+ bg[idx].info = ++rp_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(rp_text + rp_head->text_size, zz[5]);
+
+ /* Advance the index */
+ rp_head->text_size += strlen(zz[5]);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G:k' for "General skills" */
+ if ((buf[0] == 'G') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ gen_skill_basem[i] = monster_ego_modify(v);
+ gen_skill_base[i] = val;
+ gen_skill_modm[i] = monster_ego_modify(m);
+ gen_skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'R') && (buf[2] == 'N'))
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= rp_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ rp_ptr = &race_info[i];
+
+ /* Hack -- Verify space */
+ if (rp_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!rp_ptr->title) rp_ptr->title = ++rp_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(rp_name + rp_head->name_size, s);
+
+ /* Advance the index */
+ rp_head->name_size += strlen(s);
+
+ rp_ptr->powers[0] = rp_ptr->powers[1] = rp_ptr->powers[2] = rp_ptr->powers[3] = -1;
+ powers = 0;
+ lev = 1;
+ cur_ab = 0;
+ for (z = 0; z < 10; z++)
+ rp_ptr->abilities[z].level = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if ((buf[0] == 'R') && (buf[2] == 'D'))
+ {
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Hack -- Verify space */
+ if (rp_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!rp_ptr->desc)
+ {
+ rp_ptr->desc = ++rp_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(rp_text + rp_head->text_size, s);
+
+ /* Advance the index */
+ rp_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(rp_text + rp_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ rp_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "body parts" */
+ if ((buf[0] == 'R') && (buf[2] == 'E'))
+ {
+ int s[BODY_MAX], z;
+
+ /* Scan for the values */
+ if (BODY_MAX != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5])) return (1);
+
+ for (z = 0; z < BODY_MAX; z++)
+ rp_ptr->body_parts[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flag level" */
+ if ((buf[0] == 'R') && (buf[2] == 'R'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ lev = s[0];
+ rp_ptr->opval[lev] = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Stats" */
+ if ((buf[0] == 'R') && (buf[2] == 'S'))
+ {
+ int s[7], z;
+
+ /* Scan for the values */
+ if (7 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6])) return (1);
+
+ rp_ptr->luck = s[6];
+ for (z = 0; z < 6; z++)
+ rp_ptr->r_adj[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "powers" */
+ if ((buf[0] == 'R') && (buf[2] == 'Z'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ rp_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'R') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ rp_ptr->r_dis = s[0];
+ rp_ptr->r_dev = s[1];
+ rp_ptr->r_sav = s[2];
+ rp_ptr->r_stl = s[3];
+ rp_ptr->r_srh = s[4];
+ rp_ptr->r_fos = s[5];
+ rp_ptr->r_thn = s[6];
+ rp_ptr->r_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if ((buf[0] == 'R') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ rp_ptr->skill_basem[i] = monster_ego_modify(v);
+ rp_ptr->skill_base[i] = val;
+ rp_ptr->skill_modm[i] = monster_ego_modify(m);
+ rp_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if ((buf[0] == 'R') && (buf[2] == 'b'))
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 4, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ rp_ptr->abilities[cur_ab].ability = i;
+ rp_ptr->abilities[cur_ab].level = atoi(buf + 4);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Mods" */
+ if ((buf[0] == 'R') && (buf[2] == 'M'))
+ {
+ int s[10];
+
+ /* Scan for the values */
+ if (10 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7], &s[8], &s[9])) return (1);
+
+ rp_ptr->b_age = s[0];
+ rp_ptr->m_age = s[1];
+ rp_ptr->m_b_ht = s[2];
+ rp_ptr->m_m_ht = s[3];
+ rp_ptr->m_b_wt = s[4];
+ rp_ptr->m_m_wt = s[5];
+ rp_ptr->f_b_ht = s[6];
+ rp_ptr->f_m_ht = s[7];
+ rp_ptr->f_b_wt = s[8];
+ rp_ptr->f_m_wt = s[9];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'R') && (buf[2] == 'P'))
+ {
+ int s[4];
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 4, "%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3])) return (1);
+
+ rp_ptr->r_mhp = s[0];
+ rp_ptr->r_exp = s[1];
+ rp_ptr->infra = s[2];
+ rp_ptr->chart = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'R') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&rp_ptr->flags1, &rp_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'R') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&rp_ptr->oflags1[lev], &rp_ptr->oflags2[lev], &rp_ptr->oflags3[lev], &rp_ptr->oflags4[lev], &rp_ptr->oflags5[lev], &rp_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if ((buf[0] == 'R') && (buf[2] == 'O'))
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 4, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ rp_ptr->obj_pval[rp_ptr->obj_num] = s[4];
+ rp_ptr->obj_tval[rp_ptr->obj_num] = s[0];
+ rp_ptr->obj_sval[rp_ptr->obj_num] = s[1];
+ rp_ptr->obj_dd[rp_ptr->obj_num] = s[2];
+ rp_ptr->obj_ds[rp_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Class choice flags" (multiple lines) */
+ if ((buf[0] == 'R') && (buf[2] == 'C'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_class_flag(rp_ptr->choice, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'S') && (buf[2] == 'N'))
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= rmp_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ rmp_ptr = &race_mod_info[i];
+
+ /* Hack -- Verify space */
+ if (rmp_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!rmp_ptr->title) rmp_ptr->title = ++rmp_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(rmp_name + rmp_head->name_size, s);
+
+ /* Advance the index */
+ rmp_head->name_size += strlen(s);
+
+ rmp_ptr->powers[0] = rmp_ptr->powers[1] = rmp_ptr->powers[2] = rmp_ptr->powers[3] = -1;
+ powers = 0;
+ lev = 1;
+ cur_ab = 0;
+ for (z = 0; z < 10; z++)
+ rmp_ptr->abilities[z].level = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if ((buf[0] == 'S') && (buf[2] == 'D'))
+ {
+ /* Acquire the text */
+ s = buf + 6;
+
+ if (buf[4] == 'A') rmp_ptr->place = TRUE;
+ else rmp_ptr->place = FALSE;
+
+ /* Hack -- Verify space */
+ if (rmp_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!rmp_ptr->desc)
+ {
+ rmp_ptr->desc = ++rmp_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(rmp_text + rmp_head->text_size, s);
+
+ /* Advance the index */
+ rmp_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(rmp_text + rmp_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ rmp_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "body parts" */
+ if ((buf[0] == 'S') && (buf[2] == 'E'))
+ {
+ int s[BODY_MAX], z;
+
+ /* Scan for the values */
+ if (BODY_MAX != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5])) return (1);
+
+ for (z = 0; z < BODY_MAX; z++)
+ rmp_ptr->body_parts[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flag level" */
+ if ((buf[0] == 'S') && (buf[2] == 'R'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ lev = s[0];
+ rmp_ptr->opval[lev] = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Stats" */
+ if ((buf[0] == 'S') && (buf[2] == 'S'))
+ {
+ int s[8], z;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ rmp_ptr->mana = s[7];
+ rmp_ptr->luck = s[6];
+ for (z = 0; z < 6; z++)
+ rmp_ptr->r_adj[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "powers" */
+ if ((buf[0] == 'S') && (buf[2] == 'Z'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ rmp_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if ((buf[0] == 'S') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ rmp_ptr->skill_basem[i] = monster_ego_modify(v);
+ rmp_ptr->skill_base[i] = val;
+ rmp_ptr->skill_modm[i] = monster_ego_modify(m);
+ rmp_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if ((buf[0] == 'S') && (buf[2] == 'b'))
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 4, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ rmp_ptr->abilities[cur_ab].ability = i;
+ rmp_ptr->abilities[cur_ab].level = atoi(buf + 4);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'S') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ rmp_ptr->r_dis = s[0];
+ rmp_ptr->r_dev = s[1];
+ rmp_ptr->r_sav = s[2];
+ rmp_ptr->r_stl = s[3];
+ rmp_ptr->r_srh = s[4];
+ rmp_ptr->r_fos = s[5];
+ rmp_ptr->r_thn = s[6];
+ rmp_ptr->r_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Mods" */
+ if ((buf[0] == 'S') && (buf[2] == 'M'))
+ {
+ int s[10];
+
+ /* Scan for the values */
+ if (10 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7], &s[8], &s[9])) return (1);
+
+ rmp_ptr->b_age = s[0];
+ rmp_ptr->m_age = s[1];
+ rmp_ptr->m_b_ht = s[2];
+ rmp_ptr->m_m_ht = s[3];
+ rmp_ptr->m_b_wt = s[4];
+ rmp_ptr->m_m_wt = s[5];
+ rmp_ptr->f_b_ht = s[6];
+ rmp_ptr->f_m_ht = s[7];
+ rmp_ptr->f_b_wt = s[8];
+ rmp_ptr->f_m_wt = s[9];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'S') && (buf[2] == 'P'))
+ {
+ int s[3];
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 4, "%d:%d:%d",
+ &s[0], &s[1], &s[2])) return (1);
+
+ rmp_ptr->r_mhp = s[0];
+ rmp_ptr->r_exp = s[1];
+ rmp_ptr->infra = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&rmp_ptr->flags1, &rmp_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&rmp_ptr->oflags1[lev], &rmp_ptr->oflags2[lev], &rmp_ptr->oflags3[lev], &rmp_ptr->oflags4[lev], &rmp_ptr->oflags5[lev], &rmp_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if ((buf[0] == 'S') && (buf[2] == 'O'))
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 4, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ rmp_ptr->obj_pval[rmp_ptr->obj_num] = s[4];
+ rmp_ptr->obj_tval[rmp_ptr->obj_num] = s[0];
+ rmp_ptr->obj_sval[rmp_ptr->obj_num] = s[1];
+ rmp_ptr->obj_dd[rmp_ptr->obj_num] = s[2];
+ rmp_ptr->obj_ds[rmp_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Allowed races" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'A'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_allow_flag(rmp_ptr->choice, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Class choice flags" (multiple lines) */
+ if ((buf[0] == 'S') && (buf[2] == 'C'))
+ {
+ u32b choice[2] = {0, 0}, z;
+
+ /* Parse every entry */
+ for (s = buf + 6; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_class_flag(choice, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ for (z = 0; z < 2; z++)
+ {
+ if (buf[4] == 'A') rmp_ptr->pclass[z] |= choice[z];
+ else rmp_ptr->mclass[z] |= choice[z];
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'C') && (buf[2] == 'N'))
+ {
+ int z;
+
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= c_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ c_ptr = &class_info[i];
+
+ /* Hack -- Verify space */
+ if (c_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!c_ptr->title) c_ptr->title = ++c_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(c_name + c_head->name_size, s);
+
+ /* Advance the index */
+ c_head->name_size += strlen(s);
+
+ c_ptr->powers[0] = c_ptr->powers[1] = c_ptr->powers[2] = c_ptr->powers[3] = -1;
+ powers = 0;
+ lev = 1;
+ for (z = 0; z < 10; z++)
+ c_ptr->abilities[z].level = -1;
+ cur_ab = 0;
+ c_ptr->obj_num = 0;
+ tit_idx = 0;
+ spec_idx = -1;
+ for (z = 0; z < MAX_SPEC; z++)
+ c_ptr->spec[z].title = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if ((buf[0] == 'C') && (buf[2] == 'D'))
+ {
+ /* Acquire the text */
+ s = buf + 6;
+
+ /* Hack -- Verify space */
+ if (c_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ switch (buf[4])
+ {
+ case '0':
+ /* Advance and Save the text index */
+ if (!c_ptr->desc)
+ {
+ c_ptr->desc = ++c_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, s);
+
+ /* Advance the index */
+ c_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ c_head->text_size += strlen(s) + 1;
+ }
+ break;
+ case '1':
+ /* Advance and Save the text index */
+ c_ptr->titles[tit_idx++] = ++c_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, s);
+
+ /* Advance the index */
+ c_head->text_size += strlen(s);
+ break;
+ default:
+ return (6);
+ break;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if ((buf[0] == 'C') && (buf[2] == 'O'))
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 4, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ c_ptr->obj_pval[c_ptr->obj_num] = s[4];
+ c_ptr->obj_tval[c_ptr->obj_num] = s[0];
+ c_ptr->obj_sval[c_ptr->obj_num] = s[1];
+ c_ptr->obj_dd[c_ptr->obj_num] = s[2];
+ c_ptr->obj_ds[c_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "body parts" */
+ if ((buf[0] == 'C') && (buf[2] == 'E'))
+ {
+ int s[BODY_MAX], z;
+
+ /* Scan for the values */
+ if (BODY_MAX != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5])) return (1);
+
+ for (z = 0; z < BODY_MAX; z++)
+ c_ptr->body_parts[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flag level" */
+ if ((buf[0] == 'C') && (buf[2] == 'R'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ lev = s[0];
+ c_ptr->opval[lev] = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Stats" */
+ if ((buf[0] == 'C') && (buf[2] == 'S'))
+ {
+ int s[8], z;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->mana = s[6];
+ c_ptr->extra_blows = s[7];
+ for (z = 0; z < 6; z++)
+ c_ptr->c_adj[z] = s[z];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if ((buf[0] == 'C') && (buf[2] == 'k'))
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ c_ptr->skill_basem[i] = monster_ego_modify(v);
+ c_ptr->skill_base[i] = val;
+ c_ptr->skill_modm[i] = monster_ego_modify(m);
+ c_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if ((buf[0] == 'C') && (buf[2] == 'b'))
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 4, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ c_ptr->abilities[cur_ab].ability = i;
+ c_ptr->abilities[cur_ab].level = atoi(buf + 4);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'g' for "gods" */
+ if ((buf[0] == 'C') && (buf[2] == 'g'))
+ {
+ int i;
+
+ if (streq(buf + 4, "All Gods"))
+ c_ptr->gods = 0xFFFFFFFF;
+ else
+ {
+ if ((i = find_god(buf + 4)) == -1) return (1);
+ c_ptr->gods |= BIT(i);
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "powers" */
+ if ((buf[0] == 'C') && (buf[2] == 'Z'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ c_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'C') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->c_dis = s[0];
+ c_ptr->c_dev = s[1];
+ c_ptr->c_sav = s[2];
+ c_ptr->c_stl = s[3];
+ c_ptr->c_srh = s[4];
+ c_ptr->c_fos = s[5];
+ c_ptr->c_thn = s[6];
+ c_ptr->c_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'x' for "Xtra skills" */
+ if ((buf[0] == 'C') && (buf[2] == 'X'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->x_dis = s[0];
+ c_ptr->x_dev = s[1];
+ c_ptr->x_sav = s[2];
+ c_ptr->x_stl = s[3];
+ c_ptr->x_srh = s[4];
+ c_ptr->x_fos = s[5];
+ c_ptr->x_thn = s[6];
+ c_ptr->x_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'C') && (buf[2] == 'P'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ c_ptr->c_mhp = s[0];
+ c_ptr->c_exp = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "sensing" */
+ if ((buf[0] == 'C') && (buf[2] == 'C'))
+ {
+ s32b s[3];
+ char h, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c:%c:%ld:%ld:%ld",
+ &h, &m, &s[0], &s[1], &s[2])) return (1);
+
+ c_ptr->sense_heavy = (h == 'H') ? TRUE : FALSE;
+ c_ptr->sense_heavy_magic = (m == 'H') ? TRUE : FALSE;
+ c_ptr->sense_base = s[0];
+ c_ptr->sense_pl = s[1];
+ c_ptr->sense_plus = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "blows" */
+ if ((buf[0] == 'C') && (buf[2] == 'B'))
+ {
+ int s[3];
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 4, "%d:%d:%d",
+ &s[0], &s[1], &s[2])) return (1);
+
+ c_ptr->blow_num = s[0];
+ c_ptr->blow_wgt = s[1];
+ c_ptr->blow_mul = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'C') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&c_ptr->flags1, &c_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'C') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&c_ptr->oflags1[lev], &c_ptr->oflags2[lev], &c_ptr->oflags3[lev], &c_ptr->oflags4[lev], &c_ptr->oflags5[lev], &c_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Specialities */
+ if ((buf[0] == 'C') && (buf[2] == 'a'))
+ {
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[4] == 'N')
+ {
+ /* Find the colon before the name */
+ s = buf + 6;
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+ /* Get the index */
+ spec_idx++;
+
+ /* Verify information */
+ if (spec_idx >= MAX_SPEC) return (2);
+
+ /* Point at the "info" */
+ s_ptr = &c_ptr->spec[spec_idx];
+
+ /* Hack -- Verify space */
+ if (c_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!s_ptr->title) s_ptr->title = ++c_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(c_name + c_head->name_size, s);
+
+ /* Advance the index */
+ c_head->name_size += strlen(s);
+
+ s_ptr->obj_num = 0;
+ cur_ab = 0;
+ for (z = 0; z < 10; z++)
+ s_ptr->abilities[z].level = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if (buf[4] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 6;
+
+ /* Hack -- Verify space */
+ if (c_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!s_ptr->desc)
+ {
+ s_ptr->desc = ++c_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, s);
+
+ /* Advance the index */
+ c_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(c_text + c_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ c_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object birth" */
+ if (buf[4] == 'O')
+ {
+ int s[5];
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 6, "%d:%d:%d:%dd%d",
+ &s[0], &s[1], &s[4], &s[2], &s[3]))
+ {
+ s[4] = 0;
+
+ if (4 != sscanf(buf + 6, "%d:%d:%dd%d",
+ &s[0], &s[1], &s[2], &s[3]))
+ {
+ return (1);
+ }
+ }
+
+ s_ptr->obj_pval[s_ptr->obj_num] = s[4];
+ s_ptr->obj_tval[s_ptr->obj_num] = s[0];
+ s_ptr->obj_sval[s_ptr->obj_num] = s[1];
+ s_ptr->obj_dd[s_ptr->obj_num] = s[2];
+ s_ptr->obj_ds[s_ptr->obj_num++] = s[3];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'g' for "gods" */
+ if (buf[4] == 'g')
+ {
+ int i;
+
+ if (streq(buf + 6, "All Gods"))
+ s_ptr->gods = 0xFFFFFFFF;
+ else
+ {
+ if ((i = find_god(buf + 6)) == -1) return (1);
+ s_ptr->gods |= BIT(i);
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "skills" */
+ if (buf[4] == 'k')
+ {
+ long val, mod, i;
+ char name[200], v, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 6, "%c%ld:%c%ld:%s",
+ &v, &val, &m, &mod, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ s_ptr->skill_basem[i] = monster_ego_modify(v);
+ s_ptr->skill_base[i] = val;
+ s_ptr->skill_modm[i] = monster_ego_modify(m);
+ s_ptr->skill_mod[i] = mod;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'b' for "abilities" */
+ if (buf[4] == 'b')
+ {
+ char *sec;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 6, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ if ((i = find_ability(sec)) == -1) return (1);
+
+ s_ptr->abilities[cur_ab].ability = i;
+ s_ptr->abilities[cur_ab].level = atoi(buf + 6);
+ cur_ab++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if (buf[4] == 'G')
+ {
+ /* Parse every entry */
+ for (s = buf + 6; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&s_ptr->flags1, &s_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'K' for "desired skills" */
+ if (buf[4] == 'K')
+ {
+ long val;
+ char name[200];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 6, "%ld:%s",
+ &val, name)) return (1);
+
+ if ((i = find_skill(name)) == -1) return (1);
+ s_ptr->skill_ideal[i] = val;
+
+ /* Next... */
+ continue;
+ }
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if ((buf[0] == 'M') && (buf[2] == 'N'))
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 4, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 4);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= max_mc_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ mc_ptr = &meta_class_info[i];
+
+ /* Append chars to the name */
+ strcpy(mc_ptr->name, s + 2);
+ mc_ptr->color = color_char_to_attr(s[0]);
+ for (powers = 0; powers < max_c_idx; powers++)
+ mc_ptr->classes[powers] = -1;
+ powers = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "Classes" */
+ if ((buf[0] == 'M') && (buf[2] == 'C'))
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Find it in the list */
+ for (i = 0; i < max_c_idx; i++)
+ {
+ if (!stricmp(s, class_info[i].title + c_name)) break;
+ }
+
+ if (i == max_c_idx) return (6);
+
+ mc_ptr->classes[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+ /* Complete the "name" and "text" sizes */
+ ++rp_head->name_size;
+ ++rp_head->text_size;
+ ++rmp_head->name_size;
+ ++rmp_head->text_size;
+ ++c_head->name_size;
+ ++c_head->text_size;
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize the "v_info" array, by parsing an ascii "template" file
+ */
+errr init_v_info_txt(FILE *fp, char *buf, bool start)
+{
+ int i;
+ char *s;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ vault_type *v_ptr = NULL;
+
+ if (start)
+ {
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Prepare the "fake" stuff */
+ v_head->name_size = 0;
+ v_head->text_size = 0;
+ }
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+ if ((buf[0] == 'Q') || (buf[0] == 'T')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf, "V:%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != v_head->v_major) ||
+ (v2 != v_head->v_minor) ||
+ (v3 != v_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+/* Scan for the values */
+ if (3 != sscanf(buf, "V:%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= v_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ v_ptr = &v_info[i];
+
+ /* Hack -- Verify space */
+ if (v_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!v_ptr->name) v_ptr->name = ++v_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(v_name + v_head->name_size, s);
+
+ /* Advance the index */
+ v_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current v_ptr */
+ if (!v_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (v_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!v_ptr->text) v_ptr->text = ++v_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(v_text + v_head->text_size, s);
+
+ /* Advance the index */
+ v_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'X' for "Extra info" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int typ, rat, hgt, wid;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &typ, &rat, &hgt, &wid)) return (1);
+
+ /* Save the values */
+ v_ptr->typ = typ;
+ v_ptr->rat = rat;
+ v_ptr->hgt = hgt;
+ v_ptr->wid = wid;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current v_ptr */
+ if (!v_ptr) return (3);
+
+ /* Process monster, item and level info for special levels */
+ if (buf[0] == 'Y')
+ {
+
+ int mon1, mon2, mon3, mon4, mon5, mon6, mon7, mon8, mon9;
+ int mon10, item1, item2, item3, lvl, dun_type;
+
+ /* Scan for the values */
+ if (15 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &mon1, &mon2, &mon3, &mon4, &mon5, &mon6, &mon7, &mon8, &mon9, &mon10, &item1, &item2, &item3, &lvl, &dun_type)) return (1);
+
+ /* Save the values */
+ v_ptr->mon[0] = mon1;
+ v_ptr->mon[1] = mon2;
+ v_ptr->mon[2] = mon3;
+ v_ptr->mon[3] = mon4;
+ v_ptr->mon[4] = mon5;
+ v_ptr->mon[5] = mon6;
+ v_ptr->mon[6] = mon7;
+ v_ptr->mon[7] = mon8;
+ v_ptr->mon[8] = mon9;
+ v_ptr->mon[9] = mon10;
+ v_ptr->item[0] = item1;
+ v_ptr->item[1] = item2;
+ v_ptr->item[2] = item3;
+ v_ptr->lvl = lvl;
+ v_ptr->dun_type = dun_type;
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ if (!start)
+ {
+ ++v_head->name_size;
+ ++v_head->text_size;
+ }
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in an feature_type from a textual string
+ */
+static errr grab_one_feature_flag(feature_type *f_ptr, cptr what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, f_info_flags1[i]))
+ {
+ f_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+/*
+ * Initialize the "f_info" array, by parsing an ascii "template" file
+ */
+errr init_f_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+ u32b default_desc = 0, default_tunnel = 0, default_block = 0;
+
+ /* Current entry */
+ feature_type *f_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Prepare the "fake" stuff */
+ f_head->name_size = 0;
+ f_head->text_size = 0;
+
+ /* Add some fake descs */
+ default_desc = ++f_head->text_size;
+ strcpy(f_text + f_head->text_size, "a wall blocking your way");
+ f_head->text_size += strlen("a wall blocking your way");
+
+ default_tunnel = ++f_head->text_size;
+ strcpy(f_text + f_head->text_size, "You cannot tunnel through that.");
+ f_head->text_size += strlen("You cannot tunnel through that.");
+
+ default_block = ++f_head->text_size;
+ strcpy(f_text + f_head->text_size, "a wall blocking your way");
+ f_head->text_size += strlen("a wall blocking your way");
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != f_head->v_major) ||
+ (v2 != f_head->v_minor) ||
+ (v3 != f_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= f_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ f_ptr = &f_info[i];
+
+ /* Hack -- Verify space */
+ if (f_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!f_ptr->name) f_ptr->name = ++f_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(f_name + f_head->name_size, s);
+
+ /* Advance the index */
+ f_head->name_size += strlen(s);
+
+ /* Default "mimic" */
+ f_ptr->mimic = i;
+ f_ptr->text = default_desc;
+ f_ptr->block = default_desc;
+ f_ptr->tunnel = default_tunnel;
+ f_ptr->block = default_block;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current f_ptr */
+ if (!f_ptr) return (3);
+
+
+ /* Process 'D' for "Descriptions" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 4;
+
+ /* Hack -- Verify space */
+ if (f_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ switch (buf[2])
+ {
+ case '0':
+ /* Advance and Save the text index */
+ f_ptr->text = ++f_head->text_size;
+ break;
+ case '1':
+ /* Advance and Save the text index */
+ f_ptr->tunnel = ++f_head->text_size;
+ break;
+ case '2':
+ /* Advance and Save the text index */
+ f_ptr->block = ++f_head->text_size;
+ break;
+ default:
+ return (6);
+ break;
+ }
+
+ /* Append chars to the name */
+ strcpy(f_text + f_head->text_size, s);
+
+ /* Advance the index */
+ f_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'M' for "Mimic" (one line only) */
+ if (buf[0] == 'M')
+ {
+ int mimic;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &mimic)) return (1);
+
+ /* Save the values */
+ f_ptr->mimic = mimic;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Shimmer" (one line only) */
+ if (buf[0] == 'S')
+ {
+ char s0, s1, s2, s3, s4, s5, s6;
+
+ /* Scan for the values */
+ if (7 != sscanf(buf + 2, "%c:%c:%c:%c:%c:%c:%c",
+ &s0, &s1, &s2, &s3, &s4, &s5, &s6)) return (1);
+
+ /* Save the values */
+ f_ptr->shimmer[0] = color_char_to_attr(s0);
+ f_ptr->shimmer[1] = color_char_to_attr(s1);
+ f_ptr->shimmer[2] = color_char_to_attr(s2);
+ f_ptr->shimmer[3] = color_char_to_attr(s3);
+ f_ptr->shimmer[4] = color_char_to_attr(s4);
+ f_ptr->shimmer[5] = color_char_to_attr(s5);
+ f_ptr->shimmer[6] = color_char_to_attr(s6);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the color */
+ tmp = color_char_to_attr(buf[4]);
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ f_ptr->d_attr = tmp;
+ f_ptr->d_char = buf[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Effects" (up to four lines) -SC- */
+ if (buf[0] == 'E')
+ {
+ int side, dice, freq, type;
+ cptr tmp;
+
+ /* Find the next empty blow slot (if any) */
+ for (i = 0; i < 4; i++) if ((!f_ptr->d_side[i]) &&
+ (!f_ptr->d_dice[i])) break;
+
+ /* Oops, no more slots */
+ if (i == 4) return (1);
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%dd%d:%d:%d",
+ &dice, &side, &freq, &type))
+ {
+ int j;
+
+ if (3 != sscanf(buf + 2, "%dd%d:%d",
+ &dice, &side, &freq)) return (1);
+
+ tmp = buf + 2;
+ for (j = 0; j < 2; j++)
+ {
+ tmp = strchr(tmp, ':');
+ if (tmp == NULL) return (1);
+ tmp++;
+ }
+
+ j = 0;
+
+ while (d_info_dtypes[j].name != NULL)
+ if (strcmp(d_info_dtypes[j].name, tmp) == 0)
+ {
+ f_ptr->d_type[i] = d_info_dtypes[j].feat;
+ break;
+ }
+ else j++;
+
+ if (d_info_dtypes[j].name == NULL) return (1);
+ }
+ else
+ f_ptr->d_type[i] = type;
+
+ freq *= 10;
+ /* Save the values */
+ f_ptr->d_side[i] = side;
+ f_ptr->d_dice[i] = dice;
+ f_ptr->d_frequency[i] = freq;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_feature_flag(f_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++f_head->name_size;
+ ++f_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in an object_kind from a textual string
+ */
+static errr grab_one_kind_flag(object_kind *k_ptr, cptr what, bool obvious)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (obvious)
+ k_ptr->oflags1 |= (1L << i);
+ else
+ k_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (obvious)
+ k_ptr->oflags2 |= (1L << i);
+ else
+ k_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps*/
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (obvious)
+ k_ptr->oflags2 |= (1L << i);
+ else
+ k_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (obvious)
+ k_ptr->oflags3 |= (1L << i);
+ else
+ k_ptr->flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (obvious)
+ k_ptr->oflags4 |= (1L << i);
+ else
+ k_ptr->flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (obvious)
+ k_ptr->oflags5 |= (1L << i);
+ else
+ k_ptr->flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (obvious)
+ k_ptr->oesp |= (1L << i);
+ else
+ k_ptr->esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+/*
+ * Initialize the "k_info" array, by parsing an ascii "template" file
+ */
+errr init_k_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ object_kind *k_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Prepare the "fake" stuff */
+ k_head->name_size = 0;
+ k_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != k_head->v_major) ||
+ (v2 != k_head->v_minor) ||
+ (v3 != k_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= k_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ k_ptr = &k_info[i];
+
+ /* Hack -- Verify space */
+ if (k_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!k_ptr->name) k_ptr->name = ++k_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(k_name + k_head->name_size, s);
+
+ /* Advance the index */
+ k_head->name_size += strlen(s);
+
+ /* Needed hack */
+ k_ptr->esp = 0;
+ k_ptr->power = -1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current k_ptr */
+ if (!k_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (k_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!k_ptr->text) k_ptr->text = ++k_head->text_size;
+
+ /* Append a space if needed */
+ else if (k_text[k_head->text_size - 1] != ' ')
+ {
+ /* Append chars to the name */
+ strcpy(k_text + k_head->text_size, " ");
+
+ /* Advance the index */
+ k_head->text_size += 1;
+ }
+
+ /* Append chars to the name */
+ strcpy(k_text + k_head->text_size, s);
+
+ /* Advance the index */
+ k_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ char sym;
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the char */
+ sym = buf[2];
+
+ /* Extract the attr */
+ tmp = color_char_to_attr(buf[4]);
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ k_ptr->d_attr = tmp;
+ k_ptr->d_char = sym;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int tval, sval, pval, pval2 = 0;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &tval, &sval, &pval, &pval2))
+ {
+ char spl[70];
+
+ if (4 != sscanf(buf + 2, "%d:%d:%d:SPELL=%s",
+ &tval, &sval, &pval, spl))
+ {
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tval, &sval, &pval))
+ return (1);
+ }
+ else
+ {
+ char *spl = strchr(buf + 2, '=') + 1;
+
+ pval2 = find_spell(spl);
+ }
+ }
+
+ /* Save the values */
+ k_ptr->tval = tval;
+ k_ptr->sval = sval;
+ k_ptr->pval = pval;
+ k_ptr->pval2 = pval2;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, extra, wgt;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &extra, &wgt, &cost)) return (1);
+
+ /* Save the values */
+ k_ptr->level = level;
+ k_ptr->extra = extra;
+ k_ptr->weight = wgt;
+ k_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'T' for "arTifact Info" (one line only) */
+ if (buf[0] == 'T')
+ {
+ int btval, bsval;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &btval, &bsval)) return (1);
+
+ /* Save the values */
+ k_ptr->btval = btval;
+ k_ptr->bsval = bsval;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ k_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'a' for Activation */
+ if ( buf[0] == 'a')
+ {
+ if (prefix(buf + 2, "HARDCORE="))
+ {
+ k_ptr->activate = get_activation(buf + 11);
+ if (k_ptr->activate == -1)
+ return 1;
+ }
+ else if (prefix(buf + 2, "SPELL="))
+ {
+ k_ptr->activate = -find_spell(buf + 8);
+ if (k_ptr->activate == -( -1))
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Allocation" (one line only) */
+ if (buf[0] == 'A')
+ {
+ int i;
+
+ /* XXX XXX XXX Simply read each number following a colon */
+ for (i = 0, s = buf + 1; s && (s[0] == ':') && s[1]; ++i)
+ {
+ /* Default chance */
+ k_ptr->chance[i] = 1;
+
+ /* Store the level */
+ k_ptr->locale[i] = atoi(s + 1);
+
+ /* Find the slash */
+ t = strchr(s + 1, '/');
+
+ /* Find the next colon */
+ s = strchr(s + 1, ':');
+
+ /* If the slash is "nearby", use it */
+ if (t && (!s || t < s))
+ {
+ int chance = atoi(t + 1);
+ if (chance > 0) k_ptr->chance[i] = chance;
+ }
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'P' for "power" and such */
+ if (buf[0] == 'P')
+ {
+ int ac, hd1, hd2, th, td, ta;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d",
+ &ac, &hd1, &hd2, &th, &td, &ta)) return (1);
+
+ k_ptr->ac = ac;
+ k_ptr->dd = hd1;
+ k_ptr->ds = hd2;
+ k_ptr->to_h = th;
+ k_ptr->to_d = td;
+ k_ptr->to_a = ta;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_kind_flag(k_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'f' for obvious flags */
+ if (buf[0] == 'f')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_kind_flag(k_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++k_head->name_size;
+ ++k_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*Get a kind flag, return flag*32+bit number or -1 for unknown*/
+
+int get_k_flag(char *what)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ return i;
+ if (streq(what, k_info_flags2[i]))
+ return 1*32 + i;
+ if (streq(what, k_info_flags2_trap[i]))
+ return 1*32 + i;
+ if (streq(what, k_info_flags3[i]))
+ return 2*32 + i;
+ if (streq(what, k_info_flags4[i]))
+ return 3*32 + i;
+ if (streq(what, k_info_flags5[i]))
+ return 4*32 + i;
+ if (streq(what, esp_flags[i]))
+ return 5*32 + i;
+ }
+
+ /* Oops */
+ msg_format("Unknown object flag '%s'.", what);
+
+ /* Error */
+ return ( -1);
+
+}
+
+int get_r_flag(char *what)
+{
+ int i;
+
+ /* Check flags */
+ /* this processes all r_info_flag arrays in parallel.
+ Seemed like a good idea at the time..
+ */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ return i;
+ if (streq(what, r_info_flags2[i]))
+ return 1*32 + i;
+ if (streq(what, r_info_flags3[i]))
+ return 2*32 + i;
+ if (streq(what, r_info_flags4[i]))
+ return 3*32 + i;
+ if (streq(what, r_info_flags5[i]))
+ return 4*32 + i;
+ if (streq(what, r_info_flags6[i]))
+ return 5*32 + i;
+ if (streq(what, r_info_flags7[i]))
+ return 6*32 + i;
+ if (streq(what, r_info_flags8[i]))
+ return 7*32 + i;
+ if (streq(what, r_info_flags9[i]))
+ return 8*32 + i;
+ }
+
+ /* Oops */
+ msg_format("Unknown race flag '%s'.", what);
+
+ /* Error */
+ return ( -1);
+}
+int init_al_info_essence(char *essence)
+{
+ int i;
+ for ( i = 0 ; essence_names[i][0] ; i++)
+ if (!strncmp(essence_names[i], essence, 9))
+ {
+ return i;
+ }
+ return -1;
+}
+/*
+ * Initialize the "al_info" array, by parsing an ascii "template" file
+ */
+errr init_al_info_txt(FILE *fp, char *buf)
+{
+ int al_idx = 0, a_idx = 0;
+ char *s, *t;
+ struct artifact_select_flag *a_ptr = NULL;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Fun! */
+ al_head->name_size = 0;
+ *al_name = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != al_head->v_major) ||
+ (v2 != al_head->v_minor) ||
+ (v3 != al_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int tval, sval, qty;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tval, &sval, &qty))
+ {
+ return (1);
+ }
+
+ /* ignore everything after the first space. */
+ s = strchr(buf, ' ');
+ if (s != NULL)
+ *s = 0;
+
+ /* Save the values */
+ alchemist_recipes[al_idx].tval = tval;
+ alchemist_recipes[al_idx].sval = sval;
+ alchemist_recipes[al_idx].qty = qty;
+ alchemist_recipes[al_idx].sval_essence = init_al_info_essence(strrchr(buf, ':') + 1);
+ if (alchemist_recipes[al_idx].sval_essence < 0)
+ return 5;
+
+ al_idx++;
+ if (al_idx >= max_al_idx)
+ return 7;
+ /* Next... */
+ continue;
+ }
+ if (buf[0] == 'a')
+ {
+ int qty;
+ if ( 1 != sscanf(buf + 2, "%d", &qty))
+ {
+ return (1);
+ }
+ s = strrchr(buf, ':');
+ *(s++) = 0;
+ t = strchr(s, ' ');
+ *(t++) = 0;
+ alchemist_recipes[al_idx].tval = 0;
+ alchemist_recipes[al_idx].sval = get_k_flag(s);
+ alchemist_recipes[al_idx].qty = qty;
+ alchemist_recipes[al_idx].sval_essence = init_al_info_essence(t);
+ if (alchemist_recipes[al_idx].sval_essence < 0)
+ return 1;
+
+ al_idx++;
+ if (al_idx >= max_al_idx)
+ return 7; /* 7 is an 'out of memory' error */
+
+ continue;
+ }
+ if (buf[0] == 'A')
+ {
+ int group, level, pval, rtval, rsval, rpval;
+ long xp;
+ /*Verify that complete description information is
+ Recorded for previous Artifact flag
+ */
+ if (a_ptr
+ && (!a_ptr->group || !a_ptr->desc || !a_ptr->item_desc != !a_ptr->rtval)
+ )
+ return (1);
+
+ a_ptr = &a_select_flags[a_idx++];
+
+ if ( 7 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%ld",
+ &group, &rtval, &rsval, &rpval, &pval, &level, &xp))
+ return (1);
+ a_ptr->group = group;
+ a_ptr->rtval = rtval;
+ a_ptr->rsval = rsval;
+ a_ptr->rpval = rpval;
+ a_ptr->pval = pval;
+ a_ptr->level = level;
+ a_ptr->xp = xp;
+ continue;
+ }
+
+ /*Anything else here MUST be a artifact flag line*/
+ if ( !a_ptr)
+ return (3);
+
+ if (buf[0] == 'F')
+ {
+ /* Get the Item flag associated with this */
+ a_ptr->flag = get_k_flag(buf + 2);
+ if (a_ptr->flag == -1)
+ return (1);
+ continue;
+ }
+ if (buf[0] == 'x')
+ {
+ /* Get the activation name associated with this */
+ a_ptr->flag = -get_activation(buf + 2);
+ if (a_ptr->flag == 1)
+ return (1);
+ a_ptr->group = 88;
+ a_ptr->pval = 0;
+ continue;
+ }
+ /* Get the race flags associated with this */
+ if (buf[0] == 'f')
+ {
+ char *s, *t;
+ int idx = 0;
+
+ if ( a_ptr->rflag[0] )
+ {
+ msg_print("duplicate f: entries for one corpse");
+ return (5);
+ }
+
+ if ( a_ptr->rtval != TV_CORPSE )
+ {
+ msg_print("f: section for corpse flags only");
+ return (5);
+ }
+ if ( a_ptr->rpval )
+ {
+ msg_print("Can't specify r_info.txt index with f: section");
+ return (5);
+ }
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ if ( idx > 5 )
+ {
+ msg_print("Limit on race flags is currently 6");
+ return (5);
+ }
+
+ /* Parse this entry */
+ a_ptr->rflag[idx] = get_r_flag(s);
+ if (a_ptr->rflag[idx++] == -1)
+ return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'p' for "Plural Description" */
+ /* Only valid for flag which depend on pval */
+ if (buf[0] == 'p')
+ {
+ /* Reject if doesn't depend on pval */
+
+ if (!a_ptr->pval)
+ return (1);
+
+ /* Acquire the description */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (al_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ a_ptr->item_descp = ++al_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(al_name + al_head->name_size, s);
+
+ /* Advance the index */
+ al_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the description */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (al_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ a_ptr->desc = ++al_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(al_name + al_head->name_size, s);
+
+ /* Advance the index */
+ al_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'd' for "Item Description" */
+ if (buf[0] == 'd')
+ {
+ /* Acquire the name */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (al_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ if (a_ptr->item_desc)
+ return (7);
+
+ /* Advance and Save the name index */
+ a_ptr->item_desc = ++al_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(al_name + al_head->name_size, s);
+
+ /* Advance the index */
+ al_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Hack - set the al_head->text_size to byte size of array */
+ al_head->text_size = (a_idx + 1) * sizeof(artifact_select_flag);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag in an artifact_type from a textual string
+ */
+static errr grab_one_artifact_flag(artifact_type *a_ptr, cptr what, bool obvious)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (obvious)
+ a_ptr->oflags1 |= (1L << i);
+ else
+ a_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (obvious)
+ a_ptr->oflags2 |= (1L << i);
+ else
+ a_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps*/
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (obvious)
+ a_ptr->oflags2 |= (1L << i);
+ else
+ a_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (obvious)
+ a_ptr->oflags3 |= (1L << i);
+ else
+ a_ptr->flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (obvious)
+ a_ptr->oflags4 |= (1L << i);
+ else
+ a_ptr->flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (obvious)
+ a_ptr->oflags5 |= (1L << i);
+ else
+ a_ptr->flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (obvious)
+ a_ptr->oesp |= (1L << i);
+ else
+ a_ptr->esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown artifact flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+
+
+/*
+ * Initialize the "a_info" array, by parsing an ascii "template" file
+ */
+errr init_a_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ artifact_type *a_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != a_head->v_major) ||
+ (v2 != a_head->v_minor) ||
+ (v3 != a_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= a_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ a_ptr = &a_info[i];
+
+ /* Hack -- Verify space */
+ if (a_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!a_ptr->name) a_ptr->name = ++a_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(a_name + a_head->name_size, s);
+
+ /* Advance the index */
+ a_head->name_size += strlen(s);
+
+ /* Ignore everything */
+ a_ptr->flags3 |= (TR3_IGNORE_ACID);
+ a_ptr->flags3 |= (TR3_IGNORE_ELEC);
+ a_ptr->flags3 |= (TR3_IGNORE_FIRE);
+ a_ptr->flags3 |= (TR3_IGNORE_COLD);
+
+ /* Needed hack */
+ a_ptr->esp = 0;
+ a_ptr->power = -1;
+
+ /*Require activating artifacts to have a activation type */
+ if (a_ptr && a_ptr->flags3 & TR3_ACTIVATE && !a_ptr->activate)
+ {
+ msg_print("Activate flag without activate type");
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current a_ptr */
+ if (!a_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (a_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!a_ptr->text) a_ptr->text = ++a_head->text_size;
+
+ /* Append a space at the end of the line, if needed */
+ else if (a_text[a_head->text_size - 1] != ' ')
+ {
+ /* Append the space */
+ strcpy(a_text + a_head->text_size, " ");
+
+ /* Advance the index */
+ a_head->text_size += 1;
+ }
+
+ /* Append chars to the name */
+ strcpy(a_text + a_head->text_size, s);
+
+ /* Advance the index */
+ a_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int tval, sval, pval;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tval, &sval, &pval))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ a_ptr->tval = tval;
+ a_ptr->sval = sval;
+ a_ptr->pval = pval;
+
+ /* Verify */
+ if (!lookup_kind(tval, sval)) return (6);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, rarity, wgt;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &rarity, &wgt, &cost)) return (1);
+
+ /* Save the values */
+ a_ptr->level = level;
+ a_ptr->rarity = rarity;
+ a_ptr->weight = wgt;
+ a_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'P' for "power" and such */
+ if (buf[0] == 'P')
+ {
+ int ac, hd1, hd2, th, td, ta;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d",
+ &ac, &hd1, &hd2, &th, &td, &ta)) return (1);
+
+ a_ptr->ac = ac;
+ a_ptr->dd = hd1;
+ a_ptr->ds = hd2;
+ a_ptr->to_h = th;
+ a_ptr->to_d = td;
+ a_ptr->to_a = ta;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ a_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_artifact_flag(a_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'f' for obvious flags */
+ if (buf[0] == 'f')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_artifact_flag(a_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Read activation type. */
+ if (buf[0] == 'a')
+ {
+ if (prefix(buf + 2, "HARDCORE="))
+ {
+ a_ptr->activate = get_activation(buf + 11);
+ if (a_ptr->activate == -1)
+ return 1;
+ }
+ else if (prefix(buf + 2, "SPELL="))
+ {
+ a_ptr->activate = -find_spell(buf + 8);
+ if (a_ptr->activate == -( -1))
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++a_head->name_size;
+ ++a_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+* Initialize the "set_info" array, by parsing an ascii "template" file
+*/
+errr init_set_info_txt(FILE *fp, char *buf)
+{
+ int i;
+ int cur_art = 0, cur_num = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ set_type *set_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != a_head->v_major) ||
+ (v2 != a_head->v_minor) ||
+ (v3 != a_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ int z, y;
+
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= set_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ set_ptr = &set_info[i];
+
+ /* Hack -- Verify space */
+ if (set_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!set_ptr->name) set_ptr->name = ++set_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(set_name + set_head->name_size, s);
+
+ /* Advance the index */
+ set_head->name_size += strlen(s);
+
+ /* Needed hack */
+ set_ptr->num = 0;
+ set_ptr->num_use = 0;
+ for (z = 0; z < 6; z++)
+ {
+ set_ptr->arts[z].a_idx = 0;
+ set_ptr->arts[z].present = FALSE;
+ for (y = 0; y < 6; y++)
+ {
+ set_ptr->arts[z].flags1[y] = 0;
+ set_ptr->arts[z].flags2[y] = 0;
+ set_ptr->arts[z].flags3[y] = 0;
+ set_ptr->arts[z].flags4[y] = 0;
+ set_ptr->arts[z].flags5[y] = 0;
+ set_ptr->arts[z].esp[y] = 0;
+ set_ptr->arts[z].pval[y] = 0;
+ }
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current set_ptr */
+ if (!set_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (set_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!set_ptr->desc) set_ptr->desc = ++set_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(set_text + set_head->text_size, s);
+
+ /* Advance the index */
+ set_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "Power" (up to 6) */
+ if (buf[0] == 'P')
+ {
+ int a_idx, num, pval;
+ int z;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &a_idx, &num, &pval))
+ {
+ return (1);
+ }
+
+ for (z = 0; z < set_ptr->num; z++)
+ if (set_ptr->arts[z].a_idx == a_idx) break;
+ if (z == set_ptr->num)
+ {
+ set_ptr->num++;
+ set_ptr->arts[z].a_idx = a_idx;
+ }
+
+ /* Save the values */
+ set_ptr->arts[z].pval[num - 1] = pval;
+ cur_art = z;
+ cur_num = num - 1;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&set_ptr->arts[cur_art].flags1[cur_num],
+ &set_ptr->arts[cur_art].flags2[cur_num],
+ &set_ptr->arts[cur_art].flags3[cur_num],
+ &set_ptr->arts[cur_art].flags4[cur_num],
+ &set_ptr->arts[cur_art].flags5[cur_num],
+ &set_ptr->arts[cur_art].esp[cur_num],
+ s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++set_head->name_size;
+ ++set_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize the "s_info" array, by parsing an ascii "template" file
+ */
+errr init_s_info_txt(FILE *fp, char *buf)
+{
+ int i, z, order = 1;
+
+ char *s;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ skill_type *s_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != s_head->v_major) ||
+ (v2 != s_head->v_minor) ||
+ (v3 != s_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'T' for "skill Tree" */
+ if (buf[0] == 'T')
+ {
+ char *sec;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if (s2 == -1) return (1);
+
+ s_info[s2].father = s1;
+ s_info[s2].order = order++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Exclusive" */
+ if (buf[0] == 'E')
+ {
+ char *sec;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if ((s1 == -1) || (s2 == -1)) return (1);
+
+ s_info[s1].action[s2] = SKILL_EXCLUSIVE;
+ s_info[s2].action[s1] = SKILL_EXCLUSIVE;
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'O' for "Opposite" */
+ if (buf[0] == 'O')
+ {
+ char *sec, *cval;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+ if (NULL == (cval = strchr(sec, '%')))
+ {
+ return (1);
+ }
+ *cval = '\0';
+ cval++;
+ if (!*cval) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if ((s1 == -1) || (s2 == -1)) return (1);
+
+ s_info[s1].action[s2] = -atoi(cval);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Amical/friendly" */
+ if (buf[0] == 'f')
+ {
+ char *sec, *cval;
+ s16b s1, s2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+ if (NULL == (cval = strchr(sec, '%')))
+ {
+ return (1);
+ }
+ *cval = '\0';
+ cval++;
+ if (!*cval) return (1);
+
+ s1 = find_skill(buf + 2);
+ s2 = find_skill(sec);
+ if ((s1 == -1) || (s2 == -1)) return (1);
+
+ s_info[s1].action[s2] = atoi(cval);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i >= s_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ s_ptr = &s_info[i];
+
+ /* Hack -- Verify space */
+ if (s_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!s_ptr->name) s_ptr->name = ++s_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(s_name + s_head->name_size, s);
+
+ /* Advance the index */
+ s_head->name_size += strlen(s);
+
+ /* Init */
+ s_ptr->action_mkey = 0;
+ s_ptr->dev = FALSE;
+ s_ptr->random_gain_chance = 100;
+ for (z = 0; z < max_s_idx; z++)
+ {
+ s_ptr->action[z] = 0;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current s_ptr */
+ if (!s_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (s_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!s_ptr->desc)
+ {
+ s_ptr->desc = ++s_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(s_text + s_head->text_size, s);
+
+ /* Advance the index */
+ s_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(s_text + s_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ s_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Activation Description" */
+ if (buf[0] == 'A')
+ {
+ char *txt;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ if (NULL == (txt = strchr(s, ':'))) return (1);
+ *txt = '\0';
+ txt++;
+
+ /* Hack -- Verify space */
+ if (s_head->text_size + strlen(txt) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!s_ptr->action_desc) s_ptr->action_desc = ++s_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(s_text + s_head->text_size, txt);
+ s_ptr->action_mkey = atoi(s);
+
+ /* Advance the index */
+ s_head->text_size += strlen(txt);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int rate;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d", &rate))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ s_ptr->rate = rate;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "random Gain" (one line only) */
+ if (buf[0] == 'G')
+ {
+ int chance;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d", &chance))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ s_ptr->random_gain_chance = chance;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ char *t;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_skill_flag(&(s_ptr->flags1), s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++s_head->name_size;
+ ++s_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ab_info" array, by parsing an ascii "template" file
+ */
+errr init_ab_info_txt(FILE *fp, char *buf)
+{
+ int i, z;
+
+ char *s;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ ability_type *ab_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != ab_head->v_major) ||
+ (v2 != ab_head->v_minor) ||
+ (v3 != ab_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i >= ab_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ab_ptr = &ab_info[i];
+
+ /* Hack -- Verify space */
+ if (ab_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!ab_ptr->name) ab_ptr->name = ++ab_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(ab_name + ab_head->name_size, s);
+
+ /* Advance the index */
+ ab_head->name_size += strlen(s);
+
+ /* Init */
+ ab_ptr->action_mkey = 0;
+ ab_ptr->acquired = FALSE;
+ for (z = 0; z < 10; z++)
+ {
+ ab_ptr->skills[z] = -1;
+ ab_ptr->need_abilities[z] = -1;
+ ab_ptr->forbid_abilities[z] = -1;
+ }
+ for (z = 0; z < 6; z++)
+ {
+ ab_ptr->stat[z] = -1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ab_ptr */
+ if (!ab_ptr) return (3);
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (ab_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!ab_ptr->desc)
+ {
+ ab_ptr->desc = ++ab_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(ab_text + ab_head->text_size, s);
+
+ /* Advance the index */
+ ab_head->text_size += strlen(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strcpy(ab_text + ab_head->text_size, format("\n%s", s));
+
+ /* Advance the index */
+ ab_head->text_size += strlen(s) + 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Activation Description" */
+ if (buf[0] == 'A')
+ {
+ char *txt;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ if (NULL == (txt = strchr(s, ':'))) return (1);
+ *txt = '\0';
+ txt++;
+
+ /* Hack -- Verify space */
+ if (ab_head->text_size + strlen(txt) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!ab_ptr->action_desc) ab_ptr->action_desc = ++ab_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(ab_text + ab_head->text_size, txt);
+ ab_ptr->action_mkey = atoi(s);
+
+ /* Advance the index */
+ ab_head->text_size += strlen(txt);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int cost;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d", &cost))
+ {
+ return (1);
+ }
+
+ /* Save the values */
+ ab_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'k' for "Skill" */
+ if (buf[0] == 'k')
+ {
+ char *sec;
+ s16b level, skill;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ level = atoi(buf + 2);
+ skill = find_skill(sec);
+
+ if ((skill == -1)) return (1);
+
+ for (z = 0; z < 10; z++)
+ if (ab_ptr->skills[z] == -1) break;
+
+ if (z < 10)
+ {
+ ab_ptr->skills[z] = skill;
+ ab_ptr->skill_levels[z] = level;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'a' for "needed ability" */
+ if (buf[0] == 'a')
+ {
+ s16b ab;
+
+ ab = find_ability(buf + 2);
+
+ if ((ab == -1)) return (1);
+
+ for (z = 0; z < 10; z++)
+ if (ab_ptr->need_abilities[z] == -1) break;
+
+ if (z < 10)
+ {
+ ab_ptr->need_abilities[z] = ab;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Stat" */
+ if (buf[0] == 'S')
+ {
+ char *sec;
+ s16b stat;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ for (stat = 0; stat < 6; stat++)
+ {
+ if (!strcmp(stat_names[stat], sec))
+ break;
+ }
+
+ if ((stat == 6)) return (1);
+
+ ab_ptr->stat[stat] = atoi(buf + 2);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Excluding ability" */
+ if (buf[0] == 'E')
+ {
+ char *sec;
+ s16b ab1, ab2;
+
+ /* Scan for the values */
+ if (NULL == (sec = strchr(buf + 2, ':')))
+ {
+ return (1);
+ }
+ *sec = '\0';
+ sec++;
+ if (!*sec) return (1);
+
+ ab1 = find_ability(buf + 2);
+ ab2 = find_ability(sec);
+
+ if ((ab1 == -1) || (ab2 == -1)) return (1);
+
+ for (z = 0; z < 10; z++)
+ if (ab_info[ab1].forbid_abilities[z] == -1) break;
+ if (z < 10)
+ {
+ ab_info[ab1].forbid_abilities[z] = ab2;
+ }
+
+ for (z = 0; z < 10; z++)
+ if (ab_info[ab2].forbid_abilities[z] == -1) break;
+ if (z < 10)
+ {
+ ab_info[ab2].forbid_abilities[z] = ab1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++ab_head->name_size;
+ ++ab_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in a ego-item_type from a textual string
+ */
+static bool grab_one_ego_item_flag(ego_item_type *e_ptr, cptr what, int n, bool obvious)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (obvious)
+ e_ptr->oflags1[n] |= (1L << i);
+ else
+ e_ptr->flags1[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (obvious)
+ e_ptr->oflags2[n] |= (1L << i);
+ else
+ e_ptr->flags2[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (obvious)
+ e_ptr->oflags2[n] |= (1L << i);
+ else
+ e_ptr->flags2[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (obvious)
+ e_ptr->oflags3[n] |= (1L << i);
+ else
+ e_ptr->flags3[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (obvious)
+ e_ptr->oflags4[n] |= (1L << i);
+ else
+ e_ptr->flags4[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (obvious)
+ e_ptr->oflags5[n] |= (1L << i);
+ else
+ e_ptr->flags5[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (obvious)
+ e_ptr->oesp[n] |= (1L << i);
+ else
+ e_ptr->esp[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check ego_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, ego_flags[i]))
+ {
+ e_ptr->fego[n] |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown ego-item flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+static bool grab_one_ego_item_flag_restrict(ego_item_type *e_ptr, cptr what, bool need)
+{
+ int i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ if (need)
+ e_ptr->need_flags1 |= (1L << i);
+ else
+ e_ptr->forbid_flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ if (need)
+ e_ptr->need_flags2 |= (1L << i);
+ else
+ e_ptr->forbid_flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ if (need)
+ e_ptr->need_flags2 |= (1L << i);
+ else
+ e_ptr->forbid_flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ if (need)
+ e_ptr->need_flags3 |= (1L << i);
+ else
+ e_ptr->forbid_flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ if (need)
+ e_ptr->need_flags4 |= (1L << i);
+ else
+ e_ptr->forbid_flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ if (need)
+ e_ptr->need_flags5 |= (1L << i);
+ else
+ e_ptr->forbid_flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ if (need)
+ e_ptr->need_esp |= (1L << i);
+ else
+ e_ptr->forbid_esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown ego-item restrict flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+
+
+/*
+ * Initialize the "e_info" array, by parsing an ascii "template" file
+ */
+errr init_e_info_txt(FILE *fp, char *buf)
+{
+ int i, cur_r = -1, cur_t = 0, j;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ ego_item_type *e_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != e_head->v_major) ||
+ (v2 != e_head->v_minor) ||
+ (v3 != e_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= e_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ e_ptr = &e_info[i];
+
+ /* Hack -- Verify space */
+ if (e_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!e_ptr->name) e_ptr->name = ++e_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(e_name + e_head->name_size, s);
+
+ /* Advance the index */
+ e_head->name_size += strlen(s);
+
+ /* Needed hack */
+ e_ptr->power = -1;
+ cur_r = -1;
+ cur_t = 0;
+
+ for (j = 0; j < 10; j++)
+ {
+ e_ptr->tval[j] = 255;
+ }
+ for (j = 0; j < 5; j++)
+ {
+ e_ptr->rar[j] = 0;
+ e_ptr->flags1[j] = 0;
+ e_ptr->flags2[j] = 0;
+ e_ptr->flags3[j] = 0;
+ e_ptr->flags4[j] = 0;
+ e_ptr->flags5[j] = 0;
+ e_ptr->esp[j] = 0;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current e_ptr */
+ if (!e_ptr) return (3);
+
+
+#if 0
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (e_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!e_ptr->text) e_ptr->text = ++e_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(e_text + e_head->text_size, s);
+
+ /* Advance the index */
+ e_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+#endif
+
+ /* Process 'T' for "Tval/Sval" (up to 5 lines) */
+ if (buf[0] == 'T')
+ {
+ int tv, minsv, maxsv;
+
+ if (cur_t == 10) return 1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tv, &minsv, &maxsv)) return (1);
+
+ /* Save the values */
+ e_ptr->tval[cur_t] = tv;
+ e_ptr->min_sval[cur_t] = minsv;
+ e_ptr->max_sval[cur_t] = maxsv;
+
+ cur_t++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flags rarity" (up to 5 lines) */
+ if (buf[0] == 'R')
+ {
+ int rar;
+
+ if (cur_r == 5) return 1;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &rar)) return (1);
+
+ cur_r++;
+
+ /* Save the values */
+ e_ptr->rar[cur_r] = rar;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'X' for "Xtra" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int slot, rating;
+ char pos;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%c:%d:%d",
+ &pos, &slot, &rating)) return (1);
+
+ /* Save the values */
+ /* e_ptr->slot = slot; */
+ e_ptr->rating = rating;
+ e_ptr->before = (pos == 'B') ? TRUE : FALSE;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, rarity, rarity2;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &rarity, &rarity2, &cost)) return (1);
+
+ /* Save the values */
+ e_ptr->level = level;
+ e_ptr->rarity = rarity;
+ e_ptr->mrarity = rarity2;
+ e_ptr->cost = cost;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'C' for "creation" */
+ if (buf[0] == 'C')
+ {
+ int th, td, ta, pv;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &th, &td, &ta, &pv)) return (1);
+
+ e_ptr->max_to_h = th;
+ e_ptr->max_to_d = td;
+ e_ptr->max_to_a = ta;
+ e_ptr->max_pval = pv;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ e_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ if (buf[0] == 'a')
+ {
+ if (prefix(buf + 2, "HARDCORE="))
+ {
+ e_ptr->activate = get_activation(buf + 11);
+ if (e_ptr->activate == -1)
+ return 1;
+ }
+ else if (prefix(buf + 2, "SPELL="))
+ {
+ e_ptr->activate = -find_spell(buf + 8);
+ if (e_ptr->activate == -( -1))
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'r:N' for needed flags */
+ if ((buf[0] == 'r') && (buf[2] == 'N'))
+ {
+ /* Parse every entry textually */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag_restrict(e_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'r:F' for forbidden flags */
+ if ((buf[0] == 'r') && (buf[2] == 'F'))
+ {
+ /* Parse every entry textually */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag_restrict(e_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ if (cur_r == -1) return (6);
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag(e_ptr, s, cur_r, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'f' for obvious flags */
+ if (buf[0] == 'f')
+ {
+ if (cur_r == -1) return (6);
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_item_flag(e_ptr, s, cur_r, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++e_head->name_size;
+ ++e_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag in a randart_part_type from a textual string
+ */
+static bool grab_one_randart_item_flag(randart_part_type *ra_ptr, cptr what, char c)
+{
+ int i;
+ u32b *f1, *f2, *f3, *f4, *f5, *esp;
+
+ if (c == 'F')
+ {
+ f1 = &ra_ptr->flags1;
+ f2 = &ra_ptr->flags2;
+ f3 = &ra_ptr->flags3;
+ f4 = &ra_ptr->flags4;
+ f5 = &ra_ptr->flags5;
+ esp = &ra_ptr->esp;
+ }
+ else
+ {
+ f1 = &ra_ptr->aflags1;
+ f2 = &ra_ptr->aflags2;
+ f3 = &ra_ptr->aflags3;
+ f4 = &ra_ptr->aflags4;
+ f5 = &ra_ptr->aflags5;
+ esp = &ra_ptr->aesp;
+ }
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags1[i]))
+ {
+ *f1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2[i]))
+ {
+ *f2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags2 -- traps */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags2_trap[i]))
+ {
+ *f2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags3[i]))
+ {
+ *f3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags4[i]))
+ {
+ *f4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, k_info_flags5[i]))
+ {
+ *f5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check esp_flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, esp_flags[i]))
+ {
+ *esp |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Check ego_flags */
+ if (c == 'F')
+ {
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, ego_flags[i]))
+ {
+ ra_ptr->fego |= (1L << i);
+ return (0);
+ }
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown ego-item flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+
+
+/*
+ * Initialize the "ra_info" array, by parsing an ascii "template" file
+ */
+errr init_ra_info_txt(FILE *fp, char *buf)
+{
+ int i, cur_t = 0, j, cur_g = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ randart_part_type *ra_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != ra_head->v_major) ||
+ (v2 != ra_head->v_minor) ||
+ (v3 != ra_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'G' for "General" (up to 30 lines) */
+ if (buf[0] == 'G')
+ {
+ int chance, dd, ds, plus;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%dd%d:%d",
+ &chance, &dd, &ds, &plus)) return (1);
+
+ /* Save the values */
+ ra_gen[cur_g].chance = chance;
+ ra_gen[cur_g].dd = dd;
+ ra_gen[cur_g].ds = ds;
+ ra_gen[cur_g].plus = plus;
+ cur_g++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'N' for "New/Number" */
+ if (buf[0] == 'N')
+ {
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= ra_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ra_ptr = &ra_info[i];
+
+ /* Needed hack */
+ ra_ptr->power = -1;
+ cur_t = 0;
+
+ for (j = 0; j < 20; j++)
+ {
+ ra_ptr->tval[j] = 255;
+ }
+ ra_ptr->flags1 = 0;
+ ra_ptr->flags2 = 0;
+ ra_ptr->flags3 = 0;
+ ra_ptr->flags4 = 0;
+ ra_ptr->flags5 = 0;
+ ra_ptr->esp = 0;
+ ra_ptr->fego = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ra_ptr */
+ if (!ra_ptr) return (3);
+
+ /* Process 'T' for "Tval/Sval" (up to 5 lines) */
+ if (buf[0] == 'T')
+ {
+ int tv, minsv, maxsv;
+
+ if (cur_t == 20) return 1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tv, &minsv, &maxsv)) return (1);
+
+ /* Save the values */
+ ra_ptr->tval[cur_t] = tv;
+ ra_ptr->min_sval[cur_t] = minsv;
+ ra_ptr->max_sval[cur_t] = maxsv;
+
+ cur_t++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'X' for "Xtra" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int power, max;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &power, &max)) return (1);
+
+ /* Save the values */
+ ra_ptr->value = power;
+ ra_ptr->max = max;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int level, rarity, rarity2;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &level, &rarity, &rarity2)) return (1);
+
+ /* Save the values */
+ ra_ptr->level = level;
+ ra_ptr->rarity = rarity;
+ ra_ptr->mrarity = rarity2;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'C' for "creation" */
+ if (buf[0] == 'C')
+ {
+ int th, td, ta, pv;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &th, &td, &ta, &pv)) return (1);
+
+ ra_ptr->max_to_h = th;
+ ra_ptr->max_to_d = td;
+ ra_ptr->max_to_a = ta;
+ ra_ptr->max_pval = pv;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'Z' for "Granted power" */
+ if (buf[0] == 'Z')
+ {
+ int i;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Find it in the list */
+ for (i = 0; i < power_max; i++)
+ {
+ if (!stricmp(s, powers_type[i].name)) break;
+ }
+
+ if (i == power_max) return (6);
+
+ ra_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_randart_item_flag(ra_ptr, s, 'F')) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Hack -- Process 'A' for antagonic flags */
+ if (buf[0] == 'A')
+ {
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_randart_item_flag(ra_ptr, s, 'A')) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_basic_flag(monster_race *r_ptr, cptr what)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ r_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ r_ptr->flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ r_ptr->flags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ r_ptr->flags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ r_ptr->flags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ r_ptr->flags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Grab one (spell) flag in a monster_race from a textual string
+ */
+static errr grab_one_spell_flag(monster_race *r_ptr, cptr what)
+{
+ int i;
+
+ /* Scan flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags4[i]))
+ {
+ r_ptr->flags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags5[i]))
+ {
+ r_ptr->flags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags6 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags6[i]))
+ {
+ r_ptr->flags6 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Initialize the "r_info" array, by parsing an ascii "template" file
+ */
+errr init_r_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ monster_race *r_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ r_head->name_size = 0;
+ r_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != r_head->v_major) ||
+ (v2 != r_head->v_minor) ||
+ (v3 != r_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+/* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= r_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ r_ptr = &r_info[i];
+
+ /* Hack -- Verify space */
+ if (r_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!r_ptr->name) r_ptr->name = ++r_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(r_name + r_head->name_size, s);
+
+ /* Advance the index */
+ r_head->name_size += strlen(s);
+
+ /* HACK -- Those ones HAVE to have a set default value */
+ r_ptr->drops.treasure = OBJ_GENE_TREASURE;
+ r_ptr->drops.combat = OBJ_GENE_COMBAT;
+ r_ptr->drops.magic = OBJ_GENE_MAGIC;
+ r_ptr->drops.tools = OBJ_GENE_TOOL;
+ r_ptr->freq_inate = r_ptr->freq_spell = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current r_ptr */
+ if (!r_ptr) return (3);
+
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (r_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!r_ptr->text) r_ptr->text = ++r_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(r_text + r_head->text_size, s);
+
+ /* Advance the index */
+ r_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ char sym;
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the char */
+ sym = buf[2];
+
+ /* Extract the attr */
+ tmp = color_char_to_attr(buf[4]);
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ r_ptr->d_char = sym;
+ r_ptr->d_attr = tmp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int spd, hp1, hp2, aaf, ac, slp;
+
+ /* Scan for the other values */
+ if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d",
+ &spd, &hp1, &hp2, &aaf, &ac, &slp)) return (1);
+
+ /* Save the values */
+ r_ptr->speed = spd;
+ r_ptr->hdice = hp1;
+ r_ptr->hside = hp2;
+ r_ptr->aaf = aaf;
+ r_ptr->ac = ac;
+ r_ptr->sleep = slp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Body Parts" (one line only) */
+ if (buf[0] == 'E')
+ {
+ int weap, tors, fing, head, arms, legs;
+
+ /* Scan for the other values */
+ if (BODY_MAX != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &weap, &tors, &arms, &fing, &head, &legs)) return (1);
+
+ /* Save the values */
+ r_ptr->body_parts[BODY_WEAPON] = weap;
+ r_ptr->body_parts[BODY_TORSO] = tors;
+ r_ptr->body_parts[BODY_ARMS] = arms;
+ r_ptr->body_parts[BODY_FINGER] = fing;
+ r_ptr->body_parts[BODY_HEAD] = head;
+ r_ptr->body_parts[BODY_LEGS] = legs;
+
+ /* Mega debugging hack */
+ if (weap > arms) quit(format("monster %d, %d weapon(s), %d arm(s) !", error_idx, weap, arms));
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object type" (one line only) */
+ if (buf[0] == 'O')
+ {
+ int treasure, combat, magic, tools;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &treasure, &combat, &magic, &tools)) return (1);
+
+ /* Save the values */
+ r_ptr->drops.treasure = treasure;
+ r_ptr->drops.combat = combat;
+ r_ptr->drops.magic = magic;
+ r_ptr->drops.tools = tools;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int lev, rar, wt;
+ long exp;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &lev, &rar, &wt, &exp)) return (1);
+
+ /* Save the values */
+ r_ptr->level = lev;
+ r_ptr->rarity = rar;
+ /* MEGA HACK */
+ if (!wt) wt = 100;
+ r_ptr->weight = wt;
+ r_ptr->mexp = exp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "Blows" (up to four lines) */
+ if (buf[0] == 'B')
+ {
+ int n1, n2;
+
+ /* Find the next empty blow slot (if any) */
+ for (i = 0; i < 4; i++) if (!r_ptr->blow[i].method) break;
+
+ /* Oops, no more slots */
+ if (i == 4) return (1);
+
+ /* Analyze the first field */
+ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze the method */
+ for (n1 = 0; r_info_blow_method[n1]; n1++)
+ {
+ if (streq(s, r_info_blow_method[n1])) break;
+ }
+
+ /* Invalid method */
+ if (!r_info_blow_method[n1]) return (1);
+
+ /* Analyze the second field */
+ for (s = t; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze effect */
+ for (n2 = 0; r_info_blow_effect[n2]; n2++)
+ {
+ if (streq(s, r_info_blow_effect[n2])) break;
+ }
+
+ /* Invalid effect */
+ if (!r_info_blow_effect[n2]) return (1);
+
+ /* Analyze the third field */
+ for (s = t; *t && (*t != 'd'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == 'd') *t++ = '\0';
+
+ /* Save the method */
+ r_ptr->blow[i].method = n1;
+
+ /* Save the effect */
+ r_ptr->blow[i].effect = n2;
+
+ /* Extract the damage dice and sides */
+ r_ptr->blow[i].d_dice = atoi(s);
+ r_ptr->blow[i].d_side = atoi(t);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Basic Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_flag(r_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Spell Flags" (multiple lines) */
+ if (buf[0] == 'S')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read spell frequency */
+ if (1 == sscanf(s, "1_IN_%d", &i))
+ {
+ /* Extract a "frequency" */
+ r_ptr->freq_spell = r_ptr->freq_inate = 100 / i;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_flag(r_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++r_head->name_size;
+ ++r_head->text_size;
+
+ for (i = 1; i < max_r_idx; i++)
+ {
+ /* Invert flag WILD_ONLY <-> RF8_DUNGEON */
+ r_info[i].flags8 ^= 1L;
+
+ /* WILD_TOO without any other wilderness flags enables all flags */
+ if ((r_info[i].flags8 & RF8_WILD_TOO) && !(r_info[i].flags8 & 0x7FFFFFFE))
+ r_info[i].flags8 = 0x0463;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_basic_ego_flag(monster_ego *re_ptr, cptr what, bool add)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ if (add)
+ re_ptr->mflags1 |= (1L << i);
+ else
+ re_ptr->nflags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ if (add)
+ re_ptr->mflags2 |= (1L << i);
+ else
+ re_ptr->nflags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ if (add)
+ re_ptr->mflags3 |= (1L << i);
+ else
+ re_ptr->nflags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ if (add)
+ re_ptr->mflags7 |= (1L << i);
+ else
+ re_ptr->nflags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ if (add)
+ re_ptr->mflags8 |= (1L << i);
+ else
+ re_ptr->nflags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ if (add)
+ re_ptr->mflags9 |= (1L << i);
+ else
+ re_ptr->nflags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Grab one (spell) flag in a monster_race from a textual string
+ */
+static errr grab_one_spell_ego_flag(monster_ego *re_ptr, cptr what, bool add)
+{
+ int i;
+
+ /* Scan flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags4[i]))
+ {
+ if (add)
+ re_ptr->mflags4 |= (1L << i);
+ else
+ re_ptr->nflags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags5[i]))
+ {
+ if (add)
+ re_ptr->mflags5 |= (1L << i);
+ else
+ re_ptr->nflags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags6 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags6[i]))
+ {
+ if (add)
+ re_ptr->mflags6 |= (1L << i);
+ else
+ re_ptr->nflags6 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_ego_flag(monster_ego *re_ptr, cptr what, bool must)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ if (must) re_ptr->flags1 |= (1L << i);
+ else re_ptr->hflags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ if (must) re_ptr->flags2 |= (1L << i);
+ else re_ptr->hflags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ if (must) re_ptr->flags3 |= (1L << i);
+ else re_ptr->hflags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ if (must) re_ptr->flags7 |= (1L << i);
+ else re_ptr->hflags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ if (must) re_ptr->flags8 |= (1L << i);
+ else re_ptr->hflags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ if (must) re_ptr->flags9 |= (1L << i);
+ else re_ptr->hflags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "re_info" array, by parsing an ascii "template" file
+ */
+errr init_re_info_txt(FILE *fp, char *buf)
+{
+ int i, j;
+
+ byte blow_num = 0;
+ int r_char_number = 0, nr_char_number = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ monster_ego *re_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ re_head->name_size = 0;
+ re_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != re_head->v_major) ||
+ (v2 != re_head->v_minor) ||
+ (v3 != re_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= re_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ re_ptr = &re_info[i];
+
+ /* Hack -- Verify space */
+ if (re_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!re_ptr->name) re_ptr->name = ++re_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(re_name + re_head->name_size, s);
+
+ /* Advance the index */
+ re_head->name_size += strlen(s);
+
+ /* Some inits */
+ blow_num = 0;
+ r_char_number = 0;
+ nr_char_number = 0;
+ for (j = 0; j < 5; j++) re_ptr->r_char[j] = 0;
+ for (j = 0; j < 5; j++) re_ptr->nr_char[j] = 0;
+ for (j = 0; j < 4; j++)
+ {
+ re_ptr->blow[j].method = 0;
+ re_ptr->blow[j].effect = 0;
+ re_ptr->blow[j].d_dice = 0;
+ re_ptr->blow[j].d_side = 0;
+ re_ptr->blowm[j][0] = MEGO_ADD;
+ re_ptr->blowm[j][1] = MEGO_ADD;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current re_ptr */
+ if (!re_ptr) return (3);
+
+ /* Process 'G' for "Graphics" (one line only) */
+ if (buf[0] == 'G')
+ {
+ char sym;
+ int tmp;
+
+ /* Paranoia */
+ if (!buf[2]) return (1);
+ if (!buf[3]) return (1);
+ if (!buf[4]) return (1);
+
+ /* Extract the char */
+ if (buf[2] != '*') sym = buf[2];
+ else sym = MEGO_CHAR_ANY;
+
+ /* Extract the attr */
+ if (buf[4] != '*') tmp = color_char_to_attr(buf[4]);
+ else tmp = MEGO_CHAR_ANY;
+
+ /* Paranoia */
+ if (tmp < 0) return (1);
+
+ /* Save the values */
+ re_ptr->d_char = sym;
+ re_ptr->d_attr = tmp;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int spd, hp1, hp2, aaf, ac, slp;
+ char mspd, mhp1, mhp2, maaf, mac, mslp;
+
+ /* Scan for the other values */
+ if (12 != sscanf(buf + 2, "%c%d:%c%dd%c%d:%c%d:%c%d:%c%d",
+ &mspd, &spd, &mhp1, &hp1, &mhp2, &hp2, &maaf, &aaf, &mac, &ac, &mslp, &slp)) return (1);
+
+ /* Save the values */
+ re_ptr->speed = (spd << 2) + monster_ego_modify(mspd);
+ re_ptr->hdice = (hp1 << 2) + monster_ego_modify(mhp1);
+ re_ptr->hside = (hp2 << 2) + monster_ego_modify(mhp2);
+ re_ptr->aaf = (aaf << 2) + monster_ego_modify(maaf);
+ re_ptr->ac = (ac << 2) + monster_ego_modify(mac);
+ re_ptr->sleep = (slp << 2) + monster_ego_modify(mslp);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int lev, rar, wt;
+ char mlev, mwt, mexp, pos;
+ long exp;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 2, "%c%d:%d:%c%d:%c%ld:%c",
+ &mlev, &lev, &rar, &mwt, &wt, &mexp, &exp, &pos)) return (1);
+
+ /* Save the values */
+ re_ptr->level = (lev << 2) + monster_ego_modify(mlev);
+ re_ptr->rarity = rar;
+ re_ptr->weight = (wt << 2) + monster_ego_modify(mwt);
+ re_ptr->mexp = (exp << 2) + monster_ego_modify(mexp);
+ re_ptr->before = (pos == 'B') ? TRUE : FALSE;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "Blows" (up to four lines) */
+ if (buf[0] == 'B')
+ {
+ int n1, n2, dice, side;
+ char mdice, mside;
+
+ /* Oops, no more slots */
+ if (blow_num == 4) return (1);
+
+ /* Analyze the first field */
+ for (s = t = buf + 2; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze the method */
+ for (n1 = 0; r_info_blow_method[n1]; n1++)
+ {
+ if (streq(s, r_info_blow_method[n1])) break;
+ }
+
+ /* Invalid method */
+ if (!r_info_blow_method[n1]) return (1);
+
+ /* Analyze the second field */
+ for (s = t; *t && (*t != ':'); t++) /* loop */;
+
+ /* Terminate the field (if necessary) */
+ if (*t == ':') *t++ = '\0';
+
+ /* Analyze effect */
+ for (n2 = 0; r_info_blow_effect[n2]; n2++)
+ {
+ if (streq(s, r_info_blow_effect[n2])) break;
+ }
+
+ /* Invalid effect */
+ if (!r_info_blow_effect[n2]) return (1);
+
+ /* Save the method */
+ re_ptr->blow[blow_num].method = n1;
+
+ /* Save the effect */
+ re_ptr->blow[blow_num].effect = n2;
+
+ /* Extract the damage dice and sides */
+ if (4 != sscanf(t, "%c%dd%c%d",
+ &mdice, &dice, &mside, &side)) return (1);
+
+ re_ptr->blow[blow_num].d_dice = dice;
+ re_ptr->blow[blow_num].d_side = side;
+ re_ptr->blowm[blow_num][0] = monster_ego_modify(mdice);
+ re_ptr->blowm[blow_num][1] = monster_ego_modify(mside);
+ blow_num++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Flags monster must have" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ char r_char;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read monster symbols */
+ if (1 == sscanf(s, "R_CHAR_%c", &r_char))
+ {
+ /* Limited to 5 races */
+ if (r_char_number >= 5) continue;
+
+ /* Extract a "frequency" */
+ re_ptr->r_char[r_char_number++] = r_char;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_flag(re_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'H' for "Flags monster must not have" (multiple lines) */
+ if (buf[0] == 'H')
+ {
+ char r_char;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read monster symbols */
+ if (1 == sscanf(s, "R_CHAR_%c", &r_char))
+ {
+ /* Limited to 5 races */
+ if (nr_char_number >= 5) continue;
+
+ /* Extract a "frequency" */
+ re_ptr->nr_char[nr_char_number++] = r_char;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_ego_flag(re_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Basic Monster Flags" (multiple lines) */
+ if (buf[0] == 'M')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_ego_flag(re_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Basic Monster -Flags" (multiple lines) */
+ if (buf[0] == 'O')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read no flags */
+ if (!strcmp(s, "MF_ALL"))
+ {
+ /* No flags */
+ re_ptr->nflags1 = re_ptr->nflags2 = re_ptr->nflags3 = re_ptr->nflags7 = re_ptr->nflags8 = re_ptr->nflags9 = 0xFFFFFFFF;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_ego_flag(re_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Spell Flags" (multiple lines) */
+ if (buf[0] == 'S')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read spell frequency */
+ if (1 == sscanf(s, "1_IN_%d", &i))
+ {
+ /* Extract a "frequency" */
+ re_ptr->freq_spell = re_ptr->freq_inate = 100 / i;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_ego_flag(re_ptr, s, TRUE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'T' for "Spell -Flags" (multiple lines) */
+ if (buf[0] == 'T')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* XXX XXX XXX Hack -- Read no flags */
+ if (!strcmp(s, "MF_ALL"))
+ {
+ /* No flags */
+ re_ptr->nflags4 = re_ptr->nflags5 = re_ptr->nflags6 = 0xFFFFFFFF;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_ego_flag(re_ptr, s, FALSE)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++re_head->name_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Grab one flag in an trap_type from a textual string
+ */
+static errr grab_one_trap_type_flag(trap_type *t_ptr, cptr what)
+{
+ s16b i;
+
+ /* Check flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, t_info_flags[i]))
+ {
+ t_ptr->flags |= (1L << i);
+ return (0);
+ }
+ }
+ /* Oops */
+ msg_format("Unknown trap_type flag '%s'.", what);
+
+ /* Error */
+ return (1);
+}
+
+
+/*
+ * Initialize the "tr_info" array, by parsing an ascii "template" file
+ */
+errr init_t_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ trap_type *t_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Prepare the "fake" stuff */
+ t_head->name_size = 0;
+ t_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != t_head->v_major) ||
+ (v2 != t_head->v_minor) ||
+ (v3 != t_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+/* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i <= error_idx) return (4);
+
+ /* Verify information */
+ if (i >= t_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ t_ptr = &t_info[i];
+
+ /* Hack -- Verify space */
+ if (t_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!t_ptr->name) t_ptr->name = ++t_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(t_name + t_head->name_size, s);
+
+ /* Advance the index */
+ t_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current t_ptr */
+ if (!t_ptr) return (3);
+
+
+ /* Process 'I' for "Information" */
+ if (buf[0] == 'I')
+ {
+ int probability, another, p1valinc, difficulty;
+ int minlevel;
+ int dd, ds;
+ char color;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%dd%d:%c",
+ &difficulty, &probability, &another,
+ &p1valinc, &minlevel, &dd, &ds,
+ &color)) return (1);
+
+ t_ptr->difficulty = (byte)difficulty;
+ t_ptr->probability = (s16b)probability;
+ t_ptr->another = (s16b)another;
+ t_ptr->p1valinc = (s16b)p1valinc;
+ t_ptr->minlevel = (byte)minlevel;
+ t_ptr->dd = (s16b)dd;
+ t_ptr->ds = (s16b)ds;
+ t_ptr->color = color_char_to_attr(color);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Process 'D' for "Description" */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (t_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!t_ptr->text) t_ptr->text = ++t_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(t_text + t_head->text_size, s);
+
+ /* Advance the index */
+ t_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Hack -- Process 'F' for flags */
+ if (buf[0] == 'F')
+ {
+
+ t_ptr->flags = 0;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_trap_type_flag(t_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++t_head->name_size;
+ ++t_head->text_size;
+
+
+ /* No version yet */
+ if (!okay) return (2);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag for a dungeon type from a textual string
+ */
+errr grab_one_dungeon_flag(u32b *flags1, u32b *flags2, cptr what)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, d_info_flags1[i]))
+ {
+ *flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, d_info_flags2[i]))
+ {
+ *flags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown dungeon type flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one (basic) flag in a monster_race from a textual string
+ */
+static errr grab_one_basic_monster_flag(dungeon_info_type *d_ptr, cptr what, byte rule)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags1[i]))
+ {
+ d_ptr->rules[rule].mflags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags2 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags2[i]))
+ {
+ d_ptr->rules[rule].mflags2 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags3 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags3[i]))
+ {
+ d_ptr->rules[rule].mflags3 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags7 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags7[i]))
+ {
+ d_ptr->rules[rule].mflags7 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags8 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags8[i]))
+ {
+ d_ptr->rules[rule].mflags8 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags9 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags9[i]))
+ {
+ d_ptr->rules[rule].mflags9 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+ * Grab one (spell) flag in a monster_race from a textual string
+ */
+static errr grab_one_spell_monster_flag(dungeon_info_type *d_ptr, cptr what, byte rule)
+{
+ int i;
+
+ /* Scan flags4 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags4[i]))
+ {
+ d_ptr->rules[rule].mflags4 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags5 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags5[i]))
+ {
+ d_ptr->rules[rule].mflags5 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Scan flags6 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, r_info_flags6[i]))
+ {
+ d_ptr->rules[rule].mflags6 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "d_info" array, by parsing an ascii "template" file
+ */
+errr init_d_info_txt(FILE *fp, char *buf)
+{
+ int i, j;
+
+ s16b rule_num = 0;
+
+ byte r_char_number = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ dungeon_info_type *d_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ d_head->name_size = 0;
+ d_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != d_head->v_major) ||
+ (v2 != d_head->v_minor) ||
+ (v3 != d_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= d_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ d_ptr = &d_info[i];
+
+ /* Hack -- Verify space */
+ if (d_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!d_ptr->name) d_ptr->name = ++d_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(d_name + d_head->name_size, s);
+
+ /* Advance the index */
+ d_head->name_size += strlen(s);
+
+ /* HACK -- Those ones HAVE to have a set default value */
+ d_ptr->size_x = -1;
+ d_ptr->size_y = -1;
+ d_ptr->ix = -1;
+ d_ptr->iy = -1;
+ d_ptr->ox = -1;
+ d_ptr->oy = -1;
+ d_ptr->fill_method = 1;
+ rule_num = -1;
+ r_char_number = 0;
+ for (j = 0; j < 5; j++)
+ {
+ int k;
+
+ d_ptr->rules[j].mode = DUNGEON_MODE_NONE;
+ d_ptr->rules[j].percent = 0;
+
+ for (k = 0; k < 5; k++) d_ptr->rules[j].r_char[k] = 0;
+ }
+
+ /* HACK -- Those ones HAVE to have a set default value */
+ d_ptr->objs.treasure = OBJ_GENE_TREASURE;
+ d_ptr->objs.combat = OBJ_GENE_COMBAT;
+ d_ptr->objs.magic = OBJ_GENE_MAGIC;
+ d_ptr->objs.tools = OBJ_GENE_TOOL;
+
+ /* The default generator */
+ strcpy(d_ptr->generator, "dungeon");
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current d_ptr */
+ if (!d_ptr) return (3);
+
+ /* Process 'D' for "Description */
+ if (buf[0] == 'D')
+ {
+ /* Acquire short name */
+ d_ptr->short_name[0] = buf[2];
+ d_ptr->short_name[1] = buf[3];
+ d_ptr->short_name[2] = buf[4];
+
+ /* Acquire the text */
+ s = buf + 6;
+
+ /* Hack -- Verify space */
+ if (d_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!d_ptr->text) d_ptr->text = ++d_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(d_text + d_head->text_size, s);
+
+ /* Advance the index */
+ d_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int min_lev, max_lev;
+ int min_plev, next;
+ int min_alloc, max_chance;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &min_lev, &max_lev, &min_plev, &next, &min_alloc, &max_chance)) return (1);
+
+ /* Save the values */
+ d_ptr->mindepth = min_lev;
+ d_ptr->maxdepth = max_lev;
+ d_ptr->min_plev = min_plev;
+ d_ptr->next = next;
+ d_ptr->min_m_alloc_level = min_alloc;
+ d_ptr->max_m_alloc_chance = max_chance;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'L' for "fLoor type" (one line only) */
+ if (buf[0] == 'L')
+ {
+ int f1, f2, f3;
+ int p1, p2, p3;
+ int i;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &f1, &p1, &f2, &p2, &f3, &p3))
+ {
+ /* Scan for the values - part ii*/
+ if (3 != sscanf(buf + 2, "%d:%d:%d", &p1, &p2,
+ &p3)) return (1);
+
+ /* Save the values */
+ d_ptr->floor_percent1[1] = p1;
+ d_ptr->floor_percent2[1] = p2;
+ d_ptr->floor_percent3[1] = p3;
+
+ continue;
+ }
+
+ /* Save the values */
+ d_ptr->floor1 = f1;
+ d_ptr->floor2 = f2;
+ d_ptr->floor3 = f3;
+
+ for (i = 0; i < 2; i++)
+ {
+ d_ptr->floor_percent1[i] = p1;
+ d_ptr->floor_percent2[i] = p2;
+ d_ptr->floor_percent3[i] = p3;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Object type" (one line only) */
+ if (buf[0] == 'O')
+ {
+ int treasure, combat, magic, tools;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &treasure, &combat, &magic, &tools)) return (1);
+
+ /* Save the values */
+ d_ptr->objs.treasure = treasure;
+ d_ptr->objs.combat = combat;
+ d_ptr->objs.magic = magic;
+ d_ptr->objs.tools = tools;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Generator" (one line only) */
+ if (buf[0] == 'G')
+ {
+ strnfmt(d_ptr->generator, 30, "%s", buf + 2);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "wAll type" (one line only) */
+ if (buf[0] == 'A')
+ {
+ int w1, w2, w3, outer, inner;
+ int p1, p2, p3;
+ int i;
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &w1, &p1, &w2, &p2, &w3, &p3, &outer, &inner))
+ {
+ /* Scan for the values - part ii*/
+ if (3 != sscanf(buf + 2, "%d:%d:%d", &p1, &p2,
+ &p3)) return (1);
+
+ /* Save the values */
+ d_ptr->fill_percent1[1] = p1;
+ d_ptr->fill_percent2[1] = p2;
+ d_ptr->fill_percent3[1] = p3;
+ continue;
+ }
+
+ /* Save the values */
+ d_ptr->fill_type1 = w1;
+ d_ptr->fill_type2 = w2;
+ d_ptr->fill_type3 = w3;
+
+ for (i = 0; i < 2; i++)
+ {
+ d_ptr->fill_percent1[i] = p1;
+ d_ptr->fill_percent2[i] = p2;
+ d_ptr->fill_percent3[i] = p3;
+ }
+
+ d_ptr->outer_wall = outer;
+ d_ptr->inner_wall = inner;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'E' for "Effects" (up to four lines) -SC- */
+ if (buf[0] == 'E')
+ {
+ int side, dice, freq, type;
+ cptr tmp;
+
+ /* Find the next empty blow slot (if any) */
+ for (i = 0; i < 4; i++) if ((!d_ptr->d_side[i]) &&
+ (!d_ptr->d_dice[i])) break;
+
+ /* Oops, no more slots */
+ if (i == 4) return (1);
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%dd%d:%d:%d",
+ &dice, &side, &freq, &type))
+ {
+ int j;
+
+ if (3 != sscanf(buf + 2, "%dd%d:%d",
+ &dice, &side, &freq)) return (1);
+
+ tmp = buf + 2;
+ for (j = 0; j < 2; j++)
+ {
+ tmp = strchr(tmp, ':');
+ if (tmp == NULL) return (1);
+ tmp++;
+ }
+
+ j = 0;
+
+ while (d_info_dtypes[j].name != NULL)
+ if (strcmp(d_info_dtypes[j].name, tmp) == 0)
+ {
+ d_ptr->d_type[i] = d_info_dtypes[j].feat;
+ break;
+ }
+ else j++;
+
+ if (d_info_dtypes[j].name == NULL) return (1);
+ }
+ else
+ d_ptr->d_type[i] = type;
+
+ freq *= 10;
+ /* Save the values */
+ d_ptr->d_side[i] = side;
+ d_ptr->d_dice[i] = dice;
+ d_ptr->d_frequency[i] = freq;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Dungeon Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ int artif = 0, monst = 0, obj = 0;
+ int ix = -1, iy = -1, ox = -1, oy = -1;
+ int fill_method;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Read dungeon in/out coords */
+ if (4 == sscanf(s, "WILD_%d_%d__%d_%d", &ix, &iy, &ox, &oy))
+ {
+ d_ptr->ix = ix;
+ d_ptr->iy = iy;
+ d_ptr->ox = ox;
+ d_ptr->oy = oy;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read dungeon size */
+ if (2 == sscanf(s, "SIZE_%d_%d", &ix, &iy))
+ {
+ d_ptr->size_x = ix;
+ d_ptr->size_y = iy;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read dungeon fill method */
+ if (1 == sscanf(s, "FILL_METHOD_%d", &fill_method))
+ {
+ d_ptr->fill_method = fill_method;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read Final Object */
+ if (1 == sscanf(s, "FINAL_OBJECT_%d", &obj))
+ {
+ /* Extract a "Final Artifact" */
+ d_ptr->final_object = obj;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read Final Artifact */
+ if (1 == sscanf(s, "FINAL_ARTIFACT_%d", &artif ))
+ {
+ /* Extract a "Final Artifact" */
+ d_ptr->final_artifact = artif ;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Read Artifact Guardian */
+ if (1 == sscanf(s, "FINAL_GUARDIAN_%d", &monst))
+ {
+ /* Extract a "Artifact Guardian" */
+ d_ptr->final_guardian = monst;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&(d_ptr->flags1), &(d_ptr->flags2), s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "monster generation Rule" (up to 5 lines) */
+ if (buf[0] == 'R')
+ {
+ int percent, mode;
+ int z, y, lims[5];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &percent, &mode)) return (1);
+
+ /* Save the values */
+ r_char_number = 0;
+ rule_num++;
+
+ d_ptr->rules[rule_num].percent = percent;
+ d_ptr->rules[rule_num].mode = mode;
+
+ /* Lets remap the flat percents */
+ lims[0] = d_ptr->rules[0].percent;
+ for (y = 1; y <= rule_num; y++)
+ {
+ lims[y] = lims[y - 1] + d_ptr->rules[y].percent;
+ }
+ for (z = 0; z < 100; z++)
+ {
+ for (y = rule_num; y >= 0; y--)
+ {
+ if (z < lims[y]) d_ptr->rule_percents[z] = y;
+ }
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'M' for "Basic Flags" (multiple lines) */
+ if (buf[0] == 'M')
+ {
+ byte r_char;
+
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Read monster symbols */
+ if (1 == sscanf(s, "R_CHAR_%c", &r_char))
+ {
+ /* Limited to 5 races */
+ if (r_char_number >= 5) continue;
+
+ /* Extract a "frequency" */
+ d_ptr->rules[rule_num].r_char[r_char_number++] = r_char;
+
+ /* Start at next entry */
+ s = t;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_basic_monster_flag(d_ptr, s, rule_num)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'S' for "Spell Flags" (multiple lines) */
+ if (buf[0] == 'S')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while ((*t == ' ') || (*t == '|')) t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_spell_monster_flag(d_ptr, s, rule_num)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++d_head->name_size;
+ ++d_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one race flag from a textual string
+ */
+static errr grab_one_race_flag(owner_type *ow_ptr, int state, cptr what)
+{
+ /* int i;
+ cptr s; */
+
+ /* Scan race flags */
+ unknown_shut_up = TRUE;
+ if (!grab_one_race_allow_flag(ow_ptr->races[state], what))
+ {
+ unknown_shut_up = FALSE;
+ return (0);
+ }
+
+ /* Scan classes flags */
+ if (!grab_one_class_flag(ow_ptr->classes[state], what))
+ {
+ unknown_shut_up = FALSE;
+ return (0);
+ }
+
+ /* Oops */
+ unknown_shut_up = FALSE;
+ msg_format("Unknown race/class flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Grab one store flag from a textual string
+ */
+static errr grab_one_store_flag(store_info_type *st_ptr, cptr what)
+{
+ int i;
+
+ /* Scan store flags */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, st_info_flags1[i]))
+ {
+ st_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown store flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "st_info" array, by parsing an ascii "template" file
+ */
+errr init_st_info_txt(FILE *fp, char *buf)
+{
+ int i = 0, item_idx = 0;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ store_info_type *st_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ st_head->name_size = 0;
+ st_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != st_head->v_major) ||
+ (v2 != st_head->v_minor) ||
+ (v3 != st_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= st_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ st_ptr = &st_info[i];
+
+ /* Hack -- Verify space */
+ if (st_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!st_ptr->name) st_ptr->name = ++st_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(st_name + st_head->name_size, s);
+
+ /* Advance the index */
+ st_head->name_size += strlen(s);
+
+ /* We are ready for a new set of objects */
+ item_idx = 0;
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current st_ptr */
+ if (!st_ptr) return (3);
+
+ /* Process 'I' for "Items" (multiple lines) */
+ if (buf[0] == 'I')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ st_ptr->table[item_idx][1] = atoi(buf + 2);
+
+ /* Append chars to the name */
+ st_ptr->table[item_idx++][0] = test_item_name(s);
+
+ st_ptr->table_num = item_idx;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'T' for "Tval/sval" */
+ if (buf[0] == 'T')
+ {
+ int tv1, sv1, rar1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &rar1, &tv1, &sv1)) return (1);
+
+ /* Get the index */
+ st_ptr->table[item_idx][1] = rar1;
+ /* Hack -- 256 as a sval means all possible items */
+ st_ptr->table[item_idx++][0] = (sv1 < 256) ? lookup_kind(tv1, sv1) : tv1 + 10000;
+
+ st_ptr->table_num = item_idx;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Graphics" one line only) */
+ if (buf[0] == 'G')
+ {
+ char c, a;
+ int attr;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%c:%c",
+ &c, &a)) return (1);
+
+ /* Extract the color */
+ attr = color_char_to_attr(a);
+
+ /* Paranoia */
+ if (attr < 0) return (1);
+
+ /* Save the values */
+ st_ptr->d_char = c;
+ st_ptr->d_attr = attr;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Actions" (one line only) */
+ if (buf[0] == 'A')
+ {
+ int a1, a2, a3, a4, a5, a6;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d",
+ &a1, &a2, &a3, &a4, &a5, &a6)) return (1);
+
+ /* Save the values */
+ st_ptr->actions[0] = a1;
+ st_ptr->actions[1] = a2;
+ st_ptr->actions[2] = a3;
+ st_ptr->actions[3] = a4;
+ st_ptr->actions[4] = a5;
+ st_ptr->actions[5] = a6;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "store Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_store_flag(st_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'O' for "Owners" (one line only) */
+ if (buf[0] == 'O')
+ {
+ int a1, a2, a3, a4;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%d",
+ &a1, &a2, &a3, &a4)) return (1);
+
+ /* Save the values */
+ st_ptr->owners[0] = a1;
+ st_ptr->owners[1] = a2;
+ st_ptr->owners[2] = a3;
+ st_ptr->owners[3] = a4;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "Extra info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int max_obj;
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &max_obj)) return (1);
+
+ /* Save the values */
+ if (max_obj > STORE_INVEN_MAX) max_obj = STORE_INVEN_MAX;
+ st_ptr->max_obj = max_obj;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++st_head->name_size;
+ ++st_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ba_info" array, by parsing an ascii "template" file
+ */
+errr init_ba_info_txt(FILE *fp, char *buf)
+{
+ int i = 0;
+
+ char *s;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ store_action_type *ba_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ ba_head->name_size = 0;
+ ba_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != ba_head->v_major) ||
+ (v2 != ba_head->v_minor) ||
+ (v3 != ba_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= ba_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ba_ptr = &ba_info[i];
+
+ /* Hack -- Verify space */
+ if (ba_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!ba_ptr->name) ba_ptr->name = ++ba_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(ba_name + ba_head->name_size, s);
+
+ /* Advance the index */
+ ba_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ba_ptr */
+ if (!ba_ptr) return (3);
+
+ /* Process 'C' for "Costs" (one line only) */
+ if (buf[0] == 'C')
+ {
+ int ch, cn, cl;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &ch, &cn, &cl)) return (1);
+
+ /* Save the values */
+ ba_ptr->costs[STORE_HATED] = ch;
+ ba_ptr->costs[STORE_NORMAL] = cn;
+ ba_ptr->costs[STORE_LIKED] = cl;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Infos" (one line only) */
+ if (buf[0] == 'I')
+ {
+ int act, act_res;
+ char letter, letter_aux = 0;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%c:%c", &act, &act_res, &letter, &letter_aux))
+ if (3 != sscanf(buf + 2, "%d:%d:%c", &act, &act_res, &letter))
+ return (1);
+
+ /* Save the values */
+ ba_ptr->action = act;
+ ba_ptr->action_restr = act_res;
+ ba_ptr->letter = letter;
+ ba_ptr->letter_aux = letter_aux;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++ba_head->name_size;
+ ++ba_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ow_info" array, by parsing an ascii "template" file
+ */
+errr init_ow_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ owner_type *ow_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ ow_head->name_size = 0;
+ ow_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != ow_head->v_major) ||
+ (v2 != ow_head->v_minor) ||
+ (v3 != ow_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= ow_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ow_ptr = &ow_info[i];
+
+ /* Hack -- Verify space */
+ if (ow_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!ow_ptr->name) ow_ptr->name = ++ow_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(ow_name + ow_head->name_size, s);
+
+ /* Advance the index */
+ ow_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current ow_ptr */
+ if (!ow_ptr) return (3);
+
+
+ /* Process 'C' for "Costs" (one line only) */
+ if (buf[0] == 'C')
+ {
+ int ch, cn, cl;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &ch, &cn, &cl)) return (1);
+
+ /* Save the values */
+ ow_ptr->costs[STORE_HATED] = ch;
+ ow_ptr->costs[STORE_NORMAL] = cn;
+ ow_ptr->costs[STORE_LIKED] = cl;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'I' for "Info" (multiple lines line only) */
+ if (buf[0] == 'I')
+ {
+ int cost, max_inf, min_inf, haggle, insult;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 2, "%d:%d:%d:%d:%d",
+ &cost, &max_inf, &min_inf, &haggle, &insult)) return (1);
+
+ /* Save the values */
+ ow_ptr->max_cost = cost;
+ ow_ptr->max_inflate = max_inf;
+ ow_ptr->min_inflate = min_inf;
+ ow_ptr->haggle_per = haggle;
+ ow_ptr->insult_max = insult;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'L' for "Liked races/classes" (multiple lines) */
+ if (buf[0] == 'L')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_flag(ow_ptr, STORE_LIKED, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+ /* Process 'H' for "Hated races/classes" (multiple lines) */
+ if (buf[0] == 'H')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_flag(ow_ptr, STORE_HATED, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++ow_head->name_size;
+ ++ow_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Grab one flag for a dungeon type from a textual string
+ */
+static errr grab_one_wf_info_flag(wilderness_type_info *wf_ptr, cptr what)
+{
+ int i;
+
+ /* Scan flags1 */
+ for (i = 0; i < 32; i++)
+ {
+ if (streq(what, wf_info_flags1[i]))
+ {
+ wf_ptr->flags1 |= (1L << i);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ msg_format("Unknown monster flag '%s'.", what);
+
+ /* Failure */
+ return (1);
+}
+
+/*
+ * Initialize the "wf_info" array, by parsing an ascii "template" file
+ */
+errr init_wf_info_txt(FILE *fp, char *buf)
+{
+ int i;
+
+ char *s, *t;
+
+ /* Not ready yet */
+ bool okay = FALSE;
+
+ /* Current entry */
+ wilderness_type_info *wf_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Start the "fake" stuff */
+ wf_head->name_size = 0;
+ wf_head->text_size = 0;
+
+ /* Parse */
+ fp_stack_init(fp);
+ while (0 == my_fgets_dostack(buf, 1024))
+ {
+ /* Advance the line number */
+ error_line++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Verify correct "colon" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Hack -- Process 'V' for "Version" */
+ if (buf[0] == 'V')
+ {
+ int v1, v2, v3;
+
+#ifdef VERIFY_VERSION_STAMP
+
+ /* Scan for the values */
+ if ((3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) ||
+ (v1 != wf_head->v_major) ||
+ (v2 != wf_head->v_minor) ||
+ (v3 != wf_head->v_patch))
+ {
+ return (2);
+ }
+
+#else /* VERIFY_VERSION_STAMP */
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d.%d.%d", &v1, &v2, &v3)) return (2);
+
+#endif /* VERIFY_VERSION_STAMP */
+
+ /* Okay to proceed */
+ okay = TRUE;
+
+ /* Continue */
+ continue;
+ }
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Included file */
+ if (buf[0] == '<')
+ {
+ fp_stack_push(buf + 2);
+ continue;
+ }
+
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[0] == 'N')
+ {
+ /* Find the colon before the name */
+ s = strchr(buf + 2, ':');
+
+ /* Verify that colon */
+ if (!s) return (1);
+
+ /* Nuke the colon, advance to the name */
+ *s++ = '\0';
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+
+ /* Get the index */
+ i = atoi(buf + 2);
+
+ /* Verify information */
+ if (i < error_idx) return (4);
+
+ /* Verify information */
+ if (i >= wf_head->info_num) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ wf_ptr = &wf_info[i];
+
+ /* Hack -- Verify space */
+ if (wf_head->name_size + strlen(s) + 8 > fake_name_size) return (7);
+
+ /* Advance and Save the name index */
+ if (!wf_ptr->name) wf_ptr->name = ++wf_head->name_size;
+
+ /* Append chars to the name */
+ strcpy(wf_name + wf_head->name_size, s);
+
+ /* Advance the index */
+ wf_head->name_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current wf_ptr */
+ if (!wf_ptr) return (3);
+
+ /* Process 'D' for "Description */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Hack -- Verify space */
+ if (wf_head->text_size + strlen(s) + 8 > fake_text_size) return (7);
+
+ /* Advance and Save the text index */
+ if (!wf_ptr->text) wf_ptr->text = ++wf_head->text_size;
+
+ /* Append chars to the name */
+ strcpy(wf_text + wf_head->text_size, s);
+
+ /* Advance the index */
+ wf_head->text_size += strlen(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'W' for "More Info" (one line only) */
+ if (buf[0] == 'W')
+ {
+ int entrance, level;
+ int road, feat, ter_idx;
+ char car;
+
+ /* Scan for the values */
+ if (6 != sscanf(buf + 2, "%d:%d:%d:%d:%d:%c",
+ &level, &entrance, &road, &feat, &ter_idx, &car)) return (1);
+
+ /* Save the values */
+ wf_ptr->level = level;
+ wf_ptr->entrance = entrance;
+ wf_ptr->road = road;
+ wf_ptr->feat = feat;
+ wf_ptr->terrain_idx = ter_idx;
+
+ /* To acces it easily from the map structure */
+ wildc2i[(int)car] = error_idx;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'X' for "More Info" (one line only) */
+ if (buf[0] == 'X')
+ {
+ int terrain[MAX_WILD_TERRAIN], i;
+
+ /* Scan for the values */
+ if (MAX_WILD_TERRAIN != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
+ &terrain[0], &terrain[1], &terrain[2],
+ &terrain[3], &terrain[4], &terrain[5],
+ &terrain[6], &terrain[7], &terrain[8],
+ &terrain[9], &terrain[10], &terrain[11],
+ &terrain[12], &terrain[13], &terrain[14],
+ &terrain[15], &terrain[16], &terrain[17])) return (1);
+
+ /* Save the values */
+ for (i = 0; i < MAX_WILD_TERRAIN; i++)
+ {
+ wf_ptr->terrain[i] = terrain[i];
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "Wilderness feature Flags" (multiple lines) */
+ if (buf[0] == 'F')
+ {
+ /* Parse every entry */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_wf_info_flag(wf_ptr, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+
+ /* Complete the "name" and "text" sizes */
+ ++wf_head->name_size;
+ ++wf_head->text_size;
+
+ /* No version yet */
+ if (!okay) return (2);
+
+ /* Success */
+ return (0);
+}
+
+
+#else /* ALLOW_TEMPLATES */
+
+#ifdef MACINTOSH
+static int i = 0;
+#endif
+
+#endif /* ALLOW_TEMPLATES */
+
+
+/* Random dungeon grid effects */
+#define RANDOM_NONE 0x00
+#define RANDOM_FEATURE 0x01
+#define RANDOM_MONSTER 0x02
+#define RANDOM_OBJECT 0x04
+#define RANDOM_EGO 0x08
+#define RANDOM_ARTIFACT 0x10
+#define RANDOM_TRAP 0x20
+
+
+typedef struct dungeon_grid dungeon_grid;
+
+struct dungeon_grid
+{
+ int feature; /* Terrain feature */
+ int monster; /* Monster */
+ int object; /* Object */
+ int ego; /* Ego-Item */
+ int artifact; /* Artifact */
+ int trap; /* Trap */
+ int cave_info; /* Flags for CAVE_MARK, CAVE_GLOW, CAVE_ICKY, CAVE_ROOM */
+ int special; /* Reserved for special terrain info */
+ int random; /* Number of the random effect */
+ int bx, by; /* For between gates */
+ int mimic; /* Mimiced features */
+ bool ok;
+ bool defined;
+};
+static bool meta_sleep = TRUE;
+
+static dungeon_grid letter[255];
+
+/*
+ * Parse a sub-file of the "extra info"
+ */
+bool process_dungeon_file_full = FALSE;
+static errr process_dungeon_file_aux(char *buf, int *yval, int *xval, int xvalstart, int ymax, int xmax)
+{
+ int i;
+
+ char *zz[33];
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) return (0);
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) return (0);
+
+ /* Skip comments */
+ if (buf[0] == '#') return (0);
+
+ /* Require "?:*" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Process "%:<fname>" */
+ if (buf[0] == '%')
+ {
+ /* Attempt to Process the given file */
+ return (process_dungeon_file(NULL, buf + 2, yval, xval, ymax, xmax, FALSE));
+ }
+
+ /* Process "N:<sleep>" */
+ if (buf[0] == 'N')
+ {
+ int num;
+
+ if ((num = tokenize(buf + 2, 1, zz, ':', '/')) > 0)
+ {
+ meta_sleep = atoi(zz[0]);
+ }
+
+ return (0);
+ }
+
+ /* Process "F:<letter>:<terrain>:<cave_info>:<monster>:<object>:<ego>:<artifact>:<trap>:<special>:<mimic>" -- info for dungeon grid */
+ if (buf[0] == 'F')
+ {
+ int num;
+
+ if ((num = tokenize(buf + 2, 10, zz, ':', '/')) > 1)
+ {
+ int index = zz[0][0];
+
+ /* Reset the feature */
+ letter[index].feature = 0;
+ letter[index].monster = 0;
+ letter[index].object = 0;
+ letter[index].ego = 0;
+ letter[index].artifact = 0;
+ letter[index].trap = 0;
+ letter[index].cave_info = 0;
+ letter[index].special = 0;
+ letter[index].random = 0;
+ letter[index].mimic = 0;
+ letter[index].ok = TRUE;
+ letter[index].defined = TRUE;
+
+ if (num > 1)
+ {
+ if (zz[1][0] == '*')
+ {
+ letter[index].random |= RANDOM_FEATURE;
+ if (zz[1][1])
+ {
+ zz[1]++;
+ letter[index].feature = atoi(zz[1]);
+ }
+ }
+ else
+ {
+ letter[index].feature = atoi(zz[1]);
+ }
+ }
+
+ if (num > 2)
+ letter[index].cave_info = atoi(zz[2]);
+
+ /* Monster */
+ if (num > 3)
+ {
+ if (zz[3][0] == '*')
+ {
+ letter[index].random |= RANDOM_MONSTER;
+ if (zz[3][1])
+ {
+ zz[3]++;
+ letter[index].monster = atoi(zz[3]);
+ }
+ }
+ else
+ {
+ letter[index].monster = atoi(zz[3]);
+ }
+ }
+
+ /* Object */
+ if (num > 4)
+ {
+ if (zz[4][0] == '*')
+ {
+ letter[index].random |= RANDOM_OBJECT;
+
+ if (zz[4][1])
+ {
+ zz[4]++;
+ letter[index].object = atoi(zz[4]);
+ }
+ }
+ else
+ {
+ letter[index].object = atoi(zz[4]);
+ }
+ }
+
+ /* Ego-Item */
+ if (num > 5)
+ {
+ if (zz[5][0] == '*')
+ {
+ letter[index].random |= RANDOM_EGO;
+
+ if (zz[5][1])
+ {
+ zz[5]++;
+ letter[index].ego = atoi(zz[5]);
+ }
+ }
+ else
+ {
+ letter[index].ego = atoi(zz[5]);
+ }
+ }
+
+ /* Artifact */
+ if (num > 6)
+ {
+ if (zz[6][0] == '*')
+ {
+ letter[index].random |= RANDOM_ARTIFACT;
+
+ if (zz[6][1])
+ {
+ zz[6]++;
+ letter[index].artifact = atoi(zz[6]);
+ }
+ }
+ else
+ {
+ letter[index].artifact = atoi(zz[6]);
+ }
+ }
+
+ if (num > 7)
+ {
+ if (zz[7][0] == '*')
+ {
+ letter[index].random |= RANDOM_TRAP;
+
+ if (zz[7][1])
+ {
+ zz[7]++;
+ letter[index].trap = atoi(zz[7]);
+ }
+ }
+ else
+ letter[index].trap = atoi(zz[7]);
+ }
+
+ if (num > 8)
+ {
+ /* Quests can be defined by name only */
+ if (zz[8][0] == '"')
+ {
+ int i;
+
+ /* Hunt & shoot the ending " */
+ i = strlen(zz[8]) - 1;
+ if (zz[8][i] == '"') zz[8][i] = '\0';
+ letter[index].special = 0;
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (!strcmp(&zz[8][1], quest[i].name))
+ {
+ letter[index].special = i;
+ break;
+ }
+ }
+ }
+ else
+ letter[index].special = atoi(zz[8]);
+ }
+
+ if (num > 9)
+ {
+ letter[index].mimic = atoi(zz[9]);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Process "f:flags" -- level flags */
+ else if (buf[0] == 'f')
+ {
+ char *s, *t;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&dungeon_flags1, &dungeon_flags2, s)) return 1;
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ return 0;
+ }
+
+ /* Process "D:<dungeon>" -- info for the cave grids */
+ else if (buf[0] == 'D')
+ {
+ int x;
+
+ object_type object_type_body;
+
+ /* Acquire the text */
+ char *s = buf + 2;
+
+ /* Length of the text */
+ int len = strlen(s);
+
+ int y = *yval;
+ *xval = xvalstart;
+ for (x = *xval, i = 0; ((x < xmax) && (i < len)); x++, s++, i++)
+ {
+ /* Access the grid */
+ cave_type *c_ptr = &cave[y][x];
+
+ int idx = s[0];
+
+ int object_index = letter[idx].object;
+ int monster_index = letter[idx].monster;
+ int random = letter[idx].random;
+ int artifact_index = letter[idx].artifact;
+
+ if (!letter[idx].ok) msg_format("Warning '%c' not defined but used.", idx);
+
+ if (init_flags & INIT_GET_SIZE) continue;
+
+ /* use the plasma generator wilderness */
+ if (((!dun_level) || (!letter[idx].defined)) && (idx == ' ')) continue;
+
+ /* Clear some info */
+ c_ptr->info = 0;
+
+ /* Lay down a floor */
+ c_ptr->mimic = letter[idx].mimic;
+ cave_set_feat(y, x, letter[idx].feature);
+
+ /* Cave info */
+ c_ptr->info |= letter[idx].cave_info;
+
+ /* Create a monster */
+ if (random & RANDOM_MONSTER)
+ {
+ int level = monster_level;
+
+ monster_level = quest[p_ptr->inside_quest].level + monster_index;
+
+ place_monster(y, x, meta_sleep, FALSE);
+
+ monster_level = level;
+ }
+ else if (monster_index)
+ {
+ /* Place it */
+ m_allow_special[monster_index] = TRUE;
+ place_monster_aux(y, x, monster_index, meta_sleep, FALSE, MSTATUS_ENEMY);
+ m_allow_special[monster_index] = FALSE;
+ }
+
+ /* Object (and possible trap) */
+ if ((random & RANDOM_OBJECT) && (random & RANDOM_TRAP))
+ {
+ int level = object_level;
+
+ object_level = quest[p_ptr->inside_quest].level;
+
+ /*
+ * Random trap and random treasure defined
+ * 25% chance for trap and 75% chance for object
+ */
+ if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+
+ object_level = level;
+ }
+ else if (random & RANDOM_OBJECT)
+ {
+ /* Create an out of deep object */
+ if (object_index)
+ {
+ int level = object_level;
+
+ object_level = quest[p_ptr->inside_quest].level + object_index;
+ if (rand_int(100) < 75)
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ else if (rand_int(100) < 80)
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_SPECIAL);
+ else
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_SPECIAL);
+
+ object_level = level;
+ }
+ else if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else if (rand_int(100) < 80)
+ {
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else
+ {
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_SPECIAL);
+ }
+ }
+ /* Random trap */
+ else if (random & RANDOM_TRAP)
+ {
+ place_trap(y, x);
+ }
+ else if (object_index)
+ {
+ /* Get local object */
+ object_type *o_ptr = &object_type_body;
+
+ k_allow_special[object_index] = TRUE;
+
+ /* Create the item */
+ object_prep(o_ptr, object_index);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(o_ptr, dun_level, FALSE, TRUE, FALSE);
+
+ o_ptr->found = OBJ_FOUND_SPECIAL;
+
+ k_allow_special[object_index] = FALSE;
+
+ drop_near(o_ptr, -1, y, x);
+ }
+
+ /* Artifact */
+ if (artifact_index)
+ {
+ int I_kind = 0;
+
+ artifact_type *a_ptr = &a_info[artifact_index];
+
+ object_type forge;
+
+ /* Get local object */
+ object_type *q_ptr = &forge;
+
+ a_allow_special[artifact_index] = TRUE;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = artifact_index;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+ q_ptr->found = OBJ_FOUND_SPECIAL;
+
+ random_artifact_resistance(q_ptr);
+
+ a_info[artifact_index].cur_num = 1;
+
+ a_allow_special[artifact_index] = FALSE;
+
+ /* It's amazing that this "creating objects anywhere"
+ junk ever worked.
+ Let's just HACK around one observed bug: Shadow Cloak
+ of Luthien [Globe of Light] */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f5 & TR5_SPELL_CONTAIN)
+ q_ptr->pval2 = -1;
+ }
+
+ /* Drop the artifact */
+ drop_near(q_ptr, -1, y, x);
+
+ }
+
+ /* Terrain special */
+ if (letter[idx].special == -1)
+ {
+ if (!letter[idx].bx)
+ {
+ letter[idx].bx = x;
+ letter[idx].by = y;
+ }
+ else
+ {
+ c_ptr->special = (letter[idx].by << 8) + letter[idx].bx;
+ cave[letter[idx].by][letter[idx].bx].special = (y << 8) + x;
+ }
+ }
+ else
+ {
+ c_ptr->special = letter[idx].special;
+ }
+ }
+ if ((process_dungeon_file_full) && (*xval < x)) *xval = x;
+ (*yval)++;
+
+ return (0);
+ }
+
+ /* Process "W:<command>: ..." -- info for the wilderness */
+ else if (buf[0] == 'W')
+ {
+ /* Process "W:D:<layout> */
+ /* Layout of the wilderness */
+ if (buf[2] == 'D')
+ {
+ int x;
+ char i;
+
+ /* Acquire the text */
+ char *s = buf + 4;
+
+ int y = *yval;
+
+ for (x = 0; x < max_wild_x; x++)
+ {
+ if (1 != sscanf(s + x, "%c", &i)) return (1);
+ wild_map[y][x].feat = wildc2i[(int)i];
+
+ /*
+ * If this is a town/dungeon entrance, note
+ * its coordinates. (Have to check for
+ * duplicate Morias...)
+ */
+ if (wf_info[wildc2i[(int)i]].entrance &&
+ wf_info[wildc2i[(int)i]].wild_x == 0)
+ {
+ wf_info[wildc2i[(int)i]].wild_x = x;
+ wf_info[wildc2i[(int)i]].wild_y = y;
+ }
+ }
+
+ (*yval)++;
+
+ return (0);
+ }
+ /* Process "M:<plus>:<line>" -- move line lines */
+ else if (buf[2] == 'M')
+ {
+ if (tokenize(buf + 4, 2, zz, ':', '/') == 2)
+ {
+ if (atoi(zz[0]))
+ {
+ (*yval) += atoi(zz[1]);
+ }
+ else
+ {
+ (*yval) -= atoi(zz[1]);
+ }
+ }
+ else
+ {
+ return (1);
+ }
+ return (0);
+ }
+ /* Process "W:P:<x>:<y> - starting position in the wilderness */
+ else if (buf[2] == 'P')
+ {
+ if ((p_ptr->wilderness_x == 0) &&
+ (p_ptr->wilderness_y == 0))
+ {
+ if (tokenize(buf + 4, 2, zz, ':', '/') == 2)
+ {
+ p_ptr->wilderness_x = atoi(zz[0]);
+ p_ptr->wilderness_y = atoi(zz[1]);
+ }
+ else
+ {
+ return (1);
+ }
+ }
+
+ return (0);
+ }
+ /* Process "W:E:<dungeon>:<y>:<x> - entrance to the dungeon <dungeon> */
+ else if (buf[2] == 'E')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ wild_map[atoi(zz[1])][atoi(zz[2])].entrance = 1000 + atoi(zz[0]);
+ }
+ else
+ {
+ return (1);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Process "P:<x>:<y>" -- player position */
+ else if (buf[0] == 'P')
+ {
+ if (init_flags & INIT_CREATE_DUNGEON)
+ {
+ if (tokenize(buf + 2, 2, zz, ':', '/') == 2)
+ {
+ /* Place player in a quest level */
+ if (p_ptr->inside_quest || (init_flags & INIT_POSITION))
+ {
+ p_ptr->py = atoi(zz[0]);
+ p_ptr->px = atoi(zz[1]);
+ }
+ /* Place player in the town */
+ else if ((p_ptr->oldpx == 0) && (p_ptr->oldpy == 0))
+ {
+ p_ptr->oldpy = atoi(zz[0]);
+ p_ptr->oldpx = atoi(zz[1]);
+ }
+ }
+ }
+
+ return (0);
+ }
+
+ /* Process "M:<type>:<maximum>" -- set maximum values */
+ else if (buf[0] == 'M')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') >= 2)
+ {
+ /* Maximum towns */
+ if (zz[0][0] == 'T')
+ {
+ max_towns = atoi(zz[1]);
+ }
+
+ /* Maximum real towns */
+ if (zz[0][0] == 't')
+ {
+ max_real_towns = atoi(zz[1]);
+ }
+
+ /* Maximum r_idx */
+ else if (zz[0][0] == 'R')
+ {
+ max_r_idx = atoi(zz[1]);
+ }
+
+ /* Maximum re_idx */
+ else if (zz[0][0] == 'r')
+ {
+ max_re_idx = atoi(zz[1]);
+ }
+
+ /* Maximum s_idx */
+ else if (zz[0][0] == 'k')
+ {
+ max_s_idx = atoi(zz[1]);
+ if (max_s_idx > MAX_SKILLS) return (1);
+ }
+
+ /* Maximum ab_idx */
+ else if (zz[0][0] == 'b')
+ {
+ max_ab_idx = atoi(zz[1]);
+ }
+
+ /* Maximum k_idx */
+ else if (zz[0][0] == 'K')
+ {
+ max_k_idx = atoi(zz[1]);
+ }
+
+ /* Maximum v_idx */
+ else if (zz[0][0] == 'V')
+ {
+ max_v_idx = atoi(zz[1]);
+ }
+
+ /* Maximum f_idx */
+ else if (zz[0][0] == 'F')
+ {
+ max_f_idx = atoi(zz[1]);
+ }
+
+ /* Maximum a_idx */
+ else if (zz[0][0] == 'A')
+ {
+ max_a_idx = atoi(zz[1]);
+ }
+
+ /* Maximum al_idx */
+ else if (zz[0][0] == 'a')
+ {
+ max_al_idx = atoi(zz[1]);
+ }
+
+ /* Maximum e_idx */
+ else if (zz[0][0] == 'E')
+ {
+ max_e_idx = atoi(zz[1]);
+ }
+
+ /* Maximum ra_idx */
+ else if (zz[0][0] == 'Z')
+ {
+ max_ra_idx = atoi(zz[1]);
+ }
+
+ /* Maximum o_idx */
+ else if (zz[0][0] == 'O')
+ {
+ max_o_idx = atoi(zz[1]);
+ }
+
+ /* Maximum player types */
+ else if (zz[0][0] == 'P')
+ {
+ if (zz[1][0] == 'R')
+ {
+ max_rp_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'S')
+ {
+ max_rmp_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'C')
+ {
+ max_c_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'M')
+ {
+ max_mc_idx = atoi(zz[2]);
+ }
+ else if (zz[1][0] == 'H')
+ {
+ max_bg_idx = atoi(zz[2]);
+ }
+ }
+
+ /* Maximum m_idx */
+ else if (zz[0][0] == 'M')
+ {
+ max_m_idx = atoi(zz[1]);
+ }
+
+ /* Maximum tr_idx */
+ else if (zz[0][0] == 'U')
+ {
+ max_t_idx = atoi(zz[1]);
+ }
+
+ /* Maximum wf_idx */
+ else if (zz[0][0] == 'W')
+ {
+ max_wf_idx = atoi(zz[1]);
+ }
+
+ /* Maximum ba_idx */
+ else if (zz[0][0] == 'B')
+ {
+ max_ba_idx = atoi(zz[1]);
+ }
+
+ /* Maximum st_idx */
+ else if (zz[0][0] == 'S')
+ {
+ max_st_idx = atoi(zz[1]);
+ }
+
+ /* Maximum set_idx */
+ else if (zz[0][0] == 's')
+ {
+ max_set_idx = atoi(zz[1]);
+ }
+
+ /* Maximum ow_idx */
+ else if (zz[0][0] == 'N')
+ {
+ max_ow_idx = atoi(zz[1]);
+ }
+
+ /* Maximum wilderness x size */
+ else if (zz[0][0] == 'X')
+ {
+ max_wild_x = atoi(zz[1]);
+ }
+
+ /* Maximum wilderness y size */
+ else if (zz[0][0] == 'Y')
+ {
+ max_wild_y = atoi(zz[1]);
+ }
+
+ /* Maximum d_idx */
+ else if (zz[0][0] == 'D')
+ {
+ max_d_idx = atoi(zz[1]);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Failure */
+ return (1);
+}
+
+
+
+
+/*
+ * Helper function for "process_dungeon_file()"
+ */
+static cptr process_dungeon_file_expr(char **sp, char *fp)
+{
+ cptr v;
+
+ char *b;
+ char *s;
+
+ char b1 = '[';
+ char b2 = ']';
+
+ char f = ' ';
+
+ /* Initial */
+ s = (*sp);
+
+ /* Skip spaces */
+ while (isspace(*s)) s++;
+
+ /* Save start */
+ b = s;
+
+ /* Default */
+ v = "?o?o?";
+
+ /* Analyze */
+ if (*s == b1)
+ {
+ const char *p;
+ const char *t;
+
+ /* Skip b1 */
+ s++;
+
+ /* First */
+ t = process_dungeon_file_expr(&s, &f);
+
+ /* Oops */
+ if (!*t)
+ {
+ /* Nothing */
+ }
+
+ /* Function: IOR */
+ else if (streq(t, "IOR"))
+ {
+ v = "0";
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && !streq(t, "0")) v = "1";
+ }
+ }
+
+ /* Function: AND */
+ else if (streq(t, "AND"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && streq(t, "0")) v = "0";
+ }
+ }
+
+ /* Function: NOT */
+ else if (streq(t, "NOT"))
+ {
+ v = "1";
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && streq(t, "1")) v = "0";
+ }
+ }
+
+ /* Function: EQU */
+ else if (streq(t, "EQU"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && !streq(p, t)) v = "0";
+ }
+ }
+
+ /* Function: LEQ */
+ else if (streq(t, "LEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) > 0)) v = "0";
+ }
+ }
+
+ /* Function: GEQ */
+ else if (streq(t, "GEQ"))
+ {
+ v = "1";
+ if (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ while (*s && (f != b2))
+ {
+ p = t;
+ t = process_dungeon_file_expr(&s, &f);
+ if (*t && (strcmp(p, t) < 0)) v = "0";
+ }
+ }
+
+ /* Oops */
+ else
+ {
+ while (*s && (f != b2))
+ {
+ t = process_dungeon_file_expr(&s, &f);
+ }
+ }
+
+ /* Verify ending */
+ if (f != b2) v = "?x?x?";
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+ }
+
+ /* Other */
+ else
+ {
+ bool text_mode = FALSE;
+
+ /* Accept all printables except spaces and brackets */
+ while (isprint(*s))
+ {
+ if (*s == '"') text_mode = !text_mode;
+ if (!text_mode)
+ {
+ if (strchr(" []", *s))
+ break;
+ }
+ else
+ {
+ if (strchr("[]", *s))
+ break;
+ }
+
+ ++s;
+ }
+
+ /* Extract final and Terminate */
+ if ((f = *s) != '\0') * s++ = '\0';
+
+ /* Variable */
+ if (*b == '$')
+ {
+ /* System */
+ if (streq(b + 1, "SYS"))
+ {
+ v = ANGBAND_SYS;
+ }
+
+ /* Graphics */
+ else if (streq(b + 1, "GRAF"))
+ {
+ v = ANGBAND_GRAF;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACE"))
+ {
+ v = rp_ptr->title + rp_name;
+ }
+
+ /* Race Mod */
+ else if (streq(b + 1, "RACEMOD"))
+ {
+ v = rmp_ptr->title + rmp_name;
+ }
+
+ /* Class */
+ else if (streq(b + 1, "CLASS"))
+ {
+ v = cp_ptr->title + c_name;
+ }
+
+ /* Player */
+ else if (streq(b + 1, "PLAYER"))
+ {
+ v = player_base;
+ }
+
+ /* Town */
+ else if (streq(b + 1, "TOWN"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", p_ptr->town_num);
+ v = pref_tmp_value;
+ }
+
+ /* Town destroyed */
+ else if (prefix(b + 1, "TOWN_DESTROY"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", town_info[atoi(b + 13)].destroyed);
+ v = pref_tmp_value;
+ }
+
+ /* Current quest number */
+ else if (streq(b + 1, "QUEST_NUMBER"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", p_ptr->inside_quest);
+ v = pref_tmp_value;
+ }
+
+ /* Number of last quest */
+ else if (streq(b + 1, "LEAVING_QUEST"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", leaving_quest);
+ v = pref_tmp_value;
+ }
+
+ /* DAYTIME status */
+ else if (prefix(b + 1, "DAYTIME"))
+ {
+ if ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18))
+ v = "1";
+ else
+ v = "0";
+ }
+
+ /* Quest status */
+ else if (prefix(b + 1, "QUEST"))
+ {
+ /* "QUEST" uses a special parameter to determine the number of the quest */
+ if (*(b + 6) != '"')
+ strnfmt(pref_tmp_value, 8, "%d", quest[atoi(b + 6)].status);
+ else
+ {
+ char c[80];
+ int i;
+
+ /* Copy it to temp array, so that we can modify it safely */
+ strcpy(c, b + 7);
+
+ /* Hunt & shoot the ending " */
+ for (i = 0; (c[i] != '"') && (c[i] != '\0'); i++);
+ if (c[i] == '"') c[i] = '\0';
+ strcpy(pref_tmp_value, "-1");
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (streq(c, quest[i].name))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", quest[i].status);
+ break;
+ }
+ }
+ }
+ v = pref_tmp_value;
+ }
+
+ /* Variant name */
+ else if (streq(b + 1, "VARIANT"))
+ {
+ v = "ToME";
+ }
+
+ /* Wilderness */
+ else if (streq(b + 1, "WILDERNESS"))
+ {
+ if (vanilla_town) v = "NONE";
+ else v = "NORMAL";
+ }
+ }
+
+ /* Constant */
+ else
+ {
+ v = b;
+ }
+ }
+
+ /* Save */
+ (*fp) = f;
+
+ /* Save */
+ (*sp) = s;
+
+ /* Result */
+ return (v);
+}
+
+
+errr process_dungeon_file(cptr full_text, cptr name, int *yval, int *xval, int ymax, int xmax, bool init)
+{
+ FILE *fp = 0;
+
+ char buf[1024];
+
+ int num = -1, i;
+
+ errr err = 0;
+
+ bool bypass = FALSE;
+
+ /* Save the start since it ought to be modified */
+ int xmin = *xval;
+
+ if (init)
+ {
+ meta_sleep = TRUE;
+ for (i = 0; i < 255; i++)
+ {
+ letter[i].defined = FALSE;
+ if (i == ' ') letter[i].ok = TRUE;
+ else letter[i].ok = FALSE;
+ letter[i].bx = 0;
+ letter[i].by = 0;
+ }
+ }
+
+ /* We dont need any files for full_text */
+ if (full_text)
+ {
+ /* Init */
+ my_str_fgets(full_text, NULL, 0);
+ }
+ else
+ {
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, name);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* No such file */
+ if (!fp)
+ {
+ msg_format("Cannot find file %s at %s", name, buf);
+ return ( -1);
+ }
+ }
+
+ /* Process the file */
+ while (0 == ((full_text) ? my_str_fgets(full_text, buf, 1024) : my_fgets(fp, buf, 1024)))
+ {
+ /* Count lines */
+ num++;
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) continue;
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) continue;
+
+ /* Skip comments */
+ if (buf[0] == '#') continue;
+
+
+ /* Process "?:<expr>" */
+ if ((buf[0] == '?') && (buf[1] == ':'))
+ {
+ char f;
+ cptr v;
+ char *s;
+
+ /* Start */
+ s = buf + 2;
+
+ /* Parse the expr */
+ v = process_dungeon_file_expr(&s, &f);
+
+ /* Set flag */
+ bypass = (streq(v, "0") ? TRUE : FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+ /* Apply conditionals */
+ if (bypass) continue;
+
+
+ /* Process "%:<file>" */
+ if (buf[0] == '%')
+ {
+ /* Process that file if allowed */
+ (void)process_dungeon_file(NULL, buf + 2, yval, xval, ymax, xmax, FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Process the line */
+ err = process_dungeon_file_aux(buf, yval, xval, xmin, ymax, xmax);
+
+ /* Oops */
+ if (err) break;
+ }
+
+
+ /* Error */
+ if (err)
+ {
+ /* Useful error message */
+ msg_format("Error %d in line %d of file '%s'.", err, num, name);
+ msg_format("Parsing '%s'", buf);
+ }
+
+ if (!full_text)
+ {
+ /* Close the file */
+ my_fclose(fp);
+ }
+
+ /* Result */
+ return (err);
+}
diff --git a/src/init2.c b/src/init2.c
new file mode 100644
index 00000000..2d78b24e
--- /dev/null
+++ b/src/init2.c
@@ -0,0 +1,6891 @@
+/* File: init2.c */
+
+/* Purpose: Initialisation (part 2) -BEN- */
+
+#include "angband.h"
+
+
+#if !defined(MACINTOSH) && !defined(RISCOS) && defined(CHECK_MODIFICATION_TIME)
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif /* !MACINTOSH && !RISCOS && CHECK_MODIFICATION_TIME */
+
+
+/*
+ * This file is used to initialise various variables and arrays for the
+ * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass
+ * the common limitation of "read()" and "write()" to only 32767 bytes
+ * at a time.
+ *
+ * Several of the arrays for Angband are built from "template" files in
+ * the "lib/file" directory, from which quick-load binary "image" files
+ * are constructed whenever they are not present in the "lib/data"
+ * directory, or if those files become obsolete, if we are allowed.
+ *
+ * Warning -- the "ascii" file parsers use a minor hack to collect the
+ * name and text information in a single pass. Thus, the game will not
+ * be able to load any template file with more than 20K of names or 60K
+ * of text, even though technically, up to 64K should be legal.
+ *
+ * The "init1.c" file is used only to parse the ascii template files,
+ * to create the binary image files. If you include the binary image
+ * files instead of the ascii template files, then you can undefine
+ * "ALLOW_TEMPLATES", saving about 20K by removing "init1.c". Note
+ * that the binary image files are extremely system dependant.
+ */
+
+
+
+/*
+ * Find the default paths to all of our important sub-directories.
+ *
+ * The purpose of each sub-directory is described in "variable.c".
+ *
+ * All of the sub-directories should, by default, be located inside
+ * the main "lib" directory, whose location is very system dependant.
+ *
+ * This function takes a writable buffer, initially containing the
+ * "path" to the "lib" directory, for example, "/pkg/lib/angband/",
+ * or a system dependant string, for example, ":lib:". The buffer
+ * must be large enough to contain at least 32 more characters.
+ *
+ * Various command line options may allow some of the important
+ * directories to be changed to user-specified directories, most
+ * importantly, the "info" and "user" and "save" directories,
+ * but this is done after this function, see "main.c".
+ *
+ * In general, the initial path should end in the appropriate "PATH_SEP"
+ * string. All of the "sub-directory" paths (created below or supplied
+ * by the user) will NOT end in the "PATH_SEP" string, see the special
+ * "path_build()" function in "util.c" for more information.
+ *
+ * Mega-Hack -- support fat raw files under NEXTSTEP, using special
+ * "suffixed" directories for the "ANGBAND_DIR_DATA" directory, but
+ * requiring the directories to be created by hand by the user.
+ *
+ * Hack -- first we free all the strings, since this is known
+ * to succeed even if the strings have not been allocated yet,
+ * as long as the variables start out as "NULL". This allows
+ * this function to be called multiple times, for example, to
+ * try several base "path" values until a good one is found.
+ */
+void init_file_paths(char *path)
+{
+ char *tail;
+ int pathlen;
+
+ /*** Free everything ***/
+
+ /* Free the main path */
+ string_free(ANGBAND_DIR);
+
+ /* Free the sub-paths */
+ string_free(ANGBAND_DIR_APEX);
+ string_free(ANGBAND_DIR_BONE);
+ string_free(ANGBAND_DIR_CORE);
+ string_free(ANGBAND_DIR_DNGN);
+ string_free(ANGBAND_DIR_DATA);
+ string_free(ANGBAND_DIR_EDIT);
+ string_free(ANGBAND_DIR_FILE);
+ string_free(ANGBAND_DIR_HELP);
+ string_free(ANGBAND_DIR_INFO);
+ string_free(ANGBAND_DIR_MODULES);
+ string_free(ANGBAND_DIR_NOTE);
+ string_free(ANGBAND_DIR_SAVE);
+ string_free(ANGBAND_DIR_SCPT);
+ string_free(ANGBAND_DIR_PREF);
+ string_free(ANGBAND_DIR_PATCH);
+ string_free(ANGBAND_DIR_USER);
+ string_free(ANGBAND_DIR_XTRA);
+ string_free(ANGBAND_DIR_CMOV);
+
+
+ /*** Prepare the "path" ***/
+
+ pathlen = strlen(path);
+
+ /* Hack -- save the main directory without trailing PATH_SEP if present */
+ if (strlen(PATH_SEP) > 0 && pathlen > 0)
+ {
+ int seplen = strlen(PATH_SEP);
+
+ if (strcmp(path + pathlen - seplen, PATH_SEP) == 0)
+ {
+ path[pathlen - seplen] = '\0';
+ ANGBAND_DIR = string_make(path);
+ path[pathlen - seplen] = *PATH_SEP;
+ }
+ else
+ {
+ ANGBAND_DIR = string_make(path);
+ }
+ }
+ else
+ {
+ ANGBAND_DIR = string_make(path);
+ }
+
+ /* Prepare to append to the Base Path */
+ tail = path + pathlen;
+
+
+
+#ifdef VM
+
+ /*** Use "flat" paths with VM/ESA ***/
+
+ /* Use "blank" path names */
+ ANGBAND_DIR_APEX = string_make("");
+ ANGBAND_DIR_BONE = string_make("");
+ ANGBAND_DIR_CORE = string_make("");
+ ANGBAND_DIR_DNGN = string_make("");
+ ANGBAND_DIR_DATA = string_make("");
+ ANGBAND_DIR_EDIT = string_make("");
+ ANGBAND_DIR_FILE = string_make("");
+ ANGBAND_DIR_HELP = string_make("");
+ ANGBAND_DIR_INFO = string_make("");
+ ANGBAND_DIR_MODULES = string_make("");
+ ANGBAND_DIR_NOTE = string_make("");
+ ANGBAND_DIR_PATCH = string_make("");
+ ANGBAND_DIR_SAVE = string_make("");
+ ANGBAND_DIR_SCPT = string_make("");
+ ANGBAND_DIR_PREF = string_make("");
+ ANGBAND_DIR_USER = string_make("");
+ ANGBAND_DIR_XTRA = string_make("");
+ ANGBAND_DIR_CMOV = string_make("");
+
+#else /* VM */
+
+
+ /*** Build the sub-directory names ***/
+
+ /* Build a path name */
+ strcpy(tail, "apex");
+ ANGBAND_DIR_APEX = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "bone");
+ ANGBAND_DIR_BONE = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "core");
+ ANGBAND_DIR_CORE = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "dngn");
+ ANGBAND_DIR_DNGN = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "data");
+ ANGBAND_DIR_DATA = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "edit");
+ ANGBAND_DIR_EDIT = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "file");
+ ANGBAND_DIR_FILE = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "help");
+ ANGBAND_DIR_HELP = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "info");
+ ANGBAND_DIR_INFO = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "mods");
+ ANGBAND_DIR_MODULES = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "patch");
+ ANGBAND_DIR_PATCH = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "scpt");
+ ANGBAND_DIR_SCPT = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "pref");
+ ANGBAND_DIR_PREF = string_make(path);
+
+#ifdef PRIVATE_USER_PATH
+
+ /* synchronize with module_reset_dir */
+ {
+ char user_path[1024];
+
+ /* Get an absolute path from the file name */
+ path_parse(user_path, 1024, PRIVATE_USER_PATH);
+ strcat(user_path, USER_PATH_VERSION);
+ ANGBAND_DIR_USER = string_make(user_path);
+ ANGBAND_DIR_NOTE = string_make(user_path);
+ ANGBAND_DIR_CMOV = string_make(user_path);
+#ifdef PRIVATE_USER_PATH_MODULES
+ ANGBAND_DIR_MODULES = string_make(user_path);
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ ANGBAND_DIR_APEX = string_make(user_path);
+#endif
+#ifdef PRIVATE_USER_PATH_DATA
+ {
+ char user_path_data[1024];
+ strcpy(user_path_data, user_path);
+ strcat(user_path_data, "/data");
+ ANGBAND_DIR_DATA = string_make(user_path_data);
+ }
+#endif
+
+ /* Savefiles are in user directory */
+ strcat(user_path, "/save");
+ ANGBAND_DIR_SAVE = string_make(user_path);
+ savefile_setuid = 0;
+ }
+
+#else /* PRIVATE_USER_PATH */
+
+ /* Build a path name */
+ strcpy(tail, "save");
+ ANGBAND_DIR_SAVE = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "user");
+ ANGBAND_DIR_USER = string_make(path);
+
+ /* Build a path name */
+ strcpy(tail, "note");
+ ANGBAND_DIR_NOTE = string_make(path);
+
+ /* Build a .. blah blah -- Improv */
+ strcpy(tail, "cmov");
+ ANGBAND_DIR_CMOV = string_make(path);
+
+#endif /* PRIVATE_USER_PATH */
+
+ /* Build a path name */
+ strcpy(tail, "xtra");
+ ANGBAND_DIR_XTRA = string_make(path);
+
+#endif /* VM */
+
+
+#ifdef NeXT
+
+ /* Allow "fat binary" usage with NeXT */
+ if (TRUE)
+ {
+ cptr next = NULL;
+
+# if defined(m68k)
+ next = "m68k";
+# endif
+
+# if defined(i386)
+ next = "i386";
+# endif
+
+# if defined(sparc)
+ next = "sparc";
+# endif
+
+# if defined(hppa)
+ next = "hppa";
+# endif
+
+ /* Use special directory */
+ if (next)
+ {
+ /* Forget the old path name */
+ string_free(ANGBAND_DIR_DATA);
+
+ /* Build a new path name */
+ sprintf(tail, "data-%s", next);
+ ANGBAND_DIR_DATA = string_make(path);
+ }
+ }
+
+#endif /* NeXT */
+
+}
+
+
+
+#ifdef ALLOW_TEMPLATES
+
+
+/*
+ * Hack -- help give useful error messages
+ */
+s16b error_idx;
+s16b error_line;
+
+
+/*
+ * Hack -- help initialise the fake "name" and "text" arrays when
+ * parsing an "ascii" template file.
+ */
+u32b fake_name_size;
+u32b fake_text_size;
+
+
+/*
+ * Standard error message text
+ */
+static cptr err_str[9] =
+{
+ NULL,
+ "parse error",
+ "obsolete file",
+ "missing record header",
+ "non-sequential records",
+ "invalid flag specification",
+ "undefined directive",
+ "out of memory",
+ "invalid skill chart"
+};
+
+
+#endif /* ALLOW_TEMPLATES */
+
+
+#if !defined(RISCOS) && defined(CHECK_MODIFICATION_TIME)
+
+static errr check_modification_date(int fd, cptr template_file)
+{
+ char buf[1024];
+
+ struct stat txt_stat, raw_stat;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, template_file);
+
+ /* Access stats on text file */
+ if (stat(buf, &txt_stat))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+ /* Access stats on raw file */
+ if (fstat(fd, &raw_stat))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+ /* Ensure text file is not newer than raw file */
+ if (txt_stat.st_mtime > raw_stat.st_mtime)
+ {
+ /* Reprocess text file */
+ return ( -1);
+ }
+
+ return (0);
+}
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+/*
+ * Hack -- take notes on line 23
+ */
+static void note(cptr str)
+{
+ Term_erase(0, 23, 255);
+ Term_putstr(20, 23, -1, TERM_WHITE, str);
+ Term_fresh();
+}
+
+
+
+/*** Initialise from binary image files ***/
+
+
+/*
+ * Initialise the "f_info" array, by parsing a binary "image" file
+ */
+static errr init_f_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != f_head->v_major) ||
+ (test.v_minor != f_head->v_minor) ||
+ (test.v_patch != f_head->v_patch) ||
+ (test.v_extra != f_head->v_extra) ||
+ (test.info_num != f_head->info_num) ||
+ (test.info_len != f_head->info_len) ||
+ (test.head_size != f_head->head_size) ||
+ (test.info_size != f_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*f_head) = test;
+
+
+ /* Allocate the "f_info" array */
+ C_MAKE(f_info, f_head->info_num, feature_type);
+
+ /* Read the "f_info" array */
+ fd_read(fd, (char*)(f_info), f_head->info_size);
+
+
+ /* Allocate the "f_name" array */
+ C_MAKE(f_name, f_head->name_size, char);
+
+ /* Read the "f_name" array */
+ fd_read(fd, (char*)(f_name), f_head->name_size);
+
+
+#ifndef DELAY_LOAD_F_TEXT
+
+ /* Allocate the "f_text" array */
+ C_MAKE(f_text, f_head->text_size, char);
+
+ /* Read the "f_text" array */
+ fd_read(fd, (char*)(f_text), f_head->text_size);
+
+#endif /* DELAY_LOAD_F_TEXT */
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "f_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_f_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(f_head, header);
+
+ /* Save the "version" */
+ f_head->v_major = VERSION_MAJOR;
+ f_head->v_minor = VERSION_MINOR;
+ f_head->v_patch = VERSION_PATCH;
+ f_head->v_extra = 0;
+
+ /* Save the "record" information */
+ f_head->info_num = max_f_idx;
+ f_head->info_len = sizeof(feature_type);
+
+ /* Save the size of "f_head" and "f_info" */
+ f_head->head_size = sizeof(header);
+ f_head->info_size = f_head->info_num * f_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "f_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "f_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_f_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'f_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "f_name" and "f_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "f_info" array */
+ C_MAKE(f_info, f_head->info_num, feature_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(f_name, fake_name_size, char);
+ C_MAKE(f_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "f_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'f_info.txt' file.");
+
+ /* Parse the file */
+ err = init_f_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'f_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'f_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "f_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(f_head), f_head->head_size);
+
+ /* Dump the "f_info" array */
+ fd_write(fd, (char*)(f_info), f_head->info_size);
+
+ /* Dump the "f_name" array */
+ fd_write(fd, (char*)(f_name), f_head->name_size);
+
+ /* Dump the "f_text" array */
+ fd_write(fd, (char*)(f_text), f_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "f_info" array */
+ C_KILL(f_info, f_head->info_num, feature_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(f_name, fake_name_size, char);
+ C_KILL(f_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "f_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'f_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_f_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'f_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "k_info" array, by parsing a binary "image" file
+ */
+static errr init_k_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != k_head->v_major) ||
+ (test.v_minor != k_head->v_minor) ||
+ (test.v_patch != k_head->v_patch) ||
+ (test.v_extra != k_head->v_extra) ||
+ (test.info_num != k_head->info_num) ||
+ (test.info_len != k_head->info_len) ||
+ (test.head_size != k_head->head_size) ||
+ (test.info_size != k_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*k_head) = test;
+
+
+ /* Allocate the "k_info" array */
+ C_MAKE(k_info, k_head->info_num, object_kind);
+
+ /* Read the "k_info" array */
+ fd_read(fd, (char*)(k_info), k_head->info_size);
+
+
+ /* Allocate the "k_name" array */
+ C_MAKE(k_name, k_head->name_size, char);
+
+ /* Read the "k_name" array */
+ fd_read(fd, (char*)(k_name), k_head->name_size);
+
+
+#ifndef DELAY_LOAD_K_TEXT
+
+ /* Allocate the "k_text" array */
+ C_MAKE(k_text, k_head->text_size, char);
+
+ /* Read the "k_text" array */
+ fd_read(fd, (char*)(k_text), k_head->text_size);
+
+#endif /* DELAY_LOAD_K_TEXT */
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "k_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_k_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(k_head, header);
+
+ /* Save the "version" */
+ k_head->v_major = VERSION_MAJOR;
+ k_head->v_minor = VERSION_MINOR;
+ k_head->v_patch = VERSION_PATCH;
+ k_head->v_extra = 0;
+
+ /* Save the "record" information */
+ k_head->info_num = max_k_idx;
+ k_head->info_len = sizeof(object_kind);
+
+ /* Save the size of "k_head" and "k_info" */
+ k_head->head_size = sizeof(header);
+ k_head->info_size = k_head->info_num * k_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "k_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "k_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_k_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'k_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "k_name" and "k_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "k_info" array */
+ C_MAKE(k_info, k_head->info_num, object_kind);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(k_name, fake_name_size, char);
+ C_MAKE(k_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "k_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'k_info.txt' file.");
+
+ /* Parse the file */
+ err = init_k_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'k_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'k_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "k_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(k_head), k_head->head_size);
+
+ /* Dump the "k_info" array */
+ fd_write(fd, (char*)(k_info), k_head->info_size);
+
+ /* Dump the "k_name" array */
+ fd_write(fd, (char*)(k_name), k_head->name_size);
+
+ /* Dump the "k_text" array */
+ fd_write(fd, (char*)(k_text), k_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "k_info" array */
+ C_KILL(k_info, k_head->info_num, object_kind);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(k_name, fake_name_size, char);
+ C_KILL(k_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "k_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'k_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_k_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'k_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "a_info" array, by parsing a binary "image" file
+ */
+static errr init_a_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != a_head->v_major) ||
+ (test.v_minor != a_head->v_minor) ||
+ (test.v_patch != a_head->v_patch) ||
+ (test.v_extra != a_head->v_extra) ||
+ (test.info_num != a_head->info_num) ||
+ (test.info_len != a_head->info_len) ||
+ (test.head_size != a_head->head_size) ||
+ (test.info_size != a_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*a_head) = test;
+
+
+ /* Allocate the "a_info" array */
+ C_MAKE(a_info, a_head->info_num, artifact_type);
+
+ /* Read the "a_info" array */
+ fd_read(fd, (char*)(a_info), a_head->info_size);
+
+
+ /* Allocate the "a_name" array */
+ C_MAKE(a_name, a_head->name_size, char);
+
+ /* Read the "a_name" array */
+ fd_read(fd, (char*)(a_name), a_head->name_size);
+
+
+#ifndef DELAY_LOAD_A_TEXT
+
+ /* Allocate the "a_text" array */
+ C_MAKE(a_text, a_head->text_size, char);
+
+ /* Read the "a_text" array */
+ fd_read(fd, (char*)(a_text), a_head->text_size);
+
+#endif /* DELAY_LOAD_A_TEXT */
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "s_info" array, by parsing a binary "image" file
+ */
+static errr init_s_info_raw(int fd)
+{
+ header test;
+ /*int i;*/
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != s_head->v_major) ||
+ (test.v_minor != s_head->v_minor) ||
+ (test.v_patch != s_head->v_patch) ||
+ (test.v_extra != s_head->v_extra) ||
+ (test.info_num != s_head->info_num) ||
+ (test.info_len != s_head->info_len) ||
+ (test.head_size != s_head->head_size) ||
+ (test.info_size != s_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*s_head) = test;
+
+
+ /* Allocate the "s_info" array */
+ C_MAKE(s_info, s_head->info_num, skill_type);
+
+ /* Read the "s_info" array */
+ fd_read(fd, (char*)(s_info), s_head->info_size);
+
+
+ /* Allocate the "s_name" array */
+ C_MAKE(s_name, s_head->name_size, char);
+
+ /* Read the "s_name" array */
+ fd_read(fd, (char*)(s_name), s_head->name_size);
+
+
+ /* Allocate the "s_text" array */
+ C_MAKE(s_text, s_head->text_size, char);
+
+ /* Read the "s_text" array */
+ fd_read(fd, (char*)(s_text), s_head->text_size);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ab_info" array, by parsing a binary "image" file
+ */
+static errr init_ab_info_raw(int fd)
+{
+ header test;
+ /*int i;*/
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != ab_head->v_major) ||
+ (test.v_minor != ab_head->v_minor) ||
+ (test.v_patch != ab_head->v_patch) ||
+ (test.v_extra != ab_head->v_extra) ||
+ (test.info_num != ab_head->info_num) ||
+ (test.info_len != ab_head->info_len) ||
+ (test.head_size != ab_head->head_size) ||
+ (test.info_size != ab_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*ab_head) = test;
+
+
+ /* Allocate the "ab_info" array */
+ C_MAKE(ab_info, ab_head->info_num, ability_type);
+
+ /* Read the "ab_info" array */
+ fd_read(fd, (char*)(ab_info), ab_head->info_size);
+
+
+ /* Allocate the "ab_name" array */
+ C_MAKE(ab_name, ab_head->name_size, char);
+
+ /* Read the "ab_name" array */
+ fd_read(fd, (char*)(ab_name), ab_head->name_size);
+
+
+ /* Allocate the "ab_text" array */
+ C_MAKE(ab_text, ab_head->text_size, char);
+
+ /* Read the "ab_text" array */
+ fd_read(fd, (char*)(ab_text), ab_head->text_size);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "set_info" array, by parsing a binary "image" file
+ */
+static errr init_set_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != set_head->v_major) ||
+ (test.v_minor != set_head->v_minor) ||
+ (test.v_patch != set_head->v_patch) ||
+ (test.v_extra != set_head->v_extra) ||
+ (test.info_num != set_head->info_num) ||
+ (test.info_len != set_head->info_len) ||
+ (test.head_size != set_head->head_size) ||
+ (test.info_size != set_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*set_head) = test;
+
+
+ /* Allocate the "a_info" array */
+ C_MAKE(set_info, set_head->info_num, set_type);
+
+ /* Read the "a_info" array */
+ fd_read(fd, (char*)(set_info), set_head->info_size);
+
+
+ /* Allocate the "a_name" array */
+ C_MAKE(set_name, set_head->name_size, char);
+
+ /* Read the "a_name" array */
+ fd_read(fd, (char*)(set_name), set_head->name_size);
+
+
+ /* Allocate the "a_text" array */
+ C_MAKE(set_text, set_head->text_size, char);
+
+ /* Read the "a_text" array */
+ fd_read(fd, (char*)(set_text), set_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "set_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_set_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(set_head, header);
+
+ /* Save the "version" */
+ set_head->v_major = VERSION_MAJOR;
+ set_head->v_minor = VERSION_MINOR;
+ set_head->v_patch = VERSION_PATCH;
+ set_head->v_extra = 0;
+
+ /* Save the "record" information */
+ set_head->info_num = max_set_idx;
+ set_head->info_len = sizeof(set_type);
+
+ /* Save the size of "set_head" and "set_info" */
+ set_head->head_size = sizeof(header);
+ set_head->info_size = set_head->info_num * set_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "set_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "set_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_set_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'set_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "set_name" and "set_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "set_info" array */
+ C_MAKE(set_info, set_head->info_num, set_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(set_name, fake_name_size, char);
+ C_MAKE(set_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "set_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'set_info.txt' file.");
+
+ /* Parse the file */
+ err = init_set_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'set_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'set_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "set_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(set_head), set_head->head_size);
+
+ /* Dump the "set_info" array */
+ fd_write(fd, (char*)(set_info), set_head->info_size);
+
+ /* Dump the "set_name" array */
+ fd_write(fd, (char*)(set_name), set_head->name_size);
+
+ /* Dump the "set_text" array */
+ fd_write(fd, (char*)(set_text), set_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "set_info" array */
+ C_KILL(set_info, set_head->info_num, set_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(set_name, fake_name_size, char);
+ C_KILL(set_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "set_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot open 'set_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_set_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'set_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "a_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_a_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(a_head, header);
+
+ /* Save the "version" */
+ a_head->v_major = VERSION_MAJOR;
+ a_head->v_minor = VERSION_MINOR;
+ a_head->v_patch = VERSION_PATCH;
+ a_head->v_extra = 0;
+
+ /* Save the "record" information */
+ a_head->info_num = max_a_idx;
+ a_head->info_len = sizeof(artifact_type);
+
+ /* Save the size of "a_head" and "a_info" */
+ a_head->head_size = sizeof(header);
+ a_head->info_size = a_head->info_num * a_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "a_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "a_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_a_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'a_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "a_name" and "a_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "a_info" array */
+ C_MAKE(a_info, a_head->info_num, artifact_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(a_name, fake_name_size, char);
+ C_MAKE(a_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "a_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'a_info.txt' file.");
+
+ /* Parse the file */
+ err = init_a_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'a_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'a_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "a_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(a_head), a_head->head_size);
+
+ /* Dump the "a_info" array */
+ fd_write(fd, (char*)(a_info), a_head->info_size);
+
+ /* Dump the "a_name" array */
+ fd_write(fd, (char*)(a_name), a_head->name_size);
+
+ /* Dump the "a_text" array */
+ fd_write(fd, (char*)(a_text), a_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "a_info" array */
+ C_KILL(a_info, a_head->info_num, artifact_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(a_name, fake_name_size, char);
+ C_KILL(a_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "a_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot open 'a_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_a_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'a_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "s_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_s_info(void)
+{
+ int fd;
+
+ /* int i; */
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(s_head, header);
+
+ /* Save the "version" */
+ s_head->v_major = VERSION_MAJOR;
+ s_head->v_minor = VERSION_MINOR;
+ s_head->v_patch = VERSION_PATCH;
+ s_head->v_extra = 0;
+
+ /* Save the "record" information */
+ s_head->info_num = max_s_idx;
+ s_head->info_len = sizeof(skill_type);
+
+ /* Save the size of "s_head" and "s_info" */
+ s_head->head_size = sizeof(header);
+ s_head->info_size = s_head->info_num * s_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "s_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "s_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_s_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 's_info.raw' file.");
+ msg_print(NULL);
+ }
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "a_name" and "a_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "s_info" array */
+ C_MAKE(s_info, s_head->info_num, skill_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(s_name, fake_name_size, char);
+ C_MAKE(s_text, fake_text_size, char);
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "s_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 's_info.txt' file.");
+
+ /* Parse the file */
+ err = init_s_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 's_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 's_info.txt' file.");
+ }
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "s_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(s_head), s_head->head_size);
+
+ /* Dump the "s_info" array */
+ fd_write(fd, (char*)(s_info), s_head->info_size);
+
+ /* Dump the "s_name" array */
+ fd_write(fd, (char*)(s_name), s_head->name_size);
+
+ /* Dump the "s_text" array */
+ fd_write(fd, (char*)(s_text), s_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "s_info" array */
+ C_KILL(s_info, s_head->info_num, skill_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(s_name, fake_name_size, char);
+ C_KILL(s_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "s_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot open 's_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_s_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 's_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ab_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ab_info(void)
+{
+ int fd;
+
+ /* int i; */
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(ab_head, header);
+
+ /* Save the "version" */
+ ab_head->v_major = VERSION_MAJOR;
+ ab_head->v_minor = VERSION_MINOR;
+ ab_head->v_patch = VERSION_PATCH;
+ ab_head->v_extra = 0;
+
+ /* Save the "record" information */
+ ab_head->info_num = max_ab_idx;
+ ab_head->info_len = sizeof(ability_type);
+
+ /* Save the size of "ab_head" and "ab_info" */
+ ab_head->head_size = sizeof(header);
+ ab_head->info_size = ab_head->info_num * ab_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ab_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "ab_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_ab_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'ab_info.raw' file.");
+ msg_print(NULL);
+ }
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "a_name" and "a_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "ab_info" array */
+ C_MAKE(ab_info, ab_head->info_num, ability_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(ab_name, fake_name_size, char);
+ C_MAKE(ab_text, fake_text_size, char);
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ab_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ab_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ab_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'ab_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ab_info.txt' file.");
+ }
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ab_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(ab_head), ab_head->head_size);
+
+ /* Dump the "ab_info" array */
+ fd_write(fd, (char*)(ab_info), ab_head->info_size);
+
+ /* Dump the "ab_name" array */
+ fd_write(fd, (char*)(ab_name), ab_head->name_size);
+
+ /* Dump the "ab_text" array */
+ fd_write(fd, (char*)(ab_text), ab_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "ab_info" array */
+ C_KILL(ab_info, ab_head->info_num, ability_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(ab_name, fake_name_size, char);
+ C_KILL(ab_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ab_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot open 'ab_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_ab_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'ab_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "e_info" array, by parsing a binary "image" file
+ */
+static errr init_e_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != e_head->v_major) ||
+ (test.v_minor != e_head->v_minor) ||
+ (test.v_patch != e_head->v_patch) ||
+ (test.v_extra != e_head->v_extra) ||
+ (test.info_num != e_head->info_num) ||
+ (test.info_len != e_head->info_len) ||
+ (test.head_size != e_head->head_size) ||
+ (test.info_size != e_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*e_head) = test;
+
+
+ /* Allocate the "e_info" array */
+ C_MAKE(e_info, e_head->info_num, ego_item_type);
+
+ /* Read the "e_info" array */
+ fd_read(fd, (char*)(e_info), e_head->info_size);
+
+
+ /* Allocate the "e_name" array */
+ C_MAKE(e_name, e_head->name_size, char);
+
+ /* Read the "e_name" array */
+ fd_read(fd, (char*)(e_name), e_head->name_size);
+
+
+#ifndef DELAY_LOAD_E_TEXT
+
+ /* Allocate the "e_text" array */
+ C_MAKE(e_text, e_head->text_size, char);
+
+ /* Read the "e_text" array */
+ fd_read(fd, (char*)(e_text), e_head->text_size);
+
+#endif /* DELAY_LOAD_E_TEXT */
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "e_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_e_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(e_head, header);
+
+ /* Save the "version" */
+ e_head->v_major = VERSION_MAJOR;
+ e_head->v_minor = VERSION_MINOR;
+ e_head->v_patch = VERSION_PATCH;
+ e_head->v_extra = 0;
+
+ /* Save the "record" information */
+ e_head->info_num = max_e_idx;
+ e_head->info_len = sizeof(ego_item_type);
+
+ /* Save the size of "e_head" and "e_info" */
+ e_head->head_size = sizeof(header);
+ e_head->info_size = e_head->info_num * e_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "e_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "e_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_e_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'e_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "e_name" and "e_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "e_info" array */
+ C_MAKE(e_info, e_head->info_num, ego_item_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(e_name, fake_name_size, char);
+ C_MAKE(e_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "e_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'e_info.txt' file.");
+
+ /* Parse the file */
+ err = init_e_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'e_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'e_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "e_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(e_head), e_head->head_size);
+
+ /* Dump the "e_info" array */
+ fd_write(fd, (char*)(e_info), e_head->info_size);
+
+ /* Dump the "e_name" array */
+ fd_write(fd, (char*)(e_name), e_head->name_size);
+
+ /* Dump the "e_text" array */
+ fd_write(fd, (char*)(e_text), e_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "e_info" array */
+ C_KILL(e_info, e_head->info_num, ego_item_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(e_name, fake_name_size, char);
+ C_KILL(e_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "e_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'e_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_e_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'e_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "ra_info" array, by parsing a binary "image" file
+ */
+static errr init_ra_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != ra_head->v_major) ||
+ (test.v_minor != ra_head->v_minor) ||
+ (test.v_patch != ra_head->v_patch) ||
+ (test.v_extra != ra_head->v_extra) ||
+ (test.info_num != ra_head->info_num) ||
+ (test.info_len != ra_head->info_len) ||
+ (test.head_size != ra_head->head_size) ||
+ (test.info_size != ra_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*ra_head) = test;
+
+
+ /* Allocate the "ra_info" array */
+ C_MAKE(ra_info, ra_head->info_num, randart_part_type);
+
+ /* Read the "ra_info" array */
+ fd_read(fd, (char*)(ra_info), ra_head->info_size);
+ fd_read(fd, (char*)(ra_gen), 30 * sizeof (randart_gen_type));
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "ra_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ra_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the "header" ***/
+
+ /* Allocate the "header" */
+ MAKE(ra_head, header);
+
+ /* Save the "version" */
+ ra_head->v_major = VERSION_MAJOR;
+ ra_head->v_minor = VERSION_MINOR;
+ ra_head->v_patch = VERSION_PATCH;
+ ra_head->v_extra = 0;
+
+ /* Save the "record" information */
+ ra_head->info_num = max_ra_idx;
+ ra_head->info_len = sizeof(randart_part_type);
+
+ /* Save the size of "ra_head" and "ra_info" */
+ ra_head->head_size = sizeof(header);
+ ra_head->info_size = ra_head->info_num * ra_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ra_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "ra_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_ra_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'ra_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "ra_name" and "ra_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "ra_info" array */
+ C_MAKE(ra_info, ra_head->info_num, randart_part_type);
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ra_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ra_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ra_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'ra_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ra_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ra_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(ra_head), ra_head->head_size);
+
+ /* Dump the "ra_info" array */
+ fd_write(fd, (char*)(ra_info), ra_head->info_size);
+ fd_write(fd, (char*)(ra_gen), 30 * sizeof (randart_gen_type));
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "ra_info" array */
+ C_KILL(ra_info, ra_head->info_num, randart_part_type);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ra_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'ra_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_ra_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'ra_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise the "r_info" array, by parsing a binary "image" file
+ */
+static errr init_r_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != r_head->v_major) ||
+ (test.v_minor != r_head->v_minor) ||
+ (test.v_patch != r_head->v_patch) ||
+ (test.v_extra != r_head->v_extra) ||
+ (test.info_num != r_head->info_num) ||
+ (test.info_len != r_head->info_len) ||
+ (test.head_size != r_head->head_size) ||
+ (test.info_size != r_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*r_head) = test;
+
+
+ /* Allocate the "r_info" array */
+ C_MAKE(r_info, r_head->info_num, monster_race);
+
+ /* Read the "r_info" array */
+ fd_read(fd, (char*)(r_info), r_head->info_size);
+
+
+ /* Allocate the "r_name" array */
+ C_MAKE(r_name, r_head->name_size, char);
+
+ /* Read the "r_name" array */
+ fd_read(fd, (char*)(r_name), r_head->name_size);
+
+
+#ifndef DELAY_LOAD_R_TEXT
+
+ /* Allocate the "r_text" array */
+ C_MAKE(r_text, r_head->text_size, char);
+
+ /* Read the "r_text" array */
+ fd_read(fd, (char*)(r_text), r_head->text_size);
+
+#endif /* DELAY_LOAD_R_TEXT */
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "re_info" array, by parsing a binary "image" file
+ */
+static errr init_re_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != re_head->v_major) ||
+ (test.v_minor != re_head->v_minor) ||
+ (test.v_patch != re_head->v_patch) ||
+ (test.v_extra != re_head->v_extra) ||
+ (test.info_num != re_head->info_num) ||
+ (test.info_len != re_head->info_len) ||
+ (test.head_size != re_head->head_size) ||
+ (test.info_size != re_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*re_head) = test;
+
+
+ /* Allocate the "re_info" array */
+ C_MAKE(re_info, re_head->info_num, monster_ego);
+
+ /* Read the "re_info" array */
+ fd_read(fd, (char*)(re_info), re_head->info_size);
+
+
+ /* Allocate the "re_name" array */
+ C_MAKE(re_name, re_head->name_size, char);
+
+ /* Read the "re_name" array */
+ fd_read(fd, (char*)(re_name), re_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "d_info" array, by parsing a binary "image" file
+ */
+static errr init_d_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != d_head->v_major) ||
+ (test.v_minor != d_head->v_minor) ||
+ (test.v_patch != d_head->v_patch) ||
+ (test.v_extra != d_head->v_extra) ||
+ (test.info_num != d_head->info_num) ||
+ (test.info_len != d_head->info_len) ||
+ (test.head_size != d_head->head_size) ||
+ (test.info_size != d_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*d_head) = test;
+
+
+ /* Allocate the "d_info" array */
+ C_MAKE(d_info, d_head->info_num, dungeon_info_type);
+
+ /* Read the "d_info" array */
+ fd_read(fd, (char*)(d_info), d_head->info_size);
+
+
+ /* Allocate the "r_name" array */
+ C_MAKE(d_name, d_head->name_size, char);
+
+ /* Read the "d_name" array */
+ fd_read(fd, (char*)(d_name), d_head->name_size);
+
+ /* Allocate the "d_text" array */
+ C_MAKE(d_text, d_head->text_size, char);
+
+ /* Read the "d_text" array */
+ fd_read(fd, (char*)(d_text), d_head->text_size);
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "st_info" array, by parsing a binary "image" file
+ */
+static errr init_st_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != st_head->v_major) ||
+ (test.v_minor != st_head->v_minor) ||
+ (test.v_patch != st_head->v_patch) ||
+ (test.v_extra != st_head->v_extra) ||
+ (test.info_num != st_head->info_num) ||
+ (test.info_len != st_head->info_len) ||
+ (test.head_size != st_head->head_size) ||
+ (test.info_size != st_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*st_head) = test;
+
+
+ /* Allocate the "st_info" array */
+ C_MAKE(st_info, st_head->info_num, store_info_type);
+
+ /* Read the "st_info" array */
+ fd_read(fd, (char*)(st_info), st_head->info_size);
+
+
+ /* Allocate the "st_name" array */
+ C_MAKE(st_name, st_head->name_size, char);
+
+ /* Read the "st_name" array */
+ fd_read(fd, (char*)(st_name), st_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ba_info" array, by parsing a binary "image" file
+ */
+static errr init_ba_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != ba_head->v_major) ||
+ (test.v_minor != ba_head->v_minor) ||
+ (test.v_patch != ba_head->v_patch) ||
+ (test.v_extra != ba_head->v_extra) ||
+ (test.info_num != ba_head->info_num) ||
+ (test.info_len != ba_head->info_len) ||
+ (test.head_size != ba_head->head_size) ||
+ (test.info_size != ba_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*ba_head) = test;
+
+
+ /* Allocate the "ba_info" array */
+ C_MAKE(ba_info, ba_head->info_num, store_action_type);
+
+ /* Read the "ba_info" array */
+ fd_read(fd, (char*)(ba_info), ba_head->info_size);
+
+
+ /* Allocate the "ba_name" array */
+ C_MAKE(ba_name, ba_head->name_size, char);
+
+ /* Read the "ba_name" array */
+ fd_read(fd, (char*)(ba_name), ba_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ow_info" array, by parsing a binary "image" file
+ */
+static errr init_ow_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != ow_head->v_major) ||
+ (test.v_minor != ow_head->v_minor) ||
+ (test.v_patch != ow_head->v_patch) ||
+ (test.v_extra != ow_head->v_extra) ||
+ (test.info_num != ow_head->info_num) ||
+ (test.info_len != ow_head->info_len) ||
+ (test.head_size != ow_head->head_size) ||
+ (test.info_size != ow_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*ow_head) = test;
+
+
+ /* Allocate the "ow_info" array */
+ C_MAKE(ow_info, ow_head->info_num, owner_type);
+
+ /* Read the "ow_info" array */
+ fd_read(fd, (char*)(ow_info), ow_head->info_size);
+
+
+ /* Allocate the "ow_name" array */
+ C_MAKE(ow_name, ow_head->name_size, char);
+
+ /* Read the "ow_name" array */
+ fd_read(fd, (char*)(ow_name), ow_head->name_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "wf_info" array, by parsing a binary "image" file
+ */
+static errr init_wf_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != wf_head->v_major) ||
+ (test.v_minor != wf_head->v_minor) ||
+ (test.v_patch != wf_head->v_patch) ||
+ (test.v_extra != wf_head->v_extra) ||
+ (test.info_num != wf_head->info_num) ||
+ (test.info_len != wf_head->info_len) ||
+ (test.head_size != wf_head->head_size) ||
+ (test.info_size != wf_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*wf_head) = test;
+
+
+ /* Allocate the "wf_info" array */
+ C_MAKE(wf_info, wf_head->info_num, wilderness_type_info);
+
+ /* Read the "wf_info" array */
+ fd_read(fd, (char*)(wf_info), wf_head->info_size);
+
+
+ /* Allocate the "wf_name" array */
+ C_MAKE(wf_name, wf_head->name_size, char);
+
+ /* Read the "wf_name" array */
+ fd_read(fd, (char*)(wf_name), wf_head->name_size);
+
+ /* Allocate the "wf_text" array */
+ C_MAKE(wf_text, wf_head->text_size, char);
+
+ /* Read the "wf_text" array */
+ fd_read(fd, (char*)(wf_text), wf_head->text_size);
+
+ /* Read the "wildc2i" array */
+ fd_read(fd, (char*)wildc2i, 256 * sizeof(int));
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "player" arrays, by parsing a binary "image" file
+ */
+static errr init_player_info_raw(int fd)
+{
+ header test;
+ int i;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != rp_head->v_major) ||
+ (test.v_minor != rp_head->v_minor) ||
+ (test.v_patch != rp_head->v_patch) ||
+ (test.v_extra != rp_head->v_extra) ||
+ (test.info_num != rp_head->info_num) ||
+ (test.info_len != rp_head->info_len) ||
+ (test.head_size != rp_head->head_size) ||
+ (test.info_size != rp_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*rp_head) = test;
+
+
+ /* Allocate the "rp_info" array */
+ C_MAKE(race_info, rp_head->info_num, player_race);
+
+ /* Read the "rp_info" array */
+ fd_read(fd, (char*)(race_info), rp_head->info_size);
+
+
+ /* Allocate the "rp_name" array */
+ C_MAKE(rp_name, rp_head->name_size, char);
+
+ /* Read the "rp_name" array */
+ fd_read(fd, (char*)(rp_name), rp_head->name_size);
+
+ /* Allocate the "rp_text" array */
+ C_MAKE(rp_text, rp_head->text_size, char);
+
+ /* Read the "rp_text" array */
+ fd_read(fd, (char*)(rp_text), rp_head->text_size);
+
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != rmp_head->v_major) ||
+ (test.v_minor != rmp_head->v_minor) ||
+ (test.v_patch != rmp_head->v_patch) ||
+ (test.v_extra != rmp_head->v_extra) ||
+ (test.info_num != rmp_head->info_num) ||
+ (test.info_len != rmp_head->info_len) ||
+ (test.head_size != rmp_head->head_size) ||
+ (test.info_size != rmp_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*rmp_head) = test;
+
+
+ /* Allocate the "rmp_info" array */
+ C_MAKE(race_mod_info, rmp_head->info_num, player_race_mod);
+
+ /* Read the "rmp_info" array */
+ fd_read(fd, (char*)(race_mod_info), rmp_head->info_size);
+
+
+ /* Allocate the "rmp_name" array */
+ C_MAKE(rmp_name, rmp_head->name_size, char);
+
+ /* Read the "rmp_name" array */
+ fd_read(fd, (char*)(rmp_name), rmp_head->name_size);
+
+ /* Allocate the "rmp_text" array */
+ C_MAKE(rmp_text, rmp_head->text_size, char);
+
+ /* Read the "rmp_text" array */
+ fd_read(fd, (char*)(rmp_text), rmp_head->text_size);
+
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != c_head->v_major) ||
+ (test.v_minor != c_head->v_minor) ||
+ (test.v_patch != c_head->v_patch) ||
+ (test.v_extra != c_head->v_extra) ||
+ (test.info_num != c_head->info_num) ||
+ (test.info_len != c_head->info_len) ||
+ (test.head_size != c_head->head_size) ||
+ (test.info_size != c_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*c_head) = test;
+
+
+ /* Allocate the "c_info" array */
+ C_MAKE(class_info, c_head->info_num, player_class);
+
+ /* Read the "c_info" array */
+ fd_read(fd, (char*)(class_info), c_head->info_size);
+
+
+ /* Allocate the "c_name" array */
+ C_MAKE(c_name, c_head->name_size, char);
+
+ /* Read the "c_name" array */
+ fd_read(fd, (char*)(c_name), c_head->name_size);
+
+ /* Allocate the "c_text" array */
+ C_MAKE(c_text, c_head->text_size, char);
+
+ /* Read the "c_text" array */
+ fd_read(fd, (char*)(c_text), c_head->text_size);
+
+ /* Allocate the "bg" array */
+ C_MAKE(bg, max_bg_idx, hist_type);
+
+ /* Read the "bg" array */
+ fd_read(fd, (char*)bg, max_bg_idx * sizeof(hist_type));
+
+ /* Allocate the "meta_class" array */
+ C_MAKE(meta_class_info, max_mc_idx, meta_class_type);
+
+ /* Read the "meta_class" array */
+ fd_read(fd, (char*)meta_class_info, max_mc_idx * sizeof(meta_class_type));
+
+ for (i = 0; i < max_mc_idx; i++)
+ {
+ C_MAKE(meta_class_info[i].classes, max_c_idx, s16b);
+ fd_read(fd, (char*)meta_class_info[i].classes, max_c_idx * sizeof(s16b));
+ }
+
+ /* Read the "gen skills" array */
+ fd_read(fd, (char*)(gen_skill_base), MAX_SKILLS * sizeof(u32b));
+ fd_read(fd, (char*)(gen_skill_mod), MAX_SKILLS * sizeof(s16b));
+ fd_read(fd, (char*)(gen_skill_basem), MAX_SKILLS * sizeof(char));
+ fd_read(fd, (char*)(gen_skill_modm), MAX_SKILLS * sizeof(char));
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "r_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_r_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(r_head, header);
+
+ /* Save the "version" */
+ r_head->v_major = VERSION_MAJOR;
+ r_head->v_minor = VERSION_MINOR;
+ r_head->v_patch = VERSION_PATCH;
+ r_head->v_extra = 0;
+
+ /* Save the "record" information */
+ r_head->info_num = max_r_idx;
+ r_head->info_len = sizeof(monster_race);
+
+ /* Save the size of "r_head" and "r_info" */
+ r_head->head_size = sizeof(header);
+ r_head->info_size = r_head->info_num * r_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "r_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "r_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_r_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'r_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "r_name" and "r_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "r_info" array */
+ C_MAKE(r_info, r_head->info_num, monster_race);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(r_name, fake_name_size, char);
+ C_MAKE(r_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "r_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'r_info.txt' file.");
+
+ /* Parse the file */
+ err = init_r_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'r_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'r_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "r_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(r_head), r_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(r_info), r_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(r_name), r_head->name_size);
+
+ /* Dump the "r_text" array */
+ fd_write(fd, (char*)(r_text), r_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "r_info" array */
+ C_KILL(r_info, r_head->info_num, monster_race);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(r_name, fake_name_size, char);
+ C_KILL(r_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "r_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'r_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_r_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'r_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "re_info" array
+ *
+ * Note that we let each entry have a unique "name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_re_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(re_head, header);
+
+ /* Save the "version" */
+ re_head->v_major = VERSION_MAJOR;
+ re_head->v_minor = VERSION_MINOR;
+ re_head->v_patch = VERSION_PATCH;
+ re_head->v_extra = 0;
+
+ /* Save the "record" information */
+ re_head->info_num = max_re_idx;
+ re_head->info_len = sizeof(monster_ego);
+
+ /* Save the size of "re_head" and "re_info" */
+ re_head->head_size = sizeof(header);
+ re_head->info_size = re_head->info_num * re_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "re_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "re_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_re_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 're_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "re_name" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "re_info" array */
+ C_MAKE(re_info, re_head->info_num, monster_ego);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(re_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "re_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 're_info.txt' file.");
+
+ /* Parse the file */
+ err = init_re_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 're_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 're_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "re_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(re_head), re_head->head_size);
+
+ /* Dump the "re_info" array */
+ fd_write(fd, (char*)(re_info), re_head->info_size);
+
+ /* Dump the "re_name" array */
+ fd_write(fd, (char*)(re_name), re_head->name_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "re_info" array */
+ C_KILL(re_info, re_head->info_num, monster_ego);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(re_name, fake_name_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "re_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 're_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_re_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 're_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "d_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_d_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(d_head, header);
+
+ /* Save the "version" */
+ d_head->v_major = VERSION_MAJOR;
+ d_head->v_minor = VERSION_MINOR;
+ d_head->v_patch = VERSION_PATCH;
+ d_head->v_extra = 0;
+
+ /* Save the "record" information */
+ d_head->info_num = max_d_idx;
+ d_head->info_len = sizeof(dungeon_info_type);
+
+ /* Save the size of "d_head" and "d_info" */
+ d_head->head_size = sizeof(header);
+ d_head->info_size = d_head->info_num * d_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "d_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "d_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_d_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'd_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "d_name" and "d_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "d_info" array */
+ C_MAKE(d_info, d_head->info_num, dungeon_info_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(d_name, fake_name_size, char);
+ C_MAKE(d_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "d_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'd_info.txt' file.");
+
+ /* Parse the file */
+ err = init_d_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'd_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'd_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "d_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(d_head), d_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(d_info), d_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(d_name), d_head->name_size);
+
+ /* Dump the "r_text" array */
+ fd_write(fd, (char*)(d_text), d_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "d_info" array */
+ C_KILL(d_info, d_head->info_num, dungeon_info_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(d_name, fake_name_size, char);
+ C_KILL(d_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "d_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'd_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_d_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'd_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "player" arrays
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_player_info(void)
+{
+ int fd;
+
+ int i;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(rp_head, header);
+
+ /* Save the "version" */
+ rp_head->v_major = VERSION_MAJOR;
+ rp_head->v_minor = VERSION_MINOR;
+ rp_head->v_patch = VERSION_PATCH;
+ rp_head->v_extra = 0;
+
+ /* Save the "record" information */
+ rp_head->info_num = max_rp_idx;
+ rp_head->info_len = sizeof(player_race);
+
+ /* Save the size of "rp_head" and "rp_info" */
+ rp_head->head_size = sizeof(header);
+ rp_head->info_size = rp_head->info_num * rp_head->info_len;
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(rmp_head, header);
+
+ /* Save the "version" */
+ rmp_head->v_major = VERSION_MAJOR;
+ rmp_head->v_minor = VERSION_MINOR;
+ rmp_head->v_patch = VERSION_PATCH;
+ rmp_head->v_extra = 0;
+
+ /* Save the "record" information */
+ rmp_head->info_num = max_rmp_idx;
+ rmp_head->info_len = sizeof(player_race_mod);
+
+ /* Save the size of "rmp_head" and "rmp_info" */
+ rmp_head->head_size = sizeof(header);
+ rmp_head->info_size = rmp_head->info_num * rmp_head->info_len;
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(c_head, header);
+
+ /* Save the "version" */
+ c_head->v_major = VERSION_MAJOR;
+ c_head->v_minor = VERSION_MINOR;
+ c_head->v_patch = VERSION_PATCH;
+ c_head->v_extra = 0;
+
+ /* Save the "record" information */
+ c_head->info_num = max_c_idx;
+ c_head->info_len = sizeof(player_class);
+
+ /* Save the size of "c_head" and "c_info" */
+ c_head->head_size = sizeof(header);
+ c_head->info_size = c_head->info_num * c_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "p_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "p_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_player_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'p_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "rp_name" and "rp_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "rp_info" array */
+ C_MAKE(race_info, rp_head->info_num, player_race);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(rp_name, fake_name_size, char);
+ C_MAKE(rp_text, fake_text_size, char);
+
+ /* Allocate the "rmp_info" array */
+ C_MAKE(race_mod_info, rmp_head->info_num, player_race_mod);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(rmp_name, fake_name_size, char);
+ C_MAKE(rmp_text, fake_text_size, char);
+
+ /* Allocate the "c_info" array */
+ C_MAKE(class_info, c_head->info_num, player_class);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(c_name, fake_name_size, char);
+ C_MAKE(c_text, fake_text_size, char);
+
+ /* Allocate the "bg" array */
+ C_MAKE(bg, max_bg_idx, hist_type);
+
+ /* Allocate the "meta_class" array */
+ C_MAKE(meta_class_info, max_mc_idx, meta_class_type);
+
+ /* Read the "meta_class" array */
+ fd_read(fd, (char*)meta_class_info, max_mc_idx * sizeof(meta_class_type));
+
+ for (i = 0; i < max_mc_idx; i++)
+ {
+ C_MAKE(meta_class_info[i].classes, max_c_idx, s16b);
+ fd_read(fd, (char*)meta_class_info[i].classes, max_c_idx * sizeof(s16b));
+ }
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "p_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'p_info.txt' file.");
+
+ /* Parse the file */
+ err = init_player_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'p_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'p_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "p_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(rp_head), rp_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(race_info), rp_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(rp_name), rp_head->name_size);
+
+ /* Dump the "r_text" array */
+ fd_write(fd, (char*)(rp_text), rp_head->text_size);
+
+ /* Dump it */
+ fd_write(fd, (char*)(rmp_head), rmp_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(race_mod_info), rmp_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(rmp_name), rmp_head->name_size);
+
+ /* Dump the "r_text" array */
+ fd_write(fd, (char*)(rmp_text), rmp_head->text_size);
+
+ /* Dump it */
+ fd_write(fd, (char*)(c_head), c_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(class_info), c_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(c_name), c_head->name_size);
+
+ /* Dump the "r_text" array */
+ fd_write(fd, (char*)(c_text), c_head->text_size);
+
+ /* Dump the "bg" array */
+ fd_write(fd, (char*)bg, max_bg_idx * sizeof(hist_type));
+
+ /* Dump the "meta_class" array */
+ fd_write(fd, (char*)meta_class_info, max_mc_idx * sizeof(meta_class_type));
+
+ for (i = 0; i < max_mc_idx; i++)
+ {
+ fd_write(fd, (char*)meta_class_info[i].classes, max_c_idx * sizeof(s16b));
+ }
+
+ /* Read the "gen skills" array */
+ fd_write(fd, (char*)(gen_skill_base), MAX_SKILLS * sizeof(u32b));
+ fd_write(fd, (char*)(gen_skill_mod), MAX_SKILLS * sizeof(s16b));
+ fd_write(fd, (char*)(gen_skill_basem), MAX_SKILLS * sizeof(char));
+ fd_write(fd, (char*)(gen_skill_modm), MAX_SKILLS * sizeof(char));
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "rp_info" array */
+ C_KILL(race_info, rp_head->info_num, player_race);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(rp_name, fake_name_size, char);
+ C_KILL(rp_text, fake_text_size, char);
+
+ /* Free the "c_info" array */
+ C_KILL(class_info, c_head->info_num, player_class);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(c_name, fake_name_size, char);
+ C_KILL(c_text, fake_text_size, char);
+
+ /* Free the "rp_info" array */
+ C_KILL(race_mod_info, rmp_head->info_num, player_race_mod);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(rmp_name, fake_name_size, char);
+ C_KILL(rmp_text, fake_text_size, char);
+
+ /* Allocate the "rp_text" array */
+ C_KILL(bg, max_bg_idx, hist_type);
+
+ /* Free the "meta_class" array */
+ for (i = 0; i < max_mc_idx; i++)
+ {
+ C_FREE(meta_class_info[i].classes, max_c_idx, s16b);
+ }
+ C_FREE(meta_class_info, max_mc_idx, meta_class_type);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "p_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'p_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_player_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'p_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "st_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_st_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(st_head, header);
+
+ /* Save the "version" */
+ st_head->v_major = VERSION_MAJOR;
+ st_head->v_minor = VERSION_MINOR;
+ st_head->v_patch = VERSION_PATCH;
+ st_head->v_extra = 0;
+
+ /* Save the "record" information */
+ st_head->info_num = max_st_idx;
+ st_head->info_len = sizeof(store_info_type);
+
+ /* Save the size of "st_head" and "st_info" */
+ st_head->head_size = sizeof(header);
+ st_head->info_size = st_head->info_num * st_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "st_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "st_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_st_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'st_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "st_name" and "st_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "st_info" array */
+ C_MAKE(st_info, st_head->info_num, store_info_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(st_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "st_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'st_info.txt' file.");
+
+ /* Parse the file */
+ err = init_st_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'st_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'st_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "st_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(st_head), st_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(st_info), st_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(st_name), st_head->name_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "st_info" array */
+ C_KILL(st_info, st_head->info_num, store_info_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(st_name, fake_name_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "st_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'st_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_st_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'st_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ow_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ow_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(ow_head, header);
+
+ /* Save the "version" */
+ ow_head->v_major = VERSION_MAJOR;
+ ow_head->v_minor = VERSION_MINOR;
+ ow_head->v_patch = VERSION_PATCH;
+ ow_head->v_extra = 0;
+
+ /* Save the "record" information */
+ ow_head->info_num = max_ow_idx;
+ ow_head->info_len = sizeof(owner_type);
+
+ /* Save the size of "head" and "ow_info" */
+ ow_head->head_size = sizeof(header);
+ ow_head->info_size = ow_head->info_num * ow_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ow_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "ow_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_ow_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'ow_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "ow_name" and "ow_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "ow_info" array */
+ C_MAKE(ow_info, ow_head->info_num, owner_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(ow_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ow_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ow_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ow_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'ow_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ow_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ow_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(ow_head), ow_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(ow_info), ow_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(ow_name), ow_head->name_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "ow_info" array */
+ C_KILL(ow_info, ow_head->info_num, owner_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(ow_name, fake_name_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ow_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'ow_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_ow_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'ow_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "ba_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_ba_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(ba_head, header);
+
+ /* Save the "version" */
+ ba_head->v_major = VERSION_MAJOR;
+ ba_head->v_minor = VERSION_MINOR;
+ ba_head->v_patch = VERSION_PATCH;
+ ba_head->v_extra = 0;
+
+ /* Save the "record" information */
+ ba_head->info_num = max_ba_idx;
+ ba_head->info_len = sizeof(store_action_type);
+
+ /* Save the size of "head" and "ba_info" */
+ ba_head->head_size = sizeof(header);
+ ba_head->info_size = ba_head->info_num * ba_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ba_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "ba_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_ba_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'ba_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "ba_name" and "ba_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "ba_info" array */
+ C_MAKE(ba_info, ba_head->info_num, store_action_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(ba_name, fake_name_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "ba_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'ba_info.txt' file.");
+
+ /* Parse the file */
+ err = init_ba_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'ba_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'ba_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ba_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(ba_head), ba_head->head_size);
+
+ /* Dump the "r_info" array */
+ fd_write(fd, (char*)(ba_info), ba_head->info_size);
+
+ /* Dump the "r_name" array */
+ fd_write(fd, (char*)(ba_name), ba_head->name_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "ba_info" array */
+ C_KILL(ba_info, ba_head->info_num, store_action_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(ba_name, fake_name_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "ba_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'ba_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_ba_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'ba_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "wf_info" array
+ *
+ * Note that we let each entry have a unique "name" and "short name" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_wf_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(wf_head, header);
+
+ /* Save the "version" */
+ wf_head->v_major = VERSION_MAJOR;
+ wf_head->v_minor = VERSION_MINOR;
+ wf_head->v_patch = VERSION_PATCH;
+ wf_head->v_extra = 0;
+
+ /* Save the "record" information */
+ wf_head->info_num = max_wf_idx;
+ wf_head->info_len = sizeof(wilderness_type_info);
+
+ /* Save the size of "wf_head" and "wf_info" */
+ wf_head->head_size = sizeof(header);
+ wf_head->info_size = wf_head->info_num * wf_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "wf_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "wf_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_wf_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'wf_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Assume the size of "wf_name" and "wf_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "r_info" array */
+ C_MAKE(wf_info, wf_head->info_num, wilderness_type_info);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(wf_name, fake_name_size, char);
+ C_MAKE(wf_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "wf_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'wf_info.txt' file.");
+
+ /* Parse the file */
+ err = init_wf_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d df 'wf_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'wf_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "wf_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(wf_head), wf_head->head_size);
+
+ /* Dump the "wf_info" array */
+ fd_write(fd, (char*)(wf_info), wf_head->info_size);
+
+ /* Dump the "wf_name" array */
+ fd_write(fd, (char*)(wf_name), wf_head->name_size);
+
+ /* Dump the "wf_text" array */
+ fd_write(fd, (char*)(wf_text), wf_head->text_size);
+
+ /* Dump the "wildc2i" array */
+ fd_write(fd, (char*)(wildc2i), 256 * sizeof(int));
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "wf_info" array */
+ C_KILL(wf_info, wf_head->info_num, wilderness_type_info);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(wf_name, fake_name_size, char);
+ C_KILL(wf_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "wf_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'wf_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_wf_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'wf_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "t_info" array, by parsing a binary "image" file
+ */
+static errr init_t_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != t_head->v_major) ||
+ (test.v_minor != t_head->v_minor) ||
+ (test.v_patch != t_head->v_patch) ||
+ (test.v_extra != t_head->v_extra) ||
+ (test.info_num != t_head->info_num) ||
+ (test.info_len != t_head->info_len) ||
+ (test.head_size != t_head->head_size) ||
+ (test.info_size != t_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*t_head) = test;
+
+
+ /* Allocate the "f_info" array */
+ C_MAKE(t_info, t_head->info_num, trap_type);
+
+ /* Read the "t_info" array */
+ fd_read(fd, (char*)(t_info), t_head->info_size);
+
+
+ /* Allocate the "t_name" array */
+ C_MAKE(t_name, t_head->name_size, char);
+
+ /* Read the "t_name" array */
+ fd_read(fd, (char*)(t_name), t_head->name_size);
+
+
+#ifndef DELAY_LOAD_T_TEXT
+
+ /* Allocate the "t_text" array */
+ C_MAKE(t_text, t_head->text_size, char);
+
+ /* Read the "t_text" array */
+ fd_read(fd, (char*)(t_text), t_head->text_size);
+
+#endif /* DELAY_LOAD_T_TEXT */
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "t_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+static errr init_t_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err = 0;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(t_head, header);
+
+ /* Save the "version" */
+ t_head->v_major = VERSION_MAJOR;
+ t_head->v_minor = VERSION_MINOR;
+ t_head->v_patch = VERSION_PATCH;
+ t_head->v_extra = 0;
+
+ /* Save the "record" information */
+ t_head->info_num = max_t_idx;
+ t_head->info_len = sizeof(trap_type);
+
+ /* Save the size of "t_head" and "t_info" */
+ t_head->head_size = sizeof(header);
+ t_head->info_size = t_head->info_num * t_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "tr_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "tr_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_t_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'tr_info.raw' file.");
+ msg_print(NULL);
+ }
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "t_name" and "t_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "t_info" array */
+ C_MAKE(t_info, t_head->info_num, trap_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(t_name, fake_name_size, char);
+ C_MAKE(t_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "tr_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'tr_info.txt' file.");
+
+ /* Parse the file */
+ err = init_t_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'tr_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'tr_info.txt' file.");
+ }
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "tr_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(t_head), t_head->head_size);
+
+ /* Dump the "f_info" array */
+ fd_write(fd, (char*)(t_info), t_head->info_size);
+
+ /* Dump the "f_name" array */
+ fd_write(fd, (char*)(t_name), t_head->name_size);
+
+ /* Dump the "f_text" array */
+ fd_write(fd, (char*)(t_text), t_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "h_info" array */
+ C_KILL(t_info, t_head->info_num, trap_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(t_name, fake_name_size, char);
+ C_KILL(t_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+#endif /* ALLOW_TEMPLATES */
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "tr_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'tr_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_t_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'tr_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "al_info" array, by parsing a binary "image" file
+ */
+static errr init_al_info_raw(int fd)
+{
+ header test;
+ char *hack;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != al_head->v_major) ||
+ (test.v_minor != al_head->v_minor) ||
+ (test.v_patch != al_head->v_patch) ||
+ (test.v_extra != al_head->v_extra) ||
+ (test.info_num != al_head->info_num) ||
+ (test.info_len != al_head->info_len) ||
+ (test.head_size != al_head->head_size) ||
+ (test.info_size != al_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+ /* Accept the header */
+ (*al_head) = test;
+
+ /* Allocate the "al_info" array */
+ C_MAKE(alchemist_recipes, al_head->info_num, alchemist_recipe);
+
+ /* Read the "al_info" array */
+ fd_read(fd, (char*)(alchemist_recipes), al_head->info_size);
+
+ /* Allocate the "al_name" array */
+ C_MAKE(al_name, al_head->name_size, char );
+
+ /* Read the "al_info" array */
+ fd_read(fd, (char*)(al_name), al_head->name_size);
+
+ /* Allocate the "al_text" array */
+ C_MAKE(hack, al_head->text_size, char );
+ a_select_flags = (artifact_select_flag *) hack;
+
+ /* Read the "al_info" array */
+ fd_read(fd, (char*)(a_select_flags), al_head->text_size);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "al_info" array
+ *
+ * Not a flat array, but an array none the less
+ */
+errr init_al_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(al_head, header);
+
+ /* Save the "version" */
+ al_head->v_major = VERSION_MAJOR;
+ al_head->v_minor = VERSION_MINOR;
+ al_head->v_patch = VERSION_PATCH;
+ al_head->v_extra = 0;
+
+ /* Save the "record" information */
+ al_head->info_num = max_al_idx;
+ al_head->info_len = sizeof(alchemist_recipe);
+
+ /* Save the size of "al_head" and "al_info" */
+ al_head->head_size = sizeof(header);
+
+ al_head->info_size = al_head->info_num * al_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "al_info.raw");
+
+#if 0
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+#else
+
+fd = -1;
+
+#endif
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "al_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_al_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'v_info.raw' file.");
+ msg_print(NULL);
+ }
+
+ fake_text_size = FAKE_TEXT_SIZE;
+ fake_name_size = FAKE_NAME_SIZE;
+
+ /* Allocate the "al_info" array */
+ C_MAKE(alchemist_recipes, al_head->info_num, alchemist_recipe);
+
+ /* Allocate the fake arrays */
+ /* ok, so we fudge a bit, but
+ fake text size will ALWAYS be larger
+ than 32*5*sizeof(artifact_select_flag) = 10 int and 5 bytes
+ which is the maximum size of the a_select_flags array
+ */
+ C_MAKE(al_name, fake_name_size, char);
+
+ {
+ char *hack;
+ C_MAKE(hack, fake_text_size, char);
+ a_select_flags = (artifact_select_flag *) hack;
+ }
+
+ /*** Load the ascii template file ***/
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "al_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'al_info.txt' file.");
+
+ /* Parse the file */
+ err = init_al_info_txt(fp, buf);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'al_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'al_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "al_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(al_head), al_head->head_size);
+
+ /* Dump the "al_info" array */
+ fd_write(fd, (char*)(alchemist_recipes), al_head->info_size);
+
+ /* Dump the "al_name" array */
+ fd_write(fd, (char*)(al_name), al_head->name_size);
+
+ /* Dump the "al_info" array */
+ fd_write(fd, (char*)(a_select_flags), al_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "al_info" array */
+ C_KILL(alchemist_recipes, al_head->info_num, alchemist_recipe);
+
+ /* Free the 'Fake' arrays */
+ C_KILL(al_name, al_head->name_size, char);
+ {
+ char *hack = (char *) a_select_flags;
+ C_KILL(hack, al_head->text_size, char);
+ }
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "al_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'al_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_al_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'al_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialise the "v_info" array, by parsing a binary "image" file
+ */
+static errr init_v_info_raw(int fd)
+{
+ header test;
+
+ /* Read and Verify the header */
+ if (fd_read(fd, (char*)(&test), sizeof(header)) ||
+ (test.v_major != v_head->v_major) ||
+ (test.v_minor != v_head->v_minor) ||
+ (test.v_patch != v_head->v_patch) ||
+ (test.v_extra != v_head->v_extra) ||
+ (test.info_num != v_head->info_num) ||
+ (test.info_len != v_head->info_len) ||
+ (test.head_size != v_head->head_size) ||
+ (test.info_size != v_head->info_size))
+ {
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Accept the header */
+ (*v_head) = test;
+
+
+ /* Allocate the "v_info" array */
+ C_MAKE(v_info, v_head->info_num, vault_type);
+
+ /* Read the "v_info" array */
+ fd_read(fd, (char*)(v_info), v_head->info_size);
+
+
+ /* Allocate the "v_name" array */
+ C_MAKE(v_name, v_head->name_size, char);
+
+ /* Read the "v_name" array */
+ fd_read(fd, (char*)(v_name), v_head->name_size);
+
+
+#ifndef DELAY_LOAD_V_TEXT
+
+ /* Allocate the "v_text" array */
+ C_MAKE(v_text, v_head->text_size, char);
+
+ /* Read the "v_text" array */
+ fd_read(fd, (char*)(v_text), v_head->text_size);
+
+#endif /* DELAY_LOAD_V_TEXT */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialise the "v_info" array
+ *
+ * Note that we let each entry have a unique "name" and "text" string,
+ * even if the string happens to be empty (everyone has a unique '\0').
+ */
+errr init_v_info(void)
+{
+ int fd;
+
+ int mode = FILE_MODE;
+
+ errr err;
+
+ FILE *fp;
+
+ /* General buffer */
+ char buf[1024];
+
+
+ /*** Make the header ***/
+
+ /* Allocate the "header" */
+ MAKE(v_head, header);
+
+ /* Save the "version" */
+ v_head->v_major = VERSION_MAJOR;
+ v_head->v_minor = VERSION_MINOR;
+ v_head->v_patch = VERSION_PATCH;
+ v_head->v_extra = 0;
+
+ /* Save the "record" information */
+ v_head->info_num = max_v_idx;
+ v_head->info_len = sizeof(vault_type);
+
+ /* Save the size of "v_head" and "v_info" */
+ v_head->head_size = sizeof(header);
+ v_head->info_size = v_head->info_num * v_head->info_len;
+
+
+#ifdef ALLOW_TEMPLATES
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "v_info.raw");
+
+#if 0
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+#else
+
+ fd = -1;
+
+#endif
+
+ /* Process existing "raw" file */
+ if (fd >= 0)
+ {
+#ifdef CHECK_MODIFICATION_TIME
+
+ err = check_modification_date(fd, "v_info.txt");
+
+#endif /* CHECK_MODIFICATION_TIME */
+
+ /* Attempt to parse the "raw" file */
+ if (!err)
+ err = init_v_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Success */
+ if (!err) return (0);
+
+ /* Information */
+ msg_print("Ignoring obsolete/defective 'v_info.raw' file.");
+ msg_print(NULL);
+ }
+
+
+ /*** Make the fake arrays ***/
+
+ /* Fake the size of "v_name" and "v_text" */
+ fake_name_size = FAKE_NAME_SIZE;
+ fake_text_size = FAKE_TEXT_SIZE;
+
+ /* Allocate the "k_info" array */
+ C_MAKE(v_info, v_head->info_num, vault_type);
+
+ /* Hack -- make "fake" arrays */
+ C_MAKE(v_name, fake_name_size, char);
+ C_MAKE(v_text, fake_text_size, char);
+
+
+ /*** Load the ascii template file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, "v_info.txt");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Parse it */
+ if (!fp) quit("Cannot open 'v_info.txt' file.");
+
+ /* Parse the file */
+ err = init_v_info_txt(fp, buf, TRUE);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ cptr oops;
+
+ /* Error string */
+ oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of 'v_info.txt'.", err, error_line);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_format("Parsing '%s'.", buf);
+ msg_print(NULL);
+
+ /* Quit */
+ quit("Error in 'v_info.txt' file.");
+ }
+
+
+ /*** Dump the binary image file ***/
+
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "v_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Kill the old file */
+ (void)fd_kill(buf);
+
+ /* Attempt to create the raw file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump to the file */
+ if (fd >= 0)
+ {
+ /* Dump it */
+ fd_write(fd, (char*)(v_head), v_head->head_size);
+
+ /* Dump the "v_info" array */
+ fd_write(fd, (char*)(v_info), v_head->info_size);
+
+ /* Dump the "v_name" array */
+ fd_write(fd, (char*)(v_name), v_head->name_size);
+
+ /* Dump the "v_text" array */
+ fd_write(fd, (char*)(v_text), v_head->text_size);
+
+ /* Close */
+ (void)fd_close(fd);
+ }
+
+
+ /*** Kill the fake arrays ***/
+
+ /* Free the "v_info" array */
+ C_KILL(v_info, v_head->info_num, vault_type);
+
+ /* Hack -- Free the "fake" arrays */
+ C_KILL(v_name, fake_name_size, char);
+ C_KILL(v_text, fake_text_size, char);
+
+ /* Forget the array sizes */
+ fake_name_size = 0;
+ fake_text_size = 0;
+
+#endif /* ALLOW_TEMPLATES */
+
+
+ /*** Load the binary image file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "v_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Process existing "raw" file */
+ if (fd < 0) quit("Cannot load 'v_info.raw' file.");
+
+ /* Attempt to parse the "raw" file */
+ err = init_v_info_raw(fd);
+
+ /* Close it */
+ (void)fd_close(fd);
+
+ /* Error */
+ if (err) quit("Cannot parse 'v_info.raw' file.");
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the very basic arrays
+ */
+static void init_basic()
+{
+ int i;
+
+ /* Macro variables */
+ C_MAKE(macro__pat, MACRO_MAX, cptr);
+ C_MAKE(macro__act, MACRO_MAX, cptr);
+ C_MAKE(macro__cmd, MACRO_MAX, bool);
+
+ /* Macro action buffer */
+ C_MAKE(macro__buf, 1024, char);
+
+ /* Extended trigger macros */
+ C_MAKE(cli_info, CLI_MAX, cli_comm);
+
+ /* Wipe the directory list */
+ for (i = 0; i < 255; i++)
+ {
+ scansubdir_result[i] = NULL;
+ }
+}
+
+/*
+ * Pseudo, dummy quest initializer, to actualy disable them
+ */
+static bool quest_disable_init_hook(int q_idx)
+{
+ q_idx = q_idx;
+ return FALSE;
+}
+
+
+/*
+ * Initialise misc. values
+ */
+static errr init_misc(void)
+{
+ int xstart = 0;
+ int ystart = 0;
+ int i;
+ s32b allow_quest;
+ s32b allow_rquest;
+
+ /*** Prepare the various "bizarre" arrays ***/
+
+ /* Quark variables */
+ C_MAKE(quark__str, QUARK_MAX, cptr);
+
+ /* Message variables */
+ C_MAKE(message__ptr, MESSAGE_MAX, u16b);
+ C_MAKE(message__color, MESSAGE_MAX, byte);
+ C_MAKE(message__type, MESSAGE_MAX, byte);
+ C_MAKE(message__count, MESSAGE_MAX, u16b);
+ C_MAKE(message__buf, MESSAGE_BUF, char);
+
+ /* Hack -- No messages yet */
+ message__tail = MESSAGE_BUF;
+
+ /* Prepare powers */
+ p_ptr->powers = NULL;
+ powers_type = NULL;
+ power_max = POWER_MAX_INIT;
+ reinit_powers_type(power_max);
+ C_COPY(powers_type, powers_type_init, POWER_MAX_INIT, power_type);
+
+ /* Prepare quests */
+ call_lua("get_module_info", "(s)", "d", "C_quest", &allow_quest);
+ call_lua("get_module_info", "(s)", "d", "rand_quest", &allow_rquest);
+
+ quest = NULL;
+ max_q_idx = MAX_Q_IDX_INIT;
+ reinit_quests(max_q_idx);
+
+ C_COPY(quest, quest_init_tome, MAX_Q_IDX_INIT, quest_type);
+
+ /* If we dont allow C quests, we dont let them init */
+ if (!allow_quest)
+ {
+ for (i = 0; i < MAX_Q_IDX_INIT; i++)
+ {
+ if (allow_rquest && (i == QUEST_RANDOM))
+ continue;
+ quest[i].init = quest_disable_init_hook;
+ }
+ }
+
+ /* Prepare gods */
+ deity_info = NULL;
+ max_gods = MAX_GODS_INIT;
+ reinit_gods(max_gods);
+
+ C_COPY(deity_info, deity_info_init, MAX_GODS_INIT, deity_type);
+
+ /* Prepare schools */
+ max_spells = 0;
+ max_schools = 0;
+ schools = NULL;
+ school_spells = NULL;
+
+ process_hooks(HOOK_INIT_GAME, "(s)", "begin");
+
+ /* Initialise the values */
+ process_dungeon_file(NULL, "misc.txt", &ystart, &xstart, 0, 0, TRUE);
+
+ /* Init the spell effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ effects[i].time = 0;
+
+ return 0;
+}
+
+
+/*
+ * Initialise town array
+ */
+static errr init_towns(void)
+{
+ int i = 0, j = 0;
+
+ /*** Prepare the Towns ***/
+
+ /* Allocate the towns */
+ C_MAKE(town_info, max_towns, town_type);
+
+ for (i = 1; i < max_towns; i++)
+ {
+ if (i <= max_real_towns) town_info[i].flags |= (TOWN_REAL);
+
+ /* Allocate the stores */
+ C_MAKE(town_info[i].store, max_st_idx, store_type);
+
+ /* Fill in each store */
+ for (j = 0; j < max_st_idx; j++)
+ {
+ /* Access the store */
+ store_type *st_ptr = &town_info[i].store[j];
+
+ /* Know who we are */
+ st_ptr->st_idx = j;
+
+ /* Assume full stock */
+ st_ptr->stock_size = 0;
+ }
+ }
+ return 0;
+}
+
+void create_stores_stock(int t)
+{
+ int j;
+ town_type *t_ptr = &town_info[t];
+
+ if (t_ptr->stocked) return;
+
+ for (j = 0; j < max_st_idx; j++)
+ {
+ store_type *st_ptr = &t_ptr->store[j];
+
+ /* Assume full stock */
+ st_ptr->stock_size = st_info[j].max_obj;
+
+ /* Allocate the stock */
+ C_MAKE(st_ptr->stock, st_ptr->stock_size, object_type);
+ }
+ t_ptr->stocked = TRUE;
+}
+
+/*
+ * Pointer to wilderness_map
+ */
+typedef wilderness_map *wilderness_map_ptr;
+
+/*
+ * Initialise wilderness map array
+ */
+static errr init_wilderness(void)
+{
+ int i;
+
+ /* Allocate the wilderness (two-dimension array) */
+ C_MAKE(wild_map, max_wild_y, wilderness_map_ptr);
+ C_MAKE(wild_map[0], max_wild_x * max_wild_y, wilderness_map);
+
+ /* Init the other pointers */
+ for (i = 1; i < max_wild_y; i++)
+ wild_map[i] = wild_map[0] + i * max_wild_x;
+
+ /* No encounter right now */
+ generate_encounter = FALSE;
+
+ return 0;
+}
+
+/*
+ * XXX XXX XXX XXX XXX Realloc is not guaranteed to work (see main-gtk.c
+ * and main-mac.c.
+ */
+void reinit_powers_type(s16b new_size)
+{
+ power_type *new_powers_type;
+ bool *new_powers;
+
+ C_MAKE(new_powers_type, new_size, power_type);
+ C_MAKE(new_powers, new_size, bool);
+
+ /* Reallocate the extra memory */
+ if (powers_type && p_ptr->powers)
+ {
+ C_COPY(new_powers_type, powers_type, power_max, power_type);
+ C_COPY(new_powers, p_ptr->powers, power_max, bool);
+
+ C_FREE(powers_type, power_max, power_type);
+ C_FREE(p_ptr->powers, power_max, bool);
+ }
+
+ powers_type = new_powers_type;
+ p_ptr->powers = new_powers;
+
+ power_max = new_size;
+}
+
+void reinit_quests(s16b new_size)
+{
+ quest_type *new_quest;
+
+ C_MAKE(new_quest, new_size, quest_type);
+
+ /* Reallocate the extra memory */
+ if (quest)
+ {
+ C_COPY(new_quest, quest, max_q_idx, quest_type);
+
+ C_FREE(quest, max_q_idx, quest_type);
+ }
+
+ quest = new_quest;
+
+ max_q_idx = new_size;
+}
+
+void reinit_gods(s16b new_size)
+{
+ deity_type *new_deity;
+
+ C_MAKE(new_deity, new_size, deity_type);
+
+ /* Reallocate the extra memory */
+ if (deity_info)
+ {
+ C_COPY(new_deity, deity_info, max_gods, deity_type);
+
+ C_FREE(deity_info, max_gods, deity_type);
+ }
+
+ deity_info = new_deity;
+
+ max_gods = new_size;
+}
+
+void init_spells(s16b new_size)
+{
+ /* allocate the extra memory */
+ C_MAKE(school_spells, new_size, spell_type);
+ max_spells = new_size;
+}
+
+void init_schools(s16b new_size)
+{
+ /* allocate the extra memory */
+ C_MAKE(schools, new_size, school_type);
+ max_schools = new_size;
+}
+
+void init_corruptions(s16b new_size)
+{
+ /* allocate the extra memory */
+ C_MAKE(p_ptr->corruptions, new_size, bool);
+ max_corruptions = new_size;
+}
+
+/*
+ * Initialise some other arrays
+ */
+static errr init_other(void)
+{
+ int i, n;
+
+ /*** Prepare the "dungeon" information ***/
+
+ /* Allocate and Wipe the special gene flags */
+ C_MAKE(m_allow_special, max_r_idx, bool);
+ C_MAKE(k_allow_special, max_k_idx, bool);
+ C_MAKE(a_allow_special, max_a_idx, bool);
+
+
+ /*** Prepare "vinfo" array ***/
+
+ /* Used by "update_view()" */
+ (void)vinfo_init();
+
+
+ /* Allocate and Wipe the object list */
+ C_MAKE(o_list, max_o_idx, object_type);
+
+ /* Allocate and Wipe the monster list */
+ C_MAKE(m_list, max_m_idx, monster_type);
+
+ /* Allocate and Wipe the to keep monster list */
+ C_MAKE(km_list, max_m_idx, monster_type);
+
+ /* Allocate and Wipe the max dungeon level */
+ C_MAKE(max_dlv, max_d_idx, s16b);
+
+ /* Allocate and Wipe the special levels */
+ for (i = 0; i < MAX_DUNGEON_DEPTH; i++)
+ {
+ C_MAKE(special_lvl[i], max_d_idx, bool);
+ }
+
+ /* Allocate and wipe each line of the cave */
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ /* Allocate one row of the cave */
+ C_MAKE(cave[i], MAX_WID, cave_type);
+ }
+
+ /*** Pre-allocate the basic "auto-inscriptions" ***/
+
+ /* The "basic" feelings */
+ (void)quark_add("cursed");
+ (void)quark_add("broken");
+ (void)quark_add("average");
+ (void)quark_add("good");
+
+ /* The "extra" feelings */
+ (void)quark_add("excellent");
+ (void)quark_add("worthless");
+ (void)quark_add("special");
+ (void)quark_add("terrible");
+
+ /* Some extra strings */
+ (void)quark_add("uncursed");
+ (void)quark_add("on sale");
+
+
+ /*** Prepare the options ***/
+
+ /* Scan the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Set the "default" options */
+ if (option_info[i].o_var)
+ {
+ /* Accept */
+ option_mask[os] |= (1L << ob);
+
+ /* Set */
+ if (option_info[i].o_norm)
+ {
+ /* Set */
+ option_flag[os] |= (1L << ob);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ option_flag[os] &= ~(1L << ob);
+ }
+ }
+ }
+
+ /* Analyze the windows */
+ for (n = 0; n < 8; n++)
+ {
+ /* Analyze the options */
+ for (i = 0; i < 32; i++)
+ {
+ /* Accept */
+ if (window_flag_desc[i])
+ {
+ /* Accept */
+ window_mask[n] |= (1L << i);
+ }
+ }
+ }
+
+
+ /*
+ * Install the various level generators
+ */
+ add_level_generator("dungeon", level_generate_dungeon, TRUE, TRUE, TRUE, TRUE);
+ add_level_generator("maze", level_generate_maze, TRUE, TRUE, TRUE, TRUE);
+ add_level_generator("life", level_generate_life, TRUE, TRUE, TRUE, TRUE);
+
+ /*** Pre-allocate space for the "format()" buffer ***/
+
+ /* Hack -- Just call the "format()" function */
+ (void)format("%s (%s).", "Dark God <darkgod@t-o-m-e.net>", MAINTAINER);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise some other arrays
+ */
+static errr init_alloc(void)
+{
+ int i, j;
+
+ object_kind *k_ptr;
+
+ monster_race *r_ptr;
+
+ alloc_entry *table;
+
+ s16b num[MAX_DEPTH_MONSTER];
+
+ s16b aux[MAX_DEPTH_MONSTER];
+
+ s16b *tmp;
+
+ /*** Analyze object allocation info ***/
+
+ /* Clear the "aux" array */
+ tmp = C_WIPE(&aux, MAX_DEPTH_MONSTER, s16b);
+
+ /* Clear the "num" array */
+ tmp = C_WIPE(&num, MAX_DEPTH_MONSTER, s16b);
+
+ /* Size of "alloc_kind_table" */
+ alloc_kind_size = 0;
+
+ /* Scan the objects */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ k_ptr = &k_info[i];
+
+ /* Scan allocation pairs */
+ for (j = 0; j < 4; j++)
+ {
+ /* Count the "legal" entries */
+ if (k_ptr->chance[j])
+ {
+ /* Count the entries */
+ alloc_kind_size++;
+
+ /* Group by level */
+ num[k_ptr->locale[j]]++;
+ }
+ }
+ }
+
+ /* Collect the level indexes */
+ for (i = 1; i < MAX_DEPTH_MONSTER; i++)
+ {
+ /* Group by level */
+ num[i] += num[i - 1];
+ }
+
+ /* Paranoia */
+ if (!num[0]) quit("No town objects!");
+
+
+ /*** Initialise object allocation info ***/
+
+ /* Allocate the alloc_kind_table */
+ C_MAKE(alloc_kind_table, alloc_kind_size, alloc_entry);
+
+ /* Access the table entry */
+ table = alloc_kind_table;
+
+ /* Scan the objects */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ k_ptr = &k_info[i];
+
+ /* Scan allocation pairs */
+ for (j = 0; j < 4; j++)
+ {
+ /* Count the "legal" entries */
+ if (k_ptr->chance[j])
+ {
+ int p, x, y, z;
+
+ /* Extract the base level */
+ x = k_ptr->locale[j];
+
+ /* Extract the base probability */
+ p = (100 / k_ptr->chance[j]);
+
+ /* Skip entries preceding our locale */
+ y = (x > 0) ? num[x - 1] : 0;
+
+ /* Skip previous entries at this locale */
+ z = y + aux[x];
+
+ /* Load the entry */
+ table[z].index = i;
+ table[z].level = x;
+ table[z].prob1 = p;
+ table[z].prob2 = p;
+ table[z].prob3 = p;
+
+ /* Another entry complete for this locale */
+ aux[x]++;
+ }
+ }
+ }
+
+
+ /*** Analyze monster allocation info ***/
+
+ /* Clear the "aux" array */
+ tmp = C_WIPE(&aux, MAX_DEPTH_MONSTER, s16b);
+
+ /* Clear the "num" array */
+ tmp = C_WIPE(&num, MAX_DEPTH_MONSTER, s16b);
+
+ /* Size of "alloc_race_table" */
+ alloc_race_size = 0;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ /* Get the i'th race */
+ r_ptr = &r_info[i];
+
+ /* Legal monsters */
+ if (r_ptr->rarity)
+ {
+ /* Count the entries */
+ alloc_race_size++;
+
+ /* Group by level */
+ num[r_ptr->level]++;
+ }
+ }
+
+ /* Collect the level indexes */
+ for (i = 1; i < MAX_DEPTH_MONSTER; i++)
+ {
+ /* Group by level */
+ num[i] += num[i - 1];
+ }
+
+ /* Paranoia */
+ if (!num[0]) quit("No town monsters!");
+
+
+ /*** Initialise monster allocation info ***/
+
+ /* Allocate the alloc_race_table */
+ C_MAKE(alloc_race_table, alloc_race_size, alloc_entry);
+
+ /* Access the table entry */
+ table = alloc_race_table;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ /* Get the i'th race */
+ r_ptr = &r_info[i];
+
+ /* Count valid pairs */
+ if (r_ptr->rarity)
+ {
+ int p, x, y, z;
+
+ /* Extract the base level */
+ x = r_ptr->level;
+
+ /* Extract the base probability */
+ p = (100 / r_ptr->rarity);
+
+ /* Skip entries preceding our locale */
+ y = (x > 0) ? num[x - 1] : 0;
+
+ /* Skip previous entries at this locale */
+ z = y + aux[x];
+
+ /* Load the entry */
+ table[z].index = i;
+ table[z].level = x;
+ table[z].prob1 = p;
+ table[z].prob2 = p;
+ table[z].prob3 = p;
+
+ /* Another entry complete for this locale */
+ aux[x]++;
+ }
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+/* Init the sets in a_info */
+void init_sets_aux()
+{
+ int i, j;
+
+ for (i = 0; i < max_a_idx; i++)
+ a_info[i].set = -1;
+ for (i = 0; i < max_set_idx; i++)
+ {
+ for (j = 0; j < set_info[i].num; j++)
+ {
+ a_info[set_info[i].arts[j].a_idx].set = i;
+ }
+ }
+}
+
+/*
+ * Mark guardians and their artifacts with SPECIAL_GENE flag
+ */
+static void init_guardians(void)
+{
+ int i;
+
+ /* Scan dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+
+ /* Mark the guadian monster */
+ if (d_ptr->final_guardian)
+ {
+ monster_race *r_ptr = &r_info[d_ptr->final_guardian];
+
+ r_ptr->flags9 |= RF9_SPECIAL_GENE;
+
+ /* Mark the final artifact */
+ if (d_ptr->final_artifact)
+ {
+ artifact_type *a_ptr = &a_info[d_ptr->final_artifact];
+
+ a_ptr->flags4 |= TR4_SPECIAL_GENE;
+ }
+
+ /* Mark the final object */
+ if (d_ptr->final_object)
+ {
+ object_kind *k_ptr = &k_info[d_ptr->final_object];
+
+ k_ptr->flags4 |= TR4_SPECIAL_GENE;
+ }
+
+ /* Give randart if there are no final artifacts */
+ if (!(d_ptr->final_artifact) && !(d_ptr->final_object))
+ {
+ r_ptr->flags7 |= RF7_DROP_RANDART;
+ }
+ }
+ }
+}
+
+/*
+ * Hack -- Explain a broken "lib" folder and quit (see below).
+ *
+ * XXX XXX XXX This function is "messy" because various things
+ * may or may not be initialised, but the "plog()" and "quit()"
+ * functions are "supposed" to work under any conditions.
+ */
+static void init_angband_aux(cptr why)
+{
+ /* Why */
+ plog(why);
+
+ /* Explain */
+ plog("The 'lib' directory is probably missing or broken.");
+
+ /* More details */
+ plog("Perhaps the archive was not extracted correctly.");
+
+ /* Explain */
+ plog("See the 'README' file for more information.");
+
+ /* Quit with error */
+ quit("Fatal Error.");
+}
+
+/*
+ * Hack -- main Angband initialisation entry point
+ *
+ * Verify some files, display the "news.txt" file, create
+ * the high score file, initialise all internal arrays, and
+ * load the basic "user pref files".
+ *
+ * Note that we blindly assume that "news2.txt" exists. XXX
+ *
+ * Be very careful to keep track of the order in which things
+ * are initialised, in particular, the only thing *known* to
+ * be available when this function is called is the "z-term.c"
+ * package, and that may not be fully initialised until the
+ * end of this function, when the default "user pref files"
+ * are loaded and "Term_xtra(TERM_XTRA_REACT,0)" is called.
+ *
+ * Note that this function attempts to verify the "news" file,
+ * and the game aborts (cleanly) on failure, since without the
+ * "news" file, it is likely that the "lib" folder has not been
+ * correctly located. Otherwise, the news file is displayed for
+ * the user.
+ *
+ * Note that this function attempts to verify (or create) the
+ * "high score" file, and the game aborts (cleanly) on failure,
+ * since one of the most common "extraction" failures involves
+ * failing to extract all sub-directories (even empty ones), such
+ * as by failing to use the "-d" option of "pkunzip", or failing
+ * to use the "save empty directories" option with "Compact Pro".
+ * This error will often be caught by the "high score" creation
+ * code below, since the "lib/apex" directory, being empty in the
+ * standard distributions, is most likely to be "lost", making it
+ * impossible to create the high score file.
+ *
+ * Note that various things are initialised by this function,
+ * including everything that was once done by "init_some_arrays".
+ *
+ * This initialisation involves the parsing of special files
+ * in the "lib/data" and sometimes the "lib/edit" directories.
+ *
+ * Note that the "template" files are initialised first, since they
+ * often contain errors. This means that macros and message recall
+ * and things like that are not available until after they are done.
+ *
+ * We load the default "user pref files" here in case any "color"
+ * changes are needed before character creation.
+ *
+ * Note that the "graf-xxx.prf" file must be loaded separately,
+ * if needed, in the first (?) pass through "TERM_XTRA_REACT".
+ */
+void init_angband(void)
+{
+ int fd = -1;
+
+ int mode = FILE_MODE;
+
+ FILE *fp;
+
+ char *news_file;
+
+ char buf[1024];
+
+ /* Init some VERY basic stuff, like macro arrays */
+ init_basic();
+
+ /* Select & init a module if needed */
+ select_module();
+
+ /*** Choose which news.txt file to use ***/
+
+ /* Choose the news file */
+ switch (time(NULL) % 2)
+ {
+ default:
+ {
+ news_file = "news.txt";
+ break;
+ }
+
+ case 0:
+ {
+ news_file = "news2.txt";
+ break;
+ }
+ }
+
+ /*** Verify the "news" file ***/
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, news_file);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failure */
+ if (fd < 0)
+ {
+ char why[1024];
+
+ /* Message */
+ sprintf(why, "Cannot access the '%s' file!", buf);
+
+ /* Crash and burn */
+ init_angband_aux(why);
+ }
+
+ /* Close it */
+ (void)fd_close(fd);
+
+
+ /*** Display the "news" file ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, news_file);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the News file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Dump */
+ if (fp)
+ {
+ int i = 0;
+
+ /* Dump the file to the screen */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Display and advance - we use display_message to parse colour codes XXX */
+ display_message(0, i++, strlen(buf), TERM_WHITE, buf);
+ }
+
+ /* Close */
+ my_fclose(fp);
+ }
+
+ /* Flush it */
+ Term_fresh();
+
+
+ /*** Verify (or create) the "high score" file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Attempt to open the high score file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failure */
+ if (fd < 0)
+ {
+ /* File type is "DATA" */
+ FILE_TYPE(FILE_TYPE_DATA);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Create a new high score file */
+ fd = fd_make(buf, mode);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Failure */
+ if (fd < 0)
+ {
+ char why[1024];
+
+ /* Message */
+ sprintf(why, "Cannot create the '%s' file!", buf);
+
+ /* Crash and burn */
+ init_angband_aux(why);
+ }
+ }
+
+ /* Close it */
+ (void)fd_close(fd);
+
+
+ /*** Initialise some arrays ***/
+
+ /* Initilize the socket */
+ zsock_init();
+
+ /* Initialise misc. values */
+ note("[Initialising values... (misc)]");
+ if (init_misc()) quit("Cannot initialise misc. values");
+
+ wipe_hooks();
+
+ /* Initialise some other arrays */
+ note("[Initialising lua scripting... (lua)]");
+ init_lua();
+ init_lua_init();
+
+ /* Initialise skills info */
+ note("[Initialising arrays... (skills)]");
+ if (init_s_info()) quit("Cannot initialise skills");
+
+ /* Initialise abilities info */
+ note("[Initialising arrays... (abilities)]");
+ if (init_ab_info()) quit("Cannot initialise abilities");
+
+ /* Initialise alchemy info */
+ note("[Initialising arrays... (alchemy)]");
+ if (init_al_info()) quit("Cannot initialise alchemy");
+
+ /* Initialise player info */
+ note("[Initialising arrays... (players)]");
+ if (init_player_info()) quit("Cannot initialise players");
+
+ /* Initialise feature info */
+ note("[Initialising arrays... (features)]");
+ if (init_f_info()) quit("Cannot initialise features");
+
+ /* Initialise object info */
+ note("[Initialising arrays... (objects)]");
+ if (init_k_info()) quit("Cannot initialise objects");
+
+ /* Initialise artifact info */
+ note("[Initialising arrays... (artifacts)]");
+ if (init_a_info()) quit("Cannot initialise artifacts");
+
+ /* Initialise set info */
+ note("[Initialising item sets... (sets)]");
+ if (init_set_info()) quit("Cannot initialise item sets");
+ init_sets_aux();
+
+ /* Initialise ego-item info */
+ note("[Initialising arrays... (ego-items)]");
+ if (init_e_info()) quit("Cannot initialise ego-items");
+
+ /* Initialise randart parts info */
+ note("[Initialising arrays... (randarts)]");
+ if (init_ra_info()) quit("Cannot initialise randarts");
+
+ /* Initialise monster info */
+ note("[Initialising arrays... (monsters)]");
+ if (init_r_info()) quit("Cannot initialise monsters");
+
+ /* Initialise ego monster info */
+ note("[Initialising arrays... (ego monsters)]");
+ if (init_re_info()) quit("Cannot initialise ego monsters");
+
+ /* Initialise dungeon type info */
+ note("[Initialising arrays... (dungeon types)]");
+ if (init_d_info()) quit("Cannot initialise dungeon types");
+ init_guardians();
+
+ /* Initialise actions type info */
+ note("[Initialising arrays... (action types)]");
+ if (init_ba_info()) quit("Cannot initialise action types");
+
+ /* Initialise owners type info */
+ note("[Initialising arrays... (owners types)]");
+ if (init_ow_info()) quit("Cannot initialise owners types");
+
+ /* Initialise stores type info */
+ note("[Initialising arrays... (stores types)]");
+ if (init_st_info()) quit("Cannot initialise stores types");
+
+ /* Initialise wilderness features array */
+ note("[Initialising arrays... (wilderness features)]");
+ if (init_wf_info()) quit("Cannot initialise wilderness features");
+
+ /* Initialise wilderness map array */
+ note("[Initialising arrays... (wilderness map)]");
+ if (init_wilderness()) quit("Cannot initialise wilderness map");
+
+ /* Initialise town array */
+ note("[Initialising arrays... (towns)]");
+ if (init_towns()) quit("Cannot initialise towns");
+
+ /* Initialise trap info */
+ note("[Initialising arrays... (traps)]");
+ if (init_t_info()) quit("Cannot initialise traps");
+
+ /* Initialise some other arrays */
+ note("[Initialising arrays... (other)]");
+ if (init_other()) quit("Cannot initialise other stuff");
+
+ /* Initialise some other arrays */
+ note("[Initialising arrays... (alloc)]");
+ if (init_alloc()) quit("Cannot initialise alloc stuff");
+
+ /* Init random artifact names */
+ build_prob(artifact_names_list);
+
+ /*** Load default user pref files ***/
+
+ /* Initialise feature info */
+ note("[Initialising user pref files...]");
+
+ /* Access the "basic" pref file */
+ strcpy(buf, "pref.prf");
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "basic" system pref file */
+ sprintf(buf, "pref-%s.prf", ANGBAND_SYS);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "user" pref file */
+ sprintf(buf, "user.prf");
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "user" system pref file */
+ sprintf(buf, "user-%s.prf", ANGBAND_SYS);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Initialise the automatizer */
+ tome_dofile_anywhere(ANGBAND_DIR_CORE, "auto.lua", TRUE);
+ tome_dofile_anywhere(ANGBAND_DIR_USER, "automat.atm", FALSE);
+
+ /* Done */
+ note("[Initialisation complete]");
+
+ process_hooks(HOOK_INIT_GAME, "(s)", "end");
+}
diff --git a/src/irc.c b/src/irc.c
new file mode 100644
index 00000000..7d87d38f
--- /dev/null
+++ b/src/irc.c
@@ -0,0 +1,297 @@
+/* File: irc.c */
+
+/* Purpose: irc chat */
+
+/*
+ * Copyright (c) 2001 DarkGod, Andrew Sidwell
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#define IRC_SERVER "irc.worldirc.org"
+#define IRC_PORT "6667"
+#define IRC_CHANNEL "#tome"
+
+/*
+ * By the way, CTCP's for unique kills and artefact finds would be nice to
+ * have, for example:
+ *
+ * *pelpel finds Long Sword 'Ringil' (4d5) (+22,+25) (+10 to speed) :)
+ */
+
+ip_connection tome_irc_forge;
+ip_connection *tome_irc = &tome_irc_forge;
+bool irc_can_join = FALSE;
+char irc_nick[30];
+char irc_world[100];
+
+void irc_connect()
+{
+ char buf[500], *s;
+ int rnd_name = randint(999);
+
+ if (tome_irc->connected) return;
+
+ sprintf(irc_world, "%99s", IRC_CHANNEL);
+ sprintf(irc_nick, "Dummy_%03d", rnd_name);
+ get_string("Enter Nickname: ", irc_nick, 10);
+
+ zsock.setup(tome_irc, IRC_SERVER, atoi(IRC_PORT), ZSOCK_TYPE_TCP, FALSE);
+ zsock.open(tome_irc);
+ zsock.write_simple(tome_irc, format("NICK %s\r\n", irc_nick));
+ zsock.wait(tome_irc, 40);
+ zsock.read_simple(tome_irc, buf, 500);
+ s = strchr(buf, ':');
+ zsock.write_simple(tome_irc, format("PONG %s\r\n", s));
+ zsock.write_simple(tome_irc, format("USER tome 0 *BIRC :%s %d.%d.%d User\r\n",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH));
+#if 0 /* Pfft spoilsport */
+ while (!irc_can_join)
+ irc_poll();
+#endif
+
+ zsock.write_simple(tome_irc, format("JOIN %s\r\n", irc_world));
+
+ cmsg_print(TERM_L_GREEN, "Connected to IRC");
+
+ zsock.add_timer(irc_poll);
+}
+
+void irc_change_nick()
+{
+ return;
+}
+
+void irc_disconnect()
+{
+ if (!tome_irc->connected) return;
+ irc_can_join = FALSE;
+
+ irc_quit(format("%s %d.%d.%d", game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH));
+
+ cmsg_print(TERM_L_RED, "Disconnected from IRC");
+}
+
+void irc_disconnect_aux(char *str, bool message)
+{
+ if (!tome_irc->connected) return;
+ irc_can_join = FALSE;
+
+ irc_quit(str);
+
+ if (message) cmsg_print(TERM_L_RED, "Disconnected from IRC");
+}
+
+void irc_emote(char *buf)
+{
+ char *b;
+ char *base = "PRIVMSG %s :%cACTION %s%c\r\n";
+
+ if (!tome_irc->connected) return;
+
+ C_MAKE(b, strlen(buf) + strlen(base) + 1, char);
+ sprintf(b, base, irc_world, 1, buf, 1);
+ zsock.write_simple(tome_irc, b);
+ sprintf(b, "* %s %s", irc_nick, buf);
+ message_add(MESSAGE_IRC, b, TERM_YELLOW);
+ C_FREE(b, strlen(buf) + strlen(base) + 1, char);
+ fix_irc_message();
+}
+
+void irc_chat()
+{
+ char buf[80] = "";
+
+ if (!tome_irc->connected) return;
+ if (get_string("Say: ", buf, 80))
+ {
+ if (prefix(buf, "/me "))
+ {
+ irc_emote(buf + 4);
+ }
+ else if ((prefix(buf, "/join ")) && (buf[6] != '\0'))
+ {
+ zsock.write_simple(tome_irc, format("PART %s\r\n", irc_world));
+ sprintf(irc_world, "%99s", buf + 6);
+ zsock.write_simple(tome_irc, format("JOIN %s\r\n", irc_world));
+ }
+ else
+ {
+ zsock.write_simple(tome_irc, format("PRIVMSG %s :%s\r\n", irc_world, buf /*, 3, irc_world */));
+ message_add(MESSAGE_IRC, format("<%s> #w%s", irc_nick, buf), TERM_L_BLUE);
+ fix_irc_message();
+ }
+ }
+}
+
+#define TERM_CTCP TERM_L_GREEN
+#define TERM_SERVER TERM_L_BLUE
+#define TERM_CHAT1 TERM_YELLOW
+#define TERM_CHAT2 TERM_WHITE
+
+void irc_poll()
+{
+ char buf[5000], *next, *nick, *space;
+
+ if (tome_irc->connected && zsock.can_read(tome_irc))
+ {
+ zsock.read_simple(tome_irc, buf, 2500);
+
+ if (prefix(buf, "PING "))
+ {
+ message_add(MESSAGE_IRC, format("*** Recieved a PING request from server %s.", buf + 6), TERM_SERVER);
+ zsock.write_simple(tome_irc, format("PONG %s\r\n", buf + 5));
+ return;
+ }
+ if (*buf != ':') return;
+ nick = buf + 1;
+
+ space = strchr(nick, ' ');
+ if (space)
+ {
+ if (prefix(space + 1, "376"))
+ irc_can_join = TRUE;
+ }
+
+ if (prefix(nick, "_"))
+ {
+ nick = buf + 6;
+ }
+
+ next = strchr(nick, '!');
+ if (next == NULL) return;
+ *next = '\0';
+ next++;
+ next = strchr(next, ' ');
+ if (next == NULL) return;
+ next++;
+ if (prefix(next, "PRIVMSG"))
+ {
+ next = strchr(next, ':');
+ if (next == NULL) return;
+ *next = '\0';
+ next++;
+ if (*next == 1)
+ {
+ next++;
+ if (prefix(next, "ACTION"))
+ {
+ u32b i = 0, j = 0, max = (79 - strlen(nick) - 3);
+ bool nicked = FALSE;
+ char tmp[90];
+
+ next += 7;
+ if (strlen(next)) next[strlen(next) - 1] = '\0';
+
+ while (next[i])
+ {
+ tmp[j++] = next[i++];
+ if (j > max)
+ {
+ tmp[j] = '\0';
+ if (nicked)
+ message_add(MESSAGE_IRC, format("%s", tmp), TERM_CHAT1);
+ else
+ message_add(MESSAGE_IRC, format("* %s %s", nick, tmp), TERM_CHAT1);
+ nicked = TRUE;
+ j = 0;
+ }
+ }
+ if (j > 0)
+ {
+ tmp[j] = '\0';
+ if (nicked)
+ message_add(MESSAGE_IRC, format("%s", tmp), TERM_CHAT1);
+ else
+ message_add(MESSAGE_IRC, format("* %s %s", nick, tmp), TERM_CHAT1);
+ }
+
+ fix_irc_message();
+ }
+ else if (prefix(next, "PING"))
+ {
+ message_add(MESSAGE_IRC, format("*** PING request from %s", nick), TERM_CTCP);
+ fix_irc_message();
+
+ zsock.write_simple(tome_irc, format("NOTICE %s :%cPING %d%c\r\n", nick, 1, next, 1));
+ }
+ else if (prefix(next, "NICK"))
+ {
+ message_add(MESSAGE_IRC, format("*** NICK request from %s", nick), TERM_CTCP);
+ fix_irc_message();
+
+ zsock.write_simple(tome_irc, format("NOTICE %s :%cNICK %s%c\r\n", nick, 1, irc_nick, 1));
+ }
+ else if (prefix(next, "VERSION"))
+ {
+ message_add(MESSAGE_IRC, format("*** VERSION request from %s", nick), TERM_CTCP);
+ fix_irc_message();
+
+ zsock.write_simple(tome_irc, format("NOTICE %s :%cVERSION %s %d.%d.%d%c\r\n", nick, 1, game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, 1));
+ }
+ }
+ else
+ {
+ u32b i = 0, j = 0, max = (79 - strlen(nick) - 3);
+ bool nicked = FALSE;
+ char tmp[90];
+
+ while (next[i])
+ {
+ tmp[j++] = next[i++];
+ if (j > max)
+ {
+ tmp[j] = '\0';
+ if (nicked)
+ message_add(MESSAGE_IRC, format("#w%s", tmp), TERM_CHAT1);
+ else
+ message_add(MESSAGE_IRC, format("#y<%s> #w%s", nick, tmp), TERM_CHAT1);
+ nicked = TRUE;
+ j = 0;
+ }
+ }
+ if (j > 0)
+ {
+ tmp[j] = '\0';
+ if (nicked)
+ message_add(MESSAGE_IRC, format("#w%s", tmp), TERM_CHAT1);
+ else
+ message_add(MESSAGE_IRC, format("#y<%s> #w%s", nick, tmp), TERM_CHAT1);
+ }
+ fix_irc_message();
+ }
+ }
+ if (prefix(next, "JOIN"))
+ {
+ message_add(MESSAGE_IRC, format("%s has entered the Chatroom", nick), TERM_YELLOW);
+ fix_irc_message();
+ }
+ if (prefix(next, "QUIT"))
+ {
+ next = strchr(next, ':');
+ if (next == NULL) return;
+ *next = '\0';
+ next++;
+ message_add(MESSAGE_IRC, format("%s has quit the Chatroom (%s)", nick, next), TERM_YELLOW);
+ fix_irc_message();
+ }
+ }
+}
+
+
+void irc_quit(char *str)
+{
+ char buf[300];
+
+ zsock.remove_timer(irc_poll);
+
+ sprintf(buf, "QUIT :%s\r\n", str);
+
+ zsock.write_simple(tome_irc, buf);
+ zsock.close(tome_irc);
+ zsock.unsetup(tome_irc);
+}
diff --git a/src/iso/hackdef.h b/src/iso/hackdef.h
new file mode 100644
index 00000000..413d77ad
--- /dev/null
+++ b/src/iso/hackdef.h
@@ -0,0 +1,44 @@
+/* hackdef.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* hackdef.c
+ * definion of hacklevel for iso view
+ *
+ * Hj. Maltahner, Feb. 2001
+ */
+
+
+/*
+ * this enables somthing like a bigscreen mode but keeps a
+ * resonable clean interfacing between iso-view and angband
+ * this requires changes to map_info() to support clipping.
+ *
+ * Hj. Maltahner, Feb. 2001
+ */
+#define USE_SMALL_ISO_HACK
diff --git a/src/iso/readme.txt b/src/iso/readme.txt
new file mode 100644
index 00000000..ecef9638
--- /dev/null
+++ b/src/iso/readme.txt
@@ -0,0 +1,6 @@
+The iso view sources will reside here.
+
+At the moment this is just a test for CVS access.
+
+--
+Hansjoerg Malthaner
diff --git a/src/iso/simgraph.c b/src/iso/simgraph.c
new file mode 100644
index 00000000..1797f037
--- /dev/null
+++ b/src/iso/simgraph.c
@@ -0,0 +1,1429 @@
+/* simgraph.c
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph graphics engine.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+/* simgraph.c
+ *
+ * Versuch einer Graphic fuer Simulationsspiele
+ * Hj. Malthaner, Aug. 1997
+ *
+ * A try to create a graphics engine for simulation games.
+ *
+ * 3D, isometrische Darstellung
+ *
+ * 3D, isometric display
+ *
+ *
+ * 18.11.97 lineare Speicherung fuer Images -> hoehere Performance
+ * 22.03.00 run längen Speicherung fuer Images -> hoehere Performance
+ * 15.08.00 dirty tile verwaltung fuer effizientere updates
+ */
+
+//#define DEBUG 1
+
+
+#if defined(MSDOS) || defined(__MINGW32__)
+#define USE_SOFTPOINTER
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "simgraph.h"
+#include "simsys.h"
+
+extern unsigned int class;
+extern unsigned int code;
+extern int mx,my; /* es sind negative Koodinaten mgl */
+
+#ifdef USE_SOFTPOINTER
+static int softpointer = 261;
+static int old_my = -1; // die icon leiste muss neu gezeichnet werden wenn der
+ // Mauszeiger darueber schwebt
+#endif
+
+static unsigned char dr_fonttab[2048]; /* Unser Zeichensatz sitzt hier */
+
+/*
+ * pixel value type, RGB 555
+ */
+typedef unsigned short PIXVAL;
+
+
+struct imd {
+ int y; // offset top
+ int h; // image height
+ int len; // data length in entities of PIXVAL size
+ PIXVAL * data; // iamge data
+};
+
+
+static int anz_images = 0;
+
+static int disp_width = 640;
+static int disp_height = 480;
+
+/**
+ * Tile size in pixels
+ *
+ * @author Hj. Malthaner
+ */
+static int tile_size = 32;
+
+static struct imd *images = NULL;
+static struct imd *images1 = NULL;
+static struct imd *images2 = NULL;
+
+
+static PIXVAL *textur = NULL;
+
+
+static struct clip_dimension clip_rect;
+
+
+// dirty tile management strcutures
+
+#define DIRTY_TILE_SIZE 32
+#define DIRTY_TILE_SHIFT 5
+
+static char *tile_dirty = NULL;
+static char *tile_dirty_old = NULL;
+
+static int tiles_per_line = 0;
+static int tile_lines = 0;
+
+
+// colormap management structures
+
+static unsigned char day_pal[256*3];
+static unsigned char night_pal[256*3];
+static unsigned char base_pal[256*3];
+
+static int light_level = 0;
+static int color_level = 1;
+
+
+// ------------ procedure like defines --------------------
+
+
+#define mark_tile_dirty(x,y) tile_dirty[(x) + (y)*tiles_per_line] = 1
+#define is_tile_dirty(x,y) ((tile_dirty[(x) + (y)*tiles_per_line]) || (tile_dirty_old[(x) + (y)*tiles_per_line]) )
+
+// ----------------- procedures ---------------------------
+
+
+/**
+ * Markiert ein Tile as schmutzig, ohne Clipping
+ * @author Hj. Malthaner
+ */
+static void mark_rect_dirty_nc(int x1, int y1, int x2, int y2)
+{
+ int x, y;
+
+ // floor to tile size
+
+ x1 >>= DIRTY_TILE_SHIFT;
+ y1 >>= DIRTY_TILE_SHIFT;
+ x2 >>= DIRTY_TILE_SHIFT;
+ y2 >>= DIRTY_TILE_SHIFT;
+
+ for(y=y1; y<=y2; y++) {
+ for(x=x1; x<=x2; x++) {
+ mark_tile_dirty(x, y);
+ }
+ }
+}
+
+/**
+ * Markiert ein Tile as schmutzig, mit Clipping
+ * @author Hj. Malthaner
+ */
+void mark_rect_dirty_wc(int x1, int y1, int x2, int y2)
+{
+ if(x1 < 0) x1 = 0;
+ if(x1 >= disp_width) x1 = disp_width-1;
+
+ if(y1 < 0) y1 = 0;
+ if(y1 >= disp_height) y1 = disp_height-1;
+
+ if(x2 < 0) x2 = 0;
+ if(x2 >= disp_width) x2 = disp_width-1;
+
+ if(y2 < 0) y2 = 0;
+ if(y2 >= disp_height) y2 = disp_height-1;
+
+ mark_rect_dirty_nc(x1, y1, x2, y2);
+}
+
+/**
+ * Clipped einen Wert in Intervall
+ * @author Hj. Malthaner
+ */
+static int clip(int w, int u, int o)
+{
+ return w < u ? u : w > o ? o : w;
+}
+
+
+/**
+ * Laedt den Font
+ * @author Hj. Malthaner
+ */
+static void init_font()
+{
+ FILE *f = NULL;
+
+ // suche in ./draw.fnt
+
+ if(f==NULL ) {
+ f=fopen("./draw.fnt","rb");
+ }
+
+ if(f==NULL) {
+ printf("Cannot open draw.fnt!\n");
+ exit(1);
+ } else {
+ int i;
+ for(i=0;i<2048;i++) dr_fonttab[i]=getc(f);
+ fclose(f);
+ }
+}
+
+/**
+ * Laedt die Palette
+ * @author Hj. Malthaner
+ */
+static int
+load_palette(const char *fname, unsigned char *palette)
+{
+ FILE *file = fopen(fname,"rb");
+
+ if(file) {
+
+ int x;
+ int anzahl=256;
+ int r,g,b;
+
+ fscanf(file, "%d", &anzahl);
+
+ for(x=0; x<anzahl; x++) {
+ fscanf(file,"%d %d %d", &r, &g, &b);
+
+ palette[x*3+0] = r;
+ palette[x*3+1] = g;
+ palette[x*3+2] = b;
+ }
+ dr_setRGB8multi(0, anzahl, palette);
+
+ fclose(file);
+
+ return TRUE;
+ } else {
+ fprintf(stderr, "Error: can't open file '%s' for reading\n", fname);
+ return FALSE;
+ }
+}
+
+
+/**
+ * Dims (darkens) a color.
+ * @return darkended color.
+ * @author Hj. Malthaner
+ */
+static inline int darken(const int dunkel, const PIXVAL farbe)
+{
+ int r,g,b;
+
+ r = (farbe & 0x7C00) - (dunkel << 10);
+
+ if(r < 0) {
+ r = 0;
+ }
+
+ g = (farbe & 0x03E0) - (dunkel << 5);
+
+ if(g < 0) {
+ g = 0;
+ }
+
+ b = (farbe & 0x001F) - dunkel;
+
+ if(b < 0) {
+ b = 0;
+ }
+
+ return (r & 0x7C00) + (g & 0x03E0) + b;
+}
+
+
+int display_get_width()
+{
+ return disp_width;
+}
+
+int display_get_height()
+{
+ return disp_height;
+}
+
+
+/**
+ * returns the currently used tile size in pixels
+ *
+ * @author Hj. Malthaner
+ */
+int display_get_tile_size()
+{
+ return tile_size;
+}
+
+
+/**
+ * selects a tile size
+ *
+ * @param n 0 means 64x64 tiles, 1 are 32x32 tiles
+ * @author Hj. Malthaner
+ */
+void display_select_tile_size(int n)
+{
+ switch(n) {
+ case 0:
+ tile_size = 64;
+ images = images1;
+ break;
+
+ case 1:
+ tile_size = 32;
+ images = images2;
+ break;
+
+ default:
+ tile_size = 64;
+ images = images1;
+ }
+
+ printf("Switching to tile size %d\n", tile_size);
+}
+
+
+/**
+ * Holt Helligkeitseinstellungen
+ * @author Hj. Malthaner
+ */
+int display_get_light()
+{
+ return light_level;
+}
+
+
+/**
+ * Setzt Helligkeitseinstellungen
+ * @author Hj. Malthaner
+ */
+void display_set_light(int new_light_level)
+{
+ unsigned char palette[256*3];
+ const double ll = 1.0 - light_level/20.0;
+ int i;
+
+ light_level = new_light_level;
+
+ for(i=0; i<256; i++) {
+ const int n = i*3;
+ palette[n+0] = clip(pow(base_pal[n+0]/255.0, ll)*255.0, 0, 255);
+ palette[n+1] = clip(pow(base_pal[n+1]/255.0, ll)*255.0, 0, 255);
+ palette[n+2] = clip(pow(base_pal[n+2]/255.0, ll)*255.0, 0, 255);
+ }
+
+ dr_setRGB8multi(0, 256, palette);
+}
+
+/**
+ * Holt Farbeinstellungen
+ * @author Hj. Malthaner
+ */
+int display_get_color()
+{
+ return color_level;
+}
+
+
+/**
+ * Setzt Farbeinstellungen
+ * @author Hj. Malthaner
+ */
+void display_set_color(int new_color_level)
+{
+ color_level = new_color_level;
+
+ if(color_level < 0) {
+ color_level = 0;
+ }
+
+ if(color_level > 3) {
+ color_level = 3;
+ }
+
+ switch(color_level) {
+ case 0:
+ load_palette("./simud70.pal", day_pal);
+ load_palette("./simun70.pal", night_pal);
+ break;
+ case 1:
+ load_palette("./simud80.pal", day_pal);
+ load_palette("./simun80.pal", night_pal);
+ break;
+ case 2:
+ load_palette("./simud90.pal", day_pal);
+ load_palette("./simun90.pal", night_pal);
+ break;
+ case 3:
+ load_palette("./simud100.pal", day_pal);
+ load_palette("./simun100.pal", night_pal);
+ break;
+ }
+
+ memcpy(base_pal, day_pal, 256*3);
+ display_set_light(display_get_light());
+}
+
+
+static int night_shift = -1;
+
+static void calc_base_pal_from_night_shift(const int night)
+{
+ const int day = 4 - night;
+ int i;
+
+ for(i=0; i<256; i++) {
+ base_pal[i*3+0] = (day_pal[i*3+0] * day + night_pal[i*3+0] * night) >> 2;
+ base_pal[i*3+1] = (day_pal[i*3+1] * day + night_pal[i*3+1] * night) >> 2;
+ base_pal[i*3+2] = (day_pal[i*3+2] * day + night_pal[i*3+2] * night) >> 2;
+ }
+}
+
+
+void display_day_night_shift(int night)
+{
+ if(night != night_shift) {
+ night_shift = night;
+
+ calc_base_pal_from_night_shift(night);
+
+ display_set_light(light_level);
+ mark_rect_dirty_nc(0,0, disp_width-1, disp_height-1);
+ }
+}
+
+
+
+/**
+ * Setzt Farbeintrag
+ * @author Hj. Malthaner
+ */
+void display_set_player_colors(const unsigned char *day, const unsigned char *night)
+{
+ memcpy(day_pal, day, 12);
+ memcpy(night_pal, night, 12);
+
+ calc_base_pal_from_night_shift(night_shift);
+
+ display_set_light(light_level);
+ mark_rect_dirty_nc(0,0, disp_width-1, disp_height-1);
+}
+
+
+/**
+ * Liest 32Bit wert Plattfromunabhängig
+ * @author Hj. Malthaner
+ */
+static int fread_int(FILE *f)
+{
+ int i = 0;
+
+ i += fgetc(f);
+ i += fgetc(f) << 8;
+ i += fgetc(f) << 16;
+ i += fgetc(f) << 24;
+
+ return i;
+}
+
+
+/**
+ * Laedt daten.pak
+ * @author Hj. Malthaner
+ */
+static struct imd * init_images(const char *filename)
+{
+ FILE *f = fopen(filename, "rb");
+ struct imd * images = NULL;
+
+ if( f ) {
+ int i;
+
+ anz_images = fread_int(f);
+ images = malloc(sizeof(struct imd)*anz_images);
+
+
+ for(i=0; i<anz_images; i++) {
+ images[i].y = fread_int(f);
+ images[i].h = fread_int(f);
+ images[i].len = fread_int(f);
+
+// printf("len = %d\n", images[i].len);
+
+ if(images[i].h > 0) {
+ images[i].data = malloc(images[i].len*sizeof(PIXVAL));
+ fread(images[i].data, images[i].len*sizeof(PIXVAL), 1, f);
+
+ } else {
+ images[i].data = NULL;
+ }
+ }
+
+ fclose(f);
+ } else {
+ printf("Kann '%s' nicht lesen.\n", filename);
+ exit(1);
+ }
+
+ return images;
+}
+
+/**
+ * Holt Maus X-Position
+ * @author Hj. Malthaner
+ */
+int gib_maus_x()
+{
+ return mx;
+}
+
+/**
+ * Holt Maus y-Position
+ * @author Hj. Malthaner
+ */
+int gib_maus_y()
+{
+ return my;
+}
+
+
+/**
+ * this sets width < 0 if the range is out of bounds
+ * so check the value after calling and before displaying
+ * @author Hj. Malthaner
+ */
+static int clip_wh(int *x, int *width, const int min_width, const int max_width)
+{
+ // doesnt check "wider than image" case
+
+ if(*x < min_width) {
+ const int xoff = min_width - (*x);
+
+ *width += *x;
+ *x = min_width;
+ return xoff;
+ } else if(*x + *width >= max_width) {
+ *width = max_width - *x;
+ }
+ return 0;
+}
+
+
+/**
+ * Ermittelt Clipping Rechteck
+ * @author Hj. Malthaner
+ */
+struct clip_dimension display_gib_clip_wh(void)
+{
+ return clip_rect;
+}
+
+
+/**
+ * Setzt Clipping Rechteck
+ * @author Hj. Malthaner
+ */
+void display_setze_clip_wh(int x, int y, int w, int h)
+{
+ clip_wh(&x, &w, 0, disp_width);
+ clip_wh(&y, &h, 0, disp_height);
+
+ clip_rect.x = x;
+ clip_rect.y = y;
+ clip_rect.w = w;
+ clip_rect.h = h;
+
+ clip_rect.xx = x+w-1;
+ clip_rect.yy = y+h-1;
+}
+
+// ----------------- basic painting procedures ----------------
+
+
+/**
+ * Kopiert Pixel von src nach dest
+ * @author Hj. Malthaner
+ */
+static void pixcopy(PIXVAL *dest,
+ const PIXVAL *src,
+ int len)
+{
+ memcpy(dest, src, len*sizeof(PIXVAL));
+}
+
+
+/**
+ * Zeichnet Bild mit Clipping
+ * @author Hj. Malthaner
+ */
+static void
+display_img_wc(const int n, const int xp, const int yp, const int dirty)
+{
+ if(n >= 0 && n < anz_images) {
+
+ int h = images[n].h;
+ int y = yp + images[n].y;
+
+ int yoff = clip_wh(&y, &h, 0, clip_rect.yy);
+
+ if(h > 0) {
+ const int width = disp_width;
+ const PIXVAL *sp = images[n].data;
+ PIXVAL *tp = textur + y*width;
+
+ if(dirty) {
+ mark_rect_dirty_wc(xp, y, xp+tile_size-1, y+h+1);
+ }
+
+ // oben clippen
+
+ while(yoff) {
+ yoff --;
+ do {
+ if(*(++sp)) {
+ sp += *sp + 1;
+ }
+ } while(*sp);
+ sp ++;
+ }
+
+ do { // zeilen dekodieren
+ int xpos = xp;
+
+ // bild darstellen
+
+ int runlen = *sp++;
+
+ do {
+ // wir starten mit einem clear run
+
+ xpos += runlen;
+
+ // jetzt kommen farbige pixel
+ runlen = *sp++;
+
+ if(runlen) {
+
+ if(xpos >= 0 && runlen+xpos < width) {
+// pixcopy(tp+xpos, sp, runlen);
+ memcpy(tp+xpos, sp, runlen*sizeof(PIXVAL));
+ } else if(xpos < 0) {
+ if(runlen+xpos > 0) {
+// pixcopy(tp, sp-xpos, runlen+xpos);
+ memcpy(tp, sp-xpos, (runlen+xpos)*sizeof(PIXVAL));
+ }
+ } else if(width > xpos) {
+// pixcopy(tp+xpos, sp, width-xpos);
+ memcpy(tp+xpos, sp, (width-xpos)*sizeof(PIXVAL));
+ }
+ sp += runlen;
+ xpos += runlen;
+ runlen = *sp ++;
+ }
+ } while(runlen);
+
+ tp += width;
+
+ } while(--h > 0);
+ }
+ }
+}
+
+/**
+ * Zeichnet Bild ohne Clipping
+ * @author Hj. Malthaner
+ */
+static void
+display_img_nc(const int n, const int xp, const int yp, const int dirty)
+{
+ if(n >= 0 && n < anz_images) {
+
+ int h = images[n].h;
+
+ if(h > 0) {
+ const PIXVAL *sp = images[n].data;
+ PIXVAL *tp = textur + (yp + images[n].y)*disp_width + xp;
+
+ if(dirty) {
+ mark_rect_dirty_nc(xp, yp+images[n].y, xp+tile_size-1, yp+images[n].y+h-1);
+ }
+
+ do { // zeilen dekodieren
+
+ // bild darstellen
+
+ int runlen = *sp++;
+
+ do {
+
+ // wir starten mit einem clear run
+ tp += runlen;
+
+ // jetzt kommen farbige pixel
+ runlen = *sp++;
+
+ if(runlen) {
+// pixcopy(tp, sp, runlen);
+ memcpy(tp, sp, runlen*sizeof(PIXVAL));
+ sp += runlen;
+ tp += runlen;
+ runlen = *sp++;
+ }
+ } while(runlen);
+
+ tp += (disp_width-tile_size);
+
+ } while(--h);
+ }
+ }
+}
+
+/**
+ * Zeichnet Bild
+ * @author Hj. Malthaner
+ */
+void
+display_img(const int n, const int xp, const int yp, const int dirty)
+{
+ if(xp>=0 && yp>=0 && xp < disp_width-tile_size-1 && yp < disp_height-tile_size-1) {
+ display_img_nc(n, xp, yp, dirty);
+ } else {
+ if(xp>-tile_size && yp>-tile_size && xp < disp_width && yp < disp_height) {
+ display_img_wc(n, xp, yp, dirty);
+ }
+ }
+}
+
+
+
+/**
+ * Copies and shades colors
+ * @param shade the amount to darken the color
+ * @author Hj. Malthaner
+ */
+static void colorpixcpy(PIXVAL *dest, const PIXVAL *src,
+ const PIXVAL * const end,
+ const PIXVAL shade)
+{
+ while(src < end) {
+ *dest++ = darken(shade, *src++);
+ }
+}
+
+
+/**
+ * Zeichnet Bild, ersetzt Spielerfarben
+ * @author Hj. Malthaner
+ */
+static void
+display_color_img_aux(const int n, const int xp, const int yp, const int color, const int dirty)
+{
+ if(n >= 0 && n < anz_images) {
+
+ int h = images[n].h;
+ int y = yp + images[n].y;
+
+ int yoff = clip_wh(&y, &h, 0, clip_rect.yy);
+
+ if(h > 0) {
+ const int width = disp_width;
+ const PIXVAL *sp = images[n].data;
+ PIXVAL *tp = textur + y*width;
+
+// printf("textur = %p tp = %p\n", textur, tp);
+
+ if(dirty) {
+ mark_rect_dirty_wc(xp, y+yoff, xp+tile_size-1, y+yoff+h-1);
+ }
+
+ // oben clippen
+
+ while(yoff) {
+ yoff --;
+ do {
+ if(*(++sp)) {
+ sp += *sp + 1;
+ }
+ } while(*sp);
+ sp ++;
+ }
+
+ do { // zeilen dekodieren
+ int xpos = xp;
+
+ // bild darstellen
+
+ do {
+ // wir starten mit einem clear run
+
+ xpos += *sp ++;
+
+ // jetzt kommen farbige pixel
+
+ if(*sp) {
+ const int runlen = *sp++;
+
+ if(xpos >= 0 && runlen+xpos < width) {
+ colorpixcpy(tp+xpos, sp, sp+runlen, color);
+ } else if(xpos < 0) {
+ if(runlen+xpos > 0) {
+ colorpixcpy(tp, sp-xpos, sp+runlen, color);
+ }
+ } else if(width > xpos) {
+ colorpixcpy(tp+xpos, sp, sp+width-xpos, color);
+ }
+
+ sp += runlen;
+ xpos += runlen;
+ }
+ } while(*sp);
+
+ tp += width;
+ sp ++;
+
+ } while(--h);
+ }
+ }
+}
+
+
+/**
+ * Zeichnet Bild, ersetzt Farben
+ * @author Hj. Malthaner
+ */
+void
+display_color_img(const int n, const int xp, const int yp, const int color, const int dirty)
+{
+ // since the colors for player 0 are already right,
+ // only use the expensive replacement routine for colored images
+ // of other players
+
+ // printf("color=%d\n", color);
+
+ if(color) {
+ display_color_img_aux(n, xp, yp, color, dirty);
+ } else {
+ display_img_wc(n, xp, yp, dirty);
+ }
+}
+
+/**
+ * Zeichnet ein Pixel
+ * @author Hj. Malthaner
+ */
+void
+display_pixel(int x, int y, const int color)
+{
+ if(x >= clip_rect.x && x<=clip_rect.xx &&
+ y >= clip_rect.y && y<clip_rect.yy) {
+
+ PIXVAL * const p = textur + x + y*disp_width;
+ *p = color;
+
+ mark_tile_dirty(x >> DIRTY_TILE_SHIFT, y >> DIRTY_TILE_SHIFT);
+ }
+}
+
+
+/**
+ * Zeichnet einen Text, lowlevel Funktion
+ * @author Hj. Malthaner
+ */
+static void
+dr_textur_text(PIXVAL *textur,int x,int y,const char *txt,
+ const int chars, const int fgpen, const int dirty)
+{
+ int p;
+
+ y+=4; /* Korektu amiga <-> pc */
+
+ if(y < 0 || y+8 >= disp_height)
+ return; /* out of clip */
+
+
+ if(dirty) {
+ mark_rect_dirty_nc(x, y, x+chars*8-1, y+8-1);
+ }
+
+
+ for(p=0; p<chars; p++) { /* Zeichen fuer Zeichen ausgeben */
+ int base=((unsigned char *)txt)[p] << 3; /* 8 Byte je Zeichen */
+ const int end = base+8;
+ int screen_pos = x + (p << 3) + y*disp_width;
+
+
+ do {
+ const int c=dr_fonttab[base++]; /* Eine Zeile des Zeichens */
+ int b;
+
+ for(b=0; b<8; b++) {
+ if(c & (128 >> b)) {
+ textur[screen_pos+b] = fgpen;
+ }
+ }
+ screen_pos += disp_width;
+ }while(base < end);
+ }
+}
+
+
+/**
+ * Zeichnet Text, highlevel Funktion
+ * @author Hj. Malthaner
+ */
+void
+display_text(int x, int y, const char *txt, const int color, int dirty)
+{
+ const int chars = strlen(txt);
+ const int text_width = chars*8;
+
+ if(y >= 8 && y < disp_height-12) {
+
+ if(x >= 0 && x+text_width < disp_width) {
+ dr_textur_text(textur, x, y, txt, chars, color, dirty);
+ } else {
+ if(x < 0 && x+text_width > 8) {
+ const int left_chars = (-x+7)/8;
+
+ dr_textur_text(textur, (x & 7), y, txt+left_chars, chars-left_chars, color, dirty);
+ } else if(x > 0 && x < disp_width-7) {
+ const int rest_chars = (disp_width-x-1) / 8;
+
+ dr_textur_text(textur, x, y, txt, rest_chars, color, dirty);
+ }
+ }
+ }
+}
+
+/**
+ * Zeichnet gefuelltes Rechteck, ohne clipping
+ * @author Hj. Malthaner
+ */
+void display_fb_internal(int xp, int yp, int w, int h,
+ const int color, const int dirty,
+ int cL, int cR, int cT, int cB)
+{
+ clip_wh(&xp, &w, cL, cR);
+ clip_wh(&yp, &h, cT, cB);
+
+ if(w > 0 && h > 0) {
+ PIXVAL *p = textur + xp + yp*disp_width;
+
+ if(dirty) {
+ mark_rect_dirty_nc(xp, yp, xp+w-1, yp+h-1);
+ }
+
+ do {
+ memset(p, color, w*sizeof(PIXVAL));
+ p += disp_width;
+ } while(--h);
+ }
+}
+
+void
+display_fillbox_wh(int xp, int yp, int w, int h,
+ const int color, const int dirty)
+{
+ display_fb_internal(xp,yp,w,h,color,dirty,
+ 0,disp_width-1,0,disp_height-1);
+}
+void
+display_fillbox_wh_clip(int xp, int yp, int w, int h,
+ const int color, const int dirty)
+{
+ display_fb_internal(xp,yp,w,h,color,dirty,
+ clip_rect.x, clip_rect.xx, clip_rect.y, clip_rect.yy);
+}
+
+/**
+ * Zeichnet vertikale Linie
+ * @author Hj. Malthaner
+ */
+void
+display_vl_internal(const int xp, int yp, int h, const int color, int dirty,
+ int cL, int cR, int cT, int cB)
+{
+ clip_wh(&yp, &h, cT, cB);
+
+ if(xp >= cL && xp <= cR && h > 0) {
+ PIXVAL *p = textur + xp + yp*disp_width;
+
+ if (dirty) {
+ mark_rect_dirty_nc(xp, yp, xp, yp+h-1);
+ }
+
+ do {
+ *p = color;
+ p += disp_width;
+ } while(--h);
+ }
+}
+
+void
+display_vline_wh(int xp, int yp, int h, const int color, const int dirty)
+{
+ display_vl_internal(xp,yp,h,color,dirty,
+ 0,disp_width-1,0,disp_height-1);
+}
+
+void
+display_vline_wh_clip(int xp, int yp, int h, const int color, const int dirty)
+{
+ display_vl_internal(xp,yp,h,color,dirty,
+ clip_rect.x, clip_rect.xx, clip_rect.y, clip_rect.yy);
+}
+
+/**
+ * Zeichnet rohe Pixeldaten
+ * @author Hj. Malthaner
+ */
+void
+display_array_wh(int xp, int yp, int w, int h, const unsigned char *arr)
+{
+ const int arr_w = w;
+
+ clip_wh(&xp, &w, 0, disp_width);
+ clip_wh(&yp, &h, 0, disp_height);
+
+ if(w > 0 && h > 0) {
+ PIXVAL *p = textur + xp + yp*disp_width;
+
+ mark_rect_dirty_nc(xp, yp, xp+w-1, yp+h-1);
+
+ if(xp == 0) {
+ arr += arr_w - w;
+ }
+
+ do {
+ // FIXME!!!
+ memcpy(p, arr, w);
+ p += disp_width;
+ arr += arr_w;
+ } while(--h);
+ }
+}
+
+
+// --------------- compound painting procedures ---------------
+
+
+/**
+ * Zeichnet schattiertes Rechteck
+ * @author Hj. Malthaner
+ */
+void
+display_ddd_box(int x1, int y1, int w, int h, int tl_color, int rd_color)
+{
+ display_fillbox_wh(x1, y1, w, 1, tl_color, TRUE);
+ display_fillbox_wh(x1, y1+h-1, w, 1, rd_color, TRUE);
+
+ h-=2;
+
+ display_vline_wh(x1, y1+1, h, tl_color, TRUE);
+ display_vline_wh(x1+w-1, y1+1, h, rd_color, TRUE);
+}
+
+/**
+ * Zeichnet schattierten Text
+ * @author Hj. Malthaner
+ */
+void
+display_ddd_text(int xpos, int ypos, int hgt,
+ int ddd_farbe, int text_farbe,
+ const char *text, int dirty)
+{
+ const int len = strlen(text)*4;
+
+ display_fillbox_wh(xpos-2-len,
+ ypos-hgt-6,
+ 4 + len*2, 1,
+ ddd_farbe+1,
+ dirty);
+ display_fillbox_wh(xpos-2-len,
+ ypos-hgt-5,
+ 4 + len*2, 8,
+ ddd_farbe,
+ dirty);
+ display_fillbox_wh(xpos-2-len,
+ ypos-hgt+3,
+ 4 + len*2, 1,
+ ddd_farbe-1,
+ dirty);
+
+ display_text(xpos - len,
+ ypos-hgt-9,
+ text,
+ text_farbe,
+ dirty);
+}
+
+
+/**
+ * Zaehlt Vorkommen eines Buchstabens in einem String
+ * @author Hj. Malthaner
+ */
+int
+count_char(const char *str, const char c)
+{
+ int count = 0;
+
+ while(*str) {
+ count += (*str++ == c);
+ }
+ return count;
+}
+
+/**
+ * Zeichnet einen mehrzeiligen Text
+ * @author Hj. Malthaner
+ */
+void
+display_multiline_text(int x, int y, const char *inbuf, int color)
+{
+ char tmp[4096];
+ char *buf = tmp;
+ char *next;
+ int y_off = 0;
+
+ // be sure not to copy more than buffer size
+ strncpy(buf, inbuf, 4095);
+
+ // always close with a 0 byte
+ buf[4095] = 0;
+
+ while( (*buf != 0) && (next = strchr(buf,'\n')) ) {
+ *next = 0;
+ display_text(x,y+y_off, buf, color, TRUE);
+ buf = next+1;
+ y_off += LINESPACE;
+ }
+}
+
+
+/**
+ * Loescht den Bildschirm
+ * @author Hj. Malthaner
+ */
+void
+display_clear()
+{
+ memset(textur+32*disp_width, 32, disp_width*(disp_height-17-32));
+
+ mark_rect_dirty_nc(0, 0, disp_width-1, disp_height-1);
+}
+
+
+#if 0
+void display_flush_buffer()
+{
+ int x, y;
+ char * tmp;
+
+#ifdef USE_SOFTPOINTER
+ display_img(softpointer, mx, my, TRUE);
+ old_my = my;
+#endif
+
+#ifdef DEBUG
+ // just for debugging
+ int tile_count = 0;
+#endif
+
+ for(y=0; y<tile_lines; y++) {
+#ifdef DEBUG
+
+ for(x=0; x<tiles_per_line; x++) {
+ if(is_tile_dirty(x, y)) {
+ display_fillbox_wh(x << DIRTY_TILE_SHIFT,
+ y << DIRTY_TILE_SHIFT,
+ DIRTY_TILE_SIZE/4,
+ DIRTY_TILE_SIZE/4,
+ 3,
+ FALSE);
+
+
+
+ dr_textur(x << DIRTY_TILE_SHIFT,
+ y << DIRTY_TILE_SHIFT,
+ DIRTY_TILE_SIZE,
+ DIRTY_TILE_SIZE);
+
+ tile_count ++;
+ } else {
+ display_fillbox_wh(x << DIRTY_TILE_SHIFT,
+ y << DIRTY_TILE_SHIFT,
+ DIRTY_TILE_SIZE/4,
+ DIRTY_TILE_SIZE/4,
+ 0,
+ FALSE);
+
+
+
+ dr_textur(x << DIRTY_TILE_SHIFT,
+ y << DIRTY_TILE_SHIFT,
+ DIRTY_TILE_SIZE,
+ DIRTY_TILE_SIZE);
+
+ }
+ }
+#else
+ x = 0;
+
+ do {
+ if(is_tile_dirty(x, y)) {
+ const int xl = x;
+ do {
+ x++;
+ } while(x < tiles_per_line && is_tile_dirty(x, y));
+
+ dr_textur(xl << DIRTY_TILE_SHIFT,
+ y << DIRTY_TILE_SHIFT,
+ (x-xl)<<DIRTY_TILE_SHIFT,
+ DIRTY_TILE_SIZE);
+
+ }
+ x++;
+ } while(x < tiles_per_line);
+#endif
+ }
+
+#ifdef DEBUG
+// printf("%d von %d tiles wurden gezeichnet\n", tile_count, tile_lines*tiles_per_line);
+#endif
+
+ dr_flush();
+
+ // swap tile buffers
+ tmp = tile_dirty_old;
+ tile_dirty_old = tile_dirty;
+
+ tile_dirty = tmp;
+ memset(tile_dirty, 0, tile_lines*tiles_per_line);
+}
+#endif /* 0 */
+
+void display_flush_buffer()
+{
+ dr_textur(0, 0, disp_width, disp_height);
+}
+
+/**
+ * Bewegt Mauszeiger
+ * @author Hj. Malthaner
+ */
+void display_move_pointer(int dx, int dy)
+{
+ move_pointer(dx, dy);
+}
+
+
+/**
+ * Schaltet Mauszeiger sichtbar/unsichtbar
+ * @author Hj. Malthaner
+ */
+void display_show_pointer(int yesno)
+{
+#ifdef USE_SOFTPOINTER
+ if(yesno) {
+ softpointer = 261;
+ } else {
+ softpointer = 52;
+ }
+#else
+ show_pointer(yesno);
+#endif
+}
+
+/**
+ * unbenutzt ?
+ * @author Hj. Malthaner
+ */
+void
+my_save_exit()
+{
+ dr_os_close();
+}
+
+
+/**
+ * Inits. Grafikmodul
+ * @author Hj. Malthaner
+ */
+int
+simgraph_init(int width, int height)
+{
+ int parameter[2];
+ int ok;
+
+ dr_os_init(0, parameter);
+
+ ok = dr_os_open(width, height);
+
+ if(ok) {
+
+ disp_width = dr_get_width();
+ disp_height = dr_get_height();
+
+ textur = dr_textur_init();
+
+ // not needed for iso-band
+ // init_font(".drawrc");
+
+
+// display_set_color(1);
+
+
+ images1 = init_images("daten.pak");
+
+ images2 = init_images("daten2.pak");
+
+ display_select_tile_size(0);
+
+ printf("Init. done.\n");
+
+// dr_use_color(rp, SCHWARZ);
+// dr_fillbox_wh(rp, 0, 0, disp_width, WIN_disp_height);
+
+ } else {
+ puts("Error : can't open window!");
+ exit(-1);
+ }
+
+
+ // allocate dirty tile flags
+ tiles_per_line = (disp_width + DIRTY_TILE_SIZE - 1) / DIRTY_TILE_SIZE;
+ tile_lines = (disp_height + DIRTY_TILE_SIZE - 1) / DIRTY_TILE_SIZE;
+
+ tile_dirty = malloc( tile_lines*tiles_per_line );
+ tile_dirty_old = malloc( tile_lines*tiles_per_line );
+
+ memset(tile_dirty, 1, tile_lines*tiles_per_line);
+ memset(tile_dirty_old, 1, tile_lines*tiles_per_line);
+
+ display_setze_clip_wh(0, 0, disp_width, disp_height);
+
+ return TRUE;
+}
+
+/**
+ * Prueft ob das Grafikmodul schon init. wurde
+ * @author Hj. Malthaner
+ */
+int is_display_init()
+{
+ return textur != NULL;
+}
+
+/**
+ * Schliest das Grafikmodul
+ * @author Hj. Malthaner
+ */
+int
+simgraph_exit()
+{
+ free(tile_dirty);
+ free(tile_dirty_old);
+
+
+ return dr_os_close();
+}
+
+
+/**
+ * Laedt Einstellungen
+ * @author Hj. Malthaner
+ */
+void display_laden(FILE* file)
+{
+ int i,r,g,b;
+
+ unsigned char day[12];
+ unsigned char night[12];
+
+ fscanf(file, "%d %d %d\n", &light_level, &color_level, &night_shift);
+
+ display_set_light(light_level);
+ display_set_color(color_level);
+
+ for(i=0; i<4; i++) {
+ fscanf(file, "%d %d %d\n", &r, &g, &b);
+ day[i*3+0] = r;
+ day[i*3+1] = g;
+ day[i*3+2] = b;
+
+ fscanf(file, "%d %d %d\n", &r, &g, &b);
+ night[i*3+0] = r;
+ night[i*3+1] = g;
+ night[i*3+2] = b;
+ }
+
+ display_set_player_colors(day, night);
+}
+
+
+/**
+ * Speichert Einstellungen
+ * @author Hj. Malthaner
+ */
+void display_speichern(FILE* file)
+{
+ int i;
+ fprintf(file, "%d %d %d\n", light_level, color_level, night_shift);
+
+ for(i=0; i<4; i++) {
+ fprintf(file, "%d %d %d\n", day_pal[i*3+0], day_pal[i*3+1], day_pal[i*3+2]);
+ fprintf(file, "%d %d %d\n", night_pal[i*3+0], night_pal[i*3+1], night_pal[i*3+2]);
+ }
+}
+
diff --git a/src/iso/simgraph.h b/src/iso/simgraph.h
new file mode 100644
index 00000000..938edb95
--- /dev/null
+++ b/src/iso/simgraph.h
@@ -0,0 +1,159 @@
+/* simgraph.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph graphics engine.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* simgraph.h
+ *
+ * Versuch einer Graphic fuer Simulationsspiele
+ * Hj. Malthaner, Aug. 1997
+ *
+ *
+ * 3D, isometrische Darstellung
+ *
+ */
+
+#ifndef simgraph_h
+#define simgraph_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#define LINESPACE 11
+
+
+struct clip_dimension {
+ int x, xx, w, y, yy, h;
+};
+
+
+// helper macros
+
+// save the current clipping and set a new one
+#define PUSH_CLIP(x,y,w,h) \
+{\
+const struct clip_dimension p_cr = display_gib_clip_wh(); \
+display_setze_clip_wh(x, y, w, h);
+
+// restore a saved clipping rect
+#define POP_CLIP() \
+display_setze_clip_wh(p_cr.x, p_cr.y, p_cr.w, p_cr.h); \
+}
+
+
+// function prototypes
+
+int simgraph_init(int width, int height);
+int is_display_init();
+int simgraph_exit();
+
+int gib_maus_x();
+int gib_maus_y();
+
+void mark_rect_dirty_wc(int x1, int y1, int x2, int y2);
+
+
+/**
+ * returns the currently used tile size in pixels
+ *
+ * @author Hj. Malthaner
+ */
+int display_get_tile_size();
+
+
+/**
+ * selects a tile size
+ *
+ * @param n 0 means 64x64 tiles, 1 are 32x32 tiles
+ * @author Hj. Malthaner
+ */
+void display_select_tile_size(int n);
+
+
+
+int display_get_width();
+int display_get_height();
+
+
+int display_get_light();
+void display_set_light(int new_light_level);
+
+int display_get_color();
+void display_set_color(int new_color_level);
+
+void display_day_night_shift(int night);
+
+//void display_set_rgb(int n, int r, int g, int b);
+void display_set_player_colors(const unsigned char *day, const unsigned char *night);
+
+void display_img(const int n, const int xp, const int yp, const int dirty);
+void display_color_img(const int n, const int xp, const int yp, const int color, const int dirty);
+void display_fillbox_wh(int xp, int yp, int w, int h, int color, int dirty);
+void display_fillbox_wh_clip(int xp, int yp, int w, int h, int color, int d);
+void display_vline_wh(const int xp, int yp, int h, const int color, int dirty);
+void display_vline_wh_clip(const int xp, int yp, int h, const int c, int d);
+void display_clear();
+
+void display_flush_buffer();
+
+void display_move_pointer(int dx, int dy);
+void display_show_pointer(int yesno);
+
+
+void display_pixel(int x, int y, int color);
+
+void display_ddd_text(int xpos, int ypos, int hgt,
+ int ddd_farbe, int text_farbe,
+ const char *text, int dirty);
+
+void display_text(int x, int y, const char *txt, const int color, int dirty);
+void display_array_wh(int xp, int yp, int w, int h, const unsigned char *arr);
+void display_ddd_box(int x1, int y1, int w, int h, int tl_color, int rd_color);
+
+// compound painting routines
+
+int count_char(const char *str, const char c);
+void display_multiline_text(int x, int y, const char *inbuf, int color);
+
+void zeige_banner(void);
+
+void display_setze_clip_wh(int x, int y, int w, int h);
+struct clip_dimension display_gib_clip_wh(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/iso/simsys.h b/src/iso/simsys.h
new file mode 100644
index 00000000..c4b7dd6c
--- /dev/null
+++ b/src/iso/simsys.h
@@ -0,0 +1,197 @@
+/* simsys.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph graphics engine.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef simsys_h
+#define simsys_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+
+/* Variablen zur Messageverarbeitung */
+
+/* Klassen */
+
+#define SIM_NOEVENT 0
+#define SIM_MOUSE_BUTTONS 1
+#define SIM_KEYBOARD 2
+#define SIM_MOUSE_MOVE 3
+#define SIM_IGNORE_EVENT 255
+
+/* Aktionen */ /* added RIGHTUP and MIDUP */
+#define SIM_MOUSE_LEFTUP 1
+#define SIM_MOUSE_RIGHTUP 2
+#define SIM_MOUSE_MIDUP 3
+#define SIM_MOUSE_LEFTBUTTON 4
+#define SIM_MOUSE_RIGHTBUTTON 5
+#define SIM_MOUSE_MIDBUTTON 6
+#define SIM_MOUSE_MOVED 7
+
+
+/**
+ * inits operating system stuff
+ * @author Hj. Malthaner
+ */
+int dr_os_init(int n, int *parameter);
+
+
+/**
+ * opens graphics device/context/window of size w*h
+ * @param w width
+ * @param h height
+ * @author Hj. Malthaner
+ */
+int dr_os_open(int w, int h);
+
+
+/**
+ * closes operating system stuff
+ * @author Hj. Malthaner
+ */
+int dr_os_close();
+
+
+/**
+ * retrieve display width
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_get_width();
+
+
+/**
+ * retrieve display height
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_get_height();
+
+
+/**
+ * creates a (maybe virtual) array of graphics data
+ * @author Hj. Malthaner
+ */
+unsigned short * dr_textur_init();
+
+
+/**
+ * displays the array of graphics data
+ * @author Hj. Malthaner
+ */
+void dr_textur(int xp, int yp, int w, int h);
+
+
+/**
+ * use this method to flush graphics pipeline (undrawn stuff) onscreen.
+ * @author Hj. Malthaner
+ */
+void dr_flush();
+
+
+/**
+ * set colormap entries
+ * @author Hj. Malthaner
+ */
+void dr_setRGB8multi(int first, int count, unsigned char * data);
+
+
+/**
+ * display/hide mouse pointer
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void show_pointer(int yesno);
+
+
+/**
+ * move mouse pointer
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void move_pointer(int x, int y);
+
+
+/**
+ * update softpointer position
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void ex_ord_update_mx_my();
+
+
+/**
+ * get events from the system
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void GetEvents();
+
+
+/**
+ * get events from the system without waiting
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void GetEventsNoWait();
+
+
+/**
+ * @returns time since progrma start in milliseconds
+ * @author Hj. Malthaner
+ */
+long long dr_time(void);
+
+
+/**
+ * sleeps some microseconds
+ * @author Hj. Malthaner
+ */
+void dr_sleep(unsigned long usec);
+
+
+/**
+ * loads a sample
+ * @return a handle for that sample or -1 on failure
+ * @author Hj. Malthaner
+ */
+int dr_load_sample(const char *filename);
+
+
+/**
+ * plays a sample
+ * @param key the key for the sample to be played
+ * @author Hj. Malthaner
+ */
+void dr_play_sample(int key, int volume);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/iso/simview.c b/src/iso/simview.c
new file mode 100644
index 00000000..4e972841
--- /dev/null
+++ b/src/iso/simview.c
@@ -0,0 +1,87 @@
+/* simview.c
+ *
+ * Copyright (c) 2001,2002 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include "simview.h"
+#include "world_view.h"
+#include "world_adaptor.h"
+#include "simgraph.h"
+
+
+/**
+ * Draws the full iso-view display
+ * @author Hj. Malthaner
+ */
+void display()
+{
+ const int IMG_SIZE = display_get_tile_size();
+
+ const int const_x_off = display_get_width()/2 + get_x_off();
+ const int dpy_width = display_get_width()/IMG_SIZE + 2;
+ const int dpy_height = (display_get_height()*4)/IMG_SIZE;
+
+
+ const int i_off = get_i_off();
+ const int j_off = get_j_off();
+
+ int x,y;
+
+// puts("displaying");
+
+
+ // Hajo: draw grounds first
+
+ for(y=-5; y<dpy_height+10; y++) {
+ const int ypos = y*IMG_SIZE/4+16 + get_y_off();
+
+ for(x=-dpy_width + (y & 1); x<=dpy_width+2; x+=2) {
+
+ const int i = ((y+x) >> 1) + i_off;
+ const int j = ((y-x) >> 1) + j_off;
+ const int xpos = x*IMG_SIZE/2 + const_x_off;
+
+ display_boden(i, j, xpos, ypos);
+ }
+ }
+
+ // Hajo: then draw the objects
+
+ for(y=-5; y<dpy_height+10; y++) {
+ const int ypos = y*IMG_SIZE/4+16 + get_y_off();
+
+ for(x=-dpy_width + (y & 1); x<=dpy_width+2; x+=2) {
+
+ const int i = ((y+x) >> 1) + i_off;
+ const int j = ((y-x) >> 1) + j_off;
+ const int xpos = x*IMG_SIZE/2 + const_x_off;
+
+
+ display_dinge(i, j, xpos, ypos);
+ }
+ }
+}
+
diff --git a/src/iso/simview.h b/src/iso/simview.h
new file mode 100644
index 00000000..a1cac864
--- /dev/null
+++ b/src/iso/simview.h
@@ -0,0 +1,41 @@
+/* simview.h
+ *
+ * Copyright (c) 2001, 2002 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph graphics engine.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef simview_h
+#define simview_h
+
+
+/**
+ * Draws the full iso-view display
+ * @author Hj. Malthaner
+ */
+void display();
+
+
+#endif
diff --git a/src/iso/walls4.h b/src/iso/walls4.h
new file mode 100644
index 00000000..5f57d09e
--- /dev/null
+++ b/src/iso/walls4.h
@@ -0,0 +1,288 @@
+/* walls4.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+int wall_table[256] =
+{
+0, // 0
+0, // 1
+2, // 2
+0, // 3
+0, // 4
+0, // 5
+0, // 6
+0, // 7
+1, // 8
+0, // 9
+3, // 10
+3, // 11
+0, // 12
+0, // 13
+3, // 14
+3, // 15
+4, // 16
+0, // 17
+6, // 18
+6, // 19
+0, // 20
+0, // 21
+6, // 22
+0, // 23
+5, // 24
+5, // 25
+7, // 26
+0, // 27
+5, // 28
+0, // 29
+7, // 30
+5, // 31
+0, // 32
+0, // 33
+0, // 34
+0, // 35
+0, // 36
+0, // 37
+0, // 38
+0, // 39
+0, // 40
+0, // 41
+0, // 42
+0, // 43
+0, // 44
+0, // 45
+0, // 46
+0, // 47
+0, // 48
+0, // 49
+0, // 50
+0, // 51
+0, // 52
+0, // 53
+0, // 54
+0, // 55
+5, // 56
+5, // 57
+0, // 58
+0, // 59
+5, // 60
+0, // 61
+0, // 62
+0, // 63
+8, // 64
+0, // 65
+10, // 66
+10, // 67
+0, // 68
+0, // 69
+10, // 70
+10, // 71
+9, // 72
+9, // 73
+0, // 74
+10, // 75
+0, // 76
+0, // 77
+0, // 78
+0, // 79
+12, // 80
+0, // 81
+0, // 82
+0, // 83
+0, // 84
+0, // 85
+10, // 86
+0, // 87
+13, // 88
+0, // 89
+0, // 90
+0, // 91
+13, // 92
+0, // 93
+0, // 94
+0, // 95
+0, // 96
+0, // 97
+10, // 98
+0, // 99
+0, // 100
+0, // 101
+10, // 102
+0, // 103
+9, // 104
+0, // 105
+10, // 106
+10, // 107
+0, // 108
+0, // 109
+0, // 110
+10, // 111
+12, // 112
+0, // 113
+0, // 114
+0, // 115
+0, // 116
+0, // 117
+0, // 118
+0, // 119
+0, // 120
+12, // 121
+0, // 122
+0, // 123
+0, // 124
+0, // 125
+0, // 126
+0, // 127
+0, // 128
+0, // 129
+0, // 130
+0, // 131
+0, // 132
+0, // 133
+0, // 134
+0, // 135
+0, // 136
+0, // 137
+0, // 138
+0, // 139
+0, // 140
+0, // 141
+0, // 142
+0, // 143
+0, // 144
+0, // 145
+0, // 146
+6, // 147
+0, // 148
+0, // 149
+0, // 150
+0, // 151
+5, // 152
+5, // 153
+0, // 154
+0, // 155
+5, // 156
+0, // 157
+0, // 158
+0, // 159
+0, // 160
+0, // 161
+0, // 162
+0, // 163
+0, // 164
+0, // 165
+0, // 166
+0, // 167
+0, // 168
+0, // 169
+0, // 170
+0, // 171
+0, // 172
+0, // 173
+0, // 174
+0, // 175
+0, // 176
+0, // 177
+0, // 178
+0, // 179
+0, // 180
+0, // 181
+0, // 182
+0, // 183
+5, // 184
+0, // 185
+7, // 186
+0, // 187
+0, // 188
+0, // 189
+0, // 190
+0, // 191
+0, // 192
+0, // 193
+10, // 194
+10, // 195
+0, // 196
+0, // 197
+0, // 198
+0, // 199
+9, // 200
+0, // 201
+0, // 202
+0, // 203
+0, // 204
+0, // 205
+0, // 206
+0, // 207
+12, // 208
+0, // 209
+10, // 210
+0, // 211
+0, // 212
+0, // 213
+10, // 214
+10, // 215
+0, // 216
+0, // 217
+0, // 218
+0, // 219
+0, // 220
+0, // 221
+0, // 222
+0, // 223
+0, // 224
+0, // 225
+10, // 226
+0, // 227
+0, // 228
+0, // 229
+0, // 230
+0, // 231
+0, // 232
+0, // 233
+0, // 234
+10, // 235
+0, // 236
+0, // 237
+0, // 238
+0, // 239
+0, // 240
+0, // 241
+14, // 242
+0, // 243
+0, // 244
+0, // 245
+10, // 246
+0, // 247
+5, // 248
+0, // 249
+0, // 250
+0, // 251
+0, // 252
+0, // 253
+0, // 254
+0, // 255
+};
diff --git a/src/iso/walls9.h b/src/iso/walls9.h
new file mode 100644
index 00000000..038dadf1
--- /dev/null
+++ b/src/iso/walls9.h
@@ -0,0 +1,288 @@
+/* walls9.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+int wall_table[256] =
+{
+0, // 0
+1, // 1
+2, // 2
+3, // 3
+4, // 4
+5, // 5
+2, // 6
+7, // 7
+8, // 8
+8, // 9
+10, // 10
+11, // 11
+12, // 12
+13, // 13
+10, // 14
+11, // 15
+16, // 16
+17, // 17
+18, // 18
+18, // 19
+16, // 20
+21, // 21
+22, // 22
+22, // 23
+24, // 24
+24, // 25
+26, // 26
+27, // 27
+24, // 28
+24, // 29
+30, // 30
+31, // 31
+32, // 32
+33, // 33
+34, // 34
+35, // 35
+36, // 36
+37, // 37
+38, // 38
+39, // 39
+40, // 40
+41, // 41
+10, // 42
+43, // 43
+44, // 44
+45, // 45
+46, // 46
+47, // 47
+48, // 48
+49, // 49
+50, // 50
+51, // 51
+52, // 52
+53, // 53
+54, // 54
+55, // 55
+24, // 56
+24, // 57
+58, // 58
+27, // 59
+24, // 60
+61, // 61
+62, // 62
+31, // 63
+64, // 64
+65, // 65
+66, // 66
+66, // 67
+68, // 68
+69, // 69
+66, // 70
+66, // 71
+72, // 72
+72, // 73
+74, // 74
+75, // 75
+76, // 76
+77, // 77
+74, // 78
+75, // 79
+80, // 80
+81, // 81
+82, // 82
+82, // 83
+80, // 84
+85, // 85
+86, // 86
+86, // 87
+88, // 88
+88, // 89
+90, // 90
+91, // 91
+88, // 92
+93, // 93
+94, // 94
+95, // 95
+64, // 96
+97, // 97
+66, // 98
+66, // 99
+100, // 100
+101, // 101
+66, // 102
+103, // 103
+104, // 104
+105, // 105
+106, // 106
+107, // 107
+108, // 108
+109, // 109
+106, // 110
+107, // 111
+80, // 112
+113, // 113
+82, // 114
+115, // 115
+116, // 116
+117, // 117
+118, // 118
+119, // 119
+120, // 120
+121, // 121
+122, // 122
+123, // 123
+124, // 124
+125, // 125
+126, // 126
+127, // 127
+128, // 128
+129, // 129
+130, // 130
+131, // 131
+132, // 132
+133, // 133
+134, // 134
+135, // 135
+136, // 136
+137, // 137
+138, // 138
+139, // 139
+140, // 140
+141, // 141
+142, // 142
+143, // 143
+16, // 144
+145, // 145
+18, // 146
+147, // 147
+148, // 148
+149, // 149
+22, // 150
+22, // 151
+24, // 152
+24, // 153
+154, // 154
+155, // 155
+24, // 156
+157, // 157
+158, // 158
+31, // 159
+160, // 160
+161, // 161
+66, // 162
+163, // 163
+164, // 164
+165, // 165
+166, // 166
+167, // 167
+168, // 168
+169, // 169
+170, // 170
+171, // 171
+172, // 172
+173, // 173
+174, // 174
+175, // 175
+176, // 176
+177, // 177
+178, // 178
+179, // 179
+180, // 180
+181, // 181
+182, // 182
+183, // 183
+24, // 184
+185, // 185
+26, // 186
+187, // 187
+24, // 188
+189, // 189
+190, // 190
+31, // 191
+64, // 192
+193, // 193
+66, // 194
+66, // 195
+196, // 196
+197, // 197
+66, // 198
+66, // 199
+72, // 200
+201, // 201
+74, // 202
+203, // 203
+204, // 204
+205, // 205
+206, // 206
+207, // 207
+208, // 208
+209, // 209
+210, // 210
+211, // 211
+208, // 212
+213, // 213
+214, // 214
+214, // 215
+216, // 216
+217, // 217
+218, // 218
+219, // 219
+220, // 220
+221, // 221
+222, // 222
+223, // 223
+224, // 224
+225, // 225
+66, // 226
+227, // 227
+228, // 228
+229, // 229
+230, // 230
+231, // 231
+104, // 232
+233, // 233
+234, // 234
+107, // 235
+236, // 236
+237, // 237
+238, // 238
+239, // 239
+208, // 240
+241, // 241
+210, // 242
+243, // 243
+244, // 244
+245, // 245
+214, // 246
+214, // 247
+248, // 248
+248, // 249
+250, // 250
+251, // 251
+248, // 252
+248, // 253
+254, // 254
+255, // 255
+};
diff --git a/src/iso/world_adaptor.c b/src/iso/world_adaptor.c
new file mode 100644
index 00000000..a483780f
--- /dev/null
+++ b/src/iso/world_adaptor.c
@@ -0,0 +1,226 @@
+/* world_adaptor.cc
+ *
+ * Copyright (c) 2001, 2002 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* world_adaptor.c
+ *
+ * adaption between angband world and simugraph data model
+ * Hj. Maltahner, Jan. 2001
+ */
+
+#include "hackdef.h"
+
+#include "world_adaptor.h"
+#include "world_view.h"
+#include "simview.h"
+#include "simgraph.h"
+
+#undef MIN
+#undef MAX
+
+#include "../angband.h"
+
+
+/*
+ * Highlit location
+ */
+int high_x = -1;
+int high_y = -1;
+
+/**
+ * remember targetted location
+ * (hook is ToME3 only, for now)
+ * @author J. Frieling
+ */
+bool iso_target_hook(char* fmt)
+{
+ high_y = get_next_arg(fmt);
+ high_x = get_next_arg(fmt);
+
+ return FALSE;
+}
+
+/**
+ * Highlite (mark) location x,y
+ * @author Hj. Malthaner
+ */
+void highlite_spot(int x, int y)
+{
+ high_x = x;
+ high_y = y;
+}
+
+
+/**
+ * Grid type, default is grid for items and monsters
+ * 0: No grid
+ * 1: Item/monster grid
+ * 2: Full grid
+ * @author Hj. Malthaner
+ */
+static int grid = 1;
+
+
+/**
+ * Show shadow below items and monsters?
+ * 0 = no
+ * 1 = yes
+ * @author Hj. Malthaner
+ */
+//int shadow = 1;
+
+
+/**
+ * Set a grid type (takes argument modulo 3)
+ * @author Hj. Malthaner
+ */
+void set_grid(int no)
+{
+ grid = no % 3;
+}
+
+
+/**
+ * Show which grid type ?
+ * @author Hj. Malthaner
+ */
+int get_grid()
+{
+ return grid;
+}
+
+
+/**
+ * Turn shadows on/off (0=off, 1=on)
+ * @author Hj. Malthaner
+ */
+/*void set_shadow(int yesno)
+{
+ shadow = yesno;
+}
+*/
+
+
+/**
+ * Determines i-offset of the watch point
+ * @author Hj. Malthaner
+ */
+int get_i_off()
+{
+ const int p_off = display_get_width() >> 7;
+ const int mult = display_get_tile_size() == 32 ? 2 : 1;
+
+#ifdef USE_SMALL_ISO_HACK
+
+ int i_off;
+
+ if(p_ptr) {
+ i_off = p_ptr->px-p_off*mult;
+ }
+
+ return i_off;
+#else
+
+ return 47-p_off;
+
+
+#endif
+}
+
+
+/**
+ * Determines j-offset of the watch point
+ * @author Hj. Malthaner
+ */
+int get_j_off()
+{
+ const int p_off = display_get_width() >> 7;
+ const int mult = display_get_tile_size() == 32 ? 2 : 1;
+
+#ifdef USE_SMALL_ISO_HACK
+
+ int j_off;
+ if(p_ptr) {
+ j_off = p_ptr->py-p_off*mult;
+ }
+
+ return j_off;
+#else
+
+ return 10-p_off;
+
+#endif
+}
+
+
+
+/**
+ * Ermittelt x-Offset gescrollter Karte
+ * @author Hj. Malthaner
+ */
+int get_x_off()
+{
+ return 0;
+}
+
+
+/**
+ * Ermittelt y-Offset gescrollter Karte
+ * @author Hj. Malthaner
+ */
+int get_y_off()
+{
+ return 0;
+}
+
+
+
+int init_adaptor()
+{
+ printf("Preparing display ...\n");
+ simgraph_init(672, 480);
+
+ return TRUE;
+}
+
+
+int refresh_display()
+{
+ display();
+ display_flush_buffer();
+
+ return TRUE;
+}
+
+
+int close_adaptor()
+{
+ simgraph_exit();
+
+ return TRUE;
+}
diff --git a/src/iso/world_adaptor.h b/src/iso/world_adaptor.h
new file mode 100644
index 00000000..d5b5b63e
--- /dev/null
+++ b/src/iso/world_adaptor.h
@@ -0,0 +1,117 @@
+/* world_adaptor.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef hajo_world_adaptor_h
+#define hajo_world_adaptor_h
+
+/* world_adapter.h
+ *
+ * adpater between angband code and simugraph engine
+ * Hj. Malthaner, Jan 2001
+ */
+
+// I need angband's bool
+#include "../h-type.h"
+
+/*
+ * Highlit location - use read only!
+ */
+extern int high_x;
+extern int high_y;
+
+int init_adaptor();
+int close_adaptor();
+
+int refresh_display();
+
+/**
+ * remember targetted location
+ * @author J. Frieling
+ */
+bool iso_target_hook(char *fmt);
+
+/**
+ * Highlite (mark) location x,y
+ * @author Hj. Malthaner
+ */
+void highlite_spot(int x, int y);
+
+/**
+ * Set a grid type (takes argument modulo 3)
+ * @author Hj. Malthaner
+ */
+void set_grid(int no);
+
+
+/**
+ * Show which grid type ?
+ * @author Hj. Malthaner
+ */
+int get_grid();
+
+
+/**
+ * Turn shadows on/off (0=off, 1=on)
+ * @author Hj. Malthaner
+ */
+//void set_shadow(int yesno);
+
+
+
+/**
+ * Ermittelt x-Offset gescrollter Karte
+ * @author Hj. Malthaner
+ */
+int get_x_off();
+
+
+/**
+ * Ermittelt y-Offset gescrollter Karte
+ * @author Hj. Malthaner
+ */
+int get_y_off();
+
+
+/**
+ * Determines i-offset of the watch point
+ * @author Hj. Malthaner
+ */
+int get_i_off();
+
+
+/**
+ * Determines j-offset of the watch point
+ * @author Hj. Malthaner
+ */
+int get_j_off();
+
+
+#endif
+
diff --git a/src/iso/world_view.c b/src/iso/world_view.c
new file mode 100644
index 00000000..0a4b47fe
--- /dev/null
+++ b/src/iso/world_view.c
@@ -0,0 +1,499 @@
+/* world_view.c
+ *
+ * Copyright (c) 2001, 2002 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include "simgraph.h"
+#include "world_view.h"
+#include "world_adaptor.h"
+
+//#include "walls4.h"
+#include "walls9.h"
+
+#include "hackdef.h"
+
+
+#undef MIN
+#undef MAX
+
+
+#include "../angband.h"
+#include "../defines.h"
+
+
+/* from isov-x11.c */
+extern unsigned char **iso_ap;
+extern unsigned char **iso_cp;
+extern unsigned char **iso_atp;
+extern unsigned char **iso_ctp;
+# ifdef USE_EGO_GRAPHICS
+extern unsigned char **iso_aep;
+extern unsigned char **iso_cep;
+# endif /* USE_EGO_GRAPHICS */
+
+
+#ifdef USE_SMALL_ISO_HACK
+
+static bool is_valid_position(int x, int y) {
+ return x >= 0 && y >= 0 && x<cur_wid && y<cur_hgt;
+}
+
+static bool is_valid_to_show(int x, int y) {
+ return is_valid_position(x, y);
+}
+
+static int get_feat_nc(int x, int y)
+{
+ /* this is for Angabdn 2.9.1
+ return cave_feat[y][x];
+ */
+
+ /* Pernangband 4.1.5 */
+ cave_type *c_ptr;
+
+ /* Get the cave */
+ c_ptr = &cave[y][x];
+
+
+ /* Feature code */
+ return c_ptr->feat;
+}
+
+
+static int get_feat_wc(int x, int y)
+{
+ if(is_valid_position(x,y)) {
+ return get_feat_nc(x,y);
+ } else {
+ return FEAT_PERM_SOLID;
+ }
+}
+
+
+static int get_info_nc(int x, int y)
+{
+ /* this is for Angabdn 2.9.1
+ return cave_info[y][x];
+ */
+
+ /* Pernangband 4.1.5 */
+ cave_type *c_ptr;
+
+ /* Get the cave */
+ c_ptr = &cave[y][x];
+
+
+ /* Info code */
+ return c_ptr->info;
+}
+
+
+static int get_info_wc(int x, int y)
+{
+ if(is_valid_position(x,y)) {
+ return get_info_nc(x,y);
+ } else {
+ return 0;
+ }
+}
+
+
+static bool is_door(int feat)
+{
+ return
+ (feat == FEAT_OPEN) ||
+ (feat == FEAT_BROKEN) ||
+ (feat == FEAT_SECRET) ||
+ (feat >= FEAT_DOOR_HEAD && feat <= FEAT_DOOR_TAIL) ||
+ (feat == FEAT_SHOP);
+}
+
+
+static bool is_wall(int feat)
+{
+ return
+ (feat >= FEAT_MAGMA && feat <= FEAT_PERM_SOLID) ||
+ (feat >= FEAT_TREES && feat <= FEAT_SANDWALL_K) ||
+ (feat >= FEAT_QUEST1 && feat <= FEAT_QUEST4);
+}
+
+
+static int is_wall_or_door(int x, int y)
+{
+ const int feat = get_feat_wc(x, y);
+
+ return is_door(feat) || is_wall(feat);
+}
+
+
+static bool is_lit(int x, int y)
+{
+ const int info = get_info_wc(x, y);
+
+ return (info & CAVE_GLOW) && (info & CAVE_VIEW) && (info & CAVE_MARK);
+}
+
+
+static bool is_torch_lit(int x, int y)
+{
+ const int info = get_info_wc(x, y);
+
+ return (info & CAVE_PLIT) && (info & CAVE_VIEW) && (info & CAVE_MARK);
+}
+
+
+static bool is_only_torch_lit(int x, int y)
+{
+ return is_torch_lit(x, y) & !is_lit(x, y);
+}
+
+
+static bool is_blind()
+{
+ // PernAngband variant
+ return p_ptr->blind;
+}
+
+
+/**
+ * Calculates distance from player to point (x,y).
+ * Uses the distance guess from cave.c
+ * @author Hj. Malthaner
+ */
+static int get_distance_to_player(int x, int y)
+{
+ return distance(y, x, p_ptr->py, p_ptr->px);
+}
+
+#else
+
+
+static int iso_access(unsigned char **field, int x, int y)
+{
+ if(x >= 0 && y >= 0 && x<80 && y<24) {
+ return field[y][x] & 0x7f;
+ } else {
+ return 0;
+ }
+}
+
+static int is_wall_or_door(int x, int y)
+{
+ const int atp = iso_access(iso_atp, x, y);
+ const int ctp = iso_access(iso_ctp, x, y);
+
+ return (atp == 0x01 && (ctp >= 0x70 && ctp <= 0x75)) ||
+ (atp == 0x00 && (ctp =='+' || ctp == '\'')) ;
+}
+
+#endif
+
+
+/**
+ * Draw ground of location i,j onto screen position xpos,ypos
+ * @author Hj. Malthaner
+ */
+void display_boden(int x, int y, int xpos, int ypos)
+{
+ // unused in Iso-Angband
+}
+
+
+static int check_wall(int x, int y)
+{
+
+ int o = 0;
+
+#if 0
+ o |= (is_wall_or_door(x-1, y-1)) << 0;
+ o |= (is_wall_or_door(x , y-1)) << 1;
+ o |= (is_wall_or_door(x+1, y-1)) << 2;
+ o |= (is_wall_or_door(x-1, y )) << 3;
+ o |= (is_wall_or_door(x+1, y )) << 4;
+ o |= (is_wall_or_door(x-1, y+1)) << 5;
+ o |= (is_wall_or_door(x , y+1)) << 6;
+ o |= (is_wall_or_door(x+1, y+1)) << 7;
+
+
+ return wall_table[o];
+#endif
+ // jue: new algorithm as suggested by James Andrewartha
+ //
+ o |= (is_wall_or_door(x-1, y-1)
+ && is_wall_or_door(x, y-1)
+ && is_wall_or_door(x-1,y)) << 0;
+
+ o |= (is_wall_or_door(x , y-1)) << 1;
+
+ o |= (is_wall_or_door(x+1, y-1)
+ && is_wall_or_door(x, y-1)
+ && is_wall_or_door(x+1, y)) << 2;
+
+ o |= (is_wall_or_door(x-1, y )) << 3;
+
+ o |= (is_wall_or_door(x+1, y )) << 4;
+
+ o |= (is_wall_or_door(x-1, y+1)
+ && is_wall_or_door(x-1, y)
+ && is_wall_or_door(x, y+1)) << 5;
+
+ o |= (is_wall_or_door(x , y+1)) << 6;
+
+ o |= (is_wall_or_door(x+1, y+1)
+ && is_wall_or_door(x+1, y)
+ && is_wall_or_door(x, y+1)) << 7;
+
+ return o;
+}
+
+
+static int door_direction(int x, int y)
+{
+ if(is_wall_or_door(x-1, y) && is_wall_or_door(x+1, y)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int calc_nc(int c, int a)
+{
+ return ((a & 0x7F) << 7) + (c & 0x7F);
+}
+
+
+/**
+ * Draw objects of location i,j onto screen position xpos,ypos
+ * @author Hj. Malthaner
+ */
+void display_dinge(int x, int y, int xpos, int ypos)
+{
+ const int grid = get_grid();
+
+ int feat_nc = -1;
+ int obj_nc = -1;
+#ifdef USE_EGO_GRAPHICS
+ int ego_nc = -1;
+#endif
+
+ int shade;
+ int i;
+
+ // relative to view position
+// int xoff = x - p_ptr->px + (SCREEN_WID/2 + 13);
+// int xoff = x - p_ptr->px + (SCREEN_WID/2 + 13);
+//
+// new (jue):
+// look at cave.c, panel_col_of and the places it's used...
+ int xoff = x - panel_col_min + COL_MAP;
+ int yoff = y - panel_row_prt;
+
+ // try to use output of the term package
+ if(xoff >= 0 && yoff >= 1 && xoff < 80 && yoff < 23) {
+ // floor, walls
+
+ const int c = iso_cp[yoff][xoff];
+ const int a = iso_ap[yoff][xoff];
+
+ // object/monster
+ const int tc = iso_ctp[yoff][xoff];
+ const int ta = iso_atp[yoff][xoff];
+
+#ifdef USE_EGO_GRAPHICS
+ // transparent overlay
+ const int ec = iso_cep[yoff][xoff];
+ const int ea = iso_aep[yoff][xoff];
+
+ ego_nc = calc_nc(ec, ea);
+#endif
+ feat_nc = calc_nc(tc, ta);
+ obj_nc = calc_nc(c, a);
+
+ } else {
+ // outside 80x24 view, try to read map
+
+ if(is_valid_to_show(x,y)) {
+
+ byte a, ta;
+ char c, tc;
+#ifdef USE_EGO_GRAPHICS
+ byte ea;
+ char ec;
+
+ map_info(y, x, &a, &c, &ta, &tc, &ea, &ec);
+ ego_nc = calc_nc(ec, ea);
+#else
+ map_info(y, x, &a, &c, &ta, &tc);
+#endif
+ feat_nc = calc_nc(tc, ta);
+ obj_nc = calc_nc(c, a);
+ }
+
+ }
+
+
+
+ shade = 1;
+
+ if(is_lit(x, y)) {
+ shade = 2;
+ } else if(is_torch_lit(x, y)) {
+ shade = 0;
+ }
+
+
+ // did we get some data ?
+ if(feat_nc != -1) {
+
+ // check shading
+ // printf("%d %d -> %d\n", x, y, shade);
+
+ if(feat_nc >= 240 && feat_nc <= 245) {
+ // old shading
+ // const int shade = (feat_nc-240);
+ const int set_base = 240+shade*9;
+ const int bits = check_wall(x, y);
+
+
+ display_img(316+(shade%3), xpos, ypos, TRUE);
+
+ if(bits) {
+ for(i=0; i<4; i++) {
+ if(bits & (1 << i)) {
+ display_img(set_base+i, xpos, ypos, TRUE);
+ }
+ }
+
+ display_img(set_base+8, xpos, ypos, TRUE);
+
+ for(i=4; i<8; i++) {
+ if(bits & (1 << i)) {
+ display_img(set_base+i, xpos, ypos, TRUE);
+ }
+ }
+
+ } else {
+ // a pillar
+ display_img(set_base+8, xpos, ypos, TRUE);
+ }
+
+ // fields with walls never contain anything else
+ // so we can quit now
+ return;
+
+ } else if(feat_nc == 0x27) {
+ // open doors
+ display_img(138+door_direction(x,y), xpos, ypos, TRUE);
+
+
+ } else if(feat_nc == 0x2B) {
+ // closed doors
+ display_img(136+door_direction(x,y), xpos, ypos, TRUE);
+
+ } else if(feat_nc == 0x8C || // a between gate
+ feat_nc == 0x20 || // empty square
+ feat_nc == 0x16E || // brick roof
+ feat_nc == 0x16F || // brick roof top
+ feat_nc == 0x176 || // grass roof
+ feat_nc == 0x177 || // grass roof chimney
+ feat_nc == 0x17E || // grass roof top
+ (feat_nc >= 0x31 && feat_nc <= 0x39) || // shop with number
+ (feat_nc >= 0x576 && feat_nc <= 0x57D) || // trap
+ feat_nc == 0x3E || // down stairs
+ feat_nc == 0x3C // up stairs
+ ) {
+ // this features should not be shaded
+
+ display_img(feat_nc, xpos, ypos, TRUE);
+
+ } else if(feat_nc > 2){
+ // this features should be shaded
+
+ // floor
+ // known grids get shaded floors
+ if(is_only_torch_lit(x,y)) {
+ display_color_img(feat_nc+2,
+ xpos, ypos,
+ get_distance_to_player(x, y) << 1,
+ TRUE);
+ } else {
+ display_img(feat_nc+shade, xpos, ypos, TRUE);
+ }
+ }
+
+ // printf("%d a=%x c=%x (%c)\n", nc, a, c, c);
+
+ /* Hajo: display a complete grid if the user wants to */
+ if(grid == 2) {
+ display_img(6, xpos, ypos, TRUE);
+ }
+
+
+#ifdef USE_EGO_GRAPHICS
+ if( ego_nc != feat_nc &&
+ ego_nc > 2 &&
+ ego_nc != 0x27 &&
+ ego_nc != 0x2B ) {
+
+ display_img(ego_nc, xpos, ypos, TRUE);
+ }
+#endif
+
+ if( obj_nc != feat_nc &&
+ obj_nc > 2 &&
+ obj_nc != 0x27 &&
+ obj_nc != 0x2B ) {
+
+ /* Hajo: display a grid below items/monsters if the user wants to */
+ if(grid == 1) {
+ display_img(5, xpos, ypos, TRUE);
+ }
+
+ display_img(obj_nc, xpos, ypos, TRUE);
+
+ }
+ } else {
+
+ // outside of dungeon
+ display_img(32, xpos, ypos, TRUE);
+ }
+
+ /* display cursor ? */
+ if(
+ high_x >= 1 &&
+ high_y >= 1) {
+ if(xoff == high_x &&
+ yoff == high_y) {
+ display_img(7, xpos, ypos, TRUE);
+
+ /* only draw once, wait until next request */
+ high_x = high_y = -1;
+ }
+ }
+}
+
diff --git a/src/iso/world_view.h b/src/iso/world_view.h
new file mode 100644
index 00000000..426627e2
--- /dev/null
+++ b/src/iso/world_view.h
@@ -0,0 +1,49 @@
+/* world_view.h
+ *
+ * Copyright (c) 2001 Hansjörg Malthaner
+ * hansjoerg.malthaner@gmx.de
+ *
+ * This file is part of the Simugraph<->Angband adaption code.
+ *
+ *
+ * This file may be copied and modified freely so long as the above credits,
+ * this paragraph, and the below disclaimer of warranty are retained; no
+ * financial profit is derived from said modification or copying; and all
+ * licensing rights to any modifications are granted to the original author,
+ * Hansjörg Malthaner.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef hajo_world_view_h
+#define hajo_world_view_h
+
+
+
+/**
+ * Draw ground of location i,j onto screen position xpos,ypos
+ * @author Hj. Malthaner
+ */
+void display_boden(int i, int j, int xpos, int ypos);
+
+
+/**
+ * Draw objects of location i,j onto screen position xpos,ypos
+ * @author Hj. Malthaner
+ */
+void display_dinge(int i, int j, int xpos, int ypos);
+
+
+#endif
diff --git a/src/lauxlib.h b/src/lauxlib.h
new file mode 100644
index 00000000..8b6db343
--- /dev/null
+++ b/src/lauxlib.h
@@ -0,0 +1,100 @@
+/*
+** $Id: lauxlib.h,v 1.4 2002/01/04 03:31:23 pelpel Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#ifndef LUALIB_API
+#define LUALIB_API extern
+#endif
+
+
+struct luaL_reg {
+ const char *name;
+ lua_CFunction func;
+};
+
+
+LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n);
+LUALIB_API void luaL_argerror (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *luaL_check_lstr (lua_State *L, int numArg, size_t *len);
+LUALIB_API const char *luaL_opt_lstr (lua_State *L, int numArg, const char *def, size_t *len);
+LUALIB_API long luaL_check_number (lua_State *L, int numArg);
+LUALIB_API long luaL_opt_number (lua_State *L, int numArg, long def);
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg);
+LUALIB_API void luaL_checktype (lua_State *L, int narg, int t);
+LUALIB_API void luaL_checkany (lua_State *L, int narg);
+
+LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...);
+LUALIB_API int luaL_findstring (const char *name, const char *const list[]);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_arg_check(L, cond,numarg,extramsg) if (!(cond)) \
+ luaL_argerror(L, numarg,extramsg)
+#define luaL_check_string(L,n) (luaL_check_lstr(L, (n), NULL))
+#define luaL_opt_string(L,n,d) (luaL_opt_lstr(L, (n), (d), NULL))
+#define luaL_check_int(L,n) ((int)luaL_check_number(L, n))
+#define luaL_check_long(L,n) ((long)luaL_check_number(L, n))
+#define luaL_opt_int(L,n,d) ((int)luaL_opt_number(L, n,d))
+#define luaL_opt_long(L,n,d) ((long)luaL_opt_number(L, n,d))
+#define luaL_openl(L,a) luaL_openlib(L, a, (sizeof(a)/sizeof(a[0])))
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#ifndef LUAL_BUFFERSIZE
+#define LUAL_BUFFERSIZE BUFSIZ
+#endif
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int level;
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_putchar(B,c) \
+ ((void)((B)->p < &(B)->buffer[LUAL_BUFFERSIZE] || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B);
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s);
+LUALIB_API void luaL_addvalue (luaL_Buffer *B);
+LUALIB_API void luaL_pushresult (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+#endif
+
+
diff --git a/src/levels.c b/src/levels.c
new file mode 100644
index 00000000..8ecc6786
--- /dev/null
+++ b/src/levels.c
@@ -0,0 +1,240 @@
+/* File: levels.c */
+
+/* Purpose: Levels functions */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Return the parameter of the given command in the given file
+ */
+static int start_line = 0;
+bool get_command(const char *file, char comm, char *param)
+{
+ char buf[1024];
+ int i = -1;
+ FILE *fp;
+ char *s;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DNGN, file);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* The file exists ? */
+ /* no ? then command not found */
+ if (!fp) return FALSE;
+
+ /* Parse to the end of the file or when the command is found */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Advance the line number */
+ i++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Is it the command we are looking for ? */
+ if ((i > start_line) && (buf[0] == comm))
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ start_line = i;
+
+ /* Get the parameter */
+ strcpy(param, s);
+
+ /* Close it */
+ my_fclose(fp);
+
+ return TRUE;
+ }
+
+ }
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Assume command not found */
+ return FALSE;
+}
+
+
+/*
+ * Return the dungeon branch starting form the current dungeon/level
+ */
+int get_branch()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the branch */
+ start_line = -1;
+ if (get_command(file, 'B', buf)) return (atoi(buf));
+
+ /* No branch ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the father dungeon branch
+ */
+int get_fbranch()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the branch */
+ start_line = -1;
+ if (get_command(file, 'A', buf)) return (atoi(buf));
+
+ /* No branch ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the father dungeon level
+ */
+int get_flevel()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'L', buf)) return (atoi(buf));
+
+ /* No level ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the extension of the savefile for the level
+ */
+bool get_dungeon_save(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'S', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the level generator
+ */
+bool get_dungeon_generator(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'G', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level
+ */
+bool get_dungeon_special(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'U', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level name
+ */
+bool get_dungeon_name(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'N', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level name
+ */
+void get_level_flags()
+{
+ char file[20];
+ char buf[1024], *s, *t;
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ start_line = -1;
+
+ /* Parse until done */
+ while (get_command(file, 'F', buf))
+ {
+ /* Parse every entry textually */
+ for (s = buf; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&dungeon_flags1, &dungeon_flags2, s)) return;
+
+ /* Start the next entry */
+ s = t;
+ }
+ }
+}
+
+/*
+ * Return the special level desc
+ */
+bool get_level_desc(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'D', buf)) return (TRUE);
+ else return FALSE;
+}
diff --git a/src/load_gif.c b/src/load_gif.c
new file mode 100644
index 00000000..a8cb6cc0
--- /dev/null
+++ b/src/load_gif.c
@@ -0,0 +1,384 @@
+/*
+ * GIF Loader
+ * by Paul Bartrum
+ *
+ * Comment by Eric Stevens (tome@eastevens.com):
+ *
+ * This file has been modified to work with Allegro 4.0.3 that comes with
+ * DJGPP 2.0.3 for the use in compiling for ToME 2.2.7. This modification
+ * changes a variable name to one that is not in conflict with Allegro
+ * variable name. The variable 'empty_string' was changed to 'empty_str'.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <allegro.h>
+#include <string.h>
+
+int _color_load_depth(int depth);
+
+struct LZW_STRING
+{
+ short base;
+ char new;
+ short length;
+};
+
+PACKFILE *f;
+int empty_str, curr_bit_size, bit_overflow;
+int bit_pos, data_pos, data_len, entire, code;
+int cc, string_length, i, bit_size;
+unsigned char string[4096];
+struct LZW_STRING str[4096];
+BITMAP *bmp;
+int image_x, image_y, image_w, image_h, x, y;
+int interlace;
+
+
+void clear_table(void)
+{
+ empty_str = cc + 2;
+ curr_bit_size = bit_size + 1;
+ bit_overflow = 0;
+}
+
+
+void get_code(void)
+{
+ if (bit_pos + curr_bit_size > 8)
+ {
+ if (data_pos >= data_len)
+ {
+ data_len = pack_getc(f);
+ data_pos = 0;
+ }
+ entire = (pack_getc(f) << 8) + entire;
+ data_pos ++;
+ }
+ if (bit_pos + curr_bit_size > 16)
+ {
+ if (data_pos >= data_len)
+ {
+ data_len = pack_getc(f);
+ data_pos = 0;
+ }
+ entire = (pack_getc(f) << 16) + entire;
+ data_pos ++;
+ }
+ code = (entire >> bit_pos) & ((1 << curr_bit_size) - 1);
+ if (bit_pos + curr_bit_size > 8)
+ entire >>= 8;
+ if (bit_pos + curr_bit_size > 16)
+ entire >>= 8;
+ bit_pos = (bit_pos + curr_bit_size) % 8;
+ if (bit_pos == 0)
+ {
+ if (data_pos >= data_len)
+ {
+ data_len = pack_getc(f);
+ data_pos = 0;
+ }
+ entire = pack_getc(f);
+ data_pos ++;
+ }
+}
+
+
+void input_string(int num)
+{
+ if (num < cc)
+ {
+ string_length = 1;
+ string[0] = str[num].new;
+ }
+ else
+ {
+ i = str[num].length;
+ string_length = i;
+ while (i > 0)
+ {
+ i --;
+ string[i] = str[num].new;
+ num = str[num].base;
+ }
+ /* if(num != -1) **-{[ERROR]}-** */
+ }
+}
+
+
+void output_string(void)
+{
+ for (i = 0; i < string_length; i ++)
+ {
+ putpixel(bmp, x, y, string[i]);
+ x ++;
+ if (x >= image_x + image_w)
+ {
+ x = image_x;
+ y += interlace;
+ if (interlace)
+ {
+ if (y >= image_y + image_h)
+ {
+ if (interlace == 8 && (y - image_y) % 8 == 0)
+ {
+ interlace = 8;
+ y = image_y + 4;
+ }
+ else if (interlace == 8 && (y - image_y) % 8 == 4)
+ {
+ interlace = 4;
+ y = image_y + 2;
+ }
+ else if (interlace == 4)
+ {
+ interlace = 2;
+ y = image_y + 1;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/* load_gif:
+ * Loads a 2-256 colour GIF file onto a bitmap, returning the bitmap
+ * structure and storing the pallete data in the specified pallete (this
+ * should be an array of at least 256 RGB structures).
+ */
+BITMAP *load_gif (char *filename, RGB *pal)
+{
+ int width, height, depth;
+ int old;
+ BITMAP *bmp2;
+ int dest_depth;
+
+ f = pack_fopen(filename, F_READ);
+ if (!f) /* can't open file */
+ return NULL;
+
+ i = pack_mgetw(f) << 8;
+ i += pack_getc(f);
+ if (i != 0x474946) /* is it really a GIF? */
+ {
+ pack_fclose(f);
+ return NULL;
+ }
+ pack_fseek(f, 3); /* skip version */
+
+ width = pack_igetw(f);
+ height = pack_igetw(f);
+
+ bmp = create_bitmap_ex(8, width, height);
+ if (bmp == NULL)
+ {
+ pack_fclose(f);
+ return NULL;
+ }
+ clear(bmp);
+
+ i = pack_getc(f);
+ if (i & 128) /* no global colour table? */
+ depth = (i & 7) + 1;
+ else
+ depth = 0;
+
+ pack_fseek(f, 2); /* skip background colour and aspect ratio */
+
+ if (pal && depth) /* only read palette if pal and depth are not 0 */
+ {
+ for (i = 0; i < (1 << depth); i ++)
+ {
+ pal[i].r = pack_getc(f) / 4;
+ pal[i].g = pack_getc(f) / 4;
+ pal[i].b = pack_getc(f) / 4;
+ }
+ }
+ else
+ if (depth)
+ pack_fseek(f, (1 << depth) * 3);
+
+ do
+ {
+ i = pack_getc(f);
+ switch (i)
+ {
+ case 0x2C: /* Image Descriptor */
+ image_x = pack_igetw(f);
+ image_y = pack_igetw(f); /* individual image dimensions */
+ image_w = pack_igetw(f);
+ image_h = pack_igetw(f);
+
+ i = pack_getc(f);
+ if (i & 64)
+ interlace = 8;
+ else
+ interlace = 1;
+
+ if (i & 128)
+ {
+ depth = (i & 7) + 1;
+ if (pal)
+ {
+ for (i = 0; i < (1 << depth); i ++)
+ {
+ pal[i].r = pack_getc(f) / 4;
+ pal[i].g = pack_getc(f) / 4;
+ pal[i].b = pack_getc(f) / 4;
+ }
+ }
+ else
+ pack_fseek(f, (1 << depth) * 3);
+ }
+
+ /* lzw stream starts now */
+ bit_size = pack_getc(f);
+ cc = 1 << bit_size;
+
+ /* initialise string table */
+ for (i = 0; i < cc; i ++)
+ {
+ str[i].base = -1;
+ str[i].new = i;
+ str[i].length = 1;
+ }
+
+ /* initialise the variables */
+ bit_pos = 0;
+ data_len = pack_getc(f);
+ data_pos = 0;
+ entire = pack_getc(f);
+ data_pos ++;
+ string_length = 0;
+ x = image_x;
+ y = image_y;
+
+ /* starting code */
+ clear_table();
+ get_code();
+ if (code == cc)
+ get_code();
+ input_string(code);
+ output_string();
+ old = code;
+
+ while (TRUE)
+ {
+ get_code();
+
+ if (code == cc)
+ {
+ /* starting code */
+ clear_table();
+ get_code();
+ input_string(code);
+ output_string();
+ old = code;
+ }
+ else if (code == cc + 1)
+ {
+ break;
+ }
+ else if (code < empty_str)
+ {
+ input_string(code);
+ output_string();
+
+ if (bit_overflow == 0)
+ {
+ str[empty_str].base = old;
+ str[empty_str].new = string[0];
+ str[empty_str].length = str[old].length + 1;
+ empty_str ++;
+ if (empty_str == (1 << curr_bit_size))
+ curr_bit_size ++;
+ if (curr_bit_size == 13)
+ {
+ curr_bit_size = 12;
+ bit_overflow = 1;
+ }
+ }
+
+ old = code;
+ }
+ else
+ {
+ input_string(old);
+ string[str[old].length] = string[0];
+ string_length ++;
+
+ if (bit_overflow == 0)
+ {
+ str[empty_str].base = old;
+ str[empty_str].new = string[0];
+ str[empty_str].length = str[old].length + 1;
+ empty_str ++;
+ if (empty_str == (1 << curr_bit_size))
+ curr_bit_size ++;
+ if (curr_bit_size == 13)
+ {
+ curr_bit_size = 12;
+ bit_overflow = 1;
+ }
+ }
+
+ output_string();
+ old = code;
+ }
+ }
+ break;
+ case 0x21: /* Extension Introducer */
+ i = pack_getc(f);
+ if (i == 0xF9) /* Graphic Control Extension */
+ {
+ pack_fseek(f, 1); /* skip size (it's 4) */
+ i = pack_getc(f);
+ if (i & 1) /* is transparency enabled? */
+ {
+ pack_fseek(f, 2);
+ pack_getc(f); /* transparent colour */
+ }
+ else
+ pack_fseek(f, 3);
+ }
+ i = pack_getc(f);
+ while (i) /* skip Data Sub-blocks */
+ {
+ pack_fseek(f, i);
+ i = pack_getc(f);
+ }
+ break;
+ case 0x3B: /* Trailer - end of data */
+ pack_fclose(f);
+
+ /* convert to correct colour depth */
+ dest_depth = _color_load_depth(8);
+
+ if (dest_depth != 8)
+ {
+ bmp2 = create_bitmap_ex(dest_depth, bmp->w, bmp->h);
+ if (!bmp2)
+ {
+ destroy_bitmap(bmp);
+ return NULL;
+ }
+
+ select_palette(pal);
+ blit(bmp, bmp2, 0, 0, 0, 0, bmp->w, bmp->h);
+ unselect_palette();
+
+ destroy_bitmap(bmp);
+ bmp = bmp2;
+ }
+
+ return bmp;
+ }
+ }
+ while (TRUE);
+
+ /* this is never executed but DJGPP complains if you leave it out */
+ return NULL;
+}
diff --git a/src/loadsave.c b/src/loadsave.c
new file mode 100644
index 00000000..f77a5a9f
--- /dev/null
+++ b/src/loadsave.c
@@ -0,0 +1,3977 @@
+/* File: loadsave.c */
+
+/* Purpose: interact with savefiles. This file was made by
+ unifying load2.c and save.c from the old codebase. Doing it
+ this way makes maintenance easier and lets us share code. */
+
+#include "angband.h"
+
+/* Don't play with this yet */
+/* #define BZ_SAVES */
+#ifdef BZ_SAVES
+#include <bzlib.h>
+#endif /* BZ_SAVES */
+
+static void do_byte(byte *, int);
+static void do_u16b(u16b *, int);
+static void do_s16b(s16b *, int);
+static void do_u32b(u32b *, int);
+static void do_s32b(s32b *, int);
+static void do_string(char *, int, int);
+static void do_lore(int, int);
+static void do_monster(monster_type *, int);
+static void do_randomizer(int flag);
+static void do_spells(int, int);
+static void note(cptr);
+static void do_fate(int, int);
+static void do_item(object_type *, int);
+static void do_options(int);
+static bool do_store(store_type *, int);
+static void do_messages(int flag);
+static void do_xtra(int, int);
+static bool do_savefile_aux(int);
+static void junkinit(void);
+static void morejunk(void);
+static bool do_inventory(int);
+static bool do_dungeon(int, bool);
+static void do_grid(int);
+static void my_sentinel(char *, u16b, int);
+
+#ifdef BZ_SAVES
+static void bz_done(int);
+static void bz_prep(int);
+#endif
+
+static void do_ver_byte(byte *, u32b, byte, int);
+static void do_ver_u16b(u16b *, u32b, u16b, int);
+static void do_ver_s16b(s16b *, u32b, s16b, int);
+static void do_ver_u32b(u32b *, u32b, u32b, int);
+static void do_ver_s32b(s32b *, u32b, s32b, int);
+static void do_ver_string(char *, int, u32b, char *, int);
+
+static void skip_ver_byte(u32b, int);
+static void skip_ver_u16b(u32b, int);
+static void skip_ver_s16b(u32b, int);
+static void skip_ver_u32b(u32b, int);
+static void skip_ver_s32b(u32b, int);
+static void skip_ver_string(u32b, int);
+
+errr rd_savefile(void);
+
+#ifdef SAFER_PANICS
+bool panicload;
+#endif
+
+static FILE *fff; /* Local savefile ptr */
+
+#ifdef BZ_SAVES
+BZFILE *bzf;
+int bzerr;
+#endif /* BZ_SAVES */
+
+/*
+ * Basic byte-level reading from savefile. This provides a single point
+ * of interface to the pseudoencryption that ToME (and Angband)
+ * uses. I'm thinking about if it might be faster/better to modify all
+ * the do_* functions to directly do this stuff -- it'd make the code
+ * somewhat uglier to maintain, but concievably might be much faster. Or
+ * is it better maybe to scrap the pseudoencryption entirely and adopt
+ * some other means of obfuscation, should it still prove useful in any
+ * way? -- Improv
+ *
+ * What's the point of encryption on savefiles anyway? If I wanted to
+ * make a cheater savefile, I'd activate debug mode, and hack the game
+ * not to save it. There's no point. -- takkaria
+ */
+
+static byte sf_get(void)
+{
+ byte c;
+
+ /* Get a character, decode the value */
+#ifndef BZ_SAVES
+ c = getc(fff) & 0xFF;
+#else
+BZ2_bzRead(&bzerr, bzf, &c, 1);
+
+ if (bzerr != 0)
+ {
+ note(format("Compression error on read"));
+ bz_done(LS_LOAD);
+ printf("Died in sf_get\n");
+ exit(0);
+ }
+#endif /* BZ_SAVES */
+
+ /* Return the value */
+ return (c);
+}
+
+
+static void sf_put(byte v)
+{
+#ifndef BZ_SAVES
+ (void)putc((int)v, fff);
+#else
+ BZ2_bzWrite(&bzerr, bzf, &v, 1);
+
+ if (bzerr != 0)
+ {
+ note(format("Compression error on write"));
+ bz_done(LS_SAVE);
+ printf("Died in sf_put\n");
+ exit(0);
+ }
+#endif
+}
+
+/*
+ * This function does nothing if BZ_SAVES in undefined.
+ */
+#ifdef BZ_SAVES
+static void bz_prep(int flag)
+{
+
+ if (flag == LS_LOAD)
+ {
+ bzf = BZ2_bzReadOpen(&bzerr, fff, 0, 0, NULL, 0);
+ }
+
+ if (flag == LS_SAVE)
+ {
+ bzf = BZ2_bzWriteOpen(&bzerr, fff, 9, 0, 30);
+ }
+
+ if (bzerr == 0)
+ {
+ return ; /* All is Good */
+ }
+
+ /* Otherwise, all is bad */
+ note(format("Compression error on prep"));
+ printf("Died in bz_prep\n");
+ exit(0);
+
+ /* Unreachable code */
+ bz_done(flag);
+}
+
+static void bz_done(int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ BZ2_bzReadClose(&bzerr, bzf);
+ }
+ if (flag == LS_SAVE)
+ {
+ BZ2_bzWriteClose(&bzerr, bzf, 0, NULL, NULL);
+ }
+}
+#endif /* BZ_SAVES */
+
+/*
+ * Do object memory and similar stuff
+ */
+static void do_xtra(int k_idx, int flag)
+{
+ byte tmp8u = 0;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (flag == LS_SAVE)
+ {
+ if (k_ptr->aware) tmp8u |= 0x01;
+ if (k_ptr->tried) tmp8u |= 0x02;
+ if (k_ptr->know) tmp8u |= 0x04;
+ if (k_ptr->artifact) tmp8u |= 0x80;
+
+ do_byte(&tmp8u, flag);
+ }
+ if (flag == LS_LOAD)
+ {
+ do_byte(&tmp8u, flag);
+ k_ptr->aware = ((tmp8u & 0x01) ? TRUE : FALSE);
+ k_ptr->tried = ((tmp8u & 0x02) ? TRUE : FALSE);
+ k_ptr->know = ((tmp8u & 0x04) ? TRUE : FALSE);
+ k_ptr->artifact = ((tmp8u & 0x80) ? TRUE : FALSE);
+ }
+}
+
+/*
+ * Load/Save quick start data
+ */
+void do_quick_start(int flag)
+{
+ int i;
+
+ do_s16b(&previous_char.sex, flag);
+ do_s16b(&previous_char.race, flag);
+ do_s16b(&previous_char.rmod, flag);
+ do_s16b(&previous_char.pclass, flag);
+ do_s16b(&previous_char.spec, flag);
+ do_byte(&previous_char.quests, flag);
+ do_byte(&previous_char.god, flag);
+ do_s32b(&previous_char.grace, flag);
+ do_s16b(&previous_char.age, flag);
+ do_s16b(&previous_char.wt, flag);
+ do_s16b(&previous_char.ht, flag);
+ do_s16b(&previous_char.sc, flag);
+ do_s32b(&previous_char.au, flag);
+
+ for (i = 0; i < 6; i++) do_s16b(&(previous_char.stat[i]), flag);
+ do_s16b(&previous_char.luck, flag);
+
+ do_s16b(&previous_char.chaos_patron, flag);
+ do_u32b(&previous_char.weapon, flag);
+ do_byte((byte*)&previous_char.quick_ok, flag);
+
+ for (i = 0; i < 4; i++) do_string(previous_char.history[i], 60, flag);
+}
+
+/*
+ * The special saved subrace
+ */
+static void do_subrace(int flag)
+{
+ player_race_mod *sr_ptr = &race_mod_info[SUBRACE_SAVE];
+ int i;
+ char buf[81];
+
+ if (flag == LS_SAVE)
+ strcpy(buf, sr_ptr->title + rmp_name);
+ do_string(buf, 80, flag);
+ if (flag == LS_LOAD)
+ strcpy(sr_ptr->title + rmp_name, buf);
+
+ if (flag == LS_SAVE)
+ strcpy(buf, sr_ptr->desc + rmp_text);
+ do_string(buf, 80, flag);
+ if (flag == LS_LOAD)
+ strcpy(sr_ptr->desc + rmp_text, buf);
+
+ do_byte((byte*)&sr_ptr->place, flag);
+
+ for (i = 0; i < 6; i++)
+ do_s16b(&sr_ptr->r_adj[i], flag);
+
+ do_byte((byte*)&sr_ptr->luck, flag);
+ do_s16b(&sr_ptr->mana, flag);
+
+ do_s16b(&sr_ptr->r_dis, flag);
+ do_s16b(&sr_ptr->r_dev, flag);
+ do_s16b(&sr_ptr->r_sav, flag);
+ do_s16b(&sr_ptr->r_stl, flag);
+ do_s16b(&sr_ptr->r_srh, flag);
+ do_s16b(&sr_ptr->r_fos, flag);
+ do_s16b(&sr_ptr->r_thn, flag);
+ do_s16b(&sr_ptr->r_thb, flag);
+
+ do_byte((byte*)&sr_ptr->r_mhp, flag);
+ do_s16b(&sr_ptr->r_exp, flag);
+
+ do_byte((byte*)&sr_ptr->b_age, flag);
+ do_byte((byte*)&sr_ptr->m_age, flag);
+
+ do_byte((byte*)&sr_ptr->m_b_ht, flag);
+ do_byte((byte*)&sr_ptr->m_m_ht, flag);
+ do_byte((byte*)&sr_ptr->f_b_ht, flag);
+ do_byte((byte*)&sr_ptr->f_m_ht, flag);
+
+ do_byte((byte*)&sr_ptr->m_b_wt, flag);
+ do_byte((byte*)&sr_ptr->m_m_wt, flag);
+ do_byte((byte*)&sr_ptr->f_b_wt, flag);
+ do_byte((byte*)&sr_ptr->f_m_wt, flag);
+
+ do_byte((byte*)&sr_ptr->infra, flag);
+
+ for (i = 0; i < 4; i++)
+ do_s16b(&sr_ptr->powers[i], flag);
+
+ for (i = 0; i < BODY_MAX; i++)
+ do_byte((byte*)&sr_ptr->body_parts[i], flag);
+
+ do_u32b(&sr_ptr->flags1, flag);
+ do_u32b(&sr_ptr->flags2, flag);
+
+ for (i = 0; i < PY_MAX_LEVEL + 1; i++)
+ {
+ do_u32b(&sr_ptr->oflags1[i], flag);
+ do_u32b(&sr_ptr->oflags2[i], flag);
+ do_u32b(&sr_ptr->oflags3[i], flag);
+ do_u32b(&sr_ptr->oflags4[i], flag);
+ do_u32b(&sr_ptr->oflags5[i], flag);
+ do_u32b(&sr_ptr->oesp[i], flag);
+ do_s16b(&sr_ptr->opval[i], flag);
+ }
+
+ do_byte(&sr_ptr->g_attr, flag);
+ do_byte((byte*)&sr_ptr->g_char, flag);
+
+ for (i = 0; i < MAX_SKILLS; i++)
+ {
+ do_byte((byte*)&sr_ptr->skill_basem[i], flag);
+ do_u32b(&sr_ptr->skill_base[i], flag);
+ do_byte((byte*)&sr_ptr->skill_modm[i], flag);
+ do_s16b(&sr_ptr->skill_mod[i], flag);
+ }
+}
+
+/*
+ * Misc. other data
+ */
+static char loaded_game_module[80];
+static bool do_extra(int flag)
+{
+ int i, j;
+ byte tmp8u;
+ s16b tmp16s;
+ u32b tmp32u;
+ u16b tmp16b;
+
+ do_string(player_name, 32, flag);
+
+ do_string(died_from, 80, flag);
+
+ for (i = 0; i < 4; i++)
+ {
+ do_string(history[i], 60, flag);
+ }
+
+ /* Handle the special levels info */
+ if (flag == LS_SAVE)
+ {
+ tmp8u = max_d_idx;
+ tmp16s = MAX_DUNGEON_DEPTH;
+ }
+ do_byte(&tmp8u, flag);
+
+ if (flag == LS_LOAD)
+ {
+ if (tmp8u > max_d_idx)
+ {
+ note(format("Too many (%d) dungeon types!", tmp8u));
+ }
+ }
+
+ do_s16b(&tmp16s, flag);
+
+ if (flag == LS_LOAD)
+ {
+ if (tmp16s > MAX_DUNGEON_DEPTH)
+ {
+ note(format("Too many (%d) max level by dungeon type!", tmp16s));
+ }
+ }
+
+ /* Load the special levels history */
+ for (i = 0; i < tmp8u; i++)
+ {
+ for (j = 0; j < tmp16s; j++)
+ {
+ do_byte((byte*)&special_lvl[j][i], flag);
+ }
+ }
+
+ do_byte((byte*)&generate_special_feeling, flag);
+
+ /* Load the quick start data */
+ do_quick_start(flag);
+
+ /* Load/save the special subrace */
+ do_subrace(flag);
+
+ /* Race/Class/Gender/Spells */
+ do_s32b(&p_ptr->lives, flag);
+ do_byte(&p_ptr->prace, flag);
+ do_byte(&p_ptr->pracem, flag);
+ do_byte(&p_ptr->pclass, flag);
+ do_byte(&p_ptr->pspec, flag);
+ do_byte(&p_ptr->psex, flag);
+ do_u16b(&tmp16b, flag);
+ do_u16b(&tmp16b, flag);
+ do_byte(&p_ptr->mimic_form, flag);
+ do_s16b(&p_ptr->mimic_level, flag);
+ if (flag == LS_SAVE) tmp8u = 0;
+
+ do_byte(&p_ptr->hitdie, flag);
+ do_u16b(&p_ptr->expfact, flag);
+
+ do_s16b(&p_ptr->age, flag);
+ do_s16b(&p_ptr->ht, flag);
+ do_s16b(&p_ptr->wt, flag);
+
+ /* Dump the stats (maximum and current) */
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_max[i], flag);
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_cur[i], flag);
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_cnt[i], flag);
+ for (i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_los[i], flag);
+
+ /* Dump the skills */
+ do_s16b(&p_ptr->skill_points, flag);
+ do_s16b(&p_ptr->skill_last_level, flag);
+ do_s16b(&p_ptr->melee_style, flag);
+ do_s16b(&p_ptr->use_piercing_shots, flag);
+
+ tmp16s = MAX_SKILLS;
+ do_s16b(&tmp16s, flag);
+
+ if ((flag == LS_LOAD) && (tmp16s > MAX_SKILLS))
+ {
+ quit("Too many skills");
+ }
+
+ if (flag == LS_SAVE) old_max_s_idx = max_s_idx;
+ do_u16b(&old_max_s_idx, flag);
+ for (i = 0; i < tmp16s; ++i)
+ {
+ if (i < old_max_s_idx)
+ {
+ do_s32b(&s_info[i].value, flag);
+ do_s32b(&s_info[i].mod, flag);
+ do_byte((byte*)&s_info[i].dev, flag);
+ do_byte((byte*)&s_info[i].hidden, flag);
+ do_u32b(&s_info[i].uses, flag);
+ }
+ else
+ {
+ do_u32b(&tmp32u, flag);
+ do_s16b(&tmp16s, flag);
+ do_byte(&tmp8u, flag);
+ do_byte(&tmp8u, flag);
+ do_u32b(&tmp32u, flag);
+ }
+ }
+
+ tmp16s = max_ab_idx;
+ do_s16b(&tmp16s, flag);
+
+ if ((flag == LS_LOAD) && (tmp16s > max_ab_idx))
+ {
+ quit("Too many abilities");
+ }
+
+ for (i = 0; i < tmp16s; ++i)
+ {
+ do_byte((byte*)&ab_info[i].acquired, flag);
+ }
+
+ do_s16b(&p_ptr->luck_base, flag);
+ do_s16b(&p_ptr->luck_max, flag);
+
+ /* Found 24 unused bytes here...
+ Converted it to be the alchemist's
+ known artifact flags.
+ Note that the ego flags and the gained levels
+ record are recorded here too, but we use the
+ _ver_ format to protect save file compatablity.
+ Note that the other alchemist knowledge (item types known)
+ is stored in do_aux, and is a bit flag in a previously
+ unused bit.
+ */
+ for (i = 0; i < 6 ; ++i)
+ do_u32b(&alchemist_known_artifacts[i], flag);
+
+ for (i = 0; i < 32 ; ++i)
+ do_u32b(&alchemist_known_egos[i], flag);
+
+ do_u32b(&alchemist_gained, flag);
+
+ do_s32b(&p_ptr->au, flag);
+
+ do_s32b(&p_ptr->max_exp, flag);
+ do_s32b(&p_ptr->exp, flag);
+ do_u16b(&p_ptr->exp_frac, flag);
+ do_s16b(&p_ptr->lev, flag);
+
+ do_s16b(&p_ptr->town_num, flag); /* -KMW- */
+
+ /* Write arena and rewards information -KMW- */
+ do_s16b(&p_ptr->arena_number, flag);
+ do_s16b(&p_ptr->inside_arena, flag);
+ do_s16b(&p_ptr->inside_quest, flag);
+ do_byte((byte*)&p_ptr->exit_bldg, flag);
+
+
+ /* Save/load spellbinder */
+ do_byte(&p_ptr->spellbinder_num, flag);
+ do_byte(&p_ptr->spellbinder_trigger, flag);
+ for (i = 0; i < 4; i++)
+ do_u32b(&p_ptr->spellbinder[i], flag);
+
+
+ do_byte(&tmp8u, flag); /* tmp8u should be 0 at this point */
+
+ if (flag == LS_SAVE) tmp8u = MAX_PLOTS;
+ do_byte(&tmp8u, flag);
+
+ if ((flag == LS_LOAD) && (tmp8u > MAX_PLOTS))
+ {
+ quit(format("Too many plots, %d %d", tmp8u, MAX_PLOTS));
+ }
+
+ for (i = 0; i < tmp8u; i++)
+ {
+ do_s16b(&plots[i], flag);
+ }
+
+ if (flag == LS_SAVE)
+ {
+ tmp8u = MAX_RANDOM_QUEST;
+ }
+ do_byte(&tmp8u, flag);
+
+ if ((flag == LS_LOAD) &&
+ (tmp8u > MAX_RANDOM_QUEST)) quit("Too many random quests");
+ for (i = 0; i < tmp8u; i++)
+ {
+ do_byte(&random_quests[i].type, flag);
+ do_s16b(&random_quests[i].r_idx, flag);
+ do_byte((byte*)&random_quests[i].done, flag);
+ }
+
+ do_s16b(&p_ptr->oldpx, flag);
+ do_s16b(&p_ptr->oldpy, flag);
+
+ do_s16b(&p_ptr->mhp, flag);
+ do_s16b(&p_ptr->chp, flag);
+ do_u16b(&p_ptr->chp_frac, flag);
+ do_s16b(&p_ptr->hp_mod, flag);
+
+ do_s16b(&p_ptr->msane, flag);
+ do_s16b(&p_ptr->csane, flag);
+ do_u16b(&p_ptr->csane_frac, flag);
+
+ do_s16b(&p_ptr->msp, flag);
+ do_s16b(&p_ptr->csp, flag);
+ do_u16b(&p_ptr->csp_frac, flag);
+
+ /* XXX
+ Here's where tank points were.
+ Those who run the estate of you-know-who is really stupid.
+ I'll never even consider reading her books now. -- neil */
+ do_s16b(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+
+ /* Gods */
+ do_s32b(&p_ptr->grace, flag);
+ do_byte((byte*)&p_ptr->praying, flag);
+ do_s16b(&p_ptr->melkor_sacrifice, flag);
+ do_byte(&p_ptr->pgod, flag);
+
+ /* Max Player and Dungeon Levels */
+ do_s16b(&p_ptr->max_plv, flag);
+
+ if (flag == LS_SAVE)
+ tmp8u = max_d_idx;
+ do_byte(&tmp8u, flag);
+ for (i = 0; i < tmp8u; i++)
+ {
+ if (flag == LS_SAVE)
+ tmp16s = max_dlv[i];
+ do_s16b(&tmp16s, flag);
+ if ((flag == LS_LOAD) && (i <= max_d_idx))
+ max_dlv[i] = tmp16s;
+ }
+ /* Repair max player level??? */
+ if ((flag == LS_LOAD) && (p_ptr->max_plv < p_ptr->lev))
+ p_ptr->max_plv = p_ptr->lev;
+
+ do_byte((byte*)&(p_ptr->help.enabled), flag);
+ do_u32b(&(p_ptr->help.help1), flag);
+
+ /* More info */
+ tmp16s = 0;
+ do_s16b(&p_ptr->sc, flag);
+ do_s16b(&p_ptr->blind, flag);
+ do_s16b(&p_ptr->paralyzed, flag);
+ do_s16b(&p_ptr->confused, flag);
+ do_s16b(&p_ptr->food, flag);
+ do_s32b(&p_ptr->energy, flag);
+ do_s16b(&p_ptr->fast, flag);
+ do_s16b(&p_ptr->speed_factor, flag);
+ do_s16b(&p_ptr->slow, flag);
+ do_s16b(&p_ptr->afraid, flag);
+ do_s16b(&p_ptr->cut, flag);
+ do_s16b(&p_ptr->stun, flag);
+ do_s16b(&p_ptr->poisoned, flag);
+ do_s16b(&p_ptr->image, flag);
+ do_s16b(&p_ptr->protevil, flag);
+ do_s16b(&p_ptr->protundead, flag);
+ do_s16b(&p_ptr->invuln, flag);
+ do_s16b(&p_ptr->hero, flag);
+ do_s16b(&p_ptr->shero, flag);
+ do_s16b(&p_ptr->shield, flag);
+ do_s16b(&p_ptr->shield_power, flag);
+ do_s16b(&p_ptr->shield_power_opt, flag);
+ do_s16b(&p_ptr->shield_power_opt2, flag);
+ do_s16b(&p_ptr->shield_opt, flag);
+ do_s16b(&p_ptr->blessed, flag);
+ do_s16b(&p_ptr->control, flag);
+ do_byte(&p_ptr->control_dir, flag);
+ do_s16b(&p_ptr->tim_thunder, flag);
+ do_s16b(&p_ptr->tim_thunder_p1, flag);
+ do_s16b(&p_ptr->tim_thunder_p2, flag);
+ do_s16b(&p_ptr->tim_project, flag);
+ do_s16b(&p_ptr->tim_project_dam, flag);
+ do_s16b(&p_ptr->tim_project_gf, flag);
+ do_s16b(&p_ptr->tim_project_rad, flag);
+ do_s16b(&p_ptr->tim_project_flag, flag);
+
+ do_s16b(&p_ptr->tim_magic_breath, flag);
+ do_s16b(&p_ptr->tim_water_breath, flag);
+
+ do_s16b(&p_ptr->tim_roots, flag);
+ do_s16b(&p_ptr->tim_roots_ac, flag);
+ do_s16b(&p_ptr->tim_roots_dam, flag);
+
+ do_s16b(&p_ptr->tim_invis, flag);
+ do_s16b(&p_ptr->word_recall, flag);
+ do_s16b(&p_ptr->recall_dungeon, flag);
+ do_s16b(&p_ptr->see_infra, flag);
+ do_s16b(&p_ptr->tim_infra, flag);
+ do_s16b(&p_ptr->oppose_fire, flag);
+ do_s16b(&p_ptr->oppose_cold, flag);
+ do_s16b(&p_ptr->oppose_acid, flag);
+ do_s16b(&p_ptr->oppose_elec, flag);
+ do_s16b(&p_ptr->oppose_pois, flag);
+ do_s16b(&p_ptr->oppose_ld, flag);
+ do_s16b(&p_ptr->oppose_cc, flag);
+ do_s16b(&p_ptr->oppose_ss, flag);
+ do_s16b(&p_ptr->oppose_nex, flag);
+
+ do_s16b(&p_ptr->tim_esp, flag);
+ do_s16b(&p_ptr->tim_wraith, flag);
+ do_s16b(&p_ptr->tim_ffall, flag);
+ do_ver_s16b(&p_ptr->tim_fly, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&p_ptr->tim_fire_aura, flag);
+ do_ver_s16b(&p_ptr->tim_poison, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&p_ptr->resist_magic, flag);
+ do_s16b(&p_ptr->tim_invisible, flag);
+ do_s16b(&p_ptr->tim_inv_pow, flag);
+ do_s16b(&p_ptr->tim_mimic, flag);
+ do_s16b(&p_ptr->lightspeed, flag);
+ do_s16b(&p_ptr->tim_lite, flag);
+ do_ver_s16b(&p_ptr->tim_regen, SAVEFILE_VERSION, 0, flag);
+ do_ver_s16b(&p_ptr->tim_regen_pow, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&p_ptr->holy, flag);
+ do_s16b(&p_ptr->walk_water, flag);
+ do_s16b(&p_ptr->tim_mental_barrier, flag);
+ do_s16b(&p_ptr->immov_cntr, flag);
+ do_s16b(&p_ptr->strike, flag);
+ do_s16b(&p_ptr->meditation, flag);
+ do_s16b(&p_ptr->tim_reflect, flag);
+ do_s16b(&p_ptr->tim_res_time, flag);
+ do_s16b(&p_ptr->tim_deadly, flag);
+ do_s16b(&p_ptr->prob_travel, flag);
+ do_s16b(&p_ptr->disrupt_shield, flag);
+ do_s16b(&p_ptr->parasite, flag);
+ do_s16b(&p_ptr->parasite_r_idx, flag);
+ do_s32b(&p_ptr->loan, flag);
+ do_s32b(&p_ptr->loan_time, flag);
+ do_s16b(&p_ptr->absorb_soul, flag);
+
+ do_s16b(&p_ptr->chaos_patron, flag);
+
+ if (flag == LS_SAVE) tmp16s = max_corruptions;
+ do_s16b(&tmp16s, flag);
+
+ for (i = 0; i < tmp16s; i++)
+ {
+ if ((flag == LS_SAVE) && (i < max_corruptions))
+ tmp8u = p_ptr->corruptions[i];
+
+ do_byte(&tmp8u, flag);
+
+ if ((flag == LS_LOAD) && (i < max_corruptions))
+ p_ptr->corruptions[i] = tmp8u;
+ }
+
+ do_byte(&p_ptr->confusing, flag);
+ do_byte((byte*)&p_ptr->black_breath, flag);
+ do_byte((byte*)&fate_flag, flag);
+ do_byte(&p_ptr->searching, flag);
+ do_byte(&p_ptr->maximize, flag);
+ do_byte(&p_ptr->preserve, flag);
+ do_byte(&p_ptr->special, flag);
+ do_byte((byte*)&ambush_flag, flag);
+ do_byte(&p_ptr->allow_one_death, flag);
+ do_s16b(&p_ptr->xtra_spells, flag);
+
+ do_byte(&vanilla_town, flag);
+
+ do_u16b(&no_breeds, flag);
+ do_s16b(&p_ptr->protgood, flag);
+
+ /* Auxilliary variables */
+ do_u32b(&p_ptr->mimic_extra, flag);
+ do_u32b(&p_ptr->antimagic_extra, flag);
+ do_u32b(&p_ptr->druid_extra, flag);
+ do_u32b(&p_ptr->druid_extra2, flag);
+ do_u32b(&p_ptr->druid_extra3, flag);
+ do_u32b(&p_ptr->music_extra, flag);
+ do_u32b(&p_ptr->music_extra2, flag);
+ do_u32b(&p_ptr->necro_extra, flag);
+ do_u32b(&p_ptr->necro_extra2, flag);
+
+ do_u32b(&p_ptr->race_extra1, flag);
+ do_u32b(&p_ptr->race_extra2, flag);
+ do_u32b(&p_ptr->race_extra3, flag);
+ do_u32b(&p_ptr->race_extra4, flag);
+ do_u32b(&p_ptr->race_extra5, flag);
+ do_u32b(&p_ptr->race_extra6, flag);
+ do_u32b(&p_ptr->race_extra7, flag);
+
+ do_u16b(&p_ptr->body_monster, flag);
+ do_byte((byte*)&p_ptr->disembodied, flag);
+
+ /* Are we in astral mode? */
+ do_byte((byte*)&p_ptr->astral, flag);
+
+ if (flag == LS_SAVE) tmp16s = POWER_MAX_INIT;
+ do_s16b(&tmp16s, flag);
+ if ((flag == LS_LOAD) && (tmp16s > POWER_MAX_INIT))
+ note(format("Too many (%u) powers!", tmp16s));
+ if (flag == LS_SAVE) tmp16s = POWER_MAX_INIT;
+ for (i = 0; i < tmp16s; i++)
+ do_byte((byte*)&p_ptr->powers_mod[i], flag);
+
+ skip_ver_byte(100, flag);
+
+ /* The tactic */
+ do_byte((byte*)&p_ptr->tactic, flag);
+
+ /* The movement */
+ do_byte((byte*)&p_ptr->movement, flag);
+
+ /* The comapnions killed */
+ do_s16b(&p_ptr->companion_killed, flag);
+
+ /* The fate */
+ do_byte((byte*)&p_ptr->no_mortal, flag);
+
+ /* The bounties */
+ for (i = 0; i < MAX_BOUNTIES; i++)
+ {
+ do_s16b(&bounties[i][0], flag);
+ do_s16b(&bounties[i][1], flag);
+ }
+ do_u32b(&total_bounties, flag);
+ do_s16b(&spell_num, flag);
+ for (i = 0; i < MAX_SPELLS; i++)
+ do_spells(i, flag);
+ do_s16b(&rune_num, flag);
+ for (i = 0; i < MAX_RUNES; i++)
+ {
+ do_string(rune_spells[i].name, 30, flag);
+ do_s16b(&rune_spells[i].type, flag);
+ do_s16b(&rune_spells[i].rune2, flag);
+ do_s16b(&rune_spells[i].mana, flag);
+ }
+
+ /* Write the "object seeds" */
+ do_u32b(&seed_dungeon, flag);
+ do_u32b(&seed_flavor, flag);
+ do_u32b(&seed_town, flag);
+
+ /* Special stuff */
+ do_u16b(&panic_save, flag);
+ do_u16b(&total_winner, flag);
+ do_u16b(&has_won, flag);
+ do_u16b(&noscore, flag);
+
+ /* Write death */
+ if (flag == LS_SAVE) tmp8u = death;
+ do_byte(&tmp8u, flag);
+ if (flag == LS_LOAD) death = tmp8u;
+
+ /* Incompatible module? */
+ if (flag == LS_LOAD)
+ {
+ s32b ok;
+
+ call_lua("module_savefile_loadable", "(s,d)", "d", loaded_game_module, death, &ok);
+
+ /* Argh bad game module! */
+ if (!ok)
+ {
+ note(format("Bad game module. Savefile was saved with module '%s' but game is '%s'.", loaded_game_module, game_module));
+ return (FALSE);
+ }
+ }
+
+ /* Write feeling */
+ if (flag == LS_SAVE) tmp8u = feeling;
+ do_byte(&tmp8u, flag);
+ if (flag == LS_LOAD) feeling = tmp8u;
+
+ /* Turn of last "feeling" */
+ do_s32b(&old_turn, flag);
+
+ /* Current turn */
+ do_s32b(&turn, flag);
+
+ return TRUE;
+}
+
+/* Save the current persistent dungeon -SC- */
+void save_dungeon(void)
+{
+ char tmp[16];
+ char name[1024], buf[5];
+
+ /* Save only persistent dungeons */
+ if (!get_dungeon_save(buf) || (!dun_level)) return;
+
+ /* Construct filename */
+ sprintf(tmp, "%s.%s", player_base, buf);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open the file */
+ fff = my_fopen(name, "wb");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+#ifdef BZ_SAVES
+ bz_prep(LS_SAVE);
+#endif /* BZ_SAVES */
+
+ /* Save the dungeon */
+ do_dungeon(LS_SAVE, TRUE);
+
+ /* Done */
+#ifdef BZ_SAVES
+ bz_done(LS_SAVE);
+#endif /* BZ_SAVES */
+ my_fclose(fff);
+}
+
+/*
+ * Medium level player saver
+ */
+static bool save_player_aux(char *name)
+{
+ bool ok = FALSE;
+ int fd = -1;
+ int mode = 0644;
+
+ /* No file yet */
+ fff = NULL;
+
+ /* File type is "SAVE" */
+ FILE_TYPE(FILE_TYPE_SAVE);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Create the savefile */
+ fd = fd_make(name, mode);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* File is okay */
+ if (fd >= 0)
+ {
+ /* Close the "fd" */
+ (void)fd_close(fd);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open the savefile */
+ fff = my_fopen(name, "wb");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+#ifdef BZ_SAVES
+ bz_prep(LS_SAVE);
+#endif
+
+ /* Successful open */
+ if (fff)
+ {
+#ifdef BZ_SAVES
+ bz_prep(LS_SAVE);
+#endif /* BZ_SAVES */
+
+ /* Write the savefile */
+ if (do_savefile_aux(LS_SAVE)) ok = TRUE;
+
+#ifdef BZ_SAVES
+ bz_done(LS_SAVE);
+#endif /* BZ_SAVES */
+ /* Attempt to close it */
+ if (my_fclose(fff)) ok = FALSE;
+ }
+
+ /* "broken" savefile */
+ if (!ok)
+ {
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove "broken" files */
+ (void)fd_kill(name);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+ }
+ }
+
+ /* Failure */
+ if (!ok) return (FALSE);
+
+ /* Successful save */
+ character_saved = TRUE;
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Attempt to save the player in a savefile
+ */
+bool save_player(void)
+{
+ int result = FALSE;
+ char safe[1024];
+#ifdef SAFER_PANICS
+ char panicsave[1024];
+#endif /* SAFER PANICS */
+
+
+#ifdef SAFER_PANICS
+ if (panic_save)
+ {
+ /*
+ * Not sure how to do this so it's nicely portable to brain-damaged
+ * OS's with short filenames
+ */
+ strcpy(panicsave, savefile);
+ strcat(panicsave, ".pnc");
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove any old panic saves */
+ fd_kill(panicsave);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Save character */
+ save_player_aux(panicsave);
+
+ return TRUE;
+ }
+#endif /* SAFER_PANICS */
+
+
+ /* New savefile */
+ strcpy(safe, savefile);
+ strcat(safe, ".new");
+
+#ifdef VM
+ /* Hack -- support "flat directory" usage on VM/ESA */
+ strcpy(safe, savefile);
+ strcat(safe, "n");
+#endif /* VM */
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove it */
+ fd_kill(safe);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Attempt to save the player */
+ if (save_player_aux(safe))
+ {
+ char temp[1024];
+
+ /* Old savefile */
+ strcpy(temp, savefile);
+ strcat(temp, ".old");
+
+#ifdef VM
+ /* Hack -- support "flat directory" usage on VM/ESA */
+ strcpy(temp, savefile);
+ strcat(temp, "o");
+#endif /* VM */
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove it */
+ fd_kill(temp);
+
+ /* Preserve old savefile */
+ fd_move(savefile, temp);
+
+ /* Activate new savefile */
+ fd_move(safe, savefile);
+
+ /* Remove preserved savefile */
+ fd_kill(temp);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Hack -- Pretend the character was loaded */
+ character_loaded = TRUE;
+
+#ifdef VERIFY_SAVEFILE
+
+ /* Lock on savefile */
+ strcpy(temp, savefile);
+ strcat(temp, ".lok");
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove lock file */
+ fd_kill(temp);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+#endif
+
+ /* Success */
+ result = TRUE;
+ }
+
+ save_savefile_names();
+
+ /* Return the result */
+ return (result);
+}
+
+bool file_exist(char *buf)
+{
+ int fd;
+ bool result;
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open savefile */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* File exists */
+ if (fd >= 0)
+ {
+ fd_close(fd);
+ result = TRUE;
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+/*
+ * Attempt to Load a "savefile"
+ *
+ * On multi-user systems, you may only "read" a savefile if you will be
+ * allowed to "write" it later, this prevents painful situations in which
+ * the player loads a savefile belonging to someone else, and then is not
+ * allowed to save his game when he quits.
+ *
+ * We return "TRUE" if the savefile was usable, and we set the global
+ * flag "character_loaded" if a real, living, character was loaded.
+ *
+ * Note that we always try to load the "current" savefile, even if
+ * there is no such file, so we must check for "empty" savefile names.
+ */
+bool load_player(void)
+{
+ int fd = -1;
+
+ errr err = 0;
+
+#ifdef SAFER_PANICS
+ char panic_fname[1024]; /* Filename for panic savefiles */
+ int testfd = -1;
+#endif /* SAFER_PANICS */
+
+#ifdef VERIFY_TIMESTAMP
+ struct stat statbuf;
+#endif /* VERIFY_TIMESTAMP */
+
+ cptr what = "generic";
+
+#ifdef SAFER_PANICS
+ panicload = FALSE;
+ strncpy(panic_fname, savefile, 1024);
+ strcat(panic_fname, ".pnc"); /* This might concievably cause a buffer
+ overflow, but the rest of the code
+ in this file does likewise. If someone
+ ever audits pernband for security
+ problems, well, don't blame me. The rest
+ of the code was like this before I even
+ got here -- Pat */
+
+#endif /* SAFER_PANICS */
+
+
+ /* Paranoia */
+ turn = 0;
+
+ /* Paranoia */
+ death = FALSE;
+
+
+ /* Allow empty savefile name */
+ if (!savefile[0]) return (TRUE);
+
+
+ /* XXX XXX XXX Fix this */
+
+ /* Verify the existance of the savefile */
+ if ((!file_exist(savefile))
+#ifdef SAFER_PANICS
+ && (!file_exist(panic_fname))
+#endif /* SAFER_PANICS */
+ )
+ {
+ /* Give a message */
+ msg_format("Savefile does not exist: %s", savefile);
+ msg_print(NULL);
+
+ /* Allow this */
+ return (TRUE);
+ }
+
+
+#ifdef VERIFY_SAVEFILE
+
+ /* Verify savefile usage */
+ if (!err)
+ {
+ FILE *fkk;
+
+ char temp[1024];
+
+ /* Extract name of lock file */
+ strcpy(temp, savefile);
+ strcat(temp, ".lok");
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Check for lock */
+ fkk = my_fopen(temp, "r");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Oops, lock exists */
+ if (fkk)
+ {
+ /* Close the file */
+ my_fclose(fkk);
+
+ /* Message */
+ msg_print("Savefile is currently in use.");
+ msg_print(NULL);
+
+ /* Oops */
+ return (FALSE);
+ }
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Create a lock file */
+ fkk = my_fopen(temp, "w");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* Dump a line of info */
+ fprintf(fkk, "Lock file for savefile '%s'\n", savefile);
+
+ /* Close the lock file */
+ my_fclose(fkk);
+ }
+
+#endif
+
+
+ /* Okay */
+ if (!err)
+ {
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open the savefile */
+ fd = fd_open(savefile, O_RDONLY);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* No file */
+ if (fd < 0) err = -1;
+
+ /* Message (below) */
+ if (err) what = "Cannot open savefile";
+ }
+
+#ifdef SAFER_PANICS
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open panic save file */
+ testfd = fd_open(panic_fname, O_RDONLY);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ fd_close(testfd);
+
+ /* A panic save exists, which is not normally the case */
+ if (testfd > 0)
+ {
+ panicload = 1;
+
+ /* Close the normal save file */
+ fd_close(fd);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Prefer panic saves over real saves */
+ fd = fd_open(panic_fname, O_RDONLY);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ /* This is not the error if we're at this pt */
+ what = "";
+ err = 0;
+ }
+
+#endif /* SAFER_PANICS */
+
+ /* Process file */
+ if (!err)
+ {
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+#ifdef VERIFY_TIMESTAMP
+
+ /* Get the timestamp */
+ (void)fstat(fd, &statbuf);
+
+#endif
+
+ /* Open the file XXX XXX XXX XXX Should use Angband file interface */
+ fff = my_fopen(savefile, "rb");
+/* fff = fdopen(fd, "r"); */
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+#ifdef BZ_SAVES
+ bz_prep(LS_LOAD);
+#endif /* BZ_SAVES */
+
+ /* Read the first four bytes */
+ do_u32b(&vernum, LS_LOAD);
+ do_byte(&sf_extra, LS_LOAD);
+
+#ifdef BZ_SAVES
+ bz_done(LS_LOAD);
+#endif /* BZ_SAVES */
+
+ /* XXX XXX XXX XXX Should use Angband file interface */
+ my_fclose(fff);
+ /* fclose(fff) */
+
+ /* Close the file */
+ fd_close(fd);
+ }
+
+ /* Process file */
+ if (!err)
+ {
+
+ /* Extract version */
+ sf_major = VERSION_MAJOR;
+ sf_minor = VERSION_MINOR;
+ sf_patch = VERSION_PATCH;
+ sf_extra = VERSION_EXTRA;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Attempt to load */
+ err = rd_savefile();
+
+ /* Message (below) */
+ if (err) what = "Cannot parse savefile";
+ }
+
+ /* Paranoia */
+ if (!err)
+ {
+ /* Invalid turn */
+ if (!turn) err = -1;
+
+ /* Message (below) */
+ if (err) what = "Broken savefile";
+ }
+
+#ifdef VERIFY_TIMESTAMP
+
+ /* Verify timestamp */
+ if (!err && !arg_wizard)
+ {
+ /* Hack -- Verify the timestamp */
+ if (sf_when > (statbuf.st_ctime + 100) ||
+ sf_when < (statbuf.st_ctime - 100))
+ {
+ /* Message */
+ what = "Invalid timestamp";
+
+ /* Oops */
+ err = -1;
+ }
+ }
+
+#endif
+
+
+ /* Okay */
+ if (!err)
+ {
+ /* Maybe the scripts want to resurrect char */
+ if (process_hooks_ret(HOOK_LOAD_END, "d", "(d)", death))
+ {
+ character_loaded = process_hooks_return[0].num;
+ death = process_hooks_return[1].num;
+ return TRUE;
+ }
+
+ /* Player is dead */
+ if (death)
+ {
+ /* Player is no longer "dead" */
+ death = FALSE;
+
+ /* Cheat death (unless the character retired) */
+ if (arg_wizard && !total_winner)
+ {
+ /* A character was loaded */
+ character_loaded = TRUE;
+
+ /* Done */
+ return (TRUE);
+ }
+
+ /* Count lives */
+ sf_lives++;
+
+ /* Forget turns */
+ turn = old_turn = 0;
+
+ /* Done */
+ return (TRUE);
+ }
+
+ /* A character was loaded */
+ character_loaded = TRUE;
+
+ /* Still alive */
+ if (p_ptr->chp >= 0)
+ {
+ /* Reset cause of death */
+ (void)strcpy(died_from, "(alive and well)");
+ }
+
+#ifdef SAFER_PANICS
+ if (panicload)
+ {
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /*
+ * Done loading, it'll either immediately panic and re-save, or
+ * we don't need the panicsave file anymore. Either way, it's safe
+ * to zap the original panicsave
+ */
+ fd_kill(panic_fname);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+ }
+#endif /* SAFER_PANICS */
+
+ /* Success */
+ return (TRUE);
+ }
+
+
+#ifdef VERIFY_SAVEFILE
+
+ /* Verify savefile usage */
+ if (TRUE)
+ {
+ char temp[1024];
+
+ /* Extract name of lock file */
+ strcpy(temp, savefile);
+ strcat(temp, ".lok");
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Remove lock */
+ fd_kill(temp);
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+ }
+
+#endif
+
+
+ /* Message */
+ msg_format("Error (%s) reading %d.%d.%d savefile.",
+ what, sf_major, sf_minor, sf_patch);
+ msg_print(NULL);
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+/*
+ * Size-aware read/write routines for the savefile, do all their
+ * work through sf_get and sf_put.
+ */
+
+static void do_byte(byte *v, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ *v = sf_get();
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ byte val = *v;
+ sf_put(val);
+ return;
+ }
+ /* We should never reach here, so bail out */
+ printf("FATAL: do_byte passed %d\n", flag);
+ exit(0);
+}
+
+static void do_u16b(u16b *v, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ (*v) = sf_get();
+ (*v) |= ((u16b)(sf_get()) << 8);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ u16b val;
+ val = *v;
+ sf_put((byte)(val & 0xFF));
+ sf_put((byte)((val >> 8) & 0xFF));
+ return;
+ }
+ /* Never should reach here, bail out */
+ printf("FATAL: do_u16b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_s16b(s16b *ip, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ do_u16b((u16b *)ip, flag);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ s16b val;
+ val = *ip;
+ do_u16b((u16b *)ip, flag);
+ return;
+ }
+ /* Blah blah, never should reach here, die */
+ printf("FATAL: do_s16b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_u32b(u32b *ip, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ (*ip) = sf_get();
+ (*ip) |= ((u32b)(sf_get()) << 8);
+ (*ip) |= ((u32b)(sf_get()) << 16);
+ (*ip) |= ((u32b)(sf_get()) << 24);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ u32b val = *ip;
+ sf_put((byte)(val & 0xFF));
+ sf_put((byte)((val >> 8) & 0xFF));
+ sf_put((byte)((val >> 16) & 0xFF));
+ sf_put((byte)((val >> 24) & 0xFF));
+ return;
+ }
+ /* Bad news if you're here, because you're going down */
+ printf("FATAL: do_u32b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_s32b(s32b *ip, int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ do_u32b((u32b *)ip, flag);
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ do_u32b((u32b *)ip, flag);
+ return;
+ }
+ /* Raus! Schnell! */
+ printf("FATAL: do_s32b passed %d\n", flag);
+ exit(0);
+}
+
+static void do_string(char *str, int max, int flag)
+/* Max is ignored for writing */
+{
+ if (flag == LS_LOAD)
+ {
+ int i;
+
+ /* Read the string */
+ for (i = 0; TRUE; i++)
+ {
+ byte tmp8u;
+
+ /* Read a byte */
+ do_byte(&tmp8u, LS_LOAD);
+
+ /* Collect string while legal */
+ if (i < max) str[i] = tmp8u;
+
+ /* End of string */
+ if (!tmp8u) break;
+ }
+ /* Terminate */
+ str[max - 1] = '\0';
+ return;
+ }
+ if (flag == LS_SAVE)
+ {
+ while (*str)
+ {
+ do_byte((byte*)str, flag);
+ str++;
+ }
+ do_byte((byte*)str, flag); /* Output a terminator */
+ return;
+ }
+ printf("FATAL: do_string passed flag %d\n", flag);
+ exit(0);
+}
+
+/*
+ * Periodically, to reduce overhead and code clutter, we'll probably
+ * want to convert all calls to do_ver_??? with direct do_??? calls, and
+ * break the backwards compatibility. How often this needs to happen
+ * remains to be seen, as the rate of accumulation isn't very
+ * predictable. -- Improv
+ */
+static void do_ver_byte(byte *v, u32b version, byte defval, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ *v = defval; /* Use the default value, and DO NOT READ */
+ return;
+ }
+
+ do_byte(v, flag); /* Otherwise, go as normal */
+}
+
+static void skip_ver_byte(u32b version, int flag)
+/* Reads and discards a byte if the savefile is as old as/older than version */
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ byte forget;
+ do_byte(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_u16b(u16b *v, u32b version, u16b defval, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ *v = defval;
+ return;
+ }
+ do_u16b(v, flag);
+}
+
+static void skip_ver_u16b(u32b version, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ u16b forget;
+ do_u16b(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_s16b(s16b *v, u32b version, s16b defval, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ *v = defval;
+ return;
+ }
+ do_s16b(v, flag);
+}
+
+static void skip_ver_s16b(u32b version, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ s16b forget;
+ do_s16b(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_u32b(u32b *v, u32b version, u32b defval, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ *v = defval;
+ return;
+ }
+ do_u32b(v, flag);
+}
+
+static void skip_ver_u32b(u32b version, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ u32b forget;
+ do_u32b(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_s32b(s32b *v, u32b version, s32b defval, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ *v = defval;
+ return;
+ }
+ do_s32b(v, flag);
+}
+
+static void skip_ver_s32b(u32b version, int flag)
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ s32b forget;
+ do_s32b(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_string(char *str, int max, u32b version, char *defval,
+ int flag)
+/* Careful, remember the argument order here */
+{
+ if ((flag == LS_LOAD) && (vernum < version))
+ {
+ strncpy(str, defval, max);
+ str[max - 1] = '\0'; /* Ensure that whatever happens, the result string is term'd */
+ return;
+ }
+ do_string(str, max, flag);
+}
+
+static void skip_ver_string(u32b version, int flag)
+/* This function is slow and bulky */
+{
+ if ((flag == LS_LOAD) && (vernum <= version))
+ {
+ char forget[1000];
+ do_string(forget, 999, flag);
+ }
+ return;
+}
+
+/*
+ * Show information on the screen, one line at a time.
+ *
+ * Avoid the top two lines, to avoid interference with "msg_print()".
+ */
+static void note(cptr msg)
+{
+ static int y = 2;
+
+ /* Draw the message */
+ prt(msg, y, 0);
+
+ /* Advance one line (wrap if needed) */
+ if (++y >= 24) y = 2;
+
+ /* Flush it */
+ Term_fresh();
+}
+
+
+/*
+ * Determine if an item can be wielded/worn (e.g. helmet, sword, bow, arrow)
+ */
+static bool wearable_p(object_type *o_ptr)
+{
+ /* Valid "tval" codes */
+ switch (o_ptr->tval)
+ {
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_SCROLL:
+ case TV_LITE:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_AMULET:
+ case TV_RING:
+ case TV_HYPNOS:
+ case TV_INSTRUMENT:
+ case TV_DAEMON_BOOK:
+ case TV_TRAPKIT:
+ case TV_TOOL:
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * rd/wr an object
+ *
+ * FIXME! This code probably has a lot of cruft from the old Z/V codebase.
+ *
+ */
+static void do_item(object_type *o_ptr, int flag)
+{
+ byte old_dd;
+ byte old_ds;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr;
+
+ /* Kind */
+ do_s16b(&o_ptr->k_idx, flag);
+
+ /* Location */
+ do_byte(&o_ptr->iy, flag);
+ do_byte(&o_ptr->ix, flag);
+
+ /* Type/Subtype */
+ do_byte(&o_ptr->tval, flag);
+ do_byte(&o_ptr->sval, flag);
+
+ /* Special pval */
+ do_s32b(&o_ptr->pval, flag);
+
+ /* Special pval */
+ do_s16b(&o_ptr->pval2, flag);
+
+ /* Special pval */
+ do_s32b(&o_ptr->pval3, flag);
+
+ do_byte(&o_ptr->discount, flag);
+ do_byte(&o_ptr->number, flag);
+ do_s32b(&o_ptr->weight, flag);
+
+ do_byte(&o_ptr->name1, flag);
+ do_s16b(&o_ptr->name2, flag);
+ do_s16b(&o_ptr->name2b, flag);
+ do_s16b(&o_ptr->timeout, flag);
+
+ do_s16b(&o_ptr->to_h, flag);
+ do_s16b(&o_ptr->to_d, flag);
+ do_s16b(&o_ptr->to_a, flag);
+
+ do_s16b(&o_ptr->ac, flag);
+
+ /* We do special processing of this flag when reading */
+ if (flag == LS_LOAD)
+ {
+ do_byte(&old_dd, LS_LOAD);
+ do_byte(&old_ds, LS_LOAD);
+ }
+ if (flag == LS_SAVE)
+ {
+ do_byte(&o_ptr->dd, LS_SAVE);
+ do_byte(&o_ptr->ds, LS_SAVE);
+ }
+
+ do_byte(&o_ptr->ident, flag);
+
+ do_byte(&o_ptr->marked, flag);
+
+ /* flags */
+ do_u32b(&o_ptr->art_flags1, flag);
+ do_u32b(&o_ptr->art_flags2, flag);
+ do_u32b(&o_ptr->art_flags3, flag);
+ do_u32b(&o_ptr->art_flags4, flag);
+ do_u32b(&o_ptr->art_flags5, flag);
+ do_u32b(&o_ptr->art_esp, flag);
+
+ /* obvious flags */
+ do_u32b(&o_ptr->art_oflags1, flag);
+ do_u32b(&o_ptr->art_oflags2, flag);
+ do_u32b(&o_ptr->art_oflags3, flag);
+ do_u32b(&o_ptr->art_oflags4, flag);
+ do_u32b(&o_ptr->art_oflags5, flag);
+ do_u32b(&o_ptr->art_oesp, flag);
+
+ /* Monster holding object */
+ do_s16b(&o_ptr->held_m_idx, flag);
+
+ /* Special powers */
+ do_byte(&o_ptr->xtra1, flag);
+ do_s16b(&o_ptr->xtra2, flag);
+
+ do_byte(&o_ptr->elevel, flag);
+ do_s32b(&o_ptr->exp, flag);
+
+ /* Read the pseudo-id */
+ do_byte(&o_ptr->sense, flag);
+
+ /* Read the found info */
+ do_byte(&o_ptr->found, flag);
+ do_s16b(&o_ptr->found_aux1, flag);
+ do_s16b(&o_ptr->found_aux2, flag);
+ do_s16b(&o_ptr->found_aux3, flag);
+ do_s16b(&o_ptr->found_aux4, flag);
+
+ if (flag == LS_LOAD)
+ {
+ char buf[128];
+ /* Inscription */
+ do_string(buf, 128, LS_LOAD);
+ /* Save the inscription */
+ if (buf[0]) o_ptr->note = quark_add(buf);
+
+ do_string(buf, 128, LS_LOAD);
+ if (buf[0]) o_ptr->art_name = quark_add(buf);
+ }
+ if (flag == LS_SAVE)
+ {
+ /* Save the inscription (if any) */
+ if (o_ptr->note)
+ {
+ do_string((char *)quark_str(o_ptr->note), 0, LS_SAVE);
+ }
+ else
+ {
+ do_string("", 0, LS_SAVE);
+ }
+ if (o_ptr->art_name)
+ {
+ do_string((char *)quark_str(o_ptr->art_name), 0, LS_SAVE);
+ }
+ else
+ {
+ do_string("", 0, LS_SAVE);
+ }
+ }
+
+ if (flag == LS_SAVE) return ; /* Stick any more shared code before this. The rest
+ of this function is reserved for LS_LOAD's
+ cleanup functions */
+ /*********** END OF LS_SAVE ***************/
+
+ /* Obtain the "kind" template */
+ k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Obtain tval/sval from k_info */
+ o_ptr->tval = k_ptr->tval;
+ if (o_ptr->tval != TV_RANDART) o_ptr->sval = k_ptr->sval;
+
+
+ /* Repair non "wearable" items */
+ if (!wearable_p(o_ptr))
+ {
+ /* Acquire correct fields */
+ o_ptr->to_h = k_ptr->to_h;
+ o_ptr->to_d = k_ptr->to_d;
+ o_ptr->to_a = k_ptr->to_a;
+
+ /* Acquire correct fields */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+#if 0
+ /* Acquire correct weight */
+ if ((o_ptr->tval != TV_CORPSE) &&
+ (o_ptr->tval != TV_EGG)) o_ptr->weight = k_ptr->weight;
+
+ /* Paranoia */
+ o_ptr->name1 = o_ptr->name2 = 0;
+#endif
+ /* All done */
+ return;
+ }
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Paranoia */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr;
+
+ /* Obtain the artifact info */
+ a_ptr = &a_info[o_ptr->name1];
+
+ /* Verify that artifact */
+ if (!a_ptr->name) o_ptr->name1 = 0;
+ }
+
+ /* Paranoia */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+
+ /* Obtain the ego-item info */
+ e_ptr = &e_info[o_ptr->name2];
+
+ /* Verify that ego-item */
+ if (!e_ptr->name) o_ptr->name2 = 0;
+ }
+
+
+ /* Acquire standard fields */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* Artifacts */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr;
+
+ /* Obtain the artifact info */
+ a_ptr = &a_info[o_ptr->name1];
+
+ /* Acquire new artifact fields */
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+
+ /* Acquire new artifact weight */
+ o_ptr->weight = a_ptr->weight;
+ }
+
+ /* Ego items */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+
+ /* Obtain the ego-item info */
+ e_ptr = &e_info[o_ptr->name2];
+
+
+ o_ptr->dd = old_dd;
+ o_ptr->ds = old_ds;
+ }
+
+ if (o_ptr->art_name) /* A random artifact */
+ {
+ o_ptr->dd = old_dd;
+ o_ptr->ds = old_ds;
+ }
+}
+
+
+
+
+/*
+ * Read a monster
+ */
+static void do_monster(monster_type *m_ptr, int flag)
+{
+ int i;
+ bool tmp;
+
+ /* Read the monster race */
+ do_s16b(&m_ptr->r_idx, flag);
+
+ do_u16b(&m_ptr->ego, flag);
+
+ /* Read the other information */
+ do_byte(&m_ptr->fy, flag);
+ do_byte(&m_ptr->fx, flag);
+
+ do_s32b(&m_ptr->hp, flag);
+ do_s32b(&m_ptr->maxhp, flag);
+
+ do_s16b(&m_ptr->csleep, flag);
+ do_byte(&m_ptr->mspeed, flag);
+ do_byte(&m_ptr->energy, flag);
+ do_byte(&m_ptr->stunned, flag);
+ do_byte(&m_ptr->confused, flag);
+ do_byte(&m_ptr->monfear, flag);
+ do_u32b(&m_ptr->smart, flag);
+ do_s16b(&m_ptr->status, flag);
+ do_s16b(&m_ptr->possessor, flag);
+ do_byte(&m_ptr->speed, flag);
+ do_byte(&m_ptr->level, flag);
+ do_s16b(&m_ptr->ac, flag);
+ do_u32b(&m_ptr->exp, flag);
+ do_s16b(&m_ptr->target, flag);
+
+ do_s16b(&m_ptr->bleeding, flag);
+ do_s16b(&m_ptr->poisoned, flag);
+
+ do_s32b(&m_ptr->mflag, flag);
+
+ if (flag == LS_LOAD) m_ptr->mflag &= PERM_MFLAG_MASK;
+
+ /* Attacks */
+ for (i = 0; i < 4; i++)
+ {
+ do_byte(&m_ptr->blow[i].method, flag);
+ do_byte(&m_ptr->blow[i].effect, flag);
+ do_byte(&m_ptr->blow[i].d_dice, flag);
+ do_byte(&m_ptr->blow[i].d_side, flag);
+ }
+
+ /* Mind */
+ tmp = (m_ptr->mind) ? TRUE : FALSE;
+ do_byte((byte*)&tmp, flag);
+ if (tmp)
+ {
+ if (flag == LS_LOAD)
+ {
+ MAKE(m_ptr->mind, monster_mind);
+ }
+ }
+
+ /* Special race */
+ tmp = (m_ptr->sr_ptr) ? TRUE : FALSE;
+ do_byte((byte*)&tmp, flag);
+ if (tmp)
+ {
+ if (flag == LS_LOAD)
+ {
+ MAKE(m_ptr->sr_ptr, monster_race);
+ }
+ do_u32b(&m_ptr->sr_ptr->name, flag);
+ do_u32b(&m_ptr->sr_ptr->text, flag);
+
+ do_u16b(&m_ptr->sr_ptr->hdice, flag);
+ do_u16b(&m_ptr->sr_ptr->hside, flag);
+
+ do_s16b(&m_ptr->sr_ptr->ac, flag);
+
+ do_s16b(&m_ptr->sr_ptr->sleep, flag);
+ do_byte(&m_ptr->sr_ptr->aaf, flag);
+ do_byte(&m_ptr->sr_ptr->speed, flag);
+
+ do_s32b(&m_ptr->sr_ptr->mexp, flag);
+
+ do_s32b(&m_ptr->sr_ptr->weight, flag);
+
+ do_byte(&m_ptr->sr_ptr->freq_inate, flag);
+ do_byte(&m_ptr->sr_ptr->freq_spell, flag);
+
+ do_u32b(&m_ptr->sr_ptr->flags1, flag);
+ do_u32b(&m_ptr->sr_ptr->flags2, flag);
+ do_u32b(&m_ptr->sr_ptr->flags3, flag);
+ do_u32b(&m_ptr->sr_ptr->flags4, flag);
+ do_u32b(&m_ptr->sr_ptr->flags5, flag);
+ do_u32b(&m_ptr->sr_ptr->flags6, flag);
+ do_u32b(&m_ptr->sr_ptr->flags7, flag);
+ do_u32b(&m_ptr->sr_ptr->flags8, flag);
+ do_u32b(&m_ptr->sr_ptr->flags9, flag);
+
+ /* Attacks */
+ for (i = 0; i < 4; i++)
+ {
+ do_byte(&m_ptr->sr_ptr->blow[i].method, flag);
+ do_byte(&m_ptr->sr_ptr->blow[i].effect, flag);
+ do_byte(&m_ptr->sr_ptr->blow[i].d_dice, flag);
+ do_byte(&m_ptr->sr_ptr->blow[i].d_side, flag);
+ }
+
+ for (i = 0; i < BODY_MAX; i++)
+ do_byte(&m_ptr->sr_ptr->body_parts[i], flag);
+
+ do_byte(&m_ptr->sr_ptr->level, flag);
+ do_byte(&m_ptr->sr_ptr->rarity, flag);
+
+ do_byte((byte*)&m_ptr->sr_ptr->d_char, flag);
+ do_byte(&m_ptr->sr_ptr->d_attr, flag);
+
+ do_byte((byte*)&m_ptr->sr_ptr->x_char, flag);
+ do_byte(&m_ptr->sr_ptr->x_attr, flag);
+
+ do_s16b(&m_ptr->sr_ptr->max_num, flag);
+ do_byte(&m_ptr->sr_ptr->cur_num, flag);
+ }
+}
+
+
+
+
+
+/*
+ * Handle monster lore
+ */
+static void do_lore(int r_idx, int flag)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Count sights/deaths/kills */
+ do_s16b(&r_ptr->r_sights, flag);
+ do_s16b(&r_ptr->r_deaths, flag);
+ do_s16b(&r_ptr->r_pkills, flag);
+ do_s16b(&r_ptr->r_tkills, flag);
+
+ /* Count wakes and ignores */
+ do_byte(&r_ptr->r_wake, flag);
+ do_byte(&r_ptr->r_ignore, flag);
+
+ /* Extra stuff */
+ do_byte(&r_ptr->r_xtra1, flag);
+ do_byte(&r_ptr->r_xtra2, flag);
+
+ /* Count drops */
+ do_byte(&r_ptr->r_drop_gold, flag);
+ do_byte(&r_ptr->r_drop_item, flag);
+
+ /* Count spells */
+ do_byte(&r_ptr->r_cast_inate, flag);
+ do_byte(&r_ptr->r_cast_spell, flag);
+
+ /* Count blows of each type */
+ do_byte(&r_ptr->r_blows[0], flag);
+ do_byte(&r_ptr->r_blows[1], flag);
+ do_byte(&r_ptr->r_blows[2], flag);
+ do_byte(&r_ptr->r_blows[3], flag);
+
+ /* Memorize flags */
+ do_u32b(&r_ptr->r_flags1, flag); /* Just to remind you */
+ do_u32b(&r_ptr->r_flags2, flag); /* flag is unrelated to */
+ do_u32b(&r_ptr->r_flags3, flag); /* the other argument */
+ do_u32b(&r_ptr->r_flags4, flag);
+ do_u32b(&r_ptr->r_flags5, flag);
+ do_u32b(&r_ptr->r_flags6, flag);
+ do_u32b(&r_ptr->r_flags7, flag);
+ do_u32b(&r_ptr->r_flags8, flag);
+ do_u32b(&r_ptr->r_flags9, flag);
+
+ /* Read the "Racial" monster tmp16b per level */
+ do_s16b(&r_ptr->max_num, flag);
+
+ do_byte((byte*)&r_ptr->on_saved, flag);
+
+ if (flag == LS_LOAD)
+ {
+ /* Lore flag repair? */
+ r_ptr->r_flags1 &= r_ptr->flags1;
+ r_ptr->r_flags2 &= r_ptr->flags2;
+ r_ptr->r_flags3 &= r_ptr->flags3;
+ r_ptr->r_flags4 &= r_ptr->flags4;
+ r_ptr->r_flags5 &= r_ptr->flags5;
+ r_ptr->r_flags6 &= r_ptr->flags6;
+ }
+}
+
+
+
+
+/*
+ * Read a store
+ */
+static bool do_store(store_type *str, int flag)
+/* FIXME! Why does this return anything when
+ it always returns the same thing? */
+{
+ int j;
+
+ byte num;
+
+ byte store_inven_max = STORE_INVEN_MAX;
+
+ /* Some basic info */
+ do_s32b(&str->store_open, flag);
+ do_s16b(&str->insult_cur, flag);
+ do_u16b(&str->owner, flag);
+ if (flag == LS_SAVE) num = str->stock_num;
+
+ /* Could be cleaner, done this way for benefit of the for loop later on */
+ do_byte(&num, flag);
+
+ do_s16b(&str->good_buy, flag);
+ do_s16b(&str->bad_buy, flag);
+
+ /* Last visit */
+ do_s32b(&str->last_visit, flag);
+
+ /* Items */
+ for (j = 0; j < num; j++)
+ {
+ if (flag == LS_LOAD)
+ /* Can't this be cleaner? */
+ {
+ object_type forge;
+ /* Wipe the object */
+ object_wipe(&forge);
+ /* Read the item */
+ do_item(&forge, LS_LOAD);
+ /* Acquire valid items */
+ if ((str->stock_num < store_inven_max) && (str->stock_num < str->stock_size))
+ {
+ int k = str->stock_num++;
+
+ /* Acquire the item */
+ object_copy(&str->stock[k], &forge);
+ }
+ }
+ if (flag == LS_SAVE) do_item(&str->stock[j], flag);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * RNG state
+ */
+static void do_randomizer(int flag)
+{
+ int i;
+
+ u16b tmp16u = 0;
+
+ /* Tmp */
+ do_u16b(&tmp16u, flag);
+
+ /* Place */
+ do_u16b(&Rand_place, flag);
+
+ /* State */
+ for (i = 0; i < RAND_DEG; i++)
+ {
+ do_u32b(&Rand_state[i], flag);
+ }
+
+ /* Accept */
+ if (flag == LS_LOAD)
+ {
+ Rand_quick = FALSE;
+ }
+}
+
+/*
+ * Handle options
+ *
+ * Normal options are stored as a set of 256 bit flags,
+ * plus a set of 256 bit masks to indicate which bit flags were defined
+ * at the time the savefile was created. This will allow new options
+ * to be added, and old options to be removed, at any time, without
+ * hurting old savefiles.
+ *
+ * The window options are stored in the same way, but note that each
+ * window gets 32 options, and their order is fixed by certain defines.
+ */
+static void do_options(int flag)
+{
+ int i, n;
+
+ u32b oflag[8];
+ u32b mask[8];
+
+ /*** Special info */
+
+ /* Read "delay_factor" */
+ do_byte(&delay_factor, flag);
+
+ /* Read "hitpoint_warn" */
+ do_byte(&hitpoint_warn, flag);
+
+ /*** Cheating options ***/
+ if (flag == LS_LOAD) /* There *MUST* be some nice way to unify this! */
+ {
+ u16b c;
+ do_u16b(&c, LS_LOAD);
+ if (c & 0x0002) wizard = TRUE;
+ cheat_peek = (c & 0x0100) ? TRUE : FALSE;
+ cheat_hear = (c & 0x0200) ? TRUE : FALSE;
+ cheat_room = (c & 0x0400) ? TRUE : FALSE;
+ cheat_xtra = (c & 0x0800) ? TRUE : FALSE;
+ cheat_know = (c & 0x1000) ? TRUE : FALSE;
+ cheat_live = (c & 0x2000) ? TRUE : FALSE;
+ }
+ if (flag == LS_SAVE)
+ {
+ u16b c = 0;
+ if (wizard) c |= 0x0002;
+ if (cheat_peek) c |= 0x0100;
+ if (cheat_hear) c |= 0x0200;
+ if (cheat_room) c |= 0x0400;
+ if (cheat_xtra) c |= 0x0800;
+ if (cheat_know) c |= 0x1000;
+ if (cheat_live) c |= 0x2000;
+ do_u16b(&c, LS_SAVE);
+ }
+
+ do_byte((byte*)&autosave_l, flag);
+ do_byte((byte*)&autosave_t, flag);
+ do_s16b(&autosave_freq, flag);
+
+ if (flag == LS_LOAD)
+ {
+ /* Read the option flags */
+ for (n = 0; n < 8; n++) do_u32b(&oflag[n], flag);
+
+ /* Read the option masks */
+ for (n = 0; n < 8; n++) do_u32b(&mask[n], flag);
+
+ /* Analyze the options */
+ for (n = 0; n < 8; n++)
+ {
+ /* Analyze the options */
+ for (i = 0; i < 32; i++)
+ {
+ /* Process valid flags */
+ if (mask[n] & (1L << i))
+ {
+ /* Process valid flags */
+ if (option_mask[n] & (1L << i))
+ {
+ /* Set */
+ if (oflag[n] & (1L << i))
+ {
+ /* Set */
+ option_flag[n] |= (1L << i);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ option_flag[n] &= ~(1L << i);
+ }
+ }
+ }
+ }
+ }
+
+
+ /*** Window Options ***/
+
+ /* Read the window flags */
+ for (n = 0; n < 8; n++) do_u32b(&oflag[n], flag);
+
+ /* Read the window masks */
+ for (n = 0; n < 8; n++) do_u32b(&mask[n], flag);
+
+ /* Analyze the options */
+ for (n = 0; n < 8; n++)
+ {
+ /* Analyze the options */
+ for (i = 0; i < 32; i++)
+ {
+ /* Process valid flags */
+ if (mask[n] & (1L << i))
+ {
+ /* Process valid flags */
+ if (window_mask[n] & (1L << i))
+ {
+ /* Set */
+ if (oflag[n] & (1L << i))
+ {
+ /* Set */
+ window_flag[n] |= (1L << i);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ window_flag[n] &= ~(1L << i);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ /* Analyze the options */
+ for (i = 0; option_info[i].o_desc; i++)
+ {
+ int os = option_info[i].o_page;
+ int ob = option_info[i].o_bit;
+
+ /* Process real entries */
+ if (option_info[i].o_var)
+ {
+ /* Set */
+ if (*option_info[i].o_var)
+ {
+ /* Set */
+ option_flag[os] |= (1L << ob);
+ }
+
+ /* Clear */
+ else
+ {
+ /* Clear */
+ option_flag[os] &= ~(1L << ob);
+ }
+ }
+ }
+
+
+ /*** Normal options ***/
+
+ /* Dump the flags */
+ for (i = 0; i < 8; i++) do_u32b(&option_flag[i], flag);
+
+ /* Dump the masks */
+ for (i = 0; i < 8; i++) do_u32b(&option_mask[i], flag);
+
+ /*** Window options ***/
+
+ /* Dump the flags */
+ for (i = 0; i < 8; i++) do_u32b(&window_flag[i], flag);
+
+ /* Dump the masks */
+ for (i = 0; i < 8; i++) do_u32b(&window_mask[i], flag);
+ }
+}
+
+
+/* Load/Save the random spells info */
+static void do_spells(int i, int flag)
+{
+ random_spell *s_ptr = &random_spells[i];
+ do_string(s_ptr->name, 30, flag);
+ do_string(s_ptr->desc, 30, flag);
+ do_s16b(&s_ptr->mana, flag);
+ do_s16b(&s_ptr->fail, flag);
+ do_u32b(&s_ptr->proj_flags, flag);
+ do_byte(&s_ptr->GF, flag);
+ do_byte(&s_ptr->radius, flag);
+ do_byte(&s_ptr->dam_sides, flag);
+ do_byte(&s_ptr->dam_dice, flag);
+ do_byte(&s_ptr->level, flag);
+ do_byte((byte*)&s_ptr->untried, flag);
+}
+
+
+/*
+ * Handle player inventory
+ *
+ * FIXME! This function probably could be unified better
+ * Note that the inventory is "re-sorted" later by "dungeon()".
+ */
+static bool do_inventory(int flag)
+{
+ if (flag == LS_LOAD)
+ {
+ int slot = 0;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* No items */
+ inven_cnt = 0;
+ equip_cnt = 0;
+
+ /* Read until done */
+ while (1)
+ {
+ u16b n;
+
+ /* Get the next item index */
+ do_u16b(&n, LS_LOAD);
+
+ /* Nope, we reached the end */
+ if (n == 0xFFFF) break;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Read the item */
+ do_item(q_ptr, LS_LOAD);
+
+ /* Hack -- verify item */
+ if (!q_ptr->k_idx) return (FALSE);
+
+ /* Wield equipment */
+ if (n >= INVEN_WIELD)
+ {
+ /* Copy object */
+ object_copy(&p_ptr->inventory[n], q_ptr);
+
+ /* Take care of item sets */
+ if (q_ptr->name1)
+ {
+ wield_set(q_ptr->name1, a_info[q_ptr->name1].set, TRUE);
+ }
+
+ /* One more item */
+ equip_cnt++;
+ }
+
+ /* Warning -- backpack is full */
+ else if (inven_cnt == INVEN_PACK)
+ {
+ /* Oops */
+ note("Too many items in the inventory!");
+
+ /* Fail */
+ return (FALSE);
+ }
+
+ /* Carry inventory */
+ else
+ {
+ /* Get a slot */
+ n = slot++;
+
+ /* Copy object */
+ object_copy(&p_ptr->inventory[n], q_ptr);
+
+ /* One more item */
+ inven_cnt++;
+ }
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ u16b i;
+ u16b sent = 0xFFFF;
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ if (!o_ptr->k_idx) continue;
+ do_u16b(&i, flag);
+ do_item(o_ptr, flag);
+ }
+ do_u16b(&sent, LS_SAVE); /* Sentinel */
+ }
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Read the saved messages
+ */
+static void do_messages(int flag) /* FIXME! We should be able to unify this better */
+{
+ int i;
+ char buf[128];
+ byte color, type;
+
+ s16b num;
+
+ if (flag == LS_SAVE) num = (compress_savefile &&
+ (message_num() > 40)) ? 40 : message_num();
+
+ /* Total */
+ do_s16b(&num, flag);
+
+ /* Read the messages */
+ if (flag == LS_LOAD)
+ {
+ for (i = 0; i < num; i++)
+ {
+ /* Read the message */
+ do_string(buf, 128, LS_LOAD);
+ do_byte(&color, flag);
+ do_byte(&type, flag);
+
+ /* Save the message */
+ message_add(type, buf, color);
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ byte holder;
+ for (i = num - 1; i >= 0; i--)
+ {
+ do_string((char *)message_str((s16b)i), 0, LS_SAVE);
+ holder = message_color((s16b)i);
+ do_byte(&holder, flag);
+ holder = message_type((s16b)i);
+ do_byte(&holder, flag);
+ }
+ }
+}
+
+/*
+ * Handle dungeon
+ *
+ * The monsters/objects must be loaded in the same order
+ * that they were stored, since the actual indexes matter.
+ */
+static bool do_dungeon(int flag, bool no_companions)
+{
+ int i;
+
+ cave_type *c_ptr;
+
+ /* Read specific */
+ u16b tmp16b = 0;
+
+ my_sentinel("Before do_dungeon", 324, flag);
+
+ /* Header info */
+ do_s16b(&dun_level, flag);
+ do_byte(&dungeon_type, flag);
+ do_s16b(&num_repro, flag);
+ do_s16b(&p_ptr->py, flag);
+ do_s16b(&p_ptr->px, flag);
+ do_s16b(&cur_hgt, flag);
+ do_s16b(&cur_wid, flag);
+ do_s16b(&max_panel_rows, flag);
+ do_s16b(&max_panel_cols, flag);
+
+ do_u32b(&dungeon_flags1, flag);
+ do_u32b(&dungeon_flags2, flag);
+
+ /* Last teleportation */
+ do_s16b(&last_teleportation_y, flag);
+ do_s16b(&last_teleportation_y, flag);
+
+ /* Spell effects */
+ tmp16b = MAX_EFFECTS;
+ do_u16b(&tmp16b, flag);
+
+ if ((flag == LS_LOAD) && (tmp16b > MAX_EFFECTS))
+ {
+ quit("Too many spell effects");
+ }
+
+ for (i = 0; i < tmp16b; ++i)
+ {
+ do_s16b(&effects[i].type, flag);
+ do_s16b(&effects[i].dam, flag);
+ do_s16b(&effects[i].time, flag);
+ do_u32b(&effects[i].flags, flag);
+ do_s16b(&effects[i].cx, flag);
+ do_s16b(&effects[i].cy, flag);
+ do_s16b(&effects[i].rad, flag);
+ }
+
+ /* TO prevent bugs with evolving dungeons */
+ for (i = 0; i < 100; i++)
+ {
+ do_s16b(&floor_type[i], flag);
+ do_s16b(&fill_type[i], flag);
+ }
+
+ if ((flag == LS_LOAD) && (!dun_level && !p_ptr->inside_quest))
+ {
+ int xstart = 0;
+ int ystart = 0;
+ /* Init the wilderness */
+ process_dungeon_file(NULL, "w_info.txt", &ystart, &xstart, cur_hgt, cur_wid,
+ TRUE);
+
+ /* Init the town */
+ xstart = 0;
+ ystart = 0;
+ init_flags = 0;
+ process_dungeon_file(NULL, "t_info.txt", &ystart, &xstart, cur_hgt, cur_wid,
+ TRUE);
+ }
+
+ do_grid(flag);
+
+ /*** Objects ***/
+
+ if (flag == LS_SAVE) compact_objects(0);
+ if (flag == LS_SAVE) compact_monsters(0);
+ if (flag == LS_SAVE)
+ {
+ tmp16b = o_max;
+
+ if (no_companions)
+ {
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ if (o_ptr->held_m_idx && (m_list[o_ptr->held_m_idx].status == MSTATUS_COMPANION)) tmp16b--;
+ }
+ }
+
+ /* Item count */
+ do_u16b(&tmp16b, flag);
+
+ tmp16b = o_max;
+ }
+ else
+ /* Read item count */
+ do_u16b(&tmp16b, flag);
+
+ /* Verify maximum */
+ if ((flag == LS_LOAD) && (tmp16b > max_o_idx))
+ {
+ note(format("Too many (%d) object entries!", tmp16b));
+ return (FALSE);
+ }
+
+ /* Dungeon items */
+ for (i = 1; i < tmp16b; i++)
+ {
+ int o_idx;
+
+ object_type *o_ptr;
+
+ if (flag == LS_SAVE)
+ {
+ o_ptr = &o_list[i];
+ /* Don't save objects held by companions when no_companions is set */
+ if (no_companions && o_ptr->held_m_idx && (m_list[o_ptr->held_m_idx].status == MSTATUS_COMPANION)) continue;
+
+ do_item(o_ptr, LS_SAVE);
+ continue; /* Saving is easy */
+ }
+ /* Until the end of the loop, this is all LS_LOAD */
+
+ /* Get a new record */
+ o_idx = o_pop();
+
+ /* Oops */
+ if (i != o_idx)
+ {
+ note(format("Object allocation error (%d <> %d)", i, o_idx));
+ return (FALSE);
+ }
+
+
+ /* Acquire place */
+ o_ptr = &o_list[o_idx];
+
+ /* Read the item */
+ do_item(o_ptr, LS_LOAD);
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Place the object */
+ m_ptr->hold_o_idx = o_idx;
+ }
+
+ /* Dungeon */
+ else
+ {
+ /* Access the item location */
+ c_ptr = &cave[o_ptr->iy][o_ptr->ix];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+ }
+ }
+
+ /*** Monsters ***/
+
+ if (flag == LS_SAVE)
+ {
+ tmp16b = m_max;
+
+ if (no_companions)
+ {
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_COMPANION) tmp16b--;
+ }
+ }
+
+ /* Write the monster count */
+ do_u16b(&tmp16b, flag);
+
+ tmp16b = m_max;
+ }
+ else
+ /* Read the monster count */
+ do_u16b(&tmp16b, flag);
+
+ /* Validate */
+ if ((flag == LS_LOAD) && (tmp16b > max_m_idx))
+ {
+ note(format("Too many (%d) monster entries!", tmp16b));
+ return (FALSE);
+ }
+
+ /* Read the monsters */
+ for (i = 1; i < tmp16b; i++)
+ {
+ int m_idx;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ if (flag == LS_SAVE)
+ {
+ m_ptr = &m_list[i];
+
+ /* Don't save companions when no_companions is set */
+ if (no_companions && m_ptr->status == MSTATUS_COMPANION) continue;
+
+ do_monster(m_ptr, LS_SAVE);
+ continue; /* Easy to save a monster */
+ }
+ /* From here on, it's all LS_LOAD */
+ /* Get a new record */
+ m_idx = m_pop();
+
+ /* Oops */
+ if (i != m_idx)
+ {
+ note(format("Monster allocation error (%d <> %d)", i, m_idx));
+ return (FALSE);
+ }
+
+ /* Acquire monster */
+ m_ptr = &m_list[m_idx];
+
+ /* Read the monster */
+ do_monster(m_ptr, LS_LOAD);
+
+ /* Access grid */
+ c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+
+ /* Mark the location */
+ c_ptr->m_idx = m_idx;
+
+ /* Controlled ? */
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ p_ptr->control = m_idx;
+
+ /* Access race */
+ r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Count XXX XXX XXX */
+ r_ptr->cur_num++;
+ }
+
+ /* Read the kept monsters */
+
+ tmp16b = (flag == LS_SAVE && !no_companions) ? max_m_idx : 0;
+
+ /* Read the monster count */
+ do_u16b(&tmp16b, flag);
+
+ /* Hack -- verify */
+ if ((flag == LS_LOAD) && (tmp16b > max_m_idx))
+ {
+ note(format("Too many (%d) monster entries!", tmp16b));
+ return (FALSE);
+ }
+ for (i = 1; i < tmp16b; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &km_list[i];
+
+ /* Read the monster */
+ do_monster(m_ptr, flag);
+ }
+
+ /*** Success ***/
+
+ /* The dungeon is ready */
+ if (flag == LS_LOAD) character_dungeon = TRUE;
+
+ /* Success */
+ return (TRUE);
+}
+
+/* Returns TRUE if we successfully load the dungeon */
+bool load_dungeon(char *ext)
+{
+ char tmp[16];
+ char name[1024];
+ byte old_dungeon_type = dungeon_type;
+ s16b old_dun = dun_level;
+
+ /* Construct name */
+ sprintf(tmp, "%s.%s", player_base, ext);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open the file */
+ fff = my_fopen(name, "rb");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+ if (fff == NULL)
+ {
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ my_fclose(fff);
+ return (FALSE);
+ }
+
+#ifdef BZ_SAVES
+ bz_prep(LS_LOAD);
+#endif /* BZ_SAVES */
+
+ /* Read the dungeon */
+ if (!do_dungeon(LS_LOAD, FALSE))
+ {
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+#ifdef BZ_SAVES
+ bz_done(LS_LOAD);
+#endif /* BZ_SAVES */
+ my_fclose(fff);
+ return (FALSE);
+ }
+
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ /* Done */
+#ifdef BZ_SAVES
+ bz_done(LS_LOAD);
+#endif /* BZ_SAVES */
+ my_fclose(fff);
+ return (TRUE);
+}
+
+void do_blocks(int flag)
+/* Handle blocked-allocation stuff for quests and lua stuff
+ This depends on a dyn_tosave array of s32b's. Adjust as needed
+ if other data structures are desirable. This also is not hooked
+ in yet. Ideally, plug it near the end of the savefile.
+ */
+{
+ s16b numblks, n_iter = 0; /* How many blocks do we have? */
+ do_s16b(&numblks, flag);
+ while (n_iter < numblks)
+ {
+ /* do_s32b(dyn_tosave[n_iter], flag); */
+ n_iter++;
+ }
+ my_sentinel("In blocked-allocation area", 37, flag);
+}
+
+void do_fate(int i, int flag)
+{
+ if ((flag == LS_LOAD) && (i >= MAX_FATES)) i = MAX_FATES - 1;
+
+ do_byte(&fates[i].fate, flag);
+ do_byte(&fates[i].level, flag);
+ do_byte(&fates[i].serious, flag);
+ do_s16b(&fates[i].o_idx, flag);
+ do_s16b(&fates[i].e_idx, flag);
+ do_s16b(&fates[i].a_idx, flag);
+ do_s16b(&fates[i].v_idx, flag);
+ do_s16b(&fates[i].r_idx, flag);
+ do_s16b(&fates[i].count, flag);
+ do_s16b(&fates[i].time, flag);
+ do_byte((byte*)&fates[i].know, flag);
+}
+
+/*
+ * Actually read the savefile
+ */
+static bool do_savefile_aux(int flag)
+{
+ int i, j;
+
+ byte tmp8u;
+ u16b tmp16u;
+ u32b tmp32u;
+
+ bool *reals;
+ u16b real_max = 0;
+
+ /* Mention the savefile version */
+ if (flag == LS_LOAD)
+ {
+ if (vernum < 100)
+ {
+ note(format("Savefile version %lu too old! ", vernum));
+ return FALSE;
+ }
+ else
+ {
+ note(format("Loading version %lu savefile... ", vernum));
+ }
+ }
+ if (flag == LS_SAVE)
+ {
+ sf_when = time((time_t *) 0); /* Note when file was saved */
+ sf_xtra = 0L; /* What the hell is this? */
+ sf_saves++; /* Increment the saves ctr */
+ }
+
+ /* Handle version bytes. FIXME! DG wants me to change this all around */
+ if (flag == LS_LOAD)
+ {
+ u32b mt32b;
+ byte mtbyte;
+
+ /* Discard all this, we've already read it */
+ do_u32b(&mt32b, flag);
+ do_byte(&mtbyte, flag);
+ }
+ if (flag == LS_SAVE)
+ {
+ u32b saver;
+ saver = SAVEFILE_VERSION;
+ do_u32b(&saver, flag);
+ tmp8u = (byte)rand_int(256);
+ do_byte(&tmp8u, flag); /* 'encryption' */
+ }
+
+ /* Operating system info? Not really. This is just set to 0L */
+ do_u32b(&sf_xtra, flag);
+
+ /* Time of last save */
+ do_u32b(&sf_when, flag);
+
+ /* Number of past lives */
+ do_u16b(&sf_lives, flag);
+
+ /* Number of times saved */
+ do_u16b(&sf_saves, flag);
+
+ /* Game module */
+ if (flag == LS_SAVE)
+ strcpy(loaded_game_module, game_module);
+ do_string(loaded_game_module, 80, flag);
+
+ /* Read RNG state */
+ do_randomizer(flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Randomizer Info");
+
+ /* Automatizer state */
+ do_byte((byte*)&automatizer_enabled, flag);
+
+ /* Then the options */
+ do_options(flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Option Flags");
+
+ /* Then the "messages" */
+ do_messages(flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Messages");
+
+ /* Monster Memory */
+ if (flag == LS_SAVE) tmp16u = max_r_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_r_idx))
+ {
+ note(format("Too many (%u) monster races!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the available records */
+ for (i = 0; i < tmp16u; i++)
+ {
+ monster_race *r_ptr;
+
+ /* Read the lore */
+ do_lore(i, flag);
+
+ /* Access that monster */
+ r_ptr = &r_info[i]; /* FIXME! Why the hell are we doing this? */
+ }
+
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Monster Memory");
+ /* Object Memory */
+ if (flag == LS_SAVE) tmp16u = max_k_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_k_idx))
+ {
+ note(format("Too many (%u) object kinds!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the object memory */
+ for (i = 0; i < tmp16u; i++) do_xtra(i, flag);
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Object Memory");
+ if (flag == LS_LOAD) junkinit();
+
+ {
+ u16b max_towns_ldsv;
+ u16b max_quests_ldsv;
+ if (flag == LS_SAVE) max_towns_ldsv = max_towns;
+ /* Number of towns */
+ do_u16b(&max_towns_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_towns_ldsv > max_towns))
+ {
+ note(format("Too many (%u) towns!", max_towns_ldsv));
+ return (FALSE);
+ }
+ /* Min of random towns */
+ if (flag == LS_SAVE) max_towns_ldsv = TOWN_RANDOM;
+ do_u16b(&max_towns_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_towns_ldsv != TOWN_RANDOM))
+ {
+ note(format("Different random towns base (%u)!", max_towns_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < max_towns; i++)
+ {
+ do_byte((byte*)&town_info[i].destroyed, flag);
+
+ if (i >= TOWN_RANDOM)
+ {
+ do_u32b(&town_info[i].seed, flag);
+ do_byte(&town_info[i].numstores, flag);
+ do_byte(&town_info[i].flags, flag);
+
+ /* If the town is realy used create a sock */
+ if ((town_info[i].flags & (TOWN_REAL)) && (flag == LS_LOAD))
+ {
+ create_stores_stock(i);
+ }
+ }
+ }
+
+ /* Number of dungeon */
+ if (flag == LS_SAVE) max_towns_ldsv = max_d_idx;
+ do_u16b(&max_towns_ldsv, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_towns_ldsv > max_d_idx))
+ {
+ note(format("Too many dungeon types (%u)!", max_towns_ldsv));
+ return (FALSE);
+ }
+
+ /* Number of towns per dungeon */
+ if (flag == LS_SAVE) max_quests_ldsv = TOWN_DUNGEON;
+ do_u16b(&max_quests_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_quests_ldsv > TOWN_DUNGEON))
+ {
+ note(format("Too many town per dungeons (%u)!", max_quests_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < max_towns_ldsv; i++)
+ {
+ for (j = 0; j < max_quests_ldsv; j++)
+ {
+ do_s16b(&(d_info[i].t_idx[j]), flag);
+ do_s16b(&(d_info[i].t_level[j]), flag);
+ }
+ do_s16b(&(d_info[i].t_num), flag);
+ }
+
+ if (flag == LS_SAVE) max_quests_ldsv = MAX_Q_IDX_INIT;
+ /* Number of quests */
+ do_u16b(&max_quests_ldsv, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (max_quests_ldsv > MAX_Q_IDX_INIT))
+ {
+ note(format("Too many (%u) quests!", max_quests_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < max_quests_ldsv; i++)
+ {
+ do_s16b(&quest[i].status, flag);
+ for (j = 0; j < 4; j++)
+ {
+ do_s32b(&(quest[i].data[j]), flag);
+ }
+
+ /* Init the hooks */
+ if ((flag == LS_LOAD) && (quest[i].type == HOOK_TYPE_C)) quest[i].init(i);
+ }
+
+ /* Position in the wilderness */
+ do_s32b(&p_ptr->wilderness_x, flag);
+ do_s32b(&p_ptr->wilderness_y, flag);
+ do_byte((byte*)&p_ptr->wild_mode, flag);
+ do_byte((byte*)&p_ptr->old_wild_mode, flag);
+
+ {
+ s32b wild_x_size, wild_y_size;
+ if (flag == LS_SAVE)
+ {
+ wild_x_size = max_wild_x;
+ wild_y_size = max_wild_y;
+ }
+ /* Size of the wilderness */
+ do_s32b(&wild_x_size, flag);
+ do_s32b(&wild_y_size, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) &&
+ ((wild_x_size > max_wild_x) || (wild_y_size > max_wild_y)))
+ {
+ note(format("Wilderness is too big (%u/%u)!",
+ wild_x_size, wild_y_size));
+ return (FALSE);
+ }
+ /* Wilderness seeds */
+ for (i = 0; i < wild_x_size; i++)
+ {
+ for (j = 0; j < wild_y_size; j++)
+ {
+ do_u32b(&wild_map[j][i].seed, flag);
+ do_u16b(&wild_map[j][i].entrance, flag);
+ do_byte((byte*)&wild_map[j][i].known, flag);
+ }
+ }
+ }
+ }
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Quests");
+
+ /* Load the random artifacts. */
+ if (flag == LS_SAVE) tmp16u = MAX_RANDARTS;
+ do_u16b(&tmp16u, flag);
+ if ((flag == LS_LOAD) && (tmp16u > MAX_RANDARTS))
+ {
+ note(format("Too many (%u) random artifacts!", tmp16u));
+ return (FALSE);
+ }
+ for (i = 0; i < tmp16u; i++)
+ {
+ random_artifact *ra_ptr = &random_artifacts[i];
+
+ do_string(ra_ptr->name_full, 80, flag);
+ do_string(ra_ptr->name_short, 80, flag);
+ do_byte(&ra_ptr->level, flag);
+ do_byte(&ra_ptr->attr, flag);
+ do_u32b(&ra_ptr->cost, flag);
+ do_byte(&ra_ptr->activation, flag);
+ do_byte(&ra_ptr->generated, flag);
+ }
+
+ /* Load the Artifacts */
+ if (flag == LS_SAVE) tmp16u = max_a_idx;
+ do_u16b(&tmp16u, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_a_idx))
+ {
+ note(format("Too many (%u) artifacts!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the artifact flags */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_byte(&(&a_info[i])->cur_num, flag);
+ }
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded Artifacts");
+
+ /* Fates */
+ if (flag == LS_SAVE) tmp16u = MAX_FATES;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > MAX_FATES))
+ {
+ note(format("Too many (%u) fates!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the fate flags */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_fate(i, flag);
+ }
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded Fates");
+
+ /* Load the Traps */
+ if (flag == LS_SAVE) tmp16u = max_t_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > max_t_idx))
+ {
+ note(format("Too many (%u) traps!", tmp16u));
+ return (FALSE);
+ }
+
+ /* fate flags */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_byte((byte*)&t_info[i].ident, flag);
+ }
+ if ((flag == LS_LOAD) && (arg_fiddle)) note("Loaded Traps");
+
+ /* inscription knowledge */
+ if (flag == LS_SAVE) tmp16u = MAX_INSCRIPTIONS;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > MAX_INSCRIPTIONS))
+ {
+ note(format("Too many (%u) inscriptions!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the inscription flag */
+ for (i = 0; i < tmp16u; i++)
+ do_byte((byte*)&inscription_info[i].know, flag);
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded Inscriptions");
+
+
+ /* Read the extra stuff */
+ if (!do_extra(flag))
+ return FALSE;
+ if ((flag == LS_LOAD) && arg_fiddle) note("Loaded extra information");
+
+
+ /* player_hp array */
+ if (flag == LS_SAVE) tmp16u = PY_MAX_LEVEL;
+ do_u16b(&tmp16u, flag);
+ /* Incompatible save files */
+ if ((flag == LS_LOAD) && (tmp16u > PY_MAX_LEVEL))
+ {
+ note(format("Too many (%u) hitpoint entries!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the player_hp array */
+ for (i = 0; i < tmp16u; i++)
+ {
+ do_s16b(&player_hp[i], flag);
+ }
+
+ if (flag == LS_LOAD) morejunk();
+
+ /* Read the pet command settings */
+ do_byte(&p_ptr->pet_follow_distance, flag);
+ do_byte(&p_ptr->pet_open_doors, flag);
+ do_byte(&p_ptr->pet_pickup_items, flag);
+
+ /* Read the inventory */
+ if (!do_inventory(flag) && (flag == LS_LOAD)) /* do NOT reverse this ordering */
+ {
+ note("Unable to read inventory");
+ return (FALSE);
+ }
+
+ /* Note that this forbids max_towns from shrinking, but that is fine */
+ C_MAKE(reals, max_towns, bool);
+
+ /* Find the real towns */
+ if (flag == LS_SAVE)
+ {
+ for (i = 1; i < max_towns; i++)
+ {
+ if (!(town_info[i].flags & (TOWN_REAL))) continue;
+ reals[real_max++] = i;
+ }
+ }
+ do_u16b(&real_max, flag);
+ for (i = 0; i < real_max; i++)
+ {
+ do_byte((byte*)&reals[i], flag);
+ }
+
+ /* Read the stores */
+ if (flag == LS_SAVE) tmp16u = max_st_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Ok now read the real towns */
+ for (i = 0; i < real_max; i++)
+ {
+ int z = reals[i];
+
+ /* Ultra paranoia */
+ if (!town_info[z].stocked) create_stores_stock(z);
+
+ for (j = 0; j < tmp16u; j++)
+ do_store(&town_info[z].store[j], flag);
+ }
+
+ C_FREE(reals, max_towns, bool);
+
+ if (flag == LS_SAVE) tmp32u = extra_savefile_parts;
+ do_u32b(&tmp32u, flag);
+ if (flag == LS_SAVE)
+ {
+ /* Save the stuff */
+ process_hooks(HOOK_SAVE_GAME, "()");
+ }
+
+ if (flag == LS_LOAD)
+ {
+ u32b len = tmp32u;
+
+ while (len)
+ {
+ char key_buf[100];
+
+ /* Load a key */
+ load_number_key(key_buf, &tmp32u);
+
+ /* Process it -- the hooks can use it or ignore it */
+ process_hooks(HOOK_LOAD_GAME, "(s,l)", key_buf, tmp32u);
+ len--;
+ }
+ }
+
+ /* I'm not dead yet... */
+ if (!death)
+ {
+ /* Dead players have no dungeon */
+ if (flag == LS_LOAD) note("Restoring Dungeon...");
+ if ((flag == LS_LOAD) && (!do_dungeon(LS_LOAD, FALSE)))
+ {
+ note("Error reading dungeon data");
+ return (FALSE);
+ }
+ if (flag == LS_SAVE) do_dungeon(LS_SAVE, FALSE);
+ my_sentinel("Before ghost data", 435, flag);
+ my_sentinel("After ghost data", 320, flag);
+ }
+
+ {
+ byte foo = 0;
+ if (flag == LS_SAVE)
+ {
+ /*
+ * Safety Padding. It's there
+ * for a good reason. Trust me on
+ * this. Keep this at the *END*
+ * of the file, and do *NOT* try to
+ * read it. Insert any new stuff before
+ * this position.
+ */
+ do_byte(&foo, LS_SAVE);
+ }
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Actually read the savefile
+ */
+errr rd_savefile(void)
+{
+ errr err = 0;
+
+#ifdef SAFER_PANICS
+ char panic_fname[1024];
+ if (!panicload)
+ {
+#endif /* SAFER_PANICS */
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* The savefile is a binary file */
+ fff = my_fopen(savefile, "rb");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+#ifdef BZ_SAVES
+ bz_prep(LS_LOAD);
+#endif /* BZ_SAVES */
+
+#ifdef SAFER_PANICS
+ }
+ else
+ {
+ strcpy(panic_fname, savefile);
+ strcat(panic_fname, ".pnc");
+
+ /* Grab permission */
+ if (savefile_setuid) safe_setuid_grab();
+
+ /* Open panic save file */
+ fff = my_fopen(panic_fname, "rb");
+
+ /* Drop permission */
+ if (savefile_setuid) safe_setuid_drop();
+
+#ifdef BZ_SAVES
+ if (fff)
+ {
+ bz_prep(LS_LOAD);
+ }
+#endif /* BZ_SAVES */
+
+ }
+
+#endif /* SAFER_PANICS */
+
+ /* Paranoia */
+ if (!fff) return ( -1);
+
+ /* Call the sub-function */
+ err = !do_savefile_aux(LS_LOAD);
+
+ /* Check for errors */
+ if (ferror(fff)) err = -1;
+
+ /* Close the file */
+#ifdef BZ_SAVES
+ bz_done(LS_LOAD);
+#endif /* BZ_SAVES */
+ my_fclose(fff);
+
+ /* Result */
+ return (err);
+}
+
+/*
+ * Note that this function may not be needed at all.
+ * It was taken out of load_player_aux(). Do we need it?
+ */
+static void junkinit(void)
+{
+ int i, j;
+ p_ptr->arena_number = 0;
+ p_ptr->inside_arena = 0;
+ p_ptr->inside_quest = 0;
+ p_ptr->exit_bldg = TRUE;
+ p_ptr->exit_bldg = TRUE;
+ p_ptr->town_num = 1;
+ p_ptr->wilderness_x = 4;
+ p_ptr->wilderness_y = 4;
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ wild_map[j][i].seed = rand_int(0x10000000);
+ }
+ }
+}
+
+static void morejunk(void)
+{
+ sp_ptr = &sex_info[p_ptr->psex]; /* Sex */
+ rp_ptr = &race_info[p_ptr->prace]; /* Raceclass */
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+ cp_ptr = &class_info[p_ptr->pclass];
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+}
+
+#ifdef BZ_SAVES
+static void do_grid(int flag)
+{
+ int y = 0, x = 0;
+ cave_type *c_ptr;
+ int ymax = cur_hgt, xmax = cur_wid;
+
+ int part; /* Which section of the grid we're on */
+
+ my_sentinel("Before grid", 17, flag);
+
+ for (part = 0; part < 9; part++) /* There are 8 fields to the grid, each stored
+ in a seperate data structure */
+ {
+ for (y = 0; y < ymax; y++)
+ {
+ for (x = 0; x < xmax; x++)
+ {
+ c_ptr = &cave[y][x];
+ switch (part)
+ {
+ case 0:
+ do_u16b(&c_ptr->info, flag);
+ break;
+
+ case 1:
+ do_byte(&c_ptr->feat, flag);
+ break;
+
+ case 2:
+ do_byte(&c_ptr->mimic, flag);
+ break;
+
+ case 3:
+ do_u16b(&c_ptr->special, flag);
+ break;
+
+ case 4:
+ do_u16b(&c_ptr->special2, flag);
+ break;
+
+ case 5:
+ do_u16b(&c_ptr->t_idx, flag);
+ break;
+
+ case 6:
+ do_u16b(&c_ptr->inscription, flag);
+ break;
+
+ case 7:
+ do_byte(&c_ptr->mana, flag);
+ break;
+
+ case 8:
+ do_u16b(&c_ptr->effect, 12, 0, flag);
+ break;
+ }
+ }
+ }
+ }
+ my_sentinel("In grid", 36, flag);
+}
+#endif /* BZ_SAVES */
+
+#ifndef BZ_SAVES
+static void do_grid(int flag)
+/* Does the grid, RLE, blahblah. RLE sucks. I hate it. */
+{
+ int i = 0, y = 0, x = 0;
+ byte count = 0;
+ byte tmp8u = 0;
+ s16b tmp16s = 0;
+ cave_type *c_ptr;
+ byte prev_char = 0;
+ s16b prev_s16b = 0;
+ int ymax = cur_hgt, xmax = cur_wid;
+
+ int part; /* Which section of the grid we're on */
+
+ for (part = 0; part < 9; part++) /* There are 8 fields to the grid, each stored
+ in a seperate RLE data structure */
+ {
+ if (flag == LS_SAVE)
+ {
+ count = 0;
+ prev_s16b = 0;
+ prev_char = 0; /* Clear, prepare for RLE */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ c_ptr = &cave[y][x];
+ switch (part)
+ {
+ case 0:
+ tmp16s = c_ptr->info;
+ break;
+
+ case 1:
+ tmp8u = c_ptr->feat;
+ break;
+
+ case 2:
+ tmp8u = c_ptr->mimic;
+ break;
+
+ case 3:
+ tmp16s = c_ptr->special;
+ break;
+
+ case 4:
+ tmp16s = c_ptr->special2;
+ break;
+
+ case 5:
+ tmp16s = c_ptr->t_idx;
+ break;
+
+ case 6:
+ tmp16s = c_ptr->inscription;
+ break;
+
+ case 7:
+ tmp8u = c_ptr->mana;
+ break;
+
+ case 8:
+ tmp16s = c_ptr->effect;
+ break;
+ }
+ /* Flush a full run */
+ if ((((part != 1) && (part != 2) && (part != 7)) &&
+ (tmp16s != prev_s16b)) || (((part == 1) || (part == 2)
+ || (part == 7)) &&
+ (tmp8u != prev_char)) ||
+ (count == MAX_UCHAR))
+ {
+ do_byte(&count, LS_SAVE);
+ switch (part)
+ {
+ case 0:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ do_s16b(&prev_s16b, LS_SAVE);
+ prev_s16b = tmp16s;
+ break;
+
+ case 1:
+ case 2:
+ case 7:
+ do_byte(&prev_char, LS_SAVE);
+ prev_char = tmp8u;
+ break;
+ }
+ count = 1; /* Reset RLE */
+ }
+ else
+ count++; /* Otherwise, keep going */
+ }
+ }
+ /* Fallen off the end of the world, flush anything left */
+ if (count)
+ {
+ do_byte(&count, LS_SAVE);
+ switch (part)
+ {
+ case 0:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ do_s16b(&prev_s16b, LS_SAVE);
+ break;
+
+ case 1:
+ case 2:
+ case 7:
+ do_byte(&prev_char, LS_SAVE);
+ break;
+ }
+ }
+ }
+ if (flag == LS_LOAD)
+ {
+ x = 0;
+ for (y = 0; y < ymax; )
+ {
+ do_byte(&count, LS_LOAD);
+ switch (part)
+ {
+ case 0:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ do_s16b(&tmp16s, LS_LOAD);
+ break;
+
+ case 1:
+ case 2:
+ case 7:
+ do_byte(&tmp8u, LS_LOAD);
+ break;
+ }
+ for (i = count; i > 0; i--) /* RLE */
+ {
+ c_ptr = &cave[y][x];
+ switch (part)
+ {
+ case 0:
+ c_ptr->info = tmp16s;
+ break;
+
+ case 1:
+ c_ptr->feat = tmp8u;
+ break;
+
+ case 2:
+ c_ptr->mimic = tmp8u;
+ break;
+
+ case 3:
+ c_ptr->special = tmp16s;
+ break;
+
+ case 4:
+ c_ptr->special2 = tmp16s;
+ break;
+
+ case 5:
+ c_ptr->t_idx = tmp16s;
+ break;
+
+ case 6:
+ c_ptr->inscription = tmp16s;
+ break;
+
+ case 7:
+ c_ptr->mana = tmp8u;
+ break;
+
+ case 8:
+ c_ptr->effect = tmp16s;
+ break;
+ }
+ if (++x >= xmax)
+ {
+ /* Wrap */
+ x = 0;
+ if ((++y) >= ymax) break;
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif /* !BZ_SAVES */
+
+static void my_sentinel(char *place, u16b value, int flag)
+/* This function lets us know exactly where a savefile is
+ broken by reading/writing conveniently a sentinel at this
+ spot */
+{
+ if (flag == LS_SAVE)
+ {
+ do_u16b(&value, flag);
+ return;
+ }
+ if (flag == LS_LOAD)
+ {
+ u16b found;
+ do_u16b(&found, flag);
+ if (found == value) /* All is good */
+ return;
+ /* All is bad */
+ note(format("Savefile broken %s", place));
+ return;
+ }
+ note(format("Impossible has occurred")); /* Programmer error */
+ exit(0);
+}
+
+/********** Variable savefile stuff **************/
+
+/*
+ * Add num slots to the savefile
+ */
+void register_savefile(int num)
+{
+ extra_savefile_parts += (num > 0) ? num : 0;
+}
+
+void save_number_key(char *key, u32b val)
+{
+ byte len = strlen(key);
+
+ do_byte(&len, LS_SAVE);
+ while (*key)
+ {
+ do_byte((byte*)key, LS_SAVE);
+ key++;
+ }
+ do_u32b(&val, LS_SAVE);
+}
+
+void load_number_key(char *key, u32b *val)
+{
+ byte len, i = 0;
+
+ do_byte(&len, LS_LOAD);
+ while (i < len)
+ {
+ do_byte((byte*)&key[i], LS_LOAD);
+ i++;
+ }
+ key[i] = '\0';
+ do_u32b(val, LS_LOAD);
+}
diff --git a/src/lua/array.lua b/src/lua/array.lua
new file mode 100644
index 00000000..7929f8cd
--- /dev/null
+++ b/src/lua/array.lua
@@ -0,0 +1,203 @@
+-- tolua: array class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1999
+-- $Id: array.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Array class
+-- Represents a extern array variable or a public member of a class.
+-- Stores all fields present in a declaration.
+classArray = {
+ _base = classDeclaration,
+}
+
+settag(classArray,tolua_tag)
+
+-- Print method
+function classArray:print (ident,close)
+ print(ident.."Array{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." def = '"..self.def.."',")
+ print(ident.." dim = '"..self.dim.."',")
+ print(ident.." ret = '"..self.ret.."',")
+ print(ident.."}"..close)
+end
+
+-- get variable value
+function classArray:getvalue (class,static)
+ if class and static then
+ return class..'::'..self.name..'[toluaI_index]'
+ elseif class then
+ return 'self->'..self.name..'[toluaI_index]'
+ else
+ return self.name..'[toluaI_index]'
+ end
+end
+
+-- Write binding functions
+function classArray:supcode ()
+ local class = self:inclass()
+
+ -- get function ------------------------------------------------
+ if class then
+ output("/* get function:",self.name," of class ",class," */")
+ else
+ output("/* get function:",self.name," */")
+ end
+ self.cgetname = self:cfuncname("toluaI_get")
+ output("static int",self.cgetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare index
+ output(' int toluaI_index;')
+
+ -- declare self, if the case
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+ if class and static==nil then
+ output(' ',class,'*','self;')
+ output(' lua_pushstring(tolua_S,".self");')
+ output(' lua_rawget(tolua_S,1);')
+ output(' self = ')
+ output('(',class,'*) ')
+ output('lua_touserdata(tolua_S,-1);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+
+ -- check index
+ output(' if (!tolua_istype(tolua_S,2,LUA_TNUMBER,0))')
+ output(' tolua_error(tolua_S,"invalid type in array indexing.");')
+ output(' toluaI_index = (int)tolua_getnumber(tolua_S,2,0)-1;')
+ output(' if (toluaI_index<0 || toluaI_index>='..self.dim..')')
+ output(' tolua_error(tolua_S,"array indexing out of range.");')
+
+ -- return value
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static)..');')
+ else
+ if self.ptr == '&' or self.ptr == '' then
+ output(' tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static)..',',self.tag,');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static)..',',self.tag,');')
+ end
+ end
+ output(' return 1;')
+ output('}')
+ output('\n')
+
+ -- set function ------------------------------------------------
+ if not strfind(self.mod,'const') then
+ if class then
+ output("/* set function:",self.name," of class ",class," */")
+ else
+ output("/* set function:",self.name," */")
+ end
+ self.csetname = self:cfuncname("toluaI_set")
+ output("static int",self.csetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare index
+ output(' int toluaI_index;')
+
+ -- declare self, if the case
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+ if class and static==nil then
+ output(' ',class,'*','self;')
+ output(' lua_pushstring(tolua_S,".self");')
+ output(' lua_rawget(tolua_S,1);')
+ output(' self = ')
+ output('(',class,'*) ')
+ output('lua_touserdata(tolua_S,-1);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+
+ -- check index
+ output(' if (!tolua_istype(tolua_S,2,LUA_TNUMBER,0))')
+ output(' tolua_error(tolua_S,"invalid type in array indexing.");')
+ output(' toluaI_index = (int)tolua_getnumber(tolua_S,2,0)-1;')
+ output(' if (toluaI_index<0 || toluaI_index>='..self.dim..')')
+ output(' tolua_error(tolua_S,"array indexing out of range.");')
+
+ -- assign value
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(' ')
+ if class and static then
+ output(class..'::'..self.name..'[toluaI_index]')
+ elseif class then
+ output('self->'..self.name..'[toluaI_index]')
+ else
+ output(self.name..'[toluaI_index]')
+ end
+ local t = isbasic(self.type)
+ output(' = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_get'..t,'(tolua_S,3,',def,'));')
+ else
+ output('tolua_getusertype(tolua_S,3,',def,'));')
+ end
+ output(' return 0;')
+ output('}')
+ output('\n')
+ end
+
+end
+
+function classArray:register ()
+ local parent = self:inclass() or self:inmodule()
+ if parent then
+ if self.csetname then
+ output(' tolua_tablearray(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_tablearray(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ else
+ if self.csetname then
+ output(' tolua_globalarray(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_globalarray(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ end
+end
+
+function classArray:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");')
+ end
+end
+
+
+-- Internal constructor
+function _Array (t)
+ t._base = classArray
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the variable declaration.
+function Array (s)
+ return _Array (Declaration(s,'var'))
+end
+
+
diff --git a/src/lua/basic.lua b/src/lua/basic.lua
new file mode 100644
index 00000000..2bac463f
--- /dev/null
+++ b/src/lua/basic.lua
@@ -0,0 +1,190 @@
+-- tolua: basic utility functions
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: basic.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Basic C types and their corresponding Lua types
+-- All occurrences of "char*" will be replaced by "_cstring",
+-- and all occurrences of "void*" will be replaced by "_userdata"
+_basic = {
+ ['void'] = '',
+ ['char'] = 'number',
+ ['int'] = 'number',
+ ['short'] = 'number',
+ ['long'] = 'number',
+ ['_cstring'] = 'string',
+ ['_userdata'] = 'userdata',
+ ['char*'] = 'string',
+ ['void*'] = 'userdata',
+ ['bool'] = 'bool',
+ ['LUA_VALUE'] = 'value',
+ ['byte'] = 'number',
+ ['s16b'] = 'number',
+ ['u16b'] = 'number',
+ ['s32b'] = 'number',
+ ['u32b'] = 'number',
+}
+
+_basic_tag = {
+ ['void'] = '',
+ ['char'] = 'LUA_TNUMBER',
+ ['int'] = 'LUA_TNUMBER',
+ ['short'] = 'LUA_TNUMBER',
+ ['long'] = 'LUA_TNUMBER',
+ ['_cstring'] = 'LUA_TSTRING',
+ ['_userdata'] = 'LUA_TUSERDATA',
+ ['char*'] = 'LUA_TSTRING',
+ ['void*'] = 'LUA_TUSERDATA',
+ ['bool'] = 'tolua_tag(tolua_S,"bool")',
+ ['byte'] = 'LUA_TNUMBER',
+ ['s16b'] = 'LUA_TNUMBER',
+ ['u16b'] = 'LUA_TNUMBER',
+ ['s32b'] = 'LUA_TNUMBER',
+ ['u32b'] = 'LUA_TNUMBER',
+}
+
+_basic_ctype = {
+ number = "long",
+ string = "const char*",
+ userdata = "void*",
+ bool = "int",
+}
+
+-- List of user defined types
+-- Each type corresponds to a variable name that stores its tag value.
+_usertype = {}
+
+-- Tag method to provide inheritance
+function tolua_index (t,f)
+ if f == '_base' then -- to avoid loop
+ return tolua_old_index(t,f)
+ else
+ return t._base[f]
+ end
+end
+
+tolua_tag = newtag()
+tolua_old_index = settagmethod(tolua_tag,"index",tolua_index)
+
+-- Error handler
+function tolua_error (s)
+ local out = _OUTPUT
+ _OUTPUT = _STDERR
+ if strsub(s,1,1) == '#' then
+ write("\n** tolua: "..strsub(s,2)..".\n\n")
+ else
+ write("\n** tolua internal error: "..s..".\n\n")
+ return
+ end
+
+ if _curr_code then
+ local _,_,s = strfind(_curr_code,"^%s*(.-\n)") -- extract first line
+ if s==nil then s = _curr_code end
+ s = gsub(s,"_userdata","void*") -- return with 'void*'
+ s = gsub(s,"_cstring","char*") -- return with 'char*'
+ write("Code being processed:\n"..s.."\n")
+ end
+ _OUTPUT = out
+end
+
+
+_ERRORMESSAGE = tolua_error
+
+-- register an user defined type
+function regtype (t)
+ if not istype(t) then
+ _usertype[t] = t
+ end
+ return t
+end
+
+-- return tag name
+function tagvar(type,const)
+ if type == '' or type == 'void' then
+ return type,0
+ else
+ local m,t = findtypedef(type)
+ if isbasic(t) then
+ return t, _basic_tag[t]
+ end
+ if strfind(m,'const') then const = 'const' end
+ regtype(t)
+ if const and const ~= '' then
+ t = 'const '..t
+ end
+ return t,'tolua_tag(tolua_S,"'..t..'")'
+ end
+end
+
+-- check if basic type
+function isbasic (type)
+ local m,t = findtypedef(type)
+ local b = _basic[t]
+ if b then
+ return b,_basic_ctype[b]
+ end
+ return nil
+end
+
+-- check if type
+function istype (t)
+ return _basic[t] or _usertype[t] or istypedef(t)
+end
+
+
+-- split string using a token
+function split (s,t)
+ local l = {n=0}
+ local f = function (s)
+ %l.n = %l.n + 1
+ %l[%l.n] = s
+ end
+ local p = "%s*(.-)%s*"..t.."%s*"
+ s = gsub(s,"^%s+","")
+ s = gsub(s,"%s+$","")
+ s = gsub(s,p,f)
+ l.n = l.n + 1
+ l[l.n] = gsub(s,"(%s%s*)$","")
+ return l
+end
+
+
+-- concatenate strings of a table
+function concat (t,f,l)
+ local s = ''
+ local i=f
+ while i<=l do
+ s = s..t[i]
+ i = i+1
+ if i <= l then s = s..' ' end
+ end
+ return s
+end
+
+-- output line
+function output (...)
+ local i=1
+ while i<=arg.n do
+ if _cont and not strfind(_cont,'[%(,"]') and
+ strfind(arg[i],"^[%a_~]") then
+ write(' ')
+ end
+ write(arg[i])
+ if arg[i] ~= '' then
+ _cont = strsub(arg[i],-1,-1)
+ end
+ i = i+1
+ end
+ if strfind(arg[arg.n],"[%/%)%;%{%}]$") then
+ _cont=nil write('\n')
+ end
+end
+
+
diff --git a/src/lua/class.lua b/src/lua/class.lua
new file mode 100644
index 00000000..01385178
--- /dev/null
+++ b/src/lua/class.lua
@@ -0,0 +1,85 @@
+-- tolua: class class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: class.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Class class
+-- Represents a class definition.
+-- Stores the following fields:
+-- name = class name
+-- base = class base, if any (only single inheritance is supported)
+-- {i} = list of members
+classClass = {
+ _base = classContainer,
+ type = 'class',
+ name = '',
+ base = '',
+}
+settag(classClass,tolua_tag)
+
+
+-- register class
+function classClass:register ()
+ output(' tolua_cclass(tolua_S,"'..self.name..'","'..self.base..'");')
+ local i=1
+ while self[i] do
+ self[i]:register()
+ i = i+1
+ end
+end
+
+-- unregister class
+function classClass:unregister ()
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.name..'");')
+end
+
+-- output tags
+function classClass:decltag ()
+ self.itype,self.tag = tagvar(self.name);
+ self.citype,self.ctag = tagvar(self.name,'const');
+ local i=1
+ while self[i] do
+ self[i]:decltag()
+ i = i+1
+ end
+end
+
+
+-- Print method
+function classClass:print (ident,close)
+ print(ident.."Class{")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." base = '"..self.base.."';")
+ local i=1
+ while self[i] do
+ self[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Class (t)
+ t._base = classClass
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects the name, the base and the body of the class.
+function Class (n,p,b)
+ local c = _Class(_Container{name=n, base=p})
+ push(c)
+ c:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces
+ pop()
+end
+
+
diff --git a/src/lua/clean.lua b/src/lua/clean.lua
new file mode 100644
index 00000000..ba08d534
--- /dev/null
+++ b/src/lua/clean.lua
@@ -0,0 +1,74 @@
+-- mark up comments and strings
+STR1 = "\001"
+STR2 = "\002"
+STR3 = "\003"
+STR4 = "\004"
+REM = "\005"
+ANY = "([\001-\005])"
+ESC1 = "\006"
+ESC2 = "\007"
+
+MASK = { -- the substitution order is important
+ {ESC1, "\\'"},
+ {ESC2, '\\"'},
+ {STR1, "'"},
+ {STR2, '"'},
+ {STR3, "%[%["},
+ {STR4, "%]%]"},
+ {REM , "%-%-"},
+}
+
+function mask (s)
+ for i = 1,getn(MASK) do
+ s = gsub(s,MASK[i][2],MASK[i][1])
+ end
+ return s
+end
+
+function unmask (s)
+ for i = 1,getn(MASK) do
+ s = gsub(s,MASK[i][1],MASK[i][2])
+ end
+ return s
+end
+
+function clean (s)
+ -- check for compilation error
+ local code = "return function () " .. s .. " end"
+ if not dostring(code) then
+ return nil
+ end
+
+ local S = "" -- saved string
+
+ s = mask(s)
+
+ -- remove blanks and comments
+ while 1 do
+ local b,e,d = strfind(s,ANY)
+ if b then
+ S = S..strsub(s,1,b-1)
+ s = strsub(s,b+1)
+ if d==STR1 or d==STR2 then
+ e = strfind(s,d)
+ S = S ..d..strsub(s,1,e)
+ s = strsub(s,e+1)
+ elseif d==STR3 then
+ e = strfind(s,STR4)
+ S = S..d..strsub(s,1,e)
+ s = strsub(s,e+1)
+ elseif d==REM then
+ s = gsub(s,"[^\n]*(\n?)","%1",1)
+ end
+ else
+ S = S..s
+ break
+ end
+ end
+ -- eliminate unecessary spaces
+ S = gsub(S,"[ \t]+"," ")
+ S = gsub(S,"[ \t]*\n[ \t]*","\n")
+ S = unmask(S)
+ return S
+end
+
diff --git a/src/lua/code.lua b/src/lua/code.lua
new file mode 100644
index 00000000..08f38ad2
--- /dev/null
+++ b/src/lua/code.lua
@@ -0,0 +1,73 @@
+-- tolua: code class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1999
+-- $Id: code.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Code class
+-- Represents Lua code to be compiled and included
+-- in the initialization function.
+-- The following fields are stored:
+-- text = text code
+classCode = {
+ text = '',
+ _base = classFeature,
+}
+settag(classCode,tolua_tag)
+
+-- register code
+function classCode:register ()
+ -- clean Lua code
+ local s = clean(self.text)
+ if not s then
+ error("parser error in embedded code")
+ end
+
+ -- convert to C
+ output('\n { /* begin embedded lua code */\n')
+ output(' static unsigned char B[] = {\n ')
+ local t={n=0}
+ local b = gsub(s,'(.)',function (c)
+ local e = ''
+ %t.n=%t.n+1 if %t.n==15 then %t.n=0 e='\n ' end
+ return format('%3u,%s',strbyte(c),e)
+ end
+ )
+ output(b..strbyte(" "))
+ output('\n };\n')
+ output(' lua_dobuffer(tolua_S,(char*)B,sizeof(B),"tolua: embedded Lua code");')
+ output(' } /* end of embedded lua code */\n\n')
+end
+
+
+-- Print method
+function classCode:print (ident,close)
+ print(ident.."Code{")
+ print(ident.." text = [["..self.text.."]],")
+ print(ident.."}"..close)
+end
+
+
+-- Internal constructor
+function _Code (t)
+ t._base = classCode
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the code text
+function Code (l)
+ return _Code {
+ text = l
+ }
+end
+
+
diff --git a/src/lua/container.lua b/src/lua/container.lua
new file mode 100644
index 00000000..cbbf11c1
--- /dev/null
+++ b/src/lua/container.lua
@@ -0,0 +1,311 @@
+-- tolua: container abstract class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: container.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Container class
+-- Represents a container of features to be bound
+-- to lua.
+classContainer =
+{
+ curr = nil,
+ _base = classFeature,
+}
+settag(classContainer,tolua_tag)
+
+-- output tags
+function classContainer:decltag ()
+ push(self)
+ local i=1
+ while self[i] do
+ self[i]:decltag()
+ i = i+1
+ end
+ pop()
+end
+
+
+-- write support code
+function classContainer:supcode ()
+ push(self)
+ local i=1
+ while self[i] do
+ self[i]:supcode()
+ i = i+1
+ end
+ pop()
+end
+
+
+-- Internal container constructor
+function _Container (self)
+ self._base = classContainer
+ settag(self,tolua_tag)
+ self.n = 0
+ self.typedefs = {n=0}
+ self.lnames = {}
+ return self
+end
+
+-- push container
+function push (t)
+ classContainer.curr = t
+end
+
+-- pop container
+function pop ()
+ classContainer.curr = classContainer.curr.parent
+end
+
+-- append to current container
+function append (t)
+ return classContainer.curr:append(t)
+end
+
+-- append typedef to current container
+function appendtypedef (t)
+ return classContainer.curr:appendtypedef(t)
+end
+
+-- substitute typedef
+function findtypedef (type)
+ return classContainer.curr:findtypedef(type)
+end
+
+-- check if is typedef
+function istypedef (type)
+ return classContainer.curr:istypedef(type)
+end
+
+-- append feature to container
+function classContainer:append (t)
+ self.n = self.n + 1
+ self[self.n] = t
+ t.parent = self
+end
+
+-- append typedef
+function classContainer:appendtypedef (t)
+ self.typedefs.n = self.typedefs.n + 1
+ self.typedefs[self.typedefs.n] = t
+end
+
+-- determine lua function name overload
+function classContainer:overload (lname)
+ if not self.lnames[lname] then
+ self.lnames[lname] = 0
+ else
+ self.lnames[lname] = self.lnames[lname] + 1
+ end
+ return format("%02d",self.lnames[lname])
+end
+
+function classContainer:findtypedef (type)
+ local env = self
+ while env do
+ if env.typedefs then
+ local i=1
+ while env.typedefs[i] do
+ if env.typedefs[i].utype == type then
+ local mod1,type1 = env.typedefs[i].mod,env.typedefs[i].type
+ local mod2,type2 = findtypedef(type1)
+ return mod2..' '..mod1,type2
+ end
+ i = i+1
+ end
+ end
+ env = env.parent
+ end
+ return '',type
+end
+
+function classContainer:istypedef (type)
+ local env = self
+ while env do
+ if env.typedefs then
+ local i=1
+ while env.typedefs[i] do
+ if env.typedefs[i].utype == type then
+ return 1
+ end
+ i = i+1
+ end
+ end
+ env = env.parent
+ end
+ return nil
+end
+
+-- parse chunk
+function classContainer:doparse (s)
+
+ -- try module
+ do
+ local b,e,name,body = strfind(s,"^%s*module%s%s*([_%w][_%w]*)%s*(%b{})%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Module(name,body)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try define
+ do
+ local b,e,name = strfind(s,"^%s*#define%s%s*([^%s]*)[^\n]*\n%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Define(name)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try enumerates
+ do
+ local b,e,body = strfind(s,"^%s*enum[^{]*(%b{})%s*;?%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Enumerate(body)
+ return strsub(s,e+1)
+ end
+ end
+
+ do
+ local b,e,body,name = strfind(s,"^%s*typedef%s%s*enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Enumerate(body)
+ Typedef("int "..name)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try operator
+ do
+ local b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&]*operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Operator(decl,kind,arg,const)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try function
+ do
+ local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*")
+ if not b then
+ -- try a single letter function name
+ b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*")
+ end
+ if b then
+ _curr_code = strsub(s,b,e)
+ Function(decl,arg,const)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try inline function
+ do
+ local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*%b{}%s*")
+ if not b then
+ -- try a single letter function name
+ b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*%b{}%s*")
+ end
+ if b then
+ _curr_code = strsub(s,b,e)
+ Function(decl,arg,const)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try class
+ do
+ local b,e,name,base,body = strfind(s,"^%s*class%s*([_%w][_%w]*)%s*(.-)%s*(%b{})%s*;%s*")
+ if not b then
+ b,e,name,base,body = strfind(s,"^%s*struct%s*([_%w][_%w]*)%s*(.-)%s*(%b{})%s*;%s*")
+ if not b then
+ base = ''
+ b,e,body,name = strfind(s,"^%s*typedef%s%s*struct%s%s*[_%w]*%s*(%b{})%s*([_%w][_%w]*)%s*;%s*")
+ end
+ end
+ if b then
+ if base ~= '' then
+ local b,e
+ b,e,base = strfind(base,".-([_%w][_%w]*)$")
+ end
+ _curr_code = strsub(s,b,e)
+ Class(name,base,body)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try typedef
+ do
+ local b,e,types = strfind(s,"^%s*typedef%s%s*(.-)%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Typedef(types)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try variable
+ do
+ local b,e,decl = strfind(s,"^%s*([_%w][_@%s%w%d%*&]*[_%w%d])%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Variable(decl)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try array
+ do
+ local b,e,decl = strfind(s,"^%s*([_%w][][_@%s%w%d%*&%-%>]*[]_%w%d])%s*;%s*")
+ if b then
+ _curr_code = strsub(s,b,e)
+ Array(decl)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try code
+ do
+ local b,e,code = strfind(s,"^%s*(%b\1\2)")
+ if b then
+ Code(strsub(code,2,-2))
+ return strsub(s,e+1)
+ end
+ end
+
+ -- try verbatim
+ do
+ local b,e,line = strfind(s,"^%s*%$(.-\n)")
+ if b then
+ Verbatim(line)
+ return strsub(s,e+1)
+ end
+ end
+
+ -- no matching
+ if gsub(s,"%s%s*","") ~= "" then
+ _curr_code = s
+ error("#parse error")
+ else
+ return ""
+ end
+end
+
+function classContainer:parse (s)
+ while s ~= '' do
+ s = self:doparse(s)
+ end
+end
+
+
diff --git a/src/lua/declaration.lua b/src/lua/declaration.lua
new file mode 100644
index 00000000..e4d5c688
--- /dev/null
+++ b/src/lua/declaration.lua
@@ -0,0 +1,399 @@
+-- tolua: declaration class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: declaration.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Declaration class
+-- Represents variable, function, or argument declaration.
+-- Stores the following fields:
+-- mod = type modifiers
+-- type = type
+-- ptr = "*" or "&", if representing a pointer or a reference
+-- name = name
+-- dim = dimension, if a vector
+-- def = default value, if any (only for arguments)
+-- ret = "*" or "&", if value is to be returned (only for arguments)
+classDeclaration = {
+ _base = classFeature,
+ mod = '',
+ type = '',
+ ptr = '',
+ name = '',
+ dim = '',
+ ret = '',
+ def = ''
+}
+settag(classDeclaration,tolua_tag)
+
+-- Create an unique variable name
+function create_varname ()
+ if not _varnumber then _varnumber = 0 end
+ _varnumber = _varnumber + 1
+ return "tolua_var_".._varnumber
+end
+
+-- Check declaration name
+-- It also identifies default values
+function classDeclaration:checkname ()
+
+ if strsub(self.name,1,1) == '[' and not istype(self.type) then
+ self.name = self.type..self.name
+ local m = split(self.mod,'%s%s*')
+ self.type = m[m.n]
+ self.mod = concat(m,1,m.n-1)
+ end
+
+ local t = split(self.name,'=')
+ if t.n==2 then
+ self.name = t[1]
+ self.def = t[t.n]
+ end
+
+ local b,e,d = strfind(self.name,"%[(.-)%]")
+ if b then
+ self.name = strsub(self.name,1,b-1)
+ self.dim = d
+ end
+
+
+ if self.type ~= '' and self.type ~= 'void' and self.name == '' then
+ self.name = create_varname()
+ elseif self.kind=='var' then
+ if self.type=='' and self.name~='' then
+ self.type = self.type..self.name
+ self.name = create_varname()
+ elseif istype(self.name) then
+ if self.type=='' then self.type = self.name
+ else self.type = self.type..' '..self.name end
+ self.name = create_varname()
+ end
+ end
+
+end
+
+-- Check declaration type
+-- Substitutes typedef's.
+function classDeclaration:checktype ()
+
+ -- check if there is a pointer to basic type
+ if isbasic(self.type) and self.ptr~='' then
+ self.ret = self.ptr
+ self.ptr = nil
+ end
+
+ -- check if there is array to be returned
+ if self.dim~='' and self.ret~='' then
+ error('#invalid parameter: cannot return an array of values')
+ end
+
+ -- register type
+ if self.type~='' then
+ regtype(self.type)
+ end
+
+ -- restore 'void*' and 'string*'
+ if self.type == '_userdata' then self.type = 'void*'
+ elseif self.type == '_cstring' then self.type = 'char*'
+ end
+
+--
+-- -- if returning value, automatically set default value
+-- if self.ret ~= '' and self.def == '' then
+-- self.def = '0'
+-- end
+--
+
+end
+
+-- Print method
+function classDeclaration:print (ident,close)
+ print(ident.."Declaration{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." dim = '"..self.dim.."',")
+ print(ident.." def = '"..self.def.."',")
+ print(ident.." ret = '"..self.ret.."',")
+ print(ident.."}"..close)
+end
+
+-- declare tag
+function classDeclaration:decltag ()
+ self.itype, self.tag = tagvar(self.type,strfind(self.mod,'const'))
+end
+
+
+-- output type checking
+function classDeclaration:outchecktype (narg)
+ local tag, def
+ if self.dim ~= '' then
+ tag = 'LUA_TTABLE'
+ def = 0
+ else
+ tag = self.tag
+ def = self.def~='' or 0
+ end
+ return 'tolua_istype(tolua_S,'..narg..','..tag..','..def..')'
+end
+
+-- Declare variable
+function classDeclaration:declare (narg)
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(" ",self.mod,self.type,ptr)
+ if self.dim ~= '' and tonumber(self.dim)==nil then
+ output('*')
+ end
+ output(self.name)
+ if self.dim ~= '' then
+ if tonumber(self.dim)~=nil then
+ output('[',self.dim,'];')
+ else
+ output(' = (',self.mod,self.type,ptr,'*)',
+ 'malloc(',self.dim,'*sizeof(',self.type,ptr,'));')
+ end
+ else
+ local t = isbasic(self.type)
+ output(' = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_get'..t,'(tolua_S,',narg,',',def,'));')
+ else
+ output('tolua_getusertype(tolua_S,',narg,',',def,'));')
+ end
+ end
+end
+
+-- Get parameter value
+function classDeclaration:getarray (narg)
+ if self.dim ~= '' then
+ output(' {')
+ local def = self.def~='' or 0
+ output(' if (!tolua_arrayistype(tolua_S,',narg,',',self.tag,',',self.dim,',',def,'))')
+ output(' goto tolua_lerror;')
+ output(' else\n')
+ output(' {')
+ output(' int i;')
+ output(' for(i=0; i<'..self.dim..';i++)')
+ local t = isbasic(self.type)
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(' ',self.name..'[i] = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_getfield'..t..'(tolua_S,',narg,',i+1,',def,'));')
+ else
+ output('tolua_getfieldusertype(tolua_S,',narg,',i+1,',def,'));')
+ end
+ output(' }')
+ output(' }')
+ end
+end
+
+-- Get parameter value
+function classDeclaration:setarray (narg)
+ if self.dim ~= '' then
+ output(' {')
+ output(' int i;')
+ output(' for(i=0; i<'..self.dim..';i++)')
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_pushfield'..t..'(tolua_S,',narg,',i+1,(',ct,')',self.name,'[i]);')
+ else
+ if self.ptr == '' then
+ output(' {')
+ output('#ifdef __cplusplus\n')
+ output(' void* toluaI_clone = new',self.type,'(',self.name,'[i]);')
+ output('#else\n')
+ output(' void* toluaI_clone = tolua_copy(tolua_S,(void*)&',self.name,'[i],sizeof(',self.type,'));')
+ output('#endif\n')
+ output(' tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_doclone(tolua_S,toluaI_clone,',self.tag,'),',self.tag,');')
+ output(' }')
+
+ --output(' tolua_pushfieldclone(tolua_S,',narg,',i+1,(void*)&',self.name,'[i],sizeof(',self.type,'),',self.tag,');')
+ else
+ output(' tolua_pushfieldusertype(tolua_S,',narg,',i+1,(void*)',self.name,'[i],',self.tag,');')
+ end
+ end
+ output(' }')
+ end
+end
+
+-- Free dynamically allocated array
+function classDeclaration:freearray ()
+ if self.dim ~= '' and tonumber(self.dim)==nil then
+ output(' free(',self.name,');')
+ end
+end
+
+-- Pass parameter
+function classDeclaration:passpar ()
+ if self.ptr=='&' then
+ output('*'..self.name)
+ elseif self.ret=='*' then
+ output('&'..self.name)
+ else
+ output(self.name)
+ end
+end
+
+-- Return parameter value
+function classDeclaration:retvalue ()
+ if self.ret ~= '' then
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')'..self.name..');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)'..self.name..',',self.tag,');')
+ end
+ return 1
+ end
+ return 0
+end
+
+-- Internal constructor
+function _Declaration (t)
+ if t.name and t.name~='' then
+ local n = split(t.name,'@')
+ t.name = n[1]
+ t.lname = gsub(n[2] or n[1],"%[.-%]","")
+ end
+ t._base = classDeclaration
+ settag(t,tolua_tag)
+ t:checkname()
+ t:checktype()
+ return t
+end
+
+-- Constructor
+-- Expects the string declaration.
+-- The kind of declaration can be "var" or "func".
+function Declaration (s,kind)
+ -- eliminate spaces if default value is provided
+ s = gsub(s,"%s*=%s*","=")
+
+ if kind == "var" then
+ -- check the form: void
+ if s == '' or s == 'void' then
+ return _Declaration{type = 'void', kind = kind}
+ end
+ end
+
+ -- check the form: mod type*& name
+ local t = split(s,'%*%s*&')
+ if t.n == 2 then
+ if kind == 'func' then
+ error("#invalid function return type: "..s)
+ end
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '*',
+ ret = '&',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1),
+ kind = kind
+ }
+ end
+
+ -- check the form: mod type** name
+ t = split(s,'%*%s*%*')
+ if t.n == 2 then
+ if kind == 'func' then
+ error("#invalid function return type: "..s)
+ end
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '*',
+ ret = '*',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1),
+ kind = kind
+ }
+ end
+
+ -- check the form: mod type& name
+ t = split(s,'&')
+ if t.n == 2 then
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '&',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1) ,
+ kind = kind
+ }
+ end
+
+ -- check the form: mod type* name
+ local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end)
+ t = split(s1,'%*')
+ if t.n == 2 then
+ t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
+ local m = split(t[1],'%s%s*')
+ return _Declaration{
+ name = t[2],
+ ptr = '*',
+ type = m[m.n],
+ mod = concat(m,1,m.n-1) ,
+ kind = kind
+ }
+ end
+
+ if kind == 'var' then
+ -- check the form: mod type name
+ t = split(s,'%s%s*')
+ local v
+ if istype(t[t.n]) then v = '' else v = t[t.n]; t.n = t.n-1 end
+ return _Declaration{
+ name = v,
+ type = t[t.n],
+ mod = concat(t,1,t.n-1),
+ kind = kind
+ }
+
+ else -- kind == "func"
+
+ -- check the form: mod type name
+ t = split(s,'%s%s*')
+ local v = t[t.n] -- last word is the function name
+ local tp,md
+ if t.n>1 then
+ tp = t[t.n-1]
+ md = concat(t,1,t.n-2)
+ end
+ return _Declaration{
+ name = v,
+ type = tp,
+ mod = md,
+ kind = kind
+ }
+ end
+
+end
+
+
+
diff --git a/src/lua/define.lua b/src/lua/define.lua
new file mode 100644
index 00000000..db64db50
--- /dev/null
+++ b/src/lua/define.lua
@@ -0,0 +1,72 @@
+-- tolua: define class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: define.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Define class
+-- Represents a numeric const definition
+-- The following filds are stored:
+-- name = constant name
+classDefine = {
+ name = '',
+ _base = classFeature,
+}
+settag(classDefine,tolua_tag)
+
+-- register define
+function classDefine:register ()
+ local p = self:inmodule()
+ if p then
+ output(' tolua_constant(tolua_S,"'..p..'","'..self.lname..'",'..self.name..');')
+ else
+ output(' tolua_constant(tolua_S,NULL,"'..self.lname..'",'..self.name..');')
+ end
+end
+
+-- unregister define
+function classDefine:unregister ()
+ if not self:inmodule() then
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");')
+ end
+end
+
+-- Print method
+function classDefine:print (ident,close)
+ print(ident.."Define{")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." lname = '"..self.lname.."',")
+ print(ident.."}"..close)
+end
+
+
+-- Internal constructor
+function _Define (t)
+ t._base = classDefine
+ settag(t,tolua_tag)
+
+ if t.name == '' then
+ error("#invalid define")
+ end
+
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the constant name
+function Define (n)
+ local t = split(n,'@')
+ return _Define {
+ name = t[1],
+ lname = t[2] or t[1]
+ }
+end
+
+
diff --git a/src/lua/doit.lua b/src/lua/doit.lua
new file mode 100644
index 00000000..aa184d62
--- /dev/null
+++ b/src/lua/doit.lua
@@ -0,0 +1,73 @@
+-- Generate binding code
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: doit.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- open input file, if any
+if flags.f then
+ local st, msg = readfrom(flags.f)
+ if not st then
+ error('#'..msg)
+ end
+end
+
+-- define package name, if not provided
+if not flags.n then
+ if flags.f then
+ flags.n = gsub(flags.f,"%..*","")
+ else
+ error("#no package name nor input file provided")
+ end
+end
+
+local p = Package(flags.n)
+
+if flags.f then
+ readfrom()
+end
+
+if flags.p then
+ return -- only parse
+end
+
+if flags.o then
+ local st,msg = writeto(flags.o)
+ if not st then
+ error('#'..msg)
+ end
+end
+
+if flags.P then
+ p:print()
+else
+ p:decltag()
+ p:preamble()
+ p:supcode()
+ p:register()
+ p:unregister()
+end
+
+if flags.o then
+ writeto()
+end
+
+-- write header file
+if not flags.P then
+ if flags.H then
+ local st,msg = writeto(flags.H)
+ if not st then
+ error('#'..msg)
+ end
+ p:header()
+ writeto()
+ end
+end
+
diff --git a/src/lua/enumerate.lua b/src/lua/enumerate.lua
new file mode 100644
index 00000000..6b2b7466
--- /dev/null
+++ b/src/lua/enumerate.lua
@@ -0,0 +1,93 @@
+-- tolua: enumerate class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: enumerate.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Enumerate class
+-- Represents enumeration
+-- The following fields are stored:
+-- {i} = list of constant names
+classEnumerate = {
+ _base = classFeature,
+}
+settag(classEnumerate,tolua_tag)
+
+-- register enumeration
+function classEnumerate:register ()
+ local p = self:inclass() or self:inmodule()
+ local i=1
+ while self[i] do
+ if p then
+ if self:inclass() then
+ output(' tolua_constant(tolua_S,"'..p..'","'..self.lnames[i]..'",'..p..'::'..self[i]..');')
+ else
+ output(' tolua_constant(tolua_S,"'..p..'","'..self.lnames[i]..'",'..self[i]..');')
+ end
+ else
+ output(' tolua_constant(tolua_S,NULL,"'..self.lnames[i]..'",'..self[i]..');')
+ end
+ i = i+1
+ end
+end
+-- register enumeration
+function classEnumerate:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ local i=1
+ while self[i] do
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lnames[i]..'");')
+ i = i+1
+ end
+ end
+end
+
+-- Print method
+function classEnumerate:print (ident,close)
+ print(ident.."Enumerate{")
+ local i=1
+ while self[i] do
+ print(ident.." '"..self[i].."'("..self.lnames[i].."),")
+ i = i+1
+ end
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Enumerate (t)
+ t._base = classEnumerate
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the enumerate body
+function Enumerate (b)
+ local t = split(strsub(b,2,-2),',') -- eliminate braces
+ local i = 1
+ local e = {n=0}
+ while t[i] do
+ local tt = split(t[i],'=') -- discard initial value
+ e.n = e.n + 1
+ e[e.n] = tt[1]
+ i = i+1
+ end
+ -- set lua names
+ i = 1
+ e.lnames = {}
+ while e[i] do
+ local t = split(e[i],'@')
+ e[i] = t[1]
+ e.lnames[i] = t[2] or t[1]
+ i = i+1
+ end
+ return _Enumerate(e)
+end
+
+
diff --git a/src/lua/feature.lua b/src/lua/feature.lua
new file mode 100644
index 00000000..4a4379e1
--- /dev/null
+++ b/src/lua/feature.lua
@@ -0,0 +1,72 @@
+-- tolua: abstract feature class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: feature.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Feature class
+-- Represents the base class of all mapped feature.
+classFeature = {
+}
+
+-- write support code
+function classFeature:supcode ()
+end
+
+-- output tag
+function classFeature:decltag ()
+end
+
+-- register feature
+function classFeature:register ()
+end
+
+-- unregister feature
+function classFeature:unregister ()
+end
+
+-- translate verbatim
+function classFeature:preamble ()
+end
+
+-- check if feature is inside a class definition
+-- it returns the feature class name or nil.
+function classFeature:inclass ()
+ if self.parent and self.parent.type == 'class' then
+ return self.parent.name
+ else
+ return nil
+ end
+end
+
+-- check if feature is inside a module
+-- it returns the feature module name or nil.
+function classFeature:inmodule ()
+ if self.parent and self.parent.type == 'module' then
+ return self.parent.name
+ else
+ return nil
+ end
+end
+
+-- return C binding function name based on name
+-- the client specifies a prefix
+-- return C binding function name
+-- the client specifies a prefix
+function classFeature:cfuncname (n)
+ if self.parent then
+ n = self.parent:cfuncname(n)
+ end
+ if self.lname then
+ return n..'_'..self.lname
+ else
+ return n..'_'..self.name
+ end
+end
+
diff --git a/src/lua/function.lua b/src/lua/function.lua
new file mode 100644
index 00000000..b87e3488
--- /dev/null
+++ b/src/lua/function.lua
@@ -0,0 +1,317 @@
+-- tolua: function class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: function.lua,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Function class
+-- Represents a function or a class method.
+-- The following fields are stored:
+-- mod = type modifiers
+-- type = type
+-- ptr = "*" or "&", if representing a pointer or a reference
+-- name = name
+-- args = list of argument declarations
+-- const = if it is a method receiving a const "this".
+classFunction = {
+ mod = '',
+ type = '',
+ ptr = '',
+ name = '',
+ args = {n=0},
+ const = '',
+ _base = classFeature,
+}
+settag(classFunction,tolua_tag)
+
+-- declare tags
+function classFunction:decltag ()
+ self.itype,self.tag = tagvar(self.type,strfind(self.mod,'const'))
+ local i=1
+ while self.args[i] do
+ self.args[i]:decltag()
+ i = i+1
+ end
+end
+
+
+-- Write binding function
+-- Outputs C/C++ binding function.
+function classFunction:supcode ()
+ local nret = 0 -- number of returned values
+ local class = self:inclass()
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+
+ if class then
+ output("/* method:",self.name," of class ",class," */")
+ else
+ output("/* function:",self.name," */")
+ end
+ output("static int",self.cname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- check types
+ output(' if (\n')
+ -- check self
+ local narg
+ if class then narg=2 else narg=1 end
+ if class and self.name~='new' and static==nil then
+ if self.const == 'const' then
+ output(' !tolua_istype(tolua_S,1,',self.parent.ctag,',0) ||\n')
+ else
+ output(' !tolua_istype(tolua_S,1,',self.parent.tag,',0) ||\n')
+ end
+ end
+ -- check args
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ if isbasic(self.args[i].type) ~= 'value' then
+ output(' !'..self.args[i]:outchecktype(narg)..' ||\n')
+ end
+ narg = narg+1
+ i = i+1
+ end
+ end
+ -- check end of list
+ output(' !tolua_isnoobj(tolua_S,'..narg..')\n )\n goto tolua_lerror;')
+
+ output(' else\n {')
+
+ -- declare self, if the case
+ local narg
+ if class then narg=2 else narg=1 end
+ if class and self.name~='new' and static==nil then
+ output(' ',self.const,class,'*','self = ')
+ output('(',self.const,class,'*) ')
+ output('tolua_getusertype(tolua_S,1,0);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+ -- declare parameters
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:declare(narg)
+ narg = narg+1
+ i = i+1
+ end
+ end
+
+ -- check self
+ if class and self.name~='new' and static==nil then
+ output(' if (!self) tolua_error(tolua_S,"invalid \'self\' in function \''..self.name..'\'");');
+ end
+
+ -- get array element values
+ if class then narg=2 else narg=1 end
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:getarray(narg)
+ narg = narg+1
+ i = i+1
+ end
+ end
+
+ -- call function
+ if class and self.name=='delete' then
+ output(' delete self;')
+ elseif class and self.name == 'operator&[]' then
+ output(' self->operator[](',self.args[1].name,') = ',self.args[2].name,';')
+ else
+ output(' {')
+ if self.type ~= '' and self.type ~= 'void' then
+ output(' ',self.mod,self.type,self.ptr,'toluaI_ret = ')
+ output('(',self.mod,self.type,self.ptr,') ')
+ else
+ output(' ')
+ end
+ if class and self.name=='new' then
+ output('new',class,'(')
+ elseif class and static then
+ output(class..'::'..self.name,'(')
+ elseif class then
+ output('self->'..self.name,'(')
+ else
+ output(self.name,'(')
+ end
+
+ -- write parameters
+ local i=1
+ while self.args[i] do
+ self.args[i]:passpar()
+ i = i+1
+ if self.args[i] then
+ output(',')
+ end
+ end
+
+ output(');')
+
+ -- return values
+ if self.type ~= '' and self.type ~= 'void' then
+ nret = nret + 1
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')toluaI_ret);')
+ else
+ if self.ptr == '' then
+ output(' {')
+ output('#ifdef __cplusplus\n')
+ output(' void* toluaI_clone = new',self.type,'(toluaI_ret);')
+ output('#else\n')
+ output(' void* toluaI_clone = tolua_copy(tolua_S,(void*)&toluaI_ret,sizeof(',self.type,'));')
+ output('#endif\n')
+ output(' tolua_pushusertype(tolua_S,tolua_doclone(tolua_S,toluaI_clone,',self.tag,'),',self.tag,');')
+ output(' }')
+ --output(' tolua_pushclone((void*)&toluaI_ret,sizeof(',self.type,'),',self.tag,');')
+ elseif self.ptr == '&' then
+ output(' tolua_pushusertype(tolua_S,(void*)&toluaI_ret,',self.tag,');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)toluaI_ret,',self.tag,');')
+ end
+ end
+ end
+ local i=1
+ while self.args[i] do
+ nret = nret + self.args[i]:retvalue()
+ i = i+1
+ end
+ output(' }')
+
+ -- set array element values
+ if class then narg=2 else narg=1 end
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:setarray(narg)
+ narg = narg+1
+ i = i+1
+ end
+ end
+
+ -- free dynamically allocated array
+ if self.args[1].type ~= 'void' then
+ local i=1
+ while self.args[i] do
+ self.args[i]:freearray()
+ i = i+1
+ end
+ end
+ end
+
+ output(' }')
+ output(' return '..nret..';')
+
+ -- call overloaded function or generate error
+ output('tolua_lerror:\n')
+ local overload = strsub(self.cname,-2,-1) - 1
+ if overload >= 0 then
+ output(' return '..strsub(self.cname,1,-3)..format("%02d",overload)..'(tolua_S);')
+ else
+ output(' tolua_error(tolua_S,"#ferror in function \''..self.lname..'\'.");')
+ output(' return 0;')
+ end
+
+ output('}')
+ output('\n')
+end
+
+-- register function
+function classFunction:register ()
+ local parent = self:inclass() or self:inmodule()
+ if parent then
+ output(' tolua_function(tolua_S,"'..parent..'","'..self.lname..'",'..self.cname..');')
+ else
+ output(' tolua_function(tolua_S,NULL,"'..self.lname..'",'..self.cname..');')
+ end
+end
+
+-- unregister function
+function classFunction:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.lname..'");')
+ end
+end
+
+
+-- Print method
+function classFunction:print (ident,close)
+ print(ident.."Function{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." const = '"..self.const.."',")
+ print(ident.." cname = '"..self.cname.."',")
+ print(ident.." lname = '"..self.lname.."',")
+ print(ident.." args = {")
+ local i=1
+ while self.args[i] do
+ self.args[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.." }")
+ print(ident.."}"..close)
+end
+
+-- determine lua function name overload
+function classFunction:overload ()
+ return self.parent:overload(self.lname)
+end
+
+
+
+-- Internal constructor
+function _Function (t)
+ t._base = classFunction
+ settag(t,tolua_tag)
+
+ if t.const ~= 'const' and t.const ~= '' then
+ error("#invalid 'const' specification")
+ end
+
+ append(t)
+ if t:inclass() then
+ if t.name == t.parent.name then
+ t.name = 'new'
+ t.lname = 'new'
+ t.type = t.parent.name
+ t.ptr = '*'
+ elseif t.name == '~'..t.parent.name then
+ t.name = 'delete'
+ t.lname = 'delete'
+ end
+ end
+ t.cname = t:cfuncname("toluaI")..t:overload(t)
+ return t
+end
+
+-- Constructor
+-- Expects three strings: one representing the function declaration,
+-- another representing the argument list, and the third representing
+-- the "const" or empty string.
+function Function (d,a,c)
+ local t = split(strsub(a,2,-2),',') -- eliminate braces
+ local i=1
+ local l = {n=0}
+ while t[i] do
+ l.n = l.n+1
+ l[l.n] = Declaration(t[i],'var')
+ i = i+1
+ end
+ local f = Declaration(d,'func')
+ f.args = l
+ f.const = c
+ return _Function(f)
+end
+
+
diff --git a/src/lua/lapi.c b/src/lua/lapi.c
new file mode 100644
index 00000000..7be0ebfe
--- /dev/null
+++ b/src/lua/lapi.c
@@ -0,0 +1,494 @@
+/*
+** $Id: lapi.c,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Lua API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+const char lua_ident[] = "$Lua: " LUA_VERSION " " LUA_COPYRIGHT " $\n"
+ "$Authors: " LUA_AUTHORS " $";
+
+
+
+#define Index(L,i) ((i) >= 0 ? (L->Cbase+((i)-1)) : (L->top+(i)))
+
+#define api_incr_top(L) incr_top
+
+
+
+
+TObject *luaA_index (lua_State *L, int index) {
+ return Index(L, index);
+}
+
+
+static TObject *luaA_indexAcceptable (lua_State *L, int index) {
+ if (index >= 0) {
+ TObject *o = L->Cbase+(index-1);
+ if (o >= L->top) return NULL;
+ else return o;
+ }
+ else return L->top+index;
+}
+
+
+void luaA_pushobject (lua_State *L, const TObject *o) {
+ *L->top = *o;
+ incr_top;
+}
+
+LUA_API int lua_stackspace (lua_State *L) {
+ return (L->stack_last - L->top);
+}
+
+
+
+/*
+** basic stack manipulation
+*/
+
+
+LUA_API int lua_gettop (lua_State *L) {
+ return (L->top - L->Cbase);
+}
+
+
+LUA_API void lua_settop (lua_State *L, int index) {
+ if (index >= 0)
+ luaD_adjusttop(L, L->Cbase, index);
+ else
+ L->top = L->top+index+1; /* index is negative */
+}
+
+
+LUA_API void lua_remove (lua_State *L, int index) {
+ StkId p = luaA_index(L, index);
+ while (++p < L->top) *(p-1) = *p;
+ L->top--;
+}
+
+
+LUA_API void lua_insert (lua_State *L, int index) {
+ StkId p = luaA_index(L, index);
+ StkId q;
+ for (q = L->top; q>p; q--)
+ *q = *(q-1);
+ *p = *L->top;
+}
+
+
+LUA_API void lua_pushvalue (lua_State *L, int index) {
+ *L->top = *luaA_index(L, index);
+ api_incr_top(L);
+}
+
+
+
+/*
+** access functions (stack -> C)
+*/
+
+
+LUA_API int lua_type (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? LUA_TNONE : ttype(o);
+}
+
+LUA_API const char *lua_typename (lua_State *L, int t) {
+ UNUSED(L);
+ return (t == LUA_TNONE) ? "no value" : luaO_typenames[t];
+}
+
+
+LUA_API int lua_iscfunction (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? 0 : iscfunction(o);
+}
+
+LUA_API int lua_isnumber (lua_State *L, int index) {
+ TObject *o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? 0 : (tonumber(o) == 0);
+}
+
+LUA_API int lua_isstring (lua_State *L, int index) {
+ int t = lua_type(L, index);
+ return (t == LUA_TSTRING || t == LUA_TNUMBER);
+}
+
+
+LUA_API int lua_tag (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL) ? LUA_NOTAG : luaT_tag(o);
+}
+
+LUA_API int lua_equal (lua_State *L, int index1, int index2) {
+ StkId o1 = luaA_indexAcceptable(L, index1);
+ StkId o2 = luaA_indexAcceptable(L, index2);
+ if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */
+ else return luaO_equalObj(o1, o2);
+}
+
+LUA_API int lua_lessthan (lua_State *L, int index1, int index2) {
+ StkId o1 = luaA_indexAcceptable(L, index1);
+ StkId o2 = luaA_indexAcceptable(L, index2);
+ if (o1 == NULL || o2 == NULL) return 0; /* index out-of-range */
+ else return luaV_lessthan(L, o1, o2, L->top);
+}
+
+
+
+LUA_API long lua_tonumber (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || tonumber(o)) ? 0 : nvalue(o);
+}
+
+LUA_API const char *lua_tostring (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || tostring(L, o)) ? NULL : svalue(o);
+}
+
+LUA_API size_t lua_strlen (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || tostring(L, o)) ? 0 : tsvalue(o)->len;
+}
+
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || !iscfunction(o)) ? NULL : clvalue(o)->f.c;
+}
+
+LUA_API void *lua_touserdata (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ return (o == NULL || ttype(o) != LUA_TUSERDATA) ? NULL :
+ tsvalue(o)->u.d.value;
+}
+
+LUA_API const void *lua_topointer (lua_State *L, int index) {
+ StkId o = luaA_indexAcceptable(L, index);
+ if (o == NULL) return NULL;
+ switch (ttype(o)) {
+ case LUA_TTABLE:
+ return hvalue(o);
+ case LUA_TFUNCTION:
+ return clvalue(o);
+ default: return NULL;
+ }
+}
+
+
+
+/*
+** push functions (C -> stack)
+*/
+
+
+LUA_API void lua_pushnil (lua_State *L) {
+ ttype(L->top) = LUA_TNIL;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_pushnumber (lua_State *L, long n) {
+ nvalue(L->top) = n;
+ ttype(L->top) = LUA_TNUMBER;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {
+ tsvalue(L->top) = luaS_newlstr(L, s, len);
+ ttype(L->top) = LUA_TSTRING;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_pushstring (lua_State *L, const char *s) {
+ if (s == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushlstring(L, s, strlen(s));
+}
+
+
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
+ luaV_Cclosure(L, fn, n);
+}
+
+
+LUA_API void lua_pushusertag (lua_State *L, void *u, int tag) {
+ /* ORDER LUA_T */
+ if (!(tag == LUA_ANYTAG || tag == LUA_TUSERDATA || validtag(tag)))
+ luaO_verror(L, "invalid tag for a userdata (%d)", tag);
+ tsvalue(L->top) = luaS_createudata(L, u, tag);
+ ttype(L->top) = LUA_TUSERDATA;
+ api_incr_top(L);
+}
+
+
+
+/*
+** get functions (Lua -> stack)
+*/
+
+
+LUA_API void lua_getglobal (lua_State *L, const char *name) {
+ StkId top = L->top;
+ *top = *luaV_getglobal(L, luaS_new(L, name));
+ L->top = top;
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_gettable (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ StkId top = L->top;
+ *(top-1) = *luaV_gettable(L, t);
+ L->top = top; /* tag method may change top */
+}
+
+
+LUA_API void lua_rawget (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected");
+ *(L->top - 1) = *luaH_get(L, hvalue(t), L->top - 1);
+}
+
+
+LUA_API void lua_rawgeti (lua_State *L, int index, int n) {
+ StkId o = Index(L, index);
+ LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected");
+ *L->top = *luaH_getnum(hvalue(o), n);
+ api_incr_top(L);
+}
+
+
+LUA_API void lua_getglobals (lua_State *L) {
+ hvalue(L->top) = L->gt;
+ ttype(L->top) = LUA_TTABLE;
+ api_incr_top(L);
+}
+
+
+LUA_API int lua_getref (lua_State *L, int ref) {
+ if (ref == LUA_REFNIL)
+ ttype(L->top) = LUA_TNIL;
+ else if (0 <= ref && ref < L->refSize &&
+ (L->refArray[ref].st == LOCK || L->refArray[ref].st == HOLD))
+ *L->top = L->refArray[ref].o;
+ else
+ return 0;
+ api_incr_top(L);
+ return 1;
+}
+
+
+LUA_API void lua_newtable (lua_State *L) {
+ hvalue(L->top) = luaH_new(L, 0);
+ ttype(L->top) = LUA_TTABLE;
+ api_incr_top(L);
+}
+
+
+
+/*
+** set functions (stack -> Lua)
+*/
+
+
+LUA_API void lua_setglobal (lua_State *L, const char *name) {
+ StkId top = L->top;
+ luaV_setglobal(L, luaS_new(L, name));
+ L->top = top-1; /* remove element from the top */
+}
+
+
+LUA_API void lua_settable (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ StkId top = L->top;
+ luaV_settable(L, t, top-2);
+ L->top = top-2; /* pop index and value */
+}
+
+
+LUA_API void lua_rawset (lua_State *L, int index) {
+ StkId t = Index(L, index);
+ LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected");
+ *luaH_set(L, hvalue(t), L->top-2) = *(L->top-1);
+ L->top -= 2;
+}
+
+
+LUA_API void lua_rawseti (lua_State *L, int index, int n) {
+ StkId o = Index(L, index);
+ LUA_ASSERT(ttype(o) == LUA_TTABLE, "table expected");
+ *luaH_setint(L, hvalue(o), n) = *(L->top-1);
+ L->top--;
+}
+
+
+LUA_API void lua_setglobals (lua_State *L) {
+ StkId newtable = --L->top;
+ LUA_ASSERT(ttype(newtable) == LUA_TTABLE, "table expected");
+ L->gt = hvalue(newtable);
+}
+
+
+LUA_API int lua_ref (lua_State *L, int lock) {
+ int ref;
+ if (ttype(L->top-1) == LUA_TNIL)
+ ref = LUA_REFNIL;
+ else {
+ if (L->refFree != NONEXT) { /* is there a free place? */
+ ref = L->refFree;
+ L->refFree = L->refArray[ref].st;
+ }
+ else { /* no more free places */
+ luaM_growvector(L, L->refArray, L->refSize, 1, struct Ref,
+ "reference table overflow", MAX_INT);
+ L->nblocks += sizeof(struct Ref);
+ ref = L->refSize++;
+ }
+ L->refArray[ref].o = *(L->top-1);
+ L->refArray[ref].st = lock ? LOCK : HOLD;
+ }
+ L->top--;
+ return ref;
+}
+
+
+/*
+** "do" functions (run Lua code)
+** (most of them are in ldo.c)
+*/
+
+LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults) {
+ luaD_call(L, L->top-(nargs+1), nresults);
+}
+
+
+/*
+** Garbage-collection functions
+*/
+
+/* GC values are expressed in Kbytes: #bytes/2^10 */
+#define GCscale(x) ((int)((x)>>10))
+#define GCunscale(x) ((unsigned long)(x)<<10)
+
+LUA_API int lua_getgcthreshold (lua_State *L) {
+ return GCscale(L->GCthreshold);
+}
+
+LUA_API int lua_getgccount (lua_State *L) {
+ return GCscale(L->nblocks);
+}
+
+LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold) {
+ if (newthreshold > GCscale(ULONG_MAX))
+ L->GCthreshold = ULONG_MAX;
+ else
+ L->GCthreshold = GCunscale(newthreshold);
+ luaC_checkGC(L);
+}
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API void lua_settag (lua_State *L, int tag) {
+ luaT_realtag(L, tag);
+ switch (ttype(L->top-1)) {
+ case LUA_TTABLE:
+ hvalue(L->top-1)->htag = tag;
+ break;
+ case LUA_TUSERDATA:
+ tsvalue(L->top-1)->u.d.tag = tag;
+ break;
+ default:
+ luaO_verror(L, "cannot change the tag of a %.20s",
+ luaO_typename(L->top-1));
+ }
+}
+
+
+LUA_API void lua_unref (lua_State *L, int ref) {
+ if (ref >= 0) {
+ LUA_ASSERT(ref < L->refSize && L->refArray[ref].st < 0, "invalid ref");
+ L->refArray[ref].st = L->refFree;
+ L->refFree = ref;
+ }
+}
+
+
+LUA_API int lua_next (lua_State *L, int index) {
+ StkId t = luaA_index(L, index);
+ Node *n;
+ LUA_ASSERT(ttype(t) == LUA_TTABLE, "table expected");
+ n = luaH_next(L, hvalue(t), luaA_index(L, -1));
+ if (n) {
+ *(L->top-1) = *key(n);
+ *L->top = *val(n);
+ api_incr_top(L);
+ return 1;
+ }
+ else { /* no more elements */
+ L->top -= 1; /* remove key */
+ return 0;
+ }
+}
+
+
+LUA_API int lua_getn (lua_State *L, int index) {
+ Hash *h = hvalue(luaA_index(L, index));
+ const TObject *value = luaH_getstr(h, luaS_new(L, "n")); /* value = h.n */
+ if (ttype(value) == LUA_TNUMBER)
+ return (int)nvalue(value);
+ else {
+ Number max = 0;
+ int i = h->size;
+ Node *n = h->node;
+ while (i--) {
+ if (ttype(key(n)) == LUA_TNUMBER &&
+ ttype(val(n)) != LUA_TNIL &&
+ nvalue(key(n)) > max)
+ max = nvalue(key(n));
+ n++;
+ }
+ return (int)max;
+ }
+}
+
+
+LUA_API void lua_concat (lua_State *L, int n) {
+ StkId top = L->top;
+ luaV_strconc(L, n, top);
+ L->top = top-(n-1);
+ luaC_checkGC(L);
+}
+
+
+LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
+ TString *ts = luaS_newudata(L, size, NULL);
+ tsvalue(L->top) = ts;
+ ttype(L->top) = LUA_TUSERDATA;
+ api_incr_top(L);
+ return ts->u.d.value;
+}
+
diff --git a/src/lua/lapi.h b/src/lua/lapi.h
new file mode 100644
index 00000000..d6e1c44f
--- /dev/null
+++ b/src/lua/lapi.h
@@ -0,0 +1,17 @@
+/*
+** $Id: lapi.h,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions from Lua API
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lapi_h
+#define lapi_h
+
+
+#include "lobject.h"
+
+
+TObject *luaA_index (lua_State *L, int index);
+void luaA_pushobject (lua_State *L, const TObject *o);
+
+#endif
diff --git a/src/lua/lauxlib.c b/src/lua/lauxlib.c
new file mode 100644
index 00000000..810bca20
--- /dev/null
+++ b/src/lua/lauxlib.c
@@ -0,0 +1,216 @@
+/*
+** $Id: lauxlib.c,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+/* This file uses only the official API of Lua.
+** Any function declared here could be written as an application function.
+** With care, these functions can be used by other libraries.
+*/
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+
+
+
+LUALIB_API int luaL_findstring (const char *name, const char *const list[]) {
+ int i;
+ for (i=0; list[i]; i++)
+ if (strcmp(list[i], name) == 0)
+ return i;
+ return -1; /* name not found */
+}
+
+LUALIB_API void luaL_argerror (lua_State *L, int narg, const char *extramsg) {
+ lua_Debug ar;
+ lua_getstack(L, 0, &ar);
+ lua_getinfo(L, "n", &ar);
+ if (ar.name == NULL)
+ ar.name = "?";
+ luaL_verror(L, "bad argument #%d to `%.50s' (%.100s)",
+ narg, ar.name, extramsg);
+}
+
+
+static void type_error (lua_State *L, int narg, int t) {
+ char buff[50];
+ sprintf(buff, "%.8s expected, got %.8s", lua_typename(L, t),
+ lua_typename(L, lua_type(L, narg)));
+ luaL_argerror(L, narg, buff);
+}
+
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {
+ if (space > lua_stackspace(L))
+ luaL_verror(L, "stack overflow (%.30s)", mes);
+}
+
+
+LUALIB_API void luaL_checktype(lua_State *L, int narg, int t) {
+ if (lua_type(L, narg) != t)
+ type_error(L, narg, t);
+}
+
+
+LUALIB_API void luaL_checkany (lua_State *L, int narg) {
+ if (lua_type(L, narg) == LUA_TNONE)
+ luaL_argerror(L, narg, "value expected");
+}
+
+
+LUALIB_API const char *luaL_check_lstr (lua_State *L, int narg, size_t *len) {
+ const char *s = lua_tostring(L, narg);
+ if (!s) type_error(L, narg, LUA_TSTRING);
+ if (len) *len = lua_strlen(L, narg);
+ return s;
+}
+
+
+LUALIB_API const char *luaL_opt_lstr (lua_State *L, int narg, const char *def, size_t *len) {
+ if (lua_isnull(L, narg)) {
+ if (len)
+ *len = (def ? strlen(def) : 0);
+ return def;
+ }
+ else return luaL_check_lstr(L, narg, len);
+}
+
+
+LUALIB_API long luaL_check_number (lua_State *L, int narg) {
+ long d = lua_tonumber(L, narg);
+ if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
+ type_error(L, narg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API long luaL_opt_number (lua_State *L, int narg, long def) {
+ if (lua_isnull(L, narg)) return def;
+ else return luaL_check_number(L, narg);
+}
+
+
+LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n) {
+ int i;
+ for (i=0; i<n; i++)
+ lua_register(L, l[i].name, l[i].func);
+}
+
+
+LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...) {
+ char buff[500];
+ va_list argp;
+ va_start(argp, fmt);
+ vsprintf(buff, fmt, argp);
+ va_end(argp);
+ lua_error(L, buff);
+}
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#define buffempty(B) ((B)->p == (B)->buffer)
+#define bufflen(B) ((B)->p - (B)->buffer)
+#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
+
+#define LIMIT (LUA_MINSTACK/2)
+
+
+static int emptybuffer (luaL_Buffer *B) {
+ size_t l = bufflen(B);
+ if (l == 0) return 0; /* put nothing on stack */
+ else {
+ lua_pushlstring(B->L, B->buffer, l);
+ B->p = B->buffer;
+ B->level++;
+ return 1;
+ }
+}
+
+
+static void adjuststack (luaL_Buffer *B) {
+ if (B->level > 1) {
+ lua_State *L = B->L;
+ int toget = 1; /* number of levels to concat */
+ size_t toplen = lua_strlen(L, -1);
+ do {
+ size_t l = lua_strlen(L, -(toget+1));
+ if (B->level - toget + 1 >= LIMIT || toplen > l) {
+ toplen += l;
+ toget++;
+ }
+ else break;
+ } while (toget < B->level);
+ if (toget >= 2) {
+ lua_concat(L, toget);
+ B->level = B->level - toget + 1;
+ }
+ }
+}
+
+
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {
+ if (emptybuffer(B))
+ adjuststack(B);
+ return B->buffer;
+}
+
+
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
+ while (l--)
+ luaL_putchar(B, *s++);
+}
+
+
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
+ luaL_addlstring(B, s, strlen(s));
+}
+
+
+LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
+ emptybuffer(B);
+ if (B->level == 0)
+ lua_pushlstring(B->L, NULL, 0);
+ else if (B->level > 1)
+ lua_concat(B->L, B->level);
+ B->level = 1;
+}
+
+
+LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ size_t vl = lua_strlen(L, -1);
+ if (vl <= bufffree(B)) { /* fit into buffer? */
+ memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */
+ B->p += vl;
+ lua_pop(L, 1); /* remove from stack */
+ }
+ else {
+ if (emptybuffer(B))
+ lua_insert(L, -2); /* put buffer before new value */
+ B->level++; /* add new value into B stack */
+ adjuststack(B);
+ }
+}
+
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
+ B->L = L;
+ B->p = B->buffer;
+ B->level = 0;
+}
+
+/* }====================================================== */
diff --git a/src/lua/lauxlib.h b/src/lua/lauxlib.h
new file mode 100644
index 00000000..a8d35aff
--- /dev/null
+++ b/src/lua/lauxlib.h
@@ -0,0 +1,100 @@
+/*
+** $Id: lauxlib.h,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#ifndef LUALIB_API
+#define LUALIB_API extern
+#endif
+
+
+struct luaL_reg {
+ const char *name;
+ lua_CFunction func;
+};
+
+
+LUALIB_API void luaL_openlib (lua_State *L, const struct luaL_reg *l, int n);
+LUALIB_API void luaL_argerror (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *luaL_check_lstr (lua_State *L, int numArg, size_t *len);
+LUALIB_API const char *luaL_opt_lstr (lua_State *L, int numArg, const char *def, size_t *len);
+LUALIB_API long luaL_check_number (lua_State *L, int numArg);
+LUALIB_API long luaL_opt_number (lua_State *L, int numArg, long def);
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg);
+LUALIB_API void luaL_checktype (lua_State *L, int narg, int t);
+LUALIB_API void luaL_checkany (lua_State *L, int narg);
+
+LUALIB_API void luaL_verror (lua_State *L, const char *fmt, ...);
+LUALIB_API int luaL_findstring (const char *name, const char *const list[]);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_arg_check(L, cond,numarg,extramsg) if (!(cond)) \
+ luaL_argerror(L, numarg,extramsg)
+#define luaL_check_string(L,n) (luaL_check_lstr(L, (n), NULL))
+#define luaL_opt_string(L,n,d) (luaL_opt_lstr(L, (n), (d), NULL))
+#define luaL_check_int(L,n) ((int)luaL_check_number(L, n))
+#define luaL_check_long(L,n) ((long)luaL_check_number(L, n))
+#define luaL_opt_int(L,n,d) ((int)luaL_opt_number(L, n,d))
+#define luaL_opt_long(L,n,d) ((long)luaL_opt_number(L, n,d))
+#define luaL_openl(L,a) luaL_openlib(L, a, (sizeof(a)/sizeof(a[0])))
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#ifndef LUAL_BUFFERSIZE
+#define LUAL_BUFFERSIZE BUFSIZ
+#endif
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int level;
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_putchar(B,c) \
+ ((void)((B)->p < &(B)->buffer[LUAL_BUFFERSIZE] || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B);
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s);
+LUALIB_API void luaL_addvalue (luaL_Buffer *B);
+LUALIB_API void luaL_pushresult (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+#endif
+
+
diff --git a/src/lua/lbaselib.c b/src/lua/lbaselib.c
new file mode 100644
index 00000000..71c643aa
--- /dev/null
+++ b/src/lua/lbaselib.c
@@ -0,0 +1,651 @@
+/*
+** $Id: lbaselib.c,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Basic library
+** See Copyright Notice in lua.h
+*/
+
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+
+/*
+** If your system does not support `stderr', redefine this function, or
+** redefine _ERRORMESSAGE so that it won't need _ALERT.
+*/
+static int luaB__ALERT (lua_State *L) {
+ fputs(luaL_check_string(L, 1), stderr);
+ return 0;
+}
+
+
+/*
+** Basic implementation of _ERRORMESSAGE.
+** The library `liolib' redefines _ERRORMESSAGE for better error information.
+*/
+static int luaB__ERRORMESSAGE (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TSTRING);
+ lua_getglobal(L, LUA_ALERT);
+ if (lua_isfunction(L, -1)) { /* avoid error loop if _ALERT is not defined */
+ lua_Debug ar;
+ lua_pushstring(L, "error: ");
+ lua_pushvalue(L, 1);
+ if (lua_getstack(L, 1, &ar)) {
+ lua_getinfo(L, "Sl", &ar);
+ if (ar.source && ar.currentline > 0) {
+ char buff[100];
+ sprintf(buff, "\n <%.70s: line %d>", ar.short_src, ar.currentline);
+ lua_pushstring(L, buff);
+ lua_concat(L, 2);
+ }
+ }
+ lua_pushstring(L, "\n");
+ lua_concat(L, 3);
+ lua_rawcall(L, 1, 0);
+ }
+ return 0;
+}
+
+
+/*
+** If your system does not support `stdout', you can just remove this function.
+** If you need, you can define your own `print' function, following this
+** model but changing `fputs' to put the strings at a proper place
+** (a console window or a log file, for instance).
+*/
+static int luaB_print (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ lua_getglobal(L, "tostring");
+ for (i=1; i<=n; i++) {
+ const char *s;
+ lua_pushvalue(L, -1); /* function to be called */
+ lua_pushvalue(L, i); /* value to print */
+ lua_rawcall(L, 1, 1);
+ s = lua_tostring(L, -1); /* get result */
+ if (s == NULL)
+ lua_error(L, "`tostring' must return a string to `print'");
+ if (i>1) fputs("\t", stdout);
+ fputs(s, stdout);
+ lua_pop(L, 1); /* pop result */
+ }
+ fputs("\n", stdout);
+ return 0;
+}
+
+
+static int luaB_tonumber (lua_State *L) {
+ int base = luaL_opt_int(L, 2, 10);
+ if (base == 10) { /* standard conversion */
+ luaL_checkany(L, 1);
+ if (lua_isnumber(L, 1)) {
+ lua_pushnumber(L, lua_tonumber(L, 1));
+ return 1;
+ }
+ }
+ else {
+ const char *s1 = luaL_check_string(L, 1);
+ char *s2;
+ unsigned long n;
+ luaL_arg_check(L, 2 <= base && base <= 36, 2, "base out of range");
+ n = strtoul(s1, &s2, base);
+ if (s1 != s2) { /* at least one valid digit? */
+ while (isspace((unsigned char)*s2)) s2++; /* skip trailing spaces */
+ if (*s2 == '\0') { /* no invalid trailing characters? */
+ lua_pushnumber(L, n);
+ return 1;
+ }
+ }
+ }
+ lua_pushnil(L); /* else not a number */
+ return 1;
+}
+
+
+static int luaB_error (lua_State *L) {
+ lua_error(L, luaL_opt_string(L, 1, NULL));
+ return 0; /* to avoid warnings */
+}
+
+static int luaB_setglobal (lua_State *L) {
+ luaL_checkany(L, 2);
+ lua_setglobal(L, luaL_check_string(L, 1));
+ return 0;
+}
+
+static int luaB_getglobal (lua_State *L) {
+ lua_getglobal(L, luaL_check_string(L, 1));
+ return 1;
+}
+
+static int luaB_tag (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushnumber(L, lua_tag(L, 1));
+ return 1;
+}
+
+static int luaB_settag (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, 1); /* push table */
+ lua_settag(L, luaL_check_int(L, 2));
+ return 1; /* return table */
+}
+
+static int luaB_newtag (lua_State *L) {
+ lua_pushnumber(L, lua_newtag(L));
+ return 1;
+}
+
+static int luaB_copytagmethods (lua_State *L) {
+ lua_pushnumber(L, lua_copytagmethods(L, luaL_check_int(L, 1),
+ luaL_check_int(L, 2)));
+ return 1;
+}
+
+static int luaB_globals (lua_State *L) {
+ lua_getglobals(L); /* value to be returned */
+ if (!lua_isnull(L, 1)) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, 1); /* new table of globals */
+ lua_setglobals(L);
+ }
+ return 1;
+}
+
+static int luaB_rawget (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ lua_rawget(L, -2);
+ return 1;
+}
+
+static int luaB_rawset (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ luaL_checkany(L, 3);
+ lua_rawset(L, -3);
+ return 1;
+}
+
+static int luaB_settagmethod (lua_State *L) {
+ int tag = luaL_check_int(L, 1);
+ const char *event = luaL_check_string(L, 2);
+ luaL_arg_check(L, lua_isfunction(L, 3) || lua_isnil(L, 3), 3,
+ "function or nil expected");
+ if (strcmp(event, "gc") == 0)
+ lua_error(L, "deprecated use: cannot set the `gc' tag method from Lua");
+ lua_gettagmethod(L, tag, event);
+ lua_pushvalue(L, 3);
+ lua_settagmethod(L, tag, event);
+ return 1;
+}
+
+
+static int luaB_gettagmethod (lua_State *L) {
+ int tag = luaL_check_int(L, 1);
+ const char *event = luaL_check_string(L, 2);
+ if (strcmp(event, "gc") == 0)
+ lua_error(L, "deprecated use: cannot get the `gc' tag method from Lua");
+ lua_gettagmethod(L, tag, event);
+ return 1;
+}
+
+
+static int luaB_gcinfo (lua_State *L) {
+ lua_pushnumber(L, lua_getgccount(L));
+ lua_pushnumber(L, lua_getgcthreshold(L));
+ return 2;
+}
+
+
+static int luaB_collectgarbage (lua_State *L) {
+ lua_setgcthreshold(L, luaL_opt_int(L, 1, 0));
+ return 0;
+}
+
+
+static int luaB_type (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
+ return 1;
+}
+
+
+static int luaB_next (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 2); /* create a 2nd argument if there isn't one */
+ if (lua_next(L, 1))
+ return 2;
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int passresults (lua_State *L, int status, int oldtop) {
+ static const char *const errornames[] =
+ {"ok", "run-time error", "file error", "syntax error",
+ "memory error", "error in error handling"};
+ if (status == 0) {
+ int nresults = lua_gettop(L) - oldtop;
+ if (nresults > 0)
+ return nresults; /* results are already on the stack */
+ else {
+ lua_pushuserdata(L, NULL); /* at least one result to signal no errors */
+ return 1;
+ }
+ }
+ else { /* error */
+ lua_pushnil(L);
+ lua_pushstring(L, errornames[status]); /* error code */
+ return 2;
+ }
+}
+
+static int luaB_dostring (lua_State *L) {
+ int oldtop = lua_gettop(L);
+ size_t l;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ if (*s == '\27') /* binary files start with ESC... */
+ lua_error(L, "`dostring' cannot run pre-compiled code");
+ return passresults(L, lua_dobuffer(L, s, l, luaL_opt_string(L, 2, s)), oldtop);
+}
+
+
+static int luaB_dofile (lua_State *L) {
+ int oldtop = lua_gettop(L);
+ const char *fname = luaL_opt_string(L, 1, NULL);
+ return passresults(L, lua_dofile(L, fname), oldtop);
+}
+
+
+static int luaB_call (lua_State *L) {
+ int oldtop;
+ const char *options = luaL_opt_string(L, 3, "");
+ int err = 0; /* index of old error method */
+ int i, status;
+ int n;
+ luaL_checktype(L, 2, LUA_TTABLE);
+ n = lua_getn(L, 2);
+ if (!lua_isnull(L, 4)) { /* set new error method */
+ lua_getglobal(L, LUA_ERRORMESSAGE);
+ err = lua_gettop(L); /* get index */
+ lua_pushvalue(L, 4);
+ lua_setglobal(L, LUA_ERRORMESSAGE);
+ }
+ oldtop = lua_gettop(L); /* top before function-call preparation */
+ /* push function */
+ lua_pushvalue(L, 1);
+ luaL_checkstack(L, n, "too many arguments");
+ for (i=0; i<n; i++) /* push arg[1...n] */
+ lua_rawgeti(L, 2, i+1);
+ status = lua_call(L, n, LUA_MULTRET);
+ if (err != 0) { /* restore old error method */
+ lua_pushvalue(L, err);
+ lua_setglobal(L, LUA_ERRORMESSAGE);
+ }
+ if (status != 0) { /* error in call? */
+ if (strchr(options, 'x'))
+ lua_pushnil(L); /* return nil to signal the error */
+ else
+ lua_error(L, NULL); /* propagate error without additional messages */
+ return 1;
+ }
+ if (strchr(options, 'p')) /* pack results? */
+ lua_error(L, "deprecated option `p' in `call'");
+ return lua_gettop(L) - oldtop; /* results are already on the stack */
+}
+
+
+static int luaB_tostring (lua_State *L) {
+ char buff[64];
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, 1));
+ return 1;
+ case LUA_TSTRING:
+ lua_pushvalue(L, 1);
+ return 1;
+ case LUA_TTABLE:
+ sprintf(buff, "table: %p", lua_topointer(L, 1));
+ break;
+ case LUA_TFUNCTION:
+ sprintf(buff, "function: %p", lua_topointer(L, 1));
+ break;
+ case LUA_TUSERDATA:
+ sprintf(buff, "userdata(%d): %p", lua_tag(L, 1), lua_touserdata(L, 1));
+ break;
+ case LUA_TNIL:
+ lua_pushstring(L, "nil");
+ return 1;
+ default:
+ luaL_argerror(L, 1, "value expected");
+ }
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int luaB_foreachi (lua_State *L) {
+ int n, i;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ n = lua_getn(L, 1);
+ for (i=1; i<=n; i++) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushnumber(L, i); /* 1st argument */
+ lua_rawgeti(L, 1, i); /* 2nd argument */
+ lua_rawcall(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 1); /* remove nil result */
+ }
+ return 0;
+}
+
+
+static int luaB_foreach (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_pushnil(L); /* first index */
+ for (;;) {
+ if (lua_next(L, 1) == 0)
+ return 0;
+ lua_pushvalue(L, 2); /* function */
+ lua_pushvalue(L, -3); /* key */
+ lua_pushvalue(L, -3); /* value */
+ lua_rawcall(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 2); /* remove value and result */
+ }
+}
+
+
+static int luaB_assert (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (lua_isnil(L, 1))
+ luaL_verror(L, "assertion failed! %.90s", luaL_opt_string(L, 2, ""));
+ return 0;
+}
+
+
+static int luaB_getn (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushnumber(L, lua_getn(L, 1));
+ return 1;
+}
+
+
+static int luaB_tinsert (lua_State *L) {
+ int v = lua_gettop(L); /* last argument: to be inserted */
+ int n, pos;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ n = lua_getn(L, 1);
+ if (v == 2) /* called with only 2 arguments */
+ pos = n+1;
+ else
+ pos = luaL_check_int(L, 2); /* 2nd argument is the position */
+ lua_pushstring(L, "n");
+ lua_pushnumber(L, n+1);
+ lua_rawset(L, 1); /* t.n = n+1 */
+ for (; n>=pos; n--) {
+ lua_rawgeti(L, 1, n);
+ lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */
+ }
+ lua_pushvalue(L, v);
+ lua_rawseti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int luaB_tremove (lua_State *L) {
+ int pos, n;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ n = lua_getn(L, 1);
+ pos = luaL_opt_int(L, 2, n);
+ if (n <= 0) return 0; /* table is "empty" */
+ lua_rawgeti(L, 1, pos); /* result = t[pos] */
+ for ( ;pos<n; pos++) {
+ lua_rawgeti(L, 1, pos+1);
+ lua_rawseti(L, 1, pos); /* a[pos] = a[pos+1] */
+ }
+ lua_pushstring(L, "n");
+ lua_pushnumber(L, n-1);
+ lua_rawset(L, 1); /* t.n = n-1 */
+ lua_pushnil(L);
+ lua_rawseti(L, 1, n); /* t[n] = nil */
+ return 1;
+}
+
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on `Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+*/
+
+
+static void set2 (lua_State *L, int i, int j) {
+ lua_rawseti(L, 1, i);
+ lua_rawseti(L, 1, j);
+}
+
+static int sort_comp (lua_State *L, int a, int b) {
+ /* WARNING: the caller (auxsort) must ensure stack space */
+ if (!lua_isnil(L, 2)) { /* function? */
+ int res;
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
+ lua_rawcall(L, 2, 1);
+ res = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+ return res;
+ }
+ else /* a < b? */
+ return lua_lessthan(L, a, b);
+}
+
+static void auxsort (lua_State *L, int l, int u) {
+ while (l < u) { /* for tail recursion */
+ int i, j;
+ /* sort elements a[l], a[(l+u)/2] and a[u] */
+ lua_rawgeti(L, 1, l);
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
+ set2(L, l, u); /* swap a[l] - a[u] */
+ else
+ lua_pop(L, 2);
+ if (u-l == 1) break; /* only 2 elements */
+ i = (l+u)/2;
+ lua_rawgeti(L, 1, i);
+ lua_rawgeti(L, 1, l);
+ if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */
+ set2(L, i, l);
+ else {
+ lua_pop(L, 1); /* remove a[l] */
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
+ set2(L, i, u);
+ else
+ lua_pop(L, 2);
+ }
+ if (u-l == 2) break; /* only 3 elements */
+ lua_rawgeti(L, 1, i); /* Pivot */
+ lua_pushvalue(L, -1);
+ lua_rawgeti(L, 1, u-1);
+ set2(L, i, u-1);
+ /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
+ i = l; j = u-1;
+ for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
+ /* repeat ++i until a[i] >= P */
+ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (i>u) lua_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* repeat --j until a[j] <= P */
+ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (j<l) lua_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ if (j<i) {
+ lua_pop(L, 3); /* pop pivot, a[i], a[j] */
+ break;
+ }
+ set2(L, i, j);
+ }
+ lua_rawgeti(L, 1, u-1);
+ lua_rawgeti(L, 1, i);
+ set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
+ /* a[l..i-1] <= a[i] == P <= a[i+1..u] */
+ /* adjust so that smaller "half" is in [j..i] and larger one in [l..u] */
+ if (i-l < u-i) {
+ j=l; i=i-1; l=i+2;
+ }
+ else {
+ j=i+1; i=u; u=j-2;
+ }
+ auxsort(L, j, i); /* call recursively the smaller one */
+ } /* repeat the routine for the larger one */
+}
+
+static int luaB_sort (lua_State *L) {
+ int n;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ n = lua_getn(L, 1);
+ if (!lua_isnull(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_settop(L, 2); /* make sure there is two arguments */
+ auxsort(L, 1, n);
+ return 0;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Deprecated functions to manipulate global environment.
+** =======================================================
+*/
+
+
+#define num_deprecated 4
+
+static const struct luaL_reg deprecated_names [num_deprecated] = {
+ {"foreachvar", luaB_foreach},
+ {"nextvar", luaB_next},
+ {"rawgetglobal", luaB_rawget},
+ {"rawsetglobal", luaB_rawset}
+};
+
+
+#ifdef LUA_DEPRECATEDFUNCS
+
+/*
+** call corresponding function inserting `globals' as first argument
+*/
+static int deprecated_func (lua_State *L) {
+ lua_insert(L, 1); /* upvalue is the function to be called */
+ lua_getglobals(L);
+ lua_insert(L, 2); /* table of globals is 1o argument */
+ lua_rawcall(L, lua_gettop(L)-1, LUA_MULTRET);
+ return lua_gettop(L); /* return all results */
+}
+
+
+static void deprecated_funcs (lua_State *L) {
+ int i;
+ for (i=0; i<num_deprecated; i++) {
+ lua_pushcfunction(L, deprecated_names[i].func);
+ lua_pushcclosure(L, deprecated_func, 1);
+ lua_setglobal(L, deprecated_names[i].name);
+ }
+}
+
+
+#else
+
+/*
+** gives an explicit error in any attempt to call a deprecated function
+*/
+static int deprecated_func (lua_State *L) {
+ luaL_verror(L, "function `%.20s' is deprecated", lua_tostring(L, -1));
+ return 0; /* to avoid warnings */
+}
+
+
+static void deprecated_funcs (lua_State *L) {
+ int i;
+ for (i=0; i<num_deprecated; i++) {
+ lua_pushstring(L, deprecated_names[i].name);
+ lua_pushcclosure(L, deprecated_func, 1);
+ lua_setglobal(L, deprecated_names[i].name);
+ }
+}
+
+#endif
+
+/* }====================================================== */
+
+static const struct luaL_reg base_funcs[] = {
+ {LUA_ALERT, luaB__ALERT},
+ {LUA_ERRORMESSAGE, luaB__ERRORMESSAGE},
+ {"call", luaB_call},
+ {"collectgarbage", luaB_collectgarbage},
+ {"copytagmethods", luaB_copytagmethods},
+ {"dofile", luaB_dofile},
+ {"dostring", luaB_dostring},
+ {"error", luaB_error},
+ {"foreach", luaB_foreach},
+ {"foreachi", luaB_foreachi},
+ {"gcinfo", luaB_gcinfo},
+ {"getglobal", luaB_getglobal},
+ {"gettagmethod", luaB_gettagmethod},
+ {"globals", luaB_globals},
+ {"newtag", luaB_newtag},
+ {"next", luaB_next},
+ {"print", luaB_print},
+ {"rawget", luaB_rawget},
+ {"rawset", luaB_rawset},
+ {"rawgettable", luaB_rawget}, /* for compatibility */
+ {"rawsettable", luaB_rawset}, /* for compatibility */
+ {"setglobal", luaB_setglobal},
+ {"settag", luaB_settag},
+ {"settagmethod", luaB_settagmethod},
+ {"tag", luaB_tag},
+ {"tonumber", luaB_tonumber},
+ {"tostring", luaB_tostring},
+ {"type", luaB_type},
+ {"assert", luaB_assert},
+ {"getn", luaB_getn},
+ {"sort", luaB_sort},
+ {"tinsert", luaB_tinsert},
+ {"tremove", luaB_tremove}
+};
+
+
+
+LUALIB_API void lua_baselibopen (lua_State *L) {
+ luaL_openl(L, base_funcs);
+ lua_pushstring(L, LUA_VERSION);
+ lua_setglobal(L, "_VERSION");
+ deprecated_funcs(L);
+}
+
diff --git a/src/lua/lcode.c b/src/lua/lcode.c
new file mode 100644
index 00000000..89de4a55
--- /dev/null
+++ b/src/lua/lcode.c
@@ -0,0 +1,701 @@
+/*
+** $Id: lcode.c,v 1.4 2004/06/04 13:42:10 neil Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include "stdlib.h"
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldo.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+void luaK_error (LexState *ls, const char *msg) {
+ luaX_error(ls, msg, ls->t.token);
+}
+
+
+/*
+** Returns the the previous instruction, for optimizations.
+** If there is a jump target between this and the current instruction,
+** returns a dummy instruction to avoid wrong optimizations.
+*/
+static Instruction previous_instruction (FuncState *fs) {
+ if (fs->pc > fs->lasttarget) /* no jumps to current position? */
+ return fs->f->code[fs->pc-1]; /* returns previous instruction */
+ else
+ return CREATE_0(OP_END); /* no optimizations after an `END' */
+}
+
+
+int luaK_jump (FuncState *fs) {
+ int j = luaK_code1(fs, OP_JMP, NO_JUMP);
+ if (j == fs->lasttarget) { /* possible jumps to this jump? */
+ luaK_concat(fs, &j, fs->jlt); /* keep them on hold */
+ fs->jlt = NO_JUMP;
+ }
+ return j;
+}
+
+
+static void luaK_fixjump (FuncState *fs, int pc, int dest) {
+ Instruction *jmp = &fs->f->code[pc];
+ if (dest == NO_JUMP)
+ SETARG_S(*jmp, NO_JUMP); /* point to itself to represent end of list */
+ else { /* jump is relative to position following jump instruction */
+ int offset = dest-(pc+1);
+ if (abs(offset) > MAXARG_S)
+ luaK_error(fs->ls, "control structure too long");
+ SETARG_S(*jmp, offset);
+ }
+}
+
+
+static int luaK_getjump (FuncState *fs, int pc) {
+ int offset = GETARG_S(fs->f->code[pc]);
+ if (offset == NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return (pc+1)+offset; /* turn offset into absolute position */
+}
+
+
+/*
+** returns current `pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+** discharge list of jumps to last target.
+*/
+int luaK_getlabel (FuncState *fs) {
+ if (fs->pc != fs->lasttarget) {
+ int lasttarget = fs->lasttarget;
+ fs->lasttarget = fs->pc;
+ luaK_patchlist(fs, fs->jlt, lasttarget); /* discharge old list `jlt' */
+ fs->jlt = NO_JUMP; /* nobody jumps to this new label (yet) */
+ }
+ return fs->pc;
+}
+
+
+void luaK_deltastack (FuncState *fs, int delta) {
+ fs->stacklevel += delta;
+ if (fs->stacklevel > fs->f->maxstacksize) {
+ if (fs->stacklevel > MAXSTACK)
+ luaK_error(fs->ls, "function or expression too complex");
+ fs->f->maxstacksize = fs->stacklevel;
+ }
+}
+
+
+void luaK_kstr (LexState *ls, int c) {
+ luaK_code1(ls->fs, OP_PUSHSTRING, c);
+}
+
+
+static int number_constant (FuncState *fs, Number r) {
+ /* check whether `r' has appeared within the last LOOKBACKNUMS entries */
+ Proto *f = fs->f;
+ int c = f->nknum;
+ int lim = c < LOOKBACKNUMS ? 0 : c-LOOKBACKNUMS;
+ while (--c >= lim)
+ if (f->knum[c] == r) return c;
+ /* not found; create a new entry */
+ luaM_growvector(fs->L, f->knum, f->nknum, 1, Number,
+ "constant table overflow", MAXARG_U);
+ c = f->nknum++;
+ f->knum[c] = r;
+ return c;
+}
+
+
+void luaK_number (FuncState *fs, Number f) {
+ if (f <= (Number)MAXARG_S && (Number)(int)f == f)
+ luaK_code1(fs, OP_PUSHINT, (int)f); /* f has a short integer value */
+ else
+ luaK_code1(fs, OP_PUSHNUM, number_constant(fs, f));
+}
+
+
+void luaK_adjuststack (FuncState *fs, int n) {
+ if (n > 0)
+ luaK_code1(fs, OP_POP, n);
+ else
+ luaK_code1(fs, OP_PUSHNIL, -n);
+}
+
+
+int luaK_lastisopen (FuncState *fs) {
+ /* check whether last instruction is an open function call */
+ Instruction i = previous_instruction(fs);
+ if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET)
+ return 1;
+ else return 0;
+}
+
+
+void luaK_setcallreturns (FuncState *fs, int nresults) {
+ if (luaK_lastisopen(fs)) { /* expression is an open function call? */
+ SETARG_B(fs->f->code[fs->pc-1], nresults); /* set number of results */
+ luaK_deltastack(fs, nresults); /* push results */
+ }
+}
+
+
+static int discharge (FuncState *fs, expdesc *var) {
+ switch (var->k) {
+ case VLOCAL:
+ luaK_code1(fs, OP_GETLOCAL, var->u.index);
+ break;
+ case VGLOBAL:
+ luaK_code1(fs, OP_GETGLOBAL, var->u.index);
+ break;
+ case VINDEXED:
+ luaK_code0(fs, OP_GETTABLE);
+ break;
+ case VEXP:
+ return 0; /* nothing to do */
+ }
+ var->k = VEXP;
+ var->u.l.t = var->u.l.f = NO_JUMP;
+ return 1;
+}
+
+
+static void discharge1 (FuncState *fs, expdesc *var) {
+ discharge(fs, var);
+ /* if it has jumps then it is already discharged */
+ if (var->u.l.t == NO_JUMP && var->u.l.f == NO_JUMP)
+ luaK_setcallreturns(fs, 1); /* call must return 1 value */
+}
+
+
+void luaK_storevar (LexState *ls, const expdesc *var) {
+ FuncState *fs = ls->fs;
+ switch (var->k) {
+ case VLOCAL:
+ luaK_code1(fs, OP_SETLOCAL, var->u.index);
+ break;
+ case VGLOBAL:
+ luaK_code1(fs, OP_SETGLOBAL, var->u.index);
+ break;
+ case VINDEXED: /* table is at top-3; pop 3 elements after operation */
+ luaK_code2(fs, OP_SETTABLE, 3, 3);
+ break;
+ default:
+ LUA_INTERNALERROR("invalid var kind to store");
+ }
+}
+
+
+static OpCode invertjump (OpCode op) {
+ switch (op) {
+ case OP_JMPNE: return OP_JMPEQ;
+ case OP_JMPEQ: return OP_JMPNE;
+ case OP_JMPLT: return OP_JMPGE;
+ case OP_JMPLE: return OP_JMPGT;
+ case OP_JMPGT: return OP_JMPLE;
+ case OP_JMPGE: return OP_JMPLT;
+ case OP_JMPT: case OP_JMPONT: return OP_JMPF;
+ case OP_JMPF: case OP_JMPONF: return OP_JMPT;
+ default:
+ LUA_INTERNALERROR("invalid jump instruction");
+ return OP_END; /* to avoid warnings */
+ }
+}
+
+
+static void luaK_patchlistaux (FuncState *fs, int list, int target,
+ OpCode special, int special_target) {
+ Instruction *code = fs->f->code;
+ while (list != NO_JUMP) {
+ int next = luaK_getjump(fs, list);
+ Instruction *i = &code[list];
+ OpCode op = GET_OPCODE(*i);
+ if (op == special) /* this `op' already has a value */
+ luaK_fixjump(fs, list, special_target);
+ else {
+ luaK_fixjump(fs, list, target); /* do the patch */
+ if (op == OP_JMPONT) /* remove eventual values */
+ SET_OPCODE(*i, OP_JMPT);
+ else if (op == OP_JMPONF)
+ SET_OPCODE(*i, OP_JMPF);
+ }
+ list = next;
+ }
+}
+
+
+void luaK_patchlist (FuncState *fs, int list, int target) {
+ if (target == fs->lasttarget) /* same target that list `jlt'? */
+ luaK_concat(fs, &fs->jlt, list); /* delay fixing */
+ else
+ luaK_patchlistaux(fs, list, target, OP_END, 0);
+}
+
+
+static int need_value (FuncState *fs, int list, OpCode hasvalue) {
+ /* check whether list has a jump without a value */
+ for (; list != NO_JUMP; list = luaK_getjump(fs, list))
+ if (GET_OPCODE(fs->f->code[list]) != hasvalue) return 1;
+ return 0; /* not found */
+}
+
+
+void luaK_concat (FuncState *fs, int *l1, int l2) {
+ if (*l1 == NO_JUMP)
+ *l1 = l2;
+ else {
+ int list = *l1;
+ for (;;) { /* traverse `l1' */
+ int next = luaK_getjump(fs, list);
+ if (next == NO_JUMP) { /* end of list? */
+ luaK_fixjump(fs, list, l2);
+ return;
+ }
+ list = next;
+ }
+ }
+}
+
+
+static void luaK_testgo (FuncState *fs, expdesc *v, int invert, OpCode jump) {
+ int prevpos; /* position of last instruction */
+ Instruction *previous;
+ int *golist, *exitlist;
+ if (!invert) {
+ golist = &v->u.l.f; /* go if false */
+ exitlist = &v->u.l.t; /* exit if true */
+ }
+ else {
+ golist = &v->u.l.t; /* go if true */
+ exitlist = &v->u.l.f; /* exit if false */
+ }
+ discharge1(fs, v);
+ prevpos = fs->pc-1;
+ previous = &fs->f->code[prevpos];
+ LUA_ASSERT(*previous==previous_instruction(fs), "no jump allowed here");
+ if (!ISJUMP(GET_OPCODE(*previous)))
+ prevpos = luaK_code1(fs, jump, NO_JUMP);
+ else { /* last instruction is already a jump */
+ if (invert)
+ SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous)));
+ }
+ luaK_concat(fs, exitlist, prevpos); /* insert last jump in `exitlist' */
+ luaK_patchlist(fs, *golist, luaK_getlabel(fs));
+ *golist = NO_JUMP;
+}
+
+
+void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue) {
+ luaK_testgo(fs, v, 1, keepvalue ? OP_JMPONF : OP_JMPF);
+}
+
+
+static void luaK_goiffalse (FuncState *fs, expdesc *v, int keepvalue) {
+ luaK_testgo(fs, v, 0, keepvalue ? OP_JMPONT : OP_JMPT);
+}
+
+
+static int code_label (FuncState *fs, OpCode op, int arg) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_code1(fs, op, arg);
+}
+
+
+void luaK_tostack (LexState *ls, expdesc *v, int onlyone) {
+ FuncState *fs = ls->fs;
+ if (!discharge(fs, v)) { /* `v' is an expression? */
+ OpCode previous = GET_OPCODE(fs->f->code[fs->pc-1]);
+ if (!ISJUMP(previous) && v->u.l.f == NO_JUMP && v->u.l.t == NO_JUMP) {
+ /* expression has no jumps */
+ if (onlyone)
+ luaK_setcallreturns(fs, 1); /* call must return 1 value */
+ }
+ else { /* expression has jumps */
+ int final; /* position after whole expression */
+ int j = NO_JUMP; /* eventual jump over values */
+ int p_nil = NO_JUMP; /* position of an eventual PUSHNIL */
+ int p_1 = NO_JUMP; /* position of an eventual PUSHINT */
+ if (ISJUMP(previous) || need_value(fs, v->u.l.f, OP_JMPONF)
+ || need_value(fs, v->u.l.t, OP_JMPONT)) {
+ /* expression needs values */
+ if (ISJUMP(previous))
+ luaK_concat(fs, &v->u.l.t, fs->pc-1); /* put `previous' in t. list */
+ else {
+ j = code_label(fs, OP_JMP, NO_JUMP); /* to jump over both pushes */
+ /* correct stack for compiler and symbolic execution */
+ luaK_adjuststack(fs, 1);
+ }
+ p_nil = code_label(fs, OP_PUSHNILJMP, 0);
+ p_1 = code_label(fs, OP_PUSHINT, 1);
+ luaK_patchlist(fs, j, luaK_getlabel(fs));
+ }
+ final = luaK_getlabel(fs);
+ luaK_patchlistaux(fs, v->u.l.f, p_nil, OP_JMPONF, final);
+ luaK_patchlistaux(fs, v->u.l.t, p_1, OP_JMPONT, final);
+ v->u.l.f = v->u.l.t = NO_JUMP;
+ }
+ }
+}
+
+
+void luaK_prefix (LexState *ls, UnOpr op, expdesc *v) {
+ FuncState *fs = ls->fs;
+ if (op == OPR_MINUS) {
+ luaK_tostack(ls, v, 1);
+ luaK_code0(fs, OP_MINUS);
+ }
+ else { /* op == NOT */
+ Instruction *previous;
+ discharge1(fs, v);
+ previous = &fs->f->code[fs->pc-1];
+ if (ISJUMP(GET_OPCODE(*previous)))
+ SET_OPCODE(*previous, invertjump(GET_OPCODE(*previous)));
+ else
+ luaK_code0(fs, OP_NOT);
+ /* interchange true and false lists */
+ { int temp = v->u.l.f; v->u.l.f = v->u.l.t; v->u.l.t = temp; }
+ }
+}
+
+
+void luaK_infix (LexState *ls, BinOpr op, expdesc *v) {
+ FuncState *fs = ls->fs;
+ switch (op) {
+ case OPR_AND:
+ luaK_goiftrue(fs, v, 1);
+ break;
+ case OPR_OR:
+ luaK_goiffalse(fs, v, 1);
+ break;
+ default:
+ luaK_tostack(ls, v, 1); /* all other binary operators need a value */
+ }
+}
+
+
+
+static const struct {
+ OpCode opcode; /* opcode for each binary operator */
+ int arg; /* default argument for the opcode */
+} codes[] = { /* ORDER OPR */
+ {OP_ADD, 0}, {OP_SUB, 0}, {OP_MULT, 0}, {OP_DIV, 0},
+ {OP_POW, 0}, {OP_CONCAT, 2},
+ {OP_JMPNE, NO_JUMP}, {OP_JMPEQ, NO_JUMP},
+ {OP_JMPLT, NO_JUMP}, {OP_JMPLE, NO_JUMP},
+ {OP_JMPGT, NO_JUMP}, {OP_JMPGE, NO_JUMP}
+};
+
+
+void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2) {
+ FuncState *fs = ls->fs;
+ switch (op) {
+ case OPR_AND: {
+ LUA_ASSERT(v1->u.l.t == NO_JUMP, "list must be closed");
+ discharge1(fs, v2);
+ v1->u.l.t = v2->u.l.t;
+ luaK_concat(fs, &v1->u.l.f, v2->u.l.f);
+ break;
+ }
+ case OPR_OR: {
+ LUA_ASSERT(v1->u.l.f == NO_JUMP, "list must be closed");
+ discharge1(fs, v2);
+ v1->u.l.f = v2->u.l.f;
+ luaK_concat(fs, &v1->u.l.t, v2->u.l.t);
+ break;
+ }
+ default: {
+ luaK_tostack(ls, v2, 1); /* `v2' must be a value */
+ luaK_code1(fs, codes[op].opcode, codes[op].arg);
+ }
+ }
+}
+
+
+static void codelineinfo (FuncState *fs) {
+ Proto *f = fs->f;
+ LexState *ls = fs->ls;
+ if (ls->lastline > fs->lastline) {
+ luaM_growvector(fs->L, f->lineinfo, f->nlineinfo, 2, int,
+ "line info overflow", MAX_INT);
+ if (ls->lastline > fs->lastline+1)
+ f->lineinfo[f->nlineinfo++] = -(ls->lastline - (fs->lastline+1));
+ f->lineinfo[f->nlineinfo++] = fs->pc;
+ fs->lastline = ls->lastline;
+ }
+}
+
+
+int luaK_code0 (FuncState *fs, OpCode o) {
+ return luaK_code2(fs, o, 0, 0);
+}
+
+
+int luaK_code1 (FuncState *fs, OpCode o, int arg1) {
+ return luaK_code2(fs, o, arg1, 0);
+}
+
+
+int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) {
+ Instruction i = previous_instruction(fs);
+ int delta = luaK_opproperties[o].push - luaK_opproperties[o].pop;
+ int optm = 0; /* 1 when there is an optimization */
+ switch (o) {
+ case OP_CLOSURE: {
+ delta = -arg2+1;
+ break;
+ }
+ case OP_SETTABLE: {
+ delta = -arg2;
+ break;
+ }
+ case OP_SETLIST: {
+ if (arg2 == 0) return NO_JUMP; /* nothing to do */
+ delta = -arg2;
+ break;
+ }
+ case OP_SETMAP: {
+ if (arg1 == 0) return NO_JUMP; /* nothing to do */
+ delta = -2*arg1;
+ break;
+ }
+ case OP_RETURN: {
+ if (GET_OPCODE(i) == OP_CALL && GETARG_B(i) == MULT_RET) {
+ SET_OPCODE(i, OP_TAILCALL);
+ SETARG_B(i, arg1);
+ optm = 1;
+ }
+ break;
+ }
+ case OP_PUSHNIL: {
+ if (arg1 == 0) return NO_JUMP; /* nothing to do */
+ delta = arg1;
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHNIL: SETARG_U(i, GETARG_U(i)+arg1); optm = 1; break;
+ default: break;
+ }
+ break;
+ }
+ case OP_POP: {
+ if (arg1 == 0) return NO_JUMP; /* nothing to do */
+ delta = -arg1;
+ switch(GET_OPCODE(i)) {
+ case OP_SETTABLE: SETARG_B(i, GETARG_B(i)+arg1); optm = 1; break;
+ default: break;
+ }
+ break;
+ }
+ case OP_GETTABLE: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHSTRING: /* `t.x' */
+ SET_OPCODE(i, OP_GETDOTTED);
+ optm = 1;
+ break;
+ case OP_GETLOCAL: /* `t[i]' */
+ SET_OPCODE(i, OP_GETINDEXED);
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_ADD: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHINT: SET_OPCODE(i, OP_ADDI); optm = 1; break; /* `a+k' */
+ default: break;
+ }
+ break;
+ }
+ case OP_SUB: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHINT: /* `a-k' */
+ i = CREATE_S(OP_ADDI, -GETARG_S(i));
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_CONCAT: {
+ delta = -arg1+1;
+ switch(GET_OPCODE(i)) {
+ case OP_CONCAT: /* `a..b..c' */
+ SETARG_U(i, GETARG_U(i)+1);
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_MINUS: {
+ switch(GET_OPCODE(i)) {
+ case OP_PUSHINT: /* `-k' */
+ SETARG_S(i, -GETARG_S(i));
+ optm = 1;
+ break;
+ case OP_PUSHNUM: /* `-k' */
+ SET_OPCODE(i, OP_PUSHNEGNUM);
+ optm = 1;
+ break;
+ default: break;
+ }
+ break;
+ }
+ case OP_JMPNE: {
+ if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a~=nil' */
+ i = CREATE_S(OP_JMPT, NO_JUMP);
+ optm = 1;
+ }
+ break;
+ }
+ case OP_JMPEQ: {
+ if (i == CREATE_U(OP_PUSHNIL, 1)) { /* `a==nil' */
+ i = CREATE_0(OP_NOT);
+ delta = -1; /* just undo effect of previous PUSHNIL */
+ optm = 1;
+ }
+ break;
+ }
+ case OP_JMPT:
+ case OP_JMPONT: {
+ switch (GET_OPCODE(i)) {
+ case OP_NOT: {
+ i = CREATE_S(OP_JMPF, NO_JUMP);
+ optm = 1;
+ break;
+ }
+ case OP_PUSHINT: {
+ if (o == OP_JMPT) { /* JMPONT must keep original integer value */
+ i = CREATE_S(OP_JMP, NO_JUMP);
+ optm = 1;
+ }
+ break;
+ }
+ case OP_PUSHNIL: {
+ if (GETARG_U(i) == 1) {
+ fs->pc--; /* erase previous instruction */
+ luaK_deltastack(fs, -1); /* correct stack */
+ return NO_JUMP;
+ }
+ break;
+ }
+ default: break;
+ }
+ break;
+ }
+ case OP_JMPF:
+ case OP_JMPONF: {
+ switch (GET_OPCODE(i)) {
+ case OP_NOT: {
+ i = CREATE_S(OP_JMPT, NO_JUMP);
+ optm = 1;
+ break;
+ }
+ case OP_PUSHINT: { /* `while 1 do ...' */
+ fs->pc--; /* erase previous instruction */
+ luaK_deltastack(fs, -1); /* correct stack */
+ return NO_JUMP;
+ }
+ case OP_PUSHNIL: { /* `repeat ... until nil' */
+ if (GETARG_U(i) == 1) {
+ i = CREATE_S(OP_JMP, NO_JUMP);
+ optm = 1;
+ }
+ break;
+ }
+ default: break;
+ }
+ break;
+ }
+ case OP_GETDOTTED:
+ case OP_GETINDEXED:
+ case OP_TAILCALL:
+ case OP_ADDI: {
+ LUA_INTERNALERROR("instruction used only for optimizations");
+ break;
+ }
+ default: {
+ LUA_ASSERT(delta != VD, "invalid delta");
+ break;
+ }
+ }
+ luaK_deltastack(fs, delta);
+ if (optm) { /* optimize: put instruction in place of last one */
+ fs->f->code[fs->pc-1] = i; /* change previous instruction */
+ return fs->pc-1; /* do not generate new instruction */
+ }
+ /* else build new instruction */
+ switch ((enum Mode)luaK_opproperties[o].mode) {
+ case iO: i = CREATE_0(o); break;
+ case iU: i = CREATE_U(o, arg1); break;
+ case iS: i = CREATE_S(o, arg1); break;
+ case iAB: i = CREATE_AB(o, arg1, arg2); break;
+ }
+ codelineinfo(fs);
+ /* put new instruction in code array */
+ luaM_growvector(fs->L, fs->f->code, fs->pc, 1, Instruction,
+ "code size overflow", MAX_INT);
+ fs->f->code[fs->pc] = i;
+ return fs->pc++;
+}
+
+
+const struct OpProperties luaK_opproperties[NUM_OPCODES] = {
+ {iO, 0, 0}, /* OP_END */
+ {iU, 0, 0}, /* OP_RETURN */
+ {iAB, 0, 0}, /* OP_CALL */
+ {iAB, 0, 0}, /* OP_TAILCALL */
+ {iU, VD, 0}, /* OP_PUSHNIL */
+ {iU, VD, 0}, /* OP_POP */
+ {iS, 1, 0}, /* OP_PUSHINT */
+ {iU, 1, 0}, /* OP_PUSHSTRING */
+ {iU, 1, 0}, /* OP_PUSHNUM */
+ {iU, 1, 0}, /* OP_PUSHNEGNUM */
+ {iU, 1, 0}, /* OP_PUSHUPVALUE */
+ {iU, 1, 0}, /* OP_GETLOCAL */
+ {iU, 1, 0}, /* OP_GETGLOBAL */
+ {iO, 1, 2}, /* OP_GETTABLE */
+ {iU, 1, 1}, /* OP_GETDOTTED */
+ {iU, 1, 1}, /* OP_GETINDEXED */
+ {iU, 2, 1}, /* OP_PUSHSELF */
+ {iU, 1, 0}, /* OP_CREATETABLE */
+ {iU, 0, 1}, /* OP_SETLOCAL */
+ {iU, 0, 1}, /* OP_SETGLOBAL */
+ {iAB, VD, 0}, /* OP_SETTABLE */
+ {iAB, VD, 0}, /* OP_SETLIST */
+ {iU, VD, 0}, /* OP_SETMAP */
+ {iO, 1, 2}, /* OP_ADD */
+ {iS, 1, 1}, /* OP_ADDI */
+ {iO, 1, 2}, /* OP_SUB */
+ {iO, 1, 2}, /* OP_MULT */
+ {iO, 1, 2}, /* OP_DIV */
+ {iO, 1, 2}, /* OP_POW */
+ {iU, VD, 0}, /* OP_CONCAT */
+ {iO, 1, 1}, /* OP_MINUS */
+ {iO, 1, 1}, /* OP_NOT */
+ {iS, 0, 2}, /* OP_JMPNE */
+ {iS, 0, 2}, /* OP_JMPEQ */
+ {iS, 0, 2}, /* OP_JMPLT */
+ {iS, 0, 2}, /* OP_JMPLE */
+ {iS, 0, 2}, /* OP_JMPGT */
+ {iS, 0, 2}, /* OP_JMPGE */
+ {iS, 0, 1}, /* OP_JMPT */
+ {iS, 0, 1}, /* OP_JMPF */
+ {iS, 0, 1}, /* OP_JMPONT */
+ {iS, 0, 1}, /* OP_JMPONF */
+ {iS, 0, 0}, /* OP_JMP */
+ {iO, 0, 0}, /* OP_PUSHNILJMP */
+ {iS, 0, 0}, /* OP_FORPREP */
+ {iS, 0, 3}, /* OP_FORLOOP */
+ {iS, 2, 0}, /* OP_LFORPREP */
+ {iS, 0, 3}, /* OP_LFORLOOP */
+ {iAB, VD, 0} /* OP_CLOSURE */
+};
+
diff --git a/src/lua/lcode.h b/src/lua/lcode.h
new file mode 100644
index 00000000..c413c897
--- /dev/null
+++ b/src/lua/lcode.h
@@ -0,0 +1,70 @@
+/*
+** $Id: lcode.h,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcode_h
+#define lcode_h
+
+#include "llex.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+#define NO_JUMP (-1)
+
+
+/*
+** grep "ORDER OPR" if you change these enums
+*/
+typedef enum BinOpr {
+ OPR_ADD, OPR_SUB, OPR_MULT, OPR_DIV, OPR_POW,
+ OPR_CONCAT,
+ OPR_NE, OPR_EQ, OPR_LT, OPR_LE, OPR_GT, OPR_GE,
+ OPR_AND, OPR_OR,
+ OPR_NOBINOPR
+} BinOpr;
+
+typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_NOUNOPR } UnOpr;
+
+
+enum Mode {iO, iU, iS, iAB}; /* instruction format */
+
+#define VD 100 /* flag for variable delta */
+
+extern const struct OpProperties {
+ char mode;
+ unsigned char push;
+ unsigned char pop;
+} luaK_opproperties[NUM_OPCODES];
+
+
+void luaK_error (LexState *ls, const char *msg);
+int luaK_code0 (FuncState *fs, OpCode o);
+int luaK_code1 (FuncState *fs, OpCode o, int arg1);
+int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2);
+int luaK_jump (FuncState *fs);
+void luaK_patchlist (FuncState *fs, int list, int target);
+void luaK_concat (FuncState *fs, int *l1, int l2);
+void luaK_goiftrue (FuncState *fs, expdesc *v, int keepvalue);
+int luaK_getlabel (FuncState *fs);
+void luaK_deltastack (FuncState *fs, int delta);
+void luaK_kstr (LexState *ls, int c);
+void luaK_number (FuncState *fs, Number f);
+void luaK_adjuststack (FuncState *fs, int n);
+int luaK_lastisopen (FuncState *fs);
+void luaK_setcallreturns (FuncState *fs, int nresults);
+void luaK_tostack (LexState *ls, expdesc *v, int onlyone);
+void luaK_storevar (LexState *ls, const expdesc *var);
+void luaK_prefix (LexState *ls, UnOpr op, expdesc *v);
+void luaK_infix (LexState *ls, BinOpr op, expdesc *v);
+void luaK_posfix (LexState *ls, BinOpr op, expdesc *v1, expdesc *v2);
+
+
+#endif
diff --git a/src/lua/ldblib.c b/src/lua/ldblib.c
new file mode 100644
index 00000000..481f1d6f
--- /dev/null
+++ b/src/lua/ldblib.c
@@ -0,0 +1,188 @@
+/*
+** $Id: ldblib.c,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+
+static void settabss (lua_State *L, const char *i, const char *v) {
+ lua_pushstring(L, i);
+ lua_pushstring(L, v);
+ lua_settable(L, -3);
+}
+
+
+static void settabsi (lua_State *L, const char *i, int v) {
+ lua_pushstring(L, i);
+ lua_pushnumber(L, v);
+ lua_settable(L, -3);
+}
+
+
+static int getinfo (lua_State *L) {
+ lua_Debug ar;
+ const char *options = luaL_opt_string(L, 2, "flnSu");
+ char buff[20];
+ if (lua_isnumber(L, 1)) {
+ if (!lua_getstack(L, (int)lua_tonumber(L, 1), &ar)) {
+ lua_pushnil(L); /* level out of range */
+ return 1;
+ }
+ }
+ else if (lua_isfunction(L, 1)) {
+ lua_pushvalue(L, 1);
+ sprintf(buff, ">%.10s", options);
+ options = buff;
+ }
+ else
+ luaL_argerror(L, 1, "function or level expected");
+ if (!lua_getinfo(L, options, &ar))
+ luaL_argerror(L, 2, "invalid option");
+ lua_newtable(L);
+ for (; *options; options++) {
+ switch (*options) {
+ case 'S':
+ settabss(L, "source", ar.source);
+ if (ar.source)
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabss(L, "what", ar.what);
+ break;
+ case 'l':
+ settabsi(L, "currentline", ar.currentline);
+ break;
+ case 'u':
+ settabsi(L, "nups", ar.nups);
+ break;
+ case 'n':
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ break;
+ case 'f':
+ lua_pushstring(L, "func");
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ break;
+ }
+ }
+ return 1; /* return table */
+}
+
+
+static int getlocal (lua_State *L) {
+ lua_Debug ar;
+ const char *name;
+ if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */
+ luaL_argerror(L, 1, "level out of range");
+ name = lua_getlocal(L, &ar, luaL_check_int(L, 2));
+ if (name) {
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ return 2;
+ }
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int setlocal (lua_State *L) {
+ lua_Debug ar;
+ if (!lua_getstack(L, luaL_check_int(L, 1), &ar)) /* level out of range? */
+ luaL_argerror(L, 1, "level out of range");
+ luaL_checkany(L, 3);
+ lua_pushstring(L, lua_setlocal(L, &ar, luaL_check_int(L, 2)));
+ return 1;
+}
+
+
+
+/* dummy variables (to define unique addresses) */
+static char key1, key2;
+#define KEY_CALLHOOK (&key1)
+#define KEY_LINEHOOK (&key2)
+
+
+static void hookf (lua_State *L, void *key) {
+ lua_getregistry(L);
+ lua_pushuserdata(L, key);
+ lua_gettable(L, -2);
+ if (lua_isfunction(L, -1)) {
+ lua_pushvalue(L, 1);
+ lua_rawcall(L, 1, 0);
+ }
+ else
+ lua_pop(L, 1); /* pop result from gettable */
+ lua_pop(L, 1); /* pop table */
+}
+
+
+static void callf (lua_State *L, lua_Debug *ar) {
+ lua_pushstring(L, ar->event);
+ hookf(L, KEY_CALLHOOK);
+}
+
+
+static void linef (lua_State *L, lua_Debug *ar) {
+ lua_pushnumber(L, ar->currentline);
+ hookf(L, KEY_LINEHOOK);
+}
+
+
+static void sethook (lua_State *L, void *key, lua_Hook hook,
+ lua_Hook (*sethookf)(lua_State * L, lua_Hook h)) {
+ lua_settop(L, 1);
+ if (lua_isnil(L, 1))
+ (*sethookf)(L, NULL);
+ else if (lua_isfunction(L, 1))
+ (*sethookf)(L, hook);
+ else
+ luaL_argerror(L, 1, "function expected");
+ lua_getregistry(L);
+ lua_pushuserdata(L, key);
+ lua_pushvalue(L, -1); /* dup key */
+ lua_gettable(L, -3); /* get old value */
+ lua_pushvalue(L, -2); /* key (again) */
+ lua_pushvalue(L, 1);
+ lua_settable(L, -5); /* set new value */
+}
+
+
+static int setcallhook (lua_State *L) {
+ sethook(L, KEY_CALLHOOK, callf, lua_setcallhook);
+ return 1;
+}
+
+
+static int setlinehook (lua_State *L) {
+ sethook(L, KEY_LINEHOOK, linef, lua_setlinehook);
+ return 1;
+}
+
+
+static const struct luaL_reg dblib[] = {
+ {"getlocal", getlocal},
+ {"getinfo", getinfo},
+ {"setcallhook", setcallhook},
+ {"setlinehook", setlinehook},
+ {"setlocal", setlocal}
+};
+
+
+LUALIB_API void lua_dblibopen (lua_State *L) {
+ luaL_openl(L, dblib);
+}
+
diff --git a/src/lua/ldebug.c b/src/lua/ldebug.c
new file mode 100644
index 00000000..02481b4c
--- /dev/null
+++ b/src/lua/ldebug.c
@@ -0,0 +1,466 @@
+/*
+** $Id: ldebug.c,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Debug Interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "luadebug.h"
+
+
+
+static const char *getfuncname (lua_State *L, StkId f, const char **name);
+
+
+static void setnormalized (TObject *d, const TObject *s) {
+ if (ttype(s) == LUA_TMARK) {
+ clvalue(d) = infovalue(s)->func;
+ ttype(d) = LUA_TFUNCTION;
+ }
+ else *d = *s;
+}
+
+
+static int isLmark (StkId o) {
+ return (o && ttype(o) == LUA_TMARK && !infovalue(o)->func->isC);
+}
+
+
+LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func) {
+ lua_Hook oldhook = L->callhook;
+ L->callhook = func;
+ return oldhook;
+}
+
+
+LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func) {
+ lua_Hook oldhook = L->linehook;
+ L->linehook = func;
+ return oldhook;
+}
+
+
+static StkId aux_stackedfunction (lua_State *L, int level, StkId top) {
+ int i;
+ for (i = (top-1) - L->stack; i>=0; i--) {
+ if (is_T_MARK(L->stack[i].ttype)) {
+ if (level == 0)
+ return L->stack+i;
+ level--;
+ }
+ }
+ return NULL;
+}
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
+ StkId f = aux_stackedfunction(L, level, L->top);
+ if (f == NULL) return 0; /* there is no such level */
+ else {
+ ar->_func = f;
+ return 1;
+ }
+}
+
+
+static int nups (StkId f) {
+ switch (ttype(f)) {
+ case LUA_TFUNCTION:
+ return clvalue(f)->nupvalues;
+ case LUA_TMARK:
+ return infovalue(f)->func->nupvalues;
+ default:
+ return 0;
+ }
+}
+
+
+int luaG_getline (int *lineinfo, int pc, int refline, int *prefi) {
+ int refi;
+ if (lineinfo == NULL || pc == -1)
+ return -1; /* no line info or function is not active */
+ refi = prefi ? *prefi : 0;
+ if (lineinfo[refi] < 0)
+ refline += -lineinfo[refi++];
+ LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info");
+ while (lineinfo[refi] > pc) {
+ refline--;
+ refi--;
+ if (lineinfo[refi] < 0)
+ refline -= -lineinfo[refi--];
+ LUA_ASSERT(lineinfo[refi] >= 0, "invalid line info");
+ }
+ for (;;) {
+ int nextline = refline + 1;
+ int nextref = refi + 1;
+ if (lineinfo[nextref] < 0)
+ nextline += -lineinfo[nextref++];
+ LUA_ASSERT(lineinfo[nextref] >= 0, "invalid line info");
+ if (lineinfo[nextref] > pc)
+ break;
+ refline = nextline;
+ refi = nextref;
+ }
+ if (prefi) *prefi = refi;
+ return refline;
+}
+
+
+static int currentpc (StkId f) {
+ CallInfo *ci = infovalue(f);
+ LUA_ASSERT(isLmark(f), "function has no pc");
+ if (ci->pc)
+ return (*ci->pc - ci->func->f.l->code) - 1;
+ else
+ return -1; /* function is not active */
+}
+
+
+static int currentline (StkId f) {
+ if (!isLmark(f))
+ return -1; /* only active lua functions have current-line information */
+ else {
+ CallInfo *ci = infovalue(f);
+ int *lineinfo = ci->func->f.l->lineinfo;
+ return luaG_getline(lineinfo, currentpc(f), 1, NULL);
+ }
+}
+
+
+
+static Proto *getluaproto (StkId f) {
+ return (isLmark(f) ? infovalue(f)->func->f.l : NULL);
+}
+
+
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
+ const char *name;
+ StkId f = ar->_func;
+ Proto *fp = getluaproto(f);
+ if (!fp) return NULL; /* `f' is not a Lua function? */
+ name = luaF_getlocalname(fp, n, currentpc(f));
+ if (!name) return NULL;
+ luaA_pushobject(L, (f+1)+(n-1)); /* push value */
+ return name;
+}
+
+
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
+ const char *name;
+ StkId f = ar->_func;
+ Proto *fp = getluaproto(f);
+ L->top--; /* pop new value */
+ if (!fp) return NULL; /* `f' is not a Lua function? */
+ name = luaF_getlocalname(fp, n, currentpc(f));
+ if (!name || name[0] == '(') return NULL; /* `(' starts private locals */
+ *((f+1)+(n-1)) = *L->top;
+ return name;
+}
+
+
+static void infoLproto (lua_Debug *ar, Proto *f) {
+ ar->source = f->source->str;
+ ar->linedefined = f->lineDefined;
+ ar->what = "Lua";
+}
+
+
+static void funcinfo (lua_State *L, lua_Debug *ar, StkId func) {
+ Closure *cl = NULL;
+ switch (ttype(func)) {
+ case LUA_TFUNCTION:
+ cl = clvalue(func);
+ break;
+ case LUA_TMARK:
+ cl = infovalue(func)->func;
+ break;
+ default:
+ lua_error(L, "value for `lua_getinfo' is not a function");
+ }
+ if (cl->isC) {
+ ar->source = "=C";
+ ar->linedefined = -1;
+ ar->what = "C";
+ }
+ else
+ infoLproto(ar, cl->f.l);
+ luaO_chunkid(ar->short_src, ar->source, sizeof(ar->short_src));
+ if (ar->linedefined == 0)
+ ar->what = "main";
+}
+
+
+static const char *travtagmethods (lua_State *L, const TObject *o) {
+ if (ttype(o) == LUA_TFUNCTION) {
+ int e;
+ for (e=0; e<TM_N; e++) {
+ int t;
+ for (t=0; t<=L->last_tag; t++)
+ if (clvalue(o) == luaT_gettm(L, t, e))
+ return luaT_eventname[e];
+ }
+ }
+ return NULL;
+}
+
+
+static const char *travglobals (lua_State *L, const TObject *o) {
+ Hash *g = L->gt;
+ int i;
+ for (i=0; i<g->size; i++) {
+ if (luaO_equalObj(o, val(node(g, i))) &&
+ ttype(key(node(g, i))) == LUA_TSTRING)
+ return tsvalue(key(node(g, i)))->str;
+ }
+ return NULL;
+}
+
+
+static void getname (lua_State *L, StkId f, lua_Debug *ar) {
+ TObject o;
+ setnormalized(&o, f);
+ /* try to find a name for given function */
+ if ((ar->name = travglobals(L, &o)) != NULL)
+ ar->namewhat = "global";
+ /* not found: try tag methods */
+ else if ((ar->name = travtagmethods(L, &o)) != NULL)
+ ar->namewhat = "tag-method";
+ else ar->namewhat = ""; /* not found at all */
+}
+
+
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
+ StkId func;
+ int isactive = (*what != '>');
+ if (isactive)
+ func = ar->_func;
+ else {
+ what++; /* skip the '>' */
+ func = L->top - 1;
+ }
+ for (; *what; what++) {
+ switch (*what) {
+ case 'S': {
+ funcinfo(L, ar, func);
+ break;
+ }
+ case 'l': {
+ ar->currentline = currentline(func);
+ break;
+ }
+ case 'u': {
+ ar->nups = nups(func);
+ break;
+ }
+ case 'n': {
+ ar->namewhat = (isactive) ? getfuncname(L, func, &ar->name) : NULL;
+ if (ar->namewhat == NULL)
+ getname(L, func, ar);
+ break;
+ }
+ case 'f': {
+ setnormalized(L->top, func);
+ incr_top; /* push function */
+ break;
+ }
+ default: return 0; /* invalid option */
+ }
+ }
+ if (!isactive) L->top--; /* pop function */
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Symbolic Execution
+** =======================================================
+*/
+
+
+static int pushpc (int *stack, int pc, int top, int n) {
+ while (n--)
+ stack[top++] = pc-1;
+ return top;
+}
+
+
+static Instruction luaG_symbexec (const Proto *pt, int lastpc, int stackpos) {
+ int stack[MAXSTACK]; /* stores last instruction that changed a stack entry */
+ const Instruction *code = pt->code;
+ int top = pt->numparams;
+ int pc = 0;
+ if (pt->is_vararg) /* varargs? */
+ top++; /* `arg' */
+ while (pc < lastpc) {
+ const Instruction i = code[pc++];
+ LUA_ASSERT(0 <= top && top <= pt->maxstacksize, "wrong stack");
+ switch (GET_OPCODE(i)) {
+ case OP_RETURN: {
+ LUA_ASSERT(top >= GETARG_U(i), "wrong stack");
+ top = GETARG_U(i);
+ break;
+ }
+ case OP_TAILCALL: {
+ LUA_ASSERT(top >= GETARG_A(i), "wrong stack");
+ top = GETARG_B(i);
+ break;
+ }
+ case OP_CALL: {
+ int nresults = GETARG_B(i);
+ if (nresults == MULT_RET) nresults = 1;
+ LUA_ASSERT(top >= GETARG_A(i), "wrong stack");
+ top = pushpc(stack, pc, GETARG_A(i), nresults);
+ break;
+ }
+ case OP_PUSHNIL: {
+ top = pushpc(stack, pc, top, GETARG_U(i));
+ break;
+ }
+ case OP_POP: {
+ top -= GETARG_U(i);
+ break;
+ }
+ case OP_SETTABLE:
+ case OP_SETLIST: {
+ top -= GETARG_B(i);
+ break;
+ }
+ case OP_SETMAP: {
+ top -= 2*GETARG_U(i);
+ break;
+ }
+ case OP_CONCAT: {
+ top -= GETARG_U(i);
+ stack[top++] = pc-1;
+ break;
+ }
+ case OP_CLOSURE: {
+ top -= GETARG_B(i);
+ stack[top++] = pc-1;
+ break;
+ }
+ case OP_JMPONT:
+ case OP_JMPONF: {
+ int newpc = pc + GETARG_S(i);
+ /* jump is forward and do not skip `lastpc'? */
+ if (pc < newpc && newpc <= lastpc) {
+ stack[top-1] = pc-1; /* value comes from `and'/`or' */
+ pc = newpc; /* do the jump */
+ }
+ else
+ top--; /* do not jump; pop value */
+ break;
+ }
+ default: {
+ OpCode op = GET_OPCODE(i);
+ LUA_ASSERT(luaK_opproperties[op].push != VD,
+ "invalid opcode for default");
+ top -= luaK_opproperties[op].pop;
+ LUA_ASSERT(top >= 0, "wrong stack");
+ top = pushpc(stack, pc, top, luaK_opproperties[op].push);
+ }
+ }
+ }
+ return code[stack[stackpos]];
+}
+
+
+static const char *getobjname (lua_State *L, StkId obj, const char **name) {
+ StkId func = aux_stackedfunction(L, 0, obj);
+ if (!isLmark(func))
+ return NULL; /* not an active Lua function */
+ else {
+ Proto *p = infovalue(func)->func->f.l;
+ int pc = currentpc(func);
+ int stackpos = obj - (func+1); /* func+1 == function base */
+ Instruction i = luaG_symbexec(p, pc, stackpos);
+ LUA_ASSERT(pc != -1, "function must be active");
+ switch (GET_OPCODE(i)) {
+ case OP_GETGLOBAL: {
+ *name = p->kstr[GETARG_U(i)]->str;
+ return "global";
+ }
+ case OP_GETLOCAL: {
+ *name = luaF_getlocalname(p, GETARG_U(i)+1, pc);
+ LUA_ASSERT(*name, "local must exist");
+ return "local";
+ }
+ case OP_PUSHSELF:
+ case OP_GETDOTTED: {
+ *name = p->kstr[GETARG_U(i)]->str;
+ return "field";
+ }
+ default:
+ return NULL; /* no useful name found */
+ }
+ }
+}
+
+
+static const char *getfuncname (lua_State *L, StkId f, const char **name) {
+ StkId func = aux_stackedfunction(L, 0, f); /* calling function */
+ if (!isLmark(func))
+ return NULL; /* not an active Lua function */
+ else {
+ Proto *p = infovalue(func)->func->f.l;
+ int pc = currentpc(func);
+ Instruction i;
+ if (pc == -1) return NULL; /* function is not activated */
+ i = p->code[pc];
+ switch (GET_OPCODE(i)) {
+ case OP_CALL: case OP_TAILCALL:
+ return getobjname(L, (func+1)+GETARG_A(i), name);
+ default:
+ return NULL; /* no useful name found */
+ }
+ }
+}
+
+
+/* }====================================================== */
+
+
+void luaG_typeerror (lua_State *L, StkId o, const char *op) {
+ const char *name;
+ const char *kind = getobjname(L, o, &name);
+ const char *t = luaO_typename(o);
+ if (kind)
+ luaO_verror(L, "attempt to %.30s %.20s `%.40s' (a %.10s value)",
+ op, kind, name, t);
+ else
+ luaO_verror(L, "attempt to %.30s a %.10s value", op, t);
+}
+
+
+void luaG_binerror (lua_State *L, StkId p1, int t, const char *op) {
+ if (ttype(p1) == t) p1++;
+ LUA_ASSERT(ttype(p1) != t, "must be an error");
+ luaG_typeerror(L, p1, op);
+}
+
+
+void luaG_ordererror (lua_State *L, StkId top) {
+ const char *t1 = luaO_typename(top-2);
+ const char *t2 = luaO_typename(top-1);
+ if (t1[2] == t2[2])
+ luaO_verror(L, "attempt to compare two %.10s values", t1);
+ else
+ luaO_verror(L, "attempt to compare %.10s with %.10s", t1, t2);
+}
+
diff --git a/src/lua/ldebug.h b/src/lua/ldebug.h
new file mode 100644
index 00000000..76865616
--- /dev/null
+++ b/src/lua/ldebug.h
@@ -0,0 +1,21 @@
+/*
+** $Id: ldebug.h,v 1.2 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions from Debug Interface module
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldebug_h
+#define ldebug_h
+
+
+#include "lstate.h"
+#include "luadebug.h"
+
+
+void luaG_typeerror (lua_State *L, StkId o, const char *op);
+void luaG_binerror (lua_State *L, StkId p1, int t, const char *op);
+int luaG_getline (int *lineinfo, int pc, int refline, int *refi);
+void luaG_ordererror (lua_State *L, StkId top);
+
+
+#endif
diff --git a/src/lua/ldo.c b/src/lua/ldo.c
new file mode 100644
index 00000000..5f23bfd9
--- /dev/null
+++ b/src/lua/ldo.c
@@ -0,0 +1,385 @@
+/*
+** $Id: ldo.c,v 1.7 2004/06/04 13:42:10 neil Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+#include "lzio.h"
+
+
+/* space to handle stack overflow errors */
+#define EXTRA_STACK (2*LUA_MINSTACK)
+
+
+void luaD_init (lua_State *L, int stacksize) {
+ L->stack = luaM_newvector(L, stacksize+EXTRA_STACK, TObject);
+ L->nblocks += stacksize*sizeof(TObject);
+ L->stack_last = L->stack+(stacksize-1);
+ L->stacksize = stacksize;
+ L->Cbase = L->top = L->stack;
+}
+
+
+void luaD_checkstack (lua_State *L, int n) {
+ if (L->stack_last - L->top <= n) { /* stack overflow? */
+ if (L->stack_last-L->stack > (L->stacksize-1)) {
+ /* overflow while handling overflow */
+ luaD_breakrun(L, LUA_ERRERR); /* break run without error message */
+ }
+ else {
+ L->stack_last += EXTRA_STACK; /* to be used by error message */
+ lua_error(L, "stack overflow");
+ }
+ }
+}
+
+
+static void restore_stack_limit (lua_State *L) {
+ if (L->top - L->stack < L->stacksize - 1)
+ L->stack_last = L->stack + (L->stacksize-1);
+}
+
+
+/*
+** Adjust stack. Set top to base+extra, pushing NILs if needed.
+** (we cannot add base+extra unless we are sure it fits in the stack;
+** otherwise the result of such operation on pointers is undefined)
+*/
+void luaD_adjusttop (lua_State *L, StkId base, int extra) {
+ int diff = extra-(L->top-base);
+ if (diff <= 0)
+ L->top = base+extra;
+ else {
+ luaD_checkstack(L, diff);
+ while (diff--)
+ ttype(L->top++) = LUA_TNIL;
+ }
+}
+
+
+/*
+** Open a hole inside the stack at `pos'
+*/
+static void luaD_openstack (lua_State *L, StkId pos) {
+ int i = L->top-pos;
+ while (i--) pos[i+1] = pos[i];
+ incr_top;
+}
+
+
+static void dohook (lua_State *L, lua_Debug *ar, lua_Hook hook) {
+ StkId old_Cbase = L->Cbase;
+ StkId old_top = L->Cbase = L->top;
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ L->allowhooks = 0; /* cannot call hooks inside a hook */
+ (*hook)(L, ar);
+ LUA_ASSERT(L->allowhooks == 0, "invalid allow");
+ L->allowhooks = 1;
+ L->top = old_top;
+ L->Cbase = old_Cbase;
+}
+
+
+void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook) {
+ if (L->allowhooks) {
+ lua_Debug ar;
+ ar._func = func;
+ ar.event = "line";
+ ar.currentline = line;
+ dohook(L, &ar, linehook);
+ }
+}
+
+
+static void luaD_callHook (lua_State *L, StkId func, lua_Hook callhook,
+ const char *event) {
+ if (L->allowhooks) {
+ lua_Debug ar;
+ ar._func = func;
+ ar.event = event;
+ infovalue(func)->pc = NULL; /* function is not active */
+ dohook(L, &ar, callhook);
+ }
+}
+
+
+static StkId callCclosure (lua_State *L, const struct Closure *cl, StkId base) {
+ int nup = cl->nupvalues; /* number of upvalues */
+ StkId old_Cbase = L->Cbase;
+ int n;
+ L->Cbase = base; /* new base for C function */
+ luaD_checkstack(L, nup+LUA_MINSTACK); /* ensure minimum stack size */
+ for (n=0; n<nup; n++) /* copy upvalues as extra arguments */
+ *(L->top++) = cl->upvalue[n];
+ n = (*cl->f.c)(L); /* do the actual call */
+ L->Cbase = old_Cbase; /* restore old C base */
+ return L->top - n; /* return index of first result */
+}
+
+
+void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults) {
+ StkId base = L->top - nParams;
+ luaD_openstack(L, base);
+ clvalue(base) = f;
+ ttype(base) = LUA_TFUNCTION;
+ luaD_call(L, base, nResults);
+}
+
+
+/*
+** Call a function (C or Lua). The function to be called is at *func.
+** The arguments are on the stack, right after the function.
+** When returns, the results are on the stack, starting at the original
+** function position.
+** The number of results is nResults, unless nResults=LUA_MULTRET.
+*/
+void luaD_call (lua_State *L, StkId func, int nResults) {
+ lua_Hook callhook;
+ StkId firstResult;
+ CallInfo ci;
+ Closure *cl;
+ if (ttype(func) != LUA_TFUNCTION) {
+ /* `func' is not a function; check the `function' tag method */
+ Closure *tm = luaT_gettmbyObj(L, func, TM_FUNCTION);
+ if (tm == NULL)
+ luaG_typeerror(L, func, "call");
+ luaD_openstack(L, func);
+ clvalue(func) = tm; /* tag method is the new function to be called */
+ ttype(func) = LUA_TFUNCTION;
+ }
+ cl = clvalue(func);
+ ci.func = cl;
+ infovalue(func) = &ci;
+ ttype(func) = LUA_TMARK;
+ callhook = L->callhook;
+ if (callhook)
+ luaD_callHook(L, func, callhook, "call");
+ firstResult = (cl->isC ? callCclosure(L, cl, func+1) :
+ luaV_execute(L, cl, func+1));
+ if (callhook) /* same hook that was active at entry */
+ luaD_callHook(L, func, callhook, "return");
+ LUA_ASSERT(ttype(func) == LUA_TMARK, "invalid tag");
+ /* move results to `func' (to erase parameters and function) */
+ if (nResults == LUA_MULTRET) {
+ while (firstResult < L->top) /* copy all results */
+ *func++ = *firstResult++;
+ L->top = func;
+ }
+ else { /* copy at most `nResults' */
+ for (; nResults > 0 && firstResult < L->top; nResults--)
+ *func++ = *firstResult++;
+ L->top = func;
+ for (; nResults > 0; nResults--) { /* if there are not enough results */
+ ttype(L->top) = LUA_TNIL; /* adjust the stack */
+ incr_top; /* must check stack space */
+ }
+ }
+ luaC_checkGC(L);
+}
+
+
+/*
+** Execute a protected call.
+*/
+struct CallS { /* data to `f_call' */
+ StkId func;
+ int nresults;
+};
+
+static void f_call (lua_State *L, void *ud) {
+ struct CallS *c = (struct CallS *)ud;
+ luaD_call(L, c->func, c->nresults);
+}
+
+
+LUA_API int lua_call (lua_State *L, int nargs, int nresults) {
+ StkId func = L->top - (nargs+1); /* function to be called */
+ struct CallS c;
+ int status;
+ c.func = func; c.nresults = nresults;
+ status = luaD_runprotected(L, f_call, &c);
+ if (status != 0) /* an error occurred? */
+ L->top = func; /* remove parameters from the stack */
+ return status;
+}
+
+
+/*
+** Execute a protected parser.
+*/
+struct ParserS { /* data to `f_parser' */
+ ZIO *z;
+ int bin;
+};
+
+static void f_parser (lua_State *L, void *ud) {
+ struct ParserS *p = (struct ParserS *)ud;
+ Proto *tf = p->bin ? luaU_undump(L, p->z) : luaY_parser(L, p->z);
+ luaV_Lclosure(L, tf, 0);
+}
+
+
+static int protectedparser (lua_State *L, ZIO *z, int bin) {
+ struct ParserS p;
+ unsigned long old_blocks;
+ int status;
+ p.z = z; p.bin = bin;
+ luaC_checkGC(L);
+ old_blocks = L->nblocks;
+ status = luaD_runprotected(L, f_parser, &p);
+ if (status == 0) {
+ /* add new memory to threshold (as it probably will stay) */
+ L->GCthreshold += (L->nblocks - old_blocks);
+ }
+ else if (status == LUA_ERRRUN) /* an error occurred: correct error code */
+ status = LUA_ERRSYNTAX;
+ return status;
+}
+
+
+static int parse_file (lua_State *L, const char *filename) {
+ ZIO z;
+ int status;
+ int bin; /* flag for file mode */
+ int c; /* look ahead char */
+ FILE *f = (filename == NULL) ? stdin : fopen(filename, "r");
+ if (f == NULL) return LUA_ERRFILE; /* unable to open file */
+ c = fgetc(f);
+ ungetc(c, f);
+ bin = (c == ID_CHUNK);
+ if (bin && f != stdin) {
+ f = freopen(filename, "rb", f); /* set binary mode */
+ if (f == NULL) return LUA_ERRFILE; /* unable to reopen file */
+ }
+ lua_pushstring(L, "@");
+ lua_pushstring(L, (filename == NULL) ? "(stdin)" : filename);
+ lua_concat(L, 2);
+ filename = lua_tostring(L, -1); /* filename = '@'..filename */
+ lua_pop(L, 1); /* OK: there is no GC during parser */
+ luaZ_Fopen(&z, f, filename);
+ status = protectedparser(L, &z, bin);
+ if (f != stdin)
+ fclose(f);
+ return status;
+}
+
+
+LUA_API int lua_dofile (lua_State *L, const char *filename) {
+ int status = parse_file(L, filename);
+ if (status == 0) /* parse OK? */
+ status = lua_call(L, 0, LUA_MULTRET); /* call main */
+ return status;
+}
+
+
+static int parse_buffer (lua_State *L, const char *buff, size_t size,
+ const char *name) {
+ ZIO z;
+ if (!name) name = "?";
+ luaZ_mopen(&z, buff, size, name);
+ return protectedparser(L, &z, buff[0]==ID_CHUNK);
+}
+
+
+LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name) {
+ int status = parse_buffer(L, buff, size, name);
+ if (status == 0) /* parse OK? */
+ status = lua_call(L, 0, LUA_MULTRET); /* call main */
+ return status;
+}
+
+
+LUA_API int lua_dostring (lua_State *L, const char *str) {
+ return lua_dobuffer(L, str, strlen(str), str);
+}
+
+
+/*
+** {======================================================
+** Error-recover functions (based on long jumps)
+** =======================================================
+*/
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ jmp_buf b;
+ struct lua_longjmp *previous;
+ volatile int status; /* error code */
+};
+
+
+static void message (lua_State *L, const char *s) {
+ const TObject *em = luaH_getglobal(L, LUA_ERRORMESSAGE);
+ if (ttype(em) == LUA_TFUNCTION) {
+ *L->top = *em;
+ incr_top;
+ lua_pushstring(L, s);
+ luaD_call(L, L->top-2, 0);
+ }
+}
+
+
+/*
+** Reports an error, and jumps up to the available recovery label
+*/
+LUA_API void lua_error (lua_State *L, const char *s) {
+ if (s) message(L, s);
+ luaD_breakrun(L, LUA_ERRRUN);
+}
+
+
+void luaD_breakrun (lua_State *L, int errcode) {
+ if (L->errorJmp) {
+ L->errorJmp->status = errcode;
+ longjmp(L->errorJmp->b, 1);
+ }
+ else {
+ if (errcode != LUA_ERRMEM)
+ message(L, "unable to recover; exiting\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud) {
+ StkId oldCbase = L->Cbase;
+ StkId oldtop = L->top;
+ struct lua_longjmp lj;
+ int allowhooks = L->allowhooks;
+ lj.status = 0;
+ lj.previous = L->errorJmp; /* chain new error handler */
+ L->errorJmp = &lj;
+ if (setjmp(lj.b) == 0)
+ (*f)(L, ud);
+ else { /* an error occurred: restore the state */
+ L->allowhooks = allowhooks;
+ L->Cbase = oldCbase;
+ L->top = oldtop;
+ restore_stack_limit(L);
+ }
+ L->errorJmp = lj.previous; /* restore old error handler */
+ return lj.status;
+}
+
+/* }====================================================== */
+
diff --git a/src/lua/ldo.h b/src/lua/ldo.h
new file mode 100644
index 00000000..d948ad35
--- /dev/null
+++ b/src/lua/ldo.h
@@ -0,0 +1,33 @@
+/*
+** $Id: ldo.h,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+** macro to increment stack top.
+** There must be always an empty slot at the L->stack.top
+*/
+#define incr_top {if (L->top == L->stack_last) luaD_checkstack(L, 1); L->top++;}
+
+
+void luaD_init (lua_State *L, int stacksize);
+void luaD_adjusttop (lua_State *L, StkId base, int extra);
+void luaD_lineHook (lua_State *L, StkId func, int line, lua_Hook linehook);
+void luaD_call (lua_State *L, StkId func, int nResults);
+void luaD_callTM (lua_State *L, Closure *f, int nParams, int nResults);
+void luaD_checkstack (lua_State *L, int n);
+
+void luaD_breakrun (lua_State *L, int errcode);
+int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud);
+
+
+#endif
diff --git a/src/lua/lfunc.c b/src/lua/lfunc.c
new file mode 100644
index 00000000..d3427653
--- /dev/null
+++ b/src/lua/lfunc.c
@@ -0,0 +1,109 @@
+/*
+** $Id: lfunc.c,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lfunc.h"
+#include "lmem.h"
+#include "lstate.h"
+
+
+#define sizeclosure(n) ((int)sizeof(Closure) + (int)sizeof(TObject)*((n)-1))
+
+
+Closure *luaF_newclosure (lua_State *L, int nelems) {
+ int size = sizeclosure(nelems);
+ Closure *c = (Closure *)luaM_malloc(L, size);
+ c->next = L->rootcl;
+ L->rootcl = c;
+ c->mark = c;
+ c->nupvalues = nelems;
+ L->nblocks += size;
+ return c;
+}
+
+
+Proto *luaF_newproto (lua_State *L) {
+ Proto *f = luaM_new(L, Proto);
+ f->knum = NULL;
+ f->nknum = 0;
+ f->kstr = NULL;
+ f->nkstr = 0;
+ f->kproto = NULL;
+ f->nkproto = 0;
+ f->code = NULL;
+ f->ncode = 0;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->marked = 0;
+ f->lineinfo = NULL;
+ f->nlineinfo = 0;
+ f->nlocvars = 0;
+ f->locvars = NULL;
+ f->lineDefined = 0;
+ f->source = NULL;
+ f->next = L->rootproto; /* chain in list of protos */
+ L->rootproto = f;
+ return f;
+}
+
+
+static size_t protosize (Proto *f) {
+ return sizeof(Proto)
+ + f->nknum*sizeof(Number)
+ + f->nkstr*sizeof(TString *)
+ + f->nkproto*sizeof(Proto *)
+ + f->ncode*sizeof(Instruction)
+ + f->nlocvars*sizeof(struct LocVar)
+ + f->nlineinfo*sizeof(int);
+}
+
+
+void luaF_protook (lua_State *L, Proto *f, int pc) {
+ f->ncode = pc; /* signal that proto was properly created */
+ L->nblocks += protosize(f);
+}
+
+
+void luaF_freeproto (lua_State *L, Proto *f) {
+ if (f->ncode > 0) /* function was properly created? */
+ L->nblocks -= protosize(f);
+ luaM_free(L, f->code);
+ luaM_free(L, f->locvars);
+ luaM_free(L, f->kstr);
+ luaM_free(L, f->knum);
+ luaM_free(L, f->kproto);
+ luaM_free(L, f->lineinfo);
+ luaM_free(L, f);
+}
+
+
+void luaF_freeclosure (lua_State *L, Closure *c) {
+ L->nblocks -= sizeclosure(c->nupvalues);
+ luaM_free(L, c);
+}
+
+
+/*
+** Look for n-th local variable at line `line' in function `func'.
+** Returns NULL if not found.
+*/
+const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
+ int i;
+ for (i = 0; i<f->nlocvars && f->locvars[i].startpc <= pc; i++) {
+ if (pc < f->locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return f->locvars[i].varname->str;
+ }
+ }
+ return NULL; /* not found */
+}
+
diff --git a/src/lua/lfunc.h b/src/lua/lfunc.h
new file mode 100644
index 00000000..1bd9722d
--- /dev/null
+++ b/src/lua/lfunc.h
@@ -0,0 +1,24 @@
+/*
+** $Id: lfunc.h,v 1.3 2001/11/26 23:00:23 darkgod Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+
+Proto *luaF_newproto (lua_State *L);
+void luaF_protook (lua_State *L, Proto *f, int pc);
+Closure *luaF_newclosure (lua_State *L, int nelems);
+void luaF_freeproto (lua_State *L, Proto *f);
+void luaF_freeclosure (lua_State *L, Closure *c);
+
+const char *luaF_getlocalname (const Proto *func, int local_number, int pc);
+
+
+#endif
diff --git a/src/lua/lgc.c b/src/lua/lgc.c
new file mode 100644
index 00000000..4e8b234d
--- /dev/null
+++ b/src/lua/lgc.c
@@ -0,0 +1,353 @@
+/*
+** $Id: lgc.c,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+typedef struct GCState {
+ Hash *tmark; /* list of marked tables to be visited */
+ Closure *cmark; /* list of marked closures to be visited */
+} GCState;
+
+
+
+static void markobject (GCState *st, TObject *o);
+
+
+/* mark a string; marks larger than 1 cannot be changed */
+#define strmark(s) {if ((s)->marked == 0) (s)->marked = 1;}
+
+
+
+static void protomark (Proto *f) {
+ if (!f->marked) {
+ int i;
+ f->marked = 1;
+ strmark(f->source);
+ for (i=0; i<f->nkstr; i++)
+ strmark(f->kstr[i]);
+ for (i=0; i<f->nkproto; i++)
+ protomark(f->kproto[i]);
+ for (i=0; i<f->nlocvars; i++) /* mark local-variable names */
+ strmark(f->locvars[i].varname);
+ }
+}
+
+
+static void markstack (lua_State *L, GCState *st) {
+ StkId o;
+ for (o=L->stack; o<L->top; o++)
+ markobject(st, o);
+}
+
+
+static void marklock (lua_State *L, GCState *st) {
+ int i;
+ for (i=0; i<L->refSize; i++) {
+ if (L->refArray[i].st == LOCK)
+ markobject(st, &L->refArray[i].o);
+ }
+}
+
+
+static void markclosure (GCState *st, Closure *cl) {
+ if (!ismarked(cl)) {
+ if (!cl->isC)
+ protomark(cl->f.l);
+ cl->mark = st->cmark; /* chain it for later traversal */
+ st->cmark = cl;
+ }
+}
+
+
+static void marktagmethods (lua_State *L, GCState *st) {
+ int e;
+ for (e=0; e<TM_N; e++) {
+ int t;
+ for (t=0; t<=L->last_tag; t++) {
+ Closure *cl = luaT_gettm(L, t, e);
+ if (cl) markclosure(st, cl);
+ }
+ }
+}
+
+
+static void markobject (GCState *st, TObject *o) {
+ switch (ttype(o)) {
+ case LUA_TUSERDATA: case LUA_TSTRING:
+ strmark(tsvalue(o));
+ break;
+ case LUA_TMARK:
+ markclosure(st, infovalue(o)->func);
+ break;
+ case LUA_TFUNCTION:
+ markclosure(st, clvalue(o));
+ break;
+ case LUA_TTABLE: {
+ if (!ismarked(hvalue(o))) {
+ hvalue(o)->mark = st->tmark; /* chain it in list of marked */
+ st->tmark = hvalue(o);
+ }
+ break;
+ }
+ default: break; /* numbers, etc */
+ }
+}
+
+
+static void markall (lua_State *L) {
+ GCState st;
+ st.cmark = NULL;
+ st.tmark = L->gt; /* put table of globals in mark list */
+ L->gt->mark = NULL;
+ marktagmethods(L, &st); /* mark tag methods */
+ markstack(L, &st); /* mark stack objects */
+ marklock(L, &st); /* mark locked objects */
+ for (;;) { /* mark tables and closures */
+ if (st.cmark) {
+ int i;
+ Closure *f = st.cmark; /* get first closure from list */
+ st.cmark = f->mark; /* remove it from list */
+ for (i=0; i<f->nupvalues; i++) /* mark its upvalues */
+ markobject(&st, &f->upvalue[i]);
+ }
+ else if (st.tmark) {
+ int i;
+ Hash *h = st.tmark; /* get first table from list */
+ st.tmark = h->mark; /* remove it from list */
+ for (i=0; i<h->size; i++) {
+ Node *n = node(h, i);
+ if (ttype(key(n)) != LUA_TNIL) {
+ if (ttype(val(n)) == LUA_TNIL)
+ luaH_remove(h, key(n)); /* dead element; try to remove it */
+ markobject(&st, &n->key);
+ markobject(&st, &n->val);
+ }
+ }
+ }
+ else break; /* nothing else to mark */
+ }
+}
+
+
+static int hasmark (const TObject *o) {
+ /* valid only for locked objects */
+ switch (o->ttype) {
+ case LUA_TSTRING: case LUA_TUSERDATA:
+ return tsvalue(o)->marked;
+ case LUA_TTABLE:
+ return ismarked(hvalue(o));
+ case LUA_TFUNCTION:
+ return ismarked(clvalue(o));
+ default: /* number */
+ return 1;
+ }
+}
+
+
+/* macro for internal debugging; check if a link of free refs is valid */
+#define VALIDLINK(L, st,n) (NONEXT <= (st) && (st) < (n))
+
+static void invalidaterefs (lua_State *L) {
+ int n = L->refSize;
+ int i;
+ for (i=0; i<n; i++) {
+ struct Ref *r = &L->refArray[i];
+ if (r->st == HOLD && !hasmark(&r->o))
+ r->st = COLLECTED;
+ LUA_ASSERT((r->st == LOCK && hasmark(&r->o)) ||
+ (r->st == HOLD && hasmark(&r->o)) ||
+ r->st == COLLECTED ||
+ r->st == NONEXT ||
+ (r->st < n && VALIDLINK(L, L->refArray[r->st].st, n)),
+ "inconsistent ref table");
+ }
+ LUA_ASSERT(VALIDLINK(L, L->refFree, n), "inconsistent ref table");
+}
+
+
+
+static void collectproto (lua_State *L) {
+ Proto **p = &L->rootproto;
+ Proto *next;
+ while ((next = *p) != NULL) {
+ if (next->marked) {
+ next->marked = 0;
+ p = &next->next;
+ }
+ else {
+ *p = next->next;
+ luaF_freeproto(L, next);
+ }
+ }
+}
+
+
+static void collectclosure (lua_State *L) {
+ Closure **p = &L->rootcl;
+ Closure *next;
+ while ((next = *p) != NULL) {
+ if (ismarked(next)) {
+ next->mark = next; /* unmark */
+ p = &next->next;
+ }
+ else {
+ *p = next->next;
+ luaF_freeclosure(L, next);
+ }
+ }
+}
+
+
+static void collecttable (lua_State *L) {
+ Hash **p = &L->roottable;
+ Hash *next;
+ while ((next = *p) != NULL) {
+ if (ismarked(next)) {
+ next->mark = next; /* unmark */
+ p = &next->next;
+ }
+ else {
+ *p = next->next;
+ luaH_free(L, next);
+ }
+ }
+}
+
+
+static void checktab (lua_State *L, stringtable *tb) {
+ if (tb->nuse < (lint32)(tb->size/4) && tb->size > 10)
+ luaS_resize(L, tb, tb->size/2); /* table is too big */
+}
+
+
+static void collectstrings (lua_State *L, int all) {
+ int i;
+ for (i=0; i<L->strt.size; i++) { /* for each list */
+ TString **p = &L->strt.hash[i];
+ TString *next;
+ while ((next = *p) != NULL) {
+ if (next->marked && !all) { /* preserve? */
+ if (next->marked < FIXMARK) /* does not change FIXMARKs */
+ next->marked = 0;
+ p = &next->nexthash;
+ }
+ else { /* collect */
+ *p = next->nexthash;
+ L->strt.nuse--;
+ L->nblocks -= sizestring(next->len);
+ luaM_free(L, next);
+ }
+ }
+ }
+ checktab(L, &L->strt);
+}
+
+
+static void collectudata (lua_State *L, int all) {
+ int i;
+ for (i=0; i<L->udt.size; i++) { /* for each list */
+ TString **p = &L->udt.hash[i];
+ TString *next;
+ while ((next = *p) != NULL) {
+ LUA_ASSERT(next->marked <= 1, "udata cannot be fixed");
+ if (next->marked && !all) { /* preserve? */
+ next->marked = 0;
+ p = &next->nexthash;
+ }
+ else { /* collect */
+ int tag = next->u.d.tag;
+ *p = next->nexthash;
+ next->nexthash = L->TMtable[tag].collected; /* chain udata */
+ L->TMtable[tag].collected = next;
+ L->nblocks -= sizestring(next->len);
+ L->udt.nuse--;
+ }
+ }
+ }
+ checktab(L, &L->udt);
+}
+
+
+#define MINBUFFER 256
+static void checkMbuffer (lua_State *L) {
+ if (L->Mbuffsize > MINBUFFER*2) { /* is buffer too big? */
+ size_t newsize = L->Mbuffsize/2; /* still larger than MINBUFFER */
+ L->nblocks += (newsize - L->Mbuffsize)*sizeof(char);
+ L->Mbuffsize = newsize;
+ luaM_reallocvector(L, L->Mbuffer, newsize, char);
+ }
+}
+
+
+static void callgcTM (lua_State *L, const TObject *o) {
+ Closure *tm = luaT_gettmbyObj(L, o, TM_GC);
+ if (tm != NULL) {
+ int oldah = L->allowhooks;
+ L->allowhooks = 0; /* stop debug hooks during GC tag methods */
+ luaD_checkstack(L, 2);
+ clvalue(L->top) = tm;
+ ttype(L->top) = LUA_TFUNCTION;
+ *(L->top+1) = *o;
+ L->top += 2;
+ luaD_call(L, L->top-2, 0);
+ L->allowhooks = oldah; /* restore hooks */
+ }
+}
+
+
+static void callgcTMudata (lua_State *L) {
+ int tag;
+ TObject o;
+ ttype(&o) = LUA_TUSERDATA;
+ L->GCthreshold = 2*L->nblocks; /* avoid GC during tag methods */
+ for (tag=L->last_tag; tag>=0; tag--) { /* for each tag (in reverse order) */
+ TString *udata;
+ while ((udata = L->TMtable[tag].collected) != NULL) {
+ L->TMtable[tag].collected = udata->nexthash; /* remove it from list */
+ tsvalue(&o) = udata;
+ callgcTM(L, &o);
+ luaM_free(L, udata);
+ }
+ }
+}
+
+
+void luaC_collect (lua_State *L, int all) {
+ collectudata(L, all);
+ callgcTMudata(L);
+ collectstrings(L, all);
+ collecttable(L);
+ collectproto(L);
+ collectclosure(L);
+}
+
+
+static void luaC_collectgarbage (lua_State *L) {
+ markall(L);
+ invalidaterefs(L); /* check unlocked references */
+ luaC_collect(L, 0);
+ checkMbuffer(L);
+ L->GCthreshold = 2*L->nblocks; /* set new threshold */
+ callgcTM(L, &luaO_nilobject);
+}
+
+
+void luaC_checkGC (lua_State *L) {
+ if (L->nblocks >= L->GCthreshold)
+ luaC_collectgarbage(L);
+}
+
diff --git a/src/lua/lgc.h b/src/lua/lgc.h
new file mode 100644
index 00000000..2dea9e4d
--- /dev/null
+++ b/src/lua/lgc.h
@@ -0,0 +1,18 @@
+/*
+** $Id: lgc.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+
+
+void luaC_collect (lua_State *L, int all);
+void luaC_checkGC (lua_State *L);
+
+
+#endif
diff --git a/src/lua/liolib.c b/src/lua/liolib.c
new file mode 100644
index 00000000..4fb385f4
--- /dev/null
+++ b/src/lua/liolib.c
@@ -0,0 +1,710 @@
+/*
+** $Id: liolib.c,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Standard I/O (and system) library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+#ifndef OLD_ANSI
+#include <errno.h>
+#include <locale.h>
+#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s))
+#define free(b) if (b) (free)(b)
+#else
+/* no support for locale and for strerror: fake them */
+#define setlocale(a,b) ((void)a, strcmp((b),"C")==0?"C":NULL)
+#define LC_ALL 0
+#define LC_COLLATE 0
+#define LC_CTYPE 0
+#define LC_MONETARY 0
+#define LC_NUMERIC 0
+#define LC_TIME 0
+#define strerror(e) "generic I/O error"
+#define errno (-1)
+#endif
+
+
+
+#ifdef POPEN
+/* FILE *popen();
+int pclose(); */
+#define CLOSEFILE(L, f) ((pclose(f) == -1) ? fclose(f) : 0)
+#else
+/* no support for popen */
+#define popen(x,y) NULL /* that is, popen always fails */
+#define CLOSEFILE(L, f) (fclose(f))
+#endif
+
+
+#define INFILE 0
+#define OUTFILE 1
+
+typedef struct IOCtrl {
+ int ref[2]; /* ref for strings _INPUT/_OUTPUT */
+ int iotag; /* tag for file handles */
+ int closedtag; /* tag for closed handles */
+} IOCtrl;
+
+
+
+static const char *const filenames[] = {"_INPUT", "_OUTPUT"};
+
+
+static int pushresult (lua_State *L, int i) {
+ if (i) {
+ lua_pushuserdata(L, NULL);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(errno));
+ lua_pushnumber(L, errno);
+ return 3;;
+ }
+}
+
+
+/*
+** {======================================================
+** FILE Operations
+** =======================================================
+*/
+
+
+static FILE *gethandle (lua_State *L, IOCtrl *ctrl, int f) {
+ void *p = lua_touserdata(L, f);
+ if (p != NULL) { /* is `f' a userdata ? */
+ int ftag = lua_tag(L, f);
+ if (ftag == ctrl->iotag) /* does it have the correct tag? */
+ return (FILE *)p;
+ else if (ftag == ctrl->closedtag)
+ lua_error(L, "cannot access a closed file");
+ /* else go through */
+ }
+ return NULL;
+}
+
+
+static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) {
+ FILE *f = gethandle(L, ctrl, arg);
+ luaL_arg_check(L, f, arg, "invalid file handle");
+ return f;
+}
+
+
+static FILE *getfilebyref (lua_State *L, IOCtrl *ctrl, int inout) {
+ FILE *f;
+ lua_getglobals(L);
+ lua_getref(L, ctrl->ref[inout]);
+ lua_rawget(L, -2);
+ f = gethandle(L, ctrl, -1);
+ if (f == NULL)
+ luaL_verror(L, "global variable `%.10s' is not a file handle",
+ filenames[inout]);
+ return f;
+}
+
+
+static void setfilebyname (lua_State *L, IOCtrl *ctrl, FILE *f,
+ const char *name) {
+ lua_pushusertag(L, f, ctrl->iotag);
+ lua_setglobal(L, name);
+}
+
+
+#define setfile(L,ctrl,f,inout) (setfilebyname(L,ctrl,f,filenames[inout]))
+
+
+static int setreturn (lua_State *L, IOCtrl *ctrl, FILE *f, int inout) {
+ if (f == NULL)
+ return pushresult(L, 0);
+ else {
+ setfile(L, ctrl, f, inout);
+ lua_pushusertag(L, f, ctrl->iotag);
+ return 1;
+ }
+}
+
+
+static int closefile (lua_State *L, IOCtrl *ctrl, FILE *f) {
+ if (f == stdin || f == stdout || f == stderr)
+ return 1;
+ else {
+ lua_pushusertag(L, f, ctrl->iotag);
+ lua_settag(L, ctrl->closedtag);
+ return (CLOSEFILE(L, f) == 0);
+ }
+}
+
+
+static int io_close (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ lua_pop(L, 1); /* remove upvalue */
+ return pushresult(L, closefile(L, ctrl, getnonullfile(L, ctrl, 1)));
+}
+
+
+static int file_collect (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f = getnonullfile(L, ctrl, 1);
+ if (f != stdin && f != stdout && f != stderr)
+ CLOSEFILE(L, f);
+ return 0;
+}
+
+
+static int io_open (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f;
+ lua_pop(L, 1); /* remove upvalue */
+ f = fopen(luaL_check_string(L, 1), luaL_check_string(L, 2));
+ if (f) {
+ lua_pushusertag(L, f, ctrl->iotag);
+ return 1;
+ }
+ else
+ return pushresult(L, 0);
+}
+
+
+
+static int io_fromto (lua_State *L, int inout, const char *mode) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *current;
+ lua_pop(L, 1); /* remove upvalue */
+ if (lua_isnull(L, 1)) {
+ closefile(L, ctrl, getfilebyref(L, ctrl, inout));
+ current = (inout == 0) ? stdin : stdout;
+ }
+ else if (lua_tag(L, 1) == ctrl->iotag) /* deprecated option */
+ current = (FILE *)lua_touserdata(L, 1);
+ else {
+ const char *s = luaL_check_string(L, 1);
+ current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode);
+ }
+ return setreturn(L, ctrl, current, inout);
+}
+
+
+static int io_readfrom (lua_State *L) {
+ return io_fromto(L, INFILE, "r");
+}
+
+
+static int io_writeto (lua_State *L) {
+ return io_fromto(L, OUTFILE, "w");
+}
+
+
+static int io_appendto (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *current;
+ lua_pop(L, 1); /* remove upvalue */
+ current = fopen(luaL_check_string(L, 1), "a");
+ return setreturn(L, ctrl, current, OUTFILE);
+}
+
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+
+#ifdef LUA_COMPAT_READPATTERN
+
+/*
+** We cannot lookahead without need, because this can lock stdin.
+** This flag signals when we need to read a next char.
+*/
+#define NEED_OTHER (EOF-1) /* just some flag different from EOF */
+
+
+static int read_pattern (lua_State *L, FILE *f, const char *p) {
+ int inskip = 0; /* {skip} level */
+ int c = NEED_OTHER;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (*p != '\0') {
+ switch (*p) {
+ case '{':
+ inskip++;
+ p++;
+ continue;
+ case '}':
+ if (!inskip) lua_error(L, "unbalanced braces in read pattern");
+ inskip--;
+ p++;
+ continue;
+ default: {
+ const char *ep = luaI_classend(L, p); /* get what is next */
+ int m; /* match result */
+ if (c == NEED_OTHER) c = getc(f);
+ m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep);
+ if (m) {
+ if (!inskip) luaL_putchar(&b, c);
+ c = NEED_OTHER;
+ }
+ switch (*ep) {
+ case '+': /* repetition (1 or more) */
+ if (!m) goto break_while; /* pattern fails? */
+ /* else go through */
+ case '*': /* repetition (0 or more) */
+ while (m) { /* reads the same item until it fails */
+ c = getc(f);
+ m = (c==EOF) ? 0 : luaI_singlematch(c, p, ep);
+ if (m && !inskip) luaL_putchar(&b, c);
+ }
+ /* go through to continue reading the pattern */
+ case '?': /* optional */
+ p = ep+1; /* continues reading the pattern */
+ continue;
+ default:
+ if (!m) goto break_while; /* pattern fails? */
+ p = ep; /* else continues reading the pattern */
+ }
+ }
+ }
+ } break_while:
+ if (c != NEED_OTHER) ungetc(c, f);
+ luaL_pushresult(&b); /* close buffer */
+ return (*p == '\0');
+}
+
+#else
+
+#define read_pattern(L, f, p) (lua_error(L, "read patterns are deprecated"), 0)
+
+#endif
+
+
+static int read_number (lua_State *L, FILE *f) {
+ long d;
+ if (fscanf(f, "%ld", &d) == 1) {
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ else return 0; /* read fails */
+}
+
+
+static int read_word (lua_State *L, FILE *f) {
+ int c;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ do { c = fgetc(f); } while (isspace(c)); /* skip spaces */
+ while (c != EOF && !isspace(c)) {
+ luaL_putchar(&b, c);
+ c = fgetc(f);
+ }
+ ungetc(c, f);
+ luaL_pushresult(&b); /* close buffer */
+ return (lua_strlen(L, -1) > 0);
+}
+
+
+static int read_line (lua_State *L, FILE *f) {
+ int n = 0;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (;;) {
+ char *p = luaL_prepbuffer(&b);
+ if (!fgets(p, LUAL_BUFFERSIZE, f)) /* read fails? */
+ break;
+ n = strlen(p);
+ if (p[n-1] != '\n')
+ luaL_addsize(&b, n);
+ else {
+ luaL_addsize(&b, n-1); /* do not add the `\n' */
+ break;
+ }
+ }
+ luaL_pushresult(&b); /* close buffer */
+ return (n > 0); /* read something? */
+}
+
+
+static void read_file (lua_State *L, FILE *f) {
+ size_t len = 0;
+ size_t size = BUFSIZ;
+ char *buffer = NULL;
+ for (;;) {
+ char *newbuffer = (char *)realloc(buffer, size);
+ if (newbuffer == NULL) {
+ free(buffer);
+ lua_error(L, "not enough memory to read a file");
+ }
+ buffer = newbuffer;
+ len += fread(buffer+len, sizeof(char), size-len, f);
+ if (len < size) break; /* did not read all it could */
+ size *= 2;
+ }
+ lua_pushlstring(L, buffer, len);
+ free(buffer);
+}
+
+
+static int read_chars (lua_State *L, FILE *f, size_t n) {
+ char *buffer;
+ size_t n1;
+ char statbuff[BUFSIZ];
+ if (n <= BUFSIZ)
+ buffer = statbuff;
+ else {
+ buffer = (char *)malloc(n);
+ if (buffer == NULL)
+ lua_error(L, "not enough memory to read a file");
+ }
+ n1 = fread(buffer, sizeof(char), n, f);
+ lua_pushlstring(L, buffer, n1);
+ if (buffer != statbuff) free(buffer);
+ return (n1 > 0 || n == 0);
+}
+
+
+static int io_read (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ int lastarg = lua_gettop(L) - 1;
+ int firstarg = 1;
+ FILE *f = gethandle(L, ctrl, firstarg);
+ int n;
+ if (f) firstarg++;
+ else f = getfilebyref(L, ctrl, INFILE); /* get _INPUT */
+ lua_pop(L, 1);
+ if (firstarg > lastarg) { /* no arguments? */
+ lua_settop(L, 0); /* erase upvalue and other eventual garbage */
+ firstarg = lastarg = 1; /* correct indices */
+ lua_pushstring(L, "*l"); /* push default argument */
+ }
+ else /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, lastarg-firstarg+1+LUA_MINSTACK, "too many arguments");
+ for (n = firstarg; n<=lastarg; n++) {
+ int success;
+ if (lua_isnumber(L, n))
+ success = read_chars(L, f, (size_t)lua_tonumber(L, n));
+ else {
+ const char *p = luaL_check_string(L, n);
+ if (p[0] != '*')
+ success = read_pattern(L, f, p); /* deprecated! */
+ else {
+ switch (p[1]) {
+ case 'n': /* number */
+ if (!read_number(L, f)) goto endloop; /* read fails */
+ continue; /* number is already pushed; avoid the "pushstring" */
+ case 'l': /* line */
+ success = read_line(L, f);
+ break;
+ case 'a': /* file */
+ read_file(L, f);
+ success = 1; /* always success */
+ break;
+ case 'w': /* word */
+ success = read_word(L, f);
+ break;
+ default:
+ luaL_argerror(L, n, "invalid format");
+ success = 0; /* to avoid warnings */
+ }
+ }
+ }
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ break; /* read fails */
+ }
+ } endloop:
+ return n - firstarg;
+}
+
+/* }====================================================== */
+
+
+static int io_write (lua_State *L) {
+ int lastarg = lua_gettop(L) - 1;
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ int arg = 1;
+ int status = 1;
+ FILE *f = gethandle(L, ctrl, arg);
+ if (f) arg++;
+ else f = getfilebyref(L, ctrl, OUTFILE); /* get _OUTPUT */
+ for (; arg <= lastarg; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) { /* LUA_NUMBER */
+ /* optimization: could be done exactly as for strings */
+ status = status && fprintf(f, "%ld", lua_tonumber(L, arg)) > 0;
+ }
+ else {
+ size_t l;
+ const char *s = luaL_check_lstr(L, arg, &l);
+ status = status && (fwrite(s, sizeof(char), l, f) == l);
+ }
+ }
+ pushresult(L, status);
+ return 1;
+}
+
+
+static int io_seek (lua_State *L) {
+ static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f;
+ int op;
+ long offset;
+ lua_pop(L, 1); /* remove upvalue */
+ f = getnonullfile(L, ctrl, 1);
+ op = luaL_findstring(luaL_opt_string(L, 2, "cur"), modenames);
+ offset = luaL_opt_long(L, 3, 0);
+ luaL_arg_check(L, op != -1, 2, "invalid mode");
+ op = fseek(f, offset, mode[op]);
+ if (op)
+ return pushresult(L, 0); /* error */
+ else {
+ lua_pushnumber(L, ftell(f));
+ return 1;
+ }
+}
+
+
+static int io_flush (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, -1);
+ FILE *f;
+ lua_pop(L, 1); /* remove upvalue */
+ f = gethandle(L, ctrl, 1);
+ luaL_arg_check(L, f || lua_isnull(L, 1), 1, "invalid file handle");
+ return pushresult(L, fflush(f) == 0);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Other O.S. Operations
+** =======================================================
+*/
+
+static int io_execute (lua_State *L) {
+ lua_pushnumber(L, system(luaL_check_string(L, 1)));
+ return 1;
+}
+
+
+static int io_remove (lua_State *L) {
+ return pushresult(L, remove(luaL_check_string(L, 1)) == 0);
+}
+
+
+static int io_rename (lua_State *L) {
+ return pushresult(L, rename(luaL_check_string(L, 1),
+ luaL_check_string(L, 2)) == 0);
+}
+
+
+static int io_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_check_string(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int io_clock (lua_State *L) {
+ lua_pushnumber(L, ((long)clock())/CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+static int io_date (lua_State *L) {
+ char b[256];
+ const char *s = luaL_opt_string(L, 1, "%c");
+ struct tm *stm;
+ time_t t;
+ time(&t); stm = localtime(&t);
+ if (strftime(b, sizeof(b), s, stm))
+ lua_pushstring(L, b);
+ else
+ lua_error(L, "invalid `date' format");
+ return 1;
+}
+
+
+static int setloc (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ int op = luaL_findstring(luaL_opt_string(L, 2, "all"), catnames);
+ luaL_arg_check(L, op != -1, 2, "invalid option");
+ lua_pushstring(L, setlocale(cat[op], luaL_check_string(L, 1)));
+ return 1;
+}
+
+
+static int io_exit (lua_State *L) {
+ exit(luaL_opt_int(L, 1, EXIT_SUCCESS));
+ return 0; /* to avoid warnings */
+}
+
+/* }====================================================== */
+
+
+
+static int io_debug (lua_State *L) {
+ for (;;) {
+ char buffer[250];
+ fprintf(stderr, "lua_debug> ");
+ if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
+ strcmp(buffer, "cont\n") == 0)
+ return 0;
+ lua_dostring(L, buffer);
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+#define LEVELS1 12 /* size of the first part of the stack */
+#define LEVELS2 10 /* size of the second part of the stack */
+
+static int errorfb (lua_State *L) {
+ int level = 1; /* skip level 0 (it's this function) */
+ int firstpart = 1; /* still before eventual `...' */
+ lua_Debug ar;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ luaL_addstring(&b, "error: ");
+ luaL_addstring(&b, luaL_check_string(L, 1));
+ luaL_addstring(&b, "\n");
+ while (lua_getstack(L, level++, &ar)) {
+ char buff[120]; /* enough to fit following `sprintf's */
+ if (level == 2)
+ luaL_addstring(&b, "stack traceback:\n");
+ else if (level > LEVELS1 && firstpart) {
+ /* no more than `LEVELS2' more levels? */
+ if (!lua_getstack(L, level+LEVELS2, &ar))
+ level--; /* keep going */
+ else {
+ luaL_addstring(&b, " ...\n"); /* too many levels */
+ while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */
+ level++;
+ }
+ firstpart = 0;
+ continue;
+ }
+ sprintf(buff, "%4d: ", level-1);
+ luaL_addstring(&b, buff);
+ lua_getinfo(L, "Snl", &ar);
+ switch (*ar.namewhat) {
+ case 'g': case 'l': /* global, local */
+ sprintf(buff, "function `%.50s'", ar.name);
+ break;
+ case 'f': /* field */
+ sprintf(buff, "method `%.50s'", ar.name);
+ break;
+ case 't': /* tag method */
+ sprintf(buff, "`%.50s' tag method", ar.name);
+ break;
+ default: {
+ if (*ar.what == 'm') /* main? */
+ sprintf(buff, "main of %.70s", ar.short_src);
+ else if (*ar.what == 'C') /* C function? */
+ sprintf(buff, "%.70s", ar.short_src);
+ else
+ sprintf(buff, "function <%d:%.70s>", ar.linedefined, ar.short_src);
+ ar.source = NULL; /* do not print source again */
+ }
+ }
+ luaL_addstring(&b, buff);
+ if (ar.currentline > 0) {
+ sprintf(buff, " at line %d", ar.currentline);
+ luaL_addstring(&b, buff);
+ }
+ if (ar.source) {
+ sprintf(buff, " [%.70s]", ar.short_src);
+ luaL_addstring(&b, buff);
+ }
+ luaL_addstring(&b, "\n");
+ }
+ luaL_pushresult(&b);
+ lua_getglobal(L, LUA_ALERT);
+ if (lua_isfunction(L, -1)) { /* avoid loop if _ALERT is not defined */
+ lua_pushvalue(L, -2); /* error message */
+ lua_rawcall(L, 1, 0);
+ }
+ return 0;
+}
+
+
+
+static const struct luaL_reg iolib[] = {
+ {LUA_ERRORMESSAGE, errorfb},
+ {"clock", io_clock},
+ {"date", io_date},
+ {"debug", io_debug},
+ {"execute", io_execute},
+ {"exit", io_exit},
+ {"getenv", io_getenv},
+ {"remove", io_remove},
+ {"rename", io_rename},
+ {"setlocale", setloc},
+};
+
+
+static const struct luaL_reg iolibtag[] = {
+ {"appendto", io_appendto},
+ {"closefile", io_close},
+ {"flush", io_flush},
+ {"openfile", io_open},
+ {"read", io_read},
+ {"readfrom", io_readfrom},
+ {"seek", io_seek},
+ {"write", io_write},
+ {"writeto", io_writeto}
+};
+
+
+static void openwithcontrol (lua_State *L) {
+ IOCtrl *ctrl = (IOCtrl *)lua_newuserdata(L, sizeof(IOCtrl));
+ unsigned int i;
+ ctrl->iotag = lua_newtag(L);
+ ctrl->closedtag = lua_newtag(L);
+ for (i=0; i<sizeof(iolibtag)/sizeof(iolibtag[0]); i++) {
+ /* put `ctrl' as upvalue for these functions */
+ lua_pushvalue(L, -1);
+ lua_pushcclosure(L, iolibtag[i].func, 1);
+ lua_setglobal(L, iolibtag[i].name);
+ }
+ /* create references to variable names */
+ lua_pushstring(L, filenames[INFILE]);
+ ctrl->ref[INFILE] = lua_ref(L, 1);
+ lua_pushstring(L, filenames[OUTFILE]);
+ ctrl->ref[OUTFILE] = lua_ref(L, 1);
+ /* predefined file handles */
+ setfile(L, ctrl, stdin, INFILE);
+ setfile(L, ctrl, stdout, OUTFILE);
+ setfilebyname(L, ctrl, stdin, "_STDIN");
+ setfilebyname(L, ctrl, stdout, "_STDOUT");
+ setfilebyname(L, ctrl, stderr, "_STDERR");
+ /* close files when collected */
+ lua_pushcclosure(L, file_collect, 1); /* pops `ctrl' from stack */
+ lua_settagmethod(L, ctrl->iotag, "gc");
+}
+
+
+LUALIB_API void lua_iolibopen (lua_State *L) {
+ luaL_openl(L, iolib);
+ openwithcontrol(L);
+}
+
diff --git a/src/lua/llex.c b/src/lua/llex.c
new file mode 100644
index 00000000..86fb69ab
--- /dev/null
+++ b/src/lua/llex.c
@@ -0,0 +1,411 @@
+/*
+** $Id: llex.c,v 1.6 2004/06/04 13:42:10 neil Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "luadebug.h"
+#include "lzio.h"
+
+
+
+#define next(LS) (LS->current = zgetc(LS->z))
+
+
+
+/* ORDER RESERVED */
+static const char *const token2string [] = {
+ "and", "break", "do", "else", "elseif", "end", "for",
+ "function", "if", "local", "nil", "not", "or", "repeat", "return", "then",
+ "until", "while", "", "..", "...", "==", ">=", "<=", "~=", "", "", "<eof>"};
+
+
+void luaX_init (lua_State *L) {
+ int i;
+ for (i=0; i<NUM_RESERVED; i++) {
+ TString *ts = luaS_new(L, token2string[i]);
+ ts->marked = (unsigned char)(RESERVEDMARK+i); /* reserved word */
+ }
+}
+
+
+#define MAXSRC 80
+
+
+void luaX_checklimit (LexState *ls, int val, int limit, const char *msg) {
+ if (val > limit) {
+ char buff[100];
+ sprintf(buff, "too many %.50s (limit=%d)", msg, limit);
+ luaX_error(ls, buff, ls->t.token);
+ }
+}
+
+
+void luaX_syntaxerror (LexState *ls, const char *s, const char *token) {
+ char buff[MAXSRC];
+ luaO_chunkid(buff, ls->source->str, sizeof(buff));
+ luaO_verror(ls->L, "%.99s;\n last token read: `%.30s' at line %d in %.80s",
+ s, token, ls->linenumber, buff);
+}
+
+
+void luaX_error (LexState *ls, const char *s, int token) {
+ char buff[TOKEN_LEN];
+ luaX_token2str(token, buff);
+ if (buff[0] == '\0')
+ luaX_syntaxerror(ls, s, ls->L->Mbuffer);
+ else
+ luaX_syntaxerror(ls, s, buff);
+}
+
+
+void luaX_token2str (int token, char *s) {
+ if (token < 256) {
+ s[0] = (char)token;
+ s[1] = '\0';
+ }
+ else
+ strcpy(s, token2string[token-FIRST_RESERVED]);
+}
+
+
+static void luaX_invalidchar (LexState *ls, int c) {
+ char buff[8];
+ sprintf(buff, "0x%02X", c);
+ luaX_syntaxerror(ls, "invalid control char", buff);
+}
+
+
+static void inclinenumber (LexState *LS) {
+ next(LS); /* skip '\n' */
+ ++LS->linenumber;
+ luaX_checklimit(LS, LS->linenumber, MAX_INT, "lines in a chunk");
+}
+
+
+void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source) {
+ LS->L = L;
+ LS->lookahead.token = TK_EOS; /* no look-ahead token */
+ LS->z = z;
+ LS->fs = NULL;
+ LS->linenumber = 1;
+ LS->lastline = 1;
+ LS->source = source;
+ next(LS); /* read first char */
+ if (LS->current == '#') {
+ do { /* skip first line */
+ next(LS);
+ } while (LS->current != '\n' && LS->current != '\r' && LS->current != EOZ);
+ }
+}
+
+
+
+/*
+** =======================================================
+** LEXICAL ANALYZER
+** =======================================================
+*/
+
+
+/* use Mbuffer to store names, literal strings and numbers */
+
+#define EXTRABUFF 128
+#define checkbuffer(L, n, len) if ((len)+(n) > L->Mbuffsize) \
+ luaO_openspace(L, (len)+(n)+EXTRABUFF)
+
+#define save(L, c, l) (L->Mbuffer[l++] = (char)c)
+#define save_and_next(L, LS, l) (save(L, LS->current, l), next(LS))
+
+
+static const char *readname (LexState *LS) {
+ lua_State *L = LS->L;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ do {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ } while (isalnum(LS->current) || LS->current == '_');
+ save(L, '\0', l);
+ return L->Mbuffer;
+}
+
+
+/* LUA_NUMBER */
+static void read_number (LexState *LS, int comma, SemInfo *seminfo) {
+ lua_State *L = LS->L;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ if (comma) save(L, '.', l);
+ while (isdigit(LS->current)) {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ }
+ if (LS->current == '.') {
+ save_and_next(L, LS, l);
+ if (LS->current == '.') {
+ save_and_next(L, LS, l);
+ save(L, '\0', l);
+ luaX_error(LS, "ambiguous syntax"
+ " (decimal point x string concatenation)", TK_NUMBER);
+ }
+ }
+ while (isdigit(LS->current)) {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ }
+ if (LS->current == 'e' || LS->current == 'E') {
+ save_and_next(L, LS, l); /* read 'E' */
+ if (LS->current == '+' || LS->current == '-')
+ save_and_next(L, LS, l); /* optional exponent sign */
+ while (isdigit(LS->current)) {
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ }
+ }
+ save(L, '\0', l);
+ if (!luaO_str2d(L->Mbuffer, &seminfo->r))
+ luaX_error(LS, "malformed number", TK_NUMBER);
+}
+
+
+static void read_long_string (LexState *LS, SemInfo *seminfo) {
+ lua_State *L = LS->L;
+ int cont = 0;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ save(L, '[', l); /* save first '[' */
+ save_and_next(L, LS, l); /* pass the second '[' */
+ for (;;) {
+ checkbuffer(L, 10, l);
+ switch (LS->current) {
+ case EOZ:
+ save(L, '\0', l);
+ if (seminfo)
+ luaX_error(LS, "unfinished long string", TK_STRING);
+ else
+ luaX_error(LS, "unfinished comment", TK_EOS);
+ break; /* to avoid warnings */
+ case '[':
+ save_and_next(L, LS, l);
+ if (LS->current == '[') {
+ cont++;
+ save_and_next(L, LS, l);
+ }
+ continue;
+ case ']':
+ save_and_next(L, LS, l);
+ if (LS->current == ']') {
+ if (cont == 0) goto endloop;
+ cont--;
+ save_and_next(L, LS, l);
+ }
+ continue;
+ case '\n':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\r') next(LS);
+ continue;
+ case '\r':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\n') next(LS);
+ continue;
+ default:
+ if (seminfo) /* no need to save complete comment */
+ save(L, LS->current, l);
+ next(LS);
+ }
+ } endloop:
+ save_and_next(L, LS, l); /* skip the second ']' */
+ save(L, '\0', l);
+ if (seminfo)
+ seminfo->ts = luaS_newlstr(L, L->Mbuffer+2, l-5);
+}
+
+
+static void read_string (LexState *LS, int del, SemInfo *seminfo) {
+ lua_State *L = LS->L;
+ size_t l = 0;
+ checkbuffer(L, 10, l);
+ save_and_next(L, LS, l);
+ while (LS->current != del) {
+ checkbuffer(L, 10, l);
+ switch (LS->current) {
+ case EOZ: case '\n': case '\r':
+ save(L, '\0', l);
+ luaX_error(LS, "unfinished string", TK_STRING);
+ break; /* to avoid warnings */
+ case '\\':
+ next(LS); /* do not save the '\' */
+ switch (LS->current) {
+ case 'a': save(L, '\a', l); next(LS); break;
+ case 'b': save(L, '\b', l); next(LS); break;
+ case 'f': save(L, '\f', l); next(LS); break;
+ case 'n': save(L, '\n', l); next(LS); break;
+ case 'r': save(L, '\r', l); next(LS); break;
+ case 't': save(L, '\t', l); next(LS); break;
+ case 'v': save(L, '\v', l); next(LS); break;
+ case '\n':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\r') next(LS);
+ break;
+ case '\r':
+ save(L, '\n', l);
+ inclinenumber(LS);
+ if (LS->current == '\n') next(LS);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ int c = 0;
+ int i = 0;
+ do {
+ c = 10*c + (LS->current-'0');
+ next(LS);
+ } while (++i<3 && isdigit(LS->current));
+ if (c != (unsigned char)c) {
+ save(L, '\0', l);
+ luaX_error(LS, "escape sequence too large", TK_STRING);
+ }
+ save(L, c, l);
+ break;
+ }
+ default: /* handles \\, \", \', and \? */
+ save_and_next(L, LS, l);
+ }
+ break;
+ default:
+ save_and_next(L, LS, l);
+ }
+ }
+ save_and_next(L, LS, l); /* skip delimiter */
+ save(L, '\0', l);
+ seminfo->ts = luaS_newlstr(L, L->Mbuffer+1, l-3);
+}
+
+
+int luaX_lex (LexState *LS, SemInfo *seminfo) {
+ for (;;) {
+ switch (LS->current) {
+
+ case ' ': case '\t':
+ next(LS);
+ continue;
+
+ case '\n':
+ inclinenumber(LS);
+ if (LS->current == '\r') next(LS);
+ continue;
+
+ case '\r':
+ inclinenumber(LS);
+ if (LS->current == '\n') next(LS);
+ continue;
+
+ case '$':
+ luaX_error(LS, "unexpected `$' (pragmas are no longer supported)", '$');
+ break;
+
+ case '-':
+ next(LS);
+ if (LS->current != '-') return '-';
+ if (next(LS) == '[' && next(LS) == '[')
+ read_long_string(LS, NULL);
+ else
+ while (LS->current != '\n' && LS->current != '\r' && LS->current != EOZ)
+ next(LS);
+ continue;
+
+ case '[':
+ next(LS);
+ if (LS->current != '[') return '[';
+ else {
+ read_long_string(LS, seminfo);
+ return TK_STRING;
+ }
+
+ case '=':
+ next(LS);
+ if (LS->current != '=') return '=';
+ else { next(LS); return TK_EQ; }
+
+ case '<':
+ next(LS);
+ if (LS->current != '=') return '<';
+ else { next(LS); return TK_LE; }
+
+ case '>':
+ next(LS);
+ if (LS->current != '=') return '>';
+ else { next(LS); return TK_GE; }
+
+ case '~':
+ next(LS);
+ if (LS->current != '=') return '~';
+ else { next(LS); return TK_NE; }
+
+ case '"':
+ case '\'':
+ read_string(LS, LS->current, seminfo);
+ return TK_STRING;
+
+ case '.':
+ next(LS);
+ if (LS->current == '.') {
+ next(LS);
+ if (LS->current == '.') {
+ next(LS);
+ return TK_DOTS; /* ... */
+ }
+ else return TK_CONCAT; /* .. */
+ }
+ else if (!isdigit(LS->current)) return '.';
+ else {
+ read_number(LS, 1, seminfo);
+ return TK_NUMBER;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ read_number(LS, 0, seminfo);
+ return TK_NUMBER;
+
+ case EOZ:
+ return TK_EOS;
+
+ case '_': goto tname;
+
+ default:
+ if (!isalpha(LS->current)) {
+ int c = LS->current;
+ if (iscntrl(c))
+ luaX_invalidchar(LS, c);
+ next(LS);
+ return c;
+ }
+ tname: { /* identifier or reserved word */
+ TString *ts = luaS_new(LS->L, readname(LS));
+ if (ts->marked >= RESERVEDMARK) /* reserved word? */
+ return ts->marked-RESERVEDMARK+FIRST_RESERVED;
+ seminfo->ts = ts;
+ return TK_NAME;
+ }
+ }
+ }
+}
+
diff --git a/src/lua/llex.h b/src/lua/llex.h
new file mode 100644
index 00000000..5fb13c88
--- /dev/null
+++ b/src/lua/llex.h
@@ -0,0 +1,72 @@
+/*
+** $Id: llex.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llex_h
+#define llex_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+#define FIRST_RESERVED 257
+
+/* maximum length of a reserved word (+1 for final 0) */
+#define TOKEN_LEN 15
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER RESERVED"
+*/
+enum RESERVED {
+ /* terminal symbols denoted by reserved words */
+ TK_AND = FIRST_RESERVED, TK_BREAK,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FOR, TK_FUNCTION, TK_IF, TK_LOCAL,
+ TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_UNTIL, TK_WHILE,
+ /* other terminal symbols */
+ TK_NAME, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
+ TK_STRING, TK_EOS
+};
+
+/* number of reserved words */
+#define NUM_RESERVED ((int)(TK_WHILE-FIRST_RESERVED+1))
+
+
+typedef union {
+ Number r;
+ TString *ts;
+} SemInfo; /* semantics information */
+
+
+typedef struct Token {
+ int token;
+ SemInfo seminfo;
+} Token;
+
+
+typedef struct LexState {
+ int current; /* current character */
+ Token t; /* current token */
+ Token lookahead; /* look ahead token */
+ struct FuncState *fs; /* `FuncState' is private to the parser */
+ struct lua_State *L;
+ struct zio *z; /* input stream */
+ int linenumber; /* input line counter */
+ int lastline; /* line of last token `consumed' */
+ TString *source; /* current source name */
+} LexState;
+
+
+void luaX_init (lua_State *L);
+void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source);
+int luaX_lex (LexState *LS, SemInfo *seminfo);
+void luaX_checklimit (LexState *ls, int val, int limit, const char *msg);
+void luaX_syntaxerror (LexState *ls, const char *s, const char *token);
+void luaX_error (LexState *ls, const char *s, int token);
+void luaX_token2str (int token, char *s);
+
+
+#endif
diff --git a/src/lua/llimits.h b/src/lua/llimits.h
new file mode 100644
index 00000000..4b124503
--- /dev/null
+++ b/src/lua/llimits.h
@@ -0,0 +1,204 @@
+/*
+** $Id: llimits.h,v 1.2 2001/11/26 23:00:24 darkgod Exp $
+** Limits, basic types, and some other "installation-dependent" definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+
+/*
+** try to find number of bits in an integer
+*/
+#ifndef BITS_INT
+/* avoid overflows in comparison */
+#if INT_MAX-20 < 32760
+#define BITS_INT 16
+#else
+#if INT_MAX > 2147483640L
+/* machine has at least 32 bits */
+#define BITS_INT 32
+#else
+#error "you must define BITS_INT with number of bits in an integer"
+#endif
+#endif
+#endif
+
+
+/*
+** Define the type `number' of Lua
+** GREP LUA_NUMBER to change that
+*/
+#ifndef LUA_NUM_TYPE
+#define LUA_NUM_TYPE long
+#endif
+
+typedef LUA_NUM_TYPE Number;
+
+/* function to convert a Number to a string */
+#define NUMBER_FMT "%ld" /* LUA_NUMBER */
+#define lua_number2str(s,n) sprintf((s), NUMBER_FMT, (n))
+
+/* function to convert a string to a Number */
+#define lua_str2number(s,p) strtol((s), (p), 10)
+
+
+
+typedef unsigned long lint32; /* unsigned int with at least 32 bits */
+
+
+#define MAX_SIZET ((size_t)(~(size_t)0)-2)
+
+
+#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
+
+/*
+** conversion of pointer to int (for hashing only)
+** (the shift removes bits that are usually 0 because of alignment)
+*/
+#define IntPoint(p) (((unsigned long)(p)) >> 3)
+
+
+
+#define MINPOWER2 4 /* minimum size for "growing" vectors */
+
+
+
+#ifndef DEFAULT_STACK_SIZE
+#define DEFAULT_STACK_SIZE 1024
+#endif
+
+
+
+/* type to ensure maximum alignment */
+union L_Umaxalign { long d; char *s; long l; };
+
+
+
+/*
+** type for virtual-machine instructions
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+** For a very small machine, you may change that to 2 bytes (and adjust
+** the following limits accordingly)
+*/
+typedef unsigned long Instruction;
+
+
+/*
+** size and position of opcode arguments.
+** For an instruction with 2 bytes, size is 16, and size_b can be 5
+** (accordingly, size_u will be 10, and size_a will be 5)
+*/
+#define SIZE_INSTRUCTION 32
+#define SIZE_B 9
+
+#define SIZE_OP 6
+#define SIZE_U (SIZE_INSTRUCTION-SIZE_OP)
+#define POS_U SIZE_OP
+#define POS_B SIZE_OP
+#define SIZE_A (SIZE_INSTRUCTION-(SIZE_OP+SIZE_B))
+#define POS_A (SIZE_OP+SIZE_B)
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in BITS_INT-1 bits (-1 for sign)
+*/
+#if SIZE_U < BITS_INT-1
+#define MAXARG_U ((1<<SIZE_U)-1)
+#define MAXARG_S (MAXARG_U>>1) /* `S' is signed */
+#else
+#define MAXARG_U MAX_INT
+#define MAXARG_S MAX_INT
+#endif
+
+#if SIZE_A < BITS_INT-1
+#define MAXARG_A ((1<<SIZE_A)-1)
+#else
+#define MAXARG_A MAX_INT
+#endif
+
+#if SIZE_B < BITS_INT-1
+#define MAXARG_B ((1<<SIZE_B)-1)
+#else
+#define MAXARG_B MAX_INT
+#endif
+
+
+/* maximum stack size in a function */
+#ifndef MAXSTACK
+#define MAXSTACK 250
+#endif
+
+#if MAXSTACK > MAXARG_B
+#undef MAXSTACK
+#define MAXSTACK MAXARG_B
+#endif
+
+
+/* maximum number of local variables */
+#ifndef MAXLOCALS
+#define MAXLOCALS 200 /* arbitrary limit (<MAXSTACK) */
+#endif
+#if MAXLOCALS>=MAXSTACK
+#undef MAXLOCALS
+#define MAXLOCALS (MAXSTACK-1)
+#endif
+
+
+/* maximum number of upvalues */
+#ifndef MAXUPVALUES
+#define MAXUPVALUES 32 /* arbitrary limit (<=MAXARG_B) */
+#endif
+#if MAXUPVALUES>MAXARG_B
+#undef MAXUPVALUES
+#define MAXUPVALUES MAXARG_B
+#endif
+
+
+/* maximum number of variables in the left side of an assignment */
+#ifndef MAXVARSLH
+#define MAXVARSLH 100 /* arbitrary limit (<MULT_RET) */
+#endif
+#if MAXVARSLH>=MULT_RET
+#undef MAXVARSLH
+#define MAXVARSLH (MULT_RET-1)
+#endif
+
+
+/* maximum number of parameters in a function */
+#ifndef MAXPARAMS
+#define MAXPARAMS 100 /* arbitrary limit (<MAXLOCALS) */
+#endif
+#if MAXPARAMS>=MAXLOCALS
+#undef MAXPARAMS
+#define MAXPARAMS (MAXLOCALS-1)
+#endif
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 64
+#if LFIELDS_PER_FLUSH>(MAXSTACK/4)
+#undef LFIELDS_PER_FLUSH
+#define LFIELDS_PER_FLUSH (MAXSTACK/4)
+#endif
+
+/* number of record items to accumulate before a SETMAP instruction */
+/* (each item counts 2 elements on the stack: an index and a value) */
+#define RFIELDS_PER_FLUSH (LFIELDS_PER_FLUSH/2)
+
+
+/* maximum lookback to find a real constant (for code generation) */
+#ifndef LOOKBACKNUMS
+#define LOOKBACKNUMS 20 /* arbitrary constant */
+#endif
+
+
+#endif
diff --git a/src/lua/lmem.c b/src/lua/lmem.c
new file mode 100644
index 00000000..8fdecef3
--- /dev/null
+++ b/src/lua/lmem.c
@@ -0,0 +1,150 @@
+/*
+** $Id: lmem.c,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+
+#ifdef LUA_DEBUG
+/*
+** {======================================================================
+** Controlled version for realloc.
+** =======================================================================
+*/
+
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#define realloc(b, s) debug_realloc(b, s)
+#define malloc(b) debug_realloc(NULL, b)
+#define free(b) debug_realloc(b, 0)
+
+
+/* ensures maximum alignment for HEADER */
+#define HEADER (sizeof(union L_Umaxalign))
+
+#define MARKSIZE 16
+#define MARK 0x55 /* 01010101 (a nice pattern) */
+
+
+#define blocksize(b) ((unsigned long *)((char *)(b) - HEADER))
+
+unsigned long memdebug_numblocks = 0;
+unsigned long memdebug_total = 0;
+unsigned long memdebug_maxmem = 0;
+unsigned long memdebug_memlimit = LONG_MAX;
+
+
+static void *checkblock (void *block) {
+ unsigned long *b = blocksize(block);
+ unsigned long size = *b;
+ int i;
+ for (i=0;i<MARKSIZE;i++)
+ assert(*(((char *)b)+HEADER+size+i) == MARK+i); /* corrupted block? */
+ memdebug_numblocks--;
+ memdebug_total -= size;
+ return b;
+}
+
+
+static void freeblock (void *block) {
+ if (block) {
+ size_t size = *blocksize(block);
+ block = checkblock(block);
+ memset(block, -1, size+HEADER+MARKSIZE); /* erase block */
+ (free)(block); /* free original block */
+ }
+}
+
+
+static void *debug_realloc (void *block, size_t size) {
+ if (size == 0) {
+ freeblock(block);
+ return NULL;
+ }
+ else if (memdebug_total+size > memdebug_memlimit)
+ return NULL; /* to test memory allocation errors */
+ else {
+ size_t realsize = HEADER+size+MARKSIZE;
+ char *newblock = (char *)(malloc)(realsize); /* alloc a new block */
+ int i;
+ if (realsize < size) return NULL; /* overflow! */
+ if (newblock == NULL) return NULL;
+ if (block) {
+ size_t oldsize = *blocksize(block);
+ if (oldsize > size) oldsize = size;
+ memcpy(newblock+HEADER, block, oldsize);
+ freeblock(block); /* erase (and check) old copy */
+ }
+ memdebug_total += size;
+ if (memdebug_total > memdebug_maxmem) memdebug_maxmem = memdebug_total;
+ memdebug_numblocks++;
+ *(unsigned long *)newblock = size;
+ for (i=0;i<MARKSIZE;i++)
+ *(newblock+HEADER+size+i) = (char)(MARK+i);
+ return newblock+HEADER;
+ }
+}
+
+
+/* }====================================================================== */
+#endif
+
+
+
+/*
+** Real ISO (ANSI) systems do not need these tests;
+** but some systems (Sun OS) are not that ISO...
+*/
+#ifdef OLD_ANSI
+#define realloc(b,s) ((b) == NULL ? malloc(s) : (realloc)(b, s))
+#define free(b) if (b) (free)(b)
+#endif
+
+
+void *luaM_growaux (lua_State *L, void *block, size_t nelems,
+ int inc, size_t size, const char *errormsg, size_t limit) {
+ size_t newn = nelems+inc;
+ if (nelems >= limit-inc) lua_error(L, errormsg);
+ if ((newn ^ nelems) <= nelems || /* still the same power-of-2 limit? */
+ (nelems > 0 && newn < MINPOWER2)) /* or block already is MINPOWER2? */
+ return block; /* do not need to reallocate */
+ else /* it crossed a power-of-2 boundary; grow to next power */
+ return luaM_realloc(L, block, luaO_power2(newn)*size);
+}
+
+
+/*
+** generic allocation routine.
+*/
+void *luaM_realloc (lua_State *L, void *block, lint32 size) {
+ if (size == 0) {
+ free(block); /* block may be NULL; that is OK for free */
+ return NULL;
+ }
+ else if (size >= MAX_SIZET)
+ lua_error(L, "memory allocation error: block too big");
+ block = realloc(block, size);
+ if (block == NULL) {
+ if (L)
+ luaD_breakrun(L, LUA_ERRMEM); /* break run without error message */
+ else return NULL; /* error before creating state! */
+ }
+ return block;
+}
+
+
diff --git a/src/lua/lmem.h b/src/lua/lmem.h
new file mode 100644
index 00000000..0d27c336
--- /dev/null
+++ b/src/lua/lmem.h
@@ -0,0 +1,42 @@
+/*
+** $Id: lmem.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lmem_h
+#define lmem_h
+
+
+#include <stddef.h>
+
+#include "llimits.h"
+#include "lua.h"
+
+void *luaM_realloc (lua_State *L, void *oldblock, lint32 size);
+void *luaM_growaux (lua_State *L, void *block, size_t nelems,
+ int inc, size_t size, const char *errormsg,
+ size_t limit);
+
+#define luaM_free(L, b) luaM_realloc(L, (b), 0)
+#define luaM_malloc(L, t) luaM_realloc(L, NULL, (t))
+#define luaM_new(L, t) ((t *)luaM_malloc(L, sizeof(t)))
+#define luaM_newvector(L, n,t) ((t *)luaM_malloc(L, (n)*(lint32)sizeof(t)))
+
+#define luaM_growvector(L, v,nelems,inc,t,e,l) \
+ ((v)=(t *)luaM_growaux(L, v,nelems,inc,sizeof(t),e,l))
+
+#define luaM_reallocvector(L, v,n,t) \
+ ((v)=(t *)luaM_realloc(L, v,(n)*(lint32)sizeof(t)))
+
+
+#ifdef LUA_DEBUG
+extern unsigned long memdebug_numblocks;
+extern unsigned long memdebug_total;
+extern unsigned long memdebug_maxmem;
+extern unsigned long memdebug_memlimit;
+#endif
+
+
+#endif
+
diff --git a/src/lua/lobject.c b/src/lua/lobject.c
new file mode 100644
index 00000000..cd9d1f0b
--- /dev/null
+++ b/src/lua/lobject.c
@@ -0,0 +1,125 @@
+/*
+** $Id: lobject.c,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+const TObject luaO_nilobject = {LUA_TNIL, {NULL}};
+
+
+const char *const luaO_typenames[] = {
+ "userdata", "nil", "number", "string", "table", "function"
+};
+
+
+
+/*
+** returns smaller power of 2 larger than `n' (minimum is MINPOWER2)
+*/
+lint32 luaO_power2 (lint32 n) {
+ lint32 p = MINPOWER2;
+ while (p<=n) p<<=1;
+ return p;
+}
+
+
+int luaO_equalObj (const TObject *t1, const TObject *t2) {
+ if (ttype(t1) != ttype(t2)) return 0;
+ switch (ttype(t1)) {
+ case LUA_TNUMBER:
+ return nvalue(t1) == nvalue(t2);
+ case LUA_TSTRING: case LUA_TUSERDATA:
+ return tsvalue(t1) == tsvalue(t2);
+ case LUA_TTABLE:
+ return hvalue(t1) == hvalue(t2);
+ case LUA_TFUNCTION:
+ return clvalue(t1) == clvalue(t2);
+ default:
+ LUA_ASSERT(ttype(t1) == LUA_TNIL, "invalid type");
+ return 1; /* LUA_TNIL */
+ }
+}
+
+
+char *luaO_openspace (lua_State *L, size_t n) {
+ if (n > L->Mbuffsize) {
+ luaM_reallocvector(L, L->Mbuffer, n, char);
+ L->nblocks += (n - L->Mbuffsize)*sizeof(char);
+ L->Mbuffsize = n;
+ }
+ return L->Mbuffer;
+}
+
+
+int luaO_str2d (const char *s, Number *result) { /* LUA_NUMBER */
+ char *endptr;
+ Number res = lua_str2number(s, &endptr);
+ if (endptr == s) return 0; /* no conversion */
+ while (isspace((unsigned char)*endptr)) endptr++;
+ if (*endptr != '\0') return 0; /* invalid trailing characters? */
+ *result = res;
+ return 1;
+}
+
+
+/* maximum length of a string format for `luaO_verror' */
+#define MAX_VERROR 280
+
+/* this function needs to handle only '%d' and '%.XXs' formats */
+void luaO_verror (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ char buff[MAX_VERROR]; /* to hold formatted message */
+ va_start(argp, fmt);
+ vsprintf(buff, fmt, argp);
+ va_end(argp);
+ lua_error(L, buff);
+}
+
+
+void luaO_chunkid (char *out, const char *source, int bufflen) {
+ if (*source == '=') {
+ strncpy(out, source+1, bufflen); /* remove first char */
+ out[bufflen-1] = '\0'; /* ensures null termination */
+ }
+ else {
+ if (*source == '@') {
+ int l;
+ source++; /* skip the `@' */
+ bufflen -= sizeof("file `...%s'");
+ l = strlen(source);
+ if (l>bufflen) {
+ source += (l-bufflen); /* get last part of file name */
+ sprintf(out, "file `...%.99s'", source);
+ }
+ else
+ sprintf(out, "file `%.99s'", source);
+ }
+ else {
+ int len = strcspn(source, "\n"); /* stop at first newline */
+ bufflen -= sizeof("string \"%.*s...\"");
+ if (len > bufflen) len = bufflen;
+ if (source[len] != '\0') { /* must truncate? */
+ strcpy(out, "string \"");
+ out += strlen(out);
+ strncpy(out, source, len);
+ strcpy(out+len, "...\"");
+ }
+ else
+ sprintf(out, "string \"%.99s\"", source);
+ }
+ }
+}
diff --git a/src/lua/lobject.h b/src/lua/lobject.h
new file mode 100644
index 00000000..ce978205
--- /dev/null
+++ b/src/lua/lobject.h
@@ -0,0 +1,204 @@
+/*
+** $Id: lobject.h,v 1.3 2001/11/26 23:00:24 darkgod Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+#ifdef LUA_DEBUG
+#undef NDEBUG
+#include <assert.h>
+#define LUA_INTERNALERROR(s) assert(((void)s,0))
+#define LUA_ASSERT(c,s) assert(((void)s,(c)))
+#else
+#define LUA_INTERNALERROR(s) /* empty */
+#define LUA_ASSERT(c,s) /* empty */
+#endif
+
+
+#ifdef LUA_DEBUG
+/* to avoid warnings, and make sure value is really unused */
+#define UNUSED(x) (x=0, (void)(x))
+#else
+#define UNUSED(x) ((void)(x)) /* to avoid warnings */
+#endif
+
+
+/* mark for closures active in the stack */
+#define LUA_TMARK 6
+
+
+/* tags for values visible from Lua == first user-created tag */
+#define NUM_TAGS 6
+
+
+/* check whether `t' is a mark */
+#define is_T_MARK(t) ((t) == LUA_TMARK)
+
+
+typedef union {
+ struct TString *ts; /* LUA_TSTRING, LUA_TUSERDATA */
+ struct Closure *cl; /* LUA_TFUNCTION */
+ struct Hash *a; /* LUA_TTABLE */
+ struct CallInfo *i; /* LUA_TLMARK */
+ Number n; /* LUA_TNUMBER */
+} Value;
+
+
+/* Macros to access values */
+#define ttype(o) ((o)->ttype)
+#define nvalue(o) ((o)->value.n)
+#define tsvalue(o) ((o)->value.ts)
+#define clvalue(o) ((o)->value.cl)
+#define hvalue(o) ((o)->value.a)
+#define infovalue(o) ((o)->value.i)
+#define svalue(o) (tsvalue(o)->str)
+
+
+typedef struct lua_TObject {
+ int ttype;
+ Value value;
+} TObject;
+
+
+/*
+** String headers for string table
+*/
+
+/*
+** most `malloc' libraries allocate memory in blocks of 8 bytes. TSPACK
+** tries to make sizeof(TString) a multiple of this granularity, to reduce
+** waste of space.
+*/
+#define TSPACK ((int)sizeof(int))
+
+typedef struct TString {
+ union {
+ struct { /* for strings */
+ unsigned long hash;
+ int constindex; /* hint to reuse constants */
+ } s;
+ struct { /* for userdata */
+ int tag;
+ void *value;
+ } d;
+ } u;
+ size_t len;
+ struct TString *nexthash; /* chain for hash table */
+ int marked;
+ char str[TSPACK]; /* variable length string!! must be the last field! */
+} TString;
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ Number *knum; /* Number numbers used by the function */
+ int nknum; /* size of `knum' */
+ struct TString **kstr; /* strings used by the function */
+ int nkstr; /* size of `kstr' */
+ struct Proto **kproto; /* functions defined inside the function */
+ int nkproto; /* size of `kproto' */
+ Instruction *code;
+ int ncode; /* size of `code'; when 0 means an incomplete `Proto' */
+ short numparams;
+ short is_vararg;
+ short maxstacksize;
+ short marked;
+ struct Proto *next;
+ /* debug information */
+ int *lineinfo; /* map from opcodes to source lines */
+ int nlineinfo; /* size of `lineinfo' */
+ int nlocvars;
+ struct LocVar *locvars; /* information about local variables */
+ int lineDefined;
+ TString *source;
+} Proto;
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+/*
+** Closures
+*/
+typedef struct Closure {
+ union {
+ lua_CFunction c; /* C functions */
+ struct Proto *l; /* Lua functions */
+ } f;
+ struct Closure *next;
+ struct Closure *mark; /* marked closures (point to itself when not marked) */
+ short isC; /* 0 for Lua functions, 1 for C functions */
+ short nupvalues;
+ TObject upvalue[1];
+} Closure;
+
+
+#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->isC)
+
+
+typedef struct Node {
+ TObject key;
+ TObject val;
+ struct Node *next; /* for chaining */
+} Node;
+
+typedef struct Hash {
+ Node *node;
+ int htag;
+ int size;
+ Node *firstfree; /* this position is free; all positions after it are full */
+ struct Hash *next;
+ struct Hash *mark; /* marked tables (point to itself when not marked) */
+} Hash;
+
+
+/* unmarked tables and closures are represented by pointing `mark' to
+** themselves
+*/
+#define ismarked(x) ((x)->mark != (x))
+
+
+/*
+** informations about a call (for debugging)
+*/
+typedef struct CallInfo {
+ struct Closure *func; /* function being called */
+ const Instruction **pc; /* current pc of called function */
+ int lastpc; /* last pc traced */
+ int line; /* current line */
+ int refi; /* current index in `lineinfo' */
+} CallInfo;
+
+
+extern const TObject luaO_nilobject;
+extern const char *const luaO_typenames[];
+
+
+#define luaO_typename(o) (luaO_typenames[ttype(o)])
+
+
+lint32 luaO_power2 (lint32 n);
+char *luaO_openspace (lua_State *L, size_t n);
+
+int luaO_equalObj (const TObject *t1, const TObject *t2);
+int luaO_str2d (const char *s, Number *result);
+
+void luaO_verror (lua_State *L, const char *fmt, ...);
+void luaO_chunkid (char *out, const char *source, int len);
+
+
+#endif
diff --git a/src/lua/lopcodes.h b/src/lua/lopcodes.h
new file mode 100644
index 00000000..59740896
--- /dev/null
+++ b/src/lua/lopcodes.h
@@ -0,0 +1,168 @@
+/*
+** $Id: lopcodes.h,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits. Moreover,
+ an instruction can have 0, 1, or 2 arguments. Instructions can
+ have the following types:
+ type 0: no arguments
+ type 1: 1 unsigned argument in the higher bits (called `U')
+ type 2: 1 signed argument in the higher bits (`S')
+ type 3: 1st unsigned argument in the higher bits (`A')
+ 2nd unsigned argument in the middle bits (`B')
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+
+ The size of each argument is defined in `llimits.h'. The usual is an
+ instruction with 32 bits, U arguments with 26 bits (32-6), B arguments
+ with 9 bits, and A arguments with 17 bits (32-6-9). For small
+ installations, the instruction size can be 16, so U has 10 bits,
+ and A and B have 5 bits each.
+===========================================================================*/
+
+
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define CREATE_0(o) ((Instruction)(o))
+#define GET_OPCODE(i) ((OpCode)((i)&MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,0)) | (Instruction)(o)))
+
+#define CREATE_U(o,u) ((Instruction)(o) | ((Instruction)(u)<<POS_U))
+#define GETARG_U(i) ((int)((i)>>POS_U))
+#define SETARG_U(i,u) ((i) = (((i)&MASK0(SIZE_U,POS_U)) | \
+ ((Instruction)(u)<<POS_U)))
+
+#define CREATE_S(o,s) CREATE_U((o),(s)+MAXARG_S)
+#define GETARG_S(i) (GETARG_U(i)-MAXARG_S)
+#define SETARG_S(i,s) SETARG_U((i),(s)+MAXARG_S)
+
+
+#define CREATE_AB(o,a,b) ((Instruction)(o) | ((Instruction)(a)<<POS_A) \
+ | ((Instruction)(b)<<POS_B))
+#define GETARG_A(i) ((int)((i)>>POS_A))
+#define SETARG_A(i,a) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((Instruction)(a)<<POS_A)))
+#define GETARG_B(i) ((int)(((i)>>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((Instruction)(b)<<POS_B)))
+
+
+/*
+** K = U argument used as index to `kstr'
+** J = S argument used as jump offset (relative to pc of next instruction)
+** L = unsigned argument used as index of local variable
+** N = U argument used as index to `knum'
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+name args stack before stack after side effects
+------------------------------------------------------------------------*/
+OP_END,/* - - (return) no results */
+OP_RETURN,/* U v_n-v_x(at u) (return) returns v_x-v_n */
+
+OP_CALL,/* A B v_n-v_1 f(at a) r_b-r_1 f(v1,...,v_n) */
+OP_TAILCALL,/* A B v_n-v_1 f(at a) (return) f(v1,...,v_n) */
+
+OP_PUSHNIL,/* U - nil_1-nil_u */
+OP_POP,/* U a_u-a_1 - */
+
+OP_PUSHINT,/* S - (Number)s */
+OP_PUSHSTRING,/* K - KSTR[k] */
+OP_PUSHNUM,/* N - KNUM[n] */
+OP_PUSHNEGNUM,/* N - -KNUM[n] */
+
+OP_PUSHUPVALUE,/* U - Closure[u] */
+
+OP_GETLOCAL,/* L - LOC[l] */
+OP_GETGLOBAL,/* K - VAR[KSTR[k]] */
+
+OP_GETTABLE,/* - i t t[i] */
+OP_GETDOTTED,/* K t t[KSTR[k]] */
+OP_GETINDEXED,/* L t t[LOC[l]] */
+OP_PUSHSELF,/* K t t t[KSTR[k]] */
+
+OP_CREATETABLE,/* U - newarray(size = u) */
+
+OP_SETLOCAL,/* L x - LOC[l]=x */
+OP_SETGLOBAL,/* K x - VAR[KSTR[k]]=x */
+OP_SETTABLE,/* A B v a_a-a_1 i t (pops b values) t[i]=v */
+
+OP_SETLIST,/* A B v_b-v_1 t t t[i+a*FPF]=v_i */
+OP_SETMAP,/* U v_u k_u - v_1 k_1 t t t[k_i]=v_i */
+
+OP_ADD,/* - y x x+y */
+OP_ADDI,/* S x x+s */
+OP_SUB,/* - y x x-y */
+OP_MULT,/* - y x x*y */
+OP_DIV,/* - y x x/y */
+OP_POW,/* - y x x^y */
+OP_CONCAT,/* U v_u-v_1 v1..-..v_u */
+OP_MINUS,/* - x -x */
+OP_NOT,/* - x (x==nil)? 1 : nil */
+
+OP_JMPNE,/* J y x - (x~=y)? PC+=s */
+OP_JMPEQ,/* J y x - (x==y)? PC+=s */
+OP_JMPLT,/* J y x - (x<y)? PC+=s */
+OP_JMPLE,/* J y x - (x<y)? PC+=s */
+OP_JMPGT,/* J y x - (x>y)? PC+=s */
+OP_JMPGE,/* J y x - (x>=y)? PC+=s */
+
+OP_JMPT,/* J x - (x~=nil)? PC+=s */
+OP_JMPF,/* J x - (x==nil)? PC+=s */
+OP_JMPONT,/* J x (x~=nil)? x : - (x~=nil)? PC+=s */
+OP_JMPONF,/* J x (x==nil)? x : - (x==nil)? PC+=s */
+OP_JMP,/* J - - PC+=s */
+
+OP_PUSHNILJMP,/* - - nil PC++; */
+
+OP_FORPREP,/* J */
+OP_FORLOOP,/* J */
+
+OP_LFORPREP,/* J */
+OP_LFORLOOP,/* J */
+
+OP_CLOSURE/* A B v_b-v_1 closure(KPROTO[a], v_1-v_b) */
+
+} OpCode;
+
+#define NUM_OPCODES ((int)OP_CLOSURE+1)
+
+
+#define ISJUMP(o) (OP_JMPNE <= (o) && (o) <= OP_JMP)
+
+
+
+/* special code to fit a LUA_MULTRET inside an argB */
+#define MULT_RET 255 /* (<=MAXARG_B) */
+#if MULT_RET>MAXARG_B
+#undef MULT_RET
+#define MULT_RET MAXARG_B
+#endif
+
+
+#endif
diff --git a/src/lua/lparser.c b/src/lua/lparser.c
new file mode 100644
index 00000000..1ac1f37b
--- /dev/null
+++ b/src/lua/lparser.c
@@ -0,0 +1,1129 @@
+/*
+** $Id: lparser.c,v 1.8 2004/06/04 13:42:10 neil Exp $
+** LL(1) Parser and code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+
+#ifdef __MWERKS__
+/* To avoid name conflict resulting from the use of prefix header */
+#define stat lua_hack_stat
+#endif /* __MWERKS__ */
+
+
+/*
+** Constructors descriptor:
+** `n' indicates number of elements, and `k' signals whether
+** it is a list constructor (k = 0) or a record constructor (k = 1)
+** or empty (k = ';' or '}')
+*/
+typedef struct Constdesc {
+ int n;
+ int k;
+} Constdesc;
+
+
+typedef struct Breaklabel {
+ struct Breaklabel *previous; /* chain */
+ int breaklist;
+ int stacklevel;
+} Breaklabel;
+
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void body (LexState *ls, int needself, int line);
+static void chunk (LexState *ls);
+static void constructor (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+static void exp1 (LexState *ls);
+
+
+
+static void next (LexState *ls) {
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ }
+ else
+ ls->t.token = luaX_lex(ls, &ls->t.seminfo); /* read next token */
+}
+
+
+static void lookahead (LexState *ls) {
+ LUA_ASSERT(ls->lookahead.token == TK_EOS, "two look-aheads");
+ ls->lookahead.token = luaX_lex(ls, &ls->lookahead.seminfo);
+}
+
+
+static void error_expected (LexState *ls, int token) {
+ char buff[100], t[TOKEN_LEN];
+ luaX_token2str(token, t);
+ sprintf(buff, "`%.20s' expected", t);
+ luaK_error(ls, buff);
+}
+
+
+static void check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+ next(ls);
+}
+
+
+static void check_condition (LexState *ls, int c, const char *msg) {
+ if (!c) luaK_error(ls, msg);
+}
+
+
+static int optional (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+static void check_match (LexState *ls, int what, int who, int where) {
+ if (ls->t.token != what) {
+ if (where == ls->linenumber)
+ error_expected(ls, what);
+ else {
+ char buff[100];
+ char t_what[TOKEN_LEN], t_who[TOKEN_LEN];
+ luaX_token2str(what, t_what);
+ luaX_token2str(who, t_who);
+ sprintf(buff, "`%.20s' expected (to close `%.20s' at line %d)",
+ t_what, t_who, where);
+ luaK_error(ls, buff);
+ }
+ }
+ next(ls);
+}
+
+
+static int string_constant (FuncState *fs, TString *s) {
+ Proto *f = fs->f;
+ int c = s->u.s.constindex;
+ if (c >= f->nkstr || f->kstr[c] != s) {
+ luaM_growvector(fs->L, f->kstr, f->nkstr, 1, TString *,
+ "constant table overflow", MAXARG_U);
+ c = f->nkstr++;
+ f->kstr[c] = s;
+ s->u.s.constindex = c; /* hint for next time */
+ }
+ return c;
+}
+
+
+static void code_string (LexState *ls, TString *s) {
+ luaK_kstr(ls, string_constant(ls->fs, s));
+}
+
+
+static TString *str_checkname (LexState *ls) {
+ TString *ts;
+ check_condition(ls, (ls->t.token == TK_NAME), "<name> expected");
+ ts = ls->t.seminfo.ts;
+ next(ls);
+ return ts;
+}
+
+
+static int checkname (LexState *ls) {
+ return string_constant(ls->fs, str_checkname(ls));
+}
+
+
+static int luaI_registerlocalvar (LexState *ls, TString *varname) {
+ Proto *f = ls->fs->f;
+ luaM_growvector(ls->L, f->locvars, f->nlocvars, 1, LocVar, "", MAX_INT);
+ f->locvars[f->nlocvars].varname = varname;
+ return f->nlocvars++;
+}
+
+
+static void new_localvar (LexState *ls, TString *name, int n) {
+ FuncState *fs = ls->fs;
+ luaX_checklimit(ls, fs->nactloc+n+1, MAXLOCALS, "local variables");
+ fs->actloc[fs->nactloc+n] = luaI_registerlocalvar(ls, name);
+}
+
+
+static void adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ while (nvars--)
+ fs->f->locvars[fs->actloc[fs->nactloc++]].startpc = fs->pc;
+}
+
+
+static void removelocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ while (nvars--)
+ fs->f->locvars[fs->actloc[--fs->nactloc]].endpc = fs->pc;
+}
+
+
+static void new_localvarstr (LexState *ls, const char *name, int n) {
+ new_localvar(ls, luaS_newfixed(ls->L, name), n);
+}
+
+
+static int search_local (LexState *ls, TString *n, expdesc *var) {
+ FuncState *fs;
+ int level = 0;
+ for (fs=ls->fs; fs; fs=fs->prev) {
+ int i;
+ for (i=fs->nactloc-1; i >= 0; i--) {
+ if (n == fs->f->locvars[fs->actloc[i]].varname) {
+ var->k = VLOCAL;
+ var->u.index = i;
+ return level;
+ }
+ }
+ level++; /* `var' not found; check outer level */
+ }
+ var->k = VGLOBAL; /* not found in any level; must be global */
+ return -1;
+}
+
+
+static void singlevar (LexState *ls, TString *n, expdesc *var) {
+ int level = search_local(ls, n, var);
+ if (level >= 1) /* neither local (0) nor global (-1)? */
+ luaX_syntaxerror(ls, "cannot access a variable in outer scope", n->str);
+ else if (level == -1) /* global? */
+ var->u.index = string_constant(ls->fs, n);
+}
+
+
+static int indexupvalue (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int i;
+ for (i=0; i<fs->nupvalues; i++) {
+ if (fs->upvalues[i].k == v->k && fs->upvalues[i].u.index == v->u.index)
+ return i;
+ }
+ /* new one */
+ luaX_checklimit(ls, fs->nupvalues+1, MAXUPVALUES, "upvalues");
+ fs->upvalues[fs->nupvalues] = *v;
+ return fs->nupvalues++;
+}
+
+
+static void pushupvalue (LexState *ls, TString *n) {
+ FuncState *fs = ls->fs;
+ expdesc v;
+ int level = search_local(ls, n, &v);
+ if (level == -1) { /* global? */
+ if (fs->prev == NULL)
+ luaX_syntaxerror(ls, "cannot access upvalue in main", n->str);
+ v.u.index = string_constant(fs->prev, n);
+ }
+ else if (level != 1)
+ luaX_syntaxerror(ls,
+ "upvalue must be global or local to immediately outer scope", n->str);
+ luaK_code1(fs, OP_PUSHUPVALUE, indexupvalue(ls, &v));
+}
+
+
+static void adjust_mult_assign (LexState *ls, int nvars, int nexps) {
+ FuncState *fs = ls->fs;
+ int diff = nexps - nvars;
+ if (nexps > 0 && luaK_lastisopen(fs)) { /* list ends in a function call */
+ diff--; /* do not count function call itself */
+ if (diff <= 0) { /* more variables than values? */
+ luaK_setcallreturns(fs, -diff); /* function call provide extra values */
+ diff = 0; /* no more difference */
+ }
+ else /* more values than variables */
+ luaK_setcallreturns(fs, 0); /* call should provide no value */
+ }
+ /* push or pop eventual difference between list lengths */
+ luaK_adjuststack(fs, diff);
+}
+
+
+static void code_params (LexState *ls, int nparams, int dots) {
+ FuncState *fs = ls->fs;
+ adjustlocalvars(ls, nparams);
+ luaX_checklimit(ls, fs->nactloc, MAXPARAMS, "parameters");
+ fs->f->numparams = fs->nactloc; /* `self' could be there already */
+ fs->f->is_vararg = dots;
+ if (dots) {
+ new_localvarstr(ls, "arg", 0);
+ adjustlocalvars(ls, 1);
+ }
+ luaK_deltastack(fs, fs->nactloc); /* count parameters in the stack */
+}
+
+
+static void enterbreak (FuncState *fs, Breaklabel *bl) {
+ bl->stacklevel = fs->stacklevel;
+ bl->breaklist = NO_JUMP;
+ bl->previous = fs->bl;
+ fs->bl = bl;
+}
+
+
+static void leavebreak (FuncState *fs, Breaklabel *bl) {
+ fs->bl = bl->previous;
+ LUA_ASSERT(bl->stacklevel == fs->stacklevel, "wrong levels");
+ luaK_patchlist(fs, bl->breaklist, luaK_getlabel(fs));
+}
+
+
+static void pushclosure (LexState *ls, FuncState *func) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int i;
+ for (i=0; i<func->nupvalues; i++)
+ luaK_tostack(ls, &func->upvalues[i], 1);
+ luaM_growvector(ls->L, f->kproto, f->nkproto, 1, Proto *,
+ "constant table overflow", MAXARG_A);
+ f->kproto[f->nkproto++] = func->f;
+ luaK_code2(fs, OP_CLOSURE, f->nkproto-1, func->nupvalues);
+}
+
+
+static void open_func (LexState *ls, FuncState *fs) {
+ Proto *f = luaF_newproto(ls->L);
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ fs->L = ls->L;
+ ls->fs = fs;
+ fs->stacklevel = 0;
+ fs->nactloc = 0;
+ fs->nupvalues = 0;
+ fs->bl = NULL;
+ fs->f = f;
+ f->source = ls->source;
+ fs->pc = 0;
+ fs->lasttarget = 0;
+ fs->lastline = 0;
+ fs->jlt = NO_JUMP;
+ f->code = NULL;
+ f->maxstacksize = 0;
+ f->numparams = 0; /* default for main chunk */
+ f->is_vararg = 0; /* default for main chunk */
+}
+
+
+static void close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ luaK_code0(fs, OP_END);
+ luaK_getlabel(fs); /* close eventual list of pending jumps */
+ luaM_reallocvector(L, f->code, fs->pc, Instruction);
+ luaM_reallocvector(L, f->kstr, f->nkstr, TString *);
+ luaM_reallocvector(L, f->knum, f->nknum, Number);
+ luaM_reallocvector(L, f->kproto, f->nkproto, Proto *);
+ removelocalvars(ls, fs->nactloc);
+ luaM_reallocvector(L, f->locvars, f->nlocvars, LocVar);
+ luaM_reallocvector(L, f->lineinfo, f->nlineinfo+1, int);
+ f->lineinfo[f->nlineinfo++] = MAX_INT; /* end flag */
+ luaF_protook(L, f, fs->pc); /* proto is ok now */
+ ls->fs = fs->prev;
+ LUA_ASSERT(fs->bl == NULL, "wrong list end");
+}
+
+
+Proto *luaY_parser (lua_State *L, ZIO *z) {
+ struct LexState lexstate;
+ struct FuncState funcstate;
+ luaX_setinput(L, &lexstate, z, luaS_new(L, zname(z)));
+ open_func(&lexstate, &funcstate);
+ next(&lexstate); /* read first token */
+ chunk(&lexstate);
+ check_condition(&lexstate, (lexstate.t.token == TK_EOS), "<eof> expected");
+ close_func(&lexstate);
+ LUA_ASSERT(funcstate.prev == NULL, "wrong list end");
+ LUA_ASSERT(funcstate.nupvalues == 0, "no upvalues in main");
+ return funcstate.f;
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+static int explist1 (LexState *ls) {
+ /* explist1 -> expr { ',' expr } */
+ int n = 1; /* at least one expression */
+ expdesc v;
+ expr(ls, &v);
+ while (ls->t.token == ',') {
+ luaK_tostack(ls, &v, 1); /* gets only 1 value from previous expression */
+ next(ls); /* skip comma */
+ expr(ls, &v);
+ n++;
+ }
+ luaK_tostack(ls, &v, 0); /* keep open number of values of last expression */
+ return n;
+}
+
+
+static void funcargs (LexState *ls, int slf) {
+ FuncState *fs = ls->fs;
+ int slevel = fs->stacklevel - slf - 1; /* where is func in the stack */
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> '(' [ explist1 ] ')' */
+ int line = ls->linenumber;
+ int nargs = 0;
+ next(ls);
+ if (ls->t.token != ')') /* arg list not empty? */
+ nargs = explist1(ls);
+ check_match(ls, ')', '(', line);
+#ifdef LUA_COMPAT_ARGRET
+ if (nargs > 0) /* arg list is not empty? */
+ luaK_setcallreturns(fs, 1); /* last call returns only 1 value */
+#else
+ UNUSED(nargs); /* to avoid warnings */
+#endif
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */
+ next(ls);
+ break;
+ }
+ default: {
+ luaK_error(ls, "function arguments expected");
+ break;
+ }
+ }
+ fs->stacklevel = slevel; /* call will remove function and arguments */
+ luaK_code2(fs, OP_CALL, slevel, MULT_RET);
+}
+
+
+static void var_or_func_tail (LexState *ls, expdesc *v) {
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* var_or_func_tail -> '.' NAME */
+ next(ls);
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ luaK_kstr(ls, checkname(ls));
+ v->k = VINDEXED;
+ break;
+ }
+ case '[': { /* var_or_func_tail -> '[' exp1 ']' */
+ next(ls);
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ v->k = VINDEXED;
+ exp1(ls);
+ check(ls, ']');
+ break;
+ }
+ case ':': { /* var_or_func_tail -> ':' NAME funcargs */
+ int name;
+ next(ls);
+ name = checkname(ls);
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ luaK_code1(ls->fs, OP_PUSHSELF, name);
+ funcargs(ls, 1);
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* var_or_func_tail -> funcargs */
+ luaK_tostack(ls, v, 1); /* `v' must be on stack */
+ funcargs(ls, 0);
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+ break;
+ }
+ default: return; /* should be follow... */
+ }
+ }
+}
+
+
+static void var_or_func (LexState *ls, expdesc *v) {
+ /* var_or_func -> ['%'] NAME var_or_func_tail */
+ if (optional(ls, '%')) { /* upvalue? */
+ pushupvalue(ls, str_checkname(ls));
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+ }
+ else /* variable name */
+ singlevar(ls, str_checkname(ls), v);
+ var_or_func_tail(ls, v);
+}
+
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+static void recfield (LexState *ls) {
+ /* recfield -> (NAME | '['exp1']') = exp1 */
+ switch (ls->t.token) {
+ case TK_NAME: {
+ luaK_kstr(ls, checkname(ls));
+ break;
+ }
+ case '[': {
+ next(ls);
+ exp1(ls);
+ check(ls, ']');
+ break;
+ }
+ default: luaK_error(ls, "<name> or `[' expected");
+ }
+ check(ls, '=');
+ exp1(ls);
+}
+
+
+static int recfields (LexState *ls) {
+ /* recfields -> recfield { ',' recfield } [','] */
+ FuncState *fs = ls->fs;
+ int n = 1; /* at least one element */
+ recfield(ls);
+ while (ls->t.token == ',') {
+ next(ls);
+ if (ls->t.token == ';' || ls->t.token == '}')
+ break;
+ recfield(ls);
+ n++;
+ if (n%RFIELDS_PER_FLUSH == 0)
+ luaK_code1(fs, OP_SETMAP, RFIELDS_PER_FLUSH);
+ }
+ luaK_code1(fs, OP_SETMAP, n%RFIELDS_PER_FLUSH);
+ return n;
+}
+
+
+static int listfields (LexState *ls) {
+ /* listfields -> exp1 { ',' exp1 } [','] */
+ FuncState *fs = ls->fs;
+ int n = 1; /* at least one element */
+ exp1(ls);
+ while (ls->t.token == ',') {
+ next(ls);
+ if (ls->t.token == ';' || ls->t.token == '}')
+ break;
+ exp1(ls);
+ n++;
+ luaX_checklimit(ls, n/LFIELDS_PER_FLUSH, MAXARG_A,
+ "`item groups' in a list initializer");
+ if (n%LFIELDS_PER_FLUSH == 0)
+ luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH - 1, LFIELDS_PER_FLUSH);
+ }
+ luaK_code2(fs, OP_SETLIST, n/LFIELDS_PER_FLUSH, n%LFIELDS_PER_FLUSH);
+ return n;
+}
+
+
+
+static void constructor_part (LexState *ls, Constdesc *cd) {
+ switch (ls->t.token) {
+ case ';': case '}': { /* constructor_part -> empty */
+ cd->n = 0;
+ cd->k = ls->t.token;
+ break;
+ }
+ case TK_NAME: { /* may be listfields or recfields */
+ lookahead(ls);
+ if (ls->lookahead.token != '=') /* expression? */
+ goto case_default;
+ /* else go through to recfields */
+ }
+ case '[': { /* constructor_part -> recfields */
+ cd->n = recfields(ls);
+ cd->k = 1; /* record */
+ break;
+ }
+ default: { /* constructor_part -> listfields */
+ case_default:
+ cd->n = listfields(ls);
+ cd->k = 0; /* list */
+ break;
+ }
+ }
+}
+
+
+static void constructor (LexState *ls) {
+ /* constructor -> '{' constructor_part [';' constructor_part] '}' */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_code1(fs, OP_CREATETABLE, 0);
+ int nelems;
+ Constdesc cd;
+ check(ls, '{');
+ constructor_part(ls, &cd);
+ nelems = cd.n;
+ if (optional(ls, ';')) {
+ Constdesc other_cd;
+ constructor_part(ls, &other_cd);
+ check_condition(ls, (cd.k != other_cd.k), "invalid constructor syntax");
+ nelems += other_cd.n;
+ }
+ check_match(ls, '}', '{', line);
+ luaX_checklimit(ls, nelems, MAXARG_U, "elements in a table constructor");
+ SETARG_U(fs->f->code[pc], nelems); /* set initial table size */
+}
+
+/* }====================================================================== */
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void simpleexp (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs;
+ switch (ls->t.token) {
+ case TK_NUMBER: { /* simpleexp -> NUMBER */
+ Number r = ls->t.seminfo.r;
+ next(ls);
+ luaK_number(fs, r);
+ break;
+ }
+ case TK_STRING: { /* simpleexp -> STRING */
+ code_string(ls, ls->t.seminfo.ts); /* must use `seminfo' before `next' */
+ next(ls);
+ break;
+ }
+ case TK_NIL: { /* simpleexp -> NIL */
+ luaK_adjuststack(fs, -1);
+ next(ls);
+ break;
+ }
+ case '{': { /* simpleexp -> constructor */
+ constructor(ls);
+ break;
+ }
+ case TK_FUNCTION: { /* simpleexp -> FUNCTION body */
+ next(ls);
+ body(ls, 0, ls->linenumber);
+ break;
+ }
+ case '(': { /* simpleexp -> '(' expr ')' */
+ next(ls);
+ expr(ls, v);
+ check(ls, ')');
+ return;
+ }
+ case TK_NAME: case '%': {
+ var_or_func(ls, v);
+ return;
+ }
+ default: {
+ luaK_error(ls, "<expression> expected");
+ return;
+ }
+ }
+ v->k = VEXP;
+ v->u.l.t = v->u.l.f = NO_JUMP;
+}
+
+
+static void exp1 (LexState *ls) {
+ expdesc v;
+ expr(ls, &v);
+ luaK_tostack(ls, &v, 1);
+}
+
+
+static UnOpr getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MULT;
+ case '/': return OPR_DIV;
+ case '^': return OPR_POW;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+static const struct {
+ char left; /* left priority for each binary operator */
+ char right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {5, 5}, {5, 5}, {6, 6}, {6, 6}, /* arithmetic */
+ {9, 8}, {4, 3}, /* power and concat (right associative) */
+ {2, 2}, {2, 2}, /* equality */
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, /* order */
+ {1, 1}, {1, 1} /* logical */
+};
+
+#define UNARY_PRIORITY 7 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simplexep | unop subexpr) { binop subexpr }
+** where `binop' is any binary operator with a priority higher than `limit'
+*/
+static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
+ BinOpr op;
+ UnOpr uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) {
+ next(ls);
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls, uop, v);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than `limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ next(ls);
+ luaK_infix(ls, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls, op, v, &v2);
+ op = nextop;
+ }
+ return op; /* return first untreated operator */
+}
+
+
+static void expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, -1);
+}
+
+/* }==================================================================== */
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static int block_follow (int token) {
+ switch (token) {
+ case TK_ELSE: case TK_ELSEIF: case TK_END:
+ case TK_UNTIL: case TK_EOS:
+ return 1;
+ default: return 0;
+ }
+}
+
+
+static void block (LexState *ls) {
+ /* block -> chunk */
+ FuncState *fs = ls->fs;
+ int nactloc = fs->nactloc;
+ chunk(ls);
+ luaK_adjuststack(fs, fs->nactloc - nactloc); /* remove local variables */
+ removelocalvars(ls, fs->nactloc - nactloc);
+}
+
+
+static int assignment (LexState *ls, expdesc *v, int nvars) {
+ int left = 0; /* number of values left in the stack after assignment */
+ luaX_checklimit(ls, nvars, MAXVARSLH, "variables in a multiple assignment");
+ if (ls->t.token == ',') { /* assignment -> ',' NAME assignment */
+ expdesc nv;
+ next(ls);
+ var_or_func(ls, &nv);
+ check_condition(ls, (nv.k != VEXP), "syntax error");
+ left = assignment(ls, &nv, nvars+1);
+ }
+ else { /* assignment -> '=' explist1 */
+ int nexps;
+ check(ls, '=');
+ nexps = explist1(ls);
+ adjust_mult_assign(ls, nvars, nexps);
+ }
+ if (v->k != VINDEXED)
+ luaK_storevar(ls, v);
+ else { /* there may be garbage between table-index and value */
+ luaK_code2(ls->fs, OP_SETTABLE, left+nvars+2, 1);
+ left += 2;
+ }
+ return left;
+}
+
+
+static void cond (LexState *ls, expdesc *v) {
+ /* cond -> exp */
+ expr(ls, v); /* read condition */
+ luaK_goiftrue(ls->fs, v, 0);
+}
+
+
+static void whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int while_init = luaK_getlabel(fs);
+ expdesc v;
+ Breaklabel bl;
+ enterbreak(fs, &bl);
+ next(ls);
+ cond(ls, &v);
+ check(ls, TK_DO);
+ block(ls);
+ luaK_patchlist(fs, luaK_jump(fs), while_init);
+ luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
+ check_match(ls, TK_END, TK_WHILE, line);
+ leavebreak(fs, &bl);
+}
+
+
+static void repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ expdesc v;
+ Breaklabel bl;
+ enterbreak(fs, &bl);
+ next(ls);
+ block(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ cond(ls, &v);
+ luaK_patchlist(fs, v.u.l.f, repeat_init);
+ leavebreak(fs, &bl);
+}
+
+
+static void forbody (LexState *ls, int nvar, OpCode prepfor, OpCode loopfor) {
+ /* forbody -> DO block END */
+ FuncState *fs = ls->fs;
+ int prep = luaK_code1(fs, prepfor, NO_JUMP);
+ int blockinit = luaK_getlabel(fs);
+ check(ls, TK_DO);
+ adjustlocalvars(ls, nvar); /* scope for control variables */
+ block(ls);
+ luaK_patchlist(fs, luaK_code1(fs, loopfor, NO_JUMP), blockinit);
+ luaK_patchlist(fs, prep, luaK_getlabel(fs));
+ removelocalvars(ls, nvar);
+}
+
+
+static void fornum (LexState *ls, TString *varname) {
+ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ FuncState *fs = ls->fs;
+ check(ls, '=');
+ exp1(ls); /* initial value */
+ check(ls, ',');
+ exp1(ls); /* limit */
+ if (optional(ls, ','))
+ exp1(ls); /* optional step */
+ else
+ luaK_code1(fs, OP_PUSHINT, 1); /* default step */
+ new_localvar(ls, varname, 0);
+ new_localvarstr(ls, "(limit)", 1);
+ new_localvarstr(ls, "(step)", 2);
+ forbody(ls, 3, OP_FORPREP, OP_FORLOOP);
+}
+
+
+static void forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME,NAME IN exp1 forbody */
+ TString *valname;
+ check(ls, ',');
+ valname = str_checkname(ls);
+ /* next test is dirty, but avoids `in' being a reserved word */
+ check_condition(ls,
+ (ls->t.token == TK_NAME && ls->t.seminfo.ts == luaS_new(ls->L, "in")),
+ "`in' expected");
+ next(ls); /* skip `in' */
+ exp1(ls); /* table */
+ new_localvarstr(ls, "(table)", 0);
+ new_localvar(ls, indexname, 1);
+ new_localvar(ls, valname, 2);
+ forbody(ls, 3, OP_LFORPREP, OP_LFORLOOP);
+}
+
+
+static void forstat (LexState *ls, int line) {
+ /* forstat -> fornum | forlist */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ Breaklabel bl;
+ enterbreak(fs, &bl);
+ next(ls); /* skip `for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname); break;
+ case ',': forlist(ls, varname); break;
+ default: luaK_error(ls, "`=' or `,' expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leavebreak(fs, &bl);
+}
+
+
+static void test_then_block (LexState *ls, expdesc *v) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ next(ls); /* skip IF or ELSEIF */
+ cond(ls, v);
+ check(ls, TK_THEN);
+ block(ls); /* `then' part */
+}
+
+
+static void ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ expdesc v;
+ int escapelist = NO_JUMP;
+ test_then_block(ls, &v); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
+ test_then_block(ls, &v); /* ELSEIF cond THEN block */
+ }
+ if (ls->t.token == TK_ELSE) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchlist(fs, v.u.l.f, luaK_getlabel(fs));
+ next(ls); /* skip ELSE */
+ block(ls); /* `else' part */
+ }
+ else
+ luaK_concat(fs, &escapelist, v.u.l.f);
+ luaK_patchlist(fs, escapelist, luaK_getlabel(fs));
+ check_match(ls, TK_END, TK_IF, line);
+}
+
+
+static void localstat (LexState *ls) {
+ /* stat -> LOCAL NAME {',' NAME} ['=' explist1] */
+ int nvars = 0;
+ int nexps;
+ do {
+ next(ls); /* skip LOCAL or ',' */
+ new_localvar(ls, str_checkname(ls), nvars++);
+ } while (ls->t.token == ',');
+ if (optional(ls, '='))
+ nexps = explist1(ls);
+ else
+ nexps = 0;
+ adjust_mult_assign(ls, nvars, nexps);
+ adjustlocalvars(ls, nvars);
+}
+
+
+static int funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME [':' NAME | '.' NAME] */
+ int needself = 0;
+ singlevar(ls, str_checkname(ls), v);
+ if (ls->t.token == ':' || ls->t.token == '.') {
+ needself = (ls->t.token == ':');
+ next(ls);
+ luaK_tostack(ls, v, 1);
+ luaK_kstr(ls, checkname(ls));
+ v->k = VINDEXED;
+ }
+ return needself;
+}
+
+
+static void funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int needself;
+ expdesc v;
+ next(ls); /* skip FUNCTION */
+ needself = funcname(ls, &v);
+ body(ls, needself, line);
+ luaK_storevar(ls, &v);
+}
+
+
+static void namestat (LexState *ls) {
+ /* stat -> func | ['%'] NAME assignment */
+ FuncState *fs = ls->fs;
+ expdesc v;
+ var_or_func(ls, &v);
+ if (v.k == VEXP) { /* stat -> func */
+ check_condition(ls, luaK_lastisopen(fs), "syntax error"); /* an upvalue? */
+ luaK_setcallreturns(fs, 0); /* call statement uses no results */
+ }
+ else { /* stat -> ['%'] NAME assignment */
+ int left = assignment(ls, &v, 1);
+ luaK_adjuststack(fs, left); /* remove eventual garbage left on stack */
+ }
+}
+
+
+static void retstat (LexState *ls) {
+ /* stat -> RETURN explist */
+ FuncState *fs = ls->fs;
+ next(ls); /* skip RETURN */
+ if (!block_follow(ls->t.token))
+ explist1(ls); /* optional return values */
+ luaK_code1(fs, OP_RETURN, ls->fs->nactloc);
+ fs->stacklevel = fs->nactloc; /* removes all temp values */
+}
+
+
+static void breakstat (LexState *ls) {
+ /* stat -> BREAK [NAME] */
+ FuncState *fs = ls->fs;
+ int currentlevel = fs->stacklevel;
+ Breaklabel *bl = fs->bl;
+ if (!bl)
+ luaK_error(ls, "no loop to break");
+ next(ls); /* skip BREAK */
+ luaK_adjuststack(fs, currentlevel - bl->stacklevel);
+ luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
+ /* correct stack for compiler and symbolic execution */
+ luaK_adjuststack(fs, bl->stacklevel - currentlevel);
+}
+
+
+static int stat (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ switch (ls->t.token) {
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ return 0;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ return 0;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ return 0;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ return 0;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ return 0;
+ }
+ case TK_FUNCTION: { /* stat -> funcstat */
+ funcstat(ls, line);
+ return 0;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ localstat(ls);
+ return 0;
+ }
+ case TK_NAME: case '%': { /* stat -> namestat */
+ namestat(ls);
+ return 0;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ retstat(ls);
+ return 1; /* must be last statement */
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ breakstat(ls);
+ return 1; /* must be last statement */
+ }
+ default: {
+ luaK_error(ls, "<statement> expected");
+ return 0; /* to avoid warnings */
+ }
+ }
+}
+
+
+static void parlist (LexState *ls) {
+ /* parlist -> [ param { ',' param } ] */
+ int nparams = 0;
+ int dots = 0;
+ if (ls->t.token != ')') { /* is `parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_DOTS: next(ls); dots = 1; break;
+ case TK_NAME: new_localvar(ls, str_checkname(ls), nparams++); break;
+ default: luaK_error(ls, "<name> or `...' expected");
+ }
+ } while (!dots && optional(ls, ','));
+ }
+ code_params(ls, nparams, dots);
+}
+
+
+static void body (LexState *ls, int needself, int line) {
+ /* body -> '(' parlist ')' chunk END */
+ FuncState new_fs;
+ open_func(ls, &new_fs);
+ new_fs.f->lineDefined = line;
+ check(ls, '(');
+ if (needself) {
+ new_localvarstr(ls, "self", 0);
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ check(ls, ')');
+ chunk(ls);
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ close_func(ls);
+ pushclosure(ls, &new_fs);
+}
+
+
+/* }====================================================================== */
+
+
+static void chunk (LexState *ls) {
+ /* chunk -> { stat [';'] } */
+ int islast = 0;
+ while (!islast && !block_follow(ls->t.token)) {
+ islast = stat(ls);
+ optional(ls, ';');
+ LUA_ASSERT(ls->fs->stacklevel == ls->fs->nactloc,
+ "stack size != # local vars");
+ }
+}
+
diff --git a/src/lua/lparser.h b/src/lua/lparser.h
new file mode 100644
index 00000000..d83fb5f1
--- /dev/null
+++ b/src/lua/lparser.h
@@ -0,0 +1,60 @@
+/*
+** $Id: lparser.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** LL(1) Parser and code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression descriptor
+*/
+
+typedef enum {
+ VGLOBAL,
+ VLOCAL,
+ VINDEXED,
+ VEXP
+} expkind;
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ int index; /* VGLOBAL: `kstr' index of global name; VLOCAL: stack index */
+ struct {
+ int t; /* patch list of `exit when true' */
+ int f; /* patch list of `exit when false' */
+ } l;
+ } u;
+} expdesc;
+
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct lua_State *L; /* copy of the Lua state */
+ int pc; /* next position to code */
+ int lasttarget; /* `pc' of last `jump target' */
+ int jlt; /* list of jumps to `lasttarget' */
+ short stacklevel; /* number of values on activation register */
+ short nactloc; /* number of active local variables */
+ short nupvalues; /* number of upvalues */
+ int lastline; /* line where last `lineinfo' was generated */
+ struct Breaklabel *bl; /* chain of breakable blocks */
+ expdesc upvalues[MAXUPVALUES]; /* upvalues */
+ int actloc[MAXLOCALS]; /* local-variable stack (indices to locvars) */
+} FuncState;
+
+
+Proto *luaY_parser (lua_State *L, ZIO *z);
+
+
+#endif
diff --git a/src/lua/lstate.c b/src/lua/lstate.c
new file mode 100644
index 00000000..6310cb7e
--- /dev/null
+++ b/src/lua/lstate.c
@@ -0,0 +1,121 @@
+/*
+** $Id: lstate.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+#ifdef LUA_DEBUG
+static lua_State *lua_state = NULL;
+void luaB_opentests (lua_State *L);
+#endif
+
+
+/*
+** built-in implementation for ERRORMESSAGE. In a "correct" environment
+** ERRORMESSAGE should have an external definition, and so this function
+** would not be used.
+*/
+static int errormessage (lua_State *L) {
+ const char *s = lua_tostring(L, 1);
+ if (s == NULL) s = "(no message)";
+ fprintf(stderr, "error: %s\n", s);
+ return 0;
+}
+
+
+/*
+** open parts that may cause memory-allocation errors
+*/
+static void f_luaopen (lua_State *L, void *ud) {
+ int stacksize = *(int *)ud;
+ if (stacksize == 0)
+ stacksize = DEFAULT_STACK_SIZE;
+ else
+ stacksize += LUA_MINSTACK;
+ L->gt = luaH_new(L, 10); /* table of globals */
+ luaD_init(L, stacksize);
+ luaS_init(L);
+ luaX_init(L);
+ luaT_init(L);
+ lua_newtable(L);
+ lua_ref(L, 1); /* create registry */
+ lua_register(L, LUA_ERRORMESSAGE, errormessage);
+#ifdef LUA_DEBUG
+ luaB_opentests(L);
+ if (lua_state == NULL) lua_state = L; /* keep first state to be opened */
+#endif
+ LUA_ASSERT(lua_gettop(L) == 0, "wrong API stack");
+}
+
+
+LUA_API lua_State *lua_open (int stacksize) {
+ lua_State *L = luaM_new(NULL, lua_State);
+ if (L == NULL) return NULL; /* memory allocation error */
+ L->stack = NULL;
+ L->strt.size = L->udt.size = 0;
+ L->strt.nuse = L->udt.nuse = 0;
+ L->strt.hash = NULL;
+ L->udt.hash = NULL;
+ L->Mbuffer = NULL;
+ L->Mbuffsize = 0;
+ L->rootproto = NULL;
+ L->rootcl = NULL;
+ L->roottable = NULL;
+ L->TMtable = NULL;
+ L->last_tag = -1;
+ L->refArray = NULL;
+ L->refSize = 0;
+ L->refFree = NONEXT;
+ L->nblocks = sizeof(lua_State);
+ L->GCthreshold = MAX_INT; /* to avoid GC during pre-definitions */
+ L->callhook = NULL;
+ L->linehook = NULL;
+ L->allowhooks = 1;
+ L->errorJmp = NULL;
+ if (luaD_runprotected(L, f_luaopen, &stacksize) != 0) {
+ /* memory allocation error: free partial state */
+ lua_close(L);
+ return NULL;
+ }
+ L->GCthreshold = 2*L->nblocks;
+ return L;
+}
+
+
+LUA_API void lua_close (lua_State *L) {
+ LUA_ASSERT(L != lua_state || lua_gettop(L) == 0, "garbage in C stack");
+ luaC_collect(L, 1); /* collect all elements */
+ LUA_ASSERT(L->rootproto == NULL, "list should be empty");
+ LUA_ASSERT(L->rootcl == NULL, "list should be empty");
+ LUA_ASSERT(L->roottable == NULL, "list should be empty");
+ luaS_freeall(L);
+ if (L->stack)
+ L->nblocks -= (L->stack_last - L->stack + 1)*sizeof(TObject);
+ luaM_free(L, L->stack);
+ L->nblocks -= (L->last_tag+1)*sizeof(struct TM);
+ luaM_free(L, L->TMtable);
+ L->nblocks -= (L->refSize)*sizeof(struct Ref);
+ luaM_free(L, L->refArray);
+ L->nblocks -= (L->Mbuffsize)*sizeof(char);
+ luaM_free(L, L->Mbuffer);
+ LUA_ASSERT(L->nblocks == sizeof(lua_State), "wrong count for nblocks");
+ luaM_free(L, L);
+ LUA_ASSERT(L != lua_state || memdebug_numblocks == 0, "memory leak!");
+ LUA_ASSERT(L != lua_state || memdebug_total == 0,"memory leak!");
+}
+
diff --git a/src/lua/lstate.h b/src/lua/lstate.h
new file mode 100644
index 00000000..ee02db01
--- /dev/null
+++ b/src/lua/lstate.h
@@ -0,0 +1,77 @@
+/*
+** $Id: lstate.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lobject.h"
+#include "lua.h"
+#include "luadebug.h"
+
+
+
+typedef TObject *StkId; /* index to stack elements */
+
+
+/*
+** marks for Reference array
+*/
+#define NONEXT -1 /* to end the free list */
+#define HOLD -2
+#define COLLECTED -3
+#define LOCK -4
+
+
+struct Ref {
+ TObject o;
+ int st; /* can be LOCK, HOLD, COLLECTED, or next (for free list) */
+};
+
+
+struct lua_longjmp; /* defined in ldo.c */
+struct TM; /* defined in ltm.h */
+
+
+typedef struct stringtable {
+ int size;
+ lint32 nuse; /* number of elements */
+ TString **hash;
+} stringtable;
+
+
+
+struct lua_State {
+ /* thread-specific state */
+ StkId top; /* first free slot in the stack */
+ StkId stack; /* stack base */
+ StkId stack_last; /* last free slot in the stack */
+ int stacksize;
+ StkId Cbase; /* base for current C function */
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ char *Mbuffer; /* global buffer */
+ size_t Mbuffsize; /* size of Mbuffer */
+ /* global state */
+ Proto *rootproto; /* list of all prototypes */
+ Closure *rootcl; /* list of all closures */
+ Hash *roottable; /* list of all tables */
+ stringtable strt; /* hash table for strings */
+ stringtable udt; /* hash table for udata */
+ Hash *gt; /* table for globals */
+ struct TM *TMtable; /* table for tag methods */
+ int last_tag; /* last used tag in TMtable */
+ struct Ref *refArray; /* locked objects */
+ int refSize; /* size of refArray */
+ int refFree; /* list of free positions in refArray */
+ unsigned long GCthreshold;
+ unsigned long nblocks; /* number of `bytes' currently allocated */
+ lua_Hook callhook;
+ lua_Hook linehook;
+ int allowhooks;
+};
+
+
+#endif
+
diff --git a/src/lua/lstring.c b/src/lua/lstring.c
new file mode 100644
index 00000000..7293e195
--- /dev/null
+++ b/src/lua/lstring.c
@@ -0,0 +1,155 @@
+/*
+** $Id: lstring.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+
+/*
+** type equivalent to TString, but with maximum alignment requirements
+*/
+union L_UTString {
+ TString ts;
+ union L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+};
+
+
+
+void luaS_init (lua_State *L) {
+ L->strt.hash = luaM_newvector(L, 1, TString *);
+ L->udt.hash = luaM_newvector(L, 1, TString *);
+ L->nblocks += 2*sizeof(TString *);
+ L->strt.size = L->udt.size = 1;
+ L->strt.nuse = L->udt.nuse = 0;
+ L->strt.hash[0] = L->udt.hash[0] = NULL;
+}
+
+
+void luaS_freeall (lua_State *L) {
+ LUA_ASSERT(L->strt.nuse==0, "non-empty string table");
+ L->nblocks -= (L->strt.size + L->udt.size)*sizeof(TString *);
+ luaM_free(L, L->strt.hash);
+ LUA_ASSERT(L->udt.nuse==0, "non-empty udata table");
+ luaM_free(L, L->udt.hash);
+}
+
+
+static unsigned long hash_s (const char *s, size_t l) {
+ unsigned long h = l; /* seed */
+ size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */
+ for (; l>=step; l-=step)
+ h = h ^ ((h<<5)+(h>>2)+(unsigned char)*(s++));
+ return h;
+}
+
+
+void luaS_resize (lua_State *L, stringtable *tb, int newsize) {
+ TString **newhash = luaM_newvector(L, newsize, TString *);
+ int i;
+ for (i=0; i<newsize; i++) newhash[i] = NULL;
+ /* rehash */
+ for (i=0; i<tb->size; i++) {
+ TString *p = tb->hash[i];
+ while (p) { /* for each node in the list */
+ TString *next = p->nexthash; /* save next */
+ unsigned long h = (tb == &L->strt) ? p->u.s.hash : IntPoint(p->u.d.value);
+ int h1 = h&(newsize-1); /* new position */
+ LUA_ASSERT(h%newsize == (h&(newsize-1)),
+ "a&(x-1) == a%x, for x power of 2");
+ p->nexthash = newhash[h1]; /* chain it in new position */
+ newhash[h1] = p;
+ p = next;
+ }
+ }
+ luaM_free(L, tb->hash);
+ L->nblocks += (newsize - tb->size)*sizeof(TString *);
+ tb->size = newsize;
+ tb->hash = newhash;
+}
+
+
+static void newentry (lua_State *L, stringtable *tb, TString *ts, int h) {
+ ts->nexthash = tb->hash[h]; /* chain new entry */
+ tb->hash[h] = ts;
+ tb->nuse++;
+ if (tb->nuse > (lint32)tb->size && tb->size < MAX_INT/2) /* too crowded? */
+ luaS_resize(L, tb, tb->size*2);
+}
+
+
+
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ unsigned long h = hash_s(str, l);
+ int h1 = h & (L->strt.size-1);
+ TString *ts;
+ for (ts = L->strt.hash[h1]; ts; ts = ts->nexthash) {
+ if (ts->len == l && (memcmp(str, ts->str, l) == 0))
+ return ts;
+ }
+ /* not found */
+ ts = (TString *)luaM_malloc(L, sizestring(l));
+ ts->marked = 0;
+ ts->nexthash = NULL;
+ ts->len = l;
+ ts->u.s.hash = h;
+ ts->u.s.constindex = 0;
+ memcpy(ts->str, str, l);
+ ts->str[l] = 0; /* ending 0 */
+ L->nblocks += sizestring(l);
+ newentry(L, &L->strt, ts, h1); /* insert it on table */
+ return ts;
+}
+
+
+TString *luaS_newudata (lua_State *L, size_t s, void *udata) {
+ union L_UTString *uts = (union L_UTString *)luaM_malloc(L,
+ (lint32)sizeof(union L_UTString)+s);
+ TString *ts = &uts->ts;
+ ts->marked = 0;
+ ts->nexthash = NULL;
+ ts->len = s;
+ ts->u.d.tag = 0;
+ ts->u.d.value = (udata == NULL) ? uts+1 : udata;
+ L->nblocks += sizestring(s);
+ /* insert it on table */
+ newentry(L, &L->udt, ts, IntPoint(ts->u.d.value) & (L->udt.size-1));
+ return ts;
+}
+
+
+TString *luaS_createudata (lua_State *L, void *udata, int tag) {
+ int h1 = IntPoint(udata) & (L->udt.size-1);
+ TString *ts;
+ for (ts = L->udt.hash[h1]; ts; ts = ts->nexthash) {
+ if (udata == ts->u.d.value && (tag == ts->u.d.tag || tag == LUA_ANYTAG))
+ return ts;
+ }
+ /* not found */
+ ts = luaS_newudata(L, 0, udata);
+ if (tag != LUA_ANYTAG)
+ ts->u.d.tag = tag;
+ return ts;
+}
+
+
+TString *luaS_new (lua_State *L, const char *str) {
+ return luaS_newlstr(L, str, strlen(str));
+}
+
+
+TString *luaS_newfixed (lua_State *L, const char *str) {
+ TString *ts = luaS_new(L, str);
+ if (ts->marked == 0) ts->marked = FIXMARK; /* avoid GC */
+ return ts;
+}
+
diff --git a/src/lua/lstring.h b/src/lua/lstring.h
new file mode 100644
index 00000000..f23159ec
--- /dev/null
+++ b/src/lua/lstring.h
@@ -0,0 +1,37 @@
+/*
+** $Id: lstring.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+** any TString with mark>=FIXMARK is never collected.
+** Marks>=RESERVEDMARK are used to identify reserved words.
+*/
+#define FIXMARK 2
+#define RESERVEDMARK 3
+
+
+#define sizestring(l) ((long)sizeof(TString) + \
+ ((long)(l+1)-TSPACK)*(long)sizeof(char))
+
+
+void luaS_init (lua_State *L);
+void luaS_resize (lua_State *L, stringtable *tb, int newsize);
+TString *luaS_newudata (lua_State *L, size_t s, void *udata);
+TString *luaS_createudata (lua_State *L, void *udata, int tag);
+void luaS_freeall (lua_State *L);
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+TString *luaS_new (lua_State *L, const char *str);
+TString *luaS_newfixed (lua_State *L, const char *str);
+
+
+#endif
diff --git a/src/lua/lstrlib.c b/src/lua/lstrlib.c
new file mode 100644
index 00000000..051eccf7
--- /dev/null
+++ b/src/lua/lstrlib.c
@@ -0,0 +1,621 @@
+/*
+** $Id: lstrlib.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_check_lstr(L, 1, &l);
+ lua_pushnumber(L, l);
+ return 1;
+}
+
+
+static long posrelat (long pos, size_t len) {
+ /* relative string position: negative means back from end */
+ return (pos>=0) ? pos : (long)len+pos+1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ long start = posrelat(luaL_check_long(L, 2), l);
+ long end = posrelat(luaL_opt_long(L, 3, -1), l);
+ if (start < 1) start = 1;
+ if (end > (long)l) end = l;
+ if (start <= end)
+ lua_pushlstring(L, s+start-1, end-start+1);
+ else lua_pushstring(L, "");
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_putchar(&b, tolower((unsigned char)(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_putchar(&b, toupper((unsigned char)(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static int str_rep (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ int n = luaL_check_int(L, 2);
+ luaL_buffinit(L, &b);
+ while (n-- > 0)
+ luaL_addlstring(&b, s, l);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_check_lstr(L, 1, &l);
+ long pos = posrelat(luaL_opt_long(L, 2, 1), l);
+ luaL_arg_check(L, 0<pos && (size_t)pos<=l, 2, "out of range");
+ lua_pushnumber(L, (unsigned char)s[pos-1]);
+ return 1;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i=1; i<=n; i++) {
+ int c = luaL_check_int(L, i);
+ luaL_arg_check(L, (unsigned char)c == c, i, "invalid value");
+ luaL_putchar(&b, (unsigned char)c);
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+#ifndef MAX_CAPTURES
+#define MAX_CAPTURES 32 /* arbitrary limit */
+#endif
+
+
+struct Capture {
+ const char *src_end; /* end ('\0') of source string */
+ int level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ long len; /* -1 signals unfinished capture */
+ } capture[MAX_CAPTURES];
+};
+
+
+#define ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (lua_State *L, int l, struct Capture *cap) {
+ l -= '1';
+ if (!(0 <= l && l < cap->level && cap->capture[l].len != -1))
+ lua_error(L, "invalid capture index");
+ return l;
+}
+
+
+static int capture_to_close (lua_State *L, struct Capture *cap) {
+ int level = cap->level;
+ for (level--; level>=0; level--)
+ if (cap->capture[level].len == -1) return level;
+ lua_error(L, "invalid pattern capture");
+ return 0; /* to avoid warnings */
+}
+
+
+const char *luaI_classend (lua_State *L, const char *p) {
+ switch (*p++) {
+ case ESC:
+ if (*p == '\0') lua_error(L, "malformed pattern (ends with `%')");
+ return p+1;
+ case '[':
+ if (*p == '^') p++;
+ do { /* look for a ']' */
+ if (*p == '\0') lua_error(L, "malformed pattern (missing `]')");
+ if (*(p++) == ESC && *p != '\0') p++; /* skip escapes (e.g. '%]') */
+ } while (*p != ']');
+ return p+1;
+ default:
+ return p;
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == '\0'); break;
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+
+static int matchbracketclass (int c, const char *p, const char *endclass) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the '^' */
+ }
+ while (++p < endclass) {
+ if (*p == ESC) {
+ p++;
+ if (match_class(c, (unsigned char)*p))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < endclass)) {
+ p+=2;
+ if ((int)(unsigned char)*(p-2) <= c && c <= (int)(unsigned char)*p)
+ return sig;
+ }
+ else if ((int)(unsigned char)*p == c) return sig;
+ }
+ return !sig;
+}
+
+
+
+int luaI_singlematch (int c, const char *p, const char *ep) {
+ switch (*p) {
+ case '.': /* matches any char */
+ return 1;
+ case ESC:
+ return match_class(c, (unsigned char)*(p+1));
+ case '[':
+ return matchbracketclass(c, p, ep-1);
+ default:
+ return ((unsigned char)*p == c);
+ }
+}
+
+
+static const char *match (lua_State *L, const char *s, const char *p,
+ struct Capture *cap);
+
+
+static const char *matchbalance (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ if (*p == 0 || *(p+1) == 0)
+ lua_error(L, "unbalanced pattern");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < cap->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (lua_State *L, const char *s, const char *p,
+ const char *ep, struct Capture *cap) {
+ long i = 0; /* counts maximum expand for item */
+ while ((s+i)<cap->src_end && luaI_singlematch((unsigned char)*(s+i), p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(L, (s+i), ep+1, cap);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (lua_State *L, const char *s, const char *p,
+ const char *ep, struct Capture *cap) {
+ for (;;) {
+ const char *res = match(L, s, ep+1, cap);
+ if (res != NULL)
+ return res;
+ else if (s<cap->src_end && luaI_singlematch((unsigned char)*s, p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ const char *res;
+ int level = cap->level;
+ if (level >= MAX_CAPTURES) lua_error(L, "too many captures");
+ cap->capture[level].init = s;
+ cap->capture[level].len = -1;
+ cap->level = level+1;
+ if ((res=match(L, s, p+1, cap)) == NULL) /* match failed? */
+ cap->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ int l = capture_to_close(L, cap);
+ const char *res;
+ cap->capture[l].len = s - cap->capture[l].init; /* close capture */
+ if ((res = match(L, s, p+1, cap)) == NULL) /* match failed? */
+ cap->capture[l].len = -1; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (lua_State *L, const char *s, int level,
+ struct Capture *cap) {
+ int l = check_capture(L, level, cap);
+ size_t len = cap->capture[l].len;
+ if ((size_t)(cap->src_end-s) >= len &&
+ memcmp(cap->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (lua_State *L, const char *s, const char *p,
+ struct Capture *cap) {
+ init: /* using goto's to optimize tail recursion */
+ switch (*p) {
+ case '(': /* start capture */
+ return start_capture(L, s, p, cap);
+ case ')': /* end capture */
+ return end_capture(L, s, p, cap);
+ case ESC: /* may be %[0-9] or %b */
+ if (isdigit((unsigned char)(*(p+1)))) { /* capture? */
+ s = match_capture(L, s, *(p+1), cap);
+ if (s == NULL) return NULL;
+ p+=2; goto init; /* else return match(L, s, p+2, cap) */
+ }
+ else if (*(p+1) == 'b') { /* balanced string? */
+ s = matchbalance(L, s, p+2, cap);
+ if (s == NULL) return NULL;
+ p+=4; goto init; /* else return match(L, s, p+4, cap); */
+ }
+ else goto dflt; /* case default */
+ case '\0': /* end of pattern */
+ return s; /* match succeeded */
+ case '$':
+ if (*(p+1) == '\0') /* is the '$' the last char in pattern? */
+ return (s == cap->src_end) ? s : NULL; /* check end of string */
+ else goto dflt;
+ default: dflt: { /* it is a pattern item */
+ const char *ep = luaI_classend(L, p); /* points to what is next */
+ int m = s<cap->src_end && luaI_singlematch((unsigned char)*s, p, ep);
+ switch (*ep) {
+ case '?': { /* optional */
+ const char *res;
+ if (m && ((res=match(L, s+1, ep+1, cap)) != NULL))
+ return res;
+ p=ep+1; goto init; /* else return match(L, s, ep+1, cap); */
+ }
+ case '*': /* 0 or more repetitions */
+ return max_expand(L, s, p, ep, cap);
+ case '+': /* 1 or more repetitions */
+ return (m ? max_expand(L, s+1, p, ep, cap) : NULL);
+ case '-': /* 0 or more repetitions (minimum) */
+ return min_expand(L, s, p, ep, cap);
+ default:
+ if (!m) return NULL;
+ s++; p=ep; goto init; /* else return match(L, s+1, ep, cap); */
+ }
+ }
+ }
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative `l1' */
+ else {
+ const char *init; /* to search for a `*s2' inside `s1' */
+ l2--; /* 1st char will be checked by `memchr' */
+ l1 = l1-l2; /* `s2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct `l1' and `s1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+static int push_captures (lua_State *L, struct Capture *cap) {
+ int i;
+ luaL_checkstack(L, cap->level, "too many captures");
+ for (i=0; i<cap->level; i++) {
+ int l = cap->capture[i].len;
+ if (l == -1) lua_error(L, "unfinished capture");
+ lua_pushlstring(L, cap->capture[i].init, l);
+ }
+ return cap->level; /* number of strings pushed */
+}
+
+
+static int str_find (lua_State *L) {
+ size_t l1, l2;
+ const char *s = luaL_check_lstr(L, 1, &l1);
+ const char *p = luaL_check_lstr(L, 2, &l2);
+ long init = posrelat(luaL_opt_long(L, 3, 1), l1) - 1;
+ struct Capture cap;
+ luaL_arg_check(L, 0 <= init && (size_t)init <= l1, 3, "out of range");
+ if (lua_gettop(L) > 3 || /* extra argument? */
+ strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */
+ const char *s2 = lmemfind(s+init, l1-init, p, l2);
+ if (s2) {
+ lua_pushnumber(L, s2-s+1);
+ lua_pushnumber(L, s2-s+l2);
+ return 2;
+ }
+ }
+ else {
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ const char *s1=s+init;
+ cap.src_end = s+l1;
+ do {
+ const char *res;
+ cap.level = 0;
+ if ((res=match(L, s1, p, &cap)) != NULL) {
+ lua_pushnumber(L, s1-s+1); /* start */
+ lua_pushnumber(L, res-s); /* end */
+ return push_captures(L, &cap) + 2;
+ }
+ } while (s1++<cap.src_end && !anchor);
+ }
+ lua_pushnil(L); /* not found */
+ return 1;
+}
+
+
+static void add_s (lua_State *L, luaL_Buffer *b, struct Capture *cap) {
+ if (lua_isstring(L, 3)) {
+ const char *news = lua_tostring(L, 3);
+ size_t l = lua_strlen(L, 3);
+ size_t i;
+ for (i=0; i<l; i++) {
+ if (news[i] != ESC)
+ luaL_putchar(b, news[i]);
+ else {
+ i++; /* skip ESC */
+ if (!isdigit((unsigned char)news[i]))
+ luaL_putchar(b, news[i]);
+ else {
+ int level = check_capture(L, news[i], cap);
+ luaL_addlstring(b, cap->capture[level].init, cap->capture[level].len);
+ }
+ }
+ }
+ }
+ else { /* is a function */
+ int n;
+ lua_pushvalue(L, 3);
+ n = push_captures(L, cap);
+ lua_rawcall(L, n, 1);
+ if (lua_isstring(L, -1))
+ luaL_addvalue(b); /* add return to accumulated result */
+ else
+ lua_pop(L, 1); /* function result is not a string: pop it */
+ }
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl;
+ const char *src = luaL_check_lstr(L, 1, &srcl);
+ const char *p = luaL_check_string(L, 2);
+ int max_s = luaL_opt_int(L, 4, srcl+1);
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ int n = 0;
+ struct Capture cap;
+ luaL_Buffer b;
+ luaL_arg_check(L,
+ lua_gettop(L) >= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)),
+ 3, "string or function expected");
+ luaL_buffinit(L, &b);
+ cap.src_end = src+srcl;
+ while (n < max_s) {
+ const char *e;
+ cap.level = 0;
+ e = match(L, src, p, &cap);
+ if (e) {
+ n++;
+ add_s(L, &b, &cap);
+ }
+ if (e && e>src) /* non empty match? */
+ src = e; /* skip it */
+ else if (src < cap.src_end)
+ luaL_putchar(&b, *src++);
+ else break;
+ if (anchor) break;
+ }
+ luaL_addlstring(&b, src, cap.src_end-src);
+ luaL_pushresult(&b);
+ lua_pushnumber(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) {
+ size_t l;
+ const char *s = luaL_check_lstr(L, arg, &l);
+ luaL_putchar(b, '"');
+ while (l--) {
+ switch (*s) {
+ case '"': case '\\': case '\n':
+ luaL_putchar(b, '\\');
+ luaL_putchar(b, *s);
+ break;
+ case '\0': luaL_addlstring(b, "\\000", 4); break;
+ default: luaL_putchar(b, *s);
+ }
+ s++;
+ }
+ luaL_putchar(b, '"');
+}
+
+/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
+#define MAX_ITEM 512
+/* maximum size of each format specification (such as '%-099.99d') */
+#define MAX_FORMAT 20
+
+static int str_format (lua_State *L) {
+ int arg = 1;
+ const char *strfrmt = luaL_check_string(L, arg);
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (*strfrmt) {
+ if (*strfrmt != '%')
+ luaL_putchar(&b, *strfrmt++);
+ else if (*++strfrmt == '%')
+ luaL_putchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ struct Capture cap;
+ char form[MAX_FORMAT]; /* to store the format ('%...') */
+ char buff[MAX_ITEM]; /* to store the formatted item */
+ const char *initf = strfrmt;
+ form[0] = '%';
+ if (isdigit((unsigned char)*initf) && *(initf+1) == '$') {
+ arg = *initf - '0';
+ initf += 2; /* skip the 'n$' */
+ }
+ arg++;
+ cap.src_end = strfrmt+strlen(strfrmt)+1;
+ cap.level = 0;
+ strfrmt = match(L, initf, "[-+ #0]*(%d*)%.?(%d*)", &cap);
+ if (cap.capture[0].len > 2 || cap.capture[1].len > 2 || /* < 100? */
+ strfrmt-initf > MAX_FORMAT-2)
+ lua_error(L, "invalid format (width or precision too long)");
+ strncpy(form+1, initf, strfrmt-initf+1); /* +1 to include conversion */
+ form[strfrmt-initf+2] = 0;
+ switch (*strfrmt++) {
+ case 'c': case 'd': case 'i':
+ sprintf(buff, form, luaL_check_int(L, arg));
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ sprintf(buff, form, (unsigned int)luaL_check_number(L, arg));
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ sprintf(buff, form, luaL_check_number(L, arg));
+ break;
+ case 'q':
+ luaI_addquoted(L, &b, arg);
+ continue; /* skip the "addsize" at the end */
+ case 's': {
+ size_t l;
+ const char *s = luaL_check_lstr(L, arg, &l);
+ if (cap.capture[1].len == 0 && l >= 100) {
+ /* no precision and string is too long to be formatted;
+ keep original string */
+ lua_pushvalue(L, arg);
+ luaL_addvalue(&b);
+ continue; /* skip the "addsize" at the end */
+ }
+ else {
+ sprintf(buff, form, s);
+ break;
+ }
+ }
+ default: /* also treat cases 'pnLlh' */
+ lua_error(L, "invalid option in `format'");
+ }
+ luaL_addlstring(&b, buff, strlen(buff));
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static const struct luaL_reg strlib[] = {
+{"strlen", str_len},
+{"strsub", str_sub},
+{"strlower", str_lower},
+{"strupper", str_upper},
+{"strchar", str_char},
+{"strrep", str_rep},
+{"ascii", str_byte}, /* for compatibility with 3.0 and earlier */
+{"strbyte", str_byte},
+{"format", str_format},
+{"strfind", str_find},
+{"gsub", str_gsub}
+};
+
+
+/*
+** Open string library
+*/
+LUALIB_API void lua_strlibopen (lua_State *L) {
+ luaL_openl(L, strlib);
+}
diff --git a/src/lua/ltable.c b/src/lua/ltable.c
new file mode 100644
index 00000000..1e3eb4f5
--- /dev/null
+++ b/src/lua/ltable.c
@@ -0,0 +1,303 @@
+/*
+** $Id: ltable.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables);
+** uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the `original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** In other words, there are collisions only when two elements have the
+** same main position (i.e. the same hash values for that table size).
+** Because of that, the load factor of these tables can be 100% without
+** performance penalties.
+*/
+
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+#define gcsize(L, n) (sizeof(Hash)+(n)*sizeof(Node))
+
+
+
+#define TagDefault LUA_TTABLE
+
+
+
+/*
+** returns the `main' position of an element in a table (that is, the index
+** of its hash value)
+*/
+Node *luaH_mainposition (const Hash *t, const TObject *key) {
+ unsigned long h;
+ switch (ttype(key)) {
+ case LUA_TNUMBER:
+ h = (unsigned long)(long)nvalue(key);
+ break;
+ case LUA_TSTRING:
+ h = tsvalue(key)->u.s.hash;
+ break;
+ case LUA_TUSERDATA:
+ h = IntPoint(tsvalue(key));
+ break;
+ case LUA_TTABLE:
+ h = IntPoint(hvalue(key));
+ break;
+ case LUA_TFUNCTION:
+ h = IntPoint(clvalue(key));
+ break;
+ default:
+ return NULL; /* invalid key */
+ }
+ LUA_ASSERT(h%(unsigned int)t->size == (h&((unsigned int)t->size-1)),
+ "a&(x-1) == a%x, for x power of 2");
+ return &t->node[h&(t->size-1)];
+}
+
+
+static const TObject *luaH_getany (lua_State *L, const Hash *t,
+ const TObject *key) {
+ Node *n = luaH_mainposition(t, key);
+ if (!n)
+ lua_error(L, "table index is nil");
+ else do {
+ if (luaO_equalObj(key, &n->key))
+ return &n->val;
+ n = n->next;
+ } while (n);
+ return &luaO_nilobject; /* key not found */
+}
+
+
+/* specialized version for numbers */
+const TObject *luaH_getnum (const Hash *t, Number key) {
+ Node *n = &t->node[(unsigned long)(long)key&(t->size-1)];
+ do {
+ if (ttype(&n->key) == LUA_TNUMBER && nvalue(&n->key) == key)
+ return &n->val;
+ n = n->next;
+ } while (n);
+ return &luaO_nilobject; /* key not found */
+}
+
+
+/* specialized version for strings */
+const TObject *luaH_getstr (const Hash *t, TString *key) {
+ Node *n = &t->node[key->u.s.hash&(t->size-1)];
+ do {
+ if (ttype(&n->key) == LUA_TSTRING && tsvalue(&n->key) == key)
+ return &n->val;
+ n = n->next;
+ } while (n);
+ return &luaO_nilobject; /* key not found */
+}
+
+
+const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key) {
+ switch (ttype(key)) {
+ case LUA_TNUMBER: return luaH_getnum(t, nvalue(key));
+ case LUA_TSTRING: return luaH_getstr(t, tsvalue(key));
+ default: return luaH_getany(L, t, key);
+ }
+}
+
+
+Node *luaH_next (lua_State *L, const Hash *t, const TObject *key) {
+ int i;
+ if (ttype(key) == LUA_TNIL)
+ i = 0; /* first iteration */
+ else {
+ const TObject *v = luaH_get(L, t, key);
+ if (v == &luaO_nilobject)
+ lua_error(L, "invalid key for `next'");
+ i = (int)(((const char *)v -
+ (const char *)(&t->node[0].val)) / sizeof(Node)) + 1;
+ }
+ for (; i<t->size; i++) {
+ Node *n = node(t, i);
+ if (ttype(val(n)) != LUA_TNIL)
+ return n;
+ }
+ return NULL; /* no more elements */
+}
+
+
+/*
+** try to remove a key without value from a table. To avoid problems with
+** hash, change `key' for a number with the same hash.
+*/
+void luaH_remove (Hash *t, TObject *key) {
+ if (ttype(key) == LUA_TNUMBER ||
+ (ttype(key) == LUA_TSTRING && tsvalue(key)->len <= 30))
+ return; /* do not remove numbers nor small strings */
+ else {
+ /* try to find a number `n' with the same hash as `key' */
+ Node *mp = luaH_mainposition(t, key);
+ int n = mp - &t->node[0];
+ /* make sure `n' is not in `t' */
+ while (luaH_getnum(t, n) != &luaO_nilobject) {
+ if (n >= MAX_INT - t->size)
+ return; /* give up; (to avoid overflow) */
+ n += t->size;
+ }
+ ttype(key) = LUA_TNUMBER;
+ nvalue(key) = n;
+ LUA_ASSERT(luaH_mainposition(t, key) == mp, "cannot change hash");
+ }
+}
+
+
+static void setnodevector (lua_State *L, Hash *t, lint32 size) {
+ int i;
+ if (size > MAX_INT)
+ lua_error(L, "table overflow");
+ t->node = luaM_newvector(L, size, Node);
+ for (i=0; i<(int)size; i++) {
+ ttype(&t->node[i].key) = ttype(&t->node[i].val) = LUA_TNIL;
+ t->node[i].next = NULL;
+ }
+ L->nblocks += gcsize(L, size) - gcsize(L, t->size);
+ t->size = size;
+ t->firstfree = &t->node[size-1]; /* first free position to be used */
+}
+
+
+Hash *luaH_new (lua_State *L, int size) {
+ Hash *t = luaM_new(L, Hash);
+ t->htag = TagDefault;
+ t->next = L->roottable;
+ L->roottable = t;
+ t->mark = t;
+ t->size = 0;
+ L->nblocks += gcsize(L, 0);
+ t->node = NULL;
+ setnodevector(L, t, luaO_power2(size));
+ return t;
+}
+
+
+void luaH_free (lua_State *L, Hash *t) {
+ L->nblocks -= gcsize(L, t->size);
+ luaM_free(L, t->node);
+ luaM_free(L, t);
+}
+
+
+static int numuse (const Hash *t) {
+ Node *v = t->node;
+ int size = t->size;
+ int realuse = 0;
+ int i;
+ for (i=0; i<size; i++) {
+ if (ttype(&v[i].val) != LUA_TNIL)
+ realuse++;
+ }
+ return realuse;
+}
+
+
+static void rehash (lua_State *L, Hash *t) {
+ int oldsize = t->size;
+ Node *nold = t->node;
+ int nelems = numuse(t);
+ int i;
+ LUA_ASSERT(nelems<=oldsize, "wrong count");
+ if (nelems >= oldsize-oldsize/4) /* using more than 3/4? */
+ setnodevector(L, t, (lint32)oldsize*2);
+ else if (nelems <= oldsize/4 && /* less than 1/4? */
+ oldsize > MINPOWER2)
+ setnodevector(L, t, oldsize/2);
+ else
+ setnodevector(L, t, oldsize);
+ for (i=0; i<oldsize; i++) {
+ Node *old = nold+i;
+ if (ttype(&old->val) != LUA_TNIL)
+ *luaH_set(L, t, &old->key) = old->val;
+ }
+ luaM_free(L, nold); /* free old array */
+}
+
+
+/*
+** inserts a key into a hash table; first, check whether key is
+** already present; if not, check whether key's main position is free;
+** if not, check whether colliding node is in its main position or not;
+** if it is not, move colliding node to an empty place and put new key
+** in its main position; otherwise (colliding node is in its main position),
+** new key goes to an empty position.
+*/
+TObject *luaH_set (lua_State *L, Hash *t, const TObject *key) {
+ Node *mp = luaH_mainposition(t, key);
+ Node *n = mp;
+ if (!mp)
+ lua_error(L, "table index is nil");
+ do { /* check whether `key' is somewhere in the chain */
+ if (luaO_equalObj(key, &n->key))
+ return &n->val; /* that's all */
+ else n = n->next;
+ } while (n);
+ /* `key' not found; must insert it */
+ if (ttype(&mp->key) != LUA_TNIL) { /* main position is not free? */
+ Node *othern; /* main position of colliding node */
+ n = t->firstfree; /* get a free place */
+ /* is colliding node out of its main position? (can only happens if
+ its position is after "firstfree") */
+ if (mp > n && (othern=luaH_mainposition(t, &mp->key)) != mp) {
+ /* yes; move colliding node into free position */
+ while (othern->next != mp) othern = othern->next; /* find previous */
+ othern->next = n; /* redo the chain with `n' in place of `mp' */
+ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ mp->next = NULL; /* now `mp' is free */
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ n->next = mp->next; /* chain new position */
+ mp->next = n;
+ mp = n;
+ }
+ }
+ mp->key = *key;
+ for (;;) { /* correct `firstfree' */
+ if (ttype(&t->firstfree->key) == LUA_TNIL)
+ return &mp->val; /* OK; table still has a free place */
+ else if (t->firstfree == t->node) break; /* cannot decrement from here */
+ else (t->firstfree)--;
+ }
+ rehash(L, t); /* no more free places */
+ return luaH_set(L, t, key); /* `rehash' invalidates this insertion */
+}
+
+
+TObject *luaH_setint (lua_State *L, Hash *t, int key) {
+ TObject index;
+ ttype(&index) = LUA_TNUMBER;
+ nvalue(&index) = key;
+ return luaH_set(L, t, &index);
+}
+
+
+void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val) {
+ TObject *value, index;
+ ttype(&index) = LUA_TSTRING;
+ tsvalue(&index) = key;
+ value = luaH_set(L, t, &index);
+ ttype(value) = LUA_TNUMBER;
+ nvalue(value) = val;
+}
+
+
+const TObject *luaH_getglobal (lua_State *L, const char *name) {
+ return luaH_getstr(L->gt, luaS_new(L, name));
+}
+
diff --git a/src/lua/ltable.h b/src/lua/ltable.h
new file mode 100644
index 00000000..3bc2a5df
--- /dev/null
+++ b/src/lua/ltable.h
@@ -0,0 +1,34 @@
+/*
+** $Id: ltable.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define node(t,i) (&(t)->node[i])
+#define key(n) (&(n)->key)
+#define val(n) (&(n)->val)
+
+Hash *luaH_new (lua_State *L, int nhash);
+void luaH_free (lua_State *L, Hash *t);
+const TObject *luaH_get (lua_State *L, const Hash *t, const TObject *key);
+const TObject *luaH_getnum (const Hash *t, Number key);
+const TObject *luaH_getstr (const Hash *t, TString *key);
+void luaH_remove (Hash *t, TObject *key);
+TObject *luaH_set (lua_State *L, Hash *t, const TObject *key);
+Node * luaH_next (lua_State *L, const Hash *t, const TObject *r);
+TObject *luaH_setint (lua_State *L, Hash *t, int key);
+void luaH_setstrnum (lua_State *L, Hash *t, TString *key, Number val);
+unsigned long luaH_hash (lua_State *L, const TObject *key);
+const TObject *luaH_getglobal (lua_State *L, const char *name);
+
+/* exported only for debugging */
+Node *luaH_mainposition (const Hash *t, const TObject *key);
+
+
+#endif
diff --git a/src/lua/ltests.c b/src/lua/ltests.c
new file mode 100644
index 00000000..06e08f5a
--- /dev/null
+++ b/src/lua/ltests.c
@@ -0,0 +1,543 @@
+/*
+** $Id: ltests.c,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Internal Module for Debugging of the Lua Implementation
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lauxlib.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "luadebug.h"
+#include "lualib.h"
+
+
+void luaB_opentests (lua_State *L);
+
+
+/*
+** The whole module only makes sense with LUA_DEBUG on
+*/
+#ifdef LUA_DEBUG
+
+
+
+static void setnameval (lua_State *L, const char *name, int val) {
+ lua_pushstring(L, name);
+ lua_pushnumber(L, val);
+ lua_settable(L, -3);
+}
+
+
+/*
+** {======================================================
+** Disassembler
+** =======================================================
+*/
+
+
+static const char *const instrname[NUM_OPCODES] = {
+ "END", "RETURN", "CALL", "TAILCALL", "PUSHNIL", "POP", "PUSHINT",
+ "PUSHSTRING", "PUSHNUM", "PUSHNEGNUM", "PUSHUPVALUE", "GETLOCAL",
+ "GETGLOBAL", "GETTABLE", "GETDOTTED", "GETINDEXED", "PUSHSELF",
+ "CREATETABLE", "SETLOCAL", "SETGLOBAL", "SETTABLE", "SETLIST", "SETMAP",
+ "ADD", "ADDI", "SUB", "MULT", "DIV", "POW", "CONCAT", "MINUS", "NOT",
+ "JMPNE", "JMPEQ", "JMPLT", "JMPLE", "JMPGT", "JMPGE", "JMPT", "JMPF",
+ "JMPONT", "JMPONF", "JMP", "PUSHNILJMP", "FORPREP", "FORLOOP", "LFORPREP",
+ "LFORLOOP", "CLOSURE"
+};
+
+
+static int pushop (lua_State *L, Proto *p, int pc) {
+ char buff[100];
+ Instruction i = p->code[pc];
+ OpCode o = GET_OPCODE(i);
+ const char *name = instrname[o];
+ sprintf(buff, "%5d - ", luaG_getline(p->lineinfo, pc, 1, NULL));
+ switch ((enum Mode)luaK_opproperties[o].mode) {
+ case iO:
+ sprintf(buff+8, "%-12s", name);
+ break;
+ case iU:
+ sprintf(buff+8, "%-12s%4u", name, GETARG_U(i));
+ break;
+ case iS:
+ sprintf(buff+8, "%-12s%4d", name, GETARG_S(i));
+ break;
+ case iAB:
+ sprintf(buff+8, "%-12s%4d %4d", name, GETARG_A(i), GETARG_B(i));
+ break;
+ }
+ lua_pushstring(L, buff);
+ return (o != OP_END);
+}
+
+
+static int listcode (lua_State *L) {
+ int pc;
+ Proto *p;
+ int res;
+ luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = clvalue(luaA_index(L, 1))->f.l;
+ lua_newtable(L);
+ setnameval(L, "maxstack", p->maxstacksize);
+ setnameval(L, "numparams", p->numparams);
+ pc = 0;
+ do {
+ lua_pushnumber(L, pc+1);
+ res = pushop(L, p, pc++);
+ lua_settable(L, -3);
+ } while (res);
+ return 1;
+}
+
+
+static int liststrings (lua_State *L) {
+ Proto *p;
+ int i;
+ luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = clvalue(luaA_index(L, 1))->f.l;
+ lua_newtable(L);
+ for (i=0; i<p->nkstr; i++) {
+ lua_pushnumber(L, i+1);
+ lua_pushstring(L, p->kstr[i]->str);
+ lua_settable(L, -3);
+ }
+ return 1;
+}
+
+
+static int listlocals (lua_State *L) {
+ Proto *p;
+ int pc = luaL_check_int(L, 2) - 1;
+ int i = 0;
+ const char *name;
+ luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+ 1, "Lua function expected");
+ p = clvalue(luaA_index(L, 1))->f.l;
+ while ((name = luaF_getlocalname(p, ++i, pc)) != NULL)
+ lua_pushstring(L, name);
+ return i-1;
+}
+
+/* }====================================================== */
+
+
+
+static int get_limits (lua_State *L) {
+ lua_newtable(L);
+ setnameval(L, "BITS_INT", BITS_INT);
+ setnameval(L, "LFPF", LFIELDS_PER_FLUSH);
+ setnameval(L, "MAXARG_A", MAXARG_A);
+ setnameval(L, "MAXARG_B", MAXARG_B);
+ setnameval(L, "MAXARG_S", MAXARG_S);
+ setnameval(L, "MAXARG_U", MAXARG_U);
+ setnameval(L, "MAXLOCALS", MAXLOCALS);
+ setnameval(L, "MAXPARAMS", MAXPARAMS);
+ setnameval(L, "MAXSTACK", MAXSTACK);
+ setnameval(L, "MAXUPVALUES", MAXUPVALUES);
+ setnameval(L, "MAXVARSLH", MAXVARSLH);
+ setnameval(L, "RFPF", RFIELDS_PER_FLUSH);
+ setnameval(L, "SIZE_A", SIZE_A);
+ setnameval(L, "SIZE_B", SIZE_B);
+ setnameval(L, "SIZE_OP", SIZE_OP);
+ setnameval(L, "SIZE_U", SIZE_U);
+ return 1;
+}
+
+
+static int mem_query (lua_State *L) {
+ if (lua_isnull(L, 1)) {
+ lua_pushnumber(L, memdebug_total);
+ lua_pushnumber(L, memdebug_numblocks);
+ lua_pushnumber(L, memdebug_maxmem);
+ return 3;
+ }
+ else {
+ memdebug_memlimit = luaL_check_int(L, 1);
+ return 0;
+ }
+}
+
+
+static int hash_query (lua_State *L) {
+ if (lua_isnull(L, 2)) {
+ luaL_arg_check(L, lua_tag(L, 1) == LUA_TSTRING, 1, "string expected");
+ lua_pushnumber(L, tsvalue(luaA_index(L, 1))->u.s.hash);
+ }
+ else {
+ Hash *t;
+ luaL_checktype(L, 2, LUA_TTABLE);
+ t = hvalue(luaA_index(L, 2));
+ lua_pushnumber(L, luaH_mainposition(t, luaA_index(L, 1)) - t->node);
+ }
+ return 1;
+}
+
+
+static int table_query (lua_State *L) {
+ const Hash *t;
+ int i = luaL_opt_int(L, 2, -1);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ t = hvalue(luaA_index(L, 1));
+ if (i == -1) {
+ lua_pushnumber(L, t->size);
+ lua_pushnumber(L, t->firstfree - t->node);
+ return 2;
+ }
+ else if (i < t->size) {
+ luaA_pushobject(L, &t->node[i].key);
+ luaA_pushobject(L, &t->node[i].val);
+ if (t->node[i].next) {
+ lua_pushnumber(L, t->node[i].next - t->node);
+ return 3;
+ }
+ else
+ return 2;
+ }
+ return 0;
+}
+
+
+static int string_query (lua_State *L) {
+ stringtable *tb = (*luaL_check_string(L, 1) == 's') ? &L->strt : &L->udt;
+ int s = luaL_opt_int(L, 2, 0) - 1;
+ if (s==-1) {
+ lua_pushnumber(L ,tb->nuse);
+ lua_pushnumber(L ,tb->size);
+ return 2;
+ }
+ else if (s < tb->size) {
+ TString *ts;
+ int n = 0;
+ for (ts = tb->hash[s]; ts; ts = ts->nexthash) {
+ ttype(L->top) = LUA_TSTRING;
+ tsvalue(L->top) = ts;
+ incr_top;
+ n++;
+ }
+ return n;
+ }
+ return 0;
+}
+
+
+static int tref (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushvalue(L, 1);
+ lua_pushnumber(L, lua_ref(L, luaL_opt_int(L, 2, 1)));
+ return 1;
+}
+
+static int getref (lua_State *L) {
+ if (lua_getref(L, luaL_check_int(L, 1)))
+ return 1;
+ else
+ return 0;
+}
+
+static int unref (lua_State *L) {
+ lua_unref(L, luaL_check_int(L, 1));
+ return 0;
+}
+
+static int newuserdata (lua_State *L) {
+ if (lua_isnumber(L, 2))
+ lua_pushusertag(L, (void *)luaL_check_int(L, 1), luaL_check_int(L, 2));
+ else
+ lua_newuserdata(L, luaL_check_int(L, 1));
+ return 1;
+}
+
+static int udataval (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ lua_pushnumber(L, (int)lua_touserdata(L, 1));
+ return 1;
+}
+
+static int newstate (lua_State *L) {
+ lua_State *L1 = lua_open(luaL_check_int(L, 1));
+ if (L1)
+ lua_pushuserdata(L, L1);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int loadlib (lua_State *L) {
+ lua_State *L1 = (lua_State *)lua_touserdata(L, 1);
+ switch (*luaL_check_string(L, 2)) {
+ case 'm': lua_mathlibopen(L1); break;
+ case 's': lua_strlibopen(L1); break;
+ case 'i': lua_iolibopen(L1); break;
+ case 'd': lua_dblibopen(L1); break;
+ case 'b': lua_baselibopen(L1); break;
+ default: luaL_argerror(L, 2, "invalid option");
+ }
+ return 0;
+}
+
+static int closestate (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ lua_close((lua_State *)lua_touserdata(L, 1));
+ return 0;
+}
+
+static int doremote (lua_State *L) {
+ lua_State *L1;
+ const char *code = luaL_check_string(L, 2);
+ int status;
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ L1 = (lua_State *)lua_touserdata(L, 1);
+ status = lua_dostring(L1, code);
+ if (status != 0) {
+ lua_pushnil(L);
+ lua_pushnumber(L, status);
+ return 2;
+ }
+ else {
+ int i = 0;
+ while (!lua_isnull(L1, ++i))
+ lua_pushstring(L, lua_tostring(L1, i));
+ return i-1;
+ }
+}
+
+static int settagmethod (lua_State *L) {
+ int tag = luaL_check_int(L, 1);
+ const char *event = luaL_check_string(L, 2);
+ luaL_checkany(L, 3);
+ lua_gettagmethod(L, tag, event);
+ lua_pushvalue(L, 3);
+ lua_settagmethod(L, tag, event);
+ return 1;
+}
+
+static int pushbool (lua_State *L, int b) {
+ if (b) lua_pushnumber(L, 1);
+ else lua_pushnil(L);
+ return 1;
+}
+
+static int equal (lua_State *L) {
+ return pushbool(L, lua_equal(L, 1, 2));
+}
+
+
+
+/*
+** {======================================================
+** function to test the API with C. It interprets a kind of "assembler"
+** language with calls to the API, so the test can be driven by Lua code
+** =======================================================
+*/
+
+static const char *const delimits = " \t\n,;";
+
+static void skip (const char **pc) {
+ while (**pc != '\0' && strchr(delimits, **pc)) (*pc)++;
+}
+
+static int getnum (lua_State *L, const char **pc) {
+ int res = 0;
+ int sig = 1;
+ skip(pc);
+ if (**pc == '.') {
+ res = (int)lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ (*pc)++;
+ return res;
+ }
+ else if (**pc == '-') {
+ sig = -1;
+ (*pc)++;
+ }
+ while (isdigit(**pc)) res = res*10 + (*(*pc)++) - '0';
+ return sig*res;
+}
+
+static const char *getname (char *buff, const char **pc) {
+ int i = 0;
+ skip(pc);
+ while (**pc != '\0' && !strchr(delimits, **pc))
+ buff[i++] = *(*pc)++;
+ buff[i] = '\0';
+ return buff;
+}
+
+
+#define EQ(s1) (strcmp(s1, inst) == 0)
+
+#define getnum ((getnum)(L, &pc))
+#define getname ((getname)(buff, &pc))
+
+
+static int testC (lua_State *L) {
+ char buff[30];
+ const char *pc = luaL_check_string(L, 1);
+ for (;;) {
+ const char *inst = getname;
+ if EQ("") return 0;
+ else if EQ("isnumber") {
+ lua_pushnumber(L, lua_isnumber(L, getnum));
+ }
+ else if EQ("isstring") {
+ lua_pushnumber(L, lua_isstring(L, getnum));
+ }
+ else if EQ("istable") {
+ lua_pushnumber(L, lua_istable(L, getnum));
+ }
+ else if EQ("iscfunction") {
+ lua_pushnumber(L, lua_iscfunction(L, getnum));
+ }
+ else if EQ("isfunction") {
+ lua_pushnumber(L, lua_isfunction(L, getnum));
+ }
+ else if EQ("isuserdata") {
+ lua_pushnumber(L, lua_isuserdata(L, getnum));
+ }
+ else if EQ("isnil") {
+ lua_pushnumber(L, lua_isnil(L, getnum));
+ }
+ else if EQ("isnull") {
+ lua_pushnumber(L, lua_isnull(L, getnum));
+ }
+ else if EQ("tonumber") {
+ lua_pushnumber(L, lua_tonumber(L, getnum));
+ }
+ else if EQ("tostring") {
+ lua_pushstring(L, lua_tostring(L, getnum));
+ }
+ else if EQ("tonumber") {
+ lua_pushnumber(L, lua_tonumber(L, getnum));
+ }
+ else if EQ("strlen") {
+ lua_pushnumber(L, lua_strlen(L, getnum));
+ }
+ else if EQ("tocfunction") {
+ lua_pushcfunction(L, lua_tocfunction(L, getnum));
+ }
+ else if EQ("return") {
+ return getnum;
+ }
+ else if EQ("gettop") {
+ lua_pushnumber(L, lua_gettop(L));
+ }
+ else if EQ("settop") {
+ lua_settop(L, getnum);
+ }
+ else if EQ("pop") {
+ lua_pop(L, getnum);
+ }
+ else if EQ("pushnum") {
+ lua_pushnumber(L, getnum);
+ }
+ else if EQ("pushvalue") {
+ lua_pushvalue(L, getnum);
+ }
+ else if EQ("remove") {
+ lua_remove(L, getnum);
+ }
+ else if EQ("insert") {
+ lua_insert(L, getnum);
+ }
+ else if EQ("gettable") {
+ lua_gettable(L, getnum);
+ }
+ else if EQ("settable") {
+ lua_settable(L, getnum);
+ }
+ else if EQ("next") {
+ lua_next(L, -2);
+ }
+ else if EQ("concat") {
+ lua_concat(L, getnum);
+ }
+ else if EQ("rawcall") {
+ int narg = getnum;
+ int nres = getnum;
+ lua_rawcall(L, narg, nres);
+ }
+ else if EQ("call") {
+ int narg = getnum;
+ int nres = getnum;
+ lua_call(L, narg, nres);
+ }
+ else if EQ("dostring") {
+ lua_dostring(L, luaL_check_string(L, getnum));
+ }
+ else if EQ("settagmethod") {
+ int tag = getnum;
+ const char *event = getname;
+ lua_settagmethod(L, tag, event);
+ }
+ else if EQ("gettagmethod") {
+ int tag = getnum;
+ const char *event = getname;
+ lua_gettagmethod(L, tag, event);
+ }
+ else if EQ("type") {
+ lua_pushstring(L, lua_typename(L, lua_type(L, getnum)));
+ }
+ else luaL_verror(L, "unknown instruction %.30s", buff);
+ }
+ return 0;
+}
+
+/* }====================================================== */
+
+
+
+static const struct luaL_reg tests_funcs[] = {
+ {"hash", hash_query},
+ {"limits", get_limits},
+ {"listcode", listcode},
+ {"liststrings", liststrings},
+ {"listlocals", listlocals},
+ {"loadlib", loadlib},
+ {"querystr", string_query},
+ {"querytab", table_query},
+ {"testC", testC},
+ {"ref", tref},
+ {"getref", getref},
+ {"unref", unref},
+ {"newuserdata", newuserdata},
+ {"udataval", udataval},
+ {"newstate", newstate},
+ {"closestate", closestate},
+ {"doremote", doremote},
+ {"settagmethod", settagmethod},
+ {"equal", equal},
+ {"totalmem", mem_query}
+};
+
+
+void luaB_opentests (lua_State *L) {
+ lua_newtable(L);
+ lua_getglobals(L);
+ lua_pushvalue(L, -2);
+ lua_setglobals(L);
+ luaL_openl(L, tests_funcs); /* open functions inside new table */
+ lua_setglobals(L); /* restore old table of globals */
+ lua_setglobal(L, "T"); /* set new table as global T */
+}
+
+#endif
diff --git a/src/lua/ltm.c b/src/lua/ltm.c
new file mode 100644
index 00000000..3f69a6ca
--- /dev/null
+++ b/src/lua/ltm.c
@@ -0,0 +1,163 @@
+/*
+** $Id: ltm.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "ltm.h"
+
+
+const char *const luaT_eventname[] = { /* ORDER TM */
+ "gettable", "settable", "index", "getglobal", "setglobal", "add", "sub",
+ "mul", "div", "pow", "unm", "lt", "concat", "gc", "function",
+ "le", "gt", "ge", /* deprecated options!! */
+ NULL
+};
+
+
+static int findevent (const char *name) {
+ int i;
+ for (i=0; luaT_eventname[i]; i++)
+ if (strcmp(luaT_eventname[i], name) == 0)
+ return i;
+ return -1; /* name not found */
+}
+
+
+static int luaI_checkevent (lua_State *L, const char *name, int t) {
+ int e = findevent(name);
+ if (e >= TM_N)
+ luaO_verror(L, "event `%.50s' is deprecated", name);
+ if (e == TM_GC && t == LUA_TTABLE)
+ luaO_verror(L, "event `gc' for tables is deprecated");
+ if (e < 0)
+ luaO_verror(L, "`%.50s' is not a valid event name", name);
+ return e;
+}
+
+
+
+/* events in LUA_TNIL are all allowed, since this is used as a
+* 'placeholder' for "default" fallbacks
+*/
+/* ORDER LUA_T, ORDER TM */
+static const char luaT_validevents[NUM_TAGS][TM_N] = {
+ {1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TUSERDATA */
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* LUA_TNIL */
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, /* LUA_TNUMBER */
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, /* LUA_TSTRING */
+ {0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, /* LUA_TTABLE */
+ {1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0} /* LUA_TFUNCTION */
+};
+
+int luaT_validevent (int t, int e) { /* ORDER LUA_T */
+ return (t >= NUM_TAGS) ? 1 : luaT_validevents[t][e];
+}
+
+
+static void init_entry (lua_State *L, int tag) {
+ int i;
+ for (i=0; i<TM_N; i++)
+ luaT_gettm(L, tag, i) = NULL;
+ L->TMtable[tag].collected = NULL;
+}
+
+
+void luaT_init (lua_State *L) {
+ int t;
+ luaM_growvector(L, L->TMtable, 0, NUM_TAGS, struct TM, "", MAX_INT);
+ L->nblocks += NUM_TAGS*sizeof(struct TM);
+ L->last_tag = NUM_TAGS-1;
+ for (t=0; t<=L->last_tag; t++)
+ init_entry(L, t);
+}
+
+
+LUA_API int lua_newtag (lua_State *L) {
+ luaM_growvector(L, L->TMtable, L->last_tag, 1, struct TM,
+ "tag table overflow", MAX_INT);
+ L->nblocks += sizeof(struct TM);
+ L->last_tag++;
+ init_entry(L, L->last_tag);
+ return L->last_tag;
+}
+
+
+static void checktag (lua_State *L, int tag) {
+ if (!(0 <= tag && tag <= L->last_tag))
+ luaO_verror(L, "%d is not a valid tag", tag);
+}
+
+void luaT_realtag (lua_State *L, int tag) {
+ if (!validtag(tag))
+ luaO_verror(L, "tag %d was not created by `newtag'", tag);
+}
+
+
+LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom) {
+ int e;
+ checktag(L, tagto);
+ checktag(L, tagfrom);
+ for (e=0; e<TM_N; e++) {
+ if (luaT_validevent(tagto, e))
+ luaT_gettm(L, tagto, e) = luaT_gettm(L, tagfrom, e);
+ }
+ return tagto;
+}
+
+
+int luaT_tag (const TObject *o) {
+ int t = ttype(o);
+ switch (t) {
+ case LUA_TUSERDATA: return tsvalue(o)->u.d.tag;
+ case LUA_TTABLE: return hvalue(o)->htag;
+ default: return t;
+ }
+}
+
+
+LUA_API void lua_gettagmethod (lua_State *L, int t, const char *event) {
+ int e;
+ e = luaI_checkevent(L, event, t);
+ checktag(L, t);
+ if (luaT_validevent(t, e) && luaT_gettm(L, t, e)) {
+ clvalue(L->top) = luaT_gettm(L, t, e);
+ ttype(L->top) = LUA_TFUNCTION;
+ }
+ else
+ ttype(L->top) = LUA_TNIL;
+ incr_top;
+}
+
+
+LUA_API void lua_settagmethod (lua_State *L, int t, const char *event) {
+ int e = luaI_checkevent(L, event, t);
+ checktag(L, t);
+ if (!luaT_validevent(t, e))
+ luaO_verror(L, "cannot change `%.20s' tag method for type `%.20s'%.20s",
+ luaT_eventname[e], luaO_typenames[t],
+ (t == LUA_TTABLE || t == LUA_TUSERDATA) ?
+ " with default tag" : "");
+ switch (ttype(L->top - 1)) {
+ case LUA_TNIL:
+ luaT_gettm(L, t, e) = NULL;
+ break;
+ case LUA_TFUNCTION:
+ luaT_gettm(L, t, e) = clvalue(L->top - 1);
+ break;
+ default:
+ lua_error(L, "tag method must be a function (or nil)");
+ }
+ L->top--;
+}
+
diff --git a/src/lua/ltm.h b/src/lua/ltm.h
new file mode 100644
index 00000000..f6be13ed
--- /dev/null
+++ b/src/lua/ltm.h
@@ -0,0 +1,59 @@
+/*
+** $Id: ltm.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_GETTABLE = 0,
+ TM_SETTABLE,
+ TM_INDEX,
+ TM_GETGLOBAL,
+ TM_SETGLOBAL,
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_POW,
+ TM_UNM,
+ TM_LT,
+ TM_CONCAT,
+ TM_GC,
+ TM_FUNCTION,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+struct TM {
+ Closure *method[TM_N];
+ TString *collected; /* list of garbage-collected udata with this tag */
+};
+
+
+#define luaT_gettm(L,tag,event) (L->TMtable[tag].method[event])
+#define luaT_gettmbyObj(L,o,e) (luaT_gettm((L),luaT_tag(o),(e)))
+
+
+#define validtag(t) (NUM_TAGS <= (t) && (t) <= L->last_tag)
+
+extern const char *const luaT_eventname[];
+
+
+void luaT_init (lua_State *L);
+void luaT_realtag (lua_State *L, int tag);
+int luaT_tag (const TObject *o);
+int luaT_validevent (int t, int e); /* used by compatibility module */
+
+
+#endif
diff --git a/src/lua/lua.h b/src/lua/lua.h
new file mode 100644
index 00000000..87d64e71
--- /dev/null
+++ b/src/lua/lua.h
@@ -0,0 +1,248 @@
+/*
+** $Id: lua.h,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Lua - An Extensible Extension Language
+** TeCGraf: Grupo de Tecnologia em Computacao Grafica, PUC-Rio, Brazil
+** e-mail: lua@tecgraf.puc-rio.br
+** www: http://www.tecgraf.puc-rio.br/lua/
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+
+/* definition of `size_t' */
+#include <stddef.h>
+
+
+/* mark for all API functions */
+#ifndef LUA_API
+#define LUA_API extern
+#endif
+
+
+#define LUA_VERSION "Lua 4.0"
+#define LUA_COPYRIGHT "Copyright (C) 1994-2000 TeCGraf, PUC-Rio"
+#define LUA_AUTHORS "W. Celes, R. Ierusalimschy & L. H. de Figueiredo"
+
+
+/* name of global variable with error handler */
+#define LUA_ERRORMESSAGE "_ERRORMESSAGE"
+
+
+/* pre-defined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+#define LUA_REFREGISTRY 0
+
+/* pre-defined tags */
+#define LUA_ANYTAG (-1)
+#define LUA_NOTAG (-2)
+
+
+/* option for multiple returns in lua_call */
+#define LUA_MULTRET (-1)
+
+
+/* minimum stack available for a C function */
+#define LUA_MINSTACK 20
+
+
+/* error codes for lua_do* */
+#define LUA_ERRRUN 1
+#define LUA_ERRFILE 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+/*
+** types returned by `lua_type'
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TUSERDATA 0
+#define LUA_TNIL 1
+#define LUA_TNUMBER 2
+#define LUA_TSTRING 3
+#define LUA_TTABLE 4
+#define LUA_TFUNCTION 5
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *lua_open (int stacksize);
+LUA_API void lua_close (lua_State *L);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int lua_gettop (lua_State *L);
+LUA_API void lua_settop (lua_State *L, int index);
+LUA_API void lua_pushvalue (lua_State *L, int index);
+LUA_API void lua_remove (lua_State *L, int index);
+LUA_API void lua_insert (lua_State *L, int index);
+LUA_API int lua_stackspace (lua_State *L);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int lua_type (lua_State *L, int index);
+LUA_API const char *lua_typename (lua_State *L, int t);
+LUA_API int lua_isnumber (lua_State *L, int index);
+LUA_API int lua_isstring (lua_State *L, int index);
+LUA_API int lua_iscfunction (lua_State *L, int index);
+LUA_API int lua_tag (lua_State *L, int index);
+
+LUA_API int lua_equal (lua_State *L, int index1, int index2);
+LUA_API int lua_lessthan (lua_State *L, int index1, int index2);
+
+LUA_API long lua_tonumber (lua_State *L, int index);
+LUA_API const char *lua_tostring (lua_State *L, int index);
+LUA_API size_t lua_strlen (lua_State *L, int index);
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int index);
+LUA_API void *lua_touserdata (lua_State *L, int index);
+LUA_API const void *lua_topointer (lua_State *L, int index);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void lua_pushnil (lua_State *L);
+LUA_API void lua_pushnumber (lua_State *L, long n);
+LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len);
+LUA_API void lua_pushstring (lua_State *L, const char *s);
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
+LUA_API void lua_pushusertag (lua_State *L, void *u, int tag);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void lua_getglobal (lua_State *L, const char *name);
+LUA_API void lua_gettable (lua_State *L, int index);
+LUA_API void lua_rawget (lua_State *L, int index);
+LUA_API void lua_rawgeti (lua_State *L, int index, int n);
+LUA_API void lua_getglobals (lua_State *L);
+LUA_API void lua_gettagmethod (lua_State *L, int tag, const char *event);
+LUA_API int lua_getref (lua_State *L, int ref);
+LUA_API void lua_newtable (lua_State *L);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void lua_setglobal (lua_State *L, const char *name);
+LUA_API void lua_settable (lua_State *L, int index);
+LUA_API void lua_rawset (lua_State *L, int index);
+LUA_API void lua_rawseti (lua_State *L, int index, int n);
+LUA_API void lua_setglobals (lua_State *L);
+LUA_API void lua_settagmethod (lua_State *L, int tag, const char *event);
+LUA_API int lua_ref (lua_State *L, int lock);
+
+
+/*
+** "do" functions (run Lua code)
+*/
+LUA_API int lua_call (lua_State *L, int nargs, int nresults);
+LUA_API void lua_rawcall (lua_State *L, int nargs, int nresults);
+LUA_API int lua_dofile (lua_State *L, const char *filename);
+LUA_API int lua_dostring (lua_State *L, const char *str);
+LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name);
+
+/*
+** Garbage-collection functions
+*/
+LUA_API int lua_getgcthreshold (lua_State *L);
+LUA_API int lua_getgccount (lua_State *L);
+LUA_API void lua_setgcthreshold (lua_State *L, int newthreshold);
+
+/*
+** miscellaneous functions
+*/
+LUA_API int lua_newtag (lua_State *L);
+LUA_API int lua_copytagmethods (lua_State *L, int tagto, int tagfrom);
+LUA_API void lua_settag (lua_State *L, int tag);
+
+LUA_API void lua_error (lua_State *L, const char *s);
+
+LUA_API void lua_unref (lua_State *L, int ref);
+
+LUA_API int lua_next (lua_State *L, int index);
+LUA_API int lua_getn (lua_State *L, int index);
+
+LUA_API void lua_concat (lua_State *L, int n);
+
+LUA_API void *lua_newuserdata (lua_State *L, size_t size);
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
+#define lua_pushuserdata(L,u) lua_pushusertag(L, u, 0)
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, f, 0)
+#define lua_clonetag(L,t) lua_copytagmethods(L, lua_newtag(L), (t))
+
+#define lua_isfunction(L,n) (lua_type(L,n) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L,n) == LUA_TTABLE)
+#define lua_isuserdata(L,n) (lua_type(L,n) == LUA_TUSERDATA)
+#define lua_isnil(L,n) (lua_type(L,n) == LUA_TNIL)
+#define lua_isnull(L,n) (lua_type(L,n) == LUA_TNONE)
+
+#define lua_getregistry(L) lua_getref(L, LUA_REFREGISTRY)
+
+#endif
+
+
+
+/******************************************************************************
+* Copyright (C) 1994-2000 TeCGraf, PUC-Rio. All rights reserved.
+*
+* Permission is hereby granted, without written agreement and without license
+* or royalty fees, to use, copy, modify, and distribute this software and its
+* documentation for any purpose, including commercial applications, subject to
+* the following conditions:
+*
+* - The above copyright notice and this permission notice shall appear in all
+* copies or substantial portions of this software.
+*
+* - The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software in a
+* product, an acknowledgment in the product documentation would be greatly
+* appreciated (but it is not required).
+*
+* - Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+*
+* The authors specifically disclaim any warranties, including, but not limited
+* to, the implied warranties of merchantability and fitness for a particular
+* purpose. The software provided hereunder is on an "as is" basis, and the
+* authors have no obligation to provide maintenance, support, updates,
+* enhancements, or modifications. In no event shall TeCGraf, PUC-Rio, or the
+* authors be held liable to any party for direct, indirect, special,
+* incidental, or consequential damages arising out of the use of this software
+* and its documentation.
+*
+* The Lua language and this implementation have been entirely designed and
+* written by Waldemar Celes Filho, Roberto Ierusalimschy and
+* Luiz Henrique de Figueiredo at TeCGraf, PUC-Rio.
+*
+* This implementation contains no third-party code.
+******************************************************************************/
+
diff --git a/src/lua/lua2c.lua b/src/lua/lua2c.lua
new file mode 100644
index 00000000..3f8d1716
--- /dev/null
+++ b/src/lua/lua2c.lua
@@ -0,0 +1,29 @@
+-- lua2c.lua
+-- embed lua code into C source
+-- celetecgraf.puc-rio.br
+-- dez 2000
+
+function embed (code)
+
+ -- clean Lua code
+ local s = clean(code)
+ if not s then
+ error("parser error in embedded code")
+ end
+
+ -- convert to C
+ output('\n { /* begin embedded lua code */\n')
+ output(' static unsigned char B[] = {\n ')
+ local t={n=0}
+ local b = gsub(s,'(.)',function (c)
+ local e = ''
+ %t.n=%t.n+1 if %t.n==15 then %t.n=0 e='\n ' end
+ return format('%3u,%s',strbyte(c),e)
+ end
+ )
+ output(b..strbyte(" "))
+ output('\n };\n')
+ output(' lua_dobuffer(tolua_S,(char*)B,sizeof(B),"'..fn..': embedded Lua code");')
+ output(' } /* end of embedded lua code */\n\n')
+end
+
diff --git a/src/lua/luadebug.h b/src/lua/luadebug.h
new file mode 100644
index 00000000..21522445
--- /dev/null
+++ b/src/lua/luadebug.h
@@ -0,0 +1,46 @@
+/*
+** $Id: luadebug.h,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Debugging API
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef luadebug_h
+#define luadebug_h
+
+
+#include "lua.h"
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+typedef struct lua_Localvar lua_Localvar;
+
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+
+LUA_API lua_Hook lua_setcallhook (lua_State *L, lua_Hook func);
+LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func);
+
+
+#define LUA_IDSIZE 60
+
+struct lua_Debug {
+ const char *event; /* `call', `return' */
+ int currentline; /* (l) */
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `tag method', `local', `field' */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ const char *what; /* (S) `Lua' function, `C' function, Lua `main' */
+ const char *source; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ struct lua_TObject *_func; /* active function */
+};
+
+
+#endif
diff --git a/src/lua/lualib.h b/src/lua/lualib.h
new file mode 100644
index 00000000..89f5519f
--- /dev/null
+++ b/src/lua/lualib.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lualib.h,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+#ifndef LUALIB_API
+#define LUALIB_API extern
+#endif
+
+
+#define LUA_ALERT "_ALERT"
+
+LUALIB_API void lua_baselibopen (lua_State *L);
+LUALIB_API void lua_iolibopen (lua_State *L);
+LUALIB_API void lua_strlibopen (lua_State *L);
+LUALIB_API void lua_mathlibopen (lua_State *L);
+LUALIB_API void lua_dblibopen (lua_State *L);
+
+
+
+/* Auxiliary functions (private) */
+
+const char *luaI_classend (lua_State *L, const char *p);
+int luaI_singlematch (int c, const char *p, const char *ep);
+
+#endif
diff --git a/src/lua/lundump.c b/src/lua/lundump.c
new file mode 100644
index 00000000..7f69573e
--- /dev/null
+++ b/src/lua/lundump.c
@@ -0,0 +1,244 @@
+/*
+** $Id: lundump.c,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** load bytecodes from files
+** See Copyright Notice in lua.h
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lfunc.h"
+#include "lmem.h"
+#include "lopcodes.h"
+#include "lstring.h"
+#include "lundump.h"
+
+#define LoadByte ezgetc
+
+static const char* ZNAME (ZIO* Z)
+{
+ const char* s=zname(Z);
+ return (*s=='@') ? s+1 : s;
+}
+
+static void unexpectedEOZ (lua_State* L, ZIO* Z)
+{
+ luaO_verror(L,"unexpected end of file in `%.99s'",ZNAME(Z));
+}
+
+static int ezgetc (lua_State* L, ZIO* Z)
+{
+ int c=zgetc(Z);
+ if (c==EOZ) unexpectedEOZ(L,Z);
+ return c;
+}
+
+static void ezread (lua_State* L, ZIO* Z, void* b, int n)
+{
+ int r=zread(Z,b,n);
+ if (r!=0) unexpectedEOZ(L,Z);
+}
+
+static void LoadBlock (lua_State* L, void* b, size_t size, ZIO* Z, int swap)
+{
+ if (swap)
+ {
+ char *p=(char *) b+size-1;
+ int n=size;
+ while (n--) *p--=(char)ezgetc(L,Z);
+ }
+ else
+ ezread(L,Z,b,size);
+}
+
+static void LoadVector (lua_State* L, void* b, int m, size_t size, ZIO* Z, int swap)
+{
+ if (swap)
+ {
+ char *q=(char *) b;
+ while (m--)
+ {
+ char *p=q+size-1;
+ int n=size;
+ while (n--) *p--=(char)ezgetc(L,Z);
+ q+=size;
+ }
+ }
+ else
+ ezread(L,Z,b,m*size);
+}
+
+static int LoadInt (lua_State* L, ZIO* Z, int swap)
+{
+ int x;
+ LoadBlock(L,&x,sizeof(x),Z,swap);
+ return x;
+}
+
+static size_t LoadSize (lua_State* L, ZIO* Z, int swap)
+{
+ size_t x;
+ LoadBlock(L,&x,sizeof(x),Z,swap);
+ return x;
+}
+
+static Number LoadNumber (lua_State* L, ZIO* Z, int swap)
+{
+ Number x;
+ LoadBlock(L,&x,sizeof(x),Z,swap);
+ return x;
+}
+
+static TString* LoadString (lua_State* L, ZIO* Z, int swap)
+{
+ size_t size=LoadSize(L,Z,swap);
+ if (size==0)
+ return NULL;
+ else
+ {
+ char* s=luaO_openspace(L,size);
+ LoadBlock(L,s,size,Z,0);
+ return luaS_newlstr(L,s,size-1); /* remove trailing '\0' */
+ }
+}
+
+static void LoadCode (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int size=LoadInt(L,Z,swap);
+ tf->code=luaM_newvector(L,size,Instruction);
+ LoadVector(L,tf->code,size,sizeof(*tf->code),Z,swap);
+ if (tf->code[size-1]!=OP_END) luaO_verror(L,"bad code in `%.99s'",ZNAME(Z));
+ luaF_protook(L,tf,size);
+}
+
+static void LoadLocals (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int i,n;
+ tf->nlocvars=n=LoadInt(L,Z,swap);
+ tf->locvars=luaM_newvector(L,n,LocVar);
+ for (i=0; i<n; i++)
+ {
+ tf->locvars[i].varname=LoadString(L,Z,swap);
+ tf->locvars[i].startpc=LoadInt(L,Z,swap);
+ tf->locvars[i].endpc=LoadInt(L,Z,swap);
+ }
+}
+
+static void LoadLines (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int n;
+ tf->nlineinfo=n=LoadInt(L,Z,swap);
+ tf->lineinfo=luaM_newvector(L,n,int);
+ LoadVector(L,tf->lineinfo,n,sizeof(*tf->lineinfo),Z,swap);
+}
+
+static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap);
+
+static void LoadConstants (lua_State* L, Proto* tf, ZIO* Z, int swap)
+{
+ int i,n;
+ tf->nkstr=n=LoadInt(L,Z,swap);
+ tf->kstr=luaM_newvector(L,n,TString*);
+ for (i=0; i<n; i++)
+ tf->kstr[i]=LoadString(L,Z,swap);
+ tf->nknum=n=LoadInt(L,Z,swap);
+ tf->knum=luaM_newvector(L,n,Number);
+ LoadVector(L,tf->knum,n,sizeof(*tf->knum),Z,swap);
+ tf->nkproto=n=LoadInt(L,Z,swap);
+ tf->kproto=luaM_newvector(L,n,Proto*);
+ for (i=0; i<n; i++)
+ tf->kproto[i]=LoadFunction(L,Z,swap);
+}
+
+static Proto* LoadFunction (lua_State* L, ZIO* Z, int swap)
+{
+ Proto* tf=luaF_newproto(L);
+ tf->source=LoadString(L,Z,swap);
+ tf->lineDefined=LoadInt(L,Z,swap);
+ tf->numparams=LoadInt(L,Z,swap);
+ tf->is_vararg=LoadByte(L,Z);
+ tf->maxstacksize=LoadInt(L,Z,swap);
+ LoadLocals(L,tf,Z,swap);
+ LoadLines(L,tf,Z,swap);
+ LoadConstants(L,tf,Z,swap);
+ LoadCode(L,tf,Z,swap);
+ return tf;
+}
+
+static void LoadSignature (lua_State* L, ZIO* Z)
+{
+ const char* s=SIGNATURE;
+ while (*s!=0 && ezgetc(L,Z)==*s)
+ ++s;
+ if (*s!=0) luaO_verror(L,"bad signature in `%.99s'",ZNAME(Z));
+}
+
+static void TestSize (lua_State* L, int s, const char* what, ZIO* Z)
+{
+ int r=ezgetc(L,Z);
+ if (r!=s)
+ luaO_verror(L,"virtual machine mismatch in `%.99s':\n"
+ " %.20s is %d but read %d",ZNAME(Z),what,s,r);
+}
+
+#define TESTSIZE(s) TestSize(L,s,#s,Z)
+#define V(v) v/16,v%16
+
+static int LoadHeader (lua_State* L, ZIO* Z)
+{
+ int version,swap;
+ Number f=0,tf=TEST_NUMBER;
+ LoadSignature(L,Z);
+ version=ezgetc(L,Z);
+ if (version>VERSION)
+ luaO_verror(L,"`%.99s' too new:\n"
+ " read version %d.%d; expected at most %d.%d",
+ ZNAME(Z),V(version),V(VERSION));
+ if (version<VERSION0) /* check last major change */
+ luaO_verror(L,"`%.99s' too old:\n"
+ " read version %d.%d; expected at least %d.%d",
+ ZNAME(Z),V(version),V(VERSION));
+ swap=(luaU_endianess()!=ezgetc(L,Z)); /* need to swap bytes? */
+ TESTSIZE(sizeof(int));
+ TESTSIZE(sizeof(size_t));
+ TESTSIZE(sizeof(Instruction));
+ TESTSIZE(SIZE_INSTRUCTION);
+ TESTSIZE(SIZE_OP);
+ TESTSIZE(SIZE_B);
+ TESTSIZE(sizeof(Number));
+ f=LoadNumber(L,Z,swap);
+ if ((long)f!=(long)tf) /* disregard errors in last bit of fraction */
+ luaO_verror(L,"unknown number format in `%.99s':\n"
+ " read " NUMBER_FMT "; expected " NUMBER_FMT, ZNAME(Z),f,tf);
+ return swap;
+}
+
+static Proto* LoadChunk (lua_State* L, ZIO* Z)
+{
+ return LoadFunction(L,Z,LoadHeader(L,Z));
+}
+
+/*
+** load one chunk from a file or buffer
+** return main if ok and NULL at EOF
+*/
+Proto* luaU_undump (lua_State* L, ZIO* Z)
+{
+ Proto* tf=NULL;
+ int c=zgetc(Z);
+ if (c==ID_CHUNK)
+ tf=LoadChunk(L,Z);
+ c=zgetc(Z);
+ if (c!=EOZ)
+ luaO_verror(L,"`%.99s' apparently contains more than one chunk",ZNAME(Z));
+ return tf;
+}
+
+/*
+** find byte order
+*/
+int luaU_endianess (void)
+{
+ int x=1;
+ return *(char*)&x;
+}
diff --git a/src/lua/lundump.h b/src/lua/lundump.h
new file mode 100644
index 00000000..ec394f46
--- /dev/null
+++ b/src/lua/lundump.h
@@ -0,0 +1,35 @@
+/*
+** $Id: lundump.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** load pre-compiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+/* load one chunk */
+Proto* luaU_undump (lua_State* L, ZIO* Z);
+
+/* find byte order */
+int luaU_endianess (void);
+
+/* definitions for headers of binary files */
+#define VERSION 0x40 /* last format change was in 4.0 */
+#define VERSION0 0x40 /* last major change was in 4.0 */
+#define ID_CHUNK 27 /* binary files start with ESC... */
+#define SIGNATURE "Lua" /* ...followed by this signature */
+
+/* formats for error messages */
+#define SOURCE_FMT "<%d:%.99s>"
+#define SOURCE tf->lineDefined,tf->source->str
+#define IN_FMT " in %p " SOURCE_FMT
+#define IN tf,SOURCE
+
+/* a multiple of PI for testing native format */
+/* multiplying by 1E8 gives non-trivial integer values */
+#define TEST_NUMBER 3
+
+#endif
diff --git a/src/lua/lvm.c b/src/lua/lvm.c
new file mode 100644
index 00000000..e304e11e
--- /dev/null
+++ b/src/lua/lvm.c
@@ -0,0 +1,710 @@
+/*
+** $Id: lvm.c,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+#ifdef OLD_ANSI
+#define strcoll(a,b) strcmp(a,b)
+#endif
+
+
+
+/*
+** Extra stack size to run a function:
+** TAG_LINE(1), NAME(1), TM calls(3) (plus some extra...)
+*/
+#define EXTRA_STACK 8
+
+
+
+int luaV_tonumber (TObject *obj) {
+ if (ttype(obj) != LUA_TSTRING)
+ return 1;
+ else {
+ if (!luaO_str2d(svalue(obj), &nvalue(obj)))
+ return 2;
+ ttype(obj) = LUA_TNUMBER;
+ return 0;
+ }
+}
+
+
+int luaV_tostring (lua_State *L, TObject *obj) { /* LUA_NUMBER */
+ if (ttype(obj) != LUA_TNUMBER)
+ return 1;
+ else {
+ char s[32]; /* 16 digits, sign, point and \0 (+ some extra...) */
+ lua_number2str(s, nvalue(obj)); /* convert `s' to number */
+ tsvalue(obj) = luaS_new(L, s);
+ ttype(obj) = LUA_TSTRING;
+ return 0;
+ }
+}
+
+
+static void traceexec (lua_State *L, StkId base, StkId top, lua_Hook linehook) {
+ CallInfo *ci = infovalue(base-1);
+ int *lineinfo = ci->func->f.l->lineinfo;
+ int pc = (*ci->pc - ci->func->f.l->code) - 1;
+ int newline;
+ if (pc == 0) { /* may be first time? */
+ ci->line = 1;
+ ci->refi = 0;
+ ci->lastpc = pc+1; /* make sure it will call linehook */
+ }
+ newline = luaG_getline(lineinfo, pc, ci->line, &ci->refi);
+ /* calls linehook when enters a new line or jumps back (loop) */
+ if (newline != ci->line || pc <= ci->lastpc) {
+ ci->line = newline;
+ L->top = top;
+ luaD_lineHook(L, base-2, newline, linehook);
+ }
+ ci->lastpc = pc;
+}
+
+
+static Closure *luaV_closure (lua_State *L, int nelems) {
+ Closure *c = luaF_newclosure(L, nelems);
+ L->top -= nelems;
+ while (nelems--)
+ c->upvalue[nelems] = *(L->top+nelems);
+ clvalue(L->top) = c;
+ ttype(L->top) = LUA_TFUNCTION;
+ incr_top;
+ return c;
+}
+
+
+void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems) {
+ Closure *cl = luaV_closure(L, nelems);
+ cl->f.c = c;
+ cl->isC = 1;
+}
+
+
+void luaV_Lclosure (lua_State *L, Proto *l, int nelems) {
+ Closure *cl = luaV_closure(L, nelems);
+ cl->f.l = l;
+ cl->isC = 0;
+}
+
+
+/*
+** Function to index a table.
+** Receives the table at `t' and the key at top.
+*/
+const TObject *luaV_gettable (lua_State *L, StkId t) {
+ Closure *tm;
+ int tg;
+ if (ttype(t) == LUA_TTABLE && /* `t' is a table? */
+ ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */
+ luaT_gettm(L, tg, TM_GETTABLE) == NULL)) { /* or no TM? */
+ /* do a primitive get */
+ const TObject *h = luaH_get(L, hvalue(t), L->top-1);
+ /* result is no nil or there is no `index' tag method? */
+ if (ttype(h) != LUA_TNIL || ((tm=luaT_gettm(L, tg, TM_INDEX)) == NULL))
+ return h; /* return result */
+ /* else call `index' tag method */
+ }
+ else { /* try a `gettable' tag method */
+ tm = luaT_gettmbyObj(L, t, TM_GETTABLE);
+ }
+ if (tm != NULL) { /* is there a tag method? */
+ luaD_checkstack(L, 2);
+ *(L->top+1) = *(L->top-1); /* key */
+ *L->top = *t; /* table */
+ clvalue(L->top-1) = tm; /* tag method */
+ ttype(L->top-1) = LUA_TFUNCTION;
+ L->top += 2;
+ luaD_call(L, L->top - 3, 1);
+ return L->top - 1; /* call result */
+ }
+ else { /* no tag method */
+ luaG_typeerror(L, t, "index");
+ return NULL; /* to avoid warnings */
+ }
+}
+
+
+/*
+** Receives table at `t', key at `key' and value at top.
+*/
+void luaV_settable (lua_State *L, StkId t, StkId key) {
+ int tg;
+ if (ttype(t) == LUA_TTABLE && /* `t' is a table? */
+ ((tg = hvalue(t)->htag) == LUA_TTABLE || /* with default tag? */
+ luaT_gettm(L, tg, TM_SETTABLE) == NULL)) /* or no TM? */
+ *luaH_set(L, hvalue(t), key) = *(L->top-1); /* do a primitive set */
+ else { /* try a `settable' tag method */
+ Closure *tm = luaT_gettmbyObj(L, t, TM_SETTABLE);
+ if (tm != NULL) {
+ luaD_checkstack(L, 3);
+ *(L->top+2) = *(L->top-1);
+ *(L->top+1) = *key;
+ *(L->top) = *t;
+ clvalue(L->top-1) = tm;
+ ttype(L->top-1) = LUA_TFUNCTION;
+ L->top += 3;
+ luaD_call(L, L->top - 4, 0); /* call `settable' tag method */
+ }
+ else /* no tag method... */
+ luaG_typeerror(L, t, "index");
+ }
+}
+
+
+const TObject *luaV_getglobal (lua_State *L, TString *s) {
+ const TObject *value = luaH_getstr(L->gt, s);
+ Closure *tm = luaT_gettmbyObj(L, value, TM_GETGLOBAL);
+ if (tm == NULL) /* is there a tag method? */
+ return value; /* default behavior */
+ else { /* tag method */
+ luaD_checkstack(L, 3);
+ clvalue(L->top) = tm;
+ ttype(L->top) = LUA_TFUNCTION;
+ tsvalue(L->top+1) = s; /* global name */
+ ttype(L->top+1) = LUA_TSTRING;
+ *(L->top+2) = *value;
+ L->top += 3;
+ luaD_call(L, L->top - 3, 1);
+ return L->top - 1;
+ }
+}
+
+
+void luaV_setglobal (lua_State *L, TString *s) {
+ const TObject *oldvalue = luaH_getstr(L->gt, s);
+ Closure *tm = luaT_gettmbyObj(L, oldvalue, TM_SETGLOBAL);
+ if (tm == NULL) { /* is there a tag method? */
+ if (oldvalue != &luaO_nilobject) {
+ /* cast to remove `const' is OK, because `oldvalue' != luaO_nilobject */
+ *(TObject *)oldvalue = *(L->top - 1);
+ }
+ else {
+ TObject key;
+ ttype(&key) = LUA_TSTRING;
+ tsvalue(&key) = s;
+ *luaH_set(L, L->gt, &key) = *(L->top - 1);
+ }
+ }
+ else {
+ luaD_checkstack(L, 3);
+ *(L->top+2) = *(L->top-1); /* new value */
+ *(L->top+1) = *oldvalue;
+ ttype(L->top) = LUA_TSTRING;
+ tsvalue(L->top) = s;
+ clvalue(L->top-1) = tm;
+ ttype(L->top-1) = LUA_TFUNCTION;
+ L->top += 3;
+ luaD_call(L, L->top - 4, 0);
+ }
+}
+
+
+static int call_binTM (lua_State *L, StkId top, TMS event) {
+ /* try first operand */
+ Closure *tm = luaT_gettmbyObj(L, top-2, event);
+ L->top = top;
+ if (tm == NULL) {
+ tm = luaT_gettmbyObj(L, top-1, event); /* try second operand */
+ if (tm == NULL) {
+ tm = luaT_gettm(L, 0, event); /* try a `global' method */
+ if (tm == NULL)
+ return 0; /* error */
+ }
+ }
+ lua_pushstring(L, luaT_eventname[event]);
+ luaD_callTM(L, tm, 3, 1);
+ return 1;
+}
+
+
+static void call_arith (lua_State *L, StkId top, TMS event) {
+ if (!call_binTM(L, top, event))
+ luaG_binerror(L, top-2, LUA_TNUMBER, "perform arithmetic on");
+}
+
+
+static int luaV_strcomp (const TString *ls, const TString *rs) {
+ const char *l = ls->str;
+ size_t ll = ls->len;
+ const char *r = rs->str;
+ size_t lr = rs->len;
+ for (;;) {
+ int temp = strcoll(l, r);
+ if (temp != 0) return temp;
+ else { /* strings are equal up to a '\0' */
+ size_t len = strlen(l); /* index of first '\0' in both strings */
+ if (len == ll) /* l is finished? */
+ return (len == lr) ? 0 : -1; /* l is equal or smaller than r */
+ else if (len == lr) /* r is finished? */
+ return 1; /* l is greater than r (because l is not finished) */
+ /* both strings longer than `len'; go on comparing (after the '\0') */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top) {
+ if (ttype(l) == LUA_TNUMBER && ttype(r) == LUA_TNUMBER)
+ return (nvalue(l) < nvalue(r));
+ else if (ttype(l) == LUA_TSTRING && ttype(r) == LUA_TSTRING)
+ return (luaV_strcomp(tsvalue(l), tsvalue(r)) < 0);
+ else { /* call TM */
+ luaD_checkstack(L, 2);
+ *top++ = *l;
+ *top++ = *r;
+ if (!call_binTM(L, top, TM_LT))
+ luaG_ordererror(L, top-2);
+ L->top--;
+ return (ttype(L->top) != LUA_TNIL);
+ }
+}
+
+
+void luaV_strconc (lua_State *L, int total, StkId top) {
+ do {
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (tostring(L, top-2) || tostring(L, top-1)) {
+ if (!call_binTM(L, top, TM_CONCAT))
+ luaG_binerror(L, top-2, LUA_TSTRING, "concat");
+ }
+ else if (tsvalue(top-1)->len > 0) { /* if len=0, do nothing */
+ /* at least two string values; get as many as possible */
+ lint32 tl = (lint32)tsvalue(top-1)->len +
+ (lint32)tsvalue(top-2)->len;
+ char *buffer;
+ int i;
+ while (n < total && !tostring(L, top-n-1)) { /* collect total length */
+ tl += tsvalue(top-n-1)->len;
+ n++;
+ }
+ if (tl > MAX_SIZET) lua_error(L, "string size overflow");
+ buffer = luaO_openspace(L, tl);
+ tl = 0;
+ for (i=n; i>0; i--) { /* concat all strings */
+ size_t l = tsvalue(top-i)->len;
+ memcpy(buffer+tl, tsvalue(top-i)->str, l);
+ tl += l;
+ }
+ tsvalue(top-n) = luaS_newlstr(L, buffer, tl);
+ }
+ total -= n-1; /* got `n' strings to create 1 new */
+ top -= n-1;
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+static void luaV_pack (lua_State *L, StkId firstelem) {
+ int i;
+ Hash *htab = luaH_new(L, 0);
+ for (i=0; firstelem+i<L->top; i++)
+ *luaH_setint(L, htab, i+1) = *(firstelem+i);
+ /* store counter in field `n' */
+ luaH_setstrnum(L, htab, luaS_new(L, "n"), i);
+ L->top = firstelem; /* remove elements from the stack */
+ ttype(L->top) = LUA_TTABLE;
+ hvalue(L->top) = htab;
+ incr_top;
+}
+
+
+static void adjust_varargs (lua_State *L, StkId base, int nfixargs) {
+ int nvararg = (L->top-base) - nfixargs;
+ if (nvararg < 0)
+ luaD_adjusttop(L, base, nfixargs);
+ luaV_pack(L, base+nfixargs);
+}
+
+
+
+#define dojump(pc, i) { int d = GETARG_S(i); pc += d; }
+
+/*
+** Executes the given Lua function. Parameters are between [base,top).
+** Returns n such that the the results are between [n,top).
+*/
+StkId luaV_execute (lua_State *L, const Closure *cl, StkId base) {
+ const Proto *const tf = cl->f.l;
+ StkId top; /* keep top local, for performance */
+ const Instruction *pc = tf->code;
+ TString **const kstr = tf->kstr;
+ const lua_Hook linehook = L->linehook;
+ infovalue(base-1)->pc = &pc;
+ luaD_checkstack(L, tf->maxstacksize+EXTRA_STACK);
+ if (tf->is_vararg) /* varargs? */
+ adjust_varargs(L, base, tf->numparams);
+ else
+ luaD_adjusttop(L, base, tf->numparams);
+ top = L->top;
+ /* main loop of interpreter */
+ for (;;) {
+ const Instruction i = *pc++;
+ if (linehook)
+ traceexec(L, base, top, linehook);
+ switch (GET_OPCODE(i)) {
+ case OP_END: {
+ L->top = top;
+ return top;
+ }
+ case OP_RETURN: {
+ L->top = top;
+ return base+GETARG_U(i);
+ }
+ case OP_CALL: {
+ int nres = GETARG_B(i);
+ if (nres == MULT_RET) nres = LUA_MULTRET;
+ L->top = top;
+ luaD_call(L, base+GETARG_A(i), nres);
+ top = L->top;
+ break;
+ }
+ case OP_TAILCALL: {
+ L->top = top;
+ luaD_call(L, base+GETARG_A(i), LUA_MULTRET);
+ return base+GETARG_B(i);
+ }
+ case OP_PUSHNIL: {
+ int n = GETARG_U(i);
+ LUA_ASSERT(n>0, "invalid argument");
+ do {
+ ttype(top++) = LUA_TNIL;
+ } while (--n > 0);
+ break;
+ }
+ case OP_POP: {
+ top -= GETARG_U(i);
+ break;
+ }
+ case OP_PUSHINT: {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = (Number)GETARG_S(i);
+ top++;
+ break;
+ }
+ case OP_PUSHSTRING: {
+ ttype(top) = LUA_TSTRING;
+ tsvalue(top) = kstr[GETARG_U(i)];
+ top++;
+ break;
+ }
+ case OP_PUSHNUM: {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = tf->knum[GETARG_U(i)];
+ top++;
+ break;
+ }
+ case OP_PUSHNEGNUM: {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = -tf->knum[GETARG_U(i)];
+ top++;
+ break;
+ }
+ case OP_PUSHUPVALUE: {
+ *top++ = cl->upvalue[GETARG_U(i)];
+ break;
+ }
+ case OP_GETLOCAL: {
+ *top++ = *(base+GETARG_U(i));
+ break;
+ }
+ case OP_GETGLOBAL: {
+ L->top = top;
+ *top = *luaV_getglobal(L, kstr[GETARG_U(i)]);
+ top++;
+ break;
+ }
+ case OP_GETTABLE: {
+ L->top = top;
+ top--;
+ *(top-1) = *luaV_gettable(L, top-1);
+ break;
+ }
+ case OP_GETDOTTED: {
+ ttype(top) = LUA_TSTRING;
+ tsvalue(top) = kstr[GETARG_U(i)];
+ L->top = top+1;
+ *(top-1) = *luaV_gettable(L, top-1);
+ break;
+ }
+ case OP_GETINDEXED: {
+ *top = *(base+GETARG_U(i));
+ L->top = top+1;
+ *(top-1) = *luaV_gettable(L, top-1);
+ break;
+ }
+ case OP_PUSHSELF: {
+ TObject receiver;
+ receiver = *(top-1);
+ ttype(top) = LUA_TSTRING;
+ tsvalue(top++) = kstr[GETARG_U(i)];
+ L->top = top;
+ *(top-2) = *luaV_gettable(L, top-2);
+ *(top-1) = receiver;
+ break;
+ }
+ case OP_CREATETABLE: {
+ L->top = top;
+ luaC_checkGC(L);
+ hvalue(top) = luaH_new(L, GETARG_U(i));
+ ttype(top) = LUA_TTABLE;
+ top++;
+ break;
+ }
+ case OP_SETLOCAL: {
+ *(base+GETARG_U(i)) = *(--top);
+ break;
+ }
+ case OP_SETGLOBAL: {
+ L->top = top;
+ luaV_setglobal(L, kstr[GETARG_U(i)]);
+ top--;
+ break;
+ }
+ case OP_SETTABLE: {
+ StkId t = top-GETARG_A(i);
+ L->top = top;
+ luaV_settable(L, t, t+1);
+ top -= GETARG_B(i); /* pop values */
+ break;
+ }
+ case OP_SETLIST: {
+ int aux = GETARG_A(i) * LFIELDS_PER_FLUSH;
+ int n = GETARG_B(i);
+ Hash *arr = hvalue(top-n-1);
+ L->top = top-n; /* final value of `top' (in case of errors) */
+ for (; n; n--)
+ *luaH_setint(L, arr, n+aux) = *(--top);
+ break;
+ }
+ case OP_SETMAP: {
+ int n = GETARG_U(i);
+ StkId finaltop = top-2*n;
+ Hash *arr = hvalue(finaltop-1);
+ L->top = finaltop; /* final value of `top' (in case of errors) */
+ for (; n; n--) {
+ top-=2;
+ *luaH_set(L, arr, top) = *(top+1);
+ }
+ break;
+ }
+ case OP_ADD: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_ADD);
+ else
+ nvalue(top-2) += nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_ADDI: {
+ if (tonumber(top-1)) {
+ ttype(top) = LUA_TNUMBER;
+ nvalue(top) = (Number)GETARG_S(i);
+ call_arith(L, top+1, TM_ADD);
+ }
+ else
+ nvalue(top-1) += (Number)GETARG_S(i);
+ break;
+ }
+ case OP_SUB: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_SUB);
+ else
+ nvalue(top-2) -= nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_MULT: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_MUL);
+ else
+ nvalue(top-2) *= nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_DIV: {
+ if (tonumber(top-2) || tonumber(top-1))
+ call_arith(L, top, TM_DIV);
+ else
+ nvalue(top-2) /= nvalue(top-1);
+ top--;
+ break;
+ }
+ case OP_POW: {
+ if (!call_binTM(L, top, TM_POW))
+ lua_error(L, "undefined operation");
+ top--;
+ break;
+ }
+ case OP_CONCAT: {
+ int n = GETARG_U(i);
+ luaV_strconc(L, n, top);
+ top -= n-1;
+ L->top = top;
+ luaC_checkGC(L);
+ break;
+ }
+ case OP_MINUS: {
+ if (tonumber(top-1)) {
+ ttype(top) = LUA_TNIL;
+ call_arith(L, top+1, TM_UNM);
+ }
+ else
+ nvalue(top-1) = -nvalue(top-1);
+ break;
+ }
+ case OP_NOT: {
+ ttype(top-1) =
+ (ttype(top-1) == LUA_TNIL) ? LUA_TNUMBER : LUA_TNIL;
+ nvalue(top-1) = 1;
+ break;
+ }
+ case OP_JMPNE: {
+ top -= 2;
+ if (!luaO_equalObj(top, top+1)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPEQ: {
+ top -= 2;
+ if (luaO_equalObj(top, top+1)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPLT: {
+ top -= 2;
+ if (luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPLE: { /* a <= b === !(b<a) */
+ top -= 2;
+ if (!luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPGT: { /* a > b === (b<a) */
+ top -= 2;
+ if (luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPGE: { /* a >= b === !(a<b) */
+ top -= 2;
+ if (!luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i);
+ break;
+ }
+ case OP_JMPT: {
+ if (ttype(--top) != LUA_TNIL) dojump(pc, i);
+ break;
+ }
+ case OP_JMPF: {
+ if (ttype(--top) == LUA_TNIL) dojump(pc, i);
+ break;
+ }
+ case OP_JMPONT: {
+ if (ttype(top-1) == LUA_TNIL) top--;
+ else dojump(pc, i);
+ break;
+ }
+ case OP_JMPONF: {
+ if (ttype(top-1) != LUA_TNIL) top--;
+ else dojump(pc, i);
+ break;
+ }
+ case OP_JMP: {
+ dojump(pc, i);
+ break;
+ }
+ case OP_PUSHNILJMP: {
+ ttype(top++) = LUA_TNIL;
+ pc++;
+ break;
+ }
+ case OP_FORPREP: {
+ if (tonumber(top-1))
+ lua_error(L, "`for' step must be a number");
+ if (tonumber(top-2))
+ lua_error(L, "`for' limit must be a number");
+ if (tonumber(top-3))
+ lua_error(L, "`for' initial value must be a number");
+ if (nvalue(top-1) > 0 ?
+ nvalue(top-3) > nvalue(top-2) :
+ nvalue(top-3) < nvalue(top-2)) { /* `empty' loop? */
+ top -= 3; /* remove control variables */
+ dojump(pc, i); /* jump to loop end */
+ }
+ break;
+ }
+ case OP_FORLOOP: {
+ LUA_ASSERT(ttype(top-1) == LUA_TNUMBER, "invalid step");
+ LUA_ASSERT(ttype(top-2) == LUA_TNUMBER, "invalid limit");
+ if (ttype(top-3) != LUA_TNUMBER)
+ lua_error(L, "`for' index must be a number");
+ nvalue(top-3) += nvalue(top-1); /* increment index */
+ if (nvalue(top-1) > 0 ?
+ nvalue(top-3) > nvalue(top-2) :
+ nvalue(top-3) < nvalue(top-2))
+ top -= 3; /* end loop: remove control variables */
+ else
+ dojump(pc, i); /* repeat loop */
+ break;
+ }
+ case OP_LFORPREP: {
+ Node *node;
+ if (ttype(top-1) != LUA_TTABLE)
+ lua_error(L, "`for' table must be a table");
+ node = luaH_next(L, hvalue(top-1), &luaO_nilobject);
+ if (node == NULL) { /* `empty' loop? */
+ top--; /* remove table */
+ dojump(pc, i); /* jump to loop end */
+ }
+ else {
+ top += 2; /* index,value */
+ *(top-2) = *key(node);
+ *(top-1) = *val(node);
+ }
+ break;
+ }
+ case OP_LFORLOOP: {
+ Node *node;
+ LUA_ASSERT(ttype(top-3) == LUA_TTABLE, "invalid table");
+ node = luaH_next(L, hvalue(top-3), top-2);
+ if (node == NULL) /* end loop? */
+ top -= 3; /* remove table, key, and value */
+ else {
+ *(top-2) = *key(node);
+ *(top-1) = *val(node);
+ dojump(pc, i); /* repeat loop */
+ }
+ break;
+ }
+ case OP_CLOSURE: {
+ L->top = top;
+ luaV_Lclosure(L, tf->kproto[GETARG_A(i)], GETARG_B(i));
+ top = L->top;
+ luaC_checkGC(L);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/lua/lvm.h b/src/lua/lvm.h
new file mode 100644
index 00000000..ac95ae41
--- /dev/null
+++ b/src/lua/lvm.h
@@ -0,0 +1,32 @@
+/*
+** $Id: lvm.h,v 1.3 2001/11/26 23:00:26 darkgod Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#define tonumber(o) ((ttype(o) != LUA_TNUMBER) && (luaV_tonumber(o) != 0))
+#define tostring(L,o) ((ttype(o) != LUA_TSTRING) && (luaV_tostring(L, o) != 0))
+
+
+int luaV_tonumber (TObject *obj);
+int luaV_tostring (lua_State *L, TObject *obj);
+const TObject *luaV_gettable (lua_State *L, StkId t);
+void luaV_settable (lua_State *L, StkId t, StkId key);
+const TObject *luaV_getglobal (lua_State *L, TString *s);
+void luaV_setglobal (lua_State *L, TString *s);
+StkId luaV_execute (lua_State *L, const Closure *cl, StkId base);
+void luaV_Cclosure (lua_State *L, lua_CFunction c, int nelems);
+void luaV_Lclosure (lua_State *L, Proto *l, int nelems);
+int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r, StkId top);
+void luaV_strconc (lua_State *L, int total, StkId top);
+
+#endif
diff --git a/src/lua/lzio.c b/src/lua/lzio.c
new file mode 100644
index 00000000..84d4a35c
--- /dev/null
+++ b/src/lua/lzio.c
@@ -0,0 +1,84 @@
+/*
+** $Id: lzio.c,v 1.5 2004/06/04 13:42:10 neil Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lzio.h"
+
+
+
+/* ----------------------------------------------------- memory buffers --- */
+
+static int zmfilbuf (ZIO* z) {
+ (void)z; /* to avoid warnings */
+ return EOZ;
+}
+
+
+ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name) {
+ if (b==NULL) return NULL;
+ z->n = size;
+ z->p = (const unsigned char *)b;
+ z->filbuf = zmfilbuf;
+ z->u = NULL;
+ z->name = name;
+ return z;
+}
+
+/* ------------------------------------------------------------ strings --- */
+
+ZIO* zsopen (ZIO* z, const char* s, const char *name) {
+ if (s==NULL) return NULL;
+ return zmopen(z, s, strlen(s), name);
+}
+
+/* -------------------------------------------------------------- FILEs --- */
+
+static int zffilbuf (ZIO* z) {
+ size_t n;
+ if (feof((FILE *)z->u)) return EOZ;
+ n = fread(z->buffer, 1, ZBSIZE, (FILE *)z->u);
+ if (n==0) return EOZ;
+ z->n = n-1;
+ z->p = z->buffer;
+ return *(z->p++);
+}
+
+
+ZIO* zFopen (ZIO* z, FILE* f, const char *name) {
+ if (f==NULL) return NULL;
+ z->n = 0;
+ z->p = z->buffer;
+ z->filbuf = zffilbuf;
+ z->u = f;
+ z->name = name;
+ return z;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t zread (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (z->n == 0) {
+ if (z->filbuf(z) == EOZ)
+ return n; /* return number of missing bytes */
+ zungetc(z); /* put result from `filbuf' in the buffer */
+ }
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
diff --git a/src/lua/lzio.h b/src/lua/lzio.h
new file mode 100644
index 00000000..45feeee3
--- /dev/null
+++ b/src/lua/lzio.h
@@ -0,0 +1,53 @@
+/*
+** $Id: lzio.h,v 1.5 2004/06/04 13:42:10 neil Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include <stdio.h>
+
+
+
+/* For Lua only */
+#define zFopen luaZ_Fopen
+#define zsopen luaZ_sopen
+#define zmopen luaZ_mopen
+#define zread luaZ_read
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct zio ZIO;
+
+ZIO* zFopen (ZIO* z, FILE* f, const char *name); /* open FILEs */
+ZIO* zsopen (ZIO* z, const char* s, const char *name); /* string */
+ZIO* zmopen (ZIO* z, const char* b, size_t size, const char *name); /* memory */
+
+size_t zread (ZIO* z, void* b, size_t n); /* read next n bytes */
+
+#define zgetc(z) (((z)->n--)>0 ? ((int)*(z)->p++): (z)->filbuf(z))
+#define zungetc(z) (++(z)->n,--(z)->p)
+#define zname(z) ((z)->name)
+
+
+
+/* --------- Private Part ------------------ */
+
+#ifndef ZBSIZE
+#define ZBSIZE 256 /* buffer size */
+#endif
+
+struct zio {
+ size_t n; /* bytes still unread */
+ const unsigned char* p; /* current position in buffer */
+ int (*filbuf)(ZIO* z);
+ void* u; /* additional data */
+ const char *name;
+ unsigned char buffer[ZBSIZE]; /* buffer */
+};
+
+
+#endif
diff --git a/src/lua/module.lua b/src/lua/module.lua
new file mode 100644
index 00000000..98dffe6e
--- /dev/null
+++ b/src/lua/module.lua
@@ -0,0 +1,69 @@
+-- tolua: module class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: module.lua,v 1.2 2001/11/26 23:00:26 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Module class
+-- Represents module.
+-- The following fields are stored:
+-- {i} = list of objects in the module.
+classModule = {
+ _base = classContainer,
+ type = 'module'
+}
+settag(classModule,tolua_tag)
+
+-- register module
+function classModule:register ()
+ output(' tolua_module(tolua_S,"'..self.name..'");')
+ local i=1
+ while self[i] do
+ self[i]:register()
+ i = i+1
+ end
+end
+
+-- unregister module
+function classModule:unregister ()
+ output(' lua_pushnil(tolua_S); lua_setglobal(tolua_S,"'..self.name..'");')
+end
+
+-- Print method
+function classModule:print (ident,close)
+ print(ident.."Module{")
+ print(ident.." name = '"..self.name.."';")
+ local i=1
+ while self[i] do
+ self[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Module (t)
+ t._base = classModule
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects two string representing the module name and body.
+function Module (n,b)
+ local t = _Module(_Container{name=n})
+ push(t)
+ t:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces
+ pop()
+ return t
+end
+
+
diff --git a/src/lua/operator.lua b/src/lua/operator.lua
new file mode 100644
index 00000000..7a42cf1b
--- /dev/null
+++ b/src/lua/operator.lua
@@ -0,0 +1,111 @@
+-- tolua: operator class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: operator.lua,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Operator class
+-- Represents an operator function or a class operator method.
+-- It stores the same fields as functions do plus:
+-- kind = set of character representing the operator (as it appers in C++ code)
+classOperator = {
+ kind = '',
+ _base = classFunction,
+}
+settag(classOperator,tolua_tag)
+
+-- table to transform operator kind into the appropriate tag method name
+_TM = {['+'] = 'operator_add',
+ ['-'] = 'operator_sub',
+ ['*'] = 'operator_mul',
+ ['/'] = 'operator_div',
+ ['<'] = 'operator_lt',
+ ['[]'] = 'operator_get',
+ ['&[]'] = 'operator_set',
+ }
+
+
+-- Print method
+function classOperator:print (ident,close)
+ print(ident.."Operator{")
+ print(ident.." kind = '"..self.kind.."',")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." const = '"..self.const.."',")
+ print(ident.." cname = '"..self.cname.."',")
+ print(ident.." lname = '"..self.lname.."',")
+ print(ident.." args = {")
+ local i=1
+ while self.args[i] do
+ self.args[i]:print(ident.." ",",")
+ i = i+1
+ end
+ print(ident.." }")
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Operator (t)
+ t._base = classOperator
+ settag(t,tolua_tag)
+
+ if t.const ~= 'const' and t.const ~= '' then
+ error("#invalid 'const' specification")
+ end
+
+ append(t)
+ if not t:inclass() then
+ error("#operator can only be defined as class member")
+ end
+
+ t.cname = t:cfuncname("toluaI")..t:overload(t)
+ t.name = t.name..t.kind
+ return t
+end
+
+-- Constructor
+-- Expects three strings: one representing the function declaration,
+-- another representing the argument list, and the third representing
+-- the "const" or empty string.
+function Operator (d,k,a,c)
+ local t = split(strsub(a,2,strlen(a)-1),',') -- eliminate braces
+ local i=1
+ local l = {n=0}
+ while t[i] do
+ l.n = l.n+1
+ l[l.n] = Declaration(t[i],'var')
+ i = i+1
+ end
+ if k == '[]' then
+ d = gsub(d,'&','')
+ elseif k=='&[]' then
+ l.n = l.n+1
+ l[l.n] = Declaration(d,'var')
+ l[l.n].name = 'toluaI_value'
+ end
+ local f = Declaration(d,'func')
+ if k == '[]' and (l[1]==nil or isbasic(l[1].type)~='number') then
+ error('operator[] can only be defined for numeric index.')
+ end
+ f.args = l
+ f.const = c
+ f.kind = gsub(k,"%s","")
+ f.lname = _TM[f.kind]
+ if not f.lname then
+ error("tolua: no support for operator" .. f.kind)
+ end
+ if f.kind == '[]' and not strfind(f.mod,'const') then
+ Operator(d,'&'..k,a,c) -- create correspoding set operator
+ end
+ return _Operator(f)
+end
+
+
diff --git a/src/lua/package.lua b/src/lua/package.lua
new file mode 100644
index 00000000..42dbfaac
--- /dev/null
+++ b/src/lua/package.lua
@@ -0,0 +1,222 @@
+-- tolua: package class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: package.lua,v 1.4 2002/01/03 13:45:08 takkaria Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Package class
+-- Represents the whole package being bound.
+-- The following fields are stored:
+-- {i} = list of objects in the package.
+classPackage = {
+ _base = classContainer,
+ type = 'package'
+}
+settag(classPackage,tolua_tag)
+
+-- Print method
+function classPackage:print ()
+ print("Package: "..self.name)
+ local i=1
+ while self[i] do
+ self[i]:print("","")
+ i = i+1
+ end
+end
+
+function classPackage:preprocess ()
+ self.code = "\n"..self.code -- add a blank sentinel line
+ -- avoid preprocessing verbatim lines
+ local V = {}
+ self.code = gsub(self.code,"\n(%s*%$[^%[%]][^\n]*)",function (v)
+ tinsert(%V,v)
+ return "\n$"..getn(%V).."$"
+ end)
+ -- avoid preprocessing embedded lua code
+ local C = {}
+ self.code = gsub(self.code,"\n%s*%$%[","\1") -- deal with embedded Lua code
+ self.code = gsub(self.code,"\n%s*%$%]","\2")
+ self.code = gsub(self.code,"(%b\1\2)", function (c)
+ tinsert(%C,c)
+ return "\n$["..getn(%C).."]$"
+ end)
+ -- perform global substitution
+
+ self.code = gsub(self.code,"(//[^\n]*)","") -- eliminate C++ comments
+ self.code = gsub(self.code,"/%*","\1")
+ self.code = gsub(self.code,"%*/","\2")
+ self.code = gsub(self.code,"%b\1\2","")
+ self.code = gsub(self.code,"\1","/%*")
+ self.code = gsub(self.code,"\2","%*/")
+ self.code = gsub(self.code,"%s*@%s*","@") -- eliminate spaces beside @
+ self.code = gsub(self.code,"%s?inline(%s)","%1") -- eliminate 'inline' keyword
+ self.code = gsub(self.code,"%s?extern(%s)","%1") -- eliminate 'extern' keyword
+ self.code = gsub(self.code,"%s?virtual(%s)","%1") -- eliminate 'virtual' keyword
+ self.code = gsub(self.code,"public:","") -- eliminate 'public:' keyword
+ self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ self.code = gsub(self.code,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
+
+ -- restore embedded code
+ self.code = gsub(self.code,"%$%[(%d+)%]%$",function (n)
+ return %C[tonumber(n)]
+ end)
+ -- restore verbatim lines
+ self.code = gsub(self.code,"%$(%d+)%$",function (n)
+ return %V[tonumber(n)]
+ end)
+end
+
+-- translate verbatim
+function classPackage:preamble ()
+ output('/*\n')
+ output('** Lua binding: '..self.name..'\n')
+ output('** Generated automatically by '..TOLUA_VERSION..'\n')
+ output('*/\n\n')
+
+ output('#include "lua/tolua.h"\n\n')
+
+ if not flags.h then
+ output('/* Exported function */')
+ output('int tolua_'..self.name..'_open (lua_State* tolua_S);')
+ output('void tolua_'..self.name..'_close (lua_State* tolua_S);')
+ output('\n')
+ end
+
+ local i=1
+ while self[i] do
+ self[i]:preamble()
+ i = i+1
+ end
+ output('\n')
+ output('/* function to register type */')
+ output('static void toluaI_reg_types (lua_State* tolua_S)')
+ output('{')
+ foreach(_usertype,function(n,v) output(' tolua_usertype(tolua_S,"',v,'");') end)
+ output('}')
+ output('\n')
+
+ output('/* error messages */')
+ output('#define TOLUA_ERR_SELF tolua_error(tolua_S,\"invalid \'self\'\")')
+ output('#define TOLUA_ERR_ASSIGN tolua_error(tolua_S,\"#vinvalid type in variable assignment.\")')
+ output('\n')
+end
+
+-- register package
+-- write package open function
+function classPackage:register ()
+ output("/* Open function */")
+ output("int tolua_"..self.name.."_open (lua_State* tolua_S)")
+ output("{")
+ output(" tolua_open(tolua_S);")
+ output(" toluaI_reg_types(tolua_S);")
+ local i=1
+ while self[i] do
+ self[i]:register()
+ i = i+1
+ end
+ output(" return 1;")
+ output("}")
+end
+
+-- unregister package
+-- write package close function
+function classPackage:unregister ()
+ output("/* Close function */")
+ output("void tolua_"..self.name.."_close (lua_State* tolua_S)")
+ output("{")
+ local i=1
+ while self[i] do
+ self[i]:unregister()
+ i = i+1
+ end
+ output("}")
+end
+
+-- write header file
+function classPackage:header ()
+ output('/*\n') output('** Lua binding: '..self.name..'\n')
+ output('** Generated automatically by '..TOLUA_VERSION..'.\n')
+ output('*/\n\n')
+
+ if not flags.h then
+ output('/* Exported function */')
+ output('int tolua_'..self.name..'_open (lua_State* tolua_S);')
+ output('void tolua_'..self.name..'_close (lua_State* tolua_S);')
+ output('\n')
+ end
+end
+
+-- Internal constructor
+function _Package (t)
+ t._base = classPackage
+ settag(t,tolua_tag)
+ return t
+end
+
+-- Constructor
+-- Expects the base file name.
+-- It assumes the file has extension ".pkg".
+function Package (name)
+ -- read file
+ local code = read("*a")
+ code = "\n" .. code -- add sentinel
+ -- deal with include directive
+ local nsubst
+ repeat
+ code,nsubst = gsub(code,"\n%s*%$<(.-)>%s*\n",function (fn)
+ local fp,msg = openfile(fn,'r')
+ if not fp then
+ error('#'..msg..': '..fn)
+ end
+ local s = read(fp,'*a')
+ closefile(fp)
+ return "\n" .. s
+ end)
+ until nsubst==0
+
+ -- deal with include directive for C/C++ header files
+ local nsubst
+ repeat
+ code,nsubst =
+ gsub(code,"\n%s*%${(.-)}%s*\n",
+ function (fn)
+ local fp,msg = openfile(fn,'r')
+ if not fp then
+ error('#'..msg..': '..fn)
+ end
+ local s = read(fp,'*a')
+ closefile(fp)
+ -- extract marked code
+ local T = {code="\n"}
+ s= "\n" .. s .. "\n" -- add blank lines as sentinels
+ -- extract one-line statments
+ gsub(s,"\n(.-)[Tt][Oo][Ll][Uu][Aa]_[Ee][Xx][Pp][Oo][Rr][Tt][^\n]*\n",
+ function (c) %T.code = %T.code .. c .. "\n" end
+ )
+ -- extract multiline statments
+ gsub(s,"\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Bb][Ee][Gg][Ii][Nn][^\n]*"..
+ "(.-)" ..
+ "\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Ee][Nn][Dd][^\n]*\n",
+ function (c) %T.code = %T.code .. c .. "\n" end
+ )
+ return T.code
+ end)
+ until nsubst==0
+
+ local t = _Package(_Container{name=name, code=code})
+ push(t)
+ t:preprocess()
+ t:parse(t.code)
+ pop()
+ return t
+end
+
+
diff --git a/src/lua/print.h b/src/lua/print.h
new file mode 100644
index 00000000..49f4c4cb
--- /dev/null
+++ b/src/lua/print.h
@@ -0,0 +1,55 @@
+/*
+** $Id: print.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+** extracted automatically from lopcodes.h by mkprint.lua -- DO NOT EDIT
+** See Copyright Notice in lua.h
+*/
+
+ case OP_END: P_OP("END"); P_NONE; break;
+ case OP_RETURN: P_OP("RETURN"); P_U; break;
+ case OP_CALL: P_OP("CALL"); P_AB; break;
+ case OP_TAILCALL: P_OP("TAILCALL"); P_AB; break;
+ case OP_PUSHNIL: P_OP("PUSHNIL"); P_U; break;
+ case OP_POP: P_OP("POP"); P_U; break;
+ case OP_PUSHINT: P_OP("PUSHINT"); P_S; break;
+ case OP_PUSHSTRING: P_OP("PUSHSTRING"); P_Q; break;
+ case OP_PUSHNUM: P_OP("PUSHNUM"); P_N; break;
+ case OP_PUSHNEGNUM: P_OP("PUSHNEGNUM"); P_N; break;
+ case OP_PUSHUPVALUE: P_OP("PUSHUPVALUE"); P_U; break;
+ case OP_GETLOCAL: P_OP("GETLOCAL"); P_L; break;
+ case OP_GETGLOBAL: P_OP("GETGLOBAL"); P_K; break;
+ case OP_GETTABLE: P_OP("GETTABLE"); P_NONE; break;
+ case OP_GETDOTTED: P_OP("GETDOTTED"); P_K; break;
+ case OP_GETINDEXED: P_OP("GETINDEXED"); P_L; break;
+ case OP_PUSHSELF: P_OP("PUSHSELF"); P_K; break;
+ case OP_CREATETABLE: P_OP("CREATETABLE"); P_U; break;
+ case OP_SETLOCAL: P_OP("SETLOCAL"); P_L; break;
+ case OP_SETGLOBAL: P_OP("SETGLOBAL"); P_K; break;
+ case OP_SETTABLE: P_OP("SETTABLE"); P_AB; break;
+ case OP_SETLIST: P_OP("SETLIST"); P_AB; break;
+ case OP_SETMAP: P_OP("SETMAP"); P_U; break;
+ case OP_ADD: P_OP("ADD"); P_NONE; break;
+ case OP_ADDI: P_OP("ADDI"); P_S; break;
+ case OP_SUB: P_OP("SUB"); P_NONE; break;
+ case OP_MULT: P_OP("MULT"); P_NONE; break;
+ case OP_DIV: P_OP("DIV"); P_NONE; break;
+ case OP_POW: P_OP("POW"); P_NONE; break;
+ case OP_CONCAT: P_OP("CONCAT"); P_U; break;
+ case OP_MINUS: P_OP("MINUS"); P_NONE; break;
+ case OP_NOT: P_OP("NOT"); P_NONE; break;
+ case OP_JMPNE: P_OP("JMPNE"); P_J; break;
+ case OP_JMPEQ: P_OP("JMPEQ"); P_J; break;
+ case OP_JMPLT: P_OP("JMPLT"); P_J; break;
+ case OP_JMPLE: P_OP("JMPLE"); P_J; break;
+ case OP_JMPGT: P_OP("JMPGT"); P_J; break;
+ case OP_JMPGE: P_OP("JMPGE"); P_J; break;
+ case OP_JMPT: P_OP("JMPT"); P_J; break;
+ case OP_JMPF: P_OP("JMPF"); P_J; break;
+ case OP_JMPONT: P_OP("JMPONT"); P_J; break;
+ case OP_JMPONF: P_OP("JMPONF"); P_J; break;
+ case OP_JMP: P_OP("JMP"); P_J; break;
+ case OP_PUSHNILJMP: P_OP("PUSHNILJMP"); P_NONE; break;
+ case OP_FORPREP: P_OP("FORPREP"); P_J; break;
+ case OP_FORLOOP: P_OP("FORLOOP"); P_J; break;
+ case OP_LFORPREP: P_OP("LFORPREP"); P_J; break;
+ case OP_LFORLOOP: P_OP("LFORLOOP"); P_J; break;
+ case OP_CLOSURE: P_OP("CLOSURE"); P_F; break;
diff --git a/src/lua/tolua.c b/src/lua/tolua.c
new file mode 100644
index 00000000..3cb09291
--- /dev/null
+++ b/src/lua/tolua.c
@@ -0,0 +1,149 @@
+/* tolua
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua.c,v 1.4 2004/06/04 13:42:10 neil Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "lua.h"
+#include "lualib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static void help (void)
+{
+ fprintf(stderr,"\n"
+ "usage: tolua [options] input_file\n"
+ "\n"
+ "Command line options are:\n"
+ " -v : print version information.\n"
+ " -o file : set output file; default is stdout.\n"
+ " -H file : create include file.\n"
+ " -n name : set package name; default is input file root name.\n"
+ " -p : parse only.\n"
+ " -P : parse and print structure information (for debug).\n"
+ " -h : print this message.\n"
+ "Should the input file be omitted, stdin is assumed;\n"
+ "in that case, the package name must be explicitly set.\n\n"
+ );
+}
+
+static void version (void)
+{
+ fprintf(stderr, "%s (written by W. Celes)\n",TOLUA_VERSION);
+}
+
+static void setfield (lua_State* L, int table, char* f, char* v)
+{
+ lua_pushstring(L,f);
+ lua_pushstring(L,v);
+ lua_settable(L,table);
+}
+
+static void error (char* o)
+{
+ fprintf(stderr,"tolua: unknown option '%s'\n",o);
+ help();
+ exit(1);
+}
+
+int main (int argc, char* argv[])
+{
+ lua_State* L = lua_open(0);
+ lua_baselibopen(L);
+ lua_iolibopen(L);
+ lua_strlibopen(L);
+ lua_pushstring(L,TOLUA_VERSION); lua_setglobal(L,"TOLUA_VERSION");
+
+ if (argc==1)
+ {
+ help();
+ return 0;
+ }
+ else
+ {
+ int i, t;
+ lua_newtable(L);
+ lua_pushvalue(L,-1);
+ lua_setglobal(L,"flags");
+ t = lua_gettop(L);
+ for (i=1; i<argc; ++i)
+ {
+ if (*argv[i] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'v': version(); return 0;
+ case 'h': help(); return 0;
+ case 'p': setfield(L,t,"p",""); break;
+ case 'P': setfield(L,t,"P",""); break;
+ case 'o': setfield(L,t,"o",argv[++i]); break;
+ case 'n': setfield(L,t,"n",argv[++i]); break;
+ case 'H': setfield(L,t,"H",argv[++i]); break;
+ default: error(argv[i]); break;
+ }
+ }
+ else
+ {
+ setfield(L,t,"f",argv[i]);
+ break;
+ }
+ }
+ lua_pop(L,1);
+ }
+
+#if 1
+ {
+ int tolua_tolualua_open(lua_State* L);
+ tolua_tolualua_open(L);
+ }
+#else
+ {
+ int i;
+ char* p;
+ char path[BUFSIZ];
+ char* files[] = {
+ "basic.lua",
+ "feature.lua",
+ "verbatim.lua",
+ "code.lua",
+ "typedef.lua",
+ "container.lua",
+ "package.lua",
+ "module.lua",
+ "define.lua",
+ "enumerate.lua",
+ "declaration.lua",
+ "variable.lua",
+ "array.lua",
+ "function.lua",
+ "operator.lua",
+ "class.lua",
+ "clean.lua",
+ "doit.lua",
+ NULL
+ };
+ strcpy(path,argv[0]);
+ p = strrchr(path,'/');
+ p = (p==NULL) ? path : p+1;
+ for (i=0; files[i]; ++i)
+ {
+ sprintf(p,"%s",files[i]);
+ lua_dofile(L,path);
+ }
+ }
+
+#endif
+ return 0;
+}
diff --git a/src/lua/tolua.h b/src/lua/tolua.h
new file mode 100644
index 00000000..b4748f52
--- /dev/null
+++ b/src/lua/tolua.h
@@ -0,0 +1,123 @@
+/* tolua - Support code for Lua bindings.
+** Written by Waldemar Celes (celes@tecgraf.puc-rio.br)
+** TeCGraf/PUC-Rio
+** http://www.tecgraf.puc-rio.br/~celes/tolua
+** Jul 1998
+** $Id: tolua.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef tolua_h
+#define tolua_h
+
+#define TOLUA_VERSION "tolua 4.0a - angband"
+
+
+#include <stdlib.h> /* NULL, malloc, free */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lua.h"
+
+
+/*************************************** Exported functions */
+
+int tolua_open (lua_State* L);
+void tolua_using (lua_State* L, int module);
+void tolua_class (lua_State* L, int derived, int base);
+void tolua_instance (lua_State* L, int instance, int classobj);
+void tolua_foreach (lua_State* L, int lo, int f);
+int tolua_tag (lua_State* L, char* type);
+const char* tolua_type (lua_State* L, int lo);
+int tolua_base (lua_State* L, int lo);
+int tolua_cast (lua_State* L, int lo, char* type);
+void tolua_takeownership (lua_State* L, int lo);
+
+
+
+/*************************************** Support functions for binding code */
+
+#define LUA_VALUE int
+#define LUA_NIL 0 /* TODO */
+/*#define TOLUA_NIL (lua_pushnil(),lua_pop())*/
+
+/* Register functions */
+void tolua_globalvar (lua_State* L, char* name, lua_CFunction get, lua_CFunction set);
+void tolua_globalarray (lua_State* L, char* name, lua_CFunction get, lua_CFunction set);
+void tolua_module (lua_State* L, char* name);
+void tolua_cclass (lua_State* L, char* name, char* base);
+void tolua_function (lua_State* L, char* parent, char* name, lua_CFunction func);
+void tolua_constant (lua_State* L, char* parent, char* name, long value);
+void tolua_tablevar
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set);
+void tolua_tablearray
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set);
+
+
+/* Get and push functions */
+long tolua_getnumber (lua_State* L, int narg, long def);
+const char* tolua_getstring (lua_State* L, int narg, const char* def);
+void* tolua_getuserdata (lua_State* L, int narg, void* def);
+void* tolua_getusertype (lua_State* L, int narg, void* def);
+int tolua_getvalue (lua_State* L, int narg, int def);
+int tolua_getbool (lua_State* L, int narg, int def);
+long tolua_getfieldnumber (lua_State* L, int lo, int index, long def);
+const char* tolua_getfieldstring (lua_State* L, int lo, int index, const char* def);
+void* tolua_getfielduserdata (lua_State* L, int lo, int index, void* def);
+void* tolua_getfieldusertype (lua_State* L, int lo, int index, void* def);
+int tolua_getfieldvalue (lua_State* L, int lo, int index, int def);
+int tolua_getfieldbool (lua_State* L, int lo, int index, int def);
+
+void tolua_pushnumber (lua_State* L, long value);
+void tolua_pushstring (lua_State* L, const char* value);
+void tolua_pushuserdata (lua_State* L, void* value);
+void tolua_pushusertype (lua_State* L, void* value, int tag);
+void tolua_pushvalue (lua_State* L, int lo);
+void tolua_pushbool (lua_State* L, int value);
+void tolua_pushfieldnumber (lua_State* L, int lo, int index, long v);
+void tolua_pushfieldstring (lua_State* L, int lo, int index, char* v);
+void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v);
+void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, int tag);
+void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v);
+void tolua_pushfieldbool (lua_State* L, int lo, int index, int v);
+
+
+/* Type & tag manipulation */
+void tolua_usertype (lua_State* L, char* type);
+#if 0
+void tolua_settag (lua_State* L, char* type, int* tag);
+#endif
+int tolua_istype (lua_State* L, int narg, int tag, int dim);
+int tolua_arrayistype (lua_State* L, int narg, int tag, int dim, int def);
+
+int tolua_isnoobj (lua_State* L, int narg);
+
+/* Tag method manipulation */
+void* tolua_doclone (lua_State* L, void* value, int tag);
+void* tolua_copy (lua_State* L, void* value, unsigned int size);
+
+/* Error handling */
+void tolua_error (lua_State* L, char* msg);
+
+/* Exported variables */
+extern int tolua_tag_nil;
+extern int tolua_tag_number;
+extern int tolua_tag_string;
+extern int tolua_tag_userdata;
+extern int tolua_tag_table;
+extern int tolua_tag_function;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/lua/tolua_bd.c b/src/lua/tolua_bd.c
new file mode 100644
index 00000000..d36a5ce9
--- /dev/null
+++ b/src/lua/tolua_bd.c
@@ -0,0 +1,214 @@
+/*
+** Lua binding: tolua
+** Generated automatically by tolua 4.0b on Tue Nov 14 14:18:50 2000.
+*/
+
+#include "tolua.h"
+
+/* Exported function */
+int tolua_tolua_open (lua_State* tolua_S);
+void tolua_tolua_close (lua_State* tolua_S);
+
+#define tolua_using(module) (tolua_using)(tolua_S,module)
+#define tolua_type(lo) (tolua_type)(tolua_S,lo)
+#define tolua_foreach(lo,f) (tolua_foreach)(tolua_S,lo,f)
+#define tolua_class(derived,base) (tolua_class)(tolua_S,derived,base)
+#define tolua_instance(inst,cobj) (tolua_instance)(tolua_S,inst,cobj)
+#define tolua_base(lo) (tolua_base)(tolua_S,lo)
+#define tolua_cast(lo,type) (tolua_cast)(tolua_S,lo,type)
+#define tolua_takeownership(lo) (tolua_takeownership)(tolua_S,lo)
+
+/* function to register type */
+static void toluaI_reg_types (lua_State* tolua_S)
+{
+}
+
+/* function: tolua_using */
+static int toluaI_tolua_tolua_using00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE module = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ tolua_using(module);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'using'.");
+ return 0;
+}
+
+/* function: tolua_type */
+static int toluaI_tolua_tolua_type00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ char* toluaI_ret = (char*) tolua_type(lo);
+ tolua_pushstring(tolua_S,toluaI_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'type'.");
+ return 0;
+}
+
+/* function: tolua_foreach */
+static int toluaI_tolua_tolua_foreach00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ LUA_VALUE f = ((LUA_VALUE) tolua_getvalue(tolua_S,2,0));
+ {
+ tolua_foreach(lo,f);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'foreach'.");
+ return 0;
+}
+
+/* function: tolua_class */
+static int toluaI_tolua_tolua_class00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE derived = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ LUA_VALUE base = ((LUA_VALUE) tolua_getvalue(tolua_S,2,0));
+ {
+ tolua_class(derived,base);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'class'.");
+ return 0;
+}
+
+/* function: tolua_instance */
+static int toluaI_tolua_tolua_instance00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE instance = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ LUA_VALUE classobj = ((LUA_VALUE) tolua_getvalue(tolua_S,2,0));
+ {
+ tolua_instance(instance,classobj);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'instance'.");
+ return 0;
+}
+
+/* function: tolua_base */
+static int toluaI_tolua_tolua_base00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ LUA_VALUE toluaI_ret = (LUA_VALUE) tolua_base(lo);
+ tolua_pushvalue(tolua_S,toluaI_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'base'.");
+ return 0;
+}
+
+/* function: tolua_cast */
+static int toluaI_tolua_tolua_cast00(lua_State* tolua_S)
+{
+ if (
+ !tolua_istype(tolua_S,2,LUA_TSTRING,0) ||
+ !tolua_isnoobj(tolua_S,3)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ char* type = ((char*) tolua_getstring(tolua_S,2,0));
+ {
+ LUA_VALUE toluaI_ret = (LUA_VALUE) tolua_cast(lo,type);
+ tolua_pushvalue(tolua_S,toluaI_ret);
+ }
+ }
+ return 1;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'cast'.");
+ return 0;
+}
+
+/* function: tolua_takeownership */
+static int toluaI_tolua_tolua_takeownership00(lua_State* tolua_S)
+{
+ if (
+ !tolua_isnoobj(tolua_S,2)
+ )
+ goto tolua_lerror;
+ else
+ {
+ LUA_VALUE lo = ((LUA_VALUE) tolua_getvalue(tolua_S,1,0));
+ {
+ tolua_takeownership(lo);
+ }
+ }
+ return 0;
+tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'takeownership'.");
+ return 0;
+}
+
+/* Open function */
+int tolua_tolua_open (lua_State* tolua_S)
+{
+ tolua_open(tolua_S);
+ toluaI_reg_types(tolua_S);
+ tolua_module(tolua_S,"tolua");
+ tolua_function(tolua_S,"tolua","using",toluaI_tolua_tolua_using00);
+ tolua_function(tolua_S,"tolua","type",toluaI_tolua_tolua_type00);
+ tolua_function(tolua_S,"tolua","foreach",toluaI_tolua_tolua_foreach00);
+ tolua_function(tolua_S,"tolua","class",toluaI_tolua_tolua_class00);
+ tolua_function(tolua_S,"tolua","instance",toluaI_tolua_tolua_instance00);
+ tolua_function(tolua_S,"tolua","base",toluaI_tolua_tolua_base00);
+ tolua_function(tolua_S,"tolua","cast",toluaI_tolua_tolua_cast00);
+ tolua_function(tolua_S,"tolua","takeownership",toluaI_tolua_tolua_takeownership00);
+ return 1;
+}
+/* Close function */
+void tolua_tolua_close (lua_State* tolua_S)
+{
+ lua_pushnil(tolua_S); lua_setglobal(tolua_S,"tolua");
+}
diff --git a/src/lua/tolua_eh.c b/src/lua/tolua_eh.c
new file mode 100644
index 00000000..0709cb4c
--- /dev/null
+++ b/src/lua/tolua_eh.c
@@ -0,0 +1,66 @@
+/* tolua: error handling
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_eh.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_eh.h"
+#include "tolua_rg.h"
+
+#include <stdio.h>
+
+/* registry fiels used to hold current error info
+ - tolua_err_narg: number of wrong argument
+ - tolua_err_provided: provided type
+ - tolua_err_expected: expected type
+*/
+
+void toluaI_eh_set
+(lua_State* L, int narg, const char* provided, const char* expected)
+{
+ lua_pushnumber(L,narg);
+ toluaI_setregistry(L,"tolua_err_narg");
+ lua_pushstring(L,provided);
+ toluaI_setregistry(L,"tolua_err_provided");
+ lua_pushstring(L,expected);
+ toluaI_setregistry(L,"tolua_err_expected");
+}
+
+void tolua_error (lua_State* L, char* msg)
+{
+ if (msg[0]=='#')
+ {
+ static char buffer[BUFSIZ];
+ const char* err_provided;
+ const char* err_expected;
+ toluaI_getregistry(L,"tolua_err_provided");
+ err_provided = lua_tostring(L,-1);
+ toluaI_getregistry(L,"tolua_err_expected");
+ err_expected = lua_tostring(L,-1);
+ lua_pop(L,2);
+ if (msg[1]=='f')
+ {
+ int err_narg;
+ toluaI_getregistry(L,"tolua_err_narg");
+ err_narg = (int)lua_tonumber(L,-1);
+ lua_pop(L,1);
+ sprintf(buffer,"%s\n argument #%d is '%s'; '%s' expected.\n",
+ msg+2,err_narg,err_provided,err_expected);
+ }
+ else if (msg[1]=='v')
+ sprintf(buffer,"%s\n value is '%s'; '%s' expected.\n",
+ msg+2,err_provided,err_expected);
+ lua_error(L,buffer);
+ }
+ else
+ lua_error(L,msg);
+}
diff --git a/src/lua/tolua_eh.h b/src/lua/tolua_eh.h
new file mode 100644
index 00000000..168ba122
--- /dev/null
+++ b/src/lua/tolua_eh.h
@@ -0,0 +1,24 @@
+/* tolua: error handling
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_eh.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+
+#ifndef tolua_eh_h
+#define tolua_eh_h
+
+void toluaI_eh_set
+(lua_State* L, int narg, const char* provided, const char* expected);
+
+
+#endif
diff --git a/src/lua/tolua_gp.c b/src/lua/tolua_gp.c
new file mode 100644
index 00000000..77ff0c26
--- /dev/null
+++ b/src/lua/tolua_gp.c
@@ -0,0 +1,197 @@
+/* tolua: get & push functions.
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_gp.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_tm.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+long tolua_getnumber (lua_State* L, int narg, long def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_tonumber(L,narg);
+}
+
+const char* tolua_getstring (lua_State* L, int narg, const char* def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_tostring(L,narg);
+}
+
+void* tolua_getuserdata (lua_State* L, int narg, void* def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_touserdata(L,narg);
+}
+
+void* tolua_getusertype (lua_State* L, int narg, void* def)
+{
+ return lua_gettop(L)<abs(narg) ? def : lua_touserdata(L,narg);
+}
+
+int tolua_getvalue (lua_State* L, int narg, int def)
+{
+ return lua_gettop(L)<abs(narg) ? def : narg;
+}
+
+int tolua_getbool (lua_State* L, int narg, int def)
+{
+ return lua_gettop(L)<abs(narg) ?
+ def :
+ lua_isnil(L,narg) ? 0 : lua_tonumber(L,narg)!=0;
+}
+
+long tolua_getfieldnumber (lua_State* L, int lo, int index, long def)
+{
+ long v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_tonumber(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+const char* tolua_getfieldstring
+(lua_State* L, int lo, int index, const char* def)
+{
+ const char* v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_tostring(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+void* tolua_getfielduserdata (lua_State* L, int lo, int index, void* def)
+{
+ void* v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_touserdata(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+void* tolua_getfieldusertype (lua_State* L, int lo, int index, void* def)
+{
+ void* v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lua_touserdata(L,-1);
+ lua_pop(L,1);
+ return v;
+}
+
+int tolua_getfieldvalue (lua_State* L, int lo, int index, int def)
+{
+ int v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? def : lo;
+ lua_pop(L,1);
+ return v;
+}
+
+int tolua_getfieldbool (lua_State* L, int lo, int index, int def)
+{
+ int v;
+ lua_pushnumber(L,index);
+ lua_gettable(L,lo);
+ v = lua_isnil(L,-1) ? 0 : lua_tonumber(L,-1)!=0;
+ lua_pop(L,1);
+ return v;
+}
+
+void tolua_pushnumber (lua_State* L, long value)
+{
+ lua_pushnumber(L,value);
+}
+
+void tolua_pushstring (lua_State* L, const char* value)
+{
+ if (value == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushstring(L,value);
+}
+
+void tolua_pushuserdata (lua_State* L, void* value)
+{
+ if (value == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushuserdata(L,value);
+}
+
+void tolua_pushusertype (lua_State* L, void* value, int tag)
+{
+ if (value == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushusertag(L,value,tag);
+}
+
+void tolua_pushvalue (lua_State* L, int lo)
+{
+ lua_pushvalue(L,lo);
+}
+
+void tolua_pushbool (lua_State* L, int value)
+{
+ if (value)
+ lua_pushnumber(L,(long)value);
+ else
+ lua_pushnil(L);
+}
+
+void tolua_pushfieldnumber (lua_State* L, int lo, int index, long v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushnumber(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldstring (lua_State* L, int lo, int index, char* v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushstring(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushuserdata(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, int tag)
+{
+ lua_pushnumber(L,index);
+ tolua_pushusertype(L,v,tag);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v)
+{
+ lua_pushnumber(L,index);
+ lua_pushvalue(L,v);
+ lua_settable(L,lo);
+}
+
+void tolua_pushfieldbool (lua_State* L, int lo, int index, int v)
+{
+ lua_pushnumber(L,index);
+ tolua_pushbool(L,v);
+ lua_settable(L,lo);
+}
+
diff --git a/src/lua/tolua_lb.c b/src/lua/tolua_lb.c
new file mode 100644
index 00000000..5fd4c337
--- /dev/null
+++ b/src/lua/tolua_lb.c
@@ -0,0 +1,160 @@
+/* tolua
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_lb.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_rg.h"
+#include "tolua_tm.h"
+#include "tolua_tt.h"
+
+
+int tolua_open (lua_State* L)
+{
+ int tolua_tolua_open (lua_State* L);
+ /* check if alread opened */
+ toluaI_getregistry(L,"TOLUA");
+ if (lua_isnil(L,-1))
+ {
+ lua_pushnumber(L,1);
+ toluaI_setregistry(L,"TOLUA");
+ toluaI_tt_init(L);
+ toluaI_tm_init(L);
+ lua_getglobal(L,"foreach");
+ toluaI_setregistry(L,"tolua_orig_foreach");
+ tolua_tolua_open(L); /* opens tolua own binding */
+ }
+ lua_pop(L,1);
+ return 1;
+}
+
+void tolua_using (lua_State* L, int module)
+{
+ toluaI_tm_using(L,module);
+}
+
+void tolua_class (lua_State* L, int derived, int base)
+{
+ int tag = lua_newtag(L); /* new tag of instances of that class */
+ toluaI_tm_setclass(L,derived);
+ toluaI_tm_linstance(L,tag,derived);
+ lua_pushvalue(L,derived);
+ lua_pushstring(L,".base");
+ lua_pushvalue(L,base);
+ lua_rawset(L,-3);
+ lua_pushstring(L,".itag");
+ lua_pushnumber(L,tag);
+ lua_rawset(L,-3);
+ lua_pop(L,1);
+}
+
+void tolua_instance (lua_State* L, int instance, int classobj)
+{
+ int tag;
+ lua_pushvalue(L,classobj);
+ lua_pushstring(L,".itag");
+ lua_gettable(L,-2);
+ tag = (int) lua_tonumber(L,-1);
+ lua_pop(L,2); /* number and table */
+ if (tag==0)
+ tolua_error(L,"unregistered 'classobj' in function 'tolua_instance'.");
+ lua_pushvalue(L,instance);
+ lua_settag(L,tag);
+ lua_pop(L,1);
+}
+
+static int filter (lua_State* L)
+{
+ int n = 1; /* name */
+ int v = 2; /* value */
+ int f = lua_gettop(L); /* function */
+ /* do not pass string fields starting with a dot */
+ if (!lua_isstring(L,n) || *lua_tostring(L,n)!='.')
+ {
+ lua_pushvalue(L,f);
+ lua_pushvalue(L,n);
+ lua_pushvalue(L,v);
+ lua_call(L,2,1);
+ }
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+void tolua_foreach (lua_State* L, int lo, int f)
+{
+ if (toluaI_tt_isusertype(L,lo))
+ {
+ toluaI_tm_pushmate(L,lo);
+ if (lua_isnil(L,-1))
+ return; /* no field in mate table */
+ else
+ lo = lua_gettop(L);
+ }
+ toluaI_getregistry(L,"tolua_orig_foreach");
+ lua_pushvalue(L,lo);
+ lua_pushvalue(L,f);
+ lua_pushcclosure(L,filter,1);
+ lua_call(L,2,1);
+}
+
+const char* tolua_type (lua_State* L, int lo)
+{
+ return toluaI_tt_getobjtype(L,lo);
+}
+
+int tolua_tag (lua_State* L, char* type)
+{
+ return toluaI_tt_gettag(L,type);
+}
+
+int tolua_base (lua_State* L, int lo)
+{
+ if (toluaI_tt_isusertype(L,lo))
+ {
+ toluaI_tm_pushclass(L,lo);
+ return lua_gettop(L);
+ }
+ else if (lua_istable(L,lo))
+ {
+ lua_pushvalue(L,lo);
+ lua_pushstring(L,".base");
+ lua_rawget(L,-2);
+ return -1;
+ }
+ else
+ return 0;
+}
+
+int tolua_cast (lua_State* L, int lo, char* type)
+{
+ if (lua_isuserdata(L,lo))
+ {
+ tolua_pushusertype(L,lua_touserdata(L,lo),toluaI_tt_gettag(L,type));
+ return -1;
+ }
+ else
+ return 0;
+}
+
+void tolua_takeownership (lua_State* L, int lo)
+{
+ if (toluaI_tt_isusertype(L,lo))
+ {
+ /* force garbage collection to avoid C to reuse a to-be-collected address */
+ lua_setgcthreshold(L,0);
+ tolua_doclone(L,lua_touserdata(L,lo),lua_tag(L,lo));
+ }
+ else
+ tolua_error(L,"cannot take ownership of specified obejct.");
+}
+
diff --git a/src/lua/tolua_rg.c b/src/lua/tolua_rg.c
new file mode 100644
index 00000000..4337e9f9
--- /dev/null
+++ b/src/lua/tolua_rg.c
@@ -0,0 +1,243 @@
+/* tolua: register functions
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_rg.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include <stdio.h>
+
+#include "tolua.h"
+#include "tolua_rg.h"
+#include "tolua_tm.h"
+#include "tolua_tt.h"
+
+void tolua_globalvar (lua_State* L, char* name, lua_CFunction get, lua_CFunction set)
+{
+ lua_newtable(L);
+ lua_pushstring(L,".get");
+ lua_pushcfunction(L,get);
+ lua_settable(L,-3);
+ if (set)
+ {
+ lua_pushstring(L,".set");
+ lua_pushcfunction(L,set);
+ lua_settable(L,-3);
+ }
+ lua_pushvalue(L,-1); /* duplicate top */
+ lua_setglobal(L,name);
+ toluaI_tm_global(L,lua_gettop(L));
+ lua_pop(L,1);
+}
+
+static int toluaI_const_global_array (lua_State* L)
+{
+ lua_error(L,"value of const array cannot be changed");
+ return 0;
+}
+
+
+void tolua_globalarray (lua_State* L,char* name, lua_CFunction get, lua_CFunction set)
+{
+ int tag = lua_newtag(L);
+ lua_newtable(L);
+ lua_settag(L,tag);
+ lua_setglobal(L,name);
+
+ lua_pushcfunction(L,get);
+ lua_settagmethod(L,tag,"gettable");
+ if (set)
+ lua_pushcfunction(L,set);
+ else
+ lua_pushcfunction(L,toluaI_const_global_array);
+ lua_settagmethod(L,tag,"settable");
+}
+
+void tolua_tablevar
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set)
+{
+ lua_getglobal(L,table);
+
+ lua_pushstring(L,".get");
+ lua_gettable(L,-2);
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,get);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ if (set)
+ {
+ lua_pushstring(L,".set");
+ lua_gettable(L,-2);
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,set);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+
+ lua_pop(L,1);
+}
+
+static int toluaI_get_array (lua_State* L)
+{
+ void* self = tolua_getuserdata(L,1,0);
+ const char* field = tolua_getstring(L,2,0);
+
+ if (!field)
+ tolua_error(L,"invalid 'field' in accessing array");
+ if (!self)
+ {
+ static char msg[BUFSIZ];
+ sprintf(msg,"invalid 'self' in accessing array '%s'",field);
+ tolua_error(L,msg);
+ }
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,lua_tag(L,1));
+ lua_gettable(L,-2);
+ lua_getglobal(L,lua_tostring(L,-1));
+ lua_pushstring(L,".array");
+ lua_gettable(L,-2);
+ lua_pushvalue(L,2); /* field */
+ lua_gettable(L,-2);
+ lua_pushstring(L,".self");
+ lua_pushvalue(L,1); /* self */
+ lua_rawset(L,-3);
+ return 1;
+}
+
+static int toluaI_const_array (lua_State* L)
+{
+ lua_error(L,"value of const field cannot be changed");
+ return 0;
+}
+
+void tolua_tablearray
+(lua_State* L, char* table, char* name, lua_CFunction get, lua_CFunction set)
+{
+ int tag = lua_newtag(L);
+ lua_getglobal(L,table);
+ lua_pushstring(L,".array");
+ lua_rawget(L,-2);
+ lua_pushstring(L,name);
+ lua_newtable(L);
+ lua_settag(L,tag);
+ lua_settable(L,-3);
+ lua_pop(L,2);
+
+ lua_pushcfunction(L,get);
+ lua_settagmethod(L,tag,"gettable");
+ if (set)
+ lua_pushcfunction(L,set);
+ else
+ lua_pushcfunction(L,toluaI_const_array);
+ lua_settagmethod(L,tag,"settable");
+
+ tolua_tablevar(L,table,name,toluaI_get_array,NULL);
+}
+
+void tolua_module (lua_State* L, char* name)
+{
+ lua_getglobal(L,name);
+ if (!lua_istable(L,-1))
+ {
+ lua_newtable(L);
+ lua_pushstring(L,".get");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushstring(L,".set");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushvalue(L,-1); /* duplicate top */
+ lua_setglobal(L,name);
+ toluaI_tm_module(L,lua_gettop(L));
+ lua_pop(L,1);
+ }
+ lua_pop(L,1);
+}
+
+void tolua_cclass (lua_State* L, char* name, char* base)
+{
+ int t;
+ lua_newtable(L);
+ lua_pushstring(L,".get");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushstring(L,".set");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ lua_pushstring(L,".array");
+ lua_newtable(L);
+ lua_settable(L,-3);
+ if (*base != 0)
+ {
+ lua_pushstring(L,".base");
+ lua_getglobal(L,base);
+ lua_rawset(L,-3);
+ }
+ lua_pushvalue(L,-1); /* duplicate top */
+ lua_setglobal(L,name);
+ t = lua_gettop(L);
+ toluaI_tm_class(L,t,name);
+ toluaI_tt_class(L,t,name,base);
+ lua_pop(L,1);
+}
+
+
+void tolua_function (lua_State* L, char* parent, char* name, lua_CFunction func)
+{
+ if (parent==NULL)
+ {
+ lua_pushcfunction(L,func);
+ lua_setglobal(L,name);
+ }
+ else
+ {
+ lua_getglobal(L,parent);
+ lua_pushstring(L,name);
+ lua_pushcfunction(L,func);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+}
+
+void tolua_constant (lua_State* L, char* parent, char* name, long value)
+{
+ if (parent==NULL)
+ {
+ lua_pushnumber(L,value);
+ lua_setglobal(L,name);
+ }
+ else
+ {
+ lua_getglobal(L,parent);
+ lua_pushstring(L,name);
+ lua_pushnumber(L,value);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+}
+
+void toluaI_setregistry (lua_State* L, char* field)
+{
+ lua_getregistry(L);
+ lua_insert(L,-2);
+ lua_pushstring(L,field);
+ lua_insert(L,-2);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+}
+
+void toluaI_getregistry (lua_State* L, char* field)
+{
+ lua_getregistry(L);
+ lua_pushstring(L,field);
+ lua_gettable(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1);
+}
diff --git a/src/lua/tolua_rg.h b/src/lua/tolua_rg.h
new file mode 100644
index 00000000..0feb6078
--- /dev/null
+++ b/src/lua/tolua_rg.h
@@ -0,0 +1,22 @@
+/* tolua: register functions
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Nov 200
+** $Id: tolua_rg.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef tolua_rg_h
+#define tolua_rg_h
+
+void toluaI_setregistry (lua_State* L, char* field);
+void toluaI_getregistry (lua_State* L, char* field);
+
+#endif
diff --git a/src/lua/tolua_tm.c b/src/lua/tolua_tm.c
new file mode 100644
index 00000000..8fd7b28d
--- /dev/null
+++ b/src/lua/tolua_tm.c
@@ -0,0 +1,585 @@
+/* tolua: tag methods
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tm.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_tm.h"
+#include "tolua_tt.h"
+#include "tolua_rg.h"
+
+#include <string.h>
+
+
+
+/* Global tables created in Lua registry:
+ tolua_tbl_class: indexed by instance tags, stores the class tables.
+ tolua_tbl_clone: indexed by memory address, stores the tag indicanting
+ it is a clone.
+ tolua_tbl_mate: indexed by memory address, stores the associate instance
+ table.
+
+ General tags stored in Lua registry:
+ tolua_tag_global;
+ tolua_tag_module;
+ tolua_tag_class;
+ tolua_tag_instance;
+ tolua_tag_linstance;
+ tolua_tag_indirect;
+*/
+
+/* internal function prototype */
+static void setmethods (lua_State* L);
+
+static void settag (lua_State* L, int lo, char* tag_registry_field)
+{
+ toluaI_getregistry(L,tag_registry_field);
+ lua_pushvalue(L,lo);
+ lua_settag(L,(int)lua_tonumber(L,-2));
+ lua_pop(L,2);
+}
+
+void toluaI_tm_global (lua_State* L, int lo)
+{
+ settag(L,lo,"tolua_tag_global");
+}
+
+void toluaI_tm_module (lua_State* L, int lo)
+{
+ settag(L,lo,"tolua_tag_module");
+}
+
+void toluaI_tm_setclass (lua_State* L, int lo)
+{
+ settag(L,lo,"tolua_tag_class");
+}
+
+void toluaI_tm_class (lua_State* L, int lo, char* name)
+{
+ int tag_class;
+ int tag = lua_newtag(L);
+ char* type = toluaI_tt_concat("class ",name);
+ toluaI_getregistry(L,"tolua_tag_class");
+ tag_class = (int)lua_tonumber(L,-1);
+ lua_copytagmethods(L,tag,tag_class);
+ toluaI_tt_register(L,tag,type);
+ toluaI_tt_sethierarchy(L,tag,tag_class);
+ lua_pushvalue(L,lo);
+ lua_settag(L,tag);
+ lua_pop(L,2); /* tag_class and lo */
+}
+
+void toluaI_tm_instance (lua_State* L, int tag, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,tag);
+ lua_pushvalue(L,lo);
+ lua_settable(L,-3);
+ toluaI_getregistry(L,"tolua_tag_instance");
+ lua_copytagmethods(L,tag,(int)lua_tonumber(L,-1));
+ lua_pop(L,2); /* tbl_class and tag_instance */
+}
+
+void toluaI_tm_linstance (lua_State* L, int tag, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,tag);
+ lua_pushvalue(L,lo);
+ lua_settable(L,-3);
+ toluaI_getregistry(L,"tolua_tag_linstance");
+ lua_copytagmethods(L,tag,(int)lua_tonumber(L,-1));
+ lua_pop(L,2); /* tbl_class and tag_linstance */
+}
+
+void* tolua_doclone (lua_State* L, void* value, int tag)
+{
+ toluaI_getregistry(L,"tolua_tbl_clone");
+ lua_pushuserdata(L,value);
+ lua_pushnumber(L,tag);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ return value;
+}
+
+void* tolua_copy (lua_State* L, void* value, unsigned int size)
+{
+ void* clone = (void*)malloc(size);
+ if (clone)
+ memcpy(clone,value,size);
+ else
+ tolua_error(L,"insuficient memory");
+ return clone;
+}
+
+static void toluaI_tm_undoclone (lua_State* L, int tag, void* clone)
+{
+ toluaI_getregistry(L,"tolua_tbl_clone");
+ lua_pushuserdata(L,clone);
+ lua_gettable(L,-2);
+ if (lua_isnumber(L,-1) && lua_tonumber(L,-1)==tag)
+ {
+ lua_pushuserdata(L,clone);
+ lua_pushnil(L);
+ lua_settable(L,-4);
+
+ /* get base class */
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,tag);
+ lua_rawget(L,-2);
+
+ /* look for destructor */
+ lua_pushstring(L,"delete");
+ lua_gettable(L,-2);
+ if (lua_iscfunction(L,-1))
+ {
+ lua_pushusertag(L,clone,tag);
+ lua_call(L,1,0);
+ }
+ else
+ {
+ free(clone); /* no destructor: use raw free */
+ lua_pop(L,1); /* the nil function value */
+ }
+ lua_pop(L,2); /* tbl_class and class method table */
+ }
+ lua_pop(L,2); /* table and value */
+}
+
+void toluaI_tm_pushmate (lua_State* L, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_mate");
+ lua_pushvalue(L,lo);
+ lua_rawget(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1);
+}
+
+void toluaI_tm_pushclass (lua_State* L, int lo)
+{
+ toluaI_getregistry(L,"tolua_tbl_class");
+ lua_pushnumber(L,lua_tag(L,lo));
+ lua_rawget(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1);
+}
+
+int toluaI_gettag (lua_State* L, char* tagname)
+{
+ int tag;
+ toluaI_getregistry(L,tagname);
+ tag = (int)lua_tonumber(L,-1);
+ lua_pop(L,1);
+ return tag;
+}
+
+void toluaI_tm_init (lua_State* L)
+{
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_class");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_clone");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_mate");
+
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_global");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_module");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_class");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_instance");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_linstance");
+ lua_pushnumber(L,lua_newtag(L)); toluaI_setregistry(L,"tolua_tag_indirect");
+
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_global"),"generic variable");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_module"),"generic module");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_class"),"generic class");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_indirect"),"generic indirect");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_instance"),"generic instance");
+ toluaI_tt_register(L,toluaI_gettag(L,"tolua_tag_linstance"),"generic lua instance");
+
+ /* allows modules and classes to be used as ordinary tables */
+ toluaI_tt_sethierarchy(L,toluaI_gettag(L,"tolua_tag_module"),tolua_tag_table);
+ toluaI_tt_sethierarchy(L,toluaI_gettag(L,"tolua_tag_class"),tolua_tag_table);
+
+ setmethods(L);
+}
+
+static int map (lua_State* L)
+{
+ int m = lua_gettop(L);
+ /* do not pass string fields starting with a dot */
+ if (!lua_isstring(L,1) || *lua_tostring(L,1)!='.')
+ {
+ lua_getglobals(L);
+ lua_pushvalue(L,1);
+ lua_pushvalue(L,m);
+ lua_rawset(L,-3);
+ lua_pop(L,1);
+ }
+ return 0;
+}
+
+void toluaI_tm_using (lua_State* L, int module)
+{
+ lua_newtable(L);
+ lua_settag(L,toluaI_gettag(L,"tolua_tag_indirect"));
+ lua_pushstring(L,".module");
+ lua_pushvalue(L,module);
+ lua_settable(L,-3);
+
+ lua_getglobal(L,"foreach");
+ lua_pushvalue(L,module);
+ lua_pushvalue(L,-3);
+ lua_pushcclosure(L,map,1);
+ lua_call(L,2,0);
+
+ lua_getglobal(L,"foreach");
+ lua_pushvalue(L,module);
+ lua_pushstring(L,".get");
+ lua_gettable(L,-2);
+ lua_insert(L,-2);
+ lua_pop(L,1); /* module table */
+ lua_pushvalue(L,-3);
+ lua_pushcclosure(L,map,1);
+ lua_call(L,2,0);
+ lua_pop(L,1); /* indirect table */
+}
+
+/********************************************************** tag methods */
+
+/* tag methods coded in C */
+
+/* generic gettable */
+static void oo_gettable (lua_State* L, int table, int base, int index)
+{
+ while (lua_istable(L,base))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ return; /* returned value already on the top */
+ else if (lua_isnumber(L,index))
+ {
+ lua_pushstring(L,"operator_get");
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,index);
+ lua_call(L,2,1);
+ return;
+ }
+ }
+ else
+ {
+ lua_pushstring(L,".get");
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,index); /* need to access array field (?) */
+ lua_call(L,2,1);
+ return;
+ }
+ }
+ }
+ lua_pushstring(L,".base"); lua_rawget(L,base);
+ base = lua_gettop(L);
+ }
+ lua_pushnil(L);
+}
+
+/* generic settable */
+static int oo_settable (lua_State* L, int table, int base, int index, int value)
+{
+ while (lua_istable(L,base))
+ {
+ lua_pushstring(L,".set");
+ lua_rawget(L,base);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,value);
+ lua_call(L,2,0);
+ return 1;
+ }
+ }
+ lua_pushstring(L,".base"); lua_rawget(L,base);
+ base = lua_gettop(L);
+ }
+ return 0;
+}
+
+/* class tag methods */
+static int class_index (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ oo_gettable(L,table,table,index);
+ return 1;
+}
+static int class_settable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ int value = 3;
+ if (oo_settable(L,table,table,index,value) == 0)
+ {
+ lua_pushvalue(L,table);
+ lua_pushvalue(L,index);
+ lua_pushvalue(L,value);
+ lua_rawset(L,-3);
+ }
+ return 0;
+}
+
+/* instance tags */
+static int instance_gettable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ toluaI_tm_pushmate(L,table); /* pushes mate */
+ if (!lua_isnil(L,-1)) /* if there's a mate table */
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1)) /* if field in mate table exists */
+ return 1;
+ }
+ toluaI_tm_pushclass(L,table); /* pushes base */
+ oo_gettable(L,table,lua_gettop(L),index);
+ return 1;
+}
+static int instance_settable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ int value = 3;
+ toluaI_tm_pushclass(L,table); /* pushes base */
+ if (lua_isnumber(L,index))
+ {
+ lua_pushstring(L,"operator_set");
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {/* the stack here is: table,index,value,base,operator */
+ /* call operator passing table, index, and value */
+ lua_insert(L,1);
+ lua_pop(L,1); /* base */
+ lua_call(L,3,0);
+ return 0;
+ }
+ }
+ if (oo_settable(L,table,4,index,value) == 0)
+ {
+ toluaI_tm_pushmate(L,table); /* pushes mate */
+ if (lua_isnil(L,-1))
+ {
+ /* creates mate table */
+ lua_newtable(L);
+ toluaI_getregistry(L,"tolua_tbl_mate");
+ lua_pushvalue(L,table); /* that is the userdata */
+ lua_pushvalue(L,-3);
+ lua_rawset(L,-3);
+ lua_pop(L,1); /* tbl_mate */
+ }
+ /* the mate table is on the top */
+ lua_pushvalue(L,index);
+ lua_pushvalue(L,value);
+ lua_rawset(L,-3);
+ }
+ return 0;
+}
+static int instance_gc (lua_State* L)
+{
+ toluaI_tm_undoclone(L,lua_tag(L,1),lua_touserdata(L,1));
+ return 0;
+}
+static int gen_operator (lua_State* L)
+{
+ int op1 = 1;
+ int op2 = 2;
+ int event = 3;
+ char* name = toluaI_tt_concat("operator_",lua_tostring(L,event));
+ lua_pushstring(L,name);
+ lua_gettable(L,op1);
+ lua_pushvalue(L,op1);
+ lua_pushvalue(L,op2);
+ lua_call(L,2,1);
+ return 1;
+}
+static int instance_operator (lua_State* L)
+{
+ return gen_operator(L);
+}
+static int instance_relational (lua_State* L)
+{
+ gen_operator(L);
+ if ((int)lua_tonumber(L,-1)==0) lua_pushnil(L);
+ return 1;
+}
+
+/* lua instance tags */
+static int linstance_index (lua_State* L)
+{
+ toluaI_tm_pushclass(L,1);
+ oo_gettable(L,1,3,2); /* table,base,index */
+ return 1;
+}
+
+
+/* module tag methods */
+static int module_index (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ lua_pushstring(L,".get");
+ lua_rawget(L,table);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_call(L,0,1);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+static int module_settable (lua_State* L)
+{
+ int table = 1;
+ int index = 2;
+ int value = 3;
+ lua_pushstring(L,".set");
+ lua_rawget(L,table);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,index);
+ lua_rawget(L,-2);
+ if (!lua_isnil(L,-1))
+ {
+ lua_pushvalue(L,value);
+ lua_call(L,1,0);
+ return 0;
+ }
+ }
+ lua_pushvalue(L,index);
+ lua_pushvalue(L,value);
+ lua_rawset(L,table);
+ return 0;
+}
+
+/* global variable tag methods */
+static int global_getglobal (lua_State* L)
+{
+ int value = 2;
+ lua_pushstring(L,".get");
+ lua_rawget(L,value);
+ lua_call(L,0,1);
+ return 1;
+}
+static int global_setglobal (lua_State* L)
+{
+ int value = 2;
+ int newvalue = 3;
+ lua_pushstring(L,".set");
+ lua_rawget(L,value);
+ if (lua_isnil(L,-1))
+ lua_error(L,"value of const variable cannot be changed");
+ else
+ {
+ lua_pushvalue(L,newvalue);
+ lua_call(L,1,0);
+ }
+ return 0;
+}
+
+/* indirect tag methods */
+static int indirect_getglobal (lua_State* L)
+{
+ int varname = 1;
+ int value = 2;
+ lua_pushstring(L,".module");
+ lua_gettable(L,value);
+ lua_pushvalue(L,varname);
+ lua_gettable(L,-2);
+ return 1;
+}
+static int indirect_setglobal (lua_State* L)
+{
+ int varname = 1;
+ int value = 2;
+ int newvalue = 3;
+ lua_pushstring(L,".module");
+ lua_gettable(L,value);
+ lua_pushvalue(L,varname);
+ lua_pushvalue(L,newvalue);
+ lua_settable(L,-3);
+ return 0;
+}
+
+static void setmethods (lua_State* L)
+{
+ /* global variable */
+ lua_pushcfunction(L,global_getglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_global"),"getglobal");
+ lua_pushcfunction(L,global_setglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_global"),"setglobal");
+
+ /* module */
+ lua_pushcfunction(L,module_index);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_module"),"index");
+ lua_pushcfunction(L,module_settable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_module"),"settable");
+
+ /* class */
+ lua_pushcfunction(L,class_index);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_class"),"index");
+ lua_pushcfunction(L,class_settable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_class"),"settable");
+
+ /* instance */
+ lua_pushcfunction(L,instance_gettable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"gettable");
+ lua_pushcfunction(L,instance_settable);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"settable");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"add");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"sub");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"mul");
+ lua_pushcfunction(L,instance_operator);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"div");
+ lua_pushcfunction(L,instance_relational);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"lt");
+ lua_pushcfunction(L,instance_gc);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_instance"),"gc");
+
+ /* lua instance */
+ lua_pushcfunction(L,linstance_index);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_linstance"),"index");
+
+ /* indirect */
+ lua_pushcfunction(L,indirect_getglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_indirect"),"getglobal");
+ lua_pushcfunction(L,indirect_setglobal);
+ lua_settagmethod(L,toluaI_gettag(L,"tolua_tag_indirect"),"setglobal");
+}
+
+
+
diff --git a/src/lua/tolua_tm.h b/src/lua/tolua_tm.h
new file mode 100644
index 00000000..c1bf06dc
--- /dev/null
+++ b/src/lua/tolua_tm.h
@@ -0,0 +1,32 @@
+/* tolua: tag methods
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tm.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+
+#ifndef tolua_tm_h
+#define tolua_tm_h
+
+void toluaI_tm_init (lua_State* L);
+void toluaI_tm_global (lua_State* L, int lo);
+void toluaI_tm_module (lua_State* L, int lo);
+void toluaI_tm_class (lua_State* L, int lo, char* name);
+void toluaI_tm_instance (lua_State* L, int tag, int lo);
+void toluaI_tm_linstance (lua_State* L, int tag, int lo);
+void toluaI_tm_using (lua_State* L, int module);
+void toluaI_tm_setclass (lua_State* L, int lo);
+void toluaI_tm_pushmate (lua_State* L, int lo);
+void toluaI_tm_pushclass (lua_State* L, int lo);
+
+
+#endif
diff --git a/src/lua/tolua_tt.c b/src/lua/tolua_tt.c
new file mode 100644
index 00000000..33c384c6
--- /dev/null
+++ b/src/lua/tolua_tt.c
@@ -0,0 +1,316 @@
+/* tolua: type & tag manipulation.
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tt.c,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+#include "tolua.h"
+#include "tolua_tt.h"
+#include "tolua_tm.h"
+#include "tolua_eh.h"
+#include "tolua_rg.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* Global tables created in Lua registry:
+ tolua_tbl_itype: indexed by instance tags, stores the instance types.
+ tolua_tbl_itag: indexed by instance types, stores the instance tags.
+ tolua_tbl_const: indexed by constant tags, stores the tags.
+ tolua_tbl_hierarchy: indexed by instance tags, stores the base tags.
+*/
+
+/* exported basic type tags */
+int tolua_tag_nil;
+int tolua_tag_number;
+int tolua_tag_string;
+int tolua_tag_userdata;
+int tolua_tag_table;
+int tolua_tag_function;
+
+
+static const char* gettype (lua_State* L, int tag)
+{
+ const char* type;
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,tag);
+ lua_gettable(L,-2);
+ type = lua_tostring(L,-1);
+ if (type==NULL) type = "[undefined]";
+ lua_pop(L,2);
+ return type;
+}
+
+const char* toluaI_tt_getobjtype (lua_State* L, int lo)
+{
+ if (lua_gettop(L)<abs(lo))
+ return "[no object]";
+ else
+ return gettype(L,lua_tag(L,lo));
+}
+
+int toluaI_tt_gettag (lua_State* L, char* type)
+{
+ int tag;
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_gettable(L,-2);
+ tag = (int)lua_tonumber(L,-1);
+ lua_pop(L,2);
+ return tag;
+}
+
+static int basetag (lua_State* L, int hierarchy, int tag)
+{
+ int btag;
+ lua_pushnumber(L,tag);
+ lua_gettable(L,hierarchy);
+ btag = (int)lua_tonumber(L,-1);
+ lua_pop(L,1);
+ return btag;
+}
+
+static int istype (lua_State* L, int lo, int tag)
+{
+ int otag = lua_tag(L,lo);
+ if (tag==otag) /* check simplest case */
+ return 1;
+ else if (lua_isnil(L,lo) &&
+ tag!=LUA_TNUMBER &&
+ tag!=LUA_TTABLE &&
+ tag!=LUA_TFUNCTION
+ )
+ return 1;
+ else if ((tag==LUA_TSTRING && lua_isstring(L,lo)) || /* check convertions */
+ (tag==LUA_TNUMBER && lua_isnumber(L,lo))
+ )
+ return 1;
+ else if (tag==LUA_TUSERDATA && lua_isuserdata(L,lo)) /* pointer to void* */
+ return 1;
+ else if (tag==toluaI_tt_gettag(L,"bool") && otag==LUA_TNUMBER)
+ return 1;
+ else
+ {
+ /* if requested type is const, the non-const is an alternative type */
+ int tag2;
+ int tbl_hierarchy;
+ toluaI_getregistry(L,"tolua_tbl_const");
+ lua_pushnumber(L,tag);
+ lua_gettable(L,-2);
+ tag2 = (int)lua_tonumber(L,-1);
+ lua_pop(L,2);
+ if (tag2 && tag2==otag)
+ return 1;
+ /* check for base classes */
+ toluaI_getregistry(L,"tolua_tbl_hierarchy");
+ tbl_hierarchy = lua_gettop(L);
+ otag = basetag(L,tbl_hierarchy,otag);
+ while (otag)
+ {
+ if (tag==otag || (tag2 && tag2==otag))
+ break;
+ otag = basetag(L,tbl_hierarchy,otag);
+ }
+ lua_pop(L,1);
+ return otag!=0;
+ }
+}
+
+void toluaI_tt_init (lua_State* L)
+{
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_itype");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_itag");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_const");
+ lua_newtable(L); toluaI_setregistry(L,"tolua_tbl_hierarchy");
+
+ /* set and register basic Lua type tag */
+#if 0
+ lua_pushnumber(L,LUA_TNIL); toluaI_setregistry(L,"tolua_tag_nil");
+ lua_pushnumber(L,LUA_TNUMBER); toluaI_setregistry(L,"tolua_tag_number");
+ lua_pushnumber(L,LUA_TSTRING); toluaI_setregistry(L,"tolua_tag_string");
+ lua_pushnumber(L,LUA_TUSERDATA); toluaI_setregistry(L,"tolua_tag_userdata");
+ lua_pushnumber(L,LUA_TTABLE); toluaI_setregistry(L,"tolua_tag_table");
+ lua_pushnumber(L,LUA_TFUNCTION); toluaI_setregistry(L,"tolua_tag_function");
+
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_nil"),"nil");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_number"),"number");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_string"),"string");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_userdata"),"userdata");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_table"),"table");
+ toluaI_tt_register(L,toluaI_tt_gettag(L,"tolua_tag_function"),"function");
+#else
+ toluaI_tt_register(L,LUA_TNIL,"nil");
+ toluaI_tt_register(L,LUA_TNUMBER,"number");
+ toluaI_tt_register(L,LUA_TSTRING,"string");
+ toluaI_tt_register(L,LUA_TUSERDATA,"userdata");
+ toluaI_tt_register(L,LUA_TTABLE,"table");
+ toluaI_tt_register(L,LUA_TFUNCTION,"function");
+ toluaI_tt_register(L,lua_newtag(L),"bool");
+#endif
+}
+
+
+void toluaI_tt_register (lua_State* L, int tag, char* type)
+{
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,tag);
+ lua_pushstring(L,type);
+ lua_settable(L,-3);
+
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_pushnumber(L,tag);
+ lua_settable(L,-3);
+
+ lua_pop(L,2);
+}
+
+
+void toluaI_tt_class (lua_State* L, int lo, char* derived, char* base)
+{
+ char* cderived = toluaI_tt_concat("const ",derived);
+ int tag = toluaI_tt_gettag(L,derived);
+ int ctag = toluaI_tt_gettag(L,cderived);
+ toluaI_tm_instance(L,tag,lo);
+ toluaI_tm_instance(L,ctag,lo);
+ if (*base != 0)
+ {
+ char* cbase = toluaI_tt_concat("const ",base);
+ int btag = toluaI_tt_gettag(L,base);
+ int cbtag = toluaI_tt_gettag(L,cbase);
+ toluaI_tt_sethierarchy(L,tag,btag);
+ toluaI_tt_sethierarchy(L,ctag,cbtag);
+ }
+}
+
+void toluaI_tt_sethierarchy (lua_State* L, int tag, int btag)
+{
+ toluaI_getregistry(L,"tolua_tbl_hierarchy");
+ lua_pushnumber(L,tag);
+ lua_pushnumber(L,btag);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+}
+
+char* toluaI_tt_concat (const char* s1, const char* s2)
+{
+ static char s[BUFSIZ];
+ assert(strlen(s1)+strlen(s2)<BUFSIZ);
+ return strcat(strcpy(s,s1),s2);
+}
+
+void tolua_usertype (lua_State* L, char* type)
+{
+ /* check if type is already registered */
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_gettable(L,-2);
+ if (lua_isnil(L,-1))
+ {
+ char *ctype = toluaI_tt_concat("const ",type);
+ int tag = lua_newtag(L);
+ int ctag = lua_newtag(L);
+ toluaI_tt_register(L,tag,type);
+ toluaI_tt_register(L,ctag,ctype);
+ /* set const table */
+ toluaI_getregistry(L,"tolua_tbl_const");
+ lua_pushnumber(L,ctag);
+ lua_pushnumber(L,tag);
+ lua_settable(L,-3);
+ lua_pop(L,1);
+ }
+ lua_pop(L,2);
+}
+
+int toluaI_tt_isusertype (lua_State* L, int lo)
+{
+ if (lua_isuserdata(L,lo) &&
+ toluaI_tt_gettag(L,"tolua_tag_userdata")!=lua_tag(L,lo)
+ )
+ {
+ int status;
+ toluaI_getregistry(L,"tolua_tbl_itype");
+ lua_pushnumber(L,lua_tag(L,lo));
+ lua_gettable(L,-2);
+ status = !lua_isnil(L,-1);
+ lua_pop(L,2);
+ return status;
+ }
+ return 0;
+}
+
+#if 0
+void tolua_settag (lua_State* L, char* type, int* tag)
+{
+ toluaI_getregistry(L,"tolua_tbl_itag");
+ lua_pushstring(L,type);
+ lua_gettable(L,-2);
+ *tag = (int) lua_tonumber(L,-1);
+ lua_pop(L,2);
+}
+#endif
+
+int tolua_istype (lua_State* L, int narg, int tag, int def)
+{
+ if (lua_gettop(L)<abs(narg))
+ {
+ if (def==0)
+ {
+ toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg),gettype(L,tag));
+ return 0;
+ }
+ }
+ else
+ {
+ if (!istype(L,narg,tag))
+ {
+ toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg),gettype(L,tag));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int tolua_arrayistype (lua_State* L, int narg, int tag, int dim, int def)
+{
+ int i;
+ for (i=0; i<dim; ++i)
+ {
+ int tf;
+ lua_pushnumber(L,i+1);
+ lua_gettable(L,narg);
+ tf = lua_gettop(L);
+ if (!istype(L,tf,tag) && (!def || !lua_isnil(L,tf)))
+ {
+ static char t1[BUFSIZ], t2[BUFSIZ];
+ sprintf(t1,"array of %s",toluaI_tt_getobjtype(L,tf));
+ sprintf(t2,"array of %s (dimension=%d)",gettype(L,tag),dim);
+ toluaI_eh_set(L,narg,t1,t2);
+ return 0;
+ }
+ lua_pop(L,1);
+ }
+ return 1;
+}
+
+int tolua_isnoobj (lua_State* L, int narg)
+{
+ if (lua_gettop(L)>=abs(narg))
+ {
+ toluaI_eh_set(L,narg,toluaI_tt_getobjtype(L,narg),
+ toluaI_tt_getobjtype(L,lua_gettop(L)+1));
+ return 0;
+ }
+ return 1;
+}
+
+
diff --git a/src/lua/tolua_tt.h b/src/lua/tolua_tt.h
new file mode 100644
index 00000000..941a2b02
--- /dev/null
+++ b/src/lua/tolua_tt.h
@@ -0,0 +1,31 @@
+/* tolua: type & tag manipulation.
+** Support code for Lua bindings.
+** Written by Waldemar Celes
+** TeCGraf/PUC-Rio
+** Jul 1998
+** $Id: tolua_tt.h,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+*/
+
+/* This code is free software; you can redistribute it and/or modify it.
+** The software provided hereunder is on an "as is" basis, and
+** the author has no obligation to provide maintenance, support, updates,
+** enhancements, or modifications.
+*/
+
+
+#ifndef tolua_tt_h
+#define tolua_tt_h
+
+void toluaI_tt_init (lua_State* L);
+void toluaI_tt_register (lua_State* L, int tag, char* type);
+void toluaI_tt_class (lua_State* L, int lo, char* derived, char* base);
+void toluaI_tt_sethierarchy (lua_State* L, int tag, int btag);
+int toluaI_tt_isusertype (lua_State* L, int lo);
+int toluaI_tt_gettag (lua_State* L, char* type);
+const char* toluaI_tt_getobjtype (lua_State* L, int lo);
+char* toluaI_tt_concat (const char* s1, const char* s2);
+
+
+
+
+#endif
diff --git a/src/lua/tolualua.c b/src/lua/tolualua.c
new file mode 100644
index 00000000..adbb8635
--- /dev/null
+++ b/src/lua/tolualua.c
@@ -0,0 +1,2975 @@
+/*
+** Lua binding: tolualua
+** Generated automatically by tolua 4.0a - angband on Sun Nov 11 22:59:08 2001.
+*/
+
+#include "tolua.h"
+
+/* Exported function */
+int tolua_tolualua_open (lua_State* tolua_S);
+void tolua_tolualua_close (lua_State* tolua_S);
+
+
+/* function to register type */
+static void toluaI_reg_types (lua_State* tolua_S)
+{
+}
+
+/* error messages */
+#define TOLUA_ERR_SELF tolua_error(tolua_S,"invalid 'self'")
+#define TOLUA_ERR_ASSIGN tolua_error(tolua_S,"#vinvalid type in variable assignment.")
+
+/* Open function */
+int tolua_tolualua_open (lua_State* tolua_S)
+{
+ tolua_open(tolua_S);
+ toluaI_reg_types(tolua_S);
+
+ { /* begin embedded lua code */
+ static unsigned char B[] = {
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 95, 98, 97,115,105, 99, 32, 61, 32,123, 10, 91, 39,118,
+ 111,105,100, 39, 93, 32, 61, 32, 39, 39, 44, 10, 91, 39, 99,
+ 104, 97,114, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114,
+ 39, 44, 10, 91, 39,105,110,116, 39, 93, 32, 61, 32, 39,110,
+ 117,109, 98,101,114, 39, 44, 10, 91, 39,115,104,111,114,116,
+ 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10,
+ 91, 39,108,111,110,103, 39, 93, 32, 61, 32, 39,110,117,109,
+ 98,101,114, 39, 44, 10, 91, 39, 95, 99,115,116,114,105,110,
+ 103, 39, 93, 32, 61, 32, 39,115,116,114,105,110,103, 39, 44,
+ 10, 91, 39, 95,117,115,101,114,100, 97,116, 97, 39, 93, 32,
+ 61, 32, 39,117,115,101,114,100, 97,116, 97, 39, 44, 10, 91,
+ 39, 99,104, 97,114, 42, 39, 93, 32, 61, 32, 39,115,116,114,
+ 105,110,103, 39, 44, 10, 91, 39,118,111,105,100, 42, 39, 93,
+ 32, 61, 32, 39,117,115,101,114,100, 97,116, 97, 39, 44, 10,
+ 91, 39, 98,111,111,108, 39, 93, 32, 61, 32, 39, 98,111,111,
+ 108, 39, 44, 10, 91, 39, 76, 85, 65, 95, 86, 65, 76, 85, 69,
+ 39, 93, 32, 61, 32, 39,118, 97,108,117,101, 39, 44, 10, 91,
+ 39, 98,121,116,101, 39, 93, 32, 61, 32, 39,110,117,109, 98,
+ 101,114, 39, 44, 10, 91, 39,115, 49, 54, 98, 39, 93, 32, 61,
+ 32, 39,110,117,109, 98,101,114, 39, 44, 10, 91, 39,117, 49,
+ 54, 98, 39, 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39,
+ 44, 10, 91, 39,115, 51, 50, 98, 39, 93, 32, 61, 32, 39,110,
+ 117,109, 98,101,114, 39, 44, 10, 91, 39,117, 51, 50, 98, 39,
+ 93, 32, 61, 32, 39,110,117,109, 98,101,114, 39, 44, 10,125,
+ 10, 10, 95, 98, 97,115,105, 99, 95,116, 97,103, 32, 61, 32,
+ 123, 10, 91, 39,118,111,105,100, 39, 93, 32, 61, 32, 39, 39,
+ 44, 10, 91, 39, 99,104, 97,114, 39, 93, 32, 61, 32, 39, 76,
+ 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,
+ 105,110,116, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78,
+ 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,115,104,111,114,116,
+ 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66,
+ 69, 82, 39, 44, 10, 91, 39,108,111,110,103, 39, 93, 32, 61,
+ 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44,
+ 10, 91, 39, 95, 99,115,116,114,105,110,103, 39, 93, 32, 61,
+ 32, 39, 76, 85, 65, 95, 84, 83, 84, 82, 73, 78, 71, 39, 44,
+ 10, 91, 39, 95,117,115,101,114,100, 97,116, 97, 39, 93, 32,
+ 61, 32, 39, 76, 85, 65, 95, 84, 85, 83, 69, 82, 68, 65, 84,
+ 65, 39, 44, 10, 91, 39, 99,104, 97,114, 42, 39, 93, 32, 61,
+ 32, 39, 76, 85, 65, 95, 84, 83, 84, 82, 73, 78, 71, 39, 44,
+ 10, 91, 39,118,111,105,100, 42, 39, 93, 32, 61, 32, 39, 76,
+ 85, 65, 95, 84, 85, 83, 69, 82, 68, 65, 84, 65, 39, 44, 10,
+ 91, 39, 98,111,111,108, 39, 93, 32, 61, 32, 39,116,111,108,
+ 117, 97, 95,116, 97,103, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 98,111,111,108, 34, 41, 39, 44, 10, 91, 39, 98,121,116,
+ 101, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77,
+ 66, 69, 82, 39, 44, 10, 91, 39,115, 49, 54, 98, 39, 93, 32,
+ 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39,
+ 44, 10, 91, 39,117, 49, 54, 98, 39, 93, 32, 61, 32, 39, 76,
+ 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,
+ 115, 51, 50, 98, 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84,
+ 78, 85, 77, 66, 69, 82, 39, 44, 10, 91, 39,117, 51, 50, 98,
+ 39, 93, 32, 61, 32, 39, 76, 85, 65, 95, 84, 78, 85, 77, 66,
+ 69, 82, 39, 44, 10,125, 10, 10, 95, 98, 97,115,105, 99, 95,
+ 99,116,121,112,101, 32, 61, 32,123, 10,110,117,109, 98,101,
+ 114, 32, 61, 32, 34,108,111,110,103, 34, 44, 10,115,116,114,
+ 105,110,103, 32, 61, 32, 34, 99,111,110,115,116, 32, 99,104,
+ 97,114, 42, 34, 44, 10,117,115,101,114,100, 97,116, 97, 32,
+ 61, 32, 34,118,111,105,100, 42, 34, 44, 10, 98,111,111,108,
+ 32, 61, 32, 34,105,110,116, 34, 44, 10,125, 10, 10, 10, 10,
+ 95,117,115,101,114,116,121,112,101, 32, 61, 32,123,125, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32,116,111,108,117,
+ 97, 95,105,110,100,101,120, 32, 40,116, 44,102, 41, 10,105,
+ 102, 32,102, 32, 61, 61, 32, 39, 95, 98, 97,115,101, 39, 32,
+ 116,104,101,110, 10,114,101,116,117,114,110, 32,116,111,108,
+ 117, 97, 95,111,108,100, 95,105,110,100,101,120, 40,116, 44,
+ 102, 41, 10,101,108,115,101, 10,114,101,116,117,114,110, 32,
+ 116, 46, 95, 98, 97,115,101, 91,102, 93, 10,101,110,100, 10,
+ 101,110,100, 10, 10,116,111,108,117, 97, 95,116, 97,103, 32,
+ 61, 32,110,101,119,116, 97,103, 40, 41, 10,116,111,108,117,
+ 97, 95,111,108,100, 95,105,110,100,101,120, 32, 61, 32,115,
+ 101,116,116, 97,103,109,101,116,104,111,100, 40,116,111,108,
+ 117, 97, 95,116, 97,103, 44, 34,105,110,100,101,120, 34, 44,
+ 116,111,108,117, 97, 95,105,110,100,101,120, 41, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32,116,111,108,117, 97, 95,
+ 101,114,114,111,114, 32, 40,115, 41, 10,108,111, 99, 97,108,
+ 32,111,117,116, 32, 61, 32, 95, 79, 85, 84, 80, 85, 84, 10,
+ 95, 79, 85, 84, 80, 85, 84, 32, 61, 32, 95, 83, 84, 68, 69,
+ 82, 82, 10,105,102, 32,115,116,114,115,117, 98, 40,115, 44,
+ 49, 44, 49, 41, 32, 61, 61, 32, 39, 35, 39, 32,116,104,101,
+ 110, 10,119,114,105,116,101, 40, 34, 92,110, 42, 42, 32,116,
+ 111,108,117, 97, 58, 32, 34, 46, 46,115,116,114,115,117, 98,
+ 40,115, 44, 50, 41, 46, 46, 34, 46, 92,110, 92,110, 34, 41,
+ 10,101,108,115,101, 10,119,114,105,116,101, 40, 34, 92,110,
+ 42, 42, 32,116,111,108,117, 97, 32,105,110,116,101,114,110,
+ 97,108, 32,101,114,114,111,114, 58, 32, 34, 46, 46,115, 46,
+ 46, 34, 46, 92,110, 92,110, 34, 41, 10,114,101,116,117,114,
+ 110, 10,101,110,100, 10, 10,105,102, 32, 95, 99,117,114,114,
+ 95, 99,111,100,101, 32,116,104,101,110, 10,108,111, 99, 97,
+ 108, 32, 95, 44, 95, 44,115, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40, 95, 99,117,114,114, 95, 99,111,100,101, 44, 34,
+ 94, 37,115, 42, 40, 46, 45, 92,110, 41, 34, 41, 10,105,102,
+ 32,115, 61, 61,110,105,108, 32,116,104,101,110, 32,115, 32,
+ 61, 32, 95, 99,117,114,114, 95, 99,111,100,101, 32,101,110,
+ 100, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 95,
+ 117,115,101,114,100, 97,116, 97, 34, 44, 34,118,111,105,100,
+ 42, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,
+ 34, 95, 99,115,116,114,105,110,103, 34, 44, 34, 99,104, 97,
+ 114, 42, 34, 41, 10,119,114,105,116,101, 40, 34, 67,111,100,
+ 101, 32, 98,101,105,110,103, 32,112,114,111, 99,101,115,115,
+ 101,100, 58, 92,110, 34, 46, 46,115, 46, 46, 34, 92,110, 34,
+ 41, 10,101,110,100, 10, 95, 79, 85, 84, 80, 85, 84, 32, 61,
+ 32,111,117,116, 10,101,110,100, 10, 10, 10, 95, 69, 82, 82,
+ 79, 82, 77, 69, 83, 83, 65, 71, 69, 32, 61, 32,116,111,108,
+ 117, 97, 95,101,114,114,111,114, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32,114,101,103,116,121,112,101, 32, 40,116,
+ 41, 10,105,102, 32,110,111,116, 32,105,115,116,121,112,101,
+ 40,116, 41, 32,116,104,101,110, 10, 95,117,115,101,114,116,
+ 121,112,101, 91,116, 93, 32, 61, 32,116, 10,101,110,100, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32,116, 97,103,118, 97,114,
+ 40,116,121,112,101, 44, 99,111,110,115,116, 41, 10,105,102,
+ 32,116,121,112,101, 32, 61, 61, 32, 39, 39, 32,111,114, 32,
+ 116,121,112,101, 32, 61, 61, 32, 39,118,111,105,100, 39, 32,
+ 116,104,101,110, 10,114,101,116,117,114,110, 32,116,121,112,
+ 101, 44, 48, 10,101,108,115,101, 10,108,111, 99, 97,108, 32,
+ 109, 44,116, 32, 61, 32,102,105,110,100,116,121,112,101,100,
+ 101,102, 40,116,121,112,101, 41, 10,105,102, 32,105,115, 98,
+ 97,115,105, 99, 40,116, 41, 32,116,104,101,110, 10,114,101,
+ 116,117,114,110, 32,116, 44, 32, 95, 98, 97,115,105, 99, 95,
+ 116, 97,103, 91,116, 93, 10,101,110,100, 10,105,102, 32,115,
+ 116,114,102,105,110,100, 40,109, 44, 39, 99,111,110,115,116,
+ 39, 41, 32,116,104,101,110, 32, 99,111,110,115,116, 32, 61,
+ 32, 39, 99,111,110,115,116, 39, 32,101,110,100, 10,114,101,
+ 103,116,121,112,101, 40,116, 41, 10,105,102, 32, 99,111,110,
+ 115,116, 32, 97,110,100, 32, 99,111,110,115,116, 32,126, 61,
+ 32, 39, 39, 32,116,104,101,110, 10,116, 32, 61, 32, 39, 99,
+ 111,110,115,116, 32, 39, 46, 46,116, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32,116, 44, 39,116,111,108,117, 97, 95,
+ 116, 97,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,116, 46, 46, 39, 34, 41, 39, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,105,115,
+ 98, 97,115,105, 99, 32, 40,116,121,112,101, 41, 10,108,111,
+ 99, 97,108, 32,109, 44,116, 32, 61, 32,102,105,110,100,116,
+ 121,112,101,100,101,102, 40,116,121,112,101, 41, 10,108,111,
+ 99, 97,108, 32, 98, 32, 61, 32, 95, 98, 97,115,105, 99, 91,
+ 116, 93, 10,105,102, 32, 98, 32,116,104,101,110, 10,114,101,
+ 116,117,114,110, 32, 98, 44, 95, 98, 97,115,105, 99, 95, 99,
+ 116,121,112,101, 91, 98, 93, 10,101,110,100, 10,114,101,116,
+ 117,114,110, 32,110,105,108, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32,105,115,116,121,112,101, 32,
+ 40,116, 41, 10,114,101,116,117,114,110, 32, 95, 98, 97,115,
+ 105, 99, 91,116, 93, 32,111,114, 32, 95,117,115,101,114,116,
+ 121,112,101, 91,116, 93, 32,111,114, 32,105,115,116,121,112,
+ 101,100,101,102, 40,116, 41, 10,101,110,100, 10, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32,115,112,108,105,116, 32,
+ 40,115, 44,116, 41, 10,108,111, 99, 97,108, 32,108, 32, 61,
+ 32,123,110, 61, 48,125, 10,108,111, 99, 97,108, 32,102, 32,
+ 61, 32,102,117,110, 99,116,105,111,110, 32, 40,115, 41, 10,
+ 37,108, 46,110, 32, 61, 32, 37,108, 46,110, 32, 43, 32, 49,
+ 10, 37,108, 91, 37,108, 46,110, 93, 32, 61, 32,115, 10,101,
+ 110,100, 10,108,111, 99, 97,108, 32,112, 32, 61, 32, 34, 37,
+ 115, 42, 40, 46, 45, 41, 37,115, 42, 34, 46, 46,116, 46, 46,
+ 34, 37,115, 42, 34, 10,115, 32, 61, 32,103,115,117, 98, 40,
+ 115, 44, 34, 94, 37,115, 43, 34, 44, 34, 34, 41, 10,115, 32,
+ 61, 32,103,115,117, 98, 40,115, 44, 34, 37,115, 43, 36, 34,
+ 44, 34, 34, 41, 10,115, 32, 61, 32,103,115,117, 98, 40,115,
+ 44,112, 44,102, 41, 10,108, 46,110, 32, 61, 32,108, 46,110,
+ 32, 43, 32, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32,103,
+ 115,117, 98, 40,115, 44, 34, 40, 37,115, 37,115, 42, 41, 36,
+ 34, 44, 34, 34, 41, 10,114,101,116,117,114,110, 32,108, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,111,110, 99, 97,116, 32, 40,116, 44,102, 44,108, 41,
+ 10,108,111, 99, 97,108, 32,115, 32, 61, 32, 39, 39, 10,108,
+ 111, 99, 97,108, 32,105, 61,102, 10,119,104,105,108,101, 32,
+ 105, 60, 61,108, 32,100,111, 10,115, 32, 61, 32,115, 46, 46,
+ 116, 91,105, 93, 10,105, 32, 61, 32,105, 43, 49, 10,105,102,
+ 32,105, 32, 60, 61, 32,108, 32,116,104,101,110, 32,115, 32,
+ 61, 32,115, 46, 46, 39, 32, 39, 32,101,110,100, 10,101,110,
+ 100, 10,114,101,116,117,114,110, 32,115, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32,111,117,116,112,
+ 117,116, 32, 40, 46, 46, 46, 41, 10,108,111, 99, 97,108, 32,
+ 105, 61, 49, 10,119,104,105,108,101, 32,105, 60, 61, 97,114,
+ 103, 46,110, 32,100,111, 10,105,102, 32, 95, 99,111,110,116,
+ 32, 97,110,100, 32,110,111,116, 32,115,116,114,102,105,110,
+ 100, 40, 95, 99,111,110,116, 44, 39, 91, 37, 40, 44, 34, 93,
+ 39, 41, 32, 97,110,100, 10,115,116,114,102,105,110,100, 40,
+ 97,114,103, 91,105, 93, 44, 34, 94, 91, 37, 97, 95,126, 93,
+ 34, 41, 32,116,104,101,110, 10,119,114,105,116,101, 40, 39,
+ 32, 39, 41, 10,101,110,100, 10,119,114,105,116,101, 40, 97,
+ 114,103, 91,105, 93, 41, 10,105,102, 32, 97,114,103, 91,105,
+ 93, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10, 95, 99,
+ 111,110,116, 32, 61, 32,115,116,114,115,117, 98, 40, 97,114,
+ 103, 91,105, 93, 44, 45, 49, 44, 45, 49, 41, 10,101,110,100,
+ 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,105,102,
+ 32,115,116,114,102,105,110,100, 40, 97,114,103, 91, 97,114,
+ 103, 46,110, 93, 44, 34, 91, 37, 47, 37, 41, 37, 59, 37,123,
+ 37,125, 93, 36, 34, 41, 32,116,104,101,110, 10, 95, 99,111,
+ 110,116, 61,110,105,108, 32,119,114,105,116,101, 40, 39, 92,
+ 110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,
+ 115, 70,101, 97,116,117,114,101, 32, 61, 32,123, 10,125, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 70,101, 97,116,117,114,101, 58,115,117,112, 99,111,100,
+ 101, 32, 40, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117,
+ 114,101, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,101,
+ 110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 70,101, 97,116,117,114,101, 58,114,101,103,
+ 105,115,116,101,114, 32, 40, 41, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,
+ 101, 97,116,117,114,101, 58,117,110,114,101,103,105,115,116,
+ 101,114, 32, 40, 41, 10,101,110,100, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 70,101, 97,116,
+ 117,114,101, 58,112,114,101, 97,109, 98,108,101, 32, 40, 41,
+ 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 58,
+ 105,110, 99,108, 97,115,115, 32, 40, 41, 10,105,102, 32,115,
+ 101,108,102, 46,112, 97,114,101,110,116, 32, 97,110,100, 32,
+ 115,101,108,102, 46,112, 97,114,101,110,116, 46,116,121,112,
+ 101, 32, 61, 61, 32, 39, 99,108, 97,115,115, 39, 32,116,104,
+ 101,110, 10,114,101,116,117,114,110, 32,115,101,108,102, 46,
+ 112, 97,114,101,110,116, 46,110, 97,109,101, 10,101,108,115,
+ 101, 10,114,101,116,117,114,110, 32,110,105,108, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101,
+ 58,105,110,109,111,100,117,108,101, 32, 40, 41, 10,105,102,
+ 32,115,101,108,102, 46,112, 97,114,101,110,116, 32, 97,110,
+ 100, 32,115,101,108,102, 46,112, 97,114,101,110,116, 46,116,
+ 121,112,101, 32, 61, 61, 32, 39,109,111,100,117,108,101, 39,
+ 32,116,104,101,110, 10,114,101,116,117,114,110, 32,115,101,
+ 108,102, 46,112, 97,114,101,110,116, 46,110, 97,109,101, 10,
+ 101,108,115,101, 10,114,101,116,117,114,110, 32,110,105,108,
+ 10,101,110,100, 10,101,110,100, 10, 10, 10, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 70,101,
+ 97,116,117,114,101, 58, 99,102,117,110, 99,110, 97,109,101,
+ 32, 40,110, 41, 10,105,102, 32,115,101,108,102, 46,112, 97,
+ 114,101,110,116, 32,116,104,101,110, 10,110, 32, 61, 32,115,
+ 101,108,102, 46,112, 97,114,101,110,116, 58, 99,102,117,110,
+ 99,110, 97,109,101, 40,110, 41, 10,101,110,100, 10,105,102,
+ 32,115,101,108,102, 46,108,110, 97,109,101, 32,116,104,101,
+ 110, 10,114,101,116,117,114,110, 32,110, 46, 46, 39, 95, 39,
+ 46, 46,115,101,108,102, 46,108,110, 97,109,101, 10,101,108,
+ 115,101, 10,114,101,116,117,114,110, 32,110, 46, 46, 39, 95,
+ 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 86,
+ 101,114, 98, 97,116,105,109, 32, 61, 32,123, 10,108,105,110,
+ 101, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61,
+ 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10,
+ 125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115, 86,
+ 101,114, 98, 97,116,105,109, 44,116,111,108,117, 97, 95,116,
+ 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58,112,
+ 114,101, 97,109, 98,108,101, 32, 40, 41, 10,105,102, 32,110,
+ 111,116, 32,115,101,108,102, 46, 99,111,110,100, 32,116,104,
+ 101,110, 10,119,114,105,116,101, 40,115,101,108,102, 46,108,
+ 105,110,101, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86,
+ 101,114, 98, 97,116,105,109, 58,115,117,112, 99,111,100,101,
+ 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 99,111,110,
+ 100, 32,116,104,101,110, 10,119,114,105,116,101, 40,115,101,
+ 108,102, 46,108,105,110,101, 41, 10,119,114,105,116,101, 40,
+ 39, 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 86,101,114, 98, 97,116,105,109, 58,114,101,103,105,115,116,
+ 101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46, 99,
+ 111,110,100, 32,116,104,101,110, 10,119,114,105,116,101, 40,
+ 115,101,108,102, 46,108,105,110,101, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 58,
+ 112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,
+ 111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 86,101,114, 98, 97,116,105,109,123, 34, 41,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 32,108,105,110,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,
+ 102, 46,108,105,110,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34,
+ 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 95, 86,101,114, 98,
+ 97,116,105,109, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,
+ 101, 32, 61, 32, 99,108, 97,115,115, 86,101,114, 98, 97,116,
+ 105,109, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,100, 40,
+ 116, 41, 10,114,101,116,117,114,110, 32,116, 10,101,110,100,
+ 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 86,101,
+ 114, 98, 97,116,105,109, 32, 40,108, 41, 10,108,111, 99, 97,
+ 108, 32, 99, 10,105,102, 32,115,116,114,115,117, 98, 40,108,
+ 44, 49, 44, 49, 41, 32, 61, 61, 32, 39, 36, 39, 32,116,104,
+ 101,110, 10, 99, 32, 61, 32, 49, 10,108, 32, 61, 32,115,116,
+ 114,115,117, 98, 40,108, 44, 50, 41, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32, 95, 86,101,114, 98, 97,116,105,109,
+ 32,123, 10,108,105,110,101, 32, 61, 32,108, 44, 10, 99,111,
+ 110,100, 32, 61, 32, 99, 10,125, 10,101,110,100, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 99,108, 97,115,115, 67,111,100,101, 32, 61, 32,123, 10,116,
+ 101,120,116, 32, 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101,
+ 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117,114,101,
+ 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,
+ 115, 67,111,100,101, 44,116,111,108,117, 97, 95,116, 97,103,
+ 41, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 67,111,100,101, 58,114,101,103,105,115,116,101,
+ 114, 32, 40, 41, 10, 10,108,111, 99, 97,108, 32,115, 32, 61,
+ 32, 99,108,101, 97,110, 40,115,101,108,102, 46,116,101,120,
+ 116, 41, 10,105,102, 32,110,111,116, 32,115, 32,116,104,101,
+ 110, 10,101,114,114,111,114, 40, 34,112, 97,114,115,101,114,
+ 32,101,114,114,111,114, 32,105,110, 32,101,109, 98,101,100,
+ 100,101,100, 32, 99,111,100,101, 34, 41, 10,101,110,100, 10,
+ 10, 10,111,117,116,112,117,116, 40, 39, 92,110, 32,123, 32,
+ 47, 42, 32, 98,101,103,105,110, 32,101,109, 98,101,100,100,
+ 101,100, 32,108,117, 97, 32, 99,111,100,101, 32, 42, 47, 92,
+ 110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,115,116,
+ 97,116,105, 99, 32,117,110,115,105,103,110,101,100, 32, 99,
+ 104, 97,114, 32, 66, 91, 93, 32, 61, 32,123, 92,110, 32, 39,
+ 41, 10,108,111, 99, 97,108, 32,116, 61,123,110, 61, 48,125,
+ 10,108,111, 99, 97,108, 32, 98, 32, 61, 32,103,115,117, 98,
+ 40,115, 44, 39, 40, 46, 41, 39, 44,102,117,110, 99,116,105,
+ 111,110, 32, 40, 99, 41, 10,108,111, 99, 97,108, 32,101, 32,
+ 61, 32, 39, 39, 10, 37,116, 46,110, 61, 37,116, 46,110, 43,
+ 49, 32,105,102, 32, 37,116, 46,110, 61, 61, 49, 53, 32,116,
+ 104,101,110, 32, 37,116, 46,110, 61, 48, 32,101, 61, 39, 92,
+ 110, 32, 39, 32,101,110,100, 10,114,101,116,117,114,110, 32,
+ 102,111,114,109, 97,116, 40, 39, 37, 51,117, 44, 37,115, 39,
+ 44,115,116,114, 98,121,116,101, 40, 99, 41, 44,101, 41, 10,
+ 101,110,100, 10, 41, 10,111,117,116,112,117,116, 40, 98, 46,
+ 46,115,116,114, 98,121,116,101, 40, 34, 32, 34, 41, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 92,110, 32,125, 59, 92,110,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97,
+ 95,100,111, 98,117,102,102,101,114, 40,116,111,108,117, 97,
+ 95, 83, 44, 40, 99,104, 97,114, 42, 41, 66, 44,115,105,122,
+ 101,111,102, 40, 66, 41, 44, 34,116,111,108,117, 97, 58, 32,
+ 101,109, 98,101,100,100,101,100, 32, 76,117, 97, 32, 99,111,
+ 100,101, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,125, 32, 47, 42, 32,101,110,100, 32,111,102, 32,101,
+ 109, 98,101,100,100,101,100, 32,108,117, 97, 32, 99,111,100,
+ 101, 32, 42, 47, 92,110, 92,110, 39, 41, 10,101,110,100, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 67,111,100,101, 58,112,114,105,110,116, 32, 40,105,
+ 100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 67,111,100,101,
+ 123, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 32,116,101,120,116, 32, 61, 32, 91, 91, 34, 46,
+ 46,115,101,108,102, 46,116,101,120,116, 46, 46, 34, 93, 93,
+ 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,
+ 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 95, 67,111,100,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,
+ 115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,100,101, 10,
+ 115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,
+ 116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 67,111,100,101, 32,
+ 40,108, 41, 10,114,101,116,117,114,110, 32, 95, 67,111,100,
+ 101, 32,123, 10,116,101,120,116, 32, 61, 32,108, 10,125, 10,
+ 101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,
+ 115,115, 84,121,112,101,100,101,102, 32, 61, 32,123, 10,117,
+ 116,121,112,101, 32, 61, 32, 39, 39, 44, 10,109,111,100, 32,
+ 61, 32, 39, 39, 44, 10,116,121,112,101, 32, 61, 32, 39, 39,
+ 10,125, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 84,121,112,101,100,101,102, 58,112,114,105,
+ 110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 84,121,112,101,100,101,102,123, 34, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,117,116,121,
+ 112,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,117,
+ 116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100,
+ 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46,
+ 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 95, 84,121,112,101,100,101,102, 32, 40,116, 41, 10,116,
+ 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 84,
+ 121,112,101,100,101,102, 10,115,101,116,116, 97,103, 40,116,
+ 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,
+ 101,110,100,116,121,112,101,100,101,102, 40,116, 41, 10,114,
+ 101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 84,121,112,101,100,101,
+ 102, 32, 40,115, 41, 10,105,102, 32,115,116,114,102,105,110,
+ 100, 40,115, 44, 39, 91, 37, 42, 38, 93, 39, 41, 32,116,104,
+ 101,110, 10,116,111,108,117, 97, 95,101,114,114,111,114, 40,
+ 34, 35,105,110,118, 97,108,105,100, 32,116,121,112,101,100,
+ 101,102, 58, 32,112,111,105,110,116,101,114,115, 32, 40, 97,
+ 110,100, 32,114,101,102,101,114,101,110, 99,101,115, 41, 32,
+ 97,114,101, 32,110,111,116, 32,115,117,112,112,111,114,116,
+ 101,100, 34, 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,
+ 116, 32, 61, 32,115,112,108,105,116, 40,103,115,117, 98, 40,
+ 115, 44, 34, 37,115, 37,115, 42, 34, 44, 34, 32, 34, 41, 44,
+ 34, 32, 34, 41, 10,114,101,116,117,114,110, 32, 95, 84,121,
+ 112,101,100,101,102, 32,123, 10,117,116,121,112,101, 32, 61,
+ 32,116, 91,116, 46,110, 93, 44, 10,116,121,112,101, 32, 61,
+ 32,116, 91,116, 46,110, 45, 49, 93, 44, 10,109,111,100, 32,
+ 61, 32, 99,111,110, 99, 97,116, 40,116, 44, 49, 44,116, 46,
+ 110, 45, 50, 41, 10,125, 10,101,110,100, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,
+ 115, 67,111,110,116, 97,105,110,101,114, 32, 61, 10,123, 10,
+ 99,117,114,114, 32, 61, 32,110,105,108, 44, 10, 95, 98, 97,
+ 115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,117,
+ 114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,108,
+ 97,115,115, 67,111,110,116, 97,105,110,101,114, 44,116,111,
+ 108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 58,100,101, 99,108,116, 97,103, 32, 40, 41,
+ 10,112,117,115,104, 40,115,101,108,102, 41, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105,
+ 93, 58,100,101, 99,108,116, 97,103, 40, 41, 10,105, 32, 61,
+ 32,105, 43, 49, 10,101,110,100, 10,112,111,112, 40, 41, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114,
+ 58,115,117,112, 99,111,100,101, 32, 40, 41, 10,112,117,115,
+ 104, 40,115,101,108,102, 41, 10,108,111, 99, 97,108, 32,105,
+ 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,105,
+ 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,115,117,
+ 112, 99,111,100,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49,
+ 10,101,110,100, 10,112,111,112, 40, 41, 10,101,110,100, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 67,111,
+ 110,116, 97,105,110,101,114, 32, 40,115,101,108,102, 41, 10,
+ 115,101,108,102, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108,
+ 97,115,115, 67,111,110,116, 97,105,110,101,114, 10,115,101,
+ 116,116, 97,103, 40,115,101,108,102, 44,116,111,108,117, 97,
+ 95,116, 97,103, 41, 10,115,101,108,102, 46,110, 32, 61, 32,
+ 48, 10,115,101,108,102, 46,116,121,112,101,100,101,102,115,
+ 32, 61, 32,123,110, 61, 48,125, 10,115,101,108,102, 46,108,
+ 110, 97,109,101,115, 32, 61, 32,123,125, 10,114,101,116,117,
+ 114,110, 32,115,101,108,102, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32,112,117,115,104, 32, 40,116,
+ 41, 10, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,
+ 114, 46, 99,117,114,114, 32, 61, 32,116, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32,112,111,112, 32,
+ 40, 41, 10, 99,108, 97,115,115, 67,111,110,116, 97,105,110,
+ 101,114, 46, 99,117,114,114, 32, 61, 32, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 46,
+ 112, 97,114,101,110,116, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 97,112,112,101,110,100, 32, 40,
+ 116, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,
+ 97,112,112,101,110,100, 40,116, 41, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 97,112,112,101,110,
+ 100,116,121,112,101,100,101,102, 32, 40,116, 41, 10,114,101,
+ 116,117,114,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 46, 99,117,114,114, 58, 97,112,112,101,110,
+ 100,116,121,112,101,100,101,102, 40,116, 41, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32,102,105,110,
+ 100,116,121,112,101,100,101,102, 32, 40,116,121,112,101, 41,
+ 10,114,101,116,117,114,110, 32, 99,108, 97,115,115, 67,111,
+ 110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,102,105,
+ 110,100,116,121,112,101,100,101,102, 40,116,121,112,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32,105,115,116,121,112,101,100,101,102, 32, 40,116,121,112,
+ 101, 41, 10,114,101,116,117,114,110, 32, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 46, 99,117,114,114, 58,
+ 105,115,116,121,112,101,100,101,102, 40,116,121,112,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114,
+ 58, 97,112,112,101,110,100, 32, 40,116, 41, 10,115,101,108,
+ 102, 46,110, 32, 61, 32,115,101,108,102, 46,110, 32, 43, 32,
+ 49, 10,115,101,108,102, 91,115,101,108,102, 46,110, 93, 32,
+ 61, 32,116, 10,116, 46,112, 97,114,101,110,116, 32, 61, 32,
+ 115,101,108,102, 10,101,110,100, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 58, 97,112,112,101,110,100,116,121,112,101,
+ 100,101,102, 32, 40,116, 41, 10,115,101,108,102, 46,116,121,
+ 112,101,100,101,102,115, 46,110, 32, 61, 32,115,101,108,102,
+ 46,116,121,112,101,100,101,102,115, 46,110, 32, 43, 32, 49,
+ 10,115,101,108,102, 46,116,121,112,101,100,101,102,115, 91,
+ 115,101,108,102, 46,116,121,112,101,100,101,102,115, 46,110,
+ 93, 32, 61, 32,116, 10,101,110,100, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116,
+ 97,105,110,101,114, 58,111,118,101,114,108,111, 97,100, 32,
+ 40,108,110, 97,109,101, 41, 10,105,102, 32,110,111,116, 32,
+ 115,101,108,102, 46,108,110, 97,109,101,115, 91,108,110, 97,
+ 109,101, 93, 32,116,104,101,110, 10,115,101,108,102, 46,108,
+ 110, 97,109,101,115, 91,108,110, 97,109,101, 93, 32, 61, 32,
+ 48, 10,101,108,115,101, 10,115,101,108,102, 46,108,110, 97,
+ 109,101,115, 91,108,110, 97,109,101, 93, 32, 61, 32,115,101,
+ 108,102, 46,108,110, 97,109,101,115, 91,108,110, 97,109,101,
+ 93, 32, 43, 32, 49, 10,101,110,100, 10,114,101,116,117,114,
+ 110, 32,102,111,114,109, 97,116, 40, 34, 37, 48, 50,100, 34,
+ 44,115,101,108,102, 46,108,110, 97,109,101,115, 91,108,110,
+ 97,109,101, 93, 41, 10,101,110,100, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116, 97,
+ 105,110,101,114, 58,102,105,110,100,116,121,112,101,100,101,
+ 102, 32, 40,116,121,112,101, 41, 10,108,111, 99, 97,108, 32,
+ 101,110,118, 32, 61, 32,115,101,108,102, 10,119,104,105,108,
+ 101, 32,101,110,118, 32,100,111, 10,105,102, 32,101,110,118,
+ 46,116,121,112,101,100,101,102,115, 32,116,104,101,110, 10,
+ 108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101,
+ 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,105,
+ 93, 32,100,111, 10,105,102, 32,101,110,118, 46,116,121,112,
+ 101,100,101,102,115, 91,105, 93, 46,117,116,121,112,101, 32,
+ 61, 61, 32,116,121,112,101, 32,116,104,101,110, 10,108,111,
+ 99, 97,108, 32,109,111,100, 49, 44,116,121,112,101, 49, 32,
+ 61, 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,
+ 105, 93, 46,109,111,100, 44,101,110,118, 46,116,121,112,101,
+ 100,101,102,115, 91,105, 93, 46,116,121,112,101, 10,108,111,
+ 99, 97,108, 32,109,111,100, 50, 44,116,121,112,101, 50, 32,
+ 61, 32,102,105,110,100,116,121,112,101,100,101,102, 40,116,
+ 121,112,101, 49, 41, 10,114,101,116,117,114,110, 32,109,111,
+ 100, 50, 46, 46, 39, 32, 39, 46, 46,109,111,100, 49, 44,116,
+ 121,112,101, 50, 10,101,110,100, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,101,110,100, 10,101,110,118, 32, 61,
+ 32,101,110,118, 46,112, 97,114,101,110,116, 10,101,110,100,
+ 10,114,101,116,117,114,110, 32, 39, 39, 44,116,121,112,101,
+ 10,101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 58,
+ 105,115,116,121,112,101,100,101,102, 32, 40,116,121,112,101,
+ 41, 10,108,111, 99, 97,108, 32,101,110,118, 32, 61, 32,115,
+ 101,108,102, 10,119,104,105,108,101, 32,101,110,118, 32,100,
+ 111, 10,105,102, 32,101,110,118, 46,116,121,112,101,100,101,
+ 102,115, 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105,
+ 61, 49, 10,119,104,105,108,101, 32,101,110,118, 46,116,121,
+ 112,101,100,101,102,115, 91,105, 93, 32,100,111, 10,105,102,
+ 32,101,110,118, 46,116,121,112,101,100,101,102,115, 91,105,
+ 93, 46,117,116,121,112,101, 32, 61, 61, 32,116,121,112,101,
+ 32,116,104,101,110, 10,114,101,116,117,114,110, 32, 49, 10,
+ 101,110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100,
+ 10,101,110,100, 10,101,110,118, 32, 61, 32,101,110,118, 46,
+ 112, 97,114,101,110,116, 10,101,110,100, 10,114,101,116,117,
+ 114,110, 32,110,105,108, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,
+ 116, 97,105,110,101,114, 58,100,111,112, 97,114,115,101, 32,
+ 40,115, 41, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44,110, 97,109,101, 44, 98,111,100,121, 32, 61,
+ 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115,
+ 42,109,111,100,117,108,101, 37,115, 37,115, 42, 40, 91, 95,
+ 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40, 37,
+ 98,123,125, 41, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,
+ 116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101,
+ 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101,
+ 41, 10, 77,111,100,117,108,101, 40,110, 97,109,101, 44, 98,
+ 111,100,121, 41, 10,114,101,116,117,114,110, 32,115,116,114,
+ 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,
+ 105,110,100, 40,115, 44, 34, 94, 37,115, 42, 35,100,101,102,
+ 105,110,101, 37,115, 37,115, 42, 40, 91, 94, 37,115, 93, 42,
+ 41, 91, 94, 92,110, 93, 42, 92,110, 37,115, 42, 34, 41, 10,
+ 105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114,
+ 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,
+ 115, 44, 98, 44,101, 41, 10, 68,101,102,105,110,101, 40,110,
+ 97,109,101, 41, 10,114,101,116,117,114,110, 32,115,116,114,
+ 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44, 98,111,100,121, 32, 61, 32,115,116,114,102,
+ 105,110,100, 40,115, 44, 34, 94, 37,115, 42,101,110,117,109,
+ 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59,
+ 63, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,116,104,101,
+ 110, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32, 61, 32,
+ 115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41, 10, 69,
+ 110,117,109,101,114, 97,116,101, 40, 98,111,100,121, 41, 10,
+ 114,101,116,117,114,110, 32,115,116,114,115,117, 98, 40,115,
+ 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100, 10, 10,
+ 100,111, 10,108,111, 99, 97,108, 32, 98, 44,101, 44, 98,111,
+ 100,121, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115, 44, 34, 94, 37,115, 42,116,121,112,101,100,
+ 101,102, 37,115, 37,115, 42,101,110,117,109, 91, 94,123, 93,
+ 42, 40, 37, 98,123,125, 41, 37,115, 42, 40, 91, 37,119, 95,
+ 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42, 59, 37,115, 42,
+ 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,
+ 117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,
+ 117, 98, 40,115, 44, 98, 44,101, 41, 10, 69,110,117,109,101,
+ 114, 97,116,101, 40, 98,111,100,121, 41, 10, 84,121,112,101,
+ 100,101,102, 40, 34,105,110,116, 32, 34, 46, 46,110, 97,109,
+ 101, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117,
+ 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,
+ 101, 44,100,101, 99,108, 44,107,105,110,100, 44, 97,114,103,
+ 44, 99,111,110,115,116, 32, 61, 32,115,116,114,102,105,110,
+ 100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93,
+ 91, 95, 37,119, 37,115, 37, 42, 38, 93, 42,111,112,101,114,
+ 97,116,111,114, 41, 37,115, 42, 40, 91, 94, 37,115, 93, 91,
+ 94, 37,115, 93, 42, 41, 37,115, 42, 40, 37, 98, 40, 41, 41,
+ 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41,
+ 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98, 32,
+ 116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,101,
+ 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101,
+ 41, 10, 79,112,101,114, 97,116,111,114, 40,100,101, 99,108,
+ 44,107,105,110,100, 44, 97,114,103, 44, 99,111,110,115,116,
+ 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117, 98,
+ 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101,
+ 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,115,116,
+ 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94,
+ 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95, 64, 37,119,
+ 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115,
+ 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,
+ 110, 63,115, 63,116, 63, 41, 37,115, 42, 61, 63, 37,115, 42,
+ 48, 63, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32,
+ 110,111,116, 32, 98, 32,116,104,101,110, 10, 10, 98, 44,101,
+ 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,115,116,
+ 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94,
+ 37,115, 42, 40, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37,
+ 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115,
+ 63,116, 63, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,101,
+ 110,100, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,
+ 117,114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,
+ 117, 98, 40,115, 44, 98, 44,101, 41, 10, 70,117,110, 99,116,
+ 105,111,110, 40,100,101, 99,108, 44, 97,114,103, 44, 99,111,
+ 110,115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114,
+ 115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32,
+ 98, 44,101, 44,100,101, 99,108, 44, 97,114,103, 44, 99,111,
+ 110,115,116, 32, 61, 32,115,116,114,102,105,110,100, 40,115,
+ 44, 34, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95,
+ 64, 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93,
+ 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99,
+ 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 37, 98,
+ 123,125, 37,115, 42, 34, 41, 10,105,102, 32,110,111,116, 32,
+ 98, 32,116,104,101,110, 10, 10, 98, 44,101, 44,100,101, 99,
+ 108, 44, 97,114,103, 44, 99,111,110,115,116, 32, 61, 32,115,
+ 116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42, 40,
+ 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41,
+ 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41,
+ 37,115, 42, 37, 98,123,125, 37,115, 42, 34, 41, 10,101,110,
+ 100, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,
+ 114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117,
+ 98, 40,115, 44, 98, 44,101, 41, 10, 70,117,110, 99,116,105,
+ 111,110, 40,100,101, 99,108, 44, 97,114,103, 44, 99,111,110,
+ 115,116, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,
+ 117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98,
+ 44,101, 44,110, 97,109,101, 44, 98, 97,115,101, 44, 98,111,
+ 100,121, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44,
+ 34, 94, 37,115, 42, 99,108, 97,115,115, 37,115, 42, 40, 91,
+ 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40,
+ 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42,
+ 59, 37,115, 42, 34, 41, 10,105,102, 32,110,111,116, 32, 98,
+ 32,116,104,101,110, 10, 98, 44,101, 44,110, 97,109,101, 44,
+ 98, 97,115,101, 44, 98,111,100,121, 32, 61, 32,115,116,114,
+ 102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,115,116,114,
+ 117, 99,116, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,
+ 119, 93, 42, 41, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 40,
+ 37, 98,123,125, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,
+ 105,102, 32,110,111,116, 32, 98, 32,116,104,101,110, 10, 98,
+ 97,115,101, 32, 61, 32, 39, 39, 10, 98, 44,101, 44, 98,111,
+ 100,121, 44,110, 97,109,101, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115, 44, 34, 94, 37,115, 42,116,121,112,101,100,
+ 101,102, 37,115, 37,115, 42,115,116,114,117, 99,116, 37,115,
+ 37,115, 42, 91, 95, 37,119, 93, 42, 37,115, 42, 40, 37, 98,
+ 123,125, 41, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,
+ 119, 93, 42, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,101,
+ 110,100, 10,101,110,100, 10,105,102, 32, 98, 32,116,104,101,
+ 110, 10,105,102, 32, 98, 97,115,101, 32,126, 61, 32, 39, 39,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32, 98, 44,101,
+ 10, 98, 44,101, 44, 98, 97,115,101, 32, 61, 32,115,116,114,
+ 102,105,110,100, 40, 98, 97,115,101, 44, 34, 46, 45, 40, 91,
+ 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 36, 34, 41, 10,
+ 101,110,100, 10, 95, 99,117,114,114, 95, 99,111,100,101, 32,
+ 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,101, 41,
+ 10, 67,108, 97,115,115, 40,110, 97,109,101, 44, 98, 97,115,
+ 101, 44, 98,111,100,121, 41, 10,114,101,116,117,114,110, 32,
+ 115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,
+ 110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111, 99,
+ 97,108, 32, 98, 44,101, 44,116,121,112,101,115, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,
+ 116,121,112,101,100,101,102, 37,115, 37,115, 42, 40, 46, 45,
+ 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,105,102, 32, 98,
+ 32,116,104,101,110, 10, 95, 99,117,114,114, 95, 99,111,100,
+ 101, 32, 61, 32,115,116,114,115,117, 98, 40,115, 44, 98, 44,
+ 101, 41, 10, 84,121,112,101,100,101,102, 40,116,121,112,101,
+ 115, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,117,
+ 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98, 44,
+ 101, 44,100,101, 99,108, 32, 61, 32,115,116,114,102,105,110,
+ 100, 40,115, 44, 34, 94, 37,115, 42, 40, 91, 95, 37,119, 93,
+ 91, 95, 64, 37,115, 37,119, 37,100, 37, 42, 38, 93, 42, 91,
+ 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 34,
+ 41, 10,105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,
+ 114,114, 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117,
+ 98, 40,115, 44, 98, 44,101, 41, 10, 86, 97,114,105, 97, 98,
+ 108,101, 40,100,101, 99,108, 41, 10,114,101,116,117,114,110,
+ 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,
+ 101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,111,
+ 99, 97,108, 32, 98, 44,101, 44,100,101, 99,108, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115, 42,
+ 40, 91, 95, 37,119, 93, 91, 93, 91, 95, 64, 37,115, 37,119,
+ 37,100, 37, 42, 38, 37, 45, 37, 62, 93, 42, 91, 93, 95, 37,
+ 119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 34, 41, 10,
+ 105,102, 32, 98, 32,116,104,101,110, 10, 95, 99,117,114,114,
+ 95, 99,111,100,101, 32, 61, 32,115,116,114,115,117, 98, 40,
+ 115, 44, 98, 44,101, 41, 10, 65,114,114, 97,121, 40,100,101,
+ 99,108, 41, 10,114,101,116,117,114,110, 32,115,116,114,115,
+ 117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10,100,111, 10,108,111, 99, 97,108, 32, 98,
+ 44,101, 44, 99,111,100,101, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115, 44, 34, 94, 37,115, 42, 40, 37, 98, 92, 49,
+ 92, 50, 41, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110,
+ 10, 67,111,100,101, 40,115,116,114,115,117, 98, 40, 99,111,
+ 100,101, 44, 50, 44, 45, 50, 41, 41, 10,114,101,116,117,114,
+ 110, 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41,
+ 10,101,110,100, 10,101,110,100, 10, 10, 10,100,111, 10,108,
+ 111, 99, 97,108, 32, 98, 44,101, 44,108,105,110,101, 32, 61,
+ 32,115,116,114,102,105,110,100, 40,115, 44, 34, 94, 37,115,
+ 42, 37, 36, 40, 46, 45, 92,110, 41, 34, 41, 10,105,102, 32,
+ 98, 32,116,104,101,110, 10, 86,101,114, 98, 97,116,105,109,
+ 40,108,105,110,101, 41, 10,114,101,116,117,114,110, 32,115,
+ 116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10,105,102, 32,103,115,117, 98,
+ 40,115, 44, 34, 37,115, 37,115, 42, 34, 44, 34, 34, 41, 32,
+ 126, 61, 32, 34, 34, 32,116,104,101,110, 10, 95, 99,117,114,
+ 114, 95, 99,111,100,101, 32, 61, 32,115, 10,101,114,114,111,
+ 114, 40, 34, 35,112, 97,114,115,101, 32,101,114,114,111,114,
+ 34, 41, 10,101,108,115,101, 10,114,101,116,117,114,110, 32,
+ 34, 34, 10,101,110,100, 10,101,110,100, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 67,111,110,116,
+ 97,105,110,101,114, 58,112, 97,114,115,101, 32, 40,115, 41,
+ 10,119,104,105,108,101, 32,115, 32,126, 61, 32, 39, 39, 32,
+ 100,111, 10,115, 32, 61, 32,115,101,108,102, 58,100,111,112,
+ 97,114,115,101, 40,115, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 99,108, 97,115,115, 80, 97, 99,107, 97,
+ 103,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32, 61, 32,
+ 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 44,
+ 10,116,121,112,101, 32, 61, 32, 39,112, 97, 99,107, 97,103,
+ 101, 39, 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,
+ 115,115, 80, 97, 99,107, 97,103,101, 44,116,111,108,117, 97,
+ 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 58,
+ 112,114,105,110,116, 32, 40, 41, 10,112,114,105,110,116, 40,
+ 34, 80, 97, 99,107, 97,103,101, 58, 32, 34, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 41, 10,108,111, 99, 97,108, 32,
+ 105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,
+ 105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,112,
+ 114,105,110,116, 40, 34, 34, 44, 34, 34, 41, 10,105, 32, 61,
+ 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97,
+ 99,107, 97,103,101, 58,112,114,101,112,114,111, 99,101,115,
+ 115, 32, 40, 41, 10,115,101,108,102, 46, 99,111,100,101, 32,
+ 61, 32, 34, 92,110, 34, 46, 46,115,101,108,102, 46, 99,111,
+ 100,101, 10, 10,108,111, 99, 97,108, 32, 86, 32, 61, 32,123,
+ 125, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,
+ 115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,
+ 92,110, 40, 37,115, 42, 37, 36, 91, 94, 37, 91, 37, 93, 93,
+ 91, 94, 92,110, 93, 42, 41, 34, 44,102,117,110, 99,116,105,
+ 111,110, 32, 40,118, 41, 10,116,105,110,115,101,114,116, 40,
+ 37, 86, 44,118, 41, 10,114,101,116,117,114,110, 32, 34, 92,
+ 110, 36, 34, 46, 46,103,101,116,110, 40, 37, 86, 41, 46, 46,
+ 34, 36, 34, 10,101,110,100, 41, 10, 10,108,111, 99, 97,108,
+ 32, 67, 32, 61, 32,123,125, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 37, 91,
+ 34, 44, 34, 92, 49, 34, 41, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 37, 93,
+ 34, 44, 34, 92, 50, 34, 41, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 40, 37, 98, 92, 49, 92, 50, 41, 34,
+ 44, 32,102,117,110, 99,116,105,111,110, 32, 40, 99, 41, 10,
+ 116,105,110,115,101,114,116, 40, 37, 67, 44, 99, 41, 10,114,
+ 101,116,117,114,110, 32, 34, 92,110, 36, 91, 34, 46, 46,103,
+ 101,116,110, 40, 37, 67, 41, 46, 46, 34, 93, 36, 34, 10,101,
+ 110,100, 41, 10, 10, 10,115,101,108,102, 46, 99,111,100,101,
+ 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,
+ 100,101, 44, 34, 40, 47, 47, 91, 94, 92,110, 93, 42, 41, 34,
+ 44, 34, 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32,
+ 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,
+ 101, 44, 34, 47, 37, 42, 34, 44, 34, 92, 49, 34, 41, 10,115,
+ 101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98,
+ 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 37, 42, 47,
+ 34, 44, 34, 92, 50, 34, 41, 10,115,101,108,102, 46, 99,111,
+ 100,101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46,
+ 99,111,100,101, 44, 34, 37, 98, 92, 49, 92, 50, 34, 44, 34,
+ 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,
+ 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44,
+ 34, 92, 49, 34, 44, 34, 47, 37, 42, 34, 41, 10,115,101,108,
+ 102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,
+ 101,108,102, 46, 99,111,100,101, 44, 34, 92, 50, 34, 44, 34,
+ 37, 42, 47, 34, 41, 10,115,101,108,102, 46, 99,111,100,101,
+ 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,111,
+ 100,101, 44, 34, 37,115, 42, 64, 37,115, 42, 34, 44, 34, 64,
+ 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,
+ 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44,
+ 34, 37,115, 63,105,110,108,105,110,101, 40, 37,115, 41, 34,
+ 44, 34, 37, 49, 34, 41, 10,115,101,108,102, 46, 99,111,100,
+ 101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,
+ 111,100,101, 44, 34, 37,115, 63,101,120,116,101,114,110, 40,
+ 37,115, 41, 34, 44, 34, 37, 49, 34, 41, 10,115,101,108,102,
+ 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,101,
+ 108,102, 46, 99,111,100,101, 44, 34, 37,115, 63,118,105,114,
+ 116,117, 97,108, 40, 37,115, 41, 34, 44, 34, 37, 49, 34, 41,
+ 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,
+ 117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,112,
+ 117, 98,108,105, 99, 58, 34, 44, 34, 34, 41, 10,115,101,108,
+ 102, 46, 99,111,100,101, 32, 61, 32,103,115,117, 98, 40,115,
+ 101,108,102, 46, 99,111,100,101, 44, 34, 40, 91, 94, 37,119,
+ 95, 93, 41,118,111,105,100, 37,115, 42, 37, 42, 34, 44, 34,
+ 37, 49, 95,117,115,101,114,100, 97,116, 97, 32, 34, 41, 10,
+ 115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,115,117,
+ 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34, 40, 91,
+ 94, 37,119, 95, 93, 41,118,111,105,100, 37,115, 42, 37, 42,
+ 34, 44, 34, 37, 49, 95,117,115,101,114,100, 97,116, 97, 32,
+ 34, 41, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,
+ 103,115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44,
+ 34, 40, 91, 94, 37,119, 95, 93, 41, 99,104, 97,114, 37,115,
+ 42, 37, 42, 34, 44, 34, 37, 49, 95, 99,115,116,114,105,110,
+ 103, 32, 34, 41, 10, 10, 10,115,101,108,102, 46, 99,111,100,
+ 101, 32, 61, 32,103,115,117, 98, 40,115,101,108,102, 46, 99,
+ 111,100,101, 44, 34, 37, 36, 37, 91, 40, 37,100, 43, 41, 37,
+ 93, 37, 36, 34, 44,102,117,110, 99,116,105,111,110, 32, 40,
+ 110, 41, 10,114,101,116,117,114,110, 32, 37, 67, 91,116,111,
+ 110,117,109, 98,101,114, 40,110, 41, 93, 10,101,110,100, 41,
+ 10, 10,115,101,108,102, 46, 99,111,100,101, 32, 61, 32,103,
+ 115,117, 98, 40,115,101,108,102, 46, 99,111,100,101, 44, 34,
+ 37, 36, 40, 37,100, 43, 41, 37, 36, 34, 44,102,117,110, 99,
+ 116,105,111,110, 32, 40,110, 41, 10,114,101,116,117,114,110,
+ 32, 37, 86, 91,116,111,110,117,109, 98,101,114, 40,110, 41,
+ 93, 10,101,110,100, 41, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,
+ 107, 97,103,101, 58,112,114,101, 97,109, 98,108,101, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 92,110, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 76,117,
+ 97, 32, 98,105,110,100,105,110,103, 58, 32, 39, 46, 46,115,
+ 101,108,102, 46,110, 97,109,101, 46, 46, 39, 92,110, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 71,101,110,
+ 101,114, 97,116,101,100, 32, 97,117,116,111,109, 97,116,105,
+ 99, 97,108,108,121, 32, 98,121, 32, 39, 46, 46, 84, 79, 76,
+ 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 46, 46, 39, 32,111,
+ 110, 32, 39, 46, 46,100, 97,116,101, 40, 41, 46, 46, 39, 46,
+ 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 47,
+ 92,110, 92,110, 39, 41, 10, 10,111,117,116,112,117,116, 40,
+ 39, 35,105,110, 99,108,117,100,101, 32, 34,108,117, 97, 47,
+ 116,111,108,117, 97, 46,104, 34, 92,110, 92,110, 39, 41, 10,
+ 10,105,102, 32,110,111,116, 32,102,108, 97,103,115, 46,104,
+ 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 47,
+ 42, 32, 69,120,112,111,114,116,101,100, 32,102,117,110, 99,
+ 116,105,111,110, 32, 42, 47, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39,105,110,116, 32,116,111,108,117, 97, 95, 39, 46,
+ 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 95,111,
+ 112,101,110, 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42,
+ 32,116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39,118,111,105,100, 32,116,111,108,117,
+ 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46,
+ 46, 39, 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83,
+ 116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 59,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41,
+ 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,105, 61, 49,
+ 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,
+ 100,111, 10,115,101,108,102, 91,105, 93, 58,112,114,101, 97,
+ 109, 98,108,101, 40, 41, 10,105, 32, 61, 32,105, 43, 49, 10,
+ 101,110,100, 10,111,117,116,112,117,116, 40, 39, 92,110, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 32,102,117,
+ 110, 99,116,105,111,110, 32,116,111, 32,114,101,103,105,115,
+ 116,101,114, 32,116,121,112,101, 32, 42, 47, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,115,116, 97,116,105, 99, 32,118,
+ 111,105,100, 32,116,111,108,117, 97, 73, 95,114,101,103, 95,
+ 116,121,112,101,115, 32, 40,108,117, 97, 95, 83,116, 97,116,
+ 101, 42, 32,116,111,108,117, 97, 95, 83, 41, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,123, 39, 41, 10,102,111,114,101,
+ 97, 99,104, 40, 95,117,115,101,114,116,121,112,101, 44,102,
+ 117,110, 99,116,105,111,110, 40,110, 44,118, 41, 32,111,117,
+ 116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,117,115,
+ 101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 44,118, 44, 39, 34, 41, 59, 39, 41, 32,101,110,100,
+ 41, 10,111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 92,110, 39, 41, 10, 10,111,117,
+ 116,112,117,116, 40, 39, 47, 42, 32,101,114,114,111,114, 32,
+ 109,101,115,115, 97,103,101,115, 32, 42, 47, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 35,100,101,102,105,110,101, 32,
+ 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 32,
+ 116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,
+ 117, 97, 95, 83, 44, 92, 34,105,110,118, 97,108,105,100, 32,
+ 92, 39,115,101,108,102, 92, 39, 92, 34, 41, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 35,100,101,102,105,110,101, 32,
+ 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 65, 83, 83, 73, 71,
+ 78, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,
+ 111,108,117, 97, 95, 83, 44, 92, 34, 35,118,105,110,118, 97,
+ 108,105,100, 32,116,121,112,101, 32,105,110, 32,118, 97,114,
+ 105, 97, 98,108,101, 32, 97,115,115,105,103,110,109,101,110,
+ 116, 46, 92, 34, 41, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 92,110, 39, 41, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80, 97, 99,
+ 107, 97,103,101, 58,114,101,103,105,115,116,101,114, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32, 79,112,
+ 101,110, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 34,
+ 41, 10,111,117,116,112,117,116, 40, 34,105,110,116, 32,116,
+ 111,108,117, 97, 95, 34, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 34, 95,111,112,101,110, 32, 40,108,117, 97,
+ 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83,
+ 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41,
+ 10,111,117,116,112,117,116, 40, 34, 32,116,111,108,117, 97,
+ 95,111,112,101,110, 40,116,111,108,117, 97, 95, 83, 41, 59,
+ 34, 41, 10,111,117,116,112,117,116, 40, 34, 32,116,111,108,
+ 117, 97, 73, 95,114,101,103, 95,116,121,112,101,115, 40,116,
+ 111,108,117, 97, 95, 83, 41, 59, 34, 41, 10,108,111, 99, 97,
+ 108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,
+ 102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93,
+ 58,114,101,103,105,115,116,101,114, 40, 41, 10,105, 32, 61,
+ 32,105, 43, 49, 10,101,110,100, 10,111,117,116,112,117,116,
+ 40, 34, 32,114,101,116,117,114,110, 32, 49, 59, 34, 41, 10,
+ 111,117,116,112,117,116, 40, 34,125, 34, 41, 10,101,110,100,
+ 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 80, 97, 99,107, 97,103,101, 58,117,110,114,101,
+ 103,105,115,116,101,114, 32, 40, 41, 10,111,117,116,112,117,
+ 116, 40, 34, 47, 42, 32, 67,108,111,115,101, 32,102,117,110,
+ 99,116,105,111,110, 32, 42, 47, 34, 41, 10,111,117,116,112,
+ 117,116, 40, 34,118,111,105,100, 32,116,111,108,117, 97, 95,
+ 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34,
+ 95, 99,108,111,115,101, 32, 40,108,117, 97, 95, 83,116, 97,
+ 116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,
+ 111,117,116,112,117,116, 40, 34,123, 34, 41, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105,
+ 93, 58,117,110,114,101,103,105,115,116,101,114, 40, 41, 10,
+ 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,111,117,116,
+ 112,117,116, 40, 34,125, 34, 41, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 80,
+ 97, 99,107, 97,103,101, 58,104,101, 97,100,101,114, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 39, 47, 42, 92,110, 39,
+ 41, 32,111,117,116,112,117,116, 40, 39, 42, 42, 32, 76,117,
+ 97, 32, 98,105,110,100,105,110,103, 58, 32, 39, 46, 46,115,
+ 101,108,102, 46,110, 97,109,101, 46, 46, 39, 92,110, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 42, 42, 32, 71,101,110,
+ 101,114, 97,116,101,100, 32, 97,117,116,111,109, 97,116,105,
+ 99, 97,108,108,121, 32, 98,121, 32, 39, 46, 46, 84, 79, 76,
+ 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 46, 46, 39, 32,111,
+ 110, 32, 39, 46, 46,100, 97,116,101, 40, 41, 46, 46, 39, 46,
+ 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39, 42, 47,
+ 92,110, 92,110, 39, 41, 10, 10,105,102, 32,110,111,116, 32,
+ 102,108, 97,103,115, 46,104, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 47, 42, 32, 69,120,112,111,114,116,
+ 101,100, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39,105,110,116, 32,116,
+ 111,108,117, 97, 95, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 39, 95,111,112,101,110, 32, 40,108,117, 97,
+ 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83,
+ 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,118,111,
+ 105,100, 32,116,111,108,117, 97, 95, 39, 46, 46,115,101,108,
+ 102, 46,110, 97,109,101, 46, 46, 39, 95, 99,108,111,115,101,
+ 32, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,
+ 108,117, 97, 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 80, 97,
+ 99,107, 97,103,101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,
+ 115,101, 32, 61, 32, 99,108, 97,115,115, 80, 97, 99,107, 97,
+ 103,101, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10,114,101,116,117,114,110, 32,
+ 116, 10,101,110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,
+ 105,111,110, 32, 80, 97, 99,107, 97,103,101, 32, 40,110, 97,
+ 109,101, 41, 10, 10,108,111, 99, 97,108, 32, 99,111,100,101,
+ 32, 61, 32,114,101, 97,100, 40, 34, 42, 97, 34, 41, 10, 99,
+ 111,100,101, 32, 61, 32, 34, 92,110, 34, 32, 46, 46, 32, 99,
+ 111,100,101, 10, 10,108,111, 99, 97,108, 32,110,115,117, 98,
+ 115,116, 10,114,101,112,101, 97,116, 10, 99,111,100,101, 44,
+ 110,115,117, 98,115,116, 32, 61, 32,103,115,117, 98, 40, 99,
+ 111,100,101, 44, 34, 92,110, 37,115, 42, 37, 36, 60, 40, 46,
+ 45, 41, 62, 37,115, 42, 92,110, 34, 44,102,117,110, 99,116,
+ 105,111,110, 32, 40,102,110, 41, 10,108,111, 99, 97,108, 32,
+ 102,112, 44,109,115,103, 32, 61, 32,111,112,101,110,102,105,
+ 108,101, 40,102,110, 44, 39,114, 39, 41, 10,105,102, 32,110,
+ 111,116, 32,102,112, 32,116,104,101,110, 10,101,114,114,111,
+ 114, 40, 39, 35, 39, 46, 46,109,115,103, 46, 46, 39, 58, 32,
+ 39, 46, 46,102,110, 41, 10,101,110,100, 10,108,111, 99, 97,
+ 108, 32,115, 32, 61, 32,114,101, 97,100, 40,102,112, 44, 39,
+ 42, 97, 39, 41, 10, 99,108,111,115,101,102,105,108,101, 40,
+ 102,112, 41, 10,114,101,116,117,114,110, 32, 34, 92,110, 34,
+ 32, 46, 46, 32,115, 10,101,110,100, 41, 10,117,110,116,105,
+ 108, 32,110,115,117, 98,115,116, 61, 61, 48, 10, 10, 10,108,
+ 111, 99, 97,108, 32,110,115,117, 98,115,116, 10,114,101,112,
+ 101, 97,116, 10, 99,111,100,101, 44,110,115,117, 98,115,116,
+ 32, 61, 10,103,115,117, 98, 40, 99,111,100,101, 44, 34, 92,
+ 110, 37,115, 42, 37, 36,123, 40, 46, 45, 41,125, 37,115, 42,
+ 92,110, 34, 44, 10,102,117,110, 99,116,105,111,110, 32, 40,
+ 102,110, 41, 10,108,111, 99, 97,108, 32,102,112, 44,109,115,
+ 103, 32, 61, 32,111,112,101,110,102,105,108,101, 40,102,110,
+ 44, 39,114, 39, 41, 10,105,102, 32,110,111,116, 32,102,112,
+ 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39,
+ 46, 46,109,115,103, 46, 46, 39, 58, 32, 39, 46, 46,102,110,
+ 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,115, 32, 61,
+ 32,114,101, 97,100, 40,102,112, 44, 39, 42, 97, 39, 41, 10,
+ 99,108,111,115,101,102,105,108,101, 40,102,112, 41, 10, 10,
+ 108,111, 99, 97,108, 32, 84, 32, 61, 32,123, 99,111,100,101,
+ 61, 34, 92,110, 34,125, 10,115, 61, 32, 34, 92,110, 34, 32,
+ 46, 46, 32,115, 32, 46, 46, 32, 34, 92,110, 34, 10, 10,103,
+ 115,117, 98, 40,115, 44, 34, 92,110, 40, 46, 45, 41, 91, 84,
+ 116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85,117, 93, 91,
+ 65, 97, 93, 95, 91, 69,101, 93, 91, 88,120, 93, 91, 80,112,
+ 93, 91, 79,111, 93, 91, 82,114, 93, 91, 84,116, 93, 91, 94,
+ 92,110, 93, 42, 92,110, 34, 44, 10,102,117,110, 99,116,105,
+ 111,110, 32, 40, 99, 41, 32, 37, 84, 46, 99,111,100,101, 32,
+ 61, 32, 37, 84, 46, 99,111,100,101, 32, 46, 46, 32, 99, 32,
+ 46, 46, 32, 34, 92,110, 34, 32,101,110,100, 10, 41, 10, 10,
+ 103,115,117, 98, 40,115, 44, 34, 92,110, 91, 94, 92,110, 93,
+ 42, 91, 84,116, 93, 91, 79,111, 93, 91, 76,108, 93, 91, 85,
+ 117, 93, 91, 65, 97, 93, 95, 91, 66, 98, 93, 91, 69,101, 93,
+ 91, 71,103, 93, 91, 73,105, 93, 91, 78,110, 93, 91, 94, 92,
+ 110, 93, 42, 34, 46, 46, 10, 34, 40, 46, 45, 41, 34, 32, 46,
+ 46, 10, 34, 92,110, 91, 94, 92,110, 93, 42, 91, 84,116, 93,
+ 91, 79,111, 93, 91, 76,108, 93, 91, 85,117, 93, 91, 65, 97,
+ 93, 95, 91, 69,101, 93, 91, 78,110, 93, 91, 68,100, 93, 91,
+ 94, 92,110, 93, 42, 92,110, 34, 44, 10,102,117,110, 99,116,
+ 105,111,110, 32, 40, 99, 41, 32, 37, 84, 46, 99,111,100,101,
+ 32, 61, 32, 37, 84, 46, 99,111,100,101, 32, 46, 46, 32, 99,
+ 32, 46, 46, 32, 34, 92,110, 34, 32,101,110,100, 10, 41, 10,
+ 114,101,116,117,114,110, 32, 84, 46, 99,111,100,101, 10,101,
+ 110,100, 41, 10,117,110,116,105,108, 32,110,115,117, 98,115,
+ 116, 61, 61, 48, 10, 10,108,111, 99, 97,108, 32,116, 32, 61,
+ 32, 95, 80, 97, 99,107, 97,103,101, 40, 95, 67,111,110,116,
+ 97,105,110,101,114,123,110, 97,109,101, 61,110, 97,109,101,
+ 44, 32, 99,111,100,101, 61, 99,111,100,101,125, 41, 10,112,
+ 117,115,104, 40,116, 41, 10,116, 58,112,114,101,112,114,111,
+ 99,101,115,115, 40, 41, 10,116, 58,112, 97,114,115,101, 40,
+ 116, 46, 99,111,100,101, 41, 10,112,111,112, 40, 41, 10,114,
+ 101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,
+ 108, 97,115,115, 77,111,100,117,108,101, 32, 61, 32,123, 10,
+ 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 67,111,
+ 110,116, 97,105,110,101,114, 44, 10,116,121,112,101, 32, 61,
+ 32, 39,109,111,100,117,108,101, 39, 10,125, 10,115,101,116,
+ 116, 97,103, 40, 99,108, 97,115,115, 77,111,100,117,108,101,
+ 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 77,111,
+ 100,117,108,101, 58,114,101,103,105,115,116,101,114, 32, 40,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,109,111,100,117,108,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 34, 41, 59, 39, 41, 10,108,111, 99, 97,108, 32,
+ 105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102, 91,
+ 105, 93, 32,100,111, 10,115,101,108,102, 91,105, 93, 58,114,
+ 101,103,105,115,116,101,114, 40, 41, 10,105, 32, 61, 32,105,
+ 43, 49, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 77,111,100,
+ 117,108,101, 58,117,110,114,101,103,105,115,116,101,114, 32,
+ 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97,
+ 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95,
+ 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 34, 41, 59,
+ 39, 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 77,111,100,117,108,101, 58,
+ 112,114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,
+ 111,115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 77,111,100,117,108,101,123, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,110,
+ 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 110, 97,109,101, 46, 46, 34, 39, 59, 34, 41, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 91,105, 93, 32,100,111, 10,115,101,108,102, 91,105,
+ 93, 58,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32, 34, 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41,
+ 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 95, 77,111,100,117,108,101, 32, 40,116, 41, 10,116, 46,
+ 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 77,111,
+ 100,117,108,101, 10,115,101,116,116, 97,103, 40,116, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,110,
+ 100, 40,116, 41, 10,114,101,116,117,114,110, 32,116, 10,101,
+ 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 77,111,100,117,108,101, 32, 40,110, 44, 98, 41, 10,108,111,
+ 99, 97,108, 32,116, 32, 61, 32, 95, 77,111,100,117,108,101,
+ 40, 95, 67,111,110,116, 97,105,110,101,114,123,110, 97,109,
+ 101, 61,110,125, 41, 10,112,117,115,104, 40,116, 41, 10,116,
+ 58,112, 97,114,115,101, 40,115,116,114,115,117, 98, 40, 98,
+ 44, 50, 44,115,116,114,108,101,110, 40, 98, 41, 45, 49, 41,
+ 41, 10,112,111,112, 40, 41, 10,114,101,116,117,114,110, 32,
+ 116, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 68,
+ 101,102,105,110,101, 32, 61, 32,123, 10,110, 97,109,101, 32,
+ 61, 32, 39, 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,
+ 108, 97,115,115, 70,101, 97,116,117,114,101, 44, 10,125, 10,
+ 115,101,116,116, 97,103, 40, 99,108, 97,115,115, 68,101,102,
+ 105,110,101, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 68,101,102,105,110,101, 58,114,101,103,105,115,116,101,
+ 114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,
+ 115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41,
+ 10,105,102, 32,112, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,
+ 116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39,
+ 46, 46,112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,
+ 102, 46,108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39,
+ 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116,
+ 40,116,111,108,117, 97, 95, 83, 44, 78, 85, 76, 76, 44, 34,
+ 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46,
+ 39, 34, 44, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 68,101,102,105,110,101, 58,117,110,114,101,103,105,
+ 115,116,101,114, 32, 40, 41, 10,105,102, 32,110,111,116, 32,
+ 115,101,108,102, 58,105,110,109,111,100,117,108,101, 40, 41,
+ 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,
+ 108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,108,
+ 117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,
+ 108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34,
+ 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46,
+ 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 68,101,102,105,110,101, 58,112,114,105,110,116, 32, 40,
+ 105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 68,101,102,
+ 105,110,101,123, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39,
+ 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,108,110, 97,109,101, 32, 61, 32, 39, 34,
+ 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 95, 68,101,102,105,110,101, 32, 40,116, 41, 10,116, 46,
+ 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 68,101,
+ 102,105,110,101, 10,115,101,116,116, 97,103, 40,116, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,116,
+ 46,110, 97,109,101, 32, 61, 61, 32, 39, 39, 32,116,104,101,
+ 110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,
+ 105,100, 32,100,101,102,105,110,101, 34, 41, 10,101,110,100,
+ 10, 10, 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,
+ 117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 68,101,102,105,110,101, 32, 40,
+ 110, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,
+ 108,105,116, 40,110, 44, 39, 64, 39, 41, 10,114,101,116,117,
+ 114,110, 32, 95, 68,101,102,105,110,101, 32,123, 10,110, 97,
+ 109,101, 32, 61, 32,116, 91, 49, 93, 44, 10,108,110, 97,109,
+ 101, 32, 61, 32,116, 91, 50, 93, 32,111,114, 32,116, 91, 49,
+ 93, 10,125, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 69,
+ 110,117,109,101,114, 97,116,101, 32, 61, 32,123, 10, 95, 98,
+ 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,
+ 117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40, 99,
+ 108, 97,115,115, 69,110,117,109,101,114, 97,116,101, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 69,110,117,109,
+ 101,114, 97,116,101, 58,114,101,103,105,115,116,101,114, 32,
+ 40, 41, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,115,101,
+ 108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,111,114,
+ 32,115,101,108,102, 58,105,110,109,111,100,117,108,101, 40,
+ 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,
+ 108,101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,105,
+ 102, 32,112, 32,116,104,101,110, 10,105,102, 32,115,101,108,
+ 102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95, 99,111,110,115,116, 97,110,116, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,112, 46, 46, 39, 34, 44, 34,
+ 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101,115, 91,
+ 105, 93, 46, 46, 39, 34, 44, 39, 46, 46,112, 46, 46, 39, 58,
+ 58, 39, 46, 46,115,101,108,102, 91,105, 93, 46, 46, 39, 41,
+ 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116,
+ 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,
+ 110,116, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,
+ 112, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102, 46,
+ 108,110, 97,109,101,115, 91,105, 93, 46, 46, 39, 34, 44, 39,
+ 46, 46,115,101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,108,115,101, 10,111,117,116,112,
+ 117,116, 40, 39, 32,116,111,108,117, 97, 95, 99,111,110,115,
+ 116, 97,110,116, 40,116,111,108,117, 97, 95, 83, 44, 78, 85,
+ 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,
+ 109,101,115, 91,105, 93, 46, 46, 39, 34, 44, 39, 46, 46,115,
+ 101,108,102, 91,105, 93, 46, 46, 39, 41, 59, 39, 41, 10,101,
+ 110,100, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,
+ 101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 69,110,117,109,101,114, 97,116,101, 58,117,
+ 110,114,101,103,105,115,116,101,114, 32, 40, 41, 10,105,102,
+ 32,115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41,
+ 61, 61,110,105,108, 32, 97,110,100, 32,115,101,108,102, 58,
+ 105,110,109,111,100,117,108,101, 40, 41, 61, 61,110,105,108,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49,
+ 10,119,104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,
+ 100,111, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97,
+ 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95,
+ 83, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,
+ 115,101,108,102, 46,108,110, 97,109,101,115, 91,105, 93, 46,
+ 46, 39, 34, 41, 59, 39, 41, 10,105, 32, 61, 32,105, 43, 49,
+ 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 69,
+ 110,117,109,101,114, 97,116,101, 58,112,114,105,110,116, 32,
+ 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 69,110,
+ 117,109,101,114, 97,116,101,123, 34, 41, 10,108,111, 99, 97,
+ 108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,
+ 102, 91,105, 93, 32,100,111, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32, 39, 34, 46, 46,115,101,108,
+ 102, 91,105, 93, 46, 46, 34, 39, 40, 34, 46, 46,115,101,108,
+ 102, 46,108,110, 97,109,101,115, 91,105, 93, 46, 46, 34, 41,
+ 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 69,110,117,
+ 109,101,114, 97,116,101, 32, 40,116, 41, 10,116, 46, 95, 98,
+ 97,115,101, 32, 61, 32, 99,108, 97,115,115, 69,110,117,109,
+ 101,114, 97,116,101, 10,115,101,116,116, 97,103, 40,116, 44,
+ 116,111,108,117, 97, 95,116, 97,103, 41, 10, 97,112,112,101,
+ 110,100, 40,116, 41, 10,114,101,116,117,114,110, 32,116, 10,
+ 101,110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 69,110,117,109,101,114, 97,116,101, 32, 40, 98, 41, 10,
+ 108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116,
+ 40,115,116,114,115,117, 98, 40, 98, 44, 50, 44, 45, 50, 41,
+ 44, 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 32, 61,
+ 32, 49, 10,108,111, 99, 97,108, 32,101, 32, 61, 32,123,110,
+ 61, 48,125, 10,119,104,105,108,101, 32,116, 91,105, 93, 32,
+ 100,111, 10,108,111, 99, 97,108, 32,116,116, 32, 61, 32,115,
+ 112,108,105,116, 40,116, 91,105, 93, 44, 39, 61, 39, 41, 10,
+ 101, 46,110, 32, 61, 32,101, 46,110, 32, 43, 32, 49, 10,101,
+ 91,101, 46,110, 93, 32, 61, 32,116,116, 91, 49, 93, 10,105,
+ 32, 61, 32,105, 43, 49, 10,101,110,100, 10, 10,105, 32, 61,
+ 32, 49, 10,101, 46,108,110, 97,109,101,115, 32, 61, 32,123,
+ 125, 10,119,104,105,108,101, 32,101, 91,105, 93, 32,100,111,
+ 10,108,111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,
+ 116, 40,101, 91,105, 93, 44, 39, 64, 39, 41, 10,101, 91,105,
+ 93, 32, 61, 32,116, 91, 49, 93, 10,101, 46,108,110, 97,109,
+ 101,115, 91,105, 93, 32, 61, 32,116, 91, 50, 93, 32,111,114,
+ 32,116, 91, 49, 93, 10,105, 32, 61, 32,105, 43, 49, 10,101,
+ 110,100, 10,114,101,116,117,114,110, 32, 95, 69,110,117,109,
+ 101,114, 97,116,101, 40,101, 41, 10,101,110,100, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110, 32, 61, 32,123, 10, 95, 98,
+ 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,116,
+ 117,114,101, 44, 10,109,111,100, 32, 61, 32, 39, 39, 44, 10,
+ 116,121,112,101, 32, 61, 32, 39, 39, 44, 10,112,116,114, 32,
+ 61, 32, 39, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39,
+ 44, 10,100,105,109, 32, 61, 32, 39, 39, 44, 10,114,101,116,
+ 32, 61, 32, 39, 39, 44, 10,100,101,102, 32, 61, 32, 39, 39,
+ 10,125, 10,115,101,116,116, 97,103, 40, 99,108, 97,115,115,
+ 68,101, 99,108, 97,114, 97,116,105,111,110, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,
+ 105,111,110, 32, 99,114,101, 97,116,101, 95,118, 97,114,110,
+ 97,109,101, 32, 40, 41, 10,105,102, 32,110,111,116, 32, 95,
+ 118, 97,114,110,117,109, 98,101,114, 32,116,104,101,110, 32,
+ 95,118, 97,114,110,117,109, 98,101,114, 32, 61, 32, 48, 32,
+ 101,110,100, 10, 95,118, 97,114,110,117,109, 98,101,114, 32,
+ 61, 32, 95,118, 97,114,110,117,109, 98,101,114, 32, 43, 32,
+ 49, 10,114,101,116,117,114,110, 32, 34,116,111,108,117, 97,
+ 95,118, 97,114, 95, 34, 46, 46, 95,118, 97,114,110,117,109,
+ 98,101,114, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,
+ 114, 97,116,105,111,110, 58, 99,104,101, 99,107,110, 97,109,
+ 101, 32, 40, 41, 10, 10,105,102, 32,115,116,114,115,117, 98,
+ 40,115,101,108,102, 46,110, 97,109,101, 44, 49, 44, 49, 41,
+ 32, 61, 61, 32, 39, 91, 39, 32, 97,110,100, 32,110,111,116,
+ 32,105,115,116,121,112,101, 40,115,101,108,102, 46,116,121,
+ 112,101, 41, 32,116,104,101,110, 10,115,101,108,102, 46,110,
+ 97,109,101, 32, 61, 32,115,101,108,102, 46,116,121,112,101,
+ 46, 46,115,101,108,102, 46,110, 97,109,101, 10,108,111, 99,
+ 97,108, 32,109, 32, 61, 32,115,112,108,105,116, 40,115,101,
+ 108,102, 46,109,111,100, 44, 39, 37,115, 37,115, 42, 39, 41,
+ 10,115,101,108,102, 46,116,121,112,101, 32, 61, 32,109, 91,
+ 109, 46,110, 93, 10,115,101,108,102, 46,109,111,100, 32, 61,
+ 32, 99,111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110,
+ 45, 49, 41, 10,101,110,100, 10, 10,108,111, 99, 97,108, 32,
+ 116, 32, 61, 32,115,112,108,105,116, 40,115,101,108,102, 46,
+ 110, 97,109,101, 44, 39, 61, 39, 41, 10,105,102, 32,116, 46,
+ 110, 61, 61, 50, 32,116,104,101,110, 10,115,101,108,102, 46,
+ 110, 97,109,101, 32, 61, 32,116, 91, 49, 93, 10,115,101,108,
+ 102, 46,100,101,102, 32, 61, 32,116, 91,116, 46,110, 93, 10,
+ 101,110,100, 10, 10,108,111, 99, 97,108, 32, 98, 44,101, 44,
+ 100, 32, 61, 32,115,116,114,102,105,110,100, 40,115,101,108,
+ 102, 46,110, 97,109,101, 44, 34, 37, 91, 40, 46, 45, 41, 37,
+ 93, 34, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10,115,
+ 101,108,102, 46,110, 97,109,101, 32, 61, 32,115,116,114,115,
+ 117, 98, 40,115,101,108,102, 46,110, 97,109,101, 44, 49, 44,
+ 98, 45, 49, 41, 10,115,101,108,102, 46,100,105,109, 32, 61,
+ 32,100, 10,101,110,100, 10, 10, 10,105,102, 32,115,101,108,
+ 102, 46,116,121,112,101, 32,126, 61, 32, 39, 39, 32, 97,110,
+ 100, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61, 32,
+ 39,118,111,105,100, 39, 32, 97,110,100, 32,115,101,108,102,
+ 46,110, 97,109,101, 32, 61, 61, 32, 39, 39, 32,116,104,101,
+ 110, 10,115,101,108,102, 46,110, 97,109,101, 32, 61, 32, 99,
+ 114,101, 97,116,101, 95,118, 97,114,110, 97,109,101, 40, 41,
+ 10,101,108,115,101,105,102, 32,115,101,108,102, 46,107,105,
+ 110,100, 61, 61, 39,118, 97,114, 39, 32,116,104,101,110, 10,
+ 105,102, 32,115,101,108,102, 46,116,121,112,101, 61, 61, 39,
+ 39, 32, 97,110,100, 32,115,101,108,102, 46,110, 97,109,101,
+ 126, 61, 39, 39, 32,116,104,101,110, 10,115,101,108,102, 46,
+ 116,121,112,101, 32, 61, 32,115,101,108,102, 46,116,121,112,
+ 101, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,115,101,
+ 108,102, 46,110, 97,109,101, 32, 61, 32, 99,114,101, 97,116,
+ 101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,108,115,
+ 101,105,102, 32,105,115,116,121,112,101, 40,115,101,108,102,
+ 46,110, 97,109,101, 41, 32,116,104,101,110, 10,105,102, 32,
+ 115,101,108,102, 46,116,121,112,101, 61, 61, 39, 39, 32,116,
+ 104,101,110, 32,115,101,108,102, 46,116,121,112,101, 32, 61,
+ 32,115,101,108,102, 46,110, 97,109,101, 10,101,108,115,101,
+ 32,115,101,108,102, 46,116,121,112,101, 32, 61, 32,115,101,
+ 108,102, 46,116,121,112,101, 46, 46, 39, 32, 39, 46, 46,115,
+ 101,108,102, 46,110, 97,109,101, 32,101,110,100, 10,115,101,
+ 108,102, 46,110, 97,109,101, 32, 61, 32, 99,114,101, 97,116,
+ 101, 95,118, 97,114,110, 97,109,101, 40, 41, 10,101,110,100,
+ 10,101,110,100, 10, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110, 58, 99,104,101, 99,107,116,
+ 121,112,101, 32, 40, 41, 10, 10, 10,105,102, 32,105,115, 98,
+ 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41,
+ 32, 97,110,100, 32,115,101,108,102, 46,112,116,114,126, 61,
+ 39, 39, 32,116,104,101,110, 10,115,101,108,102, 46,114,101,
+ 116, 32, 61, 32,115,101,108,102, 46,112,116,114, 10,115,101,
+ 108,102, 46,112,116,114, 32, 61, 32,110,105,108, 10,101,110,
+ 100, 10, 10, 10,105,102, 32,115,101,108,102, 46,100,105,109,
+ 126, 61, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,114,
+ 101,116,126, 61, 39, 39, 32,116,104,101,110, 10,101,114,114,
+ 111,114, 40, 39, 35,105,110,118, 97,108,105,100, 32,112, 97,
+ 114, 97,109,101,116,101,114, 58, 32, 99, 97,110,110,111,116,
+ 32,114,101,116,117,114,110, 32, 97,110, 32, 97,114,114, 97,
+ 121, 32,111,102, 32,118, 97,108,117,101,115, 39, 41, 10,101,
+ 110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46,116,121,
+ 112,101,126, 61, 39, 39, 32,116,104,101,110, 10,114,101,103,
+ 116,121,112,101, 40,115,101,108,102, 46,116,121,112,101, 41,
+ 10,101,110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46,
+ 116,121,112,101, 32, 61, 61, 32, 39, 95,117,115,101,114,100,
+ 97,116, 97, 39, 32,116,104,101,110, 32,115,101,108,102, 46,
+ 116,121,112,101, 32, 61, 32, 39,118,111,105,100, 42, 39, 10,
+ 101,108,115,101,105,102, 32,115,101,108,102, 46,116,121,112,
+ 101, 32, 61, 61, 32, 39, 95, 99,115,116,114,105,110,103, 39,
+ 32,116,104,101,110, 32,115,101,108,102, 46,116,121,112,101,
+ 32, 61, 32, 39, 99,104, 97,114, 42, 39, 10,101,110,100, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 58,112,114,105,110,116,
+ 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,
+ 111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,
+ 111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116,
+ 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32,
+ 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101,
+ 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39,
+ 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39,
+ 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32,100,105,109, 32, 61, 32, 39, 34, 46, 46,115,101,108,
+ 102, 46,100,105,109, 46, 46, 34, 39, 44, 34, 41, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,100,101,
+ 102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,101,
+ 102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32,114,101,116, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,114,101,116, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,
+ 101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,
+ 110, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115,101,
+ 108,102, 46,105,116,121,112,101, 44, 32,115,101,108,102, 46,
+ 116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,
+ 108,102, 46,116,121,112,101, 44,115,116,114,102,105,110,100,
+ 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110,115,
+ 116, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101, 99,108,
+ 97,114, 97,116,105,111,110, 58,111,117,116, 99,104,101, 99,
+ 107,116,121,112,101, 32, 40,110, 97,114,103, 41, 10,108,111,
+ 99, 97,108, 32,116, 97,103, 44, 32,100,101,102, 10,105,102,
+ 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39,
+ 32,116,104,101,110, 10,116, 97,103, 32, 61, 32, 39, 76, 85,
+ 65, 95, 84, 84, 65, 66, 76, 69, 39, 10,100,101,102, 32, 61,
+ 32, 48, 10,101,108,115,101, 10,116, 97,103, 32, 61, 32,115,
+ 101,108,102, 46,116, 97,103, 10,100,101,102, 32, 61, 32,115,
+ 101,108,102, 46,100,101,102,126, 61, 39, 39, 32,111,114, 32,
+ 48, 10,101,110,100, 10,114,101,116,117,114,110, 32, 39,116,
+ 111,108,117, 97, 95,105,115,116,121,112,101, 40,116,111,108,
+ 117, 97, 95, 83, 44, 39, 46, 46,110, 97,114,103, 46, 46, 39,
+ 44, 39, 46, 46,116, 97,103, 46, 46, 39, 44, 39, 46, 46,100,
+ 101,102, 46, 46, 39, 41, 39, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 58,100,101, 99,108, 97,
+ 114,101, 32, 40,110, 97,114,103, 41, 10,108,111, 99, 97,108,
+ 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101,
+ 108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101,110,
+ 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,
+ 111,117,116,112,117,116, 40, 34, 32, 34, 44,115,101,108,102,
+ 46,109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,
+ 112,116,114, 41, 10,105,102, 32,115,101,108,102, 46,100,105,
+ 109, 32,126, 61, 32, 39, 39, 32, 97,110,100, 32,116,111,110,
+ 117,109, 98,101,114, 40,115,101,108,102, 46,100,105,109, 41,
+ 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116,
+ 112,117,116, 40,115,101,108,102, 46,110, 97,109,101, 41, 10,
+ 105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61, 32,
+ 39, 39, 32,116,104,101,110, 10,105,102, 32,116,111,110,117,
+ 109, 98,101,114, 40,115,101,108,102, 46,100,105,109, 41,126,
+ 61,110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39, 91, 39, 44,115,101,108,102, 46,100,105,109, 44,
+ 39, 93, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,
+ 117,116, 40, 39, 32, 61, 32, 40, 39, 44,115,101,108,102, 46,
+ 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,112,
+ 116,114, 44, 39, 42, 41, 39, 44, 10, 39,109, 97,108,108,111,
+ 99, 40, 39, 44,115,101,108,102, 46,100,105,109, 44, 39, 42,
+ 115,105,122,101,111,102, 40, 39, 44,115,101,108,102, 46,116,
+ 121,112,101, 44,112,116,114, 44, 39, 41, 41, 59, 39, 41, 10,
+ 101,110,100, 10,101,108,115,101, 10,108,111, 99, 97,108, 32,
+ 116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,
+ 102, 46,116,121,112,101, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32, 61, 32, 39, 41, 10,105,102, 32,110,111,116, 32,116,
+ 32, 97,110,100, 32,112,116,114, 61, 61, 39, 39, 32,116,104,
+ 101,110, 32,111,117,116,112,117,116, 40, 39, 42, 39, 41, 32,
+ 101,110,100, 10,111,117,116,112,117,116, 40, 39, 40, 40, 39,
+ 44,115,101,108,102, 46,109,111,100, 44,115,101,108,102, 46,
+ 116,121,112,101, 41, 10,105,102, 32,110,111,116, 32,116, 32,
+ 116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 42, 39,
+ 41, 10,101,110,100, 10,111,117,116,112,117,116, 40, 39, 41,
+ 32, 39, 41, 10,108,111, 99, 97,108, 32,100,101,102, 32, 61,
+ 32, 48, 10,105,102, 32,115,101,108,102, 46,100,101,102, 32,
+ 126, 61, 32, 39, 39, 32,116,104,101,110, 32,100,101,102, 32,
+ 61, 32,115,101,108,102, 46,100,101,102, 32,101,110,100, 10,
+ 105,102, 32,116, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39,116,111,108,117, 97, 95,103,101,116, 39, 46, 46,
+ 116, 44, 39, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,110,
+ 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41, 41,
+ 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 39, 44,
+ 110, 97,114,103, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41,
+ 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,
+ 103,101,116, 97,114,114, 97,121, 32, 40,110, 97,114,103, 41,
+ 10,105,102, 32,115,101,108,102, 46,100,105,109, 32,126, 61,
+ 32, 39, 39, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 32,123, 39, 41, 10,108,111, 99, 97,108, 32,100,101,
+ 102, 32, 61, 32,115,101,108,102, 46,100,101,102,126, 61, 39,
+ 39, 32,111,114, 32, 48, 10,111,117,116,112,117,116, 40, 39,
+ 32,105,102, 32, 40, 33,116,111,108,117, 97, 95, 97,114,114,
+ 97,121,105,115,116,121,112,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 39, 44,110, 97,114,103, 44, 39, 44, 39, 44,115,101,
+ 108,102, 46,116, 97,103, 44, 39, 44, 39, 44,115,101,108,102,
+ 46,100,105,109, 44, 39, 44, 39, 44,100,101,102, 44, 39, 41,
+ 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,103,111,
+ 116,111, 32,116,111,108,117, 97, 95,108,101,114,114,111,114,
+ 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,101,108,
+ 115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,123, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,105,
+ 110,116, 32,105, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,102,111,114, 40,105, 61, 48, 59, 32,105, 60, 39, 46,
+ 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 59,105, 43,
+ 43, 41, 39, 41, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,
+ 105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,
+ 112,101, 41, 10,108,111, 99, 97,108, 32,112,116,114, 32, 61,
+ 32, 39, 39, 10,105,102, 32,115,101,108,102, 46,112,116,114,
+ 126, 61, 39, 39, 32,116,104,101,110, 32,112,116,114, 32, 61,
+ 32, 39, 42, 39, 32,101,110,100, 10,111,117,116,112,117,116,
+ 40, 39, 32, 39, 44,115,101,108,102, 46,110, 97,109,101, 46,
+ 46, 39, 91,105, 93, 32, 61, 32, 39, 41, 10,105,102, 32,110,
+ 111,116, 32,116, 32, 97,110,100, 32,112,116,114, 61, 61, 39,
+ 39, 32,116,104,101,110, 32,111,117,116,112,117,116, 40, 39,
+ 42, 39, 41, 32,101,110,100, 10,111,117,116,112,117,116, 40,
+ 39, 40, 40, 39, 44,115,101,108,102, 46,109,111,100, 44,115,
+ 101,108,102, 46,116,121,112,101, 41, 10,105,102, 32,110,111,
+ 116, 32,116, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,116,112,117,
+ 116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,108, 32,100,
+ 101,102, 32, 61, 32, 48, 10,105,102, 32,115,101,108,102, 46,
+ 100,101,102, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 32,
+ 100,101,102, 32, 61, 32,115,101,108,102, 46,100,101,102, 32,
+ 101,110,100, 10,105,102, 32,116, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,
+ 116,102,105,101,108,100, 39, 46, 46,116, 46, 46, 39, 40,116,
+ 111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39,
+ 44,105, 43, 49, 44, 39, 44,100,101,102, 44, 39, 41, 41, 59,
+ 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40,
+ 39,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100,
+ 117,115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49, 44,
+ 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110,
+ 100, 10,111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 58,115,101,116, 97,114,114, 97,121, 32, 40,110,
+ 97,114,103, 41, 10,105,102, 32,115,101,108,102, 46,100,105,
+ 109, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,
+ 117,116, 40, 39, 32,105,110,116, 32,105, 59, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 32,102,111,114, 40,105, 61, 48,
+ 59, 32,105, 60, 39, 46, 46,115,101,108,102, 46,100,105,109,
+ 46, 46, 39, 59,105, 43, 43, 41, 39, 41, 10,108,111, 99, 97,
+ 108, 32,116, 44, 99,116, 32, 61, 32,105,115, 98, 97,115,105,
+ 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,105,102,
+ 32,116, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,
+ 108,100, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97,
+ 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49,
+ 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 44,115,101,108,102,
+ 46,110, 97,109,101, 44, 39, 91,105, 93, 41, 59, 39, 41, 10,
+ 101,108,115,101, 10,105,102, 32,115,101,108,102, 46,112,116,
+ 114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,
+ 117,116, 40, 39, 35,105,102,100,101,102, 32, 95, 95, 99,112,
+ 108,117,115,112,108,117,115, 92,110, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108,
+ 117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119,
+ 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 40, 39,
+ 44,115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93,
+ 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,101,
+ 108,115,101, 92,110, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95,
+ 99,108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,
+ 111,112,121, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,
+ 105,100, 42, 41, 38, 39, 44,115,101,108,102, 46,110, 97,109,
+ 101, 44, 39, 91,105, 93, 44,115,105,122,101,111,102, 40, 39,
+ 44,115,101,108,102, 46,116,121,112,101, 44, 39, 41, 41, 59,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,101,110,100,
+ 105,102, 92,110, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,108,
+ 100,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,105, 43, 49,
+ 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,
+ 116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 73, 95,
+ 99,108,111,110,101, 44, 39, 44,115,101,108,102, 46,116, 97,
+ 103, 44, 39, 41, 44, 39, 44,115,101,108,102, 46,116, 97,103,
+ 44, 39, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,125, 39, 41, 10, 10, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,
+ 104,102,105,101,108,100,117,115,101,114,116,121,112,101, 40,
+ 116,111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44,
+ 39, 44,105, 43, 49, 44, 40,118,111,105,100, 42, 41, 39, 44,
+ 115,101,108,102, 46,110, 97,109,101, 44, 39, 91,105, 93, 44,
+ 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,110,100, 10,111,117,116,112,117,
+ 116, 40, 39, 32,125, 39, 41, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 58,102,
+ 114,101,101, 97,114,114, 97,121, 32, 40, 41, 10,105,102, 32,
+ 115,101,108,102, 46,100,105,109, 32,126, 61, 32, 39, 39, 32,
+ 97,110,100, 32,116,111,110,117,109, 98,101,114, 40,115,101,
+ 108,102, 46,100,105,109, 41, 61, 61,110,105,108, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 32,102,114,101,
+ 101, 40, 39, 44,115,101,108,102, 46,110, 97,109,101, 44, 39,
+ 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110, 58,112, 97,115,115,
+ 112, 97,114, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46,
+ 112,116,114, 61, 61, 39, 38, 39, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39, 42, 39, 46, 46,115,101,108,102,
+ 46,110, 97,109,101, 41, 10,101,108,115,101,105,102, 32,115,
+ 101,108,102, 46,114,101,116, 61, 61, 39, 42, 39, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 38, 39, 46, 46,
+ 115,101,108,102, 46,110, 97,109,101, 41, 10,101,108,115,101,
+ 10,111,117,116,112,117,116, 40,115,101,108,102, 46,110, 97,
+ 109,101, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 58,114,101,116,118, 97,
+ 108,117,101, 32, 40, 41, 10,105,102, 32,115,101,108,102, 46,
+ 114,101,116, 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,
+ 108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,105,115,
+ 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101,
+ 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,
+ 104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117, 97, 95,
+ 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,
+ 101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,116,
+ 111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,
+ 112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105,
+ 100, 42, 41, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44,
+ 39, 41, 59, 39, 41, 10,101,110,100, 10,114,101,116,117,114,
+ 110, 32, 49, 10,101,110,100, 10,114,101,116,117,114,110, 32,
+ 48, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 32,
+ 40,116, 41, 10,105,102, 32,116, 46,110, 97,109,101, 32, 97,
+ 110,100, 32,116, 46,110, 97,109,101,126, 61, 39, 39, 32,116,
+ 104,101,110, 10,108,111, 99, 97,108, 32,110, 32, 61, 32,115,
+ 112,108,105,116, 40,116, 46,110, 97,109,101, 44, 39, 64, 39,
+ 41, 10,116, 46,110, 97,109,101, 32, 61, 32,110, 91, 49, 93,
+ 10,116, 46,108,110, 97,109,101, 32, 61, 32,103,115,117, 98,
+ 40,110, 91, 50, 93, 32,111,114, 32,110, 91, 49, 93, 44, 34,
+ 37, 91, 46, 45, 37, 93, 34, 44, 34, 34, 41, 10,101,110,100,
+ 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,
+ 115, 68,101, 99,108, 97,114, 97,116,105,111,110, 10,115,101,
+ 116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,116, 97,
+ 103, 41, 10,116, 58, 99,104,101, 99,107,110, 97,109,101, 40,
+ 41, 10,116, 58, 99,104,101, 99,107,116,121,112,101, 40, 41,
+ 10,114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110, 32, 40,115, 44,107,105,110,
+ 100, 41, 10, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,
+ 34, 37,115, 42, 61, 37,115, 42, 34, 44, 34, 61, 34, 41, 10,
+ 10,105,102, 32,107,105,110,100, 32, 61, 61, 32, 34,118, 97,
+ 114, 34, 32,116,104,101,110, 10, 10,105,102, 32,115, 32, 61,
+ 61, 32, 39, 39, 32,111,114, 32,115, 32, 61, 61, 32, 39,118,
+ 111,105,100, 39, 32,116,104,101,110, 10,114,101,116,117,114,
+ 110, 32, 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123,
+ 116,121,112,101, 32, 61, 32, 39,118,111,105,100, 39, 44, 32,
+ 107,105,110,100, 32, 61, 32,107,105,110,100,125, 10,101,110,
+ 100, 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,116,
+ 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 42, 37,
+ 115, 42, 38, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61,
+ 32, 50, 32,116,104,101,110, 10,105,102, 32,107,105,110,100,
+ 32, 61, 61, 32, 39,102,117,110, 99, 39, 32,116,104,101,110,
+ 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,108,105,
+ 100, 32,102,117,110, 99,116,105,111,110, 32,114,101,116,117,
+ 114,110, 32,116,121,112,101, 58, 32, 34, 46, 46,115, 41, 10,
+ 101,110,100, 10,108,111, 99, 97,108, 32,109, 32, 61, 32,115,
+ 112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115, 37,115,
+ 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,101, 99,
+ 108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32,
+ 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61, 32, 39,
+ 42, 39, 44, 10,114,101,116, 32, 61, 32, 39, 38, 39, 44, 10,
+ 116,121,112,101, 32, 61, 32,109, 91,109, 46,110, 93, 44, 10,
+ 109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,109, 44,
+ 49, 44,109, 46,110, 45, 49, 41, 44, 10,107,105,110,100, 32,
+ 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10, 10, 10,
+ 116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37, 42,
+ 37,115, 42, 37, 42, 39, 41, 10,105,102, 32,116, 46,110, 32,
+ 61, 61, 32, 50, 32,116,104,101,110, 10,105,102, 32,107,105,
+ 110,100, 32, 61, 61, 32, 39,102,117,110, 99, 39, 32,116,104,
+ 101,110, 10,101,114,114,111,114, 40, 34, 35,105,110,118, 97,
+ 108,105,100, 32,102,117,110, 99,116,105,111,110, 32,114,101,
+ 116,117,114,110, 32,116,121,112,101, 58, 32, 34, 46, 46,115,
+ 41, 10,101,110,100, 10,108,111, 99, 97,108, 32,109, 32, 61,
+ 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115,
+ 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,
+ 101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61,
+ 32, 39, 42, 39, 44, 10,114,101,116, 32, 61, 32, 39, 42, 39,
+ 44, 10,116,121,112,101, 32, 61, 32,109, 91,109, 46,110, 93,
+ 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,116, 40,
+ 109, 44, 49, 44,109, 46,110, 45, 49, 41, 44, 10,107,105,110,
+ 100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100, 10,
+ 10, 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39,
+ 38, 39, 41, 10,105,102, 32,116, 46,110, 32, 61, 61, 32, 50,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32,109, 32, 61,
+ 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39, 37,115,
+ 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32, 95, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,
+ 101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114, 32, 61,
+ 32, 39, 38, 39, 44, 10,116,121,112,101, 32, 61, 32,109, 91,
+ 109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110,
+ 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49, 41, 32,
+ 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100, 10,125,
+ 10,101,110,100, 10, 10, 10,108,111, 99, 97,108, 32,115, 49,
+ 32, 61, 32,103,115,117, 98, 40,115, 44, 34, 40, 37, 98, 92,
+ 91, 92, 93, 41, 34, 44,102,117,110, 99,116,105,111,110, 32,
+ 40,110, 41, 32,114,101,116,117,114,110, 32,103,115,117, 98,
+ 40,110, 44, 39, 37, 42, 39, 44, 39, 92, 49, 39, 41, 32,101,
+ 110,100, 41, 10,116, 32, 61, 32,115,112,108,105,116, 40,115,
+ 49, 44, 39, 37, 42, 39, 41, 10,105,102, 32,116, 46,110, 32,
+ 61, 61, 32, 50, 32,116,104,101,110, 10,116, 91, 50, 93, 32,
+ 61, 32,103,115,117, 98, 40,116, 91, 50, 93, 44, 39, 92, 49,
+ 39, 44, 39, 37, 42, 39, 41, 10,108,111, 99, 97,108, 32,109,
+ 32, 61, 32,115,112,108,105,116, 40,116, 91, 49, 93, 44, 39,
+ 37,115, 37,115, 42, 39, 41, 10,114,101,116,117,114,110, 32,
+ 95, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 10,110,
+ 97,109,101, 32, 61, 32,116, 91, 50, 93, 44, 10,112,116,114,
+ 32, 61, 32, 39, 42, 39, 44, 10,116,121,112,101, 32, 61, 32,
+ 109, 91,109, 46,110, 93, 44, 10,109,111,100, 32, 61, 32, 99,
+ 111,110, 99, 97,116, 40,109, 44, 49, 44,109, 46,110, 45, 49,
+ 41, 32, 44, 10,107,105,110,100, 32, 61, 32,107,105,110,100,
+ 10,125, 10,101,110,100, 10, 10,105,102, 32,107,105,110,100,
+ 32, 61, 61, 32, 39,118, 97,114, 39, 32,116,104,101,110, 10,
+ 10,116, 32, 61, 32,115,112,108,105,116, 40,115, 44, 39, 37,
+ 115, 37,115, 42, 39, 41, 10,108,111, 99, 97,108, 32,118, 10,
+ 105,102, 32,105,115,116,121,112,101, 40,116, 91,116, 46,110,
+ 93, 41, 32,116,104,101,110, 32,118, 32, 61, 32, 39, 39, 32,
+ 101,108,115,101, 32,118, 32, 61, 32,116, 91,116, 46,110, 93,
+ 59, 32,116, 46,110, 32, 61, 32,116, 46,110, 45, 49, 32,101,
+ 110,100, 10,114,101,116,117,114,110, 32, 95, 68,101, 99,108,
+ 97,114, 97,116,105,111,110,123, 10,110, 97,109,101, 32, 61,
+ 32,118, 44, 10,116,121,112,101, 32, 61, 32,116, 91,116, 46,
+ 110, 93, 44, 10,109,111,100, 32, 61, 32, 99,111,110, 99, 97,
+ 116, 40,116, 44, 49, 44,116, 46,110, 45, 49, 41, 44, 10,107,
+ 105,110,100, 32, 61, 32,107,105,110,100, 10,125, 10, 10,101,
+ 108,115,101, 10, 10, 10,116, 32, 61, 32,115,112,108,105,116,
+ 40,115, 44, 39, 37,115, 37,115, 42, 39, 41, 10,108,111, 99,
+ 97,108, 32,118, 32, 61, 32,116, 91,116, 46,110, 93, 10,108,
+ 111, 99, 97,108, 32,116,112, 44,109,100, 10,105,102, 32,116,
+ 46,110, 62, 49, 32,116,104,101,110, 10,116,112, 32, 61, 32,
+ 116, 91,116, 46,110, 45, 49, 93, 10,109,100, 32, 61, 32, 99,
+ 111,110, 99, 97,116, 40,116, 44, 49, 44,116, 46,110, 45, 50,
+ 41, 10,101,110,100, 10,114,101,116,117,114,110, 32, 95, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110,123, 10,110, 97,109,
+ 101, 32, 61, 32,118, 44, 10,116,121,112,101, 32, 61, 32,116,
+ 112, 44, 10,109,111,100, 32, 61, 32,109,100, 44, 10,107,105,
+ 110,100, 32, 61, 32,107,105,110,100, 10,125, 10,101,110,100,
+ 10, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 86, 97,114,105,
+ 97, 98,108,101, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32,
+ 61, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 44, 10,125, 10, 10,115,101,116,116, 97,103, 40,
+ 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,114,105,
+ 97, 98,108,101, 58,112,114,105,110,116, 32, 40,105,100,101,
+ 110,116, 44, 99,108,111,115,101, 41, 10,112,114,105,110,116,
+ 40,105,100,101,110,116, 46, 46, 34, 86, 97,114,105, 97, 98,
+ 108,101,123, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34, 32,109,111,100, 32, 61, 32, 39, 34, 46,
+ 46,115,101,108,102, 46,109,111,100, 46, 46, 34, 39, 44, 34,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32,116,121,112,101, 32, 61, 32, 39, 34, 46, 46,115,101,
+ 108,102, 46,116,121,112,101, 46, 46, 34, 39, 44, 34, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,
+ 112,116,114, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 112,116,114, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,110, 97,109,101,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,
+ 101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32,100,101,102, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,100,101,102, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44, 34, 41,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,100, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 86, 97,114,105, 97, 98,108,101, 58,103,101,116,118, 97,
+ 108,117,101, 32, 40, 99,108, 97,115,115, 44,115,116, 97,116,
+ 105, 99, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,
+ 100, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,114,
+ 101,116,117,114,110, 32, 99,108, 97,115,115, 46, 46, 39, 58,
+ 58, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,
+ 108,115,101,105,102, 32, 99,108, 97,115,115, 32,116,104,101,
+ 110, 10,114,101,116,117,114,110, 32, 39,115,101,108,102, 45,
+ 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 10,101,
+ 108,115,101, 10,114,101,116,117,114,110, 32,115,101,108,102,
+ 46,110, 97,109,101, 10,101,110,100, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 86, 97,114,105, 97, 98,108,101, 58,115,117,112, 99,111,100,
+ 101, 32, 40, 41, 10,108,111, 99, 97,108, 32, 99,108, 97,115,
+ 115, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,
+ 116,104,101,110, 10,111,117,116,112,117,116, 40, 34, 47, 42,
+ 32,103,101,116, 32,102,117,110, 99,116,105,111,110, 58, 34,
+ 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32,111,102,
+ 32, 99,108, 97,115,115, 32, 34, 44, 99,108, 97,115,115, 44,
+ 34, 32, 42, 47, 34, 41, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110,
+ 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,
+ 109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100, 10,115,
+ 101,108,102, 46, 99,103,101,116,110, 97,109,101, 32, 61, 32,
+ 115,101,108,102, 58, 99,102,117,110, 99,110, 97,109,101, 40,
+ 34,116,111,108,117, 97, 73, 95,103,101,116, 34, 41, 10,111,
+ 117,116,112,117,116, 40, 34,115,116, 97,116,105, 99, 32,105,
+ 110,116, 34, 44,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42,
+ 32,116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,
+ 112,117,116, 40, 34,123, 34, 41, 10, 10, 10,108,111, 99, 97,
+ 108, 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,
+ 100, 44, 39, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41,
+ 39, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100,
+ 32,115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44, 99,
+ 108, 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32,
+ 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39,
+ 44, 99,108, 97,115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,103,101,
+ 116,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 49, 44, 48, 41, 59, 39, 41, 10,101,108,115,101,
+ 105,102, 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,
+ 95, 44, 95, 44,115,101,108,102, 46,109,111,100, 32, 61, 32,
+ 115,116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,
+ 100, 44, 39, 94, 37,115, 42,115,116, 97,116,105, 99, 37,115,
+ 37,115, 42, 40, 46, 42, 41, 39, 41, 10,101,110,100, 10, 10,
+ 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,
+ 115,116, 97,116,105, 99, 61, 61,110,105,108, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40,
+ 33,115,101,108,102, 41, 32, 84, 79, 76, 85, 65, 95, 69, 82,
+ 82, 95, 83, 69, 76, 70, 59, 39, 41, 59, 10,101,110,100, 10,
+ 10, 10,108,111, 99, 97,108, 32,116, 44, 99,116, 32, 61, 32,
+ 105,115, 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,
+ 112,101, 41, 10,105,102, 32,116, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,
+ 117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116,111,108,117,
+ 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41, 39, 46, 46,
+ 115,101,108,102, 58,103,101,116,118, 97,108,117,101, 40, 99,
+ 108, 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39,
+ 41, 59, 39, 41, 10,101,108,115,101, 10,105,102, 32,115,101,
+ 108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,111,
+ 114, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32, 39,
+ 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,
+ 111,105,100, 42, 41, 38, 39, 46, 46,115,101,108,102, 58,103,
+ 101,116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115,
+ 116, 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,
+ 102, 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40,
+ 116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41,
+ 39, 46, 46,115,101,108,102, 58,103,101,116,118, 97,108,117,
+ 101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105, 99, 41,
+ 46, 46, 39, 44, 39, 44,115,101,108,102, 46,116, 97,103, 44,
+ 39, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,111,
+ 117,116,112,117,116, 40, 39, 32,114,101,116,117,114,110, 32,
+ 49, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,125, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 92,110, 39, 41, 10,
+ 10, 10,105,102, 32,110,111,116, 32,115,116,114,102,105,110,
+ 100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,110,
+ 115,116, 39, 41, 32,116,104,101,110, 10,105,102, 32, 99,108,
+ 97,115,115, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 34, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,105,
+ 111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101, 44,
+ 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,108,
+ 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,101,
+ 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116,
+ 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,
+ 102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,
+ 110,100, 10,115,101,108,102, 46, 99,115,101,116,110, 97,109,
+ 101, 32, 61, 32,115,101,108,102, 58, 99,102,117,110, 99,110,
+ 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,115,101,116,
+ 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97,116,
+ 105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99,115,
+ 101,116,110, 97,109,101, 44, 34, 40,108,117, 97, 95, 83,116,
+ 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34, 41,
+ 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10, 10,
+ 108,111, 99, 97,108, 32,110, 97,114,103, 61, 49, 10,105,102,
+ 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,
+ 105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44,
+ 39, 42, 39, 44, 39,115,101,108,102, 32, 61, 32, 39, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,115,
+ 115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44,
+ 48, 41, 59, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39,
+ 32,105,102, 32, 40, 33,115,101,108,102, 41, 32, 84, 79, 76,
+ 85, 65, 95, 69, 82, 82, 95, 83, 69, 76, 70, 59, 39, 41, 59,
+ 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,
+ 101,108,115,101,105,102, 32,115,116, 97,116,105, 99, 32,116,
+ 104,101,110, 10, 95, 44, 95, 44,115,101,108,102, 46,109,111,
+ 100, 32, 61, 32,115,116,114,102,105,110,100, 40,115,101,108,
+ 102, 46,109,111,100, 44, 39, 94, 37,115, 42,115,116, 97,116,
+ 105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39, 41, 10,110,
+ 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,101,110,
+ 100, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,102,
+ 32, 40, 33, 39, 46, 46,115,101,108,102, 58,111,117,116, 99,
+ 104,101, 99,107,116,121,112,101, 40,110, 97,114,103, 41, 46,
+ 46, 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,
+ 84, 79, 76, 85, 65, 95, 69, 82, 82, 95, 65, 83, 83, 73, 71,
+ 78, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,112,116,
+ 114, 32, 61, 32, 39, 39, 10,105,102, 32,115,101,108,102, 46,
+ 112,116,114,126, 61, 39, 39, 32,116,104,101,110, 32,112,116,
+ 114, 32, 61, 32, 39, 42, 39, 32,101,110,100, 10,111,117,116,
+ 112,117,116, 40, 39, 32, 39, 41, 10,105,102, 32, 99,108, 97,
+ 115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115,
+ 115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110,
+ 97,109,101, 41, 10,101,108,115,101,105,102, 32, 99,108, 97,
+ 115,115, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,108,102, 46,
+ 110, 97,109,101, 41, 10,101,108,115,101, 10,111,117,116,112,
+ 117,116, 40,115,101,108,102, 46,110, 97,109,101, 41, 10,101,
+ 110,100, 10,108,111, 99, 97,108, 32,116, 32, 61, 32,105,115,
+ 98, 97,115,105, 99, 40,115,101,108,102, 46,116,121,112,101,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41,
+ 10,105,102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112,
+ 116,114, 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116,
+ 112,117,116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117,
+ 116,112,117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,
+ 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 41, 10,
+ 105,102, 32,110,111,116, 32,116, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,
+ 111,117,116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111,
+ 99, 97,108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32,
+ 115,101,108,102, 46,100,101,102, 32,126, 61, 32, 39, 39, 32,
+ 116,104,101,110, 32,100,101,102, 32, 61, 32,115,101,108,102,
+ 46,100,101,102, 32,101,110,100, 10,105,102, 32,116, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39,116,111,108,
+ 117, 97, 95,103,101,116, 39, 46, 46,116, 44, 39, 40,116,111,
+ 108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39, 44,
+ 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,108,
+ 115,101, 10,111,117,116,112,117,116, 40, 39,116,111,108,117,
+ 97, 95,103,101,116,117,115,101,114,116,121,112,101, 40,116,
+ 111,108,117, 97, 95, 83, 44, 39, 44,110, 97,114,103, 44, 39,
+ 44, 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,
+ 110,100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,116,
+ 117,114,110, 32, 48, 59, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,
+ 110, 39, 41, 10,101,110,100, 10, 10,101,110,100, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 99,108, 97,115,115, 86, 97,
+ 114,105, 97, 98,108,101, 58,114,101,103,105,115,116,101,114,
+ 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 97,114,101,110,
+ 116, 32, 61, 32,115,101,108,102, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105,110,109,
+ 111,100,117,108,101, 40, 41, 10,105,102, 32,112, 97,114,101,
+ 110,116, 32,116,104,101,110, 10,105,102, 32,115,101,108,102,
+ 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110, 10,
+ 111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,
+ 116, 97, 98,108,101,118, 97,114, 40,116,111,108,117, 97, 95,
+ 83, 44, 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39,
+ 34, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,
+ 101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,
+ 103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,
+ 101,108,102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39,
+ 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,101,
+ 118, 97,114, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46,
+ 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34,
+ 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10,
+ 101,110,100, 10,101,108,115,101, 10,105,102, 32,115,101,108,
+ 102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110,
+ 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97,
+ 95,103,108,111, 98, 97,108,118, 97,114, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110,
+ 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102,
+ 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 39, 46,
+ 46,115,101,108,102, 46, 99,115,101,116,110, 97,109,101, 46,
+ 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,108,111,
+ 98, 97,108,118, 97,114, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46,
+ 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,
+ 116,110, 97,109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59,
+ 39, 41, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 86, 97,114,105, 97, 98,108,101, 58,117,110,114,101,103,105,
+ 115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,102,
+ 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105,108,
+ 32, 97,110,100, 32,115,101,108,102, 58,105,110,109,111,100,
+ 117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101,110,
+ 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,103,
+ 101,116,103,108,111, 98, 97,108,115, 40,116,111,108,117, 97,
+ 95, 83, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,108,117, 97, 95,112,117,115,104,115,116,114,105,110,103,
+ 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 44,115,101,108,
+ 102, 46,108,110, 97,109,101, 44, 39, 34, 41, 59, 32,108,117,
+ 97, 95,112,117,115,104,110,105,108, 40,116,111,108,117, 97,
+ 95, 83, 41, 59, 32,108,117, 97, 95,114, 97,119,115,101,116,
+ 40,116,111,108,117, 97, 95, 83, 44, 45, 51, 41, 59, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,112,
+ 111,112, 40,116,111,108,117, 97, 95, 83, 44, 49, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 95, 86, 97,114,105, 97, 98,108,
+ 101, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61,
+ 32, 99,108, 97,115,115, 86, 97,114,105, 97, 98,108,101, 10,
+ 115,101,116,116, 97,103, 40,116, 44,116,111,108,117, 97, 95,
+ 116, 97,103, 41, 10, 97,112,112,101,110,100, 40,116, 41, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 86, 97,114,105, 97,
+ 98,108,101, 32, 40,115, 41, 10,114,101,116,117,114,110, 32,
+ 95, 86, 97,114,105, 97, 98,108,101, 32, 40, 68,101, 99,108,
+ 97,114, 97,116,105,111,110, 40,115, 44, 39,118, 97,114, 39,
+ 41, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,115, 65,
+ 114,114, 97,121, 32, 61, 32,123, 10, 95, 98, 97,115,101, 32,
+ 61, 32, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 44, 10,125, 10, 10,115,101,116,116, 97,103, 40,
+ 99,108, 97,115,115, 65,114,114, 97,121, 44,116,111,108,117,
+ 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,112,
+ 114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,
+ 115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 65,114,114, 97,121,123, 34, 41, 10,112,114,105,
+ 110,116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100,
+ 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46,
+ 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46,
+ 46,115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,
+ 100,101,102, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 100,101,102, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,100,105,109, 32,
+ 61, 32, 39, 34, 46, 46,115,101,108,102, 46,100,105,109, 46,
+ 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34, 32,114,101,116, 32, 61, 32, 39, 34,
+ 46, 46,115,101,108,102, 46,114,101,116, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 65,114,114, 97,121, 58,103,101,116,118, 97,108,
+ 117,101, 32, 40, 99,108, 97,115,115, 44,115,116, 97,116,105,
+ 99, 41, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100,
+ 32,115,116, 97,116,105, 99, 32,116,104,101,110, 10,114,101,
+ 116,117,114,110, 32, 99,108, 97,115,115, 46, 46, 39, 58, 58,
+ 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39,
+ 91,116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39,
+ 10,101,108,115,101,105,102, 32, 99,108, 97,115,115, 32,116,
+ 104,101,110, 10,114,101,116,117,114,110, 32, 39,115,101,108,
+ 102, 45, 62, 39, 46, 46,115,101,108,102, 46,110, 97,109,101,
+ 46, 46, 39, 91,116,111,108,117, 97, 73, 95,105,110,100,101,
+ 120, 93, 39, 10,101,108,115,101, 10,114,101,116,117,114,110,
+ 32,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,
+ 111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 10,101,
+ 110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 65,114,114, 97,121, 58,115,
+ 117,112, 99,111,100,101, 32, 40, 41, 10,108,111, 99, 97,108,
+ 32, 99,108, 97,115,115, 32, 61, 32,115,101,108,102, 58,105,
+ 110, 99,108, 97,115,115, 40, 41, 10, 10, 10,105,102, 32, 99,
+ 108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 34, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,
+ 105,111,110, 58, 34, 44,115,101,108,102, 46,110, 97,109,101,
+ 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44, 99,
+ 108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,103,101,
+ 116, 32,102,117,110, 99,116,105,111,110, 58, 34, 44,115,101,
+ 108,102, 46,110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,
+ 101,110,100, 10,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 32, 61, 32,115,101,108,102, 58, 99,102,117,110, 99,
+ 110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 95,103,101,
+ 116, 34, 41, 10,111,117,116,112,117,116, 40, 34,115,116, 97,
+ 116,105, 99, 32,105,110,116, 34, 44,115,101,108,102, 46, 99,
+ 103,101,116,110, 97,109,101, 44, 34, 40,108,117, 97, 95, 83,
+ 116, 97,116,101, 42, 32,116,111,108,117, 97, 95, 83, 41, 34,
+ 41, 10,111,117,116,112,117,116, 40, 34,123, 34, 41, 10, 10,
+ 10,111,117,116,112,117,116, 40, 39, 32,105,110,116, 32,116,
+ 111,108,117, 97, 73, 95,105,110,100,101,120, 59, 39, 41, 10,
+ 10, 10,108,111, 99, 97,108, 32, 95, 44, 95, 44,115,116, 97,
+ 116,105, 99, 32, 61, 32,115,116,114,102,105,110,100, 40,115,
+ 101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42, 40,115,
+ 116, 97,116,105, 99, 41, 39, 41, 10,105,102, 32, 99,108, 97,
+ 115,115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,
+ 110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 32, 39, 44, 99,108, 97,115,115, 44, 39, 42, 39, 44,
+ 39,115,101,108,102, 59, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39, 32,108,117, 97, 95,112,117,115,104,115,116,114,105,
+ 110,103, 40,116,111,108,117, 97, 95, 83, 44, 34, 46,115,101,
+ 108,102, 34, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39, 32,108,117, 97, 95,114, 97,119,103,101,116, 40,116,111,
+ 108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,115,101,108,102, 32, 61, 32, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 40, 39, 44, 99,108, 97,
+ 115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39,108,117, 97, 95,116,111,117,115,101,114,100, 97,
+ 116, 97, 40,116,111,108,117, 97, 95, 83, 44, 45, 49, 41, 59,
+ 39, 41, 10,101,108,115,101,105,102, 32,115,116, 97,116,105,
+ 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115,101,108,102,
+ 46,109,111,100, 32, 61, 32,115,116,114,102,105,110,100, 40,
+ 115,101,108,102, 46,109,111,100, 44, 39, 94, 37,115, 42,115,
+ 116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 39,
+ 41, 10,101,110,100, 10, 10, 10,111,117,116,112,117,116, 40,
+ 39, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95,105,115,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 50, 44,
+ 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82, 44, 48, 41, 41,
+ 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,
+ 117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95,
+ 83, 44, 34,105,110,118, 97,108,105,100, 32,116,121,112,101,
+ 32,105,110, 32, 97,114,114, 97,121, 32,105,110,100,101,120,
+ 105,110,103, 46, 34, 41, 59, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 73, 95,105,110,100,101,
+ 120, 32, 61, 32, 40,105,110,116, 41,116,111,108,117, 97, 95,
+ 103,101,116,110,117,109, 98,101,114, 40,116,111,108,117, 97,
+ 95, 83, 44, 50, 44, 48, 41, 45, 49, 59, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39, 32,105,102, 32, 40,116,111,108,117,
+ 97, 73, 95,105,110,100,101,120, 60, 48, 32,124,124, 32,116,
+ 111,108,117, 97, 73, 95,105,110,100,101,120, 62, 61, 39, 46,
+ 46,115,101,108,102, 46,100,105,109, 46, 46, 39, 41, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97,
+ 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 97,114,114, 97,121, 32,105,110,100,101,120,105,110,103,
+ 32,111,117,116, 32,111,102, 32,114, 97,110,103,101, 46, 34,
+ 41, 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32,116, 44,
+ 99,116, 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,
+ 108,102, 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,
+ 108,117, 97, 95,112,117,115,104, 39, 46, 46,116, 46, 46, 39,
+ 40,116,111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44,
+ 39, 41, 39, 46, 46,115,101,108,102, 58,103,101,116,118, 97,
+ 108,117,101, 40, 99,108, 97,115,115, 44,115,116, 97,116,105,
+ 99, 41, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,
+ 105,102, 32,115,101,108,102, 46,112,116,114, 32, 61, 61, 32,
+ 39, 38, 39, 32,111,114, 32,115,101,108,102, 46,112,116,114,
+ 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,
+ 104,117,115,101,114,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 40,118,111,105,100, 42, 41, 38, 39, 46, 46,115,
+ 101,108,102, 58,103,101,116,118, 97,108,117,101, 40, 99,108,
+ 97,115,115, 44,115,116, 97,116,105, 99, 41, 46, 46, 39, 44,
+ 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39,
+ 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,
+ 111,105,100, 42, 41, 39, 46, 46,115,101,108,102, 58,103,101,
+ 116,118, 97,108,117,101, 40, 99,108, 97,115,115, 44,115,116,
+ 97,116,105, 99, 41, 46, 46, 39, 44, 39, 44,115,101,108,102,
+ 46,116, 97,103, 44, 39, 41, 59, 39, 41, 10,101,110,100, 10,
+ 101,110,100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,
+ 116,117,114,110, 32, 49, 59, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 92,110, 39, 41, 10, 10, 10,105,102, 32,110,111,116, 32,115,
+ 116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100,
+ 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101,110, 10,
+ 105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,111,
+ 117,116,112,117,116, 40, 34, 47, 42, 32,115,101,116, 32,102,
+ 117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,
+ 110, 97,109,101, 44, 34, 32,111,102, 32, 99,108, 97,115,115,
+ 32, 34, 44, 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41,
+ 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 34, 47,
+ 42, 32,115,101,116, 32,102,117,110, 99,116,105,111,110, 58,
+ 34, 44,115,101,108,102, 46,110, 97,109,101, 44, 34, 32, 42,
+ 47, 34, 41, 10,101,110,100, 10,115,101,108,102, 46, 99,115,
+ 101,116,110, 97,109,101, 32, 61, 32,115,101,108,102, 58, 99,
+ 102,117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97,
+ 73, 95,115,101,116, 34, 41, 10,111,117,116,112,117,116, 40,
+ 34,115,116, 97,116,105, 99, 32,105,110,116, 34, 44,115,101,
+ 108,102, 46, 99,115,101,116,110, 97,109,101, 44, 34, 40,108,
+ 117, 97, 95, 83,116, 97,116,101, 42, 32,116,111,108,117, 97,
+ 95, 83, 41, 34, 41, 10,111,117,116,112,117,116, 40, 34,123,
+ 34, 41, 10, 10, 10,111,117,116,112,117,116, 40, 39, 32,105,
+ 110,116, 32,116,111,108,117, 97, 73, 95,105,110,100,101,120,
+ 59, 39, 41, 10, 10, 10,108,111, 99, 97,108, 32, 95, 44, 95,
+ 44,115,116, 97,116,105, 99, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37,
+ 115, 42, 40,115,116, 97,116,105, 99, 41, 39, 41, 10,105,102,
+ 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,
+ 105, 99, 61, 61,110,105,108, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32, 39, 44, 99,108, 97,115,115, 44,
+ 39, 42, 39, 44, 39,115,101,108,102, 59, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39, 32,108,117, 97, 95,112,117,115,104,
+ 115,116,114,105,110,103, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 46,115,101,108,102, 34, 41, 59, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,108,117, 97, 95,114, 97,119,103,101,
+ 116, 40,116,111,108,117, 97, 95, 83, 44, 49, 41, 59, 39, 41,
+ 10,111,117,116,112,117,116, 40, 39, 32,115,101,108,102, 32,
+ 61, 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39,
+ 44, 99,108, 97,115,115, 44, 39, 42, 41, 32, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39,108,117, 97, 95,116,111,117,115,
+ 101,114,100, 97,116, 97, 40,116,111,108,117, 97, 95, 83, 44,
+ 45, 49, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115,
+ 116, 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,
+ 115,101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102,
+ 105,110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94,
+ 37,115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40,
+ 46, 42, 41, 39, 41, 10,101,110,100, 10, 10, 10,111,117,116,
+ 112,117,116, 40, 39, 32,105,102, 32, 40, 33,116,111,108,117,
+ 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97, 95,
+ 83, 44, 50, 44, 76, 85, 65, 95, 84, 78, 85, 77, 66, 69, 82,
+ 44, 48, 41, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,116,111,108,117, 97, 95,101,114,114,111,114, 40,116,111,
+ 108,117, 97, 95, 83, 44, 34,105,110,118, 97,108,105,100, 32,
+ 116,121,112,101, 32,105,110, 32, 97,114,114, 97,121, 32,105,
+ 110,100,101,120,105,110,103, 46, 34, 41, 59, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 73, 95,
+ 105,110,100,101,120, 32, 61, 32, 40,105,110,116, 41,116,111,
+ 108,117, 97, 95,103,101,116,110,117,109, 98,101,114, 40,116,
+ 111,108,117, 97, 95, 83, 44, 50, 44, 48, 41, 45, 49, 59, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32,105,102, 32, 40,
+ 116,111,108,117, 97, 73, 95,105,110,100,101,120, 60, 48, 32,
+ 124,124, 32,116,111,108,117, 97, 73, 95,105,110,100,101,120,
+ 62, 61, 39, 46, 46,115,101,108,102, 46,100,105,109, 46, 46,
+ 39, 41, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,
+ 111,108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 97,114,114, 97,121, 32,105,110,100,101,
+ 120,105,110,103, 32,111,117,116, 32,111,102, 32,114, 97,110,
+ 103,101, 46, 34, 41, 59, 39, 41, 10, 10, 10,108,111, 99, 97,
+ 108, 32,112,116,114, 32, 61, 32, 39, 39, 10,105,102, 32,115,
+ 101,108,102, 46,112,116,114,126, 61, 39, 39, 32,116,104,101,
+ 110, 32,112,116,114, 32, 61, 32, 39, 42, 39, 32,101,110,100,
+ 10,111,117,116,112,117,116, 40, 39, 32, 39, 41, 10,105,102,
+ 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,116, 97,116,
+ 105, 99, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 99,108, 97,115,115, 46, 46, 39, 58, 58, 39, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117,
+ 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,108,115,
+ 101,105,102, 32, 99,108, 97,115,115, 32,116,104,101,110, 10,
+ 111,117,116,112,117,116, 40, 39,115,101,108,102, 45, 62, 39,
+ 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 39, 91,
+ 116,111,108,117, 97, 73, 95,105,110,100,101,120, 93, 39, 41,
+ 10,101,108,115,101, 10,111,117,116,112,117,116, 40,115,101,
+ 108,102, 46,110, 97,109,101, 46, 46, 39, 91,116,111,108,117,
+ 97, 73, 95,105,110,100,101,120, 93, 39, 41, 10,101,110,100,
+ 10,108,111, 99, 97,108, 32,116, 32, 61, 32,105,115, 98, 97,
+ 115,105, 99, 40,115,101,108,102, 46,116,121,112,101, 41, 10,
+ 111,117,116,112,117,116, 40, 39, 32, 61, 32, 39, 41, 10,105,
+ 102, 32,110,111,116, 32,116, 32, 97,110,100, 32,112,116,114,
+ 61, 61, 39, 39, 32,116,104,101,110, 32,111,117,116,112,117,
+ 116, 40, 39, 42, 39, 41, 32,101,110,100, 10,111,117,116,112,
+ 117,116, 40, 39, 40, 40, 39, 44,115,101,108,102, 46,109,111,
+ 100, 44,115,101,108,102, 46,116,121,112,101, 41, 10,105,102,
+ 32,110,111,116, 32,116, 32,116,104,101,110, 10,111,117,116,
+ 112,117,116, 40, 39, 42, 39, 41, 10,101,110,100, 10,111,117,
+ 116,112,117,116, 40, 39, 41, 32, 39, 41, 10,108,111, 99, 97,
+ 108, 32,100,101,102, 32, 61, 32, 48, 10,105,102, 32,115,101,
+ 108,102, 46,100,101,102, 32,126, 61, 32, 39, 39, 32,116,104,
+ 101,110, 32,100,101,102, 32, 61, 32,115,101,108,102, 46,100,
+ 101,102, 32,101,110,100, 10,105,102, 32,116, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39,116,111,108,117, 97,
+ 95,103,101,116, 39, 46, 46,116, 44, 39, 40,116,111,108,117,
+ 97, 95, 83, 44, 51, 44, 39, 44,100,101,102, 44, 39, 41, 41,
+ 59, 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 51, 44,
+ 39, 44,100,101,102, 44, 39, 41, 41, 59, 39, 41, 10,101,110,
+ 100, 10,111,117,116,112,117,116, 40, 39, 32,114,101,116,117,
+ 114,110, 32, 48, 59, 39, 41, 10,111,117,116,112,117,116, 40,
+ 39,125, 39, 41, 10,111,117,116,112,117,116, 40, 39, 92,110,
+ 39, 41, 10,101,110,100, 10, 10,101,110,100, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 65,114,114,
+ 97,121, 58,114,101,103,105,115,116,101,114, 32, 40, 41, 10,
+ 108,111, 99, 97,108, 32,112, 97,114,101,110,116, 32, 61, 32,
+ 115,101,108,102, 58,105,110, 99,108, 97,115,115, 40, 41, 32,
+ 111,114, 32,115,101,108,102, 58,105,110,109,111,100,117,108,
+ 101, 40, 41, 10,105,102, 32,112, 97,114,101,110,116, 32,116,
+ 104,101,110, 10,105,102, 32,115,101,108,102, 46, 99,115,101,
+ 116,110, 97,109,101, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,
+ 101, 97,114,114, 97,121, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 46, 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44,
+ 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,101, 46,
+ 46, 39, 34, 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,
+ 116,110, 97,109,101, 46, 46, 39, 44, 39, 46, 46,115,101,108,
+ 102, 46, 99,115,101,116,110, 97,109,101, 46, 46, 39, 41, 59,
+ 39, 41, 10,101,108,115,101, 10,111,117,116,112,117,116, 40,
+ 39, 32,116,111,108,117, 97, 95,116, 97, 98,108,101, 97,114,
+ 114, 97,121, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46,
+ 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34,
+ 44, 39, 46, 46,115,101,108,102, 46, 99,103,101,116,110, 97,
+ 109,101, 46, 46, 39, 44, 78, 85, 76, 76, 41, 59, 39, 41, 10,
+ 101,110,100, 10,101,108,115,101, 10,105,102, 32,115,101,108,
+ 102, 46, 99,115,101,116,110, 97,109,101, 32,116,104,101,110,
+ 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117, 97,
+ 95,103,108,111, 98, 97,108, 97,114,114, 97,121, 40,116,111,
+ 108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,
+ 108,110, 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,
+ 108,102, 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44,
+ 39, 46, 46,115,101,108,102, 46, 99,115,101,116,110, 97,109,
+ 101, 46, 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,
+ 117,116,112,117,116, 40, 39, 32,116,111,108,117, 97, 95,103,
+ 108,111, 98, 97,108, 97,114,114, 97,121, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110,
+ 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102,
+ 46, 99,103,101,116,110, 97,109,101, 46, 46, 39, 44, 78, 85,
+ 76, 76, 41, 59, 39, 41, 10,101,110,100, 10,101,110,100, 10,
+ 101,110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 65,114,114, 97,121, 58,117,110,114,101,103,
+ 105,115,116,101,114, 32, 40, 41, 10,105,102, 32,115,101,108,
+ 102, 58,105,110, 99,108, 97,115,115, 40, 41, 61, 61,110,105,
+ 108, 32, 97,110,100, 32,115,101,108,102, 58,105,110,109,111,
+ 100,117,108,101, 40, 41, 61, 61,110,105,108, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,108,117, 97, 95,
+ 112,117,115,104,110,105,108, 40,116,111,108,117, 97, 95, 83,
+ 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97,
+ 108, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46, 46,115,
+ 101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34, 41, 59,
+ 39, 41, 10,101,110,100, 10,101,110,100, 10, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 95, 65,114,114, 97,121, 32,
+ 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,
+ 108, 97,115,115, 65,114,114, 97,121, 10,115,101,116,116, 97,
+ 103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,117,114,
+ 110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 65,114,114, 97,121, 32, 40,115, 41, 10,
+ 114,101,116,117,114,110, 32, 95, 65,114,114, 97,121, 32, 40,
+ 68,101, 99,108, 97,114, 97,116,105,111,110, 40,115, 44, 39,
+ 118, 97,114, 39, 41, 41, 10,101,110,100, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 99,108, 97,115,115, 70,117,110, 99,116,105,111,
+ 110, 32, 61, 32,123, 10,109,111,100, 32, 61, 32, 39, 39, 44,
+ 10,116,121,112,101, 32, 61, 32, 39, 39, 44, 10,112,116,114,
+ 32, 61, 32, 39, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39,
+ 39, 44, 10, 97,114,103,115, 32, 61, 32,123,110, 61, 48,125,
+ 44, 10, 99,111,110,115,116, 32, 61, 32, 39, 39, 44, 10, 95,
+ 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 70,101, 97,
+ 116,117,114,101, 44, 10,125, 10,115,101,116,116, 97,103, 40,
+ 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 44,116,
+ 111,108,117, 97, 95,116, 97,103, 41, 10, 10, 10,102,117,110,
+ 99,116,105,111,110, 32, 99,108, 97,115,115, 70,117,110, 99,
+ 116,105,111,110, 58,100,101, 99,108,116, 97,103, 32, 40, 41,
+ 10,115,101,108,102, 46,105,116,121,112,101, 44,115,101,108,
+ 102, 46,116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,
+ 115,101,108,102, 46,116,121,112,101, 44,115,116,114,102,105,
+ 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 99,111,
+ 110,115,116, 39, 41, 41, 10,108,111, 99, 97,108, 32,105, 61,
+ 49, 10,119,104,105,108,101, 32,115,101,108,102, 46, 97,114,
+ 103,115, 91,105, 93, 32,100,111, 10,115,101,108,102, 46, 97,
+ 114,103,115, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40,
+ 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10, 10, 10,102,117,110, 99,116,105,111,110,
+ 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,
+ 115,117,112, 99,111,100,101, 32, 40, 41, 10,108,111, 99, 97,
+ 108, 32,110,114,101,116, 32, 61, 32, 48, 10,108,111, 99, 97,
+ 108, 32, 99,108, 97,115,115, 32, 61, 32,115,101,108,102, 58,
+ 105,110, 99,108, 97,115,115, 40, 41, 10,108,111, 99, 97,108,
+ 32, 95, 44, 95, 44,115,116, 97,116,105, 99, 32, 61, 32,115,
+ 116,114,102,105,110,100, 40,115,101,108,102, 46,109,111,100,
+ 44, 39, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 39,
+ 41, 10, 10,105,102, 32, 99,108, 97,115,115, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,109,101,
+ 116,104,111,100, 58, 34, 44,115,101,108,102, 46,110, 97,109,
+ 101, 44, 34, 32,111,102, 32, 99,108, 97,115,115, 32, 34, 44,
+ 99,108, 97,115,115, 44, 34, 32, 42, 47, 34, 41, 10,101,108,
+ 115,101, 10,111,117,116,112,117,116, 40, 34, 47, 42, 32,102,
+ 117,110, 99,116,105,111,110, 58, 34, 44,115,101,108,102, 46,
+ 110, 97,109,101, 44, 34, 32, 42, 47, 34, 41, 10,101,110,100,
+ 10,111,117,116,112,117,116, 40, 34,115,116, 97,116,105, 99,
+ 32,105,110,116, 34, 44,115,101,108,102, 46, 99,110, 97,109,
+ 101, 44, 34, 40,108,117, 97, 95, 83,116, 97,116,101, 42, 32,
+ 116,111,108,117, 97, 95, 83, 41, 34, 41, 10,111,117,116,112,
+ 117,116, 40, 34,123, 34, 41, 10, 10, 10,111,117,116,112,117,
+ 116, 40, 39, 32,105,102, 32, 40, 92,110, 39, 41, 10, 10,108,
+ 111, 99, 97,108, 32,110, 97,114,103, 10,105,102, 32, 99,108,
+ 97,115,115, 32,116,104,101,110, 32,110, 97,114,103, 61, 50,
+ 32,101,108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110,
+ 100, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,
+ 115,101,108,102, 46,110, 97,109,101,126, 61, 39,110,101,119,
+ 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,
+ 105,108, 32,116,104,101,110, 10,105,102, 32,115,101,108,102,
+ 46, 99,111,110,115,116, 32, 61, 61, 32, 39, 99,111,110,115,
+ 116, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32, 33,116,111,108,117, 97, 95,105,115,116,121,112,101,
+ 40,116,111,108,117, 97, 95, 83, 44, 49, 44, 39, 44,115,101,
+ 108,102, 46,112, 97,114,101,110,116, 46, 99,116, 97,103, 44,
+ 39, 44, 48, 41, 32,124,124, 92,110, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32, 33,116,111,108,
+ 117, 97, 95,105,115,116,121,112,101, 40,116,111,108,117, 97,
+ 95, 83, 44, 49, 44, 39, 44,115,101,108,102, 46,112, 97,114,
+ 101,110,116, 46,116, 97,103, 44, 39, 44, 48, 41, 32,124,124,
+ 92,110, 39, 41, 10,101,110,100, 10,101,110,100, 10, 10,105,
+ 102, 32,115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,
+ 116,121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,
+ 116,104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,
+ 119,104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115,
+ 91,105, 93, 32,100,111, 10,105,102, 32,105,115, 98, 97,115,
+ 105, 99, 40,115,101,108,102, 46, 97,114,103,115, 91,105, 93,
+ 46,116,121,112,101, 41, 32,126, 61, 32, 39,118, 97,108,117,
+ 101, 39, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32, 33, 39, 46, 46,115,101,108,102, 46, 97,114,103,115,
+ 91,105, 93, 58,111,117,116, 99,104,101, 99,107,116,121,112,
+ 101, 40,110, 97,114,103, 41, 46, 46, 39, 32,124,124, 92,110,
+ 39, 41, 10,101,110,100, 10,110, 97,114,103, 32, 61, 32,110,
+ 97,114,103, 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,
+ 110,100, 10,101,110,100, 10, 10,111,117,116,112,117,116, 40,
+ 39, 32, 33,116,111,108,117, 97, 95,105,115,110,111,111, 98,
+ 106, 40,116,111,108,117, 97, 95, 83, 44, 39, 46, 46,110, 97,
+ 114,103, 46, 46, 39, 41, 92,110, 32, 41, 92,110, 32,103,111,
+ 116,111, 32,116,111,108,117, 97, 95,108,101,114,114,111,114,
+ 59, 39, 41, 10, 10,111,117,116,112,117,116, 40, 39, 32,101,
+ 108,115,101, 92,110, 32,123, 39, 41, 10, 10, 10,108,111, 99,
+ 97,108, 32,110, 97,114,103, 10,105,102, 32, 99,108, 97,115,
+ 115, 32,116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101,
+ 108,115,101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,
+ 105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,115,101,
+ 108,102, 46,110, 97,109,101,126, 61, 39,110,101,119, 39, 32,
+ 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,105,108,
+ 32,116,104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,
+ 39, 44,115,101,108,102, 46, 99,111,110,115,116, 44, 99,108,
+ 97,115,115, 44, 39, 42, 39, 44, 39,115,101,108,102, 32, 61,
+ 32, 39, 41, 10,111,117,116,112,117,116, 40, 39, 40, 39, 44,
+ 115,101,108,102, 46, 99,111,110,115,116, 44, 99,108, 97,115,
+ 115, 44, 39, 42, 41, 32, 39, 41, 10,111,117,116,112,117,116,
+ 40, 39,116,111,108,117, 97, 95,103,101,116,117,115,101,114,
+ 116,121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 49, 44,
+ 48, 41, 59, 39, 41, 10,101,108,115,101,105,102, 32,115,116,
+ 97,116,105, 99, 32,116,104,101,110, 10, 95, 44, 95, 44,115,
+ 101,108,102, 46,109,111,100, 32, 61, 32,115,116,114,102,105,
+ 110,100, 40,115,101,108,102, 46,109,111,100, 44, 39, 94, 37,
+ 115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46,
+ 42, 41, 39, 41, 10,101,110,100, 10, 10,105,102, 32,115,101,
+ 108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,121,112,101,
+ 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,101,110,
+ 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,
+ 101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,
+ 100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93,
+ 58,100,101, 99,108, 97,114,101, 40,110, 97,114,103, 41, 10,
+ 110, 97,114,103, 32, 61, 32,110, 97,114,103, 43, 49, 10,105,
+ 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100, 10,
+ 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,110,100, 32,
+ 115,101,108,102, 46,110, 97,109,101,126, 61, 39,110,101,119,
+ 39, 32, 97,110,100, 32,115,116, 97,116,105, 99, 61, 61,110,
+ 105,108, 32,116,104,101,110, 10,111,117,116,112,117,116, 40,
+ 39, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111,
+ 108,117, 97, 95,101,114,114,111,114, 40,116,111,108,117, 97,
+ 95, 83, 44, 34,105,110,118, 97,108,105,100, 32, 92, 39,115,
+ 101,108,102, 92, 39, 32,105,110, 32,102,117,110, 99,116,105,
+ 111,110, 32, 92, 39, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 39, 92, 39, 34, 41, 59, 39, 41, 59, 10,101,
+ 110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,116,
+ 104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115,101,
+ 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,105,102, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,121,
+ 112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,
+ 101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,
+ 105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,105,
+ 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,
+ 105, 93, 58,103,101,116, 97,114,114, 97,121, 40,110, 97,114,
+ 103, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103, 43,
+ 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32, 97,
+ 110,100, 32,115,101,108,102, 46,110, 97,109,101, 61, 61, 39,
+ 100,101,108,101,116,101, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,100,101,108,101,116,101, 32,115,
+ 101,108,102, 59, 39, 41, 10,101,108,115,101,105,102, 32, 99,
+ 108, 97,115,115, 32, 97,110,100, 32,115,101,108,102, 46,110,
+ 97,109,101, 32, 61, 61, 32, 39,111,112,101,114, 97,116,111,
+ 114, 38, 91, 93, 39, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39, 32,115,101,108,102, 45, 62,111,112,101,114,
+ 97,116,111,114, 91, 93, 40, 39, 44,115,101,108,102, 46, 97,
+ 114,103,115, 91, 49, 93, 46,110, 97,109,101, 44, 39, 41, 32,
+ 61, 32, 39, 44,115,101,108,102, 46, 97,114,103,115, 91, 50,
+ 93, 46,110, 97,109,101, 44, 39, 59, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32,123, 39, 41, 10,
+ 105,102, 32,115,101,108,102, 46,116,121,112,101, 32,126, 61,
+ 32, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,116,121,
+ 112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 39, 32, 39, 44,115,
+ 101,108,102, 46,109,111,100, 44,115,101,108,102, 46,116,121,
+ 112,101, 44,115,101,108,102, 46,112,116,114, 44, 39,116,111,
+ 108,117, 97, 73, 95,114,101,116, 32, 61, 32, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 40, 39, 44,115,101,108,102, 46,
+ 109,111,100, 44,115,101,108,102, 46,116,121,112,101, 44,115,
+ 101,108,102, 46,112,116,114, 44, 39, 41, 32, 39, 41, 10,101,
+ 108,115,101, 10,111,117,116,112,117,116, 40, 39, 32, 39, 41,
+ 10,101,110,100, 10,105,102, 32, 99,108, 97,115,115, 32, 97,
+ 110,100, 32,115,101,108,102, 46,110, 97,109,101, 61, 61, 39,
+ 110,101,119, 39, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39,110,101,119, 39, 44, 99,108, 97,115,115, 44, 39,
+ 40, 39, 41, 10,101,108,115,101,105,102, 32, 99,108, 97,115,
+ 115, 32, 97,110,100, 32,115,116, 97,116,105, 99, 32,116,104,
+ 101,110, 10,111,117,116,112,117,116, 40, 99,108, 97,115,115,
+ 46, 46, 39, 58, 58, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 44, 39, 40, 39, 41, 10,101,108,115,101,105,102, 32,
+ 99,108, 97,115,115, 32,116,104,101,110, 10,111,117,116,112,
+ 117,116, 40, 39,115,101,108,102, 45, 62, 39, 46, 46,115,101,
+ 108,102, 46,110, 97,109,101, 44, 39, 40, 39, 41, 10,101,108,
+ 115,101, 10,111,117,116,112,117,116, 40,115,101,108,102, 46,
+ 110, 97,109,101, 44, 39, 40, 39, 41, 10,101,110,100, 10, 10,
+ 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,
+ 101, 32,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,
+ 100,111, 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93,
+ 58,112, 97,115,115,112, 97,114, 40, 41, 10,105, 32, 61, 32,
+ 105, 43, 49, 10,105,102, 32,115,101,108,102, 46, 97,114,103,
+ 115, 91,105, 93, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39, 44, 39, 41, 10,101,110,100, 10,101,110,100, 10,
+ 10,111,117,116,112,117,116, 40, 39, 41, 59, 39, 41, 10, 10,
+ 10,105,102, 32,115,101,108,102, 46,116,121,112,101, 32,126,
+ 61, 32, 39, 39, 32, 97,110,100, 32,115,101,108,102, 46,116,
+ 121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,
+ 104,101,110, 10,110,114,101,116, 32, 61, 32,110,114,101,116,
+ 32, 43, 32, 49, 10,108,111, 99, 97,108, 32,116, 44, 99,116,
+ 32, 61, 32,105,115, 98, 97,115,105, 99, 40,115,101,108,102,
+ 46,116,121,112,101, 41, 10,105,102, 32,116, 32,116,104,101,
+ 110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,112,117,115,104, 39, 46, 46,116, 46, 46, 39, 40,116,
+ 111,108,117, 97, 95, 83, 44, 40, 39, 44, 99,116, 44, 39, 41,
+ 116,111,108,117, 97, 73, 95,114,101,116, 41, 59, 39, 41, 10,
+ 101,108,115,101, 10,105,102, 32,115,101,108,102, 46,112,116,
+ 114, 32, 61, 61, 32, 39, 39, 32,116,104,101,110, 10,111,117,
+ 116,112,117,116, 40, 39, 32,123, 39, 41, 10,111,117,116,112,
+ 117,116, 40, 39, 35,105,102,100,101,102, 32, 95, 95, 99,112,
+ 108,117,115,112,108,117,115, 92,110, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 32,118,111,105,100, 42, 32,116,111,108,
+ 117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119,
+ 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39, 40,116,
+ 111,108,117, 97, 73, 95,114,101,116, 41, 59, 39, 41, 10,111,
+ 117,116,112,117,116, 40, 39, 35,101,108,115,101, 92,110, 39,
+ 41, 10,111,117,116,112,117,116, 40, 39, 32,118,111,105,100,
+ 42, 32,116,111,108,117, 97, 73, 95, 99,108,111,110,101, 32,
+ 61, 32,116,111,108,117, 97, 95, 99,111,112,121, 40,116,111,
+ 108,117, 97, 95, 83, 44, 40,118,111,105,100, 42, 41, 38,116,
+ 111,108,117, 97, 73, 95,114,101,116, 44,115,105,122,101,111,
+ 102, 40, 39, 44,115,101,108,102, 46,116,121,112,101, 44, 39,
+ 41, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 35,
+ 101,110,100,105,102, 92,110, 39, 41, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 95,112,117,115,104,117,
+ 115,101,114,116,121,112,101, 40,116,111,108,117, 97, 95, 83,
+ 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,
+ 116,111,108,117, 97, 95, 83, 44,116,111,108,117, 97, 73, 95,
+ 99,108,111,110,101, 44, 39, 44,115,101,108,102, 46,116, 97,
+ 103, 44, 39, 41, 44, 39, 44,115,101,108,102, 46,116, 97,103,
+ 44, 39, 41, 59, 39, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,125, 39, 41, 10, 10,101,108,115,101,105,102, 32,115,101,
+ 108,102, 46,112,116,114, 32, 61, 61, 32, 39, 38, 39, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,116,111,
+ 108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,
+ 101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,105,100,
+ 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,116, 44, 39,
+ 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39, 41,
+ 10,101,108,115,101, 10,111,117,116,112,117,116, 40, 39, 32,
+ 116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,
+ 121,112,101, 40,116,111,108,117, 97, 95, 83, 44, 40,118,111,
+ 105,100, 42, 41,116,111,108,117, 97, 73, 95,114,101,116, 44,
+ 39, 44,115,101,108,102, 46,116, 97,103, 44, 39, 41, 59, 39,
+ 41, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10,108,
+ 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111,
+ 10,110,114,101,116, 32, 61, 32,110,114,101,116, 32, 43, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,114,101,
+ 116,118, 97,108,117,101, 40, 41, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,111,117,116,112,117,116, 40, 39, 32,
+ 125, 39, 41, 10, 10, 10,105,102, 32, 99,108, 97,115,115, 32,
+ 116,104,101,110, 32,110, 97,114,103, 61, 50, 32,101,108,115,
+ 101, 32,110, 97,114,103, 61, 49, 32,101,110,100, 10,105,102,
+ 32,115,101,108,102, 46, 97,114,103,115, 91, 49, 93, 46,116,
+ 121,112,101, 32,126, 61, 32, 39,118,111,105,100, 39, 32,116,
+ 104,101,110, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,
+ 104,105,108,101, 32,115,101,108,102, 46, 97,114,103,115, 91,
+ 105, 93, 32,100,111, 10,115,101,108,102, 46, 97,114,103,115,
+ 91,105, 93, 58,115,101,116, 97,114,114, 97,121, 40,110, 97,
+ 114,103, 41, 10,110, 97,114,103, 32, 61, 32,110, 97,114,103,
+ 43, 49, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,105,102, 32,115,101,108,102, 46, 97,
+ 114,103,115, 91, 49, 93, 46,116,121,112,101, 32,126, 61, 32,
+ 39,118,111,105,100, 39, 32,116,104,101,110, 10,108,111, 99,
+ 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,
+ 108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,
+ 101,108,102, 46, 97,114,103,115, 91,105, 93, 58,102,114,101,
+ 101, 97,114,114, 97,121, 40, 41, 10,105, 32, 61, 32,105, 43,
+ 49, 10,101,110,100, 10,101,110,100, 10,101,110,100, 10, 10,
+ 111,117,116,112,117,116, 40, 39, 32,125, 39, 41, 10,111,117,
+ 116,112,117,116, 40, 39, 32,114,101,116,117,114,110, 32, 39,
+ 46, 46,110,114,101,116, 46, 46, 39, 59, 39, 41, 10, 10, 10,
+ 111,117,116,112,117,116, 40, 39,116,111,108,117, 97, 95,108,
+ 101,114,114,111,114, 58, 92,110, 39, 41, 10,108,111, 99, 97,
+ 108, 32,111,118,101,114,108,111, 97,100, 32, 61, 32,115,116,
+ 114,115,117, 98, 40,115,101,108,102, 46, 99,110, 97,109,101,
+ 44, 45, 50, 44, 45, 49, 41, 32, 45, 32, 49, 10,105,102, 32,
+ 111,118,101,114,108,111, 97,100, 32, 62, 61, 32, 48, 32,116,
+ 104,101,110, 10,111,117,116,112,117,116, 40, 39, 32,114,101,
+ 116,117,114,110, 32, 39, 46, 46,115,116,114,115,117, 98, 40,
+ 115,101,108,102, 46, 99,110, 97,109,101, 44, 49, 44, 45, 51,
+ 41, 46, 46,102,111,114,109, 97,116, 40, 34, 37, 48, 50,100,
+ 34, 44,111,118,101,114,108,111, 97,100, 41, 46, 46, 39, 40,
+ 116,111,108,117, 97, 95, 83, 41, 59, 39, 41, 10,101,108,115,
+ 101, 10,111,117,116,112,117,116, 40, 39, 32,116,111,108,117,
+ 97, 95,101,114,114,111,114, 40,116,111,108,117, 97, 95, 83,
+ 44, 34, 35,102,101,114,114,111,114, 32,105,110, 32,102,117,
+ 110, 99,116,105,111,110, 32, 92, 39, 39, 46, 46,115,101,108,
+ 102, 46,108,110, 97,109,101, 46, 46, 39, 92, 39, 46, 34, 41,
+ 59, 39, 41, 10,111,117,116,112,117,116, 40, 39, 32,114,101,
+ 116,117,114,110, 32, 48, 59, 39, 41, 10,101,110,100, 10, 10,
+ 111,117,116,112,117,116, 40, 39,125, 39, 41, 10,111,117,116,
+ 112,117,116, 40, 39, 92,110, 39, 41, 10,101,110,100, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,115,
+ 70,117,110, 99,116,105,111,110, 58,114,101,103,105,115,116,
+ 101,114, 32, 40, 41, 10,108,111, 99, 97,108, 32,112, 97,114,
+ 101,110,116, 32, 61, 32,115,101,108,102, 58,105,110, 99,108,
+ 97,115,115, 40, 41, 32,111,114, 32,115,101,108,102, 58,105,
+ 110,109,111,100,117,108,101, 40, 41, 10,105,102, 32,112, 97,
+ 114,101,110,116, 32,116,104,101,110, 10,111,117,116,112,117,
+ 116, 40, 39, 32,116,111,108,117, 97, 95,102,117,110, 99,116,
+ 105,111,110, 40,116,111,108,117, 97, 95, 83, 44, 34, 39, 46,
+ 46,112, 97,114,101,110,116, 46, 46, 39, 34, 44, 34, 39, 46,
+ 46,115,101,108,102, 46,108,110, 97,109,101, 46, 46, 39, 34,
+ 44, 39, 46, 46,115,101,108,102, 46, 99,110, 97,109,101, 46,
+ 46, 39, 41, 59, 39, 41, 10,101,108,115,101, 10,111,117,116,
+ 112,117,116, 40, 39, 32,116,111,108,117, 97, 95,102,117,110,
+ 99,116,105,111,110, 40,116,111,108,117, 97, 95, 83, 44, 78,
+ 85, 76, 76, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110,
+ 97,109,101, 46, 46, 39, 34, 44, 39, 46, 46,115,101,108,102,
+ 46, 99,110, 97,109,101, 46, 46, 39, 41, 59, 39, 41, 10,101,
+ 110,100, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,
+ 110, 58,117,110,114,101,103,105,115,116,101,114, 32, 40, 41,
+ 10,105,102, 32,115,101,108,102, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 61, 61,110,105,108, 32, 97,110,100, 32,115,101,
+ 108,102, 58,105,110,109,111,100,117,108,101, 40, 41, 61, 61,
+ 110,105,108, 32,116,104,101,110, 10,111,117,116,112,117,116,
+ 40, 39, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,
+ 116,111,108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,
+ 101,116,103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95,
+ 83, 44, 34, 39, 46, 46,115,101,108,102, 46,108,110, 97,109,
+ 101, 46, 46, 39, 34, 41, 59, 39, 41, 10,101,110,100, 10,101,
+ 110,100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32,
+ 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 58,112,
+ 114,105,110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,
+ 115,101, 41, 10,112,114,105,110,116, 40,105,100,101,110,116,
+ 46, 46, 34, 70,117,110, 99,116,105,111,110,123, 34, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,
+ 109,111,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,
+ 109,111,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,116,121,112,101,
+ 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,116,121,112,
+ 101, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32,112,116,114, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,112,116,114, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32, 39, 34, 46,
+ 46,115,101,108,102, 46,110, 97,109,101, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32, 99,111,110,115,116, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46, 99,111,110,115,116, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32, 99,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46, 99,110, 97,109,101, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32,108,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,108,110, 97,109,101, 46, 46, 34, 39, 44,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32, 97,114,103,115, 32, 61, 32,123, 34, 41, 10,108,
+ 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,
+ 115,101,108,102, 46, 97,114,103,115, 91,105, 93, 32,100,111,
+ 10,115,101,108,102, 46, 97,114,103,115, 91,105, 93, 58,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 34,
+ 44, 34, 44, 34, 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,
+ 110,100, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34, 32,125, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34,125, 34, 46, 46, 99,108,111,115,101,
+ 41, 10,101,110,100, 10, 10, 10,102,117,110, 99,116,105,111,
+ 110, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110,
+ 58,111,118,101,114,108,111, 97,100, 32, 40, 41, 10,114,101,
+ 116,117,114,110, 32,115,101,108,102, 46,112, 97,114,101,110,
+ 116, 58,111,118,101,114,108,111, 97,100, 40,115,101,108,102,
+ 46,108,110, 97,109,101, 41, 10,101,110,100, 10, 10, 10, 10,
+ 10,102,117,110, 99,116,105,111,110, 32, 95, 70,117,110, 99,
+ 116,105,111,110, 32, 40,116, 41, 10,116, 46, 95, 98, 97,115,
+ 101, 32, 61, 32, 99,108, 97,115,115, 70,117,110, 99,116,105,
+ 111,110, 10,115,101,116,116, 97,103, 40,116, 44,116,111,108,
+ 117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,116, 46, 99,
+ 111,110,115,116, 32,126, 61, 32, 39, 99,111,110,115,116, 39,
+ 32, 97,110,100, 32,116, 46, 99,111,110,115,116, 32,126, 61,
+ 32, 39, 39, 32,116,104,101,110, 10,101,114,114,111,114, 40,
+ 34, 35,105,110,118, 97,108,105,100, 32, 39, 99,111,110,115,
+ 116, 39, 32,115,112,101, 99,105,102,105, 99, 97,116,105,111,
+ 110, 34, 41, 10,101,110,100, 10, 10, 97,112,112,101,110,100,
+ 40,116, 41, 10,105,102, 32,116, 58,105,110, 99,108, 97,115,
+ 115, 40, 41, 32,116,104,101,110, 10,105,102, 32,116, 46,110,
+ 97,109,101, 32, 61, 61, 32,116, 46,112, 97,114,101,110,116,
+ 46,110, 97,109,101, 32,116,104,101,110, 10,116, 46,110, 97,
+ 109,101, 32, 61, 32, 39,110,101,119, 39, 10,116, 46,108,110,
+ 97,109,101, 32, 61, 32, 39,110,101,119, 39, 10,116, 46,116,
+ 121,112,101, 32, 61, 32,116, 46,112, 97,114,101,110,116, 46,
+ 110, 97,109,101, 10,116, 46,112,116,114, 32, 61, 32, 39, 42,
+ 39, 10,101,108,115,101,105,102, 32,116, 46,110, 97,109,101,
+ 32, 61, 61, 32, 39,126, 39, 46, 46,116, 46,112, 97,114,101,
+ 110,116, 46,110, 97,109,101, 32,116,104,101,110, 10,116, 46,
+ 110, 97,109,101, 32, 61, 32, 39,100,101,108,101,116,101, 39,
+ 10,116, 46,108,110, 97,109,101, 32, 61, 32, 39,100,101,108,
+ 101,116,101, 39, 10,101,110,100, 10,101,110,100, 10,116, 46,
+ 99,110, 97,109,101, 32, 61, 32,116, 58, 99,102,117,110, 99,
+ 110, 97,109,101, 40, 34,116,111,108,117, 97, 73, 34, 41, 46,
+ 46,116, 58,111,118,101,114,108,111, 97,100, 40,116, 41, 10,
+ 114,101,116,117,114,110, 32,116, 10,101,110,100, 10, 10, 10,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 70,117,110,
+ 99,116,105,111,110, 32, 40,100, 44, 97, 44, 99, 41, 10,108,
+ 111, 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,
+ 115,116,114,115,117, 98, 40, 97, 44, 50, 44, 45, 50, 41, 44,
+ 39, 44, 39, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,
+ 108,111, 99, 97,108, 32,108, 32, 61, 32,123,110, 61, 48,125,
+ 10,119,104,105,108,101, 32,116, 91,105, 93, 32,100,111, 10,
+ 108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10,108, 91,108,
+ 46,110, 93, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,105,
+ 111,110, 40,116, 91,105, 93, 44, 39,118, 97,114, 39, 41, 10,
+ 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,108,111, 99,
+ 97,108, 32,102, 32, 61, 32, 68,101, 99,108, 97,114, 97,116,
+ 105,111,110, 40,100, 44, 39,102,117,110, 99, 39, 41, 10,102,
+ 46, 97,114,103,115, 32, 61, 32,108, 10,102, 46, 99,111,110,
+ 115,116, 32, 61, 32, 99, 10,114,101,116,117,114,110, 32, 95,
+ 70,117,110, 99,116,105,111,110, 40,102, 41, 10,101,110,100,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 99,108, 97,115,115, 79,112,101,114, 97,116,
+ 111,114, 32, 61, 32,123, 10,107,105,110,100, 32, 61, 32, 39,
+ 39, 44, 10, 95, 98, 97,115,101, 32, 61, 32, 99,108, 97,115,
+ 115, 70,117,110, 99,116,105,111,110, 44, 10,125, 10,115,101,
+ 116,116, 97,103, 40, 99,108, 97,115,115, 79,112,101,114, 97,
+ 116,111,114, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 10, 10, 95, 84, 77, 32, 61, 32,123, 91, 39, 43, 39, 93, 32,
+ 61, 32, 39,111,112,101,114, 97,116,111,114, 95, 97,100,100,
+ 39, 44, 10, 91, 39, 45, 39, 93, 32, 61, 32, 39,111,112,101,
+ 114, 97,116,111,114, 95,115,117, 98, 39, 44, 10, 91, 39, 42,
+ 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,111,114, 95,
+ 109,117,108, 39, 44, 10, 91, 39, 47, 39, 93, 32, 61, 32, 39,
+ 111,112,101,114, 97,116,111,114, 95,100,105,118, 39, 44, 10,
+ 91, 39, 60, 39, 93, 32, 61, 32, 39,111,112,101,114, 97,116,
+ 111,114, 95,108,116, 39, 44, 10, 91, 39, 91, 93, 39, 93, 32,
+ 61, 32, 39,111,112,101,114, 97,116,111,114, 95,103,101,116,
+ 39, 44, 10, 91, 39, 38, 91, 93, 39, 93, 32, 61, 32, 39,111,
+ 112,101,114, 97,116,111,114, 95,115,101,116, 39, 44, 10,125,
+ 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 97,115,115, 79,112,101,114, 97,116,111,114, 58,112,114,105,
+ 110,116, 32, 40,105,100,101,110,116, 44, 99,108,111,115,101,
+ 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46,
+ 34, 79,112,101,114, 97,116,111,114,123, 34, 41, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,107,105,
+ 110,100, 32, 61, 32, 39, 34, 46, 46,115,101,108,102, 46,107,
+ 105,110,100, 46, 46, 34, 39, 44, 34, 41, 10,112,114,105,110,
+ 116, 40,105,100,101,110,116, 46, 46, 34, 32,109,111,100, 32,
+ 61, 32, 39, 34, 46, 46,115,101,108,102, 46,109,111,100, 46,
+ 46, 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,
+ 101,110,116, 46, 46, 34, 32,116,121,112,101, 32, 61, 32, 39,
+ 34, 46, 46,115,101,108,102, 46,116,121,112,101, 46, 46, 34,
+ 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,
+ 116, 46, 46, 34, 32,112,116,114, 32, 61, 32, 39, 34, 46, 46,
+ 115,101,108,102, 46,112,116,114, 46, 46, 34, 39, 44, 34, 41,
+ 10,112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34,
+ 32,110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,
+ 102, 46,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99,
+ 111,110,115,116, 32, 61, 32, 39, 34, 46, 46,115,101,108,102,
+ 46, 99,111,110,115,116, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 99,
+ 110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102,
+ 46, 99,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,108,
+ 110, 97,109,101, 32, 61, 32, 39, 34, 46, 46,115,101,108,102,
+ 46,108,110, 97,109,101, 46, 46, 34, 39, 44, 34, 41, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32, 97,
+ 114,103,115, 32, 61, 32,123, 34, 41, 10,108,111, 99, 97,108,
+ 32,105, 61, 49, 10,119,104,105,108,101, 32,115,101,108,102,
+ 46, 97,114,103,115, 91,105, 93, 32,100,111, 10,115,101,108,
+ 102, 46, 97,114,103,115, 91,105, 93, 58,112,114,105,110,116,
+ 40,105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34,
+ 41, 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,
+ 114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 32,125,
+ 34, 41, 10,112,114,105,110,116, 40,105,100,101,110,116, 46,
+ 46, 34,125, 34, 46, 46, 99,108,111,115,101, 41, 10,101,110,
+ 100, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 95, 79,
+ 112,101,114, 97,116,111,114, 32, 40,116, 41, 10,116, 46, 95,
+ 98, 97,115,101, 32, 61, 32, 99,108, 97,115,115, 79,112,101,
+ 114, 97,116,111,114, 10,115,101,116,116, 97,103, 40,116, 44,
+ 116,111,108,117, 97, 95,116, 97,103, 41, 10, 10,105,102, 32,
+ 116, 46, 99,111,110,115,116, 32,126, 61, 32, 39, 99,111,110,
+ 115,116, 39, 32, 97,110,100, 32,116, 46, 99,111,110,115,116,
+ 32,126, 61, 32, 39, 39, 32,116,104,101,110, 10,101,114,114,
+ 111,114, 40, 34, 35,105,110,118, 97,108,105,100, 32, 39, 99,
+ 111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97,
+ 116,105,111,110, 34, 41, 10,101,110,100, 10, 10, 97,112,112,
+ 101,110,100, 40,116, 41, 10,105,102, 32,110,111,116, 32,116,
+ 58,105,110, 99,108, 97,115,115, 40, 41, 32,116,104,101,110,
+ 10,101,114,114,111,114, 40, 34, 35,111,112,101,114, 97,116,
+ 111,114, 32, 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,
+ 100,101,102,105,110,101,100, 32, 97,115, 32, 99,108, 97,115,
+ 115, 32,109,101,109, 98,101,114, 34, 41, 10,101,110,100, 10,
+ 10,116, 46, 99,110, 97,109,101, 32, 61, 32,116, 58, 99,102,
+ 117,110, 99,110, 97,109,101, 40, 34,116,111,108,117, 97, 73,
+ 34, 41, 46, 46,116, 58,111,118,101,114,108,111, 97,100, 40,
+ 116, 41, 10,116, 46,110, 97,109,101, 32, 61, 32,116, 46,110,
+ 97,109,101, 46, 46,116, 46,107,105,110,100, 10,114,101,116,
+ 117,114,110, 32,116, 10,101,110,100, 10, 10, 10, 10, 10, 10,
+ 102,117,110, 99,116,105,111,110, 32, 79,112,101,114, 97,116,
+ 111,114, 32, 40,100, 44,107, 44, 97, 44, 99, 41, 10,108,111,
+ 99, 97,108, 32,116, 32, 61, 32,115,112,108,105,116, 40,115,
+ 116,114,115,117, 98, 40, 97, 44, 50, 44,115,116,114,108,101,
+ 110, 40, 97, 41, 45, 49, 41, 44, 39, 44, 39, 41, 10,108,111,
+ 99, 97,108, 32,105, 61, 49, 10,108,111, 99, 97,108, 32,108,
+ 32, 61, 32,123,110, 61, 48,125, 10,119,104,105,108,101, 32,
+ 116, 91,105, 93, 32,100,111, 10,108, 46,110, 32, 61, 32,108,
+ 46,110, 43, 49, 10,108, 91,108, 46,110, 93, 32, 61, 32, 68,
+ 101, 99,108, 97,114, 97,116,105,111,110, 40,116, 91,105, 93,
+ 44, 39,118, 97,114, 39, 41, 10,105, 32, 61, 32,105, 43, 49,
+ 10,101,110,100, 10,105,102, 32,107, 32, 61, 61, 32, 39, 91,
+ 93, 39, 32,116,104,101,110, 10,100, 32, 61, 32,103,115,117,
+ 98, 40,100, 44, 39, 38, 39, 44, 39, 39, 41, 10,101,108,115,
+ 101,105,102, 32,107, 61, 61, 39, 38, 91, 93, 39, 32,116,104,
+ 101,110, 10,108, 46,110, 32, 61, 32,108, 46,110, 43, 49, 10,
+ 108, 91,108, 46,110, 93, 32, 61, 32, 68,101, 99,108, 97,114,
+ 97,116,105,111,110, 40,100, 44, 39,118, 97,114, 39, 41, 10,
+ 108, 91,108, 46,110, 93, 46,110, 97,109,101, 32, 61, 32, 39,
+ 116,111,108,117, 97, 73, 95,118, 97,108,117,101, 39, 10,101,
+ 110,100, 10,108,111, 99, 97,108, 32,102, 32, 61, 32, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 40,100, 44, 39,102,117,
+ 110, 99, 39, 41, 10,105,102, 32,107, 32, 61, 61, 32, 39, 91,
+ 93, 39, 32, 97,110,100, 32, 40,108, 91, 49, 93, 61, 61,110,
+ 105,108, 32,111,114, 32,105,115, 98, 97,115,105, 99, 40,108,
+ 91, 49, 93, 46,116,121,112,101, 41,126, 61, 39,110,117,109,
+ 98,101,114, 39, 41, 32,116,104,101,110, 10,101,114,114,111,
+ 114, 40, 39,111,112,101,114, 97,116,111,114, 91, 93, 32, 99,
+ 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105,
+ 110,101,100, 32,102,111,114, 32,110,117,109,101,114,105, 99,
+ 32,105,110,100,101,120, 46, 39, 41, 10,101,110,100, 10,102,
+ 46, 97,114,103,115, 32, 61, 32,108, 10,102, 46, 99,111,110,
+ 115,116, 32, 61, 32, 99, 10,102, 46,107,105,110,100, 32, 61,
+ 32,103,115,117, 98, 40,107, 44, 34, 37,115, 34, 44, 34, 34,
+ 41, 10,102, 46,108,110, 97,109,101, 32, 61, 32, 95, 84, 77,
+ 91,102, 46,107,105,110,100, 93, 10,105,102, 32,110,111,116,
+ 32,102, 46,108,110, 97,109,101, 32,116,104,101,110, 10,101,
+ 114,114,111,114, 40, 34,116,111,108,117, 97, 58, 32,110,111,
+ 32,115,117,112,112,111,114,116, 32,102,111,114, 32,111,112,
+ 101,114, 97,116,111,114, 34, 32, 46, 46, 32,102, 46,107,105,
+ 110,100, 41, 10,101,110,100, 10,105,102, 32,102, 46,107,105,
+ 110,100, 32, 61, 61, 32, 39, 91, 93, 39, 32, 97,110,100, 32,
+ 110,111,116, 32,115,116,114,102,105,110,100, 40,102, 46,109,
+ 111,100, 44, 39, 99,111,110,115,116, 39, 41, 32,116,104,101,
+ 110, 10, 79,112,101,114, 97,116,111,114, 40,100, 44, 39, 38,
+ 39, 46, 46,107, 44, 97, 44, 99, 41, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32, 95, 79,112,101,114, 97,116,111,114,
+ 40,102, 41, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 99,108, 97,115,
+ 115, 67,108, 97,115,115, 32, 61, 32,123, 10, 95, 98, 97,115,
+ 101, 32, 61, 32, 99,108, 97,115,115, 67,111,110,116, 97,105,
+ 110,101,114, 44, 10,116,121,112,101, 32, 61, 32, 39, 99,108,
+ 97,115,115, 39, 44, 10,110, 97,109,101, 32, 61, 32, 39, 39,
+ 44, 10, 98, 97,115,101, 32, 61, 32, 39, 39, 44, 10,125, 10,
+ 115,101,116,116, 97,103, 40, 99,108, 97,115,115, 67,108, 97,
+ 115,115, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10, 10,
+ 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,115,
+ 115, 67,108, 97,115,115, 58,114,101,103,105,115,116,101,114,
+ 32, 40, 41, 10,111,117,116,112,117,116, 40, 39, 32,116,111,
+ 108,117, 97, 95, 99, 99,108, 97,115,115, 40,116,111,108,117,
+ 97, 95, 83, 44, 34, 39, 46, 46,115,101,108,102, 46,110, 97,
+ 109,101, 46, 46, 39, 34, 44, 34, 39, 46, 46,115,101,108,102,
+ 46, 98, 97,115,101, 46, 46, 39, 34, 41, 59, 39, 41, 10,108,
+ 111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,101, 32,
+ 115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,108,102,
+ 91,105, 93, 58,114,101,103,105,115,116,101,114, 40, 41, 10,
+ 105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,100,
+ 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108, 97,
+ 115,115, 67,108, 97,115,115, 58,117,110,114,101,103,105,115,
+ 116,101,114, 32, 40, 41, 10,111,117,116,112,117,116, 40, 39,
+ 32,108,117, 97, 95,112,117,115,104,110,105,108, 40,116,111,
+ 108,117, 97, 95, 83, 41, 59, 32,108,117, 97, 95,115,101,116,
+ 103,108,111, 98, 97,108, 40,116,111,108,117, 97, 95, 83, 44,
+ 34, 39, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46,
+ 39, 34, 41, 59, 39, 41, 10,101,110,100, 10, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32, 99,108, 97,115,115, 67,108, 97,
+ 115,115, 58,100,101, 99,108,116, 97,103, 32, 40, 41, 10,115,
+ 101,108,102, 46,105,116,121,112,101, 44,115,101,108,102, 46,
+ 116, 97,103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,
+ 108,102, 46,110, 97,109,101, 41, 59, 10,115,101,108,102, 46,
+ 99,105,116,121,112,101, 44,115,101,108,102, 46, 99,116, 97,
+ 103, 32, 61, 32,116, 97,103,118, 97,114, 40,115,101,108,102,
+ 46,110, 97,109,101, 44, 39, 99,111,110,115,116, 39, 41, 59,
+ 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,104,105,108,
+ 101, 32,115,101,108,102, 91,105, 93, 32,100,111, 10,115,101,
+ 108,102, 91,105, 93, 58,100,101, 99,108,116, 97,103, 40, 41,
+ 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,101,110,
+ 100, 10, 10, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,
+ 108, 97,115,115, 67,108, 97,115,115, 58,112,114,105,110,116,
+ 32, 40,105,100,101,110,116, 44, 99,108,111,115,101, 41, 10,
+ 112,114,105,110,116, 40,105,100,101,110,116, 46, 46, 34, 67,
+ 108, 97,115,115,123, 34, 41, 10,112,114,105,110,116, 40,105,
+ 100,101,110,116, 46, 46, 34, 32,110, 97,109,101, 32, 61, 32,
+ 39, 34, 46, 46,115,101,108,102, 46,110, 97,109,101, 46, 46,
+ 34, 39, 44, 34, 41, 10,112,114,105,110,116, 40,105,100,101,
+ 110,116, 46, 46, 34, 32, 98, 97,115,101, 32, 61, 32, 39, 34,
+ 46, 46,115,101,108,102, 46, 98, 97,115,101, 46, 46, 34, 39,
+ 59, 34, 41, 10,108,111, 99, 97,108, 32,105, 61, 49, 10,119,
+ 104,105,108,101, 32,115,101,108,102, 91,105, 93, 32,100,111,
+ 10,115,101,108,102, 91,105, 93, 58,112,114,105,110,116, 40,
+ 105,100,101,110,116, 46, 46, 34, 32, 34, 44, 34, 44, 34, 41,
+ 10,105, 32, 61, 32,105, 43, 49, 10,101,110,100, 10,112,114,
+ 105,110,116, 40,105,100,101,110,116, 46, 46, 34,125, 34, 46,
+ 46, 99,108,111,115,101, 41, 10,101,110,100, 10, 10, 10,102,
+ 117,110, 99,116,105,111,110, 32, 95, 67,108, 97,115,115, 32,
+ 40,116, 41, 10,116, 46, 95, 98, 97,115,101, 32, 61, 32, 99,
+ 108, 97,115,115, 67,108, 97,115,115, 10,115,101,116,116, 97,
+ 103, 40,116, 44,116,111,108,117, 97, 95,116, 97,103, 41, 10,
+ 97,112,112,101,110,100, 40,116, 41, 10,114,101,116,117,114,
+ 110, 32,116, 10,101,110,100, 10, 10, 10, 10,102,117,110, 99,
+ 116,105,111,110, 32, 67,108, 97,115,115, 32, 40,110, 44,112,
+ 44, 98, 41, 10,108,111, 99, 97,108, 32, 99, 32, 61, 32, 95,
+ 67,108, 97,115,115, 40, 95, 67,111,110,116, 97,105,110,101,
+ 114,123,110, 97,109,101, 61,110, 44, 32, 98, 97,115,101, 61,
+ 112,125, 41, 10,112,117,115,104, 40, 99, 41, 10, 99, 58,112,
+ 97,114,115,101, 40,115,116,114,115,117, 98, 40, 98, 44, 50,
+ 44,115,116,114,108,101,110, 40, 98, 41, 45, 49, 41, 41, 10,
+ 112,111,112, 40, 41, 10,101,110,100, 10, 10, 10, 10, 83, 84,
+ 82, 49, 32, 61, 32, 34, 92, 48, 48, 49, 34, 10, 83, 84, 82,
+ 50, 32, 61, 32, 34, 92, 48, 48, 50, 34, 10, 83, 84, 82, 51,
+ 32, 61, 32, 34, 92, 48, 48, 51, 34, 10, 83, 84, 82, 52, 32,
+ 61, 32, 34, 92, 48, 48, 52, 34, 10, 82, 69, 77, 32, 61, 32,
+ 34, 92, 48, 48, 53, 34, 10, 65, 78, 89, 32, 61, 32, 34, 40,
+ 91, 92, 48, 48, 49, 45, 92, 48, 48, 53, 93, 41, 34, 10, 69,
+ 83, 67, 49, 32, 61, 32, 34, 92, 48, 48, 54, 34, 10, 69, 83,
+ 67, 50, 32, 61, 32, 34, 92, 48, 48, 55, 34, 10, 10, 77, 65,
+ 83, 75, 32, 61, 32,123, 10,123, 69, 83, 67, 49, 44, 32, 34,
+ 92, 92, 39, 34,125, 44, 10,123, 69, 83, 67, 50, 44, 32, 39,
+ 92, 92, 34, 39,125, 44, 10,123, 83, 84, 82, 49, 44, 32, 34,
+ 39, 34,125, 44, 10,123, 83, 84, 82, 50, 44, 32, 39, 34, 39,
+ 125, 44, 10,123, 83, 84, 82, 51, 44, 32, 34, 37, 91, 37, 91,
+ 34,125, 44, 10,123, 83, 84, 82, 52, 44, 32, 34, 37, 93, 37,
+ 93, 34,125, 44, 10,123, 82, 69, 77, 32, 44, 32, 34, 37, 45,
+ 37, 45, 34,125, 44, 10,125, 10, 10,102,117,110, 99,116,105,
+ 111,110, 32,109, 97,115,107, 32, 40,115, 41, 10,102,111,114,
+ 32,105, 32, 61, 32, 49, 44,103,101,116,110, 40, 77, 65, 83,
+ 75, 41, 32,100,111, 10,115, 32, 61, 32,103,115,117, 98, 40,
+ 115, 44, 77, 65, 83, 75, 91,105, 93, 91, 50, 93, 44, 77, 65,
+ 83, 75, 91,105, 93, 91, 49, 93, 41, 10,101,110,100, 10,114,
+ 101,116,117,114,110, 32,115, 10,101,110,100, 10, 10,102,117,
+ 110, 99,116,105,111,110, 32,117,110,109, 97,115,107, 32, 40,
+ 115, 41, 10,102,111,114, 32,105, 32, 61, 32, 49, 44,103,101,
+ 116,110, 40, 77, 65, 83, 75, 41, 32,100,111, 10,115, 32, 61,
+ 32,103,115,117, 98, 40,115, 44, 77, 65, 83, 75, 91,105, 93,
+ 91, 49, 93, 44, 77, 65, 83, 75, 91,105, 93, 91, 50, 93, 41,
+ 10,101,110,100, 10,114,101,116,117,114,110, 32,115, 10,101,
+ 110,100, 10, 10,102,117,110, 99,116,105,111,110, 32, 99,108,
+ 101, 97,110, 32, 40,115, 41, 10, 10,108,111, 99, 97,108, 32,
+ 99,111,100,101, 32, 61, 32, 34,114,101,116,117,114,110, 32,
+ 102,117,110, 99,116,105,111,110, 32, 40, 41, 32, 34, 32, 46,
+ 46, 32,115, 32, 46, 46, 32, 34, 32,101,110,100, 34, 10,105,
+ 102, 32,110,111,116, 32,100,111,115,116,114,105,110,103, 40,
+ 99,111,100,101, 41, 32,116,104,101,110, 10,114,101,116,117,
+ 114,110, 32,110,105,108, 10,101,110,100, 10, 10,108,111, 99,
+ 97,108, 32, 83, 32, 61, 32, 34, 34, 10, 10,115, 32, 61, 32,
+ 109, 97,115,107, 40,115, 41, 10, 10, 10,119,104,105,108,101,
+ 32, 49, 32,100,111, 10,108,111, 99, 97,108, 32, 98, 44,101,
+ 44,100, 32, 61, 32,115,116,114,102,105,110,100, 40,115, 44,
+ 65, 78, 89, 41, 10,105,102, 32, 98, 32,116,104,101,110, 10,
+ 83, 32, 61, 32, 83, 46, 46,115,116,114,115,117, 98, 40,115,
+ 44, 49, 44, 98, 45, 49, 41, 10,115, 32, 61, 32,115,116,114,
+ 115,117, 98, 40,115, 44, 98, 43, 49, 41, 10,105,102, 32,100,
+ 61, 61, 83, 84, 82, 49, 32,111,114, 32,100, 61, 61, 83, 84,
+ 82, 50, 32,116,104,101,110, 10,101, 32, 61, 32,115,116,114,
+ 102,105,110,100, 40,115, 44,100, 41, 10, 83, 32, 61, 32, 83,
+ 32, 46, 46,100, 46, 46,115,116,114,115,117, 98, 40,115, 44,
+ 49, 44,101, 41, 10,115, 32, 61, 32,115,116,114,115,117, 98,
+ 40,115, 44,101, 43, 49, 41, 10,101,108,115,101,105,102, 32,
+ 100, 61, 61, 83, 84, 82, 51, 32,116,104,101,110, 10,101, 32,
+ 61, 32,115,116,114,102,105,110,100, 40,115, 44, 83, 84, 82,
+ 52, 41, 10, 83, 32, 61, 32, 83, 46, 46,100, 46, 46,115,116,
+ 114,115,117, 98, 40,115, 44, 49, 44,101, 41, 10,115, 32, 61,
+ 32,115,116,114,115,117, 98, 40,115, 44,101, 43, 49, 41, 10,
+ 101,108,115,101,105,102, 32,100, 61, 61, 82, 69, 77, 32,116,
+ 104,101,110, 10,115, 32, 61, 32,103,115,117, 98, 40,115, 44,
+ 34, 91, 94, 92,110, 93, 42, 40, 92,110, 63, 41, 34, 44, 34,
+ 37, 49, 34, 44, 49, 41, 10,101,110,100, 10,101,108,115,101,
+ 10, 83, 32, 61, 32, 83, 46, 46,115, 10, 98,114,101, 97,107,
+ 10,101,110,100, 10,101,110,100, 10, 10, 83, 32, 61, 32,103,
+ 115,117, 98, 40, 83, 44, 34, 91, 32, 92,116, 93, 43, 34, 44,
+ 34, 32, 34, 41, 10, 83, 32, 61, 32,103,115,117, 98, 40, 83,
+ 44, 34, 91, 32, 92,116, 93, 42, 92,110, 91, 32, 92,116, 93,
+ 42, 34, 44, 34, 92,110, 34, 41, 10, 83, 32, 61, 32,117,110,
+ 109, 97,115,107, 40, 83, 41, 10,114,101,116,117,114,110, 32,
+ 83, 10,101,110,100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10,105,102, 32,102,108, 97,103,115, 46,102,
+ 32,116,104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,
+ 32,109,115,103, 32, 61, 32,114,101, 97,100,102,114,111,109,
+ 40,102,108, 97,103,115, 46,102, 41, 10,105,102, 32,110,111,
+ 116, 32,115,116, 32,116,104,101,110, 10,101,114,114,111,114,
+ 40, 39, 35, 39, 46, 46,109,115,103, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10, 10,105,102, 32,110,111,116, 32,102,108,
+ 97,103,115, 46,110, 32,116,104,101,110, 10,105,102, 32,102,
+ 108, 97,103,115, 46,102, 32,116,104,101,110, 10,102,108, 97,
+ 103,115, 46,110, 32, 61, 32,103,115,117, 98, 40,102,108, 97,
+ 103,115, 46,102, 44, 34, 37, 46, 46, 42, 34, 44, 34, 34, 41,
+ 10,101,108,115,101, 10,101,114,114,111,114, 40, 34, 35,110,
+ 111, 32,112, 97, 99,107, 97,103,101, 32,110, 97,109,101, 32,
+ 110,111,114, 32,105,110,112,117,116, 32,102,105,108,101, 32,
+ 112,114,111,118,105,100,101,100, 34, 41, 10,101,110,100, 10,
+ 101,110,100, 10, 10,108,111, 99, 97,108, 32,112, 32, 61, 32,
+ 80, 97, 99,107, 97,103,101, 40,102,108, 97,103,115, 46,110,
+ 41, 10, 10,105,102, 32,102,108, 97,103,115, 46,102, 32,116,
+ 104,101,110, 10,114,101, 97,100,102,114,111,109, 40, 41, 10,
+ 101,110,100, 10, 10,105,102, 32,102,108, 97,103,115, 46,112,
+ 32,116,104,101,110, 10,114,101,116,117,114,110, 10,101,110,
+ 100, 10, 10,105,102, 32,102,108, 97,103,115, 46,111, 32,116,
+ 104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,109,115,
+ 103, 32, 61, 32,119,114,105,116,101,116,111, 40,102,108, 97,
+ 103,115, 46,111, 41, 10,105,102, 32,110,111,116, 32,115,116,
+ 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35, 39,
+ 46, 46,109,115,103, 41, 10,101,110,100, 10,101,110,100, 10,
+ 10,105,102, 32,102,108, 97,103,115, 46, 80, 32,116,104,101,
+ 110, 10,112, 58,112,114,105,110,116, 40, 41, 10,101,108,115,
+ 101, 10,112, 58,100,101, 99,108,116, 97,103, 40, 41, 10,112,
+ 58,112,114,101, 97,109, 98,108,101, 40, 41, 10,112, 58,115,
+ 117,112, 99,111,100,101, 40, 41, 10,112, 58,114,101,103,105,
+ 115,116,101,114, 40, 41, 10,112, 58,117,110,114,101,103,105,
+ 115,116,101,114, 40, 41, 10,101,110,100, 10, 10,105,102, 32,
+ 102,108, 97,103,115, 46,111, 32,116,104,101,110, 10,119,114,
+ 105,116,101,116,111, 40, 41, 10,101,110,100, 10, 10, 10,105,
+ 102, 32,110,111,116, 32,102,108, 97,103,115, 46, 80, 32,116,
+ 104,101,110, 10,105,102, 32,102,108, 97,103,115, 46, 72, 32,
+ 116,104,101,110, 10,108,111, 99, 97,108, 32,115,116, 44,109,
+ 115,103, 32, 61, 32,119,114,105,116,101,116,111, 40,102,108,
+ 97,103,115, 46, 72, 41, 10,105,102, 32,110,111,116, 32,115,
+ 116, 32,116,104,101,110, 10,101,114,114,111,114, 40, 39, 35,
+ 39, 46, 46,109,115,103, 41, 10,101,110,100, 10,112, 58,104,
+ 101, 97,100,101,114, 40, 41, 10,119,114,105,116,101,116,111,
+ 40, 41, 10,101,110,100, 10,101,110,100,32
+ };
+ lua_dobuffer(tolua_S,(char*)B,sizeof(B),"tolua: embedded Lua code");
+ } /* end of embedded lua code */
+
+ return 1;
+}
+/* Close function */
+void tolua_tolualua_close (lua_State* tolua_S)
+{
+}
diff --git a/src/lua/tolualua.h b/src/lua/tolualua.h
new file mode 100644
index 00000000..b380dcef
--- /dev/null
+++ b/src/lua/tolualua.h
@@ -0,0 +1,2713 @@
+/* code automatically generated by bin2c -- DO NOT EDIT */
+{
+/* #include'ing this file in a C program is equivalent to calling
+ lua_dofile("basic.lo");
+ lua_dofile("feature.lo");
+ lua_dofile("declaration.lo");
+ lua_dofile("container.lo");
+ lua_dofile("package.lo");
+ lua_dofile("module.lo");
+ lua_dofile("class.lo");
+ lua_dofile("typedef.lo");
+ lua_dofile("define.lo");
+ lua_dofile("enumerate.lo");
+ lua_dofile("variable.lo");
+ lua_dofile("array.lo");
+ lua_dofile("function.lo");
+ lua_dofile("operator.lo");
+ lua_dofile("verbatim.lo");
+ lua_dofile("code.lo");
+ lua_dofile("doit.lo");
+*/
+/* basic.lo */
+static char B1[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 98, 97,115,105, 99,
+ 46,108,117, 97, 0, 0, 0, 0,190, 25, 0, 60, 17, 22, 12, 60, 18, 11, 1, 11,
+ 2, 60, 19, 11, 3, 11, 4, 60, 20, 11, 5, 11, 4, 60, 21, 11, 6, 11, 4, 60,
+ 22, 11, 7, 11, 4, 60, 23, 11, 8, 11, 4, 60, 24, 11, 9, 11, 4, 60, 25, 11,
+ 10, 11, 11, 60, 26, 11, 12, 11, 13, 60, 27, 11, 14, 11, 11, 60, 28, 11, 15, 11,
+ 13, 60, 29, 11, 16, 11, 17, 30, 11, 60, 30, 25, 0, 60, 34, 22, 0, 25, 18, 60,
+ 37, 11, 20, 25, 19, 60, 45, 15, 22, 2, 1, 0, 25, 21, 60, 46, 15, 24, 15, 21,
+ 11, 25, 15, 19, 2, 1, 3, 25, 23, 60, 49, 11, 27, 25, 26, 60, 69, 15, 28, 15,
+ 26, 2, 0, 1, 60, 72, 11, 30, 25, 29, 60, 80, 22, 0, 25, 31, 60, 81, 11, 33,
+ 25, 32, 60, 89, 11, 35, 25, 34, 60,109, 11, 37, 25, 36, 60,115, 11, 39, 25, 38,
+ 60,121, 11, 41, 25, 40, 60,138, 11, 43, 25, 42, 60,150, 11, 45, 25, 44, 0, 0,
+ 0, 0, 0, 0, 0, 0, 46, 2, 0, 0, 0, 7, 95, 98, 97,115,105, 99, 0, 2,
+ 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,
+ 99,104, 97,114, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0,
+ 0, 4,105,110,116, 0, 2, 0, 0, 0, 6,115,104,111,114,116, 0, 2, 0, 0,
+ 0, 5,108,111,110,103, 0, 2, 0, 0, 0, 6,102,108,111, 97,116, 0, 2, 0,
+ 0, 0, 7,100,111,117, 98,108,101, 0, 2, 0, 0, 0, 9, 95, 99,115,116,114,
+105,110,103, 0, 2, 0, 0, 0, 7,115,116,114,105,110,103, 0, 2, 0, 0, 0,
+ 10, 95,117,115,101,114,100, 97,116, 97, 0, 2, 0, 0, 0, 9,117,115,101,114,
+100, 97,116, 97, 0, 2, 0, 0, 0, 6, 99,104, 97,114, 42, 0, 2, 0, 0, 0,
+ 6,118,111,105,100, 42, 0, 2, 0, 0, 0, 11,108,117, 97, 95, 79, 98,106,101,
+ 99,116, 0, 2, 0, 0, 0, 7,111, 98,106,101, 99,116, 0, 2, 0, 0, 0, 10,
+ 95,117,115,101,114,116,121,112,101, 0, 2, 0, 0, 0, 12,116,111,108,117, 97,
+ 95,105,110,100,101,120, 0, 4, 0, 0, 0, 37, 0, 0, 0, 11, 64, 98, 97,115,
+105, 99, 46,108,117, 97, 0, 0, 0, 0, 40, 5, 2, 60, 38, 13, 1, 11, 2, 32,
+ 52, 13, 60, 39, 15, 3, 13, 0, 13, 1, 3, 2, 2, 50, 13, 60, 41, 13, 0, 18,
+ 2, 13, 1, 16, 1, 2, 60, 42, 60, 43, 0, 0, 0, 0, 2, 0, 0, 0, 37, 0,
+ 0, 0, 2,116, 0, 0, 0, 0, 37, 0, 0, 0, 2,102, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 16,116,111,108,117, 97, 95,111,108,100, 95,105,
+110,100,101,120, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0,
+ 2, 0, 0, 0, 7,110,101,119,116, 97,103, 0, 2, 0, 0, 0, 16,116,111,108,
+117, 97, 95,111,108,100, 95,105,110,100,101,120, 0, 2, 0, 0, 0, 13,115,101,
+116,116, 97,103,109,101,116,104,111,100, 0, 2, 0, 0, 0, 6,105,110,100,101,
+120, 0, 2, 0, 0, 0, 12,116,111,108,117, 97, 95,101,114,114,111,114, 0, 4,
+ 0, 0, 0, 49, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0, 0,
+ 0, 0,163, 9, 1, 60, 50, 15, 2, 60, 51, 15, 3, 25, 2, 60, 52, 15, 4, 13,
+ 0, 7, 1, 7, 1, 2, 1, 3, 11, 5, 32, 52, 24, 60, 53, 15, 6, 11, 7, 15,
+ 4, 13, 0, 7, 2, 2, 1, 2, 42, 11, 8, 42, 2, 0, 1, 50, 19, 60, 55, 15,
+ 6, 11, 9, 13, 0, 42, 11, 8, 42, 2, 0, 1, 60, 57, 1, 2, 60, 59, 15, 10,
+ 52, 73, 60, 60, 15, 12, 15, 10, 11, 13, 2, 3, 2, 60, 61, 13, 4, 4, 0, 32,
+ 52, 4, 15, 10, 23, 4, 60, 62, 15, 14, 13, 4, 11, 15, 11, 16, 2, 1, 3, 23,
+ 4, 60, 63, 15, 14, 13, 4, 11, 17, 11, 18, 2, 1, 3, 23, 4, 60, 64, 15, 6,
+ 11, 19, 13, 4, 42, 11, 20, 42, 2, 0, 1, 5, 3, 50, 2, 60, 65, 60, 66, 13,
+ 1, 25, 2, 60, 67, 0, 0, 0, 0, 8, 0, 0, 0, 49, 0, 0, 0, 2,115, 0,
+ 0, 0, 0, 50, 0, 0, 0, 4,111,117,116, 0, 0, 0, 0, 60, 0, 0, 0, 2,
+ 95, 0, 0, 0, 0, 60, 0, 0, 0, 2, 95, 0, 0, 0, 0, 60, 0, 0, 0, 2,
+115, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0,
+ 0, 64, 0, 0, 0, 0, 0, 0, 0, 21, 2, 0, 0, 0, 2,115, 0, 2, 0, 0,
+ 0, 4,111,117,116, 0, 2, 0, 0, 0, 8, 95, 79, 85, 84, 80, 85, 84, 0, 2,
+ 0, 0, 0, 8, 95, 83, 84, 68, 69, 82, 82, 0, 2, 0, 0, 0, 7,115,116,114,
+115,117, 98, 0, 2, 0, 0, 0, 2, 35, 0, 2, 0, 0, 0, 6,119,114,105,116,
+101, 0, 2, 0, 0, 0, 12, 10, 42, 42, 32,116,111,108,117, 97, 58, 32, 0, 2,
+ 0, 0, 0, 4, 46, 10, 10, 0, 2, 0, 0, 0, 27, 10, 42, 42, 32,116,111,108,
+117, 97, 32,105,110,116,101,114,110, 97,108, 32,101,114,114,111,114, 58, 32, 0,
+ 2, 0, 0, 0, 11, 95, 99,117,114,114, 95, 99,111,100,101, 0, 2, 0, 0, 0,
+ 2, 95, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0,
+ 10, 94, 37,115, 42, 40, 46, 45, 10, 41, 0, 2, 0, 0, 0, 5,103,115,117, 98,
+ 0, 2, 0, 0, 0, 10, 95,117,115,101,114,100, 97,116, 97, 0, 2, 0, 0, 0,
+ 6,118,111,105,100, 42, 0, 2, 0, 0, 0, 9, 95, 99,115,116,114,105,110,103,
+ 0, 2, 0, 0, 0, 6, 99,104, 97,114, 42, 0, 2, 0, 0, 0, 23, 67,111,100,
+101, 32, 98,101,105,110,103, 32,112,114,111, 99,101,115,115,101,100, 58, 10, 0,
+ 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 15,115,101,116,101,114,114,111,114,
+109,101,116,104,111,100, 0, 2, 0, 0, 0, 8,114,101,103,116,121,112,101, 0,
+ 4, 0, 0, 0, 72, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0,
+ 0, 0, 0, 36, 4, 1, 60, 73, 15, 1, 13, 0, 2, 1, 1, 44, 52, 11, 60, 74,
+ 15, 2, 13, 0, 13, 0, 26, 50, 2, 60, 75, 60, 76, 13, 0, 1, 1, 60, 77, 0,
+ 0, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 2,116, 0, 0, 0, 0, 3, 2, 0,
+ 0, 0, 2,116, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101, 0, 2, 0, 0,
+ 0, 10, 95,117,115,101,114,116,121,112,101, 0, 2, 0, 0, 0, 10, 95,116, 97,
+103,110, 97,109,101,115, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0,
+ 4, 0, 0, 0, 81, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0,
+ 0, 0, 0, 67, 5, 2, 60, 82, 13, 0, 11, 2, 31, 48, 5, 13, 1, 7, 0, 31,
+ 48, 8, 15, 3, 13, 0, 2, 1, 1, 44, 48, 6, 15, 4, 13, 1, 16, 44, 52, 26,
+ 60, 83, 15, 4, 13, 1, 13, 0, 26, 60, 84, 15, 5, 11, 6, 13, 1, 42, 11, 7,
+ 42, 2, 0, 1, 50, 2, 60, 85, 60, 86, 0, 0, 0, 0, 2, 0, 0, 0, 81, 0,
+ 0, 0, 6,105,116,121,112,101, 0, 0, 0, 0, 81, 0, 0, 0, 4,116, 97,103,
+ 0, 0, 0, 0, 8, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0,
+ 4,116, 97,103, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 8,105,115, 98, 97,
+115,105, 99, 0, 2, 0, 0, 0, 10, 95,116, 97,103,110, 97,109,101,115, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 12,115,116, 97,116,
+105, 99, 32,105,110,116, 32, 0, 2, 0, 0, 0, 2, 59, 0, 2, 0, 0, 0, 7,
+116, 97,103,118, 97,114, 0, 4, 0, 0, 0, 89, 0, 0, 0, 11, 64, 98, 97,115,
+105, 99, 46,108,117, 97, 0, 0, 0, 0,155, 8, 2, 60, 90, 13, 0, 11, 2, 32,
+ 46, 5, 13, 0, 11, 3, 32, 52, 10, 60, 91, 13, 0, 7, 0, 1, 2, 50,124, 60,
+ 92, 15, 4, 13, 0, 2, 1, 1, 52, 18, 60, 93, 13, 0, 11, 5, 15, 4, 13, 0,
+ 2, 1, 1, 42, 1, 2, 50, 95, 60, 95, 15, 8, 13, 0, 2, 2, 1, 60, 96, 15,
+ 9, 13, 2, 11, 1, 2, 1, 2, 52, 4, 11, 1, 23, 1, 60, 97, 13, 3, 23, 0,
+ 60, 98, 15, 10, 13, 3, 2, 0, 1, 60, 99, 11, 5, 60,100, 13, 1, 48, 5, 13,
+ 1, 11, 2, 31, 52, 20, 60,101, 11, 12, 13, 3, 42, 23, 3, 60,102, 13, 4, 11,
+ 13, 42, 23, 4, 50, 2, 60,103, 60,104, 13, 3, 13, 4, 13, 0, 42, 1, 5, 5,
+ 3, 60,105, 60,106, 0, 0, 0, 0, 8, 0, 0, 0, 89, 0, 0, 0, 5,116,121,
+112,101, 0, 0, 0, 0, 89, 0, 0, 0, 6, 99,111,110,115,116, 0, 0, 0, 0,
+ 95, 0, 0, 0, 2,109, 0, 0, 0, 0, 95, 0, 0, 0, 2,116, 0, 0, 0, 0,
+ 99, 0, 0, 0, 2,118, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0, 0,104, 0,
+ 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 14, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0, 8,105,115, 98,
+ 97,115,105, 99, 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 95,116, 97,103, 95,
+ 0, 2, 0, 0, 0, 2,109, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 12,
+102,105,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 8,115,116,114,
+102,105,110,100, 0, 2, 0, 0, 0, 8,114,101,103,116,121,112,101, 0, 2, 0,
+ 0, 0, 2,118, 0, 2, 0, 0, 0, 7, 99,111,110,115,116, 32, 0, 2, 0, 0,
+ 0, 7, 99,111,110,115,116, 95, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105,
+ 99, 0, 4, 0, 0, 0,109, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117,
+ 97, 0, 0, 0, 0, 23, 5, 1, 60,110, 15, 3, 13, 0, 2, 2, 1, 60,111, 15,
+ 4, 13, 2, 16, 1, 3, 60,112, 0, 0, 0, 0, 3, 0, 0, 0,109, 0, 0, 0,
+ 5,116,121,112,101, 0, 0, 0, 0,110, 0, 0, 0, 2,109, 0, 0, 0, 0,110,
+ 0, 0, 0, 2,116, 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 2,109, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 12,102,
+105,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 7, 95, 98, 97,115,
+105, 99, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101, 0, 4, 0, 0, 0,115,
+ 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0, 0, 0, 0, 30, 3,
+ 1, 60,116, 15, 1, 13, 0, 16, 46, 5, 15, 2, 13, 0, 16, 46, 7, 15, 3, 13,
+ 0, 2, 1, 1, 1, 1, 60,117, 0, 0, 0, 0, 1, 0, 0, 0,115, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 7, 95,
+ 98, 97,115,105, 99, 0, 2, 0, 0, 0, 10, 95,117,115,101,114,116,121,112,101,
+ 0, 2, 0, 0, 0, 10,105,115,116,121,112,101,100,101,102, 0, 2, 0, 0, 0,
+ 6,115,112,108,105,116, 0, 4, 0, 0, 0,121, 0, 0, 0, 11, 64, 98, 97,115,
+105, 99, 46,108,117, 97, 0, 0, 0, 0,117, 11, 2, 60,122, 22, 1, 11, 3, 7,
+ 0, 30, 0, 60,123, 13, 2, 58, 5, 1, 60,127, 11, 7, 13, 1, 42, 11, 8, 42,
+ 60,128, 15, 9, 13, 0, 11, 10, 11, 11, 2, 1, 3, 23, 0, 60,129, 15, 9, 13,
+ 0, 11, 12, 11, 11, 2, 1, 3, 23, 0, 60,130, 15, 9, 13, 0, 13, 4, 13, 3,
+ 2, 1, 3, 23, 0, 60,131, 13, 2, 11, 14, 13, 2, 18, 14, 7, 1, 37, 26, 60,
+132, 13, 2, 13, 2, 18, 14, 15, 9, 13, 0, 11, 15, 11, 11, 2, 1, 3, 26, 60,
+133, 13, 2, 1, 5, 60,134, 0, 0, 0, 0, 5, 0, 0, 0,121, 0, 0, 0, 2,
+115, 0, 0, 0, 0,121, 0, 0, 0, 2,116, 0, 0, 0, 0,122, 0, 0, 0, 2,
+108, 0, 0, 0, 0,123, 0, 0, 0, 2,102, 0, 0, 0, 0,127, 0, 0, 0, 2,
+112, 0, 0, 0, 0, 16, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 2,116, 0,
+ 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,102,
+ 0, 4, 0, 0, 0,123, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97,
+ 0, 0, 0, 0, 30, 5, 1, 60,124, 12, 0, 11, 2, 12, 0, 18, 2, 7, 1, 37,
+ 26, 60,125, 12, 0, 12, 0, 18, 2, 13, 0, 26, 60,126, 0, 0, 0, 0, 1, 0,
+ 0, 0,123, 0, 0, 0, 2,115, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2,115, 0,
+ 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,112,
+ 0, 2, 0, 0, 0, 11, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 0, 2, 0, 0,
+ 0, 4, 37,115, 42, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0,
+ 5, 94, 37,115, 43, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5, 37,115, 43,
+ 36, 0, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0,
+ 9, 40, 37,115, 37,115, 42, 41, 36, 0, 2, 0, 0, 0, 7, 99,111,110, 99, 97,
+116, 0, 4, 0, 0, 0,138, 0, 0, 0, 11, 64, 98, 97,115,105, 99, 46,108,117,
+ 97, 0, 0, 0, 0, 69, 8, 3, 60,139, 11, 4, 60,140, 13, 1, 50, 39, 60,142,
+ 13, 3, 13, 0, 13, 4, 16, 42, 23, 3, 60,143, 13, 4, 7, 1, 37, 23, 4, 60,
+144, 13, 4, 13, 2, 34, 52, 7, 13, 3, 11, 6, 42, 23, 3, 60,145, 60,141, 13,
+ 4, 13, 2, 34, 54, 48, 60,146, 13, 3, 1, 5, 60,147, 0, 0, 0, 0, 5, 0,
+ 0, 0,138, 0, 0, 0, 2,116, 0, 0, 0, 0,138, 0, 0, 0, 2,102, 0, 0,
+ 0, 0,138, 0, 0, 0, 2,108, 0, 0, 0, 0,139, 0, 0, 0, 2,115, 0, 0,
+ 0, 0,140, 0, 0, 0, 2,105, 0, 0, 0, 0, 7, 2, 0, 0, 0, 2,116, 0,
+ 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,115,
+ 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 2, 32,
+ 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 4, 0, 0, 0,150, 0, 0,
+ 0, 11, 64, 98, 97,115,105, 99, 46,108,117, 97, 0, 0, 0, 0,161, 6,128, 60,
+151, 7, 1, 50,104, 60,153, 15, 3, 48, 10, 15, 4, 15, 3, 11, 5, 2, 1, 2,
+ 44, 48, 14, 60,154, 15, 4, 13, 0, 13, 1, 16, 11, 6, 2, 1, 2, 52, 11, 60,
+155, 15, 7, 11, 8, 2, 0, 1, 50, 2, 60,156, 60,157, 15, 7, 13, 0, 13, 1,
+ 16, 2, 0, 1, 60,158, 13, 0, 13, 1, 16, 11, 9, 31, 52, 20, 60,159, 15, 10,
+ 13, 0, 13, 1, 16, 9, 1, 9, 1, 2, 1, 3, 25, 3, 50, 2, 60,160, 60,161,
+ 13, 1, 7, 1, 37, 23, 1, 60,162, 60,152, 13, 1, 13, 0, 18, 2, 34, 54,115,
+ 60,163, 15, 4, 13, 0, 13, 0, 18, 2, 16, 11, 11, 2, 1, 2, 52, 15, 60,164,
+ 4, 0, 25, 3, 15, 7, 11, 12, 2, 0, 1, 50, 2, 60,165, 60,166, 0, 0, 0,
+ 0, 2, 0, 0, 0,150, 0, 0, 0, 4, 97,114,103, 0, 0, 0, 0,151, 0, 0,
+ 0, 2,105, 0, 0, 0, 0, 13, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 4,
+ 97,114,103, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 6, 95, 99,111,110,
+116, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 7,
+ 91, 37, 40, 44, 34, 93, 0, 2, 0, 0, 0, 8, 94, 91, 37, 97, 95,126, 93, 0,
+ 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0,
+ 14, 91, 37, 47, 37, 41, 37, 59, 37,123, 37,125, 93, 36, 0, 2, 0, 0, 0, 2,
+ 10, 0,
+};
+
+/* feature.lo */
+static char B2[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 13, 64,102,101, 97,116,117,
+114,101, 46,108,117, 97, 0, 0, 0, 0, 83, 3, 0, 60, 16, 22, 0, 60, 17, 25,
+ 0, 60, 20, 15, 0, 11, 1, 11, 2, 26, 60, 24, 15, 0, 11, 3, 11, 4, 26, 60,
+ 28, 15, 0, 11, 5, 11, 6, 26, 60, 32, 15, 0, 11, 7, 11, 8, 26, 60, 36, 15,
+ 0, 11, 9, 11, 10, 26, 60, 40, 15, 0, 11, 11, 11, 12, 26, 60, 49, 15, 0, 11,
+ 13, 11, 14, 26, 60, 59, 15, 0, 11, 15, 11, 16, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 17, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 0,
+ 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 20, 0, 0,
+ 0, 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0, 0, 0, 0, 5, 1,
+ 1, 60, 21, 0, 0, 0, 0, 1, 0, 0, 0, 20, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0, 4, 0,
+ 0, 0, 24, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0,
+ 0, 0, 0, 5, 1, 1, 60, 25, 0, 0, 0, 0, 1, 0, 0, 0, 24, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 0, 2, 0, 0, 0, 9,114,101,103,105,115,
+116,101,114, 0, 4, 0, 0, 0, 28, 0, 0, 0, 13, 64,102,101, 97,116,117,114,
+101, 46,108,117, 97, 0, 0, 0, 0, 5, 1, 1, 60, 29, 0, 0, 0, 0, 1, 0,
+ 0, 0, 28, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 0, 2, 0, 0, 0,
+ 11,117,110,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 32, 0, 0, 0,
+ 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0, 0, 0, 0, 5, 1, 1,
+ 60, 33, 0, 0, 0, 0, 1, 0, 0, 0, 32, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0, 0, 2, 0, 0, 0, 9,112,114,101, 97,109, 98,108,101, 0, 4, 0,
+ 0, 0, 36, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,117, 97, 0,
+ 0, 0, 0, 5, 1, 1, 60, 37, 0, 0, 0, 0, 1, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,
+115,115, 0, 4, 0, 0, 0, 40, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101,
+ 46,108,117, 97, 0, 0, 0, 0, 44, 3, 1, 60, 41, 13, 0, 18, 1, 48, 9, 13,
+ 0, 18, 1, 18, 2, 11, 3, 32, 52, 12, 60, 42, 13, 0, 18, 1, 18, 4, 1, 1,
+ 50, 8, 60, 44, 4, 0, 1, 1, 60, 45, 60, 46, 0, 0, 0, 0, 1, 0, 0, 0,
+ 40, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 6, 99,108, 97,115,115, 0, 2, 0, 0,
+ 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101,
+ 0, 4, 0, 0, 0, 49, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,
+117, 97, 0, 0, 0, 0, 44, 3, 1, 60, 50, 13, 0, 18, 1, 48, 9, 13, 0, 18,
+ 1, 18, 2, 11, 3, 32, 52, 12, 60, 51, 13, 0, 18, 1, 18, 4, 1, 1, 50, 8,
+ 60, 53, 4, 0, 1, 1, 60, 54, 60, 55, 0, 0, 0, 0, 1, 0, 0, 0, 49, 0,
+ 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,115,101,108,
+102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 7,109,111,100,117,108,101, 0, 2, 0, 0, 0,
+ 5,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110, 99,110, 97,109,101,
+ 0, 4, 0, 0, 0, 59, 0, 0, 0, 13, 64,102,101, 97,116,117,114,101, 46,108,
+117, 97, 0, 0, 0, 0, 72, 5, 2, 60, 60, 13, 0, 18, 2, 52, 17, 60, 61, 13,
+ 0, 18, 2, 20, 3, 13, 1, 2, 1, 2, 23, 1, 50, 2, 60, 62, 60, 63, 13, 0,
+ 18, 4, 52, 16, 60, 64, 13, 1, 11, 5, 42, 13, 0, 18, 4, 42, 1, 2, 50, 16,
+ 60, 66, 13, 1, 11, 5, 42, 13, 0, 18, 6, 42, 1, 2, 60, 67, 60, 68, 0, 0,
+ 0, 0, 2, 0, 0, 0, 59, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 59,
+ 0, 0, 0, 2,110, 0, 0, 0, 0, 7, 2, 0, 0, 0, 2,110, 0, 2, 0, 0,
+ 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2,
+ 0, 0, 0, 10, 99,102,117,110, 99,110, 97,109,101, 0, 2, 0, 0, 0, 6,108,
+110, 97,109,101, 0, 2, 0, 0, 0, 2, 95, 0, 2, 0, 0, 0, 5,110, 97,109,
+101, 0,
+};
+
+/* declaration.lo */
+static char B3[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 17, 64,100,101, 99,108, 97,
+114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0,193, 17, 0, 60, 24, 22,
+ 8, 60, 25, 11, 1, 15, 2, 11, 3, 60, 26, 11, 4, 11, 5, 60, 27, 11, 4, 11,
+ 6, 60, 28, 11, 4, 11, 7, 60, 29, 11, 4, 11, 8, 60, 30, 11, 4, 11, 9, 60,
+ 31, 11, 4, 11, 10, 60, 32, 11, 4, 30, 7, 60, 33, 25, 0, 60, 34, 15, 11, 15,
+ 0, 15, 12, 2, 0, 2, 60, 37, 11, 14, 25, 13, 60, 45, 15, 0, 11, 15, 11, 16,
+ 26, 60, 84, 15, 0, 11, 17, 11, 18, 26, 60,117, 15, 0, 11, 19, 11, 20, 26, 60,
+130, 15, 0, 11, 21, 11, 22, 26, 60,136, 15, 0, 11, 23, 11, 24, 26, 60,149, 15,
+ 0, 11, 25, 11, 26, 26, 60,184, 15, 0, 11, 27, 11, 28, 26, 60,218, 15, 0, 11,
+ 29, 11, 30, 26, 60,250, 15, 0, 11, 31, 11, 32, 26, 59, 1, 1, 15, 0, 11, 33,
+ 11, 34, 26, 59, 1, 12, 15, 0, 11, 35, 11, 36, 26, 59, 1, 26, 11, 38, 25, 37,
+ 59, 1, 42, 11, 40, 25, 39, 0, 0, 0, 0, 0, 0, 0, 0, 41, 2, 0, 0, 0,
+ 17, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0,
+ 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,
+101, 97,116,117,114,101, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 4,112,116,114,
+ 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 4,100,105,109, 0,
+ 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0,
+ 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97,
+ 95,116, 97,103, 0, 2, 0, 0, 0, 15, 99,114,101, 97,116,101, 95,118, 97,114,
+110, 97,109,101, 0, 4, 0, 0, 0, 37, 0, 0, 0, 17, 64,100,101, 99,108, 97,
+114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 34, 2, 0, 60, 38, 15,
+ 0, 44, 52, 4, 7, 0, 25, 0, 60, 39, 15, 0, 7, 1, 37, 25, 0, 60, 40, 11,
+ 1, 15, 0, 42, 1, 0, 60, 41, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0,
+ 0, 11, 95,118, 97,114,110,117,109, 98,101,114, 0, 2, 0, 0, 0, 11,116,111,
+108,117, 97, 95,118, 97,114, 95, 0, 2, 0, 0, 0, 10, 99,104,101, 99,107,110,
+ 97,109,101, 0, 4, 0, 0, 0, 45, 0, 0, 0, 17, 64,100,101, 99,108, 97,114,
+ 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 1,140, 12, 1, 60, 47, 15, 0,
+ 13, 0, 18, 2, 7, 1, 7, 1, 2, 1, 3, 11, 3, 32, 48, 10, 15, 4, 13, 0,
+ 18, 5, 2, 1, 1, 44, 52, 70, 60, 48, 13, 0, 11, 2, 13, 0, 18, 5, 13, 0,
+ 18, 2, 42, 26, 60, 49, 15, 7, 13, 0, 18, 8, 11, 9, 2, 1, 2, 60, 50, 13,
+ 0, 11, 5, 13, 1, 13, 1, 18, 10, 16, 26, 60, 51, 13, 0, 11, 8, 15, 11, 13,
+ 1, 7, 1, 13, 1, 18, 10, 7, 1, 38, 2, 1, 3, 26, 5, 1, 50, 2, 60, 52,
+ 60, 54, 15, 7, 13, 0, 18, 2, 11, 13, 2, 1, 2, 60, 55, 13, 1, 18, 10, 7,
+ 2, 32, 52, 28, 60, 56, 13, 0, 11, 2, 13, 1, 7, 1, 16, 26, 60, 57, 13, 0,
+ 11, 14, 13, 1, 13, 1, 18, 10, 16, 26, 50, 2, 60, 58, 60, 60, 15, 18, 13, 0,
+ 18, 2, 11, 19, 2, 3, 2, 60, 61, 13, 2, 52, 34, 60, 62, 13, 0, 11, 2, 15,
+ 0, 13, 0, 18, 2, 7, 1, 13, 2, 7, 1, 38, 2, 1, 3, 26, 60, 63, 13, 0,
+ 11, 20, 13, 4, 26, 50, 2, 60, 64, 60, 67, 13, 0, 18, 5, 11, 21, 31, 48, 7,
+ 13, 0, 18, 5, 11, 22, 31, 48, 7, 13, 0, 18, 2, 11, 21, 32, 52, 14, 60, 68,
+ 13, 0, 11, 2, 15, 23, 2, 1, 0, 26, 50,135, 60, 69, 13, 0, 18, 24, 11, 25,
+ 32, 52,122, 60, 70, 13, 0, 18, 5, 11, 21, 32, 48, 7, 13, 0, 18, 2, 11, 21,
+ 31, 52, 30, 60, 71, 13, 0, 11, 5, 13, 0, 18, 5, 13, 0, 18, 2, 42, 26, 60,
+ 72, 13, 0, 11, 2, 15, 23, 2, 1, 0, 26, 50, 70, 60, 73, 15, 4, 13, 0, 18,
+ 2, 2, 1, 1, 52, 55, 60, 74, 13, 0, 18, 5, 11, 21, 32, 52, 11, 13, 0, 11,
+ 5, 13, 0, 18, 2, 26, 50, 19, 60, 75, 13, 0, 11, 5, 13, 0, 18, 5, 11, 26,
+ 42, 13, 0, 18, 2, 42, 26, 60, 76, 13, 0, 11, 2, 15, 23, 2, 1, 0, 26, 50,
+ 2, 60, 77, 50, 2, 60, 78, 60, 80, 0, 0, 0, 0, 7, 0, 0, 0, 45, 0, 0,
+ 0, 5,115,101,108,102, 0, 0, 0, 0, 49, 0, 0, 0, 2,109, 0, 0, 0, 0,
+ 51, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 2,116, 0, 0, 0, 0, 60, 0,
+ 0, 0, 2, 98, 0, 0, 0, 0, 60, 0, 0, 0, 2,101, 0, 0, 0, 0, 60, 0,
+ 0, 0, 2,100, 0, 0, 0, 0, 27, 2, 0, 0, 0, 7,115,116,114,115,117, 98,
+ 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101,
+ 0, 2, 0, 0, 0, 2, 91, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101, 0,
+ 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 2,109, 0, 2, 0, 0,
+ 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0,
+ 0, 6, 37,115, 37,115, 42, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 7,
+ 99,111,110, 99, 97,116, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 2, 61,
+ 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 2, 98, 0, 2, 0, 0,
+ 0, 2,101, 0, 2, 0, 0, 0, 2,100, 0, 2, 0, 0, 0, 8,115,116,114,102,
+105,110,100, 0, 2, 0, 0, 0, 9, 37, 91, 40, 46, 45, 41, 37, 93, 0, 2, 0,
+ 0, 0, 4,100,105,109, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,118,111,
+105,100, 0, 2, 0, 0, 0, 15, 99,114,101, 97,116,101, 95,118, 97,114,110, 97,
+109,101, 0, 2, 0, 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 4,118, 97,
+114, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 10, 99,104,101, 99,107,116,
+121,112,101, 0, 4, 0, 0, 0, 84, 0, 0, 0, 17, 64,100,101, 99,108, 97,114,
+ 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0,152, 4, 1, 60, 87, 15, 0,
+ 13, 0, 18, 2, 2, 1, 1, 48, 7, 13, 0, 18, 3, 11, 4, 31, 52, 22, 60, 88,
+ 13, 0, 11, 5, 13, 0, 18, 3, 26, 60, 89, 13, 0, 11, 3, 4, 0, 26, 50, 2,
+ 60, 90, 60, 93, 13, 0, 18, 6, 11, 4, 31, 48, 7, 13, 0, 18, 5, 11, 4, 31,
+ 52, 11, 60, 94, 15, 7, 11, 8, 2, 0, 1, 50, 2, 60, 95, 60, 98, 13, 0, 18,
+ 2, 11, 4, 31, 52, 13, 60, 99, 15, 9, 13, 0, 18, 2, 2, 0, 1, 50, 2, 60,
+100, 60,103, 13, 0, 18, 2, 11, 10, 32, 52, 9, 13, 0, 11, 2, 11, 11, 26, 50,
+ 22, 60,104, 13, 0, 18, 2, 11, 12, 32, 52, 9, 13, 0, 11, 2, 11, 13, 26, 50,
+ 2, 60,105, 60,114, 0, 0, 0, 0, 1, 0, 0, 0, 84, 0, 0, 0, 5,115,101,
+108,102, 0, 0, 0, 0, 14, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0,
+ 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4,
+114,101,116, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2, 0, 0, 0, 6,101,114,
+114,111,114, 0, 2, 0, 0, 0, 53, 35,105,110,118, 97,108,105,100, 32,112, 97,
+114, 97,109,101,116,101,114, 58, 32, 99, 97,110,110,111,116, 32,114,101,116,117,
+114,110, 32, 97,110, 32, 97,114,114, 97,121, 32,111,102, 32,118, 97,108,117,101,
+115, 0, 2, 0, 0, 0, 8,114,101,103,116,121,112,101, 0, 2, 0, 0, 0, 10,
+ 95,117,115,101,114,100, 97,116, 97, 0, 2, 0, 0, 0, 6,118,111,105,100, 42,
+ 0, 2, 0, 0, 0, 9, 95, 99,115,116,114,105,110,103, 0, 2, 0, 0, 0, 6,
+ 99,104, 97,114, 42, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0,
+ 0,117, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,
+117, 97, 0, 0, 0, 0,172, 6, 3, 60,118, 15, 2, 13, 1, 11, 3, 42, 2, 0,
+ 1, 60,119, 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0,
+ 1, 60,120, 15, 2, 13, 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0,
+ 1, 60,121, 15, 2, 13, 1, 11, 10, 42, 13, 0, 18, 11, 42, 11, 7, 42, 2, 0,
+ 1, 60,122, 15, 2, 13, 1, 11, 12, 42, 13, 0, 18, 13, 42, 11, 7, 42, 2, 0,
+ 1, 60,123, 15, 2, 13, 1, 11, 14, 42, 13, 0, 18, 15, 42, 11, 7, 42, 2, 0,
+ 1, 60,124, 15, 2, 13, 1, 11, 16, 42, 13, 0, 18, 17, 42, 11, 7, 42, 2, 0,
+ 1, 60,125, 15, 2, 13, 1, 11, 18, 42, 13, 0, 18, 19, 42, 11, 7, 42, 2, 0,
+ 1, 60,126, 15, 2, 13, 1, 11, 20, 42, 13, 2, 42, 2, 0, 1, 60,127, 0, 0,
+ 0, 0, 3, 0, 0, 0,117, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,117,
+ 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0,117, 0, 0, 0, 6, 99,108,
+111,115,101, 0, 0, 0, 0, 21, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2,
+ 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116,
+ 0, 2, 0, 0, 0, 13, 68,101, 99,108, 97,114, 97,116,105,111,110,123, 0, 2,
+ 0, 0, 0, 10, 32,109,111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 39, 44,
+ 0, 2, 0, 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39,
+ 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101,
+ 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 10,
+ 32,100,105,109, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2,
+ 0, 0, 0, 10, 32,100,101,102, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,100,
+101,102, 0, 2, 0, 0, 0, 10, 32,114,101,116, 32, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 8,100,
+101, 99,108,116, 97,103, 0, 4, 0, 0, 0,130, 0, 0, 0, 17, 64,100,101, 99,
+108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 56, 10, 1, 60,
+131, 13, 0, 11, 1, 13, 0, 11, 2, 15, 3, 13, 0, 18, 4, 15, 5, 13, 0, 18,
+ 6, 11, 7, 2, 1, 2, 2, 2, 2, 27, 1, 27, 2, 5, 4, 60,132, 15, 8, 13,
+ 0, 18, 1, 13, 0, 18, 2, 2, 0, 2, 60,133, 0, 0, 0, 0, 1, 0, 0, 0,
+130, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 9, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0, 4,
+116, 97,103, 0, 2, 0, 0, 0, 7,116, 97,103,118, 97,114, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2,
+ 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2,
+ 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0, 2, 0, 0, 0, 13,111,117,116,
+ 99,104,101, 99,107,116,121,112,101, 0, 4, 0, 0, 0,136, 0, 0, 0, 17, 64,
+100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 83,
+ 6, 2, 60,137, 4, 1, 60,138, 13, 0, 18, 4, 11, 5, 31, 52, 14, 60,139, 11,
+ 6, 23, 2, 60,140, 7, 0, 23, 3, 50, 25, 60,142, 13, 0, 18, 1, 23, 2, 60,
+143, 13, 0, 18, 2, 11, 5, 31, 46, 2, 7, 0, 23, 3, 60,144, 60,145, 11, 7,
+ 13, 1, 42, 11, 8, 42, 13, 2, 42, 11, 8, 42, 13, 3, 42, 11, 9, 42, 1, 4,
+ 60,146, 0, 0, 0, 0, 4, 0, 0, 0,136, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0,136, 0, 0, 0, 5,110, 97,114,103, 0, 0, 0, 0,137, 0, 0, 0,
+ 4,116, 97,103, 0, 0, 0, 0,137, 0, 0, 0, 4,100,101,102, 0, 0, 0, 0,
+ 10, 2, 0, 0, 0, 5,110, 97,114,103, 0, 2, 0, 0, 0, 4,116, 97,103, 0,
+ 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2,
+ 0, 0, 0, 4,100,105,109, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 16,116,
+111,108,117, 97, 95,116, 97,103, 95,116, 97, 98,108,101, 0, 2, 0, 0, 0, 14,
+116,111,108,117, 97, 95,105,115,116,121,112,101, 40, 0, 2, 0, 0, 0, 2, 44,
+ 0, 2, 0, 0, 0, 2, 41, 0, 2, 0, 0, 0, 8,100,101, 99,108, 97,114,101,
+ 0, 4, 0, 0, 0,149, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,
+111,110, 46,108,117, 97, 0, 0, 0, 1, 84, 15, 2, 60,150, 11, 2, 60,151, 13,
+ 0, 18, 1, 11, 2, 31, 52, 4, 11, 4, 23, 2, 60,152, 15, 5, 11, 6, 13, 0,
+ 18, 7, 13, 0, 18, 8, 13, 2, 2, 0, 4, 60,153, 13, 0, 18, 9, 11, 2, 31,
+ 48, 12, 15, 10, 13, 0, 18, 9, 2, 1, 1, 4, 0, 32, 52, 11, 60,154, 15, 5,
+ 11, 4, 2, 0, 1, 50, 2, 60,155, 60,156, 15, 5, 13, 0, 18, 11, 2, 0, 1,
+ 60,157, 13, 0, 18, 9, 11, 2, 31, 52, 76, 60,158, 15, 10, 13, 0, 18, 9, 2,
+ 1, 1, 4, 0, 31, 52, 17, 60,159, 15, 5, 11, 12, 13, 0, 18, 9, 11, 13, 2,
+ 0, 3, 50, 41, 60,161, 15, 5, 11, 14, 13, 0, 18, 7, 13, 0, 18, 8, 13, 2,
+ 11, 15, 60,162, 11, 16, 13, 0, 18, 9, 11, 17, 13, 0, 18, 8, 13, 2, 11, 18,
+ 2, 0, 11, 60,163, 50,161, 60,165, 15, 20, 13, 0, 18, 8, 2, 1, 1, 60,166,
+ 15, 5, 11, 21, 2, 0, 1, 60,167, 13, 3, 44, 48, 5, 13, 2, 11, 2, 32, 52,
+ 7, 15, 5, 11, 4, 2, 0, 1, 60,168, 15, 5, 11, 22, 13, 0, 18, 7, 13, 0,
+ 18, 8, 2, 0, 3, 60,169, 13, 3, 44, 52, 11, 60,170, 15, 5, 11, 4, 2, 0,
+ 1, 50, 2, 60,171, 60,172, 15, 5, 11, 23, 2, 0, 1, 60,173, 7, 0, 60,174,
+ 13, 0, 18, 24, 11, 2, 31, 52, 6, 13, 0, 18, 24, 23, 4, 60,175, 13, 3, 52,
+ 24, 60,176, 15, 5, 11, 25, 13, 3, 42, 11, 26, 13, 1, 11, 27, 13, 4, 11, 18,
+ 2, 0, 6, 50, 19, 60,178, 15, 5, 11, 28, 13, 1, 11, 27, 13, 4, 11, 18, 2,
+ 0, 5, 60,179, 5, 2, 60,180, 60,181, 0, 0, 0, 0, 7, 0, 0, 0,149, 0,
+ 0, 0, 5,115,101,108,102, 0, 0, 0, 0,149, 0, 0, 0, 5,110, 97,114,103,
+ 0, 0, 0, 0,150, 0, 0, 0, 4,112,116,114, 0, 0, 0, 0,165, 0, 0, 0,
+ 2,116, 0, 0, 0, 0,173, 0, 0, 0, 4,100,101,102, 0, 0, 0, 0,179, 0,
+ 0, 0, 0, 0, 0, 0,179, 0, 0, 0, 0, 0, 0, 0, 29, 2, 0, 0, 0, 5,
+110, 97,114,103, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 1, 0,
+ 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0,
+ 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0,
+ 4,109,111,100, 0, 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 4,
+100,105,109, 0, 2, 0, 0, 0, 9,116,111,110,117,109, 98,101,114, 0, 2, 0,
+ 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 2, 91, 0, 2, 0, 0, 0, 3,
+ 93, 59, 0, 2, 0, 0, 0, 5, 32, 61, 32, 40, 0, 2, 0, 0, 0, 3, 42, 41,
+ 0, 2, 0, 0, 0, 8,109, 97,108,108,111, 99, 40, 0, 2, 0, 0, 0, 9, 42,
+115,105,122,101,111,102, 40, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0,
+ 0, 4, 32, 61, 32, 0, 2, 0, 0, 0, 3, 40, 40, 0, 2, 0, 0, 0, 3, 41,
+ 32, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,103,101,116, 0, 2, 0, 0, 0, 2, 40, 0, 2, 0, 0, 0, 2, 44, 0,
+ 2, 0, 0, 0, 19,116,111,108,117, 97, 95,103,101,116,117,115,101,114,116,121,
+112,101, 40, 0, 2, 0, 0, 0, 9,103,101,116, 97,114,114, 97,121, 0, 4, 0,
+ 0, 0,184, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 1, 78, 13, 2, 60,185, 13, 0, 18, 2, 11, 3, 31, 51,
+ 1, 59, 60,186, 15, 4, 11, 5, 2, 0, 1, 60,187, 13, 0, 18, 6, 11, 3, 31,
+ 46, 2, 7, 0, 60,188, 15, 4, 11, 7, 13, 1, 11, 8, 13, 0, 18, 9, 11, 8,
+ 13, 0, 18, 2, 11, 8, 13, 2, 11, 10, 2, 0, 9, 60,189, 15, 4, 11, 11, 2,
+ 0, 1, 60,190, 15, 4, 11, 12, 2, 0, 1, 60,191, 15, 4, 11, 13, 2, 0, 1,
+ 60,192, 15, 4, 11, 14, 2, 0, 1, 60,193, 15, 4, 11, 15, 13, 1, 11, 16, 2,
+ 0, 3, 60,194, 15, 4, 11, 17, 13, 0, 18, 2, 42, 11, 18, 42, 2, 0, 1, 60,
+195, 15, 20, 13, 0, 18, 21, 2, 1, 1, 60,196, 11, 3, 60,197, 13, 0, 18, 22,
+ 11, 3, 31, 52, 4, 11, 23, 23, 4, 60,198, 15, 4, 11, 24, 13, 0, 18, 25, 11,
+ 26, 42, 2, 0, 2, 60,199, 13, 3, 44, 48, 5, 13, 4, 11, 3, 32, 52, 7, 15,
+ 4, 11, 23, 2, 0, 1, 60,200, 15, 4, 11, 27, 13, 0, 18, 28, 13, 0, 18, 21,
+ 2, 0, 3, 60,201, 13, 3, 44, 52, 11, 60,202, 15, 4, 11, 23, 2, 0, 1, 50,
+ 2, 60,203, 60,204, 15, 4, 11, 29, 2, 0, 1, 60,205, 7, 0, 60,206, 13, 0,
+ 18, 6, 11, 3, 31, 52, 6, 13, 0, 18, 6, 23, 5, 60,207, 13, 3, 52, 21, 60,
+208, 15, 4, 11, 30, 13, 3, 42, 11, 31, 42, 13, 5, 11, 32, 2, 0, 3, 50, 15,
+ 60,210, 15, 4, 11, 33, 13, 5, 11, 32, 2, 0, 3, 60,211, 60,212, 15, 4, 11,
+ 34, 2, 0, 1, 60,213, 15, 4, 11, 35, 2, 0, 1, 5, 4, 50, 2, 60,214, 60,
+215, 0, 0, 0, 0, 10, 0, 0, 0,184, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0,184, 0, 0, 0, 5,110, 97,114,103, 0, 0, 0, 0,187, 0, 0, 0, 4,
+100,101,102, 0, 0, 0, 0,195, 0, 0, 0, 2,116, 0, 0, 0, 0,196, 0, 0,
+ 0, 4,112,116,114, 0, 0, 0, 0,205, 0, 0, 0, 4,100,101,102, 0, 0, 0,
+ 0,213, 0, 0, 0, 0, 0, 0, 0,213, 0, 0, 0, 0, 0, 0, 0,213, 0, 0,
+ 0, 0, 0, 0, 0,213, 0, 0, 0, 0, 0, 0, 0, 36, 2, 0, 0, 0, 5,110,
+ 97,114,103, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 4,100,
+105,109, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116,
+ 0, 2, 0, 0, 0, 4, 32, 32,123, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2,
+ 0, 0, 0, 27, 32, 32, 32,105,102, 32, 40, 33,116,111,108,117, 97, 95, 97,114,
+114, 97,121,105,115,116,121,112,101, 40, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0,
+ 0, 0, 4,116, 97,103, 0, 2, 0, 0, 0, 3, 41, 41, 0, 2, 0, 0, 0, 16,
+ 32, 32, 32, 32,103,111,116,111, 32,101,114,114,111,114, 59, 0, 2, 0, 0, 0,
+ 9, 32, 32, 32,101,108,115,101, 10, 0, 2, 0, 0, 0, 5, 32, 32, 32,123, 0,
+ 2, 0, 0, 0, 11, 32, 32, 32, 32,105,110,116, 32,105, 59, 0, 2, 0, 0, 0,
+ 34, 32, 32, 32, 32,108,117, 97, 95, 79, 98,106,101, 99,116, 32,108,111, 32, 61,
+ 32,108,117, 97, 95,103,101,116,112, 97,114, 97,109, 40, 0, 2, 0, 0, 0, 3,
+ 41, 59, 0, 2, 0, 0, 0, 16, 32, 32, 32, 32,102,111,114, 40,105, 61, 48, 59,
+ 32,105, 60, 0, 2, 0, 0, 0, 6, 59,105, 43, 43, 41, 0, 2, 0, 0, 0, 2,
+116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 2, 42,
+ 0, 2, 0, 0, 0, 4, 32, 32, 32, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0,
+ 2, 0, 0, 0, 7, 91,105, 93, 32, 61, 32, 0, 2, 0, 0, 0, 3, 40, 40, 0,
+ 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 41, 32, 0, 2, 0, 0,
+ 0, 15,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100, 0, 2, 0, 0,
+ 0, 9, 40,108,111, 44,105, 43, 49, 44, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0,
+ 2, 0, 0, 0, 31,116,111,108,117, 97, 95,103,101,116,102,105,101,108,100,117,
+115,101,114,116,121,112,101, 40,108,111, 44,105, 43, 49, 44, 0, 2, 0, 0, 0,
+ 5, 32, 32, 32,125, 0, 2, 0, 0, 0, 4, 32, 32,125, 0, 2, 0, 0, 0, 9,
+115,101,116, 97,114,114, 97,121, 0, 4, 0, 0, 0,218, 0, 0, 0, 17, 64,100,
+101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 1, 42, 9,
+ 2, 60,219, 13, 0, 18, 2, 11, 3, 31, 51, 1, 23, 60,220, 15, 4, 11, 5, 2,
+ 0, 1, 60,221, 15, 4, 11, 6, 2, 0, 1, 60,222, 15, 4, 11, 7, 13, 1, 11,
+ 8, 2, 0, 3, 60,223, 15, 4, 11, 9, 13, 0, 18, 2, 42, 11, 10, 42, 2, 0,
+ 1, 60,224, 15, 12, 13, 0, 18, 13, 2, 1, 1, 60,225, 13, 2, 11, 14, 32, 52,
+ 23, 60,226, 15, 4, 11, 15, 13, 2, 42, 11, 16, 42, 13, 0, 18, 17, 11, 18, 2,
+ 0, 3, 50,175, 60,227, 13, 2, 52, 23, 60,228, 15, 4, 11, 15, 13, 2, 42, 11,
+ 19, 42, 13, 0, 18, 17, 11, 18, 2, 0, 3, 50,146, 60,230, 13, 0, 18, 20, 11,
+ 3, 32, 52,110, 60,231, 15, 4, 11, 21, 2, 0, 1, 60,232, 15, 4, 11, 22, 2,
+ 0, 1, 60,233, 15, 4, 11, 23, 13, 0, 18, 13, 11, 24, 13, 0, 18, 17, 11, 18,
+ 2, 0, 5, 60,234, 15, 4, 11, 25, 2, 0, 1, 60,235, 15, 4, 11, 26, 13, 0,
+ 18, 17, 11, 27, 13, 0, 18, 13, 11, 28, 2, 0, 5, 60,236, 15, 4, 11, 29, 2,
+ 0, 1, 60,237, 15, 4, 11, 30, 13, 0, 18, 31, 11, 32, 13, 0, 18, 31, 11, 8,
+ 2, 0, 5, 60,238, 15, 4, 11, 33, 2, 0, 1, 50, 23, 60,242, 15, 4, 11, 34,
+ 13, 0, 18, 17, 11, 35, 13, 0, 18, 31, 11, 8, 2, 0, 5, 60,243, 60,244, 60,
+245, 15, 4, 11, 36, 2, 0, 1, 5, 1, 50, 2, 60,246, 60,247, 0, 0, 0, 0,
+ 4, 0, 0, 0,218, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,218, 0, 0,
+ 0, 5,110, 97,114,103, 0, 0, 0, 0,224, 0, 0, 0, 2,116, 0, 0, 0, 0,
+245, 0, 0, 0, 0, 0, 0, 0, 37, 2, 0, 0, 0, 5,110, 97,114,103, 0, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0,
+ 4, 32, 32,123, 0, 2, 0, 0, 0, 10, 32, 32, 32,105,110,116, 32,105, 59, 0,
+ 2, 0, 0, 0, 33, 32, 32, 32,108,117, 97, 95, 79, 98,106,101, 99,116, 32,108,
+111, 32, 61, 32,108,117, 97, 95,103,101,116,112, 97,114, 97,109, 40, 0, 2, 0,
+ 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 15, 32, 32, 32,102,111,114, 40,105, 61,
+ 48, 59, 32,105, 60, 0, 2, 0, 0, 0, 6, 59,105, 43, 43, 41, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0,
+ 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2,
+ 0, 0, 0, 19, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104,102,105,101,
+108,100, 0, 2, 0, 0, 0, 17, 40,108,111, 44,105, 43, 49, 44, 40,100,111,117,
+ 98,108,101, 41, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 6,
+ 91,105, 93, 41, 59, 0, 2, 0, 0, 0, 9, 40,108,111, 44,105, 43, 49, 44, 0,
+ 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 5, 32, 32, 32,123, 0, 2,
+ 0, 0, 0, 20, 35,105,102,100,101,102, 32, 95, 95, 99,112,108,117,115,112,108,
+117,115, 10, 0, 2, 0, 0, 0, 29, 32, 32, 32, 32,118,111,105,100, 42, 32,116,
+111,108,117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,110,101,119, 0, 2, 0,
+ 0, 0, 2, 40, 0, 2, 0, 0, 0, 7, 35,101,108,115,101, 10, 0, 2, 0, 0,
+ 0, 45, 32, 32, 32, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95, 99,
+108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,111,112,121, 40, 40,118,
+111,105,100, 42, 41, 38, 0, 2, 0, 0, 0, 12, 91,105, 93, 44,115,105,122,101,
+111,102, 40, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0, 2, 0, 0, 0, 8, 35,101,
+110,100,105,102, 10, 0, 2, 0, 0, 0, 63, 32, 32, 32, 32,116,111,108,117, 97,
+ 95,112,117,115,104,102,105,101,108,100,117,115,101,114,116,121,112,101, 40,108,
+111, 44,105, 43, 49, 44,116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,
+116,111,108,117, 97, 73, 95, 99,108,111,110,101, 44, 0, 2, 0, 0, 0, 4,116,
+ 97,103, 0, 2, 0, 0, 0, 3, 41, 44, 0, 2, 0, 0, 0, 5, 32, 32, 32,125,
+ 0, 2, 0, 0, 0, 42, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104,102,
+105,101,108,100,117,115,101,114,116,121,112,101, 40,108,111, 44,105, 43, 49, 44,
+ 40,118,111,105,100, 42, 41, 0, 2, 0, 0, 0, 5, 91,105, 93, 44, 0, 2, 0,
+ 0, 0, 4, 32, 32,125, 0, 2, 0, 0, 0, 10,102,114,101,101, 97,114,114, 97,
+121, 0, 4, 0, 0, 0,250, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,
+105,111,110, 46,108,117, 97, 0, 0, 0, 0, 49, 5, 1, 60,251, 13, 0, 18, 1,
+ 11, 2, 31, 48, 12, 15, 3, 13, 0, 18, 1, 2, 1, 1, 4, 0, 32, 52, 17, 60,
+252, 15, 4, 11, 5, 13, 0, 18, 6, 11, 7, 2, 0, 3, 50, 2, 60,253, 60,254,
+ 0, 0, 0, 0, 1, 0, 0, 0,250, 0, 0, 0, 5,115,101,108,102, 0, 0, 0,
+ 0, 8, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 4,100,105,109,
+ 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 9,116,111,110,117,109, 98,101,114,
+ 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 8, 32, 32,
+102,114,101,101, 40, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0,
+ 3, 41, 59, 0, 2, 0, 0, 0, 8,112, 97,115,115,112, 97,114, 0, 4, 0, 0,
+ 1, 1, 0, 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,
+117, 97, 0, 0, 0, 0, 79, 4, 1, 59, 1, 2, 13, 0, 18, 1, 11, 2, 32, 52,
+ 17, 59, 1, 3, 15, 3, 11, 4, 13, 0, 18, 5, 42, 2, 0, 1, 50, 44, 59, 1,
+ 4, 13, 0, 18, 6, 11, 4, 32, 52, 17, 59, 1, 5, 15, 3, 11, 2, 13, 0, 18,
+ 5, 42, 2, 0, 1, 50, 15, 59, 1, 7, 15, 3, 13, 0, 18, 5, 2, 0, 1, 59,
+ 1, 8, 59, 1, 9, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 5,115,101,
+108,102, 0, 0, 0, 0, 7, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0,
+ 0, 4,112,116,114, 0, 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 7,111,117,
+116,112,117,116, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0, 0, 5,110, 97,109,
+101, 0, 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 9,114,101,116,118,
+ 97,108,117,101, 0, 4, 0, 0, 1, 12, 0, 0, 0, 17, 64,100,101, 99,108, 97,
+114, 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0,133, 6, 1, 59, 1, 13,
+ 13, 0, 18, 1, 11, 2, 31, 52,112, 59, 1, 14, 15, 4, 13, 0, 18, 5, 2, 1,
+ 1, 59, 1, 15, 13, 1, 11, 6, 32, 52, 26, 59, 1, 16, 15, 7, 11, 8, 13, 1,
+ 42, 11, 9, 42, 13, 0, 18, 10, 42, 11, 11, 42, 2, 0, 1, 50, 60, 59, 1, 17,
+ 13, 1, 52, 26, 59, 1, 18, 15, 7, 11, 8, 13, 1, 42, 11, 12, 42, 13, 0, 18,
+ 10, 42, 11, 11, 42, 2, 0, 1, 50, 27, 59, 1, 20, 15, 7, 11, 13, 13, 0, 18,
+ 10, 42, 11, 14, 42, 13, 0, 18, 15, 11, 11, 2, 0, 3, 59, 1, 21, 5, 1, 50,
+ 3, 59, 1, 22, 59, 1, 23, 0, 0, 0, 0, 3, 0, 0, 1, 12, 0, 0, 0, 5,
+115,101,108,102, 0, 0, 0, 1, 14, 0, 0, 0, 2,116, 0, 0, 0, 1, 21, 0,
+ 0, 0, 0, 0, 0, 0, 16, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0,
+ 0, 4,114,101,116, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2,116, 0, 2,
+ 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,116,121,112,
+101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0, 7,111,
+117,116,112,117,116, 0, 2, 0, 0, 0, 14, 32, 32, 32,116,111,108,117, 97, 95,
+112,117,115,104, 0, 2, 0, 0, 0, 10, 40, 40,100,111,117, 98,108,101, 41, 0,
+ 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0,
+ 0, 0, 2, 40, 0, 2, 0, 0, 0, 30, 32, 32, 32,116,111,108,117, 97, 95,112,
+117,115,104,117,115,101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41, 0,
+ 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 4,116, 97,103, 0, 2, 0, 0, 0,
+ 13, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 4, 0, 0, 1, 26, 0,
+ 0, 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0,
+ 0, 0, 0,136, 8, 1, 59, 1, 27, 13, 0, 18, 1, 48, 7, 13, 0, 18, 1, 11,
+ 2, 31, 52, 60, 59, 1, 28, 15, 4, 13, 0, 18, 1, 11, 5, 2, 1, 2, 59, 1,
+ 29, 13, 0, 11, 1, 13, 1, 7, 1, 16, 26, 59, 1, 30, 13, 0, 11, 6, 15, 7,
+ 13, 1, 7, 2, 16, 46, 5, 13, 1, 7, 1, 16, 11, 8, 11, 2, 2, 1, 3, 26,
+ 5, 1, 50, 3, 59, 1, 31, 59, 1, 32, 13, 0, 11, 9, 15, 10, 26, 59, 1, 33,
+ 15, 11, 13, 0, 15, 12, 2, 0, 2, 59, 1, 34, 13, 0, 20, 13, 2, 0, 1, 59,
+ 1, 35, 13, 0, 20, 14, 2, 0, 1, 59, 1, 36, 13, 0, 1, 1, 59, 1, 37, 0,
+ 0, 0, 0, 3, 0, 0, 1, 26, 0, 0, 0, 2,116, 0, 0, 0, 1, 28, 0, 0,
+ 0, 2,110, 0, 0, 0, 1, 30, 0, 0, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0,
+ 2,116, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 1, 0, 2,
+ 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0,
+ 0, 2, 64, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 5,
+103,115,117, 98, 0, 2, 0, 0, 0, 7, 37, 91, 46, 45, 37, 93, 0, 2, 0, 0,
+ 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 17, 99,108, 97,115,115, 68,101,
+ 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,
+103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0,
+ 0, 10, 99,104,101, 99,107,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,104,101,
+ 99,107,116,121,112,101, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,114, 97,116,
+105,111,110, 0, 4, 0, 0, 1, 42, 0, 0, 0, 17, 64,100,101, 99,108, 97,114,
+ 97,116,105,111,110, 46,108,117, 97, 0, 0, 0, 3,164, 20, 2, 59, 1, 44, 15,
+ 2, 13, 0, 11, 3, 11, 4, 2, 1, 3, 23, 0, 59, 1, 46, 13, 1, 11, 5, 32,
+ 52, 44, 59, 1, 48, 13, 0, 11, 6, 32, 46, 5, 13, 0, 11, 7, 32, 52, 22, 59,
+ 1, 49, 15, 8, 22, 2, 11, 9, 11, 7, 11, 1, 13, 1, 30, 1, 3, 2, 1, 50,
+ 3, 59, 1, 50, 50, 3, 59, 1, 51, 59, 1, 54, 15, 11, 13, 0, 11, 12, 2, 1,
+ 2, 59, 1, 55, 13, 2, 18, 13, 7, 2, 32, 52,126, 59, 1, 56, 13, 1, 11, 14,
+ 32, 52, 15, 59, 1, 57, 15, 15, 11, 16, 13, 0, 42, 2, 0, 1, 50, 3, 59, 1,
+ 58, 59, 1, 59, 15, 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1, 60, 15,
+ 8, 22, 6, 59, 1, 61, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1, 62, 11, 21,
+ 11, 22, 59, 1, 63, 11, 23, 11, 9, 59, 1, 64, 13, 3, 13, 3, 18, 13, 16, 11,
+ 24, 59, 1, 65, 15, 25, 13, 3, 7, 1, 13, 3, 18, 13, 7, 1, 38, 2, 1, 3,
+ 11, 1, 59, 1, 66, 13, 1, 30, 5, 59, 1, 67, 3, 4, 1, 5, 1, 50, 3, 59,
+ 1, 68, 59, 1, 71, 15, 11, 13, 0, 11, 26, 2, 1, 2, 23, 2, 59, 1, 72, 13,
+ 2, 18, 13, 7, 2, 32, 52,126, 59, 1, 73, 13, 1, 11, 14, 32, 52, 15, 59, 1,
+ 74, 15, 15, 11, 16, 13, 0, 42, 2, 0, 1, 50, 3, 59, 1, 75, 59, 1, 76, 15,
+ 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1, 77, 15, 8, 22, 6, 59, 1,
+ 78, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1, 79, 11, 21, 11, 22, 59, 1, 80,
+ 11, 21, 11, 9, 59, 1, 81, 13, 3, 13, 3, 18, 13, 16, 11, 24, 59, 1, 82, 15,
+ 25, 13, 3, 7, 1, 13, 3, 18, 13, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1, 83,
+ 13, 1, 30, 5, 59, 1, 84, 3, 4, 1, 5, 1, 50, 3, 59, 1, 85, 59, 1, 88,
+ 15, 11, 13, 0, 11, 23, 2, 1, 2, 23, 2, 59, 1, 89, 13, 2, 18, 13, 7, 2,
+ 32, 52, 91, 59, 1, 90, 15, 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1,
+ 91, 15, 8, 22, 5, 59, 1, 92, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1, 93,
+ 11, 23, 11, 9, 59, 1, 94, 13, 3, 13, 3, 18, 13, 16, 11, 24, 59, 1, 95, 15,
+ 25, 13, 3, 7, 1, 13, 3, 18, 13, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1, 96,
+ 13, 1, 30, 4, 59, 1, 97, 3, 4, 1, 5, 1, 50, 3, 59, 1, 98, 59, 1,101,
+ 15, 2, 13, 0, 11, 28, 11, 29, 2, 1, 3, 59, 1,102, 15, 11, 13, 3, 11, 30,
+ 2, 1, 2, 23, 2, 59, 1,103, 13, 2, 18, 31, 7, 2, 32, 52,113, 59, 1,104,
+ 13, 2, 7, 2, 15, 32, 13, 2, 7, 2, 16, 11, 33, 11, 30, 2, 1, 3, 26, 59,
+ 1,105, 15, 11, 13, 2, 7, 1, 16, 11, 18, 2, 1, 2, 59, 1,106, 15, 8, 22,
+ 5, 59, 1,107, 11, 19, 13, 2, 7, 2, 16, 11, 20, 59, 1,108, 11, 21, 11, 9,
+ 59, 1,109, 13, 4, 13, 4, 18, 31, 16, 11, 24, 59, 1,110, 15, 25, 13, 4, 7,
+ 1, 13, 4, 18, 31, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1,111, 13, 1, 30, 4,
+ 59, 1,112, 3, 5, 1, 5, 1, 50, 3, 59, 1,113, 59, 1,115, 13, 1, 11, 5,
+ 32, 52,129, 59, 1,117, 15, 11, 13, 0, 11, 18, 2, 1, 2, 23, 2, 59, 1,118,
+ 4, 0, 59, 1,119, 15, 35, 13, 2, 13, 2, 18, 31, 16, 2, 1, 1, 52, 6, 11,
+ 6, 23, 4, 50, 21, 13, 2, 13, 2, 18, 31, 16, 23, 4, 13, 2, 11, 31, 13, 2,
+ 18, 31, 7, 1, 38, 26, 59, 1,120, 15, 8, 22, 4, 59, 1,121, 11, 19, 13, 4,
+ 11, 9, 59, 1,122, 13, 2, 13, 2, 18, 31, 16, 11, 24, 59, 1,123, 15, 25, 13,
+ 2, 7, 1, 13, 2, 18, 31, 7, 1, 38, 2, 1, 3, 11, 1, 59, 1,124, 13, 1,
+ 30, 3, 59, 1,125, 3, 5, 1, 5, 1, 50,130, 59, 1,130, 15, 11, 13, 0, 11,
+ 18, 2, 1, 2, 23, 2, 59, 1,131, 13, 2, 13, 2, 18, 31, 16, 59, 1,132, 4,
+ 1, 59, 1,133, 13, 2, 18, 31, 7, 1, 35, 52, 38, 59, 1,134, 13, 2, 13, 2,
+ 18, 31, 7, 1, 38, 16, 23, 5, 59, 1,135, 15, 25, 13, 2, 7, 1, 13, 2, 18,
+ 31, 7, 2, 38, 2, 1, 3, 23, 6, 50, 3, 59, 1,136, 59, 1,137, 15, 8, 22,
+ 4, 59, 1,138, 11, 19, 13, 4, 11, 9, 59, 1,139, 13, 5, 11, 24, 59, 1,140,
+ 13, 6, 11, 1, 59, 1,141, 13, 1, 30, 3, 59, 1,142, 3, 7, 1, 5, 3, 59,
+ 1,143, 59, 1,145, 0, 0, 0, 0, 20, 0, 0, 1, 42, 0, 0, 0, 2,115, 0,
+ 0, 0, 1, 42, 0, 0, 0, 5,107,105,110,100, 0, 0, 0, 1, 54, 0, 0, 0,
+ 2,116, 0, 0, 0, 1, 59, 0, 0, 0, 2,109, 0, 0, 0, 1, 67, 0, 0, 0,
+ 0, 0, 0, 1, 76, 0, 0, 0, 2,109, 0, 0, 0, 1, 84, 0, 0, 0, 0, 0,
+ 0, 1, 90, 0, 0, 0, 2,109, 0, 0, 0, 1, 97, 0, 0, 0, 0, 0, 0, 1,
+101, 0, 0, 0, 3,115, 49, 0, 0, 0, 1,105, 0, 0, 0, 2,109, 0, 0, 0,
+ 1,112, 0, 0, 0, 0, 0, 0, 1,118, 0, 0, 0, 2,118, 0, 0, 0, 1,125,
+ 0, 0, 0, 0, 0, 0, 1,131, 0, 0, 0, 2,118, 0, 0, 0, 1,132, 0, 0,
+ 0, 3,116,112, 0, 0, 0, 1,132, 0, 0, 0, 3,109,100, 0, 0, 0, 1,142,
+ 0, 0, 0, 0, 0, 0, 1,142, 0, 0, 0, 0, 0, 0, 1,142, 0, 0, 0, 0,
+ 0, 0, 0, 38, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 5,107,105,110,100,
+ 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 8, 37,115, 42, 61,
+ 37,115, 42, 0, 2, 0, 0, 0, 2, 61, 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0,
+ 13, 95, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,
+116, 0, 2, 0, 0, 0, 7, 37, 42, 37,115, 42, 38, 0, 2, 0, 0, 0, 2,110,
+ 0, 2, 0, 0, 0, 5,102,117,110, 99, 0, 2, 0, 0, 0, 6,101,114,114,111,
+114, 0, 2, 0, 0, 0, 32, 35,105,110,118, 97,108,105,100, 32,102,117,110, 99,
+116,105,111,110, 32,114,101,116,117,114,110, 32,116,121,112,101, 58, 32, 0, 2,
+ 0, 0, 0, 2,109, 0, 2, 0, 0, 0, 6, 37,115, 37,115, 42, 0, 2, 0, 0,
+ 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0,
+ 2, 42, 0, 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 2, 38, 0, 2,
+ 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 7, 99,111,110, 99, 97,116, 0,
+ 2, 0, 0, 0, 8, 37, 42, 37,115, 42, 37, 42, 0, 2, 0, 0, 0, 3,115, 49,
+ 0, 2, 0, 0, 0, 7, 40, 37, 98, 91, 93, 41, 0, 4, 0, 0, 1,101, 0, 0,
+ 0, 17, 64,100,101, 99,108, 97,114, 97,116,105,111,110, 46,108,117, 97, 0, 0,
+ 0, 0, 17, 5, 1, 59, 1,101, 15, 1, 13, 0, 11, 2, 11, 3, 3, 1, 3, 0,
+ 0, 0, 0, 1, 0, 0, 1,101, 0, 0, 0, 2,110, 0, 0, 0, 0, 4, 2, 0,
+ 0, 0, 2,110, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 3,
+ 37, 42, 0, 2, 0, 0, 0, 2, 1, 0, 2, 0, 0, 0, 3, 37, 42, 0, 2, 0,
+ 0, 0, 2,110, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 2,
+ 1, 0, 2, 0, 0, 0, 2,118, 0, 2, 0, 0, 0, 7,105,115,116,121,112,101,
+ 0, 2, 0, 0, 0, 3,116,112, 0, 2, 0, 0, 0, 3,109,100, 0,
+};
+
+/* container.lo */
+static char B4[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 15, 64, 99,111,110,116, 97,
+105,110,101,114, 46,108,117, 97, 0, 0, 0, 0,162, 5, 0, 60, 17, 60, 18, 22,
+ 2, 60, 19, 11, 1, 4, 0, 11, 2, 60, 20, 15, 3, 30, 1, 60, 21, 25, 0, 60,
+ 22, 15, 4, 15, 0, 15, 5, 2, 0, 2, 60, 25, 15, 0, 11, 6, 11, 7, 26, 60,
+ 36, 15, 0, 11, 8, 11, 9, 26, 60, 48, 11, 11, 25, 10, 60, 58, 11, 13, 25, 12,
+ 60, 63, 11, 15, 25, 14, 60, 68, 11, 17, 25, 16, 60, 73, 11, 19, 25, 18, 60, 78,
+ 11, 21, 25, 20, 60, 83, 11, 23, 25, 22, 60, 88, 15, 24, 11, 25, 11, 26, 26, 60,
+ 95, 15, 24, 11, 27, 11, 28, 26, 60,101, 15, 24, 11, 29, 11, 30, 26, 60,110, 15,
+ 24, 11, 31, 11, 32, 26, 60,129, 15, 24, 11, 33, 11, 34, 26, 60,147, 15, 24, 11,
+ 35, 11, 36, 26, 59, 1, 49, 15, 24, 11, 37, 11, 38, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 39, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,
+101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,
+101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,
+111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,
+103, 0, 4, 0, 0, 0, 25, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,
+114, 46,108,117, 97, 0, 0, 0, 0, 59, 4, 1, 60, 26, 15, 0, 13, 0, 2, 0,
+ 1, 60, 27, 7, 1, 50, 23, 60, 29, 13, 0, 13, 1, 16, 20, 3, 2, 0, 1, 60,
+ 30, 13, 1, 7, 1, 37, 23, 1, 60, 31, 60, 28, 13, 0, 13, 1, 16, 54, 32, 60,
+ 32, 15, 4, 2, 0, 0, 60, 33, 0, 0, 0, 0, 2, 0, 0, 0, 25, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 27, 0, 0, 0, 2,105, 0, 0, 0, 0, 5,
+ 2, 0, 0, 0, 5,112,117,115,104, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0,
+ 2, 0, 0, 0, 4,112,111,112, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,
+101, 0, 4, 0, 0, 0, 36, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,
+114, 46,108,117, 97, 0, 0, 0, 0, 59, 4, 1, 60, 37, 15, 0, 13, 0, 2, 0,
+ 1, 60, 38, 7, 1, 50, 23, 60, 40, 13, 0, 13, 1, 16, 20, 3, 2, 0, 1, 60,
+ 41, 13, 1, 7, 1, 37, 23, 1, 60, 42, 60, 39, 13, 0, 13, 1, 16, 54, 32, 60,
+ 43, 15, 4, 2, 0, 0, 60, 44, 0, 0, 0, 0, 2, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 38, 0, 0, 0, 2,105, 0, 0, 0, 0, 5,
+ 2, 0, 0, 0, 5,112,117,115,104, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0,
+ 2, 0, 0, 0, 4,112,111,112, 0, 2, 0, 0, 0, 11, 95, 67,111,110,116, 97,
+105,110,101,114, 0, 4, 0, 0, 0, 48, 0, 0, 0, 15, 64, 99,111,110,116, 97,
+105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 64, 6, 1, 60, 49, 13, 0, 11,
+ 1, 15, 2, 26, 60, 50, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 51, 13, 0, 11,
+ 5, 7, 0, 26, 60, 52, 13, 0, 11, 6, 22, 1, 11, 5, 7, 0, 30, 0, 26, 60,
+ 53, 13, 0, 11, 7, 22, 0, 26, 60, 54, 13, 0, 1, 1, 60, 55, 0, 0, 0, 0,
+ 1, 0, 0, 0, 48, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 8, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2,
+ 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 9,116,121,
+112,101,100,101,102,115, 0, 2, 0, 0, 0, 7,108,110, 97,109,101,115, 0, 2,
+ 0, 0, 0, 5,112,117,115,104, 0, 4, 0, 0, 0, 58, 0, 0, 0, 15, 64, 99,
+111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 14, 4, 1, 60,
+ 59, 15, 1, 11, 2, 13, 0, 26, 60, 60, 0, 0, 0, 0, 1, 0, 0, 0, 58, 0,
+ 0, 0, 2,116, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0,
+ 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0,
+ 5, 99,117,114,114, 0, 2, 0, 0, 0, 4,112,111,112, 0, 4, 0, 0, 0, 63,
+ 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0,
+ 0, 0, 18, 3, 0, 60, 64, 15, 0, 11, 1, 15, 0, 18, 1, 18, 2, 26, 60, 65,
+ 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,
+111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2,
+ 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 7, 97,112,112,101,
+110,100, 0, 4, 0, 0, 0, 68, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,
+101,114, 46,108,117, 97, 0, 0, 0, 0, 18, 4, 1, 60, 69, 15, 1, 18, 2, 20,
+ 3, 13, 0, 3, 1, 2, 60, 70, 0, 0, 0, 0, 1, 0, 0, 0, 68, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 15, 99,
+108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,
+117,114,114, 0, 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0,
+ 14, 97,112,112,101,110,100,116,121,112,101,100,101,102, 0, 4, 0, 0, 0, 73,
+ 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0,
+ 0, 0, 18, 4, 1, 60, 74, 15, 1, 18, 2, 20, 3, 13, 0, 3, 1, 2, 60, 75,
+ 0, 0, 0, 0, 1, 0, 0, 0, 73, 0, 0, 0, 2,116, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116,
+ 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2, 0, 0, 0,
+ 14, 97,112,112,101,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 12,
+102,105,110,100,116,121,112,101,100,101,102, 0, 4, 0, 0, 0, 78, 0, 0, 0,
+ 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 18,
+ 4, 1, 60, 79, 15, 1, 18, 2, 20, 3, 13, 0, 3, 1, 2, 60, 80, 0, 0, 0,
+ 0, 1, 0, 0, 0, 78, 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,
+111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2,
+ 0, 0, 0, 12,102,105,110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0,
+ 10,105,115,116,121,112,101,100,101,102, 0, 4, 0, 0, 0, 83, 0, 0, 0, 15,
+ 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 18, 4,
+ 1, 60, 84, 15, 1, 18, 2, 20, 3, 13, 0, 3, 1, 2, 60, 85, 0, 0, 0, 0,
+ 1, 0, 0, 0, 83, 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0, 4, 2, 0,
+ 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,
+110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5, 99,117,114,114, 0, 2, 0,
+ 0, 0, 10,105,115,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 15, 99,108,
+ 97,115,115, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 7, 97,112,
+112,101,110,100, 0, 4, 0, 0, 0, 88, 0, 0, 0, 15, 64, 99,111,110,116, 97,
+105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 39, 6, 2, 60, 89, 13, 0, 11,
+ 2, 13, 0, 18, 2, 7, 1, 37, 26, 60, 90, 13, 0, 13, 0, 18, 2, 13, 1, 26,
+ 60, 91, 13, 1, 11, 3, 13, 0, 26, 60, 92, 0, 0, 0, 0, 2, 0, 0, 0, 88,
+ 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 88, 0, 0, 0, 2,116, 0, 0,
+ 0, 0, 4, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2,
+ 0, 0, 0, 14, 97,112,112,101,110,100,116,121,112,101,100,101,102, 0, 4, 0,
+ 0, 0, 95, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117,
+ 97, 0, 0, 0, 0, 38, 6, 2, 60, 96, 13, 0, 18, 2, 11, 3, 13, 0, 18, 2,
+ 18, 3, 7, 1, 37, 26, 60, 97, 13, 0, 18, 2, 13, 0, 18, 2, 18, 3, 13, 1,
+ 26, 60, 98, 0, 0, 0, 0, 2, 0, 0, 0, 95, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0, 95, 0, 0, 0, 2,116, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,
+116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 9,116,121,112,
+101,100,101,102,115, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 9,111,118,
+101,114,108,111, 97,100, 0, 4, 0, 0, 0,101, 0, 0, 0, 15, 64, 99,111,110,
+116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 67, 6, 2, 60,102, 13,
+ 0, 18, 2, 13, 1, 16, 44, 52, 13, 60,103, 13, 0, 18, 2, 13, 1, 7, 0, 26,
+ 50, 21, 60,105, 13, 0, 18, 2, 13, 1, 13, 0, 18, 2, 13, 1, 16, 7, 1, 37,
+ 26, 60,106, 60,107, 15, 3, 11, 4, 13, 0, 18, 2, 13, 1, 16, 3, 2, 2, 60,
+108, 0, 0, 0, 0, 2, 0, 0, 0,101, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0,101, 0, 0, 0, 6,108,110, 97,109,101, 0, 0, 0, 0, 5, 2, 0, 0,
+ 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 7,108,110, 97,109,101,115, 0, 2, 0, 0, 0, 7,102,111,114,109, 97,
+116, 0, 2, 0, 0, 0, 5, 37, 48, 50,100, 0, 2, 0, 0, 0, 12,102,105,110,
+100,116,121,112,101,100,101,102, 0, 4, 0, 0, 0,110, 0, 0, 0, 15, 64, 99,
+111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0,142, 10, 2, 60,
+111, 13, 0, 50,117, 60,113, 13, 2, 18, 3, 52, 97, 60,114, 7, 1, 50, 76, 60,
+116, 13, 2, 18, 3, 13, 3, 16, 18, 5, 13, 1, 32, 52, 47, 60,117, 13, 2, 18,
+ 3, 13, 3, 16, 18, 8, 13, 2, 18, 3, 13, 3, 16, 18, 0, 60,118, 15, 11, 13,
+ 5, 2, 2, 1, 60,119, 13, 6, 11, 12, 42, 13, 4, 42, 13, 7, 1, 8, 5, 4,
+ 50, 2, 60,120, 60,121, 13, 3, 7, 1, 37, 23, 3, 60,122, 60,115, 13, 2, 18,
+ 3, 13, 3, 16, 54, 87, 5, 1, 50, 2, 60,123, 60,124, 13, 2, 18, 13, 23, 2,
+ 60,125, 60,112, 13, 2, 54,123, 60,126, 11, 14, 13, 1, 1, 3, 60,127, 0, 0,
+ 0, 0, 13, 0, 0, 0,110, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,110,
+ 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0,111, 0, 0, 0, 4,101,110,118,
+ 0, 0, 0, 0,114, 0, 0, 0, 2,105, 0, 0, 0, 0,117, 0, 0, 0, 5,109,
+111,100, 49, 0, 0, 0, 0,117, 0, 0, 0, 6,116,121,112,101, 49, 0, 0, 0,
+ 0,118, 0, 0, 0, 5,109,111,100, 50, 0, 0, 0, 0,118, 0, 0, 0, 6,116,
+121,112,101, 50, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0,
+ 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0,
+122, 0, 0, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0, 5,116,121,112,101, 0, 2,
+ 0, 0, 0, 4,101,110,118, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 9,116,121,112,101,100,101,102,115, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 6,117,116,121,112,101, 0, 2, 0, 0, 0, 5,109,111,100, 49, 0,
+ 2, 0, 0, 0, 6,116,121,112,101, 49, 0, 2, 0, 0, 0, 4,109,111,100, 0,
+ 2, 0, 0, 0, 5,109,111,100, 50, 0, 2, 0, 0, 0, 6,116,121,112,101, 50,
+ 0, 2, 0, 0, 0, 12,102,105,110,100,116,121,112,101,100,101,102, 0, 2, 0,
+ 0, 0, 2, 32, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0,
+ 0, 1, 0, 2, 0, 0, 0, 10,105,115,116,121,112,101,100,101,102, 0, 4, 0,
+ 0, 0,129, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117,
+ 97, 0, 0, 0, 0,101, 6, 2, 60,130, 13, 0, 50, 78, 60,132, 13, 2, 18, 3,
+ 52, 58, 60,133, 7, 1, 50, 37, 60,135, 13, 2, 18, 3, 13, 3, 16, 18, 5, 13,
+ 1, 32, 52, 8, 60,136, 7, 1, 1, 4, 50, 2, 60,137, 60,138, 13, 3, 7, 1,
+ 37, 23, 3, 60,139, 60,134, 13, 2, 18, 3, 13, 3, 16, 54, 48, 5, 1, 50, 2,
+ 60,140, 60,141, 13, 2, 18, 6, 23, 2, 60,142, 60,131, 13, 2, 54, 84, 60,143,
+ 4, 0, 1, 3, 60,144, 0, 0, 0, 0, 5, 0, 0, 0,129, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0,129, 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0,
+130, 0, 0, 0, 4,101,110,118, 0, 0, 0, 0,133, 0, 0, 0, 2,105, 0, 0,
+ 0, 0,139, 0, 0, 0, 0, 0, 0, 0, 7, 2, 0, 0, 0, 5,116,121,112,101,
+ 0, 2, 0, 0, 0, 4,101,110,118, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 9,116,121,112,101,100,101,102,115, 0, 2, 0, 0, 0, 2,105,
+ 0, 2, 0, 0, 0, 6,117,116,121,112,101, 0, 2, 0, 0, 0, 7,112, 97,114,
+101,110,116, 0, 2, 0, 0, 0, 8,100,111,112, 97,114,115,101, 0, 4, 0, 0,
+ 0,147, 0, 0, 0, 15, 64, 99,111,110,116, 97,105,110,101,114, 46,108,117, 97,
+ 0, 0, 0, 4, 55, 13, 2, 60,151, 15, 5, 13, 1, 11, 6, 2, 4, 2, 60,152,
+ 13, 2, 52, 42, 60,153, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 60,
+154, 15, 9, 13, 4, 13, 5, 2, 0, 2, 60,155, 15, 8, 13, 1, 13, 3, 7, 1,
+ 37, 3, 6, 2, 50, 2, 60,156, 5, 4, 60,157, 60,161, 15, 5, 13, 1, 11, 10,
+ 2, 3, 2, 60,162, 13, 2, 52, 40, 60,163, 15, 8, 13, 1, 13, 2, 13, 3, 2,
+ 1, 3, 25, 7, 60,164, 15, 11, 13, 4, 2, 0, 1, 60,165, 15, 8, 13, 1, 13,
+ 3, 7, 1, 37, 3, 5, 2, 50, 2, 60,166, 5, 3, 60,167, 60,171, 15, 5, 13,
+ 1, 11, 12, 2, 3, 2, 60,172, 13, 2, 52, 40, 60,173, 15, 8, 13, 1, 13, 2,
+ 13, 3, 2, 1, 3, 25, 7, 60,174, 15, 13, 13, 4, 2, 0, 1, 60,175, 15, 8,
+ 13, 1, 13, 3, 7, 1, 37, 3, 5, 2, 50, 2, 60,176, 5, 3, 60,177, 60,180,
+ 15, 5, 13, 1, 11, 14, 2, 4, 2, 60,181, 13, 2, 52, 52, 60,182, 15, 8, 13,
+ 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 60,183, 15, 13, 13, 4, 2, 0, 1, 60,
+184, 15, 15, 11, 16, 13, 5, 42, 2, 0, 1, 60,185, 15, 8, 13, 1, 13, 3, 7,
+ 1, 37, 3, 6, 2, 50, 2, 60,186, 5, 4, 60,187, 60,191, 15, 5, 13, 1, 11,
+ 21, 2, 6, 2, 60,192, 13, 2, 52, 46, 60,193, 15, 8, 13, 1, 13, 2, 13, 3,
+ 2, 1, 3, 25, 7, 60,194, 15, 22, 13, 4, 13, 5, 13, 6, 13, 7, 2, 0, 4,
+ 60,195, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 8, 2, 50, 2, 60,196, 5, 6,
+ 60,197, 60,201, 15, 5, 13, 1, 11, 23, 2, 5, 2, 60,202, 13, 2, 44, 52, 23,
+ 60,204, 15, 5, 13, 1, 11, 24, 2, 5, 2, 23, 6, 23, 5, 23, 4, 23, 3, 23,
+ 2, 50, 2, 60,205, 60,206, 13, 2, 52, 44, 60,207, 15, 8, 13, 1, 13, 2, 13,
+ 3, 2, 1, 3, 25, 7, 60,208, 15, 25, 13, 4, 13, 5, 13, 6, 2, 0, 3, 60,
+209, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 7, 2, 50, 2, 60,210, 5, 5, 60,
+211, 60,215, 15, 5, 13, 1, 11, 26, 2, 5, 2, 60,216, 13, 2, 44, 52, 23, 60,
+218, 15, 5, 13, 1, 11, 27, 2, 5, 2, 23, 6, 23, 5, 23, 4, 23, 3, 23, 2,
+ 50, 2, 60,219, 60,220, 13, 2, 52, 44, 60,221, 15, 8, 13, 1, 13, 2, 13, 3,
+ 2, 1, 3, 25, 7, 60,222, 15, 25, 13, 4, 13, 5, 13, 6, 2, 0, 3, 60,223,
+ 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 7, 2, 50, 2, 60,224, 5, 5, 60,225,
+ 60,229, 15, 5, 13, 1, 11, 29, 2, 5, 2, 60,230, 13, 2, 44, 52, 59, 60,231,
+ 15, 5, 13, 1, 11, 30, 2, 5, 2, 23, 6, 23, 5, 23, 4, 23, 3, 23, 2, 60,
+232, 13, 2, 44, 52, 27, 60,233, 11, 31, 23, 5, 60,234, 15, 5, 13, 1, 11, 32,
+ 2, 4, 2, 23, 4, 23, 6, 23, 3, 23, 2, 50, 2, 60,235, 50, 2, 60,236, 60,
+237, 13, 2, 52, 80, 60,238, 13, 5, 11, 31, 31, 52, 25, 60,239, 4, 1, 60,240,
+ 15, 5, 13, 5, 11, 33, 2, 3, 2, 23, 5, 23, 8, 23, 7, 5, 2, 50, 2, 60,
+241, 60,242, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 60,243, 15, 34,
+ 13, 4, 13, 5, 13, 6, 2, 0, 3, 60,244, 15, 8, 13, 1, 13, 3, 7, 1, 37,
+ 3, 7, 2, 50, 2, 60,245, 5, 5, 60,246, 60,250, 15, 5, 13, 1, 11, 36, 2,
+ 3, 2, 60,251, 13, 2, 52, 40, 60,252, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1,
+ 3, 25, 7, 60,253, 15, 15, 13, 4, 2, 0, 1, 60,254, 15, 8, 13, 1, 13, 3,
+ 7, 1, 37, 3, 5, 2, 50, 2, 60,255, 5, 3, 59, 1, 0, 59, 1, 4, 15, 5,
+ 13, 1, 11, 37, 2, 3, 2, 59, 1, 5, 13, 2, 52, 43, 59, 1, 6, 15, 8, 13,
+ 1, 13, 2, 13, 3, 2, 1, 3, 25, 7, 59, 1, 7, 15, 38, 13, 4, 2, 0, 1,
+ 59, 1, 8, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3, 5, 2, 50, 3, 59, 1, 9,
+ 5, 3, 59, 1, 10, 59, 1, 14, 15, 5, 13, 1, 11, 39, 2, 3, 2, 59, 1, 15,
+ 13, 2, 52, 43, 59, 1, 16, 15, 8, 13, 1, 13, 2, 13, 3, 2, 1, 3, 25, 7,
+ 59, 1, 17, 15, 40, 13, 4, 2, 0, 1, 59, 1, 18, 15, 8, 13, 1, 13, 3, 7,
+ 1, 37, 3, 5, 2, 50, 3, 59, 1, 19, 5, 3, 59, 1, 20, 59, 1, 24, 15, 5,
+ 13, 1, 11, 42, 2, 3, 2, 59, 1, 25, 13, 2, 52, 36, 59, 1, 26, 15, 43, 15,
+ 8, 13, 4, 7, 2, 9, 2, 2, 1, 3, 2, 0, 1, 59, 1, 27, 15, 8, 13, 1,
+ 13, 3, 7, 1, 37, 3, 5, 2, 50, 3, 59, 1, 28, 5, 3, 59, 1, 29, 59, 1,
+ 33, 15, 5, 13, 1, 11, 45, 2, 3, 2, 59, 1, 34, 13, 2, 52, 27, 59, 1, 35,
+ 15, 46, 13, 4, 2, 0, 1, 59, 1, 36, 15, 8, 13, 1, 13, 3, 7, 1, 37, 3,
+ 5, 2, 50, 3, 59, 1, 37, 5, 3, 59, 1, 38, 59, 1, 41, 15, 47, 13, 1, 11,
+ 48, 11, 31, 2, 1, 3, 11, 31, 31, 52, 19, 59, 1, 42, 13, 1, 25, 7, 59, 1,
+ 43, 15, 49, 11, 50, 2, 0, 1, 50, 10, 59, 1, 45, 11, 31, 1, 2, 59, 1, 46,
+ 59, 1, 47, 0, 0, 0, 0,106, 0, 0, 0,147, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0,147, 0, 0, 0, 2,115, 0, 0, 0, 0,151, 0, 0, 0, 2, 98,
+ 0, 0, 0, 0,151, 0, 0, 0, 2,101, 0, 0, 0, 0,151, 0, 0, 0, 5,110,
+ 97,109,101, 0, 0, 0, 0,151, 0, 0, 0, 5, 98,111,100,121, 0, 0, 0, 0,
+156, 0, 0, 0, 0, 0, 0, 0,156, 0, 0, 0, 0, 0, 0, 0,156, 0, 0, 0,
+ 0, 0, 0, 0,156, 0, 0, 0, 0, 0, 0, 0,161, 0, 0, 0, 2, 98, 0, 0,
+ 0, 0,161, 0, 0, 0, 2,101, 0, 0, 0, 0,161, 0, 0, 0, 5,110, 97,109,
+101, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0,
+ 0,166, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 2, 98, 0, 0, 0, 0,171,
+ 0, 0, 0, 2,101, 0, 0, 0, 0,171, 0, 0, 0, 5, 98,111,100,121, 0, 0,
+ 0, 0,176, 0, 0, 0, 0, 0, 0, 0,176, 0, 0, 0, 0, 0, 0, 0,176, 0,
+ 0, 0, 0, 0, 0, 0,180, 0, 0, 0, 2, 98, 0, 0, 0, 0,180, 0, 0, 0,
+ 2,101, 0, 0, 0, 0,180, 0, 0, 0, 5, 98,111,100,121, 0, 0, 0, 0,180,
+ 0, 0, 0, 5,110, 97,109,101, 0, 0, 0, 0,186, 0, 0, 0, 0, 0, 0, 0,
+186, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0,
+ 0, 0, 0, 0,191, 0, 0, 0, 2, 98, 0, 0, 0, 0,191, 0, 0, 0, 2,101,
+ 0, 0, 0, 0,191, 0, 0, 0, 5,100,101, 99,108, 0, 0, 0, 0,191, 0, 0,
+ 0, 5,107,105,110,100, 0, 0, 0, 0,191, 0, 0, 0, 4, 97,114,103, 0, 0,
+ 0, 0,191, 0, 0, 0, 6, 99,111,110,115,116, 0, 0, 0, 0,196, 0, 0, 0,
+ 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0,
+196, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0, 0, 0, 0, 0,196, 0, 0, 0,
+ 0, 0, 0, 0,201, 0, 0, 0, 2, 98, 0, 0, 0, 0,201, 0, 0, 0, 2,101,
+ 0, 0, 0, 0,201, 0, 0, 0, 5,100,101, 99,108, 0, 0, 0, 0,201, 0, 0,
+ 0, 4, 97,114,103, 0, 0, 0, 0,201, 0, 0, 0, 6, 99,111,110,115,116, 0,
+ 0, 0, 0,210, 0, 0, 0, 0, 0, 0, 0,210, 0, 0, 0, 0, 0, 0, 0,210,
+ 0, 0, 0, 0, 0, 0, 0,210, 0, 0, 0, 0, 0, 0, 0,210, 0, 0, 0, 0,
+ 0, 0, 0,215, 0, 0, 0, 2, 98, 0, 0, 0, 0,215, 0, 0, 0, 2,101, 0,
+ 0, 0, 0,215, 0, 0, 0, 5,100,101, 99,108, 0, 0, 0, 0,215, 0, 0, 0,
+ 4, 97,114,103, 0, 0, 0, 0,215, 0, 0, 0, 6, 99,111,110,115,116, 0, 0,
+ 0, 0,224, 0, 0, 0, 0, 0, 0, 0,224, 0, 0, 0, 0, 0, 0, 0,224, 0,
+ 0, 0, 0, 0, 0, 0,224, 0, 0, 0, 0, 0, 0, 0,224, 0, 0, 0, 0, 0,
+ 0, 0,229, 0, 0, 0, 2, 98, 0, 0, 0, 0,229, 0, 0, 0, 2,101, 0, 0,
+ 0, 0,229, 0, 0, 0, 5,110, 97,109,101, 0, 0, 0, 0,229, 0, 0, 0, 5,
+ 98, 97,115,101, 0, 0, 0, 0,229, 0, 0, 0, 5, 98,111,100,121, 0, 0, 0,
+ 0,239, 0, 0, 0, 2, 98, 0, 0, 0, 0,239, 0, 0, 0, 2,101, 0, 0, 0,
+ 0,240, 0, 0, 0, 0, 0, 0, 0,240, 0, 0, 0, 0, 0, 0, 0,245, 0, 0,
+ 0, 0, 0, 0, 0,245, 0, 0, 0, 0, 0, 0, 0,245, 0, 0, 0, 0, 0, 0,
+ 0,245, 0, 0, 0, 0, 0, 0, 0,245, 0, 0, 0, 0, 0, 0, 0,250, 0, 0,
+ 0, 2, 98, 0, 0, 0, 0,250, 0, 0, 0, 2,101, 0, 0, 0, 0,250, 0, 0,
+ 0, 6,116,121,112,101,115, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 0,255,
+ 0, 0, 0, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 2,
+ 98, 0, 0, 0, 1, 4, 0, 0, 0, 2,101, 0, 0, 0, 1, 4, 0, 0, 0, 5,
+100,101, 99,108, 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 0, 1, 9, 0, 0, 0,
+ 0, 0, 0, 1, 9, 0, 0, 0, 0, 0, 0, 1, 14, 0, 0, 0, 2, 98, 0, 0,
+ 0, 1, 14, 0, 0, 0, 2,101, 0, 0, 0, 1, 14, 0, 0, 0, 5,100,101, 99,
+108, 0, 0, 0, 1, 19, 0, 0, 0, 0, 0, 0, 1, 19, 0, 0, 0, 0, 0, 0,
+ 1, 19, 0, 0, 0, 0, 0, 0, 1, 24, 0, 0, 0, 2, 98, 0, 0, 0, 1, 24,
+ 0, 0, 0, 2,101, 0, 0, 0, 1, 24, 0, 0, 0, 5, 99,111,100,101, 0, 0,
+ 0, 1, 28, 0, 0, 0, 0, 0, 0, 1, 28, 0, 0, 0, 0, 0, 0, 1, 28, 0,
+ 0, 0, 0, 0, 0, 1, 33, 0, 0, 0, 2, 98, 0, 0, 0, 1, 33, 0, 0, 0,
+ 2,101, 0, 0, 0, 1, 33, 0, 0, 0, 5,108,105,110,101, 0, 0, 0, 1, 37,
+ 0, 0, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0, 0, 0, 1, 37, 0, 0, 0, 0,
+ 0, 0, 0, 51, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 2, 98, 0, 2, 0,
+ 0, 0, 2,101, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5,
+ 98,111,100,121, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0,
+ 0, 0, 41, 94, 37,115, 42,109,111,100,117,108,101, 37,115, 37,115, 42, 40, 91,
+ 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40, 37, 98,123,125, 41,
+ 37,115, 42, 0, 2, 0, 0, 0, 11, 95, 99,117,114,114, 95, 99,111,100,101, 0,
+ 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7, 77,111,100,
+117,108,101, 0, 2, 0, 0, 0, 34, 94, 37,115, 42, 35,100,101,102,105,110,101,
+ 37,115, 37,115, 42, 40, 91, 94, 37,115, 93, 42, 41, 91, 94, 10, 93, 42, 10, 37,
+115, 42, 0, 2, 0, 0, 0, 7, 68,101,102,105,110,101, 0, 2, 0, 0, 0, 28,
+ 94, 37,115, 42,101,110,117,109, 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,
+115, 42, 59, 63, 37,115, 42, 0, 2, 0, 0, 0, 10, 69,110,117,109,101,114, 97,
+116,101, 0, 2, 0, 0, 0, 55, 94, 37,115, 42,116,121,112,101,100,101,102, 37,
+115, 37,115, 42,101,110,117,109, 91, 94,123, 93, 42, 40, 37, 98,123,125, 41, 37,
+115, 42, 40, 91, 37,119, 95, 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42, 59, 37,
+115, 42, 0, 2, 0, 0, 0, 8, 84,121,112,101,100,101,102, 0, 2, 0, 0, 0,
+ 5,105,110,116, 32, 0, 2, 0, 0, 0, 5,100,101, 99,108, 0, 2, 0, 0, 0,
+ 5,107,105,110,100, 0, 2, 0, 0, 0, 4, 97,114,103, 0, 2, 0, 0, 0, 6,
+ 99,111,110,115,116, 0, 2, 0, 0, 0, 78, 94, 37,115, 42, 40, 91, 95, 37,119,
+ 93, 91, 95, 37,119, 37,115, 37, 42, 38, 93, 42,111,112,101,114, 97,116,111,114,
+ 41, 37,115, 42, 40, 91, 94, 37,115, 93, 91, 94, 37,115, 93, 42, 41, 37,115, 42,
+ 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63,
+ 41, 37,115, 42, 59, 37,115, 42, 0, 2, 0, 0, 0, 9, 79,112,101,114, 97,116,
+111,114, 0, 2, 0, 0, 0, 71, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91,
+ 95, 64, 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115, 42,
+ 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63,
+ 41, 37,115, 42, 61, 63, 37,115, 42, 48, 63, 37,115, 42, 59, 37,115, 42, 0, 2,
+ 0, 0, 0, 43, 94, 37,115, 42, 40, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37,
+ 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,
+115, 42, 59, 37,115, 42, 0, 2, 0, 0, 0, 9, 70,117,110, 99,116,105,111,110,
+ 0, 2, 0, 0, 0, 64, 94, 37,115, 42, 40, 91,126, 95, 37,119, 93, 91, 95, 64,
+ 37,119, 37,115, 37, 42, 38, 93, 42, 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37,
+ 98, 40, 41, 41, 37,115, 42, 40, 99, 63,111, 63,110, 63,115, 63,116, 63, 41, 37,
+115, 42, 37, 98,123,125, 37,115, 42, 0, 2, 0, 0, 0, 46, 94, 37,115, 42, 40,
+ 91, 95, 37,119, 93, 41, 37,115, 42, 40, 37, 98, 40, 41, 41, 37,115, 42, 40, 99,
+ 63,111, 63,110, 63,115, 63,116, 63, 41, 37,115, 42, 37, 98,123,125, 37,115, 42,
+ 0, 2, 0, 0, 0, 5, 98, 97,115,101, 0, 2, 0, 0, 0, 49, 94, 37,115, 42,
+ 99,108, 97,115,115, 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42,
+ 41, 37,115, 42, 40, 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42,
+ 59, 37,115, 42, 0, 2, 0, 0, 0, 50, 94, 37,115, 42,115,116,114,117, 99,116,
+ 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 40,
+ 46, 45, 41, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 59, 37,115, 42, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 66, 94, 37,115, 42,116,121,112,101,100,
+101,102, 37,115, 37,115, 42,115,116,114,117, 99,116, 37,115, 37,115, 42, 91, 95,
+ 37,119, 93, 42, 37,115, 42, 40, 37, 98,123,125, 41, 37,115, 42, 40, 91, 95, 37,
+119, 93, 91, 95, 37,119, 93, 42, 41, 37,115, 42, 59, 37,115, 42, 0, 2, 0, 0,
+ 0, 17, 46, 45, 40, 91, 95, 37,119, 93, 91, 95, 37,119, 93, 42, 41, 36, 0, 2,
+ 0, 0, 0, 6, 67,108, 97,115,115, 0, 2, 0, 0, 0, 6,116,121,112,101,115,
+ 0, 2, 0, 0, 0, 28, 94, 37,115, 42,116,121,112,101,100,101,102, 37,115, 37,
+115, 42, 40, 46, 45, 41, 37,115, 42, 59, 37,115, 42, 0, 2, 0, 0, 0, 40, 94,
+ 37,115, 42, 40, 91, 95, 37,119, 93, 91, 95, 64, 37,115, 37,119, 37,100, 37, 42,
+ 38, 93, 42, 91, 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42, 0, 2,
+ 0, 0, 0, 9, 86, 97,114,105, 97, 98,108,101, 0, 2, 0, 0, 0, 43, 94, 37,
+115, 42, 40, 91, 95, 37,119, 93, 91, 93, 91, 95, 64, 37,115, 37,119, 37,100, 37,
+ 42, 38, 93, 42, 91, 93, 95, 37,119, 37,100, 93, 41, 37,115, 42, 59, 37,115, 42,
+ 0, 2, 0, 0, 0, 6, 65,114,114, 97,121, 0, 2, 0, 0, 0, 5, 99,111,100,
+101, 0, 2, 0, 0, 0, 11, 94, 37,115, 42, 40, 37, 98, 1, 2, 41, 0, 2, 0,
+ 0, 0, 5, 67,111,100,101, 0, 2, 0, 0, 0, 5,108,105,110,101, 0, 2, 0,
+ 0, 0, 12, 94, 37,115, 42, 37, 36, 40, 46, 45, 10, 41, 0, 2, 0, 0, 0, 9,
+ 86,101,114, 98, 97,116,105,109, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2,
+ 0, 0, 0, 6, 37,115, 37,115, 42, 0, 2, 0, 0, 0, 6,101,114,114,111,114,
+ 0, 2, 0, 0, 0, 13, 35,112, 97,114,115,101, 32,101,114,114,111,114, 0, 2,
+ 0, 0, 0, 6,112, 97,114,115,101, 0, 4, 0, 0, 1, 49, 0, 0, 0, 15, 64,
+ 99,111,110,116, 97,105,110,101,114, 46,108,117, 97, 0, 0, 0, 0, 35, 5, 2,
+ 50, 17, 59, 1, 51, 13, 0, 20, 3, 13, 1, 2, 1, 2, 23, 1, 59, 1, 52, 59,
+ 1, 50, 13, 1, 11, 1, 31, 54, 27, 59, 1, 53, 0, 0, 0, 0, 2, 0, 0, 1,
+ 49, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 1, 49, 0, 0, 0, 2,115, 0,
+ 0, 0, 0, 4, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0,
+ 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,100,111,112, 97,114,115,101, 0,
+
+};
+
+/* package.lo */
+static char B5[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 13, 64,112, 97, 99,107, 97,
+103,101, 46,108,117, 97, 0, 0, 0, 0,102, 5, 0, 60, 20, 22, 2, 60, 21, 11,
+ 1, 15, 2, 11, 3, 60, 22, 11, 4, 30, 1, 60, 23, 25, 0, 60, 24, 15, 5, 15,
+ 0, 15, 6, 2, 0, 2, 60, 27, 15, 0, 11, 7, 11, 8, 26, 60, 36, 15, 0, 11,
+ 9, 11, 10, 26, 60, 60, 15, 0, 11, 11, 11, 12, 26, 60,100, 15, 0, 11, 13, 11,
+ 14, 26, 60,119, 15, 0, 11, 15, 11, 16, 26, 60,132, 15, 0, 11, 17, 11, 18, 26,
+ 60,146, 11, 20, 25, 19, 60,155, 11, 22, 25, 21, 0, 0, 0, 0, 0, 0, 0, 0,
+ 23, 2, 0, 0, 0, 13, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 0, 2,
+ 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115,
+ 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 8,112, 97, 99,107, 97,103,101, 0, 2, 0, 0, 0, 7,115,101,
+116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0,
+ 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 27, 0, 0, 0, 13,
+ 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0, 61, 6, 1, 60,
+ 28, 15, 0, 11, 1, 13, 0, 18, 3, 42, 2, 0, 1, 60, 29, 7, 1, 50, 27, 60,
+ 31, 13, 0, 13, 1, 16, 20, 0, 11, 5, 11, 5, 2, 0, 3, 60, 32, 13, 1, 7,
+ 1, 37, 23, 1, 60, 33, 60, 30, 13, 0, 13, 1, 16, 54, 36, 60, 34, 0, 0, 0,
+ 0, 2, 0, 0, 0, 27, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 29, 0,
+ 0, 0, 2,105, 0, 0, 0, 0, 6, 2, 0, 0, 0, 6,112,114,105,110,116, 0,
+ 2, 0, 0, 0, 10, 80, 97, 99,107, 97,103,101, 58, 32, 0, 2, 0, 0, 0, 5,
+115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 2,
+105, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 11,112,114,101,112,114,111, 99,
+101,115,115, 0, 4, 0, 0, 0, 36, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,
+101, 46,108,117, 97, 0, 0, 0, 1, 83, 7, 1, 60, 38, 13, 0, 11, 1, 11, 2,
+ 13, 0, 18, 1, 42, 26, 60, 40, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 4,
+ 11, 5, 2, 1, 3, 26, 60, 41, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 6,
+ 11, 7, 2, 1, 3, 26, 60, 42, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 8,
+ 11, 9, 2, 1, 3, 26, 60, 43, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 10,
+ 11, 5, 2, 1, 3, 26, 60, 44, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 7,
+ 11, 6, 2, 1, 3, 26, 60, 45, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 9,
+ 11, 8, 2, 1, 3, 26, 60, 46, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 11,
+ 11, 12, 2, 1, 3, 26, 60, 47, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 13,
+ 11, 14, 2, 1, 3, 26, 60, 48, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 15,
+ 11, 14, 2, 1, 3, 26, 60, 49, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 16,
+ 11, 14, 2, 1, 3, 26, 60, 50, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 17,
+ 11, 5, 2, 1, 3, 26, 60, 51, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 18,
+ 11, 19, 2, 1, 3, 26, 60, 52, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 18,
+ 11, 19, 2, 1, 3, 26, 60, 53, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 20,
+ 11, 21, 2, 1, 3, 26, 60, 55, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 22,
+ 11, 7, 2, 1, 3, 26, 60, 56, 13, 0, 11, 1, 15, 3, 13, 0, 18, 1, 11, 23,
+ 11, 9, 2, 1, 3, 26, 60, 57, 0, 0, 0, 0, 1, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 24, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 5, 99,111,100,101, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0,
+ 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 10, 40, 47, 47, 91, 94, 10, 93, 42,
+ 41, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4, 47, 37, 42, 0, 2, 0, 0,
+ 0, 2, 1, 0, 2, 0, 0, 0, 4, 37, 42, 47, 0, 2, 0, 0, 0, 2, 2, 0,
+ 2, 0, 0, 0, 5, 37, 98, 1, 2, 0, 2, 0, 0, 0, 8, 37,115, 42, 64, 37,
+115, 42, 0, 2, 0, 0, 0, 2, 64, 0, 2, 0, 0, 0, 14, 37,115, 63,105,110,
+108,105,110,101, 40, 37,115, 41, 0, 2, 0, 0, 0, 3, 37, 49, 0, 2, 0, 0,
+ 0, 14, 37,115, 63,101,120,116,101,114,110, 40, 37,115, 41, 0, 2, 0, 0, 0,
+ 15, 37,115, 63,118,105,114,116,117, 97,108, 40, 37,115, 41, 0, 2, 0, 0, 0,
+ 8,112,117, 98,108,105, 99, 58, 0, 2, 0, 0, 0, 18, 40, 91, 94, 37,119, 95,
+ 93, 41,118,111,105,100, 37,115, 42, 37, 42, 0, 2, 0, 0, 0, 13, 37, 49, 95,
+117,115,101,114,100, 97,116, 97, 32, 0, 2, 0, 0, 0, 18, 40, 91, 94, 37,119,
+ 95, 93, 41, 99,104, 97,114, 37,115, 42, 37, 42, 0, 2, 0, 0, 0, 12, 37, 49,
+ 95, 99,115,116,114,105,110,103, 32, 0, 2, 0, 0, 0, 5, 37, 36, 37, 91, 0,
+ 2, 0, 0, 0, 5, 37, 36, 37, 93, 0, 2, 0, 0, 0, 9,112,114,101, 97,109,
+ 98,108,101, 0, 4, 0, 0, 0, 60, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,
+101, 46,108,117, 97, 0, 0, 0, 1, 79, 5, 1, 60, 61, 15, 0, 11, 1, 2, 0,
+ 1, 60, 62, 15, 0, 11, 2, 13, 0, 18, 4, 42, 11, 5, 42, 2, 0, 1, 60, 63,
+ 15, 0, 11, 6, 15, 7, 42, 11, 8, 42, 15, 9, 2, 1, 0, 42, 11, 10, 42, 2,
+ 0, 1, 60, 64, 15, 0, 11, 11, 2, 0, 1, 60, 66, 15, 12, 18, 13, 44, 52, 54,
+ 60, 67, 15, 0, 11, 14, 2, 0, 1, 60, 68, 15, 0, 11, 15, 13, 0, 18, 4, 42,
+ 11, 16, 42, 2, 0, 1, 60, 69, 15, 0, 11, 17, 13, 0, 18, 4, 42, 11, 18, 42,
+ 2, 0, 1, 60, 70, 15, 0, 11, 5, 2, 0, 1, 50, 2, 60, 71, 60, 73, 15, 12,
+ 18, 19, 52, 46, 60, 74, 15, 0, 11, 20, 2, 0, 1, 60, 75, 15, 0, 11, 21, 2,
+ 0, 1, 60, 76, 15, 0, 11, 22, 13, 0, 18, 4, 42, 11, 23, 42, 2, 0, 1, 60,
+ 77, 15, 0, 11, 24, 2, 0, 1, 50, 2, 60, 78, 60, 79, 15, 0, 11, 25, 2, 0,
+ 1, 60, 80, 7, 1, 50, 23, 60, 82, 13, 0, 13, 1, 16, 20, 27, 2, 0, 1, 60,
+ 83, 13, 1, 7, 1, 37, 23, 1, 60, 84, 60, 81, 13, 0, 13, 1, 16, 54, 32, 60,
+ 85, 15, 0, 11, 5, 2, 0, 1, 60, 86, 15, 0, 11, 28, 2, 0, 1, 60, 87, 13,
+ 0, 20, 29, 2, 0, 1, 60, 88, 15, 0, 11, 5, 2, 0, 1, 60, 89, 15, 0, 11,
+ 30, 2, 0, 1, 60, 90, 15, 0, 11, 31, 2, 0, 1, 60, 91, 15, 0, 11, 32, 2,
+ 0, 1, 60, 92, 15, 33, 15, 34, 11, 35, 2, 0, 2, 60, 93, 15, 33, 15, 36, 11,
+ 37, 2, 0, 2, 60, 94, 15, 38, 11, 39, 2, 0, 1, 60, 95, 15, 38, 11, 5, 2,
+ 0, 1, 60, 96, 0, 0, 0, 0, 2, 0, 0, 0, 60, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 80, 0, 0, 0, 2,105, 0, 0, 0, 0, 40, 2, 0, 0, 0,
+ 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 4, 47, 42, 10, 0, 2, 0, 0,
+ 0, 17, 42, 42, 32, 76,117, 97, 32, 98,105,110,100,105,110,103, 58, 32, 0, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2,
+ 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 31, 42, 42, 32, 71,101,110,101,114, 97,
+116,101,100, 32, 97,117,116,111,109, 97,116,105, 99, 97,108,108,121, 32, 98,121,
+ 32, 0, 2, 0, 0, 0, 14, 84, 79, 76, 85, 65, 95, 86, 69, 82, 83, 73, 79, 78,
+ 0, 2, 0, 0, 0, 5, 32,111,110, 32, 0, 2, 0, 0, 0, 5,100, 97,116,101,
+ 0, 2, 0, 0, 0, 3, 46, 10, 0, 2, 0, 0, 0, 5, 42, 47, 10, 10, 0, 2,
+ 0, 0, 0, 6,102,108, 97,103,115, 0, 2, 0, 0, 0, 2,104, 0, 2, 0, 0,
+ 0, 24, 47, 42, 32, 69,120,112,111,114,116,101,100, 32,102,117,110, 99,116,105,
+111,110, 32, 42, 47, 0, 2, 0, 0, 0, 12,105,110,116, 32, 32,116,111,108,117,
+ 97, 95, 0, 2, 0, 0, 0, 14, 95,111,112,101,110, 32, 40,118,111,105,100, 41,
+ 59, 0, 2, 0, 0, 0, 12,118,111,105,100, 32,116,111,108,117, 97, 95, 0, 2,
+ 0, 0, 0, 15, 95, 99,108,111,115,101, 32, 40,118,111,105,100, 41, 59, 0, 2,
+ 0, 0, 0, 2, 97, 0, 2, 0, 0, 0, 45, 47, 42, 32, 65,117,116,111,109, 97,
+116,105, 99, 32,105,110,105,116,105, 97,108,105,122, 97,116,105,111,110, 32,102,
+111,114, 32, 67, 43, 43, 32, 99,111,100,101, 32, 42, 47, 10, 0, 2, 0, 0, 0,
+ 20, 35,105,102,100,101,102, 32, 95, 95, 99,112,108,117,115,112,108,117,115, 10,
+ 0, 2, 0, 0, 0, 26,115,116, 97,116,105, 99, 32,105,110,116, 32,100,117,109,
+109,121, 32, 61, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 10, 95,111,112,
+101,110, 32, 40, 41, 59, 0, 2, 0, 0, 0, 9, 35,101,110,100,105,102, 10, 10,
+ 0, 2, 0, 0, 0, 21, 35,105,110, 99,108,117,100,101, 32, 34,116,111,108,117,
+ 97, 46,104, 34, 10, 10, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 9,112,
+114,101, 97,109, 98,108,101, 0, 2, 0, 0, 0, 20, 47, 42, 32,116, 97,103, 32,
+118, 97,114,105, 97, 98,108,101,115, 32, 42, 47, 0, 2, 0, 0, 0, 8,100,101,
+ 99,108,116, 97,103, 0, 2, 0, 0, 0, 51, 47, 42, 32,102,117,110, 99,116,105,
+111,110, 32,116,111, 32,114,101,103,105,115,116,101,114, 32,116,121,112,101, 32,
+ 97,110,100, 32,105,110,105,116,105, 97,108,105,122,101, 32,116, 97,103, 32, 42,
+ 47, 0, 2, 0, 0, 0, 35,115,116, 97,116,105, 99, 32,118,111,105,100, 32,116,
+111,108,117, 97, 73, 95,105,110,105,116, 95,116, 97,103, 32, 40,118,111,105,100,
+ 41, 0, 2, 0, 0, 0, 2,123, 0, 2, 0, 0, 0, 8,102,111,114,101, 97, 99,
+104, 0, 2, 0, 0, 0, 10, 95,117,115,101,114,116,121,112,101, 0, 4, 0, 0,
+ 0, 92, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0,
+ 0, 0, 16, 6, 2, 60, 92, 15, 2, 11, 3, 13, 1, 11, 4, 2, 0, 3, 0, 0,
+ 0, 0, 2, 0, 0, 0, 92, 0, 0, 0, 2,110, 0, 0, 0, 0, 92, 0, 0, 0,
+ 2,118, 0, 0, 0, 0, 5, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,118,
+ 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18, 32,116,
+111,108,117, 97, 95,117,115,101,114,116,121,112,101, 40, 34, 0, 2, 0, 0, 0,
+ 4, 34, 41, 59, 0, 2, 0, 0, 0, 10, 95,116, 97,103,110, 97,109,101,115, 0,
+ 4, 0, 0, 0, 93, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117,
+ 97, 0, 0, 0, 0, 21, 7, 2, 60, 93, 15, 2, 11, 3, 13, 1, 11, 4, 13, 0,
+ 42, 11, 5, 2, 0, 4, 0, 0, 0, 0, 2, 0, 0, 0, 93, 0, 0, 0, 2,110,
+ 0, 0, 0, 0, 93, 0, 0, 0, 2,118, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,
+110, 0, 2, 0, 0, 0, 2,118, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116,
+ 0, 2, 0, 0, 0, 16, 32,116,111,108,117, 97, 95,115,101,116,116, 97,103, 40,
+ 34, 0, 2, 0, 0, 0, 4, 34, 44, 38, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 2,125, 0, 2, 0,
+ 0, 0, 9,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0,100, 0, 0, 0,
+ 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0,132, 4, 1,
+ 60,101, 15, 0, 11, 1, 2, 0, 1, 60,102, 15, 0, 11, 2, 13, 0, 18, 4, 42,
+ 11, 5, 42, 2, 0, 1, 60,103, 15, 0, 11, 6, 2, 0, 1, 60,104, 15, 0, 11,
+ 7, 2, 0, 1, 60,105, 15, 0, 11, 8, 2, 0, 1, 60,106, 15, 0, 11, 9, 2,
+ 0, 1, 60,107, 7, 1, 50, 23, 60,109, 13, 0, 13, 1, 16, 20, 11, 2, 0, 1,
+ 60,110, 13, 1, 7, 1, 37, 23, 1, 60,111, 60,108, 13, 0, 13, 1, 16, 54, 32,
+ 60,112, 15, 0, 11, 12, 2, 0, 1, 60,113, 15, 0, 11, 13, 2, 0, 1, 60,114,
+ 15, 0, 11, 14, 2, 0, 1, 60,115, 0, 0, 0, 0, 2, 0, 0, 0,100, 0, 0,
+ 0, 5,115,101,108,102, 0, 0, 0, 0,107, 0, 0, 0, 2,105, 0, 0, 0, 0,
+ 15, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 20, 47, 42,
+ 32, 79,112,101,110, 32,102,117,110, 99,116,105,111,110, 32, 42, 47, 0, 2, 0,
+ 0, 0, 11,105,110,116, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 13, 95,
+111,112,101,110, 32, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2,
+ 0, 0, 0, 15, 32,116,111,108,117, 97, 95,111,112,101,110, 40, 41, 59, 0, 2,
+ 0, 0, 0, 19, 32,108,117, 97, 95, 98,101,103,105,110, 98,108,111, 99,107, 40,
+ 41, 59, 0, 2, 0, 0, 0, 20, 32,116,111,108,117, 97, 73, 95,105,110,105,116,
+ 95,116, 97,103, 40, 41, 59, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 9,
+114,101,103,105,115,116,101,114, 0, 2, 0, 0, 0, 17, 32,108,117, 97, 95,101,
+110,100, 98,108,111, 99,107, 40, 41, 59, 0, 2, 0, 0, 0, 11, 32,114,101,116,
+117,114,110, 32, 49, 59, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 11,117,
+110,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0,119, 0, 0, 0, 13, 64,
+112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0, 87, 4, 1, 60,120,
+ 15, 0, 11, 1, 2, 0, 1, 60,121, 15, 0, 11, 2, 13, 0, 18, 4, 42, 11, 5,
+ 42, 2, 0, 1, 60,122, 15, 0, 11, 6, 2, 0, 1, 60,123, 7, 1, 50, 23, 60,
+125, 13, 0, 13, 1, 16, 20, 8, 2, 0, 1, 60,126, 13, 1, 7, 1, 37, 23, 1,
+ 60,127, 60,124, 13, 0, 13, 1, 16, 54, 32, 60,128, 15, 0, 11, 9, 2, 0, 1,
+ 60,129, 0, 0, 0, 0, 2, 0, 0, 0,119, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0,123, 0, 0, 0, 2,105, 0, 0, 0, 0, 10, 2, 0, 0, 0, 7,111,
+117,116,112,117,116, 0, 2, 0, 0, 0, 21, 47, 42, 32, 67,108,111,115,101, 32,
+102,117,110, 99,116,105,111,110, 32, 42, 47, 0, 2, 0, 0, 0, 12,118,111,105,
+100, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 14, 95, 99,108,111,115,101,
+ 32, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2, 0, 0, 0, 2,
+105, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114, 0, 2, 0,
+ 0, 0, 2,125, 0, 2, 0, 0, 0, 7,104,101, 97,100,101,114, 0, 4, 0, 0,
+ 0,132, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0,
+ 0, 0,127, 4, 1, 60,133, 15, 0, 11, 1, 2, 0, 1, 15, 0, 11, 2, 13, 0,
+ 18, 4, 42, 11, 5, 42, 2, 0, 1, 60,134, 15, 0, 11, 6, 15, 7, 42, 11, 8,
+ 42, 15, 9, 2, 1, 0, 42, 11, 10, 42, 2, 0, 1, 60,135, 15, 0, 11, 11, 2,
+ 0, 1, 60,137, 15, 12, 18, 13, 44, 52, 54, 60,138, 15, 0, 11, 14, 2, 0, 1,
+ 60,139, 15, 0, 11, 15, 13, 0, 18, 4, 42, 11, 16, 42, 2, 0, 1, 60,140, 15,
+ 0, 11, 17, 13, 0, 18, 4, 42, 11, 18, 42, 2, 0, 1, 60,141, 15, 0, 11, 5,
+ 2, 0, 1, 50, 2, 60,142, 60,143, 0, 0, 0, 0, 1, 0, 0, 0,132, 0, 0,
+ 0, 5,115,101,108,102, 0, 0, 0, 0, 19, 2, 0, 0, 0, 7,111,117,116,112,
+117,116, 0, 2, 0, 0, 0, 4, 47, 42, 10, 0, 2, 0, 0, 0, 17, 42, 42, 32,
+ 76,117, 97, 32, 98,105,110,100,105,110,103, 58, 32, 0, 2, 0, 0, 0, 5,115,
+101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 2, 10,
+ 0, 2, 0, 0, 0, 31, 42, 42, 32, 71,101,110,101,114, 97,116,101,100, 32, 97,
+117,116,111,109, 97,116,105, 99, 97,108,108,121, 32, 98,121, 32, 0, 2, 0, 0,
+ 0, 14, 84, 79, 76, 85, 65, 95, 86, 69, 82, 83, 73, 79, 78, 0, 2, 0, 0, 0,
+ 5, 32,111,110, 32, 0, 2, 0, 0, 0, 5,100, 97,116,101, 0, 2, 0, 0, 0,
+ 3, 46, 10, 0, 2, 0, 0, 0, 5, 42, 47, 10, 10, 0, 2, 0, 0, 0, 6,102,
+108, 97,103,115, 0, 2, 0, 0, 0, 2,104, 0, 2, 0, 0, 0, 24, 47, 42, 32,
+ 69,120,112,111,114,116,101,100, 32,102,117,110, 99,116,105,111,110, 32, 42, 47,
+ 0, 2, 0, 0, 0, 12,105,110,116, 32, 32,116,111,108,117, 97, 95, 0, 2, 0,
+ 0, 0, 14, 95,111,112,101,110, 32, 40,118,111,105,100, 41, 59, 0, 2, 0, 0,
+ 0, 12,118,111,105,100, 32,116,111,108,117, 97, 95, 0, 2, 0, 0, 0, 15, 95,
+ 99,108,111,115,101, 32, 40,118,111,105,100, 41, 59, 0, 2, 0, 0, 0, 9, 95,
+ 80, 97, 99,107, 97,103,101, 0, 4, 0, 0, 0,146, 0, 0, 0, 13, 64,112, 97,
+ 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0, 0, 31, 4, 1, 60,147, 13, 0,
+ 11, 1, 15, 2, 26, 60,148, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60,149, 13, 0,
+ 1, 1, 60,150, 0, 0, 0, 0, 1, 0, 0, 0,146, 0, 0, 0, 2,116, 0, 0,
+ 0, 0, 5, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101,
+ 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 80, 97, 99,107, 97,103,101, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 8, 80, 97, 99,107, 97,103,101, 0, 4,
+ 0, 0, 0,155, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97,
+ 0, 0, 0, 0,114, 10, 1, 60,157, 15, 2, 11, 3, 2, 1, 1, 60,159, 4, 0,
+ 60,161, 15, 5, 13, 1, 11, 6, 11, 7, 60,169, 2, 2, 3, 23, 2, 23, 1, 60,
+170, 13, 2, 7, 0, 32, 56, 28, 60,172, 15, 9, 15, 10, 22, 2, 11, 0, 13, 0,
+ 11, 1, 13, 1, 30, 1, 2, 1, 1, 2, 1, 1, 60,173, 15, 11, 13, 3, 2, 0,
+ 1, 60,174, 13, 3, 20, 12, 2, 0, 1, 60,175, 13, 3, 20, 13, 13, 3, 18, 1,
+ 2, 0, 2, 60,176, 15, 14, 2, 0, 0, 60,177, 13, 3, 1, 4, 60,178, 0, 0,
+ 0, 0, 4, 0, 0, 0,155, 0, 0, 0, 5,110, 97,109,101, 0, 0, 0, 0,157,
+ 0, 0, 0, 5, 99,111,100,101, 0, 0, 0, 0,159, 0, 0, 0, 7,110,115,117,
+ 98,115,116, 0, 0, 0, 0,172, 0, 0, 0, 2,116, 0, 0, 0, 0, 15, 2, 0,
+ 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5, 99,111,100,101, 0, 2, 0,
+ 0, 0, 5,114,101, 97,100, 0, 2, 0, 0, 0, 3, 46, 42, 0, 2, 0, 0, 0,
+ 7,110,115,117, 98,115,116, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0,
+ 0, 0, 13, 37, 36, 60, 40, 46, 45, 41, 62, 37,115, 42, 10, 0, 4, 0, 0, 0,
+161, 0, 0, 0, 13, 64,112, 97, 99,107, 97,103,101, 46,108,117, 97, 0, 0, 0,
+ 0, 71, 6, 1, 60,162, 15, 3, 13, 0, 11, 4, 2, 2, 2, 60,163, 13, 1, 44,
+ 52, 20, 60,164, 15, 5, 11, 6, 13, 2, 42, 11, 7, 42, 13, 0, 42, 2, 0, 1,
+ 50, 2, 60,165, 60,166, 15, 9, 13, 1, 11, 10, 2, 1, 2, 60,167, 15, 11, 13,
+ 1, 2, 0, 1, 60,168, 13, 3, 1, 4, 60,169, 0, 0, 0, 0, 4, 0, 0, 0,
+161, 0, 0, 0, 3,102,110, 0, 0, 0, 0,162, 0, 0, 0, 3,102,112, 0, 0,
+ 0, 0,162, 0, 0, 0, 4,109,115,103, 0, 0, 0, 0,166, 0, 0, 0, 2,115,
+ 0, 0, 0, 0, 12, 2, 0, 0, 0, 3,102,110, 0, 2, 0, 0, 0, 3,102,112,
+ 0, 2, 0, 0, 0, 4,109,115,103, 0, 2, 0, 0, 0, 9,111,112,101,110,102,
+105,108,101, 0, 2, 0, 0, 0, 2,114, 0, 2, 0, 0, 0, 6,101,114,114,111,
+114, 0, 2, 0, 0, 0, 2, 35, 0, 2, 0, 0, 0, 3, 58, 32, 0, 2, 0, 0,
+ 0, 2,115, 0, 2, 0, 0, 0, 5,114,101, 97,100, 0, 2, 0, 0, 0, 3, 46,
+ 42, 0, 2, 0, 0, 0, 10, 99,108,111,115,101,102,105,108,101, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 9, 95, 80, 97, 99,107, 97,103,101, 0, 2, 0,
+ 0, 0, 11, 95, 67,111,110,116, 97,105,110,101,114, 0, 2, 0, 0, 0, 5,112,
+117,115,104, 0, 2, 0, 0, 0, 11,112,114,101,112,114,111, 99,101,115,115, 0,
+ 2, 0, 0, 0, 6,112, 97,114,115,101, 0, 2, 0, 0, 0, 4,112,111,112, 0,
+
+};
+
+/* module.lo */
+static char B6[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64,109,111,100,117,108,
+101, 46,108,117, 97, 0, 0, 0, 0, 75, 5, 0, 60, 19, 22, 2, 60, 20, 11, 1,
+ 15, 2, 11, 3, 60, 21, 11, 4, 30, 1, 60, 22, 25, 0, 60, 23, 15, 5, 15, 0,
+ 15, 6, 2, 0, 2, 60, 26, 15, 0, 11, 7, 11, 8, 26, 60, 36, 15, 0, 11, 9,
+ 11, 10, 26, 60, 41, 15, 0, 11, 11, 11, 12, 26, 60, 53, 11, 14, 25, 13, 60, 62,
+ 11, 16, 25, 15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 2, 0, 0, 0, 12, 99,108,
+ 97,115,115, 77,111,100,117,108,101, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101,
+ 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114,
+ 0, 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 7,109,111,100,117,
+108,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,
+116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,
+116,101,114, 0, 4, 0, 0, 0, 26, 0, 0, 0, 12, 64,109,111,100,117,108,101,
+ 46,108,117, 97, 0, 0, 0, 0, 60, 4, 1, 60, 27, 15, 0, 11, 1, 13, 0, 18,
+ 3, 42, 11, 4, 42, 2, 0, 1, 60, 28, 7, 1, 50, 23, 60, 30, 13, 0, 13, 1,
+ 16, 20, 6, 2, 0, 1, 60, 31, 13, 1, 7, 1, 37, 23, 1, 60, 32, 60, 29, 13,
+ 0, 13, 1, 16, 54, 32, 60, 33, 0, 0, 0, 0, 2, 0, 0, 0, 26, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 28, 0, 0, 0, 2,105, 0, 0, 0, 0, 7,
+ 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 16, 32,116,111,
+108,117, 97, 95,109,111,100,117,108,101, 40, 34, 0, 2, 0, 0, 0, 5,115,101,
+108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 4, 34, 41,
+ 59, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,
+101,114, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114, 0, 4,
+ 0, 0, 0, 36, 0, 0, 0, 12, 64,109,111,100,117,108,101, 46,108,117, 97, 0,
+ 0, 0, 0, 22, 4, 1, 60, 37, 15, 0, 11, 1, 13, 0, 18, 3, 42, 11, 4, 42,
+ 2, 0, 1, 60, 38, 0, 0, 0, 0, 1, 0, 0, 0, 36, 0, 0, 0, 5,115,101,
+108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2,
+ 0, 0, 0, 32, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40, 41, 59, 32,
+108,117, 97, 95,115,101,116,103,108,111, 98, 97,108, 40, 34, 0, 2, 0, 0, 0,
+ 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0,
+ 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0,
+ 41, 0, 0, 0, 12, 64,109,111,100,117,108,101, 46,108,117, 97, 0, 0, 0, 0,
+ 97, 8, 3, 60, 42, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 43, 15, 2, 13,
+ 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 44, 7, 1, 50,
+ 30, 60, 46, 13, 0, 13, 3, 16, 20, 2, 13, 1, 11, 9, 42, 11, 10, 2, 0, 3,
+ 60, 47, 13, 3, 7, 1, 37, 23, 3, 60, 48, 60, 45, 13, 0, 13, 3, 16, 54, 39,
+ 60, 49, 15, 2, 13, 1, 11, 11, 42, 13, 2, 42, 2, 0, 1, 60, 50, 0, 0, 0,
+ 0, 4, 0, 0, 0, 41, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 41, 0,
+ 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0, 41, 0, 0, 0, 6, 99,108,111,
+115,101, 0, 0, 0, 0, 44, 0, 0, 0, 2,105, 0, 0, 0, 0, 12, 2, 0, 0,
+ 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2,
+ 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 8, 77,111,100,117,108,
+101,123, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0,
+ 0, 0, 3, 39, 59, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 2, 32, 0,
+ 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 8, 95,
+ 77,111,100,117,108,101, 0, 4, 0, 0, 0, 53, 0, 0, 0, 12, 64,109,111,100,
+117,108,101, 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 54, 13, 0, 11, 1,
+ 15, 2, 26, 60, 55, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 56, 15, 5, 13, 0,
+ 2, 0, 1, 60, 57, 13, 0, 1, 1, 60, 58, 0, 0, 0, 0, 1, 0, 0, 0, 53,
+ 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0,
+ 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 12, 99,108, 97,115,115, 77,111,
+100,117,108,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0,
+ 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,
+101,110,100, 0, 2, 0, 0, 0, 7, 77,111,100,117,108,101, 0, 4, 0, 0, 0,
+ 62, 0, 0, 0, 12, 64,109,111,100,117,108,101, 46,108,117, 97, 0, 0, 0, 0,
+ 75, 10, 2, 60, 63, 15, 3, 15, 4, 22, 1, 11, 5, 13, 0, 30, 0, 2, 1, 1,
+ 2, 1, 1, 60, 64, 15, 6, 13, 2, 2, 0, 1, 60, 65, 13, 2, 20, 7, 15, 8,
+ 13, 1, 7, 2, 15, 9, 13, 1, 2, 1, 1, 7, 1, 38, 2, 1, 3, 2, 0, 2,
+ 60, 66, 15, 10, 2, 0, 0, 60, 67, 13, 2, 1, 3, 60, 68, 0, 0, 0, 0, 3,
+ 0, 0, 0, 62, 0, 0, 0, 2,110, 0, 0, 0, 0, 62, 0, 0, 0, 2, 98, 0,
+ 0, 0, 0, 63, 0, 0, 0, 2,116, 0, 0, 0, 0, 11, 2, 0, 0, 0, 2,110,
+ 0, 2, 0, 0, 0, 2, 98, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 8,
+ 95, 77,111,100,117,108,101, 0, 2, 0, 0, 0, 11, 95, 67,111,110,116, 97,105,
+110,101,114, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5,112,
+117,115,104, 0, 2, 0, 0, 0, 6,112, 97,114,115,101, 0, 2, 0, 0, 0, 7,
+115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7,115,116,114,108,101,110, 0, 2,
+ 0, 0, 0, 4,112,111,112, 0,
+};
+
+/* class.lo */
+static char B7[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 99,108, 97,115,115,
+ 46,108,117, 97, 0, 0, 0, 0, 96, 9, 0, 60, 20, 22, 4, 60, 21, 11, 1, 15,
+ 2, 11, 3, 60, 22, 11, 4, 11, 5, 60, 23, 11, 6, 11, 7, 60, 24, 11, 6, 30,
+ 3, 60, 25, 25, 0, 60, 26, 15, 8, 15, 0, 15, 9, 2, 0, 2, 60, 30, 15, 0,
+ 11, 10, 11, 11, 26, 60, 40, 15, 0, 11, 12, 11, 13, 26, 60, 45, 15, 0, 11, 14,
+ 11, 15, 26, 60, 57, 15, 0, 11, 16, 11, 17, 26, 60, 70, 11, 19, 25, 18, 60, 79,
+ 11, 21, 25, 20, 0, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 11, 99,108,
+ 97,115,115, 67,108, 97,115,115, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0,
+ 2, 0, 0, 0, 15, 99,108, 97,115,115, 67,111,110,116, 97,105,110,101,114, 0,
+ 2, 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 6, 99,108, 97,115,115,
+ 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0,
+ 0, 5, 98, 97,115,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2,
+ 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,
+101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 30, 0, 0, 0, 11, 64, 99,108,
+ 97,115,115, 46,108,117, 97, 0, 0, 0, 0, 68, 4, 1, 60, 31, 15, 0, 11, 1,
+ 13, 0, 18, 3, 42, 11, 4, 42, 13, 0, 18, 5, 42, 11, 6, 42, 2, 0, 1, 60,
+ 32, 7, 1, 50, 23, 60, 34, 13, 0, 13, 1, 16, 20, 8, 2, 0, 1, 60, 35, 13,
+ 1, 7, 1, 37, 23, 1, 60, 36, 60, 33, 13, 0, 13, 1, 16, 54, 32, 60, 37, 0,
+ 0, 0, 0, 2, 0, 0, 0, 30, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,
+ 32, 0, 0, 0, 2,105, 0, 0, 0, 0, 9, 2, 0, 0, 0, 7,111,117,116,112,
+117,116, 0, 2, 0, 0, 0, 16, 32,116,111,108,117, 97, 95, 99, 99,108, 97,115,
+115, 40, 34, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110,
+ 97,109,101, 0, 2, 0, 0, 0, 4, 34, 44, 34, 0, 2, 0, 0, 0, 5, 98, 97,
+115,101, 0, 2, 0, 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 9,114,101,103,105,115,116,101,114, 0, 2, 0, 0, 0, 11,117,110,
+114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 40, 0, 0, 0, 11, 64, 99,
+108, 97,115,115, 46,108,117, 97, 0, 0, 0, 0, 22, 4, 1, 60, 41, 15, 0, 11,
+ 1, 13, 0, 18, 3, 42, 11, 4, 42, 2, 0, 1, 60, 42, 0, 0, 0, 0, 1, 0,
+ 0, 0, 40, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 5, 2, 0, 0, 0,
+ 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,117,
+115,104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40, 34, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,
+110, 97,109,101, 0, 2, 0, 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 8,100,
+101, 99,108,116, 97,103, 0, 4, 0, 0, 0, 45, 0, 0, 0, 11, 64, 99,108, 97,
+115,115, 46,108,117, 97, 0, 0, 0, 0,128, 8, 1, 60, 46, 13, 0, 11, 1, 13,
+ 0, 11, 2, 15, 3, 13, 0, 18, 4, 2, 2, 1, 27, 1, 27, 2, 5, 4, 15, 5,
+ 13, 0, 18, 1, 13, 0, 18, 2, 2, 0, 2, 60, 47, 13, 0, 11, 6, 13, 0, 11,
+ 7, 15, 3, 13, 0, 18, 4, 11, 8, 2, 2, 2, 27, 1, 27, 2, 5, 4, 60, 48,
+ 15, 3, 13, 0, 18, 10, 2, 2, 1, 15, 5, 13, 1, 13, 2, 2, 0, 2, 60, 49,
+ 7, 1, 50, 23, 60, 51, 13, 0, 13, 3, 16, 20, 5, 2, 0, 1, 60, 52, 13, 3,
+ 7, 1, 37, 23, 3, 60, 53, 60, 50, 13, 0, 13, 3, 16, 54, 32, 60, 54, 0, 0,
+ 0, 0, 4, 0, 0, 0, 45, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 48,
+ 0, 0, 0, 5,116,121,112,101, 0, 0, 0, 0, 48, 0, 0, 0, 4,116, 97,103,
+ 0, 0, 0, 0, 49, 0, 0, 0, 2,105, 0, 0, 0, 0, 12, 2, 0, 0, 0, 5,
+115,101,108,102, 0, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0,
+ 4,116, 97,103, 0, 2, 0, 0, 0, 7,116, 97,103,118, 97,114, 0, 2, 0, 0,
+ 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103, 0,
+ 2, 0, 0, 0, 7, 99,105,116,121,112,101, 0, 2, 0, 0, 0, 5, 99,116, 97,
+103, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 5, 98, 97,115,101, 0, 2, 0, 0, 0, 2,105, 0,
+ 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 57, 0, 0, 0, 11,
+ 64, 99,108, 97,115,115, 46,108,117, 97, 0, 0, 0, 0,117, 8, 3, 60, 58, 15,
+ 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 59, 15, 2, 13, 1, 11, 4, 42, 13, 0,
+ 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 60, 15, 2, 13, 1, 11, 8, 42, 13, 0,
+ 18, 9, 42, 11, 10, 42, 2, 0, 1, 60, 61, 7, 1, 50, 30, 60, 63, 13, 0, 13,
+ 3, 16, 20, 2, 13, 1, 11, 12, 42, 11, 13, 2, 0, 3, 60, 64, 13, 3, 7, 1,
+ 37, 23, 3, 60, 65, 60, 62, 13, 0, 13, 3, 16, 54, 39, 60, 66, 15, 2, 13, 1,
+ 11, 14, 42, 13, 2, 42, 2, 0, 1, 60, 67, 0, 0, 0, 0, 4, 0, 0, 0, 57,
+ 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 57, 0, 0, 0, 6,105,100,101,
+110,116, 0, 0, 0, 0, 57, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0, 0,
+ 61, 0, 0, 0, 2,105, 0, 0, 0, 0, 15, 2, 0, 0, 0, 6,105,100,101,110,
+116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,
+105,110,116, 0, 2, 0, 0, 0, 7, 67,108, 97,115,115,123, 0, 2, 0, 0, 0,
+ 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2,
+ 0, 0, 0, 10, 32, 98, 97,115,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 3, 39, 59, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,125, 0,
+ 2, 0, 0, 0, 7, 95, 67,108, 97,115,115, 0, 4, 0, 0, 0, 70, 0, 0, 0,
+ 11, 64, 99,108, 97,115,115, 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 71,
+ 13, 0, 11, 1, 15, 2, 26, 60, 72, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 73,
+ 15, 5, 13, 0, 2, 0, 1, 60, 74, 13, 0, 1, 1, 60, 75, 0, 0, 0, 0, 1,
+ 0, 0, 0, 70, 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116,
+ 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 11, 99,108, 97,
+115,115, 67,108, 97,115,115, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0,
+ 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7,
+ 97,112,112,101,110,100, 0, 2, 0, 0, 0, 6, 67,108, 97,115,115, 0, 4, 0,
+ 0, 0, 79, 0, 0, 0, 11, 64, 99,108, 97,115,115, 46,108,117, 97, 0, 0, 0,
+ 0, 73, 11, 3, 60, 80, 15, 4, 15, 5, 22, 2, 11, 6, 13, 0, 11, 7, 13, 1,
+ 30, 1, 2, 1, 1, 2, 1, 1, 60, 81, 15, 8, 13, 3, 2, 0, 1, 60, 82, 13,
+ 3, 20, 9, 15, 10, 13, 2, 7, 2, 15, 11, 13, 2, 2, 1, 1, 7, 1, 38, 2,
+ 1, 3, 2, 0, 2, 60, 83, 15, 12, 2, 0, 0, 60, 84, 0, 0, 0, 0, 4, 0,
+ 0, 0, 79, 0, 0, 0, 2,110, 0, 0, 0, 0, 79, 0, 0, 0, 2,112, 0, 0,
+ 0, 0, 79, 0, 0, 0, 2, 98, 0, 0, 0, 0, 80, 0, 0, 0, 2, 99, 0, 0,
+ 0, 0, 13, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,112, 0, 2, 0, 0,
+ 0, 2, 98, 0, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0, 7, 95, 67,108, 97,
+115,115, 0, 2, 0, 0, 0, 11, 95, 67,111,110,116, 97,105,110,101,114, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 5, 98, 97,115,101, 0, 2,
+ 0, 0, 0, 5,112,117,115,104, 0, 2, 0, 0, 0, 6,112, 97,114,115,101, 0,
+ 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7,115,116,114,
+108,101,110, 0, 2, 0, 0, 0, 4,112,111,112, 0,
+};
+
+/* typedef.lo */
+static char B8[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 13, 64,116,121,112,101,100,
+101,102, 46,108,117, 97, 0, 0, 0, 0, 52, 7, 0, 60, 24, 22, 3, 60, 25, 11,
+ 1, 11, 2, 11, 3, 60, 26, 11, 2, 11, 4, 60, 27, 11, 2, 30, 2, 60, 28, 25,
+ 0, 60, 31, 15, 0, 11, 5, 11, 6, 26, 60, 40, 11, 8, 25, 7, 60, 49, 11, 10,
+ 25, 9, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 0, 0, 0, 13, 99,108, 97,115,
+115, 84,121,112,101,100,101,102, 0, 2, 0, 0, 0, 6,117,116,121,112,101, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0,
+ 31, 0, 0, 0, 13, 64,116,121,112,101,100,101,102, 46,108,117, 97, 0, 0, 0,
+ 0, 92, 6, 3, 60, 32, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 33, 15, 2,
+ 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 34, 15, 2,
+ 13, 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 35, 15, 2,
+ 13, 1, 11, 10, 42, 13, 0, 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 36, 15, 2,
+ 13, 1, 11, 12, 42, 13, 2, 42, 2, 0, 1, 60, 37, 0, 0, 0, 0, 3, 0, 0,
+ 0, 31, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 31, 0, 0, 0, 6,105,
+100,101,110,116, 0, 0, 0, 0, 31, 0, 0, 0, 6, 99,108,111,115,101, 0, 0,
+ 0, 0, 13, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,
+108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0,
+ 9, 84,121,112,101,100,101,102,123, 0, 2, 0, 0, 0, 11, 32,117,116,121,112,
+101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 6,117,116,121,112,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 9,
+ 32,109,111,100, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0,
+ 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 9, 95, 84,121,112,101,
+100,101,102, 0, 4, 0, 0, 0, 40, 0, 0, 0, 13, 64,116,121,112,101,100,101,
+102, 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 41, 13, 0, 11, 1, 15, 2,
+ 26, 60, 42, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 43, 15, 5, 13, 0, 2, 0,
+ 1, 60, 44, 13, 0, 1, 1, 60, 45, 0, 0, 0, 0, 1, 0, 0, 0, 40, 0, 0,
+ 0, 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,
+ 95, 98, 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 84,121,112,101,
+100,101,102, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0,
+ 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 14, 97,112,112,101,
+110,100,116,121,112,101,100,101,102, 0, 2, 0, 0, 0, 8, 84,121,112,101,100,
+101,102, 0, 4, 0, 0, 0, 49, 0, 0, 0, 13, 64,116,121,112,101,100,101,102,
+ 46,108,117, 97, 0, 0, 0, 0,109, 14, 1, 60, 50, 15, 1, 13, 0, 11, 2, 2,
+ 1, 2, 52, 11, 60, 51, 15, 3, 11, 4, 2, 0, 1, 50, 2, 60, 52, 60, 53, 15,
+ 6, 15, 7, 13, 0, 11, 8, 11, 9, 2, 1, 3, 11, 9, 2, 1, 2, 60, 54, 15,
+ 10, 22, 3, 60, 55, 11, 11, 13, 1, 13, 1, 18, 12, 16, 11, 13, 60, 56, 13, 1,
+ 13, 1, 18, 12, 7, 1, 38, 16, 11, 14, 60, 57, 15, 15, 13, 1, 7, 1, 13, 1,
+ 18, 12, 7, 2, 38, 2, 1, 3, 30, 2, 60, 58, 3, 2, 1, 60, 59, 0, 0, 0,
+ 0, 2, 0, 0, 0, 49, 0, 0, 0, 2,115, 0, 0, 0, 0, 53, 0, 0, 0, 2,
+116, 0, 0, 0, 0, 16, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 8,115,116,
+114,102,105,110,100, 0, 2, 0, 0, 0, 6, 91, 37, 42, 38, 93, 0, 2, 0, 0,
+ 0, 12,116,111,108,117, 97, 95,101,114,114,111,114, 0, 2, 0, 0, 0, 62, 35,
+105,110,118, 97,108,105,100, 32,116,121,112,101,100,101,102, 58, 32,112,111,105,
+110,116,101,114,115, 32, 40, 97,110,100, 32,114,101,102,101,114,101,110, 99,101,
+115, 41, 32, 97,114,101, 32,110,111,116, 32,115,117,112,112,111,114,116,101,100,
+ 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2,
+ 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 6, 37,115, 37,115, 42, 0,
+ 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 9, 95, 84,121,112,101,100,101,102,
+ 0, 2, 0, 0, 0, 6,117,116,121,112,101, 0, 2, 0, 0, 0, 2,110, 0, 2,
+ 0, 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0,
+ 0, 0, 7, 99,111,110, 99, 97,116, 0,
+};
+
+/* define.lo */
+static char B9[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64,100,101,102,105,110,
+101, 46,108,117, 97, 0, 0, 0, 0, 75, 5, 0, 60, 18, 22, 2, 60, 19, 11, 1,
+ 11, 2, 11, 3, 60, 20, 15, 4, 30, 1, 60, 21, 25, 0, 60, 22, 15, 5, 15, 0,
+ 15, 6, 2, 0, 2, 60, 25, 15, 0, 11, 7, 11, 8, 26, 60, 35, 15, 0, 11, 9,
+ 11, 10, 26, 60, 42, 15, 0, 11, 11, 11, 12, 26, 60, 51, 11, 14, 25, 13, 60, 65,
+ 11, 16, 25, 15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 2, 0, 0, 0, 12, 99,108,
+ 97,115,115, 68,101,102,105,110,101, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0,
+ 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0,
+ 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 0, 2, 0, 0, 0, 7,
+115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,
+103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,114, 0, 4, 0, 0, 0,
+ 25, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0, 0, 0, 0,
+ 80, 5, 1, 60, 26, 13, 0, 20, 2, 2, 1, 1, 60, 27, 13, 1, 52, 33, 60, 28,
+ 15, 3, 11, 4, 13, 1, 42, 11, 5, 42, 13, 0, 18, 6, 42, 11, 7, 42, 13, 0,
+ 18, 8, 42, 11, 9, 42, 2, 0, 1, 50, 27, 60, 30, 15, 3, 11, 10, 13, 0, 18,
+ 6, 42, 11, 7, 42, 13, 0, 18, 8, 42, 11, 9, 42, 2, 0, 1, 60, 31, 60, 32,
+ 0, 0, 0, 0, 2, 0, 0, 0, 25, 0, 0, 0, 5,115,101,108,102, 0, 0, 0,
+ 0, 26, 0, 0, 0, 2,112, 0, 0, 0, 0, 11, 2, 0, 0, 0, 2,112, 0, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,
+108,101, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18,
+ 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116, 40, 34, 0, 2, 0,
+ 0, 0, 4, 34, 44, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0,
+ 0, 0, 3, 34, 44, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0,
+ 3, 41, 59, 0, 2, 0, 0, 0, 23, 32,116,111,108,117, 97, 95, 99,111,110,115,
+116, 97,110,116, 40, 78, 85, 76, 76, 44, 34, 0, 2, 0, 0, 0, 11,117,110,114,
+101,103,105,115,116,101,114, 0, 4, 0, 0, 0, 35, 0, 0, 0, 12, 64,100,101,
+102,105,110,101, 46,108,117, 97, 0, 0, 0, 0, 38, 4, 1, 60, 36, 13, 0, 20,
+ 1, 2, 1, 1, 44, 52, 19, 60, 37, 15, 2, 11, 3, 13, 0, 18, 4, 42, 11, 5,
+ 42, 2, 0, 1, 50, 2, 60, 38, 60, 39, 0, 0, 0, 0, 1, 0, 0, 0, 35, 0,
+ 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 6, 2, 0, 0, 0, 5,115,101,108,
+102, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0,
+ 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,117,
+115,104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98,
+ 97,108, 40, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0,
+ 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0,
+ 42, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0, 0, 0, 0,
+ 72, 6, 3, 60, 43, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 44, 15, 2, 13,
+ 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 45, 15, 2, 13,
+ 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 46, 15, 2, 13,
+ 1, 11, 10, 42, 13, 2, 42, 2, 0, 1, 60, 47, 0, 0, 0, 0, 3, 0, 0, 0,
+ 42, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 42, 0, 0, 0, 6,105,100,
+101,110,116, 0, 0, 0, 0, 42, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0,
+ 0, 11, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,
+111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 8,
+ 68,101,102,105,110,101,123, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61,
+ 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110, 97,
+109,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 11, 32,108,110, 97,
+109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0,
+ 0, 0, 2,125, 0, 2, 0, 0, 0, 8, 95, 68,101,102,105,110,101, 0, 4, 0,
+ 0, 0, 51, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0, 0,
+ 0, 0, 64, 4, 1, 60, 52, 13, 0, 11, 1, 15, 2, 26, 60, 53, 15, 3, 13, 0,
+ 15, 4, 2, 0, 2, 60, 55, 13, 0, 18, 5, 11, 6, 32, 52, 11, 60, 56, 15, 7,
+ 11, 8, 2, 0, 1, 50, 2, 60, 57, 60, 59, 15, 9, 13, 0, 2, 0, 1, 60, 60,
+ 13, 0, 1, 1, 60, 61, 0, 0, 0, 0, 1, 0, 0, 0, 51, 0, 0, 0, 2,116,
+ 0, 0, 0, 0, 10, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,
+115,101, 0, 2, 0, 0, 0, 12, 99,108, 97,115,115, 68,101,102,105,110,101, 0,
+ 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,
+117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0,
+ 0, 1, 0, 2, 0, 0, 0, 6,101,114,114,111,114, 0, 2, 0, 0, 0, 16, 35,
+105,110,118, 97,108,105,100, 32,100,101,102,105,110,101, 0, 2, 0, 0, 0, 7,
+ 97,112,112,101,110,100, 0, 2, 0, 0, 0, 7, 68,101,102,105,110,101, 0, 4,
+ 0, 0, 0, 65, 0, 0, 0, 12, 64,100,101,102,105,110,101, 46,108,117, 97, 0,
+ 0, 0, 0, 54, 9, 1, 60, 66, 15, 2, 13, 0, 11, 3, 2, 1, 2, 60, 67, 15,
+ 4, 22, 2, 60, 68, 11, 5, 13, 1, 7, 1, 16, 11, 6, 60, 69, 13, 1, 7, 2,
+ 16, 46, 5, 13, 1, 7, 1, 16, 30, 1, 60, 70, 3, 2, 1, 60, 71, 0, 0, 0,
+ 0, 2, 0, 0, 0, 65, 0, 0, 0, 2,110, 0, 0, 0, 0, 66, 0, 0, 0, 2,
+116, 0, 0, 0, 0, 7, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,116, 0,
+ 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0, 2, 64, 0, 2, 0,
+ 0, 0, 8, 95, 68,101,102,105,110,101, 0, 2, 0, 0, 0, 5,110, 97,109,101,
+ 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0,
+};
+
+/* enumerate.lo */
+static char B10[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 15, 64,101,110,117,109,101,
+114, 97,116,101, 46,108,117, 97, 0, 0, 0, 0, 69, 3, 0, 60, 19, 22, 1, 60,
+ 20, 11, 1, 15, 2, 30, 0, 60, 21, 25, 0, 60, 22, 15, 3, 15, 0, 15, 4, 2,
+ 0, 2, 60, 25, 15, 0, 11, 5, 11, 6, 26, 60, 42, 15, 0, 11, 7, 11, 8, 26,
+ 60, 53, 15, 0, 11, 9, 11, 10, 26, 60, 64, 11, 12, 25, 11, 60, 73, 11, 14, 25,
+ 13, 0, 0, 0, 0, 0, 0, 0, 0, 15, 2, 0, 0, 0, 15, 99,108, 97,115,115,
+ 69,110,117,109,101,114, 97,116,101, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101,
+ 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,114, 0,
+ 4, 0, 0, 0, 25, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,
+108,117, 97, 0, 0, 0, 0,179, 7, 1, 60, 26, 13, 0, 20, 2, 2, 1, 1, 46,
+ 7, 13, 0, 20, 3, 2, 1, 1, 60, 27, 7, 1, 50,141, 60, 29, 13, 1, 52, 93,
+ 60, 30, 13, 0, 20, 2, 2, 1, 1, 52, 43, 60, 31, 15, 5, 11, 6, 13, 1, 42,
+ 11, 7, 42, 13, 0, 18, 8, 13, 2, 16, 42, 11, 9, 42, 13, 1, 42, 11, 10, 42,
+ 13, 0, 13, 2, 16, 42, 11, 11, 42, 2, 0, 1, 50, 37, 60, 33, 15, 5, 11, 6,
+ 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 13, 2, 16, 42, 11, 9, 42, 13, 0, 13,
+ 2, 16, 42, 11, 11, 42, 2, 0, 1, 60, 34, 50, 31, 60, 36, 15, 5, 11, 12, 13,
+ 0, 18, 8, 13, 2, 16, 42, 11, 9, 42, 13, 0, 13, 2, 16, 42, 11, 11, 42, 2,
+ 0, 1, 60, 37, 60, 38, 13, 2, 7, 1, 37, 23, 2, 60, 39, 60, 28, 13, 0, 13,
+ 2, 16, 54,150, 60, 40, 0, 0, 0, 0, 3, 0, 0, 0, 25, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0, 26, 0, 0, 0, 2,112, 0, 0, 0, 0, 27, 0, 0,
+ 0, 2,105, 0, 0, 0, 0, 13, 2, 0, 0, 0, 2,112, 0, 2, 0, 0, 0, 5,
+115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0,
+ 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 2,105, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18, 32,116,111,108,
+117, 97, 95, 99,111,110,115,116, 97,110,116, 40, 34, 0, 2, 0, 0, 0, 4, 34,
+ 44, 34, 0, 2, 0, 0, 0, 7,108,110, 97,109,101,115, 0, 2, 0, 0, 0, 3,
+ 34, 44, 0, 2, 0, 0, 0, 3, 58, 58, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2,
+ 0, 0, 0, 23, 32,116,111,108,117, 97, 95, 99,111,110,115,116, 97,110,116, 40,
+ 78, 85, 76, 76, 44, 34, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,
+101,114, 0, 4, 0, 0, 0, 42, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,
+116,101, 46,108,117, 97, 0, 0, 0, 0, 83, 6, 1, 60, 43, 13, 0, 20, 1, 2,
+ 1, 1, 4, 0, 32, 48, 10, 13, 0, 20, 2, 2, 1, 1, 4, 0, 32, 52, 50, 60,
+ 44, 7, 1, 50, 31, 60, 46, 15, 4, 11, 5, 13, 0, 18, 6, 13, 1, 16, 42, 11,
+ 7, 42, 2, 0, 1, 60, 47, 13, 1, 7, 1, 37, 23, 1, 60, 48, 60, 45, 13, 0,
+ 13, 1, 16, 54, 40, 5, 1, 50, 2, 60, 49, 60, 50, 0, 0, 0, 0, 3, 0, 0,
+ 0, 42, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 44, 0, 0, 0, 2,105,
+ 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0, 0, 5,115,101,
+108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0,
+ 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0,
+ 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,
+117,115,104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111,
+ 98, 97,108, 40, 34, 0, 2, 0, 0, 0, 7,108,110, 97,109,101,115, 0, 2, 0,
+ 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0,
+ 0, 0, 53, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,108,117,
+ 97, 0, 0, 0, 0, 90, 8, 3, 60, 54, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1,
+ 60, 55, 7, 1, 50, 43, 60, 57, 15, 2, 13, 1, 11, 6, 42, 13, 0, 13, 3, 16,
+ 42, 11, 7, 42, 13, 0, 18, 8, 13, 3, 16, 42, 11, 9, 42, 2, 0, 1, 60, 58,
+ 13, 3, 7, 1, 37, 23, 3, 60, 59, 60, 56, 13, 0, 13, 3, 16, 54, 52, 60, 60,
+ 15, 2, 13, 1, 11, 10, 42, 13, 2, 42, 2, 0, 1, 60, 61, 0, 0, 0, 0, 4,
+ 0, 0, 0, 53, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 53, 0, 0, 0,
+ 6,105,100,101,110,116, 0, 0, 0, 0, 53, 0, 0, 0, 6, 99,108,111,115,101,
+ 0, 0, 0, 0, 55, 0, 0, 0, 2,105, 0, 0, 0, 0, 11, 2, 0, 0, 0, 6,
+105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0,
+ 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 11, 69,110,117,109,101,114, 97,
+116,101,123, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 3, 32, 39, 0, 2, 0, 0, 0, 3, 39, 40, 0, 2, 0, 0,
+ 0, 7,108,110, 97,109,101,115, 0, 2, 0, 0, 0, 3, 41, 44, 0, 2, 0, 0,
+ 0, 2,125, 0, 2, 0, 0, 0, 11, 95, 69,110,117,109,101,114, 97,116,101, 0,
+ 4, 0, 0, 0, 64, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,
+108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60, 65, 13, 0, 11, 1, 15, 2, 26, 60,
+ 66, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 67, 15, 5, 13, 0, 2, 0, 1, 60,
+ 68, 13, 0, 1, 1, 60, 69, 0, 0, 0, 0, 1, 0, 0, 0, 64, 0, 0, 0, 2,
+116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 15, 99,108, 97,115,115, 69,110,117,109,101,114,
+ 97,116,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0,
+ 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,101,
+110,100, 0, 2, 0, 0, 0, 10, 69,110,117,109,101,114, 97,116,101, 0, 4, 0,
+ 0, 0, 73, 0, 0, 0, 15, 64,101,110,117,109,101,114, 97,116,101, 46,108,117,
+ 97, 0, 0, 0, 0,200, 9, 1, 60, 74, 15, 2, 15, 3, 13, 0, 7, 2, 9, 2,
+ 2, 1, 3, 11, 4, 2, 1, 2, 60, 75, 7, 1, 60, 76, 22, 1, 11, 7, 7, 0,
+ 30, 0, 50, 55, 60, 78, 15, 2, 13, 1, 13, 2, 16, 11, 9, 2, 1, 2, 60, 79,
+ 13, 3, 11, 7, 13, 3, 18, 7, 7, 1, 37, 26, 60, 80, 13, 3, 13, 3, 18, 7,
+ 13, 4, 7, 1, 16, 26, 60, 81, 13, 2, 7, 1, 37, 23, 2, 5, 1, 60, 82, 60,
+ 77, 13, 1, 13, 2, 16, 54, 64, 60, 84, 7, 1, 23, 2, 60, 85, 13, 3, 11, 10,
+ 22, 0, 26, 50, 60, 60, 87, 15, 2, 13, 3, 13, 2, 16, 11, 11, 2, 1, 2, 60,
+ 88, 13, 3, 13, 2, 13, 4, 7, 1, 16, 26, 60, 89, 13, 3, 18, 10, 13, 2, 13,
+ 4, 7, 2, 16, 46, 5, 13, 4, 7, 1, 16, 26, 60, 90, 13, 2, 7, 1, 37, 23,
+ 2, 5, 1, 60, 91, 60, 86, 13, 3, 13, 2, 16, 54, 69, 60, 92, 15, 12, 13, 3,
+ 3, 4, 1, 60, 93, 0, 0, 0, 0, 8, 0, 0, 0, 73, 0, 0, 0, 2, 98, 0,
+ 0, 0, 0, 74, 0, 0, 0, 2,116, 0, 0, 0, 0, 75, 0, 0, 0, 2,105, 0,
+ 0, 0, 0, 76, 0, 0, 0, 2,101, 0, 0, 0, 0, 78, 0, 0, 0, 3,116,116,
+ 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 2,116, 0, 0,
+ 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 13, 2, 0, 0, 0, 2, 98, 0, 2, 0,
+ 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0,
+ 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,
+105, 0, 2, 0, 0, 0, 2,101, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0,
+ 3,116,116, 0, 2, 0, 0, 0, 2, 61, 0, 2, 0, 0, 0, 7,108,110, 97,109,
+101,115, 0, 2, 0, 0, 0, 2, 64, 0, 2, 0, 0, 0, 11, 95, 69,110,117,109,
+101,114, 97,116,101, 0,
+};
+
+/* variable.lo */
+static char B11[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,118, 97,114,105, 97,
+ 98,108,101, 46,108,117, 97, 0, 0, 0, 0, 87, 3, 0, 60, 18, 22, 1, 60, 19,
+ 11, 1, 15, 2, 30, 0, 60, 20, 25, 0, 60, 22, 15, 3, 15, 0, 15, 4, 2, 0,
+ 2, 60, 25, 15, 0, 11, 5, 11, 6, 26, 60, 37, 15, 0, 11, 7, 11, 8, 26, 60,
+ 48, 15, 0, 11, 9, 11, 10, 26, 60,154, 15, 0, 11, 11, 11, 12, 26, 60,171, 15,
+ 0, 11, 13, 11, 14, 26, 60,179, 11, 16, 25, 15, 60,188, 11, 18, 25, 17, 0, 0,
+ 0, 0, 0, 0, 0, 0, 19, 2, 0, 0, 0, 14, 99,108, 97,115,115, 86, 97,114,
+105, 97, 98,108,101, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0,
+ 0, 17, 99,108, 97,115,115, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2,
+ 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,116, 97,103, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0,
+ 0, 25, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97, 0,
+ 0, 0, 0,152, 6, 3, 60, 26, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 27,
+ 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 28,
+ 15, 2, 13, 1, 11, 8, 42, 13, 0, 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 29,
+ 15, 2, 13, 1, 11, 10, 42, 13, 0, 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 30,
+ 15, 2, 13, 1, 11, 12, 42, 13, 0, 18, 13, 42, 11, 7, 42, 2, 0, 1, 60, 31,
+ 15, 2, 13, 1, 11, 14, 42, 13, 0, 18, 15, 42, 11, 7, 42, 2, 0, 1, 60, 32,
+ 15, 2, 13, 1, 11, 16, 42, 13, 0, 18, 17, 42, 11, 7, 42, 2, 0, 1, 60, 33,
+ 15, 2, 13, 1, 11, 18, 42, 13, 2, 42, 2, 0, 1, 60, 34, 0, 0, 0, 0, 3,
+ 0, 0, 0, 25, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 25, 0, 0, 0,
+ 6,105,100,101,110,116, 0, 0, 0, 0, 25, 0, 0, 0, 6, 99,108,111,115,101,
+ 0, 0, 0, 0, 19, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0,
+ 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0,
+ 0, 0, 10, 86, 97,114,105, 97, 98,108,101,123, 0, 2, 0, 0, 0, 10, 32,109,
+111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 10,
+ 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,
+112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 10, 32,100,101,102, 32, 32,
+ 61, 32, 39, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 10, 32,114,
+101,116, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0,
+ 0, 2,125, 0, 2, 0, 0, 0, 9,103,101,116,118, 97,108,117,101, 0, 4, 0,
+ 0, 0, 37, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97,
+ 0, 0, 0, 0, 60, 5, 3, 60, 38, 13, 1, 48, 2, 13, 2, 52, 16, 60, 39, 13,
+ 1, 11, 2, 42, 13, 0, 18, 4, 42, 1, 3, 50, 29, 60, 40, 13, 1, 52, 13, 60,
+ 41, 11, 5, 13, 0, 18, 4, 42, 1, 3, 50, 10, 60, 43, 13, 0, 18, 4, 1, 3,
+ 60, 44, 60, 45, 0, 0, 0, 0, 3, 0, 0, 0, 37, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 37, 0, 0, 0, 6, 99,108, 97,115,115, 0, 0, 0, 0, 37,
+ 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0, 6, 2, 0, 0, 0, 6,
+ 99,108, 97,115,115, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2, 0,
+ 0, 0, 3, 58, 58, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 5,110, 97,109,101, 0, 2, 0, 0, 0, 7,115,101,108,102, 45, 62, 0, 2, 0,
+ 0, 0, 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 48, 0, 0, 0, 14,
+ 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97, 0, 0, 0, 3,189, 17, 1,
+ 60, 49, 13, 0, 20, 2, 2, 1, 1, 60, 52, 13, 1, 52, 21, 60, 53, 15, 3, 11,
+ 4, 13, 0, 18, 5, 11, 6, 13, 1, 11, 7, 2, 0, 5, 50, 17, 60, 55, 15, 3,
+ 11, 4, 13, 0, 18, 5, 11, 7, 2, 0, 3, 60, 56, 60, 57, 13, 0, 11, 8, 13,
+ 0, 20, 9, 11, 10, 2, 1, 2, 26, 60, 58, 15, 3, 11, 11, 13, 0, 18, 8, 11,
+ 12, 2, 0, 3, 60, 59, 15, 3, 11, 13, 2, 0, 1, 60, 62, 15, 16, 13, 0, 18,
+ 17, 11, 18, 2, 3, 2, 60, 63, 13, 1, 48, 5, 13, 4, 4, 0, 32, 52, 39, 60,
+ 64, 15, 3, 11, 19, 13, 1, 11, 20, 11, 21, 2, 0, 4, 60, 65, 15, 3, 11, 22,
+ 13, 1, 11, 23, 2, 0, 3, 60, 66, 15, 3, 11, 24, 2, 0, 1, 50, 35, 60, 67,
+ 13, 4, 52, 27, 60, 68, 13, 0, 11, 17, 15, 16, 13, 0, 18, 17, 11, 25, 2, 3,
+ 2, 27, 2, 23, 3, 23, 3, 5, 2, 50, 2, 60, 69, 60, 73, 13, 1, 48, 5, 13,
+ 4, 4, 0, 32, 52, 19, 60, 74, 15, 3, 11, 26, 13, 0, 18, 5, 42, 11, 27, 42,
+ 2, 0, 1, 50, 2, 60, 75, 60, 78, 15, 29, 13, 0, 18, 30, 2, 1, 1, 60, 79,
+ 13, 5, 11, 31, 32, 52, 32, 60, 80, 15, 3, 11, 32, 13, 5, 42, 11, 33, 42, 13,
+ 0, 20, 34, 13, 1, 13, 4, 2, 1, 3, 42, 11, 35, 42, 2, 0, 1, 50,124, 60,
+ 81, 13, 5, 52, 32, 60, 82, 15, 3, 11, 32, 13, 5, 42, 11, 22, 42, 13, 0, 20,
+ 34, 13, 1, 13, 4, 2, 1, 3, 42, 11, 35, 42, 2, 0, 1, 50, 86, 60, 84, 13,
+ 0, 18, 36, 11, 37, 32, 46, 7, 13, 0, 18, 36, 11, 38, 32, 52, 32, 60, 85, 15,
+ 3, 11, 39, 13, 0, 20, 34, 13, 1, 13, 4, 2, 1, 3, 42, 11, 40, 42, 13, 0,
+ 18, 41, 11, 35, 2, 0, 3, 50, 32, 60, 87, 15, 3, 11, 42, 13, 0, 20, 34, 13,
+ 1, 13, 4, 2, 1, 3, 42, 11, 40, 42, 13, 0, 18, 41, 11, 35, 2, 0, 3, 60,
+ 88, 60, 89, 60, 90, 15, 3, 11, 43, 2, 0, 1, 60, 91, 15, 3, 11, 44, 2, 0,
+ 1, 60, 94, 15, 16, 13, 0, 18, 17, 11, 45, 2, 1, 2, 44, 51, 2, 0, 60, 95,
+ 13, 1, 52, 21, 60, 96, 15, 3, 11, 46, 13, 0, 18, 5, 11, 6, 13, 1, 11, 7,
+ 2, 0, 5, 50, 17, 60, 98, 15, 3, 11, 46, 13, 0, 18, 5, 11, 7, 2, 0, 3,
+ 60, 99, 60,100, 13, 0, 11, 47, 13, 0, 20, 9, 11, 48, 2, 1, 2, 26, 60,101,
+ 15, 3, 11, 11, 13, 0, 18, 47, 11, 12, 2, 0, 3, 60,102, 15, 3, 11, 13, 2,
+ 0, 1, 60,105, 7, 1, 60,106, 13, 1, 48, 5, 13, 4, 4, 0, 32, 52, 65, 60,
+107, 15, 3, 11, 19, 13, 1, 11, 20, 11, 21, 2, 0, 4, 60,108, 15, 3, 11, 22,
+ 13, 1, 11, 23, 2, 0, 3, 60,109, 15, 3, 11, 24, 2, 0, 1, 60,111, 15, 3,
+ 11, 26, 13, 0, 18, 5, 42, 11, 27, 42, 2, 0, 1, 60,112, 13, 6, 7, 1, 37,
+ 23, 6, 50, 44, 60,113, 13, 4, 52, 36, 60,114, 13, 0, 11, 17, 15, 16, 13, 0,
+ 18, 17, 11, 25, 2, 3, 2, 27, 2, 23, 3, 23, 3, 5, 2, 60,115, 13, 6, 7,
+ 1, 37, 23, 6, 50, 2, 60,116, 60,119, 15, 3, 11, 50, 13, 0, 20, 51, 13, 6,
+ 2, 1, 2, 42, 11, 52, 42, 2, 0, 1, 60,120, 15, 3, 11, 53, 2, 0, 1, 60,
+123, 11, 38, 60,124, 13, 0, 18, 36, 11, 38, 31, 52, 4, 11, 20, 23, 7, 60,125,
+ 15, 3, 11, 19, 2, 0, 1, 60,126, 13, 1, 48, 2, 13, 4, 52, 19, 60,127, 15,
+ 3, 13, 1, 11, 54, 42, 13, 0, 18, 5, 42, 2, 0, 1, 50, 35, 60,128, 13, 1,
+ 52, 16, 60,129, 15, 3, 11, 55, 13, 0, 18, 5, 42, 2, 0, 1, 50, 13, 60,131,
+ 15, 3, 13, 0, 18, 5, 2, 0, 1, 60,132, 60,133, 15, 29, 13, 0, 18, 30, 2,
+ 1, 1, 60,134, 15, 3, 11, 56, 2, 0, 1, 60,135, 13, 8, 44, 48, 5, 13, 7,
+ 11, 38, 32, 52, 7, 15, 3, 11, 20, 2, 0, 1, 60,136, 15, 3, 11, 57, 13, 0,
+ 18, 17, 13, 0, 18, 30, 2, 0, 3, 60,137, 13, 8, 44, 52, 11, 60,138, 15, 3,
+ 11, 20, 2, 0, 1, 50, 2, 60,139, 60,140, 15, 3, 11, 58, 2, 0, 1, 60,141,
+ 7, 0, 60,142, 13, 0, 18, 59, 11, 38, 31, 52, 6, 13, 0, 18, 59, 23, 9, 60,
+143, 13, 8, 52, 24, 60,144, 15, 3, 11, 60, 13, 8, 42, 11, 22, 13, 6, 11, 40,
+ 13, 9, 11, 61, 2, 0, 6, 50, 19, 60,146, 15, 3, 11, 62, 13, 6, 11, 40, 13,
+ 9, 11, 61, 2, 0, 5, 60,147, 60,148, 15, 3, 11, 43, 2, 0, 1, 60,149, 15,
+ 3, 11, 44, 2, 0, 1, 5, 4, 50, 2, 60,150, 60,152, 0, 0, 0, 0, 14, 0,
+ 0, 0, 48, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 49, 0, 0, 0, 6,
+ 99,108, 97,115,115, 0, 0, 0, 0, 62, 0, 0, 0, 2, 95, 0, 0, 0, 0, 62,
+ 0, 0, 0, 2, 95, 0, 0, 0, 0, 62, 0, 0, 0, 7,115,116, 97,116,105, 99,
+ 0, 0, 0, 0, 78, 0, 0, 0, 2,116, 0, 0, 0, 0,105, 0, 0, 0, 5,110,
+ 97,114,103, 0, 0, 0, 0,123, 0, 0, 0, 4,112,116,114, 0, 0, 0, 0,133,
+ 0, 0, 0, 2,116, 0, 0, 0, 0,141, 0, 0, 0, 4,100,101,102, 0, 0, 0,
+ 0,149, 0, 0, 0, 0, 0, 0, 0,149, 0, 0, 0, 0, 0, 0, 0,149, 0, 0,
+ 0, 0, 0, 0, 0,149, 0, 0, 0, 0, 0, 0, 0, 63, 2, 0, 0, 0, 6, 99,
+108, 97,115,115, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,
+105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0,
+ 2, 0, 0, 0, 17, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,105,111,110,
+ 58, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32,111,102,
+ 32, 99,108, 97,115,115, 32, 0, 2, 0, 0, 0, 4, 32, 42, 47, 0, 2, 0, 0,
+ 0, 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110,
+ 99,110, 97,109,101, 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 73, 95,103,101,
+116, 0, 2, 0, 0, 0, 12,115,116, 97,116,105, 99, 32,118,111,105,100, 0, 2,
+ 0, 0, 0, 7, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2, 0,
+ 0, 0, 2, 95, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2, 0, 0,
+ 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2,
+ 0, 0, 0, 13, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 0, 2, 0, 0,
+ 0, 2, 32, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0, 0, 8,115,101,108,102,
+ 32, 61, 32, 0, 2, 0, 0, 0, 2, 40, 0, 2, 0, 0, 0, 4, 42, 41, 32, 0,
+ 2, 0, 0, 0, 24,116,111,108,117, 97, 95,103,101,116,117,115,101,114,116,121,
+112,101, 40, 49, 44, 48, 41, 59, 0, 2, 0, 0, 0, 20, 94, 37,115, 42,115,116,
+ 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 0, 2, 0, 0, 0, 65, 32,
+ 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111,108,117, 97, 95,101,114,
+114,111,114, 40, 34,105,110,118, 97,108,105,100, 32, 39,115,101,108,102, 39, 32,
+105,110, 32, 97, 99, 99,101,115,115,105,110,103, 32,118, 97,114,105, 97, 98,108,
+101, 32, 39, 0, 2, 0, 0, 0, 5, 39, 34, 41, 59, 0, 2, 0, 0, 0, 2,116,
+ 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0,
+ 13, 32, 32,116,111,108,117, 97, 95,112,117,115,104, 0, 2, 0, 0, 0, 10, 40,
+ 40,100,111,117, 98,108,101, 41, 0, 2, 0, 0, 0, 9,103,101,116,118, 97,108,
+117,101, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 4,112,116,114, 0,
+ 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 30, 32, 32,
+116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40, 40,
+118,111,105,100, 42, 41, 38, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 4,
+116, 97,103, 0, 2, 0, 0, 0, 29, 32, 32,116,111,108,117, 97, 95,112,117,115,
+104,117,115,101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41, 0, 2, 0,
+ 0, 0, 2,125, 0, 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 6, 99,111,110,
+115,116, 0, 2, 0, 0, 0, 17, 47, 42, 32,115,101,116, 32,102,117,110, 99,116,
+105,111,110, 58, 0, 2, 0, 0, 0, 9, 99,115,101,116,110, 97,109,101, 0, 2,
+ 0, 0, 0, 11,116,111,108,117, 97, 73, 95,115,101,116, 0, 2, 0, 0, 0, 5,
+110, 97,114,103, 0, 2, 0, 0, 0, 8, 32, 32,105,102, 32, 40, 33, 0, 2, 0,
+ 0, 0, 13,111,117,116, 99,104,101, 99,107,116,121,112,101, 0, 2, 0, 0, 0,
+ 2, 41, 0, 2, 0, 0, 0, 58, 32, 32, 32,116,111,108,117, 97, 95,101,114,114,
+111,114, 40, 34, 35,118,105,110,118, 97,108,105,100, 32,116,121,112,101, 32,105,
+110, 32,118, 97,114,105, 97, 98,108,101, 32, 97,115,115,105,103,110,109,101,110,
+116, 46, 34, 41, 59, 0, 2, 0, 0, 0, 3, 58, 58, 0, 2, 0, 0, 0, 7,115,
+101,108,102, 45, 62, 0, 2, 0, 0, 0, 4, 32, 61, 32, 0, 2, 0, 0, 0, 3,
+ 40, 40, 0, 2, 0, 0, 0, 3, 41, 32, 0, 2, 0, 0, 0, 4,100,101,102, 0,
+ 2, 0, 0, 0, 10,116,111,108,117, 97, 95,103,101,116, 0, 2, 0, 0, 0, 4,
+ 41, 41, 59, 0, 2, 0, 0, 0, 19,116,111,108,117, 97, 95,103,101,116,117,115,
+101,114,116,121,112,101, 40, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,
+114, 0, 4, 0, 0, 0,154, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101,
+ 46,108,117, 97, 0, 0, 0, 0,185, 5, 1, 60,155, 13, 0, 20, 2, 2, 1, 1,
+ 46, 7, 13, 0, 20, 3, 2, 1, 1, 60,156, 13, 1, 52, 84, 60,157, 13, 0, 18,
+ 4, 52, 41, 60,158, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 42,
+ 11, 9, 42, 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2,
+ 0, 1, 50, 33, 60,160, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8,
+ 42, 11, 9, 42, 13, 0, 18, 10, 42, 11, 13, 42, 2, 0, 1, 60,161, 50, 72, 60,
+163, 13, 0, 18, 4, 52, 35, 60,164, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9,
+ 42, 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2, 0, 1,
+ 50, 27, 60,166, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9, 42, 13, 0, 18, 10,
+ 42, 11, 13, 42, 2, 0, 1, 60,167, 60,168, 60,169, 0, 0, 0, 0, 2, 0, 0,
+ 0,154, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,155, 0, 0, 0, 7,112,
+ 97,114,101,110,116, 0, 0, 0, 0, 15, 2, 0, 0, 0, 7,112, 97,114,101,110,
+116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,
+108, 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2,
+ 0, 0, 0, 9, 99,115,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 7,111,117,
+116,112,117,116, 0, 2, 0, 0, 0, 18, 32,116,111,108,117, 97, 95,116, 97, 98,
+108,101,118, 97,114, 40, 34, 0, 2, 0, 0, 0, 4, 34, 44, 34, 0, 2, 0, 0,
+ 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 3, 34, 44, 0, 2, 0, 0, 0,
+ 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0,
+ 0, 3, 41, 59, 0, 2, 0, 0, 0, 8, 44, 78, 85, 76, 76, 41, 59, 0, 2, 0,
+ 0, 0, 19, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108,118, 97,114, 40,
+ 34, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114, 0, 4, 0,
+ 0, 0,171, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97,
+ 0, 0, 0, 0, 52, 4, 1, 60,172, 13, 0, 20, 1, 2, 1, 1, 4, 0, 32, 48,
+ 10, 13, 0, 20, 2, 2, 1, 1, 4, 0, 32, 52, 19, 60,173, 15, 3, 11, 4, 13,
+ 0, 18, 5, 42, 11, 6, 42, 2, 0, 1, 50, 2, 60,174, 60,175, 0, 0, 0, 0,
+ 1, 0, 0, 0,171, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 7, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115,
+ 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 7,
+111,117,116,112,117,116, 0, 2, 0, 0, 0, 35, 32,108,117, 97, 95,112,117,115,
+104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,114, 97,119,115,101,116,103,108,
+111, 98, 97,108, 40, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0,
+ 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 10, 95, 86, 97,114,105, 97, 98,108,
+101, 0, 4, 0, 0, 0,179, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101,
+ 46,108,117, 97, 0, 0, 0, 0, 40, 4, 1, 60,180, 13, 0, 11, 1, 15, 2, 26,
+ 60,181, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60,182, 15, 5, 13, 0, 2, 0, 1,
+ 60,183, 13, 0, 1, 1, 60,184, 0, 0, 0, 0, 1, 0, 0, 0,179, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95,
+ 98, 97,115,101, 0, 2, 0, 0, 0, 14, 99,108, 97,115,115, 86, 97,114,105, 97,
+ 98,108,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0,
+ 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,101,
+110,100, 0, 2, 0, 0, 0, 9, 86, 97,114,105, 97, 98,108,101, 0, 4, 0, 0,
+ 0,188, 0, 0, 0, 14, 64,118, 97,114,105, 97, 98,108,101, 46,108,117, 97, 0,
+ 0, 0, 0, 21, 5, 1, 60,189, 15, 1, 15, 2, 13, 0, 11, 3, 2, 1, 2, 3,
+ 1, 1, 60,190, 0, 0, 0, 0, 1, 0, 0, 0,188, 0, 0, 0, 2,115, 0, 0,
+ 0, 0, 4, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 10, 95, 86, 97,114,105,
+ 97, 98,108,101, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,114, 97,116,105,111,
+110, 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+};
+
+/* array.lo */
+static char B12[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 97,114,114, 97,121,
+ 46,108,117, 97, 0, 0, 0, 0, 87, 3, 0, 60, 18, 22, 1, 60, 19, 11, 1, 15,
+ 2, 30, 0, 60, 20, 25, 0, 60, 22, 15, 3, 15, 0, 15, 4, 2, 0, 2, 60, 25,
+ 15, 0, 11, 5, 11, 6, 26, 60, 38, 15, 0, 11, 7, 11, 8, 26, 60, 49, 15, 0,
+ 11, 9, 11, 10, 26, 60,167, 15, 0, 11, 11, 11, 12, 26, 60,184, 15, 0, 11, 13,
+ 11, 14, 26, 60,192, 11, 16, 25, 15, 60,201, 11, 18, 25, 17, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 2, 0, 0, 0, 11, 99,108, 97,115,115, 65,114,114, 97,121, 0,
+ 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 17, 99,108, 97,115,
+115, 68,101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 7,115,101,
+116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0,
+ 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 25, 0, 0, 0, 11,
+ 64, 97,114,114, 97,121, 46,108,117, 97, 0, 0, 0, 0,172, 6, 3, 60, 26, 15,
+ 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 27, 15, 2, 13, 1, 11, 4, 42, 13, 0,
+ 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 28, 15, 2, 13, 1, 11, 8, 42, 13, 0,
+ 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 29, 15, 2, 13, 1, 11, 10, 42, 13, 0,
+ 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 30, 15, 2, 13, 1, 11, 12, 42, 13, 0,
+ 18, 13, 42, 11, 7, 42, 2, 0, 1, 60, 31, 15, 2, 13, 1, 11, 14, 42, 13, 0,
+ 18, 15, 42, 11, 7, 42, 2, 0, 1, 60, 32, 15, 2, 13, 1, 11, 16, 42, 13, 0,
+ 18, 17, 42, 11, 7, 42, 2, 0, 1, 60, 33, 15, 2, 13, 1, 11, 18, 42, 13, 0,
+ 18, 19, 42, 11, 7, 42, 2, 0, 1, 60, 34, 15, 2, 13, 1, 11, 20, 42, 13, 2,
+ 42, 2, 0, 1, 60, 35, 0, 0, 0, 0, 3, 0, 0, 0, 25, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0, 25, 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0,
+ 0, 25, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0, 0, 21, 2, 0, 0, 0,
+ 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0,
+ 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 7, 65,114,114, 97,121,123,
+ 0, 2, 0, 0, 0, 10, 32,109,111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0,
+ 5,115,101,108,102, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3,
+ 39, 44, 0, 2, 0, 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 5,116,121,112,101, 0, 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61,
+ 32, 39, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,
+109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0,
+ 0, 10, 32,100,101,102, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,100,101,102,
+ 0, 2, 0, 0, 0, 10, 32,100,105,109, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0,
+ 4,100,105,109, 0, 2, 0, 0, 0, 10, 32,114,101,116, 32, 32, 61, 32, 39, 0,
+ 2, 0, 0, 0, 4,114,101,116, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0,
+ 9,103,101,116,118, 97,108,117,101, 0, 4, 0, 0, 0, 38, 0, 0, 0, 11, 64,
+ 97,114,114, 97,121, 46,108,117, 97, 0, 0, 0, 0, 69, 5, 3, 60, 39, 13, 1,
+ 48, 2, 13, 2, 52, 19, 60, 40, 13, 1, 11, 2, 42, 13, 0, 18, 4, 42, 11, 5,
+ 42, 1, 3, 50, 35, 60, 41, 13, 1, 52, 16, 60, 42, 11, 6, 13, 0, 18, 4, 42,
+ 11, 5, 42, 1, 3, 50, 13, 60, 44, 13, 0, 18, 4, 11, 5, 42, 1, 3, 60, 45,
+ 60, 46, 0, 0, 0, 0, 3, 0, 0, 0, 38, 0, 0, 0, 5,115,101,108,102, 0,
+ 0, 0, 0, 38, 0, 0, 0, 6, 99,108, 97,115,115, 0, 0, 0, 0, 38, 0, 0,
+ 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0, 7, 2, 0, 0, 0, 6, 99,108,
+ 97,115,115, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2, 0, 0, 0,
+ 3, 58, 58, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,110,
+ 97,109,101, 0, 2, 0, 0, 0, 15, 91,116,111,108,117, 97, 73, 95,105,110,100,
+101,120, 93, 0, 2, 0, 0, 0, 7,115,101,108,102, 45, 62, 0, 2, 0, 0, 0,
+ 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 49, 0, 0, 0, 11, 64, 97,
+114,114, 97,121, 46,108,117, 97, 0, 0, 0, 4, 21, 17, 1, 60, 50, 13, 0, 20,
+ 2, 2, 1, 1, 60, 53, 13, 1, 52, 21, 60, 54, 15, 3, 11, 4, 13, 0, 18, 5,
+ 11, 6, 13, 1, 11, 7, 2, 0, 5, 50, 17, 60, 56, 15, 3, 11, 4, 13, 0, 18,
+ 5, 11, 7, 2, 0, 3, 60, 57, 60, 58, 13, 0, 11, 8, 13, 0, 20, 9, 11, 10,
+ 2, 1, 2, 26, 60, 59, 15, 3, 11, 11, 13, 0, 18, 8, 11, 12, 2, 0, 3, 60,
+ 60, 15, 3, 11, 13, 2, 0, 1, 60, 63, 15, 3, 11, 14, 2, 0, 1, 60, 66, 15,
+ 17, 13, 0, 18, 18, 11, 19, 2, 3, 2, 60, 67, 13, 1, 48, 5, 13, 4, 4, 0,
+ 32, 52, 66, 60, 68, 15, 3, 11, 20, 13, 1, 11, 21, 11, 22, 2, 0, 4, 60, 69,
+ 15, 3, 11, 23, 2, 0, 1, 60, 70, 15, 3, 11, 24, 2, 0, 1, 60, 71, 15, 3,
+ 11, 25, 2, 0, 1, 60, 72, 15, 3, 11, 26, 13, 1, 11, 27, 2, 0, 3, 60, 73,
+ 15, 3, 11, 28, 2, 0, 1, 50, 35, 60, 74, 13, 4, 52, 27, 60, 75, 13, 0, 11,
+ 18, 15, 17, 13, 0, 18, 18, 11, 29, 2, 3, 2, 27, 2, 23, 3, 23, 3, 5, 2,
+ 50, 2, 60, 76, 60, 79, 15, 3, 11, 30, 2, 0, 1, 60, 80, 15, 3, 11, 31, 2,
+ 0, 1, 60, 81, 15, 3, 11, 32, 2, 0, 1, 60, 82, 15, 3, 11, 33, 13, 0, 18,
+ 34, 42, 11, 35, 42, 2, 0, 1, 60, 83, 15, 3, 11, 36, 2, 0, 1, 60, 86, 15,
+ 38, 13, 0, 18, 39, 2, 1, 1, 60, 87, 13, 5, 11, 40, 32, 52, 32, 60, 88, 15,
+ 3, 11, 41, 13, 5, 42, 11, 42, 42, 13, 0, 20, 43, 13, 1, 13, 4, 2, 1, 3,
+ 42, 11, 44, 42, 2, 0, 1, 50,124, 60, 89, 13, 5, 52, 32, 60, 90, 15, 3, 11,
+ 41, 13, 5, 42, 11, 26, 42, 13, 0, 20, 43, 13, 1, 13, 4, 2, 1, 3, 42, 11,
+ 44, 42, 2, 0, 1, 50, 86, 60, 92, 13, 0, 18, 45, 11, 46, 32, 46, 7, 13, 0,
+ 18, 45, 11, 47, 32, 52, 32, 60, 93, 15, 3, 11, 48, 13, 0, 20, 43, 13, 1, 13,
+ 4, 2, 1, 3, 42, 11, 49, 42, 13, 0, 18, 50, 11, 44, 2, 0, 3, 50, 32, 60,
+ 95, 15, 3, 11, 51, 13, 0, 20, 43, 13, 1, 13, 4, 2, 1, 3, 42, 11, 49, 42,
+ 13, 0, 18, 50, 11, 44, 2, 0, 3, 60, 96, 60, 97, 60, 98, 15, 3, 11, 52, 2,
+ 0, 1, 60, 99, 15, 3, 11, 53, 2, 0, 1, 60,102, 15, 17, 13, 0, 18, 18, 11,
+ 54, 2, 1, 2, 44, 51, 2, 33, 60,103, 13, 1, 52, 21, 60,104, 15, 3, 11, 55,
+ 13, 0, 18, 5, 11, 6, 13, 1, 11, 7, 2, 0, 5, 50, 17, 60,106, 15, 3, 11,
+ 55, 13, 0, 18, 5, 11, 7, 2, 0, 3, 60,107, 60,108, 13, 0, 11, 56, 13, 0,
+ 20, 9, 11, 57, 2, 1, 2, 26, 60,109, 15, 3, 11, 11, 13, 0, 18, 56, 11, 12,
+ 2, 0, 3, 60,110, 15, 3, 11, 13, 2, 0, 1, 60,113, 15, 3, 11, 14, 2, 0,
+ 1, 60,116, 15, 17, 13, 0, 18, 18, 11, 19, 2, 3, 2, 60,117, 13, 1, 48, 5,
+ 13, 8, 4, 0, 32, 52, 66, 60,118, 15, 3, 11, 20, 13, 1, 11, 21, 11, 22, 2,
+ 0, 4, 60,119, 15, 3, 11, 23, 2, 0, 1, 60,120, 15, 3, 11, 24, 2, 0, 1,
+ 60,121, 15, 3, 11, 25, 2, 0, 1, 60,122, 15, 3, 11, 26, 13, 1, 11, 27, 2,
+ 0, 3, 60,123, 15, 3, 11, 28, 2, 0, 1, 50, 35, 60,124, 13, 8, 52, 27, 60,
+125, 13, 0, 11, 18, 15, 17, 13, 0, 18, 18, 11, 29, 2, 3, 2, 27, 2, 23, 7,
+ 23, 7, 5, 2, 50, 2, 60,126, 60,129, 15, 3, 11, 30, 2, 0, 1, 60,130, 15,
+ 3, 11, 31, 2, 0, 1, 60,131, 15, 3, 11, 32, 2, 0, 1, 60,132, 15, 3, 11,
+ 33, 13, 0, 18, 34, 42, 11, 35, 42, 2, 0, 1, 60,133, 15, 3, 11, 36, 2, 0,
+ 1, 60,136, 11, 47, 60,137, 13, 0, 18, 45, 11, 47, 31, 52, 4, 11, 21, 23, 9,
+ 60,138, 15, 3, 11, 20, 2, 0, 1, 60,139, 13, 1, 48, 2, 13, 8, 52, 22, 60,
+140, 15, 3, 13, 1, 11, 58, 42, 13, 0, 18, 5, 42, 11, 59, 42, 2, 0, 1, 50,
+ 41, 60,141, 13, 1, 52, 19, 60,142, 15, 3, 11, 60, 13, 0, 18, 5, 42, 11, 59,
+ 42, 2, 0, 1, 50, 16, 60,144, 15, 3, 13, 0, 18, 5, 11, 59, 42, 2, 0, 1,
+ 60,145, 60,146, 15, 38, 13, 0, 18, 39, 2, 1, 1, 60,147, 15, 3, 11, 61, 2,
+ 0, 1, 60,148, 13, 10, 44, 48, 5, 13, 9, 11, 47, 32, 52, 7, 15, 3, 11, 21,
+ 2, 0, 1, 60,149, 15, 3, 11, 62, 13, 0, 18, 18, 13, 0, 18, 39, 2, 0, 3,
+ 60,150, 13, 10, 44, 52, 11, 60,151, 15, 3, 11, 21, 2, 0, 1, 50, 2, 60,152,
+ 60,153, 15, 3, 11, 63, 2, 0, 1, 60,154, 7, 0, 60,155, 13, 0, 18, 64, 11,
+ 47, 31, 52, 6, 13, 0, 18, 64, 23, 11, 60,156, 13, 10, 52, 20, 60,157, 15, 3,
+ 11, 65, 13, 10, 42, 11, 66, 13, 11, 11, 67, 2, 0, 4, 50, 15, 60,159, 15, 3,
+ 11, 68, 13, 11, 11, 67, 2, 0, 3, 60,160, 60,161, 15, 3, 11, 52, 2, 0, 1,
+ 60,162, 15, 3, 11, 53, 2, 0, 1, 5, 6, 50, 2, 60,163, 60,165, 0, 0, 0,
+ 0, 18, 0, 0, 0, 49, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 50, 0,
+ 0, 0, 6, 99,108, 97,115,115, 0, 0, 0, 0, 66, 0, 0, 0, 2, 95, 0, 0,
+ 0, 0, 66, 0, 0, 0, 2, 95, 0, 0, 0, 0, 66, 0, 0, 0, 7,115,116, 97,
+116,105, 99, 0, 0, 0, 0, 86, 0, 0, 0, 2,116, 0, 0, 0, 0,116, 0, 0,
+ 0, 2, 95, 0, 0, 0, 0,116, 0, 0, 0, 2, 95, 0, 0, 0, 0,116, 0, 0,
+ 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0,136, 0, 0, 0, 4,112,116,114,
+ 0, 0, 0, 0,146, 0, 0, 0, 2,116, 0, 0, 0, 0,154, 0, 0, 0, 4,100,
+101,102, 0, 0, 0, 0,162, 0, 0, 0, 0, 0, 0, 0,162, 0, 0, 0, 0, 0,
+ 0, 0,162, 0, 0, 0, 0, 0, 0, 0,162, 0, 0, 0, 0, 0, 0, 0,162, 0,
+ 0, 0, 0, 0, 0, 0,162, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 6,
+ 99,108, 97,115,115, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116,
+ 0, 2, 0, 0, 0, 17, 47, 42, 32,103,101,116, 32,102,117,110, 99,116,105,111,
+110, 58, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32,111,
+102, 32, 99,108, 97,115,115, 32, 0, 2, 0, 0, 0, 4, 32, 42, 47, 0, 2, 0,
+ 0, 0, 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,
+110, 99,110, 97,109,101, 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 73, 95,103,
+101,116, 0, 2, 0, 0, 0, 12,115,116, 97,116,105, 99, 32,118,111,105,100, 0,
+ 2, 0, 0, 0, 7, 40,118,111,105,100, 41, 0, 2, 0, 0, 0, 2,123, 0, 2,
+ 0, 0, 0, 19, 32,105,110,116, 32,116,111,108,117, 97, 73, 95,105,110,100,101,
+120, 59, 0, 2, 0, 0, 0, 2, 95, 0, 2, 0, 0, 0, 7,115,116, 97,116,105,
+ 99, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,
+109,111,100, 0, 2, 0, 0, 0, 13, 94, 37,115, 42, 40,115,116, 97,116,105, 99,
+ 41, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0, 0, 0,
+ 6,115,101,108,102, 59, 0, 2, 0, 0, 0, 34, 32,108,117, 97, 95,112,117,115,
+104,111, 98,106,101, 99,116, 40,108,117, 97, 95,103,101,116,112, 97,114, 97,109,
+ 40, 49, 41, 41, 59, 0, 2, 0, 0, 0, 26, 32,108,117, 97, 95,112,117,115,104,
+115,116,114,105,110,103, 40, 34, 46,115,101,108,102, 34, 41, 59, 0, 2, 0, 0,
+ 0, 9, 32,115,101,108,102, 32, 61, 32, 0, 2, 0, 0, 0, 2, 40, 0, 2, 0,
+ 0, 0, 4, 42, 41, 32, 0, 2, 0, 0, 0, 36,108,117, 97, 95,103,101,116,117,
+115,101,114,100, 97,116, 97, 40,108,117, 97, 95,114, 97,119,103,101,116,116, 97,
+ 98,108,101, 40, 41, 41, 59, 0, 2, 0, 0, 0, 20, 94, 37,115, 42,115,116, 97,
+116,105, 99, 37,115, 37,115, 42, 40, 46, 42, 41, 0, 2, 0, 0, 0, 42, 32,105,
+102, 32, 40, 33,116,111,108,117, 97, 95,105,115,116,121,112,101, 40, 50, 44,116,
+111,108,117, 97, 95,116, 97,103, 95,110,117,109, 98,101,114, 44, 48, 41, 41, 0,
+ 2, 0, 0, 0, 50, 32, 32,116,111,108,117, 97, 95,101,114,114,111,114, 40, 34,
+105,110,118, 97,108,105,100, 32,116,121,112,101, 32,105,110, 32, 97,114,114, 97,
+121, 32,105,110,100,101,120,105,110,103, 46, 34, 41, 59, 0, 2, 0, 0, 0, 45,
+ 32,116,111,108,117, 97, 73, 95,105,110,100,101,120, 32, 61, 32, 40,105,110,116,
+ 41,116,111,108,117, 97, 95,103,101,116,110,117,109, 98,101,114, 40, 50, 44, 48,
+ 41, 45, 49, 59, 0, 2, 0, 0, 0, 38, 32,105,102, 32, 40,116,111,108,117, 97,
+ 73, 95,105,110,100,101,120, 60, 48, 32,124,124, 32,116,111,108,117, 97, 73, 95,
+105,110,100,101,120, 62, 61, 0, 2, 0, 0, 0, 4,100,105,109, 0, 2, 0, 0,
+ 0, 2, 41, 0, 2, 0, 0, 0, 47, 32, 32,116,111,108,117, 97, 95,101,114,114,
+111,114, 40, 34, 97,114,114, 97,121, 32,105,110,100,101,120,105,110,103, 32,111,
+117,116, 32,111,102, 32,114, 97,110,103,101, 46, 34, 41, 59, 0, 2, 0, 0, 0,
+ 2,116, 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0,
+ 5,116,121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0,
+ 0, 0, 13, 32, 32,116,111,108,117, 97, 95,112,117,115,104, 0, 2, 0, 0, 0,
+ 10, 40, 40,100,111,117, 98,108,101, 41, 0, 2, 0, 0, 0, 9,103,101,116,118,
+ 97,108,117,101, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 4,112,116,
+114, 0, 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 30,
+ 32, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101,
+ 40, 40,118,111,105,100, 42, 41, 38, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0,
+ 0, 4,116, 97,103, 0, 2, 0, 0, 0, 29, 32, 32,116,111,108,117, 97, 95,112,
+117,115,104,117,115,101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41, 0,
+ 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 6, 99,
+111,110,115,116, 0, 2, 0, 0, 0, 17, 47, 42, 32,115,101,116, 32,102,117,110,
+ 99,116,105,111,110, 58, 0, 2, 0, 0, 0, 9, 99,115,101,116,110, 97,109,101,
+ 0, 2, 0, 0, 0, 11,116,111,108,117, 97, 73, 95,115,101,116, 0, 2, 0, 0,
+ 0, 3, 58, 58, 0, 2, 0, 0, 0, 15, 91,116,111,108,117, 97, 73, 95,105,110,
+100,101,120, 93, 0, 2, 0, 0, 0, 7,115,101,108,102, 45, 62, 0, 2, 0, 0,
+ 0, 4, 32, 61, 32, 0, 2, 0, 0, 0, 3, 40, 40, 0, 2, 0, 0, 0, 3, 41,
+ 32, 0, 2, 0, 0, 0, 4,100,101,102, 0, 2, 0, 0, 0, 10,116,111,108,117,
+ 97, 95,103,101,116, 0, 2, 0, 0, 0, 4, 40, 51, 44, 0, 2, 0, 0, 0, 4,
+ 41, 41, 59, 0, 2, 0, 0, 0, 21,116,111,108,117, 97, 95,103,101,116,117,115,
+101,114,116,121,112,101, 40, 51, 44, 0, 2, 0, 0, 0, 9,114,101,103,105,115,
+116,101,114, 0, 4, 0, 0, 0,167, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,
+108,117, 97, 0, 0, 0, 0,185, 5, 1, 60,168, 13, 0, 20, 2, 2, 1, 1, 46,
+ 7, 13, 0, 20, 3, 2, 1, 1, 60,169, 13, 1, 52, 84, 60,170, 13, 0, 18, 4,
+ 52, 41, 60,171, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 42, 11,
+ 9, 42, 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2, 0,
+ 1, 50, 33, 60,173, 15, 5, 11, 6, 13, 1, 42, 11, 7, 42, 13, 0, 18, 8, 42,
+ 11, 9, 42, 13, 0, 18, 10, 42, 11, 13, 42, 2, 0, 1, 60,174, 50, 72, 60,176,
+ 13, 0, 18, 4, 52, 35, 60,177, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9, 42,
+ 13, 0, 18, 10, 42, 11, 11, 42, 13, 0, 18, 4, 42, 11, 12, 42, 2, 0, 1, 50,
+ 27, 60,179, 15, 5, 11, 14, 13, 0, 18, 8, 42, 11, 9, 42, 13, 0, 18, 10, 42,
+ 11, 13, 42, 2, 0, 1, 60,180, 60,181, 60,182, 0, 0, 0, 0, 2, 0, 0, 0,
+167, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,168, 0, 0, 0, 7,112, 97,
+114,101,110,116, 0, 0, 0, 0, 15, 2, 0, 0, 0, 7,112, 97,114,101,110,116,
+ 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108,
+ 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0,
+ 0, 0, 9, 99,115,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 7,111,117,116,
+112,117,116, 0, 2, 0, 0, 0, 20, 32,116,111,108,117, 97, 95,116, 97, 98,108,
+101, 97,114,114, 97,121, 40, 34, 0, 2, 0, 0, 0, 4, 34, 44, 34, 0, 2, 0,
+ 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 3, 34, 44, 0, 2, 0, 0,
+ 0, 9, 99,103,101,116,110, 97,109,101, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0,
+ 0, 0, 3, 41, 59, 0, 2, 0, 0, 0, 8, 44, 78, 85, 76, 76, 41, 59, 0, 2,
+ 0, 0, 0, 21, 32,116,111,108,117, 97, 95,103,108,111, 98, 97,108, 97,114,114,
+ 97,121, 40, 34, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,115,116,101,114,
+ 0, 4, 0, 0, 0,184, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,108,117, 97,
+ 0, 0, 0, 0, 52, 4, 1, 60,185, 13, 0, 20, 1, 2, 1, 1, 4, 0, 32, 48,
+ 10, 13, 0, 20, 2, 2, 1, 1, 4, 0, 32, 52, 19, 60,186, 15, 3, 11, 4, 13,
+ 0, 18, 5, 42, 11, 6, 42, 2, 0, 1, 50, 2, 60,187, 60,188, 0, 0, 0, 0,
+ 1, 0, 0, 0,184, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 7, 2, 0,
+ 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115,
+ 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,101, 0, 2, 0, 0, 0, 7,
+111,117,116,112,117,116, 0, 2, 0, 0, 0, 32, 32,108,117, 97, 95,112,117,115,
+104,110,105,108, 40, 41, 59, 32,108,117, 97, 95,115,101,116,103,108,111, 98, 97,
+108, 40, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 4,
+ 34, 41, 59, 0, 2, 0, 0, 0, 7, 95, 65,114,114, 97,121, 0, 4, 0, 0, 0,
+192, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,108,117, 97, 0, 0, 0, 0, 40,
+ 4, 1, 60,193, 13, 0, 11, 1, 15, 2, 26, 60,194, 15, 3, 13, 0, 15, 4, 2,
+ 0, 2, 60,195, 15, 5, 13, 0, 2, 0, 1, 60,196, 13, 0, 1, 1, 60,197, 0,
+ 0, 0, 0, 1, 0, 0, 0,192, 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2, 0,
+ 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0,
+ 11, 99,108, 97,115,115, 65,114,114, 97,121, 0, 2, 0, 0, 0, 7,115,101,116,
+116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2,
+ 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0, 6, 65,114,114, 97,
+121, 0, 4, 0, 0, 0,201, 0, 0, 0, 11, 64, 97,114,114, 97,121, 46,108,117,
+ 97, 0, 0, 0, 0, 21, 5, 1, 60,202, 15, 1, 15, 2, 13, 0, 11, 3, 2, 1,
+ 2, 3, 1, 1, 60,203, 0, 0, 0, 0, 1, 0, 0, 0,201, 0, 0, 0, 2,115,
+ 0, 0, 0, 0, 4, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 7, 95, 65,114,
+114, 97,121, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,114, 97,116,105,111,110,
+ 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+};
+
+/* function.lo */
+static char B13[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,102,117,110, 99,116,
+105,111,110, 46,108,117, 97, 0, 0, 0, 0,141, 15, 0, 60, 24, 22, 7, 60, 25,
+ 11, 1, 11, 2, 11, 3, 60, 26, 11, 2, 11, 4, 60, 27, 11, 2, 11, 5, 60, 28,
+ 11, 2, 11, 6, 60, 29, 22, 1, 11, 7, 7, 0, 30, 0, 11, 8, 60, 30, 11, 2,
+ 11, 9, 60, 31, 15, 10, 30, 6, 60, 32, 25, 0, 60, 33, 15, 11, 15, 0, 15, 12,
+ 2, 0, 2, 60, 36, 15, 0, 11, 13, 11, 14, 26, 60, 51, 15, 0, 11, 15, 11, 16,
+ 26, 60,232, 15, 0, 11, 17, 11, 18, 26, 60,242, 15, 0, 11, 19, 11, 20, 26, 60,
+250, 15, 0, 11, 21, 11, 22, 26, 59, 1, 14, 15, 0, 11, 23, 11, 24, 26, 59, 1,
+ 21, 11, 26, 25, 25, 59, 1, 49, 11, 28, 25, 27, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 2, 0, 0, 0, 14, 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 0,
+ 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 5,
+116,121,112,101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 5,110,
+ 97,109,101, 0, 2, 0, 0, 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 2,110,
+ 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,
+115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,101,
+ 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,
+108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 8,100,101, 99,108,116, 97,103,
+ 0, 4, 0, 0, 0, 36, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 0,141, 10, 1, 60, 37, 13, 0, 20, 1, 2, 1, 1, 48,
+ 7, 13, 0, 18, 2, 11, 2, 32, 52, 21, 60, 38, 15, 3, 13, 0, 18, 4, 18, 5,
+ 13, 0, 18, 4, 18, 6, 2, 0, 2, 50, 2, 60, 39, 60, 40, 13, 0, 11, 7, 13,
+ 0, 11, 8, 15, 9, 13, 0, 18, 10, 15, 11, 13, 0, 18, 12, 11, 2, 2, 1, 2,
+ 2, 2, 2, 27, 1, 27, 2, 5, 4, 60, 41, 15, 3, 13, 0, 18, 7, 13, 0, 18,
+ 8, 2, 0, 2, 60, 42, 7, 1, 50, 25, 60, 44, 13, 0, 18, 14, 13, 1, 16, 20,
+ 3, 2, 0, 1, 60, 45, 13, 1, 7, 1, 37, 23, 1, 60, 46, 60, 43, 13, 0, 18,
+ 14, 13, 1, 16, 54, 36, 60, 47, 0, 0, 0, 0, 2, 0, 0, 0, 36, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 42, 0, 0, 0, 2,105, 0, 0, 0, 0, 15,
+ 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,
+115,115, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 8,100,
+101, 99,108,116, 97,103, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2,
+ 0, 0, 0, 7, 99,105,116,121,112,101, 0, 2, 0, 0, 0, 5, 99,116, 97,103,
+ 0, 2, 0, 0, 0, 6,105,116,121,112,101, 0, 2, 0, 0, 0, 4,116, 97,103,
+ 0, 2, 0, 0, 0, 7,116, 97,103,118, 97,114, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0,
+ 4,109,111,100, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5, 97,114,103,
+115, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0, 4, 0, 0, 0, 51,
+ 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,108,117, 97, 0, 0, 0,
+ 6, 51, 15, 1, 60, 52, 13, 0, 20, 2, 2, 1, 1, 60, 53, 15, 5, 13, 0, 18,
+ 6, 11, 7, 2, 3, 2, 60, 55, 13, 1, 52, 21, 60, 56, 15, 8, 11, 9, 13, 0,
+ 18, 10, 11, 11, 13, 1, 11, 12, 2, 0, 5, 50, 17, 60, 58, 15, 8, 11, 13, 13,
+ 0, 18, 10, 11, 12, 2, 0, 3, 60, 59, 60, 60, 15, 8, 11, 14, 13, 0, 18, 15,
+ 11, 16, 2, 0, 3, 60, 61, 15, 8, 11, 17, 2, 0, 1, 60, 64, 15, 8, 11, 18,
+ 2, 0, 1, 60, 66, 4, 0, 60, 67, 13, 1, 52, 6, 7, 2, 23, 5, 50, 4, 7,
+ 1, 23, 5, 60, 68, 13, 1, 48, 7, 13, 0, 18, 10, 11, 20, 31, 48, 5, 13, 4,
+ 4, 0, 32, 52, 51, 60, 69, 13, 0, 18, 21, 11, 21, 32, 52, 19, 60, 70, 15, 8,
+ 11, 22, 13, 0, 18, 23, 18, 24, 11, 25, 2, 0, 3, 50, 19, 60, 72, 15, 8, 11,
+ 22, 13, 0, 18, 23, 18, 26, 11, 25, 2, 0, 3, 60, 73, 50, 2, 60, 74, 60, 76,
+ 13, 0, 18, 27, 7, 1, 16, 18, 28, 11, 29, 31, 52, 93, 60, 77, 7, 1, 50, 72,
+ 60, 79, 15, 31, 13, 0, 18, 27, 13, 6, 16, 18, 28, 2, 1, 1, 11, 32, 31, 52,
+ 29, 60, 80, 15, 8, 11, 33, 13, 0, 18, 27, 13, 6, 16, 20, 34, 13, 5, 2, 1,
+ 2, 42, 11, 35, 42, 2, 0, 1, 50, 2, 60, 81, 60, 82, 13, 5, 7, 1, 37, 23,
+ 5, 60, 83, 13, 6, 7, 1, 37, 23, 6, 60, 84, 60, 78, 13, 0, 18, 27, 13, 6,
+ 16, 54, 83, 5, 1, 50, 2, 60, 85, 60, 87, 15, 8, 11, 36, 13, 5, 42, 11, 37,
+ 42, 2, 0, 1, 60, 89, 15, 8, 11, 38, 2, 0, 1, 60, 92, 4, 0, 60, 93, 13,
+ 1, 52, 6, 7, 2, 23, 6, 50, 4, 7, 1, 23, 6, 60, 94, 13, 1, 48, 7, 13,
+ 0, 18, 10, 11, 20, 31, 48, 5, 13, 4, 4, 0, 32, 52, 47, 60, 95, 15, 8, 11,
+ 39, 13, 0, 18, 21, 13, 1, 11, 40, 11, 41, 2, 0, 5, 60, 96, 15, 8, 11, 42,
+ 13, 0, 18, 21, 13, 1, 11, 43, 2, 0, 4, 60, 97, 15, 8, 11, 44, 2, 0, 1,
+ 50, 35, 60, 98, 13, 4, 52, 27, 60, 99, 13, 0, 11, 6, 15, 5, 13, 0, 18, 6,
+ 11, 45, 2, 3, 2, 27, 2, 23, 3, 23, 3, 5, 2, 50, 2, 60,100, 60,102, 13,
+ 0, 18, 27, 7, 1, 16, 18, 28, 11, 29, 31, 52, 57, 60,103, 7, 1, 50, 36, 60,
+105, 13, 0, 18, 27, 13, 7, 16, 20, 46, 13, 6, 2, 0, 2, 60,106, 13, 6, 7,
+ 1, 37, 23, 6, 60,107, 13, 7, 7, 1, 37, 23, 7, 60,108, 60,104, 13, 0, 18,
+ 27, 13, 7, 16, 54, 47, 5, 1, 50, 2, 60,109, 60,112, 13, 1, 48, 7, 13, 0,
+ 18, 10, 11, 20, 31, 48, 5, 13, 4, 4, 0, 32, 52, 19, 60,113, 15, 8, 11, 47,
+ 13, 0, 18, 10, 42, 11, 48, 42, 2, 0, 1, 50, 2, 60,114, 60,117, 13, 1, 52,
+ 6, 7, 2, 23, 6, 50, 4, 7, 1, 23, 6, 60,118, 13, 0, 18, 27, 7, 1, 16,
+ 18, 28, 11, 29, 31, 52, 57, 60,119, 7, 1, 50, 36, 60,121, 13, 0, 18, 27, 13,
+ 7, 16, 20, 49, 13, 6, 2, 0, 2, 60,122, 13, 6, 7, 1, 37, 23, 6, 60,123,
+ 13, 7, 7, 1, 37, 23, 7, 60,124, 60,120, 13, 0, 18, 27, 13, 7, 16, 54, 47,
+ 5, 1, 50, 2, 60,125, 60,128, 13, 1, 48, 7, 13, 0, 18, 10, 11, 50, 32, 52,
+ 12, 60,129, 15, 8, 11, 51, 2, 0, 1, 49, 2,252, 60,130, 13, 1, 48, 7, 13,
+ 0, 18, 10, 11, 52, 32, 52, 34, 60,131, 15, 8, 11, 53, 13, 0, 18, 27, 7, 1,
+ 16, 18, 10, 11, 54, 13, 0, 18, 27, 7, 2, 16, 18, 10, 11, 55, 2, 0, 5, 49,
+ 2,203, 60,133, 15, 8, 11, 56, 2, 0, 1, 60,134, 13, 0, 18, 28, 11, 57, 31,
+ 48, 7, 13, 0, 18, 28, 11, 29, 31, 52, 48, 60,135, 15, 8, 11, 58, 13, 0, 18,
+ 6, 13, 0, 18, 28, 13, 0, 18, 59, 11, 60, 2, 0, 5, 60,136, 15, 8, 11, 42,
+ 13, 0, 18, 6, 13, 0, 18, 28, 13, 0, 18, 59, 11, 61, 2, 0, 5, 50, 11, 60,
+138, 15, 8, 11, 58, 2, 0, 1, 60,139, 60,140, 13, 1, 48, 7, 13, 0, 18, 10,
+ 11, 20, 32, 52, 15, 60,141, 15, 8, 11, 20, 13, 1, 11, 42, 2, 0, 3, 50, 70,
+ 60,142, 13, 1, 48, 2, 13, 4, 52, 21, 60,143, 15, 8, 13, 1, 11, 62, 42, 13,
+ 0, 18, 10, 42, 11, 42, 2, 0, 2, 50, 39, 60,144, 13, 1, 52, 18, 60,145, 15,
+ 8, 11, 63, 13, 0, 18, 10, 42, 11, 42, 2, 0, 2, 50, 15, 60,147, 15, 8, 13,
+ 0, 18, 10, 11, 42, 2, 0, 2, 60,148, 60,151, 7, 1, 50, 49, 60,153, 13, 0,
+ 18, 27, 13, 7, 16, 20, 64, 2, 0, 1, 60,154, 13, 7, 7, 1, 37, 23, 7, 60,
+155, 13, 0, 18, 27, 13, 7, 16, 52, 11, 60,156, 15, 8, 11, 65, 2, 0, 1, 50,
+ 2, 60,157, 60,158, 60,152, 13, 0, 18, 27, 13, 7, 16, 54, 60, 60,160, 15, 8,
+ 11, 66, 2, 0, 1, 60,163, 13, 0, 18, 28, 11, 57, 31, 48, 7, 13, 0, 18, 28,
+ 11, 29, 31, 52,220, 60,164, 15, 31, 13, 0, 18, 28, 2, 1, 1, 60,165, 13, 8,
+ 11, 68, 32, 52, 17, 60,166, 15, 8, 11, 69, 13, 8, 42, 11, 70, 42, 2, 0, 1,
+ 50,179, 60,167, 13, 8, 52, 17, 60,168, 15, 8, 11, 69, 13, 8, 42, 11, 71, 42,
+ 2, 0, 1, 50,156, 60,170, 13, 0, 18, 59, 11, 57, 32, 52, 98, 60,171, 15, 8,
+ 11, 72, 2, 0, 1, 60,172, 15, 8, 11, 73, 2, 0, 1, 60,173, 15, 8, 11, 74,
+ 13, 0, 18, 28, 11, 71, 2, 0, 3, 60,174, 15, 8, 11, 75, 2, 0, 1, 60,175,
+ 15, 8, 11, 76, 13, 0, 18, 28, 11, 77, 2, 0, 3, 60,176, 15, 8, 11, 78, 2,
+ 0, 1, 60,177, 15, 8, 11, 79, 13, 0, 18, 26, 11, 80, 13, 0, 18, 26, 11, 66,
+ 2, 0, 5, 60,178, 15, 8, 11, 81, 2, 0, 1, 50, 45, 60,180, 13, 0, 18, 59,
+ 11, 82, 32, 52, 17, 60,181, 15, 8, 11, 83, 13, 0, 18, 26, 11, 66, 2, 0, 3,
+ 50, 17, 60,183, 15, 8, 11, 84, 13, 0, 18, 26, 11, 66, 2, 0, 3, 60,184, 60,
+185, 5, 1, 50, 2, 60,186, 60,187, 7, 1, 50, 25, 60,189, 13, 0, 18, 27, 13,
+ 8, 16, 20, 85, 2, 0, 1, 60,190, 13, 8, 7, 1, 37, 23, 8, 60,191, 60,188,
+ 13, 0, 18, 27, 13, 8, 16, 54, 36, 60,192, 15, 8, 11, 86, 2, 0, 1, 60,195,
+ 13, 1, 52, 6, 7, 2, 23, 6, 50, 4, 7, 1, 23, 6, 60,196, 13, 0, 18, 27,
+ 7, 1, 16, 18, 28, 11, 29, 31, 52, 57, 60,197, 7, 1, 50, 36, 60,199, 13, 0,
+ 18, 27, 13, 9, 16, 20, 87, 13, 6, 2, 0, 2, 60,200, 13, 6, 7, 1, 37, 23,
+ 6, 60,201, 13, 9, 7, 1, 37, 23, 9, 60,202, 60,198, 13, 0, 18, 27, 13, 9,
+ 16, 54, 47, 5, 1, 50, 2, 60,203, 60,206, 13, 0, 18, 27, 7, 1, 16, 18, 28,
+ 11, 29, 31, 52, 46, 60,207, 7, 1, 50, 25, 60,209, 13, 0, 18, 27, 13, 9, 16,
+ 20, 88, 2, 0, 1, 60,210, 13, 9, 7, 1, 37, 23, 9, 60,211, 60,208, 13, 0,
+ 18, 27, 13, 9, 16, 54, 36, 5, 1, 50, 2, 60,212, 5, 2, 60,213, 60,215, 15,
+ 8, 11, 89, 2, 0, 1, 60,216, 15, 8, 11, 90, 2, 0, 1, 60,219, 15, 8, 11,
+ 91, 2, 0, 1, 60,220, 15, 93, 13, 0, 18, 15, 9, 2, 9, 1, 2, 1, 3, 7,
+ 1, 38, 60,221, 13, 7, 7, 0, 36, 52, 38, 60,222, 15, 8, 11, 39, 15, 93, 13,
+ 0, 18, 15, 7, 1, 9, 3, 2, 1, 3, 42, 15, 94, 11, 95, 13, 7, 2, 1, 2,
+ 42, 11, 96, 42, 2, 0, 1, 50, 19, 60,224, 15, 8, 11, 97, 13, 0, 18, 98, 42,
+ 11, 99, 42, 2, 0, 1, 60,225, 60,227, 15, 8, 11,100, 2, 0, 1, 60,228, 15,
+ 8, 11,101, 2, 0, 1, 60,229, 0, 0, 0, 0, 24, 0, 0, 0, 51, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0, 52, 0, 0, 0, 6, 99,108, 97,115,115, 0,
+ 0, 0, 0, 53, 0, 0, 0, 2, 95, 0, 0, 0, 0, 53, 0, 0, 0, 2, 95, 0,
+ 0, 0, 0, 53, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 0, 0, 0, 66, 0,
+ 0, 0, 5,110, 97,114,103, 0, 0, 0, 0, 77, 0, 0, 0, 2,105, 0, 0, 0,
+ 0, 84, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, 5,110, 97,114,103, 0, 0,
+ 0, 0,103, 0, 0, 0, 2,105, 0, 0, 0, 0,108, 0, 0, 0, 0, 0, 0, 0,
+119, 0, 0, 0, 2,105, 0, 0, 0, 0,124, 0, 0, 0, 0, 0, 0, 0,151, 0,
+ 0, 0, 2,105, 0, 0, 0, 0,164, 0, 0, 0, 2,116, 0, 0, 0, 0,185, 0,
+ 0, 0, 0, 0, 0, 0,187, 0, 0, 0, 2,105, 0, 0, 0, 0,197, 0, 0, 0,
+ 2,105, 0, 0, 0, 0,202, 0, 0, 0, 0, 0, 0, 0,207, 0, 0, 0, 2,105,
+ 0, 0, 0, 0,211, 0, 0, 0, 0, 0, 0, 0,212, 0, 0, 0, 0, 0, 0, 0,
+212, 0, 0, 0, 0, 0, 0, 0,220, 0, 0, 0, 9,111,118,101,114,108,111, 97,
+100, 0, 0, 0, 0,102, 2, 0, 0, 0, 6, 99,108, 97,115,115, 0, 2, 0, 0,
+ 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,105,110, 99,108, 97,115,115, 0,
+ 2, 0, 0, 0, 2, 95, 0, 2, 0, 0, 0, 7,115,116, 97,116,105, 99, 0, 2,
+ 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,109,111,100,
+ 0, 2, 0, 0, 0, 13, 94, 37,115, 42, 40,115,116, 97,116,105, 99, 41, 0, 2,
+ 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 11, 47, 42, 32,109,
+101,116,104,111,100, 58, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0,
+ 0, 11, 32,111,102, 32, 99,108, 97,115,115, 32, 0, 2, 0, 0, 0, 4, 32, 42,
+ 47, 0, 2, 0, 0, 0, 13, 47, 42, 32,102,117,110, 99,116,105,111,110, 58, 0,
+ 2, 0, 0, 0, 12,115,116, 97,116,105, 99, 32,118,111,105,100, 0, 2, 0, 0,
+ 0, 6, 99,110, 97,109,101, 0, 2, 0, 0, 0, 7, 40,118,111,105,100, 41, 0,
+ 2, 0, 0, 0, 2,123, 0, 2, 0, 0, 0, 7, 32,105,102, 32, 40, 10, 0, 2,
+ 0, 0, 0, 5,110, 97,114,103, 0, 2, 0, 0, 0, 4,110,101,119, 0, 2, 0,
+ 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 22, 32, 32, 32, 32, 32, 33,
+116,111,108,117, 97, 95,105,115,116,121,112,101, 40, 49, 44, 0, 2, 0, 0, 0,
+ 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 5, 99,116, 97,103, 0, 2, 0,
+ 0, 0, 8, 44, 48, 41, 32,124,124, 10, 0, 2, 0, 0, 0, 4,116, 97,103, 0,
+ 2, 0, 0, 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 5,118,111,105,100, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0,
+ 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 7,111, 98,106,101, 99,
+116, 0, 2, 0, 0, 0, 7, 32, 32, 32, 32, 32, 33, 0, 2, 0, 0, 0, 13,111,
+117,116, 99,104,101, 99,107,116,121,112,101, 0, 2, 0, 0, 0, 5, 32,124,124,
+ 10, 0, 2, 0, 0, 0, 21, 32, 32, 32, 32, 32, 33,116,111,108,117, 97, 95,105,
+115,110,111,111, 98,106, 40, 0, 2, 0, 0, 0, 19, 41, 10, 32, 41, 10, 32, 32,
+103,111,116,111, 32,101,114,114,111,114, 59, 0, 2, 0, 0, 0, 9, 32,101,108,
+115,101, 10, 32,123, 0, 2, 0, 0, 0, 2, 32, 0, 2, 0, 0, 0, 2, 42, 0,
+ 2, 0, 0, 0, 8,115,101,108,102, 32, 61, 32, 0, 2, 0, 0, 0, 2, 40, 0,
+ 2, 0, 0, 0, 3, 42, 41, 0, 2, 0, 0, 0, 24,116,111,108,117, 97, 95,103,
+101,116,117,115,101,114,116,121,112,101, 40, 49, 44, 48, 41, 59, 0, 2, 0, 0,
+ 0, 20, 94, 37,115, 42,115,116, 97,116,105, 99, 37,115, 37,115, 42, 40, 46, 42,
+ 41, 0, 2, 0, 0, 0, 8,100,101, 99,108, 97,114,101, 0, 2, 0, 0, 0, 55,
+ 32, 32,105,102, 32, 40, 33,115,101,108,102, 41, 32,116,111,108,117, 97, 95,101,
+114,114,111,114, 40, 34,105,110,118, 97,108,105,100, 32, 39,115,101,108,102, 39,
+ 32,105,110, 32,102,117,110, 99,116,105,111,110, 32, 39, 0, 2, 0, 0, 0, 5,
+ 39, 34, 41, 59, 0, 2, 0, 0, 0, 9,103,101,116, 97,114,114, 97,121, 0, 2,
+ 0, 0, 0, 7,100,101,108,101,116,101, 0, 2, 0, 0, 0, 15, 32, 32,100,101,
+108,101,116,101, 32,115,101,108,102, 59, 0, 2, 0, 0, 0, 12,111,112,101,114,
+ 97,116,111,114, 38, 91, 93, 0, 2, 0, 0, 0, 20, 32, 32,115,101,108,102, 45,
+ 62,111,112,101,114, 97,116,111,114, 91, 93, 40, 0, 2, 0, 0, 0, 5, 41, 32,
+ 61, 32, 0, 2, 0, 0, 0, 2, 59, 0, 2, 0, 0, 0, 4, 32, 32,123, 0, 2,
+ 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 32, 32, 0, 2, 0, 0, 0, 4,112,116,
+114, 0, 2, 0, 0, 0, 14,116,111,108,117, 97, 73, 95,114,101,116, 32, 61, 32,
+ 0, 2, 0, 0, 0, 3, 41, 32, 0, 2, 0, 0, 0, 3, 58, 58, 0, 2, 0, 0,
+ 0, 7,115,101,108,102, 45, 62, 0, 2, 0, 0, 0, 8,112, 97,115,115,112, 97,
+114, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 3, 41, 59, 0, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0,
+ 14, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104, 0, 2, 0, 0, 0, 22,
+ 40, 40,100,111,117, 98,108,101, 41,116,111,108,117, 97, 73, 95,114,101,116, 41,
+ 59, 0, 2, 0, 0, 0, 14, 40,116,111,108,117, 97, 73, 95,114,101,116, 41, 59,
+ 0, 2, 0, 0, 0, 5, 32, 32, 32,123, 0, 2, 0, 0, 0, 20, 35,105,102,100,
+101,102, 32, 95, 95, 99,112,108,117,115,112,108,117,115, 10, 0, 2, 0, 0, 0,
+ 29, 32, 32, 32, 32,118,111,105,100, 42, 32,116,111,108,117, 97, 73, 95, 99,108,
+111,110,101, 32, 61, 32,110,101,119, 0, 2, 0, 0, 0, 7, 35,101,108,115,101,
+ 10, 0, 2, 0, 0, 0, 63, 32, 32, 32, 32,118,111,105,100, 42, 32,116,111,108,
+117, 97, 73, 95, 99,108,111,110,101, 32, 61, 32,116,111,108,117, 97, 95, 99,111,
+112,121, 40, 40,118,111,105,100, 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,
+116, 44,115,105,122,101,111,102, 40, 0, 2, 0, 0, 0, 4, 41, 41, 59, 0, 2,
+ 0, 0, 0, 8, 35,101,110,100,105,102, 10, 0, 2, 0, 0, 0, 51, 32, 32, 32,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40,
+116,111,108,117, 97, 95,100,111, 99,108,111,110,101, 40,116,111,108,117, 97, 73,
+ 95, 99,108,111,110,101, 44, 0, 2, 0, 0, 0, 3, 41, 44, 0, 2, 0, 0, 0,
+ 5, 32, 32, 32,125, 0, 2, 0, 0, 0, 2, 38, 0, 2, 0, 0, 0, 42, 32, 32,
+ 32,116,111,108,117, 97, 95,112,117,115,104,117,115,101,114,116,121,112,101, 40,
+ 40,118,111,105,100, 42, 41, 38,116,111,108,117, 97, 73, 95,114,101,116, 44, 0,
+ 2, 0, 0, 0, 41, 32, 32, 32,116,111,108,117, 97, 95,112,117,115,104,117,115,
+101,114,116,121,112,101, 40, 40,118,111,105,100, 42, 41,116,111,108,117, 97, 73,
+ 95,114,101,116, 44, 0, 2, 0, 0, 0, 9,114,101,116,118, 97,108,117,101, 0,
+ 2, 0, 0, 0, 4, 32, 32,125, 0, 2, 0, 0, 0, 9,115,101,116, 97,114,114,
+ 97,121, 0, 2, 0, 0, 0, 10,102,114,101,101, 97,114,114, 97,121, 0, 2, 0,
+ 0, 0, 3, 32,125, 0, 2, 0, 0, 0, 9, 32,114,101,116,117,114,110, 59, 0,
+ 2, 0, 0, 0, 8,101,114,114,111,114, 58, 10, 0, 2, 0, 0, 0, 9,111,118,
+101,114,108,111, 97,100, 0, 2, 0, 0, 0, 7,115,116,114,115,117, 98, 0, 2,
+ 0, 0, 0, 7,102,111,114,109, 97,116, 0, 2, 0, 0, 0, 5, 37, 48, 50,100,
+ 0, 2, 0, 0, 0, 4, 40, 41, 59, 0, 2, 0, 0, 0, 36, 32,116,111,108,117,
+ 97, 95,101,114,114,111,114, 40, 34, 35,102,101,114,114,111,114, 32,105,110, 32,
+102,117,110, 99,116,105,111,110, 32, 39, 0, 2, 0, 0, 0, 6,108,110, 97,109,
+101, 0, 2, 0, 0, 0, 6, 39, 46, 34, 41, 59, 0, 2, 0, 0, 0, 2,125, 0,
+ 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,101,114,
+ 0, 4, 0, 0, 0,232, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 0, 89, 5, 1, 60,233, 13, 0, 20, 2, 2, 1, 1, 46,
+ 7, 13, 0, 20, 3, 2, 1, 1, 60,234, 13, 1, 52, 33, 60,235, 15, 4, 11, 5,
+ 13, 1, 42, 11, 6, 42, 13, 0, 18, 7, 42, 11, 8, 42, 13, 0, 18, 9, 42, 11,
+ 10, 42, 2, 0, 1, 50, 27, 60,237, 15, 4, 11, 11, 13, 0, 18, 7, 42, 11, 8,
+ 42, 13, 0, 18, 9, 42, 11, 10, 42, 2, 0, 1, 60,238, 60,239, 0, 0, 0, 0,
+ 2, 0, 0, 0,232, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,233, 0, 0,
+ 0, 7,112, 97,114,101,110,116, 0, 0, 0, 0, 12, 2, 0, 0, 0, 7,112, 97,
+114,101,110,116, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 8,
+105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,100,117,108,
+101, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0, 0, 18, 32,
+116,111,108,117, 97, 95,102,117,110, 99,116,105,111,110, 40, 34, 0, 2, 0, 0,
+ 0, 4, 34, 44, 34, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0,
+ 0, 3, 34, 44, 0, 2, 0, 0, 0, 6, 99,110, 97,109,101, 0, 2, 0, 0, 0,
+ 3, 41, 59, 0, 2, 0, 0, 0, 23, 32,116,111,108,117, 97, 95,102,117,110, 99,
+116,105,111,110, 40, 78, 85, 76, 76, 44, 34, 0, 2, 0, 0, 0, 11,117,110,114,
+101,103,105,115,116,101,114, 0, 4, 0, 0, 0,242, 0, 0, 0, 14, 64,102,117,
+110, 99,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 52, 4, 1, 60,243, 13,
+ 0, 20, 1, 2, 1, 1, 4, 0, 32, 48, 10, 13, 0, 20, 2, 2, 1, 1, 4, 0,
+ 32, 52, 19, 60,244, 15, 3, 11, 4, 13, 0, 18, 5, 42, 11, 6, 42, 2, 0, 1,
+ 50, 2, 60,245, 60,246, 0, 0, 0, 0, 1, 0, 0, 0,242, 0, 0, 0, 5,115,
+101,108,102, 0, 0, 0, 0, 7, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0,
+ 0, 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 9,105,110,109,111,
+100,117,108,101, 0, 2, 0, 0, 0, 7,111,117,116,112,117,116, 0, 2, 0, 0,
+ 0, 32, 32,108,117, 97, 95,112,117,115,104,110,105,108, 40, 41, 59, 32,108,117,
+ 97, 95,115,101,116,103,108,111, 98, 97,108, 40, 34, 0, 2, 0, 0, 0, 6,108,
+110, 97,109,101, 0, 2, 0, 0, 0, 4, 34, 41, 59, 0, 2, 0, 0, 0, 6,112,
+114,105,110,116, 0, 4, 0, 0, 0,250, 0, 0, 0, 14, 64,102,117,110, 99,116,
+105,111,110, 46,108,117, 97, 0, 0, 0, 1, 1, 8, 3, 60,251, 15, 2, 13, 1,
+ 11, 3, 42, 2, 0, 1, 60,252, 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42,
+ 11, 7, 42, 2, 0, 1, 60,253, 15, 2, 13, 1, 11, 8, 42, 13, 0, 18, 9, 42,
+ 11, 7, 42, 2, 0, 1, 60,254, 15, 2, 13, 1, 11, 10, 42, 13, 0, 18, 11, 42,
+ 11, 7, 42, 2, 0, 1, 60,255, 15, 2, 13, 1, 11, 12, 42, 13, 0, 18, 13, 42,
+ 11, 7, 42, 2, 0, 1, 59, 1, 0, 15, 2, 13, 1, 11, 14, 42, 13, 0, 18, 15,
+ 42, 11, 7, 42, 2, 0, 1, 59, 1, 1, 15, 2, 13, 1, 11, 16, 42, 13, 0, 18,
+ 17, 42, 11, 7, 42, 2, 0, 1, 59, 1, 2, 15, 2, 13, 1, 11, 18, 42, 13, 0,
+ 18, 19, 42, 11, 7, 42, 2, 0, 1, 59, 1, 3, 15, 2, 13, 1, 11, 20, 42, 2,
+ 0, 1, 59, 1, 4, 7, 1, 50, 35, 59, 1, 6, 13, 0, 18, 22, 13, 3, 16, 20,
+ 2, 13, 1, 11, 23, 42, 11, 24, 2, 0, 3, 59, 1, 7, 13, 3, 7, 1, 37, 23,
+ 3, 59, 1, 8, 59, 1, 5, 13, 0, 18, 22, 13, 3, 16, 54, 47, 59, 1, 9, 15,
+ 2, 13, 1, 11, 25, 42, 2, 0, 1, 59, 1, 10, 15, 2, 13, 1, 11, 26, 42, 13,
+ 2, 42, 2, 0, 1, 59, 1, 11, 0, 0, 0, 0, 4, 0, 0, 0,250, 0, 0, 0,
+ 5,115,101,108,102, 0, 0, 0, 0,250, 0, 0, 0, 6,105,100,101,110,116, 0,
+ 0, 0, 0,250, 0, 0, 0, 6, 99,108,111,115,101, 0, 0, 0, 1, 4, 0, 0,
+ 0, 2,105, 0, 0, 0, 0, 27, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2,
+ 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116,
+ 0, 2, 0, 0, 0, 10, 70,117,110, 99,116,105,111,110,123, 0, 2, 0, 0, 0,
+ 10, 32,109,111,100, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0,
+ 0, 0, 10, 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,
+112,101, 0, 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39, 0, 2, 0,
+ 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32,
+ 39, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32, 99,111,
+110,115,116, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2,
+ 0, 0, 0, 11, 32, 99,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6,
+ 99,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32,108,110, 97,109,101, 32, 61, 32,
+ 39, 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 10, 32, 97,
+114,103,115, 32, 61, 32,123, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5,
+ 97,114,103,115, 0, 2, 0, 0, 0, 3, 32, 32, 0, 2, 0, 0, 0, 2, 44, 0,
+ 2, 0, 0, 0, 3, 32,125, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 9,
+111,118,101,114,108,111, 97,100, 0, 4, 0, 0, 1, 14, 0, 0, 0, 14, 64,102,
+117,110, 99,116,105,111,110, 46,108,117, 97, 0, 0, 0, 0, 22, 4, 1, 59, 1,
+ 15, 13, 0, 18, 1, 20, 2, 13, 0, 18, 3, 3, 1, 2, 59, 1, 16, 0, 0, 0,
+ 0, 1, 0, 0, 1, 14, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 4, 2,
+ 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 7,112, 97,114,101,110,116,
+ 0, 2, 0, 0, 0, 9,111,118,101,114,108,111, 97,100, 0, 2, 0, 0, 0, 6,
+108,110, 97,109,101, 0, 2, 0, 0, 0, 10, 95, 70,117,110, 99,116,105,111,110,
+ 0, 4, 0, 0, 1, 21, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,110, 46,
+108,117, 97, 0, 0, 0, 0,231, 7, 1, 59, 1, 22, 13, 0, 11, 1, 15, 2, 26,
+ 59, 1, 23, 15, 3, 13, 0, 15, 4, 2, 0, 2, 59, 1, 25, 13, 0, 18, 5, 11,
+ 5, 31, 48, 7, 13, 0, 18, 5, 11, 6, 31, 52, 12, 59, 1, 26, 15, 7, 11, 8,
+ 2, 0, 1, 50, 3, 59, 1, 27, 59, 1, 29, 15, 9, 13, 0, 2, 0, 1, 59, 1,
+ 30, 13, 0, 20, 10, 2, 1, 1, 52,108, 59, 1, 31, 13, 0, 18, 11, 13, 0, 18,
+ 12, 18, 11, 32, 52, 46, 59, 1, 32, 13, 0, 11, 11, 11, 13, 26, 59, 1, 33, 13,
+ 0, 11, 14, 11, 13, 26, 59, 1, 34, 13, 0, 11, 15, 13, 0, 18, 12, 18, 11, 26,
+ 59, 1, 35, 13, 0, 11, 16, 11, 17, 26, 50, 44, 59, 1, 36, 13, 0, 18, 11, 11,
+ 18, 13, 0, 18, 12, 18, 11, 42, 32, 52, 22, 59, 1, 37, 13, 0, 11, 11, 11, 19,
+ 26, 59, 1, 38, 13, 0, 11, 14, 11, 19, 26, 50, 3, 59, 1, 39, 50, 3, 59, 1,
+ 40, 59, 1, 41, 13, 0, 11, 20, 13, 0, 20, 21, 11, 22, 2, 1, 2, 13, 0, 20,
+ 23, 13, 0, 2, 1, 2, 42, 26, 59, 1, 42, 13, 0, 1, 1, 59, 1, 43, 0, 0,
+ 0, 0, 1, 0, 0, 1, 21, 0, 0, 0, 2,116, 0, 0, 0, 0, 24, 2, 0, 0,
+ 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 14,
+ 99,108, 97,115,115, 70,117,110, 99,116,105,111,110, 0, 2, 0, 0, 0, 7,115,
+101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103,
+ 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 1, 0, 2, 0,
+ 0, 0, 6,101,114,114,111,114, 0, 2, 0, 0, 0, 31, 35,105,110,118, 97,108,
+105,100, 32, 39, 99,111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97,
+116,105,111,110, 0, 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0,
+ 0, 8,105,110, 99,108, 97,115,115, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0,
+ 2, 0, 0, 0, 7,112, 97,114,101,110,116, 0, 2, 0, 0, 0, 4,110,101,119,
+ 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 5,116,121,112,
+101, 0, 2, 0, 0, 0, 4,112,116,114, 0, 2, 0, 0, 0, 2, 42, 0, 2, 0,
+ 0, 0, 2,126, 0, 2, 0, 0, 0, 7,100,101,108,101,116,101, 0, 2, 0, 0,
+ 0, 6, 99,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110, 99,110, 97,
+109,101, 0, 2, 0, 0, 0, 7,116,111,108,117, 97, 73, 0, 2, 0, 0, 0, 9,
+111,118,101,114,108,111, 97,100, 0, 2, 0, 0, 0, 9, 70,117,110, 99,116,105,
+111,110, 0, 4, 0, 0, 1, 49, 0, 0, 0, 14, 64,102,117,110, 99,116,105,111,
+110, 46,108,117, 97, 0, 0, 0, 0,147, 11, 3, 59, 1, 50, 15, 4, 15, 5, 13,
+ 1, 7, 2, 9, 2, 2, 1, 3, 11, 6, 2, 1, 2, 59, 1, 51, 7, 1, 59, 1,
+ 52, 22, 1, 11, 9, 7, 0, 30, 0, 50, 50, 59, 1, 54, 13, 5, 11, 9, 13, 5,
+ 18, 9, 7, 1, 37, 26, 59, 1, 55, 13, 5, 13, 5, 18, 9, 15, 10, 13, 3, 13,
+ 4, 16, 11, 11, 2, 1, 2, 26, 59, 1, 56, 13, 4, 7, 1, 37, 23, 4, 59, 1,
+ 57, 59, 1, 53, 13, 3, 13, 4, 16, 54, 60, 59, 1, 58, 15, 10, 13, 0, 11, 13,
+ 2, 1, 2, 59, 1, 59, 13, 6, 11, 14, 13, 5, 26, 59, 1, 60, 13, 6, 11, 15,
+ 13, 2, 26, 59, 1, 61, 15, 16, 13, 6, 3, 7, 1, 59, 1, 62, 0, 0, 0, 0,
+ 7, 0, 0, 1, 49, 0, 0, 0, 2,100, 0, 0, 0, 1, 49, 0, 0, 0, 2, 97,
+ 0, 0, 0, 1, 49, 0, 0, 0, 2, 99, 0, 0, 0, 1, 50, 0, 0, 0, 2,116,
+ 0, 0, 0, 1, 51, 0, 0, 0, 2,105, 0, 0, 0, 1, 52, 0, 0, 0, 2,108,
+ 0, 0, 0, 1, 58, 0, 0, 0, 2,102, 0, 0, 0, 0, 17, 2, 0, 0, 0, 2,
+100, 0, 2, 0, 0, 0, 2, 97, 0, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0,
+ 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0, 0, 0, 7,115,
+116,114,115,117, 98, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,105, 0,
+ 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 12, 68,
+101, 99,108, 97,114, 97,116,105,111,110, 0, 2, 0, 0, 0, 4,118, 97,114, 0,
+ 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 5,102,117,110, 99, 0, 2, 0, 0,
+ 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0,
+ 0, 0, 10, 95, 70,117,110, 99,116,105,111,110, 0,
+};
+
+/* operator.lo */
+static char B14[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,111,112,101,114, 97,
+116,111,114, 46,108,117, 97, 0, 0, 0, 0,125, 21, 0, 60, 18, 22, 2, 60, 19,
+ 11, 1, 11, 2, 11, 3, 60, 20, 15, 4, 30, 1, 60, 21, 25, 0, 60, 22, 15, 5,
+ 15, 0, 15, 6, 2, 0, 2, 60, 25, 22, 10, 11, 8, 11, 9, 60, 26, 11, 10, 11,
+ 11, 60, 27, 11, 12, 11, 13, 60, 28, 11, 14, 11, 15, 60, 29, 11, 16, 11, 17, 60,
+ 30, 11, 18, 11, 19, 60, 31, 11, 20, 11, 21, 60, 32, 11, 22, 11, 23, 60, 33, 11,
+ 24, 11, 25, 60, 34, 11, 26, 11, 27, 30, 9, 60, 35, 25, 7, 60, 39, 15, 0, 11,
+ 28, 11, 29, 26, 60, 60, 11, 31, 25, 30, 60, 82, 11, 33, 25, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 34, 2, 0, 0, 0, 14, 99,108, 97,115,115, 79,112,101,114, 97,
+116,111,114, 0, 2, 0, 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 1, 0,
+ 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 14, 99,108, 97,115,
+115, 70,117,110, 99,116,105,111,110, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,
+103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0,
+ 0, 4, 95, 84, 77, 0, 2, 0, 0, 0, 2, 43, 0, 2, 0, 0, 0, 13,111,112,
+101,114, 97,116,111,114, 95, 97,100,100, 0, 2, 0, 0, 0, 2, 45, 0, 2, 0,
+ 0, 0, 13,111,112,101,114, 97,116,111,114, 95,115,117, 98, 0, 2, 0, 0, 0,
+ 2, 42, 0, 2, 0, 0, 0, 13,111,112,101,114, 97,116,111,114, 95,109,117,108,
+ 0, 2, 0, 0, 0, 2, 47, 0, 2, 0, 0, 0, 13,111,112,101,114, 97,116,111,
+114, 95,100,105,118, 0, 2, 0, 0, 0, 2, 60, 0, 2, 0, 0, 0, 12,111,112,
+101,114, 97,116,111,114, 95,108,116, 0, 2, 0, 0, 0, 2, 62, 0, 2, 0, 0,
+ 0, 12,111,112,101,114, 97,116,111,114, 95,103,116, 0, 2, 0, 0, 0, 3, 60,
+ 61, 0, 2, 0, 0, 0, 12,111,112,101,114, 97,116,111,114, 95,108,101, 0, 2,
+ 0, 0, 0, 3, 62, 61, 0, 2, 0, 0, 0, 12,111,112,101,114, 97,116,111,114,
+ 95,103,101, 0, 2, 0, 0, 0, 3, 91, 93, 0, 2, 0, 0, 0, 13,111,112,101,
+114, 97,116,111,114, 95,103,101,116, 0, 2, 0, 0, 0, 4, 38, 91, 93, 0, 2,
+ 0, 0, 0, 13,111,112,101,114, 97,116,111,114, 95,115,101,116, 0, 2, 0, 0,
+ 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 39, 0, 0, 0, 14, 64,111,112,
+101,114, 97,116,111,114, 46,108,117, 97, 0, 0, 0, 1, 9, 8, 3, 60, 40, 15,
+ 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 41, 15, 2, 13, 1, 11, 4, 42, 13, 0,
+ 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 42, 15, 2, 13, 1, 11, 8, 42, 13, 0,
+ 18, 9, 42, 11, 7, 42, 2, 0, 1, 60, 43, 15, 2, 13, 1, 11, 10, 42, 13, 0,
+ 18, 11, 42, 11, 7, 42, 2, 0, 1, 60, 44, 15, 2, 13, 1, 11, 12, 42, 13, 0,
+ 18, 13, 42, 11, 7, 42, 2, 0, 1, 60, 45, 15, 2, 13, 1, 11, 14, 42, 13, 0,
+ 18, 15, 42, 11, 7, 42, 2, 0, 1, 60, 46, 15, 2, 13, 1, 11, 16, 42, 13, 0,
+ 18, 17, 42, 11, 7, 42, 2, 0, 1, 60, 47, 15, 2, 13, 1, 11, 18, 42, 13, 0,
+ 18, 19, 42, 11, 7, 42, 2, 0, 1, 60, 48, 15, 2, 13, 1, 11, 20, 42, 13, 0,
+ 18, 21, 42, 11, 7, 42, 2, 0, 1, 60, 49, 15, 2, 13, 1, 11, 22, 42, 2, 0,
+ 1, 60, 50, 7, 1, 50, 32, 60, 52, 13, 0, 18, 24, 13, 3, 16, 20, 2, 13, 1,
+ 11, 25, 42, 11, 26, 2, 0, 3, 60, 53, 13, 3, 7, 1, 37, 23, 3, 60, 54, 60,
+ 51, 13, 0, 18, 24, 13, 3, 16, 54, 43, 60, 55, 15, 2, 13, 1, 11, 27, 42, 2,
+ 0, 1, 60, 56, 15, 2, 13, 1, 11, 28, 42, 13, 2, 42, 2, 0, 1, 60, 57, 0,
+ 0, 0, 0, 4, 0, 0, 0, 39, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0,
+ 39, 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0, 39, 0, 0, 0, 6, 99,
+108,111,115,101, 0, 0, 0, 0, 50, 0, 0, 0, 2,105, 0, 0, 0, 0, 29, 2,
+ 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101,
+ 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 10, 79,112,101,
+114, 97,116,111,114,123, 0, 2, 0, 0, 0, 11, 32,107,105,110,100, 32, 32, 61,
+ 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,107,105,
+110,100, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 10, 32,109,111,100,
+ 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,109,111,100, 0, 2, 0, 0, 0, 10,
+ 32,116,121,112,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,116,121,112,101, 0,
+ 2, 0, 0, 0, 10, 32,112,116,114, 32, 32, 61, 32, 39, 0, 2, 0, 0, 0, 4,
+112,116,114, 0, 2, 0, 0, 0, 10, 32,110, 97,109,101, 32, 61, 32, 39, 0, 2,
+ 0, 0, 0, 5,110, 97,109,101, 0, 2, 0, 0, 0, 11, 32, 99,111,110,115,116,
+ 32, 61, 32, 39, 0, 2, 0, 0, 0, 6, 99,111,110,115,116, 0, 2, 0, 0, 0,
+ 11, 32, 99,110, 97,109,101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 6, 99,110, 97,
+109,101, 0, 2, 0, 0, 0, 11, 32,108,110, 97,109,101, 32, 61, 32, 39, 0, 2,
+ 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 10, 32, 97,114,103,115,
+ 32, 61, 32,123, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0, 5, 97,114,103,
+115, 0, 2, 0, 0, 0, 3, 32, 32, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0,
+ 0, 3, 32,125, 0, 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 10, 95, 79,112,
+101,114, 97,116,111,114, 0, 4, 0, 0, 0, 60, 0, 0, 0, 14, 64,111,112,101,
+114, 97,116,111,114, 46,108,117, 97, 0, 0, 0, 0,140, 7, 1, 60, 61, 13, 0,
+ 11, 1, 15, 2, 26, 60, 62, 15, 3, 13, 0, 15, 4, 2, 0, 2, 60, 64, 13, 0,
+ 18, 5, 11, 5, 31, 48, 7, 13, 0, 18, 5, 11, 6, 31, 52, 11, 60, 65, 15, 7,
+ 11, 8, 2, 0, 1, 50, 2, 60, 66, 60, 68, 15, 9, 13, 0, 2, 0, 1, 60, 69,
+ 13, 0, 20, 10, 2, 1, 1, 44, 52, 11, 60, 70, 15, 7, 11, 11, 2, 0, 1, 50,
+ 2, 60, 71, 60, 73, 13, 0, 11, 12, 13, 0, 20, 13, 11, 14, 2, 1, 2, 13, 0,
+ 20, 15, 13, 0, 2, 1, 2, 42, 26, 60, 74, 13, 0, 11, 16, 13, 0, 18, 16, 13,
+ 0, 18, 17, 42, 26, 60, 75, 13, 0, 1, 1, 60, 76, 0, 0, 0, 0, 1, 0, 0,
+ 0, 60, 0, 0, 0, 2,116, 0, 0, 0, 0, 18, 2, 0, 0, 0, 2,116, 0, 2,
+ 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 14, 99,108, 97,115,115,
+ 79,112,101,114, 97,116,111,114, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103,
+ 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0,
+ 6, 99,111,110,115,116, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 6,101,114,
+114,111,114, 0, 2, 0, 0, 0, 31, 35,105,110,118, 97,108,105,100, 32, 39, 99,
+111,110,115,116, 39, 32,115,112,101, 99,105,102,105, 99, 97,116,105,111,110, 0,
+ 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0, 8,105,110, 99,
+108, 97,115,115, 0, 2, 0, 0, 0, 46, 35,111,112,101,114, 97,116,111,114, 32,
+ 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105,110,101,100, 32,
+ 97,115, 32, 99,108, 97,115,115, 32,109,101,109, 98,101,114, 0, 2, 0, 0, 0,
+ 6, 99,110, 97,109,101, 0, 2, 0, 0, 0, 10, 99,102,117,110, 99,110, 97,109,
+101, 0, 2, 0, 0, 0, 7,116,111,108,117, 97, 73, 0, 2, 0, 0, 0, 9,111,
+118,101,114,108,111, 97,100, 0, 2, 0, 0, 0, 5,110, 97,109,101, 0, 2, 0,
+ 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 9, 79,112,101,114, 97,116,111,
+114, 0, 4, 0, 0, 0, 82, 0, 0, 0, 14, 64,111,112,101,114, 97,116,111,114,
+ 46,108,117, 97, 0, 0, 0, 1, 99, 14, 4, 60, 83, 15, 5, 15, 6, 13, 2, 7,
+ 2, 15, 7, 13, 2, 2, 1, 1, 7, 1, 38, 2, 1, 3, 11, 8, 2, 1, 2, 60,
+ 84, 7, 1, 60, 85, 22, 1, 11, 11, 7, 0, 30, 0, 50, 46, 60, 87, 13, 6, 11,
+ 11, 13, 6, 18, 11, 7, 1, 37, 26, 60, 88, 13, 6, 13, 6, 18, 11, 15, 12, 13,
+ 4, 13, 5, 16, 11, 13, 2, 1, 2, 26, 60, 89, 13, 5, 7, 1, 37, 23, 5, 60,
+ 90, 60, 86, 13, 4, 13, 5, 16, 54, 55, 60, 91, 13, 1, 11, 14, 32, 52, 17, 60,
+ 92, 15, 15, 13, 0, 11, 16, 11, 17, 2, 1, 3, 23, 0, 50, 59, 60, 93, 13, 1,
+ 11, 18, 32, 52, 48, 60, 94, 13, 6, 11, 11, 13, 6, 18, 11, 7, 1, 37, 26, 60,
+ 95, 13, 6, 13, 6, 18, 11, 15, 12, 13, 0, 11, 13, 2, 1, 2, 26, 60, 96, 13,
+ 6, 13, 6, 18, 11, 16, 11, 19, 11, 20, 26, 50, 2, 60, 97, 60, 98, 15, 12, 13,
+ 0, 11, 22, 2, 1, 2, 60, 99, 13, 1, 11, 14, 32, 48, 25, 13, 6, 7, 1, 16,
+ 4, 0, 32, 46, 15, 15, 23, 13, 6, 7, 1, 16, 18, 24, 2, 1, 1, 11, 25, 31,
+ 52, 11, 60,100, 15, 26, 11, 27, 2, 0, 1, 50, 2, 60,101, 60,102, 13, 7, 11,
+ 28, 13, 6, 26, 60,103, 13, 7, 11, 29, 13, 3, 26, 60,104, 13, 7, 11, 30, 15,
+ 15, 13, 1, 11, 31, 11, 17, 2, 1, 3, 26, 60,105, 13, 7, 11, 32, 15, 33, 13,
+ 7, 18, 30, 16, 26, 60,106, 13, 7, 18, 30, 11, 14, 32, 48, 12, 15, 34, 13, 7,
+ 18, 35, 11, 29, 2, 1, 2, 44, 52, 20, 60,107, 15, 36, 13, 0, 11, 16, 13, 1,
+ 42, 13, 2, 13, 3, 2, 0, 4, 50, 2, 60,108, 60,109, 15, 37, 13, 7, 3, 8,
+ 1, 60,110, 0, 0, 0, 0, 8, 0, 0, 0, 82, 0, 0, 0, 2,100, 0, 0, 0,
+ 0, 82, 0, 0, 0, 2,107, 0, 0, 0, 0, 82, 0, 0, 0, 2, 97, 0, 0, 0,
+ 0, 82, 0, 0, 0, 2, 99, 0, 0, 0, 0, 83, 0, 0, 0, 2,116, 0, 0, 0,
+ 0, 84, 0, 0, 0, 2,105, 0, 0, 0, 0, 85, 0, 0, 0, 2,108, 0, 0, 0,
+ 0, 98, 0, 0, 0, 2,102, 0, 0, 0, 0, 38, 2, 0, 0, 0, 2,100, 0, 2,
+ 0, 0, 0, 2,107, 0, 2, 0, 0, 0, 2, 97, 0, 2, 0, 0, 0, 2, 99, 0,
+ 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6,115,112,108,105,116, 0, 2, 0,
+ 0, 0, 7,115,116,114,115,117, 98, 0, 2, 0, 0, 0, 7,115,116,114,108,101,
+110, 0, 2, 0, 0, 0, 2, 44, 0, 2, 0, 0, 0, 2,105, 0, 2, 0, 0, 0,
+ 2,108, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 12, 68,101, 99,108, 97,
+114, 97,116,105,111,110, 0, 2, 0, 0, 0, 4,118, 97,114, 0, 2, 0, 0, 0,
+ 3, 91, 93, 0, 2, 0, 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 2, 38,
+ 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 4, 38, 91, 93, 0, 2, 0, 0, 0,
+ 5,110, 97,109,101, 0, 2, 0, 0, 0, 13,116,111,108,117, 97, 73, 95,118, 97,
+108,117,101, 0, 2, 0, 0, 0, 2,102, 0, 2, 0, 0, 0, 5,102,117,110, 99,
+ 0, 2, 0, 0, 0, 8,105,115, 98, 97,115,105, 99, 0, 2, 0, 0, 0, 5,116,
+121,112,101, 0, 2, 0, 0, 0, 7,110,117,109, 98,101,114, 0, 2, 0, 0, 0,
+ 6,101,114,114,111,114, 0, 2, 0, 0, 0, 50,111,112,101,114, 97,116,111,114,
+ 91, 93, 32, 99, 97,110, 32,111,110,108,121, 32, 98,101, 32,100,101,102,105,110,
+101,100, 32,102,111,114, 32,110,117,109,101,114,105, 99, 32,105,110,100,101,120,
+ 46, 0, 2, 0, 0, 0, 5, 97,114,103,115, 0, 2, 0, 0, 0, 6, 99,111,110,
+115,116, 0, 2, 0, 0, 0, 5,107,105,110,100, 0, 2, 0, 0, 0, 3, 37,115,
+ 0, 2, 0, 0, 0, 6,108,110, 97,109,101, 0, 2, 0, 0, 0, 4, 95, 84, 77,
+ 0, 2, 0, 0, 0, 8,115,116,114,102,105,110,100, 0, 2, 0, 0, 0, 4,109,
+111,100, 0, 2, 0, 0, 0, 9, 79,112,101,114, 97,116,111,114, 0, 2, 0, 0,
+ 0, 10, 95, 79,112,101,114, 97,116,111,114, 0,
+};
+
+/* verbatim.lo */
+static char B15[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 14, 64,118,101,114, 98, 97,
+116,105,109, 46,108,117, 97, 0, 0, 0, 0, 84, 5, 0, 60, 20, 22, 2, 60, 21,
+ 11, 1, 11, 2, 11, 3, 60, 22, 15, 4, 30, 1, 60, 23, 25, 0, 60, 24, 15, 5,
+ 15, 0, 15, 6, 2, 0, 2, 60, 27, 15, 0, 11, 7, 11, 8, 26, 60, 34, 15, 0,
+ 11, 9, 11, 10, 26, 60, 42, 15, 0, 11, 11, 11, 12, 26, 60, 50, 15, 0, 11, 13,
+ 11, 14, 26, 60, 58, 11, 16, 25, 15, 60, 67, 11, 18, 25, 17, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 2, 0, 0, 0, 14, 99,108, 97,115,115, 86,101,114, 98, 97,116,
+105,109, 0, 2, 0, 0, 0, 5,108,105,110,101, 0, 2, 0, 0, 0, 1, 0, 2,
+ 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115,
+ 70,101, 97,116,117,114,101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0,
+ 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,
+112,114,101, 97,109, 98,108,101, 0, 4, 0, 0, 0, 27, 0, 0, 0, 14, 64,118,
+101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0, 0, 29, 3, 1, 60, 28,
+ 13, 0, 18, 1, 44, 52, 13, 60, 29, 15, 2, 13, 0, 18, 3, 2, 0, 1, 50, 2,
+ 60, 30, 60, 31, 0, 0, 0, 0, 1, 0, 0, 0, 27, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 4, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 5, 99,111,110,100, 0, 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0,
+ 0, 5,108,105,110,101, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0,
+ 4, 0, 0, 0, 34, 0, 0, 0, 14, 64,118,101,114, 98, 97,116,105,109, 46,108,
+117, 97, 0, 0, 0, 0, 37, 3, 1, 60, 35, 13, 0, 18, 1, 52, 22, 60, 36, 15,
+ 2, 13, 0, 18, 3, 2, 0, 1, 60, 37, 15, 2, 11, 4, 2, 0, 1, 50, 2, 60,
+ 38, 60, 39, 0, 0, 0, 0, 1, 0, 0, 0, 34, 0, 0, 0, 5,115,101,108,102,
+ 0, 0, 0, 0, 5, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5,
+ 99,111,110,100, 0, 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0,
+ 5,108,105,110,101, 0, 2, 0, 0, 0, 2, 10, 0, 2, 0, 0, 0, 9,114,101,
+103,105,115,116,101,114, 0, 4, 0, 0, 0, 42, 0, 0, 0, 14, 64,118,101,114,
+ 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0, 0, 28, 3, 1, 60, 43, 13, 0,
+ 18, 1, 52, 13, 60, 44, 15, 2, 13, 0, 18, 3, 2, 0, 1, 50, 2, 60, 45, 60,
+ 46, 0, 0, 0, 0, 1, 0, 0, 0, 42, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0, 4, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0, 5, 99,111,
+110,100, 0, 2, 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0, 5,108,
+105,110,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 4, 0, 0, 0, 50,
+ 0, 0, 0, 14, 64,118,101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0,
+ 0, 52, 6, 3, 60, 51, 15, 2, 13, 1, 11, 3, 42, 2, 0, 1, 60, 52, 15, 2,
+ 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7, 42, 2, 0, 1, 60, 53, 15, 2,
+ 13, 1, 11, 8, 42, 13, 2, 42, 2, 0, 1, 60, 54, 0, 0, 0, 0, 3, 0, 0,
+ 0, 50, 0, 0, 0, 5,115,101,108,102, 0, 0, 0, 0, 50, 0, 0, 0, 6,105,
+100,101,110,116, 0, 0, 0, 0, 50, 0, 0, 0, 6, 99,108,111,115,101, 0, 0,
+ 0, 0, 9, 2, 0, 0, 0, 6,105,100,101,110,116, 0, 2, 0, 0, 0, 6, 99,
+108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0,
+ 10, 86,101,114, 98, 97,116,105,109,123, 0, 2, 0, 0, 0, 10, 32,108,105,110,
+101, 32, 61, 32, 39, 0, 2, 0, 0, 0, 5,115,101,108,102, 0, 2, 0, 0, 0,
+ 5,108,105,110,101, 0, 2, 0, 0, 0, 3, 39, 44, 0, 2, 0, 0, 0, 2,125,
+ 0, 2, 0, 0, 0, 10, 95, 86,101,114, 98, 97,116,105,109, 0, 4, 0, 0, 0,
+ 58, 0, 0, 0, 14, 64,118,101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0,
+ 0, 0, 40, 4, 1, 60, 59, 13, 0, 11, 1, 15, 2, 26, 60, 60, 15, 3, 13, 0,
+ 15, 4, 2, 0, 2, 60, 61, 15, 5, 13, 0, 2, 0, 1, 60, 62, 13, 0, 1, 1,
+ 60, 63, 0, 0, 0, 0, 1, 0, 0, 0, 58, 0, 0, 0, 2,116, 0, 0, 0, 0,
+ 6, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2,
+ 0, 0, 0, 14, 99,108, 97,115,115, 86,101,114, 98, 97,116,105,109, 0, 2, 0,
+ 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97,
+ 95,116, 97,103, 0, 2, 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0,
+ 0, 9, 86,101,114, 98, 97,116,105,109, 0, 4, 0, 0, 0, 67, 0, 0, 0, 14,
+ 64,118,101,114, 98, 97,116,105,109, 46,108,117, 97, 0, 0, 0, 0, 75, 8, 1,
+ 60, 68, 4, 0, 60, 69, 15, 2, 13, 0, 7, 1, 7, 1, 2, 1, 3, 11, 3, 32,
+ 52, 21, 60, 70, 7, 1, 23, 1, 60, 71, 15, 2, 13, 0, 7, 2, 2, 1, 2, 23,
+ 0, 50, 2, 60, 72, 60, 73, 15, 4, 22, 2, 60, 74, 11, 5, 13, 0, 11, 6, 60,
+ 75, 13, 1, 30, 1, 60, 76, 3, 2, 1, 60, 77, 0, 0, 0, 0, 2, 0, 0, 0,
+ 67, 0, 0, 0, 2,108, 0, 0, 0, 0, 68, 0, 0, 0, 2, 99, 0, 0, 0, 0,
+ 7, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0, 7,
+115,116,114,115,117, 98, 0, 2, 0, 0, 0, 2, 36, 0, 2, 0, 0, 0, 10, 95,
+ 86,101,114, 98, 97,116,105,109, 0, 2, 0, 0, 0, 5,108,105,110,101, 0, 2,
+ 0, 0, 0, 5, 99,111,110,100, 0,
+};
+
+/* code.lo */
+static char B16[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 10, 64, 99,111,100,101, 46,
+108,117, 97, 0, 0, 0, 0, 66, 5, 0, 60, 19, 22, 2, 60, 20, 11, 1, 11, 2,
+ 11, 3, 60, 21, 15, 4, 30, 1, 60, 22, 25, 0, 60, 23, 15, 5, 15, 0, 15, 6,
+ 2, 0, 2, 60, 26, 15, 0, 11, 7, 11, 8, 26, 60, 63, 15, 0, 11, 9, 11, 10,
+ 26, 60, 71, 11, 12, 25, 11, 60, 80, 11, 14, 25, 13, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 2, 0, 0, 0, 10, 99,108, 97,115,115, 67,111,100,101, 0, 2, 0, 0,
+ 0, 5,116,101,120,116, 0, 2, 0, 0, 0, 1, 0, 2, 0, 0, 0, 6, 95, 98,
+ 97,115,101, 0, 2, 0, 0, 0, 13, 99,108, 97,115,115, 70,101, 97,116,117,114,
+101, 0, 2, 0, 0, 0, 7,115,101,116,116, 97,103, 0, 2, 0, 0, 0, 10,116,
+111,108,117, 97, 95,116, 97,103, 0, 2, 0, 0, 0, 9,114,101,103,105,115,116,
+101,114, 0, 4, 0, 0, 0, 26, 0, 0, 0, 10, 64, 99,111,100,101, 46,108,117,
+ 97, 0, 0, 0, 0,239, 13, 1, 60, 27, 15, 1, 2, 1, 0, 60, 28, 15, 1, 2,
+ 1, 0, 60, 29, 15, 4, 13, 2, 11, 5, 2, 1, 2, 60, 30, 13, 3, 44, 52, 11,
+ 60, 31, 15, 6, 11, 7, 2, 0, 1, 50, 2, 60, 32, 60, 34, 15, 8, 13, 3, 13,
+ 0, 18, 10, 2, 0, 2, 60, 35, 15, 11, 13, 3, 2, 0, 1, 60, 36, 15, 12, 11,
+ 13, 13, 1, 42, 11, 14, 42, 13, 2, 42, 2, 0, 1, 60, 37, 15, 15, 13, 2, 2,
+ 0, 1, 60, 40, 15, 4, 13, 1, 11, 17, 2, 1, 2, 60, 41, 13, 4, 4, 0, 32,
+ 52, 11, 60, 42, 15, 6, 11, 7, 2, 0, 1, 50, 2, 60, 43, 60, 44, 15, 19, 13,
+ 4, 11, 20, 2, 1, 2, 60, 45, 15, 11, 13, 4, 2, 0, 1, 60, 46, 15, 15, 13,
+ 1, 2, 0, 1, 60, 48, 15, 21, 11, 22, 2, 0, 1, 60, 49, 15, 21, 11, 23, 2,
+ 0, 1, 60, 50, 22, 1, 11, 2, 7, 0, 30, 0, 60, 51, 15, 21, 15, 25, 13, 5,
+ 11, 26, 13, 6, 58, 27, 1, 60, 55, 2, 1, 3, 2, 0, 1, 60, 56, 15, 21, 11,
+ 28, 2, 0, 1, 60, 57, 15, 21, 11, 29, 2, 0, 1, 60, 58, 15, 21, 11, 30, 2,
+ 0, 1, 60, 59, 0, 0, 0, 0, 7, 0, 0, 0, 26, 0, 0, 0, 5,115,101,108,
+102, 0, 0, 0, 0, 27, 0, 0, 0, 2,111, 0, 0, 0, 0, 28, 0, 0, 0, 2,
+110, 0, 0, 0, 0, 29, 0, 0, 0, 2,102, 0, 0, 0, 0, 40, 0, 0, 0, 3,
+102,112, 0, 0, 0, 0, 44, 0, 0, 0, 2,115, 0, 0, 0, 0, 50, 0, 0, 0,
+ 2,116, 0, 0, 0, 0, 31, 2, 0, 0, 0, 2,111, 0, 2, 0, 0, 0, 8,116,
+109,112,110, 97,109,101, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0, 2,102,
+ 0, 2, 0, 0, 0, 9,111,112,101,110,102,105,108,101, 0, 2, 0, 0, 0, 2,
+119, 0, 2, 0, 0, 0, 6,101,114,114,111,114, 0, 2, 0, 0, 0, 61, 10, 32,
+ 32, 32, 99, 97,110,110,111,116, 32,111,112,101,110, 32,116,101,109,112,111,114,
+ 97,114,121, 32,102,105,108,101, 32,116,111, 32,112,114,111, 99, 99,101,115,115,
+ 32,101,109, 98,101,100,100,101,100, 32, 76,117, 97, 32, 99,111,100,101, 0, 2,
+ 0, 0, 0, 6,119,114,105,116,101, 0, 2, 0, 0, 0, 5,115,101,108,102, 0,
+ 2, 0, 0, 0, 5,116,101,120,116, 0, 2, 0, 0, 0, 10, 99,108,111,115,101,
+102,105,108,101, 0, 2, 0, 0, 0, 8,101,120,101, 99,117,116,101, 0, 2, 0,
+ 0, 0, 9,108,117, 97, 99, 32, 45,111, 32, 0, 2, 0, 0, 0, 2, 32, 0, 2,
+ 0, 0, 0, 7,114,101,109,111,118,101, 0, 2, 0, 0, 0, 3,102,112, 0, 2,
+ 0, 0, 0, 3,114, 98, 0, 2, 0, 0, 0, 2,115, 0, 2, 0, 0, 0, 5,114,
+101, 97,100, 0, 2, 0, 0, 0, 3, 46, 42, 0, 2, 0, 0, 0, 7,111,117,116,
+112,117,116, 0, 2, 0, 0, 0, 35, 10, 32,123, 32, 47, 42, 32, 98,101,103,105,
+110, 32,101,109, 98,101,100,100,101,100, 32,108,117, 97, 32, 99,111,100,101, 32,
+ 42, 47, 10, 0, 2, 0, 0, 0, 35, 32, 32,115,116, 97,116,105, 99, 32,117,110,
+115,105,103,110,101,100, 32, 99,104, 97,114, 32, 66, 91, 93, 32, 61, 32,123, 10,
+ 32, 32, 32, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 5,103,115,117, 98,
+ 0, 2, 0, 0, 0, 4, 40, 46, 41, 0, 4, 0, 0, 0, 51, 0, 0, 0, 10, 64,
+ 99,111,100,101, 46,108,117, 97, 0, 0, 0, 0, 61, 6, 1, 60, 52, 11, 2, 60,
+ 53, 12, 0, 11, 4, 12, 0, 18, 4, 7, 1, 37, 26, 12, 0, 18, 4, 7, 20, 32,
+ 52, 11, 12, 0, 11, 4, 7, 0, 26, 11, 5, 23, 1, 60, 54, 15, 6, 11, 7, 15,
+ 8, 13, 0, 2, 1, 1, 13, 1, 3, 2, 3, 60, 55, 0, 0, 0, 0, 2, 0, 0,
+ 0, 51, 0, 0, 0, 2, 99, 0, 0, 0, 0, 52, 0, 0, 0, 2,101, 0, 0, 0,
+ 0, 9, 2, 0, 0, 0, 2, 99, 0, 2, 0, 0, 0, 2,101, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 2,110, 0, 2, 0, 0, 0,
+ 5, 10, 32, 32, 32, 0, 2, 0, 0, 0, 7,102,111,114,109, 97,116, 0, 2, 0,
+ 0, 0, 7, 37, 51,117, 44, 37,115, 0, 2, 0, 0, 0, 8,115,116,114, 98,121,
+116,101, 0, 2, 0, 0, 0, 6, 32, 32,125, 59, 10, 0, 2, 0, 0, 0, 56, 32,
+ 32,108,117, 97, 95,100,111, 98,117,102,102,101,114, 40, 66, 44,115,105,122,101,
+111,102, 40, 66, 41, 44, 34,116,111,108,117, 97, 58, 32,101,109, 98,101,100,100,
+101,100, 32, 76,117, 97, 32, 99,111,100,101, 34, 41, 59, 0, 2, 0, 0, 0, 36,
+ 32,125, 32, 47, 42, 32,101,110,100, 32,111,102, 32,101,109, 98,101,100,100,101,
+100, 32,108,117, 97, 32, 99,111,100,101, 32, 42, 47, 10, 10, 0, 2, 0, 0, 0,
+ 6,112,114,105,110,116, 0, 4, 0, 0, 0, 63, 0, 0, 0, 10, 64, 99,111,100,
+101, 46,108,117, 97, 0, 0, 0, 0, 52, 6, 3, 60, 64, 15, 2, 13, 1, 11, 3,
+ 42, 2, 0, 1, 60, 65, 15, 2, 13, 1, 11, 4, 42, 13, 0, 18, 6, 42, 11, 7,
+ 42, 2, 0, 1, 60, 66, 15, 2, 13, 1, 11, 8, 42, 13, 2, 42, 2, 0, 1, 60,
+ 67, 0, 0, 0, 0, 3, 0, 0, 0, 63, 0, 0, 0, 5,115,101,108,102, 0, 0,
+ 0, 0, 63, 0, 0, 0, 6,105,100,101,110,116, 0, 0, 0, 0, 63, 0, 0, 0,
+ 6, 99,108,111,115,101, 0, 0, 0, 0, 9, 2, 0, 0, 0, 6,105,100,101,110,
+116, 0, 2, 0, 0, 0, 6, 99,108,111,115,101, 0, 2, 0, 0, 0, 6,112,114,
+105,110,116, 0, 2, 0, 0, 0, 6, 67,111,100,101,123, 0, 2, 0, 0, 0, 11,
+ 32,116,101,120,116, 32, 61, 32, 91, 91, 0, 2, 0, 0, 0, 5,115,101,108,102,
+ 0, 2, 0, 0, 0, 5,116,101,120,116, 0, 2, 0, 0, 0, 4, 93, 93, 44, 0,
+ 2, 0, 0, 0, 2,125, 0, 2, 0, 0, 0, 6, 95, 67,111,100,101, 0, 4, 0,
+ 0, 0, 71, 0, 0, 0, 10, 64, 99,111,100,101, 46,108,117, 97, 0, 0, 0, 0,
+ 40, 4, 1, 60, 72, 13, 0, 11, 1, 15, 2, 26, 60, 73, 15, 3, 13, 0, 15, 4,
+ 2, 0, 2, 60, 74, 15, 5, 13, 0, 2, 0, 1, 60, 75, 13, 0, 1, 1, 60, 76,
+ 0, 0, 0, 0, 1, 0, 0, 0, 71, 0, 0, 0, 2,116, 0, 0, 0, 0, 6, 2,
+ 0, 0, 0, 2,116, 0, 2, 0, 0, 0, 6, 95, 98, 97,115,101, 0, 2, 0, 0,
+ 0, 10, 99,108, 97,115,115, 67,111,100,101, 0, 2, 0, 0, 0, 7,115,101,116,
+116, 97,103, 0, 2, 0, 0, 0, 10,116,111,108,117, 97, 95,116, 97,103, 0, 2,
+ 0, 0, 0, 7, 97,112,112,101,110,100, 0, 2, 0, 0, 0, 5, 67,111,100,101,
+ 0, 4, 0, 0, 0, 80, 0, 0, 0, 10, 64, 99,111,100,101, 46,108,117, 97, 0,
+ 0, 0, 0, 24, 5, 1, 60, 81, 15, 1, 22, 1, 60, 82, 11, 2, 13, 0, 30, 0,
+ 60, 83, 3, 1, 1, 60, 84, 0, 0, 0, 0, 1, 0, 0, 0, 80, 0, 0, 0, 2,
+108, 0, 0, 0, 0, 3, 2, 0, 0, 0, 2,108, 0, 2, 0, 0, 0, 6, 95, 67,
+111,100,101, 0, 2, 0, 0, 0, 5,116,101,120,116, 0,
+};
+
+/* doit.lo */
+static char B17[]={
+ 27, 76,117, 97, 50, 0, 0, 0, 0, 0, 0, 0, 0, 10, 64,100,111,105,116, 46,
+108,117, 97, 0, 0, 0, 1, 92, 6, 0, 60, 17, 15, 0, 18, 1, 52, 38, 60, 18,
+ 15, 4, 15, 0, 18, 1, 2, 2, 1, 60, 19, 13, 0, 44, 52, 14, 60, 20, 15, 5,
+ 11, 6, 13, 1, 42, 2, 0, 1, 50, 2, 60, 21, 5, 2, 50, 2, 60, 22, 60, 25,
+ 15, 0, 18, 7, 44, 52, 43, 60, 26, 15, 0, 18, 1, 52, 22, 60, 27, 15, 0, 11,
+ 7, 15, 8, 15, 0, 18, 1, 11, 9, 11, 10, 2, 1, 3, 26, 50, 11, 60, 29, 15,
+ 5, 11, 11, 2, 0, 1, 60, 30, 50, 2, 60, 31, 60, 33, 15, 13, 15, 0, 18, 7,
+ 2, 1, 1, 60, 35, 15, 0, 18, 1, 52, 9, 60, 36, 15, 4, 2, 0, 0, 50, 2,
+ 60, 37, 60, 39, 15, 0, 18, 12, 52, 4, 60, 41, 1, 1, 60, 43, 15, 0, 18, 14,
+ 52, 38, 60, 44, 15, 15, 15, 0, 18, 14, 2, 2, 1, 60, 45, 13, 1, 44, 52, 14,
+ 60, 46, 15, 5, 11, 6, 13, 2, 42, 2, 0, 1, 50, 2, 60, 47, 5, 2, 50, 2,
+ 60, 48, 60, 50, 15, 0, 18, 16, 52, 11, 60, 51, 13, 0, 20, 17, 2, 0, 1, 50,
+ 38, 60, 53, 13, 0, 20, 18, 2, 0, 1, 60, 54, 13, 0, 20, 19, 2, 0, 1, 60,
+ 55, 13, 0, 20, 20, 2, 0, 1, 60, 56, 13, 0, 20, 21, 2, 0, 1, 60, 57, 60,
+ 59, 15, 0, 18, 14, 52, 9, 60, 60, 15, 15, 2, 0, 0, 50, 2, 60, 61, 60, 64,
+ 15, 0, 18, 16, 44, 52, 66, 60, 65, 15, 0, 18, 22, 52, 54, 60, 66, 15, 15, 15,
+ 0, 18, 22, 2, 2, 1, 60, 67, 13, 1, 44, 52, 14, 60, 68, 15, 5, 11, 6, 13,
+ 2, 42, 2, 0, 1, 50, 2, 60, 69, 60, 70, 13, 0, 20, 23, 2, 0, 1, 60, 71,
+ 15, 15, 2, 0, 0, 5, 2, 50, 2, 60, 72, 50, 2, 60, 73, 0, 0, 0, 0, 0,
+ 0, 0, 0, 24, 2, 0, 0, 0, 6,102,108, 97,103,115, 0, 2, 0, 0, 0, 2,
+102, 0, 2, 0, 0, 0, 3,115,116, 0, 2, 0, 0, 0, 4,109,115,103, 0, 2,
+ 0, 0, 0, 9,114,101, 97,100,102,114,111,109, 0, 2, 0, 0, 0, 6,101,114,
+114,111,114, 0, 2, 0, 0, 0, 2, 35, 0, 2, 0, 0, 0, 2,110, 0, 2, 0,
+ 0, 0, 5,103,115,117, 98, 0, 2, 0, 0, 0, 5, 37, 46, 46, 42, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0, 0, 0, 41, 35,110,111, 32,112, 97, 99,107, 97,103,101,
+ 32,110, 97,109,101, 32,110,111,114, 32,105,110,112,117,116, 32,102,105,108,101,
+ 32,112,114,111,118,105,100,101,100, 0, 2, 0, 0, 0, 2,112, 0, 2, 0, 0,
+ 0, 8, 80, 97, 99,107, 97,103,101, 0, 2, 0, 0, 0, 2,111, 0, 2, 0, 0,
+ 0, 8,119,114,105,116,101,116,111, 0, 2, 0, 0, 0, 2, 80, 0, 2, 0, 0,
+ 0, 6,112,114,105,110,116, 0, 2, 0, 0, 0, 9,112,114,101, 97,109, 98,108,
+101, 0, 2, 0, 0, 0, 8,115,117,112, 99,111,100,101, 0, 2, 0, 0, 0, 9,
+114,101,103,105,115,116,101,114, 0, 2, 0, 0, 0, 11,117,110,114,101,103,105,
+115,116,101,114, 0, 2, 0, 0, 0, 2, 72, 0, 2, 0, 0, 0, 7,104,101, 97,
+100,101,114, 0,
+};
+
+ lua_dobuffer(B1,sizeof(B1),"basic.lo");
+ lua_dobuffer(B2,sizeof(B2),"feature.lo");
+ lua_dobuffer(B3,sizeof(B3),"declaration.lo");
+ lua_dobuffer(B4,sizeof(B4),"container.lo");
+ lua_dobuffer(B5,sizeof(B5),"package.lo");
+ lua_dobuffer(B6,sizeof(B6),"module.lo");
+ lua_dobuffer(B7,sizeof(B7),"class.lo");
+ lua_dobuffer(B8,sizeof(B8),"typedef.lo");
+ lua_dobuffer(B9,sizeof(B9),"define.lo");
+ lua_dobuffer(B10,sizeof(B10),"enumerate.lo");
+ lua_dobuffer(B11,sizeof(B11),"variable.lo");
+ lua_dobuffer(B12,sizeof(B12),"array.lo");
+ lua_dobuffer(B13,sizeof(B13),"function.lo");
+ lua_dobuffer(B14,sizeof(B14),"operator.lo");
+ lua_dobuffer(B15,sizeof(B15),"verbatim.lo");
+ lua_dobuffer(B16,sizeof(B16),"code.lo");
+ lua_dobuffer(B17,sizeof(B17),"doit.lo");
+}
diff --git a/src/lua/tolualua.pkg b/src/lua/tolualua.pkg
new file mode 100644
index 00000000..1694c2c1
--- /dev/null
+++ b/src/lua/tolualua.pkg
@@ -0,0 +1,21 @@
+$[
+$<basic.lua>
+$<feature.lua>
+$<verbatim.lua>
+$<code.lua>
+$<typedef.lua>
+$<container.lua>
+$<package.lua>
+$<module.lua>
+$<define.lua>
+$<enumerate.lua>
+$<declaration.lua>
+$<variable.lua>
+$<array.lua>
+$<function.lua>
+$<operator.lua>
+$<class.lua>
+$<clean.lua>
+$<doit.lua>
+$]
+
diff --git a/src/lua/typedef.lua b/src/lua/typedef.lua
new file mode 100644
index 00000000..1633f3e6
--- /dev/null
+++ b/src/lua/typedef.lua
@@ -0,0 +1,59 @@
+-- tolua: typedef class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: typedef.lua,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Typedef class
+-- Represents a type synonym.
+-- The 'de facto' type replaces the typedef before the
+-- remaining code is parsed.
+-- The following fields are stored:
+-- utype = typedef name
+-- type = 'de facto' type
+-- mod = modifiers to the 'de facto' type
+classTypedef = {
+ utype = '',
+ mod = '',
+ type = ''
+}
+
+-- Print method
+function classTypedef:print (ident,close)
+ print(ident.."Typedef{")
+ print(ident.." utype = '"..self.utype.."',")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.."}"..close)
+end
+
+-- Internal constructor
+function _Typedef (t)
+ t._base = classTypedef
+ settag(t,tolua_tag)
+ appendtypedef(t)
+ return t
+end
+
+-- Constructor
+-- Expects one string representing the type definition.
+function Typedef (s)
+ if strfind(s,'[%*&]') then
+ tolua_error("#invalid typedef: pointers (and references) are not supported")
+ end
+ local t = split(gsub(s,"%s%s*"," ")," ")
+ return _Typedef {
+ utype = t[t.n],
+ type = t[t.n-1],
+ mod = concat(t,1,t.n-2)
+ }
+end
+
+
diff --git a/src/lua/variable.lua b/src/lua/variable.lua
new file mode 100644
index 00000000..310808b8
--- /dev/null
+++ b/src/lua/variable.lua
@@ -0,0 +1,192 @@
+-- tolua: variable class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: variable.lua,v 1.4 2004/06/04 13:42:10 neil Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+-- Variable class
+-- Represents a extern variable or a public member of a class.
+-- Stores all fields present in a declaration.
+classVariable = {
+ _base = classDeclaration,
+}
+
+settag(classVariable,tolua_tag)
+
+-- Print method
+function classVariable:print (ident,close)
+ print(ident.."Variable{")
+ print(ident.." mod = '"..self.mod.."',")
+ print(ident.." type = '"..self.type.."',")
+ print(ident.." ptr = '"..self.ptr.."',")
+ print(ident.." name = '"..self.name.."',")
+ print(ident.." def = '"..self.def.."',")
+ print(ident.." ret = '"..self.ret.."',")
+ print(ident.."}"..close)
+end
+
+-- get variable value
+function classVariable:getvalue (class,static)
+ if class and static then
+ return class..'::'..self.name
+ elseif class then
+ return 'self->'..self.name
+ else
+ return self.name
+ end
+end
+
+-- Write binding functions
+function classVariable:supcode ()
+ local class = self:inclass()
+
+ -- get function ------------------------------------------------
+ if class then
+ output("/* get function:",self.name," of class ",class," */")
+ else
+ output("/* get function:",self.name," */")
+ end
+ self.cgetname = self:cfuncname("toluaI_get")
+ output("static int",self.cgetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare self, if the case
+ local _,_,static = strfind(self.mod,'^%s*(static)')
+ if class and static==nil then
+ output(' ',class,'*','self = ')
+ output('(',class,'*) ')
+ output('tolua_getusertype(tolua_S,1,0);')
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ end
+
+
+ -- check self value
+ if class and static==nil then
+ output(' if (!self) TOLUA_ERR_SELF;');
+ end
+
+ -- return value
+ local t,ct = isbasic(self.type)
+ if t then
+ output(' tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static)..');')
+ else
+ if self.ptr == '&' or self.ptr == '' then
+ output(' tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static)..',',self.tag,');')
+ else
+ output(' tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static)..',',self.tag,');')
+ end
+ end
+ output(' return 1;')
+ output('}')
+ output('\n')
+
+ -- set function ------------------------------------------------
+ if not strfind(self.mod,'const') then
+ if class then
+ output("/* set function:",self.name," of class ",class," */")
+ else
+ output("/* set function:",self.name," */")
+ end
+ self.csetname = self:cfuncname("toluaI_set")
+ output("static int",self.csetname,"(lua_State* tolua_S)")
+ output("{")
+
+ -- declare self, if the case
+ local narg=1
+ if class and static==nil then
+ output(' ',class,'*','self = ')
+ output('(',class,'*) ')
+ output('tolua_getusertype(tolua_S,1,0);')
+ -- check self value
+ output(' if (!self) TOLUA_ERR_SELF;');
+ narg = narg+1
+ elseif static then
+ _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
+ narg = narg+1
+ end
+
+ -- check type
+ output(' if (!'..self:outchecktype(narg)..')')
+ output(' TOLUA_ERR_ASSIGN;')
+
+ -- assign value
+ local ptr = ''
+ if self.ptr~='' then ptr = '*' end
+ output(' ')
+ if class and static then
+ output(class..'::'..self.name)
+ elseif class then
+ output('self->'..self.name)
+ else
+ output(self.name)
+ end
+ local t = isbasic(self.type)
+ output(' = ')
+ if not t and ptr=='' then output('*') end
+ output('((',self.mod,self.type)
+ if not t then
+ output('*')
+ end
+ output(') ')
+ local def = 0
+ if self.def ~= '' then def = self.def end
+ if t then
+ output('tolua_get'..t,'(tolua_S,',narg,',',def,'));')
+ else
+ output('tolua_getusertype(tolua_S,',narg,',',def,'));')
+ end
+ output(' return 0;')
+ output('}')
+ output('\n')
+ end
+
+end
+
+function classVariable:register ()
+ local parent = self:inclass() or self:inmodule()
+ if parent then
+ if self.csetname then
+ output(' tolua_tablevar(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_tablevar(tolua_S,"'..parent..'","'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ else
+ if self.csetname then
+ output(' tolua_globalvar(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');')
+ else
+ output(' tolua_globalvar(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);')
+ end
+ end
+end
+
+function classVariable:unregister ()
+ if self:inclass()==nil and self:inmodule()==nil then
+ output(' lua_getglobals(tolua_S);')
+ output(' lua_pushstring(tolua_S,"',self.lname,'"); lua_pushnil(tolua_S); lua_rawset(tolua_S,-3);')
+ output(' lua_pop(tolua_S,1);')
+ end
+end
+
+
+-- Internal constructor
+function _Variable (t)
+ t._base = classVariable
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the variable declaration.
+function Variable (s)
+ return _Variable (Declaration(s,'var'))
+end
+
+
diff --git a/src/lua/verbatim.lua b/src/lua/verbatim.lua
new file mode 100644
index 00000000..9dae0dc3
--- /dev/null
+++ b/src/lua/verbatim.lua
@@ -0,0 +1,77 @@
+-- tolua: verbatim class
+-- Written by Waldemar Celes
+-- TeCGraf/PUC-Rio
+-- Jul 1998
+-- $Id: verbatim.lua,v 1.2 2001/11/26 23:00:27 darkgod Exp $
+
+-- This code is free software; you can redistribute it and/or modify it.
+-- The software provided hereunder is on an "as is" basis, and
+-- the author has no obligation to provide maintenance, support, updates,
+-- enhancements, or modifications.
+
+
+
+-- Verbatim class
+-- Represents a line translated directed to the binding file.
+-- The following filds are stored:
+-- line = line text
+classVerbatim = {
+ line = '',
+ _base = classFeature,
+}
+settag(classVerbatim,tolua_tag)
+
+-- preamble verbatim
+function classVerbatim:preamble ()
+ if not self.cond then
+ write(self.line)
+ end
+end
+
+-- support code
+function classVerbatim:supcode ()
+ if self.cond then
+ write(self.line)
+ write('\n')
+ end
+end
+
+-- register code
+function classVerbatim:register ()
+ if self.cond then
+ write(self.line)
+ end
+end
+
+
+-- Print method
+function classVerbatim:print (ident,close)
+ print(ident.."Verbatim{")
+ print(ident.." line = '"..self.line.."',")
+ print(ident.."}"..close)
+end
+
+
+-- Internal constructor
+function _Verbatim (t)
+ t._base = classVerbatim
+ settag(t,tolua_tag)
+ append(t)
+ return t
+end
+
+-- Constructor
+-- Expects a string representing the text line
+function Verbatim (l)
+ local c
+ if strsub(l,1,1) == '$' then
+ c = 1
+ l = strsub(l,2)
+ end
+ return _Verbatim {
+ line = l,
+ cond = c
+ }
+end
+
+
diff --git a/src/lua_bind.c b/src/lua_bind.c
new file mode 100644
index 00000000..daa51f70
--- /dev/null
+++ b/src/lua_bind.c
@@ -0,0 +1,652 @@
+/* File: lua_bind.c */
+
+/* Purpose: various lua bindings */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#include "lua.h"
+#include "tolua.h"
+extern lua_State *L;
+
+/*
+ * Get a new magic type
+ */
+magic_power *new_magic_power(int num)
+{
+ magic_power *m_ptr;
+ C_MAKE(m_ptr, num, magic_power);
+ return (m_ptr);
+}
+magic_power *grab_magic_power(magic_power *m_ptr, int num)
+{
+ return (&m_ptr[num]);
+}
+static char *magic_power_info_lua_fct;
+static void magic_power_info_lua(char *p, int power)
+{
+ int oldtop = lua_gettop(L);
+
+ lua_getglobal(L, magic_power_info_lua_fct);
+ tolua_pushnumber(L, power);
+ lua_call(L, 1, 1);
+ strcpy(p, lua_tostring(L, -1));
+ lua_settop(L, oldtop);
+}
+bool get_magic_power_lua(int *sn, magic_power *powers, int max_powers, char *info_fct, int plev, int cast_stat)
+{
+ magic_power_info_lua_fct = info_fct;
+ return (get_magic_power(sn, powers, max_powers, magic_power_info_lua, plev, cast_stat));
+}
+
+bool lua_spell_success(magic_power *spell, int stat, char *oups_fct)
+{
+ int chance;
+ int minfail = 0;
+
+ /* Spell failure chance */
+ chance = spell->fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (p_ptr->lev - spell->min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell->mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell->mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[stat]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+
+ if (oups_fct != NULL)
+ exec_lua(format("%s(%d)", oups_fct, chance));
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/*
+ * Create objects
+ */
+object_type *new_object()
+{
+ object_type *o_ptr;
+ MAKE(o_ptr, object_type);
+ return (o_ptr);
+}
+
+void end_object(object_type *o_ptr)
+{
+ FREE(o_ptr, object_type);
+}
+
+/*
+ * Powers
+ */
+s16b add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff)
+{
+ /* Increase the size */
+ reinit_powers_type(power_max + 1);
+
+ /* Copy the strings */
+ C_MAKE(powers_type[power_max - 1].name, strlen(name) + 1, char);
+ strcpy(powers_type[power_max - 1].name, name);
+ C_MAKE(powers_type[power_max - 1].desc_text, strlen(desc) + 1, char);
+ strcpy(powers_type[power_max - 1].desc_text, desc);
+ C_MAKE(powers_type[power_max - 1].gain_text, strlen(gain) + 1, char);
+ strcpy(powers_type[power_max - 1].gain_text, gain);
+ C_MAKE(powers_type[power_max - 1].lose_text, strlen(lose) + 1, char);
+ strcpy(powers_type[power_max - 1].lose_text, lose);
+
+ /* Copy the other stuff */
+ powers_type[power_max - 1].level = level;
+ powers_type[power_max - 1].cost = cost;
+ powers_type[power_max - 1].stat = stat;
+ powers_type[power_max - 1].diff = diff;
+
+ return (power_max - 1);
+}
+
+static char *lua_item_tester_fct;
+static bool lua_item_tester(object_type* o_ptr)
+{
+ int oldtop = lua_gettop(L);
+ bool ret;
+
+ lua_getglobal(L, lua_item_tester_fct);
+ tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
+ lua_call(L, 1, 1);
+ ret = lua_tonumber(L, -1);
+ lua_settop(L, oldtop);
+ return (ret);
+}
+
+void lua_set_item_tester(int tval, char *fct)
+{
+ if (tval)
+ {
+ item_tester_tval = tval;
+ }
+ else
+ {
+ lua_item_tester_fct = fct;
+ item_tester_hook = lua_item_tester;
+ }
+}
+
+char *lua_object_desc(object_type *o_ptr, int pref, int mode)
+{
+ static char buf[150];
+
+ object_desc(buf, o_ptr, pref, mode);
+ return (buf);
+}
+
+/*
+ * Monsters
+ */
+
+void find_position(int y, int x, int *yy, int *xx)
+{
+ int attempts = 500;
+
+ do
+ {
+ scatter(yy, xx, y, x, 6, 0);
+ }
+ while (!(in_bounds(*yy, *xx) && cave_floor_bold(*yy, *xx)) && --attempts);
+}
+
+static char *summon_lua_okay_fct;
+bool summon_lua_okay(int r_idx)
+{
+ int oldtop = lua_gettop(L);
+ bool ret;
+
+ lua_getglobal(L, lua_item_tester_fct);
+ tolua_pushnumber(L, r_idx);
+ lua_call(L, 1, 1);
+ ret = lua_tonumber(L, -1);
+ lua_settop(L, oldtop);
+ return (ret);
+}
+
+bool lua_summon_monster(int y, int x, int lev, bool friend, char *fct)
+{
+ summon_lua_okay_fct = fct;
+
+ if (!friend)
+ return summon_specific(y, x, lev, SUMMON_LUA);
+ else
+ return summon_specific_friendly(y, x, lev, SUMMON_LUA, TRUE);
+}
+
+/*
+ * Quests
+ */
+s16b add_new_quest(char *name)
+{
+ int i;
+
+ /* Increase the size */
+ reinit_quests(max_q_idx + 1);
+ quest[max_q_idx - 1].type = HOOK_TYPE_LUA;
+ strncpy(quest[max_q_idx - 1].name, name, 39);
+
+ for (i = 0; i < 10; i++)
+ strncpy(quest[max_q_idx - 1].desc[i], "", 39);
+
+ return (max_q_idx - 1);
+}
+
+void desc_quest(int q_idx, int d, char *desc)
+{
+ if (d >= 0 && d < 10)
+ strncpy(quest[q_idx].desc[d], desc, 79);
+}
+
+/*
+ * Misc
+ */
+bool get_com_lua(cptr prompt, int *com)
+{
+ char c;
+
+ if (!get_com(prompt, &c)) return (FALSE);
+ *com = c;
+ return (TRUE);
+}
+
+/* Spell schools */
+s16b new_school(int i, cptr name, s16b skill)
+{
+ schools[i].name = string_make(name);
+ schools[i].skill = skill;
+ return (i);
+}
+
+s16b new_spell(int i, cptr name)
+{
+ school_spells[i].name = string_make(name);
+ school_spells[i].level = 0;
+ school_spells[i].level = 0;
+ return (i);
+}
+
+spell_type *grab_spell_type(s16b num)
+{
+ return (&school_spells[num]);
+}
+
+school_type *grab_school_type(s16b num)
+{
+ return (&schools[num]);
+}
+
+/* Change this fct if I want to switch to learnable spells */
+s32b lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus)
+{
+ s32b tmp;
+
+ tmp = lvl - ((school_spells[s].skill_level - 1) * (SKILL_STEP / 10));
+
+ if (tmp >= (SKILL_STEP / 10)) /* We require at least one spell level */
+ tmp += bonus;
+
+ tmp = (tmp * (max * (SKILL_STEP / 10)) / (SKILL_MAX / 10));
+
+ if (tmp < 0) /* Shift all negative values, so they map to appropriate integer */
+ tmp -= SKILL_STEP / 10 - 1;
+
+ /* Now, we can safely divide */
+ lvl = tmp / (SKILL_STEP / 10);
+
+ if (lvl < min)
+ lvl = min;
+
+ return lvl;
+}
+
+s32b lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat)
+{
+ int minfail;
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (level - 1);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (chance < 0) chance = 0;
+ if (mana > cur_mana)
+ {
+ chance += 15 * (mana - cur_mana);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[stat]];
+
+ /*
+ * Non mage characters never get too good
+ */
+ if (!(has_ability(AB_PERFECT_CASTING)))
+ {
+ if (minfail < 5) minfail = 5;
+ }
+
+ /* Hack -- Priest prayer penalty for "edged" weapons -DGK */
+ if ((forbid_non_blessed()) && (p_ptr->icky_wield)) chance += 25;
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+s32b lua_spell_device_chance(s32b chance, int level, int base_level)
+{
+ int minfail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= (level - 1);
+
+ /* Extract the minimum failure rate */
+ minfail = 15 - get_skill_scale(SKILL_DEVICE, 25);
+ if (minfail < 0) minfail = 0;
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+/* Cave */
+cave_type *lua_get_cave(int y, int x)
+{
+ return (&(cave[y][x]));
+}
+
+void set_target(int y, int x)
+{
+ target_who = -1;
+ target_col = x;
+ target_row = y;
+}
+
+void get_target(int dir, int *y, int *x)
+{
+ int ty, tx;
+
+ /* Use the given direction */
+ tx = p_ptr->px + (ddx[dir] * 100);
+ ty = p_ptr->py + (ddy[dir] * 100);
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ *y = ty;
+ *x = tx;
+}
+
+/* Level gen */
+void get_map_size(bool full_text, char *name, int *ysize, int *xsize)
+{
+ *xsize = 0;
+ *ysize = 0;
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file_full = TRUE;
+ if (full_text)
+ process_dungeon_file(name, "embeded map script", ysize, xsize, cur_hgt, cur_wid, TRUE);
+ else
+ process_dungeon_file(NULL, name, ysize, xsize, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+}
+
+void load_map(bool full_text, char *name, int *y, int *x)
+{
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ if (full_text)
+ process_dungeon_file(name, "embeded map script", y, x, cur_hgt, cur_wid, TRUE);
+ else
+ process_dungeon_file(NULL, name, y, x, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+}
+
+/* Allow lua to use a temporary file */
+static char lua_temp_name[1025];
+static bool lua_temp_file_alloc = FALSE;
+void lua_make_temp_file()
+{
+ if (lua_temp_file_alloc) return;
+
+ if (path_temp(lua_temp_name, 1024)) return;
+
+ /* Open a new file */
+ hook_file = my_fopen(lua_temp_name, "w");
+ lua_temp_file_alloc = TRUE;
+}
+
+void lua_close_temp_file()
+{
+ /* Close the file */
+ my_fclose(hook_file);
+}
+
+void lua_end_temp_file()
+{
+ /* Remove the file */
+ fd_kill(lua_temp_name);
+ lua_temp_file_alloc = FALSE;
+}
+
+cptr lua_get_temp_name()
+{
+ return lua_temp_name;
+}
+
+bool alloc_room(int by0, int bx0, int ysize, int xsize, int *y1, int *x1, int *y2, int *x2)
+{
+ int xval, yval, x, y;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ *y1 = yval - ysize / 2;
+ *x1 = xval - xsize / 2;
+ *y2 = yval + (ysize) / 2;
+ *x2 = xval + (xsize) / 2;
+
+ /* Place a full floor under the room */
+ for (y = *y1 - 1; y <= *y2 + 1; y++)
+ {
+ for (x = *x1 - 1; x <= *x2 + 1; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ c_ptr->info |= (CAVE_ROOM);
+ c_ptr->info |= (CAVE_GLOW);
+ }
+ }
+ return TRUE;
+}
+
+
+/* Files */
+void lua_print_hook(cptr str)
+{
+ fprintf(hook_file, str);
+}
+
+
+/*
+ * Finds a good random bounty monster
+ * Im too lazy to write it in lua since the lua API for monsters is not very well yet
+ */
+
+/*
+ * Hook for bounty monster selection.
+ */
+static bool lua_mon_hook_bounty(int r_idx)
+{
+ monster_race* r_ptr = &r_info[r_idx];
+
+
+ /* Reject uniques */
+ if (r_ptr->flags1 & RF1_UNIQUE) return (FALSE);
+
+ /* Reject those who cannot leave anything */
+ if (!(r_ptr->flags9 & RF9_DROP_CORPSE)) return (FALSE);
+
+ /* Accept only monsters that can be generated */
+ if (r_ptr->flags9 & RF9_SPECIAL_GENE) return (FALSE);
+ if (r_ptr->flags9 & RF9_NEVER_GENE) return (FALSE);
+
+ /* Reject pets */
+ if (r_ptr->flags7 & RF7_PET) return (FALSE);
+
+ /* Reject friendly creatures */
+ if (r_ptr->flags7 & RF7_FRIENDLY) return (FALSE);
+
+ /* Accept only monsters that are not breeders */
+ if (r_ptr->flags4 & RF4_MULTIPLY) return (FALSE);
+
+ /* Forbid joke monsters */
+ if (r_ptr->flags8 & RF8_JOKEANGBAND) return (FALSE);
+
+ /* Accept only monsters that are not good */
+ if (r_ptr->flags3 & RF3_GOOD) return (FALSE);
+
+ /* The rest are acceptable */
+ return (TRUE);
+}
+
+int lua_get_new_bounty_monster(int lev)
+{
+ int r_idx;
+
+ /*
+ * Set up the hooks -- no bounties on uniques or monsters
+ * with no corpses
+ */
+ get_mon_num_hook = lua_mon_hook_bounty;
+ get_mon_num_prep();
+
+ /* Set up the quest monster. */
+ r_idx = get_mon_num(lev);
+
+ /* Undo the filters */
+ get_mon_num_hook = NULL;
+ get_mon_num_prep();
+
+ return r_idx;
+}
+
+/*
+ * Some misc functions
+ */
+char *lua_input_box(cptr title, int max)
+{
+ static char buf[80];
+ int wid, hgt;
+
+ strcpy(buf, "");
+ Term_get_size(&wid, &hgt);
+ if (!input_box(title, hgt / 2, wid / 2, buf, (max > 79) ? 79 : max))
+ return "";
+ return buf;
+}
+
+char lua_msg_box(cptr title)
+{
+ int wid, hgt;
+
+ Term_get_size(&wid, &hgt);
+ return msg_box(title, hgt / 2, wid / 2);
+}
+
+list_type *lua_create_list(int size)
+{
+ list_type *l;
+ cptr *list;
+
+ MAKE(l, list_type);
+ C_MAKE(list, size, cptr);
+ l->list = list;
+ return l;
+}
+
+void lua_delete_list(list_type *l, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ string_free(l->list[i]);
+ C_FREE(l->list, size, cptr);
+ FREE(l, list_type);
+}
+
+void lua_add_to_list(list_type *l, int idx, cptr str)
+{
+ l->list[idx] = string_make(str);
+}
+
+void lua_display_list(int y, int x, int h, int w, cptr title, list_type* list, int max, int begin, int sel, byte sel_color)
+{
+ display_list(y, x, h, w, title, list->list, max, begin, sel, sel_color);
+}
+
+/*
+ * Level generators
+ */
+bool level_generate_script(cptr name)
+{
+ s32b ret = FALSE;
+
+ call_lua("level_generate", "(s)", "d", name, &ret);
+
+ return ret;
+}
+
+void add_scripted_generator(cptr name, bool stairs, bool monsters, bool objects, bool miscs)
+{
+ add_level_generator(name, level_generate_script, stairs, monsters, objects, miscs);
+}
+
+/*
+ * Gods
+ */
+s16b add_new_gods(char *name)
+{
+ int i;
+
+ /* Increase the size */
+ reinit_gods(max_gods + 1);
+ deity_info[max_gods - 1].name = string_make(name);
+
+ for (i = 0; i < 10; i++)
+ strncpy(deity_info[max_gods - 1].desc[i], "", 39);
+
+ return (max_gods - 1);
+}
+
+void desc_god(int g_idx, int d, char *desc)
+{
+ if (d >= 0 && d < 10)
+ strncpy(deity_info[g_idx].desc[d], desc, 79);
+}
diff --git a/src/maid-x11.c b/src/maid-x11.c
new file mode 100644
index 00000000..767413a7
--- /dev/null
+++ b/src/maid-x11.c
@@ -0,0 +1,894 @@
+/* File: maid-x11.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#if defined(USE_X11) || defined(USE_XAW)
+
+/*
+ * This file defines some "XImage" manipulation functions for X11.
+ *
+ * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr).
+ *
+ * BMP format support by Denis Eropkin (denis@dream.homepage.ru).
+ *
+ * Major fixes and cleanup by Ben Harrison (benh@phial.com).
+ *
+ * This file is designed to be "included" by "main-x11.c" or "main-xaw.c",
+ * which will have already "included" several relevant header files.
+ */
+
+#ifndef IsModifierKey
+
+/*
+ * Keysym macros, used on Keysyms to test for classes of symbols
+ * These were stolen from one of the X11 header files
+ *
+ * Also appears in "main-x11.c".
+ */
+
+#define IsKeypadKey(keysym) \
+(((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
+
+#define IsCursorKey(keysym) \
+(((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
+
+#define IsPFKey(keysym) \
+(((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
+
+#define IsFunctionKey(keysym) \
+(((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
+
+#define IsMiscFunctionKey(keysym) \
+(((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
+
+#define IsModifierKey(keysym) \
+(((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
+
+#endif /* IsModifierKey */
+
+
+/*
+ * Checks if the keysym is a special key or a normal key
+ * Assume that XK_MISCELLANY keysyms are special
+ *
+ * Also appears in "main-x11.c".
+ */
+#define IsSpecialKey(keysym) \
+((unsigned)(keysym) >= 0xFF00)
+
+
+#ifdef SUPPORT_GAMMA
+static bool gamma_table_ready = FALSE;
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * Hack -- Convert an RGB value to an X11 Pixel, or die.
+ */
+static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
+{
+ Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
+
+ char cname[8];
+
+ XColor xcolour;
+
+#ifdef SUPPORT_GAMMA
+ static u16b old_gamma_val = 0;
+
+
+ /* React to change in the gamma value */
+ if (gamma_val != old_gamma_val)
+ {
+ /* Temporarily inactivate the gamma table */
+ gamma_table_ready = FALSE;
+
+ /* Only need to build the table if gamma exists */
+ if (gamma_val)
+ {
+ /* Rebuild the table */
+ build_gamma_table(gamma_val);
+
+ /* We can use gamma_table[] now */
+ gamma_table_ready = TRUE;
+ }
+
+ /* Remember the gamma value */
+ old_gamma_val = gamma_val;
+ }
+
+ /* Hack -- Gamma Correction */
+ if (gamma_table_ready)
+ {
+ red = gamma_table[red];
+ green = gamma_table[green];
+ blue = gamma_table[blue];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Build the color */
+
+ xcolour.red = red * 255 + red;
+ xcolour.green = green * 255 + green;
+ xcolour.blue = blue * 255 + blue;
+ xcolour.flags = DoRed | DoGreen | DoBlue;
+
+ /* Attempt to Allocate the Parsed color */
+ if (!(XAllocColor(dpy, cmap, &xcolour)))
+ {
+ quit_fmt("Couldn't allocate bitmap color '%s'\n", cname);
+ }
+
+ return (xcolour.pixel);
+}
+
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * The Win32 "BITMAPFILEHEADER" type.
+ */
+typedef struct BITMAPFILEHEADER
+{
+ u16b bfType;
+ u32b bfSize;
+ u16b bfReserved1;
+ u16b bfReserved2;
+ u32b bfOffBits;
+}
+BITMAPFILEHEADER;
+
+
+/*
+ * The Win32 "BITMAPINFOHEADER" type.
+ */
+typedef struct BITMAPINFOHEADER
+{
+ u32b biSize;
+ u32b biWidth;
+ u32b biHeight;
+ u16b biPlanes;
+ u16b biBitCount;
+ u32b biCompresion;
+ u32b biSizeImage;
+ u32b biXPelsPerMeter;
+ u32b biYPelsPerMeter;
+ u32b biClrUsed;
+ u32b biClrImportand;
+}
+BITMAPINFOHEADER;
+
+/*
+ * The Win32 "RGBQUAD" type.
+ */
+typedef struct RGBQUAD
+{
+ unsigned char b, g, r;
+ unsigned char filler;
+}
+RGBQUAD;
+
+
+/*** Helper functions for system independent file loading. ***/
+
+static byte get_byte(FILE *fff)
+{
+ /* Get a character, and return it */
+ return (getc(fff) & 0xFF);
+}
+
+static void rd_byte(FILE *fff, byte *ip)
+{
+ *ip = get_byte(fff);
+}
+
+static void rd_u16b(FILE *fff, u16b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u16b)(get_byte(fff)) << 8);
+}
+
+static void rd_u32b(FILE *fff, u32b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u32b)(get_byte(fff)) << 8);
+ (*ip) |= ((u32b)(get_byte(fff)) << 16);
+ (*ip) |= ((u32b)(get_byte(fff)) << 24);
+}
+
+
+/*
+ * Read a Win32 BMP file.
+ *
+ * This function replaces the old ReadRaw and RemapColors functions.
+ *
+ * Assumes that the bitmap has a size such that no padding is needed in
+ * various places. Currently only handles bitmaps with 3 to 256 colors.
+ */
+static XImage *ReadBMP(Display *dpy, char *Name)
+{
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+
+ FILE *f;
+
+ BITMAPFILEHEADER fileheader;
+ BITMAPINFOHEADER infoheader;
+
+ XImage *Res = NULL;
+
+ char *Data;
+
+ int ncol;
+
+ int total;
+
+ int i, j;
+
+ u32b x, y;
+
+ unsigned long clr_pixels[256];
+
+
+ /* Open the BMP file */
+ f = fopen(Name, "r");
+
+ /* No such file */
+ if (f == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Read the "BITMAPFILEHEADER" */
+ rd_u16b(f, &(fileheader.bfType));
+ rd_u32b(f, &(fileheader.bfSize));
+ rd_u16b(f, &(fileheader.bfReserved1));
+ rd_u16b(f, &(fileheader.bfReserved2));
+ rd_u32b(f, &(fileheader.bfOffBits));
+
+ /* Read the "BITMAPINFOHEADER" */
+ rd_u32b(f, &(infoheader.biSize));
+ rd_u32b(f, &(infoheader.biWidth));
+ rd_u32b(f, &(infoheader.biHeight));
+ rd_u16b(f, &(infoheader.biPlanes));
+ rd_u16b(f, &(infoheader.biBitCount));
+ rd_u32b(f, &(infoheader.biCompresion));
+ rd_u32b(f, &(infoheader.biSizeImage));
+ rd_u32b(f, &(infoheader.biXPelsPerMeter));
+ rd_u32b(f, &(infoheader.biYPelsPerMeter));
+ rd_u32b(f, &(infoheader.biClrUsed));
+ rd_u32b(f, &(infoheader.biClrImportand));
+
+ /* Verify the header */
+ if (feof(f) ||
+ (fileheader.bfType != 19778) ||
+ (infoheader.biSize != 40))
+ {
+ quit_fmt("Incorrect BMP file format %s", Name);
+ }
+
+ /* The two headers above occupy 54 bytes total */
+ /* The "bfOffBits" field says where the data starts */
+ /* The "biClrUsed" field does not seem to be reliable */
+ /* Compute number of colors recorded */
+ ncol = (fileheader.bfOffBits - 54) / 4;
+
+ for (i = 0; i < ncol; i++)
+ {
+ RGBQUAD clrg;
+
+ /* Read an "RGBQUAD" */
+ rd_byte(f, &(clrg.b));
+ rd_byte(f, &(clrg.g));
+ rd_byte(f, &(clrg.r));
+ rd_byte(f, &(clrg.filler));
+
+ /* Analyze the color */
+ clr_pixels[i] = create_pixel(dpy, clrg.r, clrg.g, clrg.b);
+ }
+
+ /* Determine total bytes needed for image */
+ i = 1;
+ j = (depth - 1) >> 2;
+ while (j >>= 1) i <<= 1;
+ total = infoheader.biWidth * infoheader.biHeight * i;
+
+ /* Allocate image memory */
+ C_MAKE(Data, total, char);
+
+ Res = XCreateImage(dpy, visual, depth, ZPixmap, 0 /*offset*/,
+ Data, infoheader.biWidth, infoheader.biHeight,
+ 8 /*bitmap_pad*/, 0 /*bytes_per_line*/);
+
+ /* Failure */
+ if (Res == NULL)
+ {
+ C_KILL(Data, total, char);
+ fclose(f);
+ return (NULL);
+ }
+
+ for (y = 0; y < infoheader.biHeight; y++)
+ {
+ int y2 = infoheader.biHeight - y - 1;
+
+ for (x = 0; x < infoheader.biWidth; x++)
+ {
+ int ch = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
+
+ if (infoheader.biBitCount == 24)
+ {
+ int c2 = getc(f);
+ int c3 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
+
+ XPutPixel(Res, x, y2, create_pixel(dpy, ch, c2, c3));
+ }
+ else if (infoheader.biBitCount == 8)
+ {
+ XPutPixel(Res, x, y2, clr_pixels[ch]);
+ }
+ else if (infoheader.biBitCount == 4)
+ {
+ XPutPixel(Res, x, y2, clr_pixels[ch / 16]);
+ x++;
+ XPutPixel(Res, x, y2, clr_pixels[ch % 16]);
+ }
+ else
+ {
+ /* Technically 1 bit is legal too */
+ quit_fmt("Illegal biBitCount %d in %s",
+ infoheader.biBitCount, Name);
+ }
+ }
+ }
+
+ fclose(f);
+
+ return Res;
+}
+
+
+/* ========================================================*/
+/* Code for smooth icon rescaling from Uwe Siems, Jan 2000 */
+/* ========================================================*/
+
+/*
+ * to save ourselves some labour, define a maximum expected icon width here:
+ */
+#define MAX_ICON_WIDTH 32
+
+
+/* some static variables for composing and decomposing pixel values into
+ * red, green and blue values
+ */
+static unsigned long redMask, greenMask, blueMask;
+static int redShift, greenShift, blueShift;
+
+
+/*
+ * Use smooth rescaling?
+ */
+static bool smoothRescaling = TRUE;
+
+
+/*
+ * GetScaledRow reads a scan from the given XImage, scales it smoothly
+ * and returns the red, green and blue values in arrays.
+ * The values in this arrays must be divided by a certain value that is
+ * calculated in ScaleIcon.
+ * x, y is the position, iw is the input width and ow the output width
+ * redScan, greenScan and blueScan must be sufficiently sized
+ */
+static void GetScaledRow(XImage *Im, int x, int y, int iw, int ow,
+ unsigned long *redScan, unsigned long *greenScan,
+ unsigned long *blueScan)
+{
+ int xi, si, sifrac, ci, cifrac, addWhole, addFrac;
+ unsigned long pix;
+ int prevRed, prevGreen, prevBlue, nextRed, nextGreen, nextBlue;
+ bool getNextPix;
+
+ if (iw == ow)
+ {
+ /* unscaled */
+ for (xi = 0; xi < ow; xi++)
+ {
+ pix = XGetPixel(Im, x + xi, y);
+ redScan [xi] = (pix >> redShift) & redMask;
+ greenScan [xi] = (pix >> greenShift) & greenMask;
+ blueScan [xi] = (pix >> blueShift) & blueMask;
+ }
+ }
+ else if (iw < ow)
+ {
+ /* scaling by subsampling (grow) */
+ iw--;
+ ow--;
+ /* read first pixel: */
+ pix = XGetPixel(Im, x, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ prevRed = nextRed;
+ prevGreen = nextGreen;
+ prevBlue = nextBlue;
+ /* si and sifrac give the subsampling position: */
+ si = x;
+ sifrac = 0;
+ /* getNextPix tells us, that we need the next pixel */
+ getNextPix = TRUE;
+
+ for (xi = 0; xi <= ow; xi++)
+ {
+ if (getNextPix)
+ {
+ prevRed = nextRed;
+ prevGreen = nextGreen;
+ prevBlue = nextBlue;
+ if (xi < ow)
+ {
+ /* only get next pixel if in same icon */
+ pix = XGetPixel(Im, si + 1, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by ow occurs in ScaleIcon */
+ redScan [xi] = prevRed * (ow - sifrac) + nextRed * sifrac;
+ greenScan [xi] = prevGreen * (ow - sifrac) + nextGreen * sifrac;
+ blueScan [xi] = prevBlue * (ow - sifrac) + nextBlue * sifrac;
+
+ /* advance sampling position: */
+ sifrac += iw;
+ if (sifrac >= ow)
+ {
+ si++;
+ sifrac -= ow;
+ getNextPix = TRUE;
+ }
+ else
+ {
+ getNextPix = FALSE;
+ }
+
+ }
+ }
+ else
+ {
+ /* scaling by averaging (shrink) */
+ /* width of an output pixel in input pixels: */
+ addWhole = iw / ow;
+ addFrac = iw % ow;
+ /* start position of the first output pixel: */
+ si = x;
+ sifrac = 0;
+ /* get first input pixel: */
+ pix = XGetPixel(Im, x, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ for (xi = 0; xi < ow; xi++)
+ {
+ /* find endpoint of the current output pixel: */
+ ci = si + addWhole;
+ cifrac = sifrac + addFrac;
+ if (cifrac >= ow)
+ {
+ ci++;
+ cifrac -= ow;
+ }
+ /* take fraction of current input pixel (starting segment): */
+ redScan[xi] = nextRed * (ow - sifrac);
+ greenScan[xi] = nextGreen * (ow - sifrac);
+ blueScan[xi] = nextBlue * (ow - sifrac);
+ si++;
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ pix = XGetPixel(Im, si, y);
+ redScan[xi] += ((pix >> redShift) & redMask) * ow;
+ greenScan[xi] += ((pix >> greenShift) & greenMask) * ow;
+ blueScan[xi] += ((pix >> blueShift) & blueMask) * ow;
+ si++;
+ }
+ /* add fraction of current input pixel (ending segment): */
+ if (xi < ow - 1)
+ {
+ /* only get next pixel if still in icon: */
+ pix = XGetPixel(Im, si, y);
+ nextRed = (pix >> redShift) & redMask;
+ nextGreen = (pix >> greenShift) & greenMask;
+ nextBlue = (pix >> blueShift) & blueMask;
+ }
+ sifrac = cifrac;
+ if (sifrac > 0)
+ {
+ redScan[xi] += nextRed * sifrac;
+ greenScan[xi] += nextGreen * sifrac;
+ blueScan[xi] += nextBlue * sifrac;
+ }
+ }
+ }
+}
+
+
+/*
+ * PutRGBScan takes arrays for red, green and blue and writes pixel values
+ * according to this values in the XImage-structure. w is the number of
+ * pixels to write and div is the value by which all red/green/blue values
+ * are divided first.
+ */
+static void PutRGBScan(XImage *Im, int x, int y, int w, int div,
+ unsigned long *redScan, unsigned long *greenScan,
+ unsigned long *blueScan)
+{
+ int xi;
+ unsigned long pix;
+ unsigned long adj = div / 2;
+ for (xi = 0; xi < w; xi++)
+ {
+ pix = (((((redScan[xi] + adj) / div) & redMask) << redShift) +
+ ((((greenScan[xi] + adj) / div) & greenMask) << greenShift) +
+ ((((blueScan[xi] + adj) / div) & blueMask) << blueShift));
+ XPutPixel(Im, x + xi, y, pix);
+ }
+}
+
+
+/*
+ * ScaleIcon transfers an area from XImage ImIn, locate (x1,y1) to ImOut,
+ * locate (x2, y2).
+ * Source size is (ix, iy) and destination size is (ox, oy).
+ * It does this by getting icon scan line from GetScaledScan and handling
+ * them the same way as pixels are handled in GetScaledScan.
+ * This even allows icons to be scaled differently in horizontal and
+ * vertical directions (eg. shrink horizontal, grow vertical).
+ */
+static void ScaleIcon(XImage *ImIn, XImage *ImOut,
+ int x1, int y1, int x2, int y2,
+ int ix, int iy, int ox, int oy)
+{
+ int div;
+ int xi, yi, si, sifrac, ci, cifrac, addWhole, addFrac;
+
+ /* buffers for pixel rows: */
+ unsigned long prevRed [MAX_ICON_WIDTH];
+ unsigned long prevGreen [MAX_ICON_WIDTH];
+ unsigned long prevBlue [MAX_ICON_WIDTH];
+ unsigned long nextRed [MAX_ICON_WIDTH];
+ unsigned long nextGreen [MAX_ICON_WIDTH];
+ unsigned long nextBlue [MAX_ICON_WIDTH];
+ unsigned long tempRed [MAX_ICON_WIDTH];
+ unsigned long tempGreen [MAX_ICON_WIDTH];
+ unsigned long tempBlue [MAX_ICON_WIDTH];
+
+ bool getNextRow;
+
+ /* get divider value for the horizontal scaling: */
+ if (ix == ox)
+ div = 1;
+ else if (ix < ox)
+ div = ox - 1;
+ else
+ div = ix;
+
+ if (iy == oy)
+ {
+ /* no scaling needed vertically: */
+ for (yi = 0; yi < oy; yi++)
+ {
+ GetScaledRow(ImIn, x1, y1 + yi, ix, ox,
+ tempRed, tempGreen, tempBlue);
+ PutRGBScan(ImOut, x2, y2 + yi, ox, div,
+ tempRed, tempGreen, tempBlue);
+ }
+ }
+ else if (iy < oy)
+ {
+ /* scaling by subsampling (grow): */
+ iy--;
+ oy--;
+ div *= oy;
+ /* get first row: */
+ GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
+ /* si and sifrac give the subsampling position: */
+ si = y1;
+ sifrac = 0;
+ /* getNextRow tells us, that we need the next row */
+ getNextRow = TRUE;
+ for (yi = 0; yi <= oy; yi++)
+ {
+ if (getNextRow)
+ {
+ for (xi = 0; xi < ox; xi++)
+ {
+ prevRed[xi] = nextRed[xi];
+ prevGreen[xi] = nextGreen[xi];
+ prevBlue[xi] = nextBlue[xi];
+ }
+ if (yi < oy)
+ {
+ /* only get next row if in same icon */
+ GetScaledRow(ImIn, x1, si + 1, ix, ox,
+ nextRed, nextGreen, nextBlue);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by oy occurs in PutRGBScan */
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] = (prevRed[xi] * (oy - sifrac) +
+ nextRed[xi] * sifrac);
+ tempGreen[xi] = (prevGreen[xi] * (oy - sifrac) +
+ nextGreen[xi] * sifrac);
+ tempBlue[xi] = (prevBlue[xi] * (oy - sifrac) +
+ nextBlue[xi] * sifrac);
+ }
+
+ /* write row to output image: */
+ PutRGBScan(ImOut, x2, y2 + yi, ox, div,
+ tempRed, tempGreen, tempBlue);
+
+ /* advance sampling position: */
+ sifrac += iy;
+ if (sifrac >= oy)
+ {
+ si++;
+ sifrac -= oy;
+ getNextRow = TRUE;
+ }
+ else
+ {
+ getNextRow = FALSE;
+ }
+
+ }
+ }
+ else
+ {
+ /* scaling by averaging (shrink) */
+ div *= iy;
+ /* height of a output row in input rows: */
+ addWhole = iy / oy;
+ addFrac = iy % oy;
+ /* start position of the first output row: */
+ si = y1;
+ sifrac = 0;
+ /* get first input row: */
+ GetScaledRow(ImIn, x1, y1, ix, ox, nextRed, nextGreen, nextBlue);
+ for (yi = 0; yi < oy; yi++)
+ {
+ /* find endpoint of the current output row: */
+ ci = si + addWhole;
+ cifrac = sifrac + addFrac;
+ if (cifrac >= oy)
+ {
+ ci++;
+ cifrac -= oy;
+ }
+ /* take fraction of current input row (starting segment): */
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] = nextRed[xi] * (oy - sifrac);
+ tempGreen[xi] = nextGreen[xi] * (oy - sifrac);
+ tempBlue[xi] = nextBlue[xi] * (oy - sifrac);
+ }
+ si++;
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ GetScaledRow(ImIn, x1, si, ix, ox,
+ nextRed, nextGreen, nextBlue);
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] += nextRed[xi] * oy;
+ tempGreen[xi] += nextGreen[xi] * oy;
+ tempBlue[xi] += nextBlue[xi] * oy;
+ }
+ si++;
+ }
+ /* add fraction of current input row (ending segment): */
+ if (yi < oy - 1)
+ {
+ /* only get next row if still in icon: */
+ GetScaledRow(ImIn, x1, si, ix, ox,
+ nextRed, nextGreen, nextBlue);
+ }
+ sifrac = cifrac;
+ for (xi = 0; xi < ox; xi++)
+ {
+ tempRed[xi] += nextRed[xi] * sifrac;
+ tempGreen[xi] += nextGreen[xi] * sifrac;
+ tempBlue[xi] += nextBlue[xi] * sifrac;
+ }
+ /* write row to output image: */
+ PutRGBScan(ImOut, x2, y2 + yi, ox, div,
+ tempRed, tempGreen, tempBlue);
+ }
+ }
+}
+
+
+
+static XImage *ResizeImageSmooth(Display *dpy, XImage *Im,
+ int ix, int iy, int ox, int oy)
+{
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2;
+
+ XImage *Tmp;
+
+ char *Data;
+
+ width1 = Im->width;
+ height1 = Im->height;
+
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
+
+ Tmp = XCreateImage(dpy, visual,
+ Im->depth, ZPixmap, 0, Data, width2, height2,
+ 32, 0);
+
+ /* compute values for decomposing pixel into color values: */
+ redMask = Im->red_mask;
+ redShift = 0;
+ while ((redMask & 1) == 0)
+ {
+ redShift++;
+ redMask >>= 1;
+ }
+ greenMask = Im->green_mask;
+ greenShift = 0;
+ while ((greenMask & 1) == 0)
+ {
+ greenShift++;
+ greenMask >>= 1;
+ }
+ blueMask = Im->blue_mask;
+ blueShift = 0;
+ while ((blueMask & 1) == 0)
+ {
+ blueShift++;
+ blueMask >>= 1;
+ }
+
+ /* scale each icon: */
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
+ {
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
+ {
+ ScaleIcon(Im, Tmp, x1, y1, x2, y2,
+ ix, iy, ox, oy);
+ }
+ }
+
+ return Tmp;
+}
+
+/*
+ * Resize an image. XXX XXX XXX
+ *
+ * Also appears in "main-xaw.c".
+ */
+static XImage *ResizeImage(Display *dpy, XImage *Im,
+ int ix, int iy, int ox, int oy)
+{
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2, Tx, Ty;
+ int *px1, *px2, *dx1, *dx2;
+ int *py1, *py2, *dy1, *dy2;
+
+ XImage *Tmp;
+
+ char *Data;
+
+ if (smoothRescaling && (ix != ox || iy != oy) &&
+ visual->class == TrueColor)
+ {
+ return ResizeImageSmooth(dpy, Im, ix, iy, ox, oy);
+ }
+
+ width1 = Im->width;
+ height1 = Im->height;
+
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ Data = (char *)malloc(width2 * height2 * Im->bits_per_pixel / 8);
+
+ Tmp = XCreateImage(dpy, visual,
+ Im->depth, ZPixmap, 0, Data, width2, height2,
+ 32, 0);
+
+ if (ix > ox)
+ {
+ px1 = &x1;
+ px2 = &x2;
+ dx1 = &ix;
+ dx2 = &ox;
+ }
+ else
+ {
+ px1 = &x2;
+ px2 = &x1;
+ dx1 = &ox;
+ dx2 = &ix;
+ }
+
+ if (iy > oy)
+ {
+ py1 = &y1;
+ py2 = &y2;
+ dy1 = &iy;
+ dy2 = &oy;
+ }
+ else
+ {
+ py1 = &y2;
+ py2 = &y1;
+ dy1 = &oy;
+ dy2 = &iy;
+ }
+
+ Ty = *dy1 / 2;
+
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); )
+ {
+ Tx = *dx1 / 2;
+
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); )
+ {
+ XPutPixel(Tmp, x2, y2, XGetPixel(Im, x1, y1));
+
+ (*px1)++;
+
+ Tx -= *dx2;
+ if (Tx < 0)
+ {
+ Tx += *dx1;
+ (*px2)++;
+ }
+ }
+
+ (*py1)++;
+
+ Ty -= *dy2;
+ if (Ty < 0)
+ {
+ Ty += *dy1;
+ (*py2)++;
+ }
+ }
+
+ return Tmp;
+}
+
+#endif /* USE_GRAPHICS */
+
+#endif /* USE_X11 || USE_XAW */
diff --git a/src/maim-iso.c b/src/maim-iso.c
new file mode 100644
index 00000000..c217ca84
--- /dev/null
+++ b/src/maim-iso.c
@@ -0,0 +1,873 @@
+/*
+ *
+ * This file contains routines for maiming bitmaps as well as other
+ * supplemental routines, all for SDL.
+ *
+ * Copyright 2001 Gregory Velichansky (hmaon@bumba.net)
+ * You may use it under the terms of the standard Angband license (below) or
+ * the GNU General Public License (GPL) Version 2 or greater. (see below)
+ *
+ * The Angband license states:
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies. Other copyrights may also apply.
+ *
+ * The GNU GPL notice:
+ main-sdl.c - SDL (http://libsdl.org) Term implementation for Angband.
+ Copyright (C) 2001 Gregory Velichansky (hmaon@bumba.net)
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, please see
+ http://www.gnu.org/copyleft/gpl.html
+
+
+ Please see the file COPYING for more detail regarding Angband's current
+ license situation.
+*/
+
+
+#include "angband.h"
+
+#if defined(USE_ISO) || defined(USE_LUA_GUI)
+
+
+#include "SDL/SDL.h"
+#include <string.h>
+#include <math.h> /* for scaling blits */
+//#include "bits/nan.h"
+
+/*
+ *
+ * Supplemental SDL bitmap manipulation functions.
+ *
+ * These could be moved to a separate file. In mai?-x11.c, similar routines
+ * are separate from the main display module implementation.
+ *
+ */
+
+
+/* The most pedantic-a%& getpixel and putpixel ever, hopefully. */
+/* There may still be endianness bugs! These will be fixed after adequte testing. XXX XXX XXX */
+inline errr SDL_GetPixel (SDL_Surface *f, Uint32 x, Uint32 y, Uint8 *r, Uint8 *g, Uint8 *b)
+{
+ /*const Uint32 mask[] = {0x0, 0xff, 0xffff, 0xffffff, 0xffffffff};*/
+ Uint32 pixel;
+
+ Uint8 *pp;
+
+ int n; /* general purpose 'n'. */
+
+ if (f == NULL) return -1;
+
+ pp = (Uint8 *) f->pixels;
+
+ if (x >= f->w || y >= f->h) return -1;
+
+ pp += (f->pitch * y);
+
+ pp += (x * f->format->BytesPerPixel);
+
+ /* we do not lock the surface here, it would be inefficient XXX */
+ /* this reads the pixel as though it was a big-endian integer XXX */
+ /* I'm trying to avoid reading part the end of the pixel data by
+ * using a data-type that's larger than the pixels */
+ for (n = 0, pixel = 0; n < f->format->BytesPerPixel; ++n, ++pp)
+ {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ pixel >>= 8;
+ pixel |= *pp << (f->format->BitsPerPixel - 8);
+#else
+pixel |= *pp;
+ pixel <<= 8;
+#endif
+ }
+
+ SDL_GetRGB(pixel, f->format, r, g, b);
+ return 0;
+}
+
+/* This function looks remarkably similar to the one above. Yes, it's cut
+ * and paste. */
+inline errr SDL_PutPixel (SDL_Surface *f, Uint32 x, Uint32 y, Uint8 r, Uint8 g, Uint8 b)
+{
+ Uint32 pixel;
+
+ Uint8 *pp;
+
+ int n;
+
+ if (f == NULL) return -1;
+
+ pp = (Uint8 *) f->pixels;
+
+ if (x >= f->w || y >= f->h) return -1;
+
+ pp += (f->pitch * y);
+
+ pp += (x * f->format->BytesPerPixel);
+
+ pixel = SDL_MapRGB(f->format, r, g, b);
+
+ for (n = 0; n < f->format->BytesPerPixel; ++n, ++pp)
+ {
+ *pp = (Uint8) (pixel & 0xFF);
+ pixel >>= 8;
+ }
+
+ return 0;
+}
+
+
+/* This routine performs a scaling blit. It will shrink and magnify. :) */
+/* It uses floating point arithmetic (because I am lazy) so it's not too fast
+ * but I only intend for it to be used in pre-processing, that is image
+ * processing at load time. It's fast enough for that, at least.
+ * Actually on my machine it IS fast enough to scale fonts and bitmaps
+ * in real-time. :)
+ * This routine uses a weighted average, the weight being based on overlapping
+ * pixel area.
+ */
+inline errr SDL_ScaleBlit(SDL_Surface *src, SDL_Rect *sr, SDL_Surface *dst, SDL_Rect *dr)
+{
+ Uint8 r, g, b;
+
+ float rs, gs, bs; /* sums */
+
+ float area;
+
+ float sx, sy; /* current source x and y */
+ float dsx, dsy; /* source increment, per increment of 1 in destination */
+
+ float wsx, wsy;
+ /* width of source box. Equal to dsx,dsy except when either of then are
+ * smaller than 1. This is a hack for smoother magnification. XXX */
+
+
+ float x, y; /* x and y in sub-area */
+
+ Uint32 tx, ty; /* "to" x and y */
+ Uint32 lx, ly;
+
+ float w, e, n, s; /* some temporary variables, named after orientations */
+
+
+ if (src == NULL || sr == NULL || dst == NULL || dr == NULL) return -1;
+
+ /* these are meaningless and would cause a divide by zero: */
+ if (!dr->w || !dr->h) return -1;
+
+ wsx = dsx = ((float) sr->w) / dr->w;
+ if (wsx < 1) wsx = 1;
+ wsy = dsy = ((float) sr->h) / dr->h;
+ if (wsy < 1) wsy = 1;
+
+ lx = dr->x + dr->w;
+ ly = dr->y + dr->h;
+
+ area = wsx * wsy;
+
+
+
+ for (ty = dr->y, sy = (float)sr->y; ty < ly; ++ty, sy += dsy)
+ {
+ for (tx = dr->x, sx = (float)sr->x; tx < lx; ++tx, sx += dsx)
+ {
+ rs = gs = bs = 0.0;
+ for (y = floor(sy) - 1; ceil(sy + wsy) > y; ++y)
+ {
+ for (x = floor(sx) - 1; ceil(sx + wsx) > x; ++x)
+ {
+ w = (x > sx) ? 0 : sx - x;
+ n = (y > sy) ? 0 : sy - y;
+
+ e = (sx + wsx >= x + 1) ? 1 : sx + wsx - x;
+ s = (sy + wsy >= y + 1) ? 1 : sy + wsy - y;
+
+ if (w > e || s < n ) continue;
+
+#define gsx ((Uint32)x >= sr->x+sr->w ? sr->x+sr->w-1 : (Uint32)x)
+#define gsy ((Uint32)y >= sr->y+sr->h ? sr->y+sr->h-1 : (Uint32)y)
+ SDL_GetPixel (src, gsx, gsy, &r, &g, &b);
+
+
+
+ rs += (e - w) * (s - n) * (float)r;
+ gs += (e - w) * (s - n) * (float)g;
+ bs += (e - w) * (s - n) * (float)b;
+ }
+ }
+ rs /= area;
+ gs /= area;
+ bs /= area;
+
+ if (rs >= 256.0 || gs >= 256.0 || bs > 256.0)
+ {
+ plog("weighted average error!");
+ plog(format("Values: %f, %f, %f\n", rs, gs, bs));
+ /**((char *)0) = 0;*/
+ }
+ if (rs > 255.0) rs = 255.0;
+ if (gs > 255.0) gs = 255.0;
+ if (bs > 255.0) bs = 255.0;
+
+ r = (Uint8)rs;
+ g = (Uint8)gs;
+ b = (Uint8)bs;
+
+ SDL_PutPixel (dst, tx, ty, r, g, b);
+ }
+ }
+
+ return 0;
+#undef gsx
+#undef gsy
+}
+
+
+/* Integer math version of SDL_ScaleBlit().
+ * Where necessary, a number uses the 16 high bits for the integer
+ * and the 16 low bits for the decimal portion.
+ *
+ * eg:
+ * float a = (float) (b >> 16) + (b & 0xFFFF)/65536.0;
+ */
+
+inline Uint32 ifloor(Uint32 i)
+{
+ return i & 0xFFFF0000;
+}
+
+inline Uint32 iceil(Uint32 i)
+{
+ return (i & 0xFFFF) ? i : ifloor(i) + (1 << 16);
+}
+
+
+errr SDL_FastScaleBlit(SDL_Surface *src, SDL_Rect *sr, SDL_Surface *dst, SDL_Rect *dr)
+{
+ Uint8 r, g, b;
+ Uint32 rs, gs, bs; /* sums. */
+
+ /* temp storage for large int multiplies. Uint64 doen't exist anywhere */
+ double farea;
+ Uint32 area;
+
+ Uint32 sx, sy;
+ Uint32 dsx, dsy;
+
+ Uint32 wsx, wsy;
+
+ Uint32 x, y; /* x and y, for sub-area */
+
+ Uint32 tx, ty; /* normal integers */
+ Uint32 lx, ly; /* normal integers */
+
+ Uint32 w, e, n, s; /* temp variables, named after compass directions */
+
+ if (src == NULL || sr == NULL || dst == NULL || dr == NULL) return -1;
+
+ if (!dr->w || !dr->h) return -1;
+
+
+ /* TODO FIXME check for possible overflows! */
+
+ wsx = dsx = (sr->w << 16) / dr->w;
+ if (!(wsx & 0xFFFF0000)) wsx = 1 << 16;
+ wsy = dsy = (sr->h << 16) / dr->h;
+ if (!(wsy & 0xFFFF0000)) wsy = 1 << 16;
+
+ lx = dr->x + dr->w;
+ ly = dr->y + dr->h;
+
+ /* lazy multiplication. Hey, it's only once per blit. :P */
+ farea = ((double)wsx) * ((double)wsy);
+ farea /= (double)(1 << 16);
+ area = (Uint32) farea;
+
+ /* For optimization, those setup routines should be moved into
+ * SDL_ScaleTiledBitmap() for that function.
+ */
+
+ for (ty = dr->y, sy = sr->y << 16; ty < ly; ++ty, sy += dsy)
+ {
+ for (tx = dr->x, sx = sr->x << 16; tx < lx; ++tx, sx += dsx)
+ {
+ rs = gs = bs = 0;
+ for (y = ifloor(sy); iceil(sy + wsy) > y; y += (1 << 16))
+ {
+ for (x = ifloor(sx); iceil(sx + wsx) > x; x += (1 << 16))
+ {
+ w = (x > sx) ? 0 : sx - x;
+ n = (y > sy) ? 0 : sy - y;
+
+ e = (sx + wsx >= x + (1 << 16)) ? 1 << 16 : sx + wsx - x;
+ s = (sy + wsy >= y + (1 << 16)) ? 1 << 16 : sy + wsy - y;
+
+ if (w > e || s < n) continue;
+
+#define gsx ((x >> 16) >= sr->x+sr->w ? sr->x+sr->w-1 : x >> 16)
+#define gsy ((y >> 16) >= sr->y+sr->h ? sr->y+sr->h-1 : y >> 16)
+
+ SDL_GetPixel (src, gsx, gsy, &r, &g, &b);
+
+ rs += ((e - w) >> 8) * ((s - n) >> 8) * r;
+ gs += ((e - w) >> 8) * ((s - n) >> 8) * g;
+ bs += ((e - w) >> 8) * ((s - n) >> 8) * b;
+ }
+ }
+ rs /= area;
+ gs /= area;
+ bs /= area;
+
+ if (rs >= 256 || gs >= 256 || bs >= 256)
+ {
+ plog("fixed point weighted average overflow!");
+ plog(format("Values: %d, %d, %d\n", rs, gs, bs));
+ }
+
+ r = (Uint8) rs;
+ g = (Uint8) gs;
+ b = (Uint8) bs;
+
+ SDL_PutPixel (dst, tx, ty, r, g, b);
+ }
+ }
+
+ return 0;
+#undef gsx
+#undef gsy
+}
+
+
+
+#if 0 /* the procedure above is a more generalized version of the one below */
+
+/* The following is a function to perform a Blit while magnifying */
+/* Anti-aliasing is performed. :) */
+/* It is probably very SLOW on most systems. Use it for pre-processing. XXX */
+/* a Blit while shrinking is handled by a different function */
+errr SDL_StretchBlit(SDL_Surface *src, SDL_Rect *sr, SDL_Surface *dst, SDL_Rect *dr)
+{
+ double sx, sy; /* current source x and y */
+ Uint32 isx, isy; /* temp. values for convenience in calculation code */
+ double dsx, dsy; /* source increment, per increment of 1 in destination */
+ double wx, wy; /* temp. weight values for the color mixing calculations */
+ double weight; /* temp. weight of pixel */
+
+ /* coordinates to get pixels from: ... */
+#undef gsx
+#define gsx (isx >= sr->x+sr->w ? sr->x+sr->w-1 : isx)
+#undef gsy
+#define gsy (isy >= sr->y+sr->h ? sr->y+sr->h-1 : isy)
+
+
+ Uint32 tx, ty; /* "to" x and y. "dx, dy" would be too confusing. */
+ Uint32 lx, ly; /* end x and y in destination, not inclusive */
+
+ double r, g, b; /* temporary values on which we perform calculations */
+ /*double s;*/ /* scale factor calculation thing. gross hack. don't ask. */
+ Uint8 ir, ig, ib; /* same here. */
+
+ if (src == NULL || sr == NULL || dst == NULL || dr == NULL) return -1;
+
+ /* these are meaningless and would cause a divide by zero: */
+ if (!dr->w || !dr->h) return -1;
+
+ dsx = ((double) sr->w) / dr->w;
+ dsy = ((double) sr->h) / dr->h;
+
+ lx = dr->x + dr->w;
+ ly = dr->y + dr->h;
+
+ for (ty = dr->y, sy = (double)sr->y; ty < ly; ++ty, sy += dsy)
+ {
+ for (tx = dr->x, sx = (double)sr->x; tx < lx; ++tx, sx += dsx)
+ {
+ /* here we must consider four pixels and mix them together */
+ /* the further away we are from a pixel, the less weight it has
+ * when we mix in its color. Hence the "1 - hypot(..." etc.
+ * Actually, no. Let's not use hypot().
+ */
+ /*
+ * upper left pixel
+ */
+ wx = ((floor(sx) + 1) - sx);
+ wy = ((floor(sy) + 1) - sy);
+
+ isx = (Uint32) floor(sx);
+ isy = (Uint32) floor(sy);
+
+ if (SDL_GetPixel(src, gsx, gsy, &ir, &ig, &ib)) return -1;
+
+ weight = wx * wy;
+ /* the area of the overlap of our hypothetical and real pixel!!! */
+ if (weight < 1 / 1024.0) weight = 0;
+ r = weight * (double)ir;
+ g = weight * (double)ig;
+ b = weight * (double)ib;
+ /*s = weight * 255.0;*/
+
+ /*
+ * upper right pixel
+ */
+ wx = 1 - wx;
+ isx += 1;
+
+ if (SDL_GetPixel(src, gsx, gsy, &ir, &ig, &ib)) return -1;
+
+ weight = wx * wy;
+ if (weight < 1 / 1024.0) weight = 0;
+ r += weight * (double)ir;
+ g += weight * (double)ig;
+ b += weight * (double)ib;
+ /*s += weight * 255.0;*/
+
+ /*
+ * lower right pixel
+ */
+ wy = 1 - wy;
+ isy += 1;
+
+ if (SDL_GetPixel(src, gsx, gsy, &ir, &ig, &ib)) return -1;
+
+ weight = wx * wy;
+ if (weight < 1 / 1024.0) weight = 0;
+ r += weight * (double)ir;
+ g += weight * (double)ig;
+ b += weight * (double)ib;
+ /*s += weight * 255.0;*/
+
+ /*
+ * lower left pixel
+ */
+ wx = 1 - wx;
+ isx -= 1;
+
+ if (SDL_GetPixel(src, gsx, gsy, &ir, &ig, &ib)) return -1;
+
+ weight = wx * wy;
+ if (weight < 1 / 1024.0) weight = 0;
+ r += weight * (double)ir;
+ g += weight * (double)ig;
+ b += weight * (double)ib;
+ /*s += weight * 255.0;*/
+
+
+ /*
+ r = 255 * (r/s);
+ g = 255 * (g/s);
+ b = 255 * (b/s);
+ */
+
+ if (r >= 256.0 || g >= 256.0 || b > 256.0)
+ {
+ plog("mixing error!");
+ plog(format("Values: %f, %f, %f\n", (double)r, (double)g, (double)b));
+ /**((char *)0) = 0;*/
+ }
+ if (r > 255.0) r = 255.0;
+ if (g > 255.0) g = 255.0;
+ if (b > 255.0) b = 255.0;
+ ir = (Uint8) r;
+ ig = (Uint8) g;
+ ib = (Uint8) b;
+
+ SDL_PutPixel(dst, tx, ty, ir, ig, ib);
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+
+
+
+/* This function will take an SDL_Surface, allocate a new surface to hold
+ * the resized surface, perform the scaling operation, free the old surface
+ * and return the new one. This behaviour is vaguely modeled after C library
+ * string functions. Returns NULL on grievous errors!
+ *
+ * The scaling operation is performed one or more times to accomodate
+ * images comprised by a number of sub-images whose edges must not be blurred
+ * with the edges of adjacent sub-images. (Think fonts and tile sets.)
+ *
+ * If t_oldw and t_oldh are set to src->w and src->h respectively
+ *
+ * t_oldw, t_oldh are the size of the old tiles
+ */
+SDL_Surface *SDL_ScaleTiledBitmap (SDL_Surface *src,
+ Uint32 t_oldw,
+ Uint32 t_oldh,
+ Uint32 t_neww,
+ Uint32 t_newh,
+ int dealloc_src)
+
+{
+ SDL_Surface *dst;
+ SDL_Rect sr, dr;
+ Uint32 x, y;
+ Uint32 nx, ny;
+
+ if (!t_oldw || !t_oldh || !t_neww || !t_newh || !src) return NULL; /*dummy!*/
+
+ if (t_oldw == t_neww && t_oldh == t_newh) return src; /* OK... */
+
+ /* Get the number of tiles in the image.
+ * Any possible clipped tiles at the edges are ignored.
+ */
+ nx = src->w / t_oldw;
+ ny = src->h / t_oldh;
+
+ /* Allocate a new SDL_Surface of appropriate size, with settings otherwise
+ * identical to src.
+ */
+ dst = SDL_CreateRGBSurface(src->flags,
+ nx * t_neww,
+ ny * t_newh,
+ /*src->format->BitsPerPixel,*/
+ 16,
+ src->format->Rmask,
+ src->format->Gmask,
+ src->format->Bmask,
+ src->format->Amask);
+
+
+ for (y = 0; y < ny; ++y)
+ {
+ for (x = 0; x < nx; ++x)
+ {
+ sr.w = t_oldw;
+ sr.h = t_oldh;
+ sr.x = x * t_oldw;
+ sr.y = y * t_oldh;
+
+ dr.w = t_neww;
+ dr.h = t_newh;
+ dr.x = x * t_neww;
+ dr.y = y * t_newh;
+
+ /*printf("%d,%d -> %d,%d (%d,%d -> %d, %d)\n", sr.x, sr.y, dr.x, dr.y, sr.w, sr.h, dr.w, dr.h);*/
+
+ /* scale-blit one tile and check for error
+ * although SDl_ScaleBlit() might not have any errors to return.
+ */
+ if (SDL_FastScaleBlit(src, &sr, dst, &dr)) return NULL;
+ }
+ }
+
+ if (dealloc_src) SDL_FreeSurface(src);
+
+ return dst;
+}
+
+
+/* The following function will extract height and width info from a filename
+ * such as 16x16.xyz or 8X13.bar or even argle8ook16.foo
+ *
+ * I realize now that it's also useful for reading integers out of an argument
+ * such as --fooscale1=2
+ */
+
+errr strtoii(const char *str, Uint32 *w, Uint32 *h)
+{
+ char buf[1024];
+ char *s = buf;
+ char *tok;
+ char *numeric = "0123456789";
+
+ size_t l; /* length of numeric string */
+
+ if (!str || !w || !h) return -1;
+
+ if (strlen(str) < 3) return -1; /* must have room for at least "1x1" */
+
+ strncpy(buf, str, 1023);
+ buf[1023] = '\0';
+
+ tok = strpbrk(buf, numeric);
+ if (!tok) return -1;
+
+ l = strspn(tok, numeric);
+ if (!l) return -1;
+
+ tok[l] = '\0';
+
+ s = tok + l + 1;
+
+ if (!sscanf(tok, "%d", w)) return -1;
+
+ /* next token */
+ tok = strpbrk(s, numeric);
+ if (!tok) return -1;
+
+ l = strspn(tok, numeric);
+ if (!l) return -1;
+
+ tok[l] = '\0';
+ /* no need to set s since this is the last token */
+
+ if (!sscanf(tok, "%d", h)) return -1;
+
+ return 0;
+
+}
+
+
+
+
+char *formatsdlflags(Uint32 flags)
+{
+ return format ("%s%s%s%s%s%s%s%s%s%s (%x)",
+ (flags & SDL_HWSURFACE) ? "SDL_HWSURFACE " : "",
+ (flags & SDL_ANYFORMAT) ? "SDL_ANYFORMAT " : "",
+ (flags & SDL_HWPALETTE) ? "SDL_HWPALETTE " : "",
+ (flags & SDL_DOUBLEBUF) ? "SDL_DOUBLEBUF " : "",
+ (flags & SDL_FULLSCREEN) ? "SDL_FULLSCREEN " : "",
+ (flags & SDL_RESIZABLE) ? "SDL_RESIZABLE " : "",
+ (flags & SDL_HWACCEL) ? "SDL_HWACCEL " : "",
+ (flags & SDL_SRCCOLORKEY) ? "SDL_SRCCOLRKEY " : "",
+ (flags & SDL_RLEACCEL) ? "SDL_RLEACCEL " : "",
+ (flags & SDL_SRCALPHA) ? "SDL_SRCALPHA " : "",
+ flags);
+};
+
+
+
+
+
+
+/* A lot of code for handling keystrokes follow. */
+
+
+typedef struct sdl_keymapt sdl_keymapt;
+
+struct sdl_keymapt
+{
+ SDLKey k; /* what we get from SDL */
+ char *s; /* what we feed to the Term_keypress */
+ char *ctrl; /* what if CTRL is pressed? (NULL if the same) */
+ char *shift; /* what if SHIFT is pressed? */
+};
+
+/* XXX XXX XXX the following keymap sucks. More comments below. */
+sdl_keymapt sdl_keymap[] =
+{
+ /*{SDLK_UP, "", "Oa", "Ox"},
+ {SDLK_DOWN, "", "Ob", "Or"},
+ {SDLK_RIGHT, "", "Oc", "Ot"},
+ {SDLK_LEFT, "", "Od", "Ov"},
+ {SDLK_INSERT, "[2~", "[2^", "Op"},
+ {SDLK_HOME, "[1~", "[1^", "Ow"},
+ {SDLK_END, "[4~", "[4^", "Oq"},
+ {SDLK_PAGEUP, "[5~", "[5^", "Oy"},
+ {SDLK_PAGEDOWN, "[6~", "[6^", "Os"},*/
+ {SDLK_F1, "[[A", NULL, NULL},
+ {SDLK_F2, "[[B", NULL, NULL},
+ {SDLK_F3, "[[C", NULL, NULL},
+ {SDLK_F4, "[[D", NULL, NULL},
+ {SDLK_F5, "[[E", NULL, NULL},
+ {SDLK_F6, "[[17~", NULL, NULL},
+ {SDLK_F7, "[[18~", NULL, NULL},
+ {SDLK_F8, "[[19~", NULL, NULL},
+ {SDLK_F9, "[[20~", NULL, NULL},
+ {SDLK_F10, "[[21~", NULL, NULL},
+ {SDLK_F11, "[[23~", NULL, NULL},
+ {SDLK_F12, "[[24~", NULL, NULL},
+ /* I have no machines with F13, F14, F15. Is that a Sun thing? */
+ {SDLK_F13, "", NULL, NULL},
+ {SDLK_F14, "", NULL, NULL},
+ {SDLK_F15, "", NULL, NULL},
+ {SDLK_RSHIFT, "", NULL, NULL},
+ {SDLK_LSHIFT, "", NULL, NULL},
+ {SDLK_RALT, "", NULL, NULL},
+ {SDLK_LALT, "", NULL, NULL},
+ {SDLK_RCTRL, "", NULL, NULL},
+ {SDLK_LCTRL, "", NULL, NULL},
+ {SDLK_RMETA, "", NULL, NULL},
+ {SDLK_LMETA, "", NULL, NULL},
+ {SDLK_NUMLOCK, "", NULL, NULL},
+ {SDLK_CAPSLOCK, "", NULL, NULL},
+ {SDLK_SCROLLOCK, "", NULL, NULL},
+ {SDLK_LSUPER, "", NULL, NULL},
+ {SDLK_RSUPER, "", NULL, NULL},
+ {SDLK_HELP, "?", NULL, NULL},
+ {SDLK_PRINT, "", NULL, NULL},
+ {SDLK_SYSREQ, "", NULL, NULL},
+ {SDLK_BREAK, "", NULL, NULL},
+ {SDLK_MENU, "", NULL, NULL},
+ {SDLK_POWER, "", NULL, NULL},
+ {SDLK_EURO, "", NULL, NULL},
+ {SDLK_0, "0", NULL, ")"}, /* XXX XXX XXX The CTRL-number keys need to be */
+ {SDLK_1, "1", NULL, "!"}, /* defined since they represent digging for */
+ {SDLK_2, "2", NULL, "@"}, /* some people!. Really, this whole table */
+ {SDLK_3, "3", NULL, "#"}, /* should be replaced with something cleaner */
+ {SDLK_4, "4", NULL, "$"}, /* and an SDL pref file should be created. */
+ {SDLK_5, "5", NULL, "%"},
+ {SDLK_6, "6", NULL, "^"},
+ {SDLK_7, "7", NULL, "&"},
+ {SDLK_8, "8", NULL, "*"},
+ {SDLK_9, "9", NULL, "("},
+ {SDLK_SEMICOLON, ";", NULL, ":"},
+ {SDLK_COMMA, ",", NULL, "<"},
+ {SDLK_PERIOD, ".", NULL, ">"},
+ {SDLK_BACKSLASH, "\\", NULL, "|"},
+ {SDLK_BACKQUOTE, "`", NULL, "~"},
+ {SDLK_LEFTBRACKET, "[", NULL, "{"},
+ {SDLK_RIGHTBRACKET, "]", NULL, "}"},
+ {SDLK_MINUS, "-", NULL, "_"},
+ {SDLK_EQUALS, "=", NULL, "+"},
+ {SDLK_SLASH, "/", NULL, "?"},
+ {SDLK_UNKNOWN, NULL, NULL, NULL} /* terminator */
+};
+
+void Multikeypress(char *k)
+{
+ printf("zog : '%s'\n", k);
+ while (*k) Term_keypress(*k++);
+}
+
+int IsMovement(SDLKey k)
+{
+ switch (k)
+ {
+ case SDLK_UP:
+ case SDLK_DOWN:
+ case SDLK_RIGHT:
+ case SDLK_LEFT:
+ case SDLK_INSERT:
+ case SDLK_HOME:
+ case SDLK_END:
+ case SDLK_PAGEUP:
+ case SDLK_PAGEDOWN:
+ case SDLK_KP0:
+ case SDLK_KP1:
+ case SDLK_KP2:
+ case SDLK_KP3:
+ case SDLK_KP4:
+ case SDLK_KP5:
+ case SDLK_KP6:
+ case SDLK_KP7:
+ case SDLK_KP8:
+ case SDLK_KP9:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ return 1234567; /* all good children go to heaven */
+}
+
+
+char *SDL_keysymtostr(SDL_keysym *ks)
+{
+#ifdef bufsize
+#error bufsize steps on previous define!
+#endif
+#define bufsize 1024
+ int bufused = 0;
+
+ /* I am returning a pointer to the below variable.
+ * I /think/ this is legal but I am not sure! XXX XXX XXX
+ * It certainly seems to work fine, at least under GCC.
+ * It can easily be changed to a pointer passed as an argument.
+ */
+ static char buf[bufsize];
+ Uint8 ch;
+
+ /* cat for strings and app[end] for characters */
+#define sdlkcat(a) strncat(buf,(a),bufsize-bufused-1); bufused+=strlen((a));
+#define sdlkapp(a) if(bufused<bufsize-1) { buf[bufused]=a; buf[bufused+1]='\0'; bufused++; }
+
+ buf[0] = '\0';
+
+ // printf("%c\n", ks->unicode);
+
+
+ if (IsMovement(ks->sym))
+ {
+ sprintf(buf, "%c%s%s%s%s_%lX%c", 31,
+ ks->mod & KMOD_CTRL ? "N" : "",
+ ks->mod & KMOD_SHIFT ? "S" : "",
+ "", /* for future expansion. */
+ ks->mod & KMOD_ALT ? "M" : "",
+ (unsigned long) ks->sym, 13);
+ ch = 0;
+ }
+ else
+ {
+ if (ks->mod & KMOD_ALT)
+ {
+ sdlkapp('');
+ }
+ ch = ks->unicode;
+
+ }
+
+ if (ch) sdlkapp(ch);
+
+ return buf;
+
+
+#undef bufsize
+#undef sdlkcat
+#undef sdlkapp
+
+} /* SDL_keystring */
+
+
+
+/* Cursor hack, for testing of ideas. XXX XXX XXX */
+
+SDL_Surface *sdl_screen_cursor = NULL;
+SDL_Rect sdl_screen_cursor_sr;
+
+
+/* it must be true that w * h * 4 <= maxUint32 */
+
+errr SDL_init_screen_cursor(Uint32 w, Uint32 h)
+{
+ Uint32 i;
+
+ sdl_screen_cursor_sr.x = sdl_screen_cursor_sr.y = 0;
+ sdl_screen_cursor_sr.w = w;
+ sdl_screen_cursor_sr.h = h;
+
+ sdl_screen_cursor = NULL;
+ sdl_screen_cursor = SDL_CreateRGBSurface(SDL_SRCALPHA, w, h, 32,
+ 0xff000000,
+ 0x00ff0000,
+ 0x0000ff00,
+ 0x00000000);
+
+ if (!sdl_screen_cursor) return -1;
+
+ SDL_SetAlpha(sdl_screen_cursor, SDL_SRCALPHA | SDL_RLEACCEL, 0x80);
+ for (i = 0; i < w*h*4; ++i)
+ {
+ ((Uint8 *)(sdl_screen_cursor->pixels))[i] = !(i & 2) ? 0x80 : 0xFF;
+ }
+
+ return 0;
+}
+
+errr SDL_DrawCursor(SDL_Surface *dst, SDL_Rect *dr)
+{
+ if (!dst || !dr || !sdl_screen_cursor) return -1;
+ if (SDL_BlitSurface(sdl_screen_cursor, &sdl_screen_cursor_sr, dst, dr)) return -1;
+ SDL_UpdateRect(dst, dr->x, dr->y, dr->w, dr->h);
+ return 0;
+}
+
+#endif
diff --git a/src/main-ami.c b/src/main-ami.c
new file mode 100644
index 00000000..da267b53
--- /dev/null
+++ b/src/main-ami.c
@@ -0,0 +1,3250 @@
+/*
+
+ File: main-ami.c
+
+ Version: 2.7.9v6 (09.May.96)
+
+ Purpose: Amiga module for Angband with graphics and sound
+
+ Author: Lars Haugseth
+ Email: larshau@ifi.uio.no
+ WWW: http://www.ifi.uio.no/~larshau
+
+*/
+
+#define VERSION "Angband 2.7.9v6"
+
+///{ "includes"
+
+#include "angband.h"
+#include "sound-ami.h"
+#include <exec/memory.h>
+#include <intuition/intuitionbase.h>
+#include <intuition/intuition.h>
+#include <intuition/screens.h>
+#include <devices/inputevent.h>
+#include <graphics/gfxbase.h>
+#include <graphics/modeid.h>
+#include <graphics/scale.h>
+#include <graphics/text.h>
+#include <libraries/asl.h>
+#include <libraries/gadtools.h>
+#include <proto/asl.h>
+#include <proto/exec.h>
+#include <proto/diskfont.h>
+#include <proto/dos.h>
+#include <proto/intuition.h>
+#include <proto/graphics.h>
+#include <proto/keymap.h>
+#include <proto/gadtools.h>
+
+///}
+///{ "macros"
+
+/* Pen number convertion */
+#define PEN( p ) ( penconv[ p ] )
+
+/* Graphics pen number convertion */
+#define GPEN( p ) ( use_pub ? pubpens[ p ] : p )
+
+/* Failure */
+#define FAIL( str ) return ( amiga_fail( str ))
+
+/* Message */
+#define MSG( x, y, txt ) amiga_text( x, y, strlen( txt ), 1, txt );
+
+/* Char and attr under cursor */
+#define CUR_A ( td->t.scr->a[ td->cursor_ypos ][ td->cursor_xpos ] )
+#define CUR_C ( td->t.scr->c[ td->cursor_ypos ][ td->cursor_xpos ] )
+
+///}
+///{ "defines"
+
+/* Color to use for cursor */
+#define CURSOR_PEN 4
+
+/* Size of graphical tile */
+#define TILEW 8
+#define TILEH 8
+
+/* Size of tile image */
+#define GFXW 256
+#define GFXH 256
+#define GFXB 32
+
+/* Size of tombstone image */
+#define TOMW 512
+#define TOMH 168
+#define TOMB 64
+
+/* Filename of tile image */
+#define MGFX "Angband:xtra/gfx/tiles.raw"
+
+/* Filename of tombstone image */
+#define MTOM "Angband:xtra/gfx/tomb.raw"
+
+/* Filename of preferences file */
+#define WPRF "Angband:user/settings.prf"
+
+///}
+///{ "globals"
+
+/* DisplayID specified with option -m */
+char modestr[ 256 ] = "";
+
+/* Font specified with option -f */
+char fontstr[ 32 ] = "";
+
+/* Library bases */
+struct Library *DiskfontBase = NULL;
+struct Library *KeymapBase = NULL;
+struct Library *GadToolsBase = NULL;
+
+/* Term data structure */
+typedef struct term_data
+{
+ term t; /* Term structure */
+ cptr name; /* Name string */
+
+ BYTE use; /* Use this window */
+
+ BYTE cols; /* Number of columns */
+ BYTE rows; /* Number of rows */
+
+ UWORD wx; /* Window x-pos */
+ UWORD wy; /* Window y-pos */
+ UWORD ww; /* Window width */
+ UWORD wh; /* Window height */
+
+ BYTE fw; /* Font width */
+ BYTE fh; /* Font height */
+ BYTE fb; /* Font baseline */
+
+ struct TextFont *font; /* Font pointers */
+
+ BYTE ownf; /* Font is owned by this term */
+
+ struct Window *win; /* Window pointer */
+ struct RastPort *wrp; /* RastPort of window */
+ struct RastPort *rp; /* RastPort of screen or window */
+
+ struct BitMap *gfxbm;
+ struct BitMap *mapbm;
+ struct BitMap *mskbm;
+
+ int gfx_w;
+ int gfx_h;
+
+ int map_w;
+ int map_h;
+ int map_x;
+ int map_y;
+
+ int mpt_w;
+ int mpt_h;
+
+ int cursor_xpos;
+ int cursor_ypos;
+ int cursor_visible;
+ int cursor_lit;
+ int cursor_frame;
+ int cursor_map;
+
+ int notitle;
+ int avoidbar;
+}
+term_data;
+
+/* Term data for all windows */
+static term_data screen;
+static term_data mirror;
+static term_data recall;
+static term_data choice;
+
+/* Window names */
+static char screen_name[] = "Main";
+static char choice_name[] = "Choice";
+static char recall_name[] = "Recall";
+static char mirror_name[] = "Mirror";
+
+/* Screen pointers */
+static struct Screen *amiscr = NULL;
+static struct Screen *pubscr = NULL;
+
+/* Visual info for gadtools menus */
+static APTR *visinfo;
+
+/* TextAttr for screen font */
+static struct TextAttr ScrAttr, *scrattr = &ScrAttr;
+
+/* Screen DisplayID */
+static ULONG scr_m = 0;
+
+/* Last term for cursor */
+static term_data *term_curs = NULL;
+
+/* Temporary string */
+static char tmpstr[ 256 ];
+
+/* Iconify status of windows */
+static int iconified = FALSE;
+
+/* Use intuition menus? */
+static int use_menus = TRUE;
+
+/* Use mouseblanking? */
+static int blankmouse = FALSE;
+
+/* Window input event */
+static struct InputEvent ie;
+
+/* KickStart 3.0+ present */
+static int v39 = FALSE;
+
+/* Use a public screen */
+static int use_pub = FALSE;
+
+/* Public screen lock */
+static int publock = FALSE;
+
+/* Use a backdrop main window */
+static int backdrop = FALSE;
+
+/* Use 32 colors on custom screen */
+static int deep = FALSE;
+
+/* Invisible pointer for blanking */
+static __chip UWORD blankpointer[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/* Pointer visibility status */
+static int pointer_visible = TRUE;
+
+/* Convert textual pens to screen pens */
+static UWORD penconv[ 16 ] =
+{
+ 0, 1, 2, 4, 11, 15, 9, 6, 3, 1, 13, 4, 11, 15, 8, 5
+};
+
+/* Public screen obtained pens */
+static LONG pubpens[ 32 ] =
+{
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/* Default color palette, 16 for graphics, 16 for text */
+static ULONG default_colors[ 32 ] =
+{
+ 0x000000, 0xf0e0d0, 0x808080, 0x505050,
+ 0xe0b000, 0xc0a070, 0x806040, 0x403020,
+ 0x00a0f0, 0x0000f0, 0x000070, 0xf00000,
+ 0x800000, 0x9000b0, 0x006010, 0x60f040,
+
+ 0x000000, 0xffffff, 0xc7c7c7, 0xff9200,
+ 0xff0000, 0x00cd00, 0x0000fe, 0xc86400,
+ 0x8a8a8a, 0xe0e0e0, 0xa500ff, 0xfffd00,
+ 0xff00bc, 0x00ff00, 0x00c8ff, 0xffcc80
+};
+
+/* Palette, 32 bits per gun */
+static ULONG palette32[ 32 * 3 + 2 ];
+
+/* Palette, 4 bits per gun */
+static UWORD palette4[ 32 ];
+
+/* Version string */
+static char ver[] = "$VER: " VERSION " (" __DATE__ ")";
+
+///}
+///{ "sound"
+
+#define NSAMPLES 25
+
+struct AmiSound
+{
+ char *Name;
+ int Volume;
+ int Channel;
+ int Rate;
+ int Repeats;
+ int Memory;
+ struct SoundInfo *Address;
+};
+
+static struct AmiSound sound_data[ NSAMPLES ] =
+{
+ { "intro.8svx", 64, 0, 0, 1, 0, NULL }, /* Intro */
+ { "hit.8svx", 64, 0, 0, 1, 1, NULL }, /* Hit */
+ { "miss.8svx", 64, 3, 0, 1, 1, NULL }, /* Miss */
+ { "flee.8svx", 64, 1, 0, 1, 1, NULL }, /* Flee */
+ { "drop.8svx", 64, 2, 0, 1, 1, NULL }, /* Drop */
+ { "kill.8svx", 64, 1, 0, 1, 1, NULL }, /* Kill */
+ { "study.8svx", 64, 2, 0, 1, 1, NULL }, /* Level */
+ { "death.8svx", 64, 0, 0, 1, 0, NULL }, /* Death */
+ { "study.8svx", 64, 2, 0, 1, 1, NULL }, /* Study */
+ { "teleport.8svx", 64, 3, 0, 1, 1, NULL }, /* Teleport */
+ { "shoot.8svx", 64, 0, 0, 1, 1, NULL }, /* Shoot */
+ { "quaff.8svx", 64, 1, 0, 1, 1, NULL }, /* Quaff */
+ { "zap.8svx", 64, 3, 0, 1, 1, NULL }, /* Zap */
+ { "walk.8svx", 64, 0, 0, 1, 1, NULL }, /* Walk */
+ { "tpother.8svx", 64, 3, 0, 1, 1, NULL }, /* Teleport Other */
+ { "hitwall.8svx", 64, 3, 0, 1, 1, NULL }, /* Hit Wall */
+ { "eat.8svx", 64, 3, 0, 1, 1, NULL }, /* Eat */
+ { "store1.8svx", 64, 0, 0, 1, 0, NULL }, /* Shopkeeper furious */
+ { "store2.8svx", 64, 0, 0, 1, 0, NULL }, /* Shopkeeper angry */
+ { "store3.8svx", 64, 0, 0, 1, 0, NULL }, /* Shopkeeper glad */
+ { "store4.8svx", 64, 0, 0, 1, 0, NULL }, /* Shopkeeper happy */
+ { "dig.8svx", 64, 0, 0, 1, 1, NULL }, /* Dig */
+ { "opendoor.8svx", 64, 0, 0, 1, 1, NULL }, /* Open door */
+ { "closedoor.8svx", 64, 0, 0, 1, 1, NULL }, /* Close door */
+ { "tplevel.8svx", 64, 0, 0, 1, 0, NULL }, /* Teleport level */
+};
+
+static int channel_last[ 4 ] = { -1, -1, -1, -1 };
+
+static int has_sound = FALSE;
+
+///}
+///{ "menus"
+
+#define MENUMAX 256
+
+/* Menu userdata indexes */
+#define MNU_SCALEDMAP 1001
+
+/* Special offset indexes */
+#define MNU_KEYCOM 2001
+#define MNU_CKEYCOM 3001
+#define MNU_OPTION 4001
+#define MNU_HELP 5001
+
+/* Macro for menu userdata keycodes and help */
+#define MKC( c ) (void *)( MNU_KEYCOM + c )
+#define MCC( c ) (void *)( MNU_CKEYCOM + c )
+#define MHL( c ) (void *)( MNU_HELP + c )
+
+struct Menu *menu = NULL;
+
+/* Menu items that comes before the options */
+struct NewMenu pre_item[] =
+{
+ { NM_TITLE, "Options", 0, 0, 0, 0},
+ { 255, 0, 0, 0, 0, 0 }
+};
+
+/* Option menu items, titles of submenus */
+struct NewMenu opt_item[] =
+{
+ { NM_ITEM, "User Interface", 0, 0, 0, 0 },
+ { NM_ITEM, "Disturbance", 0, 0, 0, 0 },
+ { NM_ITEM, "Inventory", 0, 0, 0, 0 },
+ { NM_ITEM, "Game-Play", 0, 0, 0, 0 },
+ { NM_ITEM, "Efficiency", 0, 0, 0, 0 },
+ { NM_ITEM, "Special", 0, 0, 0, 0 },
+ { NM_ITEM, "Cheating", 0, 0, 0, 0 },
+ { 255, 0, 0, 0, 0, 0 }
+};
+
+/* Menu items that comes after the options */
+struct NewMenu post_item[] =
+{
+ /*
+ { NM_ITEM, "Hitpoint Warning", 0, 0, 0, 0 },
+ { NM_SUB, "90%", 0, CHECKIT, ~1, 0 },
+ { NM_SUB, "80%", 0, CHECKIT, ~2, 0 },
+ { NM_SUB, "70%", 0, CHECKIT, ~4, 0 },
+ { NM_SUB, "60%", 0, CHECKIT, ~8, 0 },
+ { NM_SUB, "50%", 0, CHECKIT, ~16, 0 },
+ { NM_SUB, "40%", 0, CHECKIT, ~32, 0 },
+ { NM_SUB, "30%", 0, CHECKIT, ~64, 0 },
+ { NM_SUB, "20%", 0, CHECKIT, ~128, 0 },
+ { NM_SUB, "10%", 0, CHECKIT, ~256, 0 },
+ { NM_SUB, "Off", 0, CHECKIT, ~512, 0 },
+ */
+
+ { NM_TITLE, "Cmd1", 0, 0, 0, 0 },
+ { NM_ITEM, "a Aim a wand", 0, 0, 0, MKC('a') },
+ { NM_ITEM, "b Browse a book", 0, 0, 0, MKC('b') },
+ { NM_ITEM, "c Close a door", 0, 0, 0, MKC('c') },
+ { NM_ITEM, "d Drop an item", 0, 0, 0, MKC('d') },
+ { NM_ITEM, "e Equipment list", 0, 0, 0, MKC('e') },
+ { NM_ITEM, "f Fire an item", 0, 0, 0, MKC('f') },
+ { NM_ITEM, "g Stay still", 0, 0, 0, MKC('g') },
+ { NM_ITEM, "i Inventory list", 0, 0, 0, MKC('i') },
+ { NM_ITEM, "j Jam a door", 0, 0, 0, MKC('j') },
+ { NM_ITEM, "k Destroy an item", 0, 0, 0, MKC('k') },
+ { NM_ITEM, "l Look around", 0, 0, 0, MKC('l') },
+
+ { NM_TITLE, "Cmd2", 0, 0, 0, 0 },
+ { NM_ITEM, "m Cast a spell", 0, 0, 0, MKC('m') },
+ { NM_ITEM, "o Open a door or chest", 0, 0, 0, MKC('o') },
+ { NM_ITEM, "p Pray a prayer", 0, 0, 0, MKC('p') },
+ { NM_ITEM, "q Quaff a potion", 0, 0, 0, MKC('q') },
+ { NM_ITEM, "r Read a scroll", 0, 0, 0, MKC('r') },
+ { NM_ITEM, "s Search for traps/doors", 0, 0, 0, MKC('s') },
+ { NM_ITEM, "t Take off equipment", 0, 0, 0, MKC('t') },
+ { NM_ITEM, "u Use a staff", 0, 0, 0, MKC('u') },
+ { NM_ITEM, "v Throw an item", 0, 0, 0, MKC('v') },
+ { NM_ITEM, "w Wear/wield equipment", 0, 0, 0, MKC('w') },
+ { NM_ITEM, "z Zap a rod", 0, 0, 0, MKC('z') },
+
+ { NM_TITLE, "Cmd3", 0, 0, 0, 0 },
+ { NM_ITEM, "A Activate an artifact", 0, 0, 0, MKC('A') },
+ { NM_ITEM, "B Bash a door", 0, 0, 0, MKC('B') },
+ { NM_ITEM, "C Character description", 0, 0, 0, MKC('C') },
+ { NM_ITEM, "D Disarm a trap", 0, 0, 0, MKC('D') },
+ { NM_ITEM, "E Eat some food", 0, 0, 0, MKC('E') },
+ { NM_ITEM, "F Fuel your lantern/torch", 0, 0, 0, MKC('F') },
+ { NM_ITEM, "G Gain new spells/prayers", 0, 0, 0, MKC('G') },
+ { NM_ITEM, "I Observe an item", 0, 0, 0, MKC('I') },
+ { NM_ITEM, "L Locate player on map", 0, 0, 0, MKC('L') },
+ { NM_ITEM, "M Full dungeon map", 0, 0, 0, MKC('M') },
+ { NM_ITEM, "Q Quit (commit suicide)", 0, 0, 0, MKC('Q') },
+ { NM_ITEM, "R Rest for a period", 0, 0, 0, MKC('R') },
+ { NM_ITEM, "S Toggle search mode", 0, 0, 0, MKC('S') },
+ { NM_ITEM, "T Dig a tunnel", 0, 0, 0, MKC('T') },
+ { NM_ITEM, "V Version info", 0, 0, 0, MKC('V') },
+
+ { NM_TITLE, "Cmd4", 0, 0, 0, 0 },
+ { NM_ITEM, "@ Interact with macros", 0, 0, 0, MKC('@') },
+ { NM_ITEM, "% Interact with visuals", 0, 0, 0, MKC('%') },
+ { NM_ITEM, "& Interact with colors", 0, 0, 0, MKC('&') },
+ { NM_ITEM, "* Target monster or location", 0, 0, 0, MKC('*') },
+ { NM_ITEM, "( Load screen dump", 0, 0, 0, MKC('(') },
+ { NM_ITEM, ") Dump screen dump", 0, 0, 0, MKC(')') },
+ { NM_ITEM, "{ Inscribe an object", 0, 0, 0, MKC('{') },
+ { NM_ITEM, "} Uninscribe an object", 0, 0, 0, MKC('}') },
+ { NM_ITEM, "- Walk (flip pickup)", 0, 0, 0, MKC('-') },
+
+ { NM_TITLE, "Cmd5", 0, 0, 0, 0 },
+ { NM_ITEM, "+ Dig tunnel", 0, 0, 0, MKC('+') },
+ { NM_ITEM, "= Set options", 0, 0, 0, MKC('=') },
+ { NM_ITEM, "; Walk (with pickup)", 0, 0, 0, MKC(';') },
+ { NM_ITEM, ": Take notes", 0, 0, 0, MKC(':') },
+ { NM_ITEM, "\" Enter a user pref command", 0, 0, 0, MKC('\"') },
+ { NM_ITEM, ", Stay still (with pickup)", 0, 0, 0, MKC(',') },
+ { NM_ITEM, "< Go up staircase", 0, 0, 0, MKC('<') },
+ { NM_ITEM, ". Run", 0, 0, 0, MKC('.') },
+ { NM_ITEM, "> Go down staircase", 0, 0, 0, MKC('>') },
+ { NM_ITEM, "/ Identify symbol", 0, 0, 0, MKC('/') },
+ { NM_ITEM, "| Check uniques", 0, 0, 0, MKC('|') },
+ { NM_ITEM, "~ Check artifacts", 0, 0, 0, MKC('~') },
+ { NM_ITEM, "? Help", 0, 0, 0, MKC('?') },
+
+ { NM_TITLE, "Cmd6", 0, 0, 0, 0 },
+ { NM_ITEM, "^f Repeat level feeling", 0, 0, 0, MCC('f') },
+ { NM_ITEM, "^k Quit", 0, 0, 0, MCC('k') },
+ { NM_ITEM, "^p Show previous messages", 0, 0, 0, MCC('p') },
+ { NM_ITEM, "^r Redraw the screen", 0, 0, 0, MCC('r') },
+ { NM_ITEM, "^s Save and don't quit", 0, 0, 0, MCC('s') },
+ { NM_ITEM, "^x Save and quit", 0, 0, 0, MCC('x') },
+ { NM_ITEM, NM_BARLABEL, 0, 0, 0, 0 },
+ { NM_ITEM, "Draw dungeon map", "m", 0, 0, (void *)MNU_SCALEDMAP },
+
+ { NM_TITLE, "Help", 0, 0, 0, 0 },
+ { NM_ITEM, "General Information", 0, 0, 0, MHL('1') },
+ { NM_ITEM, "Creating a Character", 0, 0, 0, MHL('2') },
+ { NM_ITEM, "Exploring the Dungeon", 0, 0, 0, MHL('3') },
+ { NM_ITEM, "Attacking Monsters", 0, 0, 0, MHL('4') },
+ { NM_ITEM, "List of Commands", 0, 0, 0, MHL('5') },
+ { NM_ITEM, "List of Options", 0, 0, 0, MHL('6') },
+ { NM_ITEM, "Version Information", 0, 0, 0, MHL('7') },
+
+ { NM_END, NULL, 0, 0, 0, 0 },
+ { 255, 0, 0, 0, 0, 0 }
+};
+
+/* Menu array */
+struct NewMenu newmenu[ MENUMAX ];
+
+///}
+///{ "protos"
+
+extern void map_info( int y, int x, byte *ap, char *cp );
+extern void center_string( char *buf, cptr str );
+errr init_ami( void );
+static void init_term( term_data *td );
+static term *link_term( term_data *td );
+static void free_term( term_data *td );
+static char *stripstr( char *src, char *dst );
+void request_font( char *str );
+void request_mode( char *str );
+int read_prefs( void );
+static void amiga_open( term *t );
+static void amiga_nuke( term *t );
+static errr amiga_curs( int x, int y );
+static errr amiga_wipe( int x, int y, int n );
+static errr amiga_clear( void );
+static errr amiga_pict( int x, int y, byte a, char c );
+static errr amiga_text( int x, int y, int n, byte a, cptr s );
+static errr amiga_xtra( int n, int v );
+static errr amiga_flush( int v );
+static errr amiga_event( int v );
+static errr amiga_react( int v );
+static errr amiga_fail( char *msg );
+static void cursor_on( term_data *td );
+static void cursor_off( term_data *td );
+void amiga_tomp( void );
+void tomb_str( int y, char *str );
+int load_gfx( void );
+int conv_gfx( void );
+int size_gfx( term_data *td );
+void scale_bitmap( struct BitMap *srcbm, int srcw, int srch, struct BitMap *dstbm, int dstw, int dsth );
+void remap_bitmap( struct BitMap *srcbm, struct BitMap *dstbm, long *pens, int width, int height );
+static void put_gfx( struct RastPort *rp, int x, int y, int chr, int col );
+static void put_gfx_map( term_data *td, int x, int y, int c, int a );
+static void cursor_anim( void );
+void load_palette( void );
+static void amiga_map( void );
+int init_sound( void );
+void free_sound( void );
+static void play_sound( int v );
+struct BitMap *alloc_bitmap( int width, int height, int depth, ULONG flags, struct BitMap *friend );
+void free_bitmap( struct BitMap *bitmap );
+int depth_of_bitmap( struct BitMap *bm );
+int create_menus( void );
+void update_menus( void );
+void handle_menupick( int mnum );
+void handle_rawkey( UWORD code, UWORD qual, APTR addr );
+
+///}
+
+///{ "init_ami()" - Initialize all Amiga spesific stuff
+
+errr init_ami( void )
+{
+ char *s;
+ int i;
+ LONG pen;
+ struct DimensionInfo diminfo;
+ int pw, ph, maxw, maxh, th, barh;
+
+ /* XXX XXX XXX */
+ use_sound = arg_sound;
+ use_graphics = arg_graphics;
+
+ /* Term data pointers */
+ term_data *ts = &screen;
+ term_data *tc = &choice;
+ term_data *tr = &recall;
+ term_data *tm = &mirror;
+
+ /* Clear the term data */
+ init_term( ts );
+ init_term( tc );
+ init_term( tr );
+ init_term( tm );
+
+ /* Always use the main term */
+ ts->use = TRUE;
+
+ /* We *must* have kickstart 37 or later */
+ if ( IntuitionBase->LibNode.lib_Version < 37 )
+ FAIL( "Sorry, this program requires KickStart 2.04 or later." );
+
+ /* Check if we have kickstart 39 or later */
+ v39 = ( IntuitionBase->LibNode.lib_Version >= 39 );
+
+ /* Open diskfont.library */
+ if (( DiskfontBase = OpenLibrary( "diskfont.library", 0 )) == NULL )
+ FAIL( "Unable to open diskfont.library." );
+
+ /* Read preferences file */
+ read_prefs();
+
+ /* Initialize keyboard stuff */
+ ie.ie_NextEvent = NULL;
+ ie.ie_Class = IECLASS_RAWKEY;
+ ie.ie_SubClass = 0;
+ if (( KeymapBase = OpenLibrary( "keymap.library", 36 )) == NULL )
+ FAIL( "Unable to open keymap.library v36+." );
+
+ /* Open gadtools.library */
+ if ( use_menus )
+ {
+ if (( GadToolsBase = OpenLibrary( "gadtools.library", 36 )) == NULL )
+ {
+ use_menus = FALSE;
+ }
+ }
+
+ /* Initialize color palette */
+ for ( i = 0; i < 32; i++ )
+ {
+ /* If undefined, use default palette */
+ if ( color_table[ i ][ 0 ] == 0 &&
+ color_table[ i ][ 1 ] == 0 &&
+ color_table[ i ][ 2 ] == 0 &&
+ color_table[ i ][ 3 ] == 0 )
+ {
+ color_table[ i ][ 0 ] = 1;
+ color_table[ i ][ 1 ] = ( default_colors[ i ] & 0xff0000 ) >> 16;
+ color_table[ i ][ 2 ] = ( default_colors[ i ] & 0x00ff00 ) >> 8;
+ color_table[ i ][ 3 ] = ( default_colors[ i ] & 0x0000ff );
+ }
+ }
+
+ /* Search for prefered screenmode or public screen */
+ if ( strlen( modestr ) > 0 )
+ {
+ /* Convert string to long */
+ scr_m = strtol( modestr, &s, 0 );
+
+ /* It was not a number, so treat it as a public screen name */
+ if ( scr_m == 0 )
+ {
+ /* We need kickstart 3.0+ to use a public screen */
+ if ( !v39 )
+ {
+ FAIL( "Public screen can only be used on kickstart 3.0 or later." );
+ }
+
+ /* Try to lock the named public screen if it isn't already */
+ if ( !pubscr )
+ {
+ pubscr = LockPubScreen( modestr );
+ }
+
+ /* Failed */
+ if ( !pubscr )
+ {
+ sprintf( tmpstr, "Unable to get a lock on screen '%s'.", modestr );
+ FAIL( tmpstr );
+ }
+
+ /* We got a lock now */
+ publock = TRUE;
+
+ scr_m = -1;
+
+ use_pub = TRUE;
+
+ /* Don't blank mouse on public screen */
+ blankmouse = FALSE;
+
+ /* Find suitable pens to use on public screen */
+ for ( i = 0; i < 32; i++ )
+ {
+ pen = ObtainBestPen( pubscr->ViewPort.ColorMap,
+ color_table[ i ][ 1 ] << 24,
+ color_table[ i ][ 2 ] << 24,
+ color_table[ i ][ 3 ] << 24,
+ OBP_Precision, PRECISION_EXACT );
+ if ( pen == -1 )
+ {
+ FAIL( "Unable to obtain suitable pens to use on public screen. ");
+ }
+
+ pubpens[ i ] = pen;
+ }
+
+ for ( i = 0; i < 16; i++ ) penconv[ i ] = (UWORD) pubpens[ i + 16 ];
+ }
+
+ /* Use specified screenmode if available */
+ else
+ {
+ /* Check if requested mode is available */
+ if ( ModeNotAvailable( scr_m )) scr_m = 0;
+ }
+ }
+
+ if ( !use_pub )
+ {
+ /* Extra windows not allowed on custom screen */
+ tc->use = tr->use = tm->use = FALSE;
+ }
+
+ /* Calculate window dimensions */
+ ts->ww = ts->fw * ts->cols; ts->wh = ts->fh * ts->rows;
+ tc->ww = tc->fw * tc->cols; tc->wh = tc->fh * tc->rows;
+ tr->ww = tr->fw * tr->cols; tr->wh = tr->fh * tr->rows;
+ tm->ww = tm->fw * tm->cols; tm->wh = tm->fh * tm->rows;
+
+ /* Find a nice screenmode */
+ if ( scr_m == 0 && v39 )
+ {
+ scr_m = BestModeID(
+ BIDTAG_NominalWidth, ts->ww,
+ BIDTAG_NominalHeight, ts->wh,
+ BIDTAG_DesiredWidth, ts->ww,
+ BIDTAG_DesiredHeight, ts->wh,
+ BIDTAG_Depth, 4,
+ TAG_END );
+ }
+
+ /* Use default screenmode if we don't have any */
+ if ( scr_m == 0 || scr_m == INVALID_ID )
+ {
+ scr_m = ( DEFAULT_MONITOR_ID | HIRES_KEY );
+ }
+
+ /* Open custom screen */
+ if ( !use_pub )
+ {
+ /* Use 32 colors with graphics */
+ if ( use_graphics )
+ {
+ /* Get dimension data for screenmode */
+ if ( GetDisplayInfoData( NULL, (UBYTE *) &diminfo, sizeof( struct DimensionInfo ), DTAG_DIMS, scr_m ))
+ {
+ /* Check if we support deep screens */
+ if ( diminfo.MaxDepth > 4 )
+ {
+ /* Use 32 colors */
+ deep = TRUE;
+
+ /* Use colors 16..31 for text */
+ for ( i = 0; i < 16; i++ ) penconv[ i ] = i + 16;
+ }
+ }
+ }
+
+ /* Use only 16 colors with no graphics */
+ if ( !use_graphics )
+ {
+ /* Use 16 colors */
+ deep = FALSE;
+
+ /* Use colors 0..15 for text */
+ for ( i = 0; i < 16; i++ ) penconv[ i ] = i;
+ }
+
+ if (( amiscr = OpenScreenTags( NULL,
+ SA_Width, ts->ww,
+ SA_Height, ts->wh,
+ SA_Depth, deep ? 5 : 4,
+ SA_DisplayID, scr_m,
+ SA_Font, scrattr,
+ SA_Type, CUSTOMSCREEN,
+ SA_Title, "Angband Screen",
+ SA_ShowTitle, FALSE,
+ SA_Quiet, TRUE,
+ SA_Behind, TRUE,
+ SA_AutoScroll, TRUE,
+ SA_Overscan, OSCAN_TEXT,
+ v39 ? SA_Interleaved : TAG_IGNORE, TRUE,
+ TAG_END )) == NULL)
+ {
+ FAIL( "Unable to open screen." );
+ }
+
+ /* Initialize screen rastport */
+ ts->rp = &amiscr->RastPort;
+ SetRast( ts->rp, PEN( 0 ));
+ SetAPen( ts->rp, 1 );
+ SetBPen( ts->rp, 0 );
+ SetDrMd( ts->rp, JAM2 );
+ SetFont( ts->rp, ts->font );
+
+ /* Always use backdrop window on custom screen */
+ backdrop = TRUE;
+ }
+
+ /* We are using a public screen */
+ else
+ {
+ /* Size of public screen */
+ pw = pubscr->Width;
+ ph = pubscr->Height;
+
+ /* Height difference between a window with or without a title bar */
+ th = pubscr->Font->ta_YSize + 1;
+
+ /* Find width of widest window */
+ maxw = ts->ww;
+ maxw = tc->ww > maxw ? tc->ww : maxw;
+ maxw = tr->ww > maxw ? tr->ww : maxw;
+ maxw = tm->ww > maxw ? tm->ww : maxw;
+ maxw += pubscr->WBorLeft + pubscr->WBorRight;
+
+ /* Find height of tallest window */
+ maxh = ts->wh + ts->notitle ? 0 : th;
+ maxh = ( tc->wh + tc->notitle ? 0 : th ) > maxh ? ( tc->wh + tc->notitle ? 0 : th ) : maxh;
+ maxh = ( tr->wh + tr->notitle ? 0 : th ) > maxh ? ( tr->wh + tr->notitle ? 0 : th ) : maxh;
+ maxh = ( tm->wh + tm->notitle ? 0 : th ) > maxh ? ( tm->wh + tm->notitle ? 0 : th ) : maxh;
+ maxh += pubscr->WBorTop + pubscr->WBorBottom;
+
+ /* Check if the public screen is large enough */
+ if ( pw < maxw || ph < maxh )
+ {
+ sprintf( tmpstr, "Public screen is too small for window (%d x %d).", maxw, maxh );
+ FAIL( tmpstr );
+ }
+
+ /* Use backdrop window if pubscreen is quiet */
+ backdrop = ( pubscr->Flags & SCREENQUIET ) ? TRUE : FALSE;
+
+ /* Calculate screen bar height */
+ barh = pubscr->BarHeight + 1;
+
+ /* Check if windows are to be positioned at the right or bottom */
+ if ( ts->wx == -1 ) ts->wx = pw - 1;
+ if ( ts->wy == -1 ) ts->wy = ph - 1;
+ if ( tc->wx == -1 ) tc->wx = pw - 1;
+ if ( tc->wy == -1 ) tc->wy = ph - 1;
+ if ( tr->wx == -1 ) tr->wx = pw - 1;
+ if ( tr->wy == -1 ) tr->wy = ph - 1;
+ if ( tm->wx == -1 ) tm->wx = pw - 1;
+ if ( tm->wy == -1 ) tm->wy = ph - 1;
+
+ /* Position windows below screen bar if requested */
+ if ( ts->wy == -2 ) ts->wy = barh;
+ if ( tc->wy == -2 ) tc->wy = barh;
+ if ( tr->wy == -2 ) tr->wy = barh;
+ if ( tm->wy == -2 ) tm->wy = barh;
+ }
+
+ /* Get visual info for GadTools */
+ if ( use_menus )
+ {
+ if (( visinfo = GetVisualInfo( use_pub ? pubscr : amiscr, TAG_END )) == NULL )
+ {
+ use_menus = FALSE;
+ }
+ }
+
+ /* Open window, backdrop if on custom screen */
+ if (( ts->win = OpenWindowTags( NULL,
+ WA_Left, ts->wx,
+ WA_Top, ts->wy,
+ WA_InnerWidth, ts->ww,
+ WA_InnerHeight, ts->wh,
+ use_pub ? WA_PubScreen : WA_CustomScreen, use_pub ? pubscr : amiscr,
+ WA_Backdrop, backdrop,
+ WA_Borderless, backdrop,
+ WA_GimmeZeroZero, !backdrop,
+ WA_DragBar, !backdrop && !ts->notitle,
+ WA_DepthGadget, !backdrop && !ts->notitle,
+ WA_NewLookMenus, TRUE,
+ backdrop ? TAG_IGNORE : WA_ScreenTitle, VERSION,
+ ( backdrop || ts->notitle ) ? TAG_IGNORE : WA_Title, ts->name,
+ WA_Activate, TRUE,
+ WA_RMBTrap, !use_menus,
+ WA_ReportMouse, TRUE,
+ WA_IDCMP, IDCMP_RAWKEY | IDCMP_INTUITICKS | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_MENUPICK | IDCMP_MENUVERIFY,
+ TAG_END )) == NULL )
+ {
+ FAIL("Unable to open window.");
+ }
+
+ /* Unlock public screen */
+ if ( publock )
+ {
+ UnlockPubScreen( NULL, pubscr );
+ publock = FALSE;
+ }
+
+ /* Initialize main rastport */
+ ts->wrp = ts->win->RPort;
+ SetRast( ts->wrp, PEN( 0 ));
+ SetAPen( ts->wrp, 1 );
+ SetBPen( ts->wrp, 0 );
+ SetDrMd( ts->wrp, JAM2 );
+ SetFont( ts->wrp, ts->font );
+
+ /* Never use screen's rastport on public screen */
+ if ( use_pub )
+ {
+ ts->rp = ts->wrp;
+ }
+
+ /* Use mirror window? */
+ if ( tm->use )
+ {
+ /* Open mirror window */
+ if (( tm->win = OpenWindowTags( NULL,
+ WA_Left, tm->wx,
+ WA_Top, tm->wy,
+ WA_InnerWidth, tm->ww,
+ WA_InnerHeight, tm->wh,
+ WA_PubScreen, pubscr,
+ WA_GimmeZeroZero, TRUE,
+ WA_DragBar, !tm->notitle,
+ WA_DepthGadget, !tm->notitle,
+ WA_NewLookMenus, TRUE,
+ WA_ScreenTitle, VERSION,
+ tm->notitle ? TAG_IGNORE : WA_Title, tm->name,
+ WA_ReportMouse, TRUE,
+ TAG_END )) == NULL )
+ {
+ FAIL("Unable to open recall window.");
+ }
+
+ /* Initialize mirror rastport */
+ tm->rp = tm->wrp = tm->win->RPort;
+ SetRast( tm->rp, PEN( 0 ));
+ SetAPen( tm->rp, 1 );
+ SetBPen( tm->rp, 0 );
+ SetDrMd( tm->rp, JAM2 );
+ SetFont( tm->rp, tm->font );
+ }
+
+ /* Use recall window? */
+ if ( tr->use )
+ {
+ /* Open recall window */
+ if (( tr->win = OpenWindowTags( NULL,
+ WA_Left, tr->wx,
+ WA_Top, tr->wy,
+ WA_InnerWidth, tr->ww,
+ WA_InnerHeight, tr->wh,
+ WA_PubScreen, pubscr,
+ WA_GimmeZeroZero, TRUE,
+ WA_DragBar, !tr->notitle,
+ WA_DepthGadget, !tr->notitle,
+ WA_NewLookMenus, TRUE,
+ WA_ScreenTitle, VERSION,
+ tr->notitle ? TAG_IGNORE : WA_Title, tr->name,
+ WA_ReportMouse, TRUE,
+ TAG_END )) == NULL )
+ {
+ FAIL("Unable to open recall window.");
+ }
+
+ /* Initialize recall rastport */
+ tr->rp = tr->wrp = tr->win->RPort;
+ SetRast( tr->rp, PEN( 0 ));
+ SetAPen( tr->rp, 1 );
+ SetBPen( tr->rp, 0 );
+ SetDrMd( tr->rp, JAM2 );
+ SetFont( tr->rp, tr->font );
+ }
+
+ /* Use choice window? */
+ if ( tc->use )
+ {
+ /* Open choice window */
+ if (( tc->win = OpenWindowTags( NULL,
+ WA_Left, tc->wx,
+ WA_Top, tc->wy,
+ WA_InnerWidth, tc->ww,
+ WA_InnerHeight, tc->wh,
+ WA_PubScreen, pubscr,
+ WA_GimmeZeroZero, TRUE,
+ WA_DragBar, !tc->notitle,
+ WA_DepthGadget, !tc->notitle,
+ WA_NewLookMenus, TRUE,
+ WA_ScreenTitle, VERSION,
+ tc->notitle ? TAG_IGNORE : WA_Title, tc->name,
+ WA_ReportMouse, TRUE,
+ TAG_END )) == NULL )
+ {
+ FAIL("Unable to open recall window.");
+ }
+
+ /* Initialize choice rastport */
+ tc->rp = tc->wrp = tc->win->RPort;
+ SetRast( tc->rp, PEN( 0 ));
+ SetAPen( tc->rp, 1 );
+ SetBPen( tc->rp, 0 );
+ SetDrMd( tc->rp, JAM2 );
+ SetFont( tc->rp, tc->font );
+ }
+
+ /* Create palette for screen */
+ load_palette();
+
+ /* Link terms with the metaterm */
+ if ( tc->use ) term_choice = link_term( tc );
+ if ( tr->use ) term_recall = link_term( tr );
+ if ( tm->use ) term_mirror = link_term( tm );
+ if ( ts->use ) term_screen = link_term( ts );
+
+ /* Bring main window to front */
+ if ( !backdrop ) WindowToFront( ts->win );
+
+ /* Bring screen to front */
+ ScreenToFront( use_pub ? pubscr : amiscr );
+
+ /* Load and convert graphics */
+ if ( use_graphics )
+ {
+ MSG( 0, 0, "Loading graphics" );
+ if ( !load_gfx() ) FAIL( NULL );
+
+ /* Check if conversion is necessary */
+ if ( use_pub || ( depth_of_bitmap( ts->rp->BitMap ) != depth_of_bitmap( ts->gfxbm )))
+ {
+ MSG( 0, 1, "Remapping graphics" );
+ if ( !conv_gfx() ) FAIL( "Out of memory while remapping graphics." );
+ }
+
+ /* Scale the graphics to fit font sizes */
+ if ( tm->use ) if ( !size_gfx( tm ) ) FAIL( "Out of memory while scaling graphics." );
+ if ( tr->use ) if ( !size_gfx( tr ) ) FAIL( "Out of memory while scaling graphics." );
+ if ( tc->use ) if ( !size_gfx( tc ) ) FAIL( "Out of memory while scaling graphics." );
+ if ( ts->use ) if ( !size_gfx( ts ) ) FAIL( "Out of memory while scaling graphics." );
+
+ /* Use graphics */
+ use_graphics = TRUE;
+ }
+
+ /* Load sound effects */
+ if ( use_sound )
+ {
+ MSG( 0, 2, "Loading sound effects" );
+ init_sound();
+ }
+
+ /* Success */
+ return ( 0 );
+}
+///}
+///{ "init_term()"
+
+static void init_term( term_data *td )
+{
+ /* Set term name */
+ if ( td == &screen ) td->name = screen_name;
+ if ( td == &choice ) td->name = choice_name;
+ if ( td == &recall ) td->name = recall_name;
+ if ( td == &mirror ) td->name = mirror_name;
+
+ /* Term off */
+ td->use = FALSE;
+
+ /* Term size */
+ td->cols = 80;
+ td->rows = 24;
+
+ /* Term dimension */
+ td->wx = 0;
+ td->wy = 0;
+ td->ww = 0;
+ td->wh = 0;
+
+ /* System default font */
+ td->font = GfxBase->DefaultFont;
+ td->ownf = FALSE;
+ td->fw = td->font->tf_XSize;
+ td->fh = td->font->tf_YSize;
+ td->fb = td->font->tf_Baseline;
+
+ /* No window or rastports */
+ td->win = NULL;
+ td->wrp = NULL;
+ td->rp = NULL;
+
+ /* No bitmaps */
+ td->gfxbm = NULL;
+ td->mskbm = NULL;
+ td->mapbm = NULL;
+
+ /* Cursor status */
+ td->cursor_xpos = 0;
+ td->cursor_ypos = 0;
+ td->cursor_visible = FALSE;
+ td->cursor_lit = FALSE;
+ td->cursor_frame = 0;
+ td->cursor_map = FALSE;
+
+ /* Use window title */
+ td->notitle = FALSE;
+}
+
+///}
+///{ "link_term()"
+
+static term *link_term( term_data *td )
+{
+ term *t;
+
+ /* Term pointer */
+ t = &td->t;
+
+ /* Initialize the term */
+ term_init( t, td->cols, td->rows, 256 );
+
+ /* Hooks */
+ t->init_hook = amiga_open;
+ t->nuke_hook = amiga_nuke;
+ t->text_hook = amiga_text;
+ t->pict_hook = amiga_pict;
+ t->wipe_hook = amiga_wipe;
+ t->curs_hook = amiga_curs;
+ t->xtra_hook = amiga_xtra;
+
+ /* We are emulating a hardware cursor */
+ t->soft_cursor = FALSE;
+
+ /* Draw graphical tiles one by one */
+ t->always_text = FALSE;
+ t->always_pict = FALSE;
+ t->higher_pict = TRUE;
+
+ /* Misc. efficiency flags */
+ t->never_bored = TRUE;
+ t->never_frosh = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Remember where we come from */
+ t->data = (vptr) td;
+
+ /* Activate it */
+ Term_activate( t );
+
+ /* Return pointer to the term */
+ return ( t );
+}
+
+///}
+///{ "free_term()"
+
+static void free_term( term_data *td )
+{
+ /* Do we own the font? */
+ if ( td->ownf && td->font ) CloseFont( td->font );
+
+ /* Is the window opened? */
+ if ( td->win ) CloseWindow( td->win );
+
+ /* Free bitmaps */
+ if ( td->gfxbm ) free_bitmap( td->gfxbm );
+ if ( td->mskbm ) free_bitmap( td->mskbm );
+ if ( td->mapbm ) free_bitmap( td->mapbm );
+}
+
+///}
+///{ "stripstr()"
+
+static char *stripstr( char *src, char *dst )
+{
+ int len;
+
+ /* Ignore leading spaces */
+ while ( *src == ' ' ) src++;
+
+ /* Copy string */
+ for ( len = 0; *src != 0; len++ ) *dst++ = *src++;
+ *dst = 0;
+
+ /* Remove trailing spaces */
+ for ( dst--; *dst == ' ' && len; len-- ) *dst-- = 0;
+
+ /* Return pointer to destination */
+ return ( dst );
+}
+
+///}
+///{ "yesno()"
+
+static int yesno( char *str )
+{
+ char tmp[ 256 ];
+ char *s;
+
+ /* Strip spaces around string */
+ stripstr( str, tmp);
+
+ /* Make lowercase */
+ for ( s = tmp; *s != 0; s++ ) *s = tolower( *s );
+
+ return ( !strcmp( tmp, "y" ) || !strcmp( tmp, "yes" ) || !strcmp( tmp, "true" ) || !strcmp( tmp, "on" ));
+}
+
+///}
+///{ "request_font()"
+
+void request_font( char *str )
+{
+ struct Library *AslBase = NULL;
+ struct FontRequester *req = NULL;
+
+ /* Blank string as default */
+ *str = 0;
+
+ /* Open asl.library */
+ if ( AslBase = OpenLibrary ( "asl.library", 37 ))
+ {
+ /* Allocate screenmode requester */
+ if ( req = AllocAslRequestTags( ASL_FontRequest, TAG_DONE ))
+ {
+ /* Open screenmode requester */
+ if ( AslRequestTags( req, ASLFO_FixedWidthOnly, TRUE, TAG_DONE ))
+ {
+ /* Store font name and size */
+ sprintf( str, "%s/%d", req->fo_Attr.ta_Name, req->fo_Attr.ta_YSize );
+ }
+ /* Free requester */
+ FreeAslRequest( req );
+ }
+ /* Close asl.library */
+ CloseLibrary( AslBase );
+ }
+}
+
+///}
+///{ "request_mode()"
+
+void request_mode( char *str )
+{
+ struct Library *AslBase = NULL;
+ struct ScreenModeRequester *req = NULL;
+
+ /* Blank string as default */
+ *str = 0;
+
+ /* Open asl.library */
+ if ( AslBase = OpenLibrary ( "asl.library", 37 ))
+ {
+ /* Allocate screenmode requester */
+ if ( req = AllocAslRequestTags( ASL_ScreenModeRequest, TAG_DONE ))
+ {
+ /* Open screenmode requester */
+ if ( AslRequestTags( req, TAG_DONE ))
+ {
+ /* Store font name and size */
+ sprintf( str, "0x%X", req->sm_DisplayID );
+ }
+ /* Free requester */
+ FreeAslRequest( req );
+ }
+ /* Close asl.library */
+ CloseLibrary( AslBase );
+ }
+}
+
+///}
+///{ "read_prefs()"
+
+int read_prefs( void )
+{
+ BPTR file;
+ char line[ 256 ];
+ char fname[ 256 ];
+ char *tmp, *s;
+ int len;
+ int fsize;
+ int val;
+ struct TextAttr attr;
+ term_data *td;
+
+ /* Open config file */
+ if (( file = Open( WPRF, MODE_OLDFILE )) == NULL )
+ {
+ fprintf( stderr, "\nUnable to open file '%s'.\n", WPRF );
+ return ( FALSE );
+ }
+
+ /* Read next line from file */
+ while ( FGets( file, line, 256 ))
+ {
+ /* Cut off comments */
+ if ( tmp = strchr( line, ';' )) * tmp = 0;
+
+ /* Length of line */
+ len = strlen( line );
+
+ /* Cut off trailing newline and blanks */
+ for ( tmp = line + len - 1; len > 0 && ( *tmp == '\n' || *tmp == ' ' ); len-- ) *tmp-- = 0;
+
+ /* Ignore blank lines */
+ if ( len == 0 ) continue;
+
+ /* Extract term */
+ if ( strncmp( line, "MAIN.", 5 ) == 0 ) td = &screen;
+ else if ( strncmp( line, "CHOICE.", 7 ) == 0 ) td = &choice;
+ else if ( strncmp( line, "RECALL.", 7 ) == 0 ) td = &recall;
+ else if ( strncmp( line, "MIRROR.", 7 ) == 0 ) td = &mirror;
+ else if ( strncmp( line, "SCREEN.", 7 ) != 0 &&
+ strncmp( line, "ANGBAND.", 8 ) != 0 )
+ {
+ printf( "PREFS: Error in line '%s'\n", line );
+ continue;
+ }
+
+ /* Find start of option */
+ tmp = strchr( line, '.' ) + 1;
+
+ /* Ignore blank options */
+ if ( *tmp == 0 ) continue;
+
+ /* Option 'use' - Use this term */
+ if ( !strncmp( tmp, "use", 3 ))
+ {
+ td->use = yesno( tmp + 3 );
+ }
+
+ /* Option 'cols' - Set number of columns for term */
+ else if ( !strncmp( tmp, "cols", 4 ) && sscanf( tmp + 4, "%d", &val )) td->cols = val;
+
+ /* Option 'rows' - Set number of rows for term */
+ else if ( !strncmp( tmp, "rows", 4 ) && sscanf( tmp + 4, "%d", &val )) td->rows = val;
+
+ /* Option 'xpos' - Set horizontal position for window */
+ else if ( !strncmp( tmp, "xpos", 4 ) && sscanf( tmp + 4, "%d", &val )) td->wx = val;
+
+ /* Option 'ypos' - Set vertical position for window */
+ else if ( !strncmp( tmp, "ypos", 4 ) && sscanf( tmp + 4, "%d", &val )) td->wy = val;
+
+ /* Option 'name' - Set window title */
+ else if ( !strncmp( tmp, "title", 5 ))
+ {
+ /* Get parameter */
+ stripstr( tmp + 5, fname );
+
+ /* Make a copy of the title */
+ if ( strlen( fname ) > 0 )
+ {
+ td->name = strdup( fname );
+ }
+
+ /* Don't use a title bar on this window */
+ else
+ {
+ td->notitle = TRUE;
+ }
+
+ continue;
+ }
+
+ /* Option 'font' - Set font for this window */
+ else if ( !strncmp( tmp, "font", 4 ))
+ {
+ /* Get value */
+ stripstr( tmp + 4, fname );
+
+ /* Ask for font? */
+ if ( !strcmp( fname, "?" ))
+ {
+ /* Open font requester */
+ request_font( fname );
+ }
+
+ /* No font specification given */
+ if ( strlen( fname ) == 0 )
+ {
+ /* Main window */
+ if ( td == &screen )
+ {
+ /* System default font */
+ td->font = GfxBase->DefaultFont;
+
+ /* Use default font as screen font */
+ scrattr = NULL;
+ }
+
+ /* Extra window */
+ else
+ {
+ /* Copy main window's font */
+ td->font = screen.font;
+ }
+
+ /* Set font dimensions */
+ td->fw = td->font->tf_XSize;
+ td->fh = td->font->tf_YSize;
+ td->fb = td->font->tf_Baseline;
+
+ /* This font is not opened by us */
+ td->ownf = FALSE;
+
+ /* Next line */
+ continue;
+ }
+
+ /* Get name and size */
+ else
+ {
+ /* Find font name/size delimiter */
+ if (( s = strchr( fname, '/' )) == NULL )
+ {
+ printf( "\nPREFS: Illegal font specification: '%s'.\n", fname );
+ continue;
+ }
+
+ /* End fontname here */
+ *s++ = 0;
+
+ /* Convert size string to integer */
+ fsize = atoi( s );
+
+ /* Make sure the font name ends with .font */
+ if ( !strstr( fname, ".font" )) strcat( fname, ".font" );
+ }
+
+ /* Set font attributes */
+ attr.ta_Name = fname;
+ attr.ta_YSize = fsize;
+ attr.ta_Style = FS_NORMAL;
+ attr.ta_Flags = ( !strcmp( fname, "topaz.font" ) && ( fsize == 8 || fsize == 9 )) ?
+ FPF_ROMFONT : FPF_DISKFONT;
+
+ /* Open font from disk */
+ if ( td->font = OpenDiskFont( &attr ))
+ {
+ /* We own this font */
+ td->ownf = TRUE;
+
+ /* Set font dimensions */
+ td->fw = td->font->tf_XSize;
+ td->fh = td->font->tf_YSize;
+ td->fb = td->font->tf_Baseline;
+
+ /* Copy font attr to screen font */
+ if ( td == &screen )
+ {
+ scrattr->ta_Name = strdup( fname );
+ scrattr->ta_YSize = fsize;
+ scrattr->ta_Style = FS_NORMAL;
+ scrattr->ta_Flags = attr.ta_Flags;
+ }
+ }
+
+ /* The font could not be opened */
+ else
+ {
+ /* Fallback to default font */
+ td->font = GfxBase->DefaultFont;
+
+ /* Use default font as screen font */
+ scrattr = NULL;
+
+ /* Output error message */
+ printf( "\nUnable to open font '%s/%d'.\n", fname, fsize );
+ }
+ }
+
+ /* Option .name - Set public screen name. KS 3.0+ required */
+ else if ( !strncmp( tmp, "name", 4 ) && v39 )
+ {
+ /* Copy name */
+ stripstr( tmp + 4, modestr );
+ }
+
+ /* Option .mode - Set custom screen mode */
+ else if ( !strncmp( tmp, "mode", 4 ))
+ {
+ /* If a public screen was also specified, check if it exist */
+ if ( strlen( modestr ) > 0 )
+ {
+ /* Don't lock it twice */
+ if ( pubscr ) continue;
+
+ /* Try to lock the named public screen */
+ if ( pubscr = LockPubScreen( modestr ))
+ {
+ /* We got a lock now */
+ publock = TRUE;
+
+ /* Screen exist. Skip this option */
+ continue;
+ }
+ }
+
+ /* Get parameter */
+ stripstr( tmp + 4, modestr );
+
+ /* Ask for mode? */
+ if ( !strcmp( modestr, "?" ))
+ {
+ /* Open screenmode requester */
+ request_mode( modestr );
+ }
+ }
+
+ /* Option .blankmouse - Use mouseblanking */
+ else if ( !strncmp( tmp, "blankmouse", 10 ))
+ {
+ blankmouse = yesno( tmp + 10 );
+ }
+
+ /* Option .blankmouse - Use intuition menus */
+ else if ( !strncmp( tmp, "menus", 5 ))
+ {
+ use_menus = yesno( tmp + 5 );
+ }
+
+ else if ( !strncmp( tmp, "sound", 5 ))
+ {
+ use_sound = yesno( tmp + 5 );
+ }
+
+ else if ( !strncmp( tmp, "gfx", 3 ))
+ {
+ use_graphics = yesno( tmp + 5 );
+ }
+
+ /* Unknown option */
+ else
+ {
+ /* Output error message */
+ printf ( "\nPREFS: Unknown option '%s'.\n", tmp );
+ }
+ }
+
+ /* Close the file */
+ Close( file );
+}
+
+///}
+///{ "amiga_nuke()" - Free all allocated resources
+
+static void amiga_nuke( term *t )
+{
+ if ( t == term_screen )
+ {
+ amiga_fail( NULL );
+ /* Flush the output */
+ fflush( stdout );
+ }
+}
+
+///}
+///{ "amiga_open()" - Initialize terminal
+
+static void amiga_open( term *t )
+{
+ /* Nothing to do here */
+}
+
+///}
+///{ "amiga_curs()" - Move the cursor to a new location
+
+static errr amiga_curs( int x, int y )
+{
+ term_data *td = term_curs;
+
+ if ( td->cursor_visible )
+ {
+ cursor_off( td );
+ }
+
+ td->cursor_xpos = x;
+ td->cursor_ypos = y;
+ td->cursor_frame = 0;
+
+ if ( td->cursor_visible )
+ {
+ cursor_on( td );
+ }
+
+ return ( 0 );
+}
+
+///}
+///{ "amiga_wipe()" - Erase a part of a line
+
+static errr amiga_wipe( int x, int y, int n )
+{
+ term_data *td = (term_data*)(Term->data);
+
+ if (( n > 0 ) && !iconified )
+ {
+ /* Erase rectangular area on screen */
+ SetAPen( td->rp, PEN( 0 ) );
+ RectFill( td->rp, x * td->fw, y * td->fh, ( x + n ) * td->fw - 1, ( y + 1 ) * td->fh - 1 );
+ }
+
+ return ( 0 );
+}
+
+///}
+///{ "amiga_clear()" - Clear whole window
+
+static errr amiga_clear( void )
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Fill window with background color */
+ SetRast( td->rp, PEN( 0 ));
+
+ return ( 0 );
+}
+
+///}
+///{ "amiga_pict()" - Place one tile on the screen
+
+static errr amiga_pict( int x, int y, byte a, char c )
+{
+ term_data *td = (term_data*)(Term->data);
+ char s[2];
+
+ /* Graphical tile */
+ if ( a & 0x80 )
+ {
+ put_gfx( td->rp, x, y, c & 0x7f, a & 0x7f );
+ }
+
+ /* Textual character */
+ else
+ {
+ s[0] = c;
+ s[1] = 0;
+ SetAPen( td->rp, PEN( a & 0x0f ));
+ SetBPen( td->rp, PEN( 0 ));
+ Move( td->rp, x * td->fw, y * td->fh + td->fb );
+ Text( td->rp, (char *) s, 1 );
+ }
+
+ return ( 0 );
+}
+
+///}
+///{ "amiga_text()" - Place some text on the screen using an attribute
+
+static errr amiga_text( int x, int y, int n, byte a, cptr s )
+{
+ term_data *td = (term_data*)(Term->data);
+ int i;
+
+ if ( x >= 0 && y >= 0 && n > 0 && !iconified )
+ {
+ /* Draw gfx one char at a time */
+ if (( a & 0xc0 ))
+ {
+ for ( i = 0; i < n; i++ ) put_gfx( td->rp, x + i, y, s[ i ] & 0x7f, a & 0x7f );
+ }
+
+ /* Draw the string on screen */
+ else
+ {
+ SetAPen( td->rp, PEN( a&0x0f ));
+ SetBPen( td->rp, PEN( 0 ));
+ Move( td->rp, x * td->fw, y * td->fh + td->fb );
+ Text( td->rp, (char *) s, n );
+ }
+ }
+
+ return ( 0 );
+}
+
+///}
+///{ "amiga_xtra()" - Handle a "special request"
+
+static errr amiga_xtra( int n, int v )
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Analyze the request */
+ switch ( n )
+ {
+ /* Wait for event */
+ case TERM_XTRA_EVENT:
+
+ return ( amiga_event( v ));
+
+
+ /* Flush input */
+ case TERM_XTRA_FLUSH:
+
+ return ( amiga_flush( v ));
+
+
+ /* Make a noise */
+ case TERM_XTRA_CLEAR:
+
+ return ( amiga_clear());
+
+
+ /* Change cursor visibility */
+ case TERM_XTRA_SHAPE:
+
+ /* Cursor on */
+ if ( v )
+ {
+ cursor_on( td );
+ td->cursor_visible = TRUE;
+ }
+ /* Cursor off */
+ else
+ {
+ cursor_off( td );
+ td->cursor_visible = FALSE;
+ }
+ return ( 0 );
+
+
+ /* Flash screen */
+ case TERM_XTRA_NOISE:
+
+ DisplayBeep( use_pub ? pubscr : amiscr );
+ return ( 0 );
+
+
+ /* Play a sound */
+ case TERM_XTRA_SOUND:
+
+ if ( has_sound )
+ {
+ play_sound( v );
+ }
+ return ( 0 );
+
+
+ /* React on global changes */
+ case TERM_XTRA_REACT:
+
+ return ( amiga_react( v ));
+
+
+ case TERM_XTRA_LEVEL:
+
+ term_curs = td;
+ return ( 0 );
+
+ case TERM_XTRA_DELAY:
+
+ if (v >= 20) Delay(v / 20);
+ return (0);
+
+ /* Unknown request type */
+ default:
+
+ return ( 1 );
+
+ }
+
+ /* Shouldn't be able to get here */
+ return ( 1 );
+}
+
+///}
+///{ "amiga_flush()" - Flush input buffer
+
+static errr amiga_flush( int v )
+{
+ struct IntuiMessage *imsg;
+
+ /* Ignore all messages at the port */
+ while ( imsg = (struct IntuiMessage *) GetMsg( screen.win->UserPort ))
+ {
+ ReplyMsg(( struct Message *) imsg );
+ }
+
+ return ( 1 );
+}
+
+///}
+///{ "amiga_event()" - Wait for an event, and handle it.
+
+static errr amiga_event( int v )
+{
+ struct IntuiMessage *imsg;
+ ULONG iclass;
+ UWORD icode;
+ UWORD iqual;
+ APTR iaddr;
+
+ /* Check for messages to the window */
+ if (( imsg = (struct IntuiMessage *) GetMsg( screen.win->UserPort )) == NULL )
+ {
+ /* If we don't want blocking, return */
+ if ( !v )
+ {
+ return ( 0 );
+ }
+
+ /* No messages, so wait for one */
+ Wait( 1 << screen.win->UserPort->mp_SigBit );
+
+ /* Get the new message */
+ imsg = (struct IntuiMessage *) GetMsg( screen.win->UserPort );
+ }
+
+ /* Handle message */
+ if ( imsg )
+ {
+ /* Get message attributes */
+ iclass = imsg->Class;
+ icode = imsg->Code;
+ iqual = imsg->Qualifier;
+ iaddr = imsg->IAddress;
+
+ /* Update menus before displaying */
+ if ( iclass == IDCMP_MENUVERIFY && icode == MENUHOT && use_menus )
+ {
+ update_menus();
+ }
+
+ /* Reply the message */
+ ReplyMsg(( struct Message *) imsg );
+
+ /* Do we have a keypress? */
+ if ( iclass == IDCMP_RAWKEY )
+ {
+ handle_rawkey( icode, iqual, iaddr );
+ return ( 0 );
+ }
+
+ /* Mouse event - Make pointer visible */
+ if ( iclass == IDCMP_MOUSEMOVE ||
+ iclass == IDCMP_MOUSEBUTTONS ||
+ iclass == IDCMP_MENUVERIFY )
+ {
+ if ( blankmouse && !pointer_visible )
+ {
+ ClearPointer( screen.win );
+ pointer_visible = TRUE;
+ }
+
+ return ( 0 );
+ }
+
+ /* Time for some cursor anim? */
+ if ( iclass == IDCMP_INTUITICKS )
+ {
+ cursor_anim();
+ return ( 0 );
+ }
+
+ /* Menu item picked? */
+ if ( iclass == IDCMP_MENUPICK )
+ {
+ handle_menupick( icode );
+ }
+
+ /* Unknown message class */
+ return ( 1 );
+ }
+
+ /* No events */
+ return ( 1 );
+}
+
+///}
+///{ "amiga_react()"
+
+static errr amiga_react( int v )
+{
+ /* Apply color palette, in case it has changed */
+ load_palette();
+
+ /* Create menus if we don't have any */
+ if ( !menu && use_menus )
+ {
+ create_menus();
+ }
+
+ return ( 0 );
+}
+
+///}
+///{ "amiga_tomb()"
+
+int amiga_tomb( void )
+{
+ cptr p;
+ char tmp[160];
+ time_t ct = time((time_t)0);
+ BPTR file;
+
+ char *pp;
+ int plane, row, error = FALSE;
+ struct BitMap *filebm, *scalbm, *convbm;
+ long stdpens[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ int depth;
+ BYTE *src, *dst;
+ int byt;
+
+ int tw = screen.fw * 64;
+ int th = screen.fh * 21;
+
+ /* Allocate bitmap for tomb graphics file */
+ if (( filebm = alloc_bitmap( 512, 168, 4, BMF_CLEAR, screen.rp->BitMap )) == NULL )
+ {
+ return ( FALSE );
+ }
+
+ /* Open tomb file */
+ if (( file = Open( MTOM, MODE_OLDFILE )) == NULL )
+ {
+ free_bitmap( filebm );
+ return ( FALSE );
+ }
+
+ /* Read file into bitmap */
+ for ( plane = 0; plane < 4 && !error; plane++ )
+ {
+ pp = filebm->Planes[ plane ];
+ for ( row = 0; row < 168 && !error; row++ )
+ {
+ error = ( Read( file, pp, 64 ) != 64 );
+ pp += filebm->BytesPerRow;
+ }
+ }
+
+ /* Close tomb file */
+ Close( file );
+
+ /* Get depth of display */
+ depth = depth_of_bitmap( screen.rp->BitMap );
+
+ /* Remapping needed? */
+ if ( depth != 4 || use_pub )
+ {
+ /* Allocate bitmap for remapped image */
+ if (( convbm = alloc_bitmap( 512, 168, depth, BMF_CLEAR, screen.rp->BitMap )) == NULL )
+ {
+ free_bitmap( filebm );
+ return ( FALSE );
+ }
+
+ /* Simple remapping from 4 to 5 planes? */
+ if ( depth == 5 && !use_pub )
+ {
+ for ( plane = 0; plane < 4; plane++ )
+ {
+ src = filebm->Planes[ plane ];
+ dst = convbm->Planes[ plane ];
+ for ( row = 0; row < 168; row++ )
+ {
+ for ( byt = 0; byt < 64; byt++ )
+ {
+ dst[ byt ] = src[ byt ];
+ }
+ src += filebm->BytesPerRow;
+ dst += convbm->BytesPerRow;
+ }
+ }
+ }
+
+ /* Complex remapping */
+ else
+ {
+ /* Remap old bitmap into new bitmap */
+ remap_bitmap( filebm, convbm, use_pub ? pubpens : stdpens, 512, 168 );
+ }
+
+ /* Free original bitmap */
+ free_bitmap( filebm );
+ }
+
+ /* No remapping needed */
+ else
+ {
+ convbm = filebm;
+ }
+
+ /* Allocate bitmap for scaled graphics */
+ if (( scalbm = alloc_bitmap( tw, th, depth, BMF_CLEAR, screen.rp->BitMap )) == NULL )
+ {
+ free_bitmap( convbm );
+ return ( FALSE );
+ }
+
+ /* Scale the tomb bitmap */
+ scale_bitmap( convbm, 512, 168, scalbm, tw, th );
+
+ /* Free old bitmap */
+ free_bitmap( convbm );
+
+ /* Copy the tomb graphics to the screen, centered */
+ BltBitMapRastPort( scalbm, 0, 0, screen.rp, ( screen.ww - tw ) / 2, 0, tw, th, 0xc0 );
+
+ /* Free bitmap */
+ free_bitmap( scalbm );
+
+ /* King or Queen */
+ if (total_winner || (p_ptr->lev > PY_MAX_LEVEL))
+ {
+ p = "Magnificent";
+ }
+
+ /* Normal */
+ else
+ {
+ p = player_title[p_ptr->pclass][(p_ptr->lev - 1) / 5];
+ }
+
+ tomb_str( 3, " R.I.P." );
+
+ tomb_str( 5, player_name );
+
+ tomb_str( 6, "the" );
+
+ tomb_str( 7, (char *)p );
+
+ tomb_str( 9, (char *)cp_ptr->title );
+
+ sprintf( tmp, "Level: %d", (int)p_ptr->lev );
+ tomb_str( 10, tmp );
+
+ sprintf( tmp, "Exp: %ld", (long)p_ptr->exp );
+ tomb_str( 11, tmp );
+
+ sprintf( tmp, "AU: %ld", (long)p_ptr->au );
+ tomb_str( 12, tmp );
+
+ sprintf( tmp, "Killed on Level %d", dun_level );
+ tomb_str( 13, tmp );
+
+ sprintf( tmp, "by %s", died_from );
+ tomb_str( 14, tmp );
+
+ sprintf( tmp, "%-.24s", ctime(&ct));
+ tomb_str( 16, tmp );
+
+ return ( TRUE );
+}
+
+///}
+///{ "tomb_str()"
+
+void tomb_str( int y, char *str )
+{
+ term_data *td = &screen;
+ int l = strlen( str );
+ int xp = ( 39 * td->fw ) - (( l + 1 ) * td->fw ) / 2;
+
+ SetDrMd( td->rp, JAM1 );
+
+ SetAPen( td->rp, PEN( 1 ));
+ Move( td->rp, xp + 1, y * td->fh + td->fb + 1 );
+ Text( td->rp, str, l );
+
+ SetAPen( td->rp, PEN( 0 ));
+ Move( td->rp, xp, y * td->fh + td->fb );
+ Text( td->rp, str, l );
+
+ SetDrMd( td->rp, JAM2 );
+}
+
+///}
+///{ "handle_rawkey()"
+
+void handle_rawkey( UWORD code, UWORD qual, APTR addr )
+{
+ char buf[ 80 ];
+ int i;
+ int len;
+ UWORD q;
+
+ /* Use a blank mouse-pointer on this window */
+ if ( blankmouse && pointer_visible )
+ {
+ SetPointer( screen.win, blankpointer, 2, 16, 0, 0 );
+ pointer_visible = FALSE;
+ }
+
+ /* Numeric keypad pressed with qualifier? */
+ if (( qual & IEQUALIFIER_NUMERICPAD ) && ( qual & 0xff ))
+ {
+ /* Direction key? (1,2,3,4,6,7,8,9) */
+ if (( code >= 0x1d && code <= 0x1f ) ||
+ ( code == 0x2d || code == 0x2f ) ||
+ ( code >= 0x3d && code <= 0x3f ))
+ {
+ /* Shift/Ctrl/Alt/Amiga keys */
+ q = qual & 0xff;
+
+ /* Shift + Direction */
+ if ( q == IEQUALIFIER_LSHIFT || q == IEQUALIFIER_RSHIFT )
+ {
+ /* Fake a keypress 'run' */
+ Term_keypress( '.' );
+
+ /* Remove shift key from event */
+ qual &= ~( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT );
+ }
+
+ /* Alt + Direction */
+ else if ( q == IEQUALIFIER_LALT || q == IEQUALIFIER_RALT )
+ {
+ /* Fake a keypress 'tunnel' */
+ Term_keypress( 'T' );
+
+ /* Remove alt key from event */
+ qual &= ~( IEQUALIFIER_LALT | IEQUALIFIER_RALT );
+ }
+
+ /* Ctrl + Direction */
+ else if ( q == IEQUALIFIER_CONTROL )
+ {
+ /* Fake a keypress 'open' */
+ Term_keypress( 'o' );
+
+ /* Remove ctrl key from event */
+ qual &= ~IEQUALIFIER_CONTROL;
+ }
+ }
+ }
+
+ /* Convert raw keycode to ANSI sequence */
+ ie.ie_Code = code;
+ ie.ie_Qualifier = qual;
+ ie.ie_EventAddress = (APTR *) * ((ULONG *) addr );
+ len = MapRawKey( &ie, buf, 80, NULL );
+
+ /* Send ANSI sequence to meta-terminal */
+ for ( i = 0; i < len; i++ )
+ {
+ if ( !iconified )
+ {
+ Term_keypress( (unsigned char) buf[ i ]);
+ }
+ }
+}
+
+///}
+///{ "handle_menupick()"
+
+void handle_menupick( int mnum )
+{
+ struct MenuItem *item;
+ ULONG ud;
+ int i;
+
+ /* Be sure to handle all selections */
+ while ( mnum != MENUNULL )
+ {
+ /* Find address of menuitem */
+ item = ItemAddress( menu, mnum );
+
+ /* Find userdata of menuitem */
+ ud = (ULONG) GTMENUITEM_USERDATA( item );
+
+ /* Is this a help item? */
+ if ( ud >= MNU_HELP )
+ {
+ /* Send keypresses */
+ Term_keypress( '\\' );
+ Term_keypress( '?' );
+ Term_keypress( ud - MNU_HELP );
+ }
+
+ /* Is this an option item? */
+ else if ( ud >= MNU_OPTION )
+ {
+ /* Option index */
+ i = ud - MNU_OPTION;
+
+ /* Set option according to checkmark status */
+ *options[ i ].o_var = item->Flags & CHECKED ? TRUE : FALSE;
+
+ /* Fake a dummy keypress to cause update */
+ Term_keypress( ' ' );
+ }
+
+ /* Control key shortcuts */
+ else if ( ud >= MNU_CKEYCOM )
+ {
+ /* Send keycode */
+ Term_keypress( '\\' );
+ Term_keypress( '^' );
+ Term_keypress( ud - MNU_CKEYCOM );
+ }
+
+ /* Key shortcuts */
+ else if ( ud >= MNU_KEYCOM )
+ {
+ /* Key code */
+ i = ud - MNU_KEYCOM;
+
+ /* Some functions need underlying keymap */
+ if ( i != 't' && i != 'w' && i != 'T' )
+ {
+ Term_keypress( '\\' );
+ }
+
+ /* Send keycode */
+ Term_keypress( i );
+ }
+
+ /* Scaled down Map of the dungeon */
+ else if ( ud == MNU_SCALEDMAP )
+ {
+ /* Draw the map */
+ amiga_map();
+ }
+
+ /* Find next menunumber */
+ mnum = item->NextSelect;
+ }
+}
+
+///}
+///{ "cursor_on()"
+
+static void cursor_on( term_data *td )
+{
+ int x0, y0, x1, y1;
+
+ if ( !td->cursor_lit && !iconified )
+ {
+ td->cursor_frame = 0;
+
+ /* Hack - Don't draw cursor at (0,0) */
+ if ( td->cursor_xpos == 0 && td->cursor_ypos == 0 ) return;
+
+ /* Draw an outlined cursor */
+ if ( CUR_A & 0xf0 && use_graphics )
+ {
+ x0 = td->cursor_xpos * td->fw;
+ y0 = td->cursor_ypos * td->fh;
+ x1 = x0 + td->fw - 1;
+ y1 = y0 + td->fh - 1;
+ SetAPen( td->wrp, PEN( CURSOR_PEN ));
+ Move( td->wrp, x0, y0 );
+ Draw( td->wrp, x1, y0 );
+ Draw( td->wrp, x1, y1 );
+ Draw( td->wrp, x0, y1 );
+ Draw( td->wrp, x0, y0 );
+ }
+
+ /* Draw a filled cursor */
+ else
+ {
+ SetAPen( td->wrp, PEN( CUR_A & 0x0f ));
+ SetBPen( td->wrp, PEN( CURSOR_PEN ));
+ Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb );
+ Text( td->wrp, &CUR_C, 1 );
+ }
+
+ td->cursor_lit = TRUE;
+ }
+}
+
+///}
+///{ "cursor_off()"
+
+static void cursor_off( term_data *td )
+{
+ if ( td->cursor_lit && !iconified )
+ {
+ /* Restore graphics under cursor */
+ if ( CUR_A & 0xf0 && use_graphics )
+ {
+ put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, CUR_C, CUR_A );
+ }
+
+ /* Restore char/attr under cursor */
+ else
+ {
+ SetAPen( td->wrp, PEN( CUR_A & 0x0f ));
+ SetBPen( td->wrp, PEN( 0 ));
+ Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb );
+ Text( td->wrp, &CUR_C, 1 );
+ }
+ td->cursor_lit = FALSE;
+ }
+}
+
+///}
+///{ "cursor_anim()"
+
+static void cursor_anim( void )
+{
+ term_data *td = term_curs;
+ int x0, y0, x1, y1, i = p_ptr->px, j = p_ptr->py;
+ byte tc, ta;
+
+ if ( !term_curs ) return;
+
+ td->cursor_frame = ++(td->cursor_frame) % 8;
+
+ /* Small cursor on map */
+ if ( td->cursor_map )
+ {
+ if ( td->cursor_frame & 2 )
+ {
+ SetAPen( td->wrp, PEN( CURSOR_PEN ));
+ RectFill( td->wrp,
+ td->map_x + i * td->mpt_w,
+ td->map_y + j * td->mpt_h,
+ td->map_x + ( i + 1) * td->mpt_w - 1,
+ td->map_y + ( j + 1 ) * td->mpt_h - 1
+ );
+ }
+ else
+ {
+ ta = (( p_ptr->pclass * 10 + p_ptr->prace) >> 5 ) + 12;
+ tc = (( p_ptr->pclass * 10 + p_ptr->prace) & 0x1f );
+ put_gfx_map( td, i, j, tc, ta );
+ }
+ }
+
+ else if ( td->cursor_visible && !iconified )
+ {
+ /* Hack - Don't draw cursor at (0,0) */
+ if ( td->cursor_xpos == 0 && td->cursor_ypos == 0 ) return;
+
+ /* Draw an outlined cursor */
+ if ( CUR_A & 0x80 && use_graphics )
+ {
+ /* First draw the tile under cursor */
+ put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, CUR_C, CUR_A );
+
+ if ( td->cursor_frame < 4 )
+ {
+ x0 = td->cursor_xpos * td->fw;
+ y0 = td->cursor_ypos * td->fh;
+ x1 = x0 + td->fw - 1;
+ y1 = y0 + td->fh - 1;
+ SetAPen( td->wrp, PEN( CURSOR_PEN ));
+ Move( td->wrp, x0, y0 );
+ Draw( td->wrp, x1, y0 );
+ Draw( td->wrp, x1, y1 );
+ Draw( td->wrp, x0, y1 );
+ Draw( td->wrp, x0, y0 );
+ }
+ }
+
+ /* Draw a filled cursor */
+ else
+ {
+ SetAPen( td->wrp, PEN( CUR_A & 0x0f ));
+ SetBPen( td->wrp, ( td->cursor_frame < 4 ) ? PEN( CURSOR_PEN ) : PEN( 0 ));
+ Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb );
+ Text( td->wrp, &CUR_C, 1 );
+ }
+ }
+}
+
+///}
+///{ "load_gfx()"
+
+int load_gfx( void )
+{
+ term_data *ts = &screen;
+ BPTR file;
+ char *p;
+ int plane, row, error = FALSE;
+
+ /* Allocate bitmaps */
+ if (( ts->gfxbm = alloc_bitmap( GFXW, GFXH, 4, BMF_CLEAR, ts->rp->BitMap )) == NULL ) return ( FALSE );
+ if (( ts->mskbm = alloc_bitmap( GFXW, GFXH, 1, BMF_CLEAR, ts->rp->BitMap )) == NULL ) return ( FALSE );
+
+ /* Open file */
+ if (( file = Open( MGFX, MODE_OLDFILE )) == NULL )
+ {
+ MSG( 0, 0, "Unable to open graphics file" );
+ Delay( 100 );
+ return ( FALSE );
+ }
+
+ /* Read file into bitmap */
+ for ( plane = 0; plane < 4 && !error; plane++ )
+ {
+ p = ts->gfxbm->Planes[ plane ];
+ for ( row = 0; row < GFXH && !error; row++ )
+ {
+ error = ( Read( file, p, GFXB ) != GFXB );
+ p += ts->gfxbm->BytesPerRow;
+ }
+ }
+
+ /* Read mask data into bitmap */
+ p = ts->mskbm->Planes[ 0 ];
+ for ( row = 0; row < GFXH && !error; row++ )
+ {
+ error = ( Read( file, p, GFXB ) != GFXB );
+ p += ts->mskbm->BytesPerRow;
+ }
+
+ /* Close file */
+ Close( file );
+
+ /* Did we get any errors while reading? */
+ if ( error )
+ {
+ MSG( 0, 0, "Error while reading graphics file" );
+ Delay( 100 );
+ return ( FALSE );
+ }
+
+ /* Success */
+ return ( TRUE );
+}
+///}
+///{ "conv_gfx()"
+
+int conv_gfx( void )
+{
+ term_data *ts = &screen;
+ struct BitMap *tmpbm;
+ struct BitMap *sbm = ts->rp->BitMap;
+ long stdpens[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ long mskpens[] = { 0, -1 };
+ int depth;
+ BYTE *src, *dst;
+ int plane, row, byt;
+
+ /* Get depth of display */
+ depth = depth_of_bitmap( sbm );
+
+ /* We don't want 16 or 24 bit displays (yet) */
+ if ( depth > 8 )
+ {
+ MSG( 0, 1, "Sorry, max. 8 bit display supported." );
+ Delay( 100 );
+ return ( FALSE );
+ }
+
+ /* Allocate new bitmap with screen's depth */
+ if (( tmpbm = alloc_bitmap( GFXW, GFXH, depth, BMF_CLEAR, sbm )) == NULL )
+ {
+ MSG( 0, 1, "Unable to allocate temporary bitmap." );
+ Delay( 100 );
+ return ( FALSE );
+ }
+
+ /* Simple remapping from 4 to 5 planes? */
+ if ( depth == 5 && !use_pub )
+ {
+ for ( plane = 0; plane < 4; plane++ )
+ {
+ src = ts->gfxbm->Planes[ plane ];
+ dst = tmpbm->Planes[ plane ];
+ for ( row = 0; row < GFXH; row++ )
+ {
+ for ( byt = 0; byt < GFXB; byt++ )
+ {
+ dst[ byt ] = src[ byt ];
+ }
+ src += ts->gfxbm->BytesPerRow;
+ dst += tmpbm->BytesPerRow;
+ }
+ }
+ }
+
+ /* Complex remapping */
+ else
+ {
+ /* Remap old bitmat into new bitmap */
+ remap_bitmap( ts->gfxbm, tmpbm, use_pub ? pubpens : stdpens, GFXW, GFXH );
+ }
+
+ /* Free old bitmap */
+ free_bitmap( ts->gfxbm );
+ ts->gfxbm = tmpbm;
+
+ /* Allocate new bitmap with screen's depth */
+ if (( tmpbm = alloc_bitmap( GFXW, GFXH, depth, BMF_CLEAR, sbm )) == NULL )
+ {
+ MSG( 0, 1, "Unable to allocate temporary bitmap." );
+ Delay( 100 );
+ return ( FALSE );
+ }
+
+ /* Simple remapping from 4 to 5 planes? */
+ if ( depth == 5 && !use_pub )
+ {
+ for ( plane = 0; plane < 4; plane++ )
+ {
+ src = ts->mskbm->Planes[ 0 ];
+ dst = tmpbm->Planes[ plane ];
+ for ( row = 0; row < GFXH; row++ )
+ {
+ for ( byt = 0; byt < GFXB; byt++ )
+ {
+ dst[ byt ] = src[ byt ];
+ }
+ src += ts->mskbm->BytesPerRow;
+ dst += tmpbm->BytesPerRow;
+ }
+ }
+ }
+
+ /* Complex remapping */
+ else
+ {
+ /* Remap old bitmap into new bitmap */
+ remap_bitmap( ts->mskbm, tmpbm, mskpens, GFXW, GFXH );
+ }
+
+ /* Free old bitmap */
+ free_bitmap( ts->mskbm );
+ ts->mskbm = tmpbm;
+
+ /* Done */
+ return ( TRUE );
+}
+
+///}
+///{ "size_gfx()"
+
+int size_gfx( term_data *td )
+{
+ term_data *ts = &screen;
+ int depth;
+ struct BitMap *sbm = td->rp->BitMap;
+ struct BitMap *tmpbm;
+
+ /* Calculate tile bitmap dimensions */
+ td->gfx_w = 32 * td->fw;
+ td->gfx_h = 32 * td->fh;
+
+ /* Calculate map bitmap dimensions */
+ td->mpt_w = td->ww / MAX_WID;
+ td->mpt_h = td->wh / MAX_HGT;
+ td->map_w = td->mpt_w * 32;
+ td->map_h = td->mpt_h * 32;
+
+ /* Scale tile graphics into map size */
+ depth = depth_of_bitmap( ts->gfxbm );
+ if (( td->mapbm = alloc_bitmap( td->map_w, td->map_h, depth, BMF_CLEAR, sbm )) == NULL ) return ( FALSE );
+ scale_bitmap( ts->gfxbm, GFXW, GFXH, td->mapbm, td->map_w, td->map_h );
+
+ /* Scale tile graphics */
+ depth = depth_of_bitmap( ts->gfxbm );
+ if (( tmpbm = alloc_bitmap( td->gfx_w, td->gfx_h, depth, BMF_CLEAR, sbm )) == NULL ) return ( FALSE );
+ scale_bitmap( ts->gfxbm, GFXW, GFXH, tmpbm, td->gfx_w, td->gfx_h );
+ if ( td->gfxbm ) free_bitmap( td->gfxbm );
+ td->gfxbm = tmpbm;
+
+ /* Scale tile mask */
+ depth = depth_of_bitmap( ts->mskbm );
+ if (( tmpbm = alloc_bitmap( td->gfx_w, td->gfx_h, depth, BMF_CLEAR, sbm )) == NULL ) return ( FALSE );
+ scale_bitmap( ts->mskbm, GFXW, GFXH, tmpbm, td->gfx_w, td->gfx_h );
+ if ( td->mskbm ) free_bitmap( td->mskbm );
+ td->mskbm = tmpbm;
+
+ /* Success */
+ return ( TRUE );
+}
+
+///}
+///{ "put_gfx()"
+
+static void put_gfx( struct RastPort *rp, int x, int y, int chr, int col )
+{
+ term_data *td = (term_data*)(Term->data);
+ int fw = td->fw;
+ int fh = td->fh;
+ int x0 = x * fw;
+ int y0 = y * fh;
+ int x1 = x0 + fw - 1;
+ int y1 = y0 + fh - 1;
+ int a = col & 0x1f;
+ int c = chr & 0x1f;
+
+ /* Just a black tile */
+ if ( a == 0 && c == 0 )
+ {
+ SetAPen( rp, PEN( 0 ));
+ RectFill( rp, x0, y0, x1, y1 );
+ return;
+ }
+
+ /* Player - Remap for race and class */
+ if ( a == 12 && c == 0 )
+ {
+ a = (( p_ptr->pclass * 10 + p_ptr->prace) >> 5 ) + 12;
+ c = (( p_ptr->pclass * 10 + p_ptr->prace) & 0x1f );
+ }
+
+ /* Draw tile through mask */
+ if ( col & 0x40 )
+ {
+ SetAPen( rp, PEN(0) );
+ RectFill( rp, x0, y0, x1, y1 );
+ BltMaskBitMapRastPort( td->gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, (ABC | ANBC | ABNC), td->mskbm->Planes[ 0 ] );
+ }
+
+ /* Draw full tile */
+ else
+ {
+ BltBitMapRastPort( td->gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, 0xc0 );
+ }
+}
+
+///}
+///{ "amiga_fail()"
+
+static int amiga_fail( char *msg )
+{
+ int i;
+
+ /* Print error message */
+ if ( msg )
+ {
+ fprintf( stderr, "%s\n", msg );
+ }
+
+ /* Free sound memory */
+ free_sound();
+
+ /* Unlock public screen */
+ if ( publock )
+ {
+ UnlockPubScreen( NULL, pubscr );
+ publock = FALSE;
+ }
+
+ /* Remove menu from window */
+ if ( menu && screen.win )
+ {
+ ClearMenuStrip( screen.win );
+ }
+
+ /* Free menus */
+ if ( menu )
+ {
+ FreeMenus( menu );
+ menu = NULL;
+ }
+
+ /* Free term resources */
+ free_term( &mirror );
+ free_term( &recall );
+ free_term( &choice );
+ free_term( &screen );
+
+ /* Free obtained pens */
+ if ( pubscr )
+ {
+ for ( i = 0; i < 32; i++)
+ {
+ if ( pubpens[ i ] != -1 )
+ {
+ ReleasePen( pubscr->ViewPort.ColorMap, pubpens[ i ]);
+ }
+ }
+ }
+
+ /* Free visual info */
+ if ( visinfo )
+ {
+ FreeVisualInfo( visinfo );
+ visinfo = NULL;
+ }
+
+ /* Close intuition screen */
+ if ( amiscr )
+ {
+ CloseScreen( amiscr );
+ amiscr = NULL;
+ }
+
+ /* Close gadtools.library */
+ if ( GadToolsBase )
+ {
+ CloseLibrary( GadToolsBase );
+ GadToolsBase = NULL;
+ }
+
+ /* Close keymap.library */
+ if ( KeymapBase )
+ {
+ CloseLibrary( KeymapBase );
+ KeymapBase = NULL;
+ }
+
+ /* Close keymap.library */
+ if ( DiskfontBase )
+ {
+ CloseLibrary( DiskfontBase );
+ DiskfontBase = NULL;
+ }
+
+ // Return failure
+ return ( -1);
+}
+
+///}
+///{ "amiga_map()"
+
+static void amiga_map( void )
+{
+ term_data *td = &screen; // (term_data*)(Term->data);
+ int i, j;
+ byte ta, tc;
+
+ /* Only in graphics mode */
+ if ( !use_graphics ) return;
+
+ /* Turn off cursor */
+ if ( td->cursor_visible ) cursor_off( td );
+
+ /* Save screen */
+ Term_save();
+
+ /* Clear screen */
+ Term_clear();
+ Term_fresh();
+
+ /* Calculate offset values */
+ td->map_x = (( td->fw * 80 ) - ( td->mpt_w * cur_wid )) / 2;
+ td->map_y = (( td->fh * 24 ) - ( td->mpt_h * cur_hgt )) / 2;
+
+ /* Draw all "interesting" features */
+ for ( i = 0; i < cur_wid; i++ )
+ {
+ for ( j = 0; j < cur_hgt; j++ )
+ {
+ /* Get frame tile */
+ if ( i == 0 || i == cur_wid - 1 || j == 0 || j == cur_hgt - 1 )
+ {
+ ta = f_info[ 63 ].z_attr;
+ tc = f_info[ 63 ].z_char;
+ }
+
+ /* Get tile from cave table */
+ else
+ {
+ map_info( j, i, &ta, (char *) &tc );
+ }
+
+ /* Ignore non-graphics */
+ if ( ta & 0x80 )
+ {
+ ta &= 0x1f;
+ tc &= 0x1f;
+
+ /* Player XXX XXX XXX */
+ if ( ta == 12 && tc == 0 )
+ {
+ ta = (( p_ptr->pclass * 10 + p_ptr->prace ) >> 5 ) + 12;
+ tc = (( p_ptr->pclass * 10 + p_ptr->prace ) & 0x1f );
+ }
+
+ /* Put the graphics to the screen */
+ put_gfx_map( td, i, j, tc, ta );
+ }
+ }
+ }
+
+ /* Draw a small cursor now */
+ td->cursor_map = TRUE;
+
+ /* Wait for a keypress, flush key buffer */
+ Term_inkey( &tc, TRUE, TRUE );
+ Term_flush();
+
+ /* Normal cursor again */
+ td->cursor_map = FALSE;
+
+ /* Restore screen */
+ Term_clear();
+ Term_fresh();
+ Term_load();
+ Term_fresh();
+
+ /* Turn cursor back on */
+ if ( td->cursor_visible ) cursor_on( td );
+}
+
+///}
+///{ "load_palette()"
+
+void load_palette( void )
+{
+ int i;
+ int n;
+
+ if ( amiscr == NULL ) return;
+
+ n = deep ? 32 : 16;
+
+ if ( v39 )
+ {
+ palette32[ 0 ] = n << 16;
+ palette32[ n * 3 + 1 ] = 0;
+ for ( i = 0; i < n; i++ )
+ {
+ palette32[ i * 3 + 1 ] = color_table[ use_graphics ? i : i + 16 ][ 1 ] << 24;
+ palette32[ i * 3 + 2 ] = color_table[ use_graphics ? i : i + 16 ][ 2 ] << 24;
+ palette32[ i * 3 + 3 ] = color_table[ use_graphics ? i : i + 16 ][ 3 ] << 24;
+ }
+ LoadRGB32( &amiscr->ViewPort, palette32 );
+ }
+ else
+ {
+ for ( i = 0; i < n; i++ )
+ {
+ palette4[ i ] = ( color_table[ use_graphics ? i : i + 16 ][ 1 ] >> 4 ) << 8;
+ palette4[ i ] |= ( color_table[ use_graphics ? i : i + 16 ][ 2 ] >> 4 ) << 4;
+ palette4[ i ] |= ( color_table[ use_graphics ? i : i + 16 ][ 3 ] >> 4 );
+ }
+ LoadRGB4( &amiscr->ViewPort, palette4, n );
+ }
+}
+
+///}
+///{ "create_menus()"
+
+int create_menus( void )
+{
+ option_type *opt;
+ struct NewMenu *item = newmenu;
+ int nmsize = sizeof ( struct NewMenu );
+ int page = -1;
+ int pg = 0;
+ int i, o;
+
+ /* Copy all pre-items into array */
+ for ( i = 0; pre_item[ i ].nm_Type != 255; i++ )
+ {
+ memcpy( item++, &pre_item[ i ], nmsize );
+ }
+
+ /* Find next option */
+ for ( opt = options, o = 0; opt->o_desc; opt++, o++ )
+ {
+ /* Cheating options are skipped */
+ if ( opt->o_page > 6 ) continue;
+
+ /* New page of options? */
+ if ( page != opt->o_page )
+ {
+ page = opt->o_page;
+
+ /* Copy option header */
+ memcpy( item++, &opt_item[ pg++ ], nmsize );
+ }
+
+ /* Insert fields for this option */
+ item->nm_Type = NM_SUB;
+ item->nm_CommKey = 0;
+ item->nm_MutualExclude = 0;
+
+ /* Use option description as menu text */
+ item->nm_Label = (STRPTR) opt->o_desc;
+
+ /* Insert option index into userdata field */
+ item->nm_UserData = (void *)( MNU_OPTION + o );
+
+ /* Use checkmark on this item */
+ item->nm_Flags = CHECKIT | MENUTOGGLE;
+
+ /* Done with this item */
+ item++;
+ }
+
+ /* Copy all post-items into array */
+ for ( i = 0; post_item[ i ].nm_Type != 255; i++ )
+ {
+ memcpy( item++, &post_item[ i ], nmsize );
+ }
+
+ /* Actually create the menu structures */
+ menu = CreateMenus( newmenu, NULL );
+
+ if ( menu )
+ {
+ /* Layout menus */
+ if ( LayoutMenus( menu, visinfo, v39 ? GTMN_NewLookMenus : TAG_IGNORE, TRUE, TAG_END ))
+ {
+ /* Attach menu to window */
+ SetMenuStrip( screen.win , menu );
+ }
+
+ /* Free menus */
+ else
+ {
+ FreeMenus( menu );
+ menu = NULL;
+ }
+ }
+
+ /* Success */
+ return ( menu != NULL );
+}
+
+///}
+///{ "update_menus()"
+
+void update_menus( void )
+{
+ struct MenuItem *father, *item;
+ int i;
+
+ /* Require a window and a menu */
+ if ( !screen.win || !menu ) return;
+
+ /* Detach the menu from the window */
+ // ClearMenuStrip( screen.win );
+
+ /* Initial menuitem and subitem for options */
+ father = menu->FirstItem;
+ item = father->SubItem;
+
+ /* Find next option */
+ for ( i = 0; item && options[ i ].o_desc; i++ )
+ {
+ /* Did we find it? */
+ if ( item )
+ {
+ /* Option is set, add a checkmark */
+ if ( *(options[ i ].o_var ))
+ {
+ item->Flags |= CHECKED;
+ }
+
+ /* Option is not set, remove checkmark */
+ else
+ {
+ item->Flags &= ~CHECKED;
+ }
+ }
+
+ /* Menuitem not found */
+ else
+ {
+ fprintf( stderr, "ERROR: menuitem #%d not found.\n", i );
+ return;
+ }
+
+ /* Find next menuitem */
+ if ((item = item->NextItem ) == NULL )
+ {
+ /* New set */
+ father = father->NextItem;
+ if ( father )
+ {
+ item = father->SubItem;
+ }
+ }
+ }
+
+ /* Enable/Disable the amiga map according to use_graphics */
+ if ( item = ItemAddress( menu, FULLMENUNUM( 6, 7, 0 )))
+ {
+ item->Flags = use_graphics ? item->Flags | ITEMENABLED : item->Flags & ~ITEMENABLED;
+ }
+
+ /* Attach menu to window again */
+ // ResetMenuStrip( screen.win, menu );
+}
+
+///}
+///{ "init_sound()"
+
+
+int init_sound( void )
+{
+ int i;
+ char tmp[256];
+ char buf[256];
+ struct AmiSound *snd;
+
+ /* Load samples */
+ for ( i = 0; i < NSAMPLES; i++ )
+ {
+ /* Pointer to sound data */
+ snd = &sound_data[ i ];
+
+ /* Should this sample be loaded into memory? */
+ if ( snd->Memory )
+ {
+ /* Construct filename */
+ path_build(buf, 255, ANGBAND_DIR_XTRA, "sound");
+
+ /* Construct filename */
+ path_build(tmp, 255, buf, snd->Name );
+
+ /* Load the sample into memory */
+ snd->Address = (struct SoundInfo *) PrepareSound( tmp );
+ }
+ }
+
+ /* Success */
+ has_sound = TRUE;
+ use_sound = TRUE;
+
+ return ( TRUE );
+}
+
+///}
+///{ "free_sound()"
+
+void free_sound( void )
+{
+ int i;
+
+ /* Stop all channels */
+ StopSound( LEFT0 );
+ StopSound( LEFT1 );
+ StopSound( RIGHT0 );
+ StopSound( RIGHT1 );
+
+ /* Remove all sounds from memory */
+ for ( i = 0; i < NSAMPLES; i++ )
+ {
+ if ( sound_data[ i ].Address )
+ {
+ /* Remove the sound from memory */
+ RemoveSound( sound_data[ i ].Address );
+
+ /* Clear address field */
+ sound_data[ i ].Address = NULL;
+ }
+ }
+
+ /* Done */
+ has_sound = FALSE;
+ use_sound = FALSE;
+}
+
+///}
+///{ "play_sound()"
+
+static void play_sound( int v )
+{
+ struct AmiSound *snd;
+ struct AmiSound *old_snd;
+ int rate;
+ int channel;
+ int old;
+ char buf[256];
+ char tmp[256];
+
+ if ( has_sound )
+ {
+ /* Pointer to sound data */
+ snd = &sound_data[ v ];
+
+ /* Channel number */
+ channel = snd->Channel;
+
+ /* Last sample played on channel */
+ old = channel_last[ channel ];
+
+ /* Sample Rate */
+ rate = snd->Rate;
+
+ /* Random rate on some sounds */
+ if ( v == SOUND_HIT || v == SOUND_MISS )
+ {
+ rate = rate - 50 + rand_int( 150 );
+ }
+
+ /* Pointer to old sound data */
+ old_snd = old >= 0 ? &sound_data[ old ] : NULL;
+
+ /* Stop sound currently playing on this channel */
+ StopSound( channel );
+
+ /* Free old sample if required */
+ if ( !old_snd->Memory && old_snd->Address && old != v )
+ {
+ /* Remove it from memory */
+ RemoveSound( old_snd->Address );
+
+ /* Clear address field */
+ old_snd->Address = NULL;
+ }
+
+ /* Load new sample into memory if required */
+ if ( !snd->Memory && snd->Address == NULL )
+ {
+ /* Construct filename */
+ path_build(buf, 255, ANGBAND_DIR_XTRA, "sound");
+
+ /* Construct filename */
+ path_build(tmp, 255, buf, snd->Name );
+
+ /* Load the sample into memory */
+ snd->Address = (struct SoundInfo *) PrepareSound( tmp );
+ }
+
+ /* Make sure the sample is loaded into memory */
+ if ( snd->Address )
+ {
+ /* Start playing the sound */
+ PlaySound( snd->Address, snd->Volume, channel, rate, snd->Repeats );
+ }
+
+ /* Store sample number */
+ channel_last[ channel ] = v;
+ }
+}
+
+///}
+///{ put_gfx_map()
+
+static void put_gfx_map( term_data *td, int x, int y, int c, int a )
+{
+ BltBitMapRastPort(
+ td->mapbm,
+ c * td->mpt_w,
+ a * td->mpt_h,
+ td->wrp,
+ td->map_x + x * td->mpt_w,
+ td->map_y + y * td->mpt_h,
+ td->mpt_w,
+ td->mpt_h,
+ 0xc0
+ );
+}
+
+///}
+///{ "alloc_bitmap()"
+
+struct BitMap *alloc_bitmap( int width, int height, int depth, ULONG flags, struct BitMap *friend )
+{
+ int p;
+ struct BitMap *bitmap;
+ unsigned char *bp;
+
+ /* Kickstart 39+ */
+ if ( v39 ) return ( AllocBitMap( width, height, depth, flags, friend ));
+
+ /* Kickstart 38- */
+ else
+ {
+ /* Allocate bitmap structure */
+ if (( bitmap = AllocMem( sizeof( struct BitMap ), MEMF_PUBLIC | MEMF_CLEAR )))
+ {
+ InitBitMap( bitmap, depth, width, height );
+ /* Allocate bitplanes */
+ for ( p = 0; p < depth; p++ )
+ {
+ bp = AllocRaster( width, height );
+ if ( !bp ) break;
+ bitmap->Planes[ p ] = bp;
+ }
+
+ /* Out of memory */
+ if ( p != depth )
+ {
+ /* Free bitplanes */
+ while ( --p >= 0 )
+ {
+ FreeRaster( bitmap->Planes[ p ], width, height );
+ }
+ /* Free bitmap structure */
+ FreeMem( bitmap, sizeof( struct BitMap ));
+ bitmap = NULL;
+ }
+ }
+ return ( bitmap );
+ }
+}
+
+///}
+///{ "free_bitmap()"
+
+void free_bitmap( struct BitMap *bitmap )
+{
+ int p;
+
+ /* Check for NULL */
+ if ( !bitmap ) return;
+
+ /* Kickstart 39+ */
+ if ( v39 ) FreeBitMap( bitmap );
+
+ /* Kickstart 38- */
+ else
+ {
+ /* Free bitplanes */
+ for ( p = 0; p < bitmap->Depth; p++ )
+ {
+ FreeRaster( bitmap->Planes[ p ], bitmap->BytesPerRow * 8, bitmap->Rows );
+ }
+ /* Free bitmap structure */
+ FreeMem( bitmap, sizeof( struct BitMap ));
+ }
+}
+
+///}
+///{ scale_bitmap()
+
+void scale_bitmap( struct BitMap *srcbm, int srcw, int srch, struct BitMap *dstbm, int dstw, int dsth )
+{
+ struct BitScaleArgs bsa;
+
+ /* Scale bitmap */
+ bsa.bsa_SrcBitMap = srcbm;
+ bsa.bsa_DestBitMap = dstbm;
+ bsa.bsa_SrcX = 0;
+ bsa.bsa_SrcY = 0;
+ bsa.bsa_SrcWidth = srcw;
+ bsa.bsa_SrcHeight = srch;
+ bsa.bsa_DestX = 0;
+ bsa.bsa_DestY = 0;
+ bsa.bsa_XSrcFactor = srcw;
+ bsa.bsa_YSrcFactor = srch;
+ bsa.bsa_XDestFactor = dstw;
+ bsa.bsa_YDestFactor = dsth;
+ bsa.bsa_Flags = 0;
+ BitMapScale( &bsa );
+}
+
+///}
+///{ "remap_bitmap()"
+
+void remap_bitmap( struct BitMap *srcbm, struct BitMap *dstbm, long *pens, int width, int height )
+{
+ int x, y, p, c, ox, lpr, sd, dd;
+ int bm[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+ UBYTE *sp[ 4 ];
+ UBYTE *dp[ 8 ];
+ ULONG ls[ 4 ];
+ ULONG ld[ 8 ];
+ ULONG mask;
+
+ /* Source bitplanes */
+ sd = depth_of_bitmap( srcbm );
+ for ( p = 0; p < sd; p++ )
+ {
+ sp[ p ] = srcbm->Planes[ p ];
+ }
+
+ /* Destination bitplanes */
+ dd = depth_of_bitmap( dstbm );
+ for ( p = 0; p < dd; p++ )
+ {
+ dp[ p ] = dstbm->Planes[ p ];
+ }
+
+ /* Number of longwords per row */
+ lpr = width / 32;
+
+ /* Convert graphics */
+ for ( y = 0; y < height; y++ )
+ {
+ ox = 0;
+ for ( x = 0 ; x < lpr; x++ )
+ {
+ /* Read source longwords */
+ for ( p = 0; p < sd; p++ ) ls[ p ] = *(ULONG *)( sp[ p ] + ox);
+
+ /* Clear destination longwords */
+ for ( p = 0; p < dd; ld[ p++ ] = 0 );
+
+ /* Remap */
+ for ( mask = 0x80000000; mask != 0; mask >>= 1)
+ {
+ /* Find color index */
+ for ( p = c = 0; p < sd; p++ ) if ( ls[ p ] & mask ) c |= bm[ p ];
+
+ /* Remap */
+ c = pens[ c ];
+
+ /* Update destination longwords */
+ for ( p = 0; p < dd; p++ ) if ( c & bm[ p ] ) ld[ p ] |= mask;
+ }
+
+ /* Write destination longwords */
+ for ( p = 0; p < dd; p++ ) *(ULONG *)( dp[ p ] + ox ) = ld[ p ];
+
+ /* Update offset */
+ ox += 4;
+ }
+
+ /* Update pointers to get to next line */
+ for ( p = 0; p < sd; sp[ p++ ] += srcbm->BytesPerRow );
+ for ( p = 0; p < dd; dp[ p++ ] += dstbm->BytesPerRow );
+ }
+}
+
+///}
+///{ "depth_of_bitmap()"
+
+int depth_of_bitmap( struct BitMap *bm )
+{
+ int depth;
+
+ if ( v39 )
+ {
+ depth = GetBitMapAttr( bm, BMA_DEPTH );
+ }
+ else
+ {
+ depth = bm->Depth;
+ }
+
+ return ( depth );
+}
+
+///}
+
diff --git a/src/main-cap.c b/src/main-cap.c
new file mode 100644
index 00000000..6d44af28
--- /dev/null
+++ b/src/main-cap.c
@@ -0,0 +1,1075 @@
+/* File: main-cap.c */
+
+/* Purpose: Support for "term.c" using "termcap" calls */
+
+#include "angband.h"
+
+
+#ifdef USE_CAP
+
+
+/*
+ * This file is a total hack, but is often very helpful. :-)
+ *
+ * This file allows use of the terminal without requiring the
+ * "curses" routines. In fact, if "USE_HARDCODE" is defined,
+ * this file will attempt to use various hard-coded "vt100"
+ * escape sequences to also avoid the use of the "termcap"
+ * routines. I do not know if this will work on System V.
+ *
+ * This file is intended for use only on those machines which are
+ * unable, for whatever reason, to compile the "main-gcu.c" file,
+ * but which seem to be able to support the "termcap" library, or
+ * which at least seem able to support "vt100" terminals.
+ *
+ * Large portions of this file were stolen from "main-gcu.c"
+ *
+ * This file incorrectly handles output to column 80, I think.
+ */
+
+
+/*
+ * Require a "system"
+ */
+#if !defined(USE_TERMCAP) && !defined(USE_HARDCODE)
+# define USE_TERMCAP
+#endif
+
+/*
+ * Hack -- try to guess which systems use what commands
+ * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
+ * Mega-Hack -- try to guess when "POSIX" is available.
+ * If the user defines two of these, we will probably crash.
+ */
+#if !defined(USE_TPOSIX)
+# if !defined(USE_TERMIO) && !defined(USE_TCHARS)
+# if defined(_POSIX_VERSION)
+# define USE_TPOSIX
+# else
+# if defined(USG) || defined(linux) || defined(SOLARIS)
+# define USE_TERMIO
+# else
+# define USE_TCHARS
+# endif
+# endif
+# endif
+#endif
+
+
+
+/*
+ * POSIX stuff
+ */
+#ifdef USE_TPOSIX
+# include <sys/ioctl.h>
+# include <termios.h>
+#endif
+
+/*
+ * One version needs these files
+ */
+#ifdef USE_TERMIO
+# include <sys/ioctl.h>
+# include <termio.h>
+#endif
+
+/*
+ * The other needs these files
+ */
+#ifdef USE_TCHARS
+# include <sys/ioctl.h>
+# include <sys/resource.h>
+# include <sys/param.h>
+# include <sys/file.h>
+# include <sys/types.h>
+#endif
+
+
+/*
+ * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
+ *
+ * They should both work due to the "(i != 1)" test in the code
+ * which checks for the result of the "read()" command.
+ */
+#ifndef O_NDELAY
+# define O_NDELAY O_NONBLOCK
+#endif
+
+
+
+
+#ifdef USE_TERMCAP
+
+/*
+ * Termcap string information
+ */
+
+static char blob[1024]; /* The "termcap" entry */
+static char area[1024]; /* The string extraction buffer */
+static char *next = area; /* The current "index" into "area" */
+static char *desc; /* The terminal name */
+
+#endif
+
+
+/*
+ * Pointers into the "area"
+ */
+
+static char *cm; /* Move cursor */
+static char *ch; /* Move cursor to horizontal location */
+static char *cv; /* Move cursor to vertical location */
+static char *ho; /* Move cursor to top left */
+static char *ll; /* Move cursor to bottom left */
+static char *cs; /* Set scroll area */
+static char *cl; /* Clear screen */
+static char *cd; /* Clear to end of display */
+static char *ce; /* Clear to end of line */
+static char *cr; /* Move to start of line */
+static char *so; /* Turn on standout */
+static char *se; /* Turn off standout */
+static char *md; /* Turn on bold */
+static char *me; /* Turn off bold */
+static char *vi; /* Cursor - invisible */
+static char *ve; /* Cursor - normal */
+static char *vs; /* Cursor - bright */
+
+
+/*
+ * State variables
+ */
+
+static int rows; /* Screen size (Y) */
+static int cols; /* Screen size (X) */
+static int curx; /* Cursor location (X) */
+static int cury; /* Cursor location (Y) */
+static int curv; /* Cursor visibility */
+
+
+/*
+ * Extern functions
+ */
+extern char *getenv();
+extern char *tgoto();
+extern char *tgetstr();
+
+
+/*
+ * Write some chars to the terminal
+ */
+static void ewrite(char *str)
+{
+ int numtowrite, numwritten;
+
+ /* See how much work we have */
+ numtowrite = strlen(str);
+
+ /* Write until done */
+ while (numtowrite > 0)
+ {
+ /* Try to write the chars */
+ numwritten = write(1, str, numtowrite);
+
+ /* Handle FIFOs and EINTR */
+ if (numwritten < 0) numwritten = 0;
+
+ /* See what we completed */
+ numtowrite -= numwritten;
+ str += numwritten;
+
+ /* Hack -- sleep if not done */
+ if (numtowrite > 0) sleep(1);
+ }
+}
+
+
+
+#ifdef USE_TERMCAP
+
+static char write_buffer[128];
+static char *write_buffer_ptr;
+
+static void output_one(char c)
+{
+ *write_buffer_ptr++ = c;
+}
+
+static void tp(char *s)
+{
+ /* Dump the string into us */
+ write_buffer_ptr = write_buffer;
+
+ /* Write the string with padding */
+ tputs (s, 1, output_one);
+
+ /* Finish the string */
+ *write_buffer_ptr = '\0';
+
+ /* Dump the recorded buffer */
+ ewrite (write_buffer);
+}
+
+#endif
+
+#ifdef USE_HARDCODE
+
+static void tp(char *s)
+{
+ ewrite(s);
+}
+
+#endif
+
+
+
+
+
+
+
+/*
+ * Clear the screen
+ */
+static void do_cl(void)
+{
+ if (cl) tp (cl);
+}
+
+/*
+ * Clear to the end of the line
+ */
+static void do_ce(void)
+{
+ if (ce) tp(ce);
+}
+
+
+/*
+ * Set the cursor visibility (0 = invis, 1 = normal, 2 = bright)
+ */
+static void curs_set(int vis)
+{
+ char *v = NULL;
+
+ if (!vis)
+ {
+ v = vi;
+ }
+ else if (vis > 1)
+ {
+ v = vs ? vs : ve;
+ }
+ else
+ {
+ v = ve ? ve : vs;
+ }
+
+ if (v) tp(v);
+}
+
+
+
+/*
+ * Restrict scrolling to within these rows
+ */
+static void do_cs(int y1, int y2)
+{
+
+#ifdef USE_TERMCAP
+ if (cs) tp(tgoto(cs, y2, y1));
+#endif
+
+#ifdef USE_HARDCODE
+ char temp[64];
+ sprintf(temp, cs, y1, y2);
+ tp (temp);
+#endif
+
+}
+
+
+
+/*
+ * Go to the given screen location directly
+ */
+static void do_cm(int x, int y)
+{
+
+#ifdef USE_TERMCAP
+ if (cm) tp(tgoto(cm, x, y));
+#endif
+
+#ifdef USE_HARDCODE
+ char temp[64];
+ sprintf(temp, cm, y + 1, x + 1);
+ tp(temp);
+#endif
+
+}
+
+
+/*
+ * Go to the given screen location in a "clever" manner
+ *
+ * XXX XXX XXX This function could use some work!
+ */
+static void do_move(int x1, int y1, int x2, int y2)
+{
+ /* Hack -- unknown start location */
+ if ((x1 == x2) && (y1 == y2)) do_cm(x2, y2);
+
+ /* Left edge */
+ else if (x2 == 0)
+ {
+ if ((y2 <= 0) && ho) tp(ho);
+ else if ((y2 >= rows - 1) && ll) tp(ll);
+ else if ((y2 == y1) && cr) tp(cr);
+#if 0
+ else if ((y2 == y1 + 1) && cr && dn)
+ {
+ tp(cr);
+ tp(dn);
+ }
+ else if ((y2 == y1 - 1) && cr && up)
+ {
+ tp(cr);
+ tp(up);
+ }
+#endif
+ else do_cm(x2, y2);
+ }
+
+#if 0
+ /* Up/Down one line */
+ else if ((x2 == x1) && (y2 == y1 + 1) && dn) tp(dn);
+ else if ((x2 == x1) && (y2 == y1 - 1) && up) tp(up);
+#endif
+
+ /* Default -- go directly there */
+ else do_cm(x2, y2);
+}
+
+
+
+
+/*
+ * Help initialize this file (see below)
+ */
+errr init_cap_aux(void)
+{
+
+#ifdef USE_TERMCAP
+
+ /* Get the terminal name (if possible) */
+ desc = getenv("TERM");
+ if (!desc) return (1);
+
+ /* Get the terminal info */
+ if (tgetent(blob, desc) != 1) return (2);
+
+ /* Get the (initial) columns and rows, or default */
+ if ((cols = tgetnum("co")) == -1) cols = 80;
+ if ((rows = tgetnum("li")) == -1) rows = 24;
+
+ /* Find out how to move the cursor to a given location */
+ cm = tgetstr("cm", &next);
+ if (!cm) return (10);
+
+ /* Find out how to move the cursor to a given position */
+ ch = tgetstr("ch", &next);
+ cv = tgetstr("cv", &next);
+
+ /* Find out how to "home" the screen */
+ ho = tgetstr("ho", &next);
+
+ /* Find out how to "last-line" the screen */
+ ll = tgetstr("ll", &next);
+
+ /* Find out how to do a "carriage return" */
+ cr = tgetstr("cr", &next);
+ if (!cr) cr = "\r";
+
+ /* Find out how to clear the screen */
+ cl = tgetstr("cl", &next);
+ if (!cl) return (11);
+
+ /* Find out how to clear to the end of display */
+ cd = tgetstr("cd", &next);
+
+ /* Find out how to clear to the end of the line */
+ ce = tgetstr("ce", &next);
+
+ /* Find out how to scroll (set the scroll region) */
+ cs = tgetstr("cs", &next);
+
+ /* Find out how to hilite */
+ so = tgetstr("so", &next);
+ se = tgetstr("se", &next);
+ if (!so || !se) so = se = NULL;
+
+ /* Find out how to bold */
+ md = tgetstr("md", &next);
+ me = tgetstr("me", &next);
+ if (!md || !me) md = me = NULL;
+
+ /* Check the cursor visibility stuff */
+ vi = tgetstr("vi", &next);
+ vs = tgetstr("vs", &next);
+ ve = tgetstr("ve", &next);
+
+#endif
+
+#ifdef USE_HARDCODE
+
+ /* Assume some defualt information */
+ rows = 24;
+ cols = 80;
+
+ /* Clear screen */
+ cl = "\033[2J\033[H"; /* --]--]-- */
+
+ /* Clear to end of line */
+ ce = "\033[K"; /* --]-- */
+
+ /* Hilite on/off */
+ so = "\033[7m"; /* --]-- */
+ se = "\033[m"; /* --]-- */
+
+ /* Scroll region */
+ cs = "\033[%d;%dr"; /* --]-- */
+
+ /* Move cursor */
+ cm = "\033[%d;%dH"; /* --]-- */
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+
+
+
+/*
+ * Save the "normal" and "angband" terminal settings
+ */
+
+#ifdef USE_TPOSIX
+
+static struct termios norm_termios;
+
+static struct termios game_termios;
+
+#endif
+
+#ifdef USE_TERMIO
+
+static struct termio norm_termio;
+
+static struct termio game_termio;
+
+#endif
+
+#ifdef USE_TCHARS
+
+static struct sgttyb norm_ttyb;
+static struct tchars norm_tchars;
+static struct ltchars norm_ltchars;
+static int norm_local_chars;
+
+static struct sgttyb game_ttyb;
+static struct tchars game_tchars;
+static struct ltchars game_ltchars;
+static int game_local_chars;
+
+#endif
+
+
+
+/*
+ * Are we active? Not really needed.
+ */
+static int active = FALSE;
+
+
+/*
+ * The main screen (no sub-screens)
+ */
+static term term_screen_body;
+
+
+
+/*
+ * Place the "keymap" into its "normal" state
+ */
+static void keymap_norm(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCSLTC, (char *)&norm_ltchars);
+ (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Place the "keymap" into the "game" state
+ */
+static void keymap_game(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &game_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&game_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCSLTC, (char *)&game_ltchars);
+ (void)ioctl(0, TIOCLSET, (char *)&game_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the normal keymap
+ */
+static void keymap_norm_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Get the normal keymap */
+ tcgetattr(0, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TCGETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCGLTC, (char *)&norm_ltchars);
+ (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the keymaps (normal and game)
+ */
+static void keymap_game_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Acquire the current mapping */
+ tcgetattr(0, &game_termios);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termios.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termios.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termios.c_cc[VQUIT] = (char) - 1;
+ game_termios.c_cc[VERASE] = (char) - 1;
+ game_termios.c_cc[VKILL] = (char) - 1;
+ game_termios.c_cc[VEOF] = (char) - 1;
+ game_termios.c_cc[VEOL] = (char) - 1;
+
+ /* Normally, block until a character is read */
+ game_termios.c_cc[VMIN] = 1;
+ game_termios.c_cc[VTIME] = 0;
+
+ /* Hack -- Turn off "echo" and "canonical" mode */
+ game_termios.c_lflag &= ~(ECHO | ICANON);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Acquire the current mapping */
+ (void)ioctl(0, TCGETA, (char *)&game_termio);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termio.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termio.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termio.c_cc[VQUIT] = (char) - 1;
+ game_termio.c_cc[VERASE] = (char) - 1;
+ game_termio.c_cc[VKILL] = (char) - 1;
+ game_termio.c_cc[VEOF] = (char) - 1;
+ game_termio.c_cc[VEOL] = (char) - 1;
+
+#if 0
+ /* Disable the non-posix control characters */
+ game_termio.c_cc[VEOL2] = (char) - 1;
+ game_termio.c_cc[VSWTCH] = (char) - 1;
+ game_termio.c_cc[VDSUSP] = (char) - 1;
+ game_termio.c_cc[VREPRINT] = (char) - 1;
+ game_termio.c_cc[VDISCARD] = (char) - 1;
+ game_termio.c_cc[VWERASE] = (char) - 1;
+ game_termio.c_cc[VLNEXT] = (char) - 1;
+ game_termio.c_cc[VSTATUS] = (char) - 1;
+#endif
+
+ /* Normally, block until a character is read */
+ game_termio.c_cc[VMIN] = 1;
+ game_termio.c_cc[VTIME] = 0;
+
+ /* Hack -- Turn off "echo" and "canonical" mode */
+ game_termio.c_lflag &= ~(ECHO | ICANON);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the default game characters */
+ (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCGLTC, (char *)&game_ltchars);
+ (void)ioctl(0, TIOCLGET, (char *)&game_local_chars);
+
+ /* Force interupt (^C) */
+ game_tchars.t_intrc = (char)3;
+
+ /* Force start/stop (^Q, ^S) */
+ game_tchars.t_startc = (char)17;
+ game_tchars.t_stopc = (char)19;
+
+ /* Cancel some things */
+ game_tchars.t_quitc = (char) - 1;
+ game_tchars.t_eofc = (char) - 1;
+ game_tchars.t_brkc = (char) - 1;
+
+ /* Force suspend (^Z) */
+ game_ltchars.t_suspc = (char)26;
+
+ /* Cancel some things */
+ game_ltchars.t_dsuspc = (char) - 1;
+ game_ltchars.t_rprntc = (char) - 1;
+ game_ltchars.t_flushc = (char) - 1;
+ game_ltchars.t_werasc = (char) - 1;
+ game_ltchars.t_lnextc = (char) - 1;
+
+ /* XXX XXX XXX XXX Verify this before use */
+ /* Hack -- Turn off "echo" and "canonical" mode */
+ /* game_termios.c_lflag &= ~(ECHO | ICANON); */
+ game_ttyb.flag &= ~(ECHO | ICANON);
+
+#endif
+
+}
+
+
+
+
+
+
+
+
+/*
+ * Suspend/Resume
+ */
+static errr Term_xtra_cap_alive(int v)
+{
+ /* Suspend */
+ if (!v)
+ {
+ if (!active) return (1);
+
+ /* Hack -- make sure the cursor is visible */
+ curs_set(1);
+
+ /* Move to bottom right */
+ do_move(0, rows - 1, 0, rows - 1);
+
+ /* Go to normal keymap mode */
+ keymap_norm();
+
+ /* No longer active */
+ active = FALSE;
+ }
+
+ /* Resume */
+ else
+ {
+ if (active) return (1);
+
+ /* Hack -- restore the cursor location */
+ do_move(curx, cury, curx, cury);
+
+ /* Hack -- restore the cursor visibility */
+ curs_set(curv);
+
+ /* Go to angband keymap mode */
+ keymap_game();
+
+ /* Now we are active */
+ active = TRUE;
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Process an event
+ */
+static errr Term_xtra_cap_event(int v)
+{
+ int i, arg;
+ char buf[2];
+
+ /* Wait */
+ if (v)
+ {
+ /* Wait for one byte */
+ i = read(0, buf, 1);
+
+ /* Hack -- Handle "errors" */
+ if ((i <= 0) && (errno != EINTR)) exit_game_panic();
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Get the current flags for stdin */
+ if ((arg = fcntl(0, F_GETFL, 0)) < 1) return (1);
+
+ /* Tell stdin not to block */
+ if (fcntl(0, F_SETFL, arg | O_NDELAY) < 0) return (1);
+
+ /* Read one byte, if possible */
+ i = read(0, buf, 1);
+
+ /* Replace the flags for stdin */
+ if (fcntl(0, F_SETFL, arg)) return (1);
+ }
+
+ /* No keys ready */
+ if ((i != 1) || (!buf[0])) return (1);
+
+ /* Enqueue the keypress */
+ Term_keypress(buf[0]);
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+/*
+ * Actually move the hardware cursor
+ */
+static errr Term_curs_cap(int x, int y)
+{
+ /* Literally move the cursor */
+ do_move(curx, cury, x, y);
+
+ /* Save the cursor location */
+ curx = x;
+ cury = y;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a grid of space
+ *
+ * XXX XXX XXX Note that we will never be asked to clear the
+ * bottom line all the way to the bottom right edge, since we
+ * have set the "avoid the bottom right corner" flag.
+ */
+static errr Term_wipe_cap(int x, int y, int n)
+{
+ int dx;
+
+ /* Place the cursor */
+ Term_curs_cap(x, y);
+
+ /* Wipe to end of line */
+ if (x + n >= 80)
+ {
+ do_ce();
+ }
+
+ /* Wipe region */
+ else
+ {
+ for (dx = 0; dx < n; ++dx)
+ {
+ putc(' ', stdout);
+ curx++;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_cap(int x, int y, int n, byte a, cptr s)
+{
+ int i;
+
+ /* Move the cursor */
+ Term_curs_cap(x, y);
+
+ /* Dump the text, advance the cursor */
+ for (i = 0; s[i]; i++)
+ {
+ /* Dump the char */
+ putc(s[i], stdout);
+
+ /* Advance cursor 'X', and wrap */
+ if (++curx >= cols)
+ {
+ /* Reset cursor 'X' */
+ curx = 0;
+
+ /* Hack -- Advance cursor 'Y', and wrap */
+ if (++cury == rows) cury = 0;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_cap(int n, int v)
+{
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ do_cl();
+ do_move(0, 0, 0, 0);
+ return (0);
+
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ (void)write(1, "\007", 1);
+ return (0);
+
+ /* Change the cursor visibility */
+ case TERM_XTRA_SHAPE:
+ curv = v;
+ curs_set(v);
+ return (0);
+
+ /* Suspend/Resume */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_cap_alive(v));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_cap_event(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_cap_event(FALSE));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+ }
+
+ /* Not parsed */
+ return (1);
+}
+
+
+
+
+/*
+ * Init a "term" for this file
+ */
+static void Term_init_cap(term *t)
+{
+ if (active) return;
+
+ /* Assume cursor at top left */
+ curx = 0;
+ cury = 0;
+
+ /* Assume visible cursor */
+ curv = 1;
+
+ /* Clear the screen */
+ do_cl();
+
+ /* Hack -- visible cursor */
+ curs_set(1);
+
+ /* Assume active */
+ active = TRUE;
+}
+
+
+/*
+ * Nuke a "term" for this file
+ */
+static void Term_nuke_cap(term *t)
+{
+ if (!active) return;
+
+ /* Hack -- make sure the cursor is visible */
+ curs_set(1);
+
+ /* Move to bottom right */
+ do_move(0, rows - 1, 0, rows - 1);
+
+ /* Normal keymap */
+ keymap_norm();
+
+ /* No longer active */
+ active = FALSE;
+}
+
+
+
+
+
+
+
+
+
+
+/*
+ * Prepare this file for Angband usage
+ */
+errr init_cap(void)
+{
+ term *t = &term_screen_body;
+
+
+ /*** Initialize ***/
+
+ /* Initialize the screen */
+ if (init_cap_aux()) return ( -1);
+
+ /* Hack -- Require large screen, or Quit with message */
+ if ((rows < 24) || (cols < 80)) quit("Screen too small!");
+
+
+ /*** Prepare to play ***/
+
+ /* Extract the normal keymap */
+ keymap_norm_prepare();
+
+ /* Extract the game keymap */
+ keymap_game_prepare();
+
+ /* Hack -- activate the game keymap */
+ keymap_game();
+
+ /* Hack -- Do NOT buffer stdout */
+ setbuf(stdout, NULL);
+
+
+ /*** Now prepare the term ***/
+
+ /* Initialize the term */
+ term_init(t, 80, 24, 256);
+
+ /* Avoid the bottom right corner */
+ t->icky_corner = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Set some hooks */
+ t->init_hook = Term_init_cap;
+ t->nuke_hook = Term_nuke_cap;
+
+ /* Set some more hooks */
+ t->text_hook = Term_text_cap;
+ t->wipe_hook = Term_wipe_cap;
+ t->curs_hook = Term_curs_cap;
+ t->xtra_hook = Term_xtra_cap;
+
+ /* Save the term */
+ term_screen = t;
+
+ /* Activate it */
+ Term_activate(term_screen);
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* USE_CAP */
+
+
diff --git a/src/main-crb.c b/src/main-crb.c
new file mode 100644
index 00000000..7c8d11b7
--- /dev/null
+++ b/src/main-crb.c
@@ -0,0 +1,6450 @@
+/* File: main-crb.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Keith Randall, Peter Ammon, Ron Anderson
+ * and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with Macintosh computers running OS X,
+ * or OS 8/9 with CarbonLib system extention.
+ *
+ * To use this file, use an appropriate "Makefile" or "Project File", which
+ * should define "MACINTOSH".
+ *
+ * The official compilation uses the CodeWarrior Pro compiler.
+ *
+ * If you are never going to use "graphics" (especially if you are not
+ * compiling support for graphics anyway) then you can delete the "pict"
+ * resources with id "1001", "1002", "1003" and "1004" with no dangerous
+ * side effects.
+ *
+ *
+ * This file assumes that you will be using a PPC Mac running OS X
+ * or OS 8/9 (8.6 or greater) with CarbonLib system extention enabled.
+ * In fact, the game will refuse to run unless these features are available.
+ *
+ * MACH_O_CARBON code pushes the system requirement a bit further, and
+ * I don't think it works on System 8, even with CarbonLib, because it uses
+ * the Bundle services, but I may be wrong.
+ *
+ * Note that the "preference" file is now a simple XML text file
+ * called "<program name>.plist" in case of PEF Carbon, and "<Java-style
+ * program id defined in Info.plist>.plist" for Mach-O Carbon, which contains
+ * key-value paris, so it no longer has to check version stamp to validate
+ * its contents.
+ *
+ *
+ * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c"
+ * should probably be "unloaded" as soon as they are no longer needed,
+ * to save space, but I do not know how to do this. XXX XXX XXX
+ *
+ * Stange bug -- The first "ClipRect()" call crashes if the user closes
+ * all the windows, switches to another application, switches back, and
+ * re-opens the main window, for example, using "command-a". XXX XXX XXX
+ *
+ *
+ * Initial framework (and most code) by Ben Harrison (benh@phial.com).
+ *
+ * Some code adapted from "MacAngband 2.6.1" by Keith Randall
+ *
+ * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl).
+ *
+ * Most Apple Event code provided by Steve Linberg (slinberg@crocker.com).
+ *
+ * Most of the graphics code is adapted from an extremely minimal subset of
+ * the "Sprite World II" package, an amazing (and free) animation package.
+ *
+ * Carbon code adapted from works by Peter Ammon and Ron Anderson.
+ *
+ * (List of changes made by "pelpel" follow)
+ * Some API calls are updated to OS 8.x-- ones.
+ *
+ * Pixmap locking code in Term_pict_map() follows Carbon Porting Guide
+ * by Apple.
+ *
+ * The idle loop in TERM_XTRA_DELAY is rewritten to sleep on WaitNextEvent
+ * for a couple of reasons.
+ *
+ * CheckEvent now really blocks whenever asked to wait.
+ *
+ * The unused buffer GWorld is completely removed. It has long been pure waste
+ * of memory.
+ *
+ * The default font-size combination was changed because the old one, Monaco
+ * at 12 points causes the redraw artefact problem on OS X.
+ *
+ * Characters in the ASCII mode are clipped by their bounding rects to reduce
+ * redraw artefacts that were quite annoying in certain font-point combos.
+ *
+ * Transparency effect now avoids double bitblts whenever possible.
+ *
+ * Old tiles were drawn in a wrong fashion by the USE_TRANSPARENCY code.
+ *
+ * ASCII and the two graphics modes are now controlled by single graf_mode
+ * variable. arg_* and use_* variables are set when requested mode is
+ * successfully initialised.
+ *
+ * Most of the menus are now loaded from resources.
+ *
+ * Moved TileWidth and TileHeight menus into Special. There were too many menus.
+ *
+ * Added support for 32x32 tiles, now for [V] only.
+ *
+ * Related to the above, globe_init no longer loads tile images twice if
+ * a tileset doesn't have corresponding masks.
+ *
+ * Added support for POSIX-style pathnames, for Mach-O Carbon (gcc, CW >= 7).
+ * We can finally live without Pascal strings to handle files this way.
+ *
+ * (Mach-O Carbon) Graphics tiles are moved out of the resource fork into
+ * bundle-based data fork files.
+ *
+ * Changed size-related menu code, because they no longer function because
+ * some APIs have been changed to return Unicode in some cases.
+ *
+ * Changed the transparency code again, this time using Ron Anderson's code,
+ * which makes more sound assumption about background colour and is more
+ * efficient.
+ *
+ * The old asynchronous sound player could try to lock the same handle more
+ * than once, load same sound resource already in use, or unlock and release
+ * currently playing sound.
+ *
+ * hook_quit() now releases memory-related resources dynamically allocated by
+ * the graphics and sound code.
+ *
+ * Important Resources in the resource file:
+ *
+ * FREF 130 = ANGBAND_CREATOR / 'APPL' (application)
+ * FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file)
+ * FREF 130 = ANGBAND_CREATOR / 'TEXT' (bone file, generic text file)
+ * FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file)
+ *
+ * DLOG 128 = "About Angband..."
+ *
+ * ALRT 128 = unused (?)
+ * ALRT 129 = "Warning..."
+ *
+ * DITL 128 = body for DLOG 128
+ * DITL 129 = body for ALRT 129
+ * DITL 130 = body for ALRT 130
+ *
+ * ICON 128 = "warning" icon
+ *
+ * MBAR 128 = array of MENU id's (128, 129, 130, 131, 132, 133, 134)
+ * MENU 128 = apple (about, -, ...)
+ * MENU 129 = File (new, open, close, save, -, score, quit)
+ * (If SAVEFILE_SCREEN is defined)
+ * MENU 129 = File (close, save, -, score, quit)
+ * MENU 130 = Edit (undo, -, cut, copy, paste, clear)
+ * MENU 131 = Font (bold, wide, -)
+ * MENU 132 = Size ()
+ * MENU 133 = Windows ()
+ * MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle,
+ * Wizard)
+ * Graphics have following submenu attached:
+ * MENU 144 = Graphics (None, 8x8, 16x16, 32x32, enlarge tiles)
+ * TileWidth and TileHeight submenus are filled in by this program.
+ * MENU 145 = TileWidth ()
+ * MENU 146 = TileHeight ()
+ *
+ * On CFM(PEF) Carbon only:
+ * PICT 1001 = Graphics tile set (8x8)
+ * PICT 1002 = Graphics tile set (16x16 images)
+ * PICT 1004 = Graphics tile set (32x32)
+ *
+ * Mach-O Carbon now uses data fork resources:
+ * 8x8.png = Graphics tile set (8x8)
+ * 16x16.png = Graphics tile set (16x16 images)
+ * 32x32.png = Graphics tile set (32x32)
+ * These files should go into the Resources subdirectory of an application
+ * bundle.
+ *
+ * STR# 128 = "Please select the "lib" folder"
+ *
+ * plst 0 can be empty, but required for single binary Carbon apps on OS X
+ * Isn't necessary for Mach-O Carbon.
+ *
+ *
+ * File name patterns:
+ * all 'APEX' files have a filename of the form "*:apex:*" (?)
+ * all 'BONE' files have a filename of the form "*:bone:*" (?)
+ * all 'DATA' files have a filename of the form "*:data:*"
+ * all 'SAVE' files have a filename of the form "*:save:*"
+ * all 'USER' files have a filename of the form "*:user:*" (?)
+ *
+ * Perhaps we should attempt to set the "_ftype" flag inside this file,
+ * to avoid nasty file type information being spread all through the
+ * rest of the code. (?) This might require adding hooks into the
+ * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX
+ *
+ *
+ * Reasons for each header file:
+ *
+ * angband.h = Angband header file
+ *
+ * Types.h = (included anyway)
+ * Gestalt.h = gestalt code
+ * QuickDraw.h = (included anyway)
+ * OSUtils.h = (included anyway)
+ * Files.h = file code
+ * Fonts.h = font code
+ * Menus.h = menu code
+ * Dialogs.h = dialog code
+ * Windows.h = (included anyway)
+ * Palettes.h = palette code
+ * ToolUtils.h = HiWord() / LoWord()
+ * Events.h = event code
+ * Resources.h = resource code
+ * Controls.h = button code
+ * SegLoad.h = ExitToShell(), AppFile, etc
+ * Memory.h = NewPtr(), etc
+ * QDOffscreen.h = GWorld code
+ * Sound.h = Sound code
+ * Navigation.h = save file / lib locating dialogues
+ * CFPreferences.h = Preferences
+ * CFNumber.h = read/write short values from/to preferences
+ */
+
+/*
+ * Yet another main-xxx.c for Carbon (pelpel) - revision 11d
+ *
+ * Since I'm using CodeWarrior, the traditional header files are
+ * #include'd below.
+ *
+ * I also compiled Angband 3.0.2 successfully with OS X's gcc.
+ * Please follow these instructions if you are interested.
+ *
+ * ---(developer CD gcc + makefile porting notes, for Angband 3.0.2)-------
+ * 1. Compiling the binary
+ *
+ * If you try this on OS X + gcc, please use makefile.std, replacing
+ * main.c and main.o with main-crb.c and main-crb.o, removing all main-xxx.c
+ * and main-xxx.o from SRCS and OBJS, and, and use these settings:
+ *
+ * COPTS = -Wall -O1 -g -fpascal-strings
+ * INCLUDES =
+ * DEFINES = -DMACH_O_CARBON -DANGBAND30X
+ * LIBS = -framework CoreFoundation -framework QuickTime -framework Carbon
+ *
+ * -DANGBAND30X only affects main-crb.c. This is because I'm also compiling
+ * a couple of variants, and this arrangement makes my life easier.
+ *
+ * Never, ever #define MACINTOSH. It'll wreck havoc in system interface
+ * (mostly because of totally different pathname convention).
+ *
+ * You might wish to disable some SET_UID features for various reasons:
+ * to have user folder within the lib folder, savefile names etc.
+ *
+ * For the best compatibility with the Classic ports and my PEF Carbon
+ * ports, my_fopen, fd_make and fd_open [in util.c] should call
+ * (void)fsetfileinfo(buf, _fcreator, _ftype);
+ * when a file is successfully opened. Or you'll see odd icons for some files
+ * in the lib folder. In order to do so, extern.h should contain these lines,
+ * within #ifdef MACH_O_CARBON:
+ * extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype);
+ * extern u32b _fcreator;
+ * extern u32b _ftype;
+ * And enable the four FILE_TYPE macros in h-config.h for defined(MACH_O_CARBON)
+ * in addition to defined(MACINTOSH) && !defined(applec), i.e.
+ * #if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON)
+ *
+ * This is a very good way to spot bugs in use of these macros, btw.
+ *
+ * 2. Installation
+ *
+ * The "angband" binary must be arranged this way for it to work:
+ *
+ * lib/ <- the lib folder
+ * Angband (OS X).app/
+ * Contents/
+ * MacOS/
+ * angband <- the binary you've just compiled
+ * Info.plist <- to be explained below
+ * Resources/
+ * Angband.icns
+ * Data.icns
+ * Edit.icns
+ * Save.icns
+ * 8x8.png <- 8x8 tiles
+ * 16x16.png <- 16x16 tiles
+ * angband.rsrc <- see below
+ *
+ * 3. Preparing Info.plist
+ *
+ * Info.plist is an XML file describing some attributes of an application,
+ * and this is appropriate for Angband:
+ *
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <plist version="1.0">
+ * <dict>
+ * <key>CFBundleName</key><string>Angband</string>
+ * <key>CFBundleDisplayName</key><string>Angband (OS X)</string>
+ * <key>CFBundleExecutable</key><string>angband</string>
+ * <key>CFBundlePackageType</key><string>APPL</string>
+ * <key>CFBundleSignature</key><string>A271</string>
+ * <key>CFBundleVersion</key><string>3.0.2</string>
+ * <key>CFBundleShortVersionString</key><string>3.0.2</string>
+ * <key>CFBundleIconFile</key><string>Angband</string>
+ * <key>CFBundleIdentifier</key><string>net.thangorodrim.Angband</string>
+ * <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
+ * <key>CFBundleDocumentTypes</key>
+ * <array>
+ * <dict>
+ * <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ * <key>CFBundleTypeIconFile</key><string>Save</string>
+ * <key>CFBundleTypeName</key><string>Angband saved game</string>
+ * <key>CFBundleTypeOSTypes</key><array><string>SAVE</string></array>
+ * <key>CFBundleTypeRole</key><string>Editor</string>
+ * </dict>
+ * <dict>
+ * <key>CFBundleTypeExtentions</key><array><string>*</string></array>
+ * <key>CFBundleTypeIconFile</key><string>Edit</string>
+ * <key>CFBundleTypeName</key><string>Angband game data</string>
+ * <key>CFBundleTypeOSTypes</key><array><string>TEXT</string></array>
+ * <key>CFBundleTypeRole</key><string>Editor</string>
+ * </dict>
+ * <dict>
+ * <key>CFBundleTypeExtentions</key><array><string>raw</string></array>
+ * <key>CFBundleTypeIconFile</key><string>Data</string>
+ * <key>CFBundleTypeName</key><string>Angband game data</string>
+ * <key>CFBundleTypeOSTypes</key><array><string>DATA</string></array>
+ * <key>CFBundleTypeRole</key><string>Editor</string>
+ * </dict>
+ * </array>
+ * </dict>
+ * </plist>
+ *
+ * 4. Menu, diaglogue and gfx resources
+ *
+ * The binary assumes angband.rsrc should be in the traditional resource
+ * mangager format. Please run this command to create it from its textual
+ * description:
+ *
+ * Rez -i /Developer/Headers/FlatCarbon -d MACH_O -o angband.rsrc Angband.r
+ *
+ * The command is in /Developer/Tools. You might wish to include it in your
+ * PATH.
+ *
+ * It's better to comment out the definitions of BNDL and plst resources
+ * before you do that. I think you can DeRez the resulting tome.rsrc and
+ * feed it to the Interface Builder to produce a set of compatible .nib files,
+ * but this file also needs to be updated to understand .nib... On the other
+ * hand, I really don't like to hardcode UI definitions in C.
+ *
+ * Graphics resources are moved out of the resource fork and become ordinary
+ * PNG files. Make sure to set its resolution to 72 dpi (<- VERY important)
+ * while keeping vertical and horizontal scaling factor to 100% (<- VERY
+ * important), when you convert tiles in any formats to PNG. This means
+ * that the real size of an image must shrink or grow when you change it's dpi.
+ *
+ * Sound resources are a bit more complicated.
+ * The easiest way is:
+ * 1) Grab recent Mac Angband binary.
+ * 2) Run this command:
+ * DeRez -only 'snd ' (Angband binary) > sound.r
+ * 3) And specify sound.r files in addition to Angband.r when you run Rez.
+ *
+ * ---(end of OS X + gcc porting note)--------------------------------------
+ *
+ * Code adapted from Peter Ammon's work on 2.8.3 and some modifications
+ * are made when Apple's Carbon Porting Guide says they are absolutely
+ * necessary. Other arbirary changes are mostly because of my hatred
+ * of deep nestings and indentations. The code for controlling graphics modes
+ * have been thoroughly revised simply because I didn't like it (^ ^;).
+ * A bonus of this is that graphics settings can be loaded from Preferences
+ * quite easily.
+ *
+ * I also took Ron Anderson's (minimising the use of local-global coordinate
+ * conversions). Some might say his QuickTime multimedia is the most
+ * significant achievement... Play your favourite CD instead, if you really
+ * miss that (^ ^;) I might consider incorporating it if it makes use of
+ * event notification.
+ *
+ * I replaced some old API calls with new (OS 8.x--) ones, especially
+ * when I felt Apple is strongly against their continued usage.
+ *
+ * Similarly, USE_SFL_CODE should be always active, so I removed ifdef's
+ * just to prevent accidents, as well as to make the code a bit cleaner.
+ *
+ * On the contrary, I deliberately left traditional resource interfaces.
+ * Whatever Apple might say, I abhor file name extentions. And keeping two
+ * different sets of resources for Classic and Carbon is just too much for
+ * a personal project XXX
+ *
+ * Because Carbon forbids the use of 68K code, ANGBAND_LITE_MAC sections
+ * are removed.
+ *
+ * Because the default font-size combination causes redraw artefact problem
+ * (some characters, even in monospace fonts, have negative left bearings),
+ * I introduced rather crude hack to clip all character drawings within
+ * their bounding rects. If you don't like this, please comment out the line
+ * #define CLIP_HACK
+ * below.
+ *
+ * The check for return values of AEProcessAppleEvent is removed,
+ * because it results in annoying dialogues on OS X, also because
+ * Apple says it isn't usually necessary.
+ *
+ * Because the always_pict code is *so* slow, I changed the graphics
+ * mode selection a bit to use higher_pict when a user chooses a fixed
+ * width font and doesn't changes tile width / height.
+ *
+ * Added support for David Gervais' 32x32 tiles.
+ *
+ * Replaced transparency effect code by Ron Anderson's.
+ *
+ * Added support for gcc & make compilation. They come free with OS X
+ * (on the developer CD). This means that it can be compiled as a bundle.
+ *
+ * For Mach-O Carbon binary, moved graphics tiles out of the
+ * resource fork and made them plain PNG files, to be stored in the application
+ * bundle's "Resources" subdirectory.
+ *
+ * For Mach-O Carbon binary, provided a compile-time option (USE_QT_SOUND) to
+ * move sound effect samples out of the resource fork and use *.wav files in
+ * the bundle's "Resources" subdirectory. The "*" part must match the names in
+ * angband_sound_name (variable.c) exactly. This doesn't hurt performance
+ * a lot in [V] (it's got ~25 sound events), but problematic in [Z]-based
+ * ones (they have somewhere around 70 sound events).
+ *
+ * You still can use the resource file that comes with the ext-mac archive
+ * on the Angband FTP server, with these additions:
+ * - MENUs 131--134 and 144--146, as described above
+ * - MBAR 128: just a array of 128 through 134
+ * - plst 0 : can be empty, although Apple recommends us to fill it in.
+ * - STR# 128 : something like "Please select your lib folder"
+ *
+ * Since this involves considerable amount of work, I attached
+ * a plain text resource definition (= Rez format) .
+ * I heavily commented on the file, hoping it could be easily adapted
+ * to future versions of Angband as well as variants.
+ * I omitted sound effects and graphic tiles to make it reasonably small.
+ * Please copy them from any recent Mac binaries - you can use, say ZAngband
+ * ones for Vanilla or [V]-based variants quite safely. T.o.M.E. uses fairly
+ * extended 16x16 tileset and it is maintained actively. IIRC Thangorodrim
+ * compile page has an intruction explaining how to convert tiles for
+ * use on the Mac... It can be tricky, depending on your choice of
+ * graphics utility. Remember setting resolution to 72 pixels per inch,
+ * while keeping vertical/horizontal scale factor to 100% and dump the
+ * result as a PICT from resource.
+ *
+ * To build Carbonised Angband with CodeWarrior, copy your PPC project
+ * and
+ * - replace main-mac.c in the project with this file (in the link order tab)
+ * - remove InterfaceLib and MathLib
+ * - add CarbonLib (found in Carbon SDK or CW's UniversalInterfaces) --
+ * if you have compiler/linker errors, you'll need Carbon SDK 1.1 or greater
+ * - replace MSL C.PPC.Lib with MSL C.Carbon.Lib (both found in
+ * MSL:MSL_C:MSL_MacOS:Lib:PPC)
+ * - leave MSL RuntimePPC.Lib as it is
+ * - don't forget to update resource file, as described above
+ * - as in Classic targets, you may have to include <unistd.h> and
+ * <fcntl.h>. The most convinient place for them is the first
+ * #ifdef MACINTOSH in h-system.h
+ * - check variant dependent ifdef's explained below, and add
+ * appropriate one(s) in your A-mac-h.pch.
+ */
+
+
+/*
+ * Force Carbon-compatible APIs
+ */
+#ifndef MACH_O_CARBON
+
+/* Can be CodeWarrior or MPW */
+# define TARGET_API_MAC_CARBON 1
+
+#else
+
+/*
+* Must be Mach-O Carbon target with OS X gcc.
+* No need to set TARGET_API_MAC_CARBON to 1 here, but I assume it should
+* be able to make efficient use of BSD functions, hence:
+*/
+# define USE_MALLOC
+/* Not yet */
+/* # define USE_NIB */
+
+#endif /* !MACH_O_CARBON */
+
+
+#include "angband.h"
+
+#if defined(MACINTOSH) || defined(MACH_O_CARBON)
+
+#ifdef PRIVATE_USER_PATH
+
+/*
+ * Check and create if needed the directory dirpath
+ */
+bool private_check_user_directory(cptr dirpath)
+{
+ /* Is this used anywhere else in *bands? */
+ struct stat stat_buf;
+
+ int ret;
+
+ /* See if it already exists */
+ ret = stat(dirpath, &stat_buf);
+
+ /* It does */
+ if (ret == 0)
+ {
+ /* Now we see if it's a directory */
+ if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) return (TRUE);
+
+ /*
+ * Something prevents us from create a directory with
+ * the same pathname
+ */
+ return (FALSE);
+ }
+
+ /* No - this maybe the first time. Try to create a directory */
+ else
+ {
+ /* Create the ~/.ToME directory */
+ ret = mkdir(dirpath, 0700);
+
+ /* An error occured */
+ if (ret == -1) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+ }
+}
+
+/*
+ * Check existence of ".ToME/" directory in the user's
+ * home directory or try to create it if it doesn't exist.
+ * Returns FALSE if all the attempts fail.
+ */
+static bool check_create_user_dir(void)
+{
+ char dirpath[1024];
+ char versionpath[1024];
+ char savepath[1024];
+#ifdef PRIVATE_USER_PATH_DATA
+ char datapath[1024];
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ char apexpath[1024];
+#endif
+
+ /* Get an absolute path from the filename */
+ path_parse(dirpath, 1024, PRIVATE_USER_PATH);
+ strcpy(versionpath, dirpath);
+ strcat(versionpath, USER_PATH_VERSION);
+ strcpy(savepath, versionpath);
+ strcat(savepath, "/save");
+#ifdef PRIVATE_USER_PATH_DATA
+ strcpy(datapath, versionpath);
+ strcat(datapath, "/data");
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ strcpy(apexpath, versionpath);
+ strcat(apexpath, "/apex");
+#endif
+
+ return /* don't forget, the dirpath muts come first */
+ private_check_user_directory(dirpath) &&
+ private_check_user_directory(versionpath) &&
+#ifdef PRIVATE_USER_PATH_DATA
+ private_check_user_directory(datapath) &&
+#endif
+#ifdef PRIVATE_USER_PATH_APEX
+ private_check_user_directory(apexpath) &&
+#endif
+ private_check_user_directory(savepath);
+}
+
+#endif /* PRIVATE_USER_PATH */
+
+
+/*
+ * Variant-dependent features:
+ *
+ * #define ALLOW_BIG_SCREEN (V, Ey, O, T.o.M.E., and Z. Dr's big screen needs
+ * more work. New S one is too idiosyncratic...)
+ * #define ANG281_RESET_VISUALS (Cth, Gum, T.o.M.E., Z)
+ * #define SAVEFILE_SCREEN (T.o.M.E.)
+ * #define ZANG_AUTO_SAVE (O and Z)
+ * #define HAS_SCORE_MENU (V and T.o.M.E.)
+ * #define ANGBAND_CREATOR four letter code for your variant, if any.
+ * or use the default one.
+ *
+ * For [Z], you also have to say -- #define inkey_flag (p_ptr->inkey_flag)
+ * but before that, please, please consider using main-mac-carbon.c in [Z],
+ * that has some interesting features.
+ */
+
+/* Some porting examples */
+#ifdef ANGBAND30X
+# define USE_DOUBLE_TILES
+# define ALLOW_BIG_SCREEN
+# define HAS_SCORE_MENU
+# define NEW_ZVIRT_HOOKS
+/* I can't ditch this, yet, because there are many variants */
+# define USE_TRANSPARENCY
+#endif /* ANGBAND30X */
+
+# define USE_DOUBLE_TILES
+# define SAVEFILE_SCREEN
+# define ANG281_RESET_VISUALS
+# define ALLOW_BIG_SCREEN
+# define HAS_SCORE_MENU
+# define ANGBAND_CREATOR 'PrnA'
+
+/* Default creator signature */
+#ifndef ANGBAND_CREATOR
+# define ANGBAND_CREATOR 'A271'
+#endif
+
+
+/*
+ * Use rewritten asynchronous sound player
+ */
+#define USE_ASYNC_SOUND
+
+
+/*
+ * A rather crude fix to reduce amount of redraw artefacts.
+ * Some fixed width fonts (i.e. Monaco) has characters with negative
+ * left bearings, so Term_wipe_mac or overwriting cannot completely
+ * erase them. This could be introduced to Classic Mac OS ports too,
+ * but since I've never heard any complaints and I don't like to
+ * make 68K ports even slower, I won't do so there.
+ */
+#define CLIP_HACK /* */
+
+/*
+ * To cope with pref file related problems. It no longer has to be acculate,
+ * because preferences are stored in plist.
+ */
+#define PREF_VER_MAJOR VERSION_MAJOR
+#define PREF_VER_MINOR VERSION_MINOR
+#define PREF_VER_PATCH VERSION_PATCH
+#define PREF_VER_EXTRA VERSION_EXTRA
+
+
+/*
+ * In OS X + gcc, use <Carbon/Carbon.h>, <CoreServices/CoreServices.h> and
+ * <CoreFoundation/CoreFoundation.h> for ALL of these, including the Apple
+ * Event ones. <QuickTime/QuickTime.h> is used by the tile loading code.
+ */
+#ifdef MACH_O_CARBON
+
+#include <Carbon/Carbon.h>
+#include <QuickTime/QuickTime.h>
+#include <CoreServices/CoreServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#else /* MACH_O_CARBON */
+
+#include <Types.h>
+#include <Gestalt.h>
+#include <QuickDraw.h>
+#include <Files.h>
+#include <Fonts.h>
+#include <Menus.h>
+#include <Dialogs.h>
+#include <Windows.h>
+#include <Palettes.h>
+#include <ToolUtils.h>
+#include <Events.h>
+#include <SegLoad.h>
+#include <Resources.h>
+#include <Controls.h>
+#include <Memory.h>
+#include <QDOffscreen.h>
+#include <Sound.h>
+#include <Navigation.h>
+#include <CFPreferences.h>
+#include <CFNumber.h>
+#include <AppleEvents.h>
+#include <EPPC.h>
+#include <Folders.h>
+
+#endif /* MACH_O_CARBON */
+
+/* MacOSX == Unix == Good */
+#ifdef USE_MACOSX
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#endif
+
+
+/*
+ * Use "malloc()" instead of "NewPtr()"
+ */
+/* #define USE_MALLOC */
+
+
+/*
+ * Information about each of the 256 available colors
+ */
+static RGBColor color_info[256];
+
+
+#ifdef MACH_O_CARBON
+
+/*
+ * Creator signature and file type - Didn't I say that I abhor file name
+ * extentions? Names and metadata are entirely different set of notions.
+ */
+OSType _fcreator;
+OSType _ftype;
+
+#endif /* MACH_O_CARBON */
+
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+/*
+ * Extra "term" data
+ */
+struct term_data
+{
+ term *t;
+
+ Rect r;
+
+ WindowPtr w;
+
+
+ short padding;
+
+ short pixelDepth;
+
+ GWorldPtr theGWorld; /* not used ... */
+
+ GDHandle theGDH;
+
+ GDHandle mainSWGDH; /* not used ... */
+
+ Str15 title;
+
+ s16b oops;
+
+ s16b keys;
+
+ s16b last;
+
+ s16b mapped;
+
+ s16b rows;
+ s16b cols;
+
+ s16b font_id;
+ s16b font_size;
+ s16b font_face;
+ s16b font_mono;
+
+ s16b font_o_x;
+ s16b font_o_y;
+ s16b font_wid;
+ s16b font_hgt;
+
+ s16b tile_o_x;
+ s16b tile_o_y;
+ s16b tile_wid;
+ s16b tile_hgt;
+
+ s16b size_wid;
+ s16b size_hgt;
+
+ s16b size_ow1;
+ s16b size_oh1;
+ s16b size_ow2;
+ s16b size_oh2;
+};
+
+
+
+
+/*
+ * Forward declare -- see below
+ */
+static bool CheckEvents(bool wait);
+
+
+#ifndef MACH_O_CARBON
+
+/*
+ * Hack -- location of the main directory
+ */
+static short app_vol;
+static long app_dir;
+
+#endif /* !MACH_O_CARBON */
+
+
+/*
+ * Delay handling of double-clicked savefiles
+ */
+Boolean open_when_ready = FALSE;
+
+/*
+ * Delay handling of pre-emptive "quit" event
+ */
+Boolean quit_when_ready = FALSE;
+
+
+/*
+ * Aqua automatically supplies the Quit menu.
+ */
+static Boolean is_aqua = FALSE;
+
+/*
+ * Version of Mac OS - for version specific bug workarounds (; ;)
+ */
+static long mac_os_version;
+
+
+/*
+ * Hack -- game in progress
+ */
+static int game_in_progress = 0;
+
+
+/*
+ * Only do "SetPort()" when needed
+ */
+static WindowPtr active = NULL;
+
+
+/*
+ * Maximum number of terms
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Note when "open"/"new" become valid
+ */
+static bool initialized = FALSE;
+
+
+
+/*
+ * Convert a C string to a pascal string in place
+ *
+ * This function may be defined elsewhere, but since it is so
+ * small, it is not worth finding the proper function name for
+ * all the different platforms.
+ */
+static void ctopstr(StringPtr src)
+{
+ int i;
+ byte len;
+
+ /* Hack -- pointer */
+ char *s = (char*)(src);
+
+ len = strlen(s);
+
+ /* Hack -- convert the string */
+ for (i = len; i > 1; i--) s[i] = s[i - 1];
+
+ /* Hack -- terminate the string */
+ s[0] = len;
+}
+
+
+#ifdef MACH_O_CARBON
+
+/* Carbon File Manager utilities by pelpel */
+
+/*
+ * (Carbon)
+ * Convert a pathname to a corresponding FSSpec.
+ * Returns noErr on success.
+ */
+static OSErr path_to_spec(const char *path, FSSpec *spec)
+{
+ OSErr err;
+ FSRef ref;
+
+ /* Convert pathname to FSRef ... */
+ err = FSPathMakeRef(path, &ref, NULL);
+ if (err != noErr) return (err);
+
+ /* ... then FSRef to FSSpec */
+ err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
+
+ /* Inform caller of success or failure */
+ return (err);
+}
+
+
+/*
+ * (Carbon)
+ * Convert a FSSpec to a corresponding pathname.
+ * Returns noErr on success.
+ */
+static OSErr spec_to_path(const FSSpec *spec, char *buf, size_t size)
+{
+ OSErr err;
+ FSRef ref;
+
+ /* Convert FSSpec to FSRef ... */
+ err = FSpMakeFSRef(spec, &ref);
+ if (err != noErr) return (err);
+
+ /* ... then FSRef to pathname */
+ err = FSRefMakePath(&ref, buf, size);
+
+ /* Inform caller of success or failure */
+ return (err);
+}
+
+
+/*
+ * (Carbon) [via path_to_spec]
+ * Set creator and filetype of a file specified by POSIX-style pathname.
+ * Returns 0 on success, -1 in case of errors.
+ */
+int fsetfileinfo(char *pathname, OSType fcreator, OSType ftype)
+{
+ OSErr err;
+ FSSpec spec;
+ FInfo info;
+
+ /* Convert pathname to FSSpec */
+ if (path_to_spec(pathname, &spec) != noErr) return ( -1);
+
+ /* Obtain current finder info of the file */
+ if (FSpGetFInfo(&spec, &info) != noErr) return ( -1);
+
+ /* Overwrite creator and type */
+ info.fdCreator = fcreator;
+ info.fdType = ftype;
+ err = FSpSetFInfo(&spec, &info);
+
+ /* Inform caller of success or failure */
+ return ((err == noErr) ? 0 : -1);
+}
+
+
+#else /* MACH_O_CARBON */
+
+/*
+* Convert refnum+vrefnum+fname into a full file name
+* Store this filename in 'buf' (make sure it is long enough)
+* Note that 'fname' looks to be a "pascal" string
+*/
+static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname)
+{
+ DirInfo pb;
+ Str255 name;
+ int err;
+ int i, j;
+
+ char res[1000];
+
+ i = 999;
+
+ res[i] = 0;
+ i--;
+ for (j = 1; j <= fname[0]; j++)
+ {
+ res[i - fname[0] + j] = fname[j];
+ }
+ i -= fname[0];
+
+ pb.ioCompletion = NULL;
+ pb.ioNamePtr = name;
+ pb.ioVRefNum = vrefnum;
+ pb.ioDrParID = refnum;
+ pb.ioFDirIndex = -1;
+
+ while (1)
+ {
+ pb.ioDrDirID = pb.ioDrParID;
+ err = PBGetCatInfoSync((CInfoPBPtr) & pb);
+ res[i] = ':';
+ i--;
+ for (j = 1; j <= name[0]; j++)
+ {
+ res[i - name[0] + j] = name[j];
+ }
+ i -= name[0];
+
+ if (pb.ioDrDirID == fsRtDirID) break;
+ }
+
+ /* Extract the result */
+ for (j = 0, i++; res[i]; j++, i++) buf[j] = res[i];
+ buf[j] = 0;
+}
+
+
+/*
+* Convert a pascal string in place
+*
+* This function may be defined elsewhere, but since it is so
+* small, it is not worth finding the proper function name for
+* all the different platforms.
+*/
+static void ptocstr(StringPtr src)
+{
+ int i;
+
+ /* Hack -- pointer */
+ char *s = (char*)(src);
+
+ /* Hack -- convert the string */
+ for (i = s[0]; i; i--, s++) s[0] = s[1];
+
+ /* Hack -- terminate the string */
+ s[0] = '\0';
+}
+
+
+/*
+* Utility routines by Steve Linberg
+*
+* The following three routines (pstrcat, pstrinsert, and PathNameFromDirID)
+* were taken from the Think Reference section called "Getting a Full Pathname"
+* (under the File Manager section). We need PathNameFromDirID to get the
+* full pathname of the opened savefile, making no assumptions about where it
+* is.
+*
+* I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully
+* nice.
+*/
+static void pstrcat(StringPtr dst, StringPtr src)
+{
+ /* copy string in */
+ BlockMove(src + 1, dst + *dst + 1, *src);
+
+ /* adjust length byte */
+ *dst += *src;
+}
+
+
+/*
+* pstrinsert - insert string 'src' at beginning of string 'dst'
+*/
+static void pstrinsert(StringPtr dst, StringPtr src)
+{
+ /* make room for new string */
+ BlockMove(dst + 1, dst + *src + 1, *dst);
+
+ /* copy new string in */
+ BlockMove(src + 1, dst + 1, *src);
+
+ /* adjust length byte */
+ *dst += *src;
+}
+
+
+static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName)
+{
+ CInfoPBRec block;
+ Str255 directoryName;
+ OSErr err;
+
+ fullPathName[0] = '\0';
+
+ block.dirInfo.ioDrParID = dirID;
+ block.dirInfo.ioNamePtr = directoryName;
+
+ while (1)
+ {
+ block.dirInfo.ioVRefNum = vRefNum;
+ block.dirInfo.ioFDirIndex = -1;
+ block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID;
+ err = PBGetCatInfoSync(&block);
+ pstrcat(directoryName, (StringPtr)"\p:");
+ pstrinsert(fullPathName, directoryName);
+ if (block.dirInfo.ioDrDirID == 2) break;
+ }
+}
+
+#endif /* MACH_O_CARBON */
+
+
+
+
+/*
+ * Center a rectangle inside another rectangle
+ *
+ * Consider using RepositionWindow() whenever possible
+ */
+static void center_rect(Rect *r, Rect *s)
+{
+ int centerx = (s->left + s->right) / 2;
+ int centery = (2 * s->top + s->bottom) / 3;
+ int dx = centerx - (r->right - r->left) / 2 - r->left;
+ int dy = centery - (r->bottom - r->top) / 2 - r->top;
+ r->left += dx;
+ r->right += dx;
+ r->top += dy;
+ r->bottom += dy;
+}
+
+
+/*
+ * Activate a given window, if necessary
+ */
+static void activate(WindowPtr w)
+{
+ /* Activate */
+ if (active != w)
+ {
+ /* Activate */
+ if (w) SetPort(GetWindowPort(w));
+
+ /* Remember */
+ active = w;
+ }
+}
+
+
+/*
+ * Display a warning message
+ */
+static void mac_warning(cptr warning)
+{
+ Str255 text;
+ int len, i;
+
+ /* Limit of 250 chars */
+ len = strlen(warning);
+ if (len > 250) len = 250;
+
+ /* Make a "Pascal" string */
+ text[0] = len;
+ for (i = 0; i < len; i++) text[i + 1] = warning[i];
+
+ /* Prepare the dialog box values */
+ ParamText(text, "\p", "\p", "\p");
+
+ /* Display the Alert, wait for Okay */
+ Alert(129, 0L);
+}
+
+
+
+/*** Some generic functions ***/
+
+/*
+ * Hack -- activate a color (0 to 255)
+ */
+static void term_data_color(term_data *td, int a)
+{
+ /* Activate the color */
+ if (td->last != a)
+ {
+ /* Activate the color */
+ RGBForeColor(&color_info[a]);
+
+ /* Memorize color */
+ td->last = a;
+ }
+}
+
+
+/*
+ * Hack -- Apply and Verify the "font" info
+ *
+ * This should usually be followed by "term_data_check_size()"
+ *
+ * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt
+ * you have to reset them to zero before this function is called.
+ * XXX XXX This is automatic when the program starts because the term_data
+ * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e.
+ * font, font style and size changes.
+ */
+static void term_data_check_font(term_data *td)
+{
+ int i;
+
+ FontInfo info;
+
+ WindowPtr old = active;
+
+
+ /* Activate */
+ activate(td->w);
+
+ /* Instantiate font */
+ TextFont(td->font_id);
+ TextSize(td->font_size);
+ TextFace(td->font_face);
+
+ /* Extract the font info */
+ GetFontInfo(&info);
+
+ /* Assume monospaced */
+ td->font_mono = TRUE;
+
+ /* Extract the font sizing values XXX XXX XXX */
+ td->font_wid = CharWidth('@'); /* info.widMax; */
+ td->font_hgt = info.ascent + info.descent;
+ td->font_o_x = 0;
+ td->font_o_y = info.ascent;
+
+ /* Check important characters */
+ for (i = 33; i < 127; i++)
+ {
+ /* Hack -- notice non-mono-space */
+ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE;
+
+ /* Hack -- collect largest width */
+ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i);
+ }
+
+ /* Set default offsets */
+ td->tile_o_x = td->font_o_x;
+ td->tile_o_y = td->font_o_y;
+
+ /* Set default tile size */
+ if (td->tile_wid == 0) td->tile_wid = td->font_wid;
+ if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt;
+
+ /* Re-activate the old window */
+ activate(old);
+}
+
+
+/*
+ * Hack -- Apply and Verify the "size" info
+ */
+static void term_data_check_size(term_data *td)
+{
+ if (td == &data[0])
+ {
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Forbid resizing of the Angband window */
+ td->cols = 80;
+ td->rows = 24;
+
+#else
+
+ /* Enforce minimal size */
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+
+#endif /* !ALLOW_BIG_SCREEN */
+ }
+
+ /* Information windows can be much smaller */
+ else
+ {
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Enforce maximal sizes */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+
+ /* Minimal tile size */
+ if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid;
+ if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt;
+
+ /* Default tile offsets */
+ td->tile_o_x = (td->tile_wid - td->font_wid) / 2;
+ td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2;
+
+ /* Minimal tile offsets */
+ if (td->tile_o_x < 0) td->tile_o_x = 0;
+ if (td->tile_o_y < 0) td->tile_o_y = 0;
+
+ /* Apply font offsets */
+ td->tile_o_x += td->font_o_x;
+ td->tile_o_y += td->font_o_y;
+
+ /* Calculate full window size */
+ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
+ td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ {
+ BitMap tScreen;
+
+ /* Get current screen */
+ (void)GetQDGlobalsScreenBits(&tScreen);
+
+ /* Verify the top */
+ if (td->r.top > tScreen.bounds.bottom - td->size_hgt)
+ {
+ td->r.top = tScreen.bounds.bottom - td->size_hgt;
+ }
+
+ /* Verify the top */
+ if (td->r.top < tScreen.bounds.top + GetMBarHeight())
+ {
+ td->r.top = tScreen.bounds.top + GetMBarHeight();
+ }
+
+ /* Verify the left */
+ if (td->r.left > tScreen.bounds.right - td->size_wid)
+ {
+ td->r.left = tScreen.bounds.right - td->size_wid;
+ }
+
+ /* Verify the left */
+ if (td->r.left < tScreen.bounds.left)
+ {
+ td->r.left = tScreen.bounds.left;
+ }
+ }
+
+ /* Calculate bottom right corner */
+ td->r.right = td->r.left + td->size_wid;
+ td->r.bottom = td->r.top + td->size_hgt;
+
+ /* Assume no graphics */
+ td->t->higher_pict = FALSE;
+ td->t->always_pict = FALSE;
+
+
+ /* Handle graphics */
+ if (use_graphics)
+ {
+ /* Use higher pict whenever possible */
+ if (td->font_mono) td->t->higher_pict = TRUE;
+
+ /* Use always_pict only when necessary */
+ else td->t->always_pict = TRUE;
+ }
+
+ /* Fake mono-space */
+ if (!td->font_mono ||
+ (td->font_wid != td->tile_wid) ||
+ (td->font_hgt != td->tile_hgt))
+ {
+ /*
+ * Handle fake monospace
+ *
+ * pelpel: This is SLOW. Couldn't we use CharExtra
+ * and SpaceExtra for monospaced fonts?
+ */
+ if (td->t->higher_pict) td->t->higher_pict = FALSE;
+ td->t->always_pict = TRUE;
+ }
+}
+
+
+/*
+ * Hack -- resize a term_data
+ *
+ * This should normally be followed by "term_data_redraw()"
+ */
+static void term_data_resize(term_data *td)
+{
+ /*
+ * Actually resize the window
+ *
+ * ResizeWindow is the preferred API call, but it cannot
+ * be used here.
+ */
+ SizeWindow(td->w, td->size_wid, td->size_hgt, 0);
+}
+
+
+
+/*
+ * Hack -- redraw a term_data
+ *
+ * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR"
+ */
+static void term_data_redraw(term_data *td)
+{
+ term *old = Term;
+ Rect tRect;
+
+ /* Activate the term */
+ Term_activate(td->t);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Flush the output */
+ Term_fresh();
+
+ /* Restore the old term */
+ Term_activate(old);
+
+ /* No need to redraw */
+ ValidWindowRect(td->w, GetPortBounds(GetWindowPort(td->w), &tRect));
+}
+
+
+/*
+ * Graphics support
+ */
+
+/* Set by Term_xtra_mac_react */
+#ifdef MACH_O_CARBON
+static CFStringRef pict_id; /* PICT id of image tiles */
+#else
+static int pict_id; /* PICT id of image tiles */
+#endif /* MACH_O_CARBON */
+
+static int graf_width; /* Width of a tile in pixels */
+static int graf_height; /* Height of a tile in pixels */
+
+/* Calculated by PICT loading code */
+static int pict_cols; /* Number of columns in tiles */
+static int pict_rows; /* Number of rows in tiles */
+
+/* Available graphics modes */
+#define GRAF_MODE_NONE 0 /* plain ASCII */
+#define GRAF_MODE_8X8 1 /* 8x8 tiles */
+#define GRAF_MODE_16X16 2 /* 16x16 tiles */
+#define GRAF_MODE_32X32 3 /* 32x32 tiles */
+
+static int graf_mode = GRAF_MODE_NONE; /* current graphics mode */
+static int graf_mode_req = GRAF_MODE_NONE; /* requested graphics mode */
+
+#define TR_NONE 0 /* No transparency */
+#define TR_OVER 1 /* Overwriting with transparent black pixels */
+static int transparency_mode = TR_NONE; /* types of transparency effect */
+
+
+/*
+ * Forward Declare
+ */
+typedef struct FrameRec FrameRec;
+
+/*
+ * Frame
+ *
+ * - GWorld for the frame image
+ * - Handle to pix map (saved for unlocking/locking)
+ * - Pointer to color pix map (valid only while locked)
+ */
+struct FrameRec
+{
+ GWorldPtr framePort;
+ PixMapHandle framePixHndl;
+ PixMapPtr framePix;
+};
+
+
+/*
+ * The global picture data
+ */
+static FrameRec *frameP = NULL;
+
+
+/*
+ * Lock a frame
+ */
+static void BenSWLockFrame(FrameRec *srcFrameP)
+{
+ PixMapHandle pixMapH;
+
+ pixMapH = GetGWorldPixMap(srcFrameP->framePort);
+ (void)LockPixels(pixMapH);
+ HLockHi((Handle)pixMapH);
+ srcFrameP->framePixHndl = pixMapH;
+ srcFrameP->framePix = (PixMapPtr)(*(Handle)pixMapH);
+}
+
+
+/*
+ * Unlock a frame
+ */
+static void BenSWUnlockFrame(FrameRec *srcFrameP)
+{
+ if (srcFrameP->framePort != NULL)
+ {
+ HUnlock((Handle)srcFrameP->framePixHndl);
+ UnlockPixels(srcFrameP->framePixHndl);
+ }
+
+ srcFrameP->framePix = NULL;
+}
+
+
+
+#ifdef MACH_O_CARBON
+
+/* Moving graphics resources into data fork -- pelpel */
+
+/*
+ * (Carbon, Bundle)
+ * Given base and type names of a resource, find a file in the
+ * current application bundle and return its FSSpec in the third argument.
+ * Returns true on success, false otherwise.
+ * e.g. get_resource_spec(CFSTR("8x8"), CFSTR("png"), &spec);
+ */
+static Boolean get_resource_spec(
+ CFStringRef base_name, CFStringRef type_name, FSSpec *spec)
+{
+ CFURLRef res_url;
+ FSRef ref;
+
+ /* Find the tile resource specified in the current bundle */
+ res_url = CFBundleCopyResourceURL(
+ CFBundleGetMainBundle(), base_name, type_name, NULL);
+
+ /* Oops */
+ if (res_url == NULL) return (false);
+
+ /* Convert CFURL to FSRef */
+ (void)CFURLGetFSRef(res_url, &ref);
+
+ /* Convert FSRef to FSSpec */
+ (void)FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
+
+ /* Free allocated CF data */
+ CFRelease(res_url);
+
+ /* Success */
+ return (true);
+}
+
+
+/*
+ * (QuickTime)
+ * Create a off-screen GWorld from contents of a file specified by a FSSpec.
+ *
+ * Globals referenced: data[0], graf_height, graf_width
+ * Globals updated: pict_rows, pict_cols.
+ */
+static OSErr create_gworld_from_spec(
+ GWorldPtr *tile_gw, FSSpec *tile_spec)
+{
+ OSErr err;
+ GraphicsImportComponent gi;
+ GWorldPtr gw, tmp_gw;
+ GDHandle gdh, tmp_gdh;
+ Rect r;
+ SInt16 depth;
+
+ /* See if QuickTime understands the file format */
+ err = GetGraphicsImporterForFile(tile_spec, &gi);
+
+ /* Oops */
+ if (err != noErr) return (err);
+
+ /* Get depth */
+ depth = data[0].pixelDepth;
+
+ /* Get GDH */
+ gdh = data[0].theGDH;
+
+ /* Retrieve the rect of the image */
+ err = GraphicsImportGetNaturalBounds(gi, &r);
+
+ /* Adjust it, so that the upper left corner becomes (0, 0) */
+ OffsetRect(&r, -r.left, -r.top);
+
+ /* Calculate and set numbers of rows and columns */
+ pict_rows = r.bottom / graf_height;
+ pict_cols = r.right / graf_width;
+
+ /* Create a GWorld */
+ err = NewGWorld(&gw, depth, &r, NULL, gdh, noNewDevice);
+
+ /* Oops */
+ if (err != noErr) return (err);
+
+ /* Save the pointer to the GWorld */
+ *tile_gw = gw;
+
+ /* Save the current GWorld */
+ GetGWorld(&tmp_gw, &tmp_gdh);
+
+ /* Activate the newly created GWorld */
+ (void)GraphicsImportSetGWorld(gi, gw, NULL);
+
+ /* Prevent pixmap from moving while drawing */
+ (void)LockPixels(GetGWorldPixMap(gw));
+
+ /* Clear the pixels */
+ EraseRect(&r);
+
+ /* Draw the image into it */
+ (void)GraphicsImportDraw(gi);
+
+ /* Release the lock*/
+ UnlockPixels(GetGWorldPixMap(gw));
+
+ /* Restore GWorld */
+ SetGWorld(tmp_gw, tmp_gdh);
+
+ /* Close the image importer */
+ CloseComponent(gi);
+
+ /* Success */
+ return (noErr);
+}
+
+#else /* MACH_O_CARBON */
+
+static OSErr BenSWCreateGWorldFromPict(
+ GWorldPtr *pictGWorld, PicHandle pictH)
+{
+ OSErr err;
+ GWorldPtr saveGWorld;
+ GDHandle saveGDevice;
+ GWorldPtr tempGWorld;
+ Rect pictRect;
+ short depth;
+ GDHandle theGDH;
+
+ tempGWorld = NULL;
+
+ /* Reset */
+ *pictGWorld = NULL;
+
+ /* Get depth */
+ depth = data[0].pixelDepth;
+
+ /* Get GDH */
+ theGDH = data[0].theGDH;
+
+ /* Obtain size rectangle */
+ pictRect = (**pictH).picFrame;
+ OffsetRect(&pictRect, -pictRect.left, -pictRect.top);
+
+ /* Calculate and set numbers of rows and columns */
+ pict_rows = pictRect.bottom / graf_height;
+ pict_cols = pictRect.right / graf_width;
+
+ /* Create a GWorld */
+ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice);
+
+ /* Oops */
+ if (err != noErr) return (err);
+
+ /* Save pointer */
+ *pictGWorld = tempGWorld;
+
+ /* Save GWorld */
+ GetGWorld(&saveGWorld, &saveGDevice);
+
+ /* Activate */
+ SetGWorld(tempGWorld, nil);
+
+ /* Dump the pict into the GWorld */
+ (void)LockPixels(GetGWorldPixMap(tempGWorld));
+ EraseRect(&pictRect);
+ DrawPicture(pictH, &pictRect);
+ UnlockPixels(GetGWorldPixMap(tempGWorld));
+
+ /* Restore GWorld */
+ SetGWorld(saveGWorld, saveGDevice);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* MACH_O_CARBON */
+
+
+/*
+ * Init the global "frameP"
+ */
+static errr globe_init(void)
+{
+ OSErr err;
+
+ GWorldPtr tempPictGWorldP;
+
+#ifdef MACH_O_CARBON
+ FSSpec pict_spec;
+#else
+ PicHandle newPictH;
+#endif /* MACH_O_CARBON */
+
+
+ /* Use window XXX XXX XXX */
+ SetPort(GetWindowPort(data[0].w));
+
+
+#ifdef MACH_O_CARBON
+
+ /* Get the tile resources */
+ if (!get_resource_spec(pict_id, CFSTR("png"), &pict_spec)) return ( -1);
+
+ /* Create GWorld */
+ err = create_gworld_from_spec(&tempPictGWorldP, &pict_spec);
+
+#else /* MACH_O_CARBON */
+
+ /* Get the pict resource */
+ if ((newPictH = GetPicture(pict_id)) == 0) return ( -1);
+
+ /* Create GWorld */
+ err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH);
+
+ /* Release resource */
+ ReleaseResource((Handle)newPictH);
+
+#endif /* MACH_O_CARBON */
+
+ /* Error */
+ if (err != noErr) return (err);
+
+ /* Create the frame */
+ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec));
+
+ /* Analyze result */
+ if (frameP == NULL) return ( -1);
+
+ /* Save GWorld */
+ frameP->framePort = tempPictGWorldP;
+
+ /* Lock it */
+ BenSWLockFrame(frameP);
+
+ /* Success */
+ return (noErr);
+}
+
+
+/*
+ * Nuke the global "frameP"
+ */
+static errr globe_nuke(void)
+{
+ /* Dispose */
+ if (frameP)
+ {
+ /* Unlock */
+ BenSWUnlockFrame(frameP);
+
+ /* Dispose of the GWorld */
+ DisposeGWorld(frameP->framePort);
+
+ /* Dispose of the memory */
+ DisposePtr((Ptr)frameP);
+
+ /* Forget */
+ frameP = NULL;
+ }
+
+ /* Flush events */
+ FlushEvents(everyEvent, 0);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_ASYNC_SOUND
+
+/*
+ * Asynchronous sound player - completely revised (beta)
+ */
+#if defined(USE_QT_SOUND) && !defined(MACH_O_CARBON)
+# undef USE_QT_SOUND
+#endif /* USE_QT_SOUND && !MACH_O_CARBON */
+
+/*
+ * How many sound channels will be pooled
+ *
+ * Was: 20, but I don't think we need 20 sound effects playing
+ * simultaneously :) -- pelpel
+ */
+#define MAX_CHANNELS 8
+
+/*
+ * A pool of sound channels
+ */
+static SndChannelPtr channels[MAX_CHANNELS];
+
+/*
+ * Status of the channel pool
+ */
+static Boolean channel_initialised = FALSE;
+
+/*
+ * Data handles containing sound samples
+ */
+static SndListHandle samples[SOUND_MAX];
+
+/*
+ * Reference counts of sound samples
+ */
+static SInt16 sample_refs[SOUND_MAX];
+
+#define SOUND_VOLUME_MIN 0 /* Default minimum sound volume */
+#define SOUND_VOLUME_MAX 255 /* Default maximum sound volume */
+#define VOLUME_MIN 0 /* Minimum sound volume in % */
+#define VOLUME_MAX 100 /* Maximum sound volume in % */
+#define VOLUME_INC 5 /* Increment sound volume in % */
+
+/*
+ * I'm just too lazy to write a panel for this XXX XXX
+ */
+static int sound_volume = SOUND_VOLUME_MAX;
+
+
+#ifdef USE_QT_SOUND
+
+/*
+ * QuickTime sound, by Ron Anderson
+ *
+ * I didn't choose to use Windows-style .ini files (Ron wrote a parser
+ * for it, but...), nor did I use lib/xtra directory, hoping someone
+ * would code plist-based configuration code in the future -- pelpel
+ */
+
+/*
+ * (QuickTime)
+ * Load sound effects from data-fork resources. They are wav files
+ * with the same names as angband_sound_name[] (variable.c)
+ *
+ * Globals referenced: angband_sound_name[]
+ * Globals updated: samples[] (they can be *huge*)
+ */
+static void load_sounds(void)
+{
+ OSErr err;
+ int i;
+
+ /* Start QuickTime */
+ err = EnterMovies();
+
+ /* Error */
+ if (err != noErr) return;
+
+ /*
+ * This loop may take a while depending on the count and size of samples
+ * to load.
+ *
+ * We should use a progress dialog for this.
+ */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Apple APIs always give me headacke :( */
+ CFStringRef name;
+ FSSpec spec;
+ SInt16 file_id;
+ SInt16 res_id;
+ Str255 movie_name;
+ Movie movie;
+ Track track;
+ Handle h;
+ Boolean res;
+
+ /* Allocate CFString with the name of sound event to be processed */
+ name = CFStringCreateWithCString(NULL, angband_sound_name[i],
+ kTextEncodingUS_ASCII);
+
+ /* Error */
+ if (name == NULL) continue;
+
+ /* Find sound sample resource with the same name */
+ res = get_resource_spec(name, CFSTR("wav"), &spec);
+
+ /* Free the reference to CFString */
+ CFRelease(name);
+
+ /* Error */
+ if (!res) continue;
+
+ /* Open the sound file */
+ err = OpenMovieFile(&spec, &file_id, fsRdPerm);
+
+ /* Error */
+ if (err != noErr) continue;
+
+ /* Create Movie from the file */
+ err = NewMovieFromFile(&movie, file_id, &res_id, movie_name,
+ newMovieActive, NULL);
+
+ /* Error */
+ if (err != noErr) goto close_file;
+
+ /* Get the first track of the movie */
+ track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic,
+ movieTrackCharacteristic | movieTrackEnabledOnly );
+
+ /* Error */
+ if (track == NULL) goto close_movie;
+
+ /* Allocate a handle to store sample */
+ h = NewHandle(0);
+
+ /* Error */
+ if (h == NULL) goto close_track;
+
+ /* Dump the sample into the handle */
+ err = PutMovieIntoTypedHandle(movie, track, soundListRsrc, h, 0,
+ GetTrackDuration(track), 0L, NULL);
+
+ /* Success */
+ if (err == noErr)
+ {
+ /* Store the handle in the sample list */
+ samples[i] = (SndListHandle)h;
+ }
+
+ /* Failure */
+ else
+ {
+ /* Free unused handle */
+ DisposeHandle(h);
+ }
+
+ /* Free the track */
+close_track:
+ DisposeMovieTrack(track);
+
+ /* Free the movie */
+close_movie:
+ DisposeMovie(movie);
+
+ /* Close the movie file */
+close_file:
+ CloseMovieFile(file_id);
+ }
+
+ /* Stop QuickTime */
+ ExitMovies();
+}
+
+#else /* USE_QT_SOUND */
+
+/*
+* Return a handle of 'snd ' resource given Angband sound event number,
+* or NULL if it isn't found.
+*
+* Globals referenced: angband_sound_name[] (variable.c)
+*/
+static SndListHandle find_sound(int num)
+{
+Str255 sound;
+
+/* Get the proper sound name */
+strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
+sound[0] = strlen((char*)sound + 1);
+
+/* Obtain resource XXX XXX XXX */
+return ((SndListHandle)GetNamedResource('snd ', sound));
+}
+
+#endif /* USE_QT_SOUND */
+
+
+/*
+ * Clean up sound support - to be called when the game exits.
+ *
+ * Globals referenced: channels[], samples[], sample_refs[].
+ */
+static void cleanup_sound(void)
+{
+ int i;
+
+ /* No need to clean it up */
+ if (!channel_initialised) return;
+
+ /* Dispose channels */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Drain sound commands and free the channel */
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Free sound data */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Still locked */
+ if ((sample_refs[i] > 0) && (samples[i] != NULL))
+ {
+ /* Unlock it */
+ HUnlock((Handle)samples[i]);
+ }
+
+#ifndef USE_QT_SOUND
+
+ /* Release it */
+ if (samples[i]) ReleaseResource((Handle)samples[i]);
+
+#else
+/* Free handle */
+ if (samples[i]) DisposeHandle((Handle)samples[i]);
+
+#endif /* !USE_QT_SOUND */
+ }
+}
+
+
+/*
+ * Play sound effects asynchronously -- pelpel
+ *
+ * I don't believe those who first started using the previous implementations
+ * imagined this is *much* more complicated as it may seem. Anyway,
+ * introduced round-robin scheduling of channels and made it much more
+ * paranoid about HLock/HUnlock.
+ *
+ * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done
+ * using channel's callback procedures, which set global flags, and
+ * a procedure hooked into CheckEvents does housekeeping. On the other
+ * hand, this lazy reclaiming strategy keeps things simple (no interrupt
+ * time code) and provides a sort of cache for sound data.
+ *
+ * Globals referenced: channel_initialised, channels[], samples[],
+ * sample_refs[].
+ * Globals updated: channel_initialised, channels[], sample_refs[].
+ * Only in !USE_QT_SOUND, samples[].
+ */
+static void play_sound(int num, int vol)
+{
+ OSErr err;
+ int i;
+ int prev_num;
+ SndListHandle h;
+ SndChannelPtr chan;
+ SCStatus status;
+
+ static int next_chan;
+ static SInt16 channel_occupants[MAX_CHANNELS];
+ static SndCommand volume_cmd, quiet_cmd;
+
+
+ /* Initialise sound channels */
+ if (!channel_initialised)
+ {
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Paranoia - Clear occupant table */
+ /* channel_occupants[i] = 0; */
+
+ /* Create sound channel for all sounds to play from */
+ err = SndNewChannel(&channels[i], sampledSynth, initMono, NULL);
+
+ /* Error */
+ if (err != noErr)
+ {
+ /* Free channels */
+ while (--i >= 0)
+ {
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Notify error */
+ plog("Cannot initialise sound channels!");
+
+ /* Cancel request */
+ use_sound = arg_sound = FALSE;
+
+ /* Failure */
+ return;
+ }
+ }
+
+ /* First channel to use */
+ next_chan = 0;
+
+ /* Prepare volume command */
+ volume_cmd.cmd = volumeCmd;
+ volume_cmd.param1 = 0;
+ volume_cmd.param2 = 0;
+
+ /* Prepare quiet command */
+ quiet_cmd.cmd = quietCmd;
+ quiet_cmd.param1 = 0;
+ quiet_cmd.param2 = 0;
+
+ /* Initialisation complete */
+ channel_initialised = TRUE;
+ }
+
+ /* Paranoia */
+ if ((num <= 0) || (num >= SOUND_MAX)) return;
+
+ /* Prepare volume command */
+ volume_cmd.param2 = (SInt16)((vol << 4) | vol);
+
+ /* Channel to use (round robin) */
+ chan = channels[next_chan];
+
+ /* See if the resource is already in use */
+ if (sample_refs[num] > 0)
+ {
+ /* Resource in use */
+ h = samples[num];
+
+ /* Increase the refcount */
+ sample_refs[num]++;
+ }
+
+ /* Sound is not currently in use */
+ else
+ {
+ /* Get handle for the sound */
+#ifdef USE_QT_SOUND
+ h = samples[num];
+#else
+ h = find_sound(num);
+#endif /* USE_QT_SOUND */
+
+ /* Sample not available */
+ if (h == NULL) return;
+
+#ifndef USE_QT_SOUND
+
+ /* Load resource */
+ LoadResource((Handle)h);
+
+ /* Remember it */
+ samples[num] = h;
+
+#endif /* !USE_QT_SOUND */
+
+ /* Lock the handle */
+ HLock((Handle)h);
+
+ /* Initialise refcount */
+ sample_refs[num] = 1;
+ }
+
+ /* Poll the channel */
+ err = SndChannelStatus(chan, sizeof(SCStatus), &status);
+
+ /* It isn't available */
+ if ((err != noErr) || status.scChannelBusy)
+ {
+ /* Shut it down */
+ SndDoImmediate(chan, &quiet_cmd);
+ }
+
+ /* Previously played sound on this channel */
+ prev_num = channel_occupants[next_chan];
+
+ /* Process previously played sound */
+ if (prev_num != 0)
+ {
+ /* Decrease refcount */
+ sample_refs[prev_num]--;
+
+ /* We can free it now */
+ if (sample_refs[prev_num] <= 0)
+ {
+ /* Unlock */
+ HUnlock((Handle)samples[prev_num]);
+
+#ifndef USE_QT_SOUND
+
+ /* Release */
+ ReleaseResource((Handle)samples[prev_num]);
+
+ /* Forget handle */
+ samples[prev_num] = NULL;
+
+#endif /* !USE_QT_SOUND */
+
+ /* Paranoia */
+ sample_refs[prev_num] = 0;
+ }
+ }
+
+ /* Remember this sound as the current occupant of the channel */
+ channel_occupants[next_chan] = num;
+
+ /* Set up volume for channel */
+ SndDoImmediate(chan, &volume_cmd);
+
+ /* Play new sound asynchronously */
+ SndPlay(chan, h, TRUE);
+
+ /* Schedule next channel (round robin) */
+ next_chan++;
+ if (next_chan >= MAX_CHANNELS) next_chan = 0;
+}
+
+#endif /* USE_ASYNC_SOUND */
+
+
+
+
+/*** Support for the "z-term.c" package ***/
+
+
+/*
+ * Initialize a new Term
+ *
+ * Note also the "window type" called "noGrowDocProc", which might be more
+ * appropriate for the main "screen" window.
+ *
+ * Note the use of "srcCopy" mode for optimized screen writes.
+ */
+static void Term_init_mac(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+ WindowAttributes wattrs;
+ OSStatus err;
+
+ static RGBColor black = {0x0000, 0x0000, 0x0000};
+ static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
+
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Every window has close and collapse boxes */
+ wattrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute;
+
+ /* Information windows are resizable */
+ if (td != &data[0]) wattrs |= kWindowResizableAttribute;
+
+#else
+
+ /* Big screen - every window has close, collapse and resize boxes */
+ wattrs = kWindowCloseBoxAttribute |
+ kWindowCollapseBoxAttribute |
+ kWindowResizableAttribute;
+
+#endif /* !ALLOW_BIG_SCREEN */
+
+ /* Make the window */
+ err = CreateNewWindow(
+ kDocumentWindowClass,
+ wattrs,
+ &td->r,
+ &td->w);
+
+ /*
+ * XXX XXX Although the original main-mac.c doesn't perform error
+ * checking, it should be done here.
+ */
+
+ /* Set window title */
+ SetWTitle(td->w, td->title);
+
+ /* Activate the window */
+ activate(td->w);
+
+ /* Erase behind words */
+ TextMode(srcCopy);
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize the window */
+ term_data_resize(td);
+
+
+ /* Prepare the colors (real colors) */
+ RGBBackColor(&black);
+ RGBForeColor(&white);
+
+ /* Block */
+ {
+ Rect globalRect;
+ GDHandle mainGDH;
+ GDHandle currentGDH;
+ GWorldPtr windowGWorld;
+ PixMapHandle basePixMap;
+
+ /* Obtain the global rect */
+ GetWindowBounds((WindowRef)td->w, kWindowContentRgn, &globalRect);
+
+ /* Obtain the proper GDH */
+ mainGDH = GetMaxDevice(&globalRect);
+
+ /* Extract GWorld and GDH */
+ GetGWorld(&windowGWorld, &currentGDH);
+
+ /* Obtain base pixmap */
+ basePixMap = (**mainGDH).gdPMap;
+
+ /* Save pixel depth */
+ td->pixelDepth = (**basePixMap).pixelSize;
+
+ /* Save Window GWorld - unused */
+ td->theGWorld = windowGWorld;
+
+ /* Save Window GDH */
+ td->theGDH = currentGDH;
+
+ /* Save main GDH - unused */
+ td->mainSWGDH = mainGDH;
+ }
+
+ {
+ Rect portRect;
+
+ /* Get current Rect */
+ GetPortBounds(GetWindowPort(td->w), &portRect);
+
+ /* Clip to the window */
+ ClipRect(&portRect);
+
+ /* Erase the window */
+ EraseRect(&portRect);
+
+ /* Invalidate the window */
+ InvalWindowRect(td->w, &portRect);
+ }
+
+ /*
+ * A certain release of OS X fails to display windows at proper
+ * locations (_ _#)
+ */
+ if ((mac_os_version >= 0x1000) && (mac_os_version < 0x1010))
+ {
+ /* Hack - Make sure the window is displayed at (r.left,r.top) */
+ MoveWindow(td->w, td->r.left, td->r.top, 1);
+ }
+
+ /* Display the window if needed */
+ if (td->mapped)
+ {
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
+ }
+
+ /* Hack -- set "mapped" flag */
+ t->mapped_flag = td->mapped;
+
+ /* Forget color */
+ td->last = -1;
+}
+
+
+
+/*
+ * Nuke an old Term
+ */
+static void Term_nuke_mac(term *t)
+{
+ /* XXX */
+}
+
+
+
+/*
+ * Unused
+ */
+static errr Term_user_mac(int n)
+{
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_mac_react(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+
+ /* Reset color */
+ td->last = -1;
+
+ /* Update colors */
+ for (i = 0; i < 256; i++)
+ {
+ u16b rv, gv, bv;
+
+ /* Extract the R,G,B data */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Save the actual color */
+ color_info[i].red = (rv | (rv << 8));
+ color_info[i].green = (gv | (gv << 8));
+ color_info[i].blue = (bv | (bv << 8));
+ }
+
+
+ /* Handle sound */
+ if (use_sound != arg_sound)
+ {
+ /* Apply request */
+ use_sound = arg_sound;
+ }
+
+
+ /* Handle graphics */
+ if (graf_mode_req != graf_mode)
+ {
+ /* dispose old GWorld's if present */
+ globe_nuke();
+
+ /*
+ * Setup parameters according to request
+ *
+ * In [Z], you have to set use_graphics and arg_graphics to
+ * GRAPHICS_NONE, GRAPHICS_ORIGINAL or GRAPHICS_ADAM_BOLT, and
+ * comment ANGBAND_GRAF out.
+ */
+ switch (graf_mode_req)
+ {
+ /* ASCII - no graphics whatsoever */
+ case GRAF_MODE_NONE:
+ {
+ use_graphics = arg_graphics = FALSE;
+ transparency_mode = TR_NONE;
+ break;
+ }
+
+ /*
+ * 8x8 tiles (PICT id 1001)
+ * no transparency effect
+ * "old" graphics definitions
+ */
+ case GRAF_MODE_8X8:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "old";
+ transparency_mode = TR_NONE;
+#ifdef MACH_O_CARBON
+ pict_id = CFSTR("8x8");
+#else
+ pict_id = 1001;
+#endif /* MACH_O_CARBON */
+ graf_width = graf_height = 8;
+ break;
+ }
+
+ /*
+ * 16x16 tiles (images: PICT id 1002, masks: PICT id 1003)
+ * with transparency effect
+ * "new" graphics definitions
+ */
+ case GRAF_MODE_16X16:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "new";
+ transparency_mode = TR_OVER;
+#ifdef MACH_O_CARBON
+ pict_id = CFSTR("16x16");
+#else
+ pict_id = 1002;
+#endif /* MACH_O_CARBON */
+ graf_width = graf_height = 16;
+ break;
+ }
+
+ /*
+ * 32x32 tiles (images: PICT id 1004)
+ * with transparency effect
+ * "david" graphics definitions
+ * Vanilla-specific
+ */
+ case GRAF_MODE_32X32:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "david";
+ transparency_mode = TR_OVER;
+#ifdef MACH_O_CARBON
+ pict_id = CFSTR("32x32");
+#else
+ pict_id = 1004;
+#endif /* MACH_O_CARBON */
+ graf_width = graf_height = 32;
+ break;
+ }
+ }
+
+ /* load tiles and setup GWorlds if tiles are requested */
+ if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0))
+ {
+ /* Oops */
+ plog("Cannot initialize graphics!");
+
+ /* reject request */
+ graf_mode_req = GRAF_MODE_NONE;
+
+ /* reset graphics flags */
+ use_graphics = arg_graphics = FALSE;
+
+ /* reset transparency mode */
+ transparency_mode = TR_NONE;
+ }
+
+ /* update current graphics mode */
+ graf_mode = graf_mode_req;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize the window */
+ term_data_resize(td);
+
+ /* Reset visuals */
+#ifndef ANG281_RESET_VISUALS
+ reset_visuals(TRUE);
+#else
+ reset_visuals();
+#endif /* !ANG281_RESET_VISUALS */
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Do a "special thing"
+ */
+static errr Term_xtra_mac(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ Rect r;
+
+ /* Analyze */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Make a noise */
+ SysBeep(1);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Make a sound */
+ case TERM_XTRA_SOUND:
+ {
+#ifndef USE_ASYNC_SOUND
+
+ /*
+ * This may not be your choice, but much safer and much less
+ * resource hungry. Existing implementations can quite easily
+ * crash, by starting asynchronous playing and immediately
+ * unlocking and releasing the sound data just started playing...
+ * -- pelpel
+ */
+ Handle handle;
+ Str255 sound;
+
+ /* Get the proper sound name */
+ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[v]);
+ sound[0] = strlen((char*)sound + 1);
+
+ /* Obtain resource XXX XXX XXX */
+ handle = GetNamedResource('snd ', sound);
+
+ /* Oops -- it is a failure, but we return 0 anyway */
+ if (handle == NULL) return (0);
+
+ /* Load and Lock */
+ LoadResource(handle);
+ HLock(handle);
+
+ /* Play sound (wait for completion) */
+ SndPlay(NULL, (SndListHandle)handle, FALSE);
+
+ /* Unlock and release */
+ HUnlock(handle);
+ ReleaseResource(handle);
+
+#else /* !USE_ASYNC_SOUND */
+
+ /* Play sound */
+ play_sound(v, sound_volume);
+
+#endif /* !USE_ASYNC_SOUND */
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ /* Process an event */
+ (void)CheckEvents(FALSE);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process pending events */
+ case TERM_XTRA_EVENT:
+ {
+ /* Process an event */
+ (void)CheckEvents(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush all pending events (if any) */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Hack -- flush all events */
+ while (CheckEvents(FALSE)) /* loop */;
+
+ /* Success */
+ return (0);
+ }
+
+ /* Hack -- Change the "soft level" */
+ case TERM_XTRA_LEVEL:
+ {
+ /* Activate if requested */
+ if (v) activate(td->w);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ Rect portRect;
+
+ /* Get current Rect */
+ GetPortBounds(GetWindowPort(td->w), &portRect);
+
+ /* No clipping XXX XXX XXX */
+ ClipRect(&portRect);
+
+ /* Erase the window */
+ EraseRect(&portRect);
+
+ /* Set the color */
+ term_data_color(td, TERM_WHITE);
+
+ /* Frame the window in white */
+ MoveTo(0, 0);
+ LineTo(0, td->size_hgt - 1);
+ LineTo(td->size_wid - 1, td->size_hgt - 1);
+ LineTo(td->size_wid - 1, 0);
+
+ /* Clip to the new size */
+ r.left = portRect.left + td->size_ow1;
+ r.top = portRect.top + td->size_oh1;
+ r.right = portRect.right - td->size_ow2;
+ r.bottom = portRect.bottom - td->size_oh2;
+ ClipRect(&r);
+
+ /* Success */
+ return (0);
+ }
+
+ /* React to changes */
+ case TERM_XTRA_REACT:
+ {
+ /* React to changes */
+ return (Term_xtra_mac_react());
+ }
+
+ /* Delay (milliseconds) */
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * WaitNextEvent relinquishes CPU as well as
+ * induces a screen refresh on OS X
+ */
+
+ /* If needed */
+ if (v > 0)
+ {
+ EventRecord tmp;
+ UInt32 ticks;
+
+ /* Convert millisecs to ticks */
+ ticks = (v * 60L) / 1000;
+
+ /*
+ * Hack? - Put the programme into sleep.
+ * No events match ~everyEvent, so nothing
+ * should be lost in Angband's event queue.
+ * Even if ticks are 0, it's worth calling for
+ * the above mentioned reasons.
+ */
+ WaitNextEvent((EventMask)~everyEvent, &tmp, ticks, nil);
+ }
+
+ /* Success */
+ return (0);
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ char *s = strdup(angband_term_name[0]);
+
+ ctopstr((StringPtr)s);
+ SetWTitle(data[0].w, (StringPtr)s);
+
+ free(s);
+ return (0);
+ }
+
+/* MacOSX == Unix == Good */
+#ifdef USE_MACOSX
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+
+ closedir(directory);
+ return 0;
+ }
+#endif
+ }
+
+ /* Oops */
+ return (1);
+}
+
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ * Draw a "cursor" at (x,y), using a "yellow box".
+ * We are allowed to use "Term_what()" to determine
+ * the current screen contents (for inverting, etc).
+ */
+static errr Term_curs_mac(int x, int y)
+{
+ Rect r;
+
+ term_data *td = (term_data*)(Term->data);
+
+ /* Set the color */
+ term_data_color(td, TERM_YELLOW);
+
+ /* Frame the grid */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Adjust it if double width tiles are requested */
+ if (use_bigtile &&
+ (x + 1 < Term->wid) &&
+ (Term->old->a[y][x + 1] == 255))
+ {
+ r.right += td->tile_wid;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ FrameRect(&r);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Erase "n" characters starting at (x,y)
+ */
+static errr Term_wipe_mac(int x, int y, int n)
+{
+ Rect r;
+
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Erase the block of characters */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + n * td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+ EraseRect(&r);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw several ("n") chars, with an attr, at a given location.
+ */
+static errr Term_text_mac(int x, int y, int n, byte a, const char *cp)
+{
+ int xp, yp;
+
+#ifdef CLIP_HACK
+ Rect r;
+#endif /* CLIP_HACK */
+
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Set the color */
+ term_data_color(td, a);
+
+#ifdef CLIP_HACK
+ /* Hack - only draw within the bounding rect */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + n * td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+ ClipRect(&r);
+
+ /* Hack - clear the content of the bounding rect */
+ EraseRect(&r);
+#endif /* CLIP_HACK */
+
+ /* Starting pixel */
+ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1;
+ yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1;
+
+ /* Move to the correct location */
+ MoveTo(xp, yp);
+
+ /* Draw the character */
+ if (n == 1) DrawChar(*cp);
+
+ /* Draw the string */
+ else DrawText(cp, 0, n);
+
+#ifdef CLIP_HACK
+ /* Obtain current window's rect */
+ GetPortBounds(GetWindowPort(td->w), &r);
+
+ /* Clip to the window again */
+ ClipRect(&r);
+#endif /* CLIP_HACK */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Erase "n" characters starting at (x,y)
+ */
+#ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp)
+#endif /* USE_TRANSPARENCY */
+{
+ int i;
+ Rect dst_r;
+ GrafPtr port;
+ PixMapHandle pixmap_h;
+
+#ifdef CLIP_HACK
+ Rect portRect;
+#endif /* CLIP_HACK */
+
+ term_data *td = (term_data*)(Term->data);
+
+ static RGBColor black = {0x0000, 0x0000, 0x0000};
+ static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
+
+
+#ifdef CLIP_HACK
+ /* Remember current window's rect */
+ GetPortBounds(GetWindowPort(td->w), &portRect);
+#endif /* CLIP_HACK */
+
+ /* Destination rectangle */
+ dst_r.left = x * td->tile_wid + td->size_ow1;
+#ifndef USE_DOUBLE_TILES
+ dst_r.right = dst_r.left + td->tile_wid;
+#endif /* !USE_DOUBLE_TILES */
+ dst_r.top = y * td->tile_hgt + td->size_oh1;
+ dst_r.bottom = dst_r.top + td->tile_hgt;
+
+ /* Scan the input */
+ for (i = 0; i < n; i++)
+ {
+ bool done = FALSE;
+
+ byte a = *ap++;
+ char c = *cp++;
+
+#ifdef USE_TRANSPARENCY
+ byte ta = *tap++;
+ char tc = *tcp++;
+# ifdef USE_EGO_GRAPHICS
+ byte ea = *eap++;
+ char ec = *ecp++;
+ bool has_overlay = (ea && ec);
+# endif /* USE_EGO_GRAPHICS */
+#endif
+
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Hack -- a filler for double-width tile */
+ if (use_bigtile && (a == 255))
+ {
+ /* Advance */
+ dst_r.left += td->tile_wid;
+
+ /* Ignore */
+ continue;
+ }
+
+ /* Prepare right side of rectagle now */
+ dst_r.right = dst_r.left + td->tile_wid;
+
+#endif /* USE_DOUBLE_TILES */
+
+ /* Graphics -- if Available and Needed */
+ if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80))
+ {
+ int col, row;
+ Rect src_r;
+#ifdef USE_TRANSPARENCY
+ int t_col, t_row;
+ Rect terrain_r;
+# ifdef USE_EGO_GRAPHICS
+ int e_col, e_row;
+ Rect ego_r;
+# endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ /* Row and Col */
+ row = ((byte)a & 0x7F) % pict_rows;
+ col = ((byte)c & 0x7F) % pict_cols;
+
+ /* Source rectangle */
+ src_r.left = col * graf_width;
+ src_r.top = row * graf_height;
+ src_r.right = src_r.left + graf_width;
+ src_r.bottom = src_r.top + graf_height;
+
+#ifdef USE_TRANSPARENCY
+ /* Row and Col */
+ t_row = ((byte)ta & 0x7F) % pict_rows;
+ t_col = ((byte)tc & 0x7F) % pict_cols;
+
+ /* Source rectangle */
+ terrain_r.left = t_col * graf_width;
+ terrain_r.top = t_row * graf_height;
+ terrain_r.right = terrain_r.left + graf_width;
+ terrain_r.bottom = terrain_r.top + graf_height;
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* If there's an overlay */
+ if (has_overlay)
+ {
+ /* Row and Col */
+ e_row = ((byte)ea & 0x7F) % pict_rows;
+ e_col = ((byte)ec & 0x7F) % pict_cols;
+
+ /* Source rectangle */
+ ego_r.left = e_col * graf_width;
+ ego_r.top = e_row * graf_height;
+ ego_r.right = ego_r.left + graf_width;
+ ego_r.bottom = ego_r.top + graf_height;
+ }
+
+# endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Hardwire CopyBits */
+ RGBBackColor(&white);
+ RGBForeColor(&black);
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Double width tiles */
+ if (use_bigtile) dst_r.right += td->tile_wid;
+
+#endif /* USE_DOUBLE_TILES */
+
+ /*
+ * OS X requires locking and unlocking of window port
+ * when we draw directly to its pixmap.
+ * The Lock/Unlock protocol is described in the Carbon
+ * Porting Guide.
+ */
+
+ /* Obtain current window's graphic port */
+ port = GetWindowPort(td->w);
+
+ /* Lock pixels, so we can use handle safely */
+ LockPortBits(port);
+
+ /* Get Pixmap handle */
+ pixmap_h = GetPortPixMap(port);
+
+#ifdef USE_TRANSPARENCY
+
+ /* Transparency effect */
+ switch (transparency_mode)
+ {
+ /* No transparency effects */
+ case TR_NONE:
+ default:
+ {
+ /* Draw the picture */
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &src_r, &dst_r, srcCopy, NULL);
+
+ break;
+ }
+
+ /* Overwriting with transparent black pixels */
+ case TR_OVER:
+ {
+ /* Draw the terrain */
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &terrain_r, &dst_r, srcCopy, NULL);
+
+ /* Make black pixels transparent */
+ RGBBackColor(&black);
+
+ /* Draw mon/obj if there's one */
+ if ((row != t_row) || (col != t_col))
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &src_r, &dst_r, transparent, NULL);
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Draw overlay if there's one */
+ if (has_overlay)
+ {
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &ego_r, &dst_r, transparent, NULL);
+ }
+
+# endif /* USE_EGO_GRAPHICS */
+
+ break;
+ }
+ }
+
+#else /* USE_TRANSPARENCY */
+
+ /* Draw the picture */
+ CopyBits((BitMap*)frameP->framePix,
+ (BitMap*)*pixmap_h,
+ &src_r, &dst_r, srcCopy, NULL);
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Release the lock and dispose the PixMap handle */
+ UnlockPortBits(port);
+
+ /* Restore colors */
+ RGBBackColor(&black);
+ RGBForeColor(&white);
+
+ /* Forget color */
+ td->last = -1;
+
+ /* Done */
+ done = TRUE;
+ }
+
+ /* Normal */
+ if (!done)
+ {
+ int xp, yp;
+
+#ifdef CLIP_HACK
+ /* Hack - avoid writing outside of dst_r */
+ ClipRect(&dst_r);
+ /* Some characters do not match dst_r, therefore we have to... */
+#endif /* CLIP_HACK */
+
+ /* Erase */
+ EraseRect(&dst_r);
+
+ /* Set the color */
+ term_data_color(td, a);
+
+ /* Starting pixel */
+ xp = dst_r.left + td->tile_o_x;
+ yp = dst_r.top + td->tile_o_y;
+
+ /* Move to the correct location */
+ MoveTo(xp, yp);
+
+ /* Draw the character */
+ DrawChar(c);
+
+#ifdef CLIP_HACK
+ /* Clip to the window - inefficient (; ;) XXX XXX */
+ ClipRect(&portRect);
+#endif /* CLIP_HACK */
+ }
+
+ /* Advance */
+ dst_r.left += td->tile_wid;
+#ifndef USE_DOUBLE_TILES
+ dst_r.right += td->tile_wid;
+#endif /* !USE_DOUBLE_TILES */
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+
+/*
+ * Create and initialize window number "i"
+ */
+static void term_data_link(int i)
+{
+ term *old = Term;
+
+ term_data *td = &data[i];
+
+ /* Only once */
+ if (td->t) return;
+
+ /* Require mapped */
+ if (!td->mapped) return;
+
+ /* Allocate */
+ MAKE(td->t, term);
+
+ /* Initialize the term */
+ term_init(td->t, td->cols, td->rows, td->keys);
+
+ /* Use a "software" cursor */
+ td->t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ td->t->attr_blank = TERM_WHITE;
+ td->t->char_blank = ' ';
+
+ /* Prepare the init/nuke hooks */
+ td->t->init_hook = Term_init_mac;
+ td->t->nuke_hook = Term_nuke_mac;
+
+ /* Prepare the function hooks */
+ td->t->user_hook = Term_user_mac;
+ td->t->xtra_hook = Term_xtra_mac;
+ td->t->wipe_hook = Term_wipe_mac;
+ td->t->curs_hook = Term_curs_mac;
+ td->t->text_hook = Term_text_mac;
+ td->t->pict_hook = Term_pict_mac;
+
+#if 0
+
+ /* Doesn't make big difference? */
+ td->t->never_bored = TRUE;
+
+#endif
+
+ /* Link the local structure */
+ td->t->data = (void *)(td);
+
+ /* Activate it */
+ Term_activate(td->t);
+
+ /* Global pointer */
+ angband_term[i] = td->t;
+
+ /* Activate old */
+ Term_activate(old);
+}
+
+
+
+
+#ifdef MACH_O_CARBON
+
+/*
+ * (Carbon, Bundle)
+ * Return a POSIX pathname of the lib directory, or NULL if it can't be
+ * located. Caller must supply a buffer along with its size in bytes,
+ * where returned pathname will be stored.
+ * I prefer use of goto's to several nested if's, if they involve error
+ * handling. Sorry if you are offended by their presence. Modern
+ * languages have neater constructs for this kind of jobs -- pelpel
+ */
+static char *locate_lib(char *buf, size_t size)
+{
+ CFURLRef main_url = NULL;
+ CFStringRef main_str = NULL;
+ char *p;
+ char *res = NULL;
+
+ /* Obtain the URL of the main bundle */
+ main_url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+
+ /* Oops */
+ if (main_url == NULL) goto ret;
+
+ /* Convert it to POSIX pathname */
+ main_str = CFURLCopyFileSystemPath(main_url, kCFURLPOSIXPathStyle);
+
+ /* Oops */
+ if (main_str == NULL) goto ret;
+
+ /* Convert it again from darn unisomething encoding to ASCII */
+ if (CFStringGetCString(main_str, buf, size, kTextEncodingUS_ASCII) == FALSE)
+ goto ret;
+
+ /*
+ * Paranoia - bounds check
+ */
+ if (strlen(buf) + 25 + 1 > size) goto ret;
+
+ /* Location of the data */
+ strcat(buf, "/Contents/Resources/");
+
+ /* Set result */
+ res = buf;
+
+ret:
+
+ /* Release objects allocated and implicitly retained by the program */
+ if (main_str) CFRelease(main_str);
+ if (main_url) CFRelease(main_url);
+
+ /* pathname of the lib folder or NULL */
+ return (res);
+}
+
+
+#else /* MACH_O_CARBON */
+
+/*
+* Set the "current working directory" (also known as the "default"
+* volume/directory) to the location of the current application.
+*
+* Original code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl)
+*
+* Completely rewritten to use Carbon Process Manager. It retrieves the
+* volume and direcotry of the current application and simply stores it
+* in the (static) global variables app_vol and app_dir, but doesn't
+* mess with the "current working directory", because it has long been
+* an obsolete (and arcane!) feature.
+*/
+static void SetupAppDir(void)
+{
+ OSErr err;
+ ProcessSerialNumber curPSN;
+ ProcessInfoRec procInfo;
+ FSSpec cwdSpec;
+
+ /* Initialise PSN info for the current process */
+ curPSN.highLongOfPSN = 0;
+ curPSN.lowLongOfPSN = kCurrentProcess;
+
+ /* Fill in mandatory fields */
+ procInfo.processInfoLength = sizeof(ProcessInfoRec);
+ procInfo.processName = nil;
+ procInfo.processAppSpec = &cwdSpec;
+
+ /* Obtain current process information */
+ err = GetProcessInformation(&curPSN, &procInfo);
+
+ /* Oops */
+ if (err != noErr)
+ {
+ mac_warning("Unable to get process information");
+
+ /* Quit without writing anything */
+ ExitToShell();
+ }
+
+ /* Extract and save the Vol and Dir */
+ app_vol = cwdSpec.vRefNum;
+ app_dir = cwdSpec.parID;
+}
+
+#endif /* MACH_O_CARBON */
+
+
+
+
+/*
+ * Using Core Foundation's Preferences services -- pelpel
+ *
+ * Requires OS 8.6 or greater with CarbonLib 1.1 or greater. Or OS X,
+ * of course.
+ *
+ * Without this, we can support older versions of OS 8 as well
+ * (with CarbonLib 1.0.4).
+ *
+ * Frequent allocation/deallocation of small chunks of data is
+ * far from my liking, but since this is only called at the
+ * beginning and the end of a session, I hope this hardly matters.
+ */
+
+
+/*
+ * Store "value" as the value for preferences item name
+ * pointed by key
+ */
+static void save_pref_short(const char *key, short value)
+{
+ CFStringRef cf_key;
+ CFNumberRef cf_value;
+
+ /* allocate and initialise the key */
+ cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);
+
+ /* allocate and initialise the value */
+ cf_value = CFNumberCreate(NULL, kCFNumberShortType, &value);
+
+ if ((cf_key != NULL) && (cf_value != NULL))
+ {
+ /* Store the key-value pair in the applications preferences */
+ CFPreferencesSetAppValue(
+ cf_key,
+ cf_value,
+ kCFPreferencesCurrentApplication);
+ }
+
+ /*
+ * Free CF data - the reverse order is a vain attempt to
+ * minimise memory fragmentation.
+ */
+ if (cf_value) CFRelease(cf_value);
+ if (cf_key) CFRelease(cf_key);
+}
+
+
+/*
+ * Load preference value for key, returns TRUE if it succeeds with
+ * vptr updated appropriately, FALSE otherwise.
+ */
+static bool query_load_pref_short(const char *key, short *vptr)
+{
+ CFStringRef cf_key;
+ CFNumberRef cf_value;
+
+ /* allocate and initialise the key */
+ cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);
+
+ /* Oops */
+ if (cf_key == NULL) return (FALSE);
+
+ /* Retrieve value for the key */
+ cf_value = CFPreferencesCopyAppValue(
+ cf_key,
+ kCFPreferencesCurrentApplication);
+
+ /* Value not found */
+ if (cf_value == NULL)
+ {
+ CFRelease(cf_key);
+ return (FALSE);
+ }
+
+ /* Convert the value to short */
+ CFNumberGetValue(
+ cf_value,
+ kCFNumberShortType,
+ vptr);
+
+ /* Free CF data */
+ CFRelease(cf_value);
+ CFRelease(cf_key);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Update short data pointed by vptr only if preferences
+ * value for key is located.
+ */
+static void load_pref_short(const char *key, short *vptr)
+{
+ short tmp;
+
+ if (query_load_pref_short(key, &tmp)) *vptr = tmp;
+ return;
+}
+
+
+/*
+ * Save preferences to preferences file for current host+current user+
+ * current application.
+ */
+static void cf_save_prefs()
+{
+ int i;
+
+ /* Version stamp */
+ save_pref_short("version.major", PREF_VER_MAJOR);
+ save_pref_short("version.minor", PREF_VER_MINOR);
+ save_pref_short("version.patch", PREF_VER_PATCH);
+ save_pref_short("version.extra", PREF_VER_EXTRA);
+
+ /* Gfx settings */
+ save_pref_short("arg.arg_sound", arg_sound);
+ save_pref_short("arg.graf_mode", graf_mode);
+#ifdef USE_DOUBLE_TILES
+ save_pref_short("arg.big_tile", use_bigtile);
+#endif /* USE_DOUBLE_TILES */
+
+ /* Windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data *td = &data[i];
+
+ save_pref_short(format("term%d.font_mono", i), td->font_mono);
+ save_pref_short(format("term%d.font_o_x", i), td->font_o_x);
+ save_pref_short(format("term%d.font_o_y", i), td->font_o_y);
+ save_pref_short(format("term%d.font_wid", i), td->font_wid);
+ save_pref_short(format("term%d.font_hgt", i), td->font_hgt);
+ save_pref_short(format("term%d.tile_o_x", i), td->tile_o_x);
+ save_pref_short(format("term%d.tile_o_y", i), td->tile_o_y);
+ save_pref_short(format("term%d.right", i), td->r.right);
+ save_pref_short(format("term%d.bottom", i), td->r.bottom);
+ save_pref_short(format("term%d.ow1", i), td->size_ow1);
+ save_pref_short(format("term%d.oh1", i), td->size_oh1);
+ save_pref_short(format("term%d.ow2", i), td->size_ow2);
+ save_pref_short(format("term%d.oh2", i), td->size_oh2);
+
+ save_pref_short(format("term%d.mapped", i), td->mapped);
+
+ save_pref_short(format("term%d.font_id", i), td->font_id);
+ save_pref_short(format("term%d.font_size", i), td->font_size);
+ save_pref_short(format("term%d.font_face", i), td->font_face);
+
+ save_pref_short(format("term%d.tile_wid", i), td->tile_wid);
+ save_pref_short(format("term%d.tile_hgt", i), td->tile_hgt);
+
+ save_pref_short(format("term%d.cols", i), td->cols);
+ save_pref_short(format("term%d.rows", i), td->rows);
+ save_pref_short(format("term%d.left", i), td->r.left);
+ save_pref_short(format("term%d.top", i), td->r.top);
+ }
+
+ /*
+ * Make sure preferences are persistent
+ */
+ CFPreferencesAppSynchronize(
+ kCFPreferencesCurrentApplication);
+}
+
+
+/*
+ * Load preferences from preferences file for current host+current user+
+ * current application.
+ */
+static void cf_load_prefs()
+{
+ bool ok;
+ short pref_major, pref_minor, pref_patch, pref_extra;
+ int i;
+
+ /* Assume nothing is wrong, yet */
+ ok = TRUE;
+
+ /* Load version information */
+ ok &= query_load_pref_short("version.major", &pref_major);
+ ok &= query_load_pref_short("version.minor", &pref_minor);
+ ok &= query_load_pref_short("version.patch", &pref_patch);
+ ok &= query_load_pref_short("version.extra", &pref_extra);
+
+ /* Any of the above failed */
+ if (!ok)
+ {
+ /* This may be the first run */
+ mac_warning("Preferences are not found.");
+
+ /* Ignore the rest */
+ return;
+ }
+
+#if 0
+
+ /* Check version */
+ if ((pref_major != PREF_VER_MAJOR) ||
+ (pref_minor != PREF_VER_MINOR) ||
+ (pref_patch != PREF_VER_PATCH) ||
+ (pref_extra != PREF_VER_EXTRA))
+ {
+ /* Message */
+ mac_warning(
+ format("Ignoring %d.%d.%d.%d preferences.",
+ pref_major, pref_minor, pref_patch, pref_extra));
+
+ /* Ignore */
+ return;
+ }
+
+#endif
+
+ /* Gfx settings */
+ {
+ short pref_tmp;
+
+ /* sound */
+ if (query_load_pref_short("arg.arg_sound", &pref_tmp))
+ arg_sound = pref_tmp;
+
+ /* graphics */
+ if (query_load_pref_short("arg.graf_mode", &pref_tmp))
+ graf_mode_req = pref_tmp;
+
+#ifdef USE_DOUBLE_TILES
+
+ /* double-width tiles */
+ if (query_load_pref_short("arg.big_tile", &pref_tmp))
+ {
+ use_bigtile = pref_tmp;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data *td = &data[i];
+
+ load_pref_short(format("term%d.mapped", i), &td->mapped);
+
+ load_pref_short(format("term%d.font_id", i), &td->font_id);
+ load_pref_short(format("term%d.font_size", i), &td->font_size);
+ load_pref_short(format("term%d.font_face", i), &td->font_face);
+
+ load_pref_short(format("term%d.tile_wid", i), &td->tile_wid);
+ load_pref_short(format("term%d.tile_hgt", i), &td->tile_hgt);
+
+ load_pref_short(format("term%d.cols", i), &td->cols);
+ load_pref_short(format("term%d.rows", i), &td->rows);
+ load_pref_short(format("term%d.left", i), &td->r.left);
+ load_pref_short(format("term%d.top", i), &td->r.top);
+
+ load_pref_short(format("term%d.font_mono", i), &td->font_mono);
+ load_pref_short(format("term%d.font_o_x", i), &td->font_o_x);
+ load_pref_short(format("term%d.font_o_y", i), &td->font_o_y);
+ load_pref_short(format("term%d.font_wid", i), &td->font_wid);
+ load_pref_short(format("term%d.font_hgt", i), &td->font_hgt);
+ load_pref_short(format("term%d.tile_o_x", i), &td->tile_o_x);
+ load_pref_short(format("term%d.tile_o_y", i), &td->tile_o_y);
+ load_pref_short(format("term%d.right", i), &td->r.right);
+ load_pref_short(format("term%d.bottom", i), &td->r.bottom);
+ load_pref_short(format("term%d.ow1", i), &td->size_ow1);
+ load_pref_short(format("term%d.oh1", i), &td->size_oh1);
+ load_pref_short(format("term%d.ow2", i), &td->size_ow2);
+ load_pref_short(format("term%d.oh2", i), &td->size_oh2);
+ }
+}
+
+
+
+
+/*
+ * Hack -- default data for a window
+ */
+static void term_data_hack(term_data *td)
+{
+ short fid;
+
+ /* Default to Monaco font */
+ GetFNum("\pmonaco", &fid);
+
+ /* Wipe it */
+ WIPE(td, term_data);
+
+ /* No color */
+ td->last = -1;
+
+ /* Default borders */
+ td->size_ow1 = 2;
+ td->size_ow2 = 2;
+ td->size_oh1 = 2;
+ td->size_oh2 = 2;
+
+ /* Start hidden */
+ td->mapped = FALSE;
+
+ /* Default font */
+ td->font_id = fid;
+
+ /* Default font size - was 12 */
+ td->font_size = 14;
+
+ /* Default font face */
+ td->font_face = 0;
+
+ /* Default size */
+ td->rows = 24;
+ td->cols = 80;
+
+ /* Default position */
+ td->r.left = 10;
+ td->r.top = 40;
+
+ /* Minimal keys */
+ td->keys = 16;
+}
+
+
+/*
+ * Read the preference file, Create the windows.
+ *
+ * We attempt to use "FindFolder()" to track down the preference file.
+ */
+static void init_windows(void)
+{
+ int i, b = 0;
+
+ term_data *td;
+
+
+ /*** Default values ***/
+
+ /* Initialize (backwards) */
+ for (i = MAX_TERM_DATA; i-- > 0; )
+ {
+ int n;
+
+ cptr s;
+
+ /* Obtain */
+ td = &data[i];
+
+ /* Defaults */
+ term_data_hack(td);
+
+ /* Obtain title */
+ s = angband_term_name[i];
+
+ /* Get length */
+ n = strlen(s);
+
+ /* Maximal length */
+ if (n > 15) n = 15;
+
+ /* Copy the title */
+ strncpy((char*)(td->title) + 1, s, n);
+
+ /* Save the length */
+ td->title[0] = n;
+
+ /* Tile the windows */
+ td->r.left += (b * 30);
+ td->r.top += (b * 30);
+
+ /* Tile */
+ b++;
+ }
+
+
+ /*** Load preferences ***/
+
+ cf_load_prefs();
+
+
+ /*** Instantiate ***/
+
+ /* Main window */
+ td = &data[0];
+
+ /* Many keys */
+ td->keys = 1024;
+
+ /* Start visible */
+ td->mapped = TRUE;
+
+ /* Link (backwards, for stacking order) */
+ for (i = MAX_TERM_DATA; i-- > 0; )
+ {
+ term_data_link(i);
+ }
+
+ /* Main window */
+ td = &data[0];
+
+ /* Main window */
+ Term_activate(td->t);
+}
+
+
+/*
+ * Save preferences
+ */
+static void save_pref_file(void)
+{
+ cf_save_prefs();
+}
+
+
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Prepare savefile dialogue and set the variable
+ * savefile accordingly. Returns true if it succeeds, false (or
+ * aborts) otherwise. If all is false, only allow files whose type
+ * is 'SAVE'.
+ * Originally written by Peter Ammon
+ */
+static bool select_savefile(bool all)
+{
+ OSErr err;
+ FSSpec theFolderSpec;
+ FSSpec savedGameSpec;
+ NavDialogOptions dialogOptions;
+ NavReplyRecord reply;
+ /* Used only when 'all' is true */
+ NavTypeList types = {ANGBAND_CREATOR, 1, 1, {'SAVE'}};
+ NavTypeListHandle myTypeList;
+ AEDesc defaultLocation;
+
+#ifdef MACH_O_CARBON
+
+ /* Find the save folder */
+ err = path_to_spec(ANGBAND_DIR_SAVE, &theFolderSpec);
+
+#else
+
+ /* Find :lib:save: folder */
+ err = FSMakeFSSpec(
+ app_vol,
+ app_dir,
+ "\p:lib:save:",
+ &theFolderSpec);
+
+#endif
+
+ /* Oops */
+ if (err != noErr) quit("Unable to find the folder :lib:save:");
+
+ /* Get default Navigator dialog options */
+ err = NavGetDefaultDialogOptions(&dialogOptions);
+
+ /* Clear preview option */
+ dialogOptions.dialogOptionFlags &= ~kNavAllowPreviews;
+
+ /* Disable multiple file selection */
+ dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
+
+ /* Make descriptor for default location */
+ err = AECreateDesc(
+ typeFSS,
+ &theFolderSpec,
+ sizeof(FSSpec),
+ &defaultLocation);
+
+ /* Oops */
+ if (err != noErr) quit("Unable to allocate descriptor");
+
+ /* We are indifferent to signature and file types */
+ if (all)
+ {
+ myTypeList = (NavTypeListHandle)nil;
+ }
+
+ /* Set up type handle */
+ else
+ {
+ err = PtrToHand(&types, (Handle *) & myTypeList, sizeof(NavTypeList));
+
+ /* Oops */
+ if (err != noErr) quit("Error in PtrToHand. Try enlarging heap");
+
+ }
+
+ /* Call NavGetFile() with the types list */
+ err = NavChooseFile(
+ &defaultLocation,
+ &reply,
+ &dialogOptions,
+ nil,
+ nil,
+ nil,
+ myTypeList,
+ nil);
+
+ /* Free type list */
+ DisposeHandle((Handle)myTypeList);
+
+ /* Invalid response -- allow the user to cancel */
+ if (!reply.validRecord) return (FALSE);
+
+ /* Retrieve FSSpec from the reply */
+ if (err == noErr)
+ {
+ AEKeyword theKeyword;
+ DescType actualType;
+ Size actualSize;
+
+ /* Get a pointer to selected file */
+ (void)AEGetNthPtr(
+ &reply.selection,
+ 1,
+ typeFSS,
+ &theKeyword,
+ &actualType,
+ &savedGameSpec,
+ sizeof(FSSpec),
+ &actualSize);
+
+ /* Dispose NavReplyRecord, resources and descriptors */
+ (void)NavDisposeReply(&reply);
+ }
+
+ /* Dispose location info */
+ AEDisposeDesc(&defaultLocation);
+
+#ifdef MACH_O_CARBON
+
+ /* Convert FSSpec to pathname and store it in variable savefile */
+ (void)spec_to_path(&savedGameSpec, savefile, sizeof(savefile));
+
+#else
+
+ /* Convert FSSpec to pathname and store it in variable savefile */
+ refnum_to_name(
+ savefile,
+ savedGameSpec.parID,
+ savedGameSpec.vRefNum,
+ (char *)savedGameSpec.name);
+
+#endif
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Handle menu: "File" + "New"
+ */
+static void do_menu_file_new(void)
+{
+ /* Hack */
+ HiliteMenu(0);
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play a game */
+ play_game(TRUE);
+
+ /* Hack -- quit */
+ quit(NULL);
+}
+
+
+/*
+ * Handle menu: "File" + "Open" / "Import"
+ */
+static void do_menu_file_open(bool all)
+{
+ /* Let the player to choose savefile */
+ if (!select_savefile(all)) return;
+
+ /* Hack */
+ HiliteMenu(0);
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Flush input */
+ flush();
+
+ /* Play a game */
+ play_game(FALSE);
+
+ /* Hack -- quit */
+ quit(NULL);
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * Handle the "open_when_ready" flag
+ */
+static void handle_open_when_ready(void)
+{
+ /* Check the flag XXX XXX XXX make a function for this */
+ if (open_when_ready && initialized && !game_in_progress)
+ {
+ /* Forget */
+ open_when_ready = FALSE;
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Wait for it */
+ pause_line(23);
+
+ /* Flush input */
+ flush();
+
+#ifdef SAVEFILE_SCREEN
+
+ /* User double-clicked savefile; no savefile screen */
+ no_begin_screen = TRUE;
+
+#endif /* SAVEFILE_SCREEN */
+
+ /* Play a game */
+ play_game(FALSE);
+
+ /* Quit */
+ quit(NULL);
+ }
+}
+
+
+
+
+/*
+ * Menus
+ *
+ * The standard menus are:
+ *
+ * Apple (128) = { About, -, ... }
+ * File (129) = { New,Open,Import,Close,Save,-,Score,Quit }
+ * (If SAVEFILE_SCREEN is defined, this becomes)
+ * File (129) = { Close,Save,-,Score,Quit }
+ * Edit (130) = { Cut, Copy, Paste, Clear } (?)
+ * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... }
+ * Size (132) = { ... }
+ * Window (133) = { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice,
+ * Term-4, Term-5, Term-6, Term-7 }
+ * Special (134) = { Sound, Graphics, TileWidth, TileHeight, -,
+ * Fiddle, Wizard }
+ */
+
+/* Apple menu */
+#define MENU_APPLE 128
+#define ITEM_ABOUT 1
+
+/* File menu */
+#define MENU_FILE 129
+#ifndef SAVEFILE_SCREEN
+# define ITEM_NEW 1
+# define ITEM_OPEN 2
+# define ITEM_IMPORT 3
+# define ITEM_CLOSE 4
+# define ITEM_SAVE 5
+# ifdef HAS_SCORE_MENU
+# define ITEM_SCORE 7
+# define ITEM_QUIT 8
+# else
+# define ITEM_QUIT 7
+# endif /* HAS_SCORE_MENU */
+#else /* !SAVEFILE_SCREEN - in-game savefile menu */
+# define ITEM_CLOSE 1
+# define ITEM_SAVE 2
+# ifdef HAS_SCORE_MENU
+# define ITEM_SCORE 4
+# define ITEM_QUIT 5
+# else
+# define ITEM_QUIT 4
+# endif /* HAS_SCORE_MENU */
+#endif /* !SAVEFILE_SCREEN */
+
+/* Edit menu */
+#define MENU_EDIT 130
+#define ITEM_UNDO 1
+#define ITEM_CUT 3
+#define ITEM_COPY 4
+#define ITEM_PASTE 5
+#define ITEM_CLEAR 6
+
+/* Font menu */
+#define MENU_FONT 131
+#define ITEM_BOLD 1
+#define ITEM_WIDE 2
+
+/* Size menu */
+#define MENU_SIZE 132
+
+/* Windows menu */
+#define MENU_WINDOWS 133
+
+/* Special menu */
+#define MENU_SPECIAL 134
+#define ITEM_SOUND 1
+#define ITEM_GRAPH 2
+# define SUBMENU_GRAPH 144
+# define ITEM_NONE 1
+# define ITEM_8X8 2
+# define ITEM_16X16 3
+# define ITEM_32X32 4
+# define ITEM_BIGTILE 6
+#define ITEM_TILEWIDTH 3
+# define SUBMENU_TILEWIDTH 145
+#define ITEM_TILEHEIGHT 4
+# define SUBMENU_TILEHEIGHT 146
+#define ITEM_FIDDLE 6
+#define ITEM_WIZARD 7
+
+
+/*
+ * I HATE UNICODE! We've never wanted it. Some multi-national companies
+ * made it up as their internationalisation "solution". So I won't use
+ * any such API's -- pelpel
+ */
+#define NSIZES 32
+static byte menu_size_values[NSIZES];
+static byte menu_tilewidth_values[NSIZES];
+static byte menu_tileheight_values[NSIZES];
+
+/*
+ * Initialize the menus
+ *
+ * Fixed top level menus are now loaded all at once by GetNewMBar().
+ * Although this simplifies the function a bit, we have to make sure
+ * that resources have all the expected entries defined XXX XXX
+ */
+static void init_menubar(void)
+{
+ int i, n;
+
+ Rect r;
+
+ WindowPtr tmpw;
+
+ MenuRef m;
+
+#ifdef USE_NIB
+
+ /* The new way - loading main menu using Interface Builder services */
+ {
+ IBNibRef nib;
+ OSStatus err;
+
+ /* Create a nib reference to the main nib file */
+ err = CreateNibReference(CFSTR("main"), &nib);
+
+ /* Fatal error - missing Main.nib */
+ if (err != noErr) quit("Cannot find Main.nib in the bundle!");
+
+ /* Unarchive the menu bar and make it ready to use */
+ err = SetMenuBarFromNib(nib, CFSTR("MainMenu"));
+
+ /* Fatal error - couldn't insert menu bar */
+ if (err != noErr) quit("Cannot prepare menu bar!");
+
+ /* Dispose of the nib reference because we don't need it any longer */
+ DisposeNibReference(nib);
+ }
+
+#else /* USE_NIB */
+
+ /* The old way - loading main menu from Resource Manager resource */
+ {
+ Handle mbar;
+
+ /* Load menubar from resources */
+ mbar = GetNewMBar(128);
+
+ /* Whoops! */
+ if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!");
+
+ /* Insert them into the current menu list */
+ SetMenuBar(mbar);
+
+ /* Free handle */
+ DisposeHandle(mbar);
+ }
+
+#endif /* USE_NIB */
+
+
+ /* Apple menu (id 128) - we don't have to do anything */
+
+#ifndef USE_NIB
+
+ /* File menu (id 129) - Aqua provides Quit menu for us */
+ if (is_aqua)
+ {
+ /* Get a handle to the file menu */
+ m = GetMenuHandle(MENU_FILE);
+
+ /* Nuke the quit menu since Aqua does that for us */
+ DeleteMenuItem(m, ITEM_QUIT);
+
+#ifndef HAS_SCORE_MENU
+
+ /* Hack - because the above leaves a separator as the last item */
+ DeleteMenuItem(m, ITEM_QUIT - 1);
+
+#endif /* !HAS_SCORE_MENU */
+ }
+
+#endif /* !USE_NIB */
+
+
+ /* Edit menu (id 130) - we don't have to do anything */
+
+
+ /*
+ * Font menu (id 131) - append names of mono-spaced fonts
+ * followed by all available ones
+ */
+ m = GetMenuHandle(MENU_FONT);
+
+ /* Fake window */
+ r.left = r.right = r.top = r.bottom = 0;
+
+ /* Make the fake window so that we can retrieve font info */
+ (void)CreateNewWindow(
+ kDocumentWindowClass,
+ kWindowNoAttributes,
+ &r,
+ &tmpw);
+
+ /* Activate the "fake" window */
+ SetPort(GetWindowPort(tmpw));
+
+ /* Default mode */
+ TextMode(0);
+
+ /* Default size */
+ TextSize(12);
+
+ /* Add the fonts to the menu */
+ AppendResMenu(m, 'FONT');
+
+ /* Size of menu */
+ n = CountMenuItems(m);
+
+ /* Scan the menu */
+ for (i = n; i >= 4; i--)
+ {
+ Str255 tmpName;
+ short fontNum;
+
+ /* Acquire the font name */
+ GetMenuItemText(m, i, tmpName);
+
+ /* Acquire the font index */
+ GetFNum(tmpName, &fontNum);
+
+ /* Apply the font index */
+ TextFont(fontNum);
+
+ /* Remove non-mono-spaced fonts */
+ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0))
+ {
+ /* Delete the menu item */
+ DeleteMenuItem(m, i);
+ }
+ }
+
+ /* Destroy the fake window */
+ DisposeWindow(tmpw);
+
+ /* Add a separator */
+ AppendMenu(m, "\p-");
+
+ /* Add the fonts to the menu */
+ AppendResMenu(m, 'FONT');
+
+
+#ifndef USE_NIB
+
+ /* Size menu (id 132) */
+ m = GetMenuHandle(MENU_SIZE);
+
+ /* Add some sizes (stagger choices) */
+ for (i = 8, n = 1; i <= 32; i += ((i / 16) + 1), n++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Add the item */
+ AppendMenu(m, buf);
+
+ /* Remember its value, for we can't be sure it's in ASCII */
+ menu_size_values[n] = i;
+ }
+
+#endif /* !USE_NIB */
+
+
+ /* Windows menu (id 133) */
+ m = GetMenuHandle(MENU_WINDOWS);
+
+ /* Default choices */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Str15 buf;
+
+ /* Describe the item */
+ strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Add the item */
+ AppendMenu(m, buf);
+
+ /* Command-Key shortcuts */
+ if (i < 8) SetItemCmd(m, i + 1, I2D(i));
+ }
+
+
+#ifndef USE_NIB
+
+ /* Special menu (id 134) */
+ m = GetMenuHandle(MENU_SPECIAL);
+
+ /* Insert Graphics submenu (id 144) */
+ {
+ MenuHandle submenu;
+
+ /* Get the submenu */
+ submenu = GetMenu(SUBMENU_GRAPH);
+
+ /* Insert it */
+ SetMenuItemHierarchicalMenu(m, ITEM_GRAPH, submenu);
+ }
+
+ /* Insert TileWidth submenu (id 145) */
+ {
+ MenuHandle submenu;
+
+ /* Get the submenu */
+ submenu = GetMenu(SUBMENU_TILEWIDTH);
+
+ /* Add some sizes */
+ for (i = 4, n = 1; i <= 32; i++, n++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Append item */
+ AppendMenu(submenu, buf);
+
+ /* Remember its value, for we can't be sure it's in ASCII */
+ menu_tilewidth_values[n] = i;
+ }
+
+ /* Insert it */
+ SetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, submenu);
+ }
+
+ /* Insert TileHeight submenu (id 146) */
+ {
+ MenuHandle submenu;
+
+ /* Get the submenu */
+ submenu = GetMenu(SUBMENU_TILEHEIGHT);
+
+
+ /* Add some sizes */
+ for (i = 4, n = 1; i <= 32; i++, n++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Append item */
+ AppendMenu(submenu, buf);
+
+ /* Remember its value, for we can't be sure it's in ASCII */
+ menu_tileheight_values[n] = i;
+ }
+
+ /* Insert it */
+ SetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, submenu);
+ }
+
+#endif /* !USE_NIB */
+
+ /* Update the menu bar */
+ DrawMenuBar();
+}
+
+
+/*
+ * Prepare the menus
+ *
+ * It is very important that the player not be allowed to "save" the game
+ * unless the "inkey_flag" variable is set, indicating that the game is
+ * waiting for a new command. XXX XXX XXX
+ */
+
+static void setup_menus(void)
+{
+ int i, n;
+
+ short value;
+
+ Str255 s;
+
+ MenuHandle m;
+
+ term_data *td = NULL;
+
+
+ /* Relevant "term_data" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Unused */
+ if (!data[i].t) continue;
+
+ /* Notice the matching window */
+ if (data[i].w == FrontWindow()) td = &data[i];
+ }
+
+
+ /* File menu */
+ m = GetMenuHandle(MENU_FILE);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Enable "new"/"open..."/"import..." */
+ if (initialized && !game_in_progress)
+ {
+ EnableMenuItem(m, ITEM_NEW);
+ EnableMenuItem(m, ITEM_OPEN);
+ EnableMenuItem(m, ITEM_IMPORT);
+ }
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Enable "close" */
+ if (initialized)
+ {
+ EnableMenuItem(m, ITEM_CLOSE);
+ }
+
+ /* Enable "save" */
+ if (initialized && character_generated && inkey_flag)
+ {
+ EnableMenuItem(m, ITEM_SAVE);
+ }
+
+#ifdef HAS_SCORE_MENU
+
+ /* Enable "score" */
+ if (initialized && character_generated && !character_icky)
+ {
+ EnableMenuItem(m, ITEM_SCORE);
+ }
+
+#endif /* HAS_SCORE_MENU */
+
+ /* Enable "quit" */
+ if (!is_aqua)
+ {
+ if (!initialized || !character_generated || inkey_flag)
+ {
+ EnableMenuItem(m, ITEM_QUIT);
+ }
+ }
+
+
+ /* Edit menu */
+ m = GetMenuHandle(MENU_EDIT);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Enable "edit" options if "needed" */
+ if (!td)
+ {
+ EnableMenuItem(m, ITEM_UNDO);
+ EnableMenuItem(m, ITEM_CUT);
+ EnableMenuItem(m, ITEM_COPY);
+ EnableMenuItem(m, ITEM_PASTE);
+ EnableMenuItem(m, ITEM_CLEAR);
+ }
+
+
+ /* Font menu */
+ m = GetMenuHandle(MENU_FONT);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Hack -- look cute XXX XXX */
+ /* SetItemStyle(m, ITEM_BOLD, bold); */
+
+ /* Hack -- look cute XXX XXX */
+ /* SetItemStyle(m, ITEM_WIDE, extend); */
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Enable "bold" */
+ EnableMenuItem(m, ITEM_BOLD);
+
+ /* Enable "extend" */
+ EnableMenuItem(m, ITEM_WIDE);
+
+ /* Check the appropriate "bold-ness" */
+ if (td->font_face & bold) CheckMenuItem(m, ITEM_BOLD, TRUE);
+
+ /* Check the appropriate "wide-ness" */
+ if (td->font_face & extend) CheckMenuItem(m, ITEM_WIDE, TRUE);
+
+ /* Analyze fonts */
+ for (i = 4; i <= n; i++)
+ {
+ /* Enable it */
+ EnableMenuItem(m, i);
+
+ /* Analyze font */
+ GetMenuItemText(m, i, s);
+ GetFNum(s, &value);
+
+ /* Check active font */
+ if (td->font_id == value) CheckMenuItem(m, i, TRUE);
+ }
+ }
+
+
+ /* Size menu */
+ m = GetMenuHandle(MENU_SIZE);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ value = menu_size_values[i];
+
+ /* Enable the "real" sizes */
+ if (RealFont(td->font_id, value)) EnableMenuItem(m, i);
+
+ /* Check the current size */
+ if (td->font_size == value) CheckMenuItem(m, i, TRUE);
+ }
+ }
+
+
+ /* Windows menu */
+ m = GetMenuHandle(MENU_WINDOWS);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Check active windows */
+ for (i = 1; i <= n; i++)
+ {
+ /* Check if needed */
+ CheckMenuItem(m, i, data[i - 1].mapped);
+ }
+
+
+ /* Special menu */
+ m = GetMenuHandle(MENU_SPECIAL);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+
+ /* Item "arg_sound" */
+ EnableMenuItem(m, ITEM_SOUND);
+ CheckMenuItem(m, ITEM_SOUND, arg_sound);
+
+ /* Item "Graphics" */
+ EnableMenuItem(m, ITEM_GRAPH);
+ {
+ MenuRef submenu;
+
+ /* Graphics submenu */
+ (void)GetMenuItemHierarchicalMenu(m, ITEM_GRAPH, &submenu);
+
+ /* Get menu size */
+ n = CountMenuItems(submenu);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(submenu, i);
+ CheckMenuItem(submenu, i, FALSE);
+ }
+
+ /* Item "None" */
+ EnableMenuItem(submenu, ITEM_NONE);
+ CheckMenuItem(submenu, ITEM_NONE, (graf_mode == GRAF_MODE_NONE));
+
+ /* Item "8x8" */
+ EnableMenuItem(submenu, ITEM_8X8);
+ CheckMenuItem(submenu, ITEM_8X8, (graf_mode == GRAF_MODE_8X8));
+
+ /* Item "16x16" */
+ EnableMenuItem(submenu, ITEM_16X16);
+ CheckMenuItem(submenu, ITEM_16X16, (graf_mode == GRAF_MODE_16X16));
+
+ /* Item "32x32" */
+ /*EnableMenuItem(submenu, ITEM_32X32);
+ CheckMenuItem(submenu, ITEM_32X32, (graf_mode == GRAF_MODE_32X32));*/
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Item "Big tiles" */
+ if (inkey_flag) EnableMenuItem(submenu, ITEM_BIGTILE);
+ CheckMenuItem(submenu, ITEM_BIGTILE, use_bigtile);
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Item "TileWidth" */
+ EnableMenuItem(m, ITEM_TILEWIDTH);
+ {
+ MenuRef submenu;
+
+ /* TileWidth submenu */
+ (void)GetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, &submenu);
+
+ /* Get menu size */
+ n = CountMenuItems(submenu);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(submenu, i);
+ CheckMenuItem(submenu, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ value = menu_tilewidth_values[i];
+
+ /* Enable */
+ if (value >= td->font_wid) EnableMenuItem(submenu, i);
+
+ /* Check the current size */
+ if (td->tile_wid == value) CheckMenuItem(submenu, i, TRUE);
+ }
+ }
+ }
+
+ /* Item "TileHeight" */
+ EnableMenuItem(m, ITEM_TILEHEIGHT);
+ {
+ MenuRef submenu;
+
+ /* TileWidth submenu */
+ (void)GetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, &submenu);
+
+ /* Get menu size */
+ n = CountMenuItems(submenu);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(submenu, i);
+ CheckMenuItem(submenu, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ value = menu_tileheight_values[i];
+
+ /* Enable */
+ if (value >= td->font_hgt) EnableMenuItem(submenu, i);
+
+ /* Check the current size */
+ if (td->tile_hgt == value) CheckMenuItem(submenu, i, TRUE);
+ }
+ }
+ }
+
+ /* Item "arg_fiddle" */
+ EnableMenuItem(m, ITEM_FIDDLE);
+ CheckMenuItem(m, ITEM_FIDDLE, arg_fiddle);
+
+ /* Item "arg_wizard" */
+ EnableMenuItem(m, ITEM_WIZARD);
+ CheckMenuItem(m, ITEM_WIZARD, arg_wizard);
+
+
+ /* TileHeight menu */
+ m = GetMenuHandle(SUBMENU_TILEHEIGHT);
+
+ /* Get menu size */
+ n = CountMenuItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableMenuItem(m, i);
+ CheckMenuItem(m, i, FALSE);
+ }
+}
+
+
+/*
+ * Process a menu selection (see above)
+ *
+ * Hack -- assume that invalid menu selections are disabled above,
+ * which I have been informed may not be reliable. XXX XXX XXX
+ */
+static void menu(long mc)
+{
+ int i;
+
+ int menuid, selection;
+
+ static unsigned char s[1000];
+
+ short fid;
+
+ term_data *td = NULL;
+
+ WindowPtr old_win;
+
+
+ /* Analyze the menu command */
+ menuid = HiWord(mc);
+ selection = LoWord(mc);
+
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == FrontWindow()) td = &data[i];
+ }
+
+
+ /* Branch on the menu */
+ switch (menuid)
+ {
+ /* Apple Menu */
+ case MENU_APPLE:
+ {
+ /* About Angband... */
+ if (selection == ITEM_ABOUT)
+ {
+ DialogPtr dialog;
+ short item_hit;
+
+ /* Get the about dialogue */
+ dialog = GetNewDialog(128, 0, (WindowPtr) - 1);
+
+ /* Move it to the middle of the screen */
+ RepositionWindow(
+ GetDialogWindow(dialog),
+ NULL,
+ kWindowCenterOnMainScreen);
+
+ /* Show the dialog */
+ TransitionWindow(GetDialogWindow(dialog),
+ kWindowZoomTransitionEffect,
+ kWindowShowTransitionAction,
+ NULL);
+
+ /* Wait for user to click on it */
+ ModalDialog(0, &item_hit);
+
+ /* Free the dialogue */
+ DisposeDialog(dialog);
+ break;
+ }
+
+ break;
+ }
+
+ /* File Menu */
+ case MENU_FILE:
+ {
+ switch (selection)
+ {
+#ifndef SAVEFILE_SCREEN
+
+ /* New */
+ case ITEM_NEW:
+ {
+ do_menu_file_new();
+ break;
+ }
+
+ /* Open... */
+ case ITEM_OPEN:
+ {
+ do_menu_file_open(FALSE);
+ break;
+ }
+
+ /* Import... */
+ case ITEM_IMPORT:
+ {
+ do_menu_file_open(TRUE);
+ break;
+ }
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Close */
+ case ITEM_CLOSE:
+ {
+ /* No window */
+ if (!td) break;
+
+ /* Not Mapped */
+ td->mapped = FALSE;
+
+ /* Not Mapped */
+ td->t->mapped_flag = FALSE;
+
+ /* Hide the window */
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect,
+ kWindowHideTransitionAction,
+ NULL);
+
+ break;
+ }
+
+ /* Save */
+ case ITEM_SAVE:
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Hack -- Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+
+ break;
+ }
+
+#ifdef HAS_SCORE_MENU
+
+ /* Show score */
+ case ITEM_SCORE:
+ {
+ char buf[1024];
+
+ /* Paranoia */
+ if (!initialized || character_icky ||
+ !game_in_progress || !character_generated)
+ {
+ /* Can't happen but just in case */
+ plog("You may not do that right now.");
+
+ break;
+ }
+
+ /* Build the pathname of the score file */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX,
+ "scores.raw");
+
+ /* Hack - open the score file for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Paranoia - No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file is not available.");
+
+ break;
+ }
+
+ /* Mega-Hack - prevent various functions XXX XXX XXX */
+ initialized = FALSE;
+
+ /* Save screen */
+ screen_save();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prepare scores */
+ if (game_in_progress && character_generated)
+ {
+ predict_score();
+ }
+
+#if 0 /* I don't like this - pelpel */
+
+ /* Mega-Hack - No current player XXX XXX XXX XXX */
+ else
+ {
+ display_scores_aux(0, MAX_HISCORES, -1, NULL);
+ }
+
+#endif
+
+ /* Close the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the fd */
+ highscore_fd = -1;
+
+ /* Restore screen */
+ screen_load();
+
+ /* Hack - Flush it */
+ Term_fresh();
+
+ /* Mega-Hack - We are ready again */
+ initialized = TRUE;
+
+ /* Done */
+ break;
+ }
+
+#endif /* HAS_SCORE_MENU */
+
+ /* Quit (with save) */
+ case ITEM_QUIT:
+ {
+ /* Save the game (if necessary) */
+ if (game_in_progress && character_generated)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+ }
+
+ /* Quit */
+ quit(NULL);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Edit menu */
+ case MENU_EDIT:
+ {
+ /* Unused */
+ break;
+ }
+
+ /* Font menu */
+ case MENU_FONT:
+ {
+ /* Require a window */
+ if (!td) break;
+
+ /* Memorize old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Toggle the "bold" setting */
+ if (selection == ITEM_BOLD)
+ {
+ /* Toggle the setting */
+ if (td->font_face & bold)
+ {
+ td->font_face &= ~bold;
+ }
+ else
+ {
+ td->font_face |= bold;
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ break;
+ }
+
+ /* Toggle the "wide" setting */
+ if (selection == ITEM_WIDE)
+ {
+ /* Toggle the setting */
+ if (td->font_face & extend)
+ {
+ td->font_face &= ~extend;
+ }
+ else
+ {
+ td->font_face |= extend;
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ break;
+ }
+
+ /* Get a new font name */
+ GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s);
+ GetFNum(s, &fid);
+
+ /* Save the new font id */
+ td->font_id = fid;
+
+ /* Current size is bad for new font */
+ if (!RealFont(td->font_id, td->font_size))
+ {
+ /* Find similar size */
+ for (i = 1; i <= 32; i++)
+ {
+ /* Adjust smaller */
+ if (td->font_size - i >= 8)
+ {
+ if (RealFont(td->font_id, td->font_size - i))
+ {
+ td->font_size -= i;
+ break;
+ }
+ }
+
+ /* Adjust larger */
+ if (td->font_size + i <= 128)
+ {
+ if (RealFont(td->font_id, td->font_size + i))
+ {
+ td->font_size += i;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore the window */
+ activate(old_win);
+
+ break;
+ }
+
+ /* Size menu */
+ case MENU_SIZE:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ td->font_size = menu_size_values[selection];
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ /* Window menu */
+ case MENU_WINDOWS:
+ {
+ /* Parse */
+ i = selection - 1;
+
+ /* Check legality of choice */
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ /* Obtain the window */
+ td = &data[i];
+
+ /* Mapped */
+ td->mapped = TRUE;
+
+ /* Link */
+ term_data_link(i);
+
+ /* Mapped (?) */
+ td->t->mapped_flag = TRUE;
+
+ /* Show the window */
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect,
+ kWindowShowTransitionAction,
+ NULL);
+
+ /* Bring to the front */
+ SelectWindow(td->w);
+
+ break;
+ }
+
+ /* Special menu */
+ case MENU_SPECIAL:
+ {
+ switch (selection)
+ {
+ case ITEM_SOUND:
+ {
+ /* Toggle arg_sound */
+ arg_sound = !arg_sound;
+
+ /* React to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ break;
+ }
+
+ case ITEM_FIDDLE:
+ {
+ arg_fiddle = !arg_fiddle;
+
+ break;
+ }
+
+ case ITEM_WIZARD:
+ {
+ arg_wizard = !arg_wizard;
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* Graphics submenu */
+ case SUBMENU_GRAPH:
+ {
+ switch (selection)
+ {
+ case ITEM_NONE:
+ {
+ graf_mode_req = GRAF_MODE_NONE;
+
+ break;
+ }
+
+ case ITEM_8X8:
+ {
+ graf_mode_req = GRAF_MODE_8X8;
+
+ break;
+ }
+
+ case ITEM_16X16:
+ {
+ graf_mode_req = GRAF_MODE_16X16;
+
+ break;
+ }
+
+ case ITEM_32X32:
+ {
+ graf_mode_req = GRAF_MODE_32X32;
+
+ break;
+ }
+
+#ifdef USE_DOUBLE_TILES
+
+ case ITEM_BIGTILE:
+ {
+ term *old = Term;
+ term_data *td = &data[0];
+
+ /* Toggle "use_bigtile" */
+ use_bigtile = !use_bigtile;
+ arg_bigtile = use_bigtile;
+
+ /* Activate */
+ Term_activate(td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Activate old */
+ Term_activate(old);
+
+ break;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ /* TileWidth menu */
+ case SUBMENU_TILEWIDTH:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyse value */
+ td->tile_wid = menu_tilewidth_values[selection];
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ /* TileHeight menu */
+ case SUBMENU_TILEHEIGHT:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyse value */
+ td->tile_hgt = menu_tileheight_values[selection];
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+ }
+
+
+ /* Clean the menu */
+ HiliteMenu(0);
+}
+
+
+/*
+ * Check for extra required parameters -- From "Maarten Hazewinkel"
+ */
+static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent)
+{
+ OSErr aeError;
+ DescType returnedType;
+ Size actualSize;
+
+ aeError = AEGetAttributePtr(
+ theAppleEvent, keyMissedKeywordAttr, typeWildCard,
+ &returnedType, NULL, 0, &actualSize);
+
+ if (aeError == errAEDescNotFound) return (noErr);
+
+ if (aeError == noErr) return (errAEParamMissed);
+
+ return (aeError);
+}
+
+
+/*
+ * Apple Event Handler -- Open Application
+ */
+static OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply,
+ SInt32 handlerRefCon)
+{
+ return (CheckRequiredAEParams(theAppleEvent));
+}
+
+
+/*
+ * Apple Event Handler -- Quit Application
+ */
+static OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply,
+ SInt32 handlerRefCon)
+{
+ /* Quit later */
+ quit_when_ready = TRUE;
+
+ /* Check arguments */
+ return (CheckRequiredAEParams(theAppleEvent));
+}
+
+
+/*
+ * Apple Event Handler -- Print Documents
+ */
+static OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply,
+ SInt32 handlerRefCon)
+{
+ return (errAEEventNotHandled);
+}
+
+
+/*
+ * Apple Event Handler by Steve Linberg (slinberg@crocker.com).
+ *
+ * The old method of opening savefiles from the finder does not work
+ * on the Power Macintosh, because CountAppFiles and GetAppFiles,
+ * used to return information about the selected document files when
+ * an application is launched, are part of the Segment Loader, which
+ * is not present in the RISC OS due to the new memory architecture.
+ *
+ * The "correct" way to do this is with AppleEvents. The following
+ * code is modeled on the "Getting Files Selected from the Finder"
+ * snippet from Think Reference 2.0. (The prior sentence could read
+ * "shamelessly swiped & hacked")
+ */
+static OSErr AEH_Open(const AppleEvent *theAppleEvent, AppleEvent* reply,
+ SInt32 handlerRefCon)
+{
+ FSSpec myFSS;
+ AEDescList docList;
+ OSErr err;
+ Size actualSize;
+ AEKeyword keywd;
+ DescType returnedType;
+ char msg[128];
+ FInfo myFileInfo;
+
+ /* Put the direct parameter (a descriptor list) into a docList */
+ err = AEGetParamDesc(
+ theAppleEvent, keyDirectObject, typeAEList, &docList);
+ if (err) return err;
+
+ /*
+ * We ignore the validity check, because we trust the FInder, and we only
+ * allow one savefile to be opened, so we ignore the depth of the list.
+ */
+ err = AEGetNthPtr(
+ &docList, 1L, typeFSS, &keywd, &returnedType,
+ (Ptr) & myFSS, sizeof(myFSS), &actualSize);
+ if (err) return err;
+
+ /* Only needed to check savefile type below */
+ err = FSpGetFInfo(&myFSS, &myFileInfo);
+ if (err)
+ {
+ strnfmt(msg, 128, "Argh! FSpGetFInfo failed with code %d", err);
+ mac_warning(msg);
+ return err;
+ }
+
+ /* Ignore non 'SAVE' files */
+ if (myFileInfo.fdType != 'SAVE') return noErr;
+
+#ifdef MACH_O_CARBON
+
+ /* Extract a file name */
+ (void)spec_to_path(&myFSS, savefile, sizeof(savefile));
+
+#else
+
+ /* XXX XXX XXX Extract a file name */
+ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile);
+ pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name);
+
+ /* Convert the string */
+ ptocstr((StringPtr)savefile);
+
+#endif /* MACH_O_CARBON */
+
+ /* Delay actual open */
+ open_when_ready = TRUE;
+
+ /* Dispose */
+ err = AEDisposeDesc(&docList);
+
+ /* Success */
+ return noErr;
+}
+
+
+/*
+ * Handle quit_when_ready, by Peter Ammon,
+ * slightly modified to check inkey_flag.
+ */
+static void quit_calmly(void)
+{
+ /* Quit immediately if game's not started */
+ if (!game_in_progress || !character_generated) quit(NULL);
+
+ /* Save the game and Quit (if it's safe) */
+ if (inkey_flag)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+
+ /* Quit */
+ quit(NULL);
+ }
+
+ /* Wait until inkey_flag is set */
+}
+
+
+/*
+ * Macintosh modifiers (event.modifier & ccc):
+ * cmdKey, optionKey, shiftKey, alphaLock, controlKey
+ *
+ *
+ * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra):
+ *
+ * Return:36
+ * Delete:51
+ *
+ * Period:65
+ * Star:67
+ * Plus:69
+ * Clear:71
+ * Slash:75
+ * Enter:76
+ * Minus:78
+ * Equal:81
+ * 0-7:82-89
+ * 8-9:91-92
+ *
+ * backslash/vertical bar (Japanese keyboard):93
+ *
+ * F5: 96
+ * F6: 97
+ * F7: 98
+ * F3:99
+ * F8:100
+ * F10:101
+ * F11:103
+ * F13:105
+ * F14:107
+ * F9:109
+ * F12:111
+ * F15:113
+ * Help:114
+ * Home:115
+ * PgUp:116
+ * Del:117
+ * F4: 118
+ * End:119
+ * F2:120
+ * PgDn:121
+ * F1:122
+ * Lt:123
+ * Rt:124
+ * Dn:125
+ * Up:126
+ */
+
+
+/*
+ * Check for Events, return TRUE if we process any
+ *
+ * Now it really waits for events if wait set to true, to prevent
+ * undesirable monopoly of CPU. The side-effect is that you cannot do
+ * while (CheckEvents(TRUE)); without discretion.
+ */
+static bool CheckEvents(bool wait)
+{
+ EventRecord event;
+
+ WindowPtr w;
+
+ Rect r;
+
+ UInt32 sleep_ticks;
+
+ int ch, ck;
+
+ int mc, ms, mo, mx;
+
+ int i;
+
+ term_data *td = NULL;
+
+
+ /*
+ * With the wait mode blocking for available event / timeout,
+ * the non-wait mode should actually call WaitNextEvent,
+ * because of those event draining loops. Or we had to
+ * implement yet another mode.
+ */
+
+ /* Handles the quit_when_ready flag */
+ if (quit_when_ready) quit_calmly();
+
+ /* Blocking call to WaitNextEvent - should use MAX_INT XXX XXX */
+ if (wait) sleep_ticks = 0x7FFFFFFFL;
+
+ /* Non-blocking */
+ else sleep_ticks = 0L;
+
+ /* Get an event (or null) */
+ WaitNextEvent(everyEvent, &event, sleep_ticks, nil);
+
+ /* Hack -- Nothing is ready yet */
+ if (event.what == nullEvent) return (FALSE);
+
+
+ /* Analyze the event */
+ switch (event.what)
+ {
+
+#if 0
+
+ case activateEvt:
+ {
+ w = (WindowPtr)event.message;
+
+ activate(w);
+
+ break;
+ }
+
+#endif
+
+ case updateEvt:
+ {
+ /* Extract the window */
+ w = (WindowPtr)event.message;
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == w) td = &data[i];
+ }
+
+ /* Hack XXX XXX XXX */
+ BeginUpdate(w);
+ EndUpdate(w);
+
+ /* Redraw the window */
+ if (td) term_data_redraw(td);
+
+ break;
+ }
+
+ case keyDown:
+ case autoKey:
+ {
+ /* Extract some modifiers */
+ mc = (event.modifiers & controlKey) ? TRUE : FALSE;
+ ms = (event.modifiers & shiftKey) ? TRUE : FALSE;
+ mo = (event.modifiers & optionKey) ? TRUE : FALSE;
+ mx = (event.modifiers & cmdKey) ? TRUE : FALSE;
+
+ /* Keypress: (only "valid" if ck < 96) */
+ ch = (event.message & charCodeMask) & 255;
+
+ /* Keycode: see table above */
+ ck = ((event.message & keyCodeMask) >> 8) & 255;
+
+ /* Command + "normal key" -> menu action */
+ if (mx && (ck < 64))
+ {
+#ifdef MENU_SHORTCUTS
+ /* Hack -- Prepare the menus */
+ setup_menus();
+
+ /* Run the Menu-Handler */
+ menu(MenuKey(ch));
+
+ /* Turn off the menus */
+ HiliteMenu(0);
+
+ /* Done */
+ break;
+#else
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send some modifier keys */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (mo) Term_keypress('O');
+ if (mx) Term_keypress('X');
+
+ /* Enqueue the keypress */
+ Term_keypress(ch);
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+#endif
+ }
+
+ /* Hide the mouse pointer */
+ ObscureCursor();
+
+ /* Normal key -> simple keypress */
+ if ((ck < 64) || (ck == 93))
+ {
+ /* Enqueue the keypress */
+ Term_keypress(ch);
+ }
+
+ /* Keypad keys -> trigger plus simple keypress */
+ else if (!mc && !ms && !mo && !mx && (ck < 96))
+ {
+ /* Hack -- "enter" is confused */
+ if (ck == 76) ch = '\n';
+
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send the "keypad" modifier */
+ Term_keypress('K');
+
+ /* Send the "ascii" keypress */
+ Term_keypress(ch);
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+ }
+
+ /* Bizarre key -> encoded keypress */
+ else if (ck <= 127)
+ {
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send some modifier keys */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (mo) Term_keypress('O');
+ if (mx) Term_keypress('X');
+
+ /* Downshift and encode the keycode */
+ Term_keypress(I2D((ck - 64) / 10));
+ Term_keypress(I2D((ck - 64) % 10));
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+ }
+
+ break;
+ }
+
+ case mouseDown:
+ {
+ int code;
+
+ /* Analyze click location */
+ code = FindWindow(event.where, &w);
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == w) td = &data[i];
+ }
+
+ /* Analyze */
+ switch (code)
+ {
+ case inMenuBar:
+ {
+ setup_menus();
+ menu(MenuSelect(event.where));
+ HiliteMenu(0);
+ break;
+ }
+
+ case inDrag:
+ {
+ WindowPtr old_win;
+ BitMap tBitMap;
+ Rect pRect;
+
+ r = GetQDGlobalsScreenBits(&tBitMap)->bounds;
+ r.top += 20; /* GetMBarHeight() XXX XXX XXX */
+ InsetRect(&r, 4, 4);
+ DragWindow(w, event.where, &r);
+
+ /* Oops */
+ if (!td) break;
+
+ /* Save */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyze */
+ GetWindowBounds(
+ (WindowRef)td->w,
+ kWindowContentRgn,
+ &pRect);
+ td->r.left = pRect.left;
+ td->r.top = pRect.top;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ case inGoAway:
+ {
+ /* Oops */
+ if (!td) break;
+
+ /* Track the go-away box */
+ if (TrackGoAway(w, event.where))
+ {
+ /* Not Mapped */
+ td->mapped = FALSE;
+
+ /* Not Mapped */
+ td->t->mapped_flag = FALSE;
+
+ /* Hide the window */
+ TransitionWindow(td->w,
+ kWindowZoomTransitionEffect,
+ kWindowHideTransitionAction,
+ NULL);
+ }
+
+ break;
+ }
+
+ case inGrow:
+ {
+ int x, y;
+
+ Rect nr;
+
+ term *old = Term;
+
+ /* Oops */
+ if (!td) break;
+
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Minimum and maximum sizes */
+ r.left = 20 * td->tile_wid + td->size_ow1;
+ r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1;
+ r.top = 1 * td->tile_hgt + td->size_oh1;
+ r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
+
+ /* Grow the rectangle */
+ if (!ResizeWindow(w, event.where, &r, NULL)) break;
+#else
+
+ /* Grow the rectangle */
+ if (!ResizeWindow(w, event.where, NULL, NULL)) break;
+
+#endif /* !ALLOW_BIG_SCREEN */
+
+
+ /* Obtain geometry of resized window */
+ GetWindowBounds(w, kWindowContentRgn, &nr);
+
+ /* Extract the new size in pixels */
+ y = nr.bottom - nr.top - td->size_oh1 - td->size_oh2;
+ x = nr.right - nr.left - td->size_ow1 - td->size_ow2;
+
+ /* Extract a "close" approximation */
+ td->rows = y / td->tile_hgt;
+ td->cols = x / td->tile_wid;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Activate */
+ Term_activate(td->t);
+
+ /* Hack -- Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ Term_activate(old);
+
+ break;
+ }
+
+ case inContent:
+ {
+ SelectWindow(w);
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* OS Event -- From "Maarten Hazewinkel" */
+ case osEvt:
+ {
+ switch ((event.message >> 24) & 0x000000FF)
+ {
+ case suspendResumeMessage:
+
+ /* Resuming: activate the front window */
+ if (event.message & resumeFlag)
+ {
+ Cursor tempCursor;
+ SetPort(GetWindowPort(FrontWindow()));
+ SetCursor(GetQDGlobalsArrow(&tempCursor));
+ }
+
+ /* Suspend: deactivate the front window */
+ else
+ {
+ /* Nothing */
+ }
+
+ break;
+ }
+
+ break;
+ }
+
+ /* From "Steve Linberg" and "Maarten Hazewinkel" */
+ case kHighLevelEvent:
+ {
+ /* Process apple events */
+ (void)AEProcessAppleEvent(&event);
+
+ /* Handle "quit_when_ready" */
+ if (quit_when_ready)
+ {
+#if 0 /* Doesn't work with Aqua well */
+ /* Forget */
+ quit_when_ready = FALSE;
+
+ /* Do the menu key */
+ menu(MenuKey('q'));
+#endif
+ /* Turn off the menus */
+ HiliteMenu(0);
+ }
+
+ /* Handle "open_when_ready" */
+ else if (open_when_ready)
+ {
+ handle_open_when_ready();
+ }
+
+ break;
+ }
+
+ }
+
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+/*** Some Hooks for various routines ***/
+
+
+/*
+ * Mega-Hack -- emergency lifeboat
+ */
+static void *lifeboat = NULL;
+
+
+/*
+ * Hook to "release" memory
+ */
+#ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */
+static void *hook_rnfree(void *v)
+#else
+static void *hook_rnfree(void *v, size_t size)
+#endif /* NEW_ZVIRT_HOOKS */
+{
+
+#ifdef USE_MALLOC
+
+ /* Alternative method */
+ free(v);
+
+#else
+
+ /* Dispose */
+ DisposePtr(v);
+
+#endif
+
+ /* Success */
+ return (NULL);
+}
+
+/*
+ * Hook to "allocate" memory
+ */
+static void *hook_ralloc(size_t size)
+{
+
+#ifdef USE_MALLOC
+
+ /* Make a new pointer */
+ return (malloc(size));
+
+#else
+
+ /* Make a new pointer */
+ return (NewPtr(size));
+
+#endif
+
+}
+
+/*
+ * Hook to handle "out of memory" errors
+ */
+static void *hook_rpanic(size_t size)
+{
+ /* void *mem = NULL; */
+
+ /* Free the lifeboat */
+ if (lifeboat)
+ {
+ /* Free the lifeboat */
+ DisposePtr(lifeboat);
+
+ /* Forget the lifeboat */
+ lifeboat = NULL;
+
+ /* Mega-Hack -- Warning */
+ mac_warning("Running out of Memory!\rAbort this process now!");
+
+ /* Mega-Hack -- Never leave this function */
+ while (TRUE) CheckEvents(TRUE);
+ }
+
+ /* Mega-Hack -- Crash */
+ return (NULL);
+}
+
+
+/*
+ * Hook to tell the user something important
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning message */
+ mac_warning(str);
+}
+
+
+/*
+ * Hook to tell the user something, and then quit
+ */
+static void hook_quit(cptr str)
+{
+ /* Warning if needed */
+ if (str) mac_warning(str);
+
+#ifdef USE_ASYNC_SOUND
+
+ /* Clean up sound support */
+ cleanup_sound();
+
+#endif /* USE_ASYNC_SOUND */
+
+ /* Dispose of graphic tiles */
+ if (frameP)
+ {
+ /* Unlock */
+ BenSWUnlockFrame(frameP);
+
+ /* Dispose of the GWorld */
+ DisposeGWorld(frameP->framePort);
+
+ /* Dispose of the memory */
+ DisposePtr((Ptr)frameP);
+ }
+
+ /* Write a preference file */
+ save_pref_file();
+
+ /* All done */
+ ExitToShell();
+}
+
+
+/*
+ * Hook to tell the user something, and then crash
+ */
+static void hook_core(cptr str)
+{
+ /* XXX Use the debugger */
+ /* DebugStr(str); */
+
+ /* Warning */
+ if (str) mac_warning(str);
+
+ /* Warn, then save player */
+ mac_warning("Fatal error.\rI will now attempt to save and quit.");
+
+ /* Attempt to save */
+ if (!save_player()) mac_warning("Warning -- save failed!");
+
+ /* Quit */
+ quit(NULL);
+}
+
+
+
+/*** Main program ***/
+
+
+/*
+ * Init some stuff
+ *
+ * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
+ * "Macintosh Save Bug" by using "absolute" path names, since on
+ * System 7 machines anyway, the "current working directory" often
+ * "changes" due to background processes, invalidating any "relative"
+ * path names. Note that the Macintosh is limited to 255 character
+ * path names, so be careful about deeply embedded directories...
+ *
+ * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
+ * "missing lib folder bug" by allowing the user to help find the
+ * "lib" folder by hand if the "application folder" code fails...
+ *
+ *
+ * The problem description above no longer applies, but I left it here,
+ * modified for Carbon, to allow the game proceeds when a user doesn't
+ * placed the Angband binary and the lib folder in the same place for
+ * whatever reasons. -- pelpel
+ */
+static void init_stuff(void)
+{
+ Rect r;
+ BitMap tBitMap;
+ Rect screenRect;
+ Point topleft;
+
+ char path[1024];
+
+ OSErr err = noErr;
+ NavDialogOptions dialogOptions;
+ FSSpec theFolderSpec;
+ NavReplyRecord theReply;
+
+
+ /* Fake rectangle */
+ r.left = 0;
+ r.top = 0;
+ r.right = 344;
+ r.bottom = 188;
+
+ /* Center it */
+ screenRect = GetQDGlobalsScreenBits(&tBitMap)->bounds;
+ center_rect(&r, &screenRect);
+
+ /* Extract corner */
+ topleft.v = r.top;
+ topleft.h = r.left;
+
+ /* Default to the "lib" folder with the application */
+#ifdef MACH_O_CARBON
+ if (locate_lib(path, sizeof(path)) == NULL) quit(NULL);
+#else
+ refnum_to_name(path, app_dir, app_vol, (char*)("\plib:"));
+#endif
+
+
+ /* Check until done */
+ while (1)
+ {
+ /* Prepare the paths */
+ init_file_paths(path);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_FILE, "news.txt");
+
+ /* Attempt to open and close that file */
+ if (0 == fd_close(fd_open(path, O_RDONLY))) break;
+
+ /* Warning */
+ plog_fmt("Unable to open the '%s' file.", path);
+
+ /* Warning */
+ plog("The Angband 'lib' folder is probably missing or misplaced.");
+
+ /* Ask the user to choose the lib folder */
+ err = NavGetDefaultDialogOptions(&dialogOptions);
+
+ /* Paranoia */
+ if (err != noErr) quit(NULL);
+
+ /* Set default location option */
+ dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
+
+ /* Clear preview option */
+ dialogOptions.dialogOptionFlags &= ~(kNavAllowPreviews);
+
+ /* Forbit selection of multiple files */
+ dialogOptions.dialogOptionFlags &= ~(kNavAllowMultipleFiles);
+
+ /* Display location */
+ dialogOptions.location = topleft;
+
+#if 0
+
+ /* Load the message for the missing folder from the resource fork */
+ /* GetIndString(dialogOptions.message, 128, 1); */
+
+#else
+
+ /* Set the message for the missing folder XXX XXX */
+ strcpy(dialogOptions.message + 1, "Please select the \"lib\" folder");
+ dialogOptions.message[0] = strlen(dialogOptions.message + 1);
+
+#endif
+
+ /* Wait for the user to choose a folder */
+ err = NavChooseFolder(
+ nil, &theReply, &dialogOptions, nil, nil, nil);
+
+ /* Assume the player doesn't want to go on */
+ if ((err != noErr) || !theReply.validRecord) quit(NULL);
+
+ /* Retrieve FSSpec from the reply */
+ {
+ AEKeyword theKeyword;
+ DescType actualType;
+ Size actualSize;
+
+ /* Get a pointer to selected folder */
+ err = AEGetNthPtr(
+ &(theReply.selection), 1, typeFSS, &theKeyword,
+ &actualType, &theFolderSpec, sizeof(FSSpec), &actualSize);
+
+ /* Paranoia */
+ if (err != noErr) quit(NULL);
+ }
+
+ /* Free navitagor reply */
+ err = NavDisposeReply(&theReply);
+
+ /* Paranoia */
+ if (err != noErr) quit(NULL);
+
+ /* Extract textual file name for given file */
+#ifdef MACH_O_CARBON
+ if (spec_to_path(&theFolderSpec, path, sizeof(path)) != noErr)
+ {
+ quit(NULL);
+ }
+#else /* MACH_O_CARBON */
+refnum_to_name(
+ path,
+ theFolderSpec.parID,
+ theFolderSpec.vRefNum,
+ (char *)theFolderSpec.name);
+#endif /* MACH_O_CARBON */
+ }
+}
+
+
+/*
+ * Macintosh Main loop
+ */
+int main(void)
+{
+ int i;
+ long response;
+ OSStatus err;
+ EventRecord tempEvent;
+ UInt32 numberOfMasters = 10;
+
+ /* Get more Masters -- it is not recommended by Apple, should go away */
+ MoreMasterPointers(numberOfMasters);
+
+ /* Check for existence of Carbon */
+ err = Gestalt(gestaltCarbonVersion, &response);
+
+ if (err != noErr) quit("This program requires Carbon API");
+
+ /* See if we are running on Aqua */
+ err = Gestalt(gestaltMenuMgrAttr, &response);
+
+ /* Cache the result */
+ if ((err == noErr) &&
+ (response & gestaltMenuMgrAquaLayoutMask)) is_aqua = TRUE;
+
+ /*
+ * Remember Mac OS version, in case we have to cope with version-specific
+ * problems
+ */
+ (void)Gestalt(gestaltSystemVersion, &mac_os_version);
+
+
+ /* Set up the Macintosh */
+ InitCursor();
+
+ /* Flush events */
+ FlushEvents(everyEvent, 0);
+
+ /* Flush events some more (?) */
+ if (EventAvail(everyEvent, &tempEvent)) FlushEvents(everyEvent, 0);
+
+
+ /* Install the start event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEOpenApplication,
+ NewAEEventHandlerUPP(AEH_Start),
+ 0L,
+ FALSE);
+
+ /* Install the quit event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEQuitApplication,
+ NewAEEventHandlerUPP(AEH_Quit),
+ 0L,
+ FALSE);
+
+ /* Install the print event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEPrintDocuments,
+ NewAEEventHandlerUPP(AEH_Print),
+ 0L,
+ FALSE);
+
+ /* Install the open event hook (ignore error codes) */
+ AEInstallEventHandler(
+ kCoreEventClass,
+ kAEOpenDocuments,
+ NewAEEventHandlerUPP(AEH_Open),
+ 0L,
+ FALSE);
+
+
+#ifndef MACH_O_CARBON
+
+ /* Find the current application */
+ SetupAppDir();
+
+#endif /* !MACH_O_CARBON */
+
+ /* Mark ourself as the file creator */
+ _fcreator = ANGBAND_CREATOR;
+
+ /* Default to saving a "text" file */
+ _ftype = 'TEXT';
+
+
+ /* Hook in some "z-virt.c" hooks */
+ rnfree_aux = hook_rnfree;
+ ralloc_aux = hook_ralloc;
+ rpanic_aux = hook_rpanic;
+
+ /* Hooks in some "z-util.c" hooks */
+ plog_aux = hook_plog;
+ quit_aux = hook_quit;
+ core_aux = hook_core;
+
+
+ /* Initialize colors */
+ for (i = 0; i < 256; i++)
+ {
+ u16b rv, gv, bv;
+
+ /* Extract the R,G,B data */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Save the actual color */
+ color_info[i].red = (rv | (rv << 8));
+ color_info[i].green = (gv | (gv << 8));
+ color_info[i].blue = (bv | (bv << 8));
+ }
+
+
+ /* Show the "watch" cursor */
+ SetCursor(*(GetCursor(watchCursor)));
+
+ /* Prepare the menubar */
+ init_menubar();
+
+ /* Prepare the windows */
+ init_windows();
+
+ /* Hack -- process all events */
+ while (CheckEvents(FALSE)) /* loop */;
+
+ /* Reset the cursor */
+ {
+ Cursor tempCursor;
+
+ SetCursor(GetQDGlobalsArrow(&tempCursor));
+ }
+
+ /* Mega-Hack -- Allocate a "lifeboat" */
+ lifeboat = NewPtr(16384);
+
+#ifdef USE_QT_SOUND
+
+ /* Load sound effect resources */
+ load_sounds();
+
+#endif /* USE_QT_SOUND */
+
+ /* Note the "system" */
+ ANGBAND_SYS = "mac";
+
+#ifdef PRIVATE_USER_PATH
+ if (check_create_user_dir() == FALSE)
+ quit("Cannot create directory " PRIVATE_USER_PATH);
+#endif
+
+ /* Initialize */
+ init_stuff();
+
+ /* Initialize */
+ init_angband();
+
+
+ /* Hack -- process all events */
+ while (CheckEvents(FALSE)) /* loop */;
+
+
+ /* We are now initialized */
+ initialized = TRUE;
+
+
+ /* Handle "open_when_ready" */
+ handle_open_when_ready();
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Prompt the user - You may have to change this for some variants */
+ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 15);
+
+ /* Flush the prompt */
+ Term_fresh();
+
+ /* Hack -- Process Events Forever */
+ while (TRUE) CheckEvents(TRUE);
+
+#else
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Wait for keypress */
+ pause_line(23);
+
+ /* flush input - Warning: without this, _system_ would hang */
+ flush();
+
+ /* Play the game - note the value of the argument */
+ play_game(FALSE);
+
+ /* Quit */
+ quit(NULL);
+
+ /* Since it's a int function */
+ return (0);
+#endif /* !SAVEFILE_SCREEN */
+}
+
+#endif /* MACINTOSH || MACH_O_CARBON */
+
diff --git a/src/main-dmy.c b/src/main-dmy.c
new file mode 100644
index 00000000..6f144fdd
--- /dev/null
+++ b/src/main-dmy.c
@@ -0,0 +1,315 @@
+/* File: main-dmy.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifdef USE_DMY
+
+/*
+ * This file helps ToME run on nothing.
+ */
+
+
+#include "angband.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+/*
+ * Information about a term
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t; /* All term info */
+};
+
+/* Max number of windows on screen */
+#define MAX_TERM_DATA 4
+
+/* Information about our windows */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Hack -- Number of initialized "term" structures
+ */
+static int active = 0;
+
+
+
+
+/*
+ * Suspend/Resume
+ */
+static errr Term_xtra_dummy_alive(int v)
+{
+ int x, y;
+
+
+ /* Suspend */
+ if (!v)
+ {}
+
+ /* Resume */
+ else
+ {}
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Init the "net" system
+ */
+static void Term_init_dummy(term *t)
+{
+ term_data *td = (term_data *)(t->data);
+
+ /* Count init's, handle first */
+ if (active++ != 0) return;
+}
+
+
+/*
+ * Nuke the "net" system
+ */
+static void Term_nuke_dummy(term *t)
+{
+ int x, y;
+ term_data *td = (term_data *)(t->data);
+}
+
+
+
+
+/*
+ * Process events (with optional wait)
+ */
+static errr Term_xtra_dummy_event(int v)
+{
+ /* Success */
+ Term_keypress('\r');
+ return (0);
+}
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_dummy_react(void)
+{
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_dummy(int n, int v)
+{
+ term_data *td = (term_data *)(Term->data);
+ char buf[2];
+
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Clear screen */
+ case TERM_XTRA_CLEAR:
+ return (0);
+
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ return (0);
+
+ /* Flush the Curses buffer */
+ case TERM_XTRA_FRESH:
+ return (0);
+
+ /* Suspend/Resume curses */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_dummy_alive(v));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_dummy_event(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_dummy_event(FALSE));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ return (0);
+
+ /* React to events */
+ case TERM_XTRA_REACT:
+ Term_xtra_dummy_react();
+ return (0);
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+
+ closedir(directory);
+ return 0;
+ }
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Actually MOVE the hardware cursor
+ */
+static errr Term_curs_dummy(int x, int y)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Literally move the cursor */
+ // DGDGDGD
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a grid of space
+ * Hack -- try to be "semi-efficient".
+ */
+static errr Term_wipe_dummy(int x, int y, int n)
+{
+ /* Success */
+ return (0);
+}
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_dummy(int x, int y, int n, byte a, cptr s)
+{
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Create a window for the given "term_data" argument.
+ *
+ * Assumes legal arguments.
+ */
+static errr term_data_init_dummy(term_data *td, int rows, int cols)
+{
+ term *t = &td->t;
+
+ /* Initialize the term */
+ term_init(t, cols, rows, 256);
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Set some hooks */
+ t->init_hook = Term_init_dummy;
+ t->nuke_hook = Term_nuke_dummy;
+
+ /* Set some more hooks */
+ t->text_hook = Term_text_dummy;
+ t->wipe_hook = Term_wipe_dummy;
+ t->curs_hook = Term_curs_dummy;
+ t->xtra_hook = Term_xtra_dummy;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate it */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+static void hook_quit(cptr str)
+{
+ /* Unused */
+ (void)str;
+}
+
+/*
+ * Prepare "curses" for use by the file "z-term.c"
+ *
+ * Installs the "hook" functions defined above, and then activates
+ * the main screen "term", which clears the screen and such things.
+ *
+ * Someone should really check the semantics of "initscr()"
+ */
+errr init_dummy(int argc, char **argv)
+{
+ int num_term = MAX_TERM_DATA, next_win = 0;
+
+ /* Activate hooks */
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Create a term */
+ term_data_init_dummy(&data[0], 25, 80);
+
+ /* Remember the term */
+ angband_term[0] = &data[0].t;
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Remember the active screen */
+ term_screen = &data[0].t;
+
+ /* Success */
+ return (0);
+}
+#endif
diff --git a/src/main-dos.c b/src/main-dos.c
new file mode 100644
index 00000000..0a93cdc8
--- /dev/null
+++ b/src/main-dos.c
@@ -0,0 +1,2417 @@
+/* File: main-dos.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Robert Ruehlmann, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with DOS computers.
+ *
+ * Adapted from "main-ibm.c".
+ *
+ * Author: Robert Ruehlmann (rr9@angband.org).
+ * See "http://thangorodrim.angband.org/".
+ *
+ * Initial framework (and some code) by Ben Harrison (benh@phial.com).
+ *
+ * This file requires the (free) DJGPP compiler (based on "gcc").
+ * See "http://www.delorie.com/djgpp/".
+ *
+ * This file uses the (free) "Allegro" library (SVGA graphics library).
+ * See "http://www.talula.demon.co.uk/allegro/".
+ *
+ * To compile this file, use "Makefile.dos", which defines "USE_DOS".
+ *
+ * See also "main-ibm.c" and "main-win.c".
+ *
+ *
+ * The "lib/user/pref.prf" file contains macro definitions and possible
+ * alternative color set definitions.
+ *
+ * The "lib/user/font.prf" contains attr/char mappings for use with the
+ * special fonts in the "lib/xtra/font/" directory.
+ *
+ * The "lib/user/graf.prf" contains attr/char mappings for use with the
+ * special bitmaps in the "lib/xtra/graf/" directory.
+ *
+ *
+ * Both "shift" keys are treated as "identical", and all the modifier keys
+ * (control, shift, alt) are ignored when used with "normal" keys, unless
+ * they modify the underlying "ascii" value of the key. You must use the
+ * new "user pref files" to be able to interact with the keypad and such.
+ *
+ * Note that "Term_xtra_dos_react()" allows runtime color, graphics,
+ * screen resolution, and sound modification.
+ *
+ *
+ * The sound code uses *.wav files placed in the "lib/xtra/sound" folder.
+ * Every sound-event can have several samples assigned to it and a random
+ * one will be played when the event occures. Look at the
+ * "lib/xtra/sound/sound.cfg" configuration file for more informations.
+ *
+ * The background music uses midi-files (and mod files) from the
+ * "lib/xtra/music" folder.
+ *
+ *
+ * Comment by Ben Harrison (benh@phial.com):
+ *
+ * On my Windows NT box, modes "VESA1" and "VESA2B" seem to work, but
+ * result in the game running with no visible output. Mode "VESA2L"
+ * clears the screen and then fails silently. All other modes fail
+ * instantly. To recover from such "invisible" modes, you can try
+ * typing escape, plus control-x, plus escape. XXX XXX XXX
+ *
+ * Comment by Eric Stevens (tome@eastevens.com):
+ *
+ * This file has been modified to work with Allegro 4.0.3 that comes with
+ * DJGPP 2.0.3. This modification allows ToME to use the .dat font files
+ * provided with the Angband release for DOS.
+ *
+ */
+
+#include "angband.h"
+
+
+#ifdef USE_DOS
+
+#include <allegro.h>
+
+#ifdef USE_MOD_FILES
+#include <jgmod.h>
+#endif /* USE_MOD_FILES */
+
+#include "load_gif.c"
+
+
+#include <bios.h>
+#include <dos.h>
+#include <keys.h>
+#include <unistd.h>
+#include <dir.h>
+
+/*
+ * ZAngband remapping for race and class in the enhanced bitmap
+ */
+/* #define PLAYER_REMAP */
+
+
+/*
+ * Index of the first standard Angband color.
+ *
+ * All colors below this index are defined by
+ * the palette of the tiles-bitmap.
+ */
+#define COLOR_OFFSET 240
+
+
+/*
+ * Maximum number of terminals
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+
+/*
+ * Extra "term" data
+ */
+struct term_data
+{
+ term t;
+
+ int number;
+
+ int x;
+ int y;
+
+ int cols;
+ int rows;
+
+ int tile_wid;
+ int tile_hgt;
+
+ int font_wid;
+ int font_hgt;
+
+ FONT *font;
+
+#ifdef USE_GRAPHICS
+
+ BITMAP *tiles;
+
+#endif /* USE_GRAPHICS */
+
+#ifdef USE_BACKGROUND
+
+ int window_type;
+
+#endif /* USE_BACKGROUND */
+
+};
+
+/*
+ * The current screen resolution
+ */
+static int resolution;
+
+#ifdef USE_BACKGROUND
+
+/*
+ * The background images
+ */
+BITMAP *background[17];
+
+#endif /* USE_BACKGROUND */
+
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Are graphics already initialized ?
+ */
+static bool graphics_initialized = FALSE;
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Small bitmap for the cursor
+ */
+static BITMAP *cursor;
+
+
+#ifdef USE_SOUND
+
+/*
+ * Is the sound already initialized ?
+ */
+static bool sound_initialized = FALSE;
+
+# ifdef USE_MOD_FILES
+/*
+ * Is the mod-file support already initialized ?
+ */
+static bool mod_file_initialized = FALSE;
+
+# endif /* USE_MOD_FILES */
+
+/*
+ * Volume settings
+ */
+static int digi_volume;
+static int midi_volume;
+
+/*
+ * The currently playing song
+ */
+static MIDI *midi_song = NULL;
+
+# ifdef USE_MOD_FILES
+
+static JGMOD *mod_song = NULL;
+
+# endif /* USE_MOD_FILES */
+
+static int current_song;
+
+/*
+ * The number of available songs
+ */
+static int song_number;
+
+/*
+ * The maximum number of available songs
+ */
+#define MAX_SONGS 255
+
+static char music_files[MAX_SONGS][16];
+
+/*
+ * The maximum number of samples per sound-event
+ */
+#define SAMPLE_MAX 10
+
+/*
+ * An array of sound files
+ */
+static SAMPLE* samples[SOUND_MAX][SAMPLE_MAX];
+
+/*
+ * The number of available samples for every event
+ */
+static int sample_count[SOUND_MAX];
+
+#endif /* USE_SOUND */
+
+
+/*
+ * Extra paths
+ */
+static char xtra_font_dir[1024];
+static char xtra_graf_dir[1024];
+static char xtra_sound_dir[1024];
+static char xtra_music_dir[1024];
+
+
+/*
+ * List of the available videomodes to reduce executable size
+ */
+ /*
+DECLARE_GFX_DRIVER_LIS
+(
+ GFX_DRIVER_VBEAF
+ GFX_DRIVER_VESA2L
+ GFX_DRIVER_VESA2B
+ GFX_DRIVER_ATI
+ GFX_DRIVER_MACH64
+ GFX_DRIVER_CIRRUS64
+ GFX_DRIVER_CIRRUS54
+ GFX_DRIVER_PARADISE
+ GFX_DRIVER_S3
+ GFX_DRIVER_TRIDENT
+ GFX_DRIVER_ET3000
+ GFX_DRIVER_ET4000
+ GFX_DRIVER_VIDEO7
+ GFX_DRIVER_VESA1
+)
+*/
+
+/*
+ * Declare the videomode list
+ */
+//DECLARE_COLOR_DEPTH_LIST(COLOR_DEPTH_8)
+
+
+/*
+ * Keypress input modifier flags (hard-coded by DOS)
+ */
+#define K_RSHIFT 0 /* Right shift key down */
+#define K_LSHIFT 1 /* Left shift key down */
+#define K_CTRL 2 /* Ctrl key down */
+#define K_ALT 3 /* Alt key down */
+#define K_SCROLL 4 /* Scroll lock on */
+#define K_NUM 5 /* Num lock on */
+#define K_CAPS 6 /* Caps lock on */
+#define K_INSERT 7 /* Insert on */
+
+
+/*
+ * Prototypes
+ */
+static errr Term_xtra_dos_event(int v);
+static void Term_xtra_dos_react(void);
+static void Term_xtra_dos_clear(void);
+static errr Term_xtra_dos(int n, int v);
+static errr Term_user_dos(int n);
+static errr Term_curs_dos(int x, int y);
+static errr Term_wipe_dos(int x, int y, int n);
+static errr Term_text_dos(int x, int y, int n, byte a, const char *cp);
+static void Term_init_dos(term *t);
+static void Term_nuke_dos(term *t);
+static void term_data_link(term_data *td);
+static void dos_dump_screen(void);
+static void dos_quit_hook(cptr str);
+static bool init_windows(void);
+errr init_dos(void);
+#ifdef USE_SOUND
+static bool init_sound(void);
+static errr Term_xtra_dos_sound(int v);
+static void play_song(void);
+#endif /* USE_SOUND */
+#ifdef USE_GRAPHICS
+static bool init_graphics(void);
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp);
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp);
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp);
+# endif /* USE_TRANSPARENCY */
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Process an event (check for a keypress)
+ *
+ * The keypress processing code is often the most system dependant part
+ * of Angband, since sometimes even the choice of compiler is important.
+ *
+ * For this file, we divide all keypresses into two categories, first, the
+ * "normal" keys, including all keys required to play Angband, and second,
+ * the "special" keys, such as keypad keys, function keys, and various keys
+ * used in combination with various modifier keys.
+ *
+ * To simplify this file, we use Angband's "macro processing" ability, in
+ * combination with the "lib/user/pref.prf" file, to handle most of the
+ * "special" keys, instead of attempting to fully analyze them here. This
+ * file only has to determine when a "special" key has been pressed, and
+ * translate it into a simple string which signals the use of a "special"
+ * key, the set of modifiers used, if any, and the hardware scan code of
+ * the actual key which was pressed. To simplify life for the user, we
+ * treat both "shift" keys as identical modifiers.
+ *
+ * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers
+ * ("C" for control, "S" for shift, "A" for alt, or any ordered combination),
+ * and "SS" encodes the keypress (as the two "digit" hexadecimal encoding of
+ * the scan code of the key that was pressed), and the "^_" and "x" and "\r"
+ * delimit the encoding for recognition by the macro processing code.
+ *
+ * Some important facts about scan codes follow. All "normal" keys use
+ * scan codes from 1-58. The "function" keys use 59-68 (and 133-134).
+ * The "keypad" keys use 69-83. Escape uses 1. Enter uses 28. Control
+ * uses 29. Left Shift uses 42. Right Shift uses 54. PrtScrn uses 55.
+ * Alt uses 56. Space uses 57. CapsLock uses 58. NumLock uses 69.
+ * ScrollLock uses 70. The "keypad" keys which use scan codes 71-83
+ * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL.
+ *
+ * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more
+ * information, including better access to the keypad keys in combination
+ * with various modifiers, but only works on "PC's after 6/1/86", and there
+ * is no way to determine if the function is provided on a machine. I have
+ * been told that without it you cannot detect, for example, control-left.
+ * The basic scan code + ascii value pairs returned by the keypad follow,
+ * with values in parentheses only available to "bioskey(0x10)".
+ *
+ * / * - + 1 2 3 4
+ * Norm: 352f 372a 4a2d 4e2b 4f00 5000 5100 4b00
+ * Shft: 352f 372a 4a2d 4e2b 4f31 5032 5133 4b34
+ * Ctrl: (9500) (9600) (8e00) (9000) 7500 (9100) 7600 7300
+ *
+ * 5 6 7 8 9 0 . Enter
+ * Norm: (4c00) 4d00 4700 4800 4900 5200 5300 (e00d)
+ * Shft: 4c35 4d36 4737 4838 4939 5230 532e (e00d)
+ * Ctrl: (8f00) 7400 7700 (8d00) 8400 (9200) (9300) (e00a)
+ *
+ * See "lib/user/pref-win.prf" for the "standard" macros for various keys.
+ *
+ * Certain "bizarre" keypad keys (such as "enter") return a "scan code"
+ * of "0xE0", and a "usable" ascii value. These keys should be treated
+ * like the normal keys, see below. XXX XXX XXX Note that these "special"
+ * keys could be prefixed with an optional "ctrl-^" which would allow them
+ * to be used in macros without hurting their use in normal situations.
+ *
+ * This function also appears in "main-ibm.c". XXX XXX XXX
+ *
+ * Addition for the DOS version: Dump-screen function with the
+ * "Ctrl-Print" key saves a bitmap with the screen contents to
+ * "lib/user/dump.bmp".
+ */
+static errr Term_xtra_dos_event(int v)
+{
+ int i, k, s;
+
+ bool mc = FALSE;
+ bool ms = FALSE;
+ bool ma = FALSE;
+
+ /* Hack -- Check for a keypress */
+ if (!v && !bioskey(1)) return (1);
+
+ /* Wait for a keypress */
+ k = bioskey(0x10);
+
+ /* Access the "modifiers" */
+ i = bioskey(2);
+
+ /* Extract the "scan code" */
+ s = ((k >> 8) & 0xFF);
+
+ /* Extract the "ascii value" */
+ k = (k & 0xFF);
+
+ /* Process "normal" keys */
+ if ((s <= 58) || (s == 0xE0))
+ {
+ /* Enqueue it */
+ if (k) Term_keypress(k);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Extract the modifier flags */
+ if (i & (1 << K_CTRL)) mc = TRUE;
+ if (i & (1 << K_LSHIFT)) ms = TRUE;
+ if (i & (1 << K_RSHIFT)) ms = TRUE;
+ if (i & (1 << K_ALT)) ma = TRUE;
+
+ /* Dump the screen with "Ctrl-Print" */
+ if ((s == 0x72) && mc)
+ {
+ /* Dump the screen */
+ dos_dump_screen();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Begin a "macro trigger" */
+ Term_keypress(31);
+
+ /* Hack -- Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Introduce the hexidecimal scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[s / 16]);
+ Term_keypress(hexsym[s % 16]);
+
+ /* End the "macro trigger" */
+ Term_keypress(13);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef SUPPORT_GAMMA
+
+/*
+ * When set to TRUE, indicates that we can use gamma_table
+ */
+static bool gamma_table_ready = FALSE;
+
+
+/*
+ * Build gamma correction table if requested and applicable
+ */
+static void setup_gamma_table(void)
+{
+ static u16b old_gamma_val = 0;
+
+ /* (Re)build the table only when gamma value changes */
+ if (gamma_val == old_gamma_val) return;
+
+ /* Temporarily inactivate the table */
+ gamma_table_ready = FALSE;
+
+ /* Validate gamma_val */
+ if ((gamma_val <= 0) || (gamma_val >= 256))
+ {
+ /* Reset */
+ old_gamma_val = gamma_val = 0;
+
+ /* Leave it inactive */
+ return;
+ }
+
+ /* (Re)build the table */
+ build_gamma_table(gamma_val);
+
+ /* Remember gamma_val */
+ old_gamma_val = gamma_val;
+
+ /* Activate the table */
+ gamma_table_ready = TRUE;
+}
+
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * React to global changes in the colors, graphics, and sound settings.
+ */
+static void Term_xtra_dos_react(void)
+{
+ int i;
+
+#ifdef USE_SPECIAL_BACKGROUND
+
+ int j;
+
+ term_data *td;
+
+#endif /* USE_SPECIAL_BACKGROUND */
+
+
+#ifdef SUPPORT_GAMMA
+
+ /* Setup gamma_table */
+ setup_gamma_table();
+
+#endif /* SUPPORT_GAMMA */
+
+ /*
+ * Set the Angband colors
+ */
+ for (i = 0; i < 16; i++)
+ {
+ byte rv, gv, bv;
+ RGB colour;
+
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+#ifdef SUPPORT_GAMMA
+
+ /* Hack - Gamma correction */
+ if (gamma_table_ready)
+ {
+ rv = gamma_table[rv];
+ gv = gamma_table[gv];
+ bv = gamma_table[bv];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* 8 bit to 6 bit conversion */
+ colour.r = rv >> 2;
+ colour.g = gv >> 2;
+ colour.b = bv >> 2;
+
+ set_color(COLOR_OFFSET + i, &colour);
+ }
+
+#ifdef USE_GRAPHICS
+
+ /*
+ * Handle "arg_graphics"
+ */
+ if (use_graphics != arg_graphics)
+ {
+ /* Initialize (if needed) */
+ if (arg_graphics && !init_graphics())
+ {
+ /* Warning */
+ plog("Cannot initialize graphics!");
+
+ /* Cannot enable */
+ arg_graphics = FALSE;
+ }
+
+ /* Change setting */
+ use_graphics = arg_graphics;
+ }
+
+#endif /* USE_GRAPHICS */
+
+#ifdef USE_SOUND
+
+ /*
+ * Handle "arg_sound"
+ */
+ if (use_sound != arg_sound)
+ {
+ /* Clear the old song */
+ if (midi_song) destroy_midi(midi_song);
+ midi_song = NULL;
+#ifdef USE_MOD_FILES
+ if (mod_file_initialized)
+ {
+ stop_mod();
+ if (mod_song) destroy_mod(mod_song);
+ mod_song = NULL;
+ }
+#endif /* USE_MOD_FILES */
+
+ /* Initialize (if needed) */
+ if (arg_sound && !init_sound())
+ {
+ /* Warning */
+ plog("Cannot initialize sound!");
+
+ /* Cannot enable */
+ arg_sound = FALSE;
+ }
+
+ /* Change setting */
+ use_sound = arg_sound;
+ }
+
+#endif /* USE_SOUND */
+
+#ifdef USE_SPECIAL_BACKGROUND
+
+ /*
+ * Initialize the window backgrounds
+ */
+ for (i = 0; i < 8; i++)
+ {
+ td = &data[i];
+
+ /* Window flags */
+ for (j = 0; j < 16; j++)
+ {
+ if (op_ptr->window_flag[i] & (1L << j))
+ {
+ if (background[j + 1])
+ {
+ td->window_type = j + 1;
+ }
+ else
+ {
+ td->window_type = 0;
+ }
+ }
+ }
+ }
+#endif /* USE_SPECIAL_BACKGROUND */
+}
+
+
+/*
+ * Clear a terminal
+ *
+ * Fills the terminal area with black color or with
+ * the background image
+ */
+static void Term_xtra_dos_clear(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+#ifdef USE_BACKGROUND
+ int bgrnd;
+#endif /* USE_BACKGROUND */
+
+ int x1, y1;
+ int w1, h1;
+
+ /* Location */
+ x1 = td->x;
+ y1 = td->y;
+
+ /* Size */
+ w1 = td->tile_wid * td->cols;
+ h1 = td->tile_hgt * td->rows;
+
+#ifdef USE_BACKGROUND
+
+ bgrnd = td->window_type;
+
+ if (background[bgrnd])
+ {
+ /* Draw the background */
+ stretch_blit(background[bgrnd], screen,
+ 0, 0, background[bgrnd]->w, background[bgrnd]->h,
+ x1, y1, w1, h1);
+ }
+ else
+
+#endif /* USE_BACKGROUND */
+
+ {
+ /* Draw the Term black */
+ rectfill(screen,
+ x1, y1, x1 + w1 - 1, y1 + h1 - 1,
+ COLOR_OFFSET + TERM_DARK);
+ }
+}
+
+
+/*
+ * Handle a "special request"
+ *
+ * The given parameters are "valid".
+ */
+static errr Term_xtra_dos(int n, int v)
+{
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Make a "bell" noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Make a bell noise */
+ (void)write(1, "\007", 1);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ /* Clear the screen */
+ Term_xtra_dos_clear();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ {
+ irc_poll();
+
+ /* Process one event */
+ return (Term_xtra_dos_event(v));
+ }
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Strip events */
+ while (!Term_xtra_dos_event(FALSE));
+
+ /* Success */
+ return (0);
+ }
+
+ /* Do something useful if bored */
+ case TERM_XTRA_BORED:
+ {
+ irc_poll();
+
+#ifdef USE_SOUND
+ /*
+ * Check for end of song and start a new one
+ */
+ if (!use_sound) return (0);
+
+#ifdef USE_MOD_FILES
+ if (song_number && ((midi_pos == -1) && !is_mod_playing()))
+#else /* USE_MOD_FILES */
+if (song_number && (midi_pos == -1))
+#endif /* USE_MOD_FILES */
+ {
+ if (song_number > 1)
+ {
+ /* Get a *new* song at random */
+ while (1)
+ {
+ n = randint(song_number);
+ if (n != current_song) break;
+ }
+ current_song = n;
+ }
+ else
+ {
+ /* We only have one song, so loop it */
+ current_song = 1;
+ }
+
+ /* Play the song */
+ play_song();
+ }
+
+#endif /* USE_SOUND */
+
+ /* Success */
+ return (0);
+ }
+
+ /* React to global changes */
+ case TERM_XTRA_REACT:
+ {
+ /* Change the colors */
+ Term_xtra_dos_react();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ irc_poll();
+
+ /* Delay if needed */
+ if (v > 0) delay(v);
+
+ /* Success */
+ return (0);
+ }
+
+#ifdef USE_SOUND
+
+ /* Make a sound */
+ case TERM_XTRA_SOUND:
+ {
+ return (Term_xtra_dos_sound(v));
+ }
+
+#endif /* USE_SOUND */
+
+ /*
+ * Scans for subdirectories in a directory "scansubdir_dir"
+ * and place teh result in "scansubdir_result/scansubdir_max"
+ */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ struct ffblk f;
+ int done;
+
+ done = findfirst(format("%s\\*", scansubdir_dir), &f, FA_DIREC);
+
+ scansubdir_max = 0;
+ while ((!done) && (scansubdir_max < 255))
+ {
+ if ((f.ff_attrib & FA_DIREC) && (strcmp(f.ff_name, ".")) && (strcmp(f.ff_name, "..")))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(f.ff_name);
+ scansubdir_max++;
+ }
+
+ done = findnext(&f);
+ }
+
+ return 0;
+ }
+
+ }
+
+ /* Unknown request */
+ return (1);
+}
+
+
+/*
+ * Do a "user action" on the current "term"
+ */
+static errr Term_user_dos(int n)
+{
+ int k;
+
+ char status[4];
+
+ char section[80];
+
+ /* Interact */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Print date and time of compilation */
+ prt(format("Compiled: %s %s\n", __TIME__, __DATE__), 1, 45);
+
+ /* Why are we here */
+ prt("DOS options", 2, 0);
+
+ /* Give some choices */
+#ifdef USE_SOUND
+ prt("(V) Sound Volume", 4, 5);
+ prt("(M) Music Volume", 5, 5);
+#endif /* USE_SOUND */
+
+#ifdef USE_GRAPHICS
+
+ if (arg_graphics)
+ {
+ strcpy(status, "On");
+ }
+ else
+ {
+ strcpy(status, "Off");
+ }
+ prt(format("(G) Graphics : %s", status), 7, 5);
+
+#endif /* USE_GRAPHICS */
+
+#ifdef USE_SOUND
+
+ if (arg_sound)
+ {
+ strcpy(status, "On");
+ }
+ else
+ {
+ strcpy(status, "Off");
+ }
+ prt(format("(S) Sound/Music : %s", status), 8, 5);
+
+#endif /* USE_SOUND */
+
+ prt("(R) Screen resolution", 12, 5);
+
+ prt("(W) Save current options", 14, 5);
+
+ /* Prompt */
+ prt("Command: ", 18, 0);
+
+ /* Get command */
+ k = inkey();
+
+ /* Exit */
+ if (k == ESCAPE) break;
+
+ /* Analyze */
+ switch (k)
+ {
+#ifdef USE_SOUND
+ /* Sound Volume */
+ case 'V':
+ case 'v':
+ {
+ /* Prompt */
+ prt("Command: Sound Volume", 18, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ prt(format("Current Volume: %d", digi_volume), 22, 0);
+ prt("Change Volume (+, - or ESC to accept): ", 20, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ switch (k)
+ {
+ case '+':
+ {
+ digi_volume++;
+ if (digi_volume > 255) digi_volume = 255;
+ break;
+ }
+ case '-':
+ {
+ digi_volume--;
+ if (digi_volume < 0) digi_volume = 0;
+ break;
+ }
+ /* Unknown option */
+ default:
+ {
+ break;
+ }
+ }
+ set_volume(digi_volume, -1);
+ }
+ break;
+ }
+
+ /* Music Volume */
+ case 'M':
+ case 'm':
+ {
+ /* Prompt */
+ prt("Command: Music Volume", 18, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ prt(format("Current Volume: %d", midi_volume), 22, 0);
+ prt("Change Volume (+, - or ESC to accept): ", 20, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ switch (k)
+ {
+ case '+':
+ {
+ midi_volume++;
+ if (midi_volume > 255) midi_volume = 255;
+ break;
+ }
+ case '-':
+ {
+ midi_volume--;
+ if (midi_volume < 0) midi_volume = 0;
+ break;
+ }
+ /* Unknown option */
+ default:
+ {
+ break;
+ }
+ }
+ set_volume( -1, midi_volume);
+ }
+ break;
+ }
+
+#endif /*USE_SOUND */
+
+#ifdef USE_GRAPHICS
+
+ /* Switch graphics on/off */
+ case 'G':
+ case 'g':
+ {
+ /* Toggle "arg_graphics" */
+ arg_graphics = !arg_graphics;
+
+ /* React to changes */
+ Term_xtra_dos_react();
+
+ /* Reset visuals */
+#ifdef ANGBAND_2_8_1
+ reset_visuals();
+#else /* ANGBAND_2_8_1 */
+ reset_visuals(TRUE);
+#endif /* ANGBAND_2_8_1 */
+ break;
+ }
+
+#endif /* USE_GRAPHICS */
+
+#ifdef USE_SOUND
+
+ /* Sound/Music On/Off */
+ case 'S':
+ case 's':
+ {
+ /* Toggle "arg_sound" */
+ arg_sound = !arg_sound;
+
+ /* React to changes */
+ Term_xtra_dos_react();
+
+ break;
+ }
+
+#endif /* USE_SOUND */
+
+ /* Screen Resolution */
+ case 'R':
+ case 'r':
+ {
+ int h, w, i = 1;
+ char *descr;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prompt */
+ prt("Command: Screen Resolution", 1, 0);
+ prt("Restart Angband to get the new screenmode.", 3, 0);
+
+ /* Get a list of the available presets */
+ while (1)
+ {
+ /* Section name */
+ sprintf(section, "Mode-%d", i);
+
+ /* Get new values or end the list */
+ if (!(w = get_config_int(section, "screen_wid", 0)) || (i == 16)) break;
+ h = get_config_int(section, "screen_hgt", 0);
+
+ /* Get a extra description of the resolution */
+ descr = get_config_string(section, "Description", "");
+
+ /* Print it */
+ prt(format("(%d) %d x %d %s", i, w, h, descr), 4 + i, 0);
+
+ /* Next */
+ i++;
+ }
+
+ /* Get a new resolution */
+ prt(format("Screen Resolution : %d", resolution), 20, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) resolution = D2I(k);
+
+ /* Check for min, max value */
+ if ((resolution < 1) || (resolution >= i)) resolution = 1;
+
+ /* Save the resolution */
+ set_config_int("Angband", "Resolution", resolution);
+
+ /* Return */
+ break;
+ }
+
+
+ /* Save current option */
+ case 'W':
+ case 'w':
+ {
+ prt("Saving current options", 18, 0);
+
+#ifdef USE_SOUND
+ set_config_int("sound", "digi_volume", digi_volume);
+ set_config_int("sound", "midi_volume", midi_volume);
+#endif /* USE_SOUND */
+ set_config_int("Angband", "Graphics", arg_graphics);
+ set_config_int("Angband", "Sound", arg_sound);
+
+ break;
+ }
+
+ /* Unknown option */
+ default:
+ {
+ break;
+ }
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Redraw it */
+ Term_key_push(KTRL('R'));
+
+ /* Unknown */
+ return (0);
+}
+
+
+/*
+ * Move the cursor
+ *
+ * The given parameters are "valid".
+ */
+static errr Term_curs_dos(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int x1, y1;
+
+ /* Location */
+ x1 = x * td->tile_wid + td->x;
+ y1 = y * td->tile_hgt + td->y;
+
+ /* Draw the cursor */
+ draw_sprite(screen, cursor, x1, y1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a block of the screen
+ *
+ * The given parameters are "valid".
+ */
+static errr Term_wipe_dos(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+#ifdef USE_BACKGROUND
+ int bgrnd;
+#endif /* USE_BACKGROUND */
+
+ int x1, y1;
+ int w1, h1;
+
+ /* Location */
+ x1 = x * td->tile_wid + td->x;
+ y1 = y * td->tile_hgt + td->y;
+
+ /* Size */
+ w1 = n * td->tile_wid;
+ h1 = td->tile_hgt;
+
+#ifdef USE_BACKGROUND
+
+ bgrnd = td->window_type;
+
+ if (background[bgrnd])
+ {
+ int source_x = x * background[bgrnd]->w / td->cols;
+ int source_y = y * background[bgrnd]->h / td->rows;
+ int source_w = n * background[bgrnd]->w / td->cols;
+ int source_h = background[bgrnd]->h / td->rows;
+
+ /* Draw the background */
+ stretch_blit(background[bgrnd], screen,
+ source_x, source_y, source_w, source_h,
+ x1, y1, w1, h1);
+ }
+ else
+
+#endif /* USE_BACKGROUND */
+
+ {
+ /* Draw a black block */
+ rectfill(screen, x1, y1, x1 + w1 - 1, y1 + h1 - 1,
+ COLOR_OFFSET + TERM_DARK);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ *
+ * The given parameters are "valid". Be careful with "a".
+ *
+ * The string "cp" has length "n" and is NOT null-terminated.
+ */
+static errr Term_text_dos(int x, int y, int n, byte a, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+ int x1, y1;
+
+ char text[257];
+
+ /* Location */
+ x1 = x * td->tile_wid + td->x;
+ y1 = y * td->tile_hgt + td->y;
+
+ /* Erase old contents */
+ Term_wipe_dos(x, y, n);
+
+#ifdef USE_SPECIAL_BACKGROUND
+
+ /* Show text in black in the message window */
+ if (op_ptr->window_flag[td->number] & (PW_MESSAGE)) a = 0;
+
+#endif /* USE_SPECIAL_BACKGROUND */
+
+ /* No stretch needed */
+ if (td->font_wid == td->tile_wid)
+ {
+ /* Copy the string */
+ for (i = 0; i < n; ++i) text[i] = cp[i];
+
+ /* Terminate */
+ text[i] = '\0';
+
+ /* Dump the text */
+ textout(screen, td->font, text, x1, y1,
+ COLOR_OFFSET + (a & 0x0F));
+ }
+
+ /* Stretch needed */
+ else
+ {
+ /* Pre-Terminate */
+ text[1] = '\0';
+
+ /* Write the chars to the screen */
+ for (i = 0; i < n; ++i)
+ {
+ /* Build a one character string */
+ text[0] = cp[i];
+
+ /* Dump some text */
+ textout(screen, td->font, text, x1, y1,
+ COLOR_OFFSET + (a & 0x0F));
+
+ /* Advance */
+ x1 += td->tile_wid;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Place some attr/char pairs on the screen
+ *
+ * The given parameters are "valid".
+ *
+ * To prevent crashes, we must not only remove the high bits of the
+ * "ap[i]" and "cp[i]" values, but we must map the resulting value
+ * onto the legal bitmap size, which is normally 32x32. XXX XXX XXX
+ */
+#ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp)
+#endif /* USE_TRANSPARENCY */
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+ int w, h;
+
+ int x1, y1;
+ int x2, y2;
+
+# ifdef USE_TRANSPARENCY
+
+ int x3, y3;
+
+# ifdef USE_EGO_GRAPHICS
+
+ int x4, y4;
+ bool has_overlay;
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+ /* Size */
+ w = td->tile_wid;
+ h = td->tile_hgt;
+
+ /* Location (window) */
+ x1 = x * w + td->x;
+ y1 = y * h + td->y;
+
+ /* Dump the tiles */
+ for (i = 0; i < n; i++)
+ {
+ /* Location (bitmap) */
+ x2 = (cp[i] & 0x7F) * w;
+ y2 = (ap[i] & 0x7F) * h;
+
+# ifdef USE_TRANSPARENCY
+ x3 = (tcp[i] & 0x7F) * w;
+ y3 = (tap[i] & 0x7F) * h;
+
+# ifdef EGO_GRAPHICS
+ x4 = (ecp[i] & 0x7F) * w;
+ y4 = (eap[i] & 0x7F) * h;
+ has_overlay = (ecp[i] && eap[i]);
+
+# endif /* EGO_GRAPHICS */
+
+ /* Blit the tile to the screen */
+ blit(td->tiles, screen, x3, y3, x1, y1, w, h);
+
+ /* Blit the tile to the screen */
+ masked_blit(td->tiles, screen, x2, y2, x1, y1, w, h);
+
+# ifdef EGO_GRAPHICS
+
+ /* Blit the overlay to the screen */
+ if (has_overlay)
+ masked_blit(td->tiles, screen, x4, y4, x1, y1, w, h);
+
+# endif /* EGO_GRAPHICS */
+
+# else /* USE_TRANSPARENCY */
+
+ /* Blit the tile to the screen */
+ blit(td->tiles, screen, x2, y2, x1, y1, w, h);
+
+# endif /* USE_TRANSPARENCY */
+
+ /* Advance (window) */
+ x1 += w;
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Init a Term
+ */
+static void Term_init_dos(term *t)
+{
+ /* XXX Nothing */
+}
+
+
+/*
+ * Nuke a Term
+ */
+static void Term_nuke_dos(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+
+ /* Free the terminal font */
+ if (td->font) destroy_font(td->font);
+
+#ifdef USE_GRAPHICS
+
+ /* Free the terminal bitmap */
+ if (td->tiles) destroy_bitmap(td->tiles);
+
+#endif /* USE_GRAPHICS */
+}
+
+
+
+/*
+ * Instantiate a "term_data" structure
+ */
+static void term_data_link(term_data *td)
+{
+ term *t = &td->t;
+
+ /* Initialize the term */
+ term_init(t, td->cols, td->rows, 255);
+
+ /* Use a "software" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Ignore the "TERM_XTRA_BORED" action */
+ t->never_bored = FALSE;
+
+ /* Ignore the "TERM_XTRA_FROSH" action */
+ t->never_frosh = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Prepare the init/nuke hooks */
+ t->init_hook = Term_init_dos;
+ t->nuke_hook = Term_nuke_dos;
+
+ /* Prepare the template hooks */
+ t->xtra_hook = Term_xtra_dos;
+ t->curs_hook = Term_curs_dos;
+ t->wipe_hook = Term_wipe_dos;
+ t->user_hook = Term_user_dos;
+ t->text_hook = Term_text_dos;
+
+#ifdef USE_GRAPHICS
+
+ /* Prepare the graphics hook */
+ t->pict_hook = Term_pict_dos;
+
+ /* Use "Term_pict" for "graphic" data */
+ t->higher_pict = TRUE;
+
+#endif /* USE_GRAPHICS */
+
+ /* Remember where we came from */
+ t->data = (vptr)(td);
+}
+
+
+/*
+ * Shut down visual system, then fall back into standard "quit()"
+ */
+static void dos_quit_hook(cptr str)
+{
+ int i;
+
+ /* Destroy sub-windows */
+ for (i = MAX_TERM_DATA - 1; i >= 1; i--)
+ {
+ /* Unused */
+ if (!angband_term[i]) continue;
+
+ /* Nuke it */
+ term_nuke(angband_term[i]);
+ }
+
+
+ /* Free all resources */
+ if (cursor) destroy_bitmap(cursor);
+
+#ifdef USE_BACKGROUND
+
+ /* Free the background bitmaps */
+ for (i = 0; i < 17; i++)
+ {
+ if (background[i]) destroy_bitmap(background[i]);
+ }
+
+#endif /* USE_BACKGROUND */
+
+
+#ifdef USE_SOUND
+
+ if (sound_initialized)
+ {
+ /* Destroy samples */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ int j;
+
+ for (j = 0; j < sample_count[i]; j++)
+ {
+ if (samples[i][j]) destroy_sample(samples[i][j]);
+ }
+ }
+ }
+
+ /* Clear the old song */
+ if (midi_song) destroy_midi(midi_song);
+ midi_song = NULL;
+# ifdef USE_MOD_FILES
+ if (mod_file_initialized)
+ {
+ stop_mod();
+ if (mod_song) destroy_mod(mod_song);
+ mod_song = NULL;
+ }
+# endif /* USE_MOD_FILES */
+
+#endif /* USE_SOUND */
+
+ /* Shut down Allegro */
+ allegro_exit();
+}
+
+
+/*
+ * Dump the screen to "lib/user/dump.bmp"
+ */
+static void dos_dump_screen(void)
+{
+ /* Bitmap and palette of the screen */
+ BITMAP *bmp;
+ PALETTE pal;
+
+ /* Filename */
+ char filename[1024];
+
+ /* Get bitmap and palette of the screen */
+ bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H);
+ get_palette(pal);
+
+ /* Build the filename for the screen-dump */
+ path_build(filename, 1024, ANGBAND_DIR_USER, "dump.bmp");
+
+ /* Save it */
+ save_bmp(filename, bmp, pal);
+
+ /* Free up the memory */
+ if (bmp) destroy_bitmap(bmp);
+
+ /* Success message */
+ msg_print("Screen dump saved.");
+ msg_print(NULL);
+}
+
+
+/*
+ * GRX font file reader by Mark Wodrich.
+ *
+ * GRX FNT files consist of the header data (see struct below). If the font
+ * is proportional, followed by a table of widths per character (unsigned
+ * shorts). Then, the data for each character follows. 1 bit/pixel is used,
+ * with each line of the character stored in contiguous bytes. High bit of
+ * first byte is leftmost pixel of line.
+ *
+ * Note that GRX FNT files can have a variable number of characters, so you
+ * must verify that any "necessary" characters exist before using them.
+ *
+ * The GRX FNT files were developed by ???.
+ */
+
+
+/*
+ * Magic value
+ */
+#define FONTMAGIC 0x19590214L
+
+
+/*
+ * Forward declare
+ */
+typedef struct FNTfile_header FNTfile_header;
+
+
+/*
+ * .FNT file header
+ */
+struct FNTfile_header
+{
+ unsigned long magic;
+ unsigned long bmpsize;
+ unsigned short width;
+ unsigned short height;
+ unsigned short minchar;
+ unsigned short maxchar;
+ unsigned short isfixed;
+ unsigned short reserved;
+ unsigned short baseline;
+ unsigned short undwidth;
+ char fname[16];
+ char family[16];
+};
+
+
+/*
+ * A "bitmap" is simply an array of bytes
+ */
+typedef byte *GRX_BITMAP;
+
+
+/*
+ * Temporary space to store font bitmap
+ */
+#define GRX_TMP_SIZE 4096
+
+
+/*
+ * ???
+ */
+void convert_grx_bitmap(int width, int height, GRX_BITMAP src, GRX_BITMAP dest)
+{
+ unsigned short x, y, bytes_per_line;
+ unsigned char bitpos, bitset;
+
+ bytes_per_line = (width + 7) >> 3;
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ bitpos = 7 - (x & 7);
+ bitset = !!(src[(bytes_per_line * y) + (x >> 3)] & (1 << bitpos));
+ dest[y*width + x] = bitset;
+ }
+ }
+}
+
+
+/*
+ * ???
+ */
+GRX_BITMAP *load_grx_bmps(PACKFILE *f, FNTfile_header *hdr,
+ int numchar, unsigned short *wtable)
+{
+ int t, width, bmp_size;
+ GRX_BITMAP temp;
+ GRX_BITMAP *bmp;
+
+ /* alloc array of bitmap pointers */
+ bmp = malloc(sizeof(GRX_BITMAP) * numchar);
+
+ /* assume it's fixed width for now */
+ width = hdr->width;
+
+ /* temporary working area to store FNT bitmap */
+ temp = malloc(GRX_TMP_SIZE);
+
+ for (t = 0; t < numchar; t++)
+ {
+ /* if prop. get character width */
+ if (!hdr->isfixed) width = wtable[t];
+
+ /* work out how many bytes to read */
+ bmp_size = ((width + 7) >> 3) * hdr->height;
+
+ /* oops, out of space! */
+ if (bmp_size > GRX_TMP_SIZE)
+ {
+ free(temp);
+ for (t--; t >= 0; t--) free(bmp[t]);
+ free(bmp);
+ return NULL;
+ }
+
+ /* alloc space for converted bitmap */
+ bmp[t] = malloc(width * hdr->height);
+
+ /* read data */
+ pack_fread(temp, bmp_size, f);
+
+ /* convert to 1 byte/pixel */
+ convert_grx_bitmap(width, hdr->height, temp, bmp[t]);
+ }
+
+ free(temp);
+ return bmp;
+}
+
+
+/*
+ * ???
+ */
+
+FONT* import_grx_font(char *fname)
+ {
+ return NULL;
+ }
+
+//FONT *import_grx_font(char *fname)
+//{
+// PACKFILE *f;
+//
+// /* GRX font header */
+// FNTfile_header hdr;
+//
+// /* number of characters in the font */
+// int numchar;
+//
+// /* table of widths for each character */
+// unsigned short *wtable = NULL;
+//
+// /* array of font bitmaps */
+// GRX_BITMAP *bmp;
+//
+// /* the Allegro font */
+// FONT *font = NULL;
+//
+// FONT_PROP *font_prop;
+// int c, c2, start, width;
+//
+//
+// f = pack_fopen(fname, F_READ);
+//
+// if (!f) return NULL;
+//
+// /* read the header structure */
+// pack_fread(&hdr, sizeof(hdr), f);
+//
+// /* check magic number */
+// if (hdr.magic != FONTMAGIC)
+// {
+// pack_fclose(f);
+// return NULL;
+// }
+//
+// numchar = hdr.maxchar - hdr.minchar + 1;
+//
+// /* proportional font */
+// if (!hdr.isfixed)
+// {
+// wtable = malloc(sizeof(unsigned short) * numchar);
+// pack_fread(wtable, sizeof(unsigned short) * numchar, f);
+// }
+//
+// bmp = load_grx_bmps(f, &hdr, numchar, wtable);
+//
+// if (!bmp) goto get_out;
+//
+// if (pack_ferror(f)) goto get_out;
+//
+// font = malloc(sizeof(FONT));
+// font->height = -1;
+// font->dat.dat_prop = font_prop = malloc(sizeof(FONT_PROP));
+//
+// start = 32 - hdr.minchar;
+// width = hdr.width;
+//
+// for (c = 0; c < FONT_SIZE; c++)
+// {
+// c2 = c + start;
+//
+// if ((c2 >= 0) && (c2 < numchar))
+// {
+// if (!hdr.isfixed) width = wtable[c2];
+//
+// font_prop->dat[c] = create_bitmap_ex(8, width, hdr.height);
+// memcpy(font_prop->dat[c]->dat, bmp[c2], width*hdr.height);
+// }
+// else
+// {
+// font_prop->dat[c] = create_bitmap_ex(8, 8, hdr.height);
+// clear(font_prop->dat[c]);
+// }
+// }
+//
+//get_out:
+//
+// pack_fclose(f);
+//
+// if (wtable) free(wtable);
+//
+// if (bmp)
+// {
+// for (c = 0; c < numchar; c++) free(bmp[c]);
+//
+// free(bmp);
+// }
+//
+// return font;
+//}
+
+
+/*
+ * Initialize the terminal windows
+ */
+static bool init_windows(void)
+{
+ int i, num_windows;
+
+ term_data *td;
+
+ char section[80];
+
+ char filename[1024];
+
+ char buf[128];
+
+ /* Section name */
+ sprintf(section, "Mode-%d", resolution);
+
+ /* Get number of windows */
+ num_windows = get_config_int(section, "num_windows", 1);
+
+ /* Paranoia */
+ if (num_windows > 8) num_windows = 8;
+
+ /* Init the terms */
+ for (i = 0; i < num_windows; i++)
+ {
+ td = &data[i];
+ WIPE(td, term_data);
+
+ /* Section name */
+ sprintf(section, "Term-%d-%d", resolution, i);
+
+ /* Term number */
+ td->number = i;
+
+ /* Coordinates of left top corner */
+ td->x = get_config_int(section, "x", 0);
+ td->y = get_config_int(section, "y", 0);
+
+ /* Rows and cols of term */
+ td->rows = get_config_int(section, "rows", 24);
+ td->cols = get_config_int(section, "cols", 80);
+
+ /* Tile size */
+ td->tile_wid = get_config_int(section, "tile_wid", 8);
+ td->tile_hgt = get_config_int(section, "tile_hgt", 13);
+
+ /* Font size */
+ td->font_wid = get_config_int(section, "tile_wid", 8);
+ td->font_hgt = get_config_int(section, "tile_hgt", 13);
+
+ /* Get font filename */
+ strcpy(buf, get_config_string(section, "font_file", "xm8x13.fnt"));
+
+ /* Build the name of the font file */
+ path_build(filename, 1024, xtra_font_dir, buf);
+
+ /* Load a "*.fnt" file */
+ if (suffix(filename, ".fnt"))
+ {
+ /* Load the font file */
+ if (!(td->font = import_grx_font(filename)))
+ {
+ quit_fmt("Error reading font file '%s'", filename);
+ }
+ }
+
+ /* Load a "*.dat" file */
+ else if (suffix(filename, ".dat"))
+ {
+ DATAFILE *fontdata;
+
+ /* Load the font file */
+ if (!(fontdata = load_datafile(filename)))
+ {
+ quit_fmt("Error reading font file '%s'", filename);
+ }
+
+ /* Save the font data */
+ td->font = fontdata[1].dat;
+
+ /* Unload the font file */
+ unload_datafile_object(fontdata);
+ }
+
+ /* Oops */
+ else
+ {
+ quit_fmt("Unknown suffix in font file '%s'", filename);
+ }
+
+ /* Link the term */
+ term_data_link(td);
+ angband_term[i] = &td->t;
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+#ifdef USE_BACKGROUND
+
+/*
+ * Initialize the window backgrounds
+ */
+static void init_background(void)
+{
+ int i;
+
+ char filename[1024];
+
+ char buf[128];
+
+ PALLETE background_pallete;
+
+ /* Get the backgrounds */
+ for (i = 0; i < 16; i++)
+ {
+ /* Get background filename */
+ strcpy(buf, get_config_string("Background", format("Background-%d", i), ""));
+
+ /* Build the filename for the background-bitmap */
+ path_build(filename, 1024, xtra_graf_dir, buf);
+
+ /* Try to open the bitmap file */
+ background[i] = load_bitmap(filename, background_pallete);
+ }
+
+#ifndef USE_SPECIAL_BACKGROUND
+ /*
+ * Set the palette for the background
+ */
+ if (background[0])
+ {
+ set_palette_range(background_pallete, 0, COLOR_OFFSET - 1, 0);
+ }
+#endif /* USE_SPECIAL_BACKGROUND */
+}
+
+#endif /* USE_BACKGROUND */
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Initialize graphics
+ */
+static bool init_graphics(void)
+{
+ char filename[1024];
+ char section[80];
+ char name_tiles[128];
+
+ /* Large bitmap for the tiles */
+ BITMAP *tiles = NULL;
+ PALLETE tiles_pallete;
+
+ /* Size of each bitmap tile */
+ int bitmap_wid;
+ int bitmap_hgt;
+
+ int num_windows;
+
+ if (!graphics_initialized)
+ {
+ /* Section name */
+ sprintf(section, "Mode-%d", resolution);
+
+ /* Get bitmap tile size */
+ bitmap_wid = get_config_int(section, "bitmap_wid", 8);
+ bitmap_hgt = get_config_int(section, "bitmap_hgt", 8);
+
+ /* Get bitmap filename */
+ strcpy(name_tiles, get_config_string(section, "bitmap_file", "8x8.bmp"));
+
+ /* Get number of windows */
+ num_windows = get_config_int(section, "num_windows", 1);
+
+ /* Build the name of the bitmap file */
+ path_build(filename, 1024, xtra_graf_dir, name_tiles);
+
+ /* Open the bitmap file */
+ if ((tiles = load_bitmap(filename, tiles_pallete)) != NULL)
+ {
+ int i;
+
+ /*
+ * Set the graphics mode to "new" if Adam Bolt's
+ * new 16x16 tiles are used.
+ */
+ ANGBAND_GRAF = get_config_string(section, "graf-mode", "old");
+
+ /* Select the bitmap pallete */
+ set_palette_range(tiles_pallete, 0, COLOR_OFFSET - 1, 0);
+
+ /* Prepare the graphics */
+ for (i = 0; i < num_windows; i++)
+ {
+ term_data *td;
+
+ int col, row;
+ int cols, rows;
+ int width, height;
+ int src_x, src_y;
+ int tgt_x, tgt_y;
+
+ td = &data[i];
+
+ cols = tiles->w / bitmap_wid;
+ rows = tiles->h / bitmap_hgt;
+
+ width = td->tile_wid * cols;
+ height = td->tile_hgt * rows;
+
+ /* Initialize the tile graphics */
+ td->tiles = create_bitmap(width, height);
+
+ for (row = 0; row < rows; ++row)
+ {
+ src_y = row * bitmap_hgt;
+ tgt_y = row * td->tile_hgt;
+
+ for (col = 0; col < cols; ++col)
+ {
+ src_x = col * bitmap_wid;
+ tgt_x = col * td->tile_wid;
+
+ stretch_blit(tiles, td->tiles,
+ src_x, src_y,
+ bitmap_wid, bitmap_hgt,
+ tgt_x, tgt_y,
+ td->tile_wid, td->tile_hgt);
+ }
+ }
+ }
+
+ /* Free the old tiles bitmap */
+ if (tiles) destroy_bitmap(tiles);
+
+ graphics_initialized = TRUE;
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+#endif /* USE_GRAPHICS */
+
+#ifdef USE_SOUND
+
+/*
+ * Initialize sound
+ * We try to get a list of the available sound-files from "lib/xtra/sound/sound.cfg"
+ * and then preload the samples. Every Angband-sound-event can have several samples
+ * assigned. Angband will randomly select which is played. This makes it easy to
+ * create "sound-packs", just copy wav-files into the "lib/xtra/sound/" folder and
+ * add the filenames to "sound.cfg" in the same folder.
+ */
+static bool init_sound(void)
+{
+ int i, j, done;
+
+ char section[128];
+ char filename[1024];
+ char **argv;
+
+ struct ffblk f;
+
+ if (sound_initialized) return (TRUE);
+
+ /* Initialize Allegro sound */
+ if (!install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL))
+ {
+#ifdef USE_MOD_FILES
+ /*
+ * Try to enable support for MOD-, and S3M-files
+ * The parameter for install_mod() is the number
+ * of channels reserved for the MOD/S3M-file.
+ */
+ if (install_mod(16) > 0) mod_file_initialized = TRUE;
+#endif /* USE_MOD_FILES */
+
+ /* Access the new sample */
+ path_build(filename, 1024, xtra_sound_dir, "sound.cfg");
+
+ /* Read config info from "lib/xtra/sound/sound.cfg" */
+ override_config_file(filename);
+
+ /* Sound section */
+ strcpy(section, "Sound");
+
+ /* Prepare the sounds */
+ for (i = 1; i < SOUND_MAX + 1; i++)
+ {
+ /* Get the sample names */
+ argv = get_config_argv(section, angband_sound_name[i], &sample_count[i]);
+
+ /* Limit the number of samples */
+ if (sample_count[i] > SAMPLE_MAX) sample_count[i] = SAMPLE_MAX;
+
+ for (j = 0; j < sample_count[i]; j++)
+ {
+ /* Access the new sample */
+ path_build(filename, 1024, xtra_sound_dir, argv[j]);
+
+ /* Load the sample */
+ samples[i][j] = load_sample(filename);
+ }
+ }
+
+ /*
+ * Get a list of music files
+ */
+#ifdef USE_MOD_FILES
+ if (mod_file_initialized)
+ {
+ done = findfirst(format("%s/*.*", xtra_music_dir), &f, FA_ARCH | FA_RDONLY);
+ }
+ else
+#endif /* USE_MOD_FILES */
+ done = findfirst(format("%s/*.mid", xtra_music_dir), &f, FA_ARCH | FA_RDONLY);
+
+
+ while (!done && (song_number <= MAX_SONGS))
+ {
+ /* Add music files */
+ {
+ strcpy(music_files[song_number], f.ff_name);
+ song_number++;
+ }
+
+ done = findnext(&f);
+ }
+
+ /* Use "angdos.cfg" */
+ override_config_file("angdos.cfg");
+
+ /* Sound section */
+ strcpy(section, "Sound");
+
+ /* Get the volume setting */
+ digi_volume = get_config_int(section, "digi_volume", 255);
+ midi_volume = get_config_int(section, "midi_volume", 255);
+
+ /* Set the volume */
+ set_volume(digi_volume, midi_volume);
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Init failed */
+ return (FALSE);
+}
+
+
+/*
+ * Make a sound
+ */
+static errr Term_xtra_dos_sound(int v)
+{
+ int n;
+
+ /* Sound disabled */
+ if (!use_sound) return (1);
+
+ /* Illegal sound */
+ if ((v < 0) || (v >= SOUND_MAX)) return (1);
+
+ /* Get a random sample from the available ones */
+ n = rand_int(sample_count[v]);
+
+ /* Play the sound, catch errors */
+ if (samples[v][n])
+ {
+ return (play_sample(samples[v][n], 255, 128, 1000, 0) == 0);
+ }
+
+ /* Oops */
+ return (1);
+}
+
+
+/*
+ * Play a song-file
+ */
+static void play_song(void)
+{
+ char filename[256];
+
+ /* Clear the old song */
+ if (midi_song) destroy_midi(midi_song);
+ midi_song = NULL;
+#ifdef USE_MOD_FILES
+ if (mod_file_initialized)
+ {
+ stop_mod();
+ if (mod_song) destroy_mod(mod_song);
+ mod_song = NULL;
+ }
+#endif /* USE_MOD_FILES */
+
+ /* Access the new song */
+ path_build(filename, 1024, xtra_music_dir, music_files[current_song - 1]);
+
+ /* Load and play the new song */
+ if ((midi_song = load_midi(filename))) play_midi(midi_song, 0);
+#ifdef USE_MOD_FILES
+ else if (mod_file_initialized)
+ {
+ if ((mod_song = load_mod(filename))) play_mod(mod_song, FALSE);
+ }
+#endif /* USE_MOD_FILES */
+}
+
+#endif /* USE_SOUND */
+
+
+/*
+ * Attempt to initialize this file
+ *
+ * Hack -- we assume that "blank space" should be "white space"
+ * (and not "black space" which might make more sense).
+ *
+ * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value
+ * into an 8 bit value, without losing much precision, by using the 2
+ * most significant bits as the least significant bits in the new value.
+ *
+ * We should attempt to "share" bitmaps (and fonts) between windows
+ * with the same "tile" size. XXX XXX XXX
+ */
+errr init_dos(void)
+{
+ term_data *td;
+
+ char section[80];
+
+ int screen_wid;
+ int screen_hgt;
+
+ /* Initialize the Allegro library (never fails) */
+ (void)allegro_init();
+
+ /* Install timer support for music and sound */
+ install_timer();
+
+ /* Enable the gif-loading function */
+ register_bitmap_file_type("GIF", load_gif , NULL);
+
+ /* Read config info from filename */
+ set_config_file("angdos.cfg");
+
+ /* Main section */
+ strcpy(section, "Angband");
+
+ /* Get screen size */
+ resolution = get_config_int(section, "Resolution", 1);
+
+ /* Section name */
+ sprintf(section, "Mode-%d", resolution);
+
+ /* Get the screen dimensions */
+ screen_wid = get_config_int(section, "screen_wid", 640);
+ screen_hgt = get_config_int(section, "screen_hgt", 480);
+
+ /* Set the color depth */
+ set_color_depth(8);
+
+ /* Auto-detect, and instantiate, the appropriate graphics mode */
+ if ((set_gfx_mode(GFX_AUTODETECT, screen_wid, screen_hgt, 0, 0)) < 0)
+ {
+ /*
+ * Requested graphics mode is not available
+ * We retry with the basic 640x480 mode
+ */
+ resolution = 1;
+
+ if ((set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0)) < 0)
+ {
+ char error_text[1024];
+
+ /* Save the Allegro error description */
+ strcpy(error_text, allegro_error);
+
+ /* Shut down Allegro */
+ allegro_exit();
+
+ /* Print the error description */
+ plog_fmt("Error selecting screen mode: %s", error_text);
+
+ /* Failure */
+ return ( -1);
+ }
+ }
+
+ /* Hook in "z-util.c" hook */
+ quit_aux = dos_quit_hook;
+
+ /* Build the "graf" path */
+ path_build(xtra_graf_dir, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ /* Build the "font" path */
+ path_build(xtra_font_dir, 1024, ANGBAND_DIR_XTRA, "font");
+
+ /* Build the "sound" path */
+ path_build(xtra_sound_dir, 1024, ANGBAND_DIR_XTRA, "sound");
+
+ /* Build the "music" path */
+ path_build(xtra_music_dir, 1024, ANGBAND_DIR_XTRA, "music");
+
+ /* Initialize the windows */
+ init_windows();
+
+#ifdef USE_SOUND
+
+ /* Look for the sound preferences in "angdos.cfg" */
+ if (!arg_sound)
+ {
+ arg_sound = get_config_int("Angband", "Sound", TRUE);
+ }
+
+#endif /* USE_SOUND */
+
+#ifdef USE_GRAPHICS
+
+ /* Look for the graphic preferences in "angdos.cfg" */
+ if (!arg_graphics)
+ {
+ arg_graphics = get_config_int("Angband", "Graphics", TRUE);
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Initialize the "complex" RNG for the midi-shuffle function */
+ Rand_quick = FALSE;
+ Rand_state_init(time(NULL));
+
+ /* Set the Angband colors/graphics/sound mode */
+ Term_xtra_dos_react();
+
+#ifdef USE_BACKGROUND
+
+ /* Initialize the background graphics */
+ init_background();
+
+#endif /* USE_BACKGROUND */
+
+ /* Clear the screen */
+ clear_to_color(screen, COLOR_OFFSET + TERM_DARK);
+
+ /* Main screen */
+ td = &data[0];
+
+ /* Build a cursor bitmap */
+ cursor = create_bitmap(td->tile_wid, td->tile_hgt);
+
+ /* Erase the cursor sprite */
+ clear(cursor);
+
+ /* Draw the cursor sprite (yellow rectangle) */
+ rect(cursor, 0, 0, td->tile_wid - 1, td->tile_hgt - 1,
+ COLOR_OFFSET + TERM_YELLOW);
+
+ /* Activate the main term */
+ Term_activate(angband_term[0]);
+
+ /* Place the cursor */
+ Term_curs_dos(0, 0);
+
+#ifdef USE_BACKGROUND
+
+ /* Use transparent text */
+ text_mode( -1);
+
+#endif /* USE_BACKGROUND */
+
+ /* Success */
+ return 0;
+}
+
+#endif /* USE_DOS */
diff --git a/src/main-emx.c b/src/main-emx.c
new file mode 100644
index 00000000..6d048e79
--- /dev/null
+++ b/src/main-emx.c
@@ -0,0 +1,1268 @@
+/* File: main-emx.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Ekkehard Kraemer, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: Support for OS/2 EMX Angband */
+
+/* Author: ekraemer@pluto.camelot.de (Ekkehard Kraemer) */
+
+/* Current maintainer: silasd@psyber.com (Silas Dunsmore) */
+/* Unless somebody else wants it.... */
+
+#ifdef USE_EMX
+
+/*
+ * === Instructions for using Angband 2.7.X with OS/2 ===
+ *
+ * The patches (file "main-emx.c") to compile Angband 2.7.X under OS/2
+ * were written by ekraemer@pluto.camelot.de (Ekkehard Kraemer).
+ *
+ * TO COMPILE:
+ *
+ * - untar the archive into /angband (or /games/angband or whatever)
+ * - change directory to /angband/src
+ * - run "dmake -B -r -f makefile.emx" (not gmake or make)
+ *
+ * TO INSTALL:
+ *
+ * - change directory to /angband/src
+ * - run "dmake -B -r -f makefile.emx install" (not gmake or make)
+ * - copy your old savefile into ./lib/save and your old pref.prf into ./lib/user
+ * - start /angband/angband.exe for one single window
+ * - start /angband/startwnd.cmd for multiple windows
+ *
+ * TO REMOVE TEMPORARY FILES:
+ *
+ * - run 'dmake -B -r -f makefile.emx clean'
+ *
+ *
+ * I use EMX 0.9b, but every EMX compiler since 0.8g or so should work
+ * fine. EMX is available at ftp-os2.cdrom.com ("Hobbes"), as is dmake.
+ *
+ * dmake: ftp://ftp-os2.cdrom.com/all/program/dmake38X.zip
+ * EMX: ftp://ftp-os2.cdrom.com/2_x/unix/emx???/ (most probably)
+ *
+ * Old savefiles must be renamed to follow the new "savefile" naming
+ * conventions. Either rename the savefile to "PLAYER", or start the
+ * program with the "-uName" flag. See "main.c" for details. The
+ * savefiles are stores in "./lib/save" if you forgot the old names...
+ *
+ * Changes
+ * =======
+ *
+ * When By Version What
+ * -------------------------------------------------------------------
+ *
+ * 18.11.95 EK 2.7.8 Added window/pipe code
+ * Introduced __EMX__CLIENT__ hack
+ *
+ * 15.12.95 EK 2.7.9 Updated for 2.7.9
+ * beta Added third view
+ * Added number of line support in aclient
+ *
+ * 25.12.95 EK 2.7.9 Added 'install' target
+ * non-beta Updated installation hints
+ * Uploaded binary to export.andrew.cmu.edu
+ *
+ * 25.01.96 EK 2.7.9 Updated for 2.7.9v3
+ * v3 Removed (improved) keyboard hack
+ * Introduced pref-emx.prf
+ * Phew... Makefile.emx grows! (patches, export)
+ * Uploaded binary to export.andrew.cmu.edu
+ *
+ * 26.01.96 EK Added files.uue target
+ *
+ * 22.02.96 EK Added PM support
+ *
+ * 2.03.96 EK 2.7.9 Uploaded binaries to export.andrew.cmu.edu
+ * v4
+ *
+ * 9.03.96 EK 2.7.9 Adjustable background color (PM)
+ * v5 Added map window
+
+ * 3 Dec 97 SWD 282 Brought key-handling, macros in sync with DOS.
+ * Hacked on sub-window code -- it compiles, but
+ * doesn't link.
+ *
+ * 23 Jan 98 SWD 282 Hacked more on sub-windows. Now links, with
+ * warnings. Seems to work.
+ *
+ * 01 Nov 98 AGA 2.8.3 Adjusted for 2.8.3 sources, typos corrected
+ *
+ * 01 Nov 98 AGA Z214b Corrections for ZAngband 2.1.4 beta
+ *
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/kbdscan.h>
+#include <io.h>
+#define INCL_KBD 1
+#include <os2.h>
+#include <sys/video.h>
+
+#include "angband.h"
+
+
+/*
+ * Maximum windows
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * Keypress input modifier flags (copied from main-ibm.c)
+ *
+ * SWD: these could be changed to the definitions in <os2.h>, which are
+ * direct bitmasks instead of shift-counts.
+ */
+#define K_RSHIFT 0 /* Right shift key down */
+#define K_LSHIFT 1 /* Left shift key down */
+#define K_CTRL 2 /* Ctrl key down */
+#define K_ALT 3 /* Alt key down */
+#define K_SCROLL 4 /* Scroll lock on */
+#define K_NUM 5 /* Num lock on */
+#define K_CAPS 6 /* Caps lock on */
+#define K_INSERT 7 /* Insert on */
+
+
+/*
+ * Prototypes!
+ */
+static errr Term_curs_emx(int x, int y);
+static errr Term_wipe_emx(int x, int y, int n);
+static errr Term_text_emx(int x, int y, int n, unsigned char a, cptr s);
+static void Term_init_emx(term *t);
+static void Term_nuke_emx(term *t);
+
+#ifndef EMXPM
+
+/*
+ * termPipe* is sometimes cast to term* and vice versa,
+ * so "term t;" must be the first line
+ */
+
+typedef struct
+{
+ term t;
+ FILE *out; /* used by the ..._pipe_emx stuff */
+}
+termPipe;
+
+/*
+ * Communication between server and client
+ */
+
+enum
+{
+ PIP_INIT,
+ PIP_NUKE,
+ PIP_XTRA,
+ PIP_CURS,
+ PIP_WIPE,
+ PIP_TEXT,
+};
+
+
+/*
+ * Current cursor "size"
+ */
+static int curs_start = 0;
+static int curs_end = 0;
+
+/*
+ * Angband color conversion table
+ *
+ * Note that "Light Green"/"Yellow"/"Red" are used for
+ * "good"/"fair"/"awful" stats and conditions.
+ *
+ * But "brown"/"light brown"/"orange" are really only used
+ * for "objects" (such as doors and spellbooks), and these
+ * values can in fact be re-defined.
+ *
+ * Future versions of Angband will probably allow the
+ * "use" of more that 16 colors, so "extra" entries can be
+ * made at the end of this table in preparation.
+ */
+static int colors[16] =
+{
+ F_BLACK, /* Black */
+ F_WHITE | INTENSITY, /* White */
+ F_WHITE, /* XXX Gray */
+ F_RED | INTENSITY, /* Orange */
+ F_RED, /* Red */
+ F_GREEN, /* Green */
+ F_BLUE, /* Blue */
+ F_BROWN, /* Brown */
+ F_BLACK | INTENSITY, /* Dark-grey */
+ F_WHITE, /* XXX Light gray */
+ F_MAGENTA, /* Purple */
+ F_YELLOW | INTENSITY, /* Yellow */
+ F_RED | INTENSITY, /* Light Red */
+ F_GREEN | INTENSITY, /* Light Green */
+ F_BLUE | INTENSITY, /* Light Blue */
+ F_BROWN | INTENSITY /* Light brown */
+};
+
+/*
+ * Display a cursor, on top of a given attr/char
+ */
+static errr Term_curs_emx(int x, int y)
+{
+ v_gotoxy(x, y);
+ v_ctype(curs_start, curs_end);
+
+ return (0);
+}
+
+/*
+ * Erase a grid of space (as if spaces were printed)
+ */
+static errr Term_wipe_emx(int x, int y, int n)
+{
+ v_gotoxy(x, y);
+ v_putn(' ', n);
+
+ return (0);
+}
+
+/*
+ * Draw some text, wiping behind it first
+ */
+static errr Term_text_emx(int x, int y, int n, unsigned char a, cptr s)
+{
+ /* Convert the color and put the text */
+ v_attrib(colors[a & 0x0F]);
+ v_gotoxy(x, y);
+ v_putm(s, n);
+
+ return (0);
+}
+
+/*
+ * EMX initialization
+ */
+static void Term_init_emx(term *t)
+{
+ struct _KBDINFO kbdinfo; /* see structure description ?somewhere? */
+
+ v_init();
+ v_getctype(&curs_start, &curs_end);
+ /* hide cursor (?) XXX XXX XXX */
+ v_clear();
+
+ /* the documentation I (SWD) have implies, in passing, that setting */
+ /* "binary mode" on the keyboard device will prevent the O/S from */
+ /* acting on keys such as ^S (pause) and ^P (printer echo). */
+
+ /* note also that "KbdSetStatus is ignored for a Vio-windowed application." */
+ /* so there may well be problems with running this in a window. Damnit. */
+
+ /* this is kind of a nasty structure, as you can't just flip a bit */
+ /* to change binary/ASCII mode, or echo on/off mode... nor can you */
+ /* clear the whole thing -- certain bits need to be preserved. */
+
+ KbdGetStatus(&kbdinfo, (HKBD)0);
+ kbdinfo.fsMask &= ~ (KEYBOARD_ECHO_ON | /* clear lowest four bits */
+ KEYBOARD_ECHO_OFF | KEYBOARD_BINARY_MODE | KEYBOARD_ASCII_MODE);
+ kbdinfo.fsMask |= (KEYBOARD_BINARY_MODE); /* set bit two */
+ KbdSetStatus(&kbdinfo, (HKBD)0);
+
+#if 1 /* turn off for debug */
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ /* signal(SIGILL,SIG_IGN); */
+ /* signal(SIGTRAP,SIG_IGN); */
+ /* signal(SIGABRT,SIG_IGN); */
+ /* signal(SIGEMT,SIG_IGN); */
+ /* signal(SIGFPE,SIG_IGN); */
+ /* signal(SIGBUS,SIG_IGN); */
+ /* signal(SIGSEGV,SIG_IGN); */
+ /* signal(SIGSYS,SIG_IGN); */
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+ /* signal(SIGTERM,SIG_IGN); */
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGUSR2, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGBREAK, SIG_IGN);
+#endif
+
+}
+
+/*
+ * EMX shutdown
+ */
+static void Term_nuke_emx(term *t)
+{
+ /* Move the cursor to bottom of screen */
+ v_gotoxy(0, 23);
+
+ /* Restore the cursor (not necessary) */
+ v_ctype(curs_start, curs_end);
+
+ /* Set attribute to gray on black */
+ v_attrib(F_WHITE);
+
+ /* Clear the screen */
+ v_clear();
+}
+
+
+#ifndef __EMX__CLIENT__
+
+/*
+ * Oh no, more prototypes!
+ */
+static errr CheckEvents(int returnImmediately);
+static errr Term_xtra_pipe_emx(int n, int v);
+static errr Term_curs_pipe_emx(int x, int y);
+static errr Term_wipe_pipe_emx(int x, int y, int n);
+static errr Term_text_pipe_emx(int x, int y, int n, unsigned char a, cptr s);
+static void Term_init_pipe_emx(term *t);
+static void Term_nuke_pipe_emx(term *t);
+static FILE *initPipe(const char *name);
+static void initPipeTerm(termPipe *pipe, const char *name, term **term);
+
+/*
+ * Main initialization function
+ */
+errr init_emx(void);
+
+/*
+ * The screens
+ */
+static termPipe term_screen_aga[MAX_TERM_DATA];
+
+
+/*
+ * Check for events -- called by "Term_scan_emx()"
+ *
+ * Note -- this is probably NOT the most efficient way
+ * to "wait" for a keypress (TERM_XTRA_EVENT).
+ *
+ *
+ * This code was ripped from "main-ibm.c" -- consult it to
+ * figure out what's going on.
+ *
+ * See "main-ibm.c" for more information about "macro encoding".
+ *
+ *
+ * The following documentation was cut&pasted from
+ * the OS/2 Programming Reference, PRCP.INF
+ * <ftp://hobbes.nmsu.edu/pub/os2/dev/16-bit/inf16bit.zip>
+-------------------------------------------------------------------------------
+
+
+ This call returns a character data record from the keyboard.
+
+ KbdCharIn (CharData, IOWait, KbdHandle)
+
+ CharData (PKBDKEYINFO) - output
+ Address of the character data structure:
+
+ asciicharcode (UCHAR)
+ ASCII character code. The scan code received from the keyboard is
+ translated to the ASCII character code.
+
+ scancode (UCHAR)
+ Code received from the keyboard. The scan code received from the
+ keyboard is translated to the ASCII character code.
+
+ status (UCHAR)
+ State of the keystroke event:
+
+ Bit Description
+
+ 7-6 00 = Undefined
+
+ 01 = Final character, interim character flag off
+
+ 10 = Interim character
+
+ 11 = Final character, interim character flag on.
+
+ 5 1 = Immediate conversion requested.
+
+ 4-2 Reserved.
+
+ 1 0 = Scan code is a character.
+
+ 1 = Scan code is not a character; is an extended key code
+ from the keyboard.
+
+ 0 1 = Shift status returned without character.
+
+ reserved (UCHAR)
+ NLS shift status. Reserved, set to zero.
+
+ shiftkeystat (USHORT)
+ Shift key status.
+
+ Bit Description
+ 15 SysReq key down
+ 14 CapsLock key down
+ 13 NumLock key down
+ 12 ScrollLock key down
+ 11 Right Alt key down
+ 10 Right Ctrl key down
+ 9 Left Alt key down
+ 8 Left Ctrl key down
+ 7 Insert on
+ 6 CapsLock on
+ 5 NumLock on
+ 4 ScrollLock on
+ 3 Either Alt key down
+ 2 Either Ctrl key down
+ 1 Left Shift key down
+ 0 Right Shift key down
+
+ time (ULONG)
+ Time stamp indicating when a key was pressed. It is specified in
+ milliseconds from the time the system was started.
+
+ IOWait (USHORT) - input
+ Wait if a character is not available.
+
+ Value Definition
+ 0 Requestor waits for a character if one is not available.
+ 1 Requestor gets an immediate return if no character is
+ available.
+
+ KbdHandle (HKBD) - input
+ Default keyboard or the logical keyboard.
+
+ rc (USHORT) - return
+ Return code descriptions are:
+
+ 0 NO_ERROR
+ 375 ERROR_KBD_INVALID_IOWAIT
+ 439 ERROR_KBD_INVALID_HANDLE
+ 445 ERROR_KBD_FOCUS_REQUIRED
+ 447 ERROR_KBD_KEYBOARD_BUSY
+ 464 ERROR_KBD_DETACHED
+ 504 ERROR_KBD_EXTENDED_SG
+
+ Remarks
+
+ On an enhanced keyboard, the secondary enter key returns the normal
+ character 0DH and a scan code of E0H.
+
+ Double-byte character codes (DBCS) require two function calls to obtain
+ the entire code.
+
+ If shift report is set with KbdSetStatus, the CharData record returned
+ reflects changed shift information only.
+
+ Extended ASCII codes are identified with the status byte, bit 1 on and the
+ ASCII character code being either 00H or E0H. Both conditions must be
+ satisfied for the character to be an extended keystroke. For extended
+ ASCII codes, the scan code byte returned is the second code (extended
+ code). Usually the extended ASCII code is the scan code of the primary
+ key that was pressed.
+
+ A thread in the foreground session that repeatedly polls the keyboard
+ with KbdCharIn (with no wait), can prevent all regular priority class
+ threads from executing. If polling must be used and a minimal amount of
+ other processing is being performed, the thread should periodically yield to
+ the CPU by issuing a DosSleep call for an interval of at least 5
+ milliseconds.
+
+
+ Family API Considerations
+
+ Some options operate differently in the DOS mode than in the OS /2 mode.
+ Therefore, the following restrictions apply to KbdCharIn when coding in
+ the DOS mode:
+
+ o The CharData structure includes everything except the time stamp.
+ o Interim character is not supported
+ o Status can be 0 or 40H
+ o KbdHandle is ignored.
+
+
+-------------------------------------------------------------------------------
+
+
+ typedef struct _KBDKEYINFO { / * kbci * /
+ UCHAR chChar; / * ASCII character code * /
+ UCHAR chScan; / * Scan Code * /
+ UCHAR fbStatus; / * State of the character * /
+ UCHAR bNlsShift; / * Reserved (set to zero) * /
+ USHORT fsState; / * State of the shift keys * /
+ ULONG time; / * Time stamp of keystroke (ms since ipl) * /
+ }KBDKEYINFO;
+
+ #define INCL_KBD
+
+ USHORT rc = KbdCharIn(CharData, IOWait, KbdHandle);
+
+ PKBDKEYINFO CharData; / * Buffer for data * /
+ USHORT IOWait; / * Indicate if wait * /
+ HKBD KbdHandle; / * Keyboard handle * /
+
+ USHORT rc; / * return code * /
+
+
+-------------------------------------------------------------------------------
+ *
+ */
+static errr CheckEvents(int returnImmediately)
+{
+ int i, k, s;
+
+ bool mc = FALSE;
+ bool ms = FALSE;
+ bool ma = FALSE;
+
+ /* start OS/2 specific section */
+
+ struct _KBDKEYINFO keyinfo; /* see structure description above */
+
+ /* Check (and possibly wait) for a keypress */
+ /* see function description above */
+ KbdCharIn( &keyinfo, returnImmediately, (HKBD)0 );
+
+#if 0
+ printf("AC:%x SC:%x ST:%x R1:%x SH:%x TI:%ld\n", /* OS/2 debug */
+ keyinfo.chChar,
+ keyinfo.chScan,
+ keyinfo.fbStatus,
+ keyinfo.bNlsShift,
+ keyinfo.fsState,
+ keyinfo.time );
+#endif
+
+ /* If there wasn't a key, leave now. */
+ if ((keyinfo.fbStatus & 0xC0) == 0) return (1);
+
+
+ /* by a set of lucky coincidences, the data maps directly over. */
+ k = keyinfo.chChar;
+ s = keyinfo.chScan;
+ i = (keyinfo.fsState & 0xFF);
+
+ /* end OS/2 specific section */
+
+
+ /* Process "normal" keys */
+ if ( k != 0 && ((s <= 58) || (s == 0xE0)) ) /* Tweak: allow for ALT-keys */
+ {
+ /* Enqueue it */
+ Term_keypress(k);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Extract the modifier flags */
+ if (i & (1 << K_CTRL)) mc = TRUE;
+ if (i & (1 << K_LSHIFT)) ms = TRUE;
+ if (i & (1 << K_RSHIFT)) ms = TRUE;
+ if (i & (1 << K_ALT)) ma = TRUE;
+
+
+ /* Begin a "macro trigger" */
+ Term_keypress(31);
+
+ /* Hack -- Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Introduce the hexidecimal scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[s / 16]);
+ Term_keypress(hexsym[s % 16]);
+
+ /* End the "macro trigger" */
+ Term_keypress(13);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Do a special thing (beep, flush, etc)
+ */
+static errr Term_xtra_emx(int n, int v)
+{
+ switch (n)
+ {
+ case TERM_XTRA_SHAPE:
+ if (v)
+ {
+ v_ctype(curs_start, curs_end);
+ }
+ else
+ {
+ v_hidecursor();
+ }
+ return (0);
+
+ case TERM_XTRA_NOISE:
+ DosBeep(440, 50);
+ return (0);
+
+ case TERM_XTRA_FLUSH:
+ while (!CheckEvents(TRUE));
+ return 0;
+
+ case TERM_XTRA_EVENT:
+
+ /* Process an event */
+ return (CheckEvents(!v));
+
+ /* Success */
+ return (0);
+
+ case TERM_XTRA_CLEAR:
+ v_clear();
+ return (0);
+ }
+
+ return (1);
+}
+
+static errr Term_xtra_pipe_emx(int n, int v)
+{
+ termPipe *tp = (termPipe*)Term;
+
+ switch (n)
+ {
+ case TERM_XTRA_NOISE:
+ DosBeep(440, 50);
+ return (0);
+
+ case TERM_XTRA_SHAPE:
+ return (0);
+
+ case TERM_XTRA_EVENT:
+ return (CheckEvents(FALSE));
+
+ case TERM_XTRA_CLEAR:
+
+ if (!tp->out) return -1;
+
+ fputc(PIP_XTRA, tp->out);
+ fwrite(&n, sizeof(n), 1, tp->out);
+ fwrite(&v, sizeof(v), 1, tp->out);
+ fflush(tp->out);
+
+ return (0);
+ }
+
+ return (1);
+}
+
+static errr Term_curs_pipe_emx(int x, int y)
+{
+ termPipe *tp = (termPipe*)Term;
+
+ if (!tp->out) return -1;
+
+ fputc(PIP_CURS, tp->out);
+ fwrite(&x, sizeof(x), 1, tp->out);
+ fwrite(&y, sizeof(y), 1, tp->out);
+ fflush(tp->out);
+
+ return (0);
+}
+
+
+static errr Term_wipe_pipe_emx(int x, int y, int n)
+{
+ termPipe *tp = (termPipe*)Term;
+
+ if (!tp->out) return -1;
+
+ fputc(PIP_WIPE, tp->out);
+ fwrite(&x, sizeof(x), 1, tp->out);
+ fwrite(&y, sizeof(y), 1, tp->out);
+ fwrite(&n, sizeof(n), 1, tp->out);
+ fflush(tp->out);
+
+ return (0);
+}
+
+
+static errr Term_text_pipe_emx(int x, int y, int n, unsigned char a, cptr s)
+{
+ termPipe *tp = (termPipe*)Term;
+
+ if (!tp->out) return -1;
+
+ fputc(PIP_TEXT, tp->out);
+ fwrite(&x, sizeof(x), 1, tp->out);
+ fwrite(&y, sizeof(y), 1, tp->out);
+ fwrite(&n, sizeof(n), 1, tp->out);
+ fwrite(&a, sizeof(a), 1, tp->out);
+ fwrite(s, n, 1, tp->out);
+ fflush(tp->out);
+
+ return (0);
+}
+
+
+static void Term_init_pipe_emx(term *t)
+{
+ termPipe *tp = (termPipe*)t;
+
+ if (tp->out)
+ {
+ fputc(PIP_INIT, tp->out);
+ fflush(tp->out);
+ }
+}
+
+
+static void Term_nuke_pipe_emx(term *t)
+{
+ termPipe *tp = (termPipe*)t;
+
+ if (tp->out)
+ {
+ fputc(PIP_NUKE, tp->out); /* Terminate client */
+ fflush(tp->out);
+ fclose(tp->out); /* Close Pipe */
+ tp->out = NULL; /* Paranoia */
+ }
+}
+
+static void initPipeTerm(termPipe *pipe, const char *name, term **termTarget)
+{
+ term *t;
+
+ t = &pipe->t;
+
+ if ((pipe->out = initPipe(name)) != NULL)
+ {
+ /* Initialize the term */
+ term_init(t, 80, 24, 1);
+
+ /* Special hooks */
+ t->init_hook = Term_init_pipe_emx;
+ t->nuke_hook = Term_nuke_pipe_emx;
+
+ /* Add the hooks */
+ t->text_hook = Term_text_pipe_emx;
+ t->wipe_hook = Term_wipe_pipe_emx;
+ t->curs_hook = Term_curs_pipe_emx;
+ t->xtra_hook = Term_xtra_pipe_emx;
+
+ /* Save it */
+ *termTarget = t;
+
+ /* Activate it */
+ Term_activate(t);
+ }
+}
+
+/*
+ * Prepare "term.c" to use "USE_EMX" built-in video library
+ */
+errr init_emx(void)
+{
+ int i;
+
+ term *t;
+
+ /* Initialize the pipe windows */
+ for (i = MAX_TERM_DATA - 1; i > 0; --i)
+ {
+ const char *name = angband_term_name[i];
+ initPipeTerm(&term_screen_aga[i], name, &angband_term[i]);
+ }
+
+ /* Initialize main window */
+ t = &term_screen_aga[0].t;
+
+ /* Initialize the term -- big key buffer */
+ term_init(t, 80, 24, 1024);
+
+ /* Special hooks */
+ t->init_hook = Term_init_emx;
+ t->nuke_hook = Term_nuke_emx;
+
+ /* Add the hooks */
+ t->text_hook = Term_text_emx;
+ t->wipe_hook = Term_wipe_emx;
+ t->curs_hook = Term_curs_emx;
+ t->xtra_hook = Term_xtra_emx;
+
+ /* Save it */
+ angband_term[0] = t;
+
+ /* Activate it */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+static FILE *initPipe(const char *name)
+{
+ char buf[256];
+ FILE *fi;
+
+ sprintf(buf, "\\pipe\\angband\\%s", name); /* Name of pipe */
+ fi = fopen(buf, "wb"); /* Look for server */
+ return fi;
+}
+
+#else /* __EMX__CLIENT__ */
+
+int main(int argc, char **argv)
+{
+ int c, end = 0, lines = 25;
+ int x, y, h, n, v;
+
+ FILE *in = NULL;
+ char a;
+ char buf[160];
+ HPIPE pipe;
+ APIRET rc;
+ char *target;
+
+ /* Check command line */
+ if (argc != 2 && argc != 3)
+ {
+ printf("Usage: %s Mirror|Recall|Choice|Term-4|...|Term-7 [number of lines]\n"
+ "Start this before angband.exe\n", argv[0]);
+ exit(1);
+ }
+
+ if (argc == 3) lines = atoi(argv[2]);
+ if (lines <= 0) lines = 25;
+
+ printf("Looking for Angband... press ^C to abort\n");
+
+ target = strdup(argv[1]);
+ for (c = 0; c < strlen(target); c++) target[c] = tolower(target[c]);
+
+ sprintf(buf, "\\pipe\\angband\\%s", target);
+
+ do
+ {
+ rc = DosCreateNPipe((PSZ)buf, /* Create pipe */
+ &pipe,
+ NP_ACCESS_INBOUND,
+ NP_WAIT | NP_TYPE_BYTE | NP_READMODE_BYTE | 1,
+ 1, /* No output buffer */
+ 1, /* No input buffer */
+ -1);
+
+ if (rc) /* Pipe not created */
+ {
+ printf("DosCreateNPipe: rc=%ld, pipe=%ld\n", (long)rc, (long)pipe);
+ break;
+ }
+
+ do
+ {
+ rc = DosConnectNPipe(pipe); /* Wait for angband to connect */
+ if (!rc) break;
+ _sleep2(500); /* Sleep for 0.5s */
+ }
+ while (_read_kbd(0, 0, 0) == -1); /* Until key pressed */
+
+ if (rc) break;
+
+ h = _imphandle(pipe); /* Register handle with io */
+ setmode(h, O_BINARY); /* Make it binary */
+ in = fdopen(h, "rb"); /* Register handle with stdio */
+
+ }
+ while (0); /* We don't need no stinking exception handling <g> */
+
+ if (!in)
+ {
+ printf("Sorry, the pipe connection to Angband could not be established.\n");
+ exit(1);
+ }
+
+ printf("Connected.\n");
+
+ sprintf(buf, "mode co80,%d", lines);
+ system(buf);
+
+ /* Infinite loop */
+ while (!end)
+ {
+ /* Get command */
+ c = fgetc(in);
+
+ switch (c)
+ {
+ case PIP_XTRA:
+ if (!fread(&n, sizeof(x), 1, in) ||
+ !fread(&v, sizeof(y), 1, in))
+ abort();
+
+ /* This hack prevents another hack */
+ switch (n)
+ {
+ case TERM_XTRA_CLEAR:
+ v_clear();
+ break;
+
+ default:
+ printf("Sorry, angband.exe and aclient.exe don't fit together.\n");
+ exit(1);
+ }
+
+ break;
+
+ case PIP_CURS:
+ if (!fread(&x, sizeof(x), 1, in) ||
+ !fread(&y, sizeof(y), 1, in))
+ abort();
+ Term_curs_emx(x, y);
+ break;
+
+ case PIP_WIPE:
+ if (!fread(&x, sizeof(x), 1, in) ||
+ !fread(&y, sizeof(y), 1, in) ||
+ !fread(&n, sizeof(n), 1, in))
+ abort();
+ Term_wipe_emx(x, y, n);
+ break;
+
+ case PIP_TEXT:
+ if (!fread(&x, sizeof(x), 1, in) ||
+ !fread(&y, sizeof(y), 1, in) ||
+ !fread(&n, sizeof(n), 1, in) ||
+ !fread(&a, sizeof(a), 1, in) || (n > 160) ||
+ !fread(buf, n, 1, in))
+ abort();
+ Term_text_emx(x, y, n, a, buf);
+ break;
+
+ case PIP_INIT:
+ Term_init_emx(NULL);
+ break;
+
+ case PIP_NUKE:
+ case EOF:
+ default:
+ Term_nuke_emx(NULL);
+ end = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif /* __EMX__CLIENT__ */
+
+#else /* EMXPM */
+
+void emx_endPM(const char *reason);
+int emx_options(char **ANGBAND_DIR_USER,
+ char **ANGBAND_DIR_SAVE,
+ char **ANGBAND_DIR_INFO,
+ char *arg_force_roguelike,
+ char *arg_force_original,
+ char *arg_fiddle,
+ char *arg_wizard,
+ char player_name[32]);
+
+void emx_init_window(void **instance, void *main_instance, int n);
+
+errr emx_curs(void *instance, int x, int y);
+errr emx_wipe(void *instance, int x, int y, int n);
+errr emx_text(void *instance, int x, int y, int n, unsigned char a, cptr s);
+void emx_init(void *instance);
+void emx_nuke(void *instance);
+int emx_read_kbd(void *instance, int wait);
+void emx_clear(void *instance);
+void emx_hidecursor(void *instance);
+void emx_showcursor(void *instance);
+
+/*
+* termWindow* is sometimes cast to term* and vice versa,
+* so "term t;" must be the first line
+*/
+
+typedef struct
+{
+ term t;
+ void *instance; /* Pointer to window */
+}
+termWindow;
+
+/*
+* Display a cursor, on top of a given attr/char
+*/
+static errr Term_curs_emx(int x, int y)
+{
+ return emx_curs(((termWindow*)Term)->instance, x, y);
+}
+
+/*
+* Erase a grid of space (as if spaces were printed)
+*/
+static errr Term_wipe_emx(int x, int y, int n)
+{
+ return emx_wipe(((termWindow*)Term)->instance, x, y, n);
+}
+
+/*
+* Draw some text, wiping behind it first
+*/
+static errr Term_text_emx(int x, int y, int n, unsigned char a, cptr s)
+{
+ return emx_text(((termWindow*)Term)->instance, x, y, n, a, s);
+}
+
+/*
+* EMX initialization
+*/
+static void Term_init_emx(term *t)
+{
+ return emx_init(((termWindow*)t)->instance);
+}
+
+/*
+* EMX shutdown
+*/
+static void Term_nuke_emx(term *t)
+{}
+
+/*
+* Oh no, more prototypes!
+*/
+static errr CheckEvents(int returnImmediately);
+
+/*
+* Main initialization function
+*/
+errr init_emx(void);
+
+/*
+* The screens
+*/
+static termWindow term_screen_aga[MAX_TERM_DATA];
+
+/*
+* Check for events -- called by "Term_scan_emx()"
+*/
+static errr CheckEvents(int returnImmediately)
+{
+ /* Get key - Macro triggers are generated by emx_read_kbd() */
+ int k = emx_read_kbd(((termWindow*)Term)->instance, returnImmediately ? 0 : 1);
+
+ /* Nothing ready */
+ if (k < 0) return (1);
+
+ /* Enqueue the key */
+ Term_keypress(k);
+
+ /* Success */
+ return (0);
+}
+
+/*
+* Do a special thing (beep, flush, etc)
+*/
+static errr Term_xtra_emx(int n, int v)
+{
+ void *instance = ((termWindow*)Term)->instance;
+
+ switch (n)
+ {
+ case TERM_XTRA_SHAPE:
+ if (v)
+ {
+ emx_showcursor(instance);
+ }
+ else
+ {
+ emx_hidecursor(instance);
+ }
+ return (0);
+
+ case TERM_XTRA_NOISE:
+ DosBeep(440, 50);
+ return (0);
+
+ case TERM_XTRA_FLUSH:
+ while (!CheckEvents(TRUE));
+ return 0;
+
+ case TERM_XTRA_EVENT:
+ return (CheckEvents(!v));
+
+ case TERM_XTRA_CLEAR:
+ emx_clear(instance);
+ return (0);
+
+ case TERM_XTRA_DELAY:
+ if (v > 0) _sleep2(v);
+ return (0);
+ }
+
+ return (1);
+}
+
+void emx_init_term(termWindow *t, void *main_instance, term **angTerm, int n)
+{
+ term *te = (term*)t;
+
+ /* Initialize window */
+ emx_init_window(&t->instance, main_instance, n);
+
+ *angTerm = te;
+
+ /* Initialize the term -- big key buffer */
+ term_init(te, 80, 24, 1024);
+
+ /* Special hooks */
+ te->init_hook = Term_init_emx;
+ te->nuke_hook = Term_nuke_emx;
+
+ /* Add the hooks */
+ te->text_hook = Term_text_emx;
+ te->wipe_hook = Term_wipe_emx;
+ te->curs_hook = Term_curs_emx;
+ te->xtra_hook = Term_xtra_emx;
+}
+
+/*
+* Prepare "term.c" to use "USE_EMX" built-in faked video library
+*/
+errr init_emx(void)
+{
+ int i;
+
+ /* Initialize the windows */
+ emx_init_term(&term_screen_aga[0], NULL, &angband_term[0], 0);
+
+ for (i = 1; i < MAX_TERM_DATA; ++i)
+ {
+ emx_init_term(&term_screen_aga[i], term_screen_aga[0].instance, &angband_term[i], i);
+ }
+
+ /* Activate main window */
+ Term_activate(angband_term[0]);
+
+ /* Success */
+ return (0);
+}
+
+static void init_stuff(void)
+{
+ char path[1024];
+ cptr tail;
+
+ /* Get the environment variable */
+ tail = getenv("ANGBAND_PATH");
+
+ /* Use the angband_path, or a default */
+ strcpy(path, tail ? tail : DEFAULT_PATH);
+
+ /* Hack -- Add a path separator (only if needed) */
+ if (!suffix(path, PATH_SEP)) strcat(path, PATH_SEP);
+
+ /* Initialize */
+ init_file_paths(path);
+}
+
+static void quit_hook(cptr s)
+{
+ int i;
+
+ for (i = MAX_TERM_DATA - 1; i >= 0; --i)
+ {
+ /* Shut down the term windows */
+ if (angband_term[i])
+ {
+ term_nuke(angband_term[i]);
+ emx_nuke(((termWindow*)angband_term[i])->instance);
+ }
+ }
+
+ /* Shut down window system - doesn't return */
+ emx_endPM(s);
+}
+
+
+void angbandThread(void *arg)
+{
+ bool new_game = FALSE;
+
+ int show_score = 0;
+
+ char player_name_aga[32];
+
+ /* Save the "program name" */
+ argv0 = (char*)arg;
+
+ /* Use the "main-emx.c" support */
+ init_emx();
+ ANGBAND_SYS = "ibm";
+
+ /* Get the file paths */
+ init_stuff();
+
+ if (!emx_options((char**)&ANGBAND_DIR_USER,
+ (char**)&ANGBAND_DIR_SAVE,
+ (char**)&ANGBAND_DIR_INFO,
+ &arg_force_roguelike,
+ &arg_force_original,
+ &arg_fiddle,
+ &arg_wizard,
+ player_name_aga)) quit(NULL);
+
+ /* XXX XXX XXX (?) */
+ strcpy(player_name, player_name_aga);
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ /* Tell "quit()" to call "Term_nuke()" */
+ quit_aux = quit_hook;
+
+ /* If requested, display scores and quit */
+ if (show_score > 0) display_scores(0, show_score);
+
+ /* Catch nasty signals */
+ signals_init();
+
+ /* Initialize */
+ init_angband();
+
+ /* Wait for response */
+ pause_line(23);
+
+ /* Play the game */
+ play_game(new_game);
+
+ /* Quit */
+ quit(NULL);
+}
+
+#endif /* EMXPM */
+
+#endif /* USE_EMX */
+
+/*
+ * Local Variables:
+ * comment-column: 45
+ * End:
+ *
+ */
+
diff --git a/src/main-gcu.c b/src/main-gcu.c
new file mode 100644
index 00000000..365fe4d7
--- /dev/null
+++ b/src/main-gcu.c
@@ -0,0 +1,1261 @@
+/* File: main-gcu.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband run on Unix/Curses machines.
+ *
+ *
+ * To use this file, you must define "USE_GCU" in the Makefile.
+ *
+ *
+ * Note that this file is not "intended" to support non-Unix machines,
+ * nor is it intended to support VMS or other bizarre setups.
+ *
+ * Also, this package assumes that the underlying "curses" handles both
+ * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below.
+ *
+ * This code should work with most versions of "curses" or "ncurses",
+ * and the "main-ncu.c" file (and USE_NCU define) are no longer used.
+ *
+ * See also "USE_CAP" and "main-cap.c" for code that bypasses "curses"
+ * and uses the "termcap" information directly, or even bypasses the
+ * "termcap" information and sends direct vt100 escape sequences.
+ *
+ * This file provides up to 4 term windows.
+ *
+ * This file will attempt to redefine the screen colors to conform to
+ * standard Angband colors. It will only do so if the terminal type
+ * indicates that it can do so. See the page:
+ *
+ * http://www.umr.edu/~keldon/ang-patch/ncurses_color.html
+ *
+ * for information on this.
+ *
+ * Consider the use of "savetty()" and "resetty()". XXX XXX XXX
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_GCU
+
+#include <limits.h>
+
+/*
+ * Hack -- play games with "bool" and "term"
+ */
+#undef bool
+
+/* Avoid 'struct term' name conflict with <curses.h> (via <term.h>) on AIX */
+#define term System_term
+
+/*
+ * Include the proper "header" file
+ */
+#ifdef USE_NCURSES
+# include <ncurses.h>
+#else
+# include <curses.h>
+#endif
+
+#undef term
+
+/*
+ * Try redefining the colors at startup.
+ */
+#define REDEFINE_COLORS
+
+
+/*
+ * Hack -- try to guess which systems use what commands
+ * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
+ * Mega-Hack -- try to guess when "POSIX" is available.
+ * If the user defines two of these, we will probably crash.
+ */
+#if !defined(USE_TPOSIX)
+# if !defined(USE_TERMIO) && !defined(USE_TCHARS)
+# if defined(_POSIX_VERSION)
+# define USE_TPOSIX
+# else
+# if defined(USG) || defined(linux) || defined(SOLARIS)
+# define USE_TERMIO
+# else
+# define USE_TCHARS
+# endif
+# endif
+# endif
+#endif
+
+/*
+ * Hack -- Amiga uses "fake curses" and cannot do any of this stuff
+ */
+#if defined(AMIGA)
+# undef USE_TPOSIX
+# undef USE_TERMIO
+# undef USE_TCHARS
+#endif
+
+
+
+
+/*
+ * POSIX stuff
+ */
+#ifdef USE_TPOSIX
+# include <sys/ioctl.h>
+# include <termios.h>
+#endif
+
+/*
+ * One version needs these files
+ */
+#ifdef USE_TERMIO
+# include <sys/ioctl.h>
+# include <termio.h>
+#endif
+
+/*
+ * The other needs these files
+ */
+#ifdef USE_TCHARS
+# include <sys/ioctl.h>
+# include <sys/resource.h>
+# include <sys/param.h>
+# include <sys/file.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+
+/*
+ * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
+ *
+ * They should both work due to the "(i != 1)" test below.
+ */
+#ifndef O_NDELAY
+# define O_NDELAY O_NONBLOCK
+#endif
+
+
+/*
+ * OPTION: some machines lack "cbreak()"
+ * On these machines, we use an older definition
+ */
+/* #define cbreak() crmode() */
+
+
+/*
+ * OPTION: some machines cannot handle "nonl()" and "nl()"
+ * On these machines, we can simply ignore those commands.
+ */
+/* #define nonl() */
+/* #define nl() */
+
+
+/*
+ * Save the "normal" and "angband" terminal settings
+ */
+
+#ifdef USE_TPOSIX
+
+static struct termios norm_termios;
+
+static struct termios game_termios;
+
+#endif
+
+#ifdef USE_TERMIO
+
+static struct termio norm_termio;
+
+static struct termio game_termio;
+
+#endif
+
+#ifdef USE_TCHARS
+
+static struct ltchars norm_special_chars;
+static struct sgttyb norm_ttyb;
+static struct tchars norm_tchars;
+static int norm_local_chars;
+
+static struct ltchars game_special_chars;
+static struct sgttyb game_ttyb;
+static struct tchars game_tchars;
+static int game_local_chars;
+
+#endif
+
+/*
+ * Information about a term
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t; /* All term info */
+
+ WINDOW *win; /* Pointer to the curses window */
+};
+
+/* Max number of windows on screen */
+#define MAX_TERM_DATA 4
+
+/* Information about our windows */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Hack -- Number of initialized "term" structures
+ */
+static int active = 0;
+
+
+#ifdef A_COLOR
+
+/*
+ * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
+ * machines, "A_BRIGHT" produces ugly "inverse" video.
+ */
+#ifndef A_BRIGHT
+# define A_BRIGHT A_BOLD
+#endif
+
+/*
+ * Software flag -- we are allowed to use color
+ */
+static int can_use_color = FALSE;
+
+/*
+ * Software flag -- we are allowed to change the colors
+ */
+static int can_fix_color = FALSE;
+
+/*
+ * Simple Angband to Curses color conversion table
+ */
+static int colortable[16];
+
+#endif
+
+
+
+/*
+ * Place the "keymap" into its "normal" state
+ */
+static void keymap_norm(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSLTC, (char *)&norm_special_chars);
+ (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Place the "keymap" into the "game" state
+ */
+static void keymap_game(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &game_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&game_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSLTC, (char *)&game_special_chars);
+ (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCLSET, (char *)&game_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the normal keymap
+ */
+static void keymap_norm_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Get the normal keymap */
+ tcgetattr(0, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TCGETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCGLTC, (char *)&norm_special_chars);
+ (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the keymaps (normal and game)
+ */
+static void keymap_game_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Acquire the current mapping */
+ tcgetattr(0, &game_termios);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termios.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termios.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termios.c_cc[VQUIT] = (char) - 1;
+ game_termios.c_cc[VERASE] = (char) - 1;
+ game_termios.c_cc[VKILL] = (char) - 1;
+ game_termios.c_cc[VEOF] = (char) - 1;
+ game_termios.c_cc[VEOL] = (char) - 1;
+
+ /* Normally, block until a character is read */
+ game_termios.c_cc[VMIN] = 1;
+ game_termios.c_cc[VTIME] = 0;
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Acquire the current mapping */
+ (void)ioctl(0, TCGETA, (char *)&game_termio);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termio.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termio.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termio.c_cc[VQUIT] = (char) - 1;
+ game_termio.c_cc[VERASE] = (char) - 1;
+ game_termio.c_cc[VKILL] = (char) - 1;
+ game_termio.c_cc[VEOF] = (char) - 1;
+ game_termio.c_cc[VEOL] = (char) - 1;
+
+#if 0
+ /* Disable the non-posix control characters */
+ game_termio.c_cc[VEOL2] = (char) - 1;
+ game_termio.c_cc[VSWTCH] = (char) - 1;
+ game_termio.c_cc[VDSUSP] = (char) - 1;
+ game_termio.c_cc[VREPRINT] = (char) - 1;
+ game_termio.c_cc[VDISCARD] = (char) - 1;
+ game_termio.c_cc[VWERASE] = (char) - 1;
+ game_termio.c_cc[VLNEXT] = (char) - 1;
+ game_termio.c_cc[VSTATUS] = (char) - 1;
+#endif
+
+ /* Normally, block until a character is read */
+ game_termio.c_cc[VMIN] = 1;
+ game_termio.c_cc[VTIME] = 0;
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the default game characters */
+ (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCGLTC, (char *)&game_special_chars);
+ (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCLGET, (char *)&game_local_chars);
+
+ /* Force suspend (^Z) */
+ game_special_chars.t_suspc = (char)26;
+
+ /* Cancel some things */
+ game_special_chars.t_dsuspc = (char) - 1;
+ game_special_chars.t_rprntc = (char) - 1;
+ game_special_chars.t_flushc = (char) - 1;
+ game_special_chars.t_werasc = (char) - 1;
+ game_special_chars.t_lnextc = (char) - 1;
+
+ /* Force interupt (^C) */
+ game_tchars.t_intrc = (char)3;
+
+ /* Force start/stop (^Q, ^S) */
+ game_tchars.t_startc = (char)17;
+ game_tchars.t_stopc = (char)19;
+
+ /* Cancel some things */
+ game_tchars.t_quitc = (char) - 1;
+ game_tchars.t_eofc = (char) - 1;
+ game_tchars.t_brkc = (char) - 1;
+
+#endif
+
+}
+
+
+
+
+/*
+ * Suspend/Resume
+ */
+static errr Term_xtra_gcu_alive(int v)
+{
+ int x, y;
+
+
+ /* Suspend */
+ if (!v)
+ {
+ /* Go to normal keymap mode */
+ keymap_norm();
+
+ /* Restore modes */
+ nocbreak();
+ echo();
+ nl();
+
+ /* Hack -- make sure the cursor is visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+
+ /* Flush the curses buffer */
+ (void)refresh();
+
+ /* Get current cursor position */
+ getyx(curscr, y, x);
+
+ /* Move the cursor to bottom right corner */
+ mvcur(y, x, LINES - 1, 0);
+
+ /* Exit curses */
+ endwin();
+
+ /* Flush the output */
+ (void)fflush(stdout);
+ }
+
+ /* Resume */
+ else
+ {
+ /* Refresh */
+ /* (void)touchwin(curscr); */
+ /* (void)wrefresh(curscr); */
+
+ /* Restore the settings */
+ cbreak();
+ noecho();
+ nonl();
+
+ /* Go to angband keymap mode */
+ keymap_game();
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+#if 0
+
+#ifdef USE_NCURSES
+const char help_gcu[] = "NCurses, for terminal console, subopts -b(ig screen)";
+#else /* USE_NCURSES */
+const char help_gcu[] = "Curses, for terminal console, subopts -b(ig screen)";
+#endif /* USE_NCURSES */
+
+#endif
+
+
+/*
+ * Init the "curses" system
+ */
+static void Term_init_gcu(term *t)
+{
+ term_data *td = (term_data *)(t->data);
+
+ /* Count init's, handle first */
+ if (active++ != 0) return;
+
+ /* Erase the window */
+ (void)wclear(td->win);
+
+ /* Reset the cursor */
+ (void)wmove(td->win, 0, 0);
+
+ /* Flush changes */
+ (void)wrefresh(td->win);
+
+ /* Game keymap */
+ keymap_game();
+}
+
+
+/*
+ * Nuke the "curses" system
+ */
+static void Term_nuke_gcu(term *t)
+{
+ int x, y;
+ term_data *td = (term_data *)(t->data);
+
+ /* Delete this window */
+ delwin(td->win);
+
+ /* Count nuke's, handle last */
+ if (--active != 0) return;
+
+ /* Hack -- make sure the cursor is visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+
+#ifdef A_COLOR
+ /* Reset colors to defaults */
+ start_color();
+#endif
+
+ /* Get current cursor position */
+ getyx(curscr, y, x);
+
+ /* Move the cursor to bottom right corner */
+ mvcur(y, x, LINES - 1, 0);
+
+ /* Flush the curses buffer */
+ (void)refresh();
+
+ /* Exit curses */
+ endwin();
+
+ /* Flush the output */
+ (void)fflush(stdout);
+
+ /* Normal keymap */
+ keymap_norm();
+}
+
+
+
+
+#ifdef USE_GETCH
+
+/*
+ * Process events, with optional wait
+ */
+static errr Term_xtra_gcu_event(int v)
+{
+ int i, k;
+
+ /* Wait */
+ if (v)
+ {
+ /* Paranoia -- Wait for it */
+ nodelay(stdscr, FALSE);
+
+ /* Get a keypress */
+ i = getch();
+
+ /* Mega-Hack -- allow graceful "suspend" */
+ for (k = 0; (k < 10) && (i == ERR); k++) i = getch();
+
+ /* Broken input is special */
+ if (i == ERR) exit_game_panic();
+ if (i == EOF) exit_game_panic();
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Do not wait for it */
+ nodelay(stdscr, TRUE);
+
+ /* Check for keypresses */
+ i = getch();
+
+ /* Wait for it next time */
+ nodelay(stdscr, FALSE);
+
+ /* None ready */
+ if (i == ERR) return (1);
+ if (i == EOF) return (1);
+ }
+
+ /* Enqueue the keypress */
+ Term_keypress(i);
+
+ /* Success */
+ return (0);
+}
+
+#else /* USE_GETCH */
+
+/*
+* Process events (with optional wait)
+*/
+static errr Term_xtra_gcu_event(int v)
+{
+ int i, k;
+
+ char buf[2];
+
+ /* Wait */
+ if (v)
+ {
+ /* Wait for one byte */
+ i = read(0, buf, 1);
+
+ /* Hack -- Handle bizarre "errors" */
+ if ((i <= 0) && (errno != EINTR)) exit_game_panic();
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Get the current flags for stdin */
+ k = fcntl(0, F_GETFL, 0);
+
+ /* Oops */
+ if (k < 0) return (1);
+
+ /* Tell stdin not to block */
+ if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
+
+ /* Read one byte, if possible */
+ i = read(0, buf, 1);
+
+ /* Replace the flags for stdin */
+ if (fcntl(0, F_SETFL, k)) return (1);
+ }
+
+ /* Ignore "invalid" keys */
+ if ((i != 1) || (!buf[0])) return (1);
+
+ /* Enqueue the keypress */
+ Term_keypress(buf[0]);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GETCH */
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_gcu_react(void)
+{
+
+#ifdef A_COLOR
+
+ int i;
+
+ /* Cannot handle color redefinition */
+ if (!can_fix_color) return (0);
+
+ /* Set the colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Set one color (note scaling) */
+ init_color(i,
+ angband_color_table[i][1] * 1000 / 255,
+ angband_color_table[i][2] * 1000 / 255,
+ angband_color_table[i][3] * 1000 / 255);
+ }
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_gcu(int n, int v)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Clear screen */
+ case TERM_XTRA_CLEAR:
+ touchwin(td->win);
+ (void)wclear(td->win);
+ return (0);
+
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ (void)write(1, "\007", 1);
+ return (0);
+
+ /* Flush the Curses buffer */
+ case TERM_XTRA_FRESH:
+ (void)wrefresh(td->win);
+ return (0);
+
+#ifdef USE_CURS_SET
+
+ /* Change the cursor visibility */
+ case TERM_XTRA_SHAPE:
+ curs_set(v);
+ return (0);
+
+#endif
+
+ /* Suspend/Resume curses */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_gcu_alive(v));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_gcu_event(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_gcu_event(FALSE));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ /* React to events */
+ case TERM_XTRA_REACT:
+ Term_xtra_gcu_react();
+ return (0);
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Actually MOVE the hardware cursor
+ */
+static errr Term_curs_gcu(int x, int y)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Literally move the cursor */
+ wmove(td->win, y, x);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a grid of space
+ * Hack -- try to be "semi-efficient".
+ */
+static errr Term_wipe_gcu(int x, int y, int n)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Place cursor */
+ wmove(td->win, y, x);
+
+ /* Clear to end of line */
+ if (x + n >= td->t.wid)
+ {
+ wclrtoeol(td->win);
+ }
+
+ /* Clear some characters */
+ else
+ {
+ while (n-- > 0) waddch(td->win, ' ');
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_gcu(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ int i, pic;
+
+#ifdef A_COLOR
+ /* Set the color */
+ if (can_use_color) wattrset(td->win, colortable[a & 0x0F]);
+#endif
+
+ /* Move the cursor */
+ wmove(td->win, y, x);
+
+ /* Draw each character */
+ for (i = 0; i < n; i++)
+ {
+#ifdef USE_GRAPHICS
+ /* Special character */
+ if (use_graphics && (s[i] & 0x80))
+ {
+ /* Determine picture to use */
+ switch (s[i] & 0x7F)
+ {
+
+#ifdef ACS_CKBOARD
+ /* Wall */
+ case '#':
+ pic = ACS_CKBOARD;
+ break;
+#endif /* ACS_CKBOARD */
+
+#ifdef ACS_BOARD
+ /* Mineral vein */
+ case '%':
+ pic = ACS_BOARD;
+ break;
+#endif /* ACS_BOARD */
+
+ /* XXX */
+ default:
+ pic = '?';
+ break;
+ }
+
+ /* Draw the picture */
+ waddch(td->win, pic);
+
+ /* Next character */
+ continue;
+ }
+#endif
+
+ /* Draw a normal character */
+ waddch(td->win, (byte)s[i]);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Create a window for the given "term_data" argument.
+ *
+ * Assumes legal arguments.
+ */
+static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
+{
+ term *t = &td->t;
+
+ /* Create new window */
+ td->win = newwin(rows, cols, y, x);
+
+ /* Check for failure */
+ if (!td->win)
+ {
+ /* Error */
+ quit("Failed to setup curses window.");
+ }
+
+ /* Initialize the term */
+ term_init(t, cols, rows, 256);
+
+ /* Avoid bottom right corner */
+ t->icky_corner = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Set some hooks */
+ t->init_hook = Term_init_gcu;
+ t->nuke_hook = Term_nuke_gcu;
+
+ /* Set some more hooks */
+ t->text_hook = Term_text_gcu;
+ t->wipe_hook = Term_wipe_gcu;
+ t->curs_hook = Term_curs_gcu;
+ t->xtra_hook = Term_xtra_gcu;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate it */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+static void hook_quit(cptr str)
+{
+ /* Unused */
+ (void)str;
+
+ /* Exit curses */
+ endwin();
+}
+
+
+/*
+ * Prepare "curses" for use by the file "z-term.c"
+ *
+ * Installs the "hook" functions defined above, and then activates
+ * the main screen "term", which clears the screen and such things.
+ *
+ * Someone should really check the semantics of "initscr()"
+ */
+errr init_gcu(int argc, char **argv)
+{
+ int i;
+
+ int num_term = MAX_TERM_DATA, next_win = 0;
+
+ bool use_big_screen = FALSE;
+
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-b"))
+ {
+ use_big_screen = TRUE;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Extract the normal keymap */
+ keymap_norm_prepare();
+
+
+#if defined(USG)
+ /* Initialize for USG Unix */
+ if (initscr() == NULL) return ( -1);
+#else
+/* Initialize for other systems */
+ if (initscr() == (WINDOW*)ERR) return ( -1);
+#endif
+
+ /* Activate hooks */
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Require standard size screen */
+ if ((LINES < 24) || (COLS < 80))
+ {
+ quit("Angband needs at least an 80x24 'curses' screen");
+ }
+
+
+#ifdef USE_GRAPHICS
+
+ /* Set graphics flag */
+ use_graphics = arg_graphics;
+
+#endif
+
+#ifdef A_COLOR
+
+ /*** Init the Color-pairs and set up a translation table ***/
+
+ /* Do we have color, and enough color, available? */
+ can_use_color = ((start_color() != ERR) && has_colors() &&
+ (COLORS >= 8) && (COLOR_PAIRS >= 8));
+
+#ifdef REDEFINE_COLORS
+
+ /* Can we change colors? */
+ can_fix_color = (can_use_color && can_change_color() &&
+ (COLORS >= 16) && (COLOR_PAIRS > 8));
+
+#endif
+
+ /* Attempt to use customized colors */
+ if (can_fix_color)
+ {
+ /* Prepare the color pairs */
+ for (i = 1; i <= 8; i++)
+ {
+ /* Reset the color */
+ if (init_pair(i, i - 1, 0) == ERR)
+ {
+ quit("Color pair init failed");
+ }
+
+ /* Set up the colormap */
+ colortable[i - 1] = (COLOR_PAIR(i) | A_NORMAL);
+ colortable[i + 7] = (COLOR_PAIR(i) | A_BRIGHT);
+ }
+
+ /* Take account of "gamma correction" XXX XXX XXX */
+
+ /* Prepare the "Angband Colors" */
+ Term_xtra_gcu_react();
+ }
+
+ /* Attempt to use colors */
+ else if (can_use_color)
+ {
+ /* Color-pair 0 is *always* WHITE on BLACK */
+
+ /* Prepare the color pairs */
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(4, COLOR_BLUE, COLOR_BLACK);
+ init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(6, COLOR_CYAN, COLOR_BLACK);
+ init_pair(7, COLOR_BLACK, COLOR_BLACK);
+
+ /* Prepare the "Angband Colors" -- Bright white is too bright */
+ colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
+ colortable[1] = (COLOR_PAIR(0) | A_NORMAL); /* White */
+ colortable[2] = (COLOR_PAIR(6) | A_NORMAL); /* Grey XXX */
+ colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
+ colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
+ colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
+ colortable[6] = (COLOR_PAIR(4) | A_NORMAL); /* Blue */
+ colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
+ colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
+ colortable[9] = (COLOR_PAIR(6) | A_BRIGHT); /* Light-grey XXX */
+ colortable[10] = (COLOR_PAIR(5) | A_NORMAL); /* Purple */
+ colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
+ colortable[12] = (COLOR_PAIR(5) | A_BRIGHT); /* Light Red XXX */
+ colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
+ colortable[14] = (COLOR_PAIR(4) | A_BRIGHT); /* Light Blue */
+ colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
+ }
+
+#endif
+
+
+ /*** Low level preparation ***/
+
+#ifdef USE_GETCH
+
+ /* Paranoia -- Assume no waiting */
+ nodelay(stdscr, FALSE);
+
+#endif
+
+ /* Prepare */
+ cbreak();
+ noecho();
+ nonl();
+
+ /* Extract the game keymap */
+ keymap_game_prepare();
+
+
+ /*** Now prepare the term(s) ***/
+
+ /* Big screen -- one big term */
+ if (use_big_screen)
+ {
+ /* Create a term */
+ term_data_init_gcu(&data[0], LINES, COLS, 0, 0);
+
+ /* Remember the term */
+ angband_term[0] = &data[0].t;
+ }
+
+ /* No big screen -- create as many term windows as possible */
+ else
+ {
+ /* Create several terms */
+ for (i = 0; i < num_term; i++)
+ {
+ int rows, cols, y, x;
+
+ /* Decide on size and position */
+ switch (i)
+ {
+ /* Upper left */
+ case 0:
+ {
+ rows = 24;
+ cols = 80;
+ y = x = 0;
+ break;
+ }
+
+ /* Lower left */
+ case 1:
+ {
+ rows = LINES - 25;
+ cols = 80;
+ y = 25;
+ x = 0;
+ break;
+ }
+
+ /* Upper right */
+ case 2:
+ {
+ rows = 24;
+ cols = COLS - 81;
+ y = 0;
+ x = 81;
+ break;
+ }
+
+ /* Lower right */
+ case 3:
+ {
+ rows = LINES - 25;
+ cols = COLS - 81;
+ y = 25;
+ x = 81;
+ break;
+ }
+
+ /* XXX */
+ default:
+ {
+ rows = cols = y = x = 0;
+ break;
+ }
+ }
+
+ /* Skip non-existant windows */
+ if (rows <= 0 || cols <= 0) continue;
+
+ /* Create a term */
+ term_data_init_gcu(&data[next_win], rows, cols, y, x);
+
+ /* Remember the term */
+ angband_term[next_win] = &data[next_win].t;
+
+ /* One more window */
+ next_win++;
+ }
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Remember the active screen */
+ term_screen = &data[0].t;
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* USE_GCU */
+
+
diff --git a/src/main-gtk.c b/src/main-gtk.c
new file mode 100644
index 00000000..8a15e237
--- /dev/null
+++ b/src/main-gtk.c
@@ -0,0 +1,5251 @@
+/* File: main-gtk.c */
+
+/*
+ * Copyright (c) 2000-2001 Robert Ruehlmann,
+ * Steven Fuerst, Uwe Siems, "pelpel", et al.
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/*
+ * Robert Ruehlmann wrote the original Gtk port. Since an initial work is
+ * much harder than enhancements, his effort worth more credits than
+ * others.
+ *
+ * Steven Fuerst implemented colour-depth independent X server support,
+ * graphics, resizing and big screen support for ZAngband as well as
+ * fast image rescaling that is included here.
+ *
+ * Uwe Siems wrote smooth tiles rescaling code (on by default).
+ * Try this with 8x8 tiles. They *will* look different.
+ *
+ * "pelpel" wrote another colour-depth independent X support
+ * using GdkRGB, added several hooks and callbacks for various
+ * reasons, wrote no-backing store mode (off by default),
+ * added GtkItemFactory based menu system, introduced
+ * USE_GRAPHICS code bloat (^ ^;), added comments (I have
+ * a strange habit of writing comments while I code...)
+ * and reorganised the file a bit.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Activate variant-specific features
+ *
+ * Angband 2.9.3 and close variants don't require any.
+ *
+ * Angband 2.9.4 alpha and later removed the short-lived
+ * can_save flag, so please #define can_save TRUE, or remove
+ * all the references to it. They also changed long-lived
+ * z-virt macro names. Find C_FREE/C_KILL and replace them
+ * with FREE/KILL, which takes one pointer parameter.
+ *
+ * [Z]-based variants (Gum and Cth, for example) usually need
+ * ANG293_COMPAT, ANG291_COMPAT and ANG281_RESET_VISUALS.
+ *
+ * [O] needs ANG293_COMPAT and ZANG_SAVE_GAME.
+ *
+ * ZAngband has its own enhanced main-gtk.c as mentioned above, and
+ * you *should* use it :-)
+ *
+ * ANG291_COMPAT does not include Angband 2.9.x's gamma correction code.
+ * If you like to use SUPPORT_GAMMA, copy the code bracketed
+ * inside of #ifdef SUPPORT_GAMMA in util.c of Angband 2.9.1 or greater.
+ */
+#define TOME
+
+#ifdef TOME
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ANG291_COMPAT /* Requires V2.9.1 compatibility code */
+# define ANG281_RESET_VISUALS /* The old style reset_visuals() */
+# define INTERACTIVE_GAMMA /* Supports interactive gamma correction */
+# define SAVEFILE_SCREEN /* New/Open integrated into the game */
+# define USE_DOUBLE_TILES /* Mogami's bigtile patch */
+#endif /* TOME */
+
+/*
+ * Some examples
+ */
+#ifdef ANGBAND300
+# define can_save TRUE /* Mimick the short-lived flag */
+# define C_FREE(P, N, T) FREE(P) /* Emulate the long-lived macro */
+# define USE_TRANSPARENCY /* Because it's default now */
+#endif /* ANGBAND300 */
+
+#ifdef GUMBAND
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ANG291_COMPAT /* Requires V2.9.1 compatibility code */
+# define ANG281_RESET_VISUALS /* The old style reset_visuals() */
+# define OLD_SAVEFILE_CODE /* See also SAVEFILE_MUTABLE in files.c */
+# define NO_REDRAW_SECTION /* Doesn't have Term_redraw_section() */
+#endif /* GUMBAND */
+
+#ifdef OANGBAND
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ZANG_SAVE_GAME /* do_cmd_save_game with auto_save parameter */
+#endif /* OANGBAND */
+
+
+#ifdef USE_GTK
+
+/* Force ANSI standard */
+/* #define __STRICT_ANSI__ */
+
+/* No GCC-specific includes */
+/* #undef __GNUC__ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+/*
+ * Include some helpful X11 code.
+ */
+#ifndef ANG293_COMPAT
+# include "maid-x11.h"
+#endif /* !ANG293_COMPAT */
+
+
+/*
+ * Number of pixels inserted between the menu bar and the main screen
+ */
+#define NO_PADDING 0
+
+
+/*
+ * Largest possible number of terminal windows supported by the game
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * Extra data to associate with each "window"
+ *
+ * Each "window" is represented by a "term_data" structure, which
+ * contains a "term" structure, which contains a pointer (t->data)
+ * back to the term_data structure.
+ */
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Since GdkRGB doesn't provide us some useful functions...
+ */
+typedef struct GdkRGBImage GdkRGBImage;
+
+struct GdkRGBImage
+{
+ gint width;
+ gint height;
+ gint ref_count;
+ guchar *image;
+};
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * This structure holds everything you need to manipulate terminals
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t;
+
+ GtkWidget *window;
+ GtkWidget *drawing_area;
+ GdkPixmap *backing_store;
+ GdkFont *font;
+ GdkGC *gc;
+
+ bool shown;
+ byte last_attr;
+
+ int font_wid;
+ int font_hgt;
+
+ int rows;
+ int cols;
+
+#ifdef USE_GRAPHICS
+
+ int tile_wid;
+ int tile_hgt;
+
+ GdkRGBImage *tiles;
+# ifdef USE_TRANSPARENCY
+ guint32 bg_pixel;
+ GdkRGBImage *trans_buf;
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+ cptr name;
+};
+
+
+/*
+ * Where to draw when we call Gdk drawing primitives
+ */
+# define TERM_DATA_DRAWABLE(td) \
+((td)->backing_store ? (td)->backing_store : (td)->drawing_area->window)
+
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt) \
+if ((td)->backing_store) gdk_draw_pixmap( \
+(td)->drawing_area->window, \
+(td)->gc, \
+(td)->backing_store, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(wid) * (td)->font_wid, \
+(hgt) * (td)->font_hgt)
+
+
+#if 0
+
+/* Compile time option version */
+
+# ifdef USE_BACKING_STORE
+
+# define TERM_DATA_DRAWABLE(td) (td)->backing_store
+
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt) \
+gdk_draw_pixmap( \
+(td)->drawing_area->window, \
+(td)->gc, \
+(td)->backing_store, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(wid) * (td)->font_wid, \
+(hgt) * (td)->font_hgt)
+
+# else /* USE_BACKING_STORE */
+
+# define TERM_DATA_DRAWABLE(td) (td)->drawing_area->window
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt)
+
+# endif /* USE_BACKING_STORE */
+
+#endif /* 0 */
+
+
+/*
+ * An array of "term_data" structures, one for each "sub-window"
+ */
+static term_data data[MAX_TERM_DATA];
+
+/*
+ * Number of active terms
+ */
+static int num_term = 1;
+
+
+/*
+ * RGB values of the sixteen Angband colours
+ */
+static guint32 angband_colours[16];
+
+
+/*
+ * Set to TRUE when a game is in progress
+ */
+static bool game_in_progress = FALSE;
+
+
+/*
+ * This is in some cases used for double buffering as well as
+ * a backing store, speeding things up under client-server
+ * configurations, while turning this off *might* work better
+ * with the MIT Shm extention which is usually active if you run
+ * Angband locally, because it reduces amount of memory-to-memory copy.
+ */
+static bool use_backing_store = TRUE;
+
+
+
+
+/**** Vanilla compatibility functions ****/
+
+#ifdef ANG293_COMPAT
+
+/*
+ * Look up some environment variables to find font name for each window.
+ */
+static cptr get_default_font(int term)
+{
+ char buf[64];
+ cptr font_name;
+
+ /* Window specific font name */
+ strnfmt(buf, 64, "ANGBAND_X11_FONT_%s", angband_term_name[term]);
+
+ /* Check environment for that font */
+ font_name = getenv(buf);
+
+ /* Window specific font name */
+ strnfmt(buf, 64, "ANGBAND_X11_FONT_%d", term);
+
+ /* Check environment for that font */
+ if (!font_name) font_name = getenv(buf);
+
+ /* Check environment for "base" font */
+ if (!font_name) font_name = getenv("ANGBAND_X11_FONT");
+
+ /* No environment variables, use default font */
+ if (!font_name) font_name = DEFAULT_X11_FONT_SCREEN;
+
+ return (font_name);
+}
+
+
+# ifndef SAVEFILE_SCREEN
+
+/*
+ * In [V]2.9.3, this frees all dynamically allocated memory
+ */
+static void cleanup_angband(void)
+{
+ /* XXX XXX XXX */
+}
+
+# endif /* !SAVEFILE_SCREEN */
+
+/*
+ * New global flag to indicate if it's safe to save now
+ */
+#define can_save TRUE
+
+#endif /* ANG293_COMPAT */
+
+
+#ifdef ANG291_COMPAT
+
+/*
+ * The standard game uses this to implement lighting effects
+ * for 16x16 tiles in cave.c...
+ *
+ * Because of the way it is implemented in X11 ports,
+ * we can set this to TRUE even if we are using the 8x8 tileset.
+ */
+static bool use_transparency = TRUE;
+
+#endif /* ANG291_COMPAT */
+
+
+
+
+/**** Low level routines - memory allocation ****/
+
+/*
+ * Hook to "release" memory
+ */
+#ifdef ANGBAND300
+static vptr hook_rnfree(vptr v)
+#else
+static vptr hook_rnfree(vptr v, huge size)
+#endif /* ANGBAND300 */
+{
+ /* Dispose */
+ g_free(v);
+
+ /* Success */
+ return (NULL);
+}
+
+
+/*
+ * Hook to "allocate" memory
+ */
+static vptr hook_ralloc(huge size)
+{
+ /* Make a new pointer */
+ return (g_malloc(size));
+}
+
+
+
+/**** Low level routines - colours and graphics ****/
+
+#ifdef SUPPORT_GAMMA
+
+/*
+ * When set to TRUE, indicates that we can use gamma_table
+ */
+static bool gamma_table_ready = FALSE;
+
+
+# ifdef INTERACTIVE_GAMMA
+
+/*
+ * Initialise the gamma-correction table for current gamma_val
+ * - interactive version
+ */
+static void setup_gamma_table(void)
+{
+ static u16b old_gamma_val = 0;
+
+ /* Don't have to rebuild the table */
+ if (gamma_val == old_gamma_val) return;
+
+ /* Temporarily inactivate the table */
+ gamma_table_ready = FALSE;
+
+ /* Validate gamma_val */
+ if ((gamma_val <= 0) || (gamma_val >= 256))
+ {
+ /* Reset */
+ old_gamma_val = gamma_val = 0;
+
+ /* Leave it inactive */
+ return;
+ }
+
+ /* (Re)build the gamma table */
+ build_gamma_table(gamma_val);
+
+ /* Remember the gamma value used */
+ old_gamma_val = gamma_val;
+
+ /* Activate the table */
+ gamma_table_ready = TRUE;
+}
+
+# else /* INTERACTIVE_GAMMA */
+
+/*
+ * Initialise the gamma-correction table if environment variable
+ * ANGBAND_X11_GAMMA is set and contains a meaningful value
+ *
+ * Restored for cross-variant compatibility
+ */
+static void setup_gamma_table(void)
+{
+ cptr tmp;
+ int gamma_val;
+
+
+ /* The table's already set up */
+ if (gamma_table_ready) return;
+
+ /*
+ * XXX XXX It's documented nowhere, but ANGBAND_X11_GAMMA is
+ * 256 * (1 / gamma), rounded to integer. A recommended value
+ * is 183, which is an approximation of the Macintosh hardware
+ * gamma of 1.4.
+ *
+ * gamma ANGBAND_X11_GAMMA
+ * ----- -----------------
+ * 1.2 213
+ * 1.25 205
+ * 1.3 197
+ * 1.35 190
+ * 1.4 183
+ * 1.45 177
+ * 1.5 171
+ * 1.6 160
+ * 1.7 151
+ * ...
+ *
+ * XXX XXX The environment variable, or better,
+ * the interact with colours command should allow users
+ * to specify gamma values (or gamma value * 100).
+ */
+ tmp = getenv("ANGBAND_X11_GAMMA");
+
+ /* Nothing to do */
+ if (tmp == NULL) return;
+
+ /* Extract the value */
+ gamma_val = atoi(tmp);
+
+ /*
+ * Only need to build the table if gamma exists and set to
+ * a meaningful value.
+ *
+ * XXX It may be a good idea to prevent use of very high gamma values,
+ * say, greater than 2.5, which is gamma of normal CRT display IIRC.
+ */
+ if ((gamma_val <= 0) || (gamma_val >= 256)) return;
+
+ /* Build the gamma correction table */
+ build_gamma_table(gamma_val);
+
+ /* The table is properly set up */
+ gamma_table_ready = TRUE;
+}
+
+# endif /* INTERACTIVE_GAMMA */
+
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * Remeber RGB values for sixteen Angband colours, in a format
+ * that is convinient for GdkRGB GC functions.
+ *
+ * XXX XXX Duplication of maid-x11.c is far from the Angband
+ * ideal of code cleanliness, but the whole point of using GdkRGB
+ * is to let it handle colour allocation which it does in a very
+ * clever fashion. Ditto for the tile scaling code and the BMP loader
+ * below.
+ */
+static void init_colours(void)
+{
+ int i;
+
+
+#ifdef SUPPORT_GAMMA
+
+ /* (Re)build gamma table if necessary */
+ setup_gamma_table();
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Process each colour */
+ for (i = 0; i < 16; i++)
+ {
+ u32b red, green, blue;
+
+ /* Retrieve RGB values from the game */
+ red = angband_color_table[i][1];
+ green = angband_color_table[i][2];
+ blue = angband_color_table[i][3];
+
+#ifdef SUPPORT_GAMMA
+
+ /* Hack -- Gamma Correction */
+ if (gamma_table_ready)
+ {
+ red = gamma_table[red];
+ green = gamma_table[green];
+ blue = gamma_table[blue];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Remember a GdkRGB value, that is 0xRRGGBB */
+ angband_colours[i] = (red << 16) | (green << 8) | blue;
+ }
+}
+
+
+/*
+ * Set foreground colour of window td to attr, only when it is necessary
+ */
+static void term_data_set_fg(term_data *td, byte attr)
+{
+ /* We can use the current gc */
+ if (td->last_attr == attr) return;
+
+ /* Activate the colour */
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[attr]);
+
+ /* Remember it */
+ td->last_attr = attr;
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Graphics mode selector - current setting and requested value
+ */
+#define GRAF_MODE_NONE 0
+#define GRAF_MODE_OLD 1
+#define GRAF_MODE_NEW 2
+
+static int graf_mode = GRAF_MODE_NONE;
+static int graf_mode_request = GRAF_MODE_NONE;
+
+/*
+ * Use smooth rescaling?
+ */
+static bool smooth_rescaling = TRUE;
+static bool smooth_rescaling_request = TRUE;
+
+/*
+ * Dithering
+ */
+static GdkRgbDither dith_mode = GDK_RGB_DITHER_NORMAL;
+
+/*
+ * Need to reload and resize tiles when fonts are changed.
+ */
+static bool resize_request = FALSE;
+
+/*
+ * Numbers of columns and rows in current tileset
+ * calculated and set by the tile loading code in graf_init()
+ * and used by Term_pict_gtk()
+ */
+static int tile_rows;
+static int tile_cols;
+
+
+/*
+ * Directory name(s)
+ */
+static cptr ANGBAND_DIR_XTRA_GRAF;
+
+
+/*
+ * Be nice to old graphics hardwares -- using GdkRGB.
+ *
+ * We don't have colour allocation failure any longer this way,
+ * even with 8bpp X servers. Gimp *does* work with 8bpp, why not Angband?
+ *
+ * Initialisation (before any widgets are created)
+ * gdk_rgb_init();
+ * gtk_widget_set_default_colormap (gdk_rgb_get_cmap());
+ * gtk_widget_set_default_visual (gdk_rgb_get_visual());
+ *
+ * Setting fg/bg colours
+ * void gdk_rgb_gc_set_foreground(GdkGC *gc, guint32 rgb);
+ * void gdk_rgb_gc_set_background(GdkGC *gc, guint32 rgb);
+ * where rgb is 0xRRGGBB.
+ *
+ * Drawing rgb images
+ * void gdk_draw_rgb_image(
+ * GdkDrawable *drawable,
+ * GdkGC *gc,
+ * gint x, gint y,
+ * gint width, gint height,
+ * GdkRgbDither dith,
+ * guchar *rgb_buf,
+ * gint rowstride);
+ *
+ * dith:
+ * GDK_RGB_DITHER_NORMAL : dither if 8bpp or below
+ * GDK_RGB_DITHER_MAX : dither if 16bpp or below.
+ *
+ * for 0 <= i < width and 0 <= j < height,
+ * the pixel (x + i, y + j) is colored with
+ * red value rgb_buf[j * rowstride + i * 3],
+ * green value rgb_buf[j * rowstride + i * 3 + 1], and
+ * blue value rgb_buf[j * rowstride + i * 3 + 2].
+ */
+
+/*
+ * gdk_image compatibility functions - should be part of gdk, IMHO.
+ */
+
+/*
+ * Create GdkRGBImage of width * height and return pointer
+ * to it. Returns NULL on failure
+ */
+static GdkRGBImage *gdk_rgb_image_new(
+ gint width,
+ gint height)
+{
+ GdkRGBImage *result;
+
+ /* Allocate a struct */
+ result = g_new(GdkRGBImage, 1);
+
+ /* Oops */
+ if (result == NULL) return (NULL);
+
+ /* Allocate buffer */
+ result->image = g_new0(guchar, width * height * 3);
+
+ /* Oops */
+ if (result->image == NULL)
+ {
+ g_free(result);
+ return (NULL);
+ }
+
+ /* Initialise size fields */
+ result->width = width;
+ result->height = height;
+
+ /* Initialise reference count */
+ result->ref_count = 1;
+
+ /* Success */
+ return (result);
+}
+
+/*
+ * Free a GdkRGBImage
+ */
+static void gdk_rgb_image_destroy(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ if (im == NULL) return;
+
+ /* Free the RGB buffer */
+ g_free(im->image);
+
+ /* Free the structure */
+ g_free(im);
+}
+
+
+#if 0
+
+/*
+ * Unref a GdkRGBImage
+ */
+static void gdk_rgb_image_unref(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Decrease reference count by 1 */
+ im->ref_count--;
+
+ /* Free if nobody's using it */
+ if (im->ref_count <= 0) gdk_rgb_image_destroy(im);
+}
+
+
+/*
+ * Reference a GdkRGBImage
+ */
+static void gdk_rgb_image_ref(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Increase reference count by 1 */
+ im->ref_count++;
+}
+
+#endif /* 0 */
+
+
+/*
+ * Write RGB pixel of the format 0xRRGGBB to (x, y) in GdkRGBImage
+ */
+static void gdk_rgb_image_put_pixel(
+ GdkRGBImage *im,
+ gint x,
+ gint y,
+ guint32 pixel)
+{
+ guchar *rgbp;
+
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Paranoia */
+ if ((x < 0) || (x >= im->width)) return;
+
+ /* Paranoia */
+ if ((y < 0) || (y >= im->height)) return;
+
+ /* Access RGB data */
+ rgbp = &im->image[(y * im->width * 3) + (x * 3)];
+
+ /* Red */
+ *rgbp++ = (pixel >> 16) & 0xFF;
+ /* Green */
+ *rgbp++ = (pixel >> 8) & 0xFF;
+ /* Blue */
+ *rgbp = pixel & 0xFF;
+}
+
+
+/*
+ * Returns RGB pixel (0xRRGGBB) at (x, y) in GdkRGBImage
+ */
+static guint32 gdk_rgb_image_get_pixel(
+ GdkRGBImage *im,
+ gint x,
+ gint y)
+{
+ guchar *rgbp;
+
+ /* Paranoia */
+ if (im == NULL) return (0);
+
+ /* Paranoia - returns black */
+ if ((x < 0) || (x >= im->width)) return (0);
+
+ /* Paranoia */
+ if ((y < 0) || (y >= im->height)) return (0);
+
+ /* Access RGB data */
+ rgbp = &im->image[(y * im->width * 3) + (x * 3)];
+
+ /* Return result */
+ return ((rgbp[0] << 16) | (rgbp[1] << 8) | (rgbp[2]));
+}
+
+
+/*
+ * Since gdk_draw_rgb_image is a bit harder to use than it's
+ * GdkImage counterpart, I wrote a grue function that takes
+ * exactly the same parameters as gdk_draw_image, with
+ * the GdkImage parameter replaced with GdkRGBImage.
+ */
+static void gdk_draw_rgb_image_2(
+ GdkDrawable *drawable,
+ GdkGC *gc,
+ GdkRGBImage *image,
+ gint xsrc,
+ gint ysrc,
+ gint xdest,
+ gint ydest,
+ gint width,
+ gint height)
+{
+ /* Paranoia */
+ g_return_if_fail(drawable != NULL);
+ g_return_if_fail(image != NULL);
+
+ /* Paranoia */
+ if (xsrc < 0 || (xsrc + width - 1) >= image->width) return;
+ if (ysrc < 0 || (ysrc + height - 1) >= image->height) return;
+
+ /* Draw the image at (xdest, ydest), with dithering if bpp <= 8/16 */
+ gdk_draw_rgb_image(
+ drawable,
+ gc,
+ xdest,
+ ydest,
+ width,
+ height,
+ dith_mode,
+ &image->image[(ysrc * image->width * 3) + (xsrc * 3)],
+ image->width * 3);
+}
+
+
+/*
+ * Code for smooth icon rescaling from Uwe Siems, Jan 2000
+ *
+ * XXX XXX Duplication of maid-x11.c, again. It doesn't do any colour
+ * allocation, either.
+ */
+
+/*
+ * to save ourselves some labour, define a maximum expected icon width here:
+ */
+#define MAX_ICON_WIDTH 32
+
+
+/*
+ * Each pixel is kept in this structure during smooth rescaling
+ * calculations, to make things a bit easier
+ */
+typedef struct rgb_type rgb_type;
+
+struct rgb_type
+{
+ guint32 red;
+ guint32 green;
+ guint32 blue;
+};
+
+/*
+ * Because there are many occurences of this, and because
+ * it's logical to do so...
+ */
+#define pixel_to_rgb(pix, rgb_buf) \
+(rgb_buf)->red = ((pix) >> 16) & 0xFF; \
+(rgb_buf)->green = ((pix) >> 8) & 0xFF; \
+(rgb_buf)->blue = (pix) & 0xFF
+
+
+/*
+ * get_scaled_row reads a scan from the given GdkRGBImage, scales it smoothly
+ * and returns the red, green and blue values in arrays.
+ * The values in this arrays must be divided by a certain value that is
+ * calculated in scale_icon.
+ * x, y is the position, iw is the input width and ow the output width
+ * scan must be sufficiently sized
+ */
+static void get_scaled_row(
+ GdkRGBImage *im,
+ int x,
+ int y,
+ int iw,
+ int ow,
+ rgb_type *scan)
+{
+ int xi, si, sifrac, ci, cifrac, add_whole, add_frac;
+ guint32 pix;
+ rgb_type prev;
+ rgb_type next;
+ bool get_next_pix;
+
+ /* Unscaled */
+ if (iw == ow)
+ {
+ for (xi = 0; xi < ow; xi++)
+ {
+ pix = gdk_rgb_image_get_pixel(im, x + xi, y);
+ pixel_to_rgb(pix, &scan[xi]);
+ }
+ }
+
+ /* Scaling by subsampling (grow) */
+ else if (iw < ow)
+ {
+ iw--;
+ ow--;
+
+ /* read first pixel: */
+ pix = gdk_rgb_image_get_pixel(im, x, y);
+ pixel_to_rgb(pix, &next);
+ prev = next;
+
+ /* si and sifrac give the subsampling position: */
+ si = x;
+ sifrac = 0;
+
+ /* get_next_pix tells us, that we need the next pixel */
+ get_next_pix = TRUE;
+
+ for (xi = 0; xi <= ow; xi++)
+ {
+ if (get_next_pix)
+ {
+ prev = next;
+ if (xi < ow)
+ {
+ /* only get next pixel if in same icon */
+ pix = gdk_rgb_image_get_pixel(im, si + 1, y);
+ pixel_to_rgb(pix, &next);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by ow occurs in scale_icon */
+ scan[xi].red = prev.red * (ow - sifrac) + next.red * sifrac;
+ scan[xi].green = prev.green * (ow - sifrac) + next.green * sifrac;
+ scan[xi].blue = prev.blue * (ow - sifrac) + next.blue * sifrac;
+
+ /* advance sampling position: */
+ sifrac += iw;
+ if (sifrac >= ow)
+ {
+ si++;
+ sifrac -= ow;
+ get_next_pix = TRUE;
+ }
+ else
+ {
+ get_next_pix = FALSE;
+ }
+
+ }
+ }
+
+ /* Scaling by averaging (shrink) */
+ else
+ {
+ /* width of an output pixel in input pixels: */
+ add_whole = iw / ow;
+ add_frac = iw % ow;
+
+ /* start position of the first output pixel: */
+ si = x;
+ sifrac = 0;
+
+ /* get first input pixel: */
+ pix = gdk_rgb_image_get_pixel(im, x, y);
+ pixel_to_rgb(pix, &next);
+
+ for (xi = 0; xi < ow; xi++)
+ {
+ /* find endpoint of the current output pixel: */
+ ci = si + add_whole;
+ cifrac = sifrac + add_frac;
+ if (cifrac >= ow)
+ {
+ ci++;
+ cifrac -= ow;
+ }
+
+ /* take fraction of current input pixel (starting segment): */
+ scan[xi].red = next.red * (ow - sifrac);
+ scan[xi].green = next.green * (ow - sifrac);
+ scan[xi].blue = next.blue * (ow - sifrac);
+ si++;
+
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ rgb_type tmp_rgb;
+
+ pix = gdk_rgb_image_get_pixel(im, si, y);
+ pixel_to_rgb(pix, &tmp_rgb);
+ scan[xi].red += tmp_rgb.red * ow;
+ scan[xi].green += tmp_rgb.green * ow;
+ scan[xi].blue += tmp_rgb.blue * ow;
+ si++;
+ }
+
+ /* add fraction of current input pixel (ending segment): */
+ if (xi < ow - 1)
+ {
+ /* only get next pixel if still in icon: */
+ pix = gdk_rgb_image_get_pixel(im, si, y);
+ pixel_to_rgb(pix, &next);
+ }
+
+ sifrac = cifrac;
+ if (sifrac > 0)
+ {
+ scan[xi].red += next.red * sifrac;
+ scan[xi].green += next.green * sifrac;
+ scan[xi].blue += next.blue * sifrac;
+ }
+ }
+ }
+}
+
+
+/*
+ * put_rgb_scan takes arrays for red, green and blue and writes pixel values
+ * according to this values in the GdkRGBImage-structure. w is the number of
+ * pixels to write and div is the value by which all red/green/blue values
+ * are divided first.
+ */
+static void put_rgb_scan(
+ GdkRGBImage *im,
+ int x,
+ int y,
+ int w,
+ int div,
+ rgb_type *scan)
+{
+ int xi;
+ guint32 pix;
+ guint32 adj = div / 2;
+
+ for (xi = 0; xi < w; xi++)
+ {
+ byte r, g, b;
+
+ /* un-factor the RGB values */
+ r = (scan[xi].red + adj) / div;
+ g = (scan[xi].green + adj) / div;
+ b = (scan[xi].blue + adj) / div;
+
+#ifdef SUPPORT_GAMMA
+
+ /* Apply gamma correction if requested and available */
+ if (gamma_table_ready)
+ {
+ r = gamma_table[r];
+ g = gamma_table[g];
+ b = gamma_table[b];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Make a (virtual) 24-bit pixel */
+ pix = (r << 16) | (g << 8) | (b);
+
+ /* Draw it into image */
+ gdk_rgb_image_put_pixel(im, x + xi, y, pix);
+ }
+}
+
+
+/*
+ * scale_icon transfers an area from GdkRGBImage im_in, locate (x1,y1) to
+ * im_out, locate (x2, y2). Source size is (ix, iy) and destination size
+ * is (ox, oy).
+ *
+ * It does this by getting icon scan line from get_scaled_scan and handling
+ * them the same way as pixels are handled in get_scaled_scan.
+ * This even allows icons to be scaled differently in horizontal and
+ * vertical directions (eg. shrink horizontal, grow vertical).
+ */
+static void scale_icon(
+ GdkRGBImage *im_in,
+ GdkRGBImage *im_out,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ int div;
+ int xi, yi, si, sifrac, ci, cifrac, add_whole, add_frac;
+
+ /* buffers for pixel rows: */
+ rgb_type prev[MAX_ICON_WIDTH];
+ rgb_type next[MAX_ICON_WIDTH];
+ rgb_type temp[MAX_ICON_WIDTH];
+
+ bool get_next_row;
+
+ /* get divider value for the horizontal scaling: */
+ if (ix == ox)
+ div = 1;
+ else if (ix < ox)
+ div = ox - 1;
+ else
+ div = ix;
+
+ /* no scaling needed vertically: */
+ if (iy == oy)
+ {
+ for (yi = 0; yi < oy; yi++)
+ {
+ get_scaled_row(im_in, x1, y1 + yi, ix, ox, temp);
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+ }
+ }
+
+ /* scaling by subsampling (grow): */
+ else if (iy < oy)
+ {
+ iy--;
+ oy--;
+ div *= oy;
+
+ /* get first row: */
+ get_scaled_row(im_in, x1, y1, ix, ox, next);
+
+ /* si and sifrac give the subsampling position: */
+ si = y1;
+ sifrac = 0;
+
+ /* get_next_row tells us, that we need the next row */
+ get_next_row = TRUE;
+ for (yi = 0; yi <= oy; yi++)
+ {
+ if (get_next_row)
+ {
+ for (xi = 0; xi < ox; xi++)
+ {
+ prev[xi] = next[xi];
+ }
+ if (yi < oy)
+ {
+ /* only get next row if in same icon */
+ get_scaled_row(im_in, x1, si + 1, ix, ox, next);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by oy occurs in put_rgb_scan */
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red = (prev[xi].red * (oy - sifrac) +
+ next[xi].red * sifrac);
+ temp[xi].green = (prev[xi].green * (oy - sifrac) +
+ next[xi].green * sifrac);
+ temp[xi].blue = (prev[xi].blue * (oy - sifrac) +
+ next[xi].blue * sifrac);
+ }
+
+ /* write row to output image: */
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+
+ /* advance sampling position: */
+ sifrac += iy;
+ if (sifrac >= oy)
+ {
+ si++;
+ sifrac -= oy;
+ get_next_row = TRUE;
+ }
+ else
+ {
+ get_next_row = FALSE;
+ }
+
+ }
+ }
+
+ /* scaling by averaging (shrink) */
+ else
+ {
+ div *= iy;
+
+ /* height of a output row in input rows: */
+ add_whole = iy / oy;
+ add_frac = iy % oy;
+
+ /* start position of the first output row: */
+ si = y1;
+ sifrac = 0;
+
+ /* get first input row: */
+ get_scaled_row(im_in, x1, y1, ix, ox, next);
+ for (yi = 0; yi < oy; yi++)
+ {
+ /* find endpoint of the current output row: */
+ ci = si + add_whole;
+ cifrac = sifrac + add_frac;
+ if (cifrac >= oy)
+ {
+ ci++;
+ cifrac -= oy;
+ }
+
+ /* take fraction of current input row (starting segment): */
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red = next[xi].red * (oy - sifrac);
+ temp[xi].green = next[xi].green * (oy - sifrac);
+ temp[xi].blue = next[xi].blue * (oy - sifrac);
+ }
+ si++;
+
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ get_scaled_row(im_in, x1, si, ix, ox, next);
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red += next[xi].red * oy;
+ temp[xi].green += next[xi].green * oy;
+ temp[xi].blue += next[xi].blue * oy;
+ }
+ si++;
+ }
+
+ /* add fraction of current input row (ending segment): */
+ if (yi < oy - 1)
+ {
+ /* only get next row if still in icon: */
+ get_scaled_row(im_in, x1, si, ix, ox, next);
+ }
+ sifrac = cifrac;
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red += next[xi].red * sifrac;
+ temp[xi].green += next[xi].green * sifrac;
+ temp[xi].blue += next[xi].blue * sifrac;
+ }
+
+ /* write row to output image: */
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+ }
+ }
+}
+
+
+/*
+ * Rescale icons using sort of anti-aliasing technique.
+ */
+static GdkRGBImage *resize_tiles_smooth(
+ GdkRGBImage *im,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2;
+
+ GdkRGBImage *tmp;
+
+ /* Original size */
+ width1 = im->width;
+ height1 = im->height;
+
+ /* Rescaled size */
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ /* Allocate GdkRGBImage for resized tiles */
+ tmp = gdk_rgb_image_new(width2, height2);
+
+ /* Oops */
+ if (tmp == NULL) return (NULL);
+
+ /* Scale each icon */
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
+ {
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
+ {
+ scale_icon(im, tmp, x1, y1, x2, y2,
+ ix, iy, ox, oy);
+ }
+ }
+
+ return tmp;
+}
+
+
+/*
+ * Steven Fuerst's tile resizing code
+ * Taken from Z because I think the algorithm is cool.
+ */
+
+/* 24-bit version - GdkRGB uses 24 bit RGB data internally */
+static void copy_pixels(
+ int wid,
+ int y,
+ int offset,
+ int *xoffsets,
+ GdkRGBImage *old_image,
+ GdkRGBImage *new_image)
+{
+ int i;
+
+ /* Get source and destination */
+ byte *src = &old_image->image[offset * old_image->width * 3];
+ byte *dst = &new_image->image[y * new_image->width * 3];
+
+ /* Copy to the image */
+ for (i = 0; i < wid; i++)
+ {
+#ifdef SUPPORT_GAMMA
+
+ if (gamma_table_ready)
+ {
+ *dst++ = gamma_table[src[3 * xoffsets[i]]];
+ *dst++ = gamma_table[src[3 * xoffsets[i] + 1]];
+ *dst++ = gamma_table[src[3 * xoffsets[i] + 2]];
+
+ continue;
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ *dst++ = src[3 * xoffsets[i]];
+ *dst++ = src[3 * xoffsets[i] + 1];
+ *dst++ = src[3 * xoffsets[i] + 2];
+ }
+}
+
+
+#if 0
+
+/* 32-bit version: it might be useful in the future */
+static void copy_pixels(
+ int wid,
+ int y,
+ int offset,
+ int *xoffsets,
+ GdkRGBImage *old_image,
+ GdkRGBImage *new_image)
+{
+ int i;
+
+ /* Get source and destination */
+ byte *src = &old_image->image[offset * old_image->width * 4];
+ byte *dst = &new_image->image[y * new_image->width * 4];
+
+ /* Copy to the image */
+ for (i = 0; i < wid; i++)
+ {
+ *dst++ = src[4 * xoffsets[i]];
+ *dst++ = src[4 * xoffsets[i] + 1];
+ *dst++ = src[4 * xoffsets[i] + 2];
+ *dst++ = src[4 * xoffsets[i] + 3];
+ }
+}
+
+#endif
+
+
+/*
+ * Resize ix * iy pixel tiles in old to ox * oy pixels
+ * and return a new GdkRGBImage containing the resized tiles
+ */
+static GdkRGBImage *resize_tiles_fast(
+ GdkRGBImage *old_image,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ GdkRGBImage *new_image;
+
+ int old_wid, old_hgt;
+
+ int new_wid, new_hgt;
+
+ int add, remainder, rem_tot, offset;
+
+ int *xoffsets;
+
+ int i;
+
+
+ /* Get the size of the old image */
+ old_wid = old_image->width;
+ old_hgt = old_image->height;
+
+ /* Calculate the size of the new image */
+ new_wid = (old_wid / ix) * ox;
+ new_hgt = (old_hgt / iy) * oy;
+
+ /* Allocate a GdkRGBImage to store resized tiles */
+ new_image = gdk_rgb_image_new(new_wid, new_hgt);
+
+ /* Paranoia */
+ if (new_image == NULL) return (NULL);
+
+ /* now begins the cool part of SF's code */
+
+ /*
+ * Calculate an offsets table, so the transformation
+ * is faster. This is much like the Bresenham algorithm
+ */
+
+ /* Set up x offset table */
+ C_MAKE(xoffsets, new_wid, int);
+
+ /* Initialize line parameters */
+ add = old_wid / new_wid;
+ remainder = old_wid % new_wid;
+
+ /* Start at left */
+ offset = 0;
+
+ /* Half-tile offset so 'line' is centered correctly */
+ rem_tot = new_wid / 2;
+
+ for (i = 0; i < new_wid; i++)
+ {
+ /* Store into the table */
+ xoffsets[i] = offset;
+
+ /* Move to next entry */
+ offset += add;
+
+ /* Take care of fractional part */
+ rem_tot += remainder;
+ if (rem_tot >= new_wid)
+ {
+ rem_tot -= new_wid;
+ offset++;
+ }
+ }
+
+ /* Scan each row */
+
+ /* Initialize line parameters */
+ add = old_hgt / new_hgt;
+ remainder = old_hgt % new_hgt;
+
+ /* Start at left */
+ offset = 0;
+
+ /* Half-tile offset so 'line' is centered correctly */
+ rem_tot = new_hgt / 2;
+
+ for (i = 0; i < new_hgt; i++)
+ {
+ /* Copy pixels to new image */
+ copy_pixels(new_wid, i, offset, xoffsets, old_image, new_image);
+
+ /* Move to next entry */
+ offset += add;
+
+ /* Take care of fractional part */
+ rem_tot += remainder;
+ if (rem_tot >= new_hgt)
+ {
+ rem_tot -= new_hgt;
+ offset++;
+ }
+ }
+
+ /* Free offset table */
+ C_FREE(xoffsets, new_wid, int);
+
+ return (new_image);
+}
+
+
+/*
+ * Resize an image of ix * iy pixels and return a newly allocated
+ * image of ox * oy pixels.
+ */
+static GdkRGBImage *resize_tiles(
+ GdkRGBImage *im,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ GdkRGBImage *result;
+
+ /*
+ * I hope we can always use this with GdkRGB, which uses a 5x5x5
+ * colour cube (125 colours) by default, and resort to dithering
+ * when it can't find good match there or expand the cube, so it
+ * works with 8bpp X servers.
+ */
+ if (smooth_rescaling_request && (ix != ox || iy != oy))
+ {
+ result = resize_tiles_smooth(im, ix, iy, ox, oy);
+ }
+
+ /*
+ * Unless smoothing is requested by user, we use the fast
+ * resizing code.
+ */
+ else
+ {
+ result = resize_tiles_fast(im, ix, iy, ox, oy);
+ }
+
+ /* Return rescaled tiles, or NULL */
+ return (result);
+}
+
+
+/*
+ * Tile loaders - XPM and BMP
+ */
+
+/*
+ * A helper function for the XPM loader
+ *
+ * Read next string delimited by double quotes from
+ * the input stream. Return TRUE on success, FALSE
+ * if it finds EOF or buffer overflow.
+ *
+ * I never mean this to be generic, so its EOF and buffer
+ * overflow behaviour is terribly stupid -- there are no
+ * provisions for recovery.
+ *
+ * CAVEAT: treatment of backslash is not compatible with the standard
+ * C usage XXX XXX XXX XXX
+ */
+static bool read_str(char *buf, u32b len, FILE *f)
+{
+ int c;
+
+ /* Paranoia - Buffer too small */
+ if (len <= 0) return (FALSE);
+
+ /* Find " */
+ while ((c = getc(f)) != '"')
+ {
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+ }
+
+ while (1)
+ {
+ /* Read next char */
+ c = getc(f);
+
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+
+ /* Terminating " */
+ if (c == '"') break;
+
+ /* Escape */
+ if (c == '\\')
+ {
+ /* Use next char */
+ c = getc(f);
+
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+ }
+
+ /* Store character in the buffer */
+ *buf++ = c;
+
+ /* Decrement count */
+ len--;
+
+ /* Buffer full - we have to place a NULL at the end */
+ if (len <= 0) return (FALSE);
+ }
+
+ /* Make a C string if there's room left */
+ if (len > 0) *buf = '\0';
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Remember pixel symbol to RGB colour mappings
+ */
+
+/*
+ * I've forgot the formula, but I remember prime number yields
+ * good results
+ */
+#define HASH_SIZE 19
+
+typedef struct pal_type pal_type;
+
+struct pal_type
+{
+ u32b str;
+ u32b rgb;
+ pal_type *next;
+};
+
+
+/*
+ * A simple, slow and stupid XPM loader
+ */
+static GdkRGBImage *load_xpm(cptr filename)
+{
+ FILE *f;
+ GdkRGBImage *img = NULL;
+ int width, height, colours, chars;
+ int i, j, k;
+ bool ret;
+ pal_type *pal = NULL;
+ pal_type *head[HASH_SIZE];
+ u32b buflen = 0;
+ char *lin = NULL;
+ char buf[1024];
+
+ /* Build path to the XPM file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, filename);
+
+ /* Open it */
+ f = my_fopen(buf, "r");
+
+ /* Oops */
+ if (f == NULL) return (NULL);
+
+ /* Read header */
+ ret = read_str(buf, 1024, f);
+
+ /* Oops */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("Cannot find XPM header");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Parse header */
+ if (4 != sscanf(buf, "%d %d %d %d", &width, &height, &colours, &chars))
+ {
+ /* Notify error */
+ plog("Bad XPM header");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /*
+ * Paranoia - the code can handle upto four letters per pixel,
+ * but such large number of colours certainly requires a smarter
+ * symbol-to-colour mapping algorithm...
+ */
+ if ((width <= 0) || (height <= 0) || (colours <= 0) || (chars <= 0) ||
+ (chars > 2))
+ {
+ /* Notify error */
+ plog("Invalid width/height/depth");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Allocate palette */
+ C_MAKE(pal, colours, pal_type);
+
+ /* Initialise hash table */
+ for (i = 0; i < HASH_SIZE; i++) head[i] = NULL;
+
+ /* Parse palette */
+ for (i = 0; i < colours; i++)
+ {
+ u32b tmp;
+ int h_idx;
+
+ /* Read next string */
+ ret = read_str(buf, 1024, f);
+
+ /* Check I/O result */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("EOF in palette");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Clear symbol code */
+ tmp = 0;
+
+ /* Encode pixel symbol */
+ for (j = 0; j < chars; j++)
+ {
+ tmp = (tmp << 8) | (buf[j] & 0xFF);
+ }
+
+ /* Remember it */
+ pal[i].str = tmp;
+
+ /* Skip spaces */
+ while ((buf[j] == ' ') || (buf[j] == '\t')) j++;
+
+ /* Verify 'c' */
+ if (buf[j] != 'c')
+ {
+ /* Notify error */
+ plog("No 'c' in palette definition");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Advance cursor */
+ j++;
+
+ /* Skip spaces */
+ while ((buf[j] == ' ') || (buf[j] == '\t')) j++;
+
+ /* Hack - Assume 'None' */
+ if (buf[j] == 'N')
+ {
+ /* Angband always uses black background */
+ pal[i].rgb = 0x000000;
+ }
+
+ /* Read colour */
+ else if ((1 != sscanf(&buf[j], "#%06lX", &tmp)) &&
+ (1 != sscanf(&buf[j], "#%06lx", &tmp)))
+ {
+ /* Notify error */
+ plog("Badly formatted colour");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Remember it */
+ pal[i].rgb = tmp;
+
+ /* Store it in hash table as well */
+ h_idx = pal[i].str % HASH_SIZE;
+
+ /* Link the entry */
+ pal[i].next = head[h_idx];
+ head[h_idx] = &pal[i];
+ }
+
+ /* Allocate image */
+ img = gdk_rgb_image_new(width, height);
+
+ /* Oops */
+ if (img == NULL)
+ {
+ /* Notify error */
+ plog("Cannot allocate image");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Calculate buffer length */
+ buflen = width * chars + 1;
+
+ /* Allocate line buffer */
+ C_MAKE(lin, buflen, char);
+
+ /* For each row */
+ for (i = 0; i < height; i++)
+ {
+ /* Read a row of image data */
+ ret = read_str(lin, buflen, f);
+
+ /* Oops */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("EOF in middle of image data");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* For each column */
+ for (j = 0; j < width; j++)
+ {
+ u32b tmp;
+ pal_type *h_ptr;
+
+ /* Clear encoded pixel */
+ tmp = 0;
+
+ /* Encode pixel symbol */
+ for (k = 0; k < chars; k++)
+ {
+ tmp = (tmp << 8) | (lin[j * chars + k] & 0xFF);
+ }
+
+ /* Find colour */
+ for (h_ptr = head[tmp % HASH_SIZE];
+ h_ptr != NULL;
+ h_ptr = h_ptr->next)
+ {
+ /* Found a match */
+ if (h_ptr->str == tmp) break;
+ }
+
+ /* No match found */
+ if (h_ptr == NULL)
+ {
+ /* Notify error */
+ plog("Invalid pixel symbol");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Draw it */
+ gdk_rgb_image_put_pixel(
+ img,
+ j,
+ i,
+ h_ptr->rgb);
+ }
+ }
+
+ /* Close file */
+ my_fclose(f);
+
+ /* Free line buffer */
+ C_FREE(lin, buflen, char);
+
+ /* Free palette */
+ C_FREE(pal, colours, pal_type);
+
+ /* Return result */
+ return (img);
+
+oops:
+
+ /* Close file */
+ my_fclose(f);
+
+ /* Free image */
+ if (img) gdk_rgb_image_destroy(img);
+
+ /* Free line buffer */
+ if (lin) C_FREE(lin, buflen, char);
+
+ /* Free palette */
+ if (pal) C_FREE(pal, colours, pal_type);
+
+ /* Failure */
+ return (NULL);
+}
+
+
+/*
+ * A BMP loader, yet another duplication of maid-x11.c functions.
+ *
+ * Another duplication, again because of different image format and
+ * avoidance of colour allocation.
+ *
+ * XXX XXX XXX XXX Should avoid using a propriatary and closed format.
+ * Since it's much bigger than gif that was used before, why don't
+ * we switch to XPM? NetHack does. Well, NH has always been much
+ * closer to the GNU/Un*x camp and it's GPL'ed quite early...
+ *
+ * The names and naming convention are worse than the worst I've ever
+ * seen, so I deliberately changed them to fit well with the rest of
+ * the code. Or are they what xx calls them? If it's the case, there's
+ * no reason to follow *their* words.
+ */
+
+/*
+ * BMP file header
+ */
+typedef struct bmp_file_type bmp_file_type;
+
+struct bmp_file_type
+{
+ u16b type;
+ u32b size;
+ u16b reserved1;
+ u16b reserved2;
+ u32b offset;
+};
+
+
+/*
+ * BMP file information fields
+ */
+typedef struct bmp_info_type bmp_info_type;
+
+struct bmp_info_type
+{
+ u32b size;
+ u32b width;
+ u32b height;
+ u16b planes;
+ u16b bit_count;
+ u32b compression;
+ u32b size_image;
+ u32b x_pels_per_meter;
+ u32b y_pels_per_meter;
+ u32b colors_used;
+ u32b color_importand;
+};
+
+/*
+ * "RGBQUAD" type.
+ */
+typedef struct rgb_quad_type rgb_quad_type;
+
+struct rgb_quad_type
+{
+ unsigned char b, g, r;
+ unsigned char filler;
+};
+
+
+/*** Helper functions for system independent file loading. ***/
+
+static byte get_byte(FILE *fff)
+{
+ /* Get a character, and return it */
+ return (getc(fff) & 0xFF);
+}
+
+static void rd_byte(FILE *fff, byte *ip)
+{
+ *ip = get_byte(fff);
+}
+
+static void rd_u16b(FILE *fff, u16b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u16b)(get_byte(fff)) << 8);
+}
+
+static void rd_u32b(FILE *fff, u32b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u32b)(get_byte(fff)) << 8);
+ (*ip) |= ((u32b)(get_byte(fff)) << 16);
+ (*ip) |= ((u32b)(get_byte(fff)) << 24);
+}
+
+
+/*
+ * Read a BMP file (a certain trademark nuked)
+ *
+ * This function replaces the old ReadRaw and RemapColors functions.
+ *
+ * Assumes that the bitmap has a size such that no padding is needed in
+ * various places. Currently only handles bitmaps with 3 to 256 colors.
+ */
+GdkRGBImage *load_bmp(cptr filename)
+{
+ FILE *f;
+
+ char path[1024];
+
+ bmp_file_type file_hdr;
+ bmp_info_type info_hdr;
+
+ GdkRGBImage *result = NULL;
+
+ int ncol;
+
+ int i;
+
+ u32b x, y;
+
+ guint32 colour_pixels[256];
+
+
+ /* Build the path to the bmp file */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_GRAF, filename);
+
+ /* Open the BMP file */
+ f = fopen(path, "r");
+
+ /* No such file */
+ if (f == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Read the "bmp_file_type" */
+ rd_u16b(f, &file_hdr.type);
+ rd_u32b(f, &file_hdr.size);
+ rd_u16b(f, &file_hdr.reserved1);
+ rd_u16b(f, &file_hdr.reserved2);
+ rd_u32b(f, &file_hdr.offset);
+
+ /* Read the "bmp_info_type" */
+ rd_u32b(f, &info_hdr.size);
+ rd_u32b(f, &info_hdr.width);
+ rd_u32b(f, &info_hdr.height);
+ rd_u16b(f, &info_hdr.planes);
+ rd_u16b(f, &info_hdr.bit_count);
+ rd_u32b(f, &info_hdr.compression);
+ rd_u32b(f, &info_hdr.size_image);
+ rd_u32b(f, &info_hdr.x_pels_per_meter);
+ rd_u32b(f, &info_hdr.y_pels_per_meter);
+ rd_u32b(f, &info_hdr.colors_used);
+ rd_u32b(f, &info_hdr.color_importand);
+
+ /* Verify the header */
+ if (feof(f) ||
+ (file_hdr.type != 19778) ||
+ (info_hdr.size != 40))
+ {
+ plog(format("Incorrect BMP file format %s", filename));
+ fclose(f);
+ return (NULL);
+ }
+
+ /*
+ * The two headers above occupy 54 bytes total
+ * The "offset" field says where the data starts
+ * The "colors_used" field does not seem to be reliable
+ */
+
+ /* Compute number of colors recorded */
+ ncol = (file_hdr.offset - 54) / 4;
+
+ for (i = 0; i < ncol; i++)
+ {
+ rgb_quad_type clr;
+
+ /* Read an "rgb_quad_type" */
+ rd_byte(f, &clr.b);
+ rd_byte(f, &clr.g);
+ rd_byte(f, &clr.r);
+ rd_byte(f, &clr.filler);
+
+ /* Remember the pixel */
+ colour_pixels[i] = (clr.r << 16) | (clr.g << 8) | (clr.b);
+ }
+
+ /* Allocate GdkRGBImage large enough to store the image */
+ result = gdk_rgb_image_new(info_hdr.width, info_hdr.height);
+
+ /* Failure */
+ if (result == NULL)
+ {
+ fclose(f);
+ return (NULL);
+ }
+
+ for (y = 0; y < info_hdr.height; y++)
+ {
+ u32b y2 = info_hdr.height - y - 1;
+
+ for (x = 0; x < info_hdr.width; x++)
+ {
+ int ch = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ if (info_hdr.bit_count == 24)
+ {
+ int c3, c2 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ c3 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ /* Draw the pixel */
+ gdk_rgb_image_put_pixel(
+ result,
+ x,
+ y2,
+ (ch << 16) | (c2 << 8) | (c3));
+ }
+ else if (info_hdr.bit_count == 8)
+ {
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch]);
+ }
+ else if (info_hdr.bit_count == 4)
+ {
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch / 16]);
+ x++;
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch % 16]);
+ }
+ else
+ {
+ /* Technically 1 bit is legal too */
+ plog(format("Illegal bit count %d in %s",
+ info_hdr.bit_count, filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+ }
+ }
+
+ fclose(f);
+
+ return result;
+}
+
+
+/*
+ * Try to load an XPM file, or a BMP file if it fails
+ *
+ * Choice of file format may better be made yet another option XXX
+ */
+static GdkRGBImage *load_tiles(cptr basename)
+{
+ char buf[32];
+ GdkRGBImage *img;
+
+ /* build xpm file name */
+ strnfmt(buf, 32, "%s.xpm", basename);
+
+ /* Try to load it */
+ img = load_xpm(buf);
+
+ /* OK */
+ if (img) return (img);
+
+ /* Try again for a bmp file */
+ strnfmt(buf, 32, "%s.bmp", basename);
+
+ /* Try loading it */
+ img = load_bmp(buf);
+
+ /* Return result, success or failure */
+ return (img);
+}
+
+
+/*
+ * Free all tiles and graphics buffers associated with windows
+ *
+ * This is conspirator of graf_init() below, sharing its inefficiency
+ */
+static void graf_nuke()
+{
+ int i;
+
+ term_data *td;
+
+
+ /* Nuke all terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access term_data structure */
+ td = &data[i];
+
+ /* Disable graphics */
+ td->t.higher_pict = FALSE;
+
+ /* Free previously allocated tiles */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Free previously allocated transparency buffer */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Forget stale pointer */
+ td->trans_buf = NULL;
+
+# endif /* USE_TRANSPARENCY */
+
+ }
+}
+
+
+/*
+ * Load tiles, scale them to current font size, and store a pointer
+ * to them in a term_data structure for each term.
+ *
+ * XXX XXX XXX This is a terribly stupid quick hack.
+ *
+ * XXX XXX XXX Windows using the same font should share resized tiles
+ */
+static bool graf_init(
+ cptr filename,
+ int tile_wid,
+ int tile_hgt)
+{
+ term_data *td;
+
+ bool result;
+
+ GdkRGBImage *raw_tiles, *scaled_tiles;
+
+# ifdef USE_TRANSPARENCY
+ GdkRGBImage *buffer;
+# endif /* USE_TRANSPARENCY */
+
+ int i;
+
+
+ /* Paranoia */
+ if (filename == NULL) return (FALSE);
+
+ /* Load tiles */
+ raw_tiles = load_tiles(filename);
+
+ /* Oops */
+ if (raw_tiles == NULL)
+ {
+ /* Clean up */
+ graf_nuke();
+
+ /* Failure */
+ return (FALSE);
+ }
+
+ /* Calculate and remember numbers of rows and columns */
+ tile_rows = raw_tiles->height / tile_hgt;
+ tile_cols = raw_tiles->width / tile_wid;
+
+ /* Be optimistic */
+ result = TRUE;
+
+
+ /*
+ * (Re-)init each term
+ * XXX It might help speeding this up to avoid doing so if a window
+ * doesn't need graphics (e.g. inventory/equipment and message recall).
+ */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access term_data */
+ td = &data[i];
+
+ /* Shouldn't waste anything for unused terms */
+ if (!td->shown) continue;
+
+ /* Enable graphics */
+ td->t.higher_pict = TRUE;
+
+ /* See if we need rescaled tiles XXX */
+ if ((td->tiles == NULL) ||
+ (td->tiles->width != td->tile_wid * tile_cols) ||
+ (td->tiles->height != td->tile_hgt * tile_rows))
+ {
+ /* Free old tiles if present */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+ /* Scale the tiles to current font bounding rect */
+ scaled_tiles = resize_tiles(
+ raw_tiles,
+ tile_wid, tile_hgt,
+ td->tile_wid, td->tile_hgt);
+
+ /* Oops */
+ if (scaled_tiles == NULL)
+ {
+ /* Failure */
+ result = FALSE;
+
+ break;
+ }
+
+ /* Store it */
+ td->tiles = scaled_tiles;
+ }
+
+# ifdef USE_TRANSPARENCY
+
+ /* See if we have to (re)allocate a new buffer XXX */
+ if ((td->trans_buf == NULL) ||
+ (td->trans_buf->width != td->tile_wid) ||
+ (td->trans_buf->height != td->tile_hgt))
+ {
+ /* Free old buffer if present */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Forget pointer */
+ td->trans_buf = NULL;
+
+ /* Allocate a new buffer */
+ buffer = gdk_rgb_image_new(td->tile_wid, td->tile_hgt);
+
+ /* Oops */
+ if (buffer == NULL)
+ {
+ /* Failure */
+ result = FALSE;
+
+ break;
+ }
+
+ /* Store it */
+ td->trans_buf = buffer;
+ }
+
+ /*
+ * Giga-Hack - assume top left corner of 0x86/0x80 should be
+ * in the background colour XXX XXX XXX XXX
+ */
+ td->bg_pixel = gdk_rgb_image_get_pixel(
+ raw_tiles,
+ 0,
+ tile_hgt * 6);
+
+# endif /* USE_TRANSPARENCY */
+
+ }
+
+
+ /* Alas, we need to free wasted images */
+ if (result == FALSE) graf_nuke();
+
+ /* We don't need the raw image any longer */
+ gdk_rgb_image_destroy(raw_tiles);
+
+ /* Report success or failure */
+ return (result);
+}
+
+
+/*
+ * React to various changes in graphics mode settings
+ *
+ * It is *not* a requirement for tiles to have same pixel width and height.
+ * The program can work with any conbinations of graf_wid and graf_hgt
+ * (oops, they must be representable by u16b), as long as they are lesser
+ * or equal to 32 if you use smooth rescaling.
+ */
+static void init_graphics(void)
+{
+ cptr tile_name;
+
+ u16b graf_wid = 0, graf_hgt = 0;
+
+
+ /* No graphics requests are made - Can't this be simpler? XXX XXX */
+ if ((graf_mode_request == graf_mode) &&
+ (smooth_rescaling_request == smooth_rescaling) &&
+ !resize_request) return;
+
+ /* Prevent further unsolicited reaction */
+ resize_request = FALSE;
+
+
+ /* Dispose unusable old tiles - awkward... XXX XXX */
+ if ((graf_mode_request == GRAF_MODE_NONE) ||
+ (graf_mode_request != graf_mode) ||
+ (smooth_rescaling_request != smooth_rescaling)) graf_nuke();
+
+
+ /* Setup parameters according to request */
+ switch (graf_mode_request)
+ {
+ /* ASCII - no graphics whatsoever */
+ default:
+ case GRAF_MODE_NONE:
+ {
+ tile_name = NULL;
+ use_graphics = arg_graphics = FALSE;
+
+ break;
+ }
+
+ /*
+ * 8x8 tiles originally collected for the Amiga port
+ * from several contributers by Lars Haugseth, converted
+ * to 256 colours and expanded by the Z devteam
+ *
+ * Use the "old" tile assignments
+ *
+ * Dawnmist is working on it for ToME
+ */
+ case GRAF_MODE_OLD:
+ {
+ tile_name = "8x8";
+ graf_wid = graf_hgt = 8;
+ ANGBAND_GRAF = "old";
+ use_graphics = arg_graphics = TRUE;
+
+ break;
+ }
+
+ /*
+ * Adam Bolt's 16x16 tiles
+ * "new" tile assignments
+ * It is updated for ToME by Andreas Koch
+ */
+ case GRAF_MODE_NEW:
+ {
+ tile_name = "16x16";
+ graf_wid = graf_hgt = 16;
+ ANGBAND_GRAF = "new";
+ use_graphics = arg_graphics = TRUE;
+
+ break;
+ }
+ }
+
+
+ /* load tiles and set them up if tiles are requested */
+ if ((graf_mode_request != GRAF_MODE_NONE) &&
+ !graf_init(tile_name, graf_wid, graf_hgt))
+ {
+ /* Oops */
+ plog("Cannot initialize graphics");
+
+ /* reject requests */
+ graf_mode_request = GRAF_MODE_NONE;
+ smooth_rescaling_request = smooth_rescaling;
+
+ /* reset graphics flags */
+ use_graphics = arg_graphics = FALSE;
+ }
+
+ /* Update current graphics mode */
+ graf_mode = graf_mode_request;
+ smooth_rescaling = smooth_rescaling_request;
+
+ /* Reset visuals */
+#ifndef ANG281_RESET_VISUALS
+ reset_visuals(TRUE);
+#else
+ reset_visuals();
+#endif /* !ANG281_RESET_VISUALS */
+}
+
+#endif /* USE_GRAPHICS */
+
+
+
+
+/**** Term package support routines ****/
+
+
+/*
+ * Free data used by a term
+ */
+static void Term_nuke_gtk(term *t)
+{
+ term_data *td = t->data;
+
+
+ /* Free name */
+ if (td->name) string_free(td->name);
+
+ /* Forget it */
+ td->name = NULL;
+
+ /* Free font */
+ if (td->font) gdk_font_unref(td->font);
+
+ /* Forget it */
+ td->font = NULL;
+
+ /* Free backing store */
+ if (td->backing_store) gdk_pixmap_unref(td->backing_store);
+
+ /* Forget it too */
+ td->backing_store = NULL;
+
+#ifdef USE_GRAPHICS
+
+ /* Free tiles */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Free transparency buffer */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Amnesia */
+ td->trans_buf = NULL;
+
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+}
+
+
+/*
+ * Erase the whole term.
+ */
+static errr Term_clear_gtk(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Clear the area */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->drawing_area->style->black_gc,
+ 1,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, 0, 0, td->cols, td->rows);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters.
+ */
+static errr Term_wipe_gtk(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Fill the area with the background colour */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->drawing_area->style->black_gc,
+ TRUE,
+ x * td->font_wid,
+ y * td->font_hgt,
+ n * td->font_wid,
+ td->font_hgt);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some textual characters.
+ */
+static errr Term_text_gtk(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(td, a);
+
+ /* Clear the line */
+ Term_wipe_gtk(x, y, n);
+
+ /* Draw the text to the window */
+ gdk_draw_text(
+ TERM_DATA_DRAWABLE(td),
+ td->font,
+ td->gc,
+ x * td->font_wid,
+ td->font->ascent + y * td->font_hgt,
+ s,
+ n);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw software cursor at (x, y)
+ */
+static errr Term_curs_gtk(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+ int cells = 1;
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(td, TERM_YELLOW);
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Adjust it if wide tiles are requested */
+ if (use_bigtile &&
+ (x + 1 < Term->wid) &&
+ (Term->old->a[y][x + 1] == 255))
+ {
+ cells = 2;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ /* Draw the software cursor */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->gc,
+ FALSE,
+ x * td->font_wid,
+ y * td->font_hgt,
+ td->font_wid * cells - 1,
+ td->font_hgt - 1);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, cells, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+# ifdef USE_TRANSPARENCY
+
+/*
+ * XXX XXX Low level graphics helper
+ * Draw a tile at (s_x, s_y) over one at (t_x, t_y) and store the
+ * result in td->trans_buf
+ *
+ * XXX XXX Even if CPU's are faster than necessary these days,
+ * this should be made inline. Or better, there should be an API
+ * to take advantage of graphics hardware. They almost always have
+ * assortment of builtin bitblt's...
+ */
+static void overlay_tiles_2(
+ term_data *td,
+ int s_x, int s_y,
+ int t_x, int t_y)
+{
+ guint32 pix;
+ int x, y;
+
+
+ /* Process each row */
+ for (y = 0; y < td->tile_hgt; y++)
+ {
+ /* Process each column */
+ for (x = 0; x < td->tile_wid; x++)
+ {
+ /* Get an overlay pixel */
+ pix = gdk_rgb_image_get_pixel(td->tiles, s_x + x, s_y + y);
+
+ /* If it's in background color, use terrain instead */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, t_x + x, t_y + y);
+
+ /* Store the result in trans_buf */
+ gdk_rgb_image_put_pixel(td->trans_buf, x, y, pix);
+ }
+ }
+}
+
+
+# ifdef USE_EGO_GRAPHICS
+
+/*
+ * XXX XXX Low level graphics helper
+ * Draw a tile at (e_x, e_y) over one at (s_x, s_y) over another one
+ * at (t_x, t_y) and store the result in td->trans_buf
+ *
+ * XXX XXX The same comment applies as that for the above...
+ */
+static void overlay_tiles_3(
+ term_data *td,
+ int e_x, int e_y,
+ int s_x, int s_y,
+ int t_x, int t_y)
+{
+ guint32 pix;
+ int x, y;
+
+
+ /* Process each row */
+ for (y = 0; y < td->tile_hgt; y++)
+ {
+ /* Process each column */
+ for (x = 0; x < td->tile_wid; x++)
+ {
+ /* Get an overlay pixel */
+ pix = gdk_rgb_image_get_pixel(td->tiles, e_x + x, e_y + y);
+
+ /*
+ * If it's background colour, try to use one from
+ * the second layer
+ */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, s_x + x, s_y + y);
+
+ /*
+ * If it's background colour again, fall back to
+ * the terrain layer
+ */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, t_x + x, t_y + y);
+
+ /* Store the pixel in trans_buf */
+ gdk_rgb_image_put_pixel(td->trans_buf, x, y, pix);
+ }
+ }
+}
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Draw "n" tiles/characters starting at (x,y)
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp,
+ const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+ int d_x, d_y;
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Hack - remember real number of columns affected XXX XXX XXX */
+ int cols;
+
+# endif /* USE_DOUBLE_TILES */
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Top left corner of the destination rect */
+ d_x = x * td->font_wid;
+ d_y = y * td->font_hgt;
+
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Reset column counter */
+ cols = 0;
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Scan the input */
+ for (i = 0; i < n; i++)
+ {
+ byte a;
+ char c;
+ int s_x, s_y;
+
+# ifdef USE_TRANSPARENCY
+
+ byte ta;
+ char tc;
+ int t_x, t_y;
+
+# ifdef USE_EGO_GRAPHICS
+
+ byte ea;
+ char ec;
+ int e_x = 0, e_y = 0;
+ bool has_overlay;
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+
+ /* Grid attr/char */
+ a = *ap++;
+ c = *cp++;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Terrain attr/char */
+ ta = *tap++;
+ tc = *tcp++;
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Overlay attr/char */
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+ /* Row and Col */
+ s_y = (((byte)a & 0x7F) % tile_rows) * td->tile_hgt;
+ s_x = (((byte)c & 0x7F) % tile_cols) * td->tile_wid;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Terrain Row and Col */
+ t_y = (((byte)ta & 0x7F) % tile_rows) * td->tile_hgt;
+ t_x = (((byte)tc & 0x7F) % tile_cols) * td->tile_wid;
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Overlay Row and Col */
+ if (has_overlay)
+ {
+ e_y = (((byte)ea & 0x7F) % tile_rows) * td->tile_hgt;
+ e_x = (((byte)ec & 0x7F) % tile_cols) * td->tile_wid;
+ }
+
+# endif /* USE_EGO_GRAPHICS */
+
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Hack -- a filler for wide tile */
+ if (use_bigtile && (a == 255))
+ {
+ /* Advance */
+ d_x += td->font_wid;
+
+ /* Ignore */
+ continue;
+ }
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Optimise the common case: terrain == obj/mons */
+ if (!use_transparency ||
+ ((s_x == t_x) && (s_y == t_y)))
+ {
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* The simplest possible case - no overlay */
+ if (!has_overlay)
+ {
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+ }
+
+ /* We have to draw overlay... */
+ else
+ {
+ /* Overlay */
+ overlay_tiles_2(td, e_x, e_y, s_x, s_y);
+
+ /* And draw the result */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->trans_buf,
+ 0, 0,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+ /* Hack -- Prevent potential display problem */
+ gdk_flush();
+ }
+
+# else /* USE_EGO_GRAPHICS */
+
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+# endif /* USE_EGO_GRAPHICS */
+
+ }
+
+ /*
+ * Since there's no masking bitblt in X,
+ * we have to do that manually...
+ */
+ else
+ {
+
+# ifndef USE_EGO_GRAPHICS
+
+ /* Draw mon/PC/obj over terrain */
+ overlay_tiles_2(td, s_x, s_y, t_x, t_y);
+
+# else /* !USE_EGO_GRAPHICS */
+
+ /* No overlay */
+ if (!has_overlay)
+ {
+ /* Build terrain + masked overlay image */
+ overlay_tiles_2(td, s_x, s_y, t_x, t_y);
+ }
+
+ /* With overlay */
+ else
+ {
+ /* Ego over mon/PC over terrain */
+ overlay_tiles_3(td, e_x, e_y, s_x, s_y,
+ t_x, t_y);
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+ /* Draw it */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->trans_buf,
+ 0, 0,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+ /* Hack -- Prevent potential display problem */
+ gdk_flush();
+ }
+
+# else /* USE_TRANSPARENCY */
+
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+# endif /* USE_TRANSPARENCY */
+
+ /*
+ * Advance x-coordinate - wide font fillers are taken care of
+ * before entering the tile drawing code.
+ */
+ d_x += td->font_wid;
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Add up *real* number of columns updated XXX XXX XXX */
+ cols += use_bigtile ? 2 : 1;
+
+# endif /* USE_DOUBLE_TILES */
+ }
+
+# ifndef USE_DOUBLE_TILES
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+# else
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, cols, 1);
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Process an event, if there's none block when wait is set true,
+ * return immediately otherwise.
+ */
+static void CheckEvent(bool wait)
+{
+ /* Process an event */
+ (void)gtk_main_iteration_do(wait);
+}
+
+
+/*
+ * Process all pending events (without blocking)
+ */
+static void DrainEvents(void)
+{
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_gtk(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Beep */
+ gdk_beep();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush the output */
+ case TERM_XTRA_FRESH:
+ {
+ /* Flush pending X requests - almost always no-op */
+ gdk_flush();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ /* Process a pending event if there's one */
+ CheckEvent(FALSE);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process Events */
+ case TERM_XTRA_EVENT:
+ {
+ /* Process an event */
+ CheckEvent(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush the events */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Process all pending events */
+ DrainEvents();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL:
+ return (0);
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ return (Term_clear_gtk());
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ /* sleep for v milliseconds */
+ usleep(v * 1000);
+
+ /* Done */
+ return (0);
+ }
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory) return (1);
+
+ while ((entry = readdir(directory)) != NULL)
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if ((stat(file, &filedata) == 0) && S_ISDIR(filedata.st_mode))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] =
+ string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN: gtk_window_set_title(GTK_WINDOW(data[0].window), angband_term_name[0]); return (0);
+
+ /* React to changes */
+ case TERM_XTRA_REACT:
+ {
+ /* (re-)init colours */
+ init_colours();
+
+#ifdef USE_GRAPHICS
+
+ /* Initialise graphics */
+ init_graphics();
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return (0);
+ }
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+
+
+/**** Event handlers ****/
+
+
+/*
+ * Operation overkill
+ * Verify term size info - just because the other windowing ports have this
+ */
+static void term_data_check_size(term_data *td)
+{
+ /* Enforce minimum window size */
+ if (td == &data[0])
+ {
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+ }
+ else
+ {
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Paranoia - Enforce maximum size allowed by the term package */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+}
+
+
+/*
+ * Enforce these size constraints within Gtk/Gdk
+ * These increments are nice, because you can see numbers of rows/cols
+ * while you resize a term.
+ */
+static void term_data_set_geometry_hints(term_data *td)
+{
+ GdkGeometry geometry;
+
+ /* Resizing is character size oriented */
+ geometry.width_inc = td->font_wid;
+ geometry.height_inc = td->font_hgt;
+
+ /* Enforce minimum size - the main window */
+ if (td == &data[0])
+ {
+ geometry.min_width = 80 * td->font_wid;
+ geometry.min_height = 24 * td->font_hgt;
+ }
+
+ /* Subwindows can be much smaller */
+ else
+ {
+ geometry.min_width = 1 * td->font_wid;
+ geometry.min_height = 1 * td->font_hgt;
+ }
+
+ /* Enforce term package's hard limit */
+ geometry.max_width = 255 * td->font_wid;
+ geometry.max_height = 255 * td->font_hgt;
+
+ /* This affects geometry display while we resize a term */
+ geometry.base_width = 0;
+ geometry.base_height = 0;
+
+ /* Give the window a new set of resizing hints */
+ gtk_window_set_geometry_hints(GTK_WINDOW(td->window),
+ td->drawing_area, &geometry,
+ GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE
+ | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC);
+}
+
+
+/*
+ * (Re)allocate a backing store for the window
+ */
+static void term_data_set_backing_store(term_data *td)
+{
+ /* Paranoia */
+ if (!GTK_WIDGET_REALIZED(td->drawing_area)) return;
+
+ /* Free old one if we cannot use it any longer */
+ if (td->backing_store)
+ {
+ int wid, hgt;
+
+ /* Retrive the size of the old backing store */
+ gdk_window_get_size(td->backing_store, &wid, &hgt);
+
+ /* Continue using it if it's the same with desired size */
+ if (use_backing_store &&
+ (td->cols * td->font_wid == wid) &&
+ (td->rows * td->font_hgt == hgt)) return;
+
+ /* Free it */
+ gdk_pixmap_unref(td->backing_store);
+
+ /* Forget the pointer */
+ td->backing_store = NULL;
+ }
+
+ /* See user preference */
+ if (use_backing_store)
+ {
+ /* Allocate new backing store */
+ td->backing_store = gdk_pixmap_new(
+ td->drawing_area->window,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt,
+ -1);
+
+ /* Oops - but we can do without it */
+ g_return_if_fail(td->backing_store != NULL);
+
+ /* Clear the backing store */
+ gdk_draw_rectangle(
+ td->backing_store,
+ td->drawing_area->style->black_gc,
+ TRUE,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+ }
+}
+
+
+/*
+ * Save game only when it's safe to do so
+ */
+static void save_game_gtk(void)
+{
+ /* We have nothing to save, yet */
+ if (!game_in_progress || !character_generated) return;
+
+ /* It isn't safe to save game now */
+ if (!inkey_flag || !can_save)
+ {
+ plog("You may not save right now.");
+ return;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifdef ZANG_SAVE_GAME
+ /* Also for OAngband - the parameter tells if it's autosave */
+ do_cmd_save_game(FALSE);
+#else
+/* Everything else */
+ do_cmd_save_game();
+#endif /* ZANG_SAVE_GAME */
+}
+
+
+/*
+ * Display message in a modal dialog
+ */
+static void gtk_message(cptr msg)
+{
+ GtkWidget *dialog, *label, *ok_button;
+
+ /* Create the widgets */
+ dialog = gtk_dialog_new();
+ g_assert(dialog != NULL);
+
+ label = gtk_label_new(msg);
+ g_assert(label != NULL);
+
+ ok_button = gtk_button_new_with_label("OK");
+ g_assert(ok_button != NULL);
+
+ /* Ensure that the dialogue box is destroyed when OK is clicked */
+ gtk_signal_connect_object(
+ GTK_OBJECT(ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)dialog);
+ gtk_container_add(
+ GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ ok_button);
+
+ /* Add the label, and show the dialog */
+ gtk_container_add(
+ GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
+ label);
+
+ /* And make it modal */
+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+
+ /* Show the dialog */
+ gtk_widget_show_all(dialog);
+}
+
+
+/*
+ * Hook to tell the user something important
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning message */
+ gtk_message(str);
+}
+
+
+/*
+ * Process File-Quit menu command
+ */
+static void quit_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* Save current game */
+ save_game_gtk();
+
+ /* It's done */
+ quit(NULL);
+}
+
+
+/*
+ * Process File-Save menu command
+ */
+static void save_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* Save current game */
+ save_game_gtk();
+}
+
+
+/*
+ * Handle destruction of the Angband window
+ */
+static void destroy_main_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* This allows for cheating, but... */
+ quit(NULL);
+}
+
+
+/*
+ * Handle destruction of Subwindows
+ */
+static void destroy_sub_event_handler(
+ GtkWidget *window,
+ gpointer user_data)
+{
+ /* Hide the window */
+ gtk_widget_hide_all(window);
+}
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Process File-New menu command
+ */
+static void new_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ if (game_in_progress)
+ {
+ plog("You can't start a new game while you're still playing!");
+ return;
+ }
+
+ /* The game is in progress */
+ game_in_progress = TRUE;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play game */
+ play_game(TRUE);
+
+ /* Houseclearing */
+ cleanup_angband();
+
+ /* Done */
+ quit(NULL);
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * Load fond specified by an XLFD fontname and
+ * set up related term_data members
+ */
+static void load_font(term_data *td, cptr fontname)
+{
+ GdkFont *old = td->font;
+
+ /* Load font */
+ td->font = gdk_font_load(fontname);
+
+ if (td->font)
+ {
+ /* Free the old font */
+ if (old) gdk_font_unref(old);
+ }
+ else
+ {
+ /* Oops, but we can still use the old one */
+ td->font = old;
+ }
+
+ /* Calculate the size of the font XXX */
+ td->font_wid = gdk_char_width(td->font, '@');
+ td->font_hgt = td->font->ascent + td->font->descent;
+
+#ifndef USE_DOUBLE_TILES
+
+ /* Use the current font size for tiles as well */
+ td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+#else /* !USE_DOUBLE_TILES */
+
+ /* Calculate the size of tiles */
+ if (use_bigtile && (td == &data[0])) td->tile_wid = td->font_wid * 2;
+ else td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+#endif /* !USE_DOUBLE_TILES */
+}
+
+
+/*
+ * React to OK button press in font selection dialogue
+ */
+static void font_ok_callback(
+ GtkWidget *widget,
+ GtkWidget *font_selector)
+{
+ gchar *fontname;
+ term_data *td;
+
+ td = gtk_object_get_data(GTK_OBJECT(font_selector), "term_data");
+
+ g_assert(td != NULL);
+
+ /* Retrieve font name from player's selection */
+ fontname = gtk_font_selection_dialog_get_font_name(
+ GTK_FONT_SELECTION_DIALOG(font_selector));
+
+ /* Leave unless selection was valid */
+ if (fontname == NULL) return;
+
+ /* Load font and update font size info */
+ load_font(td, fontname);
+
+ /* Hack - Hide the window - finally found the trick... */
+ gtk_widget_hide_all(td->window);
+
+ /* Resizes the drawing area */
+ gtk_drawing_area_size(
+ GTK_DRAWING_AREA(td->drawing_area),
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Update the geometry hints for the window */
+ term_data_set_geometry_hints(td);
+
+ /* Reallocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Hack - Show the window */
+ gtk_widget_show_all(td->window);
+
+#ifdef USE_GRAPHICS
+
+ /* We have to resize tiles when we are in graphics mode */
+ resize_request = TRUE;
+
+#endif /* USE_GRAPHICS */
+
+ /* Hack - force redraw */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Process Options-Font-* menu command
+ */
+static void change_font_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *font_selector;
+
+ gchar *spacings[] = { "c", "m", NULL };
+
+ font_selector = gtk_font_selection_dialog_new("Select font");
+
+ gtk_object_set_data(
+ GTK_OBJECT(font_selector),
+ "term_data",
+ user_data);
+
+ /* Filter to show only fixed-width fonts */
+ gtk_font_selection_dialog_set_filter(
+ GTK_FONT_SELECTION_DIALOG(font_selector),
+ GTK_FONT_FILTER_BASE,
+ GTK_FONT_ALL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ spacings,
+ NULL);
+
+ gtk_signal_connect(
+ GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->ok_button),
+ "clicked",
+ font_ok_callback,
+ (gpointer)font_selector);
+
+ /*
+ * Ensure that the dialog box is destroyed when the user clicks
+ * a button.
+ */
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)font_selector);
+
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)font_selector);
+
+ gtk_widget_show(GTK_WIDGET(font_selector));
+}
+
+
+/*
+ * Process Terms-* menu command - hide/show terminal window
+ */
+static void term_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* We don't mess with the Angband window */
+ if (td == &data[0]) return;
+
+ /* It's shown */
+ if (td->shown)
+ {
+ /* Hide the window */
+ gtk_widget_hide_all(td->window);
+ }
+
+ /* It's hidden */
+ else
+ {
+ /* Show the window */
+ gtk_widget_show_all(td->window);
+ }
+}
+
+
+/*
+ * Toggles the boolean value of use_backing_store and
+ * setup / remove backing store for each term
+ */
+static void change_backing_store_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ int i;
+
+ /* Toggle the backing store mode */
+ use_backing_store = !use_backing_store;
+
+ /* Reset terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data_set_backing_store(&data[i]);
+ }
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Set graf_mode_request according to user selection,
+ * and let Term_xtra react to the change.
+ */
+static void change_graf_mode_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* Set request according to user selection */
+ graf_mode_request = (int)user_data;
+
+ /*
+ * Hack - force redraw
+ * This induces a call to Term_xtra(TERM_XTRA_REACT, 0) as well
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Set dither_mode according to user selection
+ */
+static void change_dith_mode_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* Set request according to user selection */
+ dith_mode = (int)user_data;
+
+ /*
+ * Hack - force redraw
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Toggles the graphics tile scaling mode (Fast/Smooth)
+ */
+static void change_smooth_mode_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* (Try to) toggle the smooth rescaling mode */
+ smooth_rescaling_request = !smooth_rescaling;
+
+ /*
+ * Hack - force redraw
+ * This induces a call to Term_xtra(TERM_XTRA_REACT, 0) as well
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+# ifdef USE_DOUBLE_TILES
+
+static void change_wide_tile_mode_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ term *old = Term;
+ term_data *td = &data[0];
+
+ /* Toggle "use_bigtile" */
+ use_bigtile = !use_bigtile;
+
+#ifdef TOME
+ /* T.o.M.E. requires this as well */
+ arg_bigtile = use_bigtile;
+#endif /* TOME */
+
+ /* Double the width of tiles (only for the main window) */
+ if (use_bigtile)
+ {
+ td->tile_wid = td->font_wid * 2;
+ }
+
+ /* Use the width of current font */
+ else
+ {
+ td->tile_wid = td->font_wid;
+ }
+
+ /* Need to resize the tiles */
+ resize_request = TRUE;
+
+ /* Activate the main window */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Activate the old term */
+ Term_activate(old);
+
+ /* Hack - force redraw XXX ??? XXX */
+ Term_key_push(KTRL('R'));
+}
+
+# endif /* USE_DOUBLE_TILES */
+
+
+# ifdef USE_TRANSPARENCY
+
+/*
+ * Toggles the boolean value of use_transparency
+ */
+static void change_trans_mode_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* Toggle the transparency mode */
+ use_transparency = !use_transparency;
+
+ /* Hack - force redraw */
+ Term_key_push(KTRL('R'));
+}
+
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Caution: Modal or not, callbacks are called by gtk_main(),
+ * so this is the right place to start a game.
+ */
+static void file_ok_callback(
+ GtkWidget *widget,
+ GtkWidget *file_selector)
+{
+ strcpy(savefile,
+ gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector)));
+
+ gtk_widget_destroy(file_selector);
+
+ /* game is in progress */
+ game_in_progress = TRUE;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play game */
+ play_game(FALSE);
+
+ /* Free memory allocated by game */
+ cleanup_angband();
+
+ /* Done */
+ quit(NULL);
+}
+
+
+/*
+ * Process File-Open menu command
+ */
+static void open_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ GtkWidget *file_selector;
+ char buf[1024];
+
+
+ if (game_in_progress)
+ {
+ plog("You can't open a new game while you're still playing!");
+ return;
+ }
+
+ /* Prepare the savefile path */
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, "*");
+
+ file_selector = gtk_file_selection_new("Select a savefile");
+ gtk_file_selection_set_filename(
+ GTK_FILE_SELECTION(file_selector),
+ buf);
+ gtk_signal_connect(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked",
+ file_ok_callback,
+ (gpointer)file_selector);
+
+ /*
+ * Ensure that the dialog box is destroyed when the user
+ * clicks a button.
+ */
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)file_selector);
+
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)file_selector);
+
+ gtk_window_set_modal(GTK_WINDOW(file_selector), TRUE);
+ gtk_widget_show(GTK_WIDGET(file_selector));
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * React to "delete" signal sent to Window widgets
+ */
+static gboolean delete_event_handler(
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ /* Save game if possible */
+ save_game_gtk();
+
+ /* Don't prevent closure */
+ return (FALSE);
+}
+
+
+/*
+ * Convert keypress events to ASCII codes and enqueue them
+ * for game
+ */
+static gboolean keypress_event_handler(
+ GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ int i, mc, ms, mo, mx;
+
+ char msg[128];
+
+
+ /* Extract four "modifier flags" */
+ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+ ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+ mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE;
+ mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE;
+
+ /*
+ * Hack XXX
+ * Parse shifted numeric (keypad) keys specially.
+ */
+ if ((event->state == GDK_SHIFT_MASK)
+ && (event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9))
+ {
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+ }
+
+ /* Normal keys with no modifiers */
+ if (event->length && !mo && !mx)
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]);
+
+ /* All done */
+ return (TRUE);
+ }
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch ((uint) event->keyval)
+ {
+ case GDK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return (TRUE);
+ }
+
+ case GDK_Return:
+ {
+ Term_keypress('\r');
+ return (TRUE);
+ }
+
+ case GDK_Tab:
+ {
+ Term_keypress('\t');
+ return (TRUE);
+ }
+
+ case GDK_Delete:
+ case GDK_BackSpace:
+ {
+ Term_keypress('\010');
+ return (TRUE);
+ }
+
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R:
+ {
+ /* Hack - do nothing to control characters */
+ return (TRUE);
+ }
+ }
+
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "realize" signal
+ *
+ * In this program, called when window containing the drawing
+ * area is shown first time.
+ */
+static void realize_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Create graphic context */
+ td->gc = gdk_gc_new(td->drawing_area->window);
+
+ /* Set foreground and background colours - isn't bg used at all? */
+ gdk_rgb_gc_set_background(td->gc, 0x000000);
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[TERM_WHITE]);
+
+ /* No last foreground colour, yet */
+ td->last_attr = -1;
+
+ /* Allocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Clear the window */
+ gdk_draw_rectangle(
+ widget->window,
+ widget->style->black_gc,
+ TRUE,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "show" signal
+ */
+static void show_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Set the shown flag */
+ td->shown = TRUE;
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "hide" signal
+ */
+static void hide_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Set the shown flag */
+ td->shown = FALSE;
+}
+
+
+/*
+ * Widget customisation (for drawing area)- handle size allocation requests
+ */
+static void size_allocate_event_handler(
+ GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer user_data)
+{
+ term_data *td = user_data;
+ int old_rows, old_cols;
+ term *old = Term;
+
+ /* Paranoia */
+ g_return_if_fail(widget != NULL);
+ g_return_if_fail(allocation != NULL);
+ g_return_if_fail(td != NULL);
+
+ /* Remember old values */
+ old_cols = td->cols;
+ old_rows = td->rows;
+
+ /* Update numbers of rows and columns */
+ td->cols = (allocation->width + td->font_wid - 1) / td->font_wid;
+ td->rows = (allocation->height + td->font_hgt - 1) / td->font_hgt;
+
+ /* Overkill - Validate them */
+ term_data_check_size(td);
+
+ /* Adjust size request and set it */
+ allocation->width = td->cols * td->font_wid;
+ allocation->height = td->rows * td->font_hgt;
+ widget->allocation = *allocation;
+
+ /* Widget is realized, so we do some drawing works */
+ if (GTK_WIDGET_REALIZED(widget))
+ {
+ /* Reallocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Actually handles resizing in Gtk */
+ gdk_window_move_resize(
+ widget->window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+
+ /* And in the term package */
+ Term_activate(&td->t);
+
+ /* Resize if necessary */
+ if ((td->cols != old_cols) || (td->rows != old_rows))
+ (void)Term_resize(td->cols, td->rows);
+
+ /* Redraw its content */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Update exposed area in a window (for drawing area)
+ */
+static gboolean expose_event_handler(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer user_data)
+{
+ term_data *td = user_data;
+
+ term *old = Term;
+
+#ifndef NO_REDRAW_SECTION
+
+ int x1, x2, y1, y2;
+
+#endif /* !NO_REDRAW_SECTION */
+
+
+ /* Paranoia */
+ if (td == NULL) return (TRUE);
+
+ /* The window has a backing store */
+ if (td->backing_store)
+ {
+ /* Simply restore the exposed area from the backing store */
+ gdk_draw_pixmap(
+ td->drawing_area->window,
+ td->gc,
+ td->backing_store,
+ event->area.x,
+ event->area.y,
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height);
+ }
+
+ /* No backing store - use the game's code to redraw the area */
+ else
+ {
+
+ /* Activate the relevant term */
+ Term_activate(&td->t);
+
+# ifdef NO_REDRAW_SECTION
+
+ /* K.I.S.S. version */
+
+ /* Redraw */
+ Term_redraw();
+
+# else /* NO_REDRAW_SECTION */
+
+ /*
+ * Complex version - The above is enough, but since we have
+ * Term_redraw_section... This might help if we had a graphics
+ * mode.
+ */
+
+ /* Convert coordinate in pixels to character cells */
+ x1 = event->area.x / td->font_wid;
+ x2 = (event->area.x + event->area.width) / td->font_wid;
+ y1 = event->area.y / td->font_hgt;
+ y2 = (event->area.y + event->area.height) / td->font_hgt;
+
+ /*
+ * No paranoia - boundary checking is done in
+ * Term_redraw_section
+ */
+
+ /* Redraw the area */
+ Term_redraw_section(x1, y1, x2, y2);
+
+# endif /* NO_REDRAW_SECTION */
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+
+ /* We've processed the event ourselves */
+ return (TRUE);
+}
+
+
+
+
+/**** Initialisation ****/
+
+/*
+ * Initialise a term_data struct
+ */
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &td->t;
+ char *p;
+
+ td->cols = 80;
+ td->rows = 24;
+
+ /* Initialize the term */
+ term_init(t, td->cols, td->rows, 1024);
+
+ /* Store the name of the term */
+ td->name = string_make(angband_term_name[i]);
+
+ /* Instance names should start with a lowercase letter XXX */
+ for (p = (char *)td->name; *p; p++) *p = tolower(*p);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ t->xtra_hook = Term_xtra_gtk;
+ t->text_hook = Term_text_gtk;
+ t->wipe_hook = Term_wipe_gtk;
+ t->curs_hook = Term_curs_gtk;
+#ifdef USE_GRAPHICS
+ t->pict_hook = Term_pict_gtk;
+#endif /* USE_GRAPHICS */
+ t->nuke_hook = Term_nuke_gtk;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Neater menu code with GtkItemFactory.
+ *
+ * Menu bar of the Angband window
+ *
+ * Entry format: Path, Accelerator, Callback, Callback arg, type
+ * where type is one of:
+ * <Item> - simple item, alias NULL
+ * <Branch> - has submenu
+ * <Separator> - as you read it
+ * <CheckItem> - has a check mark
+ * <ToggleItem> - is a toggle
+ */
+static GtkItemFactoryEntry main_menu_items[] =
+{
+ /* "File" menu */
+ { "/File", NULL,
+ NULL, 0, "<Branch>"
+ },
+#ifndef SAVEFILE_SCREEN
+ { "/File/New", "<mod1>N",
+ new_event_handler, 0, NULL },
+ { "/File/Open", "<mod1>O",
+ open_event_handler, 0, NULL },
+ { "/File/sep1", NULL,
+ NULL, 0, "<Separator>" },
+#endif /* !SAVEFILE_SCREEN */
+ { "/File/Save", "<mod1>S",
+ save_event_handler, 0, NULL },
+ { "/File/Quit", "<mod1>Q",
+ quit_event_handler, 0, NULL },
+
+ /* "Terms" menu */
+ { "/Terms", NULL,
+ NULL, 0, "<Branch>" },
+ /* XXX XXX XXX NULL's are replaced by the program */
+ { NULL, "<mod1>0",
+ term_event_handler, (guint)&data[0], "<CheckItem>" },
+ { NULL, "<mod1>1",
+ term_event_handler, (guint)&data[1], "<CheckItem>" },
+ { NULL, "<mod1>2",
+ term_event_handler, (guint)&data[2], "<CheckItem>" },
+ { NULL, "<mod1>3",
+ term_event_handler, (guint)&data[3], "<CheckItem>" },
+ { NULL, "<mod1>4",
+ term_event_handler, (guint)&data[4], "<CheckItem>" },
+ { NULL, "<mod1>5",
+ term_event_handler, (guint)&data[5], "<CheckItem>" },
+ { NULL, "<mod1>6",
+ term_event_handler, (guint)&data[6], "<CheckItem>" },
+ { NULL, "<mod1>7",
+ term_event_handler, (guint)&data[7], "<CheckItem>" },
+
+ /* "Options" menu */
+ { "/Options", NULL,
+ NULL, 0, "<Branch>" },
+
+ /* "Font" submenu */
+ { "/Options/Font", NULL,
+ NULL, 0, "<Branch>" },
+ /* XXX XXX XXX Again, NULL's are filled by the program */
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[0], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[1], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[2], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[3], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[4], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[5], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[6], NULL },
+ { NULL, NULL,
+ change_font_event_handler, (guint)&data[7], NULL },
+
+#ifdef USE_GRAPHICS
+
+ /* "Graphics" submenu */
+ { "/Options/Graphics", NULL,
+ NULL, 0, "<Branch>" },
+ { "/Options/Graphics/None", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_NONE, "<CheckItem>" },
+ { "/Options/Graphics/Old", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_OLD, "<CheckItem>" },
+ { "/Options/Graphics/New", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_NEW, "<CheckItem>" },
+# ifdef USE_DOUBLE_TILES
+ { "/Options/Graphics/sep3", NULL,
+ NULL, 0, "<Separator>" },
+ { "/Options/Graphics/Wide tiles", NULL,
+ change_wide_tile_mode_event_handler, 0, "<CheckItem>" },
+# endif /* USE_DOUBLE_TILES */
+ { "/Options/Graphics/sep1", NULL,
+ NULL, 0, "<Separator>" },
+ { "/Options/Graphics/Dither if <= 8bpp", NULL,
+ change_dith_mode_event_handler, GDK_RGB_DITHER_NORMAL, "<CheckItem>" },
+ { "/Options/Graphics/Dither if <= 16bpp", NULL,
+ change_dith_mode_event_handler, GDK_RGB_DITHER_MAX, "<CheckItem>" },
+ { "/Options/Graphics/sep2", NULL,
+ NULL, 0, "<Separator>" },
+ { "/Options/Graphics/Smoothing", NULL,
+ change_smooth_mode_event_handler, 0, "<CheckItem>" },
+# ifdef USE_TRANSPARENCY
+ { "/Options/Graphics/Transparency", NULL,
+ change_trans_mode_event_handler, 0, "<CheckItem>" },
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+ /* "Misc" submenu */
+ { "/Options/Misc", NULL,
+ NULL, 0, "<Branch>" },
+ { "/Options/Misc/Backing store", NULL,
+ change_backing_store_event_handler, 0, "<CheckItem>" },
+};
+
+
+/*
+ * XXX XXX Fill those NULL's in the menu definition with
+ * angband_term_name[] strings
+ */
+static void setup_menu_paths(void)
+{
+ int i;
+ int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+ GtkItemFactoryEntry *term_entry, *font_entry;
+ char buf[64];
+
+ /* Find the "Terms" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Terms")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ term_entry = &main_menu_items[i + 1];
+
+ /* Find "Font" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Options/Font")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ font_entry = &main_menu_items[i + 1];
+
+ /* For each terminal */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* XXX XXX Build the real path name to the entry */
+ strnfmt(buf, 64, "/Terms/%s", angband_term_name[i]);
+
+ /* XXX XXX Store it in the menu definition */
+ (cptr)term_entry[i].path = string_make(buf);
+
+ /* XXX XXX Build the real path name to the entry */
+ strnfmt(buf, 64, "/Options/Font/%s", angband_term_name[i]);
+
+ /* XXX XXX Store it in the menu definition */
+ (cptr)font_entry[i].path = string_make(buf);
+ }
+}
+
+
+/*
+ * XXX XXX Free strings allocated by setup_menu_paths()
+ */
+static void free_menu_paths(void)
+{
+ int i;
+ int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+ GtkItemFactoryEntry *term_entry, *font_entry;
+
+ /* Find the "Terms" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Terms")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ term_entry = &main_menu_items[i + 1];
+
+ /* Find "Font" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Options/Font")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ font_entry = &main_menu_items[i + 1];
+
+ /* For each terminal */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* XXX XXX Free Term menu path */
+ if (term_entry[i].path) string_free((cptr)term_entry[i].path);
+
+ /* XXX XXX Free Font menu path */
+ if (font_entry[i].path) string_free((cptr)font_entry[i].path);
+ }
+}
+
+
+/*
+ * Find widget corresponding to path name
+ * return NULL on error
+ */
+static GtkWidget *get_widget_from_path(cptr path)
+{
+ GtkItemFactory *item_factory;
+ GtkWidget *widget;
+
+ /* Paranoia */
+ if (path == NULL) return (NULL);
+
+ /* Look up item factory */
+ item_factory = gtk_item_factory_from_path(path);
+
+ /* Oops */
+ if (item_factory == NULL) return (NULL);
+
+ /* Look up widget */
+ widget = gtk_item_factory_get_widget(item_factory, path);
+
+ /* Return result */
+ return (widget);
+}
+
+
+/*
+ * Enable/disable a menu item
+ */
+void enable_menu_item(cptr path, bool enabled)
+{
+ GtkWidget *widget;
+
+ /* Access menu item widget */
+ widget = get_widget_from_path(path);
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU_ITEM(widget));
+
+ /*
+ * In Gtk's terminology, enabled is sensitive
+ * and disabled insensitive
+ */
+ gtk_widget_set_sensitive(widget, enabled);
+}
+
+
+/*
+ * Check/uncheck a menu item. The item should be of the GtkCheckMenuItem type
+ */
+void check_menu_item(cptr path, bool checked)
+{
+ GtkWidget *widget;
+
+ /* Access menu item widget */
+ widget = get_widget_from_path(path);
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_CHECK_MENU_ITEM(widget));
+
+ /*
+ * Put/remove check mark
+ *
+ * Mega-Hack -- The function supposed to be used here,
+ * gtk_check_menu_item_set_active(), emits an "activate" signal
+ * to the GtkMenuItem class of the widget, as if the menu item
+ * were selected by user, thereby causing bizarre behaviour.
+ * XXX XXX XXX
+ */
+ GTK_CHECK_MENU_ITEM(widget)->active = checked;
+}
+
+
+/*
+ * Update the "File" menu
+ */
+static void file_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+#ifndef SAVEFILE_SCREEN
+ bool game_start_ok;
+#endif /* !SAVEFILE_SCREEN */
+ bool save_ok, quit_ok;
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Can we start a game now? */
+ game_start_ok = !game_in_progress;
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Cave we save/quit now? */
+ if (!character_generated || !game_in_progress)
+ {
+ save_ok = FALSE;
+ quit_ok = TRUE;
+ }
+ else
+ {
+ if (inkey_flag && can_save) save_ok = quit_ok = TRUE;
+ else save_ok = quit_ok = FALSE;
+ }
+
+ /* Enable / disable menu items according to those conditions */
+#ifndef SAVEFILE_SCREEN
+ enable_menu_item("<Angband>/File/New", game_start_ok);
+ enable_menu_item("<Angband>/File/Open", game_start_ok);
+#endif /* !SAVEFILE_SCREEN */
+ enable_menu_item("<Angband>/File/Save", save_ok);
+ enable_menu_item("<Angband>/File/Quit", quit_ok);
+}
+
+
+/*
+ * Update the "Terms" menu
+ */
+static void term_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ int i;
+ char buf[64];
+
+ /* For each term */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Build the path name */
+ strnfmt(buf, 64, "<Angband>/Terms/%s", angband_term_name[i]);
+
+ /* Update the check mark on the item */
+ check_menu_item(buf, data[i].shown);
+ }
+}
+
+
+/*
+ * Update the "Font" submenu
+ */
+static void font_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ int i;
+ char buf[64];
+
+ /* For each term */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Build the path name */
+ strnfmt(buf, 64, "<Angband>/Options/Font/%s", angband_term_name[i]);
+
+ /* Enable selection if the term is shown */
+ enable_menu_item(buf, data[i].shown);
+ }
+}
+
+
+/*
+ * Update the "Misc" submenu
+ */
+static void misc_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Update an item */
+ check_menu_item(
+ "<Angband>/Options/Misc/Backing store",
+ use_backing_store);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Update the "Graphics" submenu
+ */
+static void graf_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Update menu items */
+ check_menu_item(
+ "<Angband>/Options/Graphics/None",
+ (graf_mode == GRAF_MODE_NONE));
+ check_menu_item(
+ "<Angband>/Options/Graphics/Old",
+ (graf_mode == GRAF_MODE_OLD));
+ check_menu_item(
+ "<Angband>/Options/Graphics/New",
+ (graf_mode == GRAF_MODE_NEW));
+
+#ifdef USE_DOUBLE_TILES
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Wide tiles",
+ use_bigtile);
+
+#endif /* USE_DOUBLE_TILES */
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Dither if <= 8bpp",
+ (dith_mode == GDK_RGB_DITHER_NORMAL));
+ check_menu_item(
+ "<Angband>/Options/Graphics/Dither if <= 16bpp",
+ (dith_mode == GDK_RGB_DITHER_MAX));
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Smoothing",
+ smooth_rescaling);
+
+# ifdef USE_TRANSPARENCY
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Transparency",
+ use_transparency);
+
+# endif /* USE_TRANSPARENCY */
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Construct a menu hierarchy using GtkItemFactory, setting up
+ * callbacks and accelerators along the way, and return
+ * a GtkMenuBar widget.
+ */
+GtkWidget *get_main_menu(term_data *td)
+{
+ GtkItemFactory *item_factory;
+ GtkAccelGroup *accel_group;
+ gint nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+
+
+ /* XXX XXX Setup path names in the "Terms" and "Font" menus */
+ setup_menu_paths();
+
+ /* Allocate an accelerator group */
+ accel_group = gtk_accel_group_new();
+ g_assert(accel_group != NULL);
+
+ /* Initialise the item factory */
+ item_factory = gtk_item_factory_new(
+ GTK_TYPE_MENU_BAR,
+ "<Angband>",
+ accel_group);
+ g_assert(item_factory != NULL);
+
+ /* Generate the menu items */
+ gtk_item_factory_create_items(
+ item_factory,
+ nmenu_items,
+ main_menu_items,
+ NULL);
+
+ /* Attach the new accelerator group to the window */
+ gtk_window_add_accel_group(
+ GTK_WINDOW(td->window),
+ accel_group);
+
+ /* Return the actual menu bar created */
+ return (gtk_item_factory_get_widget(item_factory, "<Angband>"));
+}
+
+
+/*
+ * Install callbacks to update menus
+ */
+static void add_menu_update_callbacks()
+{
+ GtkWidget *widget;
+
+ /* Access the "File" menu */
+ widget = get_widget_from_path("<Angband>/File");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(file_menu_update_handler),
+ NULL);
+
+ /* Access the "Terms" menu */
+ widget = get_widget_from_path("<Angband>/Terms");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(term_menu_update_handler),
+ NULL);
+
+ /* Access the "Font" menu */
+ widget = get_widget_from_path("<Angband>/Options/Font");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(font_menu_update_handler),
+ NULL);
+
+ /* Access the "Misc" menu */
+ widget = get_widget_from_path("<Angband>/Options/Misc");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(misc_menu_update_handler),
+ NULL);
+
+#ifdef USE_GRAPHICS
+
+ /* Access Graphics menu */
+ widget = get_widget_from_path("<Angband>/Options/Graphics");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(graf_menu_update_handler),
+ NULL);
+
+#endif /* USE_GRAPHICS */
+}
+
+
+/*
+ * Create Gtk widgets for a terminal window and set up callbacks
+ */
+static void init_gtk_window(term_data *td, int i)
+{
+ GtkWidget *menu_bar = NULL, *box;
+ cptr font;
+
+ bool main_window = (i == 0) ? TRUE : FALSE;
+
+
+ /* Create window */
+ td->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ /* Set title */
+ gtk_window_set_title(GTK_WINDOW(td->window), td->name);
+
+
+ /* Get default font for this term */
+ font = get_default_font(i);
+
+ /* Load font and initialise related term_data fields */
+ load_font(td, font);
+
+
+ /* Create drawing area */
+ td->drawing_area = gtk_drawing_area_new();
+
+ /* Set the size of the drawing area */
+ gtk_drawing_area_size(
+ GTK_DRAWING_AREA(td->drawing_area),
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Set geometry hints */
+ term_data_set_geometry_hints(td);
+
+
+ /* Install window event handlers */
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(delete_event_handler),
+ NULL);
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "key_press_event",
+ GTK_SIGNAL_FUNC(keypress_event_handler),
+ NULL);
+
+ /* Destroying the Angband window terminates the game */
+ if (main_window)
+ {
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "destroy_event",
+ GTK_SIGNAL_FUNC(destroy_main_event_handler),
+ NULL);
+ }
+
+ /* The other windows are just hidden */
+ else
+ {
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "destroy_event",
+ GTK_SIGNAL_FUNC(destroy_sub_event_handler),
+ td);
+ }
+
+
+ /* Install drawing area event handlers */
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "realize",
+ GTK_SIGNAL_FUNC(realize_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "show",
+ GTK_SIGNAL_FUNC(show_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "hide",
+ GTK_SIGNAL_FUNC(hide_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "size_allocate",
+ GTK_SIGNAL_FUNC(size_allocate_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "expose_event",
+ GTK_SIGNAL_FUNC(expose_event_handler),
+ (gpointer)td);
+
+
+ /* Create menu */
+ if (main_window)
+ {
+ /* Build the main menu bar */
+ menu_bar = get_main_menu(td);
+ g_assert(menu_bar != NULL);
+
+ /* Since it's tedious to scatter the menu update code around */
+ add_menu_update_callbacks();
+ }
+
+
+ /* Pack the menu bar together with the main window */
+ /* For vertical placement of the menu bar and the drawing area */
+ box = gtk_vbox_new(FALSE, 0);
+
+ /* Let the window widget own it */
+ gtk_container_add(GTK_CONTAINER(td->window), box);
+
+ /* The main window has a menu bar */
+ if (main_window)
+ gtk_box_pack_start(
+ GTK_BOX(box),
+ menu_bar,
+ FALSE,
+ FALSE,
+ NO_PADDING);
+
+ /* And place the drawing area just beneath it */
+ gtk_box_pack_start_defaults(GTK_BOX(box), td->drawing_area);
+
+
+ /* Show the widgets - use of td->shown is a dirty hack XXX XXX */
+ if (td->shown) gtk_widget_show_all(td->window);
+}
+
+
+/*
+ * To be hooked into quit(). See z-util.c
+ */
+static void hook_quit(cptr str)
+{
+ /* Free menu paths dynamically allocated */
+ free_menu_paths();
+
+# ifdef USE_GRAPHICS
+
+ /* Free pathname string */
+ if (ANGBAND_DIR_XTRA_GRAF) string_free(ANGBAND_DIR_XTRA_GRAF);
+
+# endif /* USE_GRAPHICS */
+
+ /* Terminate the program */
+ gtk_exit(0);
+}
+
+
+#ifdef ANGBAND300
+
+/*
+ * Help message for this port
+ */
+const char help_gtk[] =
+ "GTK for X11, subopts -n<windows>\n"
+ " -b(acking store off)\n"
+#ifdef USE_GRAPHICS
+ " -g(raphics) -o(ld graphics) -s(moothscaling off) \n"
+ " -t(ransparency on)\n"
+# ifdef USE_DOUBLE_TILES
+ " -w(ide tiles)\n"
+# endif /* USE_DOUBLE_TILES */
+#endif /* USE_GRAPHICS */
+ " and standard GTK options";
+
+#endif /* ANGBAND300 */
+
+
+/*
+ * Initialization function
+ */
+errr init_gtk(int argc, char **argv)
+{
+ int i;
+
+
+ /* Initialize the environment */
+ gtk_init(&argc, &argv);
+
+ /* Activate hooks - Use gtk/glib interface throughout */
+ ralloc_aux = hook_ralloc;
+ rnfree_aux = hook_rnfree;
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ /* Number of terminals displayed at start up */
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ /* Disable use of pixmaps as backing store */
+ if (streq(argv[i], "-b"))
+ {
+ use_backing_store = FALSE;
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ /* Requests "old" graphics */
+ if (streq(argv[i], "-o"))
+ {
+ graf_mode_request = GRAF_MODE_OLD;
+ continue;
+ }
+
+ /* Requests "new" graphics */
+ if (streq(argv[i], "-g"))
+ {
+ graf_mode_request = GRAF_MODE_NEW;
+ continue;
+ }
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Requests wide tile mode */
+ if (streq(argv[i], "-w"))
+ {
+ use_bigtile = TRUE;
+# ifdef TOME
+ /* T.o.M.E. uses older version of the patch */
+ arg_bigtile = TRUE;
+# endif /* TOME */
+ continue;
+ }
+
+# endif /* USE_DOUBLE_TILES */
+
+
+ /* Enable transparency effect */
+ if (streq(argv[i], "-t"))
+ {
+ use_transparency = TRUE;
+ continue;
+ }
+
+ /* Disable smooth rescaling of tiles */
+ if (streq(argv[i], "-s"))
+ {
+ smooth_rescaling_request = FALSE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* None of the above */
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+#ifdef USE_GRAPHICS
+
+ {
+ char path[1024];
+
+ /* Build the "graf" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_GRAF = string_make(path);
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Initialise colours */
+ gdk_rgb_init();
+ gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
+ gtk_widget_set_default_visual(gdk_rgb_get_visual());
+ init_colours();
+
+ /*
+ * Initialise the windows backwards, so that
+ * the Angband window comes in front
+ */
+ for (i = MAX_TERM_DATA - 1; i >= 0; i--)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Hack - Set the shown flag, meaning "to be shown" XXX XXX */
+ if (i < num_term) td->shown = TRUE;
+ else td->shown = FALSE;
+
+ /* Save global entry */
+ angband_term[i] = Term;
+
+ /* Init the window */
+ init_gtk_window(td, i);
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Set the system suffix */
+ ANGBAND_SYS = "gtk";
+
+ /* Catch nasty signals */
+ signals_init();
+
+ /* Initialize */
+ init_angband();
+
+#ifndef OLD_SAVEFILE_CODE
+
+ /* Hack - because this port has New/Open menus XXX */
+ savefile[0] = '\0';
+
+#endif /* !OLD_SAVEFILE_CODE */
+
+ /* Prompt the user */
+ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 17);
+ Term_fresh();
+
+ /* Activate more hook */
+ plog_aux = hook_plog;
+
+
+ /* Processing loop */
+ gtk_main();
+
+
+ /* Free allocated memory */
+ cleanup_angband();
+
+ /* Stop now */
+ quit(NULL);
+
+#else /* !SAVEFILE_SCREEN */
+
+ /* Activate more hook */
+ plog_aux = hook_plog;
+
+ /* It's too early to set this, but cannot do so elsewhere XXX XXX */
+ game_in_progress = TRUE;
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GTK */
diff --git a/src/main-gtk2.c b/src/main-gtk2.c
new file mode 100644
index 00000000..ada45aa9
--- /dev/null
+++ b/src/main-gtk2.c
@@ -0,0 +1,5412 @@
+/* File: main-gtk.c */
+
+/*
+ * Copyright (c) 2000-2001 Robert Ruehlmann,
+ * Steven Fuerst, Uwe Siems, "pelpel", et al.
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/*
+ * Robert Ruehlmann wrote the original Gtk port. Since an initial work is
+ * much harder than enhancements, his effort worth more credits than
+ * others.
+ *
+ * Steven Fuerst implemented colour-depth independent X server support,
+ * graphics, resizing and big screen support for ZAngband as well as
+ * fast image rescaling that is included here.
+ *
+ * Uwe Siems wrote smooth tiles rescaling code (on by default).
+ * Try this with 8x8 tiles. They *will* look different.
+ *
+ * "pelpel" wrote another colour-depth independent X support
+ * using GdkRGB, added several hooks and callbacks for various
+ * reasons, wrote no-backing store mode (off by default),
+ * added GtkItemFactory based menu system, introduced
+ * USE_GRAPHICS code bloat (^ ^;), added comments (I have
+ * a strange habit of writing comments while I code...)
+ * and reorganised the file a bit.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Activate variant-specific features
+ *
+ * Angband 2.9.3 and close variants don't require any.
+ *
+ * Angband 2.9.4 alpha and later removed the short-lived
+ * can_save flag, so please #define can_save TRUE, or remove
+ * all the references to it. They also changed long-lived
+ * z-virt macro names. Find C_FREE/C_KILL and replace them
+ * with FREE/KILL, which takes one pointer parameter.
+ *
+ * [Z]-based variants (Gum and Cth, for example) usually need
+ * ANG293_COMPAT, ANG291_COMPAT and ANG281_RESET_VISUALS.
+ *
+ * [O] needs ANG293_COMPAT and ZANG_SAVE_GAME.
+ *
+ * ZAngband has its own enhanced main-gtk.c as mentioned above, and
+ * you *should* use it :-)
+ *
+ * ANG291_COMPAT does not include Angband 2.9.x's gamma correction code.
+ * If you like to use SUPPORT_GAMMA, copy the code bracketed
+ * inside of #ifdef SUPPORT_GAMMA in util.c of Angband 2.9.1 or greater.
+ */
+#define TOME
+
+#ifdef TOME
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ANG291_COMPAT /* Requires V2.9.1 compatibility code */
+# define ANG281_RESET_VISUALS /* The old style reset_visuals() */
+# define INTERACTIVE_GAMMA /* Supports interactive gamma correction */
+# define SAVEFILE_SCREEN /* New/Open integrated into the game */
+# define USE_DOUBLE_TILES /* Mogami's bigtile patch */
+#endif /* TOME */
+
+/*
+ * Some examples
+ */
+#ifdef ANGBAND300
+# define can_save TRUE /* Mimick the short-lived flag */
+# define C_FREE(P, N, T) FREE(P) /* Emulate the long-lived macro */
+# define USE_TRANSPARENCY /* Because it's default now */
+#endif /* ANGBAND300 */
+
+#ifdef GUMBAND
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ANG291_COMPAT /* Requires V2.9.1 compatibility code */
+# define ANG281_RESET_VISUALS /* The old style reset_visuals() */
+# define OLD_SAVEFILE_CODE /* See also SAVEFILE_MUTABLE in files.c */
+# define NO_REDRAW_SECTION /* Doesn't have Term_redraw_section() */
+#endif /* GUMBAND */
+
+#ifdef OANGBAND
+# define ANG293_COMPAT /* Requires V2.9.3 compatibility code */
+# define ZANG_SAVE_GAME /* do_cmd_save_game with auto_save parameter */
+#endif /* OANGBAND */
+
+
+#ifdef USE_GTK2
+
+/* Force ANSI standard */
+/* #define __STRICT_ANSI__ */
+
+/* No GCC-specific includes */
+/* #undef __GNUC__ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+/*
+ * Include some helpful X11 code.
+ */
+#ifndef ANG293_COMPAT
+# include "maid-x11.h"
+#endif /* !ANG293_COMPAT */
+
+
+/*
+ * Number of pixels inserted between the menu bar and the main screen
+ */
+#define NO_PADDING 0
+
+
+/*
+ * Largest possible number of terminal windows supported by the game
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * Extra data to associate with each "window"
+ *
+ * Each "window" is represented by a "term_data" structure, which
+ * contains a "term" structure, which contains a pointer (t->data)
+ * back to the term_data structure.
+ */
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Since GdkRGB doesn't provide us some useful functions...
+ */
+typedef struct GdkRGBImage GdkRGBImage;
+
+struct GdkRGBImage
+{
+ gint width;
+ gint height;
+ gint ref_count;
+ guchar *image;
+};
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * This structure holds everything you need to manipulate terminals
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t;
+
+ GtkWidget *window;
+ GtkWidget *drawing_area;
+ GdkPixmap *backing_store;
+ GdkFont *font;
+ GdkGC *gc;
+
+ bool shown;
+ byte last_attr;
+
+ int font_wid;
+ int font_hgt;
+
+ int rows;
+ int cols;
+
+#ifdef USE_GRAPHICS
+
+ int tile_wid;
+ int tile_hgt;
+
+ GdkRGBImage *tiles;
+# ifdef USE_TRANSPARENCY
+ guint32 bg_pixel;
+ GdkRGBImage *trans_buf;
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+ cptr name;
+};
+
+
+/*
+ * Where to draw when we call Gdk drawing primitives
+ */
+# define TERM_DATA_DRAWABLE(td) \
+((td)->backing_store ? (td)->backing_store : (td)->drawing_area->window)
+
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt) \
+if ((td)->backing_store) gdk_draw_pixmap( \
+(td)->drawing_area->window, \
+(td)->gc, \
+(td)->backing_store, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(wid) * (td)->font_wid, \
+(hgt) * (td)->font_hgt)
+
+
+#if 0
+
+/* Compile time option version */
+
+# ifdef USE_BACKING_STORE
+
+# define TERM_DATA_DRAWABLE(td) (td)->backing_store
+
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt) \
+gdk_draw_pixmap( \
+(td)->drawing_area->window, \
+(td)->gc, \
+(td)->backing_store, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(x) * (td)->font_wid, \
+(y) * (td)->font_hgt, \
+(wid) * (td)->font_wid, \
+(hgt) * (td)->font_hgt)
+
+# else /* USE_BACKING_STORE */
+
+# define TERM_DATA_DRAWABLE(td) (td)->drawing_area->window
+# define TERM_DATA_REFRESH(td, x, y, wid, hgt)
+
+# endif /* USE_BACKING_STORE */
+
+#endif /* 0 */
+
+
+/*
+ * An array of "term_data" structures, one for each "sub-window"
+ */
+static term_data data[MAX_TERM_DATA];
+
+/*
+ * Number of active terms
+ */
+static int num_term = 1;
+
+
+/*
+ * RGB values of the sixteen Angband colours
+ */
+static guint32 angband_colours[16];
+
+
+/*
+ * Set to TRUE when a game is in progress
+ */
+static bool game_in_progress = FALSE;
+
+
+/*
+ * This is in some cases used for double buffering as well as
+ * a backing store, speeding things up under client-server
+ * configurations, while turning this off *might* work better
+ * with the MIT Shm extention which is usually active if you run
+ * Angband locally, because it reduces amount of memory-to-memory copy.
+ */
+static bool use_backing_store = TRUE;
+
+
+
+
+/**** Vanilla compatibility functions ****/
+
+#ifdef ANG293_COMPAT
+
+/*
+ * Look up some environment variables to find font name for each window.
+ */
+static cptr get_default_font(int term)
+{
+ char buf[64];
+ cptr font_name;
+
+ /* Window specific font name */
+ strnfmt(buf, 64, "ANGBAND_X11_FONT_%s", angband_term_name[term]);
+
+ /* Check environment for that font */
+ font_name = getenv(buf);
+
+ /* Window specific font name */
+ strnfmt(buf, 64, "ANGBAND_X11_FONT_%d", term);
+
+ /* Check environment for that font */
+ if (!font_name) font_name = getenv(buf);
+
+ /* Check environment for "base" font */
+ if (!font_name) font_name = getenv("ANGBAND_X11_FONT");
+
+ /* No environment variables, use default font */
+ if (!font_name) font_name = DEFAULT_X11_FONT_SCREEN;
+
+ return (font_name);
+}
+
+
+# ifndef SAVEFILE_SCREEN
+
+/*
+ * In [V]2.9.3, this frees all dynamically allocated memory
+ */
+static void cleanup_angband(void)
+{
+ /* XXX XXX XXX */
+}
+
+# endif /* !SAVEFILE_SCREEN */
+
+/*
+ * New global flag to indicate if it's safe to save now
+ */
+#define can_save TRUE
+
+#endif /* ANG293_COMPAT */
+
+
+#ifdef ANG291_COMPAT
+
+/*
+ * The standard game uses this to implement lighting effects
+ * for 16x16 tiles in cave.c...
+ *
+ * Because of the way it is implemented in X11 ports,
+ * we can set this to TRUE even if we are using the 8x8 tileset.
+ */
+static bool use_transparency = TRUE;
+
+#endif /* ANG291_COMPAT */
+
+
+
+
+/**** Low level routines - memory allocation ****/
+
+/*
+ * Hook to "release" memory
+ */
+#ifdef ANGBAND300
+static vptr hook_rnfree(vptr v)
+#else
+static vptr hook_rnfree(vptr v, huge size)
+#endif /* ANGBAND300 */
+{
+ /* Dispose */
+ g_free(v);
+
+ /* Success */
+ return (NULL);
+}
+
+
+/*
+ * Hook to "allocate" memory
+ */
+static vptr hook_ralloc(huge size)
+{
+ /* Make a new pointer */
+ return (g_malloc(size));
+}
+
+
+
+/**** Low level routines - colours and graphics ****/
+
+#ifdef SUPPORT_GAMMA
+
+/*
+ * When set to TRUE, indicates that we can use gamma_table
+ */
+static bool gamma_table_ready = FALSE;
+
+
+# ifdef INTERACTIVE_GAMMA
+
+/*
+ * Initialise the gamma-correction table for current gamma_val
+ * - interactive version
+ */
+static void setup_gamma_table(void)
+{
+ static u16b old_gamma_val = 0;
+
+ /* Don't have to rebuild the table */
+ if (gamma_val == old_gamma_val) return;
+
+ /* Temporarily inactivate the table */
+ gamma_table_ready = FALSE;
+
+ /* Validate gamma_val */
+ if ((gamma_val <= 0) || (gamma_val >= 256))
+ {
+ /* Reset */
+ old_gamma_val = gamma_val = 0;
+
+ /* Leave it inactive */
+ return;
+ }
+
+ /* (Re)build the gamma table */
+ build_gamma_table(gamma_val);
+
+ /* Remember the gamma value used */
+ old_gamma_val = gamma_val;
+
+ /* Activate the table */
+ gamma_table_ready = TRUE;
+}
+
+# else /* INTERACTIVE_GAMMA */
+
+/*
+ * Initialise the gamma-correction table if environment variable
+ * ANGBAND_X11_GAMMA is set and contains a meaningful value
+ *
+ * Restored for cross-variant compatibility
+ */
+static void setup_gamma_table(void)
+{
+ cptr tmp;
+ int gamma_val;
+
+
+ /* The table's already set up */
+ if (gamma_table_ready) return;
+
+ /*
+ * XXX XXX It's documented nowhere, but ANGBAND_X11_GAMMA is
+ * 256 * (1 / gamma), rounded to integer. A recommended value
+ * is 183, which is an approximation of the Macintosh hardware
+ * gamma of 1.4.
+ *
+ * gamma ANGBAND_X11_GAMMA
+ * ----- -----------------
+ * 1.2 213
+ * 1.25 205
+ * 1.3 197
+ * 1.35 190
+ * 1.4 183
+ * 1.45 177
+ * 1.5 171
+ * 1.6 160
+ * 1.7 151
+ * ...
+ *
+ * XXX XXX The environment variable, or better,
+ * the interact with colours command should allow users
+ * to specify gamma values (or gamma value * 100).
+ */
+ tmp = getenv("ANGBAND_X11_GAMMA");
+
+ /* Nothing to do */
+ if (tmp == NULL) return;
+
+ /* Extract the value */
+ gamma_val = atoi(tmp);
+
+ /*
+ * Only need to build the table if gamma exists and set to
+ * a meaningful value.
+ *
+ * XXX It may be a good idea to prevent use of very high gamma values,
+ * say, greater than 2.5, which is gamma of normal CRT display IIRC.
+ */
+ if ((gamma_val <= 0) || (gamma_val >= 256)) return;
+
+ /* Build the gamma correction table */
+ build_gamma_table(gamma_val);
+
+ /* The table is properly set up */
+ gamma_table_ready = TRUE;
+}
+
+# endif /* INTERACTIVE_GAMMA */
+
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * Remeber RGB values for sixteen Angband colours, in a format
+ * that is convinient for GdkRGB GC functions.
+ *
+ * XXX XXX Duplication of maid-x11.c is far from the Angband
+ * ideal of code cleanliness, but the whole point of using GdkRGB
+ * is to let it handle colour allocation which it does in a very
+ * clever fashion. Ditto for the tile scaling code and the BMP loader
+ * below.
+ */
+static void init_colours(void)
+{
+ int i;
+
+
+#ifdef SUPPORT_GAMMA
+
+ /* (Re)build gamma table if necessary */
+ setup_gamma_table();
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Process each colour */
+ for (i = 0; i < 16; i++)
+ {
+ u32b red, green, blue;
+
+ /* Retrieve RGB values from the game */
+ red = angband_color_table[i][1];
+ green = angband_color_table[i][2];
+ blue = angband_color_table[i][3];
+
+#ifdef SUPPORT_GAMMA
+
+ /* Hack -- Gamma Correction */
+ if (gamma_table_ready)
+ {
+ red = gamma_table[red];
+ green = gamma_table[green];
+ blue = gamma_table[blue];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Remember a GdkRGB value, that is 0xRRGGBB */
+ angband_colours[i] = (red << 16) | (green << 8) | blue;
+ }
+}
+
+
+/*
+ * Set foreground colour of window td to attr, only when it is necessary
+ */
+static void term_data_set_fg(term_data *td, byte attr)
+{
+ /* We can use the current gc */
+ if (td->last_attr == attr) return;
+
+ /* Activate the colour */
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[attr]);
+
+ /* Remember it */
+ td->last_attr = attr;
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Graphics mode selector - current setting and requested value
+ */
+#define GRAF_MODE_NONE 0
+#define GRAF_MODE_OLD 1
+#define GRAF_MODE_NEW 2
+
+static int graf_mode = GRAF_MODE_NONE;
+static int graf_mode_request = GRAF_MODE_NONE;
+
+/*
+ * Use smooth rescaling?
+ */
+static bool smooth_rescaling = TRUE;
+static bool smooth_rescaling_request = TRUE;
+
+/*
+ * Dithering
+ */
+static GdkRgbDither dith_mode = GDK_RGB_DITHER_NORMAL;
+
+/*
+ * Need to reload and resize tiles when fonts are changed.
+ */
+static bool resize_request = FALSE;
+
+/*
+ * Numbers of columns and rows in current tileset
+ * calculated and set by the tile loading code in graf_init()
+ * and used by Term_pict_gtk()
+ */
+static int tile_rows;
+static int tile_cols;
+
+
+/*
+ * Directory name(s)
+ */
+static cptr ANGBAND_DIR_XTRA_GRAF;
+
+
+/*
+ * Be nice to old graphics hardwares -- using GdkRGB.
+ *
+ * We don't have colour allocation failure any longer this way,
+ * even with 8bpp X servers. Gimp *does* work with 8bpp, why not Angband?
+ *
+ * Initialisation (before any widgets are created)
+ * gdk_rgb_init();
+ * gtk_widget_set_default_colormap (gdk_rgb_get_cmap());
+ * gtk_widget_set_default_visual (gdk_rgb_get_visual());
+ *
+ * Setting fg/bg colours
+ * void gdk_rgb_gc_set_foreground(GdkGC *gc, guint32 rgb);
+ * void gdk_rgb_gc_set_background(GdkGC *gc, guint32 rgb);
+ * where rgb is 0xRRGGBB.
+ *
+ * Drawing rgb images
+ * void gdk_draw_rgb_image(
+ * GdkDrawable *drawable,
+ * GdkGC *gc,
+ * gint x, gint y,
+ * gint width, gint height,
+ * GdkRgbDither dith,
+ * guchar *rgb_buf,
+ * gint rowstride);
+ *
+ * dith:
+ * GDK_RGB_DITHER_NORMAL : dither if 8bpp or below
+ * GDK_RGB_DITHER_MAX : dither if 16bpp or below.
+ *
+ * for 0 <= i < width and 0 <= j < height,
+ * the pixel (x + i, y + j) is colored with
+ * red value rgb_buf[j * rowstride + i * 3],
+ * green value rgb_buf[j * rowstride + i * 3 + 1], and
+ * blue value rgb_buf[j * rowstride + i * 3 + 2].
+ */
+
+/*
+ * gdk_image compatibility functions - should be part of gdk, IMHO.
+ */
+
+/*
+ * Create GdkRGBImage of width * height and return pointer
+ * to it. Returns NULL on failure
+ */
+static GdkRGBImage *gdk_rgb_image_new(
+ gint width,
+ gint height)
+{
+ GdkRGBImage *result;
+
+ /* Allocate a struct */
+ result = g_new(GdkRGBImage, 1);
+
+ /* Oops */
+ if (result == NULL) return (NULL);
+
+ /* Allocate buffer */
+ result->image = g_new0(guchar, width * height * 3);
+
+ /* Oops */
+ if (result->image == NULL)
+ {
+ g_free(result);
+ return (NULL);
+ }
+
+ /* Initialise size fields */
+ result->width = width;
+ result->height = height;
+
+ /* Initialise reference count */
+ result->ref_count = 1;
+
+ /* Success */
+ return (result);
+}
+
+/*
+ * Free a GdkRGBImage
+ */
+static void gdk_rgb_image_destroy(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ if (im == NULL) return;
+
+ /* Free the RGB buffer */
+ g_free(im->image);
+
+ /* Free the structure */
+ g_free(im);
+}
+
+
+#if 0
+
+/*
+ * Unref a GdkRGBImage
+ */
+static void gdk_rgb_image_unref(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Decrease reference count by 1 */
+ im->ref_count--;
+
+ /* Free if nobody's using it */
+ if (im->ref_count <= 0) gdk_rgb_image_destroy(im);
+}
+
+
+/*
+ * Reference a GdkRGBImage
+ */
+static void gdk_rgb_image_ref(
+ GdkRGBImage *im)
+{
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Increase reference count by 1 */
+ im->ref_count++;
+}
+
+#endif /* 0 */
+
+
+/*
+ * Write RGB pixel of the format 0xRRGGBB to (x, y) in GdkRGBImage
+ */
+static void gdk_rgb_image_put_pixel(
+ GdkRGBImage *im,
+ gint x,
+ gint y,
+ guint32 pixel)
+{
+ guchar *rgbp;
+
+ /* Paranoia */
+ g_return_if_fail(im != NULL);
+
+ /* Paranoia */
+ if ((x < 0) || (x >= im->width)) return;
+
+ /* Paranoia */
+ if ((y < 0) || (y >= im->height)) return;
+
+ /* Access RGB data */
+ rgbp = &im->image[(y * im->width * 3) + (x * 3)];
+
+ /* Red */
+ *rgbp++ = (pixel >> 16) & 0xFF;
+ /* Green */
+ *rgbp++ = (pixel >> 8) & 0xFF;
+ /* Blue */
+ *rgbp = pixel & 0xFF;
+}
+
+
+/*
+ * Returns RGB pixel (0xRRGGBB) at (x, y) in GdkRGBImage
+ */
+static guint32 gdk_rgb_image_get_pixel(
+ GdkRGBImage *im,
+ gint x,
+ gint y)
+{
+ guchar *rgbp;
+
+ /* Paranoia */
+ if (im == NULL) return (0);
+
+ /* Paranoia - returns black */
+ if ((x < 0) || (x >= im->width)) return (0);
+
+ /* Paranoia */
+ if ((y < 0) || (y >= im->height)) return (0);
+
+ /* Access RGB data */
+ rgbp = &im->image[(y * im->width * 3) + (x * 3)];
+
+ /* Return result */
+ return ((rgbp[0] << 16) | (rgbp[1] << 8) | (rgbp[2]));
+}
+
+
+/*
+ * Since gdk_draw_rgb_image is a bit harder to use than it's
+ * GdkImage counterpart, I wrote a grue function that takes
+ * exactly the same parameters as gdk_draw_image, with
+ * the GdkImage parameter replaced with GdkRGBImage.
+ */
+static void gdk_draw_rgb_image_2(
+ GdkDrawable *drawable,
+ GdkGC *gc,
+ GdkRGBImage *image,
+ gint xsrc,
+ gint ysrc,
+ gint xdest,
+ gint ydest,
+ gint width,
+ gint height)
+{
+ /* Paranoia */
+ g_return_if_fail(drawable != NULL);
+ g_return_if_fail(image != NULL);
+
+ /* Paranoia */
+ if (xsrc < 0 || (xsrc + width - 1) >= image->width) return;
+ if (ysrc < 0 || (ysrc + height - 1) >= image->height) return;
+
+ /* Draw the image at (xdest, ydest), with dithering if bpp <= 8/16 */
+ gdk_draw_rgb_image(
+ drawable,
+ gc,
+ xdest,
+ ydest,
+ width,
+ height,
+ dith_mode,
+ &image->image[(ysrc * image->width * 3) + (xsrc * 3)],
+ image->width * 3);
+}
+
+
+/*
+ * Code for smooth icon rescaling from Uwe Siems, Jan 2000
+ *
+ * XXX XXX Duplication of maid-x11.c, again. It doesn't do any colour
+ * allocation, either.
+ */
+
+/*
+ * to save ourselves some labour, define a maximum expected icon width here:
+ */
+#define MAX_ICON_WIDTH 32
+
+
+/*
+ * Each pixel is kept in this structure during smooth rescaling
+ * calculations, to make things a bit easier
+ */
+typedef struct rgb_type rgb_type;
+
+struct rgb_type
+{
+ guint32 red;
+ guint32 green;
+ guint32 blue;
+};
+
+/*
+ * Because there are many occurences of this, and because
+ * it's logical to do so...
+ */
+#define pixel_to_rgb(pix, rgb_buf) \
+(rgb_buf)->red = ((pix) >> 16) & 0xFF; \
+(rgb_buf)->green = ((pix) >> 8) & 0xFF; \
+(rgb_buf)->blue = (pix) & 0xFF
+
+
+/*
+ * get_scaled_row reads a scan from the given GdkRGBImage, scales it smoothly
+ * and returns the red, green and blue values in arrays.
+ * The values in this arrays must be divided by a certain value that is
+ * calculated in scale_icon.
+ * x, y is the position, iw is the input width and ow the output width
+ * scan must be sufficiently sized
+ */
+static void get_scaled_row(
+ GdkRGBImage *im,
+ int x,
+ int y,
+ int iw,
+ int ow,
+ rgb_type *scan)
+{
+ int xi, si, sifrac, ci, cifrac, add_whole, add_frac;
+ guint32 pix;
+ rgb_type prev;
+ rgb_type next;
+ bool get_next_pix;
+
+ /* Unscaled */
+ if (iw == ow)
+ {
+ for (xi = 0; xi < ow; xi++)
+ {
+ pix = gdk_rgb_image_get_pixel(im, x + xi, y);
+ pixel_to_rgb(pix, &scan[xi]);
+ }
+ }
+
+ /* Scaling by subsampling (grow) */
+ else if (iw < ow)
+ {
+ iw--;
+ ow--;
+
+ /* read first pixel: */
+ pix = gdk_rgb_image_get_pixel(im, x, y);
+ pixel_to_rgb(pix, &next);
+ prev = next;
+
+ /* si and sifrac give the subsampling position: */
+ si = x;
+ sifrac = 0;
+
+ /* get_next_pix tells us, that we need the next pixel */
+ get_next_pix = TRUE;
+
+ for (xi = 0; xi <= ow; xi++)
+ {
+ if (get_next_pix)
+ {
+ prev = next;
+ if (xi < ow)
+ {
+ /* only get next pixel if in same icon */
+ pix = gdk_rgb_image_get_pixel(im, si + 1, y);
+ pixel_to_rgb(pix, &next);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by ow occurs in scale_icon */
+ scan[xi].red = prev.red * (ow - sifrac) + next.red * sifrac;
+ scan[xi].green = prev.green * (ow - sifrac) + next.green * sifrac;
+ scan[xi].blue = prev.blue * (ow - sifrac) + next.blue * sifrac;
+
+ /* advance sampling position: */
+ sifrac += iw;
+ if (sifrac >= ow)
+ {
+ si++;
+ sifrac -= ow;
+ get_next_pix = TRUE;
+ }
+ else
+ {
+ get_next_pix = FALSE;
+ }
+
+ }
+ }
+
+ /* Scaling by averaging (shrink) */
+ else
+ {
+ /* width of an output pixel in input pixels: */
+ add_whole = iw / ow;
+ add_frac = iw % ow;
+
+ /* start position of the first output pixel: */
+ si = x;
+ sifrac = 0;
+
+ /* get first input pixel: */
+ pix = gdk_rgb_image_get_pixel(im, x, y);
+ pixel_to_rgb(pix, &next);
+
+ for (xi = 0; xi < ow; xi++)
+ {
+ /* find endpoint of the current output pixel: */
+ ci = si + add_whole;
+ cifrac = sifrac + add_frac;
+ if (cifrac >= ow)
+ {
+ ci++;
+ cifrac -= ow;
+ }
+
+ /* take fraction of current input pixel (starting segment): */
+ scan[xi].red = next.red * (ow - sifrac);
+ scan[xi].green = next.green * (ow - sifrac);
+ scan[xi].blue = next.blue * (ow - sifrac);
+ si++;
+
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ rgb_type tmp_rgb;
+
+ pix = gdk_rgb_image_get_pixel(im, si, y);
+ pixel_to_rgb(pix, &tmp_rgb);
+ scan[xi].red += tmp_rgb.red * ow;
+ scan[xi].green += tmp_rgb.green * ow;
+ scan[xi].blue += tmp_rgb.blue * ow;
+ si++;
+ }
+
+ /* add fraction of current input pixel (ending segment): */
+ if (xi < ow - 1)
+ {
+ /* only get next pixel if still in icon: */
+ pix = gdk_rgb_image_get_pixel(im, si, y);
+ pixel_to_rgb(pix, &next);
+ }
+
+ sifrac = cifrac;
+ if (sifrac > 0)
+ {
+ scan[xi].red += next.red * sifrac;
+ scan[xi].green += next.green * sifrac;
+ scan[xi].blue += next.blue * sifrac;
+ }
+ }
+ }
+}
+
+
+/*
+ * put_rgb_scan takes arrays for red, green and blue and writes pixel values
+ * according to this values in the GdkRGBImage-structure. w is the number of
+ * pixels to write and div is the value by which all red/green/blue values
+ * are divided first.
+ */
+static void put_rgb_scan(
+ GdkRGBImage *im,
+ int x,
+ int y,
+ int w,
+ int div,
+ rgb_type *scan)
+{
+ int xi;
+ guint32 pix;
+ guint32 adj = div / 2;
+
+ for (xi = 0; xi < w; xi++)
+ {
+ byte r, g, b;
+
+ /* un-factor the RGB values */
+ r = (scan[xi].red + adj) / div;
+ g = (scan[xi].green + adj) / div;
+ b = (scan[xi].blue + adj) / div;
+
+#ifdef SUPPORT_GAMMA
+
+ /* Apply gamma correction if requested and available */
+ if (gamma_table_ready)
+ {
+ r = gamma_table[r];
+ g = gamma_table[g];
+ b = gamma_table[b];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Make a (virtual) 24-bit pixel */
+ pix = (r << 16) | (g << 8) | (b);
+
+ /* Draw it into image */
+ gdk_rgb_image_put_pixel(im, x + xi, y, pix);
+ }
+}
+
+
+/*
+ * scale_icon transfers an area from GdkRGBImage im_in, locate (x1,y1) to
+ * im_out, locate (x2, y2). Source size is (ix, iy) and destination size
+ * is (ox, oy).
+ *
+ * It does this by getting icon scan line from get_scaled_scan and handling
+ * them the same way as pixels are handled in get_scaled_scan.
+ * This even allows icons to be scaled differently in horizontal and
+ * vertical directions (eg. shrink horizontal, grow vertical).
+ */
+static void scale_icon(
+ GdkRGBImage *im_in,
+ GdkRGBImage *im_out,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ int div;
+ int xi, yi, si, sifrac, ci, cifrac, add_whole, add_frac;
+
+ /* buffers for pixel rows: */
+ rgb_type prev[MAX_ICON_WIDTH];
+ rgb_type next[MAX_ICON_WIDTH];
+ rgb_type temp[MAX_ICON_WIDTH];
+
+ bool get_next_row;
+
+ /* get divider value for the horizontal scaling: */
+ if (ix == ox)
+ div = 1;
+ else if (ix < ox)
+ div = ox - 1;
+ else
+ div = ix;
+
+ /* no scaling needed vertically: */
+ if (iy == oy)
+ {
+ for (yi = 0; yi < oy; yi++)
+ {
+ get_scaled_row(im_in, x1, y1 + yi, ix, ox, temp);
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+ }
+ }
+
+ /* scaling by subsampling (grow): */
+ else if (iy < oy)
+ {
+ iy--;
+ oy--;
+ div *= oy;
+
+ /* get first row: */
+ get_scaled_row(im_in, x1, y1, ix, ox, next);
+
+ /* si and sifrac give the subsampling position: */
+ si = y1;
+ sifrac = 0;
+
+ /* get_next_row tells us, that we need the next row */
+ get_next_row = TRUE;
+ for (yi = 0; yi <= oy; yi++)
+ {
+ if (get_next_row)
+ {
+ for (xi = 0; xi < ox; xi++)
+ {
+ prev[xi] = next[xi];
+ }
+ if (yi < oy)
+ {
+ /* only get next row if in same icon */
+ get_scaled_row(im_in, x1, si + 1, ix, ox, next);
+ }
+ }
+
+ /* calculate subsampled color values: */
+ /* division by oy occurs in put_rgb_scan */
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red = (prev[xi].red * (oy - sifrac) +
+ next[xi].red * sifrac);
+ temp[xi].green = (prev[xi].green * (oy - sifrac) +
+ next[xi].green * sifrac);
+ temp[xi].blue = (prev[xi].blue * (oy - sifrac) +
+ next[xi].blue * sifrac);
+ }
+
+ /* write row to output image: */
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+
+ /* advance sampling position: */
+ sifrac += iy;
+ if (sifrac >= oy)
+ {
+ si++;
+ sifrac -= oy;
+ get_next_row = TRUE;
+ }
+ else
+ {
+ get_next_row = FALSE;
+ }
+
+ }
+ }
+
+ /* scaling by averaging (shrink) */
+ else
+ {
+ div *= iy;
+
+ /* height of a output row in input rows: */
+ add_whole = iy / oy;
+ add_frac = iy % oy;
+
+ /* start position of the first output row: */
+ si = y1;
+ sifrac = 0;
+
+ /* get first input row: */
+ get_scaled_row(im_in, x1, y1, ix, ox, next);
+ for (yi = 0; yi < oy; yi++)
+ {
+ /* find endpoint of the current output row: */
+ ci = si + add_whole;
+ cifrac = sifrac + add_frac;
+ if (cifrac >= oy)
+ {
+ ci++;
+ cifrac -= oy;
+ }
+
+ /* take fraction of current input row (starting segment): */
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red = next[xi].red * (oy - sifrac);
+ temp[xi].green = next[xi].green * (oy - sifrac);
+ temp[xi].blue = next[xi].blue * (oy - sifrac);
+ }
+ si++;
+
+ /* add values for whole pixels: */
+ while (si < ci)
+ {
+ get_scaled_row(im_in, x1, si, ix, ox, next);
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red += next[xi].red * oy;
+ temp[xi].green += next[xi].green * oy;
+ temp[xi].blue += next[xi].blue * oy;
+ }
+ si++;
+ }
+
+ /* add fraction of current input row (ending segment): */
+ if (yi < oy - 1)
+ {
+ /* only get next row if still in icon: */
+ get_scaled_row(im_in, x1, si, ix, ox, next);
+ }
+ sifrac = cifrac;
+ for (xi = 0; xi < ox; xi++)
+ {
+ temp[xi].red += next[xi].red * sifrac;
+ temp[xi].green += next[xi].green * sifrac;
+ temp[xi].blue += next[xi].blue * sifrac;
+ }
+
+ /* write row to output image: */
+ put_rgb_scan(im_out, x2, y2 + yi, ox, div, temp);
+ }
+ }
+}
+
+
+/*
+ * Rescale icons using sort of anti-aliasing technique.
+ */
+static GdkRGBImage *resize_tiles_smooth(
+ GdkRGBImage *im,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ int width1, height1, width2, height2;
+ int x1, x2, y1, y2;
+
+ GdkRGBImage *tmp;
+
+ /* Original size */
+ width1 = im->width;
+ height1 = im->height;
+
+ /* Rescaled size */
+ width2 = ox * width1 / ix;
+ height2 = oy * height1 / iy;
+
+ /* Allocate GdkRGBImage for resized tiles */
+ tmp = gdk_rgb_image_new(width2, height2);
+
+ /* Oops */
+ if (tmp == NULL) return (NULL);
+
+ /* Scale each icon */
+ for (y1 = 0, y2 = 0; (y1 < height1) && (y2 < height2); y1 += iy, y2 += oy)
+ {
+ for (x1 = 0, x2 = 0; (x1 < width1) && (x2 < width2); x1 += ix, x2 += ox)
+ {
+ scale_icon(im, tmp, x1, y1, x2, y2,
+ ix, iy, ox, oy);
+ }
+ }
+
+ return tmp;
+}
+
+
+/*
+ * Steven Fuerst's tile resizing code
+ * Taken from Z because I think the algorithm is cool.
+ */
+
+/* 24-bit version - GdkRGB uses 24 bit RGB data internally */
+static void copy_pixels(
+ int wid,
+ int y,
+ int offset,
+ int *xoffsets,
+ GdkRGBImage *old_image,
+ GdkRGBImage *new_image)
+{
+ int i;
+
+ /* Get source and destination */
+ byte *src = &old_image->image[offset * old_image->width * 3];
+ byte *dst = &new_image->image[y * new_image->width * 3];
+
+ /* Copy to the image */
+ for (i = 0; i < wid; i++)
+ {
+#ifdef SUPPORT_GAMMA
+
+ if (gamma_table_ready)
+ {
+ *dst++ = gamma_table[src[3 * xoffsets[i]]];
+ *dst++ = gamma_table[src[3 * xoffsets[i] + 1]];
+ *dst++ = gamma_table[src[3 * xoffsets[i] + 2]];
+
+ continue;
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ *dst++ = src[3 * xoffsets[i]];
+ *dst++ = src[3 * xoffsets[i] + 1];
+ *dst++ = src[3 * xoffsets[i] + 2];
+ }
+}
+
+
+#if 0
+
+/* 32-bit version: it might be useful in the future */
+static void copy_pixels(
+ int wid,
+ int y,
+ int offset,
+ int *xoffsets,
+ GdkRGBImage *old_image,
+ GdkRGBImage *new_image)
+{
+ int i;
+
+ /* Get source and destination */
+ byte *src = &old_image->image[offset * old_image->width * 4];
+ byte *dst = &new_image->image[y * new_image->width * 4];
+
+ /* Copy to the image */
+ for (i = 0; i < wid; i++)
+ {
+ *dst++ = src[4 * xoffsets[i]];
+ *dst++ = src[4 * xoffsets[i] + 1];
+ *dst++ = src[4 * xoffsets[i] + 2];
+ *dst++ = src[4 * xoffsets[i] + 3];
+ }
+}
+
+#endif
+
+
+/*
+ * Resize ix * iy pixel tiles in old to ox * oy pixels
+ * and return a new GdkRGBImage containing the resized tiles
+ */
+static GdkRGBImage *resize_tiles_fast(
+ GdkRGBImage *old_image,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ GdkRGBImage *new_image;
+
+ int old_wid, old_hgt;
+
+ int new_wid, new_hgt;
+
+ int add, remainder, rem_tot, offset;
+
+ int *xoffsets;
+
+ int i;
+
+
+ /* Get the size of the old image */
+ old_wid = old_image->width;
+ old_hgt = old_image->height;
+
+ /* Calculate the size of the new image */
+ new_wid = (old_wid / ix) * ox;
+ new_hgt = (old_hgt / iy) * oy;
+
+ /* Allocate a GdkRGBImage to store resized tiles */
+ new_image = gdk_rgb_image_new(new_wid, new_hgt);
+
+ /* Paranoia */
+ if (new_image == NULL) return (NULL);
+
+ /* now begins the cool part of SF's code */
+
+ /*
+ * Calculate an offsets table, so the transformation
+ * is faster. This is much like the Bresenham algorithm
+ */
+
+ /* Set up x offset table */
+ C_MAKE(xoffsets, new_wid, int);
+
+ /* Initialize line parameters */
+ add = old_wid / new_wid;
+ remainder = old_wid % new_wid;
+
+ /* Start at left */
+ offset = 0;
+
+ /* Half-tile offset so 'line' is centered correctly */
+ rem_tot = new_wid / 2;
+
+ for (i = 0; i < new_wid; i++)
+ {
+ /* Store into the table */
+ xoffsets[i] = offset;
+
+ /* Move to next entry */
+ offset += add;
+
+ /* Take care of fractional part */
+ rem_tot += remainder;
+ if (rem_tot >= new_wid)
+ {
+ rem_tot -= new_wid;
+ offset++;
+ }
+ }
+
+ /* Scan each row */
+
+ /* Initialize line parameters */
+ add = old_hgt / new_hgt;
+ remainder = old_hgt % new_hgt;
+
+ /* Start at left */
+ offset = 0;
+
+ /* Half-tile offset so 'line' is centered correctly */
+ rem_tot = new_hgt / 2;
+
+ for (i = 0; i < new_hgt; i++)
+ {
+ /* Copy pixels to new image */
+ copy_pixels(new_wid, i, offset, xoffsets, old_image, new_image);
+
+ /* Move to next entry */
+ offset += add;
+
+ /* Take care of fractional part */
+ rem_tot += remainder;
+ if (rem_tot >= new_hgt)
+ {
+ rem_tot -= new_hgt;
+ offset++;
+ }
+ }
+
+ /* Free offset table */
+ C_FREE(xoffsets, new_wid, int);
+
+ return (new_image);
+}
+
+
+/*
+ * Resize an image of ix * iy pixels and return a newly allocated
+ * image of ox * oy pixels.
+ */
+static GdkRGBImage *resize_tiles(
+ GdkRGBImage *im,
+ int ix,
+ int iy,
+ int ox,
+ int oy)
+{
+ GdkRGBImage *result;
+
+ /*
+ * I hope we can always use this with GdkRGB, which uses a 5x5x5
+ * colour cube (125 colours) by default, and resort to dithering
+ * when it can't find good match there or expand the cube, so it
+ * works with 8bpp X servers.
+ */
+ if (smooth_rescaling_request && (ix != ox || iy != oy))
+ {
+ result = resize_tiles_smooth(im, ix, iy, ox, oy);
+ }
+
+ /*
+ * Unless smoothing is requested by user, we use the fast
+ * resizing code.
+ */
+ else
+ {
+ result = resize_tiles_fast(im, ix, iy, ox, oy);
+ }
+
+ /* Return rescaled tiles, or NULL */
+ return (result);
+}
+
+
+/*
+ * Tile loaders - XPM and BMP
+ */
+
+/*
+ * A helper function for the XPM loader
+ *
+ * Read next string delimited by double quotes from
+ * the input stream. Return TRUE on success, FALSE
+ * if it finds EOF or buffer overflow.
+ *
+ * I never mean this to be generic, so its EOF and buffer
+ * overflow behaviour is terribly stupid -- there are no
+ * provisions for recovery.
+ *
+ * CAVEAT: treatment of backslash is not compatible with the standard
+ * C usage XXX XXX XXX XXX
+ */
+static bool read_str(char *buf, u32b len, FILE *f)
+{
+ int c;
+
+ /* Paranoia - Buffer too small */
+ if (len <= 0) return (FALSE);
+
+ /* Find " */
+ while ((c = getc(f)) != '"')
+ {
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+ }
+
+ while (1)
+ {
+ /* Read next char */
+ c = getc(f);
+
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+
+ /* Terminating " */
+ if (c == '"') break;
+
+ /* Escape */
+ if (c == '\\')
+ {
+ /* Use next char */
+ c = getc(f);
+
+ /* Premature EOF */
+ if (c == EOF) return (FALSE);
+ }
+
+ /* Store character in the buffer */
+ *buf++ = c;
+
+ /* Decrement count */
+ len--;
+
+ /* Buffer full - we have to place a NULL at the end */
+ if (len <= 0) return (FALSE);
+ }
+
+ /* Make a C string if there's room left */
+ if (len > 0) *buf = '\0';
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Remember pixel symbol to RGB colour mappings
+ */
+
+/*
+ * I've forgot the formula, but I remember prime number yields
+ * good results
+ */
+#define HASH_SIZE 19
+
+typedef struct pal_type pal_type;
+
+struct pal_type
+{
+ u32b str;
+ u32b rgb;
+ pal_type *next;
+};
+
+
+/*
+ * A simple, slow and stupid XPM loader
+ */
+static GdkRGBImage *load_xpm(cptr filename)
+{
+ FILE *f;
+ GdkRGBImage *img = NULL;
+ int width, height, colours, chars;
+ int i, j, k;
+ bool ret;
+ pal_type *pal = NULL;
+ pal_type *head[HASH_SIZE];
+ u32b buflen = 0;
+ char *lin = NULL;
+ char buf[1024];
+
+ /* Build path to the XPM file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, filename);
+
+ /* Open it */
+ f = my_fopen(buf, "r");
+
+ /* Oops */
+ if (f == NULL) return (NULL);
+
+ /* Read header */
+ ret = read_str(buf, 1024, f);
+
+ /* Oops */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("Cannot find XPM header");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Parse header */
+ if (4 != sscanf(buf, "%d %d %d %d", &width, &height, &colours, &chars))
+ {
+ /* Notify error */
+ plog("Bad XPM header");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /*
+ * Paranoia - the code can handle upto four letters per pixel,
+ * but such large number of colours certainly requires a smarter
+ * symbol-to-colour mapping algorithm...
+ */
+ if ((width <= 0) || (height <= 0) || (colours <= 0) || (chars <= 0) ||
+ (chars > 2))
+ {
+ /* Notify error */
+ plog("Invalid width/height/depth");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Allocate palette */
+ C_MAKE(pal, colours, pal_type);
+
+ /* Initialise hash table */
+ for (i = 0; i < HASH_SIZE; i++) head[i] = NULL;
+
+ /* Parse palette */
+ for (i = 0; i < colours; i++)
+ {
+ u32b tmp;
+ int h_idx;
+
+ /* Read next string */
+ ret = read_str(buf, 1024, f);
+
+ /* Check I/O result */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("EOF in palette");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Clear symbol code */
+ tmp = 0;
+
+ /* Encode pixel symbol */
+ for (j = 0; j < chars; j++)
+ {
+ tmp = (tmp << 8) | (buf[j] & 0xFF);
+ }
+
+ /* Remember it */
+ pal[i].str = tmp;
+
+ /* Skip spaces */
+ while ((buf[j] == ' ') || (buf[j] == '\t')) j++;
+
+ /* Verify 'c' */
+ if (buf[j] != 'c')
+ {
+ /* Notify error */
+ plog("No 'c' in palette definition");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Advance cursor */
+ j++;
+
+ /* Skip spaces */
+ while ((buf[j] == ' ') || (buf[j] == '\t')) j++;
+
+ /* Hack - Assume 'None' */
+ if (buf[j] == 'N')
+ {
+ /* Angband always uses black background */
+ pal[i].rgb = 0x000000;
+ }
+
+ /* Read colour */
+ else if ((1 != sscanf(&buf[j], "#%06lX", &tmp)) &&
+ (1 != sscanf(&buf[j], "#%06lx", &tmp)))
+ {
+ /* Notify error */
+ plog("Badly formatted colour");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Remember it */
+ pal[i].rgb = tmp;
+
+ /* Store it in hash table as well */
+ h_idx = pal[i].str % HASH_SIZE;
+
+ /* Link the entry */
+ pal[i].next = head[h_idx];
+ head[h_idx] = &pal[i];
+ }
+
+ /* Allocate image */
+ img = gdk_rgb_image_new(width, height);
+
+ /* Oops */
+ if (img == NULL)
+ {
+ /* Notify error */
+ plog("Cannot allocate image");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Calculate buffer length */
+ buflen = width * chars + 1;
+
+ /* Allocate line buffer */
+ C_MAKE(lin, buflen, char);
+
+ /* For each row */
+ for (i = 0; i < height; i++)
+ {
+ /* Read a row of image data */
+ ret = read_str(lin, buflen, f);
+
+ /* Oops */
+ if (!ret)
+ {
+ /* Notify error */
+ plog("EOF in middle of image data");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* For each column */
+ for (j = 0; j < width; j++)
+ {
+ u32b tmp;
+ pal_type *h_ptr;
+
+ /* Clear encoded pixel */
+ tmp = 0;
+
+ /* Encode pixel symbol */
+ for (k = 0; k < chars; k++)
+ {
+ tmp = (tmp << 8) | (lin[j * chars + k] & 0xFF);
+ }
+
+ /* Find colour */
+ for (h_ptr = head[tmp % HASH_SIZE];
+ h_ptr != NULL;
+ h_ptr = h_ptr->next)
+ {
+ /* Found a match */
+ if (h_ptr->str == tmp) break;
+ }
+
+ /* No match found */
+ if (h_ptr == NULL)
+ {
+ /* Notify error */
+ plog("Invalid pixel symbol");
+
+ /* Failure */
+ goto oops;
+ }
+
+ /* Draw it */
+ gdk_rgb_image_put_pixel(
+ img,
+ j,
+ i,
+ h_ptr->rgb);
+ }
+ }
+
+ /* Close file */
+ my_fclose(f);
+
+ /* Free line buffer */
+ C_FREE(lin, buflen, char);
+
+ /* Free palette */
+ C_FREE(pal, colours, pal_type);
+
+ /* Return result */
+ return (img);
+
+oops:
+
+ /* Close file */
+ my_fclose(f);
+
+ /* Free image */
+ if (img) gdk_rgb_image_destroy(img);
+
+ /* Free line buffer */
+ if (lin) C_FREE(lin, buflen, char);
+
+ /* Free palette */
+ if (pal) C_FREE(pal, colours, pal_type);
+
+ /* Failure */
+ return (NULL);
+}
+
+
+/*
+ * A BMP loader, yet another duplication of maid-x11.c functions.
+ *
+ * Another duplication, again because of different image format and
+ * avoidance of colour allocation.
+ *
+ * XXX XXX XXX XXX Should avoid using a propriatary and closed format.
+ * Since it's much bigger than gif that was used before, why don't
+ * we switch to XPM? NetHack does. Well, NH has always been much
+ * closer to the GNU/Un*x camp and it's GPL'ed quite early...
+ *
+ * The names and naming convention are worse than the worst I've ever
+ * seen, so I deliberately changed them to fit well with the rest of
+ * the code. Or are they what xx calls them? If it's the case, there's
+ * no reason to follow *their* words.
+ */
+
+/*
+ * BMP file header
+ */
+typedef struct bmp_file_type bmp_file_type;
+
+struct bmp_file_type
+{
+ u16b type;
+ u32b size;
+ u16b reserved1;
+ u16b reserved2;
+ u32b offset;
+};
+
+
+/*
+ * BMP file information fields
+ */
+typedef struct bmp_info_type bmp_info_type;
+
+struct bmp_info_type
+{
+ u32b size;
+ u32b width;
+ u32b height;
+ u16b planes;
+ u16b bit_count;
+ u32b compression;
+ u32b size_image;
+ u32b x_pels_per_meter;
+ u32b y_pels_per_meter;
+ u32b colors_used;
+ u32b color_importand;
+};
+
+/*
+ * "RGBQUAD" type.
+ */
+typedef struct rgb_quad_type rgb_quad_type;
+
+struct rgb_quad_type
+{
+ unsigned char b, g, r;
+ unsigned char filler;
+};
+
+
+/*** Helper functions for system independent file loading. ***/
+
+static byte get_byte(FILE *fff)
+{
+ /* Get a character, and return it */
+ return (getc(fff) & 0xFF);
+}
+
+static void rd_byte(FILE *fff, byte *ip)
+{
+ *ip = get_byte(fff);
+}
+
+static void rd_u16b(FILE *fff, u16b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u16b)(get_byte(fff)) << 8);
+}
+
+static void rd_u32b(FILE *fff, u32b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u32b)(get_byte(fff)) << 8);
+ (*ip) |= ((u32b)(get_byte(fff)) << 16);
+ (*ip) |= ((u32b)(get_byte(fff)) << 24);
+}
+
+
+/*
+ * Read a BMP file (a certain trademark nuked)
+ *
+ * This function replaces the old ReadRaw and RemapColors functions.
+ *
+ * Assumes that the bitmap has a size such that no padding is needed in
+ * various places. Currently only handles bitmaps with 3 to 256 colors.
+ */
+GdkRGBImage *load_bmp(cptr filename)
+{
+ FILE *f;
+
+ char path[1024];
+
+ bmp_file_type file_hdr;
+ bmp_info_type info_hdr;
+
+ GdkRGBImage *result = NULL;
+
+ int ncol;
+
+ int i;
+
+ u32b x, y;
+
+ guint32 colour_pixels[256];
+
+
+ /* Build the path to the bmp file */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_GRAF, filename);
+
+ /* Open the BMP file */
+ f = fopen(path, "r");
+
+ /* No such file */
+ if (f == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Read the "bmp_file_type" */
+ rd_u16b(f, &file_hdr.type);
+ rd_u32b(f, &file_hdr.size);
+ rd_u16b(f, &file_hdr.reserved1);
+ rd_u16b(f, &file_hdr.reserved2);
+ rd_u32b(f, &file_hdr.offset);
+
+ /* Read the "bmp_info_type" */
+ rd_u32b(f, &info_hdr.size);
+ rd_u32b(f, &info_hdr.width);
+ rd_u32b(f, &info_hdr.height);
+ rd_u16b(f, &info_hdr.planes);
+ rd_u16b(f, &info_hdr.bit_count);
+ rd_u32b(f, &info_hdr.compression);
+ rd_u32b(f, &info_hdr.size_image);
+ rd_u32b(f, &info_hdr.x_pels_per_meter);
+ rd_u32b(f, &info_hdr.y_pels_per_meter);
+ rd_u32b(f, &info_hdr.colors_used);
+ rd_u32b(f, &info_hdr.color_importand);
+
+ /* Verify the header */
+ if (feof(f) ||
+ (file_hdr.type != 19778) ||
+ (info_hdr.size != 40))
+ {
+ plog(format("Incorrect BMP file format %s", filename));
+ fclose(f);
+ return (NULL);
+ }
+
+ /*
+ * The two headers above occupy 54 bytes total
+ * The "offset" field says where the data starts
+ * The "colors_used" field does not seem to be reliable
+ */
+
+ /* Compute number of colors recorded */
+ ncol = (file_hdr.offset - 54) / 4;
+
+ for (i = 0; i < ncol; i++)
+ {
+ rgb_quad_type clr;
+
+ /* Read an "rgb_quad_type" */
+ rd_byte(f, &clr.b);
+ rd_byte(f, &clr.g);
+ rd_byte(f, &clr.r);
+ rd_byte(f, &clr.filler);
+
+ /* Remember the pixel */
+ colour_pixels[i] = (clr.r << 16) | (clr.g << 8) | (clr.b);
+ }
+
+ /* Allocate GdkRGBImage large enough to store the image */
+ result = gdk_rgb_image_new(info_hdr.width, info_hdr.height);
+
+ /* Failure */
+ if (result == NULL)
+ {
+ fclose(f);
+ return (NULL);
+ }
+
+ for (y = 0; y < info_hdr.height; y++)
+ {
+ u32b y2 = info_hdr.height - y - 1;
+
+ for (x = 0; x < info_hdr.width; x++)
+ {
+ int ch = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ if (info_hdr.bit_count == 24)
+ {
+ int c3, c2 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ c3 = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f))
+ {
+ plog(format("Unexpected end of file in %s", filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+
+ /* Draw the pixel */
+ gdk_rgb_image_put_pixel(
+ result,
+ x,
+ y2,
+ (ch << 16) | (c2 << 8) | (c3));
+ }
+ else if (info_hdr.bit_count == 8)
+ {
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch]);
+ }
+ else if (info_hdr.bit_count == 4)
+ {
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch / 16]);
+ x++;
+ gdk_rgb_image_put_pixel(result, x, y2, colour_pixels[ch % 16]);
+ }
+ else
+ {
+ /* Technically 1 bit is legal too */
+ plog(format("Illegal bit count %d in %s",
+ info_hdr.bit_count, filename));
+ gdk_rgb_image_destroy(result);
+ fclose(f);
+ return (NULL);
+ }
+ }
+ }
+
+ fclose(f);
+
+ return result;
+}
+
+
+/*
+ * Try to load an XPM file, or a BMP file if it fails
+ *
+ * Choice of file format may better be made yet another option XXX
+ */
+static GdkRGBImage *load_tiles(cptr basename)
+{
+ char buf[32];
+ GdkRGBImage *img;
+
+ /* build xpm file name */
+ strnfmt(buf, 32, "%s.xpm", basename);
+
+ /* Try to load it */
+ img = load_xpm(buf);
+
+ /* OK */
+ if (img) return (img);
+
+ /* Try again for a bmp file */
+ strnfmt(buf, 32, "%s.bmp", basename);
+
+ /* Try loading it */
+ img = load_bmp(buf);
+
+ /* Return result, success or failure */
+ return (img);
+}
+
+
+/*
+ * Free all tiles and graphics buffers associated with windows
+ *
+ * This is conspirator of graf_init() below, sharing its inefficiency
+ */
+static void graf_nuke()
+{
+ int i;
+
+ term_data *td;
+
+
+ /* Nuke all terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access term_data structure */
+ td = &data[i];
+
+ /* Disable graphics */
+ td->t.higher_pict = FALSE;
+
+ /* Free previously allocated tiles */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Free previously allocated transparency buffer */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Forget stale pointer */
+ td->trans_buf = NULL;
+
+# endif /* USE_TRANSPARENCY */
+
+ }
+}
+
+
+/*
+ * Load tiles, scale them to current font size, and store a pointer
+ * to them in a term_data structure for each term.
+ *
+ * XXX XXX XXX This is a terribly stupid quick hack.
+ *
+ * XXX XXX XXX Windows using the same font should share resized tiles
+ */
+static bool graf_init(
+ cptr filename,
+ int tile_wid,
+ int tile_hgt)
+{
+ term_data *td;
+
+ bool result;
+
+ GdkRGBImage *raw_tiles, *scaled_tiles;
+
+# ifdef USE_TRANSPARENCY
+ GdkRGBImage *buffer;
+# endif /* USE_TRANSPARENCY */
+
+ int i;
+
+
+ /* Paranoia */
+ if (filename == NULL) return (FALSE);
+
+ /* Load tiles */
+ raw_tiles = load_tiles(filename);
+
+ /* Oops */
+ if (raw_tiles == NULL)
+ {
+ /* Clean up */
+ graf_nuke();
+
+ /* Failure */
+ return (FALSE);
+ }
+
+ /* Calculate and remember numbers of rows and columns */
+ tile_rows = raw_tiles->height / tile_hgt;
+ tile_cols = raw_tiles->width / tile_wid;
+
+ /* Be optimistic */
+ result = TRUE;
+
+
+ /*
+ * (Re-)init each term
+ * XXX It might help speeding this up to avoid doing so if a window
+ * doesn't need graphics (e.g. inventory/equipment and message recall).
+ */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access term_data */
+ td = &data[i];
+
+ /* Shouldn't waste anything for unused terms */
+ if (!td->shown) continue;
+
+ /* Enable graphics */
+ td->t.higher_pict = TRUE;
+
+ /* See if we need rescaled tiles XXX */
+ if ((td->tiles == NULL) ||
+ (td->tiles->width != td->tile_wid * tile_cols) ||
+ (td->tiles->height != td->tile_hgt * tile_rows))
+ {
+ /* Free old tiles if present */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+ /* Scale the tiles to current font bounding rect */
+ scaled_tiles = resize_tiles(
+ raw_tiles,
+ tile_wid, tile_hgt,
+ td->tile_wid, td->tile_hgt);
+
+ /* Oops */
+ if (scaled_tiles == NULL)
+ {
+ /* Failure */
+ result = FALSE;
+
+ break;
+ }
+
+ /* Store it */
+ td->tiles = scaled_tiles;
+ }
+
+# ifdef USE_TRANSPARENCY
+
+ /* See if we have to (re)allocate a new buffer XXX */
+ if ((td->trans_buf == NULL) ||
+ (td->trans_buf->width != td->tile_wid) ||
+ (td->trans_buf->height != td->tile_hgt))
+ {
+ /* Free old buffer if present */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Forget pointer */
+ td->trans_buf = NULL;
+
+ /* Allocate a new buffer */
+ buffer = gdk_rgb_image_new(td->tile_wid, td->tile_hgt);
+
+ /* Oops */
+ if (buffer == NULL)
+ {
+ /* Failure */
+ result = FALSE;
+
+ break;
+ }
+
+ /* Store it */
+ td->trans_buf = buffer;
+ }
+
+ /*
+ * Giga-Hack - assume top left corner of 0x86/0x80 should be
+ * in the background colour XXX XXX XXX XXX
+ */
+ td->bg_pixel = gdk_rgb_image_get_pixel(
+ raw_tiles,
+ 0,
+ tile_hgt * 6);
+
+# endif /* USE_TRANSPARENCY */
+
+ }
+
+
+ /* Alas, we need to free wasted images */
+ if (result == FALSE) graf_nuke();
+
+ /* We don't need the raw image any longer */
+ gdk_rgb_image_destroy(raw_tiles);
+
+ /* Report success or failure */
+ return (result);
+}
+
+
+/*
+ * React to various changes in graphics mode settings
+ *
+ * It is *not* a requirement for tiles to have same pixel width and height.
+ * The program can work with any conbinations of graf_wid and graf_hgt
+ * (oops, they must be representable by u16b), as long as they are lesser
+ * or equal to 32 if you use smooth rescaling.
+ */
+static void init_graphics(void)
+{
+ cptr tile_name;
+
+ u16b graf_wid = 0, graf_hgt = 0;
+
+
+ /* No graphics requests are made - Can't this be simpler? XXX XXX */
+ if ((graf_mode_request == graf_mode) &&
+ (smooth_rescaling_request == smooth_rescaling) &&
+ !resize_request) return;
+
+ /* Prevent further unsolicited reaction */
+ resize_request = FALSE;
+
+
+ /* Dispose unusable old tiles - awkward... XXX XXX */
+ if ((graf_mode_request == GRAF_MODE_NONE) ||
+ (graf_mode_request != graf_mode) ||
+ (smooth_rescaling_request != smooth_rescaling)) graf_nuke();
+
+
+ /* Setup parameters according to request */
+ switch (graf_mode_request)
+ {
+ /* ASCII - no graphics whatsoever */
+ default:
+ case GRAF_MODE_NONE:
+ {
+ tile_name = NULL;
+ use_graphics = arg_graphics = FALSE;
+
+ break;
+ }
+
+ /*
+ * 8x8 tiles originally collected for the Amiga port
+ * from several contributers by Lars Haugseth, converted
+ * to 256 colours and expanded by the Z devteam
+ *
+ * Use the "old" tile assignments
+ *
+ * Dawnmist is working on it for ToME
+ */
+ case GRAF_MODE_OLD:
+ {
+ tile_name = "8x8";
+ graf_wid = graf_hgt = 8;
+ ANGBAND_GRAF = "old";
+ use_graphics = arg_graphics = TRUE;
+
+ break;
+ }
+
+ /*
+ * Adam Bolt's 16x16 tiles
+ * "new" tile assignments
+ * It is updated for ToME by Andreas Koch
+ */
+ case GRAF_MODE_NEW:
+ {
+ tile_name = "16x16";
+ graf_wid = graf_hgt = 16;
+ ANGBAND_GRAF = "new";
+ use_graphics = arg_graphics = TRUE;
+
+ break;
+ }
+ }
+
+
+ /* load tiles and set them up if tiles are requested */
+ if ((graf_mode_request != GRAF_MODE_NONE) &&
+ !graf_init(tile_name, graf_wid, graf_hgt))
+ {
+ /* Oops */
+ plog("Cannot initialize graphics");
+
+ /* reject requests */
+ graf_mode_request = GRAF_MODE_NONE;
+ smooth_rescaling_request = smooth_rescaling;
+
+ /* reset graphics flags */
+ use_graphics = arg_graphics = FALSE;
+ }
+
+ /* Update current graphics mode */
+ graf_mode = graf_mode_request;
+ smooth_rescaling = smooth_rescaling_request;
+
+ /* Reset visuals */
+#ifndef ANG281_RESET_VISUALS
+ reset_visuals(TRUE);
+#else
+ reset_visuals();
+#endif /* !ANG281_RESET_VISUALS */
+}
+
+#endif /* USE_GRAPHICS */
+
+
+
+
+/**** Term package support routines ****/
+
+
+/*
+ * Free data used by a term
+ */
+static void Term_nuke_gtk(term *t)
+{
+ term_data *td = t->data;
+
+
+ /* Free name */
+ if (td->name) string_free(td->name);
+
+ /* Forget it */
+ td->name = NULL;
+
+ /* Free font */
+ if (td->font) gdk_font_unref(td->font);
+
+ /* Forget it */
+ td->font = NULL;
+
+ /* Free backing store */
+ if (td->backing_store) gdk_pixmap_unref(td->backing_store);
+
+ /* Forget it too */
+ td->backing_store = NULL;
+
+#ifdef USE_GRAPHICS
+
+ /* Free tiles */
+ if (td->tiles) gdk_rgb_image_destroy(td->tiles);
+
+ /* Forget pointer */
+ td->tiles = NULL;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Free transparency buffer */
+ if (td->trans_buf) gdk_rgb_image_destroy(td->trans_buf);
+
+ /* Amnesia */
+ td->trans_buf = NULL;
+
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+}
+
+
+/*
+ * Erase the whole term.
+ */
+static errr Term_clear_gtk(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Clear the area */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->drawing_area->style->black_gc,
+ 1,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, 0, 0, td->cols, td->rows);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters.
+ */
+static errr Term_wipe_gtk(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Fill the area with the background colour */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->drawing_area->style->black_gc,
+ TRUE,
+ x * td->font_wid,
+ y * td->font_hgt,
+ n * td->font_wid,
+ td->font_hgt);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some textual characters.
+ */
+static errr Term_text_gtk(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data*)(Term->data);
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(td, a);
+
+ /* Clear the line */
+ Term_wipe_gtk(x, y, n);
+
+ /* Draw the text to the window */
+ gdk_draw_text(
+ TERM_DATA_DRAWABLE(td),
+ td->font,
+ td->gc,
+ x * td->font_wid,
+ td->font->ascent + y * td->font_hgt,
+ s,
+ n);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw software cursor at (x, y)
+ */
+static errr Term_curs_gtk(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+ int cells = 1;
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(td, TERM_YELLOW);
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Adjust it if wide tiles are requested */
+ if (use_bigtile &&
+ (x + 1 < Term->wid) &&
+ (Term->old->a[y][x + 1] == 255))
+ {
+ cells = 2;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ /* Draw the software cursor */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(td),
+ td->gc,
+ FALSE,
+ x * td->font_wid,
+ y * td->font_hgt,
+ td->font_wid * cells - 1,
+ td->font_hgt - 1);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, cells, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+# ifdef USE_TRANSPARENCY
+
+/*
+ * XXX XXX Low level graphics helper
+ * Draw a tile at (s_x, s_y) over one at (t_x, t_y) and store the
+ * result in td->trans_buf
+ *
+ * XXX XXX Even if CPU's are faster than necessary these days,
+ * this should be made inline. Or better, there should be an API
+ * to take advantage of graphics hardware. They almost always have
+ * assortment of builtin bitblt's...
+ */
+static void overlay_tiles_2(
+ term_data *td,
+ int s_x, int s_y,
+ int t_x, int t_y)
+{
+ guint32 pix;
+ int x, y;
+
+
+ /* Process each row */
+ for (y = 0; y < td->tile_hgt; y++)
+ {
+ /* Process each column */
+ for (x = 0; x < td->tile_wid; x++)
+ {
+ /* Get an overlay pixel */
+ pix = gdk_rgb_image_get_pixel(td->tiles, s_x + x, s_y + y);
+
+ /* If it's in background color, use terrain instead */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, t_x + x, t_y + y);
+
+ /* Store the result in trans_buf */
+ gdk_rgb_image_put_pixel(td->trans_buf, x, y, pix);
+ }
+ }
+}
+
+
+# ifdef USE_EGO_GRAPHICS
+
+/*
+ * XXX XXX Low level graphics helper
+ * Draw a tile at (e_x, e_y) over one at (s_x, s_y) over another one
+ * at (t_x, t_y) and store the result in td->trans_buf
+ *
+ * XXX XXX The same comment applies as that for the above...
+ */
+static void overlay_tiles_3(
+ term_data *td,
+ int e_x, int e_y,
+ int s_x, int s_y,
+ int t_x, int t_y)
+{
+ guint32 pix;
+ int x, y;
+
+
+ /* Process each row */
+ for (y = 0; y < td->tile_hgt; y++)
+ {
+ /* Process each column */
+ for (x = 0; x < td->tile_wid; x++)
+ {
+ /* Get an overlay pixel */
+ pix = gdk_rgb_image_get_pixel(td->tiles, e_x + x, e_y + y);
+
+ /*
+ * If it's background colour, try to use one from
+ * the second layer
+ */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, s_x + x, s_y + y);
+
+ /*
+ * If it's background colour again, fall back to
+ * the terrain layer
+ */
+ if (pix == td->bg_pixel)
+ pix = gdk_rgb_image_get_pixel(td->tiles, t_x + x, t_y + y);
+
+ /* Store the pixel in trans_buf */
+ gdk_rgb_image_put_pixel(td->trans_buf, x, y, pix);
+ }
+ }
+}
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Draw "n" tiles/characters starting at (x,y)
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp,
+ const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_gtk(
+ int x, int y, int n,
+ const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+ int d_x, d_y;
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Hack - remember real number of columns affected XXX XXX XXX */
+ int cols;
+
+# endif /* USE_DOUBLE_TILES */
+
+
+ /* Don't draw to hidden windows */
+ if (!td->shown) return (0);
+
+ /* Paranoia */
+ g_assert(td->drawing_area->window != 0);
+
+ /* Top left corner of the destination rect */
+ d_x = x * td->font_wid;
+ d_y = y * td->font_hgt;
+
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Reset column counter */
+ cols = 0;
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Scan the input */
+ for (i = 0; i < n; i++)
+ {
+ byte a;
+ char c;
+ int s_x, s_y;
+
+# ifdef USE_TRANSPARENCY
+
+ byte ta;
+ char tc;
+ int t_x, t_y;
+
+# ifdef USE_EGO_GRAPHICS
+
+ byte ea;
+ char ec;
+ int e_x = 0, e_y = 0;
+ bool has_overlay;
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+
+ /* Grid attr/char */
+ a = *ap++;
+ c = *cp++;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Terrain attr/char */
+ ta = *tap++;
+ tc = *tcp++;
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Overlay attr/char */
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+# endif /* USE_EGO_GRAPHICS */
+
+# endif /* USE_TRANSPARENCY */
+
+ /* Row and Col */
+ s_y = (((byte)a & 0x7F) % tile_rows) * td->tile_hgt;
+ s_x = (((byte)c & 0x7F) % tile_cols) * td->tile_wid;
+
+# ifdef USE_TRANSPARENCY
+
+ /* Terrain Row and Col */
+ t_y = (((byte)ta & 0x7F) % tile_rows) * td->tile_hgt;
+ t_x = (((byte)tc & 0x7F) % tile_cols) * td->tile_wid;
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Overlay Row and Col */
+ if (has_overlay)
+ {
+ e_y = (((byte)ea & 0x7F) % tile_rows) * td->tile_hgt;
+ e_x = (((byte)ec & 0x7F) % tile_cols) * td->tile_wid;
+ }
+
+# endif /* USE_EGO_GRAPHICS */
+
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Mogami's bigtile patch */
+
+ /* Hack -- a filler for wide tile */
+ if (use_bigtile && (a == 255))
+ {
+ /* Advance */
+ d_x += td->font_wid;
+
+ /* Ignore */
+ continue;
+ }
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Optimise the common case: terrain == obj/mons */
+ if (!use_transparency ||
+ ((s_x == t_x) && (s_y == t_y)))
+ {
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* The simplest possible case - no overlay */
+ if (!has_overlay)
+ {
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+ }
+
+ /* We have to draw overlay... */
+ else
+ {
+ /* Overlay */
+ overlay_tiles_2(td, e_x, e_y, s_x, s_y);
+
+ /* And draw the result */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->trans_buf,
+ 0, 0,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+ /* Hack -- Prevent potential display problem */
+ gdk_flush();
+ }
+
+# else /* USE_EGO_GRAPHICS */
+
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+# endif /* USE_EGO_GRAPHICS */
+
+ }
+
+ /*
+ * Since there's no masking bitblt in X,
+ * we have to do that manually...
+ */
+ else
+ {
+
+# ifndef USE_EGO_GRAPHICS
+
+ /* Draw mon/PC/obj over terrain */
+ overlay_tiles_2(td, s_x, s_y, t_x, t_y);
+
+# else /* !USE_EGO_GRAPHICS */
+
+ /* No overlay */
+ if (!has_overlay)
+ {
+ /* Build terrain + masked overlay image */
+ overlay_tiles_2(td, s_x, s_y, t_x, t_y);
+ }
+
+ /* With overlay */
+ else
+ {
+ /* Ego over mon/PC over terrain */
+ overlay_tiles_3(td, e_x, e_y, s_x, s_y,
+ t_x, t_y);
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+ /* Draw it */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->trans_buf,
+ 0, 0,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+ /* Hack -- Prevent potential display problem */
+ gdk_flush();
+ }
+
+# else /* USE_TRANSPARENCY */
+
+ /* Draw the tile */
+ gdk_draw_rgb_image_2(
+ TERM_DATA_DRAWABLE(td), td->gc, td->tiles,
+ s_x, s_y,
+ d_x, d_y,
+ td->tile_wid, td->tile_hgt);
+
+# endif /* USE_TRANSPARENCY */
+
+ /*
+ * Advance x-coordinate - wide font fillers are taken care of
+ * before entering the tile drawing code.
+ */
+ d_x += td->font_wid;
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Add up *real* number of columns updated XXX XXX XXX */
+ cols += use_bigtile ? 2 : 1;
+
+# endif /* USE_DOUBLE_TILES */
+ }
+
+# ifndef USE_DOUBLE_TILES
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, n, 1);
+
+# else
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(td, x, y, cols, 1);
+
+# endif /* USE_DOUBLE_TILES */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Process an event, if there's none block when wait is set true,
+ * return immediately otherwise.
+ */
+static void CheckEvent(bool wait)
+{
+ /* Process an event */
+ (void)gtk_main_iteration_do(wait);
+}
+
+
+/*
+ * Process all pending events (without blocking)
+ */
+static void DrainEvents(void)
+{
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_gtk(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Beep */
+ gdk_beep();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush the output */
+ case TERM_XTRA_FRESH:
+ {
+ /* Flush pending X requests - almost always no-op */
+ gdk_flush();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ /* Process a pending event if there's one */
+ CheckEvent(FALSE);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process Events */
+ case TERM_XTRA_EVENT:
+ {
+ /* Process an event */
+ CheckEvent(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush the events */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Process all pending events */
+ DrainEvents();
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL:
+ return (0);
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ return (Term_clear_gtk());
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ /* sleep for v milliseconds */
+ usleep(v * 1000);
+
+ /* Done */
+ return (0);
+ }
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory) return (1);
+
+ while ((entry = readdir(directory)) != NULL)
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if ((stat(file, &filedata) == 0) && S_ISDIR(filedata.st_mode))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] =
+ string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN: gtk_window_set_title(GTK_WINDOW(data[0].window), angband_term_name[0]); return (0);
+
+ /* React to changes */
+ case TERM_XTRA_REACT:
+ {
+ /* (re-)init colours */
+ init_colours();
+
+#ifdef USE_GRAPHICS
+
+ /* Initialise graphics */
+ init_graphics();
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return (0);
+ }
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+
+
+/**** Event handlers ****/
+
+
+/*
+ * Operation overkill
+ * Verify term size info - just because the other windowing ports have this
+ */
+static void term_data_check_size(term_data *td)
+{
+ /* Enforce minimum window size */
+ if (td == &data[0])
+ {
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+ }
+ else
+ {
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Paranoia - Enforce maximum size allowed by the term package */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+}
+
+
+/*
+ * Enforce these size constraints within Gtk/Gdk
+ * These increments are nice, because you can see numbers of rows/cols
+ * while you resize a term.
+ */
+static void term_data_set_geometry_hints(term_data *td)
+{
+ GdkGeometry geometry;
+
+ /* Resizing is character size oriented */
+ geometry.width_inc = td->font_wid;
+ geometry.height_inc = td->font_hgt;
+
+ /* Enforce minimum size - the main window */
+ if (td == &data[0])
+ {
+ geometry.min_width = 80 * td->font_wid;
+ geometry.min_height = 24 * td->font_hgt;
+ }
+
+ /* Subwindows can be much smaller */
+ else
+ {
+ geometry.min_width = 1 * td->font_wid;
+ geometry.min_height = 1 * td->font_hgt;
+ }
+
+ /* Enforce term package's hard limit */
+ geometry.max_width = 255 * td->font_wid;
+ geometry.max_height = 255 * td->font_hgt;
+
+ /* This affects geometry display while we resize a term */
+ geometry.base_width = 0;
+ geometry.base_height = 0;
+
+ /* Give the window a new set of resizing hints */
+ gtk_window_set_geometry_hints(GTK_WINDOW(td->window),
+ td->drawing_area, &geometry,
+ GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE
+ | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC);
+}
+
+
+/*
+ * (Re)allocate a backing store for the window
+ */
+static void term_data_set_backing_store(term_data *td)
+{
+ /* Paranoia */
+ if (!GTK_WIDGET_REALIZED(td->drawing_area)) return;
+
+ /* Free old one if we cannot use it any longer */
+ if (td->backing_store)
+ {
+ int wid, hgt;
+
+ /* Retrive the size of the old backing store */
+ gdk_window_get_size(td->backing_store, &wid, &hgt);
+
+ /* Continue using it if it's the same with desired size */
+ if (use_backing_store &&
+ (td->cols * td->font_wid == wid) &&
+ (td->rows * td->font_hgt == hgt)) return;
+
+ /* Free it */
+ gdk_pixmap_unref(td->backing_store);
+
+ /* Forget the pointer */
+ td->backing_store = NULL;
+ }
+
+ /* See user preference */
+ if (use_backing_store)
+ {
+ /* Allocate new backing store */
+ td->backing_store = gdk_pixmap_new(
+ td->drawing_area->window,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt,
+ -1);
+
+ /* Oops - but we can do without it */
+ g_return_if_fail(td->backing_store != NULL);
+
+ /* Clear the backing store */
+ gdk_draw_rectangle(
+ td->backing_store,
+ td->drawing_area->style->black_gc,
+ TRUE,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+ }
+}
+
+
+/*
+ * Save game only when it's safe to do so
+ */
+static void save_game_gtk(void)
+{
+ /* We have nothing to save, yet */
+ if (!game_in_progress || !character_generated) return;
+
+ /* It isn't safe to save game now */
+ if (!inkey_flag || !can_save)
+ {
+ plog("You may not save right now.");
+ return;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifdef ZANG_SAVE_GAME
+ /* Also for OAngband - the parameter tells if it's autosave */
+ do_cmd_save_game(FALSE);
+#else
+/* Everything else */
+ do_cmd_save_game();
+#endif /* ZANG_SAVE_GAME */
+}
+
+
+/*
+ * Display message in a modal dialog
+ */
+static void gtk_message(cptr msg)
+{
+ GtkWidget *dialog, *label, *ok_button;
+
+ /* Create the widgets */
+ dialog = gtk_dialog_new();
+ g_assert(dialog != NULL);
+
+ label = gtk_label_new(msg);
+ g_assert(label != NULL);
+
+ ok_button = gtk_button_new_with_label("OK");
+ g_assert(ok_button != NULL);
+
+ /* Ensure that the dialogue box is destroyed when OK is clicked */
+ gtk_signal_connect_object(
+ GTK_OBJECT(ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)dialog);
+ gtk_container_add(
+ GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ ok_button);
+
+ /* Add the label, and show the dialog */
+ gtk_container_add(
+ GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
+ label);
+
+ /* And make it modal */
+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+
+ /* Show the dialog */
+ gtk_widget_show_all(dialog);
+}
+
+
+/*
+ * Hook to tell the user something important
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning message */
+ gtk_message(str);
+}
+
+
+/*
+ * Process File-Quit menu command
+ */
+static void quit_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Save current game */
+ save_game_gtk();
+
+ /* It's done */
+ quit(NULL);
+}
+
+
+/*
+ * Process File-Save menu command
+ */
+static void save_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Save current game */
+ save_game_gtk();
+}
+
+
+/*
+ * Handle destruction of the Angband window
+ */
+static void destroy_main_event_handler(
+ GtkButton *was_clicked,
+ gpointer user_data)
+{
+ /* This allows for cheating, but... */
+ quit(NULL);
+}
+
+
+/*
+ * Handle destruction of Subwindows
+ */
+static void destroy_sub_event_handler(
+ GtkWidget *window,
+ gpointer user_data)
+{
+ /* Hide the window */
+ gtk_widget_hide_all(window);
+}
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Process File-New menu command
+ */
+static void new_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ if (game_in_progress)
+ {
+ plog("You can't start a new game while you're still playing!");
+ return;
+ }
+
+ /* The game is in progress */
+ game_in_progress = TRUE;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play game */
+ play_game(TRUE);
+
+ /* Houseclearing */
+ cleanup_angband();
+
+ /* Done */
+ quit(NULL);
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * Load fond specified by an XLFD fontname and
+ * set up related term_data members
+ */
+static void load_font(term_data *td, cptr fontname)
+{
+ GdkFont *old = td->font;
+
+ /* Load font */
+ td->font = gdk_font_load(fontname);
+
+ if (td->font)
+ {
+ /* Free the old font */
+ if (old) gdk_font_unref(old);
+ }
+ else
+ {
+ /* Oops, but we can still use the old one */
+ td->font = old;
+ }
+
+ /* Calculate the size of the font XXX */
+ td->font_wid = gdk_char_width(td->font, '@');
+ td->font_hgt = td->font->ascent + td->font->descent;
+
+#ifndef USE_DOUBLE_TILES
+
+ /* Use the current font size for tiles as well */
+ td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+#else /* !USE_DOUBLE_TILES */
+
+ /* Calculate the size of tiles */
+ if (use_bigtile && (td == &data[0])) td->tile_wid = td->font_wid * 2;
+ else td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+#endif /* !USE_DOUBLE_TILES */
+}
+
+
+/*
+ * React to OK button press in font selection dialogue
+ */
+static void font_ok_callback(
+ GtkWidget *widget,
+ GtkWidget *font_selector)
+{
+ gchar *fontname;
+ term_data *td;
+
+ td = gtk_object_get_data(GTK_OBJECT(font_selector), "term_data");
+
+ g_assert(td != NULL);
+
+ /* Retrieve font name from player's selection */
+ fontname = gtk_font_selection_dialog_get_font_name(
+ GTK_FONT_SELECTION_DIALOG(font_selector));
+
+ /* Leave unless selection was valid */
+ if (fontname == NULL) return;
+
+ /* Load font and update font size info */
+ load_font(td, fontname);
+
+ /* Hack - Hide the window - finally found the trick... */
+ gtk_widget_hide_all(td->window);
+
+ /* Resizes the drawing area */
+ gtk_drawing_area_size(
+ GTK_DRAWING_AREA(td->drawing_area),
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Update the geometry hints for the window */
+ term_data_set_geometry_hints(td);
+
+ /* Reallocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Hack - Show the window */
+ gtk_widget_show_all(td->window);
+
+#ifdef USE_GRAPHICS
+
+ /* We have to resize tiles when we are in graphics mode */
+ resize_request = TRUE;
+
+#endif /* USE_GRAPHICS */
+
+ /* Hack - force redraw */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Process Options-Font-* menu command
+ */
+static void change_font_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *widget)
+{
+ GtkWidget *font_selector;
+
+ font_selector = gtk_font_selection_dialog_new("Select font");
+#if 0 // DGDGDGDG
+ gtk_object_set_data(
+ GTK_OBJECT(font_selector),
+ "term_data",
+ user_data);
+
+ /* Filter to show only fixed-width fonts */
+ gtk_font_selection_dialog_set_filter(
+ GTK_FONT_SELECTION_DIALOG(font_selector),
+ GTK_FONT_FILTER_BASE,
+ GTK_FONT_ALL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ spacings,
+ NULL);
+
+ gtk_signal_connect(
+ GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->ok_button),
+ "clicked",
+ font_ok_callback,
+ (gpointer)font_selector);
+
+ /*
+ * Ensure that the dialog box is destroyed when the user clicks
+ * a button.
+ */
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)font_selector);
+
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(font_selector)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)font_selector);
+
+ gtk_widget_show(GTK_WIDGET(font_selector));
+#endif
+}
+
+
+/*
+ * Process Terms-* menu command - hide/show terminal window
+ */
+static void term_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *widget)
+{
+ term_data *td = &data[user_action];
+
+ /* We don't mess with the Angband window */
+ if (td == &data[0]) return;
+
+ /* It's shown */
+ if (td->shown)
+ {
+ /* Hide the window */
+ gtk_widget_hide_all(td->window);
+ }
+
+ /* It's hidden */
+ else
+ {
+ /* Show the window */
+ gtk_widget_show_all(td->window);
+ }
+}
+
+
+/*
+ * Toggles the boolean value of use_backing_store and
+ * setup / remove backing store for each term
+ */
+static void change_backing_store_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ int i;
+
+ /* Toggle the backing store mode */
+ use_backing_store = !use_backing_store;
+
+ /* Reset terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data_set_backing_store(&data[i]);
+ }
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Set graf_mode_request according to user selection,
+ * and let Term_xtra react to the change.
+ */
+static void change_graf_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Set request according to user selection */
+ graf_mode_request = (int)user_action;
+
+ /*
+ * Hack - force redraw
+ * This induces a call to Term_xtra(TERM_XTRA_REACT, 0) as well
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Set dither_mode according to user selection
+ */
+static void change_dith_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* Set request according to user selection */
+ dith_mode = (int)user_action;
+
+ /*
+ * Hack - force redraw
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+/*
+ * Toggles the graphics tile scaling mode (Fast/Smooth)
+ */
+static void change_smooth_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ /* (Try to) toggle the smooth rescaling mode */
+ smooth_rescaling_request = !smooth_rescaling;
+
+ /*
+ * Hack - force redraw
+ * This induces a call to Term_xtra(TERM_XTRA_REACT, 0) as well
+ */
+ Term_key_push(KTRL('R'));
+}
+
+
+# ifdef USE_DOUBLE_TILES
+
+static void change_wide_tile_mode_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ term *old = Term;
+ term_data *td = &data[0];
+
+ /* Toggle "use_bigtile" */
+ use_bigtile = !use_bigtile;
+
+#ifdef TOME
+ /* T.o.M.E. requires this as well */
+ arg_bigtile = use_bigtile;
+#endif /* TOME */
+
+ /* Double the width of tiles (only for the main window) */
+ if (use_bigtile)
+ {
+ td->tile_wid = td->font_wid * 2;
+ }
+
+ /* Use the width of current font */
+ else
+ {
+ td->tile_wid = td->font_wid;
+ }
+
+ /* Need to resize the tiles */
+ resize_request = TRUE;
+
+ /* Activate the main window */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Activate the old term */
+ Term_activate(old);
+
+ /* Hack - force redraw XXX ??? XXX */
+ Term_key_push(KTRL('R'));
+}
+
+# endif /* USE_DOUBLE_TILES */
+
+
+# ifdef USE_TRANSPARENCY
+
+/*
+ * Toggles the boolean value of use_transparency
+ */
+static void change_trans_mode_event_handler(
+ gpointer user_data,
+ guint user_aciton,
+ GtkWidget *was_clicked)
+{
+ /* Toggle the transparency mode */
+ use_transparency = !use_transparency;
+
+ /* Hack - force redraw */
+ Term_key_push(KTRL('R'));
+}
+
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Caution: Modal or not, callbacks are called by gtk_main(),
+ * so this is the right place to start a game.
+ */
+static void file_ok_callback(
+ GtkWidget *widget,
+ GtkWidget *file_selector)
+{
+ strcpy(savefile,
+ gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector)));
+
+ gtk_widget_destroy(file_selector);
+
+ /* game is in progress */
+ game_in_progress = TRUE;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play game */
+ play_game(FALSE);
+
+ /* Free memory allocated by game */
+ cleanup_angband();
+
+ /* Done */
+ quit(NULL);
+}
+
+
+/*
+ * Process File-Open menu command
+ */
+static void open_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ GtkWidget *file_selector;
+ char buf[1024];
+
+
+ if (game_in_progress)
+ {
+ plog("You can't open a new game while you're still playing!");
+ return;
+ }
+
+ /* Prepare the savefile path */
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, "*");
+
+ file_selector = gtk_file_selection_new("Select a savefile");
+ gtk_file_selection_set_filename(
+ GTK_FILE_SELECTION(file_selector),
+ buf);
+ gtk_signal_connect(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked",
+ file_ok_callback,
+ (gpointer)file_selector);
+
+ /*
+ * Ensure that the dialog box is destroyed when the user
+ * clicks a button.
+ */
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)file_selector);
+
+ gtk_signal_connect_object(
+ GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer)file_selector);
+
+ gtk_window_set_modal(GTK_WINDOW(file_selector), TRUE);
+ gtk_widget_show(GTK_WIDGET(file_selector));
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * React to "delete" signal sent to Window widgets
+ */
+static gboolean delete_event_handler(
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ /* Save game if possible */
+ save_game_gtk();
+
+ /* Don't prevent closure */
+ return (FALSE);
+}
+
+
+/*
+ * Convert keypress events to ASCII codes and enqueue them
+ * for game
+ */
+static gboolean keypress_event_handler(
+ GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+#if 1
+ int i, mc, ms, mo, mx;
+
+ char msg[128];
+
+ /* Hack - do not do anything until the player picks from the menu */
+ if (!game_in_progress) return (TRUE);
+
+ /* Hack - Ignore parameters */
+ (void) widget;
+ (void) user_data;
+
+ /* Extract four "modifier flags" */
+ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+ ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+ mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE;
+ mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE;
+
+ /*
+ * Hack XXX
+ * Parse shifted numeric (keypad) keys specially.
+ */
+ if ((event->state == GDK_SHIFT_MASK)
+ && (event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9))
+ {
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+ }
+
+ /* Normal keys with no modifiers */
+ if (event->length && !mo && !mx)
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]);
+
+ /* All done */
+ return (TRUE);
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch ((uint) event->keyval)
+ {
+ case GDK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return (TRUE);
+ }
+
+ case GDK_Return:
+ {
+ Term_keypress('\r');
+ return (TRUE);
+ }
+
+ case GDK_Tab:
+ {
+ Term_keypress('\t');
+ return (TRUE);
+ }
+
+ case GDK_Delete:
+ case GDK_BackSpace:
+ {
+ Term_keypress('\010');
+ return (TRUE);
+ }
+
+ /* Hack - the cursor keys */
+ case GDK_Up:
+ {
+ Term_keypress('8');
+ return (TRUE);
+ }
+
+ case GDK_Down:
+ {
+ Term_keypress('2');
+ return (TRUE);
+ }
+
+ case GDK_Left:
+ {
+ Term_keypress('4');
+ return (TRUE);
+ }
+
+ case GDK_Right:
+ {
+ Term_keypress('6');
+ return (TRUE);
+ }
+
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R:
+ {
+ /* Hack - do nothing to control characters */
+ return (TRUE);
+ }
+ }
+
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+
+#else
+ int i, mc, ms, mo, mx;
+
+ char msg[128];
+
+
+ /* Extract four "modifier flags" */
+ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+ ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+ mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE;
+ mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE;
+ printf("0=%d 9=%d;; keyval=%d; mc=%d, ms=%d ::=:: ", GDK_KP_0, GDK_KP_9, event->keyval, mc, ms);
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) printf("%d;", event->string[i]);
+ printf("\n");
+
+ /*
+ * Hack XXX
+ * Parse shifted numeric (keypad) keys specially.
+ */
+ if ((event->state & GDK_SHIFT_MASK)
+ && (event->keyval >= GDK_KP_Left) && (event->keyval <= GDK_KP_Delete))
+ {
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13);
+ printf("%cS_%X%c", 31, event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+ }
+
+ /* Normal keys with no modifiers */
+ if (event->length && !mo && !mx)
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]);
+
+ /* All done */
+ return (TRUE);
+ }
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch ((uint) event->keyval)
+ {
+ case GDK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return (TRUE);
+ }
+
+ case GDK_Return:
+ {
+ Term_keypress('\r');
+ return (TRUE);
+ }
+
+ case GDK_Tab:
+ {
+ Term_keypress('\t');
+ return (TRUE);
+ }
+
+ case GDK_Delete:
+ case GDK_BackSpace:
+ {
+ Term_keypress('\010');
+ return (TRUE);
+ }
+
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R:
+ {
+ /* Hack - do nothing to control characters */
+ return (TRUE);
+ }
+ }
+
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+#endif
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "realize" signal
+ *
+ * In this program, called when window containing the drawing
+ * area is shown first time.
+ */
+static void realize_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Create graphic context */
+ td->gc = gdk_gc_new(td->drawing_area->window);
+
+ /* Set foreground and background colours - isn't bg used at all? */
+ gdk_rgb_gc_set_background(td->gc, 0x000000);
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[TERM_WHITE]);
+
+ /* No last foreground colour, yet */
+ td->last_attr = -1;
+
+ /* Allocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Clear the window */
+ gdk_draw_rectangle(
+ widget->window,
+ widget->style->black_gc,
+ TRUE,
+ 0,
+ 0,
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "show" signal
+ */
+static void show_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Set the shown flag */
+ td->shown = TRUE;
+}
+
+
+/*
+ * Widget customisation (for drawing area) - "hide" signal
+ */
+static void hide_event_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ term_data *td = (term_data *)user_data;
+
+ /* Set the shown flag */
+ td->shown = FALSE;
+}
+
+
+/*
+ * Widget customisation (for drawing area)- handle size allocation requests
+ */
+static void size_allocate_event_handler(
+ GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer user_data)
+{
+ term_data *td = user_data;
+ int old_rows, old_cols;
+ term *old = Term;
+
+ /* Paranoia */
+ g_return_if_fail(widget != NULL);
+ g_return_if_fail(allocation != NULL);
+ g_return_if_fail(td != NULL);
+
+ /* Remember old values */
+ old_cols = td->cols;
+ old_rows = td->rows;
+
+ /* Update numbers of rows and columns */
+ td->cols = (allocation->width + td->font_wid - 1) / td->font_wid;
+ td->rows = (allocation->height + td->font_hgt - 1) / td->font_hgt;
+
+ /* Overkill - Validate them */
+ term_data_check_size(td);
+
+ /* Adjust size request and set it */
+ allocation->width = td->cols * td->font_wid;
+ allocation->height = td->rows * td->font_hgt;
+ widget->allocation = *allocation;
+
+ /* Widget is realized, so we do some drawing works */
+ if (GTK_WIDGET_REALIZED(widget))
+ {
+ /* Reallocate the backing store */
+ term_data_set_backing_store(td);
+
+ /* Actually handles resizing in Gtk */
+ gdk_window_move_resize(
+ widget->window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+
+ /* And in the term package */
+ Term_activate(&td->t);
+
+ /* Resize if necessary */
+ if ((td->cols != old_cols) || (td->rows != old_rows))
+ (void)Term_resize(td->cols, td->rows);
+
+ /* Redraw its content */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Update exposed area in a window (for drawing area)
+ */
+static gboolean expose_event_handler(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer user_data)
+{
+ term_data *td = user_data;
+
+ term *old = Term;
+
+#ifndef NO_REDRAW_SECTION
+
+ int x1, x2, y1, y2;
+
+#endif /* !NO_REDRAW_SECTION */
+
+
+ /* Paranoia */
+ if (td == NULL) return (TRUE);
+
+ /* The window has a backing store */
+ if (td->backing_store)
+ {
+ /* Simply restore the exposed area from the backing store */
+ gdk_draw_pixmap(
+ td->drawing_area->window,
+ td->gc,
+ td->backing_store,
+ event->area.x,
+ event->area.y,
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height);
+ }
+
+ /* No backing store - use the game's code to redraw the area */
+ else
+ {
+
+ /* Activate the relevant term */
+ Term_activate(&td->t);
+
+# ifdef NO_REDRAW_SECTION
+
+ /* K.I.S.S. version */
+
+ /* Redraw */
+ Term_redraw();
+
+# else /* NO_REDRAW_SECTION */
+
+ /*
+ * Complex version - The above is enough, but since we have
+ * Term_redraw_section... This might help if we had a graphics
+ * mode.
+ */
+
+ /* Convert coordinate in pixels to character cells */
+ x1 = event->area.x / td->font_wid;
+ x2 = (event->area.x + event->area.width) / td->font_wid;
+ y1 = event->area.y / td->font_hgt;
+ y2 = (event->area.y + event->area.height) / td->font_hgt;
+
+ /*
+ * No paranoia - boundary checking is done in
+ * Term_redraw_section
+ */
+
+ /* Redraw the area */
+ Term_redraw_section(x1, y1, x2, y2);
+
+# endif /* NO_REDRAW_SECTION */
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+
+ /* We've processed the event ourselves */
+ return (TRUE);
+}
+
+
+
+
+/**** Initialisation ****/
+
+/*
+ * Initialise a term_data struct
+ */
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &td->t;
+ char *p;
+
+ td->cols = 80;
+ td->rows = 24;
+
+ /* Initialize the term */
+ term_init(t, td->cols, td->rows, 1024);
+
+ /* Store the name of the term */
+ td->name = string_make(angband_term_name[i]);
+
+ /* Instance names should start with a lowercase letter XXX */
+ for (p = (char *)td->name; *p; p++) *p = tolower(*p);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ t->xtra_hook = Term_xtra_gtk;
+ t->text_hook = Term_text_gtk;
+ t->wipe_hook = Term_wipe_gtk;
+ t->curs_hook = Term_curs_gtk;
+#ifdef USE_GRAPHICS
+ t->pict_hook = Term_pict_gtk;
+#endif /* USE_GRAPHICS */
+ t->nuke_hook = Term_nuke_gtk;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Neater menu code with GtkItemFactory.
+ *
+ * Menu bar of the Angband window
+ *
+ * Entry format: Path, Accelerator, Callback, Callback arg, type
+ * where type is one of:
+ * <Item> - simple item, alias NULL
+ * <Branch> - has submenu
+ * <Separator> - as you read it
+ * <CheckItem> - has a check mark
+ * <ToggleItem> - is a toggle
+ */
+static GtkItemFactoryEntry main_menu_items[] =
+{
+ /* "File" menu */
+ { "/File", NULL,
+ NULL, 0, "<Branch>", NULL
+ },
+#ifndef SAVEFILE_SCREEN
+ { "/File/New", "<mod1>N",
+ new_event_handler, 0, NULL, NULL },
+ { "/File/Open", "<mod1>O",
+ open_event_handler, 0, NULL, NULL },
+ { "/File/sep1", NULL,
+ NULL, 0, "<Separator>", NULL },
+#endif /* !SAVEFILE_SCREEN */
+ { "/File/Save", "<mod1>S",
+ save_event_handler, 0, NULL, NULL },
+ { "/File/Quit", "<mod1>Q",
+ quit_event_handler, 0, NULL, NULL },
+
+ /* "Terms" menu */
+ { "/Terms", NULL,
+ NULL, 0, "<Branch>", NULL },
+ /* XXX XXX XXX NULL's are replaced by the program */
+ { NULL, "<mod1>0",
+ term_event_handler, 0, "<CheckItem>", NULL },
+ { NULL, "<mod1>1",
+ term_event_handler, 1, "<CheckItem>", NULL },
+ { NULL, "<mod1>2",
+ term_event_handler, 2, "<CheckItem>", NULL },
+ { NULL, "<mod1>3",
+ term_event_handler, 3, "<CheckItem>", NULL },
+ { NULL, "<mod1>4",
+ term_event_handler, 4, "<CheckItem>", NULL },
+ { NULL, "<mod1>5",
+ term_event_handler, 5, "<CheckItem>", NULL },
+ { NULL, "<mod1>6",
+ term_event_handler, 6, "<CheckItem>", NULL },
+ { NULL, "<mod1>7",
+ term_event_handler, 7, "<CheckItem>", NULL },
+
+ /* "Options" menu */
+ { "/Options", NULL,
+ NULL, 0, "<Branch>", NULL },
+
+ /* "Font" submenu */
+ { "/Options/Font", NULL,
+ NULL, 0, "<Branch>", NULL },
+ /* XXX XXX XXX Again, NULL's are filled by the program */
+ { NULL, NULL,
+ change_font_event_handler, 0, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 1, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 2, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 3, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 4, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 5, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 6, NULL, NULL },
+ { NULL, NULL,
+ change_font_event_handler, 7, NULL, NULL },
+
+#ifdef USE_GRAPHICS
+
+ /* "Graphics" submenu */
+ { "/Options/Graphics", NULL,
+ NULL, 0, "<Branch>", NULL },
+ { "/Options/Graphics/None", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_NONE, "<CheckItem>", NULL },
+ { "/Options/Graphics/Old", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_OLD, "<CheckItem>", NULL },
+ { "/Options/Graphics/New", NULL,
+ change_graf_mode_event_handler, GRAF_MODE_NEW, "<CheckItem>", NULL },
+# ifdef USE_DOUBLE_TILES
+ { "/Options/Graphics/sep3", NULL,
+ NULL, 0, "<Separator>", NULL },
+ { "/Options/Graphics/Wide tiles", NULL,
+ change_wide_tile_mode_event_handler, 0, "<CheckItem>", NULL },
+# endif /* USE_DOUBLE_TILES */
+ { "/Options/Graphics/sep1", NULL,
+ NULL, 0, "<Separator>", NULL },
+ { "/Options/Graphics/Dither if <= 8bpp", NULL,
+ change_dith_mode_event_handler, GDK_RGB_DITHER_NORMAL, "<CheckItem>", NULL },
+ { "/Options/Graphics/Dither if <= 16bpp", NULL,
+ change_dith_mode_event_handler, GDK_RGB_DITHER_MAX, "<CheckItem>", NULL },
+ { "/Options/Graphics/sep2", NULL,
+ NULL, 0, "<Separator>", NULL },
+ { "/Options/Graphics/Smoothing", NULL,
+ change_smooth_mode_event_handler, 0, "<CheckItem>", NULL },
+# ifdef USE_TRANSPARENCY
+ { "/Options/Graphics/Transparency", NULL,
+ change_trans_mode_event_handler, 0, "<CheckItem>", NULL },
+# endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+ /* "Misc" submenu */
+ { "/Options/Misc", NULL,
+ NULL, 0, "<Branch>", NULL },
+ { "/Options/Misc/Backing store", NULL,
+ change_backing_store_event_handler, 0, "<CheckItem>", NULL },
+};
+
+
+/*
+ * XXX XXX Fill those NULL's in the menu definition with
+ * angband_term_name[] strings
+ */
+static void setup_menu_paths(void)
+{
+ int i;
+ int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+ GtkItemFactoryEntry *term_entry, *font_entry;
+ char buf[64];
+
+ /* Find the "Terms" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Terms")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ term_entry = &main_menu_items[i + 1];
+
+ /* Find "Font" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Options/Font")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ font_entry = &main_menu_items[i + 1];
+
+ /* For each terminal */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* XXX XXX Build the real path name to the entry */
+ strnfmt(buf, 64, "/Terms/%s", angband_term_name[i]);
+
+ /* XXX XXX Store it in the menu definition */
+ term_entry[i].path = (gchar*)string_make(buf);
+
+ /* XXX XXX Build the real path name to the entry */
+ strnfmt(buf, 64, "/Options/Font/%s", angband_term_name[i]);
+
+ /* XXX XXX Store it in the menu definition */
+ font_entry[i].path = (gchar*)string_make(buf);
+ }
+}
+
+
+/*
+ * XXX XXX Free strings allocated by setup_menu_paths()
+ */
+static void free_menu_paths(void)
+{
+ int i;
+ int nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+ GtkItemFactoryEntry *term_entry, *font_entry;
+
+ /* Find the "Terms" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Terms")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ term_entry = &main_menu_items[i + 1];
+
+ /* Find "Font" menu */
+ for (i = 0; i < nmenu_items; i++)
+ {
+ /* Skip NULLs */
+ if (main_menu_items[i].path == NULL) continue;
+
+ /* Find a match */
+ if (streq(main_menu_items[i].path, "/Options/Font")) break;
+ }
+ g_assert(i < (nmenu_items - MAX_TERM_DATA));
+
+ /* Remember the location */
+ font_entry = &main_menu_items[i + 1];
+
+ /* For each terminal */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* XXX XXX Free Term menu path */
+ if (term_entry[i].path) string_free((cptr)term_entry[i].path);
+
+ /* XXX XXX Free Font menu path */
+ if (font_entry[i].path) string_free((cptr)font_entry[i].path);
+ }
+}
+
+
+/*
+ * Find widget corresponding to path name
+ * return NULL on error
+ */
+static GtkWidget *get_widget_from_path(cptr path)
+{
+ GtkItemFactory *item_factory;
+ GtkWidget *widget;
+
+ /* Paranoia */
+ if (path == NULL) return (NULL);
+
+ /* Look up item factory */
+ item_factory = gtk_item_factory_from_path(path);
+
+ /* Oops */
+ if (item_factory == NULL) return (NULL);
+
+ /* Look up widget */
+ widget = gtk_item_factory_get_widget(item_factory, path);
+
+ /* Return result */
+ return (widget);
+}
+
+
+/*
+ * Enable/disable a menu item
+ */
+void enable_menu_item(cptr path, bool enabled)
+{
+ GtkWidget *widget;
+
+ /* Access menu item widget */
+ widget = get_widget_from_path(path);
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU_ITEM(widget));
+
+ /*
+ * In Gtk's terminology, enabled is sensitive
+ * and disabled insensitive
+ */
+ gtk_widget_set_sensitive(widget, enabled);
+}
+
+
+/*
+ * Check/uncheck a menu item. The item should be of the GtkCheckMenuItem type
+ */
+void check_menu_item(cptr path, bool checked)
+{
+ GtkWidget *widget;
+
+ /* Access menu item widget */
+ widget = get_widget_from_path(path);
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_CHECK_MENU_ITEM(widget));
+
+ /*
+ * Put/remove check mark
+ *
+ * Mega-Hack -- The function supposed to be used here,
+ * gtk_check_menu_item_set_active(), emits an "activate" signal
+ * to the GtkMenuItem class of the widget, as if the menu item
+ * were selected by user, thereby causing bizarre behaviour.
+ * XXX XXX XXX
+ */
+ GTK_CHECK_MENU_ITEM(widget)->active = checked;
+}
+
+
+/*
+ * Update the "File" menu
+ */
+static void file_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+#ifndef SAVEFILE_SCREEN
+ bool game_start_ok;
+#endif /* !SAVEFILE_SCREEN */
+ bool save_ok, quit_ok;
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Can we start a game now? */
+ game_start_ok = !game_in_progress;
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Cave we save/quit now? */
+ if (!character_generated || !game_in_progress)
+ {
+ save_ok = FALSE;
+ quit_ok = TRUE;
+ }
+ else
+ {
+ if (inkey_flag && can_save) save_ok = quit_ok = TRUE;
+ else save_ok = quit_ok = FALSE;
+ }
+
+ /* Enable / disable menu items according to those conditions */
+#ifndef SAVEFILE_SCREEN
+ enable_menu_item("<Angband>/File/New", game_start_ok);
+ enable_menu_item("<Angband>/File/Open", game_start_ok);
+#endif /* !SAVEFILE_SCREEN */
+ enable_menu_item("<Angband>/File/Save", save_ok);
+ enable_menu_item("<Angband>/File/Quit", quit_ok);
+}
+
+
+/*
+ * Update the "Terms" menu
+ */
+static void term_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ int i;
+ char buf[64];
+
+ /* For each term */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Build the path name */
+ strnfmt(buf, 64, "<Angband>/Terms/%s", angband_term_name[i]);
+
+ /* Update the check mark on the item */
+ check_menu_item(buf, data[i].shown);
+ }
+}
+
+
+/*
+ * Update the "Font" submenu
+ */
+static void font_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ int i;
+ char buf[64];
+
+ /* For each term */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Build the path name */
+ strnfmt(buf, 64, "<Angband>/Options/Font/%s", angband_term_name[i]);
+
+ /* Enable selection if the term is shown */
+ enable_menu_item(buf, data[i].shown);
+ }
+}
+
+
+/*
+ * Update the "Misc" submenu
+ */
+static void misc_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Update an item */
+ check_menu_item(
+ "<Angband>/Options/Misc/Backing store",
+ use_backing_store);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Update the "Graphics" submenu
+ */
+static void graf_menu_update_handler(
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Update menu items */
+ check_menu_item(
+ "<Angband>/Options/Graphics/None",
+ (graf_mode == GRAF_MODE_NONE));
+ check_menu_item(
+ "<Angband>/Options/Graphics/Old",
+ (graf_mode == GRAF_MODE_OLD));
+ check_menu_item(
+ "<Angband>/Options/Graphics/New",
+ (graf_mode == GRAF_MODE_NEW));
+
+#ifdef USE_DOUBLE_TILES
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Wide tiles",
+ use_bigtile);
+
+#endif /* USE_DOUBLE_TILES */
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Dither if <= 8bpp",
+ (dith_mode == GDK_RGB_DITHER_NORMAL));
+ check_menu_item(
+ "<Angband>/Options/Graphics/Dither if <= 16bpp",
+ (dith_mode == GDK_RGB_DITHER_MAX));
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Smoothing",
+ smooth_rescaling);
+
+# ifdef USE_TRANSPARENCY
+
+ check_menu_item(
+ "<Angband>/Options/Graphics/Transparency",
+ use_transparency);
+
+# endif /* USE_TRANSPARENCY */
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Construct a menu hierarchy using GtkItemFactory, setting up
+ * callbacks and accelerators along the way, and return
+ * a GtkMenuBar widget.
+ */
+GtkWidget *get_main_menu(term_data *td)
+{
+ GtkItemFactory *item_factory;
+ GtkAccelGroup *accel_group;
+ gint nmenu_items = sizeof(main_menu_items) / sizeof(main_menu_items[0]);
+
+
+ /* XXX XXX Setup path names in the "Terms" and "Font" menus */
+ setup_menu_paths();
+
+ /* Allocate an accelerator group */
+ accel_group = gtk_accel_group_new();
+ g_assert(accel_group != NULL);
+
+ /* Initialise the item factory */
+ item_factory = gtk_item_factory_new(
+ GTK_TYPE_MENU_BAR,
+ "<Angband>",
+ accel_group);
+ g_assert(item_factory != NULL);
+
+ /* Generate the menu items */
+ gtk_item_factory_create_items(
+ item_factory,
+ nmenu_items,
+ main_menu_items,
+ NULL);
+
+ /* Attach the new accelerator group to the window */
+ gtk_window_add_accel_group(
+ GTK_WINDOW(td->window),
+ accel_group);
+
+ /* Return the actual menu bar created */
+ return (gtk_item_factory_get_widget(item_factory, "<Angband>"));
+}
+
+
+/*
+ * Install callbacks to update menus
+ */
+static void add_menu_update_callbacks()
+{
+ GtkWidget *widget;
+
+ /* Access the "File" menu */
+ widget = get_widget_from_path("<Angband>/File");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(file_menu_update_handler),
+ NULL);
+
+ /* Access the "Terms" menu */
+ widget = get_widget_from_path("<Angband>/Terms");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(term_menu_update_handler),
+ NULL);
+
+ /* Access the "Font" menu */
+ widget = get_widget_from_path("<Angband>/Options/Font");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(font_menu_update_handler),
+ NULL);
+
+ /* Access the "Misc" menu */
+ widget = get_widget_from_path("<Angband>/Options/Misc");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(misc_menu_update_handler),
+ NULL);
+
+#ifdef USE_GRAPHICS
+
+ /* Access Graphics menu */
+ widget = get_widget_from_path("<Angband>/Options/Graphics");
+
+ /* Paranoia */
+ g_assert(widget != NULL);
+ g_assert(GTK_IS_MENU(widget));
+
+ /* Assign callback */
+ gtk_signal_connect(
+ GTK_OBJECT(widget),
+ "show",
+ GTK_SIGNAL_FUNC(graf_menu_update_handler),
+ NULL);
+
+#endif /* USE_GRAPHICS */
+}
+
+
+/*
+ * Create Gtk widgets for a terminal window and set up callbacks
+ */
+static void init_gtk_window(term_data *td, int i)
+{
+ GtkWidget *menu_bar = NULL, *box;
+ cptr font;
+
+ bool main_window = (i == 0) ? TRUE : FALSE;
+
+
+ /* Create window */
+ td->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ /* Set title */
+ gtk_window_set_title(GTK_WINDOW(td->window), td->name);
+
+
+ /* Get default font for this term */
+ font = get_default_font(i);
+
+ /* Load font and initialise related term_data fields */
+ load_font(td, font);
+
+
+ /* Create drawing area */
+ td->drawing_area = gtk_drawing_area_new();
+
+ /* Set the size of the drawing area */
+ gtk_drawing_area_size(
+ GTK_DRAWING_AREA(td->drawing_area),
+ td->cols * td->font_wid,
+ td->rows * td->font_hgt);
+
+ /* Set geometry hints */
+ term_data_set_geometry_hints(td);
+
+
+ /* Install window event handlers */
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "delete_event",
+ GTK_SIGNAL_FUNC(delete_event_handler),
+ NULL);
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "key_press_event",
+ GTK_SIGNAL_FUNC(keypress_event_handler),
+ NULL);
+
+ /* Destroying the Angband window terminates the game */
+ if (main_window)
+ {
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "destroy_event",
+ GTK_SIGNAL_FUNC(destroy_main_event_handler),
+ NULL);
+ }
+
+ /* The other windows are just hidden */
+ else
+ {
+ gtk_signal_connect(
+ GTK_OBJECT(td->window),
+ "destroy_event",
+ GTK_SIGNAL_FUNC(destroy_sub_event_handler),
+ td);
+ }
+
+
+ /* Install drawing area event handlers */
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "realize",
+ GTK_SIGNAL_FUNC(realize_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "show",
+ GTK_SIGNAL_FUNC(show_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "hide",
+ GTK_SIGNAL_FUNC(hide_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "size_allocate",
+ GTK_SIGNAL_FUNC(size_allocate_event_handler),
+ (gpointer)td);
+ gtk_signal_connect(
+ GTK_OBJECT(td->drawing_area),
+ "expose_event",
+ GTK_SIGNAL_FUNC(expose_event_handler),
+ (gpointer)td);
+
+
+ /* Create menu */
+ if (main_window)
+ {
+ /* Build the main menu bar */
+ menu_bar = get_main_menu(td);
+ g_assert(menu_bar != NULL);
+
+ /* Since it's tedious to scatter the menu update code around */
+ add_menu_update_callbacks();
+ }
+
+
+ /* Pack the menu bar together with the main window */
+ /* For vertical placement of the menu bar and the drawing area */
+ box = gtk_vbox_new(FALSE, 0);
+
+ /* Let the window widget own it */
+ gtk_container_add(GTK_CONTAINER(td->window), box);
+
+ /* The main window has a menu bar */
+ if (main_window)
+ gtk_box_pack_start(
+ GTK_BOX(box),
+ menu_bar,
+ FALSE,
+ FALSE,
+ NO_PADDING);
+
+ /* And place the drawing area just beneath it */
+ gtk_box_pack_start_defaults(GTK_BOX(box), td->drawing_area);
+
+
+ /* Show the widgets - use of td->shown is a dirty hack XXX XXX */
+ if (td->shown) gtk_widget_show_all(td->window);
+}
+
+
+/*
+ * To be hooked into quit(). See z-util.c
+ */
+static void hook_quit(cptr str)
+{
+ /* Free menu paths dynamically allocated */
+ free_menu_paths();
+
+# ifdef USE_GRAPHICS
+
+ /* Free pathname string */
+ if (ANGBAND_DIR_XTRA_GRAF) string_free(ANGBAND_DIR_XTRA_GRAF);
+
+# endif /* USE_GRAPHICS */
+
+ /* Terminate the program */
+ gtk_exit(0);
+}
+
+
+#ifdef ANGBAND300
+
+/*
+ * Help message for this port
+ */
+const char help_gtk[] =
+ "GTK for X11, subopts -n<windows>\n"
+ " -b(acking store off)\n"
+#ifdef USE_GRAPHICS
+ " -g(raphics) -o(ld graphics) -s(moothscaling off) \n"
+ " -t(ransparency on)\n"
+# ifdef USE_DOUBLE_TILES
+ " -w(ide tiles)\n"
+# endif /* USE_DOUBLE_TILES */
+#endif /* USE_GRAPHICS */
+ " and standard GTK options";
+
+#endif /* ANGBAND300 */
+
+
+/*
+ * Initialization function
+ */
+errr init_gtk2(int argc, char **argv)
+{
+ int i;
+
+
+ /* Initialize the environment */
+ gtk_init(&argc, &argv);
+
+ /* Activate hooks - Use gtk/glib interface throughout */
+ ralloc_aux = hook_ralloc;
+ rnfree_aux = hook_rnfree;
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ /* Number of terminals displayed at start up */
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ /* Disable use of pixmaps as backing store */
+ if (streq(argv[i], "-b"))
+ {
+ use_backing_store = FALSE;
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ /* Requests "old" graphics */
+ if (streq(argv[i], "-o"))
+ {
+ graf_mode_request = GRAF_MODE_OLD;
+ continue;
+ }
+
+ /* Requests "new" graphics */
+ if (streq(argv[i], "-g"))
+ {
+ graf_mode_request = GRAF_MODE_NEW;
+ continue;
+ }
+
+# ifdef USE_DOUBLE_TILES
+
+ /* Requests wide tile mode */
+ if (streq(argv[i], "-w"))
+ {
+ use_bigtile = TRUE;
+# ifdef TOME
+ /* T.o.M.E. uses older version of the patch */
+ arg_bigtile = TRUE;
+# endif /* TOME */
+ continue;
+ }
+
+# endif /* USE_DOUBLE_TILES */
+
+
+ /* Enable transparency effect */
+ if (streq(argv[i], "-t"))
+ {
+ use_transparency = TRUE;
+ continue;
+ }
+
+ /* Disable smooth rescaling of tiles */
+ if (streq(argv[i], "-s"))
+ {
+ smooth_rescaling_request = FALSE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* None of the above */
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+#ifdef USE_GRAPHICS
+
+ {
+ char path[1024];
+
+ /* Build the "graf" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_GRAF = string_make(path);
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Initialise colours */
+ gdk_rgb_init();
+ gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
+ gtk_widget_set_default_visual(gdk_rgb_get_visual());
+ init_colours();
+
+ /*
+ * Initialise the windows backwards, so that
+ * the Angband window comes in front
+ */
+ for (i = MAX_TERM_DATA - 1; i >= 0; i--)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Hack - Set the shown flag, meaning "to be shown" XXX XXX */
+ if (i < num_term) td->shown = TRUE;
+ else td->shown = FALSE;
+
+ /* Save global entry */
+ angband_term[i] = Term;
+
+ /* Init the window */
+ init_gtk_window(td, i);
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Set the system suffix */
+ ANGBAND_SYS = "gtk";
+
+ /* Catch nasty signals */
+ signals_init();
+
+ /* Initialize */
+ init_angband();
+
+#ifndef OLD_SAVEFILE_CODE
+
+ /* Hack - because this port has New/Open menus XXX */
+ savefile[0] = '\0';
+
+#endif /* !OLD_SAVEFILE_CODE */
+
+ /* Prompt the user */
+ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 17);
+ Term_fresh();
+
+ /* Activate more hook */
+ plog_aux = hook_plog;
+
+
+ /* Processing loop */
+ gtk_main();
+
+
+ /* Free allocated memory */
+ cleanup_angband();
+
+ /* Stop now */
+ quit(NULL);
+
+#else /* !SAVEFILE_SCREEN */
+
+ /* Activate more hook */
+ plog_aux = hook_plog;
+
+ /* It's too early to set this, but cannot do so elsewhere XXX XXX */
+ game_in_progress = TRUE;
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GTK2 */
diff --git a/src/main-ibm.c b/src/main-ibm.c
new file mode 100644
index 00000000..6201649c
--- /dev/null
+++ b/src/main-ibm.c
@@ -0,0 +1,1594 @@
+/* File: main-ibm.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: Visual Display Support for "term.c", for the IBM */
+
+/*
+ * Original code by "Billy Tanksley (wtanksle@ucsd.edu)"
+ * Use "Makefile.ibm" to compile Angband using this file.
+ *
+ * Support for DJGPP v2 by "Scott Egashira (egashira@u.washington.edu)"
+ *
+ * Extensive modifications by "Ben Harrison (benh@phial.com)",
+ * including "collation" of the Watcom C/C++ and DOS-286 patches.
+ *
+ * Watcom C/C++ changes by "David Boeren (akemi@netcom.com)"
+ * Use "Makefile.wat" to compile this file with Watcom C/C++, and
+ * be sure to define "USE_IBM" and "USE_WAT".
+ *
+ * DOS-286 (conio.h) changes by (Roland Jay Roberts (jay@map.com)
+ * Use "Makefile.286" (not ready) to compile this file for DOS-286,
+ * and be sure to define "USE_IBM", "USE_WAT", and "USE_286". Also,
+ * depending on your compiler, you may need to define "USE_CONIO".
+ *
+ * True color palette support by "Mike Marcelais (michmarc@microsoft.com)",
+ * with interface to the "color_table" array by Ben Harrison.
+ *
+ * Both "shift" keys are treated as "identical", and all the modifier keys
+ * (control, shift, alt) are ignored when used with "normal" keys, unless
+ * they modify the underlying "ascii" value of the key. You must use the
+ * new "user pref files" to be able to interact with the keypad and such.
+ *
+ * The "lib/user/pref-ibm.prf" file contains macro definitions and possible
+ * alternative color set definitions. The "lib/user/font-ibm.prf" contains
+ * attr/char mappings for walls and floors and such.
+ *
+ * Note the "Term_user_ibm()" function hook, which could allow the user
+ * to interact with the "main-ibm.c" visual system. Currently this hook
+ * is unused, but, for example, it could allow the user to toggle "sound"
+ * or "graphics" modes, or to select the number of screen rows, with the
+ * extra screen rows being used for the mirror window.
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_IBM
+
+
+/*
+ * Use a "virtual" screen to "buffer" screen writes.
+ */
+#define USE_VIRTUAL
+
+#ifdef USE_DOSSOCK
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+#include <bios.h>
+#include <dos.h>
+#include <dir.h>
+
+#ifdef USE_WAT
+
+# include <conio.h>
+
+# ifdef USE_CONIO
+# else /* USE_CONIO */
+
+# include <graph.h>
+
+# define bioskey(C) _bios_keybrd(C)
+
+# endif /* USE_CONIO */
+
+# ifndef USE_286
+# define int86(a, b, c) int386(a, b, c)
+# endif
+
+# define inportb(x) inp(x)
+# define outportb(x, y) outp(x, y)
+
+#else /* USE_WAT */
+
+# if __DJGPP__ > 1
+
+# include <pc.h>
+# include <osfcn.h>
+
+# else /* __DJGPP__ > 1 */
+# ifdef __DJGPP__
+# error "Upgrade to version 2.0 of DJGPP"
+# endif /* __DJGPP__ */
+# endif /* __DJGPP__ > 1 */
+
+#endif /* USE_WAT */
+
+
+#ifdef USE_CONIO
+
+# include <conio.h>
+
+/*
+ * Hack -- write directly to video card
+ */
+extern int directvideo = 1;
+
+/*
+ * Hack -- no virtual screen
+ */
+# undef USE_VIRTUAL
+
+#endif /* USE_CONIO */
+
+
+/*
+ * Keypress input modifier flags (hard-coded by DOS)
+ */
+#define K_RSHIFT 0 /* Right shift key down */
+#define K_LSHIFT 1 /* Left shift key down */
+#define K_CTRL 2 /* Ctrl key down */
+#define K_ALT 3 /* Alt key down */
+#define K_SCROLL 4 /* Scroll lock on */
+#define K_NUM 5 /* Num lock on */
+#define K_CAPS 6 /* Caps lock on */
+#define K_INSERT 7 /* Insert on */
+
+
+/*
+ * Foreground color bits (hard-coded by DOS)
+ */
+#define VID_BLACK 0x00
+#define VID_BLUE 0x01
+#define VID_GREEN 0x02
+#define VID_CYAN 0x03
+#define VID_RED 0x04
+#define VID_MAGENTA 0x05
+#define VID_YELLOW 0x06
+#define VID_WHITE 0x07
+
+/*
+ * Bright text (hard-coded by DOS)
+ */
+#define VID_BRIGHT 0x08
+
+/*
+ * Background color bits (hard-coded by DOS)
+ */
+#define VUD_BLACK 0x00
+#define VUD_BLUE 0x10
+#define VUD_GREEN 0x20
+#define VUD_CYAN 0x30
+#define VUD_RED 0x40
+#define VUD_MAGENTA 0x50
+#define VUD_YELLOW 0x60
+#define VUD_WHITE 0x70
+
+/*
+ * Blinking text (hard-coded by DOS)
+ */
+#define VUD_BRIGHT 0x80
+
+
+/*
+ * Screen Size
+ */
+static int rows = 25;
+static int cols = 80;
+
+
+/*
+ * Physical Screen
+ */
+#ifdef USE_286
+# define PhysicalScreen ((byte *)MK_FP(0xB800, 0x0000))
+#else
+# define PhysicalScreen ((byte *)(0xB800 << 4))
+#endif
+
+
+#ifdef USE_VIRTUAL
+
+/*
+ * Virtual Screen Contents
+ */
+static byte *VirtualScreen;
+
+#else
+
+/*
+* Physical screen access
+*/
+#define VirtualScreen PhysicalScreen
+
+#endif
+
+
+/*
+ * Hack -- the cursor "visibility"
+ */
+static int saved_cur_v;
+static int saved_cur_high;
+static int saved_cur_low;
+
+
+#ifdef USE_CONIO
+#else /* USE_CONIO */
+
+/*
+* This array is used for "wiping" the screen
+*/
+static byte wiper[160];
+
+#endif /* USE_CONIO */
+
+
+/*
+ * The main screen (currently the only screen)
+ */
+static term term_screen_body;
+
+
+/*
+ * Choose between the "complex" and "simple" color methods
+ */
+static byte use_color_complex = FALSE;
+
+
+/*
+ * The "complex" color set
+ */
+static long ibm_color_complex[16];
+
+
+/*
+ * The "simple" color set
+ *
+ * This table is used by the "color" code to instantiate the "approximate"
+ * Angband colors using the only colors available on crappy monitors.
+ *
+ * The entries below are taken from the "color bits" defined above.
+ *
+ * Note that values from 16 to 255 are extremely ugly.
+ *
+ * The values below came from various sources, if you do not like them,
+ * get a better monitor, or edit "pref-ibm.prf" to use different codes.
+ *
+ * Note that many of the choices below suck, but so do crappy monitors.
+ */
+static byte ibm_color_simple[16] =
+{
+ VID_BLACK, /* Dark */
+ VID_WHITE, /* White */
+ VID_CYAN, /* Slate XXX */
+ VID_RED | VID_BRIGHT, /* Orange XXX */
+ VID_RED, /* Red */
+ VID_GREEN, /* Green */
+ VID_BLUE, /* Blue */
+ VID_YELLOW, /* Umber XXX */
+ VID_BLACK | VID_BRIGHT, /* Light Dark */
+ VID_CYAN | VID_BRIGHT, /* Light Slate XXX */
+ VID_MAGENTA, /* Violet */
+ VID_YELLOW | VID_BRIGHT, /* Yellow */
+ VID_MAGENTA | VID_BRIGHT, /* Light Red XXX */
+ VID_GREEN | VID_BRIGHT, /* Light Green */
+ VID_BLUE | VID_BRIGHT, /* Light Blue */
+ VID_YELLOW /* Light Umber XXX */
+};
+
+
+
+/*
+ * Activate the "ibm_color_complex" palette information.
+ *
+ * Code by Mike Marcelais, with help from "The programmer's guide
+ * to the EGA and VGA video cards" [Farraro].
+ *
+ * On VGA cards, colors go through a double-indirection when looking
+ * up the `real' color when in 16 color mode. The color value in the
+ * attribute is looked up in the EGA color registers. Then that value
+ * is looked up in the VGA color registers. Then the color is displayed.
+ * This is done for compatability. However, the EGA registers are
+ * initialized by default to 0..5, 14, 7, 38..3F and not 0..F which means
+ * that unless these are reset, the VGA setpalette function will not
+ * update the correct palette register!
+ *
+ * DJGPP's GrSetColor() does _not_ set the EGA palette list, only the
+ * VGA color list.
+ *
+ * Note that the "traditional" method, using "int86(0x10)", is very slow
+ * when called in protected mode, so we use a faster method using video
+ * ports instead.
+ *
+ * On Watcom machines, we could simply use the special "_remapallpalette()"
+ * function, which not only sets both palette lists (see below) but also
+ * checks for legality of the monitor mode, but, if we are doing bitmapped
+ * graphics, that function forgets to set the EGA registers for some reason.
+ */
+static void activate_color_complex(void)
+{
+ int i;
+ printf("%c%c%c%c", 8, 8, 8, 8);
+
+#if 1
+
+ /* Edit the EGA palette */
+ inportb(0x3da);
+
+ /* Edit the colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Set color "i" */
+ outportb(0x3c0, i);
+
+ /* To value "i" */
+ outportb(0x3c0, i);
+ }
+
+ /* Use that EGA palette */
+ outportb(0x3c0, 0x20);
+
+ /* Edit VGA palette, starting at color zero */
+ outportb(0x3c8, 0);
+
+ /* Send the colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Send the red, green, blue components */
+ outportb(0x3c9, ((ibm_color_complex[i]) & 0xFF));
+ outportb(0x3c9, ((ibm_color_complex[i] >> 8) & 0xFF));
+ outportb(0x3c9, ((ibm_color_complex[i] >> 16) & 0xFF));
+ }
+
+#else /* 1 */
+
+ /* Set the colors */
+ for (i = 0; i < 16; i++)
+ {
+ union REGS r;
+
+ /* Set EGA color */
+ r.h.ah = 0x10;
+ r.h.al = 0x00;
+
+ /* Set color "i" */
+ r.h.bl = i;
+
+ /* To value "i" */
+ r.h.bh = i;
+
+ /* Do it */
+ int86(0x10, &r, &r);
+
+ /* Set VGA color */
+ r.h.ah = 0x10;
+ r.h.al = 0x10;
+
+ /* Set color "i" */
+ r.h.bh = 0x00;
+ r.h.bl = i;
+
+ /* Use this "green" value */
+ r.h.ch = ((ibm_color_complex[i] >> 8) & 0xFF);
+
+ /* Use this "blue" value */
+ r.h.cl = ((ibm_color_complex[i] >> 16) & 0xFF);
+
+ /* Use this "red" value */
+ r.h.dh = ((ibm_color_complex[i]) & 0xFF);
+
+ /* Do it */
+ int86(0x10, &r, &r);
+ }
+
+#endif /* 1 */
+
+}
+
+
+#ifdef SUPPORT_GAMMA
+
+/*
+ * When set to TRUE, indicates that we can use gamma_table
+ */
+static bool gamma_table_ready = FALSE;
+
+
+/*
+ * Build gamma correction table if requested and applicable
+ */
+static void setup_gamma_table(void)
+{
+ static u16b old_gamma_val = 0;
+
+ /* (Re)build the table only when gamma value changes */
+ if (gamma_val == old_gamma_val) return;
+
+ /* Temporarily inactivate the table */
+ gamma_table_ready = FALSE;
+
+ /* Validate gamma_val */
+ if ((gamma_val <= 0) || (gamma_val >= 256))
+ {
+ /* Reset */
+ old_gamma_val = gamma_val = 0;
+
+ /* Leave it inactive */
+ return;
+ }
+
+ /* (Re)build the table */
+ build_gamma_table(gamma_val);
+
+ /* Remember gamma_val */
+ old_gamma_val = gamma_val;
+
+ /* Activate the table */
+ gamma_table_ready = TRUE;
+}
+
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * Note the use of "(x >> 2)" to convert an 8 bit value to a 6 bit value
+ * without losing much precision.
+ */
+static int Term_xtra_ibm_react(void)
+{
+ int i;
+
+ /* Complex method */
+ if (use_color_complex)
+ {
+ long rv, gv, bv, code;
+
+ bool change = FALSE;
+
+#ifdef SUPPORT_GAMMA
+
+ /* Build gamma_table */
+ setup_gamma_table();
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Save the default colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+#ifdef SUPPORT_GAMMA
+
+ /* Hack - Gamma correction */
+ if (gamma_table_ready)
+ {
+ rv = gamma_table[rv];
+ gv = gamma_table[gv];
+ bv = gamma_table[bv];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* 8 bit to 6 bit conversion */
+ rv = rv >> 2;
+ gv = gv >> 2;
+ bv = bv >> 2;
+
+ /* Extract a full color code */
+ code = ((rv) | (gv << 8) | (bv << 16));
+
+ /* Activate changes */
+ if (ibm_color_complex[i] != code)
+ {
+ /* Note the change */
+ change = TRUE;
+
+ /* Apply the desired color */
+ ibm_color_complex[i] = code;
+ }
+ }
+
+ /* Activate the palette if needed */
+ if (change) activate_color_complex();
+ }
+
+ /* Simple method */
+ else
+ {
+ /* Save the default colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Simply accept the desired colors */
+ ibm_color_simple[i] = angband_color_table[i][0];
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Hack -- set the cursor "visibility"
+ */
+static void curs_set(int v)
+{
+ /* If needed */
+ if (saved_cur_v != v)
+ {
+ union REGS r;
+
+ /* Set cursor */
+ r.h.ah = 1;
+
+ /* Visible */
+ if (v)
+ {
+ /* Use the saved values */
+ r.h.ch = saved_cur_high;
+ r.h.cl = saved_cur_low;
+ }
+
+ /* Invisible */
+ else
+ {
+ /* Make it invisible */
+ r.h.ch = 0x20;
+ r.h.cl = 0x00;
+ }
+
+ /* Make the call */
+ int86(0x10, &r, &r);
+
+ /* Save the cursor state */
+ saved_cur_v = v;
+ }
+}
+
+
+
+/*
+ * Process an event (check for a keypress)
+ *
+ * The keypress processing code is often the most system dependant part
+ * of Angband, since sometimes even the choice of compiler is important.
+ *
+ * For the IBM, we divide all keypresses into two categories, first, the
+ * "normal" keys, including all keys required to play Angband, and second,
+ * the "special" keys, such as keypad keys, function keys, and various keys
+ * used in combination with various modifier keys.
+ *
+ * To simplify this file, we use Angband's "macro processing" ability, in
+ * combination with a specialized "pref-ibm.prf" file, to handle most of the
+ * "special" keys, instead of attempting to fully analyze them here. This
+ * file only has to determine when a "special" key has been pressed, and
+ * translate it into a simple string which signals the use of a "special"
+ * key, the set of modifiers used, if any, and the hardware scan code of
+ * the actual key which was pressed. To simplify life for the user, we
+ * treat both "shift" keys as identical modifiers.
+ *
+ * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers
+ * ("C" for control, "S" for shift, "A" for alt, or any ordered combination),
+ * and "SS" encodes the keypress (as the two "digit" hexadecimal encoding of
+ * the scan code of the key that was pressed), and the "^_" and "x" and "\r"
+ * delimit the encoding for recognition by the macro processing code.
+ *
+ * Some important facts about scan codes follow. All "normal" keys use
+ * scan codes from 1-58. The "function" keys use 59-68 (and 133-134).
+ * The "keypad" keys use 69-83. Escape uses 1. Enter uses 28. Control
+ * uses 29. Left Shift uses 42. Right Shift uses 54. PrtScrn uses 55.
+ * Alt uses 56. Space uses 57. CapsLock uses 58. NumLock uses 69.
+ * ScrollLock uses 70. The "keypad" keys which use scan codes 71-83
+ * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL.
+ *
+ * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more
+ * information, including better access to the keypad keys in combination
+ * with various modifiers, but only works on "PC's after 6/1/86", and there
+ * is no way to determine if the function is provided on a machine. I have
+ * been told that without it you cannot detect, for example, control-left.
+ * The basic scan code + ascii value pairs returned by the keypad follow,
+ * with values in parentheses only available to "bioskey(0x10)".
+ *
+ * / * - + 1 2 3 4
+ * Norm: 352f 372a 4a2d 4e2b 4f00 5000 5100 4b00
+ * Shft: 352f 372a 4a2d 4e2b 4f31 5032 5133 4b34
+ * Ctrl: (9500) (9600) (8e00) (9000) 7500 (9100) 7600 7300
+ *
+ * 5 6 7 8 9 0 . Enter
+ * Norm: (4c00) 4d00 4700 4800 4900 5200 5300 (e00d)
+ * Shft: 4c35 4d36 4737 4838 4939 5230 532e (e00d)
+ * Ctrl: (8f00) 7400 7700 (8d00) 8400 (9200) (9300) (e00a)
+ *
+ * See "pref-ibm.prf" for the "standard" macros for various keys.
+ *
+ * Certain "bizarre" keypad keys (such as "enter") return a "scan code"
+ * of "0xE0", and a "usable" ascii value. These keys should be treated
+ * like the normal keys, see below. XXX XXX XXX Note that these "special"
+ * keys could be prefixed with an optional "ctrl-^" which would allow them
+ * to be used in macros without hurting their use in normal situations.
+ */
+static errr Term_xtra_ibm_event(int v)
+{
+ int i, k, s;
+
+ bool mc = FALSE;
+ bool ms = FALSE;
+ bool ma = FALSE;
+
+
+ /* Hack -- Check for a keypress */
+ if (!v && !bioskey(1)) return (1);
+
+ /* Wait for a keypress */
+ k = bioskey(0x10);
+
+ /* Access the "modifiers" */
+ i = bioskey(2);
+
+ /* Extract the "scan code" */
+ s = ((k >> 8) & 0xFF);
+
+ /* Extract the "ascii value" */
+ k = (k & 0xFF);
+
+ /* Process "normal" keys */
+ if ((s <= 58) || (s == 0xE0))
+ {
+ /* Enqueue it */
+ if (k) Term_keypress(k);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Extract the modifier flags */
+ if (i & (1 << K_CTRL)) mc = TRUE;
+ if (i & (1 << K_LSHIFT)) ms = TRUE;
+ if (i & (1 << K_RSHIFT)) ms = TRUE;
+ if (i & (1 << K_ALT)) ma = TRUE;
+
+
+ /* Begin a "macro trigger" */
+ Term_keypress(31);
+
+ /* Hack -- Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Introduce the hexidecimal scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[s / 16]);
+ Term_keypress(hexsym[s % 16]);
+
+ /* End the "macro trigger" */
+ Term_keypress(13);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ *
+ * The given parameters are "valid".
+ */
+static errr Term_xtra_ibm(int n, int v)
+{
+ int i;
+
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Make a "bell" noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Make a bell noise */
+ (void)write(1, "\007", 1);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Set the cursor shape */
+ case TERM_XTRA_SHAPE:
+ {
+ /* Set cursor shape */
+ curs_set(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush one line of output */
+ case TERM_XTRA_FROSH:
+ {
+
+#ifdef USE_VIRTUAL
+
+# ifdef USE_WAT
+
+ /* Copy the virtual screen to the physical screen */
+ memcpy(PhysicalScreen + (v*160), VirtualScreen + (v*160), 160);
+
+# else /* USE_WAT */
+
+ /* Apply the virtual screen to the physical screen */
+ ScreenUpdateLine(VirtualScreen + ((v*cols) << 1), v);
+
+# endif /* USE_WAT */
+
+#endif /* USE_VIRTUAL */
+
+ /* Success */
+ return (0);
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+
+#ifdef USE_CONIO
+
+ /* Clear the screen */
+ clrscr();
+
+#else /* USE_CONIO */
+
+ /* Clear each line (virtual or physical) */
+ for (i = 0; i < rows; i++)
+ {
+ /* Clear the line */
+ memcpy((VirtualScreen + ((i*cols) << 1)), wiper, (cols << 1));
+ }
+
+# ifdef USE_VIRTUAL
+
+# ifdef USE_WAT
+
+ /* Copy the virtual screen to the physical screen */
+ memcpy(PhysicalScreen, VirtualScreen, 25*80*2);
+
+# else /* USE_WAT */
+
+ /* Erase the physical screen */
+ ScreenClear();
+
+# endif /* USE_WAT */
+
+# endif /* USE_VIRTUAL */
+
+#endif /* USE_CONIO */
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ {
+ irc_poll();
+
+ /* Process one event */
+ return (Term_xtra_ibm_event(v));
+ }
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Strip events */
+ while (!Term_xtra_ibm_event(FALSE)) /* loop */;
+
+ /* Success */
+ return (0);
+ }
+
+ /* React to global changes */
+ case TERM_XTRA_REACT:
+ {
+ /* React to "color_table" changes */
+ return (Term_xtra_ibm_react());
+ }
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ /* Delay if needed */
+ if (v > 0) delay(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /*
+ * Scans for subdirectories in a directory "scansubdir_dir"
+ * and place teh result in "scansubdir_result/scansubdir_max"
+ */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ struct ffblk f;
+ int done;
+
+ done = findfirst(format("%s\\*", scansubdir_dir), &f, FA_DIREC);
+
+ scansubdir_max = 0;
+ while ((!done) && (scansubdir_max < 255))
+ {
+ if ((f.ff_attrib & FA_DIREC) && (strcmp(f.ff_name, ".")) && (strcmp(f.ff_name, "..")))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(f.ff_name);
+ scansubdir_max++;
+ }
+
+ done = findnext(&f);
+ }
+
+ return 0;
+ }
+ }
+
+ /* Unknown request */
+ return (1);
+}
+
+
+
+/*
+ * Move the cursor
+ *
+ * The given parameters are "valid".
+ */
+static errr Term_curs_ibm(int x, int y)
+{
+
+#ifdef USE_WAT
+
+# ifdef USE_CONIO
+
+ /* Place the cursor */
+ gotoxy(x + 1, y + 1);
+
+# else /* USE_CONIO */
+
+ union REGS r;
+
+ r.h.ah = 2;
+ r.h.bh = 0;
+ r.h.dl = x;
+ r.h.dh = y;
+
+ /* Place the cursor */
+ int86(0x10, &r, &r);
+
+# endif /* USE_CONIO */
+
+#else /* USE_WAT */
+
+ /* Move the cursor */
+ ScreenSetCursor(y, x);
+
+#endif /* USE_WAT */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a block of the screen
+ *
+ * The given parameters are "valid".
+ */
+static errr Term_wipe_ibm(int x, int y, int n)
+{
+
+#ifdef USE_CONIO
+
+ /* Wipe the region */
+ window(x + 1, y + 1, x + n, y + 1);
+ clrscr();
+ window(1, 1, cols, rows);
+
+#else /* USE_CONIO */
+
+ /* Wipe part of the virtual (or physical) screen */
+ memcpy(VirtualScreen + ((cols*y + x) << 1), wiper, n << 1);
+
+#endif /* USE_CONIO */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ *
+ * The given parameters are "valid". Be careful with "a".
+ *
+ * The string "cp" has length "n" and is NOT null-terminated.
+ */
+static errr Term_text_ibm(int x, int y, int n, byte a, const char *cp)
+{
+ register int i;
+ register byte attr;
+ register byte *dest;
+
+
+ /* Handle "complex" color */
+ if (use_color_complex)
+ {
+ /* Extract a color index */
+ attr = (a & 0x0F);
+ }
+
+ /* Handle "simple" color */
+ else
+ {
+ /* Extract a color value */
+ attr = ibm_color_simple[a & 0x0F];
+ }
+
+#ifdef USE_CONIO
+
+ /* Place the cursor */
+ gotoxy(x + 1, y + 1);
+
+ /* Set the attribute */
+ textattr(attr);
+
+ /* Dump the text */
+ for (i = 0; i < n; i++) putch(cp[i]);
+
+#else /* USE_CONIO */
+
+ /* Access the virtual (or physical) screen */
+ dest = VirtualScreen + (((cols * y) + x) << 1);
+
+ /* Save the data */
+ for (i = 0; i < n; i++)
+ {
+ /* Apply */
+ *dest++ = cp[i];
+ *dest++ = attr;
+ }
+
+#endif /* USE_CONIO */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place some attr/char pairs on the screen
+ *
+ * The given parameters are "valid".
+ */
+#ifdef USE_TRANSPARENCY
+static errr Term_pict_ibm(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
+#else /* USE_TRANSPARENCY */
+static errr Term_pict_ibm(int x, int y, int n, const byte *ap, const char *cp)
+#endif /* USE_TRANSPARENCY */
+{
+ register int i;
+ register byte attr;
+ register byte *dest;
+
+
+#ifdef USE_CONIO
+
+ /* Place the cursor */
+ gotoxy(x + 1, y + 1);
+
+ /* Dump the text */
+ for (i = 0; i < n; i++)
+ {
+ /* Handle "complex" color */
+ if (use_color_complex)
+ {
+ /* Extract a color index */
+ attr = (ap[i] & 0x0F);
+ }
+
+ /* Handle "simple" color */
+ else
+ {
+ /* Extract a color value */
+ attr = ibm_color_simple[ap[i] & 0x0F];
+ }
+
+ /* Set the attribute */
+ textattr(attr);
+
+ /* Dump the char */
+ putch(cp[i]);
+ }
+
+#else /* USE_CONIO */
+
+ /* Access the virtual (or physical) screen */
+ dest = VirtualScreen + (((cols * y) + x) << 1);
+
+ /* Save the data */
+ for (i = 0; i < n; i++)
+ {
+ /* Handle "complex" color */
+ if (use_color_complex)
+ {
+ /* Extract a color index */
+ attr = (ap[i] & 0x0F);
+ }
+
+ /* Handle "simple" color */
+ else
+ {
+ /* Extract a color value */
+ attr = ibm_color_simple[ap[i] & 0x0F];
+ }
+
+ /* Apply */
+ *dest++ = cp[i];
+ *dest++ = attr;
+ }
+
+#endif /* USE_CONIO */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Init a Term
+ */
+static void Term_init_ibm(term *t)
+{
+ /* XXX Nothing */
+}
+
+
+/*
+ * Nuke a Term
+ */
+static void Term_nuke_ibm(term *t)
+{
+
+#ifdef USE_WAT
+
+ /* Nothing */
+
+#else /* USE_WAT */
+
+ union REGS r;
+
+#endif /* USE_WAT */
+
+ /* Move the cursor to the bottom of the screen */
+ Term_curs_ibm(0, rows - 1);
+
+#ifdef USE_WAT
+
+ /* Restore the original video mode */
+ _setvideomode(_DEFAULTMODE);
+
+#else /* USE_WAT */
+
+ /* Restore the original video mode */
+ r.h.ah = 0x00;
+ r.h.al = 0x03;
+ int86(0x10, &r, &r);
+
+#endif /* USE_WAT */
+
+ /* Make the cursor visible */
+ curs_set(1);
+}
+
+
+
+#ifdef USE_GRAPHICS
+
+#ifdef USE_286
+
+/*
+ * In 286 mode we don't need to worry about translating from a 32bit
+ * pointer to a 16 bit pointer so we just call the interrupt function
+ *
+ * Note the use of "intr()" instead of "int86()" so we can pass
+ * segment registers.
+ */
+void enable_graphic_font(void *font)
+{
+ union REGPACK regs =
+ {0};
+
+ regs.h.ah = 0x11; /* Text font function */
+ regs.h.bh = 0x10; /* Size of a character -- 16 bytes */
+ regs.h.cl = 0xFF; /* Last character in font */
+ regs.x.es = FP_SEG(font); /* Pointer to font */
+ regs.x.bp = FP_OFF(font);
+ intr(0x10, &regs);
+}
+
+#else /* USE_286 */
+
+#ifdef USE_WAT
+
+/*
+* This structure is used by the DMPI function to hold registers when
+* doing a real mode interrupt call. (Stolen from the DJGPP <dpmi.h>
+ * header file).
+*/
+
+typedef union
+{
+ struct
+ {
+ unsigned long edi;
+ unsigned long esi;
+ unsigned long ebp;
+ unsigned long res;
+ unsigned long ebx;
+ unsigned long edx;
+ unsigned long ecx;
+ unsigned long eax;
+ }
+ d;
+ struct
+ {
+ unsigned short di, di_hi;
+ unsigned short si, si_hi;
+ unsigned short bp, bp_hi;
+ unsigned short res, res_hi;
+ unsigned short bx, bx_hi;
+ unsigned short dx, dx_hi;
+ unsigned short cx, cx_hi;
+ unsigned short ax, ax_hi;
+ unsigned short flags;
+ unsigned short es;
+ unsigned short ds;
+ unsigned short fs;
+ unsigned short gs;
+ unsigned short ip;
+ unsigned short cs;
+ unsigned short sp;
+ unsigned short ss;
+ }
+ x;
+ struct
+ {
+ unsigned char edi[4];
+ unsigned char esi[4];
+ unsigned char ebp[4];
+ unsigned char res[4];
+ unsigned char bl, bh, ebx_b2, ebx_b3;
+ unsigned char dl, dh, edx_b2, edx_b3;
+ unsigned char cl, ch, ecx_b2, ecx_b3;
+ unsigned char al, ah, eax_b2, eax_b3;
+ }
+ h;
+} __dpmi_regs;
+
+unsigned __dpmi_allocate_dos_memory(int size, unsigned *selector)
+{
+ union REGPACK regs =
+ {0};
+
+ regs.w.ax = 0x100; /* DPMI function -- allocate low memory */
+ regs.w.bx = size; /* Number of Paragraphs to allocate */
+ intr(0x31, &regs); /* DPMI interface */
+
+ *selector = regs.w.dx;
+ return (regs.w.ax);
+}
+
+void __dpmi_free_dos_memory(unsigned sel)
+{
+ union REGPACK regs =
+ {0};
+
+ regs.w.ax = 0x101; /* DPMI function -- free low memory */
+ regs.x.edx = sel; /* PM selector for memory block */
+ intr(0x31, &regs); /* DPMI interface */
+}
+
+void __dpmi_int(int intno, __dpmi_regs *dblock)
+{
+ union REGPACK regs =
+ {0};
+
+ regs.w.ax = 0x300; /* DPMI function -- real mode interrupt */
+ regs.h.bl = intno; /* interrupt 0x10 */
+ regs.x.edi = FP_OFF(dblock); /* Pointer to dblock (offset and segment) */
+ regs.x.es = FP_SEG(dblock);
+ intr(0x31, &regs); /* DPMI interface */
+}
+
+unsigned short __dpmi_sel = 0x0000;
+#define _farsetsel(x) __dpmi_sel=(x)
+extern void _farnspokeb(unsigned long offset, unsigned char value);
+#pragma aux _farnspokeb = \
+"push fs" \
+"mov fs,__dpmi_sel" \
+"mov fs:[eax],bl" \
+"pop fs" \
+parm [eax] [bl];
+
+#else /* USE_WAT */
+
+#include <dpmi.h>
+#include <go32.h>
+#include <sys/farptr.h>
+
+#endif /* USE_WAT */
+
+
+/*
+* Since you cannot send 32bit pointers to a 16bit interrupt handler
+* and the video BIOS wants a (16bit) pointer to the font, we have
+* to allocate a block of dos memory, copy the font into it, then
+* translate a 32bit pointer into a 16bit pointer to that block.
+*
+* DPMI - Dos Protected Mode Interface provides functions that let
+* us do that.
+*/
+void enable_graphic_font(const char *font)
+{
+ __dpmi_regs dblock = {{0}};
+
+ unsigned seg, sel, i;
+
+ /*
+ * Allocate a block of memory 4096 bytes big in `low memory' so a real
+ * mode interrupt can access it. Real mode pointer is returned as seg:0
+ * Protected mode pointer is sel:0.
+ */
+ seg = __dpmi_allocate_dos_memory(256, &sel);
+
+ /* Copy the information into low memory buffer, by copying one byte at
+ * a time. According to the info in <sys/farptr.h>, the functions
+ * _farsetsel() and _farnspokeb() will optimise away completely
+ */
+ _farsetsel(sel); /* Set the selector to write to */
+ for (i = 0; i < 4096; i++)
+ {
+ _farnspokeb(i, *font++); /* Copy 1 byte into low (far) memory */
+ }
+
+ /*
+ * Now we use DPMI as a jumper to call the real mode interrupt. This
+ * is needed because loading `es' while in protected mode with a real
+ * mode pointer will cause an Protection Fault and calling the interrupt
+ * directly using the protected mode pointer will result in garbage
+ * being received by the interrupt routine
+ */
+ dblock.d.eax = 0x1100; /* BIOS function -- set font */
+ dblock.d.ebx = 0x1000; /* bh = size of a letter; bl = 0 (reserved) */
+ dblock.d.ecx = 0x00FF; /* Last character in font */
+ dblock.x.es = seg; /* Pointer to font segment */
+ dblock.d.ebp = 0x0000; /* Pointer to font offset */
+
+ __dpmi_int(0x10, &dblock);
+
+ /* We're done with the low memory, free it */
+ __dpmi_free_dos_memory(sel);
+}
+
+#endif /* USE_286 */
+
+#endif /* ALLOW_GRAPH */
+
+#ifdef USE_DOSSOCK
+void *zsock_connect(char *hos, short port)
+{
+ struct hostent *host;
+ struct sockaddr_in sin;
+ int *client;
+
+ MAKE(client, int);
+ *client = socket(AF_INET, SOCK_STREAM, 0);
+
+ host = gethostbyname(hos);
+
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ((struct in_addr *)(host->h_addr))->s_addr;
+ sin.sin_port = htons(port);
+
+ if (connect(*client, (struct sockaddr*)&sin, sizeof sin) == -1)
+ {
+ /* could not connect to server */
+ return NULL;
+ }
+
+ return client;
+}
+
+bool zsock_can_read(void *client)
+{
+ struct timeval t;
+ fd_set rd;
+ int *c = client;
+
+ FD_ZERO(&rd);
+ FD_SET(*c, &rd);
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ select(*c + 1, &rd, NULL, NULL, &t);
+ if (FD_ISSET(*c, &rd)) return TRUE;
+ else return (FALSE);
+}
+
+bool zsock_wait(void *client)
+{
+ struct timeval t;
+ fd_set rd;
+ int *c = client;
+
+ t.tv_sec = 30;
+ t.tv_usec = 0;
+
+ FD_ZERO(&rd);
+ FD_SET(*c, &rd);
+ select(*c + 1, &rd, NULL, NULL, &t);
+ if (FD_ISSET(*c, &rd)) return TRUE;
+ else return (FALSE);
+}
+
+void zsock_disconnect(void *client)
+{
+ close(*(int*)client);
+ FREE(client, int);
+}
+
+void zsock_send(void *sock, char *str)
+{
+ send(*(int*)sock, str, strlen(str), 0);
+}
+
+void zsock_recv(void *sock, char *str, int len)
+{
+ char c;
+ int l = 0;
+
+ while ((l < len) && zsock_can_read(sock))
+ {
+ if (!recv(*(int*)sock, &c, 1, 0))
+ {
+ irc_disconnect();
+ break;
+ }
+ if (c == '\r') continue;
+ if (c == '\n') break;
+ str[l++] = c;
+ }
+ str[l] = '\0';
+}
+#endif
+
+
+/*
+ * Initialize the IBM "visual module"
+ *
+ * Hack -- we assume that "blank space" should be "white space"
+ * (and not "black space" which might make more sense).
+ *
+ * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value
+ * into an 8 bit value, without losing much precision, by using the 2
+ * most significant bits as the least significant bits in the new value.
+ */
+errr init_ibm(void)
+{
+ int i;
+ int mode;
+
+ term *t = &term_screen_body;
+
+ union REGS r;
+
+ /* Check for "Windows" */
+ if (getenv("windir"))
+ {
+ r.h.ah = 0x16; /* Windows API Call -- Set device focus */
+ r.h.al = 0x8B; /* Causes Dos boxes to become fullscreen */
+ r.h.bh = r.h.bl = 0x00; /* 0x0000 = current Dos box */
+ int86(0x2F, &r, &r); /* Call the Windows API */
+ }
+
+ /* Initialize "color_table" */
+ for (i = 0; i < 16; i++)
+ {
+ long rv, gv, bv;
+
+ /* Extract desired values */
+ rv = angband_color_table[i][1] >> 2;
+ gv = angband_color_table[i][2] >> 2;
+ bv = angband_color_table[i][3] >> 2;
+
+ /* Extract the "complex" codes */
+ ibm_color_complex[i] = ((rv) | (gv << 8) | (bv << 16));
+
+ /* Save the "simple" codes */
+ angband_color_table[i][0] = ibm_color_simple[i];
+ }
+
+#ifdef USE_WAT
+
+ /* Set the video mode */
+ if (_setvideomode(_VRES16COLOR))
+ {
+ mode = 0x13;
+ }
+
+ /* Wimpy monitor */
+ else
+ {
+ mode = 0x03;
+ }
+
+ /* Force 25 line mode */
+ _setvideomode(_TEXTC80);
+ _settextrows(25);
+
+#else /* USE_WAT */
+
+ /* Set video mode */
+ r.h.ah = 0x00;
+ r.h.al = 0x13; /* VGA only mode */
+ int86(0x10, &r, &r);
+
+ /* Get video mode */
+ r.h.ah = 0x0F;
+ int86(0x10, &r, &r);
+ mode = r.h.al;
+
+ /* Set video mode */
+ r.h.ah = 0x00;
+ r.h.al = 0x03; /* Color text mode */
+ int86(0x10, &r, &r);
+
+#endif /* USE_WAT */
+
+ /* Check video mode */
+ if (mode == 0x13)
+ {
+ /* Remember the mode */
+ use_color_complex = TRUE;
+
+ /* Instantiate the color set */
+ activate_color_complex();
+ }
+
+#ifdef USE_GRAPHICS
+
+ /* Try to activate bitmap graphics */
+ if (arg_graphics && use_color_complex)
+ {
+ FILE *f;
+
+ char buf[4096];
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA, "angband.fnt");
+
+ /* Open the file */
+ f = fopen(buf, "rb");
+
+ /* Okay */
+ if (f)
+ {
+ /* Load the bitmap data */
+ if (fread(buf, 1, 4096, f) != 4096)
+ {
+ quit("Corrupt 'angband.fnt' file");
+ }
+
+ /* Close the file */
+ fclose(f);
+
+ /* Enable graphics */
+ enable_graphic_font(buf);
+
+ /* Enable colors (again) */
+ activate_color_complex();
+
+ /* Use graphics */
+ use_graphics = TRUE;
+ }
+ }
+
+#endif
+
+#ifdef USE_CONIO
+#else /* USE_CONIO */
+
+ /* Build a "wiper line" */
+ for (i = 0; i < 80; i++)
+ {
+ /* Space */
+ wiper[2*i] = ' ';
+
+ /* Black */
+ wiper[2*i + 1] = TERM_WHITE;
+ }
+
+#endif /* USE_CONIO */
+
+
+#ifdef USE_VIRTUAL
+
+ /* Make the virtual screen */
+ C_MAKE(VirtualScreen, rows * cols * 2, byte);
+
+#endif /* USE_VIRTUAL */
+
+
+ /* Erase the screen */
+ Term_xtra_ibm(TERM_XTRA_CLEAR, 0);
+
+
+ /* Place the cursor */
+ Term_curs_ibm(0, 0);
+
+
+ /* Access the "default" cursor info */
+ r.h.ah = 3;
+ r.h.bh = 0;
+
+ /* Make the call */
+ int86(0x10, &r, &r);
+
+ /* Extract the standard cursor info */
+ saved_cur_v = 1;
+ saved_cur_high = r.h.ch;
+ saved_cur_low = r.h.cl;
+
+
+ /* Initialize the term */
+ term_init(t, 80, 24, 256);
+
+#ifdef USE_CONIO
+#else /* USE_CONIO */
+
+ /* Always use "Term_pict()" */
+ t->always_pict = TRUE;
+
+#endif /* USE_CONIO */
+
+ /* Use "white space" to erase */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Prepare the init/nuke hooks */
+ t->init_hook = Term_init_ibm;
+ t->nuke_hook = Term_nuke_ibm;
+
+ /* Connect the hooks */
+ t->xtra_hook = Term_xtra_ibm;
+ t->curs_hook = Term_curs_ibm;
+ t->wipe_hook = Term_wipe_ibm;
+ t->text_hook = Term_text_ibm;
+ t->pict_hook = Term_pict_ibm;
+
+ /* Save it */
+ term_screen = t;
+
+ /* Activate it */
+ Term_activate(term_screen);
+
+ /* Success */
+ return 0;
+}
+
+
+#endif /* USE_IBM */
diff --git a/src/main-lsl.c b/src/main-lsl.c
new file mode 100644
index 00000000..d24ede3f
--- /dev/null
+++ b/src/main-lsl.c
@@ -0,0 +1,598 @@
+/*
+ * File: main-lsl.c
+ * Purpose: Support for Linux-SVGALIB Angband
+ * Original Author: Jon Taylor (taylorj@gaia.ecs.csus.edu)
+ * Update by: Dennis Payne (dulsi@identicalsoftware.com)
+ * Version: 1.4.0, 12/05/99
+ *
+ * Large amounts of code rewritten by Steven Fuerst. 20/04/2001
+ *
+ * It now uses a hacked-up version of the X11 bmp-loading code.
+ * (Preparing to use 256 colour 16x16 mode)
+ */
+
+#include "angband.h"
+
+#ifdef USE_LSL
+
+/* Standard C header files */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* SVGAlib header files */
+#include <vga.h>
+#include <vgagl.h>
+#include <vgakeyboard.h>
+#include <zlib.h>
+
+#define COLOR_OFFSET 240
+
+/* Hack - Define font/graphics cell width and height */
+#define CHAR_W 8
+#define CHAR_H 13
+
+/* Global palette */
+static byte *pal = NULL;
+
+#ifdef USE_GRAPHICS
+
+/*
+ * The Win32 "BITMAPFILEHEADER" type.
+ */
+typedef struct BITMAPFILEHEADER
+{
+ u16b bfType;
+ u32b bfSize;
+ u16b bfReserved1;
+ u16b bfReserved2;
+ u32b bfOffBits;
+}
+BITMAPFILEHEADER;
+
+
+/*
+ * The Win32 "BITMAPINFOHEADER" type.
+ */
+typedef struct BITMAPINFOHEADER
+{
+ u32b biSize;
+ u32b biWidth;
+ u32b biHeight;
+ u16b biPlanes;
+ u16b biBitCount;
+ u32b biCompresion;
+ u32b biSizeImage;
+ u32b biXPelsPerMeter;
+ u32b biYPelsPerMeter;
+ u32b biClrUsed;
+ u32b biClrImportand;
+}
+BITMAPINFOHEADER;
+
+/*
+ * The Win32 "RGBQUAD" type.
+ */
+typedef struct RGBQUAD
+{
+ unsigned char b, g, r;
+ unsigned char filler;
+}
+RGBQUAD;
+
+
+/*** Helper functions for system independent file loading. ***/
+
+static byte get_byte(FILE *fff)
+{
+ /* Get a character, and return it */
+ return (getc(fff) & 0xFF);
+}
+
+static void rd_byte(FILE *fff, byte *ip)
+{
+ *ip = get_byte(fff);
+}
+
+static void rd_u16b(FILE *fff, u16b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u16b)(get_byte(fff)) << 8);
+}
+
+static void rd_u32b(FILE *fff, u32b *ip)
+{
+ (*ip) = get_byte(fff);
+ (*ip) |= ((u32b)(get_byte(fff)) << 8);
+ (*ip) |= ((u32b)(get_byte(fff)) << 16);
+ (*ip) |= ((u32b)(get_byte(fff)) << 24);
+}
+
+
+/*
+ * Read a Win32 BMP file.
+ *
+ * Assumes that the bitmap has a size such that no padding is needed in
+ * various places. Currently only handles bitmaps with 3 to 256 colors.
+ */
+static byte *ReadBMP(char *Name, int *bw, int *bh)
+{
+ FILE *f;
+
+ BITMAPFILEHEADER fileheader;
+ BITMAPINFOHEADER infoheader;
+
+ byte *Data;
+
+ int ncol;
+
+ int total;
+
+ int i;
+
+ u16b x, y;
+
+ /* Open the BMP file */
+ f = fopen(Name, "r");
+
+ /* No such file */
+ if (!f)
+ {
+ quit ("No bitmap to load!");
+ }
+
+ /* Read the "BITMAPFILEHEADER" */
+ rd_u16b(f, &(fileheader.bfType));
+ rd_u32b(f, &(fileheader.bfSize));
+ rd_u16b(f, &(fileheader.bfReserved1));
+ rd_u16b(f, &(fileheader.bfReserved2));
+ rd_u32b(f, &(fileheader.bfOffBits));
+
+ /* Read the "BITMAPINFOHEADER" */
+ rd_u32b(f, &(infoheader.biSize));
+ rd_u32b(f, &(infoheader.biWidth));
+ rd_u32b(f, &(infoheader.biHeight));
+ rd_u16b(f, &(infoheader.biPlanes));
+ rd_u16b(f, &(infoheader.biBitCount));
+ rd_u32b(f, &(infoheader.biCompresion));
+ rd_u32b(f, &(infoheader.biSizeImage));
+ rd_u32b(f, &(infoheader.biXPelsPerMeter));
+ rd_u32b(f, &(infoheader.biYPelsPerMeter));
+ rd_u32b(f, &(infoheader.biClrUsed));
+ rd_u32b(f, &(infoheader.biClrImportand));
+
+ /* Verify the header */
+ if (feof(f) ||
+ (fileheader.bfType != 19778) ||
+ (infoheader.biSize != 40))
+ {
+ quit_fmt("Incorrect BMP file format %s", Name);
+ }
+
+ /* The two headers above occupy 54 bytes total */
+ /* The "bfOffBits" field says where the data starts */
+ /* The "biClrUsed" field does not seem to be reliable */
+ /* Compute number of colors recorded */
+ ncol = (fileheader.bfOffBits - 54) / 4;
+
+ for (i = 0; i < ncol; i++)
+ {
+ RGBQUAD clrg;
+
+ /* Read an "RGBQUAD" */
+ rd_byte(f, &(clrg.b));
+ rd_byte(f, &(clrg.g));
+ rd_byte(f, &(clrg.r));
+ rd_byte(f, &(clrg.filler));
+
+ /* Analyze the color */
+ pal[i * 3] = clrg.b;
+ pal[i * 3 + 1] = clrg.g;
+ pal[i * 3 + 2] = clrg.r;
+ }
+
+ /* Look for illegal bitdepths. */
+ if ((infoheader.biBitCount == 1) || (infoheader.biBitCount == 24))
+ {
+ quit_fmt("Illegal biBitCount %d in %s",
+ infoheader.biBitCount, Name);
+ }
+
+ /* Determine total bytes needed for image */
+ total = infoheader.biWidth * (infoheader.biHeight + 2);
+
+ /* Allocate image memory */
+ C_MAKE(Data, total, byte);
+
+ for (y = 0; y < infoheader.biHeight; y++)
+ {
+ int y2 = infoheader.biHeight - y - 1;
+
+ for (x = 0; x < infoheader.biWidth; x++)
+ {
+ int ch = getc(f);
+
+ /* Verify not at end of file XXX XXX */
+ if (feof(f)) quit_fmt("Unexpected end of file in %s", Name);
+
+ if (infoheader.biBitCount == 8)
+ {
+ Data[x + y2 * infoheader.biWidth] = ch;
+ }
+ else if (infoheader.biBitCount == 4)
+ {
+ Data[x + y2 * infoheader.biWidth] = ch / 16;
+ x++;
+ Data[x + y2 * infoheader.biWidth] = ch % 16;
+ }
+ }
+ }
+
+ fclose(f);
+
+ /* Save the size for later */
+ *bw = infoheader.biWidth;
+ *bh = infoheader.biHeight;
+
+ return (Data);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/* The main "term" structure */
+static term term_screen_body;
+
+/* The visible and virtual screens */
+GraphicsContext *screen;
+GraphicsContext *buffer;
+
+/* The font data */
+static void *font;
+
+/* Initialize the screen font */
+static void initfont(void)
+{
+ gzFile fontfile;
+ void *temp;
+ long junk;
+
+ if (!(fontfile = gzopen("/usr/lib/kbd/consolefonts/lat1-12.psf.gz", "r")))
+ {
+ /* Try uncompressed */
+ if (!(fontfile = gzopen("/usr/lib/kbd/consolefonts/lat1-12.psf", "r")))
+ {
+ printf ("Error: could not open font file. Aborting....\n");
+ exit(1);
+ }
+ }
+
+ /* Junk the 4-byte header */
+ gzread(fontfile, &junk, 4);
+
+ /* Initialize font */
+
+ /*
+ * Read in 13 bytes per character, and there are 256 characters
+ * in the font. This means we need to load 13x256 = 3328 bytes.
+ */
+ C_MAKE(temp, 256 * 13, byte);
+ gzread(fontfile, temp, 256 * 13);
+
+ /*
+ * I don't understand this code - SF
+ * (Is it converting from 8x13 -> 8x12?)
+ *
+ * I assume 15 is a colour...
+ */
+ font = malloc(256 * 8 * 12 * BYTESPERPIXEL);
+ gl_expandfont(8, 12, 15, temp, font);
+ gl_setfont(8, 12, font);
+
+ /* Cleanup */
+ C_FREE(temp, 256 * 13, byte);
+ gzclose(fontfile);
+}
+
+/* Initialize palette values for colors 0-15 */
+static void setpal(void)
+{
+ int i;
+ gl_setpalette(pal);
+ for (i = 0; i < 16; i++)
+ {
+ gl_setpalettecolor(COLOR_OFFSET + i,
+ angband_color_table[i][1] >> 2,
+ angband_color_table[i][2] >> 2,
+ angband_color_table[i][3] >> 2);
+ }
+}
+
+/*
+ * Check for "events"
+ * If block, then busy-loop waiting for event, else check once and exit.
+ */
+static errr CheckEvents(int block)
+{
+ int k = 0;
+
+ if (block)
+ {
+ k = vga_getkey();
+ if (k < 1) return (1);
+ }
+ else
+ {
+ k = vga_getch();
+ }
+
+ Term_keypress(k);
+ return (0);
+}
+
+
+/*
+ * Low-level graphics routine (assumes valid input)
+ * Do a "special thing"
+ */
+static errr term_xtra_svgalib(int n, int v)
+{
+ switch (n)
+ {
+ case TERM_XTRA_EVENT:
+ {
+ /* Process some pending events */
+ if (v) return (CheckEvents (FALSE));
+ while (!CheckEvents (TRUE));
+ return 0;
+ }
+
+ case TERM_XTRA_FLUSH:
+ {
+ /* Flush all pending events */
+ /* Should discard all key presses but unimplemented */
+ return 0;
+ }
+
+ case TERM_XTRA_CLEAR:
+ {
+ /* Clear the entire window */
+ gl_fillbox (0, 0, 80 * CHAR_W, 25 * CHAR_H, 0);
+ return 0;
+ }
+
+ case TERM_XTRA_DELAY:
+ {
+ /* Delay for some milliseconds */
+ usleep(1000 * v);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Low-level graphics routine (assumes valid input)
+ * Draws a "cursor" at (x,y)
+ */
+static errr term_curs_svgalib(int x, int y)
+{
+ gl_fillbox(x * CHAR_W, y * CHAR_H, CHAR_W, CHAR_H, 15);
+ return (0);
+}
+
+/*
+ * Low-level graphics routine (assumes valid input)
+ * Erases a rectangular block of characters from (x,y) to (x+w,y+h)
+ */
+static errr term_wipe_svgalib(int x, int y, int n)
+{
+ gl_fillbox(x * CHAR_W, y * CHAR_H, n * CHAR_W, CHAR_H, 0);
+ return (0);
+}
+
+/*
+ * Low-level graphics routine (assumes valid input)
+ * Draw n chars at location (x,y) with value s and attribute a
+ */
+static errr term_text_svgalib(int x, int y, int n, byte a, cptr s)
+{
+ /* Clear the area */
+ term_wipe_svgalib(x, y, n);
+
+ /* Draw the coloured text */
+ gl_colorfont(8, 12, COLOR_OFFSET + (a & 0x0F), font);
+ gl_writen(x * CHAR_W, y * CHAR_H, n, (char *) s);
+ return (0);
+}
+
+/*
+ * Low-level graphics routine (assumes valid input)
+ * Draw n chars at location (x,y) with value s and attribute a
+ */
+
+#ifdef USE_GRAPHICS
+
+# ifdef USE_TRANSPARENCY
+static errr term_pict_svgalib(int x, int y, int n,
+ const byte *ap, const char *cp, const byte *tap, const char *tcp)
+# else /* USE_TRANSPARENCY */
+static errr term_pict_svgalib(int x, int y, int n,
+ const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ int i;
+ int x2, y2;
+
+
+# ifdef USE_TRANSPARENCY
+ /* Hack - Ignore unused transparency data for now */
+ (void) tap;
+ (void) tcp;
+# endif /* USE_TRANSPARENCY */
+
+ for (i = 0; i < n; i++)
+ {
+ x2 = (cp[i] & 0x7F) * CHAR_W;
+ y2 = (ap[i] & 0x7F) * CHAR_H;
+
+ gl_copyboxfromcontext(buffer, x2, y2, CHAR_W, CHAR_H,
+ (x + i) * CHAR_W, y * CHAR_H);
+ }
+ return (0);
+}
+
+static void term_load_bitmap(void)
+{
+ char path[1024];
+
+ byte *temp = NULL;
+
+ int bw, bh;
+
+ /* Build the "graf" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ sprintf (path, "%s/8x13.bmp", path);
+
+ /* See if the file exists */
+ if (fd_close(fd_open(path, O_RDONLY)))
+ {
+ printf ("Unable to load bitmap data file %s, bailing out....\n", path);
+ exit ( -1);
+ }
+
+ temp = ReadBMP(path, &bw, &bh);
+
+ /* Blit bitmap into buffer */
+ gl_putbox(0, 0, bw, bh, temp);
+
+ FREE(temp, byte);
+
+ return;
+}
+
+#endif /* USE_GRAPHICS */
+
+/*
+ * Term hook
+ * Initialize a new term
+ */
+static void term_init_svgalib(term *t)
+{
+ int vgamode;
+
+ /* Only one term */
+ (void) t;
+
+ vga_init();
+
+ /* The palette is 256x3 bytes big (RGB). */
+ C_MAKE(pal, 768, byte);
+
+#ifdef USE_GRAPHICS
+
+ /* Hardwire this mode in for now */
+ vgamode = G1024x768x256;
+
+ /* Set up the bitmap buffer context */
+ gl_setcontextvgavirtual(vgamode);
+ buffer = gl_allocatecontext();
+ gl_getcontext(buffer);
+
+ /* Load bitmap into virtual screen */
+ term_load_bitmap();
+
+#endif /* USE_GRAPHICS */
+
+ /* Hardwire this mode in for now */
+ vgamode = G640x480x256;
+
+ /* Set up the physical screen context */
+ if (vga_setmode(vgamode) < 0)
+ {
+ quit("Graphics mode not available!");
+ }
+
+ gl_setcontextvga(vgamode);
+ screen = gl_allocatecontext();
+ gl_getcontext(screen);
+
+ /* Is this needed? */
+ gl_enablepageflipping(screen);
+
+ /* Set up palette colors */
+ setpal();
+
+ /* Load the character font data */
+ initfont();
+
+ /* Color 0 isn't transparent */
+ gl_setwritemode(WRITEMODE_OVERWRITE);
+}
+
+/*
+ * Term hook
+ * Nuke an old term
+ */
+static void term_nuke_svgalib(term *t)
+{
+ /* Only one term */
+ (void) t;
+
+ vga_setmode(TEXT);
+}
+
+/*
+ * Hook SVGAlib routines into term.c
+ */
+errr init_lsl(void)
+{
+ term *t = &term_screen_body;
+
+#ifdef USE_GRAPHICS
+
+ if (arg_graphics)
+ {
+ use_graphics = TRUE;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Initialize the term */
+ term_init(t, 80, 24, 1024);
+
+ /* The cursor is done via software and needs erasing */
+ t->soft_cursor = TRUE;
+
+ t->attr_blank = TERM_DARK;
+ t->char_blank = ' ';
+
+ /* Add hooks */
+ t->init_hook = term_init_svgalib;
+ t->nuke_hook = term_nuke_svgalib;
+ t->text_hook = term_text_svgalib;
+
+#ifdef USE_GRAPHICS
+
+ if (use_graphics)
+ {
+ t->pict_hook = term_pict_svgalib;
+ t->higher_pict = TRUE;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ t->wipe_hook = term_wipe_svgalib;
+ t->curs_hook = term_curs_svgalib;
+ t->xtra_hook = term_xtra_svgalib;
+
+ /* Save the term */
+ term_screen = t;
+
+ /* Activate it */
+ Term_activate(term_screen);
+
+ return (0);
+}
+
+#endif /* USE_LSL */
diff --git a/src/main-mac.c b/src/main-mac.c
new file mode 100644
index 00000000..2de64abd
--- /dev/null
+++ b/src/main-mac.c
@@ -0,0 +1,5504 @@
+/* File: main-mac.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Keith Randall, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with Macintosh computers.
+ *
+ * To use this file, use an appropriate "Makefile" or "Project File", which
+ * should define "MACINTOSH".
+ *
+ * The official compilation uses the CodeWarrior Pro compiler.
+ *
+ * If you are never going to use "graphics" (especially if you are not
+ * compiling support for graphics anyway) then you can delete the "pict"
+ * resource with id "1001" with no dangerous side effects.
+ *
+ *
+ * By default, this file assumes that you will be using a 68020 or better
+ * machine, running System 7 and Color Quickdraw. In fact, the game will
+ * refuse to run unless these features are available. This allows the use
+ * of a variety of interesting features such as graphics and sound.
+ *
+ * To create a version which can be used on 68000 machines, or on machines
+ * which are not running System 7 or Color Quickdraw, simply activate the
+ * "ANGBAND_LITE_MAC" compilation flag in the proper header file. This
+ * will disable all "modern" features used in this file, including support
+ * for multiple sub-windows, color, graphics, and sound.
+ *
+ * When compiling with the "ANGBAND_LITE_MAC" flag, the "ANGBAND_LITE"
+ * flag will be automatically defined, which will disable many of the
+ * advanced features of the game itself, reducing the total memory usage.
+ *
+ *
+ * Note that the "preference" file is now a simple text file called
+ * "Angband Preferences", which contains a version stamp, so that
+ * obsolete preference files can be ignored. This should probably
+ * be replaced with a "structured" preference file of some kind.
+ *
+ * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c"
+ * should probably be "unloaded" as soon as they are no longer needed,
+ * to save space, but I do not know how to do this. XXX XXX XXX
+ *
+ * Stange bug -- The first "ClipRect()" call crashes if the user closes
+ * all the windows, switches to another application, switches back, and
+ * re-opens the main window, for example, using "command-a". XXX XXX XXX
+ *
+ *
+ * Initial framework (and most code) by Ben Harrison (benh@phial.com).
+ *
+ * Some code adapted from "MacAngband 2.6.1" by Keith Randall
+ *
+ * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl).
+ *
+ * Most "USE_SFL_CODE" code provided by Steve Linberg (slinberg@crocker.com).
+ *
+ * Most of the graphics code is adapted from an extremely minimal subset of
+ * the "Sprite World II" package, an amazing (and free) animation package.
+ *
+ *
+ * Important Resources in the resource file:
+ *
+ * FREF 130 = ANGBAND_CREATOR / 'APPL' (application)
+ * FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file)
+ * FREF 130 = ANGBAND_CREATOR / 'TEXT' (bone file, generic text file)
+ * FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file)
+ *
+ * DLOG 128 = "About Angband..."
+ *
+ * ALRT 128 = unused (?)
+ * ALRT 129 = "Warning..."
+ * ALRT 130 = "Are you sure you want to quit without saving?"
+ *
+ * DITL 128 = body for DLOG 128
+ * DITL 129 = body for ALRT 129
+ * DITL 130 = body for ALRT 130
+ *
+ * ICON 128 = "warning" icon
+ *
+ * MENU 128 = apple (about, -, ...)
+ * MENU 129 = File (new, open, close, save, -, score, exit, quit)
+ * In T.o.M.E.
+ * MENU 129 = File (close, save, -, score, exit, quit)
+ * MENU 130 = Edit (undo, -, cut, copy, paste, clear)
+ * MENU 131 = Font (bold, wide, -)
+ * MENU 132 = Size ()
+ * MENU 133 = Windows ()
+ * MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle, Wizard)
+ * Graphics have following submenu attached:
+ * MENU 144 = Graphics (None, 8x8, 16x16)
+ * TileWidth and TileHeight submenus are filled in by this program
+ * MENU 145 = TileWidth ()
+ * MENU 146 = TileHeight ()
+ *
+ * PICT 1001 = Graphics tile set (8x8)
+ * PICT 1002 = Graphics tile set (16x16 images)
+ *
+ * Note: You can no longer use the exit menu unless you build the programme
+ * with an appropriate compile-time option.
+ *
+ *
+ * File name patterns:
+ * all 'APEX' files have a filename of the form "*:apex:*" (?)
+ * all 'BONE' files have a filename of the form "*:bone:*" (?)
+ * all 'DATA' files have a filename of the form "*:data:*"
+ * all 'SAVE' files have a filename of the form "*:save:*"
+ * all 'USER' files have a filename of the form "*:user:*" (?)
+ *
+ * Perhaps we should attempt to set the "_ftype" flag inside this file,
+ * to avoid nasty file type information being spread all through the
+ * rest of the code. (?) This might require adding hooks into the
+ * "fd_open()" and "my_fopen()" functions in "util.c". XXX XXX XXX
+ *
+ *
+ * Reasons for each header file:
+ *
+ * angband.h = Angband header file
+ *
+ * Types.h = (included anyway)
+ * Gestalt.h = gestalt code
+ * QuickDraw.h = (included anyway)
+ * OSUtils.h = (included anyway)
+ * Files.h = file code
+ * Fonts.h = font code
+ * Menus.h = menu code
+ * Dialogs.h = dialog code
+ * Windows.h = (included anyway)
+ * Palettes.h = palette code
+ * StandardFile.h = file dialog box
+ * DiskInit.h = disk initialization
+ * ToolUtils.h = HiWord() / LoWord()
+ * Desk.h = OpenDeskAcc()
+ * Devices.h = OpenDeskAcc()
+ * Events.h = event code
+ * Resources.h = resource code
+ * Controls.h = button code
+ * SegLoad.h = ExitToShell(), AppFile, etc
+ * Memory.h = SetApplLimit(), NewPtr(), etc
+ * QDOffscreen.h = GWorld code
+ * Sound.h = Sound code
+ *
+ * For backwards compatibility:
+ * Use GestaltEqu.h instead of Gestalt.h
+ * Add Desk.h to include simply includes Menus.h, Devices.h, Events.h
+ */
+
+
+#include "angband.h"
+
+
+#ifdef MACINTOSH
+
+/*
+ * Variant-dependent features:
+ *
+ * #define ALLOW_BIG_SCREEN (V, Ey, O, T.o.M.E. and Z.
+ * Dr's big screen needs more work. New S one needs some thought)
+ * #define ANG281_RESET_VISUALS (Cth, Gum, T.o.M.E., Z)
+ * #define SAVEFILE_SCREEN (T.o.M.E.)
+ * #define USE_DOUBLE_TILES ("bigtile" patch, V and T.o.M.E.
+ * T requires #define TOME in addition to this)
+ * #define ZANG_AUTO_SAVE (O and Z)
+ * #define HAS_SCORE_MENU (V and T.o.M.E.)
+ * #define ANGBAND_PREFERENCES "_your_variant_name_ Preferences"
+ * #define ANGBAND_CREATOR four letter code for your variant, if any.
+ * or use the default one.
+ *
+ * In [Z], please replace inkey_flag with p_ptr->inkey_flag as well.
+ */
+
+/* Some porting examples */
+#ifdef ANGBAND30X
+# define USE_DOUBLE_TILES
+# define ALLOW_BIG_SCREEN
+# define HAS_SCORE_MENU
+# define NEW_ZVIRT_HOOKS
+/* I can't ditch this, yet, because there are many variants */
+# define USE_TRANSPARENCY
+#endif /* ANGBAND30X */
+
+#ifdef TOME
+# define USE_DOUBLE_TILES
+# define SAVEFILE_SCREEN
+# define ANG281_RESET_VISUALS
+# define ALLOW_BIG_SCREEN
+# define HAS_SCORE_MENU
+# define ANGBAND_CREATOR 'PrnA'
+# define ANGBAND_PREFERENCES "T.o.M.E. Preferences"
+#endif /* TOME */
+
+/* Default creator signature */
+# ifndef ANGBAND_CREATOR
+# define ANGBAND_CREATOR 'A271'
+# endif
+
+/* Default preferences file name */
+# ifndef ANGBAND_PREFERENCES
+# define ANGBAND_PREFERENCES "Angband Preferences"
+# endif
+
+
+/*
+ * To cope with pref file related problems
+ *
+ * Please note that some variants don't set them to "real" version number
+ * and uses other defines for that purpose.
+ *
+ * This is *very* important for Classic ports, because wrong pref file formats
+ * can crash the system.
+ */
+#ifndef PREF_VER_MAJOR
+# define PREF_VER_MAJOR VERSION_MAJOR
+#endif
+#ifndef PREF_VER_MINOR
+# define PREF_VER_MINOR VERSION_MINOR
+#endif
+#ifndef PREF_VER_PATCH
+# define PREF_VER_PATCH VERSION_PATCH
+#endif
+#ifndef PREF_VER_EXTRA
+# define PREF_VER_EXTRA VERSION_EXTRA
+#endif
+
+
+#include <Types.h>
+#include <Gestalt.h>
+#include <QuickDraw.h>
+#include <Files.h>
+#include <Fonts.h>
+#include <Menus.h>
+#include <Dialogs.h>
+#include <Windows.h>
+#include <Palettes.h>
+#include <StandardFile.h>
+#include <DiskInit.h>
+#include <ToolUtils.h>
+#include <Devices.h>
+#include <Events.h>
+#include <Resources.h>
+#include <Controls.h>
+#include <SegLoad.h>
+#include <Memory.h>
+#include <QDOffscreen.h>
+#include <Sound.h>
+
+
+/*
+ * Use "malloc()" instead of "NewPtr()"
+ */
+/* #define USE_MALLOC */
+
+
+#if defined(powerc) || defined(__powerc)
+
+/*
+ * Disable "LITE" version
+ */
+# undef ANGBAND_LITE_MAC
+
+#endif
+
+
+#ifndef ANGBAND_LITE_MAC
+
+/*
+ * Activate some special code
+ */
+# define USE_SFL_CODE
+
+#endif /* ANGBAND_LITE_MAC */
+
+
+
+#ifdef USE_SFL_CODE
+
+/*
+ * Include the necessary header files
+ */
+#include <AppleEvents.h>
+#include <EPPC.h>
+#include <Folders.h>
+
+#endif
+
+
+#ifdef ANGBAND_LITE_MAC
+
+/*
+ * Everything in drawn as white on black
+ */
+
+#else /* ANGBAND_LITE_MAC */
+
+/*
+* Information about each of the 256 available colors
+*/
+static RGBColor color_info[256];
+
+#endif /* ANGBAND_LITE_MAC */
+
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+/*
+ * Extra "term" data
+ */
+struct term_data
+{
+ term *t;
+
+ Rect r;
+
+ WindowPtr w;
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Nothing */
+
+#else /* ANGBAND_LITE_MAC */
+
+ short padding;
+
+ short pixelDepth;
+
+ GWorldPtr theGWorld;
+
+ GDHandle theGDH;
+
+ GDHandle mainSWGDH;
+
+#endif /* ANGBAND_LITE_MAC */
+
+ Str15 title;
+
+ s16b oops;
+
+ s16b keys;
+
+ s16b last;
+
+ s16b mapped;
+
+ s16b rows;
+ s16b cols;
+
+ s16b font_id;
+ s16b font_size;
+ s16b font_face;
+ s16b font_mono;
+
+ s16b font_o_x;
+ s16b font_o_y;
+ s16b font_wid;
+ s16b font_hgt;
+
+ s16b tile_o_x;
+ s16b tile_o_y;
+ s16b tile_wid;
+ s16b tile_hgt;
+
+ s16b size_wid;
+ s16b size_hgt;
+
+ s16b size_ow1;
+ s16b size_oh1;
+ s16b size_ow2;
+ s16b size_oh2;
+};
+
+
+
+
+#ifdef MAC_MPW
+
+/*
+ * MPW 68K compiler cannot process ToME's variable.c correctly...
+ * but support for it is here, for your reference. I have tried
+ * this with SC and MrC to compile Vanilla successfully.
+ */
+QDGlobals qd;
+
+/*
+ * File type assigner - declare them in externs.h as well.
+ *
+ * You still have to call
+ * fsetfileinfo(buf, _fcreator, _ftype);
+ * in fd_make() and my_fopen() to assign creator and type
+ * to a file.
+ */
+u32b _ftype;
+u32b _fcreator;
+
+/*
+ * Since MPW's C library doesn't have stat or fstat, you have to
+ * disable CHECK_MODIFICATION_TIME in config.h
+ *
+ * Another source code change required for MPW compilation is
+ * to #define MACINTOSH and #undef __STDC__
+ * This can be done conveniently in h-system.h
+ *
+ * You may have to cast some pointers to non-offending types e.g. (void *).
+ * This typically occurs when passing a const pointer to a library
+ * function whose prototype doesn't have const in corresponding
+ * parameter.
+ */
+
+#endif /* MAC_MPW */
+
+
+/*
+ * Forward declare -- see below
+ */
+static bool CheckEvents(bool wait);
+
+
+/*
+ * Hack -- location of the main directory
+ */
+static short app_vol;
+static long app_dir;
+
+
+/*
+ * Delay handling of double-clicked savefiles
+ */
+Boolean open_when_ready = FALSE;
+
+/*
+ * Delay handling of pre-emptive "quit" event
+ */
+Boolean quit_when_ready = FALSE;
+
+
+/*
+ * Hack -- game in progress
+ */
+static int game_in_progress = 0;
+
+
+/*
+ * Only do "SetPort()" when needed
+ */
+static WindowPtr active = NULL;
+
+
+/*
+ * Maximum number of terms
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+
+/*
+ * Note when "open"/"new" become valid
+ */
+static bool initialized = FALSE;
+
+
+
+#ifdef ALLOW_NO_SAVE_QUITS
+
+/*
+ * CodeWarrior uses Universal Procedure Pointers
+ */
+static ModalFilterUPP ynfilterUPP;
+
+#endif /* ALLOW_NO_SAVE_QUITS */
+
+
+
+#ifdef USE_SFL_CODE
+
+/*
+ * Apple Event Hooks
+ */
+AEEventHandlerUPP AEH_Start_UPP;
+AEEventHandlerUPP AEH_Quit_UPP;
+AEEventHandlerUPP AEH_Print_UPP;
+AEEventHandlerUPP AEH_Open_UPP;
+
+#endif
+
+
+
+/*
+ * Convert a C string to a pascal string in place
+ *
+ * This function may be defined elsewhere, but since it is so
+ * small, it is not worth finding the proper function name for
+ * all the different platforms.
+ */
+static void ctopstr(StringPtr src)
+{
+ int i;
+ byte len;
+
+ /* Hack -- pointer */
+ char *s = (char*)(src);
+
+ len = strlen(s);
+
+ /* Hack -- convert the string */
+ for (i = len; i > 1; i--) s[i] = s[i - 1];
+
+ /* Hack -- terminate the string */
+ s[0] = len;
+}
+
+
+/*
+ * Convert refnum+vrefnum+fname into a full file name
+ * Store this filename in 'buf' (make sure it is long enough)
+ * Note that 'fname' looks to be a "pascal" string
+ */
+static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname)
+{
+ DirInfo pb;
+ Str255 name;
+ int err;
+ int i, j;
+
+ char res[1000];
+
+ i = 999;
+
+ res[i] = 0;
+ i--;
+ for (j = 1; j <= fname[0]; j++)
+ {
+ res[i - fname[0] + j] = fname[j];
+ }
+ i -= fname[0];
+
+ pb.ioCompletion = NULL;
+ pb.ioNamePtr = name;
+ pb.ioVRefNum = vrefnum;
+ pb.ioDrParID = refnum;
+ pb.ioFDirIndex = -1;
+
+ while (1)
+ {
+ pb.ioDrDirID = pb.ioDrParID;
+ err = PBGetCatInfoSync((CInfoPBPtr) & pb);
+ res[i] = ':';
+ i--;
+ for (j = 1; j <= name[0]; j++)
+ {
+ res[i - name[0] + j] = name[j];
+ }
+ i -= name[0];
+
+ if (pb.ioDrDirID == fsRtDirID) break;
+ }
+
+ /* Extract the result */
+ for (j = 0, i++; res[i]; j++, i++) buf[j] = res[i];
+ buf[j] = 0;
+}
+
+
+#if 0
+
+/*
+ * XXX XXX XXX Allow the system to ask us for a filename
+ */
+static bool askfor_file(char *buf, int len)
+{
+ SFReply reply;
+ Str255 dflt;
+ Point topleft;
+ short vrefnum;
+ long drefnum, junk;
+
+ /* Default file name */
+ strnfmt((char*)dflt + 1, 255, "%s's description", buf);
+ dflt[0] = strlen((char*)dflt + 1);
+
+ /* Ask for a file name */
+ topleft.h = (qd.screenBits.bounds.left + qd.screenBits.bounds.right) / 2 - 344 / 2;
+ topleft.v = (2 * qd.screenBits.bounds.top + qd.screenBits.bounds.bottom) / 3 - 188 / 2;
+ SFPutFile(topleft, "\pSelect a filename:", dflt, NULL, &reply);
+ /* StandardPutFile("\pSelect a filename:", dflt, &reply); */
+
+ /* Process */
+ if (reply.good)
+ {
+ int fc;
+
+ /* Get info */
+ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk);
+
+ /* Extract the name */
+ refnum_to_name(buf, drefnum, vrefnum, (char*)reply.fName);
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+#endif
+
+
+
+/*
+ * Center a rectangle inside another rectangle
+ */
+static void center_rect(Rect *r, Rect *s)
+{
+ int centerx = (s->left + s->right) / 2;
+ int centery = (2 * s->top + s->bottom) / 3;
+ int dx = centerx - (r->right - r->left) / 2 - r->left;
+ int dy = centery - (r->bottom - r->top) / 2 - r->top;
+ r->left += dx;
+ r->right += dx;
+ r->top += dy;
+ r->bottom += dy;
+}
+
+
+/*
+ * Convert a pascal string in place
+ *
+ * This function may be defined elsewhere, but since it is so
+ * small, it is not worth finding the proper function name for
+ * all the different platforms.
+ */
+static void ptocstr(StringPtr src)
+{
+ int i;
+
+ /* Hack -- pointer */
+ char *s = (char*)(src);
+
+ /* Hack -- convert the string */
+ for (i = s[0]; i; i--, s++) s[0] = s[1];
+
+ /* Hack -- terminate the string */
+ s[0] = '\0';
+}
+
+
+#if defined(USE_SFL_CODE)
+
+
+/*
+ * The following three routines (pstrcat, pstrinsert, and PathNameFromDirID)
+ * were taken from the Think Reference section called "Getting a Full Pathname"
+ * (under the File Manager section). We need PathNameFromDirID to get the
+ * full pathname of the opened savefile, making no assumptions about where it
+ * is.
+ *
+ * I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully
+ * nice.
+ */
+static void pstrcat(StringPtr dst, StringPtr src)
+{
+ /* copy string in */
+ BlockMove(src + 1, dst + *dst + 1, *src);
+
+ /* adjust length byte */
+ *dst += *src;
+}
+
+/*
+ * pstrinsert - insert string 'src' at beginning of string 'dst'
+ */
+static void pstrinsert(StringPtr dst, StringPtr src)
+{
+ /* make room for new string */
+ BlockMove(dst + 1, dst + *src + 1, *dst);
+
+ /* copy new string in */
+ BlockMove(src + 1, dst + 1, *src);
+
+ /* adjust length byte */
+ *dst += *src;
+}
+
+static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName)
+{
+ CInfoPBRec block;
+ Str255 directoryName;
+ OSErr err;
+
+ fullPathName[0] = '\0';
+
+ block.dirInfo.ioDrParID = dirID;
+ block.dirInfo.ioNamePtr = directoryName;
+
+ while (1)
+ {
+ block.dirInfo.ioVRefNum = vRefNum;
+ block.dirInfo.ioFDirIndex = -1;
+ block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID;
+ err = PBGetCatInfoSync(&block);
+ pstrcat(directoryName, (StringPtr)"\p:");
+ pstrinsert(fullPathName, directoryName);
+ if (block.dirInfo.ioDrDirID == 2) break;
+ }
+}
+
+#endif
+
+
+
+/*
+ * Activate a given window, if necessary
+ */
+static void activate(WindowPtr w)
+{
+ /* Activate */
+ if (active != w)
+ {
+ /* Activate */
+ if (w) SetPort(w);
+
+ /* Remember */
+ active = w;
+ }
+}
+
+
+/*
+ * Display a warning message
+ */
+static void mac_warning(cptr warning)
+{
+ Str255 text;
+ int len, i;
+
+ /* Limit of 250 chars */
+ len = strlen(warning);
+ if (len > 250) len = 250;
+
+ /* Make a "Pascal" string */
+ text[0] = len;
+ for (i = 0; i < len; i++) text[i + 1] = warning[i];
+
+ /* Prepare the dialog box values */
+ ParamText(text, "\p", "\p", "\p");
+
+ /* Display the Alert, wait for Okay */
+ Alert(129, 0L);
+}
+
+
+
+/*** Some generic functions ***/
+
+
+#ifdef ANGBAND_LITE_MAC
+
+/*
+ * Hack -- activate a color (0 to 255)
+ */
+#define term_data_color(TD,A) /* Nothing */
+
+#else /* ANGBAND_LITE_MAC */
+
+/*
+* Hack -- activate a color (0 to 255)
+*/
+static void term_data_color(term_data *td, int a)
+{
+ /* Activate the color */
+ if (td->last != a)
+ {
+ /* Activate the color */
+ RGBForeColor(&color_info[a]);
+
+ /* Memorize color */
+ td->last = a;
+ }
+}
+
+#endif /* ANGBAND_LITE_MAC */
+
+
+/*
+ * Hack -- Apply and Verify the "font" info
+ *
+ * This should usually be followed by "term_data_check_size()"
+ *
+ * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt
+ * you have to reset them to zero before this function is called.
+ * XXX XXX This is automatic when the program starts because the term_data
+ * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e.
+ * font, font style and size changes.
+ */
+static void term_data_check_font(term_data *td)
+{
+ int i;
+
+ FontInfo info;
+
+ WindowPtr old = active;
+
+
+ /* Activate */
+ activate(td->w);
+
+ /* Instantiate font */
+ TextFont(td->font_id);
+ TextSize(td->font_size);
+ TextFace(td->font_face);
+
+ /* Extract the font info */
+ GetFontInfo(&info);
+
+ /* Assume monospaced */
+ td->font_mono = TRUE;
+
+ /* Extract the font sizing values XXX XXX XXX */
+ td->font_wid = CharWidth('@'); /* info.widMax; */
+ td->font_hgt = info.ascent + info.descent;
+ td->font_o_x = 0;
+ td->font_o_y = info.ascent;
+
+ /* Check important characters */
+ for (i = 33; i < 127; i++)
+ {
+ /* Hack -- notice non-mono-space */
+ if (td->font_wid != CharWidth(i)) td->font_mono = FALSE;
+
+ /* Hack -- collect largest width */
+ if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i);
+ }
+
+ /* Set default offsets */
+ td->tile_o_x = td->font_o_x;
+ td->tile_o_y = td->font_o_y;
+
+ /* Set default tile size */
+ if (td->tile_wid == 0) td->tile_wid = td->font_wid;
+ if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt;
+
+ /* Re-activate the old window */
+ activate(old);
+}
+
+
+/*
+ * Hack -- Apply and Verify the "size" info
+ */
+static void term_data_check_size(term_data *td)
+{
+ /* Minimal window size for the Angband window */
+ if (td == &data[0])
+ {
+#ifdef ALLOW_BIG_SCREEN
+
+ /* Enforce minimal size */
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+
+#else
+
+ /* Enforce the traditional size */
+ if (td->cols != 80) td->cols = 80;
+ if (td->rows != 24) td->rows = 24;
+
+#endif /* ALLOW_BIG_SCREEN */
+ }
+
+ /* Allow small windows for the rest */
+ else
+ {
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Enforce maximal sizes */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+
+ /* Minimal tile size */
+ if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid;
+ if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt;
+
+ /* Default tile offsets */
+ td->tile_o_x = (td->tile_wid - td->font_wid) / 2;
+ td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2;
+
+ /* Minimal tile offsets */
+ if (td->tile_o_x < 0) td->tile_o_x = 0;
+ if (td->tile_o_y < 0) td->tile_o_y = 0;
+
+ /* Apply font offsets */
+ td->tile_o_x += td->font_o_x;
+ td->tile_o_y += td->font_o_y;
+
+ /* Calculate full window size */
+ td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
+ td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Verify the top */
+ if (td->r.top > qd.screenBits.bounds.bottom - td->size_hgt)
+ {
+ td->r.top = qd.screenBits.bounds.bottom - td->size_hgt;
+ }
+
+ /* Verify the top */
+ if (td->r.top < qd.screenBits.bounds.top + 30)
+ {
+ td->r.top = qd.screenBits.bounds.top + 30;
+ }
+
+ /* Verify the left */
+ if (td->r.left > qd.screenBits.bounds.right - td->size_wid)
+ {
+ td->r.left = qd.screenBits.bounds.right - td->size_wid;
+ }
+
+ /* Verify the left */
+ if (td->r.left < qd.screenBits.bounds.left)
+ {
+ td->r.left = qd.screenBits.bounds.left;
+ }
+
+ /* Calculate bottom right corner */
+ td->r.right = td->r.left + td->size_wid;
+ td->r.bottom = td->r.top + td->size_hgt;
+
+ /* Assume no graphics */
+ td->t->higher_pict = FALSE;
+ td->t->always_pict = FALSE;
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* No graphics */
+
+#else /* ANGBAND_LITE_MAC */
+
+ /* Handle graphics */
+ if (use_graphics)
+ {
+ /* Use higher_pict whenever possible */
+ if (td->font_mono) td->t->higher_pict = TRUE;
+
+ /* Use always_pict only when necessary */
+ else td->t->always_pict = TRUE;
+ }
+
+#endif /* ANGBAND_LITE_MAC */
+
+ /* Fake mono-space */
+ if (!td->font_mono ||
+ (td->font_wid != td->tile_wid) ||
+ (td->font_hgt != td->tile_hgt))
+ {
+ /* Handle fake monospace -- this is SLOW */
+ if (td->t->higher_pict) td->t->higher_pict = FALSE;
+ td->t->always_pict = TRUE;
+ }
+}
+
+
+/*
+ * Hack -- resize a term_data
+ *
+ * This should normally be followed by "term_data_resize()"
+ */
+static void term_data_resize(term_data *td)
+{
+ /* Actually resize the window */
+ SizeWindow(td->w, td->size_wid, td->size_hgt, 0);
+}
+
+
+
+/*
+ * Hack -- redraw a term_data
+ *
+ * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR"
+ */
+static void term_data_redraw(term_data *td)
+{
+ term *old = Term;
+
+ /* Activate the term */
+ Term_activate(td->t);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Flush the output */
+ Term_fresh();
+
+ /* Restore the old term */
+ Term_activate(old);
+
+ /* No need to redraw */
+ ValidRect(&td->w->portRect);
+}
+
+
+
+
+#ifdef ANGBAND_LITE_MAC
+
+/* No graphics */
+
+#else /* ANGBAND_LITE_MAC */
+
+
+/*
+* Graphics support
+*/
+
+/* Set by Term_xtra_mac_react */
+static int pictID; /* PICT id of image tiles */
+
+static int grafWidth; /* Width of a tile in pixels */
+static int grafHeight; /* Height of a tile in pixels */
+
+/* Calculated by PICT loading code */
+static int pictCols; /* Number of columns in tiles */
+static int pictRows; /* Number of rows in tiles */
+
+/* Available graphics modes */
+#define GRAF_MODE_NONE 0 /* plain ASCII */
+#define GRAF_MODE_8X8 1 /* 8x8 tiles, no transparency effect */
+#define GRAF_MODE_16X16 2 /* 16x16 tiles, transparency effect */
+/* This doesn't work... */
+#define GRAF_MODE_32X32 3 /* 32x32 tiles, transparency effect */
+
+static int graf_mode = GRAF_MODE_NONE; /* current graphics mode */
+static int graf_mode_req = GRAF_MODE_NONE; /* requested graphics mode */
+
+#define TR_NONE 0 /* No transparency effect */
+#define TR_OVER 1 /* Overwriting with transparent black pixels */
+static int transparency_mode = TR_NONE;
+
+
+/*
+* Forward Declare
+*/
+typedef struct FrameRec FrameRec;
+
+/*
+* Frame
+*
+* - GWorld for the frame image
+* - Handle to pix map (saved for unlocking/locking)
+* - Pointer to color pix map (valid only while locked)
+*/
+struct FrameRec
+{
+ GWorldPtr framePort;
+ PixMapHandle framePixHndl;
+ PixMapPtr framePix;
+};
+
+
+/*
+* The global picture data
+*/
+static FrameRec *frameP = NULL;
+
+
+/*
+* Lock a frame
+*/
+static void BenSWLockFrame(FrameRec *srcFrameP)
+{
+ PixMapHandle pixMapH;
+
+ pixMapH = GetGWorldPixMap(srcFrameP->framePort);
+ (void)LockPixels(pixMapH);
+ HLockHi((Handle)pixMapH);
+ srcFrameP->framePixHndl = pixMapH;
+ srcFrameP->framePix = (PixMapPtr)StripAddress(*(Handle)pixMapH);
+}
+
+
+/*
+* Unlock a frame
+*/
+static void BenSWUnlockFrame(FrameRec *srcFrameP)
+{
+ if (srcFrameP->framePort != NULL)
+ {
+ HUnlock((Handle)srcFrameP->framePixHndl);
+ UnlockPixels(srcFrameP->framePixHndl);
+ }
+
+ srcFrameP->framePix = NULL;
+}
+
+
+
+static OSErr BenSWCreateGWorldFromPict(
+ GWorldPtr *pictGWorld, PicHandle pictH)
+{
+ OSErr err;
+ GWorldPtr saveGWorld;
+ GDHandle saveGDevice;
+ GWorldPtr tempGWorld;
+ Rect pictRect;
+ short depth;
+ GDHandle theGDH;
+
+ tempGWorld = NULL;
+
+ /* Reset */
+ *pictGWorld = NULL;
+
+ /* Get depth */
+ depth = data[0].pixelDepth;
+
+ /* Get GDH */
+ theGDH = data[0].theGDH;
+
+ /* Obtain size rectangle */
+ pictRect = (**pictH).picFrame;
+ OffsetRect(&pictRect, -pictRect.left, -pictRect.top);
+
+ /* Calculate and set numbers of rows and columns */
+ pictRows = pictRect.bottom / grafHeight;
+ pictCols = pictRect.right / grafWidth;
+
+ /* Create a GWorld */
+ err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice);
+
+ /* Error */
+ if (err != noErr) return (err);
+
+ /* Save pointer */
+ *pictGWorld = tempGWorld;
+
+ /* Save GWorld */
+ GetGWorld(&saveGWorld, &saveGDevice);
+
+ /* Activate */
+ SetGWorld(tempGWorld, nil);
+
+ /* Dump the pict into the GWorld */
+ (void)LockPixels(GetGWorldPixMap(tempGWorld));
+ EraseRect(&pictRect);
+ DrawPicture(pictH, &pictRect);
+ UnlockPixels(GetGWorldPixMap(tempGWorld));
+
+ /* Restore GWorld */
+ SetGWorld(saveGWorld, saveGDevice);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Init the global "frameP"
+*/
+static OSErr globe_init(void)
+{
+ OSErr err;
+
+ GWorldPtr tempPictGWorldP;
+
+ PicHandle newPictH;
+
+
+ /* Use window XXX XXX XXX */
+ SetPort(data[0].w);
+
+
+ /* Get the pict resource */
+ newPictH = GetPicture(pictID);
+
+ /* Error */
+ if (newPictH == NULL) return ( -1);
+
+ /* Create GWorld */
+ err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH);
+
+ /* Release resource */
+ ReleaseResource((Handle)newPictH);
+
+ /* Error */
+ if (err != noErr) return (err);
+
+ /* Create the frame */
+ frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec));
+
+ /* Error */
+ if (frameP == NULL) return ( -1);
+
+ /* Save GWorld */
+ frameP->framePort = tempPictGWorldP;
+
+ /* Lock it */
+ BenSWLockFrame(frameP);
+
+ /* Success */
+ return (noErr);
+}
+
+
+/*
+* Nuke the global "frameP"
+*/
+static errr globe_nuke(void)
+{
+ /* Dispose */
+ if (frameP)
+ {
+ /* Unlock */
+ BenSWUnlockFrame(frameP);
+
+ /* Dispose of the GWorld */
+ DisposeGWorld(frameP->framePort);
+
+ /* Dispose of the memory */
+ DisposePtr((Ptr)frameP);
+
+ /* Forget */
+ frameP = NULL;
+ }
+
+ /* Flush events */
+ FlushEvents(everyEvent, 0);
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* ANGBAND_LITE_MAC */
+
+
+
+
+#ifdef USE_ASYNC_SOUND
+
+/*
+ * Asynchronous sound player - completely revised (beta)
+ */
+
+/*
+ * Number of channels in the channel pool
+ */
+#define MAX_CHANNELS 4
+
+/*
+ * A pool of sound channels
+ */
+static SndChannelPtr channels[MAX_CHANNELS];
+
+/*
+ * Status of the channel pool
+ */
+static Boolean channel_initialised = FALSE;
+
+/*
+ * Data handles containing sound samples
+ */
+static SndListHandle samples[SOUND_MAX];
+
+/*
+ * Reference counts of sound samples
+ */
+static SInt16 sample_refs[SOUND_MAX];
+
+#define SOUND_VOLUME_MIN 0 /* Default minimum sound volume */
+#define SOUND_VOLUME_MAX 255 /* Default maximum sound volume */
+#define VOLUME_MIN 0 /* Minimum sound volume in % */
+#define VOLUME_MAX 100 /* Maximum sound volume in % */
+#define VOLUME_INC 5 /* Increment sound volume in % */
+
+/* I'm just too lazy to write a panel for this XXX XXX */
+static int sound_volume = SOUND_VOLUME_MAX;
+
+/*
+ * Return a handle of 'snd ' resource given Angband sound event number,
+ * or NULL if it isn't found.
+ *
+ * Globals referenced: angband_sound_name[] (variable.c)
+ */
+static SndListHandle find_sound(int num)
+{
+ Str255 sound;
+
+ /* Get the proper sound name */
+ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
+ sound[0] = strlen((char*)sound + 1);
+
+ /* Obtain resource XXX XXX XXX */
+ return ((SndListHandle)GetNamedResource('snd ', sound));
+}
+
+
+/*
+ * Clean up sound support - to be called when the game exits.
+ *
+ * Globals referenced: channels[], samples[], sample_refs[].
+ */
+static void cleanup_sound(void)
+{
+ int i;
+
+ /* No need to clean it up */
+ if (!channel_initialised) return;
+
+ /* Dispose channels */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Drain sound commands and free the channel */
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Free sound data */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Still locked */
+ if ((sample_refs[i] > 0) && (samples[i] != NULL))
+ {
+ /* Unlock it */
+ HUnlock((Handle)samples[i]);
+ }
+
+ /* Release it */
+ if (samples[i]) ReleaseResource((Handle)samples[i]);
+ }
+}
+
+
+/*
+ * Play sound effects asynchronously -- pelpel
+ *
+ * I don't believe those who first started using the previous implementations
+ * imagined this is *much* more complicated as it may seem. Anyway,
+ * introduced round-robin scheduling of channels and made it much more
+ * paranoid about HLock/HUnlock.
+ *
+ * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done
+ * using channel's callback procedures, which set global flags, and
+ * a procedure hooked into CheckEvents does housekeeping. On the other
+ * hand, this lazy reclaiming strategy keeps things simple (no interrupt
+ * time code) and provides a sort of cache for sound data.
+ *
+ * Globals referenced: channel_initialised, channels[], samples[],
+ * sample_refs[].
+ * Globals updated: ditto.
+ */
+static void play_sound(int num, int vol)
+{
+ OSErr err;
+ int i;
+ int prev_num;
+ SndListHandle h;
+ SndChannelPtr chan;
+ SCStatus status;
+
+ static int next_chan;
+ static SInt16 channel_occupants[MAX_CHANNELS];
+ static SndCommand volume_cmd, quiet_cmd;
+
+
+ /* Initialise sound channels */
+ if (!channel_initialised)
+ {
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Paranoia - Clear occupant table */
+ /* channel_occupants[i] = 0; */
+
+ /* Create sound channel for all sounds to play from */
+ err = SndNewChannel(&channels[i], sampledSynth, initMono, 0L);
+
+ /* Error */
+ if (err != noErr)
+ {
+ /* Free channels */
+ while (--i >= 0)
+ {
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Notify error */
+ plog("Cannot initialise sound channels!");
+
+ /* Cancel request */
+ use_sound = arg_sound = FALSE;
+
+ /* Failure */
+ return;
+ }
+ }
+
+ /* First channel to use */
+ next_chan = 0;
+
+ /* Prepare volume command */
+ volume_cmd.cmd = volumeCmd;
+ volume_cmd.param1 = 0;
+ volume_cmd.param2 = 0;
+
+ /* Prepare quiet command */
+ quiet_cmd.cmd = quietCmd;
+ quiet_cmd.param1 = 0;
+ quiet_cmd.param2 = 0;
+
+ /* Initialisation complete */
+ channel_initialised = TRUE;
+ }
+
+ /* Paranoia */
+ if ((num <= 0) || (num >= SOUND_MAX)) return;
+
+ /* Prepare volume command */
+ volume_cmd.param2 = (SInt16)((vol << 4) | vol);
+
+ /* Channel to use (round robin) */
+ chan = channels[next_chan];
+
+ /* See if the resource is already in use */
+ if (sample_refs[num] > 0)
+ {
+ /* Resource in use */
+ h = samples[num];
+
+ /* Increase the refcount */
+ sample_refs[num]++;
+ }
+
+ /* Sound is not currently in use */
+ else
+ {
+ /* Get handle for the sound */
+ h = find_sound(num);
+
+ /* Sample not available */
+ if (h == NULL) return;
+
+ /* Load resource */
+ LoadResource((Handle)h);
+
+ /* Lock the handle */
+ HLock((Handle)h);
+
+ /* Remember it */
+ samples[num] = h;
+
+ /* Initialise refcount */
+ sample_refs[num] = 1;
+ }
+
+ /* Poll the channel */
+ err = SndChannelStatus(chan, sizeof(SCStatus), &status);
+
+ /* It isn't available */
+ if ((err != noErr) || status.scChannelBusy)
+ {
+ /* Shut it down */
+ SndDoImmediate(chan, &quiet_cmd);
+ }
+
+ /* Previously played sound on this channel */
+ prev_num = channel_occupants[next_chan];
+
+ /* Process previously played sound */
+ if (prev_num != 0)
+ {
+ /* Decrease refcount */
+ sample_refs[prev_num]--;
+
+ /* We can free it now */
+ if (sample_refs[prev_num] <= 0)
+ {
+ /* Unlock */
+ HUnlock((Handle)samples[prev_num]);
+
+ /* Release */
+ ReleaseResource((Handle)samples[prev_num]);
+
+ /* Forget handle */
+ samples[prev_num] = NULL;
+
+ /* Paranoia */
+ sample_refs[prev_num] = 0;
+ }
+ }
+
+ /* Remember this sound as the current occupant of the channel */
+ channel_occupants[next_chan] = num;
+
+ /* Set up volume for channel */
+ SndDoImmediate(chan, &volume_cmd);
+
+ /* Play new sound asynchronously */
+ SndPlay(chan, h, TRUE);
+
+ /* Schedule next channel (round robin) */
+ next_chan++;
+ if (next_chan >= MAX_CHANNELS) next_chan = 0;
+}
+
+#endif /* USE_ASYNC_SOUND */
+
+
+/*** Support for the "z-term.c" package ***/
+
+
+/*
+ * Initialize a new Term
+ *
+ * Note also the "window type" called "noGrowDocProc", which might be more
+ * appropriate for the main "screen" window.
+ *
+ * Note the use of "srcCopy" mode for optimized screen writes.
+ */
+static void Term_init_mac(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+
+ static RGBColor black = {0x0000, 0x0000, 0x0000};
+ static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Make the window */
+ td->w = NewWindow(0, &td->r, td->title, 0, noGrowDocProc, (WindowPtr) - 1, 1, 0L);
+
+#else /* ANGBAND_LITE_MAC */
+
+ /* Make the window */
+ td->w = NewCWindow(0, &td->r, td->title, 0, documentProc, (WindowPtr) - 1, 1, 0L);
+
+#endif /* ANGBAND_LITE_MAC */
+
+ /* Activate the window */
+ activate(td->w);
+
+ /* Erase behind words */
+ TextMode(srcCopy);
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize the window */
+ term_data_resize(td);
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Prepare the colors (base colors) */
+ BackColor(blackColor);
+ ForeColor(whiteColor);
+
+#else /* ANGBAND_LITE_MAC */
+
+ /* Prepare the colors (real colors) */
+ RGBBackColor(&black);
+ RGBForeColor(&white);
+
+ /* Block */
+ {
+ Rect tempRect;
+ Rect globalRect;
+ GDHandle mainGDH;
+ GDHandle currentGDH;
+ GWorldPtr windowGWorld;
+ PixMapHandle basePixMap;
+
+ /* Obtain the rect */
+ tempRect = td->w->portRect;
+
+ /* Obtain the global rect */
+ globalRect = tempRect;
+ LocalToGlobal((Point*)&globalRect.top);
+ LocalToGlobal((Point*)&globalRect.bottom);
+
+ /* Obtain the proper GDH */
+ mainGDH = GetMaxDevice(&globalRect);
+
+ /* Extract GWorld and GDH */
+ GetGWorld(&windowGWorld, &currentGDH);
+
+ /* Obtain base pixmap */
+ basePixMap = (**mainGDH).gdPMap;
+
+ /* Save pixel depth */
+ td->pixelDepth = (**basePixMap).pixelSize;
+
+ /* Save Window GWorld */
+ td->theGWorld = windowGWorld;
+
+ /* Save Window GDH */
+ td->theGDH = currentGDH;
+
+ /* Save main GDH */
+ td->mainSWGDH = mainGDH;
+ }
+
+#endif /* ANGBAND_LITE_MAC */
+
+ /* Clip to the window */
+ ClipRect(&td->w->portRect);
+
+ /* Erase the window */
+ EraseRect(&td->w->portRect);
+
+ /* Invalidate the window */
+ InvalRect(&td->w->portRect);
+
+ /* Display the window if needed */
+ if (td->mapped) ShowWindow(td->w);
+
+ /* Hack -- set "mapped" flag */
+ t->mapped_flag = td->mapped;
+
+ /* Forget color */
+ td->last = -1;
+}
+
+
+
+/*
+ * Nuke an old Term
+ */
+static void Term_nuke_mac(term *t)
+{
+
+#pragma unused (t)
+
+ /* XXX */
+}
+
+
+
+/*
+ * Unused
+ */
+static errr Term_user_mac(int n)
+{
+
+#pragma unused (n)
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_mac_react(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ int i;
+
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Nothing */
+
+#else /* ANGBAND_LITE_MAC */
+
+ /* Reset color */
+ td->last = -1;
+
+ /* Update colors */
+ for (i = 0; i < 256; i++)
+ {
+ u16b rv, gv, bv;
+
+ /* Extract the R,G,B data */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Save the actual color */
+ color_info[i].red = (rv | (rv << 8));
+ color_info[i].green = (gv | (gv << 8));
+ color_info[i].blue = (bv | (bv << 8));
+ }
+
+ /* Handle sound */
+ if (use_sound != arg_sound)
+ {
+ /* Apply request */
+ use_sound = arg_sound;
+ }
+
+ /* Handle graphics */
+ if (graf_mode_req != graf_mode)
+ {
+ /* dispose old GWorld's if present */
+ globe_nuke();
+
+ /* Setup parameters according to request */
+ switch (graf_mode_req)
+ {
+ /* ASCII - no graphics whatsoever */
+ case GRAF_MODE_NONE:
+ {
+#ifndef ZANGBAND
+ use_graphics = arg_graphics = FALSE;
+#else
+use_graphics = arg_graphics = GRAPHICS_NONE;
+#endif /* !ZANGBAND */
+ transparency_mode = TR_NONE;
+ break;
+ }
+
+ /*
+ * 8x8 tiles (PICT id 1001)
+ * no transparency effect
+ * "old" graphics definitions
+ */
+ case GRAF_MODE_8X8:
+ {
+#ifndef ZANGBAND
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "old";
+#else
+ use_graphics = arg_graphics = GRAPHICS_ORIGINAL;
+#endif /* !ZANGBAND */
+ pictID = 1001;
+ transparency_mode = TR_NONE;
+ grafWidth = grafHeight = 8;
+ break;
+ }
+
+ /*
+ * 16x16 tiles (images: PICT id 1002)
+ * with transparency effect
+ * "new" graphics definitions
+ */
+ case GRAF_MODE_16X16:
+ {
+#ifndef ZANGBAND
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "new";
+#else
+ use_graphics = arg_graphics = GRAPHICS_ADAM_BOLT;
+#endif /* !ZANGBAND */
+ pictID = 1002;
+ transparency_mode = TR_OVER;
+ grafWidth = grafHeight = 16;
+ break;
+ }
+
+#if 0 /* Good Lord! It doesn't work... Is it too large? */
+
+ /*
+ * 32x32 tiles (images: PICT id 1004)
+ * no transparency effect
+ * "david" graphics definitions
+ */
+ case GRAF_MODE_32X32:
+ {
+ use_graphics = arg_graphics = TRUE;
+ ANGBAND_GRAF = "david";
+ pictID = 1004;
+ transparency_mode = TR_OVER;
+ grafWidth = grafHeight = 32;
+ break;
+ }
+
+#endif /* sigh */
+ }
+
+ /* load tiles and setup GWorlds if tiles are requested */
+ if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0))
+ {
+ /* Oops */
+ plog("Cannot initialize graphics!");
+
+ /* reject request */
+ graf_mode_req = GRAF_MODE_NONE;
+
+ /* reset graphics flags */
+ use_graphics = arg_graphics = FALSE;
+
+ /* reset transparency mode */
+ transparency_mode = TR_NONE;
+ }
+
+ /* update current graphics mode */
+ graf_mode = graf_mode_req;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize the window */
+ term_data_resize(td);
+
+ /* Reset visuals */
+#ifndef ANG281_RESET_VISUALS
+ reset_visuals(TRUE);
+#else
+reset_visuals();
+#endif /* !ANG281_RESET_VISUALS */
+ }
+
+#endif /* ANGBAND_LITE_MAC */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Do a "special thing"
+ */
+static errr Term_xtra_mac(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ Rect r;
+
+ /* Analyze */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ {
+ /* Make a noise */
+ SysBeep(1);
+
+ /* Success */
+ return (0);
+ }
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Nothing */
+
+#else /* ANGBAND_LITE_MAC */
+
+ /* Make a sound */
+ case TERM_XTRA_SOUND:
+ {
+#ifndef USE_ASYNC_SOUND
+
+ /*
+ * This may not be your choice, but much safer and much less
+ * resource hungry. Existing implementations can quite easily
+ * crash, by starting asynchronous playing and immediately
+ * unlocking and releasing the sound data just started playing...
+ * -- pelpel
+ */
+ Handle handle;
+ Str255 sound;
+
+ /* Get the proper sound name */
+ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[v]);
+ sound[0] = strlen((char*)sound + 1);
+
+ /* Obtain resource XXX XXX XXX */
+ handle = GetNamedResource('snd ', sound);
+
+ /* Oops -- it is a failure, but we return 0 anyway */
+ if (handle == NULL) return (0);
+
+ /* Load and Lock */
+ LoadResource(handle);
+ HLock(handle);
+
+ /* Play sound (wait for completion) */
+ SndPlay(nil, (SndListHandle)handle, false);
+
+ /* Unlock and release */
+ HUnlock(handle);
+ ReleaseResource(handle);
+
+#else /* !USE_ASYNC_SOUND */
+
+ /* If you really want async player, please try this one */
+ play_sound(v, sound_volume);
+
+#endif /* !USE_ASYNC_SOUND */
+
+ /* Success */
+ return (0);
+ }
+
+#endif /* ANGBAND_LITE_MAC */
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ /* Process an event */
+ (void)CheckEvents(FALSE);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Process pending events */
+ case TERM_XTRA_EVENT:
+ {
+ /* Process an event */
+ (void)CheckEvents(v);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Flush all pending events (if any) */
+ case TERM_XTRA_FLUSH:
+ {
+ /* Hack -- flush all events */
+ while (CheckEvents(TRUE)) /* loop */;
+
+ /* Success */
+ return (0);
+ }
+
+ /* Hack -- Change the "soft level" */
+ case TERM_XTRA_LEVEL:
+ {
+ /* Activate if requested */
+ if (v) activate(td->w);
+
+ /* Success */
+ return (0);
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ /* No clipping XXX XXX XXX */
+ ClipRect(&td->w->portRect);
+
+ /* Erase the window */
+ EraseRect(&td->w->portRect);
+
+ /* Set the color */
+ term_data_color(td, TERM_WHITE);
+
+ /* Frame the window in white */
+ MoveTo(0, 0);
+ LineTo(0, td->size_hgt - 1);
+ LineTo(td->size_wid - 1, td->size_hgt - 1);
+ LineTo(td->size_wid - 1, 0);
+
+ /* Clip to the new size */
+ r.left = td->w->portRect.left + td->size_ow1;
+ r.top = td->w->portRect.top + td->size_oh1;
+ r.right = td->w->portRect.right - td->size_ow2;
+ r.bottom = td->w->portRect.bottom - td->size_oh2;
+ ClipRect(&r);
+
+ /* Success */
+ return (0);
+ }
+
+ /* React to changes */
+ case TERM_XTRA_REACT:
+ {
+ /* React to changes */
+ return (Term_xtra_mac_react());
+ }
+
+ /* Delay (milliseconds) */
+ case TERM_XTRA_DELAY:
+ {
+ /* If needed */
+ if (v > 0)
+ {
+ long m = TickCount() + (v * 60L) / 1000;
+
+ /* Wait for it */
+ while (TickCount() < m) /* loop */;
+ }
+
+ /* Success */
+ return (0);
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ char *s = strdup(angband_term_name[0]);
+
+ ctopstr((StringPtr)s);
+ SetWTitle(data[0].w, (StringPtr)s);
+
+ free(s);
+ return (0);
+ }
+ }
+
+ /* Oops */
+ return (1);
+}
+
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ * Draw a "cursor" at (x,y), using a "yellow box".
+ * We are allowed to use "Term_what()" to determine
+ * the current screen contents (for inverting, etc).
+ */
+static errr Term_curs_mac(int x, int y)
+{
+ Rect r;
+
+ term_data *td = (term_data*)(Term->data);
+
+ /* Set the color */
+ term_data_color(td, TERM_YELLOW);
+
+ /* Frame the grid */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Mogami's double width tile patch */
+ if (use_bigtile &&
+ (x + 1 < Term->wid) &&
+ (Term->old->a[y][x + 1] == 255))
+ {
+ r.right += td->tile_wid;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ FrameRect(&r);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Erase "n" characters starting at (x,y)
+ */
+static errr Term_wipe_mac(int x, int y, int n)
+{
+ Rect r;
+
+ term_data *td = (term_data*)(Term->data);
+
+ /* Erase the block of characters */
+ r.left = x * td->tile_wid + td->size_ow1;
+ r.right = r.left + n * td->tile_wid;
+ r.top = y * td->tile_hgt + td->size_oh1;
+ r.bottom = r.top + td->tile_hgt;
+ EraseRect(&r);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw several ("n") chars, with an attr, at a given location.
+ */
+static errr Term_text_mac(int x, int y, int n, byte a, const char *cp)
+{
+ int xp, yp;
+
+ term_data *td = (term_data*)(Term->data);
+
+ /* Set the color */
+ term_data_color(td, a);
+
+ /* Starting pixel */
+ xp = x * td->tile_wid + td->tile_o_x + td->size_ow1;
+ yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1;
+
+ /* Move to the correct location */
+ MoveTo(xp, yp);
+
+ /* Draw the character */
+ if (n == 1) DrawChar(*cp);
+
+ /* Draw the string */
+ else DrawText(cp, 0, n);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Low level graphics (Assumes valid input)
+ *
+ * Erase "n" characters starting at (x,y)
+ */
+#ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp)
+#endif /* USE_TRANSPARENCY */
+{
+ int i;
+ Rect dst_r;
+ static RGBColor black = {0x0000, 0x0000, 0x0000};
+ static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};
+ term_data *td = (term_data*)(Term->data);
+
+ /* Destination rectangle */
+ dst_r.left = x * td->tile_wid + td->size_ow1;
+#ifndef USE_DOUBLE_TILES
+ dst_r.right = dst_r.left + td->tile_wid;
+#endif /* !USE_DOUBLE_TILES */
+ dst_r.top = y * td->tile_hgt + td->size_oh1;
+ dst_r.bottom = dst_r.top + td->tile_hgt;
+
+ /* Scan the input */
+ for (i = 0; i < n; i++)
+ {
+ byte a = *ap++;
+ char c = *cp++;
+
+#ifdef USE_TRANSPARENCY
+ byte ta = *tap++;
+ char tc = *tcp++;
+# ifdef USE_EGO_GRAPHICS
+ byte ea = *eap++;
+ char ec = *ecp++;
+ bool has_overlay = (ea && ec);
+# endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Second byte of bigtile */
+ if (use_bigtile && (a == 255))
+ {
+ /* Advance */
+ dst_r.left += td->tile_wid;
+
+ /* Ignore it */
+ continue;
+ }
+
+ /* Prepare right side of rectangle */
+ dst_r.right = dst_r.left + td->tile_wid;
+
+#endif /* USE_DOUBLE_TILES */
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Nothing */
+
+#else /* ANGBAND_LITE_MAC */
+
+ /* Graphics -- if Available and Needed */
+ if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80))
+ {
+#ifdef USE_TRANSPARENCY
+ int t_col, t_row;
+ Rect terrain_r;
+# ifdef USE_EGO_GRAPHICS
+ int e_col, e_row;
+ Rect ego_r;
+# endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ int col, row;
+ Rect src_r;
+
+ /* Row and Col */
+ row = ((byte)a & 0x7F) % pictRows;
+ col = ((byte)c & 0x7F) % pictCols;
+
+ /* Source rectangle */
+ src_r.left = col * grafWidth;
+ src_r.top = row * grafHeight;
+ src_r.right = src_r.left + grafWidth;
+ src_r.bottom = src_r.top + grafHeight;
+
+#ifdef USE_TRANSPARENCY
+
+ /* Terrain Row and Col */
+ t_row = ((byte)ta & 0x7F) % pictRows;
+ t_col = ((byte)tc & 0x7F) % pictCols;
+
+ /* Terrain Source rectangle */
+ terrain_r.left = t_col * grafWidth;
+ terrain_r.top = t_row * grafHeight;
+ terrain_r.right = terrain_r.left + grafWidth;
+ terrain_r.bottom = terrain_r.top + grafHeight;
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* If and only if there's overlay */
+ if (has_overlay)
+ {
+ /* Overlay Row and Col */
+ e_row = ((byte)ea & 0x7F) % pictRows;
+ e_col = ((byte)ec & 0x7F) % pictCols;
+
+ /* Overlay Source rectangle */
+ ego_r.left = e_col * grafWidth;
+ ego_r.top = e_row * grafHeight;
+ ego_r.right = ego_r.left + grafWidth;
+ ego_r.bottom = ego_r.top + grafHeight;
+ }
+
+# endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Hardwire CopyBits */
+ RGBBackColor(&white);
+ RGBForeColor(&black);
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Double width tiles */
+ if (use_bigtile) dst_r.right += td->tile_wid;
+
+#endif /* USE_DOUBLE_TILES */
+
+#ifdef USE_TRANSPARENCY
+
+ /* Transparency effect */
+ switch (transparency_mode)
+ {
+ /* No transparency effect */
+ case TR_NONE:
+ default:
+ {
+ /* Draw the picture */
+ CopyBits((BitMap*)frameP->framePix,
+ &(td->w->portBits),
+ &src_r, &dst_r, srcCopy, NULL);
+
+ break;
+ }
+
+ /* Overwriting with transparent black pixels */
+ case TR_OVER:
+ {
+ /* Draw the terrain */
+ CopyBits((BitMap*)frameP->framePix,
+ &(td->w->portBits),
+ &terrain_r, &dst_r, srcCopy, NULL);
+
+ /* Make black pixels transparent */
+ RGBBackColor(&black);
+
+ /* Draw mon/obj if there's one */
+ if ((row != t_row) || (col != t_col))
+ CopyBits((BitMap*)frameP->framePix,
+ &(td->w->portBits),
+ &src_r, &dst_r, transparent, NULL);
+
+# ifdef USE_EGO_GRAPHICS
+
+ /* Draw overlay if there's one */
+ if (has_overlay)
+ CopyBits((BitMap*)frameP->framePix,
+ &(td->w->portBits),
+ &ego_r, &dst_r, transparent, NULL);
+
+# endif /* USE_EGO_GRAPHICS */
+
+ break;
+ }
+ }
+
+#else /* USE_TRANSPARENCY */
+
+/* Draw the picture */
+ CopyBits((BitMap*)frameP->framePix,
+ &(td->w->portBits),
+ &src_r, &dst_r, srcCopy, NULL);
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Restore colors */
+ RGBBackColor(&black);
+ RGBForeColor(&white);
+
+ /* Forget color */
+ td->last = -1;
+ }
+
+#endif /* ANGBAND_LITE_MAC */
+
+ /* Normal */
+ else
+ {
+ int xp, yp;
+
+ /* Erase */
+ EraseRect(&dst_r);
+
+ /* Set the color */
+ term_data_color(td, a);
+
+ /* Starting pixel */
+ xp = dst_r.left + td->tile_o_x;
+ yp = dst_r.top + td->tile_o_y;
+
+ /* Move to the correct location */
+ MoveTo(xp, yp);
+
+ /* Draw the character */
+ DrawChar(c);
+ }
+
+ /* Advance */
+ dst_r.left += td->tile_wid;
+#ifndef USE_DOUBLE_TILES
+ dst_r.right += td->tile_wid;
+#endif /* USE_DOUBLE_TILES */
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+
+/*
+ * Create and initialize window number "i"
+ */
+static void term_data_link(int i)
+{
+ term *old = Term;
+
+ term_data *td = &data[i];
+
+ /* Only once */
+ if (td->t) return;
+
+ /* Require mapped */
+ if (!td->mapped) return;
+
+ /* Allocate */
+ MAKE(td->t, term);
+
+ /* Initialize the term */
+ term_init(td->t, td->cols, td->rows, td->keys);
+
+ /* Use a "software" cursor */
+ td->t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ td->t->attr_blank = TERM_WHITE;
+ td->t->char_blank = ' ';
+
+ /* Prepare the init/nuke hooks */
+ td->t->init_hook = Term_init_mac;
+ td->t->nuke_hook = Term_nuke_mac;
+
+ /* Prepare the function hooks */
+ td->t->user_hook = Term_user_mac;
+ td->t->xtra_hook = Term_xtra_mac;
+ td->t->wipe_hook = Term_wipe_mac;
+ td->t->curs_hook = Term_curs_mac;
+ td->t->text_hook = Term_text_mac;
+ td->t->pict_hook = Term_pict_mac;
+
+ /* Link the local structure */
+ td->t->data = (void *)(td);
+
+ /* Activate it */
+ Term_activate(td->t);
+
+ /* Global pointer */
+ angband_term[i] = td->t;
+
+ /* Activate old */
+ Term_activate(old);
+}
+
+
+
+
+/*
+ * Set the "current working directory" (also known as the "default"
+ * volume/directory) to the location of the current application.
+ *
+ * Code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl)
+ *
+ * This function does not appear to work correctly with System 6.
+ */
+static void SetupAppDir(void)
+{
+ FCBPBRec fcbBlock;
+ OSErr err = noErr;
+ char errString[100];
+
+ /* Get the location of the Angband executable */
+ fcbBlock.ioCompletion = NULL;
+ fcbBlock.ioNamePtr = NULL;
+ fcbBlock.ioVRefNum = 0;
+ fcbBlock.ioRefNum = CurResFile();
+ fcbBlock.ioFCBIndx = 0;
+ err = PBGetFCBInfoSync(&fcbBlock);
+ if (err != noErr)
+ {
+ strnfmt(errString, 100, "Fatal PBGetFCBInfo Error #%d.\r Exiting.", err);
+ mac_warning(errString);
+ ExitToShell();
+ }
+
+ /* Extract the Vol and Dir */
+ app_vol = fcbBlock.ioFCBVRefNum;
+ app_dir = fcbBlock.ioFCBParID;
+
+ /* Set the current working directory to that location */
+ err = HSetVol(NULL, app_vol, app_dir);
+ if (err != noErr)
+ {
+ strnfmt(errString, 100, "Fatal HSetVol Error #%d.\r Exiting.", err);
+ mac_warning(errString);
+ ExitToShell();
+ }
+}
+
+
+
+
+/*
+ * Global "preference" file pointer
+ */
+static FILE *fff;
+
+/*
+ * Read a "short" from the file
+ */
+static int getshort(void)
+{
+ int x = 0;
+ char buf[256];
+ if (0 == my_fgets(fff, buf, 256)) x = atoi(buf);
+ return (x);
+}
+
+/*
+ * Dump a "short" to the file
+ */
+static void putshort(int x)
+{
+ fprintf(fff, "%d\n", x);
+}
+
+
+
+/*
+ * Write the "preference" data to the current "file"
+ */
+static void save_prefs(void)
+{
+ int i;
+
+ term_data *td;
+
+
+ /*** The current version ***/
+
+ putshort(PREF_VER_MAJOR);
+ putshort(PREF_VER_MINOR);
+ putshort(PREF_VER_PATCH);
+ putshort(PREF_VER_EXTRA);
+
+ /* Gfx settings */
+ putshort(arg_sound);
+ putshort(graf_mode);
+#ifdef USE_DOUBLE_TILES
+ putshort(use_bigtile);
+#endif /* USE_DOUBLE_TILES */
+
+ /* Dump */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access */
+ td = &data[i];
+
+ putshort(td->mapped);
+
+ putshort(td->font_id);
+ putshort(td->font_size);
+ putshort(td->font_face);
+
+ putshort(td->tile_wid);
+ putshort(td->tile_hgt);
+
+ putshort(td->cols);
+ putshort(td->rows);
+
+ putshort(td->r.left);
+ putshort(td->r.top);
+ }
+}
+
+
+/*
+ * Load the preferences from the current "file"
+ *
+ * XXX XXX XXX Being able to undefine various windows is
+ * slightly bizarre, and may cause problems.
+ */
+static void load_prefs(void)
+{
+ int i;
+
+ int old_major, old_minor, old_patch, old_extra;
+
+ term_data *td;
+
+
+ /*** Version information ***/
+
+ /* Preferences version */
+ old_major = getshort();
+ old_minor = getshort();
+ old_patch = getshort();
+ old_extra = getshort();
+
+ /* Hack -- Verify or ignore */
+ if ((old_major != PREF_VER_MAJOR) ||
+ (old_minor != PREF_VER_MINOR) ||
+ (old_patch != PREF_VER_PATCH) ||
+ (old_extra != PREF_VER_EXTRA))
+ {
+ /* Message */
+ mac_warning("Ignoring old preferences.");
+
+ /* Ignore */
+ return;
+ }
+
+ /* Gfx mode */
+ arg_sound = getshort();
+ graf_mode_req = getshort();
+#ifdef USE_DOUBLE_TILES
+ use_bigtile = getshort();
+#endif /* USE_DOUBLE_TILES */
+
+ /* Windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Access */
+ td = &data[i];
+
+ td->mapped = getshort();
+
+ td->font_id = getshort();
+ td->font_size = getshort();
+ td->font_face = getshort();
+
+ td->tile_wid = getshort();
+ td->tile_hgt = getshort();
+
+ td->cols = getshort();
+ td->rows = getshort();
+
+ td->r.left = getshort();
+ td->r.top = getshort();
+
+ /* Done */
+ if (feof(fff)) break;
+ }
+}
+
+
+
+
+/*
+ * Hack -- default data for a window
+ */
+static void term_data_hack(term_data *td)
+{
+ short fid;
+
+ /* Default to Monaco font */
+ GetFNum("\pmonaco", &fid);
+
+ /* Wipe it */
+ WIPE(td, term_data);
+
+ /* No color */
+ td->last = -1;
+
+ /* Default borders */
+ td->size_ow1 = 2;
+ td->size_ow2 = 2;
+ td->size_oh2 = 2;
+
+ /* Start hidden */
+ td->mapped = FALSE;
+
+ /* Default font */
+ td->font_id = fid;
+
+ /* Default font size */
+ td->font_size = 12;
+
+ /* Default font face */
+ td->font_face = 0;
+
+ /* Default size */
+ td->rows = 24;
+ td->cols = 80;
+
+ /* Default position */
+ td->r.left = 10;
+ td->r.top = 40;
+
+ /* Minimal keys */
+ td->keys = 16;
+}
+
+
+/*
+ * Read the preference file, Create the windows.
+ *
+ * We attempt to use "FindFolder()" to track down the preference file,
+ * but if this fails, for any reason, we will try the "SysEnvirons()"
+ * method, which may work better with System 6.
+ */
+static void init_windows(void)
+{
+ int i, b = 0;
+
+ term_data *td;
+
+ bool oops;
+
+
+ /*** Default values ***/
+
+ /* Initialize (backwards) */
+ for (i = MAX_TERM_DATA; i-- > 0; )
+ {
+ int n;
+
+ cptr s;
+
+ /* Obtain */
+ td = &data[i];
+
+ /* Defaults */
+ term_data_hack(td);
+
+ /* Obtain title */
+ s = angband_term_name[i];
+
+ /* Get length */
+ n = strlen(s);
+
+ /* Maximal length */
+ if (n > 15) n = 15;
+
+ /* Copy the title */
+ strncpy((char*)(td->title) + 1, s, n);
+
+ /* Save the length */
+ td->title[0] = n;
+
+ /* Tile the windows */
+ td->r.left += (b * 30);
+ td->r.top += (b * 30);
+
+ /* Tile */
+ b++;
+ }
+
+
+ /*** Load preferences ***/
+
+ /* Assume failure */
+ oops = TRUE;
+
+ /* Assume failure */
+ fff = NULL;
+
+#ifdef USE_SFL_CODE
+
+ /* Block */
+ if (TRUE)
+ {
+ OSErr err;
+ short vref;
+ long dirID;
+ char foo[128];
+
+ /* Find the folder */
+ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
+ &vref, &dirID);
+
+ /* Success */
+ if (!err)
+ {
+ /* Extract a path name */
+ PathNameFromDirID(dirID, vref, (StringPtr)foo);
+
+ /* Convert the string */
+ ptocstr((StringPtr)foo);
+
+ /* Append the preference file name */
+ strcat(foo, ANGBAND_PREFERENCES);
+
+ /* Open the preference file */
+ fff = fopen(foo, "r");
+
+ /* Success */
+ oops = FALSE;
+ }
+ }
+
+#endif /* USE_SFL_CODE */
+
+#if 0 /* SysEnvirons? SetVol? I'd say no */
+
+ /* Oops */
+ if (oops)
+ {
+ SysEnvRec env;
+ short savev;
+ long saved;
+
+ /* Save */
+ HGetVol(0, &savev, &saved);
+
+ /* Go to the "system" folder */
+ SysEnvirons(curSysEnvVers, &env);
+ SetVol(0, env.sysVRefNum);
+
+ /* Open the file */
+ fff = fopen(":Preferences:" ANGBAND_PREFERENCES, "r");
+ if (!fff) fff = fopen(":" ANGBAND_PREFERENCES, "r");
+
+ /* Restore */
+ HSetVol(0, savev, saved);
+ }
+
+#endif /* relic */
+
+ /* Load preferences */
+ if (fff)
+ {
+ /* Load a real preference file */
+ load_prefs();
+
+ /* Close the file */
+ my_fclose(fff);
+ }
+
+
+ /*** Instantiate ***/
+
+ /* Main window */
+ td = &data[0];
+
+ /* Many keys */
+ td->keys = 1024;
+
+ /* Start visible */
+ td->mapped = TRUE;
+
+ /* Link (backwards, for stacking order) */
+ for (i = MAX_TERM_DATA; i-- > 0; )
+ {
+ term_data_link(i);
+ }
+
+ /* Main window */
+ td = &data[0];
+
+ /* Main window */
+ Term_activate(td->t);
+}
+
+
+/*
+ * Exit the program
+ */
+static void save_pref_file(void)
+{
+ bool oops;
+
+
+ /* Assume failure */
+ oops = TRUE;
+
+ /* Assume failure */
+ fff = NULL;
+
+ /* Text file */
+ _ftype = 'TEXT';
+
+
+#ifdef USE_SFL_CODE
+
+ /* Block */
+ if (TRUE)
+ {
+ OSErr err;
+ short vref;
+ long dirID;
+ char foo[128];
+
+ /* Find the folder */
+ err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
+ &vref, &dirID);
+
+ /* Success */
+ if (!err)
+ {
+ /* Extract a path name */
+ PathNameFromDirID(dirID, vref, (StringPtr)foo);
+
+ /* Convert the string */
+ ptocstr((StringPtr)foo);
+
+ /* Append the preference file name */
+ strcat(foo, ANGBAND_PREFERENCES);
+
+ /* Open the preference file */
+ fff = fopen(foo, "w");
+
+ /* Success */
+ oops = FALSE;
+ }
+ }
+
+#endif /* USE_SFL_CODE */
+
+#if 0 /* I don't believe we need SysEnvirons any longer */
+
+ /* Oops */
+ if (oops)
+ {
+ SysEnvRec env;
+ short savev;
+ long saved;
+
+ /* Save */
+ HGetVol(0, &savev, &saved);
+
+ /* Go to "system" folder */
+ SysEnvirons(curSysEnvVers, &env);
+ SetVol(0, env.sysVRefNum);
+
+ /* Open the preference file */
+ fff = fopen(":Preferences:" ANGBAND_PREFERENCES, "w");
+ if (!fff) fff = fopen(":" ANGBAND_PREFERENCES, "w");
+
+ /* Restore */
+ HSetVol(0, savev, saved);
+ }
+
+#endif /* relic */
+
+ /* Save preferences */
+ if (fff)
+ {
+ /* Write the preferences */
+ save_prefs();
+
+ /* Close it */
+ my_fclose(fff);
+ }
+}
+
+
+
+#ifdef ALLOW_NO_SAVE_QUITS
+
+/*
+ * A simple "Yes/No" filter to parse "key press" events in dialog windows
+ */
+static pascal Boolean ynfilter(DialogPtr dialog, EventRecord *event, short *ip)
+{
+ /* Parse key press events */
+ if (event->what == keyDown)
+ {
+ int i = 0;
+ char c;
+
+ /* Extract the pressed key */
+ c = (event->message & charCodeMask);
+
+ /* Accept "no" and <return> and <enter> */
+ if ((c == 'n') || (c == 'N') || (c == 13) || (c == 3)) i = 1;
+
+ /* Accept "yes" */
+ else if ((c == 'y') || (c == 'Y')) i = 2;
+
+ /* Handle "yes" or "no" */
+ if (i)
+ {
+ short type;
+ ControlHandle control;
+ Rect r;
+
+ /* Get the button */
+ GetDialogItem(dialog, i, &type, (Handle*)&control, &r);
+
+ /* Blink button for 1/10 second */
+ HiliteControl(control, 1);
+ Term_xtra_mac(TERM_XTRA_DELAY, 100);
+ HiliteControl(control, 0);
+
+ /* Result */
+ *ip = i;
+ return (1);
+ }
+ }
+
+ /* Ignore */
+ return (0);
+}
+
+#endif /* ALLOW_NO_SAVE_QUITS */
+
+
+
+#ifndef SAVEFILE_SCREEN
+
+/*
+ * Handle menu: "File" + "New"
+ */
+static void do_menu_file_new(void)
+{
+ /* Hack */
+ HiliteMenu(0);
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Flush input */
+ Term_flush();
+
+ /* Play a game */
+ play_game(TRUE);
+
+ /* Hack -- quit */
+ quit(NULL);
+}
+
+/*
+ * Handle menu: "File" + "Open"
+ */
+static void do_menu_file_open(bool all)
+{
+ int err;
+ short vrefnum;
+ long drefnum;
+ long junk;
+ DirInfo pb;
+ SFTypeList types;
+ SFReply reply;
+ Point topleft;
+
+
+ /* XXX XXX XXX */
+
+ /* vrefnum = GetSFCurVol(); */
+ vrefnum = -*((short*)0x214);
+
+ /* drefnum = GetSFCurDir(); */
+ drefnum = *((long*)0x398);
+
+ /* Descend into "lib" folder */
+ pb.ioCompletion = NULL;
+ pb.ioNamePtr = "\plib";
+ pb.ioVRefNum = vrefnum;
+ pb.ioDrDirID = drefnum;
+ pb.ioFDirIndex = 0;
+
+ /* Check for errors */
+ err = PBGetCatInfoSync((CInfoPBPtr) & pb);
+
+ /* Success */
+ if ((err == noErr) && (pb.ioFlAttrib & 0x10))
+ {
+ /* Descend into "lib/save" folder */
+ pb.ioCompletion = NULL;
+ pb.ioNamePtr = "\psave";
+ pb.ioVRefNum = vrefnum;
+ pb.ioDrDirID = pb.ioDrDirID;
+ pb.ioFDirIndex = 0;
+
+ /* Check for errors */
+ err = PBGetCatInfoSync((CInfoPBPtr) & pb);
+
+ /* Success */
+ if ((err == noErr) && (pb.ioFlAttrib & 0x10))
+ {
+ /* SetSFCurDir(pb.ioDrDirID); */
+ *((long*)0x398) = pb.ioDrDirID;
+ }
+ }
+
+ /* Window location */
+ topleft.h = (qd.screenBits.bounds.left + qd.screenBits.bounds.right) / 2 - 344 / 2;
+ topleft.v = (2 * qd.screenBits.bounds.top + qd.screenBits.bounds.bottom) / 3 - 188 / 2;
+
+ /* Allow "all" files */
+ if (all)
+ {
+ /* Get any file */
+ SFGetFile(topleft, "\p", NULL, -1, types, NULL, &reply);
+ }
+
+ /* Allow "save" files */
+ else
+ {
+ /* Legal types */
+ types[0] = 'SAVE';
+
+ /* Get a file */
+ SFGetFile(topleft, "\p", NULL, 1, types, NULL, &reply);
+ }
+
+ /* Allow cancel */
+ if (!reply.good) return;
+
+ /* Extract textual file name for save file */
+ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk);
+ refnum_to_name(savefile, drefnum, vrefnum, (char*)reply.fName);
+
+ /* Hack */
+ HiliteMenu(0);
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Flush input */
+ flush();
+
+ /* Play a game */
+ play_game(FALSE);
+
+ /* Hack -- quit */
+ quit(NULL);
+}
+
+#endif /* !SAVEFILE_SCREEN */
+
+
+/*
+ * Handle the "open_when_ready" flag
+ */
+static void handle_open_when_ready(void)
+{
+ /* Check the flag XXX XXX XXX make a function for this */
+ if (open_when_ready && initialized && !game_in_progress)
+ {
+ /* Forget */
+ open_when_ready = FALSE;
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Wait for it */
+ pause_line(23);
+
+ /* Flush input */
+ flush();
+
+#ifdef SAVEFILE_SCREEN
+
+ /* User double-clicked savefile; no savefile screen */
+ no_begin_screen = TRUE;
+
+#endif /* SAVEFILE_SCREEN */
+
+ /* Play a game */
+ play_game(FALSE);
+
+ /* Quit */
+ quit(NULL);
+ }
+}
+
+
+
+/*
+ * The standard menus are:
+ *
+ * Apple (128) = { About, -, ... }
+ * File (129) = { New,Open,Import,Close,Save,-,Score,Exit,Quit }
+ * In ToME, this becomes
+ * File (129) = { Close,Save,-,Score,Exit,Quit }
+ * Edit (130) = { Cut, Copy, Paste, Clear } (?)
+ * Font (131) = { Bold, Extend, -, Monaco, ..., -, ... }
+ * Size (132) = { ... }
+ * Window (133) = { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice,
+ * Term-4, Term-5, Term-6, Term-7 }
+ * Special (134) = { Sound, Graphics, TileWidth, TileHeight, -,
+ * Fiddle, Wizard }
+ */
+
+/* Apple menu */
+#define MENU_APPLE 128
+#define ITEM_ABOUT 1
+
+/* File menu */
+#define MENU_FILE 129
+#ifndef SAVEFILE_SCREEN
+# define ITEM_NEW 1
+# define ITEM_OPEN 2
+# define ITEM_IMPORT 3
+# define ITEM_CLOSE 4
+# define ITEM_SAVE 5
+# ifdef HAS_SCORE_MENU
+# define ITEM_SCORE 7
+# define ITEM_EXIT 8
+# define ITEM_QUIT 9
+# else
+# define ITEM_EXIT 7
+# define ITEM_QUIT 8
+# endif /* HAS_SCORE_MENU */
+#else /* !SAVEFILE_SCREEN - in-game savefile menu */
+# define ITEM_CLOSE 1
+# define ITEM_SAVE 2
+# ifdef HAS_SCORE_MENU
+# define ITEM_SCORE 4
+# define ITEM_EXIT 5
+# define ITEM_QUIT 6
+# else
+# define ITEM_EXIT 4
+# define ITEM_QUIT 5
+# endif /* HAS_SCORE_MENU */
+#endif /* !SAVEFILE_SCREEN */
+
+/* Edit menu */
+#define MENU_EDIT 130
+#define ITEM_UNDO 1
+#define ITEM_CUT 3
+#define ITEM_COPY 4
+#define ITEM_PASTE 5
+#define ITEM_CLEAR 6
+
+/* Font menu */
+#define MENU_FONT 131
+#define ITEM_BOLD 1
+#define ITEM_WIDE 2
+
+/* Size menu */
+#define MENU_SIZE 132
+
+/* Windows menu */
+#define MENU_WINDOWS 133
+
+/* Special menu */
+#define MENU_SPECIAL 134
+#define ITEM_SOUND 1
+#define ITEM_GRAPH 2
+#define ITEM_TILEWIDTH 3
+#define ITEM_TILEHEIGHT 4
+#define ITEM_FIDDLE 6
+#define ITEM_WIZARD 7
+
+/* Graphics submenu */
+#define SUBMENU_GRAPH 144
+#define ITEM_NONE 1
+#define ITEM_8X8 2
+#define ITEM_16X16 3
+#define ITEM_BIGTILE 5
+
+/* TileWidth submenu */
+#define SUBMENU_TILEWIDTH 145
+
+/* TileHeight submenu */
+#define SUBMENU_TILEHEIGHT 146
+
+
+/*
+ * Initialize the menus
+ */
+static void init_menubar(void)
+{
+ int i, n;
+
+ Rect r;
+
+ WindowPtr tmpw;
+
+ MenuHandle m;
+
+ Handle mbar;
+
+
+ /* Load menubar from resources */
+ mbar = GetNewMBar(128);
+
+ /* Whoops! */
+ if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!");
+
+ /* Insert them into the current menu list */
+ SetMenuBar(mbar);
+
+ /* Free handle */
+ DisposeHandle(mbar);
+
+
+ /* Apple menu (id 128) */
+ m = GetMenuHandle(MENU_APPLE);
+
+ /* Add the DA's to the "apple" menu */
+ AppendResMenu(m, 'DRVR');
+
+
+ /* File menu (id 129) - we don't have to do anything */
+
+
+ /* Edit menu (id 130) - we don't have to do anything */
+
+
+ /*
+ * Font menu (id 131) - append names of mono-spaced fonts
+ * followed by all available ones
+ */
+ m = GetMenuHandle(MENU_FONT);
+
+ /* Fake window */
+ r.left = r.right = r.top = r.bottom = 0;
+
+ /* Make the fake window so that we can retrive font info */
+ tmpw = NewWindow(0, &r, "\p", false, documentProc, 0, 0, 0);
+
+ /* Activate the "fake" window */
+ SetPort(tmpw);
+
+ /* Default mode */
+ TextMode(0);
+
+ /* Default size */
+ TextSize(12);
+
+ /* Add the fonts to the menu */
+ AppendResMenu(m, 'FONT');
+
+ /* Size of menu */
+ n = CountMItems(m);
+
+ /* Scan the menu */
+ for (i = n; i >= 4; i--)
+ {
+ Str255 tmpName;
+ short fontNum;
+
+ /* Acquire the font name */
+ GetMenuItemText(m, i, tmpName);
+
+ /* Acquire the font index */
+ GetFNum(tmpName, &fontNum);
+
+ /* Apply the font index */
+ TextFont(fontNum);
+
+ /* Remove non-mono-spaced fonts */
+ if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0))
+ {
+ /* Delete the menu item */
+ DeleteMenuItem(m, i);
+ }
+ }
+
+ /* Destroy the old window */
+ DisposeWindow(tmpw);
+
+ /* Add a separator */
+ AppendMenu(m, "\p-");
+
+ /* Add the fonts to the menu */
+ AppendResMenu(m, 'FONT');
+
+
+ /* Size menu (id 132) */
+ m = GetMenuHandle(MENU_SIZE);
+
+ /* Add some sizes (stagger choices) */
+ for (i = 8; i <= 32; i += ((i / 16) + 1))
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Add the item */
+ AppendMenu(m, buf);
+ }
+
+
+ /* Windows menu (id 133) */
+ m = GetMenuHandle(MENU_WINDOWS);
+
+ /* Default choices */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Str15 buf;
+
+ /* Describe the item */
+ strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Add the item */
+ AppendMenu(m, buf);
+
+ /* Command-Key shortcuts */
+ if (i < 8) SetItemCmd(m, i + 1, I2D(i));
+ }
+
+
+ /* Special menu (id 134) */
+
+ /* Get graphics (sub)menu (id 144) */
+ m = GetMenu(SUBMENU_GRAPH);
+
+ /* Insert it as a submenu */
+ InsertMenu(m, hierMenu);
+
+
+ /* Get TileWidth (sub)menu (id 145) */
+ m = GetMenu(SUBMENU_TILEWIDTH);
+
+ /* Add some sizes */
+ for (i = 4; i <= 32; i++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Append item */
+ AppendMenu(m, buf);
+ }
+
+ /* Insert it as a submenu */
+ InsertMenu(m, hierMenu);
+
+ /* Get TileHeight (sub)menu (id 146) */
+ m = GetMenu(SUBMENU_TILEHEIGHT);
+
+ /* Add some sizes */
+ for (i = 4; i <= 32; i++)
+ {
+ Str15 buf;
+
+ /* Textual size */
+ strnfmt((char*)buf + 1, 15, "%d", i);
+ buf[0] = strlen((char*)buf + 1);
+
+ /* Append item */
+ AppendMenu(m, buf);
+ }
+
+ /* Insert it as a submenu */
+ InsertMenu(m, hierMenu);
+
+
+ /* Update the menu bar */
+ DrawMenuBar();
+}
+
+
+/*
+ * Prepare the menus
+ *
+ * It is very important that the player not be allowed to "save" the game
+ * unless the "inkey_flag" variable is set, indicating that the game is
+ * waiting for a new command. XXX XXX XXX
+ */
+static void setup_menus(void)
+{
+ int i, n;
+
+ short value;
+
+ Str255 s;
+
+ MenuHandle m;
+
+ term_data *td = NULL;
+
+
+ /* Relevant "term_data" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Unused */
+ if (!data[i].t) continue;
+
+ /* Notice the matching window */
+ if (data[i].w == FrontWindow()) td = &data[i];
+ }
+
+
+ /* File menu */
+ m = GetMenuHandle(MENU_FILE);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Enable "new"/"open..."/"import..." */
+ if (initialized && !game_in_progress)
+ {
+ EnableItem(m, ITEM_NEW);
+ EnableItem(m, ITEM_OPEN);
+ EnableItem(m, ITEM_IMPORT);
+ }
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Enable "close" */
+ if (initialized)
+ {
+ EnableItem(m, ITEM_CLOSE);
+ }
+
+ /* Enable "save" */
+ if (initialized && character_generated && inkey_flag)
+ {
+ EnableItem(m, ITEM_SAVE);
+ }
+
+#ifdef HAS_SCORE_MENU
+
+ /* Enable "score" */
+ if (initialized && character_generated && !character_icky)
+ {
+ EnableItem(m, ITEM_SCORE);
+ }
+
+#endif /* HAS_SCORE_MENU */
+
+#ifdef ALLOW_NO_SAVE_QUITS
+
+ /* Enable "exit" */
+ if (TRUE)
+ {
+ EnableItem(m, ITEM_EXIT);
+ }
+
+#endif /* ALLOW_NO_SAVE_QUITS */
+
+ /* Enable "quit" */
+ if (!initialized || !character_generated || inkey_flag)
+ {
+ EnableItem(m, ITEM_QUIT);
+ }
+
+
+ /* Edit menu */
+ m = GetMenuHandle(MENU_EDIT);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+ /* Enable "edit" options if "needed" */
+ if (!td)
+ {
+ EnableItem(m, ITEM_UNDO);
+ EnableItem(m, ITEM_CUT);
+ EnableItem(m, ITEM_COPY);
+ EnableItem(m, ITEM_PASTE);
+ EnableItem(m, ITEM_CLEAR);
+ }
+
+
+ /* Font menu */
+ m = GetMenuHandle(MENU_FONT);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+ /* Hack -- look cute XXX XXX */
+ /* SetItemStyle(m, ITEM_BOLD, bold); */
+
+ /* Hack -- look cute XXX XXX */
+ /* SetItemStyle(m, ITEM_WIDE, extend); */
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Enable "bold" */
+ EnableItem(m, ITEM_BOLD);
+
+ /* Enable "extend" */
+ EnableItem(m, ITEM_WIDE);
+
+ /* Check the appropriate "bold-ness" */
+ if (td->font_face & bold) CheckItem(m, ITEM_BOLD, TRUE);
+
+ /* Check the appropriate "wide-ness" */
+ if (td->font_face & extend) CheckItem(m, ITEM_WIDE, TRUE);
+
+ /* Analyze fonts */
+ for (i = 4; i <= n; i++)
+ {
+ /* Enable it */
+ EnableItem(m, i);
+
+ /* Analyze font */
+ GetMenuItemText(m, i, s);
+ GetFNum(s, &value);
+
+ /* Check active font */
+ if (td->font_id == value) CheckItem(m, i, TRUE);
+ }
+ }
+
+
+ /* Size menu */
+ m = GetMenuHandle(MENU_SIZE);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ GetMenuItemText(m, i, s);
+ s[s[0] + 1] = '\0';
+ value = atoi((char*)(s + 1));
+
+ /* Enable the "real" sizes */
+ if (RealFont(td->font_id, value)) EnableItem(m, i);
+
+ /* Check the current size */
+ if (td->font_size == value) CheckItem(m, i, TRUE);
+ }
+ }
+
+
+ /* Windows menu */
+ m = GetMenuHandle(MENU_WINDOWS);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Check active windows */
+ for (i = 1; i <= n; i++)
+ {
+ /* Check if needed */
+ CheckItem(m, i, data[i - 1].mapped);
+ }
+
+
+ /* Special menu */
+ m = GetMenuHandle(MENU_SPECIAL);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+
+ /* XXX Oh no, this removes submenu... */
+ if ((i != ITEM_GRAPH) &&
+ (i != ITEM_TILEWIDTH) &&
+ (i != ITEM_TILEHEIGHT)) CheckItem(m, i, FALSE);
+ }
+
+ /* Item "Sound" */
+ EnableItem(m, ITEM_SOUND);
+ CheckItem(m, ITEM_SOUND, arg_sound);
+
+ /* Item "Graphics" */
+ EnableItem(m, ITEM_GRAPH);
+
+ /* Item "TileWidth" */
+ EnableItem(m, ITEM_TILEWIDTH);
+
+ /* Item "TileHeight" */
+ EnableItem(m, ITEM_TILEHEIGHT);
+
+ /* Item "Fiddle" */
+ EnableItem(m, ITEM_FIDDLE);
+ CheckItem(m, ITEM_FIDDLE, arg_fiddle);
+
+ /* Item "Wizard" */
+ EnableItem(m, ITEM_WIZARD);
+ CheckItem(m, ITEM_WIZARD, arg_wizard);
+
+ /* Graphics submenu */
+ m = GetMenuHandle(SUBMENU_GRAPH);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+ /* Item "None" */
+ EnableItem(m, ITEM_NONE);
+ CheckItem(m, ITEM_NONE, (graf_mode == GRAF_MODE_NONE));
+
+ /* Item "8x8" */
+ EnableItem(m, ITEM_8X8);
+ CheckItem(m, ITEM_8X8, (graf_mode == GRAF_MODE_8X8));
+
+ /* Item "16x16" */
+ EnableItem(m, ITEM_16X16);
+ CheckItem(m, ITEM_16X16, (graf_mode == GRAF_MODE_16X16));
+
+#ifdef USE_DOUBLE_TILES
+
+ /* Item "Bigtile" */
+ if (inkey_flag) EnableItem(m, ITEM_BIGTILE);
+ CheckItem(m, ITEM_BIGTILE, use_bigtile);
+
+#endif /* USE_DOUBLE_TILES */
+
+
+ /* TIleWidth submenu */
+ m = GetMenuHandle(SUBMENU_TILEWIDTH);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ GetMenuItemText(m, i, s);
+ s[s[0] + 1] = '\0';
+ value = atoi((char*)(s + 1));
+
+ /* Enable */
+ if (value >= td->font_wid) EnableItem(m, i);
+
+ /* Check the current size */
+ if (td->tile_wid == value) CheckItem(m, i, TRUE);
+ }
+ }
+
+
+ /* TileHeight submenu */
+ m = GetMenuHandle(SUBMENU_TILEHEIGHT);
+
+ /* Get menu size */
+ n = CountMItems(m);
+
+ /* Reset menu */
+ for (i = 1; i <= n; i++)
+ {
+ /* Reset */
+ DisableItem(m, i);
+ CheckItem(m, i, FALSE);
+ }
+
+ /* Active window */
+ if (initialized && td)
+ {
+ /* Analyze sizes */
+ for (i = 1; i <= n; i++)
+ {
+ /* Analyze size */
+ GetMenuItemText(m, i, s);
+ s[s[0] + 1] = '\0';
+ value = atoi((char*)(s + 1));
+
+ /* Enable */
+ if (value >= td->font_hgt) EnableItem(m, i);
+
+ /* Check the current size */
+ if (td->tile_hgt == value) CheckItem(m, i, TRUE);
+ }
+ }
+}
+
+
+/*
+ * Process a menu selection (see above)
+ *
+ * Hack -- assume that invalid menu selections are disabled above,
+ * which I have been informed may not be reliable. XXX XXX XXX
+ */
+static void menu(long mc)
+{
+ int i;
+
+ int menuid, selection;
+
+ static unsigned char s[1000];
+
+ short fid;
+
+ term_data *td = NULL;
+
+ WindowPtr old_win;
+
+
+ /* Analyze the menu command */
+ menuid = HiWord(mc);
+ selection = LoWord(mc);
+
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == FrontWindow()) td = &data[i];
+ }
+
+
+ /* Branch on the menu */
+ switch (menuid)
+ {
+ /* Apple Menu */
+ case MENU_APPLE:
+ {
+ /* About Angband... */
+ if (selection == ITEM_ABOUT)
+ {
+ DialogPtr dialog;
+ Rect r;
+ short item_hit;
+
+ dialog = GetNewDialog(128, 0, (WindowPtr) - 1);
+
+ r = dialog->portRect;
+ center_rect(&r, &qd.screenBits.bounds);
+ MoveWindow(dialog, r.left, r.top, 1);
+ ShowWindow(dialog);
+ ModalDialog(0, &item_hit);
+ DisposeDialog(dialog);
+ break;
+ }
+
+ /* Desk accessory */
+ GetMenuItemText(GetMenuHandle(MENU_APPLE), selection, s);
+ OpenDeskAcc(s);
+ break;
+ }
+
+
+ /* File Menu */
+ case MENU_FILE:
+ {
+ switch (selection)
+ {
+#ifndef SAVEFILE_SCREEN
+
+ /* New */
+ case ITEM_NEW:
+ {
+ do_menu_file_new();
+ break;
+ }
+
+ /* Open... */
+ case ITEM_OPEN:
+ {
+ do_menu_file_open(FALSE);
+ break;
+ }
+
+ /* Import... */
+ case ITEM_IMPORT:
+ {
+ do_menu_file_open(TRUE);
+ break;
+ }
+
+#endif /* !SAVEFILE_SCREEN */
+
+ /* Close */
+ case ITEM_CLOSE:
+ {
+ /* No window */
+ if (!td) break;
+
+ /* Not Mapped */
+ td->mapped = FALSE;
+
+ /* Not Mapped */
+ td->t->mapped_flag = FALSE;
+
+ /* Hide the window */
+ HideWindow(td->w);
+
+ break;
+ }
+
+ /* Save */
+ case ITEM_SAVE:
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Hack -- Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+
+ break;
+ }
+
+#ifdef HAS_SCORE_MENU
+
+ /* Show score */
+ case ITEM_SCORE:
+ {
+ char buf[1024];
+
+ /* Paranoia */
+ if (!initialized || character_icky ||
+ !game_in_progress || !character_generated)
+ {
+ /* Can't happen but just in case */
+ plog("You may not do that right now.");
+
+ break;
+ }
+
+ /* Build the pathname of the score file */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX,
+ "scores.raw");
+
+ /* Hack - open the score file for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Paranoia - No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file is not available.");
+
+ break;
+ }
+
+ /* Mega-Hack - prevent various functions XXX XXX XXX */
+ initialized = FALSE;
+
+ /* Save screen */
+ screen_save();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prepare scores */
+ if (game_in_progress && character_generated)
+ {
+ predict_score();
+ }
+
+#if 0 /* I don't like this - pelpel */
+
+ /* Mega-Hack - No current player XXX XXX XXX XXX */
+ else
+ {
+ display_scores_aux(0, MAX_HISCORES, -1, NULL);
+ }
+
+#endif
+
+ /* Close the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the fd */
+ highscore_fd = -1;
+
+ /* Restore screen */
+ screen_load();
+
+ /* Hack - Flush it */
+ Term_fresh();
+
+ /* Mega-Hack - We are ready again */
+ initialized = TRUE;
+
+ /* Done */
+ break;
+ }
+
+#endif /* HAS_SCORE_MENU */
+
+#ifdef ALLOW_NO_SAVE_QUITS
+
+ /* Exit (without save) */
+ case ITEM_EXIT:
+ {
+ /* Allow user to cancel "dangerous" exit */
+ if (game_in_progress && character_generated)
+ {
+ AlertTHndl alert;
+ short item_hit;
+
+ /* Get the "alert" info */
+ alert = (AlertTHndl)GetResource('ALRT', 130);
+
+ /* Center the "alert" rectangle */
+ center_rect(&(*alert)->boundsRect,
+ &qd.screenBits.bounds);
+
+ /* Display the Alert, get "No" or "Yes" */
+ item_hit = Alert(130, ynfilterUPP);
+
+ /* Require "yes" button */
+ if (item_hit != 2) break;
+ }
+
+ /* Quit */
+ quit(NULL);
+ break;
+ }
+
+#endif /* ALLOW_NO_SAVE_QUITS */
+
+ /* Quit (with save) */
+ case ITEM_QUIT:
+ {
+ /* Save the game (if necessary) */
+ if (game_in_progress && character_generated)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+#ifndef ZANG_AUTO_SAVE
+ do_cmd_save_game();
+#else
+ do_cmd_save_game(FALSE);
+#endif /* !ZANG_AUTO_SAVE */
+ }
+
+ /* Quit */
+ quit(NULL);
+ break;
+ }
+ }
+
+ break;
+ }
+
+
+ /* Edit menu */
+ case MENU_EDIT:
+ {
+ /* Unused */
+ break;
+ }
+
+
+ /* Font menu */
+ case MENU_FONT:
+ {
+ /* Require a window */
+ if (!td) break;
+
+ /* Memorize old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Toggle the "bold" setting */
+ if (selection == ITEM_BOLD)
+ {
+ /* Toggle the setting */
+ if (td->font_face & bold)
+ {
+ td->font_face &= ~bold;
+ }
+ else
+ {
+ td->font_face |= bold;
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ break;
+ }
+
+ /* Toggle the "wide" setting */
+ if (selection == ITEM_WIDE)
+ {
+ /* Toggle the setting */
+ if (td->font_face & extend)
+ {
+ td->font_face &= ~extend;
+ }
+ else
+ {
+ td->font_face |= extend;
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ break;
+ }
+
+ /* Get a new font name */
+ GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s);
+ GetFNum(s, &fid);
+
+ /* Save the new font id */
+ td->font_id = fid;
+
+ /* Current size is bad for new font */
+ if (!RealFont(td->font_id, td->font_size))
+ {
+ /* Find similar size */
+ for (i = 1; i <= 32; i++)
+ {
+ /* Adjust smaller */
+ if (td->font_size - i >= 8)
+ {
+ if (RealFont(td->font_id, td->font_size - i))
+ {
+ td->font_size -= i;
+ break;
+ }
+ }
+
+ /* Adjust larger */
+ if (td->font_size + i <= 128)
+ {
+ if (RealFont(td->font_id, td->font_size + i))
+ {
+ td->font_size += i;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore the window */
+ activate(old_win);
+
+ break;
+ }
+
+ /* Size menu */
+ case MENU_SIZE:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ GetMenuItemText(GetMenuHandle(MENU_SIZE), selection, s);
+ s[s[0] + 1] = 0;
+ td->font_size = atoi((char*)(s + 1));
+
+ /* Hack - clear tile size info XXX XXX */
+ td->tile_wid = td->tile_hgt = 0;
+
+ /* Apply and Verify */
+ term_data_check_font(td);
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+ /* Window menu */
+ case MENU_WINDOWS:
+ {
+ /* Parse */
+ i = selection - 1;
+
+ /* Check legality of choice */
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ /* Obtain the window */
+ td = &data[i];
+
+ /* Mapped */
+ td->mapped = TRUE;
+
+ /* Link */
+ term_data_link(i);
+
+ /* Mapped (?) */
+ td->t->mapped_flag = TRUE;
+
+ /* Show the window */
+ ShowWindow(td->w);
+
+ /* Bring to the front */
+ SelectWindow(td->w);
+
+ break;
+ }
+
+ /* Special menu */
+ case MENU_SPECIAL:
+ {
+ switch (selection)
+ {
+ case ITEM_SOUND:
+ {
+ /* Toggle arg_sound */
+ arg_sound = !arg_sound;
+
+ /* React to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ break;
+ }
+
+ case ITEM_FIDDLE:
+ {
+ arg_fiddle = !arg_fiddle;
+ break;
+ }
+
+ case ITEM_WIZARD:
+ {
+ arg_wizard = !arg_wizard;
+ break;
+ }
+ }
+
+ break;
+ }
+
+
+ /* Graphics submenu */
+ case SUBMENU_GRAPH:
+ {
+ switch (selection)
+ {
+ case ITEM_NONE:
+ {
+ graf_mode_req = GRAF_MODE_NONE;
+
+ break;
+ }
+
+ case ITEM_8X8:
+ {
+ graf_mode_req = GRAF_MODE_8X8;
+
+ break;
+ }
+
+ case ITEM_16X16:
+ {
+ graf_mode_req = GRAF_MODE_16X16;
+
+ break;
+ }
+
+#ifdef USE_DOUBLE_TILES
+
+ case ITEM_BIGTILE:
+ {
+ term *old = Term;
+ term_data *td = &data[0];
+
+ /* Toggle "arg_bigtile" */
+ use_bigtile = !use_bigtile;
+# ifdef TOME
+ arg_bigtile = use_bigtile;
+# endif /* TOME */
+
+ /* Activate */
+ Term_activate(td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Activate old */
+ Term_activate(old);
+
+ break;
+ }
+
+#endif /* USE_DOUBLE_TILES */
+
+ }
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+
+ /* TileWidth submenu */
+ case SUBMENU_TILEWIDTH:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ GetMenuItemText(GetMenuHandle(SUBMENU_TILEWIDTH), selection, s);
+ s[s[0] + 1] = 0;
+ td->tile_wid = atoi((char*)(s + 1));
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+
+
+ /* TileHeight submenu */
+ case SUBMENU_TILEHEIGHT:
+ {
+ if (!td) break;
+
+ /* Save old */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ GetMenuItemText(GetMenuHandle(SUBMENU_TILEHEIGHT), selection, s);
+ s[s[0] + 1] = 0;
+ td->tile_hgt = atoi((char*)(s + 1));
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ activate(old_win);
+
+ break;
+ }
+ }
+
+
+ /* Clean the menu */
+ HiliteMenu(0);
+}
+
+
+#ifdef USE_SFL_CODE
+
+
+/*
+ * Check for extra required parameters -- From "Maarten Hazewinkel"
+ */
+static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent)
+{
+ OSErr aeError;
+ DescType returnedType;
+ Size actualSize;
+
+ aeError = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard,
+ &returnedType, NULL, 0, &actualSize);
+
+ if (aeError == errAEDescNotFound) return (noErr);
+
+ if (aeError == noErr) return (errAEParamMissed);
+
+ return (aeError);
+}
+
+
+/*
+ * Apple Event Handler -- Open Application
+ */
+static OSErr AEH_Start(const AppleEvent *theAppleEvent,
+ AppleEvent *reply, long handlerRefCon)
+{
+#pragma unused(reply, handlerRefCon)
+
+ return (CheckRequiredAEParams(theAppleEvent));
+}
+
+
+/*
+ * Apple Event Handler -- Quit Application
+ */
+static OSErr AEH_Quit(const AppleEvent *theAppleEvent,
+ AppleEvent *reply, long handlerRefCon)
+{
+#pragma unused(reply, handlerRefCon)
+
+ /* Quit later */
+ quit_when_ready = TRUE;
+
+ /* Check arguments */
+ return (CheckRequiredAEParams(theAppleEvent));
+}
+
+
+/*
+ * Apple Event Handler -- Print Documents
+ */
+static OSErr AEH_Print(const AppleEvent *theAppleEvent,
+ AppleEvent *reply, long handlerRefCon)
+{
+#pragma unused(theAppleEvent, reply, handlerRefCon)
+
+ return (errAEEventNotHandled);
+}
+
+
+/*
+ * Apple Event Handler by Steve Linberg (slinberg@crocker.com).
+ *
+ * The old method of opening savefiles from the finder does not work
+ * on the Power Macintosh, because CountAppFiles and GetAppFiles,
+ * used to return information about the selected document files when
+ * an application is launched, are part of the Segment Loader, which
+ * is not present in the RISC OS due to the new memory architecture.
+ *
+ * The "correct" way to do this is with AppleEvents. The following
+ * code is modeled on the "Getting Files Selected from the Finder"
+ * snippet from Think Reference 2.0. (The prior sentence could read
+ * "shamelessly swiped & hacked")
+ */
+static OSErr AEH_Open(AppleEvent *theAppleEvent,
+ AppleEvent* reply, long handlerRefCon)
+{
+#pragma unused(reply, handlerRefCon)
+
+ FSSpec myFSS;
+ AEDescList docList;
+ OSErr err;
+ Size actualSize;
+ AEKeyword keywd;
+ DescType returnedType;
+ char msg[128];
+ FInfo myFileInfo;
+
+ /* Put the direct parameter (a descriptor list) into a docList */
+ err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList);
+ if (err) return err;
+
+ /*
+ * We ignore the validity check, because we trust the FInder, and we only
+ * allow one savefile to be opened, so we ignore the depth of the list.
+ */
+
+ err = AEGetNthPtr(&docList, 1L, typeFSS, &keywd,
+ &returnedType, (Ptr) & myFSS, sizeof(myFSS), &actualSize);
+ if (err) return err;
+
+ /* Only needed to check savefile type below */
+ err = FSpGetFInfo(&myFSS, &myFileInfo);
+ if (err)
+ {
+ strnfmt(msg, 128, "Argh! FSpGetFInfo failed with code %d", err);
+ mac_warning(msg);
+ return err;
+ }
+
+ /* Ignore non 'SAVE' files */
+ if (myFileInfo.fdType != 'SAVE') return noErr;
+
+ /* XXX XXX XXX Extract a file name */
+ PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile);
+ pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name);
+
+ /* Convert the string */
+ ptocstr((StringPtr)savefile);
+
+ /* Delay actual open */
+ open_when_ready = TRUE;
+
+ /* Dispose */
+ err = AEDisposeDesc(&docList);
+
+ /* Success */
+ return noErr;
+}
+
+
+#endif
+
+
+
+/*
+ * Macintosh modifiers (event.modifier & ccc):
+ * cmdKey, optionKey, shiftKey, alphaLock, controlKey
+ *
+ *
+ * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra):
+ *
+ * Return:36
+ * Delete:51
+ *
+ * Period:65
+ * Star:67
+ * Plus:69
+ * Clear:71
+ * Slash:75
+ * Enter:76
+ * Minus:78
+ * Equal:81
+ * 0-7:82-89
+ * 8-9:91-92
+ *
+ * backslash/vertical bar (Japanese keyboard):93
+ *
+ * F5: 96
+ * F6: 97
+ * F7: 98
+ * F3:99
+ * F8:100
+ * F10:101
+ * F11:103
+ * F13:105
+ * F14:107
+ * F9:109
+ * F12:111
+ * F15:113
+ * Help:114
+ * Home:115
+ * PgUp:116
+ * Del:117
+ * F4: 118
+ * End:119
+ * F2:120
+ * PgDn:121
+ * F1:122
+ * Lt:123
+ * Rt:124
+ * Dn:125
+ * Up:126
+ */
+
+
+/*
+ * Optimize non-blocking calls to "CheckEvents()"
+ * Idea from "Maarten Hazewinkel <mmhazewi@cs.ruu.nl>"
+ */
+#define EVENT_TICKS 6
+
+
+/*
+ * Check for Events, return TRUE if we process any
+ *
+ * Hack -- Handle AppleEvents if appropriate (ignore result code).
+ */
+static bool CheckEvents(bool wait)
+{
+ EventRecord event;
+
+ WindowPtr w;
+
+ Rect r;
+
+ long newsize;
+
+ int ch, ck;
+
+ int mc, ms, mo, mx;
+
+ int i;
+
+ term_data *td = NULL;
+
+ UInt32 curTicks;
+
+ static UInt32 lastTicks = 0L;
+
+
+ /* Access the clock */
+ curTicks = TickCount();
+
+ /* Hack -- Allow efficient checking for non-pending events */
+ if (!wait && (curTicks < lastTicks + EVENT_TICKS)) return (FALSE);
+
+ /* Timestamp last check */
+ lastTicks = curTicks;
+
+ /* Let the "system" run */
+ SystemTask();
+
+ /* Get an event (or null) */
+ WaitNextEvent(everyEvent, &event, 0L, nil);
+
+ /* Hack -- Nothing is ready yet */
+ if (event.what == nullEvent) return (FALSE);
+
+
+ /* Analyze the event */
+ switch (event.what)
+ {
+
+#if 0
+
+ case activateEvt:
+ {
+ w = (WindowPtr)event.message;
+
+ activate(w);
+
+ break;
+ }
+
+#endif
+
+ case updateEvt:
+ {
+ /* Extract the window */
+ w = (WindowPtr)event.message;
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == w) td = &data[i];
+ }
+
+ /* Hack XXX XXX XXX */
+ BeginUpdate(w);
+ EndUpdate(w);
+
+ /* Redraw the window */
+ if (td) term_data_redraw(td);
+
+ break;
+ }
+
+ case keyDown:
+ case autoKey:
+ {
+ /* Extract some modifiers */
+ mc = (event.modifiers & controlKey) ? TRUE : FALSE;
+ ms = (event.modifiers & shiftKey) ? TRUE : FALSE;
+ mo = (event.modifiers & optionKey) ? TRUE : FALSE;
+ mx = (event.modifiers & cmdKey) ? TRUE : FALSE;
+
+ /* Keypress: (only "valid" if ck < 96) */
+ ch = (event.message & charCodeMask) & 255;
+
+ /* Keycode: see table above */
+ ck = ((event.message & keyCodeMask) >> 8) & 255;
+
+ /* Command + "normal key" -> menu action */
+ if (mx && (ck < 64))
+ {
+ /* Hack -- Prepare the menus */
+ setup_menus();
+
+ /* Mega-Hack -- allow easy exit if nothing to save */
+ /* if (!character_generated && (ch=='Q' || ch=='q')) ch = 'e'; */
+
+ /* Run the Menu-Handler */
+ menu(MenuKey(ch));
+
+ /* Turn off the menus */
+ HiliteMenu(0);
+
+ /* Done */
+ break;
+ }
+
+
+ /* Hide the mouse pointer */
+ ObscureCursor();
+
+ /* Normal key -> simple keypress */
+ if ((ck < 64) || (ck == 93))
+ {
+ /* Enqueue the keypress */
+ Term_keypress(ch);
+ }
+
+ /* Keypad keys -> trigger plus simple keypress */
+ else if (!mc && !ms && !mo && !mx && (ck < 96))
+ {
+ /* Hack -- "enter" is confused */
+ if (ck == 76) ch = '\n';
+
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send the "keypad" modifier */
+ Term_keypress('K');
+
+ /* Send the "ascii" keypress */
+ Term_keypress(ch);
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+ }
+
+ /* Bizarre key -> encoded keypress */
+ else if (ck <= 127)
+ {
+ /* Begin special trigger */
+ Term_keypress(31);
+
+ /* Send some modifier keys */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (mo) Term_keypress('O');
+ if (mx) Term_keypress('X');
+
+ /* Downshift and encode the keycode */
+ Term_keypress(I2D((ck - 64) / 10));
+ Term_keypress(I2D((ck - 64) % 10));
+
+ /* Terminate the trigger */
+ Term_keypress(13);
+ }
+
+ break;
+ }
+
+ case mouseDown:
+ {
+ int code;
+
+ /* Analyze click location */
+ code = FindWindow(event.where, &w);
+
+ /* Find the window */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Skip dead windows */
+ if (!data[i].t) continue;
+
+ /* Notice matches */
+ if (data[i].w == w) td = &data[i];
+ }
+
+ /* Analyze */
+ switch (code)
+ {
+ case inMenuBar:
+ {
+ setup_menus();
+ menu(MenuSelect(event.where));
+ HiliteMenu(0);
+ break;
+ }
+
+ case inSysWindow:
+ {
+ SystemClick(&event, w);
+ break;
+ }
+
+ case inDrag:
+ {
+ Point p;
+
+ WindowPtr old_win;
+
+ r = qd.screenBits.bounds;
+ r.top += 20; /* GetMBarHeight() XXX XXX XXX */
+ InsetRect(&r, 4, 4);
+ DragWindow(w, event.where, &r);
+
+ /* Oops */
+ if (!td) break;
+
+ /* Save */
+ old_win = active;
+
+ /* Activate */
+ activate(td->w);
+
+ /* Analyze */
+ p.h = td->w->portRect.left;
+ p.v = td->w->portRect.top;
+ LocalToGlobal(&p);
+ td->r.left = p.h;
+ td->r.top = p.v;
+
+ /* Restore */
+ activate(old_win);
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ break;
+ }
+
+ case inGoAway:
+ {
+ /* Oops */
+ if (!td) break;
+
+ /* Track the go-away box */
+ if (TrackGoAway(w, event.where))
+ {
+ /* Not Mapped */
+ td->mapped = FALSE;
+
+ /* Not Mapped */
+ td->t->mapped_flag = FALSE;
+
+ /* Hide the window */
+ HideWindow(td->w);
+ }
+
+ break;
+ }
+
+ case inGrow:
+ {
+ int x, y;
+
+ term *old = Term;
+
+ /* Oops */
+ if (!td) break;
+
+#ifndef ALLOW_BIG_SCREEN
+
+ /* Fake rectangle */
+ r.left = 20 * td->tile_wid + td->size_ow1;
+ r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1;
+ r.top = 1 * td->tile_hgt + td->size_oh1;
+ r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
+
+#else /* ALLOW_BIG_SCREEN */
+
+ /* Fake rectangle */
+ r.left = 20 * td->tile_wid + td->size_ow1;
+ r.right = qd.screenBits.bounds.right;
+ r.top = 1 * td->tile_hgt + td->size_oh1;
+ r.bottom = qd.screenBits.bounds.bottom;
+
+#endif /* ALLOW_BIG_SCREEN */
+
+ /* Grow the rectangle */
+ newsize = GrowWindow(w, event.where, &r);
+
+ /* Handle abort */
+ if (!newsize) break;
+
+ /* Extract the new size in pixels */
+ y = HiWord(newsize) - td->size_oh1 - td->size_oh2;
+ x = LoWord(newsize) - td->size_ow1 - td->size_ow2;
+
+ /* Extract a "close" approximation */
+ td->rows = y / td->tile_hgt;
+ td->cols = x / td->tile_wid;
+
+ /* Apply and Verify */
+ term_data_check_size(td);
+
+ /* Activate */
+ Term_activate(td->t);
+
+ /* Hack -- Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Resize and Redraw */
+ term_data_resize(td);
+ term_data_redraw(td);
+
+ /* Restore */
+ Term_activate(old);
+
+ break;
+ }
+
+ case inContent:
+ {
+ SelectWindow(w);
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* Disk Event -- From "Maarten Hazewinkel" */
+ case diskEvt:
+ {
+ /* check for error when mounting the disk */
+ if (HiWord(event.message) != noErr)
+ {
+ Point p =
+ {120, 120};
+
+ DILoad();
+ DIBadMount(p, event.message);
+ DIUnload();
+ }
+
+ break;
+ }
+
+ /* OS Event -- From "Maarten Hazewinkel" */
+ case osEvt:
+ {
+ switch ((event.message >> 24) & 0x000000FF)
+ {
+ case suspendResumeMessage:
+
+ /* Resuming: activate the front window */
+ if (event.message & resumeFlag)
+ {
+ SetPort(FrontWindow());
+ SetCursor(&qd.arrow);
+ }
+
+ /* Suspend: deactivate the front window */
+ else
+ {
+ /* Nothing */
+ }
+
+ break;
+ }
+
+ break;
+ }
+
+#ifdef USE_SFL_CODE
+
+ /* From "Steve Linberg" and "Maarten Hazewinkel" */
+ case kHighLevelEvent:
+ {
+ /* Process apple events */
+ if (AEProcessAppleEvent(&event) != noErr)
+ {
+ plog("Error in Apple Event Handler!");
+ }
+
+ /* Handle "quit_when_ready" */
+ if (quit_when_ready)
+ {
+ /* Forget */
+ quit_when_ready = FALSE;
+
+ /* Do the menu key */
+ menu(MenuKey('q'));
+
+ /* Turn off the menus */
+ HiliteMenu(0);
+ }
+
+ /* Handle "open_when_ready" */
+ handle_open_when_ready();
+
+ break;
+ }
+
+#endif
+
+ }
+
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+
+
+/*** Some Hooks for various routines ***/
+
+
+/*
+ * Mega-Hack -- emergency lifeboat
+ */
+static void *lifeboat = NULL;
+
+
+/*
+ * Hook to "release" memory
+ */
+#ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */
+static void *hook_rnfree(void *v)
+#else
+static void *hook_rnfree(void *v, size_t size)
+#endif /* NEW_ZVIRT_HOOKS */
+{
+
+#ifndef NEW_ZVIRT_HOOKS /* Oh, no. */
+#pragma unused (size)
+#endif
+
+#ifdef USE_MALLOC
+
+ /* Alternative method */
+ free(v);
+
+#else
+
+ /* Dispose */
+ DisposePtr(v);
+
+#endif
+
+ /* Success */
+ return (NULL);
+}
+
+/*
+ * Hook to "allocate" memory
+ */
+static void *hook_ralloc(size_t size)
+{
+
+#ifdef USE_MALLOC
+
+ /* Make a new pointer */
+ return (malloc(size));
+
+#else
+
+ /* Make a new pointer */
+ return (NewPtr(size));
+
+#endif
+
+}
+
+/*
+ * Hook to handle "out of memory" errors
+ */
+static void *hook_rpanic(size_t size)
+{
+
+#pragma unused (size)
+
+ /* void *mem = NULL; */
+
+ /* Free the lifeboat */
+ if (lifeboat)
+ {
+ /* Free the lifeboat */
+ DisposePtr(lifeboat);
+
+ /* Forget the lifeboat */
+ lifeboat = NULL;
+
+ /* Mega-Hack -- Warning */
+ mac_warning("Running out of Memory!\rAbort this process now!");
+
+ /* Mega-Hack -- Never leave this function */
+ while (TRUE) CheckEvents(TRUE);
+ }
+
+ /* Mega-Hack -- Crash */
+ return (NULL);
+}
+
+
+/*
+ * Hook to tell the user something important
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning message */
+ mac_warning(str);
+}
+
+
+/*
+ * Hook to tell the user something, and then quit
+ */
+static void hook_quit(cptr str)
+{
+ /* Warning if needed */
+ if (str) mac_warning(str);
+
+#ifdef USE_ASYNC_SOUND
+
+ /* Cleanup sound player */
+ cleanup_sound();
+
+#endif /* USE_ASYNC_SOUND */
+
+ /* Dispose of graphic tiles */
+ if (frameP)
+ {
+ /* Unlock */
+ BenSWUnlockFrame(frameP);
+
+ /* Dispose of the GWorld */
+ DisposeGWorld(frameP->framePort);
+
+ /* Dispose of the memory */
+ DisposePtr((Ptr)frameP);
+ }
+
+ /* Write a preference file */
+ save_pref_file();
+
+ /* All done */
+ ExitToShell();
+}
+
+
+/*
+ * Hook to tell the user something, and then crash
+ */
+static void hook_core(cptr str)
+{
+ /* XXX Use the debugger */
+ /* DebugStr(str); */
+
+ /* Warning */
+ if (str) mac_warning(str);
+
+ /* Warn, then save player */
+ mac_warning("Fatal error.\rI will now attempt to save and quit.");
+
+ /* Attempt to save */
+ if (!save_player()) mac_warning("Warning -- save failed!");
+
+ /* Quit */
+ quit(NULL);
+}
+
+
+
+/*** Main program ***/
+
+
+/*
+ * Init some stuff
+ *
+ * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
+ * "Macintosh Save Bug" by using "absolute" path names, since on
+ * System 7 machines anyway, the "current working directory" often
+ * "changes" due to background processes, invalidating any "relative"
+ * path names. Note that the Macintosh is limited to 255 character
+ * path names, so be careful about deeply embedded directories...
+ *
+ * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
+ * "missing lib folder bug" by allowing the user to help find the
+ * "lib" folder by hand if the "application folder" code fails...
+ */
+static void init_stuff(void)
+{
+ int i;
+
+ short vrefnum;
+ long drefnum;
+ long junk;
+
+ SFTypeList types;
+ SFReply reply;
+
+ Rect r;
+ Point topleft;
+
+ char path[1024];
+
+
+ /* Fake rectangle */
+ r.left = 0;
+ r.top = 0;
+ r.right = 344;
+ r.bottom = 188;
+
+ /* Center it */
+ center_rect(&r, &qd.screenBits.bounds);
+
+ /* Extract corner */
+ topleft.v = r.top;
+ topleft.h = r.left;
+
+
+ /* Default to the "lib" folder with the application */
+ refnum_to_name(path, app_dir, app_vol, (char*)("\plib:"));
+
+
+ /* Check until done */
+ while (1)
+ {
+ /* Prepare the paths */
+ init_file_paths(path);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_FILE, "news.txt");
+
+ /* Attempt to open and close that file */
+ if (0 == fd_close(fd_open(path, O_RDONLY))) break;
+
+ /* Warning */
+ plog_fmt("Unable to open the '%s' file.", path);
+
+ /* Warning */
+ plog("The Angband 'lib' folder is probably missing or misplaced.");
+
+ /* Warning */
+ plog("Please 'open' any file in any sub-folder of the 'lib' folder.");
+
+ /* Allow "text" files */
+ types[0] = 'TEXT';
+
+ /* Allow "save" files */
+ types[1] = 'SAVE';
+
+ /* Allow "data" files */
+ types[2] = 'DATA';
+
+ /* Get any file */
+ SFGetFile(topleft, "\p", NULL, 3, types, NULL, &reply);
+
+ /* Allow cancel */
+ if (!reply.good) quit(NULL);
+
+ /* Extract textual file name for given file */
+ GetWDInfo(reply.vRefNum, &vrefnum, &drefnum, &junk);
+ refnum_to_name(path, drefnum, vrefnum, (char*)reply.fName);
+
+ /* Hack -- Remove the "filename" */
+ i = strlen(path) - 1;
+ while ((i > 0) && (path[i] != ':')) i--;
+ if (path[i] == ':') path[i + 1] = '\0';
+
+ /* Hack -- allow "lib" folders */
+ if (suffix(path, "lib:")) continue;
+
+ /* Hack -- Remove the "sub-folder" */
+ i = i - 1;
+ while ((i > 1) && (path[i] != ':')) i--;
+ if (path[i] == ':') path[i + 1] = '\0';
+ }
+}
+
+
+/*
+ * Macintosh Main loop
+ */
+int main(void)
+{
+ int i;
+
+ EventRecord tempEvent;
+ int numberOfMasters = 10;
+
+ /* Increase stack space by 64K */
+ SetApplLimit(GetApplLimit() - 65536L);
+
+ /* Stretch out the heap to full size */
+ MaxApplZone();
+
+ /* Get more Masters */
+ while (numberOfMasters--) MoreMasters();
+
+ /* Set up the Macintosh */
+ InitGraf(&qd.thePort);
+ InitFonts();
+ InitWindows();
+ InitMenus();
+ /* TEInit(); */
+ InitDialogs(NULL);
+ InitCursor();
+
+ /* Flush events */
+ FlushEvents(everyEvent, 0);
+
+ /* Flush events some more (?) */
+ if (EventAvail(everyEvent, &tempEvent)) FlushEvents(everyEvent, 0);
+
+
+#ifdef ANGBAND_LITE_MAC
+
+ /* Nothing */
+
+#else /* ANGBAND_LITE_MAC */
+
+# if defined(powerc) || defined(__powerc)
+
+ /* Assume System 7 */
+
+ /* Assume Color Quickdraw */
+
+# else
+
+ /* Block */
+ if (TRUE)
+ {
+ OSErr err;
+ long versionNumber;
+
+ /* Check the Gestalt */
+ err = Gestalt(gestaltSystemVersion, &versionNumber);
+
+ /* Check the version */
+ if ((err != noErr) || (versionNumber < 0x0700))
+ {
+ quit("You must have System 7 to use this program.");
+ }
+ }
+
+ /* Block */
+ if (TRUE)
+ {
+ SysEnvRec env;
+
+ /* Check the environs */
+ if (SysEnvirons(1, &env) != noErr)
+ {
+ quit("The SysEnvirons call failed!");
+ }
+
+ /* Check for System Seven Stuff */
+ if (env.systemVersion < 0x0700)
+ {
+ quit("You must have System 7 to use this program.");
+ }
+
+ /* Check for Color Quickdraw */
+ if (!env.hasColorQD)
+ {
+ quit("You must have Color Quickdraw to use this program.");
+ }
+ }
+
+# endif
+
+#endif /* ANGBAND_LITE_MAC */
+
+
+#ifdef USE_SFL_CODE
+
+ /* Obtain a "Universal Procedure Pointer" */
+ AEH_Start_UPP = NewAEEventHandlerProc(AEH_Start);
+
+ /* Install the hook (ignore error codes) */
+ AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, AEH_Start_UPP,
+ 0L, FALSE);
+
+ /* Obtain a "Universal Procedure Pointer" */
+ AEH_Quit_UPP = NewAEEventHandlerProc(AEH_Quit);
+
+ /* Install the hook (ignore error codes) */
+ AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, AEH_Quit_UPP,
+ 0L, FALSE);
+
+ /* Obtain a "Universal Procedure Pointer" */
+ AEH_Print_UPP = NewAEEventHandlerProc(AEH_Print);
+
+ /* Install the hook (ignore error codes) */
+ AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, AEH_Print_UPP,
+ 0L, FALSE);
+
+ /* Obtain a "Universal Procedure Pointer" */
+ AEH_Open_UPP = NewAEEventHandlerProc(AEH_Open);
+
+ /* Install the hook (ignore error codes) */
+ AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, AEH_Open_UPP,
+ 0L, FALSE);
+
+#endif
+
+
+ /* Find the current application */
+ SetupAppDir();
+
+
+ /* Mark ourself as the file creator */
+ _fcreator = ANGBAND_CREATOR;
+
+ /* Default to saving a "text" file */
+ _ftype = 'TEXT';
+
+
+#if defined(ALLOW_NO_SAVE_QUITS) && defined(__MWERKS__)
+
+ /* Obtian a "Universal Procedure Pointer" */
+ ynfilterUPP = NewModalFilterProc(ynfilter);
+
+#endif /* ALLOW_NO_SAVE_QUITS && __MWERKS__ */
+
+
+ /* Hook in some "z-virt.c" hooks */
+ rnfree_aux = hook_rnfree;
+ ralloc_aux = hook_ralloc;
+ rpanic_aux = hook_rpanic;
+
+ /* Hooks in some "z-util.c" hooks */
+ plog_aux = hook_plog;
+ quit_aux = hook_quit;
+ core_aux = hook_core;
+
+
+ /* Initialize colors */
+ for (i = 0; i < 256; i++)
+ {
+ u16b rv, gv, bv;
+
+ /* Extract the R,G,B data */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Save the actual color */
+ color_info[i].red = (rv | (rv << 8));
+ color_info[i].green = (gv | (gv << 8));
+ color_info[i].blue = (bv | (bv << 8));
+ }
+
+
+ /* Show the "watch" cursor */
+ SetCursor(*(GetCursor(watchCursor)));
+
+ /* Prepare the menubar */
+ init_menubar();
+
+ /* Prepare the windows */
+ init_windows();
+
+ /* Hack -- process all events */
+ while (CheckEvents(TRUE)) /* loop */;
+
+ /* Reset the cursor */
+ SetCursor(&qd.arrow);
+
+
+ /* Mega-Hack -- Allocate a "lifeboat" */
+ lifeboat = NewPtr(16384);
+
+ /* Note the "system" */
+ ANGBAND_SYS = "mac";
+
+
+ /* Initialize */
+ init_stuff();
+
+ /* Initialize */
+ init_angband();
+
+
+ /* Hack -- process all events */
+ while (CheckEvents(TRUE)) /* loop */;
+
+
+ /* We are now initialized */
+ initialized = TRUE;
+
+
+ /* Handle "open_when_ready" */
+ handle_open_when_ready();
+
+#ifndef SAVEFILE_SCREEN
+
+ /* Prompt the user */
+ /* In [Z], it's currently prtf(17, 23, <msg>); */
+ prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 15);
+
+ /* Flush the prompt */
+ Term_fresh();
+
+ /* Hack -- Process Events Forever */
+ while (TRUE) CheckEvents(TRUE);
+
+#else
+
+ /* Game is in progress */
+ game_in_progress = 1;
+
+ /* Wait for keypress */
+ pause_line(23);
+
+ /* flush input - Warning: without this, _system_ would hang */
+ flush();
+
+ /* Play the game - note the value of the argument */
+ play_game(FALSE);
+
+ /* Quit */
+ quit(NULL);
+
+#endif /* !SAVEFILE_SCREEN */
+}
+
+#endif /* MACINTOSH */
diff --git a/src/main-net.c b/src/main-net.c
new file mode 100644
index 00000000..339d6a73
--- /dev/null
+++ b/src/main-net.c
@@ -0,0 +1,442 @@
+/* File: main-gcu.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband run on Unix/Curses machines.
+ *
+ *
+ * To use this file, you must define "USE_NET" in the Makefile.
+ *
+ *
+ * Note that this file is not "intended" to support non-Unix machines,
+ * nor is it intended to support VMS or other bizarre setups.
+ *
+ * Also, this package assumes that the underlying "curses" handles both
+ * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below.
+ *
+ * This code should work with most versions of "curses" or "ncurses",
+ * and the "main-ncu.c" file (and USE_NCU define) are no longer used.
+ *
+ * See also "USE_CAP" and "main-cap.c" for code that bypasses "curses"
+ * and uses the "termcap" information directly, or even bypasses the
+ * "termcap" information and sends direct vt100 escape sequences.
+ *
+ * This file provides up to 4 term windows.
+ *
+ * This file will attempt to redefine the screen colors to conform to
+ * standard Angband colors. It will only do so if the terminal type
+ * indicates that it can do so. See the page:
+ *
+ * http://www.umr.edu/~keldon/ang-patch/ncurses_color.html
+ *
+ * for information on this.
+ *
+ * Consider the use of "savetty()" and "resetty()". XXX XXX XXX
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_NET
+
+/* The server connection */
+ip_connection net_serv_connection_forge;
+ip_connection *net_serv_connection = &net_serv_connection_forge;
+
+/* The client connection */
+ip_connection net_connection_forge;
+ip_connection *net_connection = &net_connection_forge;
+
+#define PACKET_STOP 255
+#define PACKET_TEXT 254
+#define PACKET_CLEAR 253
+
+/*
+ * Information about a term
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t; /* All term info */
+};
+
+/* Max number of windows on screen */
+#define MAX_TERM_DATA 4
+
+/* Information about our windows */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Hack -- Number of initialized "term" structures
+ */
+static int active = 0;
+
+
+
+
+/*
+ * Suspend/Resume
+ */
+static errr Term_xtra_net_alive(int v)
+{
+ int x, y;
+
+
+ /* Suspend */
+ if (!v)
+ {}
+
+ /* Resume */
+ else
+ {}
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Init the "net" system
+ */
+static void Term_init_net(term *t)
+{
+ term_data *td = (term_data *)(t->data);
+
+ /* Count init's, handle first */
+ if (active++ != 0) return;
+}
+
+
+/*
+ * Nuke the "net" system
+ */
+static void Term_nuke_net(term *t)
+{
+ int x, y;
+ term_data *td = (term_data *)(t->data);
+}
+
+
+
+
+/*
+ * Process events (with optional wait)
+ */
+static errr Term_xtra_net_event(int v)
+{
+ int i, k;
+
+ char buf[2];
+
+ /* Wait */
+ if (v)
+ {
+ /* Wait for one byte */
+ i = zsock.read_simple(net_connection, buf, 1);
+#if 1
+ /* Hack -- Handle bizarre "errors" */
+ // if (!i) exit_game_panic();
+ if (!i) return 1;
+#else
+ /* Try again(must be a new connection) */
+ while (!i)
+ i = zsock.read_simple(net_connection, buf, 1);
+#endif
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Read one byte, if possible */
+ if (zsock.can_read(net_connection))
+ zsock.read_simple(net_connection, buf, 1);
+ }
+
+ /* Ignore "invalid" keys */
+ if (!buf[0]) return (1);
+
+ /* Enqueue the keypress */
+ Term_keypress(buf[0]);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_net_react(void)
+{
+
+#ifdef A_COLOR
+
+ int i;
+
+ /* Cannot handle color redefinition */
+ if (!can_fix_color) return (0);
+
+ /* Set the colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Set one color (note scaling) */
+ init_color(i,
+ angband_color_table[i][1] * 1000 / 255,
+ angband_color_table[i][2] * 1000 / 255,
+ angband_color_table[i][3] * 1000 / 255);
+ }
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_net(int n, int v)
+{
+ term_data *td = (term_data *)(Term->data);
+ char buf[2];
+
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Clear screen */
+ case TERM_XTRA_CLEAR:
+ buf[0] = PACKET_CLEAR;
+ buf[1] = '\0';
+ zsock.write_simple(net_connection, buf);
+ return (0);
+
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ return (0);
+
+ /* Flush the Curses buffer */
+ case TERM_XTRA_FRESH:
+ return (0);
+
+ /* Suspend/Resume curses */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_net_alive(v));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_net_event(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_net_event(FALSE));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* React to events */
+ case TERM_XTRA_REACT:
+ Term_xtra_net_react();
+ return (0);
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Actually MOVE the hardware cursor
+ */
+static errr Term_curs_net(int x, int y)
+{
+ term_data *td = (term_data *)(Term->data);
+
+ /* Literally move the cursor */
+ // DGDGDGD
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase a grid of space
+ * Hack -- try to be "semi-efficient".
+ */
+static errr Term_wipe_net(int x, int y, int n)
+{
+ char buf[2];
+
+ term_data *td = (term_data *)(Term->data);
+
+ buf[0] = PACKET_CLEAR;
+ buf[1] = '\0';
+ zsock.write_simple(net_connection, buf);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_net(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data *)(Term->data);
+ char buf[5];
+
+ buf[0] = PACKET_TEXT;
+ buf[1] = y + 1;
+ buf[2] = x + 1;
+ buf[3] = a + 1;
+ buf[4] = '\0';
+
+ zsock.write_simple(net_connection, buf);
+ zsock.write_simple(net_connection, s);
+
+ buf[0] = PACKET_STOP;
+ buf[1] = '\0';
+ zsock.write_simple(net_connection, buf);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Create a window for the given "term_data" argument.
+ *
+ * Assumes legal arguments.
+ */
+static errr term_data_init_net(term_data *td, int rows, int cols)
+{
+ term *t = &td->t;
+
+ /* Initialize the term */
+ term_init(t, cols, rows, 256);
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Set some hooks */
+ t->init_hook = Term_init_net;
+ t->nuke_hook = Term_nuke_net;
+
+ /* Set some more hooks */
+ t->text_hook = Term_text_net;
+ t->wipe_hook = Term_wipe_net;
+ t->curs_hook = Term_curs_net;
+ t->xtra_hook = Term_xtra_net;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate it */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+static void hook_quit(cptr str)
+{
+ /* Unused */
+ (void)str;
+}
+
+static void net_lost_connection_hook(ip_connection *conn)
+{
+ printf("Lost connection ! ARGGGG\nAccepting a new one...\n");
+ zsock.accept(net_serv_connection, net_connection);
+ zsock.set_lose_connection(net_connection, net_lost_connection_hook);
+ printf("...accepted\n");
+}
+
+/*
+ * Prepare "curses" for use by the file "z-term.c"
+ *
+ * Installs the "hook" functions defined above, and then activates
+ * the main screen "term", which clears the screen and such things.
+ *
+ * Someone should really check the semantics of "initscr()"
+ */
+errr init_net(int argc, char **argv)
+{
+ int i;
+
+ int num_term = MAX_TERM_DATA, next_win = 0;
+ int port = 6666;
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-P"))
+ {
+ port = atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+ /* Activate hooks */
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Create a term */
+ term_data_init_net(&data[0], 25, 80);
+
+ /* Remember the term */
+ angband_term[0] = &data[0].t;
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Remember the active screen */
+ term_screen = &data[0].t;
+
+ /* Initialize the server and wait for thhe client */
+ zsock.setup(net_serv_connection, "127.0.0.1", port, ZSOCK_TYPE_TCP, TRUE);
+ zsock.open(net_serv_connection);
+
+ printf("Accepting...\n");
+ zsock.accept(net_serv_connection, net_connection);
+ zsock.set_lose_connection(net_connection, net_lost_connection_hook);
+ printf("...accepted\n");
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* USE_NET */
diff --git a/src/main-ros.c b/src/main-ros.c
new file mode 100644
index 00000000..124505d2
--- /dev/null
+++ b/src/main-ros.c
@@ -0,0 +1,8670 @@
+/*
+ * File: main-ros.c
+ *
+ * Abstract: Support for RISC OS versions of Angband, including support
+ * for multitasking and dynamic areas.
+ *
+ * Authors: Musus Umbra, Andrew Sidwell, Ben Harrison, and others.
+ *
+ * Licences: Angband licence.
+ */
+
+#ifdef __riscos
+
+#include "angband.h"
+
+/*
+ * Purpose: Support for RISC OS Angband 2.9.x onwards (and variants)
+ *
+ * NB: This code is still under continuous development - if you want to use
+ * it for your own compilation/variant, please contact me so that I can
+ * keep you up to date and give you support :)
+ *
+ * Prerequisites to compiling:
+ *
+ * DeskLib 2.30 or later (earlier versions may be OK though)
+ *
+ * An ANSI C compiler (tested with Acorn's C/C++ and GCC, but should be OK
+ * with any decent compiler)
+ *
+ * My binary distribution (for the templates and other bits)
+ *
+ * Note:
+ * The following symbols are *required* and *must* be defined properly.
+ */
+
+/*
+ * VARIANT & VERSION
+ * These two get variant and version data from Angband itself; older
+ * variants may not have these defined and will have to be altered.
+ */
+#define VARIANT "ToME"
+#define VERSION "2.2.2"
+
+/*
+ * PORTVERSION
+ * This is the port version; it appears in the infobox.
+ */
+#define PORTVERSION "1.29-dev (2003-08-06)"
+
+/*
+ * RISCOS_VARIANT
+ * This must match the entry in the !Variant Obey file, and it must only
+ * contain characters that are valid as part of a RISC OS path variable.
+ * [eg. "Yin-Yangband" is not okay, "EyAngband" is.]
+ */
+#define RISCOS_VARIANT "ToME"
+
+/*
+ * AUTHORS
+ * For the info box. [eg. "Ben Harrison"]
+ */
+#define AUTHORS "DarkGod & co."
+
+/*
+ * PORTERS
+ * For the info box. [eg. "Musus Umbra"]
+ */
+#define PORTERS "A. Sidwell"
+
+/*
+ * ICONNAME
+ * Iconbar icon sprite name eg. "!angband". Note that this must be a valid
+ * sprite name; it may need modifying for long variant names.
+ */
+#define ICONNAME "!"RISCOS_VARIANT
+
+/*
+ * PDEADCHK
+ * This should expand to an expression that is true if the player is dead.
+ * [eg. (p_ptr->is_dead) for Angband or (!alive || dead) for some Zangbands]
+ */
+
+#define PDEADCHK (!alive)
+
+/*
+ * The following symbols control the (optional) file-cache:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * NB: Variants that don't repeatedly read any files whilst running
+ * (eg. vanilla, sang, etc) should NOT define USE_FILECACHE, etc. as
+ * it causes a non-negligable amount of code to be compiled in.
+ *
+ * NB: The file-cache functions require that some code in files.c is modified
+ * to use the cached_* functions. This should be utterly trivial.
+ *
+ * NB: The returned handle from cached_fopen() is almost certainly *NOT*
+ * a |FILE*| (although it may be if the cache cannot accomodate the file).
+ *
+ * Therefore, you *MUST* ensure that any file opened with cached_fopen()
+ * is only ever accessed via cached_fgets() and cached_fclose().
+ *
+ * Failure to do so will result in, ahem, unpleasantness. Extreme
+ * unpleasantness. "Him fall down, go boom."
+ *
+ * This /may/ change in the near future (ie. to apply caching in a
+ * transparent manner), so do keep a backup of files.c (and any other files
+ * you modify). You always keep backups anyway, don't you? Don't you?!
+ */
+
+/*
+ * USE_FILECACHE
+ * if defined then some caching functions will be compiled for use by the
+ * various get_rnd_line(), etc. in files.c. This could be used in a
+ * variety of places that read data repeatedly, but it's up to you to
+ * implement it then.
+ */
+
+/* #define USE_FILECACHE */
+
+/*
+ * SMART_FILECACHE
+ * This causes lines beginning with '#' (and blank lines) to be discarded
+ * when caching files. This should help Zangband 2.2.5+ but could cause
+ * trouble for other variants. If defined, then smart file caching will be
+ * on by default.
+ */
+
+/* #define SMART_FILECACHE */
+
+/*
+ * ABBR_FILECACHE
+ * ABBR_FILECACHE causes data read into file-cache to be compressed (using a
+ * simple set of abbreviations) by default. This can be overridden using a
+ * command line option. If this symbol is not defined then no compression
+ * code will be compiled and the user option will be ignored/unavailable.
+ */
+
+/* #define ABBR_FILECACHE */
+
+/*
+ * Note:
+ * The following symbols control debugging information.
+ */
+
+/*
+ * FE_DEBUG_INFO
+ * If defined, some functions will be compiled to display some info. on the
+ * state of the front-end (accessible) from the '!' user menu.
+ *
+ * NB: For actual releases you should NOT define this symbol since it causes
+ * a non-negligable amount of code/data to be sucked in.
+ */
+/* #define FE_DEBUG_INFO */
+
+/*
+ * USE_DA
+ * If defined, it enables the use of dynamic areas (these are still only
+ * used when the !Variant file allows it). It is likely that this option
+ * will eventually be removed altogether as there is no major advantege
+ * to using DAs over just using the Wimpslot.
+ */
+#define USE_DA
+
+
+/* Constants, etc. ---------------------------------------------------------*/
+
+/* Deal with any weird file-caching symbols */
+#ifndef USE_FILECACHE
+# undef ABBR_FILECACHE
+# undef SMART_FILECACHE
+#endif
+
+/* Maximum terminals */
+#define MAX_TERM_DATA 8
+
+/* Menu entry numbers */
+#define IBAR_MENU_INFO 0
+#define IBAR_MENU_SAVE 1
+#define IBAR_MENU_FULLSCREEN 2
+#define IBAR_MENU_GAMMA 3
+#define IBAR_MENU_SOUND 4
+#define IBAR_MENU_WINDOWS 5
+#define IBAR_MENU_SAVECHOICES 6
+#define IBAR_MENU_QUIT 7
+
+#define TERM_MENU_INFO 0
+#define TERM_MENU_SAVE 1
+#define TERM_MENU_FONT 2
+#define TERM_MENU_WINDOWS 3
+
+/* Icon numbers */
+#define SND_VOL_SLIDER 0
+#define SND_VOL_DOWN 1
+#define SND_VOL_UP 2
+#define SND_ENABLE 3
+
+#define GAMMA_ICN 0
+#define GAMMA_DOWN 1
+#define GAMMA_UP 2
+
+#define SAVE_ICON 2
+#define SAVE_PATH 1
+#define SAVE_OK 0
+#define SAVE_CANCEL 3
+
+/* Position and size of the colours strip in the gamma window */
+#define GC_XOFF 20
+#define GC_YOFF -14
+#define GC_WIDTH 512
+#define GC_HEIGHT 72
+
+/* Maximum and minimum allowed volume levels */
+#define SOUND_VOL_MIN 16
+#define SOUND_VOL_MAX 176
+
+/*--------------------------------------------------------------------------*/
+
+
+#undef rename
+#undef remove
+
+#include "Desklib:Event.h"
+#include "Desklib:EventMsg.h"
+#include "Desklib:Template.h"
+#include "Desklib:Window.h"
+#include "Desklib:Handler.h"
+#include "Desklib:Screen.h"
+#include "Desklib:Menu.h"
+#include "Desklib:Msgs.h"
+#include "Desklib:Icon.h"
+#include "Desklib:Resource.h"
+#include "Desklib:SWI.h"
+#include "Desklib:Time.h"
+#include "Desklib:Sound.h"
+#include "Desklib:KeyCodes.h"
+#include "Desklib:Kbd.h"
+#include "Desklib:GFX.h"
+#include "Desklib:ColourTran.h"
+#include "Desklib:Error.h"
+#include "Desklib:Coord.h"
+#include "Desklib:Slider.h"
+#include "Desklib:Hourglass.h"
+#include "Desklib:Save.h"
+#include "Desklib:Sprite.h"
+#include "Desklib:KernelSWIs.h"
+#include "DeskLib:Filing.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <time.h>
+#include <math.h>
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | We use the hourglass around calls to Wimp_Poll in an attempt to stop
+ | users thinking that the game has 'hung'.
+ | Kamband/Zangband and the Borg in particular can have quite long delays at
+ | times.
+ */
+#define Start_Hourglass \
+ { if ( use_glass && !glass_on ) { glass_on=1; Hourglass_Start(50); } }
+#define Stop_Hourglass \
+ { if ( glass_on ) { glass_on=0; Hourglass_Off(); } }
+
+
+/*--------------------------------------------------------------------------*/
+/* Types */
+/*--------------------------------------------------------------------------*/
+
+/*
+ | A ZapRedraw block
+ */
+typedef struct
+{
+ union
+ {
+ unsigned int value;
+ struct
+ {
+ unsigned int vdu:1;
+ unsigned int double_height:1;
+ unsigned int extension:1;
+ unsigned int padding:29;
+ }
+ bits;
+ }
+ r_flags;
+
+ int r_minx; /* min x of redraw in pixels from LHS, incl */
+ int r_miny; /* min y of redraw in pixels from top, incl */
+ int r_maxx; /* max x of redraw in pixels from LHS, excl */
+ int r_maxy; /* max y of redraw in pixels from top, excl */
+
+ void *r_screen; /* DSA: address of screen to write to (0=>read) */
+ int r_bpl; /* DSA: bytes per raster line */
+
+ int r_bpp; /* log base 2 of bits per pixel */
+ int r_charw; /* width of a character in pixels */
+ int r_charh; /* height of a character in pixels */
+ void *r_caddr; /* DSA: ->character cache | VDU: ->font name */
+ int r_cbpl; /* DSA: #bytes/character line | VDU: x OS offset */
+ int r_cbpc; /* DSA: #bytes/character | VDU: y OS offset */
+
+ int r_linesp; /* line spacing (pixels) */
+
+ void *r_data; /* -> text to display */
+ int r_scrollx; /* see Redraw dox */
+ int r_scrolly; /* see Redraw dox */
+
+ void *r_palette; /* -> palette lookup table */
+ int r_for; /* foreground colour at start of line */
+ int r_bac; /* background colour at start of line */
+
+ void *r_workarea; /* -> word aligned workspace */
+
+ int r_magx; /* log2 x OS coords per pixel */
+ int r_magy; /* log2 y OS coords per pixel */
+
+ int r_xsize; /* width of screen in pixels */
+ int r_ysize; /* height of screen in pixels */
+
+ int r_mode; /* current screen mode */
+}
+ZapRedrawBlock;
+
+
+/*
+ | We cache font data using an array of 'font handles' (since there is a
+ | known maximum no. of fonts required).
+ | This is what a font 'handle' looks like:
+ */
+typedef struct
+{
+ char *name; /* font name */
+ int usage; /* usage count */
+ int w, h; /* width, height */
+ int f, l; /* first and last character defined */
+ void *bpp_1; /* source bitmap */
+ void *bpp_n; /* bitmap for the current screen mode */
+}
+ZapFont;
+
+/*
+ | A struct to hold all the data relevant to a term window
+ */
+typedef struct
+{
+ term t; /* The Term itself */
+ window_handle w; /* Window handle */
+ ZapFont *font; /* Font */
+ wimp_box changed_box; /* Area out of date */
+ struct
+ {
+ wimp_point pos; /* Cursor position */
+ BOOL visible; /* visibility flag */
+ }
+ cursor;
+ char name[12]; /* Name to give menus opened from the term */
+ int def_open; /* Open by default? */
+ wimp_box def_pos; /* default position */
+ wimp_point def_scroll; /* default scroll offset */
+ int unopened; /* Has this window not been opened yet? */
+}
+term_data;
+
+
+
+/*--------------------------------------------------------------------------*/
+/* ZapRedraw SWI numbers */
+/*--------------------------------------------------------------------------*/
+
+#define SWI_ZapRedraw_ 0x48480
+#define SWI_ZapRedraw_RedrawArea (SWI_ZapRedraw_ + 0x00)
+#define SWI_ZapRedraw_GetPaletteEntry (SWI_ZapRedraw_ + 0x01)
+#define SWI_ZapRedraw_RedrawRaster (SWI_ZapRedraw_ + 0x02)
+#define SWI_ZapRedraw_ConvertBitmap (SWI_ZapRedraw_ + 0x03)
+#define SWI_ZapRedraw_PrepareDataLine (SWI_ZapRedraw_ + 0x04)
+#define SWI_ZapRedraw_AddCursor (SWI_ZapRedraw_ + 0x05)
+#define SWI_ZapRedraw_FindCharacter (SWI_ZapRedraw_ + 0x06)
+#define SWI_ZapRedraw_MoveBytes (SWI_ZapRedraw_ + 0x07)
+#define SWI_ZapRedraw_CachedCharSize (SWI_ZapRedraw_ + 0x08)
+#define SWI_ZapRedraw_ConvBitmapChar (SWI_ZapRedraw_ + 0x09)
+#define SWI_ZapRedraw_CreatePalette (SWI_ZapRedraw_ + 0x0a)
+#define SWI_ZapRedraw_InsertChar (SWI_ZapRedraw_ + 0x0b)
+#define SWI_ZapRedraw_ReadSystemChars (SWI_ZapRedraw_ + 0x0c)
+#define SWI_ZapRedraw_ReverseBitmaps (SWI_ZapRedraw_ + 0x0d)
+#define SWI_ZapRedraw_ReadVduVars (SWI_ZapRedraw_ + 0x0e)
+#define SWI_ZapRedraw_GetRectangle (SWI_ZapRedraw_ + 0x0f)
+#define SWI_ZapRedraw_AddVduBitmaps (SWI_ZapRedraw_ + 0x10)
+#define SWI_ZapRedraw_CacheFontChars (SWI_ZapRedraw_ + 0x11)
+#define SWI_ZapRedraw_SpriteSize (SWI_ZapRedraw_ + 0x12)
+#define SWI_ZapRedraw_RedrawWindow (SWI_ZapRedraw_ + 0x13)
+
+
+/*
+ | Other SWI numbers that aren't defined in DeskLib's SWI.h:
+ */
+#define SWI_OS_ScreenMode 0x65
+#define SWI_OS_DynamicArea 0x66
+#define SWI_ColourTrans_ReturnColourNumber 0x40744
+#define SWI_Wimp_ReportError 0x400df
+#define SWI_PlayIt_Volume 0x4d146
+
+
+
+/*--------------------------------------------------------------------------*
+ | File scope variables |
+ *--------------------------------------------------------------------------*/
+static int ftype = 0xffd; /* hack so saved games get the right type */
+static int filehandle[16]; /* we keep track of open files with this */
+static int openfiles = 0; /* how many files are currently open */
+
+/*
+ | Paths we use...
+ */
+static char resource_path[260] = ""; /* Path pointng to "!Angband.Lib." */
+static char scrap_path[260] = ""; /* Path to create scrap files on */
+static char choices_file[3][260] =
+{ "", "", "" }; /* Choices paths (read/write, mirror, read) */
+static char alarm_file[2][260] =
+{ "", "" }; /* Alarm choices paths (read/write, mirror, read) */
+/*
+ | So we can use something more meaningful later...
+ | NB: Mirror is only meaningful for Choices and we don't
+ | even reserve space for alarm_file[CHFILE_MIRROR].
+ */
+#define CHFILE_WRITE 0
+#define CHFILE_READ 1
+#define CHFILE_MIRROR 2
+
+/*
+ | Other 'globals':
+ */
+static int initialised = 0; /* Used to determine whether to try to save */
+static int game_in_progress = 0; /* if Quit (or core() is called), etc. */
+
+static byte a_palette[256][4]; /* a copy of the raw Angband palette */
+static unsigned int palette[256]; /* palette as gamma'd bbggrrxx words */
+static unsigned int zpalette[256]; /* And our version for ZapRedraw */
+static double gamma = 1.0; /* assume gamma of 1.0 if unspecified */
+
+static int enable_sound = 0; /* enable sound FX */
+static int sound_volume = 127; /* Full volume */
+static int force_mono = 0; /* force monochrome */
+static int start_fullscreen = 0; /* start up full screen (added in 1.18) */
+static int hack_flush = 0; /* Should TERM_XTRA_FLUSH wait for all keys to be released? */
+static int flush_scrap = 1; /* Should any scrapfiles (incl. filecache) be deleted at exit? */
+static int max_file_cache_size = 64 << 10;
+static unsigned int vfiletype;
+static int allow_iclear_hack = 0; /* Allow the hideously evil Iclear workaround thing */
+static int alarm_type = 0; /* is there an alarm set? */
+static int alarm_h = 0, alarm_m = 0; /* alarm time (midnight) */
+static char alarm_message[80] = "Time for bed!"; /* the message to give */
+static int alarm_disp = 0; /* is the alarm being displayed? */
+static int alarm_beep = 0; /* should be beep? */
+static const char *alarm_types[] =
+{ "Off", "On (one-shot)", "On (repeating)", "On (one-shot)" };
+static unsigned int alarm_lastcheck = 0;
+
+
+/* A little macro to save some typing later: */
+#define COLOUR_CHANGED(x) \
+ ( (angband_color_table[x][1]!=a_palette[x][1]) || \
+ (angband_color_table[x][2]!=a_palette[x][2]) || \
+ (angband_color_table[x][3]!=a_palette[x][3]) )
+
+static int got_caret = 0; /* Do we own the caret? */
+static int key_pressed = 0; /* 'Key has been pressed' Flag */
+static int use_glass = 1; /* use the hourglass between WimpPolls? */
+static int glass_on = 1; /* is the hourglass on? */
+static int user_menu_active = FALSE; /* set to TRUE when the user menu is active */
+
+/* Font system variables */
+static ZapFont fonts[MAX_TERM_DATA + 1]; /* The +1 is for the system font */
+
+/* The system font is always font 0 */
+#define SYSTEM_FONT (&(fonts[0]))
+
+/* Term system variables */
+static term_data data[MAX_TERM_DATA]; /* One per term */
+
+#ifndef FULLSCREEN_ONLY
+static char r_data[24 * (80 * 5 + 4) + 25 * 4]; /* buffer for ZapRedraw data */
+
+/* Wimp variables */
+static icon_handle ibar_icon; /* Iconbar icon handle */
+static window_handle info_box; /* handle of the info window */
+static window_handle gamma_win; /* gamma correction window */
+static window_handle sound_win; /* sound options window */
+static window_handle save_box; /* The savebox */
+static menu_ptr ibar_menu; /* Iconbar menu */
+static menu_ptr term_menu; /* Term window menu */
+static menu_ptr wind_menu; /* windows (sub) menu */
+static menu_ptr font_menu; /* Font (sub)menu */
+
+static save_saveblock *saveblk = NULL; /* For the save box */
+
+/* List of Wimp messages we want to be given */
+static int message_list[] =
+{
+ message_MODECHANGE,
+ message_PALETTECHANGE,
+
+ /* For the savebox */
+ message_MENUWARN,
+ message_DATASAVEACK,
+
+ message_PREQUIT,
+ 0
+};
+
+
+static term_data *menu_term; /* term the last menu was opened for */
+
+#endif /* FULLSCREEN_ONLY */
+
+static ZapRedrawBlock zrb; /* a redraw block */
+
+/* Cursor colour */
+#define CURSOR_COLOUR 255 /* Cursor's Angband colour */
+#define CURSOR_RGB 0x00ffff00 /* if undefined, use bbggrrxx */
+
+static int cursor_rgb = -1; /* colour to use for cursor */
+
+static int fullscreen_mode = 0; /* screen mode in use */
+static int old_screenmode = 0; /* Mode we started out in */
+static int *fullscreen_font = 0; /* font data for fullscreen use */
+static int *fullscreen_base = 0; /* base address of screen */
+static int fullscreen_height; /* height of the fullscreen font */
+static int fullscreen_topline; /* raster offset of fullscreen */
+
+#define KEYPRESS_QUIT 0x1cc /* F12 gets back to the desktop */
+#define TERM_TOPLINE_HR 32 /* vertical pixel offset in mode 27 */
+#define TERM_TOPLINE_LR 16 /* vertical pixel offset in mode 12 */
+#define TIME_LINE 26 /* Line to display the clock on */
+
+/* text to display at the bottom left of the fullscreen display */
+static const char *fs_quit_key_text = "Press f12 to return to the desktop";
+static const char *alarm_cancel_text = "(Press ^Escape to cancel the alarm)";
+
+/* Debugging flags, etc. */
+static int log_g_malloc = 0; /* Log calls to ralloc, etc */
+static int show_sound_alloc = 0; /* Log sound mappings, etc */
+
+/* Activate file caching? */
+#ifdef USE_FILECACHE
+static int use_filecache = TRUE;
+#else
+static int use_filecache = FALSE;
+#endif
+
+/* Cripple some things to save memory */
+static int minimise_memory = 0;
+
+/* Forward declarations of some of the Full Screen Mode stuff */
+static void enter_fullscreen_mode(void);
+static void leave_fullscreen_mode(void);
+static void set_keys(int claim);
+
+/* Forwards declarations of the sound stuff */
+static void initialise_sound(void);
+static void play_sound(int event);
+
+/* Forward declarations of Term hooks, etc. */
+static void Term_init_acn(term *t);
+static errr Term_user_acn(int n);
+
+#ifndef FULLSCREEN_ONLY
+static errr Term_curs_acn(int x, int y);
+static errr Term_text_acn(int x, int y, int n, byte a, cptr s);
+static errr Term_xtra_acn(int n, int v);
+static errr Term_wipe_acn(int x, int y, int n);
+static errr Term_xtra_acn_check(void);
+static errr Term_xtra_acn_event(void);
+static errr Term_xtra_acn_react(void);
+#endif /* FULLSCREEN_ONLY */
+
+static errr Term_curs_acnFS(int x, int y);
+static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
+static errr Term_xtra_acnFS(int n, int v);
+static errr Term_wipe_acnFS(int x, int y, int n);
+static errr Term_xtra_acn_checkFS(void);
+static errr Term_xtra_acn_clearFS(void);
+static errr Term_xtra_acn_eventFS(void);
+static errr Term_xtra_acn_reactFS(int force);
+
+#ifdef USE_DA
+/* Forward declarations of the memory stuff */
+static void init_memory(int, int);
+#endif
+
+/* Forward declarations of the alarm stuff */
+static void check_alarm(void);
+#ifndef FULLSCREEN_ONLY
+static void trigger_alarm_desktop(void);
+#endif /* FULLSCREEN_ONLY */
+static void ack_alarm(void);
+static void write_alarm_choices(void);
+static void read_alarm_choices(void);
+
+
+/* This just shows some debugging info (if enabled with FE_DEBUG_INFO) */
+static void show_debug_info(void);
+
+
+
+/* File-caching functions (if enabled at compile time) */
+#ifdef USE_FILECACHE
+FILE *cached_fopen(char *name, char *mode);
+errr cached_fclose(FILE *fch);
+errr cached_fgets(FILE *fch, char *buffer, int max_len);
+#endif
+
+/*
+ | These functions act as malloc/free, but (if possible) using memory
+ | in the 'Game' Dynamic Area created by init_memory()
+ | We attach these functions to the ralloc_aux and rnfree_aux hooks
+ | that z-virt.c provides.
+ */
+#ifdef USE_DA
+static vptr g_malloc(huge size);
+static vptr g_free(vptr blk, huge);
+#else
+ #define g_malloc(size) malloc(size);
+ #define g_free(block, size) free(block);
+#endif
+
+/*
+ | These functions act as malloc/free, but (if possible) using memory
+ | in the 'Fonts' Dynamic Area created by init_memory()
+ */
+#ifdef USE_DA
+static void* f_malloc(size_t size);
+static void f_free(void *blk);
+#else
+ #define f_malloc(size) malloc(size);
+ #define f_free(block) free(block);
+#endif
+
+/*
+ | These two functions perpetrate great evil to stop IClear from mucking
+ | with the cursor keys in fullscreen mode.
+ */
+static void iclear_hack(void);
+static void remove_iclear_hack(void);
+
+/*
+ | We use this to locate the choices file(s)...
+ */
+static char *find_choices(int write);
+static char *find_choices_mirror(void);
+static char *find_alarmfile(int write);
+
+
+
+/*
+ | This function is supplied as a wrapper to the save_player function.
+ |
+ | Its purpose is to change the filename that the game will be saved with
+ | the leafname "!!PANIC!!" so that panic saves that break the savefile
+ | won't overwrite the original savefile.
+ |
+ | To get this to work, you'll need to ammend files.c and change the call
+ | to save_player in the panic save function(s) (search for "panic save")
+ | to a call to save_player_panic_acn. You can declare a prototype for
+ | the function if you like.
+ */
+
+extern int save_player_panic_acn(void)
+{
+ char *e, *l;
+
+ /* Find the final / in the savefile name */
+ for (l = e = savefile; *e; e++)
+ if (*e == '/')
+ {
+ l = e + 1;
+ }
+
+ /* Write over the current leaf with the special panic one */
+ strcpy(l, "!!PANIC!!");
+
+ /* save the game */
+ return save_player();
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* Error reporting, etc. */
+/*--------------------------------------------------------------------------*/
+
+
+/* Tell the user something important */
+static void plog_hook(cptr str)
+{
+ Msgs_Report(1, "err.plog", str);
+}
+
+/* Tell the user something, then quit */
+static void quit_hook(cptr str)
+{
+ /* str may be null */
+ if (str) Msgs_Report(1, "err.quit", str);
+ exit(0);
+}
+
+/* Tell the user something then crash ;) */
+static void core_hook(cptr str)
+{
+ Msgs_Report(1, "err.core", str);
+
+ if (game_in_progress && character_generated)
+ save_player_panic_acn();
+
+ quit(NULL);
+}
+
+static void debug(const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[260];
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+ plog(buffer);
+}
+
+
+
+/*
+static void oserror_handler( int sig )
+{
+ core(_kernel_last_oserror()->errmess);
+}
+*/
+
+
+/*--------------------------------------------------------------------------*/
+/* File handling */
+/*--------------------------------------------------------------------------*/
+
+static int myFile_Open(const char *name, int mode)
+{
+ int handle;
+ if (SWI(2, 1, SWI_OS_Find, mode, name, /**/ &handle))
+ return 0;
+ return handle;
+}
+
+static int myFile_Size(const char *name)
+{
+ int size, type;
+ if (SWI(2, 5, SWI_OS_File, 17, name, /**/ &type, 0, 0, 0, &size))
+ return -2;
+ return type ? size : -1;
+}
+
+static os_error *myFile_Close(const int handle)
+{
+ return SWI(2, 0, SWI_OS_Find, 0, handle);
+}
+
+static os_error *myFile_Seek(const int handle, const int offset)
+{
+ return SWI(3, 0, SWI_OS_Args, 1, handle, offset);
+}
+
+static int myFile_WriteBytes(const int handle, const void *buf, const int n)
+{
+ int ntf;
+ if (SWI(4, 4, SWI_OS_GBPB, 2, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
+ return n;
+ return ntf;
+}
+
+static int myFile_ReadBytes(const int handle, void *buf, const int n)
+{
+ int ntf;
+ if (SWI(4, 4, SWI_OS_GBPB, 4, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
+ return n;
+ return ntf;
+}
+
+static os_error *myFile_SetType(const char *n, const int type)
+{
+ return SWI(3, 0, SWI_OS_File, 18, n, type);
+}
+
+
+static int myFile_Extent(const int handle)
+{
+ int ext;
+ if (SWI(2, 3, SWI_OS_Args, 2, handle, /**/ NULL, NULL, &ext))
+ return -1;
+ return ext;
+}
+
+
+/*
+ | Determine if one file is newer than another.
+ |
+ | The filenames should be specified in RISC OS style.
+ |
+ | Returns -1 if 'a' is newer than 'b'.
+ */
+static int file_is_newer(const char *a, const char *b)
+{
+ os_error *e;
+ struct
+ {
+ unsigned int msw;
+ unsigned int lsw;
+ }
+ a_time;
+ struct
+ {
+ unsigned int msw;
+ unsigned int lsw;
+ }
+ b_time;
+ int a_type, b_type;
+
+ /* Get the datestamp of the 'a' file */
+ e = SWI(2, 4, SWI_OS_File,
+ /* In */
+ 17, (int)a,
+ /* Out */
+ &a_type, /* object type */
+ NULL, &(a_time.msw), /* Load Addr */
+ &(a_time.lsw) /* Exec Addr */
+ );
+ if (e)
+ {
+ core(e->errmess);
+ }
+
+
+ /* Get the datestamp of the 'b' file */
+ e = SWI(2, 4, SWI_OS_File,
+ /* In */
+ 17, (int)b,
+ /* Out */
+ &b_type, /* object type */
+ NULL, &(b_time.msw), /* Load Addr */
+ &(b_time.lsw) /* Exec Addr */
+ );
+ if (e)
+ {
+ core(e->errmess);
+ }
+
+ /* If 'b' doesn't exist then 'b' is OOD. */
+ if (!b_type)
+ {
+ return -1;
+ }
+ /* If 'a' doesn't exist then 'b' isn't OOD. (?) */
+ if (!a_type)
+ {
+ return 0;
+ }
+
+ /* Compare the timestamps (assume that the files are typed) */
+ if ((a_time.msw & 0xff) >= (b_time.msw & 0xff))
+ if ((a_time.lsw) > (b_time.lsw))
+ return -1; /* OOD */
+ return 0; /* Not OOD */
+}
+
+
+/*
+ | As fprintf, but outout to all files (if their handles are non zero).
+ | NB: void type.
+ */
+static void f2printf(FILE *a, FILE *b, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[2048];
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+ if (a)
+ {
+ fprintf(a, buffer);
+ }
+ if (b)
+ {
+ fprintf(b, buffer);
+ }
+ va_end(ap);
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Clean up (ie. close files, etc). */
+/*--------------------------------------------------------------------------*/
+
+static void final_acn(void)
+{
+ int i;
+
+ for (i = 0; i < openfiles; i++)
+ myFile_Close(filehandle[i]);
+
+ if (fullscreen_mode)
+ {
+ /* Restore the screen mode */
+ Wimp_SetMode(old_screenmode);
+
+ /* Restore the various soft keys */
+ set_keys(FALSE);
+
+ /*
+ | Hack: Early WIMP versions do the "Press SPACE" thing, or something
+ | odd. It's bloody annoying, whatever it is...
+ */
+ if (event_wimpversion < 300)
+ Wimp_CommandWindow(-1);
+ }
+
+ if (flush_scrap && *scrap_path)
+ {
+ char tmp[512];
+ strcpy(tmp, scrap_path);
+ tmp[strlen(tmp) - 1] = 0; /* Remove trailing dot */
+ SWI(4, 0, SWI_OS_FSControl, 27, tmp, 0, 1); /* ie. "*Wipe <scrapdir> r~c~v~f" */
+ }
+
+#ifdef FULLSCREEN_ONLY
+ Wimp_CommandWindow(-1);
+#endif /* FULLSCREEN_ONLY */
+
+ Stop_Hourglass;
+}
+
+
+/*--------------------------------------------------------------------------*
+ | Various UNIX-like support funtions |
+ *--------------------------------------------------------------------------*/
+
+/*
+ | Hack: determine whether filenames should be truncated to 10 chars or not.
+ |
+ | Needed since RO2 (and RO3 with Truncate configured off) will return
+ | errors instead of automatically truncating long filenames.
+ */
+static int truncate_names(void)
+{
+ int r1, r2;
+
+ /* First, check the OS version */
+ OS_Byte(osbyte_READOSIDENTIFIER, 0x00, 0xff, &r1, &r2);
+
+ /* Assume that we need to truncate if running under RO2 */
+ if (r1 == 0xa1 || r1 == 0xa2)
+ return TRUE;
+
+ /* Okay, so we've got RO3 (or later), so check the CMOS RAM */
+ OS_Byte(osbyte_READCMOSRAM, 28, 0, &r1, &r2);
+
+ /* Bit 0 of byte 28 is the Truncate flag */
+ return !(r2 & 1);
+}
+
+
+/*
+ | The PathName translation is now done by two separate functions:
+ | unixify_name() and riscosify_name().
+ |
+ | This is done because only the UNIX=>RISCOS translation should
+ | ever affect the length of the leafname (ie. by truncating it to
+ | 10 chars if necessary).
+ |
+ | Note that the two functions are identical but for the truncation
+ | check so all that's really been done is that translate_name() now
+ | takes an extra argument: 'trunc' that controls whether truncation
+ | is applied, and riscosify and unixify just call translate_name().
+ */
+static char *translate_name(const char *path, int trunc)
+{
+ static char buf[260];
+ char c, *p;
+
+ /* Copy 'path' into 'buf', swapping dots and slashes */
+ p = buf; /* Output position */
+ do
+ {
+ c = *path++;
+ if (c == '/')
+ c = '.';
+ else if (c == '.')
+ c = '/';
+ *p++ = c;
+ }
+ while (c); /* Terminator /is/ copied */
+
+ /*
+ | When saving a game, the old game is renamed as
+ | "SavedGame.old", the new one is saved as "SavedGame.new",
+ | "SavedGame.old" is deleted, "SavedGame.new" is renamed
+ | as "SavedGame". This will go wrong on a Filecore based filing
+ | system if the saved game has a leafname > 8 chars.
+ */
+
+ if ((p = strstr(buf, "/old")) == NULL)
+ {
+ p = strstr(buf, "/new");
+ }
+ if (!p)
+ {
+ ftype = 0xffd;
+ }
+ else
+ {
+ char *q = strrchr(buf, '.');
+ if (q)
+ if (p - q > 6)
+ {
+ memmove(q + 6, p, 5);
+ }
+ ftype = vfiletype;
+ }
+
+ /*
+ | Hack: Do we need to truncate the leafname?
+ */
+ if (trunc)
+ {
+ if (truncate_names())
+ {
+ char *a, *b;
+ /*
+ | Assume that only the leafname needs attention
+ | (this should be true for any variant)
+ */
+ for (a = b = buf; *a; a++)
+ if (*a == '.')
+ b = a + 1;
+ /*
+ | Now b points to the start of the leafname.
+ | If the leafname is >10 chars, write over the 10th with a
+ | terminator.
+ */
+ if (strlen(b) > 10)
+ {
+ b[10] = 0;
+ };
+ }
+ }
+
+ return buf;
+}
+
+
+extern char *riscosify_name(const char *path)
+{
+ return translate_name(path, TRUE);
+}
+
+static char *unixify_name(const char *path)
+{
+ return translate_name(path, FALSE);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+
+/* Open a file [as fopen()] but translate the requested filename first */
+
+FILE *my_fopen(const char *f, const char *m)
+{
+ FILE *fp;
+ char *n = riscosify_name(f); /* translate for RO */
+
+ /* Try to open the file */
+ fp = fopen(n, m);
+
+ /* If it succeded and the file was opened for binary output
+ | then set the type according to the 'ftype' hack.
+ | NB: This will fail on some filing systems.
+ */
+
+ if (fp && strstr(m, "wb"))
+ {
+ myFile_SetType(n, ftype);
+ }
+
+ return fp;
+}
+
+
+
+
+/* Close a file, a la fclose() */
+
+errr my_fclose(FILE *fp)
+{
+ /* Close the file, return 1 for an error, 0 otherwise */
+ return fclose(fp) ? 1 : 0;
+}
+
+
+/* Open/Create a file */
+
+int fd_make(cptr file, int mode)
+{
+ char *real_path;
+ int handle;
+
+ /* Translate the filename into a RISCOS one */
+ real_path = riscosify_name(file);
+
+ /* Try to OPENOUT the file (no path, error if dir or not found) */
+ handle = myFile_Open(real_path, 0x8f);
+
+ /* Check for failure */
+ if (!handle)
+ {
+ return -1;
+ }
+
+ /* Try to set the filetype according to the ftype hack */
+ myFile_SetType(real_path, ftype);
+
+ /* We keep track of up to 16 open files at any given time */
+ if (openfiles < 16)
+ filehandle[openfiles++] = handle;
+
+ return (handle);
+}
+
+
+/* Delete a file [as remove()] */
+errr fd_kill(cptr file)
+{
+ return remove(riscosify_name(file)) ? 1 : 0;
+}
+
+
+/* Rename a file [as rename()] */
+errr fd_move(cptr old, cptr new)
+{
+ char new_[260];
+ strcpy(new_, riscosify_name(new));
+ return rename(riscosify_name(old), new_) ? 1 : 0;
+}
+
+/* Open a file */
+int fd_open(cptr path, int flags)
+{
+ int handle = 0;
+ char *real_path = riscosify_name(path);
+
+ switch (flags & 0x0f)
+ {
+ case O_RDONLY: /* Read only */
+ handle = myFile_Open(real_path, 0x4f);
+ break;
+ case O_WRONLY: /* Write only */
+ case O_RDWR: /* Read/Write */
+ handle = myFile_Open(real_path, 0xcf);
+ }
+
+ /* Check for failure */
+ if (!handle)
+ {
+ return (-1);
+ }
+
+ /* Keep track of upto 16 open files... */
+ if (openfiles < 16)
+ filehandle[openfiles++] = handle;
+
+ return (handle);
+}
+
+
+/* Close a file opened with fd_make or fd_open */
+errr fd_close(int handle)
+{
+ int i;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+
+ /* Try to close the file */
+ if (myFile_Close(handle))
+ {
+ return 1;
+ }
+
+ /* Mark the file as closed in our array of file handles */
+ openfiles--;
+ /* Find the entry in the array (if it exists) */
+ for (i = 0; i < 16; i++)
+ if (filehandle[i] == handle)
+ {
+ break;
+ }
+ /* Shuffle the remaining entries down */
+ for (; i < openfiles; i++)
+ filehandle[i] = filehandle[i + 1];
+
+ return 0; /* Sucess */
+}
+
+
+
+/* Read some bytes from a file */
+errr fd_read(int handle, char *buf, huge nbytes)
+{
+ int unread;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+ unread = myFile_ReadBytes(handle, buf, (int)nbytes);
+
+ return unread ? 1 : 0;
+}
+
+
+/* Write some bytes to a file */
+errr fd_write(int handle, const char *buf, huge nbytes)
+{
+ int unwritten;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+ unwritten = myFile_WriteBytes(handle, (const void *)buf, (int)nbytes);
+
+ return unwritten ? 1 : 0;
+}
+
+
+/* Seek in a file */
+errr fd_seek(int handle, huge offset)
+{
+ os_error *e;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+ e = myFile_Seek(handle, (int)offset);
+
+ return e ? 1 : 0;
+}
+
+
+/* RISC OS provides no file locking facilities, so: */
+errr fd_lock(int handle, int what)
+{
+ return 0;
+}
+
+
+/* Get a temporary filename */
+errr path_temp(char *buf, int max)
+{
+
+ /*
+ | New in 1.25 - use the scrap path we decided on earlier, or
+ | fall back on tmpnam() if that fails for some reason.
+ */
+ if (*scrap_path)
+ {
+ time_t t;
+ int m;
+ char tmp[512];
+
+ time(&t);
+ for (m = 0; m < 80; m++)
+ {
+ sprintf(tmp, "%s0x%08x", scrap_path, (int)t + m);
+ if (myFile_Size(tmp) == -1)
+ {
+ break;
+ }
+ }
+
+ if (m < 80)
+ {
+ strncpy(buf, unixify_name(tmp), max);
+ return 0;
+ }
+ }
+
+ strncpy(buf, unixify_name(tmpnam(NULL)), max);
+ return 0;
+}
+
+
+
+/*
+ * Create a new path by appending a file (or directory) to a path
+ *
+ * This requires no special processing on simple machines, except
+ * for verifying the size of the filename, but note the ability to
+ * bypass the given "path" with certain special file-names.
+ *
+ * Note that the "file" may actually be a "sub-path", including
+ * a path and a file.
+ *
+ * Note that this function yields a path which must be "parsed"
+ * using the "parse" function above.
+ */
+errr path_build(char *buf, int max, cptr path, cptr file)
+{
+ /* Special file */
+ if (file[0] == '~')
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Absolute file, on "normal" systems */
+ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* No path given */
+ else if (!path[0])
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Path and File */
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Font Functions */
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Cache the system font as fonts[0]
+ | Returns 1 for sucess or 0 for failure.
+ | NB: The n_bpp data is *not* cached, just the 1bpp data and font info.
+ | Also, the usage is never affected.
+ */
+static int cache_system_font(void)
+{
+ ZapFont *sys = SYSTEM_FONT;
+ ZapRedrawBlock zrb;
+ char work_area[16];
+ int i;
+
+ /* Cache the system font (as fonts[0]) */
+ if (sys->bpp_1)
+ {
+ f_free(sys->bpp_1);
+ sys->bpp_1 = 0;
+ }
+ sys->bpp_1 = f_malloc(8 * 256); /* 2K */
+ if (!sys->bpp_1)
+ {
+ return 0;
+ }
+
+ /* Mung so that undefined characters show up as inverted ?s */
+ work_area[3] = '?';
+ SWI(2, 0, SWI_OS_Word, 10, work_area + 3);
+ for (i = 4; i < 12; i++)
+ work_area[i] ^= 255; /* invert colours */
+ SWI(4, 0, SWI_ZapRedraw_ReverseBitmaps, 0, work_area + 4, work_area + 4, 8);
+ for (i = 0; i < 0x20; i++)
+ memcpy(((char *)sys->bpp_1) + i * 8, work_area + 4, 8);
+
+ /* Read the system font */
+ zrb.r_workarea = work_area;
+ SWI(2, 0, SWI_ZapRedraw_ReadSystemChars, sys->bpp_1, &zrb);
+
+ /* Set up some little bits of info */
+ sys->name = (char *) "<System>";
+ sys->w = sys->h = 8;
+ sys->f = 0;
+ sys->l = 255;
+
+ return 1;
+}
+
+
+
+/*
+ | Prepare the font system
+ */
+static void initialise_fonts(void)
+{
+ /* Initialise the array */
+ memset(fonts, 0, sizeof(fonts)); /* Clear to zeroes */
+
+ /* Cache the system font */
+ cache_system_font();
+ fonts[0].usage = 0; /* No users */
+}
+
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Find a font (by name) in the array.
+ | Returns 0 if the font isn't loaded, or a ZapFont* for it if it is.
+ */
+static ZapFont *find_font_by_name(char *name)
+{
+ int i;
+ for (i = 0; i <= MAX_TERM_DATA; i++)
+ if (fonts[i].name)
+ if (!strcmp(fonts[i].name, name))
+ return &(fonts[i]);
+ return NULL;
+}
+
+/*
+ | Find a free slot in the fonts array
+ */
+static ZapFont *find_free_font(void)
+{
+ int i;
+ for (i = 1; i <= MAX_TERM_DATA; i++)
+ if (!fonts[i].name)
+ {
+ return &(fonts[i]);
+ }
+ return NULL;
+}
+
+
+
+/*
+ | Load a font from disc and set up the header info, etc.
+ | NB: doesn't cache the nbpp data, just the 1bpp data.
+ | (Sets usage to 1)
+ | Returns NULL if failed.
+ */
+static ZapFont *load_font(char *name, ZapFont *f)
+{
+ int handle, extent;
+ char path[260];
+ struct
+ {
+ char id[8];
+ int w, h, f, l, r1, r2;
+ }
+ header;
+ char *font_path;
+ char *t;
+ char *real_name = name; /* need to preserve this */
+
+ /*
+ | 1.10 - the first element of the name determines the path to load
+ | the font from.
+ */
+
+ /* The font paths start <RISCOS_VARIANT>$ */
+ t = path + sprintf(path, "%s$", RISCOS_VARIANT);
+
+ /* Copy the path specifier and move 'name' past it */
+ for (; *name != '.'; *t++ = *name++) ;
+
+ /* After this, the name now points to the font name proper */
+ name++;
+
+ /* Append the end of the path name */
+ strcpy(t, "$FontPath");
+
+ /* Get the path setting */
+ font_path = getenv(path);
+ if (!font_path || !*font_path)
+ strcpy(path, "null:$.");
+ else
+ {
+ strcpy(path, font_path);
+ for (t = path; *t > ' '; t++)
+ ;
+ if (t[-1] != '.' && t[-1] != ':')
+ {
+ *t++ = '.';
+ }
+ *t = 0;
+ }
+ strcat(path, name);
+
+
+ /* Open the file */
+ handle = myFile_Open(path, 0x4f);
+ if (!handle)
+ {
+ return NULL;
+ }
+
+ /* Read the header */
+ if (myFile_ReadBytes(handle, &header, sizeof(header)))
+ {
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Check that it's a zapfont */
+ if (strncmp(header.id, "ZapFont\r", 8))
+ {
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Calculate the size of the 1bpp data */
+ extent = myFile_Extent(handle) - sizeof(header);
+
+ /* Allocate the storage for the 1bpp data */
+ f->bpp_1 = f_malloc(extent);
+ if (!f->bpp_1)
+ {
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Load the 1bpp data */
+ if (myFile_ReadBytes(handle, f->bpp_1, extent))
+ {
+ f_free(f->bpp_1);
+ f->bpp_1 = 0;
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Close the file and set the header, etc. */
+ myFile_Close(handle);
+ f->name = f_malloc(strlen(real_name) + 1);
+ if (!f->name)
+ {
+ f_free(f->bpp_1);
+ f->bpp_1 = 0;
+ return NULL;
+ }
+
+ strcpy(f->name, real_name);
+ f->w = header.w;
+ f->h = header.h;
+ f->f = header.f;
+ f->l = header.l;
+ f->usage = 1;
+
+ return f;
+}
+
+
+
+
+/*
+ | Cache a font at a suitable number of bpp for the current mode
+ | Returns 0 for failure, 1 for sucess.
+ | If the call fails then the font's bpp_n entry will be NULL.
+ */
+static int cache_font_for_mode(ZapFont *f)
+{
+ ZapRedrawBlock b;
+ char work_area[128];
+ int size;
+
+ if (!f)
+ {
+ return 0;
+ }
+ if (!f->bpp_1)
+ {
+ return 0;
+ }
+
+ b.r_workarea = work_area;
+ SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
+
+ b.r_workarea = work_area; /* Paranoia */
+ b.r_charh = f->h;
+ b.r_charw = f->w;
+ SWI(4, 4, SWI_ZapRedraw_CachedCharSize, b.r_bpp, 0, f->w, f->h,
+ NULL, NULL, &(b.r_cbpl), &(b.r_cbpc));
+
+ size = 256 * b.r_cbpc;
+ if (f->bpp_n)
+ {
+ f_free(f->bpp_n);
+ f->bpp_n = NULL;
+ }
+ f->bpp_n = f_malloc(size);
+ if (!f->bpp_n)
+ {
+ return 0;
+ }
+
+ b.r_workarea = work_area; /* Paranoia */
+ b.r_caddr = f->bpp_n;
+ SWI(5, 0, SWI_ZapRedraw_ConvertBitmap, 0, &b, 0, 255, f->bpp_1);
+
+ return 1;
+}
+
+
+
+/*
+ | Stop using a font.
+ | If the font's usage drops to zero then the font data is purged.
+ */
+static void lose_font(ZapFont *f)
+{
+ if (--f->usage)
+ {
+ /*debug("Losing font %s (still cached)",f->name); */
+ return;
+ }
+ /*debug("Losing font %s (no longer in use)",f->name); */
+ f_free(f->name);
+ f_free(f->bpp_1);
+ if (f->bpp_n)
+ {
+ f_free(f->bpp_n);
+ }
+ memset(f, 0, sizeof(ZapFont));
+}
+
+
+/*
+ | Get a font.
+ */
+static ZapFont *find_font(char *name)
+{
+ ZapFont *f;
+
+ /* Check to see if it's already loaded */
+ f = find_font_by_name(name);
+ if (f)
+ {
+ /*debug("Find font %s (already cached)",name); */
+ f->usage++;
+ if (f == SYSTEM_FONT)
+ {
+ if (!cache_system_font())
+ core("Failed to cache system font!");
+ if (!cache_font_for_mode(SYSTEM_FONT))
+ core("Failed to cache system font!");
+ }
+ return f;
+ }
+
+ /* Ok, now check to see if there's a free slot for it */
+ f = find_free_font();
+ if (!f)
+ {
+ return NULL;
+ } /* Oh dear :( */
+
+ /* Load the font */
+ /*debug("Find font %s (loading)",name); */
+ f = load_font(name, f);
+ if (f)
+ {
+ if (!cache_font_for_mode(f))
+ return NULL;
+ return f;
+ }
+ return NULL;
+}
+
+
+
+
+/*
+ | Cache the n_bpp data for all the active fonts (including system)
+ */
+static void cache_fonts(void)
+{
+ int i;
+ for (i = 0; i <= MAX_TERM_DATA; i++)
+ if (fonts[i].name)
+ if (!cache_font_for_mode(&(fonts[i])))
+ core("Failed to (re)cache font tables");
+}
+
+
+
+
+
+
+typedef struct
+{
+ int load, exec, size, attr, type;
+ char name[4]; /* Actual size is unknown */
+}
+osgbpb10_block;
+
+
+static char *leafname(char *path)
+{
+ char *s = path + strlen(path);
+ while (--s > path)
+ if (*s == '.' || *s == ':')
+ {
+ return s + 1;
+ }
+ return path;
+}
+
+/*
+ | NB: This function is recursive.
+ */
+static menu_ptr make_zfont_menu(char *dir)
+{
+ int entries, entry;
+ int read, offset;
+ unsigned int max_width;
+ menu_ptr m;
+ menu_item *mi;
+ char *temp;
+ osgbpb10_block *item_info;
+ char buffer[1024]; /* 1Kb buffer */
+
+ /* Count the entries in the directory */
+ entries = read = offset = 0;
+ while (offset != -1)
+ {
+ if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0,
+ NULL, NULL, NULL, &read, &offset))
+ {
+ offset = -1;
+ read = 0;
+ }
+ entries += read;
+ }
+
+ if (!entries)
+ {
+ return NULL;
+ }
+
+ /* Allocate a big enough area of storage for the number of entries */
+ m = f_malloc(sizeof(menu_block) + entries * sizeof(menu_item));
+ if (!m)
+ {
+ return NULL;
+ }
+ memset(m, 0, sizeof(menu_block) + entries * sizeof(menu_item));
+
+ /* Set up the menu header */
+ strncpy(m->title, leafname(dir), 12);
+ m->titlefore = 7;
+ m->titleback = 2;
+ m->workfore = 7;
+ m->workback = 0;
+ m->height = 44;
+ m->gap = 0;
+ mi = (menu_item *) (((int)m) + sizeof(menu_block));
+ max_width = strlen(m->title);
+
+ entry = 0;
+
+ /* Read the entries */
+ read = offset = 0;
+ while (offset != -1)
+ {
+ if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0,
+ NULL, NULL, NULL, &read, &offset))
+ {
+ offset = -1;
+ read = 0;
+ /*free(m);return NULL; */
+ }
+
+ item_info = (osgbpb10_block *) buffer;
+
+ /* Create a menu item for each entry read (if it fits) */
+ while (read-- > 0)
+ {
+ switch (item_info->type)
+ {
+ case 1: /* File */
+ if ((item_info->load & 0xffffff00) == 0xfffffd00)
+ {
+ /* Data file */
+ mi[entry].submenu.value = -1;
+ mi[entry].iconflags.data.text = 1;
+ mi[entry].iconflags.data.filled = 1;
+ mi[entry].iconflags.data.foreground = 7;
+ mi[entry].iconflags.data.background = 0;
+ strncpy(mi[entry].icondata.text, item_info->name, 12);
+ if (strlen(mi[entry].icondata.text) > max_width)
+ max_width = strlen(mi[entry].icondata.text);
+ entry++;
+ }
+ break;
+ case 2: /* Directory */
+ case 3: /* Image */
+ {
+ menu_ptr sub;
+ char new_path[260];
+ if (strchr(":.", dir[strlen(dir) - 1]))
+ sprintf(new_path, "%s%s", dir, item_info->name);
+ else
+ sprintf(new_path, "%s.%s", dir, item_info->name);
+ sub = make_zfont_menu(new_path);
+ if (sub)
+ {
+ /* Add the submenu */
+ mi[entry].submenu.menu = sub;
+ mi[entry].iconflags.data.text = 1;
+ mi[entry].iconflags.data.filled = 1;
+ mi[entry].iconflags.data.foreground = 7;
+ mi[entry].iconflags.data.background = 0;
+ strncpy(mi[entry].icondata.text, item_info->name, 12);
+ if (strlen(mi[entry].icondata.text) > max_width)
+ max_width = strlen(mi[entry].icondata.text);
+ entry++;
+ }
+ }
+ break;
+ }
+ temp = ((char *)item_info) + 20;
+ while (*temp++) ;
+ item_info = (osgbpb10_block *) ((((int)temp) + 3) & ~3);
+ }
+ }
+
+ if (entry)
+ {
+ m->width = (max_width + 2) * 16;
+ mi[entry - 1].menuflags.data.last = 1;
+ /*
+ | We could possibly realloc() the storage to fit the
+ | actual no. of entries read, but this is probably more
+ | trouble than it's worth.
+ */
+ }
+ else
+ {
+ /*
+ | No point in returning an empty menu.
+ */
+ f_free(m);
+ m = NULL;
+ }
+
+ return m;
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Initialise the palette stuff
+ */
+static void initialise_palette(void)
+{
+ memset(a_palette, 0, sizeof(a_palette));
+ memset(palette, 0, sizeof(palette));
+ memset(zpalette, 0, sizeof(zpalette));
+}
+
+
+
+#ifndef FULLSCREEN_ONLY
+
+/*
+ | Cache the ZapRedraw palette
+ */
+static void cache_palette(void)
+{
+ static ZapRedrawBlock b;
+ char workspace[128];
+ int i;
+
+ static double old_gamma = -1.0;
+
+ /* Idiocy check: */
+ if (gamma < 0.01)
+ {
+ plog("Internal error: Attempt to apply zero gamma - recovering...");
+ gamma = 1.00;
+ }
+
+ if (gamma != old_gamma)
+ {
+ memset(a_palette, 0, sizeof(a_palette));
+ old_gamma = gamma;
+ }
+
+ /* Go through the palette updating any changed values */
+ for (i = 0; i < 256; i++)
+ {
+ if (COLOUR_CHANGED(i))
+ {
+ int r, g, b;
+ r = (int)(255.0 *
+ pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
+ g = (int)(255.0 *
+ pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
+ b = (int)(255.0 *
+ pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
+ palette[i] = (b << 24) | (g << 16) | (r << 8);
+ a_palette[i][1] = angband_color_table[i][1];
+ a_palette[i][2] = angband_color_table[i][2];
+ a_palette[i][3] = angband_color_table[i][3];
+ }
+ }
+
+ cursor_rgb = palette[CURSOR_COLOUR];
+
+ /* Cache the ZapRedraw palette for it */
+ b.r_workarea = workspace;
+ if (b.r_mode != screen_mode)
+ SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
+ SWI(5, 0, SWI_ZapRedraw_CreatePalette, 2, &b, palette, zpalette, 256);
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Functions for dealing with the SaveBox
+ */
+
+/*
+ | Create the window and claim various handlers for it
+ */
+static void init_save_window(void)
+{
+ /* Create the window */
+ save_box = Window_Create("save", template_TITLEMIN);
+
+ /* Set the file icon */
+ Icon_printf(save_box, SAVE_ICON, "file_%03x", vfiletype);
+
+}
+#endif /* FULLSCREEN_ONLY */
+
+/*
+ | Hack: can't use Str.h without defining HAS_STRICMP. Rather than
+ | require that the header files are altered we simply provide our
+ | own strnicmp() function.
+ */
+static int my_strnicmp(const char *a, const char *b, int n)
+{
+ int i;
+
+ n--;
+
+ for (i = 0; i <= n; i++)
+ {
+ if (tolower((unsigned char)a[i]) != tolower((unsigned char)b[i]))
+ return tolower((unsigned char)a[i]) - tolower((unsigned char)b[i]);
+
+ if (a[i] == '\0')
+ break;
+ }
+
+ return 0;
+}
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | This is the handler called when a 'save' occurrs.
+ | All it does is to update the game's own savefile setting and
+ | then (if possible) save the character.
+ */
+static BOOL SaveHnd_FileSave(char *filename, void *ref)
+{
+ char old_savefile[1024];
+
+ /* Hack: refuse to save if the character is dead */
+ if (PDEADCHK)
+ {
+ Msgs_Report(0, "err.cheat");
+ return FALSE;
+ }
+
+ /* Hack: disallow saves to <Wimp$Scrap>* */
+ if (!my_strnicmp("<wimp$scrap>", filename, 12))
+ {
+ Msgs_Report(0, "err.scrap");
+ return FALSE;
+ }
+
+ /* Preserve the old path, in case something goes wrong... */
+ strcpy(old_savefile, savefile);
+
+ /* Set the new path */
+ strcpy(savefile, unixify_name(filename));
+
+ /* Try a save (if sensible) */
+ if (game_in_progress && character_generated)
+ {
+ if (!save_player())
+ {
+ Msgs_Report(0, "err.save", filename);
+ strcpy(savefile, old_savefile);
+ return FALSE; /* => failure */
+ }
+ }
+
+ /* Set the pathname icon */
+ Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
+
+ /* Kill the menu */
+ Wimp_CreateMenu((menu_block *) - 1, -1, -1);
+
+ return TRUE; /* => Success */
+}
+
+
+
+/*
+ | Init the handlers for the savebox (eg. as a result of a menuwarning
+ | being received for the savebox)
+ */
+
+static void init_savehandlers(void)
+{
+ if (saveblk)
+ {
+ Save_ReleaseSaveHandlers(saveblk);
+ saveblk = 0;
+ }
+
+ saveblk = Save_InitSaveWindowHandler(save_box, /* Window handle */
+ TRUE, /* it's part of a menu */
+ FALSE, /* not a window */
+ FALSE, /* Don't auto release the handlers */
+ SAVE_ICON, /* The file icon */
+ SAVE_OK, /* The OK icon */
+ SAVE_CANCEL, /* The cancel icon */
+ SAVE_PATH, /* The pathname icon */
+ SaveHnd_FileSave, /* Handler to "save the file" */
+ NULL, /* No RAM transfer support */
+ NULL, /* No 'result handler' */
+ 100 << 10, /* Est. size (irelevant anyway) */
+ vfiletype, /* filetype (irelevant) */
+ NULL /* ref */
+ );
+}
+
+
+/*
+ | Handle a MenuWarning message for the savebox
+ */
+static BOOL Hnd_SaveWarning(event_pollblock * pb, void *ref)
+{
+ os_error *e;
+
+ init_savehandlers();
+
+ /* Set the pathname */
+ Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
+
+ /* Open the submenu */
+ e = Wimp_CreateSubMenu((menu_block *) save_box,
+ pb->data.message.data.menuwarn.openpos.x,
+ pb->data.message.data.menuwarn.openpos.y);
+
+ if (e)
+ {
+ Msgs_ReportFatal(0, "err.swi", __LINE__, e->errmess);
+ }
+
+ return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+
+/*
+ | Initialise the r_data array
+ | Mainly we just set up the line offset pointers and make sure that the
+ | lines themselves are 'safe' by writing end-of-line codes to them.
+ */
+static void initialise_r_data(void)
+{
+ int *lo = (int *)r_data;
+ char *ld;
+ int j;
+ for (j = 0; j < 24; j++)
+ {
+ lo[j] = 25 * 4 + (80 * 5 + 4) * j; /* Offset of line */
+ ld = r_data + lo[j];
+ *ld++ = 0; /* 0,2 == */
+ *ld = 2; /* end of line */
+ }
+ lo[j] = 0; /* Terminate line index */
+}
+
+
+
+/*
+ | Create the r_data array for a term
+ | This is typically quite fast (1ms or so on a RPC700)
+ | so we don't bother caching r_data for each term or using the
+ | 'frosh' concept.
+ */
+static void make_r_data(term_data *t)
+{
+ char **c = t->t.old->c; /* char array [24][80] */
+ byte **a = t->t.old->a; /* attr array [24][80] */
+ char *o;
+ int i, j, cf;
+
+/* New code: */
+
+ o = r_data + 25 * 4; /* First byte of r_data after line index */
+
+ if (force_mono)
+ {
+ for (j = 0; j < 24; j++)
+ {
+ /* Set up the line offset entry */
+ ((int *)r_data)[j] = o - r_data;
+
+ for (i = 0; i < 80; i++)
+ *o++ = a[j][i] != TERM_DARK ? c[j][i] : ' ';
+ /* 0,2 => end of line */
+ *o++ = 0;
+ *o++ = 2;
+ }
+ }
+ else
+ {
+ for (j = 0; j < 24; j++)
+ {
+ /* Set up the line offset entry */
+ ((int *)r_data)[j] = o - r_data;
+
+ /* Each line starts in white */
+ cf = TERM_WHITE;
+
+ for (i = 0; i < 80; i++)
+ {
+ if (a[j][i] != cf)
+ {
+ /* 0,6 => change FG */
+ *o++ = 0;
+ *o++ = 6;
+ cf = *o++ = a[j][i];
+ }
+ *o++ = c[j][i];
+ }
+ /* 0,2 => end of line */
+ *o++ = 0;
+ *o++ = 2;
+ }
+ }
+}
+
+
+/*
+ | Set up 'zrb' for the current screen mode.
+ */
+static void set_up_zrb_for_mode(void)
+{
+ static char work_area[4096];
+ zrb.r_workarea = work_area;
+ zrb.r_palette = zpalette;
+ zrb.r_linesp = 0;
+ zrb.r_for = TERM_WHITE;
+ zrb.r_bac = 0;
+ zrb.r_data = r_data;
+ SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &zrb);
+}
+
+
+
+/*
+ | Set up the ZapRedrawBlock ready to redraw term 't'
+ | (caches the r_data as part of the process)
+ */
+static void set_up_zrb(term_data *t)
+{
+ int fw, fh;
+
+ zrb.r_flags.value = 0;
+
+ /* Set font info up */
+ fw = t->font->w;
+ fh = t->font->h;
+ SWI(4, 4, SWI_ZapRedraw_CachedCharSize, zrb.r_bpp, 0, fw, fh,
+ NULL, NULL, &(zrb.r_cbpl), &(zrb.r_cbpc));
+ zrb.r_caddr = (void *)(((int)t->font->bpp_n) - (t->font->f * zrb.r_cbpc));
+
+ zrb.r_charw = fw; /* Character size in pixels */
+ zrb.r_charh = fh;
+
+ if (t->font == SYSTEM_FONT)
+ zrb.r_flags.bits.double_height = screen_eig.y == 1;
+ else
+ zrb.r_flags.bits.double_height = 0;
+
+ make_r_data(t); /* Cache the r_data */
+}
+
+
+
+
+
+static void redraw_window(window_redrawblock * rb, BOOL *more, term_data *t)
+{
+ int cx, cy, cw, ch;
+
+ /* set GCOL for cursor colour */
+ if (t->cursor.visible)
+ {
+ cw = zrb.r_charw << screen_eig.x;
+ ch = -(zrb.r_charh << screen_eig.y);
+ if (zrb.r_flags.bits.double_height)
+ {
+ ch *= 2;
+ }
+ cx = t->cursor.pos.x * cw;
+ cy = t->cursor.pos.y * ch;
+ cx += (rb->rect.min.x - rb->scroll.x);
+ cy += (rb->rect.max.y - rb->scroll.y);
+ cw -= (1 << screen_eig.x);
+ ch += (1 << screen_eig.y);
+ cy -= (1 << screen_eig.y);
+ }
+
+ while (*more)
+ {
+ SWI(2, 0, SWI_ZapRedraw_GetRectangle, rb, &zrb);
+ SWI(2, 0, SWI_ZapRedraw_RedrawArea, NULL, &zrb);
+ if (t->cursor.visible)
+ {
+ ColourTrans_SetGCOL(cursor_rgb, 0, 0);
+ GFX_Move(cx, cy);
+ GFX_DrawBy(cw, 0);
+ GFX_DrawBy(0, ch);
+ GFX_DrawBy(-cw, 0);
+ GFX_DrawBy(0, -ch);
+ }
+ Wimp_GetRectangle(rb, more);
+ }
+}
+
+
+
+
+
+static BOOL Hnd_Redraw(event_pollblock * pb, void *ref)
+{
+ term_data *t = (term_data *)ref;
+ window_redrawblock rb;
+ BOOL more;
+
+ rb.window = t->w;
+ Wimp_RedrawWindow(&rb, &more);
+
+ set_up_zrb(t);
+
+ redraw_window(&rb, &more, t);
+
+ return TRUE;
+}
+
+
+
+
+
+static void refresh_window(term_data *t)
+{
+ window_redrawblock rb;
+ BOOL more;
+ int fw, fh;
+
+ if ((t->changed_box.min.x >= t->changed_box.max.x) ||
+ (t->changed_box.min.y >= t->changed_box.max.y))
+ return;
+
+ set_up_zrb(t);
+
+ fw = zrb.r_charw << screen_eig.x;
+ fh = -(zrb.r_charh << screen_eig.y);
+ if (zrb.r_flags.bits.double_height)
+ {
+ fh *= 2;
+ }
+
+ rb.window = t->w;
+ rb.rect.min.x = fw * t->changed_box.min.x;
+ rb.rect.max.x = fw * t->changed_box.max.x;
+
+ rb.rect.max.y = fh * t->changed_box.min.y;
+ rb.rect.min.y = fh * t->changed_box.max.y;
+
+ Wimp_UpdateWindow(&rb, &more);
+ redraw_window(&rb, &more, t);
+
+ t->changed_box.min.x = t->changed_box.min.y = 255;
+ t->changed_box.max.x = t->changed_box.max.y = 0;
+}
+
+
+
+static void refresh_windows(void)
+{
+ int i;
+ window_info info;
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ info.window = data[i].w;
+ Wimp_GetWindowInfo(&info);
+ if (info.block.flags.data.open)
+ refresh_window(&(data[i]));
+ }
+}
+
+
+/*
+ | Set the size of a window.
+ | If the window grows but has no scroll bars then it is re-sized to
+ | the new extent. If it shrinks then it is resized regardless.
+ |
+ | If the window isn't open then it is opened behind the backwindow,
+ | resized and then closed again... I /did/ have a reason for doing this
+ | rather than simply recreating the window at the new size, but for the
+ | life of me I can't remember what it was...
+ */
+static void set_window_size(window_handle w, int width, int height)
+{
+ window_state ws;
+ int reclose;
+/*
+ * int cw,ch;
+ */
+
+ Wimp_GetWindowState(w, &ws);
+ Window_SetExtent(w, 0, -height, width, 0);
+
+ reclose = !ws.flags.data.open;
+ if (!(ws.flags.value & (0xf << 27)))
+ {
+ if (reclose)
+ {
+ ws.openblock.behind = -3;
+ Wimp_OpenWindow(&(ws.openblock));
+ }
+ /* Removed - caused "pixel creep" :)
+ * cw = ws.openblock.screenrect.max.x;
+ * ch = ws.openblock.screenrect.max.y;
+ * cw -= ws.openblock.screenrect.min.x;
+ * ch -= ws.openblock.screenrect.min.y;
+ * ws.openblock.screenrect.min.x -= ((width-cw)/2)-(1<<screen_eig.x);
+ * ws.openblock.screenrect.min.y -= ((height-ch)/2)-(1<<screen_eig.y);
+ */
+ ws.openblock.screenrect.max.x = ws.openblock.screenrect.min.x + width;
+ ws.openblock.screenrect.max.y = ws.openblock.screenrect.min.y + height;
+ Wimp_OpenWindow(&(ws.openblock));
+ if (reclose)
+ {
+ Wimp_CloseWindow(w);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ | Change the size of a window to suit the font displayed in it
+ */
+static void resize_term_for_font(term_data *t)
+{
+ int fw, fh;
+ set_up_zrb(t);
+
+ fw = zrb.r_charw << screen_eig.x;
+ fh = zrb.r_charh << screen_eig.y;
+ if (zrb.r_flags.bits.double_height)
+ {
+ fh *= 2;
+ }
+
+ /* Caulculate new size */
+ fw *= 80;
+ fh *= 24;
+
+ set_window_size(t->w, fw, fh);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static BOOL Hnd_Caret(event_pollblock * pb, void *ref)
+{
+ if (ref)
+ got_caret = 1;
+ else
+ got_caret = 0;
+ return TRUE;
+}
+
+
+
+
+/*
+ | Attach a (named) font to the specified term.
+ | If 'font' is NULL then the system font is attached.
+ | The bpp_n data is calculated if necessary
+ | returns:
+ | 1 => the font was attached OK
+ | 0 => the system font was substituted
+ */
+static int attach_font_to_term(term_data *t, char *font)
+{
+ if (t->font != SYSTEM_FONT)
+ {
+ lose_font(t->font);
+ }
+ if (font)
+ {
+ t->font = find_font(font);
+ }
+ if (!t->font)
+ {
+ t->font = SYSTEM_FONT;
+ if (font)
+ {
+ Msgs_Report(1, "err.font_l", font);
+ }
+ }
+ else
+ {
+ if (!t->font->bpp_n)
+ {
+ lose_font(t->font);
+ t->font = SYSTEM_FONT;
+ if (font)
+ {
+ Msgs_Report(1, "err.font_c", font);
+ }
+ }
+ }
+ resize_term_for_font(t);
+ return !(t->font == SYSTEM_FONT);
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+
+
+/*
+ | Create a menu of all the (probable!) fonts in the specified location
+ | NB: Any file of type 'data' is considered a font.
+ |
+ | Subdirectories are recursively searched.
+ |
+ | 1.10 - Uses <variant>$FontPaths to get a (space separated) list of paths
+ | to search. For each path name, the menu text will be the name and the
+ | path searched will be <variant>$<name>$FontPath
+ |
+ | Eg. (for angband):
+ | Angband$FontPaths Zap Angband
+ | Angband$Zap$FontPath ZapFonts:
+ | Angband$Angband$FontPath Angband:xtra.fonts.
+ */
+static void make_font_menu(void)
+{
+ char *t;
+ char buffer[260];
+ char menu_buffer[260];
+ int paths;
+ int i;
+ unsigned int max_width;
+ const char *path[64]; /* pointers to path names */
+ menu_item *mi;
+
+ font_menu = NULL;
+
+ /* Get the path (ie. dir) to look under */
+ t = getenv(RISCOS_VARIANT "$FontPaths");
+
+ /* Hack: cope if the path isn't set */
+ if (!t)
+ {
+ t = "";
+ }
+
+ strcpy(buffer, t);
+
+ /*
+ | Count how many paths there are, build an array of pointers to them
+ | and terminate them in the buffer
+ */
+ paths = 1; /* including the system font fake path '<System>' */
+ for (t = buffer; *t; t++)
+ {
+ if (*t == ' ')
+ {
+ *t = 0;
+ }
+ else
+ {
+ if (t == buffer || !t[-1])
+ {
+ path[paths] = t;
+ paths++;
+ }
+ }
+ }
+
+ /*
+ | Create the menu
+ */
+ path[0] = SYSTEM_FONT->name;
+
+ font_menu = f_malloc(sizeof(menu_block) + paths * sizeof(menu_item));
+ if (!font_menu)
+ {
+ core("Out of memory (building font menu)");
+ }
+ memset(font_menu, 0, sizeof(menu_block) + paths * sizeof(menu_item));
+
+ strncpy(font_menu->title, "Fonts", 12);
+ font_menu->titlefore = 7;
+ font_menu->titleback = 2;
+ font_menu->workfore = 7;
+ font_menu->workback = 0;
+ font_menu->height = 44;
+ font_menu->gap = 0;
+ max_width = strlen(font_menu->title);
+
+ mi = (menu_item *) (font_menu + 1);
+
+ for (i = 0; i < paths; i++)
+ {
+ mi[i].submenu.value = -1;
+ mi[i].iconflags.data.text = 1;
+ mi[i].iconflags.data.filled = 1;
+ mi[i].iconflags.data.foreground = 7;
+ mi[i].iconflags.data.background = 0;
+ strncpy(mi[i].icondata.text, path[i], 12);
+ if (strlen(mi[i].icondata.text) > max_width)
+ max_width = strlen(mi[i].icondata.text);
+ }
+ font_menu->width = (max_width + 2) * 16;
+ mi[i - 1].menuflags.data.last = 1;
+
+ /*
+ | Hack: add a dotted line after the system font entry if appropriate
+ */
+ if (paths > 1) mi[0].menuflags.data.dotted = 1;
+
+ /*
+ | Iterate over the paths, building the appropriate submenus
+ */
+ for (i = 1; i < paths; i++)
+ {
+ menu_ptr sub_menu = NULL;
+
+ sprintf(menu_buffer, "%s$%s$FontPath", RISCOS_VARIANT, path[i]);
+ t = getenv(menu_buffer);
+ /* Hack: cope if the path isn't defined */
+ if (!t)
+ {
+ t = "";
+ }
+
+ /* Fudge so that the fontpath can be a path, not just a dir. */
+ strcpy(menu_buffer, t);
+ for (t = menu_buffer; *t > ' '; t++)
+ ;
+ if (t[-1] == '.')
+ {
+ t--;
+ }
+ *t = 0;
+
+ /* Build the menu. Don't bother if the path variable was empty */
+ if (*menu_buffer)
+ sub_menu = make_zfont_menu(menu_buffer);
+
+ if (!sub_menu)
+ {
+ mi[i].iconflags.data.shaded = 1;
+ }
+ else
+ {
+ mi[i].submenu.menu = sub_menu;
+ /* Override the title of the 'root' sub-menu */
+ strncpy(sub_menu->title, path[i], 12);
+ /* Add the submenu to the main menu */
+ }
+ }
+
+ return;
+}
+
+/* ----------------------------------------------- musus, xxxx-xx-xx ---
+ * Create and set up the infobox.
+ * --------------------------------------------------------------------- */
+static void create_info_box(void)
+{
+ info_box = Window_Create("info", template_TITLEMIN);
+ Icon_printf(info_box, 0, "%s %s", VARIANT, VERSION);
+ Icon_SetText(info_box, 2, AUTHORS);
+ Icon_SetText(info_box, 3, PORTERS);
+ Icon_SetText(info_box, 7, PORTVERSION);
+
+ return;
+}
+
+/*
+ | Create the various menus
+ */
+static void init_menus(void)
+{
+ char buffer1[256];
+ char buffer2[32];
+ char *o;
+
+ create_info_box(); /* For the Info> entries */
+ make_font_menu(); /* Make the fonts menu */
+
+ Msgs_Lookup("menu.ibar:Info|>Save As|Full screen,Gamma correction,Sound,"
+ "Windows|Save choices|Quit (& save)", buffer1, 256);
+ ibar_menu = Menu_New(VARIANT, buffer1);
+ if (!ibar_menu) core("Can't create Iconbar menu!");
+
+ Msgs_Lookup("menu.term:Info|>Save As|Font,Windows", buffer1, 256);
+ term_menu = Menu_New(VARIANT, buffer1);
+ if (!term_menu) core("Can't create Term menu!");
+
+#ifndef OLD_TERM_MENU
+ o = buffer1;
+ o += sprintf(buffer1, "%s|", VARIANT);
+ o += sprintf(o, "%s,%s,%s|", angband_term_name[1], angband_term_name[2],
+ angband_term_name[3]);
+ sprintf(o, "%s,%s,%s,%s", angband_term_name[4], angband_term_name[5],
+ angband_term_name[6], angband_term_name[7]);
+#else
+ Msgs_printf(buffer1, "menu.windows:%s|Term-1 (Mirror),Term-2 (Recall),"
+ "Term-3 (Choice)|Term-4,Term-5,Term-6,Term-7", VARIANT);
+#endif
+ Msgs_Lookup("menu.winT:Windows", buffer2, 32);
+ wind_menu = Menu_New(buffer2, buffer1);
+ if (!wind_menu)
+ {
+ core("Can't create Windows menu!");
+ }
+
+ /* Now attach the various submenus to where they belong */
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_INFO, (menu_ptr) info_box);
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_GAMMA, (menu_ptr) gamma_win);
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_SOUND, (menu_ptr) sound_win);
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_WINDOWS, (menu_ptr) wind_menu);
+ Menu_AddSubMenu(term_menu, TERM_MENU_INFO, (menu_ptr) info_box);
+ Menu_AddSubMenu(term_menu, TERM_MENU_WINDOWS, wind_menu);
+
+ /* Add the savebox */
+ Menu_Warn(ibar_menu, IBAR_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL);
+ Menu_Warn(term_menu, TERM_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL);
+
+ if (font_menu)
+ /* add the submenu */
+ Menu_AddSubMenu(term_menu, TERM_MENU_FONT, font_menu);
+ else
+ /* If the font menu is buggered, shade its entry */
+ /* unticked, shaded */
+ Menu_SetFlags(term_menu, TERM_MENU_FONT, FALSE, TRUE);
+}
+
+
+
+
+static void grab_caret(void)
+{
+ caret_block cb;
+ cb.window = data[0].w;
+ cb.icon = -1;
+ cb.height = 1 << 25; /* Invisible */
+ Wimp_SetCaretPosition(&cb);
+}
+
+
+
+
+/*
+ | (Recursively) clear all ticks from the specified menu
+ */
+static void clear_all_menu_ticks(menu_ptr mp)
+{
+ menu_item *mi = (menu_item *) (mp + 1);
+
+ do
+ {
+ if (mi->menuflags.data.ticked)
+ mi->menuflags.data.ticked = 0;
+ if (mi->submenu.value != -1)
+ clear_all_menu_ticks(mi->submenu.menu);
+ mi++;
+ }
+ while (mi[-1].menuflags.data.last != 1);
+}
+
+
+
+
+
+
+/*
+ | Set the font menu's ticks to match the specifed font name.
+ |
+ | fm is the (sub) menu to scan down (recursing into it's submenus (if any))
+ | fn is the font name to match
+ | prefix is the menu text to be prepended to the menu entries due to
+ | previous menus (eg. "08x16" will cause fn="08x16.fred" to match menu
+ | entry "fred".
+ |
+ | NB: recursive.
+ |
+ */
+
+static void set_font_menu_ticks(menu_ptr fm, char *fn, const char *prefix)
+{
+ char buffer[260];
+ char *b_leaf; /* -> menu 'leaf' text in buffer */
+ int pl; /* prefix string length */
+ menu_item *mi = (menu_item *) (fm + 1);
+
+ strcpy(buffer, prefix);
+ pl = strlen(buffer);
+ b_leaf = buffer + pl;
+
+ do
+ {
+ /* Check for (substring) match */
+ strncpy(b_leaf, mi->icondata.text, 12);
+
+ /* Is it a sub-menu? */
+ if (mi->submenu.value == -1)
+ {
+ /* No - must be an exact match */
+ mi->menuflags.data.ticked = !strcmp(buffer, fn);
+ }
+ else
+ {
+ /* Yes - must be a partial match (with a dot on :) */
+ strcat(b_leaf, ".");
+ mi->menuflags.data.ticked =
+ !strncmp(buffer, fn, pl + strlen(b_leaf));
+ if (mi->menuflags.data.ticked)
+ set_font_menu_ticks(mi->submenu.menu, fn, buffer);
+ else
+ clear_all_menu_ticks(mi->submenu.menu);
+ }
+
+ /* Next item */
+ mi++;
+ }
+ while (mi[-1].menuflags.data.last != 1); /* Until finished */
+}
+
+
+
+
+
+
+
+
+
+
+
+/*
+ | Set ticks, etc. in the term_menu to reflect the current state of the
+ | term 't'
+ */
+static void set_up_term_menu(term_data *t)
+{
+ int i;
+ menu_ptr mp;
+ menu_item *mi;
+ window_state ws;
+
+ /* First of all, set up menu title to be the term's title */
+ strncpy(term_menu->title, t->name, 12);
+
+ /* Now set the ticks in the Windows> submenu (cuz it's easy) */
+ mp = wind_menu; /* Windows submenu */
+ mi = (menu_item *) (mp + 1); /* First entry */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Wimp_GetWindowState(data[i].w, &ws);
+ mi[i].menuflags.data.ticked = ws.flags.data.open;
+ }
+
+ /*
+ | Now, the tricky bit: find out which font is selected in this
+ | term and tick it in the menu. Untick all the rest.
+ */
+ set_font_menu_ticks(font_menu, t->font->name, "");
+
+ /* Shade the 'Save>' entry if saving isn't possible (yet) */
+ if (game_in_progress && character_generated)
+ Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, PDEADCHK);
+ else
+ Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, TRUE);
+
+}
+
+
+
+/*
+ | Generic 'click' handler for windows - turns a drag on the window
+ | into a move-window style drag.
+ */
+static BOOL Hnd_Click(event_pollblock * pb, void *ref)
+{
+ if (pb->data.mouse.button.data.dragselect ||
+ pb->data.mouse.button.data.dragadjust)
+ {
+ drag_block b;
+ b.window = pb->data.mouse.window;
+ b.screenrect.min.x = b.screenrect.min.y = 0;
+ b.screenrect.max.x = screen_size.x;
+ b.screenrect.max.y = screen_size.y;
+ b.type = drag_MOVEWINDOW;
+ Wimp_DragBox(&b);
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ | Handle a click on a Term window.
+ */
+static BOOL Hnd_TermClick(event_pollblock * pb, void *ref)
+{
+ term_data *t = (term_data *)ref;
+
+ if (pb->data.mouse.button.data.menu)
+ {
+ menu_term = t;
+ set_up_term_menu(t);
+ Menu_Show(term_menu, pb->data.mouse.pos.x - 32,
+ pb->data.mouse.pos.y + 32);
+ }
+ else
+ {
+ grab_caret();
+ Hnd_Click(pb, ref);
+ }
+
+ return TRUE;
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+static void mark_ood(term_data *t, int minx, int miny, int maxx, int maxy)
+{
+ if (t->changed_box.min.x > minx)
+ t->changed_box.min.x = minx;
+ if (t->changed_box.max.x < maxx)
+ t->changed_box.max.x = maxx;
+ if (t->changed_box.min.y > miny)
+ t->changed_box.min.y = miny;
+ if (t->changed_box.max.y < maxy)
+ t->changed_box.max.y = maxy;
+}
+
+
+#ifndef FULLSCREEN_ONLY
+
+/* Check for an event (ie. key press) */
+static errr Term_xtra_acn_check(void)
+{
+ static int last_poll = 0;
+ unsigned int curr_time;
+ int bh, bl;
+
+ /*
+ | Only poll the wimp if there's something in the keyboard buffer
+ | or every 10cs. This is presumably so that we process as many
+ | keypresses as possible before any other application gets a go.
+ */
+
+ /* Check the kbd buffer */
+ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
+ bl = (bl & 0xff) + (bh << 8);
+
+ /* Check how long it is since we last polled */
+ curr_time = Time_Monotonic();
+
+ if ((bl > 0 && got_caret) || ((curr_time - last_poll) > 9))
+ {
+ last_poll = curr_time;
+ Stop_Hourglass;
+ Event_Poll();
+ Start_Hourglass;
+ }
+
+ /*
+ | This allows the user to interrupt the borg.
+ */
+ if (key_pressed) return key_pressed = 0;
+
+ return 1;
+}
+
+
+/*
+ | Wait for an event (ie. keypress)
+ | Note that we idle poll once a second to allow us to implement the
+ | alarm system.
+ */
+static errr Term_xtra_acn_event(void)
+{
+ Stop_Hourglass;
+
+ while (!key_pressed && !fullscreen_font)
+ {
+ Event_PollIdle(100);
+ }
+ Start_Hourglass;
+
+ return key_pressed = 0;
+}
+
+
+
+/* React to changes (eg. palette change) */
+static errr Term_xtra_acn_react(void)
+{
+ int c;
+
+ cache_palette();
+
+ /* Mark the entirety of each window as out of date */
+ for (c = 0; c < MAX_TERM_DATA; c++)
+ mark_ood(&data[c], 0, 0, 80, 24);
+
+ /* Force a redraw of the windows */
+ refresh_windows();
+
+ /* Success */
+ return 0;
+}
+
+
+
+/* Do various things to a term */
+static errr Term_xtra_acn(int n, int v)
+{
+ term_data *t = (term_data *)Term;
+
+ switch (n)
+ {
+ case TERM_XTRA_CLEAR: /* Clear the Term */
+ /*for ( i=0; i<Term->hgt; i++ )
+ t->froshed[i] = 1; */
+ mark_ood(t, 0, 0, Term->wid, Term->hgt);
+ /*refresh_window( t ); - NB: Term isn't actually cleared yet! */
+ /* Success */
+ return 0;
+
+ case TERM_XTRA_EVENT: /* Wait/check for an event */
+ if (v)
+ return Term_xtra_acn_event();
+ else
+ return Term_xtra_acn_check();
+
+ case TERM_XTRA_BORED: /* Bored */
+ return Term_xtra_acn_check();
+
+ case TERM_XTRA_FLUSH: /* Flush input */
+ if (got_caret)
+ {
+ /* 1.21 - Hack: wait until no keys are pressed */
+ if (hack_flush)
+ for (v = 0; v != 0xff;)
+ SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
+ SWI(3, 0, SWI_OS_Byte, 21, 0, 0); /* Flush Kbd buffer */
+ }
+ return 0;
+
+ case TERM_XTRA_FRESH: /* Flush output */
+ refresh_window(t);
+ return 0;
+
+ case TERM_XTRA_FROSH: /* Ensure line 'v' is plotted */
+ /* Doesn't do anything */
+ return 0;
+
+ case TERM_XTRA_SHAPE: /* Set cursor visibility */
+ t->cursor.visible = v ? TRUE : FALSE;
+ mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
+ t->cursor.pos.x + 1, t->cursor.pos.y + 1);
+ refresh_window(t); /* needed? */
+ return 0;
+
+ case TERM_XTRA_NOISE: /* Make a beep */
+ Sound_SysBeep();
+ return 0;
+
+ case TERM_XTRA_REACT: /* React to, eg. palette changes */
+ return Term_xtra_acn_react();
+
+ case TERM_XTRA_DELAY: /* Delay for 'v' ms */
+ if (v > 0)
+ {
+ unsigned int start = Time_Monotonic();
+ v = (v + 5) / 10; /* Round to nearest cs */
+ GFX_Wait();
+ while ((Time_Monotonic() - start) < v)
+ ;
+ }
+ return 0;
+
+ case TERM_XTRA_SOUND: /* Play a sound :) */
+ if (enable_sound)
+ {
+ play_sound(v);
+ }
+ return 0;
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ filing_dirdata directory;
+ filing_direntry *entry;
+
+ scansubdir_max = 0;
+
+ if (Filing_OpenDir(riscosify_name(scansubdir_dir), &directory, sizeof(filing_direntry), readdirtype_DIRENTRY) != NULL)
+ {
+ Error_Report(0, "Couldn't open directory \"%s\"", riscosify_name(scansubdir_dir));
+ return 0;
+ }
+
+ while ((entry = Filing_ReadDir(&directory)) != NULL)
+ {
+ if (entry->objtype == filing_DIRECTORY)
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->name);
+ ++scansubdir_max;
+ }
+ }
+
+ Filing_CloseDir(&directory);
+
+ return 0;
+ }
+
+ /* Return current "time" in milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ Term_xtra_long = Time_Monotonic() * 100;
+
+ return 0;
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ Window_SetTitle(data[0].w, angband_term_name[0]);
+ return 0;
+ }
+
+ default:
+ return 1; /* Unsupported */
+ }
+}
+
+
+
+/* Move (but don't necessarily display) the cursor */
+static errr Term_curs_acn(int x, int y)
+{
+ term_data *t = (term_data *)Term;
+
+ if (t->cursor.visible)
+ mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
+ t->cursor.pos.x + 1, t->cursor.pos.y + 1);
+
+ t->cursor.pos.x = x;
+ t->cursor.pos.y = y;
+
+ if (t->cursor.visible)
+ mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
+ t->cursor.pos.x + 1, t->cursor.pos.y + 1);
+
+ return 0;
+}
+
+
+
+/*
+ | NB: these two are very simple since we use the Term's contents
+ | directly to generate the r_data for ZapRedraw.
+ */
+
+/* Erase 'n' characters at (x,y) */
+static errr Term_wipe_acn(int x, int y, int n)
+{
+ mark_ood((term_data *)Term, x, y, x + n, y + 1);
+ return 0;
+}
+
+/* Write 'n' characters from 's' with attr 'a' at (x,y) */
+static errr Term_text_acn(int x, int y, int n, byte a, cptr s)
+{
+ mark_ood((term_data *)Term, x, y, x + n, y + 1);
+ return 0;
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+/* Initialise one of our terms */
+static void Term_init_acn(term *t)
+{
+ term_data *term = (term_data *)t;
+
+ /* Ludicrous changed box settings :) */
+ term->changed_box.min.x = 256;
+ term->changed_box.min.y = 256;
+ term->changed_box.max.x = 0;
+ term->changed_box.max.y = 0;
+}
+
+
+
+static void term_data_link(term_data *td, int k)
+{
+ term *t = &(td->t);
+
+ /* Initialise the term */
+ term_init(t, 80, 24, k);
+
+ /* Set flags and hooks */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Experiment (FS mode requires them) */
+ t->always_text = TRUE;
+ t->never_frosh = TRUE;
+ /* Experiment (FS mode requires them) */
+
+#ifdef FULLSCREEN_ONLY
+ t->wipe_hook = Term_wipe_acnFS;
+ t->xtra_hook = Term_xtra_acnFS;
+ t->curs_hook = Term_curs_acnFS;
+ t->text_hook = Term_text_acnFS;
+#else
+ t->init_hook = Term_init_acn;
+ t->xtra_hook = Term_xtra_acn;
+ t->wipe_hook = Term_wipe_acn;
+ t->curs_hook = Term_curs_acn;
+ t->text_hook = Term_text_acn;
+ t->user_hook = Term_user_acn;
+#endif /* FULLSCREEN_ONLY */
+
+ t->data = td;
+
+ Term_activate(t);
+}
+
+
+#ifndef FULLSCREEN_ONLY
+
+/* Open default windows (ie. as set in choices) at the appropriate sizes */
+static void show_windows(void)
+{
+ int i;
+ for (i = MAX_TERM_DATA; i-- > 0;)
+ {
+ if (!data[i].unopened)
+ {
+ if (data[i].def_open)
+ Window_Show(data[i].w, open_WHEREVER);
+ }
+ else
+ {
+ if (data[i].def_open)
+ {
+ window_openblock ob;
+ ob.window = data[i].w;
+ ob.screenrect = data[i].def_pos;
+ ob.scroll = data[i].def_scroll;
+ ob.behind = -1;
+ Wimp_OpenWindow(&ob);
+ data[i].unopened = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ | 'ref' is used to indicate whether this close is being forced by some other
+ | part of the code (eg. the Windows> submenu code). This is used to modify
+ | the 'adjust doesn't close other terms' behavior.
+ */
+
+static BOOL Hnd_MainClose(event_pollblock * pb, void *ref)
+{
+ int i;
+ window_state ws;
+ mouse_block mb;
+
+ /* New in 1.08: don't close other Terms if closed with adjust */
+ Wimp_GetPointerInfo(&mb);
+ if (ref || mb.button.data.adjust)
+ {
+ Wimp_CloseWindow(data[0].w);
+ }
+ else
+ {
+ /* Close all the terms, but mark the open ones as 'def_open' */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Wimp_GetWindowState(data[i].w, &ws);
+ if (!ws.flags.data.open)
+ {
+ data[i].def_open = 0;
+ }
+ else
+ {
+ Wimp_CloseWindow(data[i].w);
+ data[i].def_open = 1;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+static BOOL Hnd_PaletteChange(event_pollblock * pb, void *ref)
+{
+ cache_palette();
+ return TRUE;
+}
+
+
+
+static BOOL Hnd_ModeChange(event_pollblock * pb, void *ref)
+{
+ int i;
+ Screen_CacheModeInfo();
+ set_up_zrb_for_mode(); /* (re)set up the redraw block */
+ cache_palette(); /* (re)cache the palette */
+ cache_fonts(); /* (re)cache the fonts */
+ /* Enforce sizes (eg. if screen_eig.y has changed) */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ resize_term_for_font(&(data[i]));
+ return TRUE;
+}
+
+
+
+
+static BOOL Hnd_Keypress(event_pollblock * pb, void *ref)
+{
+ static const char hex[] = "0123456789ABCDEF";
+ int c = pb->data.key.code;
+ /* Check whether this key was pressed in Term 0 */
+ if (pb->data.key.caret.window == data[0].w)
+ {
+ switch (c)
+ {
+ case keycode_F12:
+ case keycode_SHIFT_F12:
+ case keycode_CTRL_F12:
+ case keycode_CTRL_SHIFT_F12:
+ /* Never intercept these */
+ break;
+
+ case 27: /* handle escape specially */
+ if (Kbd_KeyDown(inkey_CTRL))
+ {
+ ack_alarm();
+ return TRUE;
+ }
+
+ /* Send everything else onto the Term package */
+ default:
+ /* Take care of "special" keypresses */
+ switch (c)
+ {
+ case keycode_TAB:
+ {
+ c = '\t';
+ break;
+ }
+
+ case keycode_PAGEUP:
+ {
+ c = '9';
+ break;
+ }
+
+ case keycode_PAGEDOWN:
+ {
+ c = '3';
+ break;
+ }
+
+ case keycode_COPY:
+ {
+ c = '1';
+ break;
+ }
+
+ case keycode_HOME:
+ {
+ c = '7';
+ break;
+ }
+ }
+ /* Pass to the angband engine */
+ /* Allow shift & ctrl to modify the keypad keys */
+ if (c >= '0' && c <= '9')
+ {
+ kbd_modifiers m = Kbd_GetModifiers(FALSE);
+ if (m.shift)
+ {
+ c |= 0x800;
+ }
+ if (m.ctrl)
+ {
+ c |= 0x400;
+ }
+ /* Could maybe add ALT as 0x1000 ??? */
+ }
+
+ /* Keys >255 have to be send as escape sequences (31=escape) */
+ if (c > 255 || c == 31)
+ {
+ Term_keypress(31);
+ Term_keypress(hex[(c & 0xf00) >> 8]);
+ Term_keypress(hex[(c & 0x0f0) >> 4]);
+ Term_keypress(hex[(c & 0x00f)]);
+ c = 13;
+ }
+ Term_keypress(c);
+ key_pressed = 1;
+ /*if ( c==27 ) { escape_pressed = 1; } */
+ return TRUE;
+ }
+ }
+
+ Wimp_ProcessKey(c);
+ return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* Gamma correction window stuff */
+/*--------------------------------------------------------------------------*/
+
+static void redraw_gamma(window_redrawblock * rb, BOOL *more)
+{
+ int i, y, x, h, w;
+ int bx, by;
+ int dither;
+
+ bx = Coord_XToScreen(GC_XOFF, (convert_block *) & (rb->rect));
+ by = Coord_YToScreen(GC_YOFF, (convert_block *) & (rb->rect));
+
+ h = GC_HEIGHT / 4;
+ w = GC_WIDTH / 16;
+
+ x = bx;
+
+ while (*more)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ y = by;
+ for (dither = 0; dither < 2; dither++)
+ {
+ /* Solid block: */
+ ColourTrans_SetGCOL(palette[i], dither << 8, 0);
+ GFX_RectangleFill(x, y, w, -h);
+ y -= h;
+ /* Dot on black: */
+ ColourTrans_SetGCOL(palette[0], dither << 8, 0);
+ GFX_RectangleFill(x, y, w, -h);
+ ColourTrans_SetGCOL(palette[i], dither << 8, 0);
+ GFX_RectangleFill(x + (w / 2) - 2, y - (h / 2), 2, 2);
+ y -= h;
+ }
+ x += w;
+ }
+ Wimp_GetRectangle(rb, more);
+ }
+}
+
+
+static void update_gamma(void)
+{
+ window_redrawblock rb;
+ BOOL more;
+
+ rb.window = gamma_win;
+ rb.rect.min.x = GC_XOFF;
+ rb.rect.min.y = GC_YOFF - GC_HEIGHT;
+ rb.rect.max.y = GC_XOFF + GC_WIDTH + screen_delta.x;
+ rb.rect.max.y = GC_YOFF + screen_delta.y;
+
+ Wimp_UpdateWindow(&rb, &more);
+ if (more)
+ {
+ redraw_gamma(&rb, &more);
+ }
+}
+
+
+
+static BOOL Hnd_RedrawGamma(event_pollblock * pb, void *ref)
+{
+ window_redrawblock rb;
+ BOOL more;
+
+ rb.window = pb->data.openblock.window;
+ Wimp_RedrawWindow(&rb, &more);
+ if (more)
+ {
+ redraw_gamma(&rb, &more);
+ }
+
+ return TRUE;
+}
+
+
+static BOOL Hnd_GammaClick(event_pollblock * pb, void *ref)
+{
+ int up = (ref == 0);
+
+ if (up)
+ {
+ if (gamma < 9.0)
+ {
+ gamma += 0.05;
+ Icon_SetDouble(gamma_win, 0, gamma, 2);
+ Term_xtra_acn_react();
+ update_gamma();
+ }
+ }
+ else
+ {
+ if (gamma > 0.05)
+ {
+ gamma -= 0.05;
+ Icon_SetDouble(gamma_win, GAMMA_ICN, gamma, 2);
+ Term_xtra_acn_react();
+ update_gamma();
+ }
+ }
+
+ /* Hack: if the user menu is active then force it to redraw */
+ if (user_menu_active)
+ {
+ Term_keypress(18);
+ key_pressed = 1;
+ }
+
+ return TRUE;
+}
+
+/*
+ | Reflect the current options in the gamma window
+ */
+static void set_gamma_window_state(void)
+{
+ if (minimise_memory) return;
+
+ Icon_SetDouble(gamma_win, 0, gamma, 2);
+}
+
+
+static void init_gamma_window(void)
+{
+ if (minimise_memory) return;
+
+ Template_UseSpriteArea(resource_sprites);
+ gamma_win = Window_Create("gamma", template_TITLEMIN);
+ Template_UseSpriteArea(NULL);
+ Event_Claim(event_REDRAW, gamma_win, event_ANY, Hnd_RedrawGamma, 0);
+ Event_Claim(event_CLICK, gamma_win, GAMMA_DOWN, Hnd_GammaClick, (void *)1);
+ Event_Claim(event_CLICK, gamma_win, GAMMA_UP, Hnd_GammaClick, (void *)0);
+ set_gamma_window_state();
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* Sound options window stuff */
+/*--------------------------------------------------------------------------*/
+
+static slider_info volume_slider;
+
+
+/*
+ | Reflect the current sound config in the sound options window
+ */
+static void set_sound_window_state(void)
+{
+ if (minimise_memory) return;
+
+ Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
+ Slider_SetValue(&volume_slider, sound_volume, NULL, NULL);
+
+ if (sound_volume > 127)
+ volume_slider.colour.foreground = colour_RED;
+ else
+ volume_slider.colour.foreground = colour_GREEN;
+}
+
+
+
+/*
+ | The sound slider has been dragged, so update the sound volume setting
+ */
+static int update_volume_from_slider(slider_info *si, void *ref)
+{
+ sound_volume = Slider_ReadValue(si);
+
+ if (sound_volume > 127)
+ volume_slider.colour.foreground = colour_RED;
+ else
+ volume_slider.colour.foreground = colour_GREEN;
+
+ return 0;
+}
+
+
+/*
+ | Handle redraw events for the sound options window
+ */
+static BOOL Hnd_RedrawSnd(event_pollblock * pb, void *ref)
+{
+ window_redrawblock redraw;
+ BOOL more;
+
+ redraw.window = pb->data.openblock.window;
+ Wimp_RedrawWindow(&redraw, &more);
+
+ while (more)
+ {
+ Slider_Redraw(((slider_info *) ref), &redraw.cliprect);
+ Wimp_GetRectangle(&redraw, &more);
+ }
+ return (TRUE);
+}
+
+
+/*
+ | Handle clicks on the sound options window
+ */
+static BOOL Hnd_SndClick(event_pollblock * pb, void *ref)
+{
+ int icn = pb->data.mouse.icon;
+ int adj = pb->data.mouse.button.data.adjust;
+ slider_info *si = (slider_info *) ref;
+
+ switch (icn)
+ {
+ /* Bump arrows for the slider: */
+ case SND_VOL_DOWN:
+ adj = !adj;
+
+ case SND_VOL_UP:
+ adj = adj ? -1 : 1;
+ sound_volume += adj;
+ if (sound_volume < SOUND_VOL_MIN)
+ {
+ sound_volume = SOUND_VOL_MIN;
+ }
+ if (sound_volume > SOUND_VOL_MAX)
+ {
+ sound_volume = SOUND_VOL_MAX;
+ }
+ set_sound_window_state();
+ break;
+
+ /* The slider itself */
+ case SND_VOL_SLIDER:
+ Icon_ForceRedraw(sound_win, SND_VOL_SLIDER);
+ Slider_Drag(si, NULL, NULL, NULL);
+ break;
+
+ /* The enable/disable icon */
+ case SND_ENABLE:
+ enable_sound = !enable_sound;
+ Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
+ if (enable_sound)
+ {
+ initialise_sound();
+ }
+ break;
+ }
+
+ /* Hack: if the user menu is active then force it to redraw */
+ if (user_menu_active)
+ {
+ Term_keypress(18);
+ key_pressed = 1;
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ | Set the sound options window up, ready to rock :)
+ */
+static void init_sound_window(void)
+{
+ Template_UseSpriteArea(resource_sprites);
+ sound_win = Window_Create("sound", template_TITLEMIN);
+ Template_UseSpriteArea(NULL);
+
+ Event_Claim(event_REDRAW, sound_win, event_ANY, Hnd_RedrawSnd,
+ (void *)&volume_slider);
+ Event_Claim(event_CLICK, sound_win, event_ANY, Hnd_SndClick,
+ (void *)&volume_slider);
+
+ /* Set up the slider info */
+ volume_slider.window = sound_win;
+ volume_slider.icon = SND_VOL_SLIDER;
+ volume_slider.limits.min = SOUND_VOL_MIN;
+ volume_slider.limits.max = SOUND_VOL_MAX;
+ volume_slider.colour.foreground = colour_GREEN;
+ volume_slider.colour.background = colour_WHITE;
+ volume_slider.border.x = 8;
+ volume_slider.border.y = 8;
+ volume_slider.update = update_volume_from_slider;
+
+ set_sound_window_state();
+}
+
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+
+
+
+
+
+
+
+
+
+
+/*
+ | A font has been selected.
+ | At this point, menu_term is a pointer to the term for which the
+ | menu was opened.
+ */
+static void handle_font_selection(int *s)
+{
+ char name[260];
+ os_error *e;
+ char *r;
+ menu_ptr mp = font_menu;
+ int *mis;
+
+ /* Follow the >s to the entry specified */
+ for (mis = s; *mis != -1; mis++)
+ mp = ((menu_item *) (mp + 1))[*mis].submenu.menu;
+
+ /*
+ | Now, check to see if we've hit a leaf entry.
+ | NB: If the entry isn't a leaf entry then the first entry in its submenu
+ | is used instead
+ */
+ if (((int)mp) != -1)
+ {
+ mis[0] = 0;
+ mis[1] = -1;
+ mp = ((menu_item *) (mp + 1))[0].submenu.menu;
+ }
+
+ if (((int)mp) != -1)
+ return;
+
+ e = Wimp_DecodeMenu(font_menu, s, name);
+ if (e)
+ {
+ plog(e->errmess);
+ return;
+ }
+
+ /* Make sure that the string is NULL terminated */
+ for (r = name; *r >= ' '; r++)
+ ;
+ *r = 0;
+
+ attach_font_to_term(menu_term, name);
+ mark_ood(menu_term, 0, 0, 80, 24);
+ refresh_window(menu_term);
+}
+
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+
+static void load_choices(void)
+{
+ FILE *fp = NULL;
+ char *cf;
+ int i;
+ char buffer[260];
+
+ cf = find_choices(FALSE);
+ if (*cf)
+ fp = fopen(cf, "r");
+
+ /* Implement default choices */
+ data[0].def_open = 1;
+ data[0].unopened = 1; /* ie. force def_pos */
+ data[0].def_pos.min.x = (screen_size.x - 1280) / 2;
+ data[0].def_pos.max.x = (screen_size.x + 1280) / 2;
+ data[0].def_pos.min.y = (screen_size.y - 768) / 2 - 32;
+ data[0].def_pos.max.y = (screen_size.y + 768) / 2 - 32;
+ data[0].def_scroll.x = data[0].def_scroll.y = 0;
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ data[i].def_open = 0;
+ data[i].unopened = 1; /* ie. force def_pos */
+ data[i].def_pos.min.x = (screen_size.x - 1280) / 2;
+ data[i].def_pos.max.x = (screen_size.x + 1280) / 2;
+ data[i].def_pos.min.y = (screen_size.y - 768) / 2;
+ data[i].def_pos.max.y = (screen_size.y + 768) / 2;
+ data[i].def_scroll.x = data[i].def_scroll.y = 0;
+ }
+
+ if (fp)
+ {
+ const char *t_;
+ char *o_;
+
+ if (!fgets(buffer, sizeof(buffer), fp))
+ {
+ fclose(fp);
+ return;
+ }
+ if (strcmp(buffer, "[Angband config, Musus' port]\n"))
+ {
+ fclose(fp);
+ return;
+ }
+
+ /* Load choices */
+ while (fgets(buffer, sizeof(buffer), fp))
+ {
+ t_ = strtok(buffer, " "); /* Term number (or keyword, "Gamma", etc.) */
+ o_ = strtok(NULL, "\n"); /* argument string */
+ if (!o_)
+ {
+ o_ = "";
+ } /* missing (or null) argument? */
+ if (t_)
+ {
+ if (!strcmp(t_, "Gamma"))
+ gamma = atof(o_);
+ else if (!strcmp(t_, "Monochrome"))
+ force_mono = !strcmp(o_, "on");
+ else if (!strcmp(t_, "Sound"))
+ enable_sound = !strcmp(o_, "on");
+ else if (!strcmp(t_, "Volume"))
+ sound_volume = atoi(o_);
+ else if (!strcmp(t_, "FullScreen"))
+ start_fullscreen = !strcmp(o_, "on");
+ else if (!strcmp(t_, "Hourglass"))
+ use_glass = !strcmp(o_, "on");
+ else if (!strcmp(t_, "HackFlush"))
+ hack_flush = !strcmp(o_, "on");
+ else if (!strcmp(t_, "AlarmTimeH"))
+ alarm_h = atoi(o_);
+ else if (!strcmp(t_, "AlarmTimeM"))
+ alarm_m = atoi(o_);
+ else if (!strcmp(t_, "AlarmText"))
+ strcpy(alarm_message, o_);
+ else if (!strcmp(t_, "AlarmBeep"))
+ alarm_beep = !strcmp(o_, "on");
+ else if (!strcmp(t_, "AlarmType"))
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ if (!strcmp(alarm_types[i], o_))
+ alarm_type = i;
+ }
+ else if (isdigit((unsigned char)*t_))
+ {
+ int t = atoi(t_);
+ if (t >= 0 && t < MAX_TERM_DATA)
+ {
+ char *f_, *x0_, *y0_, *x1_, *y1_, *sx_, *sy_;
+ o_ = strtok(o_, " "); /* first word */
+ f_ = strtok(NULL, " "); /* font name */
+ x0_ = strtok(NULL, " "); /* x posn (min) */
+ y0_ = strtok(NULL, " "); /* y posn (min) */
+ x1_ = strtok(NULL, " "); /* x posn (max) */
+ y1_ = strtok(NULL, " "); /* y posn (max) */
+ sx_ = strtok(NULL, " "); /* x scroll offset */
+ sy_ = strtok(NULL, "\n"); /* y scroll offset */
+ data[t].def_open = (t == 0) || atoi(o_);
+ data[t].def_pos.min.x = atoi(x0_);
+ data[t].def_pos.min.y = atoi(y0_);
+ data[t].def_pos.max.x = atoi(x1_);
+ data[t].def_pos.max.y = atoi(y1_);
+ data[t].def_scroll.x = atoi(sx_);
+ data[t].def_scroll.y = atoi(sy_);
+ data[t].unopened = 1; /* ie. force def_pos */
+#ifndef FULLSCREEN_ONLY
+ attach_font_to_term(&(data[t]), f_);
+#endif /* FULLSCREEN_ONLY */
+ }
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+#ifndef FULLSCREEN_ONLY
+ /*
+ | Fudge so that the main term is *always* fullsize
+ */
+ {
+ int fw, fh;
+
+ set_up_zrb(&(data[0]));
+ fw = zrb.r_charw << screen_eig.x;
+ fh = zrb.r_charh << screen_eig.y;
+ if (zrb.r_flags.bits.double_height)
+ {
+ fh *= 2;
+ }
+ fw *= 80;
+ fh *= 24;
+ data[0].def_pos.max.x = data[0].def_pos.min.x + fw;
+ data[0].def_pos.max.y = data[0].def_pos.min.y + fh;
+ data[0].def_scroll.x = 0;
+ data[0].def_scroll.y = 0;
+ }
+#endif /* FULLSCREEN_ONLY */
+
+
+}
+
+
+
+
+static void save_choices(void)
+{
+ FILE *fp = NULL;
+ FILE *fpm = NULL;
+ char *cf;
+ int i;
+
+ write_alarm_choices();
+
+ cf = find_choices(TRUE);
+ if (!*cf)
+ {
+ plog("Failed to locate writable choices file!");
+ return;
+ }
+
+ fp = fopen(cf, "w");
+ if (!fp)
+ {
+ plog("Can't write choices file");
+ return;
+ }
+
+ fpm = fopen(find_choices_mirror(), "w");
+
+ f2printf(fp, fpm, "[Angband config, Musus' port]\n");
+ f2printf(fp, fpm, "Gamma %.2lf\n", gamma);
+ f2printf(fp, fpm, "Monochrome %s\n", force_mono ? "on" : "off");
+ f2printf(fp, fpm, "Sound %s\n", enable_sound ? "on" : "off");
+ f2printf(fp, fpm, "Volume %d\n", sound_volume);
+ f2printf(fp, fpm, "FullScreen %s\n", start_fullscreen ? "on" : "off");
+ f2printf(fp, fpm, "Hourglass %s\n", use_glass ? "on" : "off");
+ f2printf(fp, fpm, "HackFlush %s\n", hack_flush ? "on" : "off");
+
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ window_state ws;
+ Wimp_GetWindowState(data[i].w, &ws);
+ f2printf(fp, fpm, "%d %d %s ", i, ws.flags.data.open,
+ data[i].font->name);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.x);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.y);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.x);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.y);
+ f2printf(fp, fpm, "%d %d\n", ws.openblock.scroll.x,
+ ws.openblock.scroll.y);
+ }
+
+ fclose(fp);
+
+ if (fpm)
+ {
+ fclose(fpm);
+ }
+}
+
+/*
+ | Update the Alarm choices file to reflect changed alarm settings.
+ */
+static void write_alarm_choices(void)
+{
+ FILE *fp;
+ char *cf;
+
+ /* Open the choices file for reading */
+ cf = find_alarmfile(TRUE);
+ if (!*cf)
+ {
+ plog("Can't determine Alarm file location!");
+ return;
+ }
+
+ fp = fopen(cf, "w");
+ if (!fp)
+ {
+ plog("Can't write Alarm file");
+ return;
+ }
+
+ /* Write the new alarm options */
+ fprintf(fp, "AlarmType %s\n", alarm_types[alarm_type]);
+ fprintf(fp, "AlarmTimeH %d\n", alarm_h);
+ fprintf(fp, "AlarmTimeM %d\n", alarm_m);
+ fprintf(fp, "AlarmText %s\n", alarm_message);
+ fprintf(fp, "AlarmBeep %s\n", alarm_beep ? "on" : "off");
+
+ fclose(fp);
+}
+
+/*
+ | Read the Alarm choices file.
+ */
+static void read_alarm_choices(void)
+{
+ char buffer[260];
+ FILE *fp;
+ char *cf;
+
+ cf = find_alarmfile(FALSE);
+ if (!*cf)
+ {
+ return;
+ }
+
+ fp = fopen(cf, "r");
+ if (fp)
+ {
+ const char *t_, *o_;
+ /* Load choices */
+ while (fgets(buffer, sizeof(buffer), fp))
+ {
+ t_ = strtok(buffer, " "); /* Keyword */
+ o_ = strtok(NULL, "\n"); /* argument string */
+ if (!o_)
+ {
+ o_ = "";
+ } /* missing (or null) argument? */
+ if (t_)
+ {
+ if (!strcmp(t_, "AlarmTimeH"))
+ alarm_h = atoi(o_);
+ else if (!strcmp(t_, "AlarmTimeM"))
+ alarm_m = atoi(o_);
+ else if (!strcmp(t_, "AlarmText"))
+ strcpy(alarm_message, o_);
+ else if (!strcmp(t_, "AlarmBeep"))
+ alarm_beep = !strcmp(o_, "on");
+ else if (!strcmp(t_, "AlarmType"))
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ if (!strcmp(alarm_types[i], o_))
+ alarm_type = i;
+ }
+ }
+ }
+ fclose(fp);
+ }
+}
+
+
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Handle selections from the term menu(s)
+ */
+static BOOL Hnd_TermMenu(event_pollblock * pb, void *ref)
+{
+ mouse_block mb;
+ int i;
+
+ Wimp_GetPointerInfo(&mb);
+
+ switch (pb->data.selection[0])
+ {
+ case TERM_MENU_INFO: /* Info> */
+ break;
+ case TERM_MENU_FONT: /* Font> */
+ /* Sub item selected? */
+ if (pb->data.selection[1] == -1)
+ {
+ break;
+ }
+ handle_font_selection(pb->data.selection + 1);
+ break;
+ case TERM_MENU_WINDOWS: /* Windows> */
+ if (pb->data.selection[1] == -1)
+ {
+ break;
+ }
+ i = pb->data.selection[1];
+ {
+ window_state ws;
+ Wimp_GetWindowState(data[i].w, &ws);
+ if (ws.flags.data.open)
+ {
+ if (!i)
+ Hnd_MainClose(NULL, (void *)TRUE);
+ else
+ Window_Hide(data[i].w);
+ }
+ else
+ {
+ if (!i)
+ {
+ show_windows();
+ grab_caret();
+ }
+ else
+ {
+ if (!data[i].unopened)
+ {
+ Window_Show(data[i].w, open_WHEREVER);
+ }
+ else
+ {
+ window_openblock ob;
+ ob.window = data[i].w;
+ ob.screenrect = data[i].def_pos;
+ ob.scroll = data[i].def_scroll;
+ ob.behind = -1; /* could use data[0].w; ? */
+ Wimp_OpenWindow(&ob);
+ data[i].unopened = 0;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ if (mb.button.data.adjust)
+ {
+ set_up_term_menu(menu_term);
+ Menu_ShowLast();
+ }
+
+ return TRUE;
+}
+
+
+
+
+
+
+/*
+ | Handle selections from the iconbar menu
+ */
+static BOOL Hnd_IbarMenu(event_pollblock * pb, void *ref)
+{
+ mouse_block mb;
+ Wimp_GetPointerInfo(&mb);
+
+ switch (pb->data.selection[0])
+ {
+ case IBAR_MENU_INFO: /* Info> */
+ break;
+ case IBAR_MENU_FULLSCREEN: /* Full screen */
+ /* Do Full Screen mode */
+ enter_fullscreen_mode();
+ break;
+ case IBAR_MENU_GAMMA: /* Gamma correction */
+ break;
+ case IBAR_MENU_SOUND: /* Sound */
+ /*
+ | enable_sound = !enable_sound;
+ | if ( enable_sound ) { initialise_sound(); }
+ | Menu_SetFlags( ibar_menu, IBAR_MENU_SOUND, enable_sound, 0 );
+ | set_sound_window_state();
+ */
+ break;
+ case IBAR_MENU_WINDOWS: /* Windows> */
+ /*
+ | Hack: pass it off as the equivalent selection from
+ | the term menu.
+ */
+ pb->data.selection[0] = TERM_MENU_WINDOWS;
+ return Hnd_TermMenu(pb, ref);
+ break;
+ case IBAR_MENU_SAVECHOICES: /* Save choices */
+ save_choices();
+ break;
+ case IBAR_MENU_QUIT: /* Quit */
+ if (game_in_progress && character_generated)
+ save_player();
+ quit(NULL);
+ break;
+ }
+
+ if (mb.button.data.adjust)
+ Menu_ShowLast();
+
+ return TRUE;
+}
+
+
+
+
+/*
+ * Handler for NULL events (should this check the alarm in the desktop?
+ */
+static BOOL Hnd_null(event_pollblock *event, void *ref)
+{
+ /* Really no need to check the alarm more than once per second. */
+ if (alarm_type && Time_Monotonic() > alarm_lastcheck + 100)
+ {
+ check_alarm();
+ }
+
+ return TRUE;
+}
+
+
+
+
+
+
+
+
+static BOOL Hnd_MenuSel(event_pollblock * pb, void *ref)
+{
+ if (menu_currentopen == ibar_menu)
+ return Hnd_IbarMenu(pb, ref);
+ else if (menu_currentopen == term_menu)
+ return Hnd_TermMenu(pb, ref);
+ return FALSE;
+}
+
+
+static BOOL Hnd_IbarClick(event_pollblock * pb, void *ref)
+{
+ if (pb->data.mouse.button.data.menu)
+ {
+ set_gamma_window_state();
+ set_sound_window_state();
+
+ /* Hack: shade the Save> option if appropriate */
+ if (game_in_progress && character_generated)
+ Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, PDEADCHK);
+ else
+ Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, TRUE);
+
+ /*
+ | Hack: set up the Term menu as if it was opened over the main
+ | window (so that the Windows> submenu is set correctly)
+ */
+ menu_term = (term_data *)&data[0];
+ set_up_term_menu(menu_term);
+
+ Menu_Show(ibar_menu, pb->data.mouse.pos.x, -1);
+ return TRUE;
+ }
+
+ if (pb->data.mouse.button.data.select)
+ {
+ show_windows();
+ grab_caret();
+ return TRUE;
+ }
+
+ if (pb->data.mouse.button.data.adjust)
+ {
+ enter_fullscreen_mode();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+
+/*
+ | Handler for PreQuit messages (eg. at shutdown).
+ */
+static BOOL Hnd_PreQuit(event_pollblock * b, void *ref)
+{
+ BOOL shutdown = (b->data.message.data.words[0] & 1) == 0;
+ task_handle originator = b->data.message.header.sender;
+ unsigned int quitref;
+ message_block mb;
+ char buffer1[64];
+ os_error e;
+ int ok;
+
+ if (!(game_in_progress && character_generated))
+ return TRUE; /* ignore, we're OK to die */
+
+ /* Stop the shutdown/quit */
+ memcpy(&mb, &(b->data.message), 24);
+ quitref = mb.header.yourref;
+ mb.header.yourref = mb.header.myref;
+ Wimp_SendMessage(event_ACK, &mb, originator, 0);
+
+ /*
+ | We handle this differently depending on the version of the Wimp;
+ | newer versions give us much more flexibility.
+ */
+ if (event_wimpversion < 350)
+ {
+ /*
+ | Older versions - use 'OK' and 'Cancel'.
+ | There is no "Save & Quit" button.
+ */
+ Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
+ sprintf(buffer1, e.errmess, VARIANT);
+ Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
+ e.errmess, 260);
+ e.errnum = 0;
+ SWI(3, 2, SWI_Wimp_ReportError, &e, 3 | 16, buffer1, NULL, &ok);
+
+ if (ok != 1)
+ return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */
+ }
+ else
+ {
+ /*
+ | Newer version: can add buttons to the dialog.
+ | we add a 'Save and Quit' button to allow the shutdown to
+ | continue /after/ saving.
+ */
+ int flags;
+ char buttons[64];
+
+ Msgs_Lookup("err.shutbuts:Save & quit,Don't quit,Quit anyway",
+ buttons, 64);
+ Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
+ sprintf(buffer1, e.errmess, VARIANT);
+ Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
+ e.errmess, 260);
+ e.errnum = 0;
+
+ flags = 0 | 16 | 256 | (4 << 9);
+
+ SWI(6, 2, SWI_Wimp_ReportError, &e, flags, buffer1, ICONNAME, 0,
+ buttons, NULL, &ok);
+
+ if (ok == 4)
+ return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */
+
+ if (ok == 3)
+ save_player(); /* Save & Quit */
+ }
+
+
+ /* RO2 doesn't use the shudown flag */
+ if (shutdown && event_wimpversion >= 300 && mb.header.size >= 24)
+ {
+ key_block kb;
+ kb.code = 0x1fc; /* restart shutdown sequence */
+ Wimp_SendMessage(event_KEY, (message_block *) & kb, originator, 0);
+ }
+
+ /* "Time... to die." */
+ Event_CloseDown();
+ exit(0);
+ return TRUE; /* The one great certainty (sic) */
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+
+static void initialise_terms(void)
+{
+ char t[80];
+ int i;
+
+#ifndef FULLSCREEN_ONLY
+ if (!minimise_memory)
+ {
+ /* Create a window for each term. Term 0 is special (no scroll bars) */
+ data[0].w = Window_Create("angband", sizeof(angband_term_name[0]));
+ data[0].font = SYSTEM_FONT;
+ data[0].def_open = 1;
+ data[0].unopened = 1;
+ sprintf(t, "%s %s", VARIANT, VERSION);
+ Window_SetTitle(data[0].w, t);
+ strncpy(data[0].name, VARIANT, 12);
+ Event_Claim(event_KEY, data[0].w, event_ANY, Hnd_Keypress,
+ (void *)&(data[0]));
+ Event_Claim(event_REDRAW, data[0].w, event_ANY, Hnd_Redraw,
+ (void *)&(data[0]));
+ Event_Claim(event_CLICK, data[0].w, event_ANY, Hnd_TermClick,
+ (void *)&(data[0]));
+ Event_Claim(event_CLOSE, data[0].w, event_ANY, Hnd_MainClose, NULL);
+
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ data[i].w = Window_Create("term", template_TITLEMIN);
+ data[i].font = SYSTEM_FONT;
+ data[i].def_open = 0;
+ data[i].unopened = 1;
+#ifndef OLD_TERM_MENU
+ sprintf(t, "%s (%s %s)", angband_term_name[i], VARIANT, VERSION);
+#else
+ sprintf(t, "Term-%d (%s %s)", i, VARIANT, VERSION);
+#endif
+ Window_SetTitle(data[i].w, t);
+ strncpy(data[i].name, t, 12);
+ Event_Claim(event_CLICK, data[i].w, event_ANY, Hnd_TermClick,
+ (void *)&(data[i]));
+ Event_Claim(event_REDRAW, data[i].w, event_ANY, Hnd_Redraw,
+ (void *)&(data[i]));
+ }
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ term_data_link(&(data[0]), 256);
+
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ term_data_link(&(data[i]), 16);
+ angband_term[i] = &(data[i].t);
+ }
+
+ angband_term[0] = &(data[0].t);
+ Term_activate(&(data[0].t));
+}
+
+
+
+
+/*
+ | Hack(ish) - determine the version of RISC OS
+ */
+static int os_version(void)
+{
+ int osv;
+ SWI(3, 2, SWI_OS_Byte, 129, 0, 255, NULL, &osv);
+ switch (osv)
+ {
+ case 0xa0: return 120;
+ case 0xa1: return 200;
+ case 0xa2: return 201;
+ case 0xa3: return 300;
+ case 0xa4: return 310;
+ case 0xa5: return 350;
+ case 0xa6: return 360;
+ case 0xa7: return 370;
+ case 0xa8: return 400;
+ default: return 370;
+ }
+ return -1; /* -sigh- */
+}
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Determine whether the current screen mode is "high-res"
+ | (ie. should we use the "Sprites22" or the "Sprites" file.
+ */
+static int sprites22(void)
+{
+ int yeig;
+
+ OS_ReadModeVariable(-1, modevar_YEIGFACTOR, &yeig);
+
+ return yeig < 2;
+}
+
+/*
+ | Determine whether we should use 2D or 3D templates.
+ | 2D templates *must* be used under Wimp <3.00, but under RO3 we
+ | use the CMOS settings to decide.
+ */
+static int templates2d(void)
+{
+ int r1, r2;
+
+ if (event_wimpversion < 300)
+ return TRUE;
+
+ /* The 3D bit is bit 0 of byte 140 */
+ OS_Byte(osbyte_READCMOSRAM, 140, 0, &r1, &r2);
+
+ return !(r2 && 1);
+}
+#endif /* FULLSCREEN_ONLY */
+
+
+
+static unsigned int htoi(char *s)
+{
+ static const char hex[] = "0123456789ABCDEF";
+ unsigned int v = 0;
+ while (*s)
+ {
+ char *m;
+ int d = toupper((unsigned char)*s++);
+ m = strchr(hex, d);
+ if (!m)
+ {
+ return v;
+ }
+ v = (v << 4) + (m - hex);
+ }
+ return v;
+}
+
+static int read_unsigned(char *t)
+{
+ int r;
+ if (SWI(2, 3, SWI_OS_ReadUnsigned, 2, t, NULL, NULL, &r))
+ r = 0;
+ return r;
+}
+
+/*
+ | Scan the string at 'n', replacing dodgy characters with underbars
+ */
+static void sanitise_name(char *n)
+{
+ for (; *n; n++)
+ {
+ if (strchr("\"$%^&*\\\'@#.,", *n))
+ *n = '_';
+ }
+}
+
+
+/*
+ | Ensure that the path to a given object exists.
+ | Ie. if |p| = "a.b.c.d" then we attempt to
+ | create directories a, a.b and a.b.c if they don't
+ | already exist.
+ | Note that 'd' may be absent.
+ */
+static int ensure_path(char *p)
+{
+ char tmp[260];
+ char *l = tmp;
+
+ while (*p)
+ {
+ if (*p == '.')
+ {
+ *l = 0;
+ if (SWI(5, 0, SWI_OS_File, 8, tmp, 0, 0, 77))
+ return 0; /* Eeek! */
+ }
+ *l++ = *p++;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Set up the Scrap, Choices and Alarm paths, trying for
+ * Choices:blah...,etc. by preference, but falling back on lib/xtra
+ * if need be.
+ */
+static void init_paths(void)
+{
+ char tmp[512];
+ char subpath[128];
+ char *v;
+ char *t;
+
+ /* Form the sub-path we use for both Choices and Scrap dirs: */
+ v = subpath + sprintf(subpath, "%s", VARIANT);
+ sanitise_name(subpath);
+ sprintf(v, ".%s", VERSION);
+ sanitise_name(v + 1);
+
+ /* Do the Scrap path first: */
+ *scrap_path = 0;
+
+ /* Try for Wimp$ScrapDir... */
+ t = getenv("Wimp$ScrapDir");
+ if (t && *t)
+ {
+ sprintf(tmp, "%s.AngbandEtc.%s.", t, subpath);
+ if (ensure_path(tmp))
+ {
+ strcpy(scrap_path, tmp);
+ }
+ }
+
+ /* Couldn't use Wimp$ScrapDir, so fall back on lib.xtra.scrap */
+ if (!*scrap_path)
+ {
+ sprintf(tmp, "%sxtra.scrap.", resource_path);
+ if (ensure_path(tmp))
+ {
+ strcpy(scrap_path, tmp);
+ }
+ }
+
+ /* Now set up the Choices and Alarm files: */
+
+ /* Read only Choices file is always lib.xtra.Choices */
+ sprintf(choices_file[CHFILE_READ], "%sXtra.Choices", resource_path);
+ /* Default writable Choices file is the same */
+ strcpy(choices_file[CHFILE_WRITE], choices_file[CHFILE_READ]);
+ /* No default mirror Choices file */
+ strcpy(choices_file[CHFILE_MIRROR], "");
+
+ /* Read only Alarm file is always lib.xtra.Alarm */
+ sprintf(alarm_file[CHFILE_READ], "%sXtra.Alarm", resource_path);
+ /* Default writable Alarm file is the same */
+ strcpy(alarm_file[CHFILE_WRITE], alarm_file[CHFILE_READ]);
+
+ /* Try to use Choices$Path, etc. for the others... */
+
+ t = getenv("Choices$Write"); /* Ie. where choices should be written */
+ if (t && *t)
+ {
+ /* Choices file: */
+ sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
+ if (ensure_path(tmp))
+ {
+ /* Use for writable file: */
+ strcpy(choices_file[CHFILE_WRITE], tmp);
+ /* Form 'mirror' filename: same path but with a fixed leafname */
+ strcpy(v + 1, "Default");
+ sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
+ strcpy(choices_file[CHFILE_MIRROR], tmp);
+ }
+
+ /* Alarm file (doesn't involve subpath) */
+ sprintf(tmp, "%s.AngbandEtc.Global.Alarm", t);
+ if (ensure_path(tmp))
+ {
+ /* Use for read/writable file */
+ strcpy(alarm_file[CHFILE_WRITE], tmp);
+ }
+ }
+}
+
+
+
+
+
+
+
+/*
+ * Return the appropriate (full) pathname.
+ *
+ * For write ops, the read/write file is returned.
+ *
+ * For read ops, either the read/write file, the mirror file,
+ * or the read only file will be returned as appropriate.
+ */
+static char *find_choices(int write)
+{
+ if (write)
+ return choices_file[CHFILE_WRITE];
+
+ if (myFile_Size(choices_file[CHFILE_WRITE]) > 0)
+ return choices_file[CHFILE_WRITE];
+
+ if (myFile_Size(choices_file[CHFILE_MIRROR]) > 0)
+ return choices_file[CHFILE_MIRROR];
+
+ return choices_file[CHFILE_READ];
+}
+
+static char *find_choices_mirror(void)
+{
+ return choices_file[CHFILE_MIRROR];
+}
+
+static char *find_alarmfile(int write)
+{
+ if (write)
+ return alarm_file[CHFILE_WRITE];
+
+ if (myFile_Size(alarm_file[CHFILE_WRITE]) > 0)
+ return alarm_file[CHFILE_WRITE];
+
+ return alarm_file[CHFILE_READ];
+}
+
+
+
+
+int main(int argc, char *argv[])
+{
+ int i, j;
+ int start_full = 0;
+ char *arg_savefile = 0;
+ char *t;
+#ifdef USE_DA
+ int da_font = 1, da_game = 1;
+#endif
+
+ atexit(final_acn); /* "I never did care about the little things." */
+
+ Start_Hourglass;
+
+ /* Parse arguments */
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (tolower((unsigned char)argv[i][1]))
+ {
+ case 'm':
+ {
+ /* Minimise Memory */
+ minimise_memory = 1;
+
+ /* Break */
+ break;
+ }
+ case 'c': /* -c[a][s][f][<n>] */
+ for (j = 2; argv[i][j]; j++)
+ {
+ int on = isupper((unsigned char)argv[i][j]);
+
+ switch (tolower((unsigned char)argv[i][j]))
+ {
+#ifdef ABBR_FILECACHE
+ case 'a': abbr_filecache =
+ on;
+ break;
+ case 'f': abbr_tmpfile =
+ on;
+ break;
+#endif
+#ifdef SMART_FILECACHE
+ case 's': smart_filecache =
+ on;
+ break;
+#endif
+
+ case 'p': flush_scrap =
+ !on;
+ break;
+
+ default:
+ if (isdigit((unsigned char)argv[i][j]))
+ {
+ max_file_cache_size =
+ atoi(argv[i] + j) << 10;
+ while (isdigit((unsigned char)argv[i][++j]))
+ ;
+ if (max_file_cache_size <= 0)
+ {
+ use_filecache = 0;
+ }
+ j--;
+ }
+ else
+ {
+ fprintf(stderr, "Unrecognised option: -c%s",
+ argv[i] + j);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case 'w': /* -waitrelease */
+ hack_flush = 1;
+ break;
+ case 'e': /* -Evil */
+ allow_iclear_hack = 1;
+ break;
+ case 's': /* -s<savefile> */
+ if (argv[i][2])
+ arg_savefile = argv[i] + 2;
+ break;
+ case 'f': /* -fullscreen */
+ start_full = 1;
+ break;
+ case 'h': /* -hourglass */
+ use_glass = 1;
+ break;
+ case 't': /* -T<filetype> */
+ if (argv[i][2])
+ vfiletype = htoi(argv[i] + 2);
+ break;
+#ifdef USE_DA
+ case 'd': /* -df, -dg, -dc or -d : disable DAs */
+ switch (tolower((unsigned char)argv[i][2]))
+ {
+ case 0: /* -d => disable both */
+ da_font = da_game = 0;
+ break;
+ case 'f': /* -df => disable font only */
+ da_font = 0;
+ break;
+ case 'g': /* -dg => disable game only */
+ da_game = 0;
+ break;
+ }
+ break;
+#endif
+ case '%': /* -%<debug_opts> */
+ {
+ int v = read_unsigned(argv[i] + 2);
+ log_g_malloc = v & 1;
+ show_sound_alloc = v & 2;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unrecognised option: %s", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* 1.27 - new handling of -minimise-memory: */
+#ifndef FULLSCREEN_ONLY
+ if (minimise_memory)
+#endif /* FULLSCREEN_ONLY */
+ {
+ start_full = 1;
+ fs_quit_key_text = "(fullscreen only mode)";
+ }
+#ifdef USE_DA
+ init_memory(da_font, da_game); /* Set up dynamic areas, etc. if possible */
+
+ /* Install memory allocation hooks */
+ ralloc_aux = g_malloc;
+ rnfree_aux = g_free;
+#endif
+
+ /* Install replacement error reporting routines */
+ quit_aux = quit_hook;
+ plog_aux = plog_hook;
+ core_aux = core_hook;
+
+ /* Expand the (Angband) resource path */
+ t = getenv(RISCOS_VARIANT "$Path");
+ if (!t || !*t) Msgs_ReportFatal(0, "A resources path could not be formed.");
+ strcpy(resource_path, t);
+
+ /* Decide where scrap, choices and alarm files live: */
+ init_paths();
+
+ /* Hack: if no savefile specified, use a default */
+ if (!arg_savefile)
+ {
+ arg_savefile = malloc(strlen(resource_path) + 32);
+ if (!arg_savefile)
+ {
+ Msgs_ReportFatal(0, "err.mem");
+ }
+ sprintf(arg_savefile, "%s%s", resource_path, ">Save.Savefile");
+ }
+
+ /* This crap appears here so that plog() will work properly before
+ init_acn() is called... */
+ Resource_Initialise(RISCOS_VARIANT);
+ Msgs_LoadFile("Messages");
+
+#ifndef FULLSCREEN_ONLY
+ if (!minimise_memory)
+ {
+ /* This is a hack to only call Event_Initialise3 under RO3 */
+ if (os_version() < 300)
+ Event_Initialise(RISCOS_VARIANT);
+ else
+ Event_Initialise3(RISCOS_VARIANT, 300, message_list);
+
+ EventMsg_Initialise();
+
+ /*
+ | This is a possible workaround for the FP regs getting
+ | bolloxed in the ! menu because the compiler sets them
+ | up before a call to Wimp_Poll if CSE optimisation is on.
+ | At the moment I've just turned off CSE for the function
+ | affected.
+ |
+ | event_mask.data.keepfpregisters = 1;
+ */
+
+ /* Load Templates */
+ Template_Initialise();
+ if (templates2d())
+ Template_LoadFile("Templates2");
+ else
+ Template_LoadFile("Templates");
+
+ /* Load Sprites */
+ if (sprites22())
+ resource_sprites =
+ Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites22");
+ else
+ resource_sprites = Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites");
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ Screen_CacheModeInfo();
+
+ /* Initialise some ZapRedraw stuff */
+ initialise_palette();
+ initialise_fonts(); /* Set up the fonts */
+#ifndef FULLSCREEN_ONLY
+ initialise_r_data(); /* Set up the r_data buffer */
+
+ if (!minimise_memory)
+ {
+ /* Initialise some Wimp specific stuff */
+ init_gamma_window();
+ init_sound_window();
+ init_save_window();
+ init_menus();
+ ibar_icon = Icon_BarIcon(ICONNAME, iconbar_RIGHT);
+
+ /* Global handlers */
+ Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL);
+ Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL);
+ Event_Claim(event_GAINCARET, event_ANY, event_ANY, Hnd_Caret, (void *)1);
+ Event_Claim(event_LOSECARET, event_ANY, event_ANY, Hnd_Caret, (void *)0);
+ Event_Claim(event_MENU, event_ANY, event_ANY, Hnd_MenuSel, NULL);
+ Event_Claim(event_CLICK, window_ICONBAR, ibar_icon, Hnd_IbarClick, NULL);
+ Event_Claim(event_CLICK, event_ANY, event_ANY, Hnd_Click, NULL);
+ EventMsg_Claim(message_PALETTECHANGE, event_ANY, Hnd_PaletteChange, NULL);
+ EventMsg_Claim(message_MODECHANGE, event_ANY, Hnd_ModeChange, NULL);
+ EventMsg_Claim(message_PREQUIT, event_ANY, Hnd_PreQuit, NULL);
+
+ /* Initialise the sound stuff */
+ initialise_sound();
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ /* Initialise some Angband stuff */
+ initialise_terms();
+ load_choices();
+ read_alarm_choices();
+ init_file_paths(unixify_name(resource_path));
+
+ Start_Hourglass; /* Paranoia */
+
+ /* Hack - override the saved options if -F was on the command line */
+ start_fullscreen |= start_full;
+
+ /* hack so that the cursor is yellow if undefined */
+ if (palette[CURSOR_COLOUR] == palette[0])
+ {
+ angband_color_table[CURSOR_COLOUR][1] = (CURSOR_RGB & 0xff00) >> 8;
+ angband_color_table[CURSOR_COLOUR][2] = (CURSOR_RGB & 0xff0000) >> 16;
+ angband_color_table[CURSOR_COLOUR][3] = (CURSOR_RGB & 0xff000000) >> 24;
+ }
+
+ /* Catch nasty signals */
+ signals_init();
+ /* use pref-acn.prf */
+ ANGBAND_SYS = "acn";
+
+#ifndef FULLSCREEN_ONLY
+ if (start_fullscreen)
+ {
+#endif /* FULLSCREEN_ONLY */
+ enter_fullscreen_mode();
+#ifndef FULLSCREEN_ONLY
+ }
+ else
+ {
+ Start_Hourglass; /* Paranoia */
+ Hnd_ModeChange(NULL, NULL); /* Caches the various fonts/palettes */
+ show_windows();
+ grab_caret();
+
+ Event_Claim(event_NULL, event_ANY, event_ANY, Hnd_null, NULL);
+
+ /* Wait for a null poll so that the windows can appear */
+ do
+ {
+ Event_Poll();
+ }
+ while (event_lastevent.type != 0);
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ /* Initialise Angband */
+ Start_Hourglass; /* Paranoia */
+
+ strncpy(savefile, unixify_name(arg_savefile), sizeof(savefile));
+ savefile[sizeof(savefile) - 1] = '\0';
+
+ use_sound = 1;
+ init_angband();
+ initialised = 1;
+ game_in_progress = 1;
+/* pause_line(23);*/
+ flush();
+ /* Stop_Hourglass; */
+ play_game(FALSE);
+
+ if (fullscreen_mode) leave_fullscreen_mode();
+
+ Stop_Hourglass;
+
+ quit(NULL);
+
+ return 0;
+
+ debug("to stop the 'unused' warning :)");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Stuff below here is for the full screen display */
+/*--------------------------------------------------------------------------*/
+
+static errr Term_xtra_acn_checkFS(void);
+static errr Term_xtra_acn_eventFS(void);
+static errr Term_xtra_acn_reactFS(int force);
+static errr Term_curs_acnFS(int x, int y);
+static errr Term_xtra_acn_clearFS(void);
+static errr Term_xtra_acnFS(int n, int v);
+static errr Term_wipe_acnFS(int x, int y, int n);
+static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
+static void bored(void);
+static void redraw_areaFS(int x, int y, int w, int h);
+static void draw_cursor(int x, int y);
+
+/*
+ | We use this to keep the mouse in the same place on return from full-screen
+ | mode.
+ */
+static wimp_point old_mouse_posn;
+
+/*
+ | Take a copy of the current mode descriptor/number and return either
+ | a pointer to it (as an int) if it's a new mode, or the mode number.
+ | NB: A static pointer is used and the descriptor returned is only
+ | valid until the next call to this function.
+ |
+ | Basically, a replacement for OS_Byte 135 / OS_ScreenMode1, IYSWIM.
+ */
+static int current_mode(void)
+{
+ static void *descriptor = NULL;
+ int mode;
+ int size;
+ int i;
+ int *vals;
+
+ if (descriptor)
+ {
+ free(descriptor);
+ descriptor = NULL;
+ }
+
+ SWI(1, 3, SWI_OS_Byte, 135, NULL, NULL, &mode);
+ if (mode < 256)
+ {
+ return mode;
+ }
+
+ vals = (int *)(mode + 20);
+ for (i = 0; vals[i] != -1; i += 2)
+ ;
+
+ size = 24 + 8 * i; /* Size of data */
+ descriptor = malloc(size);
+ if (!descriptor)
+ {
+ core("Out of memory!");
+ }
+ memcpy(descriptor, (void *)mode, size);
+
+ return (int)descriptor;
+}
+
+
+
+/*
+ | Select the best mode we can for full screen.
+ | Returns 12 for (low-res, ie. mode 12) or 27 for high-res,
+ | or a pointer to a mode descriptor (as an int).
+ */
+static int select_fullscreen_mode(void)
+{
+ static struct
+ {
+ int flags, x, y, l2bpp, hz, term;
+ }
+ desc;
+ int mode = 0;
+
+ desc.flags = 1; /* format 0 */
+ desc.x = 640;
+ desc.y = 480; /* 640x480 */
+ desc.l2bpp = 2; /* 16 colours */
+ desc.hz = -1; /* best we can get */
+ desc.term = -1; /* don't fuss about modevars */
+
+ SWI(1, 1, SWI_OS_CheckModeValid, &desc, &mode);
+ if (mode != (int)&desc)
+ {
+ SWI(1, 1, SWI_OS_CheckModeValid, 27, /**/ &mode);
+ if (mode != 27)
+ {
+ SWI(1, 1, SWI_OS_CheckModeValid, 12, /**/ &mode);
+ if (mode != 12)
+ {
+ mode = 0;
+ }
+ }
+ }
+
+ return mode;
+}
+
+
+/*
+ | Change screen mode
+ */
+static void change_screenmode(int to)
+{
+ if (SWI(2, 0, SWI_OS_ScreenMode, 0, to))
+ {
+ if (to < 256)
+ {
+ GFX_VDU(22);
+ GFX_VDU(to);
+ }
+ else
+ {
+ /* Finished with my woman / cos she couldn't help me with ... */
+ core("Eeek! mode isn't valid, but it /should/ be...");
+ }
+ }
+}
+
+
+/*
+ | Constrain the mouse pointer to a point - this means that the damn
+ | hourglass won't move around with the mouse :)
+ */
+static void constrain_pointer(void)
+{
+ mouse_block ptr;
+ wimp_rect r;
+ int ys = screen_eig.y == 1 ? 32 : 64; /* Cope with dbl height glass */
+
+ Screen_CacheModeInfo(); /* Make sure we know the screen size */
+ r.min.x = r.max.x = screen_size.x - 32;
+ r.min.y = r.max.y = screen_size.y - ys;
+
+ /* Retrieve and store old (wimp) pointer position */
+ Wimp_GetPointerInfo(&ptr);
+ old_mouse_posn = ptr.pos;
+
+ Pointer_RestrictToRect(r);
+
+ /* Turn the pointer off also */
+ SWI(2, 0, SWI_OS_Byte, 106, 0);
+}
+
+static void release_pointer()
+{
+ wimp_rect r;
+
+ r.min.x = r.max.x = old_mouse_posn.x;
+ r.min.y = r.max.y = old_mouse_posn.y;
+
+ Pointer_RestrictToRect(r);
+
+ Pointer_Unrestrict();
+
+ /* Turn the pointer back on also */
+ SWI(2, 0, SWI_OS_Byte, 106, 1);
+}
+
+
+
+
+
+/*
+ | Convert a 1bpp bitmap into a 4bpp bitmap (bit flipped)
+ */
+static int byte_to_word_flipped(int b)
+{
+ int w;
+ if (b & 128)
+ {
+ w = 0xf0000000;
+ }
+ else
+ {
+ w = 0;
+ }
+ if (b & 64)
+ {
+ w |= 0x0f000000;
+ }
+ if (b & 32)
+ {
+ w |= 0x00f00000;
+ }
+ if (b & 16)
+ {
+ w |= 0x000f0000;
+ }
+ if (b & 8)
+ {
+ w |= 0x0000f000;
+ }
+ if (b & 4)
+ {
+ w |= 0x00000f00;
+ }
+ if (b & 2)
+ {
+ w |= 0x000000f0;
+ }
+ if (b & 1)
+ {
+ w |= 0x0000000f;
+ }
+ return w;
+}
+
+
+
+
+/*
+ | try to load the fallback fullscreen font and convert it to 4bpp
+ */
+static int cache_zapfontHR(void)
+{
+ int handle;
+ unsigned int extent;
+ char buffer[260];
+ struct
+ {
+ char id[8];
+ int w, h, f, l, r1, r2;
+ }
+ zfh;
+ int *op;
+ char *ip;
+ int l, i;
+
+ /* Try to open the file */
+ sprintf(buffer, "%s%s", resource_path, "xtra.FullScreen");
+ handle = myFile_Open(buffer, 0x4f);
+ if (!handle)
+ {
+ return 0;
+ }
+
+ /* Check file's extent */
+ extent = myFile_Extent(handle);
+ if (extent > sizeof(zfh) + 256 * 16)
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ /* Load the header */
+ if (myFile_ReadBytes(handle, &zfh, sizeof(zfh)))
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ /* Check font size */
+ if ((zfh.w != 8) || (zfh.h > 16))
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ /* Load the 1bpp data */
+ if (myFile_ReadBytes(handle, fullscreen_font, extent - sizeof(zfh)))
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ myFile_Close(handle);
+
+ l = zfh.l > 255 ? 255 : zfh.l;
+
+ if (zfh.h > 8)
+ {
+ op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 4);
+ ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
+ (zfh.f * zfh.h));
+ while (l-- >= zfh.f)
+ {
+ for (i = 0; i < zfh.h; i++)
+ {
+ *--op = byte_to_word_flipped(*--ip);
+ }
+ }
+ fullscreen_height = zfh.h;
+ }
+ else
+ {
+ op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 8);
+ ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
+ (zfh.f * zfh.h));
+ while (l-- >= zfh.f)
+ {
+ for (i = -zfh.h; i < zfh.h; i++)
+ {
+ int t = byte_to_word_flipped(*--ip);
+ *--op = t;
+ *--op = t;
+ }
+ }
+ fullscreen_height = zfh.h * 2;
+ }
+
+ fullscreen_topline = TERM_TOPLINE_HR;
+ fullscreen_topline += ((16 - fullscreen_height) * 13);
+
+ return 1;
+}
+
+
+
+
+
+static int cache_fs_fontHR(void)
+{
+ ZapFont *src = SYSTEM_FONT;
+ int c;
+ int *op;
+ char *ip;
+
+ /* Allocate the storage for the font */
+ fullscreen_font = f_malloc(256 * 4 * 16);
+ if (!fullscreen_font)
+ {
+ return 0;
+ }
+ op = (int *)fullscreen_font;
+
+ /* Check to see if the main term's font is suitable (ie. 8x16 or 8x8) */
+ if ((data[0].font->w == 8) && (data[0].font->h <= 16))
+ src = data[0].font;
+
+ /*
+ | Hack: if we're forced to use the system font, try to load the
+ | 'fullscreen' font from lib.xtra. If that fails, then I guess we're
+ | stuck with the system font.
+ */
+
+ if (src == SYSTEM_FONT)
+ if (cache_zapfontHR())
+ {
+ return 1;
+ }
+
+ ip = (char *)(src->bpp_1);
+
+ /* Now, create the font */
+ if (src->h > 8)
+ {
+ int e = src->h * (src->l > 256 ? 256 : src->l);
+ op += (src->f * src->h);
+ for (c = src->f * src->h; c < e; c++)
+ *op++ = byte_to_word_flipped(*ip++);
+ fullscreen_height = src->h;
+ }
+ else
+ {
+ int e = src->h * (src->l > 256 ? 256 : src->l);
+ op += (src->f * src->h) * 2;
+ for (c = src->f * src->h; c < e; c++)
+ {
+ int t = byte_to_word_flipped(*ip++);
+ *op++ = t;
+ *op++ = t;
+ }
+ fullscreen_height = src->h * 2;
+ }
+
+ fullscreen_topline = TERM_TOPLINE_HR;
+ fullscreen_topline += ((16 - fullscreen_height) * 13);
+
+ return 1;
+}
+
+
+
+
+
+static int cache_fs_fontLR(void)
+{
+ ZapFont *src = SYSTEM_FONT;
+ int c, e;
+ int *op;
+ char *ip;
+
+ /* Allocate the storage for the font */
+ fullscreen_font = f_malloc(256 * 4 * 8);
+ if (!fullscreen_font)
+ {
+ return 0;
+ }
+ op = (int *)fullscreen_font;
+
+ /* Check to see if the main term's font is suitable (ie. 8x8) */
+ if ((data[0].font->w == 8) && (data[0].font->h <= 8))
+ src = data[0].font;
+
+ ip = (char *)(src->bpp_1);
+
+ /* Now, create the font */
+ e = src->h * (src->l > 256 ? 256 : src->l);
+ op += (src->f * src->h);
+ for (c = src->f * src->h; c < e; c++)
+ *op++ = byte_to_word_flipped(*ip++);
+
+ fullscreen_height = src->h;
+ fullscreen_topline = TERM_TOPLINE_LR;
+ fullscreen_topline += ((8 - fullscreen_height) * 13);
+
+ return 1;
+}
+
+
+
+static void set_keys(int claim)
+{
+ static int old_c_state;
+ static int old_f_state[8];
+ int i;
+
+ if (claim)
+ {
+ /* Cursors/copy act as function keys */
+ /* f0-f9, cursors, generate 0x80-0x8f */
+ /* sh-f0-f9,cursors, generate 0x90-0x9f */
+ /* ctrl f0-f9,cursors, generate 0xa0-0xaf */
+ /* sh-c-f0-f9,cursors, generate 0xb0-0xbf */
+ /* f10-f12 generate 0xca-0xcc */
+ /* shift f10-f12 generate 0xda-0xdc */
+ /* ctrl f10-f12 generate 0xea-0xec */
+ /* ctrlshift f10-f12 generate 0xfa-0xfc */
+
+ SWI(3, 2, SWI_OS_Byte, 4, 2, 0, /**/ NULL, &old_c_state);
+
+ for (i = 0; i < 4; i++)
+ {
+ SWI(3, 2, SWI_OS_Byte, 225 + i, 0x80 + (i * 0x10), 0, NULL,
+ old_f_state + i);
+ SWI(3, 2, SWI_OS_Byte, 221 + i, 0xc0 + (i * 0x10), 0, NULL,
+ old_f_state + i + 4);
+ }
+ }
+ else
+ {
+ SWI(3, 0, SWI_OS_Byte, 4, old_c_state, 0);
+ for (i = 0; i < 4; i++)
+ {
+ SWI(3, 0, SWI_OS_Byte, 225 + i, old_f_state[i], 0);
+ SWI(3, 0, SWI_OS_Byte, 221 + i, old_f_state[i + 4], 0);
+ }
+ }
+}
+
+
+
+
+
+
+/*
+ | Enter the full screen mode.
+ |
+ | Full screen display uses either mode 27 (if supported) and 8x16 fonts
+ | (or system font 'twiddled' to double height), or mode 12 (if mode 27
+ | is unavailable) and the system font (or an 8x8 font).
+ |
+ */
+
+static void enter_fullscreen_mode(void)
+{
+ int vduvars[2] =
+ { 149, -1 };
+ int i;
+
+ /* New in 1.18 - protect against 're-entracy' */
+ if (fullscreen_font)
+ return;
+
+ /* New in 1.20 - hack IClear out of the way */
+ if (allow_iclear_hack)
+ iclear_hack();
+
+ /* Choose the mode we want */
+ fullscreen_mode = select_fullscreen_mode();
+
+ if (!fullscreen_mode)
+ {
+ plog("Unable to select a suitable screen mode (27 or 12)");
+ return;
+ }
+
+ if (!((fullscreen_mode == 12) ? cache_fs_fontLR() : cache_fs_fontHR()))
+ {
+ plog("Unable to cache a font for full screen mode");
+ return;
+ }
+
+ /* Read the current screen mode */
+ /* SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &old_screenmode ); */
+ old_screenmode = current_mode();
+
+ Stop_Hourglass;
+
+ /* Change to the chosen screen mode */
+ change_screenmode(fullscreen_mode);
+
+ /* Restrict the pointer */
+ constrain_pointer();
+
+ /* Remove the cursors */
+ SWI(0, 0, SWI_OS_RemoveCursors);
+
+ Start_Hourglass;
+
+ /* Get the base address of screen memory */
+ SWI(2, 0, SWI_OS_ReadVduVariables, vduvars, vduvars);
+ fullscreen_base = (int *)(vduvars[0]);
+
+ /* Fudge the Term interface */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term *t = &(data[i].t);
+ t->xtra_hook = Term_xtra_acnFS;
+ t->wipe_hook = Term_wipe_acnFS;
+ t->curs_hook = Term_curs_acnFS;
+ t->text_hook = Term_text_acnFS;
+ }
+
+ /* Grab the palette */
+ Term_xtra_acn_reactFS(TRUE);
+
+ /* Make sure that the keys work properly */
+ set_keys(TRUE);
+
+ /* refresh the term */
+ /*Term_activate( &(data[0].t) ); */
+ redraw_areaFS(0, 0, 80, 24);
+ if (data[0].cursor.visible)
+ draw_cursor(data[0].cursor.pos.x, data[0].cursor.pos.y);
+
+ /* Display a reminder of how to get back... */
+ /* Hack: disable force_mono */
+ i = force_mono;
+ force_mono = 0;
+ Term_text_acnFS(0, TIME_LINE, strlen(fs_quit_key_text), 8,
+ fs_quit_key_text);
+ force_mono = i;
+}
+
+
+
+
+static void leave_fullscreen_mode(void)
+{
+ int i;
+
+ /* New in 1.18 - protect against 're-entracy' */
+ if (!fullscreen_font)
+ return;
+
+ /* Restore the Term interface */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+#ifndef FULLSCREEN_ONLY
+ term *t = &(data[i].t);
+ t->xtra_hook = Term_xtra_acn;
+ t->wipe_hook = Term_wipe_acn;
+ t->curs_hook = Term_curs_acn;
+ t->text_hook = Term_text_acn;
+#endif
+ mark_ood(&(data[i]), 0, 0, 80, 24);
+ }
+
+ /* Deallocate the font */
+ f_free(fullscreen_font);
+ fullscreen_font = 0;
+ fullscreen_mode = 0;
+
+ Stop_Hourglass;
+
+ /* Restore the screen mode */
+ Wimp_SetMode(old_screenmode);
+
+ /* Restore the pointer */
+ release_pointer();
+
+ Start_Hourglass;
+
+ /* Restore the various soft keys */
+ set_keys(FALSE);
+
+ /* New in 1.20 Remove the IClear hack */
+ if (allow_iclear_hack)
+ remove_iclear_hack();
+
+#ifndef FULLSCREEN_ONLY
+ /* Refresh the windows - this probably isn't necessary anyway */
+ if (!minimise_memory)
+ refresh_windows();
+#endif /* FULLSCREEN_ONLY */
+}
+
+
+
+
+
+static void fs_writechars(int x, int y, int n, const char *chars, char attr)
+{
+ int *scr, *scrb;
+ int *cdat;
+ int j;
+ unsigned int fgm;
+
+ if (force_mono)
+ {
+ if (attr != TERM_DARK)
+ {
+ attr = TERM_WHITE;
+ }
+ }
+ fgm = (unsigned int)zpalette[(unsigned int) attr];
+
+ scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
+ + x * 4 + 320 * fullscreen_topline);
+
+ while (n--)
+ {
+ scr = scrb++;
+ cdat = (int *)(((int)fullscreen_font)
+ + (*chars++) * (fullscreen_height << 2));
+ for (j = 0; j < fullscreen_height; j++)
+ {
+ *scr = *cdat++ & fgm;
+ scr += 80;
+ }
+ }
+}
+
+
+static void fs_writechar(int x, int y, char c, char attr)
+{
+ int *scrb;
+ int *cdat;
+ int j;
+ unsigned int fgm;
+
+ if (force_mono)
+ {
+ if (attr != TERM_DARK)
+ {
+ attr = TERM_WHITE;
+ }
+ }
+ fgm = (unsigned int)zpalette[(unsigned int) attr];
+
+ scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
+ + x * 4 + 320 * fullscreen_topline);
+ cdat = (int *)(((int)fullscreen_font) + (c * (fullscreen_height << 2)));
+ for (j = 0; j < fullscreen_height; j++)
+ {
+ *scrb = *cdat++ & fgm;
+ scrb += 80;
+ }
+}
+
+
+
+static void draw_cursorHR(int x, int y)
+{
+ ColourTrans_SetGCOL(cursor_rgb, 0, 0);
+ GFX_Move(x * 16,
+ 959 - y * (fullscreen_height * 2) - fullscreen_topline * 2);
+ GFX_DrawBy(14, 0);
+ GFX_DrawBy(0, -(fullscreen_height * 2 - 2));
+ GFX_DrawBy(-14, 0);
+ GFX_DrawBy(0, fullscreen_height * 2 - 2);
+}
+
+static void draw_cursorLR(int x, int y)
+{
+ ColourTrans_SetGCOL(cursor_rgb, 0, 0);
+ GFX_Move(x * 16,
+ 1023 - y * (fullscreen_height * 4) - fullscreen_topline * 4);
+ GFX_DrawBy(14, 0);
+ GFX_DrawBy(0, -(fullscreen_height * 4 - 4));
+ GFX_DrawBy(-14, 0);
+ GFX_DrawBy(0, fullscreen_height * 4 - 4);
+}
+
+
+
+
+
+static void draw_cursor(int x, int y)
+{
+ if (fullscreen_mode == 12)
+ draw_cursorLR(x, y);
+ else
+ draw_cursorHR(x, y);
+}
+
+
+
+static void redraw_areaFS(int x, int y, int w, int h)
+{
+ int i, j;
+ for (j = y; j < y + h; j++)
+ for (i = x; i < x + w; i++)
+ fs_writechar(i, j, data[0].t.old->c[j][i], data[0].t.old->a[j][i]);
+}
+
+
+
+static int wimp_code(int c)
+{
+ /* shift/ctrl keypad? */
+ if (c >= '0' && c <= '9')
+ {
+ kbd_modifiers m = Kbd_GetModifiers(FALSE);
+ if (m.shift)
+ {
+ c |= 0x800;
+ }
+ if (m.ctrl)
+ {
+ c |= 0x400;
+ }
+ return c;
+ }
+ if (c == 9)
+ {
+ return 0x18a;
+ } /* Tab */
+ if (c <= 127)
+ {
+ return c;
+ } /* normal ASCII/ctrl */
+ if (c >= 0x80 && c <= 0xff)
+ {
+ return c + 0x100;
+ } /* f0-f9, etc. */
+
+ return -1; /* unknown */
+}
+
+
+
+
+static void do_keypress(int code)
+{
+ static const char hex[] = "0123456789ABCDEF";
+
+ if (code == KEYPRESS_QUIT && !minimise_memory)
+ {
+#ifdef FULLSCREEN_ONLY
+ Sound_SysBeep();
+#else
+ leave_fullscreen_mode();
+#endif
+ return;
+ }
+
+ if (code == 27)
+ {
+ if (Kbd_KeyDown(inkey_CTRL))
+ {
+ ack_alarm();
+ return;
+ }
+ }
+
+ if (code <= 255)
+ {
+ Term_keypress(code);
+ }
+ else
+ {
+ Term_keypress(31);
+ Term_keypress(hex[(code & 0xf00) >> 8]);
+ Term_keypress(hex[(code & 0x0f0) >> 4]);
+ Term_keypress(hex[(code & 0x00f)]);
+ Term_keypress(13);
+ }
+}
+
+
+
+
+static errr Term_xtra_acn_checkFS(void)
+{
+ int bh, bl;
+ int c;
+
+ Stop_Hourglass;
+
+ bored();
+
+ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
+ bl = (bl & 0xff) + (bh << 8);
+
+ if (bl > 0)
+ {
+ SWI(0, 1, SWI_OS_ReadC, &c);
+ bl = wimp_code(c);
+ if (bl >= 0)
+ {
+ do_keypress(bl);
+ }
+ }
+
+ Start_Hourglass;
+
+ return 0;
+}
+
+
+
+
+static errr Term_xtra_acn_eventFS(void)
+{
+ int c;
+ int w = -1;
+
+ Stop_Hourglass;
+
+ for (w = -1; w == -1;)
+ {
+ int bh, bl;
+ do
+ {
+ bored();
+ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
+ bl = (bl & 0xff) + (bh << 8);
+ }
+ while (!bl);
+
+ SWI(0, 1, SWI_OS_ReadC, &c);
+ w = wimp_code(c);
+ if (w >= 0)
+ {
+ do_keypress(w);
+ }
+ }
+
+ Start_Hourglass;
+
+ return 0;
+}
+
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_acn_reactFS(int force)
+{
+ unsigned int i;
+ int p, r, g, b;
+
+ static double old_gamma = -1.0;
+
+ if (gamma != old_gamma)
+ {
+ force = 1;
+ old_gamma = gamma;
+ }
+
+ /* Set the screen colours */
+ for (i = 0; i < 16; i++)
+ {
+ if (COLOUR_CHANGED(i) || force)
+ {
+ r = (int)(255.0 *
+ pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
+ g = (int)(255.0 *
+ pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
+ b = (int)(255.0 *
+ pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
+ GFX_VDU(19);
+ GFX_VDU(i);
+ GFX_VDU(16);
+ GFX_VDU(r);
+ GFX_VDU(g);
+ GFX_VDU(b);
+
+ palette[i] = (b << 24) | (g << 16) | (r << 8);
+ p = i;
+ p |= (p << 4);
+ p |= (p << 8);
+ p |= (p << 16);
+ zpalette[i] = p;
+
+ a_palette[i][1] = angband_color_table[i][1];
+ a_palette[i][2] = angband_color_table[i][2];
+ a_palette[i][3] = angband_color_table[i][3];
+
+ /* Find any higher colour numbers and make them "wrong" */
+ for (p = 16; p < 256; p++)
+ if ((zpalette[p] & 0xf) == i)
+ a_palette[p][1] = angband_color_table[p][1] + 2;
+ }
+ }
+
+
+ /* Go through the palette updating any changed values */
+ for (i = 16; i < 256; i++)
+ {
+ if (COLOUR_CHANGED(i) || force)
+ {
+ r = (int)(255.0 *
+ pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
+ g = (int)(255.0 *
+ pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
+ b = (int)(255.0 *
+ pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
+ p = (b << 24) | (g << 16) | (r << 8);
+ palette[i] = p;
+ SWI(1, 1, SWI_ColourTrans_ReturnColourNumber, palette[i], &p);
+ p |= (p << 4);
+ p |= (p << 8);
+ p |= (p << 16);
+ zpalette[i] = p;
+ a_palette[i][1] = angband_color_table[i][1];
+ a_palette[i][2] = angband_color_table[i][2];
+ a_palette[i][3] = angband_color_table[i][3];
+ }
+ }
+
+ cursor_rgb = palette[CURSOR_COLOUR];
+
+ return 0;
+}
+
+
+static errr Term_curs_acnFS(int x, int y)
+{
+ if (Term == &(data[0].t))
+ {
+ if (data[0].cursor.visible)
+ redraw_areaFS(data[0].cursor.pos.x, data[0].cursor.pos.y, 1, 1);
+ data[0].cursor.pos.x = x;
+ data[0].cursor.pos.y = y;
+ if (data[0].cursor.visible)
+ draw_cursor(x, y);
+ }
+ return 0;
+}
+
+static errr Term_xtra_acn_clearFS(void)
+{
+ char e[80];
+ int j;
+
+ if (Term == &(data[0].t))
+ {
+ for (j = 0; j < 80; j++)
+ e[j] = ' ';
+
+ GFX_Wait();
+
+ for (j = 0; j < 24; j++)
+ fs_writechars(0, j, 80, e, 0);
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+
+static errr Term_xtra_acnFS(int n, int v)
+{
+ term_data *t = (term_data *)Term;
+
+ switch (n)
+ {
+ case TERM_XTRA_CLEAR:
+ if (t == (&data[0]))
+ Term_xtra_acn_clearFS();
+ return 0;
+
+ case TERM_XTRA_EVENT:
+ if (v)
+ return Term_xtra_acn_eventFS();
+ else
+ return Term_xtra_acn_checkFS();
+
+ case TERM_XTRA_BORED:
+ bored();
+ return Term_xtra_acn_checkFS();
+
+ case TERM_XTRA_FLUSH:
+ /* 1.21 - Hack: wait until no keys are pressed */
+ if (hack_flush)
+ for (v = 0; v != 0xff;)
+ SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
+ SWI(3, 0, SWI_OS_Byte, 21, 0, 0); /* Flush Kbd buffer */
+ return 0;
+
+ case TERM_XTRA_FRESH:
+ return 0;
+
+ case TERM_XTRA_FROSH:
+ return 0;
+
+ case TERM_XTRA_SHAPE:
+ if (t == (&data[0]))
+ {
+ t->cursor.visible = v;
+ if (v)
+ draw_cursor(t->cursor.pos.x, t->cursor.pos.y);
+ else
+ redraw_areaFS(t->cursor.pos.x, t->cursor.pos.y, 1, 1);
+ }
+ return 0;
+
+ case TERM_XTRA_NOISE:
+ Sound_SysBeep();
+ return 0;
+
+ case TERM_XTRA_REACT:
+ return Term_xtra_acn_reactFS(FALSE);
+
+ case TERM_XTRA_DELAY:
+ if (v > 0)
+ {
+ int start = Time_Monotonic();
+ v = (v + 5) / 10; /* Round to nearest cs */
+ GFX_Wait();
+ while ((Time_Monotonic() - start) < v)
+ ;
+ }
+ return (0);
+
+ case TERM_XTRA_SOUND: /* Play a sound :) */
+ if (enable_sound)
+ {
+ play_sound(v);
+ }
+ return 0;
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ filing_dirdata directory;
+ filing_direntry *entry;
+
+ scansubdir_max = 0;
+
+ if (Filing_OpenDir(riscosify_name(scansubdir_dir), &directory, sizeof(filing_direntry), readdirtype_DIRENTRY) != NULL)
+ {
+ Error_Report(0, "Couldn't open directory \"%s\"", riscosify_name(scansubdir_dir));
+ return 0;
+ }
+
+ while ((entry = Filing_ReadDir(&directory)) != NULL)
+ {
+ if (entry->objtype == filing_DIRECTORY)
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->name);
+ ++scansubdir_max;
+ }
+ }
+
+ Filing_CloseDir(&directory);
+
+ return 0;
+ }
+
+ /* Return current "time" in milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ Term_xtra_long = Time_Monotonic() * 100;
+
+ return 0;
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ Window_SetTitle(data[0].w, angband_term_name[0]);
+ return 0;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+static errr Term_wipe_acnFS(int x, int y, int n)
+{
+ if (Term == &(data[0].t))
+ while (n--)
+ fs_writechar(x++, y, ' ', 0);
+ return 0;
+}
+
+static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s)
+{
+ if (Term == &(data[0].t))
+ fs_writechars(x, y, n, s, (char)a);
+ return 0;
+}
+
+
+
+static void bored()
+{
+ static int last = -1;
+ char ts[80];
+ time_t ct;
+ struct tm *lt;
+ unsigned int l;
+ int ofm;
+ static int alarm_flash = 1;
+
+ /* Really no need to check the alarm more than once per second. */
+ if (alarm_type && Time_Monotonic() > alarm_lastcheck + 100)
+ {
+ check_alarm();
+ }
+
+ l = Time_Monotonic();
+ if ((l - last) < (alarm_flash ? 25 : 50))
+ {
+ return;
+ }
+ last = l;
+
+ time(&ct);
+ lt = localtime(&ct);
+ l = strftime(ts, 80, "%c %Z", lt);
+
+ /* Hack: disable force_mono around printing the time */
+ ofm = force_mono;
+ force_mono = 0;
+
+ /* Hack: Is the alarm supposed to be going off? */
+ if (alarm_disp || alarm_flash)
+ {
+ char blk[60];
+ int c = 8;
+ if (!alarm_disp)
+ {
+ alarm_flash = 11;
+ }
+ switch (alarm_flash / 2)
+ {
+ case 4: sprintf(blk, "%-57s", alarm_cancel_text);
+ break;
+ case 5: sprintf(blk, "%-57s", fs_quit_key_text);
+ break;
+ default:
+ c = alarm_flash & 1 ? TERM_RED : TERM_WHITE;
+ sprintf(blk, "%02d:%02d %-51s", alarm_h, alarm_m,
+ alarm_message);
+ }
+ fs_writechars(0, TIME_LINE, 57, blk, c);
+ if (++alarm_flash > 11)
+ {
+ alarm_flash = 0;
+ }
+ }
+
+ /* Display time */
+ fs_writechar(79 - l, TIME_LINE, ' ', 0);
+ fs_writechars(80 - l, TIME_LINE, l, ts, 8);
+
+ force_mono = ofm;
+}
+
+
+
+
+
+
+#ifdef USE_DA
+/*--------------------------------------------------------------------------*/
+/* (Simple) Heap management (using OS_Heap) */
+/*--------------------------------------------------------------------------*/
+
+typedef void *heap;
+
+static os_error *Heap_Initialise(heap h, size_t size)
+{
+ return SWI(4, 0, SWI_OS_Heap, 0, h, 0, size);
+}
+
+static void *Heap_Claim(heap h, size_t size)
+{
+ void *fred;
+ os_error *e;
+ e = SWI(4, 3, SWI_OS_Heap, 2, h, 0, size, NULL, NULL, &fred);
+ return e ? NULL : fred;
+}
+
+static os_error *Heap_Release(heap h, void *block)
+{
+ return SWI(3, 0, SWI_OS_Heap, 3, h, block);
+}
+
+static int Heap_ChangeHeapSize(heap h, int resize_by)
+{
+ int by;
+ SWI(4, 4, SWI_OS_Heap, 5, h, 0, resize_by, 0, 0, 0, &by);
+ return by;
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Stuff below here is for using Dynamic areas (under RO3.5+) */
+/*--------------------------------------------------------------------------*/
+
+static int game_area = -1; /* The DA the game is using */
+static int font_area = -1; /* The DA the fonts are using */
+
+static void *game_area_base; /* base address of game area */
+static void *font_area_base; /* base address of font area */
+
+static int font_area_size; /* size of the fonts' DA */
+static int font_heap_size; /* size of the fonts' heap */
+static int game_area_size; /* size of the game's DA */
+static int game_heap_size; /* size of the game's heap */
+
+#define MAX_F_DA_SIZE (2<<20) /* Max size of font area (2Mb) */
+#define MAX_G_DA_SIZE (4<<20) /* Max size of game area (4Mb) */
+#define SHRINK_GRAN (4<<10) /* Try to recalaim wastage > this (4Kb) */
+
+
+/*
+ | Free dynamic areas when we exit
+ */
+static void cleanup_memory(void)
+{
+ if (game_area != -1)
+ {
+ SWI(2, 0, SWI_OS_DynamicArea, 1, game_area);
+ game_area = -1;
+ }
+
+ if (font_area != -1)
+ {
+ SWI(2, 0, SWI_OS_DynamicArea, 1, font_area);
+ font_area = -1;
+ }
+
+}
+
+
+
+/*
+ | Set up the memory allocation stuff.
+ | We check to see if DAs are possible and if so initialise two:
+ | one for the game's use (via the rnalloc() hooks) and one for
+ | our own use (for fonts, etc).
+ |
+ | Each area is created 16Kb in size, with a max size of 2/4Mb.
+ |
+ | If 'daf' is TRUE, an area is created for the fonts.
+ | If 'dag' is TRUE, an area is created for the game.
+ */
+static void init_memory(int daf, int dag)
+{
+ os_error *e = NULL;
+
+ if (!daf)
+ {
+ /* Paranoia */
+ font_area = -1;
+ font_area_base = 0;
+ }
+ else
+ {
+ e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */
+ -1, /* Let OS allocate no. */
+ 16 << 10, /* Initial size */
+ -1, /* Let OS allocate address */
+ 1 << 7, /* Cacheable, bufferable, RW */
+ MAX_F_DA_SIZE, /* Max size */
+ 0, /* handler */
+ 0, /* handler workspace */
+ VARIANT " font data", /* Name */
+ /* */
+ NULL, /* r0 */
+ &font_area, /* area number allocated */
+ NULL, /* r2 */
+ &font_area_base /* base address of area */
+ );
+
+ if (e)
+ {
+ game_area = font_area = -1;
+ game_area_base = font_area_base = 0; /* paranoia */
+ return;
+ }
+ else
+ {
+ e = SWI(2, 3, SWI_OS_DynamicArea, 2, font_area,
+ NULL, NULL, &font_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+
+ e = Heap_Initialise((heap) font_area_base, font_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+ font_heap_size = font_area_size;
+ }
+ }
+
+ /* Make sure DA(s) are removed when we quit */
+ atexit(cleanup_memory);
+
+ if (!dag)
+ {
+ /* Paranoia */
+ game_area = -1;
+ game_area_base = 0;
+ }
+ else
+ {
+ e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */
+ -1, /* Let OS allocate no. */
+ 16 << 10, /* Initial size */
+ -1, /* Let OS allocate address */
+ 1 << 7, /* Cacheable, bufferable, RW */
+ MAX_G_DA_SIZE, /* Max size */
+ 0, /* handler */
+ 0, /* handler workspace */
+ VARIANT " game data", /* Name */
+ /* */
+ NULL, /* r0 */
+ &game_area, /* area number allocated */
+ NULL, /* r2 */
+ &game_area_base /* base address of area */
+ );
+
+ if (e)
+ {
+ game_area = -1;
+ game_area_base = 0; /* paranoia */
+ }
+ else
+ {
+ e = SWI(2, 3, SWI_OS_DynamicArea, 2, game_area,
+ NULL, NULL, &game_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+
+ e = Heap_Initialise((heap) game_area_base, game_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+ game_heap_size = game_area_size;
+ }
+ }
+}
+
+static int grow_dynamicarea(int area, int by)
+{
+ os_error *e;
+ e = SWI(2, 2, SWI_OS_ChangeDynamicArea, area, by, /**/ NULL, &by);
+ /* Can't check errors since a 'failed' shrink returns one... */
+ return by;
+}
+
+
+/*
+ | Try to shrink the font-cache heap and area as much as possible.
+ */
+static void f_shrink_heap(void)
+{
+ int s;
+ /* Shrink the heap as far as possible */
+ font_heap_size -=
+ Heap_ChangeHeapSize((heap) font_area_base, -MAX_F_DA_SIZE);
+ /* Shrink the dynamic area if necessary */
+ s = font_area_size - font_heap_size;
+ if (s >= SHRINK_GRAN)
+ font_area_size -= grow_dynamicarea(font_area, -s);
+}
+
+/*
+ | Allocate a block of memory in the font heap
+ */
+static void *f_malloc(size_t size)
+{
+ void *c;
+ int s;
+ if (font_area == -1)
+ {
+ return malloc(size);
+ }
+ c = Heap_Claim((heap) font_area_base, size);
+
+ if (!c)
+ {
+ /* The Claim failed. Try to grow the area by the size of the block */
+ s = grow_dynamicarea(font_area, size + 64); /* 64 is overkill */
+ if (!s)
+ {
+ return NULL;
+ }
+ font_area_size += s;
+ s = font_area_size - font_heap_size;
+ font_heap_size += Heap_ChangeHeapSize((heap) font_area_base, s);
+ c = Heap_Claim((heap) font_area_base, size);
+ if (c)
+ {
+ f_shrink_heap();
+ }
+ }
+ return c;
+}
+
+
+/*
+ | Free a block of memory in the font heap
+ */
+static void f_free(void *blk)
+{
+ os_error *e;
+ if (font_area == -1)
+ {
+ free(blk);
+ return;
+ }
+ e = Heap_Release((heap) font_area_base, blk);
+ if (e)
+ Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
+ f_shrink_heap();
+}
+
+
+
+
+
+/*
+ | Allocate a block of memory in the game heap
+ */
+static vptr g_malloc(huge size)
+{
+ void *c;
+ int s;
+
+ if (game_area == -1)
+ {
+ return malloc((size_t) size);
+ }
+ c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
+ if (!c)
+ {
+ /* The Claim failed. Try to grow the area by the size of the block */
+ s = grow_dynamicarea(game_area, (size_t) size + 64); /* 64 is overkill */
+ if (!s)
+ {
+ return NULL;
+ }
+ game_area_size += s;
+ s = game_area_size - game_heap_size;
+ game_heap_size += Heap_ChangeHeapSize((heap) game_area_base, s);
+ c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
+ }
+
+ if (c)
+ {
+ strcpy((char *)c, "MUSH");
+ c = (void *)(((int)c) + 4);
+ }
+
+ if (log_g_malloc)
+ fprintf(stderr, "ralloc(%ld) == %p\n", (long)size, c);
+
+ return c;
+}
+
+
+/*
+ | Free a block of memory in the game heap
+ |
+ | The 'len' is to be compatible with z-virt.c (we don't need/use it)
+ | Returns NULL.
+ */
+static vptr g_free(vptr blk, huge size)
+{
+ os_error *e;
+ int s;
+
+ if (game_area == -1)
+ {
+ free(blk);
+ return NULL;
+ }
+
+ if (log_g_malloc)
+ fprintf(stderr, "rnfree(%p)\n", blk);
+
+ if (strncmp(((char *)blk) - 4, "MUSH", 4))
+ core("game heap corrupt / bad attempt to free memory");
+
+ blk = (void *)(((int)blk) - 4);
+
+ e = Heap_Release((heap) game_area_base, blk);
+ if (e)
+ Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
+
+ /* Shrink the heap as far as possible */
+ game_heap_size -=
+ Heap_ChangeHeapSize((heap) game_area_base, -MAX_G_DA_SIZE);
+
+ /* Shrink the dynamic area if necessary */
+ s = game_area_size - game_heap_size;
+ if (s >= SHRINK_GRAN)
+ game_area_size -= grow_dynamicarea(game_area, -s);
+
+ return NULL;
+}
+
+
+#endif /* USE_DA */
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | New to 1.04: Sound support :)
+ |
+ | We use the PlayIt module (for convenience).
+ |
+ | The Lib/xtra/sound/sound.cfg file is used to map sample names onto
+ | event names.
+ |
+ | Since textual names are used in the .cfg file, we need to have a lookup
+ | table to translate them into numbers. At present we use the
+ | angband_sound_name array defined in variable.c
+ |
+ | Since there can be multiple sounds for each event we need to use a
+ | list to store them.
+ */
+
+/* NB: This will be clipped to 10 under RISC OS 2 */
+#define MAX_SAMPNAME_LEN 64
+
+
+
+
+/*
+ | The list format:
+ */
+typedef struct samp_node
+{
+ char sample_name[MAX_SAMPNAME_LEN + 1]; /* Sample name */
+ struct samp_node *next; /* -> next node */
+}
+SampNode;
+
+typedef struct samp_info
+{
+ int samples; /* # samples for this event */
+ SampNode *samplist; /* list of sample names */
+}
+SampInfo;
+
+
+/*
+ | Just need an array of SampInfos
+ */
+static SampInfo sample[SOUND_MAX];
+
+/*
+ | This flag will only be set non-zero if the SampInfo array is
+ | valid.
+ */
+static int sound_initd = 0;
+
+
+static void read_sound_config(void)
+{
+ int i;
+ char buffer[2048];
+ FILE *f;
+ int max_sampname_len = truncate_names()? 10 : MAX_SAMPNAME_LEN;
+ FILE *dbo = NULL;
+
+ if (show_sound_alloc)
+ {
+ sprintf(buffer, "%s%s", resource_path, "sndmap/out");
+ dbo = fopen(buffer, "w");
+ if (!dbo)
+ {
+ core("can't create sndmap/out debugging file");
+ }
+ }
+
+ if (!sound_initd)
+ {
+ /* Initialise the sample array */
+ for (i = 0; i < SOUND_MAX; i++)
+ {
+ sample[i].samples = 0;
+ sample[i].samplist = NULL;
+ }
+ sound_initd = 1;
+ }
+ else
+ {
+ /* Deallocate the sample lists */
+ for (i = 0; i < SOUND_MAX; i++)
+ {
+ SampNode *si = sample[i].samplist;
+ sample[i].samples = 0;
+ sample[i].samplist = NULL;
+ while (si)
+ {
+ SampNode *ns = si->next;
+ free(si);
+ si = ns;
+ }
+ }
+ }
+
+
+ /* Open the config file */
+ sprintf(buffer, "%sSound:%s", RISCOS_VARIANT, "sound/cfg");
+ f = fopen(buffer, "r");
+
+ /* No cfg file => no sounds */
+ if (!f)
+ {
+ if (show_sound_alloc)
+ {
+ fprintf(dbo, "** Can't open cfg file '%s'\n", buffer);
+ fclose(dbo);
+ }
+ return;
+ }
+
+ /* Parse the file */
+ while (fgets(buffer, sizeof(buffer), f))
+ {
+ char *sample_name;
+ int event_number;
+
+ /* Skip comments and lines that begin with whitespace */
+ if (*buffer == '#' || isspace((unsigned char)*buffer))
+ {
+ continue;
+ }
+
+ /* Hack: ignore any line beginning '[' (section marker) */
+ if (*buffer == '[')
+ {
+ continue;
+ }
+
+ /* Place a NULL after the event name and find the first sample name */
+ sample_name = buffer;
+ while (*sample_name && !isspace((unsigned char)*sample_name))
+ sample_name++;
+
+ /* Bad line? */
+ if (*sample_name == 0)
+ {
+ continue;
+ } /* just ignore it */
+
+ /* Terminate the sample name */
+ *sample_name++ = 0;
+
+ /* Look up the event name to get the event number */
+ for (event_number = SOUND_MAX - 1; event_number >= 0; event_number--)
+ if (!strcmp(buffer, angband_sound_name[event_number]))
+ break;
+
+ /* No match -> just ignore the line */
+ if (event_number < 0)
+ {
+ if (show_sound_alloc)
+ fprintf(dbo, "* Ignoring unknown event '%s'\n", buffer);
+ continue;
+ }
+
+ /* Find the = */
+ while (*sample_name && *sample_name != '=')
+ sample_name++;
+
+ /* Bad line? */
+ if (*sample_name == 0)
+ {
+ continue;
+ } /* just ignore it */
+
+ /* Skip the '=' */
+ sample_name++;
+
+
+ /*
+ | Now we find all the sample names and add them to the
+ | appropriate list in the sample mapping array
+ */
+
+ while (*sample_name)
+ {
+ char *s;
+ SampNode *sn;
+
+ /* Find the start of the next word */
+ while (isspace((unsigned char)*sample_name) && *sample_name)
+ sample_name++;
+
+ /* End of line? */
+ if (!*sample_name)
+ {
+ break;
+ }
+
+ /* Find the end of the sample name */
+ s = sample_name; /* start of the name */
+ while (!isspace((unsigned char)*sample_name) && *sample_name)
+ sample_name++;
+
+ /* Hack: shorten sample names that are too long */
+ if ((sample_name - s) > max_sampname_len)
+ s[max_sampname_len] = ' ';
+
+ /* Allocate a node in the sample list for the event */
+ if ((sn = malloc(sizeof(SampNode))) == NULL)
+ core("Out of memory (scanning sound.cfg)");
+
+ /* Link the node to the list */
+ sn->next = sample[event_number].samplist;
+ sample[event_number].samplist = sn;
+
+ /* Imcrement the sample count for that event */
+ sample[event_number].samples++;
+
+ /*
+ | Copy the sample name into the node, converting it into
+ | RISC OS style as we go.
+ */
+ for (i = 0; !isspace((unsigned char)s[i]) && s[i]; i++)
+ {
+ if (s[i] == '.')
+ sn->sample_name[i] = '/';
+ else if (s[i] == '/')
+ sn->sample_name[i] = '.';
+ else
+ sn->sample_name[i] = s[i];
+ }
+ /*
+ | The sample name '*' is special and means "no new sound"
+ | so don't store a filename for these mappings.
+ */
+ if (i == 1 && sn->sample_name[0] == '*')
+ {
+ i = 0;
+ }
+ sn->sample_name[i] = 0;
+ }
+ }
+
+ /* Close the file */
+ fclose(f);
+
+ if (show_sound_alloc)
+ {
+ int i;
+ SampNode *l;
+
+ for (i = 0; i < SOUND_MAX; i++)
+ {
+ fprintf(dbo, "\n\nEvent '%s'", angband_sound_name[i]);
+ fprintf(dbo, " (%d sounds)\n", sample[i].samples);
+ for (l = sample[i].samplist; l; l = l->next)
+ fprintf(dbo, "\t%s\n", l->sample_name);
+ }
+ fclose(dbo);
+ }
+
+}
+
+
+
+/*
+ | Try to make sure that PlayIt is loaded.
+ | This requires AngSound rel. 4
+ */
+static void check_playit(void)
+{
+ if (SWI(2, 0, SWI_OS_Module, 18, "PlayIt"))
+ {
+ int t;
+ SWI(2, 1, SWI_OS_File, 17, "Angsound:LoadPlayIt", &t);
+ if (t == 1)
+ SWI(1, 0, SWI_OS_CLI,
+ "RMEnsure PlayIt 0.00 Run AngSound:LoadPlayIt");
+ }
+}
+
+
+
+
+static void initialise_sound(void)
+{
+ /* Load the configuration file */
+ Hourglass_On();
+ read_sound_config();
+ check_playit();
+ Hourglass_Off();
+}
+
+
+
+static void play_sample(char *leafname)
+{
+ char buffer[260];
+
+ strcpy(buffer, "%playit_stop");
+
+ if (!SWI(1, 0, SWI_OS_CLI, buffer))
+ {
+ SWI(1, 0, SWI_PlayIt_Volume, sound_volume);
+ sprintf(buffer, "%%playit_play %sSound:%s", RISCOS_VARIANT, leafname);
+ SWI(1, 0, SWI_OS_CLI, buffer);
+ }
+
+ return;
+}
+
+
+
+static void play_sound(int event)
+{
+ /* Paranoia */
+ if (!sound_initd)
+ {
+ return;
+ }
+
+ /* Paranoia */
+ if (event < 0 || event >= SOUND_MAX)
+ return;
+
+ /* msg_format("Sound '%s'",angband_sound_name[event]); */
+
+ /* Choose a sample */
+ if (sample[event].samples)
+ {
+ int s = rand() % sample[event].samples;
+ SampNode *sn = sample[event].samplist;
+ while (s--)
+ {
+ sn = sn->next;
+ if (!sn)
+ {
+ plog("Adny botched the sound config - please send him a copy of your sound/cfg file.");
+ }
+ }
+ if (*(sn->sample_name))
+ play_sample(sn->sample_name);
+ }
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | This stuff is for the Term_user hook
+ */
+
+
+
+static void display_line(int x, int y, int c, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[260];
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ Term_putstr(x, y, -1, c, buffer);
+ va_end(ap);
+}
+
+
+
+/*
+ | Let the user change the alarm message
+ */
+static void do_alarm_message_input(int y)
+{
+ int k;
+ int inspos = strlen(alarm_message);
+ char old_message[52];
+
+ strcpy(old_message, alarm_message);
+
+ do
+ {
+ display_line(26, y, TERM_YELLOW, "%-51s", alarm_message);
+ Term_gotoxy(26 + inspos, y);
+ k = inkey();
+ switch (k)
+ {
+ case 21: /* ^U */
+ *alarm_message = 0;
+ inspos = 0;
+ break;
+ case 128: case 8: /* delete */
+ if (inspos > 0)
+ {
+ alarm_message[--inspos] = 0;
+ }
+ break;
+ case 27: /* escape */
+ strcpy(alarm_message, old_message);
+ k = 13;
+ break;
+ default:
+ if (k > 31 && k < 127 && inspos < 50)
+ {
+ alarm_message[inspos++] = k;
+ alarm_message[inspos] = 0;
+ }
+ }
+ }
+ while (k != 13);
+
+ display_line(26, y, TERM_WHITE, "%-51s", alarm_message);
+}
+
+
+#define tum_col(X) ((X) ? TERM_L_BLUE : TERM_WHITE )
+#define tum_onoff(X) ((X) ? "On " : "Off")
+
+static errr Term_user_acn(int n)
+{
+ int cursor_state;
+ int optn = 0;
+ int k, adj;
+ int redraw_mung = 0;
+ int max_opt = 11;
+ int alarm_modified = 0; /* Will be true if the alarm choices need to be (re)saved */
+
+ /*
+ | Hack: let the desktop front end know that
+ | the user menu is active...
+ */
+ user_menu_active = TRUE;
+
+
+ /*
+ | This is thanks to Norcroft CC... it seems to want
+ | to set up FP regs nice and early and then relies
+ | on them remaining constant over the function call.
+ | The trouble is that we implicitly call Wimp_Poll
+ | whilst waiting for a key press...
+ */
+ event_mask.data.keepfpregisters = 1;
+
+ /*
+ | Hack: alarm type 1 /looks/ the same as type 3 but doesn't get
+ | cancelled as a type 3 would. This allows alarms to go off and
+ | be cancelled without affecting the alarm type whilst it's being
+ | set up here.
+ */
+ if (alarm_type == 3)
+ {
+ alarm_type = 1;
+ }
+
+ /*
+ | Store the screen
+ */
+ Term_activate(&(data[0].t));
+ Term_save();
+ Term_get_cursor(&cursor_state);
+ Term_set_cursor(TRUE);
+
+ do
+ {
+ redraw_mung = 0;
+ Term_clear();
+ display_line(2, 1, TERM_YELLOW, "%s %s", VARIANT, VERSION);
+ display_line(2, 2, TERM_SLATE, "Front-end %s", PORTVERSION);
+ display_line(2, 4, TERM_WHITE,
+ "Use cursor up/down to select an option then cursor left/right to alter it.");
+ display_line(2, 5, TERM_WHITE,
+ "Hit 'S' to save these settings (alarm settings are saved automatically).");
+ display_line(2, 6, TERM_WHITE, "Hit ESC to return to the game.");
+
+ for (k = 0; k < 16; k++)
+ display_line(31 + k * 3, 8, k, "##", k);
+
+ do
+ {
+ display_line(2, 8, tum_col(optn == 0),
+ " Gamma correction : %.2lf", gamma);
+ display_line(2, 9, tum_col(optn == 1), " Force monochrome : %s",
+ tum_onoff(force_mono));
+ display_line(2, 10, tum_col(optn == 2), " Sound effects : %s",
+ tum_onoff(enable_sound));
+ display_line(2, 11, tum_col(optn == 3), " Sound effect volume : ");
+ display_line(26, 11,
+ sound_volume > 127 ? TERM_RED : tum_col(optn == 3),
+ "%-3d", sound_volume);
+ display_line(30, 11, tum_col(optn == 3), "(127 = full volume)");
+ display_line(2, 12, tum_col(optn == 4), " Start fullscreen : %s",
+ tum_onoff(start_fullscreen));
+ display_line(30, 12, tum_col(optn == 4),
+ "(also selects fullscreen/desktop now)");
+ display_line(2, 13, tum_col(optn == 5), " Use hourglass : %s",
+ tum_onoff(use_glass));
+ display_line(2, 14, tum_col(optn == 6),
+ "'Hard' input flushing : %s", tum_onoff(hack_flush));
+
+ display_line(7, 16, tum_col(optn == 7), " Alarm type : %-20s",
+ alarm_types[alarm_type]);
+ display_line(7, 17, TERM_WHITE, " Time : ");
+ display_line(26, 17, tum_col(optn == 8), "%02d", alarm_h);
+ display_line(28, 17, TERM_WHITE, ":");
+ display_line(29, 17, tum_col(optn == 9), "%02d", alarm_m);
+ display_line(7, 18, tum_col(optn == 10), " Message : %-51s",
+ alarm_message);
+ display_line(7, 19, tum_col(optn == 11), " Beep : %s",
+ tum_onoff(alarm_beep));
+
+#ifdef FE_DEBUG_INFO
+ display_line(2, 23, tum_col(optn == 23), "Show debug info");
+ max_opt = 12;
+#endif
+
+ switch (optn)
+ {
+ case 12: Term_gotoxy(2, 23);
+ break;
+ case 11: Term_gotoxy(26, 19);
+ break;
+ case 10: Term_gotoxy(26, 18);
+ break;
+ case 9: Term_gotoxy(29, 17);
+ break;
+ case 8: Term_gotoxy(26, 17);
+ break;
+ case 7: Term_gotoxy(26, 16);
+ break;
+ default: Term_gotoxy(26, optn + 8);
+ }
+
+ k = inkey();
+ adj = (k == '4' || k == 'h') ? -1 : (k == '6' || k == 'l') ? 1 : 0;
+
+ switch (k)
+ {
+ case 18: /* Hack: force the screen to update */
+ redraw_mung = 1;
+ k = 27;
+ break;
+ case 's': case 'S':
+ save_choices();
+ display_line(2, 23, TERM_YELLOW, "Options saved. ");
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, 750);
+ Term_erase(2, 23, 60);
+ break;
+ case '8': case 'k':
+ if (--optn < 0)
+ {
+ optn = max_opt;
+ }
+ break;
+ case '2': case 'j':
+ if (++optn > max_opt)
+ {
+ optn = 0;
+ }
+ break;
+ case 13: case 32: case 't': /* Allow return, space and t to toggle some options */
+ case '4': case 'h':
+ case '6': case 'l':
+ {
+ switch (optn)
+ {
+ case 0: /* Gamma correction */
+ gamma += adj * 0.05;
+ if (gamma > 9.00)
+ {
+ gamma = 9.00;
+ }
+ if (gamma < 0.05)
+ {
+ gamma = 0.05;
+ }
+ Term_xtra(TERM_XTRA_REACT, 0);
+#ifndef FULLSCREEN_ONLY
+ set_gamma_window_state();
+#endif /* FULLSCREEN_ONLY */
+ /* flush(); */
+ Term_fresh();
+ break;
+ case 1: /* Force monochrome */
+ force_mono = !force_mono;
+ if (fullscreen_font)
+ redraw_areaFS(0, 0, 80, 24);
+ else
+ Term_xtra(TERM_XTRA_REACT, 0);
+ /* flush(); */
+ Term_fresh();
+ break;
+ case 2: /* Sound enable / disable */
+ enable_sound = !enable_sound;
+#ifndef FULLSCREEN_ONLY
+ set_sound_window_state();
+#endif /* FULLSCREEN_ONLY */
+ if (enable_sound)
+ {
+ initialise_sound();
+ }
+ break;
+ case 3: /* Sound volume */
+ sound_volume += adj;
+ if (sound_volume < SOUND_VOL_MIN)
+ sound_volume = SOUND_VOL_MIN;
+ if (sound_volume > SOUND_VOL_MAX)
+ sound_volume = SOUND_VOL_MAX;
+#ifndef FULLSCREEN_ONLY
+ set_sound_window_state();
+#endif /* FULLSCREEN_ONLY */
+ break;
+ case 4: /* Start fullscreen */
+ start_fullscreen = !start_fullscreen;
+ if (start_fullscreen)
+ enter_fullscreen_mode();
+ else if (!minimise_memory)
+ leave_fullscreen_mode();
+ break;
+ case 5: /* Start fullscreen */
+ use_glass = !use_glass;
+ if (!use_glass)
+ {
+ if (glass_on)
+ {
+ Hourglass_Off();
+ }
+ glass_on = 0;
+ }
+ break;
+ case 6: /* hack flush */
+ hack_flush = !hack_flush;
+ break;
+ case 7: /* Alarm on/off */
+ alarm_type += adj;
+ if (adj)
+ {
+ alarm_modified = 1;
+ }
+ if (alarm_type > 2)
+ {
+ alarm_type = 0;
+ }
+ if (alarm_type < 0)
+ {
+ alarm_type = 2;
+ }
+ if (!alarm_type && alarm_disp)
+ {
+ ack_alarm();
+ } /* XXXXX Cancel an already active alarm? */
+ break;
+ case 8: /* Alarm hours */
+ alarm_h += adj;
+ if (adj)
+ {
+ alarm_modified = 1;
+ }
+ if (alarm_h < 0)
+ {
+ alarm_h += 24;
+ }
+ if (alarm_h > 23)
+ {
+ alarm_h -= 24;
+ }
+ if (alarm_disp)
+ {
+ ack_alarm();
+ }
+ break;
+ case 9: /* Alarm minutes */
+ alarm_m += adj;
+ if (adj)
+ {
+ alarm_modified = 1;
+ }
+ if (alarm_m < 0)
+ {
+ alarm_m += 60;
+ }
+ if (alarm_m > 59)
+ {
+ alarm_m -= 60;
+ }
+ if (alarm_disp)
+ {
+ ack_alarm();
+ }
+ break;
+ case 10:
+ alarm_modified = 1;
+ do_alarm_message_input(18);
+ break;
+ case 11:
+ alarm_modified = 1;
+ alarm_beep = !alarm_beep;
+ break;
+ case 12:
+ show_debug_info();
+ redraw_mung = 1;
+ k = 27;
+ break;
+ }
+ }
+ }
+ }
+ while (k != 27);
+ }
+ while (redraw_mung);
+
+ /* Rehack the alarm type: */
+ if (alarm_type == 1)
+ {
+ alarm_type = 3;
+ }
+
+ if (alarm_modified)
+ {
+ write_alarm_choices();
+ }
+
+ Term_set_cursor(cursor_state);
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Don't need to preserve FP regs any more */
+ event_mask.data.keepfpregisters = 0;
+
+ /*
+ | Hack: tell the desktop front end that we're done.
+ */
+ user_menu_active = FALSE;
+
+ return 0;
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef USE_FILECACHE
+
+/*
+ | 'Random' File-cacheing for *band.
+ |
+ | Rewritten since as of Zang 225 the mechanism for handling
+ | these files has changed dramatically and the old system
+ | is no longer viable.
+ |
+ | These new functions basically provide an alternative to the
+ | normal my_fopen() (or fopen()) and my_fgets() functions.
+ |
+ | To use the file caching it is therefore necessary to alter
+ | files.c to call cached_fopen(), cached_fclose() and cached_fgets()
+ | rather than the normal functions.
+ |
+ | Note that these funtions will only work for files that are intended
+ | to be read as a series of \n terminated lines of ASCII text using my_fgets().
+ |
+ */
+
+/*
+ | Hack: use the game's dynamic area if possible:
+ */
+#define fc_malloc(X) (g_malloc(X))
+#define fc_free(X) (g_free(X,0))
+
+#ifndef ABBR_FILECACHE
+/*
+ | Make these to do nothing. They'll never
+ | be called anyway. Having them present makes
+ | for neater code later on (ie. we use a variable
+ | rather than the pre-processor to decide whether
+ | to do compression).
+ */
+static int compress_string(char *os, char *s)
+{
+ core("main-acn internal logic error 001");
+ return 0;
+}
+static int decompress_string(char *d, char *s, int max_len)
+{
+ core("main-acn internal logic error 002");
+ return 0;
+}
+static int compressed_length(char *s)
+{
+ core("main-acn internal logic error 003");
+ return 0;
+}
+
+#else
+
+/*
+ | When caching files we try to use some abbreviations.
+ | We use both whole words and pairs of letters.
+ | NB: For this to work, the file must contain only
+ | 7 bit characters.
+ */
+static char *abbrv_w[] =
+{
+ /* These words all begin with a space */
+ " of ", " the ", " you ", " to ", " a ", " says", " is ", " that ", " and ",
+ " your ", " are ", " it ", " be ", " for ", " me", " will ", " in ",
+ " not ", " this ", " have ", " can ", " on ", " my ", " with ", " say ",
+ " all", " by ", " get ", " but ", " just ", " die", " as ", " time ",
+ " if ",
+ " like ",
+ /* These words do not */
+ "I ", "The ", "You ", "They ", "It ", "don", 0
+};
+
+/* Number of words */
+#define FC_ABBRV_NUMWORDS 41
+
+/* Number of them that don't start with a space */
+#define FC_ABBRV_NONSPC 6
+
+/*
+ | NB: No letter pair may start with \0.
+ */
+static char abbrv_lp[] =
+ "e ttht s heiner aoure'\0, anonf sd y r ongofator.\0"
+ "n arllstha wes m ieaisen bl yndtoo yometele d f hve"
+ "ayuralitneelN: chig ilroassaseliti lraa otedbede 'ri" "..u nntno!'ee\0\0";
+
+
+/*
+ | Compress the given string using the abbreviation tables above.
+ | Returns compressed length *including* terminator (it may
+ | be part of an abbreviation, you see...)
+ | Note that we can compress the string in-place, ie. 'os' may be
+ | the same as 's'.
+ */
+static int compress_string(char *os, char *s)
+{
+ char *o, *f, *d;
+ int i;
+
+ o = os;
+
+ while (*s)
+ {
+ int fw, lw;
+ if (*s == ' ')
+ {
+ fw = 0;
+ lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ }
+ else
+ {
+ fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ lw = FC_ABBRV_NUMWORDS;
+ }
+ for (i = fw; i < lw; i++)
+ {
+ d = abbrv_w[i]; /* Word to check against */
+ for (f = s; *f && *f == *d; f++, d++)
+ ;
+ if (*d == 0) /* Match? */
+ {
+ s = *f ? f : f - 1; /* Update string pointer */
+ *o++ = 128 + i; /* store code */
+ break; /* Quit looking for words */
+ }
+ }
+
+ /* Do we need to check the letter pairs? */
+ if (i == lw)
+ {
+ for (i = 0; abbrv_lp[i]; i += 2)
+ {
+ if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
+ {
+ *o++ = 128 + FC_ABBRV_NUMWORDS + i / 2;
+ /* NB: If the next character is the terminator then we're done. */
+ if (!s[1])
+ {
+ return (o - os);
+ }
+ s += 2; /* Quit looking for letters */
+ break;
+ }
+ }
+ /* NB: This next check is only safe because no letter pair starts with a NULL */
+ if (!abbrv_lp[i])
+ *o++ = *s++;
+ }
+ }
+
+ /* Don't forget that terminator! */
+ *o++ = 0;
+
+ return o - os;
+}
+
+/*
+ | As compress_string (above), but stores nothing and
+ | only returns the length of the compressed string.
+ */
+static int compressed_length(char *s)
+{
+ char *f, *d;
+ int i, l;
+
+ l = 0;
+ while (*s)
+ {
+ int fw, lw;
+ if (*s == ' ')
+ {
+ fw = 0;
+ lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ }
+ else
+ {
+ fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ lw = FC_ABBRV_NUMWORDS;
+ }
+ for (i = fw; i < lw; i++)
+ {
+ d = abbrv_w[i];
+ for (f = s; *f && *f == *d; f++, d++)
+ ;
+ if (*d == 0) /* Match? */
+ {
+ s = *f ? f : f - 1; /* Update string pointer */
+ l++; /* increment output length */
+ break; /* Quit looking for words */
+ }
+ }
+
+ /* Do we need to check the letter pairs? */
+ if (i == lw)
+ {
+ for (i = 0; abbrv_lp[i]; i += 2)
+ {
+ if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
+ {
+ l++; /* increment output length */
+ /* NB: If the next character is the terminator then we're done. */
+ if (!s[1])
+ {
+ return l;
+ }
+ s += 2; /* Quit looking for letters */
+ break;
+ }
+ }
+ /* NB: This next check is only safe because no letter pair starts with a NULL */
+ if (!abbrv_lp[i])
+ {
+ l++;
+ s++;
+ }
+ }
+ }
+ /* Don't forget that terminator! */
+ return l + 1;
+}
+
+
+
+/*
+ | Decompress the given string 's' into the buffer at 'd'.
+ | At most, max_len characters (incl. \0 terminator) will be
+ | written into d.
+ | Returns the length of 's'.
+ */
+static int decompress_string(char *d, char *s, int max_len)
+{
+ char *os = s;
+
+ while (max_len > 1)
+ {
+ int nc = *s++; /* Get next character */
+
+ if (nc < 128) /* Is it a plain character? */
+ {
+ if (0 == (*d++ = nc))
+ {
+ break;
+ }
+ max_len--;
+ }
+ else /* Abbreviation to expand. */
+ {
+ if (nc >= FC_ABBRV_NUMWORDS + 128) /* Letter pair? */
+ {
+ *d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2];
+ if (0 ==
+ (*d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2 + 1]))
+ break;
+ max_len -= 2;
+ }
+ else /* It's a word */
+ {
+ char *ws = abbrv_w[nc - 128];
+ while (*ws && max_len > 1)
+ {
+ *d++ = *ws++;
+ max_len--;
+ }
+ }
+ }
+ }
+
+ /* Skip over the rest of the abbreviated string if we ran out of space */
+ if (max_len <= 1) /* Out of space? */
+ {
+ int nc;
+ *d = 0; /* Terminate */
+ do
+ {
+ nc = *s++; /* Next char */
+ if (nc >= 128 + FC_ABBRV_NUMWORDS) /* Ignore words */
+ nc = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128) * 2) + 1]; /* Only check 2nd letter of pair */
+ }
+ while (nc);
+ }
+ return s - os; /* Length of abbreviated string */
+}
+
+#endif /* ABBR_FILECACHE */
+
+
+/* Each entry in the cache looks like this: */
+typedef struct fce_
+{
+ char *name; /* canonical pathname of file */
+ char *text; /* text (be it compressed or not) */
+ char *eof; /* byte beyond the last byte of text */
+ int used; /* access counter when the file was last used */
+ int compressed; /* compression method, (ie. 0 for none or 1 for abbreviations) */
+}
+FileCacheEntry;
+
+/*
+ | The handles we chuck around are pointers to one of these structs.
+ | Note that since we actually return (and take) |FILE*|s we just
+ | compare the value of a |FILE*| with the limits of the array of
+ | |CachedFileHandle|s to decide whether its 'ours' or a genuine
+ | (ie. stdio) file handle. This /is/ pretty lame, I know...
+ */
+typedef struct cfh_
+{
+ char *ptr; /* sequential file pointer, as it were */
+ FileCacheEntry *fce; /* ->the file-cache entry data */
+}
+CachedFileHandle;
+
+#define MAX_OPEN_CACHED_FILES 16 /* We allow up to 16 of these files open at once */
+#define MAX_CACHE_ENTRIES 64 /* We allow up to 64 cache entries */
+
+static FileCacheEntry *file_cache; /* to be used as file_cache[MAX_CACHE_ENTRIES] */
+static CachedFileHandle *cached_file_handle; /* to be used as cached_file_handle[MAX_OPEN_CACHED_FILES] */
+static int file_cache_initd = 0; /* Is the cache initialised? */
+static int file_cache_size = 0; /* Total size of the cached files */
+static int fc_access_counter = 1; /* incremented on each cache access */
+/*
+ | Pre-calculate max. possible value of a FILE* (ie. address in memory)
+ | that could be a valid |CachedFileHandle*|.
+ */
+static FILE *max_cfh_addr; /* == (FILE*) (&(cached_file_handle[MAX_OPEN_CACHED_FILES-1])) */
+
+/*
+ | Initialise the file cache
+ */
+static void init_file_cache(void)
+{
+ int i;
+
+ /* Allocate storage */
+ file_cache = fc_malloc(MAX_CACHE_ENTRIES * sizeof(FileCacheEntry));
+ cached_file_handle =
+ fc_malloc(MAX_OPEN_CACHED_FILES * sizeof(CachedFileHandle));
+
+ if (!file_cache || !cached_file_handle)
+ {
+ /* Disable file-caching */
+ if (file_cache)
+ {
+ fc_free(file_cache);
+ }
+ if (cached_file_handle)
+ {
+ fc_free(cached_file_handle);
+ }
+ use_filecache = 0;
+ }
+ else
+ {
+ /* Initialise the cache */
+ for (i = 0; i < MAX_CACHE_ENTRIES; i++)
+ file_cache[i].name = NULL;
+ for (i = 0; i < MAX_OPEN_CACHED_FILES; i++)
+ cached_file_handle[i].fce = NULL;
+ fc_access_counter = 1;
+ file_cache_size = 0;
+ max_cfh_addr =
+ (FILE *)(&(cached_file_handle[MAX_OPEN_CACHED_FILES - 1]));
+ }
+ file_cache_initd = 1;
+}
+
+
+/*
+ | Helper: take a copy of the string and return a pointer to it
+ */
+static char *string_cpy(char *s)
+{
+ char *d = fc_malloc(strlen(s) + 1L);
+ if (d)
+ {
+ strcpy(d, s);
+ }
+ return d;
+}
+
+
+/*
+ | Cache the specified file, returning either the cache entry
+ | that it has been cached at, or NULL for failure.
+ |
+ | Note that for the abbreviated file cache a temporary file
+ | is used to allow the compression to be applied just once.
+ | (otherwise it has to be done twice - once to determine the
+ | eventual compressed size and once to actually store and compress
+ | it).
+ */
+static FileCacheEntry *cache_file(char *name)
+{
+ int i, size = 0;
+ FILE *fp;
+ char buffer[1024];
+ char *d;
+
+ FILE *tf = NULL; /* Used if abbr_filecache and abbr_tmpfile are set */
+ char cfn[1024]; /* Used if abbr_filecache and abbr_tmpfile are set */
+
+ /* Find the first free slot in the cache */
+ for (i = 0; i < MAX_CACHE_ENTRIES; i++)
+ if (!file_cache[i].name)
+ break;
+
+ /* No more entries? */
+ if (i >= MAX_CACHE_ENTRIES)
+ {
+ return NULL;
+ }
+
+ /* Set up the info on the file */
+ if ((file_cache[i].name = string_cpy(name)) == NULL)
+ {
+ return NULL;
+ }
+
+ /* Open the file */
+ fp = my_fopen(name, "r");
+ if (!fp)
+ {
+ fc_free(file_cache[i].name);
+ file_cache[i].name = 0;
+ return NULL;
+ }
+
+ /* Open/create tempfile if need be: */
+ if (abbr_filecache && abbr_tmpfile)
+ {
+ /* Hack: Form the pathname of the cached compressed file (in canonical form) */
+ sprintf(cfn, "%s%s", scrap_path,
+ riscosify_name(name + strlen(resource_path)));
+ /* Ensure that that particular directory exists... */
+ ensure_path(cfn);
+ /* Check whether cache file is out of date */
+ if (file_is_newer(riscosify_name(name), cfn))
+ {
+ tf = fopen(cfn, "wb");
+ size = 0;
+ }
+ else
+ {
+ tf = fopen(cfn, "rb");
+ if (tf)
+ {
+ size = myFile_Size(cfn);
+ }
+ }
+ }
+
+ /* If we don't have the cached file (but want it), compress the source text to it */
+ if (tf)
+ {
+ if (!size)
+ {
+ int k;
+ while (!my_fgets(fp, buffer, sizeof(buffer)))
+ {
+ if (smart_filecache && (!*buffer || *buffer == '#'))
+ continue;
+ k = compress_string(buffer, buffer);
+ if (fwrite(buffer, 1, k, tf) != k)
+ {
+ fclose(tf);
+ remove(cfn);
+ core("error writing tempfile");
+ }
+ size += k;
+ }
+ fclose(tf);
+ tf = fopen(cfn, "rb");
+ }
+ }
+ else
+ {
+ /* Count the number of bytes */
+ while (!my_fgets(fp, buffer, sizeof(buffer)))
+ {
+ if (smart_filecache && (!*buffer || *buffer == '#'))
+ continue;
+ if (abbr_filecache)
+ size += compressed_length(buffer);
+ else
+ size += strlen(buffer) + 1;
+ }
+ }
+
+ /* Close the (source) file */
+ my_fclose(fp);
+
+ /* Allocate enough storage for the text */
+ file_cache[i].text = fc_malloc(size + 1L);
+ if (!file_cache[i].text)
+ {
+ fc_free(file_cache[i].name);
+ file_cache[i].name = 0;
+ if (tf)
+ {
+ fclose(tf);
+ }
+ return NULL;
+ }
+
+ /* Do we have a tempfile to load? */
+ if (tf)
+ {
+ if (fread(file_cache[i].text, 1, size, tf) != size)
+ core("error reading tempfile");
+ fclose(tf);
+ }
+ else
+ {
+ /* Re-open the file... */
+ fp = my_fopen(name, "r");
+ if (!fp)
+ {
+ fc_free(file_cache[i].name);
+ fc_free(file_cache[i].text);
+ file_cache[i].name = 0;
+ return NULL;
+ }
+
+ /* And read it into the buffer... */
+ d = file_cache[i].text;
+ while (!my_fgets(fp, buffer, sizeof(buffer)))
+ {
+ if (smart_filecache && (!*buffer || *buffer == '#'))
+ continue;
+ if (abbr_filecache)
+ d += compress_string(d, buffer);
+ else
+ {
+ strcpy(d, buffer);
+ d += strlen(buffer) + 1;
+ }
+ }
+
+ if ((d - file_cache[i].text) != size)
+ {
+ debug("Calculated size is %d, pointer offset is %d", size,
+ (d - file_cache[i].text));
+ core("Cached file is larger than calculated!");
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+ }
+
+ /* Set up the 'last accessed' value, etc. */
+ file_cache[i].used = fc_access_counter++;
+ file_cache[i].eof = file_cache[i].text + size;
+ file_cache[i].compressed = abbr_filecache;
+ file_cache_size += size;
+
+ /* Return success */
+ return &(file_cache[i]);
+}
+
+
+/*
+ | Discard a file from the cache
+ */
+static void discard_cached_file(int i)
+{
+ if (!file_cache[i].name)
+ {
+ return;
+ } /* invalid request */
+ fc_free(file_cache[i].text);
+ fc_free(file_cache[i].name);
+ file_cache_size -= (file_cache[i].eof) - (file_cache[i].text);
+ file_cache[i].name = 0;
+}
+
+
+/*
+ | Attempt to flush as much of the cache as required
+ | to bring it within the size limit.
+ | If protect != 0 then that entry in the cache won't be flushed.
+ */
+static void flush_file_cache(FileCacheEntry * protect)
+{
+ int i, j, done;
+ int oldest_u, oldest_e;
+ FileCacheEntry *fce;
+ int needed = (4 << 10); /* Hack: try to free at least 4K */
+
+ done = (file_cache_size + needed) <= max_file_cache_size;
+
+ while (!done)
+ {
+ oldest_u = fc_access_counter;
+ oldest_e = -1;
+
+ fce = file_cache;
+ /* Find oldest entry that isn't in use */
+ for (i = 0; i < MAX_CACHE_ENTRIES; fce++, i++)
+ {
+ if (fce == protect)
+ {
+ continue;
+ } /* Hack ;) */
+ if (fce->name) /* Is this cache slot full? */
+ {
+ for (j = 0; j < MAX_OPEN_CACHED_FILES; j++)
+ if (cached_file_handle[j].fce == fce)
+ break;
+ if (j < MAX_OPEN_CACHED_FILES)
+ continue; /* Cached file is still open */
+ if (fce->used < oldest_u)
+ {
+ oldest_e = i;
+ oldest_u = file_cache[i].used;
+ }
+ }
+ }
+
+ if (oldest_e < 0)
+ done = 1; /* We can flush nothing more */
+ else
+ {
+ discard_cached_file(oldest_e);
+ done = (file_cache_size + needed) <= max_file_cache_size;
+ }
+ }
+}
+
+
+/*
+ | Locate the specified file within the cache.
+ | Returns NULL if the file is not cached
+ */
+static FileCacheEntry *find_cached_file(char *name)
+{
+ int i;
+ FileCacheEntry *fce = file_cache;
+
+ for (i = 0; i < MAX_CACHE_ENTRIES; i++, fce++)
+ {
+ if (fce->name)
+ if (streq(fce->name, name))
+ return fce;
+ }
+ return NULL;
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Externally visible file cache stuff */
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Open a file...
+ | Returns the file cache handle of the file, or NULL for failure.
+ | Note that if mode is anything other than "r" the call defers to
+ | my_fopen().
+ |
+ | NB: The returned handle is almost certainly *NOT* a |FILE*|
+ | (although it may be if the cache cannot accomodate the file).
+ |
+ | Therefore, you *MUST* ensure that any file opened with cached_fopen()
+ | is only ever accessed via cached_fgets() and cached_fclose().
+ |
+ | Failure to do so will result in, ahem, unpleasantness. Extreme
+ | unpleasantness.
+ */
+FILE *cached_fopen(char *name, char *mode)
+{
+ FileCacheEntry *fcs = NULL;
+ int fch;
+
+ if (strcmp(mode, "r") || !use_filecache)
+ return my_fopen(name, mode);
+
+ if (!file_cache_initd)
+ {
+ init_file_cache();
+ }
+
+ if (max_file_cache_size >= 0)
+ {
+ /* Find a free cache entry */
+ for (fch = 0; fch < MAX_OPEN_CACHED_FILES; fch++)
+ if (!cached_file_handle[fch].fce)
+ break;
+
+ /* Out of handles? */
+ if (fch >= MAX_OPEN_CACHED_FILES)
+ return my_fopen(name, mode);
+
+ /* Is the file already cached? */
+ fcs = find_cached_file(name);
+ if (!fcs)
+ {
+ /* File wasn't cached, so cache it */
+ flush_file_cache(NULL); /* Clean stuff out of the cache if need be */
+ fcs = cache_file(name); /* Cache the new file */
+ flush_file_cache(fcs); /* Flush, but keep the latest file */
+ }
+ }
+
+ /* Did we fail to cache the file? */
+ if (!fcs)
+ {
+ return my_fopen(name, mode);
+ }
+
+ /* File was cached OK */
+ cached_file_handle[fch].ptr = fcs->text; /* Init sequential pointer */
+ cached_file_handle[fch].fce = fcs; /* Cache block pointer */
+ fcs->used = fc_access_counter++; /* Opening the file counts as an access */
+
+ return (FILE *)(&cached_file_handle[fch]);
+}
+
+
+/*
+ | Close a file
+ */
+errr cached_fclose(FILE *fch_)
+{
+ CachedFileHandle *fch;
+
+ /* Is the FILE* genuine? */
+ if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
+ return my_fclose(fch_);
+
+ fch = (CachedFileHandle *) fch_;
+
+ /* Check for "Ooopses": */
+ if (fch->fce == NULL)
+ core("cached_fclose called on a non-open file handle");
+
+ flush_file_cache(NULL); /* Clean out the cache if need be */
+ fch->fce = NULL; /* Mark file handle as inactive */
+
+ return 0;
+}
+
+
+/*
+ | Do the my_fgets thing on a file
+ */
+errr cached_fgets(FILE *fch_, char *buffer, int max_len)
+{
+ CachedFileHandle *fch;
+ char *eof;
+ char *ptr;
+
+ /* Is the FILE* genuine? */
+ if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
+ return my_fgets(fch_, buffer, max_len);
+
+ fch = (CachedFileHandle *) fch_;
+
+ /* Check for "Oopses": */
+ if (!file_cache_initd)
+ core("cached_fgets() on uninitialised file-cache");
+ if (!fch->fce)
+ core("cached_fgets called for a un-open file");
+
+ eof = fch->fce->eof;
+ ptr = fch->ptr;
+
+ /* Out of bounds? */
+ if (ptr >= eof)
+ {
+ return 1;
+ } /* Read failed */
+
+ /*
+ | Read the next line, up to \0 (which would have v=been \n in the original file),
+ | or max_len-1 characters
+ */
+ if (fch->fce->compressed)
+ ptr += decompress_string(buffer, ptr, max_len);
+ else
+ {
+ if (eof - ptr < max_len)
+ {
+ max_len = eof - ptr;
+ }
+ for (; max_len >= 1; max_len--)
+ if ((*buffer++ = *ptr++) == 0)
+ break;
+ *buffer = 0; /* terminate (paranoia) */
+ }
+
+ /* Update sequential pointer */
+ fch->ptr = ptr;
+
+ return 0;
+}
+
+#endif /* USE_FILECACHE */
+
+
+/*
+ | This section deals with checking that the .raw files are up to date
+ | wrt to the .txt files. Note that this function won't work for RISC OS 2
+ | (due to the lack of OS_Args 7) so it simply returns 0 to indicate that
+ | the file isn't OOD.
+ |
+ | For this to work, the equivalent function (in init2.c) needs to be
+ | #if-d out (and this function should be declared). You'll probably
+ | also need to zap the UNIX #includes at the top of the file
+ */
+
+extern errr check_modification_date(int fd, cptr template_file)
+{
+ char raw_buf[1024];
+ char txt_buf[1024];
+ int i;
+ os_error *e;
+
+ if (os_version() < 300)
+ {
+ return 0;
+ }
+
+ /* Use OS_Args 7 to find out the pathname 'fd' refers to */
+ e = SWI(6, 0, SWI_OS_Args,
+ /* In: */
+ 7, /* Get path from filehandle */
+ fd, /* file handle */
+ raw_buf, /* buffer */
+ 0, 0, /* unused */
+ 1024 /* size of buffer */
+ /* No output regs used */
+ );
+ if (e)
+ {
+ core(e->errmess);
+ }
+
+ /* Build the path to the template_file */
+ path_build(txt_buf, sizeof(txt_buf), ANGBAND_DIR_EDIT, template_file);
+
+ i = file_is_newer(riscosify_name(txt_buf), raw_buf);
+
+ return i;
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | This is the hideous IClear hack. Basically what we do is to take a copy
+ | of the module and kill it in the RMA. Then, on return to the desktop
+ | we reinstate the module.
+ |
+ | NB: This is truly, truly evil.
+ */
+
+static int *iclear_module = NULL;
+
+/*
+ | Start the IClear hack
+ */
+static void iclear_hack(void)
+{
+ os_error *e;
+ int code = 0;
+ int *i;
+
+ /* Get base address of module */
+ e = SWI(2, 4, SWI_OS_Module, 18, "IClear", 0, 0, 0, &code);
+ if (e || !code)
+ return;
+
+ /* Module size is at code!-4 */
+ i = (int *)code;
+ iclear_module = malloc(i[-1] + 4);
+ if (!iclear_module)
+ return;
+
+ /* Copy the module */
+ *iclear_module = i[-1];
+ memcpy(iclear_module + 1, (void *)code, i[-1]);
+
+ /* Kill the current version */
+ e = SWI(2, 0, SWI_OS_Module, 4, "IClear");
+ if (e)
+ {
+ free(iclear_module);
+ iclear_module = NULL;
+ }
+}
+
+
+/*
+ | Remove the IClear hack
+ */
+static void remove_iclear_hack(void)
+{
+ os_error *e;
+
+ if (!iclear_module)
+ return;
+
+ e = SWI(3, 0, SWI_OS_Module, 11, iclear_module + 1, *iclear_module);
+ if (e)
+ debug("Failed to reinstall IClear: %s", e->errmess);
+
+ free(iclear_module);
+ iclear_module = NULL;
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/* Alarm functions */
+static int alarm_ackd = 0; /* has the alarm been acknowledged? */
+static window_handle aw = 0; /* alarm window */
+
+/*
+ | Is the alarm due to go off, ie. is it enabled, and if so
+ | does the current time match the alarm time?
+ */
+static void check_alarm()
+{
+ time_t t;
+ struct tm *lt;
+
+ alarm_lastcheck = Time_Monotonic();
+
+ time(&t);
+ lt = localtime(&t);
+ if (lt->tm_hour == alarm_h && lt->tm_min == alarm_m)
+ {
+ if (!alarm_ackd) alarm_disp = 1;
+ }
+ else
+ {
+ alarm_ackd = 0;
+ }
+
+ /* Hack: if the alarm has already been acknowledged then don't re-trigger it */
+ if (alarm_ackd)
+ {
+ alarm_disp = 0;
+ }
+
+ /* Hack: if the alarm should make a noise, then make one: */
+ if (alarm_disp && alarm_beep == 1)
+ {
+ static unsigned int last_beep = 0;
+ unsigned int t = Time_Monotonic();
+ if (t >= last_beep + 100)
+ {
+ Sound_SysBeep();
+ last_beep = t;
+ }
+ }
+
+ /*
+ | If we're in the desktop then fire the alarm off if need be.
+ | If we aren't then do nothing - the fullscreen bored() function
+ | will take care of the alarm.
+ */
+#ifndef FULLSCREEN_ONLY
+ if (!fullscreen_font && alarm_disp)
+ trigger_alarm_desktop();
+#endif /* FULLSCREEN_ONLY */
+}
+
+
+static void ack_alarm(void)
+{
+ if (aw)
+ {
+ Window_Delete(aw);
+ aw = 0;
+ }
+ alarm_ackd = 1;
+ alarm_disp = 0;
+
+ if (alarm_type == 3)
+ {
+ /* One shot alarm */
+ alarm_type = 0;
+ write_alarm_choices();
+ }
+
+}
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Click in the (desktop) alarm window
+ */
+static BOOL Hnd_AlarmClick(event_pollblock * pb, void *ref)
+{
+ ack_alarm();
+ return TRUE;
+}
+
+
+/*
+ | The alarm has gone off in the desktop
+ */
+static void trigger_alarm_desktop(void)
+{
+ char buffer[120];
+ if (aw)
+ return;
+
+ aw = Window_Create("alarm", template_TITLEMIN);
+ if (!aw)
+ {
+ core("failed to create Alarm window!");
+ }
+ sprintf(buffer, "Alarm from %s", VARIANT);
+ Window_SetTitle(aw, buffer);
+ Event_Claim(event_CLICK, aw, 0, Hnd_AlarmClick, NULL);
+ Event_Claim(event_CLOSE, aw, event_ANY, Hnd_AlarmClick, NULL);
+
+ Icon_printf(aw, 1, "An alarm was set for %02d:%02d", alarm_h, alarm_m);
+ Icon_SetText(aw, 2, alarm_message);
+ Window_Show(aw, open_CENTERED);
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+/*--------------------------------------------------------------------------*/
+
+#ifndef FE_DEBUG_INFO
+static void show_debug_info(void)
+{
+ core("main-acn internal logic error 004");
+}
+#else
+
+static int debug_cx = 0;
+static int debug_cy = 0;
+static int debug_cl = TERM_WHITE;
+static int debug_sl = 0;
+
+
+static void debug_cls(void)
+{
+ Term_clear();
+ debug_cx = debug_cy = debug_sl = 0;
+}
+
+static void debug_tcol(int c)
+{
+ debug_cl = c;
+}
+
+
+static void debug_scroll(void)
+{
+ char **c = ((term_data *)Term)->t.scr->c; /* char array [24][80] */
+ byte **a = ((term_data *)Term)->t.scr->a; /* attr array [24][80] */
+ int cc;
+ char tmp[82];
+ int y, x, p;
+
+ cc = a[1][0];
+
+ for (y = 1; y < 23; y++)
+ {
+ for (x = p = 0; x < 80; x++)
+ {
+ if (a[y][x] != cc)
+ {
+ tmp[p] = 0;
+ Term_putstr(x - p, y - 1, p, cc, tmp);
+ p = 0;
+ cc = a[y][x];
+ }
+ tmp[p++] = c[y][x];
+ }
+ Term_putstr(x - p, y - 1, p, cc, tmp);
+ }
+
+ Term_erase(0, 22, 80);
+}
+
+
+static void debug_print_line(char *l)
+{
+ char *le;
+ int cr = 0;
+
+ /* Handle scrolling */
+ if (debug_cy > 22)
+ {
+ debug_cy = 22;
+ if (--debug_sl < 0)
+ {
+ int k;
+ display_line(0, 23, TERM_YELLOW, "[RET one line, SPC one page]");
+ do
+ {
+ k = inkey();
+ }
+ while (k != 32 && k != 13);
+ Term_erase(0, 23, 79);
+ debug_sl = k == 32 ? 21 : 0;
+ }
+ debug_scroll();
+ }
+
+ /* Hack: check for NL */
+ for (le = l; *le; le++)
+ if (*le == '\n')
+ {
+ cr = 1;
+ break;
+ }
+
+ /* display text */
+ Term_putstr(debug_cx, debug_cy, le - l, debug_cl, l);
+
+ /* move cursor */
+ if (!cr)
+ {
+ debug_cx += (le - l);
+ if (debug_cx >= 80)
+ {
+ cr = 1;
+ }
+ }
+ if (cr)
+ {
+ debug_cx = 0;
+ debug_cy += 1;
+ }
+
+ Term_gotoxy(debug_cx, debug_cy);
+
+}
+
+
+static int debug_next_line(char *lb, char **t, int cx)
+{
+ int i = 0;
+ char *lt = *t;
+
+ if (!*lt)
+ {
+ return -1;
+ } /* Out of text */
+
+ while (*lt && cx < 80)
+ {
+ lb[i] = *lt++;
+
+ if (lb[i] == '\n') /* New line */
+ {
+ cx = 0; /* Cursor x will be 0 after displaying */
+ i++; /* Keep the \n in the output */
+ break; /* All done */
+ }
+ else if (lb[i] == '\t') /* Tab */
+ {
+ while (cx < 80)
+ {
+ lb[i++] = ' ';
+ cx++;
+ if ((cx & 7) == 0)
+ {
+ break;
+ }
+ }
+ }
+ else /* Anything else */
+ {
+ cx++;
+ i++;
+ }
+ }
+
+ lb[i] = 0; /* terminate line buffer */
+ *t = lt; /* update text pointer */
+ return cx; /* return cursor x after printing */
+}
+
+static void debug_printf(char *fmt, ...)
+{
+ char buffer[1024];
+ char line[82];
+ va_list ap;
+ char *p = buffer;
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ /* Now split the string into display lines */
+ while (debug_next_line(line, &p, debug_cx) >= 0)
+ debug_print_line(line);
+}
+
+
+static void debug_version_info(void)
+{
+ debug_tcol(TERM_YELLOW);
+
+ debug_printf("\n\nMisc. Info:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tVariant name = \"%s\"\n", VARIANT);
+ debug_printf("\tFront-end version: %s\n", PORTVERSION);
+ debug_printf("\tFront-end compiled: %s %s\n", __TIME__, __DATE__);
+ debug_printf("\tCompile time flags:\n");
+
+#ifdef USE_FILECACHE
+ debug_printf("\t\tUSE_FILECACHE\n");
+#endif
+
+#ifdef ABBR_FILECACHE
+ debug_printf("\t\tABBR_FILECACHE\n");
+#endif
+
+#ifdef SMART_FILECACHE
+ debug_printf("\t\tSMART_FILECACHE\n");
+#endif
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nResource path:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t\"%s\"\n", resource_path);
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nTempfile path:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t\"%s\"\n", scrap_path);
+ debug_printf("\tScrapfiles are %s deleted at exit.\n",
+ (flush_scrap ? "" : "NOT"));
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nChoices files:\n");
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tDesired files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tPrimary (r/w): \"%s\"\n", choices_file[CHFILE_WRITE]);
+ debug_printf("\t Fallback (r): \"%s\"\n", choices_file[CHFILE_READ]);
+ debug_printf("\t Mirror (r/w): \"%s\"\n", choices_file[CHFILE_MIRROR]);
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tActual files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t Write: \"%s\"\n", find_choices(TRUE));
+ debug_printf("\t Read: \"%s\"\n", find_choices(FALSE));
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nAlarm files:\n");
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tDesired files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tPrimary (r/w): \"%s\"\n", alarm_file[CHFILE_WRITE]);
+ debug_printf("\t Fallback (r): \"%s\"\n", alarm_file[CHFILE_READ]);
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tActual files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t Write: \"%s\"\n", find_alarmfile(TRUE));
+ debug_printf("\t Read: \"%s\"\n", find_alarmfile(FALSE));
+#ifdef USE_DA
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nDynamic areas:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tFontcache DA = %d\t", font_area);
+ debug_printf("size = %d\theap size = %d\n", font_area_size, font_heap_size);
+ debug_printf("\t ralloc DA = %d\t", game_area);
+ debug_printf("size = %d\theap size = %d\n", game_area_size, game_heap_size);
+#endif
+}
+
+
+static void debug_filecache_info(void)
+{
+#ifndef USE_FILECACHE
+ debug_tcol(TERM_L_DARK);
+ debug_printf("File cache disabled at compile time.\n");
+#else
+ int j, k;
+ int t, cs, ucs, cf;
+
+ cf = cs = ucs = j = k = 0; /* To stop an usused warning if USE_FILECACHE is undefined */
+ t = strlen(resource_path);
+
+ if (!file_cache_initd)
+ {
+ init_file_cache();
+ } /* Paranoia */
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nFilecache contents:\n");
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("Flags: Smart=%d; Abbrv=%d; Slave=%d; Enable=%d\n",
+ smart_filecache, abbr_filecache, abbr_tmpfile, use_filecache);
+ debug_tcol(TERM_SLATE);
+ if (smart_filecache || abbr_filecache)
+ debug_printf("\t\t%3s %6s/%-6s %6s %6s Path (relative to lib/)\n",
+ "Hnd", "Cache", "Disc", "Time", "Status");
+ else
+ debug_printf("\t\t%3s %6s %6s %6s Path (relative to lib/)\n", "Hnd",
+ "Size", "Time", "Status");
+
+ for (j = 0; j < MAX_CACHE_ENTRIES; j++)
+ {
+ FileCacheEntry *fce = &(file_cache[j]);
+ if (fce->name)
+ {
+ cf++;
+ debug_tcol(TERM_L_GREEN);
+ debug_printf("\t\t%3d ", j);
+ debug_tcol(TERM_L_UMBER);
+ if (!smart_filecache && !abbr_filecache)
+ debug_printf("%6d ", fce->eof - fce->text);
+ else
+ {
+ debug_printf("%6d/", fce->eof - fce->text);
+ k = myFile_Size(riscosify_name(fce->name));
+ debug_printf("%-6d ", k);
+ if (k > 0)
+ {
+ ucs += k;
+ }
+ }
+ cs += fce->eof - fce->text;
+ debug_printf("%6d ", fce->used);
+ for (k = 0; k < MAX_OPEN_CACHED_FILES; k++)
+ if (cached_file_handle[k].fce == fce)
+ break;
+ debug_tcol(TERM_RED);
+ debug_printf("%-6s ", k < MAX_OPEN_CACHED_FILES ? "Open" : "");
+ debug_tcol(TERM_L_UMBER);
+ debug_printf("%s\n", fce->name + t);
+ }
+ }
+
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tTotal:\t%3d ", cf);
+ if (ucs)
+ debug_printf("%6d/%-6d\n", cs, ucs);
+ else
+ debug_printf("%6d\n", cs);
+ debug_tcol(TERM_BLUE);
+#endif /* USE_FILECACHE */
+}
+
+
+static void show_debug_info(void)
+{
+ int k;
+ /* blank the term */
+ debug_cls();
+
+ /* Repeatedly prompt for a command */
+ do
+ {
+ debug_tcol(TERM_VIOLET);
+ debug_printf("\nInfo: (V)ersion, (F)ilecache, ESC=exit ");
+ do
+ {
+ k = inkey();
+ switch (k)
+ {
+ case 'v': case 'V': debug_version_info();
+ break;
+ case 'f': case 'F': debug_filecache_info
+ ();
+ break;
+ case 27: break;
+ default: k = 0;
+ }
+ }
+ while (!k);
+ }
+ while (k != 27);
+
+ Term_clear();
+}
+
+#endif /* FE_DEBUG_INFO */
+
+#endif /* __riscos */
+
+
diff --git a/src/main-sdl-iso.c b/src/main-sdl-iso.c
new file mode 100644
index 00000000..aaae4b18
--- /dev/null
+++ b/src/main-sdl-iso.c
@@ -0,0 +1,1969 @@
+/* Copyright (C) 2003-2004 Neil Stevens <neil@hakubi.us>
+ // Copyright (C) 2004 Ethan Stump <estump@seas.upenn.edu>
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
+ // of this software and associated documentation files (the "Software"), to deal
+ // in the Software without restriction, including without limitation the rights
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ // copies of the Software, and to permit persons to whom the Software is
+ // furnished to do so, subject to the following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included in
+ // all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ // THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ //
+ // Except as contained in this notice, the name(s) of the author(s) shall not be
+ // used in advertising or otherwise to promote the sale, use or other dealings
+ // in this Software without prior written authorization from the author(s).
+ */
+
+#ifdef USE_SDL
+
+#include "angband.h"
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_ttf.h>
+
+#include <math.h>
+
+#ifdef USE_ISO
+/*
+ * Simugraph system (Hj. Malthaner)
+ */
+#include "iso/simsys.h"
+#include "iso/simgraph.h"
+#include "iso/world_adaptor.h"
+#include "iso/world_view.h"
+/*
+ * Simugraph specific routines
+ * by Hj. Malthaner
+ */
+#include "iso/hackdef.h"
+
+/*
+ * Text place marker function protype (Hj. Malthaner)
+ */
+static void set_spots(int x, int y, int n, bool v);
+
+/**
+ * we need to track spots with text to avoid overdrawing text with images
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+bool spots[80][24];
+
+/**
+ * mouse coordinates for Simugraph engine
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int mx, my;
+
+/*
+ * Hajo: this flags need to be set when opening windows
+ */
+
+static int tex_width;
+static int tex_height;
+static int tex_xoff;
+static int tex_yoff;
+
+static unsigned int tab16[1 << 16];
+
+/**
+ * this is used if we need to fake an 8 bit array
+ * @author Hj. Malthaner
+ */
+static unsigned short * data8;
+
+// buffers to store char code/attr for graphics
+unsigned char **iso_ap;
+unsigned char **iso_cp;
+unsigned char **iso_atp;
+unsigned char **iso_ctp;
+unsigned char **iso_aep;
+unsigned char **iso_cep;
+
+#endif /* USE_ISO */
+
+
+/*************************************************
+ GLOBAL SDL-ToME PROPERTIES
+ *************************************************/
+
+/* Default window properties - used if none are available
+from other places*/
+#define DEF_SCREEN_WIDTH 800
+#define DEF_SCREEN_HEIGHT 600
+#define DEF_SCREEN_BPP 16
+
+/*Main window properties that may be loaded at runtime from
+a preference file or environmental variables. However,
+default values (defined above) can be used. */
+static int arg_width = DEF_SCREEN_WIDTH;
+static int arg_height = DEF_SCREEN_HEIGHT;
+static int arg_bpp = DEF_SCREEN_BPP;
+
+/**************/
+
+/* Default font properties - used unless otherwise changed.
+These properties are the size and also default font to load. */
+#define DEF_FONT_SIZE 16
+#define DEF_FONT_FILE "lib/xtra/font/VeraMono.ttf"
+
+/* The font properties that may perhaps be changed at runtime,
+due to environmental variables, preference files, or in-program
+commands.*/
+static int arg_font_size = DEF_FONT_SIZE;
+static char arg_font_name[64] = DEF_FONT_FILE;
+
+/**************/
+
+/* The number of term_data structures to set aside mem for */
+#define MAX_CONSOLE_COUNT 8
+
+/* The number of consoles that are actually being used.
+This number could be changed via preference files, environmental
+variables, command-line arguments, or possibly even in-game
+keypresses or menu-selections. */
+static int arg_console_count = 1;
+
+/* When rendering multiple terminals, each is drawn with a
+surrounding border. These values control the width of this
+border and also the color to use when drawing it. */
+#define BORDER_THICKNESS 1
+static int border_color = 0;
+
+/**************/
+
+/* some miscellaneous settings which have not been dealt
+with yet */
+static bool arg_old_graphics = FALSE;
+static bool arg_double_width = FALSE;
+
+/* not dealt with yet (although full screen toggle
+is available using Alt-Enter) */
+static bool arg_full_screen = FALSE;
+
+
+/*************************************************
+ GLOBAL SDL-ToME VARIABLES
+ *************************************************/
+
+/* the main screen to draw to */
+static SDL_Surface *screen;
+
+/* some helper surfaces that are used for rendering
+characters */
+static SDL_Surface *worksurf;
+static SDL_Surface *crayon;
+
+/* the array of pre-rendered characters
+(see loadAndRenderFont() below) */
+SDL_Surface *text[128];
+
+/* the actual TTF_Font used (XXX should get rid of this)*/
+TTF_Font *font=0;
+
+/* the width and height of the uniformly-sized pre-rendered
+characters */
+int t_width = 1, t_height = 1;
+
+
+/*************************************************
+ COLOR SETUP
+ *************************************************/
+int screen_black;
+int screen_white;
+
+static int color_data[16];
+/* The following macro is for color defining...
+ Note that the color is fully opaque... */
+#define COLOR(r,g,b) \
+ SDL_MapRGBA(crayon->format,r,g,b,SDL_ALPHA_OPAQUE)
+
+/*These color macros will setup the colors to use, but must be called after
+ the SDL video has been set. That way SDL can correct for any funky video
+ setttings. */
+
+#define BLACK COLOR(0x00,0x00,0x00)
+#define WHITE COLOR(0xff,0xff,0xff)
+#define MID_GREY COLOR(0x80,0x80,0x80)
+#define BRIGHT_ORANGE COLOR(0xff,0x80,0x00)
+#define RED COLOR(0xc0,0x00,0x00)
+#define GREEN COLOR(0x00,0x80,0x40)
+#define BRIGHT_BLUE COLOR(0x00,0x00,0xff)
+#define DARK_ORANGE COLOR(0x80,0x40,0x00)
+#define DARK_GREY COLOR(0x40,0x40,0x40)
+#define BRIGHT_GREY COLOR(0xc0,0xc0,0xc0)
+#define PURPLE COLOR(0xff,0x00,0xff)
+#define YELLOW COLOR(0xff,0xff,0x00)
+#define BRIGHT_RED COLOR(0xff,0x00,0x00)
+#define BRIGHT_GREEN COLOR(0x00,0xff,0x00)
+#define AQUAMARINE COLOR(0x00,0xff,0xff)
+#define BROWN COLOR(0xc0,0x80,0x40)
+
+/*************************************************
+ TERMINAL DATA STRUCTURE SETUP
+ *************************************************/
+
+/* Forward declare */
+typedef struct _term_data term_data;
+
+/* A structure for each "term" */
+struct _term_data
+{
+ term t; /* the term structure, defined in z-term.h */
+ cptr name; /* name of this term sub-window */
+
+ uint rows, cols; /* row/column count */
+ uint pos_x, pos_y; /* upper left corner of rendering box */
+ uint size_w, size_h; /* width, height of rendering box */
+
+ bool has_border; /* whether this sub-window has a border or not */
+ uint border_thick; /* thickness of border to draw around window */
+#ifdef USE_GRAPHICS
+#ifdef USE_TRANSPARENCY
+#endif
+#endif
+};
+
+/* The array of term data structures */
+static term_data data[MAX_CONSOLE_COUNT];
+
+/*************************************************
+ FILE-SPECIFIC MACROS
+ *************************************************/
+
+/* Debug macros! */
+#define DB(str) \
+ printf("main-sdl: %s\n",str);
+
+/*************************************************
+ COLOR SETUP
+ *************************************************/
+
+/* SDL Quitting function... declare a few functions first.*/
+void killFontAndAlphabet(void);
+static void sdl_quit(cptr string)
+{
+ printf("sdl_quit called.\n");
+ printf("message: %s\n",string);
+ /* Need to take care of font and rendered characters */
+ killFontAndAlphabet();
+ if (TTF_WasInit())
+ TTF_Quit();
+ /* Then exit SDL */
+ SDL_Quit();
+ /* And now for the default quit behavior */
+ quit_aux = 0;
+ quit(string);
+}
+
+/*************************************************
+ FONT SUPPORT FUNCTIONS
+ *************************************************/
+
+/* killFontAndAlphabet will effectively de-initialize the font system;
+it does this by closing the font and destroying any pre-rendered
+text in memory */
+void killFontAndAlphabet(void)
+{
+ int i;
+ /* need to close a font and free all of its corresponding pre-rendered
+ surfaces */
+ if (font)
+ {
+ TTF_CloseFont(font);
+ font = 0;
+ }
+ for (i=0;i<128;i++)
+ {
+ if(text[i])
+ {
+ SDL_FreeSurface(text[i]);
+ text[i] = NULL;
+ }
+ }
+}
+
+/* loadAndRenderFont is responsible for loading and initializing
+a font. First, SDL_ttf calls are made to load and set the style
+for the desired font. Next, a character alphabet is rendered and
+each character is placed onto a uniformly-sized surface within
+the text[] array. Whenever text is needed for displaying on-screen,
+this array is referenced and the desired character picture is used. */
+void loadAndRenderFont(char *fname, int size)
+{
+ int minx,maxx,miny,maxy,advance,i,midline = 0;
+ SDL_Color base_color = {255,255,255,255};
+ SDL_Surface *temp_surf;
+ SDL_Rect tgt = {0,0,0,0};
+
+ /* Assuming that the filename is valid,
+ open the font (pointer is global var)*/
+ if (fname == NULL)
+ sdl_quit("Gimme a font to load!");
+ font = TTF_OpenFont(fname,size);
+ if (font == NULL)
+ sdl_quit("Error loading that font!");
+ /* Set the font style to normal */
+ TTF_SetFontStyle(font,TTF_STYLE_NORMAL);
+
+ /* Collect some measurements on this font -
+ arbitrarily choose the letter 'a' to get width*/
+ TTF_GlyphMetrics(font,'a',&minx,&maxx,&miny,&maxy,&advance);
+ /* the width of each character tile */
+ t_width = advance;
+ /* the height of each character tile */
+ t_height = TTF_FontHeight(font);
+ /* position of the y=0 line in each tile */
+ midline = TTF_FontAscent(font);
+
+ /* now... render each of the individual characters */
+ for (i=0;i<128;i++)
+ {
+ /* make a pretty blended glyph */
+ temp_surf=TTF_RenderGlyph_Blended(font,i,base_color);
+ /* and make sure that we got it right! */
+ if (temp_surf == NULL)
+ sdl_quit("Glyph failed to render!");
+ /* get the metrics of this particular glyph so we can position it */
+ TTF_GlyphMetrics(font,i,&minx,&maxx,&miny,&maxy,&advance);
+ /* copy rendered glyph into text queue, at the right position*/
+ tgt.x = minx;
+ tgt.y = midline-maxy;
+ /* but first... we'll need a surface in the text queue to blit to! */
+ text[i] = SDL_CreateRGBSurface(SDL_HWSURFACE,t_width,\
+ t_height,16,0xf000,0x0f00,0x00f0,0x000f);
+ /* turn OFF src-alpha... results in brute
+ copy of the RGBA contents of surf */
+ SDL_SetAlpha(temp_surf,0,0);
+ SDL_BlitSurface(temp_surf,NULL,text[i],&tgt);
+ /* turn OFF src-alpha since we'll be using worksurf for blitting */
+ SDL_SetAlpha(text[i],0,0);
+ /* kill the surface to patch up memory leaks */
+ SDL_FreeSurface(temp_surf);
+ }
+}
+
+/***********************************************/
+
+#ifdef USE_ISO
+/*************************************************
+ ISO SUPPORT FUNCTIONS
+ *************************************************/
+/**
+ * inits operating system stuff
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_os_init(int n, int *parameter)
+{
+ // Hajo:
+ // unused in isov-x11
+ return TRUE;
+}
+
+
+/**
+ * opens graphics device/context/window of size w*h
+ * @param w width
+ * @param h height
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_os_open(int w, int h)
+{
+ const int left = 13;
+
+/* tex_width = (data[0].fd->dw * (data[0].t.wid - left + 2) + 3) & 0xFFFC;
+ tex_height = data[0].fd->dh * (data[0].t.hgt - 2);
+
+ tex_xoff = data[0].fd->dw * left;
+ tex_yoff = data[0].fd->dh * 1 + 1;
+*/
+
+ //tex_width = (data[0].size_w - (left - 2) * (data[0].size_w / data[0].cols) + 3) & 0xFFFC;
+ // this is too big (but works :-))
+ tex_width = data[0].size_w & 0xfffc;
+ tex_height = (data[0].size_h / data[0].rows) * (data[0].rows-2);
+
+ tex_xoff = (tex_width / data[0].cols) * left;
+ tex_yoff = (tex_height / data[0].rows) * 1 + 1;
+
+ return TRUE;
+}
+
+
+/**
+ * closes operating system stuff
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_os_close()
+{
+ // Hajo:
+ // unused in isov-x11
+ return TRUE;
+}
+
+
+/**
+ * retrieve display width
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_get_width()
+{
+ return data[0].size_w;
+}
+
+
+/**
+ * retrieve display height
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_get_height()
+{
+ return data[0].size_h;
+}
+
+
+/**
+ * this is used if we need to fake an 8 bit array
+ * @author Hj. Malthaner
+ */
+static unsigned short * data8;
+
+
+/**
+ * creates a (maybe virtual) array of graphics data
+ * @author Hj. Malthaner
+ */
+unsigned short * dr_textur_init()
+{
+ int i;
+
+ printf("isov-sdl::dr_textur_init()\n");
+ printf(" width = %d\n", data[0].size_w);
+ printf(" height = %d\n", data[0].size_h);
+
+ for (i = 0; i < (1 << 16); i++)
+ {
+ // FIXME!!!
+ // must consider color bits, or breaks in anything else but RGB 555
+ unsigned int R;
+ unsigned int G;
+ unsigned int B;
+
+ // RGB 555
+ R = (i & 0x7C00) >> 10;
+ G = (i & 0x03E0) >> 5;
+ B = (i & 0x001F) >> 0;
+
+
+ tab16[i] = SDL_MapRGB(screen->format, R << 3, G << 3, B << 3);
+ }
+
+
+
+ data8 = malloc((data[0].size_w) * (data[0].size_h) * 2);
+
+ printf(" textur = %p\n", data8);
+
+ // fake an 16 bit array and convert data before displaying
+ return data8;
+}
+
+static void flush_area(int dest_x, int dest_y,
+ int x, int y, int w, int h)
+{
+ SDL_Surface *face = screen;
+
+ if (SDL_LockSurface(face) == 0)
+ {
+ int i, j;
+ const int bpp = screen->format->BytesPerPixel;
+
+ for (j = 0; j < h; j++)
+ {
+ unsigned short * p = data8 + (y + j) * tex_width + x;
+ unsigned char * row = face->pixels + (dest_y + j) * face->pitch + dest_x * bpp;
+
+
+ for (i = 0; i < w; i++)
+ {
+ *((unsigned short*)row) = tab16[ *p++ ];
+ row += bpp;
+ }
+ }
+ SDL_UnlockSurface(face);
+ }
+}
+
+/**
+ * displays the array of graphics data
+ * @author Hj. Malthaner
+ */
+void dr_textur(int xp, int yp, int w, int h)
+{
+ int y;
+
+ // clipping unten
+ if (yp + h > tex_height)
+ {
+ h = tex_height - yp;
+ }
+
+ /* debug spots
+ for(y=0; y<24; y++) {
+ int x;
+
+ for(x=0; x<80; x++) {
+ if(spots[x][y]) {
+ printf("X");
+ } else {
+ printf(".");
+ }
+}
+ printf("\n");
+}
+ */
+
+ for (y = 0; y < SCREEN_HGT; y++)
+ {
+ const int left = 13;
+ const int y1 = y + 1;
+ int x = 0;
+
+ yp = data[0].size_h / data[0].rows * y;
+
+ spots[79][y1] = FALSE;
+
+ do
+ {
+ int n = 0;
+ while (x + n + left < 80 && !spots[x + n + left][y1])
+ {
+ n++;
+ }
+
+ xp = data[0].size_w / data[0].cols * x;
+
+
+ flush_area(tex_xoff + xp, tex_yoff + yp,
+ xp, yp,
+ data[0].size_w / data[0].cols*(n),
+ data[0].size_h / data[0].rows);
+
+ x += n;
+
+ while (x + left < 80 && spots[x + left][y1])
+ {
+ x++;
+ }
+ }
+ while (x + left < 80);
+ }
+}
+
+
+/**
+ * use this method to flush graphics pipeline (undrawn stuff) onscreen.
+ * @author Hj. Malthaner
+ */
+void dr_flush()
+{
+ // Iso-view for angband needs no sync.
+ // XSync(md,FALSE);
+}
+
+
+/**
+ * set colormap entries
+ * @author Hj. Malthaner
+ */
+void dr_setRGB8multi(int first, int count, unsigned char * data)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * display/hide mouse pointer
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void show_pointer(int yesno)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * move mouse pointer
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void move_pointer(int x, int y)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * update softpointer position
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void ex_ord_update_mx_my()
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * get events from the system
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void GetEvents()
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * get events from the system without waiting
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void GetEventsNoWait()
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * @returns time since progrma start in milliseconds
+ * @author Hj. Malthaner
+ */
+long long dr_time(void)
+{
+ // Hajo:
+ // unused in isov-x11
+ return 0;
+}
+
+
+/**
+ * sleeps some microseconds
+ * @author Hj. Malthaner
+ */
+void dr_sleep(unsigned long usec)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * loads a sample
+ * @return a handle for that sample or -1 on failure
+ * @author Hj. Malthaner
+ */
+int dr_load_sample(const char *filename)
+{
+ // Hajo:
+ // unused in isov-x11
+ return TRUE;
+}
+
+
+/**
+ * plays a sample
+ * @param key the key for the sample to be played
+ * @author Hj. Malthaner
+ */
+void dr_play_sample(int key, int volume)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+static unsigned char ** halloc(int w, int h)
+{
+ unsigned char **field = (unsigned char **)malloc(sizeof(unsigned char *) * h);
+ int i;
+
+ for (i = 0; i < h; i++)
+ {
+ field[i] = (unsigned char *)malloc(sizeof(unsigned char) * w);
+ memset(field[i], 32 , w);
+ }
+
+ return field;
+}
+
+/**
+ * spot array access procedure. Mark text output spots
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+static void set_spots(const int x, const int y, const int n, const bool v)
+{
+ int i;
+
+ for (i = x; i < x + n; i++)
+ {
+ spots[i][y] = v;
+ }
+}
+
+
+/***********************************************/
+#endif /* USE_ISO */
+
+
+/*** Function hooks needed by "Term" ***/
+
+static void Term_init_sdl(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+ DB("Term_init_sdl");
+ /* XXX XXX XXX */
+}
+
+static void Term_nuke_sdl(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+ DB("Term_nuke_sdl");
+ /* XXX XXX XXX */
+}
+
+static errr Term_user_sdl(int n)
+{
+ term_data *td = (term_data*)(Term->data);
+ DB("Term_user_sdl");
+ /* XXX XXX XXX */
+
+ /* Unknown */
+ return (1);
+}
+
+/* KEYPRESS_STRING repeatedly sends characters to the terminal
+XXX - should implement routine from maim-sdl.c, it's sooo much
+cleaner */
+#define KEYPRESS_STRING(str) \
+strcpy(buf,str); \
+n = buf; \
+while (*n != '\0') { \
+ Term_keypress((int)(*(n++))); \
+}
+
+/* This is the main event handling routine that will be called
+whenever an event is pulled off of the queue (in Term_xtra_sdl())*/
+void handleEvent(SDL_Event *event)
+{
+ static char buf[24]; /* a buffer used when passing key names */
+ char *n; /* and a pointer to manipulate this buffer */
+
+ switch( event->type )
+ {
+ case SDL_KEYDOWN:
+ {
+ /* handle key presses */
+
+ /* I'm reading that as long as the upper 9 bits of the unicode
+ * value are zero, then the lower 7 bits are direct ASCII characters.
+ * Furthermore, it seems that all basic keys return non-zero values
+ * for the lower 7 bits, but function keys and other various things
+ * return 0000000 for the lower 7 bits.
+ * Basically, if the lower 7 bits are zero, do something special
+ * (like start a macro), but otherwise just pass along the ASCII
+ * code!
+ */
+ byte ascii_part = event->key.keysym.unicode & 0x00ff;
+
+ /* gimme the key name */
+ printf("Key is: %s\n",SDL_GetKeyName(event->key.keysym.sym));
+
+ /* allow for full screen toggling! */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_ALT))
+ {
+ SDL_WM_ToggleFullScreen(screen);
+ }
+#ifdef USE_ISO
+ /* toggle tile size */
+ if (event->key.keysym.sym == SDLK_SCROLLOCK)
+ {
+ switch (display_get_tile_size())
+ {
+ case 32:
+ display_select_tile_size(0);
+ break;
+ case 64:
+ display_select_tile_size(1);
+ break;
+ default:
+ display_select_tile_size(0);
+ break;
+ }
+ reset_visuals();
+ strcpy(buf, "graf-iso.prf");
+ process_pref_file(buf);
+ refresh_display();
+ SDL_UpdateRect(screen, 0, 0, data[0].size_w, data[0].size_h);
+ }
+
+ /* cycle grid type none/objects+monsters only/full */
+ if ((event->key.keysym.sym == '#') && \
+ (SDL_GetModState() & KMOD_ALT))
+ {
+ set_grid(get_grid()+1);
+ refresh_display();
+ }
+
+#endif
+
+ /*printf("ascii_part: %d\n",ascii_part);*/
+ if (ascii_part)
+ {
+ /* We have now determined that the ASCII part is not '0', so
+ we can safely pass along the ASCII value! */
+ Term_keypress(ascii_part);
+ }
+ else
+ {
+ /* We want to ignore keypresses that are simply the modifier
+ keys*/
+ if (!( (event->key.keysym.sym == SDLK_RSHIFT) |
+ (event->key.keysym.sym == SDLK_LSHIFT) |
+ (event->key.keysym.sym == SDLK_RALT) |
+ (event->key.keysym.sym == SDLK_LALT) |
+ (event->key.keysym.sym == SDLK_RCTRL) |
+ (event->key.keysym.sym == SDLK_LCTRL) ))
+ {
+
+ /* now build a macro string using the modifiers together
+ with the key that was just pressed*/
+
+ /* As for the formatting...
+ * We pass the key press and modifiers as follows:
+ * \[ctrl-alt-shift-"key name"]
+ * following the previously established convention...
+ *
+ * All of the things that happen are defined in pref-sdl.prf
+ */
+
+ KEYPRESS_STRING("\["); /*Output the first part... */
+ /* See if a control key is down */
+ if (event->key.keysym.mod & KMOD_CTRL)
+ {
+ KEYPRESS_STRING("ctrl-");
+ }
+ /* See if an alt key is down */
+ if (event->key.keysym.mod & KMOD_ALT)
+ {
+ KEYPRESS_STRING("alt-");
+ }
+ /* See if a shift key is down */
+ if (event->key.keysym.mod & KMOD_SHIFT)
+ {
+ KEYPRESS_STRING("shift-");
+ }
+
+ /* Add in the name of whatever key was pressed */
+ KEYPRESS_STRING(SDL_GetKeyName(event->key.keysym.sym));
+
+ /* and end it... */
+ KEYPRESS_STRING("]");
+ }
+ }
+ break;
+ }
+ case SDL_QUIT:
+ {
+ /* handle quit requests */
+ sdl_quit("Quitting!\n");
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+static errr Term_xtra_sdl(int n, int v)
+{
+ static SDL_Event event;
+ term_data *td;
+ char buf[1024];
+
+ /* Analyze */
+ switch (n)
+ {
+ case TERM_XTRA_EVENT:
+ {
+ if (v)
+ {
+ /* Perform event checking with blocking */
+ SDL_WaitEvent( &event );
+ handleEvent( &event );
+ } else {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_FLUSH:
+ {
+ /* Keep doing events until the queue is empty! */
+ while (SDL_PollEvent(&event))
+ {
+ handleEvent(&event);
+ }
+ return (0);
+ }
+
+ case TERM_XTRA_CLEAR:
+ {
+ /* Perform a full screen clear; redraw the sub-window borders.*/
+ static SDL_Rect base;
+ base.x = 0;
+ base.y = 0;
+ base.w = arg_width;
+ base.h = arg_height;
+
+ td = (term_data*)(Term->data);
+
+ /* Lock the screen */
+ if (SDL_MUSTLOCK(screen) ){
+ if (SDL_LockSurface(screen) < 0) {
+ printf("Can't lock the screen: %s\n", SDL_GetError());
+ exit(1);
+ }
+ }
+
+ /* blank the screen area */
+ SDL_FillRect(screen, &base, screen_black);
+
+ /* Unlock the screen */
+ if (SDL_MUSTLOCK(screen) ){
+ SDL_UnlockSurface(screen);
+ }
+
+ /* And... UPDATE the whole screen */
+ SDL_Flip(screen);
+
+ return (0);
+ }
+
+ case TERM_XTRA_SHAPE:
+ {
+ /*
+ * Set the cursor visibility XXX XXX XXX
+ *
+ * This action should change the visibility of the cursor,
+ * if possible, to the requested value (0=off, 1=on)
+ *
+ * This action is optional, but can improve both the
+ * efficiency (and attractiveness) of the program.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FROSH:
+ {
+ /*
+ * Flush a row of output XXX XXX XXX
+ *
+ * This action should make sure that row "v" of the "output"
+ * to the window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FRESH" entry below takes care of any
+ * necessary flushing issues.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_FRESH:
+ {
+ /*
+ * Flush output XXX XXX XXX
+ *
+ * This action should make sure that all "output" to the
+ * window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FROSH" entry above takes care of any
+ * necessary flushing issues.
+ */
+#ifdef USE_ISO
+ // Hajo:
+ // refresh the graphical view
+
+ refresh_display();
+ SDL_UpdateRect(screen, 0, 0, data[0].size_w, data[0].size_h);
+// SDL_UpdateRect(td->face, 0, 0, 80*td->w, 24*td->h);
+#endif /* USE_ISO */
+ return (1);
+ }
+
+ case TERM_XTRA_NOISE:
+ {
+ /*
+ * Make a noise XXX XXX XXX
+ *
+ * This action should produce a "beep" noise.
+ *
+ * This action is optional, but convenient.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_SOUND:
+ {
+ /*
+ * Make a sound XXX XXX XXX
+ *
+ * This action should produce sound number "v", where the
+ * "name" of that sound is "sound_names[v]". This method
+ * is still under construction.
+ *
+ * This action is optional, and not very important.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_BORED:
+ {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_REACT:
+ {
+ /*
+ * React to global changes XXX XXX XXX
+ *
+ * For example, this action can be used to react to
+ * changes in the global "color_table[256][4]" array.
+ *
+ * This action is optional, but can be very useful for
+ * handling "color changes" and the "arg_sound" and/or
+ * "arg_graphics" options.
+ */
+#ifdef USE_ISO
+ strcpy(buf, "graf-iso.prf");
+ process_pref_file(buf);
+#endif /* USE_ISO */
+ return (1);
+ }
+
+ case TERM_XTRA_ALIVE:
+ {
+ /*
+ * Change the "hard" level XXX XXX XXX
+ *
+ * This action is used if the program changes "aliveness"
+ * by being either "suspended" (v=0) or "resumed" (v=1)
+ * This action is optional, unless the computer uses the
+ * same "physical screen" for multiple programs, in which
+ * case this action should clean up to let other programs
+ * use the screen, or resume from such a cleaned up state.
+ *
+ * This action is currently only used by "main-gcu.c",
+ * on UNIX machines, to allow proper "suspending".
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_LEVEL:
+ {
+ /*
+ * Change the "soft" level XXX XXX XXX
+ *
+ * This action is used when the term window changes "activation"
+ * either by becoming "inactive" (v=0) or "active" (v=1)
+ *
+ * This action can be used to do things like activate the proper
+ * font / drawing mode for the newly active term window. This
+ * action should NOT change which window has the "focus", which
+ * window is "raised", or anything like that.
+ *
+ * This action is optional if all the other things which depend
+ * on what term is active handle activation themself, or if only
+ * one "term_data" structure is supported by this file.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * Delay for some milliseconds XXX XXX XXX
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as breath attacks.
+ *
+ * This action is optional, but may be required by this file,
+ * especially if special "macro sequences" must be supported.
+ */
+
+ /* I think that this command is system independent... */
+ /*sleep(v/1000);*/
+ /* main-x11 uses usleep(1000*v); */
+ /* main-win uses Sleep(v); */
+ return (1);
+ }
+
+ case TERM_XTRA_GET_DELAY:
+ {
+ /*
+ * Get Delay of some milliseconds XXX XXX XXX
+ * place the result in Term_xtra_long
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as recording cmovies.
+ *
+ * This action is optional, but cmovies wont perform
+ * good without it
+ */
+
+ return (1);
+ }
+ }
+
+ /* Unknown or Unhandled action */
+ return (1);
+}
+
+#define TYPECOLOR(i) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ color_data[i]>>24,(color_data[i]&0x00ff0000)>>16,\
+ (color_data[i]&0x0000ff00)>>8,(color_data[i]&0x000000ff));
+/*
+ * Display the cursor
+ *
+ * This routine should display the cursor at the given location
+ * (x,y) in some manner. On some machines this involves actually
+ * moving the physical cursor, on others it involves drawing a fake
+ * cursor in some form of graphics mode. Note the "soft_cursor"
+ * flag which tells "z-term.c" to treat the "cursor" as a "visual"
+ * thing and not as a "hardware" cursor.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You may use the "Term_grab(x, y, &a, &c)" function, if needed,
+ * to determine what attr/char should be "under" the new cursor,
+ * for "inverting" purposes or whatever.
+ */
+static errr Term_curs_sdl(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+ DB("Term_curs_sdl");
+ /* XXX XXX XXX */
+#ifdef USE_ISO
+ highlite_spot(x, y);
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters
+ *
+ * This function should erase "n" characters starting at (x,y).
+ *
+ * You may assume "valid" input if the window is properly sized.
+ */
+static errr Term_wipe_sdl(int x, int y, int n)
+{
+ static SDL_Rect base;
+ term_data *td = (term_data*)(Term->data);
+ DB("Wiping");
+ /* calculate boundaries of the area to clear */
+ base.x = td->pos_x + x*t_width;
+ base.y = td->pos_y + y*t_height;
+ base.w = n*t_width;
+ base.h = t_height;
+
+ /* Lock the screen */
+ if (SDL_MUSTLOCK(screen) ){
+ if (SDL_LockSurface(screen) < 0) {
+ printf("Can't lock the screen: %s\n", SDL_GetError());
+ sdl_quit("Bah");
+ }
+ }
+
+ /* blank the screen area */
+ SDL_FillRect(screen, &base, screen_black);
+
+ /* Unlock the screen */
+ if (SDL_MUSTLOCK(screen) ){
+ SDL_UnlockSurface(screen);
+ }
+
+ /* And... UPDATE the rectangle we just wrote to! */
+ SDL_UpdateRects(screen,1,&base);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some text on the screen
+ *
+ * This function should actually display an array of characters
+ * starting at the given location, using the given "attribute",
+ * and using the given string of characters, which contains
+ * exactly "n" characters and which is NOT null-terminated.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You must be sure that the string, when written, erases anything
+ * (including any visual cursor) that used to be where the text is
+ * drawn. On many machines this happens automatically, on others,
+ * you must first call "Term_wipe_xxx()" to clear the area.
+ *
+ * In color environments, you should activate the color contained
+ * in "color_data[a & 0x0F]", if needed, before drawing anything.
+ *
+ * You may ignore the "attribute" if you are only supporting a
+ * monochrome environment, since this routine is normally never
+ * called to display "black" (invisible) text, including the
+ * default "spaces", and all other colors should be drawn in
+ * the "normal" color in a monochrome environment.
+ *
+ * Note that if you have changed the "attr_blank" to something
+ * which is not black, then this function must be able to draw
+ * the resulting "blank" correctly.
+ *
+ * Note that this function must correctly handle "black" text if
+ * the "always_text" flag is set, if this flag is not set, all the
+ * "black" text will be handled by the "Term_wipe_xxx()" hook.
+ */
+static errr Term_text_sdl(int x, int y, int n, byte a, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+ SDL_Rect base_back;
+ int i = n;
+ char old = 0;
+
+ /* calculate place to clear off and draw to */
+ base.x = td->pos_x + x*t_width;
+ base.y = td->pos_y + y*t_height;
+ base.w = n*t_width;
+ base.h = t_height;
+
+ base_back = base;
+
+ /* Lock the screen */
+ if (SDL_MUSTLOCK(screen) ){
+ if (SDL_LockSurface(screen) < 0) {
+ printf("Can't lock the screen: %s\n", SDL_GetError());
+ sdl_quit("Bah");
+ }
+ }
+
+ /* blank the screen area */
+ SDL_FillRect(screen, &base, screen_black);
+
+ /* Unlock the screen */
+ if (SDL_MUSTLOCK(screen) ){
+ SDL_UnlockSurface(screen);
+ }
+
+ /* Note that SDL docs specify that SDL_BlitSurface should not be called
+ on locked surfaces... since the character printing routine below revolves
+ around blitting, the surface has been unlocked first*/
+
+ /* loop through the input string, drawing characters */
+ i = n;
+ old = 0;
+ while (i--)
+ {
+ /* Output the character... */
+ /* If character has not changed, then just blit the old surface into
+ the new location to save effort*/
+ if (*cp == old)
+ {
+ /* the desired character/color combo is already on the work surf */
+ /* just blit it! */
+ SDL_BlitSurface(worksurf,NULL,screen,&base);
+ } else {
+ /* copy the desired character onto working surface */
+ SDL_BlitSurface(text[*cp],NULL,worksurf,NULL);
+ /* color our crayon surface with the desired color */
+ SDL_FillRect(crayon,NULL,color_data[a&0x0f]);
+ /* apply the color to the character on the working surface */
+ SDL_BlitSurface(crayon,NULL,worksurf,NULL);
+ /* and blit it onto our screen! */
+ SDL_BlitSurface(worksurf,NULL,screen,&base);
+ }
+ /* Move to the next position */
+ base.x += t_width;
+ /* Store the old character */
+ old = *cp;
+ /* Increment the character pointer */
+ cp++;
+ }
+
+ // And... UPDATE the rectangle we just wrote to!
+ SDL_UpdateRects(screen,1,&base_back);
+
+#ifdef USE_ISO
+ if (a < 16)
+ {
+ set_spots(x, y, n, TRUE);
+ }
+ else
+ {
+ set_spots(x, y, n, FALSE);
+ }
+#endif /* USE_ISO */
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Draw some attr/char pairs on the screen
+ *
+ * This routine should display the given "n" attr/char pairs at
+ * the given location (x,y). This function is only used if one
+ * of the flags "always_pict" or "higher_pict" is defined.
+ *
+ * You must be sure that the attr/char pairs, when displayed, will
+ * erase anything (including any visual cursor) that used to be at
+ * the given location. On many machines this is automatic, but on
+ * others, you must first call "Term_wipe_xxx(x, y, 1)".
+ *
+ * With the "higher_pict" flag, this function can be used to allow
+ * the display of "pseudo-graphic" pictures, for example, by using
+ * the attr/char pair as an encoded index into a pixmap of special
+ * "pictures".
+ *
+ * With the "always_pict" flag, this function can be used to force
+ * every attr/char pair to be drawn by this function, which can be
+ * very useful if this file can optimize its own display calls.
+ *
+ * This function is often associated with the "arg_graphics" flag.
+ *
+ * This function is only used if one of the "higher_pict" and/or
+ * "always_pict" flags are set.
+ */
+#ifndef USE_ISO
+static errr Term_pict_sdl(int x, int y, int n, const byte *ap, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+ DB("Term_pict_sdl");
+ /* XXX XXX XXX */
+#else
+// for ISO-view we need USE_TRANSPARENCY and USE_EGO_GRAPHICS defined
+static errr Term_pict_sdl(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+{
+ /* Hajo: memorize output */
+ memcpy(&iso_ap[y][x], ap, n);
+ memcpy(&iso_cp[y][x], cp, n);
+ memcpy(&iso_atp[y][x], tap, n);
+ memcpy(&iso_ctp[y][x], tcp, n);
+ memcpy(&iso_aep[y][x], eap, n);
+ memcpy(&iso_cep[y][x], ecp, n);
+
+ // here is no text
+ set_spots(x, y, n, FALSE);
+
+#endif /* USE_ISO */
+
+ /* Success */
+ return (0);
+}
+
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &(td->t);
+ int x = 0;
+ int y = 0;
+ int cols = 80;
+ int rows = 24;
+
+ /* Initialize the term */
+ // gets: pointer to address, number of columns, number of rows, number
+ // of keypresses to queue up (guess 24?)
+ term_init(t, cols, rows, 24);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ // Picture routine flags
+ t->always_pict = FALSE;
+ t->higher_pict = FALSE;
+ t->always_text = FALSE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_sdl;
+ t->curs_hook = Term_curs_sdl;
+ t->wipe_hook = Term_wipe_sdl;
+ t->text_hook = Term_text_sdl;
+#ifdef USE_ISO
+ t->pict_hook = Term_pict_sdl;
+#endif /* USE_ISO */
+
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ // *** Initialize the rest of the term_data stuff....
+ td->name = angband_term_name[i];// name of this term window
+
+ td->rows = rows;
+ td->cols = cols;
+ td->pos_x = x;
+ td->pos_y = y;
+
+
+ td->size_w = cols*t_width;
+ td->size_h = rows*t_height;
+
+ /* Turn on a border, thickness specified by BORDER_THICKNESS */
+ td->has_border = TRUE;
+ td->border_thick = BORDER_THICKNESS;
+
+#ifdef USE_GRAPHICS
+#ifdef USE_TRANSPARENCY
+#endif
+#endif
+
+ /* Success */
+ return (0);
+}
+
+#ifdef PRIVATE_USER_PATH
+
+/*
+ * Check and create if needed the directory dirpath -- copied from main.c
+ */
+bool private_check_user_directory(cptr dirpath)
+{
+ /* Is this used anywhere else in *bands? */
+ struct stat stat_buf;
+
+ int ret;
+
+ /* See if it already exists */
+ ret = stat(dirpath, &stat_buf);
+
+ /* It does */
+ if (ret == 0)
+ {
+ /* Now we see if it's a directory */
+ if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) return (TRUE);
+
+ /*
+ * Something prevents us from create a directory with
+ * the same pathname
+ */
+ return (FALSE);
+ }
+
+ /* No - this maybe the first time. Try to create a directory */
+ else
+ {
+ /* Create the ~/.ToME directory */
+ ret = mkdir(dirpath, 0700);
+
+ /* An error occured */
+ if (ret == -1) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+ }
+}
+
+/*
+ * Check existence of ".ToME/" directory in the user's
+ * home directory or try to create it if it doesn't exist.
+ * Returns FALSE if all the attempts fail.
+ */
+static bool check_create_user_dir(void)
+{
+ char dirpath[1024];
+ char versionpath[1024];
+ char savepath[1024];
+
+ /* Get an absolute path from the filename */
+ path_parse(dirpath, 1024, PRIVATE_USER_PATH);
+ strcpy(versionpath, dirpath);
+ strcat(versionpath, USER_PATH_VERSION);
+ strcpy(savepath, versionpath);
+ strcat(savepath, "/save");
+
+ return private_check_user_directory(dirpath) && private_check_user_directory(versionpath) && private_check_user_directory(savepath);
+}
+
+#endif /* PRIVATE_USER_PATH */
+
+/*
+ * Init some stuff - copied from main.c
+ */
+static void init_stuff(void)
+{
+ char path[1024];
+
+ cptr tail;
+
+ /* Get the environment variable */
+ tail = getenv("TOME_PATH");
+
+ /* Use the angband_path, or a default */
+#ifndef ENABLE_BINRELOC
+ strcpy(path, tail ? tail : DEFAULT_PATH);
+#else /* Runtime lookup of location */
+ strcpy(path, br_strcat(DATADIR, "/tome/lib"));
+#endif
+
+ /* Hack -- Add a path separator (only if needed) */
+ if (!suffix(path, PATH_SEP)) strcat(path, PATH_SEP);
+
+ /* Initialize */
+ init_file_paths(path);
+}
+
+
+errr init_sdl(int argc, char **argv)
+{
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ bool done = FALSE;
+
+ bool new_game = FALSE;
+
+ int show_score = 0;
+
+ cptr mstr = NULL;
+
+ bool args = TRUE;
+
+ float gamma;
+ char filename[PATH_MAX + 1];
+ /* Flags to pass to SDL_SetVideoMode */
+ int videoFlags;
+ /* this holds some info about our display */
+ const SDL_VideoInfo *videoInfo;
+
+
+#ifdef CHECK_MEMORY_LEAKS
+ GC_find_leak = 1;
+#endif /* CHECK_MEMORY_LEAKS */
+
+
+ /* Save the "program name" XXX XXX XXX */
+ argv0 = argv[0];
+
+
+#ifdef USE_286
+ /* Attempt to use XMS (or EMS) memory for swap space */
+ if (_OvrInitExt(0L, 0L))
+ {
+ _OvrInitEms(0, 0, 64);
+ }
+#endif
+
+
+#ifdef SET_UID
+
+ /* Default permissions on files */
+ (void)umask(022);
+
+#endif /* SET_UID */
+
+
+ /* Get the file paths */
+ init_stuff();
+
+
+#ifdef SET_UID
+
+ /* Get the user id (?) */
+ player_uid = getuid();
+
+#ifdef VMS
+ /* Mega-Hack -- Factor group id */
+ player_uid += (getgid() * 1000);
+#endif
+
+# ifdef SAFE_SETUID
+
+# ifdef _POSIX_SAVED_IDS
+
+ /* Save some info for later */
+ player_euid = geteuid();
+ player_egid = getegid();
+
+# endif
+
+# if 0 /* XXX XXX XXX */
+
+ /* Redundant setting necessary in case root is running the game */
+ /* If not root or game not setuid the following two calls do nothing */
+
+ if (setgid(getegid()) != 0)
+ {
+ sdl_quit("setgid(): cannot set permissions correctly!");
+ }
+
+ if (setuid(geteuid()) != 0)
+ {
+ sdl_quit("setuid(): cannot set permissions correctly!");
+ }
+
+# endif /* XXX XXX XXX */
+
+# endif /* SAFE_SETUID */
+
+#endif /* SET_UID */
+
+
+#ifdef SET_UID
+
+ /* Please note that the game is still running in the game's permission */
+
+ /* Initialize the "time" checker */
+ if (check_time_init() || check_time())
+ {
+ sdl_quit("The gates to Angband are closed (bad time).");
+ }
+
+ /* Initialize the "load" checker */
+ if (check_load_init() || check_load())
+ {
+ sdl_quit("The gates to Angband are closed (bad load).");
+ }
+
+
+ /*
+ * Become user -- This will be the normal state for the rest of the game.
+ *
+ * Put this here because it's totally irrelevant to single user operating
+ * systems, as witnessed by huge number of cases where these functions
+ * weren't used appropriately (at least in this variant).
+ *
+ * Whenever it is necessary to open/remove/move the files in the lib folder,
+ * this convention must be observed:
+ *
+ * safe_setuid_grab();
+ *
+ * fd_open/fd_make/fd_kill/fd_move which requires game's permission,
+ * i.e. manipulating files under the lib directory
+ *
+ * safe_setuid_drop();
+ *
+ * Please never ever make unmatched calls to these grab/drop functions.
+ *
+ * Please note that temporary files used by various information commands
+ * and ANGBAND_DIR_USER files shouldn't be manipulated this way, because
+ * they reside outside of the lib directory on multiuser installations.
+ * -- pelpel
+ */
+ safe_setuid_drop();
+
+
+ /* Acquire the "user name" as a default player name */
+ user_name(player_name, player_uid);
+
+
+#ifdef PRIVATE_USER_PATH
+
+ /*
+ * On multiuser systems, users' private directories are
+ * used to store pref files, chardumps etc.
+ */
+ {
+ bool ret;
+
+ /* Create a directory for the user's files */
+ ret = check_create_user_dir();
+
+ /* Oops */
+ if (ret == FALSE) sdl_quit("Cannot create directory " PRIVATE_USER_PATH);
+ }
+
+#endif /* PRIVATE_USER_PATH */
+
+#endif /* SET_UID */
+
+ /* Before sdl_quit could possible be called, need to make sure that the text
+ array is zeroed, so that sdl_quit->killFontAndAlphabet() doesn't try to free
+ SDL_Surfaces that don't exist ! */
+ memset(text,0,sizeof(text));
+
+ /* Initialize the SDL window*/
+ filename[PATH_MAX] = 0;
+
+ /* initialize SDL */
+ if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
+ {
+ sdl_quit("Video initialization failed!");
+ }
+ DB("SDL Initialized!");
+
+ /* Skip to our arguments -- (Neil)*/
+ /* for (i = 1; (i < argc) && (0 != strcmp(argv[i], "--")); ++i); */
+ /* Handle our arguments -- (Neil)*/
+ /* for (++i; i < argc ; ++i)
+ {
+ if (0 == strcmp(argv[i], "-n"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -n\n");
+ return -1;
+ }
+
+ arg_console_count = atoi(argv[i]);
+ if (arg_console_count <= 0 || arg_console_count > MAX_CONSOLE_COUNT)
+ {
+ printf("Invalid console count given.\n");
+ arg_console_count = 1;
+ }
+ }
+ else if (0 == strcmp(argv[i], "-o"))
+ {
+ arg_old_graphics = TRUE;
+ }
+ else if (0 == strcmp(argv[i], "-b"))
+ {
+ arg_double_width = TRUE;
+ }
+ else if (0 == strcmp(argv[i], "-w"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -w\n");
+ return -1;
+ }
+
+ arg_width = atoi(argv[i]);
+ }
+ else if (0 == strcmp(argv[i], "-h"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -h\n");
+ return -1;
+ }
+
+ arg_height = atoi(argv[i]);
+ }
+ else if (0 == strcmp(argv[i], "-fs"))
+ {
+ arg_full_screen = TRUE;
+ }
+ else if (0 == strcmp(argv[i], "-bpp"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -bpp\n");
+ return -1;
+ }
+
+ arg_bpp = atoi(argv[i]);
+ }
+ }
+ */
+
+
+ /* Now for the meat of the initialization -- (Neil)*/
+ quit_aux = sdl_quit;
+
+ /* Window Manager stuff -- (Neil)*/
+ path_build(filename, PATH_MAX, ANGBAND_DIR_XTRA, "graf/icon.png");
+ SDL_WM_SetIcon(IMG_Load(filename), 0);
+ SDL_WM_SetCaption("ToME", "tome");
+
+ /* how about a hardware surface with hardware palette?
+ XXX XXX XXX should probably be chosen at compile-time! */
+ videoFlags = SDL_HWSURFACE | SDL_HWPALETTE;
+
+ /* XXX XXX XXX */
+ if(getenv("TOME_SCREEN_WIDTH")) arg_width = atoi(getenv("TOME_SCREEN_WIDTH"));
+ if(getenv("TOME_SCREEN_HEIGHT")) arg_height = atoi(getenv("TOME_SCREEN_HEIGHT"));
+ if(getenv("TOME_SCREEN_BPP")) arg_bpp = atoi(getenv("TOME_SCREEN_BPP"));
+
+ /* get a SDL surface */
+ screen = SDL_SetVideoMode( arg_width, arg_height, arg_bpp, videoFlags );
+
+ DB("Video Mode Set!");
+
+ /* Verify there is a surface */
+ if ( !screen )
+ {
+ DB("No screen!");
+ sdl_quit("Failed to set SDL Surface.");
+ }
+
+ DB("SDL Window Created!");
+
+ /* Now ready the fonts! */
+
+ DB("initializing SDL_ttf");
+ if(TTF_Init()==-1) {
+ printf("TTF_Init: %s\n", TTF_GetError());
+ sdl_quit("Bah");
+ }
+
+ DB("loading font...");
+ /* XXX centralize these environment calls*/
+ if(getenv("TOME_FONT_SIZE")) arg_font_size = atoi(getenv("TOME_FONT_SIZE"));
+
+ /* load and render the font */
+ loadAndRenderFont(arg_font_name,arg_font_size);
+
+ /* Initialize the working surface and crayon surface used for rendering
+ text in different colors... */
+ worksurf = SDL_CreateRGBSurface(SDL_HWSURFACE,t_width,\
+ t_height,16,0xf000,0x0f00,0x00f0,0x000f);
+ crayon = SDL_CreateRGBSurface(SDL_HWSURFACE,t_width,\
+ t_height,16,0xf000,0x0f00,0x00f0,0x000f);
+
+ /* The working surface will blit using alpha values... */
+ SDL_SetAlpha(worksurf,SDL_SRCALPHA,0);
+
+ /* Set up the colors using the great little color macros! */
+ color_data[0] = BLACK;
+ color_data[1] = WHITE;
+ color_data[2] = MID_GREY;
+ color_data[3] = BRIGHT_ORANGE;
+ color_data[4] = RED;
+ color_data[5] = GREEN;
+ color_data[6] = BRIGHT_BLUE;
+ color_data[7] = DARK_ORANGE;
+ color_data[8] = DARK_GREY;
+ color_data[9] = BRIGHT_GREY;
+ color_data[10] = PURPLE;
+ color_data[11] = YELLOW;
+ color_data[12] = BRIGHT_RED;
+ color_data[13] = BRIGHT_GREEN;
+ color_data[14] = AQUAMARINE;
+ color_data[15] = BROWN;
+
+ /* And setup the screen black color */
+ screen_black = SDL_MapRGB(screen->format,0,0,0);
+ screen_white = SDL_MapRGB(screen->format,255,255,255);
+
+ /* Initialize the windows, or whatever that means in this case */
+ for (i = 0; i < MAX_CONSOLE_COUNT; i++)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+ /* Save global entry */
+ angband_term[i] = Term;
+ }
+
+ /* Enable UNICODE keysyms */
+ SDL_EnableUNICODE(1);
+
+ /* By setting this value, 'pref-sdl.prf' will be loaded on start.
+ Since this contains mappings for various keys, this is important! */
+ ANGBAND_SYS = "sdl";
+#ifdef USE_ISO
+ DB("Isometric view uses always graphics mode.\n");
+ use_graphics = TRUE;
+
+
+ /* Hajo: allocate memory for output data */
+ /* These arrays are read by the iso-view and written from this file */
+ iso_cp = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_ap = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_ctp = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_atp = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_cep = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_aep = halloc(data[0].t.wid, data[0].t.hgt);
+
+ // Hmm, no ANGBAND_SYS in old iso-code
+ // if I change this I don't have to load the *.prf manually?
+ //
+ // seems not to work for the following:
+ /* Hajo: set mode */
+ ANGBAND_GRAF = "iso";
+
+ /* Hajo: init view */
+ init_adaptor();
+
+ center_player = TRUE;
+#endif /* USE_ISO */
+
+ printf("Signals?\n");
+
+ /* Catch nasty signals */
+ signals_init();
+
+ printf("Initialize Angband!\n");
+ /* Initialize */
+ init_angband();
+
+ printf("Angband Initialized!\n");
+
+ /* Hack -- If requested, display scores and quit */
+ if (show_score > 0) display_scores(0, show_score);
+
+ /* Wait for response */
+ pause_line(23);
+
+ printf("Play the game!\n");
+#ifdef USE_ISO
+ // Juergen: HACK, but this all is just for testing ...
+ data[0].t.higher_pict = TRUE;
+#endif
+
+ /* Play the game */
+ play_game(new_game);
+
+ /* Quit */
+ sdl_quit("Game over, man");
+
+ /* Exit */
+ return (0);
+
+}
+
+#endif
diff --git a/src/main-sdl.c b/src/main-sdl.c
new file mode 100644
index 00000000..42c76958
--- /dev/null
+++ b/src/main-sdl.c
@@ -0,0 +1,2865 @@
+/* Copyright (C) 2003-2004 Neil Stevens <neil@hakubi.us>
+ // Copyright (C) 2004 Ethan Stump <estump@seas.upenn.edu>
+ //
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
+ // of this software and associated documentation files (the "Software"), to deal
+ // in the Software without restriction, including without limitation the rights
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ // copies of the Software, and to permit persons to whom the Software is
+ // furnished to do so, subject to the following conditions:
+ //
+ // The above copyright notice and this permission notice shall be included in
+ // all copies or substantial portions of the Software.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ // THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ //
+ // Except as contained in this notice, the name(s) of the author(s) shall not be
+ // used in advertising or otherwise to promote the sale, use or other dealings
+ // in this Software without prior written authorization from the author(s).
+ */
+
+#ifdef USE_SDL
+
+#include "angband.h"
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_ttf.h>
+
+#include <math.h>
+
+#ifdef USE_ISO
+/*
+ * Simugraph system (Hj. Malthaner)
+ */
+#include "iso/simsys.h"
+#include "iso/simgraph.h"
+#include "iso/world_adaptor.h"
+#include "iso/world_view.h"
+/*
+ * Simugraph specific routines
+ * by Hj. Malthaner
+ */
+#include "iso/hackdef.h"
+
+/*
+ * Text place marker function protype (Hj. Malthaner)
+ */
+static void set_spots(int x, int y, int n, bool v);
+
+/**
+ * we need to track spots with text to avoid overdrawing text with images
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+bool spots[80][24];
+
+/**
+ * mouse coordinates for Simugraph engine
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int mx, my;
+
+/*
+ * Hajo: this flags need to be set when opening windows
+ */
+
+static int tex_width;
+static int tex_height;
+static int tex_xoff;
+static int tex_yoff;
+
+static unsigned int tab16[1 << 16];
+
+/**
+ * this is used if we need to fake an 8 bit array
+ * @author Hj. Malthaner
+ */
+static unsigned short * data8;
+
+// buffers to store char code/attr for graphics
+unsigned char **iso_ap;
+unsigned char **iso_cp;
+unsigned char **iso_atp;
+unsigned char **iso_ctp;
+unsigned char **iso_aep;
+unsigned char **iso_cep;
+
+#endif /* USE_ISO */
+
+/*************************************************
+ GLOBAL SDL-ToME PROPERTIES
+ *************************************************/
+
+/* Default window properties - used if none are available
+from other places*/
+#define DEF_SCREEN_WIDTH 800
+#define DEF_SCREEN_HEIGHT 600
+#define DEF_SCREEN_BPP 16
+
+/*Main window properties that may be loaded at runtime from
+a preference file or environmental variables. However,
+default values (defined above) can be used. */
+static int arg_width = DEF_SCREEN_WIDTH;
+static int arg_height = DEF_SCREEN_HEIGHT;
+static int arg_bpp = DEF_SCREEN_BPP;
+
+/**************/
+
+/* Default font properties - used unless otherwise changed.
+These properties are the size and also default font to load. */
+#define DEF_FONT_SIZE 14
+#define DEF_FONT_FILE "VeraMono.ttf"
+
+/* The font properties that may perhaps be changed at runtime,
+due to environmental variables, preference files, or in-program
+commands.*/
+static int arg_font_size = DEF_FONT_SIZE;
+static char arg_font_name[64] = DEF_FONT_FILE;
+
+/**************/
+
+/* Graphics setting - signifies what graphics to use. Valid ints
+are available with given defines */
+
+/* No graphics - use only colored text */
+#define NO_GRAPHICS 0
+/* "Old" graphics - use 8x8.bmp to extract graphics tiles */
+#define GRAPHICS_8x8 8
+/* "New" graphics - use 16x16.bmp as tiles and apply mask.bmp for transparency*/
+#define GRAPHICS_16x16 16
+
+static int arg_graphics_type = NO_GRAPHICS;
+
+
+/**************/
+
+/* The number of term_data structures to set aside mem for */
+#define MAX_CONSOLE_COUNT 8
+
+/* The number of consoles that are actually being used.
+This number could be changed via preference files, environmental
+variables, command-line arguments, or possibly even in-game
+keypresses or menu-selections. */
+static int arg_console_count = 1;
+
+/* When rendering multiple terminals, each is drawn with a
+surrounding border. This value controls the width of this
+border */
+#define BORDER_THICKNESS 1
+
+/**************/
+
+/* some miscellaneous settings which have not been dealt
+with yet */
+static bool arg_double_width = FALSE;
+
+/* flag signifying whether the game is in full screen */
+static bool arg_full_screen = FALSE;
+
+/* a flag to show whether window properties have been
+set or not... if so, the properties can be dumped
+upon quit*/
+static bool window_properties_set = FALSE;
+
+/*************************************************
+ GLOBAL SDL-ToME VARIABLES
+ *************************************************/
+
+/* the main screen to draw to */
+static SDL_Surface *screen;
+
+/* the video settings for the system */
+static SDL_VideoInfo *videoInfo;
+
+/* a flag to suspend updating of the screen;
+this is in place so that when a large area is being
+redrawn -- like when doing a Term_redraw() or when
+redoing the entire screen -- all of the changes
+can be stored up before doing an update. This
+should cut down on screen flicker */
+static bool suspendUpdate = FALSE;
+
+/* some helper surfaces that are used for rendering
+characters */
+static SDL_Surface *worksurf;
+static SDL_Surface *crayon;
+
+/* the cursor surface */
+static SDL_Surface *cursor = NULL;
+
+/* the array of pre-rendered characters
+(see loadAndRenderFont() below) */
+SDL_Surface *text[128];
+
+/* the actual TTF_Font used (XXX should get rid of this)*/
+TTF_Font *font=0;
+
+/* the width and height of the uniformly-sized pre-rendered
+characters */
+int t_width = 0, t_height = 0;
+
+
+/*************************************************
+ COLOR SETUP
+ *************************************************/
+
+/* Simple black, mapped using the format of the main screen */
+int screen_black;
+
+/* The color to use for the cursor */
+static int cursor_color = 0;
+/* default cursor color is a semi-transparent yellow */
+#define DEF_CURSOR_COLOR 255,255,0,128
+
+/* The array of colors, mapped to the format of the crayon surface,
+since this is ultimately the surface that color is begin applied to */
+static int color_data[16];
+
+/* The following macro is for color defining...
+ Note that the color is fully opaque... */
+#define COLOR(r,g,b) \
+ SDL_MapRGBA(crayon->format,r,g,b,SDL_ALPHA_OPAQUE)
+
+/*These color macros will setup the colors to use, but must be called after
+ the SDL video has been set. That way SDL can correct for any funky video
+ setttings. */
+
+#define BLACK COLOR( 0, 0, 0) /* 0*/
+#define WHITE COLOR(255,255,255) /* 1*/
+#define MID_GREY COLOR(128,128,128) /* 2*/
+#define BRIGHT_ORANGE COLOR(255,128, 0) /* 3*/
+#define RED COLOR(192, 0, 0) /* 4*/
+#define GREEN COLOR( 0,128, 64) /* 5*/
+#define BRIGHT_BLUE COLOR( 0, 0,255) /* 6*/
+#define DARK_ORANGE COLOR(128, 64, 0) /* 7*/
+#define DARK_GREY COLOR( 64, 64, 64) /* 8*/
+#define BRIGHT_GREY COLOR(192,192,192) /* 9*/
+#define PURPLE COLOR(255, 0,255) /*10*/
+#define YELLOW COLOR(255,255, 0) /*11*/
+#define BRIGHT_RED COLOR(255, 0, 0) /*12*/
+#define BRIGHT_GREEN COLOR( 0,255, 0) /*13*/
+#define AQUAMARINE COLOR( 0,255,255) /*14*/
+#define BROWN COLOR(192,128, 64) /*15*/
+
+/*************************************************
+ TERMINAL DATA STRUCTURE SETUP
+ *************************************************/
+
+/* Forward declare */
+typedef struct _term_data term_data;
+
+/* A structure for each "term" */
+struct _term_data
+{
+ term t; /* the term structure, defined in z-term.h */
+ cptr name; /* name of this term sub-window */
+
+ uint rows, cols; /* row/column count */
+ SDL_Rect rect; /* the bounding rectangle for the entire box;
+ includes border and empty space as well */
+ /* this rectangle is in screen coordinates */
+
+ int border_thick; /* thickness of border to draw around window */
+ int border_color; /* current color of the border */
+ uint cushion_x_top, cushion_x_bot, cushion_y_top, cushion_y_bot;
+ /* empty space cushion between border and tiles */
+
+ uint tile_width; /* the width of each tile (graphic or otherwise)*/
+ uint tile_height; /* the height of each tile (graphic or otherwise)*/
+
+ SDL_Surface *surf; /* the surface that graphics for this screen are
+ rendered to before blitting to main screen */
+ int black,white,purple; /* basic colors keyed to this terminal's surface */
+
+#ifdef USE_GRAPHICS
+#ifdef USE_TRANSPARENCY
+#endif
+#endif
+};
+
+/* The array of term data structures */
+static term_data data[MAX_CONSOLE_COUNT];
+
+/* Ordered array of pointers to term data structures, placed in order of
+priority: lowest is on top of all others, the higher the index, the further
+back into the screen that it is drawn */
+static term_data *term_order[MAX_CONSOLE_COUNT];
+
+/*************************************************
+ FILE-SPECIFIC MACROS
+ *************************************************/
+
+/* Debug macros! */
+#define DB(str) \
+ printf("main-sdl: %s\n",str);
+
+/* Prints out the RGBA values of a given color */
+#define TYPECOLOR32(color) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ color>>24,(color&0x00ff0000)>>16,\
+ (color&0x0000ff00)>>8,(color&0x000000ff))
+
+#define TYPECOLOR16(color) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ (color&0xf000)>>12,(color&0x0f00)>>8,\
+ (color&0x00f0)>>4,(color&0x000f))
+
+/* SDL Surface locking and unlocking */
+#define SDL_LOCK(surf) \
+ if (SDL_MUSTLOCK(surf) ){ \
+ if (SDL_LockSurface(surf) < 0) { \
+ printf("Can't lock the screen: %s\n", SDL_GetError()); \
+ exit(1); \
+ } \
+ }
+
+#define SDL_UNLOCK(surf) \
+ if (SDL_MUSTLOCK(surf) ){ \
+ SDL_UnlockSurface(surf); \
+ }
+
+/* Wrapped SDL_UpdateRects function, to take into
+account the fact that updates may be suspended by
+the suspendUpdate flag... this macro should be used
+whenever a rect needs updated */
+#define SDL_UPDATE(rect) \
+ if (!suspendUpdate) \
+ SDL_UpdateRects(screen,1,&rect)
+
+/* A complete screen redraw macro */
+#define SDL_REDRAW_SCREEN \
+ SDL_UpdateRect(screen,0,0,arg_width,arg_height)
+
+/*************************************************
+ QUITTING
+ *************************************************/
+
+/* function prototype */
+void dumpWindowSettings(void);
+
+/* SDL Quitting function... declare a few functions first.*/
+void killFontAndAlphabet(void);
+static void sdl_quit(cptr string)
+{
+ printf("sdl_quit called.\n");
+ printf("message: %s\n",string);
+ /* Need to take care of font and rendered characters */
+ killFontAndAlphabet();
+ if (TTF_WasInit())
+ TTF_Quit();
+ /* Then exit SDL */
+ SDL_Quit();
+
+ /* Dump the window properties, if available */
+ if (window_properties_set)
+ dumpWindowSettings();
+
+ /* And now for the default quit behavior */
+ quit_aux = 0;
+ quit(string);
+}
+
+/*************************************************
+ FONT SUPPORT FUNCTIONS
+ *************************************************/
+
+/* function prototype for creating surfaces */
+SDL_Surface *createSurface(int width, int height);
+
+/* killFontAndAlphabet will effectively de-initialize the font system;
+it does this by closing the font and destroying any pre-rendered
+text in memory */
+void killFontAndAlphabet(void)
+{
+ int i;
+ /* need to close a font and free all of its corresponding pre-rendered
+ surfaces */
+ if (font)
+ {
+ TTF_CloseFont(font);
+ font = 0;
+ }
+ for (i=0;i<128;i++)
+ {
+ if(text[i])
+ {
+ SDL_FreeSurface(text[i]);
+ text[i] = NULL;
+ }
+ }
+}
+
+/* loadAndRenderFont is responsible for loading and initializing
+a font. First, SDL_ttf calls are made to load and set the style
+for the desired font. Next, a character alphabet is rendered and
+each character is placed onto a uniformly-sized surface within
+the text[] array. Whenever text is needed for displaying on-screen,
+this array is referenced and the desired character picture is used. */
+void loadAndRenderFont(char *fname, int size)
+{
+ int minx,maxx,miny,maxy,advance,i,midline = 0;
+ char filename[PATH_MAX + 1];
+ char fontdir[PATH_MAX + 1];
+ SDL_Color base_color = {255,255,255,255};
+ SDL_Surface *temp_surf;
+ SDL_Rect tgt = {0,0,0,0};
+
+
+ /* Assuming that the filename is valid,open the font */
+ path_build(fontdir, PATH_MAX, ANGBAND_DIR_XTRA, "font");
+ path_build(filename, PATH_MAX, fontdir, fname);
+ font = TTF_OpenFont(filename,size);
+ if (font == NULL)
+ sdl_quit("Error loading that font!");
+ /* Set the font style to normal */
+ TTF_SetFontStyle(font,TTF_STYLE_NORMAL);
+
+ /* Collect some measurements on this font -
+ arbitrarily choose the letter 'a' to get width*/
+ TTF_GlyphMetrics(font,'a',&minx,&maxx,&miny,&maxy,&advance);
+ /* the width of each character tile */
+ t_width = advance;
+ /* the height of each character tile */
+ t_height = TTF_FontHeight(font);
+ /* position of the y=0 line in each tile */
+ midline = TTF_FontAscent(font);
+
+ /* now... render each of the individual characters */
+ for (i=0;i<128;i++)
+ {
+ /* make a pretty blended glyph */
+ temp_surf=TTF_RenderGlyph_Blended(font,i,base_color);
+ /* and make sure that we got it right! */
+ if (temp_surf == NULL)
+ sdl_quit("Glyph failed to render!");
+ /* get the metrics of this particular glyph so we can position it */
+ TTF_GlyphMetrics(font,i,&minx,&maxx,&miny,&maxy,&advance);
+ /* copy rendered glyph into text queue, at the right position*/
+ tgt.x = minx;
+ tgt.y = midline-maxy;
+ /* but first... we'll need a surface in the text queue to blit to! */
+ text[i] = createSurface(t_width,t_height);
+ /* turn OFF src-alpha... results in brute
+ copy of the RGBA contents of surf */
+ SDL_SetAlpha(temp_surf,0,0);
+ SDL_BlitSurface(temp_surf,NULL,text[i],&tgt);
+ /* turn OFF src-alpha since we'll be using worksurf for blitting */
+ SDL_SetAlpha(text[i],0,0);
+ /* kill the surface to patch up memory leaks */
+ SDL_FreeSurface(temp_surf);
+ }
+}
+
+#ifdef USE_ISO
+/*************************************************
+ ISO SUPPORT FUNCTIONS
+ *************************************************/
+/**
+ * inits operating system stuff
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_os_init(int n, int *parameter)
+{
+ // Hajo:
+ // unused in isov-x11
+ return TRUE;
+}
+
+
+/**
+ * opens graphics device/context/window of size w*h
+ * @param w width
+ * @param h height
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_os_open(int w, int h)
+{
+ const int left = 13;
+
+/* tex_width = (data[0].fd->dw * (data[0].t.wid - left + 2) + 3) & 0xFFFC;
+ tex_height = data[0].fd->dh * (data[0].t.hgt - 2);
+
+ tex_xoff = data[0].fd->dw * left;
+ tex_yoff = data[0].fd->dh * 1 + 1;
+*/
+
+ //tex_width = (data[0].size_w - (left - 2) * (data[0].size_w / data[0].cols) + 3) & 0xFFFC;
+ // this is too big (but works :-))
+ tex_width = data[0].size_w & 0xfffc;
+ tex_height = (data[0].size_h / data[0].rows) * (data[0].rows-2);
+
+ tex_xoff = (tex_width / data[0].cols) * left;
+ tex_yoff = (tex_height / data[0].rows) * 1 + 1;
+
+ return TRUE;
+}
+
+
+/**
+ * closes operating system stuff
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_os_close()
+{
+ // Hajo:
+ // unused in isov-x11
+ return TRUE;
+}
+
+
+/**
+ * retrieve display width
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_get_width()
+{
+ return data[0].size_w;
+}
+
+
+/**
+ * retrieve display height
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+int dr_get_height()
+{
+ return data[0].size_h;
+}
+
+
+/**
+ * this is used if we need to fake an 8 bit array
+ * @author Hj. Malthaner
+ */
+static unsigned short * data8;
+
+
+/**
+ * creates a (maybe virtual) array of graphics data
+ * @author Hj. Malthaner
+ */
+unsigned short * dr_textur_init()
+{
+ int i;
+
+ printf("isov-sdl::dr_textur_init()\n");
+ printf(" width = %d\n", data[0].size_w);
+ printf(" height = %d\n", data[0].size_h);
+
+ for (i = 0; i < (1 << 16); i++)
+ {
+ // FIXME!!!
+ // must consider color bits, or breaks in anything else but RGB 555
+ unsigned int R;
+ unsigned int G;
+ unsigned int B;
+
+ // RGB 555
+ R = (i & 0x7C00) >> 10;
+ G = (i & 0x03E0) >> 5;
+ B = (i & 0x001F) >> 0;
+
+
+ tab16[i] = SDL_MapRGB(screen->format, R << 3, G << 3, B << 3);
+ }
+
+
+
+ data8 = malloc((data[0].size_w) * (data[0].size_h) * 2);
+
+ printf(" textur = %p\n", data8);
+
+ // fake an 16 bit array and convert data before displaying
+ return data8;
+}
+
+static void flush_area(int dest_x, int dest_y,
+ int x, int y, int w, int h)
+{
+ SDL_Surface *face = screen;
+
+ if (SDL_LockSurface(face) == 0)
+ {
+ int i, j;
+ const int bpp = screen->format->BytesPerPixel;
+
+ for (j = 0; j < h; j++)
+ {
+ unsigned short * p = data8 + (y + j) * tex_width + x;
+ unsigned char * row = face->pixels + (dest_y + j) * face->pitch + dest_x * bpp;
+
+
+ for (i = 0; i < w; i++)
+ {
+ *((unsigned short*)row) = tab16[ *p++ ];
+ row += bpp;
+ }
+ }
+ SDL_UnlockSurface(face);
+ }
+}
+
+/**
+ * displays the array of graphics data
+ * @author Hj. Malthaner
+ */
+void dr_textur(int xp, int yp, int w, int h)
+{
+ int y;
+
+ // clipping unten
+ if (yp + h > tex_height)
+ {
+ h = tex_height - yp;
+ }
+
+ /* debug spots
+ for(y=0; y<24; y++) {
+ int x;
+
+ for(x=0; x<80; x++) {
+ if(spots[x][y]) {
+ printf("X");
+ } else {
+ printf(".");
+ }
+}
+ printf("\n");
+}
+ */
+
+ for (y = 0; y < SCREEN_HGT; y++)
+ {
+ const int left = 13;
+ const int y1 = y + 1;
+ int x = 0;
+
+ yp = data[0].size_h / data[0].rows * y;
+
+ spots[79][y1] = FALSE;
+
+ do
+ {
+ int n = 0;
+ while (x + n + left < 80 && !spots[x + n + left][y1])
+ {
+ n++;
+ }
+
+ xp = data[0].size_w / data[0].cols * x;
+
+
+ flush_area(tex_xoff + xp, tex_yoff + yp,
+ xp, yp,
+ data[0].size_w / data[0].cols*(n),
+ data[0].size_h / data[0].rows);
+
+ x += n;
+
+ while (x + left < 80 && spots[x + left][y1])
+ {
+ x++;
+ }
+ }
+ while (x + left < 80);
+ }
+}
+
+
+/**
+ * use this method to flush graphics pipeline (undrawn stuff) onscreen.
+ * @author Hj. Malthaner
+ */
+void dr_flush()
+{
+ // Iso-view for angband needs no sync.
+ // XSync(md,FALSE);
+}
+
+
+/**
+ * set colormap entries
+ * @author Hj. Malthaner
+ */
+void dr_setRGB8multi(int first, int count, unsigned char * data)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * display/hide mouse pointer
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void show_pointer(int yesno)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * move mouse pointer
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void move_pointer(int x, int y)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * update softpointer position
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void ex_ord_update_mx_my()
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * get events from the system
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void GetEvents()
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * get events from the system without waiting
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+void GetEventsNoWait()
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * @returns time since progrma start in milliseconds
+ * @author Hj. Malthaner
+ */
+long long dr_time(void)
+{
+ // Hajo:
+ // unused in isov-x11
+ return 0;
+}
+
+
+/**
+ * sleeps some microseconds
+ * @author Hj. Malthaner
+ */
+void dr_sleep(unsigned long usec)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+
+/**
+ * loads a sample
+ * @return a handle for that sample or -1 on failure
+ * @author Hj. Malthaner
+ */
+int dr_load_sample(const char *filename)
+{
+ // Hajo:
+ // unused in isov-x11
+ return TRUE;
+}
+
+
+/**
+ * plays a sample
+ * @param key the key for the sample to be played
+ * @author Hj. Malthaner
+ */
+void dr_play_sample(int key, int volume)
+{
+ // Hajo:
+ // unused in isov-x11
+}
+
+static unsigned char ** halloc(int w, int h)
+{
+ unsigned char **field = (unsigned char **)malloc(sizeof(unsigned char *) * h);
+ int i;
+
+ for (i = 0; i < h; i++)
+ {
+ field[i] = (unsigned char *)malloc(sizeof(unsigned char) * w);
+ memset(field[i], 32 , w);
+ }
+
+ return field;
+}
+
+/**
+ * spot array access procedure. Mark text output spots
+ * @author Hj. Malthaner (hansjoerg.malthaner@gmx.de)
+ */
+static void set_spots(const int x, const int y, const int n, const bool v)
+{
+ int i;
+
+ for (i = x; i < x + n; i++)
+ {
+ spots[i][y] = v;
+ }
+}
+
+
+/***********************************************/
+#endif /* USE_ISO */
+
+/***********************************************/
+
+/*** Function hooks needed by "Term" ***/
+
+static void Term_init_sdl(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+ DB("Term_init_sdl");
+ /* XXX XXX XXX */
+}
+
+static void Term_nuke_sdl(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+ DB("Term_nuke_sdl");
+ /* XXX XXX XXX */
+}
+
+static errr Term_user_sdl(int n)
+{
+ term_data *td = (term_data*)(Term->data);
+ DB("Term_user_sdl");
+ /* XXX XXX XXX */
+
+ /* Unknown */
+ return (1);
+}
+
+/* KEYPRESS_STRING repeatedly sends characters to the terminal
+XXX - should implement routine from maim-sdl.c, it's sooo much
+cleaner */
+#define KEYPRESS_STRING(str) \
+strcpy(buf,str); \
+n = buf; \
+while (*n != '\0') { \
+ Term_keypress((int)(*(n++))); \
+}
+
+/* function prototype */
+void manipulationMode(void);
+void redrawAllTerminals(void);
+/* This is the main event handling routine that will be called
+whenever an event is pulled off of the queue (in Term_xtra_sdl())*/
+void handleEvent(SDL_Event *event)
+{
+ static char buf[24]; /* a buffer used when passing key names */
+ char *n; /* and a pointer to manipulate this buffer */
+
+ switch( event->type )
+ {
+ case SDL_KEYDOWN:
+ {
+ /* handle key presses */
+
+ /* I'm reading that as long as the upper 9 bits of the unicode
+ * value are zero, then the lower 7 bits are direct ASCII characters.
+ * Furthermore, it seems that all basic keys return non-zero values
+ * for the lower 7 bits, but function keys and other various things
+ * return 0000000 for the lower 7 bits.
+ * Basically, if the lower 7 bits are zero, do something special
+ * (like start a macro), but otherwise just pass along the ASCII
+ * code!
+ */
+ byte ascii_part = event->key.keysym.unicode & 0x00ff;
+
+ /* gimme the key name */
+ printf("Key is: %s\n",SDL_GetKeyName(event->key.keysym.sym));
+
+ /* allow for full screen toggling! */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_ALT))
+ {
+ SDL_WM_ToggleFullScreen(screen);
+ /* toggle the internal full screen flag */
+ arg_full_screen = (arg_full_screen ? FALSE : TRUE);
+ }
+
+ /* entry into window manipulation mode */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_CTRL))
+ {
+ DB("Manipulation mode!");
+ manipulationMode();
+ }
+
+#ifdef USE_ISO
+ /* toggle tile size */
+ if (event->key.keysym.sym == SDLK_SCROLLOCK)
+ {
+ switch (display_get_tile_size())
+ {
+ case 32:
+ display_select_tile_size(0);
+ break;
+ case 64:
+ display_select_tile_size(1);
+ break;
+ default:
+ display_select_tile_size(0);
+ break;
+ }
+ reset_visuals();
+ strcpy(buf, "graf-iso.prf");
+ process_pref_file(buf);
+ refresh_display();
+ SDL_UpdateRect(screen, 0, 0, data[0].size_w, data[0].size_h);
+ }
+
+ /* cycle grid type none/objects+monsters only/full */
+ if ((event->key.keysym.sym == '#') && \
+ (SDL_GetModState() & KMOD_ALT))
+ {
+ set_grid(get_grid()+1);
+ refresh_display();
+ }
+
+#endif
+
+ /*printf("ascii_part: %d\n",ascii_part);*/
+ if (ascii_part)
+ {
+ /* We have now determined that the ASCII part is not '0', so
+ we can safely pass along the ASCII value! */
+ Term_keypress(ascii_part);
+ }
+ else
+ {
+ /* We want to ignore keypresses that are simply the modifier
+ keys*/
+ if (!( (event->key.keysym.sym == SDLK_RSHIFT) |
+ (event->key.keysym.sym == SDLK_LSHIFT) |
+ (event->key.keysym.sym == SDLK_RALT) |
+ (event->key.keysym.sym == SDLK_LALT) |
+ (event->key.keysym.sym == SDLK_RCTRL) |
+ (event->key.keysym.sym == SDLK_LCTRL) ))
+ {
+
+ /* now build a macro string using the modifiers together
+ with the key that was just pressed*/
+
+ /* As for the formatting...
+ * We pass the key press and modifiers as follows:
+ * \[ctrl-alt-shift-"key name"]
+ * following the previously established convention...
+ *
+ * All of the things that happen are defined in pref-sdl.prf
+ */
+
+ KEYPRESS_STRING("\["); /*Output the first part... */
+ /* See if a control key is down */
+ if (event->key.keysym.mod & KMOD_CTRL)
+ {
+ KEYPRESS_STRING("ctrl-");
+ }
+ /* See if an alt key is down */
+ if (event->key.keysym.mod & KMOD_ALT)
+ {
+ KEYPRESS_STRING("alt-");
+ }
+ /* See if a shift key is down */
+ if (event->key.keysym.mod & KMOD_SHIFT)
+ {
+ KEYPRESS_STRING("shift-");
+ }
+
+ /* Add in the name of whatever key was pressed */
+ KEYPRESS_STRING(SDL_GetKeyName(event->key.keysym.sym));
+
+ /* and end it... */
+ KEYPRESS_STRING("]");
+ }
+ }
+ break;
+ }
+ case SDL_QUIT:
+ {
+ /* handle quit requests */
+ DB("Emergency Blit");
+ redrawAllTerminals();
+ /*sdl_quit("Quitting!\n");*/
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+/* declare the screen clearing function used below */
+void eraseTerminal();
+void drawTermStuff(term_data *td, SDL_Rect *rect);
+static errr Term_xtra_sdl(int n, int v)
+{
+ static SDL_Event event;
+ term_data *td;
+
+
+ /* Analyze */
+ switch (n)
+ {
+ case TERM_XTRA_EVENT:
+ {
+ if (v)
+ {
+ /* Perform event checking with blocking */
+ SDL_WaitEvent( &event );
+ handleEvent( &event );
+ } else {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_FLUSH:
+ {
+ /* Keep doing events until the queue is empty! */
+ while (SDL_PollEvent(&event))
+ {
+ handleEvent(&event);
+ }
+ return (0);
+ }
+
+ case TERM_XTRA_CLEAR:
+ {
+ /* Clear the terminal */
+ DB("TERM_XTRA_CLEAR");
+ suspendUpdate = TRUE;
+ eraseTerminal();
+ return (0);
+ }
+
+ case TERM_XTRA_SHAPE:
+ {
+ /*
+ * Set the cursor visibility XXX XXX XXX
+ *
+ * This action should change the visibility of the cursor,
+ * if possible, to the requested value (0=off, 1=on)
+ *
+ * This action is optional, but can improve both the
+ * efficiency (and attractiveness) of the program.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FROSH:
+ {
+ /*
+ * Flush a row of output XXX XXX XXX
+ *
+ * This action should make sure that row "v" of the "output"
+ * to the window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FRESH" entry below takes care of any
+ * necessary flushing issues.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_FRESH:
+ {
+ /*
+ * Flush output XXX XXX XXX
+ *
+ * This action should make sure that all "output" to the
+ * window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FROSH" entry above takes care of any
+ * necessary flushing issues.
+ */
+
+#ifdef USE_ISO
+ // Hajo:
+ // refresh the graphical view
+
+ refresh_display();
+ SDL_UpdateRect(screen, 0, 0, data[0].size_w, data[0].size_h);
+// SDL_UpdateRect(td->face, 0, 0, 80*td->w, 24*td->h);
+#else /* regular SDL */
+ /* If terminal display has been held for any reason,
+ then update the whole thing now!*/
+ DB("TERM_XTRA_FRESH");
+ if (suspendUpdate)
+ {
+ DB(" update WAS suspended... updating now");
+ td = (term_data*)(Term->data);
+ suspendUpdate = FALSE;
+ drawTermStuff(td,NULL);
+ }
+#endif /* USE_ISO */
+ return (0);
+ }
+
+ case TERM_XTRA_NOISE:
+ {
+ /*
+ * Make a noise XXX XXX XXX
+ *
+ * This action should produce a "beep" noise.
+ *
+ * This action is optional, but convenient.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_SOUND:
+ {
+ /*
+ * Make a sound XXX XXX XXX
+ *
+ * This action should produce sound number "v", where the
+ * "name" of that sound is "sound_names[v]". This method
+ * is still under construction.
+ *
+ * This action is optional, and not very important.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_BORED:
+ {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_REACT:
+ {
+ /*
+ * React to global changes XXX XXX XXX
+ *
+ * For example, this action can be used to react to
+ * changes in the global "color_table[256][4]" array.
+ *
+ * This action is optional, but can be very useful for
+ * handling "color changes" and the "arg_sound" and/or
+ * "arg_graphics" options.
+ */
+#ifdef USE_ISO
+ strcpy(buf, "graf-iso.prf");
+ process_pref_file(buf);
+#endif /* USE_ISO */
+ return (1);
+ }
+
+ case TERM_XTRA_ALIVE:
+ {
+ /*
+ * Change the "hard" level XXX XXX XXX
+ *
+ * This action is used if the program changes "aliveness"
+ * by being either "suspended" (v=0) or "resumed" (v=1)
+ * This action is optional, unless the computer uses the
+ * same "physical screen" for multiple programs, in which
+ * case this action should clean up to let other programs
+ * use the screen, or resume from such a cleaned up state.
+ *
+ * This action is currently only used by "main-gcu.c",
+ * on UNIX machines, to allow proper "suspending".
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_LEVEL:
+ {
+ /*
+ * Change the "soft" level XXX XXX XXX
+ *
+ * This action is used when the term window changes "activation"
+ * either by becoming "inactive" (v=0) or "active" (v=1)
+ *
+ * This action can be used to do things like activate the proper
+ * font / drawing mode for the newly active term window. This
+ * action should NOT change which window has the "focus", which
+ * window is "raised", or anything like that.
+ *
+ * This action is optional if all the other things which depend
+ * on what term is active handle activation themself, or if only
+ * one "term_data" structure is supported by this file.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * Delay for some milliseconds XXX XXX XXX
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as breath attacks.
+ *
+ * This action is optional, but may be required by this file,
+ * especially if special "macro sequences" must be supported.
+ */
+
+ /* I think that this command is system independent... */
+ /*sleep(v/1000);*/
+ /* main-x11 uses usleep(1000*v); */
+ /* main-win uses Sleep(v); */
+ return (1);
+ }
+
+ case TERM_XTRA_GET_DELAY:
+ {
+ /*
+ * Get Delay of some milliseconds XXX XXX XXX
+ * place the result in Term_xtra_long
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as recording cmovies.
+ *
+ * This action is optional, but cmovies wont perform
+ * good without it
+ */
+
+ return (1);
+ }
+ }
+
+ /* Unknown or Unhandled action */
+ return (1);
+}
+
+/*************************************************
+ GRAPHICS ROUTINES
+ *************************************************/
+
+/* wrapper routine for creating an RGB surface with given height and
+ width that corresponds to desired color depth and respects the system
+ pixel format */
+SDL_Surface *createSurface(int width, int height)
+{
+ SDL_Surface *surf;
+ int surface_type;
+
+ if (videoInfo->hw_available)
+ surface_type = SDL_HWSURFACE;
+ else
+ surface_type = SDL_SWSURFACE;
+
+ /* XXX need to make RGBA masks correspond to system pixel format! */
+ switch (arg_bpp)
+ {
+ case 8:
+ {
+ /* I really don't know if 8 bpp is even possible, but here it is */
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,8,0xc0,0x30,0x0c,0x03);
+ break;
+ }
+ case 16:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,16,0xf000,0x0f00,0x00f0,0x000f);
+ break;
+ }
+ case 24:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,24,0xfc0000,0x03f000,0x000fc0,0x000030);
+ break;
+ }
+ case 32:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,32,0xff000000,0x00ff0000,0x0000ff00,0x000000ff);
+ break;
+ }
+ default:
+ {
+ surf = NULL;
+ break;
+ }
+ }
+
+ if (surf == NULL)
+ sdl_quit("Bad Surface Creation!");
+
+ return surf;
+}
+
+/* Take a rectangle in terminal coordinates and then transform it into
+screen coordinates; td is the term_data that this rect belongs to */
+void term_to_screen(SDL_Rect *termrect, term_data *td)
+{
+ termrect->x += td->rect.x;
+ termrect->y += td->rect.y;
+}
+
+/* Do the opposite, take a rectangle in screen coordinates and transform
+it into the terminal coordinates of the given term_data */
+void screen_to_term(SDL_Rect *scrrect, term_data *td)
+{
+ scrrect->x -= td->rect.x;
+ scrrect->y -= td->rect.y;
+}
+
+/* A macro that determines if the 'top' rectangle completely occludes the
+'bottom' rectangle */
+#define BLOCKS(top,bottom) \
+( (top.x <= bottom.x) & ((top.x+top.w)>=(bottom.x+bottom.w)) & \
+ (top.y <= bottom.y) & ((top.y+top.h)>=(bottom.y+bottom.h)) )
+
+#define INTERSECT(r1,r2) \
+!( ( (r1.x > (r2.x+r2.w)) | (r2.x > (r1.x+r1.w)) ) & \
+ ( (r1.y > (r2.y+r2.h)) | (r2.y > (r1.y+r1.h)) ) )
+
+/* A function to calculate the intersection of two rectangles. Takes base
+rectangle and then updates it to include only the rectangles that intersect
+with the test rectangle. If there is an intersection, the function returns
+TRUE and base now contains the intersecting rectangle. If there is no
+intersection, then the function returns FALSE */
+bool intersectRects(SDL_Rect *base, SDL_Rect *test)
+{
+ if (INTERSECT((*base),(*test)))
+ {
+ /* Scoot the x-coordinates for the left side*/
+ if ( test->x > base->x )
+ {
+ base->w -= test->x - base->x;
+ base->x = test->x;
+ }
+ /* Scoot the x-coordinates for the right side*/
+ if ( (test->x + test->w) < (base->x + base->w) )
+ {
+ base->w = test->x + test->w - base->x;
+ }
+ /* Scoot the upper y-coordinates */
+ if ( test->y > base->y )
+ {
+ base->h -= test->y - base->y;
+ base->y = test->y;
+ }
+ /* Scoot the lower y-coordinates */
+ if ( (test->y + test->h) < (base->y + base->h) )
+ {
+ base->h = test->y + test->h - base->y;
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* A function to calculate the join of two rectangles; the first argument is
+changed to the joined rectangle */
+SDL_Rect joinRects(SDL_Rect *r1, SDL_Rect *r2)
+{
+ SDL_Rect out = {0,0,0,0};
+
+ if ( (r1 != NULL) & (r2 != NULL) )
+ {
+ /* Lower x-coordinate */
+ if ( r2->x < r1->x )
+ out.x = r2->x;
+ else
+ out.x = r1->x;
+ /* Upper x-coordinate */
+ if ( (r2->x+r2->w) > (r1->x+r1->w) )
+ out.w = (r2->x+r2->w) - out.x;
+ else
+ out.w = (r1->x+r1->w) - out.x;
+ /* Lower y-coordinate */
+ if ( r2->y < r1->y )
+ out.y = r2->y;
+ else
+ out.y = r1->y;
+ if ( (r2->y+r2->h) > (r1->y+r1->h) )
+ out.h = (r2->y+r2->h) - out.y;
+ else
+ out.h = (r1->y+r1->h) - out.y;
+ }
+ return out;
+}
+
+/* Given a term_data (and its associated screen) and a rectangle in terminal
+coordinates (with NULL signifying to take the whole terminal surface), blit
+graphics from the term_data surface to the screen, using the term_data's rect
+to indicate how terminal coordinates transform into screen coordinates.
+This is complicated, however, by the possibility that the indicated area may be
+occluded by overlaying terminals. In this case, if the target area is
+completely occluded, nothing will be done. If partially occluded, it will be
+drawn, but occluding terminals will then re-blit to re-cover the area. */
+void drawTermStuff(term_data *td, SDL_Rect *rect)
+{
+ int n = 0, i;
+ bool block = FALSE, cover = FALSE;
+ SDL_Rect spot, isect_term, isect_scr;
+
+ /* first of all, if updating is suspended, do nothing! */
+ if (!suspendUpdate)
+ {
+ /* find out which number in the ordered stack of screens that this
+ terminal is */
+ while ((term_order[n] != td) & (n < MAX_CONSOLE_COUNT))
+ {
+ n++;
+ }
+ if (n == MAX_CONSOLE_COUNT)
+ printf("Could not find terminal in display list...\n");
+ /* now loop through and see if any terminals completely occlude
+ the desired spot; if num=0, note that this will be skipped */
+ if (rect == NULL)
+ {
+ /* Grab the whole terminal screen */
+ spot.x = 0; spot.y = 0;
+ spot.w = td->surf->w; spot.h = td->surf->h;
+ }
+ else
+ {
+ /* Just copy the given area */
+ spot.x = rect->x; spot.y = rect->y;
+ spot.w = rect->w; spot.h = rect->h;
+ }
+ term_to_screen(&spot,td);
+ i = n;
+ while (i--)
+ {
+ if (BLOCKS(term_order[i]->rect,spot))
+ {
+ /* Higher terminal completely occludes this spot */
+ block = TRUE;
+ DB(" Blocks!");
+ }
+ else if (INTERSECT(term_order[i]->rect,spot))
+ {
+ /* Partial occlusion */
+ cover = TRUE;
+ DB(" Covers!");
+ }
+ }
+ /* If any of the higher terminals blocked, then don't do
+ anything */
+ if (!block)
+ {
+ /*printf("Blitting to %d %d %d %d\n",spot.x,spot.y,spot.w,spot.h);*/
+ /* First of all, draw the graphics */
+ SDL_BlitSurface(td->surf,rect,screen,&spot);
+ if (cover)
+ {
+ printf("covering...");
+ /* There are covering terminals, so go through and blit all
+ partially occluding ones */
+ while (n--)
+ {
+ /* copy spot to find the intersect */
+ isect_scr.x = spot.x; isect_scr.y = spot.y;
+ isect_scr.w = spot.w; isect_scr.h = spot.h;
+ if (intersectRects(&isect_scr,&(term_order[n]->rect)))
+ {
+ /* this terminal intersects... re-blit */
+ /* first, convert to term coordinates */
+ isect_term.x = isect_scr.x; isect_term.y = isect_scr.y;
+ isect_term.w = isect_scr.w; isect_term.h = isect_scr.h;
+ screen_to_term(&isect_term,term_order[n]);
+ /* blit from term coordinates to screen coordinates */
+ SDL_BlitSurface(term_order[n]->surf,&isect_term,\
+ screen,&isect_scr);
+ }
+ }
+ }
+ /* Now update what was drawn */
+ DB("Update");
+ SDL_UpdateRects(screen,1,&spot);
+ }
+ }
+}
+
+/* utility routine for creating and setting the color of the cursor;
+it could be useful for setting a new cursor color if desired.
+Could later be expanded to do other stuff with the cursor,
+like a hollow rectangle a la main-win.c or even a graphic */
+void createCursor(byte r, byte g, byte b, byte a)
+{
+ /* free the cursor if it exists */
+ if (cursor != NULL)
+ SDL_FreeSurface(cursor);
+
+ /* and create it anew! (or the first time) */
+ cursor = createSurface(t_width,t_height);
+
+ /* be sure to use alpha channel when blitting! */
+ SDL_SetAlpha(cursor,SDL_SRCALPHA,0);
+
+ /* just set the color for now - drawing rectangles
+ needs surface locking for some setups */
+ cursor_color = SDL_MapRGBA(cursor->format,r,g,b,a);
+ SDL_LOCK(cursor);
+ SDL_FillRect(cursor,NULL,cursor_color);
+ SDL_UNLOCK(cursor);
+}
+
+/* Cursor Display routine - just blits the global cursor
+surface onto the correct location */
+static errr Term_curs_sdl(int x, int y)
+{
+#ifdef USE_ISO
+ highlite_spot(x, y);
+#else /* regular SDL */
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+
+ /* calculate the position to place the cursor */
+ base.x = td->surf->clip_rect.x + x*t_width;
+ base.y = td->surf->clip_rect.y + y*t_height;
+ base.w = t_width;
+ base.h = t_height;
+
+ /* blit the cursor over top of the given spot;
+ note that surface should not be locked
+ (see note in Term_text_sdl() below) */
+ SDL_BlitSurface(cursor,NULL,td->surf,&base);
+
+ /* Now draw to the main screen */
+ drawTermStuff(td,&base);
+#endif /* USE_ISO */
+ /* Success */
+ return (0);
+}
+
+/* routine for wiping terminal locations - simply draws
+a black rectangle over the offending spots! */
+static errr Term_wipe_sdl(int x, int y, int n)
+{
+ static SDL_Rect base;
+ term_data *td = (term_data*)(Term->data);
+
+ /* calculate boundaries of the area to clear */
+ base.x = td->surf->clip_rect.x + x*t_width;
+ base.y = td->surf->clip_rect.y + y*t_height;
+ base.w = n*t_width;
+ base.h = t_height;
+
+ SDL_LOCK(td->surf);
+
+ /* blank the screen area */
+ SDL_FillRect(td->surf, &base, td->black);
+
+ SDL_UNLOCK(td->surf);
+
+ /* And... UPDATE the rectangle we just wrote to! */
+ drawTermStuff(td,&base);
+
+ /* Success */
+ return (0);
+}
+
+/* Perform a full clear of active terminal; redraw the borders.*/
+void eraseTerminal(void)
+{
+ static SDL_Rect base;
+ term_data *td = (term_data*)(Term->data);
+
+ /* temporarily remove clipping rectangle */
+ SDL_SetClipRect(td->surf,NULL);
+
+ SDL_LOCK(td->surf);
+ /* flood terminal with border color */
+ SDL_FillRect(td->surf,NULL,td->border_color);
+
+ /* get smaller rectangle to hollow out window */
+ base.x = td->border_thick;
+ base.y = td->border_thick;
+ base.w = td->rect.w - 2*td->border_thick;
+ base.h = td->rect.h - 2*td->border_thick;
+
+ /* hollow out terminal */
+ SDL_FillRect(td->surf,&base,td->black);
+
+ SDL_UNLOCK(screen);
+
+ /* reset clipping rectangle */
+ base.x += td->cushion_x_top;
+ base.y += td->cushion_y_top;
+ base.w -= td->cushion_x_top + td->cushion_x_bot;
+ base.h -= td->cushion_y_top + td->cushion_y_bot;
+ SDL_SetClipRect(td->surf,&base);
+ printf("Clip rect: %d %d %d %d\n",base.x,base.y,base.w,base.h);
+ /* And... UPDATE the whole thing */
+ drawTermStuff(td,NULL);
+
+}
+
+/*
+ * Draw some text on the screen
+ *
+ * This function should actually display an array of characters
+ * starting at the given location, using the given "attribute",
+ * and using the given string of characters, which contains
+ * exactly "n" characters and which is NOT null-terminated.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You must be sure that the string, when written, erases anything
+ * (including any visual cursor) that used to be where the text is
+ * drawn. On many machines this happens automatically, on others,
+ * you must first call "Term_wipe_xxx()" to clear the area.
+ *
+ * In color environments, you should activate the color contained
+ * in "color_data[a & 0x0F]", if needed, before drawing anything.
+ *
+ * You may ignore the "attribute" if you are only supporting a
+ * monochrome environment, since this routine is normally never
+ * called to display "black" (invisible) text, including the
+ * default "spaces", and all other colors should be drawn in
+ * the "normal" color in a monochrome environment.
+ *
+ * Note that if you have changed the "attr_blank" to something
+ * which is not black, then this function must be able to draw
+ * the resulting "blank" correctly.
+ *
+ * Note that this function must correctly handle "black" text if
+ * the "always_text" flag is set, if this flag is not set, all the
+ * "black" text will be handled by the "Term_wipe_xxx()" hook.
+ */
+static errr Term_text_sdl(int x, int y, int n, byte a, const char *cp)
+{
+#ifdef USE_ISO
+ if (a < 16)
+ {
+ set_spots(x, y, n, TRUE);
+ }
+ else
+ {
+ set_spots(x, y, n, FALSE);
+ }
+#else
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+ SDL_Rect base_back;
+ int i = n;
+ char old = 0;
+
+ /* calculate place to clear off and draw to */
+ base.x = td->surf->clip_rect.x + x*td->tile_width;
+ base.y = td->surf->clip_rect.y + y*td->tile_height;
+ base.w = n*td->tile_width;
+ base.h = td->tile_height;
+
+ base_back = base;
+
+ SDL_LOCK(screen);
+
+ /* blank the drawing area */
+ SDL_FillRect(td->surf, &base, td->black);
+
+ SDL_UNLOCK(screen);
+
+ /* Note that SDL docs specify that SDL_BlitSurface should not be called
+ on locked surfaces... since the character printing routine below revolves
+ around blitting, the surface has been unlocked first*/
+
+ /* loop through the input string, drawing characters */
+ i = n;
+ old = 0;
+ while (i--)
+ {
+ /* Output the character... */
+ /* If character has not changed, then just blit the old surface into
+ the new location to save effort*/
+ if (*cp == old)
+ {
+ /* the desired character/color combo is already on the work surf */
+ /* just blit it! */
+ SDL_BlitSurface(worksurf,NULL,td->surf,&base);
+ } else {
+ /* copy the desired character onto working surface */
+ SDL_BlitSurface(text[*cp],NULL,worksurf,NULL);
+ /* color our crayon surface with the desired color */
+ SDL_FillRect(crayon,NULL,color_data[a&0x0f]);
+ /* apply the color to the character on the working surface */
+ SDL_BlitSurface(crayon,NULL,worksurf,NULL);
+ /* and blit it onto our screen! */
+ SDL_BlitSurface(worksurf,NULL,td->surf,&base);
+ }
+ /* Move to the next position */
+ base.x += t_width;
+ /* Store the old character */
+ old = *cp;
+ /* Increment the character pointer */
+ cp++;
+ }
+
+ /* And update */
+ drawTermStuff(td,&base_back);
+
+#endif /* USE_ISO */
+ /* Success */
+ return (0);
+}
+
+/*
+ * Draw some attr/char pairs on the screen
+ *
+ * This routine should display the given "n" attr/char pairs at
+ * the given location (x,y). This function is only used if one
+ * of the flags "always_pict" or "higher_pict" is defined.
+ *
+ * You must be sure that the attr/char pairs, when displayed, will
+ * erase anything (including any visual cursor) that used to be at
+ * the given location. On many machines this is automatic, but on
+ * others, you must first call "Term_wipe_xxx(x, y, 1)".
+ *
+ * With the "higher_pict" flag, this function can be used to allow
+ * the display of "pseudo-graphic" pictures, for example, by using
+ * the attr/char pair as an encoded index into a pixmap of special
+ * "pictures".
+ *
+ * With the "always_pict" flag, this function can be used to force
+ * every attr/char pair to be drawn by this function, which can be
+ * very useful if this file can optimize its own display calls.
+ *
+ * This function is often associated with the "arg_graphics" flag.
+ *
+ * This function is only used if one of the "higher_pict" and/or
+ * "always_pict" flags are set.
+ */
+#ifndef USE_ISO
+static errr Term_pict_sdl(int x, int y, int n, const byte *ap, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+ DB("Term_pict_sdl");
+ /* XXX XXX XXX */
+
+#else
+// for ISO-view we need USE_TRANSPARENCY and USE_EGO_GRAPHICS defined
+static errr Term_pict_sdl(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+{
+ /* Hajo: memorize output */
+ memcpy(&iso_ap[y][x], ap, n);
+ memcpy(&iso_cp[y][x], cp, n);
+ memcpy(&iso_atp[y][x], tap, n);
+ memcpy(&iso_ctp[y][x], tcp, n);
+ memcpy(&iso_aep[y][x], eap, n);
+ memcpy(&iso_cep[y][x], ecp, n);
+
+ // here is no text
+ set_spots(x, y, n, FALSE);
+
+#endif /* USE_ISO */
+ /* Success */
+ return (0);
+}
+
+/*************************************************
+ SPECIAL TERMINAL WINDOW MANIPULATION ROUTINES
+ *************************************************/
+
+/* macro for bounding a value between two given values */
+#define BOUND(val,low,high) \
+if (val < low) \
+{ \
+ val = low; \
+} \
+else if (val > high) \
+{ \
+ val = high; \
+}
+
+/* two macros to get the adjusted maximums for window
+positions... eg the screen width minus the width of the
+window is the maximum x-position that the window can
+be set at. */
+#define MAX_X(td) \
+( arg_width - td->rect.w )
+
+#define MAX_Y(td) \
+( arg_height - td->rect.h )
+
+/* another two macros that give maximum window widths
+based on screen size and current window position together
+width tile widths/heights */
+#define MAX_WIDTH(td) \
+( (int)floorf((arg_width - td->rect.x - 2*td->border_thick - \
+ td->cushion_x_bot - td->cushion_x_top )/td->tile_width))
+
+#define MAX_HEIGHT(td) \
+( (int)floorf((arg_height - td->rect.y - 2*td->border_thick - \
+ td->cushion_y_bot - td->cushion_y_top )/td->tile_height))
+
+/* update the width and height of given term's rectangle by simply
+multiplying the tile count by tile size and adding in cushions and
+borders */
+#define UPDATE_SIZE(td) \
+ td->rect.w = (td->cols)*td->tile_width + td->cushion_x_top \
+ + td->cushion_x_bot + 2*td->border_thick; \
+ td->rect.h = (td->rows)*td->tile_height + td->cushion_y_top \
+ + td->cushion_y_bot + 2*td->border_thick
+
+void recompose(void);
+
+/* Resize the active terminal with new width and height.
+Note that his involves a complicated sequence of events...
+Details to follow below! */
+void resizeTerminal(int width, int height)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* First of all, bound the input width and height to satisfy
+ these conditions:
+ - The main ToME window should be at least 80 cols, 24 rows
+ - no part of each window should be drawn off screen....
+ I'm including borders in this restriction!
+ But no bounds checking needs to take place if the input width
+ and height are unchanged....
+ */
+
+ if (td == &data[0])
+ {
+ /* The active terminal is the main ToME window...
+ don't let the width get below 80, don't let the heights below
+ 24, and don't let it leak off of the edge! */
+ if (width != td->cols)
+ {
+ BOUND(width,80,MAX_WIDTH(td));
+ }
+ if (height != td->rows)
+ {
+ BOUND(height,24,MAX_HEIGHT(td));
+ }
+ }
+ else
+ {
+ /* This is not the main window... just make sure it
+ doesn't shrink to nothing or go past the edge */
+ if (width != td->cols)
+ {
+ BOUND(width,1,MAX_WIDTH(td));
+ }
+ if (height != td->rows)
+ {
+ BOUND(height,1,MAX_HEIGHT(td));
+ }
+ }
+
+ /* Okay, now make sure that something has ACTUALLY changed
+ before doing anything */
+ if ((width != td->cols) || (height != td->rows))
+ {
+
+ /* Now, ask zterm to please resize the term structure! */
+ Term_resize(width,height);
+
+ /* Reactivate, since Term_resize seems to activate the
+ main window again...*/
+ Term_activate(&td->t);
+
+ /* It might not have resized completely to the new
+ size we wanted (some windows have size limits it seems,
+ like the message window). So, update our structure with
+ the size that were actually obtained.*/
+ td->cols = Term->wid;
+ td->rows = Term->hgt;
+
+ /* And recalculate the sizes */
+ UPDATE_SIZE(td);
+
+ /* Create a new surface that can hold the updated size */
+ SDL_FreeSurface(td->surf);
+ td->surf = createSurface(td->rect.w,td->rect.h);
+
+ /* Now we should be in business for a complete redraw! */
+ Term_redraw();
+
+ /* Re-blit everything so it looks good */
+ recompose();
+
+ /* That's it! */
+ }
+}
+
+/* Move the terminal around... a much simpler action that involves
+just changing the pos_x/pos_y values and redrawing!*/
+void moveTerminal(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Now, the window is being shifted about... much simpler
+ situation to handle! But of course, the window must not
+ drift too far or else parts will be hanging off the screen
+ and may lead to errors - bound the input positions to
+ prevent this unfortunate situation... do nothing if the
+ input is no different than the current */
+ if (x != td->rect.x)
+ {
+ BOUND(x,0,MAX_X(td));
+ }
+ if (y != td->rect.y)
+ {
+ BOUND(y,0,MAX_Y(td));
+ }
+
+ /* Okay, now make sure that something changed before doing
+ anything */
+ if ((x != td->rect.x) || (y != td->rect.y))
+ {
+ /* Now update OUR structure */
+ td->rect.x = x;
+ td->rect.y = y;
+
+ /* Then do a reblit to see the results */
+ recompose();
+
+ /* That's it! */
+ }
+}
+
+/* Routine to bring a given term_data to the top of the drawing stack */
+void bringToTop(int current)
+{
+ term_data *td;
+ term_data *tc;
+ int n = 0;
+ int i;
+
+ /* Get the pointer to the desired term_data from the data structure */
+ td = &data[current];
+
+ printf("Current stack: \n");
+ for (i=0;i<arg_console_count;i++)
+ {
+ printf(" %d: %p\n",i,term_order[i]);
+ }
+ printf("\n");
+
+ /* Find the number in the term_order stack */
+ while ((term_order[n] != td) & (n < MAX_CONSOLE_COUNT))
+ {
+ n++;
+ }
+ if (n == MAX_CONSOLE_COUNT)
+ printf("Could not find terminal in display list...\n");
+
+ printf("Order is %d\n",n);
+
+ /* Now move all lower-indexed pointers up one index */
+ while (n)
+ {
+ printf(" move %d to %d\n",n-1,n);
+ printf(" %p\n",term_order[n-1]);
+ printf(" %p\n",term_order[n]);
+ term_order[n] = term_order[n-1];
+ n--;
+ }
+ /* And stick this term_data pointer on top */
+ term_order[0] = td;
+
+ printf("Final stack: \n");
+ for (i=0;i<arg_console_count;i++)
+ {
+ printf(" %d: %p\n",i,term_order[i]);
+ }
+ printf("\n");
+
+}
+
+/* This utility routine will cycle the active term to the
+next available in the data[] array. It will then do a
+redraw of this term so that it is ready to be manipulated.
+The input is the current active terminal, and the output is
+the new active terminal */
+int cycleTerminal(int current)
+{
+ /* redraw the current term to get rid of its purple
+ border */
+ data[current].border_color = data[current].white;
+ Term_redraw();
+
+ /* increment the terminal number*/
+ current++;
+ /* now do a little modulo cycle action and
+ activate the next term! */
+ current %= arg_console_count;
+ Term_activate(&(data[current].t));
+
+ /* before redrawing, set the border color to purple to
+ indicate that this terminal is being manipulated*/
+ data[current].border_color = data[current].purple;
+
+ /* then bring this terminal to the top of the order, so it is drawn on
+ top during manipulation mode */
+ bringToTop(current);
+
+ /* and do a complete redraw */
+ Term_redraw();
+
+ /* return the current terminal... */
+ return current;
+}
+
+/* This routine will simply re-blit all of the surfaces onto the main screen,
+respecting the current term_order */
+void recompose(void)
+{
+ int i = arg_console_count;
+ /* do a complete screen wipe */
+ SDL_LOCK(screen);
+ SDL_FillRect(screen,NULL,screen_black);
+ SDL_UNLOCK(screen);
+
+ /* cycle through the term_order */
+ while (i--)
+ {
+ SDL_BlitSurface(term_order[i]->surf,NULL,screen,&(term_order[i]->rect));
+ }
+
+ /* Update everything */
+ SDL_REDRAW_SCREEN;
+}
+
+/* This utility routine will completely blank the screen and
+then cycle through all terminals, performing a Term_redraw()
+on each and every term that is being used (according to
+arg_term_count that is). The terminals will be redrawn
+last-to-first, so that the main is over top of everything */
+void redrawAllTerminals(void)
+{
+ int i = arg_console_count;
+ DB("Total redraw");
+ /* do a complete screen wipe */
+ SDL_LOCK(screen);
+ SDL_FillRect(screen,NULL,screen_black);
+ SDL_UNLOCK(screen);
+
+ while (i--)
+ {
+ /* Re-order the terminals */
+ term_order[i] = &data[i];
+ }
+
+ i = arg_console_count;
+ /* cycle down through each terminal */
+ while (i--)
+ {
+ /* Activate this terminal */
+ Term_activate(&(data[i].t));
+
+ /* Make its border white since manipulation mode is over */
+ data[i].border_color = data[i].white;
+
+ /* And redraw it */
+ Term_redraw();
+ }
+ /* Loop will end on i=0 ! */
+
+ printf("Current stack: \n");
+ for (i=0;i<arg_console_count;i++)
+ {
+ printf(" %d: %p\n",i,term_order[i]);
+ }
+ printf("\n");
+
+ /* now update the screen completely, just in case*/
+ SDL_REDRAW_SCREEN;
+}
+
+/* This is the special event handling function for doing
+terminal window manipulation! When special manipulation
+mode is activated, execution goes here. This special mode
+has its own keypresses. To begin with, the main terminal
+border is highlighted (in purple) to indicate that it is
+being manipulated. The following keypresses are accepted:
+-Space: switches between editing modes. The system begins
+ in position editing mode, and Enter will toggle size
+ (row/col) editing mode.
+-Arrows: increments/decrements the position/size in an
+ intuitive way! ;) Some modifiers are accepted in order
+ to speed things up on very high resolution screens:
+ . with shift down, increment is five
+ . with ctrl down, increment is ten
+ . with both, increment is fifty!
+ Of course, no movement or resize can cause the window
+ to leave the confines of the screen, so using the big
+ jump is safe.
+-Enter: cycles to the next available terminal to edit.
+-Escape: quits manipulation mode, performing one final
+ redraw to take into account all changes.
+*/
+void manipulationMode(void)
+{
+ term_data *td;
+ SDL_Event event;
+ bool done = FALSE, moveMode = TRUE;
+ int mouse_x, mouse_y;
+ int value = 0, delta_x = 0, delta_y = 0;
+ int current_term;
+ SDL_Surface backup;
+
+ /* Begin by redrawing the main terminal with its
+ purple border to signify that it is being edited*/
+
+ /* start with the main terminal */
+ current_term = 0;
+
+ /* get the pointer */
+ td = &data[0];
+
+ /* before redrawing, set the border color to purple to
+ indicate that this terminal is being manipulated*/
+ td->border_color = td->purple;
+
+ /* and do a complete redraw */
+ DB("Term_redraw");
+ Term_redraw();
+
+ /* Now keep looping until Esc has been pressed. */
+ while (!done)
+ {
+ /* Get the keypress event */
+ SDL_WaitEvent(&event);
+ /* Make sure that it is a keypress */
+ if (event.type == SDL_KEYDOWN)
+ {
+ /* Act on the keypress! */
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_ESCAPE:
+ {
+ /* Escape has been pressed, so we're done!*/
+ done = TRUE;
+ break;
+ }
+ case SDLK_SPACE:
+ {
+ /* Space has been pressed: toggle move mode */
+ moveMode = ( moveMode ? FALSE : TRUE );
+ break;
+ }
+ case SDLK_RETURN:
+ {
+ /* Return... cycle the terminals!
+ update the current_term appropriately*/
+ current_term = cycleTerminal(current_term);
+
+ /* Get the new term_data */
+ td = &data[current_term];
+
+ break;
+ }
+ case SDLK_RIGHT:
+ case SDLK_LEFT:
+ case SDLK_DOWN:
+ case SDLK_UP:
+ {
+ /* Increase either the x-position or column
+ width - multiply according to modifiers */
+ value = 1;
+ if (SDL_GetModState() & KMOD_SHIFT)
+ {
+ /* shift is down... a muliplier of 5 */
+ value *= 5;
+ }
+ if (SDL_GetModState() & KMOD_CTRL)
+ {
+ /* control is down... multiply by 10 */
+ value *= 10;
+ }
+
+ /* Now, behavior depends on which key was pressed
+ and whether we are in moveMode resize mode... */
+
+ /* First, set the delta_x/y based on key */
+ if (event.key.keysym.sym == SDLK_RIGHT)
+ {
+ delta_x = 1;
+ delta_y = 0;
+ }
+ if (event.key.keysym.sym == SDLK_LEFT)
+ {
+ delta_x = -1;
+ delta_y = 0;
+ }
+ if (event.key.keysym.sym == SDLK_DOWN)
+ {
+ delta_x = 0;
+ delta_y = 1;
+ }
+ if (event.key.keysym.sym == SDLK_UP)
+ {
+ delta_x = 0;
+ delta_y = -1;
+ }
+
+ /* Now either moveTerminal() or
+ resizeTerminal() based on value of
+ moveMode! */
+ if (moveMode)
+ {
+ moveTerminal(td->rect.x + value*delta_x,\
+ td->rect.y + value*delta_y);
+ }
+ else
+ {
+ resizeTerminal(td->cols + value*delta_x,\
+ td->rows + value*delta_y);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if (event.type == SDL_MOUSEBUTTONDOWN)
+ {
+ /* Store the coordinates where the button was pressed */
+ mouse_x = event.button.x;
+ mouse_y = event.button.y;
+ }
+ else if (event.type == SDL_MOUSEMOTION)
+ {
+ /* Mouse is moving... maybe move or resize the window, based
+ on the state of the mouse buttons */
+
+ /* To keep the motion quick, temporarily ignore all mouse motion
+ events until window moving is complete */
+ SDL_EventState(SDL_MOUSEMOTION,SDL_IGNORE);
+
+ if(event.motion.state & SDL_BUTTON(1))
+ {
+ /* the left mouse button is down, move the window,
+ do a differential based on where the button was pressed */
+ moveTerminal(td->rect.x + (event.motion.x - mouse_x), \
+ td->rect.y + (event.motion.y - mouse_y));
+ /* save the most current mouse location */
+ SDL_GetMouseState(&mouse_x,&mouse_y);
+ }
+
+ if(event.motion.state & SDL_BUTTON(3))
+ {
+ /* the right mouse button is down, so resize the window;
+ do a differential, but divide the number by the tile sizes */
+
+ /* see if at least one whole tile width/height has been
+ reached */
+ int delta_cols, delta_rows;
+ delta_cols = (int)floorf(\
+ (float)(event.motion.x - mouse_x)/td->tile_width);
+ delta_rows = (int)floorf(\
+ (float)(event.motion.y - mouse_y)/td->tile_height);
+ if ( delta_cols || delta_rows )
+ {
+ /* something changed, so update */
+ resizeTerminal(td->cols + delta_cols, \
+ td->rows + delta_rows);
+ /* save the most current mouse location */
+ SDL_GetMouseState(&mouse_x,&mouse_y);
+ }
+ }
+
+ /* Deal with mouse motion again */
+ SDL_EventState(SDL_MOUSEMOTION,SDL_ENABLE);
+
+ }
+ }
+ /* Perform the last redraw to take all changes
+ into account */
+ redrawAllTerminals();
+}
+
+/*************************************************
+ INITIALIZATION ROUTINES
+ *************************************************/
+
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &(td->t);
+ char env_var[80];
+ cptr val;
+
+
+ /***** load position, size information */
+
+ int cols, rows, x, y;
+
+ /* grab the column and row counts from
+ environmental variables for now */
+ sprintf(env_var,"TOME_NUM_COLS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ cols = atoi(val);
+ /* now make sure that the main window will
+ have at least 80x24 */
+ if (td == &data[0])
+ {
+ /* can't really pick an upper bound without
+ knowing what the position is... oh well. */
+ BOUND(cols,80,255);
+ }
+ }
+ else
+ {
+ /* no environmental variable... have to guess
+ something. If it's the main window, choose
+ the minimum. */
+ if (td == &data[0])
+ cols = 80;
+ else
+ cols = 5;
+ }
+ /* do the rows */
+ sprintf(env_var,"TOME_NUM_ROWS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ rows = atoi(val);
+ /* now make sure that the main window will
+ have at least 80x24 */
+ if (td == &data[0])
+ {
+ /* can't really pick an upper bound without
+ knowing what the position is... oh well. */
+ BOUND(rows,24,128);
+ }
+ }
+ else
+ {
+ /* no environmental variable... have to guess
+ something. If it's the main window, choose
+ the minimum. */
+ if (td == &data[0])
+ rows = 24;
+ else
+ rows = 3;
+ }
+ /* store these values in the term_data structure */
+ td->rows = rows;
+ td->cols = cols;
+
+ /* the position will be loaded from environmental
+ variables as well - for the time being*/
+ /* x-location */
+ sprintf(env_var,"TOME_X_POS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ x = atoi(val);
+ /* now do intelligent position checking */
+ BOUND(x,0,MAX_X(td));
+ }
+ else
+ {
+ /* no variable, choose something */
+ x = 20*i;
+ }
+ /* y-location */
+ sprintf(env_var,"TOME_Y_POS_%d",i);
+ val = getenv(env_var);
+ /* make sure it is valid */
+ if (val != NULL)
+ {
+ y = atoi(val);
+ /* position checking again */
+ BOUND(y,0,MAX_Y(td));
+ }
+ else
+ {
+ /* no variable */
+ y = 20*i;
+ }
+ /* and store these values into the structure */
+ td->rect.x = x;
+ td->rect.y = y;
+
+ /*********** term structure initializing */
+
+ /* Initialize the term
+ gets: pointer to address, number of columns, number of rows, number
+ of keypresses to queue up (guess 24?)*/
+ term_init(t, cols, rows, 24);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Picture routine flags */
+ t->always_pict = FALSE;
+ t->higher_pict = FALSE;
+ t->always_text = FALSE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_sdl;
+ t->curs_hook = Term_curs_sdl;
+ t->wipe_hook = Term_wipe_sdl;
+ t->text_hook = Term_text_sdl;
+#ifdef USE_ISO
+ t->pict_hook = Term_pict_sdl;
+#endif /* USE_ISO */
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /************* finish term_data intializing */
+
+ /* name of this term window */
+ td->name = angband_term_name[i];
+
+ /* For now, all font is the same size... use global t_width/height */
+ td->tile_width = t_width;
+ td->tile_height = t_height;
+
+ td->cushion_x_top = 1;
+ td->cushion_x_bot = 1;
+ td->cushion_y_top = 1;
+ td->cushion_y_bot = 1;
+
+ /* Now calculate the total width and height*/
+ UPDATE_SIZE(td);
+
+ /* Create a surface to draw to */
+ td->surf = createSurface(td->rect.w,td->rect.h);
+ SDL_SetAlpha(td->surf,0,0);
+
+ /* Key some colors to this surface */
+ td->black = SDL_MapRGB(td->surf->format, 0, 0, 0);
+ td->white = SDL_MapRGB(td->surf->format,255,255,255);
+ td->purple = SDL_MapRGB(td->surf->format,255, 0,255);
+
+ /* Turn on a border, thickness specified by BORDER_THICKNESS */
+ td->border_thick = BORDER_THICKNESS;
+
+ /* make the default terminal border color to be white */
+ td->border_color = td->white;
+
+
+ printf("Init-int term: %d\n",i);
+#ifdef USE_GRAPHICS
+#ifdef USE_TRANSPARENCY
+#endif
+#endif
+
+ /* Success */
+ return (0);
+}
+
+/* dumpWindowSettings is responsible for exporting all current
+values of the window positions, etc. to the screen, so that
+the user can see what the final values were after tweaking */
+void dumpWindowSettings(void)
+{
+ char name[80];
+ char value[8];
+ int i;
+
+ DB("Dumping settings");
+ printf("---------------------------\n");
+ /* cycle through each available terminal */
+ for (i=0; i<arg_console_count; i++)
+ {
+ printf("Terminal %d:\n",i);
+ /* get the name, and value of each value to dump */
+ sprintf(name,"TOME_X_POS_%d",i);
+ sprintf(value,"%d",data[i].rect.x);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_Y_POS_%d",i);
+ sprintf(value,"%d",data[i].rect.y);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_NUM_COLS_%d",i);
+ sprintf(value,"%d",data[i].cols);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_NUM_ROWS_%d",i);
+ sprintf(value,"%d",data[i].rows);
+ printf("%s=%s\n",name,value);
+
+ /* Simple! */
+ printf("\n");
+ }
+}
+
+/* The main-sdl initialization routine!
+This routine processes arguments, opens the SDL
+window, loads fonts, etc. */
+errr init_sdl(int argc, char **argv)
+{
+ int i, surface_type;
+ char filename[PATH_MAX + 1];
+ const char file_sep = '.';
+ /* Flags to pass to SDL_SetVideoMode */
+ int videoFlags;
+
+ /* Before sdl_quit could possible be called, need to make sure that the text
+ array is zeroed, so that sdl_quit->killFontAndAlphabet() doesn't try to free
+ SDL_Surfaces that don't exist ! */
+ memset(text,0,sizeof(text));
+
+ /* Also, clear out the term order array */
+ memset(term_order,0,sizeof(term_order));
+
+ /* initialize SDL */
+ if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
+ {
+ sdl_quit("Video initialization failed!");
+ }
+ DB("SDL Initialized!");
+
+ /* get video info, to be used for determining if hardware acceleration is
+ available, pixel format, etc.... */
+ videoInfo = SDL_GetVideoInfo();
+
+ /* Environment calls to retrieve specific settings...
+ Note that these can be overridden by the command-line
+ arguments that are handled below */
+ if(getenv("TOME_CONSOLE_COUNT"))
+ arg_console_count = atoi(getenv("TOME_CONSOLE_COUNT"));
+ if(getenv("TOME_SCREEN_WIDTH"))
+ arg_width = atoi(getenv("TOME_SCREEN_WIDTH"));
+ if(getenv("TOME_SCREEN_HEIGHT"))
+ arg_height = atoi(getenv("TOME_SCREEN_HEIGHT"));
+ if(getenv("TOME_SCREEN_BPP"))
+ arg_bpp = atoi(getenv("TOME_SCREEN_BPP"));
+ if(getenv("TOME_FONT_SIZE"))
+ arg_font_size = atoi(getenv("TOME_FONT_SIZE"));
+
+ /* Argument handling routine;
+ the argv pointer is already pointing at the '--'
+ argument, so just start from there, parsing each
+ option as it comes along */
+ for (i=1; i < argc ; i++)
+ {
+ /* Set the number of consoles to handle
+ (ie the number of windows) */
+ if (0 == strcmp(argv[i], "-n"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -n\n");
+ return -1;
+ }
+
+ arg_console_count = atoi(argv[i]);
+ if (arg_console_count <= 0 || \
+ arg_console_count > MAX_CONSOLE_COUNT)
+ {
+ printf("Invalid console count given.\n");
+ arg_console_count = 1;
+ }
+ }
+ /* Set the SDL window/screen width in pixels */
+ else if (0 == strcmp(argv[i], "-w"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -w\n");
+ return -1;
+ }
+
+ arg_width = atoi(argv[i]);
+ }
+ /* Set the SDL window/screen height in pixels */
+ else if (0 == strcmp(argv[i], "-h"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -h\n");
+ return -1;
+ }
+
+ arg_height = atoi(argv[i]);
+ }
+ /* Set the SDL window/screen color depth
+ (in bits per pixel -- only 8,16,32 are okay) */
+ else if (0 == strcmp(argv[i], "-bpp"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -bpp\n");
+ return -1;
+ }
+
+ arg_bpp = atoi(argv[i]);
+ if ( (arg_bpp != 8) && (arg_bpp != 16) \
+ && (arg_bpp != 24) && (arg_bpp != 32) )
+ {
+ printf("Invalid color depth. Must be either 8, 16, or 32 bpp!\n");
+ return -1;
+ }
+ }
+ /* see if new graphics are requested...*/
+ else if (0 == strcmp(argv[i], "-g"))
+ {
+ printf("New graphics (16x16) enabled!\n");
+ arg_graphics_type = GRAPHICS_16x16;
+ }
+ /* see if old graphics are requested...*/
+ else if (0 == strcmp(argv[i], "-o"))
+ {
+ printf("Old graphics (8x8) enabled!\n");
+ arg_graphics_type = GRAPHICS_8x8;
+ }
+
+ /* see if double width tiles are requested */
+ else if (0 == strcmp(argv[i], "-b"))
+ {
+ /* do nothing for now */
+ /* arg_double_width = TRUE; */
+ }
+ /* switch into full-screen at startup */
+ else if (0 == strcmp(argv[i], "-fs"))
+ {
+ DB("Full-screen enabled!");
+ arg_full_screen = TRUE;
+ }
+ /* change the font size */
+ else if (0 == strcmp(argv[i], "-s"))
+ {
+ if (++i == argc)
+ {
+ printf("Argument missing for option -s\n");
+ printf("Please specify font size!\n");
+ return -1;
+ }
+
+ arg_font_size = atoi(argv[i]);
+ }
+ /* change the font to use */
+ else if (0 == strcmp(argv[i], "-f"))
+ {
+ /* Can we please not be so MS Windows-specific? One of the main goals
+ of SDL in ToME was to be more portable. These file name hacks are
+ only the idiom of that one OS, though. -- Neil */
+ DB("Getting font name");
+ if (++i == argc)
+ {
+ printf("Argument missing for option -f\n");
+ printf("Please specify a true-type font found in /lib/xtra/font!\n");
+ return -1;
+ }
+
+ /* tokenize the font name so that no .ttf extension
+ is required */
+ strcpy(arg_font_name,\
+ strtok(argv[i],&file_sep));
+
+ /* and append the extension */
+ strcat(arg_font_name,".ttf");
+
+ /* print a little debug message, so
+ user sees what font was actually selected */
+ printf("\tUsing font: %s\n",arg_font_name);
+
+ /* maybe check to see if file is even
+ existant in /lib/xtra/font */
+ }
+
+ } /* end argument handling */
+
+ /* Make sure that the engine will shutdown SDL properly*/
+ quit_aux = sdl_quit;
+
+ /* Use the ToME logo and set the window name */
+ filename[PATH_MAX] = 0;
+ path_build(filename, PATH_MAX, ANGBAND_DIR_XTRA, "graf/icon.png");
+ SDL_WM_SetIcon(IMG_Load(filename), 0);
+ SDL_WM_SetCaption("ToME", "tome");
+
+ /* SDL video settings, dependent on whether hardware is available */
+ if (videoInfo->hw_available)
+ videoFlags = SDL_HWSURFACE;
+ else
+ videoFlags = SDL_SWSURFACE;
+
+ /* now set the video mode that has been configured */
+ screen = SDL_SetVideoMode( arg_width, arg_height, arg_bpp, videoFlags );
+
+ /* Verify there is a surface */
+ if ( !screen )
+ {
+ DB("No screen!");
+ sdl_quit("Failed to set SDL Surface.");
+ }
+
+ DB("Video Mode Set!");
+
+ /* now switch into full screen if asked for */
+ if (arg_full_screen)
+ SDL_WM_ToggleFullScreen(screen);
+
+ DB("SDL Window Created!");
+
+ /* Now ready the fonts! */
+
+ DB("initializing SDL_ttf");
+ if(TTF_Init()==-1) {
+ printf("TTF_Init: %s\n", TTF_GetError());
+ sdl_quit("Bah");
+ }
+
+ DB("loading font...");
+
+ /* load and render the font */
+ loadAndRenderFont(arg_font_name,arg_font_size);
+
+ /* Graphics! ----
+ If graphics are selected, then load graphical tiles! */
+ if (arg_graphics_type != NO_GRAPHICS)
+ {
+ /* load graphics tiles */
+ }
+
+ /* Initialize the working surface and crayon surface used for rendering
+ text in different colors. */
+
+ worksurf = createSurface(t_width,t_height);
+ crayon = createSurface(t_width,t_height);
+
+ /* The working surface will blit using alpha values... */
+ SDL_SetAlpha(worksurf,SDL_SRCALPHA,0);
+
+ /* Set up the colors using the great little color macros! */
+ color_data[0] = BLACK;
+ color_data[1] = WHITE;
+ color_data[2] = MID_GREY;
+ color_data[3] = BRIGHT_ORANGE;
+ color_data[4] = RED;
+ color_data[5] = GREEN;
+ color_data[6] = BRIGHT_BLUE;
+ color_data[7] = DARK_ORANGE;
+ color_data[8] = DARK_GREY;
+ color_data[9] = BRIGHT_GREY;
+ color_data[10] = PURPLE;
+ color_data[11] = YELLOW;
+ color_data[12] = BRIGHT_RED;
+ color_data[13] = BRIGHT_GREEN;
+ color_data[14] = AQUAMARINE;
+ color_data[15] = BROWN;
+
+ /* And setup the cursor, using the default color...
+ XXX - in the future, this should (and will) be loaded from prefs */
+ createCursor(DEF_CURSOR_COLOR);
+
+ /* Initialize the windows, or whatever that means in this case.
+ Do this in reverse order so that the main window is on top.*/
+ suspendUpdate = TRUE; /* draw everything at the end */
+ i = arg_console_count;
+ while (i--)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Save global entry */
+ angband_term[i] = Term;
+
+ /* Add into term_order */
+ term_order[i] = td;
+
+ }
+
+ /* And setup the basic screen colors -- these are keyed to the format of
+ the main terminal surface */
+ screen_black = SDL_MapRGB(screen->format, 0, 0, 0);
+
+ suspendUpdate = FALSE; /* now draw everything */
+ redrawAllTerminals();
+ /*SDL_REDRAW_SCREEN;*/
+
+ /* now that the windows have been set, their settings can
+ be dumped upon quit! */
+ window_properties_set = TRUE;
+
+ /* Enable UNICODE keysyms - needed for current eventHandling routine */
+ SDL_EnableUNICODE(1);
+
+ /* Enable key repeat! */
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
+
+ #ifdef USE_ISO
+ DB("Isometric view uses always graphics mode.\n");
+ use_graphics = TRUE;
+
+
+ /* Hajo: allocate memory for output data */
+ /* These arrays are read by the iso-view and written from this file */
+ iso_cp = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_ap = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_ctp = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_atp = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_cep = halloc(data[0].t.wid, data[0].t.hgt);
+ iso_aep = halloc(data[0].t.wid, data[0].t.hgt);
+
+ // Hmm, no ANGBAND_SYS in old iso-code
+ // if I change this I don't have to load the *.prf manually?
+ //
+ // seems not to work for the following:
+ /* Hajo: set mode */
+ ANGBAND_GRAF = "iso";
+
+ /* Hajo: init view */
+ init_adaptor();
+
+ center_player = TRUE;
+#endif /* USE_ISO */
+
+#ifdef USE_ISO
+ // Juergen: HACK, but this all is just for testing ...
+ data[0].t.higher_pict = TRUE;
+#endif
+
+ /* main-sdl initialized! */
+ return 0;
+}
+
+#endif
diff --git a/src/main-sla.c b/src/main-sla.c
new file mode 100644
index 00000000..c38f019c
--- /dev/null
+++ b/src/main-sla.c
@@ -0,0 +1,455 @@
+/* File: main-sla.c */
+
+/* Purpose: Actual Unix "slang" support for Angband */
+
+/*
+ * Author: hans@grumbeer.pfalz.de (Hans-Joachim Baader)
+ *
+ * Most of this code is adapted directly from "main-gcu.c"
+ */
+
+#include "angband.h"
+
+
+#ifdef USE_SLA
+
+
+
+#include <slang.h>
+
+
+/*
+ * Are we "active"?
+ */
+static int slang_on = FALSE;
+
+
+/*
+ * Can we use "color"?
+ */
+static bool can_use_color = FALSE;
+
+
+/*
+ * Angband to SLang color conversion table
+ */
+static int colortable[16];
+
+
+/*
+ * Currently, only a single "term" is supported here
+ */
+static term term_screen_body;
+
+
+
+
+
+/*
+ * Hack -- see below
+ */
+void init_pair (int index, char *foreground, char *background)
+{
+ SLtt_set_color (index, "", foreground, background);
+}
+
+
+
+
+
+#define A_NORMAL 0
+#define A_BOLD 8
+#define A_REVERSE 0
+#define REVERSE 8
+
+#define COLOR_BLACK "black"
+#define COLOR_BLUE "blue"
+#define COLOR_GREEN "green"
+#define COLOR_CYAN "cyan"
+#define COLOR_RED "red"
+#define COLOR_MAGENTA "magenta"
+#define COLOR_YELLOW "brown"
+#define COLOR_WHITE "lightgray"
+#define COLOR_BBLACK "gray"
+#define COLOR_BBLUE "brightblue"
+#define COLOR_BGREEN "brightgreen"
+#define COLOR_BCYAN "brightcyan"
+#define COLOR_BRED "brightred"
+#define COLOR_BMAGENTA "brightmagenta"
+#define COLOR_BYELLOW "yellow"
+#define COLOR_BWHITE "white"
+
+
+
+
+
+static char *color_terminals [] =
+{
+#ifdef linux
+ "console",
+#endif
+ "linux",
+ "xterm-color",
+ "color-xterm",
+ "xtermc",
+ "ansi",
+ 0
+};
+
+
+
+
+
+/*
+ * Stolen from the Midnight Commander
+ */
+int has_colors(void)
+{
+ int i;
+
+ char *terminal;
+
+
+ /* Access the terminal type */
+ terminal = getenv("TERM");
+
+ /* Check for colors */
+ SLtt_Use_Ansi_Colors = 0;
+ if (NULL != getenv ("COLORTERM"))
+ {
+ SLtt_Use_Ansi_Colors = 1;
+ }
+
+ /* We want to allow overriding */
+ for (i = 0; color_terminals [i]; i++)
+ {
+ if (strcmp (color_terminals [i], terminal) == 0)
+ {
+ SLtt_Use_Ansi_Colors = 1;
+ }
+ }
+
+ /* Setup emulated colors */
+ if (SLtt_Use_Ansi_Colors)
+ {
+ /*init_pair (REVERSE, "black", "white");*/
+ }
+
+ /* Setup bizarre colors */
+ else
+ {
+ SLtt_set_mono (A_BOLD, NULL, SLTT_BOLD_MASK);
+ SLtt_set_mono (A_REVERSE, NULL, SLTT_REV_MASK);
+ SLtt_set_mono (A_BOLD | A_REVERSE, NULL, SLTT_BOLD_MASK | SLTT_REV_MASK);
+ }
+
+ return SLtt_Use_Ansi_Colors;
+}
+
+
+
+
+
+/*
+ * Nuke SLang
+ */
+static void Term_nuke_sla(term *t)
+{
+ if (!slang_on) return;
+
+ /* Show the cursor */
+ /* curs_set(1); */
+
+ /* Clear the screen */
+ (void)SLsmg_cls();
+
+ /* Refresh */
+ SLsmg_refresh();
+
+ /* We are now off */
+ slang_on = FALSE;
+
+ /* Shut down */
+ SLsmg_reset_smg();
+ SLang_reset_tty();
+}
+
+
+/*
+ * Init SLang
+ */
+static void Term_init_sla(term *t)
+{
+ /* Note that we are on */
+ slang_on = TRUE;
+}
+
+
+/*
+ * Process an event, wait if requested
+ */
+static errr Term_xtra_sla_event(int v)
+{
+ /* Do not wait unless requested */
+ if (!v && (SLang_input_pending (0) == 0)) return (1);
+
+ /* Get and enqueue the key */
+ Term_keypress(SLang_getkey ());
+
+ /* Success */
+ return 0;
+}
+
+
+
+/*
+ * Suspend / Resume
+ */
+static errr Term_xtra_sla_alive(int v)
+{
+ /* Suspend */
+ if (!v)
+ {
+ /* Oops */
+ if (!slang_on) return (1);
+
+ /* We are now off */
+ slang_on = FALSE;
+
+ /* Shut down (temporarily) */
+ SLsmg_reset_smg();
+ SLang_reset_tty();
+ }
+
+ /* Resume */
+ else
+ {
+ /* Oops */
+ if (slang_on) return (1);
+
+ /* Fix the screen */
+ SLsmg_refresh();
+
+ /* Note that we are on */
+ slang_on = TRUE;
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_sla(int n, int v)
+{
+ /* Analyze the request */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ (void)SLsmg_write_char('\007');
+ return (0);
+
+ /* Flush the ncurses buffer */
+ case TERM_XTRA_FRESH:
+ (void)SLsmg_refresh();
+ return (0);
+
+ /* Make the cursor invisible or visible */
+ case TERM_XTRA_SHAPE:
+ /* curs_set(v); */
+ return (0);
+
+ /* Handle events */
+ case TERM_XTRA_EVENT:
+ return (Term_xtra_sla_event(v));
+
+ /* Handle events */
+ case TERM_XTRA_FLUSH:
+ while (!Term_xtra_sla_event(FALSE));
+ return (0);
+
+ /* Suspend/Resume */
+ case TERM_XTRA_ALIVE:
+ return (Term_xtra_sla_alive(v));
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ (void)SLsmg_cls();
+ SLsmg_gotorc(0, 0);
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+ }
+
+ /* Oops */
+ return (1);
+}
+
+
+
+
+/*
+ * Actually MOVE the hardware cursor
+ */
+static errr Term_curs_sla(int x, int y, int z)
+{
+ /* Literally move the cursor */
+ SLsmg_gotorc (y, x);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Erase some characters
+ */
+static errr Term_wipe_sla(int x, int y, int n)
+{
+ int i;
+
+ /* Place the cursor */
+ SLsmg_gotorc(y, x);
+
+ /* Dump spaces */
+ for (i = 0; i < n; i++) SLsmg_write_char(' ');
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_sla(int x, int y, int n, byte a, cptr s)
+{
+ /* Move the cursor */
+ SLsmg_gotorc(y, x);
+
+ /* Set the color */
+ if (can_use_color) SLsmg_set_color(colortable[a&0x0F]);
+
+ /* Dump the string */
+ SLsmg_write_nchars(s, n);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Prepare "SLang" for use by the file "term.c"
+ * Installs the "hook" functions defined above
+ */
+errr init_sla(void)
+{
+ int i, err;
+
+ term *t = &term_screen_body;
+
+
+ /* Initialize, check for errors */
+ err = (SLang_init_tty( -1, TRUE, 0) == -1);
+
+ /* Quit on error */
+ if (err) quit("SLang initialization failed");
+
+ /* Get terminal info */
+ SLtt_get_terminfo ();
+
+ /* Initialize some more */
+ if (SLsmg_init_smg() == 0)
+ {
+ quit("Could not get virtual display memory");
+ }
+
+ /* Check we have enough screen. */
+ err = ((SLtt_Screen_Rows < 24) || (SLtt_Screen_Cols < 80));
+
+ /* Quit with message */
+ if (err) quit("SLang screen must be at least 80x24");
+
+ /* Now let's go for a little bit of color! */
+ err = !has_colors();
+
+ /* Do we have color available? */
+ can_use_color = !err;
+
+ /* Init the Color-pairs and set up a translation table */
+ /* If the terminal has enough colors */
+ /* Color-pair 0 is *always* WHITE on BLACK */
+
+ /* XXX XXX XXX See "main-gcu.c" for proper method */
+
+ /* Only do this on color machines */
+ if (can_use_color)
+ {
+ /* Prepare the color pairs */
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ init_pair(2, COLOR_GREEN, COLOR_BLACK);
+ init_pair(3, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(4, COLOR_BLUE, COLOR_BLACK);
+ init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(6, COLOR_CYAN, COLOR_BLACK);
+ init_pair(7, COLOR_BLACK, COLOR_BLACK);
+ init_pair(9, COLOR_BRED, COLOR_BLACK);
+ init_pair(10, COLOR_BGREEN, COLOR_BLACK);
+ init_pair(11, COLOR_BYELLOW, COLOR_BLACK);
+ init_pair(12, COLOR_BBLUE, COLOR_BLACK);
+ init_pair(13, COLOR_BMAGENTA, COLOR_BLACK);
+ init_pair(14, COLOR_BCYAN, COLOR_BLACK);
+ init_pair(15, COLOR_BBLACK, COLOR_BLACK);
+
+ /* Prepare the color table */
+ colortable[0] = 7; /* Black */
+ colortable[1] = 0; /* White */
+ colortable[2] = 6; /* Grey XXX */
+ colortable[3] = 11; /* Orange XXX */
+ colortable[4] = 1; /* Red */
+ colortable[5] = 2; /* Green */
+ colortable[6] = 4; /* Blue */
+ colortable[7] = 3; /* Brown */
+ colortable[8] = 15; /* Dark-grey XXX */
+ colortable[9] = 14; /* Light-grey XXX */
+ colortable[10] = 5; /* Purple */
+ colortable[11] = 11; /* Yellow */
+ colortable[12] = 9; /* Light Red */
+ colortable[13] = 10; /* Light Green */
+ colortable[14] = 12; /* Light Blue */
+ colortable[15] = 3; /* Light Brown XXX */
+ }
+
+
+ /* Initialize the term */
+ term_init(t, 80, 24, 64);
+
+ /* Stick in some hooks */
+ t->nuke_hook = Term_nuke_sla;
+ t->init_hook = Term_init_sla;
+
+ /* Stick in some more hooks */
+ t->xtra_hook = Term_xtra_sla;
+ t->curs_hook = Term_curs_sla;
+ t->wipe_hook = Term_wipe_sla;
+ t->text_hook = Term_text_sla;
+
+ /* Save the term */
+ term_screen = t;
+
+ /* Activate it */
+ Term_activate(t);
+
+
+ /* Success */
+ return 0;
+}
+
+#endif /* USE_SLA */
+
diff --git a/src/main-vme.c b/src/main-vme.c
new file mode 100644
index 00000000..81fc0e78
--- /dev/null
+++ b/src/main-vme.c
@@ -0,0 +1,1198 @@
+/* File: main-vme.c */
+
+/* Purpose: Support for "Vax Angband" */
+
+/*
+This is MAIN-VME C for VM/ESA machines.
+First enable definition of VM in file "h-config.h"
+You need to unpack archive EXT-VM VMARC .
+
+To do that first set fixed file record length:
+'COPYFILE EXT-VM VMARC A = = = ( REC F'
+Then unpack:
+VMARC UNPACK EXT-VM VMARC A
+
+( if you don't have archivator vmarc, there is many places where you
+get it, i got it from cc1.kuleuven.ac.be )
+
+Then read MAKEFILE EXEC. It contains all neccessary information to
+compile Angband.
+
+EXT-VM VMARC content:
+MAKEFILE EXEC
+VMSERV TXTLIB
+CNSHND ASSEMBLE
+
+You will need about 3-4 MB free disk space to compile it.
+If you have any problems, mail to
+
+SM20616@vm.lanet.lv or SD30066@vm.lanet.lv
+
+A large amount of this file appears to be a complete hack, but
+what can you expect from a system designed for the Vax... :-)
+ */
+
+
+#include "angband.h"
+
+#if defined(USE_VME) || defined(VM)
+
+
+/*
+ * Convert EBCDIC to ASCII
+ */
+char e2a[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x1A, 0x09, 0x1A, 0x7F, 0x1A, 0x1A, 0x1A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x1A, 0x0A, 0x08, 0x1A, 0x18, 0x19, 0x1A, 0x1A, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x1A, 0x1A, 0x1C, 0x1A, 0x1A, 0x0A, 0x17, 0x1B, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x05, 0x06, 0x07,
+ 0x1A, 0x1A, 0x16, 0x1A, 0x1A, 0x1E, 0x1A, 0x04, 0x1A, 0x1A, 0x1A, 0x1A, 0x14, 0x15, 0x1A, 0x1A,
+ 0x20, 0xA6, 0xE1, 0x80, 0xEB, 0x90, 0x9F, 0xE2, 0xAB, 0x8B, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
+ 0x26, 0xA9, 0xAA, 0x9C, 0xDB, 0xA5, 0x99, 0xE3, 0xA8, 0x9E, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
+ 0x2D, 0x2F, 0xDF, 0xDC, 0x9A, 0xDD, 0xDE, 0x98, 0x9D, 0xAC, 0xBA, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
+ 0xD7, 0x88, 0x94, 0xB0, 0xB1, 0xB2, 0xFC, 0xD6, 0xFB, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
+ 0xF8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x96, 0xA4, 0xF3, 0xAF, 0xAE, 0xC5,
+ 0x8C, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x97, 0x87, 0xCE, 0x93, 0xF1, 0xFE,
+ 0xC8, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xEF, 0xC0, 0xDA, 0x5B, 0xF2, 0xF9,
+ 0xB5, 0xB6, 0xFD, 0xB7, 0xB8, 0xB9, 0xE6, 0xBB, 0xBC, 0xBD, 0x8D, 0xD9, 0xBF, 0x5D, 0xD8, 0xC4,
+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xCB, 0xCA, 0xBE, 0xE8, 0xEC, 0xED,
+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0xA1, 0xAD, 0xF5, 0xF4, 0xA3, 0x8F,
+ 0x5C, 0xE7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xA0, 0x85, 0x8E, 0xE9, 0xE4, 0xD1,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xB3, 0xF7, 0xF0, 0xFA, 0xA7, 0xFF
+};
+
+
+/*
+ * Convert ASCII to EBCDIC
+ */
+char a2e[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x35, 0x1F,
+ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x35, 0x1F,
+ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07
+};
+
+
+/*
+ * System headers (already included by "angband.h" XXX XXX XXX)
+ */
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+/* Some prototypes and definitions */
+
+#define FIELD_SY 1
+#define FIELD_SX 80
+#define FIELD_EY 2
+#define FIELD_EX 10
+
+void GetAddr(int, int, char *);
+void InitConsole(void);
+void TerminateConsole(void);
+void ResetScrBuf(void);
+void AddScrBuf(char *, int);
+char InKey(void);
+char InKeyBuf(void);
+void ScreenClear(void);
+void ResetDISP(void);
+int kbhit(void);
+void ShowLine(int y, int x, int len);
+void LoadProfile(void);
+
+/* Changed lines marker. */
+
+char DISP[26];
+
+/* Max rows & columns number in screen (note that in older version
+ ** than 2.7.9 we needed 25 lines, though 24th was unused. So it is set
+ ** to 25 although screen actually has only 24 lines. (we just used to
+ ** transfer 25th line into 24th.
+ ** But in new version we no longer use it.
+ */
+
+int rows, cols;
+
+/* Game cursor position on screen */
+
+int curx = 1;
+int cury = 1;
+
+/*
+ * Virtual Screen
+ */
+byte VirtualScreen[2048];
+byte ScreenAttr[2048];
+
+/*
+ * This array is used for "wiping" the screen, initialized in "init_wat()"
+ */
+byte wiper[256];
+
+/*
+ * The main screen
+ */
+static term term_screen_body;
+
+
+/* Update line on screen.
+ ** Actually just set flag that we should update it.
+ */
+void ScreenUpdateLine(int line)
+{
+ DISP[line + 1] = 1;
+}
+
+/*
+ * Clear the whole screen
+ */
+void ScreenClear(void)
+{
+ int iy;
+
+ for (iy = 0; iy < rows; iy++)
+ {
+ memcpy(VirtualScreen + (iy*cols), wiper, cols);
+ memset(ScreenAttr + (iy*cols), 0xF1, cols);
+ ScreenUpdateLine(iy);
+ }
+}
+
+
+/*
+ * Nuke a Term
+ */
+static void Term_nuke_vm(term *t)
+{
+ TerminateConsole();
+ puts("Console has been terminated(nuke).");
+}
+
+/*
+ * Move the cursor
+ */
+static errr Term_curs_vm(int x, int y)
+{
+ /* Hack: mark line cursor was at as changed to ensure that old
+ ** cursor would be removed.
+ */
+ DISP[cury] = 1;
+ curx = x + 1;
+ cury = y + 1;
+ return (0);
+}
+
+
+/*
+ * The Angband color table
+ *
+ * Comment: White has been changed to blue to make screen look better.
+ * Green and Light Green are exchanged for the same reason.
+ * -0x80 means changing to bold font.
+ *
+ * Color Table:
+ * Dark to Blue, White to Blue, Slate to Blue, Orange to Pink,
+ * Red to Red, Green to GreenB, Blue to BlueB, Umber to Yellow,
+ * Gray to Blue, Light White to WhiteB, Violet to PinkB, Yellow to Yellow
+ * Light colors: Red to RedB,Green to Green,Blue to Cyan, Umber to YellB
+ */
+static const byte vm_color[] =
+{
+ 0xF1, 0xF1, 0xF1, 0xF3,
+ 0xF2, 0xF4 - 0x80, 0xF1 - 0x80, 0xF6,
+ 0xF1, 0xF7 - 0x80, 0xF3 - 0x80, 0xF6,
+ 0xF2 - 0x80, 0xF4, 0xF5, 0xF6 - 0x80
+};
+
+/*
+ * Place some text on the screen using an attribute
+ */
+static errr Term_text_vm(int x, int y, int n, byte a, cptr s)
+{
+ register int i;
+ register byte attr;
+ register byte *dest;
+ register byte *dest2;
+
+ /* Attribute... */
+ attr = vm_color[a & 0x0F];
+
+ /* Paranoia */
+ if (n > cols - x) n = cols - x;
+
+ /* Access the destination */
+ dest = VirtualScreen + ((cols * y) + x);
+ dest2 = ScreenAttr + ((cols * y) + x);
+
+ /* Virtually write the string */
+ for (i = 0; (i < n) && s[i]; i++)
+ {
+ *dest++ = s[i];
+ *dest2++ = attr;
+ }
+
+ /* Dump the line */
+ ScreenUpdateLine(y);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase part of line.
+ */
+static errr Term_wipe_vm(int x, int y, int w)
+{
+
+ /* Paranoia -- Verify the dimensions */
+ if (cols < (w + x)) w = (cols - x);
+
+ /* Wipe part of the virtual screen, and update */
+ memcpy(VirtualScreen + (y*cols + x), wiper, w);
+ memset(ScreenAttr + (y*cols + x), 0xF1, w);
+ ScreenUpdateLine(y);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle special request.
+ */
+static errr Term_xtra_vm(int n, int v)
+{
+ int i, j;
+ char tmp;
+
+ /* Analyze the request */
+ switch (n)
+ {
+
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+
+ /* No noises here! :) */
+ return (0);
+
+ /* Wait for a single event */
+ case TERM_XTRA_EVENT:
+
+ /* No wait key press check */
+ if (!v && !kbhit()) return (1);
+
+ /* Wait for a keypress */
+ i = getkey();
+
+ /* Save keypress */
+ Term_keypress(i);
+
+ /* Success */
+ return (0);
+
+ case TERM_XTRA_CLEAR:
+
+ ScreenClear();
+ return (0);
+
+#if 0
+ case TERM_XTRA_FROSH:
+ ScreenUpdateLine(VirtualScreen + (cols*v), v);
+ return (0);
+#endif
+
+ case TERM_XTRA_FLUSH:
+
+ /* Flush keys */
+ while (1)
+ {
+ tmp = getkeybuf();
+ if (!tmp) break;
+ Term_keypress(tmp);
+ }
+
+ /* Success */
+ return (0);
+ }
+
+ /* Unknown request */
+ return (1);
+}
+
+
+/*
+ * Initialize the VM/CNSconsole.
+ */
+errr init_vme(void)
+{
+ register i;
+
+ term *t = &term_screen_body;
+
+ short blank = ' ';
+
+ static int done = FALSE;
+
+ /* Paranoia -- Already done */
+ if (done) return ( -1);
+
+ /* Build a "wiper line" of blank spaces */
+ for (i = 0; i < 256; i++) wiper[i] = blank;
+
+ /* Acquire the size of the screen */
+ rows = 25;
+ cols = 80;
+
+ /* Initialize the console */
+ InitConsole();
+
+ /* Wipe the screen */
+ for (i = 0; i < rows; i++)
+ {
+ /* Wipe that row */
+ memcpy(VirtualScreen + (i*cols), wiper, cols);
+ memset(ScreenAttr + (i*cols), 0xF1, cols);
+ }
+
+ /* Erase the screen */
+ ScreenClear();
+
+ /* Initialize the term -- very large key buffer */
+ term_init(t, cols, rows - 1, 1024);
+
+ /* Prepare the init/nuke hooks */
+ t->nuke_hook = Term_nuke_vm;
+
+ /* Connect the hooks */
+ t->text_hook = Term_text_vm;
+ t->wipe_hook = Term_wipe_vm;
+ t->curs_hook = Term_curs_vm;
+ t->xtra_hook = Term_xtra_vm;
+
+ /* Save it */
+ term_screen = t;
+
+ /* Activate it */
+ Term_activate(term_screen);
+
+ /* Done */
+ done = TRUE;
+
+ /* Success */
+ return 0;
+}
+
+/* Wait for keypress */
+int
+getkey(void)
+{
+ return ((int)InKey());
+}
+
+/* Read key buffer if not empty */
+
+int
+getkeybuf(void)
+{
+ return ((int)InKeyBuf());
+}
+
+
+/*********************************************************************/
+/*********************************************************************/
+/****************** Actual work with console *********************/
+/*********************************************************************/
+
+
+/* Low-level functions */
+int CNSINIT(char *path, int device);
+int CNSTERM(char *path);
+int CNSREAD(char *path, char *buffer, int buflen);
+int CNSWRITE(char *path, char *buffer, int buflen);
+
+#define _PF1 (0xF1)
+#define _PF2 (0xF2)
+#define _PF3 (0xF3)
+#define _PF4 (0xF4)
+#define _PF5 (0xF5)
+#define _PF6 (0xF6)
+#define _PF7 (0xF7)
+#define _PF8 (0xF8)
+#define _PF9 (0xF9)
+#define _PF10 (0x7A)
+#define _PF11 (0x7B)
+#define _PF12 (0x7C)
+#define _PF13 (0xC1)
+#define _PF14 (0xC2)
+#define _PF15 (0xC3)
+#define _PF16 (0xC4)
+#define _PF17 (0xC5)
+#define _PF18 (0xC6)
+#define _PF19 (0xC7)
+#define _PF20 (0xC8)
+#define _PF21 (0xC9)
+#define _PF22 (0x4A)
+#define _PF23 (0x4B)
+#define _PF24 (0x4C)
+
+#define _PA2 (0x6E)
+#define _PA3 (0x6B)
+
+#define _CLEAR (0x60)
+
+#define _ENTER (0x7D)
+
+extern char cnscrstb[]; /* Hardware 3270 cursor offsets */
+
+/* Console identificator */
+char *cons = "console";
+
+/* Console interrupt flag; should be set by assembler patch */
+int CNSINTR;
+/* Assembler function prototype; this handles console interrupt */
+extern int CNSHD();
+
+/* Buffer for output stream (VM/ESA uses streams to control terminals */
+static char * ScrBuf;
+/* Incoming buffer from console after cnsread */
+static char * InBuf;
+/* User entered command buffer (paranoia: make it so long no one will
+ ** ever run short of it :)
+ */
+static char ComBuf[256];
+/* Pointer to current position in ComBuf */
+static char * ComPtr;
+/* This array is used to clean up input field.
+ ** Comment: erase everything user entered after we accepted it.
+ */
+static char wiping[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+/* Flag: should we wipe input field ? */
+static int wipe = 0;
+/* Counter: how many times did game checked if we pressed any key.
+ ** Comment: after every 100th we update the screen.
+ ** Comment2: because of slow work with screen it would be _very_
+ ** unwise to make it more often.
+ */
+static int kbhitcount;
+/* Keep length of collected output stream */
+static int BufLen;
+/* Define array which will be used to bring actual cursor
+ ** (not game cursor!!!) back to the beginning of input field.
+ */
+static char CursorHome[] = {0, 0, 0, 0x13};
+/* Define array to make fields on the screen.
+ ** Comment: 2 fields: input field(writable), and game field(protected).
+ ** Comment2: We will change PSS settings into dumb repeating of color
+ ** setting if PSS fonts are not abailable.
+ */
+static char ScrField[] =
+ {0, 0, 0, 0x29, 0x03, 0xC0, 0xC1, 0x42, 0xF3, 0x43, 0xC1, \
+ 0, 0, 0, 0x29, 0x03, 0xC0, 0x60, 0x42, 0xF1, 0x43, 0xC1};
+/* Last command user entered (for repeating purposes */
+static char LastCmd[256];
+/* Array which holds PFkeys definitions */
+static char PFcmd[25][80];
+
+
+/* Actually initialize console ... */
+void InitConsole(void)
+{
+ /* Use this to activate full screen mode after console has been
+ ** initialized.
+ */
+ unsigned char init[6] =
+ {0xC2, 0x11, 0x40, 0x40, 0, 0};
+ int i;
+ /* Array for checking is PSS fonts are loaded */
+ char pss[256];
+
+ /* Allocate memory */
+ ScrBuf = malloc(4100);
+ if (ScrBuf == NULL)
+ {
+ puts("Cannot mallocate memory for screen buffer!");
+ exit(77);
+ }
+ /* Block system standarts... */
+ system("cp set msg off");
+ system("cp set emsg off");
+ system("cp set imsg off");
+ system("cp term brkkey none");
+ /* Test PSS */
+ system("desbuf");
+ system("query display (stack");
+ gets(pss);
+ i = 1;
+ if (pss[63] != 'P') i = 0;
+ if (pss[64] != 'S') i = 0;
+ if (pss[65] != 'S') i = 0;
+ if (pss[72] != '0') i = 0;
+ if (pss[73] != '1') i = 0;
+ if (pss[74] != 'A') i = 0;
+ if (pss[75] != 'B') i = 0;
+ if (i == 0)
+ {
+ /* No PSS. Cannot run without them... */
+ puts("ERROR: Cannot run without PSS fonts!");
+ exit(77);
+ }
+ ScrBuf[0] = 0xC2;
+ ScrBuf[1] = 0;
+ BufLen = 1;
+ /* Allocate memory */
+ InBuf = malloc(200);
+ if (InBuf == NULL)
+ {
+ puts("Cannot mallocate memory for screen buffer!");
+ exit(77);
+ }
+ /* Okay, lets make some intial assigments */
+ InBuf[0] = 0;
+ ComBuf[0] = 0;
+ ComPtr = ComBuf;
+ LastCmd[0] = 0;
+ GetAddr(FIELD_SY, FIELD_SX, ScrField);
+ GetAddr(FIELD_EY, FIELD_EX, ScrField + 11);
+ GetAddr(FIELD_SY, FIELD_SX + 1, wiping);
+ GetAddr(FIELD_SY, FIELD_SX + 1, CursorHome);
+ /* Initialize console */
+ cnsxinit(cons, 0x9, 1, CNSHD);
+ /* Activate full-screen mode */
+ cnswrite(cons, init, 6);
+ /* No lines on screen were changed */
+ for (i = 1; i < 25; i++)
+ DISP[i] = 0;
+ /* Corsor home... */
+ AddScrBuf(CursorHome, sizeof(CursorHome));
+ /* No console interrupt yet */
+ CNSINTR = 0;
+ /* No check keypresses yet */
+ kbhitcount = 0;
+ /* Let's load 'profile angband' for PFs settings */
+ LoadProfile();
+}
+
+void TerminateConsole(void)
+{
+ free(ScrBuf);
+ free(InBuf);
+ /* Terminate console */
+ cnsterm(cons);
+ /* Restore system defaults.
+ ** Comment: better we should have written them down at start but
+ ** it is reasonably difficult to make, so just set standart values.
+ */
+ system("cp set msg on");
+ system("cp set emsg on");
+ system("cp set imsg on");
+ system("cp term brkkey pa1");
+}
+
+/* Reset collected output stream */
+void ResetScrBuf(void)
+{
+ int i;
+ unsigned char attr;
+
+ /* Where shall cursor be ? */
+ GetAddr(cury, curx, ScrBuf + BufLen);
+ BufLen += 3;
+ /* Actually 'make' cursor : underline symbol */
+ ScrBuf[BufLen++] = 0x28;
+ ScrBuf[BufLen++] = 0x41;
+ ScrBuf[BufLen++] = 0xF4;
+ attr = *(ScreenAttr + (cury - 1) * cols + curx - 1);
+ ScrBuf[BufLen++] = 0x28;
+ ScrBuf[BufLen++] = 0x42;
+ ScrBuf[BufLen++] = attr | 0x80;
+ ScrBuf[BufLen++] = 0x28;
+ ScrBuf[BufLen++] = 0x43;
+ if (attr > 0x80) ScrBuf[BufLen++] = 0xC1;
+ else ScrBuf[BufLen++] = 0xC2;
+ ScrBuf[BufLen++] = *(VirtualScreen + (cury - 1) * cols + curx - 1);
+ ScrBuf[BufLen++] = 0x28;
+ ScrBuf[BufLen++] = 0x41;
+ ScrBuf[BufLen++] = 0x00;
+ ScrBuf[BufLen++] = 0x28;
+ ScrBuf[BufLen++] = 0x43;
+ ScrBuf[BufLen++] = 0xC1;
+ /* Draw screen fields.
+ ** Comment: paranoia, should always be there, but won't be any worse
+ ** if we redraw them.
+ */
+ memcpy(ScrBuf + BufLen, ScrField, 22);
+ if (wipe)
+ {
+ /* If we should wipe input field, let's do it. */
+ wipe = 0;
+ memcpy(ScrBuf + BufLen + 22, wiping, sizeof(wiping));
+ /* Actually write to terminal */
+ cnswrite(cons, ScrBuf, BufLen + 22 + sizeof(wiping));
+ }
+ else
+ {
+ /* Actually write to terminal */
+ cnswrite(cons, ScrBuf, BufLen + 22);
+ }
+ /* Discard old output stream */
+ ScrBuf[0] = 0xC2;
+ ScrBuf[1] = 0;
+ BufLen = 1;
+}
+
+/* Just add some more to output stream */
+void AddScrBuf(char * ptr, int len)
+{
+ /* Stream cannot be longer than 4k, so reset it */
+ if (len + BufLen > 4000) ResetScrBuf();
+ memcpy(ScrBuf + BufLen, ptr, len);
+ BufLen += len;
+}
+
+/* Calculate codes for cursor setting and write them down into *stream.
+ ** Comment: system has _really crazy tables for this calculation so
+ ** we need function for this.
+ */
+void GetAddr(int y, int x, char *stream)
+{
+ int sx, sy, len;
+
+ len = y * 80 + x - 81;
+
+ sx = len & 0x3F;
+ sy = len >> 6;
+
+ *stream++ = 0x11;
+ /* Use standart array for cursor offsets (VMSERV TXTLIB) */
+ *stream++ = cnscrstb[sy];
+ *stream = cnscrstb[sx];
+}
+
+/* Okay, program requested pressed key, let's return it */
+char InKey(void)
+{
+ char ret;
+ char * info;
+ int i;
+ char *ptr, *ptrs;
+ int PF;
+ int prev;
+
+ /* If we didn't finish previous line user entered just return
+ ** next symbol.
+ ** Comment: unlikely IBM PC and others user may enter string
+ ** of any length (limited by input field length to 9
+ ** symbols) and only them by pressing ENTER key or
+ ** functional key make real console interrupt.
+ ** So we should handle everything user entered.
+ */
+ if (*ComPtr)
+ {
+ ret = *ComPtr;
+ ComPtr++;
+ return (ret);
+ }
+ /* If we get here than user command buffer is empty and we have
+ ** actually wait for new command.
+ */
+ /* Okay, update screen before :) */
+ ResetDISP();
+ /* Well, now wait till user enters something and read it. */
+ cnsread(cons, InBuf, 200);
+ /* Oh, we already handling interrupt so, reset interrupt flag */
+ CNSINTR = 0;
+ /* No previous cmd inserted yet */
+ prev = 0;
+ /* User pressed CLEAR key. Redraw all screen then. */
+ if (*InBuf == 0x6D)
+ {
+ /* Hack: mark all lines as changed */
+ for (i = 1; i < 26; i++)
+ DISP[i] = 1;
+ /* Cursor home */
+ AddScrBuf(CursorHome, sizeof(CursorHome));
+ /* Redraw the whole screen */
+ ResetDISP();
+ /* Hack: we should return something, shouldn't we?
+ ** Just return CR. It seems safest.
+ */
+ return (13);
+ }
+ /* Okay parse stream from console to make string only of
+ ** user's input.
+ */
+ info = memchr(InBuf, 0x1D, 200);
+ info += 2;
+ info[9] = 0;
+ /* Mark that we should clear input field */
+ wipe = 1;
+ /* Cursor home */
+ AddScrBuf(CursorHome, sizeof(CursorHome));
+ /* Pointer to current letter in buffered command set to start */
+ ComPtr = ComBuf;
+ /* Clear length buffer */
+ ComBuf[0] = 0;
+ switch (*InBuf)
+ {
+ /* PA2 & PA1 */
+ case 0x6E:
+ ;
+ case 0x6C:
+ /* These 2 keys are supposed to mean 'ESC' */
+ return (27);
+ break;
+ /* Hack: determine which PF key was exactly pressed.
+ */
+ case _PF1:
+ PF = 1;
+ break;
+ case _PF2:
+ PF = 2;
+ break;
+ case _PF3:
+ PF = 3;
+ break;
+ case _PF4:
+ PF = 4;
+ break;
+ case _PF5:
+ PF = 5;
+ break;
+ case _PF6:
+ PF = 6;
+ break;
+ case _PF7:
+ PF = 7;
+ break;
+ case _PF8:
+ PF = 8;
+ break;
+ case _PF9:
+ PF = 9;
+ break;
+ case _PF10:
+ PF = 10;
+ break;
+ case _PF11:
+ PF = 11;
+ break;
+ case _PF12:
+ PF = 12;
+ break;
+ case _PF13:
+ PF = 13;
+ break;
+ case _PF14:
+ PF = 14;
+ break;
+ case _PF15:
+ PF = 15;
+ break;
+ case _PF16:
+ PF = 16;
+ break;
+ case _PF17:
+ PF = 17;
+ break;
+ case _PF18:
+ PF = 18;
+ break;
+ case _PF19:
+ PF = 19;
+ break;
+ case _PF20:
+ PF = 20;
+ break;
+ case _PF21:
+ PF = 21;
+ break;
+ case _PF22:
+ PF = 22;
+ break;
+ case _PF23:
+ PF = 23;
+ break;
+ case _PF24:
+ PF = 24;
+ break;
+ /* Uh, user pressed ENTER.
+ ** Determine if we should add 'CR' at the end or not ?
+ */
+ case _ENTER:
+ PF = 0;
+ /* Copy entered string into buffer */
+ strcpy(ComBuf, info);
+ /* Never end strings of length 1 or 0 with CR */
+ if (strlen(ComBuf) < 2) break;
+ /* If string ends with R* or R& add CR */
+ ptr = info + strlen(info) - 1;
+ if (*ptr == '*' || *ptr == '&')
+ {
+ ptr--;
+ if (ptr < info) break;
+ if (*ptr != 'R') break;
+ ptr = ComBuf + strlen(ComBuf);
+ ptr[0] = 13;
+ ptr[1] = 0;
+ break;
+ }
+ /* Well, only numbers should be padded with CR.
+ ** Comment: handle 18/... too.
+ */
+ if (!isdigit(*ptr)) break;
+ ptr--;
+ i = 1;
+ while (ptr >= info)
+ {
+ if (*ptr == 'R') break;
+ if (*ptr == '@' && ptr == info)
+ break;
+ if (*ptr == '/')
+ {
+ i = 0;
+ if (*(ptr - 1) != '8') break;
+ if (*(ptr - 2) != '1') break;
+ if ((ptr - 2) != info) break;
+ i = 1;
+ break;
+ }
+ if (!isdigit(*ptr))
+ {
+ i = 0;
+ break;
+ }
+ ptr--;
+ }
+ if (i)
+ ptr = ComBuf + strlen(ComBuf);
+ ptr[0] = 13;
+ ptr[1] = 0;
+ break;
+ default:
+ PF = 0;
+ }
+ /* Okay let's proceed with parsing PFkeys commands */
+ ptrs = PFcmd[PF];
+ ptr = ComBuf;
+ /* If key actually was PF and we didn't run into end of PF
+ ** command definition...
+ */
+ while (*ptrs && PF)
+ {
+ /* If not '\' just copy symbol into buffer */
+ if (*ptrs != '\\')
+ {
+ *ptr = *ptrs;
+ ptr++;
+ ptrs++;
+ continue;
+ }
+ /* We have discovered '\'. Let's resolve it. */
+ ptrs++;
+ switch (*ptrs)
+ {
+ /* End of line ? Do nothing then. */
+ case 0:
+ break;
+ /* User actually wants '\' */
+ case '\\':
+ *ptr = '\\';
+ ptr++;
+ break;
+ /* User wants his previous command inserted. */
+ case 'p':
+ /* Set flag that we used previous command */
+ prev = 1;
+ strcpy(ptr, LastCmd);
+ ptr = ComBuf + strlen(ComBuf);
+ break;
+ /* Insert string user actually entered */
+ case 's':
+ strcpy(ptr, info);
+ ptr = ComBuf + strlen(ComBuf);
+ break;
+ /* User wants 'CR' */
+ case 'n':
+ *ptr = 13;
+ ptr++;
+ break;
+ /* Simulate CTRL key pressing */
+ case '^':
+ ptrs++;
+ if (!(*ptrs)) break;
+ *ptr = KTRL(*ptrs);
+ ptr++;
+ break;
+ /* ESC */
+ case 'c':
+ *ptr = 27;
+ ptr++;
+ break;
+ /* Hmm, urecognized command. Just copy letter. */
+ default:
+ *ptr = *ptrs;
+ ptr++;
+ }
+ if (ptrs) ptrs++;
+ }
+ /* Terminate string with 0 if neccessary */
+ if (PF) *ptr = 0;
+ /* Empty string? User probably wants to send CR. */
+ if (!(*ComBuf))
+ {
+ return (13);
+ }
+ /* Okay, set pointer to next letter */
+ ComPtr++;
+ /* Backup last command if not empty */
+ ptr = ComBuf;
+ /* String still "empty" */
+ i = 1;
+ while ((*ptr) && i)
+ {
+ if ((*ptr != 13) && (*ptr != 27) && (*ptr != ' '))
+ i = 0;
+ ptr++;
+ }
+ /* Never update previous command if we inserted it.
+ ** Comment: this is to avoid recursively multiplying previous
+ ** commands.
+ */
+ if ((!i) && (!prev))
+ strcpy(LastCmd, ComBuf);
+ /* Return something */
+ return (*ComBuf);
+}
+
+/* Get symbol from buffer if exists */
+
+char InKeyBuf(void)
+{
+ char ret;
+
+ ret = *ComPtr;
+ if (ret) ComPtr++;
+ return (ret);
+}
+
+/* Here we draw all changed lines */
+void ResetDISP(void)
+{
+ int i;
+
+ /* No need to redraw screen in near future */
+ kbhitcount = 0;
+ for (i = 1; i < 25; i++)
+ if (DISP[i] != 0)
+ {
+ /* If line was changed, it won't stay changed any more */
+ DISP[i] = 0;
+ /* We don't want to erase input field definitons from
+ ** screen, so check it carefully.
+ */
+ switch (i)
+ {
+ case FIELD_SY:
+ ShowLine(i, 1, FIELD_SX - 1);
+ if (i == FIELD_EY)
+ {
+ ShowLine(i, FIELD_EX + 1, 80-FIELD_EX);
+ }
+ break;
+ case FIELD_EY:
+ ShowLine(i, FIELD_EX + 1, 80-FIELD_EX);
+ break;
+ default:
+ ShowLine(i, 1, 80);
+ }
+ }
+ /* Okay, actually show something */
+ ResetScrBuf();
+}
+
+/* Did user press something? */
+int kbhit(void)
+{
+ kbhitcount++;
+ if (kbhitcount > 99)
+ {
+ /* We waited long enough, time to redraw screen.
+ ** Comment: during sleep every 100th turn is shown.
+ */
+ kbhitcount = 0;
+ ResetDISP();
+ }
+ return (CNSINTR);
+}
+
+/* Let's show requested line(or part of it). */
+void ShowLine(int y, int x, int len)
+{
+ char slbuf[512];
+ char *ptr, *scr, *attr;
+ int i;
+ unsigned char curattr;
+
+ ptr = slbuf + 3;
+ /* Hack: was used for older version to place 25th string on
+ ** 24th.
+ */
+ if (y < 25)
+ GetAddr(y, x, slbuf);
+ else
+ GetAddr(24, x, slbuf);
+ /* Set some adresses */
+ curattr = 0;
+ scr = VirtualScreen + (y - 1) * cols + x - 1;
+ attr = ScreenAttr + (y - 1) * cols + x - 1;
+ /* Let's proceed with string. */
+ for (i = 0; i < len; i++)
+ {
+ /* Attribute has changed ? Let's add codes for changing color */
+ if (*attr != curattr)
+ {
+ /* Pss has changed ? Let's change pss. */
+ if (((*attr)&0x80) != (curattr&0x80) || (!curattr))
+ {
+ *ptr++ = 0x28;
+ *ptr++ = 0x43;
+ if (*attr > 0x80) *ptr++ = 0xC1;
+ else *ptr++ = 0xC2;
+ }
+ /* Color has changed ? Let's change it. */
+ if (((*attr)&0x7F) != (curattr&0x7F))
+ {
+ *ptr++ = 0x28;
+ *ptr++ = 0x42;
+ if (*attr > 0x80) *ptr++ = *attr;
+ else *ptr++ = *attr + 0x80;
+ }
+ curattr = *attr;
+ }
+ *ptr++ = *scr;
+ scr++;
+ attr++;
+ }
+ /* Add string to output stream */
+ AddScrBuf(slbuf, ptr - slbuf);
+}
+
+void LoadProfile(void)
+{
+
+ FILE *fp;
+ char line[128], *ptr, *pfptr, *p;
+ int pf, i;
+
+ for (i = 1; i <= 24; i++) strcpy(PFcmd[i], "\\s");
+
+ fp = fopen("PROFILE ANGBAND", "r");
+ if (!fp) return;
+ {
+ while (fgets(line, 128, fp))
+ {
+ if (*line == '#') continue;
+ ptr = strstr(line, "PF");
+ if (!ptr) continue;
+ ptr += 2;
+ p = ptr;
+ while (isdigit(*p)) ++p;
+ *p++ = 0;
+ pf = atoi(ptr);
+ if (pf < 1 || pf > 24) continue;
+ ptr = strchr(p, '"');
+ ++ptr;
+ p = strchr(ptr, '"');
+ if (!p)
+ {
+ puts("'\"' missing in PROFILE ANGBAND.");
+ continue;
+ }
+ *p = 0;
+ strcpy(PFcmd[pf], ptr);
+ }
+ }
+}
+
+/*
+ **
+ ** File descriptor emulation for VM/ESA
+ **
+ **
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "fcntl.h"
+
+static FILE *file_descriptors[40];
+
+int
+open(char *name, int flags, int mode)
+{
+ char fmode[16];
+ FILE *fp;
+ int i;
+
+ strcpy(fmode, "rb");
+ if ((flags & O_WRONLY) || (flags & O_RDWR)) strcpy(fmode, "wb+");
+
+ fp = fopen(name, fmode);
+
+ if (!fp) return ( -1);
+ for (i = 1; i < 40; i++)
+ if (!file_descriptors[i])
+ {
+ file_descriptors[i] = fp;
+ return (i);
+ }
+ return ( -1);
+}
+
+void
+close(int fd)
+{
+ fclose(file_descriptors[fd]);
+}
+
+int
+read(int fd, char *buff, int bytes)
+{
+ return (fread(buff, 1, bytes, file_descriptors[fd]));
+}
+
+int
+write(int fd, char *buff, int bytes)
+{
+ return (fwrite(buff, 1, bytes, file_descriptors[fd]));
+}
+
+long
+lseek(int fd, long pos, int set)
+{
+ return (fseek(file_descriptors[fd], pos, set));
+}
+
+void
+unlink(char *filename)
+{
+ remove(filename);
+}
+
+
+#endif /* USE_VME */
+
diff --git a/src/main-win.c b/src/main-win.c
new file mode 100644
index 00000000..e2e71e95
--- /dev/null
+++ b/src/main-win.c
@@ -0,0 +1,4486 @@
+/* File: main-win.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Skirmantas Kligys, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with Windows computers.
+ *
+ * To use this file, use an appropriate "Makefile" or "Project File",
+ * make sure that "WINDOWS" and/or "WIN32" are defined somewhere, and
+ * make sure to obtain various extra files as described below.
+ *
+ * The official compilation uses the CodeWarrior Pro compiler, which
+ * includes a special project file and precompilable header file.
+ *
+ *
+ * See also "main-dos.c" and "main-ibm.c".
+ *
+ *
+ * The "lib/user/pref-win.prf" file contains keymaps, macro definitions,
+ * and/or color redefinitions.
+ *
+ * The "lib/user/font-win.prf" contains attr/char mappings for use with the
+ * normal "lib/xtra/font/ .fon" font files.
+ *
+ * The "lib/user/graf-win.prf" contains attr/char mappings for use with the
+ * special "lib/xtra/graf/ .bmp" bitmap files, which are activated by a
+ * menu item.
+ *
+ *
+ * Compiling this file, and using the resulting executable, requires
+ * several extra files not distributed with the standard Angband code.
+ * If "USE_GRAPHICS" is defined, then "readdib.h" and "readdib.c" must
+ * be placed into "src/", and the "8X8.BMP" bitmap file must be placed
+ * into "lib/xtra/graf". In any case, some "*.fon" files (including
+ * "8X13.FON" if nothing else) must be placed into "lib/xtra/font/".
+ * If "USE_SOUND" is defined, then some special library (for example,
+ * "winmm.lib") may need to be linked in, and desired "*.WAV" sound
+ * files must be placed into "lib/xtra/sound/". All of these extra
+ * files can be found in the "ext-win" archive.
+ *
+ *
+ * The "Term_xtra_win_clear()" function should probably do a low-level
+ * clear of the current window, and redraw the borders and other things,
+ * if only for efficiency. XXX XXX XXX
+ *
+ * A simpler method is needed for selecting the "tile size" for windows.
+ * XXX XXX XXX
+ *
+ * The various "warning" messages assume the existance of the "screen.w"
+ * window, I think, and only a few calls actually check for its existance,
+ * this may be okay since "NULL" means "on top of all windows". (?) The
+ * user must never be allowed to "hide" the main window, or the "menubar"
+ * will disappear. XXX XXX XXX
+ *
+ * Special "Windows Help Files" can be placed into "lib/xtra/help/" for
+ * use with the "winhelp.exe" program. These files *may* be available
+ * at the ftp site somewhere, but I have not seen them. XXX XXX XXX
+ *
+ *
+ * Initial framework (and most code) by Ben Harrison (benh@phial.com).
+ *
+ * Original code by Skirmantas Kligys (kligys@scf.usc.edu).
+ *
+ * Additional code by Ross E Becker (beckerr@cis.ohio-state.edu),
+ * and Chris R. Martin (crm7479@tam2000.tamu.edu).
+ */
+
+
+#include "angband.h"
+
+
+#ifdef WINDOWS
+
+
+/*
+ * Extract the "WIN32" flag from the compiler
+ */
+#if defined(__WIN32__) || defined(__WINNT__) || defined(__NT__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+
+
+/*
+ * Hack -- allow use of "screen saver" mode
+ */
+#define USE_SAVER
+
+/*
+ * Menu constants -- see "ANGBAND.RC"
+ */
+
+#define IDM_FILE_NEW 100
+#define IDM_FILE_OPEN 101
+#define IDM_FILE_SAVE 110
+#ifdef ALLOW_QUITTING
+# define IDM_FILE_ABORT 120
+#else
+# define IDM_FILE_SCORE 120
+#endif
+#define IDM_FILE_EXIT 121
+
+#define IDM_WINDOW_VIS_0 200
+#define IDM_WINDOW_VIS_1 201
+#define IDM_WINDOW_VIS_2 202
+#define IDM_WINDOW_VIS_3 203
+#define IDM_WINDOW_VIS_4 204
+#define IDM_WINDOW_VIS_5 205
+#define IDM_WINDOW_VIS_6 206
+#define IDM_WINDOW_VIS_7 207
+
+#define IDM_WINDOW_FONT_0 210
+#define IDM_WINDOW_FONT_1 211
+#define IDM_WINDOW_FONT_2 212
+#define IDM_WINDOW_FONT_3 213
+#define IDM_WINDOW_FONT_4 214
+#define IDM_WINDOW_FONT_5 215
+#define IDM_WINDOW_FONT_6 216
+#define IDM_WINDOW_FONT_7 217
+
+#define IDM_WINDOW_BIZ_0 230
+#define IDM_WINDOW_BIZ_1 231
+#define IDM_WINDOW_BIZ_2 232
+#define IDM_WINDOW_BIZ_3 233
+#define IDM_WINDOW_BIZ_4 234
+#define IDM_WINDOW_BIZ_5 235
+#define IDM_WINDOW_BIZ_6 236
+#define IDM_WINDOW_BIZ_7 237
+
+#define IDM_WINDOW_I_WID_0 240
+#define IDM_WINDOW_I_WID_1 241
+#define IDM_WINDOW_I_WID_2 242
+#define IDM_WINDOW_I_WID_3 243
+#define IDM_WINDOW_I_WID_4 244
+#define IDM_WINDOW_I_WID_5 245
+#define IDM_WINDOW_I_WID_6 246
+#define IDM_WINDOW_I_WID_7 247
+
+#define IDM_WINDOW_D_WID_0 250
+#define IDM_WINDOW_D_WID_1 251
+#define IDM_WINDOW_D_WID_2 252
+#define IDM_WINDOW_D_WID_3 253
+#define IDM_WINDOW_D_WID_4 254
+#define IDM_WINDOW_D_WID_5 255
+#define IDM_WINDOW_D_WID_6 256
+#define IDM_WINDOW_D_WID_7 257
+
+#define IDM_WINDOW_I_HGT_0 260
+#define IDM_WINDOW_I_HGT_1 261
+#define IDM_WINDOW_I_HGT_2 262
+#define IDM_WINDOW_I_HGT_3 263
+#define IDM_WINDOW_I_HGT_4 264
+#define IDM_WINDOW_I_HGT_5 265
+#define IDM_WINDOW_I_HGT_6 266
+#define IDM_WINDOW_I_HGT_7 267
+
+#define IDM_WINDOW_D_HGT_0 270
+#define IDM_WINDOW_D_HGT_1 271
+#define IDM_WINDOW_D_HGT_2 272
+#define IDM_WINDOW_D_HGT_3 273
+#define IDM_WINDOW_D_HGT_4 274
+#define IDM_WINDOW_D_HGT_5 275
+#define IDM_WINDOW_D_HGT_6 276
+#define IDM_WINDOW_D_HGT_7 277
+
+#define IDM_OPTIONS_OLD_GRAPHICS 400
+#define IDM_OPTIONS_NEW_GRAPHICS 401
+#define IDM_OPTIONS_ASCII_GRAPHICS 403
+#define IDM_OPTIONS_SOUND 402
+#define IDM_OPTIONS_BIGTILE 409
+#define IDM_OPTIONS_UNUSED 410
+#define IDM_OPTIONS_SAVER 411
+
+#define IDM_HELP_GENERAL 901
+#define IDM_HELP_SPOILERS 902
+
+
+/*
+ * This may need to be removed for some compilers XXX XXX XXX
+ */
+#define STRICT
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed
+ */
+#define NOCOMM /* Comm driver APIs and definitions */
+#define NOLOGERROR /* LogError() and related definitions */
+#define NOPROFILER /* Profiler APIs */
+#define NOLFILEIO /* _l* file I/O routines */
+#define NOOPENFILE /* OpenFile and related definitions */
+#define NORESOURCE /* Resource management */
+#define NOATOM /* Atom management */
+#define NOLANGUAGE /* Character test routines */
+#define NOLSTRING /* lstr* string management routines */
+#define NODBCS /* Double-byte character set routines */
+#define NOKEYBOARDINFO /* Keyboard driver routines */
+#define NOCOLOR /* COLOR_* color values */
+#define NODRAWTEXT /* DrawText() and related definitions */
+#define NOSCALABLEFONT /* Truetype scalable font support */
+#define NOMETAFILE /* Metafile support */
+#define NOSYSTEMPARAMSINFO /* SystemParametersInfo() and SPI_* definitions */
+#define NODEFERWINDOWPOS /* DeferWindowPos and related definitions */
+#define NOKEYSTATES /* MK_* message key state flags */
+#define NOWH /* SetWindowsHook and related WH_* definitions */
+#define NOCLIPBOARD /* Clipboard APIs and definitions */
+#define NOICONS /* IDI_* icon IDs */
+#define NOMDI /* MDI support */
+#define NOHELP /* Help support */
+
+/* Not defined since it breaks Borland C++ 5.5 */
+/* #define NOCTLMGR */ /* Control management and controls */
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed (Win32)
+ */
+#define WIN32_LEAN_AND_MEAN
+#define NONLS /* All NLS defines and routines */
+#define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */
+#define NOKANJI /* Kanji support stuff. */
+#define NOMCX /* Modem Configuration Extensions */
+
+/*
+ * Include the "windows" support file
+ */
+#include <windows.h>
+
+/*
+ * For IRC stuff
+ */
+#ifdef USE_WINSOCK
+#define ZSOCK_TIMER_ID 1
+#define ZSOCK_TIMER_RATE 50
+#endif
+
+
+/*
+ * Exclude parts of MMSYSTEM.H that are not needed
+ */
+#define MMNODRV /* Installable driver support */
+#define MMNOWAVE /* Waveform support */
+#define MMNOMIDI /* MIDI support */
+#define MMNOAUX /* Auxiliary audio support */
+#define MMNOJOY /* Joystick support */
+#define MMNOMCI /* MCI support */
+#define MMNOMMIO /* Multimedia file I/O support */
+#define MMNOMMSYSTEM /* General MMSYSTEM functions */
+
+/*
+ * Include some more files
+ */
+#include <mmsystem.h>
+#include <commdlg.h>
+
+/*
+ * Include the support for loading bitmaps
+ */
+#ifdef USE_GRAPHICS
+# include "readdib.h"
+#endif
+
+/*
+ * Hack -- Fake declarations from "dos.h" XXX XXX XXX
+ */
+#ifdef WIN32
+#define INVALID_FILE_NAME (DWORD)0xFFFFFFFF
+#else /* WIN32 */
+#define FA_LABEL 0x08 /* Volume label */
+#define FA_DIREC 0x10 /* Directory */
+unsigned _cdecl _dos_getfileattr(const char *, unsigned *);
+#endif /* WIN32 */
+
+/*
+ * Silliness in WIN32 drawing routine
+ */
+#ifdef WIN32
+# define MoveTo(H, X, Y) MoveToEx(H, X, Y, NULL)
+#endif /* WIN32 */
+
+/*
+ * Silliness for Windows 95
+ */
+#ifndef WS_EX_TOOLWINDOW
+# define WS_EX_TOOLWINDOW 0
+#endif
+
+/*
+ * Foreground color bits (hard-coded by DOS)
+ */
+#define VID_BLACK 0x00
+#define VID_BLUE 0x01
+#define VID_GREEN 0x02
+#define VID_CYAN 0x03
+#define VID_RED 0x04
+#define VID_MAGENTA 0x05
+#define VID_YELLOW 0x06
+#define VID_WHITE 0x07
+
+/*
+ * Bright text (hard-coded by DOS)
+ */
+#define VID_BRIGHT 0x08
+
+/*
+ * Background color bits (hard-coded by DOS)
+ */
+#define VUD_BLACK 0x00
+#define VUD_BLUE 0x10
+#define VUD_GREEN 0x20
+#define VUD_CYAN 0x30
+#define VUD_RED 0x40
+#define VUD_MAGENTA 0x50
+#define VUD_YELLOW 0x60
+#define VUD_WHITE 0x70
+
+/*
+ * Blinking text (hard-coded by DOS)
+ */
+#define VUD_BRIGHT 0x80
+
+
+
+/*
+ * Forward declare
+ */
+typedef struct _term_data term_data;
+
+/*
+ * Extra "term" data
+ *
+ * Note the use of "font_want" for the names of the font file requested by
+ * the user, and the use of "font_file" for the currently active font file.
+ *
+ * The "font_file" is uppercased, and takes the form "8X13.FON", while
+ * "font_want" can be in almost any form as long as it could be construed
+ * as attempting to represent the name of a font.
+ */
+struct _term_data
+{
+ term t;
+
+ cptr s;
+
+ HWND w;
+
+ DWORD dwStyle;
+ DWORD dwExStyle;
+
+ uint keys;
+
+ uint rows;
+ uint cols;
+
+ uint pos_x;
+ uint pos_y;
+ uint size_wid;
+ uint size_hgt;
+ uint size_ow1;
+ uint size_oh1;
+ uint size_ow2;
+ uint size_oh2;
+
+ bool size_hack;
+
+ bool xtra_hack;
+
+ bool visible;
+
+ bool bizarre;
+
+ cptr font_want;
+
+ cptr font_file;
+
+ HFONT font_id;
+
+ uint font_wid;
+ uint font_hgt;
+
+ uint tile_wid;
+ uint tile_hgt;
+};
+
+
+/*
+ * Maximum number of windows XXX XXX XXX
+ */
+#define MAX_TERM_DATA 8
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+/*
+ * Hack -- global "window creation" pointer
+ */
+static term_data *my_td;
+
+/*
+ * game in progress
+ */
+bool game_in_progress = FALSE;
+
+/*
+ * note when "open"/"new" become valid
+ */
+bool initialized = FALSE;
+
+/*
+ * screen paletted, i.e. 256 colors
+ */
+bool paletted = FALSE;
+
+/*
+ * 16 colors screen, don't use RGB()
+ */
+bool colors16 = FALSE;
+
+/*
+ * Saved instance handle
+ */
+static HINSTANCE hInstance;
+
+/*
+ * Yellow brush for the cursor
+ */
+static HBRUSH hbrYellow;
+
+/*
+ * An icon
+ */
+static HICON hIcon;
+
+/*
+ * A palette
+ */
+static HPALETTE hPal;
+
+
+#ifdef USE_SAVER
+
+/*
+ * The screen saver window
+ */
+static HWND hwndSaver;
+
+#endif /* USE_SAVER */
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Flag set once "graphics" has been initialized
+ */
+static bool can_use_graphics = FALSE;
+
+/*
+ * The global bitmap
+ */
+static DIBINIT infGraph;
+
+#ifdef USE_TRANSPARENCY
+
+/*
+ * The global bitmap mask
+ */
+static DIBINIT infMask;
+
+#endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+
+#ifdef USE_SOUND
+
+/*
+ * Flag set once "sound" has been initialized
+ */
+static bool can_use_sound = FALSE;
+
+/*
+ * An array of sound file names
+ */
+static cptr sound_file[SOUND_MAX];
+
+#endif /* USE_SOUND */
+
+
+/*
+ * Full path to ANGBAND.INI
+ */
+static cptr ini_file = NULL;
+
+/*
+ * Name of application
+ */
+static cptr AppName = "ANGBAND";
+
+/*
+ * Name of sub-window type
+ */
+static cptr AngList = "AngList";
+
+/*
+ * Directory names
+ */
+static cptr ANGBAND_DIR_XTRA_FONT;
+static cptr ANGBAND_DIR_XTRA_GRAF;
+static cptr ANGBAND_DIR_XTRA_SOUND;
+static cptr ANGBAND_DIR_XTRA_HELP;
+
+
+/*
+ * The "complex" color values
+ */
+static COLORREF win_clr[256];
+
+
+/*
+ * The "simple" color values
+ *
+ * See "main-ibm.c" for original table information
+ *
+ * The entries below are taken from the "color bits" defined above.
+ *
+ * Note that many of the choices below suck, but so do crappy monitors.
+ */
+static BYTE win_pal[256] =
+{
+ VID_BLACK, /* Dark */
+ VID_WHITE, /* White */
+ VID_CYAN, /* Slate XXX */
+ VID_RED | VID_BRIGHT, /* Orange XXX */
+ VID_RED, /* Red */
+ VID_GREEN, /* Green */
+ VID_BLUE, /* Blue */
+ VID_YELLOW, /* Umber XXX */
+ VID_BLACK | VID_BRIGHT, /* Light Dark */
+ VID_CYAN | VID_BRIGHT, /* Light Slate XXX */
+ VID_MAGENTA, /* Violet XXX */
+ VID_YELLOW | VID_BRIGHT, /* Yellow */
+ VID_MAGENTA | VID_BRIGHT, /* Light Red XXX */
+ VID_GREEN | VID_BRIGHT, /* Light Green */
+ VID_BLUE | VID_BRIGHT, /* Light Blue */
+ VID_YELLOW /* Light Umber XXX */
+};
+
+
+/*
+ * Hack -- define which keys are "special"
+ */
+static bool special_key[256];
+static bool ignore_key[256];
+
+#if 1
+/*
+ * Hack -- initialization list for "special_key"
+ */
+static byte special_key_list[] = {
+ VK_CLEAR, VK_PAUSE, VK_CAPITAL, VK_KANA,
+#ifdef JP
+ VK_JUNJA, VK_FINAL, VK_KANJI, VK_CONVERT, VK_NONCONVERT, VK_ACCEPT, VK_MODECHANGE,
+#endif
+ VK_PRIOR, VK_NEXT, VK_END, VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN,
+ VK_SELECT, VK_PRINT, VK_EXECUTE, VK_SNAPSHOT, VK_INSERT, VK_DELETE,
+ VK_HELP, VK_APPS,
+ VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10,
+ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, VK_F19, VK_F20,
+ VK_F21, VK_F22, VK_F23, VK_F24, VK_NUMLOCK, VK_SCROLL,
+ VK_ATTN, VK_CRSEL, VK_EXSEL, VK_EREOF, VK_PLAY, VK_ZOOM, VK_NONAME,
+ VK_PA1, 0
+};
+
+static byte ignore_key_list[] = {
+ VK_ESCAPE, VK_TAB, VK_SPACE,
+ 'F', 'W', 'O', 'H', /* these are menu characters.*/
+ VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN,
+ VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, VK_RMENU, 0
+};
+
+#else
+/*
+* Hack -- initialization list for "special_key"
+*
+* We ignore the modifier keys (shift, control, alt, num lock, scroll lock),
+* and the normal keys (escape, tab, return, letters, numbers, etc), but we
+* catch the keypad keys (with and without numlock set, including keypad 5),
+* the function keys (including the "menu" key which maps to F10), and the
+* "pause" key (between scroll lock and numlock). We also catch a few odd
+* keys which I do not recognize, but which are listed among keys which we
+* do catch, so they should be harmless to catch.
+*/
+static byte special_key_list[] =
+{
+ VK_CLEAR, /* 0x0C (KP<5>) */
+
+ VK_PAUSE, /* 0x13 (pause) */
+
+ VK_PRIOR, /* 0x21 (KP<9>) */
+ VK_NEXT, /* 0x22 (KP<3>) */
+ VK_END, /* 0x23 (KP<1>) */
+ VK_HOME, /* 0x24 (KP<7>) */
+ VK_LEFT, /* 0x25 (KP<4>) */
+ VK_UP, /* 0x26 (KP<8>) */
+ VK_RIGHT, /* 0x27 (KP<6>) */
+ VK_DOWN, /* 0x28 (KP<2>) */
+ VK_SELECT, /* 0x29 (?????) */
+ VK_PRINT, /* 0x2A (?????) */
+ VK_EXECUTE, /* 0x2B (?????) */
+ VK_SNAPSHOT, /* 0x2C (?????) */
+ VK_INSERT, /* 0x2D (KP<0>) */
+ VK_DELETE, /* 0x2E (KP<.>) */
+ VK_HELP, /* 0x2F (?????) */
+
+#if 0
+ VK_NUMPAD0, /* 0x60 (KP<0>) */
+ VK_NUMPAD1, /* 0x61 (KP<1>) */
+ VK_NUMPAD2, /* 0x62 (KP<2>) */
+ VK_NUMPAD3, /* 0x63 (KP<3>) */
+ VK_NUMPAD4, /* 0x64 (KP<4>) */
+ VK_NUMPAD5, /* 0x65 (KP<5>) */
+ VK_NUMPAD6, /* 0x66 (KP<6>) */
+ VK_NUMPAD7, /* 0x67 (KP<7>) */
+ VK_NUMPAD8, /* 0x68 (KP<8>) */
+ VK_NUMPAD9, /* 0x69 (KP<9>) */
+ VK_MULTIPLY, /* 0x6A (KP<*>) */
+ VK_ADD, /* 0x6B (KP<+>) */
+ VK_SEPARATOR, /* 0x6C (?????) */
+ VK_SUBTRACT, /* 0x6D (KP<->) */
+ VK_DECIMAL, /* 0x6E (KP<.>) */
+ VK_DIVIDE, /* 0x6F (KP</>) */
+#endif
+
+ VK_F1, /* 0x70 */
+ VK_F2, /* 0x71 */
+ VK_F3, /* 0x72 */
+ VK_F4, /* 0x73 */
+ VK_F5, /* 0x74 */
+ VK_F6, /* 0x75 */
+ VK_F7, /* 0x76 */
+ VK_F8, /* 0x77 */
+ VK_F9, /* 0x78 */
+ VK_F10, /* 0x79 */
+ VK_F11, /* 0x7A */
+ VK_F12, /* 0x7B */
+ VK_F13, /* 0x7C */
+ VK_F14, /* 0x7D */
+ VK_F15, /* 0x7E */
+ VK_F16, /* 0x7F */
+ VK_F17, /* 0x80 */
+ VK_F18, /* 0x81 */
+ VK_F19, /* 0x82 */
+ VK_F20, /* 0x83 */
+ VK_F21, /* 0x84 */
+ VK_F22, /* 0x85 */
+ VK_F23, /* 0x86 */
+ VK_F24, /* 0x87 */
+
+ 0
+};
+#endif
+
+
+/*
+ * Hack -- given a pathname, point at the filename
+ */
+static cptr extract_file_name(cptr s)
+{
+ cptr p;
+
+ /* Start at the end */
+ p = s + strlen(s) - 1;
+
+ /* Back up to divider */
+ while ((p >= s) && (*p != ':') && (*p != '\\')) p--;
+
+ /* Return file name */
+ return (p + 1);
+}
+
+
+/*
+ * Hack -- given a simple filename, extract the "font size" info
+ *
+ * Return a pointer to a static buffer holding the capitalized base name.
+ */
+static char *analyze_font(char *path, int *wp, int *hp)
+{
+ int wid, hgt;
+
+ char *s, *p;
+
+ /* Start at the end */
+ p = path + strlen(path) - 1;
+
+ /* Back up to divider */
+ while ((p >= path) && (*p != ':') && (*p != '\\')) --p;
+
+ /* Advance to file name */
+ ++p;
+
+ /* Capitalize */
+ for (s = p; *s; ++s)
+ {
+ /* Capitalize (be paranoid) */
+ if (islower(*s)) *s = toupper(*s);
+ }
+
+ /* Find first 'X' */
+ s = strchr(p, 'X');
+
+ /* Extract font width */
+ wid = atoi(p);
+
+ /* Extract height */
+ hgt = s ? atoi(s + 1) : 0;
+
+ /* Save results */
+ (*wp) = wid;
+ (*hp) = hgt;
+
+ /* Result */
+ return (p);
+}
+
+
+/*
+ * Check for existance of a file
+ */
+static bool check_file(cptr s)
+{
+ char path[1024];
+
+#ifdef WIN32
+
+ DWORD attrib;
+
+#else /* WIN32 */
+
+ unsigned int attrib;
+
+#endif /* WIN32 */
+
+ /* Copy it */
+ strcpy(path, s);
+
+#ifdef WIN32
+
+ /* Examine */
+ attrib = GetFileAttributes(path);
+
+ /* Require valid filename */
+ if (attrib == INVALID_FILE_NAME) return (FALSE);
+
+ /* Prohibit directory */
+ if (attrib & FILE_ATTRIBUTE_DIRECTORY) return (FALSE);
+
+#else /* WIN32 */
+
+ /* Examine and verify */
+ if (_dos_getfileattr(path, &attrib)) return (FALSE);
+
+ /* Prohibit something */
+ if (attrib & FA_LABEL) return (FALSE);
+
+ /* Prohibit directory */
+ if (attrib & FA_DIREC) return (FALSE);
+
+#endif /* WIN32 */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Check for existance of a directory
+ */
+static bool check_dir(cptr s)
+{
+ int i;
+
+ char path[1024];
+
+#ifdef WIN32
+
+ DWORD attrib;
+
+#else /* WIN32 */
+
+ unsigned int attrib;
+
+#endif /* WIN32 */
+
+ /* Copy it */
+ strcpy(path, s);
+
+ /* Check length */
+ i = strlen(path);
+
+ /* Remove trailing backslash */
+ if (i && (path[i - 1] == '\\')) path[--i] = '\0';
+
+#ifdef WIN32
+
+ /* Examine */
+ attrib = GetFileAttributes(path);
+
+ /* Require valid filename */
+ if (attrib == INVALID_FILE_NAME) return (FALSE);
+
+ /* Require directory */
+ if (!(attrib & FILE_ATTRIBUTE_DIRECTORY)) return (FALSE);
+
+#else /* WIN32 */
+
+ /* Examine and verify */
+ if (_dos_getfileattr(path, &attrib)) return (FALSE);
+
+ /* Prohibit something */
+ if (attrib & FA_LABEL) return (FALSE);
+
+ /* Require directory */
+ if (!(attrib & FA_DIREC)) return (FALSE);
+
+#endif /* WIN32 */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Validate a file
+ */
+static void validate_file(cptr s)
+{
+ /* Verify or fail */
+ if (!check_file(s))
+ {
+ quit_fmt("Cannot find required file:\n%s", s);
+ }
+}
+
+
+/*
+ * Validate a directory
+ */
+static void validate_dir(cptr s)
+{
+ /* Verify or fail */
+ if (!check_dir(s))
+ {
+ quit_fmt("Cannot find required directory:\n%s", s);
+ }
+}
+
+
+/*
+ * Get the "size" for a window
+ */
+static void term_getsize(term_data *td)
+{
+ RECT rc;
+
+ int wid, hgt;
+
+ /*
+ * The Angband window is always at least 80x24. I'm not sure if
+ * small windows work... -- pelpel
+ */
+ if (td == &data[0])
+ {
+ if (td->cols < 80) td->cols = 80;
+ if (td->rows < 24) td->rows = 24;
+ }
+
+ /* The other windows can be smaller */
+ else
+ {
+ /* Paranoia */
+ if (td->cols < 1) td->cols = 1;
+ if (td->rows < 1) td->rows = 1;
+ }
+
+ /* Paranoia */
+ if (td->cols > 255) td->cols = 255;
+ if (td->rows > 255) td->rows = 255;
+
+ /* Window sizes */
+ wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
+ hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Fake window size */
+ rc.left = 0;
+ rc.right = rc.left + wid;
+ rc.top = 0;
+ rc.bottom = rc.top + hgt;
+
+ /* XXX XXX XXX */
+ /* rc.right += 1; */
+ /* rc.bottom += 1; */
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Total size */
+ td->size_wid = rc.right - rc.left;
+ td->size_hgt = rc.bottom - rc.top;
+
+ /* See CreateWindowEx */
+ if (!td->w) return;
+
+ /* Extract actual location */
+ GetWindowRect(td->w, &rc);
+
+ /* Save the location */
+ td->pos_x = rc.left;
+ td->pos_y = rc.top;
+}
+
+
+/*
+ * Write the "prefs" for a single term
+ */
+static void save_prefs_aux(term_data *td, cptr sec_name)
+{
+ char buf[1024];
+
+ RECT rc;
+
+ /* Paranoia */
+ if (!td->w) return;
+
+ /* Visible */
+ strcpy(buf, td->visible ? "1" : "0");
+ WritePrivateProfileString(sec_name, "Visible", buf, ini_file);
+
+ /* Font */
+ strcpy(buf, td->font_file ? td->font_file : "8X13.FON");
+ WritePrivateProfileString(sec_name, "Font", buf, ini_file);
+
+ /* Bizarre */
+ strcpy(buf, td->bizarre ? "1" : "0");
+ WritePrivateProfileString(sec_name, "Bizarre", buf, ini_file);
+
+ /* Tile size (x) */
+ wsprintf(buf, "%d", td->tile_wid);
+ WritePrivateProfileString(sec_name, "TileWid", buf, ini_file);
+
+ /* Tile size (y) */
+ wsprintf(buf, "%d", td->tile_hgt);
+ WritePrivateProfileString(sec_name, "TileHgt", buf, ini_file);
+
+ /* Window size (x) */
+ wsprintf(buf, "%d", td->cols);
+ WritePrivateProfileString(sec_name, "NumCols", buf, ini_file);
+
+ /* Window size (y) */
+ wsprintf(buf, "%d", td->rows);
+ WritePrivateProfileString(sec_name, "NumRows", buf, ini_file);
+
+ /* Acquire position */
+ GetWindowRect(td->w, &rc);
+
+ /* Window position (x) */
+ wsprintf(buf, "%d", rc.left);
+ WritePrivateProfileString(sec_name, "PositionX", buf, ini_file);
+
+ /* Window position (y) */
+ wsprintf(buf, "%d", rc.top);
+ WritePrivateProfileString(sec_name, "PositionY", buf, ini_file);
+}
+
+
+/*
+ * Write the "prefs"
+ *
+ * We assume that the windows have all been initialized
+ */
+static void save_prefs(void)
+{
+ int i;
+
+ char buf[128];
+
+ /* Save the "arg_graphics" flag */
+ sprintf(buf, "%d", arg_graphics);
+ WritePrivateProfileString("Angband", "Graphics", buf, ini_file);
+
+ /* Save the "arg_bigtile" flag */
+ strcpy(buf, arg_bigtile ? "1" : "0");
+ WritePrivateProfileString("Angband", "Bigtile", buf, ini_file);
+
+ /* Save the "arg_sound" flag */
+ strcpy(buf, arg_sound ? "1" : "0");
+ WritePrivateProfileString("Angband", "Sound", buf, ini_file);
+
+#ifdef SUPPORT_GAMMA
+ /* Save the "gamma_val" */
+ sprintf(buf, "%d", gamma_val);
+ WritePrivateProfileString("Angband", "GammaVal", buf, ini_file);
+#endif /* SUPPORT_GAMMA */
+
+ /* Save window prefs */
+ for (i = 0; i < MAX_TERM_DATA; ++i)
+ {
+ term_data *td = &data[i];
+
+ sprintf(buf, "Term-%d", i);
+
+ save_prefs_aux(td, buf);
+ }
+}
+
+
+/*
+ * Load the "prefs" for a single term
+ */
+static void load_prefs_aux(term_data *td, cptr sec_name)
+{
+ char tmp[1024];
+
+ int wid, hgt;
+
+ /* Visible */
+ td->visible = (GetPrivateProfileInt(sec_name, "Visible", td->visible, ini_file) != 0);
+
+ /* Desired font, with default */
+ GetPrivateProfileString(sec_name, "Font", "8X13.FON", tmp, 127, ini_file);
+
+ /* Bizarre */
+ td->bizarre = (GetPrivateProfileInt(sec_name, "Bizarre", td->bizarre, ini_file) != 0);
+
+ /* Analyze font, save desired font name */
+ td->font_want = string_make(analyze_font(tmp, &wid, &hgt));
+
+ /* Tile size */
+ td->tile_wid = GetPrivateProfileInt(sec_name, "TileWid", wid, ini_file);
+ td->tile_hgt = GetPrivateProfileInt(sec_name, "TileHgt", hgt, ini_file);
+
+ /* Window size */
+ td->cols = GetPrivateProfileInt(sec_name, "NumCols", td->cols, ini_file);
+ td->rows = GetPrivateProfileInt(sec_name, "NumRows", td->rows, ini_file);
+
+ /* Window position */
+ td->pos_x = GetPrivateProfileInt(sec_name, "PositionX", td->pos_x, ini_file);
+ td->pos_y = GetPrivateProfileInt(sec_name, "PositionY", td->pos_y, ini_file);
+}
+
+
+/*
+ * Load the "prefs"
+ */
+static void load_prefs(void)
+{
+ int i;
+
+ char buf[1024];
+
+ /* Extract the "arg_graphics" flag */
+ arg_graphics = GetPrivateProfileInt("Angband", "Graphics", 0, ini_file);
+
+ /* Extract the "arg_bigtile" flag */
+ arg_bigtile = GetPrivateProfileInt("Angband", "Bigtile", FALSE, ini_file);
+ use_bigtile = arg_bigtile;
+
+ /* Extract the "arg_sound" flag */
+ arg_sound = (GetPrivateProfileInt("Angband", "Sound", 0, ini_file) != 0);
+
+#ifdef SUPPORT_GAMMA
+ /* Extract the "gamma_val" */
+ gamma_val = GetPrivateProfileInt("Angband", "GammaVal", 0, ini_file);
+#endif /* SUPPORT_GAMMA */
+
+ /* Load window prefs */
+ for (i = 0; i < MAX_TERM_DATA; ++i)
+ {
+ term_data *td = &data[i];
+
+ sprintf(buf, "Term-%d", i);
+
+ load_prefs_aux(td, buf);
+ }
+}
+
+
+/*
+ * Create the new global palette based on the bitmap palette
+ * (if any), and the standard 16 entry palette derived from
+ * "win_clr[]" which is used for the basic 16 Angband colors.
+ *
+ * This function is never called before all windows are ready.
+ *
+ * This function returns FALSE if the new palette could not be
+ * prepared, which should normally be a fatal error. XXX XXX
+ *
+ * Note that only some machines actually use a "palette".
+ */
+static int new_palette(void)
+{
+ HPALETTE hBmPal;
+ HPALETTE hNewPal;
+ HDC hdc;
+ int i, nEntries;
+ int pLogPalSize;
+ int lppeSize;
+ LPLOGPALETTE pLogPal;
+ LPPALETTEENTRY lppe;
+
+ term_data *td;
+
+
+ /* This makes no sense */
+ if (!paletted) return (TRUE);
+
+
+ /* No palette */
+ hBmPal = NULL;
+
+ /* No bitmap */
+ lppeSize = 0;
+ lppe = NULL;
+ nEntries = 0;
+
+#ifdef USE_GRAPHICS
+
+ /* Check the bitmap palette */
+ hBmPal = infGraph.hPalette;
+
+ /* Use the bitmap */
+ if (hBmPal)
+ {
+ lppeSize = 256 * sizeof(PALETTEENTRY);
+ lppe = (LPPALETTEENTRY)ralloc(lppeSize);
+ nEntries = GetPaletteEntries(hBmPal, 0, 255, lppe);
+ if ((nEntries == 0) || (nEntries > 220))
+ {
+ /* Warn the user */
+ plog_fmt("Unusable bitmap palette (%d entries)", nEntries);
+
+ /* Cleanup */
+ rnfree(lppe, lppeSize);
+
+ /* Fail */
+ return (FALSE);
+ }
+ }
+
+#endif
+
+ /* Size of palette */
+ pLogPalSize = sizeof(LOGPALETTE) + (nEntries + 16) * sizeof(PALETTEENTRY);
+
+ /* Allocate palette */
+ pLogPal = (LPLOGPALETTE)ralloc(pLogPalSize);
+
+ /* Version */
+ pLogPal->palVersion = 0x300;
+
+ /* Make room for bitmap and normal data */
+ pLogPal->palNumEntries = nEntries + 16;
+
+ /* Save the bitmap data */
+ for (i = 0; i < nEntries; i++)
+ {
+ pLogPal->palPalEntry[i] = lppe[i];
+ }
+
+ /* Save the normal data */
+ for (i = 0; i < 16; i++)
+ {
+ LPPALETTEENTRY p;
+
+ /* Access the entry */
+ p = &(pLogPal->palPalEntry[i + nEntries]);
+
+ /* Save the colors */
+ p->peRed = GetRValue(win_clr[i]);
+ p->peGreen = GetGValue(win_clr[i]);
+ p->peBlue = GetBValue(win_clr[i]);
+
+ /* Save the flags */
+ p->peFlags = PC_NOCOLLAPSE;
+ }
+
+ /* Free something */
+ if (lppe) rnfree(lppe, lppeSize);
+
+ /* Create a new palette, or fail */
+ hNewPal = CreatePalette(pLogPal);
+ if (!hNewPal) quit("Cannot create palette!");
+
+ /* Free the palette */
+ rnfree(pLogPal, pLogPalSize);
+
+ /* Main window */
+ td = &data[0];
+
+ /* Realize the palette */
+ hdc = GetDC(td->w);
+ SelectPalette(hdc, hNewPal, 0);
+ i = RealizePalette(hdc);
+ ReleaseDC(td->w, hdc);
+ if (i == 0) quit("Cannot realize palette!");
+
+ /* Sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+
+ hdc = GetDC(td->w);
+ SelectPalette(hdc, hNewPal, 0);
+ ReleaseDC(td->w, hdc);
+ }
+
+ /* Delete old palette */
+ if (hPal) DeleteObject(hPal);
+
+ /* Save new palette */
+ hPal = hNewPal;
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Initialize graphics
+ */
+static bool init_graphics()
+{
+ /* Initialize once */
+ /*if (can_use_graphics != arg_graphics) */
+ {
+ char buf[1024];
+ int wid, hgt;
+ cptr name;
+
+#ifdef USE_TRANSPARENCY
+
+ /* Unused */
+ PALETTEENTRY entry =
+ {
+ 0, 0, 0, 0
+ };
+ (void)entry;
+
+#endif /* USE_TRANSPARENCY */
+
+ if (arg_graphics == 2)
+ {
+ wid = 16;
+ hgt = 16;
+
+ name = "16X16.BMP";
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ wid = 8;
+ hgt = 8;
+
+ name = "8X8.BMP";
+ ANGBAND_GRAF = "old";
+ }
+
+ /* Access the bitmap file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, name);
+
+ /* Load the bitmap or quit */
+ if (!ReadDIB(data[0].w, buf, &infGraph))
+ {
+ plog_fmt("Cannot read bitmap file '%s'", name);
+ return (FALSE);
+ }
+
+ /* Save the new sizes */
+ infGraph.CellWidth = wid;
+ infGraph.CellHeight = hgt;
+
+
+#ifdef USE_TRANSPARENCY
+
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_GRAF, "mask.bmp");
+ /* Load the bitmap or quit */
+ if (!ReadDIB(data[0].w, buf, &infMask))
+ {
+ plog_fmt("Cannot read bitmap file '%s'", name);
+ return (FALSE);
+ }
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Activate a palette */
+ if (!new_palette())
+ {
+ /* Free bitmap XXX XXX XXX */
+
+ /* Oops */
+ plog("Cannot activate palette!");
+ return (FALSE);
+ }
+
+ /* Graphics available */
+ can_use_graphics = arg_graphics;
+ }
+
+ /* Result */
+ return (can_use_graphics);
+}
+
+
+/*
+ * Initialize sound
+ */
+static bool init_sound()
+{
+ /* Initialize once */
+ if (!can_use_sound)
+ {
+ int i;
+
+ char wav[128];
+ char buf[1024];
+
+ /* Prepare the sounds */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Extract name of sound file */
+ sprintf(wav, "%s.wav", angband_sound_name[i]);
+
+ /* Access the sound */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_SOUND, wav);
+
+ /* Save the sound filename, if it exists */
+ if (check_file(buf)) sound_file[i] = string_make(buf);
+ }
+
+ /* Sound available */
+ can_use_sound = TRUE;
+ }
+
+ /* Result */
+ return (can_use_sound);
+}
+
+
+
+/*
+ * Resize a window
+ */
+static void term_window_resize(term_data *td)
+{
+ /* Require window */
+ if (!td->w) return;
+
+ /* Resize the window */
+ SetWindowPos(td->w, 0, 0, 0,
+ td->size_wid, td->size_hgt,
+ SWP_NOMOVE | SWP_NOZORDER);
+
+ /* Redraw later */
+ InvalidateRect(td->w, NULL, TRUE);
+}
+
+
+
+/*
+ * Force the use of a new "font file" for a term_data
+ *
+ * This function may be called before the "window" is ready
+ *
+ * This function returns zero only if everything succeeds.
+ *
+ * Note that the "font name" must be capitalized!!!
+ */
+static errr term_force_font(term_data *td, cptr path)
+{
+ int i;
+
+ int wid, hgt;
+
+ char *base;
+
+ char buf[1024];
+
+
+ /* Forget the old font (if needed) */
+ if (td->font_id) DeleteObject(td->font_id);
+
+ /* Forget old font */
+ if (td->font_file)
+ {
+ bool used = FALSE;
+
+ /* Scan windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ /* Check "screen" */
+ if ((td != &data[i]) &&
+ (data[i].font_file) &&
+ (streq(data[i].font_file, td->font_file)))
+ {
+ used = TRUE;
+ }
+ }
+
+ /* Remove unused font resources */
+ if (!used) RemoveFontResource(td->font_file);
+
+ /* Free the old name */
+ string_free(td->font_file);
+
+ /* Forget it */
+ td->font_file = NULL;
+ }
+
+
+ /* No path given */
+ if (!path) return (1);
+
+
+ /* Local copy */
+ strcpy(buf, path);
+
+ /* Analyze font path */
+ base = analyze_font(buf, &wid, &hgt);
+
+ /* Verify suffix */
+ if (!suffix(base, ".FON")) return (1);
+
+ /* Verify file */
+ if (!check_file(buf)) return (1);
+
+ /* Load the new font */
+ if (!AddFontResource(buf)) return (1);
+
+ /* Save new font name */
+ td->font_file = string_make(base);
+
+ /* Remove the "suffix" */
+ base[strlen(base) - 4] = '\0';
+
+ /* Create the font (using the 'base' of the font file name!) */
+ td->font_id = CreateFont(hgt, wid, 0, 0, FW_DONTCARE, 0, 0, 0,
+ ANSI_CHARSET, OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ FIXED_PITCH | FF_DONTCARE, base);
+
+ /* Hack -- Unknown size */
+ if (!wid || !hgt)
+ {
+ HDC hdcDesktop;
+ HFONT hfOld;
+ TEXTMETRIC tm;
+
+ /* all this trouble to get the cell size */
+ hdcDesktop = GetDC(HWND_DESKTOP);
+ hfOld = SelectObject(hdcDesktop, td->font_id);
+ GetTextMetrics(hdcDesktop, &tm);
+ SelectObject(hdcDesktop, hfOld);
+ ReleaseDC(HWND_DESKTOP, hdcDesktop);
+
+ /* Font size info */
+ wid = tm.tmAveCharWidth;
+ hgt = tm.tmHeight;
+ }
+
+ /* Save the size info */
+ td->font_wid = wid;
+ td->font_hgt = hgt;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Allow the user to change the font for this window.
+ */
+static void term_change_font(term_data *td)
+{
+ OPENFILENAME ofn;
+
+ char tmp[1024] = "";
+
+ /* Extract a default if possible */
+ if (td->font_file) strcpy(tmp, td->font_file);
+
+ /* Ask for a choice */
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = data[0].w;
+ ofn.lpstrFilter = "Angband Font Files (*.fon)\0*.fon\0";
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = tmp;
+ ofn.nMaxFile = 128;
+ ofn.lpstrInitialDir = ANGBAND_DIR_XTRA_FONT;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
+ ofn.lpstrDefExt = "fon";
+
+ /* Force choice if legal */
+ if (GetOpenFileName(&ofn))
+ {
+ /* Force the font */
+ if (term_force_font(td, tmp))
+ {
+ /* Access the standard font file */
+ path_build(tmp, 1024, ANGBAND_DIR_XTRA_FONT, "8X13.FON");
+
+ /* Force the use of that font */
+ (void)term_force_font(td, tmp);
+ }
+
+ /* Assume not bizarre */
+ td->bizarre = FALSE;
+
+ /* Reset the tile info */
+ td->tile_wid = td->font_wid;
+ td->tile_hgt = td->font_hgt;
+
+ /* Analyze the font */
+ term_getsize(td);
+
+ /* Resize the window */
+ term_window_resize(td);
+ }
+}
+
+
+
+/*
+ * Hack -- redraw a term_data
+ */
+static void term_data_redraw(term_data *td)
+{
+ /* Activate the term */
+ Term_activate(&td->t);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Restore the term */
+ Term_activate(term_screen);
+}
+
+
+
+
+
+/*** Function hooks needed by "Term" ***/
+
+
+#if 0
+
+/*
+ * Initialize a new Term
+ */
+static void Term_init_win(term *t)
+{
+ /* XXX Unused */
+}
+
+
+/*
+ * Nuke an old Term
+ */
+static void Term_nuke_win(term *t)
+{
+ /* XXX Unused */
+}
+
+#endif
+
+
+/*
+ * Interact with the User
+ */
+static errr Term_user_win(int n)
+{
+ /* Success */
+ return (0);
+}
+
+
+#ifdef SUPPORT_GAMMA
+
+/*
+ * When set to TRUE, indicates that we can use gamma_table
+ */
+static bool gamma_table_ready = FALSE;
+
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * React to global changes
+ */
+static errr Term_xtra_win_react(void)
+{
+ int i;
+
+
+ /* Simple color */
+ if (colors16)
+ {
+ /* Save the default colors */
+ for (i = 0; i < 256; i++)
+ {
+ /* Simply accept the desired colors */
+ win_pal[i] = angband_color_table[i][0];
+ }
+ }
+
+ /* Complex color */
+ else
+ {
+ COLORREF code;
+
+ byte rv, gv, bv;
+
+ bool change = FALSE;
+
+#ifdef SUPPORT_GAMMA
+
+ static u16b old_gamma_val = 0;
+
+
+ /* React to change in the gamma value */
+ if (gamma_val != old_gamma_val)
+ {
+ /* Temporarily inactivate the gamma table */
+ gamma_table_ready = FALSE;
+
+ /* Only need to build the table if gamma exists */
+ if (gamma_val)
+ {
+ /* Rebuild the table */
+ build_gamma_table(gamma_val);
+
+ /* Activate the table */
+ gamma_table_ready = TRUE;
+ }
+
+ /* Remember the gamma value used */
+ old_gamma_val = gamma_val;
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Save the default colors */
+ for (i = 0; i < 256; i++)
+ {
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+#ifdef SUPPORT_GAMMA
+
+ /* Hack - Gamma correction */
+ if (gamma_table_ready)
+ {
+ rv = gamma_table[rv];
+ gv = gamma_table[gv];
+ bv = gamma_table[bv];
+ }
+
+#endif /* SUPPORT_GAMMA */
+
+ /* Extract a full color code */
+ code = PALETTERGB(rv, gv, bv);
+
+ /* Activate changes */
+ if (win_clr[i] != code)
+ {
+ /* Note the change */
+ change = TRUE;
+
+ /* Apply the desired color */
+ win_clr[i] = code;
+ }
+ }
+
+ /* Activate the palette if needed */
+ if (change) (void)new_palette();
+ }
+
+
+#ifdef USE_SOUND
+
+ /* Handle "arg_sound" */
+ if (use_sound != arg_sound)
+ {
+ /* Initialize (if needed) */
+ if (arg_sound && !init_sound())
+ {
+ /* Warning */
+ plog("Cannot initialize sound!");
+
+ /* Cannot enable */
+ arg_sound = FALSE;
+ }
+
+ /* Change setting */
+ use_sound = arg_sound;
+ }
+
+#endif
+
+
+#ifdef USE_GRAPHICS
+
+ /* Handle "arg_graphics" */
+ if (use_graphics != arg_graphics)
+ {
+ /* Initialize (if needed) */
+ if (arg_graphics && !init_graphics())
+ {
+ /* Warning */
+ plog("Cannot initialize graphics!");
+
+ /* Cannot enable */
+ arg_graphics = FALSE;
+ }
+
+ /* Change setting */
+ use_graphics = arg_graphics;
+
+ /* Reset visuals */
+ reset_visuals();
+ }
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Clean up windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term *old = Term;
+
+ term_data *td = &data[i];
+
+ /* Update resized windows */
+ if ((td->cols != td->t.wid) || (td->rows != td->t.hgt))
+ {
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Hack -- Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Restore */
+ Term_activate(old);
+ }
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Process at least one event
+ */
+static errr Term_xtra_win_event(int v)
+{
+ MSG msg;
+
+ /* Wait for an event */
+ if (v)
+ {
+ /* Block */
+ if (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Check for an event */
+ else
+ {
+ irc_poll();
+
+ /* Check */
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Process all pending events
+ */
+static errr Term_xtra_win_flush(void)
+{
+ MSG msg;
+
+ /* Process all pending events */
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Hack -- clear the screen
+ *
+ * Make this more efficient XXX XXX XXX
+ */
+static errr Term_xtra_win_clear(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ HDC hdc;
+ RECT rc;
+
+ /* Rectangle to erase */
+ rc.left = td->size_ow1;
+ rc.right = rc.left + td->cols * td->tile_wid;
+ rc.top = td->size_oh1;
+ rc.bottom = rc.top + td->rows * td->tile_hgt;
+
+ /* Erase it */
+ hdc = GetDC(td->w);
+ SetBkColor(hdc, RGB(0, 0, 0));
+ SelectObject(hdc, td->font_id);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Hack -- make a noise
+ */
+static errr Term_xtra_win_noise(void)
+{
+ MessageBeep(MB_ICONASTERISK);
+ return (0);
+}
+
+
+/*
+ * Hack -- make a sound
+ */
+static errr Term_xtra_win_sound(int v)
+{
+ /* Sound disabled */
+ if (!use_sound) return (1);
+
+ /* Illegal sound */
+ if ((v < 0) || (v >= SOUND_MAX)) return (1);
+
+ /* Unknown sound */
+ if (!sound_file[v]) return (1);
+
+#ifdef USE_SOUND
+
+#ifdef WIN32
+
+ /* Play the sound, catch errors */
+ return (PlaySound(sound_file[v], 0, SND_FILENAME | SND_ASYNC));
+
+#else /* WIN32 */
+
+/* Play the sound, catch errors */
+ return (sndPlaySound(sound_file[v], SND_ASYNC));
+
+#endif /* WIN32 */
+
+#endif /* USE_SOUND */
+
+ /* Oops */
+ return (1);
+}
+
+
+/*
+ * Delay for "x" milliseconds
+ */
+static int Term_xtra_win_delay(int v)
+{
+
+#ifdef WIN32
+
+ /* Sleep */
+ Sleep(v);
+
+#else /* WIN32 */
+
+ DWORD t;
+ MSG msg;
+
+ /* Final count */
+ t = GetTickCount() + v;
+
+ /* Wait for it */
+ while (GetTickCount() < t)
+ {
+ /* Handle messages */
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+#endif /* WIN32 */
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Return the HWND of the main window
+ */
+HWND get_main_hwnd()
+{
+ return data[0].w;
+}
+
+/*
+ * Do a "special thing"
+ */
+static errr Term_xtra_win(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a bell sound */
+ case TERM_XTRA_NOISE:
+ {
+ return (Term_xtra_win_noise());
+ }
+
+ /* Make a special sound */
+ case TERM_XTRA_SOUND:
+ {
+ return (Term_xtra_win_sound(v));
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ return (Term_xtra_win_event(0));
+ }
+
+ /* Process an event */
+ case TERM_XTRA_EVENT:
+ {
+ return (Term_xtra_win_event(v));
+ }
+
+ /* Flush all events */
+ case TERM_XTRA_FLUSH:
+ {
+ return (Term_xtra_win_flush());
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ return (Term_xtra_win_clear());
+ }
+
+ /* React to global changes */
+ case TERM_XTRA_REACT:
+ {
+ return (Term_xtra_win_react());
+ }
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ irc_poll();
+
+ return (Term_xtra_win_delay(v));
+ }
+
+ /* Get the current time in milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ DWORD t;
+
+ t = GetTickCount();
+ Term_xtra_long = t;
+ return 0;
+ }
+
+ /*
+ * Scans for subdirectories in a directory "scansubdir_dir"
+ * and place teh result in "scansubdir_result/scansubdir_max"
+ */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ BOOL ok;
+ HANDLE h;
+ WIN32_FIND_DATA fd;
+ for (h = FindFirstFile(format("%s\\*", scansubdir_dir), &fd), ok = 1;
+ h != INVALID_HANDLE_VALUE && ok;
+ ok = FindNextFile(h, &fd))
+ {
+ if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (strcmp(fd.cFileName, ".")) && (strcmp(fd.cFileName, "..")))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(fd.cFileName);
+ scansubdir_max++;
+ }
+ }
+
+ return 0;
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ SetWindowText(get_main_hwnd(), angband_term_name[0]); return (0);
+ }
+
+ /* Oops */
+ return 1;
+}
+
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ *
+ * Draw a "cursor" at (x,y), using a "yellow box".
+ */
+static errr Term_curs_win(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ RECT rc;
+ HDC hdc;
+
+ /* Frame the grid */
+ rc.left = x * td->tile_wid + td->size_ow1;
+ rc.right = rc.left + td->tile_wid;
+ rc.top = y * td->tile_hgt + td->size_oh1;
+ rc.bottom = rc.top + td->tile_hgt;
+
+ if (use_bigtile && x + 1 < Term->wid && Term->old->a[y][x + 1] == 255)
+ rc.right += td->tile_wid;
+
+ /* Cursor is done as a yellow "box" */
+ hdc = GetDC(data[0].w);
+ FrameRect(hdc, &rc, hbrYellow);
+ ReleaseDC(data[0].w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Low level graphics (Assumes valid input).
+ *
+ * Erase a "block" of "n" characters starting at (x,y).
+ */
+static errr Term_wipe_win(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ HDC hdc;
+ RECT rc;
+
+ /* Rectangle to erase in client coords */
+ rc.left = x * td->tile_wid + td->size_ow1;
+ rc.right = rc.left + n * td->tile_wid;
+ rc.top = y * td->tile_hgt + td->size_oh1;
+ rc.bottom = rc.top + td->tile_hgt;
+
+ hdc = GetDC(td->w);
+ SetBkColor(hdc, RGB(0, 0, 0));
+ SelectObject(hdc, td->font_id);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw several ("n") chars, with an attr, at a given location.
+ *
+ * All "graphic" data is handled by "Term_pict_win()", below.
+ *
+ * One would think there is a more efficient method for telling a window
+ * what color it should be using to draw with, but perhaps simply changing
+ * it every time is not too inefficient. XXX XXX XXX
+ */
+static errr Term_text_win(int x, int y, int n, byte a, const char *s)
+{
+ term_data *td = (term_data*)(Term->data);
+ RECT rc;
+ HDC hdc;
+
+
+ /* Total rectangle */
+ rc.left = x * td->tile_wid + td->size_ow1;
+ rc.right = rc.left + n * td->tile_wid;
+ rc.top = y * td->tile_hgt + td->size_oh1;
+ rc.bottom = rc.top + td->tile_hgt;
+
+ /* Acquire DC */
+ hdc = GetDC(td->w);
+
+ /* Background color */
+ SetBkColor(hdc, RGB(0, 0, 0));
+
+ /* Foreground color */
+ if (colors16)
+ {
+ SetTextColor(hdc, PALETTEINDEX(win_pal[a]));
+ }
+ else if (paletted)
+ {
+ SetTextColor(hdc, win_clr[a&0x0F]);
+ }
+ else
+ {
+ SetTextColor(hdc, win_clr[a]);
+ }
+
+ /* Use the font */
+ SelectObject(hdc, td->font_id);
+
+ /* Bizarre size */
+ if (td->bizarre ||
+ (td->tile_hgt != td->font_hgt) ||
+ (td->tile_wid != td->font_wid))
+ {
+ int i;
+
+ /* Erase complete rectangle */
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+
+ /* New rectangle */
+ rc.left += ((td->tile_wid - td->font_wid) / 2);
+ rc.right = rc.left + td->font_wid;
+ rc.top += ((td->tile_hgt - td->font_hgt) / 2);
+ rc.bottom = rc.top + td->font_hgt;
+
+ /* Dump each character */
+ for (i = 0; i < n; i++)
+ {
+ /* Dump the text */
+ ExtTextOut(hdc, rc.left, rc.top, 0, &rc,
+ s + i, 1, NULL);
+
+ /* Advance */
+ rc.left += td->tile_wid;
+ rc.right += td->tile_wid;
+ }
+ }
+
+ /* Normal size */
+ else
+ {
+ /* Dump the text */
+ ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE | ETO_CLIPPED, &rc,
+ s, n, NULL);
+ }
+
+ /* Release DC */
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Low level graphics. Assumes valid input.
+ *
+ * Draw an array of "special" attr/char pairs at the given location.
+ *
+ * We use the "Term_pict_win()" function for "graphic" data, which are
+ * encoded by setting the "high-bits" of both the "attr" and the "char"
+ * data. We use the "attr" to represent the "row" of the main bitmap,
+ * and the "char" to represent the "col" of the main bitmap. The use
+ * of this function is induced by the "higher_pict" flag.
+ *
+ * If "graphics" is not available, we simply "wipe" the given grids.
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_win(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_win(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_win(int x, int y, int n, const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ term_data *td = (term_data*)(Term->data);
+
+#ifdef USE_GRAPHICS
+
+ int i;
+ int x1, y1, w1, h1;
+ int x2, y2, w2, h2, tw2;
+
+# ifdef USE_TRANSPARENCY
+
+ int x3, y3;
+
+ HDC hdcMask = NULL;
+
+#ifdef USE_EGO_GRAPHICS
+
+ int x4, y4;
+
+#endif
+
+# endif /* USE_TRANSPARENCY */
+
+ HDC hdc;
+ HDC hdcSrc;
+ HBITMAP hbmSrcOld;
+
+ /* Paranoia */
+ if (!use_graphics)
+ {
+ /* Erase the grids */
+ return (Term_wipe_win(x, y, n));
+ }
+
+ /* Size of bitmap cell */
+ w1 = infGraph.CellWidth;
+ h1 = infGraph.CellHeight;
+
+ /* Size of window cell */
+ w2 = td->tile_wid;
+ h2 = td->tile_hgt;
+ tw2 = w2;
+
+ /* big tile mode */
+ if (use_bigtile) tw2 *= 2;
+
+ /* Location of window cell */
+ x2 = x * w2 + td->size_ow1;
+ y2 = y * h2 + td->size_oh1;
+
+ /* Info */
+ hdc = GetDC(td->w);
+
+ /* More info */
+ hdcSrc = CreateCompatibleDC(hdc);
+ hbmSrcOld = SelectObject(hdcSrc, infGraph.hBitmap);
+
+# ifdef USE_TRANSPARENCY
+
+ if (arg_graphics == 2)
+ {
+ hdcMask = CreateCompatibleDC(hdc);
+ SelectObject(hdcMask, infMask.hBitmap);
+ }
+
+# endif /* USE_TRANSPARENCY */
+
+ /* Draw attr/char pairs */
+ for (i = 0; i < n; i++, x2 += w2)
+ {
+ byte a = ap[i];
+ char c = cp[i];
+
+ /* Extract picture */
+ int row = (a & 0x7F);
+ int col = (c & 0x7F);
+
+ /* Location of bitmap cell */
+ x1 = col * w1;
+ y1 = row * h1;
+
+# ifdef USE_TRANSPARENCY
+
+ if (arg_graphics == 2)
+ {
+ x3 = (tcp[i] & 0x7F) * w1;
+ y3 = (tap[i] & 0x7F) * h1;
+
+ /* Perfect size */
+ if ((w1 == tw2) && (h1 == h2))
+ {
+ /* Copy the terrain picture from the bitmap to the window */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, SRCCOPY);
+
+ /* Mask out the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, SRCAND);
+
+ /* Draw the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCPAINT);
+
+#ifdef USE_EGO_GRAPHICS
+ if (ecp[i] != 0 && eap[i] != 0)
+ {
+ x4 = (ecp[i] & 0x7F) * w1;
+ y4 = (eap[i] & 0x7F) * h1;
+
+ /* Mask out the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcMask, x4, y4, SRCAND);
+
+ /* Draw the tile */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x4, y4, SRCPAINT);
+ }
+#endif
+ }
+
+ /* Need to stretch */
+ else
+ {
+ /* Set the correct mode for stretching the tiles */
+ SetStretchBltMode(hdc, COLORONCOLOR);
+
+ /* Copy the terrain picture from the bitmap to the window */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, w1, h1, SRCCOPY);
+
+ /* Only draw if terrain and overlay are different */
+ if ((x1 != x3) || (y1 != y3))
+ {
+ /* Mask out the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, w1, h1, SRCAND);
+
+ /* Draw the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCPAINT);
+ }
+
+#ifdef USE_EGO_GRAPHICS
+ if (ecp[i] != 0 && eap[i] != 0)
+ {
+ x4 = (ecp[i] & 0x7F) * w1;
+ y4 = (eap[i] & 0x7F) * h1;
+
+ /* Mask out the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x4, y4, w1, h1, SRCAND);
+
+ /* Draw the tile */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x4, y4, w1, h1, SRCPAINT);
+ }
+#endif
+ }
+ }
+ else
+
+# endif /* USE_TRANSPARENCY */
+
+ {
+ /* Perfect size */
+ if ((w1 == tw2) && (h1 == h2))
+ {
+ /* Copy the picture from the bitmap to the window */
+ BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCCOPY);
+ }
+
+ /* Need to stretch */
+ else
+ {
+ /* Set the correct mode for stretching the tiles */
+ SetStretchBltMode(hdc, COLORONCOLOR);
+
+ /* Copy the picture from the bitmap to the window */
+ StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCCOPY);
+ }
+ }
+ }
+
+ /* Release */
+ SelectObject(hdcSrc, hbmSrcOld);
+ DeleteDC(hdcSrc);
+
+# ifdef USE_TRANSPARENCY
+
+ if (arg_graphics == 2)
+ {
+ /* Release */
+ SelectObject(hdcMask, hbmSrcOld);
+ DeleteDC(hdcMask);
+ }
+
+# endif /* USE_TRANSPARENCY */
+
+ /* Release */
+ ReleaseDC(td->w, hdc);
+
+#else /* USE_GRAPHICS */
+
+ /* Just erase this grid */
+ return (Term_wipe_win(x, y, n));
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return 0;
+}
+
+
+/*** Other routines ***/
+
+
+/*
+ * Create and initialize a "term_data" given a title
+ */
+static void term_data_link(term_data *td)
+{
+ term *t = &td->t;
+
+ /* Initialize the term */
+ term_init(t, td->cols, td->rows, td->keys);
+
+ /* Use a "software" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Use "Term_pict" for "graphic" data */
+ t->higher_pict = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+#if 0
+ /* Prepare the init/nuke hooks */
+ t->init_hook = Term_init_win;
+ t->nuke_hook = Term_nuke_win;
+#endif
+
+ /* Prepare the template hooks */
+ t->user_hook = Term_user_win;
+ t->xtra_hook = Term_xtra_win;
+ t->curs_hook = Term_curs_win;
+ t->wipe_hook = Term_wipe_win;
+ t->text_hook = Term_text_win;
+ t->pict_hook = Term_pict_win;
+
+ /* Remember where we came from */
+ t->data = (vptr)(td);
+}
+
+
+/*
+ * Create the windows
+ *
+ * First, instantiate the "default" values, then read the "ini_file"
+ * to over-ride selected values, then create the windows, and fonts.
+ *
+ * Must use SW_SHOW not SW_SHOWNA, since on 256 color display
+ * must make active to realize the palette. XXX XXX XXX
+ */
+static void init_windows(void)
+{
+ int i;
+
+ term_data *td;
+
+ char buf[1024];
+
+
+ /* Main window */
+ td = &data[0];
+ WIPE(td, term_data);
+ td->s = angband_term_name[0];
+ td->keys = 1024;
+ td->rows = 24;
+ td->cols = 80;
+ td->visible = TRUE;
+ td->size_ow1 = 2;
+ td->size_ow2 = 2;
+ td->size_oh1 = 2;
+ td->size_oh2 = 2;
+ td->pos_x = 7 * 30;
+ td->pos_y = 7 * 20;
+
+ /* Sub windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+ WIPE(td, term_data);
+ td->s = angband_term_name[i];
+ td->keys = 16;
+ td->rows = 24;
+ td->cols = 80;
+ td->visible = FALSE;
+ td->size_ow1 = 1;
+ td->size_ow2 = 1;
+ td->size_oh1 = 1;
+ td->size_oh2 = 1;
+ td->pos_x = (7 - i) * 30;
+ td->pos_y = (7 - i) * 20;
+ }
+
+
+ /* Load prefs */
+ load_prefs();
+
+
+ /* Main window (need these before term_getsize gets called) */
+ td = &data[0];
+ td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU |
+ WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION |
+ WS_VISIBLE);
+ td->dwExStyle = 0;
+ td->visible = TRUE;
+
+ /* Sub windows (need these before term_getsize gets called) */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+ td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU);
+ td->dwExStyle = (WS_EX_TOOLWINDOW);
+ }
+
+
+ /* All windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ td = &data[i];
+
+ /* Access the standard font file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_FONT, td->font_want);
+
+ /* Activate the chosen font */
+ if (term_force_font(td, buf))
+ {
+ /* Access the standard font file */
+ path_build(buf, 1024, ANGBAND_DIR_XTRA_FONT, "8X13.FON");
+
+ /* Force the use of that font */
+ (void)term_force_font(td, buf);
+
+ /* Oops */
+ td->tile_wid = 8;
+ td->tile_hgt = 13;
+
+ /* Assume not bizarre */
+ td->bizarre = FALSE;
+ }
+
+ /* Analyze the font */
+ term_getsize(td);
+
+ /* Resize the window */
+ term_window_resize(td);
+ }
+
+
+ /* Sub windows (reverse order) */
+ for (i = MAX_TERM_DATA - 1; i >= 1; --i)
+ {
+ td = &data[i];
+
+ my_td = td;
+ td->w = CreateWindowEx(td->dwExStyle, AngList,
+ td->s, td->dwStyle,
+ td->pos_x, td->pos_y,
+ td->size_wid, td->size_hgt,
+ HWND_DESKTOP, NULL, hInstance, NULL);
+ my_td = NULL;
+ if (!td->w) quit("Failed to create sub-window");
+
+ if (td->visible)
+ {
+ td->size_hack = TRUE;
+ ShowWindow(td->w, SW_SHOW);
+ td->size_hack = FALSE;
+ }
+
+ term_data_link(td);
+ angband_term[i] = &td->t;
+
+ if (td->visible)
+ {
+ /* Activate the window */
+ SetActiveWindow(td->w);
+
+ /* Bring window to top */
+ SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+
+
+ /* Main window */
+ td = &data[0];
+
+ /* Main window */
+ my_td = td;
+ td->w = CreateWindowEx(td->dwExStyle, AppName,
+ td->s, td->dwStyle,
+ td->pos_x, td->pos_y,
+ td->size_wid, td->size_hgt,
+ HWND_DESKTOP, NULL, hInstance, NULL);
+ my_td = NULL;
+ if (!td->w) quit("Failed to create Angband window");
+
+ term_data_link(td);
+ angband_term[0] = &td->t;
+
+ /* Activate the main window */
+ SetActiveWindow(td->w);
+
+ /* Bring main window back to top */
+ SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+
+
+ /* New palette XXX XXX XXX */
+ (void)new_palette();
+
+
+ /* Create a "brush" for drawing the "cursor" */
+ hbrYellow = CreateSolidBrush(win_clr[TERM_YELLOW]);
+
+
+ /* Process pending messages */
+ (void)Term_xtra_win_flush();
+}
+
+
+
+/*
+ * Prepare the menus
+ */
+static void setup_menus(void)
+{
+ int i;
+
+ HMENU hm = GetMenu(data[0].w);
+
+
+ /* Menu "File", Disable all */
+ EnableMenuItem(hm, IDM_FILE_NEW,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_FILE_OPEN,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_FILE_SAVE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+#ifdef ALLOW_QUITTING
+ EnableMenuItem(hm, IDM_FILE_ABORT,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+#else
+ EnableMenuItem(hm, IDM_FILE_SCORE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+#endif /* ALLOW_QUITTING */
+ EnableMenuItem(hm, IDM_FILE_EXIT,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ /* No character available */
+ if (!character_generated)
+ {
+ /* Menu "File", Item "New" */
+ EnableMenuItem(hm, IDM_FILE_NEW,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ /* Menu "File", Item "Open" */
+ EnableMenuItem(hm, IDM_FILE_OPEN,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /* A character available */
+ if (character_generated)
+ {
+ /* Menu "File", Item "Save" */
+ EnableMenuItem(hm, IDM_FILE_SAVE,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+#ifdef ALLOW_QUITTING
+
+ /* Menu "File", Item "Abort" */
+ EnableMenuItem(hm, IDM_FILE_ABORT,
+ MF_BYCOMMAND | MF_ENABLED);
+
+#else
+
+ /* Menu "File", Item "Score" */
+ if (initialized && character_generated && !character_icky)
+ {
+ EnableMenuItem(hm, IDM_FILE_SCORE,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+#endif
+
+ /* Menu "File", Item "Exit" */
+ EnableMenuItem(hm, IDM_FILE_EXIT,
+ MF_BYCOMMAND | MF_ENABLED);
+
+
+ /* Menu "Window::Visibility" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ CheckMenuItem(hm, IDM_WINDOW_VIS_0 + i,
+ (data[i].visible ? MF_CHECKED : MF_UNCHECKED));
+
+ EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /* Menu "Window::Font" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+ }
+
+ /* Menu "Window::Bizarre Display" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_BIZ_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ CheckMenuItem(hm, IDM_WINDOW_BIZ_0 + i,
+ (data[i].bizarre ? MF_CHECKED : MF_UNCHECKED));
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_BIZ_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Increase Tile Width" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Decrease Tile Width" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Increase Tile Height" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Window::Decrease Tile Height" */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ if (data[i].visible)
+ {
+ EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i,
+ MF_BYCOMMAND | MF_ENABLED);
+
+ }
+ }
+
+ /* Menu "Options", disable all */
+ EnableMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_ASCII_GRAPHICS,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_BIGTILE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_SOUND,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_UNUSED,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hm, IDM_OPTIONS_SAVER,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+
+ /* Menu "Options", update all */
+ CheckMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS,
+ (arg_graphics == 1 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS,
+ (arg_graphics == 2 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_ASCII_GRAPHICS,
+ (arg_graphics == 0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_BIGTILE,
+ (arg_bigtile ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_SOUND,
+ (arg_sound ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_UNUSED,
+ (0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_SAVER,
+ (hwndSaver ? MF_CHECKED : MF_UNCHECKED));
+
+#ifdef USE_GRAPHICS
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS, MF_ENABLED);
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS, MF_ENABLED);
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_ASCII_GRAPHICS, MF_ENABLED);
+ /* Menu "Options", Item "Graphics" */
+ EnableMenuItem(hm, IDM_OPTIONS_BIGTILE, MF_ENABLED);
+#endif
+
+#ifdef USE_SOUND
+ /* Menu "Options", Item "Sound" */
+ EnableMenuItem(hm, IDM_OPTIONS_SOUND, MF_ENABLED);
+#endif
+
+#ifdef USE_SAVER
+ /* Menu "Options", Item "ScreenSaver" */
+ EnableMenuItem(hm, IDM_OPTIONS_SAVER,
+ MF_BYCOMMAND | MF_ENABLED);
+#endif
+
+}
+
+
+/*
+ * Check for double clicked (or dragged) savefile
+ *
+ * Apparently, Windows copies the entire filename into the first
+ * piece of the "command line string". Perhaps we should extract
+ * the "basename" of that filename and append it to the "save" dir.
+ */
+static void check_for_save_file(LPSTR cmd_line)
+{
+ char *s, *p;
+
+ /* First arg */
+ s = cmd_line;
+
+ /* Second arg */
+ p = strchr(s, ' ');
+
+ /* Tokenize, advance */
+ if (p) *p++ = '\0';
+
+ /* No args */
+ if (!*s) return;
+
+ /* Extract filename */
+ strcat(savefile, s);
+
+ /* Validate the file */
+ validate_file(savefile);
+
+ /* Game in progress */
+ game_in_progress = TRUE;
+
+ /* Play game */
+ play_game(FALSE);
+}
+
+
+/*
+ * Process a menu command
+ */
+static void process_menus(WORD wCmd)
+{
+ int i;
+
+ term_data *td;
+
+ OPENFILENAME ofn;
+
+ /* Analyze */
+ switch (wCmd)
+ {
+ /* New game */
+ case IDM_FILE_NEW:
+ {
+ if (!initialized)
+ {
+ plog("You cannot do that yet...");
+ }
+ else if (game_in_progress)
+ {
+ plog("You can't start a new game while you're still playing!");
+ }
+ else
+ {
+ game_in_progress = TRUE;
+ Term_flush();
+ play_game(TRUE);
+ quit(NULL);
+ }
+ break;
+ }
+
+ /* Open game */
+ case IDM_FILE_OPEN:
+ {
+ if (!initialized)
+ {
+ plog("You cannot do that yet...");
+ }
+ else if (game_in_progress)
+ {
+ plog("You can't open a new game while you're still playing!");
+ }
+ else
+ {
+ memset(&ofn, 0, sizeof(ofn));
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
+ ofn.lStructSize = sizeof(OPENFILENAME) - (sizeof(void*) + 2 * sizeof(DWORD));
+#else // old headers
+ofn.lStructSize = sizeof(OPENFILENAME);
+#endif
+ ofn.hwndOwner = data[0].w;
+ ofn.lpstrFilter = "Save Files (*.)\0*\0";
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = savefile;
+ ofn.nMaxFile = 1024;
+ ofn.lpstrInitialDir = ANGBAND_DIR_SAVE;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
+
+ if (GetOpenFileName(&ofn))
+ {
+ /* Load 'savefile' */
+ validate_file(savefile);
+ game_in_progress = TRUE;
+ Term_flush();
+ play_game(FALSE);
+ quit(NULL);
+ }
+ }
+ break;
+ }
+
+ /* Save game */
+ case IDM_FILE_SAVE:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+ }
+ else
+ {
+ plog("You may not do that right now.");
+ }
+ break;
+ }
+
+ /* Exit */
+ case IDM_FILE_EXIT:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+ }
+ quit(NULL);
+ break;
+ }
+
+#ifdef ALLOW_QUITTING
+
+ /* Abort */
+ case IDM_FILE_ABORT:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* XXX XXX XXX */
+ if (MessageBox(data[0].w,
+ "Your character will be not saved!", "Warning",
+ MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
+ {
+ break;
+ }
+ }
+ quit(NULL);
+ break;
+ }
+
+#else
+
+ /* Score */
+ case IDM_FILE_SCORE:
+ {
+ char buf[1024];
+
+ /* Paranoia */
+ if (!initialized || character_icky ||
+ !game_in_progress || !character_generated)
+ {
+ /* Can't happen but just in case */
+ plog("You may not do that right now.");
+
+ break;
+ }
+
+ /* Build the pathname of the score file */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
+
+ /* Hack - open the score file for reading */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ /* Paranoia - No score file */
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file is not available.");
+
+ break;
+ }
+
+ /* Mega-Hack - prevent various functions XXX XXX XXX */
+ initialized = FALSE;
+
+ /* Save screen */
+ screen_save();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prepare scores */
+ if (game_in_progress && character_generated)
+ {
+ predict_score();
+ }
+
+#if 0 /* I don't like this - pelpel */
+
+ /* Mega-Hack - No current player XXX XXX XXX XXX */
+ else
+ {
+ display_scores_aux(0, MAX_HISCORES, -1, NULL);
+ }
+
+#endif
+
+ /* Close the high score file */
+ (void)fd_close(highscore_fd);
+
+ /* Forget the fd */
+ highscore_fd = -1;
+
+ /* Restore screen */
+ screen_load();
+
+ /* Hack - Flush it */
+ Term_fresh();
+
+ /* Mega-Hack - We are ready again */
+ initialized = TRUE;
+
+ /* Done */
+ break;
+ }
+
+#endif
+
+ case IDM_WINDOW_VIS_0:
+ {
+ plog("You are not allowed to do that!");
+
+ break;
+ }
+
+ /* Window visibility */
+ case IDM_WINDOW_VIS_1:
+ case IDM_WINDOW_VIS_2:
+ case IDM_WINDOW_VIS_3:
+ case IDM_WINDOW_VIS_4:
+ case IDM_WINDOW_VIS_5:
+ case IDM_WINDOW_VIS_6:
+ case IDM_WINDOW_VIS_7:
+ {
+ i = wCmd - IDM_WINDOW_VIS_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ if (!td->visible)
+ {
+ td->visible = TRUE;
+ ShowWindow(td->w, SW_SHOW);
+ term_data_redraw(td);
+ }
+ else
+ {
+ td->visible = FALSE;
+ ShowWindow(td->w, SW_HIDE);
+ }
+
+ break;
+ }
+
+ /* Window fonts */
+ case IDM_WINDOW_FONT_0:
+ case IDM_WINDOW_FONT_1:
+ case IDM_WINDOW_FONT_2:
+ case IDM_WINDOW_FONT_3:
+ case IDM_WINDOW_FONT_4:
+ case IDM_WINDOW_FONT_5:
+ case IDM_WINDOW_FONT_6:
+ case IDM_WINDOW_FONT_7:
+ {
+ i = wCmd - IDM_WINDOW_FONT_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ term_change_font(td);
+
+ break;
+ }
+
+ /* Bizarre Display */
+ case IDM_WINDOW_BIZ_0:
+ case IDM_WINDOW_BIZ_1:
+ case IDM_WINDOW_BIZ_2:
+ case IDM_WINDOW_BIZ_3:
+ case IDM_WINDOW_BIZ_4:
+ case IDM_WINDOW_BIZ_5:
+ case IDM_WINDOW_BIZ_6:
+ case IDM_WINDOW_BIZ_7:
+ {
+ i = wCmd - IDM_WINDOW_BIZ_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->bizarre = !td->bizarre;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Increase Tile Width */
+ case IDM_WINDOW_I_WID_0:
+ case IDM_WINDOW_I_WID_1:
+ case IDM_WINDOW_I_WID_2:
+ case IDM_WINDOW_I_WID_3:
+ case IDM_WINDOW_I_WID_4:
+ case IDM_WINDOW_I_WID_5:
+ case IDM_WINDOW_I_WID_6:
+ case IDM_WINDOW_I_WID_7:
+ {
+ i = wCmd - IDM_WINDOW_I_WID_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_wid += 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Decrease Tile Height */
+ case IDM_WINDOW_D_WID_0:
+ case IDM_WINDOW_D_WID_1:
+ case IDM_WINDOW_D_WID_2:
+ case IDM_WINDOW_D_WID_3:
+ case IDM_WINDOW_D_WID_4:
+ case IDM_WINDOW_D_WID_5:
+ case IDM_WINDOW_D_WID_6:
+ case IDM_WINDOW_D_WID_7:
+ {
+ i = wCmd - IDM_WINDOW_D_WID_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_wid -= 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Increase Tile Height */
+ case IDM_WINDOW_I_HGT_0:
+ case IDM_WINDOW_I_HGT_1:
+ case IDM_WINDOW_I_HGT_2:
+ case IDM_WINDOW_I_HGT_3:
+ case IDM_WINDOW_I_HGT_4:
+ case IDM_WINDOW_I_HGT_5:
+ case IDM_WINDOW_I_HGT_6:
+ case IDM_WINDOW_I_HGT_7:
+ {
+ i = wCmd - IDM_WINDOW_I_HGT_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_hgt += 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ /* Decrease Tile Height */
+ case IDM_WINDOW_D_HGT_0:
+ case IDM_WINDOW_D_HGT_1:
+ case IDM_WINDOW_D_HGT_2:
+ case IDM_WINDOW_D_HGT_3:
+ case IDM_WINDOW_D_HGT_4:
+ case IDM_WINDOW_D_HGT_5:
+ case IDM_WINDOW_D_HGT_6:
+ case IDM_WINDOW_D_HGT_7:
+ {
+ i = wCmd - IDM_WINDOW_D_HGT_0;
+
+ if ((i < 0) || (i >= MAX_TERM_DATA)) break;
+
+ td = &data[i];
+
+ td->tile_hgt -= 1;
+
+ term_getsize(td);
+
+ term_window_resize(td);
+
+ break;
+ }
+
+ case IDM_OPTIONS_OLD_GRAPHICS:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Set "arg_graphics" */
+ arg_graphics = 1;
+
+ /* React to changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ case IDM_OPTIONS_NEW_GRAPHICS:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Set "arg_graphics" */
+ arg_graphics = 2;
+
+ /* React to changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+ case IDM_OPTIONS_ASCII_GRAPHICS:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Set "ASCII Graphics" */
+ arg_graphics = 0;
+ /* React to Changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ case IDM_OPTIONS_BIGTILE:
+ {
+ term_data *td = &data[0];
+
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Toggle "arg_sound" */
+ arg_bigtile = !arg_bigtile;
+
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw later */
+ InvalidateRect(td->w, NULL, TRUE);
+
+ break;
+ }
+
+ case IDM_OPTIONS_SOUND:
+ {
+ /* Paranoia */
+ if (!inkey_flag)
+ {
+ plog("You may not do that right now.");
+ break;
+ }
+
+ /* Toggle "arg_sound" */
+ arg_sound = !arg_sound;
+
+ /* React to changes */
+ Term_xtra_win_react();
+
+ /* Hack -- Force redraw */
+ Term_key_push(KTRL('R'));
+
+ break;
+ }
+
+ case IDM_OPTIONS_UNUSED:
+ {
+ /* Unused for now XXX XXX XXX */
+
+ break;
+ }
+
+#ifdef USE_SAVER
+
+ case IDM_OPTIONS_SAVER:
+ {
+ if (hwndSaver)
+ {
+ DestroyWindow(hwndSaver);
+ hwndSaver = NULL;
+ }
+ else
+ {
+ /* Create a screen scaver window */
+ hwndSaver = CreateWindowEx(WS_EX_TOPMOST, "WindowsScreenSaverClass",
+ "Angband Screensaver",
+ WS_POPUP | WS_MAXIMIZE | WS_VISIBLE,
+ 0, 0, GetSystemMetrics(SM_CXSCREEN),
+ GetSystemMetrics(SM_CYSCREEN),
+ NULL, NULL, hInstance, NULL);
+
+ if (hwndSaver)
+ {
+ /* Push the window to the bottom XXX XXX XXX */
+ SetWindowPos(hwndSaver, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ else
+ {
+ plog("Failed to create saver window");
+ }
+ }
+ break;
+ }
+
+#endif
+
+ case IDM_HELP_GENERAL:
+ {
+ char buf[1024];
+ char tmp[1024];
+ path_build(tmp, 1024, ANGBAND_DIR_XTRA_HELP, "angband.hlp");
+ if (check_file(tmp))
+ {
+ sprintf(buf, "winhelp.exe %s", tmp);
+ WinExec(buf, SW_NORMAL);
+ }
+ else
+ {
+ plog_fmt("Cannot find help file: %s", tmp);
+ plog("Use the online help files instead.");
+ }
+ break;
+ }
+
+ case IDM_HELP_SPOILERS:
+ {
+ char buf[1024];
+ char tmp[1024];
+ path_build(tmp, 1024, ANGBAND_DIR_XTRA_HELP, "spoilers.hlp");
+ if (check_file(tmp))
+ {
+ sprintf(buf, "winhelp.exe %s", tmp);
+ WinExec(buf, SW_NORMAL);
+ }
+ else
+ {
+ plog_fmt("Cannot find help file: %s", tmp);
+ plog("Use the online help files instead.");
+ }
+ break;
+ }
+ }
+}
+
+
+
+#ifdef __MWERKS__
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#else /* __MWERKS__ */
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#endif /* __MWERKS__ */
+{
+ PAINTSTRUCT ps;
+ HDC hdc;
+ term_data *td;
+ MINMAXINFO FAR *lpmmi;
+ RECT rc;
+ int i;
+
+
+ /* Acquire proper "term_data" info */
+ td = (term_data *)GetWindowLong(hWnd, 0);
+
+ /* Handle message */
+ switch (uMsg)
+ {
+ /* XXX XXX XXX */
+ case WM_NCCREATE:
+ {
+ SetWindowLong(hWnd, 0, (LONG)(my_td));
+ break;
+ }
+
+ /* XXX XXX XXX */
+ case WM_CREATE:
+ {
+ return 0;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ lpmmi = (MINMAXINFO FAR *)lParam;
+
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ /* Minimum window size is 8x2 */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 8 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 2 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save minimum size */
+ lpmmi->ptMinTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
+
+ /* Maximum window size */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 255 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 255 * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Paranoia */
+ rc.right += (td->tile_wid - 1);
+ rc.bottom += (td->tile_hgt - 1);
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save maximum size */
+ lpmmi->ptMaxSize.x = rc.right - rc.left;
+ lpmmi->ptMaxSize.y = rc.bottom - rc.top;
+
+ /* Save maximum size */
+ lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top;
+
+ return 0;
+ }
+
+ case WM_PAINT:
+ {
+ BeginPaint(hWnd, &ps);
+ if (td) term_data_redraw(td);
+ EndPaint(hWnd, &ps);
+ ValidateRect(hWnd, NULL);
+ return 0;
+ }
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ {
+ /* Unused */
+ /* BYTE KeyState = 0x00; */
+
+ bool mc = FALSE;
+ bool ms = FALSE;
+ bool ma = FALSE;
+
+ /* Extract the modifiers */
+ if (GetKeyState(VK_CONTROL) & 0x8000) mc = TRUE;
+ if (GetKeyState(VK_SHIFT) & 0x8000) ms = TRUE;
+ if (GetKeyState(VK_MENU) & 0x8000) ma = TRUE;
+
+ /* Handle "special" keys */
+ if (special_key[(byte)(wParam)] || (ma && !ignore_key[(byte)(wParam)]) )
+ {
+ /* Begin the macro trigger */
+ Term_keypress(31);
+
+ /* Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Extract "scan code" */
+ i = LOBYTE(HIWORD(lParam));
+
+ /* Introduce the scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[i / 16]);
+ Term_keypress(hexsym[i % 16]);
+
+ /* End the macro trigger */
+ Term_keypress(13);
+
+ return 0;
+ }
+
+ break;
+ }
+
+ case WM_CHAR:
+ {
+ Term_keypress(wParam);
+ return 0;
+ }
+
+ case WM_INITMENU:
+ {
+ setup_menus();
+ return 0;
+ }
+
+ case WM_CLOSE:
+ {
+ if (game_in_progress && character_generated)
+ {
+ /* Hack -- Forget messages */
+ msg_flag = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+ }
+ quit(NULL);
+ return 0;
+ }
+
+ case WM_QUIT:
+ {
+ quit(NULL);
+ return 0;
+ }
+
+ case WM_COMMAND:
+ {
+ process_menus(LOWORD(wParam));
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ /* it was sent from inside CreateWindowEx */
+ if (!td->w) return 1;
+
+ /* was sent from WM_SIZE */
+ if (td->size_hack) return 1;
+
+ switch (wParam)
+ {
+ case SIZE_MINIMIZED:
+ {
+ /* Hide sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ if (data[i].visible) ShowWindow(data[i].w, SW_HIDE);
+ }
+ return 0;
+ }
+
+ case SIZE_MAXIMIZED:
+ {
+ /* fall through XXX XXX XXX */
+ }
+
+ case SIZE_RESTORED:
+ {
+ uint cols = (LOWORD(lParam) - td->size_ow1 - td->size_ow2) / td->tile_wid;
+ uint rows = (HIWORD(lParam) - td->size_oh1 - td->size_oh2) / td->tile_hgt;
+
+ /* New size */
+ if ((td->cols != cols) || (td->rows != rows))
+ {
+ /* save the new size */
+ td->cols = cols;
+ td->rows = rows;
+
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw later */
+ InvalidateRect(td->w, NULL, TRUE);
+ }
+
+ td->size_hack = TRUE;
+
+ /* Restore sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ if (data[i].visible) ShowWindow(data[i].w, SW_SHOWNOACTIVATE);
+ }
+
+ td->size_hack = FALSE;
+
+ return 0;
+ }
+ }
+ break;
+ }
+
+ case WM_PALETTECHANGED:
+ {
+ /* Ignore if palette change caused by itself */
+ if ((HWND)wParam == hWnd) return 0;
+
+ /* Fall through... */
+ }
+
+ case WM_QUERYNEWPALETTE:
+ {
+ if (!paletted) return 0;
+
+ hdc = GetDC(hWnd);
+
+ SelectPalette(hdc, hPal, FALSE);
+
+ i = RealizePalette(hdc);
+
+ /* if any palette entries changed, repaint the window. */
+ if (i) InvalidateRect(hWnd, NULL, TRUE);
+
+ ReleaseDC(hWnd, hdc);
+
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ {
+ if (wParam && !HIWORD(lParam))
+ {
+ /* Do something to sub-windows */
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ SetWindowPos(data[i].w, hWnd, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ /* Focus on main window */
+ SetFocus(hWnd);
+
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+#ifdef __MWERKS__
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#else /* __MWERKS__ */
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#endif /* __MWERKS__ */
+{
+ term_data *td;
+ MINMAXINFO FAR *lpmmi;
+ RECT rc;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ int i;
+
+
+ /* Acquire proper "term_data" info */
+ td = (term_data *)GetWindowLong(hWnd, 0);
+
+ /* Process message */
+ switch (uMsg)
+ {
+ /* XXX XXX XXX */
+ case WM_NCCREATE:
+ {
+ SetWindowLong(hWnd, 0, (LONG)(my_td));
+ break;
+ }
+
+ /* XXX XXX XXX */
+ case WM_CREATE:
+ {
+ return 0;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ lpmmi = (MINMAXINFO FAR *)lParam;
+
+ /* Minimum size */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 8 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 2 * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save the minimum size */
+ lpmmi->ptMinTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
+
+ /* Maximum window size */
+ rc.left = rc.top = 0;
+ rc.right = rc.left + 80 * td->tile_wid + td->size_ow1 + td->size_ow2;
+ rc.bottom = rc.top + 24 * td->tile_hgt + td->size_oh1 + td->size_oh2;
+
+ /* Paranoia */
+ rc.right += (td->tile_wid - 1);
+ rc.bottom += (td->tile_hgt - 1);
+
+ /* Adjust */
+ AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
+
+ /* Save maximum size */
+ lpmmi->ptMaxSize.x = rc.right - rc.left;
+ lpmmi->ptMaxSize.y = rc.bottom - rc.top;
+
+ /* Save the maximum size */
+ lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
+ lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top;
+
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ /* this message was sent before WM_NCCREATE */
+ if (!td) return 1;
+
+ /* it was sent from inside CreateWindowEx */
+ if (!td->w) return 1;
+
+ /* was sent from inside WM_SIZE */
+ if (td->size_hack) return 1;
+
+ td->size_hack = TRUE;
+
+ td->cols = (LOWORD(lParam) - td->size_ow1 - td->size_ow2) / td->tile_wid;
+ td->rows = (HIWORD(lParam) - td->size_oh1 - td->size_oh2) / td->tile_hgt;
+
+ term_getsize(td);
+
+ MoveWindow(hWnd, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, TRUE);
+
+ td->size_hack = FALSE;
+
+ return 0;
+ }
+
+ case WM_PAINT:
+ {
+ BeginPaint(hWnd, &ps);
+ if (td) term_data_redraw(td);
+ EndPaint(hWnd, &ps);
+ return 0;
+ }
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ {
+ /* Unused */
+ /* BYTE KeyState = 0x00; */
+
+ bool mc = FALSE;
+ bool ms = FALSE;
+ bool ma = FALSE;
+
+ /* Extract the modifiers */
+ if (GetKeyState(VK_CONTROL) & 0x8000) mc = TRUE;
+ if (GetKeyState(VK_SHIFT) & 0x8000) ms = TRUE;
+ if (GetKeyState(VK_MENU) & 0x8000) ma = TRUE;
+
+ /* Handle "special" keys */
+ if (special_key[(byte)(wParam)] || (ma && !ignore_key[(byte)(wParam)]) )
+ {
+ /* Begin the macro trigger */
+ Term_keypress(31);
+
+ /* Send the modifiers */
+ if (mc) Term_keypress('C');
+ if (ms) Term_keypress('S');
+ if (ma) Term_keypress('A');
+
+ /* Extract "scan code" */
+ i = LOBYTE(HIWORD(lParam));
+
+ /* Introduce the scan code */
+ Term_keypress('x');
+
+ /* Encode the hexidecimal scan code */
+ Term_keypress(hexsym[i / 16]);
+ Term_keypress(hexsym[i % 16]);
+
+ /* End the macro trigger */
+ Term_keypress(13);
+
+ return 0;
+ }
+
+ break;
+ }
+
+ case WM_CHAR:
+ {
+ Term_keypress(wParam);
+ return 0;
+ }
+
+ case WM_PALETTECHANGED:
+ {
+ /* ignore if palette change caused by itself */
+ if ((HWND)wParam == hWnd) return FALSE;
+ /* otherwise, fall through!!! */
+ }
+
+ case WM_QUERYNEWPALETTE:
+ {
+ if (!paletted) return 0;
+ hdc = GetDC(hWnd);
+ SelectPalette(hdc, hPal, FALSE);
+ i = RealizePalette(hdc);
+ /* if any palette entries changed, repaint the window. */
+ if (i) InvalidateRect(hWnd, NULL, TRUE);
+ ReleaseDC(hWnd, hdc);
+ return 0;
+ }
+
+ case WM_NCLBUTTONDOWN:
+ {
+
+#ifdef HTCLOSE
+ if (wParam == HTCLOSE) wParam = HTSYSMENU;
+#endif /* HTCLOSE */
+
+ if (wParam == HTSYSMENU)
+ {
+ if (td->visible)
+ {
+ td->visible = FALSE;
+ ShowWindow(td->w, SW_HIDE);
+ }
+
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+#ifdef USE_SAVER
+
+#define MOUSE_SENS 40
+
+#ifdef __MWERKS__
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+#else /* __MWERKS__ */
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+WPARAM wParam, LPARAM lParam)
+#endif /* __MWERKS__ */
+{
+ static int iMouse = 0;
+ static WORD xMouse = 0;
+ static WORD yMouse = 0;
+
+ int dx, dy;
+
+
+ /* Process */
+ switch (uMsg)
+ {
+ /* XXX XXX XXX */
+ case WM_NCCREATE:
+ {
+ break;
+ }
+
+ case WM_SETCURSOR:
+ {
+ SetCursor(NULL);
+ return 0;
+ }
+
+#if 0
+ case WM_ACTIVATE:
+ {
+ if (LOWORD(wParam) == WA_INACTIVE) break;
+
+ /* else fall through */
+ }
+#endif
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_KEYDOWN:
+ {
+ SendMessage(hWnd, WM_CLOSE, 0, 0);
+ return 0;
+ }
+
+ case WM_MOUSEMOVE:
+ {
+ if (iMouse)
+ {
+ dx = LOWORD(lParam) - xMouse;
+ dy = HIWORD(lParam) - yMouse;
+
+ if (dx < 0) dx = -dx;
+ if (dy < 0) dy = -dy;
+
+ if ((dx > MOUSE_SENS) || (dy > MOUSE_SENS))
+ {
+ SendMessage(hWnd, WM_CLOSE, 0, 0);
+ }
+ }
+
+ /* Save last location */
+ iMouse = 1;
+ xMouse = LOWORD(lParam);
+ yMouse = HIWORD(lParam);
+
+ return 0;
+ }
+
+ case WM_CLOSE:
+ {
+ DestroyWindow(hwndSaver);
+ hwndSaver = NULL;
+ return 0;
+ }
+ }
+
+ /* Oops */
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+#endif /* USE_SAVER */
+
+
+
+
+
+/*** Temporary Hooks ***/
+
+
+/*
+ * Display warning message (see "z-util.c")
+ */
+static void hack_plog(cptr str)
+{
+ /* Give a warning */
+ if (str)
+ {
+ MessageBox(NULL, str, "Warning",
+ MB_ICONEXCLAMATION | MB_OK);
+ }
+}
+
+
+/*
+ * Display error message and quit (see "z-util.c")
+ */
+static void hack_quit(cptr str)
+{
+ /* Give a warning */
+ if (str)
+ {
+ MessageBox(NULL, str, "Error",
+ MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
+ }
+
+ /* Unregister the classes */
+ UnregisterClass(AppName, hInstance);
+
+ /* Destroy the icon */
+ if (hIcon) DestroyIcon(hIcon);
+
+ /* Exit */
+ exit(0);
+}
+
+
+
+/*** Various hooks ***/
+
+
+/*
+ * Display warning message (see "z-util.c")
+ */
+static void hook_plog(cptr str)
+{
+ /* Warning */
+ if (str)
+ {
+ MessageBox(data[0].w, str, "Warning",
+ MB_ICONEXCLAMATION | MB_OK);
+ }
+}
+
+
+/*
+ * Display error message and quit (see "z-util.c")
+ */
+static void hook_quit(cptr str)
+{
+ int i;
+
+
+ /* Give a warning */
+ if (str)
+ {
+ MessageBox(data[0].w, str, "Error",
+ MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
+ }
+
+
+ /* Save the preferences */
+ save_prefs();
+
+
+ /*** Could use 'Term_nuke_win()' XXX XXX XXX */
+
+ /* Destroy all windows */
+ for (i = MAX_TERM_DATA - 1; i >= 0; --i)
+ {
+ term_force_font(&data[i], NULL);
+ if (data[i].font_want) string_free(data[i].font_want);
+ if (data[i].w) DestroyWindow(data[i].w);
+ data[i].w = 0;
+ }
+
+
+ /*** Free some other stuff ***/
+
+ DeleteObject(hbrYellow);
+
+ if (hPal) DeleteObject(hPal);
+
+ UnregisterClass(AppName, hInstance);
+
+ if (hIcon) DestroyIcon(hIcon);
+
+ exit(0);
+}
+
+/*** Initialize ***/
+
+
+/*
+ * Init some stuff
+ */
+static void init_stuff(void)
+{
+ int i;
+
+ char path[1024];
+
+
+ /* Get program name with full path */
+ GetModuleFileName(hInstance, path, 512);
+
+ /* Get the name of the "*.ini" file */
+ strcpy(path + strlen(path) - 4, ".INI");
+
+ /* Save the the name of the ini-file */
+ ini_file = string_make(path);
+
+ /* Validate the ini-file */
+ validate_file(ini_file);
+
+ /* Analyze the path */
+ i = strlen(path);
+
+ /* Get the path */
+ for (; i > 0; i--)
+ {
+ if (path[i] == '\\')
+ {
+ /* End of path */
+ break;
+ }
+ }
+
+ /* Add "lib" to the path */
+ strcpy(path + i + 1, "lib\\");
+
+ /* Validate the path */
+ validate_dir(path);
+
+ /*** Initialise the file paths ***/
+
+ /* Start with standard ones */
+ init_file_paths(path);
+
+ /* Build the "font" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "font");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_FONT = string_make(path);
+
+
+ /*** Validate the paths to ensure we have a working install ***/
+
+ validate_dir(ANGBAND_DIR_APEX);
+ validate_dir(ANGBAND_DIR_BONE);
+ validate_dir(ANGBAND_DIR_DATA);
+ validate_dir(ANGBAND_DIR_EDIT);
+ validate_dir(ANGBAND_DIR_FILE);
+ validate_dir(ANGBAND_DIR_HELP);
+ validate_dir(ANGBAND_DIR_INFO);
+ validate_dir(ANGBAND_DIR_NOTE);
+ validate_dir(ANGBAND_DIR_SAVE);
+ validate_dir(ANGBAND_DIR_PREF);
+ validate_dir(ANGBAND_DIR_USER);
+ validate_dir(ANGBAND_DIR_XTRA);
+ validate_dir(ANGBAND_DIR_CMOV);
+ validate_dir(ANGBAND_DIR_XTRA_FONT);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_FONT, "8X13.FON");
+
+ /* Hack -- Validate the basic font */
+ validate_file(path);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Build the "graf" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "graf");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_GRAF = string_make(path);
+
+ /* Validate the "graf" directory */
+ validate_dir(ANGBAND_DIR_XTRA_GRAF);
+
+ /* Build the filename */
+ path_build(path, 1024, ANGBAND_DIR_XTRA_GRAF, "8X8.BMP");
+
+ /* Hack -- Validate the basic graf */
+ validate_file(path);
+
+#endif
+
+
+#ifdef USE_SOUND
+
+ /* Build the "sound" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "sound");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_SOUND = string_make(path);
+
+ /* Validate the "sound" directory */
+ validate_dir(ANGBAND_DIR_XTRA_SOUND);
+
+#endif
+
+
+ /* Build the "help" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "help");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_HELP = string_make(path);
+
+ /* Validate the "help" directory */
+ /* validate_dir(ANGBAND_DIR_XTRA_HELP); */
+}
+
+int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
+ LPSTR lpCmdLine, int nCmdShow)
+{
+ int i;
+
+ WNDCLASS wc;
+ HDC hdc;
+ MSG msg;
+
+ /* Save globally */
+ hInstance = hInst;
+
+ /* Initialize */
+ if (hPrevInst == NULL)
+ {
+ wc.style = CS_CLASSDC;
+ wc.lpfnWndProc = AngbandWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 4; /* one long pointer to term_data */
+ wc.hInstance = hInst;
+ wc.hIcon = hIcon = LoadIcon(hInst, AppName);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = GetStockObject(BLACK_BRUSH);
+ wc.lpszMenuName = AppName;
+ wc.lpszClassName = AppName;
+
+ if (!RegisterClass(&wc)) exit(1);
+
+ wc.lpfnWndProc = AngbandListProc;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = AngList;
+
+ if (!RegisterClass(&wc)) exit(2);
+
+#ifdef USE_SAVER
+
+ wc.style = CS_VREDRAW | CS_HREDRAW | CS_SAVEBITS | CS_DBLCLKS;
+ wc.lpfnWndProc = AngbandSaverProc;
+ wc.hCursor = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "WindowsScreenSaverClass";
+
+ if (!RegisterClass(&wc)) exit(3);
+
+#endif
+
+ }
+
+ /* Temporary hooks */
+ plog_aux = hack_plog;
+ quit_aux = hack_quit;
+ core_aux = hack_quit;
+
+ /* Prepare the filepaths */
+ init_stuff();
+
+ /* Initialize the keypress analyzer */
+ for (i = 0; special_key_list[i]; ++i)
+ {
+ special_key[special_key_list[i]] = TRUE;
+ }
+
+ /* Determine if display is 16/256/true color */
+ hdc = GetDC(NULL);
+ colors16 = (GetDeviceCaps(hdc, BITSPIXEL) == 4);
+ paletted = ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) ? TRUE : FALSE);
+ ReleaseDC(NULL, hdc);
+
+ /* Initialize the colors */
+ for (i = 0; i < 256; i++)
+ {
+ byte rv, gv, bv;
+
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Extract the "complex" code */
+ win_clr[i] = PALETTERGB(rv, gv, bv);
+
+ /* Save the "simple" code */
+ angband_color_table[i][0] = win_pal[i];
+ }
+
+ /* Prepare the windows */
+ init_windows();
+
+ /* Activate hooks */
+ plog_aux = hook_plog;
+ quit_aux = hook_quit;
+ core_aux = hook_quit;
+
+ /* Set the system suffix */
+ ANGBAND_SYS = "win";
+
+
+ /* Set the keyboard suffix */
+ if (7 != GetKeyboardType(0))
+ ANGBAND_KEYBOARD = "0";
+ else
+ {
+ /* Japanese keyboard */
+ switch (GetKeyboardType(1))
+ {
+ case 0x0D01:
+ case 0x0D02:
+ case 0x0D03:
+ case 0x0D04:
+ case 0x0D05:
+ case 0x0D06:
+ /* NEC PC-98x1 */
+ ANGBAND_KEYBOARD = "NEC98";
+ break;
+ default:
+ /* PC/AT */
+ ANGBAND_KEYBOARD = "JAPAN";
+ }
+ }
+
+ /* Initialize */
+ init_angband();
+
+ /* Prompt the user */
+ prt("", 23, 0);
+ prt("[Press any key to proceed]", 23, 27);
+ Term_fresh();
+
+ inkey();
+
+ /* We are now initialized */
+ initialized = TRUE;
+
+ /* Did the user double click on a save file? */
+ check_for_save_file(lpCmdLine);
+
+ game_in_progress = TRUE;
+ play_game(FALSE);
+
+ /* Prompt the user */
+ Term_fresh();
+
+ /* Process messages forever */
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ /* Initialize the keypress analyzer */
+ for (i = 0; ignore_key_list[i]; ++i)
+ {
+ ignore_key[ignore_key_list[i]] = TRUE;
+ }
+
+ /* Paranoia */
+ quit(NULL);
+
+ /* Paranoia */
+ return (0);
+}
+
+
+#endif /* WINDOWS */
+
+
diff --git a/src/main-x11.c b/src/main-x11.c
new file mode 100644
index 00000000..c15f5982
--- /dev/null
+++ b/src/main-x11.c
@@ -0,0 +1,3295 @@
+/* File: main-x11.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with UNIX/X11 computers.
+ *
+ * To use this file, compile with "USE_X11" defined, and link against all
+ * the various "X11" libraries which may be needed.
+ *
+ * See also "main-xaw.c".
+ *
+ * Part of this file provides a user interface package composed of several
+ * pseudo-objects, including "metadpy" (a display), "infowin" (a window),
+ * "infoclr" (a color), and "infofnt" (a font). Actually, the package was
+ * originally much more interesting, but it was bastardized to keep this
+ * file simple.
+ *
+ * The rest of this file is an implementation of "main-xxx.c" for X11.
+ *
+ * Most of this file is by Ben Harrison (benh@phial.com).
+ */
+
+/*
+ * The following shell script can be used to launch Angband, assuming that
+ * it was extracted into "~/Angband", and compiled using "USE_X11", on a
+ * Linux machine, with a 1280x1024 screen, using 6 windows (with the given
+ * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142,
+ * and without graphics (add "-g" for graphics). Just copy this comment
+ * into a file, remove the leading " * " characters (and the head/tail of
+ * this comment), and make the file executable.
+ *
+ *
+ * #!/bin/csh
+ *
+ * # Describe attempt
+ * echo "Launching angband..."
+ * sleep 2
+ *
+ * # Main window
+ * setenv ANGBAND_X11_FONT_0 10x20
+ * setenv ANGBAND_X11_AT_X_0 5
+ * setenv ANGBAND_X11_AT_Y_0 510
+ *
+ * # Message window
+ * setenv ANGBAND_X11_FONT_1 8x13
+ * setenv ANGBAND_X11_AT_X_1 5
+ * setenv ANGBAND_X11_AT_Y_1 22
+ * setenv ANGBAND_X11_ROWS_1 35
+ *
+ * # Inventory window
+ * setenv ANGBAND_X11_FONT_2 8x13
+ * setenv ANGBAND_X11_AT_X_2 635
+ * setenv ANGBAND_X11_AT_Y_2 182
+ * setenv ANGBAND_X11_ROWS_3 23
+ *
+ * # Equipment window
+ * setenv ANGBAND_X11_FONT_3 8x13
+ * setenv ANGBAND_X11_AT_X_3 635
+ * setenv ANGBAND_X11_AT_Y_3 22
+ * setenv ANGBAND_X11_ROWS_3 12
+ *
+ * # Monster recall window
+ * setenv ANGBAND_X11_FONT_4 6x13
+ * setenv ANGBAND_X11_AT_X_4 817
+ * setenv ANGBAND_X11_AT_Y_4 847
+ * setenv ANGBAND_X11_COLS_4 76
+ * setenv ANGBAND_X11_ROWS_4 11
+ *
+ * # Object recall window
+ * setenv ANGBAND_X11_FONT_5 6x13
+ * setenv ANGBAND_X11_AT_X_5 817
+ * setenv ANGBAND_X11_AT_Y_5 520
+ * setenv ANGBAND_X11_COLS_5 76
+ * setenv ANGBAND_X11_ROWS_5 24
+ *
+ * # The build directory
+ * cd ~/Angband
+ *
+ * # Gamma correction
+ * setenv ANGBAND_X11_GAMMA 142
+ *
+ * # Launch Angband
+ * ./src/angband -mx11 -- -n6 &
+ *
+ */
+
+#include "angband.h"
+
+#ifdef USE_X11
+
+#ifndef __MAKEDEPEND__
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/Xatom.h>
+#endif /* __MAKEDEPEND__ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <sys/time.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+/*
+ * Include some helpful X11 code.
+ */
+#include "maid-x11.c"
+
+/*
+ * Hack -- avoid some compiler warnings
+ */
+#define IGNORE_UNUSED_FUNCTIONS
+
+
+/*
+ * Notes on Colors:
+ *
+ * 1) On a monochrome (or "fake-monochrome") display, all colors
+ * will be "cast" to "fg," except for the bg color, which is,
+ * obviously, cast to "bg". Thus, one can ignore this setting.
+ *
+ * 2) Because of the inner functioning of the color allocation
+ * routines, colors may be specified as (a) a typical color name,
+ * (b) a hexidecimal color specification (preceded by a pound sign),
+ * or (c) by strings such as "fg", "bg", "zg".
+ *
+ * 3) Due to the workings of the init routines, many colors
+ * may also be dealt with by their actual pixel values. Note that
+ * the pixel with all bits set is "zg = (1<<metadpy->depth)-1", which
+ * is not necessarily either black or white.
+ */
+
+
+
+/**** Generic Types ****/
+
+
+/*
+ * An X11 pixell specifier
+ */
+typedef unsigned long Pixell;
+
+/*
+ * The structures defined below
+ */
+typedef struct metadpy metadpy;
+typedef struct infowin infowin;
+typedef struct infoclr infoclr;
+typedef struct infofnt infofnt;
+
+
+/*
+ * A structure summarizing a given Display.
+ *
+ * - The Display itself
+ * - The default Screen for the display
+ * - The virtual root (usually just the root)
+ * - The default colormap (from a macro)
+ *
+ * - The "name" of the display
+ *
+ * - The socket to listen to for events
+ *
+ * - The width of the display screen (from a macro)
+ * - The height of the display screen (from a macro)
+ * - The bit depth of the display screen (from a macro)
+ *
+ * - The black Pixell (from a macro)
+ * - The white Pixell (from a macro)
+ *
+ * - The background Pixell (default: black)
+ * - The foreground Pixell (default: white)
+ * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly)
+ *
+ * - Bit Flag: Force all colors to black and white (default: !color)
+ * - Bit Flag: Allow the use of color (default: depth > 1)
+ * - Bit Flag: We created 'dpy', and so should nuke it when done.
+ */
+struct metadpy
+{
+ Display *dpy;
+ Screen *screen;
+ Window root;
+ Colormap cmap;
+
+ char *name;
+
+ int fd;
+
+ uint width;
+ uint height;
+ uint depth;
+
+ Pixell black;
+ Pixell white;
+
+ Pixell bg;
+ Pixell fg;
+ Pixell zg;
+
+uint mono:
+ 1;
+uint color:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+/*
+ * A Structure summarizing Window Information.
+ *
+ * I assume that a window is at most 30000 pixels on a side.
+ * I assume that the root windw is also at most 30000 square.
+ *
+ * - The Window
+ * - The current Input Event Mask
+ *
+ * - The location of the window
+ * - The width, height of the window
+ * - The border width of this window
+ *
+ * - Byte: 1st Extra byte
+ *
+ * - Bit Flag: This window is currently Mapped
+ * - Bit Flag: This window needs to be redrawn
+ * - Bit Flag: This window has been resized
+ *
+ * - Bit Flag: We should nuke 'win' when done with it
+ *
+ * - Bit Flag: 1st extra flag
+ * - Bit Flag: 2nd extra flag
+ * - Bit Flag: 3rd extra flag
+ * - Bit Flag: 4th extra flag
+ */
+struct infowin
+{
+ Window win;
+ long mask;
+
+ s16b ox, oy;
+
+ s16b x, y;
+ s16b w, h;
+ u16b b;
+
+ byte byte1;
+
+uint mapped:
+ 1;
+uint redraw:
+ 1;
+uint resize:
+ 1;
+
+uint nuke:
+ 1;
+
+uint flag1:
+ 1;
+uint flag2:
+ 1;
+uint flag3:
+ 1;
+uint flag4:
+ 1;
+};
+
+
+
+
+
+
+/*
+ * A Structure summarizing Operation+Color Information
+ *
+ * - The actual GC corresponding to this info
+ *
+ * - The Foreground Pixell Value
+ * - The Background Pixell Value
+ *
+ * - Num (0-15): The operation code (As in Clear, Xor, etc)
+ * - Bit Flag: The GC is in stipple mode
+ * - Bit Flag: Destroy 'gc' at Nuke time.
+ */
+struct infoclr
+{
+ GC gc;
+
+ Pixell fg;
+ Pixell bg;
+
+uint code:
+ 4;
+uint stip:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+/*
+ * A Structure to Hold Font Information
+ *
+ * - The 'XFontStruct*' (yields the 'Font')
+ *
+ * - The font name
+ *
+ * - The default character width
+ * - The default character height
+ * - The default character ascent
+ *
+ * - Byte: Pixel offset used during fake mono
+ *
+ * - Flag: Force monospacing via 'wid'
+ * - Flag: Nuke info when done
+ */
+struct infofnt
+{
+ XFontStruct *info;
+
+ cptr name;
+
+ s16b wid;
+ s16b twid;
+ s16b hgt;
+ s16b asc;
+
+ byte off;
+
+uint mono:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+
+/**** Generic Macros ****/
+
+
+
+/* Set current metadpy (Metadpy) to 'M' */
+#define Metadpy_set(M) \
+Metadpy = M
+
+
+/* Initialize 'M' using Display 'D' */
+#define Metadpy_init_dpy(D) \
+Metadpy_init_2(D,cNULL)
+
+/* Initialize 'M' using a Display named 'N' */
+#define Metadpy_init_name(N) \
+Metadpy_init_2((Display*)(NULL),N)
+
+/* Initialize 'M' using the standard Display */
+#define Metadpy_init() \
+Metadpy_init_name("")
+
+
+/* Init an infowin by giving father as an (info_win*) (or NULL), and data */
+#define Infowin_init_dad(D,X,Y,W,H,B,FG,BG) \
+Infowin_init_data(((D) ? ((D)->win) : (Window)(None)), \
+X,Y,W,H,B,FG,BG)
+
+
+/* Init a top level infowin by pos,size,bord,Colors */
+#define Infowin_init_top(X,Y,W,H,B,FG,BG) \
+Infowin_init_data(None,X,Y,W,H,B,FG,BG)
+
+
+/* Request a new standard window by giving Dad infowin and X,Y,W,H */
+#define Infowin_init_std(D,X,Y,W,H,B) \
+Infowin_init_dad(D,X,Y,W,H,B,Metadpy->fg,Metadpy->bg)
+
+
+/* Set the current Infowin */
+#define Infowin_set(I) \
+(Infowin = (I))
+
+
+/* Set the current Infoclr */
+#define Infoclr_set(C) \
+(Infoclr = (C))
+
+
+#define Infoclr_init_ppo(F,B,O,M) \
+Infoclr_init_data(F,B,O,M)
+
+#define Infoclr_init_cco(F,B,O,M) \
+Infoclr_init_ppo(Infoclr_Pixell(F),Infoclr_Pixell(B),O,M)
+
+#define Infoclr_init_ppn(F,B,O,M) \
+Infoclr_init_ppo(F,B,Infoclr_Opcode(O),M)
+
+#define Infoclr_init_ccn(F,B,O,M) \
+Infoclr_init_cco(F,B,Infoclr_Opcode(O),M)
+
+
+/* Set the current infofnt */
+#define Infofnt_set(I) \
+(Infofnt = (I))
+
+
+/* Errr: Expose Infowin */
+#define Infowin_expose() \
+(!(Infowin->redraw = 1))
+
+/* Errr: Unxpose Infowin */
+#define Infowin_unexpose() \
+(Infowin->redraw = 0)
+
+
+
+/**** Generic Globals ****/
+
+
+/*
+ * The "default" values
+ */
+static metadpy metadpy_default;
+
+
+/*
+ * The "current" variables
+ */
+static metadpy *Metadpy = &metadpy_default;
+static infowin *Infowin = (infowin*)(NULL);
+static infoclr *Infoclr = (infoclr*)(NULL);
+static infofnt *Infofnt = (infofnt*)(NULL);
+
+
+/**** Generic code ****/
+
+/*
+ * Simple routine to save the state of the game when the display connection
+ * is broken. Remember, you cannot do anything in this function that will
+ * generate X protocol requests.
+ */
+int x_io_error_handler(Display *d)
+{
+ /* We have nothing to save */
+ if (!character_generated) return 0;
+
+ save_dungeon();
+ save_player();
+
+ return 0;
+}
+
+/*
+ * Calculate how much space there is in the key queue for the current term.
+ */
+int Term_queue_space(void)
+{
+ /* Find the gap if the tail is before the head. */
+ int space = Term->key_tail - Term->key_head;
+
+ /* Otherwise, add in the extra for looping. */
+ if (space <= 0) space = Term->key_size - space;
+
+ /* The last space is never used as that would be interpreted as leaving
+ * no pending keypresses. */
+ return space -1;
+}
+
+
+/*
+ * Add a series of keypresses to the "queue".
+ *
+ * Return any errors generated by Term_keypress() in doing so, or SUCCESS
+ * if there are none.
+ *
+ * Catch the "out of space" error before anything is printed.
+ *
+ * NB: The keys added here will be interpreted by any macros or keymaps.
+ */
+errr type_string(char *str, uint len)
+{
+ char *s;
+
+ term *old = Term;
+
+ /* Paranoia - no string. */
+ if (!str) return 5;
+
+ /* Hack - calculate the string length here if none given. */
+ if (!len) len = strlen(str);
+
+ /* Activate the main window, as all pastes go there. */
+ Term_activate(term_screen);
+
+ /* Not enough space for the string. */
+ if (Term_queue_space() <= (int)len)
+ return 7;
+
+ for (s = str; s < str + len; s++)
+ {
+ errr err = Term_keypress(*s);
+
+ /* Catch errors other than "str[i] == 0", which is ignored. */
+ if (err && err != -1) return err;
+ }
+
+ /* Activate the original window. */
+ Term_activate(old);
+
+ return 0;
+}
+
+
+/*
+ * Init the current metadpy, with various initialization stuff.
+ *
+ * Inputs:
+ * dpy: The Display* to use (if NULL, create it)
+ * name: The name of the Display (if NULL, the current)
+ *
+ * Notes:
+ * If 'name' is NULL, but 'dpy' is set, extract name from dpy
+ * If 'dpy' is NULL, then Create the named Display
+ * If 'name' is NULL, and so is 'dpy', use current Display
+ *
+ * Return -1 if no Display given, and none can be opened.
+ */
+static errr Metadpy_init_2(Display *dpy, cptr name)
+{
+ metadpy *m = Metadpy;
+
+ /*** Open the display if needed ***/
+
+ /* If no Display given, attempt to Create one */
+ if (!dpy)
+ {
+ /* Attempt to open the display */
+ dpy = XOpenDisplay(name);
+
+ /* Failure */
+ if (!dpy) return ( -1);
+
+ /* We will have to nuke it when done */
+ m->nuke = 1;
+ }
+
+ /* Since the Display was given, use it */
+ else
+ {
+ /* We will not have to nuke it when done */
+ m->nuke = 0;
+ }
+
+ XSetIOErrorHandler(x_io_error_handler);
+
+ /*** Save some information ***/
+
+ /* Save the Display itself */
+ m->dpy = dpy;
+
+ /* Get the Screen and Virtual Root Window */
+ m->screen = DefaultScreenOfDisplay(dpy);
+ m->root = RootWindowOfScreen(m->screen);
+
+ /* Get the default colormap */
+ m->cmap = DefaultColormapOfScreen(m->screen);
+
+ /* Extract the true name of the display */
+ m->name = DisplayString(dpy);
+
+ /* Extract the fd */
+ m->fd = ConnectionNumber(Metadpy->dpy);
+
+ /* Save the Size and Depth of the screen */
+ m->width = WidthOfScreen(m->screen);
+ m->height = HeightOfScreen(m->screen);
+ m->depth = DefaultDepthOfScreen(m->screen);
+
+ /* Save the Standard Colors */
+ m->black = BlackPixelOfScreen(m->screen);
+ m->white = WhitePixelOfScreen(m->screen);
+
+ /*** Make some clever Guesses ***/
+
+ /* Guess at the desired 'fg' and 'bg' Pixell's */
+ m->bg = m->black;
+ m->fg = m->white;
+
+ /* Calculate the Maximum allowed Pixel value. */
+ m->zg = (1 << m->depth) - 1;
+
+ /* Save various default Flag Settings */
+ m->color = ((m->depth > 1) ? 1 : 0);
+ m->mono = ((m->color) ? 0 : 1);
+
+ /* Return "success" */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Nuke the current metadpy
+ */
+static errr Metadpy_nuke(void)
+{
+ metadpy *m = Metadpy;
+
+
+ /* If required, Free the Display */
+ if (m->nuke)
+ {
+ /* Close the Display */
+ XCloseDisplay(m->dpy);
+
+ /* Forget the Display */
+ m->dpy = (Display*)(NULL);
+
+ /* Do not nuke it again */
+ m->nuke = 0;
+ }
+
+ /* Return Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * General Flush/ Sync/ Discard routine
+ */
+static errr Metadpy_update(int flush, int sync, int discard)
+{
+ /* Flush if desired */
+ if (flush) XFlush(Metadpy->dpy);
+
+ /* Sync if desired, using 'discard' */
+ if (sync) XSync(Metadpy->dpy, discard);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Make a simple beep
+ */
+static errr Metadpy_do_beep(void)
+{
+ /* Make a simple beep */
+ XBell(Metadpy->dpy, 100);
+
+ return (0);
+}
+
+
+
+/*
+ * Set the name (in the title bar) of Infowin
+ */
+static errr Infowin_set_name(cptr name)
+{
+ Status st;
+ XTextProperty tp;
+ char buf[128];
+ char *bp = buf;
+ strcpy(buf, name);
+ st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp);
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Set the icon name of Infowin
+ */
+static errr Infowin_set_icon_name(cptr name)
+{
+ Status st;
+ XTextProperty tp;
+ char buf[128];
+ char *bp = buf;
+ strcpy(buf, name);
+ st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) XSetWMIconName(Metadpy->dpy, Infowin->win, &tp);
+ return (0);
+}
+
+
+/*
+ * Nuke Infowin
+ */
+static errr Infowin_nuke(void)
+{
+ infowin *iwin = Infowin;
+
+ /* Nuke if requested */
+ if (iwin->nuke)
+ {
+ /* Destory the old window */
+ XDestroyWindow(Metadpy->dpy, iwin->win);
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Prepare a new 'infowin'.
+ */
+static errr Infowin_prepare(Window xid)
+{
+ infowin *iwin = Infowin;
+
+ Window tmp_win;
+ XWindowAttributes xwa;
+ int x, y;
+ unsigned int w, h, b, d;
+
+ /* Assign stuff */
+ iwin->win = xid;
+
+ /* Check For Error XXX Extract some ACTUAL data from 'xid' */
+ XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d);
+
+ /* Apply the above info */
+ iwin->x = x;
+ iwin->y = y;
+ iwin->w = w;
+ iwin->h = h;
+ iwin->b = b;
+
+ /* Check Error XXX Extract some more ACTUAL data */
+ XGetWindowAttributes(Metadpy->dpy, xid, &xwa);
+
+ /* Apply the above info */
+ iwin->mask = xwa.your_event_mask;
+ iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1);
+
+ /* And assume that we are exposed */
+ iwin->redraw = 1;
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Initialize a new 'infowin'.
+ */
+static errr Infowin_init_real(Window xid)
+{
+ /* Wipe it clean */
+ (void)WIPE(Infowin, infowin);
+
+ /* Start out non-nukable */
+ Infowin->nuke = 0;
+
+ /* Attempt to Prepare ourself */
+ return (Infowin_prepare(xid));
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Init an infowin by giving some data.
+ *
+ * Inputs:
+ * dad: The Window that should own this Window (if any)
+ * x,y: The position of this Window
+ * w,h: The size of this Window
+ * b,d: The border width and pixel depth
+ *
+ * Notes:
+ * If 'dad == None' assume 'dad == root'
+ */
+static errr Infowin_init_data(Window dad, int x, int y, int w, int h,
+ int b, Pixell fg, Pixell bg)
+{
+ Window xid;
+
+ /* Wipe it clean */
+ (void)WIPE(Infowin, infowin);
+
+
+ /*** Error Check XXX ***/
+
+
+ /*** Create the Window 'xid' from data ***/
+
+ /* What happened here? XXX XXX XXX */
+
+ /* If no parent given, depend on root */
+ if (dad == None)
+
+ /* #ifdef USE_GRAPHICS
+
+ xid = XCreateWindow(Metadpy->dpy, Metadpy->root, x, y, w, h, b, 8, InputOutput, CopyFromParent, 0, 0);
+
+ else
+ */
+
+ /* #else */
+
+ dad = Metadpy->root;
+
+ /* #endif */
+
+ /* Create the Window XXX Error Check */
+ xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg);
+
+ /* Start out selecting No events */
+ XSelectInput(Metadpy->dpy, xid, 0L);
+
+
+ /*** Prepare the new infowin ***/
+
+ /* Mark it as nukable */
+ Infowin->nuke = 1;
+
+ /* Attempt to Initialize the infowin */
+ return (Infowin_prepare(xid));
+}
+
+
+
+/*
+ * Modify the event mask of an Infowin
+ */
+static errr Infowin_set_mask(long mask)
+{
+ /* Save the new setting */
+ Infowin->mask = mask;
+
+ /* Execute the Mapping */
+ XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Request that Infowin be mapped
+ */
+static errr Infowin_map(void)
+{
+ /* Execute the Mapping */
+ XMapWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request that Infowin be unmapped
+ */
+static errr Infowin_unmap(void)
+{
+ /* Execute the Un-Mapping */
+ XUnmapWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Request that Infowin be raised
+ */
+static errr Infowin_raise(void)
+{
+ /* Raise towards visibility */
+ XRaiseWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request that Infowin be lowered
+ */
+static errr Infowin_lower(void)
+{
+ /* Lower towards invisibility */
+ XLowerWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Request that Infowin be moved to a new location
+ */
+static errr Infowin_impell(int x, int y)
+{
+ /* Execute the request */
+ XMoveWindow(Metadpy->dpy, Infowin->win, x, y);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Move and Resize an infowin
+ */
+static errr Infowin_locate(int x, int y, int w, int h)
+{
+ /* Execute the request */
+ XMoveResizeWindow(Metadpy->dpy, Infowin->win, x, y, w, h);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Visually clear Infowin
+ */
+static errr Infowin_wipe(void)
+{
+ /* Execute the request */
+ XClearWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Visually Paint Infowin with the current color
+ */
+static errr Infowin_fill(void)
+{
+ /* Execute the request */
+ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ 0, 0, Infowin->w, Infowin->h);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * A NULL terminated pair list of legal "operation names"
+ *
+ * Pairs of values, first is texttual name, second is the string
+ * holding the decimal value that the operation corresponds to.
+ */
+static cptr opcode_pairs[] =
+{
+ "cpy", "3",
+ "xor", "6",
+ "and", "1",
+ "ior", "7",
+ "nor", "8",
+ "inv", "10",
+ "clr", "0",
+ "set", "15",
+
+ "src", "3",
+ "dst", "5",
+
+ "+andReverse", "2",
+ "+andInverted", "4",
+ "+noop", "5",
+ "+equiv", "9",
+ "+orReverse", "11",
+ "+copyInverted", "12",
+ "+orInverted", "13",
+ "+nand", "14",
+ NULL
+};
+
+
+/*
+ * Parse a word into an operation "code"
+ *
+ * Inputs:
+ * str: A string, hopefully representing an Operation
+ *
+ * Output:
+ * 0-15: if 'str' is a valid Operation
+ * -1: if 'str' could not be parsed
+ */
+static int Infoclr_Opcode(cptr str)
+{
+ register int i;
+
+ /* Scan through all legal operation names */
+ for (i = 0; opcode_pairs[i*2]; ++i)
+ {
+ /* Is this the right oprname? */
+ if (streq(opcode_pairs[i*2], str))
+ {
+ /* Convert the second element in the pair into a Code */
+ return (atoi(opcode_pairs[i*2 + 1]));
+ }
+ }
+
+ /* The code was not found, return -1 */
+ return ( -1);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request a Pixell by name. Note: uses 'Metadpy'.
+ *
+ * Inputs:
+ * name: The name of the color to try to load (see below)
+ *
+ * Output:
+ * The Pixell value that metched the given name
+ * 'Metadpy->fg' if the name was unparseable
+ *
+ * Valid forms for 'name':
+ * 'fg', 'bg', 'zg', '<name>' and '#<code>'
+ */
+static Pixell Infoclr_Pixell(cptr name)
+{
+ XColor scrn;
+
+ /* Attempt to Parse the name */
+ if (name && name[0])
+ {
+ /* The 'bg' color is available */
+ if (streq(name, "bg")) return (Metadpy->bg);
+
+ /* The 'fg' color is available */
+ if (streq(name, "fg")) return (Metadpy->fg);
+
+ /* The 'zg' color is available */
+ if (streq(name, "zg")) return (Metadpy->zg);
+
+ /* The 'white' color is available */
+ if (streq(name, "white")) return (Metadpy->white);
+
+ /* The 'black' color is available */
+ if (streq(name, "black")) return (Metadpy->black);
+
+ /* Attempt to parse 'name' into 'scrn' */
+ if (!(XParseColor(Metadpy->dpy, Metadpy->cmap, name, &scrn)))
+ {
+ plog_fmt("Warning: Couldn't parse color '%s'\n", name);
+ }
+
+ /* Attempt to Allocate the Parsed color */
+ if (!(XAllocColor(Metadpy->dpy, Metadpy->cmap, &scrn)))
+ {
+ plog_fmt("Warning: Couldn't allocate color '%s'\n", name);
+ }
+
+ /* The Pixel was Allocated correctly */
+ else return (scrn.pixel);
+ }
+
+ /* Warn about the Default being Used */
+ plog_fmt("Warning: Using 'fg' for unknown color '%s'\n", name);
+
+ /* Default to the 'Foreground' color */
+ return (Metadpy->fg);
+}
+
+
+/*
+ * Initialize a new 'infoclr' with a real GC.
+ */
+static errr Infoclr_init_1(GC gc)
+{
+ infoclr *iclr = Infoclr;
+
+ /* Wipe the iclr clean */
+ (void)WIPE(iclr, infoclr);
+
+ /* Assign the GC */
+ iclr->gc = gc;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Nuke an old 'infoclr'.
+ */
+static errr Infoclr_nuke(void)
+{
+ infoclr *iclr = Infoclr;
+
+ /* Deal with 'GC' */
+ if (iclr->nuke)
+ {
+ /* Free the GC */
+ XFreeGC(Metadpy->dpy, iclr->gc);
+ }
+
+ /* Forget the current */
+ Infoclr = (infoclr*)(NULL);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Initialize an infoclr with some data
+ *
+ * Inputs:
+ * fg: The Pixell for the requested Foreground (see above)
+ * bg: The Pixell for the requested Background (see above)
+ * op: The Opcode for the requested Operation (see above)
+ * stip: The stipple mode
+ */
+static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip)
+{
+ infoclr *iclr = Infoclr;
+
+ GC gc;
+ XGCValues gcv;
+ unsigned long gc_mask;
+
+
+
+ /*** Simple error checking of opr and clr ***/
+
+ /* Check the 'Pixells' for realism */
+ if (bg > Metadpy->zg) return ( -1);
+ if (fg > Metadpy->zg) return ( -1);
+
+ /* Check the data for trueness */
+ if ((op < 0) || (op > 15)) return ( -1);
+
+
+ /*** Create the requested 'GC' ***/
+
+ /* Assign the proper GC function */
+ gcv.function = op;
+
+ /* Assign the proper GC background */
+ gcv.background = bg;
+
+ /* Assign the proper GC foreground */
+ gcv.foreground = fg;
+
+ /* Hack -- Handle XOR (xor is code 6) by hacking bg and fg */
+ if (op == 6) gcv.background = 0;
+ if (op == 6) gcv.foreground = (bg ^ fg);
+
+ /* Assign the proper GC Fill Style */
+ gcv.fill_style = (stip ? FillStippled : FillSolid);
+
+ /* Turn off 'Give exposure events for pixmap copying' */
+ gcv.graphics_exposures = False;
+
+ /* Set up the GC mask */
+ gc_mask = (GCFunction | GCBackground | GCForeground |
+ GCFillStyle | GCGraphicsExposures);
+
+ /* Create the GC detailed above */
+ gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv);
+
+
+ /*** Initialize ***/
+
+ /* Wipe the iclr clean */
+ (void)WIPE(iclr, infoclr);
+
+ /* Assign the GC */
+ iclr->gc = gc;
+
+ /* Nuke it when done */
+ iclr->nuke = 1;
+
+ /* Assign the parms */
+ iclr->fg = fg;
+ iclr->bg = bg;
+ iclr->code = op;
+ iclr->stip = stip ? 1 : 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Change the 'fg' for an infoclr
+ *
+ * Inputs:
+ * fg: The Pixell for the requested Foreground (see above)
+ */
+static errr Infoclr_change_fg(Pixell fg)
+{
+ infoclr *iclr = Infoclr;
+
+
+ /*** Simple error checking of opr and clr ***/
+
+ /* Check the 'Pixells' for realism */
+ if (fg > Metadpy->zg) return ( -1);
+
+
+ /*** Change ***/
+
+ /* Change */
+ XSetForeground(Metadpy->dpy, iclr->gc, fg);
+
+ /* Success */
+ return (0);
+}
+
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Nuke an old 'infofnt'.
+ */
+static errr Infofnt_nuke(void)
+{
+ infofnt *ifnt = Infofnt;
+
+ /* Deal with 'name' */
+ if (ifnt->name)
+ {
+ /* Free the name */
+ string_free(ifnt->name);
+ }
+
+ /* Nuke info if needed */
+ if (ifnt->nuke)
+ {
+ /* Free the font */
+ XFreeFont(Metadpy->dpy, ifnt->info);
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Prepare a new 'infofnt'
+ */
+static errr Infofnt_prepare(XFontStruct *info)
+{
+ infofnt *ifnt = Infofnt;
+
+ XCharStruct *cs;
+
+ /* Assign the struct */
+ ifnt->info = info;
+
+ /* Jump into the max bouonds thing */
+ cs = &(info->max_bounds);
+
+ /* Extract default sizing info */
+ ifnt->asc = info->ascent;
+ ifnt->hgt = info->ascent + info->descent;
+ ifnt->wid = cs->width;
+ if (use_bigtile)
+ ifnt->twid = 2 * ifnt->wid;
+ else
+ ifnt->twid = ifnt->wid;
+
+
+#ifdef OBSOLETE_SIZING_METHOD
+ /* Extract default sizing info */
+ ifnt->asc = cs->ascent;
+ ifnt->hgt = (cs->ascent + cs->descent);
+ ifnt->wid = cs->width;
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Initialize a new 'infofnt'.
+ */
+static errr Infofnt_init_real(XFontStruct *info)
+{
+ /* Wipe the thing */
+ (void)WIPE(Infofnt, infofnt);
+
+ /* No nuking */
+ Infofnt->nuke = 0;
+
+ /* Attempt to prepare it */
+ return (Infofnt_prepare(info));
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Init an infofnt by its Name
+ *
+ * Inputs:
+ * name: The name of the requested Font
+ */
+static errr Infofnt_init_data(cptr name)
+{
+ XFontStruct *info;
+
+
+ /*** Load the info Fresh, using the name ***/
+
+ /* If the name is not given, report an error */
+ if (!name) return ( -1);
+
+ /* Attempt to load the font */
+ info = XLoadQueryFont(Metadpy->dpy, name);
+
+ /* The load failed, try to recover */
+ if (!info) return ( -1);
+
+
+ /*** Init the font ***/
+
+ /* Wipe the thing */
+ (void)WIPE(Infofnt, infofnt);
+
+ /* Attempt to prepare it */
+ if (Infofnt_prepare(info))
+ {
+ /* Free the font */
+ XFreeFont(Metadpy->dpy, info);
+
+ /* Fail */
+ return ( -1);
+ }
+
+ /* Save a copy of the font name */
+ Infofnt->name = string_make(name);
+
+ /* Mark it as nukable */
+ Infofnt->nuke = 1;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Standard Text
+ */
+static errr Infofnt_text_std(int x, int y, cptr str, int len)
+{
+ int i;
+
+
+ /*** Do a brief info analysis ***/
+
+ /* Do nothing if the string is null */
+ if (!str || !*str) return ( -1);
+
+ /* Get the length of the string */
+ if (len < 0) len = strlen(str);
+
+
+ /*** Decide where to place the string, vertically ***/
+
+ /* Ignore Vertical Justifications */
+ y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
+
+
+ /*** Decide where to place the string, horizontally ***/
+
+ /* Line up with x at left edge of column 'x' */
+ x = (x * Infofnt->wid) + Infowin->ox;
+
+
+ /*** Actually draw 'str' onto the infowin ***/
+
+ /* Be sure the correct font is ready */
+ XSetFont(Metadpy->dpy, Infoclr->gc, Infofnt->info->fid);
+
+
+ /*** Handle the fake mono we can enforce on fonts ***/
+
+ /* Monotize the font */
+ if (Infofnt->mono)
+ {
+ /* Do each character */
+ for (i = 0; i < len; ++i)
+ {
+ /* Note that the Infoclr is set up to contain the Infofnt */
+ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ x + i * Infofnt->wid + Infofnt->off, y, str + i, 1);
+ }
+ }
+
+ /* Assume monoospaced font */
+ else
+ {
+ /* Note that the Infoclr is set up to contain the Infofnt */
+ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ x, y, str, len);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Painting where text would be
+ */
+static errr Infofnt_text_non(int x, int y, cptr str, int len)
+{
+ int w, h;
+
+
+ /*** Find the width ***/
+
+ /* Negative length is a flag to count the characters in str */
+ if (len < 0) len = strlen(str);
+
+ /* The total width will be 'len' chars * standard width */
+ w = len * Infofnt->wid;
+
+
+ /*** Find the X dimensions ***/
+
+ /* Line up with x at left edge of column 'x' */
+ x = x * Infofnt->wid + Infowin->ox;
+
+
+ /*** Find other dimensions ***/
+
+ /* Simply do 'Infofnt->hgt' (a single row) high */
+ h = Infofnt->hgt;
+
+ /* Simply do "at top" in row 'y' */
+ y = y * h + Infowin->oy;
+
+
+ /*** Actually 'paint' the area ***/
+
+ /* Just do a Fill Rectangle */
+ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*************************************************************************/
+
+
+/*
+ * Angband specific code follows... (ANGBAND)
+ */
+
+
+/*
+ * Hack -- cursor color
+ */
+static infoclr *xor;
+
+/*
+ * Actual color table
+ */
+static infoclr *clr[256];
+
+/*
+ * Color info (unused, red, green, blue).
+ */
+static byte color_table[256][4];
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+/*
+ * A structure for each "term"
+ */
+struct term_data
+{
+ term t;
+
+ infofnt *fnt;
+
+ infowin *win;
+
+#ifdef USE_GRAPHICS
+
+ XImage *tiles;
+
+#ifdef USE_TRANSPARENCY
+
+ /* Tempory storage for overlaying tiles. */
+ XImage *TmpImage;
+
+#endif
+
+#endif
+
+};
+
+
+/*
+ * The number of term data structures
+ */
+#define MAX_TERM_DATA 8
+
+/*
+ * The array of term data structures
+ */
+static term_data data[MAX_TERM_DATA];
+
+/* Use short names for the most commonly used elements of various structures. */
+#define DPY (Metadpy->dpy)
+#define WIN (Infowin->win)
+
+/*
+ * Simply push a set of co-ordinates around.
+ */
+typedef struct co_ord co_ord;
+struct co_ord
+{
+ int x;
+ int y;
+};
+
+/*
+ * A special structure to store information about the text currently
+ * selected.
+ */
+typedef struct x11_selection_type x11_selection_type;
+struct x11_selection_type
+{
+ bool select; /* The selection is currently in use. */
+ bool drawn; /* The selection is currently displayed. */
+ term *t; /* The window where the selection is found. */
+ co_ord init; /* The starting co-ordinates. */
+ co_ord cur; /* The end co-ordinates (the current ones if still copying). */
+ co_ord old; /* The previous end co-ordinates. */
+ Time time; /* The time at which the selection was finalised. */
+};
+
+static x11_selection_type s_ptr[1];
+
+
+
+/*
+ * Process a keypress event
+ *
+ * Also appears in "main-xaw.c".
+ */
+static void react_keypress(XKeyEvent *xev)
+{
+ int i, n, mc, ms, mo, mx;
+
+ uint ks1;
+
+ XKeyEvent *ev = (XKeyEvent*)(xev);
+
+ KeySym ks;
+
+ char buf[128];
+ char msg[128];
+
+
+ /* Check for "normal" keypresses */
+ n = XLookupString(ev, buf, 125, &ks, NULL);
+
+ /* Terminate */
+ buf[n] = '\0';
+
+
+ /* Hack -- Ignore "modifier keys" */
+ if (IsModifierKey(ks)) return;
+
+
+ /* Hack -- convert into an unsigned int */
+ ks1 = (uint)(ks);
+
+ /* Extract four "modifier flags" */
+ mc = (ev->state & ControlMask) ? TRUE : FALSE;
+ ms = (ev->state & ShiftMask) ? TRUE : FALSE;
+ mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
+ mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
+
+
+ /* Normal keys with no modifiers */
+ if (n && !mo && !mx && !IsSpecialKey(ks))
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; buf[i]; i++) Term_keypress(buf[i]);
+
+ /* All done */
+ return;
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch (ks1)
+ {
+ case XK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return;
+ }
+
+ case XK_Return:
+ {
+ Term_keypress('\r');
+ return;
+ }
+
+ case XK_Tab:
+ {
+ Term_keypress('\t');
+ return;
+ }
+
+ case XK_Delete:
+ case XK_BackSpace:
+ {
+ Term_keypress('\010');
+ return;
+ }
+ }
+
+
+ /* Hack -- Use the KeySym */
+ if (ks)
+ {
+ sprintf(msg, "%c%s%s%s%s_%lX%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ (unsigned long)(ks), 13);
+ }
+
+ /* Hack -- Use the Keycode */
+ else
+ {
+ sprintf(msg, "%c%s%s%s%sK_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ ev->keycode, 13);
+ }
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+
+ /* Hack -- auto-define macros as needed */
+ if (n && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, buf);
+ }
+}
+
+
+/*
+ * Find the square a particular pixel is part of.
+ */
+static void pixel_to_square(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = (ox - Infowin->ox) / Infofnt->wid;
+ (*y) = (oy - Infowin->oy) / Infofnt->hgt;
+}
+
+/*
+ * Find the pixel at the top-left corner of a square.
+ */
+static void square_to_pixel(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = ox * Infofnt->wid + Infowin->ox;
+ (*y) = oy * Infofnt->hgt + Infowin->oy;
+}
+
+/*
+ * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
+ */
+static void sort_co_ord(co_ord *min, co_ord *max,
+ const co_ord *b, const co_ord *a)
+{
+ min->x = MIN(a->x, b->x);
+ min->y = MIN(a->y, b->y);
+ max->x = MAX(a->x, b->x);
+ max->y = MAX(a->y, b->y);
+}
+
+/*
+ * Remove the selection by redrawing it.
+ */
+static void mark_selection_clear(int x1, int y1, int x2, int y2)
+{
+ Term_redraw_section(x1, y1, x2, y2);
+}
+
+/*
+ * Select an area by drawing a grey box around it.
+ * NB. These two functions can cause flicker as the selection is modified,
+ * as the game redraws the entire marked section.
+ */
+static void mark_selection_mark(int x1, int y1, int x2, int y2)
+{
+ square_to_pixel(&x1, &y1, x1, y1);
+ square_to_pixel(&x2, &y2, x2, y2);
+ XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1,
+ x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
+}
+
+/*
+ * Mark a selection by drawing boxes around it (for now).
+ */
+static void mark_selection(void)
+{
+ co_ord min, max;
+ term *old = Term;
+ bool draw = s_ptr->select;
+ bool clear = s_ptr->drawn;
+
+ /* Open the correct term if necessary. */
+ if (s_ptr->t != old) Term_activate(s_ptr->t);
+
+ if (clear)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
+ mark_selection_clear(min.x, min.y, max.x, max.y);
+ }
+ if (draw)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+ mark_selection_mark(min.x, min.y, max.x, max.y);
+ }
+
+ /* Finish on the current term. */
+ if (s_ptr->t != old) Term_activate(old);
+
+ s_ptr->old.x = s_ptr->cur.x;
+ s_ptr->old.y = s_ptr->cur.y;
+ s_ptr->drawn = s_ptr->select;
+}
+
+/*
+ * Forget a selection for one reason or another.
+ */
+static void copy_x11_release(void)
+{
+ /* Deselect the current selection. */
+ s_ptr->select = FALSE;
+
+ /* Remove its graphical represesntation. */
+ mark_selection();
+}
+
+/*
+ * Start to select some text on the screen.
+ */
+static void copy_x11_start(int x, int y)
+{
+ if (s_ptr->select) copy_x11_release();
+
+ /* Remember where the selection started. */
+ s_ptr->t = Term;
+ s_ptr->init.x = s_ptr->cur.x = s_ptr->old.x = x;
+ s_ptr->init.y = s_ptr->cur.y = s_ptr->old.y = y;
+}
+
+/*
+ * Respond to movement of the mouse when selecting text.
+ */
+static void copy_x11_cont(int x, int y, unsigned int buttons)
+{
+ /* Use the nearest square within bounds if the mouse is outside. */
+ x = MIN(MAX(x, 0), Term->wid - 1);
+ y = MIN(MAX(y, 0), Term->hgt - 1);
+
+ /* The left mouse button isn't pressed. */
+ if (~buttons & Button1Mask) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Not enough movement. */
+ if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) return;
+
+ /* Something is being selected. */
+ s_ptr->select = TRUE;
+
+ /* Track the selection. */
+ s_ptr->cur.x = x;
+ s_ptr->cur.y = y;
+
+ /* Hack - display it inefficiently. */
+ mark_selection();
+}
+
+/*
+ * Respond to release of the left mouse button by putting the selected text in
+ * the primary buffer.
+ */
+static void copy_x11_end(const Time time)
+{
+ /* No selection. */
+ if (!s_ptr->select) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Remember when the selection was finalised. */
+ s_ptr->time = time;
+
+ /* Acquire the primary selection. */
+ XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
+ if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win)
+ {
+ /* Failed to acquire the selection, so forget it. */
+ bell();
+ s_ptr->select = FALSE;
+ mark_selection();
+ }
+}
+
+/*
+ * Send a message to request that the PRIMARY buffer be sent here.
+ */
+static void paste_x11_request(const Time time)
+{
+ XEvent event[1];
+ XSelectionRequestEvent *ptr = &(event->xselectionrequest);
+
+ /* Set various things. */
+ ptr->type = SelectionRequest;
+ ptr->display = Metadpy->dpy;
+ ptr->owner = XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY);
+ ptr->requestor = Infowin->win;
+ ptr->selection = XA_PRIMARY;
+ ptr->target = XA_STRING;
+ ptr->property = XA_STRING; /* Unused */
+ ptr->time = time;
+
+ /* Check the owner. */
+ if (ptr->owner == None)
+ {
+ /* No selection. */
+ bell();
+ return;
+ }
+
+ /* Send the SelectionRequest event. */
+ XSendEvent(Metadpy->dpy, ptr->owner, False, NoEventMask, event);
+}
+
+/*
+ * Add a character to a string in preparation for sending it to another
+ * client as a STRING.
+ * This doesn't change anything, as clients tend not to have difficulty in
+ * receiving this format (although the standard specifies a restricted set).
+ * Strings do not have a colour.
+ */
+static int add_char_string(char *buf, byte a, char c)
+{
+ *buf = c;
+ return 1;
+}
+
+/*
+ * Send some text requested by another X client.
+ */
+static void paste_x11_send(XSelectionRequestEvent *rq)
+{
+ XEvent event;
+ XSelectionEvent *ptr = &(event.xselection);
+ int (*add)(char *, byte, char) = 0;
+
+ /* Set the event parameters. */
+ ptr->type = SelectionNotify;
+ ptr->property = rq->property;
+ ptr->display = rq->display;
+ ptr->requestor = rq->requestor;
+ ptr->selection = rq->selection;
+ ptr->target = rq->target;
+ ptr->time = rq->time;
+
+ /* Determine the correct "add a character" function.
+ * As Term->wid is at most 255, these can add up to 4 characters of
+ * output per character of input without problem.
+ * The mechanism will need to change if much more than this is needed.
+ */
+ switch (rq->target)
+ {
+ case XA_STRING:
+ add = add_char_string;
+ break;
+ default:
+ goto error;
+ }
+
+ /* Reply to a known target received recently with data. */
+ if (rq->time >= s_ptr->time && add)
+ {
+ char buf[1024];
+ co_ord max, min;
+ int x, y, l;
+ byte a;
+ char c;
+
+ /* Work out which way around to paste. */
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+
+ /* Paranoia. */
+ if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN)
+ {
+ bell();
+ goto error;
+ }
+
+ /* Delete the old value of the property. */
+ XDeleteProperty(DPY, rq->requestor, rq->property);
+
+ for (y = 0; y < Term->hgt; y++)
+ {
+ if (y < min.y) continue;
+ if (y > max.y) break;
+
+ for (x = l = 0; x < Term->wid; x++)
+ {
+ if (x < min.x) continue;
+ if (x > max.x) break;
+
+ /* Find the character. */
+ Term_what(x, y, &a, &c);
+
+ /* Add it. */
+ l += (*add)(buf + l, a, c);
+ }
+
+ /* Terminate all but the last line in an appropriate way. */
+ if (y != max.y) l += (*add)(buf + l, TERM_WHITE, '\n');
+
+ /* Send the (non-empty) string. */
+ XChangeProperty(DPY, rq->requestor, rq->property, rq->target, 8,
+ PropModeAppend, (unsigned char*)buf, l);
+ }
+ }
+ else
+ {
+ /* Respond to all bad requests with property None. */
+error:
+ ptr->property = None;
+ }
+
+ /* Send whatever event we're left with. */
+ XSendEvent(DPY, rq->requestor, FALSE, NoEventMask, &event);
+}
+
+extern errr type_string(char *str, uint len);
+
+/*
+ * Add the contents of the PRIMARY buffer to the input queue.
+ *
+ * Hack - This doesn't use the "time" of the event, and so accepts anything a
+ * client tries to send it.
+ */
+static void paste_x11_accept(const XSelectionEvent *ptr)
+{
+ long offset;
+ unsigned long left;
+
+ /* Failure. */
+ if (ptr->property == None)
+ {
+ bell();
+ return;
+ }
+
+ if (ptr->selection != XA_PRIMARY)
+ {
+ bell();
+ return;
+ }
+ if (ptr->target != XA_STRING)
+ {
+ bell();
+ return;
+ }
+
+ for (offset = 0; ; offset += left)
+ {
+ errr err;
+
+ /* A pointer for the pasted information. */
+ unsigned char *data;
+
+ Atom type;
+ int fmt;
+ unsigned long nitems;
+
+ /* Set data to the string, and catch errors. */
+ if (XGetWindowProperty(Metadpy->dpy, Infowin->win, XA_STRING, offset,
+ 32767, TRUE, XA_STRING, &type, &fmt, &nitems, &left, &data)
+ != Success) break;
+
+ /* Paste the text. */
+ err = type_string((char*)data, (uint)nitems);
+
+ /* Free the data pasted. */
+ XFree(data);
+
+ /* No room. */
+ if (err == 7)
+ {
+ bell();
+ break;
+ }
+ /* Paranoia? - strange errors. */
+ else if (err)
+ {
+ break;
+ }
+
+ /* Pasted everything. */
+ if (!left) return;
+ }
+
+ /* An error has occurred, so free the last bit of data before returning. */
+ XFree(data);
+}
+
+/*
+ * Handle various events conditional on presses of a mouse button.
+ */
+static void handle_button(Time time, int x, int y, int button,
+ bool press)
+{
+ /* The co-ordinates are only used in Angband format. */
+ pixel_to_square(&x, &y, x, y);
+
+ if (press && button == 1) copy_x11_start(x, y);
+ if (!press && button == 1) copy_x11_end(time);
+ if (!press && button == 2) paste_x11_request(time);
+}
+
+
+/*
+ * Process events
+ */
+static errr CheckEvent(bool wait)
+{
+ term_data *old_td = (term_data*)(Term->data);
+
+ XEvent xev_body, *xev = &xev_body;
+
+ term_data *td = NULL;
+ infowin *iwin = NULL;
+
+ int i;
+
+
+ /* Do not wait unless requested */
+ if (!wait && !XPending(Metadpy->dpy)) return (1);
+
+ /* Hack - redraw the selection, if needed.
+ * This doesn't actually check that one of its squares was drawn to,
+ * only that this may have happened.
+ */
+ if (s_ptr->select && !s_ptr->drawn) mark_selection();
+
+ /* Load the Event */
+ XNextEvent(Metadpy->dpy, xev);
+
+
+ /* Notice new keymaps */
+ if (xev->type == MappingNotify)
+ {
+ XRefreshKeyboardMapping(&xev->xmapping);
+ return 0;
+ }
+
+
+ /* Scan the windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ if (xev->xany.window == data[i].win->win)
+ {
+ td = &data[i];
+ iwin = td->win;
+ break;
+ }
+ }
+
+ /* Unknown window */
+ if (!td || !iwin) return (0);
+
+
+ /* Hack -- activate the Term */
+ Term_activate(&td->t);
+
+ /* Hack -- activate the window */
+ Infowin_set(iwin);
+
+
+ /* Switch on the Type */
+ switch (xev->type)
+ {
+
+ case ButtonPress:
+ case ButtonRelease:
+ {
+ bool press = (xev->type == ButtonPress);
+
+ /* Where is the mouse */
+ int x = xev->xbutton.x;
+ int y = xev->xbutton.y;
+
+ int z;
+
+ /* Which button is involved */
+ if (xev->xbutton.button == Button1) z = 1;
+ else if (xev->xbutton.button == Button2) z = 2;
+ else if (xev->xbutton.button == Button3) z = 3;
+ else if (xev->xbutton.button == Button4) z = 4;
+ else if (xev->xbutton.button == Button5) z = 5;
+ else z = 0;
+
+ /* Where is the mouse */
+ x = xev->xbutton.x;
+ y = xev->xbutton.y;
+
+ /* XXX Handle */
+ handle_button(xev->xbutton.time, x, y, z, press);
+
+ break;
+ }
+
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ /* Where is the mouse */
+ /* XXX Handle */
+
+ break;
+ }
+
+ case MotionNotify:
+ {
+ int x = xev->xmotion.x;
+ int y = xev->xmotion.y;
+ unsigned int z = xev->xmotion.state;
+
+ /* Convert to co-ordinates Angband understands. */
+ pixel_to_square(&x, &y, x, y);
+
+ /* Alter the selection if appropriate. */
+ copy_x11_cont(x, y, z);
+
+ /* XXX Handle */
+
+ break;
+ }
+
+ case SelectionNotify:
+ {
+ paste_x11_accept(&(xev->xselection));
+ break;
+ }
+
+ case SelectionRequest:
+ {
+ paste_x11_send(&(xev->xselectionrequest));
+ break;
+ }
+
+ case SelectionClear:
+ {
+ s_ptr->select = FALSE;
+ mark_selection();
+ break;
+ }
+
+ case KeyRelease:
+ {
+ /* Nothing */
+ break;
+ }
+
+ case KeyPress:
+ {
+ /* Hack -- use "old" term */
+ Term_activate(&old_td->t);
+
+ /* Process the key */
+ react_keypress(&(xev->xkey));
+
+ break;
+ }
+
+ case Expose:
+ {
+ /* Ignore "extra" exposes */
+ if (xev->xexpose.count) break;
+
+ /* Clear the window */
+ Infowin_wipe();
+
+ /* Redraw */
+ Term_redraw();
+
+ break;
+ }
+
+ case MapNotify:
+ {
+ Infowin->mapped = 1;
+ Term->mapped_flag = TRUE;
+ break;
+ }
+
+ case UnmapNotify:
+ {
+ Infowin->mapped = 0;
+ Term->mapped_flag = FALSE;
+ break;
+ }
+
+ /* Move and/or Resize */
+ case ConfigureNotify:
+ {
+ int cols, rows, wid, hgt;
+
+ int ox = Infowin->ox;
+ int oy = Infowin->oy;
+
+ /* Save the new Window Parms */
+ Infowin->x = xev->xconfigure.x;
+ Infowin->y = xev->xconfigure.y;
+ Infowin->w = xev->xconfigure.width;
+ Infowin->h = xev->xconfigure.height;
+
+ /* Determine "proper" number of rows/cols */
+ cols = ((Infowin->w - (ox + ox)) / td->fnt->wid);
+ rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt);
+
+ /* Hack -- minimal size */
+ if (td == &data[0])
+ {
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ else
+ {
+ if (cols < 1) cols = 1;
+ if (rows < 1) rows = 1;
+ }
+
+ /* Paranoia */
+ if (cols > 255) cols = 255;
+ if (rows > 255) rows = 255;
+
+ /* Desired size of window */
+ wid = cols * td->fnt->wid + (ox + ox);
+ hgt = rows * td->fnt->hgt + (oy + oy);
+
+ /* Resize the Term (if needed) */
+ Term_resize(cols, rows);
+ break;
+ }
+ }
+
+
+ /* Hack -- Activate the old term */
+ Term_activate(&old_td->t);
+
+ /* Hack -- Activate the proper window */
+ Infowin_set(old_td->win);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle "activation" of a term
+ */
+static errr Term_xtra_x11_level(int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Handle "activate" */
+ if (v)
+ {
+ /* Activate the window */
+ Infowin_set(td->win);
+
+ /* Activate the font */
+ Infofnt_set(td->fnt);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_x11_react(void)
+{
+ int i;
+
+ if (Metadpy->color)
+ {
+ /* Check the colors */
+ for (i = 0; i < 256; i++)
+ {
+ if ((color_table[i][0] != angband_color_table[i][0]) ||
+ (color_table[i][1] != angband_color_table[i][1]) ||
+ (color_table[i][2] != angband_color_table[i][2]) ||
+ (color_table[i][3] != angband_color_table[i][3]))
+ {
+ Pixell pixel;
+
+ /* Save new values */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+
+ /* Change the foreground */
+ Infoclr_set(clr[i]);
+ Infoclr_change_fg(pixel);
+ }
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_x11(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ Metadpy_do_beep(); return (0);
+
+ /* Flush the output XXX XXX */
+ case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0);
+
+ /* Process random events XXX */
+ case TERM_XTRA_BORED:
+ {
+ irc_poll();
+
+ return (CheckEvent(0));
+ }
+
+ /* Process Events XXX */
+ case TERM_XTRA_EVENT:
+ {
+ irc_poll();
+
+ return (CheckEvent(v));
+ }
+
+ /* Flush the events XXX */
+ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0);
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL: return (Term_xtra_x11_level(v));
+
+ /* Clear the screen, and redraw any selection later. */
+ case TERM_XTRA_CLEAR: Infowin_wipe(); s_ptr->drawn = FALSE; return (0);
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ irc_poll();
+
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+
+ closedir(directory);
+ return 0;
+ }
+
+ /* React to changes */
+ case TERM_XTRA_REACT: return (Term_xtra_x11_react());
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN: Infowin_set_name(angband_term_name[0]); return (0);
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Draw the cursor as an inverted rectangle.
+ *
+ * Consider a rectangular outline like "main-mac.c". XXX XXX
+ */
+static errr Term_curs_x11(int x, int y)
+{
+ /* Draw the cursor */
+ Infoclr_set(xor);
+
+ if (use_bigtile && x + 1 < Term->wid && Term->old->a[y][x + 1] == 255)
+ Infofnt_text_non(x, y, " ", 2);
+ else
+ /* Hilite the cursor character */
+ Infofnt_text_non(x, y, " ", 1);
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters.
+ */
+static errr Term_wipe_x11(int x, int y, int n)
+{
+ /* Erase (use black) */
+ Infoclr_set(clr[TERM_DARK]);
+
+ /* Mega-Hack -- Erase some space */
+ Infofnt_text_non(x, y, "", n);
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some textual characters.
+ */
+static errr Term_text_x11(int x, int y, int n, byte a, cptr s)
+{
+ /* Draw the text */
+ Infoclr_set(clr[a]);
+
+ /* Draw the text */
+ Infofnt_text_std(x, y, s, n);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ int i, x1, y1;
+
+ byte a;
+ char c;
+
+
+#ifdef USE_TRANSPARENCY
+ byte ta;
+ char tc;
+ int x2, y2;
+
+# ifdef USE_EGO_GRAPHICS
+ byte ea;
+ char ec;
+ int x3, y3;
+ bool has_overlay;
+# endif /* USE_EGO_GRAPHICS */
+
+ int k, l;
+
+ unsigned long pixel, blank;
+#endif /* USE_TRANSPARENCY */
+
+ term_data *td = (term_data*)(Term->data);
+
+ y *= Infofnt->hgt;
+ x *= Infofnt->wid;
+
+ /* Add in affect of window boundaries */
+ y += Infowin->oy;
+ x += Infowin->ox;
+
+ for (i = 0; i < n; ++i, x += td->fnt->wid)
+ {
+ a = *ap++;
+ c = *cp++;
+
+ /* For extra speed - cache these values */
+ x1 = (c & 0x7F) * td->fnt->twid;
+ y1 = (a & 0x7F) * td->fnt->hgt;
+
+#ifdef USE_TRANSPARENCY
+
+ ta = *tap++;
+ tc = *tcp++;
+
+ /* For extra speed - cache these values */
+ x2 = (tc & 0x7F) * td->fnt->twid;
+ y2 = (ta & 0x7F) * td->fnt->hgt;
+
+# ifdef USE_EGO_GRAPHICS
+
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+ /* For extra speed - cache these values too */
+ x3 = (ec & 0x7F) * td->fnt->twid;
+ y3 = (ea & 0x7F) * td->fnt->hgt;
+
+# endif /* USE_EGO_GRAPHICS */
+
+ /* Optimise the common case */
+ if ((x1 == x2) && (y1 == y2))
+ {
+# ifndef USE_EGO_GRAPHICS
+
+ /* Draw object / terrain */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+# else /* !USE_EGO_GRAPHICS */
+
+ /* Draw object / terrain */
+ if (!has_overlay)
+ {
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+ /* There's a terrain overlay */
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* If mask set in overlay... */
+ if ((pixel = XGetPixel(td->tiles, x3 + k, y3 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(td->tiles, x1 + k, y1 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+ /* Draw to screen */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->TmpImage,
+ 0, 0, x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+ }
+ else
+ {
+
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
+
+# ifndef USE_EGO_GRAPHICS
+
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* If mask set... */
+ if ((pixel = XGetPixel(td->tiles, x1 + k, y1 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+# else /* !USE_EGO_GRAPHICS */
+
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* Overlay */
+ if (has_overlay)
+ {
+ pixel = XGetPixel(td->tiles, x3 + k, y3 + l);
+ }
+
+ /* Hack -- No overlay */
+ else
+ {
+ pixel = blank;
+ }
+
+ /* If it's blank... */
+ if (pixel == blank)
+ {
+ /* Look at mon/obj */
+ pixel = XGetPixel(td->tiles, x1 + k, y1 + l);
+ }
+
+ /* If it's blank too, use terrain */
+ if (pixel == blank)
+ {
+ pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+
+ /* Draw to screen */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->TmpImage,
+ 0, 0, x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+#else /* USE_TRANSPARENCY */
+
+/* Draw object / terrain */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+
+#endif /* USE_TRANSPARENCY */
+ x += td->fnt->wid;
+ }
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+
+/*
+ * Initialize a term_data
+ */
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &td->t;
+
+ cptr name = angband_term_name[i];
+
+ cptr font;
+
+ int x = 0;
+ int y = 0;
+
+ int cols = 80;
+ int rows = 24;
+
+ int ox = 1;
+ int oy = 1;
+
+ int wid, hgt, num;
+
+ char buf[80];
+
+ cptr str;
+
+ int val;
+
+ XClassHint *ch;
+
+ char res_name[20];
+ char res_class[20];
+
+ XSizeHints *sh;
+
+
+ /* Window specific font name */
+ sprintf(buf, "ANGBAND_X11_FONT_%d", i);
+
+ /* Check environment for that font */
+ font = getenv(buf);
+
+ /* Check environment for "base" font */
+ if (!font) font = getenv("ANGBAND_X11_FONT");
+
+ /* No environment variables, use default font */
+ if (!font)
+ {
+ switch (i)
+ {
+ case 0:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 1:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 2:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 3:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 4:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 5:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 6:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 7:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ default:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ }
+ }
+
+ /* Window specific location (x) */
+ sprintf(buf, "ANGBAND_X11_AT_X_%d", i);
+ str = getenv(buf);
+ x = (str != NULL) ? atoi(str) : -1;
+
+ /* Window specific location (y) */
+ sprintf(buf, "ANGBAND_X11_AT_Y_%d", i);
+ str = getenv(buf);
+ y = (str != NULL) ? atoi(str) : -1;
+
+
+ /* Window specific cols */
+ sprintf(buf, "ANGBAND_X11_COLS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) cols = val;
+
+ /* Window specific rows */
+ sprintf(buf, "ANGBAND_X11_ROWS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) rows = val;
+
+
+ /* Window specific inner border offset (ox) */
+ sprintf(buf, "ANGBAND_X11_IBOX_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) ox = val;
+
+ /* Window specific inner border offset (oy) */
+ sprintf(buf, "ANGBAND_X11_IBOY_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) oy = val;
+
+
+ /* Prepare the standard font */
+ MAKE(td->fnt, infofnt);
+ Infofnt_set(td->fnt);
+ Infofnt_init_data(font);
+
+ /* Hack -- key buffer size */
+ num = (i == 0 ? 1024 : 16);
+
+ /* Assume full size windows */
+ wid = cols * td->fnt->wid + (ox + ox);
+ hgt = rows * td->fnt->hgt + (oy + oy);
+
+ /* Create a top-window */
+ MAKE(td->win, infowin);
+ Infowin_set(td->win);
+ Infowin_init_top(x, y, wid, hgt, 0,
+ Metadpy->fg, Metadpy->bg);
+
+ /* Ask for certain events */
+ Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask |
+ PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
+
+ /* Set the window name */
+ Infowin_set_name(name);
+
+ /* Save the inner border */
+ Infowin->ox = ox;
+ Infowin->oy = oy;
+
+ /* Make Class Hints */
+ ch = XAllocClassHint();
+
+ if (ch == NULL) quit("XAllocClassHint failed");
+
+ strcpy(res_name, name);
+ res_name[0] = FORCELOWER(res_name[0]);
+ ch->res_name = res_name;
+
+ strcpy(res_class, "Angband");
+ ch->res_class = res_class;
+
+ XSetClassHint(Metadpy->dpy, Infowin->win, ch);
+
+ /* Make Size Hints */
+ sh = XAllocSizeHints();
+
+ /* Oops */
+ if (sh == NULL) quit("XAllocSizeHints failed");
+
+ /* Fixed window size */
+ if (i == 0)
+ {
+ /* Main window: 80x24 -- 255x255 */
+ sh->flags = PMinSize | PMaxSize;
+ sh->min_width = 80 * td->fnt->wid + (ox + ox);
+ sh->min_height = 24 * td->fnt->hgt + (oy + oy);
+ sh->max_width = 255 * td->fnt->wid + (ox + ox);
+ sh->max_height = 255 * td->fnt->hgt + (oy + oy);
+ }
+
+ /* Variable window size */
+ else
+ {
+ /* Subwindows: 1x1 -- 255x255 */
+ sh->flags = PMinSize | PMaxSize;
+ sh->min_width = td->fnt->wid + (ox + ox);
+ sh->min_height = td->fnt->hgt + (oy + oy);
+ sh->max_width = 255 * td->fnt->wid + (ox + ox);
+ sh->max_height = 255 * td->fnt->hgt + (oy + oy);
+ }
+
+ /* Resize increment */
+ sh->flags |= PResizeInc;
+ sh->width_inc = td->fnt->wid;
+ sh->height_inc = td->fnt->hgt;
+
+ /* Base window size */
+ sh->flags |= PBaseSize;
+ sh->base_width = (ox + ox);
+ sh->base_height = (oy + oy);
+
+ /* Use the size hints */
+ XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);
+
+ /* Map the window */
+ Infowin_map();
+
+
+ /* Move the window to requested location */
+ if ((x >= 0) && (y >= 0)) Infowin_impell(x, y);
+
+
+ /* Initialize the term */
+ term_init(t, cols, rows, num);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_x11;
+ t->curs_hook = Term_curs_x11;
+ t->wipe_hook = Term_wipe_x11;
+ t->text_hook = Term_text_x11;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialization function for an "X11" module to Angband
+ */
+errr init_x11(int argc, char *argv[])
+{
+ int i;
+
+ cptr dpy_name = "";
+
+ int num_term = 1;
+
+#ifdef USE_GRAPHICS
+
+ char filename[1024];
+
+ int pict_wid = 0;
+ int pict_hgt = 0;
+ bool force_old_graphics = FALSE;
+
+#ifdef USE_TRANSPARENCY
+
+ char *TmpData;
+#endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-d"))
+ {
+ dpy_name = &argv[i][2];
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ if (prefix(argv[i], "-s"))
+ {
+ smoothRescaling = FALSE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-o"))
+ {
+ force_old_graphics = TRUE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-b"))
+ {
+ arg_bigtile = use_bigtile = TRUE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Init the Metadpy if possible */
+ if (Metadpy_init_name(dpy_name)) return ( -1);
+
+
+ /* Prepare cursor color */
+ MAKE(xor, infoclr);
+ Infoclr_set(xor);
+ Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
+
+
+ /* Prepare normal colors */
+ for (i = 0; i < 256; ++i)
+ {
+ Pixell pixel;
+
+ MAKE(clr[i], infoclr);
+
+ Infoclr_set(clr[i]);
+
+ /* Acquire Angband colors */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Default to monochrome */
+ pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);
+
+ /* Handle color */
+ if (Metadpy->color)
+ {
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+ }
+
+ /* Initialize the color */
+ Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
+ }
+
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Save global entry */
+ angband_term[i] = Term;
+ }
+
+ /* Raise the "Angband" window */
+ Infowin_set(data[0].win);
+ Infowin_raise();
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Try graphics */
+ if (arg_graphics)
+ {
+ /* Try the "16x16.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp");
+
+ /* Use the "16x16.bmp" file if it exists */
+ if (!force_old_graphics &&
+ (0 == fd_close(fd_open(filename, O_RDONLY))))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 16;
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ /* Try the "8x8.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp");
+
+ /* Use the "8x8.bmp" file if it exists */
+ if (0 == fd_close(fd_open(filename, O_RDONLY)))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 8;
+
+ ANGBAND_GRAF = "old";
+ }
+ }
+ }
+
+ /* Load graphics */
+ if (use_graphics)
+ {
+ Display *dpy = Metadpy->dpy;
+
+ XImage *tiles_raw;
+
+ /* Load the graphical tiles */
+ tiles_raw = ReadBMP(dpy, filename);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term *t = &td->t;
+
+ /* Graphics hook */
+ t->pict_hook = Term_pict_x11;
+
+ /* Use graphics sometimes */
+ t->higher_pict = TRUE;
+
+ /* Resize tiles */
+ td->tiles =
+ ResizeImage(dpy, tiles_raw,
+ pict_wid, pict_hgt,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+#ifdef USE_TRANSPARENCY
+ /* Initialize the transparency masks */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+ int ii, jj;
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+ int total;
+
+
+ /* Determine total bytes needed for image */
+ ii = 1;
+ jj = (depth - 1) >> 2;
+ while (jj >>= 1) ii <<= 1;
+ total = td->fnt->twid * td->fnt->hgt * ii;
+
+
+ TmpData = (char *)malloc(total);
+
+ td->TmpImage = XCreateImage(dpy, visual, depth,
+ ZPixmap, 0, TmpData,
+ td->fnt->twid, td->fnt->hgt, 8, 0);
+
+ }
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Free tiles_raw? XXX XXX */
+ }
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_X11 */
+
diff --git a/src/main-x11.c.orig b/src/main-x11.c.orig
new file mode 100644
index 00000000..5d859751
--- /dev/null
+++ b/src/main-x11.c.orig
@@ -0,0 +1,3299 @@
+/* File: main-x11.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with UNIX/X11 computers.
+ *
+ * To use this file, compile with "USE_X11" defined, and link against all
+ * the various "X11" libraries which may be needed.
+ *
+ * See also "main-xaw.c".
+ *
+ * Part of this file provides a user interface package composed of several
+ * pseudo-objects, including "metadpy" (a display), "infowin" (a window),
+ * "infoclr" (a color), and "infofnt" (a font). Actually, the package was
+ * originally much more interesting, but it was bastardized to keep this
+ * file simple.
+ *
+ * The rest of this file is an implementation of "main-xxx.c" for X11.
+ *
+ * Most of this file is by Ben Harrison (benh@phial.com).
+ */
+
+/*
+ * The following shell script can be used to launch Angband, assuming that
+ * it was extracted into "~/Angband", and compiled using "USE_X11", on a
+ * Linux machine, with a 1280x1024 screen, using 6 windows (with the given
+ * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142,
+ * and without graphics (add "-g" for graphics). Just copy this comment
+ * into a file, remove the leading " * " characters (and the head/tail of
+ * this comment), and make the file executable.
+ *
+ *
+ * #!/bin/csh
+ *
+ * # Describe attempt
+ * echo "Launching angband..."
+ * sleep 2
+ *
+ * # Main window
+ * setenv ANGBAND_X11_FONT_0 10x20
+ * setenv ANGBAND_X11_AT_X_0 5
+ * setenv ANGBAND_X11_AT_Y_0 510
+ *
+ * # Message window
+ * setenv ANGBAND_X11_FONT_1 8x13
+ * setenv ANGBAND_X11_AT_X_1 5
+ * setenv ANGBAND_X11_AT_Y_1 22
+ * setenv ANGBAND_X11_ROWS_1 35
+ *
+ * # Inventory window
+ * setenv ANGBAND_X11_FONT_2 8x13
+ * setenv ANGBAND_X11_AT_X_2 635
+ * setenv ANGBAND_X11_AT_Y_2 182
+ * setenv ANGBAND_X11_ROWS_3 23
+ *
+ * # Equipment window
+ * setenv ANGBAND_X11_FONT_3 8x13
+ * setenv ANGBAND_X11_AT_X_3 635
+ * setenv ANGBAND_X11_AT_Y_3 22
+ * setenv ANGBAND_X11_ROWS_3 12
+ *
+ * # Monster recall window
+ * setenv ANGBAND_X11_FONT_4 6x13
+ * setenv ANGBAND_X11_AT_X_4 817
+ * setenv ANGBAND_X11_AT_Y_4 847
+ * setenv ANGBAND_X11_COLS_4 76
+ * setenv ANGBAND_X11_ROWS_4 11
+ *
+ * # Object recall window
+ * setenv ANGBAND_X11_FONT_5 6x13
+ * setenv ANGBAND_X11_AT_X_5 817
+ * setenv ANGBAND_X11_AT_Y_5 520
+ * setenv ANGBAND_X11_COLS_5 76
+ * setenv ANGBAND_X11_ROWS_5 24
+ *
+ * # The build directory
+ * cd ~/Angband
+ *
+ * # Gamma correction
+ * setenv ANGBAND_X11_GAMMA 142
+ *
+ * # Launch Angband
+ * ./src/angband -mx11 -- -n6 &
+ *
+ */
+
+#include "angband.h"
+
+#ifdef USE_X11
+
+#ifndef __MAKEDEPEND__
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/Xatom.h>
+#endif /* __MAKEDEPEND__ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <sys/time.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+/*
+ * Include some helpful X11 code.
+ */
+#include "maid-x11.c"
+
+/*
+ * Hack -- avoid some compiler warnings
+ */
+#define IGNORE_UNUSED_FUNCTIONS
+
+
+/*
+ * Notes on Colors:
+ *
+ * 1) On a monochrome (or "fake-monochrome") display, all colors
+ * will be "cast" to "fg," except for the bg color, which is,
+ * obviously, cast to "bg". Thus, one can ignore this setting.
+ *
+ * 2) Because of the inner functioning of the color allocation
+ * routines, colors may be specified as (a) a typical color name,
+ * (b) a hexidecimal color specification (preceded by a pound sign),
+ * or (c) by strings such as "fg", "bg", "zg".
+ *
+ * 3) Due to the workings of the init routines, many colors
+ * may also be dealt with by their actual pixel values. Note that
+ * the pixel with all bits set is "zg = (1<<metadpy->depth)-1", which
+ * is not necessarily either black or white.
+ */
+
+
+
+/**** Generic Types ****/
+
+
+/*
+ * An X11 pixell specifier
+ */
+typedef unsigned long Pixell;
+
+/*
+ * The structures defined below
+ */
+typedef struct metadpy metadpy;
+typedef struct infowin infowin;
+typedef struct infoclr infoclr;
+typedef struct infofnt infofnt;
+
+
+/*
+ * A structure summarizing a given Display.
+ *
+ * - The Display itself
+ * - The default Screen for the display
+ * - The virtual root (usually just the root)
+ * - The default colormap (from a macro)
+ *
+ * - The "name" of the display
+ *
+ * - The socket to listen to for events
+ *
+ * - The width of the display screen (from a macro)
+ * - The height of the display screen (from a macro)
+ * - The bit depth of the display screen (from a macro)
+ *
+ * - The black Pixell (from a macro)
+ * - The white Pixell (from a macro)
+ *
+ * - The background Pixell (default: black)
+ * - The foreground Pixell (default: white)
+ * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly)
+ *
+ * - Bit Flag: Force all colors to black and white (default: !color)
+ * - Bit Flag: Allow the use of color (default: depth > 1)
+ * - Bit Flag: We created 'dpy', and so should nuke it when done.
+ */
+struct metadpy
+{
+ Display *dpy;
+ Screen *screen;
+ Window root;
+ Colormap cmap;
+
+ char *name;
+
+ int fd;
+
+ uint width;
+ uint height;
+ uint depth;
+
+ Pixell black;
+ Pixell white;
+
+ Pixell bg;
+ Pixell fg;
+ Pixell zg;
+
+uint mono:
+ 1;
+uint color:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+/*
+ * A Structure summarizing Window Information.
+ *
+ * I assume that a window is at most 30000 pixels on a side.
+ * I assume that the root windw is also at most 30000 square.
+ *
+ * - The Window
+ * - The current Input Event Mask
+ *
+ * - The location of the window
+ * - The width, height of the window
+ * - The border width of this window
+ *
+ * - Byte: 1st Extra byte
+ *
+ * - Bit Flag: This window is currently Mapped
+ * - Bit Flag: This window needs to be redrawn
+ * - Bit Flag: This window has been resized
+ *
+ * - Bit Flag: We should nuke 'win' when done with it
+ *
+ * - Bit Flag: 1st extra flag
+ * - Bit Flag: 2nd extra flag
+ * - Bit Flag: 3rd extra flag
+ * - Bit Flag: 4th extra flag
+ */
+struct infowin
+{
+ Window win;
+ long mask;
+
+ s16b ox, oy;
+
+ s16b x, y;
+ s16b w, h;
+ u16b b;
+
+ byte byte1;
+
+uint mapped:
+ 1;
+uint redraw:
+ 1;
+uint resize:
+ 1;
+
+uint nuke:
+ 1;
+
+uint flag1:
+ 1;
+uint flag2:
+ 1;
+uint flag3:
+ 1;
+uint flag4:
+ 1;
+};
+
+
+
+
+
+
+/*
+ * A Structure summarizing Operation+Color Information
+ *
+ * - The actual GC corresponding to this info
+ *
+ * - The Foreground Pixell Value
+ * - The Background Pixell Value
+ *
+ * - Num (0-15): The operation code (As in Clear, Xor, etc)
+ * - Bit Flag: The GC is in stipple mode
+ * - Bit Flag: Destroy 'gc' at Nuke time.
+ */
+struct infoclr
+{
+ GC gc;
+
+ Pixell fg;
+ Pixell bg;
+
+uint code:
+ 4;
+uint stip:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+/*
+ * A Structure to Hold Font Information
+ *
+ * - The 'XFontStruct*' (yields the 'Font')
+ *
+ * - The font name
+ *
+ * - The default character width
+ * - The default character height
+ * - The default character ascent
+ *
+ * - Byte: Pixel offset used during fake mono
+ *
+ * - Flag: Force monospacing via 'wid'
+ * - Flag: Nuke info when done
+ */
+struct infofnt
+{
+ XFontStruct *info;
+
+ cptr name;
+
+ s16b wid;
+ s16b twid;
+ s16b hgt;
+ s16b asc;
+
+ byte off;
+
+uint mono:
+ 1;
+uint nuke:
+ 1;
+};
+
+
+
+
+/**** Generic Macros ****/
+
+
+
+/* Set current metadpy (Metadpy) to 'M' */
+#define Metadpy_set(M) \
+Metadpy = M
+
+
+/* Initialize 'M' using Display 'D' */
+#define Metadpy_init_dpy(D) \
+Metadpy_init_2(D,cNULL)
+
+/* Initialize 'M' using a Display named 'N' */
+#define Metadpy_init_name(N) \
+Metadpy_init_2((Display*)(NULL),N)
+
+/* Initialize 'M' using the standard Display */
+#define Metadpy_init() \
+Metadpy_init_name("")
+
+
+/* Init an infowin by giving father as an (info_win*) (or NULL), and data */
+#define Infowin_init_dad(D,X,Y,W,H,B,FG,BG) \
+Infowin_init_data(((D) ? ((D)->win) : (Window)(None)), \
+X,Y,W,H,B,FG,BG)
+
+
+/* Init a top level infowin by pos,size,bord,Colors */
+#define Infowin_init_top(X,Y,W,H,B,FG,BG) \
+Infowin_init_data(None,X,Y,W,H,B,FG,BG)
+
+
+/* Request a new standard window by giving Dad infowin and X,Y,W,H */
+#define Infowin_init_std(D,X,Y,W,H,B) \
+Infowin_init_dad(D,X,Y,W,H,B,Metadpy->fg,Metadpy->bg)
+
+
+/* Set the current Infowin */
+#define Infowin_set(I) \
+(Infowin = (I))
+
+
+/* Set the current Infoclr */
+#define Infoclr_set(C) \
+(Infoclr = (C))
+
+
+#define Infoclr_init_ppo(F,B,O,M) \
+Infoclr_init_data(F,B,O,M)
+
+#define Infoclr_init_cco(F,B,O,M) \
+Infoclr_init_ppo(Infoclr_Pixell(F),Infoclr_Pixell(B),O,M)
+
+#define Infoclr_init_ppn(F,B,O,M) \
+Infoclr_init_ppo(F,B,Infoclr_Opcode(O),M)
+
+#define Infoclr_init_ccn(F,B,O,M) \
+Infoclr_init_cco(F,B,Infoclr_Opcode(O),M)
+
+
+/* Set the current infofnt */
+#define Infofnt_set(I) \
+(Infofnt = (I))
+
+
+/* Errr: Expose Infowin */
+#define Infowin_expose() \
+(!(Infowin->redraw = 1))
+
+/* Errr: Unxpose Infowin */
+#define Infowin_unexpose() \
+(Infowin->redraw = 0)
+
+
+
+/**** Generic Globals ****/
+
+
+/*
+ * The "default" values
+ */
+static metadpy metadpy_default;
+
+
+/*
+ * The "current" variables
+ */
+static metadpy *Metadpy = &metadpy_default;
+static infowin *Infowin = (infowin*)(NULL);
+static infoclr *Infoclr = (infoclr*)(NULL);
+static infofnt *Infofnt = (infofnt*)(NULL);
+
+
+/**** Generic code ****/
+
+/*
+ * Calculate how much space there is in the key queue for the current term.
+ */
+int Term_queue_space(void)
+{
+ /* Find the gap if the tail is before the head. */
+ int space = Term->key_tail - Term->key_head;
+
+ /* Otherwise, add in the extra for looping. */
+ if (space <= 0) space = Term->key_size - space;
+
+ /* The last space is never used as that would be interpreted as leaving
+ * no pending keypresses. */
+ return space -1;
+}
+
+
+/*
+ * Add a series of keypresses to the "queue".
+ *
+ * Return any errors generated by Term_keypress() in doing so, or SUCCESS
+ * if there are none.
+ *
+ * Catch the "out of space" error before anything is printed.
+ *
+ * NB: The keys added here will be interpreted by any macros or keymaps.
+ */
+errr type_string(char *str, uint len)
+{
+ char *s;
+
+ term *old = Term;
+
+ /* Paranoia - no string. */
+ if (!str) return 5;
+
+ /* Hack - calculate the string length here if none given. */
+ if (!len) len = strlen(str);
+
+ /* Activate the main window, as all pastes go there. */
+ Term_activate(term_screen);
+
+ /* Not enough space for the string. */
+ if (Term_queue_space() <= (int)len)
+ return 7;
+
+ for (s = str; s < str + len; s++)
+ {
+ errr err = Term_keypress(*s);
+
+ /* Catch errors other than "str[i] == 0", which is ignored. */
+ if (err && err != -1) return err;
+ }
+
+ /* Activate the original window. */
+ Term_activate(old);
+
+ return 0;
+}
+
+
+/*
+ * Init the current metadpy, with various initialization stuff.
+ *
+ * Inputs:
+ * dpy: The Display* to use (if NULL, create it)
+ * name: The name of the Display (if NULL, the current)
+ *
+ * Notes:
+ * If 'name' is NULL, but 'dpy' is set, extract name from dpy
+ * If 'dpy' is NULL, then Create the named Display
+ * If 'name' is NULL, and so is 'dpy', use current Display
+ *
+ * Return -1 if no Display given, and none can be opened.
+ */
+static errr Metadpy_init_2(Display *dpy, cptr name)
+{
+ metadpy *m = Metadpy;
+
+ /*** Open the display if needed ***/
+
+ /* If no Display given, attempt to Create one */
+ if (!dpy)
+ {
+ /* Attempt to open the display */
+ dpy = XOpenDisplay(name);
+
+ /* Failure */
+ if (!dpy) return ( -1);
+
+ /* We will have to nuke it when done */
+ m->nuke = 1;
+ }
+
+ /* Since the Display was given, use it */
+ else
+ {
+ /* We will not have to nuke it when done */
+ m->nuke = 0;
+ }
+
+
+ /*** Save some information ***/
+
+ /* Save the Display itself */
+ m->dpy = dpy;
+
+ /* Get the Screen and Virtual Root Window */
+ m->screen = DefaultScreenOfDisplay(dpy);
+ m->root = RootWindowOfScreen(m->screen);
+
+ /* Get the default colormap */
+ m->cmap = DefaultColormapOfScreen(m->screen);
+
+ /* Extract the true name of the display */
+ m->name = DisplayString(dpy);
+
+ /* Extract the fd */
+ m->fd = ConnectionNumber(Metadpy->dpy);
+
+ /* Save the Size and Depth of the screen */
+ m->width = WidthOfScreen(m->screen);
+ m->height = HeightOfScreen(m->screen);
+ m->depth = DefaultDepthOfScreen(m->screen);
+
+ /* Save the Standard Colors */
+ m->black = BlackPixelOfScreen(m->screen);
+ m->white = WhitePixelOfScreen(m->screen);
+
+ /*** Make some clever Guesses ***/
+
+ /* Guess at the desired 'fg' and 'bg' Pixell's */
+ m->bg = m->black;
+ m->fg = m->white;
+
+ /* Calculate the Maximum allowed Pixel value. */
+ m->zg = (1 << m->depth) - 1;
+
+ /* Save various default Flag Settings */
+ m->color = ((m->depth > 1) ? 1 : 0);
+ m->mono = ((m->color) ? 0 : 1);
+
+ /* Return "success" */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Nuke the current metadpy
+ */
+static errr Metadpy_nuke(void)
+{
+ metadpy *m = Metadpy;
+
+
+ /* If required, Free the Display */
+ if (m->nuke)
+ {
+ /* Close the Display */
+ XCloseDisplay(m->dpy);
+
+ /* Forget the Display */
+ m->dpy = (Display*)(NULL);
+
+ /* Do not nuke it again */
+ m->nuke = 0;
+ }
+
+ /* Return Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * General Flush/ Sync/ Discard routine
+ */
+static errr Metadpy_update(int flush, int sync, int discard)
+{
+ /* Flush if desired */
+ if (flush) XFlush(Metadpy->dpy);
+
+ /* Sync if desired, using 'discard' */
+ if (sync) XSync(Metadpy->dpy, discard);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Make a simple beep
+ */
+static errr Metadpy_do_beep(void)
+{
+ /* Make a simple beep */
+ XBell(Metadpy->dpy, 100);
+
+ return (0);
+}
+
+
+
+/*
+ * Set the name (in the title bar) of Infowin
+ */
+static errr Infowin_set_name(cptr name)
+{
+ Status st;
+ XTextProperty tp;
+ char buf[128];
+ char *bp = buf;
+ strcpy(buf, name);
+ st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp);
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Set the icon name of Infowin
+ */
+static errr Infowin_set_icon_name(cptr name)
+{
+ Status st;
+ XTextProperty tp;
+ char buf[128];
+ char *bp = buf;
+ strcpy(buf, name);
+ st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) XSetWMIconName(Metadpy->dpy, Infowin->win, &tp);
+ return (0);
+}
+
+
+/*
+ * Nuke Infowin
+ */
+static errr Infowin_nuke(void)
+{
+ infowin *iwin = Infowin;
+
+ /* Nuke if requested */
+ if (iwin->nuke)
+ {
+ /* Destory the old window */
+ XDestroyWindow(Metadpy->dpy, iwin->win);
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Prepare a new 'infowin'.
+ */
+static errr Infowin_prepare(Window xid)
+{
+ infowin *iwin = Infowin;
+
+ Window tmp_win;
+ XWindowAttributes xwa;
+ int x, y;
+ unsigned int w, h, b, d;
+
+ /* Assign stuff */
+ iwin->win = xid;
+
+ /* Check For Error XXX Extract some ACTUAL data from 'xid' */
+ XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d);
+
+ /* Apply the above info */
+ iwin->x = x;
+ iwin->y = y;
+ iwin->w = w;
+ iwin->h = h;
+ iwin->b = b;
+
+ /* Check Error XXX Extract some more ACTUAL data */
+ XGetWindowAttributes(Metadpy->dpy, xid, &xwa);
+
+ /* Apply the above info */
+ iwin->mask = xwa.your_event_mask;
+ iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1);
+
+ /* And assume that we are exposed */
+ iwin->redraw = 1;
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Initialize a new 'infowin'.
+ */
+static errr Infowin_init_real(Window xid)
+{
+ /* Wipe it clean */
+ (void)WIPE(Infowin, infowin);
+
+ /* Start out non-nukable */
+ Infowin->nuke = 0;
+
+ /* Attempt to Prepare ourself */
+ return (Infowin_prepare(xid));
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Init an infowin by giving some data.
+ *
+ * Inputs:
+ * dad: The Window that should own this Window (if any)
+ * x,y: The position of this Window
+ * w,h: The size of this Window
+ * b,d: The border width and pixel depth
+ *
+ * Notes:
+ * If 'dad == None' assume 'dad == root'
+ */
+static errr Infowin_init_data(Window dad, int x, int y, int w, int h,
+ int b, Pixell fg, Pixell bg)
+{
+ Window xid;
+
+ /* Wipe it clean */
+ (void)WIPE(Infowin, infowin);
+
+
+ /*** Error Check XXX ***/
+
+
+ /*** Create the Window 'xid' from data ***/
+
+ /* What happened here? XXX XXX XXX */
+
+ /* If no parent given, depend on root */
+ if (dad == None)
+
+ /* #ifdef USE_GRAPHICS
+
+ xid = XCreateWindow(Metadpy->dpy, Metadpy->root, x, y, w, h, b, 8, InputOutput, CopyFromParent, 0, 0);
+
+ else
+ */
+
+ /* #else */
+
+ dad = Metadpy->root;
+
+ /* #endif */
+
+ /* Create the Window XXX Error Check */
+ xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg);
+
+ /* Start out selecting No events */
+ XSelectInput(Metadpy->dpy, xid, 0L);
+
+
+ /*** Prepare the new infowin ***/
+
+ /* Mark it as nukable */
+ Infowin->nuke = 1;
+
+ /* Attempt to Initialize the infowin */
+ return (Infowin_prepare(xid));
+}
+
+
+
+/*
+ * Modify the event mask of an Infowin
+ */
+static errr Infowin_set_mask(long mask)
+{
+ /* Save the new setting */
+ Infowin->mask = mask;
+
+ /* Execute the Mapping */
+ XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Request that Infowin be mapped
+ */
+static errr Infowin_map(void)
+{
+ /* Execute the Mapping */
+ XMapWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request that Infowin be unmapped
+ */
+static errr Infowin_unmap(void)
+{
+ /* Execute the Un-Mapping */
+ XUnmapWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Request that Infowin be raised
+ */
+static errr Infowin_raise(void)
+{
+ /* Raise towards visibility */
+ XRaiseWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request that Infowin be lowered
+ */
+static errr Infowin_lower(void)
+{
+ /* Lower towards invisibility */
+ XLowerWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Request that Infowin be moved to a new location
+ */
+static errr Infowin_impell(int x, int y)
+{
+ /* Execute the request */
+ XMoveWindow(Metadpy->dpy, Infowin->win, x, y);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Resize an infowin
+ */
+static errr Infowin_resize(int w, int h)
+{
+ /* Execute the request */
+ XResizeWindow(Metadpy->dpy, Infowin->win, w, h);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Move and Resize an infowin
+ */
+static errr Infowin_locate(int x, int y, int w, int h)
+{
+ /* Execute the request */
+ XMoveResizeWindow(Metadpy->dpy, Infowin->win, x, y, w, h);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Visually clear Infowin
+ */
+static errr Infowin_wipe(void)
+{
+ /* Execute the request */
+ XClearWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Visually Paint Infowin with the current color
+ */
+static errr Infowin_fill(void)
+{
+ /* Execute the request */
+ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ 0, 0, Infowin->w, Infowin->h);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * A NULL terminated pair list of legal "operation names"
+ *
+ * Pairs of values, first is texttual name, second is the string
+ * holding the decimal value that the operation corresponds to.
+ */
+static cptr opcode_pairs[] =
+{
+ "cpy", "3",
+ "xor", "6",
+ "and", "1",
+ "ior", "7",
+ "nor", "8",
+ "inv", "10",
+ "clr", "0",
+ "set", "15",
+
+ "src", "3",
+ "dst", "5",
+
+ "+andReverse", "2",
+ "+andInverted", "4",
+ "+noop", "5",
+ "+equiv", "9",
+ "+orReverse", "11",
+ "+copyInverted", "12",
+ "+orInverted", "13",
+ "+nand", "14",
+ NULL
+};
+
+
+/*
+ * Parse a word into an operation "code"
+ *
+ * Inputs:
+ * str: A string, hopefully representing an Operation
+ *
+ * Output:
+ * 0-15: if 'str' is a valid Operation
+ * -1: if 'str' could not be parsed
+ */
+static int Infoclr_Opcode(cptr str)
+{
+ register int i;
+
+ /* Scan through all legal operation names */
+ for (i = 0; opcode_pairs[i*2]; ++i)
+ {
+ /* Is this the right oprname? */
+ if (streq(opcode_pairs[i*2], str))
+ {
+ /* Convert the second element in the pair into a Code */
+ return (atoi(opcode_pairs[i*2 + 1]));
+ }
+ }
+
+ /* The code was not found, return -1 */
+ return ( -1);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Request a Pixell by name. Note: uses 'Metadpy'.
+ *
+ * Inputs:
+ * name: The name of the color to try to load (see below)
+ *
+ * Output:
+ * The Pixell value that metched the given name
+ * 'Metadpy->fg' if the name was unparseable
+ *
+ * Valid forms for 'name':
+ * 'fg', 'bg', 'zg', '<name>' and '#<code>'
+ */
+static Pixell Infoclr_Pixell(cptr name)
+{
+ XColor scrn;
+
+ /* Attempt to Parse the name */
+ if (name && name[0])
+ {
+ /* The 'bg' color is available */
+ if (streq(name, "bg")) return (Metadpy->bg);
+
+ /* The 'fg' color is available */
+ if (streq(name, "fg")) return (Metadpy->fg);
+
+ /* The 'zg' color is available */
+ if (streq(name, "zg")) return (Metadpy->zg);
+
+ /* The 'white' color is available */
+ if (streq(name, "white")) return (Metadpy->white);
+
+ /* The 'black' color is available */
+ if (streq(name, "black")) return (Metadpy->black);
+
+ /* Attempt to parse 'name' into 'scrn' */
+ if (!(XParseColor(Metadpy->dpy, Metadpy->cmap, name, &scrn)))
+ {
+ plog_fmt("Warning: Couldn't parse color '%s'\n", name);
+ }
+
+ /* Attempt to Allocate the Parsed color */
+ if (!(XAllocColor(Metadpy->dpy, Metadpy->cmap, &scrn)))
+ {
+ plog_fmt("Warning: Couldn't allocate color '%s'\n", name);
+ }
+
+ /* The Pixel was Allocated correctly */
+ else return (scrn.pixel);
+ }
+
+ /* Warn about the Default being Used */
+ plog_fmt("Warning: Using 'fg' for unknown color '%s'\n", name);
+
+ /* Default to the 'Foreground' color */
+ return (Metadpy->fg);
+}
+
+
+/*
+ * Initialize a new 'infoclr' with a real GC.
+ */
+static errr Infoclr_init_1(GC gc)
+{
+ infoclr *iclr = Infoclr;
+
+ /* Wipe the iclr clean */
+ (void)WIPE(iclr, infoclr);
+
+ /* Assign the GC */
+ iclr->gc = gc;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Nuke an old 'infoclr'.
+ */
+static errr Infoclr_nuke(void)
+{
+ infoclr *iclr = Infoclr;
+
+ /* Deal with 'GC' */
+ if (iclr->nuke)
+ {
+ /* Free the GC */
+ XFreeGC(Metadpy->dpy, iclr->gc);
+ }
+
+ /* Forget the current */
+ Infoclr = (infoclr*)(NULL);
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Initialize an infoclr with some data
+ *
+ * Inputs:
+ * fg: The Pixell for the requested Foreground (see above)
+ * bg: The Pixell for the requested Background (see above)
+ * op: The Opcode for the requested Operation (see above)
+ * stip: The stipple mode
+ */
+static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip)
+{
+ infoclr *iclr = Infoclr;
+
+ GC gc;
+ XGCValues gcv;
+ unsigned long gc_mask;
+
+
+
+ /*** Simple error checking of opr and clr ***/
+
+ /* Check the 'Pixells' for realism */
+ if (bg > Metadpy->zg) return ( -1);
+ if (fg > Metadpy->zg) return ( -1);
+
+ /* Check the data for trueness */
+ if ((op < 0) || (op > 15)) return ( -1);
+
+
+ /*** Create the requested 'GC' ***/
+
+ /* Assign the proper GC function */
+ gcv.function = op;
+
+ /* Assign the proper GC background */
+ gcv.background = bg;
+
+ /* Assign the proper GC foreground */
+ gcv.foreground = fg;
+
+ /* Hack -- Handle XOR (xor is code 6) by hacking bg and fg */
+ if (op == 6) gcv.background = 0;
+ if (op == 6) gcv.foreground = (bg ^ fg);
+
+ /* Assign the proper GC Fill Style */
+ gcv.fill_style = (stip ? FillStippled : FillSolid);
+
+ /* Turn off 'Give exposure events for pixmap copying' */
+ gcv.graphics_exposures = False;
+
+ /* Set up the GC mask */
+ gc_mask = (GCFunction | GCBackground | GCForeground |
+ GCFillStyle | GCGraphicsExposures);
+
+ /* Create the GC detailed above */
+ gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv);
+
+
+ /*** Initialize ***/
+
+ /* Wipe the iclr clean */
+ (void)WIPE(iclr, infoclr);
+
+ /* Assign the GC */
+ iclr->gc = gc;
+
+ /* Nuke it when done */
+ iclr->nuke = 1;
+
+ /* Assign the parms */
+ iclr->fg = fg;
+ iclr->bg = bg;
+ iclr->code = op;
+ iclr->stip = stip ? 1 : 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Change the 'fg' for an infoclr
+ *
+ * Inputs:
+ * fg: The Pixell for the requested Foreground (see above)
+ */
+static errr Infoclr_change_fg(Pixell fg)
+{
+ infoclr *iclr = Infoclr;
+
+
+ /*** Simple error checking of opr and clr ***/
+
+ /* Check the 'Pixells' for realism */
+ if (fg > Metadpy->zg) return ( -1);
+
+
+ /*** Change ***/
+
+ /* Change */
+ XSetForeground(Metadpy->dpy, iclr->gc, fg);
+
+ /* Success */
+ return (0);
+}
+
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Nuke an old 'infofnt'.
+ */
+static errr Infofnt_nuke(void)
+{
+ infofnt *ifnt = Infofnt;
+
+ /* Deal with 'name' */
+ if (ifnt->name)
+ {
+ /* Free the name */
+ string_free(ifnt->name);
+ }
+
+ /* Nuke info if needed */
+ if (ifnt->nuke)
+ {
+ /* Free the font */
+ XFreeFont(Metadpy->dpy, ifnt->info);
+ }
+
+ /* Success */
+ return (0);
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Prepare a new 'infofnt'
+ */
+static errr Infofnt_prepare(XFontStruct *info)
+{
+ infofnt *ifnt = Infofnt;
+
+ XCharStruct *cs;
+
+ /* Assign the struct */
+ ifnt->info = info;
+
+ /* Jump into the max bouonds thing */
+ cs = &(info->max_bounds);
+
+ /* Extract default sizing info */
+ ifnt->asc = info->ascent;
+ ifnt->hgt = info->ascent + info->descent;
+ ifnt->wid = cs->width;
+ if (use_bigtile)
+ ifnt->twid = 2 * ifnt->wid;
+ else
+ ifnt->twid = ifnt->wid;
+
+
+#ifdef OBSOLETE_SIZING_METHOD
+ /* Extract default sizing info */
+ ifnt->asc = cs->ascent;
+ ifnt->hgt = (cs->ascent + cs->descent);
+ ifnt->wid = cs->width;
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+#ifndef IGNORE_UNUSED_FUNCTIONS
+
+/*
+ * Initialize a new 'infofnt'.
+ */
+static errr Infofnt_init_real(XFontStruct *info)
+{
+ /* Wipe the thing */
+ (void)WIPE(Infofnt, infofnt);
+
+ /* No nuking */
+ Infofnt->nuke = 0;
+
+ /* Attempt to prepare it */
+ return (Infofnt_prepare(info));
+}
+
+#endif /* IGNORE_UNUSED_FUNCTIONS */
+
+
+/*
+ * Init an infofnt by its Name
+ *
+ * Inputs:
+ * name: The name of the requested Font
+ */
+static errr Infofnt_init_data(cptr name)
+{
+ XFontStruct *info;
+
+
+ /*** Load the info Fresh, using the name ***/
+
+ /* If the name is not given, report an error */
+ if (!name) return ( -1);
+
+ /* Attempt to load the font */
+ info = XLoadQueryFont(Metadpy->dpy, name);
+
+ /* The load failed, try to recover */
+ if (!info) return ( -1);
+
+
+ /*** Init the font ***/
+
+ /* Wipe the thing */
+ (void)WIPE(Infofnt, infofnt);
+
+ /* Attempt to prepare it */
+ if (Infofnt_prepare(info))
+ {
+ /* Free the font */
+ XFreeFont(Metadpy->dpy, info);
+
+ /* Fail */
+ return ( -1);
+ }
+
+ /* Save a copy of the font name */
+ Infofnt->name = string_make(name);
+
+ /* Mark it as nukable */
+ Infofnt->nuke = 1;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Standard Text
+ */
+static errr Infofnt_text_std(int x, int y, cptr str, int len)
+{
+ int i;
+
+
+ /*** Do a brief info analysis ***/
+
+ /* Do nothing if the string is null */
+ if (!str || !*str) return ( -1);
+
+ /* Get the length of the string */
+ if (len < 0) len = strlen(str);
+
+
+ /*** Decide where to place the string, vertically ***/
+
+ /* Ignore Vertical Justifications */
+ y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
+
+
+ /*** Decide where to place the string, horizontally ***/
+
+ /* Line up with x at left edge of column 'x' */
+ x = (x * Infofnt->wid) + Infowin->ox;
+
+
+ /*** Actually draw 'str' onto the infowin ***/
+
+ /* Be sure the correct font is ready */
+ XSetFont(Metadpy->dpy, Infoclr->gc, Infofnt->info->fid);
+
+
+ /*** Handle the fake mono we can enforce on fonts ***/
+
+ /* Monotize the font */
+ if (Infofnt->mono)
+ {
+ /* Do each character */
+ for (i = 0; i < len; ++i)
+ {
+ /* Note that the Infoclr is set up to contain the Infofnt */
+ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ x + i * Infofnt->wid + Infofnt->off, y, str + i, 1);
+ }
+ }
+
+ /* Assume monoospaced font */
+ else
+ {
+ /* Note that the Infoclr is set up to contain the Infofnt */
+ XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc,
+ x, y, str, len);
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Painting where text would be
+ */
+static errr Infofnt_text_non(int x, int y, cptr str, int len)
+{
+ int w, h;
+
+
+ /*** Find the width ***/
+
+ /* Negative length is a flag to count the characters in str */
+ if (len < 0) len = strlen(str);
+
+ /* The total width will be 'len' chars * standard width */
+ w = len * Infofnt->wid;
+
+
+ /*** Find the X dimensions ***/
+
+ /* Line up with x at left edge of column 'x' */
+ x = x * Infofnt->wid + Infowin->ox;
+
+
+ /*** Find other dimensions ***/
+
+ /* Simply do 'Infofnt->hgt' (a single row) high */
+ h = Infofnt->hgt;
+
+ /* Simply do "at top" in row 'y' */
+ y = y * h + Infowin->oy;
+
+
+ /*** Actually 'paint' the area ***/
+
+ /* Just do a Fill Rectangle */
+ XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*************************************************************************/
+
+
+/*
+ * Angband specific code follows... (ANGBAND)
+ */
+
+
+/*
+ * Hack -- cursor color
+ */
+static infoclr *xor;
+
+/*
+ * Actual color table
+ */
+static infoclr *clr[256];
+
+/*
+ * Color info (unused, red, green, blue).
+ */
+static byte color_table[256][4];
+
+/*
+ * Forward declare
+ */
+typedef struct term_data term_data;
+
+/*
+ * A structure for each "term"
+ */
+struct term_data
+{
+ term t;
+
+ infofnt *fnt;
+
+ infowin *win;
+
+#ifdef USE_GRAPHICS
+
+ XImage *tiles;
+
+#ifdef USE_TRANSPARENCY
+
+ /* Tempory storage for overlaying tiles. */
+ XImage *TmpImage;
+
+#endif
+
+#endif
+
+};
+
+
+/*
+ * The number of term data structures
+ */
+#define MAX_TERM_DATA 8
+
+/*
+ * The array of term data structures
+ */
+static term_data data[MAX_TERM_DATA];
+
+/* Use short names for the most commonly used elements of various structures. */
+#define DPY (Metadpy->dpy)
+#define WIN (Infowin->win)
+
+/*
+ * Simply push a set of co-ordinates around.
+ */
+typedef struct co_ord co_ord;
+struct co_ord
+{
+ int x;
+ int y;
+};
+
+/*
+ * A special structure to store information about the text currently
+ * selected.
+ */
+typedef struct x11_selection_type x11_selection_type;
+struct x11_selection_type
+{
+ bool select; /* The selection is currently in use. */
+ bool drawn; /* The selection is currently displayed. */
+ term *t; /* The window where the selection is found. */
+ co_ord init; /* The starting co-ordinates. */
+ co_ord cur; /* The end co-ordinates (the current ones if still copying). */
+ co_ord old; /* The previous end co-ordinates. */
+ Time time; /* The time at which the selection was finalised. */
+};
+
+static x11_selection_type s_ptr[1];
+
+
+
+/*
+ * Process a keypress event
+ *
+ * Also appears in "main-xaw.c".
+ */
+static void react_keypress(XKeyEvent *xev)
+{
+ int i, n, mc, ms, mo, mx;
+
+ uint ks1;
+
+ XKeyEvent *ev = (XKeyEvent*)(xev);
+
+ KeySym ks;
+
+ char buf[128];
+ char msg[128];
+
+
+ /* Check for "normal" keypresses */
+ n = XLookupString(ev, buf, 125, &ks, NULL);
+
+ /* Terminate */
+ buf[n] = '\0';
+
+
+ /* Hack -- Ignore "modifier keys" */
+ if (IsModifierKey(ks)) return;
+
+
+ /* Hack -- convert into an unsigned int */
+ ks1 = (uint)(ks);
+
+ /* Extract four "modifier flags" */
+ mc = (ev->state & ControlMask) ? TRUE : FALSE;
+ ms = (ev->state & ShiftMask) ? TRUE : FALSE;
+ mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
+ mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
+
+
+ /* Normal keys with no modifiers */
+ if (n && !mo && !mx && !IsSpecialKey(ks))
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; buf[i]; i++) Term_keypress(buf[i]);
+
+ /* All done */
+ return;
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch (ks1)
+ {
+ case XK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return;
+ }
+
+ case XK_Return:
+ {
+ Term_keypress('\r');
+ return;
+ }
+
+ case XK_Tab:
+ {
+ Term_keypress('\t');
+ return;
+ }
+
+ case XK_Delete:
+ case XK_BackSpace:
+ {
+ Term_keypress('\010');
+ return;
+ }
+ }
+
+
+ /* Hack -- Use the KeySym */
+ if (ks)
+ {
+ sprintf(msg, "%c%s%s%s%s_%lX%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ (unsigned long)(ks), 13);
+ }
+
+ /* Hack -- Use the Keycode */
+ else
+ {
+ sprintf(msg, "%c%s%s%s%sK_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ ev->keycode, 13);
+ }
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+
+ /* Hack -- auto-define macros as needed */
+ if (n && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, buf);
+ }
+}
+
+
+/*
+ * Find the square a particular pixel is part of.
+ */
+static void pixel_to_square(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = (ox - Infowin->ox) / Infofnt->wid;
+ (*y) = (oy - Infowin->oy) / Infofnt->hgt;
+}
+
+/*
+ * Find the pixel at the top-left corner of a square.
+ */
+static void square_to_pixel(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = ox * Infofnt->wid + Infowin->ox;
+ (*y) = oy * Infofnt->hgt + Infowin->oy;
+}
+
+/*
+ * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
+ */
+static void sort_co_ord(co_ord *min, co_ord *max,
+ const co_ord *b, const co_ord *a)
+{
+ min->x = MIN(a->x, b->x);
+ min->y = MIN(a->y, b->y);
+ max->x = MAX(a->x, b->x);
+ max->y = MAX(a->y, b->y);
+}
+
+/*
+ * Remove the selection by redrawing it.
+ */
+static void mark_selection_clear(int x1, int y1, int x2, int y2)
+{
+ Term_redraw_section(x1, y1, x2, y2);
+}
+
+/*
+ * Select an area by drawing a grey box around it.
+ * NB. These two functions can cause flicker as the selection is modified,
+ * as the game redraws the entire marked section.
+ */
+static void mark_selection_mark(int x1, int y1, int x2, int y2)
+{
+ square_to_pixel(&x1, &y1, x1, y1);
+ square_to_pixel(&x2, &y2, x2, y2);
+ XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1,
+ x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
+}
+
+/*
+ * Mark a selection by drawing boxes around it (for now).
+ */
+static void mark_selection(void)
+{
+ co_ord min, max;
+ term *old = Term;
+ bool draw = s_ptr->select;
+ bool clear = s_ptr->drawn;
+
+ /* Open the correct term if necessary. */
+ if (s_ptr->t != old) Term_activate(s_ptr->t);
+
+ if (clear)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
+ mark_selection_clear(min.x, min.y, max.x, max.y);
+ }
+ if (draw)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+ mark_selection_mark(min.x, min.y, max.x, max.y);
+ }
+
+ /* Finish on the current term. */
+ if (s_ptr->t != old) Term_activate(old);
+
+ s_ptr->old.x = s_ptr->cur.x;
+ s_ptr->old.y = s_ptr->cur.y;
+ s_ptr->drawn = s_ptr->select;
+}
+
+/*
+ * Forget a selection for one reason or another.
+ */
+static void copy_x11_release(void)
+{
+ /* Deselect the current selection. */
+ s_ptr->select = FALSE;
+
+ /* Remove its graphical represesntation. */
+ mark_selection();
+}
+
+/*
+ * Start to select some text on the screen.
+ */
+static void copy_x11_start(int x, int y)
+{
+ if (s_ptr->select) copy_x11_release();
+
+ /* Remember where the selection started. */
+ s_ptr->t = Term;
+ s_ptr->init.x = s_ptr->cur.x = s_ptr->old.x = x;
+ s_ptr->init.y = s_ptr->cur.y = s_ptr->old.y = y;
+}
+
+/*
+ * Respond to movement of the mouse when selecting text.
+ */
+static void copy_x11_cont(int x, int y, unsigned int buttons)
+{
+ /* Use the nearest square within bounds if the mouse is outside. */
+ x = MIN(MAX(x, 0), Term->wid - 1);
+ y = MIN(MAX(y, 0), Term->hgt - 1);
+
+ /* The left mouse button isn't pressed. */
+ if (~buttons & Button1Mask) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Not enough movement. */
+ if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) return;
+
+ /* Something is being selected. */
+ s_ptr->select = TRUE;
+
+ /* Track the selection. */
+ s_ptr->cur.x = x;
+ s_ptr->cur.y = y;
+
+ /* Hack - display it inefficiently. */
+ mark_selection();
+}
+
+/*
+ * Respond to release of the left mouse button by putting the selected text in
+ * the primary buffer.
+ */
+static void copy_x11_end(const Time time)
+{
+ /* No selection. */
+ if (!s_ptr->select) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Remember when the selection was finalised. */
+ s_ptr->time = time;
+
+ /* Acquire the primary selection. */
+ XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
+ if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win)
+ {
+ /* Failed to acquire the selection, so forget it. */
+ bell();
+ s_ptr->select = FALSE;
+ mark_selection();
+ }
+}
+
+/*
+ * Send a message to request that the PRIMARY buffer be sent here.
+ */
+static void paste_x11_request(const Time time)
+{
+ XEvent event[1];
+ XSelectionRequestEvent *ptr = &(event->xselectionrequest);
+
+ /* Set various things. */
+ ptr->type = SelectionRequest;
+ ptr->display = Metadpy->dpy;
+ ptr->owner = XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY);
+ ptr->requestor = Infowin->win;
+ ptr->selection = XA_PRIMARY;
+ ptr->target = XA_STRING;
+ ptr->property = XA_STRING; /* Unused */
+ ptr->time = time;
+
+ /* Check the owner. */
+ if (ptr->owner == None)
+ {
+ /* No selection. */
+ bell();
+ return;
+ }
+
+ /* Send the SelectionRequest event. */
+ XSendEvent(Metadpy->dpy, ptr->owner, False, NoEventMask, event);
+}
+
+/*
+ * Add a character to a string in preparation for sending it to another
+ * client as a STRING.
+ * This doesn't change anything, as clients tend not to have difficulty in
+ * receiving this format (although the standard specifies a restricted set).
+ * Strings do not have a colour.
+ */
+static int add_char_string(char *buf, byte a, char c)
+{
+ *buf = c;
+ return 1;
+}
+
+/*
+ * Send some text requested by another X client.
+ */
+static void paste_x11_send(XSelectionRequestEvent *rq)
+{
+ XEvent event;
+ XSelectionEvent *ptr = &(event.xselection);
+ int (*add)(char *, byte, char) = 0;
+
+ /* Set the event parameters. */
+ ptr->type = SelectionNotify;
+ ptr->property = rq->property;
+ ptr->display = rq->display;
+ ptr->requestor = rq->requestor;
+ ptr->selection = rq->selection;
+ ptr->target = rq->target;
+ ptr->time = rq->time;
+
+ /* Determine the correct "add a character" function.
+ * As Term->wid is at most 255, these can add up to 4 characters of
+ * output per character of input without problem.
+ * The mechanism will need to change if much more than this is needed.
+ */
+ switch (rq->target)
+ {
+ case XA_STRING:
+ add = add_char_string;
+ break;
+ default:
+ goto error;
+ }
+
+ /* Reply to a known target received recently with data. */
+ if (rq->time >= s_ptr->time && add)
+ {
+ char buf[1024];
+ co_ord max, min;
+ int x, y, l;
+ byte a;
+ char c;
+
+ /* Work out which way around to paste. */
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+
+ /* Paranoia. */
+ if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN)
+ {
+ bell();
+ goto error;
+ }
+
+ /* Delete the old value of the property. */
+ XDeleteProperty(DPY, rq->requestor, rq->property);
+
+ for (y = 0; y < Term->hgt; y++)
+ {
+ if (y < min.y) continue;
+ if (y > max.y) break;
+
+ for (x = l = 0; x < Term->wid; x++)
+ {
+ if (x < min.x) continue;
+ if (x > max.x) break;
+
+ /* Find the character. */
+ Term_what(x, y, &a, &c);
+
+ /* Add it. */
+ l += (*add)(buf + l, a, c);
+ }
+
+ /* Terminate all but the last line in an appropriate way. */
+ if (y != max.y) l += (*add)(buf + l, TERM_WHITE, '\n');
+
+ /* Send the (non-empty) string. */
+ XChangeProperty(DPY, rq->requestor, rq->property, rq->target, 8,
+ PropModeAppend, buf, l);
+ }
+ }
+ else
+ {
+ /* Respond to all bad requests with property None. */
+error:
+ ptr->property = None;
+ }
+
+ /* Send whatever event we're left with. */
+ XSendEvent(DPY, rq->requestor, FALSE, NoEventMask, &event);
+}
+
+extern errr type_string(char *str, uint len);
+
+/*
+ * Add the contents of the PRIMARY buffer to the input queue.
+ *
+ * Hack - This doesn't use the "time" of the event, and so accepts anything a
+ * client tries to send it.
+ */
+static void paste_x11_accept(const XSelectionEvent *ptr)
+{
+ long offset, left;
+
+ /* Failure. */
+ if (ptr->property == None)
+ {
+ bell();
+ return;
+ }
+
+ if (ptr->selection != XA_PRIMARY)
+ {
+ bell();
+ return;
+ }
+ if (ptr->target != XA_STRING)
+ {
+ bell();
+ return;
+ }
+
+ for (offset = 0; ; offset += left)
+ {
+ errr err;
+
+ /* A pointer for the pasted information. */
+ unsigned char *data;
+
+ Atom type;
+ int fmt;
+ unsigned long nitems;
+
+ /* Set data to the string, and catch errors. */
+ if (XGetWindowProperty(Metadpy->dpy, Infowin->win, XA_STRING, offset,
+ 32767, TRUE, XA_STRING, &type, &fmt, &nitems, &left, &data)
+ != Success) break;
+
+ /* Paste the text. */
+ err = type_string(data, nitems);
+
+ /* Free the data pasted. */
+ XFree(data);
+
+ /* No room. */
+ if (err == 7)
+ {
+ bell();
+ break;
+ }
+ /* Paranoia? - strange errors. */
+ else if (err)
+ {
+ break;
+ }
+
+ /* Pasted everything. */
+ if (!left) return;
+ }
+
+ /* An error has occurred, so free the last bit of data before returning. */
+ XFree(data);
+}
+
+/*
+ * Handle various events conditional on presses of a mouse button.
+ */
+static void handle_button(Time time, int x, int y, int button,
+ bool press)
+{
+ /* The co-ordinates are only used in Angband format. */
+ pixel_to_square(&x, &y, x, y);
+
+ if (press && button == 1) copy_x11_start(x, y);
+ if (!press && button == 1) copy_x11_end(time);
+ if (!press && button == 2) paste_x11_request(time);
+}
+
+
+/*
+ * Process events
+ */
+static errr CheckEvent(bool wait)
+{
+ term_data *old_td = (term_data*)(Term->data);
+
+ XEvent xev_body, *xev = &xev_body;
+
+ term_data *td = NULL;
+ infowin *iwin = NULL;
+
+ int i;
+
+
+ /* Do not wait unless requested */
+ if (!wait && !XPending(Metadpy->dpy)) return (1);
+
+ /* Hack - redraw the selection, if needed.
+ * This doesn't actually check that one of its squares was drawn to,
+ * only that this may have happened.
+ */
+ if (s_ptr->select && !s_ptr->drawn) mark_selection();
+
+ /* Load the Event */
+ XNextEvent(Metadpy->dpy, xev);
+
+
+ /* Notice new keymaps */
+ if (xev->type == MappingNotify)
+ {
+ XRefreshKeyboardMapping(&xev->xmapping);
+ return 0;
+ }
+
+
+ /* Scan the windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ if (xev->xany.window == data[i].win->win)
+ {
+ td = &data[i];
+ iwin = td->win;
+ break;
+ }
+ }
+
+ /* Unknown window */
+ if (!td || !iwin) return (0);
+
+
+ /* Hack -- activate the Term */
+ Term_activate(&td->t);
+
+ /* Hack -- activate the window */
+ Infowin_set(iwin);
+
+
+ /* Switch on the Type */
+ switch (xev->type)
+ {
+
+ case ButtonPress:
+ case ButtonRelease:
+ {
+ bool press = (xev->type == ButtonPress);
+
+ /* Where is the mouse */
+ int x = xev->xbutton.x;
+ int y = xev->xbutton.y;
+
+ int z;
+
+ /* Which button is involved */
+ if (xev->xbutton.button == Button1) z = 1;
+ else if (xev->xbutton.button == Button2) z = 2;
+ else if (xev->xbutton.button == Button3) z = 3;
+ else if (xev->xbutton.button == Button4) z = 4;
+ else if (xev->xbutton.button == Button5) z = 5;
+ else z = 0;
+
+ /* Where is the mouse */
+ x = xev->xbutton.x;
+ y = xev->xbutton.y;
+
+ /* XXX Handle */
+ handle_button(xev->xbutton.time, x, y, z, press);
+
+ break;
+ }
+
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ /* Where is the mouse */
+ /* XXX Handle */
+
+ break;
+ }
+
+ case MotionNotify:
+ {
+ int x = xev->xmotion.x;
+ int y = xev->xmotion.y;
+ unsigned int z = xev->xmotion.state;
+
+ /* Convert to co-ordinates Angband understands. */
+ pixel_to_square(&x, &y, x, y);
+
+ /* Alter the selection if appropriate. */
+ copy_x11_cont(x, y, z);
+
+ /* XXX Handle */
+
+ break;
+ }
+
+ case SelectionNotify:
+ {
+ paste_x11_accept(&(xev->xselection));
+ break;
+ }
+
+ case SelectionRequest:
+ {
+ paste_x11_send(&(xev->xselectionrequest));
+ break;
+ }
+
+ case SelectionClear:
+ {
+ s_ptr->select = FALSE;
+ mark_selection();
+ break;
+ }
+
+ case KeyRelease:
+ {
+ /* Nothing */
+ break;
+ }
+
+ case KeyPress:
+ {
+ /* Hack -- use "old" term */
+ Term_activate(&old_td->t);
+
+ /* Process the key */
+ react_keypress(&(xev->xkey));
+
+ break;
+ }
+
+ case Expose:
+ {
+ /* Ignore "extra" exposes */
+ if (xev->xexpose.count) break;
+
+ /* Clear the window */
+ Infowin_wipe();
+
+ /* Redraw */
+ Term_redraw();
+
+ break;
+ }
+
+ case MapNotify:
+ {
+ Infowin->mapped = 1;
+ Term->mapped_flag = TRUE;
+ break;
+ }
+
+ case UnmapNotify:
+ {
+ Infowin->mapped = 0;
+ Term->mapped_flag = FALSE;
+ break;
+ }
+
+ /* Move and/or Resize */
+ case ConfigureNotify:
+ {
+ int cols, rows, wid, hgt;
+
+ int ox = Infowin->ox;
+ int oy = Infowin->oy;
+
+ /* Save the new Window Parms */
+ Infowin->x = xev->xconfigure.x;
+ Infowin->y = xev->xconfigure.y;
+ Infowin->w = xev->xconfigure.width;
+ Infowin->h = xev->xconfigure.height;
+
+ /* Determine "proper" number of rows/cols */
+ cols = ((Infowin->w - (ox + ox)) / td->fnt->wid);
+ rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt);
+
+ /* Hack -- minimal size */
+ if (td == &data[0])
+ {
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ else
+ {
+ if (cols < 1) cols = 1;
+ if (rows < 1) rows = 1;
+ }
+
+ /* Paranoia */
+ if (cols > 255) cols = 255;
+ if (rows > 255) rows = 255;
+
+ /* Desired size of window */
+ wid = cols * td->fnt->wid + (ox + ox);
+ hgt = rows * td->fnt->hgt + (oy + oy);
+
+ /* Resize the Term (if needed) */
+ Term_resize(cols, rows);
+
+ /* Resize the windows if any "change" is needed */
+ if ((Infowin->w != wid) || (Infowin->h != hgt))
+ {
+ /* Resize window */
+ Infowin_set(td->win);
+ Infowin_resize(wid, hgt);
+ }
+
+ break;
+ }
+ }
+
+
+ /* Hack -- Activate the old term */
+ Term_activate(&old_td->t);
+
+ /* Hack -- Activate the proper window */
+ Infowin_set(old_td->win);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle "activation" of a term
+ */
+static errr Term_xtra_x11_level(int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Handle "activate" */
+ if (v)
+ {
+ /* Activate the window */
+ Infowin_set(td->win);
+
+ /* Activate the font */
+ Infofnt_set(td->fnt);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_x11_react(void)
+{
+ int i;
+
+ if (Metadpy->color)
+ {
+ /* Check the colors */
+ for (i = 0; i < 256; i++)
+ {
+ if ((color_table[i][0] != angband_color_table[i][0]) ||
+ (color_table[i][1] != angband_color_table[i][1]) ||
+ (color_table[i][2] != angband_color_table[i][2]) ||
+ (color_table[i][3] != angband_color_table[i][3]))
+ {
+ Pixell pixel;
+
+ /* Save new values */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+
+ /* Change the foreground */
+ Infoclr_set(clr[i]);
+ Infoclr_change_fg(pixel);
+ }
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_x11(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ Metadpy_do_beep(); return (0);
+
+ /* Flush the output XXX XXX */
+ case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0);
+
+ /* Process random events XXX */
+ case TERM_XTRA_BORED:
+ {
+ irc_poll();
+
+ return (CheckEvent(0));
+ }
+
+ /* Process Events XXX */
+ case TERM_XTRA_EVENT:
+ {
+ irc_poll();
+
+ return (CheckEvent(v));
+ }
+
+ /* Flush the events XXX */
+ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0);
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL: return (Term_xtra_x11_level(v));
+
+ /* Clear the screen, and redraw any selection later. */
+ case TERM_XTRA_CLEAR: Infowin_wipe(); s_ptr->drawn = FALSE; return (0);
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ irc_poll();
+
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while ((entry = readdir(directory)))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+
+ closedir(directory);
+ return 0;
+ }
+
+ /* React to changes */
+ case TERM_XTRA_REACT: return (Term_xtra_x11_react());
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN: Infowin_set_name(angband_term_name[0]); return (0);
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Draw the cursor as an inverted rectangle.
+ *
+ * Consider a rectangular outline like "main-mac.c". XXX XXX
+ */
+static errr Term_curs_x11(int x, int y)
+{
+ /* Draw the cursor */
+ Infoclr_set(xor);
+
+ if (use_bigtile && x + 1 < Term->wid && Term->old->a[y][x + 1] == 255)
+ Infofnt_text_non(x, y, " ", 2);
+ else
+ /* Hilite the cursor character */
+ Infofnt_text_non(x, y, " ", 1);
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters.
+ */
+static errr Term_wipe_x11(int x, int y, int n)
+{
+ /* Erase (use black) */
+ Infoclr_set(clr[TERM_DARK]);
+
+ /* Mega-Hack -- Erase some space */
+ Infofnt_text_non(x, y, "", n);
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some textual characters.
+ */
+static errr Term_text_x11(int x, int y, int n, byte a, cptr s)
+{
+ /* Draw the text */
+ Infoclr_set(clr[a]);
+
+ /* Draw the text */
+ Infofnt_text_std(x, y, s, n);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_x11(int x, int y, int n, const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ int i, x1, y1;
+
+ byte a;
+ char c;
+
+
+#ifdef USE_TRANSPARENCY
+ byte ta;
+ char tc;
+ int x2, y2;
+
+# ifdef USE_EGO_GRAPHICS
+ byte ea;
+ char ec;
+ int x3, y3;
+ bool has_overlay;
+# endif /* USE_EGO_GRAPHICS */
+
+ int k, l;
+
+ unsigned long pixel, blank;
+#endif /* USE_TRANSPARENCY */
+
+ term_data *td = (term_data*)(Term->data);
+
+ y *= Infofnt->hgt;
+ x *= Infofnt->wid;
+
+ /* Add in affect of window boundaries */
+ y += Infowin->oy;
+ x += Infowin->ox;
+
+ for (i = 0; i < n; ++i, x += td->fnt->wid)
+ {
+ a = *ap++;
+ c = *cp++;
+
+ /* For extra speed - cache these values */
+ x1 = (c & 0x7F) * td->fnt->twid;
+ y1 = (a & 0x7F) * td->fnt->hgt;
+
+#ifdef USE_TRANSPARENCY
+
+ ta = *tap++;
+ tc = *tcp++;
+
+ /* For extra speed - cache these values */
+ x2 = (tc & 0x7F) * td->fnt->twid;
+ y2 = (ta & 0x7F) * td->fnt->hgt;
+
+# ifdef USE_EGO_GRAPHICS
+
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+ /* For extra speed - cache these values too */
+ x3 = (ec & 0x7F) * td->fnt->twid;
+ y3 = (ea & 0x7F) * td->fnt->hgt;
+
+# endif /* USE_EGO_GRAPHICS */
+
+ /* Optimise the common case */
+ if ((x1 == x2) && (y1 == y2))
+ {
+# ifndef USE_EGO_GRAPHICS
+
+ /* Draw object / terrain */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+# else /* !USE_EGO_GRAPHICS */
+
+ /* Draw object / terrain */
+ if (!has_overlay)
+ {
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+ /* There's a terrain overlay */
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* If mask set in overlay... */
+ if ((pixel = XGetPixel(td->tiles, x3 + k, y3 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(td->tiles, x1 + k, y1 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+ /* Draw to screen */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->TmpImage,
+ 0, 0, x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+ }
+ else
+ {
+
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
+
+# ifndef USE_EGO_GRAPHICS
+
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* If mask set... */
+ if ((pixel = XGetPixel(td->tiles, x1 + k, y1 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+# else /* !USE_EGO_GRAPHICS */
+
+ for (k = 0; k < td->fnt->twid; k++)
+ {
+ for (l = 0; l < td->fnt->hgt; l++)
+ {
+ /* Overlay */
+ if (has_overlay)
+ {
+ pixel = XGetPixel(td->tiles, x3 + k, y3 + l);
+ }
+
+ /* Hack -- No overlay */
+ else
+ {
+ pixel = blank;
+ }
+
+ /* If it's blank... */
+ if (pixel == blank)
+ {
+ /* Look at mon/obj */
+ pixel = XGetPixel(td->tiles, x1 + k, y1 + l);
+ }
+
+ /* If it's blank too, use terrain */
+ if (pixel == blank)
+ {
+ pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(td->TmpImage, k, l, pixel);
+ }
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+
+ /* Draw to screen */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->TmpImage,
+ 0, 0, x, y,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+#else /* USE_TRANSPARENCY */
+
+/* Draw object / terrain */
+ XPutImage(Metadpy->dpy, td->win->win,
+ clr[0]->gc,
+ td->tiles,
+ x1, y1,
+ x, y,
+ td->fnt->twid, td->fnt->hgt);
+
+#endif /* USE_TRANSPARENCY */
+ x += td->fnt->wid;
+ }
+
+ /* Redraw the selection if any, as it may have been obscured. (later) */
+ s_ptr->drawn = FALSE;
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+
+/*
+ * Initialize a term_data
+ */
+static errr term_data_init(term_data *td, int i)
+{
+ term *t = &td->t;
+
+ cptr name = angband_term_name[i];
+
+ cptr font;
+
+ int x = 0;
+ int y = 0;
+
+ int cols = 80;
+ int rows = 24;
+
+ int ox = 1;
+ int oy = 1;
+
+ int wid, hgt, num;
+
+ char buf[80];
+
+ cptr str;
+
+ int val;
+
+ XClassHint *ch;
+
+ char res_name[20];
+ char res_class[20];
+
+ XSizeHints *sh;
+
+
+ /* Window specific font name */
+ sprintf(buf, "ANGBAND_X11_FONT_%d", i);
+
+ /* Check environment for that font */
+ font = getenv(buf);
+
+ /* Check environment for "base" font */
+ if (!font) font = getenv("ANGBAND_X11_FONT");
+
+ /* No environment variables, use default font */
+ if (!font)
+ {
+ switch (i)
+ {
+ case 0:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 1:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 2:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 3:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 4:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 5:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 6:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ case 7:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ break;
+ default:
+ {
+ font = DEFAULT_X11_FONT;
+ }
+ }
+ }
+
+ /* Window specific location (x) */
+ sprintf(buf, "ANGBAND_X11_AT_X_%d", i);
+ str = getenv(buf);
+ x = (str != NULL) ? atoi(str) : -1;
+
+ /* Window specific location (y) */
+ sprintf(buf, "ANGBAND_X11_AT_Y_%d", i);
+ str = getenv(buf);
+ y = (str != NULL) ? atoi(str) : -1;
+
+
+ /* Window specific cols */
+ sprintf(buf, "ANGBAND_X11_COLS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) cols = val;
+
+ /* Window specific rows */
+ sprintf(buf, "ANGBAND_X11_ROWS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) rows = val;
+
+
+ /* Window specific inner border offset (ox) */
+ sprintf(buf, "ANGBAND_X11_IBOX_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) ox = val;
+
+ /* Window specific inner border offset (oy) */
+ sprintf(buf, "ANGBAND_X11_IBOY_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) oy = val;
+
+
+ /* Prepare the standard font */
+ MAKE(td->fnt, infofnt);
+ Infofnt_set(td->fnt);
+ Infofnt_init_data(font);
+
+ /* Hack -- key buffer size */
+ num = (i == 0 ? 1024 : 16);
+
+ /* Assume full size windows */
+ wid = cols * td->fnt->wid + (ox + ox);
+ hgt = rows * td->fnt->hgt + (oy + oy);
+
+ /* Create a top-window */
+ MAKE(td->win, infowin);
+ Infowin_set(td->win);
+ Infowin_init_top(x, y, wid, hgt, 0,
+ Metadpy->fg, Metadpy->bg);
+
+ /* Ask for certain events */
+ Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask |
+ PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
+
+ /* Set the window name */
+ Infowin_set_name(name);
+
+ /* Save the inner border */
+ Infowin->ox = ox;
+ Infowin->oy = oy;
+
+ /* Make Class Hints */
+ ch = XAllocClassHint();
+
+ if (ch == NULL) quit("XAllocClassHint failed");
+
+ strcpy(res_name, name);
+ res_name[0] = FORCELOWER(res_name[0]);
+ ch->res_name = res_name;
+
+ strcpy(res_class, "Angband");
+ ch->res_class = res_class;
+
+ XSetClassHint(Metadpy->dpy, Infowin->win, ch);
+
+ /* Make Size Hints */
+ sh = XAllocSizeHints();
+
+ /* Oops */
+ if (sh == NULL) quit("XAllocSizeHints failed");
+
+ /* Fixed window size */
+ if (i == 0)
+ {
+ /* Main window: 80x24 -- 255x255 */
+ sh->flags = PMinSize | PMaxSize;
+ sh->min_width = 80 * td->fnt->wid + (ox + ox);
+ sh->min_height = 24 * td->fnt->hgt + (oy + oy);
+ sh->max_width = 255 * td->fnt->wid + (ox + ox);
+ sh->max_height = 255 * td->fnt->hgt + (oy + oy);
+ }
+
+ /* Variable window size */
+ else
+ {
+ /* Subwindows: 1x1 -- 255x255 */
+ sh->flags = PMinSize | PMaxSize;
+ sh->min_width = td->fnt->wid + (ox + ox);
+ sh->min_height = td->fnt->hgt + (oy + oy);
+ sh->max_width = 255 * td->fnt->wid + (ox + ox);
+ sh->max_height = 255 * td->fnt->hgt + (oy + oy);
+ }
+
+ /* Resize increment */
+ sh->flags |= PResizeInc;
+ sh->width_inc = td->fnt->wid;
+ sh->height_inc = td->fnt->hgt;
+
+ /* Base window size */
+ sh->flags |= PBaseSize;
+ sh->base_width = (ox + ox);
+ sh->base_height = (oy + oy);
+
+ /* Use the size hints */
+ XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);
+
+ /* Map the window */
+ Infowin_map();
+
+
+ /* Move the window to requested location */
+ if ((x >= 0) && (y >= 0)) Infowin_impell(x, y);
+
+
+ /* Initialize the term */
+ term_init(t, cols, rows, num);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_x11;
+ t->curs_hook = Term_curs_x11;
+ t->wipe_hook = Term_wipe_x11;
+ t->text_hook = Term_text_x11;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialization function for an "X11" module to Angband
+ */
+errr init_x11(int argc, char *argv[])
+{
+ int i;
+
+ cptr dpy_name = "";
+
+ int num_term = 1;
+
+#ifdef USE_GRAPHICS
+
+ char filename[1024];
+
+ int pict_wid = 0;
+ int pict_hgt = 0;
+ bool force_old_graphics = FALSE;
+
+#ifdef USE_TRANSPARENCY
+
+ char *TmpData;
+#endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-d"))
+ {
+ dpy_name = &argv[i][2];
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ if (prefix(argv[i], "-s"))
+ {
+ smoothRescaling = FALSE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-o"))
+ {
+ force_old_graphics = TRUE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-b"))
+ {
+ arg_bigtile = use_bigtile = TRUE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Init the Metadpy if possible */
+ if (Metadpy_init_name(dpy_name)) return ( -1);
+
+
+ /* Prepare cursor color */
+ MAKE(xor, infoclr);
+ Infoclr_set(xor);
+ Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
+
+
+ /* Prepare normal colors */
+ for (i = 0; i < 256; ++i)
+ {
+ Pixell pixel;
+
+ MAKE(clr[i], infoclr);
+
+ Infoclr_set(clr[i]);
+
+ /* Acquire Angband colors */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Default to monochrome */
+ pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);
+
+ /* Handle color */
+ if (Metadpy->color)
+ {
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+ }
+
+ /* Initialize the color */
+ Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
+ }
+
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Save global entry */
+ angband_term[i] = Term;
+ }
+
+ /* Raise the "Angband" window */
+ Infowin_set(data[0].win);
+ Infowin_raise();
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Try graphics */
+ if (arg_graphics)
+ {
+ /* Try the "16x16.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp");
+
+ /* Use the "16x16.bmp" file if it exists */
+ if (!force_old_graphics &&
+ (0 == fd_close(fd_open(filename, O_RDONLY))))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 16;
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ /* Try the "8x8.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp");
+
+ /* Use the "8x8.bmp" file if it exists */
+ if (0 == fd_close(fd_open(filename, O_RDONLY)))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 8;
+
+ ANGBAND_GRAF = "old";
+ }
+ }
+ }
+
+ /* Load graphics */
+ if (use_graphics)
+ {
+ Display *dpy = Metadpy->dpy;
+
+ XImage *tiles_raw;
+
+ /* Load the graphical tiles */
+ tiles_raw = ReadBMP(dpy, filename);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term *t = &td->t;
+
+ /* Graphics hook */
+ t->pict_hook = Term_pict_x11;
+
+ /* Use graphics sometimes */
+ t->higher_pict = TRUE;
+
+ /* Resize tiles */
+ td->tiles =
+ ResizeImage(dpy, tiles_raw,
+ pict_wid, pict_hgt,
+ td->fnt->twid, td->fnt->hgt);
+ }
+
+#ifdef USE_TRANSPARENCY
+ /* Initialize the transparency masks */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+ int ii, jj;
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+ int total;
+
+
+ /* Determine total bytes needed for image */
+ ii = 1;
+ jj = (depth - 1) >> 2;
+ while (jj >>= 1) ii <<= 1;
+ total = td->fnt->twid * td->fnt->hgt * ii;
+
+
+ TmpData = (char *)malloc(total);
+
+ td->TmpImage = XCreateImage(dpy, visual, depth,
+ ZPixmap, 0, TmpData,
+ td->fnt->twid, td->fnt->hgt, 8, 0);
+
+ }
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Free tiles_raw? XXX XXX */
+ }
+
+#endif /* USE_GRAPHICS */
+
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_X11 */
+
diff --git a/src/main-x11.c.rej b/src/main-x11.c.rej
new file mode 100644
index 00000000..43a35fb7
--- /dev/null
+++ b/src/main-x11.c.rej
@@ -0,0 +1,17 @@
+***************
+*** 3198,3199
+- /* Try the "20x20.bmp" file */
+- path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/20x20.bmp");
+--- 3198,3199 -----
++ /* Try the "16x16.bmp" file */
++ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp");
+***************
+*** 3201
+- /* Use the "20x20.bmp" file if it exists */
+--- 3201 -----
++ /* Use the "16x16.bmp" file if it exists */
+***************
+*** 3208
+- pict_wid = pict_hgt = 20;
+--- 3208 -----
++ pict_wid = pict_hgt = 16;
diff --git a/src/main-xaw.c b/src/main-xaw.c
new file mode 100644
index 00000000..5d5333e9
--- /dev/null
+++ b/src/main-xaw.c
@@ -0,0 +1,1993 @@
+/* File: main-xaw.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, Torbjorn Lindgren, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband work with UNIX/X11 computers.
+ *
+ * To use this file, compile with "USE_XAW" defined, and link against all
+ * the various "X11" libraries which may be needed.
+ *
+ * See also "main-x11.c".
+ *
+ * The Angband widget is not as self-contained as it really should be.
+ * Originally everything was output to a Pixmap which was later copied
+ * to the screen when necessary. The idea was abandoned since Pixmaps
+ * create big performance problems for some really old X terminals (such
+ * as 3/50's running Xkernel).
+ *
+ * Initial framework (and some code) by Ben Harrison (benh@phial.com).
+ *
+ * Most of this file is by Torbjorn Lindgren (tl@cd.chalmers.se).
+ *
+ * Major modifications by Ben Harrison (benh@phial.com).
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_XAW
+
+
+#ifndef __MAKEDEPEND__
+#include <X11/Xlib.h>
+#include <X11/StringDefs.h>
+#include <X11/Xutil.h>
+#include <X11/Intrinsic.h>
+#include <X11/Shell.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/IntrinsicP.h>
+#include <X11/CoreP.h>
+#include <X11/ShellP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/SimpleP.h>
+#include <X11/Xaw/Simple.h>
+#include <X11/Xaw/XawInit.h>
+#endif /* __MAKEDEPEND__ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* /me pffts Solaris */
+#ifndef NAME_MAX
+#define NAME_MAX _POSIX_NAME_MAX
+#endif
+
+
+/*
+ * Include some helpful X11 code.
+ */
+#include "maid-x11.c"
+
+
+
+/**** Resources ****/
+
+
+/*
+
+Name Class RepType Default Value
+---- ----- ------- -------------
+background Background Pixel XtDefaultBackground
+border BorderColor Pixel XtDefaultForeground
+borderWidth BorderWidth Dimension 1
+cursor Cursor Cursor None
+cursorName Cursor String NULL
+destroyCallback Callback Pointer NULL
+height Height Dimension 0
+insensitiveBorder Insensitive Pixmap Gray
+mappedWhenManaged MappedWhenManaged Boolean True
+pointerColor Foreground Pixel XtDefaultForeground
+pointerColorBackground Background Pixel XtDefaultBackground
+sensitive Sensitive Boolean True
+width Width Dimension 0
+x Position Position 0
+y Position Position 0
+
+
+The colors can be changed using the standard Angband user pref files,
+which can also be used to provide black text on a white background,
+by setting color zero to "#FFFFFF" and color one to "#000000", since
+the other colors are unused.
+
+*/
+
+
+/*
+ * New resource names
+ */
+#define XtNstartRows "startRows"
+#define XtNstartColumns "startColumns"
+#define XtNminRows "minRows"
+#define XtNminColumns "minColumns"
+#define XtNmaxRows "maxRows"
+#define XtNmaxColumns "maxColumns"
+#define XtNinternalBorder "internalBorder"
+#define XtNredrawCallback "redrawCallback"
+
+/*
+ * Total normal colors
+ */
+#define NUM_COLORS 256
+
+/*
+ * Special "XOR" color
+ */
+#define COLOR_XOR 256
+
+
+
+/**** The Widget Code ****/
+
+
+/*
+ * Forward declarations
+ */
+typedef struct AngbandPart AngbandPart;
+typedef struct AngbandRec *AngbandWidget;
+typedef struct AngbandRec AngbandRec;
+typedef struct AngbandClassRec *AngbandWidgetClass;
+typedef struct AngbandClassPart AngbandClassPart;
+typedef struct AngbandClassRec AngbandClassRec;
+
+typedef struct term_data term_data;
+
+
+/*
+ * A structure for each "term"
+ */
+struct term_data
+{
+ term t;
+
+ AngbandWidget widget;
+};
+
+
+/*
+ * Maximum number of windows
+ */
+#define MAX_TERM_DATA 8
+
+
+/*
+ * An array of term_data's
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Current number of windows open
+ */
+static int num_term = MAX_TERM_DATA;
+
+
+/*
+ * New fields for the Angband widget record
+ */
+struct AngbandPart
+{
+ /* Settable resources */
+ int start_rows;
+ int start_columns;
+ int min_rows;
+ int min_columns;
+ int max_rows;
+ int max_columns;
+ int internal_border;
+ String font;
+
+ XtCallbackList redraw_callbacks;
+
+#ifdef USE_GRAPHICS
+
+ /* Tiles */
+ XImage *tiles;
+
+#ifdef USE_TRANSPARENCY
+
+ /* Tempory storage for overlaying tiles. */
+ XImage *TmpImage;
+
+#endif
+
+#endif /* USE_GRAPHICS */
+
+ /* Private state */
+ XFontStruct *fnt;
+ Dimension fontheight;
+ Dimension fontwidth;
+ Dimension fontascent;
+
+ /* Color info for GC's */
+ byte color[NUM_COLORS][4];
+
+ /* GC's (including "xor") */
+ GC gc[NUM_COLORS + 1];
+};
+
+
+/*
+ * Full instance record declaration
+ */
+struct AngbandRec
+{
+ CorePart core;
+ SimplePart simple;
+ AngbandPart angband;
+};
+
+
+/*
+ * New fields for the Angband widget class record
+ */
+struct AngbandClassPart
+{
+ int dummy;
+};
+
+
+/*
+ * Full class record declaration
+ */
+struct AngbandClassRec
+{
+ CoreClassPart core_class;
+ SimpleClassPart simple_class;
+ AngbandClassPart angband_class;
+};
+
+
+
+/*
+ * Hack -- see below
+ */
+#define offset(field) XtOffsetOf(AngbandRec, angband.field)
+
+
+/*
+ * Fallback resources for Angband widget
+ */
+static XtResource resources[] =
+{
+ { XtNstartRows, XtCValue, XtRInt, sizeof(int),
+ offset(start_rows), XtRImmediate, (XtPointer) 24 },
+ { XtNstartColumns, XtCValue, XtRInt, sizeof(int),
+ offset(start_columns), XtRImmediate, (XtPointer) 80 },
+ { XtNminRows, XtCValue, XtRInt, sizeof(int),
+ offset(min_rows), XtRImmediate, (XtPointer) 1 },
+ { XtNminColumns, XtCValue, XtRInt, sizeof(int),
+ offset(min_columns), XtRImmediate, (XtPointer) 1 },
+ { XtNmaxRows, XtCValue, XtRInt, sizeof(int),
+ offset(max_rows), XtRImmediate, (XtPointer) 255 },
+ { XtNmaxColumns, XtCValue, XtRInt, sizeof(int),
+ offset(max_columns), XtRImmediate, (XtPointer) 255 },
+ { XtNinternalBorder, XtCValue, XtRInt, sizeof(int),
+ offset(internal_border), XtRImmediate, (XtPointer) 2 },
+ { XtNfont, XtCFont, XtRString, sizeof(char *),
+ offset(font), XtRString, DEFAULT_X11_FONT },
+ { XtNredrawCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+ offset(redraw_callbacks), XtRCallback, (XtPointer)NULL }
+};
+
+
+/*
+ * Hack -- see above
+ */
+#undef offset
+
+
+/*
+ * Forward declarations for Widget functions
+ */
+static void Initialize(AngbandWidget request, AngbandWidget wnew);
+static void Redisplay(AngbandWidget w, XEvent *event, Region region);
+static Boolean SetValues(AngbandWidget current, AngbandWidget request,
+ AngbandWidget wnew, ArgList args, Cardinal *num_args);
+static void Destroy(AngbandWidget widget);
+static void Resize_term(AngbandWidget wnew);
+
+/*
+ * Forward declaration for internal functions
+ */
+static void calculateSizeHints(AngbandWidget wnew);
+static XFontStruct *getFont(AngbandWidget widget,
+ String font, Boolean fallback);
+
+
+/*
+ * Hack -- see below
+ */
+#define superclass (&simpleClassRec)
+
+
+/*
+ * Class record constanst
+ */
+AngbandClassRec angbandClassRec =
+{
+ {
+ /* Core class fields initialization */
+ /* superclass */ (WidgetClass) superclass,
+ /* class_name */ "Angband",
+ /* widget_size */ sizeof(AngbandRec),
+ /* class_initialize */ NULL,
+ /* class_part_initialize*/ NULL,
+ /* class_inited */ FALSE,
+ /* initialize */ (XtInitProc) Initialize,
+ /* initialize_hook */ NULL,
+ /* realize */ XtInheritRealize,
+ /* actions */ NULL,
+ /* num_actions */ 0,
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ TRUE,
+ /* compress_exposure */ XtExposeCompressMultiple,
+ /* compress_enterleave */ TRUE,
+ /* visible_interest */ FALSE,
+ /* destroy */ (XtWidgetProc) Destroy,
+ /* resize */ (XtWidgetProc) Resize_term,
+ /* expose */ (XtExposeProc) Redisplay,
+ /* set_values */ (XtSetValuesFunc) SetValues,
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ NULL,
+ /* accept_focus */ NULL,
+ /* version */ XtVersion,
+ /* callback_private */ NULL,
+ /* tm_table */ NULL,
+ /* query_geometry */ NULL,
+ /* display_accelerator */ XtInheritDisplayAccelerator,
+ /* extension */ NULL
+ },
+ /* Simple class fields initialization */
+ {
+ /* change_sensitive */ XtInheritChangeSensitive
+#ifndef OLDXAW
+ , NULL
+#endif
+ },
+ /* Angband class fields initialization */
+ {
+ /* nothing */ 0
+ }
+};
+
+/*
+ * Hack -- see above
+ */
+#undef superclass
+
+
+/*
+ * Class record pointer
+ */
+WidgetClass angbandWidgetClass = (WidgetClass) &angbandClassRec;
+
+
+/*
+ * Public procedures
+ */
+
+/*
+ * Simple routine to save the state of the game when the display connection
+ * is broken. Remember, you cannot do anything in this function that will
+ * generate X protocol requests.
+ */
+static int x_io_error_handler(Display *d)
+{
+ /* We have nothing to save */
+ if (!character_generated) return 0;
+
+ save_dungeon();
+ save_player();
+
+ return 0;
+}
+
+
+/*
+ * Clear an area
+ */
+static void AngbandClearArea(AngbandWidget widget,
+ int x, int y, int w, int h, int a)
+{
+ /* Figure out which area to clear */
+ y = y * widget->angband.fontheight + widget->angband.internal_border;
+ x = x * widget->angband.fontwidth + widget->angband.internal_border;
+
+ /* Clear the area */
+ XFillRectangle(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[a],
+ x, y,
+ widget->angband.fontwidth * w,
+ widget->angband.fontheight * h);
+}
+
+
+
+/*
+ * Output some text
+ */
+static void AngbandOutputText(AngbandWidget widget, int x, int y,
+ String txt, int len, int a)
+{
+ /* Do nothing if the string is null */
+ if (!txt || !*txt) return;
+
+ /* Check the length, and fix it if it's below zero */
+ if (len < 0) len = strlen(txt);
+
+ /* Figure out where to place the text */
+ y = (y * widget->angband.fontheight + widget->angband.fontascent +
+ widget->angband.internal_border);
+ x = (x * widget->angband.fontwidth + widget->angband.internal_border);
+
+ /* Place the string */
+ XDrawImageString(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[a], x, y, txt, len);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static void AngbandOutputPict(AngbandWidget widget, int x, int y, int n,
+ const byte *ap, const char *cp, const byte *tap, const char *tcp,
+ const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static void AngbandOutputPict(AngbandWidget widget, int x, int y, int n,
+ const byte *ap, const char *cp, const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static void AngbandOutputPict(AngbandWidget widget, int x, int y, int n,
+ const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+
+
+{
+ int i, x1, y1;
+
+ byte a;
+ char c;
+
+#ifdef USE_TRANSPARENCY
+ byte ta;
+ char tc;
+
+ int x2, y2;
+
+# ifdef USE_EGO_GRAPHICS
+ byte ea;
+ char ec;
+
+ int x3, y3;
+ bool has_overlay;
+
+# endif /* USE_EGO_GRAPHICS */
+ int k, l;
+ unsigned long pixel, blank;
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Figure out where to place the text */
+ y = (y * widget->angband.fontheight + widget->angband.internal_border);
+ x = (x * widget->angband.fontwidth + widget->angband.internal_border);
+
+ for (i = 0; i < n; ++i)
+ {
+ a = *ap++;
+ c = *cp++;
+
+ /* For extra speed - cache these values */
+ x1 = (c & 0x7F) * widget->angband.fontwidth;
+ y1 = (a & 0x7F) * widget->angband.fontheight;
+
+#ifdef USE_TRANSPARENCY
+
+ ta = *tap++;
+ tc = *tcp++;
+
+ /* For extra speed - cache these values */
+ x2 = (tc & 0x7F) * widget->angband.fontwidth;
+ y2 = (ta & 0x7F) * widget->angband.fontheight;
+
+# ifdef USE_EGO_GRAPHICS
+
+ ea = *eap++;
+ ec = *ecp++;
+ has_overlay = (ea && ec);
+
+ /* For extra speed -- cache these values */
+ x3 = (ec & 0x7F) * widget->angband.fontwidth;
+ y3 = (ea & 0x7F) * widget->angband.fontheight;
+
+# endif /* USE_EGO_GRAPHICS */
+
+ /* Optimise the common case */
+ if ((x1 == x2) && (y1 == y2))
+ {
+
+# ifndef USE_EGO_GRAPHICS
+
+ /* Draw object / terrain */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.tiles,
+ x1, y1,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+
+# else /* !USE_EGO_GRAPHICS */
+
+ /* No overlay */
+ if (!has_overlay)
+ {
+ /* Draw object / terrain */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.tiles,
+ x1, y1,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+ }
+
+ /* Terrain overlay */
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(widget->angband.tiles,
+ 0, widget->angband.fontheight * 6);
+
+ for (k = 0; k < widget->angband.fontwidth; k++)
+ {
+ for (l = 0; l < widget->angband.fontheight; l++)
+ {
+ /* If mask set... */
+ if ((pixel = XGetPixel(widget->angband.tiles,
+ x3 + k, y3 + l)) == blank)
+ {
+ /* Output from the terrain */
+ pixel = XGetPixel(widget->angband.tiles,
+ x1 + k, y1 + l);
+ }
+
+ /* Store into the temp storage */
+ XPutPixel(widget->angband.TmpImage,
+ k, l, pixel);
+ }
+ }
+
+ /* Draw terrain + overlay */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.TmpImage,
+ 0, 0,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+ }
+
+# endif /* !USE_EGO_GRAPHICS */
+
+ }
+ else
+ {
+ /* Mega Hack^2 - assume the top left corner is "black" */
+ blank = XGetPixel(widget->angband.tiles,
+ 0, widget->angband.fontheight * 6);
+
+# ifndef USE_EGO_GRAPHICS
+
+ for (k = 0; k < widget->angband.fontwidth; k++)
+ {
+ for (l = 0; l < widget->angband.fontheight; l++)
+ {
+ /* If mask set... */
+ if ((pixel = XGetPixel(widget->angband.tiles,
+ x1 + k, y1 + l)) == blank)
+ {
+
+ /* Output from the terrain */
+ pixel = XGetPixel(widget->angband.tiles,
+ x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(widget->angband.TmpImage,
+ k, l, pixel);
+ }
+ }
+
+#else /* !USE_EGO_GRAPHICS */
+
+ for (k = 0; k < widget->angband.fontwidth; k++)
+ {
+ for (l = 0; l < widget->angband.fontheight; l++)
+ {
+ /* Get overlay pixel */
+ if (has_overlay)
+ {
+ pixel = XGetPixel(widget->angband.tiles,
+ x3 + k, y3 + l);
+ }
+
+ /* Hack -- no overlay */
+ else
+ {
+ pixel = blank;
+ }
+
+ /* If it's blank */
+ if (pixel == blank)
+ {
+ /* Use obj/mon */
+ pixel = XGetPixel(widget->angband.tiles,
+ x1 + k, y1 + l);
+ }
+
+ /* Use terrain if it's blank too */
+ if (pixel == blank)
+ {
+ pixel = XGetPixel(widget->angband.tiles,
+ x2 + k, y2 + l);
+ }
+
+ /* Store into the temp storage. */
+ XPutPixel(widget->angband.TmpImage,
+ k, l, pixel);
+ }
+ }
+
+#endif /* !USE_EGO_GRAPHICS */
+
+ /* Draw to screen */
+
+ /* Draw object / terrain */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.TmpImage,
+ 0, 0,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+ }
+
+#else /* USE_TRANSPARENCY */
+
+ /* Draw object / terrain */
+ XPutImage(XtDisplay(widget), XtWindow(widget),
+ widget->angband.gc[0],
+ widget->angband.tiles,
+ x1, y1,
+ x, y,
+ widget->angband.fontwidth,
+ widget->angband.fontheight);
+
+#endif /* USE_TRANSPARENCY */
+
+ x += widget->angband.fontwidth;
+ }
+}
+
+#endif /* USE_GRAPHICS */
+
+/*
+ * Private procedures
+ */
+
+
+/*
+ * Procedure Initialize() is called during the widget creation
+ * process. Initialize() load fonts and calculates window geometry.
+ * The request parameter is filled in by parents to this widget. The
+ * wnew parameter is the request parameter plus data filled in by this
+ * widget. All changes should be done to the wnew parameter.
+ */
+static void Initialize(AngbandWidget request, AngbandWidget wnew)
+{
+ Display *dpy = XtDisplay(wnew);
+
+ int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew));
+
+ XGCValues gcv;
+ TopLevelShellWidget parent =
+ (TopLevelShellWidget)XtParent((Widget) wnew);
+ int i;
+
+ /* Default background pixel */
+ unsigned long bg = create_pixel(dpy,
+ angband_color_table[0][1],
+ angband_color_table[0][2],
+ angband_color_table[0][3]);
+
+ /* Default foreground pixel */
+ unsigned long fg = create_pixel(dpy,
+ angband_color_table[1][1],
+ angband_color_table[1][2],
+ angband_color_table[1][3]);
+
+ /* Fix the background color */
+ wnew->core.background_pixel = bg;
+
+ /* Get some information about the font */
+ wnew->angband.fnt = getFont(wnew, wnew->angband.font, TRUE);
+ wnew->angband.fontheight = wnew->angband.fnt->ascent +
+ wnew->angband.fnt->descent;
+ wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width;
+ wnew->angband.fontascent = wnew->angband.fnt->ascent;
+
+ /* Create and initialize the graphics contexts */ /* GXset? */
+ gcv.font = wnew->angband.fnt->fid;
+ gcv.graphics_exposures = FALSE;
+ gcv.background = bg;
+
+ for (i = 0; i < NUM_COLORS; i++)
+ {
+ unsigned long pixel;
+
+ /* Acquire Angband colors */
+ wnew->angband.color[i][0] = angband_color_table[i][0];
+ wnew->angband.color[i][1] = angband_color_table[i][1];
+ wnew->angband.color[i][2] = angband_color_table[i][2];
+ wnew->angband.color[i][3] = angband_color_table[i][3];
+
+ if (depth > 1)
+ {
+ /* Create pixel */
+ pixel = create_pixel(dpy,
+ wnew->angband.color[i][1],
+ wnew->angband.color[i][2],
+ wnew->angband.color[i][3]);
+ }
+ else
+ {
+ /* Use background or foreground */
+ pixel = ((i == 0) ? bg : fg);
+ }
+
+ gcv.foreground = pixel;
+
+ /* Copy */
+ gcv.function = 3;
+
+ wnew->angband.gc[i] = XtGetGC((Widget)wnew,
+ (GCFont | GCForeground | GCFunction |
+ GCBackground | GCGraphicsExposures),
+ &gcv);
+ }
+
+ /* Create a special GC for highlighting */
+ gcv.foreground = (BlackPixelOfScreen(XtScreen((Widget)wnew)) ^
+ WhitePixelOfScreen(XtScreen((Widget)wnew)));
+ gcv.background = 0;
+
+ gcv.function = GXxor;
+ wnew->angband.gc[COLOR_XOR] = XtGetGC((Widget)wnew,
+ (GCFunction | GCForeground | GCBackground |
+ GCGraphicsExposures),
+ &gcv);
+
+ /* Calculate window geometry */
+ wnew->core.height = (wnew->angband.start_rows * wnew->angband.fontheight +
+ 2 * wnew->angband.internal_border);
+ wnew->core.width = (wnew->angband.start_columns * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* We need to be able to resize the Widget if the user wants to */
+ /* change font on the fly! */
+ parent->shell.allow_shell_resize = TRUE;
+
+ /* Calculates all the size hints */
+ calculateSizeHints(wnew);
+}
+
+
+/*
+ * Procedure Destroy() is called during the destruction of the widget.
+ * Destroy() releases and frees GCs, frees the pixmaps and frees the
+ * fonts.
+ */
+static void Destroy(AngbandWidget widget)
+{
+ int n;
+
+ /* Free all GC's */
+ for (n = 0; n < NUM_COLORS + 1; n++)
+ {
+ XtReleaseGC((Widget)widget, widget->angband.gc[n]);
+ }
+
+ /* Free the font */
+ XFreeFont(XtDisplay((Widget)widget), widget->angband.fnt);
+}
+
+
+static void Resize_term(AngbandWidget wnew)
+{
+ int cols, rows, wid, hgt;
+
+ int ox = wnew->angband.internal_border;
+ int oy = wnew->angband.internal_border;
+
+ int i;
+ term_data *old_td;
+ term_data *td = &data[0];
+
+
+ /*
+ * Mega-Hack -- avoid calling this before the terms package is
+ * initialised
+ */
+ if (!Term) return;
+
+ old_td = (term_data*)(Term->data);
+
+ /* Hack - Find the term to activate */
+ for (i = 0; i < num_term; i++)
+ {
+ td = &data[i];
+
+ /* Paranoia: none of the widgets matched */
+ if (!td) return;
+
+ /* Have we found it? */
+ if (td->widget == wnew) break;
+ }
+
+ /* Paranoia -- No matches */
+ if (i == num_term) return;
+
+ /* Activate the proper Term */
+ Term_activate(&td->t);
+
+ /* Determine "proper" number of rows/cols */
+ cols = ((wnew->core.width - (ox + ox)) / wnew->angband.fontwidth);
+ rows = ((wnew->core.height - (oy + oy)) / wnew->angband.fontheight);
+
+ /* Hack -- minimal size */
+ if (cols < 1) cols = 1;
+ if (rows < 1) rows = 1;
+
+ if (i == 0)
+ {
+ /* Hack the main window must be at least 80x24 */
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ /* Desired size of window */
+ wid = cols * wnew->angband.fontwidth + (ox + ox);
+ hgt = rows * wnew->angband.fontheight + (oy + oy);
+
+ /* Resize the Term (if needed) */
+ (void) Term_resize(cols, rows);
+
+ /* Activate the old term */
+ Term_activate(&old_td->t);
+}
+
+/*
+ * Procedure Redisplay() is called as the result of an Expose event.
+ * Use the redraw callback to do a full redraw
+ */
+static void Redisplay(AngbandWidget wnew, XEvent *xev, Region region)
+{
+ int x1, x2, y1, y2;
+
+ int i;
+
+ term_data *old_td = (term_data*)(Term->data);
+ term_data *td = &data[0];
+
+ /* Hack - Find the term to activate */
+ for (i = 0; i < num_term; i++)
+ {
+ td = &data[i];
+
+ /* Have we found it? */
+ if (td->widget == wnew) break;
+
+ /* Paranoia: none of the widgets matched */
+ if (!td) return;
+ }
+
+ /* Activate the proper Term */
+ Term_activate(&td->t);
+
+ /* Find the bounds of the exposed region */
+
+ /*
+ * This probably could be obtained from the Region parameter -
+ * but I don't know anything about XAW.
+ */
+ x1 = (xev->xexpose.x - wnew->angband.internal_border)
+ / wnew->angband.fontwidth;
+ x2 = (xev->xexpose.x + xev->xexpose.width -
+ wnew->angband.internal_border) / wnew->angband.fontwidth;
+
+ y1 = (xev->xexpose.y - wnew->angband.internal_border)
+ / wnew->angband.fontheight;
+ y2 = (xev->xexpose.y + xev->xexpose.height -
+ wnew->angband.internal_border) / wnew->angband.fontheight;
+
+ Term_redraw_section(x1, y1, x2, y2);
+
+ /* Activate the old term */
+ Term_activate(&old_td->t);
+
+
+#if 0
+ if (XtHasCallbacks((Widget)widget, XtNredrawCallback) == XtCallbackHasSome)
+ {
+ XtCallCallbacks((Widget)widget, XtNredrawCallback, NULL);
+ }
+#endif /* 0 */
+}
+
+
+/*
+ * Font and internal_border can be changed on the fly.
+ *
+ * The entire widget is redrawn if any of those parameters change (all
+ * can potentially have effects that spans the whole widget).
+ *
+ * Color changes are handled elsewhere.
+ *
+ * This function is very underspecified, in terms of how these changes can
+ * occur, and what is true about the various AngbandWidget's passed in. It
+ * is very likely that this code no longer works.
+ */
+static Boolean SetValues(AngbandWidget current, AngbandWidget request,
+ AngbandWidget wnew, ArgList args,
+ Cardinal *num_args)
+{
+ Display *dpy = XtDisplay(wnew);
+
+ Boolean font_changed = FALSE;
+ Boolean border_changed = FALSE;
+ int height, width;
+ int i;
+
+
+ /* Handle font change */
+ if (wnew->angband.font != current->angband.font)
+ {
+ /* Check if the font exists */
+ wnew->angband.fnt = getFont(wnew, wnew->angband.font, FALSE);
+
+ /* The font didn't exist */
+ if (wnew->angband.fnt == NULL)
+ {
+ wnew->angband.fnt = current->angband.fnt;
+ wnew->angband.font = current->angband.font;
+ XtWarning("Couldn't find the requested font!");
+ }
+ else
+ {
+ font_changed = TRUE;
+
+ /* Free the old font */
+ XFreeFont(XtDisplay((Widget)wnew), current->angband.fnt);
+ /* Update font information */
+ wnew->angband.fontheight = wnew->angband.fnt->ascent +
+ wnew->angband.fnt->descent;
+ wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width;
+ wnew->angband.fontascent = wnew->angband.fnt->ascent;
+ }
+ }
+
+ /* Handle font change */
+ if (font_changed)
+ {
+ /* Update all GC's */
+ for (i = 0; i < NUM_COLORS; i++)
+ {
+ /* Steal the old GC */
+ wnew->angband.gc[i] = current->angband.gc[i];
+ current->angband.gc[i] = NULL;
+
+ /* Be sure the correct font is ready */
+ XSetFont(dpy, wnew->angband.gc[i], wnew->angband.fnt->fid);
+ }
+
+ /* Steal the old GC */
+ wnew->angband.gc[NUM_COLORS] = current->angband.gc[NUM_COLORS];
+ current->angband.gc[NUM_COLORS] = NULL;
+ }
+
+
+ /* Check if internal border width has changed, used later */
+ if (current->angband.internal_border != wnew->angband.internal_border)
+ {
+ border_changed = TRUE;
+ }
+
+
+ /* If the font or the internal border has changed, all geometry */
+ /* has to be recalculated */
+ if (font_changed || border_changed)
+ {
+ /* Change window size */
+ height = ((current->core.height - 2 * current->angband.internal_border) /
+ current->angband.fontheight * wnew->angband.fontheight +
+ 2 * current->angband.internal_border);
+ width = ((current->core.width - 2 * current->angband.internal_border) /
+ current->angband.fontwidth * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* Get the new width */
+ if (XtMakeResizeRequest((Widget)wnew, width, height, NULL, NULL) ==
+ XtGeometryNo)
+ {
+ /* Not allowed */
+ XtWarning("Size change denied!");
+ }
+ else
+ {
+ /* Recalculate size hints */
+ calculateSizeHints(wnew);
+ }
+ }
+
+ /* Tell it to redraw the widget if anything has changed */
+ return (font_changed || border_changed);
+}
+
+
+/*
+ * Calculate size hints
+ */
+static void calculateSizeHints(AngbandWidget wnew)
+{
+ TopLevelShellWidget parent =
+ (TopLevelShellWidget)XtParent((Widget) wnew);
+
+ /* Calculate minimum size */
+ parent->wm.size_hints.min_height =
+ (wnew->angband.min_rows * wnew->angband.fontheight +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate minimum size */
+ parent->wm.size_hints.min_width =
+ (wnew->angband.min_columns * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate minimum size */
+ parent->wm.size_hints.flags |= PMinSize;
+
+ /* Calculate maximum size */
+ parent->wm.size_hints.max_height =
+ (wnew->angband.max_rows * wnew->angband.fontheight +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate maximum size */
+ parent->wm.size_hints.max_width =
+ (wnew->angband.max_columns * wnew->angband.fontwidth +
+ 2 * wnew->angband.internal_border);
+
+ /* Calculate maximum size */
+ parent->wm.size_hints.flags |= PMaxSize;
+
+ /* Calculate increment size */
+ parent->wm.size_hints.height_inc = wnew->angband.fontheight;
+ parent->wm.size_hints.width_inc = wnew->angband.fontwidth;
+ parent->wm.size_hints.flags |= PResizeInc;
+
+ /* Calculate base size */
+ parent->wm.base_height = 2 * wnew->angband.internal_border;
+ parent->wm.base_width = 2 * wnew->angband.internal_border;
+ parent->wm.size_hints.flags |= PBaseSize;
+}
+
+
+/*
+ * Load a font
+ */
+static XFontStruct *getFont(AngbandWidget widget,
+ String font, Boolean fallback)
+{
+ Display *dpy = XtDisplay((Widget) widget);
+ char buf[256];
+ XFontStruct *fnt = NULL;
+
+ if (!(fnt = XLoadQueryFont(dpy, font)) && fallback)
+ {
+ sprintf(buf, "Can't find the font \"%s\", trying fixed\n", font);
+ XtWarning(buf);
+ if (!(fnt = XLoadQueryFont(dpy, "fixed")))
+ {
+ XtError("Can't fint the font \"fixed\"!, bailing out\n");
+ }
+ }
+
+ return fnt;
+}
+
+
+
+/*** The Angband code ****/
+
+
+
+
+
+/*
+ * Number of fallback resources per window
+ */
+#define TERM_FALLBACKS 6
+
+
+
+/*
+ * The names of the term_data's
+ */
+char *termNames[MAX_TERM_DATA] =
+{
+ "angband",
+ "mirror",
+ "recall",
+ "choice",
+ "term-4",
+ "term-5",
+ "term-6",
+ "term-7"
+};
+
+
+/*
+ * The special Arg's
+ */
+Arg specialArgs[TERM_FALLBACKS] =
+{
+ { XtNstartRows, 24},
+ { XtNstartColumns, 80},
+ { XtNminRows, 24},
+ { XtNminColumns, 80},
+ { XtNmaxRows, 255},
+ { XtNmaxColumns, 255}
+};
+
+
+/*
+ * The default Arg's
+ */
+Arg defaultArgs[TERM_FALLBACKS] =
+{
+ { XtNstartRows, 24},
+ { XtNstartColumns, 80},
+ { XtNminRows, 1},
+ { XtNminColumns, 1},
+ { XtNmaxRows, 255},
+ { XtNmaxColumns, 255}
+};
+
+
+/*
+ * The application context
+ */
+XtAppContext appcon;
+
+
+/*
+ * User changable information about widgets
+ */
+static String fallback[] =
+{
+ "Angband.angband.iconName: ToME",
+ "Angband.angband.title: ToME",
+ "Angband.term-1.iconName: Mirror",
+ "Angband.term-1.title: Mirror",
+ "Angband.term-2.iconName: Recall",
+ "Angband.term-2.title: Recall",
+ "Angband.term-3.iconName: Choice",
+ "Angband.term-3.title: Choice",
+ "Angband.term-4.iconName: Term 4",
+ "Angband.term-4.title: Term 4",
+ "Angband.term-5.iconName: Term 5",
+ "Angband.term-5.title: Term 5",
+ "Angband.term-6.iconName: Term 6",
+ "Angband.term-6.title: Term 6",
+ "Angband.term-7.iconName: Term 7",
+ "Angband.term-7.title: Term 7",
+ NULL
+};
+
+
+
+/*
+ * Do a redraw
+ */
+static void react_redraw(Widget widget,
+ XtPointer client_data, XtPointer call_data)
+{
+ term_data *old_td = (term_data*)(Term->data);
+ term_data *td = (term_data*)client_data;
+
+ /* Activate the proper Term */
+ Term_activate(&td->t);
+
+ /* Request a redraw */
+ Term_redraw();
+
+ /* Activate the old Term */
+ Term_activate(&old_td->t);
+}
+
+
+
+/*
+ * Process a keypress event
+ *
+ * Also appears in "main-x11.c".
+ */
+static void react_keypress(XKeyEvent *xev)
+{
+ int i, n, mc, ms, mo, mx;
+
+ uint ks1;
+
+ XKeyEvent *ev = (XKeyEvent*)(xev);
+
+ KeySym ks;
+
+ char buf[128];
+ char msg[128];
+
+
+ /* Check for "normal" keypresses */
+ n = XLookupString(ev, buf, 125, &ks, NULL);
+
+ /* Terminate */
+ buf[n] = '\0';
+
+
+ /* Hack -- Ignore "modifier keys" */
+ if (IsModifierKey(ks)) return;
+
+
+ /* Hack -- convert into an unsigned int */
+ ks1 = (uint)(ks);
+
+ /* Extract four "modifier flags" */
+ mc = (ev->state & ControlMask) ? TRUE : FALSE;
+ ms = (ev->state & ShiftMask) ? TRUE : FALSE;
+ mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
+ mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
+
+
+ /* Normal keys with no modifiers */
+ if (n && !mo && !mx && !IsSpecialKey(ks))
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; buf[i]; i++) Term_keypress(buf[i]);
+
+ /* All done */
+ return;
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch (ks1)
+ {
+ case XK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return;
+ }
+
+ case XK_Return:
+ {
+ Term_keypress('\r');
+ return;
+ }
+
+ case XK_Tab:
+ {
+ Term_keypress('\t');
+ return;
+ }
+
+ case XK_Delete:
+ case XK_BackSpace:
+ {
+ Term_keypress('\010');
+ return;
+ }
+ }
+
+
+ /* Hack -- Use the KeySym */
+ if (ks)
+ {
+ sprintf(msg, "%c%s%s%s%s_%lX%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ (unsigned long)(ks), 13);
+ }
+
+ /* Hack -- Use the Keycode */
+ else
+ {
+ sprintf(msg, "%c%s%s%s%sK_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ ev->keycode, 13);
+ }
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+
+ /* Hack -- auto-define macros as needed */
+ if (n && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, buf);
+ }
+}
+
+
+/*
+ * Handle an event
+ */
+static void handle_event(Widget widget, XtPointer client_data, XEvent *event,
+ Boolean *continue_to_dispatch)
+{
+ term_data *old_td = (term_data*)(Term->data);
+ term_data *td = (term_data *)client_data;
+
+ /* Continue to process the event by default */
+ *continue_to_dispatch = TRUE;
+
+ /* Activate the Term */
+ Term_activate(&td->t);
+
+ switch (event->type)
+ {
+ case KeyPress:
+ {
+ /* Hack -- use old term */
+ Term_activate(&old_td->t);
+
+ /* Handle the keypress */
+ react_keypress(&(event->xkey));
+
+ /* We took care of the event */
+ *continue_to_dispatch = FALSE;
+
+ break;
+ }
+
+ /* Oops */
+ default:
+ {
+ break;
+ }
+ }
+
+ /* Activate the old term */
+ Term_activate(&old_td->t);
+
+ return;
+}
+
+
+/*
+ * Process an event (or just check for one)
+ */
+errr CheckEvent(bool wait)
+{
+ XEvent event;
+
+ /* No events ready, and told to just check */
+ if (!wait && !XtAppPending(appcon)) return 1;
+
+ /* Process */
+ while (1)
+ {
+ XtAppNextEvent(appcon, &event);
+ XtDispatchEvent(&event);
+ if (!XtAppPending(appcon)) break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Monstrous hack.
+ */
+static void Term_xtra_xaw_react_aux(term_data *td)
+{
+ AngbandWidget wnew = td->widget;
+
+ Display *dpy = XtDisplay((Widget) wnew);
+
+ int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew));
+
+ int i;
+
+ /* See if any colors need to be changed */
+ for (i = 0; i < NUM_COLORS; i++)
+ {
+ if (depth > 1)
+ {
+ if ((wnew->angband.color[i][0] != angband_color_table[i][0]) ||
+ (wnew->angband.color[i][1] != angband_color_table[i][1]) ||
+ (wnew->angband.color[i][2] != angband_color_table[i][2]) ||
+ (wnew->angband.color[i][3] != angband_color_table[i][3]))
+ {
+ unsigned long pixel;
+
+ /* Save new values */
+ wnew->angband.color[i][0] = angband_color_table[i][0];
+ wnew->angband.color[i][1] = angband_color_table[i][1];
+ wnew->angband.color[i][2] = angband_color_table[i][2];
+ wnew->angband.color[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(dpy,
+ wnew->angband.color[i][1],
+ wnew->angband.color[i][2],
+ wnew->angband.color[i][3]);
+
+
+ /* Change */
+ XSetForeground(dpy, wnew->angband.gc[i], pixel);
+ }
+ }
+ }
+}
+
+
+/*
+ * Monstrous hack.
+ */
+static errr Term_xtra_xaw_react(void)
+{
+ int i;
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ if (!td) break;
+
+ Term_xtra_xaw_react_aux(td);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_xaw(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ Widget widget = (Widget)(td->widget);
+
+ Display *dpy = XtDisplay(widget);
+
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ XBell(dpy, 100);
+ return (0);
+
+ /* Flush the output */
+ case TERM_XTRA_FRESH:
+ XFlush(dpy);
+ /* Allow flushed events to be showed */
+ CheckEvent(FALSE);
+ return (0);
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ return (CheckEvent(0));
+
+ /* Process events */
+ case TERM_XTRA_EVENT:
+ return (CheckEvent(v));
+
+ /* Flush events */
+ case TERM_XTRA_FLUSH:
+ while (!CheckEvent(FALSE));
+ return (0);
+
+ /* Clear the window */
+ case TERM_XTRA_CLEAR:
+ XClearWindow(dpy, XtWindow(widget));
+ return (0);
+
+ /* Delay */
+ case TERM_XTRA_DELAY:
+ usleep(1000 * v);
+ return (0);
+
+ /* Get Delay of some milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ int ret;
+ struct timeval tv;
+
+ ret = gettimeofday(&tv, NULL);
+ Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ return ret;
+ }
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ DIR *directory;
+ struct dirent *entry;
+
+ scansubdir_max = 0;
+
+ directory = opendir(scansubdir_dir);
+ if (!directory)
+ return 1;
+
+ while (entry = readdir(directory))
+ {
+ char file[PATH_MAX + NAME_MAX + 2];
+ struct stat filedata;
+
+ file[PATH_MAX + NAME_MAX] = 0;
+ strncpy(file, scansubdir_dir, PATH_MAX);
+ strncat(file, "/", 2);
+ strncat(file, entry->d_name, NAME_MAX);
+ if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->d_name);
+ ++scansubdir_max;
+ }
+ }
+ }
+
+ case TERM_XTRA_REACT:
+ return (Term_xtra_xaw_react());
+ }
+
+ /* Unknown */
+ return (1);
+}
+
+
+
+/*
+ * Erase a number of characters
+ */
+static errr Term_wipe_xaw(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Erase using color 0 */
+ AngbandClearArea(td->widget, x, y, n, 1, 0);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Draw the cursor, by hiliting with XOR
+ *
+ * Should perhaps use rectangle outline, ala "main-mac.c". XXX XXX XXX
+ */
+static errr Term_curs_xaw(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Hilite the cursor character */
+ AngbandClearArea(td->widget, x, y, 1, 1, COLOR_XOR);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw a number of characters
+ */
+static errr Term_text_xaw(int x, int y, int n, byte a, cptr s)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Draw the text */
+ AngbandOutputText(td->widget, x, y, (String)s, n, a);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef USE_GRAPHICS
+
+/*
+ * Draw some graphical characters.
+ */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+static errr Term_pict_xaw(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+# else /* USE_EGO_GRAPHICS */
+static errr Term_pict_xaw(int x, int y, int n, const byte *ap, const char *cp,
+ const byte *tap, const char *tcp)
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+static errr Term_pict_xaw(int x, int y, int n, const byte *ap, const char *cp)
+# endif /* USE_TRANSPARENCY */
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Draw the pictures */
+# ifdef USE_TRANSPARENCY
+# ifdef USE_EGO_GRAPHICS
+ AngbandOutputPict(td->widget, x, y, n, ap, cp, tap, tcp, eap, ecp);
+# else /* USE_EGO_GRAPHICS */
+ AngbandOutputPict(td->widget, x, y, n, ap, cp, tap, tcp);
+# endif /* USE_EGO_GRAPHICS */
+# else /* USE_TRANSPARENCY */
+ AngbandOutputPict(td->widget, x, y, n, ap, cp);
+# endif /* USE_TRANSPARENCY */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GRAPHICS */
+
+
+/*
+ * Raise a term
+ */
+static void term_raise(term_data *td)
+{
+ Widget widget = (Widget)(td->widget);
+
+ XRaiseWindow(XtDisplay(XtParent(widget)), XtWindow(XtParent(widget)));
+}
+
+
+/*
+ * Initialize a term_data
+ */
+static errr term_data_init(term_data *td, Widget topLevel,
+ int key_buf, String name,
+ ArgList widget_arg, Cardinal widget_arg_no, int i)
+{
+ Widget parent;
+ term *t = &td->t;
+
+ int cols = 80;
+ int rows = 24;
+
+ char buf[80];
+ cptr str;
+
+ int val;
+
+ /* Create the shell widget */
+ parent = XtCreatePopupShell(name, topLevelShellWidgetClass, topLevel,
+ NULL, 0);
+
+ /* Window specific cols */
+ sprintf(buf, "ANGBAND_X11_COLS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) cols = val;
+
+ /* Window specific rows */
+ sprintf(buf, "ANGBAND_X11_ROWS_%d", i);
+ str = getenv(buf);
+ val = (str != NULL) ? atoi(str) : -1;
+ if (val > 0) rows = val;
+
+ /* Hack the main window must be at least 80x24 */
+ if (i == 0)
+ {
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ /* Reset the initial size */
+ widget_arg[0].value = rows;
+ widget_arg[1].value = cols;
+
+ /* Create the interior widget */
+ td->widget = (AngbandWidget)
+ XtCreateManagedWidget(name, angbandWidgetClass,
+ parent, widget_arg, widget_arg_no);
+
+ /* Initialize the term (full size) */
+ term_init(t, cols, rows, key_buf);
+
+ /* Use a "soft" cursor */
+ t->soft_cursor = TRUE;
+
+ /* Erase with "white space" */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_xaw;
+ t->curs_hook = Term_curs_xaw;
+ t->wipe_hook = Term_wipe_xaw;
+ t->text_hook = Term_text_xaw;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Register the keypress event handler */
+ XtAddEventHandler((Widget)td->widget, KeyPressMask,
+ False, (XtEventHandler) handle_event, td);
+
+ /* Redraw callback */
+ XtAddCallback((Widget)td->widget, XtNredrawCallback,
+ react_redraw, td);
+
+ /* Realize the widget */
+ XtRealizeWidget(parent);
+
+ /* Make it visible */
+ XtPopup(parent, XtGrabNone);
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ Resize_term(td->widget);
+
+ return 0;
+}
+
+
+/*
+ * Initialization function for an X Athena Widget module to Angband
+ *
+ * We should accept "-d<dpy>" requests in the "argv" array. XXX XXX XXX
+ */
+errr init_xaw(int argc, char *argv[])
+{
+ int i;
+ Widget topLevel;
+ Display *dpy;
+
+ cptr dpy_name = "";
+
+
+#ifdef USE_GRAPHICS
+
+ char filename[1024];
+
+ int pict_wid = 0;
+ int pict_hgt = 0;
+ bool force_old_graphics = FALSE;
+
+#ifdef USE_TRANSPARENCY
+
+ char *TmpData;
+#endif /* USE_TRANSPARENCY */
+
+#endif /* USE_GRAPHICS */
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-d"))
+ {
+ dpy_name = &argv[i][2];
+ continue;
+ }
+
+#ifdef USE_GRAPHICS
+
+ if (prefix(argv[i], "-s"))
+ {
+ smoothRescaling = FALSE;
+ continue;
+ }
+
+ if (prefix(argv[i], "-o"))
+ {
+ force_old_graphics = TRUE;
+ continue;
+ }
+
+#endif /* USE_GRAPHICS */
+
+ if (prefix(argv[i], "-n"))
+ {
+ num_term = atoi(&argv[i][2]);
+ if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
+ else if (num_term < 1) num_term = 1;
+ continue;
+ }
+
+ plog_fmt("Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Attempt to open the local display */
+ dpy = XOpenDisplay(dpy_name);
+
+ /* Failure -- assume no X11 available */
+ if (!dpy) return ( -1);
+
+ /* Close the local display */
+ XCloseDisplay(dpy);
+
+
+#ifdef USE_XAW_LANG
+
+ /* Support locale processing */
+ XtSetLanguageProc(NULL, NULL, NULL);
+
+#endif /* USE_XAW_LANG */
+
+
+ /* Initialize the toolkit */
+ topLevel = XtAppInitialize(&appcon, "Angband", NULL, 0, &argc, argv,
+ fallback, NULL, 0);
+
+ XSetIOErrorHandler(x_io_error_handler);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term_data_init(td, topLevel, 1024, termNames[i],
+ (i == 0) ? specialArgs : defaultArgs,
+ TERM_FALLBACKS, i);
+
+ angband_term[i] = Term;
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Raise the "Angband" window */
+ term_raise(&data[0]);
+
+
+#ifdef USE_GRAPHICS
+
+ /* Try graphics */
+ if (arg_graphics)
+ {
+ /* Try the "16x16.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp");
+
+ /* Use the "16x16.bmp" file if it exists */
+ if (!force_old_graphics &&
+ (0 == fd_close(fd_open(filename, O_RDONLY))))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 16;
+
+ ANGBAND_GRAF = "new";
+ }
+ else
+ {
+ /* Try the "8x8.bmp" file */
+ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp");
+
+ /* Use the "8x8.bmp" file if it exists */
+ if (0 == fd_close(fd_open(filename, O_RDONLY)))
+ {
+ /* Use graphics */
+ use_graphics = TRUE;
+
+ pict_wid = pict_hgt = 8;
+
+ ANGBAND_GRAF = "old";
+ }
+ }
+ }
+
+ /* Load graphics */
+ if (use_graphics)
+ {
+ /* Hack -- Get the Display */
+ term_data *td = &data[0];
+ Widget widget = (Widget)(td->widget);
+ Display *dpy = XtDisplay(widget);
+
+ XImage *tiles_raw;
+
+ /* Load the graphical tiles */
+ tiles_raw = ReadBMP(dpy, filename);
+
+ /* Initialize the windows */
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+
+ term *t = &td->t;
+
+ t->pict_hook = Term_pict_xaw;
+
+ t->higher_pict = TRUE;
+
+ /* Resize tiles */
+ td->widget->angband.tiles =
+ ResizeImage(dpy, tiles_raw,
+ pict_wid, pict_hgt,
+ td->widget->angband.fontwidth,
+ td->widget->angband.fontheight);
+ }
+
+#ifdef USE_TRANSPARENCY
+
+ /* Initialize the transparency temp storage*/
+ for (i = 0; i < num_term; i++)
+ {
+ term_data *td = &data[i];
+ int ii, jj;
+ int depth = DefaultDepth(dpy, DefaultScreen(dpy));
+ Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
+ int total;
+
+
+ /* Determine total bytes needed for image */
+ ii = 1;
+ jj = (depth - 1) >> 2;
+ while (jj >>= 1) ii <<= 1;
+ total = td->widget->angband.fontwidth *
+ td->widget->angband.fontheight * ii;
+
+
+ TmpData = (char *)malloc(total);
+
+ td->widget->angband.TmpImage = XCreateImage(dpy,
+ visual, depth,
+ ZPixmap, 0, TmpData,
+ td->widget->angband.fontwidth,
+ td->widget->angband.fontheight, 8, 0);
+
+ }
+
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Free tiles_raw? XXX XXX */
+ }
+
+#endif /* USE_GRAPHICS */
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_XAW */
+
diff --git a/src/main-xxx.c b/src/main-xxx.c
new file mode 100644
index 00000000..43e88e95
--- /dev/null
+++ b/src/main-xxx.c
@@ -0,0 +1,785 @@
+/* File: main-xxx.c */
+
+/* Purpose: Sample visual module for Angband 2.8.1 */
+
+/*
+ * This file written by "Ben Harrison (benh@phial.com)".
+ *
+ * This file is intended to show one way to build a "visual module"
+ * for Angband to allow it to work with a new system. It does not
+ * actually work, but if the code near "XXX XXX XXX" comments were
+ * replaced with functional code, then it probably would.
+ *
+ * See "z-term.c" for info on the concept of the "generic terminal",
+ * and for more comments about what this file must supply.
+ *
+ * There are two basic ways to port Angband to a new system. The
+ * first involves modifying the "main-gcu.c" and/or "main-x11.c"
+ * files to support some version of "curses" and/or "X11" on your
+ * machine, and to compile with the "USE_GCU" and/or "USE_X11"
+ * compilation flags defined. The second involves creating a
+ * new "main-xxx.c" file, based on this sample file (or on any
+ * existing "main-xxx.c" file), and comes in two flavors, based
+ * on whether it contains a "main()" function (as in "main-mac.c"
+ * and "main-win.c") or not (as in "main-gcu.c" or "main-x11.c").
+ *
+ * If the "main-xxx.c" file includes its own "main()" function,
+ * then you should NOT link in the "main.c" file, and your "main()"
+ * function must process any command line arguments, initialize the
+ * "visual system", and call "play_game()" with appropriate arguments.
+ *
+ * If the "main-xxx.c" file does not include its own "main()"
+ * function, then you must add some code to "main.c" which, if
+ * the appropriate "USE_XXX" compilation flag is defined, will
+ * attempt to call the "init_xxx()" function in the "main-xxx.c"
+ * file, which should initialize the "visual system" and return
+ * zero if it was successful. The "main()" function in "main.c"
+ * will take care of processing command line arguments and then
+ * calling "play_game()" with appropriate arguments.
+ *
+ * Note that the "util.c" file often contains functions which must
+ * be modified in small ways for various platforms, even if you are
+ * able to use the existing "main-gcu.c" and/or "main-x11.c" files,
+ * in particular, the "file handling" functions may not work on all
+ * systems.
+ *
+ * When you complete a port to a new system, you should email any
+ * newly created files, and any changes made to existing files,
+ * including "h-config.h", "config.h", and any of the "Makefile"
+ * files, to "benh@phial.com" for inclusion in the next version.
+ *
+ * Try to stick to a "three letter" naming scheme for "main-xxx.c"
+ * and "Makefile.xxx" and such for consistency and simplicity.
+ */
+
+
+#include "angband.h"
+
+
+#ifdef USE_XXX
+
+
+/*
+ * Extra data to associate with each "window"
+ *
+ * Each "window" is represented by a "term_data" structure, which
+ * contains a "term" structure, which contains a pointer (t->data)
+ * back to the term_data structure.
+ */
+
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t;
+
+ /* Other fields if needed XXX XXX XXX */
+};
+
+
+
+/*
+ * Number of "term_data" structures to support XXX XXX XXX
+ *
+ * You MUST support at least one "term_data" structure, and the
+ * game will currently use up to eight "term_data" structures if
+ * they are available.
+ *
+ * If only one "term_data" structure is supported, then a lot of
+ * the things that would normally go into a "term_data" structure
+ * could be made into global variables instead.
+ */
+#define MAX_TERM_DATA 1
+
+
+/*
+ * An array of "term_data" structures, one for each "sub-window"
+ */
+static term_data data[MAX_TERM_DATA];
+
+
+#if 0 /* Fix the syntax below XXX XXX XXX */
+
+/*
+ * The "color" array for the visual module XXX XXX XXX
+ *
+ * This table should be used in whetever way is necessary to
+ * convert the Angband Color Indexes into the proper "color data"
+ * for the visual system. On the Macintosh, these are arrays of
+ * three shorts, on the IBM, these are combinations of the eight
+ * basic color codes with optional "bright" bits, on X11, these
+ * are actual "pixel" codes extracted from another table which
+ * contains textual color names.
+ *
+ * The Angband Color Set (0 to 15):
+ * Black, White, Slate, Orange, Red, Blue, Green, Umber
+ * D-Gray, L-Gray, Violet, Yellow, L-Red, L-Blue, L-Green, L-Umber
+ *
+ * Colors 8 to 15 are basically "enhanced" versions of Colors 0 to 7.
+ *
+ * As decribed in one of the header files, in a perfect world, the
+ * colors below should fit a nice clean "quartered" specification
+ * in RGB codes, but this must often be Gamma Corrected. The 1/4
+ * parts of each Red,Green,Blue are shown in the comments below,
+ * again, these values are *before* gamma correction.
+ */
+static local_color_data_type color_data[16] =
+{
+ /* XXX XXX XXX 0,0,0 */, /* TERM_DARK */
+ /* XXX XXX XXX 4,4,4 */, /* TERM_WHITE */
+ /* XXX XXX XXX 2,2,2 */, /* TERM_SLATE */
+ /* XXX XXX XXX 4,2,0 */, /* TERM_ORANGE */
+ /* XXX XXX XXX 3,0,0 */, /* TERM_RED */
+ /* XXX XXX XXX 0,2,1 */, /* TERM_GREEN */
+ /* XXX XXX XXX 0,0,4 */, /* TERM_BLUE */
+ /* XXX XXX XXX 2,1,0 */, /* TERM_UMBER */
+ /* XXX XXX XXX 1,1,1 */, /* TERM_L_DARK */
+ /* XXX XXX XXX 3,3,3 */, /* TERM_L_WHITE */
+ /* XXX XXX XXX 4,0,4 */, /* TERM_VIOLET */
+ /* XXX XXX XXX 4,4,0 */, /* TERM_YELLOW */
+ /* XXX XXX XXX 4,0,0 */, /* TERM_L_RED */
+ /* XXX XXX XXX 0,4,0 */, /* TERM_L_GREEN */
+ /* XXX XXX XXX 0,4,4 */, /* TERM_L_BLUE */
+ /* XXX XXX XXX 3,2,1 */ /* TERM_L_UMBER */
+};
+
+#endif
+
+
+
+/*** Function hooks needed by "Term" ***/
+
+
+/*
+ * Init a new "term"
+ *
+ * This function should do whatever is necessary to prepare a new "term"
+ * for use by the "term.c" package. This may include clearing the window,
+ * preparing the cursor, setting the font/colors, etc. Usually, this
+ * function does nothing, and the "init_xxx()" function does it all.
+ */
+static void Term_init_xxx(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+
+ /* XXX XXX XXX */
+}
+
+
+
+/*
+ * Nuke an old "term"
+ *
+ * This function is called when an old "term" is no longer needed. It should
+ * do whatever is needed to clean up before the program exits, such as wiping
+ * the screen, restoring the cursor, fixing the font, etc. Often this function
+ * does nothing and lets the operating system clean up when the program quits.
+ */
+static void Term_nuke_xxx(term *t)
+{
+ term_data *td = (term_data*)(t->data);
+
+ /* XXX XXX XXX */
+}
+
+
+
+/*
+ * Do a "user action" on the current "term"
+ *
+ * This function allows the visual module to do implementation defined
+ * things when the user activates the "system defined command" command.
+ *
+ * This function is normally not used.
+ *
+ * In general, this function should return zero if the action is successfully
+ * handled, and non-zero if the action is unknown or incorrectly handled.
+ */
+static errr Term_user_xxx(int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Unknown */
+ return (1);
+}
+
+
+/*
+ * Do a "special thing" to the current "term"
+ *
+ * This function must react to a large number of possible arguments, each
+ * corresponding to a different "action request" by the "z-term.c" package,
+ * or by the application itself.
+ *
+ * The "action type" is specified by the first argument, which must be a
+ * constant of the form "TERM_XTRA_*" as given in "term.h", and the second
+ * argument specifies the "information" for that argument, if any, and will
+ * vary according to the first argument.
+ *
+ * In general, this function should return zero if the action is successfully
+ * handled, and non-zero if the action is unknown or incorrectly handled.
+ */
+static errr Term_xtra_xxx(int n, int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Analyze */
+ switch (n)
+ {
+ case TERM_XTRA_EVENT:
+ {
+ /*
+ * Process some pending events XXX XXX XXX
+ *
+ * Wait for at least one event if "v" is non-zero
+ * otherwise, if no events are ready, return at once.
+ * When "keypress" events are encountered, the "ascii"
+ * value corresponding to the key should be sent to the
+ * "Term_keypress()" function. Certain "bizarre" keys,
+ * such as function keys or arrow keys, may send special
+ * sequences of characters, such as control-underscore,
+ * plus letters corresponding to modifier keys, plus an
+ * underscore, plus carriage return, which can be used by
+ * the main program for "macro" triggers. This action
+ * should handle as many events as is efficiently possible
+ * but is only required to handle a single event, and then
+ * only if one is ready or "v" is true.
+ *
+ * This action is required.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FLUSH:
+ {
+ /*
+ * Flush all pending events XXX XXX XXX
+ *
+ * This action should handle all events waiting on the
+ * queue, optionally discarding all "keypress" events,
+ * since they will be discarded anyway in "z-term.c".
+ *
+ * This action is required, but may not be "essential".
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_CLEAR:
+ {
+ /*
+ * Clear the entire window XXX XXX XXX
+ *
+ * This action should clear the entire window, and redraw
+ * any "borders" or other "graphic" aspects of the window.
+ *
+ * This action is required.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_SHAPE:
+ {
+ /*
+ * Set the cursor visibility XXX XXX XXX
+ *
+ * This action should change the visibility of the cursor,
+ * if possible, to the requested value (0=off, 1=on)
+ *
+ * This action is optional, but can improve both the
+ * efficiency (and attractiveness) of the program.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FROSH:
+ {
+ /*
+ * Flush a row of output XXX XXX XXX
+ *
+ * This action should make sure that row "v" of the "output"
+ * to the window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FRESH" entry below takes care of any
+ * necessary flushing issues.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_FRESH:
+ {
+ /*
+ * Flush output XXX XXX XXX
+ *
+ * This action should make sure that all "output" to the
+ * window will actually appear on the window.
+ *
+ * This action is optional, assuming that "Term_text_xxx()"
+ * (and similar functions) draw directly to the screen, or
+ * that the "TERM_XTRA_FROSH" entry above takes care of any
+ * necessary flushing issues.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_NOISE:
+ {
+ /*
+ * Make a noise XXX XXX XXX
+ *
+ * This action should produce a "beep" noise.
+ *
+ * This action is optional, but convenient.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_SOUND:
+ {
+ /*
+ * Make a sound XXX XXX XXX
+ *
+ * This action should produce sound number "v", where the
+ * "name" of that sound is "sound_names[v]". This method
+ * is still under construction.
+ *
+ * This action is optional, and not very important.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_BORED:
+ {
+ /*
+ * Handle random events when bored XXX XXX XXX
+ *
+ * This action is optional, and normally not important
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_REACT:
+ {
+ /*
+ * React to global changes XXX XXX XXX
+ *
+ * For example, this action can be used to react to
+ * changes in the global "color_table[256][4]" array.
+ *
+ * This action is optional, but can be very useful for
+ * handling "color changes" and the "arg_sound" and/or
+ * "arg_graphics" options.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_ALIVE:
+ {
+ /*
+ * Change the "hard" level XXX XXX XXX
+ *
+ * This action is used if the program changes "aliveness"
+ * by being either "suspended" (v=0) or "resumed" (v=1)
+ * This action is optional, unless the computer uses the
+ * same "physical screen" for multiple programs, in which
+ * case this action should clean up to let other programs
+ * use the screen, or resume from such a cleaned up state.
+ *
+ * This action is currently only used by "main-gcu.c",
+ * on UNIX machines, to allow proper "suspending".
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_LEVEL:
+ {
+ /*
+ * Change the "soft" level XXX XXX XXX
+ *
+ * This action is used when the term window changes "activation"
+ * either by becoming "inactive" (v=0) or "active" (v=1)
+ *
+ * This action can be used to do things like activate the proper
+ * font / drawing mode for the newly active term window. This
+ * action should NOT change which window has the "focus", which
+ * window is "raised", or anything like that.
+ *
+ * This action is optional if all the other things which depend
+ * on what term is active handle activation themself, or if only
+ * one "term_data" structure is supported by this file.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_DELAY:
+ {
+ /*
+ * Delay for some milliseconds XXX XXX XXX
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as breath attacks.
+ *
+ * This action is optional, but may be required by this file,
+ * especially if special "macro sequences" must be supported.
+ */
+
+ return (0);
+ }
+
+ case TERM_XTRA_GET_DELAY:
+ {
+ /*
+ * Get Delay of some milliseconds XXX XXX XXX
+ * place the result in Term_xtra_long
+ *
+ * This action is useful for proper "timing" of certain
+ * visual effects, such as recording cmovies.
+ *
+ * This action is optional, but cmovies wont perform
+ * good without it
+ */
+
+ return (0);
+ }
+ }
+
+ /* Unknown or Unhandled action */
+ return (1);
+}
+
+
+/*
+ * Display the cursor
+ *
+ * This routine should display the cursor at the given location
+ * (x,y) in some manner. On some machines this involves actually
+ * moving the physical cursor, on others it involves drawing a fake
+ * cursor in some form of graphics mode. Note the "soft_cursor"
+ * flag which tells "z-term.c" to treat the "cursor" as a "visual"
+ * thing and not as a "hardware" cursor.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You may use the "Term_grab(x, y, &a, &c)" function, if needed,
+ * to determine what attr/char should be "under" the new cursor,
+ * for "inverting" purposes or whatever.
+ */
+static errr Term_curs_xxx(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Erase some characters
+ *
+ * This function should erase "n" characters starting at (x,y).
+ *
+ * You may assume "valid" input if the window is properly sized.
+ */
+static errr Term_wipe_xxx(int x, int y, int n)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some text on the screen
+ *
+ * This function should actually display an array of characters
+ * starting at the given location, using the given "attribute",
+ * and using the given string of characters, which contains
+ * exactly "n" characters and which is NOT null-terminated.
+ *
+ * You may assume "valid" input if the window is properly sized.
+ *
+ * You must be sure that the string, when written, erases anything
+ * (including any visual cursor) that used to be where the text is
+ * drawn. On many machines this happens automatically, on others,
+ * you must first call "Term_wipe_xxx()" to clear the area.
+ *
+ * In color environments, you should activate the color contained
+ * in "color_data[a & 0x0F]", if needed, before drawing anything.
+ *
+ * You may ignore the "attribute" if you are only supporting a
+ * monochrome environment, since this routine is normally never
+ * called to display "black" (invisible) text, including the
+ * default "spaces", and all other colors should be drawn in
+ * the "normal" color in a monochrome environment.
+ *
+ * Note that if you have changed the "attr_blank" to something
+ * which is not black, then this function must be able to draw
+ * the resulting "blank" correctly.
+ *
+ * Note that this function must correctly handle "black" text if
+ * the "always_text" flag is set, if this flag is not set, all the
+ * "black" text will be handled by the "Term_wipe_xxx()" hook.
+ */
+static errr Term_text_xxx(int x, int y, int n, byte a, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Draw some attr/char pairs on the screen
+ *
+ * This routine should display the given "n" attr/char pairs at
+ * the given location (x,y). This function is only used if one
+ * of the flags "always_pict" or "higher_pict" is defined.
+ *
+ * You must be sure that the attr/char pairs, when displayed, will
+ * erase anything (including any visual cursor) that used to be at
+ * the given location. On many machines this is automatic, but on
+ * others, you must first call "Term_wipe_xxx(x, y, 1)".
+ *
+ * With the "higher_pict" flag, this function can be used to allow
+ * the display of "pseudo-graphic" pictures, for example, by using
+ * the attr/char pair as an encoded index into a pixmap of special
+ * "pictures".
+ *
+ * With the "always_pict" flag, this function can be used to force
+ * every attr/char pair to be drawn by this function, which can be
+ * very useful if this file can optimize its own display calls.
+ *
+ * This function is often associated with the "arg_graphics" flag.
+ *
+ * This function is only used if one of the "higher_pict" and/or
+ * "always_pict" flags are set.
+ */
+static errr Term_pict_xxx(int x, int y, int n, const byte *ap, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* XXX XXX XXX */
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Internal Functions ***/
+
+
+/*
+ * Instantiate a "term_data" structure
+ *
+ * This is one way to prepare the "term_data" structures and to
+ * "link" the various informational pieces together.
+ *
+ * This function assumes that every window should be 80x24 in size
+ * (the standard size) and should be able to queue 256 characters.
+ * Technically, only the "main screen window" needs to queue any
+ * characters, but this method is simple. One way to allow some
+ * variation is to add fields to the "term_data" structure listing
+ * parameters for that window, initialize them in the "init_xxx()"
+ * function, and then use them in the code below.
+ *
+ * Note that "activation" calls the "Term_init_xxx()" hook for
+ * the "term" structure, if needed.
+ */
+static void term_data_link(int i)
+{
+ term_data *td = &data[i];
+
+ /* Initialize the term */
+ term_init(td->t, 80, 24, 256);
+
+ /* Choose "soft" or "hard" cursor XXX XXX XXX */
+ /* A "soft" cursor must be explicitly "drawn" by the program */
+ /* while a "hard" cursor has some "physical" existance and is */
+ /* moved whenever text is drawn on the screen. See "term.c". */
+ /* td->t->soft_cursor = TRUE; */
+
+ /* Avoid the "corner" of the window XXX XXX XXX */
+ /* td->t->icky_corner = TRUE; */
+
+ /* Use "Term_pict()" for all attr/char pairs XXX XXX XXX */
+ /* See the "Term_pict_xxx()" function above. */
+ /* td->t->always_pict = TRUE; */
+
+ /* Use "Term_pict()" for some attr/char pairs XXX XXX XXX */
+ /* See the "Term_pict_xxx()" function above. */
+ /* td->t->higher_pict = TRUE; */
+
+ /* Use "Term_text()" even for "black" text XXX XXX XXX */
+ /* See the "Term_text_xxx()" function above. */
+ /* td->t->always_text = TRUE; */
+
+ /* Ignore the "TERM_XTRA_BORED" action XXX XXX XXX */
+ /* This may make things slightly more efficient. */
+ /* td->t->never_bored = TRUE; */
+
+ /* Ignore the "TERM_XTRA_FROSH" action XXX XXX XXX */
+ /* This may make things slightly more efficient. */
+ /* td->t->never_frosh = TRUE; */
+
+ /* Erase with "white space" XXX XXX XXX */
+ /* td->t->attr_blank = TERM_WHITE; */
+ /* td->t->char_blank = ' '; */
+
+ /* Prepare the init/nuke hooks */
+ td->t->init_hook = Term_init_xxx;
+ td->t->nuke_hook = Term_nuke_xxx;
+
+ /* Prepare the template hooks */
+ td->t->user_hook = Term_user_xxx;
+ td->t->xtra_hook = Term_xtra_xxx;
+ td->t->curs_hook = Term_curs_xxx;
+ td->t->wipe_hook = Term_wipe_xxx;
+ td->t->text_hook = Term_text_xxx;
+ td->t->pict_hook = Term_pict_xxx;
+
+ /* Remember where we came from */
+ td->t->data = (vptr)(td);
+
+ /* Activate it */
+ Term_activate(td->t);
+
+ /* Global pointer */
+ ang_term[i] = td->t;
+}
+
+
+
+/*
+ * Initialization function
+ */
+errr init_xxx(void)
+{
+ /* Initialize globals XXX XXX XXX */
+
+ /* Initialize "term_data" structures XXX XXX XXX */
+
+ /* Create windows (backwards!) */
+ for (i = TERM_DATA_MAX - 1; i >= 0; i--)
+ {
+ /* Link */
+ term_data_link(i);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef INTERNAL_MAIN
+
+
+/*
+ * Some special machines need their own "main()" function, which they
+ * can provide here, making sure NOT to compile the "main.c" file.
+ *
+ * These systems usually have some form of "event loop", run forever
+ * as the last step of "main()", which handles things like menus and
+ * window movement, and calls "play_game(FALSE)" to load a game after
+ * initializing "savefile" to a filename, or "play_game(TRUE)" to make
+ * a new game. The event loop would also be triggered by "Term_xtra()"
+ * (the TERM_XTRA_EVENT action), in which case the event loop would not
+ * actually "loop", but would run once and return.
+ */
+
+
+/*
+ * An event handler XXX XXX XXX
+ *
+ * You may need an event handler, which can be used by both
+ * by the "TERM_XTRA_BORED" and "TERM_XTRA_EVENT" entries in
+ * the "Term_xtra_xxx()" function, and also to wait for the
+ * user to perform whatever user-interface operation is needed
+ * to request the start of a new game or the loading of an old
+ * game, both of which should launch the "play_game()" function.
+ */
+static bool CheckEvents(bool wait)
+{
+ /* XXX XXX XXX */
+
+ return (0);
+}
+
+
+/*
+ * Init some stuff
+ *
+ * This function is used to keep the "path" variable off the stack.
+ */
+static void init_stuff(void)
+{
+ char path[1024];
+
+ /* Prepare the path XXX XXX XXX */
+ /* This must in some way prepare the "path" variable */
+ /* so that it points at the "lib" directory. Every */
+ /* machine handles this in a different way... */
+ strcpy(path, "XXX XXX XXX");
+
+ /* Prepare the filepaths */
+ init_file_paths(path);
+}
+
+
+/*
+ * Main function
+ *
+ * This function must do a lot of stuff.
+ */
+int main(int argc, char *argv[])
+{
+ /* Initialize the machine itself XXX XXX XXX */
+
+ /* Process command line arguments XXX XXX XXX */
+
+ /* Initialize the windows */
+ if (init_xxx()) quit("Oops!");
+
+ /* XXX XXX XXX */
+ ANGBAND_SYS = "xxx";
+
+ /* Initialize some stuff */
+ init_stuff();
+
+ /* Initialize */
+ init_angband * /
+
+ /* Allow auto-startup XXX XXX XXX */
+
+ /* Event loop forever XXX XXX XXX */
+ while (TRUE) CheckEvents(TRUE);
+}
+
+
+#endif /* INTERNAL_MAIN */
+
+
+#endif /* USE_XXX */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..8c6f673c
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,1076 @@
+/* File: main.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#include "angband.h"
+
+/* Use runtime location of lib directory */
+#ifdef ENABLE_BINRELOC
+#include "prefix.h"
+#endif
+
+
+/*
+ * Some machines have a "main()" function in their "main-xxx.c" file,
+ * all the others use this file for their "main()" function.
+ */
+
+
+#if !defined(MACINTOSH) && !defined(WINDOWS) && !defined(ACORN)
+
+
+/*
+ * A hook for "quit()".
+ *
+ * Close down, then fall back into "quit()".
+ */
+static void quit_hook(cptr s)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 8 - 1; j >= 0; j--)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Nuke it */
+ term_nuke(angband_term[j]);
+ }
+}
+
+
+
+/*
+ * Set the stack size (for the Amiga)
+ */
+#ifdef AMIGA
+# include <dos.h>
+__near long __stack = 32768L;
+#endif
+
+
+/*
+ * Set the stack size and overlay buffer (see main-286.c")
+ */
+#ifdef USE_286
+# include <dos.h>
+extern unsigned _stklen = 32768U;
+extern unsigned _ovrbuffer = 0x1500;
+#endif
+
+
+#ifdef PRIVATE_USER_PATH
+
+/*
+ * Check and create if needed the directory dirpath
+ */
+bool private_check_user_directory(cptr dirpath)
+{
+ /* Is this used anywhere else in *bands? */
+ struct stat stat_buf;
+
+ int ret;
+
+ /* See if it already exists */
+ ret = stat(dirpath, &stat_buf);
+
+ /* It does */
+ if (ret == 0)
+ {
+ /* Now we see if it's a directory */
+ if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) return (TRUE);
+
+ /*
+ * Something prevents us from create a directory with
+ * the same pathname
+ */
+ return (FALSE);
+ }
+
+ /* No - this maybe the first time. Try to create a directory */
+ else
+ {
+ /* Create the ~/.ToME directory */
+ ret = mkdir(dirpath, 0700);
+
+ /* An error occured */
+ if (ret == -1) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+ }
+}
+
+/*
+ * Check existence of ".ToME/" directory in the user's
+ * home directory or try to create it if it doesn't exist.
+ * Returns FALSE if all the attempts fail.
+ */
+static bool check_create_user_dir(void)
+{
+ char dirpath[1024];
+ char versionpath[1024];
+ char savepath[1024];
+
+ /* Get an absolute path from the filename */
+ path_parse(dirpath, 1024, PRIVATE_USER_PATH);
+ strcpy(versionpath, dirpath);
+ strcat(versionpath, USER_PATH_VERSION);
+ strcpy(savepath, versionpath);
+ strcat(savepath, "/save");
+
+ return private_check_user_directory(dirpath) && private_check_user_directory(versionpath) && private_check_user_directory(savepath);
+}
+
+#endif /* PRIVATE_USER_PATH */
+
+
+/*
+ * Initialize and verify the file paths, and the score file.
+ *
+ * Use the ANGBAND_PATH environment var if possible, else use
+ * DEFAULT_PATH, and in either case, branch off appropriately.
+ *
+ * First, we'll look for the ANGBAND_PATH environment variable,
+ * and then look for the files in there. If that doesn't work,
+ * we'll try the DEFAULT_PATH constant. So be sure that one of
+ * these two things works...
+ *
+ * We must ensure that the path ends with "PATH_SEP" if needed,
+ * since the "init_file_paths()" function will simply append the
+ * relevant "sub-directory names" to the given path.
+ *
+ * Note that the "path" must be "Angband:" for the Amiga, and it
+ * is ignored for "VM/ESA", so I just combined the two.
+ */
+static void init_stuff(void)
+{
+ char path[1024];
+
+#if defined(AMIGA) || defined(VM)
+
+ /* Hack -- prepare "path" */
+ strcpy(path, "Angband:");
+
+#else /* AMIGA / VM */
+
+ cptr tail;
+
+ /* Get the environment variable */
+ tail = getenv("TOME_PATH");
+
+ /* Use the angband_path, or a default */
+#ifndef ENABLE_BINRELOC
+ strcpy(path, tail ? tail : DEFAULT_PATH);
+#else /* Runtime lookup of location */
+ strcpy(path, br_strcat(DATADIR, "/tome/lib"));
+#endif
+
+ /* Hack -- Add a path separator (only if needed) */
+ if (!suffix(path, PATH_SEP)) strcat(path, PATH_SEP);
+
+#endif /* AMIGA / VM */
+
+ /* Initialize */
+ init_file_paths(path);
+}
+
+
+
+/*
+ * Handle a "-d<what>=<path>" option
+ *
+ * The "<what>" can be any string starting with the same letter as the
+ * name of a subdirectory of the "lib" folder (i.e. "i" or "info").
+ *
+ * The "<path>" can be any legal path for the given system, and should
+ * not end in any special path separator (i.e. "/tmp" or "~/.ang-info").
+ */
+static void change_path(cptr info)
+{
+ cptr s;
+
+ /* Find equal sign */
+ s = strchr(info, '=');
+
+ /* Verify equal sign */
+ if (!s) quit_fmt("Try '-d<what>=<path>' not '-d%s'", info);
+
+ /* Analyze */
+ switch (tolower(info[0]))
+ {
+ case 'a':
+ {
+ string_free(ANGBAND_DIR_APEX);
+ ANGBAND_DIR_APEX = string_make(s + 1);
+ break;
+ }
+
+ case 'f':
+ {
+ string_free(ANGBAND_DIR_FILE);
+ ANGBAND_DIR_FILE = string_make(s + 1);
+ break;
+ }
+
+ case 'h':
+ {
+ string_free(ANGBAND_DIR_HELP);
+ ANGBAND_DIR_HELP = string_make(s + 1);
+ break;
+ }
+
+ case 'i':
+ {
+ string_free(ANGBAND_DIR_INFO);
+ ANGBAND_DIR_INFO = string_make(s + 1);
+ break;
+ }
+
+ case 'u':
+ {
+ string_free(ANGBAND_DIR_USER);
+ ANGBAND_DIR_USER = string_make(s + 1);
+ break;
+ }
+
+ case 'x':
+ {
+ string_free(ANGBAND_DIR_XTRA);
+ ANGBAND_DIR_XTRA = string_make(s + 1);
+ break;
+ }
+
+#ifdef VERIFY_SAVEFILE
+
+ case 'b':
+ case 'd':
+ case 'e':
+ case 's':
+ {
+ quit_fmt("Restricted option '-d%s'", info);
+ }
+
+#else /* VERIFY_SAVEFILE */
+
+ case 'b':
+ {
+ string_free(ANGBAND_DIR_BONE);
+ ANGBAND_DIR_BONE = string_make(s + 1);
+ break;
+ }
+
+ case 'd':
+ {
+ string_free(ANGBAND_DIR_DATA);
+ ANGBAND_DIR_DATA = string_make(s + 1);
+ break;
+ }
+
+ case 'e':
+ {
+ string_free(ANGBAND_DIR_EDIT);
+ ANGBAND_DIR_EDIT = string_make(s + 1);
+ break;
+ }
+
+ case 's':
+ {
+ string_free(ANGBAND_DIR_SAVE);
+ ANGBAND_DIR_SAVE = string_make(s + 1);
+ break;
+ }
+
+#endif /* VERIFY_SAVEFILE */
+
+ default:
+ {
+ quit_fmt("Bad semantics in '-d%s'", info);
+ }
+ }
+}
+
+
+/*
+ * Simple "main" function for multiple platforms.
+ *
+ * Note the special "--" option which terminates the processing of
+ * standard options. All non-standard options (if any) are passed
+ * directly to the "init_xxx()" function.
+ */
+int main(int argc, char *argv[])
+{
+ int i;
+
+ bool done = FALSE;
+
+ bool new_game = FALSE;
+
+ int show_score = 0;
+
+ cptr mstr = NULL;
+
+ bool args = TRUE;
+
+#ifdef CHECK_MEMORY_LEAKS
+ GC_find_leak = 1;
+#endif /* CHECK_MEMORY_LEAKS */
+
+
+ /* Save the "program name" XXX XXX XXX */
+ argv0 = argv[0];
+
+
+#ifdef USE_286
+ /* Attempt to use XMS (or EMS) memory for swap space */
+ if (_OvrInitExt(0L, 0L))
+ {
+ _OvrInitEms(0, 0, 64);
+ }
+#endif
+
+
+#ifdef SET_UID
+
+ /* Default permissions on files */
+ (void)umask(022);
+
+#endif /* SET_UID */
+
+
+ /* Get the file paths */
+ init_stuff();
+
+
+#ifdef SET_UID
+
+ /* Get the user id (?) */
+ player_uid = getuid();
+
+#ifdef VMS
+ /* Mega-Hack -- Factor group id */
+ player_uid += (getgid() * 1000);
+#endif
+
+# ifdef SAFE_SETUID
+
+# ifdef _POSIX_SAVED_IDS
+
+ /* Save some info for later */
+ player_euid = geteuid();
+ player_egid = getegid();
+
+# endif
+
+# if 0 /* XXX XXX XXX */
+
+ /* Redundant setting necessary in case root is running the game */
+ /* If not root or game not setuid the following two calls do nothing */
+
+ if (setgid(getegid()) != 0)
+ {
+ quit("setgid(): cannot set permissions correctly!");
+ }
+
+ if (setuid(geteuid()) != 0)
+ {
+ quit("setuid(): cannot set permissions correctly!");
+ }
+
+# endif /* XXX XXX XXX */
+
+# endif /* SAFE_SETUID */
+
+#endif /* SET_UID */
+
+
+#ifdef SET_UID
+
+ /* Please note that the game is still running in the game's permission */
+
+ /* Initialize the "time" checker */
+ if (check_time_init() || check_time())
+ {
+ quit("The gates to Angband are closed (bad time).");
+ }
+
+ /* Initialize the "load" checker */
+ if (check_load_init() || check_load())
+ {
+ quit("The gates to Angband are closed (bad load).");
+ }
+
+
+ /*
+ * Become user -- This will be the normal state for the rest of the game.
+ *
+ * Put this here because it's totally irrelevant to single user operating
+ * systems, as witnessed by huge number of cases where these functions
+ * weren't used appropriately (at least in this variant).
+ *
+ * Whenever it is necessary to open/remove/move the files in the lib folder,
+ * this convention must be observed:
+ *
+ * safe_setuid_grab();
+ *
+ * fd_open/fd_make/fd_kill/fd_move which requires game's permission,
+ * i.e. manipulating files under the lib directory
+ *
+ * safe_setuid_drop();
+ *
+ * Please never ever make unmatched calls to these grab/drop functions.
+ *
+ * Please note that temporary files used by various information commands
+ * and ANGBAND_DIR_USER files shouldn't be manipulated this way, because
+ * they reside outside of the lib directory on multiuser installations.
+ * -- pelpel
+ */
+ safe_setuid_drop();
+
+
+ /* Acquire the "user name" as a default player name */
+ user_name(player_name, player_uid);
+
+
+#ifdef PRIVATE_USER_PATH
+
+ /*
+ * On multiuser systems, users' private directories are
+ * used to store pref files, chardumps etc.
+ */
+ {
+ bool ret;
+
+ /* Create a directory for the user's files */
+ ret = check_create_user_dir();
+
+ /* Oops */
+ if (ret == FALSE) quit("Cannot create directory " PRIVATE_USER_PATH);
+ }
+
+#endif /* PRIVATE_USER_PATH */
+
+#endif /* SET_UID */
+
+
+
+
+ /* Process the command line arguments */
+ for (i = 1; args && (i < argc); i++)
+ {
+ /* Require proper options */
+ if (argv[i][0] != '-') goto usage;
+
+ /* Analyze option */
+ switch (argv[i][1])
+ {
+ case 'N':
+ case 'n':
+ {
+ new_game = TRUE;
+ break;
+ }
+
+ case 'F':
+ case 'f':
+ {
+ arg_fiddle = TRUE;
+ break;
+ }
+
+ case 'W':
+ case 'w':
+ {
+ arg_wizard = TRUE;
+ break;
+ }
+
+ case 'V':
+ case 'v':
+ {
+ arg_sound = TRUE;
+ break;
+ }
+
+ case 'G':
+ case 'g':
+ {
+ arg_graphics = TRUE;
+ break;
+ }
+
+ case 'R':
+ case 'r':
+ {
+ arg_force_roguelike = TRUE;
+ break;
+ }
+
+ case 'O':
+ case 'o':
+ {
+ arg_force_original = TRUE;
+ break;
+ }
+
+ case 'S':
+ case 's':
+ {
+ show_score = atoi(&argv[i][2]);
+ if (show_score <= 0) show_score = 10;
+ break;
+ }
+
+ case 'u':
+ case 'U':
+ {
+ if (!argv[i][2]) goto usage;
+ strcpy(player_name, &argv[i][2]);
+ strcpy(player_base, &argv[i][2]);
+ no_begin_screen = TRUE;
+ break;
+ }
+
+ case 'm':
+ {
+ if (!argv[i][2]) goto usage;
+ mstr = &argv[i][2];
+ break;
+ }
+
+ case 'M':
+ {
+ if (!argv[i][2]) goto usage;
+ force_module = string_make(&argv[i][2]);
+ break;
+ }
+
+ case 'h':
+ {
+ goto usage;
+ break;
+ }
+
+ case 'H':
+ {
+ char *s;
+ int j;
+
+ init_lua();
+ for (j = i + 1; j < argc; j++)
+ {
+ s = argv[j];
+
+ while (*s != '.') s++;
+ *s = '\0';
+ s++;
+ txt_to_html("head.aux", "foot.aux", argv[j], s, FALSE, FALSE);
+ }
+
+ return 0;
+ }
+
+ case 'c':
+ case 'C':
+ {
+ chg_to_txt(argv[i + 1], argv[i + 2]);
+
+ return 0;
+ }
+
+ case 'd':
+ case 'D':
+ {
+ change_path(&argv[i][2]);
+ break;
+ }
+
+ case '-':
+ {
+ if (argv[i][2] == 'h' && !strcmp((argv[i] + 2), "help"))
+ goto usage;
+ else
+ {
+ argv[i] = argv[0];
+ argc = argc - i;
+ argv = argv + i;
+ args = FALSE;
+ break;
+ }
+ }
+
+ default:
+usage:
+ {
+ int j;
+
+ /* Dump usage information */
+ for (j = 0; j < argc; j++) printf("%s ", argv[j]);
+ printf("\n");
+ puts("Usage: tome [options] [-- subopts]");
+ puts(" -h This help");
+ puts(" -n Start a new character");
+ puts(" -f Request fiddle mode");
+ puts(" -w Request wizard mode");
+ puts(" -v Request sound mode");
+ puts(" -g Request graphics mode");
+ puts(" -o Request original keyset");
+ puts(" -r Request rogue-like keyset");
+ puts(" -H <list of files> Convert helpfile to html");
+ puts(" -c f1 f2 Convert changelog f1 to nice txt f2");
+ puts(" -s<num> Show <num> high scores");
+ puts(" -u<who> Use your <who> savefile");
+ puts(" -M<which> Use the <which> module");
+ puts(" -m<sys> Force 'main-<sys>.c' usage");
+ puts(" -d<def> Define a 'lib' dir sub-path");
+
+#ifdef USE_GTK
+ puts(" -mgtk To use GTK");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -b Turn off software backing store");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+ puts(" -- -g Requests \"new\" graphics");
+ puts(" -- -t Enable transparency effect");
+# endif /* USE_GRAPHICS */
+#endif /* USE_GTK */
+
+#ifdef USE_GTK2
+ puts(" -mgtk2 To use GTK2");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -b Turn off software backing store");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+ puts(" -- -g Requests \"new\" graphics");
+ puts(" -- -t Enable transparency effect");
+# endif /* USE_GRAPHICS */
+#endif /* USE_GTK2 */
+
+#ifdef USE_XAW
+ puts(" -mxaw To use XAW");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -d<name> Display to use");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+# endif /* USE_GRAPHICS */
+#endif /* USE_XAW */
+
+#ifdef USE_X11
+ puts(" -mx11 To use X11");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -d<name> Display to use");
+# ifdef USE_GRAPHICS
+ puts(" -- -s Turn off smoothscaling graphics");
+ puts(" -- -o Requests \"old\" graphics");
+ puts(" -- -b Requests double-width tiles");
+# endif /* USE_GRAPHICS */
+#endif /* USE_X11 */
+
+#ifdef USE_GCU
+ puts(" -mgcu To use curses");
+ puts(" -- Sub options");
+ puts(" -- -b Requests big screen");
+#endif /* USE_GCU */
+
+#ifdef USE_CAP
+ puts(" -mcap To use termcap");
+#endif /* USE_CAP */
+
+#ifdef USE_DOS
+ puts(" -mdos To use Allegro");
+#endif /* USE_DOS */
+
+#ifdef USE_IBM
+ puts(" -mibm To use IBM/PC console");
+#endif /* USE_IBM */
+
+#ifdef USE_EMX
+ puts(" -memx To use EMX");
+#endif /* USE_EMX */
+
+#ifdef USE_SLA
+ puts(" -msla To use SLang");
+#endif /* USE_SLA */
+
+#ifdef USE_LSL
+ puts(" -mlsl To use SVGALIB");
+#endif /* USE_LSL */
+
+#ifdef USE_AMI
+ puts(" -mami To use Amiga");
+#endif /* USE_AMI */
+
+#ifdef USE_VME
+ puts(" -mvme To use VM/ESA");
+#endif /* USE_VME */
+
+#ifdef USE_ISO
+ puts(" -miso To use ISO");
+#endif /* USE_ISO */
+
+#ifdef USE_SDL
+ puts(" -msdl To use SDL");
+ puts(" -- Sub options");
+ puts(" -- -n # Number of virtual consoles to use");
+ puts(" -- -g Request new graphics (16x16)");
+ puts(" -- -o Request old graphics (8x8)");
+ puts(" -- -b Requests double-width tiles");
+ puts(" -- -w # Request screen width in pixels");
+ puts(" -- -h # Request screen height in pixels");
+ puts(" -- -bpp # Request screen color depth in bits");
+ puts(" -- -fs Start with full-screen display");
+ puts(" -- -s # Request font size");
+ puts(" -- -f <font> Request true-type font by name");
+#endif /* USE_SDL */
+
+ /* Actually abort the process */
+ quit(NULL);
+ }
+ }
+ }
+
+ /* Hack -- Forget standard args */
+ if (args)
+ {
+ argc = 1;
+ argv[1] = NULL;
+ }
+
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+
+ /* Install "quit" hook */
+ quit_aux = quit_hook;
+
+
+ /* Install the zsock hooks we cannot do it later because main-net needs them */
+ zsock_init();
+
+
+#ifdef USE_GLU
+ /* Attempt to use the "main-glu.c" support */
+ if (!done && (!mstr || (streq(mstr, "glu"))))
+ {
+ extern errr init_glu(int, char**);
+ if (0 == init_glu(argc, argv))
+ {
+ ANGBAND_SYS = "glu";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GTK2
+ /* Attempt to use the "main-gtk2.c" support */
+ if (!done && (!mstr || (streq(mstr, "gtk2"))))
+ {
+ extern errr init_gtk2(int, char**);
+ if (0 == init_gtk2(argc, argv))
+ {
+ ANGBAND_SYS = "gtk2";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GTK
+ /* Attempt to use the "main-gtk.c" support */
+ if (!done && (!mstr || (streq(mstr, "gtk"))))
+ {
+ extern errr init_gtk(int, char**);
+ if (0 == init_gtk(argc, argv))
+ {
+ ANGBAND_SYS = "gtk";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_XAW
+ /* Attempt to use the "main-xaw.c" support */
+ if (!done && (!mstr || (streq(mstr, "xaw"))))
+ {
+ extern errr init_xaw(int, char**);
+ if (0 == init_xaw(argc, argv))
+ {
+ ANGBAND_SYS = "xaw";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_X11
+ /* Attempt to use the "main-x11.c" support */
+ if (!done && (!mstr || (streq(mstr, "x11"))))
+ {
+ extern errr init_x11(int, char**);
+ if (0 == init_x11(argc, argv))
+ {
+ ANGBAND_SYS = "x11";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GCU
+ /* Attempt to use the "main-gcu.c" support */
+ if (!done && (!mstr || (streq(mstr, "gcu"))))
+ {
+ extern errr init_gcu(int, char**);
+ if (0 == init_gcu(argc, argv))
+ {
+ ANGBAND_SYS = "gcu";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_GLU
+ /* Attempt to use the "main-glu.c" support */
+ if (!done && (!mstr || (streq(mstr, "glu"))))
+ {
+ extern errr init_glu(int, char**);
+ if (0 == init_glu(argc, argv))
+ {
+ ANGBAND_SYS = "glu";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_CAP
+ /* Attempt to use the "main-cap.c" support */
+ if (!done && (!mstr || (streq(mstr, "cap"))))
+ {
+ extern errr init_cap(int, char**);
+ if (0 == init_cap(argc, argv))
+ {
+ ANGBAND_SYS = "cap";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_DOS
+ /* Attempt to use the "main-dos.c" support */
+ if (!done && (!mstr || (streq(mstr, "dos"))))
+ {
+ extern errr init_dos(void);
+ if (0 == init_dos())
+ {
+ ANGBAND_SYS = "dos";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_IBM
+ /* Attempt to use the "main-ibm.c" support */
+ if (!done && (!mstr || (streq(mstr, "ibm"))))
+ {
+ extern errr init_ibm(void);
+ if (0 == init_ibm())
+ {
+ ANGBAND_SYS = "ibm";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_EMX
+ /* Attempt to use the "main-emx.c" support */
+ if (!done && (!mstr || (streq(mstr, "emx"))))
+ {
+ extern errr init_emx(void);
+ if (0 == init_emx())
+ {
+ ANGBAND_SYS = "emx";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_SLA
+ /* Attempt to use the "main-sla.c" support */
+ if (!done && (!mstr || (streq(mstr, "sla"))))
+ {
+ extern errr init_sla(void);
+ if (0 == init_sla())
+ {
+ ANGBAND_SYS = "sla";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_LSL
+ /* Attempt to use the "main-lsl.c" support */
+ if (!done && (!mstr || (streq(mstr, "lsl"))))
+ {
+ extern errr init_lsl(void);
+ if (0 == init_lsl())
+ {
+ ANGBAND_SYS = "lsl";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_AMI
+ /* Attempt to use the "main-ami.c" support */
+ if (!done && (!mstr || (streq(mstr, "ami"))))
+ {
+ extern errr init_ami(void);
+ if (0 == init_ami())
+ {
+ ANGBAND_SYS = "ami";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_VME
+ /* Attempt to use the "main-vme.c" support */
+ if (!done && (!mstr || (streq(mstr, "vme"))))
+ {
+ extern errr init_vme(void);
+ if (0 == init_vme())
+ {
+ ANGBAND_SYS = "vme";
+ done = TRUE;
+ }
+ }
+#endif
+
+
+#ifdef USE_PARAGUI
+ /* Attempt to use the "main-pgu.c" support */
+ if (!done && (!mstr || (streq(mstr, "pgu"))))
+ {
+ extern errr init_pgu(int, char**);
+ if (0 == init_pgu(argc, argv))
+ {
+ ANGBAND_SYS = "pgu";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_ISO
+ /* Attempt to use the "main-iso.c" support */
+ if (!done && (!mstr || (streq(mstr, "iso"))))
+ {
+ extern errr init_iso(int, char**);
+ if (0 == init_iso(argc, argv))
+ {
+ ANGBAND_SYS = "iso";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_LUA_GUI
+ /* Attempt to use the "main-lua.c" support */
+ if (!done && (!mstr || (streq(mstr, "lua"))))
+ {
+ extern errr init_lua_gui(int, char**);
+ if (0 == init_lua_gui(argc, argv))
+ {
+ ANGBAND_SYS = "lua";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_NET
+ /* Attempt to use the "main-net.c" support */
+ if (!done && (!mstr || (streq(mstr, "net"))))
+ {
+ extern errr init_net(int, char**);
+ if (0 == init_net(argc, argv))
+ {
+ ANGBAND_SYS = "net";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_SDL
+ /* Attempt to use the "main-sdl.c" support */
+ if (!done && (!mstr || (streq(mstr, "sdl"))))
+ {
+ extern errr init_sdl(int, char**);
+ if (0 == init_sdl(argc, argv))
+ {
+ ANGBAND_SYS = "sdl";
+ done = TRUE;
+ }
+ }
+#endif
+
+#ifdef USE_DMY
+ /* Attempt to use the "main-dmy.c" support */
+ if (!done && (!mstr || (streq(mstr, "dmy"))))
+ {
+ extern errr init_dummy(int, char**);
+ if (0 == init_dummy(argc, argv))
+ {
+ ANGBAND_SYS = "dmy";
+ done = TRUE;
+ }
+ }
+#endif
+
+ /* Make sure we have a display! */
+ if (!done) quit("Unable to prepare any 'display module'!");
+
+
+ /* Catch nasty signals */
+ signals_init();
+
+ /* Initialize */
+ init_angband();
+
+ /* Hack -- If requested, display scores and quit */
+ if (show_score > 0) display_scores(0, show_score);
+
+ /* Wait for response */
+ pause_line(23);
+
+ /* Play the game */
+ play_game(new_game);
+
+#ifdef CHECK_MEMORY_LEAKS
+ CHECK_LEAKS();
+#endif
+
+ /* Quit */
+ quit(NULL);
+
+ /* Exit */
+ return (0);
+}
+
+#endif
diff --git a/src/makefile.WHICH b/src/makefile.WHICH
new file mode 100644
index 00000000..eb5e0963
--- /dev/null
+++ b/src/makefile.WHICH
@@ -0,0 +1,64 @@
+This file documents which makefiles you should use for each system.
+
+ * makefile.ami:
+ Amiga makefile.
+
+ * makefile.bcc:
+ Borland C/C++ compiler version 5 makefile.
+
+ * makefile.cyg:
+ A makefile for people using Cygwin for windows.
+
+ * makefile.emx:
+ A makefile for OS/2. You need the EMX packages and ReXX installed for
+ this to work.
+
+ * makefile.gdb:
+ Another makefile for DOS, includes debugging symbols so if you're using
+ a DOS-based debugger, things will work. Ideally would be combined into
+ makefile.dos. You want DJGPP and related tools for this.
+
+ * makefile.ibm:
+ Another makefile for people using DOS and DJGPP. Ideally will be rolled
+ into makefile.dos eventually.
+
+ * makefile.lsl:
+ If you want to try to use SVGAlib, this might provide a start.
+
+ * makefile.std:
+ Most Unix users should use this. Currently the only makefile with a real
+ install rule. More instructions are found in the file itself.
+
+ * makefile.wat:
+ Watcom C/C++ for DOS makefile.
+
+ * makefile.win:
+ A makefile for people using Borland C 4.5x for windows.
+
+ * ToME.proj.xml:
+ This one isn't really a makefile, but serves the same purpose. It's a
+ project file for people using CodeWarrior 6 or 7 for the Macintosh.
+ Because the IDE doesn't make things easier, IMHO, here are instructions:
+ - Launch CodeWarrior and choose File-Import Project, then select
+ ToME.proj.xml in the src folder;
+ - Save your project as ToME.proj where you find readme files;
+ - (Optional) Convert graphics tiles to resource from PICT and put them
+ in your top folder, i.e. where you find readmes:
+ 8x8.rsrc - 8x8.bmp, PICT ID 1001
+ 16x16.rsrc - 16x16.bmp, PICT ID 1002
+ mask.rsrc - mask.bmp, PICT ID 1003
+ If you are using GraphicsConverter, make sure to scale the images to
+ 72 pixel per inch while keeping horizontal and vertical scale factors
+ to 100%, and *never* add previews;
+ - (Optional) Grab Sound resources from any Angband binaries and save it
+ as AngSound.rsrc, also in the top folder;
+ - If you don't want graphics and sound effects, remove 8x8.rsrc,
+ 16x16.rsrc, mask.rsrc and AngSound.rsrc from your project;
+ - Please take a look at the comment in A-mac-h.pch for a couple of
+ source code modifications that may be necessary;
+ - Click on the build button, or choose that from the project menu; and
+ - Hope it works.
+ Carbon compilation requires Carbon SDK 1.1(?) or greater, freely
+ downloadable from Apple. The project file assumes that "Carbon Support"
+ folder should be placed directly beneath your CodeWarrior compiler
+ folder, i.e. where "MacOS Support", "MSL" etc are.
diff --git a/src/makefile.ami b/src/makefile.ami
new file mode 100644
index 00000000..324667dd
--- /dev/null
+++ b/src/makefile.ami
@@ -0,0 +1,110 @@
+# Smakefile
+#
+# Amiga SAS/C 6.50
+
+all: angband
+
+# Object files
+OBJS = \
+ z-util.o z-virt.o z-form.o z-rand.o z-term.o z-sock.o \
+ variable.o tables.o util.o cave.o cmovie.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ xtra1.o xtra2.o spells1.o spells2.o help.o \
+ melee1.o melee2.o save.o files.o notes.o \
+ status.o randart.o gods.o modules.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ store.o birth.o loadsave.o ghost.o powers.o \
+ wizard1.o wizard2.o wild.o plots.o \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o \
+ bldg.o levels.o squeltch.o \
+ main-ami.o sound-ami.o main.o
+
+# Header files
+HDRS = \
+ h-basic.h \
+ h-define.h h-type.h h-system.h h-config.h
+
+# Include files
+INCS = \
+ angband.h \
+ config.h defines.h types.h externs.h \
+ z-term.h z-rand.h z-util.h z-virt.h z-form.h z-sock.h$(HDRS)
+
+# Linking
+angband: $(OBJS)
+ echo Linking
+ slink QUIET WITH angband.lnk
+
+# Compiling
+.c.o:
+ echo Compiling $*.c
+ sc DEFINE AMIGA DEFINE USE_AMI $*
+
+# Dependencies
+birth.o: birth.c $(INCS)
+borg-aux.o: borg-aux.c $(INCS) borg-aux.h borg-ext.h
+borg-aux.o: borg-obj.h borg-map.h borg.h
+borg-ben.o: borg-ben.c $(INCS) borg-ben.h borg-aux.h borg-ext.h
+borg-ben.o: borg-obj.h borg-map.h borg.h
+borg-ext.o: borg-ext.c $(INCS) borg-ext.h
+borg-ext.o: borg-obj.h borg-map.h borg.h
+borg-obj.o: borg-obj.c $(INCS) borg-obj.h borg.h
+borg-map.o: borg-map.c $(INCS) borg-map.h borg.h
+borg.o: borg.c $(INCS) borg.h
+cave.o: cave.c $(INCS)
+cmd1.o: cmd1.c $(INCS)
+cmd2.o: cmd2.c $(INCS)
+cmd3.o: cmd3.c $(INCS)
+cmd4.o: cmd4.c $(INCS)
+cmd5.o: cmd5.c $(INCS)
+cmd6.o: cmd6.c $(INCS)
+cmd7.o: cmd7.c $(INCS)
+cmovie.o: cmovie.c $(INCS)
+dungeon.o: dungeon.c $(INCS)
+files.o: files.c $(INCS)
+generate.o: generate.c $(INCS)
+gen_evol.o: gen_evol.c $(INCS)
+gen_maze.o: gen_maze.c $(INCS)
+init1.o: init1.c $(INCS)
+init2.o: init2.c $(INCS)
+loadsave.o: loadsave.c $(INCS)
+main-ami.o: main-ami.c $(INCS) sound-ami.h
+main.o: main.c $(INCS)
+melee1.o: melee1.c $(INCS)
+melee2.o: melee2.c $(INCS)
+monster1.o: monster1.c $(INCS)
+monster2.o: monster2.c $(INCS)
+monster3.o: monster3.c $(INCS)
+notes.o: notes.c $(INCS)
+object1.o: object1.c $(INCS)
+object2.o: object2.c $(INCS)
+powers.o: powers.c $(INCS)
+help.o: help.c $(INCS)
+randart.o: randart.c $(INCS)
+tarps.o: traps.c $(INCS)
+bldg.o: bldg.c $(INCS)
+plots.o: plots.c $(INCS)
+sound-ami.o: sound-ami.c sound-ami.h
+spells1.o: spells1.c $(INCS)
+spells2.o: spells2.c $(INCS)
+status.o: status.c $(INCS)
+store.o: store.c $(INCS)
+tables.o: tables.c $(INCS)
+util.o: util.c $(INCS)
+variable.o: variable.c $(INCS)
+wizard1.o: wizard1.c $(INCS)
+wizard2.o: wizard2.c $(INCS)
+xtra1.o: xtra1.c $(INCS)
+xtra2.o: xtra2.c $(INCS)
+gods.o: gods.c $(INCS)
+levels.o: levels.c $(INCS)
+ghost.o: ghost.c $(INCS)
+squeltch.o: squeltch.c $(INCS)
+wild.o: wild.c $(INCS)
+modules.o: modules.c $(INCS)
+z-form.o: z-form.c $(HDRS) z-form.h z-util.h z-virt.h
+z-rand.o: z-rand.c $(HDRS) z-rand.h
+z-term.o: z-term.c $(HDRS) z-term.h z-virt.h
+z-util.o: z-util.c $(HDRS) z-util.h
+z-virt.o: z-virt.c $(HDRS) z-virt.h z-util.h
+z-sock.o: z-sock.c $(HDRS) z-sock.h
diff --git a/src/makefile.bcc b/src/makefile.bcc
new file mode 100644
index 00000000..d8fc7e42
--- /dev/null
+++ b/src/makefile.bcc
@@ -0,0 +1,222 @@
+###################################################################
+#
+# makefile.bcc - Angband makefile for Borland C++ 5.5 (Win32)
+#
+###################################################################
+
+
+###################################################################
+#
+# Borland specific directives ---
+#
+.AUTODEPEND
+
+###################################################################
+#
+# Set tool and version names:
+
+CPP = bcc32
+LINKER = ilink32
+RC = brc32
+
+###################################################################
+#
+# Name of the *.exe-file
+
+EXE_FILE = tome.exe
+TOLUA = tolua.exe
+
+###################################################################
+#
+# Debug mode (un-comment for debugging)
+
+# DBGOPT= -v -N -x -xp
+
+
+###################################################################
+#
+# Set any compiler options
+
+CCOPTS = -jb -j1 -Hc -tW -lGn -w- \
+ -D_WIN32_WINNT=0x0400 -DWINVER=0x0400 \
+ -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA \
+ -I$(BCCDIR)\include -I. -Ilua -DUSE_LUA \
+ -DUSE_PRECISE_CMOVIE \
+ -DUSE_WINSOCK \
+ -DIRC_SERVER=\"irc.worldirc.org\" -DIRC_PORT=\"6667\" -DIRC_CHANNEL=\"\#tome\"
+
+# Compile flags:
+CPPFLAGS= $(CCOPTS) $(DBGOPT)
+
+######################## Targets ##################################
+
+OBJ = \
+ gods.obj \
+ skills.obj \
+ irc.obj \
+ birth.obj \
+ bldg.obj \
+ cave.obj \
+ cmd1.obj \
+ cmd2.obj \
+ cmd3.obj \
+ cmd4.obj \
+ cmd5.obj \
+ cmd6.obj \
+ cmd7.obj \
+ cmovie.obj \
+ dungeon.obj \
+ files.obj \
+ generate.obj \
+ gen_maze.obj \
+ gen_evol.obj \
+ ghost.obj \
+ help.obj \
+ init1.obj \
+ init2.obj \
+ levels.obj \
+ loadsave.obj \
+ lua_bind.obj \
+ main-win.obj \
+ main.obj \
+ melee1.obj \
+ melee2.obj \
+ modules.obj \
+ monster1.obj \
+ monster2.obj \
+ monster3.obj \
+ notes.obj \
+ object1.obj \
+ object2.obj \
+ plots.obj \
+ powers.obj \
+ randart.obj \
+ readdib.obj \
+ script.obj \
+ spells1.obj \
+ spells2.obj \
+ squeltch.obj \
+ status.obj \
+ store.obj \
+ tables.obj \
+ traps.obj \
+ util.obj \
+ variable.obj \
+ wild.obj \
+ wizard1.obj \
+ wizard2.obj \
+ xtra1.obj \
+ xtra2.obj \
+ z-form.obj \
+ z-rand.obj \
+ z-term.obj \
+ z-util.obj \
+ z-virt.obj \
+ z-sock.obj \
+ w_mnster.obj \
+ w_obj.obj \
+ w_play_c.obj \
+ w_player.obj \
+ w_quest.obj \
+ w_spells.obj \
+ w_util.obj \
+ w_z_pack.obj \
+ w_dun.obj \
+ $(TOLUAOBJS)
+
+TOLUAOBJS = \
+ lua\lapi.obj \
+ lua\lauxlib.obj \
+ lua\lbaselib.obj \
+ lua\lcode.obj \
+ lua\ldblib.obj \
+ lua\ldebug.obj \
+ lua\ldo.obj \
+ lua\lfunc.obj \
+ lua\lgc.obj \
+ lua\liolib.obj \
+ lua\llex.obj \
+ lua\lmem.obj \
+ lua\lobject.obj \
+ lua\lparser.obj \
+ lua\lstate.obj \
+ lua\lstring.obj \
+ lua\lstrlib.obj \
+ lua\ltable.obj \
+ lua\ltests.obj \
+ lua\ltm.obj \
+ lua\lundump.obj \
+ lua\lvm.obj \
+ lua\lzio.obj \
+ lua\tolua.obj \
+ lua\tolua_bd.obj \
+ lua\tolua_eh.obj \
+ lua\tolua_gp.obj \
+ lua\tolua_lb.obj \
+ lua\tolua_rg.obj \
+ lua\tolua_tm.obj \
+ lua\tolua_tt.obj \
+ lua\tolualua.obj \
+
+all : $(EXE_FILE)
+ copy $(EXE_FILE) ..
+
+clean:
+ -@if exist *.obj del *.obj >nul
+ -@if exist lua\*.obj del lua\*.obj >nul
+ -@if exist *.exe del *.exe >nul
+ -@if exist *.res del *.res >nul
+ -@if exist *.tds del *.tds >nul
+ -@if exist *.ilc del *.ilc >nul
+ -@if exist *.ild del *.ild >nul
+ -@if exist *.ilf del *.ilf >nul
+ -@if exist *.ils del *.ils >nul
+
+install: $(EXE_FILE)
+ copy $(EXE_FILE) ..
+
+
+########################### Explicit Rules ########################
+$(EXE_FILE): $(OBJ) angband.res
+ $(LINKER) -aa -L$(BCCDIR)\lib -x $(OBJ) c0w32.obj, $(EXE_FILE),, cw32.lib import32.lib,, angband.res
+
+$(TOLUA): $(TOLUAOBJS)
+ $(LINKER) -aa -L$(BCCDIR)\lib -x $(TOLUAOBJS) c0x32.obj, tolua.exe,, cw32.lib import32.lib
+
+angband.res: angband.rc
+ $(RC) -r angband.rc
+
+w_mnster.c: monster.pkg $(TOLUA)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUA)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUA)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUA)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUA)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUA)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUA)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUA)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+########################### Implicit Rules ########################
+.c.obj:
+ $(CPP) $(CPPFLAGS) -c -o$*.obj $*.c
+
+.obj.exe:
+ $(CPP) $(CPPFLAGS) $<
+
diff --git a/src/makefile.bsd b/src/makefile.bsd
new file mode 100644
index 00000000..5ac2deac
--- /dev/null
+++ b/src/makefile.bsd
@@ -0,0 +1,298 @@
+# This is based on Makefile.std but stripped and tuned for FreeBSD (and
+# presumably will cooperate with other BSDs)
+
+# Comment out this line if you don't want the IRC support
+IRC=TRUE
+
+## Installation locations and such
+##
+## LIBDIR, BINDIR and OWNER should be set appropriately for
+## multiuser installations.
+##
+## If you want to keep it private or don't have root privilege
+## required by "make install", set LIBDIR to ./lib/ or an absolute
+## path pointing to your lib directory, and don't run "make install".
+##
+## NOTE: If LIBDIR is set to ./lib/ , you have to cd to parent directory
+## of lib before you start the game.
+##
+
+PREFIX ?= /usr/local
+X11BASE ?= /usr/X11R6
+
+# Where lib/ files goes
+LIBDIR = $(PREFIX)/share/tome
+
+# Where ToME binary goes
+BINDIR = $(PREFIX)/bin
+
+# The game will run suid to this user
+OWNER = root
+GROUP = games
+FILE_MODE = 0664
+
+## Some "system" definitions
+##
+## No changes are needed to compile a version that will run on both
+## X11 and Curses, in debugging mode, with maximal warnings, on many
+## normal Unix machines of the Sun OS variety (non-solaris).
+##
+## To use an "alternative" definition, simply "modify" (or "replace")
+## the definition below with one that you like. For example, you can
+## change the compiler to "cc", or remove the "debugging" options, or
+## remove the X11 or Curses support, etc, as desired.
+##
+## See also "config.h" and "h-config.h" for important information.
+##
+## Some "examples" are given below, they can be used by simply
+## removing the FIRST column of "#" signs from the "block" of lines
+## you wish to use, and commenting out "standard" block below.
+##
+## This is not intended to be a "good" Makefile, just a "simple" one.
+##
+
+
+# This is my compiler of choice, it seems to work most everywhere
+#
+CC ?= cc
+
+# Standard version (see main-x11.c and main-gcu.c)
+#
+# This version supports both "X11" and "curses" in a single executable.
+#
+# You may have to add various X11 include/library directories to the
+# "INCLUDES", if your machine places files in a weird location
+# (e.g. -I/usr/X11R6/include, as is almost always the case with
+# linux and *BSD). Since we have seen many linux users -- arguably
+# the largest unix population -- confused by this, and adding this
+# usually doesn't hurt, the default rule has been changed to search
+# for /usr/X11R6/include.
+#
+# You may be able to remove "-ltermcap" on some machines (ex: Solaris).
+#
+# You may have to replace "-lcurses" with "-lncurses" to use the
+# "new curses" library instead of the "old curses" library, and
+# you may have to add "-I/usr/include/ncurses" to the "INCLUDES",
+# and/or "-DUSE_NCURSES" to "DEFINES".
+#
+# See "main-gcu.c" and "config.h" for some optional "curses" defines,
+# including "USE_GETCH" and "USE_CURS_SET". Note that "config.h" will
+# attempt to "guess" at many of these flags based on your system.
+#
+COPTS = -Wall -g
+INCLUDES = -I$(X11BASE)/include
+DEFINES = -DUSE_X11 -DUSE_GCU\
+ -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY -DSUPPORT_GAMMA \
+ -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK -DFILE_MODE=$(FILE_MODE)
+LIBS = -lX11 -lcurses -L$(X11BASE)/lib
+
+
+### End of configurable section ###
+
+#
+# The "source" and "object" files.
+#
+
+BASESRCS = \
+ main-gtk.c main-gcu.c main-x11.c main-xaw.c main-sdl.c \
+ z-rand.c z-util.c z-form.c z-virt.c z-term.c z-sock.c \
+ variable.c tables.c plots.c util.c cave.c dungeon.c \
+ melee1.c melee2.c modules.c \
+ object1.c object2.c randart.c squeltch.c traps.c \
+ monster1.c monster2.c monster3.c ghost.c \
+ xtra1.c xtra2.c skills.c powers.c gods.c \
+ spells1.c spells2.c \
+ status.c files.c notes.c loadsave.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ help.c \
+ generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c \
+ cmovie.c irc.c \
+ wizard2.c init2.c birth.c wizard1.c init1.c main.c
+
+BASEOBJS = \
+ main-gtk.o main-gcu.o main-x11.o main-xaw.o main-sdl.o \
+ z-rand.o z-util.o z-form.o z-virt.o z-term.o z-sock.o \
+ variable.o tables.o plots.o util.o cave.o dungeon.o \
+ melee1.o melee2.o modules.o \
+ object1.o object2.o randart.o squeltch.o traps.o \
+ monster1.o monster2.o monster3.o ghost.o \
+ xtra1.o xtra2.o skills.o powers.o gods.o \
+ spells1.o spells2.o \
+ status.o files.o notes.o loadsave.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ help.o \
+ generate.o gen_maze.o gen_evol.o wild.o levels.o store.o bldg.o \
+ cmovie.o irc.o \
+ wizard2.o init2.o birth.o wizard1.o init1.o main.o
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS += $(COPTS) $(INCLUDES) $(DEFINES) -DDEFAULT_PATH=\"$(LIBDIR)\"
+
+
+#
+# Lua support
+#
+
+INCLUDES += -Ilua -I.
+DEFINES += -DUSE_LUA
+SRCS = $(LUASRCS) $(TOLUASRCS) $(BASESRCS)
+OBJS = $(LUAOBJS) $(TOLUAOBJS) $(BASEOBJS)
+
+# Force recreation of stub files when lua source files are updated
+# To be included in dependency rules
+TOLUADEP = $(TOLUA) $(TOLUASRCS) lua/tolua.c lua/tolualua.c
+
+#
+# IRC support
+#
+
+IRC_SERVER=irc.worldirc.org
+IRC_PORT=6667
+IRC_CHANNEL=\#tome
+
+DEFINES += \
+ -DIRC_SERVER=\"$(IRC_SERVER)\" \
+ -DIRC_PORT=\"$(IRC_PORT)\" \
+ -DIRC_CHANNEL=\"$(IRC_CHANNEL)\"
+
+
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+
+TOLUA = ./tolua
+
+default: $(TOLUA) $(TARGET)
+ @echo "*** Note: In order to use the install rule, which now actually"
+ @echo "*** handles the installation of the library dir, you need to edit"
+ @echo "*** this makefile, going to the top and making sure LIBDIR suits"
+ @echo "*** your desired install dir properly. The LIBRARY_DIR you used"
+ @echo "*** to set in config.h is now ignored and obsolete with respect"
+ @echo "*** to this makefile. Note that if you edit this makefile, you may"
+ @echo "*** need to recompile so all the files that reference those defines"
+ @echo "*** notice the changes."
+
+$(TARGET): $(OBJS)
+ $(CC) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+#
+# An install rule.
+#
+mini_install: default
+ cp -f $(TARGET) ..
+
+install: default
+ [ -d $(LIBDIR) ] || mkdir -p $(LIBDIR)
+ [ -d $(BINDIR) ] || mkdir -p $(BINDIR)
+ cp -r ../lib/* $(LIBDIR)
+ chown -R $(OWNER):$(GROUP) $(LIBDIR)
+ chmod -R g+w $(LIBDIR)
+ cp -f $(TARGET) $(BINDIR)/$(TARGET)
+ strip $(BINDIR)/$(TARGET)
+ chown $(OWNER):$(GROUP) $(BINDIR)/$(TARGET)
+ chmod 2755 $(BINDIR)/$(TARGET)
+
+# old-install: $(TARGET)
+# cp $(TARGET) ..
+
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak *.o lua/*.o w_*.c
+
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Quests
+#
+plots.o: q_rand.c q_main.c q_one.c q_ultrag.c q_ultrae.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_shroom.c q_thrain.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+#
+# Lua library compilation rules
+#
+
+w_mnster.c: monster.pkg $(TOLUADEP)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUADEP)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUADEP)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUADEP)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUADEP)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUADEP)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUADEP)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUADEP)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.cyg b/src/makefile.cyg
new file mode 100644
index 00000000..5481e9ae
--- /dev/null
+++ b/src/makefile.cyg
@@ -0,0 +1,433 @@
+# This is a makefile for the Cygwin tools which may
+# be downloaded by following the links from the URL:
+# http://sources.redhat.com/cygwin/
+#
+# To compile with this makefile, rename it from 'makefile.cyg'
+# to 'makefile', then enter the src directory and type
+# 'make install'.
+
+##
+## Before you type "make depend; make", please follow these three steps.
+##
+## 1. Lua-support and GNU Make ifdef's
+##
+## The ifdef ... endif struct may not be supported by 'make'
+## you are using. GNU make does and Berkeley make doesn't, for example.
+## If it doesn't, you can still manually enable/disable them with minimum
+## amount of effort:
+## (1) Search for lines containing ifdef and endif (one occurence for each)
+## (2) Comment these lines out
+## (3-optional) Comment out lines surrounded by these if you are
+## *not* going to compile the game with Lua scripting enabled.
+##
+
+# Comment out this line if you don't want Lua scripting
+LUA=TRUE
+
+#IRCARGS = -DIRC_SERVER=\"irc.worldirc.org\" -DIRC_PORT=\"6667\" -DIRC_CHANNEL=\"\#tome\"
+
+
+
+##
+## 2. Installation locations and such
+##
+## LIBDIR, BINDIR and OWNER should be set appropriately for
+## multiuser installations.
+##
+## If you want to keep it private or don't have root privilege
+## required by "make install", set LIBDIR to ./lib/ or an absolute
+## path pointing to your lib directory, and don't run "make install".
+##
+## NOTE: If LIBDIR is set to ./lib/ , you have to cd to parent directory
+## of lib before you start the game.
+##
+
+# Where lib/ files goes
+#LIBDIR = /usr/lib/games/pernband/
+# Sysadmins of commercial Unix and/or BSD might prefer this
+#LIBDIR = /usr/local/lib/pernband/
+# If you like the old default, use this one
+LIBDIR = ./lib/
+# Another example: single user installation using absolute path
+#LIBDIR = /home/myloginname/lib/pern/
+
+
+# Where PernAngband binary goes
+#BINDIR = /usr/local/games
+# Another common location
+#BINDIR = /usr/local/bin
+
+# The game will run suid to this user
+#OWNER = games
+
+##
+## 3. Some "system" definitions
+##
+## No changes are needed to compile a version that will run on both
+## X11 and Curses, in debugging mode, with maximal warnings, on many
+## normal Unix machines of the Sun OS variety (non-solaris).
+##
+## To use an "alternative" definition, simply "modify" (or "replace")
+## the definition below with one that you like. For example, you can
+## change the compiler to "cc", or remove the "debugging" options, or
+## remove the X11 or Curses support, etc, as desired.
+##
+## See also "config.h" and "h-config.h" for important information.
+##
+## Some "examples" are given below, they can be used by simply
+## removing the FIRST column of "#" signs from the "block" of lines
+## you wish to use, and commenting out "standard" block below.
+##
+## This is not intended to be a "good" Makefile, just a "simple" one.
+##
+
+
+#
+# This is my compiler of choice, it seems to work most everywhere
+#
+CC = gcc
+
+#
+# resource complier
+#
+
+WRES = windres
+
+#
+# Standard version (see main-x11.c and main-gcu.c)
+#
+# This version supports both "X11" and "curses" in a single executable.
+#
+# You may have to add various X11 include/library directories to the
+# "INCLUDES", if your machine places files in a weird location
+# (e.g. -I/usr/X11R6/include, as is almost always the case with
+# linux and *BSD). Since we have seen many linux users -- arguably
+# the largest unix population -- confused by this, and adding this
+# usually doesn't hurt, the default rule has been changed to search
+# for /usr/X11R6/include.
+#
+# You may be able to remove "-ltermcap" on some machines (ex: Solaris).
+#
+# You may have to replace "-lcurses" with "-lncurses" to use the
+# "new curses" library instead of the "old curses" library, and
+# you may have to add "-I/usr/include/ncurses" to the "INCLUDES",
+# and/or "-DUSE_NCURSES" to "DEFINES".
+#
+# See "main-gcu.c" and "config.h" for some optional "curses" defines,
+# including "USE_GETCH" and "USE_CURS_SET". Note that "config.h" will
+# attempt to "guess" at many of these flags based on your system.
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU
+#LIBS = -lX11 -lcurses -L/usr/X11R6/lib
+
+
+##
+## For cygwin
+##
+COPTS = -s -Wall -mno-cygwin -O2 -fno-strength-reduce
+INCLUDES =
+DEFINES = -DWINDOWS -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA \
+ -DUSE_PRECISE_CMOVIE \
+ -DUSE_WINSOCK \
+ $(IRCARGS)
+
+LIBS = -mno-cygwin -mwindows -e _mainCRTStartup -lwinmm -lwsock32
+
+
+##
+## Variation -- Only support "main-gtk.c" (not "main-gcu.c, main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `gtk-config --cflags`
+#DEFINES = -DUSE_GTK -DUSE_TRANSPARENCY
+#LIBS = -lX11 -L/usr/X11R6/lib `gtk-config --libs`
+
+##
+## Variation -- Only support "main-x11.c" (not "main-gcu.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11
+#LIBS = -lX11 -L/usr/X11R6/lib
+
+
+##
+## Variation -- Only support "main-gcu.c" (not "main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+##
+## Variation -- Use "main-xaw.c" instead of "main-x11.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_XAW -DUSE_GCU
+#LIBS = -lXaw -lXmu -lXt -lX11 -lcurses -ltermcap -L/usr/X11R6/lib
+
+
+##
+## Variation -- Use "main-cap.c" instead of "main-gcu.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_CAP
+#LIBS = -lX11 -ltermcap
+
+
+##
+## Variation -- Only work on simple vt100 terminals
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_CAP -DUSE_HARDCODE
+
+
+##
+## Variation -- this might work for Linux 1.1.93 using ncurses-1.8.7.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU
+#LIBS = -L/usr/X11R6/lib -lX11 -lncurses
+
+
+##
+## Variation -- this might work better than the suggestion above
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TPOSIX -DUSE_CURS_SET
+#LIBS = -lX11 -lncurses
+#LDFLAGS = -s
+
+
+##
+## Variation -- compile for FreeBSD with ncurses
+## -- BSD curses gives you B&W display.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_NCURSES
+#LIBS = -L/usr/X11R6/lib -lX11 -lncurses
+
+
+##
+## Variation -- compile for Solaris
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSOLARIS
+#LIBS = -lX11 -lsocket -lcurses
+
+
+##
+## Variation -- compile for SGI Indigo runnig Irix
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSGI
+#LIBS = -lX11 -lcurses -ltermcap -lsun
+
+
+
+##
+## Variation -- compile for Dec ALPHA OSF/1 v2.0
+##
+#CC = cc
+##COPTS = -std -O -g3 -Olimit 4000
+#COPTS = -std -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU
+#LIBS = -lX11 -lcurses -ltermcap -lrpcsvc
+
+
+##
+## Variation -- compile for Interactive Unix (ISC) systems
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DISC
+#LIBS = -lX11 -lcurses -lnsl_s -linet -lcposix
+
+
+##
+## Variation -- Support fat binaries under NEXTSTEP
+##
+#COPTS = -Wall -O1 -pipe -g -arch m68k -arch i386
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+### End of configurable section ###
+
+#
+# The "source" and "object" files.
+#
+
+BASESRCS = \
+ z-util.c z-virt.c z-form.c z-rand.c z-term.c z-sock.c \
+ variable.c tables.c util.c cave.c cmovie.c \
+ object1.c object2.c traps.c monster1.c monster2.c monster3.c \
+ xtra1.c xtra2.c spells1.c spells2.c \
+ melee1.c melee2.c loadsave.c files.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ store.c birth.c notes.c help.c \
+ status.c randart.c gods.c modules.c \
+ wizard1.c wizard2.c levels.c ghost.c \
+ generate.c gen_maze.c gen_evol.c dungeon.c init1.c init2.c \
+ bldg.c squeltch.c wild.c powers.c plots.c \
+ irc.c skills.c \
+ readdib.c angband.rc main-win.c main.c
+
+BASEOBJS = \
+ z-term.o z-rand.o z-form.o z-virt.o z-util.o z-sock.o \
+ main.o main-win.o readdib.o angband.res \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o plots.o help.o \
+ store.o birth.o wizard1.o wizard2.o bldg.o cmovie.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ loadsave.o files.o levels.o notes.o squeltch.o \
+ status.o randart.o irc.o skills.o gods.o modules.o \
+ xtra1.o xtra2.o spells1.o spells2.o melee1.o melee2.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ variable.o tables.o util.o cave.o ghost.o wild.o powers.o
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) -DDEFAULT_PATH=\"$(LIBDIR)\"
+
+
+#
+# Lua support
+#
+
+LUAFLAGS = -DUSE_LUA -Ilua -I.
+
+ifdef LUA
+SRCS += $(LUASRCS)
+OBJS += $(LUAOBJS)
+SRCS += $(TOLUASRCS)
+OBJS += $(TOLUAOBJS)
+CFLAGS += $(LUAFLAGS)
+endif
+
+
+#
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+TOLUA = ./tolua
+
+default: $(TARGET) $(TOLUA)
+
+$(TARGET): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+#
+# An install rule.
+#
+install: default
+ cp -f $(TARGET) ..
+
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak $(OBJS)
+
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+.SUFFIXES: .rc .res
+.rc.res:
+ $(WRES) $< -O coff -o $@
+
+#
+# Lua library compilation rules
+#
+
+ifdef LUA
+w_mnster.c: monster.pkg $(TOLUA)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUA)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUA)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUA)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUA)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUA)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUA)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUA)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+endif
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.dos b/src/makefile.dos
new file mode 100644
index 00000000..93d710dd
--- /dev/null
+++ b/src/makefile.dos
@@ -0,0 +1,169 @@
+# File: Makefile.dos
+
+# Purpose: Makefile support for "main-dos.c"
+
+#
+# Note: Rename to "Makefile" before using
+#
+# Allegro support by Robert Ruehlmann (rr9@angband.org)
+#
+
+# Compiling with MOD-file support:
+# - Get the JG-MOD library from http://www.jgmod.home.ml.org and install it.
+# - Insert -ljgmod in front of -lalleg to the Libraries section.
+# - Add -DUSE_MOD_FILES to the compiler flags.
+# - Copy your MOD-files into the "lib/xtra/music" folder.
+
+# Enable lua scripting supoprt
+LUA = TRUE
+
+# Enable the IRC client -- need libsocket
+#IRC = TRUE
+
+#
+# Basic definitions
+#
+
+# Objects
+OBJS = \
+ main.o main-dos.o main-ibm.o irc.o \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o plots.o help.o \
+ store.o birth.o wizard1.o wizard2.o bldg.o cmovie.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ loadsave.o files.o levels.o notes.o squeltch.o \
+ status.o randart.o skills.o gods.o modules.o \
+ xtra1.o xtra2.o spells1.o spells2.o melee1.o melee2.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ variable.o tables.o util.o cave.o ghost.o wild.o powers.o \
+ z-term.o z-rand.o z-form.o z-virt.o z-util.o z-sock.o
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+ifdef LUA
+OBJS += $(LUAOBJS)
+OBJS += $(TOLUAOBJS)
+endif
+
+# Compiler
+CC = gcc
+
+ifdef LUA
+LUAFLAGS = -DUSE_LUA -I. -I./lua
+endif
+
+# Compiler flags
+CFLAGS = -Wall -O2 -s -DUSE_DOS -DUSE_IBM -DUSE_BACKGROUND \
+-DUSE_TRANSPARENCY $(LUAFLAGS)
+
+# Libraries
+LIBS = -lpc -lalleg $(LUALIBS)
+
+ifdef IRC
+CFLAGS += -DUSE_SOCK -DUSE_DOSSOCK
+LIBS += -lsocket
+
+# cant get libsocket to resolve hosts
+CFLAGS += -DIRC_SERVER=\"216.41.105.77\"
+CFLAGS += -DIRC_PORT=\"6667\"
+CFLAGS += -DIRC_CHANNEL=\"\#tome\"
+
+endif
+
+#
+# Targets
+#
+
+TOLUA = tolua.exe
+
+default: ../tome.exe $(TOLUA)
+
+release: ../tome.exe
+ upx -9 ../tome.exe
+# copy tome.exe ..
+# del tome.exe
+
+install: ../tome.exe
+# copy tome.exe ..
+
+all: ../tome.exe
+# @echo All done. Use 'make install' to install.
+
+re: clean all
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+
+#
+# Link executables
+#
+
+../tome.exe: $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
+
+
+#
+# Compile source files
+#
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Clean up
+#
+
+clean:
+ del *.o
+
+cleanall: clean
+ del *.exe
+
+plots.o: q_rand.c q_main.c q_one.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c q_shroom.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_wolves.c q_drag~1.c q_haun~1.c q_evil.c
+
+LUA_RECOMP = true
+ifdef LUA_RECOMP
+w_mnster.c: monster.pkg $(TOLUA)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUA)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUA)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUA)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUA)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUA)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUA)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUA)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+endif
diff --git a/src/makefile.emx b/src/makefile.emx
new file mode 100644
index 00000000..214932dd
--- /dev/null
+++ b/src/makefile.emx
@@ -0,0 +1,205 @@
+# File: Makefile.emx
+
+# Purpose: Makefile support for "main-emx.c"
+
+# Note: Use 'dmake -B -r -f makefile.emx' to compile (see "main-emx.c" for details).
+# Use 'dmake -B -r -f makefile.emx install' to install the executables
+# and the batch file used for multiple VIO windows in the parent directory.
+#
+# Use 'dmake -B -r -f makefile.emx clean' to remove temporary files.
+#
+# Advanced targets (used only by developers):
+#
+# 'patches': If the original sources are located in "..\old\src",
+# then a file "..\patches.txt" will be created which contains
+# all changed files (diff -c). "..\exp\patches.uue" will
+# contain the same file gziped and uuencodes. "..\exp\files.uue"
+# will contain the three emx-specific files tared, gziped and
+# uuencoded. Needs 4OS2.
+#
+# 'export': The file "..\exp\$(EXPORT)" is created so that it
+# can directly be uploaded as an official distribution archive.
+# Needs 4OS2.
+#
+
+VERSION = 279v5
+EXPORT = angband-$(VERSION).os2.zip
+
+CC = gcc
+AR = ar
+CFLAGS = -MMD -O3 -DUSE_EMX -Zmt
+LFLAGS = -lvideo
+
+# Uncomment this if you have nice installed
+#NICE = nice -i -n -30
+
+###################################################################################
+
+.KEEP_STATE:
+
+default: all depends :-)
+
+install: ..\angband.exe ..\aclient.exe ..\startwnd.cmd
+
+clean:
+ -+@ del angband.exe
+ -+@ del aclient.exe
+ -+@ del depends
+ -+@ del _state.mk
+ -+@ del *.a
+ -+@ del *.d
+ -+@ del *.o
+
+patches: ..\patches.txt ..\exp\patches.uue ..\exp\files.uue
+
+export: ..\exp\$(EXPORT)
+
+####################################################################################
+### You don't have to change anything below. ###################################
+####################################################################################
+
+OBJS = \
+ z-util.o z-virt.o z-form.o z-rand.o z-term.o z-sock.o \
+ variable.o tables.o util.o cave.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ xtra1.o xtra2.o spells1.o spells2.o \
+ melee1.o melee2.o files.o cmovie.o \
+ status.o randart.o gods.o modules.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ store.o birth.o loadsave.o notes.o squeltch.o \
+ wizard1.o wizard2.o levels.o ghost.o plots.o \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o \
+ bldg.o wild.o powers.o help.o
+
+.c.o:
+ $(NICE) $(CC) $(CFLAGS) -c $*.c
+
+all .PHONY: angband.exe aclient.exe
+ +@echo.
+ +@echo Now type
+ +@echo.
+ +@echo '$(MAKECMD) $(MFLAGS) $(MAKEFILE) install'
+ +@echo.
+ +@echo to install Angband in the parent directory, and/or
+ +@echo.
+ +@echo '$(MAKECMD) $(MFLAGS) $(MAKEFILE) clean'
+ +@echo.
+ +@echo to remove a bunch of temporary files used during compilation!
+ +@echo You may want to remove the src subdirectory, now that you have
+ +@echo working executables.
+ +@echo.
+
+EXPFILES = angband.exe;aclient.exe;patches.txt;readme;startwnd.cmd
+
+..\exp\$(EXPORT) .PHONY .IGNORE: install patches
+# Needs 4OS2!
+ +@ md ..\exp >& nul ^ \
+ md ..\exp\tmpdir >& nul ^ \
+ cd ..\exp\tmpdir ^ \
+ copy ...\$(EXPFILES) > nul ^ \
+ md lib ^ \
+ copy ...\lib\ lib\ /s >& nul ^ \
+ del lib\save\player >& nul ^ \
+ zip -m -r $(EXPORT) * > nul ^ \
+ move $(EXPORT) .. ^ \
+ cd .. ^ \
+ del tmpdir /xsqy >& nul
+
+PATCHFILES = *.c *.h makefile* ..\lib\user\pref-emx.prf
+
+# Needs 4OS2!
+..\patches.txt .PHONY .IGNORE:
+ +@ echo These are the changes to the original source > ..\patches.txt ^ \
+ echo archive ($(VERSION)). You don't need to apply them >> ..\patches.txt ^ \
+ echo if you can get the latest archive, which will >> ..\patches.txt ^ \
+ echo have them applied already. >> ..\patches.txt ^ \
+ echo. >> ..\patches.txt ^ \
+ except (*~) for %a in ($(PATCHFILES)) \
+ do (diff -c ..\old\src\%a %a >> ..\patches.txt)
+
+# Needs 4OS2!
+..\exp\patches.uue: ..\patches.txt
+ +@ md ..\exp >& nul ^ \
+ cd ..\exp ^ \
+ copy ..\patches.txt patches-$(VERSION).os2 ^ \
+ gzip -f patches-$(VERSION).os2 ^ \
+ uuencode patches-$(VERSION).os2.gz >& nul ^ \
+ del patches-$(VERSION).os2.gz ^ \
+ move patches-$(VERSION).os2.gz.uue patches.uue
+
+FILES = main-emx.c makefile.emx ..\lib\user\pref-emx.prf
+FILESP = main-emx.c makefile.emx pref-emx.prf
+
+# Needs 4OS2!
+..\exp\files.uue: $(FILES)
+ +@ md ..\exp >& nul ^ \
+ cd ..\exp ^ \
+ for %a in ($(FILES)) copy ..\src\%a > nul ^ \
+ tar -cvf files.tar $(FILESP) ^ \
+ del $(FILESP) ^ \
+ gzip files.tar ^ \
+ uuencode files.tar.gz ^ \
+ del files.tar.gz >& nul ^ \
+ move files.tar.gz.uue files.uue
+
+depends .IGNORE: $(OBJS)
+ + echo. > depends
+ + for %a in (*.d) type %a >> depends
+
+..\angband.exe: angband.exe
+ + copy angband.exe ..
+ emxbind -s ..\angband.exe
+
+..\aclient.exe: aclient.exe
+ + copy aclient.exe ..
+ emxbind -s ..\aclient.exe
+
+EC=+@ echo
+ECF=>> ..\startwnd.cmd
+
+..\startwnd.cmd:
+ $(EC) /* rexx, sir */ > ..\startwnd.cmd
+ $(EC). $(ECF)
+ $(EC) call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs' $(ECF)
+ $(EC) call SysLoadFuncs $(ECF)
+ $(EC). $(ECF)
+ $(EC) /* This file starts up Angband and up to seven other views. The */ $(ECF)
+ $(EC) /* optional number behind the name sets the number of lines for */ $(ECF)
+ $(EC) /* that screen. Choose contents for each via the game options. */ $(ECF)
+ $(EC). $(ECF)
+ $(EC) say 'starting up terminals...' $(ECF)
+ $(EC). $(ECF)
+ $(EC) '@start /b /win /n aclient Mirror 23' $(ECF)
+ $(EC) '@start /b /win /n aclient Recall 10' $(ECF)
+ $(EC) /* $(ECF)
+ $(EC) '@start /b /win /n aclient Choice' $(ECF)
+ $(EC) '@start /b /win /n aclient Term-4' $(ECF)
+ $(EC) '@start /b /win /n aclient Term-5' $(ECF)
+ $(EC) '@start /b /win /n aclient Term-6' $(ECF)
+ $(EC) '@start /b /win /n aclient Term-7' $(ECF)
+ $(EC) */ $(ECF)
+ $(EC) call SysSleep(2) $(ECF)
+ $(EC) '@angband 'ARG(1) $(ECF)
+ $(EC) exit $(ECF)
+
+angband.exe: angband.a main.o main-emx.o main-epm.o
+ $(CC) -o angband.exe main.o main-emx.o angband.a $(LFLAGS)
+
+angband.a: $(OBJS)
+ $(AR) r angband.a $(OBJS)
+
+aclient.exe: main-emx.c
+ $(NICE) $(CC) $(CFLAGS) -Wall -D__EMX__CLIENT__ -o aclient.exe main-emx.c -lvideo
+
+main-emx.o: main-emx.c
+ $(NICE) $(CC) $(CFLAGS) -Wall -c main-emx.c -o main-emx.o
+
+main-epm.o: main-emx.c
+ $(NICE) $(CC) $(CFLAGS) -Wall -DEMXPM -c main-emx.c -o main-epm.o
+
+# Forgive me :)
+":-)":
+ +@echo.
+
+.INCLUDE .IGNORE: depends
+
diff --git a/src/makefile.gdb b/src/makefile.gdb
new file mode 100644
index 00000000..47062cd7
--- /dev/null
+++ b/src/makefile.gdb
@@ -0,0 +1,155 @@
+# File: Makefile.gdb
+# By DarkGod, to create a tome.bin to be used with gdb
+
+# Purpose: Makefile support for "main-dos.c"
+
+#
+# Note: Rename to "Makefile" before using
+#
+# Allegro support by Robert Ruehlmann (rr9@angband.org)
+#
+
+# Compiling with MOD-file support:
+# - Get the JG-MOD library from http://www.jgmod.home.ml.org and install it.
+# - Insert -ljgmod in front of -lalleg to the Libraries section.
+# - Add -DUSE_MOD_FILES to the compiler flags.
+# - Copy your MOD-files into the "lib/xtra/music" folder.
+
+# Enable lua scripting supoprt
+LUA = TRUE
+
+#
+# Basic definitions
+#
+
+# Objects
+OBJS = \
+ main.o main-dos.o main-ibm.o \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o plots.o help.o \
+ store.o birth.o wizard1.o wizard2.o bldg.o cmovie.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ loadsave.o files.o levels.o notes.o squeltch.o \
+ status.o randart.o gods.o skills.o modules.o \
+ xtra1.o xtra2.o spells1.o spells2.o melee1.o melee2.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ variable.o tables.o util.o cave.o ghost.o wild.o powers.o \
+ z-term.o z-rand.o z-form.o z-virt.o z-util.o z-sock.o
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+ifdef LUA
+OBJS += $(LUAOBJS)
+OBJS += $(TOLUAOBJS)
+endif
+
+# Compiler
+CC = gcc
+
+ifdef LUA
+LUAFLAGS = -DUSE_LUA -DLUA_NUM_TYPE='long long' -I. -I./lua
+endif
+
+# Compiler flags
+CFLAGS = -Wall -g -DUSE_DOS -DUSE_IBM -DUSE_BACKGROUND \
+-DUSE_TRANSPARENCY $(LUAFLAGS)
+
+# Libraries
+LIBS = -lpc -lalleg $(LUALIBS)
+
+
+#
+# Targets
+#
+
+TOLUA = tolua.exe
+
+default: ../tome.exe $(TOLUA)
+
+release: ../tome.exe
+ upx -9 ../tome.exe
+# copy tome.exe ..
+# del tome.exe
+
+install: ../tome.exe
+# copy tome.exe ..
+
+all: ../tome.exe
+# @echo All done. Use 'make install' to install.
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+
+#
+# Link executables
+#
+
+../tome.exe: $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
+
+
+#
+# Compile source files
+#
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Clean up
+#
+
+clean:
+ del *.o
+
+cleanall: clean
+ del *.exe
+
+plots.o: q_rand.c q_main.c q_one.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c q_shroom.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+LUA_RECOMP = true
+ifdef LUA_RECOMP
+w_mnster.c: monster.pkg $(TOLUA)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUA)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUA)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUA)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUA)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUA)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUA)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUA)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+endif
diff --git a/src/makefile.ibm b/src/makefile.ibm
new file mode 100644
index 00000000..ffee2830
--- /dev/null
+++ b/src/makefile.ibm
@@ -0,0 +1,165 @@
+# File: Makefile.dos
+
+# Purpose: Makefile support for "main-dos.c"
+
+#
+# Note: Rename to "Makefile" before using
+#
+# Allegro support by Robert Ruehlmann (rr9@angband.org)
+#
+
+# Compiling with MOD-file support:
+# - Get the JG-MOD library from http://www.jgmod.home.ml.org and install it.
+# - Insert -ljgmod in front of -lalleg to the Libraries section.
+# - Add -DUSE_MOD_FILES to the compiler flags.
+# - Copy your MOD-files into the "lib/xtra/music" folder.
+
+# Enable lua scripting supoprt
+LUA = TRUE
+
+# Enable the IRC client -- need libsocket
+#IRC = TRUE
+
+#
+# Basic definitions
+#
+
+# Objects
+OBJS = \
+ main.o main-dos.o main-ibm.o irc.o \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o plots.o help.o \
+ store.o birth.o wizard1.o wizard2.o bldg.o cmovie.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ loadsave.o files.o levels.o notes.o squeltch.o \
+ status.o randart.o skills.o gods.o modules.o \
+ xtra1.o xtra2.o spells1.o spells2.o melee1.o melee2.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ variable.o tables.o util.o cave.o ghost.o wild.o powers.o \
+ z-term.o z-rand.o z-form.o z-virt.o z-util.o z-sock.o
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+OBJS += $(LUAOBJS)
+OBJS += $(TOLUAOBJS)
+
+# Compiler
+CC = gcc
+
+LUAFLAGS = -DUSE_LUA -I. -I./lua
+
+# Compiler flags
+CFLAGS = $(LUAFLAGS) -Wall -g -DUSE_IBM
+
+# Libraries
+LIBS = -lpc $(LUALIBS)
+
+ifdef IRC
+CFLAGS += -DUSE_SOCK -DUSE_DOSSOCK
+LIBS += -lsocket
+
+# cant get libsocket to resolve hosts
+CFLAGS += -DIRC_SERVER=\"216.41.105.77\"
+CFLAGS += -DIRC_PORT=\"6667\"
+CFLAGS += -DIRC_CHANNEL=\"\#
+angband\"
+
+endif
+
+#
+# Targets
+#
+
+TOLUA = tolua.exe
+
+default: ../tome.exe $(TOLUA)
+
+release: ../tome.exe
+ upx -9 ../tome.exe
+# copy tome.exe ..
+# del tome.exe
+
+install: ../tome.exe
+# copy tome.exe ..
+
+all: ../tome.exe
+# @echo All done. Use 'make install' to install.
+
+re: clean all
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+
+#
+# Link executables
+#
+
+../tome.exe: $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
+
+
+#
+# Compile source files
+#
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Clean up
+#
+
+clean:
+ del *.o
+
+cleanall: clean
+ del *.exe
+
+plots.o: q_rand.c q_main.c q_one.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c q_shroom.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+LUA_RECOMP = true
+ifdef LUA_RECOMP
+w_mnster.c: monster.pkg $(TOLUA)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUA)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUA)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUA)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUA)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUA)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUA)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUA)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+endif
diff --git a/src/makefile.lsl b/src/makefile.lsl
new file mode 100644
index 00000000..ae03b803
--- /dev/null
+++ b/src/makefile.lsl
@@ -0,0 +1,42 @@
+# File: Makefile.lsl
+
+# Purpose: Makefile for Linux + SVGA library
+
+SRCS = \
+ z-util.c z-virt.c z-form.c z-rand.c z-term.c z-sock.c \
+ variable.c tables.c util.c cave.c cmovie.c \
+ object1.c object2.c traps.c monster1.c monster2.c monster3.c \
+ xtra1.c xtra2.c spells1.c spells2.c help.c \
+ melee1.c melee2.c save.c files.c notes.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ status.c randart.c gods.c modules.c \
+ store.c birth.c loadsave.c ghost.c \
+ wizard1.c wizard2.c wild.c powers.c \
+ generate.c gen_maze.c gen_evol.c dungeon.c init1.c init2.c \
+ bldg.c levels.c squeltch.c plots.c \
+ main-ami.c main.c
+
+OBJS = \
+ z-util.o z-virt.o z-form.o z-rand.o z-term.o z-sock.o \
+ variable.o tables.o util.o cave.o cmovie.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ xtra1.o xtra2.o spells1.o spells2.o help.o \
+ melee1.o melee2.o files.o notes.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ status.o randart.o gods.o modules.o \
+ store.o birth.o loadsave.o ghost.c \
+ wizard1.o wizard2.o wild.o powers.o \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o \
+ bldg.o levels.o squeltch.o plots.o \
+ main-lsl.o main.o
+
+CC = gcc
+
+CFLAGS = -Wall -O6 -D"USE_LSL"
+LIBS = -lvgagl -lvga
+
+# Build the program
+
+angsvga: $(SRCS) $(OBJS)
+ $(CC) $(CFLAGS) -o angband $(OBJS) $(LDFLAGS) $(LIBS)
+
diff --git a/src/makefile.mingw b/src/makefile.mingw
new file mode 100644
index 00000000..27a8cb6e
--- /dev/null
+++ b/src/makefile.mingw
@@ -0,0 +1,431 @@
+# This is a makefile for the mingw corss platform compiler
+#
+# To compile with this makefile, rename it from 'makefile.mingw'
+# to 'makefile', then enter the src directory and type
+# 'make'.
+
+##
+## Before you type "make depend; make", please follow these three steps.
+##
+## 1. Lua-support and GNU Make ifdef's
+##
+## The ifdef ... endif struct may not be supported by 'make'
+## you are using. GNU make does and Berkeley make doesn't, for example.
+## If it doesn't, you can still manually enable/disable them with minimum
+## amount of effort:
+## (1) Search for lines containing ifdef and endif (one occurence for each)
+## (2) Comment these lines out
+## (3-optional) Comment out lines surrounded by these if you are
+## *not* going to compile the game with Lua scripting enabled.
+##
+
+# Comment out this line if you don't want Lua scripting
+LUA=TRUE
+
+#IRCARGS = -DIRC_SERVER=\"irc.worldirc.org\" -DIRC_PORT=\"6667\" -DIRC_CHANNEL=\"\#tome\"
+
+
+
+##
+## 2. Installation locations and such
+##
+## LIBDIR, BINDIR and OWNER should be set appropriately for
+## multiuser installations.
+##
+## If you want to keep it private or don't have root privilege
+## required by "make install", set LIBDIR to ./lib/ or an absolute
+## path pointing to your lib directory, and don't run "make install".
+##
+## NOTE: If LIBDIR is set to ./lib/ , you have to cd to parent directory
+## of lib before you start the game.
+##
+
+# Where lib/ files goes
+#LIBDIR = /usr/lib/games/pernband/
+# Sysadmins of commercial Unix and/or BSD might prefer this
+#LIBDIR = /usr/local/lib/pernband/
+# If you like the old default, use this one
+LIBDIR = .\lib
+# Another example: single user installation using absolute path
+#LIBDIR = /home/myloginname/lib/pern/
+
+
+# Where PernAngband binary goes
+#BINDIR = /usr/local/games
+# Another common location
+#BINDIR = /usr/local/bin
+
+# The game will run suid to this user
+#OWNER = games
+
+##
+## 3. Some "system" definitions
+##
+## No changes are needed to compile a version that will run on both
+## X11 and Curses, in debugging mode, with maximal warnings, on many
+## normal Unix machines of the Sun OS variety (non-solaris).
+##
+## To use an "alternative" definition, simply "modify" (or "replace")
+## the definition below with one that you like. For example, you can
+## change the compiler to "cc", or remove the "debugging" options, or
+## remove the X11 or Curses support, etc, as desired.
+##
+## See also "config.h" and "h-config.h" for important information.
+##
+## Some "examples" are given below, they can be used by simply
+## removing the FIRST column of "#" signs from the "block" of lines
+## you wish to use, and commenting out "standard" block below.
+##
+## This is not intended to be a "good" Makefile, just a "simple" one.
+##
+
+
+#
+# This is my compiler of choice, it seems to work most everywhere
+#
+CC = i386-mingw32msvc-gcc
+
+#
+# resource complier
+#
+
+WRES = i386-mingw32msvc-windres
+
+#
+# Standard version (see main-x11.c and main-gcu.c)
+#
+# This version supports both "X11" and "curses" in a single executable.
+#
+# You may have to add various X11 include/library directories to the
+# "INCLUDES", if your machine places files in a weird location
+# (e.g. -I/usr/X11R6/include, as is almost always the case with
+# linux and *BSD). Since we have seen many linux users -- arguably
+# the largest unix population -- confused by this, and adding this
+# usually doesn't hurt, the default rule has been changed to search
+# for /usr/X11R6/include.
+#
+# You may be able to remove "-ltermcap" on some machines (ex: Solaris).
+#
+# You may have to replace "-lcurses" with "-lncurses" to use the
+# "new curses" library instead of the "old curses" library, and
+# you may have to add "-I/usr/include/ncurses" to the "INCLUDES",
+# and/or "-DUSE_NCURSES" to "DEFINES".
+#
+# See "main-gcu.c" and "config.h" for some optional "curses" defines,
+# including "USE_GETCH" and "USE_CURS_SET". Note that "config.h" will
+# attempt to "guess" at many of these flags based on your system.
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU
+#LIBS = -lX11 -lcurses -L/usr/X11R6/lib
+
+
+##
+## For cygwin
+##
+COPTS = -s -Wall -mno-cygwin -O2 -fno-strength-reduce
+INCLUDES =
+DEFINES = -DWINDOWS -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA \
+ -DUSE_PRECISE_CMOVIE \
+ -DUSE_WINSOCK \
+ $(IRCARGS)
+
+LIBS = -mno-cygwin -mwindows -e _mainCRTStartup -lwinmm -lwsock32
+
+
+##
+## Variation -- Only support "main-gtk.c" (not "main-gcu.c, main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `gtk-config --cflags`
+#DEFINES = -DUSE_GTK -DUSE_TRANSPARENCY
+#LIBS = -lX11 -L/usr/X11R6/lib `gtk-config --libs`
+
+##
+## Variation -- Only support "main-x11.c" (not "main-gcu.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11
+#LIBS = -lX11 -L/usr/X11R6/lib
+
+
+##
+## Variation -- Only support "main-gcu.c" (not "main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+##
+## Variation -- Use "main-xaw.c" instead of "main-x11.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_XAW -DUSE_GCU
+#LIBS = -lXaw -lXmu -lXt -lX11 -lcurses -ltermcap -L/usr/X11R6/lib
+
+
+##
+## Variation -- Use "main-cap.c" instead of "main-gcu.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_CAP
+#LIBS = -lX11 -ltermcap
+
+
+##
+## Variation -- Only work on simple vt100 terminals
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_CAP -DUSE_HARDCODE
+
+
+##
+## Variation -- this might work for Linux 1.1.93 using ncurses-1.8.7.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU
+#LIBS = -L/usr/X11R6/lib -lX11 -lncurses
+
+
+##
+## Variation -- this might work better than the suggestion above
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TPOSIX -DUSE_CURS_SET
+#LIBS = -lX11 -lncurses
+#LDFLAGS = -s
+
+
+##
+## Variation -- compile for FreeBSD with ncurses
+## -- BSD curses gives you B&W display.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_NCURSES
+#LIBS = -L/usr/X11R6/lib -lX11 -lncurses
+
+
+##
+## Variation -- compile for Solaris
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSOLARIS
+#LIBS = -lX11 -lsocket -lcurses
+
+
+##
+## Variation -- compile for SGI Indigo runnig Irix
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSGI
+#LIBS = -lX11 -lcurses -ltermcap -lsun
+
+
+
+##
+## Variation -- compile for Dec ALPHA OSF/1 v2.0
+##
+#CC = cc
+##COPTS = -std -O -g3 -Olimit 4000
+#COPTS = -std -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU
+#LIBS = -lX11 -lcurses -ltermcap -lrpcsvc
+
+
+##
+## Variation -- compile for Interactive Unix (ISC) systems
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DISC
+#LIBS = -lX11 -lcurses -lnsl_s -linet -lcposix
+
+
+##
+## Variation -- Support fat binaries under NEXTSTEP
+##
+#COPTS = -Wall -O1 -pipe -g -arch m68k -arch i386
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+### End of configurable section ###
+
+#
+# The "source" and "object" files.
+#
+
+BASESRCS = \
+ z-util.c z-virt.c z-form.c z-rand.c z-term.c z-sock.c \
+ variable.c tables.c util.c cave.c cmovie.c \
+ object1.c object2.c traps.c monster1.c monster2.c monster3.c \
+ xtra1.c xtra2.c spells1.c spells2.c \
+ melee1.c melee2.c loadsave.c files.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ store.c birth.c notes.c help.c \
+ status.c randart.c gods.c modules.c \
+ wizard1.c wizard2.c levels.c ghost.c \
+ generate.c gen_maze.c gen_evol.c dungeon.c init1.c init2.c \
+ bldg.c squeltch.c wild.c powers.c plots.c \
+ irc.c skills.c \
+ readdib.c angband.rc main-win.c main.c
+
+BASEOBJS = \
+ z-term.o z-rand.o z-form.o z-virt.o z-util.o z-sock.o \
+ main.o main-win.o readdib.o angband.res \
+ generate.o gen_maze.o gen_evol.o dungeon.o init1.o init2.o plots.o help.o \
+ store.o birth.o wizard1.o wizard2.o bldg.o cmovie.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ loadsave.o files.o levels.o notes.o squeltch.o \
+ status.o randart.o irc.o skills.o gods.o modules.o \
+ xtra1.o xtra2.o spells1.o spells2.o melee1.o melee2.o \
+ object1.o object2.o traps.o monster1.o monster2.o monster3.o \
+ variable.o tables.o util.o cave.o ghost.o wild.o powers.o
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) -DDEFAULT_PATH=\"$(LIBDIR)\"
+
+
+#
+# Lua support
+#
+
+LUAFLAGS = -DUSE_LUA -Ilua -I.
+
+ifdef LUA
+SRCS += $(LUASRCS)
+OBJS += $(LUAOBJS)
+SRCS += $(TOLUASRCS)
+OBJS += $(TOLUAOBJS)
+CFLAGS += $(LUAFLAGS)
+endif
+
+
+#
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+TOLUA = ./tolua
+
+default: $(TARGET) $(TOLUA)
+
+$(TARGET): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+#
+# An install rule.
+#
+install: default
+ cp -f $(TARGET) ..
+
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak $(OBJS)
+
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+.SUFFIXES: .rc .res
+.rc.res:
+ $(WRES) $< -O coff -o $@
+
+#
+# Lua library compilation rules
+#
+
+ifdef LUA
+w_mnster.c: monster.pkg $(TOLUA)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUA)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUA)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUA)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUA)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUA)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUA)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUA)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+endif
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.my b/src/makefile.my
new file mode 100644
index 00000000..de1def21
--- /dev/null
+++ b/src/makefile.my
@@ -0,0 +1,519 @@
+# File: Makefile
+
+# This is not a very "nice" Makefile, but it usually works.
+
+#
+# Note that you may have to make modifications below according
+# to your machine, available libraries, compilation options,
+# and your "visual module" of choice. This Makefile is intended
+# for use with Unix machines running X11, Curses, Ncurses, or Vt100,
+# or possibly for "Atari" or "Amiga" computers with "Curses" ports,
+# see below for more information.
+#
+# Note that "main-mac.c" and "main-crb.c", the visual modules for
+# the Macintosh, must be compiled in a special way, see elsewhere.
+#
+# Note that "main-win.c", the visual module for Windows,
+# must be compiled in a special way, see elsewhere.
+#
+# Note that "main-ibm.c" and "main-emx.c", the visual modules
+# for various types of IBM-PC computers, must be compiled with
+# special Makefiles, see elsewhere.
+#
+# Note that "main-lsl.c", the visual module for Linux-SVGALIB
+# must be compiled with "Makefile.lsl", see elsewhere.
+#
+# Note that "main-acn.c", the visual module for Risc Acorn,
+# must be compiled with "Makefile.acn", see elsewhere.
+#
+# Note that "Makefile.wat" is a slight variation on "Makefile.ibm",
+# which allow the use of "main-ibm.c" with special compiler
+# (might be out-of-date).
+#
+# If you are able to construct "main-xxx.c" and/or "Makefile.xxx"
+# files for a currently unsupported system, please send them to me
+# (rr9@angband.org) for inclusion in future versions.
+#
+
+##
+## 1. Installation locations and such
+##
+## LIBDIR, BINDIR and OWNER should be set appropriately for
+## multiuser installations.
+##
+## If you want to keep it private or don't have root privilege
+## required by "make install", set LIBDIR to ./lib/ or an absolute
+## path pointing to your lib directory, and don't run "make install".
+##
+## NOTE: If LIBDIR is set to ./lib/ , you have to cd to parent directory
+## of lib before you start the game.
+##
+
+# Where lib/ files goes
+#LIBDIR = /usr/lib/games/tome/
+# Sysadmins of commercial Unix and/or BSD might prefer this
+#LIBDIR = /usr/local/lib/tome/
+# If you like the old default, use this one
+LIBDIR = ./lib/
+# Another example: single user installation using absolute path
+#LIBDIR = /home/myloginname/lib/tome/
+
+
+# Where ToME binary goes
+BINDIR = /usr/local/games
+# Another common location
+#BINDIR = /usr/local/bin
+
+# The game will run suid to this user
+OWNER = games
+
+# Ignore this if you're not making a package
+DESTDIR=
+
+##
+## 2. Some "system" definitions
+##
+## No changes are needed to compile a version that will run on both
+## X11 and Curses, in debugging mode, with maximal warnings, on many
+## normal Unix machines of the Sun OS variety (non-solaris).
+##
+## To use an "alternative" definition, simply "modify" (or "replace")
+## the definition below with one that you like. For example, you can
+## change the compiler to "cc", or remove the "debugging" options, or
+## remove the X11 or Curses support, etc, as desired.
+##
+## See also "config.h" and "h-config.h" for important information.
+##
+## Some "examples" are given below, they can be used by simply
+## removing the FIRST column of "#" signs from the "block" of lines
+## you wish to use, and commenting out "standard" block below.
+##
+## This is not intended to be a "good" Makefile, just a "simple" one.
+##
+
+
+#
+# This is my compiler of choice, it seems to work most everywhere
+#
+CC = gcc
+
+# Profiling options
+# PROFILECOPTS = -pg
+# PROFILELDFLAGS = -pg
+
+#
+# Standard version (see main-x11.c)
+#
+# This version supports "X11" only.
+#
+# You may have to add various X11 include/library directories to the
+# "INCLUDES", if your machine places files in a weird location
+# (e.g. -I/usr/X11R6/include, as is almost always the case with
+# linux and *BSD). Since we have seen many linux users -- arguably
+# the largest unix population -- confused by this, and adding this
+# usually doesn't hurt, the default rule has been changed to search
+# for /usr/X11R6/include.
+#
+# You may be able to remove "-ltermcap" on some machines (ex: Solaris).
+#
+# You may have to replace "-lcurses" with "-lncurses" to use the
+# "new curses" library instead of the "old curses" library, and
+# you may have to add "-I/usr/include/ncurses" to the "INCLUDES",
+# and/or "-DUSE_NCURSES" to "DEFINES".
+#
+# See "main-gcu.c" and "config.h" for some optional "curses" defines,
+# including "USE_GETCH" and "USE_CURS_SET". Note that "config.h" will
+# attempt to "guess" at many of these flags based on your system.
+#
+COPTS = -Wall -O1 -pipe -g
+INCLUDES = -I/usr/X11R6/include
+DEFINES = -DUSE_X11 \
+ -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY -DSUPPORT_GAMMA \
+ -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK
+LIBS = -L/usr/X11R6/lib -lX11
+
+##
+## Variation -- Only support "main-gtk.c" (not "main-gcu.c, main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `gtk-config --cflags`
+#DEFINES = -DUSE_GTK -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = `gtk-config --libs`
+
+##
+## Variation -- Both "main-x11.c" and "main-gcu.c" in a single executable.
+##
+## Your Unix system's curses library may be called "-lcurses" instead of
+## "-lncurses", and you may also need to add "-ltermlib" to the end of the
+## LIBS on some platforms.
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS \
+# -DUSE_TRANSPARENCY -DSUPPORT_GAMMA \
+# -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK
+#LIBS = -L/usr/X11R6/lib -lX11 -lncurses
+
+
+##
+## Variation -- Only support "main-gcu.c" (not "main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+##
+## Variation -- Use "main-xaw.c" instead of "main-x11.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_XAW -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lXaw -lXmu -lXt -lX11 -lcurses -ltermcap -L/usr/X11R6/lib
+
+##
+## Variation -- Use "main-sdl.c" instead of "main-x11.c"
+##
+#SDL_CONFIG ?= sdl-config
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `$(SDL_CONFIG) --cflags`
+#DEFINES = -DUSE_SDL -DUSE_GCU \
+# -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY \
+# -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK -DSUPPORT_GAMMA
+#LIBS = `$(SDL_CONFIG) --libs` -lSDL_image -lSDL_ttf -lcurses
+
+##
+## Variation -- Use "main-cap.c" instead of "main-gcu.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_CAP \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -ltermcap
+
+
+##
+## Variation -- Only work on simple vt100 terminals
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_CAP -DUSE_HARDCODE
+
+
+##
+## Variation -- this might work for Linux 1.1.93 using ncurses-1.8.7.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+
+
+##
+## Variation -- this might work better than the suggestion above
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TPOSIX -DUSE_CURS_SET \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+#LDFLAGS = -s
+
+
+##
+## Variation -- compile for FreeBSD with ncurses
+## -- BSD curses gives you B&W display.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_NCURSES \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+
+
+##
+## Variation -- compile for Solaris
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSOLARIS \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lsocket -lcurses
+
+
+##
+## Variation -- compile for SGI Indigo runnig Irix
+## The SGI has hardware gamma correction.
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSGI \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS
+#LIBS = -lX11 -lcurses -ltermcap -lsun
+
+
+
+##
+## Variation -- compile for Dec ALPHA OSF/1 v2.0
+##
+#CC = cc
+##COPTS = -std -O -g3 -Olimit 4000
+#COPTS = -std -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lcurses -ltermcap -lrpcsvc
+
+
+##
+## Variation -- compile for Interactive Unix (ISC) systems
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DISC \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lcurses -lnsl_s -linet -lcposix
+
+
+##
+## Variation -- Support fat binaries under NEXTSTEP
+##
+#COPTS = -Wall -O1 -pipe -g -arch m68k -arch i386
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+### End of configurable section ###
+
+#
+# The "source" and "object" files.
+#
+
+BASESRCS = \
+ main-gtk.c main-gcu.c main-x11.c main-xaw.c main-sdl.c main-dmy.c \
+ z-rand.c z-util.c z-form.c z-virt.c z-term.c z-sock.c \
+ variable.c tables.c plots.c util.c cave.c dungeon.c \
+ melee1.c melee2.c modules.c \
+ object1.c object2.c randart.c squeltch.c traps.c \
+ monster1.c monster2.c monster3.c ghost.c \
+ xtra1.c xtra2.c skills.c powers.c gods.c \
+ spells1.c spells2.c \
+ status.c files.c notes.c loadsave.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ help.c \
+ generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c \
+ cmovie.c irc.c \
+ wizard2.c init2.c birth.c wizard1.c init1.c main.c
+
+BASEOBJS = \
+ main-gtk.o main-gcu.o main-x11.o main-xaw.o main-sdl.o main-dmy.o \
+ z-rand.o z-util.o z-form.o z-virt.o z-term.o z-sock.o \
+ variable.o tables.o plots.o util.o cave.o dungeon.o \
+ melee1.o melee2.o modules.o \
+ object1.o object2.o randart.o squeltch.o traps.o \
+ monster1.o monster2.o monster3.o ghost.o \
+ xtra1.o xtra2.o skills.o powers.o gods.o \
+ spells1.o spells2.o \
+ status.o files.o notes.o loadsave.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ help.o \
+ generate.o gen_maze.o gen_evol.o wild.o levels.o store.o bldg.o \
+ cmovie.o irc.o \
+ wizard2.o init2.o birth.o wizard1.o init1.o main.o
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS = $(COPTS) $(PROFILECOPTS) $(INCLUDES) $(DEFINES) -DDEFAULT_PATH=\"$(LIBDIR)\"
+
+
+#
+# Lua support
+#
+
+INCLUDES += -Ilua -I.
+DEFINES += -DUSE_LUA
+SRCS = $(LUASRCS) $(TOLUASRCS) $(BASESRCS)
+OBJS = $(LUAOBJS) $(TOLUAOBJS) $(BASEOBJS)
+
+# Force recreation of stub files when lua source files are updated
+# To be included in dependency rules
+TOLUADEP = $(TOLUASRCS) lua/tolua.c lua/tolualua.c
+
+#
+# IRC support
+#
+
+IRC_SERVER=irc.worldirc.org
+IRC_PORT=6667
+IRC_CHANNEL=\#tome
+
+DEFINES += \
+ -DIRC_SERVER=\"$(IRC_SERVER)\" \
+ -DIRC_PORT=\"$(IRC_PORT)\" \
+ -DIRC_CHANNEL=\"$(IRC_CHANNEL)\"
+
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+
+TOLUA = ./tolua
+
+default: $(TOLUA) $(TARGET)
+ @echo "*** Note: In order to use the install rule, which now actually"
+ @echo "*** handles the installation of the library dir, you need to edit"
+ @echo "*** this makefile, going to the top and making sure LIBDIR suits"
+ @echo "*** your desired install dir properly. The LIBRARY_DIR you used"
+ @echo "*** to set in config.h is now ignored and obsolete with respect"
+ @echo "*** to this makefile. Note that if you edit this makefile, you may"
+ @echo "*** need to recompile so all the files that reference those defines"
+ @echo "*** notice the changes."
+
+$(TARGET): $(OBJS)
+ $(CC) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+#
+# An install rule.
+#
+mini_install: default
+ cp -f $(TARGET) ..
+
+install: default
+ [ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR)
+ [ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+ cp -r ../lib/* $(DESTDIR)$(LIBDIR)
+ chown -R $(OWNER) $(DESTDIR)$(LIBDIR)
+ cp -f $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chown $(OWNER) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chmod 4755 $(DESTDIR)$(BINDIR)/$(TARGET)
+
+# old-install: $(TARGET)
+# cp $(TARGET) ..
+
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak *.o lua/*.o w_*.c $(TOLUA) $(TARGET)
+
+
+# Make a src dist
+TARGET_VERSION=`fgrep '[V]' ../changes.txt | sed 's/\[V\]-*\s*T.o.M.E \(.\).\(.\).\(.\) .*/\1\2\3/g'`
+DIST_TARGET_VERSION=../dist/tome-$(TARGET_VERSION)-src
+DIST_TARGET=../dist/tome-xxx-src
+dist: clean
+ mkdir -p $(DIST_TARGET)
+ cp -r ../lib $(DIST_TARGET)
+ cp -r ../src $(DIST_TARGET)
+ ../tome -c ../changes.txt $(DIST_TARGET)/changes.txt
+ cp ../changes.old $(DIST_TARGET)
+ cp ../credits.txt $(DIST_TARGET)
+ cp ../tome.ini $(DIST_TARGET)
+ cp ../angdos.cfg $(DIST_TARGET)
+ rm -f $(DIST_TARGET)/src/tome $(DIST_TARGET)/src/tolua
+ find $(DIST_TARGET) -name '*~' -exec rm {} \;
+ find $(DIST_TARGET)/lib/data/ -name '*.raw' -exec rm {} \;
+ find $(DIST_TARGET)/lib/xtra/graf/ -name '*.gif' -exec rm {} \;
+ find $(DIST_TARGET)/lib/xtra/font/ -name '*.hex' -exec rm {} \;
+ rm -rf `find $(DIST_TARGET) -name 'CVS'`
+ rm -rf `find $(DIST_TARGET)/lib/mods/ -mindepth 1 -maxdepth 1 -type d`
+ sed -i 's/(CVS)//' $(DIST_TARGET)/src/defines.h
+ mv $(DIST_TARGET) $(DIST_TARGET_VERSION)
+ cd ../dist; tar -cvjf tome-$(TARGET_VERSION)-src.tar.bz2 tome-$(TARGET_VERSION)-src
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Quests
+#
+plots.o: q_rand.c q_main.c q_one.c q_ultrag.c q_ultrae.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_shroom.c q_thrain.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+#
+# Lua library compilation rules
+#
+
+w_mnster.c: monster.pkg $(TOLUADEP)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUADEP)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUADEP)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUADEP)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUADEP)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUADEP)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUADEP)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUADEP)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.osx b/src/makefile.osx
new file mode 100644
index 00000000..1c719e88
--- /dev/null
+++ b/src/makefile.osx
@@ -0,0 +1,210 @@
+BUNDLENAME = ToME
+BUNDLEDIR = ../$(BUNDLENAME).app
+CONTENTSDIR = $(BUNDLEDIR)/Contents
+BINDIR = $(CONTENTSDIR)/MacOS
+RESOURCEDIR = $(CONTENTSDIR)/Resources
+LIBDIR = $(RESOURCEDIR)
+
+CC = MACOSX_DEPLOYMENT_TARGET="10.1" cc
+
+COPTS = -Wall -Os -g -pipe -fpascal-strings
+#-Wno-deprecated-declarations
+DEFINES = -DUSE_MACOSX -DMACH_O_CARBON \
+ -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK \
+ -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY -DSUPPORT_GAMMA
+LIBS = -framework CoreFoundation -framework QuickTime -framework Carbon
+
+# Universal binary support
+COPTS += -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc
+LIBS += -arch i386 -arch ppc -Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk
+
+BASESRCS = \
+ main-crb.c \
+ z-rand.c z-util.c z-form.c z-virt.c z-term.c z-sock.c \
+ variable.c tables.c plots.c util.c cave.c dungeon.c \
+ melee1.c melee2.c modules.c \
+ object1.c object2.c randart.c squeltch.c traps.c \
+ monster1.c monster2.c monster3.c ghost.c \
+ xtra1.c xtra2.c skills.c powers.c gods.c \
+ spells1.c spells2.c \
+ status.c files.c notes.c loadsave.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ help.c \
+ generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c \
+ cmovie.c irc.c \
+ wizard2.c init2.c birth.c wizard1.c init1.c
+
+BASEOBJS = \
+ main-crb.o \
+ z-rand.o z-util.o z-form.o z-virt.o z-term.o z-sock.o \
+ variable.o tables.o plots.o util.o cave.o dungeon.o \
+ melee1.o melee2.o modules.o \
+ object1.o object2.o randart.o squeltch.o traps.o \
+ monster1.o monster2.o monster3.o ghost.o \
+ xtra1.o xtra2.o skills.o powers.o gods.o \
+ spells1.o spells2.o \
+ status.o files.o notes.o loadsave.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ help.o \
+ generate.o gen_maze.o gen_evol.o wild.o levels.o store.o bldg.o \
+ cmovie.o irc.o \
+ wizard2.o init2.o birth.o wizard1.o init1.o
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES)
+
+
+#
+# Lua support
+#
+
+INCLUDES += -Ilua -I.
+DEFINES += -DUSE_LUA
+SRCS = $(LUASRCS) $(TOLUASRCS) $(BASESRCS)
+OBJS = $(LUAOBJS) $(TOLUAOBJS) $(BASEOBJS)
+
+# Force recreation of stub files when lua source files are updated
+# To be included in dependency rules
+TOLUADEP = $(TOLUASRCS) lua/tolua.c lua/tolualua.c $(TOLUA)
+
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+
+TOLUA = ./tolua
+
+default: $(TOLUA) $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+install: default
+ [ -d $(BUNDLEDIR) ] || mkdir $(BUNDLEDIR)
+ [ -d $(CONTENTSDIR) ] || mkdir $(CONTENTSDIR)
+ [ -d $(BINDIR) ] || mkdir $(BINDIR)
+ [ -d $(RESOURCEDIR) ] || mkdir $(RESOURCEDIR)
+ install -m 755 $(TARGET) $(BINDIR)
+ install -m 644 carbon/*.icns $(RESOURCEDIR)
+ /Developer/Tools/Rez -i /Developer/Headers/FlatCarbon -d MACH_O -o $(RESOURCEDIR)/tome.rsrc carbon/Carbon.r
+ install -m 644 carbon/Info.plist $(CONTENTSDIR)/Info.plist
+ /Developer/Tools/SetFile -a B $(BUNDLEDIR)
+ rsync -r --exclude="*CVS*" --exclude="*xtra/graf*" --exclude="*xtra/font*" ../lib/* $(RESOURCEDIR)
+ rsync ../lib/mods/*.lua $(RESOURCEDIR)/core
+ rsync ../lib/xtra/graf/*.png $(RESOURCEDIR)
+
+BUNDLEDIR := ../ToME.app
+APPVERSION := `carbon/getversion`
+IMAGENAME := ToME $(APPVERSION)
+IMAGEPATH := /Volumes/$(IMAGENAME)
+
+image: install
+ [ -d "$(IMAGEPATH)" ] && diskutil eject "$(IMAGEPATH)" || true
+
+ hdiutil create -fs HFS+ -volname "$(IMAGENAME)" -ov -type SPARSE -attach "$(IMAGENAME)"
+ cp -r "$(BUNDLEDIR)" ../changes.txt ../credits.txt ../changes.old "$(IMAGEPATH)"
+ cp carbon/Image-DS_Store "$(IMAGEPATH)/.DS_Store"
+ diskutil eject "$(IMAGEPATH)"
+
+ rm -f "$(IMAGENAME).dmg"
+ hdiutil convert "$(IMAGENAME).sparseimage" -format UDZO -o "$(IMAGENAME)"
+ rm "$(IMAGENAME).sparseimage"
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak *.o lua/*.o w_*.c $(TOLUA) $(TARGET)
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Quests
+#
+plots.o: q_rand.c q_main.c q_one.c q_ultrag.c q_ultrae.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_shroom.c q_thrain.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+#
+# Lua library compilation rules
+#
+
+w_mnster.c: monster.pkg $(TOLUADEP)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUADEP)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUADEP)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUADEP)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUADEP)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUADEP)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUADEP)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUADEP)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.ros b/src/makefile.ros
new file mode 100644
index 00000000..af287c51
--- /dev/null
+++ b/src/makefile.ros
@@ -0,0 +1,159 @@
+# Project: ToME
+# Make: make
+# Wimpslot: 5000k
+#
+# To use this makefile, the current working directory must be where
+# this makefile is (if this makefile is at "raFS::Temp.$.ang.src.makefile",
+# the CWD must be "raFS::Temp.$.ang.src").
+#
+# This is a GNU make makefile
+
+# Defaults
+COMPILER := norcroft
+default: standard
+
+# Set this to the location of StubsG if using Norcroft
+STUBSG := <CLibs$$Dir>.clib.o.StubsG
+
+# Lua object files
+LUAOBJS := lapi.o ldebug.o lmem.o lstrlib.o lvm.o \
+ tolua_lb.o lauxlib.o ldo.o lobject.o ltable.o \
+ lzio.o tolua_rg.o lbaselib.o lfunc.o lparser.o \
+ tolua_bd.o tolua_tm.o lcode.o lgc.o \
+ lstate.o ltm.o tolua_eh.o tolua_tt.o ldblib.o \
+ llex.o lstring.o lundump.o tolua_gp.o \
+ liolib.o
+
+# toLua object files
+TOLUAOBJS := tolua.o tolualua.o liolib.o $(LUAOBJS)
+
+# Lua package files:
+LUAPKGS := \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+# The standard object files:
+OBJS := \
+ z-rand.o z-util.o z-form.o z-virt.o z-term.o z-sock.o \
+ variable.o tables.o plots.o util.o cave.o dungeon.o \
+ melee1.o melee2.o modules.o \
+ object1.o object2.o randart.o squeltch.o traps.o \
+ monster1.o monster2.o monster3.o ghost.o \
+ xtra1.o xtra2.o skills.o powers.o gods.o \
+ spells1.o spells2.o \
+ status.o files.o notes.o loadsave.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ help.o \
+ generate.o gen_maze.o gen_evol.o wild.o levels.o store.o bldg.o \
+ cmovie.o irc.o \
+ wizard2.o init2.o birth.o wizard1.o init1.o\
+ script.o lua_bind.o w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o \
+ main-ros.o
+
+
+
+# Defaults for norcy
+ifeq (norcroft, $(COMPILER))
+ # Tools:
+ CC := cc -DRISCOS -Ilua
+ LD := link
+ RM := remove
+ tolua := tolua
+
+ # Libraries:
+ LIBS := $(STUBSG) <DeskLib$$Dir>.o.DeskLib
+
+ CCFLAGS_BASIC := -apcs 3/32/fpe2/swst/fp/nofpr -c -Wan
+
+ SLUAOBJS := $(addprefix lua/,$(LUAOBJS))
+ STOLUAOBJS := $(addprefix lua/,$(TOLUAOBJS))
+
+ DLUAOBJS := $(addprefix lua.,$(LUAOBJS))
+ DTOLUAOBJS := $(addprefix lua.,$(TOLUAOBJS))
+
+endif
+
+# Defaults for gcc
+ifeq (gcc, $(COMPILER))
+ WARNINGS := -ansi -pedantic -Wall -Wno-unused -Wno-long-long -W -Wcast-qual
+
+ # Tools:
+ CC := gcc -mlibscl -c -DRISCOS -Ilua
+ LD := gcc -mlibscl
+ RM := remove
+ tolua := tolua
+
+ # Libraries:
+ LIBS := <DeskLib$$Dir>.o.DeskLib
+
+ WARNINGS := -ansi -pedantic -Wall -Wundef -Wpointer-arith \
+ -Wcast-align -Wwrite-strings -Wstrict-prototypes \
+ -Wmissing-prototypes -Wmissing-declarations -Wnested-externs \
+ -Winline -Wno-unused -Wno-long-long -W -Wcast-qual
+
+ CCFLAGS_BASIC := -O2 $(WARNINGS) -mthrowback -mpoke-function-name
+
+ SLUAOBJS := $(addprefix lua/,$(LUAOBJS))
+ STOLUAOBJS := $(addprefix lua/,$(TOLUAOBJS))
+
+ DLUAOBJS := $(SLUAOBJS)
+ DTOLUAOBJS := $(STOLUAOBJS)
+endif
+
+
+#
+# Rules to make the various targets
+#
+ALL_TARGETS := standard fullscreen
+
+$(ALL_TARGETS): tolua
+
+standard: CCFLAGS := $(CCFLAGS_BASIC)
+standard: $(LUAPKGS) $(OBJS) $(SLUAOBJS) $(LIBS)
+ $(LD) $(LDFLAGS) -o ^.!RunImage $(OBJS) $(DLUAOBJS) $(LIBS)
+
+fullscreen: CCFLAGS := -DFULLSCREEN_ONLY $(CCFLAGS_BASIC)
+fullscreen: $(LUAPKGS) $(OBJS) $(SLUAOBJS) $(LIBS)
+ $(LD) $(LDFLAGS) -o ^.!RunImageF $(OBJS) $(DLUAOBJS) $(LIBS)
+
+# tolua
+tolua: $(STOLUAOBJS)
+ $(LD) -o tolua $(DTOLUAOBJS) $(LDFLAGS) $(LIBS)
+
+# Lua packages
+w_mnster.c: monster.pkg $(tolua)
+ $(tolua) -n monster -o c.w_mnster monster/pkg
+
+w_player.c: player.pkg $(tolua)
+ $(tolua) -n player -o c.w_player player/pkg
+
+w_play_c.c: player_c.pkg $(tolua)
+ $(tolua) -n player_c -o c.w_play_c player_c/pkg
+
+w_z_pack.c: z_pack.pkg $(tolua)
+ $(tolua) -n z_pack -o c.w_z_pack z_pack/pkg
+
+w_obj.c: object.pkg $(tolua)
+ $(tolua) -n object -o c.w_obj object/pkg
+
+w_util.c: util.pkg $(tolua)
+ $(tolua) -n util -o c.w_util util/pkg
+
+w_spells.c: spells.pkg $(tolua)
+ $(tolua) -n spells -o c.w_spells spells/pkg
+
+w_quest.c: quest.pkg $(tolua)
+ $(tolua) -n quest -o c.w_quest quest/pkg
+
+w_dun.c: dungeon.pkg $(tolua)
+ $(tolua) -n dungeon -o c.w_dun dungeon/pkg
+
+
+# Suffix rules
+.SUFFIXES: .o .c
+
+# A basic rule
+.c.o:; $(CC) $(CCFLAGS) -o $@ $<
+
+# Dynamic dependencies:
+
diff --git a/src/makefile.sdliso b/src/makefile.sdliso
new file mode 100644
index 00000000..f9f653ba
--- /dev/null
+++ b/src/makefile.sdliso
@@ -0,0 +1,496 @@
+# File: Makefile
+
+# This is not a very "nice" Makefile, but it usually works.
+
+#
+# Note that you may have to make modifications below according
+# to your machine, available libraries, compilation options,
+# and your "visual module" of choice. This Makefile is intended
+# for use with Unix machines running X11, Curses, Ncurses, or Vt100,
+# or possibly for "Atari" or "Amiga" computers with "Curses" ports,
+# see below for more information.
+#
+# Note that "main-mac.c" and "main-crb.c", the visual modules for
+# the Macintosh, must be compiled in a special way, see elsewhere.
+#
+# Note that "main-win.c", the visual module for Windows,
+# must be compiled in a special way, see elsewhere.
+#
+# Note that "main-ibm.c" and "main-emx.c", the visual modules
+# for various types of IBM-PC computers, must be compiled with
+# special Makefiles, see elsewhere.
+#
+# Note that "main-lsl.c", the visual module for Linux-SVGALIB
+# must be compiled with "Makefile.lsl", see elsewhere.
+#
+# Note that "main-acn.c", the visual module for Risc Acorn,
+# must be compiled with "Makefile.acn", see elsewhere.
+#
+# Note that "Makefile.wat" is a slight variation on "Makefile.ibm",
+# which allow the use of "main-ibm.c" with special compiler
+# (might be out-of-date).
+#
+# If you are able to construct "main-xxx.c" and/or "Makefile.xxx"
+# files for a currently unsupported system, please send them to me
+# (rr9@angband.org) for inclusion in future versions.
+#
+
+##
+## 1. Installation locations and such
+##
+## LIBDIR, BINDIR and OWNER should be set appropriately for
+## multiuser installations.
+##
+## If you want to keep it private or don't have root privilege
+## required by "make install", set LIBDIR to ./lib/ or an absolute
+## path pointing to your lib directory, and don't run "make install".
+##
+## NOTE: If LIBDIR is set to ./lib/ , you have to cd to parent directory
+## of lib before you start the game.
+##
+
+# Where lib/ files goes
+#LIBDIR = /usr/lib/games/tome/
+# Sysadmins of commercial Unix and/or BSD might prefer this
+#LIBDIR = /usr/local/lib/tome/
+# If you like the old default, use this one
+LIBDIR = ./lib/
+# Another example: single user installation using absolute path
+#LIBDIR = /home/myloginname/lib/tome/
+
+
+# Where ToME binary goes
+BINDIR = /usr/local/games
+# Another common location
+#BINDIR = /usr/local/bin
+
+# The game will run suid to this user
+OWNER = games
+
+# Ignore this if you're not making a package
+DESTDIR=
+
+##
+## 2. Some "system" definitions
+##
+## No changes are needed to compile a version that will run on both
+## X11 and Curses, in debugging mode, with maximal warnings, on many
+## normal Unix machines of the Sun OS variety (non-solaris).
+##
+## To use an "alternative" definition, simply "modify" (or "replace")
+## the definition below with one that you like. For example, you can
+## change the compiler to "cc", or remove the "debugging" options, or
+## remove the X11 or Curses support, etc, as desired.
+##
+## See also "config.h" and "h-config.h" for important information.
+##
+## Some "examples" are given below, they can be used by simply
+## removing the FIRST column of "#" signs from the "block" of lines
+## you wish to use, and commenting out "standard" block below.
+##
+## This is not intended to be a "good" Makefile, just a "simple" one.
+##
+
+
+#
+# This is my compiler of choice, it seems to work most everywhere
+#
+CC = gcc
+
+# Profiling options
+# PROFILECOPTS = -pg
+# PROFILELDFLAGS = -pg
+
+#
+# Standard version (see main-x11.c and main-gcu.c)
+#
+# This version supports both "X11" and "curses" in a single executable.
+#
+# You may have to add various X11 include/library directories to the
+# "INCLUDES", if your machine places files in a weird location
+# (e.g. -I/usr/X11R6/include, as is almost always the case with
+# linux and *BSD). Since we have seen many linux users -- arguably
+# the largest unix population -- confused by this, and adding this
+# usually doesn't hurt, the default rule has been changed to search
+# for /usr/X11R6/include.
+#
+# You may be able to remove "-ltermcap" on some machines (ex: Solaris).
+#
+# You may have to replace "-lcurses" with "-lncurses" to use the
+# "new curses" library instead of the "old curses" library, and
+# you may have to add "-I/usr/include/ncurses" to the "INCLUDES",
+# and/or "-DUSE_NCURSES" to "DEFINES".
+#
+# See "main-gcu.c" and "config.h" for some optional "curses" defines,
+# including "USE_GETCH" and "USE_CURS_SET". Note that "config.h" will
+# attempt to "guess" at many of these flags based on your system.
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 \
+# -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY -DSUPPORT_GAMMA \
+# -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK
+#LIBS = -lX11 -L/usr/X11R6/lib
+
+##
+## Variation -- Only support "main-gtk.c" (not "main-gcu.c, main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `gtk-config --cflags`
+#DEFINES = -DUSE_GTK -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = `gtk-config --libs`
+
+#
+# Variation -- Only support "main-x11.c" (not "main-gcu.c")
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -L/usr/X11R6/lib
+
+
+##
+## Variation -- Only support "main-gcu.c" (not "main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+##
+## Variation -- Use "main-xaw.c" instead of "main-x11.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_XAW -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lXaw -lXmu -lXt -lX11 -lcurses -ltermcap -L/usr/X11R6/lib
+
+##
+## Variation -- Use "main-sdl.c" instead of "main-x11.c"
+##
+SDL_CONFIG ?= sdl-config
+COPTS = -Wall -O1 -pipe -g
+INCLUDES = `$(SDL_CONFIG) --cflags`
+DEFINES = -DUSE_SDL -DUSE_GCU \
+ -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY \
+ -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK -DSUPPORT_GAMMA \
+ -DUSE_ISO
+LIBS = `$(SDL_CONFIG) --libs` -lSDL_image -lSDL_ttf -lcurses
+
+##
+## Variation -- Use "main-cap.c" instead of "main-gcu.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_CAP \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -ltermcap
+
+
+##
+## Variation -- Only work on simple vt100 terminals
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_CAP -DUSE_HARDCODE
+
+
+##
+## Variation -- this might work for Linux 1.1.93 using ncurses-1.8.7.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+
+
+##
+## Variation -- this might work better than the suggestion above
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TPOSIX -DUSE_CURS_SET \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+#LDFLAGS = -s
+
+
+##
+## Variation -- compile for FreeBSD with ncurses
+## -- BSD curses gives you B&W display.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_NCURSES \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+
+
+##
+## Variation -- compile for Solaris
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSOLARIS \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lsocket -lcurses
+
+
+##
+## Variation -- compile for SGI Indigo runnig Irix
+## The SGI has hardware gamma correction.
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSGI \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS
+#LIBS = -lX11 -lcurses -ltermcap -lsun
+
+
+
+##
+## Variation -- compile for Dec ALPHA OSF/1 v2.0
+##
+#CC = cc
+##COPTS = -std -O -g3 -Olimit 4000
+#COPTS = -std -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lcurses -ltermcap -lrpcsvc
+
+
+##
+## Variation -- compile for Interactive Unix (ISC) systems
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DISC \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lcurses -lnsl_s -linet -lcposix
+
+
+##
+## Variation -- Support fat binaries under NEXTSTEP
+##
+#COPTS = -Wall -O1 -pipe -g -arch m68k -arch i386
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+### End of configurable section ###
+
+#
+# The "source" and "object" files.
+#
+
+BASESRCS = \
+ main-gtk.c main-gcu.c main-x11.c main-xaw.c main-dmy.c \
+ z-rand.c z-util.c z-form.c z-virt.c z-term.c z-sock.c \
+ variable.c tables.c plots.c util.c cave.c dungeon.c \
+ melee1.c melee2.c modules.c \
+ object1.c object2.c randart.c squeltch.c traps.c \
+ monster1.c monster2.c monster3.c ghost.c \
+ xtra1.c xtra2.c skills.c powers.c gods.c \
+ spells1.c spells2.c \
+ status.c files.c notes.c loadsave.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ help.c \
+ generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c \
+ cmovie.c irc.c \
+ wizard2.c init2.c birth.c wizard1.c init1.c \
+ iso/simgraph.c iso/simview.c iso/world_adaptor.c iso/world_view.c \
+ main-sdl-iso.c maim-iso.c
+
+
+BASEOBJS = \
+ main-gtk.o main-gcu.o main-x11.o main-xaw.o main-dmy.o \
+ z-rand.o z-util.o z-form.o z-virt.o z-term.o z-sock.o \
+ variable.o tables.o plots.o util.o cave.o dungeon.o \
+ melee1.o melee2.o modules.o \
+ object1.o object2.o randart.o squeltch.o traps.o \
+ monster1.o monster2.o monster3.o ghost.o \
+ xtra1.o xtra2.o skills.o powers.o gods.o \
+ spells1.o spells2.o \
+ status.o files.o notes.o loadsave.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ help.o \
+ generate.o gen_maze.o gen_evol.o wild.o levels.o store.o bldg.o \
+ cmovie.o irc.o \
+ wizard2.o init2.o birth.o wizard1.o init1.o \
+ iso/simgraph.o iso/simview.o iso/world_adaptor.o iso/world_view.o \
+ main-sdl-iso.o maim-iso.o
+
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS = $(COPTS) $(PROFILECOPTS) $(INCLUDES) $(DEFINES) -DDEFAULT_PATH=\"$(LIBDIR)\"
+
+
+#
+# Lua support
+#
+
+INCLUDES += -Ilua -I.
+DEFINES += -DUSE_LUA
+SRCS = $(LUASRCS) $(TOLUASRCS) $(BASESRCS)
+OBJS = $(LUAOBJS) $(TOLUAOBJS) $(BASEOBJS)
+
+# Force recreation of stub files when lua source files are updated
+# To be included in dependency rules
+TOLUADEP = $(TOLUASRCS) lua/tolua.c lua/tolualua.c
+
+#
+# IRC support
+#
+
+IRC_SERVER=irc.worldirc.org
+IRC_PORT=6667
+IRC_CHANNEL=\#tome
+
+DEFINES += \
+ -DIRC_SERVER=\"$(IRC_SERVER)\" \
+ -DIRC_PORT=\"$(IRC_PORT)\" \
+ -DIRC_CHANNEL=\"$(IRC_CHANNEL)\"
+
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+
+TOLUA = ./tolua
+
+default: $(TOLUA) $(TARGET)
+ @echo "*** Note: In order to use the install rule, which now actually"
+ @echo "*** handles the installation of the library dir, you need to edit"
+ @echo "*** this makefile, going to the top and making sure LIBDIR suits"
+ @echo "*** your desired install dir properly. The LIBRARY_DIR you used"
+ @echo "*** to set in config.h is now ignored and obsolete with respect"
+ @echo "*** to this makefile. Note that if you edit this makefile, you may"
+ @echo "*** need to recompile so all the files that reference those defines"
+ @echo "*** notice the changes."
+
+$(TARGET): $(OBJS)
+ $(CC) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+#
+# An install rule.
+#
+mini_install: default
+ cp -f $(TARGET) ..
+
+install: default
+ [ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR)
+ [ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+ cp -r ../lib/* $(DESTDIR)$(LIBDIR)
+ chown -R $(OWNER) $(DESTDIR)$(LIBDIR)
+ cp -f $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chown $(OWNER) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chmod 4755 $(DESTDIR)$(BINDIR)/$(TARGET)
+
+# old-install: $(TARGET)
+# cp $(TARGET) ..
+
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak *.o lua/*.o
+
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Quests
+#
+plots.o: q_rand.c q_main.c q_one.c q_ultrag.c q_ultrae.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_shroom.c q_thrain.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+#
+# Lua library compilation rules
+#
+
+w_mnster.c: monster.pkg $(TOLUADEP)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUADEP)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUADEP)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUADEP)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUADEP)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUADEP)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUADEP)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUADEP)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.std b/src/makefile.std
new file mode 100644
index 00000000..c9247df8
--- /dev/null
+++ b/src/makefile.std
@@ -0,0 +1,566 @@
+# File: Makefile
+
+# This is not a very "nice" Makefile, but it usually works.
+
+#
+# Note that you may have to make modifications below according
+# to your machine, available libraries, compilation options,
+# and your "visual module" of choice. This Makefile is intended
+# for use with Unix machines running X11, Curses, Ncurses, or Vt100,
+# or possibly for "Atari" or "Amiga" computers with "Curses" ports,
+# see below for more information.
+#
+# Note that "main-mac.c" and "main-crb.c", the visual modules for
+# the Macintosh, must be compiled in a special way, see elsewhere.
+#
+# Note that "main-win.c", the visual module for Windows,
+# must be compiled in a special way, see elsewhere.
+#
+# Note that "main-ibm.c" and "main-emx.c", the visual modules
+# for various types of IBM-PC computers, must be compiled with
+# special Makefiles, see elsewhere.
+#
+# Note that "main-lsl.c", the visual module for Linux-SVGALIB
+# must be compiled with "Makefile.lsl", see elsewhere.
+#
+# Note that "main-acn.c", the visual module for Risc Acorn,
+# must be compiled with "Makefile.acn", see elsewhere.
+#
+# Note that "Makefile.wat" is a slight variation on "Makefile.ibm",
+# which allow the use of "main-ibm.c" with special compiler
+# (might be out-of-date).
+#
+# If you are able to construct "main-xxx.c" and/or "Makefile.xxx"
+# files for a currently unsupported system, please send them to me
+# (rr9@angband.org) for inclusion in future versions.
+#
+
+##
+## 1. Installation locations and such
+##
+## LIBDIR, BINDIR and OWNER should be set appropriately for
+## multiuser installations.
+##
+## If you want to keep it private or don't have root privilege
+## required by "make install", set LIBDIR to ./lib/ or an absolute
+## path pointing to your lib directory, and don't run "make install".
+##
+## NOTE: If LIBDIR is set to ./lib/ , you have to cd to parent directory
+## of lib before you start the game.
+##
+
+# Where lib/ files goes
+#LIBDIR = /usr/lib/games/tome/
+# Sysadmins of commercial Unix and/or BSD might prefer this
+#LIBDIR = /usr/local/lib/tome/
+# If you like the old default, use this one
+LIBDIR = ./lib/
+# Another example: single user installation using absolute path
+#LIBDIR = /home/myloginname/lib/tome/
+
+
+# Where ToME binary goes
+BINDIR = /usr/local/games
+# Another common location
+#BINDIR = /usr/local/bin
+
+# The game will run suid to this user
+OWNER = games
+
+# Ignore this if you're not making a package
+DESTDIR=
+
+##
+## 2. Some "system" definitions
+##
+## No changes are needed to compile a version that will run on both
+## X11 and Curses, in debugging mode, with maximal warnings, on many
+## normal Unix machines of the Sun OS variety (non-solaris).
+##
+## To use an "alternative" definition, simply "modify" (or "replace")
+## the definition below with one that you like. For example, you can
+## change the compiler to "cc", or remove the "debugging" options, or
+## remove the X11 or Curses support, etc, as desired.
+##
+## See also "config.h" and "h-config.h" for important information.
+##
+## Some "examples" are given below, they can be used by simply
+## removing the FIRST column of "#" signs from the "block" of lines
+## you wish to use, and commenting out "standard" block below.
+##
+## This is not intended to be a "good" Makefile, just a "simple" one.
+##
+
+
+#
+# This is my compiler of choice, it seems to work most everywhere
+#
+CC = gcc
+
+# Profiling options
+# PROFILECOPTS = -pg
+# PROFILELDFLAGS = -pg
+
+#
+# Standard version (see main-x11.c)
+#
+# This version supports "X11" only.
+#
+# You may have to add various X11 include/library directories to the
+# "INCLUDES", if your machine places files in a weird location
+# (e.g. -I/usr/X11R6/include, as is almost always the case with
+# linux and *BSD). Since we have seen many linux users -- arguably
+# the largest unix population -- confused by this, and adding this
+# usually doesn't hurt, the default rule has been changed to search
+# for /usr/X11R6/include.
+#
+# You may be able to remove "-ltermcap" on some machines (ex: Solaris).
+#
+# You may have to replace "-lcurses" with "-lncurses" to use the
+# "new curses" library instead of the "old curses" library, and
+# you may have to add "-I/usr/include/ncurses" to the "INCLUDES",
+# and/or "-DUSE_NCURSES" to "DEFINES".
+#
+# See "main-gcu.c" and "config.h" for some optional "curses" defines,
+# including "USE_GETCH" and "USE_CURS_SET". Note that "config.h" will
+# attempt to "guess" at many of these flags based on your system.
+#
+COPTS = -Wall -O1 -pipe -g
+INCLUDES = -I/usr/X11R6/include
+DEFINES = -DUSE_X11 \
+ -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY -DSUPPORT_GAMMA \
+ -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK
+LIBS = -L/usr/X11R6/lib -lX11
+
+##
+## Variation -- Only support "main-gtk2.c" (not "main-gcu.c, main-x11.c")
+## this variation uses pkg-config and gnu make
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = $(shell pkg-config --cflags-only-I gtk+-2.0)
+#DEFINES = -DUSE_GTK2 -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = $(shell pkg-config --libs gtk+-2.0)
+#GTK_SRC_FILE= main-gtk2.c
+#GTK_OBJ_FILE= main-gtk2.o
+
+##
+## Variation -- Only support "main-gtk2.c" (not "main-gcu.c, main-x11.c")
+## this variation uses pkg-config
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `pkg-config --cflags-only-I gtk+-2.0`
+#DEFINES = -DUSE_GTK2 -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = `pkg-config --libs gtk+-2.0`
+#GTK_SRC_FILE= main-gtk2.c
+#GTK_OBJ_FILE= main-gtk2.o
+
+##
+## Variation -- Only support "main-gtk.c" (not "main-gcu.c, main-x11.c")
+## this variation uses pkg-config
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `pkg-config --cflags-only-I gtk+-2.0`
+#DEFINES = -DUSE_GTK -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = `pkg-config --libs gtk+-2.0`
+#GTK_SRC_FILE= main-gtk.c
+#GTK_OBJ_FILE= main-gtk.o
+
+##
+## Variation -- Only support "main-gtk.c" (not "main-gcu.c, main-x11.c")
+## this variation uses gtk-config
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `gtk-config --cflags`
+#DEFINES = -DUSE_GTK -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = `gtk-config --libs`
+#GTK_SRC_FILE= main-gtk.c
+#GTK_OBJ_FILE= main-gtk.o
+
+##
+## Variation -- Both "main-x11.c" and "main-gcu.c" in a single executable.
+##
+## Your Unix system's curses library may be called "-lcurses" instead of
+## "-lncurses", and you may also need to add "-ltermlib" to the end of the
+## LIBS on some platforms.
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS \
+# -DUSE_TRANSPARENCY -DSUPPORT_GAMMA \
+# -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK
+#LIBS = -L/usr/X11R6/lib -lX11 -lncurses
+
+
+##
+## Variation -- Only support "main-gcu.c" (not "main-x11.c")
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+
+##
+## Variation -- Use "main-xaw.c" instead of "main-x11.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_XAW -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lXaw -lXmu -lXt -lX11 -lcurses -ltermcap -L/usr/X11R6/lib
+
+##
+## Variation -- Use "main-sdl.c" instead of "main-x11.c"
+##
+#SDL_CONFIG ?= sdl-config
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = `$(SDL_CONFIG) --cflags`
+#DEFINES = -DUSE_SDL -DUSE_GCU \
+# -DUSE_EGO_GRAPHICS -DUSE_TRANSPARENCY \
+# -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK -DSUPPORT_GAMMA
+#LIBS = `$(SDL_CONFIG) --libs` -lSDL_image -lSDL_ttf -lcurses
+
+##
+## Variation -- Use "main-cap.c" instead of "main-gcu.c"
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_CAP \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -ltermcap
+
+
+##
+## Variation -- Only work on simple vt100 terminals
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_CAP -DUSE_HARDCODE
+
+
+##
+## Variation -- this might work for Linux 1.1.93 using ncurses-1.8.7.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+
+
+##
+## Variation -- this might work better than the suggestion above
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer
+#INCLUDES = -I/usr/X11R6/include -I/usr/include/ncurses
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_TPOSIX -DUSE_CURS_SET \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+#LDFLAGS = -s
+
+
+##
+## Variation -- compile for FreeBSD with ncurses
+## -- BSD curses gives you B&W display.
+##
+#COPTS = -Wall -O2 -fomit-frame-pointer -m486
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DUSE_X11 -DUSE_GCU -DUSE_NCURSES \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lncurses -L/usr/X11R6/lib
+
+
+##
+## Variation -- compile for Solaris
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSOLARIS \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lsocket -lcurses
+
+
+##
+## Variation -- compile for SGI Indigo runnig Irix
+## The SGI has hardware gamma correction.
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DSGI \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS
+#LIBS = -lX11 -lcurses -ltermcap -lsun
+
+##
+## Variation -- compile for Dec ALPHA OSF/1 v2.0
+##
+#CC = cc
+##COPTS = -std -O -g3 -Olimit 4000
+#COPTS = -std -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lcurses -ltermcap -lrpcsvc
+
+
+##
+## Variation -- compile for Interactive Unix (ISC) systems
+##
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES =
+#DEFINES = -DUSE_X11 -DUSE_GCU -DISC \
+# -DUSE_TRANSPARENCY -DUSE_EGO_GRAPHICS -DSUPPORT_GAMMA
+#LIBS = -lX11 -lcurses -lnsl_s -linet -lcposix
+
+
+##
+## Variation -- Support fat binaries under NEXTSTEP
+##
+#COPTS = -Wall -O1 -pipe -g -arch m68k -arch i386
+#INCLUDES =
+#DEFINES = -DUSE_GCU
+#LIBS = -lcurses -ltermcap
+
+#
+# Variation -- compile for 64 bit machines where the size of long is 64 bits.
+#
+# On some machines it may be necessary to change the -L directory so that
+# it points at a library directory containing 64-bit libraries. For example,
+# on Suse 10.x you need to change it to: -L/usr/X11R6/lib64.
+#
+#COPTS = -Wall -O1 -pipe -g
+#INCLUDES = -I/usr/X11R6/include
+#DEFINES = -DLUA_NUM_TYPE=int -DUSE_X11 \
+# -DUSE_EGO_GRAPHICS -DUSE_TRANSPARAENCY -DSUPPORT_GAMMA \
+# -DUSE_PRECISE_CMOVIE -DUSE_UNIXSOCK
+#LIBS = -L/usr/X11R6/lib -LX11
+
+### End of configurable section ###
+
+#
+# The "source" and "object" files.
+#
+
+BASESRCS = \
+ $(GTK_SRC_FILE) main-gcu.c main-x11.c main-xaw.c main-sdl.c main-dmy.c \
+ z-rand.c z-util.c z-form.c z-virt.c z-term.c z-sock.c \
+ variable.c tables.c plots.c util.c cave.c dungeon.c \
+ melee1.c melee2.c modules.c \
+ object1.c object2.c randart.c squeltch.c traps.c \
+ monster1.c monster2.c monster3.c ghost.c \
+ xtra1.c xtra2.c skills.c powers.c gods.c \
+ spells1.c spells2.c \
+ status.c files.c notes.c loadsave.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ help.c \
+ generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c \
+ cmovie.c irc.c \
+ wizard2.c init2.c birth.c wizard1.c init1.c main.c
+
+BASEOBJS = \
+ $(GTK_OBJ_FILE) main-gcu.o main-x11.o main-xaw.o main-sdl.o main-dmy.o \
+ z-rand.o z-util.o z-form.o z-virt.o z-term.o z-sock.o \
+ variable.o tables.o plots.o util.o cave.o dungeon.o \
+ melee1.o melee2.o modules.o \
+ object1.o object2.o randart.o squeltch.o traps.o \
+ monster1.o monster2.o monster3.o ghost.o \
+ xtra1.o xtra2.o skills.o powers.o gods.o \
+ spells1.o spells2.o \
+ status.o files.o notes.o loadsave.o \
+ cmd1.o cmd2.o cmd3.o cmd4.o cmd5.o cmd6.o cmd7.o \
+ help.o \
+ generate.o gen_maze.o gen_evol.o wild.o levels.o store.o bldg.o \
+ cmovie.o irc.o \
+ wizard2.o init2.o birth.o wizard1.o init1.o main.o
+
+LUASRCS = \
+ script.c lua_bind.c \
+ w_util.c w_player.c w_z_pack.c w_obj.c w_mnster.c w_spells.c w_quest.c w_play_c.c w_dun.c
+
+TOLUASRCS = \
+ lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/lfunc.c lua/lgc.c \
+ lua/llex.c lua/lmem.c lua/lobject.c lua/lparser.c lua/lstate.c lua/lstring.c \
+ lua/ltable.c lua/ltests.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \
+ lua/lauxlib.c lua/lbaselib.c lua/ldblib.c lua/liolib.c lua/lstrlib.c \
+ lua/tolua_lb.c lua/tolua_rg.c lua/tolua_tt.c lua/tolua_tm.c lua/tolua_gp.c \
+ lua/tolua_eh.c lua/tolua_bd.c
+
+LUAOBJS = \
+ script.o lua_bind.o \
+ w_util.o w_player.o w_z_pack.o w_obj.o w_mnster.o w_spells.o w_quest.o w_play_c.o w_dun.o
+
+TOLUAOBJS = \
+ lua/lapi.o lua/lcode.o lua/ldebug.o lua/ldo.o lua/lfunc.o lua/lgc.o \
+ lua/llex.o lua/lmem.o lua/lobject.o lua/lparser.o lua/lstate.o lua/lstring.o \
+ lua/ltable.o lua/ltests.o lua/ltm.o lua/lundump.o lua/lvm.o lua/lzio.o \
+ lua/lauxlib.o lua/lbaselib.o lua/ldblib.o lua/liolib.o lua/lstrlib.o \
+ lua/tolua_lb.o lua/tolua_rg.o lua/tolua_tt.o lua/tolua_tm.o lua/tolua_gp.o \
+ lua/tolua_eh.o lua/tolua_bd.o
+
+#
+# Base sources and objects
+#
+
+SRCS = $(BASESRCS)
+OBJS = $(BASEOBJS)
+
+
+#
+# Compiler options
+#
+
+CFLAGS = $(COPTS) $(PROFILECOPTS) $(INCLUDES) $(DEFINES) -DDEFAULT_PATH=\"$(LIBDIR)\"
+
+
+#
+# Lua support
+#
+
+INCLUDES += -Ilua -I.
+DEFINES += -DUSE_LUA
+SRCS = $(LUASRCS) $(TOLUASRCS) $(BASESRCS)
+OBJS = $(LUAOBJS) $(TOLUAOBJS) $(BASEOBJS)
+
+# Force recreation of stub files when lua source files are updated
+# To be included in dependency rules
+TOLUADEP = $(TOLUA) $(TOLUASRCS) lua/tolua.c lua/tolualua.c
+
+#
+# IRC support
+#
+
+IRC_SERVER=irc.worldirc.org
+IRC_PORT=6667
+IRC_CHANNEL=\#tome
+
+DEFINES += \
+ -DIRC_SERVER=\"$(IRC_SERVER)\" \
+ -DIRC_PORT=\"$(IRC_PORT)\" \
+ -DIRC_CHANNEL=\"$(IRC_CHANNEL)\"
+
+# Build the binary. The new base target.
+#
+
+TARGET = tome
+
+TOLUA = ./tolua
+
+default: $(TOLUA) $(TARGET)
+ @echo "*** Note: In order to use the install rule, which now actually"
+ @echo "*** handles the installation of the library dir, you need to edit"
+ @echo "*** this makefile, going to the top and making sure LIBDIR suits"
+ @echo "*** your desired install dir properly. The LIBRARY_DIR you used"
+ @echo "*** to set in config.h is now ignored and obsolete with respect"
+ @echo "*** to this makefile. Note that if you edit this makefile, you may"
+ @echo "*** need to recompile so all the files that reference those defines"
+ @echo "*** notice the changes."
+
+$(TARGET): $(OBJS)
+ $(CC) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(TOLUA): $(TOLUAOBJS) lua/tolua.c lua/tolualua.c
+ $(CC) $(CFLAGS) $(PROFILELDFLAGS) $(LDFLAGS) -o $@ $(TOLUAOBJS) lua/tolua.c lua/tolualua.c $(LIBS)
+
+#
+# An install rule.
+#
+mini_install: default
+ cp -f $(TARGET) ..
+
+install: default
+ [ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR)
+ [ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+ cp -r ../lib/* $(DESTDIR)$(LIBDIR)
+ chown -R $(OWNER) $(DESTDIR)$(LIBDIR)
+ cp -f $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chown $(OWNER) $(DESTDIR)$(BINDIR)/$(TARGET)
+ chmod 4755 $(DESTDIR)$(BINDIR)/$(TARGET)
+
+# old-install: $(TARGET)
+# cp $(TARGET) ..
+
+
+#
+# Clean up old junk
+#
+
+clean:
+ rm -f *.bak *.o lua/*.o w_*.c $(TOLUA) $(TARGET)
+
+
+# Make a src dist
+TARGET_VERSION=`fgrep '[V]' ../changes.txt | sed 's/\[V\]-*\s*T.o.M.E \(.\).\(.\).\(.\) .*/\1\2\3/g'`
+DIST_TARGET_VERSION=../dist/tome-$(TARGET_VERSION)-src
+DIST_TARGET=../dist/tome-xxx-src
+dist: clean
+ mkdir -p $(DIST_TARGET)
+ cp -r ../lib $(DIST_TARGET)
+ cp -r ../src $(DIST_TARGET)
+ ../tome -c ../changes.txt $(DIST_TARGET)/changes.txt
+ cp ../changes.old $(DIST_TARGET)
+ cp ../credits.txt $(DIST_TARGET)
+ cp ../tome.ini $(DIST_TARGET)
+ cp ../angdos.cfg $(DIST_TARGET)
+ rm -f $(DIST_TARGET)/src/tome $(DIST_TARGET)/src/tolua
+ find $(DIST_TARGET) -name '*~' -exec rm {} \;
+ find $(DIST_TARGET)/lib/data/ -name '*.raw' -exec rm {} \;
+ find $(DIST_TARGET)/lib/xtra/graf/ -name '*.gif' -exec rm {} \;
+ find $(DIST_TARGET)/lib/xtra/font/ -name '*.hex' -exec rm {} \;
+ rm -rf `find $(DIST_TARGET) -name 'CVS'`
+ rm -rf `find $(DIST_TARGET)/lib/mods/ -mindepth 1 -maxdepth 1 -type d`
+ sed -i 's/(CVS)//' $(DIST_TARGET)/src/defines.h
+ mv $(DIST_TARGET) $(DIST_TARGET_VERSION)
+ cd ../dist; tar -cvjf tome-$(TARGET_VERSION)-src.tar.bz2 tome-$(TARGET_VERSION)-src
+
+#
+# Generate dependancies automatically
+#
+
+depend:
+ makedepend $(INCLUDES) $(DEFINES) -D__MAKEDEPEND__ $(SRCS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $*.c
+
+
+#
+# Quests
+#
+plots.o: q_rand.c q_main.c q_one.c q_ultrag.c q_ultrae.c \
+ q_thief.c q_hobbit.c q_nazgul.c q_troll.c q_wight.c \
+ q_spider.c q_poison.c \
+ q_eol.c q_nirna.c q_invas.c \
+ q_betwen.c \
+ q_narsil.c q_shroom.c q_thrain.c q_wolves.c q_dragons.c q_haunted.c q_evil.c
+
+#
+# Lua library compilation rules
+#
+
+w_mnster.c: monster.pkg $(TOLUADEP)
+ $(TOLUA) -n monster -o w_mnster.c monster.pkg
+
+w_player.c: player.pkg $(TOLUADEP)
+ $(TOLUA) -n player -o w_player.c player.pkg
+
+w_play_c.c: player_c.pkg $(TOLUADEP)
+ $(TOLUA) -n player_c -o w_play_c.c player_c.pkg
+
+w_z_pack.c: z_pack.pkg $(TOLUADEP)
+ $(TOLUA) -n z_pack -o w_z_pack.c z_pack.pkg
+
+w_obj.c: object.pkg $(TOLUADEP)
+ $(TOLUA) -n object -o w_obj.c object.pkg
+
+w_util.c: util.pkg $(TOLUADEP)
+ $(TOLUA) -n util -o w_util.c util.pkg
+
+w_spells.c: spells.pkg $(TOLUADEP)
+ $(TOLUA) -n spells -o w_spells.c spells.pkg
+
+w_quest.c: quest.pkg $(TOLUADEP)
+ $(TOLUA) -n quest -o w_quest.c quest.pkg
+
+w_dun.c: dungeon.pkg $(TOLUA)
+ $(TOLUA) -n dungeon -o w_dun.c dungeon.pkg
+
+# DO NOT DELETE THIS LINE - make depend depends on it.
diff --git a/src/makefile.wat b/src/makefile.wat
new file mode 100644
index 00000000..d4d0dd09
--- /dev/null
+++ b/src/makefile.wat
@@ -0,0 +1,71 @@
+# File: Makefile.wat
+
+# Purpose: Makefile support for "main-ibm.c" for Watcom C/C++
+
+# From: akemi@netcom.com (David Boeren)
+# Extra program targets by: mrmarcel@eos.ncsu.edu (Mike Marcelais)
+
+CC = wcc386
+
+CFLAGS = /mf /3r /3 /wx /s /oneatx /DUSE_IBM /DUSE_WAT
+# CFLAGS = /mf /3r /3 /wx /oaeilmnrt /DUSE_IBM /DUSE_WAT
+
+OBJS = &
+ z-util.obj z-virt.obj z-form.obj z-rand.obj z-term.obj z-sock.obj &
+ variable.obj tables.obj util.obj cave.obj cmovie.obj &
+ object1.obj object2.obj traps.obj monster1.obj monster2.obj monster3.obj &
+ xtra1.obj xtra2.obj spells1.obj spells2.obj melee1.obj melee2.obj &
+ loadsave.obj files.obj ghost.obj powers.obj &
+ cmd1.obj cmd2.obj cmd3.obj cmd4.obj cmd5.obj cmd6.obj cmd7.obj &
+ store.obj birth.obj wizard1.obj wizard2.obj &
+ status.obj randart.obj &
+ generate.obj gen_maze.obj gen_evol.obj dungeon.obj init1.obj init2.obj plots.obj &
+ bldg.obj levels.obj notes.obj squeltch.obj wild.obj help.obj &
+ main-ibm.obj main.obj
+
+all: angband.exe gredit.exe makepref.exe
+
+# Use whichever of these two you wish...
+angband.exe: $(OBJS) angband.lnk
+ wlink system dos4g @angband.lnk
+# wlink system pmodew @angband.lnk
+
+# Use whichever of these two you wish...
+gredit.exe: gredit.obj gredit.lnk
+ wlink system dos4g @gredit.lnk
+# wlink system pmodew @gredit.lnk
+
+# Use whichever of these two you wish...
+makepref.exe: makepref.obj makepref.lnk
+ wlink system dos4g @makepref.lnk
+# wlink system pmodew @makepref.lnk
+
+angband.lnk:
+ %create angband.lnk
+# @%append angband.lnk debug all
+ @%append angband.lnk OPTION CASEEXACT
+ @%append angband.lnk OPTION STACK=16k
+ @%append angband.lnk name angband
+ @for %i in ($(OBJS)) do @%append angband.lnk file %i
+
+makepref.lnk:
+ %create makepref.lnk
+# @%append makepref.lnk debug all
+ @%append makepref.lnk OPTION CASEEXACT
+ @%append makepref.lnk OPTION STACK=16k
+ @%append makepref.lnk name makepref
+ @%append makepref.lnk file makepref.obj
+
+gredit.lnk:
+ %create gredit.lnk
+# @%append gredit.lnk debug all
+ @%append gredit.lnk OPTION CASEEXACT
+ @%append gredit.lnk OPTION STACK=16k
+ @%append gredit.lnk name gredit
+ @%append gredit.lnk file gredit.obj
+
+.c.obj:
+ $(CC) $(CFLAGS) $[*.c
+
+clean:
+ del *.err *.obj *.exe *.lnk
diff --git a/src/makefile.win b/src/makefile.win
new file mode 100644
index 00000000..c5ee9997
--- /dev/null
+++ b/src/makefile.win
@@ -0,0 +1,206 @@
+# File: Makefile.win
+
+# For Borland C++ 4.52 Win16 and Win32
+
+.autodepend
+
+# change this to suit your taste
+
+CC = c:\apps\bc45\bin\bcc.exe +bccw16.cfg
+CC32 = c:\apps\bc45\bin\bcc32.exe +bccw32.cfg
+LINK = c:\apps\bc45\bin\tlink.exe
+LINK32 = c:\apps\bc45\bin\tlink32.exe
+RC = c:\apps\bc45\bin\brc.exe
+RC32 = c:\apps\bc45\bin\brc32.exe
+LIBDIR = c:\apps\bc45\lib
+INCLDIR = c:\apps\bc45\include;c:\angband\ext-win\src
+
+# shouldn't need to change anything below
+
+SRCS = \
+ z-util.c z-virt.c z-form.c z-rand.c z-term.c z-sock.c \
+ variable.c tables.c util.c cave.c cmovie.c modules.c \
+ object1.c object2.c traps.c monster1.c monster2.c monster3.c \
+ xtra1.c xtra2.c spells1.c spells2.c \
+ melee1.c melee2.c files.c plots.c help.c \
+ cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ status.c randart.c gods.c \
+ store.c birth.c loadsave.c notes.c squeltch.c \
+ wizard1.c wizard2.c levels.c ghost.c \
+ generate.c gen_maze.c gen_evol.c dungeon.c init1.c init2.c \
+ main-win.c readdib.c \
+ bldg.c wild.c powers.c
+
+OBJS = \
+ z-util.obj z-virt.obj z-form.obj z-rand.obj z-term.obj z-sock.obj \
+ variable.obj tables.obj util.obj cave.obj cmovie.obj modules.obj \
+ object1.obj object2.obj traps.obj monster1.obj monster2.obj monster3.obj \
+ xtra1.obj xtra2.obj spells1.obj spells2.obj \
+ melee1.obj melee2.obj files.obj plots.obj help.obj \
+ cmd1.obj cmd2.obj cmd3.obj cmd4.obj cmd5.obj cmd6.obj cmd7.obj \
+ status.obj randart.obj gods.obj \
+ store.obj birth.obj loadsave.obj notes.obj squeltch.obj \
+ wizard1.obj wizard2.obj levels.obj ghost.obj \
+ generate.obj gen_maze.obj gen_evol.obj dungeon.obj init1.obj init2.obj \
+ main-win.obj readdib.obj \
+ bldg.obj wild.obj powers.obj
+
+OBJS32 = \
+ z-util.o32 z-virt.o32 z-form.o32 z-rand.o32 z-term.o32 z-sock.o32 \
+ variable.o32 tables.o32 util.o32 cave.o32 cmovie.o32 modules.o32 \
+ object1.o32 object2.o32 traps.o32 monster1.o32 monster2.o32 monster3.o32 \
+ xtra1.o32 xtra2.o32 spells1.o32 spells2.o32 \
+ melee1.o32 melee2.o32 files.o32 plots.o32 help.o32 \
+ cmd1.o32 cmd2.o32 cmd3.o32 cmd4.o32 cmd5.o32 cmd6.o32 cmd7.o32 \
+ status.o32 randart.o32 gods.o32 \
+ store.o32 birth.o32 loadsave.o32 notes.o32 squeltch.o32 \
+ wizard1.o32 wizard2.o32 levels.o32 ghost.o32 \
+ generate.o32 gen_maze.o32 gen_evol.o32 dungeon.o32 init1.o32 init2.o32 \
+ main-win.o32 readdib.o32 \
+ bldg.o32 wild.o32
+
+default: bccw16.cfg angband cleanobj bccw32.cfg angband32 cleanobj32
+
+clean:
+ del *.obj
+ del ..\*.map
+ del ..\angband.exe
+
+cleanobj:
+ del *.obj
+ del bccw16.cfg
+
+cleanobj32:
+ del *.o32
+ del bccw32.cfg
+
+bccw16.cfg: makefile.win
+ if not exist ..\angband.ini copy ..\ext-win\src\angband.ini ..
+ copy &&|
+-I$(INCLDIR)
+-L$(LIBDIR)
+-ml
+-WE
+-x-
+-K
+-y-
+-v-
+-N-
+-f-
+-3
+-dc
+-Ff=8
+-r
+-b-
+-p-
+-Os
+-Oa
+-OW
+-O
+-d
+-Ob
+-Oe
+-Og
+-Ol
+-Om
+-Ot
+-Op
+-Ov
+-k-
+-Z
+-w-
+-X
+-D__STDC__=1
+| bccw16.cfg
+
+bccw32.cfg: makefile.win
+ copy &&|
+-I$(INCLDIR)
+-L$(LIBDIR)
+-WE
+-x-
+-K
+-y-
+-v-
+-N-
+-f-
+-5
+-r
+-b-
+-p-
+-Os
+-OW
+-O
+-d
+-Oa
+-Ob
+-Oe
+-Og
+-Ol
+-Om
+-Ot
+-Op
+-Ov
+-k-
+-Z
+-w-
+-X
+-D__STDC__=1
+| bccw32.cfg
+
+.c.obj:
+ $(CC) -c $<
+
+generate.obj: generate.c
+ $(CC) -c generate.c
+
+readdib.obj: ..\ext-win\src\readdib.c ..\ext-win\src\readdib.h
+ $(CC) -c ..\ext-win\src\readdib.c
+
+angband: $(OBJS) ..\ext-win\src\angband.rc
+ $(LINK) /L$(LIBDIR) @&&|
+-Twe -P -d -x -Gn -Oc -yx4096 +
+c0wl+
+z-util z-virt z-form z-rand z-term z-sock +
+variable tables util cave cmovie modules +
+object1 object2 traps monster1 monster2 monster3 +
+xtra1 xtra2 spells1 spells2 +
+melee1 melee2 files plots help +
+cmd1 cmd2 cmd3 cmd4 cmd5 cmd6 cmd7 +
+status randart gods +
+store birth loadsave notes squeltch +
+wizard1 wizard2 levels ghost +
+generate gen_maze gen_evol dungeon init1 init2 +
+main-win readdib bldg wild powers +
+..\angband.exe,..\angband.map,import cwl,..\ext-win\src\angband.def
+|
+ $(RC) -31 ..\ext-win\src\angband.rc ..\angband.exe
+
+.c.o32:
+ $(CC32) -c -o$@ $<
+
+# bug in BC4.5 optimizer scrambles town level
+generate.o32: generate.c
+ $(CC32) -c -ogenerate.o32 -O- generate.c
+
+readdib.o32: ..\ext-win\src\readdib.c ..\ext-win\src\readdib.h
+ $(CC32) -c -oreaddib.o32 ..\ext-win\src\readdib.c
+
+angband32: $(OBJS32) ..\ext-win\src\angband.rc
+ $(LINK32) /L$(LIBDIR) @&&|
+-aa -B:0x400000 -S:0x100000 -P -x +
+c0w32 +
+z-util.o32 z-virt.o32 z-form.o32 z-rand.o32 z-term.o32 z-sock.o32 +
+variable.o32 tables.o32 util.o32 cave.o32 cmovie.o32 modules.o32 +
+object1.o32 object2.o32 traps.o32 monster1.o32 monster2.o32 monster3.o32 +
+xtra1.o32 xtra2.o32 spells1.o32 spells2.o32 +
+melee1.o32 melee2.o32 files.o32 plots.o32 help.o32 +
+cmd1.o32 cmd2.o32 cmd3.o32 cmd4.o32 cmd5.o32 cmd6.o32 cmd7.o32 +
+status.o32 randart.o32 gods.o32 +
+store.o32 birth.o32 loadsave.o32 notes.o32 squeltch.o32 +
+wizard1.o32 wizard2.o32 levels.o32 ghost.o32 +
+generate.o32 gen_maze.o32 gen_evol.o32 dungeon.o32 init1.o32 init2.o32 +
+main-win.o32 readdib.o32 bldg.o32 wild.o32 powers.o32+
+..\angband32.exe,..\angband.map,import32 cw32,..\ext-win\src\angband.def
+|
+ $(RC32) -w32 ..\ext-win\src\angband.rc ..\angband32.exe
diff --git a/src/melee1.c b/src/melee1.c
new file mode 100644
index 00000000..9e34c110
--- /dev/null
+++ b/src/melee1.c
@@ -0,0 +1,3108 @@
+/* File: melee1.c */
+
+/* Purpose: Monster attacks */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+/*
+ * Critical blow. All hits that do 95% of total possible damage,
+ * and which also do at least 20 damage, or, sometimes, N damage.
+ * This is used only to determine "cuts" and "stuns".
+ */
+static int monster_critical(int dice, int sides, int dam)
+{
+ int max = 0;
+ int total = dice * sides;
+
+ /* Must do at least 95% of perfect */
+ if (dam < total * 19 / 20) return (0);
+
+ /* Weak blows rarely work */
+ if ((dam < 20) && (rand_int(100) >= dam)) return (0);
+
+ /* Perfect damage */
+ if (dam == total) max++;
+
+ /* Super-charge */
+ if (dam >= 20)
+ {
+ while (rand_int(100) < 2) max++;
+ }
+
+ /* Critical damage */
+ if (dam > 45) return (6 + max);
+ if (dam > 33) return (5 + max);
+ if (dam > 25) return (4 + max);
+ if (dam > 18) return (3 + max);
+ if (dam > 11) return (2 + max);
+ return (1 + max);
+}
+
+
+
+
+
+/*
+ * Determine if a monster attack against the player succeeds.
+ * Always miss 5% of the time, Always hit 5% of the time.
+ * Otherwise, match monster power against player armor.
+ */
+static int check_hit(int power, int level)
+{
+ int i, k, ac;
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Always miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Calculate the "attack quality" */
+ i = (power + (level * 3));
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Power and Level compete against Armor */
+ if ((i > 0) && (randint(i - luck( -10, 10)) > ((ac * 3) / 4))) return (TRUE);
+
+ /* Assume miss */
+ return (FALSE);
+}
+
+
+
+/*
+ * Hack -- possible "insult" messages
+ */
+static cptr desc_insult[] =
+{
+ "insults you!",
+ "insults your mother!",
+ "jumps around you!",
+ "humiliates you!",
+ "defiles you!",
+ "dances around you!",
+ "makes obnoxious gestures!",
+ "pokes you!!!"
+};
+
+
+
+/*
+ * Hack -- possible "insult" messages
+ */
+static cptr desc_moan[] =
+{
+ "seems sad about something.",
+ "asks if you have seen his dogs.",
+ "tells you to get off his land.",
+ "mumbles something about mushrooms.",
+
+ /* Mathilde's sentence */
+ "giggles at you.",
+ "asks you if you want to giggle with her.",
+ "says she is always happy."
+};
+
+/*
+ * Attack the player via physical attacks.
+ */
+bool carried_make_attack_normal(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int ap_cnt;
+
+ int k, tmp, ac, rlev;
+ int do_cut, do_stun;
+
+ char ddesc[80] = "your symbiote";
+ cptr sym_name = symbiote_name(TRUE);
+
+ bool blinked;
+ bool touched = FALSE, alive = TRUE;
+ bool explode = FALSE;
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE);
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Extract the effective monster level */
+ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool visible = FALSE;
+ bool obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+#if 0
+
+ /* Extract visibility from carrying lite */
+ if (r_ptr->flags9 & RF9_HAS_LITE) visible = TRUE;
+
+#endif /* 0 */
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ power = 60;
+ break;
+ case RBE_POISON:
+ power = 5;
+ break;
+ case RBE_UN_BONUS:
+ power = 20;
+ break;
+ case RBE_UN_POWER:
+ power = 15;
+ break;
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ case RBE_ABOMINATION:
+ power = 30;
+ break;
+ }
+
+
+ /* Monster hits player */
+ if (!effect || check_hit(power, rlev))
+ {
+ /* Always disturbing */
+ disturb(1, 0);
+
+ /* Hack -- Apply "protection from evil" */
+ if ((p_ptr->protevil > 0) &&
+ (r_ptr->flags3 & (RF3_EVIL)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Evil-ness */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Message */
+ msg_format("%s is repelled.", sym_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Hack -- Apply "protection from good" */
+ if ((p_ptr->protgood > 0) &&
+ (r_ptr->flags3 & (RF3_GOOD)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Good-ness */
+ r_ptr->r_flags3 |= (RF3_GOOD);
+
+ /* Message */
+ msg_format("%s is repelled.", sym_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Assume no cut or stun */
+ do_cut = do_stun = 0;
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits you.";
+ do_cut = do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches you.";
+ touched = TRUE;
+ sound(SOUND_TOUCH);
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws you.";
+ touched = TRUE;
+ do_cut = 1;
+ sound(SOUND_CLAW);
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites you.";
+ do_cut = 1;
+ touched = TRUE;
+ sound(SOUND_BITE);
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings you.";
+ touched = TRUE;
+ sound(SOUND_STING);
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's you.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs you.";
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges you.";
+ touched = TRUE;
+ sound(SOUND_BUY); /* Note! This is "charges", not "charges at". */
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on you.";
+ touched = TRUE;
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ explode = TRUE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at you.";
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at you.";
+ sound(SOUND_WAIL);
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at you.";
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs you for money.";
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = desc_insult[rand_int(8)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = desc_moan[rand_int(4)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ if (randint(3) == 1)
+ act = "sings 'We are a happy family.'";
+ else
+ act = "sings 'I love you, you love me.'";
+ sound(SOUND_SHOW);
+ break;
+ }
+ }
+
+ /* Message */
+ if (act) msg_format("%s %s", sym_name, act);
+
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ /* Hack -- Assume obvious */
+ obvious = TRUE;
+
+ /* Hack -- No damage */
+ damage = 0;
+
+ break;
+ }
+
+ case RBE_HURT:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Player armor reduces total damage */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ABOMINATION:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Morph, but let mimicry skill have a chance to stop this */
+ if (magik(60 - get_skill(SKILL_MIMICRY)))
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers twisting your body!");
+
+ set_mimic(damage, resolve_mimic_name("Abomination"), 50);
+ }
+ else
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers trying to twisting your body, but they fail.");
+ }
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ obvious = TRUE;
+
+ take_sanity_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Allow complete resist */
+ if (!p_ptr->resist_disen)
+ {
+ /* Apply disenchantment */
+ if (apply_disenchant(0)) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_UN_POWER:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_GOLD:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_LITE:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered in acid!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ acid_dam(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are struck by electricity!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ elec_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are enveloped in flames!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ fire_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered with frost!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ cold_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "blind" */
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + 10 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "confused" */
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ /* Hack -- Prevent perma-paralysis via damage */
+ if (p_ptr->paralyzed && (damage < 1)) damage = 1;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "paralyzed" */
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_paralyzed(p_ptr->paralyzed + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_INT:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_WIS:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_DEX:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CON:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CHR:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_ALL:
+ {
+ /* Damage (physical) */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Damage (stats) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Reduce damage based on the player armor class */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Radius 8 earthquake centered at the monster */
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 8);
+ }
+
+ break;
+ }
+
+ case RBE_EXP_10:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 95))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(10, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_20:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 90))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(20, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_40:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(40, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_80:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(80, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_DISEASE:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Damage CON (10% chance)*/
+ if (randint(100) < 11)
+ {
+ /* 1% chance for perm. damage */
+ bool perm = (randint(10) == 1);
+ if (dec_stat(A_CON, randint(10), perm)) obvious = TRUE;
+ }
+
+ break;
+ }
+ case RBE_PARASITE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ if (!p_ptr->parasite) set_parasite(damage, r_idx);
+
+ break;
+ }
+ case RBE_HALLU:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "image" */
+ if (!p_ptr->resist_chaos)
+ {
+ if (set_image(p_ptr->image + 3 + randint(rlev / 2)))
+ {
+ obvious = TRUE;
+ }
+ }
+ break;
+
+ }
+ case RBE_TIME:
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ int stat = rand_int(6);
+
+ switch (stat)
+ {
+ case A_STR:
+ act = "strong";
+ break;
+ case A_INT:
+ act = "bright";
+ break;
+ case A_WIS:
+ act = "wise";
+ break;
+ case A_DEX:
+ act = "agile";
+ break;
+ case A_CON:
+ act = "hardy";
+ break;
+ case A_CHR:
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[stat] = (p_ptr->stat_cur[stat] * 3) / 4;
+ if (p_ptr->stat_cur[stat] < 3) p_ptr->stat_cur[stat] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+ }
+
+
+ /* Hack -- only one of cut or stun */
+ if (do_cut && do_stun)
+ {
+ /* Cancel cut */
+ if (rand_int(100) < 50)
+ {
+ do_cut = 0;
+ }
+
+ /* Cancel stun */
+ else
+ {
+ do_stun = 0;
+ }
+ }
+
+ /* Handle cut */
+ if (do_cut)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(5) + 5;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(50) + 50;
+ break;
+ case 5:
+ k = randint(100) + 100;
+ break;
+ case 6:
+ k = 300;
+ break;
+ default:
+ k = 500;
+ break;
+ }
+
+ /* Apply the cut */
+ if (k) (void)set_cut(p_ptr->cut + k);
+ }
+
+ /* Handle stun */
+ if (do_stun)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(10) + 10;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(30) + 30;
+ break;
+ case 5:
+ k = randint(40) + 40;
+ break;
+ case 6:
+ k = 100;
+ break;
+ default:
+ k = 200;
+ break;
+ }
+
+ /* Apply the stun */
+ if (k) (void)set_stun(p_ptr->stun + k);
+ }
+
+ if (touched)
+ {
+ if (p_ptr->sh_fire && alive)
+ {
+ r_ptr->r_flags3 |= RF3_IM_FIRE;
+ }
+
+ if (p_ptr->sh_elec && alive)
+ {
+ r_ptr->r_flags3 |= RF3_IM_ELEC;
+ }
+ touched = FALSE;
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+
+ /* Disturbing */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("%s misses you.", sym_name);
+
+ break;
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+ /* Assume we attacked */
+ return (TRUE);
+}
+
+/*
+ * Give unprotected player the Black Breath with a 1 in (chance) probability
+ *
+ */
+void black_breath_attack(int chance)
+{
+ if (!p_ptr->protundead && randint(chance) == 1)
+ {
+ msg_print("Your foe calls upon your soul!");
+ msg_print("You feel the Black Breath slowly draining you of life...");
+ p_ptr->black_breath = TRUE;
+ }
+}
+
+/*
+ * Attack the player via physical attacks.
+ */
+bool make_attack_normal(int m_idx, byte divis)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int ap_cnt;
+
+ int i, j, k, tmp, ac, rlev;
+ int do_cut, do_stun, do_vampire;
+
+ s32b gold;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char m_name[80];
+
+ char ddesc[80];
+
+ bool blinked;
+ bool touched = FALSE, fear = FALSE, alive = TRUE;
+ bool explode = FALSE;
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE);
+
+ /* ...nor if friendly */
+ if (is_friend(m_ptr) >= 0)
+ {
+ if (p_ptr->control == m_idx) swap_position(m_ptr->fy, m_ptr->fx);
+ return FALSE;
+ }
+
+ /* Cannot attack the player if mortal and player fated to never die by the ... */
+ if ((r_ptr->flags7 & RF7_MORTAL) && (p_ptr->no_mortal)) return (FALSE);
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Extract the effective monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the "died from" information (i.e. "a kobold") */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool visible = FALSE;
+ bool obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = m_ptr->blow[ap_cnt].effect;
+ int method = m_ptr->blow[ap_cnt].method;
+ int d_dice = m_ptr->blow[ap_cnt].d_dice;
+ int d_side = m_ptr->blow[ap_cnt].d_side;
+
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Extract visibility (before blink) */
+ if (m_ptr->ml) visible = TRUE;
+
+#if 0
+
+ /* Extract visibility from carrying lite */
+ if (r_ptr->flags9 & RF9_HAS_LITE) visible = TRUE;
+
+#endif
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ power = 60;
+ break;
+ case RBE_POISON:
+ power = 5;
+ break;
+ case RBE_UN_BONUS:
+ power = 20;
+ break;
+ case RBE_UN_POWER:
+ power = 15;
+ break;
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ case RBE_ABOMINATION:
+ power = 20;
+ break;
+ }
+
+
+ /* Monster hits player */
+ if (!effect || check_hit(power, rlev))
+ {
+ int chance = p_ptr->dodge_chance - ((rlev * 5) / 6);
+
+ /* Always disturbing */
+ disturb(1, 0);
+
+ if ((chance > 0) && magik(chance))
+ {
+ msg_format("You dodge %s attack!", m_name);
+ continue;
+ }
+
+ /* Eru can help you */
+ PRAY_GOD(GOD_ERU)
+ {
+ s32b chance = p_ptr->grace;
+
+ if (chance > 50000) chance = 50000;
+ chance -= rlev * 300;
+
+ if ((randint(100000) < chance) && (r_ptr->flags3 & (RF3_EVIL)))
+ {
+ /* Remember the Evil-ness */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Message */
+ msg_format("The hand of Eru Iluvatar stops %s blow.", m_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+ }
+
+ /* Hack -- Apply "protection from evil" */
+ if ((p_ptr->protevil > 0) &&
+ (r_ptr->flags3 & (RF3_EVIL)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Evil-ness */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+
+ /* Message */
+ msg_format("%^s is repelled.", m_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Hack -- Apply "protection from good" */
+ if ((p_ptr->protgood > 0) &&
+ (r_ptr->flags3 & (RF3_GOOD)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Good-ness */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_GOOD);
+ }
+
+ /* Message */
+ msg_format("%^s is repelled.", m_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Assume no cut or stun */
+ do_cut = do_stun = do_vampire = 0;
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits you.";
+ do_cut = do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches you.";
+ touched = TRUE;
+ sound(SOUND_TOUCH);
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws you.";
+ touched = TRUE;
+ do_cut = 1;
+ sound(SOUND_CLAW);
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites you.";
+ do_cut = 1;
+ if (magik(5) && (strstr(r_name + r_ptr->name, "Vampire") || strstr(r_name + r_ptr->name, "vampire")))
+ do_vampire = TRUE;
+ touched = TRUE;
+ sound(SOUND_BITE);
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings you.";
+ touched = TRUE;
+ sound(SOUND_STING);
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's you.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs you.";
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges you.";
+ touched = TRUE;
+ sound(SOUND_BUY); /* Note! This is "charges", not "charges at". */
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on you.";
+ touched = TRUE;
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ explode = TRUE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at you.";
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at you.";
+ sound(SOUND_WAIL);
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at you.";
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs you for money.";
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = desc_insult[rand_int(8)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ if (strstr((r_name + r_ptr->name), "Mathilde, the Science Student"))
+ act = desc_moan[rand_int(3) + 4];
+ else
+ act = desc_moan[rand_int(4)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ if (randint(3) == 1)
+ act = "sings 'We are a happy family.'";
+ else
+ act = "sings 'I love you, you love me.'";
+ sound(SOUND_SHOW);
+ break;
+ }
+ }
+
+ /* Message */
+ if (act) msg_format("%^s %s", m_name, act);
+
+ /* The undead can give the player the Black Breath with
+ * a successful blow. Uniques have a better chance. -LM-
+ * Nazgul have a 25% chance
+ */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ black_breath_attack(4);
+ }
+ else if ((m_ptr->level >= 35) && (r_ptr->flags3 & (RF3_UNDEAD)) &&
+ (r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ black_breath_attack(300 - m_ptr->level);
+ }
+ else if ((m_ptr->level >= 40) && (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ black_breath_attack(450 - m_ptr->level);
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Sometime reduce the damage */
+ damage /= divis;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ /* Hack -- Assume obvious */
+ obvious = TRUE;
+
+ /* Hack -- No damage */
+ damage = 0;
+
+ break;
+ }
+
+ case RBE_HURT:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Player armor reduces total damage */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ABOMINATION:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Morph, but let mimicry skill have a chance to stop this */
+ if (magik(60 - get_skill(SKILL_MIMICRY)))
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers twisting your body!");
+
+ set_mimic(damage, resolve_mimic_name("Abomination"), 50);
+ }
+ else
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers trying to twisting your body, but they fail.");
+ }
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ obvious = TRUE;
+
+ take_sanity_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_POIS);
+
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Allow complete resist */
+ if (!p_ptr->resist_disen)
+ {
+ /* Apply disenchantment */
+ if (apply_disenchant(0)) obvious = TRUE;
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_DISEN);
+
+ break;
+ }
+
+ case RBE_UN_POWER:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item */
+ i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Drain charged wands/staffs
+ Hack -- don't let artifacts get drained */
+ if (((o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND)) &&
+ (o_ptr->pval) &&
+ !artifact_p(o_ptr))
+ {
+ /* Message */
+ msg_print("Energy drains from your pack!");
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Heal */
+ j = rlev;
+ m_ptr->hp += j * o_ptr->pval * o_ptr->number;
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Uncharge */
+ o_ptr->pval = 0;
+
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Done */
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case RBE_EAT_GOLD:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Saving throw (unless paralyzed) based on dex and level */
+ if (!p_ptr->paralyzed &&
+ (rand_int(100) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("You quickly protect your money pouch!");
+
+ /* Occasional blink anyway */
+ if (rand_int(3)) blinked = TRUE;
+ }
+
+ /* Eat gold */
+ else
+ {
+ gold = (p_ptr->au / 10) + randint(25);
+ if (gold < 2) gold = 2;
+ if (gold > 5000) gold = (p_ptr->au / 20) + randint(3000);
+ if (gold > p_ptr->au) gold = p_ptr->au;
+ p_ptr->au -= gold;
+ if (gold <= 0)
+ {
+ msg_print("Nothing was stolen.");
+ }
+ else if (p_ptr->au)
+ {
+ msg_print("Your purse feels lighter.");
+ msg_format("%ld coins were stolen!", (long)gold);
+ }
+ else
+ {
+ msg_print("Your purse feels lighter.");
+ msg_print("All of your coins were stolen!");
+ }
+
+ while (gold > 0)
+ {
+ object_type forge, *j_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(j_ptr);
+
+ /* Prepare a gold object */
+ object_prep(j_ptr, lookup_kind(TV_GOLD, 9));
+
+ /* Determine how much the treasure is "worth" */
+ j_ptr->pval = (gold >= 15000) ? 15000 : gold;
+
+ monster_carry(m_ptr, m_idx, j_ptr);
+
+ gold -= 15000;
+ }
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Blink away */
+ blinked = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Saving throw (unless paralyzed) based on dex and level */
+ if (!p_ptr->paralyzed &&
+ (rand_int(100) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("You grab hold of your backpack!");
+
+ /* Occasional "blink" anyway */
+ blinked = TRUE;
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item */
+ i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) continue;
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) was stolen!",
+ ((o_ptr->number > 1) ? "One of y" : "Y"),
+ o_name, index_to_label(i));
+
+ /* Option */
+ if (testing_carry)
+ {
+ s16b o_idx;
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *j_ptr;
+
+ /* Get new object */
+ j_ptr = &o_list[o_idx];
+
+ /* Copy object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify number */
+ j_ptr->number = 1;
+
+ /* Hack -- If a wand, allocate total
+ * maximum timeouts or charges between those
+ * stolen and those missed. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Forget mark */
+ j_ptr->marked = FALSE;
+
+ /* Memorize monster */
+ j_ptr->held_m_idx = m_idx;
+
+ /* Build stack */
+ j_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Build stack */
+ m_ptr->hold_o_idx = o_idx;
+ }
+ }
+ else
+ {
+ if (strstr((r_name + r_ptr->name), "black market")
+ && randint(2) != 1)
+ {
+ s16b o_idx;
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *j_ptr;
+ if (cheat_xtra || cheat_peek)
+ msg_print("Moving object to black market...");
+
+ /* Get new object */
+ j_ptr = &o_list[o_idx];
+
+ /* Copy object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify number */
+ j_ptr->number = 1;
+
+ /* Forget mark */
+ j_ptr->marked = FALSE;
+
+ move_to_black_market(j_ptr);
+ }
+ }
+ }
+
+ /* Steal the items */
+ inven_item_increase(i, -1);
+ inven_item_optimize(i);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Blink away */
+ blinked = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Steal some food */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item from the pack */
+ i = rand_int(INVEN_PACK);
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip non-food objects */
+ if (o_ptr->tval != TV_FOOD) continue;
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Message */
+ msg_format("%sour %s (%c) was eaten!",
+ ((o_ptr->number > 1) ? "One of y" : "Y"),
+ o_name, index_to_label(i));
+
+ /* Steal the items */
+ inven_item_increase(i, -1);
+ inven_item_optimize(i);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_LITE:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Access the lite */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Drain fuel */
+ if ((o_ptr->pval > 0) && (!artifact_p(o_ptr)))
+ {
+ /* Reduce fuel */
+ o_ptr->pval -= (250 + randint(250));
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Notice */
+ if (!p_ptr->blind)
+ {
+ msg_print("Your light dims.");
+ obvious = TRUE;
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered in acid!");
+
+ /* Special damage */
+ acid_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_ACID);
+
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are struck by electricity!");
+
+ /* Special damage */
+ elec_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_ELEC);
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are enveloped in flames!");
+
+ /* Special damage */
+ fire_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FIRE);
+
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered with frost!");
+
+ /* Special damage */
+ cold_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_COLD);
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "blind" */
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + 10 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_BLIND);
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "confused" */
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_CONF);
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FEAR);
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ /* Hack -- Prevent perma-paralysis via damage */
+ if (p_ptr->paralyzed && (damage < 1)) damage = 1;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "paralyzed" */
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_paralyzed(p_ptr->paralyzed + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FREE);
+
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_INT:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_WIS:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_DEX:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CON:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_CHR:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stat) */
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_LOSE_ALL:
+ {
+ /* Damage (physical) */
+ take_hit(damage, ddesc);
+
+ /* Damage (stats) */
+ if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) obvious = TRUE;
+ if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) obvious = TRUE;
+
+ break;
+ }
+
+ case RBE_SHATTER:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Reduce damage based on the player armor class */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Radius 8 earthquake centered at the monster */
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(m_ptr->fy, m_ptr->fx, 8);
+ }
+
+ break;
+ }
+
+ case RBE_EXP_10:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 95))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(10, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_20:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 90))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(20, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_40:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(40, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_EXP_80:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ if (p_ptr->hold_life && (rand_int(100) < 50))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else
+ {
+ s32b d = damroll(80, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE;
+ if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(d / 10);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(d);
+ }
+ }
+ break;
+ }
+
+ case RBE_DISEASE:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Damage CON (10% chance)*/
+ if (randint(100) < 11)
+ {
+ /* 1% chance for perm. damage */
+ bool perm = (randint(10) == 1);
+ if (dec_stat(A_CON, randint(10), perm)) obvious = TRUE;
+ }
+
+ break;
+ }
+ case RBE_HALLU:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "image" */
+ if (!p_ptr->resist_chaos)
+ {
+ if (set_image(p_ptr->image + 3 + randint(rlev / 2)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_CHAOS);
+
+ break;
+
+ }
+ case RBE_TIME:
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ int stat = rand_int(6);
+
+ switch (stat)
+ {
+ case A_STR:
+ act = "strong";
+ break;
+ case A_INT:
+ act = "bright";
+ break;
+ case A_WIS:
+ act = "wise";
+ break;
+ case A_DEX:
+ act = "agile";
+ break;
+ case A_CON:
+ act = "hardy";
+ break;
+ case A_CHR:
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[stat] = (p_ptr->stat_cur[stat] * 3) / 4;
+ if (p_ptr->stat_cur[stat] < 3) p_ptr->stat_cur[stat] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ take_hit(damage, ddesc);
+ break;
+ }
+ case RBE_PARASITE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ if (!p_ptr->parasite) set_parasite(damage, m_ptr->r_idx);
+
+ break;
+ }
+ }
+
+
+ /* Hack -- only one of cut or stun */
+ if (do_cut && do_stun)
+ {
+ /* Cancel cut */
+ if (rand_int(100) < 50)
+ {
+ do_cut = 0;
+ }
+
+ /* Cancel stun */
+ else
+ {
+ do_stun = 0;
+ }
+ }
+
+ /* Handle cut */
+ if (do_cut)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(5) + 5;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(50) + 50;
+ break;
+ case 5:
+ k = randint(100) + 100;
+ break;
+ case 6:
+ k = 300;
+ break;
+ default:
+ k = 500;
+ break;
+ }
+
+ /* Apply the cut */
+ if (k) (void)set_cut(p_ptr->cut + k);
+ }
+
+ /* Handle stun */
+ if (do_stun)
+ {
+ int k = 0;
+
+ /* Critical hit (zero if non-critical) */
+ tmp = monster_critical(d_dice, d_side, damage);
+
+ /* Roll for damage */
+ switch (tmp)
+ {
+ case 0:
+ k = 0;
+ break;
+ case 1:
+ k = randint(5);
+ break;
+ case 2:
+ k = randint(10) + 10;
+ break;
+ case 3:
+ k = randint(20) + 20;
+ break;
+ case 4:
+ k = randint(30) + 30;
+ break;
+ case 5:
+ k = randint(40) + 40;
+ break;
+ case 6:
+ k = 100;
+ break;
+ default:
+ k = 200;
+ break;
+ }
+
+ /* Apply the stun */
+ if (k) (void)set_stun(p_ptr->stun + k);
+ }
+
+ /* Do vampiric thingies */
+ if (do_vampire)
+ {
+ /* Change to resist(but never total protection) */
+/* if (magik(3) || (magik(m_ptr->level - (p_ptr->lev / 2))))
+ call_lua("gain_corruption", "(s)", "", "Vampire");*/
+ }
+
+ if (explode)
+ {
+ sound(SOUND_EXPLODE);
+ if (mon_take_hit(m_idx, m_ptr->hp + 1, &fear, NULL))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+
+ if (touched)
+ {
+ if (p_ptr->sh_fire && alive)
+ {
+ if (!(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ msg_format("%^s is suddenly very hot!", m_name);
+ if (mon_take_hit(m_idx, damroll(2, 6), &fear,
+ " turns into a pile of ash."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ else
+ {
+ if (m_ptr->ml)
+ r_ptr->r_flags3 |= RF3_IM_FIRE;
+ }
+ }
+
+ if (p_ptr->sh_elec && alive)
+ {
+ if (!(r_ptr->flags3 & RF3_IM_ELEC))
+ {
+ msg_format("%^s gets zapped!", m_name);
+ if (mon_take_hit(m_idx, damroll(2, 6), &fear,
+ " turns into a pile of cinder."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ else
+ {
+ if (m_ptr->ml)
+ r_ptr->r_flags3 |= RF3_IM_ELEC;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_COUNTER) && alive)
+ {
+ msg_format("%^s gets bashed by your mystic shield!", m_name);
+ if (mon_take_hit(m_idx, damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2), &fear,
+ " is bashed by your mystic shield."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_FIRE) && alive)
+ {
+ if (!(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ msg_format("%^s gets burned by your fiery shield!", m_name);
+ if (mon_take_hit(m_idx, damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2), &fear,
+ " is burned by your fiery shield."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ else
+ {
+ if (m_ptr->ml)
+ r_ptr->r_flags3 |= RF3_IM_FIRE;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_GREAT_FIRE) && alive)
+ {
+ msg_format("%^s gets burned by your fiery shield!", m_name);
+ if (mon_take_hit(m_idx, damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2), &fear,
+ " is burned by your fiery shield."))
+ {
+ blinked = FALSE;
+ alive = FALSE;
+ }
+ }
+ if (p_ptr->shield && (p_ptr->shield_opt & SHIELD_FEAR) && alive)
+ {
+ int tmp;
+
+ if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (damroll(p_ptr->shield_power_opt, p_ptr->shield_power_opt2) - m_ptr->level > 0))
+ {
+ msg_format("%^s gets scared away!", m_name);
+
+ /* Increase fear */
+ tmp = m_ptr->monfear + p_ptr->shield_power_opt;
+ fear = TRUE;
+
+ /* Set fear */
+ m_ptr->monfear = (tmp < 200) ? tmp : 200;
+ }
+ }
+
+ touched = FALSE;
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+
+ /* Visible monsters */
+ if (m_ptr->ml)
+ {
+ /* Disturbing */
+ disturb(1, 0);
+
+ /* Message */
+ msg_format("%^s misses you.", m_name);
+ }
+
+ break;
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+
+ /* Blink away */
+ if (blinked)
+ {
+ msg_print("The thief flees laughing!");
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ }
+
+
+ /* Always notice cause of death */
+ if (death && (r_ptr->r_deaths < MAX_SHORT))
+ {
+ r_ptr->r_deaths++;
+ }
+
+ if (m_ptr->ml && fear)
+ {
+ sound (SOUND_FLEE);
+ msg_format("%^s flees in terror!", m_name);
+ }
+
+ /* Assume we attacked */
+ return (TRUE);
+}
+
+
diff --git a/src/melee2.c b/src/melee2.c
new file mode 100644
index 00000000..291a7d9f
--- /dev/null
+++ b/src/melee2.c
@@ -0,0 +1,7837 @@
+/* File: melee2.c */
+
+/* Purpose: Monster spells and movement */
+
+/*
+* Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+*
+* This software may be copied and distributed for educational, research, and
+* not for profit purposes provided that this copyright and statement are
+* included in all such copies.
+*/
+
+/*
+* This file has several additions to it by Keldon Jones (keldon@umr.edu)
+* to improve the general quality of the AI (version 0.1.1).
+*/
+
+#include "angband.h"
+
+#define SPEAK_CHANCE 8
+#define GRINDNOISE 20
+
+#define FOLLOW_DISTANCE 6
+
+/*
+ * Based on mon_take_hit... all monster attacks on
+ * other monsters should use
+ */
+bool mon_take_hit_mon(int s_idx, int m_idx, int dam, bool *fear, cptr note)
+{
+ monster_type *m_ptr = &m_list[m_idx], *s_ptr = &m_list[s_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ s32b div, new_exp, new_exp_frac;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Some mosnters are immune to death */
+ if (r_ptr->flags7 & RF7_NO_DEATH) return FALSE;
+
+ /* Wake it up */
+ m_ptr->csleep = 0;
+
+ /* Hurt it */
+ m_ptr->hp -= dam;
+
+ /* It is dead now... or is it? */
+ if (m_ptr->hp < 0)
+ {
+ if (((r_ptr->flags1 & RF1_UNIQUE) && (m_ptr->status <= MSTATUS_NEUTRAL_P)) ||
+ (m_ptr->mflag & MFLAG_QUEST))
+ {
+ m_ptr->hp = 1;
+ }
+ else
+ {
+ char m_name[80];
+ s32b dive = s_ptr->level;
+
+ if (!dive) dive = 1;
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Make a sound */
+ if ((r_ptr->flags3 & RF3_DEMON) ||
+ (r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags2 & RF2_STUPID) ||
+ (r_ptr->flags3 & RF3_NONLIVING) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ sound(SOUND_N_KILL);
+ }
+ else
+ {
+ sound(SOUND_KILL);
+ }
+
+ /* Death by Missile/Spell attack */
+ if (note)
+ {
+ cmonster_msg(TERM_L_RED, "%^s%s", m_name, note);
+ }
+ /* Death by Physical attack -- living monster */
+ else if (!m_ptr->ml)
+ {
+ /* Do nothing */
+ }
+ /* Death by Physical attack -- non-living monster */
+ else if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ cmonster_msg(TERM_L_RED, "%^s is destroyed.", m_name);
+ }
+ else
+ {
+ cmonster_msg(TERM_L_RED, "%^s is killed.", m_name);
+ }
+
+ dive = r_ptr->mexp * m_ptr->level / dive;
+ if (!dive) dive = 1;
+
+ /* Monster gains some xp */
+ monster_gain_exp(s_idx, dive, FALSE);
+
+ /* Monster lore skill allows gaining xp from pets */
+ if (get_skill(SKILL_LORE) && (s_ptr->status >= MSTATUS_PET))
+ {
+ /* Maximum player level */
+ div = p_ptr->max_plv;
+
+ /* Give some experience for the kill */
+ new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;
+
+ /* Handle fractional experience */
+ new_exp_frac = ((((long)r_ptr->mexp * m_ptr->level) % div)
+ * 0x10000L / div) + p_ptr->exp_frac;
+
+ /* Keep track of experience */
+ if (new_exp_frac >= 0x10000L)
+ {
+ new_exp++;
+ p_ptr->exp_frac = new_exp_frac - 0x10000;
+ }
+ else
+ {
+ p_ptr->exp_frac = new_exp_frac;
+ }
+
+ /*
+ * Factor the xp by the skill level
+ * Note that a score of 50 in the skill makes the gain be 120% of the exp
+ */
+ new_exp = new_exp * get_skill_scale(SKILL_LORE, 120) / 100;
+
+ /* Gain experience */
+ gain_exp(new_exp);
+ }
+
+ /* When an Unique dies, it stays dead */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ r_ptr->max_num = 0;
+ }
+
+ /* Generate treasure */
+ monster_death(m_idx);
+
+ /* Delete the monster */
+ delete_monster_idx(m_idx);
+
+ /* Not afraid */
+ (*fear) = FALSE;
+
+ /* Monster is dead */
+ return (TRUE);
+ }
+
+ }
+
+#ifdef ALLOW_FEAR
+
+ /* Mega-Hack -- Pain cancels fear */
+ if (m_ptr->monfear && (dam > 0))
+ {
+ int tmp = randint(dam);
+
+ /* Cure a little fear */
+ if (tmp < m_ptr->monfear)
+ {
+ /* Reduce fear */
+ m_ptr->monfear -= tmp;
+ }
+
+ /* Cure all the fear */
+ else
+ {
+ /* Cure fear */
+ m_ptr->monfear = 0;
+
+ /* No more fear */
+ (*fear) = FALSE;
+ }
+ }
+
+ /* Sometimes a monster gets scared by damage */
+ if (!m_ptr->monfear && !(r_ptr->flags3 & (RF3_NO_FEAR)))
+ {
+ int percentage;
+
+ /* Percentage of fully healthy */
+ percentage = (100L * m_ptr->hp) / m_ptr->maxhp;
+
+ /*
+ * Run (sometimes) if at 10% or less of max hit points,
+ * or (usually) when hit for half its current hit points
+ */
+ if (((percentage <= 10) && (rand_int(10) < percentage)) ||
+ ((dam >= m_ptr->hp) && (rand_int(100) < 80)))
+ {
+ /* Hack -- note fear */
+ (*fear) = TRUE;
+
+ /* XXX XXX XXX Hack -- Add some timed fear */
+ m_ptr->monfear = (randint(10) +
+ (((dam >= m_ptr->hp) && (percentage > 7)) ?
+ 20 : ((11 - percentage) * 5)));
+ }
+ }
+
+#endif /* ALLOW_FEAR */
+
+ /* Not dead yet */
+ return (FALSE);
+}
+
+
+
+
+
+/*
+* And now for Intelligent monster attacks (including spells).
+*
+* Original idea and code by "DRS" (David Reeves Sward).
+* Major modifications by "BEN" (Ben Harrison).
+*
+* Give monsters more intelligent attack/spell selection based on
+* observations of previous attacks on the player, and/or by allowing
+* the monster to "cheat" and know the player status.
+*
+* Maintain an idea of the player status, and use that information
+* to occasionally eliminate "ineffective" spell attacks. We could
+* also eliminate ineffective normal attacks, but there is no reason
+* for the monster to do this, since he gains no benefit.
+* Note that MINDLESS monsters are not allowed to use this code.
+* And non-INTELLIGENT monsters only use it partially effectively.
+*
+* Actually learn what the player resists, and use that information
+* to remove attacks or spells before using them. This will require
+* much less space, if I am not mistaken. Thus, each monster gets a
+* set of 32 bit flags, "smart", build from the various "SM_*" flags.
+*
+* This has the added advantage that attacks and spells are related.
+* The "smart_learn" option means that the monster "learns" the flags
+* that should be set, and "smart_cheat" means that he "knows" them.
+* So "smart_cheat" means that the "smart" field is always up to date,
+* while "smart_learn" means that the "smart" field is slowly learned.
+* Both of them have the same effect on the "choose spell" routine.
+*/
+
+
+
+/*
+* Internal probability routine
+*/
+static bool int_outof(monster_race *r_ptr, int prob)
+{
+ /* Non-Smart monsters are half as "smart" */
+ if (!(r_ptr->flags2 & (RF2_SMART))) prob = prob / 2;
+
+ /* Roll the dice */
+ return (rand_int(100) < prob);
+}
+
+
+
+/*
+ * Remove the "bad" spells from a spell list
+ */
+static void remove_bad_spells(int m_idx, u32b *f4p, u32b *f5p, u32b *f6p)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ u32b f4 = (*f4p);
+ u32b f5 = (*f5p);
+ u32b f6 = (*f6p);
+
+ u32b smart = 0L;
+
+
+ /* Too stupid to know anything */
+ if (r_ptr->flags2 & (RF2_STUPID)) return;
+
+
+ /* Must be cheating or learning */
+ if (!smart_cheat && !smart_learn) return;
+
+
+ /* Update acquired knowledge */
+ if (smart_learn)
+ {
+ /* Hack -- Occasionally forget player status */
+ if (m_ptr->smart && (rand_int(100) < 1)) m_ptr->smart = 0L;
+
+ /* Use the memorized flags */
+ smart = m_ptr->smart;
+ }
+
+
+ /* Cheat if requested */
+ if (smart_cheat)
+ {
+ /* Know basic info */
+ if (p_ptr->resist_acid) smart |= (SM_RES_ACID);
+ if (p_ptr->oppose_acid) smart |= (SM_OPP_ACID);
+ if (p_ptr->immune_acid) smart |= (SM_IMM_ACID);
+ if (p_ptr->resist_elec) smart |= (SM_RES_ELEC);
+ if (p_ptr->oppose_elec) smart |= (SM_OPP_ELEC);
+ if (p_ptr->immune_elec) smart |= (SM_IMM_ELEC);
+ if (p_ptr->resist_fire) smart |= (SM_RES_FIRE);
+ if (p_ptr->oppose_fire) smart |= (SM_OPP_FIRE);
+ if (p_ptr->immune_fire) smart |= (SM_IMM_FIRE);
+ if (p_ptr->resist_cold) smart |= (SM_RES_COLD);
+ if (p_ptr->oppose_cold) smart |= (SM_OPP_COLD);
+ if (p_ptr->immune_cold) smart |= (SM_IMM_COLD);
+
+ /* Know poison info */
+ if (p_ptr->resist_pois) smart |= (SM_RES_POIS);
+ if (p_ptr->oppose_pois) smart |= (SM_OPP_POIS);
+
+ /* Know special resistances */
+ if (p_ptr->resist_neth) smart |= (SM_RES_NETH);
+ if (p_ptr->resist_lite) smart |= (SM_RES_LITE);
+ if (p_ptr->resist_dark) smart |= (SM_RES_DARK);
+ if (p_ptr->resist_fear) smart |= (SM_RES_FEAR);
+ if (p_ptr->resist_conf) smart |= (SM_RES_CONF);
+ if (p_ptr->resist_chaos) smart |= (SM_RES_CHAOS);
+ if (p_ptr->resist_disen) smart |= (SM_RES_DISEN);
+ if (p_ptr->resist_blind) smart |= (SM_RES_BLIND);
+ if (p_ptr->resist_nexus) smart |= (SM_RES_NEXUS);
+ if (p_ptr->resist_sound) smart |= (SM_RES_SOUND);
+ if (p_ptr->resist_shard) smart |= (SM_RES_SHARD);
+ if (p_ptr->reflect) smart |= (SM_IMM_REFLECT);
+
+ /* Know bizarre "resistances" */
+ if (p_ptr->free_act) smart |= (SM_IMM_FREE);
+ if (!p_ptr->msp) smart |= (SM_IMM_MANA);
+ }
+
+
+ /* Nothing known */
+ if (!smart) return;
+
+
+ if (smart & (SM_IMM_ACID))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ACID);
+ }
+ else if ((smart & (SM_OPP_ACID)) && (smart & (SM_RES_ACID)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ACID);
+ }
+ else if ((smart & (SM_OPP_ACID)) || (smart & (SM_RES_ACID)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ACID);
+ }
+
+
+ if (smart & (SM_IMM_ELEC))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ELEC);
+ }
+ else if ((smart & (SM_OPP_ELEC)) && (smart & (SM_RES_ELEC)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ELEC);
+ }
+ else if ((smart & (SM_OPP_ELEC)) || (smart & (SM_RES_ELEC)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ELEC);
+ }
+
+
+ if (smart & (SM_IMM_FIRE))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_FIRE);
+ }
+ else if ((smart & (SM_OPP_FIRE)) && (smart & (SM_RES_FIRE)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_FIRE);
+ }
+ else if ((smart & (SM_OPP_FIRE)) || (smart & (SM_RES_FIRE)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_FIRE);
+ }
+
+
+ if (smart & (SM_IMM_COLD))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ICEE);
+ }
+ else if ((smart & (SM_OPP_COLD)) && (smart & (SM_RES_COLD)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ICEE);
+ }
+ else if ((smart & (SM_OPP_COLD)) || (smart & (SM_RES_COLD)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ICEE);
+ }
+
+
+ if ((smart & (SM_OPP_POIS)) && (smart & (SM_RES_POIS)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_POIS);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_POIS);
+ if (int_outof(r_ptr, 40)) f4 &= ~(RF4_BA_NUKE);
+ if (int_outof(r_ptr, 40)) f4 &= ~(RF4_BR_NUKE);
+ }
+ else if ((smart & (SM_OPP_POIS)) || (smart & (SM_RES_POIS)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_POIS);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_POIS);
+ }
+
+
+ if (smart & (SM_RES_NETH))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_NETH);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BA_NETH);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BO_NETH);
+ }
+
+ if (smart & (SM_RES_LITE))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_LITE);
+ }
+
+ if (smart & (SM_RES_DARK))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_DARK);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BA_DARK);
+ }
+
+ if (smart & (SM_RES_FEAR))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_SCARE);
+ }
+
+ if (smart & (SM_RES_CONF))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CONF);
+ }
+
+ if (smart & (SM_RES_CHAOS))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CHAO);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BA_CHAO);
+ }
+
+ if (smart & (SM_RES_DISEN))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_DISE);
+ }
+
+ if (smart & (SM_RES_BLIND))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BLIND);
+ }
+
+ if (smart & (SM_RES_NEXUS))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_NEXU);
+ if (int_outof(r_ptr, 50)) f6 &= ~(RF6_TELE_LEVEL);
+ }
+
+ if (smart & (SM_RES_SOUND))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_SOUN);
+ }
+
+ if (smart & (SM_RES_SHARD))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_SHAR);
+ if (int_outof(r_ptr, 20)) f4 &= ~(RF4_ROCKET);
+ }
+
+ if (smart & (SM_IMM_REFLECT))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_POIS);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_NETH);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_WATE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_MANA);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_PLAS);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ICEE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_MISSILE);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_1);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_2);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_3);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_4);
+ }
+
+ if (smart & (SM_IMM_FREE))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_HOLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_SLOW);
+ }
+
+ if (smart & (SM_IMM_MANA))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_DRAIN_MANA);
+ }
+
+ /* XXX XXX XXX No spells left? */
+ /* if (!f4 && !f5 && !f6) ... */
+
+ (*f4p) = f4;
+ (*f5p) = f5;
+ (*f6p) = f6;
+}
+
+
+/*
+ * Determine if there is a space near the player in which
+ * a summoned creature can appear
+ */
+static bool summon_possible(int y1, int x1)
+{
+ int y, x;
+
+ /* Start at the player's location, and check 2 grids in each dir */
+ for (y = y1 - 2; y <= y1 + 2; y++)
+ {
+ for (x = x1 - 2; x <= x1 + 2; x++)
+ {
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Only check a circular area */
+ if (distance(y1, x1, y, x) > 2) continue;
+
+ /* Hack: no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ...nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START)
+ && (cave[y][x].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* Require empty floor grid in line of sight */
+ if (cave_empty_bold(y, x) && los(y1, x1, y, x)) return (TRUE);
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+/*
+ * Determine if a bolt spell will hit the player.
+ *
+ * This is exactly like "projectable", but it will return FALSE if a monster
+ * is in the way.
+ */
+static bool clean_shot(int y1, int x1, int y2, int x2)
+{
+ int dist, y, x;
+
+ /* Start at the initial location */
+ y = y1, x = x1;
+
+ /* See "project()" and "projectable()" */
+ for (dist = 0; dist <= MAX_RANGE; dist++)
+ {
+ /* Never pass through walls */
+ if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break;
+
+ /* Never pass through monsters */
+ if (dist && cave[y][x].m_idx > 0)
+ {
+ if (is_friend(&m_list[cave[y][x].m_idx]) < 0) break;
+ }
+
+ /* Check for arrival at "final target" */
+ if ((x == x2) && (y == y2)) return (TRUE);
+
+ /* Calculate the new location */
+ mmove2(&y, &x, y1, x1, y2, x2);
+ }
+
+ /* Assume obstruction */
+ return (FALSE);
+}
+
+
+/*
+ * Cast a bolt at the player
+ * Stop if we hit a monster
+ * Affect monsters and the player
+ */
+static void bolt(int m_idx, int typ, int dam_hp)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+
+ /* Target the player with a bolt attack */
+ (void)project(m_idx, 0, p_ptr->py, p_ptr->px, dam_hp, typ, flg);
+}
+
+
+/*
+ * Return TRUE if a spell is good for hurting the player (directly).
+ */
+static bool spell_attack(byte spell)
+{
+ /* All RF4 spells hurt (except for shriek, multiply, summon animal) */
+ if (spell >= 96 + 3 && spell <= 96 + 31) return (TRUE);
+
+ /* Various "ball" spells */
+ if (spell >= 128 && spell <= 128 + 8) return (TRUE);
+
+ /* "Cause wounds" and "bolt" spells */
+ if (spell >= 128 + 12 && spell <= 128 + 26) return (TRUE);
+
+ /* Hand of Doom */
+ if (spell == 160 + 1) return (TRUE);
+
+ /* Doesn't hurt */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good for escaping.
+ */
+static bool spell_escape(byte spell)
+{
+ /* Blink or Teleport */
+ if (spell == 160 + 4 || spell == 160 + 5) return (TRUE);
+
+ /* Teleport the player away */
+ if (spell == 160 + 7 || spell == 160 + 8) return (TRUE);
+
+ /* Isn't good for escaping */
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if a spell is good for annoying the player.
+ */
+static bool spell_annoy(byte spell)
+{
+ /* Shriek */
+ if (spell == 96 + 0) return (TRUE);
+
+ /* Brain smash, et al (added curses) */
+ if (spell >= 128 + 9 && spell <= 128 + 14) return (TRUE);
+
+ /* Scare, confuse, blind, slow, paralyze */
+ if (spell >= 128 + 27 && spell <= 128 + 31) return (TRUE);
+
+ /* Teleport to */
+ if (spell == 160 + 6) return (TRUE);
+
+#if 0
+ /* Hand of Doom */
+ if (spell == 160 + 1) return (TRUE);
+#endif
+
+ /* Darkness, make traps, cause amnesia */
+ if (spell >= 160 + 9 && spell <= 160 + 11) return (TRUE);
+
+ /* Doesn't annoy */
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if a spell summons help.
+ */
+static bool spell_summon(byte spell)
+{
+ /* RF4_S_ANIMAL, RF6_S_ANIMALS */
+ if (spell == 96 + 2 || spell == 160 + 3) return (TRUE);
+ /* All other summon spells */
+ if (spell >= 160 + 13 && spell <= 160 + 31) return (TRUE);
+
+ /* Doesn't summon */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good in a tactical situation.
+ */
+static bool spell_tactic(byte spell)
+{
+ /* Blink */
+ if (spell == 160 + 4) return (TRUE);
+
+ /* Not good */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell hastes.
+ */
+static bool spell_haste(byte spell)
+{
+ /* Haste self */
+ if (spell == 160 + 0) return (TRUE);
+
+ /* Not a haste spell */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good for healing.
+ */
+static bool spell_heal(byte spell)
+{
+ /* Heal */
+ if (spell == 160 + 2) return (TRUE);
+
+ /* No healing */
+ return (FALSE);
+}
+
+
+/*
+ * Have a monster choose a spell from a list of "useful" spells.
+ *
+ * Note that this list does NOT include spells that will just hit
+ * other monsters, and the list is restricted when the monster is
+ * "desperate". Should that be the job of this function instead?
+ *
+ * Stupid monsters will just pick a spell randomly. Smart monsters
+ * will choose more "intelligently".
+ *
+ * Use the helper functions above to put spells into categories.
+ *
+ * This function may well be an efficiency bottleneck.
+ */
+static int choose_attack_spell(int m_idx, byte spells[], byte num)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ byte escape[96], escape_num = 0;
+ byte attack[96], attack_num = 0;
+ byte summon[96], summon_num = 0;
+ byte tactic[96], tactic_num = 0;
+ byte annoy[96], annoy_num = 0;
+ byte haste[96], haste_num = 0;
+ byte heal[96], heal_num = 0;
+
+ int i;
+
+ /* Stupid monsters choose randomly */
+ if (r_ptr->flags2 & (RF2_STUPID))
+ {
+ /* Pick at random */
+ return (spells[rand_int(num)]);
+ }
+
+ /* Categorize spells */
+ for (i = 0; i < num; i++)
+ {
+ /* Escape spell? */
+ if (spell_escape(spells[i])) escape[escape_num++] = spells[i];
+
+ /* Attack spell? */
+ if (spell_attack(spells[i])) attack[attack_num++] = spells[i];
+
+ /* Summon spell? */
+ if (spell_summon(spells[i])) summon[summon_num++] = spells[i];
+
+ /* Tactical spell? */
+ if (spell_tactic(spells[i])) tactic[tactic_num++] = spells[i];
+
+ /* Annoyance spell? */
+ if (spell_annoy(spells[i])) annoy[annoy_num++] = spells[i];
+
+ /* Haste spell? */
+ if (spell_haste(spells[i])) haste[haste_num++] = spells[i];
+
+ /* Heal spell? */
+ if (spell_heal(spells[i])) heal[heal_num++] = spells[i];
+ }
+
+ /*** Try to pick an appropriate spell type ***/
+
+ /* Hurt badly or afraid, attempt to flee */
+ if ((m_ptr->hp < m_ptr->maxhp / 3) || m_ptr->monfear)
+ {
+ /* Choose escape spell if possible */
+ if (escape_num) return (escape[rand_int(escape_num)]);
+ }
+
+ /* Still hurt badly, couldn't flee, attempt to heal */
+ if (m_ptr->hp < m_ptr->maxhp / 3)
+ {
+ /* Choose heal spell if possible */
+ if (heal_num) return (heal[rand_int(heal_num)]);
+ }
+
+ /* Player is close and we have attack spells, blink away */
+ if ((distance(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx) < 4) && attack_num && (rand_int(100) < 75))
+ {
+ /* Choose tactical spell */
+ if (tactic_num) return (tactic[rand_int(tactic_num)]);
+ }
+
+ /* We're hurt (not badly), try to heal */
+ if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (rand_int(100) < 75))
+ {
+ /* Choose heal spell if possible */
+ if (heal_num) return (heal[rand_int(heal_num)]);
+ }
+
+ /* Summon if possible (sometimes) */
+ if (summon_num && (rand_int(100) < 50))
+ {
+ /* Choose summon spell */
+ return (summon[rand_int(summon_num)]);
+ }
+
+ /* Attack spell (most of the time) */
+ if (attack_num && (rand_int(100) < 85))
+ {
+ /* Choose attack spell */
+ return (attack[rand_int(attack_num)]);
+ }
+
+ /* Try another tactical spell (sometimes) */
+ if (tactic_num && (rand_int(100) < 50))
+ {
+ /* Choose tactic spell */
+ return (tactic[rand_int(tactic_num)]);
+ }
+
+ /* Haste self if we aren't already somewhat hasted (rarely) */
+ if (haste_num && (rand_int(100) < (20 + m_ptr->speed - m_ptr->mspeed)))
+ {
+ /* Choose haste spell */
+ return (haste[rand_int(haste_num)]);
+ }
+
+ /* Annoy player (most of the time) */
+ if (annoy_num && (rand_int(100) < 85))
+ {
+ /* Choose annoyance spell */
+ return (annoy[rand_int(annoy_num)]);
+ }
+
+ /* Choose no spell */
+ return (0);
+}
+
+
+/*
+ * Cast a breath (or ball) attack at the player
+ * Pass over any monsters that may be in the way
+ * Affect grids, objects, monsters, and the player
+ */
+static void breath(int m_idx, int typ, int dam_hp, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Determine the radius of the blast */
+ if (rad < 1) rad = (r_ptr->flags2 & (RF2_POWERFUL)) ? 3 : 2;
+
+ /* Target the player with a ball attack */
+ (void)project(m_idx, rad, p_ptr->py, p_ptr->px, dam_hp, typ, flg);
+}
+
+
+/*
+ * Monster casts a breath (or ball) attack at another monster.
+ * Pass over any monsters that may be in the way
+ * Affect grids, objects, monsters, and the player
+ */
+static void monst_breath_monst(int m_idx, int y, int x, int typ, int dam_hp, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Determine the radius of the blast */
+ if (rad < 1) rad = (r_ptr->flags2 & (RF2_POWERFUL)) ? 3 : 2;
+
+ (void)project(m_idx, rad, y, x, dam_hp, typ, flg);
+}
+
+
+/*
+ * Monster casts a bolt at another monster
+ * Stop if we hit a monster
+ * Affect monsters and the player
+ */
+static void monst_bolt_monst(int m_idx, int y, int x, int typ, int dam_hp)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+
+ (void)project(m_idx, 0, y, x, dam_hp, typ, flg);
+}
+
+
+void monster_msg(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ if (disturb_other)
+ msg_print(buf);
+ else
+ {
+ message_add(MESSAGE_MSG, buf, TERM_WHITE);
+ p_ptr->window |= PW_MESSAGE;
+ }
+}
+
+void cmonster_msg(char a, cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ if (disturb_other)
+ cmsg_print(a, buf);
+ else
+ {
+ message_add(MESSAGE_MSG, buf, a);
+ p_ptr->window |= PW_MESSAGE;
+ }
+}
+
+
+/*
+ * Monster tries to 'cast a spell' (or breath, etc)
+ * at another monster.
+ */
+int monst_spell_monst_spell = -1;
+static bool monst_spell_monst(int m_idx)
+{
+ int y = 0, x = 0;
+ int i = 1, k, t_idx;
+ int chance, thrown_spell, count = 0;
+ byte spell[96], num = 0;
+ char m_name[80], t_name[80];
+ char m_poss[80];
+ char ddesc[80];
+ int rlev; /* monster level */
+ monster_type *m_ptr = &m_list[m_idx]; /* Attacker */
+ monster_race *r_ptr = race_inf(m_ptr);
+ monster_type *t_ptr; /* Putative target */
+ monster_race *tr_ptr;
+ u32b f4, f5, f6; /* racial spell flags */
+ bool direct = TRUE;
+ bool wake_up = FALSE;
+
+ /* Extract the blind-ness */
+ bool blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Extract the "see-able-ness" */
+ bool seen = (!blind && m_ptr->ml);
+
+ bool see_m;
+ bool see_t;
+ bool see_either;
+ bool see_both;
+
+ bool friendly = FALSE;
+
+ if (is_friend(m_ptr) > 0) friendly = TRUE;
+
+ /* Cannot cast spells when confused */
+ if (m_ptr->confused) return (FALSE);
+
+ /* Hack -- Extract the spell probability */
+ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2;
+
+ /* Not allowed to cast spells */
+ if ((!chance) && (monst_spell_monst_spell == -1)) return (FALSE);
+
+ if ((rand_int(100) >= chance) && (monst_spell_monst_spell == -1)) return (FALSE);
+
+ /* Target location */
+ if (m_ptr->target > -1)
+ {
+ if (m_ptr->target > 0)
+ {
+ i = m_ptr->target;
+ }
+ else return FALSE;
+ }
+ else return FALSE;
+
+
+ {
+ t_idx = i;
+ t_ptr = &m_list[t_idx];
+ tr_ptr = race_inf(t_ptr);
+
+ /* Hack -- no fighting >100 squares from player */
+ if (t_ptr->cdis > MAX_RANGE) return FALSE;
+
+ /* Monster must be projectable */
+ if (!projectable(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) return FALSE;
+
+ /* OK -- we-ve got a target */
+ y = t_ptr->fy;
+ x = t_ptr->fx;
+
+ /* Extract the monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Extract the racial spell flags */
+ f4 = r_ptr->flags4;
+ f5 = r_ptr->flags5;
+ f6 = r_ptr->flags6;
+
+ /* Hack -- allow "desperate" spells */
+ if ((r_ptr->flags2 & (RF2_SMART)) &&
+ (m_ptr->hp < m_ptr->maxhp / 10) &&
+ (rand_int(100) < 50))
+ {
+ /* Require intelligent spells */
+ f4 &= (RF4_INT_MASK);
+ f5 &= (RF5_INT_MASK);
+ f6 &= (RF6_INT_MASK);
+
+ /* No spells left */
+ if ((!f4 && !f5 && !f6) && (monst_spell_monst_spell == -1)) return (FALSE);
+ }
+
+ /* Extract the "inate" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f4 & (1L << k)) spell[num++] = k + 32 * 3;
+ }
+
+ /* Extract the "normal" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f5 & (1L << k)) spell[num++] = k + 32 * 4;
+ }
+
+ /* Extract the "bizarre" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f6 & (1L << k)) spell[num++] = k + 32 * 5;
+ }
+
+ /* No spells left */
+ if (!num) return (FALSE);
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) return (FALSE);
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) return (FALSE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0x00);
+
+ /* Get the monster possessive ("his"/"her"/"its") */
+ monster_desc(m_poss, m_ptr, 0x22);
+
+ /* Get the target's name (or "it") */
+ monster_desc(t_name, t_ptr, 0x00);
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+ /* Choose a spell to cast */
+ thrown_spell = spell[rand_int(num)];
+
+ /* Force a spell ? */
+ if (monst_spell_monst_spell > -1)
+ {
+ thrown_spell = monst_spell_monst_spell;
+ monst_spell_monst_spell = -1;
+ }
+
+ see_m = seen;
+ see_t = (!blind && t_ptr->ml);
+ see_either = (see_m || see_t);
+ see_both = (see_m && see_t);
+
+ switch (thrown_spell)
+ {
+ /* RF4_SHRIEK */
+ case 96 + 0:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!see_m) monster_msg("You hear a shriek.");
+ else monster_msg("%^s shrieks at %s.", m_name, t_name);
+#if 0
+ aggravate_monsters(m_idx);
+#endif
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF4_MULTIPLY */
+ case 96 + 1:
+ {
+ break;
+ }
+
+ /* RF4_S_ANIMAL */
+ case 96 + 2:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an animal!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF4_ROCKET */
+ case 96 + 3:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear an explosion!");
+ else if (blind) monster_msg("%^s shoots something.", m_name);
+ else monster_msg("%^s fires a rocket at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_ROCKET,
+ ((m_ptr->hp / 4) > 800 ? 800 : (m_ptr->hp / 4)), 2);
+ break;
+ }
+
+ /* RF4_ARROW_1 */
+ case 96 + 4:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires an arrow at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(1, 6));
+ break;
+ }
+
+ /* RF4_ARROW_2 */
+ case 96 + 5:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires an arrow at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(3, 6));
+ break;
+ }
+
+ /* RF4_ARROW_3 */
+ case 96 + 6:
+ {
+ if (disturb_other) disturb(1, 0);
+
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires a missile at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(5, 6));
+ break;
+ }
+
+ /* RF4_ARROW_4 */
+ case 96 + 7:
+ {
+ if (!see_either) monster_msg("You hear a strange noise.");
+ else if (disturb_other) disturb(1, 0);
+ if (blind) monster_msg("%^s makes a strange noise.", m_name);
+ else monster_msg("%^s fires a missile at %s.", m_name, t_name);
+ sound(SOUND_SHOOT);
+ monst_bolt_monst(m_idx, y, x, GF_ARROW, damroll(7, 6));
+ break;
+ }
+
+ /* RF4_BR_ACID */
+ case 96 + 8:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes acid at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_ACID,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_ELEC */
+ case 96 + 9:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes lightning at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_ELEC,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_FIRE */
+ case 96 + 10:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes fire at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_FIRE,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_COLD */
+ case 96 + 11:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes frost at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_COLD,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_POIS */
+ case 96 + 12:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes gas at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_POIS,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_NETH */
+ case 96 + 13:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes nether at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NETHER,
+ ((m_ptr->hp / 6) > 550 ? 550 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_LITE */
+ case 96 + 14:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes light at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_LITE,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_DARK */
+ case 96 + 15:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes darkness at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_DARK,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_CONF */
+ case 96 + 16:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes confusion at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_CONFUSION,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_SOUN */
+ case 96 + 17:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes sound at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_SOUND,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_CHAO */
+ case 96 + 18:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes chaos at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_CHAOS,
+ ((m_ptr->hp / 6) > 600 ? 600 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_DISE */
+ case 96 + 19:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes disenchantment at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_DISENCHANT,
+ ((m_ptr->hp / 6) > 500 ? 500 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_NEXU */
+ case 96 + 20:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes nexus at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NEXUS,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_TIME */
+ case 96 + 21:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes time at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_TIME,
+ ((m_ptr->hp / 3) > 150 ? 150 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_INER */
+ case 96 + 22:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes inertia at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_INERTIA,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_GRAV */
+ case 96 + 23:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes gravity at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_GRAVITY,
+ ((m_ptr->hp / 3) > 200 ? 200 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_SHAR */
+ case 96 + 24:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes shards at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_SHARDS,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_PLAS */
+ case 96 + 25:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes plasma at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_PLASMA,
+ ((m_ptr->hp / 6) > 150 ? 150 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_WALL */
+ case 96 + 26:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes force at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_FORCE,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_MANA */
+ case 96 + 27:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes magical energy at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_MANA,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BA_NUKE */
+ case 96 + 28:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear someone mumble.");
+ else if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a ball of radiation at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NUKE,
+ (rlev + damroll(10, 6)), 2);
+ break;
+ }
+
+ /* RF4_BR_NUKE */
+ case 96 + 29:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes toxic waste at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_NUKE,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BA_CHAO */
+ case 96 + 30:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear someone mumble frighteningly.");
+ else if (blind) monster_msg("%^s mumbles frighteningly.", m_name);
+ else monster_msg("%^s invokes a raw Chaos upon %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_CHAOS,
+ (rlev * 2) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF4_BR_DISI -> Breathe Disintegration */
+ case 96 + 31:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg("You hear breathing noise.");
+ else if (blind) monster_msg("%^s breathes.", m_name);
+ else monster_msg("%^s breathes disintegration at %s.", m_name, t_name);
+ sound(SOUND_BREATH);
+ monst_breath_monst(m_idx, y, x, GF_DISINTEGRATE,
+ ((m_ptr->hp / 3) > 300 ? 300 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF5_BA_ACID */
+ case 128 + 0:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts an acid ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_ACID, randint(rlev * 3) + 15, 2);
+ break;
+ }
+
+ /* RF5_BA_ELEC */
+ case 128 + 1:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a lightning ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_ELEC, randint(rlev * 3 / 2) + 8, 2);
+ break;
+ }
+
+ /* RF5_BA_FIRE */
+ case 128 + 2:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a fire ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_FIRE, randint(rlev * 7 / 2) + 10, 2);
+ break;
+ }
+
+ /* RF5_BA_COLD */
+ case 128 + 3:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a frost ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_COLD, randint(rlev * 3 / 2) + 10, 2);
+ break;
+ }
+
+ /* RF5_BA_POIS */
+ case 128 + 4:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a stinking cloud at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_POIS, damroll(12, 2), 2);
+ break;
+ }
+
+ /* RF5_BA_NETH */
+ case 128 + 5:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a nether ball at %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_NETHER, (50 + damroll(10, 10) + rlev), 2);
+ break;
+ }
+
+ /* RF5_BA_WATE */
+ case 128 + 6:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble.");
+ else
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s gestures fluidly at %s.", m_name, t_name);
+ monster_msg("%^s is engulfed in a whirlpool.", t_name);
+ monst_breath_monst(m_idx, y, x, GF_WATER, randint(rlev * 5 / 2) + 50, 4);
+ break;
+ }
+
+ /* RF5_BA_MANA */
+ case 128 + 7:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble powerfully.");
+ else
+ if (blind) monster_msg("%^s mumbles powerfully.", m_name);
+ else monster_msg("%^s invokes a mana storm upon %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_MANA, (rlev * 5) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF5_BA_DARK */
+ case 128 + 8:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (!see_either) monster_msg ("You hear someone mumble powerfully.");
+ else
+ if (blind) monster_msg("%^s mumbles powerfully.", m_name);
+ else monster_msg("%^s invokes a darkness storm upon %s.", m_name, t_name);
+ monst_breath_monst(m_idx, y, x, GF_DARK, (rlev * 5) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF5_DRAIN_MANA */
+ case 128 + 9:
+ {
+ /* Attack power */
+ int r1 = (randint(rlev) / 2) + 1;
+
+ if (see_m)
+ {
+ /* Basic message */
+ monster_msg("%^s draws psychic energy from %s.", m_name, t_name);
+ }
+
+ /* Heal the monster */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ if (!(tr_ptr->flags4 || tr_ptr->flags5 || tr_ptr->flags6))
+ {
+ if (see_both)
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ else
+ {
+ /* Heal */
+ m_ptr->hp += (6 * r1);
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Special message */
+ if (seen)
+ {
+ monster_msg("%^s appears healthier.", m_name);
+ }
+ }
+ }
+
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_MIND_BLAST */
+ case 128 + 10:
+ {
+ if (!direct) break;
+
+ if (disturb_other) disturb(1, 0);
+
+ if (!seen)
+ {
+ /* */
+ }
+ else
+ {
+ monster_msg("%^s gazes intently at %s.", m_name, t_name);
+ }
+
+ /* Attempt a saving throw */
+ if ((tr_ptr->flags1 & (RF1_UNIQUE)) ||
+ (tr_ptr->flags3 & (RF3_NO_CONF)) ||
+ (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (tr_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) tr_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* No obvious effect */
+ if (see_t)
+ {
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ }
+ else
+ {
+ bool fear;
+ monster_msg("%^s is blasted by psionic energy.", t_name);
+ t_ptr->confused += rand_int(4) + 4;
+
+ mon_take_hit_mon(m_idx, t_idx, damroll(8, 8), &fear, " collapses, a mindless husk.");
+ }
+
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_BRAIN_SMASH */
+ case 128 + 11:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!seen)
+ {
+ /* */
+ }
+ else
+ {
+ monster_msg("%^s gazes intently at %s.", m_name, t_name);
+ }
+
+ /* Attempt a saving throw */
+ if ((tr_ptr->flags1 & (RF1_UNIQUE)) ||
+ (tr_ptr->flags3 & (RF3_NO_CONF)) ||
+ (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (tr_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) tr_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+ /* No obvious effect */
+ if (see_t)
+ {
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ }
+ else
+ {
+ bool fear;
+ if (see_t)
+ {
+ monster_msg("%^s is blasted by psionic energy.", t_name);
+ }
+ t_ptr->confused += rand_int(4) + 4;
+ t_ptr->mspeed -= rand_int(4) + 4;
+ t_ptr->stunned += rand_int(4) + 4;
+ mon_take_hit_mon(m_idx, t_idx, damroll(12, 15), &fear, " collapses, a mindless husk.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_1 */
+ case 128 + 12:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s and curses.", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(3, 8), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_2 */
+ case 128 + 13:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s and curses horribly.", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(8, 8), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_3 */
+ case 128 + 14:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s, incanting terribly!", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(10, 15), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_CAUSE_4 */
+ case 128 + 15:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s points at %s, screaming the word 'DIE!'", m_name, t_name);
+ if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ else
+ {
+ bool fear;
+ mon_take_hit_mon(m_idx, t_idx, damroll(15, 15), &fear, " is destroyed.");
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_BO_ACID */
+ case 128 + 16:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts an acid bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_ACID,
+ damroll(7, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_ELEC */
+ case 128 + 17:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a lightning bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_ELEC,
+ damroll(4, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_FIRE */
+ case 128 + 18:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a fire bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_FIRE,
+ damroll(9, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_COLD */
+ case 128 + 19:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a frost bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_COLD,
+ damroll(6, 8) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_BO_POIS */
+ case 128 + 20:
+ {
+ /* XXX XXX XXX */
+ break;
+ }
+
+ /* RF5_BO_NETH */
+ case 128 + 21:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a nether bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_NETHER,
+ 30 + damroll(5, 5) + (rlev * 3) / 2);
+ break;
+ }
+
+ /* RF5_BO_WATE */
+ case 128 + 22:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a water bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_WATER,
+ damroll(10, 10) + (rlev));
+ break;
+ }
+
+ /* RF5_BO_MANA */
+ case 128 + 23:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a mana bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_MANA,
+ randint(rlev * 7 / 2) + 50);
+ break;
+ }
+
+ /* RF5_BO_PLAS */
+ case 128 + 24:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a plasma bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_PLASMA,
+ 10 + damroll(8, 7) + (rlev));
+ break;
+ }
+
+ /* RF5_BO_ICEE */
+ case 128 + 25:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts an ice bolt at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_ICE,
+ damroll(6, 6) + (rlev));
+ break;
+ }
+
+ /* RF5_MISSILE */
+ case 128 + 26:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a magic missile at %s.", m_name, t_name);
+ monst_bolt_monst(m_idx, y, x, GF_MISSILE,
+ damroll(2, 6) + (rlev / 3));
+ break;
+ }
+
+ /* RF5_SCARE */
+ case 128 + 27:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles, and you hear scary noises.", m_name);
+ else monster_msg("%^s casts a fearful illusion at %s.", m_name, t_name);
+ if (tr_ptr->flags3 & RF3_NO_FEAR)
+ {
+ if (see_t) monster_msg("%^s refuses to be frightened.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s refuses to be frightened.", t_name);
+ }
+ else
+ {
+ if (!(t_ptr->monfear) && see_t) monster_msg("%^s flees in terror!", t_name);
+ t_ptr->monfear += rand_int(4) + 4;
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_BLIND */
+ case 128 + 28:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s casts a spell, burning %s%s eyes.", m_name, t_name,
+ (!strcmp(t_name, "it") ? "s" : "'s"));
+ if (tr_ptr->flags3 & RF3_NO_CONF) /* Simulate blindness with confusion */
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else
+ {
+ if (see_t) monster_msg("%^s is blinded!", t_name);
+ t_ptr->confused += 12 + (byte)rand_int(4);
+ }
+ wake_up = TRUE;
+ break;
+
+ }
+
+ /* RF5_CONF */
+ case 128 + 29:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles, and you hear puzzling noises.", m_name);
+ else monster_msg("%^s creates a mesmerising illusion in front of %s.", m_name, t_name);
+ if (tr_ptr->flags3 & RF3_NO_CONF)
+ {
+ if (see_t) monster_msg("%^s disbelieves the feeble spell.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s disbelieves the feeble spell.", t_name);
+ }
+ else
+ {
+ if (see_t) monster_msg("%^s seems confused.", t_name);
+ t_ptr->confused += 12 + (byte)rand_int(4);
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_SLOW */
+ case 128 + 30:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!blind && see_either) monster_msg("%^s drains power from %s%s muscles.", m_name, t_name,
+ (!strcmp(t_name, "it") ? "s" : "'s"));
+ if (tr_ptr->flags1 & RF1_UNIQUE)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else
+ {
+ t_ptr->mspeed -= 10;
+ if (see_t) monster_msg("%^s starts moving slower.", t_name);
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF5_HOLD */
+ case 128 + 31:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!blind && see_m) monster_msg("%^s stares intently at %s.", m_name, t_name);
+ if ((tr_ptr->flags1 & RF1_UNIQUE) ||
+ (tr_ptr->flags3 & RF3_NO_STUN))
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else if (t_ptr->level > randint((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10)
+ {
+ if (see_t) monster_msg("%^s is unaffected.", t_name);
+ }
+ else
+ {
+ t_ptr->stunned += randint(4) + 4;
+ if (see_t) monster_msg("%^s is paralyzed!", t_name);
+ }
+ wake_up = TRUE;
+ break;
+ }
+
+
+ /* RF6_HASTE */
+ case 160 + 0:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m)
+ {
+ monster_msg("%^s mumbles.", m_name);
+ }
+ else
+ {
+ monster_msg("%^s concentrates on %s body.", m_name, m_poss);
+ }
+
+ /* Allow quick speed increases to base+10 */
+ if (m_ptr->mspeed < m_ptr->speed + 10)
+ {
+ if (see_m) monster_msg("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 10;
+ }
+
+ /* Allow small speed increases to base+20 */
+ else if (m_ptr->mspeed < m_ptr->speed + 20)
+ {
+ if (see_m) monster_msg("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 2;
+ }
+
+ break;
+ }
+
+ /* RF6_HAND_DOOM */
+ case 160 + 1:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (!see_m) monster_msg("You hear someone invoke the Hand of Doom!");
+ else if (!blind) monster_msg("%^s invokes the Hand of Doom on %s.", m_name, t_name);
+ else
+ monster_msg ("You hear someone invoke the Hand of Doom!");
+ if (tr_ptr->flags1 & RF1_UNIQUE)
+ {
+ if (!blind && see_t) monster_msg("^%s is unaffected!", t_name);
+ }
+ else
+ {
+ if (((m_ptr->level) + randint(20)) >
+ ((t_ptr->level) + 10 + randint(20)))
+ {
+ t_ptr->hp = t_ptr->hp
+ - (((s32b) ((65 + randint(25)) * (t_ptr->hp))) / 100);
+ if (t_ptr->hp < 1) t_ptr->hp = 1;
+ }
+ else
+ {
+ if (see_t) monster_msg("%^s resists!", t_name);
+ }
+ }
+
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF6_HEAL */
+ case 160 + 2:
+ {
+ if (disturb_other) disturb(1, 0);
+
+ /* Message */
+ if (blind || !see_m)
+ {
+ monster_msg("%^s mumbles.", m_name);
+ }
+ else
+ {
+ monster_msg("%^s concentrates on %s wounds.", m_name, m_poss);
+ }
+
+ /* Heal some */
+ m_ptr->hp += (rlev * 6);
+
+ /* Fully healed */
+ if (m_ptr->hp >= m_ptr->maxhp)
+ {
+ /* Fully healed */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Message */
+ if (seen)
+ {
+ monster_msg("%^s looks completely healed!", m_name);
+ }
+ else
+ {
+ monster_msg("%^s sounds completely healed!", m_name);
+ }
+ }
+
+ /* Partially healed */
+ else
+ {
+ /* Message */
+ if (seen)
+ {
+ monster_msg("%^s looks healthier.", m_name);
+ }
+ else
+ {
+ monster_msg("%^s sounds healthier.", m_name);
+ }
+ }
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Cancel fear */
+ if (m_ptr->monfear)
+ {
+ /* Cancel fear */
+ m_ptr->monfear = 0;
+
+ /* Message */
+ if (see_m) monster_msg("%^s recovers %s courage.", m_name, m_poss);
+ }
+
+ break;
+ }
+
+ /* RF6_S_ANIMALS */
+ case 160 + 3:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons some animals!", m_name);
+ for (k = 0; k < 4; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_BLINK */
+ case 160 + 4:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (see_m) monster_msg("%^s blinks away.", m_name);
+ teleport_away(m_idx, 10);
+ break;
+ }
+
+ /* RF6_TPORT */
+ case 160 + 5:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ else
+ {
+ if (disturb_other) disturb(1, 0);
+ if (see_m) monster_msg("%^s teleports away.", m_name);
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ break;
+ }
+ }
+
+ /* RF6_TELE_TO */
+ case 160 + 6:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_TELE_AWAY */
+ case 160 + 7:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break;
+
+ if (!direct) break;
+ else
+ {
+ bool resists_tele = FALSE;
+ if (disturb_other) disturb(1, 0);
+ monster_msg("%^s teleports %s away.", m_name, t_name);
+
+
+ if (tr_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (tr_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (see_t)
+ {
+ tr_ptr->r_flags3 |= RF3_RES_TELE;
+ monster_msg("%^s is unaffected!", t_name);
+ }
+ resists_tele = TRUE;
+ }
+ else if (t_ptr->level > randint(100))
+ {
+ if (see_t)
+ {
+ tr_ptr->r_flags3 |= RF3_RES_TELE;
+ monster_msg("%^s resists!", t_name);
+ }
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ teleport_away(t_idx, MAX_SIGHT * 2 + 5);
+ }
+ }
+
+ break;
+ }
+
+ /* RF6_TELE_LEVEL */
+ case 160 + 8:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_DARKNESS */
+ case 160 + 9:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1, 0);
+ if (blind) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s gestures in shadow.", m_name);
+ if (seen)
+ monster_msg("%^s is surrounded by darkness.", t_name);
+ (void)project(m_idx, 3, y, x, 0, GF_DARK_WEAK, PROJECT_GRID | PROJECT_KILL);
+ /* Lite up the room */
+ unlite_room(y, x);
+ break;
+ }
+
+ /* RF6_TRAPS */
+ case 160 + 10:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_FORGET */
+ case 160 + 11:
+ {
+ /* Not implemented */
+ break;
+ }
+
+ /* RF6_ANIM_DEAD */
+ case 160 + 12:
+ {
+ break;
+ }
+
+ /* RF6_S_BUG */
+ case 160 + 13:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically codes some software bugs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_BUG, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_BUG);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_RNG */
+ case 160 + 14:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically codes some RNGs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_RNG, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_RNG);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+
+ /* RF6_S_THUNDERLORD */
+ case 160 + 15:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a Thunderlord!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_THUNDERLORD, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_THUNDERLORD);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_SUMMON_KIN */
+ case 160 + 16:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons %s %s.",
+ m_name, m_poss,
+ ((r_ptr->flags1) & RF1_UNIQUE ?
+ "minions" : "kin"));
+ summon_kin_type = r_ptr->d_char; /* Big hack */
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_KIN, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_KIN);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+
+
+ break;
+ }
+
+ /* RF6_S_HI_DEMON */
+ case 160 + 17:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons greater demons!", m_name);
+ if (blind && count) monster_msg("You hear heavy steps nearby.");
+ if (friendly)
+ summon_specific_friendly(y, x, rlev, SUMMON_HI_DEMON, TRUE);
+ else
+ summon_cyber();
+ break;
+ }
+
+ /* RF6_S_MONSTER */
+ case 160 + 18:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons help!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_MONSTERS */
+ case 160 + 19:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons monsters!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANT */
+ case 160 + 20:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons ants.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANT, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANT);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_SPIDER */
+ case 160 + 21:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons spiders.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_SPIDER, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_SPIDER);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HOUND */
+ case 160 + 22:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons hounds.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HOUND, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HOUND);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HYDRA */
+ case 160 + 23:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons hydras.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HYDRA, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HYDRA);
+ }
+ if (blind && count) monster_msg("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANGEL */
+ case 160 + 24:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an angel!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_ANGEL, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_ANGEL);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DEMON */
+ case 160 + 25:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a demon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_DEMON, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_DEMON);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_UNDEAD */
+ case 160 + 26:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an undead adversary!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_UNDEAD, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_UNDEAD);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DRAGON */
+ case 160 + 27:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a dragon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_DRAGON, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_DRAGON);
+ }
+ if (blind && count) monster_msg("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HI_UNDEAD */
+ case 160 + 28:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons greater undead!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ monster_msg("You hear many creepy things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_HI_DRAGON */
+ case 160 + 29:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons ancient dragons!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HI_DRAGON_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HI_DRAGON);
+ }
+ if (blind && count)
+ {
+ monster_msg("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_WRAITH */
+ case 160 + 30:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a wraith!", m_name);
+
+
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_WRAITH);
+ }
+
+#if 0
+ /* these are not Lords of Amber... */
+ for (k = 0; k < 12; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+#endif
+
+ if (blind && count)
+ {
+ monster_msg("You hear immortal beings appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_UNIQUE */
+ case 160 + 31:
+ {
+ if (disturb_other) disturb(1, 0);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons special opponents!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ if (!friendly)
+ count += summon_specific(y, x, rlev, SUMMON_UNIQUE);
+ }
+ for (k = 0; k < 8; k++)
+ {
+ if (friendly)
+ count += summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE);
+ else
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ monster_msg("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+ }
+
+ if (wake_up)
+ {
+ t_ptr->csleep = 0;
+ }
+
+
+ /* Remember what the monster did, if we saw it */
+ if (seen)
+ {
+ /* Inate spell */
+ if (thrown_spell < 32*4)
+ {
+ r_ptr->r_flags4 |= (1L << (thrown_spell - 32 * 3));
+ if (r_ptr->r_cast_inate < MAX_UCHAR) r_ptr->r_cast_inate++;
+ }
+
+ /* Bolt or Ball */
+ else if (thrown_spell < 32*5)
+ {
+ r_ptr->r_flags5 |= (1L << (thrown_spell - 32 * 4));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+
+ /* Special spell */
+ else if (thrown_spell < 32*6)
+ {
+ r_ptr->r_flags6 |= (1L << (thrown_spell - 32 * 5));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+ }
+
+ /* Always take note of monsters that kill you ---
+ * even accidentally */
+ if (death && (r_ptr->r_deaths < MAX_SHORT))
+ {
+ r_ptr->r_deaths++;
+ }
+
+ /* A spell was cast */
+ return (TRUE);
+ }
+
+ /* No enemy found */
+ return (FALSE);
+}
+
+
+void curse_equipment(int chance, int heavy_chance)
+{
+ bool changed = FALSE;
+ u32b o1, o2, o3, o4, esp, o5;
+ object_type * o_ptr =
+ &p_ptr->inventory[rand_range(INVEN_WIELD, INVEN_TOTAL - 1)];
+
+ if (randint(100) > chance) return;
+
+ if (!(o_ptr->k_idx)) return;
+
+ object_flags(o_ptr, &o1, &o2, &o3, &o4, &o5, &esp);
+
+
+ /* Extra, biased saving throw for blessed items */
+ if ((o3 & (TR3_BLESSED)) && (randint(888) > chance))
+ {
+ char o_name[256];
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("Your %s resist%s cursing!", o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+ /* Hmmm -- can we wear multiple items? If not, this is unnecessary */
+ return;
+ }
+
+ if ((randint(100) <= heavy_chance) &&
+ (o_ptr->name1 || o_ptr->name2 || o_ptr->art_name))
+ {
+ if (!(o3 & TR3_HEAVY_CURSE))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_HEAVY_CURSE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+ else
+ {
+ if (!(o_ptr->ident & (IDENT_CURSED)))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ if (changed)
+ {
+ msg_print("There is a malignant black aura surrounding you...");
+ if (o_ptr->note)
+ {
+ if (streq(quark_str(o_ptr->note), "uncursed"))
+ {
+ o_ptr->note = 0;
+ }
+ }
+ }
+}
+
+
+void curse_equipment_dg(int chance, int heavy_chance)
+{
+ bool changed = FALSE;
+ u32b o1, o2, o3, o4, esp, o5;
+ object_type * o_ptr =
+ &p_ptr->inventory[rand_range(INVEN_WIELD, INVEN_TOTAL - 1)];
+
+ if (randint(100) > chance) return;
+
+ if (!(o_ptr->k_idx)) return;
+
+ object_flags(o_ptr, &o1, &o2, &o3, &o4, &o5, &esp);
+
+
+ /* Extra, biased saving throw for blessed items */
+ if ((o3 & (TR3_BLESSED)) && (randint(888) > chance))
+ {
+ char o_name[256];
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("Your %s resist%s cursing!", o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+ /* Hmmm -- can we wear multiple items? If not, this is unnecessary */
+ /* DG -- Yes we can, in the quiver */
+ return;
+ }
+
+ if ((randint(100) <= heavy_chance) &&
+ (o_ptr->name1 || o_ptr->name2 || o_ptr->art_name))
+ {
+ if (!(o3 & TR3_HEAVY_CURSE))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_HEAVY_CURSE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->art_flags4 |= TR4_DG_CURSE;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+ else
+ {
+ if (!(o_ptr->ident & (IDENT_CURSED)))
+ changed = TRUE;
+ o_ptr->art_flags3 |= TR3_CURSED;
+ o_ptr->art_flags4 |= TR4_DG_CURSE;
+ o_ptr->ident |= IDENT_CURSED;
+ }
+
+ if (changed)
+ {
+ msg_print("There is a malignant black aura surrounding you...");
+ if (o_ptr->note)
+ {
+ if (streq(quark_str(o_ptr->note), "uncursed"))
+ {
+ o_ptr->note = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * Creatures can cast spells, shoot missiles, and breathe.
+ *
+ * Returns "TRUE" if a spell (or whatever) was (successfully) cast.
+ *
+ * XXX XXX XXX This function could use some work, but remember to
+ * keep it as optimized as possible, while retaining generic code.
+ *
+ * Verify the various "blind-ness" checks in the code.
+ *
+ * XXX XXX XXX Note that several effects should really not be "seen"
+ * if the player is blind. See also "effects.c" for other "mistakes".
+ *
+ * Perhaps monsters should breathe at locations *near* the player,
+ * since this would allow them to inflict "partial" damage.
+ *
+ * Perhaps smart monsters should decline to use "bolt" spells if
+ * there is a monster in the way, unless they wish to kill it.
+ *
+ * Note that, to allow the use of the "track_target" option at some
+ * later time, certain non-optimal things are done in the code below,
+ * including explicit checks against the "direct" variable, which is
+ * currently always true by the time it is checked, but which should
+ * really be set according to an explicit "projectable()" test, and
+ * the use of generic "x,y" locations instead of the player location,
+ * with those values being initialized with the player location.
+ *
+ * It will not be possible to "correctly" handle the case in which a
+ * monster attempts to attack a location which is thought to contain
+ * the player, but which in fact is nowhere near the player, since this
+ * might induce all sorts of messages about the attack itself, and about
+ * the effects of the attack, which the player might or might not be in
+ * a position to observe. Thus, for simplicity, it is probably best to
+ * only allow "faulty" attacks by a monster if one of the important grids
+ * (probably the initial or final grid) is in fact in view of the player.
+ * It may be necessary to actually prevent spell attacks except when the
+ * monster actually has line of sight to the player. Note that a monster
+ * could be left in a bizarre situation after the player ducked behind a
+ * pillar and then teleported away, for example.
+ *
+ * Note that certain spell attacks do not use the "project()" function
+ * but "simulate" it via the "direct" variable, which is always at least
+ * as restrictive as the "project()" function. This is necessary to
+ * prevent "blindness" attacks and such from bending around walls, etc,
+ * and to allow the use of the "track_target" option in the future.
+ *
+ * Note that this function attempts to optimize the use of spells for the
+ * cases in which the monster has no spells, or has spells but cannot use
+ * them, or has spells but they will have no "useful" effect. Note that
+ * this function has been an efficiency bottleneck in the past.
+ *
+ * Note the special "MFLAG_NICE" flag, which prevents a monster from using
+ * any spell attacks until the player has had a single chance to move.
+ */
+bool make_attack_spell(int m_idx)
+{
+ int k, chance, thrown_spell, rlev, failrate;
+ byte spell[96], num = 0;
+ u32b f4, f5, f6;
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ char m_name[80];
+ bool no_inate = FALSE;
+ int x, y;
+
+ /* Summon count */
+ int count = 0;
+
+ /* Extract the blind-ness */
+ bool blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Extract the "see-able-ness" */
+ bool seen = (!blind && m_ptr->ml);
+
+ /* Assume "normal" target */
+ bool normal = TRUE;
+
+ /* Assume "projectable" */
+ bool direct = TRUE;
+
+ /* Target location */
+ if (m_ptr->target > -1)
+ {
+ if (!m_ptr->target)
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+ else
+ {
+ return (FALSE);
+ }
+ }
+ else return FALSE;
+
+ /* Cannot cast spells when confused */
+ if (m_ptr->confused) return (FALSE);
+
+ /* Cannot cast spells when nice */
+ if (m_ptr->mflag & (MFLAG_NICE)) return (FALSE);
+ if (is_friend(m_ptr) >= 0) return (FALSE);
+
+ /* Cannot attack the player if mortal and player fated to never die by the ... */
+ if ((r_ptr->flags7 & RF7_MORTAL) && (p_ptr->no_mortal)) return (FALSE);
+
+ /* Hack -- Extract the spell probability */
+ chance = (r_ptr->freq_inate + r_ptr->freq_spell) / 2;
+
+ /* Not allowed to cast spells */
+ if (!chance) return (FALSE);
+
+ if (stupid_monsters)
+ {
+ /* Only do spells occasionally */
+ if (rand_int(100) >= chance) return (FALSE);
+ }
+ else
+ {
+ if (rand_int(100) >= chance) return (FALSE);
+
+ /* Sometimes forbid inate attacks (breaths) */
+ if (rand_int(100) >= (chance * 2)) no_inate = TRUE;
+ }
+
+ /* XXX XXX XXX Handle "track_target" option (?) */
+
+
+ /* Hack -- require projectable player */
+ if (normal)
+ {
+ /* Check range */
+ if (m_ptr->cdis > MAX_RANGE) return (FALSE);
+
+ /* Check path */
+ if (!projectable(m_ptr->fy, m_ptr->fx, y, x)) return (FALSE);
+ }
+
+ /* Extract the monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Extract the racial spell flags */
+ f4 = r_ptr->flags4;
+ f5 = r_ptr->flags5;
+ f6 = r_ptr->flags6;
+
+ if (!stupid_monsters)
+ {
+ /* Forbid inate attacks sometimes */
+ if (no_inate) f4 = 0L;
+ }
+
+ /* Hack -- allow "desperate" spells */
+ if ((r_ptr->flags2 & (RF2_SMART)) &&
+ (m_ptr->hp < m_ptr->maxhp / 10) &&
+ (rand_int(100) < 50))
+ {
+ /* Require intelligent spells */
+ f4 &= (RF4_INT_MASK);
+ f5 &= (RF5_INT_MASK);
+ f6 &= (RF6_INT_MASK);
+
+ /* No spells left */
+ if (!f4 && !f5 && !f6) return (FALSE);
+ }
+
+ /* Remove the "ineffective" spells */
+ remove_bad_spells(m_idx, &f4, &f5, &f6);
+
+ /* No spells left */
+ if (!f4 && !f5 && !f6) return (FALSE);
+
+ if (!stupid_monsters)
+ {
+ /* Check for a clean bolt shot */
+ if ((f4&(RF4_BOLT_MASK) || f5 & (RF5_BOLT_MASK) ||
+ f6&(RF6_BOLT_MASK)) &&
+ !(r_ptr->flags2 & (RF2_STUPID)) &&
+ !clean_shot(m_ptr->fy, m_ptr->fx, y, x))
+ {
+ /* Remove spells that will only hurt friends */
+ f4 &= ~(RF4_BOLT_MASK);
+ f5 &= ~(RF5_BOLT_MASK);
+ f6 &= ~(RF6_BOLT_MASK);
+ }
+
+ /* Check for a possible summon */
+ if ((f4 & (RF4_SUMMON_MASK) || f5 & (RF5_SUMMON_MASK) ||
+ f6 & (RF6_SUMMON_MASK)) &&
+ !(r_ptr->flags2 & (RF2_STUPID)) &&
+ !(summon_possible(y, x)))
+ {
+ /* Remove summoning spells */
+ f4 &= ~(RF4_SUMMON_MASK);
+ f5 &= ~(RF5_SUMMON_MASK);
+ f6 &= ~(RF6_SUMMON_MASK);
+ }
+
+ /* No spells left */
+ if (!f4 && !f5 && !f6) return (FALSE);
+ }
+
+ /* Extract the "inate" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f4 & (1L << k)) spell[num++] = k + 32 * 3;
+ }
+
+ /* Extract the "normal" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f5 & (1L << k)) spell[num++] = k + 32 * 4;
+ }
+
+ /* Extract the "bizarre" spells */
+ for (k = 0; k < 32; k++)
+ {
+ if (f6 & (1L << k)) spell[num++] = k + 32 * 5;
+ }
+
+ /* No spells left */
+ if (!num) return (FALSE);
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) return (FALSE);
+
+ /* Stop if player is leaving */
+ if (p_ptr->leaving) return (FALSE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0x00);
+
+ if (stupid_monsters)
+ {
+ /* Choose a spell to cast */
+ thrown_spell = spell[rand_int(num)];
+ }
+ else
+ {
+ thrown_spell = choose_attack_spell(m_idx, spell, num);
+
+ /* Abort if no spell was chosen */
+ if (!thrown_spell) return (FALSE);
+
+ /* Calculate spell failure rate */
+ failrate = 25 - (rlev + 3) / 4;
+
+ /* Hack -- Stupid monsters will never fail (for jellies and such) */
+ if (r_ptr->flags2 & (RF2_STUPID)) failrate = 0;
+
+ /* Check for spell failure (inate attacks never fail) */
+ if ((thrown_spell >= 128) && (rand_int(100) < failrate))
+ {
+ /* Message */
+ msg_format("%^s tries to cast a spell, but fails.", m_name);
+
+ return (TRUE);
+ }
+ }
+
+ /* Can the player disrupt its puny attempts? */
+ if ((p_ptr->antimagic_dis >= m_ptr->cdis) && (magik(p_ptr->antimagic)) && (thrown_spell >= 128))
+ {
+ char m_poss[80];
+
+ /* Get monster's possessive noun form ("the Illusionist's") */
+ monster_desc(m_poss, m_ptr, 0x06);
+
+ msg_format("Your anti-magic field disrupts %s spell.", m_poss);
+ }
+ else
+ {
+ char m_poss[80];
+ char ddesc[80];
+
+ /* Get the monster possessive ("his"/"her"/"its") */
+ monster_desc(m_poss, m_ptr, 0x22);
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+ /* Cast the spell. */
+ switch (thrown_spell)
+ {
+ /* RF4_SHRIEK */
+ case 96 + 0:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s makes a high pitched shriek.", m_name);
+ aggravate_monsters(m_idx);
+ break;
+ }
+
+ /* RF4_MULTIPLY */
+ case 96 + 1:
+ {
+ break;
+ }
+
+ /* RF4_S_ANIMAL */
+ case 96 + 2:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons an animal!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF4_ROCKET */
+ case 96 + 3:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s shoots something.", m_name);
+ else msg_format("%^s fires a rocket.", m_name);
+ breath(m_idx, GF_ROCKET,
+ ((m_ptr->hp / 4) > 800 ? 800 : (m_ptr->hp / 4)), 2);
+ update_smart_learn(m_idx, DRS_SHARD);
+ break;
+ }
+
+ /* RF4_ARROW_1 */
+ case 96 + 4:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires an arrow.", m_name);
+ bolt(m_idx, GF_ARROW, damroll(1, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_ARROW_2 */
+ case 96 + 5:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires an arrow!", m_name);
+ bolt(m_idx, GF_ARROW, damroll(3, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_ARROW_3 */
+ case 96 + 6:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires a missile.", m_name);
+ bolt(m_idx, GF_ARROW, damroll(5, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_ARROW_4 */
+ case 96 + 7:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s makes a strange noise.", m_name);
+ else msg_format("%^s fires a missile!", m_name);
+ bolt(m_idx, GF_ARROW, damroll(7, 6));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF4_BR_ACID */
+ case 96 + 8:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes acid.", m_name);
+ breath(m_idx, GF_ACID,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_ACID);
+ break;
+ }
+
+ /* RF4_BR_ELEC */
+ case 96 + 9:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes lightning.", m_name);
+ breath(m_idx, GF_ELEC,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_ELEC);
+ break;
+ }
+
+ /* RF4_BR_FIRE */
+ case 96 + 10:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes fire.", m_name);
+ breath(m_idx, GF_FIRE,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_FIRE);
+ break;
+ }
+
+ /* RF4_BR_COLD */
+ case 96 + 11:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes frost.", m_name);
+ breath(m_idx, GF_COLD,
+ ((m_ptr->hp / 3) > 1600 ? 1600 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_COLD);
+ break;
+ }
+
+ /* RF4_BR_POIS */
+ case 96 + 12:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes gas.", m_name);
+ breath(m_idx, GF_POIS,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+
+ /* RF4_BR_NETH */
+ case 96 + 13:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes nether.", m_name);
+ breath(m_idx, GF_NETHER,
+ ((m_ptr->hp / 6) > 550 ? 550 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_NETH);
+ break;
+ }
+
+ /* RF4_BR_LITE */
+ case 96 + 14:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes light.", m_name);
+ breath(m_idx, GF_LITE,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_LITE);
+ break;
+ }
+
+ /* RF4_BR_DARK */
+ case 96 + 15:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes darkness.", m_name);
+ breath(m_idx, GF_DARK,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_DARK);
+ break;
+ }
+
+ /* RF4_BR_CONF */
+ case 96 + 16:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes confusion.", m_name);
+ breath(m_idx, GF_CONFUSION,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_CONF);
+ break;
+ }
+
+ /* RF4_BR_SOUN */
+ case 96 + 17:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes sound.", m_name);
+ breath(m_idx, GF_SOUND,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_SOUND);
+ break;
+ }
+
+ /* RF4_BR_CHAO */
+ case 96 + 18:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes chaos.", m_name);
+ breath(m_idx, GF_CHAOS,
+ ((m_ptr->hp / 6) > 600 ? 600 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_CHAOS);
+ break;
+ }
+
+ /* RF4_BR_DISE */
+ case 96 + 19:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes disenchantment.", m_name);
+ breath(m_idx, GF_DISENCHANT,
+ ((m_ptr->hp / 6) > 500 ? 500 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_DISEN);
+ break;
+ }
+
+ /* RF4_BR_NEXU */
+ case 96 + 20:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes nexus.", m_name);
+ breath(m_idx, GF_NEXUS,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_NEXUS);
+ break;
+ }
+
+ /* RF4_BR_TIME */
+ case 96 + 21:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes time.", m_name);
+ breath(m_idx, GF_TIME,
+ ((m_ptr->hp / 3) > 150 ? 150 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_INER */
+ case 96 + 22:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes inertia.", m_name);
+ breath(m_idx, GF_INERTIA,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_GRAV */
+ case 96 + 23:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes gravity.", m_name);
+ breath(m_idx, GF_GRAVITY,
+ ((m_ptr->hp / 3) > 200 ? 200 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BR_SHAR */
+ case 96 + 24:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes shards.", m_name);
+ breath(m_idx, GF_SHARDS,
+ ((m_ptr->hp / 6) > 400 ? 400 : (m_ptr->hp / 6)), 0);
+ update_smart_learn(m_idx, DRS_SHARD);
+ break;
+ }
+
+ /* RF4_BR_PLAS */
+ case 96 + 25:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes plasma.", m_name);
+ breath(m_idx, GF_PLASMA,
+ ((m_ptr->hp / 6) > 150 ? 150 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_WALL */
+ case 96 + 26:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes force.", m_name);
+ breath(m_idx, GF_FORCE,
+ ((m_ptr->hp / 6) > 200 ? 200 : (m_ptr->hp / 6)), 0);
+ break;
+ }
+
+ /* RF4_BR_MANA */
+ case 96 + 27:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes magical energy.", m_name);
+ breath(m_idx, GF_MANA,
+ ((m_ptr->hp / 3) > 250 ? 250 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+ /* RF4_BA_NUKE */
+ case 96 + 28:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a ball of radiation.", m_name);
+ breath(m_idx, GF_NUKE, (rlev + damroll(10, 6)), 2);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+ /* RF4_BR_NUKE */
+ case 96 + 29:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes toxic waste.", m_name);
+ breath(m_idx, GF_NUKE,
+ ((m_ptr->hp / 3) > 800 ? 800 : (m_ptr->hp / 3)), 0);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+ /* RF4_BA_CHAO */
+ case 96 + 30:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles frighteningly.", m_name);
+ else msg_format("%^s invokes a raw chaos.", m_name);
+ breath(m_idx, GF_CHAOS, (rlev * 2) + damroll(10, 10), 4);
+ update_smart_learn(m_idx, DRS_CHAOS);
+ break;
+ }
+
+ /* RF4_BR_DISI -> Disintegration breath! */
+ case 96 + 31:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s breathes.", m_name);
+ else msg_format("%^s breathes disintegration.", m_name);
+ breath(m_idx, GF_DISINTEGRATE,
+ ((m_ptr->hp / 3) > 300 ? 300 : (m_ptr->hp / 3)), 0);
+ break;
+ }
+
+
+
+ /* RF5_BA_ACID */
+ case 128 + 0:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts an acid ball.", m_name);
+ breath(m_idx, GF_ACID,
+ randint(rlev * 3) + 15, 2);
+ update_smart_learn(m_idx, DRS_ACID);
+ break;
+ }
+
+ /* RF5_BA_ELEC */
+ case 128 + 1:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a lightning ball.", m_name);
+ breath(m_idx, GF_ELEC,
+ randint(rlev * 3 / 2) + 8, 2);
+ update_smart_learn(m_idx, DRS_ELEC);
+ break;
+ }
+
+ /* RF5_BA_FIRE */
+ case 128 + 2:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a fire ball.", m_name);
+ breath(m_idx, GF_FIRE,
+ randint(rlev * 7 / 2) + 10, 2);
+ update_smart_learn(m_idx, DRS_FIRE);
+ break;
+ }
+
+ /* RF5_BA_COLD */
+ case 128 + 3:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a frost ball.", m_name);
+ breath(m_idx, GF_COLD,
+ randint(rlev * 3 / 2) + 10, 2);
+ update_smart_learn(m_idx, DRS_COLD);
+ break;
+ }
+
+ /* RF5_BA_POIS */
+ case 128 + 4:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a stinking cloud.", m_name);
+ breath(m_idx, GF_POIS,
+ damroll(12, 2), 2);
+ update_smart_learn(m_idx, DRS_POIS);
+ break;
+ }
+
+ /* RF5_BA_NETH */
+ case 128 + 5:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a nether ball.", m_name);
+ breath(m_idx, GF_NETHER,
+ (50 + damroll(10, 10) + rlev), 2);
+ update_smart_learn(m_idx, DRS_NETH);
+ break;
+ }
+
+ /* RF5_BA_WATE */
+ case 128 + 6:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s gestures fluidly.", m_name);
+ msg_print("You are engulfed in a whirlpool.");
+ breath(m_idx, GF_WATER,
+ randint(rlev * 5 / 2) + 50, 4);
+ break;
+ }
+
+ /* RF5_BA_MANA */
+ case 128 + 7:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles powerfully.", m_name);
+ else msg_format("%^s invokes a mana storm.", m_name);
+ breath(m_idx, GF_MANA,
+ (rlev * 5) + damroll(10, 10), 4);
+ break;
+ }
+
+ /* RF5_BA_DARK */
+ case 128 + 8:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles powerfully.", m_name);
+ else msg_format("%^s invokes a darkness storm.", m_name);
+ breath(m_idx, GF_DARK,
+ (rlev * 5) + damroll(10, 10), 4);
+ update_smart_learn(m_idx, DRS_DARK);
+ break;
+ }
+
+ /* RF5_DRAIN_MANA */
+ case 128 + 9:
+ {
+ if (!direct) break;
+ if (p_ptr->csp)
+ {
+ int r1;
+
+ /* Disturb if legal */
+ disturb(1, 0);
+
+ /* Basic message */
+ msg_format("%^s draws psychic energy from you!", m_name);
+
+ /* Attack power */
+ r1 = (randint(rlev) / 2) + 1;
+
+ /* Full drain */
+ if (r1 >= p_ptr->csp)
+ {
+ r1 = p_ptr->csp;
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Partial drain */
+ else
+ {
+ p_ptr->csp -= r1;
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Heal the monster */
+ if (m_ptr->hp < m_ptr->maxhp)
+ {
+ /* Heal */
+ m_ptr->hp += (6 * r1);
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Special message */
+ if (seen)
+ {
+ msg_format("%^s appears healthier.", m_name);
+ }
+ }
+ }
+ update_smart_learn(m_idx, DRS_MANA);
+ break;
+ }
+
+ /* RF5_MIND_BLAST */
+ case 128 + 10:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (!seen)
+ {
+ msg_print("You feel something focusing on your mind.");
+ }
+ else
+ {
+ msg_format("%^s gazes deep into your eyes.", m_name);
+ }
+
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ msg_print("Your mind is blasted by psionic energy.");
+
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+
+ if ((!p_ptr->resist_chaos) && (randint(3) == 1))
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+
+ take_sanity_hit(damroll(8, 8), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_BRAIN_SMASH */
+ case 128 + 11:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (!seen)
+ {
+ msg_print("You feel something focusing on your mind.");
+ }
+ else
+ {
+ msg_format("%^s looks deep into your eyes.", m_name);
+ }
+
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ msg_print("Your mind is blasted by psionic energy.");
+ take_sanity_hit(damroll(12, 15), ddesc);
+ if (!p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + 8 + rand_int(8));
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ if (!p_ptr->free_act)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4);
+ }
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+
+ if (!p_ptr->resist_chaos)
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_1 */
+ case 128 + 12:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s points at you and curses.", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ curse_equipment(33, 0);
+ take_hit(damroll(3, 8), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_2 */
+ case 128 + 13:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s points at you and curses horribly.", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ curse_equipment(50, 5);
+ take_hit(damroll(8, 8), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_3 */
+ case 128 + 14:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles loudly.", m_name);
+ else msg_format("%^s points at you, incanting terribly!", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ curse_equipment(80, 15);
+ take_hit(damroll(10, 15), ddesc);
+ }
+ break;
+ }
+
+ /* RF5_CAUSE_4 */
+ case 128 + 15:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s screams the word 'DIE!'", m_name);
+ else msg_format("%^s points at you, screaming the word DIE!", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ take_hit(damroll(15, 15), ddesc);
+ (void)set_cut(p_ptr->cut + damroll(10, 10));
+ }
+ break;
+ }
+
+ /* RF5_BO_ACID */
+ case 128 + 16:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a acid bolt.", m_name);
+ bolt(m_idx, GF_ACID, damroll(7, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_ACID);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_ELEC */
+ case 128 + 17:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a lightning bolt.", m_name);
+ bolt(m_idx, GF_ELEC, damroll(4, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_ELEC);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_FIRE */
+ case 128 + 18:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a fire bolt.", m_name);
+ bolt(m_idx, GF_FIRE, damroll(9, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_FIRE);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_COLD */
+ case 128 + 19:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a frost bolt.", m_name);
+ bolt(m_idx, GF_COLD, damroll(6, 8) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_COLD);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_POIS */
+ case 128 + 20:
+ {
+ /* XXX XXX XXX */
+ break;
+ }
+
+ /* RF5_BO_NETH */
+ case 128 + 21:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a nether bolt.", m_name);
+ bolt(m_idx, GF_NETHER, 30 + damroll(5, 5) + (rlev * 3) / 2);
+ update_smart_learn(m_idx, DRS_NETH);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_WATE */
+ case 128 + 22:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a water bolt.", m_name);
+ bolt(m_idx, GF_WATER, damroll(10, 10) + (rlev));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_MANA */
+ case 128 + 23:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a mana bolt.", m_name);
+ bolt(m_idx, GF_MANA, randint(rlev * 7 / 2) + 50);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_PLAS */
+ case 128 + 24:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a plasma bolt.", m_name);
+ bolt(m_idx, GF_PLASMA, 10 + damroll(8, 7) + (rlev));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_BO_ICEE */
+ case 128 + 25:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts an ice bolt.", m_name);
+ bolt(m_idx, GF_ICE, damroll(6, 6) + (rlev));
+ update_smart_learn(m_idx, DRS_COLD);
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_MISSILE */
+ case 128 + 26:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a magic missile.", m_name);
+ bolt(m_idx, GF_MISSILE, damroll(2, 6) + (rlev / 3));
+ update_smart_learn(m_idx, DRS_REFLECT);
+ break;
+ }
+
+ /* RF5_SCARE */
+ case 128 + 27:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles, and you hear scary noises.", m_name);
+ else msg_format("%^s casts a fearful illusion.", m_name);
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You refuse to be frightened.");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You refuse to be frightened.");
+ }
+ else
+ {
+ (void)set_afraid(p_ptr->afraid + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FEAR);
+ break;
+ }
+
+ /* RF5_BLIND */
+ case 128 + 28:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s casts a spell, burning your eyes!", m_name);
+ if (p_ptr->resist_blind)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ (void)set_blind(12 + rand_int(4));
+ }
+ update_smart_learn(m_idx, DRS_BLIND);
+ break;
+ }
+
+ /* RF5_CONF */
+ case 128 + 29:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles, and you hear puzzling noises.", m_name);
+ else msg_format("%^s creates a mesmerizing illusion.", m_name);
+ if (p_ptr->resist_conf)
+ {
+ msg_print("You disbelieve the feeble spell.");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You disbelieve the feeble spell.");
+ }
+ else
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_CONF);
+ break;
+ }
+
+ /* RF5_SLOW */
+ case 128 + 30:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s drains power from your muscles!", m_name);
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FREE);
+ break;
+ }
+
+ /* RF5_HOLD */
+ case 128 + 31:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s stares deep into your eyes!", m_name);
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_format("You resist the effects!");
+ }
+ else
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FREE);
+ break;
+ }
+
+
+
+ /* RF6_HASTE */
+ case 160 + 0:
+ {
+ disturb(1, 0);
+ if (blind)
+ {
+ msg_format("%^s mumbles.", m_name);
+ }
+ else
+ {
+ msg_format("%^s concentrates on %s body.", m_name, m_poss);
+ }
+
+ /* Allow quick speed increases to base+10 */
+ if (m_ptr->mspeed < m_ptr->speed + 10)
+ {
+ msg_format("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 10;
+ }
+
+ /* Allow small speed increases to base+20 */
+ else if (m_ptr->mspeed < m_ptr->speed + 20)
+ {
+ msg_format("%^s starts moving faster.", m_name);
+ m_ptr->mspeed += 2;
+ }
+
+ break;
+ }
+
+ /* RF6_HAND_DOOM */
+ case 160 + 1:
+ {
+ disturb(1, 0);
+ msg_format("%^s invokes the Hand of Doom!", m_name);
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_format("You resist the effects!");
+ }
+ else
+ {
+ int dummy = (((s32b) ((65 + randint(25)) * (p_ptr->chp))) / 100);
+ msg_print("Your feel your life fade away!");
+ take_hit(dummy, m_name);
+ curse_equipment(100, 20);
+
+ if (p_ptr->chp < 1) p_ptr->chp = 1;
+ }
+ break;
+ }
+
+ /* RF6_HEAL */
+ case 160 + 2:
+ {
+ disturb(1, 0);
+
+ /* Message */
+ if (blind)
+ {
+ msg_format("%^s mumbles.", m_name);
+ }
+ else
+ {
+ msg_format("%^s concentrates on %s wounds.", m_name, m_poss);
+ }
+
+ /* Heal some */
+ m_ptr->hp += (rlev * 6);
+
+ /* Fully healed */
+ if (m_ptr->hp >= m_ptr->maxhp)
+ {
+ /* Fully healed */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Message */
+ if (seen)
+ {
+ msg_format("%^s looks completely healed!", m_name);
+ }
+ else
+ {
+ msg_format("%^s sounds completely healed!", m_name);
+ }
+ }
+
+ /* Partially healed */
+ else
+ {
+ /* Message */
+ if (seen)
+ {
+ msg_format("%^s looks healthier.", m_name);
+ }
+ else
+ {
+ msg_format("%^s sounds healthier.", m_name);
+ }
+ }
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Cancel fear */
+ if (m_ptr->monfear)
+ {
+ /* Cancel fear */
+ m_ptr->monfear = 0;
+
+ /* Message */
+ msg_format("%^s recovers %s courage.", m_name, m_poss);
+ }
+ break;
+ }
+
+ /* RF6_S_ANIMALS */
+ case 160 + 3:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons some animals!", m_name);
+ for (k = 0; k < 4; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANIMAL);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_BLINK */
+ case 160 + 4:
+ {
+ disturb(1, 0);
+ msg_format("%^s blinks away.", m_name);
+ teleport_away(m_idx, 10);
+ break;
+ }
+
+ /* RF6_TPORT */
+ case 160 + 5:
+ {
+ disturb(1, 0);
+ msg_format("%^s teleports away.", m_name);
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ break;
+ }
+
+ /* RF6_TELE_TO */
+ case 160 + 6:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s commands you to return.", m_name);
+ teleport_player_to(m_ptr->fy, m_ptr->fx);
+ break;
+ }
+
+ /* RF6_TELE_AWAY */
+ case 160 + 7:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s teleports you away.", m_name);
+ teleport_player(100);
+ break;
+ }
+
+ /* RF6_TELE_LEVEL */
+ case 160 + 8:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles strangely.", m_name);
+ else msg_format("%^s gestures at your feet.", m_name);
+ if (p_ptr->resist_nexus)
+ {
+ msg_print("You are unaffected!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ teleport_player_level();
+ }
+ update_smart_learn(m_idx, DRS_NEXUS);
+ break;
+ }
+
+ /* RF6_DARKNESS */
+ case 160 + 9:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s gestures in shadow.", m_name);
+ (void)unlite_area(0, 3);
+ break;
+ }
+
+ /* RF6_TRAPS */
+ case 160 + 10:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles, and then cackles evilly.", m_name);
+ else msg_format("%^s casts a spell and cackles evilly.", m_name);
+ (void)trap_creation();
+ break;
+ }
+
+ /* RF6_FORGET */
+ case 160 + 11:
+ {
+ if (!direct) break;
+ disturb(1, 0);
+ msg_format("%^s tries to blank your mind.", m_name);
+
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else if (lose_all_info())
+ {
+ msg_print("Your memories fade away.");
+ }
+ break;
+ }
+
+ /* RF6_ANIM_DEAD */
+ case 160 + 12:
+ break;
+
+ /* RF6_S_BUG */
+ case 160 + 13:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically codes some software bugs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_BUG);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_RNG */
+ case 160 + 14:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically codes some RNGs.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_RNG);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_THUNDERLORD */
+ case 160 + 15:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons a Thunderlord!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_THUNDERLORD);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_SUMMON_KIN */
+ case 160 + 16:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons %s %s.",
+ m_name, m_poss,
+ ((r_ptr->flags1) & RF1_UNIQUE ?
+ "minions" : "kin"));
+ summon_kin_type = r_ptr->d_char; /* Big hack */
+
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_KIN);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+
+ break;
+ }
+
+ /* RF6_S_HI_DEMON */
+ case 160 + 17:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons greater demons!", m_name);
+ if (blind && count) msg_print("You hear heavy steps nearby.");
+ summon_cyber();
+ break;
+ }
+
+ /* RF6_S_MONSTER */
+ case 160 + 18:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons help!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_MONSTERS */
+ case 160 + 19:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons monsters!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, 0);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANT */
+ case 160 + 20:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons ants.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANT);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_SPIDER */
+ case 160 + 21:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons spiders.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_SPIDER);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HOUND */
+ case 160 + 22:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons hounds.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HOUND);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HYDRA */
+ case 160 + 23:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons hydras.", m_name);
+ for (k = 0; k < 6; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HYDRA);
+ }
+ if (blind && count) msg_print("You hear many things appear nearby.");
+ break;
+ }
+
+ /* RF6_S_ANGEL */
+ case 160 + 24:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons an angel!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_ANGEL);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DEMON */
+ case 160 + 25:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons a demon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_DEMON);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_UNDEAD */
+ case 160 + 26:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons an undead adversary!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_UNDEAD);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_DRAGON */
+ case 160 + 27:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons a dragon!", m_name);
+ for (k = 0; k < 1; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_DRAGON);
+ }
+ if (blind && count) msg_print("You hear something appear nearby.");
+ break;
+ }
+
+ /* RF6_S_HI_UNDEAD */
+ case 160 + 28:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons greater undead!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ msg_print("You hear many creepy things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_HI_DRAGON */
+ case 160 + 29:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons ancient dragons!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_DRAGON);
+ }
+ if (blind && count)
+ {
+ msg_print("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_WRAITH */
+ case 160 + 30:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons Wraith!", m_name);
+
+
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_WRAITH);
+ }
+
+#if 0
+ /* these are not Lords of Amber... */
+ for (k = 0; k < 12; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+#endif
+
+ if (blind && count)
+ {
+ msg_print("You hear immortal beings appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_UNIQUE */
+ case 160 + 31:
+ {
+ disturb(1, 0);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons special opponents!", m_name);
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_UNIQUE);
+ }
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_HI_UNDEAD);
+ }
+ if (blind && count)
+ {
+ msg_print("You hear many powerful things appear nearby.");
+ }
+ break;
+ }
+ }
+ }
+
+ /* Remember what the monster did to us */
+ if (seen)
+ {
+ /* Inate spell */
+ if (thrown_spell < 32*4)
+ {
+ r_ptr->r_flags4 |= (1L << (thrown_spell - 32 * 3));
+ if (r_ptr->r_cast_inate < MAX_UCHAR) r_ptr->r_cast_inate++;
+ }
+
+ /* Bolt or Ball */
+ else if (thrown_spell < 32*5)
+ {
+ r_ptr->r_flags5 |= (1L << (thrown_spell - 32 * 4));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+
+ /* Special spell */
+ else if (thrown_spell < 32*6)
+ {
+ r_ptr->r_flags6 |= (1L << (thrown_spell - 32 * 5));
+ if (r_ptr->r_cast_spell < MAX_UCHAR) r_ptr->r_cast_spell++;
+ }
+ }
+
+
+ /* Always take note of monsters that kill you */
+ if (death && (r_ptr->r_deaths < MAX_SHORT))
+ {
+ r_ptr->r_deaths++;
+ }
+
+ /* A spell was cast */
+ return (TRUE);
+}
+
+
+/*
+ * Returns whether a given monster will try to run from the player.
+ *
+ * Monsters will attempt to avoid very powerful players. See below.
+ *
+ * Because this function is called so often, little details are important
+ * for efficiency. Like not using "mod" or "div" when possible. And
+ * attempting to check the conditions in an optimal order. Note that
+ * "(x << 2) == (x * 4)" if "x" has enough bits to hold the result.
+ *
+ * Note that this function is responsible for about one to five percent
+ * of the processor use in normal conditions...
+ */
+static int mon_will_run(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+#ifdef ALLOW_TERROR
+
+ u16b p_lev, m_lev;
+ u16b p_chp, p_mhp;
+ u16b m_chp, m_mhp;
+ u32b p_val, m_val;
+
+#endif
+
+ /* Keep monsters from running too far away */
+ if (m_ptr->cdis > MAX_SIGHT + 5) return (FALSE);
+
+ /* Friends don't run away */
+ if (is_friend(m_ptr) >= 0) return (FALSE);
+
+ /* All "afraid" monsters will run away */
+ if (m_ptr->monfear) return (TRUE);
+
+#ifdef ALLOW_TERROR
+
+ /* Nearby monsters will not become terrified */
+ if (m_ptr->cdis <= 5) return (FALSE);
+
+ /* Examine player power (level) */
+ p_lev = p_ptr->lev;
+
+ /* Examine monster power (level plus morale) */
+ m_lev = m_ptr->level + (m_idx & 0x08) + 25;
+
+ /* Optimize extreme cases below */
+ if (m_lev > p_lev + 4) return (FALSE);
+ if (m_lev + 4 <= p_lev) return (TRUE);
+
+ /* Examine player health */
+ p_chp = p_ptr->chp;
+ p_mhp = p_ptr->mhp;
+
+ /* Examine monster health */
+ m_chp = m_ptr->hp;
+ m_mhp = m_ptr->maxhp;
+
+ /* Prepare to optimize the calculation */
+ p_val = (p_lev * p_mhp) + (p_chp << 2); /* div p_mhp */
+ m_val = (m_lev * m_mhp) + (m_chp << 2); /* div m_mhp */
+
+ /* Strong players scare strong monsters */
+ if (p_val * m_mhp > m_val * p_mhp) return (TRUE);
+
+#endif
+
+ /* Assume no terror */
+ return (FALSE);
+}
+
+
+
+
+#ifdef MONSTER_FLOW
+
+/*
+* Choose the "best" direction for "flowing"
+*
+* Note that ghosts and rock-eaters are never allowed to "flow",
+* since they should move directly towards the player.
+*
+* Prefer "non-diagonal" directions, but twiddle them a little
+* to angle slightly towards the player's actual location.
+*
+* Allow very perceptive monsters to track old "spoor" left by
+* previous locations occupied by the player. This will tend
+* to have monsters end up either near the player or on a grid
+* recently occupied by the player (and left via "teleport").
+*
+* Note that if "smell" is turned on, all monsters get vicious.
+*
+* Also note that teleporting away from a location will cause
+* the monsters who were chasing you to converge on that location
+* as long as you are still near enough to "annoy" them without
+* being close enough to chase directly. I have no idea what will
+* happen if you combine "smell" with low "aaf" values.
+*/
+
+#if 0
+static bool get_moves_aux(int m_idx, int *yp, int *xp)
+{
+ int i, y, x, y1, x1, when = 0, cost = 999;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Monster flowing disabled */
+ if (!flow_by_sound) return (FALSE);
+
+ /* Monster can go through rocks */
+ if (r_ptr->flags2 & (RF2_PASS_WALL)) return (FALSE);
+ if (r_ptr->flags2 & (RF2_KILL_WALL)) return (FALSE);
+
+ /* Monster location */
+ y1 = m_ptr->fy;
+ x1 = m_ptr->fx;
+
+ /* Monster grid */
+ c_ptr = &cave[y1][x1];
+
+ /* The player is not currently near the monster grid */
+ if (c_ptr->when < cave[p_ptr->py][p_ptr->px].when)
+ {
+ /* The player has never been near the monster grid */
+ if (!c_ptr->when) return (FALSE);
+
+ /* The monster is not allowed to track the player */
+ if (!flow_by_smell) return (FALSE);
+ }
+
+ /* Monster is too far away to notice the player */
+ if (c_ptr->cost > MONSTER_FLOW_DEPTH) return (FALSE);
+ if (c_ptr->cost > r_ptr->aaf) return (FALSE);
+
+ /* Hack -- Player can see us, run towards him */
+ if (player_has_los_bold(y1, x1)) return (FALSE);
+
+ /* Check nearby grids, diagonals first */
+ for (i = 7; i >= 0; i--)
+ {
+ /* Get the location */
+ y = y1 + ddy_ddd[i];
+ x = x1 + ddx_ddd[i];
+
+ /* Ignore illegal locations */
+ if (!cave[y][x].when) continue;
+
+ /* Ignore ancient locations */
+ if (cave[y][x].when < when) continue;
+
+ /* Ignore distant locations */
+ if (cave[y][x].cost > cost) continue;
+
+ /* Save the cost and time */
+ when = cave[y][x].when;
+ cost = cave[y][x].cost;
+
+ /* Hack -- Save the "twiddled" location */
+ (*yp) = p_ptr->py + 16 * ddy_ddd[i];
+ (*xp) = p_ptr->px + 16 * ddx_ddd[i];
+ }
+
+ /* No legal move (?) */
+ if (!when) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+#endif
+
+/*
+* Provide a location to flee to, but give the player a wide berth.
+*
+* A monster may wish to flee to a location that is behind the player,
+* but instead of heading directly for it, the monster should "swerve"
+* around the player so that he has a smaller chance of getting hit.
+*/
+static bool get_fear_moves_aux(int m_idx, int *yp, int *xp)
+{
+ int y, x, y1, x1, fy, fx, gy = 0, gx = 0;
+ int when = 0, score = -1;
+ int i;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Monster flowing disabled */
+ if (!flow_by_sound) return (FALSE);
+
+ /* Monster location */
+ fy = m_ptr->fy;
+ fx = m_ptr->fx;
+
+ /* Desired destination */
+ y1 = fy - (*yp);
+ x1 = fx - (*xp);
+
+ /* The player is not currently near the monster grid */
+ if (cave[fy][fx].when < cave[p_ptr->py][p_ptr->px].when)
+ {
+ /* No reason to attempt flowing */
+ return (FALSE);
+ }
+
+ /* Monster is too far away to use flow information */
+ if (cave[fy][fx].cost > MONSTER_FLOW_DEPTH) return (FALSE);
+ if (cave[fy][fx].cost > r_ptr->aaf) return (FALSE);
+
+ /* Check nearby grids, diagonals first */
+ for (i = 7; i >= 0; i--)
+ {
+ int dis, s;
+
+ /* Get the location */
+ y = fy + ddy_ddd[i];
+ x = fx + ddx_ddd[i];
+
+ /* Ignore illegal locations */
+ if (cave[y][x].when == 0) continue;
+
+ /* Ignore ancient locations */
+ if (cave[y][x].when < when) continue;
+
+ /* Calculate distance of this grid from our destination */
+ dis = distance(y, x, y1, x1);
+
+ /* Score this grid */
+ s = 5000 / (dis + 3) - 500 / (cave[y][x].cost + 1);
+
+ /* No negative scores */
+ if (s < 0) s = 0;
+
+ /* Ignore lower scores */
+ if (s < score) continue;
+
+ /* Save the score and time */
+ when = cave[y][x].when;
+ score = s;
+
+ /* Save the location */
+ gy = y;
+ gx = x;
+ }
+
+ /* No legal move (?) */
+ if (!when) return (FALSE);
+
+ /* Find deltas */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Success */
+ return (TRUE);
+}
+
+#endif /* MONSTER_FLOW */
+
+
+/*
+* Choose a "safe" location near a monster for it to run toward.
+*
+* A location is "safe" if it can be reached quickly and the player
+* is not able to fire into it (it isn't a "clean shot"). So, this will
+* cause monsters to "duck" behind walls. Hopefully, monsters will also
+* try to run towards corridor openings if they are in a room.
+*
+* This function may take lots of CPU time if lots of monsters are
+* fleeing.
+*
+* Return TRUE if a safe location is available.
+*/
+static bool find_safety(int m_idx, int *yp, int *xp)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ int y, x, d, dis;
+ int gy = 0, gx = 0, gdis = 0;
+
+ /* Start with adjacent locations, spread further */
+ for (d = 1; d < 10; d++)
+ {
+ /* Check nearby locations */
+ for (y = fy - d; y <= fy + d; y++)
+ {
+ for (x = fx - d; x <= fx + d; x++)
+ {
+ /* Skip illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Skip locations in a wall */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Check distance */
+ if (distance(y, x, fy, fx) != d) continue;
+
+ /* Check for "availability" (if monsters can flow) */
+ if (flow_by_sound)
+ {
+ /* Ignore grids very far from the player */
+ if (cave[y][x].when < cave[p_ptr->py][p_ptr->px].when) continue;
+
+ /* Ignore too-distant grids */
+ if (cave[y][x].cost > cave[fy][fx].cost + 2 * d) continue;
+ }
+
+ /* Check for absence of shot */
+ if (!projectable(y, x, p_ptr->py, p_ptr->px))
+ {
+ /* Calculate distance from player */
+ dis = distance(y, x, p_ptr->py, p_ptr->px);
+
+ /* Remember if further than previous */
+ if (dis > gdis)
+ {
+ gy = y;
+ gx = x;
+ gdis = dis;
+ }
+ }
+ }
+ }
+
+ /* Check for success */
+ if (gdis > 0)
+ {
+ /* Good location */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Found safe place */
+ return (TRUE);
+ }
+ }
+
+ /* No safe place */
+ return (FALSE);
+}
+
+
+/*
+ * Choose a good hiding place near a monster for it to run toward.
+ *
+ * Pack monsters will use this to "ambush" the player and lure him out
+ * of corridors into open space so they can swarm him.
+ *
+ * Return TRUE if a good location is available.
+ */
+static bool find_hiding(int m_idx, int *yp, int *xp)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ int y, x, d, dis;
+ int gy = 0, gx = 0, gdis = 999, min;
+
+ /* Closest distance to get */
+ min = distance(p_ptr->py, p_ptr->px, fy, fx) * 3 / 4 + 2;
+
+ /* Start with adjacent locations, spread further */
+ for (d = 1; d < 10; d++)
+ {
+ /* Check nearby locations */
+ for (y = fy - d; y <= fy + d; y++)
+ {
+ for (x = fx - d; x <= fx + d; x++)
+ {
+ /* Skip illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Skip locations in a wall */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Check distance */
+ if (distance(y, x, fy, fx) != d) continue;
+
+ /* Check for hidden, available grid */
+ if (!player_can_see_bold(y, x) && clean_shot(fy, fx, y, x))
+ {
+ /* Calculate distance from player */
+ dis = distance(y, x, p_ptr->py, p_ptr->px);
+
+ /* Remember if closer than previous */
+ if (dis < gdis && dis >= min)
+ {
+ gy = y;
+ gx = x;
+ gdis = dis;
+ }
+ }
+ }
+ }
+
+ /* Check for success */
+ if (gdis < 999)
+ {
+ /* Good location */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Found good place */
+ return (TRUE);
+ }
+ }
+
+ /* No good place */
+ return (FALSE);
+}
+
+
+/* Find an appropriate corpse */
+void find_corpse(monster_type *m_ptr, int *y, int *x)
+{
+ int k, last = -1;
+
+ for (k = 0; k < max_o_idx; k++)
+ {
+ object_type *o_ptr = &o_list[k];
+ monster_race *rt_ptr, *rt2_ptr;
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->tval != TV_CORPSE) continue;
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_SKELETON)) continue;
+
+ rt_ptr = &r_info[o_ptr->pval2];
+
+ /* Cannot incarnate into a higher level monster */
+ if (rt_ptr->level > m_ptr->level) continue;
+
+ /* Must be in LOS */
+ if (!los(m_ptr->fy, m_ptr->fx, o_ptr->iy, o_ptr->ix)) continue;
+
+ if (last != -1)
+ {
+ rt2_ptr = &r_info[o_list[last].pval2];
+ if (rt_ptr->level > rt2_ptr->level) last = k;
+ else continue;
+ }
+ else
+ {
+ last = k;
+ }
+ }
+
+ /* Must be ok now */
+ if (last != -1)
+ {
+ *y = o_list[last].iy;
+ *x = o_list[last].ix;
+ }
+}
+
+/*
+ * Choose target
+ */
+static void get_target_monster(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int i, t = -1, d = 9999;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[i];
+ /* hack should call the function for ego monsters ... but no_target i not meant to be added by ego and it speeds up the code */
+ monster_race *rt_ptr = &r_info[t_ptr->r_idx];
+ int dd;
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (m_idx == i) continue;
+
+ /* Cannot be targeted */
+ if (rt_ptr->flags7 & RF7_NO_TARGET) continue;
+
+ if (is_enemy(m_ptr, t_ptr) && (los(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx) &&
+ ((dd = distance(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) < d)))
+ {
+ t = i;
+ d = dd;
+ }
+ }
+ /* Hack */
+ if ((is_friend(m_ptr) < 0) && los(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) && (distance(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) < d)) t = 0;
+
+ m_ptr->target = t;
+}
+
+/*
+ * Choose "logical" directions for monster movement
+ */
+static bool get_moves(int m_idx, int *mm)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int y, ay, x, ax;
+
+ int move_val = 0;
+
+ int y2 = p_ptr->py;
+ int x2 = p_ptr->px;
+ bool done = FALSE;
+
+#if 0
+
+#ifdef MONSTER_FLOW
+ /* Flow towards the player */
+ if (flow_by_sound)
+ {
+ /* Flow towards the player */
+ (void)get_moves_aux(m_idx, &y2, &x2);
+ }
+#endif
+
+ /* Extract the "pseudo-direction" */
+ y = m_ptr->fy - y2;
+ x = m_ptr->fx - x2;
+
+
+
+#else
+
+ /* Oups get nearer */
+ if ((is_friend(m_ptr) > 0) && (m_ptr->cdis > p_ptr->pet_follow_distance))
+ {
+ y2 = p_ptr->py;
+ x2 = p_ptr->px;
+ }
+ /* Use the target */
+ else if (!m_ptr->target)
+ {
+ y2 = p_ptr->py;
+ x2 = p_ptr->px;
+ }
+ else if (m_ptr->target > 0)
+ {
+ y2 = m_list[m_ptr->target].fy;
+ x2 = m_list[m_ptr->target].fx;
+ }
+
+ /* Hack doppleganger confuses monsters(even pets) */
+ if (doppleganger)
+ {
+ if (magik(70))
+ {
+ y2 = m_list[doppleganger].fy;
+ x2 = m_list[doppleganger].fx;
+ }
+ }
+
+ /* A possessor is not interrested in the player, it only wants a corpse */
+ if (r_ptr->flags7 & RF7_POSSESSOR)
+ {
+ find_corpse(m_ptr, &y2, &x2);
+ }
+
+ /* Let quests redefine AI */
+ if (r_ptr->flags7 & RF7_AI_SPECIAL)
+ {
+ if (process_hooks_ret(HOOK_MONSTER_AI, "dd", "(d)", m_idx))
+ {
+ y2 = process_hooks_return[0].num;
+ x2 = process_hooks_return[1].num;
+ }
+ }
+
+ if (m_idx == p_ptr->control)
+ {
+ if ((r_ptr->flags7 & RF7_AI_PLAYER) || magik(85))
+ {
+ if (distance(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx) < 50)
+ {
+ y2 = m_ptr->fy + ddy[p_ptr->control_dir];
+ x2 = m_ptr->fx + ddx[p_ptr->control_dir];
+ }
+ }
+ }
+
+ /* Extract the "pseudo-direction" */
+ y = m_ptr->fy - y2;
+ x = m_ptr->fx - x2;
+
+ /* Tease the player */
+ if (r_ptr->flags7 & RF7_AI_ANNOY)
+ {
+ if (distance(m_ptr->fy, m_ptr->fx, y2, x2) < 4)
+ {
+ y = -y;
+ x = -x;
+ }
+ }
+
+ /* Death orbs .. */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ if (!los(m_ptr->fy, m_ptr->fx, y2, x2))
+ {
+ return (FALSE);
+ }
+ }
+
+ if (!stupid_monsters && (is_friend(m_ptr) < 0))
+ {
+ int tx = x2, ty = y2;
+
+ /*
+ * Animal packs try to get the player out of corridors
+ * (...unless they can move through walls -- TY)
+ */
+ if ((r_ptr->flags1 & RF1_FRIENDS) &&
+ (r_ptr->flags3 & RF3_ANIMAL) &&
+ !((r_ptr->flags2 & (RF2_PASS_WALL)) ||
+ (r_ptr->flags2 & (RF2_KILL_WALL))))
+ {
+ int i, room = 0;
+
+ /* Count room grids next to player */
+ for (i = 0; i < 8; i++)
+ {
+ /* Check grid */
+ if (cave[ty + ddy_ddd[i]][tx + ddx_ddd[i]].info & (CAVE_ROOM))
+ {
+ /* One more room grid */
+ room++;
+ }
+ }
+
+ /* Not in a room and strong player */
+ if ((room < 8) && (p_ptr->chp > ((p_ptr->mhp * 3) / 4)))
+ {
+ /* Find hiding place */
+ if (find_hiding(m_idx, &y, &x)) done = TRUE;
+ }
+ }
+
+ /* Monster groups try to surround the player */
+ if (!done && (r_ptr->flags1 & RF1_FRIENDS))
+ {
+ int i;
+
+ /* Find an empty square near the target to fill */
+ for (i = 0; i < 8; i++)
+ {
+ /* Pick squares near target (semi-randomly) */
+ y2 = ty + ddy_ddd[(m_idx + i) & 7];
+ x2 = tx + ddx_ddd[(m_idx + i) & 7];
+
+ /* Already there? */
+ if ((m_ptr->fy == y2) && (m_ptr->fx == x2))
+ {
+ /* Attack the target */
+ y2 = ty;
+ x2 = tx;
+
+ break;
+ }
+
+ /* Ignore filled grids */
+ if (!cave_empty_bold(y2, x2)) continue;
+
+ /* Try to fill this hole */
+ break;
+ }
+
+ /* Extract the new "pseudo-direction" */
+ y = m_ptr->fy - y2;
+ x = m_ptr->fx - x2;
+
+ /* Done */
+ done = TRUE;
+ }
+ }
+
+ /* Apply fear if possible and necessary */
+ if ((stupid_monsters) || (is_friend(m_ptr) > 0))
+ {
+ if (mon_will_run(m_idx))
+ {
+ /* XXX XXX Not very "smart" */
+ y = ( -y), x = ( -x);
+ }
+ }
+ else
+ {
+ if (!done && mon_will_run(m_idx))
+ {
+ /* Try to find safe place */
+ if (!find_safety(m_idx, &y, &x))
+ {
+ /* This is not a very "smart" method XXX XXX */
+ y = ( -y);
+ x = ( -x);
+ }
+ else
+ {
+ /* Attempt to avoid the player */
+ if (flow_by_sound)
+ {
+ /* Adjust movement */
+ (void)get_fear_moves_aux(m_idx, &y, &x);
+ }
+ }
+ }
+ }
+
+
+ if (!stupid_monsters)
+ {
+ /* Check for no move */
+ if (!x && !y) return (FALSE);
+ }
+#endif
+ /* Extract the "absolute distances" */
+ ax = ABS(x);
+ ay = ABS(y);
+
+ /* Do something weird */
+ if (y < 0) move_val += 8;
+ if (x > 0) move_val += 4;
+
+ /* Prevent the diamond maneuvre */
+ if (ay > (ax << 1))
+ {
+ move_val++;
+ move_val++;
+ }
+ else if (ax > (ay << 1))
+ {
+ move_val++;
+ }
+
+ /* Extract some directions */
+ switch (move_val)
+ {
+ case 0:
+ mm[0] = 9;
+ if (ay > ax)
+ {
+ mm[1] = 8;
+ mm[2] = 6;
+ mm[3] = 7;
+ mm[4] = 3;
+ }
+ else
+ {
+ mm[1] = 6;
+ mm[2] = 8;
+ mm[3] = 3;
+ mm[4] = 7;
+ }
+ break;
+ case 1:
+ case 9:
+ mm[0] = 6;
+ if (y < 0)
+ {
+ mm[1] = 3;
+ mm[2] = 9;
+ mm[3] = 2;
+ mm[4] = 8;
+ }
+ else
+ {
+ mm[1] = 9;
+ mm[2] = 3;
+ mm[3] = 8;
+ mm[4] = 2;
+ }
+ break;
+ case 2:
+ case 6:
+ mm[0] = 8;
+ if (x < 0)
+ {
+ mm[1] = 9;
+ mm[2] = 7;
+ mm[3] = 6;
+ mm[4] = 4;
+ }
+ else
+ {
+ mm[1] = 7;
+ mm[2] = 9;
+ mm[3] = 4;
+ mm[4] = 6;
+ }
+ break;
+ case 4:
+ mm[0] = 7;
+ if (ay > ax)
+ {
+ mm[1] = 8;
+ mm[2] = 4;
+ mm[3] = 9;
+ mm[4] = 1;
+ }
+ else
+ {
+ mm[1] = 4;
+ mm[2] = 8;
+ mm[3] = 1;
+ mm[4] = 9;
+ }
+ break;
+ case 5:
+ case 13:
+ mm[0] = 4;
+ if (y < 0)
+ {
+ mm[1] = 1;
+ mm[2] = 7;
+ mm[3] = 2;
+ mm[4] = 8;
+ }
+ else
+ {
+ mm[1] = 7;
+ mm[2] = 1;
+ mm[3] = 8;
+ mm[4] = 2;
+ }
+ break;
+ case 8:
+ mm[0] = 3;
+ if (ay > ax)
+ {
+ mm[1] = 2;
+ mm[2] = 6;
+ mm[3] = 1;
+ mm[4] = 9;
+ }
+ else
+ {
+ mm[1] = 6;
+ mm[2] = 2;
+ mm[3] = 9;
+ mm[4] = 1;
+ }
+ break;
+ case 10:
+ case 14:
+ mm[0] = 2;
+ if (x < 0)
+ {
+ mm[1] = 3;
+ mm[2] = 1;
+ mm[3] = 6;
+ mm[4] = 4;
+ }
+ else
+ {
+ mm[1] = 1;
+ mm[2] = 3;
+ mm[3] = 4;
+ mm[4] = 6;
+ }
+ break;
+ case 12:
+ mm[0] = 1;
+ if (ay > ax)
+ {
+ mm[1] = 2;
+ mm[2] = 4;
+ mm[3] = 3;
+ mm[4] = 7;
+ }
+ else
+ {
+ mm[1] = 4;
+ mm[2] = 2;
+ mm[3] = 7;
+ mm[4] = 3;
+ }
+ break;
+ }
+
+
+
+ /* Wants to move... */
+ return (TRUE);
+}
+
+
+int check_hit2(int power, int level, int ac)
+{
+ int i, k;
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Always miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Calculate the "attack quality" */
+ i = (power + (level * 3));
+
+ /* Power and Level compete against Armor */
+ if ((i > 0) && (randint(i) > ((ac * 3) / 4))) return (TRUE);
+
+ /* Assume miss */
+ return (FALSE);
+}
+
+
+/* Monster attacks monster */
+static bool monst_attack_monst(int m_idx, int t_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx], *t_ptr = &m_list[t_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ monster_race *tr_ptr = race_inf(t_ptr);
+ int ap_cnt;
+ int ac, rlev, pt;
+ char m_name[80], t_name[80];
+ char ddesc[80], temp[80];
+ bool blinked = FALSE, touched = FALSE;
+ bool explode = FALSE;
+ bool fear = FALSE;
+ byte y_saver = t_ptr->fy;
+ byte x_saver = t_ptr->fx;
+
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return FALSE;
+ if (tr_ptr->flags7 & RF7_IM_MELEE) return FALSE;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Get the "died from" information (i.e. "a kobold") */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!(m_ptr->ml || t_ptr->ml))
+ {
+ monster_msg("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool visible = FALSE;
+ bool obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = m_ptr->blow[ap_cnt].effect;
+ int method = m_ptr->blow[ap_cnt].method;
+ int d_dice = m_ptr->blow[ap_cnt].d_dice;
+ int d_side = m_ptr->blow[ap_cnt].d_side;
+
+ if (t_ptr == m_ptr) /* Paranoia */
+ {
+ if (wizard)
+ monster_msg("Monster attacking self?");
+ break;
+ }
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ if (m_ptr->ml) visible = TRUE;
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ power = 60;
+ break;
+ case RBE_POISON:
+ power = 5;
+ break;
+ case RBE_UN_BONUS:
+ power = 20;
+ break;
+ case RBE_UN_POWER:
+ power = 15;
+ break;
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ case RBE_ABOMINATION:
+ power = 20;
+ break;
+ }
+
+
+ /* Monster hits*/
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ if (disturb_other) disturb(1, 0);
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's %s.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on %s.";
+ touched = TRUE;
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ explode = TRUE;
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at %s.";
+ touched = FALSE;
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs %s for money.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = "insults %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = "moans at %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ act = "sings to %s.";
+ touched = FALSE;
+ t_ptr->csleep = 0;
+ break;
+ }
+ }
+
+ /* Message */
+ if (act)
+ {
+ strfmt(temp, act, t_name);
+ if (m_ptr->ml || t_ptr->ml)
+ monster_msg("%^s %s", m_name, temp);
+
+ }
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Hack need more punch against monsters */
+ damage *= 3;
+
+ pt = GF_MISSILE;
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ damage = 0;
+ pt = 0;
+ break;
+ }
+
+ case RBE_HURT:
+ case RBE_SANITY:
+ {
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+ break;
+ }
+
+ case RBE_POISON:
+ case RBE_DISEASE:
+ {
+ pt = GF_POIS;
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ case RBE_UN_POWER:
+ case RBE_ABOMINATION:
+ {
+ pt = GF_DISENCHANT;
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ case RBE_EAT_LITE:
+ {
+ pt = damage = 0;
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ case RBE_EAT_GOLD:
+ {
+ pt = damage = 0;
+ if (randint(2) == 1) blinked = TRUE;
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ pt = GF_ACID;
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ pt = GF_ELEC;
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ pt = GF_FIRE;
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ pt = GF_COLD;
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ break;
+ }
+
+ case RBE_HALLU:
+ case RBE_CONFUSE:
+ {
+ pt = GF_CONFUSION;
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ pt = GF_TURN_ALL;
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ pt = GF_OLD_SLEEP; /* sort of close... */
+ break;
+ }
+
+ case RBE_LOSE_STR:
+ case RBE_LOSE_INT:
+ case RBE_LOSE_WIS:
+ case RBE_LOSE_DEX:
+ case RBE_LOSE_CON:
+ case RBE_LOSE_CHR:
+ case RBE_LOSE_ALL:
+ case RBE_PARASITE:
+ {
+ break;
+ }
+ case RBE_SHATTER:
+ {
+ if (damage > 23)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(m_ptr->fy, m_ptr->fx, 8);
+ }
+ break;
+ }
+ case RBE_EXP_10:
+ case RBE_EXP_20:
+ case RBE_EXP_40:
+ case RBE_EXP_80:
+ {
+ pt = GF_NETHER;
+ break;
+ }
+ case RBE_TIME:
+ {
+ pt = GF_TIME;
+ break;
+ }
+ default:
+ {
+ pt = 0;
+ break;
+ }
+ }
+
+ if (pt)
+ {
+ /* Do damage if not exploding */
+ if (!explode)
+ {
+ project(m_idx, 0, t_ptr->fy, t_ptr->fx,
+ (pt == GF_OLD_SLEEP ? m_ptr->level : damage), pt, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ if (touched)
+ {
+ /* Aura fire */
+ if ((tr_ptr->flags2 & RF2_AURA_FIRE) &&
+ !(r_ptr->flags3 & RF3_IM_FIRE))
+ {
+ if (m_ptr->ml || t_ptr->ml)
+ {
+ blinked = FALSE;
+ monster_msg("%^s is suddenly very hot!", m_name);
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_FIRE;
+ }
+ project(t_idx, 0, m_ptr->fy, m_ptr->fx,
+ damroll (1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_FIRE, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ /* Aura elec */
+ if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && !(r_ptr->flags3 & (RF3_IM_ELEC)))
+ {
+ if (m_ptr->ml || t_ptr->ml)
+ {
+ blinked = FALSE;
+ monster_msg("%^s gets zapped!", m_name);
+ if (t_ptr->ml)
+ tr_ptr->r_flags2 |= RF2_AURA_ELEC;
+ }
+ project(t_idx, 0, m_ptr->fy, m_ptr->fx,
+ damroll (1 + ((t_ptr->level) / 26),
+ 1 + ((t_ptr->level) / 17)),
+ GF_ELEC, PROJECT_KILL | PROJECT_STOP);
+ }
+
+ }
+ }
+ }
+
+ /* Monster missed player */
+ else
+ {
+ /* Analyze failed attacks */
+ switch (method)
+ {
+ case RBM_HIT:
+ case RBM_TOUCH:
+ case RBM_PUNCH:
+ case RBM_KICK:
+ case RBM_CLAW:
+ case RBM_BITE:
+ case RBM_STING:
+ case RBM_XXX1:
+ case RBM_BUTT:
+ case RBM_CRUSH:
+ case RBM_ENGULF:
+ case RBM_CHARGE:
+ {
+ /* Visible monsters */
+ if (m_ptr->ml)
+ {
+ /* Disturbing */
+ disturb(1, 0);
+
+ /* Message */
+ monster_msg("%^s misses %s.", m_name, t_name);
+ }
+
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze "visible" monsters only */
+ if (visible)
+ {
+ /* Count "obvious" attacks (and ones that cause damage) */
+ if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10))
+ {
+ /* Count attacks of this type */
+ if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR)
+ {
+ r_ptr->r_blows[ap_cnt]++;
+ }
+ }
+ }
+ }
+
+ if (explode)
+ {
+ sound(SOUND_EXPLODE);
+ mon_take_hit_mon(m_idx, m_idx, m_ptr->hp + 1, &fear, " explodes into tiny shreds.");
+
+ blinked = FALSE;
+ }
+
+
+ /* Blink away */
+ if (blinked)
+ {
+ if (m_ptr->ml)
+ {
+ monster_msg("The thief flees laughing!");
+ }
+ else
+ {
+ monster_msg("You hear laughter!");
+ }
+
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Hack -- local "player stealth" value (see below)
+ */
+static u32b noise = 0L;
+
+/* Determine whether the player is invisible to a monster */
+static bool player_invis(monster_type * m_ptr)
+{
+ s16b inv, mlv;
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ inv = p_ptr->invis;
+
+ mlv = (s16b) m_ptr->level;
+
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ mlv += 10;
+ if (r_ptr->flags3 & RF3_DRAGON)
+ mlv += 20;
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ mlv += 15;
+ if (r_ptr->flags3 & RF3_DEMON)
+ mlv += 15;
+ if (r_ptr->flags3 & RF3_ANIMAL)
+ mlv += 15;
+ if (r_ptr->flags3 & RF3_ORC)
+ mlv -= 15;
+ if (r_ptr->flags3 & RF3_TROLL)
+ mlv -= 10;
+ if (r_ptr->flags2 & RF2_STUPID)
+ mlv /= 2;
+ if (r_ptr->flags2 & RF2_SMART)
+ mlv = (mlv * 5) / 4;
+ if (m_ptr->mflag & MFLAG_QUEST)
+ inv = 0;
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ inv = 0;
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ inv = 0;
+ if (mlv < 1)
+ mlv = 1;
+ return (inv >= randint(mlv*2));
+}
+
+/*
+ * Process a monster
+ *
+ * The monster is known to be within 100 grids of the player
+ *
+ * In several cases, we directly update the monster lore
+ *
+ * Note that a monster is only allowed to "reproduce" if there
+ * are a limited number of "reproducing" monsters on the current
+ * level. This should prevent the level from being "swamped" by
+ * reproducing monsters. It also allows a large mass of mice to
+ * prevent a louse from multiplying, but this is a small price to
+ * pay for a simple multiplication method.
+ *
+ * XXX Monster fear is slightly odd, in particular, monsters will
+ * fixate on opening a door even if they cannot open it. Actually,
+ * the same thing happens to normal monsters when they hit a door
+ *
+ * XXX XXX XXX In addition, monsters which *cannot* open or bash
+ * down a door will still stand there trying to open it...
+ *
+ * XXX Technically, need to check for monster in the way
+ * combined with that monster being in a wall (or door?)
+ *
+ * A "direction" of "5" means "pick a random direction".
+ */
+static void process_monster(int m_idx, bool is_frien)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ cave_type *c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+
+ int i, d, oy, ox, ny, nx;
+
+ int mm[8];
+
+ monster_type *y_ptr;
+
+ bool do_turn;
+ bool do_move;
+ bool do_view;
+
+ bool did_open_door;
+ bool did_bash_door;
+ bool did_take_item;
+ bool did_kill_item;
+ bool did_move_body;
+ bool did_kill_body;
+ bool did_pass_wall;
+ bool did_kill_wall;
+ bool gets_angry = FALSE;
+ bool inv;
+ bool xxx = FALSE;
+
+ inv = player_invis(m_ptr);
+
+ if (r_ptr->flags9 & RF9_DOPPLEGANGER) doppleganger = m_idx;
+
+ /* Handle "bleeding" */
+ if (m_ptr->bleeding)
+ {
+ int d = 1 + (m_ptr->maxhp / 50);
+ if (d > m_ptr->bleeding) d = m_ptr->bleeding;
+
+ /* Exit if the monster dies */
+ if (mon_take_hit(m_idx, d, &xxx, " bleeds to death.")) return;
+
+ /* Hack -- Recover from bleeding */
+ if (m_ptr->bleeding > d)
+ {
+ /* Recover somewhat */
+ m_ptr->bleeding -= d;
+ }
+
+ /* Fully recover */
+ else
+ {
+ /* Recover fully */
+ m_ptr->bleeding = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer bleeding.", m_name);
+
+ /* Hack -- Update the health bar */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+ }
+
+ /* Handle "poisoned" */
+ if (m_ptr->poisoned)
+ {
+ int d = (m_ptr->poisoned) / 10;
+ if (d < 1) d = 1;
+
+ /* Exit if the monster dies */
+ if (mon_take_hit(m_idx, d, &xxx, " dies of poison.")) return;
+
+ /* Hack -- Recover from bleeding */
+ if (m_ptr->poisoned > d)
+ {
+ /* Recover somewhat */
+ m_ptr->poisoned -= d;
+ }
+
+ /* Fully recover */
+ else
+ {
+ /* Recover fully */
+ m_ptr->poisoned = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer poisoned.", m_name);
+
+ /* Hack -- Update the health bar */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+ }
+ }
+ }
+
+ /* Handle "sleep" */
+ if (m_ptr->csleep)
+ {
+ u32b notice = 0;
+
+ /* Hack -- handle non-aggravation */
+ if (!p_ptr->aggravate) notice = rand_int(1024);
+
+ /* Hack -- See if monster "notices" player */
+ if ((notice * notice * notice) <= noise)
+ {
+ /* Hack -- amount of "waking" */
+ int d = 1;
+
+ /* Wake up faster near the player */
+ if (m_ptr->cdis < 50) d = (100 / m_ptr->cdis);
+
+ /* Hack -- handle aggravation */
+ if (p_ptr->aggravate) d = m_ptr->csleep;
+
+ /* Still asleep */
+ if (m_ptr->csleep > d)
+ {
+ /* Monster wakes up "a little bit" */
+ m_ptr->csleep -= d;
+
+ /* Notice the "not waking up" */
+ if (m_ptr->ml)
+ {
+ /* Hack -- Count the ignores */
+ if (r_ptr->r_ignore < MAX_UCHAR)
+ {
+ r_ptr->r_ignore++;
+ }
+ }
+ }
+
+ /* Just woke up */
+ else
+ {
+ /* Reset sleep counter */
+ m_ptr->csleep = 0;
+
+ /* Notice the "waking up" */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s wakes up.", m_name);
+
+ /* Hack -- Count the wakings */
+ if (r_ptr->r_wake < MAX_UCHAR)
+ {
+ r_ptr->r_wake++;
+ }
+ }
+ }
+ }
+
+ /* Still sleeping */
+ if (m_ptr->csleep) return;
+ }
+
+
+ /* Handle "stun" */
+ if (m_ptr->stunned)
+ {
+ int d = 1;
+
+ /* Make a "saving throw" against stun */
+ if (rand_int(5000) <= m_ptr->level * m_ptr->level)
+ {
+ /* Recover fully */
+ d = m_ptr->stunned;
+ }
+
+ /* Hack -- Recover from stun */
+ if (m_ptr->stunned > d)
+ {
+ /* Recover somewhat */
+ m_ptr->stunned -= d;
+ }
+
+ /* Fully recover */
+ else
+ {
+ /* Recover fully */
+ m_ptr->stunned = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer stunned.", m_name);
+ }
+ }
+
+ /* Still stunned */
+ if (m_ptr->stunned) return;
+ }
+
+
+ /* Handle confusion */
+ if (m_ptr->confused)
+ {
+ /* Amount of "boldness" */
+ int d = randint(m_ptr->level / 10 + 1);
+
+ /* Still confused */
+ if (m_ptr->confused > d)
+ {
+ /* Reduce the confusion */
+ m_ptr->confused -= d;
+ }
+
+ /* Recovered */
+ else
+ {
+ /* No longer confused */
+ m_ptr->confused = 0;
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s is no longer confused.", m_name);
+ }
+ }
+ }
+
+ /* No one wants to be your friend if you're aggravating */
+ if ((m_ptr->status > MSTATUS_NEUTRAL) && (m_ptr->status < MSTATUS_COMPANION) && (p_ptr->aggravate) && !(r_ptr->flags7 & RF7_PET))
+ gets_angry = TRUE;
+
+ /* Paranoia... no friendly uniques outside wizard mode -- TY */
+ if ((m_ptr->status > MSTATUS_NEUTRAL) && (m_ptr->status < MSTATUS_COMPANION) && !(wizard) &&
+ (r_ptr->flags1 & (RF1_UNIQUE)) && !(r_ptr->flags7 & RF7_PET))
+ gets_angry = TRUE;
+
+ if (gets_angry)
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s suddenly becomes hostile!", m_name);
+ change_side(m_ptr);
+ break;
+ }
+ }
+
+ /* Handle "fear" */
+ if (m_ptr->monfear)
+ {
+ /* Amount of "boldness" */
+ int d = randint(m_ptr->level / 10 + 1);
+
+ /* Still afraid */
+ if (m_ptr->monfear > d)
+ {
+ /* Reduce the fear */
+ m_ptr->monfear -= d;
+ }
+
+ /* Recover from fear, take note if seen */
+ else
+ {
+ /* No longer afraid */
+ m_ptr->monfear = 0;
+
+ /* Visual note */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+ char m_poss[80];
+
+ /* Acquire the monster name/poss */
+ monster_desc(m_name, m_ptr, 0);
+ monster_desc(m_poss, m_ptr, 0x22);
+
+ /* Dump a message */
+ msg_format("%^s recovers %s courage.", m_name, m_poss);
+ }
+ }
+ }
+
+ /* Get the origin */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+
+ /* Attempt to "multiply" if able and allowed */
+ if ((r_ptr->flags4 & (RF4_MULTIPLY)) && (num_repro < MAX_REPRO))
+ {
+ if (ai_multiply(m_idx)) return;
+ }
+
+ if (speak_unique)
+ {
+ if (randint(SPEAK_CHANCE) == 1)
+ {
+ if (player_has_los_bold(oy, ox) && (r_ptr->flags2 & RF2_CAN_SPEAK))
+ {
+ char m_name[80];
+ char monmessage[80];
+
+ /* Acquire the monster name/poss */
+ if (m_ptr->ml)
+ monster_desc(m_name, m_ptr, 0);
+ else
+ strcpy(m_name, "It");
+
+ /* xtra_line function by Matt Graham--allow uniques to */
+ /* say "unique" things based on their monster index. */
+ /* Try for the unique's lines in "monspeak.txt" first. */
+ /* 0 is SUCCESS, of course.... */
+
+ if (!process_hooks(HOOK_MON_SPEAK, "(d,s)", m_idx, m_name))
+ {
+ if (get_xtra_line("monspeak.txt", m_ptr, monmessage) != 0)
+ {
+ /* Get a message from old defaults if new don't work */
+
+ if (is_friend(m_ptr) > 0)
+ get_rnd_line("speakpet.txt", monmessage);
+ else if (m_ptr->monfear)
+ get_rnd_line("monfear.txt", monmessage);
+ else
+ get_rnd_line("bravado.txt", monmessage);
+ }
+ msg_format("%^s %s", m_name, monmessage);
+ }
+ }
+ }
+ }
+
+ /* Need a new target ? */
+ if ((m_ptr->target == -1) || magik(10)) get_target_monster(m_idx);
+
+
+ /* Attempt to cast a spell */
+ if (make_attack_spell(m_idx)) return;
+
+ /*
+ * Attempt to cast a spell at an enemy other than the player
+ * (may slow the game a smidgeon, but I haven't noticed.)
+ */
+ hack_message_pain_may_silent = TRUE;
+ if (monst_spell_monst(m_idx))
+ {
+ hack_message_pain_may_silent = FALSE;
+ return;
+ }
+ hack_message_pain_may_silent = FALSE;
+
+
+ /* Hack -- Assume no movement */
+ mm[0] = mm[1] = mm[2] = mm[3] = 0;
+ mm[4] = mm[5] = mm[6] = mm[7] = 0;
+
+ /* Confused -- 100% random */
+ if (m_ptr->confused || (inv == TRUE && m_ptr->target == 0))
+ {
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* 75% random movement */
+ else if ((r_ptr->flags1 & (RF1_RAND_50)) &&
+ (r_ptr->flags1 & (RF1_RAND_25)) &&
+ (rand_int(100) < 75))
+ {
+ /* Memorize flags */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_50);
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_25);
+
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* 50% random movement */
+ else if ((r_ptr->flags1 & (RF1_RAND_50)) &&
+ (rand_int(100) < 50))
+ {
+ /* Memorize flags */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_50);
+
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* 25% random movement */
+ else if ((r_ptr->flags1 & (RF1_RAND_25)) &&
+ (rand_int(100) < 25))
+ {
+ /* Memorize flags */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_RAND_25);
+
+ /* Try four "random" directions */
+ mm[0] = mm[1] = mm[2] = mm[3] = 5;
+ }
+
+ /* Normal movement */
+ else
+ {
+ if (stupid_monsters)
+ {
+ /* Logical moves */
+ get_moves(m_idx, mm);
+ }
+ else
+ {
+ /* Logical moves, may do nothing */
+ if (!get_moves(m_idx, mm)) return;
+ }
+ }
+
+ /* Paranoia -- quest code could delete it */
+ if (!c_ptr->m_idx) return;
+
+ /* Assume nothing */
+ do_turn = FALSE;
+ do_move = FALSE;
+ do_view = FALSE;
+
+ /* Assume nothing */
+ did_open_door = FALSE;
+ did_bash_door = FALSE;
+ did_take_item = FALSE;
+ did_kill_item = FALSE;
+ did_move_body = FALSE;
+ did_kill_body = FALSE;
+ did_pass_wall = FALSE;
+ did_kill_wall = FALSE;
+
+
+ /* Take a zero-terminated array of "directions" */
+ for (i = 0; mm[i]; i++)
+ {
+ /* Get the direction */
+ d = mm[i];
+
+ /* Hack -- allow "randomized" motion */
+ if (d == 5) d = ddd[rand_int(8)];
+
+ /* Get the destination */
+ ny = oy + ddy[d];
+ nx = ox + ddx[d];
+
+ /* Access that cave grid */
+ c_ptr = &cave[ny][nx];
+
+ /* Access that cave grid's contents */
+ y_ptr = &m_list[c_ptr->m_idx];
+
+
+ /* Floor is open? */
+ if (cave_floor_bold(ny, nx))
+ {
+ /* Go ahead and move */
+ do_move = TRUE;
+ }
+
+ /* Floor is trapped? */
+ else if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ /* Go ahead and move */
+ do_move = TRUE;
+ }
+
+ /* Hack -- check for Glyph of Warding */
+ if ((c_ptr->feat == FEAT_GLYPH) &&
+ !(r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+
+ /* Break the ward */
+ if (randint(BREAK_GLYPH) < m_ptr->level)
+ {
+ /* Describe observable breakage */
+ if (c_ptr->info & CAVE_MARK)
+ {
+ msg_print("The rune of protection is broken!");
+ }
+
+ /* Forget the rune */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Break the rune */
+ place_floor(ny, nx);
+
+ /* Allow movement */
+ do_move = TRUE;
+ }
+ }
+
+ /* Hack -- trees are obstacle */
+ else if ((cave[ny][nx].feat == FEAT_TREES) && (r_ptr->flags9 & RF9_KILL_TREES))
+ {
+ do_move = TRUE;
+
+ /* Forget the tree */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ cave_set_feat(ny, nx, FEAT_GRASS);
+ }
+
+ /* Hack -- player 'in' wall */
+ else if ((ny == p_ptr->py) && (nx == p_ptr->px))
+ {
+ do_move = TRUE;
+ }
+
+ else if (c_ptr->m_idx)
+ {
+ /* Possibly a monster to attack */
+ do_move = TRUE;
+ }
+
+ /* Permanent wall */
+ else if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT)
+ {
+ /* Nothing */
+ }
+
+
+ /* Some monsters can fly */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_LEVITATE) && (r_ptr->flags7 & (RF7_CAN_FLY)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+ }
+
+ /* Some monsters can fly */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_FLY) && (r_ptr->flags7 & (RF7_CAN_FLY)))
+ {
+ /* Pass through trees/... */
+ do_move = TRUE;
+ }
+
+ /* Monster moves through walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster went through a wall */
+ did_pass_wall = TRUE;
+ }
+
+ /* Monster destroys walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_KILL_WALL)))
+ {
+ /* Eat through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster destroyed a wall */
+ did_kill_wall = TRUE;
+
+ if (randint(GRINDNOISE) == 1)
+ {
+ msg_print("There is a grinding sound.");
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ cave_set_feat(ny, nx, FEAT_FLOOR);
+
+ /* Note changes to viewable region */
+ if (player_has_los_bold(ny, nx)) do_view = TRUE;
+ }
+
+ /* Monster moves through walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster went through a wall */
+ did_pass_wall = TRUE;
+ }
+
+ /* Monster moves through webs */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_WEB) &&
+ (r_ptr->flags7 & RF7_SPIDER))
+ {
+ /* Pass through webs */
+ do_move = TRUE;
+ }
+
+ /* Handle doors and secret doors */
+ else if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ bool may_bash = TRUE;
+
+ /* Take a turn */
+ do_turn = TRUE;
+
+ if ((r_ptr->flags2 & (RF2_OPEN_DOOR)) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_open_doors))
+ {
+ /* Closed doors and secret doors */
+ if ((c_ptr->feat == FEAT_DOOR_HEAD) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ /* The door is open */
+ did_open_door = TRUE;
+
+ /* Do not bash the door */
+ may_bash = FALSE;
+ }
+
+ /* Locked doors (not jammed) */
+ else if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08)
+ {
+ int k;
+
+ /* Door power */
+ k = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+#if 0
+ /* XXX XXX XXX Old test (pval 10 to 20) */
+ if (randint((m_ptr->hp + 1) * (50 + o_ptr->pval)) <
+ 40 * (m_ptr->hp - 10 - o_ptr->pval));
+#endif
+
+ /* Try to unlock it XXX XXX XXX */
+ if (rand_int(m_ptr->hp / 10) > k)
+ {
+ /* Unlock the door */
+ cave_set_feat(ny, nx, FEAT_DOOR_HEAD + 0x00);
+
+ /* Do not bash the door */
+ may_bash = FALSE;
+ }
+ }
+ }
+
+ /* Stuck doors -- attempt to bash them down if allowed */
+ if (may_bash && (r_ptr->flags2 & RF2_BASH_DOOR) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_open_doors))
+ {
+ int k;
+
+ /* Door power */
+ k = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+#if 0
+ /* XXX XXX XXX Old test (pval 10 to 20) */
+ if (randint((m_ptr->hp + 1) * (50 + o_ptr->pval)) <
+ 40 * (m_ptr->hp - 10 - o_ptr->pval));
+#endif
+
+ /* Attempt to Bash XXX XXX XXX */
+ if (rand_int(m_ptr->hp / 10) > k)
+ {
+ /* Message */
+ msg_print("You hear a door burst open!");
+
+ /* Disturb (sometimes) */
+ if (disturb_minor) disturb(0, 0);
+
+ /* The door was bashed open */
+ did_bash_door = TRUE;
+
+ /* Hack -- fall into doorway */
+ do_move = TRUE;
+ }
+ }
+
+
+ /* Deal with doors in the way */
+ if (did_open_door || did_bash_door)
+ {
+ /* It's no longer hidden */
+ cave[ny][nx].mimic = 0;
+
+ /* Break down the door */
+ if (did_bash_door && (rand_int(100) < 50))
+ {
+ cave_set_feat(ny, nx, FEAT_BROKEN);
+ }
+
+ /* Open the door */
+ else
+ {
+ cave_set_feat(ny, nx, FEAT_OPEN);
+ }
+
+ /* Handle viewable doors */
+ if (player_has_los_bold(ny, nx)) do_view = TRUE;
+ }
+ }
+ else if (do_move && (c_ptr->feat == FEAT_MINOR_GLYPH)
+ && !(r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+
+ /* Break the ward */
+ if (randint(BREAK_MINOR_GLYPH) < m_ptr->level)
+ {
+ /* Describe observable breakage */
+ if (c_ptr->info & CAVE_MARK)
+ {
+ if (ny == p_ptr->py && nx == p_ptr->px)
+ {
+ msg_print("The rune explodes!");
+ fire_ball(GF_MANA, 0,
+ 2 * ((p_ptr->lev / 2) + damroll(7, 7)), 2);
+ }
+ else
+ msg_print("An explosive rune was disarmed.");
+ }
+
+ /* Forget the rune */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Break the rune */
+ place_floor(ny, nx);
+
+ /* Allow movement */
+ do_move = TRUE;
+ }
+ }
+
+ /* Hack -- the Between teleport the monsters too */
+ else if (cave[ny][nx].feat == FEAT_BETWEEN)
+ {
+ nx = cave[ny][nx].special & 255;
+ ny = cave[ny][nx].special >> 8;
+ get_pos_player(10, &ny, &nx);
+
+ /* Access that cave grid */
+ c_ptr = &cave[ny][nx];
+
+ /* Access that cave grid's contents */
+ y_ptr = &m_list[c_ptr->m_idx];
+
+ if (!(r_ptr->flags3 & RF3_IM_COLD))
+ {
+ if ((m_ptr->hp - distance(ny, nx, oy, ox)*2) <= 0)
+ {
+ ny = oy + ddy[d];
+ nx = ox + ddx[d];
+ do_move = FALSE;
+ }
+ else
+ {
+ m_ptr->hp -= distance(ny, nx, oy, ox) * 2;
+ do_move = TRUE;
+ }
+ }
+ else
+ {
+ do_move = TRUE;
+ }
+ }
+
+ /* Execute the inscription -- MEGA HACK -- */
+ if ((c_ptr->inscription) && (c_ptr->inscription != INSCRIP_CHASM))
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ bool t;
+ t = execute_inscription(c_ptr->inscription, ny, nx);
+ if (!t && do_move)
+ {
+ /* Hack -- attack the player even if on the inscription */
+ if ((ny == p_ptr->py) && (nx == p_ptr->px))
+ do_move = TRUE;
+ else
+ do_move = FALSE;
+ }
+ }
+ }
+
+ /* Some monsters never attack */
+ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px) &&
+ (r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+#if 0
+ /* Hack -- memorize lack of attacks */
+ if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_NEVER_BLOW);
+#endif
+
+ /* Do not move */
+ do_move = FALSE;
+ }
+
+ /* The player is in the way. Attack him. */
+ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px))
+ {
+ /* Do the attack */
+ (void)make_attack_normal(m_idx, 1);
+
+ /* Do not move */
+ do_move = FALSE;
+
+ /* Took a turn */
+ do_turn = TRUE;
+ }
+
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2) &&
+ do_turn == FALSE)
+ {
+ do_move = FALSE;
+ }
+
+
+ /* A monster is in the way */
+ if (do_move && c_ptr->m_idx)
+ {
+ monster_race *z_ptr = race_inf(y_ptr);
+ monster_type *m2_ptr = &m_list[c_ptr->m_idx];
+
+ /* Assume no movement */
+ do_move = FALSE;
+
+ /* Kill weaker monsters */
+ if ((r_ptr->flags2 & RF2_KILL_BODY) &&
+ (r_ptr->mexp > z_ptr->mexp) && (cave_floor_bold(ny, nx)) &&
+ /* Friends don't kill friends... */
+ !((is_friend(m_ptr) > 0) && (is_friend(m2_ptr) > 0)) &&
+ /* Uniques aren't faceless monsters in a crowd */
+ !(z_ptr->flags1 & RF1_UNIQUE) &&
+ /* Don't wreck quests */
+ !(m2_ptr->mflag & (MFLAG_QUEST | MFLAG_QUEST2)) &&
+ /* Don't punish summoners for relying on their friends */
+ (is_friend(m2_ptr) <= 0))
+ {
+ /* Allow movement */
+ do_move = TRUE;
+
+ /* Monster ate another monster */
+ did_kill_body = TRUE;
+
+ /* XXX XXX XXX Message */
+
+ /* Kill the monster */
+ delete_monster(ny, nx);
+
+ /* Hack -- get the empty monster */
+ y_ptr = &m_list[c_ptr->m_idx];
+ }
+
+ /* Attack 'enemies' */
+ else if (is_enemy(m_ptr, m2_ptr) || m_ptr->confused)
+ {
+ do_move = FALSE;
+ /* attack */
+ if (m2_ptr->r_idx && (m2_ptr->hp >= 0))
+ {
+ hack_message_pain_may_silent = TRUE;
+ if (monst_attack_monst(m_idx, c_ptr->m_idx))
+ {
+ hack_message_pain_may_silent = FALSE;
+ return;
+ }
+ hack_message_pain_may_silent = FALSE;
+ }
+ }
+
+ /* Push past weaker monsters (unless leaving a wall) */
+ else if ((r_ptr->flags2 & RF2_MOVE_BODY) &&
+ (r_ptr->mexp > z_ptr->mexp) && cave_floor_bold(ny, nx) &&
+ (cave_floor_bold(m_ptr->fy, m_ptr->fx)))
+ {
+ /* Allow movement */
+ do_move = TRUE;
+
+ /* Monster pushed past another monster */
+ did_move_body = TRUE;
+
+ /* XXX XXX XXX Message */
+ }
+ }
+
+ /*
+ * Check if monster can cross terrain
+ * This is checked after the normal attacks
+ * to allow monsters to attack an enemy,
+ * even if it can't enter the terrain.
+ */
+ if (do_move && !monster_can_cross_terrain(c_ptr->feat, r_ptr))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+ }
+
+ /* Some monsters never move */
+ if (do_move && (r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ /* Hack -- memorize lack of attacks */
+ /* if (m_ptr->ml) r_ptr->r_flags1 |= (RF1_NEVER_MOVE); */
+
+ /* Do not move */
+ do_move = FALSE;
+ }
+
+
+
+ /* Creature has been allowed move */
+ if (do_move)
+ {
+ s16b this_o_idx, next_o_idx = 0;
+
+ /* Take a turn */
+ do_turn = TRUE;
+
+ /* Hack -- Update the old location */
+ cave[oy][ox].m_idx = c_ptr->m_idx;
+
+ /* Mega-Hack -- move the old monster, if any */
+ if (c_ptr->m_idx)
+ {
+ /* Move the old monster */
+ y_ptr->fy = oy;
+ y_ptr->fx = ox;
+
+ /* Update the old monster */
+ update_mon(c_ptr->m_idx, TRUE);
+
+ /* Wake up the moved monster */
+ m_list[c_ptr->m_idx].csleep = 0;
+
+ /*
+ * Update monster light -- I'm too lazy to check flags
+ * here, and those ego monster_race functions aren't
+ * re-entrant XXX XXX XXX
+ */
+ p_ptr->update |= (PU_MON_LITE);
+ }
+
+ /* Hack -- Update the new location */
+ c_ptr->m_idx = m_idx;
+
+ /* Move the monster */
+ m_ptr->fy = ny;
+ m_ptr->fx = nx;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(oy, ox);
+
+ /* Redraw the new grid */
+ lite_spot(ny, nx);
+
+ /* Execute the inscription -- MEGA HACK -- */
+ if (c_ptr->inscription == INSCRIP_CHASM)
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ execute_inscription(c_ptr->inscription, ny, nx);
+ }
+ }
+
+ /* Possible disturb */
+ if (m_ptr->ml && (disturb_move ||
+ ((m_ptr->mflag & (MFLAG_VIEW)) &&
+ disturb_near)))
+ {
+ /* Disturb */
+ if ((is_friend(m_ptr) < 0) || disturb_pets)
+ disturb(0, 0);
+ }
+
+ /* Check for monster trap */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ if (mon_hit_trap(m_idx)) return;
+ }
+ else
+ {
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip gold */
+ if (o_ptr->tval == TV_GOLD) continue;
+
+ /* Incarnate ? */
+ if ((o_ptr->tval == TV_CORPSE) && (r_ptr->flags7 & RF7_POSSESSOR) &&
+ ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON)))
+ {
+ if (ai_possessor(m_idx, this_o_idx)) return;
+ }
+
+ /* Take or Kill objects on the floor */
+ /* rr9: Pets will no longer pick up/destroy items */
+ if ((((r_ptr->flags2 & (RF2_TAKE_ITEM)) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_pickup_items)) ||
+ (r_ptr->flags2 & (RF2_KILL_ITEM))) &&
+ (is_friend(m_ptr) <= 0))
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ u32b flg3 = 0L;
+
+ char m_name[80];
+ char o_name[80];
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Acquire the object name */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0x04);
+
+ /* React to objects that hurt the monster */
+ if (f5 & (TR5_KILL_DEMON)) flg3 |= (RF3_DEMON);
+ if (f5 & (TR5_KILL_UNDEAD)) flg3 |= (RF3_UNDEAD);
+ if (f1 & (TR1_SLAY_DRAGON)) flg3 |= (RF3_DRAGON);
+ if (f1 & (TR1_SLAY_TROLL)) flg3 |= (RF3_TROLL);
+ if (f1 & (TR1_SLAY_GIANT)) flg3 |= (RF3_GIANT);
+ if (f1 & (TR1_SLAY_ORC)) flg3 |= (RF3_ORC);
+ if (f1 & (TR1_SLAY_DEMON)) flg3 |= (RF3_DEMON);
+ if (f1 & (TR1_SLAY_UNDEAD)) flg3 |= (RF3_UNDEAD);
+ if (f1 & (TR1_SLAY_ANIMAL)) flg3 |= (RF3_ANIMAL);
+ if (f1 & (TR1_SLAY_EVIL)) flg3 |= (RF3_EVIL);
+
+ /* The object cannot be picked up by the monster */
+ if (artifact_p(o_ptr) || (r_ptr->flags3 & flg3) ||
+ (o_ptr->art_name))
+ {
+ /* Only give a message for "take_item" */
+ if (r_ptr->flags2 & (RF2_TAKE_ITEM))
+ {
+ /* Take note */
+ did_take_item = TRUE;
+
+ /* Describe observable situations */
+ if (m_ptr->ml && player_has_los_bold(ny, nx))
+ {
+ /* Dump a message */
+ msg_format("%^s tries to pick up %s, but fails.",
+ m_name, o_name);
+ }
+ }
+ }
+
+ /* Pick up the item */
+ else if (r_ptr->flags2 & (RF2_TAKE_ITEM))
+ {
+ /* Take note */
+ did_take_item = TRUE;
+
+ /* Describe observable situations */
+ if (player_has_los_bold(ny, nx))
+ {
+ /* Dump a message */
+ msg_format("%^s picks up %s.", m_name, o_name);
+ }
+
+ /* Option */
+ if (testing_carry)
+ {
+ /* Excise the object */
+ excise_object_idx(this_o_idx);
+
+ /* Forget mark */
+ o_ptr->marked = FALSE;
+
+ /* Forget location */
+ o_ptr->iy = o_ptr->ix = 0;
+
+ /* Memorize monster */
+ o_ptr->held_m_idx = m_idx;
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Carry object */
+ m_ptr->hold_o_idx = this_o_idx;
+ }
+
+ /* Nope */
+ else
+ {
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+
+ /* Destroy the item */
+ else
+ {
+ /* Take note */
+ did_kill_item = TRUE;
+
+ /* Describe observable situations */
+ if (player_has_los_bold(ny, nx))
+ {
+ /* Dump a message */
+ msg_format("%^s crushes %s.", m_name, o_name);
+ }
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+ }
+ }
+
+ /* Update monster light */
+ if (r_ptr->flags9 & RF9_HAS_LITE) p_ptr->update |= (PU_MON_LITE);
+ }
+
+ /* Stop when done */
+ if (do_turn) break;
+ }
+
+
+ /* If we haven't done anything, try casting a spell again */
+ if (!do_turn && !do_move && !m_ptr->monfear && !stupid_monsters &&
+ (is_friend(m_ptr) < 0))
+ {
+ /* Cast spell */
+ if (make_attack_spell(m_idx)) return;
+ }
+
+
+ /* Notice changes in view */
+ if (do_view)
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+
+ /* Learn things from observable monster */
+ if (m_ptr->ml)
+ {
+ /* Monster opened a door */
+ if (did_open_door) r_ptr->r_flags2 |= (RF2_OPEN_DOOR);
+
+ /* Monster bashed a door */
+ if (did_bash_door) r_ptr->r_flags2 |= (RF2_BASH_DOOR);
+
+ /* Monster tried to pick something up */
+ if (did_take_item) r_ptr->r_flags2 |= (RF2_TAKE_ITEM);
+
+ /* Monster tried to crush something */
+ if (did_kill_item) r_ptr->r_flags2 |= (RF2_KILL_ITEM);
+
+ /* Monster pushed past another monster */
+ if (did_move_body) r_ptr->r_flags2 |= (RF2_MOVE_BODY);
+
+ /* Monster ate another monster */
+ if (did_kill_body) r_ptr->r_flags2 |= (RF2_KILL_BODY);
+
+ /* Monster passed through a wall */
+ if (did_pass_wall) r_ptr->r_flags2 |= (RF2_PASS_WALL);
+
+ /* Monster destroyed a wall */
+ if (did_kill_wall) r_ptr->r_flags2 |= (RF2_KILL_WALL);
+ }
+
+
+ /* Hack -- get "bold" if out of options */
+ if (!do_turn && !do_move && m_ptr->monfear)
+ {
+ /* No longer afraid */
+ m_ptr->monfear = 0;
+
+ /* Message if seen */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s turns to fight!", m_name);
+ }
+
+ /* XXX XXX XXX Actually do something now (?) */
+ }
+}
+
+
+void summon_maint(int m_idx)
+{
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Can you pay? */
+ if ((s32b)(p_ptr->maintain_sum / 10000) > p_ptr->csp)
+ {
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0);
+
+ msg_format("You lose control of %s.", m_name);
+
+ /* Well, then, I guess I'm dead. */
+ delete_monster_idx(m_idx);
+ }
+ else
+ {
+ s32b cl, ml, floor, cost;
+
+ cl = get_skill_scale(SKILL_SUMMON, 100);
+ ml = m_ptr->level * 10000;
+
+ /* Floor = 19 * ml / 990 + 8 / 199
+ This gives a floor of 0.1 at level 1 and a floor of 2 at level 100
+
+ Since ml is multiplied by 10000 already, we multiply the 8/199 too
+ */
+ floor = ml * 19 / 990 + 80000 / 199;
+ cost = (ml / cl - 10000) / 4;
+ if(cost < floor)
+ cost = floor;
+
+ /* Well, then I'll take my wages from you. */
+ p_ptr->maintain_sum += cost;
+ }
+ return;
+}
+
+
+/*
+ * Process all the "live" monsters, once per game turn.
+ *
+ * During each game turn, we scan through the list of all the "live" monsters,
+ * (backwards, so we can excise any "freshly dead" monsters), energizing each
+ * monster, and allowing fully energized monsters to move, attack, pass, etc.
+ *
+ * Note that monsters can never move in the monster array (except when the
+ * "compact_monsters()" function is called by "dungeon()" or "save_player()").
+ *
+ * This function is responsible for at least half of the processor time
+ * on a normal system with a "normal" amount of monsters and a player doing
+ * normal things.
+ *
+ * When the player is resting, virtually 90% of the processor time is spent
+ * in this function, and its children, "process_monster()" and "make_move()".
+ *
+ * Most of the rest of the time is spent in "update_view()" and "lite_spot()",
+ * especially when the player is running.
+ *
+ * Note the special "MFLAG_BORN" flag, which allows us to ignore "fresh"
+ * monsters while they are still being "born". A monster is "fresh" only
+ * during the turn in which it is created, and we use the "hack_m_idx" to
+ * determine if the monster is yet to be processed during the current turn.
+ *
+ * Note the special "MFLAG_NICE" flag, which allows the player to get one
+ * move before any "nasty" monsters get to use their spell attacks.
+ *
+ * Note that when the "knowledge" about the currently tracked monster
+ * changes (flags, attacks, spells), we induce a redraw of the monster
+ * recall window.
+ */
+void process_monsters(void)
+{
+ int i, e;
+ int fx, fy;
+
+ bool test;
+ bool is_frien = FALSE;
+
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ int old_monster_race_idx;
+ int old_monster_ego_idx;
+
+ u32b old_r_flags1 = 0L;
+ u32b old_r_flags2 = 0L;
+ u32b old_r_flags3 = 0L;
+ u32b old_r_flags4 = 0L;
+ u32b old_r_flags5 = 0L;
+ u32b old_r_flags6 = 0L;
+
+ byte old_r_blows0 = 0;
+ byte old_r_blows1 = 0;
+ byte old_r_blows2 = 0;
+ byte old_r_blows3 = 0;
+
+ byte old_r_cast_inate = 0;
+ byte old_r_cast_spell = 0;
+
+ /* Check the doppleganger */
+ if (doppleganger && !(r_info[m_list[doppleganger].r_idx].flags9 & RF9_DOPPLEGANGER))
+ doppleganger = 0;
+
+ /* Memorize old race */
+ old_monster_race_idx = monster_race_idx;
+ old_monster_ego_idx = monster_ego_idx;
+
+ /* Acquire knowledge */
+ if (monster_race_idx)
+ {
+ /* Acquire current monster */
+ r_ptr = &r_info[monster_race_idx];
+
+ /* Memorize flags */
+ old_r_flags1 = r_ptr->r_flags1;
+ old_r_flags2 = r_ptr->r_flags2;
+ old_r_flags3 = r_ptr->r_flags3;
+ old_r_flags4 = r_ptr->r_flags4;
+ old_r_flags5 = r_ptr->r_flags5;
+ old_r_flags6 = r_ptr->r_flags6;
+
+ /* Memorize blows */
+ old_r_blows0 = r_ptr->r_blows[0];
+ old_r_blows1 = r_ptr->r_blows[1];
+ old_r_blows2 = r_ptr->r_blows[2];
+ old_r_blows3 = r_ptr->r_blows[3];
+
+ /* Memorize castings */
+ old_r_cast_inate = r_ptr->r_cast_inate;
+ old_r_cast_spell = r_ptr->r_cast_spell;
+ }
+
+
+ /* Hack -- calculate the "player noise" */
+ noise = (1L << (30 - p_ptr->skill_stl));
+
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Calculate "upkeep" for friendly monsters */
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ total_friends++;
+ total_friend_levels += m_ptr->level;
+ }
+
+
+ /* Handle "fresh" monsters */
+ if (m_ptr->mflag & (MFLAG_BORN))
+ {
+ /* No longer "fresh" */
+ m_ptr->mflag &= ~(MFLAG_BORN);
+
+ /* Skip */
+ continue;
+ }
+
+
+ /* Obtain the energy boost */
+ e = extract_energy[m_ptr->mspeed];
+
+ /* Give this monster some energy */
+ m_ptr->energy += e;
+
+
+ /* Not enough energy to move */
+ if (m_ptr->energy < 100) continue;
+
+ /* Use up "some" energy */
+ m_ptr->energy -= 100;
+
+
+ /* Hack -- Require proximity */
+ if (m_ptr->cdis >= 100) continue;
+
+
+ /* Access the race */
+ r_ptr = race_inf(m_ptr);
+
+ /* Access the location */
+ fx = m_ptr->fx;
+ fy = m_ptr->fy;
+
+
+ /* Assume no move */
+ test = FALSE;
+
+ /* Control monster aint affected by distance */
+ if (p_ptr->control == i)
+ {
+ test = TRUE;
+ }
+
+ /* No free upkeep on partial summons just because they're out
+ * of line of sight. */
+ else if (m_ptr->mflag & MFLAG_PARTIAL) test = TRUE;
+
+ /* Handle "sensing radius" */
+ else if (m_ptr->cdis <= r_ptr->aaf)
+ {
+ /* We can "sense" the player */
+ test = TRUE;
+ }
+
+ /* Handle "sight" and "aggravation" */
+ else if ((m_ptr->cdis <= MAX_SIGHT) &&
+ (player_has_los_bold(fy, fx) ||
+ p_ptr->aggravate))
+ {
+ /* We can "see" or "feel" the player */
+ test = TRUE;
+ }
+
+#ifdef MONSTER_FLOW
+ /* Hack -- Monsters can "smell" the player from far away */
+ /* Note that most monsters have "aaf" of "20" or so */
+ else if (flow_by_sound &&
+ (cave[p_ptr->py][p_ptr->px].when == cave[fy][fx].when) &&
+ (cave[fy][fx].cost < MONSTER_FLOW_DEPTH) &&
+ (cave[fy][fx].cost < r_ptr->aaf))
+ {
+ /* We can "smell" the player */
+ test = TRUE;
+ }
+#endif /* MONSTER_FLOW */
+
+ /* Running away wont save them ! */
+ if (m_ptr->poisoned || m_ptr->bleeding) test = TRUE;
+
+ /* Do nothing */
+ if (!test) continue;
+
+ /* Save global index */
+ hack_m_idx = i;
+
+ if (is_friend(m_ptr) > 0) is_frien = TRUE;
+
+ /* Process the monster */
+ process_monster(i, is_frien);
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) break;
+
+ /* If it's still alive and friendly, charge upkeep. */
+ if (m_ptr->mflag & MFLAG_PARTIAL) summon_maint(i);
+
+ /* Notice leaving */
+ if (p_ptr->leaving) break;
+ }
+
+ /* Reset global index */
+ hack_m_idx = 0;
+
+
+ /* Tracking a monster race (the same one we were before) */
+ if (monster_race_idx && (monster_race_idx == old_monster_race_idx))
+ {
+ /* Acquire monster race */
+ r_ptr = &r_info[monster_race_idx];
+
+ /* Check for knowledge change */
+ if ((old_r_flags1 != r_ptr->r_flags1) ||
+ (old_r_flags2 != r_ptr->r_flags2) ||
+ (old_r_flags3 != r_ptr->r_flags3) ||
+ (old_r_flags4 != r_ptr->r_flags4) ||
+ (old_r_flags5 != r_ptr->r_flags5) ||
+ (old_r_flags6 != r_ptr->r_flags6) ||
+ (old_r_blows0 != r_ptr->r_blows[0]) ||
+ (old_r_blows1 != r_ptr->r_blows[1]) ||
+ (old_r_blows2 != r_ptr->r_blows[2]) ||
+ (old_r_blows3 != r_ptr->r_blows[3]) ||
+ (old_r_cast_inate != r_ptr->r_cast_inate) ||
+ (old_r_cast_spell != r_ptr->r_cast_spell))
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+ }
+}
diff --git a/src/modules.c b/src/modules.c
new file mode 100644
index 00000000..af382169
--- /dev/null
+++ b/src/modules.c
@@ -0,0 +1,289 @@
+/* File: modules.c */
+
+/* Purpose: T-engine modules */
+
+/*
+ * Copyright (c) 2003 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#ifdef PRIVATE_USER_PATH
+
+static void module_reset_dir_aux(cptr *dir, cptr new_path)
+{
+ char buf[1024];
+
+ /* Build the new path */
+ strnfmt(buf, sizeof (buf), "%s%s%s", dir, PATH_SEP, new_path);
+
+ string_free(*dir);
+ *dir = string_make(buf);
+
+ /* Make it if needed */
+ if (!private_check_user_directory(*dir))
+ quit(format("Unable to create module dir %s\n", *dir));
+}
+
+#endif
+
+void module_reset_dir(cptr dir, cptr new_path)
+{
+ cptr *d = 0;
+ char buf[1025];
+
+ if (!strcmp(dir, "apex")) d = &ANGBAND_DIR_APEX;
+ if (!strcmp(dir, "bone")) d = &ANGBAND_DIR_BONE;
+ if (!strcmp(dir, "core")) d = &ANGBAND_DIR_CORE;
+ if (!strcmp(dir, "dngn")) d = &ANGBAND_DIR_DNGN;
+ if (!strcmp(dir, "data")) d = &ANGBAND_DIR_DATA;
+ if (!strcmp(dir, "edit")) d = &ANGBAND_DIR_EDIT;
+ if (!strcmp(dir, "file")) d = &ANGBAND_DIR_FILE;
+ if (!strcmp(dir, "help")) d = &ANGBAND_DIR_HELP;
+ if (!strcmp(dir, "info")) d = &ANGBAND_DIR_INFO;
+ if (!strcmp(dir, "scpt")) d = &ANGBAND_DIR_SCPT;
+ if (!strcmp(dir, "patch")) d = &ANGBAND_DIR_PATCH;
+ if (!strcmp(dir, "pref")) d = &ANGBAND_DIR_PREF;
+ if (!strcmp(dir, "xtra")) d = &ANGBAND_DIR_XTRA;
+ if (!strcmp(dir, "user")) d = &ANGBAND_DIR_USER;
+ if (!strcmp(dir, "note")) d = &ANGBAND_DIR_NOTE;
+ if (!strcmp(dir, "cmov")) d = &ANGBAND_DIR_CMOV;
+#ifndef PRIVATE_USER_PATH
+ if (!strcmp(dir, "save")) d = &ANGBAND_DIR_SAVE;
+#else /* PRIVATE_USER_PATH */
+ if (
+#ifdef PRIVATE_USER_PATH_APEX
+ !strcmp(dir, "apex") ||
+#endif
+ !strcmp(dir, "user") ||
+ !strcmp(dir, "note") ||
+ !strcmp(dir, "cmov"))
+ {
+ char user_path[1024];
+ /* copied from init_file_paths */
+ path_parse(user_path, 1024, PRIVATE_USER_PATH);
+ strcat(user_path, USER_PATH_VERSION);
+ strnfmt(buf, 1024, "%s%s%s", user_path, PATH_SEP, new_path);
+ string_free(*d);
+ *d = string_make(buf);
+ }
+#ifdef PRIVATE_USER_PATH_DATA
+ else if (!strcmp(dir, "data"))
+ {
+ module_reset_dir_aux(&ANGBAND_DIR_DATA, new_path);
+ }
+#endif
+ else if (!strcmp(dir, "save"))
+ {
+ module_reset_dir_aux(&ANGBAND_DIR_SAVE, new_path);
+
+ /* Tell the savefile code that we must not use setuid */
+ savefile_setuid = FALSE;
+ }
+ else
+#endif /* PRIVATE_USER_PATH */
+ {
+ /* Build the new path */
+ strnfmt(buf, 1024, "%s%s%s%s%s", ANGBAND_DIR_MODULES, PATH_SEP, new_path, PATH_SEP, dir);
+
+ string_free(*d);
+ *d = string_make(buf);
+ }
+}
+
+static void dump_modules(int sel, int max)
+{
+ int i;
+
+ char buf[40], pre = ' ', post = ')';
+ cptr name;
+
+ char ind;
+
+
+ for (i = 0; i < max; i++)
+ {
+ ind = I2A(i % 26);
+ if (i >= 26) ind = toupper(ind);
+
+ if (sel == i)
+ {
+ pre = '[';
+ post = ']';
+ }
+ else
+ {
+ pre = ' ';
+ post = ')';
+ }
+
+ call_lua("get_module_name", "(d)", "s", i, &name);
+ strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, name);
+
+ if (sel == i)
+ {
+ call_lua("get_module_desc", "(d)", "s", i, &name);
+ print_desc_aux(name, 5, 0);
+
+ c_put_str(TERM_L_BLUE, buf, 10 + (i / 4), 20 * (i % 4));
+ }
+ else
+ put_str(buf, 10 + (i / 4), 20 * (i % 4));
+ }
+}
+
+static void activate_module()
+{
+ /* Initialize the module table */
+ call_lua("assign_current_module", "(s)", "", game_module);
+
+ /* Do misc inits */
+ call_lua("get_module_info", "(s)", "d", "max_plev", &max_plev);
+ call_lua("get_module_info", "(s)", "d", "death_dungeon", &DUNGEON_DEATH);
+
+ call_lua("get_module_info", "(s)", "d", "random_artifact_weapon_chance", &RANDART_WEAPON);
+ call_lua("get_module_info", "(s)", "d", "random_artifact_armor_chance", &RANDART_ARMOR);
+ call_lua("get_module_info", "(s)", "d", "random_artifact_jewelry_chance", &RANDART_JEWEL);
+
+ call_lua("get_module_info", "(s,d)", "d", "version", 1, &VERSION_MAJOR);
+ call_lua("get_module_info", "(s,d)", "d", "version", 2, &VERSION_MINOR);
+ call_lua("get_module_info", "(s,d)", "d", "version", 3, &VERSION_PATCH);
+ version_major = VERSION_MAJOR;
+ version_minor = VERSION_MINOR;
+ version_patch = VERSION_PATCH;
+
+ /* Change window name if needed */
+ if (strcmp(game_module, "ToME"))
+ {
+ strnfmt(angband_term_name[0], 79, "T-Engine: %s", game_module);
+ Term_xtra(TERM_XTRA_RENAME_MAIN_WIN, 0);
+ }
+
+ /* Reprocess the player name, just in case */
+ process_player_base();
+}
+
+/* Did the player force a module on command line */
+cptr force_module = NULL;
+
+/* Display possible modules and select one */
+bool select_module()
+{
+ s32b k, sel, max;
+
+ /* Hack */
+ use_color = TRUE;
+
+ /* Init some lua */
+ init_lua();
+
+ /* Some ports need to separate the module scripts from the installed mods,
+ so we need to check for these in two different places */
+ if(!tome_dofile_anywhere(ANGBAND_DIR_CORE, "mods_aux.lua", FALSE))
+ tome_dofile_anywhere(ANGBAND_DIR_MODULES, "mods_aux.lua", TRUE);
+ if(!tome_dofile_anywhere(ANGBAND_DIR_CORE, "modules.lua", FALSE))
+ tome_dofile_anywhere(ANGBAND_DIR_MODULES, "modules.lua", TRUE);
+
+ /* Grab the savefiles */
+ call_lua("max_modules", "()", "d", &max);
+
+ /* No need to bother the player if there is only one module */
+ sel = -1;
+ if (force_module)
+ call_lua("find_module", "(s)", "d", force_module, &sel);
+ if (max == 1)
+ sel = 0;
+ if (sel != -1)
+ {
+ cptr tmp;
+
+ /* Process the module */
+ call_lua("init_module", "(d)", "", sel);
+ call_lua("get_module_name", "(d)", "s", sel, &tmp);
+ game_module = string_make(tmp);
+
+ activate_module();
+
+ return FALSE;
+ }
+
+ sel = 0;
+
+ /* Preprocess the basic prefs, we need them to have movement keys */
+ process_pref_file("pref.prf");
+
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Let the user choose */
+ c_put_str(TERM_YELLOW, "Welcome to ToME, you must select a module to play,", 1, 12);
+ c_put_str(TERM_YELLOW, "either ToME official module or third party ones.", 2, 13);
+ put_str("Press 8/2/4/6 to move, Return to select and Esc to quit.", 4, 3);
+
+ dump_modules(sel, max);
+
+ k = inkey();
+
+ if (k == ESCAPE)
+ {
+ quit(NULL);
+ }
+ if (k == '6')
+ {
+ sel++;
+ if (sel >= max) sel = 0;
+ continue;
+ }
+ else if (k == '4')
+ {
+ sel--;
+ if (sel < 0) sel = max - 1;
+ continue;
+ }
+ else if (k == '2')
+ {
+ sel += 4;
+ if (sel >= max) sel = sel % max;
+ continue;
+ }
+ else if (k == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = (sel + max - 1) % max;
+ continue;
+ }
+ else if (k == '\r')
+ {
+ if (sel < 26) k = I2A(sel);
+ else k = toupper(I2A(sel));
+ }
+
+ {
+ int x;
+ cptr tmp;
+
+ if (islower(k)) x = A2I(k);
+ else x = A2I(tolower(k)) + 26;
+
+ if ((x < 0) || (x >= max)) continue;
+
+ /* Process the module */
+ call_lua("init_module", "(d)", "", x);
+ call_lua("get_module_name", "(d)", "s", x, &tmp);
+ game_module = string_make(tmp);
+
+ activate_module();
+
+ return (FALSE);
+ }
+ }
+
+ /* Shouldnt happen */
+ return (FALSE);
+}
diff --git a/src/monster.pkg b/src/monster.pkg
new file mode 100644
index 00000000..a9efd089
--- /dev/null
+++ b/src/monster.pkg
@@ -0,0 +1,2324 @@
+/* File: monster.pkg */
+
+/*
+ * Purpose: Lua interface defitions for monsters.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/* To make easy object creations */
+$static monster_type lua_monster_forge;
+/** @var monster_forge;
+ * @brief monster_type
+ */
+static monster_type lua_monster_forge @ monster_forge;
+
+/** @name Monster status
+ * @note Player POV
+ * @{ */
+/** @def MSTATUS_ENEMY */
+#define MSTATUS_ENEMY -2
+
+/** @def MSTATUS_NEUTRAL_M */
+#define MSTATUS_NEUTRAL_M -1
+
+/** @def MSTATUS_NEUTRAL */
+#define MSTATUS_NEUTRAL 0
+
+/** @def MSTATUS_NEUTRAL_P */
+#define MSTATUS_NEUTRAL_P 1
+
+/** @def MSTATUS_FRIEND */
+#define MSTATUS_FRIEND 2
+
+/** @def MSTATUS_PET */
+#define MSTATUS_PET 3
+
+/** @def MSTATUS_COMPANION */
+#define MSTATUS_COMPANION 4
+/** @} */
+
+/** @name Race flags #1
+ * @{ */
+/** @def RF1_UNIQUE
+ * @note Unique Monster
+ */
+#define RF1_UNIQUE 0x00000001
+/** @def RF1_QUESTOR
+ * @note Quest Monster
+ */
+#define RF1_QUESTOR 0x00000002
+/** @def RF1_MALE
+ * @note Male gender
+ */
+#define RF1_MALE 0x00000004
+/** @def RF1_FEMALE
+ * @note Female gender
+ */
+#define RF1_FEMALE 0x00000008
+/** @def RF1_CHAR_CLEAR
+ * @note Absorbs symbol
+ */
+#define RF1_CHAR_CLEAR 0x00000010
+/** @def RF1_CHAR_MULTI
+ * @note Changes symbol
+ */
+#define RF1_CHAR_MULTI 0x00000020
+/** @def RF1_ATTR_CLEAR
+ * @note Absorbs color
+ */
+#define RF1_ATTR_CLEAR 0x00000040
+/** @def RF1_ATTR_MULTI
+ * @note Changes color
+ */
+#define RF1_ATTR_MULTI 0x00000080
+/** @def RF1_FORCE_DEPTH
+ * @note Start at "correct" depth
+ */
+#define RF1_FORCE_DEPTH 0x00000100
+/** @def RF1_FORCE_MAXHP
+ * @note Start with max hitpoints
+ */
+#define RF1_FORCE_MAXHP 0x00000200
+/** @def RF1_FORCE_SLEEP
+ * @note Start out sleeping
+ */
+#define RF1_FORCE_SLEEP 0x00000400
+/** @def RF1_FORCE_EXTRA
+ * @note Start out something
+ */
+#define RF1_FORCE_EXTRA 0x00000800
+/** @def RF1_FRIEND
+ * @note Arrive with a friend
+ */
+#define RF1_FRIEND 0x00001000
+/** @def RF1_FRIENDS
+ * @note Arrive with some friends
+ */
+#define RF1_FRIENDS 0x00002000
+/** @def RF1_ESCORT
+ * @note Arrive with an escort
+ */
+#define RF1_ESCORT 0x00004000
+/** @def RF1_ESCORTS
+ * @note Arrive with some escorts
+ */
+#define RF1_ESCORTS 0x00008000
+/** @def RF1_NEVER_BLOW
+ * @note Never make physical blow
+ */
+#define RF1_NEVER_BLOW 0x00010000
+/** @def RF1_NEVER_MOVE
+ * @note Never make physical move
+ */
+#define RF1_NEVER_MOVE 0x00020000
+/** @def RF1_RAND_25
+ * @note Moves randomly (25%)
+ */
+#define RF1_RAND_25 0x00040000
+/** @def RF1_RAND_50
+ * @note Moves randomly (50%)
+ */
+#define RF1_RAND_50 0x00080000
+/** @def RF1_ONLY_GOLD
+ * @note Drop only gold
+ */
+#define RF1_ONLY_GOLD 0x00100000
+/** @def RF1_ONLY_ITEM
+ * @note Drop only items
+ */
+#define RF1_ONLY_ITEM 0x00200000
+/** @def RF1_DROP_60
+ * @note Drop an item/gold (60%)
+ */
+#define RF1_DROP_60 0x00400000
+/** @def RF1_DROP_90
+ * @note Drop an item/gold (90%)
+ */
+#define RF1_DROP_90 0x00800000
+/** @def RF1_DROP_1D2
+ * @note Drop 1d2 items/gold
+ */
+#define RF1_DROP_1D2 0x01000000
+/** @def RF1_DROP_2D2
+ * @note Drop 2d2 items/gold
+ */
+#define RF1_DROP_2D2 0x02000000
+/** @def RF1_DROP_3D2
+ * @note Drop 3d2 items/gold
+ */
+#define RF1_DROP_3D2 0x04000000
+/** @def RF1_DROP_4D2
+ * @note Drop 4d2 items/gold
+ */
+#define RF1_DROP_4D2 0x08000000
+/** @def RF1_DROP_GOOD
+ * @note Drop good items
+ */
+#define RF1_DROP_GOOD 0x10000000
+/** @def RF1_DROP_GREAT
+ * @note Drop great items
+ */
+#define RF1_DROP_GREAT 0x20000000
+/** @def RF1_DROP_USEFUL
+ * @note Drop "useful" items
+ */
+#define RF1_DROP_USEFUL 0x40000000
+/** @def RF1_DROP_CHOSEN
+ * @note Drop "chosen" items
+ */
+#define RF1_DROP_CHOSEN 0x80000000
+/** @} */
+
+/** @name Race flags #2
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF2_STUPID
+ * @note Monster is stupid
+ */
+#define RF2_STUPID 0x00000001
+/** @def RF2_SMART
+ * @note Monster is smart
+ */
+#define RF2_SMART 0x00000002
+/** @def RF2_CAN_SPEAK
+ * @note TY: can speak
+ */
+#define RF2_CAN_SPEAK 0x00000004
+/** @def RF2_REFLECTING
+ * @note Reflects bolts
+ */
+#define RF2_REFLECTING 0x00000008
+/** @def RF2_INVISIBLE
+ * @note Monster avoids vision
+ */
+#define RF2_INVISIBLE 0x00000010
+/** @def RF2_COLD_BLOOD
+ * @note Monster avoids infra
+ */
+#define RF2_COLD_BLOOD 0x00000020
+/** @def RF2_EMPTY_MIND
+ * @note Monster avoids telepathy
+ */
+#define RF2_EMPTY_MIND 0x00000040
+/** @def RF2_WEIRD_MIND
+ * @note Monster avoids telepathy?
+ */
+#define RF2_WEIRD_MIND 0x00000080
+/** @def RF2_DEATH_ORB
+ * @note Death Orb
+ */
+#define RF2_DEATH_ORB 0x00000100
+/** @def RF2_REGENERATE
+ * @note Monster regenerates
+ */
+#define RF2_REGENERATE 0x00000200
+/** @def RF2_SHAPECHANGER
+ * @note TY: shapechanger
+ */
+#define RF2_SHAPECHANGER 0x00000400
+/** @def RF2_ATTR_ANY
+ * @note TY: Attr_any
+ */
+#define RF2_ATTR_ANY 0x00000800
+/** @def RF2_POWERFUL
+ * @note Monster has strong breath
+ */
+#define RF2_POWERFUL 0x00001000
+/** @def RF2_ELDRITCH_HORROR
+ * @note Sanity-blasting horror
+ */
+#define RF2_ELDRITCH_HORROR 0x00002000
+/** @def RF2_AURA_FIRE
+ * @note Burns in melee
+ */
+#define RF2_AURA_FIRE 0x00004000
+/** @def RF2_AURA_ELEC
+ * @note Shocks in melee
+ */
+#define RF2_AURA_ELEC 0x00008000
+/** @def RF2_OPEN_DOOR
+ * @note Monster can open doors
+ */
+#define RF2_OPEN_DOOR 0x00010000
+/** @def RF2_BASH_DOOR
+ * @note Monster can bash doors
+ */
+#define RF2_BASH_DOOR 0x00020000
+/** @def RF2_PASS_WALL
+ * @note Monster can pass walls
+ */
+#define RF2_PASS_WALL 0x00040000
+/** @def RF2_KILL_WALL
+ * @note Monster can destroy walls
+ */
+#define RF2_KILL_WALL 0x00080000
+/** @def RF2_MOVE_BODY
+ * @note Monster can move monsters
+ */
+#define RF2_MOVE_BODY 0x00100000
+/** @def RF2_KILL_BODY
+ * @note Monster can kill monsters
+ */
+#define RF2_KILL_BODY 0x00200000
+/** @def RF2_TAKE_ITEM
+ * @note Monster can pick up items
+ */
+#define RF2_TAKE_ITEM 0x00400000
+/** @def RF2_KILL_ITEM
+ * @note Monster can crush items
+ */
+#define RF2_KILL_ITEM 0x00800000
+/** @def RF2_BRAIN_1 */
+#define RF2_BRAIN_1 0x01000000
+
+/** @def RF2_BRAIN_2 */
+#define RF2_BRAIN_2 0x02000000
+
+/** @def RF2_BRAIN_3 */
+#define RF2_BRAIN_3 0x04000000
+
+/** @def RF2_BRAIN_4 */
+#define RF2_BRAIN_4 0x08000000
+
+/** @def RF2_BRAIN_5 */
+#define RF2_BRAIN_5 0x10000000
+
+/** @def RF2_BRAIN_6 */
+#define RF2_BRAIN_6 0x20000000
+
+/** @def RF2_BRAIN_7 */
+#define RF2_BRAIN_7 0x40000000
+
+/** @def RF2_BRAIN_8 */
+#define RF2_BRAIN_8 0x80000000
+/** @} */
+
+/** @name Race flags #3
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF3_ORC
+ * @note Orc
+ */
+#define RF3_ORC 0x00000001
+/** @def RF3_TROLL
+ * @note Troll
+ */
+#define RF3_TROLL 0x00000002
+/** @def RF3_GIANT
+ * @note Giant
+ */
+#define RF3_GIANT 0x00000004
+/** @def RF3_DRAGON
+ * @note Dragon
+ */
+#define RF3_DRAGON 0x00000008
+/** @def RF3_DEMON
+ * @note Demon
+ */
+#define RF3_DEMON 0x00000010
+/** @def RF3_UNDEAD
+ * @note Undead
+ */
+#define RF3_UNDEAD 0x00000020
+/** @def RF3_EVIL
+ * @note Evil
+ */
+#define RF3_EVIL 0x00000040
+/** @def RF3_ANIMAL
+ * @note Animal
+ */
+#define RF3_ANIMAL 0x00000080
+/** @def RF3_THUNDERLORD
+ * @note DG: Thunderlord
+ */
+#define RF3_THUNDERLORD 0x00000100
+/** @def RF3_GOOD
+ * @note Good
+ */
+#define RF3_GOOD 0x00000200
+/** @def RF3_AURA_COLD
+ * @note Freezes in melee
+ */
+#define RF3_AURA_COLD 0x00000400
+/** @def RF3_NONLIVING
+ * @note TY: Non-Living (?)
+ */
+#define RF3_NONLIVING 0x00000800
+/** @def RF3_HURT_LITE
+ * @note Hurt by lite
+ */
+#define RF3_HURT_LITE 0x00001000
+/** @def RF3_HURT_ROCK
+ * @note Hurt by rock remover
+ */
+#define RF3_HURT_ROCK 0x00002000
+/** @def RF3_SUSCEP_FIRE
+ * @note Hurt badly by fire
+ */
+#define RF3_SUSCEP_FIRE 0x00004000
+/** @def RF3_SUSCEP_COLD
+ * @note Hurt badly by cold
+ */
+#define RF3_SUSCEP_COLD 0x00008000
+/** @def RF3_IM_ACID
+ * @note Resist acid a lot
+ */
+#define RF3_IM_ACID 0x00010000
+/** @def RF3_IM_ELEC
+ * @note Resist elec a lot
+ */
+#define RF3_IM_ELEC 0x00020000
+/** @def RF3_IM_FIRE
+ * @note Resist fire a lot
+ */
+#define RF3_IM_FIRE 0x00040000
+/** @def RF3_IM_COLD
+ * @note Resist cold a lot
+ */
+#define RF3_IM_COLD 0x00080000
+/** @def RF3_IM_POIS
+ * @note Resist poison a lot
+ */
+#define RF3_IM_POIS 0x00100000
+/** @def RF3_RES_TELE
+ * @note Resist teleportation
+ */
+#define RF3_RES_TELE 0x00200000
+/** @def RF3_RES_NETH
+ * @note Resist nether a lot
+ */
+#define RF3_RES_NETH 0x00400000
+/** @def RF3_RES_WATE
+ * @note Resist water
+ */
+#define RF3_RES_WATE 0x00800000
+/** @def RF3_RES_PLAS
+ * @note Resist plasma
+ */
+#define RF3_RES_PLAS 0x01000000
+/** @def RF3_RES_NEXU
+ * @note Resist nexus
+ */
+#define RF3_RES_NEXU 0x02000000
+/** @def RF3_RES_DISE
+ * @note Resist disenchantment
+ */
+#define RF3_RES_DISE 0x04000000
+/** @def RF3_UNIQUE_4
+ * @note Is a "Nazgul" unique
+ */
+#define RF3_UNIQUE_4 0x08000000
+/** @def RF3_NO_FEAR
+ * @note Cannot be scared
+ */
+#define RF3_NO_FEAR 0x10000000
+/** @def RF3_NO_STUN
+ * @note Cannot be stunned
+ */
+#define RF3_NO_STUN 0x20000000
+/** @def RF3_NO_CONF
+ * @note Cannot be confused
+ */
+#define RF3_NO_CONF 0x40000000
+/** @def RF3_NO_SLEEP
+ * @note Cannot be slept
+ */
+#define RF3_NO_SLEEP 0x80000000
+/** @} */
+
+/** @name Race flags #4
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF4_SHRIEK
+ * @note Shriek for help
+ */
+#define RF4_SHRIEK 0x00000001
+/** @def RF4_MULTIPLY
+ * @note Monster reproduces
+ */
+#define RF4_MULTIPLY 0x00000002
+/** @def RF4_S_ANIMAL
+ * @note Summon animals
+ */
+#define RF4_S_ANIMAL 0x00000004
+/** @def RF4_ROCKET
+ * @note TY: Rocket
+ */
+#define RF4_ROCKET 0x00000008
+/** @def RF4_ARROW_1
+ * @note Fire an arrow (light)
+ */
+#define RF4_ARROW_1 0x00000010
+/** @def RF4_ARROW_2
+ * @note Fire an arrow (heavy)
+ */
+#define RF4_ARROW_2 0x00000020
+/** @def RF4_ARROW_3
+ * @note Fire missiles (light)
+ */
+#define RF4_ARROW_3 0x00000040
+/** @def RF4_ARROW_4
+ * @note Fire missiles (heavy)
+ */
+#define RF4_ARROW_4 0x00000080
+/** @def RF4_BR_ACID
+ * @note Breathe Acid
+ */
+#define RF4_BR_ACID 0x00000100
+/** @def RF4_BR_ELEC
+ * @note Breathe Elec
+ */
+#define RF4_BR_ELEC 0x00000200
+/** @def RF4_BR_FIRE
+ * @note Breathe Fire
+ */
+#define RF4_BR_FIRE 0x00000400
+/** @def RF4_BR_COLD
+ * @note Breathe Cold
+ */
+#define RF4_BR_COLD 0x00000800
+/** @def RF4_BR_POIS
+ * @note Breathe Poison
+ */
+#define RF4_BR_POIS 0x00001000
+/** @def RF4_BR_NETH
+ * @note Breathe Nether
+ */
+#define RF4_BR_NETH 0x00002000
+/** @def RF4_BR_LITE
+ * @note Breathe Lite
+ */
+#define RF4_BR_LITE 0x00004000
+/** @def RF4_BR_DARK
+ * @note Breathe Dark
+ */
+#define RF4_BR_DARK 0x00008000
+/** @def RF4_BR_CONF
+ * @note Breathe Confusion
+ */
+#define RF4_BR_CONF 0x00010000
+/** @def RF4_BR_SOUN
+ * @note Breathe Sound
+ */
+#define RF4_BR_SOUN 0x00020000
+/** @def RF4_BR_CHAO
+ * @note Breathe Chaos
+ */
+#define RF4_BR_CHAO 0x00040000
+/** @def RF4_BR_DISE
+ * @note Breathe Disenchant
+ */
+#define RF4_BR_DISE 0x00080000
+/** @def RF4_BR_NEXU
+ * @note Breathe Nexus
+ */
+#define RF4_BR_NEXU 0x00100000
+/** @def RF4_BR_TIME
+ * @note Breathe Time
+ */
+#define RF4_BR_TIME 0x00200000
+/** @def RF4_BR_INER
+ * @note Breathe Inertia
+ */
+#define RF4_BR_INER 0x00400000
+/** @def RF4_BR_GRAV
+ * @note Breathe Gravity
+ */
+#define RF4_BR_GRAV 0x00800000
+/** @def RF4_BR_SHAR
+ * @note Breathe Shards
+ */
+#define RF4_BR_SHAR 0x01000000
+/** @def RF4_BR_PLAS
+ * @note Breathe Plasma
+ */
+#define RF4_BR_PLAS 0x02000000
+/** @def RF4_BR_WALL
+ * @note Breathe Force
+ */
+#define RF4_BR_WALL 0x04000000
+/** @def RF4_BR_MANA
+ * @note Breathe Mana
+ */
+#define RF4_BR_MANA 0x08000000
+/** @def RF4_BA_NUKE
+ * @note TY: Nuke Ball
+ */
+#define RF4_BA_NUKE 0x10000000
+/** @def RF4_BR_NUKE
+ * @note TY: Toxic Breath
+ */
+#define RF4_BR_NUKE 0x20000000
+/** @def RF4_BA_CHAO
+ * @note Chaos Ball
+ */
+#define RF4_BA_CHAO 0x40000000
+/** @def RF4_BR_DISI
+ * @note Breathe Disintegration
+ */
+#define RF4_BR_DISI 0x80000000
+/** @} */
+
+/** @name Race flags #5
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF5_BA_ACID
+ * @note Acid Ball
+ */
+#define RF5_BA_ACID 0x00000001
+/** @def RF5_BA_ELEC
+ * @note Elec Ball
+ */
+#define RF5_BA_ELEC 0x00000002
+/** @def RF5_BA_FIRE
+ * @note Fire Ball
+ */
+#define RF5_BA_FIRE 0x00000004
+/** @def RF5_BA_COLD
+ * @note Cold Ball
+ */
+#define RF5_BA_COLD 0x00000008
+/** @def RF5_BA_POIS
+ * @note Poison Ball
+ */
+#define RF5_BA_POIS 0x00000010
+/** @def RF5_BA_NETH
+ * @note Nether Ball
+ */
+#define RF5_BA_NETH 0x00000020
+/** @def RF5_BA_WATE
+ * @note Water Ball
+ */
+#define RF5_BA_WATE 0x00000040
+/** @def RF5_BA_MANA
+ * @note Mana Storm
+ */
+#define RF5_BA_MANA 0x00000080
+/** @def RF5_BA_DARK
+ * @note Darkness Storm
+ */
+#define RF5_BA_DARK 0x00000100
+/** @def RF5_DRAIN_MANA
+ * @note Drain Mana
+ */
+#define RF5_DRAIN_MANA 0x00000200
+/** @def RF5_MIND_BLAST
+ * @note Blast Mind
+ */
+#define RF5_MIND_BLAST 0x00000400
+/** @def RF5_BRAIN_SMASH
+ * @note Smash Brain
+ */
+#define RF5_BRAIN_SMASH 0x00000800
+/** @def RF5_CAUSE_1
+ * @note Cause Light Wound
+ */
+#define RF5_CAUSE_1 0x00001000
+/** @def RF5_CAUSE_2
+ * @note Cause Serious Wound
+ */
+#define RF5_CAUSE_2 0x00002000
+/** @def RF5_CAUSE_3
+ * @note Cause Critical Wound
+ */
+#define RF5_CAUSE_3 0x00004000
+/** @def RF5_CAUSE_4
+ * @note Cause Mortal Wound
+ */
+#define RF5_CAUSE_4 0x00008000
+/** @def RF5_BO_ACID
+ * @note Acid Bolt
+ */
+#define RF5_BO_ACID 0x00010000
+/** @def RF5_BO_ELEC
+ * @note Elec Bolt (unused)
+ */
+#define RF5_BO_ELEC 0x00020000
+/** @def RF5_BO_FIRE
+ * @note Fire Bolt
+ */
+#define RF5_BO_FIRE 0x00040000
+/** @def RF5_BO_COLD
+ * @note Cold Bolt
+ */
+#define RF5_BO_COLD 0x00080000
+/** @def RF5_BO_POIS
+ * @note Poison Bolt (unused)
+ */
+#define RF5_BO_POIS 0x00100000
+/** @def RF5_BO_NETH
+ * @note Nether Bolt
+ */
+#define RF5_BO_NETH 0x00200000
+/** @def RF5_BO_WATE
+ * @note Water Bolt
+ */
+#define RF5_BO_WATE 0x00400000
+/** @def RF5_BO_MANA
+ * @note Mana Bolt
+ */
+#define RF5_BO_MANA 0x00800000
+/** @def RF5_BO_PLAS
+ * @note Plasma Bolt
+ */
+#define RF5_BO_PLAS 0x01000000
+/** @def RF5_BO_ICEE
+ * @note Ice Bolt
+ */
+#define RF5_BO_ICEE 0x02000000
+/** @def RF5_MISSILE
+ * @note Magic Missile
+ */
+#define RF5_MISSILE 0x04000000
+/** @def RF5_SCARE
+ * @note Frighten Player
+ */
+#define RF5_SCARE 0x08000000
+/** @def RF5_BLIND
+ * @note Blind Player
+ */
+#define RF5_BLIND 0x10000000
+/** @def RF5_CONF
+ * @note Confuse Player
+ */
+#define RF5_CONF 0x20000000
+/** @def RF5_SLOW
+ * @note Slow Player
+ */
+#define RF5_SLOW 0x40000000
+/** @def RF5_HOLD
+ * @note Paralyze Player
+ */
+#define RF5_HOLD 0x80000000
+/** @} */
+
+/** @name Race flags #6
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF6_HASTE
+ * @note Speed self
+ */
+#define RF6_HASTE 0x00000001
+/** @def RF6_HAND_DOOM
+ * @note Hand of Doom
+ */
+#define RF6_HAND_DOOM 0x00000002
+/** @def RF6_HEAL
+ * @note Heal self
+ */
+#define RF6_HEAL 0x00000004
+/** @def RF6_S_ANIMALS
+ * @note Summon animals
+ */
+#define RF6_S_ANIMALS 0x00000008
+/** @def RF6_BLINK
+ * @note Teleport Short
+ */
+#define RF6_BLINK 0x00000010
+/** @def RF6_TPORT
+ * @note Teleport Long
+ */
+#define RF6_TPORT 0x00000020
+/** @def RF6_TELE_TO
+ * @note Move player to monster
+ */
+#define RF6_TELE_TO 0x00000040
+/** @def RF6_TELE_AWAY
+ * @note Move player far away
+ */
+#define RF6_TELE_AWAY 0x00000080
+/** @def RF6_TELE_LEVEL
+ * @note Move player vertically
+ */
+#define RF6_TELE_LEVEL 0x00000100
+/** @def RF6_DARKNESS
+ * @note Create Darkness
+ */
+#define RF6_DARKNESS 0x00000200
+/** @def RF6_TRAPS
+ * @note Create Traps
+ */
+#define RF6_TRAPS 0x00000400
+/** @def RF6_FORGET
+ * @note Cause amnesia
+ */
+#define RF6_FORGET 0x00000800
+/** @def RF6_RAISE_DEAD
+ * @note Raise Dead
+ */
+#define RF6_RAISE_DEAD 0x00001000
+/** @def RF6_S_BUG
+ * @note Summon Software bug
+ */
+#define RF6_S_BUG 0x00002000
+/** @def RF6_S_RNG
+ * @note Summon RNG
+ */
+#define RF6_S_RNG 0x00004000
+/** @def RF6_S_THUNDERLORD
+ * @note Summon Thunderlords
+ */
+#define RF6_S_THUNDERLORD 0x00008000
+/** @def RF6_S_KIN
+ * @note Summon "kin"
+ */
+#define RF6_S_KIN 0x00010000
+/** @def RF6_S_HI_DEMON
+ * @note Summon greater demons!
+ */
+#define RF6_S_HI_DEMON 0x00020000
+/** @def RF6_S_MONSTER
+ * @note Summon Monster
+ */
+#define RF6_S_MONSTER 0x00040000
+/** @def RF6_S_MONSTERS
+ * @note Summon Monsters
+ */
+#define RF6_S_MONSTERS 0x00080000
+/** @def RF6_S_ANT
+ * @note Summon Ants
+ */
+#define RF6_S_ANT 0x00100000
+/** @def RF6_S_SPIDER
+ * @note Summon Spiders
+ */
+#define RF6_S_SPIDER 0x00200000
+/** @def RF6_S_HOUND
+ * @note Summon Hounds
+ */
+#define RF6_S_HOUND 0x00400000
+/** @def RF6_S_HYDRA
+ * @note Summon Hydras
+ */
+#define RF6_S_HYDRA 0x00800000
+/** @def RF6_S_ANGEL
+ * @note Summon Angel
+ */
+#define RF6_S_ANGEL 0x01000000
+/** @def RF6_S_DEMON
+ * @note Summon Demon
+ */
+#define RF6_S_DEMON 0x02000000
+/** @def RF6_S_UNDEAD
+ * @note Summon Undead
+ */
+#define RF6_S_UNDEAD 0x04000000
+/** @def RF6_S_DRAGON
+ * @note Summon Dragon
+ */
+#define RF6_S_DRAGON 0x08000000
+/** @def RF6_S_HI_UNDEAD
+ * @note Summon Greater Undead
+ */
+#define RF6_S_HI_UNDEAD 0x10000000
+/** @def RF6_S_HI_DRAGON
+ * @note Summon Ancient Dragon
+ */
+#define RF6_S_HI_DRAGON 0x20000000
+/** @def RF6_S_WRAITH
+ * @note Summon Unique Wraith
+ */
+#define RF6_S_WRAITH 0x40000000
+/** @def RF6_S_UNIQUE
+ * @note Summon Unique Monster
+ */
+#define RF6_S_UNIQUE 0x80000000
+/** @} */
+
+/** @name Race flags #7
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF7_AQUATIC
+ * @note Aquatic monster
+ */
+#define RF7_AQUATIC 0x00000001
+/** @def RF7_CAN_SWIM
+ * @note Monster can swim
+ */
+#define RF7_CAN_SWIM 0x00000002
+/** @def RF7_CAN_FLY
+ * @note Monster can fly
+ */
+#define RF7_CAN_FLY 0x00000004
+/** @def RF7_FRIENDLY
+ * @note Monster is friendly
+ */
+#define RF7_FRIENDLY 0x00000008
+/** @def RF7_PET
+ * @note Monster is a pet
+ */
+#define RF7_PET 0x00000010
+/** @def RF7_MORTAL
+ * @note Monster is a mortal being
+ */
+#define RF7_MORTAL 0x00000020
+/** @def RF7_SPIDER
+ * @note Monster is a spider (can pass webs)
+ */
+#define RF7_SPIDER 0x00000040
+/** @def RF7_NAZGUL
+ * @note Monster is a Nazgul
+ */
+#define RF7_NAZGUL 0x00000080
+/** @def RF7_DG_CURSE
+ * @note If killed the monster grant a DG Curse to the player
+ */
+#define RF7_DG_CURSE 0x00000100
+/** @def RF7_POSSESSOR
+ * @note Is it a dreaded possessor monster ?
+ */
+#define RF7_POSSESSOR 0x00000200
+/** @def RF7_NO_DEATH
+ * @note Cannot be killed
+ */
+#define RF7_NO_DEATH 0x00000400
+/** @def RF7_NO_TARGET
+ * @note Cannot be targeted
+ */
+#define RF7_NO_TARGET 0x00000800
+/** @def RF7_AI_ANNOY
+ * @note Try to tease the player
+ */
+#define RF7_AI_ANNOY 0x00001000
+/** @def RF7_AI_SPECIAL
+ * @note For quests
+ */
+#define RF7_AI_SPECIAL 0x00002000
+/** @def RF7_NO_THEFT
+ * @note Monster is immune to theft
+ */
+#define RF7_NO_THEFT 0x00040000
+/** @def RF7_SPIRIT
+ * @note This is a Spirit, coming from the Void
+ */
+#define RF7_SPIRIT 0x00080000
+/** @def RF7_IM_MELEE
+ * @note IM melee
+ */
+#define RF7_IM_MELEE 0x00100000
+/** @} */
+
+/** @name Race flags #8
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF8_DUNGEON */
+#define RF8_DUNGEON 0x00000001
+
+/** @def RF8_WILD_TOWN */
+#define RF8_WILD_TOWN 0x00000002
+
+/** @def RF8_XXX8X02 */
+#define RF8_XXX8X02 0x00000004
+
+/** @def RF8_WILD_SHORE */
+#define RF8_WILD_SHORE 0x00000008
+
+/** @def RF8_WILD_OCEAN */
+#define RF8_WILD_OCEAN 0x00000010
+
+/** @def RF8_WILD_WASTE */
+#define RF8_WILD_WASTE 0x00000020
+
+/** @def RF8_WILD_WOOD */
+#define RF8_WILD_WOOD 0x00000040
+
+/** @def RF8_WILD_VOLCANO */
+#define RF8_WILD_VOLCANO 0x00000080
+
+/** @def RF8_XXX8X08 */
+#define RF8_XXX8X08 0x00000100
+
+/** @def RF8_WILD_MOUNTAIN */
+#define RF8_WILD_MOUNTAIN 0x00000200
+
+/** @def RF8_WILD_GRASS */
+#define RF8_WILD_GRASS 0x00000400
+
+/********* FREE *********/
+/** @def RF8_CTHANGBAND */
+#define RF8_CTHANGBAND 0x00001000
+
+/********* FREE *********/
+/** @def RF8_ZANGBAND */
+#define RF8_ZANGBAND 0x00004000
+
+/** @def RF8_JOKEANGBAND */
+#define RF8_JOKEANGBAND 0x00008000
+
+/** @def RF8_ANGBAND */
+#define RF8_ANGBAND 0x00010000
+
+
+/** @def RF8_WILD_TOO */
+#define RF8_WILD_TOO 0x80000000
+/** @} */
+
+/** @name Race flags #9
+ * @note New monster race bit flags
+ * @{ */
+/** @def RF9_DROP_CORPSE */
+#define RF9_DROP_CORPSE 0x00000001
+
+/** @def RF9_DROP_SKELETON */
+#define RF9_DROP_SKELETON 0x00000002
+
+/** @def RF9_HAS_LITE
+ * @note Carries a lite
+ */
+#define RF9_HAS_LITE 0x00000004
+/** @def RF9_MIMIC
+ * @note *REALLY* looks like an object ... only nastier
+ */
+#define RF9_MIMIC 0x00000008
+/** @def RF9_HAS_EGG
+ * @note Can be monster's eggs
+ */
+#define RF9_HAS_EGG 0x00000010
+/** @def RF9_IMPRESED
+ * @note The monster can follow you on each level until he dies
+ */
+#define RF9_IMPRESED 0x00000020
+/** @def RF9_SUSCEP_ACID
+ * @note Susceptible to acid
+ */
+#define RF9_SUSCEP_ACID 0x00000040
+/** @def RF9_SUSCEP_ELEC
+ * @note Susceptible to lightning
+ */
+#define RF9_SUSCEP_ELEC 0x00000080
+/** @def RF9_SUSCEP_POIS
+ * @note Susceptible to poison
+ */
+#define RF9_SUSCEP_POIS 0x00000100
+/** @def RF9_KILL_TREES
+ * @note Monster can eat trees
+ */
+#define RF9_KILL_TREES 0x00000200
+/** @def RF9_WYRM_PROTECT
+ * @note The monster is protected by great wyrms of power: They'll be summoned if it's killed
+ */
+#define RF9_WYRM_PROTECT 0x00000400
+/** @def RF9_DOPPLEGANGER
+ * @note The monster looks like you
+ */
+#define RF9_DOPPLEGANGER 0x00000800
+/** @def RF9_ONLY_DEPTH
+ * @note The monster can only be generated at the GIVEN depth
+ */
+#define RF9_ONLY_DEPTH 0x00001000
+/** @def RF9_SPECIAL_GENE
+ * @note The monster can only be generated in special conditions like quests, special dungeons, ...
+ */
+#define RF9_SPECIAL_GENE 0x00002000
+/** @def RF9_NEVER_GENE
+ * @note The monster cannot be normaly generated
+ */
+#define RF9_NEVER_GENE 0x00004000
+/** @} */
+
+/** @name Monster flags
+ * @{ */
+/** @def MFLAG_VIEW
+ * @note Monster is in line of sight
+ */
+#define MFLAG_VIEW 0x00000001
+/** @def MFLAG_QUEST
+ * @note Monster is subject to a quest
+ */
+#define MFLAG_QUEST 0x00000002
+/** @def MFLAG_PARTIAL
+ * @note Monster is a partial summon
+ */
+#define MFLAG_PARTIAL 0x00000004
+/** @def MFLAG_CONTROL
+ * @note Monster is controlled
+ */
+#define MFLAG_CONTROL 0x00000008
+/** @def MFLAG_BORN
+ * @note Monster is still being born
+ */
+#define MFLAG_BORN 0x00000010
+/** @def MFLAG_NICE
+ * @note Monster is still being nice
+ */
+#define MFLAG_NICE 0x00000020
+/** @def MFLAG_SHOW
+ * @note Monster is recently memorized
+ */
+#define MFLAG_SHOW 0x00000040
+/** @def MFLAG_MARK
+ * @note Monster is currently memorized
+ */
+#define MFLAG_MARK 0x00000080
+/** @def MFLAG_NO_DROP
+ * @note Monster wont drop obj/corpse
+ */
+#define MFLAG_NO_DROP 0x00000100
+/** @def MFLAG_QUEST2
+ * @note Monster is subject to a quest
+ */
+#define MFLAG_QUEST2 0x00000200
+/** @} */
+
+/** @struct monster_blow
+ * @brief Monster blows (attacks)
+ */
+struct monster_blow
+{
+ /** @structvar method
+ * @brief Number
+ */
+ byte method;
+ /** @structvar effect
+ * @brief Number
+ */
+ byte effect;
+ /** @structvar d_dice
+ * @brief Number
+ */
+ byte d_dice;
+ /** @structvar d_side
+ * @brief Number
+ */
+ byte d_side;
+};
+
+/** @struct monster_race
+ * @brief Monster race
+ */
+struct monster_race
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name (offset)
+ */
+ u32b name;
+ /** @structvar text
+ * @brief Number
+ * @note Text (offset)
+ */
+ u32b text;
+
+ /** @structvar hdice
+ * @brief Number
+ * @note Creatures hit dice count
+ */
+ byte hdice;
+ /** @structvar hside
+ * @brief Number
+ * @note Creatures hit dice sides
+ */
+ byte hside;
+
+ /** @structvar ac
+ * @brief Number
+ * @note Armour Class
+ */
+ s16b ac;
+
+ /** @structvar sleep
+ * @brief Number
+ * @note Inactive counter (base)
+ */
+ s16b sleep;
+ /** @structvar aaf
+ * @brief Number
+ * @note Area affect radius (1-100)
+ */
+ byte aaf;
+ /** @structvar speed
+ * @brief Number
+ * @note Speed (normally 110)
+ */
+ byte speed;
+
+ /** @structvar mexp
+ * @brief Number
+ * @note Exp value for kill
+ */
+ s32b mexp;
+
+ /** @structvar weight
+ * @brief Number
+ * @note Weight of the monster
+ */
+ s32b weight;
+
+ /** @structvar freq_inate
+ * @brief Number
+ * @note Inate spell frequency
+ */
+ byte freq_inate;
+ /** @structvar freq_spell
+ * @brief Number
+ * @note Other spell frequency
+ */
+ byte freq_spell;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note Flags 1 (general)
+ */
+ u32b flags1;
+ /** @structvar flags2
+ * @brief Number
+ * @note Flags 2 (abilities)
+ */
+ u32b flags2;
+ /** @structvar flags3
+ * @brief Number
+ * @note Flags 3 (race/resist)
+ */
+ u32b flags3;
+ /** @structvar flags4
+ * @brief Number
+ * @note Flags 4 (inate/breath)
+ */
+ u32b flags4;
+ /** @structvar flags5
+ * @brief Number
+ * @note Flags 5 (normal spells)
+ */
+ u32b flags5;
+ /** @structvar flags6
+ * @brief Number
+ * @note Flags 6 (special spells)
+ */
+ u32b flags6;
+ /** @structvar flags7
+ * @brief Number
+ * @note Flags 7 (movement related abilities)
+ */
+ u32b flags7;
+ /** @structvar flags8
+ * @brief Number
+ * @note Flags 8 (wilderness info)
+ */
+ u32b flags8;
+ /** @structvar flags9
+ * @brief Number
+ * @note Flags 9 (drops info)
+ */
+ u32b flags9;
+
+ /** @structvar blow[4]
+ * @brief magic_power
+ * @note Up to four blows per round
+ */
+ monster_blow blow[4];
+
+ /** @structvar body_parts[BODY_MAX]
+ * @brief Number
+ * @note To help to decide what to use when body changing
+ */
+ byte body_parts[BODY_MAX];
+
+ /** @structvar level
+ * @brief Number
+ * @note Level of creature
+ */
+ byte level;
+ /** @structvar rarity
+ * @brief Number
+ * @note Rarity of creature
+ */
+ byte rarity;
+
+
+ /** @structvar d_attr
+ * @brief Number
+ * @note Default monster attribute
+ */
+ byte d_attr;
+ /** @structvar d_char
+ * @brief String
+ * @note Default monster character
+ */
+ char d_char;
+
+
+ /** @structvar x_attr
+ * @brief Number
+ * @note Desired monster attribute
+ */
+ byte x_attr;
+ /** @structvar x_char
+ * @brief String
+ * @note Desired monster character
+ */
+ char x_char;
+
+
+ /** @structvar max_num
+ * @brief Number
+ * @note Maximum population allowed per level
+ */
+ s16b max_num;
+
+ /** @structvar cur_num
+ * @brief Number
+ * @note Monster population on current level
+ */
+ byte cur_num;
+
+
+ /** @structvar r_sights
+ * @brief Number
+ * @note Count sightings of this monster
+ */
+ s16b r_sights;
+ /** @structvar r_deaths
+ * @brief Number
+ * @note Count deaths from this monster
+ */
+ s16b r_deaths;
+
+ /** @structvar r_pkills
+ * @brief Number
+ * @note Count monsters killed in this life
+ */
+ s16b r_pkills;
+ /** @structvar r_tkills
+ * @brief Number
+ * @note Count monsters killed in all lives
+ */
+ s16b r_tkills;
+
+ /** @structvar r_wake
+ * @brief Number
+ * @note Number of times woken up (?)
+ */
+ byte r_wake;
+ /** @structvar r_ignore
+ * @brief Number
+ * @note Number of times ignored (?)
+ */
+ byte r_ignore;
+
+ /** @structvar r_xtra1
+ * @brief Number
+ * @note Something (unused)
+ */
+ byte r_xtra1;
+ /** @structvar r_xtra2
+ * @brief Number
+ * @note Something (unused)
+ */
+ byte r_xtra2;
+
+ /** @structvar r_drop_gold
+ * @brief Number
+ * @note Max number of gold dropped at once
+ */
+ byte r_drop_gold;
+ /** @structvar r_drop_item
+ * @brief Number
+ * @note Max number of item dropped at once
+ */
+ byte r_drop_item;
+
+ /** @structvar r_cast_inate
+ * @brief Number
+ * @note Max number of inate spells seen
+ */
+ byte r_cast_inate;
+ /** @structvar r_cast_spell
+ * @brief Number
+ * @note Max number of other spells seen
+ */
+ byte r_cast_spell;
+
+ /** @structvar r_blows[4]
+ * @brief Number
+ * @note Number of times each blow type was seen
+ */
+ byte r_blows[4];
+
+ /** @structvar r_flags1
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags1;
+ /** @structvar r_flags2
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags2;
+ /** @structvar r_flags3
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags3;
+ /** @structvar r_flags4
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags4;
+ /** @structvar r_flags5
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags5;
+ /** @structvar r_flags6
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags6;
+ /** @structvar r_flags7
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags7;
+ /** @structvar r_flags8
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags8;
+ /** @structvar r_flags9
+ * @brief Number
+ * @note Observed racial flags
+ */
+ u32b r_flags9;
+
+ /** @structvar on_saved
+ * @brief Boolean
+ * @note Is the (unique) on a saved level ?
+ */
+ bool on_saved;
+
+ /** @structvar total_visible
+ * @brief Number
+ * @note Amount of this race that are visible
+ */
+ byte total_visible;
+
+ /** @structvar drops
+ * @brief obj_theme
+ * @note The drops type
+ */
+ obj_theme drops;
+};
+
+/** @struct monster_type
+ * @brief Monster type
+ */
+struct monster_type
+{
+ /** @structvar r_idx
+ * @brief Number
+ * @note Monster race index
+ */
+ s16b r_idx;
+
+ /** @structvar ego
+ * @brief Number
+ * @note Ego monster type
+ */
+ u16b ego;
+
+ /** @structvar fy
+ * @brief Number
+ * @note Y location on map
+ */
+ byte fy;
+ /** @structvar fx
+ * @brief Number
+ * @note X location on map
+ */
+ byte fx;
+
+ /** @structvar hp
+ * @brief Number
+ * @note Current Hit points
+ */
+ s16b hp;
+ /** @structvar maxhp
+ * @brief Number
+ * @note Max Hit points
+ */
+ s16b maxhp;
+
+ /** @structvar blow[4]
+ * @brief magic_power
+ * @note Up to four blows per round
+ */
+ monster_blow blow[4];
+
+ /** @structvar speed
+ * @brief Number
+ * @note Speed (normally 110)
+ */
+ byte speed;
+ /** @structvar level
+ * @brief Number
+ * @note Level of creature
+ */
+ byte level;
+ /** @structvar ac
+ * @brief Number
+ * @note Armour Class
+ */
+ s16b ac;
+ /** @structvar exp
+ * @brief Number
+ * @note Experience
+ */
+ u32b exp;
+
+ /** @structvar csleep
+ * @brief Number
+ * @note Inactive counter
+ */
+ s16b csleep;
+
+ /** @structvar mspeed
+ * @brief Number
+ * @note Monster "speed"
+ */
+ byte mspeed;
+ /** @structvar energy
+ * @brief Number
+ * @note Monster "energy"
+ */
+ byte energy;
+
+ /** @structvar stunned
+ * @brief Number
+ * @note Monster is stunned
+ */
+ byte stunned;
+ /** @structvar confused
+ * @brief Number
+ * @note Monster is confused
+ */
+ byte confused;
+ /** @structvar monfear
+ * @brief Number
+ * @note Monster is afraid
+ */
+ byte monfear;
+
+ /** @structvar bleeding
+ * @brief Number
+ * @note Monster is bleeding
+ */
+ s16b bleeding;
+ /** @structvar poisoned
+ * @brief Number
+ * @note Monster is poisoned
+ */
+ s16b poisoned;
+
+ /** @structvar cdis
+ * @brief Number
+ * @note Current dis from player
+ */
+ byte cdis;
+
+ /** @structvar mflag
+ * @brief Number
+ * @note Extra monster flags
+ */
+ s32b mflag;
+
+ /** @structvar ml
+ * @brief Boolean
+ * @note Monster is "visible"
+ */
+ bool ml;
+
+ /** @structvar hold_o_idx
+ * @brief Number
+ * @note Object being held (if any)
+ */
+ s16b hold_o_idx;
+
+ /** @structvar smart
+ * @brief Number
+ * @note Field for "smart_learn"
+ */
+ u32b smart;
+
+ /** @structvar status
+ * @brief Number
+ * @note Status(friendly, pet, companion, ..)
+ */
+ s16b status;
+
+ /** @structvar target
+ * @brief Number
+ * @note Monster target
+ */
+ s16b target;
+
+ /** @structvar possessor
+ * @brief Number
+ * @note Is it under the control of a possessor ?
+ */
+ s16b possessor;
+};
+
+$static monster_type *lua_get_monster(int m_idx){return (&m_list[m_idx]);}
+/** @fn monster(int m_idx);
+ * @brief Return the monster with index "m_idx" in the monster list.\n
+ * @param m_idx Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @return monster_type \n The monster.
+ * @note (see file w_mnster.c)
+ */
+static monster_type *lua_get_monster @ monster(int m_idx);
+
+/** @var m_list[max_m_idx]
+ * @brief monster_type
+ * @note List of monsters
+ */
+extern monster_type m_list[max_m_idx];
+
+/** @fn race_info_idx(int r_idx, int ego)
+ * @brief Get monster info and ego info for monster with monster index "r_idx"
+ * and monster ego "ego".\n
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @return monster_race \n The monster race.
+ * @note
+ * If "ego" > 0, the ego information is applied to the monster information and
+ * the new monster information is returned.
+ * @note
+ * For example, race_info_idx(141,7) will create a brown yeek (monster)
+ * shaman (ego).
+ * @note (see file monster2.c)
+ */
+extern monster_race* race_info_idx(int r_idx, int ego);
+
+/** @fn delete_monster_idx(int i)
+ * @brief Delete monster "i" from the monster array.\n
+ * @param i Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @note (see file monster2.c)
+ */
+extern void delete_monster_idx(int i);
+
+/** @fn m_pop(void)
+ * @brief Get an empty slot in the monster list.
+ * @return Number \n The index of an empty slot the monster list.
+ * @note
+ * If there are no empty slots, a slot will be reclaimed from a "dead"
+ * monster. If all slots are full, 0 is returned, which means the function
+ * has failed ("Too many monsters!").
+ * @note (see file monster2.c)
+ */
+extern s16b m_pop(void);
+
+/** @fn get_mon_num_prep(void)
+ * @brief Apply a "monster restriction function" to the "monster allocation
+ * table".
+ * @return Number \n 0 (success) always.
+ * @note
+ * There are no parameters, but there are some other variables which will
+ * need to be set. They are get_mon_num_hook and get_mon_num2_hook. They
+ * are pointers to functions.
+ * @note
+ * For example, get_mon_num_hook = monster_volcano means when
+ * get_mon_num_hook is called (*get_mon_num_hook)(index), the actual
+ * function called is monster_volcano(index). This particular function
+ * returns TRUE if the monster indicated by "index" has the
+ * RF8_WILD_VOLCANO flag set.
+ * @note
+ * It is a good idea to store the old value of get_mon_num_hook before
+ * setting a new one, and restoring it when your function is finished.
+ * @note
+ * Following is a list of functions which can be assigned to
+ * get_mon_num_hook:\n
+ * create_molds_hook\n
+ * create_townpeople_hook\n
+ * mon_hook_bounty\n
+ * monster_dungeon\n
+ * monster_grass\n
+ * monster_mountain\n
+ * monster_ocean\n
+ * monster_quest\n
+ * monster_shore\n
+ * monster_town\n
+ * monster_volcano\n
+ * monster_waste\n
+ * monster_wood\n
+ * mutate_monster_okay\n
+ * place_monster_okay\n
+ * summon_specific_okay\n
+ * vault_aux_animal\n
+ * vault_aux_chapel\n
+ * vault_aux_clone\n
+ * vault_aux_demon\n
+ * vault_aux_dragon\n
+ * vault_aux_giant\n
+ * vault_aux_jelly\n
+ * vault_aux_kennel\n
+ * vault_aux_orc\n
+ * vault_aux_symbol\n
+ * vault_aux_treasure\n
+ * vault_aux_troll\n
+ * vault_aux_undead
+ * @note
+ * Or you can write your own. The function must take an integer (index)
+ * as a parameter and return boolean (TRUE if the monster is selected,
+ * or FALSE if it is not).
+ * @note (see file monster2.c)
+ */
+extern errr get_mon_num_prep(void);
+
+/** @fn get_mon_num(int level)
+ * @brief For the given level "level", return the index of an appropriate
+ * monster race.\n
+ * @param level Number \n a dungeon level (which is adjusted before
+ * it is used).
+ * @brief Dungeon level
+ * @return Number \n The index of a monster race in the monster race array.
+ * @note
+ * This function uses the "prob2" field of the "monster allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" monster, in
+ * a relatively efficient manner.
+ * @note
+ * Note that "town" monsters will *only* be created in the town, and
+ * "normal" monsters will *never* be created in the town, unless the
+ * "level" is "modified", for example, by polymorph or summoning.
+ * @note
+ * There is a small chance (1/50) of "boosting" the given depth by
+ * a small amount (up to four levels), except in the town.
+ * @note
+ * It is (slightly) more likely to acquire a monster of the given level
+ * than one of a lower level. This is done by choosing several monsters
+ * appropriate to the given level and keeping the "hardest" one.
+ * @note
+ * Note that if no monsters are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ * @note (see file monster2.c)
+ */
+extern s16b get_mon_num(int level);
+
+$static char *lua_monster_desc(monster_type *m_ptr, int mode){static char buf[200]; monster_desc(buf, m_ptr, mode); return buf;}
+/** @fn monster_desc(monster_type *m_ptr, int mode);
+ * @brief Return a monster description for monster "monster_type" using flag
+ * "mode".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @param mode Number \n description mode (see below)
+ * @brief Description mode
+ * @return String \n The description of the monster.
+ * @note
+ * We can correctly describe monsters based on their visibility.\n
+ * We can force all monsters to be treated as visible or invisible.\n
+ * We can build nominatives, objectives, possessives, or reflexives.\n
+ * We can selectively pronominalize hidden, visible, or all monsters.\n
+ * We can use definite or indefinite descriptions for hidden monsters.\n
+ * We can use definite or indefinite descriptions for visible monsters.
+ * @note
+ * Pronominalization involves the gender whenever possible and allowed,
+ * so that by cleverly requesting pronominalization / visibility, you
+ * can get messages like "You hit someone. She screams in agony!".
+ * @note
+ * Reflexives are acquired by requesting Objective plus Possessive.
+ * @note
+ * If no m_ptr arg is given (?), the monster is assumed to be hidden,
+ * unless the "Assume Visible" mode is requested.
+ * @note
+ * If no r_ptr arg is given, it is extracted from m_ptr and r_info
+ * If neither m_ptr nor r_ptr is given, the monster is assumed to
+ * be neuter, singular, and hidden (unless "Assume Visible" is set),
+ * in which case you may be in trouble... :-)
+ * @note
+ * I am assuming that no monster name is more than 70 characters long,
+ * so that "char desc[80];" is sufficiently large for any result.
+ * @note
+ * Mode Flags:\n
+ * 0x01 --> Objective (or Reflexive)\n
+ * 0x02 --> Possessive (or Reflexive)\n
+ * 0x04 --> Use indefinites for hidden monsters ("something")\n
+ * 0x08 --> Use indefinites for visible monsters ("a kobold")\n
+ * 0x10 --> Pronominalize hidden monsters\n
+ * 0x20 --> Pronominalize visible monsters\n
+ * 0x40 --> Assume the monster is hidden\n
+ * 0x80 --> Assume the monster is visible
+ * @note
+ * Useful Modes:\n
+ * 0x00 --> Full nominative name ("the kobold") or "it"\n
+ * 0x04 --> Full nominative name ("the kobold") or "something"\n
+ * 0x80 --> Genocide resistance name ("the kobold")\n
+ * 0x88 --> Killing name ("a kobold")\n
+ * 0x22 --> Possessive, genderized if visible ("his") or "its"\n
+ * 0x23 --> Reflexive, genderized if visible ("himself") or "itself"
+ * @note (see file monster2.c)
+ */
+static char *lua_monster_desc @ monster_desc(monster_type *m_ptr, int mode);
+
+$static char *lua_monster_race_desc(int r_idx, int ego){static char buf[200]; monster_race_desc(buf, r_idx, ego); return buf;}
+/** @fn monster_race_desc(int r_idx, int ego);
+ * @brief Return the monster description for monster with monster index
+ * "r_idx" and monster ego "ego".\n
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @return String \n The description of the monster race.
+ * @note
+ * The monster description is made up of the ego name (if any) and monster
+ * name, or the unique name.
+ * @note (see file w_mnster.c)
+ */
+static char *lua_monster_race_desc @ monster_race_desc(int r_idx, int ego);
+
+/** @fn monster_race_desc(char *desc, int r_idx, int ego)
+ * @brief Return the monster description "desc" for monster with monster index
+ * "r_idx" and monster ego "ego".\n
+ * @param *desc String
+ * @brief Description
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @return *desc String \n The description of the monster race.
+ * @note
+ * The monster description is made up of the ego name (if any) and monster
+ * name, or the unique name.
+ * @note (see file monster2.c)
+ */
+extern void monster_race_desc(char *desc, int r_idx, int ego);
+
+/** @fn monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr)
+ * @brief Allow monster "m_ptr" with monster index "m_idx" to carry object
+ * "q_ptr".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @param m_idx Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @param *q_ptr object_type \n the object
+ * @brief Object
+ * @note
+ * The monster can only carry the object if there is room for the object in the
+ * object list.
+ * @note (see file monster2.c)
+ */
+extern void monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr);
+
+/** @fn place_monster_aux(int y, int x, int r_idx, bool slp, bool grp, int status)
+ * @brief Attempt to place a monster with monster race index "r_idx" and status
+ * "status" at grid "y", "x". The monster may be asleep ("slp") or surrounded
+ * by a group of identical monsters ("grp").\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param slp Boolean \n TRUE if monster is asleep, otherwise FALSE
+ * @brief Asleep?
+ * @param grp Boolean \n TRUE if monster appears in a group, otherwise FALSE
+ * @brief Group?
+ * @param status Number \n the status of the monster from the player's point
+ * of view (see MSTATUS_foo flags)
+ * @brief Monster status
+ * @return Boolean \n TRUE if the monster is placed successfully, otherwise
+ * FALSE.
+ * @note
+ * Note that certain monsters are now marked as requiring "friends".
+ * These monsters, if successfully placed, and if the "grp" parameter
+ * is TRUE, will be surrounded by a "group" of identical monsters.
+ * @note
+ * Note that certain monsters are now marked as requiring an "escort",
+ * which is a collection of monsters with similar "race" but lower
+ * level.
+ * @note
+ * Some monsters induce a fake "group" flag on their escorts.
+ * @note
+ * Note the "bizarre" use of non-recursion to prevent annoying output
+ * when running a code profiler.
+ * @note
+ * Note the use of the new "monster allocation table" code to restrict
+ * the "get_mon_num()" function to "legal" escort types.
+ * @note (see file monster2.c)
+ */
+extern bool place_monster_aux(int y, int x, int r_idx, bool slp, bool grp, int status);
+
+/** @fn place_monster(int y, int x, bool slp, bool grp)
+ * @brief Attempt to place a monster at grid "y", "x". The monster may be
+ * asleep ("slp") or surrounded by a group of identical monsters ("grp").\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param slp Boolean \n TRUE if monster is asleep, otherwise FALSE
+ * @brief Asleep?
+ * @param grp Boolean \n TRUE if monster appears in a group, otherwise FALSE
+ * @brief Group?
+ * @return Boolean \n TRUE if the monster is placed successfully, otherwise
+ * FALSE.
+ * @note
+ * Attempt to find a monster appropriate to the "monster_level"
+ * @note (see file monster2.c)
+ */
+extern bool place_monster(int y, int x, bool slp, bool grp);
+
+/** @fn place_monster_one(int y, int x, int r_idx, int ego, bool slp, int status)
+ * @brief Attempt to place a monster with monster race index "r_idx", monster
+ * ego "ego" and status "status" at grid "y", "x". The monster may be asleep
+ * ("slp").\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param r_idx Number \n the index of the race in the monster race array
+ * @brief Race index
+ * @param ego Number \n the index of the ego in the monster ego array
+ * @brief Ego index
+ * @param slp Boolean \n TRUE if monster is asleep, otherwise FALSE
+ * @brief Asleep?
+ * @param status Number \n the status of the monster from the player's point
+ * of view (see MSTATUS_foo flags)
+ * @brief Monster status
+ * @return Number \n The index of the placed monster in the monster list.
+ * @note
+ * To give the player a sporting chance, any monster that appears in
+ * line-of-sight and is extremely dangerous can be marked as
+ * "FORCE_SLEEP", which will cause them to be placed with low energy,
+ * which often (but not always) lets the player move before they do.
+ * @note
+ * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
+ * @note
+ * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
+ * remove old "cur_num" and "max_num" fields.
+ * @note
+ * XXX XXX XXX Actually, do something similar for artifacts, to simplify
+ * the "preserve" mode, and to make the "what artifacts" flag more useful.
+ * @note
+ * This is the only function which may place a monster in the dungeon,
+ * except for the savefile loading code.
+ * @note (see file monster2.c)
+ */
+extern s16b place_monster_one(int y, int x, int r_idx, int ego, bool slp, int status);
+
+/** @fn is_friend(monster_type *m_ptr)
+ * @brief Return a value to indicate the status of monster "m_ptr".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @return Number \n -1 if monster is an enemy, 0 if it is neutral, and 1 if
+ * it is friendly.
+ * @note (see file monster3.c)
+ */
+extern int is_friend(monster_type *m_ptr);
+
+/** @fn is_enemy(monster_type *m_ptr, monster_type *t_ptr)
+ * @brief Determine if monster "m_ptr" should attack monster "t_ptr".\n
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @param *t_ptr monster_type \n the target monster
+ * @brief Target monster
+ * @return Boolean \n TRUE if monster "m_ptr" should attack monster "t_ptr",
+ * otherwise FALSE.
+ * @note
+ * If "m_ptr" is stupid and "r_ptr" is a different type of monster then the
+ * function will return TRUE.\n
+ * If "m_ptr" is not neutral and "r_ptr" is a breeder, and "r_ptr" is a
+ * different type of monster then the function will return TRUE (and vice
+ * versa).\n
+ * If both monsters are not neutral and one is friendly and the other isn't
+ * then the function will return TRUE.
+ * @note (see file monster3.c)
+ */
+extern bool is_enemy(monster_type *m_ptr, monster_type *t_ptr);
+
+/** @fn change_side(monster_type *m_ptr)
+ * @brief Change the status of monster "m_ptr" from friendly to unfriendly and
+ * vice versa.
+ * @param *m_ptr monster_type \n the monster
+ * @brief Monster
+ * @return Boolean \n TRUE if the status changed, otherwise FALSE.
+ * @note
+ * Friends and pets become enemies.\n
+ * Neutral monsters become neutral pets and vice versa.\n
+ * Companions are unaffected.
+ * @note (see file monster3.c)
+ */
+extern bool change_side(monster_type *m_ptr);
+
+/** @fn find_position(int y, int x, int *yy = 0, int *xx = 0)
+ * @brief Find a new grid "yy", "xx" within 6 grids of target grid "y", "x".\n
+ * @param y Number \n the y coordinate of the origin grid
+ * @brief Origin y-coordinate
+ * @param x Number \n the x coordinate of the origin grid
+ * @brief Origin x-coordinate
+ * @param yy Number \n the y coordinate of the target grid
+ * @brief Target y-coordinate
+ * @param xx Number \n the x coordinate of the target grid
+ * @brief Target x-coordinate
+ * @note
+ * The new grid must be within line-of-sight of the target grid. A
+ * maximum of 5000 attempts is made.
+ * @note (see file lua_bind.c)
+ */
+extern void find_position(int y, int x, int *yy = 0, int *xx = 0);
+
+/** @var summon_specific_level
+ * @brief Number
+ * @note
+ * Force summoned monsters to be at this level.
+ */
+extern int summon_specific_level;
+
+/** @var summon_kin_type
+ * @brief String
+ * @note
+ * The monster character for those monsters who can summon kin.
+ */
+extern char summon_kin_type;
+
+/** @fn summon_specific(int y1, int x1, int lev, int type)
+ * @brief Place a monster of type "type" near grid "y","x".\n
+ * @param y1 Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x1 Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param lev Number \n the monster level of the summoning monster
+ * @brief Summoner level
+ * @param type Number \n the type of summoned monster
+ * @brief Monster type
+ * @return Boolean \n TRUE if a monster was summoned, otherwise FALSE.
+ * @note
+ * We will attempt to place the monster up to 20 times before giving up.
+ * @note
+ * Note: SUMMON_UNIQUE and SUMMON_WRAITH (XXX) will summon Unique's\n
+ * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's\n
+ * Note: None of the other summon codes will ever summon Unique's.
+ * @note
+ * This function has been changed. We now take the "monster level"
+ * of the summoning monster as a parameter, and use that, along with
+ * the current dungeon level, to help determine the level of the
+ * desired monster. Note that this is an upper bound, and also
+ * tends to "prefer" monsters of that level. Currently, we use
+ * the average of the dungeon and monster levels, and then add
+ * five to allow slight increases in monster power.
+ * @note
+ * Note that we use the new "monster allocation table" creation code
+ * to restrict the "get_mon_num()" function to the set of "legal"
+ * monsters, making this function much faster and more reliable.
+ * @note
+ * Note that this function may not succeed, though this is very rare.
+ * @note (see file monster2.c)
+ */
+extern bool summon_specific(int y1, int x1, int lev, int type);
+
+/** @fn summon_specific_friendly(int y1, int x1, int lev, int type, bool Group_ok)
+ * @brief Place a friendly monster of type "type" near grid "y","x". The
+ * monster may be surrounded by a group of identical monsters ("Group_ok").\n
+ * @param y1 Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x1 Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param lev Number \n the monster level of the summoning monster
+ * @brief Summoner level
+ * @param type Number \n the type of summoned monster
+ * @brief Monster type
+ * @param Group_ok Boolean \n TRUE if monster appears in a group, otherwise
+ * FALSE
+ * @brief Group?
+ * @return Boolean \n TRUE if a monster was summoned, otherwise FALSE.
+ * @note (see file monster2.c)
+ */
+extern bool summon_specific_friendly(int y1, int x1, int lev, int type, bool Group_ok);
+
+/** @fn summon_monster_aux(int y, int x, int lev, bool friend, char *fct);
+ * @brief Place a monster near grid "y","x".\n
+ * @param y Number \n the y coordinate of the target grid
+ * @brief Y-coordinate
+ * @param x Number \n the x coordinate of the target grid
+ * @brief X-coordinate
+ * @param lev Number \n the monster level of the summoning monster
+ * @brief Summoner level
+ * @param friend Boolean \n TRUE if friendly monsters are to be summoned,
+ * otherwise FALSE
+ * @brief Friendly?
+ * @param *fct String \n the function which determines which type of monster
+ * will be summoned
+ * @brief Monster type function
+ * @return Boolean \n TRUE if a monster was summoned, otherwise FALSE.
+ * @note (see file w_mnster.c)
+ */
+extern bool lua_summon_monster @ summon_monster_aux(int y, int x, int lev, bool friend, char *fct);
+
+/** @fn can_create_companion()
+ * @brief Determine if a companion can be created.
+ * @return Boolean \n TRUE if a companion can be created, otherwise FALSE.
+ * @note
+ * The companions are counted. If this is less than the number allowed by
+ * the player monster lore skill, the function returns TRUE, otherwise the
+ * function returns FALSE.
+ * @note (see file monster3.c)
+ */
+extern bool can_create_companion();
+
+/** @fn monster_set_level(int m_idx, int level)
+ * @brief Set a new level for monster with monster index "m_idx".\n
+ * @param m_idx Number \n the index of the monster in the monster list
+ * @brief Monster index
+ * @param level Number \n the new level of the monster
+ * @brief Monster level
+ * @note
+ * The new level can not exceed 150. If the new level is higher than the
+ * monster level, the monster experience value is recalculated.
+ * @note (see file monster2.c)
+ */
+extern void monster_set_level(int m_idx, int level);
+
+/** @name Summon types
+ * @note Legal restrictions for "summon_specific()"
+ */
+/** @def SUMMON_ANT
+ * @note Summon giant ant (a) excluding uniques.
+ */
+#define SUMMON_ANT 11
+
+/** @def SUMMON_SPIDER
+ * @note Summon spider/scorpion/tick (S) excluding uniques.
+ */
+#define SUMMON_SPIDER 12
+
+/** @def SUMMON_HOUND
+ * @note Summon canine (C) or zephyr hound (Z) excluding uniques.
+ */
+#define SUMMON_HOUND 13
+
+/** @def SUMMON_HYDRA
+ * @note Summon multi-headed hydra (M) excluding uniques.
+ */
+#define SUMMON_HYDRA 14
+
+/** @def SUMMON_ANGEL
+ * @note Summon angelic being (A) excluding uniques.
+ */
+#define SUMMON_ANGEL 15
+
+/** @def SUMMON_DEMON
+ * @note Summon demon (RF3_DEMON) excluding uniques.
+ */
+#define SUMMON_DEMON 16
+
+/** @def SUMMON_UNDEAD
+ * @note Summon undead (RF3_UNDEAD) excluding uniques.
+ */
+#define SUMMON_UNDEAD 17
+
+/** @def SUMMON_DRAGON
+ * @note Summon dragon (RF3_DRAGON) excluding uniques.
+ */
+#define SUMMON_DRAGON 18
+
+/** @def SUMMON_HI_UNDEAD
+ * @note Summon lich (L) or vampire (V) or wight/wraith (W) including uniques.
+ */
+#define SUMMON_HI_UNDEAD 21
+
+/** @def SUMMON_HI_DRAGON
+ * @note Summon ancient dragon (D) including uniques.
+ */
+#define SUMMON_HI_DRAGON 22
+
+/** @def SUMMON_WRAITH
+ * @note Summon wight/wraith (W) including uniques.
+ */
+#define SUMMON_WRAITH 31
+
+/** @def SUMMON_UNIQUE
+ * @note Summon unique (RF1_UNIQUE).
+ */
+#define SUMMON_UNIQUE 32
+
+/** @def SUMMON_BIZARRE1
+ * @note Summon mold (m) excluding uniques.
+ */
+#define SUMMON_BIZARRE1 33
+
+/** @def SUMMON_BIZARRE2
+ * @note Summon giant bat (b) excluding uniques.
+ */
+#define SUMMON_BIZARRE2 34
+
+/** @def SUMMON_BIZARRE3
+ * @note Summon quylthulg (Q) excluding uniques.
+ */
+#define SUMMON_BIZARRE3 35
+
+/** @def SUMMON_BIZARRE4
+ * @note Summon vortex (v) excluding uniques.
+ */
+#define SUMMON_BIZARRE4 36
+
+/** @def SUMMON_BIZARRE5
+ * @note Summon creeping coins ($) excluding uniques.
+ */
+#define SUMMON_BIZARRE5 37
+
+/** @def SUMMON_BIZARRE6
+ * @note Summon mimic (!?=$|) excluding uniques.
+ */
+#define SUMMON_BIZARRE6 38
+
+/** @def SUMMON_HI_DEMON
+ * @note Summon demon (RF3_DEMON) and major demon (U) excluding uniques.
+ */
+#define SUMMON_HI_DEMON 39
+
+/** @def SUMMON_KIN
+ * @note Summon monster of the same character type excluding uniques.
+ */
+#define SUMMON_KIN 40
+
+/** @def SUMMON_DAWN
+ * @note Summon monster with "the Dawn" in the name excluding uniques.
+ */
+#define SUMMON_DAWN 41
+
+/** @def SUMMON_ANIMAL
+ * @note Summon animal (RF3_ANIMAL) excluding uniques.
+ */
+#define SUMMON_ANIMAL 42
+
+/** @def SUMMON_ANIMAL_RANGER
+ * @note Summon animal (RF3_ANIMAL) and giant ant, giant bat, centipede,
+ * feline,giant louse, quadruped, rodent, worm or worm mass, bird, canine,
+ * insect, snake, killer beetle, multi-headed hydra, reptile/amphibian,
+ * spider/scorpion/tick (abcflqrwBCIJKMRS) and not dragon (RF3_DRAGON) and not
+ * evil (RF3_EVIL) and not undead (RF3_UNDEAD) and not demon (RF3_DEMON) and
+ * not inate/breath and not normal spells and not special spells excluding
+ * uniques.
+ */
+#define SUMMON_ANIMAL_RANGER 43
+
+/** @def SUMMON_HI_UNDEAD_NO_UNIQUES
+ * @note Summon lich (L) or vampire (V) or wight/wraith (W) excluding uniques.
+ */
+#define SUMMON_HI_UNDEAD_NO_UNIQUES 44
+
+/** @def SUMMON_HI_DRAGON_NO_UNIQUES
+ * @note Summon ancient dragon (D) excluding uniques.
+ */
+#define SUMMON_HI_DRAGON_NO_UNIQUES 45
+
+/** @def SUMMON_NO_UNIQUES
+ * @note Summon non-uniques (not RF1_UNIQUE).
+ */
+#define SUMMON_NO_UNIQUES 46
+
+/** @def SUMMON_PHANTOM
+ * @note Summon monster with "Phantom" in the name excluding uniques.
+ */
+#define SUMMON_PHANTOM 47
+
+/** @def SUMMON_ELEMENTAL
+ * @note Summon monster with "lemental" in the name excluding uniques.
+ */
+#define SUMMON_ELEMENTAL 48
+
+/** @def SUMMON_THUNDERLORD
+ * @note Summon thunderlords (RF3_THUNDERLORD) including uniques.
+ */
+#define SUMMON_THUNDERLORD 49
+
+/** @def SUMMON_BLUE_HORROR
+ * @note Summon monster with "lue horror" in the name excluding uniques.
+ */
+#define SUMMON_BLUE_HORROR 50
+
+/** @def SUMMON_BUG
+ * @note Summon monster with "Software bug" in the name excluding uniques.
+ */
+#define SUMMON_BUG 51
+
+/** @def SUMMON_RNG
+ * @note Summon monster with "Random Number Generator" in the name excluding
+ * uniques.
+ */
+#define SUMMON_RNG 52
+
+/** @def SUMMON_MINE
+ * @note Summon mines (RF1_NEVER_MOVE) including uniques.
+ */
+#define SUMMON_MINE 53
+
+/** @def SUMMON_HUMAN
+ * @note Summon (p) excluding uniques.
+ */
+#define SUMMON_HUMAN 54
+
+/** @def SUMMON_SHADOWS
+ * @note Summon ghost (G) excluding uniques.
+ */
+#define SUMMON_SHADOWS 55
+
+/** @def SUMMON_GHOST
+ * @note Summon ghost (G) including uniques.
+ */
+#define SUMMON_GHOST 56
+
+/** @def SUMMON_QUYLTHULG
+ * @note Summon (Q) excluding uniques.
+ */
+#define SUMMON_QUYLTHULG 57
+
+/** @def SUMMON_LUA
+ * @note Summon monsters according to a Lua script.
+ */
+#define SUMMON_LUA 58
+/** @} */
+
+/** @fn do_control_reconnect()
+ * @brief Find the controlled monster and reconnect to it.
+ * @return Boolean \n TRUE if there is a controlled monster, otherwise FALSE.
+ * @note
+ * The monster list is scanned for a monster with MFLAG_CONTROL. If it is
+ * found, the function returns TRUE.
+ * @note (see file monster3.c)
+ */
+extern bool do_control_reconnect();
+
+/* monster thing */
+/** @var m_max
+ * @brief Number
+ * @note The number of monsters currently in the monster list.
+ */
+extern s16b m_max;
diff --git a/src/monster1.c b/src/monster1.c
new file mode 100644
index 00000000..790453bc
--- /dev/null
+++ b/src/monster1.c
@@ -0,0 +1,1998 @@
+/* File: monster1.c */
+
+/* Purpose: describe monsters (using monster memory) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Christopher J. Stuart
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Pronoun arrays, by gender.
+ */
+static cptr wd_he[3] = { "it", "he", "she" };
+static cptr wd_his[3] = { "its", "his", "her" };
+
+
+/*
+ * Pluralizer. Args(count, singular, plural)
+ */
+#define plural(c,s,p) \
+(((c) == 1) ? (s) : (p))
+
+
+
+
+
+
+/*
+ * Determine if the "armor" is known
+ * The higher the level, the fewer kills needed.
+ */
+static bool know_armour(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ s32b level = r_ptr->level;
+
+ s32b kills = r_ptr->r_tkills;
+
+ /* Normal monsters */
+ if (kills > 304 / (4 + level)) return (TRUE);
+
+ /* Skip non-uniques */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE))) return (FALSE);
+
+ /* Unique monsters */
+ if (kills > 304 / (38 + (5*level) / 4)) return (TRUE);
+
+ /* Assume false */
+ return (FALSE);
+}
+
+
+/*
+ * Determine if the "damage" of the given attack is known
+ * the higher the level of the monster, the fewer the attacks you need,
+ * the more damage an attack does, the more attacks you need
+ */
+static bool know_damage(int r_idx, int i)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ s32b level = r_ptr->level;
+
+ s32b a = r_ptr->r_blows[i];
+
+ s32b d1 = r_ptr->blow[i].d_dice;
+ s32b d2 = r_ptr->blow[i].d_side;
+
+ s32b d = d1 * d2;
+
+ /* Normal monsters */
+ if ((4 + level) * a > 80 * d) return (TRUE);
+
+ /* Skip non-uniques */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE))) return (FALSE);
+
+ /* Unique monsters */
+ if ((4 + level) * (2 * a) > 80 * d) return (TRUE);
+
+ /* Assume false */
+ return (FALSE);
+}
+
+
+/*
+ * Hack -- display monster information using "text_out()"
+ *
+ * Note that there is now a compiler option to only read the monster
+ * descriptions from the raw file when they are actually needed, which
+ * saves about 60K of memory at the cost of disk access during monster
+ * recall, which is optional to the user.
+ *
+ * This function should only be called with the cursor placed at the
+ * left edge of the screen, on a cleared line, in which the recall is
+ * to take place. One extra blank line is left after the recall.
+ */
+static void roff_aux(int r_idx, int ego, int remem)
+{
+ monster_race *r_ptr;
+
+ bool old = FALSE;
+ bool sin = FALSE;
+
+ int m, n, r;
+
+ cptr p, q;
+
+ int msex = 0;
+
+ bool breath = FALSE;
+ bool magic = FALSE;
+
+ u32b flags1;
+ u32b flags2;
+ u32b flags3;
+ u32b flags4;
+ u32b flags5;
+ u32b flags6;
+ u32b flags7;
+ u32b flags8;
+ u32b flags9;
+
+ int vn = 0;
+ byte color[64];
+ cptr vp[64];
+
+ monster_race save_mem;
+
+
+
+#if 0
+
+ /* Nothing erased */
+ roff_old = 0;
+
+ /* Reset the row */
+ roff_row = 1;
+
+ /* Reset the pointer */
+ roff_p = roff_buf;
+
+ /* No spaces yet */
+ roff_s = NULL;
+
+#endif
+
+
+ /* Access the race and lore */
+ r_ptr = race_info_idx(r_idx, ego);
+
+
+ /* Cheat -- Know everything */
+ if (cheat_know)
+ {
+ /* XXX XXX XXX */
+
+ /* Save the "old" memory */
+ save_mem = *r_ptr;
+
+ /* Hack -- Maximal kills */
+ r_ptr->r_tkills = MAX_SHORT;
+
+ /* Hack -- Maximal info */
+ r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR;
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags7 = r_ptr->flags7;
+ r_ptr->r_flags8 = r_ptr->flags8;
+ r_ptr->r_flags9 = r_ptr->flags9;
+ }
+
+
+ /* Extract a gender (if applicable) */
+ if (r_ptr->flags1 & (RF1_FEMALE)) msex = 2;
+ else if (r_ptr->flags1 & (RF1_MALE)) msex = 1;
+
+
+ /* Obtain a copy of the "known" flags */
+ flags1 = (r_ptr->flags1 & r_ptr->r_flags1);
+ flags2 = (r_ptr->flags2 & r_ptr->r_flags2);
+ flags3 = (r_ptr->flags3 & r_ptr->r_flags3);
+ flags4 = (r_ptr->flags4 & r_ptr->r_flags4);
+ flags5 = (r_ptr->flags5 & r_ptr->r_flags5);
+ flags6 = (r_ptr->flags6 & r_ptr->r_flags6);
+ flags7 = (r_ptr->flags7 & r_ptr->r_flags7);
+ flags8 = (r_ptr->flags8 & r_ptr->r_flags8);
+ flags9 = (r_ptr->flags9 & r_ptr->r_flags9);
+
+
+ /* Assume some "obvious" flags */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) flags1 |= (RF1_UNIQUE);
+ if (r_ptr->flags1 & (RF1_MALE)) flags1 |= (RF1_MALE);
+ if (r_ptr->flags1 & (RF1_FEMALE)) flags1 |= (RF1_FEMALE);
+
+ /* Assume some "creation" flags */
+ if (r_ptr->flags1 & (RF1_FRIEND)) flags1 |= (RF1_FRIEND);
+ if (r_ptr->flags1 & (RF1_FRIENDS)) flags1 |= (RF1_FRIENDS);
+ if (r_ptr->flags1 & (RF1_ESCORT)) flags1 |= (RF1_ESCORT);
+ if (r_ptr->flags1 & (RF1_ESCORTS)) flags1 |= (RF1_ESCORTS);
+
+ /* Killing a monster reveals some properties */
+ if (r_ptr->r_tkills)
+ {
+ /* Know "race" flags */
+ if (r_ptr->flags3 & (RF3_ORC)) flags3 |= (RF3_ORC);
+ if (r_ptr->flags3 & (RF3_TROLL)) flags3 |= (RF3_TROLL);
+ if (r_ptr->flags3 & (RF3_GIANT)) flags3 |= (RF3_GIANT);
+ if (r_ptr->flags3 & (RF3_DRAGON)) flags3 |= (RF3_DRAGON);
+ if (r_ptr->flags3 & (RF3_DEMON)) flags3 |= (RF3_DEMON);
+ if (r_ptr->flags3 & (RF3_UNDEAD)) flags3 |= (RF3_UNDEAD);
+ if (r_ptr->flags3 & (RF3_EVIL)) flags3 |= (RF3_EVIL);
+ if (r_ptr->flags3 & (RF3_GOOD)) flags3 |= (RF3_GOOD);
+ if (r_ptr->flags3 & (RF3_ANIMAL)) flags3 |= (RF3_ANIMAL);
+ if (r_ptr->flags3 & (RF3_THUNDERLORD)) flags3 |= (RF3_THUNDERLORD);
+ if (r_ptr->flags7 & (RF7_SPIDER)) flags7 |= (RF7_SPIDER);
+
+ /* Know "forced" flags */
+ if (r_ptr->flags1 & (RF1_FORCE_DEPTH)) flags1 |= (RF1_FORCE_DEPTH);
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP)) flags1 |= (RF1_FORCE_MAXHP);
+ }
+
+
+ /* Require a flag to show kills */
+ if (!(show_details))
+ {
+ /* nothing */
+ }
+
+ /* Treat uniques differently */
+ else if (flags1 & (RF1_UNIQUE))
+ {
+ /* Hack -- Determine if the unique is "dead" */
+ bool dead = (r_ptr->max_num == 0) ? TRUE : FALSE;
+
+ /* We've been killed... */
+ if (r_ptr->r_deaths)
+ {
+ /* Killed ancestors */
+ text_out(format("%^s has slain %d of your ancestors",
+ wd_he[msex], r_ptr->r_deaths));
+
+ /* But we've also killed it */
+ if (dead)
+ {
+ text_out(format(", but you have avenged them! ") );
+ }
+
+ /* Unavenged (ever) */
+ else
+ {
+ text_out(format(", who %s unavenged. ",
+ plural(r_ptr->r_deaths, "remains", "remain")));
+ }
+ }
+
+ /* Dead unique who never hurt us */
+ else if (dead)
+ {
+ text_out("You have slain this foe. ");
+ }
+ }
+
+ /* Not unique, but killed us */
+ else if (r_ptr->r_deaths)
+ {
+ /* Dead ancestors */
+ text_out(format("%d of your ancestors %s been killed by this creature, ",
+ r_ptr->r_deaths, plural(r_ptr->r_deaths, "has", "have")));
+
+ /* Some kills this life */
+ if (r_ptr->r_pkills)
+ {
+ text_out("and you have exterminated at least ");
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->r_pkills));
+ text_out(" of the creatures. ");
+ }
+
+ /* Some kills past lives */
+ else if (r_ptr->r_tkills)
+ {
+ text_out(format("and %s have exterminated at least %d of the creatures. ",
+ "your ancestors", r_ptr->r_tkills));
+ }
+
+ /* No kills */
+ else
+ {
+ text_out(format("and %s is not ever known to have been defeated. ",
+ wd_he[msex]));
+ }
+ }
+
+ /* Normal monsters */
+ else
+ {
+ /* Killed some this life */
+ if (r_ptr->r_pkills)
+ {
+ text_out("You have killed at least ");
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->r_pkills));
+ text_out(" of these creatures. ");
+ }
+
+ /* Killed some last life */
+ else if (r_ptr->r_tkills)
+ {
+ text_out(format("Your ancestors have killed at least %d of these creatures. ",
+ r_ptr->r_tkills));
+ }
+
+ /* Killed none */
+ else
+ {
+ text_out("No battles to the death are recalled. ");
+ }
+ }
+
+
+ /* Descriptions */
+ if (show_details)
+ {
+ char buf[2048];
+
+#ifdef DELAY_LOAD_R_TEXT
+
+ int fd;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DATA, "r_info.raw");
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Open the "raw" file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Drop permission */
+ safe_setuid_drop();
+
+ /* Use file */
+ if (fd >= 0)
+ {
+ huge pos;
+
+ /* Starting position */
+ pos = r_ptr->text;
+
+ /* Additional offsets */
+ pos += r_head->head_size;
+ pos += r_head->info_size;
+ pos += r_head->name_size;
+
+#if 0
+
+ /* Maximal length */
+ len = r_head->text_size - r_ptr->text;
+
+ /* Actual length */
+ for (i = r_idx + 1; i < max_r_idx; i++)
+ {
+ /* Actual length */
+ if (r_info[i].text > r_ptr->text)
+ {
+ /* Extract length */
+ len = r_info[i].text - r_ptr->text;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Maximal length */
+ if (len > 2048) len = 2048;
+
+#endif
+
+ /* Seek */
+ (void)fd_seek(fd, pos);
+
+ /* Read a chunk of data */
+ (void)fd_read(fd, buf, 2048);
+
+ /* Close it */
+ (void)fd_close(fd);
+ }
+
+#else
+
+ /* Simple method */
+ strcpy(buf, r_text + r_ptr->text);
+
+#endif
+
+ /* Dump it */
+ text_out(buf);
+ text_out(" ");
+ }
+
+
+ /* Nothing yet */
+ old = FALSE;
+
+ /* Describe location */
+ if (r_ptr->flags7 & RF7_PET)
+ {
+ text_out(format("%^s is ", wd_he[msex]));
+ text_out_c(TERM_L_BLUE, "friendly");
+ text_out(" to you");
+ old = TRUE;
+ }
+
+ /* Describe location */
+ if (r_ptr->level == 0)
+ {
+ if (old)
+ text_out(", ");
+ else
+ text_out(format("%^s ", wd_he[msex]));
+ text_out_c(TERM_L_GREEN, "lives in the town or the wilderness");
+ old = TRUE;
+ }
+ else if (r_ptr->r_tkills)
+ {
+ if (old)
+ text_out(", ");
+ else
+ text_out(format("%^s ", wd_he[msex]));
+ if (depth_in_feet)
+ {
+ text_out(format("is normally found at depths of ", wd_he[msex]));
+ if (dun_level < r_ptr->level) /* out of depth monster */
+ {
+ text_out_c(TERM_L_RED, format("%d", r_ptr->level * 50));
+ }
+ else
+ {
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->level * 50));
+ }
+ text_out(" feet");
+ }
+ else
+ {
+ text_out(format("is normally found on level ", wd_he[msex]));
+ if (dun_level < r_ptr->level) /* out of depth monster */
+ {
+ text_out_c(TERM_L_RED, format("%d", r_ptr->level));
+ }
+ else
+ {
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->level));
+ }
+ }
+ old = TRUE;
+ }
+
+
+ /* Describe movement */
+ if (TRUE)
+ {
+ /* Introduction */
+ if (old)
+ {
+ text_out(", and ");
+ }
+ else
+ {
+ text_out(format("%^s ", wd_he[msex]));
+ old = TRUE;
+ }
+ text_out("moves");
+
+ /* Random-ness */
+ if ((flags1 & (RF1_RAND_50)) || (flags1 & (RF1_RAND_25)))
+ {
+ /* Adverb */
+ if ((flags1 & (RF1_RAND_50)) && (flags1 & (RF1_RAND_25)))
+ {
+ text_out(" extremely");
+ }
+ else if (flags1 & (RF1_RAND_50))
+ {
+ text_out(" somewhat");
+ }
+ else if (flags1 & (RF1_RAND_25))
+ {
+ text_out(" a bit");
+ }
+
+ /* Adjective */
+ text_out(" erratically");
+
+ /* Hack -- Occasional conjunction */
+ if (r_ptr->speed != 110) text_out(", and");
+ }
+
+ /* Speed */
+ if (r_ptr->speed > 110)
+ {
+ if (r_ptr->speed > 130) text_out_c(TERM_RED, " incredibly");
+ else if (r_ptr->speed > 120) text_out_c(TERM_ORANGE, " very");
+ text_out_c(TERM_L_RED, " quickly");
+ }
+ else if (r_ptr->speed < 110)
+ {
+ if (r_ptr->speed < 90) text_out_c(TERM_L_GREEN, " incredibly");
+ else if (r_ptr->speed < 100) text_out_c(TERM_BLUE, " very");
+ text_out_c(TERM_L_BLUE, " slowly");
+ }
+ else
+ {
+ text_out(" at normal speed");
+ }
+ }
+
+ /* The code above includes "attack speed" */
+ if (flags1 & (RF1_NEVER_MOVE))
+ {
+ /* Introduce */
+ if (old)
+ {
+ text_out(", but ");
+ }
+ else
+ {
+ text_out(format("%^s ", wd_he[msex]));
+ old = TRUE;
+ }
+
+ /* Describe */
+ text_out("does not deign to chase intruders");
+ }
+
+ /* End this sentence */
+ if (old)
+ {
+ text_out(". ");
+ old = FALSE;
+ }
+
+
+ /* Describe experience if known */
+ if (r_ptr->r_tkills)
+ {
+ /* Introduction */
+ if (flags1 & (RF1_UNIQUE))
+ {
+ text_out("Killing this");
+ }
+ else
+ {
+ text_out("A kill of this");
+ }
+
+ /* Describe the "quality" */
+ if (flags2 & (RF2_ELDRITCH_HORROR)) text_out_c(TERM_VIOLET, " sanity-blasting");
+ if (flags3 & (RF3_ANIMAL)) text_out_c(TERM_VIOLET, " natural");
+ if (flags3 & (RF3_EVIL)) text_out_c(TERM_VIOLET, " evil");
+ if (flags3 & (RF3_GOOD)) text_out_c(TERM_VIOLET, " good");
+ if (flags3 & (RF3_UNDEAD)) text_out_c(TERM_VIOLET, " undead");
+
+ /* Describe the "race" */
+ if (flags3 & (RF3_DRAGON)) text_out_c(TERM_VIOLET, " dragon");
+ else if (flags3 & (RF3_DEMON)) text_out_c(TERM_VIOLET, " demon");
+ else if (flags3 & (RF3_GIANT)) text_out_c(TERM_VIOLET, " giant");
+ else if (flags3 & (RF3_TROLL)) text_out_c(TERM_VIOLET, " troll");
+ else if (flags3 & (RF3_ORC)) text_out_c(TERM_VIOLET, " orc");
+ else if (flags3 & (RF3_THUNDERLORD))text_out_c(TERM_VIOLET, " Thunderlord");
+ else if (flags7 & (RF7_SPIDER)) text_out_c(TERM_VIOLET, " spider");
+ else if (flags7 & (RF7_NAZGUL)) text_out_c(TERM_VIOLET, " Nazgul");
+ else text_out(" creature");
+
+ /* Group some variables */
+ if (TRUE)
+ {
+ long i, j;
+
+ /* calculate the integer exp part */
+ i = (long)r_ptr->mexp * r_ptr->level / p_ptr->lev;
+
+ /* calculate the fractional exp part scaled by 100, */
+ /* must use long arithmetic to avoid overflow */
+ j = ((((long)r_ptr->mexp * r_ptr->level % p_ptr->lev) *
+ (long)1000 / p_ptr->lev + 5) / 10);
+
+ /* Mention the experience */
+ text_out(" is worth ");
+ text_out_c(TERM_ORANGE, format("%ld.%02ld", (long)i, (long)j));
+ text_out(" point");
+ text_out(((i == 1) && (j == 0)) ? "" : "s");
+
+ /* Take account of annoying English */
+ p = "th";
+ i = p_ptr->lev % 10;
+ if ((p_ptr->lev / 10) == 1) /* nothing */;
+ else if (i == 1) p = "st";
+ else if (i == 2) p = "nd";
+ else if (i == 3) p = "rd";
+
+ /* Take account of "leading vowels" in numbers */
+ q = "";
+ i = p_ptr->lev;
+ if ((i == 8) || (i == 11) || (i == 18)) q = "n";
+
+ /* Mention the dependance on the player's level */
+ text_out(format(" for a%s %lu%s level character. ",
+ q, (long)i, p));
+ }
+ }
+
+ if ((flags2 & (RF2_AURA_FIRE)) && (flags2 & (RF2_AURA_ELEC)))
+ {
+ text_out(format("%^s is surrounded by ", wd_he[msex]));
+ text_out_c(TERM_VIOLET, "flames and electricity");
+ text_out(". ");
+ }
+ else if (flags2 & (RF2_AURA_FIRE))
+ {
+ text_out(format("%^s is surrounded by ", wd_he[msex]));
+ text_out_c(TERM_ORANGE, "flames");
+ text_out(". ");
+ }
+ else if (flags2 & (RF2_AURA_ELEC))
+ {
+ text_out(format("%^s is surrounded by ", wd_he[msex]));
+ text_out_c(TERM_L_BLUE, "electricity");
+ text_out(". ");
+ }
+
+ if (flags2 & (RF2_REFLECTING))
+ {
+ text_out(format("%^s ", wd_he[msex]));
+ text_out_c(TERM_L_UMBER, "reflects");
+ text_out(" bolt spells. ");
+ }
+
+
+ /* Describe escorts */
+ if ((flags1 & (RF1_ESCORT)) || (flags1 & (RF1_ESCORTS)))
+ {
+ text_out(format("%^s usually appears with escorts. ",
+ wd_he[msex]));
+ }
+
+ /* Describe friends */
+ else if ((flags1 & (RF1_FRIEND)) || (flags1 & (RF1_FRIENDS)))
+ {
+ text_out(format("%^s usually appears in groups. ",
+ wd_he[msex]));
+ }
+
+
+ /* Collect inate attacks */
+ vn = 0;
+ if (flags4 & (RF4_SHRIEK)) vp[vn++] = "shriek for help";
+ if (flags4 & (RF4_ROCKET)) vp[vn++] = "shoot a rocket";
+ if (flags4 & (RF4_ARROW_1)) vp[vn++] = "fire an arrow";
+ if (flags4 & (RF4_ARROW_2)) vp[vn++] = "fire arrows";
+ if (flags4 & (RF4_ARROW_3)) vp[vn++] = "fire a missile";
+ if (flags4 & (RF4_ARROW_4)) vp[vn++] = "fire missiles";
+
+ /* Describe inate attacks */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" may ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out_c(TERM_YELLOW, vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect breaths */
+ vn = 0;
+ if (flags4 & (RF4_BR_ACID)) vp[vn++] = "acid";
+ if (flags4 & (RF4_BR_ELEC)) vp[vn++] = "lightning";
+ if (flags4 & (RF4_BR_FIRE)) vp[vn++] = "fire";
+ if (flags4 & (RF4_BR_COLD)) vp[vn++] = "frost";
+ if (flags4 & (RF4_BR_POIS)) vp[vn++] = "poison";
+ if (flags4 & (RF4_BR_NETH)) vp[vn++] = "nether";
+ if (flags4 & (RF4_BR_LITE)) vp[vn++] = "light";
+ if (flags4 & (RF4_BR_DARK)) vp[vn++] = "darkness";
+ if (flags4 & (RF4_BR_CONF)) vp[vn++] = "confusion";
+ if (flags4 & (RF4_BR_SOUN)) vp[vn++] = "sound";
+ if (flags4 & (RF4_BR_CHAO)) vp[vn++] = "chaos";
+ if (flags4 & (RF4_BR_DISE)) vp[vn++] = "disenchantment";
+ if (flags4 & (RF4_BR_NEXU)) vp[vn++] = "nexus";
+ if (flags4 & (RF4_BR_TIME)) vp[vn++] = "time";
+ if (flags4 & (RF4_BR_INER)) vp[vn++] = "inertia";
+ if (flags4 & (RF4_BR_GRAV)) vp[vn++] = "gravity";
+ if (flags4 & (RF4_BR_SHAR)) vp[vn++] = "shards";
+ if (flags4 & (RF4_BR_PLAS)) vp[vn++] = "plasma";
+ if (flags4 & (RF4_BR_WALL)) vp[vn++] = "force";
+ if (flags4 & (RF4_BR_MANA)) vp[vn++] = "mana";
+ if (flags4 & (RF4_BR_NUKE)) vp[vn++] = "toxic waste";
+ if (flags4 & (RF4_BR_DISI)) vp[vn++] = "disintegration";
+
+ /* Describe breaths */
+ if (vn)
+ {
+ /* Note breath */
+ breath = TRUE;
+
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" may breathe ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out_c(TERM_YELLOW, vp[n]);
+ }
+ }
+
+
+ /* Collect spells */
+ vn = 0;
+ if (flags5 & (RF5_BA_ACID)) vp[vn++] = "produce acid balls";
+ if (flags5 & (RF5_BA_ELEC)) vp[vn++] = "produce lightning balls";
+ if (flags5 & (RF5_BA_FIRE)) vp[vn++] = "produce fire balls";
+ if (flags5 & (RF5_BA_COLD)) vp[vn++] = "produce frost balls";
+ if (flags5 & (RF5_BA_POIS)) vp[vn++] = "produce poison balls";
+ if (flags5 & (RF5_BA_NETH)) vp[vn++] = "produce nether balls";
+ if (flags5 & (RF5_BA_WATE)) vp[vn++] = "produce water balls";
+ if (flags4 & (RF4_BA_NUKE)) vp[vn++] = "produce balls of radiation";
+ if (flags5 & (RF5_BA_MANA)) vp[vn++] = "invoke mana storms";
+ if (flags5 & (RF5_BA_DARK)) vp[vn++] = "invoke darkness storms";
+ if (flags4 & (RF4_BA_CHAO)) vp[vn++] = "invoke raw chaos";
+ if (flags6 & (RF6_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom";
+ if (flags5 & (RF5_DRAIN_MANA)) vp[vn++] = "drain mana";
+ if (flags5 & (RF5_MIND_BLAST)) vp[vn++] = "cause mind blasting";
+ if (flags5 & (RF5_BRAIN_SMASH)) vp[vn++] = "cause brain smashing";
+ if (flags5 & (RF5_CAUSE_1)) vp[vn++] = "cause light wounds and cursing";
+ if (flags5 & (RF5_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing";
+ if (flags5 & (RF5_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing";
+ if (flags5 & (RF5_CAUSE_4)) vp[vn++] = "cause mortal wounds";
+ if (flags5 & (RF5_BO_ACID)) vp[vn++] = "produce acid bolts";
+ if (flags5 & (RF5_BO_ELEC)) vp[vn++] = "produce lightning bolts";
+ if (flags5 & (RF5_BO_FIRE)) vp[vn++] = "produce fire bolts";
+ if (flags5 & (RF5_BO_COLD)) vp[vn++] = "produce frost bolts";
+ if (flags5 & (RF5_BO_POIS)) vp[vn++] = "produce poison bolts";
+ if (flags5 & (RF5_BO_NETH)) vp[vn++] = "produce nether bolts";
+ if (flags5 & (RF5_BO_WATE)) vp[vn++] = "produce water bolts";
+ if (flags5 & (RF5_BO_MANA)) vp[vn++] = "produce mana bolts";
+ if (flags5 & (RF5_BO_PLAS)) vp[vn++] = "produce plasma bolts";
+ if (flags5 & (RF5_BO_ICEE)) vp[vn++] = "produce ice bolts";
+ if (flags5 & (RF5_MISSILE)) vp[vn++] = "produce magic missiles";
+ if (flags5 & (RF5_SCARE)) vp[vn++] = "terrify";
+ if (flags5 & (RF5_BLIND)) vp[vn++] = "blind";
+ if (flags5 & (RF5_CONF)) vp[vn++] = "confuse";
+ if (flags5 & (RF5_SLOW)) vp[vn++] = "slow";
+ if (flags5 & (RF5_HOLD)) vp[vn++] = "paralyze";
+ if (flags6 & (RF6_HASTE)) vp[vn++] = "haste-self";
+ if (flags6 & (RF6_HEAL)) vp[vn++] = "heal-self";
+ if (flags6 & (RF6_BLINK)) vp[vn++] = "blink-self";
+ if (flags6 & (RF6_TPORT)) vp[vn++] = "teleport-self";
+ if (flags6 & (RF6_S_BUG)) vp[vn++] = "summon software bugs";
+ if (flags6 & (RF6_S_RNG)) vp[vn++] = "summon RNG";
+ if (flags6 & (RF6_TELE_TO)) vp[vn++] = "teleport to";
+ if (flags6 & (RF6_TELE_AWAY)) vp[vn++] = "teleport away";
+ if (flags6 & (RF6_TELE_LEVEL)) vp[vn++] = "teleport level";
+ if (flags6 & (RF6_S_THUNDERLORD)) vp[vn++] = "summon a Thunderlord";
+ if (flags6 & (RF6_DARKNESS)) vp[vn++] = "create darkness";
+ if (flags6 & (RF6_TRAPS)) vp[vn++] = "create traps";
+ if (flags6 & (RF6_FORGET)) vp[vn++] = "cause amnesia";
+ if (flags6 & (RF6_RAISE_DEAD)) vp[vn++] = "raise dead";
+ if (flags6 & (RF6_S_MONSTER)) vp[vn++] = "summon a monster";
+ if (flags6 & (RF6_S_MONSTERS)) vp[vn++] = "summon monsters";
+ if (flags6 & (RF6_S_KIN)) vp[vn++] = "summon aid";
+ if (flags6 & (RF6_S_ANT)) vp[vn++] = "summon ants";
+ if (flags6 & (RF6_S_SPIDER)) vp[vn++] = "summon spiders";
+ if (flags6 & (RF6_S_HOUND)) vp[vn++] = "summon hounds";
+ if (flags6 & (RF6_S_HYDRA)) vp[vn++] = "summon hydras";
+ if (flags6 & (RF6_S_ANGEL)) vp[vn++] = "summon an angel";
+ if (flags6 & (RF6_S_DEMON)) vp[vn++] = "summon a demon";
+ if (flags6 & (RF6_S_UNDEAD)) vp[vn++] = "summon an undead";
+ if (flags6 & (RF6_S_DRAGON)) vp[vn++] = "summon a dragon";
+ if (flags4 & (RF4_S_ANIMAL)) vp[vn++] = "summon animal";
+ if (flags6 & (RF6_S_ANIMALS)) vp[vn++] = "summon animals";
+ if (flags6 & (RF6_S_HI_UNDEAD)) vp[vn++] = "summon Greater Undead";
+ if (flags6 & (RF6_S_HI_DRAGON)) vp[vn++] = "summon Ancient Dragons";
+ if (flags6 & (RF6_S_HI_DEMON)) vp[vn++] = "summon Greater Demons";
+ if (flags6 & (RF6_S_WRAITH)) vp[vn++] = "summon Ringwraith";
+ if (flags6 & (RF6_S_UNIQUE)) vp[vn++] = "summon Unique Monsters";
+
+ /* Describe spells */
+ if (vn)
+ {
+ /* Note magic */
+ magic = TRUE;
+
+ /* Intro */
+ if (breath)
+ {
+ text_out(", and is also");
+ }
+ else
+ {
+ text_out(format("%^s is", wd_he[msex]));
+ }
+
+ /* Verb Phrase */
+ text_out(" magical, casting spells");
+
+ /* Adverb */
+ if (flags2 & (RF2_SMART)) text_out_c(TERM_YELLOW, " intelligently");
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" which ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out_c(TERM_YELLOW, vp[n]);
+ }
+ }
+
+
+ /* End the sentence about inate/other spells */
+ if (breath || magic)
+ {
+ /* Total casting */
+ m = r_ptr->r_cast_inate + r_ptr->r_cast_spell;
+
+ /* Average frequency */
+ n = (r_ptr->freq_inate + r_ptr->freq_spell) / 2;
+
+ /* Describe the spell frequency */
+ if (m > 100)
+ {
+ text_out("; ");
+ text_out_c(TERM_L_GREEN, "1");
+ text_out(" time in ");
+ text_out_c(TERM_L_GREEN, format("%d", 100 / n));
+ }
+
+ /* Guess at the frequency */
+ else if (m)
+ {
+ n = ((n + 9) / 10) * 10;
+ text_out("; about ");
+ text_out_c(TERM_L_GREEN, "1");
+ text_out(" time in ");
+ text_out_c(TERM_L_GREEN, format("%d", 100 / n));
+ }
+
+ /* End this sentence */
+ text_out(". ");
+ }
+
+
+ /* Describe monster "toughness" */
+ if (know_armour(r_idx))
+ {
+ /* Armor */
+ text_out(format("%^s has an armor rating of ", wd_he[msex]));
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->ac));
+
+ /* Maximized hitpoints */
+ if (flags1 & (RF1_FORCE_MAXHP))
+ {
+ text_out(" and a life rating of ");
+ text_out_c(TERM_L_GREEN, format("%d", r_ptr->hdice * r_ptr->hside));
+ text_out(". ");
+ }
+
+ /* Variable hitpoints */
+ else
+ {
+ text_out(" and a life rating of ");
+ text_out_c(TERM_L_GREEN, format("%dd%d", r_ptr->hdice, r_ptr->hside));
+ text_out(". ");
+ }
+ }
+
+
+
+ /* Collect special abilities. */
+ vn = 0;
+ if (flags2 & (RF2_OPEN_DOOR)) vp[vn++] = "open doors";
+ if (flags2 & (RF2_BASH_DOOR)) vp[vn++] = "bash down doors";
+ if (flags2 & (RF2_PASS_WALL)) vp[vn++] = "pass through walls";
+ if (flags2 & (RF2_KILL_WALL)) vp[vn++] = "bore through walls";
+ if (flags2 & (RF2_MOVE_BODY)) vp[vn++] = "push past weaker monsters";
+ if (flags2 & (RF2_KILL_BODY)) vp[vn++] = "destroy weaker monsters";
+ if (flags2 & (RF2_TAKE_ITEM)) vp[vn++] = "pick up objects";
+ if (flags2 & (RF2_KILL_ITEM)) vp[vn++] = "destroy objects";
+ if (flags9 & (RF9_HAS_LITE)) vp[vn++] = "illuminate the dungeon";
+
+ /* Describe special abilities. */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" can ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out(vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Describe special abilities. */
+ if (flags2 & (RF2_INVISIBLE))
+ {
+ text_out_c(TERM_GREEN, format("%^s is invisible. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_COLD_BLOOD))
+ {
+ text_out(format("%^s is cold blooded. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_EMPTY_MIND))
+ {
+ text_out(format("%^s is not detected by telepathy. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_WEIRD_MIND))
+ {
+ text_out(format("%^s is rarely detected by telepathy. ", wd_he[msex]));
+ }
+ if (flags4 & (RF4_MULTIPLY))
+ {
+ text_out_c(TERM_L_UMBER, format("%^s breeds explosively. ", wd_he[msex]));
+ }
+ if (flags2 & (RF2_REGENERATE))
+ {
+ text_out_c(TERM_L_WHITE, format("%^s regenerates quickly. ", wd_he[msex]));
+ }
+ if (r_ptr->flags7 & (RF7_MORTAL))
+ {
+ text_out_c(TERM_RED, format("%^s is a mortal being. ", wd_he[msex]));
+ }
+ else
+ {
+ text_out_c(TERM_L_BLUE, format("%^s is an immortal being. ", wd_he[msex]));
+ }
+
+
+ /* Collect susceptibilities */
+ vn = 0;
+ if (flags3 & (RF3_HURT_ROCK))
+ {
+ vp[vn++] = "rock remover";
+ color[vn - 1] = TERM_UMBER;
+ }
+ if (flags3 & (RF3_HURT_LITE))
+ {
+ vp[vn++] = "bright light";
+ color[vn - 1] = TERM_YELLOW;
+ }
+ if (flags3 & (RF3_SUSCEP_FIRE))
+ {
+ vp[vn++] = "fire";
+ color[vn - 1] = TERM_RED;
+ }
+ if (flags3 & (RF3_SUSCEP_COLD))
+ {
+ vp[vn++] = "cold";
+ color[vn - 1] = TERM_L_WHITE;
+ }
+ if (flags9 & (RF9_SUSCEP_ACID))
+ {
+ vp[vn++] = "acid";
+ color[vn - 1] = TERM_GREEN;
+ }
+ if (flags9 & (RF9_SUSCEP_ELEC))
+ {
+ vp[vn++] = "lightning";
+ color[vn - 1] = TERM_L_BLUE;
+ }
+ if (flags9 & (RF9_SUSCEP_POIS))
+ {
+ vp[vn++] = "poison";
+ color[vn - 1] = TERM_L_GREEN;
+ }
+
+ /* Describe susceptibilities */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" is hurt by ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out_c(color[n], vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect immunities */
+ vn = 0;
+ if (flags3 & (RF3_IM_ACID))
+ {
+ vp[vn++] = "acid";
+ color[vn - 1] = TERM_L_GREEN;
+ }
+ if (flags3 & (RF3_IM_ELEC))
+ {
+ vp[vn++] = "lightning";
+ color[vn - 1] = TERM_L_BLUE;
+ }
+ if (flags3 & (RF3_IM_FIRE))
+ {
+ vp[vn++] = "fire";
+ color[vn - 1] = TERM_L_RED;
+ }
+ if (flags3 & (RF3_IM_COLD))
+ {
+ vp[vn++] = "cold";
+ color[vn - 1] = TERM_L_BLUE;
+ }
+ if (flags3 & (RF3_IM_POIS))
+ {
+ vp[vn++] = "poison";
+ color[vn - 1] = TERM_L_GREEN;
+ }
+
+ /* Describe immunities */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" resists ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out_c(color[n], vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect resistances */
+ vn = 0;
+ if (flags3 & (RF3_RES_NETH)) vp[vn++] = "nether";
+ if (flags3 & (RF3_RES_WATE)) vp[vn++] = "water";
+ if (flags3 & (RF3_RES_PLAS)) vp[vn++] = "plasma";
+ if (flags3 & (RF3_RES_NEXU)) vp[vn++] = "nexus";
+ if (flags3 & (RF3_RES_DISE)) vp[vn++] = "disenchantment";
+ if (flags3 & (RF3_RES_TELE)) vp[vn++] = "teleportation";
+
+ /* Describe resistances */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" resists ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump */
+ text_out_c(TERM_L_BLUE, vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Collect non-effects */
+ vn = 0;
+ if (flags3 & (RF3_NO_STUN)) vp[vn++] = "stunned";
+ if (flags3 & (RF3_NO_FEAR)) vp[vn++] = "frightened";
+ if (flags3 & (RF3_NO_CONF)) vp[vn++] = "confused";
+ if (flags3 & (RF3_NO_SLEEP)) vp[vn++] = "slept";
+
+ /* Describe non-effects */
+ if (vn)
+ {
+ /* Intro */
+ text_out(format("%^s", wd_he[msex]));
+
+ /* Scan */
+ for (n = 0; n < vn; n++)
+ {
+ /* Intro */
+ if (n == 0) text_out(" cannot be ");
+ else if (n < vn - 1) text_out(", ");
+ else text_out(" or ");
+
+ /* Dump */
+ text_out(vp[n]);
+ }
+
+ /* End */
+ text_out(". ");
+ }
+
+
+ /* Do we know how aware it is? */
+ if ((((int)r_ptr->r_wake * (int)r_ptr->r_wake) > r_ptr->sleep) ||
+ (r_ptr->r_ignore == MAX_UCHAR) ||
+ ((r_ptr->sleep == 0) && (r_ptr->r_tkills >= 10)))
+ {
+ cptr act;
+
+ if (r_ptr->sleep > 200)
+ {
+ act = "prefers to ignore";
+ }
+ else if (r_ptr->sleep > 95)
+ {
+ act = "pays very little attention to";
+ }
+ else if (r_ptr->sleep > 75)
+ {
+ act = "pays little attention to";
+ }
+ else if (r_ptr->sleep > 45)
+ {
+ act = "tends to overlook";
+ }
+ else if (r_ptr->sleep > 25)
+ {
+ act = "takes quite a while to see";
+ }
+ else if (r_ptr->sleep > 10)
+ {
+ act = "takes a while to see";
+ }
+ else if (r_ptr->sleep > 5)
+ {
+ act = "is fairly observant of";
+ }
+ else if (r_ptr->sleep > 3)
+ {
+ act = "is observant of";
+ }
+ else if (r_ptr->sleep > 1)
+ {
+ act = "is very observant of";
+ }
+ else if (r_ptr->sleep > 0)
+ {
+ act = "is vigilant for";
+ }
+ else
+ {
+ act = "is ever vigilant for";
+ }
+
+ text_out(format("%^s %s intruders, which %s may notice from %d feet. ",
+ wd_he[msex], act, wd_he[msex], 10 * r_ptr->aaf));
+ }
+
+
+ /* Drops gold and/or items */
+ if (r_ptr->r_drop_gold || r_ptr->r_drop_item)
+ {
+ /* No "n" needed */
+ sin = FALSE;
+
+ /* Intro */
+ text_out(format("%^s may carry", wd_he[msex]));
+
+ /* Count maximum drop */
+ n = MAX(r_ptr->r_drop_gold, r_ptr->r_drop_item);
+
+ /* One drop (may need an "n") */
+ if (n == 1)
+ {
+ text_out(" a");
+ sin = TRUE;
+ }
+
+ /* Two drops */
+ else if (n == 2)
+ {
+ text_out(" one or two");
+ }
+
+ /* Many drops */
+ else
+ {
+ text_out(format(" up to %d", n));
+ }
+
+
+ /* Great */
+ if (flags1 & (RF1_DROP_GREAT))
+ {
+ p = " exceptional";
+ }
+
+ /* Good (no "n" needed) */
+ else if (flags1 & (RF1_DROP_GOOD))
+ {
+ p = " good";
+ sin = FALSE;
+ }
+
+ /* Okay */
+ else
+ {
+ p = NULL;
+ }
+
+
+ /* Objects */
+ if (r_ptr->r_drop_item)
+ {
+ /* Handle singular "an" */
+ if (sin) text_out("n");
+ sin = FALSE;
+
+ /* Dump "object(s)" */
+ if (p) text_out_c(TERM_ORANGE, p);
+ text_out(" object");
+ if (n != 1) text_out("s");
+
+ /* Conjunction replaces variety, if needed for "gold" below */
+ p = " or";
+ }
+
+ /* Treasures */
+ if (r_ptr->r_drop_gold)
+ {
+ /* Cancel prefix */
+ if (!p) sin = FALSE;
+
+ /* Handle singular "an" */
+ if (sin) text_out("n");
+ sin = FALSE;
+
+ /* Dump "treasure(s)" */
+ if (p) text_out(p);
+ text_out(" treasure");
+ if (n != 1) text_out("s");
+ }
+
+ /* End this sentence */
+ text_out(". ");
+ }
+
+
+ /* Count the number of "known" attacks */
+ for (n = 0, m = 0; m < 4; m++)
+ {
+ /* Skip non-attacks */
+ if (!r_ptr->blow[m].method) continue;
+
+ /* Count known attacks */
+ if (r_ptr->r_blows[m]) n++;
+ }
+
+ /* Examine (and count) the actual attacks */
+ for (r = 0, m = 0; m < 4; m++)
+ {
+ int method, effect, d1, d2;
+
+ /* Skip non-attacks */
+ if (!r_ptr->blow[m].method) continue;
+
+ /* Skip unknown attacks */
+ if (!r_ptr->r_blows[m]) continue;
+
+
+ /* Extract the attack info */
+ method = r_ptr->blow[m].method;
+ effect = r_ptr->blow[m].effect;
+ d1 = r_ptr->blow[m].d_dice;
+ d2 = r_ptr->blow[m].d_side;
+
+
+ /* No method yet */
+ p = NULL;
+
+ /* Acquire the method */
+ switch (method)
+ {
+ case RBM_HIT:
+ p = "hit";
+ break;
+ case RBM_TOUCH:
+ p = "touch";
+ break;
+ case RBM_PUNCH:
+ p = "punch";
+ break;
+ case RBM_KICK:
+ p = "kick";
+ break;
+ case RBM_CLAW:
+ p = "claw";
+ break;
+ case RBM_BITE:
+ p = "bite";
+ break;
+ case RBM_STING:
+ p = "sting";
+ break;
+ case RBM_XXX1:
+ break;
+ case RBM_BUTT:
+ p = "butt";
+ break;
+ case RBM_CRUSH:
+ p = "crush";
+ break;
+ case RBM_ENGULF:
+ p = "engulf";
+ break;
+ case RBM_CHARGE:
+ p = "charge";
+ break;
+ case RBM_CRAWL:
+ p = "crawl on you";
+ break;
+ case RBM_DROOL:
+ p = "drool on you";
+ break;
+ case RBM_SPIT:
+ p = "spit";
+ break;
+ case RBM_EXPLODE:
+ p = "explode";
+ break;
+ case RBM_GAZE:
+ p = "gaze";
+ break;
+ case RBM_WAIL:
+ p = "wail";
+ break;
+ case RBM_SPORE:
+ p = "release spores";
+ break;
+ case RBM_XXX4:
+ break;
+ case RBM_BEG:
+ p = "beg";
+ break;
+ case RBM_INSULT:
+ p = "insult";
+ break;
+ case RBM_MOAN:
+ p = "moan";
+ break;
+ case RBM_SHOW:
+ p = "sing";
+ break;
+ }
+
+
+ /* Default effect */
+ q = NULL;
+
+ /* Acquire the effect */
+ switch (effect)
+ {
+ case RBE_HURT:
+ q = "attack";
+ break;
+ case RBE_POISON:
+ q = "poison";
+ break;
+ case RBE_UN_BONUS:
+ q = "disenchant";
+ break;
+ case RBE_UN_POWER:
+ q = "drain charges";
+ break;
+ case RBE_EAT_GOLD:
+ q = "steal gold";
+ break;
+ case RBE_EAT_ITEM:
+ q = "steal items";
+ break;
+ case RBE_EAT_FOOD:
+ q = "eat your food";
+ break;
+ case RBE_EAT_LITE:
+ q = "absorb light";
+ break;
+ case RBE_ACID:
+ q = "shoot acid";
+ break;
+ case RBE_ELEC:
+ q = "electrocute";
+ break;
+ case RBE_FIRE:
+ q = "burn";
+ break;
+ case RBE_COLD:
+ q = "freeze";
+ break;
+ case RBE_BLIND:
+ q = "blind";
+ break;
+ case RBE_CONFUSE:
+ q = "confuse";
+ break;
+ case RBE_TERRIFY:
+ q = "terrify";
+ break;
+ case RBE_PARALYZE:
+ q = "paralyze";
+ break;
+ case RBE_LOSE_STR:
+ q = "reduce strength";
+ break;
+ case RBE_LOSE_INT:
+ q = "reduce intelligence";
+ break;
+ case RBE_LOSE_WIS:
+ q = "reduce wisdom";
+ break;
+ case RBE_LOSE_DEX:
+ q = "reduce dexterity";
+ break;
+ case RBE_LOSE_CON:
+ q = "reduce constitution";
+ break;
+ case RBE_LOSE_CHR:
+ q = "reduce charisma";
+ break;
+ case RBE_LOSE_ALL:
+ q = "reduce all stats";
+ break;
+ case RBE_SHATTER:
+ q = "shatter";
+ break;
+ case RBE_EXP_10:
+ q = "lower experience (by 10d6+)";
+ break;
+ case RBE_EXP_20:
+ q = "lower experience (by 20d6+)";
+ break;
+ case RBE_EXP_40:
+ q = "lower experience (by 40d6+)";
+ break;
+ case RBE_EXP_80:
+ q = "lower experience (by 80d6+)";
+ break;
+ case RBE_DISEASE:
+ q = "disease";
+ break;
+ case RBE_TIME:
+ q = "time";
+ break;
+ case RBE_SANITY:
+ q = "blast sanity";
+ break;
+ case RBE_HALLU:
+ q = "cause hallucinations";
+ break;
+ case RBE_PARASITE:
+ q = "parasite";
+ break;
+ }
+
+
+ /* Introduce the attack description */
+ if (!r)
+ {
+ text_out(format("%^s can ", wd_he[msex]));
+ }
+ else if (r < n - 1)
+ {
+ text_out(", ");
+ }
+ else
+ {
+ text_out(", and ");
+ }
+
+
+ /* Hack -- force a method */
+ if (!p) p = "do something weird";
+
+ /* Describe the method */
+ text_out(p);
+
+
+ /* Describe the effect (if any) */
+ if (q)
+ {
+ /* Describe the attack type */
+ text_out(" to ");
+ text_out_c(TERM_YELLOW, q);
+
+ /* Describe damage (if known) */
+ if (d1 && d2 && know_damage(r_idx, m))
+ {
+ /* Display the damage */
+ text_out(" with damage");
+ text_out_c(TERM_L_GREEN, format(" %dd%d", d1, d2));
+ }
+ }
+
+
+ /* Count the attacks as printed */
+ r++;
+ }
+
+ /* Finish sentence above */
+ if (r)
+ {
+ text_out(". ");
+ }
+
+ /* Notice lack of attacks */
+ else if (flags1 & (RF1_NEVER_BLOW))
+ {
+ text_out(format("%^s has no physical attacks. ", wd_he[msex]));
+ }
+
+ /* Or describe the lack of knowledge */
+ else
+ {
+ text_out(format("Nothing is known about %s attack. ", wd_his[msex]));
+ }
+
+
+ /* All done */
+ text_out("\n");
+
+ /* Cheat -- know everything */
+ if ((cheat_know) && (remem == 0))
+ {
+ /* Hack -- restore memory */
+ COPY(r_ptr, &save_mem, monster_race);
+ }
+}
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race
+ */
+static void roff_name(int r_idx, int ego)
+{
+ monster_race *r_ptr = race_info_idx(r_idx, ego);
+
+ byte a1, a2;
+ char c1, c2;
+
+ /* Access the chars */
+ c1 = r_ptr->d_char;
+ c2 = r_ptr->x_char;
+
+ /* Access the attrs */
+ a1 = r_ptr->d_attr;
+ a2 = r_ptr->x_attr;
+
+ /* Hack -- fake monochrome */
+ if (!use_color) a1 = TERM_WHITE;
+ if (!use_color) a2 = TERM_WHITE;
+
+ /* A title (use "The" for non-uniques) */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ Term_addstr( -1, TERM_WHITE, "The ");
+ }
+
+ /* Dump the name */
+ if (ego)
+ {
+ if (re_info[ego].before) Term_addstr( -1, TERM_WHITE, format("%s %s", re_name + re_info[ego].name, r_name + r_ptr->name));
+ else Term_addstr( -1, TERM_WHITE, format("%s %s", r_name + r_ptr->name, re_name + re_info[ego].name));
+ }
+ else
+ {
+ Term_addstr( -1, TERM_WHITE, r_name + r_ptr->name);
+ }
+
+ /* Append the "standard" attr/char info */
+ Term_addstr( -1, TERM_WHITE, " ('");
+ Term_addch(a1, c1);
+ if (use_bigtile && (a1 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "')");
+
+ /* Append the "optional" attr/char info */
+ Term_addstr( -1, TERM_WHITE, "/('");
+ Term_addch(a2, c2);
+ if (use_bigtile && (a2 & 0x80)) Term_addch(255, 255);
+ Term_addstr( -1, TERM_WHITE, "'):");
+}
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race on top
+ */
+static void roff_top(int r_idx, int ego)
+{
+ /* Clear the top line */
+ Term_erase(0, 0, 255);
+
+ /* Reset the cursor */
+ Term_gotoxy(0, 0);
+
+ roff_name(r_idx, ego);
+}
+
+/*
+ * Hack -- describe the given monster race at the top of the screen
+ */
+void screen_roff(int r_idx, int ego, int remember)
+{
+ /* Flush messages */
+ msg_print(NULL);
+
+ /* Begin recall */
+ Term_erase(0, 1, 255);
+
+ /* Recall monster */
+ roff_aux(r_idx, ego, remember);
+
+ /* Describe monster */
+ roff_top(r_idx, ego);
+}
+
+/*
+ * Ddescribe the given monster race at the current pos of the "term" window
+ */
+void monster_description_out(int r_idx, int ego)
+{
+ roff_name(r_idx, ego);
+ roff_aux(r_idx, ego, 0);
+}
+
+/*
+ * Hack -- describe the given monster race in the current "term" window
+ */
+void display_roff(int r_idx, int ego)
+{
+ int y;
+
+ /* Erase the window */
+ for (y = 0; y < Term->hgt; y++)
+ {
+ /* Erase the line */
+ Term_erase(0, y, 255);
+ }
+
+ /* Begin recall */
+ Term_gotoxy(0, 1);
+
+ /* Recall monster */
+ roff_aux(r_idx, ego, 0);
+
+ /* Describe monster */
+ roff_top(r_idx, ego);
+}
+
+
+bool monster_quest(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Random quests are in the dungeon */
+ if (!(r_ptr->flags8 & RF8_DUNGEON)) return FALSE;
+
+ /* No random quests for aquatic monsters */
+ if (r_ptr->flags7 & RF7_AQUATIC) return FALSE;
+
+ /* No random quests for multiplying monsters */
+ if (r_ptr->flags4 & RF4_MULTIPLY) return FALSE;
+
+ return TRUE;
+}
+
+
+bool monster_dungeon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_DUNGEON)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_ocean(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_OCEAN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_shore(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_SHORE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_waste(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_WASTE)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_town(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_TOWN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_wood(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_WOOD)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_volcano(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_VOLCANO)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_mountain(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_MOUNTAIN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_grass(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags8 & RF8_WILD_GRASS)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_deep_water(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (!monster_dungeon(r_idx)) return FALSE;
+
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+bool monster_shallow_water(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (!monster_dungeon(r_idx)) return FALSE;
+
+ if (r_ptr->flags2 & RF2_AURA_FIRE)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+bool monster_lava(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (!monster_dungeon(r_idx)) return FALSE;
+
+ if (((r_ptr->flags3 & RF3_IM_FIRE) ||
+ (r_ptr->flags7 & RF7_CAN_FLY)) &&
+ !(r_ptr->flags3 & RF3_AURA_COLD))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+void set_mon_num_hook(void)
+{
+ if (!dun_level)
+ {
+ switch (wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].terrain_idx)
+ {
+ case TERRAIN_TOWN:
+ get_mon_num_hook = monster_town;
+ break;
+ case TERRAIN_DEEP_WATER:
+ get_mon_num_hook = monster_ocean;
+ break;
+ case TERRAIN_SHALLOW_WATER:
+ get_mon_num_hook = monster_shore;
+ break;
+ case TERRAIN_DIRT:
+ get_mon_num_hook = monster_waste;
+ break;
+ case TERRAIN_GRASS:
+ get_mon_num_hook = monster_grass;
+ break;
+ case TERRAIN_TREES:
+ get_mon_num_hook = monster_wood;
+ break;
+ case TERRAIN_SHALLOW_LAVA:
+ case TERRAIN_DEEP_LAVA:
+ get_mon_num_hook = monster_volcano;
+ break;
+ case TERRAIN_MOUNTAIN:
+ get_mon_num_hook = monster_mountain;
+ break;
+ default:
+ get_mon_num_hook = monster_dungeon;
+ break;
+ }
+ }
+ else
+ {
+ get_mon_num_hook = monster_dungeon;
+ }
+}
+
+
+/*
+ * Check if monster can cross terrain
+ */
+bool monster_can_cross_terrain(byte feat, monster_race *r_ptr)
+{
+ /* Deep water */
+ if (feat == FEAT_DEEP_WATER)
+ {
+ if ((r_ptr->flags7 & RF7_AQUATIC) ||
+ (r_ptr->flags7 & RF7_CAN_FLY) ||
+ (r_ptr->flags7 & RF7_CAN_SWIM))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ /* Shallow water */
+ else if (feat == FEAT_SHAL_WATER)
+ {
+ if (r_ptr->flags2 & RF2_AURA_FIRE)
+ return FALSE;
+ else
+ return TRUE;
+ }
+ /* Aquatic monster */
+ else if ((r_ptr->flags7 & RF7_AQUATIC) &&
+ !(r_ptr->flags7 & RF7_CAN_FLY))
+ {
+ return FALSE;
+ }
+ /* Lava */
+ else if ((feat == FEAT_SHAL_LAVA) ||
+ (feat == FEAT_DEEP_LAVA))
+ {
+ if ((r_ptr->flags3 & RF3_IM_FIRE) ||
+ (r_ptr->flags7 & RF7_CAN_FLY))
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void set_mon_num2_hook(int y, int x)
+{
+ /* Set the monster list */
+ switch (cave[y][x].feat)
+ {
+ case FEAT_SHAL_WATER:
+ get_mon_num2_hook = monster_shallow_water;
+ break;
+ case FEAT_DEEP_WATER:
+ get_mon_num2_hook = monster_deep_water;
+ break;
+ case FEAT_DEEP_LAVA:
+ case FEAT_SHAL_LAVA:
+ get_mon_num2_hook = monster_lava;
+ break;
+ default:
+ get_mon_num2_hook = NULL;
+ break;
+ }
+}
diff --git a/src/monster2.c b/src/monster2.c
new file mode 100644
index 00000000..4e5b31db
--- /dev/null
+++ b/src/monster2.c
@@ -0,0 +1,4099 @@
+/* File: monster2.c */
+
+/* Purpose: misc code for monsters */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#define MAX_HORROR 20
+#define MAX_FUNNY 22
+#define MAX_COMMENT 5
+
+/* Monster gain a few levels ? */
+void monster_check_experience(int m_idx, bool silent)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ char m_name[80];
+
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Gain levels while possible */
+ while ((m_ptr->level < MONSTER_LEVEL_MAX) &&
+ (m_ptr->exp >= (u32b)(MONSTER_EXP(m_ptr->level + 1))))
+ {
+ /* Gain a level */
+ m_ptr->level++;
+
+ if (m_ptr->ml && (!silent)) cmsg_format(TERM_L_BLUE, "%^s gains a level.", m_name);
+
+ /* Gain hp */
+ if (magik(80))
+ {
+ m_ptr->maxhp += r_ptr->hside;
+ m_ptr->hp += r_ptr->hside;
+ }
+
+ /* Gain speed */
+ if (magik(40))
+ {
+ int speed = randint(2);
+ m_ptr->speed += speed;
+ m_ptr->mspeed += speed;
+ }
+
+ /* Gain ac */
+ if (magik(50))
+ {
+ m_ptr->ac += (r_ptr->ac / 10) ? r_ptr->ac / 10 : 1;
+ }
+
+ /* Gain melee power */
+ if (magik(30))
+ {
+ int i = rand_int(3), try = 20;
+
+ while ((try--) && !m_ptr->blow[i].d_dice) i = rand_int(3);
+
+ m_ptr->blow[i].d_dice++;
+ }
+ }
+}
+
+/* Monster gain some xp */
+void monster_gain_exp(int m_idx, u32b exp, bool silent)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ m_ptr->exp += exp;
+ if (wizard)
+ {
+ char m_name[80];
+
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ if (!silent) msg_format("%^s gains %ld exp.", m_name, exp);
+ }
+
+ monster_check_experience(m_idx, silent);
+}
+
+void monster_set_level(int m_idx, int level)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (level > 150) level = 150;
+
+ if (m_ptr->level < level)
+ {
+ m_ptr->exp = MONSTER_EXP(level);
+ monster_check_experience(m_idx, TRUE);
+ }
+}
+
+/* Will add, sub, .. */
+s32b modify_aux(s32b a, s32b b, char mod)
+{
+ switch (mod)
+ {
+ case MEGO_ADD:
+ return (a + b);
+ break;
+ case MEGO_SUB:
+ return (a - b);
+ break;
+ case MEGO_FIX:
+ return (b);
+ break;
+ case MEGO_PRC:
+ return (a * b / 100);
+ break;
+ default:
+ msg_format("WARNING, unmatching MEGO(%d).", mod);
+ return (0);
+ }
+}
+
+/* Is this ego ok for this monster ? */
+bool mego_ok(int r_idx, int ego)
+{
+ monster_ego *re_ptr = &re_info[ego];
+ monster_race *r_ptr = &r_info[r_idx];
+ bool ok = FALSE;
+ int i;
+
+ /* needed flags */
+ if (re_ptr->flags1 && ((re_ptr->flags1 & r_ptr->flags1) != re_ptr->flags1)) return FALSE;
+ if (re_ptr->flags2 && ((re_ptr->flags2 & r_ptr->flags2) != re_ptr->flags2)) return FALSE;
+ if (re_ptr->flags3 && ((re_ptr->flags3 & r_ptr->flags3) != re_ptr->flags3)) return FALSE;
+ if (re_ptr->flags7 && ((re_ptr->flags7 & r_ptr->flags7) != re_ptr->flags7)) return FALSE;
+ if (re_ptr->flags8 && ((re_ptr->flags8 & r_ptr->flags8) != re_ptr->flags8)) return FALSE;
+ if (re_ptr->flags9 && ((re_ptr->flags9 & r_ptr->flags9) != re_ptr->flags9)) return FALSE;
+
+ /* unwanted flags */
+ if (re_ptr->hflags1 && (re_ptr->hflags1 & r_ptr->flags1)) return FALSE;
+ if (re_ptr->hflags2 && (re_ptr->hflags2 & r_ptr->flags2)) return FALSE;
+ if (re_ptr->hflags3 && (re_ptr->hflags3 & r_ptr->flags3)) return FALSE;
+ if (re_ptr->hflags7 && (re_ptr->hflags7 & r_ptr->flags7)) return FALSE;
+ if (re_ptr->hflags8 && (re_ptr->hflags8 & r_ptr->flags8)) return FALSE;
+ if (re_ptr->hflags9 && (re_ptr->hflags9 & r_ptr->flags9)) return FALSE;
+
+ /* Need good race -- IF races are specified */
+ if (re_ptr->r_char[0])
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (r_ptr->d_char == re_ptr->r_char[i]) ok = TRUE;
+ }
+ if (!ok) return FALSE;
+ }
+ if (re_ptr->nr_char[0])
+ {
+ for (i = 0; i < 5; i++)
+ {
+ if (r_ptr->d_char == re_ptr->nr_char[i]) return (FALSE);
+ }
+ }
+
+ /* Passed all tests ? */
+ return TRUE;
+}
+
+/* Choose an ego type */
+int pick_ego_monster(int r_idx)
+{
+ /* Assume no ego */
+ int ego = 0, lvl;
+ int tries = max_re_idx + 10;
+ monster_ego *re_ptr;
+
+ if ((!(dungeon_flags2 & DF2_ELVEN)) && (!(dungeon_flags2 & DF2_DWARVEN)))
+ {
+ /* No townspeople ego */
+ if (!r_info[r_idx].level) return 0;
+
+ /* First are we allowed to find an ego */
+ if (!magik(MEGO_CHANCE)) return 0;
+
+ /* Lets look for one */
+ while (tries--)
+ {
+ /* Pick one */
+ ego = rand_range(1, max_re_idx - 1);
+ re_ptr = &re_info[ego];
+
+ /* No hope so far */
+ if (!mego_ok(r_idx, ego)) continue;
+
+ /* Not too much OoD */
+ lvl = r_info[r_idx].level;
+ MODIFY(lvl, re_ptr->level, 0);
+ lvl -= ((dun_level / 2) + (rand_int(dun_level / 2)));
+ if (lvl < 1) lvl = 1;
+ if (rand_int(lvl)) continue;
+
+ /* Each ego types have a rarity */
+ if (rand_int(re_ptr->rarity)) continue;
+
+ /* We finally got one ? GREAT */
+ return ego;
+ }
+ }
+ /* Bypass restrictions for themed townspeople */
+ else
+ {
+ if (dungeon_flags2 & DF2_ELVEN)
+ ego = test_mego_name("Elven");
+ else if (dungeon_flags2 & DF2_DWARVEN)
+ ego = test_mego_name("Dwarven");
+
+ if (mego_ok(r_idx, ego))
+ return ego;
+ }
+
+ /* Found none ? so sad, well no ego for the time being */
+ return 0;
+}
+
+/*
+ * Return a (monster_race*) with the combination of the monster
+ * properties and the ego type
+ */
+monster_race* race_info_idx(int r_idx, int ego)
+{
+ static monster_race race;
+ monster_ego *re_ptr = &re_info[ego];
+ monster_race *r_ptr = &r_info[r_idx], *nr_ptr = &race;
+ int i;
+
+ /* No work needed */
+ if (!ego) return r_ptr;
+
+ /* Copy the base monster */
+ COPY(nr_ptr, r_ptr, monster_race);
+
+ /* Adjust the values */
+ for (i = 0; i < 4; i++)
+ {
+ s32b j, k;
+
+ j = modify_aux(nr_ptr->blow[i].d_dice, re_ptr->blow[i].d_dice, re_ptr->blowm[i][0]);
+ if (j < 0) j = 0;
+ k = modify_aux(nr_ptr->blow[i].d_side, re_ptr->blow[i].d_side, re_ptr->blowm[i][1]);
+ if (k < 0) k = 0;
+
+ nr_ptr->blow[i].d_dice = j;
+ nr_ptr->blow[i].d_side = k;
+
+ if (re_ptr->blow[i].method) nr_ptr->blow[i].method = re_ptr->blow[i].method;
+ if (re_ptr->blow[i].effect) nr_ptr->blow[i].effect = re_ptr->blow[i].effect;
+ }
+
+ MODIFY(nr_ptr->hdice, re_ptr->hdice, 1);
+ MODIFY(nr_ptr->hside, re_ptr->hside, 1);
+
+ MODIFY(nr_ptr->ac, re_ptr->ac, 0);
+
+ MODIFY(nr_ptr->sleep, re_ptr->sleep, 0);
+
+ MODIFY(nr_ptr->aaf, re_ptr->aaf, 1);
+ MODIFY(nr_ptr->speed, re_ptr->speed, 50);
+ MODIFY(nr_ptr->mexp, re_ptr->mexp, 0);
+
+ MODIFY(nr_ptr->weight, re_ptr->weight, 10);
+
+ nr_ptr->freq_inate = (nr_ptr->freq_inate > re_ptr->freq_inate)
+ ? nr_ptr->freq_inate : re_ptr->freq_inate;
+ nr_ptr->freq_spell = (nr_ptr->freq_spell > re_ptr->freq_spell)
+ ? nr_ptr->freq_spell : re_ptr->freq_spell;
+
+ MODIFY(nr_ptr->level, re_ptr->level, 1);
+
+ /* Take off some flags */
+ nr_ptr->flags1 &= ~(re_ptr->nflags1);
+ nr_ptr->flags2 &= ~(re_ptr->nflags2);
+ nr_ptr->flags3 &= ~(re_ptr->nflags3);
+ nr_ptr->flags4 &= ~(re_ptr->nflags4);
+ nr_ptr->flags5 &= ~(re_ptr->nflags5);
+ nr_ptr->flags6 &= ~(re_ptr->nflags6);
+ nr_ptr->flags7 &= ~(re_ptr->nflags7);
+ nr_ptr->flags8 &= ~(re_ptr->nflags8);
+ nr_ptr->flags9 &= ~(re_ptr->nflags9);
+
+ /* Add some flags */
+ nr_ptr->flags1 |= re_ptr->mflags1;
+ nr_ptr->flags2 |= re_ptr->mflags2;
+ nr_ptr->flags3 |= re_ptr->mflags3;
+ nr_ptr->flags4 |= re_ptr->mflags4;
+ nr_ptr->flags5 |= re_ptr->mflags5;
+ nr_ptr->flags6 |= re_ptr->mflags6;
+ nr_ptr->flags7 |= re_ptr->mflags7;
+ nr_ptr->flags8 |= re_ptr->mflags8;
+ nr_ptr->flags9 |= re_ptr->mflags9;
+
+ /* Change the char/attr is needed */
+ if (re_ptr->d_char != MEGO_CHAR_ANY)
+ {
+ nr_ptr->d_char = re_ptr->d_char;
+ nr_ptr->x_char = re_ptr->d_char;
+ }
+ if (re_ptr->d_attr != MEGO_CHAR_ANY)
+ {
+ nr_ptr->d_attr = re_ptr->d_attr;
+ nr_ptr->x_attr = re_ptr->d_attr;
+ }
+
+ /* And finanly return a pointer to a fully working monster race */
+ return nr_ptr;
+}
+
+static cptr horror_desc[MAX_HORROR] =
+{
+ "abominable",
+ "abysmal",
+ "appalling",
+ "baleful",
+ "blasphemous",
+
+ "disgusting",
+ "dreadful",
+ "filthy",
+ "grisly",
+ "hideous",
+
+ "hellish",
+ "horrible",
+ "infernal",
+ "loathsome",
+ "nightmarish",
+
+ "repulsive",
+ "sacrilegious",
+ "terrible",
+ "unclean",
+ "unspeakable",
+};
+
+static cptr funny_desc[MAX_FUNNY] =
+{
+ "silly",
+ "hilarious",
+ "absurd",
+ "insipid",
+ "ridiculous",
+
+ "laughable",
+ "ludicrous",
+ "far-out",
+ "groovy",
+ "postmodern",
+
+ "fantastic",
+ "dadaistic",
+ "cubistic",
+ "cosmic",
+ "awesome",
+
+ "incomprehensible",
+ "fabulous",
+ "amazing",
+ "incredible",
+ "chaotic",
+
+ "wild",
+ "preposterous",
+};
+
+static cptr funny_comments[MAX_COMMENT] =
+{
+ "Wow, cosmic, man!",
+ "Rad!",
+ "Groovy!",
+ "Cool!",
+ "Far out!"
+};
+
+
+int get_wilderness_flag(void)
+{
+ int x = p_ptr->wilderness_x;
+ int y = p_ptr->wilderness_y;
+
+ if (dun_level)
+ return (RF8_DUNGEON);
+ else
+ return (1L << wf_info[wild_map[y][x].feat].terrain_idx);
+}
+
+
+/*
+ * Delete a monster by index.
+ *
+ * When a monster is deleted, all of its objects are deleted.
+ */
+void delete_monster_idx(int i)
+{
+ int x, y, j;
+
+ monster_type *m_ptr = &m_list[i];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ bool had_lite = FALSE;
+ ;
+
+
+ /* Get location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Hack -- Reduce the racial counter */
+ r_ptr->cur_num--;
+ r_ptr->on_saved = FALSE;
+
+ /* Hack -- count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro--;
+
+ /* XXX XXX XXX remove monster light source */
+ if (r_ptr->flags9 & (RF9_HAS_LITE)) had_lite = TRUE;
+
+
+ /* Hack -- remove target monster */
+ if (i == target_who) target_who = 0;
+
+ /* Hack -- remove tracked monster */
+ if (i == health_who) health_track(0);
+
+ /* Hack -- remove tracked monster */
+ if (i == p_ptr->control) p_ptr->control = 0;
+
+ for (j = m_max - 1; j >= 1; j--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[j];
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (t_ptr->target == i) t_ptr->target = -1;
+ }
+
+ /* Monster is gone */
+ cave[y][x].m_idx = 0;
+
+
+ /* Delete objects */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Hack -- efficiency */
+ o_ptr->held_m_idx = 0;
+
+ if ( p_ptr->preserve )
+ {
+ /* Hack -- Preserve unknown artifacts */
+ if (artifact_p(o_ptr) && !object_known_p(o_ptr))
+ {
+ /* Mega-Hack -- Preserve the artifact */
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[o_ptr->k_idx].artifact = FALSE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+
+ /* Delete mind & special race if needed */
+ if (m_ptr->sr_ptr)
+ KILL(m_ptr->sr_ptr, monster_race);
+ if (m_ptr->mind)
+ KILL(m_ptr->mind, monster_mind);
+
+ /* Wipe the Monster */
+ m_ptr = WIPE(m_ptr, monster_type);
+
+ /* Count monsters */
+ m_cnt--;
+
+ /* Do we survided our fate ? */
+ if ((dungeon_type == DUNGEON_DEATH) && (!m_cnt))
+ {
+ msg_print("You overcome your fate, mortal!");
+
+ dungeon_type = DUNGEON_WILDERNESS;
+ dun_level = 0;
+
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Update monster light */
+ if (had_lite) p_ptr->update |= (PU_MON_LITE);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Visual update */
+ lite_spot(y, x);
+}
+
+
+/*
+ * Delete the monster, if any, at a given location
+ */
+void delete_monster(int y, int x)
+{
+ cave_type *c_ptr;
+
+ /* Paranoia */
+ if (!in_bounds(y, x)) return;
+
+ /* Check the grid */
+ c_ptr = &cave[y][x];
+
+ /* Delete the monster (if any) */
+ if (c_ptr->m_idx) delete_monster_idx(c_ptr->m_idx);
+}
+
+
+/*
+ * Move an object from index i1 to index i2 in the object list
+ */
+static void compact_monsters_aux(int i1, int i2)
+{
+ int y, x, j;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Do nothing */
+ if (i1 == i2) return;
+
+
+ /* Old monster */
+ m_ptr = &m_list[i1];
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Update the cave */
+ c_ptr->m_idx = i2;
+
+ /* Repair objects being carried by monster */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Reset monster pointer */
+ o_ptr->held_m_idx = i2;
+ }
+
+ /* Hack -- Update the control */
+ if (p_ptr->control == i1) p_ptr->control = i2;
+
+ /* Hack -- Update the doppleganger */
+ if (doppleganger == i1) doppleganger = i2;
+
+ /* Hack -- Update the target */
+ if (target_who == i1) target_who = i2;
+
+ /* Hack -- Update the health bar */
+ if (health_who == i1) health_track(i2);
+
+ for (j = m_max - 1; j >= 1; j--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[j];
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (t_ptr->target == i1) t_ptr->target = i2;
+ }
+
+ /* Structure copy */
+ COPY(&m_list[i2], &m_list[i1], monster_type);
+
+ /* Delete mind & special race if needed */
+ if (m_list[i1].sr_ptr)
+ KILL(m_list[i1].sr_ptr, monster_race);
+ if (m_list[i1].mind)
+ KILL(m_list[i1].mind, monster_mind);
+
+ /* Wipe the hole */
+ m_ptr = WIPE(&m_list[i1], monster_type);
+}
+
+
+/*
+ * Compact and Reorder the monster list
+ *
+ * This function can be very dangerous, use with caution!
+ *
+ * When actually "compacting" monsters, we base the saving throw
+ * on a combination of monster level, distance from player, and
+ * current "desperation".
+ *
+ * After "compacting" (if needed), we "reorder" the monsters into a more
+ * compact order, and we reset the allocation info, and the "live" array.
+ */
+void compact_monsters(int size)
+{
+ int i, num, cnt;
+ int cur_lev, cur_dis, chance;
+
+
+ /* Message (only if compacting) */
+ if (size) msg_print("Compacting monsters...");
+
+
+ /* Compact at least 'size' objects */
+ for (num = 0, cnt = 1; num < size; cnt++)
+ {
+ /* Get more vicious each iteration */
+ cur_lev = 5 * cnt;
+
+ /* Get closer each iteration */
+ cur_dis = 5 * (20 - cnt);
+
+ /* Check all the monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- skip "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- High level monsters start out "immune" */
+ if (m_ptr->level > cur_lev) continue;
+
+ /* Ignore nearby monsters */
+ if ((cur_dis > 0) && (m_ptr->cdis < cur_dis)) continue;
+
+ /* Saving throw chance */
+ chance = 90;
+
+ /* Only compact "Quest" Monsters in emergencies */
+ if ((m_ptr->mflag & MFLAG_QUEST) && (cnt < 1000)) chance = 100;
+
+ /* Try not to compact Unique Monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) chance = 99;
+
+ /* All monsters get a saving throw */
+ if (rand_int(100) < chance) continue;
+
+ /* Delete the monster */
+ delete_monster_idx(i);
+
+ /* Count the monster */
+ num++;
+ }
+ }
+
+
+ /* Excise dead monsters (backwards!) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Get the i'th monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Skip real monsters */
+ if (m_ptr->r_idx) continue;
+
+ /* Move last monster into open hole */
+ compact_monsters_aux(m_max - 1, i);
+
+ /* Compress "m_max" */
+ m_max--;
+ }
+}
+
+/*
+ * Delete/Remove all the monsters when the player leaves the level
+ *
+ * This is an efficient method of simulating multiple calls to the
+ * "delete_monster()" function, with no visual effects.
+ */
+void wipe_m_list(void)
+{
+ int i;
+
+ /* Delete all the monsters */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Mega-Hack -- preserve Unique's XXX XXX XXX */
+
+ /* Hack -- Reduce the racial counter */
+ r_ptr->cur_num--;
+
+ /* Monster is gone */
+ cave[m_ptr->fy][m_ptr->fx].m_idx = 0;
+
+ /* Delete mind & special race if needed */
+ if (m_ptr->sr_ptr)
+ KILL(m_ptr->sr_ptr, monster_race);
+ if (m_ptr->mind)
+ KILL(m_ptr->mind, monster_mind);
+
+
+ /* Wipe the Monster */
+ m_ptr = WIPE(m_ptr, monster_type);
+ }
+
+ /* Reset "m_max" */
+ m_max = 1;
+
+ /* Reset "m_cnt" */
+ m_cnt = 0;
+
+ /* Hack -- reset "reproducer" count */
+ num_repro = 0;
+
+ /* Hack -- no more target */
+ target_who = 0;
+
+ /* Reset control */
+ p_ptr->control = 0;
+
+ /* Hack -- no more tracking */
+ health_track(0);
+}
+
+
+/*
+ * Acquires and returns the index of a "free" monster.
+ *
+ * This routine should almost never fail, but it *can* happen.
+ */
+s16b m_pop(void)
+{
+ int i;
+
+
+ /* Normal allocation */
+ if (m_max < max_m_idx)
+ {
+ /* Access the next hole */
+ i = m_max;
+
+ /* Expand the array */
+ m_max++;
+
+ /* Count monsters */
+ m_cnt++;
+
+ /* Return the index */
+ return (i);
+ }
+
+
+ /* Recycle dead monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &m_list[i];
+
+ /* Skip live monsters */
+ if (m_ptr->r_idx) continue;
+
+ /* Count monsters */
+ m_cnt++;
+
+ /* Use this monster */
+ return (i);
+ }
+
+
+ /* Warn the player (except during dungeon creation) */
+ if (character_dungeon) msg_print("Too many monsters!");
+
+ /* Try not to crash */
+ return (0);
+}
+
+
+
+
+/*
+ * Apply a "monster restriction function" to the "monster allocation table"
+ */
+errr get_mon_num_prep(void)
+{
+ int i;
+
+ /* Scan the allocation table */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Get the entry */
+ alloc_entry *entry = &alloc_race_table[i];
+
+ /* Accept monsters which pass the restriction, if any */
+ if ((!get_mon_num_hook || (*get_mon_num_hook)(entry->index)) &&
+ (!get_mon_num2_hook || (*get_mon_num2_hook)(entry->index)))
+ {
+ /* Accept this monster */
+ entry->prob2 = entry->prob1;
+ }
+
+ /* Do not use this monster */
+ else
+ {
+ /* Decline this monster */
+ entry->prob2 = 0;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Some dungeon types restrict the possible monsters.
+ * Return TRUE is the monster is OK and FALSE otherwise
+ */
+bool apply_rule(monster_race *r_ptr, byte rule)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ if (d_ptr->rules[rule].mode == DUNGEON_MODE_NONE)
+ {
+ return TRUE;
+ }
+ else if ((d_ptr->rules[rule].mode == DUNGEON_MODE_AND) || (d_ptr->rules[rule].mode == DUNGEON_MODE_NAND))
+ {
+ int a;
+
+ if (d_ptr->rules[rule].mflags1)
+ {
+ if ((d_ptr->rules[rule].mflags1 & r_ptr->flags1) != d_ptr->rules[rule].mflags1)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags2)
+ {
+ if ((d_ptr->rules[rule].mflags2 & r_ptr->flags2) != d_ptr->rules[rule].mflags2)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags3)
+ {
+ if ((d_ptr->rules[rule].mflags3 & r_ptr->flags3) != d_ptr->rules[rule].mflags3)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags4)
+ {
+ if ((d_ptr->rules[rule].mflags4 & r_ptr->flags4) != d_ptr->rules[rule].mflags4)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags5)
+ {
+ if ((d_ptr->rules[rule].mflags5 & r_ptr->flags5) != d_ptr->rules[rule].mflags5)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags6)
+ {
+ if ((d_ptr->rules[rule].mflags6 & r_ptr->flags6) != d_ptr->rules[rule].mflags6)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags7)
+ {
+ if ((d_ptr->rules[rule].mflags7 & r_ptr->flags7) != d_ptr->rules[rule].mflags7)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags8)
+ {
+ if ((d_ptr->rules[rule].mflags8 & r_ptr->flags8) != d_ptr->rules[rule].mflags8)
+ return FALSE;
+ }
+ if (d_ptr->rules[rule].mflags9)
+ {
+ if ((d_ptr->rules[rule].mflags9 & r_ptr->flags9) != d_ptr->rules[rule].mflags9)
+ return FALSE;
+ }
+ for (a = 0; a < 5; a++)
+ {
+ if (d_ptr->rules[rule].r_char[a] && (d_ptr->rules[rule].r_char[a] != r_ptr->d_char)) return FALSE;
+ }
+
+ /* All checks passed ? lets go ! */
+ return TRUE;
+ }
+ else if ((d_ptr->rules[rule].mode == DUNGEON_MODE_OR) || (d_ptr->rules[rule].mode == DUNGEON_MODE_NOR))
+ {
+ int a;
+
+ if (d_ptr->rules[rule].mflags1 && (r_ptr->flags1 & d_ptr->rules[rule].mflags1)) return TRUE;
+ if (d_ptr->rules[rule].mflags2 && (r_ptr->flags2 & d_ptr->rules[rule].mflags2)) return TRUE;
+ if (d_ptr->rules[rule].mflags3 && (r_ptr->flags3 & d_ptr->rules[rule].mflags3)) return TRUE;
+ if (d_ptr->rules[rule].mflags4 && (r_ptr->flags4 & d_ptr->rules[rule].mflags4)) return TRUE;
+ if (d_ptr->rules[rule].mflags5 && (r_ptr->flags5 & d_ptr->rules[rule].mflags5)) return TRUE;
+ if (d_ptr->rules[rule].mflags6 && (r_ptr->flags6 & d_ptr->rules[rule].mflags6)) return TRUE;
+ if (d_ptr->rules[rule].mflags7 && (r_ptr->flags7 & d_ptr->rules[rule].mflags7)) return TRUE;
+ if (d_ptr->rules[rule].mflags8 && (r_ptr->flags8 & d_ptr->rules[rule].mflags8)) return TRUE;
+ if (d_ptr->rules[rule].mflags9 && (r_ptr->flags9 & d_ptr->rules[rule].mflags9)) return TRUE;
+
+ for (a = 0; a < 5; a++)
+ if (d_ptr->rules[rule].r_char[a] == r_ptr->d_char) return TRUE;
+
+ /* All checks failled ? Sorry ... */
+ return FALSE;
+ }
+
+ /* Should NEVER happen */
+ return FALSE;
+}
+
+bool restrict_monster_to_dungeon(int r_idx)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Select a random rule */
+ byte rule = d_ptr->rule_percents[rand_int(100)];
+
+ /* Apply the rule */
+ bool rule_ret = apply_rule(r_ptr, rule);
+
+ /* Should the rule be right or not ? */
+ if ((d_ptr->rules[rule].mode == DUNGEON_MODE_NAND) || (d_ptr->rules[rule].mode == DUNGEON_MODE_NOR)) rule_ret = !rule_ret;
+
+ /* Rule ok ? */
+ if (rule_ret) return TRUE;
+
+ /* Not allowed */
+ return FALSE;
+}
+
+/* Ugly hack, let summon unappropriate monsters */
+bool summon_hack = FALSE;
+
+/*
+ * Choose a monster race that seems "appropriate" to the given level
+ *
+ * This function uses the "prob2" field of the "monster allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" monster, in
+ * a relatively efficient manner.
+ *
+ * Note that "town" monsters will *only* be created in the town, and
+ * "normal" monsters will *never* be created in the town, unless the
+ * "level" is "modified", for example, by polymorph or summoning.
+ *
+ * There is a small chance (1/50) of "boosting" the given depth by
+ * a small amount (up to four levels), except in the town.
+ *
+ * It is (slightly) more likely to acquire a monster of the given level
+ * than one of a lower level. This is done by choosing several monsters
+ * appropriate to the given level and keeping the "hardest" one.
+ *
+ * Note that if no monsters are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ */
+s16b get_mon_num(int level)
+{
+ int i, j, p;
+
+ int r_idx;
+
+ long value, total;
+
+ monster_race *r_ptr;
+
+ alloc_entry *table = alloc_race_table;
+
+ int in_tome;
+
+ /* Boost the level */
+ if (level > 0)
+ {
+ /* Occasional "nasty" monster */
+ if (rand_int(NASTY_MON) == 0)
+ {
+ /* Pick a level bonus */
+ int d = level / 4 + 2;
+
+ /* Boost the level */
+ level += ((d < 5) ? d : 5);
+ }
+
+ /* Occasional "nasty" monster */
+ if (rand_int(NASTY_MON) == 0)
+ {
+ /* Pick a level bonus */
+ int d = level / 4 + 2;
+
+ /* Boost the level */
+ level += ((d < 5) ? d : 5);
+ }
+ }
+
+
+ /* Reset total */
+ total = 0L;
+
+ /* Check whether this is ToME or a module */
+ in_tome = strcmp(game_module, "ToME") == 0;
+
+ /* Process probabilities */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Monsters are sorted by depth */
+ if (table[i].level > level) break;
+
+ /* Default */
+ table[i].prob3 = 0;
+
+ /* Access the "r_idx" of the chosen monster */
+ r_idx = table[i].index;
+
+ /* Access the actual race */
+ r_ptr = &r_info[r_idx];
+
+ /* Hack -- "unique" monsters must be "unique" */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) &&
+ (r_ptr->cur_num >= r_ptr->max_num))
+ {
+ continue;
+ }
+
+ /* Depth Monsters never appear out of depth */
+ if ((r_ptr->flags1 & (RF1_FORCE_DEPTH)) && (r_ptr->level > dun_level))
+ {
+ continue;
+ }
+
+ /* Depth Monsters never appear out of their depth */
+ if ((r_ptr->flags9 & (RF9_ONLY_DEPTH)) && (r_ptr->level != dun_level))
+ {
+ continue;
+ }
+
+ if(in_tome)
+ {
+ /* Zangbandish monsters not allowed */
+ if (r_ptr->flags8 & RF8_ZANGBAND) continue;
+
+ /* Lovecraftian monsters not allowed */
+ if (r_ptr->flags8 & RF8_CTHANGBAND) continue;
+ }
+
+ /* Joke monsters allowed ? or not ? */
+ if (!joke_monsters && (r_ptr->flags8 & RF8_JOKEANGBAND)) continue;
+
+ /* Some dungeon types restrict the possible monsters */
+ if (!summon_hack && !restrict_monster_to_dungeon(r_idx) && dun_level) continue;
+
+ /* Accept */
+ table[i].prob3 = table[i].prob2;
+
+ /* Total */
+ total += table[i].prob3;
+ }
+
+ /* No legal monsters */
+ if (total <= 0) return (0);
+
+
+ /* Pick a monster */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+
+ /* Power boost */
+ p = rand_int(100);
+
+ /* Try for a "harder" monster once (50%) or twice (10%) */
+ if (p < 60)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a monster */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+ /* Try for a "harder" monster twice (10%) */
+ if (p < 10)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a monster */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_race_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+
+ /* Result */
+ return (table[i].index);
+}
+
+
+
+
+
+/*
+ * Build a string describing a monster in some way.
+ *
+ * We can correctly describe monsters based on their visibility.
+ * We can force all monsters to be treated as visible or invisible.
+ * We can build nominatives, objectives, possessives, or reflexives.
+ * We can selectively pronominalize hidden, visible, or all monsters.
+ * We can use definite or indefinite descriptions for hidden monsters.
+ * We can use definite or indefinite descriptions for visible monsters.
+ *
+ * Pronominalization involves the gender whenever possible and allowed,
+ * so that by cleverly requesting pronominalization / visibility, you
+ * can get messages like "You hit someone. She screams in agony!".
+ *
+ * Reflexives are acquired by requesting Objective plus Possessive.
+ *
+ * If no m_ptr arg is given (?), the monster is assumed to be hidden,
+ * unless the "Assume Visible" mode is requested.
+ *
+ * If no r_ptr arg is given, it is extracted from m_ptr and r_info
+ * If neither m_ptr nor r_ptr is given, the monster is assumed to
+ * be neuter, singular, and hidden (unless "Assume Visible" is set),
+ * in which case you may be in trouble... :-)
+ *
+ * I am assuming that no monster name is more than 70 characters long,
+ * so that "char desc[80];" is sufficiently large for any result.
+ *
+ * Mode Flags:
+ * 0x01 --> Objective (or Reflexive)
+ * 0x02 --> Possessive (or Reflexive)
+ * 0x04 --> Use indefinites for hidden monsters ("something")
+ * 0x08 --> Use indefinites for visible monsters ("a kobold")
+ * 0x10 --> Pronominalize hidden monsters
+ * 0x20 --> Pronominalize visible monsters
+ * 0x40 --> Assume the monster is hidden
+ * 0x80 --> Assume the monster is visible
+ * 0x100 --> Ignore insanity
+ *
+ * Useful Modes:
+ * 0x00 --> Full nominative name ("the kobold") or "it"
+ * 0x04 --> Full nominative name ("the kobold") or "something"
+ * 0x80 --> Genocide resistance name ("the kobold")
+ * 0x88 --> Killing name ("a kobold")
+ * 0x22 --> Possessive, genderized if visible ("his") or "its"
+ * 0x23 --> Reflexive, genderized if visible ("himself") or "itself"
+ */
+void monster_desc(char *desc, monster_type *m_ptr, int mode)
+{
+ cptr res;
+ monster_race *r_ptr = race_inf(m_ptr);
+ cptr b_name = (r_name + r_ptr->name);
+ char silly_name[80], name[100];
+ bool seen, pron;
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+
+ if (m_ptr->ego)
+ {
+ if (re_info[m_ptr->ego].before) sprintf(name, "%s %s", re_name + re_info[m_ptr->ego].name, b_name);
+ else sprintf(name, "%s %s", b_name, re_name + re_info[m_ptr->ego].name);
+ }
+ else
+ {
+ sprintf(name, "%s", b_name);
+ }
+
+ /*
+ * Are we hallucinating? (Idea from Nethack...)
+ * insanity roll added by pelpel
+ */
+ if (!(mode & 0x100) && (p_ptr->image || (rand_int(300) < insanity)))
+ {
+ if (rand_int(2) == 0)
+ {
+ monster_race *hallu_race;
+
+ do
+ {
+ hallu_race = &r_info[randint(max_r_idx - 2)];
+ }
+ while (hallu_race->flags1 & RF1_UNIQUE);
+
+ strcpy(silly_name, (r_name + hallu_race->name));
+ }
+ else
+ {
+ get_rnd_line("silly.txt", silly_name);
+ }
+
+ strcpy(name, silly_name);
+ }
+
+ /* Can we "see" it (exists + forced, or visible + not unforced) */
+ seen = (m_ptr && ((mode & 0x80) || (!(mode & 0x40) && m_ptr->ml)));
+
+ /* Sexed Pronouns (seen and allowed, or unseen and allowed) */
+ pron = (m_ptr && ((seen && (mode & 0x20)) || (!seen && (mode & 0x10))));
+
+
+ /* First, try using pronouns, or describing hidden monsters */
+ if (!seen || pron)
+ {
+ /* an encoding of the monster "sex" */
+ int kind = 0x00;
+
+ /* Extract the gender (if applicable) */
+ if (r_ptr->flags1 & (RF1_FEMALE)) kind = 0x20;
+ else if (r_ptr->flags1 & (RF1_MALE)) kind = 0x10;
+
+ /* Ignore the gender (if desired) */
+ if (!m_ptr || !pron) kind = 0x00;
+
+
+ /* Assume simple result */
+ res = "it";
+
+ /* Brute force: split on the possibilities */
+ switch (kind | (mode & 0x07))
+ {
+ /* Neuter, or unknown */
+ case 0x00:
+ res = "it";
+ break;
+ case 0x01:
+ res = "it";
+ break;
+ case 0x02:
+ res = "its";
+ break;
+ case 0x03:
+ res = "itself";
+ break;
+ case 0x04:
+ res = "something";
+ break;
+ case 0x05:
+ res = "something";
+ break;
+ case 0x06:
+ res = "something's";
+ break;
+ case 0x07:
+ res = "itself";
+ break;
+
+ /* Male (assume human if vague) */
+ case 0x10:
+ res = "he";
+ break;
+ case 0x11:
+ res = "him";
+ break;
+ case 0x12:
+ res = "his";
+ break;
+ case 0x13:
+ res = "himself";
+ break;
+ case 0x14:
+ res = "someone";
+ break;
+ case 0x15:
+ res = "someone";
+ break;
+ case 0x16:
+ res = "someone's";
+ break;
+ case 0x17:
+ res = "himself";
+ break;
+
+ /* Female (assume human if vague) */
+ case 0x20:
+ res = "she";
+ break;
+ case 0x21:
+ res = "her";
+ break;
+ case 0x22:
+ res = "her";
+ break;
+ case 0x23:
+ res = "herself";
+ break;
+ case 0x24:
+ res = "someone";
+ break;
+ case 0x25:
+ res = "someone";
+ break;
+ case 0x26:
+ res = "someone's";
+ break;
+ case 0x27:
+ res = "herself";
+ break;
+ }
+
+ /* Copy the result */
+ (void)strcpy(desc, res);
+ }
+
+
+ /* Handle visible monsters, "reflexive" request */
+ else if ((mode & 0x02) && (mode & 0x01))
+ {
+ /* The monster is visible, so use its gender */
+ if (r_ptr->flags1 & (RF1_FEMALE)) strcpy(desc, "herself");
+ else if (r_ptr->flags1 & (RF1_MALE)) strcpy(desc, "himself");
+ else strcpy(desc, "itself");
+ }
+
+
+ /* Handle all other visible monster requests */
+ else
+ {
+ /* It could be a Unique */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && !(p_ptr->image))
+ {
+ /* Start with the name (thus nominative and objective) */
+ (void)strcpy(desc, name);
+ }
+
+ /* It could be an indefinite monster */
+ else if (mode & 0x08)
+ {
+ /* XXX Check plurality for "some" */
+
+ /* Indefinite monsters need an indefinite article */
+ (void)strcpy(desc, is_a_vowel(name[0]) ? "an " : "a ");
+ (void)strcat(desc, name);
+ }
+
+ /* It could be a normal, definite, monster */
+ else
+ {
+ /* Definite monsters need a definite article */
+ if (m_ptr->status >= MSTATUS_PET)
+ (void)strcpy(desc, "your ");
+ else
+ (void)strcpy(desc, "the ");
+
+ (void)strcat(desc, name);
+ }
+
+ /* Handle the Possessive as a special afterthought */
+ if (mode & 0x02)
+ {
+ /* XXX Check for trailing "s" */
+
+ /* Simply append "apostrophe" and "s" */
+ (void)strcat(desc, "'s");
+ }
+ }
+}
+
+void monster_race_desc(char *desc, int r_idx, int ego)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+ cptr b_name = (r_name + r_ptr->name);
+ char name[80];
+
+ if (ego)
+ {
+ if (re_info[ego].before) sprintf(name, "%s %s", re_name + re_info[ego].name, b_name);
+ else sprintf(name, "%s %s", b_name, re_name + re_info[ego].name);
+ }
+ else
+ {
+ sprintf(name, "%s", b_name);
+ }
+
+ /* It could be a Unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ /* Start with the name (thus nominative and objective) */
+ (void)strcpy(desc, name);
+ }
+
+ /* It could be a normal, definite, monster */
+ else
+ {
+ /* Definite monsters need a definite article */
+ (void)strcpy(desc, is_a_vowel(name[0]) ? "an " : "a ");
+
+ (void)strcat(desc, name);
+ }
+}
+
+
+
+/*
+ * Learn about a monster (by "probing" it)
+ */
+void lore_do_probe(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Hack -- Memorize some flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+}
+
+
+/*
+ * Take note that the given monster just dropped some treasure
+ *
+ * Note that learning the "GOOD"/"GREAT" flags gives information
+ * about the treasure (even when the monster is killed for the first
+ * time, such as uniques, and the treasure has not been examined yet).
+ *
+ * This "indirect" method is used to prevent the player from learning
+ * exactly how much treasure a monster can drop from observing only
+ * a single example of a drop. This method actually observes how much
+ * gold and items are dropped, and remembers that information to be
+ * described later by the monster recall code.
+ */
+void lore_treasure(int m_idx, int num_item, int num_gold)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Note the number of things dropped */
+ if (num_item > r_ptr->r_drop_item) r_ptr->r_drop_item = num_item;
+ if (num_gold > r_ptr->r_drop_gold) r_ptr->r_drop_gold = num_gold;
+
+ /* Hack -- memorize the good/great flags */
+ if (r_ptr->flags1 & (RF1_DROP_GOOD)) r_ptr->r_flags1 |= (RF1_DROP_GOOD);
+ if (r_ptr->flags1 & (RF1_DROP_GREAT)) r_ptr->r_flags1 |= (RF1_DROP_GREAT);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+}
+
+
+
+void sanity_blast(monster_type * m_ptr, bool necro)
+{
+ bool happened = FALSE;
+ int power = 100;
+
+ if (!necro)
+ {
+ char m_name[80];
+ monster_race *r_ptr;
+
+ if (m_ptr != NULL) r_ptr = race_inf(m_ptr);
+ else return;
+
+ power = (m_ptr->level) + 10;
+
+ if (m_ptr != NULL)
+ {
+ monster_desc(m_name, m_ptr, 0);
+
+ if (!(r_ptr->flags1 & RF1_UNIQUE))
+ {
+ if (r_ptr->flags1 & RF1_FRIENDS)
+ power /= 2;
+ }
+ else power *= 2;
+
+ if (!hack_mind)
+ return ; /* No effect yet, just loaded... */
+
+ if (!(m_ptr->ml))
+ return ; /* Cannot see it for some reason */
+
+ if (!(r_ptr->flags2 & RF2_ELDRITCH_HORROR))
+ return ; /* oops */
+
+
+
+ if ((is_friend(m_ptr) > 0) && (randint(8) != 1))
+ return ; /* Pet eldritch horrors are safe most of the time */
+
+
+ if (randint(power) < p_ptr->skill_sav)
+ {
+ return ; /* Save, no adverse effects */
+ }
+
+
+ if (p_ptr->image)
+ {
+ /* Something silly happens... */
+ msg_format("You behold the %s visage of %s!",
+ funny_desc[(randint(MAX_FUNNY)) - 1], m_name);
+ if (randint(3) == 1)
+ {
+ msg_print(funny_comments[randint(MAX_COMMENT) - 1]);
+ p_ptr->image = (p_ptr->image + randint(m_ptr->level));
+ }
+ return ; /* Never mind; we can't see it clearly enough */
+ }
+
+ /* Something frightening happens... */
+ msg_format("You behold the %s visage of %s!",
+ horror_desc[(randint(MAX_HORROR)) - 1], m_name);
+
+ r_ptr->r_flags2 |= RF2_ELDRITCH_HORROR;
+
+ }
+
+ /* Undead characters are 50% likely to be unaffected */
+ if ((PRACE_FLAG(PR1_UNDEAD)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")))
+ {
+ if (randint(100) < (25 + (p_ptr->lev))) return;
+ }
+ }
+ else
+ {
+ msg_print("Your sanity is shaken by reading the Necronomicon!");
+ }
+ if (randint(power) < p_ptr->skill_sav) /* Mind blast */
+ {
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ if ((!p_ptr->resist_chaos) && (randint(3) == 1))
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+ return;
+ }
+
+ if (randint(power) < p_ptr->skill_sav) /* Lose int & wis */
+ {
+ do_dec_stat (A_INT, STAT_DEC_NORMAL);
+ do_dec_stat (A_WIS, STAT_DEC_NORMAL);
+ return;
+ }
+
+
+ if (randint(power) < p_ptr->skill_sav) /* Brain smash */
+ {
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(4) + 4);
+ }
+ if (!p_ptr->free_act)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4);
+ }
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ while (rand_int(100) > p_ptr->skill_sav)
+ (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ if (!p_ptr->resist_chaos)
+ {
+ (void) set_image(p_ptr->image + rand_int(250) + 150);
+ }
+ return;
+ }
+
+ if (randint(power) < p_ptr->skill_sav) /* Permanent lose int & wis */
+ {
+ if (dec_stat(A_INT, 10, TRUE)) happened = TRUE;
+ if (dec_stat(A_WIS, 10, TRUE)) happened = TRUE;
+ if (happened)
+ msg_print("You feel much less sane than before.");
+ return;
+ }
+
+
+ if (randint(power) < p_ptr->skill_sav) /* Amnesia */
+ {
+
+ if (lose_all_info())
+ msg_print("You forget everything in your utmost terror!");
+ return;
+ }
+
+ p_ptr->update |= PU_BONUS;
+ handle_stuff();
+}
+
+
+/*
+ * This function updates the monster record of the given monster
+ *
+ * This involves extracting the distance to the player, checking
+ * for visibility (natural, infravision, see-invis, telepathy),
+ * updating the monster visibility flag, redrawing or erasing the
+ * monster when the visibility changes, and taking note of any
+ * "visual" features of the monster (cold-blooded, invisible, etc).
+ *
+ * The only monster fields that are changed here are "cdis" (the
+ * distance from the player), "los" (clearly visible to player),
+ * and "ml" (visible to the player in any way).
+ *
+ * There are a few cases where the calling routine knows that the
+ * distance from the player to the monster has not changed, and so
+ * we have a special parameter "full" to request distance computation.
+ * This lets many calls to this function run very quickly.
+ *
+ * Note that every time a monster moves, we must call this function
+ * for that monster, and update distance. Note that every time the
+ * player moves, we must call this function for every monster, and
+ * update distance. Note that every time the player "state" changes
+ * in certain ways (including "blindness", "infravision", "telepathy",
+ * and "see invisible"), we must call this function for every monster.
+ *
+ * The routines that actually move the monsters call this routine
+ * directly, and the ones that move the player, or notice changes
+ * in the player state, call "update_monsters()".
+ *
+ * Routines that change the "illumination" of grids must also call
+ * this function, since the "visibility" of some monsters may be
+ * based on the illumination of their grid.
+ *
+ * Note that this function is called once per monster every time the
+ * player moves, so it is important to optimize it for monsters which
+ * are far away. Note the optimization which skips monsters which
+ * are far away and were completely invisible last turn.
+ *
+ * Note the optimized "inline" version of the "distance()" function.
+ *
+ * Note that only monsters on the current panel can be "visible",
+ * and then only if they are (1) in line of sight and illuminated
+ * by light or infravision, or (2) nearby and detected by telepathy.
+ *
+ * The player can choose to be disturbed by several things, including
+ * "disturb_move" (monster which is viewable moves in some way), and
+ * "disturb_near" (monster which is "easily" viewable moves in some
+ * way). Note that "moves" includes "appears" and "disappears".
+ *
+ * Note the new "xtra" field which encodes several state flags such
+ * as "detected last turn", and "detected this turn", and "currently
+ * in line of sight", all of which are used for visibility testing.
+ */
+void update_mon(int m_idx, bool full)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* The current monster location */
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ bool old_ml = m_ptr->ml;
+
+ /* Seen at all */
+ bool flag = FALSE;
+
+ /* Seen by vision */
+ bool easy = FALSE;
+
+ /* Seen by telepathy */
+ bool hard = FALSE;
+
+ /* Various extra flags */
+ bool do_empty_mind = FALSE;
+ bool do_weird_mind = FALSE;
+ bool do_invisible = FALSE;
+ bool do_cold_blood = FALSE;
+
+
+ /* Calculate distance */
+ if (full)
+ {
+ int d, dy, dx;
+
+ /* Distance components */
+ dy = (p_ptr->py > fy) ? (p_ptr->py - fy) : (fy - p_ptr->py);
+ dx = (p_ptr->px > fx) ? (p_ptr->px - fx) : (fx - p_ptr->px);
+
+ /* Approximate distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+
+ /* Save the distance (in a byte) */
+ m_ptr->cdis = (d < 255) ? d : 255;
+ }
+
+
+#if 0
+
+ /* Process monster light for a monster within MAX_SIGHT + max radius */
+ if (m_ptr->cdis <= MAX_SIGHT + 1)
+ {
+ if (r_ptr->flags9 & RF9_HAS_LITE)
+ {
+ p_ptr->update |= (PU_MON_LITE);
+ }
+ }
+
+#endif
+
+ /* Process "distant" monsters */
+ if (m_ptr->cdis > MAX_SIGHT)
+ {
+ /* Ignore unseen monsters */
+ if (!m_ptr->ml) return;
+
+ /* Detected */
+ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;
+ }
+
+
+ /* Process "nearby" monsters on the current "panel" */
+ else if (panel_contains(fy, fx))
+ {
+ /* Normal line of sight, and player is not blind */
+ if (player_has_los_bold(fy, fx) && !p_ptr->blind)
+ {
+ /* Use "infravision" */
+ if (m_ptr->cdis <= (byte)(p_ptr->see_infra))
+ {
+ /* Infravision only works on "warm" creatures */
+ /* Below, we will need to know that infravision failed */
+ if (r_ptr->flags2 & (RF2_COLD_BLOOD)) do_cold_blood = TRUE;
+
+ /* Infravision works */
+ if (!do_cold_blood) easy = flag = TRUE;
+ }
+
+ /* Use "illumination" */
+ if (player_can_see_bold(fy, fx))
+ {
+ /* Take note of invisibility */
+ if (r_ptr->flags2 & (RF2_INVISIBLE)) do_invisible = TRUE;
+
+ /* Visible, or detectable, monsters get seen */
+ if (!do_invisible || p_ptr->see_inv) easy = flag = TRUE;
+ }
+ }
+
+ /* Telepathy can see all "nearby" monsters with "minds" */
+ if (p_ptr->telepathy)
+ {
+ /* Assume we cant see */
+ bool can_esp = FALSE;
+
+ /* Different ESP */
+ if ((p_ptr->telepathy & ESP_ORC) && (r_ptr->flags3 & RF3_ORC)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_SPIDER) && (r_ptr->flags7 & RF7_SPIDER)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_TROLL) && (r_ptr->flags3 & RF3_TROLL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_DRAGON) && (r_ptr->flags3 & RF3_DRAGON)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_GIANT) && (r_ptr->flags3 & RF3_GIANT)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_DEMON) && (r_ptr->flags3 & RF3_DEMON)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_UNDEAD) && (r_ptr->flags3 & RF3_UNDEAD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_EVIL) && (r_ptr->flags3 & RF3_EVIL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_ANIMAL) && (r_ptr->flags3 & RF3_ANIMAL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_THUNDERLORD) && (r_ptr->flags3 & RF3_THUNDERLORD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_GOOD) && (r_ptr->flags3 & RF3_GOOD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_NONLIVING) && (r_ptr->flags3 & RF3_NONLIVING)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_UNIQUE) && ((r_ptr->flags1 & RF1_UNIQUE) || (r_ptr->flags3 & RF3_UNIQUE_4))) can_esp = TRUE;
+ if (p_ptr->telepathy & ESP_ALL) can_esp = TRUE;
+
+ /* Only do this when we can really detect monster */
+ if (can_esp)
+ {
+ /* Empty mind, no telepathy */
+ if (r_ptr->flags2 & (RF2_EMPTY_MIND))
+ {
+ do_empty_mind = TRUE;
+ }
+
+ /* Weird mind, occasional telepathy */
+ else if (r_ptr->flags2 & (RF2_WEIRD_MIND))
+ {
+ do_weird_mind = TRUE;
+ if (rand_int(100) < 10)
+ {
+ hard = TRUE;
+ flag = TRUE;
+ }
+ }
+
+ /* Normal mind, allow telepathy */
+ else
+ {
+ hard = TRUE;
+ flag = TRUE;
+ }
+ }
+ }
+
+ /* Merchants sense objects */
+#if 0 /* DGDGDGDG -- use a skill */
+ if ((cp_ptr->magic_key == MKEY_TELEKINESIS) && (p_ptr->lev >= 20) &&
+ (m_ptr->hold_o_idx)) flag = TRUE;
+#endif
+ /* Apply "detection" spells */
+ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;
+
+ /* Hack -- Wizards have "perfect telepathy" */
+ if (wizard) flag = TRUE;
+ }
+
+
+ /* The monster is now visible */
+ if (flag)
+ {
+ /* It was previously unseen */
+ if (!m_ptr->ml)
+ {
+ /* Mark as visible */
+ m_ptr->ml = TRUE;
+
+ /* Draw the monster */
+ lite_spot(fy, fx);
+
+ /* Update health bar as needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Hack -- Count "fresh" sightings */
+ if (r_ptr->r_sights < MAX_SHORT) r_ptr->r_sights++;
+
+ /* Disturb on appearance */
+ if (disturb_move)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+ }
+
+ /* Apply telepathy */
+ if (hard)
+ {
+ /* Hack -- Memorize mental flags */
+ if (r_ptr->flags2 & (RF2_SMART)) r_ptr->r_flags2 |= (RF2_SMART);
+ if (r_ptr->flags2 & (RF2_STUPID)) r_ptr->r_flags2 |= (RF2_STUPID);
+ }
+
+ /* Memorize various observable flags */
+ if (do_empty_mind) r_ptr->r_flags2 |= (RF2_EMPTY_MIND);
+ if (do_weird_mind) r_ptr->r_flags2 |= (RF2_WEIRD_MIND);
+ if (do_cold_blood) r_ptr->r_flags2 |= (RF2_COLD_BLOOD);
+ if (do_invisible) r_ptr->r_flags2 |= (RF2_INVISIBLE);
+ }
+
+ /* The monster is not visible */
+ else
+ {
+ /* It was previously seen */
+ if (m_ptr->ml)
+ {
+ /* Mark as not visible */
+ m_ptr->ml = FALSE;
+
+ /* Erase the monster */
+ lite_spot(fy, fx);
+
+ /* Update health bar as needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Disturb on disappearance*/
+ if (disturb_move)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+ }
+ }
+
+
+ /* The monster is now easily visible */
+ if (easy)
+ {
+
+ if (m_ptr->ml != old_ml)
+ {
+ if (r_ptr->flags2 & RF2_ELDRITCH_HORROR)
+ {
+ sanity_blast(m_ptr, FALSE);
+ }
+ }
+
+ /* Change */
+ if (!(m_ptr->mflag & (MFLAG_VIEW)))
+ {
+ /* Mark as easily visible */
+ m_ptr->mflag |= (MFLAG_VIEW);
+
+ /* Disturb on appearance */
+ if (disturb_near)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+
+ }
+ }
+
+ /* The monster is not easily visible */
+ else
+ {
+ /* Change */
+ if (m_ptr->mflag & (MFLAG_VIEW))
+ {
+ /* Mark as not easily visible */
+ m_ptr->mflag &= ~(MFLAG_VIEW);
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Disturb on disappearance */
+ if (disturb_near)
+ {
+ if (disturb_pets || (is_friend(m_ptr) <= 0)) disturb(1, 0);
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * This function simply updates all the (non-dead) monsters (see above).
+ */
+void update_monsters(bool full)
+{
+ int i;
+
+ /* Update each (live) monster */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Update the monster */
+ update_mon(i, full);
+ }
+}
+
+
+void monster_carry(monster_type *m_ptr, int m_idx, object_type *q_ptr)
+{
+ object_type *o_ptr;
+
+ /* Get new object */
+ int o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_ptr->hold_o_idx = o_idx;
+ }
+
+ else
+ {
+ /* Hack -- Preserve artifacts */
+ if (q_ptr->name1)
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ else if (k_info[q_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[q_ptr->k_idx].artifact = 0;
+ }
+ else if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ }
+}
+
+
+static int possible_randart[] =
+{
+ TV_MSTAFF,
+ TV_BOOMERANG,
+ TV_DIGGING,
+ TV_HAFTED,
+ TV_POLEARM,
+ TV_AXE,
+ TV_SWORD,
+ TV_BOOTS,
+ TV_GLOVES,
+ TV_HELM,
+ TV_CROWN,
+ TV_SHIELD,
+ TV_CLOAK,
+ TV_SOFT_ARMOR,
+ TV_HARD_ARMOR,
+ TV_LITE,
+ TV_AMULET,
+ TV_RING,
+ -1,
+};
+
+
+bool kind_is_randart(int k_idx)
+{
+ int max;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (!kind_is_legal(k_idx)) return (FALSE);
+
+ for (max = 0; possible_randart[max] != -1; max++)
+ {
+ if (k_ptr->tval == possible_randart[max]) return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * Attempt to place a monster of the given race at the given location.
+ *
+ * To give the player a sporting chance, any monster that appears in
+ * line-of-sight and is extremely dangerous can be marked as
+ * "FORCE_SLEEP", which will cause them to be placed with low energy,
+ * which often (but not always) lets the player move before they do.
+ *
+ * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
+ *
+ * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
+ * remove old "cur_num" and "max_num" fields.
+ *
+ * XXX XXX XXX Actually, do something similar for artifacts, to simplify
+ * the "preserve" mode, and to make the "what artifacts" flag more useful.
+ *
+ * This is the only function which may place a monster in the dungeon,
+ * except for the savefile loading code.
+ */
+bool bypass_r_ptr_max_num = FALSE;
+int place_monster_result = 0;
+bool place_monster_one_no_drop = FALSE;
+monster_race *place_monster_one_race = NULL;
+s16b place_monster_one(int y, int x, int r_idx, int ego, bool slp, int status)
+{
+ int i, base;
+ char dummy[5];
+ bool add_level = FALSE;
+ int min_level = 0, max_level = 0;
+
+ cave_type *c_ptr;
+
+ monster_type *m_ptr;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+ cptr name = (r_name + r_ptr->name);
+
+ /* Grab the special race if needed */
+ if (place_monster_one_race)
+ {
+ r_ptr = place_monster_one_race;
+ }
+
+ /* DO NOT PLACE A MONSTER IN THE SMALL SCALE WILDERNESS !!! */
+ if (p_ptr->wild_mode)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Verify location */
+ if (!in_bounds(y, x))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Require empty space */
+ if (!cave_empty_bold(y, x))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): EMPTY BOLD", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Require no monster free grid, or special permission */
+ if ((cave[y][x].info & CAVE_FREE) && (!m_allow_special[r_idx]))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): CAVE_FREE", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Hack -- no creation on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Nor on the altars */
+ if ((cave[y][x].feat >= FEAT_ALTAR_HEAD)
+ && (cave[y][x].feat <= FEAT_ALTAR_TAIL))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START)
+ && (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Paranoia */
+ if (!r_idx)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Paranoia */
+ if (!r_ptr->name)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Are we allowed to continue ? */
+ if (process_hooks(HOOK_NEW_MONSTER, "(d)", r_idx))
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Ego Uniques are NOT to be created */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && ego)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Now could we generate an Ego Monster */
+ /* Grab the special race if needed */
+ if (place_monster_one_race)
+ {
+ r_ptr = place_monster_one_race;
+ }
+ else
+ {
+ r_ptr = race_info_idx(r_idx, ego);
+ }
+
+ if (!monster_can_cross_terrain(cave[y][x].feat, r_ptr))
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: cannot cross terrain");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Unallow some uniques to be generated outside of their quests/special levels/dungeons */
+ if ((r_ptr->flags9 & RF9_SPECIAL_GENE) && (!m_allow_special[r_idx]))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): SPECIAL_GENE", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Disallow Spirits in The Void, now this *IS* an ugly hack, I hate to do it ... */
+ if ((r_ptr->flags7 & RF7_SPIRIT) && (dungeon_type != DUNGEON_VOID))
+ {
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster(%d): SPIRIT in non VOID", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Fully forbid it */
+ if (r_ptr->flags9 & RF9_NEVER_GENE)
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: NEVER_GENE");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Hack -- "unique" monsters must be "unique" */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->max_num == -1) && (!m_allow_special[r_idx]))
+ {
+ /* Cannot create */
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster %d: unique not unique", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* The monster is already on an unique level */
+ if (r_ptr->on_saved)
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: unique already on saved level");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Anyway that doesn't work .... hum... TO FIX -- DG */
+#if 0
+ /* Hack -- non "town" monsters are NEVER generated in town */
+ if ((!(r_ptr->flags8 & (RF8_WILD_TOWN))) && (wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].terrain_idx == TERRAIN_TOWN) && !dun_level)
+ {
+ /* Cannot create */
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return (0);
+ }
+#endif
+ /* Hack -- "unique" monsters must be "unique" */
+#if 0
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->cur_num >= r_ptr->max_num) && (!m_allow_special[r_idx]))
+#else
+#if 0
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->cur_num >= r_ptr->max_num) && (!m_allow_special[r_idx]) && (r_ptr->max_num != -1))
+#else
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->cur_num >= r_ptr->max_num) && (r_ptr->max_num != -1) && (!bypass_r_ptr_max_num))
+#endif
+#endif
+ {
+ /* Cannot create */
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster %d: cur_num >= max_num", r_idx);
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Depth monsters may NOT be created out of depth */
+ if ((r_ptr->flags1 & (RF1_FORCE_DEPTH)) && (dun_level < r_ptr->level))
+ {
+ /* Cannot create */
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: FORCE_DEPTH");
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+ /* Powerful monster */
+ if (r_ptr->level > dun_level)
+ {
+ /* Unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Message for cheaters */
+ if ((cheat_hear) || (p_ptr->precognition)) msg_format("Deep Unique (%s).", name);
+
+ /* Boost rating by twice delta-depth */
+ rating += (r_ptr->level - dun_level) * 2;
+ }
+
+ /* Normal monsters */
+ else
+ {
+ /* Message for cheaters */
+ if ((cheat_hear) || (p_ptr->precognition)) msg_format("Deep Monster (%s).", name);
+
+ /* Boost rating by delta-depth */
+ rating += (r_ptr->level - dun_level);
+ }
+ }
+
+ /* Note the monster */
+ else if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Unique monsters induce message */
+ if ((cheat_hear) || (p_ptr->precognition)) msg_format("Unique (%s).", name);
+ }
+
+
+ /* Access the location */
+ c_ptr = &cave[y][x];
+
+ /* Make a new monster */
+ c_ptr->m_idx = m_pop();
+ hack_m_idx_ii = c_ptr->m_idx;
+
+ /* Mega-Hack -- catch "failure" */
+ if (!c_ptr->m_idx)
+ {
+ if (place_monster_one_race) KILL(place_monster_one_race, monster_race);
+ return 0;
+ }
+
+
+ /* Get a new monster record */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Save the race */
+ m_ptr->r_idx = r_idx;
+ m_ptr->ego = ego;
+
+ /* No special, no mind */
+ m_ptr->sr_ptr = place_monster_one_race;
+ m_ptr->mind = NULL;
+
+ /* Place the monster at the location */
+ m_ptr->fy = y;
+ m_ptr->fx = x;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Unknown distance */
+ m_ptr->cdis = 0;
+
+ /* No flags */
+ m_ptr->mflag = 0;
+
+ /* Not visible */
+ m_ptr->ml = FALSE;
+
+ /* No objects yet */
+ m_ptr->hold_o_idx = 0;
+
+ m_ptr->status = status;
+
+ /* Friendly? */
+ if (m_ptr->status < MSTATUS_FRIEND && r_ptr->flags7 & RF7_PET)
+ {
+ m_ptr->status = MSTATUS_FRIEND;
+ }
+ if (m_ptr->status < MSTATUS_NEUTRAL && r_ptr->flags7 & RF7_NEUTRAL)
+ {
+ m_ptr->status = MSTATUS_NEUTRAL;
+ }
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Enforce sleeping if needed */
+ if (slp && r_ptr->sleep)
+ {
+ int val = r_ptr->sleep;
+ m_ptr->csleep = ((val * 2) + randint(val * 10));
+ }
+
+ /* Generate the monster's inventory(if any) */
+ /* Only if not fated to die */
+ if ((dungeon_type != DUNGEON_DEATH) && (!place_monster_one_no_drop))
+ {
+ bool good = (r_ptr->flags1 & (RF1_DROP_GOOD)) ? TRUE : FALSE;
+ bool great = (r_ptr->flags1 & (RF1_DROP_GREAT)) ? TRUE : FALSE;
+
+ bool do_gold = (!(r_ptr->flags1 & (RF1_ONLY_ITEM)));
+ bool do_item = (!(r_ptr->flags1 & (RF1_ONLY_GOLD)));
+ bool do_mimic = (r_ptr->flags9 & (RF9_MIMIC));
+ int j;
+
+ int force_coin = get_coin_type(r_ptr);
+
+ int dump_item = 0;
+ int dump_gold = 0;
+ object_type forge;
+ object_type *q_ptr;
+
+ int number = 0;
+
+ /* Average dungeon and monster levels */
+ object_level = (dun_level + r_ptr->level) / 2;
+
+ /* Determine how much we can drop */
+ if ((r_ptr->flags1 & (RF1_DROP_60)) && (rand_int(100) < 60)) number++;
+ if ((r_ptr->flags1 & (RF1_DROP_90)) && (rand_int(100) < 90)) number++;
+ if (r_ptr->flags1 & (RF1_DROP_1D2)) number += damroll(1, 2);
+ if (r_ptr->flags1 & (RF1_DROP_2D2)) number += damroll(2, 2);
+ if (r_ptr->flags1 & (RF1_DROP_3D2)) number += damroll(3, 2);
+ if (r_ptr->flags1 & (RF1_DROP_4D2)) number += damroll(4, 2);
+ if (r_ptr->flags9 & (RF9_MIMIC)) number = 1;
+
+ /* Hack -- handle creeping coins */
+ coin_type = force_coin;
+
+ if (r_ptr->flags7 & RF7_DROP_RANDART)
+ {
+ int tries = 1000;
+ obj_theme theme;
+ int i;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ theme.treasure = 101;
+ theme.combat = 101;
+ theme.magic = 101;
+ theme.tools = 101;
+
+ init_match_theme(theme);
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ i = 0;
+ while (tries)
+ {
+ tries--;
+ i = get_obj_num(dun_level);
+ if (!i) continue;
+
+ if (!kind_is_randart(i)) continue;
+ break;
+ }
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ if (tries)
+ {
+ object_prep(q_ptr, i);
+ create_artifact(q_ptr, FALSE, TRUE);
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ monster_carry(m_ptr, c_ptr->m_idx, q_ptr);
+ }
+ }
+
+ /* Drop some objects */
+ for (j = 0; j < number; j++)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make Gold */
+ if ((!do_mimic) && do_gold && (!do_item || (rand_int(100) < 50)))
+ {
+ /* Make some gold */
+ if (!make_gold(q_ptr)) continue;
+
+ /* XXX XXX XXX */
+ dump_gold++;
+ }
+
+ /* Make Object */
+ else
+ {
+ /* Make an object */
+ if (!do_mimic)
+ {
+ if (!make_object(q_ptr, good, great, r_ptr->drops)) continue;
+ }
+ else
+ {
+ /* Try hard for mimics */
+ int tries = 1000;
+
+ while (tries--)
+ {
+ if (make_object(q_ptr, good, great, r_ptr->drops)) break;
+ }
+ /* BAD */
+ if (!tries) continue;
+ }
+
+ /* XXX XXX XXX */
+ dump_item++;
+ }
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+ monster_carry(m_ptr, c_ptr->m_idx, q_ptr);
+ }
+
+ /* Reset the object level */
+ object_level = dun_level;
+
+ /* Reset "coin" type */
+ coin_type = 0;
+ }
+ place_monster_one_no_drop = FALSE;
+
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = MONSTER_EXP(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ /* Hack -- small racial variety */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ /* Allow some small variation per monster */
+ i = extract_energy[m_ptr->speed] / 10;
+ if (i) m_ptr->mspeed += rand_spread(0, i);
+ }
+
+ /* Need to match dungeon level ? */
+ base = dun_level;
+ if (dungeon_flags2 & DF2_ADJUST_LEVEL_PLAYER)
+ base = p_ptr->lev * 2;
+
+ if (dungeon_flags2 & DF2_ADJUST_LEVEL_1_2)
+ {
+ min_level = max_level = dun_level / 2;
+ add_level = TRUE;
+ }
+ if (dungeon_flags1 & DF1_ADJUST_LEVEL_1)
+ {
+ if (!min_level) min_level = dun_level;
+ max_level = dun_level;
+ add_level = TRUE;
+ }
+ if (dungeon_flags1 & DF1_ADJUST_LEVEL_2)
+ {
+ if (!min_level) min_level = dun_level * 2;
+ max_level = dun_level * 2;
+ add_level = TRUE;
+ }
+ if (add_level) monster_set_level(c_ptr->m_idx, rand_range(min_level, max_level));
+
+ /* Give a random starting energy */
+ m_ptr->energy = (byte)rand_int(100);
+
+ /* Force monster to wait for player */
+ if (r_ptr->flags1 & (RF1_FORCE_SLEEP))
+ {
+ /* Monster is still being nice */
+ m_ptr->mflag |= (MFLAG_NICE);
+
+ /* Must repair monsters */
+ repair_monsters = TRUE;
+ }
+
+ /* Hack -- see "process_monsters()" */
+ if (c_ptr->m_idx < hack_m_idx)
+ {
+ /* Monster is still being born */
+ m_ptr->mflag |= (MFLAG_BORN);
+ }
+
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, TRUE);
+
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- we need to modify the REAL r_info, not the fake one */
+ r_ptr = &r_info[r_idx];
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+
+ /* Unique monsters on saved levels should be "marked" */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && get_dungeon_save(dummy))
+ {
+ r_ptr->on_saved = TRUE;
+ }
+
+ place_monster_one_race = NULL;
+
+ /* Success */
+ place_monster_result = c_ptr->m_idx;
+ return c_ptr->m_idx;
+}
+
+/*
+ * Maximum size of a group of monsters
+ */
+#define GROUP_MAX 32
+
+
+/*
+ * Attempt to place a "group" of monsters around the given location
+ */
+static bool place_monster_group(int y, int x, int r_idx, bool slp, int status)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int old, n, i;
+ int total = 0, extra = 0;
+
+ int hack_n = 0;
+
+ byte hack_y[GROUP_MAX];
+ byte hack_x[GROUP_MAX];
+
+
+ /* Pick a group size */
+ total = randint(13);
+
+ /* Hard monsters, small groups */
+ if (r_ptr->level > dun_level)
+ {
+ extra = r_ptr->level - dun_level;
+ extra = 0 - randint(extra);
+ }
+
+ /* Easy monsters, large groups */
+ else if (r_ptr->level < dun_level)
+ {
+ extra = dun_level - r_ptr->level;
+ extra = randint(extra);
+ }
+
+ /* Hack -- limit group reduction */
+ if (extra > 12) extra = 12;
+
+ /* Modify the group size */
+ total += extra;
+
+ /* Minimum size */
+ if (total < 1) total = 1;
+
+ /* Maximum size */
+ if (total > GROUP_MAX) total = GROUP_MAX;
+
+
+ /* Save the rating */
+ old = rating;
+
+ /* Start on the monster */
+ hack_n = 1;
+ hack_x[0] = x;
+ hack_y[0] = y;
+
+ /* Puddle monsters, breadth first, up to total */
+ for (n = 0; (n < hack_n) && (hack_n < total); n++)
+ {
+ /* Grab the location */
+ int hx = hack_x[n];
+ int hy = hack_y[n];
+
+ /* Check each direction, up to total */
+ for (i = 0; (i < 8) && (hack_n < total); i++)
+ {
+ int mx = hx + ddx_ddd[i];
+ int my = hy + ddy_ddd[i];
+
+ /* Walls and Monsters block flow */
+ if (!cave_empty_bold(my, mx)) continue;
+
+ /* Attempt to place another monster */
+ if (place_monster_one(my, mx, r_idx, pick_ego_monster(r_idx), slp, status))
+ {
+ /* Add it to the "hack" set */
+ hack_y[hack_n] = my;
+ hack_x[hack_n] = mx;
+ hack_n++;
+ }
+ }
+ }
+
+ /* Hack -- restore the rating */
+ rating = old;
+
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- help pick an escort type
+ */
+static int place_monster_idx = 0;
+
+/*
+ * Hack -- help pick an escort type
+ */
+static bool place_monster_okay(int r_idx)
+{
+ monster_race *r_ptr = &r_info[place_monster_idx];
+
+ monster_race *z_ptr = &r_info[r_idx];
+
+ /* Hack - Escorts have to have the same dungeon flag */
+ if (monster_dungeon(place_monster_idx) != monster_dungeon(r_idx)) return (FALSE);
+
+ /* Require similar "race" */
+ if (z_ptr->d_char != r_ptr->d_char) return (FALSE);
+
+ /* Skip more advanced monsters */
+ if (z_ptr->level > r_ptr->level) return (FALSE);
+
+ /* Skip unique monsters */
+ if (z_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Paranoia -- Skip identical monsters */
+ if (place_monster_idx == r_idx) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Attempt to place a monster of the given race at the given location
+ *
+ * Note that certain monsters are now marked as requiring "friends".
+ * These monsters, if successfully placed, and if the "grp" parameter
+ * is TRUE, will be surrounded by a "group" of identical monsters.
+ *
+ * Note that certain monsters are now marked as requiring an "escort",
+ * which is a collection of monsters with similar "race" but lower level.
+ *
+ * Some monsters induce a fake "group" flag on their escorts.
+ *
+ * Note the "bizarre" use of non-recursion to prevent annoying output
+ * when running a code profiler.
+ *
+ * Note the use of the new "monster allocation table" code to restrict
+ * the "get_mon_num()" function to "legal" escort types.
+ */
+bool place_monster_aux(int y, int x, int r_idx, bool slp, bool grp, int status)
+{
+ int i;
+ monster_race *r_ptr = &r_info[r_idx];
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+
+ /* Place one monster, or fail */
+ if (!place_monster_one(y, x, r_idx, pick_ego_monster(r_idx), slp, status)) return (FALSE);
+
+
+ /* Require the "group" flag */
+ if (!grp) return (TRUE);
+
+
+ /* Friends for certain monsters */
+ if (r_ptr->flags1 & (RF1_FRIENDS))
+ {
+ /* Attempt to place a group */
+ (void)place_monster_group(y, x, r_idx, slp, status);
+ }
+
+
+ /* Escorts for certain monsters */
+ if (r_ptr->flags1 & (RF1_ESCORT))
+ {
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Set the escort index */
+ place_monster_idx = r_idx;
+
+ /* Set the escort hook */
+ get_mon_num_hook = place_monster_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Try to place several "escorts" */
+ for (i = 0; i < 50; i++)
+ {
+ int nx, ny, z, d = 3;
+
+ /* Pick a location */
+ scatter(&ny, &nx, y, x, d, 0);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ set_mon_num2_hook(ny, nx);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a random race */
+ z = get_mon_num(r_ptr->level);
+
+ /* Reset restriction */
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!z) break;
+
+ /* Place a single escort */
+ place_monster_one(ny, nx, z, pick_ego_monster(z), slp, status);
+
+ /* Place a "group" of escorts if needed */
+ if ((r_info[z].flags1 & (RF1_FRIENDS)) ||
+ (r_ptr->flags1 & (RF1_ESCORTS)))
+ {
+ /* Place a group of monsters */
+ (void)place_monster_group(ny, nx, z, slp, status);
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- attempt to place a monster at the given location
+ *
+ * Attempt to find a monster appropriate to the "monster_level"
+ */
+bool place_monster(int y, int x, bool slp, bool grp)
+{
+ int r_idx;
+
+ /* Set monster restriction */
+ set_mon_num2_hook(y, x);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster */
+ r_idx = get_mon_num(monster_level);
+
+ /* Reset restriction */
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+ /* Attempt to place the monster */
+ if (place_monster_aux(y, x, r_idx, slp, grp, MSTATUS_ENEMY)) return (TRUE);
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+#ifdef MONSTER_HORDES
+
+bool alloc_horde(int y, int x)
+{
+ int r_idx = 0;
+ monster_race * r_ptr = NULL;
+ monster_type * m_ptr;
+ int attempts = 1000;
+
+ set_mon_num2_hook(y, x);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ while (--attempts)
+ {
+ /* Pick a monster */
+ r_idx = get_mon_num(monster_level);
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+ r_ptr = &r_info[r_idx];
+
+ if (!(r_ptr->flags1 & (RF1_UNIQUE))
+ && !(r_ptr->flags1 & (RF1_ESCORTS)))
+ break;
+ }
+
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ if (attempts < 1) return FALSE;
+
+ attempts = 1000;
+
+ while (--attempts)
+ {
+ /* Attempt to place the monster */
+ if (place_monster_aux(y, x, r_idx, FALSE, FALSE, MSTATUS_ENEMY)) break;
+ }
+
+ if (attempts < 1) return FALSE;
+
+
+ m_ptr = &m_list[hack_m_idx_ii];
+
+ summon_kin_type = r_ptr->d_char;
+
+ for (attempts = randint(10) + 5; attempts; attempts--)
+ {
+ (void) summon_specific(m_ptr->fy, m_ptr->fx, dun_level, SUMMON_KIN);
+ }
+
+ return TRUE;
+}
+
+#endif /* MONSTER_HORDES */
+
+/*
+ * Attempt to allocate a random monster in the dungeon.
+ *
+ * Place the monster at least "dis" distance from the player.
+ *
+ * Use "slp" to choose the initial "sleep" status
+ *
+ * Use "monster_level" for the monster level
+ */
+bool alloc_monster(int dis, bool slp)
+{
+ int y, x;
+ int attempts_left = 10000;
+
+ /* Find a legal, distant, unoccupied, space */
+ while (attempts_left--)
+ {
+ /* Pick a location */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require empty floor grid (was "naked") */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Accept far away grids */
+ if (distance(y, x, p_ptr->py, p_ptr->px) > dis) break;
+ }
+
+ if (!attempts_left)
+ {
+ if (cheat_xtra || cheat_hear)
+ {
+ msg_print("Warning! Could not allocate a new monster. Small level?");
+ }
+
+ return (FALSE);
+ }
+
+
+#ifdef MONSTER_HORDES
+ if (randint(5000) <= dun_level)
+ {
+ if (alloc_horde(y, x))
+ {
+ if ((cheat_hear) || (p_ptr->precognition)) msg_print("Monster horde.");
+ return (TRUE);
+ }
+ }
+ else
+ {
+#endif /* MONSTER_HORDES */
+
+ /* Attempt to place the monster, allow groups */
+ if (place_monster(y, x, slp, TRUE)) return (TRUE);
+
+#ifdef MONSTER_HORDES
+ }
+#endif /* MONSTER_HORDES */
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Hack -- the "type" of the current "summon specific"
+ */
+static int summon_specific_type = 0;
+
+
+/*
+ * Hack -- help decide if a monster race is "okay" to summon
+ */
+bool summon_specific_okay(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ bool okay = FALSE;
+
+ /* Hack - Only summon dungeon monsters */
+ if (!monster_dungeon(r_idx)) return (FALSE);
+
+ /* Hack -- no specific type specified */
+ if (!summon_specific_type) return (TRUE);
+
+ /* Check our requirements */
+ switch (summon_specific_type)
+ {
+ case SUMMON_ANT:
+ {
+ okay = ((r_ptr->d_char == 'a') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_SPIDER:
+ {
+ okay = ((r_ptr->d_char == 'S') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HOUND:
+ {
+ okay = (((r_ptr->d_char == 'C') || (r_ptr->d_char == 'Z')) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HYDRA:
+ {
+ okay = ((r_ptr->d_char == 'M') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ANGEL:
+ {
+ okay = ((r_ptr->d_char == 'A') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_DEMON:
+ {
+ okay = ((r_ptr->flags3 & (RF3_DEMON)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_UNDEAD:
+ {
+ okay = ((r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_DRAGON:
+ {
+ okay = ((r_ptr->flags3 & (RF3_DRAGON)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_UNDEAD:
+ {
+ okay = ((r_ptr->d_char == 'L') ||
+ (r_ptr->d_char == 'V') ||
+ (r_ptr->d_char == 'W'));
+ break;
+ }
+
+ case SUMMON_HI_DRAGON:
+ {
+ okay = (r_ptr->d_char == 'D');
+ break;
+ }
+
+ case SUMMON_WRAITH:
+ {
+ okay = (r_ptr->d_char == 'W');
+ break;
+ }
+
+ case SUMMON_GHOST:
+ {
+ okay = (r_ptr->d_char == 'G');
+ break;
+ }
+
+ case SUMMON_UNIQUE:
+ {
+ okay = (r_ptr->flags1 & (RF1_UNIQUE)) ? TRUE : FALSE;
+ break;
+ }
+
+ case SUMMON_BIZARRE1:
+ {
+ okay = ((r_ptr->d_char == 'm') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+ case SUMMON_BIZARRE2:
+ {
+ okay = ((r_ptr->d_char == 'b') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+ case SUMMON_BIZARRE3:
+ {
+ okay = ((r_ptr->d_char == 'Q') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BIZARRE4:
+ {
+ okay = ((r_ptr->d_char == 'v') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BIZARRE5:
+ {
+ okay = ((r_ptr->d_char == '$') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BIZARRE6:
+ {
+ okay = (((r_ptr->d_char == '!') ||
+ (r_ptr->d_char == '?') ||
+ (r_ptr->d_char == '=') ||
+ (r_ptr->d_char == '$') ||
+ (r_ptr->d_char == '|')) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_DEMON:
+ {
+ okay = ((r_ptr->flags3 & (RF3_DEMON)) &&
+ (r_ptr->d_char == 'U') &&
+ !(r_ptr->flags1 & RF1_UNIQUE));
+ break;
+ }
+
+
+ case SUMMON_KIN:
+ {
+ okay = ((r_ptr->d_char == summon_kin_type) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_DAWN:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "the Dawn")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ANIMAL:
+ {
+ okay = ((r_ptr->flags3 & (RF3_ANIMAL)) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ANIMAL_RANGER:
+ {
+ okay = ((r_ptr->flags3 & (RF3_ANIMAL)) &&
+ (strchr("abcflqrwBCIJKMRS", r_ptr->d_char)) &&
+ !(r_ptr->flags3 & (RF3_DRAGON)) &&
+ !(r_ptr->flags3 & (RF3_EVIL)) &&
+ !(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_DEMON)) &&
+ !(r_ptr->flags4 || r_ptr->flags5 || r_ptr->flags6) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_UNDEAD_NO_UNIQUES:
+ {
+ okay = (((r_ptr->d_char == 'L') ||
+ (r_ptr->d_char == 'V') ||
+ (r_ptr->d_char == 'W')) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_HI_DRAGON_NO_UNIQUES:
+ {
+ okay = ((r_ptr->d_char == 'D') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_NO_UNIQUES:
+ {
+ okay = (!(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_PHANTOM:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "Phantom")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ELEMENTAL:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "lemental")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_THUNDERLORD:
+ {
+ okay = (r_ptr->flags3 & RF3_THUNDERLORD) ? TRUE : FALSE;
+ break;
+ }
+
+ case SUMMON_BLUE_HORROR:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "lue horror")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BUG:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "Software bug")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_RNG:
+ {
+ okay = ((strstr((r_name + r_ptr->name), "Random Number Generator")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+ case SUMMON_MINE:
+ {
+ okay = (r_ptr->flags1 & RF1_NEVER_MOVE) ? TRUE : FALSE;
+ break;
+ }
+
+ case SUMMON_HUMAN:
+ {
+ okay = ((r_ptr->d_char == 'p') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_SHADOWS:
+ {
+ okay = ((r_ptr->d_char == 'G') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_QUYLTHULG:
+ {
+ okay = ((r_ptr->d_char == 'Q') &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_LUA:
+ {
+ okay = summon_lua_okay(r_idx);
+ break;
+ }
+ }
+
+ /* Result */
+ return (okay);
+}
+
+
+/*
+ * Place a monster (of the specified "type") near the given
+ * location. Return TRUE if a monster was actually summoned.
+ *
+ * We will attempt to place the monster up to 10 times before giving up.
+ *
+ * Note: SUMMON_UNIQUE and SUMMON_WRAITH (XXX) will summon Unique's
+ * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's
+ * Note: None of the other summon codes will ever summon Unique's.
+ *
+ * This function has been changed. We now take the "monster level"
+ * of the summoning monster as a parameter, and use that, along with
+ * the current dungeon level, to help determine the level of the
+ * desired monster. Note that this is an upper bound, and also
+ * tends to "prefer" monsters of that level. Currently, we use
+ * the average of the dungeon and monster levels, and then add
+ * five to allow slight increases in monster power.
+ *
+ * Note that we use the new "monster allocation table" creation code
+ * to restrict the "get_mon_num()" function to the set of "legal"
+ * monsters, making this function much faster and more reliable.
+ *
+ * Note that this function may not succeed, though this is very rare.
+ */
+int summon_specific_level = 0;
+bool summon_specific(int y1, int x1, int lev, int type)
+{
+ int i, x, y, r_idx;
+ bool Group_ok = TRUE;
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, y1, x1, d, 0);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ /* Failure */
+ if (i == 20) return (FALSE);
+
+ /* Save the "summon" type */
+ summon_specific_type = type;
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = summon_specific_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+
+ /* Pick a monster, using the level calculation */
+ summon_hack = TRUE;
+ r_idx = get_mon_num((dun_level + lev) / 2 + 5);
+ summon_hack = FALSE;
+
+#ifdef R_IDX_TESTING_HACK
+ r_idx = 356;
+#endif
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+
+ if ((type == SUMMON_DAWN) || (type == SUMMON_BLUE_HORROR))
+ {
+ Group_ok = FALSE;
+ }
+
+ /* Attempt to place the monster (awake, allow groups) */
+ if (!place_monster_aux(y, x, r_idx, FALSE, Group_ok, MSTATUS_ENEMY)) return (FALSE);
+ if (summon_specific_level)
+ {
+ monster_set_level(place_monster_result, summon_specific_level);
+ summon_specific_level = 0;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+bool summon_specific_friendly(int y1, int x1, int lev, int type, bool Group_ok)
+{
+ int i, x, y, r_idx;
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, y1, x1, d, 0);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ /* Failure */
+ if (i == 20) return (FALSE);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Save the "summon" type */
+ summon_specific_type = type;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = summon_specific_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster, using the level calculation */
+ r_idx = get_mon_num((dun_level + lev) / 2 + 5);
+
+#ifdef R_IDX_TESTING_HACK
+ r_idx = 356;
+#endif
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+ /* Attempt to place the monster (awake, allow groups) */
+ if (!place_monster_aux(y, x, r_idx, FALSE, Group_ok, MSTATUS_PET)) return (FALSE);
+ if (summon_specific_level)
+ {
+ monster_set_level(place_monster_result, summon_specific_level);
+ summon_specific_level = 0;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Swap the players/monsters (if any) at two locations XXX XXX XXX
+ */
+void monster_swap(int y1, int x1, int y2, int x2)
+{
+ int m1, m2;
+
+ monster_type *m_ptr;
+ cave_type *c_ptr1, *c_ptr2;
+
+ c_ptr1 = &cave[y1][x1];
+ c_ptr2 = &cave[y2][x2];
+
+ /* Monsters */
+ m1 = c_ptr1->m_idx;
+ m2 = c_ptr2->m_idx;
+
+
+ /* Update grids */
+ c_ptr1->m_idx = m2;
+ c_ptr2->m_idx = m1;
+
+
+ /* Monster 1 */
+ if (m1 > 0)
+ {
+ m_ptr = &m_list[m1];
+
+ /* Move monster */
+ m_ptr->fy = y2;
+ m_ptr->fx = x2;
+
+ /* Update monster */
+ update_mon(m1, TRUE);
+ }
+
+ /* Player 1 */
+ else if (m1 < 0)
+ {
+ /* Move player */
+ p_ptr->py = y2;
+ p_ptr->px = x2;
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Window stuff */
+ /* It's probably not a good idea to recalculate the
+ * overhead view each turn.
+
+ p_ptr->window |= (PW_OVERHEAD);
+ */
+ }
+
+ /* Monster 2 */
+ if (m2 > 0)
+ {
+ m_ptr = &m_list[m2];
+
+ /* Move monster */
+ m_ptr->fy = y1;
+ m_ptr->fx = x1;
+
+ /* Update monster */
+ update_mon(m2, TRUE);
+ }
+
+ /* Player 2 */
+ else if (m2 > 0)
+ {
+ /* Move player */
+ p_ptr->py = y1;
+ p_ptr->px = x1;
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Window stuff */
+ /* It's probably not a good idea to recalculate the
+ * overhead view each turn.
+
+ p_ptr->window |= (PW_OVERHEAD);
+ */
+ }
+
+
+ /* Redraw */
+ lite_spot(y1, x1);
+ lite_spot(y2, x2);
+}
+
+
+/*
+ * Hack -- help decide if a monster race is "okay" to summon
+ */
+static bool mutate_monster_okay(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ bool okay = FALSE;
+
+ /* Hack - Only summon dungeon monsters */
+ if (!monster_dungeon(r_idx)) return (FALSE);
+
+ okay = ((r_ptr->d_char == summon_kin_type) && !(r_ptr->flags1 & (RF1_UNIQUE))
+ && (r_ptr->level >= dun_level));
+
+ return okay;
+}
+
+
+/*
+ * Let the given monster attempt to reproduce.
+ *
+ * Note that "reproduction" REQUIRES empty space.
+ */
+bool multiply_monster(int m_idx, bool charm, bool clone)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int i, y, x, new_race;
+
+ bool result = FALSE;
+
+ if (no_breeds)
+ {
+ msg_print("It tries to breed but it fails!");
+ return FALSE;
+ }
+
+ /* Try up to 18 times */
+ for (i = 0; i < 18; i++)
+ {
+ int d = 1;
+
+
+ /* Pick a location */
+ scatter(&y, &x, m_ptr->fy, m_ptr->fx, d, 0);
+
+ /* Require an "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ new_race = m_ptr->r_idx;
+
+ /* It can mutate into a nastier monster */
+ if ((rand_int(100) < 3) && (!clone))
+ {
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = mutate_monster_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ summon_kin_type = r_ptr->d_char;
+
+ /* Pick a monster, using the level calculation */
+ new_race = get_mon_num(dun_level + 5);
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+ }
+
+ /* Create a new monster (awake, no groups) */
+ result = place_monster_aux(y, x, new_race, FALSE, FALSE, (charm) ? MSTATUS_PET : MSTATUS_ENEMY);
+
+ /* Done */
+ break;
+ }
+
+ if (clone && result) m_list[hack_m_idx_ii].smart |= SM_CLONED;
+
+ /* Result */
+ return (result);
+}
+
+
+
+
+
+/*
+ * Dump a message describing a monster's reaction to damage
+ *
+ * Technically should attempt to treat "Beholder"'s as jelly's
+ */
+bool hack_message_pain_may_silent = FALSE;
+void message_pain_hook(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ if (hack_message_pain_may_silent)
+ monster_msg(buf);
+ else
+ msg_print(buf);
+}
+
+void message_pain(int m_idx, int dam)
+{
+ long oldhp, newhp, tmp;
+ int percentage;
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ char m_name[80];
+
+#if 0
+ if (!(player_can_see_bold(m_ptr->fy, m_ptr->fx)))
+ return;
+#endif
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Notice non-damage */
+ if (dam == 0)
+ {
+ message_pain_hook("%^s is unharmed.", m_name);
+ return;
+ }
+
+ /* Note -- subtle fix -CFT */
+ newhp = (long)(m_ptr->hp);
+ oldhp = newhp + (long)(dam);
+ tmp = (newhp * 100L) / oldhp;
+ percentage = (int)(tmp);
+
+
+ /* Jelly's, Mold's, Vortex's, Quthl's */
+ if (strchr("jmvQ", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s barely notices.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s flinches.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s squelches.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s quivers in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s writhes about.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s jerks limply.", m_name);
+ }
+
+ /* Dogs and Hounds */
+ else if (strchr("CZ", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s shrugs off the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s snarls with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s yelps in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s howls in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s howls in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s yelps feebly.", m_name);
+ }
+
+ /* One type of monsters (ignore,squeal,shriek) */
+ else if (strchr("FIKMRSXabclqrst", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s ignores the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s grunts with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s squeals in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s shrieks in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s shrieks in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s cries out feebly.", m_name);
+ }
+
+ /* Another type of monsters (shrug,cry,scream) */
+ else
+ {
+ if (percentage > 95)
+ message_pain_hook("%^s shrugs off the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("%^s grunts with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("%^s cries out in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("%^s screams in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("%^s screams in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("%^s writhes in agony.", m_name);
+ else
+ message_pain_hook("%^s cries out feebly.", m_name);
+ }
+}
+
+
+
+/*
+ * Learn about an "observed" resistance.
+ */
+void update_smart_learn(int m_idx, int what)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+
+ /* Not allowed to learn */
+ if (!smart_learn) return;
+
+ /* Too stupid to learn anything */
+ if (r_ptr->flags2 & (RF2_STUPID)) return;
+
+ /* Not intelligent, only learn sometimes */
+ if (!(r_ptr->flags2 & (RF2_SMART)) && (rand_int(100) < 50)) return;
+
+
+ /* XXX XXX XXX */
+
+ /* Analyze the knowledge */
+ switch (what)
+ {
+ case DRS_ACID:
+ if (p_ptr->resist_acid) m_ptr->smart |= (SM_RES_ACID);
+ if (p_ptr->oppose_acid) m_ptr->smart |= (SM_OPP_ACID);
+ if (p_ptr->immune_acid) m_ptr->smart |= (SM_IMM_ACID);
+ break;
+
+ case DRS_ELEC:
+ if (p_ptr->resist_elec) m_ptr->smart |= (SM_RES_ELEC);
+ if (p_ptr->oppose_elec) m_ptr->smart |= (SM_OPP_ELEC);
+ if (p_ptr->immune_elec) m_ptr->smart |= (SM_IMM_ELEC);
+ break;
+
+ case DRS_FIRE:
+ if (p_ptr->resist_fire) m_ptr->smart |= (SM_RES_FIRE);
+ if (p_ptr->oppose_fire) m_ptr->smart |= (SM_OPP_FIRE);
+ if (p_ptr->immune_fire) m_ptr->smart |= (SM_IMM_FIRE);
+ break;
+
+ case DRS_COLD:
+ if (p_ptr->resist_cold) m_ptr->smart |= (SM_RES_COLD);
+ if (p_ptr->oppose_cold) m_ptr->smart |= (SM_OPP_COLD);
+ if (p_ptr->immune_cold) m_ptr->smart |= (SM_IMM_COLD);
+ break;
+
+ case DRS_POIS:
+ if (p_ptr->resist_pois) m_ptr->smart |= (SM_RES_POIS);
+ if (p_ptr->oppose_pois) m_ptr->smart |= (SM_OPP_POIS);
+ break;
+
+
+ case DRS_NETH:
+ if (p_ptr->resist_neth) m_ptr->smart |= (SM_RES_NETH);
+ break;
+
+ case DRS_LITE:
+ if (p_ptr->resist_lite) m_ptr->smart |= (SM_RES_LITE);
+ break;
+
+ case DRS_DARK:
+ if (p_ptr->resist_dark) m_ptr->smart |= (SM_RES_DARK);
+ break;
+
+ case DRS_FEAR:
+ if (p_ptr->resist_fear) m_ptr->smart |= (SM_RES_FEAR);
+ break;
+
+ case DRS_CONF:
+ if (p_ptr->resist_conf) m_ptr->smart |= (SM_RES_CONF);
+ break;
+
+ case DRS_CHAOS:
+ if (p_ptr->resist_chaos) m_ptr->smart |= (SM_RES_CHAOS);
+ break;
+
+ case DRS_DISEN:
+ if (p_ptr->resist_disen) m_ptr->smart |= (SM_RES_DISEN);
+ break;
+
+ case DRS_BLIND:
+ if (p_ptr->resist_blind) m_ptr->smart |= (SM_RES_BLIND);
+ break;
+
+ case DRS_NEXUS:
+ if (p_ptr->resist_nexus) m_ptr->smart |= (SM_RES_NEXUS);
+ break;
+
+ case DRS_SOUND:
+ if (p_ptr->resist_sound) m_ptr->smart |= (SM_RES_SOUND);
+ break;
+
+ case DRS_SHARD:
+ if (p_ptr->resist_shard) m_ptr->smart |= (SM_RES_SHARD);
+ break;
+
+
+ case DRS_FREE:
+ if (p_ptr->free_act) m_ptr->smart |= (SM_IMM_FREE);
+ break;
+
+ case DRS_MANA:
+ if (!p_ptr->msp) m_ptr->smart |= (SM_IMM_MANA);
+ break;
+
+ case DRS_REFLECT:
+ if (p_ptr->reflect) m_ptr-> smart |= (SM_IMM_REFLECT);
+ break;
+ }
+}
+
+
+/*
+ * Place the player in the dungeon XXX XXX
+ */
+s16b player_place(int y, int x)
+{
+ /* Paranoia XXX XXX */
+ if (cave[y][x].m_idx != 0) return (0);
+
+ /* Save player location */
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ /* Success */
+ return ( -1);
+}
+
+/*
+ * Drop all items carried by a monster
+ */
+void monster_drop_carried_objects(monster_type *m_ptr)
+{
+ s16b this_o_idx, next_o_idx = 0;
+ object_type forge;
+ object_type *o_ptr;
+ object_type *q_ptr;
+
+
+ /* Drop objects being carried */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Paranoia */
+ o_ptr->held_m_idx = 0;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Drop it */
+ drop_near(q_ptr, -1, m_ptr->fy, m_ptr->fx);
+ }
+
+ /* Forget objects */
+ m_ptr->hold_o_idx = 0;
+}
diff --git a/src/monster3.c b/src/monster3.c
new file mode 100644
index 00000000..81c6c2d0
--- /dev/null
+++ b/src/monster3.c
@@ -0,0 +1,679 @@
+/* File: monster3.c */
+
+/* Purpose: Monsters AI */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Is the mon,ster in friendly state(pet, friend, ..)
+ * -1 = enemy, 0 = neutral, 1 = friend
+ */
+int is_friend(monster_type *m_ptr)
+{
+ switch (m_ptr->status)
+ {
+ /* pets/friends/companion attacks monsters */
+ case MSTATUS_NEUTRAL_P:
+ case MSTATUS_FRIEND:
+ case MSTATUS_PET:
+ case MSTATUS_COMPANION:
+ return 1;
+ break;
+ case MSTATUS_NEUTRAL_M:
+ case MSTATUS_ENEMY:
+ return -1;
+ break;
+ case MSTATUS_NEUTRAL:
+ return 0;
+ break;
+ }
+
+ /* OUPS */
+ return (0);
+}
+
+/* Should they attack each others */
+bool is_enemy(monster_type *m_ptr, monster_type *t_ptr)
+{
+ monster_race *r_ptr = &r_info[m_ptr->r_idx], *rt_ptr = &r_info[t_ptr->r_idx];
+ int s1 = is_friend(m_ptr), s2 = is_friend(t_ptr);
+#if 0
+ /* Stupid monsters attacks just about everything */
+ if ((r_ptr->flags2 & RF2_STUPID) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+#endif
+ /* Monsters hates breeders */
+ if ((m_ptr->status != MSTATUS_NEUTRAL) && (rt_ptr->flags4 & RF4_MULTIPLY) && (num_repro > MAX_REPRO * 2 / 3) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+ if ((t_ptr->status != MSTATUS_NEUTRAL) && (r_ptr->flags4 & RF4_MULTIPLY) && (num_repro > MAX_REPRO * 2 / 3) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+
+ /* No special conditions, lets test normal flags */
+ if (s1 && s2 && (s1 == -s2)) return TRUE;
+
+ /* Not ennemy */
+ return (FALSE);
+}
+
+bool change_side(monster_type *m_ptr)
+{
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* neutrals and companions */
+ switch (m_ptr->status)
+ {
+#if 0
+ case MSTATUS_ENEMY:
+ m_ptr->status = MSTATUS_FRIEND;
+ break;
+#endif
+ case MSTATUS_FRIEND:
+ m_ptr->status = MSTATUS_ENEMY;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, -m_ptr->level * 4);
+ break;
+ case MSTATUS_NEUTRAL_P:
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ case MSTATUS_NEUTRAL_M:
+ m_ptr->status = MSTATUS_NEUTRAL_P;
+ break;
+ case MSTATUS_PET:
+ m_ptr->status = MSTATUS_ENEMY;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, -m_ptr->level * 4);
+ break;
+ case MSTATUS_COMPANION:
+ return FALSE;
+ break;
+ }
+ /* changed */
+ return TRUE;
+}
+
+/* Multiply !! */
+bool ai_multiply(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ int k, y, x, oy = m_ptr->fy, ox = m_ptr->fx;
+ bool is_frien = (is_friend(m_ptr) > 0);
+
+ /* Count the adjacent monsters */
+ for (k = 0, y = oy - 1; y <= oy + 1; y++)
+ {
+ for (x = ox - 1; x <= ox + 1; x++)
+ {
+ if (cave[y][x].m_idx) k++;
+ }
+ }
+
+ if (is_friend(m_ptr) > 0)
+ {
+ is_frien = TRUE;
+ }
+ else
+ {
+ is_frien = FALSE;
+ }
+
+ /* Hack -- multiply slower in crowded areas */
+ if ((k < 4) && (!k || !rand_int(k * MON_MULT_ADJ)))
+ {
+ /* Try to multiply */
+ if (multiply_monster(m_idx, (is_frien), FALSE))
+ {
+ /* Take note if visible */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags4 |= (RF4_MULTIPLY);
+ }
+
+ /* Multiplying takes energy */
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* Possessor incarnates */
+bool ai_possessor(int m_idx, int o_idx)
+{
+ object_type *o_ptr = &o_list[o_idx];
+ monster_type *m_ptr = &m_list[m_idx];
+ int r_idx = m_ptr->r_idx, r2_idx = o_ptr->pval2;
+ int i;
+ monster_race *r_ptr = &r_info[r2_idx];
+ char m_name[80], m_name2[80];
+
+ monster_desc(m_name, m_ptr, 0x00);
+ monster_race_desc(m_name2, r2_idx, 0);
+
+ if (m_ptr->ml) msg_format("%^s incarnates into a %s!", m_name, m_name2);
+
+ /* Remove the old one */
+ delete_object_idx(o_idx);
+
+ m_ptr->r_idx = r2_idx;
+ m_ptr->ego = 0;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = MONSTER_EXP(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ m_ptr->energy = 0;
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+ r_info[r_idx].cur_num--;
+
+ m_ptr->possessor = r_idx;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+
+ return TRUE;
+}
+
+void ai_deincarnate(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int r2_idx = m_ptr->possessor, r_idx = m_ptr->r_idx;
+ monster_race *r_ptr = &r_info[r2_idx];
+ int i;
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0x04);
+
+ if (m_ptr->ml) msg_format("The soul of %s deincarnates!", m_name);
+
+ m_ptr->r_idx = r2_idx;
+ m_ptr->ego = 0;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = MONSTER_EXP(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ m_ptr->energy = 0;
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+ r_info[r_idx].cur_num--;
+
+ m_ptr->possessor = 0;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+}
+
+/* Returns if a new companion is allowed */
+bool can_create_companion(void)
+{
+ int i, mcnt = 0;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status == MSTATUS_COMPANION) mcnt++;
+ }
+
+ if (mcnt < (1 + get_skill_scale(SKILL_LORE, 6))) return (TRUE);
+ else return (FALSE);
+}
+
+
+/* Player controlled monsters */
+bool do_control_walk(void)
+{
+ /* Get a "repeated" direction */
+ if (p_ptr->control)
+ {
+ int dir;
+
+ if (get_rep_dir(&dir))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Actually move the monster */
+ p_ptr->control_dir = dir;
+ }
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+bool do_control_inven(void)
+{
+ int monst_list[23];
+
+ if (!p_ptr->control) return FALSE;
+ screen_save();
+ prt("Carried items", 0, 0);
+ show_monster_inven(p_ptr->control, monst_list);
+ inkey();
+ screen_load();
+ return TRUE;
+}
+
+bool do_control_pickup(void)
+{
+ int this_o_idx, next_o_idx = 0;
+ monster_type *m_ptr = &m_list[p_ptr->control];
+ cave_type *c_ptr;
+ bool done = FALSE;
+
+ if (!p_ptr->control) return FALSE;
+
+ /* Scan all objects in the grid */
+ c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Skip gold */
+ if (o_ptr->tval == TV_GOLD) continue;
+
+ /* Excise the object */
+ excise_object_idx(this_o_idx);
+
+ /* Forget mark */
+ o_ptr->marked = FALSE;
+
+ /* Forget location */
+ o_ptr->iy = o_ptr->ix = 0;
+
+ /* Memorize monster */
+ o_ptr->held_m_idx = p_ptr->control;
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_ptr->hold_o_idx;
+
+ /* Carry object */
+ m_ptr->hold_o_idx = this_o_idx;
+ done = TRUE;
+ }
+ if (done) msg_print("You pick up all objects on the floor.");
+ return TRUE;
+}
+
+bool do_control_drop(void)
+{
+ monster_type *m_ptr = &m_list[p_ptr->control];
+
+ if (!p_ptr->control) return FALSE;
+ monster_drop_carried_objects(m_ptr);
+ return TRUE;
+}
+
+bool do_control_magic(void)
+{
+ int power = -1;
+ int num = 0, i;
+ int powers[96];
+ bool flag, redraw;
+ int ask;
+ char choice;
+ char out_val[160];
+ monster_race *r_ptr = &r_info[m_list[p_ptr->control].r_idx];
+ int label;
+
+ if (!p_ptr->control) return FALSE;
+
+ if (get_check("Do you want to abandon the creature?"))
+ {
+ if (get_check("Abandon it permanently?"))
+ delete_monster_idx(p_ptr->control);
+ p_ptr->control = 0;
+ return TRUE;
+ }
+
+ /* List the monster powers -- RF4_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags4 & BIT(i))
+ {
+ if (!monster_powers[i].power) continue;
+ powers[num++] = i;
+ }
+ }
+
+ /* List the monster powers -- RF5_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags5 & BIT(i))
+ {
+ if (!monster_powers[i + 32].power) continue;
+ powers[num++] = i + 32;
+ }
+ }
+
+ /* List the monster powers -- RF6_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags6 & BIT(i))
+ {
+ if (!monster_powers[i + 64].power) continue;
+ powers[num++] = i + 64;
+ }
+ }
+
+ if (!num)
+ {
+ msg_print("You have no powers you can use.");
+ return (TRUE);
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Get the last label */
+ label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26);
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78,
+ "(Powers a-%c, *=List, ESC=exit) Use which power of your golem? ",
+ label);
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt ("", y++, x);
+
+ while (ctr < num)
+ {
+ monster_power *mp_ptr = &monster_powers[powers[ctr]];
+
+ label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26);
+
+ strnfmt(dummy, 80, " %c) %s",
+ label, mp_ptr->name);
+
+ if (ctr < 17)
+ {
+ prt(dummy, y + ctr, x);
+ }
+ else
+ {
+ prt(dummy, y + ctr - 17, x + 40);
+ }
+
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt ("", y + ctr, x);
+ }
+ else
+ {
+ prt ("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ /* Can't uppercase digits XXX XXX XXX */
+ ask = FALSE;
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ power = powers[i];
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Take a turn */
+ if (flag)
+ {
+ energy_use = 100;
+ monst_spell_monst_spell = power + 96;
+ }
+ return TRUE;
+}
+
+/* Finds the controlled monster and "reconnect" to it */
+bool do_control_reconnect()
+{
+ int i;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ {
+ p_ptr->control = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Turns a simple pet into a faithful companion
+ */
+void do_cmd_companion()
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!can_create_companion())
+ {
+ msg_print("You cannot have any more companions.");
+ return;
+ }
+
+ if (!tgt_pt(&ii, &jj))
+ {
+ msg_print("You must target a pet.");
+ return;
+ }
+
+ if (cave[jj][ii].m_idx)
+ {
+ char m_name[100];
+
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ monster_desc(m_name, m_ptr, 0);
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ m_ptr->status = MSTATUS_COMPANION;
+ msg_format("%^s agrees to follow you.", m_name);
+ }
+ else
+ {
+ msg_format("%^s is not your pet!", m_name);
+ }
+ }
+ else
+ msg_print("You must target a pet.");
+}
diff --git a/src/notes.c b/src/notes.c
new file mode 100644
index 00000000..8cfc08ae
--- /dev/null
+++ b/src/notes.c
@@ -0,0 +1,189 @@
+/* File: notes.c */
+
+/* Purpose: Note taking to a file */
+
+/*
+ * Copyright (c) 1989, 1999 James E. Wilson, Robert A. Koeneke,
+ * Robert Ruehlmann
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Show the notes file on the screen
+ */
+void show_notes_file(void)
+{
+ char basename[13];
+ char buf[1024];
+ char caption[10 + 13];
+
+ /* Hack -- extract first 8 characters of name and append an extension */
+ (void)strnfmt(basename, sizeof(basename), "%.8s.nte", player_base);
+ basename[sizeof(basename) - 1] = '\0';
+
+ /* Build the path */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_NOTE, basename);
+
+ /* Use a caption, forcing direct access to the note file */
+ sprintf(caption, "Note file %s", basename);
+
+ /* Invoke show_file */
+ (void)show_file(buf, caption, 0, 0);
+
+ /* Done */
+ return;
+}
+
+/*
+ * Output a string to the notes file.
+ * This is the only function that references that file.
+ */
+void output_note(char *final_note)
+{
+ FILE *fff;
+ char basename[13];
+ char buf[1024];
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Hack -- extract first 8 characters of name and append an extension */
+ (void)strnfmt(basename, sizeof(basename), "%.8s.nte", player_base);
+ basename[sizeof(basename) - 1] = '\0';
+
+ /* Build the path */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_NOTE, basename);
+
+ /* Open notes file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return;
+
+ /* Add note, and close note file */
+ my_fputs(fff, final_note, 0);
+
+ /* Close the handle */
+ my_fclose(fff);
+
+ /* Done */
+ return;
+}
+
+
+/*
+ * Add note to file using a string + character symbol
+ * to specify its type so that the notes file can be
+ * searched easily by external utilities.
+ */
+void add_note(char *note, char code)
+{
+ char buf[100];
+ char final_note[100];
+ char long_day[50];
+ char depths[32];
+ char *tmp;
+
+ /* Get the first 60 chars - so do not have an overflow */
+ tmp = C_WIPE(buf, 100, char);
+ strncpy(buf, note, 60);
+
+ /* Get date and time */
+ sprintf(long_day, "%ld:%02ld %s, %s", (bst(HOUR, turn) % 12 == 0) ? 12 : (bst(HOUR, turn) % 12),
+ bst(MINUTE, turn), (bst(HOUR, turn) < 12) ? "AM" : "PM", get_month_name(bst(DAY, turn), FALSE,
+ FALSE));
+
+ /* Get depth */
+ if (!dun_level) strcpy(depths, " Town");
+ else if (depth_in_feet) sprintf(depths, "%4dft", dun_level * 50);
+ else sprintf(depths, "Lev%3d", dun_level);
+
+ /* Make note */
+ sprintf(final_note, "%-20s %s %c: %s", long_day, depths, code, buf);
+
+ /* Output to the notes file */
+ output_note(final_note);
+}
+
+
+/*
+ * Append a note to the notes file using a "type".
+ */
+void add_note_type(int note_number)
+{
+ char long_day[50], true_long_day[50];
+ char buf[1024];
+ time_t ct = time((time_t*)0);
+
+ /* Get the date */
+ strftime(true_long_day, 30, "%Y-%m-%d at %H:%M:%S", localtime(&ct));
+
+ /* Get the date */
+ sprintf(buf, "%ld", bst(YEAR, turn) + START_YEAR);
+ sprintf(long_day, "%ld:%02ld %s the %s of III %s", (bst(HOUR, turn) % 12 == 0) ? 12 : (bst(HOUR, turn) % 12), bst(MINUTE, turn), (bst(HOUR, turn) < 12) ? "AM" : "PM", get_month_name(bst(DAY, turn), FALSE, FALSE), buf);
+
+ /* Work out what to do */
+ switch (note_number)
+ {
+ case NOTE_BIRTH:
+ {
+ /* Player has just been born */
+ char player[100];
+
+ /* Build the string containing the player information */
+ sprintf(player, "the %s %s", get_player_race_name(p_ptr->prace, p_ptr->pracem), class_info[p_ptr->pclass].spec[p_ptr->pspec].title + c_name);
+
+ /* Add in "character start" information */
+ sprintf(buf,
+ "\n"
+ "================================================\n"
+ "%s %s\n"
+ "Born on %s\n"
+ "================================================\n",
+ player_name, player, true_long_day);
+
+ break;
+ }
+
+ case NOTE_WINNER:
+ {
+ sprintf(buf,
+ "%s slew Morgoth on %s\n"
+ "Long live %s!\n"
+ "================================================",
+ player_name, long_day, player_name);
+
+ break;
+ }
+
+ case NOTE_SAVE_GAME:
+ {
+ /* Saving the game */
+ sprintf(buf, "\nSession end: %s", true_long_day);
+
+ break;
+ }
+
+ case NOTE_ENTER_DUNGEON:
+ {
+ /* Entering the game after a break. */
+ sprintf(buf,
+ "================================================\n"
+ "New session start: %s\n",
+ true_long_day);
+
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ /* Output the notes to the file */
+ output_note(buf);
+}
diff --git a/src/object.pkg b/src/object.pkg
new file mode 100644
index 00000000..0141b5f1
--- /dev/null
+++ b/src/object.pkg
@@ -0,0 +1,1171 @@
+/* File: object.pkg */
+
+/*
+ * Purpose: Lua interface defitions for objects.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+typedef char* cptr;
+typedef int errr;
+typedef unsigned char bool;
+typedef unsigned char byte;
+typedef signed short s16b;
+typedef unsigned short u16b;
+typedef signed int s32b;
+typedef unsigned int u32b;
+
+/* To make easy object creations */
+$static object_type lua_obj_forge;
+static object_type lua_obj_forge @ obj_forge;
+$static obj_theme lua_obj_theme;
+static obj_theme lua_obj_theme @ theme_forge;
+
+#define TR1_STR 0x00000001L /* STR += "pval" */
+#define TR1_INT 0x00000002L /* INT += "pval" */
+#define TR1_WIS 0x00000004L /* WIS += "pval" */
+#define TR1_DEX 0x00000008L /* DEX += "pval" */
+#define TR1_CON 0x00000010L /* CON += "pval" */
+#define TR1_CHR 0x00000020L /* CHR += "pval" */
+#define TR1_MANA 0x00000040L /* Mana multipler */
+#define TR1_SPELL 0x00000080L /* Spell power increase */
+#define TR1_STEALTH 0x00000100L /* Stealth += "pval" */
+#define TR1_SEARCH 0x00000200L /* Search += "pval" */
+#define TR1_INFRA 0x00000400L /* Infra += "pval" */
+#define TR1_TUNNEL 0x00000800L /* Tunnel += "pval" */
+#define TR1_SPEED 0x00001000L /* Speed += "pval" */
+#define TR1_BLOWS 0x00002000L /* Blows += "pval" */
+#define TR1_CHAOTIC 0x00004000L
+#define TR1_VAMPIRIC 0x00008000L
+#define TR1_SLAY_ANIMAL 0x00010000L
+#define TR1_SLAY_EVIL 0x00020000L
+#define TR1_SLAY_UNDEAD 0x00040000L
+#define TR1_SLAY_DEMON 0x00080000L
+#define TR1_SLAY_ORC 0x00100000L
+#define TR1_SLAY_TROLL 0x00200000L
+#define TR1_SLAY_GIANT 0x00400000L
+#define TR1_SLAY_DRAGON 0x00800000L
+#define TR1_KILL_DRAGON 0x01000000L /* Execute Dragon */
+#define TR1_VORPAL 0x02000000L /* Later */
+#define TR1_IMPACT 0x04000000L /* Cause Earthquakes */
+#define TR1_BRAND_POIS 0x08000000L
+#define TR1_BRAND_ACID 0x10000000L
+#define TR1_BRAND_ELEC 0x20000000L
+#define TR1_BRAND_FIRE 0x40000000L
+#define TR1_BRAND_COLD 0x80000000L
+#define TR1_NULL_MASK 0x00000000L
+
+#define TR2_SUST_STR 0x00000001L
+#define TR2_SUST_INT 0x00000002L
+#define TR2_SUST_WIS 0x00000004L
+#define TR2_SUST_DEX 0x00000008L
+#define TR2_SUST_CON 0x00000010L
+#define TR2_SUST_CHR 0x00000020L
+#define TR2_INVIS 0x00000040L /* Invisibility */
+#define TR2_LIFE 0x00000080L /* Life multiplier */
+#define TR2_IM_ACID 0x00000100L
+#define TR2_IM_ELEC 0x00000200L
+#define TR2_IM_FIRE 0x00000400L
+#define TR2_IM_COLD 0x00000800L
+#define TR2_SENS_FIRE 0x00001000L /* Sensibility to fire */
+#define TR2_REFLECT 0x00002000L /* Reflect 'bolts' */
+#define TR2_FREE_ACT 0x00004000L /* Free Action */
+#define TR2_HOLD_LIFE 0x00008000L /* Hold Life */
+#define TR2_RES_ACID 0x00010000L
+#define TR2_RES_ELEC 0x00020000L
+#define TR2_RES_FIRE 0x00040000L
+#define TR2_RES_COLD 0x00080000L
+#define TR2_RES_POIS 0x00100000L
+#define TR2_RES_FEAR 0x00200000L
+#define TR2_RES_LITE 0x00400000L
+#define TR2_RES_DARK 0x00800000L
+#define TR2_RES_BLIND 0x01000000L
+#define TR2_RES_CONF 0x02000000L
+#define TR2_RES_SOUND 0x04000000L
+#define TR2_RES_SHARDS 0x08000000L
+#define TR2_RES_NETHER 0x10000000L
+#define TR2_RES_NEXUS 0x20000000L
+#define TR2_RES_CHAOS 0x40000000L
+#define TR2_RES_DISEN 0x80000000L
+#define TR2_NULL_MASK 0x00000000L
+
+#define TR3_SH_FIRE 0x00000001L /* Immolation (Fire) */
+#define TR3_SH_ELEC 0x00000002L /* Electric Sheath */
+#define TR3_AUTO_CURSE 0x00000004L /* The obj will recurse itself */
+#define TR3_DECAY 0x00000008L /* Decay */
+#define TR3_NO_TELE 0x00000010L /* Anti-teleportation */
+#define TR3_NO_MAGIC 0x00000020L /* Anti-magic */
+#define TR3_WRAITH 0x00000040L /* Wraithform */
+#define TR3_TY_CURSE 0x00000080L /* The Ancient Curse */
+#define TR3_EASY_KNOW 0x00000100L /* Aware -> Known */
+#define TR3_HIDE_TYPE 0x00000200L /* Hide "pval" description */
+#define TR3_SHOW_MODS 0x00000400L /* Always show Tohit/Todam */
+#define TR3_INSTA_ART 0x00000800L /* Item must be an artifact */
+#define TR3_FEATHER 0x00001000L /* Feather Falling */
+#define TR3_LITE1 0x00002000L /* lite radius 1 */
+#define TR3_SEE_INVIS 0x00004000L /* See Invisible */
+#define TR3_NORM_ART 0x00008000L /* Artifact in k_info */
+#define TR3_SLOW_DIGEST 0x00010000L /* Item slows down digestion */
+#define TR3_REGEN 0x00020000L /* Item induces regeneration */
+#define TR3_XTRA_MIGHT 0x00040000L /* Bows get extra multiplier */
+#define TR3_XTRA_SHOTS 0x00080000L /* Bows get extra shots */
+#define TR3_IGNORE_ACID 0x00100000L /* Item ignores Acid Damage */
+#define TR3_IGNORE_ELEC 0x00200000L /* Item ignores Elec Damage */
+#define TR3_IGNORE_FIRE 0x00400000L /* Item ignores Fire Damage */
+#define TR3_IGNORE_COLD 0x00800000L /* Item ignores Cold Damage */
+#define TR3_ACTIVATE 0x01000000L /* Item can be activated */
+#define TR3_DRAIN_EXP 0x02000000L /* Item drains Experience */
+#define TR3_TELEPORT 0x04000000L /* Item teleports player */
+#define TR3_AGGRAVATE 0x08000000L /* Item aggravates monsters */
+#define TR3_BLESSED 0x10000000L /* Item is Blessed */
+#define TR3_CURSED 0x20000000L /* Item is Cursed */
+#define TR3_HEAVY_CURSE 0x40000000L /* Item is Heavily Cursed */
+#define TR3_PERMA_CURSE 0x80000000L /* Item is Perma Cursed */
+#define TR3_NULL_MASK 0x00000000L
+
+#define TR4_NEVER_BLOW 0x00000001L /* Weapon can't attack */
+#define TR4_PRECOGNITION 0x00000002L /* Like activating the cheat mode */
+#define TR4_BLACK_BREATH 0x00000004L /* Tolkien's Black Breath */
+#define TR4_RECHARGE 0x00000008L /* For artifact Wands and Staffs */
+#define TR4_FLY 0x00000010L /* This one and ONLY this one allow you to fly over trees */
+#define TR4_DG_CURSE 0x00000020L /* The Ancient Morgothian Curse */
+#define TR4_COULD2H 0x00000040L /* Can wield it 2 Handed */
+#define TR4_MUST2H 0x00000080L /* Must wield it 2 Handed */
+#define TR4_LEVELS 0x00000100L /* Can gain exp/exp levels !! */
+#define TR4_CLONE 0x00000200L /* Can clone monsters */
+#define TR4_SPECIAL_GENE 0x00000400L /* The object can only be generated in special conditions like quests, special dungeons, ... */
+#define TR4_CLIMB 0x00000800L /* Allow climbing mountains */
+#define TR4_FAST_CAST 0x00001000L /* Rod is x2 time faster to use */
+#define TR4_CAPACITY 0x00002000L /* Rod can take x2 mana */
+#define TR4_CHARGING 0x00004000L /* Rod recharge faster */
+#define TR4_CHEAPNESS 0x00008000L /* Rod spells are cheaper(in mana cost) to cast */
+#define TR4_FOUNTAIN 0x00010000L /* Available as fountain (for potions) */
+#define TR4_ANTIMAGIC_50 0x00020000L /* Forbid magic */
+#define TR4_ANTIMAGIC_30 0x00040000L /* Forbid magic */
+#define TR4_ANTIMAGIC_20 0x00080000L /* Forbid magic */
+#define TR4_ANTIMAGIC_10 0x00100000L /* Forbid magic */
+#define TR4_EASY_USE 0x00200000L /* Easily activable */
+#define TR4_IM_NETHER 0x00400000L /* Immunity to nether */
+#define TR4_RECHARGED 0x00800000L /* Object has been recharged once */
+#define TR4_ULTIMATE 0x01000000L /* ULTIMATE artifact */
+#define TR4_AUTO_ID 0x02000000L /* Id stuff on floor */
+#define TR4_LITE2 0x04000000L /* lite radius 2 */
+#define TR4_LITE3 0x08000000L /* lite radius 3 */
+#define TR4_FUEL_LITE 0x10000000L /* fuelable lite */
+#define TR4_ART_EXP 0x20000000L /* Will accumulate xp */
+#define TR4_CURSE_NO_DROP 0x40000000L /* The obj wont be dropped */
+#define TR4_NO_RECHARGE 0x80000000L /* Object Cannot be recharged */
+#define TR4_NULL_MASK 0xFFFFFFFCL
+
+#define TR5_TEMPORARY 0x00000001L /* In timeout turns it is destroyed */
+#define TR5_DRAIN_MANA 0x00000002L /* Drains mana */
+#define TR5_DRAIN_HP 0x00000004L /* Drains hp */
+#define TR5_KILL_DEMON 0x00000008L /* Execute Demon */
+#define TR5_KILL_UNDEAD 0x00000010L /* Execute Undead */
+#define TR5_CRIT 0x00000020L /* More critical hits */
+#define TR5_ATTR_MULTI 0x00000040L /* Object shimmer -- only allowed in k_info */
+#define TR5_WOUNDING 0x00000080L /* Wounds monsters */
+#define TR5_FULL_NAME 0x00000100L /* Uses direct name from k_info */
+#define TR5_LUCK 0x00000200L /* Luck += pval */
+#define TR5_IMMOVABLE 0x00000400L /* Cannot move */
+#define TR5_SPELL_CONTAIN 0x00000800L /* Can contain a spell */
+#define TR5_RES_MORGUL 0x00001000L /* Is not shattered by morgul fiends(nazguls) */
+#define TR5_ACTIVATE_NO_WIELD 0x00002000L /* Can be 'A'ctivated without being wielded */
+#define TR5_MAGIC_BREATH 0x00004000L /* Can breath anywere */
+#define TR5_WATER_BREATH 0x00008000L /* Can breath underwater */
+#define TR5_WIELD_CAST 0x00010000L /* Need to be wielded to cast spelsl fomr it(if it can be wiekded) */
+
+#define ESP_ORC 0x00000001L
+#define ESP_TROLL 0x00000002L
+#define ESP_DRAGON 0x00000004L
+#define ESP_GIANT 0x00000008L
+#define ESP_DEMON 0x00000010L
+#define ESP_UNDEAD 0x00000020L
+#define ESP_EVIL 0x00000040L
+#define ESP_ANIMAL 0x00000080L
+#define ESP_THUNDERLORD 0x00000100L
+#define ESP_GOOD 0x00000200L
+#define ESP_NONLIVING 0x00000400L
+#define ESP_UNIQUE 0x00000800L
+#define ESP_SPIDER 0x00001000L
+#define ESP_ALL 0x80000000L
+
+/*
+ * Bit flags for the "get_item" function
+ */
+#define USE_EQUIP 0x01 /* Allow equip items */
+#define USE_INVEN 0x02 /* Allow inven items */
+#define USE_FLOOR 0x04 /* Allow floor items */
+#define USE_EXTRA 0x08 /* Allow extra items */
+
+#define INVEN_WIELD 24 /* 3 weapons -- WEAPONS */
+#define INVEN_BOW 27 /* 1 bow -- WEAPON */
+#define INVEN_RING 28 /* 6 rings -- FINGER */
+#define INVEN_NECK 34 /* 2 amulets -- HEAD */
+#define INVEN_LITE 36 /* 1 lite -- TORSO */
+#define INVEN_BODY 37 /* 1 body -- TORSO */
+#define INVEN_OUTER 38 /* 1 cloak -- TORSO */
+#define INVEN_ARM 39 /* 3 arms -- ARMS */
+#define INVEN_HEAD 42 /* 2 heads -- HEAD */
+#define INVEN_HANDS 44 /* 3 hands -- ARMS */
+#define INVEN_FEET 47 /* 2 feets -- LEGS */
+#define INVEN_CARRY 49 /* 1 carried monster -- TORSO */
+#define INVEN_AMMO 50 /* 1 quiver -- TORSO */
+#define INVEN_TOOL 51 /* 1 tool -- ARMS */
+#define INVEN_TOTAL 52
+#define INVEN_EQ (INVEN_TOTAL - INVEN_WIELD)
+
+#define TV_SKELETON 1 /* Skeletons ('s') */
+#define TV_BOTTLE 2 /* Empty bottles ('!') */
+#define TV_BATERIE 4 /* For the Alchemists */
+#define TV_SPIKE 5 /* Spikes ('~') */
+#define TV_MSTAFF 6 /* Mage Staffs */
+#define TV_CHEST 7 /* Chests ('~') */
+#define TV_PARCHMENT 8 /* Parchments from Kamband */
+#define TV_PARCHEMENT 8 /* compatibility define */
+#define TV_CORPSE 9 /* Monster corpses */
+#define TV_EGG 10 /* Monster Eggs */
+#define TV_JUNK 11 /* Sticks, Pottery, etc ('~') */
+#define TV_TOOL 12 /* Tools */
+#define TV_INSTRUMENT 14 /* Musical instruments */
+#define TV_BOOMERANG 15 /* Boomerangs */
+#define TV_SHOT 16 /* Ammo for slings */
+#define TV_ARROW 17 /* Ammo for bows */
+#define TV_BOLT 18 /* Ammo for x-bows */
+#define TV_BOW 19 /* Slings/Bows/Xbows */
+#define TV_DIGGING 20 /* Shovels/Picks */
+#define TV_HAFTED 21 /* Priest Weapons */
+#define TV_POLEARM 22 /* Pikes/Glaives/Spears/etc. */
+#define TV_SWORD 23 /* Edged Weapons */
+#define TV_AXE 24 /* Axes/Cleavers */
+#define TV_BOOTS 30 /* Boots */
+#define TV_GLOVES 31 /* Gloves */
+#define TV_HELM 32 /* Helms */
+#define TV_CROWN 33 /* Crowns */
+#define TV_SHIELD 34 /* Shields */
+#define TV_CLOAK 35 /* Cloaks */
+#define TV_SOFT_ARMOR 36 /* Soft Armor */
+#define TV_HARD_ARMOR 37 /* Hard Armor */
+#define TV_DRAG_ARMOR 38 /* Dragon Scale Mail */
+#define TV_LITE 39 /* Lites (including Specials) */
+#define TV_AMULET 40 /* Amulets (including Specials) */
+#define TV_RING 45 /* Rings (including Specials) */
+#define TV_TRAPKIT 46 /* Trapkits */
+#define TV_TOTEM 54 /* Summoner totems */
+#define TV_STAFF 55
+#define TV_WAND 65
+#define TV_ROD 66
+#define TV_ROD_MAIN 67
+#define TV_SCROLL 70
+#define TV_POTION 71
+#define TV_POTION2 72 /* Second set of potion */
+#define TV_FLASK 77
+#define TV_FOOD 80
+#define TV_HYPNOS 99 /* To wield monsters !:) */
+#define TV_GOLD 100 /* Gold can only be picked up by players */
+#define TV_RANDART 102 /* Random Artifacts */
+#define TV_RUNE1 104 /* Base runes */
+#define TV_RUNE2 105 /* Modifier runes */
+
+#define TV_BOOK 111
+#define TV_SYMBIOTIC_BOOK 112
+#define TV_MUSIC_BOOK 113
+#define TV_DRUID_BOOK 114
+#define TV_DAEMON_BOOK 115
+
+/* The "sval" codes for TV_TOOL */
+#define SV_TOOL_CLIMB 0
+#define SV_PORTABLE_HOLE 1
+
+/* The "sval" codes for TV_MSTAFF */
+#define SV_MSTAFF 1
+
+/* The "sval" codes for TV_SHOT/TV_ARROW/TV_BOLT */
+#define SV_AMMO_LIGHT 0 /* pebbles */
+#define SV_AMMO_NORMAL 1 /* shots, arrows, bolts */
+#define SV_AMMO_HEAVY 2 /* seeker arrows and bolts, mithril shots */
+
+/* The "sval" codes for TV_INSTRUMENT */
+#define SV_DRUM 58
+#define SV_HARP 59
+#define SV_HORN 60
+
+/* The "sval" codes for TV_TRAPKIT */
+#define SV_TRAPKIT_SLING 1
+#define SV_TRAPKIT_BOW 2
+#define SV_TRAPKIT_XBOW 3
+#define SV_TRAPKIT_POTION 4
+#define SV_TRAPKIT_SCROLL 5
+#define SV_TRAPKIT_DEVICE 6
+
+/* The "sval" codes for TV_BOOMERANG */
+#define SV_BOOM_S_WOOD 1 /* 1d4 */
+#define SV_BOOM_WOOD 2 /* 1d9 */
+#define SV_BOOM_S_METAL 3 /* 1d8 */
+#define SV_BOOM_METAL 4 /* 2d4 */
+
+/* The "sval" codes for TV_BOW (note information in "sval") */
+#define SV_SLING 2 /* (x2) */
+#define SV_SHORT_BOW 12 /* (x2) */
+#define SV_LONG_BOW 13 /* (x3) */
+#define SV_LIGHT_XBOW 23 /* (x3) */
+#define SV_HEAVY_XBOW 24 /* (x4) */
+
+/* The "sval" codes for TV_DIGGING */
+#define SV_SHOVEL 1
+#define SV_GNOMISH_SHOVEL 2
+#define SV_DWARVEN_SHOVEL 3
+#define SV_PICK 4
+#define SV_ORCISH_PICK 5
+#define SV_DWARVEN_PICK 6
+#define SV_MATTOCK 7
+
+/* The "sval" values for TV_HAFTED */
+#define SV_CLUB 1 /* 1d4 */
+#define SV_WHIP 2 /* 1d6 */
+#define SV_QUARTERSTAFF 3 /* 1d9 */
+#define SV_NUNCHAKU 4 /* 2d3 */
+#define SV_MACE 5 /* 2d4 */
+#define SV_BALL_AND_CHAIN 6 /* 2d4 */
+#define SV_WAR_HAMMER 8 /* 3d3 */
+#define SV_LUCERN_HAMMER 10 /* 2d5 */
+#define SV_THREE_PIECE_ROD 11 /* 3d3 */
+#define SV_MORNING_STAR 12 /* 2d6 */
+#define SV_FLAIL 13 /* 2d6 */
+#define SV_LEAD_FILLED_MACE 15 /* 3d4 */
+#define SV_TWO_HANDED_FLAIL 18 /* 3d6 */
+#define SV_GREAT_HAMMER 19 /* 4d6 */
+#define SV_MACE_OF_DISRUPTION 20 /* 5d8 */
+#define SV_GROND 50 /* 3d4 */
+
+/* The "sval" values for TV_AXE */
+#define SV_HATCHET 1 /* 1d5 */
+#define SV_CLEAVER 2 /* 2d4 */
+#define SV_LIGHT_WAR_AXE 8 /* 2d5 */
+#define SV_BEAKED_AXE 10 /* 2d6 */
+#define SV_BROAD_AXE 11 /* 2d6 */
+#define SV_BATTLE_AXE 22 /* 2d8 */
+#define SV_GREAT_AXE 25 /* 4d4 */
+#define SV_LOCHABER_AXE 28 /* 3d8 */
+#define SV_SLAUGHTER_AXE 30 /* 5d7 */
+
+/* The "sval" values for TV_POLEARM */
+#define SV_SPEAR 2 /* 1d6 */
+#define SV_SICKLE 3 /* 2d3 */
+#define SV_AWL_PIKE 4 /* 1d8 */
+#define SV_TRIDENT 5 /* 1d9 */
+#define SV_FAUCHARD 6 /* 1d10 */
+#define SV_BROAD_SPEAR 7 /* 1d9 */
+#define SV_PIKE 8 /* 2d5 */
+#define SV_GLAIVE 13 /* 2d6 */
+#define SV_HALBERD 15 /* 3d4 */
+#define SV_GUISARME 16 /* 2d5 */
+#define SV_SCYTHE 17 /* 5d3 */
+#define SV_LANCE 20 /* 2d8 */
+#define SV_TRIFURCATE_SPEAR 26 /* 2d9 */
+#define SV_HEAVY_LANCE 29 /* 4d8 */
+#define SV_SCYTHE_OF_SLICING 30 /* 8d4 */
+
+/* The "sval" codes for TV_SWORD */
+#define SV_BROKEN_DAGGER 1 /* 1d1 */
+#define SV_BROKEN_SWORD 2 /* 1d2 */
+#define SV_DAGGER 4 /* 1d4 */
+#define SV_MAIN_GAUCHE 5 /* 1d5 */
+#define SV_RAPIER 7 /* 1d6 */
+#define SV_SMALL_SWORD 8 /* 1d6 */
+#define SV_BASILLARD 9 /* 1d8 */
+#define SV_SHORT_SWORD 10 /* 1d7 */
+#define SV_SABRE 11 /* 1d7 */
+#define SV_CUTLASS 12 /* 1d7 */
+#define SV_KHOPESH 14 /* 2d4 */
+#define SV_TULWAR 15 /* 2d4 */
+#define SV_BROAD_SWORD 16 /* 2d5 */
+#define SV_LONG_SWORD 17 /* 2d5 */
+#define SV_SCIMITAR 18 /* 2d5 */
+#define SV_KATANA 20 /* 3d4 */
+#define SV_BASTARD_SWORD 21 /* 3d4 */
+#define SV_GREAT_SCIMITAR 22 /* 4d5 */
+#define SV_CLAYMORE 23 /* 2d8 */
+#define SV_ESPADON 24 /* 2d9 */
+#define SV_TWO_HANDED_SWORD 25 /* 3d6 */
+#define SV_FLAMBERGE 26 /* 3d7 */
+#define SV_EXECUTIONERS_SWORD 28 /* 4d5 */
+#define SV_ZWEIHANDER 29 /* 4d6 */
+#define SV_BLADE_OF_CHAOS 30 /* 6d5 */
+#define SV_BLUESTEEL_BLADE 31 /* 3d9 */
+#define SV_SHADOW_BLADE 32 /* 4d4 */
+#define SV_DARK_SWORD 33 /* 3d7 */
+
+/* The "sval" codes for TV_SHIELD */
+#define SV_SMALL_LEATHER_SHIELD 2
+#define SV_SMALL_METAL_SHIELD 3
+#define SV_LARGE_LEATHER_SHIELD 4
+#define SV_LARGE_METAL_SHIELD 5
+#define SV_DRAGON_SHIELD 6
+#define SV_SHIELD_OF_DEFLECTION 10
+
+/* The "sval" codes for TV_HELM */
+#define SV_HARD_LEATHER_CAP 2
+#define SV_METAL_CAP 3
+#define SV_IRON_HELM 5
+#define SV_STEEL_HELM 6
+#define SV_DRAGON_HELM 7
+#define SV_IRON_CROWN 10
+#define SV_GOLDEN_CROWN 11
+#define SV_JEWELED_CROWN 12
+#define SV_MORGOTH 50
+
+/* The "sval" codes for TV_BOOTS */
+#define SV_PAIR_OF_SOFT_LEATHER_BOOTS 2
+#define SV_PAIR_OF_HARD_LEATHER_BOOTS 3
+#define SV_PAIR_OF_METAL_SHOD_BOOTS 6
+
+/* The "sval" codes for TV_CLOAK */
+#define SV_CLOAK 1
+#define SV_ELVEN_CLOAK 2
+#define SV_FUR_CLOAK 3
+#define SV_SHADOW_CLOAK 6
+
+/* The "sval" codes for TV_GLOVES */
+#define SV_SET_OF_LEATHER_GLOVES 1
+#define SV_SET_OF_GAUNTLETS 2
+#define SV_SET_OF_CESTI 5
+
+/* The "sval" codes for TV_SOFT_ARMOR */
+#define SV_FILTHY_RAG 1
+#define SV_ROBE 2
+#define SV_PAPER_ARMOR 3 /* 4 */
+#define SV_SOFT_LEATHER_ARMOR 4
+#define SV_SOFT_STUDDED_LEATHER 5
+#define SV_HARD_LEATHER_ARMOR 6
+#define SV_HARD_STUDDED_LEATHER 7
+#define SV_RHINO_HIDE_ARMOR 8
+#define SV_CORD_ARMOR 9 /* 6 */
+#define SV_PADDED_ARMOR 10 /* 4 */
+#define SV_LEATHER_SCALE_MAIL 11
+#define SV_LEATHER_JACK 12
+#define SV_STONE_AND_HIDE_ARMOR 15 /* 15 */
+#define SV_THUNDERLORD_SUIT 16
+
+/* The "sval" codes for TV_HARD_ARMOR */
+#define SV_RUSTY_CHAIN_MAIL 1 /* 14- */
+#define SV_RING_MAIL 2 /* 12 */
+#define SV_METAL_SCALE_MAIL 3 /* 13 */
+#define SV_CHAIN_MAIL 4 /* 14 */
+#define SV_DOUBLE_RING_MAIL 5 /* 15 */
+#define SV_AUGMENTED_CHAIN_MAIL 6 /* 16 */
+#define SV_DOUBLE_CHAIN_MAIL 7 /* 16 */
+#define SV_BAR_CHAIN_MAIL 8 /* 18 */
+#define SV_METAL_BRIGANDINE_ARMOUR 9 /* 19 */
+#define SV_SPLINT_MAIL 10 /* 19 */
+#define SV_PARTIAL_PLATE_ARMOUR 12 /* 22 */
+#define SV_METAL_LAMELLAR_ARMOUR 13 /* 23 */
+#define SV_FULL_PLATE_ARMOUR 15 /* 25 */
+#define SV_RIBBED_PLATE_ARMOUR 18 /* 28 */
+#define SV_MITHRIL_CHAIN_MAIL 20 /* 28+ */
+#define SV_MITHRIL_PLATE_MAIL 25 /* 35+ */
+#define SV_ADAMANTITE_PLATE_MAIL 30 /* 40+ */
+
+/* The "sval" codes for TV_DRAG_ARMOR */
+#define SV_DRAGON_BLACK 1
+#define SV_DRAGON_BLUE 2
+#define SV_DRAGON_WHITE 3
+#define SV_DRAGON_RED 4
+#define SV_DRAGON_GREEN 5
+#define SV_DRAGON_MULTIHUED 6
+#define SV_DRAGON_SHINING 10
+#define SV_DRAGON_LAW 12
+#define SV_DRAGON_BRONZE 14
+#define SV_DRAGON_GOLD 16
+#define SV_DRAGON_CHAOS 18
+#define SV_DRAGON_BALANCE 20
+#define SV_DRAGON_POWER 30
+
+/* The sval codes for TV_LITE */
+#define SV_LITE_TORCH 0
+#define SV_LITE_LANTERN 1
+#define SV_LITE_TORCH_EVER 2
+#define SV_LITE_DWARVEN 3
+#define SV_LITE_FEANORIAN 4
+#define SV_LITE_GALADRIEL 100
+#define SV_LITE_ELENDIL 101
+#define SV_LITE_THRAIN 102
+#define SV_LITE_UNDEATH 103
+#define SV_LITE_PALANTIR 104
+#define SV_ANCHOR_SPACETIME 105
+#define SV_STONE_LORE 106
+
+
+/* The "sval" codes for TV_AMULET */
+#define SV_AMULET_DOOM 0
+#define SV_AMULET_TELEPORT 1
+#define SV_AMULET_ADORNMENT 2
+#define SV_AMULET_SLOW_DIGEST 3
+#define SV_AMULET_RESIST_ACID 4
+#define SV_AMULET_SEARCHING 5
+#define SV_AMULET_BRILLANCE 6
+#define SV_AMULET_CHARISMA 7
+#define SV_AMULET_THE_MAGI 8
+#define SV_AMULET_REFLECTION 9
+#define SV_AMULET_CARLAMMAS 10
+#define SV_AMULET_INGWE 11
+#define SV_AMULET_DWARVES 12
+#define SV_AMULET_NO_MAGIC 13
+#define SV_AMULET_NO_TELE 14
+#define SV_AMULET_RESISTANCE 15
+#define SV_AMULET_NOTHING 16
+#define SV_AMULET_SERPENT 17
+#define SV_AMULET_TORIS_MEJISTOS 18
+#define SV_AMULET_TRICKERY 23
+#define SV_AMULET_DEVOTION 25
+#define SV_AMULET_WEAPONMASTERY 24
+#define SV_AMULET_WISDOM 28
+#define SV_AMULET_INFRA 26
+#define SV_AMULET_SPELL 27
+
+/* The sval codes for TV_RING */
+#define SV_RING_WOE 0
+#define SV_RING_AGGRAVATION 1
+#define SV_RING_WEAKNESS 2
+#define SV_RING_STUPIDITY 3
+#define SV_RING_TELEPORTATION 4
+#define SV_RING_SPECIAL 5
+#define SV_RING_SLOW_DIGESTION 6
+#define SV_RING_FEATHER_FALL 7
+#define SV_RING_RESIST_FIRE 8
+#define SV_RING_RESIST_COLD 9
+#define SV_RING_SUSTAIN_STR 10
+#define SV_RING_SUSTAIN_INT 11
+#define SV_RING_SUSTAIN_WIS 12
+#define SV_RING_SUSTAIN_DEX 13
+#define SV_RING_SUSTAIN_CON 14
+#define SV_RING_SUSTAIN_CHR 15
+#define SV_RING_PROTECTION 16
+#define SV_RING_ACID 17
+#define SV_RING_FLAMES 18
+#define SV_RING_ICE 19
+#define SV_RING_RESIST_POIS 20
+#define SV_RING_FREE_ACTION 21
+#define SV_RING_SEE_INVIS 22
+#define SV_RING_SEARCHING 23
+#define SV_RING_STR 24
+#define SV_RING_INT 25
+#define SV_RING_DEX 26
+#define SV_RING_CON 27
+#define SV_RING_ACCURACY 28
+#define SV_RING_DAMAGE 29
+#define SV_RING_SLAYING 30
+#define SV_RING_SPEED 31
+#define SV_RING_BARAHIR 32
+#define SV_RING_TULKAS 33
+#define SV_RING_NARYA 34
+#define SV_RING_NENYA 35
+#define SV_RING_VILYA 36
+#define SV_RING_POWER 37
+#define SV_RING_RES_FEAR 38
+#define SV_RING_RES_LD 39
+#define SV_RING_RES_NETHER 40
+#define SV_RING_RES_NEXUS 41
+#define SV_RING_RES_SOUND 42
+#define SV_RING_RES_CONFUSION 43
+#define SV_RING_RES_SHARDS 44
+#define SV_RING_RES_DISENCHANT 45
+#define SV_RING_RES_CHAOS 46
+#define SV_RING_RES_BLINDNESS 47
+#define SV_RING_LORDLY 48
+#define SV_RING_ATTACKS 49
+#define SV_RING_NOTHING 50
+#define SV_RING_PRECONITION 51
+#define SV_RING_FLAR 52
+#define SV_RING_INVIS 53
+#define SV_RING_FLYING 54
+#define SV_RING_WRAITH 55
+#define SV_RING_ELEC 56
+#define SV_RING_CRIT 57
+#define SV_RING_SPELL 58
+
+/* The "sval" codes for TV_STAFF */
+#define SV_STAFF_SCHOOL 1
+#define SV_STAFF_NOTHING 2
+
+/* The "sval" codes for TV_WAND */
+#define SV_WAND_SCHOOL 1
+#define SV_WAND_NOTHING 2
+
+/* The "sval" codes for TV_ROD(Rod Tips) */
+#define SV_ROD_NOTHING 0
+#define SV_ROD_DETECT_DOOR 1
+#define SV_ROD_IDENTIFY 2
+#define SV_ROD_RECALL 3
+#define SV_ROD_ILLUMINATION 4
+#define SV_ROD_MAPPING 5
+#define SV_ROD_DETECTION 6
+#define SV_ROD_PROBING 7
+#define SV_ROD_CURING 8
+#define SV_ROD_HEALING 9
+#define SV_ROD_RESTORATION 10
+#define SV_ROD_SPEED 11
+/* xxx (aimed) */
+#define SV_ROD_TELEPORT_AWAY 13
+#define SV_ROD_DISARMING 14
+#define SV_ROD_LITE 15
+#define SV_ROD_SLEEP_MONSTER 16
+#define SV_ROD_SLOW_MONSTER 17
+#define SV_ROD_DRAIN_LIFE 18
+#define SV_ROD_POLYMORPH 19
+#define SV_ROD_ACID_BOLT 20
+#define SV_ROD_ELEC_BOLT 21
+#define SV_ROD_FIRE_BOLT 22
+#define SV_ROD_COLD_BOLT 23
+#define SV_ROD_ACID_BALL 24
+#define SV_ROD_ELEC_BALL 25
+#define SV_ROD_FIRE_BALL 26
+#define SV_ROD_COLD_BALL 27
+#define SV_ROD_HAVOC 28
+#define SV_ROD_DETECT_TRAP 29
+#define SV_ROD_HOME 30
+
+
+/* The "sval" codes for TV_ROD_MAIN(Rods) */
+/* Note that the sval is the max mana capacity of the rod */
+
+#define SV_ROD_WOODEN 10
+#define SV_ROD_COPPER 20
+#define SV_ROD_IRON 50
+#define SV_ROD_ALUMINIUM 75
+#define SV_ROD_SILVER 100
+#define SV_ROD_GOLDEN 125
+#define SV_ROD_MITHRIL 160
+#define SV_ROD_ADMANTITE 200
+
+
+/* The "sval" codes for TV_SCROLL */
+
+#define SV_SCROLL_DARKNESS 0
+#define SV_SCROLL_AGGRAVATE_MONSTER 1
+#define SV_SCROLL_CURSE_ARMOR 2
+#define SV_SCROLL_CURSE_WEAPON 3
+#define SV_SCROLL_SUMMON_MONSTER 4
+#define SV_SCROLL_SUMMON_UNDEAD 5
+#define SV_SCROLL_SUMMON_MINE 6
+#define SV_SCROLL_TRAP_CREATION 7
+#define SV_SCROLL_PHASE_DOOR 8
+#define SV_SCROLL_TELEPORT 9
+#define SV_SCROLL_TELEPORT_LEVEL 10
+#define SV_SCROLL_WORD_OF_RECALL 11
+#define SV_SCROLL_IDENTIFY 12
+#define SV_SCROLL_STAR_IDENTIFY 13
+#define SV_SCROLL_REMOVE_CURSE 14
+#define SV_SCROLL_STAR_REMOVE_CURSE 15
+#define SV_SCROLL_ENCHANT_ARMOR 16
+#define SV_SCROLL_ENCHANT_WEAPON_TO_HIT 17
+#define SV_SCROLL_ENCHANT_WEAPON_TO_DAM 18
+#define SV_SCROLL_ENCHANT_WEAPON_PVAL 19
+#define SV_SCROLL_STAR_ENCHANT_ARMOR 20
+#define SV_SCROLL_STAR_ENCHANT_WEAPON 21
+#define SV_SCROLL_RECHARGING 22
+#define SV_SCROLL_RESET_RECALL 23
+#define SV_SCROLL_LIGHT 24
+#define SV_SCROLL_MAPPING 25
+#define SV_SCROLL_DETECT_GOLD 26
+#define SV_SCROLL_DETECT_ITEM 27
+#define SV_SCROLL_DETECT_TRAP 28
+#define SV_SCROLL_DETECT_DOOR 29
+#define SV_SCROLL_DETECT_INVIS 30
+#define SV_SCROLL_DIVINATION 31
+#define SV_SCROLL_SATISFY_HUNGER 32
+#define SV_SCROLL_BLESSING 33
+#define SV_SCROLL_HOLY_CHANT 34
+#define SV_SCROLL_HOLY_PRAYER 35
+#define SV_SCROLL_MONSTER_CONFUSION 36
+#define SV_SCROLL_PROTECTION_FROM_EVIL 37
+#define SV_SCROLL_RUNE_OF_PROTECTION 38
+#define SV_SCROLL_TRAP_DOOR_DESTRUCTION 39
+#define SV_SCROLL_DEINCARNATION 40
+#define SV_SCROLL_STAR_DESTRUCTION 41
+#define SV_SCROLL_DISPEL_UNDEAD 42
+#define SV_SCROLL_MASS_RESURECTION 43
+#define SV_SCROLL_GENOCIDE 44
+#define SV_SCROLL_MASS_GENOCIDE 45
+#define SV_SCROLL_ACQUIREMENT 46
+#define SV_SCROLL_STAR_ACQUIREMENT 47
+#define SV_SCROLL_FIRE 48
+#define SV_SCROLL_ICE 49
+#define SV_SCROLL_CHAOS 50
+#define SV_SCROLL_RUMOR 51
+#define SV_SCROLL_ARTIFACT 52
+#define SV_SCROLL_NOTHING 53
+
+/* The "sval" codes for TV_POTION */
+#define SV_POTION_WATER 0
+#define SV_POTION_APPLE_JUICE 1
+#define SV_POTION_SLIME_MOLD 2
+#define SV_POTION_BLOOD 3
+#define SV_POTION_SLOWNESS 4
+#define SV_POTION_SALT_WATER 5
+#define SV_POTION_POISON 6
+#define SV_POTION_BLINDNESS 7
+#define SV_POTION_INVIS 8
+#define SV_POTION_CONFUSION 9
+#define SV_POTION_MUTATION 10
+#define SV_POTION_SLEEP 11
+#define SV_POTION_LEARNING 12
+#define SV_POTION_LOSE_MEMORIES 13
+/* xxx */
+#define SV_POTION_RUINATION 15
+#define SV_POTION_DEC_STR 16
+#define SV_POTION_DEC_INT 17
+#define SV_POTION_DEC_WIS 18
+#define SV_POTION_DEC_DEX 19
+#define SV_POTION_DEC_CON 20
+#define SV_POTION_DEC_CHR 21
+#define SV_POTION_DETONATIONS 22
+#define SV_POTION_DEATH 23
+#define SV_POTION_INFRAVISION 24
+#define SV_POTION_DETECT_INVIS 25
+#define SV_POTION_SLOW_POISON 26
+#define SV_POTION_CURE_POISON 27
+#define SV_POTION_BOLDNESS 28
+#define SV_POTION_SPEED 29
+#define SV_POTION_RESIST_HEAT 30
+#define SV_POTION_RESIST_COLD 31
+#define SV_POTION_HEROISM 32
+#define SV_POTION_BESERK_STRENGTH 33
+#define SV_POTION_CURE_LIGHT 34
+#define SV_POTION_CURE_SERIOUS 35
+#define SV_POTION_CURE_CRITICAL 36
+#define SV_POTION_HEALING 37
+#define SV_POTION_STAR_HEALING 38
+#define SV_POTION_LIFE 39
+#define SV_POTION_RESTORE_MANA 40
+#define SV_POTION_RESTORE_EXP 41
+#define SV_POTION_RES_STR 42
+#define SV_POTION_RES_INT 43
+#define SV_POTION_RES_WIS 44
+#define SV_POTION_RES_DEX 45
+#define SV_POTION_RES_CON 46
+#define SV_POTION_RES_CHR 47
+#define SV_POTION_INC_STR 48
+#define SV_POTION_INC_INT 49
+#define SV_POTION_INC_WIS 50
+#define SV_POTION_INC_DEX 51
+#define SV_POTION_INC_CON 52
+#define SV_POTION_INC_CHR 53
+/* xxx */
+#define SV_POTION_AUGMENTATION 55
+#define SV_POTION_ENLIGHTENMENT 56
+#define SV_POTION_STAR_ENLIGHTENMENT 57
+#define SV_POTION_SELF_KNOWLEDGE 58
+#define SV_POTION_EXPERIENCE 59
+#define SV_POTION_RESISTANCE 60
+#define SV_POTION_CURING 61
+#define SV_POTION_INVULNERABILITY 62
+#define SV_POTION_NEW_LIFE 63
+
+#define SV_POTION_LAST 63
+
+/* The "sval" codes for TV_POTION2 */
+#define SV_POTION2_MIMIC 1
+#define SV_POTION2_CURE_LIGHT_SANITY 14
+#define SV_POTION2_CURE_SERIOUS_SANITY 15
+#define SV_POTION2_CURE_CRITICAL_SANITY 16
+#define SV_POTION2_CURE_SANITY 17
+#define SV_POTION2_CURE_WATER 18
+
+#define SV_POTION2_LAST 18
+
+/* The "sval" codes for TV_FOOD */
+#define SV_FOOD_POISON 0
+#define SV_FOOD_BLINDNESS 1
+#define SV_FOOD_PARANOIA 2
+#define SV_FOOD_CONFUSION 3
+#define SV_FOOD_HALLUCINATION 4
+#define SV_FOOD_PARALYSIS 5
+#define SV_FOOD_WEAKNESS 6
+#define SV_FOOD_SICKNESS 7
+#define SV_FOOD_STUPIDITY 8
+#define SV_FOOD_NAIVETY 9
+#define SV_FOOD_UNHEALTH 10
+#define SV_FOOD_DISEASE 11
+#define SV_FOOD_CURE_POISON 12
+#define SV_FOOD_CURE_BLINDNESS 13
+#define SV_FOOD_CURE_PARANOIA 14
+#define SV_FOOD_CURE_CONFUSION 15
+#define SV_FOOD_CURE_SERIOUS 16
+#define SV_FOOD_RESTORE_STR 17
+#define SV_FOOD_RESTORE_CON 18
+#define SV_FOOD_RESTORING 19
+/* many missing mushrooms */
+#define SV_FOOD_BISCUIT 32
+#define SV_FOOD_JERKY 33
+#define SV_FOOD_RATION 35
+#define SV_FOOD_SLIME_MOLD 36
+#define SV_FOOD_WAYBREAD 37
+#define SV_FOOD_PINT_OF_ALE 38
+#define SV_FOOD_PINT_OF_WINE 39
+#define SV_FOOD_ATHELAS 40
+#define SV_FOOD_GREAT_HEALTH 41
+#define SV_FOOD_FORTUNE_COOKIE 42
+
+/* The "sval" codes for TV_BATERIE */
+#define SV_BATERIE_POISON 1
+#define SV_BATERIE_EXPLOSION 2
+#define SV_BATERIE_TELEPORT 3
+#define SV_BATERIE_COLD 4
+#define SV_BATERIE_FIRE 5
+#define SV_BATERIE_ACID 6
+#define SV_BATERIE_LIFE 7
+#define SV_BATERIE_CONFUSION 8
+#define SV_BATERIE_LITE 9
+#define SV_BATERIE_CHAOS 10
+#define SV_BATERIE_TIME 11
+#define SV_BATERIE_MAGIC 12
+#define SV_BATERIE_XTRA_LIFE 13
+#define SV_BATERIE_DARKNESS 14
+#define SV_BATERIE_KNOWLEDGE 15
+#define SV_BATERIE_FORCE 16
+#define SV_BATERIE_LIGHTNING 17
+#define SV_BATERIE_MANA 18
+
+/* The "sval" codes for TV_CORPSE */
+#define SV_CORPSE_CORPSE 1
+#define SV_CORPSE_SKELETON 2
+#define SV_CORPSE_HEAD 3
+#define SV_CORPSE_SKULL 4
+#define SV_CORPSE_MEAT 5
+
+/* The "sval" codes for TV_DAEMON_BOOK */
+#define SV_DEMONBLADE 55
+#define SV_DEMONSHIELD 56
+#define SV_DEMONHORN 57
+
+/*
+ * Special Object Flags
+ */
+#define IDENT_SENSE 0x01 /* Item has been "sensed" */
+#define IDENT_FIXED 0x02 /* Item has been "haggled" */
+#define IDENT_EMPTY 0x04 /* Item charges are known */
+#define IDENT_KNOWN 0x08 /* Item abilities are known */
+#define IDENT_STOREB 0x10 /* Item is storebought !!!! */
+#define IDENT_MENTAL 0x20 /* Item information is known */
+#define IDENT_CURSED 0x40 /* Item is temporarily cursed */
+
+/*
+ * Location of objects when they were found
+ */
+#define OBJ_FOUND_MONSTER 1
+#define OBJ_FOUND_FLOOR 2
+#define OBJ_FOUND_VAULT 3
+#define OBJ_FOUND_SPECIAL 4
+#define OBJ_FOUND_RUBBLE 5
+#define OBJ_FOUND_REWARD 6
+#define OBJ_FOUND_STORE 7
+#define OBJ_FOUND_STOLEN 8
+#define OBJ_FOUND_SELFMADE 9
+
+struct obj_theme
+{
+ byte treasure;
+ byte combat;
+ byte magic;
+ byte tools;
+};
+
+struct object_kind
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Object type */
+ byte sval; /* Object sub type */
+
+ s32b pval; /* Object extra info */
+ s32b pval2; /* Object extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b ac; /* Base armor */
+
+ byte dd;
+ byte ds; /* Damage dice/sides */
+
+ s32b weight; /* Weight */
+
+ s32b cost; /* Object "base cost" */
+
+ u32b flags1; /* Flags, set 1 */
+ u32b flags2; /* Flags, set 2 */
+ u32b flags3; /* Flags, set 3 */
+ u32b flags4; /* Flags, set 4 */
+ u32b flags5; /* Flags, set 5 */
+
+ byte locale[4]; /* Allocation level(s) */
+ byte chance[4]; /* Allocation chance(s) */
+
+ byte level; /* Level */
+ byte extra; /* Something */
+
+
+ byte d_attr; /* Default object attribute */
+ char d_char; /* Default object character */
+
+
+ byte x_attr; /* Desired object attribute */
+ char x_char; /* Desired object character */
+
+
+ byte flavor; /* Special object flavor (or zero) */
+
+ bool easy_know; /* This object is always known (if aware) */
+
+
+ bool aware; /* The player is "aware" of the item's effects */
+
+ bool tried; /* The player has "tried" one of the items */
+
+ bool know; /* extractable flag for the alchemist */
+
+ u32b esp; /* ESP flags */
+
+ byte btval; /* Become Object type */
+ byte bsval; /* Become Object sub type */
+ bool artifact; /* Is it a normal artifact(already generated) */
+
+ s16b power; /* Power granted(if any) */
+};
+
+struct artifact_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Artifact type */
+ byte sval; /* Artifact sub type */
+
+ s16b pval; /* Artifact extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b ac; /* Base armor */
+
+ byte dd;
+ byte ds; /* Damage when hits */
+
+ s16b weight; /* Weight */
+
+ s32b cost; /* Artifact "cost" */
+
+ u32b flags1; /* Artifact Flags, set 1 */
+ u32b flags2; /* Artifact Flags, set 2 */
+ u32b flags3; /* Artifact Flags, set 3 */
+ u32b flags4; /* Artifact Flags, set 4 */
+ u32b flags5; /* Artifact Flags, set 5 */
+
+ byte level; /* Artifact level */
+ byte rarity; /* Artifact rarity */
+
+ byte cur_num; /* Number created (0 or 1) */
+ byte max_num; /* Unused (should be "1") */
+
+ u32b esp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+struct ego_item_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ bool before; /* Before or after the object name ? */
+
+ byte tval[6];
+ byte min_sval[6];
+ byte max_sval[6];
+
+ byte rating; /* Rating boost */
+
+ byte level; /* Minimum level */
+ byte rarity; /* Object rarity */
+ byte mrarity; /* Object rarity */
+
+ s16b max_to_h; /* Maximum to-hit bonus */
+ s16b max_to_d; /* Maximum to-dam bonus */
+ s16b max_to_a; /* Maximum to-ac bonus */
+
+ s32b max_pval; /* Maximum pval */
+
+ s32b cost; /* Ego-item "cost" */
+
+ byte rar[5];
+ u32b flags1[5]; /* Ego-Item Flags, set 1 */
+ u32b flags2[5]; /* Ego-Item Flags, set 2 */
+ u32b flags3[5]; /* Ego-Item Flags, set 3 */
+ u32b flags4[5]; /* Ego-Item Flags, set 4 */
+ u32b flags5[5]; /* Ego-Item Flags, set 5 */
+ u32b esp[5]; /* ESP flags */
+ u32b fego[5]; /* ego flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+struct object_type
+{
+ s16b k_idx; /* Kind index (zero if "dead") */
+
+ byte iy; /* Y-position on map, or zero */
+ byte ix; /* X-position on map, or zero */
+
+ byte tval; /* Item type (from kind) */
+ byte sval; /* Item sub-type (from kind) */
+
+ s32b pval; /* Item extra-parameter */
+ s16b pval2; /* Item extra-parameter for some special
+ items*/
+ s32b pval3; /* Item extra-parameter for some special
+ items*/
+
+ byte discount; /* Discount (if any) */
+
+ byte number; /* Number of items */
+
+ s32b weight; /* Item weight */
+
+ byte elevel; /* Item exp level */
+ s32b exp; /* Item exp */
+
+ byte name1; /* Artifact type, if any */
+ s16b name2; /* Ego-Item type, if any */
+ s16b name2b; /* Second Ego-Item type, if any */
+
+ byte xtra1; /* Extra info type */
+ s16b xtra2; /* Extra info index */
+
+ s16b to_h; /* Plusses to hit */
+ s16b to_d; /* Plusses to damage */
+ s16b to_a; /* Plusses to AC */
+
+ s16b ac; /* Normal AC */
+
+ byte dd;
+ byte ds; /* Damage dice/sides */
+
+ s16b timeout; /* Timeout Counter */
+
+ byte ident; /* Special flags */
+
+ byte marked; /* Object is marked */
+
+ u16b note; /* Inscription index */
+ u16b art_name; /* Artifact name (random artifacts) */
+
+ u32b art_flags1; /* Flags, set 1 Alas, these were necessary */
+ u32b art_flags2; /* Flags, set 2 for the random artifacts of*/
+ u32b art_flags3; /* Flags, set 3 Zangband */
+ u32b art_flags4; /* Flags, set 4 PernAngband */
+ u32b art_flags5; /* Flags, set 5 PernAngband */
+ u32b art_esp; /* Flags, set esp PernAngband */
+
+ s16b next_o_idx; /* Next object in stack (if any) */
+
+ s16b held_m_idx; /* Monster holding us (if any) */
+
+ byte sense; /* Pseudo-id status */
+
+ byte found; /* How did we find it */
+ s16b found_aux1; /* Stores info for found */
+ s16b found_aux2; /* Stores info for found */
+};
+
+/* Pseudo-id defines */
+#define SENSE_NONE 0
+#define SENSE_CURSED 1
+#define SENSE_AVERAGE 2
+#define SENSE_GOOD_LIGHT 3
+#define SENSE_GOOD_HEAVY 4
+#define SENSE_EXCELLENT 5
+#define SENSE_WORTHLESS 6
+#define SENSE_TERRIBLE 7
+#define SENSE_SPECIAL 8
+#define SENSE_BROKEN 9
+#define SENSE_UNCURSED 10
+
+extern object_type o_list[max_o_idx];
+extern object_kind k_info[max_k_idx];
+extern char *k_name;
+extern char *k_text;
+extern artifact_type a_info[max_a_idx];
+extern char *a_name;
+extern char *a_text;
+extern header *e_head;
+extern ego_item_type e_info[max_e_idx];
+extern char *e_name;
+extern char *e_text;
+
+extern s16b m_bonus(int max, int level);
+extern s16b wield_slot_ideal(object_type *o_ptr, bool ideal);
+extern s16b wield_slot(object_type *o_ptr);
+extern void object_flags(object_type *o_ptr, u32b *f1 = 0, u32b *f2 = 0, u32b *f3 = 0, u32b *f4 = 0, u32b *f5 = 0, u32b *esp = 0);
+extern char *lua_object_desc @ object_desc(object_type *o_ptr, int pref, int mode);
+extern bool object_out_desc(object_type *o_ptr, FILE *fff, bool trim_down, bool wait_for_it);
+extern void inven_item_describe(int item);
+extern void inven_item_increase(int item, int num);
+extern bool inven_item_optimize(int item);
+extern void floor_item_describe(int item);
+extern void floor_item_increase(int item, int num);
+extern void floor_item_optimize(int item);
+extern void delete_object_idx(int o_idx);
+extern s16b o_pop(void);
+extern errr get_obj_num_prep(void);
+extern bool ident_all(void);
+extern s16b get_obj_num(int level);
+extern s16b lookup_kind(int tval, int sval);
+extern void object_wipe(object_type *o_ptr);
+extern void object_prep(object_type *o_ptr, int k_idx);
+extern void object_copy(object_type *o_ptr, object_type *j_ptr);
+extern bool inven_carry_okay(object_type *o_ptr);
+extern void apply_magic(object_type *o_ptr, int lev, bool okay, bool good, bool great);
+extern bool make_object(object_type *j_ptr, bool good, bool great, obj_theme theme);
+extern s16b drop_near(object_type *o_ptr, int chance, int y, int x);
+extern object_type *get_object(int item);
+extern object_type *new_object();
+extern void end_object(object_type *o_ptr);
+extern bool get_item @ get_item_aux(int *cp, cptr pmt, cptr str, int mode);
+extern void lua_set_item_tester(int tval, char *fct);
+extern bool is_magestaff();
+extern void identify_pack_fully(void);
+extern s16b inven_carry(object_type *o_ptr, bool final);
+extern s32b calc_total_weight(void);
+extern int get_slot(int slot);
+extern bool is_blessed(object_type *o_ptr);
+extern cptr sense_desc[1000]; /* 1000 is just a hack for tolua */
+extern void object_pickup(int this_o_idx);
+
+$static bool lua_is_artifact(object_type *o_ptr) { return artifact_p(o_ptr); }
+static bool lua_is_artifact@is_artifact(object_type *o_ptr);
+
+$static bool lua_is_aware(object_type *o_ptr) { return object_aware_p(o_ptr); }
+static bool lua_is_aware@is_aware(object_type *o_ptr);
+
+$static bool lua_is_known(object_type *o_ptr) { return object_known_p(o_ptr); }
+static bool lua_is_known@is_known(object_type *o_ptr);
+
+$static void lua_set_aware(object_type *o_ptr) { object_aware(o_ptr); }
+static void lua_set_aware@set_aware(object_type *o_ptr);
+
+$static void lua_set_known(object_type *o_ptr) { object_known(o_ptr); }
+static void lua_set_known@set_known(object_type *o_ptr);
+
+extern byte value_check_aux1(object_type *o_ptr);
+extern byte value_check_aux1_magic(object_type *o_ptr);
+extern byte value_check_aux2(object_type *o_ptr);
+extern byte value_check_aux2_magic(object_type *o_ptr);
+extern byte select_sense(object_type *o_ptr, bool ok_combat, bool ok_magic);
+extern bool psychometry(void);
+
+extern bool remove_curse_object(object_type *o_ptr, bool all);
diff --git a/src/object1.c b/src/object1.c
new file mode 100644
index 00000000..2911d6eb
--- /dev/null
+++ b/src/object1.c
@@ -0,0 +1,6866 @@
+/* File: object1.c */
+
+/* Purpose: Object code, part 1 */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Hack -- note that "TERM_MULTI" is now just "TERM_VIOLET".
+ * We will have to find a cleaner method for "MULTI_HUED" later.
+ * There were only two multi-hued "flavors" (one potion, one food).
+ * Plus five multi-hued "base-objects" (3 dragon scales, one blade
+ * of chaos, and one something else). See the SHIMMER_OBJECTS code
+ * in "dungeon.c" and the object color extractor in "cave.c".
+ */
+#define TERM_MULTI TERM_VIOLET
+
+
+/*
+ * Max sizes of the following arrays
+ */
+#define MAX_ROCKS 62 /* Used with rings (min 58) */
+#define MAX_AMULETS 34 /* Used with amulets (min 30) */
+#define MAX_WOODS 35 /* Used with staffs (min 32) */
+#define MAX_METALS 39 /* Used with wands/rods (min 32/30) */
+#define MAX_COLORS 66 /* Used with potions (min 62) */
+#define MAX_SHROOM 20 /* Used with mushrooms (min 20) */
+#define MAX_TITLES 55 /* Used with scrolls (min 55) */
+#define MAX_SYLLABLES 164 /* Used with scrolls (see below) */
+
+
+/*
+ * Rings (adjectives and colors)
+ */
+
+static cptr ring_adj[MAX_ROCKS] =
+{
+ "Alexandrite", "Amethyst", "Aquamarine", "Azurite", "Beryl",
+ "Bloodstone", "Calcite", "Carnelian", "Corundum", "Diamond",
+ "Emerald", "Fluorite", "Garnet", "Granite", "Jade",
+ "Jasper", "Lapis Lazuli", "Malachite", "Marble", "Moonstone",
+ "Onyx", "Opal", "Pearl", "Quartz", "Quartzite",
+ "Rhodonite", "Ruby", "Sapphire", "Tiger Eye", "Topaz",
+ "Turquoise", "Zircon", "Platinum", "Bronze", "Gold",
+ "Obsidian", "Silver", "Tortoise Shell", "Mithril", "Jet",
+ "Engagement", "Adamantite",
+ "Wire", "Dilithium", "Bone", "Wooden",
+ "Spikard", "Serpent", "Wedding", "Double",
+ "Plain", "Brass", "Scarab", "Shining",
+ "Rusty", "Transparent", "Copper", "Black Opal", "Nickel",
+ "Glass", "Fluorspar", "Agate",
+};
+
+static byte ring_col[MAX_ROCKS] =
+{
+ TERM_GREEN, TERM_VIOLET, TERM_L_BLUE, TERM_L_BLUE, TERM_L_GREEN,
+ TERM_RED, TERM_WHITE, TERM_RED, TERM_SLATE, TERM_WHITE,
+ TERM_GREEN, TERM_L_GREEN, TERM_RED, TERM_L_DARK, TERM_L_GREEN,
+ TERM_UMBER, TERM_BLUE, TERM_GREEN, TERM_WHITE, TERM_L_WHITE,
+ TERM_L_RED, TERM_L_WHITE, TERM_WHITE, TERM_L_WHITE, TERM_L_WHITE,
+ TERM_L_RED, TERM_RED, TERM_BLUE, TERM_YELLOW, TERM_YELLOW,
+ TERM_L_BLUE, TERM_L_UMBER, TERM_WHITE, TERM_L_UMBER, TERM_YELLOW,
+ TERM_L_DARK, TERM_L_WHITE, TERM_GREEN, TERM_L_BLUE, TERM_L_DARK,
+ TERM_YELLOW, TERM_VIOLET,
+ TERM_UMBER, TERM_L_WHITE, TERM_WHITE, TERM_UMBER,
+ TERM_BLUE, TERM_GREEN, TERM_YELLOW, TERM_ORANGE,
+ TERM_YELLOW, TERM_ORANGE, TERM_L_GREEN, TERM_YELLOW,
+ TERM_RED, TERM_WHITE, TERM_UMBER, TERM_L_DARK, TERM_L_WHITE,
+ TERM_WHITE, TERM_BLUE, TERM_L_WHITE
+};
+
+
+/*
+ * Amulets (adjectives and colors)
+ */
+
+static cptr amulet_adj[MAX_AMULETS] =
+{
+ "Amber", "Driftwood", "Coral", "Agate", "Ivory",
+ "Obsidian", "Bone", "Brass", "Bronze", "Pewter",
+ "Tortoise Shell", "Golden", "Azure", "Crystal", "Silver",
+ "Copper", "Amethyst", "Mithril", "Sapphire", "Dragon Tooth",
+ "Carved Oak", "Sea Shell", "Flint Stone", "Ruby", "Scarab",
+ "Origami Paper", "Meteoric Iron", "Platinum", "Glass", "Beryl",
+ "Malachite", "Adamantite", "Mother-of-pearl", "Runed"
+};
+
+static byte amulet_col[MAX_AMULETS] =
+{
+ TERM_YELLOW, TERM_L_UMBER, TERM_WHITE, TERM_L_WHITE, TERM_WHITE,
+ TERM_L_DARK, TERM_WHITE, TERM_ORANGE, TERM_L_UMBER, TERM_SLATE,
+ TERM_GREEN, TERM_YELLOW, TERM_L_BLUE, TERM_L_BLUE, TERM_L_WHITE,
+ TERM_L_UMBER, TERM_VIOLET, TERM_L_BLUE, TERM_BLUE, TERM_L_WHITE,
+ TERM_UMBER, TERM_L_BLUE, TERM_SLATE, TERM_RED, TERM_L_GREEN,
+ TERM_WHITE, TERM_L_DARK, TERM_L_WHITE, TERM_WHITE, TERM_L_GREEN,
+ TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_UMBER
+};
+
+
+/*
+ * Staffs (adjectives and colors)
+ */
+
+static cptr staff_adj[MAX_WOODS] =
+{
+ "Aspen", "Balsa", "Banyan", "Birch", "Cedar",
+ "Cottonwood", "Cypress", "Dogwood", "Elm", "Eucalyptus",
+ "Hemlock", "Hickory", "Ironwood", "Locust", "Mahogany",
+ "Maple", "Mulberry", "Oak", "Pine", "Redwood",
+ "Rosewood", "Spruce", "Sycamore", "Teak", "Walnut",
+ "Mistletoe", "Hawthorn", "Bamboo", "Silver", "Runed",
+ "Golden", "Ashen", "Gnarled", "Ivory", "Willow"
+};
+
+static byte staff_col[MAX_WOODS] =
+{
+ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER,
+ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER,
+ TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER, TERM_L_UMBER, TERM_UMBER,
+ TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_RED,
+ TERM_RED, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER,
+ TERM_GREEN, TERM_L_UMBER, TERM_L_UMBER, TERM_L_WHITE, TERM_UMBER,
+ TERM_YELLOW, TERM_SLATE, TERM_UMBER, TERM_L_WHITE, TERM_L_UMBER
+};
+
+
+/*
+ * Wands (adjectives and colors)
+ */
+
+static cptr wand_adj[MAX_METALS] =
+{
+ "Aluminium", "Cast Iron", "Chromium", "Copper", "Gold",
+ "Iron", "Magnesium", "Molybdenum", "Nickel", "Rusty",
+ "Silver", "Steel", "Tin", "Titanium", "Tungsten",
+ "Zirconium", "Zinc", "Aluminium-Plated", "Copper-Plated", "Gold-Plated",
+ "Nickel-Plated", "Silver-Plated", "Steel-Plated", "Tin-Plated", "Zinc-Plated",
+ "Mithril-Plated", "Mithril", "Runed", "Bronze", "Brass",
+ "Platinum", "Lead", "Lead-Plated", "Ivory" , "Adamantite",
+ "Uridium", "Long", "Short", "Hexagonal"
+};
+
+static byte wand_col[MAX_METALS] =
+{
+ TERM_L_BLUE, TERM_L_DARK, TERM_WHITE, TERM_UMBER, TERM_YELLOW,
+ TERM_SLATE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_RED,
+ TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_WHITE, TERM_WHITE,
+ TERM_L_WHITE, TERM_L_WHITE, TERM_L_BLUE, TERM_L_UMBER, TERM_YELLOW,
+ TERM_L_UMBER, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE,
+ TERM_L_BLUE, TERM_L_BLUE, TERM_UMBER, TERM_L_UMBER, TERM_L_UMBER,
+ TERM_WHITE, TERM_SLATE, TERM_SLATE, TERM_WHITE, TERM_VIOLET,
+ TERM_L_RED, TERM_L_BLUE, TERM_BLUE, TERM_RED
+};
+
+
+/*
+ * Rods (adjectives and colors).
+ * Efficiency -- copied from wand arrays
+ */
+
+static cptr rod_adj[MAX_METALS];
+
+static byte rod_col[MAX_METALS];
+
+
+/*
+ * Mushrooms (adjectives and colors)
+ */
+
+static cptr food_adj[MAX_SHROOM] =
+{
+ "Blue", "Black", "Black Spotted", "Brown", "Dark Blue",
+ "Dark Green", "Dark Red", "Yellow", "Furry", "Green",
+ "Grey", "Light Blue", "Light Green", "Violet", "Red",
+ "Slimy", "Tan", "White", "White Spotted", "Wrinkled",
+};
+
+static byte food_col[MAX_SHROOM] =
+{
+ TERM_BLUE, TERM_L_DARK, TERM_L_DARK, TERM_UMBER, TERM_BLUE,
+ TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_L_WHITE, TERM_GREEN,
+ TERM_SLATE, TERM_L_BLUE, TERM_L_GREEN, TERM_VIOLET, TERM_RED,
+ TERM_SLATE, TERM_L_UMBER, TERM_WHITE, TERM_WHITE, TERM_UMBER
+};
+
+
+/*
+ * Color adjectives and colors, for potions.
+ * Hack -- The first four entries are hard-coded.
+ * (water, apple juice, slime mold juice, something)
+ */
+
+static cptr potion_adj[MAX_COLORS] =
+{
+ "Clear", "Light Brown", "Icky Green", "Strangely Phosphorescent",
+ "Azure", "Blue", "Blue Speckled", "Black", "Brown", "Brown Speckled",
+ "Bubbling", "Chartreuse", "Cloudy", "Copper Speckled", "Crimson", "Cyan",
+ "Dark Blue", "Dark Green", "Dark Red", "Gold Speckled", "Green",
+ "Green Speckled", "Grey", "Grey Speckled", "Hazy", "Indigo",
+ "Light Blue", "Light Green", "Magenta", "Metallic Blue", "Metallic Red",
+ "Metallic Green", "Metallic Purple", "Misty", "Orange", "Orange Speckled",
+ "Pink", "Pink Speckled", "Puce", "Purple", "Purple Speckled",
+ "Red", "Red Speckled", "Silver Speckled", "Smoky", "Tangerine",
+ "Violet", "Vermilion", "White", "Yellow", "Violet Speckled",
+ "Pungent", "Clotted Red", "Viscous Pink", "Oily Yellow", "Gloopy Green",
+ "Shimmering", "Coagulated Crimson", "Yellow Speckled", "Gold",
+ "Manly", "Stinking", "Oily Black", "Ichor", "Ivory White", "Sky Blue",
+};
+
+static byte potion_col[MAX_COLORS] =
+{
+ TERM_WHITE, TERM_L_UMBER, TERM_GREEN, TERM_MULTI,
+ TERM_L_BLUE, TERM_BLUE, TERM_BLUE, TERM_L_DARK, TERM_UMBER, TERM_UMBER,
+ TERM_L_WHITE, TERM_L_GREEN, TERM_WHITE, TERM_L_UMBER, TERM_RED, TERM_L_BLUE,
+ TERM_BLUE, TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_GREEN,
+ TERM_GREEN, TERM_SLATE, TERM_SLATE, TERM_L_WHITE, TERM_VIOLET,
+ TERM_L_BLUE, TERM_L_GREEN, TERM_RED, TERM_BLUE, TERM_RED,
+ TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_ORANGE, TERM_ORANGE,
+ TERM_L_RED, TERM_L_RED, TERM_VIOLET, TERM_VIOLET, TERM_VIOLET,
+ TERM_RED, TERM_RED, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE,
+ TERM_VIOLET, TERM_RED, TERM_WHITE, TERM_YELLOW, TERM_VIOLET,
+ TERM_L_RED, TERM_RED, TERM_L_RED, TERM_YELLOW, TERM_GREEN,
+ TERM_MULTI, TERM_RED, TERM_YELLOW, TERM_YELLOW,
+ TERM_L_UMBER, TERM_UMBER, TERM_L_DARK, TERM_RED, TERM_WHITE, TERM_L_BLUE
+};
+
+
+/*
+ * Syllables for scrolls (must be 1-4 letters each)
+ */
+
+static cptr syllables[MAX_SYLLABLES] =
+{
+ "a", "ab", "ag", "aks", "ala", "an", "ankh", "app",
+ "arg", "arze", "ash", "aus", "ban", "bar", "bat", "bek",
+ "bie", "bin", "bit", "bjor", "blu", "bot", "bu",
+ "byt", "comp", "con", "cos", "cre", "dalf", "dan",
+ "den", "der", "doe", "dok", "eep", "el", "eng", "er", "ere", "erk",
+ "esh", "evs", "fa", "fid", "flit", "for", "fri", "fu", "gan",
+ "gar", "glen", "gop", "gre", "ha", "he", "hyd", "i",
+ "ing", "ion", "ip", "ish", "it", "ite", "iv", "jo",
+ "kho", "kli", "klis", "la", "lech", "man", "mar",
+ "me", "mi", "mic", "mik", "mon", "mung", "mur", "nag", "nej",
+ "nelg", "nep", "ner", "nes", "nis", "nih", "nin", "o",
+ "od", "ood", "org", "orn", "ox", "oxy", "pay", "pet",
+ "ple", "plu", "po", "pot", "prok", "re", "rea", "rhov",
+ "ri", "ro", "rog", "rok", "rol", "sa", "san", "sat",
+ "see", "sef", "seh", "shu", "ski", "sna", "sne", "snik",
+ "sno", "so", "sol", "sri", "sta", "sun", "ta", "tab",
+ "tem", "ther", "ti", "tox", "trol", "tue", "turs", "u",
+ "ulk", "um", "un", "uni", "ur", "val", "viv", "vly",
+ "vom", "wah", "wed", "werg", "wex", "whon", "wun", "x",
+ "yerg", "yp", "zun", "tri", "blaa", "jah", "bul", "on",
+ "foo", "ju", "xuxu"
+};
+
+/*
+ * Hold the titles of scrolls, 6 to 14 characters each
+ * Also keep an array of scroll colors (always WHITE for now)
+ */
+
+static char scroll_adj[MAX_TITLES][16];
+
+static byte scroll_col[MAX_TITLES];
+
+
+/*
+ * Certain items have a flavor
+ * This function is used only by "flavor_init()"
+ */
+static bool object_flavor(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ /* Analyze the item */
+ switch (k_ptr->tval)
+ {
+ case TV_AMULET:
+ {
+ return (0x80 + amulet_col[k_ptr->sval]);
+ }
+
+ case TV_RING:
+ {
+ return (0x90 + ring_col[k_ptr->sval]);
+ }
+
+ case TV_STAFF:
+ {
+ return (0xA0 + staff_col[k_ptr->sval]);
+ }
+
+ case TV_WAND:
+ {
+ return (0xB0 + wand_col[k_ptr->sval]);
+ }
+
+ case TV_ROD:
+ {
+ return (0xC0 + rod_col[k_ptr->sval]);
+ }
+
+ case TV_SCROLL:
+ {
+ return (0xD0 + scroll_col[k_ptr->sval]);
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ {
+ return (0xE0 + potion_col[k_ptr->sval]);
+ }
+
+ case TV_FOOD:
+ {
+ if (k_ptr->sval < SV_FOOD_MIN_FOOD)
+ {
+ return (0xF0 + food_col[k_ptr->sval]);
+ }
+
+ break;
+ }
+ }
+
+ /* No flavor */
+ return (0);
+}
+
+
+void get_table_name(char *out_string)
+{
+ int testcounter = (randint(3)) + 1;
+
+ strcpy(out_string, "'");
+
+ if (randint(3) == 2)
+ {
+ while (testcounter--)
+ strcat(out_string, syllables[(randint(MAX_SYLLABLES)) - 1]);
+ }
+
+ else
+ {
+ char Syllable[80];
+ testcounter = (randint(2)) + 1;
+ while (testcounter--)
+ {
+ get_rnd_line("elvish.txt", Syllable);
+ strcat(out_string, Syllable);
+ }
+ }
+
+ out_string[1] = toupper(out_string[1]);
+
+ strcat(out_string, "'");
+
+ out_string[18] = '\0';
+
+ return;
+}
+
+
+/*
+ * Certain items, if aware, are known instantly
+ * This function is used only by "flavor_init()"
+ *
+ * XXX XXX XXX Add "EASY_KNOW" flag to "k_info.txt" file
+ */
+static bool object_easy_know(int i)
+{
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze the "tval" */
+ switch (k_ptr->tval)
+ {
+ /* Spellbooks */
+ case TV_DRUID_BOOK:
+ case TV_MUSIC_BOOK:
+ case TV_SYMBIOTIC_BOOK:
+ {
+ return (TRUE);
+ }
+
+ /* Simple items */
+ case TV_FLASK:
+ case TV_EGG:
+ case TV_BOTTLE:
+ case TV_SKELETON:
+ case TV_CORPSE:
+ case TV_HYPNOS:
+ case TV_SPIKE:
+ case TV_JUNK:
+ {
+ return (TRUE);
+ }
+
+ /* All Food, Potions, Scrolls, Rods */
+ case TV_FOOD:
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ case TV_BATERIE:
+ {
+ if (k_ptr->flags3 & TR3_NORM_ART)
+ return ( FALSE );
+ return (TRUE);
+ }
+
+ /* Some Rings, Amulets, Lites */
+ case TV_RING:
+ case TV_AMULET:
+ case TV_LITE:
+ {
+ if (k_ptr->flags3 & (TR3_EASY_KNOW)) return (TRUE);
+ return (FALSE);
+ }
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+/*
+ * Prepare the "variable" part of the "k_info" array.
+ *
+ * The "color"/"metal"/"type" of an item is its "flavor".
+ * For the most part, flavors are assigned randomly each game.
+ *
+ * Initialize descriptions for the "colored" objects, including:
+ * Rings, Amulets, Staffs, Wands, Rods, Food, Potions, Scrolls.
+ *
+ * The first 4 entries for potions are fixed (Water, Apple Juice,
+ * Slime Mold Juice, Unused Potion).
+ *
+ * Scroll titles are always between 6 and 14 letters long. This is
+ * ensured because every title is composed of whole words, where every
+ * word is from 1 to 8 letters long (one or two syllables of 1 to 4
+ * letters each), and that no scroll is finished until it attempts to
+ * grow beyond 15 letters. The first time this can happen is when the
+ * current title has 6 letters and the new word has 8 letters, which
+ * would result in a 6 letter scroll title.
+ *
+ * Duplicate titles are avoided by requiring that no two scrolls share
+ * the same first four letters (not the most efficient method, and not
+ * the least efficient method, but it will always work).
+ *
+ * Hack -- make sure everything stays the same for each saved game
+ * This is accomplished by the use of a saved "random seed", as in
+ * "town_gen()". Since no other functions are called while the special
+ * seed is in effect, so this function is pretty "safe".
+ *
+ * Note that the "hacked seed" may provide an RNG with alternating parity!
+ */
+void flavor_init(void)
+{
+ int i, j;
+
+ byte temp_col;
+
+ cptr temp_adj;
+
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant flavors */
+ Rand_value = seed_flavor;
+
+
+ /* Efficiency -- Rods/Wands share initial array */
+ for (i = 0; i < MAX_METALS; i++)
+ {
+ rod_adj[i] = wand_adj[i];
+ rod_col[i] = wand_col[i];
+ }
+
+
+ /* Rings have "ring colors" */
+ for (i = 0; i < MAX_ROCKS; i++)
+ {
+ j = rand_int(MAX_ROCKS);
+ temp_adj = ring_adj[i];
+ ring_adj[i] = ring_adj[j];
+ ring_adj[j] = temp_adj;
+ temp_col = ring_col[i];
+ ring_col[i] = ring_col[j];
+ ring_col[j] = temp_col;
+ }
+
+ /* Amulets have "amulet colors" */
+ for (i = 0; i < MAX_AMULETS; i++)
+ {
+ j = rand_int(MAX_AMULETS);
+ temp_adj = amulet_adj[i];
+ amulet_adj[i] = amulet_adj[j];
+ amulet_adj[j] = temp_adj;
+ temp_col = amulet_col[i];
+ amulet_col[i] = amulet_col[j];
+ amulet_col[j] = temp_col;
+ }
+
+ /* Staffs */
+ for (i = 0; i < MAX_WOODS; i++)
+ {
+ j = rand_int(MAX_WOODS);
+ temp_adj = staff_adj[i];
+ staff_adj[i] = staff_adj[j];
+ staff_adj[j] = temp_adj;
+ temp_col = staff_col[i];
+ staff_col[i] = staff_col[j];
+ staff_col[j] = temp_col;
+ }
+
+ /* Wands */
+ for (i = 0; i < MAX_METALS; i++)
+ {
+ j = rand_int(MAX_METALS);
+ temp_adj = wand_adj[i];
+ wand_adj[i] = wand_adj[j];
+ wand_adj[j] = temp_adj;
+ temp_col = wand_col[i];
+ wand_col[i] = wand_col[j];
+ wand_col[j] = temp_col;
+ }
+
+ /* Rods */
+ for (i = 0; i < MAX_METALS; i++)
+ {
+ j = rand_int(MAX_METALS);
+ temp_adj = rod_adj[i];
+ rod_adj[i] = rod_adj[j];
+ rod_adj[j] = temp_adj;
+ temp_col = rod_col[i];
+ rod_col[i] = rod_col[j];
+ rod_col[j] = temp_col;
+ }
+
+ /* Foods (Mushrooms) */
+ for (i = 0; i < MAX_SHROOM; i++)
+ {
+ j = rand_int(MAX_SHROOM);
+ temp_adj = food_adj[i];
+ food_adj[i] = food_adj[j];
+ food_adj[j] = temp_adj;
+ temp_col = food_col[i];
+ food_col[i] = food_col[j];
+ food_col[j] = temp_col;
+ }
+
+ /* Potions */
+ for (i = 4; i < MAX_COLORS; i++)
+ {
+ j = rand_int(MAX_COLORS - 4) + 4;
+ temp_adj = potion_adj[i];
+ potion_adj[i] = potion_adj[j];
+ potion_adj[j] = temp_adj;
+ temp_col = potion_col[i];
+ potion_col[i] = potion_col[j];
+ potion_col[j] = temp_col;
+ }
+
+ /* Scrolls (random titles, always white) */
+ for (i = 0; i < MAX_TITLES; i++)
+ {
+ /* Get a new title */
+ while (TRUE)
+ {
+ char buf[80];
+
+ bool okay;
+
+ /* Start a new title */
+ buf[0] = '\0';
+
+ /* Collect words until done */
+ while (1)
+ {
+ int q, s;
+
+ char tmp[80];
+
+ /* Start a new word */
+ tmp[0] = '\0';
+
+ /* Choose one or two syllables */
+ s = ((rand_int(100) < 30) ? 1 : 2);
+
+ /* Add a one or two syllable word */
+ for (q = 0; q < s; q++)
+ {
+ /* Add the syllable */
+ strcat(tmp, syllables[rand_int(MAX_SYLLABLES)]);
+ }
+
+ /* Stop before getting too long */
+ if (strlen(buf) + 1 + strlen(tmp) > 15) break;
+
+ /* Add a space */
+ strcat(buf, " ");
+
+ /* Add the word */
+ strcat(buf, tmp);
+ }
+
+ /* Save the title */
+ strcpy(scroll_adj[i], buf + 1);
+
+ /* Assume okay */
+ okay = TRUE;
+
+ /* Check for "duplicate" scroll titles */
+ for (j = 0; j < i; j++)
+ {
+ cptr hack1 = scroll_adj[j];
+ cptr hack2 = scroll_adj[i];
+
+ /* Compare first four characters */
+ if (*hack1++ != *hack2++) continue;
+ if (*hack1++ != *hack2++) continue;
+ if (*hack1++ != *hack2++) continue;
+ if (*hack1++ != *hack2++) continue;
+
+ /* Not okay */
+ okay = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Break when done */
+ if (okay) break;
+ }
+
+ /* All scrolls are white */
+ scroll_col[i] = TERM_WHITE;
+ }
+
+
+ /* Hack -- Use the "complex" RNG */
+ Rand_quick = FALSE;
+
+ /* Analyze every object */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Skip "empty" objects */
+ if (!k_ptr->name) continue;
+
+ /* Extract "flavor" (if any) */
+ k_ptr->flavor = object_flavor(i);
+
+ /* No flavor yields aware */
+ if ((!k_ptr->flavor) && (k_ptr->tval != TV_ROD_MAIN)) k_ptr->aware = TRUE;
+
+ /* Check for "easily known" */
+ k_ptr->easy_know = object_easy_know(i);
+ }
+}
+
+/*
+ * Reset the "visual" lists
+ *
+ * This involves resetting various things to their "default" state.
+ *
+ * If the "prefs" flag is TRUE, then we will also load the appropriate
+ * "user pref file" based on the current setting of the "use_graphics"
+ * flag. This is useful for switching "graphics" on/off.
+ *
+ * The features, objects, and monsters, should all be encoded in the
+ * relevant "font.pref" and/or "graf.prf" files. XXX XXX XXX
+ *
+ * The "prefs" parameter is no longer meaningful. XXX XXX XXX
+ */
+void reset_visuals(void)
+{
+ int i;
+
+ /* Extract some info about terrain features */
+ for (i = 0; i < max_f_idx; i++)
+ {
+ feature_type *f_ptr = &f_info[i];
+
+ /* Assume we will use the underlying values */
+ f_ptr->x_attr = f_ptr->d_attr;
+ f_ptr->x_char = f_ptr->d_char;
+ }
+
+ /* Extract default attr/char code for stores */
+ for (i = 0; i < max_st_idx; i++)
+ {
+ store_info_type *st_ptr = &st_info[i];
+
+ /* Default attr/char */
+ st_ptr->x_attr = st_ptr->d_attr;
+ st_ptr->x_char = st_ptr->d_char;
+ }
+
+ /* Extract default attr/char code for objects */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Default attr/char */
+ k_ptr->x_attr = k_ptr->d_attr;
+ k_ptr->x_char = k_ptr->d_char;
+ }
+
+ /* Extract default attr/char code for monsters */
+ for (i = 0; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Default attr/char */
+ r_ptr->x_attr = r_ptr->d_attr;
+ r_ptr->x_char = r_ptr->d_char;
+ }
+
+ /* Reset attr/char code for ego monster overlay graphics */
+ for (i = 0; i < max_re_idx; i++)
+ {
+ monster_ego *re_ptr = &re_info[i];
+
+ /* Default attr/char */
+ re_ptr->g_attr = 0;
+ re_ptr->g_char = 0;
+ }
+
+ /* Reset attr/char code for race modifier overlay graphics */
+ for (i = 0; i < max_rmp_idx; i++)
+ {
+ player_race_mod *rmp_ptr = &race_mod_info[i];
+
+ /* Default attr/char */
+ rmp_ptr->g_attr = 0;
+ rmp_ptr->g_char = 0;
+ }
+
+ /* Reset attr/char code for trap overlay graphics */
+ for (i = 0; i < max_rmp_idx; i++)
+ {
+ trap_type *t_ptr = &t_info[i];
+
+ /* Default attr/char */
+ t_ptr->g_attr = 0;
+ t_ptr->g_char = 0;
+ }
+
+
+ if (use_graphics)
+ {
+ /* Process "graf.prf" */
+ process_pref_file("graf.prf");
+
+ /*
+ * Hack -- remember graphics mode as an integer value,
+ * for faster processing of map_info()
+ */
+
+ /* IBM-PC pseudo-graphics -- not maintained, but the code is there */
+ if (streq(ANGBAND_SYS, "ibm"))
+ {
+ graphics_mode = GRAPHICS_IBM;
+ }
+
+ /*
+ * Isometric view. Also assumes all the attributes of the "new"
+ * graphics.
+ */
+ else if (streq(ANGBAND_GRAF, "iso"))
+ {
+ graphics_mode = GRAPHICS_ISO;
+ }
+
+ /*
+ * "New" graphics -- supports graphics overlay for traps, ego monsters
+ * and player subraces, and has tiles for lighting effects (row + 1
+ * and row + 2 for "darker" versions of terrain features)
+ */
+ else if (streq(ANGBAND_GRAF, "new"))
+ {
+ graphics_mode = GRAPHICS_NEW;
+ }
+
+ /*
+ * "Old" graphics -- doesn't support graphics overlay and lighting
+ * effects
+ */
+ else if (streq(ANGBAND_GRAF, "old"))
+ {
+ graphics_mode = GRAPHICS_OLD;
+ }
+
+ /* ??? */
+ else
+ {
+ graphics_mode = GRAPHICS_UNKNOWN;
+ }
+ }
+
+ /* Normal symbols */
+ else
+ {
+ /* Process "font.prf" */
+ process_pref_file("font.prf");
+
+ graphics_mode = GRAPHICS_NONE;
+ }
+}
+
+
+
+
+
+
+
+
+
+/*
+ * Obtain the "flags" for an item
+ */
+bool object_flags_no_set = FALSE;
+void object_flags(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Base object */
+ (*f1) = k_ptr->flags1;
+ (*f2) = k_ptr->flags2;
+ (*f3) = k_ptr->flags3;
+ (*f4) = k_ptr->flags4;
+ (*f5) = k_ptr->flags5;
+ (*esp) = k_ptr->esp;
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ (*f1) = a_ptr->flags1;
+ (*f2) = a_ptr->flags2;
+ (*f3) = a_ptr->flags3;
+ (*f4) = a_ptr->flags4;
+ (*f5) = a_ptr->flags5;
+ (*esp) = a_ptr->esp;
+
+ if ((!object_flags_no_set) && (a_ptr->set != -1))
+ apply_flags_set(o_ptr->name1, a_ptr->set, f1, f2, f3, f4, f5, esp);
+ }
+
+ /* Random artifact ! */
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3 || o_ptr->art_flags4 || o_ptr->art_flags5 || o_ptr->art_esp)
+ {
+ (*f1) |= o_ptr->art_flags1;
+ (*f2) |= o_ptr->art_flags2;
+ (*f3) |= o_ptr->art_flags3;
+ (*f4) |= o_ptr->art_flags4;
+ (*f5) |= o_ptr->art_flags5;
+ (*esp) |= o_ptr->art_esp;
+ }
+
+ /* Extra powers */
+ if (!(o_ptr->art_name))
+ {
+ switch (o_ptr->xtra1)
+ {
+ case EGO_XTRA_SUSTAIN:
+ {
+ /* Choose a sustain */
+ switch (o_ptr->xtra2 % 6)
+ {
+ case 0:
+ (*f2) |= (TR2_SUST_STR);
+ break;
+ case 1:
+ (*f2) |= (TR2_SUST_INT);
+ break;
+ case 2:
+ (*f2) |= (TR2_SUST_WIS);
+ break;
+ case 3:
+ (*f2) |= (TR2_SUST_DEX);
+ break;
+ case 4:
+ (*f2) |= (TR2_SUST_CON);
+ break;
+ case 5:
+ (*f2) |= (TR2_SUST_CHR);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_POWER:
+ {
+ /* Choose a power */
+ switch (o_ptr->xtra2 % 11)
+ {
+ case 0:
+ (*f2) |= (TR2_RES_BLIND);
+ break;
+ case 1:
+ (*f2) |= (TR2_RES_CONF);
+ break;
+ case 2:
+ (*f2) |= (TR2_RES_SOUND);
+ break;
+ case 3:
+ (*f2) |= (TR2_RES_SHARDS);
+ break;
+ case 4:
+ (*f2) |= (TR2_RES_NETHER);
+ break;
+ case 5:
+ (*f2) |= (TR2_RES_NEXUS);
+ break;
+ case 6:
+ (*f2) |= (TR2_RES_CHAOS);
+ break;
+ case 7:
+ (*f2) |= (TR2_RES_DISEN);
+ break;
+ case 8:
+ (*f2) |= (TR2_RES_POIS);
+ break;
+ case 9:
+ (*f2) |= (TR2_RES_DARK);
+ break;
+ case 10:
+ (*f2) |= (TR2_RES_LITE);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_ABILITY:
+ {
+ /* Choose an ability */
+ switch (o_ptr->xtra2 % 8)
+ {
+ case 0:
+ (*f3) |= (TR3_FEATHER);
+ break;
+ case 1:
+ (*f3) |= (TR3_LITE1);
+ break;
+ case 2:
+ (*f3) |= (TR3_SEE_INVIS);
+ break;
+ case 3:
+ (*esp) |= (ESP_ALL);
+ break;
+ case 4:
+ (*f3) |= (TR3_SLOW_DIGEST);
+ break;
+ case 5:
+ (*f3) |= (TR3_REGEN);
+ break;
+ case 6:
+ (*f2) |= (TR2_FREE_ACT);
+ break;
+ case 7:
+ (*f2) |= (TR2_HOLD_LIFE);
+ break;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+/* Return object granted power */
+int object_power(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+ int power = -1;
+
+ /* Base object */
+ power = k_ptr->power;
+
+ /* Ego-item */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ if (power == -1) power = e_ptr->power;
+
+ if (o_ptr->name2b)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2b];
+
+ if (power == -1) power = e_ptr->power;
+ }
+ }
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ if (power == -1) power = a_ptr->power;
+ }
+
+ return (power);
+}
+
+
+
+/*
+ * Obtain the "flags" for an item which are known to the player
+ */
+void object_flags_known(object_type *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ bool spoil = FALSE;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Clear */
+ (*f1) = (*f2) = (*f3) = (*f4) = (*esp) = (*f5) = 0L;
+
+ /* Must be identified */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Base object */
+ (*f1) = k_ptr->flags1;
+ (*f2) = k_ptr->flags2;
+ (*f3) = k_ptr->flags3;
+ (*f4) = k_ptr->flags4;
+ (*f5) = k_ptr->flags5;
+ (*esp) = k_ptr->esp;
+
+ (*f1) |= k_ptr->oflags1;
+ (*f2) |= k_ptr->oflags2;
+ (*f3) |= k_ptr->oflags3;
+ (*f4) |= k_ptr->oflags4;
+ (*f5) |= k_ptr->oflags5;
+ (*esp) |= k_ptr->oesp;
+
+#ifdef SPOIL_ARTIFACTS
+ /* Full knowledge for some artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) spoil = TRUE;
+#endif /* SPOIL_ARTIFACTS */
+
+#ifdef SPOIL_EGO_ITEMS
+ /* Full knowledge for some ego-items */
+ if (ego_item_p(o_ptr)) spoil = TRUE;
+#endif /* SPOIL_EGO_ITEMS */
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Need full knowledge or spoilers */
+ if (spoil || (o_ptr->ident & IDENT_MENTAL))
+ {
+ (*f1) = a_ptr->flags1;
+ (*f2) = a_ptr->flags2;
+ (*f3) = a_ptr->flags3;
+ (*f4) = a_ptr->flags4;
+ (*f5) = a_ptr->flags5;
+ (*esp) = a_ptr->esp;
+
+ if ((!object_flags_no_set) && (a_ptr->set != -1))
+ apply_flags_set(o_ptr->name1, a_ptr->set, f1, f2, f3, f4, f5, esp);
+ }
+ else
+ {
+ (*f1) = (*f2) = (*f3) = (*f4) = (*esp) = (*f5) = 0L;
+ }
+
+ (*f1) |= a_ptr->oflags1;
+ (*f2) |= a_ptr->oflags2;
+ (*f3) |= a_ptr->oflags3;
+ (*f4) |= a_ptr->oflags4;
+ (*f5) |= a_ptr->oflags5;
+ (*esp) |= a_ptr->oesp;
+ }
+
+ /* Random artifact or ego item! */
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3 || o_ptr->art_flags4 || o_ptr->art_flags5 || o_ptr->art_esp)
+ {
+ /* Need full knowledge or spoilers */
+ if (spoil || (o_ptr->ident & IDENT_MENTAL))
+ {
+ (*f1) |= o_ptr->art_flags1;
+ (*f2) |= o_ptr->art_flags2;
+ (*f3) |= o_ptr->art_flags3;
+ (*f4) |= o_ptr->art_flags4;
+ (*f5) |= o_ptr->art_flags5;
+ (*esp) |= o_ptr->art_esp;
+ }
+
+ (*f1) |= o_ptr->art_oflags1;
+ (*f2) |= o_ptr->art_oflags2;
+ (*f3) |= o_ptr->art_oflags3;
+ (*f4) |= o_ptr->art_oflags4;
+ (*f5) |= o_ptr->art_oflags5;
+ (*esp) |= o_ptr->art_oesp;
+ }
+
+ /* Full knowledge for *identified* objects */
+ if (!(o_ptr->ident & IDENT_MENTAL)) return;
+
+ if (!(o_ptr->art_name))
+ {
+ /* Extra powers */
+ switch (o_ptr->xtra1)
+ {
+ case EGO_XTRA_SUSTAIN:
+ {
+ /* Choose a sustain */
+ switch (o_ptr->xtra2 % 6)
+ {
+ case 0:
+ (*f2) |= (TR2_SUST_STR);
+ break;
+ case 1:
+ (*f2) |= (TR2_SUST_INT);
+ break;
+ case 2:
+ (*f2) |= (TR2_SUST_WIS);
+ break;
+ case 3:
+ (*f2) |= (TR2_SUST_DEX);
+ break;
+ case 4:
+ (*f2) |= (TR2_SUST_CON);
+ break;
+ case 5:
+ (*f2) |= (TR2_SUST_CHR);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_POWER:
+ {
+ /* Choose a power */
+ switch (o_ptr->xtra2 % 11)
+ {
+ case 0:
+ (*f2) |= (TR2_RES_BLIND);
+ break;
+ case 1:
+ (*f2) |= (TR2_RES_CONF);
+ break;
+ case 2:
+ (*f2) |= (TR2_RES_SOUND);
+ break;
+ case 3:
+ (*f2) |= (TR2_RES_SHARDS);
+ break;
+ case 4:
+ (*f2) |= (TR2_RES_NETHER);
+ break;
+ case 5:
+ (*f2) |= (TR2_RES_NEXUS);
+ break;
+ case 6:
+ (*f2) |= (TR2_RES_CHAOS);
+ break;
+ case 7:
+ (*f2) |= (TR2_RES_DISEN);
+ break;
+ case 8:
+ (*f2) |= (TR2_RES_POIS);
+ break;
+ case 9:
+ (*f2) |= (TR2_RES_DARK);
+ break;
+ case 10:
+ (*f2) |= (TR2_RES_LITE);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_ABILITY:
+ {
+ /* Choose an ability */
+ switch (o_ptr->xtra2 % 8)
+ {
+ case 0:
+ (*f3) |= (TR3_FEATHER);
+ break;
+ case 1:
+ (*f3) |= (TR3_LITE1);
+ break;
+ case 2:
+ (*f3) |= (TR3_SEE_INVIS);
+ break;
+ case 3:
+ (*esp) |= (ESP_ALL);
+ break;
+ case 4:
+ (*f3) |= (TR3_SLOW_DIGEST);
+ break;
+ case 5:
+ (*f3) |= (TR3_REGEN);
+ break;
+ case 6:
+ (*f2) |= (TR2_FREE_ACT);
+ break;
+ case 7:
+ (*f2) |= (TR2_HOLD_LIFE);
+ break;
+ }
+
+ break;
+ }
+ }
+ }
+
+ /* Hack - Res Chaos -> Res Confusion */
+ if (*f2 & TR2_RES_CHAOS) (*f2) |= (TR2_RES_CONF);
+}
+
+
+
+
+
+/*
+ * Print a char "c" into a string "t", as if by sprintf(t, "%c", c),
+ * and return a pointer to the terminator (t + 1).
+ */
+static char *object_desc_chr(char *t, char c)
+{
+ /* Copy the char */
+ *t++ = c;
+
+ /* Terminate */
+ *t = '\0';
+
+ /* Result */
+ return (t);
+}
+
+
+/*
+ * Print a string "s" into a string "t", as if by strcpy(t, s),
+ * and return a pointer to the terminator.
+ */
+static char *object_desc_str(char *t, cptr s)
+{
+ /* Copy the string */
+ while (*s) *t++ = *s++;
+
+ /* Terminate */
+ *t = '\0';
+
+ /* Result */
+ return (t);
+}
+
+/*
+ * Do the actual conversion of a number for object_desc_num() and
+ * object_desc_int().
+ */
+static char *convert_number(char *result, u32b num)
+{
+ char *tp;
+ char temp[11];
+
+ tp = temp;
+ *tp = '0' + (num % 10);
+ for (num /= 10; num != 0; num /= 10)
+ {
+ *++tp = '0' + (num % 10);
+ }
+
+ while (tp != temp)
+ {
+ *result++ = *tp--;
+ }
+ *result++ = *tp;
+ *result = '\0';
+
+ return result;
+}
+
+/*
+ * Print a nnumber "n" into a string "t", as if by
+ * sprintf(t, "%u", n), and return a pointer to the terminator.
+ */
+static char *object_desc_num(char *result, s32b num)
+{
+ u32b n;
+
+ if (num < 0)
+ {
+ *result++ = '-';
+ n = -num;
+ }
+ else
+ n = num;
+
+ /* Result */
+ return convert_number(result, n);
+}
+
+/*
+ * Print an signed number "num" into a string "result", as if by
+ * sprintf(t, "%+d", n), and return a pointer to the terminator.
+ * Note that we always print a sign, either "+" or "-".
+ */
+static char *object_desc_int(char *result, s32b num)
+{
+ u32b n;
+
+ /* Negative */
+ if (num < 0)
+ {
+ /* Take the absolute value */
+ n = -num;
+
+ /* Use a "minus" sign */
+ *result++ = '-';
+ }
+ /* Positive (or zero) */
+ else
+ {
+ /* Use the actual number */
+ n = num;
+
+ /* Use a "plus" sign */
+ *result++ = '+';
+ }
+
+ /* Result */
+ return convert_number(result, n);
+}
+
+/*
+ * Creates a description of the item "o_ptr", and stores it in "out_val".
+ *
+ * One can choose the "verbosity" of the description, including whether
+ * or not the "number" of items should be described, and how much detail
+ * should be used when describing the item.
+ *
+ * The given "buf" must be 80 chars long to hold the longest possible
+ * description, which can get pretty long, including incriptions, such as:
+ * "no more Maces of Disruption (Defender) (+10,+10) [+5] (+3 to stealth)".
+ * Note that the inscription will be clipped to keep the total description
+ * under 79 chars (plus a terminator).
+ *
+ * Note the use of "object_desc_num()" and "object_desc_int()" as hyper-efficient,
+ * portable, versions of some common "sprintf()" commands.
+ *
+ * Note that all ego-items (when known) append an "Ego-Item Name", unless
+ * the item is also an artifact, which should NEVER happen.
+ *
+ * Note that all artifacts (when known) append an "Artifact Name", so we
+ * have special processing for "Specials" (artifact Lites, Rings, Amulets).
+ * The "Specials" never use "modifiers" if they are "known", since they
+ * have special "descriptions", such as "The Necklace of the Dwarves".
+ *
+ * Special Lite's use the "k_info" base-name (Phial, Star, or Arkenstone),
+ * plus the artifact name, just like any other artifact, if known.
+ *
+ * Special Ring's and Amulet's, if not "aware", use the same code as normal
+ * rings and amulets, and if "aware", use the "k_info" base-name (Ring or
+ * Amulet or Necklace). They will NEVER "append" the "k_info" name. But,
+ * they will append the artifact name, just like any artifact, if known.
+ *
+ * None of the Special Rings/Amulets are "EASY_KNOW", though they could be,
+ * at least, those which have no "pluses", such as the three artifact lites.
+ *
+ * Hack -- Display "The One Ring" as "a Plain Gold Ring" until aware.
+ *
+ * If "pref" then a "numeric" prefix will be pre-pended.
+ *
+ * Mode:
+ * 0 -- The Cloak of Death
+ * 1 -- The Cloak of Death [1,+3]
+ * 2 -- The Cloak of Death [1,+3] (+2 to Stealth)
+ * 3 -- The Cloak of Death [1,+3] (+2 to Stealth) {nifty}
+ */
+void object_desc(char *buf, object_type *o_ptr, int pref, int mode)
+{
+ bool hack_name = FALSE;
+ cptr basenm, modstr;
+ int indexx;
+
+ bool aware = FALSE;
+ bool known = FALSE;
+
+ bool append_name = FALSE;
+
+ bool show_weapon = FALSE;
+ bool show_armour = FALSE;
+
+ cptr s, u;
+ char *t;
+
+ char p1 = '(', p2 = ')';
+ char b1 = '[', b2 = ']';
+ char c1 = '{', c2 = '}';
+
+ char tmp_val[160];
+ char tmp_val2[90];
+
+ s32b power;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ cptr str;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ /* See if the object is "aware" */
+ if (object_aware_p(o_ptr)) aware = TRUE;
+
+ /* See if the object is "known" */
+ if (object_known_p(o_ptr)) known = TRUE;
+
+ /* Hack -- Extract the sub-type "indexx" */
+ indexx = o_ptr->sval;
+
+ /* Extract default "base" string */
+ basenm = (k_name + k_ptr->name);
+
+ /* Assume no "modifier" string */
+ modstr = "";
+
+ /* Analyze the object */
+ switch (o_ptr->tval)
+ {
+ /* Some objects are easy to describe */
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_JUNK:
+ case TV_SPIKE:
+ case TV_FLASK:
+ case TV_CHEST:
+ case TV_INSTRUMENT:
+ case TV_TOOL:
+ case TV_DIGGING:
+ {
+ break;
+ }
+
+
+ /* Missiles/ Bows/ Weapons */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ {
+ show_weapon = TRUE;
+ break;
+ }
+
+ /* Trapping Kits */
+ case TV_TRAPKIT:
+ {
+ modstr = basenm;
+ basenm = "& # Trap Set~";
+ break;
+ }
+
+ /* Armour */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ show_armour = TRUE;
+ break;
+ }
+
+
+ /* Lites (including a few "Specials") */
+ case TV_LITE:
+ {
+ break;
+ }
+
+ /* Amulets (including a few "Specials") */
+ case TV_AMULET:
+ {
+ /* Color the object */
+ modstr = amulet_adj[indexx];
+ if (aware) append_name = TRUE;
+
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Amulet~";
+ else
+ basenm = aware ? "& # Amulet~" : "& # Amulet~";
+
+ if (known && !o_ptr->art_name && artifact_p(o_ptr)) basenm = k_ptr->name + k_name;
+
+ break;
+ }
+
+ /* Rings (including a few "Specials") */
+ case TV_RING:
+ {
+ /* Color the object */
+ modstr = ring_adj[indexx];
+ if (aware) append_name = TRUE;
+
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Ring~";
+ else
+ basenm = aware ? "& # Ring~" : "& # Ring~";
+
+ /* Hack -- The One Ring */
+ if (!aware && (o_ptr->sval == SV_RING_POWER)) modstr = "Plain Gold";
+
+ if (known && !o_ptr->art_name && artifact_p(o_ptr)) basenm = k_ptr->name + k_name;
+
+ break;
+ }
+
+ case TV_STAFF:
+ {
+ /* Color the object */
+ modstr = staff_adj[o_ptr->pval2 % MAX_WOODS];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Staff~";
+ else
+ basenm = "& # Staff~";
+ break;
+ }
+
+ case TV_WAND:
+ {
+ /* Color the object */
+ modstr = wand_adj[o_ptr->pval2 % MAX_METALS];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Wand~";
+ else
+ basenm = "& # Wand~";
+ break;
+ }
+
+ case TV_ROD:
+ {
+ /* Color the object */
+ modstr = rod_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Rod Tip~";
+ else
+ basenm = aware ? "& # Rod Tip~" : "& # Rod Tip~";
+ if (o_ptr->sval == SV_ROD_HOME)
+ {
+ basenm = "& Great Rod Tip~ of Home Summoning";
+ hack_name = TRUE;
+ }
+ break;
+ }
+
+ case TV_ROD_MAIN:
+ {
+ object_kind *tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)];
+
+ modstr = k_name + tip_ptr->name;
+ break;
+ }
+
+ case TV_SCROLL:
+ {
+ /* Color the object */
+ modstr = scroll_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Scroll~";
+ else
+ basenm = aware ? "& Scroll~ titled \"#\"" : "& Scroll~ titled \"#\"";
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ {
+ /* Color the object */
+ if ((o_ptr->tval != TV_POTION2) || (o_ptr->sval != SV_POTION2_MIMIC) || (!aware))
+ {
+ modstr = potion_adj[indexx];
+ if (aware) append_name = TRUE;
+ }
+ else
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", o_ptr->pval2, "name", &modstr);
+ }
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Potion~";
+ else
+ basenm = aware ? "& # Potion~" : "& # Potion~";
+ break;
+ }
+
+ case TV_FOOD:
+ {
+ /* Ordinary food is "boring" */
+ if (o_ptr->sval >= SV_FOOD_MIN_FOOD) break;
+
+ /* Color the object */
+ modstr = food_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (((plain_descriptions) && (aware)) || o_ptr->ident & IDENT_STOREB)
+ basenm = "& Mushroom~";
+ else
+ basenm = aware ? "& # Mushroom~" : "& # Mushroom~";
+ break;
+ }
+
+
+ /* Cloak of Mimicry */
+ case TV_CLOAK:
+ {
+ show_armour = TRUE;
+ if (o_ptr->sval == SV_MIMIC_CLOAK)
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", o_ptr->pval2, "obj_name", &modstr);
+ }
+ break;
+ }
+
+
+ case TV_SYMBIOTIC_BOOK:
+ {
+ modstr = basenm;
+ basenm = "& Symbiotic Spellbook~ #";
+ break;
+ }
+
+ case TV_MUSIC_BOOK:
+ {
+ modstr = basenm;
+ basenm = "& Songbook~ #";
+ break;
+ }
+
+ /* Druid Books */
+ case TV_DRUID_BOOK:
+ {
+ modstr = basenm;
+ basenm = "& Elemental Stone~ #";
+ break;
+ }
+
+ case TV_BATERIE:
+ {
+ modstr = basenm;
+ basenm = "& Essence~ of #";
+ break;
+ }
+
+ case TV_PARCHMENT:
+ {
+ modstr = basenm;
+ basenm = "& Parchment~ - #";
+ break;
+ }
+
+
+ /* Hack -- Gold/Gems */
+ case TV_GOLD:
+ {
+ strcpy(buf, basenm);
+ return;
+ }
+
+ case TV_CORPSE:
+ {
+ monster_race* r_ptr = &r_info[o_ptr->pval2];
+ modstr = basenm;
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ basenm = format("& %s's #~", r_name + r_ptr->name);
+ else
+ basenm = format("& %s #~", r_name + r_ptr->name);
+ break;
+ }
+
+ case TV_EGG:
+ {
+ monster_race* r_ptr = &r_info[o_ptr->pval2];
+ modstr = basenm;
+
+ basenm = format("& %s #~", r_name + r_ptr->name);
+ break;
+ }
+
+ case TV_HYPNOS:
+ {
+ /* We print hit points further down. --dsb */
+ monster_race* r_ptr = &r_info[o_ptr->pval];
+ modstr = basenm;
+ basenm = format("& %s~", r_name + r_ptr->name);
+ break;
+ }
+
+ case TV_TOTEM:
+ {
+ char name[80];
+ monster_type monster;
+
+ monster.sr_ptr = 0;
+ monster.r_idx = o_ptr->pval;
+ monster.ego = o_ptr->pval2;
+ monster.ml = TRUE;
+ monster.status = MSTATUS_ENEMY;
+
+ monster_desc(name, &monster, 0x188);
+
+ modstr = basenm;
+ basenm = format("& #~ of %s", name);
+ break;
+ }
+
+ case TV_RANDART:
+ {
+ modstr = basenm;
+
+ if (known)
+ {
+ basenm = random_artifacts[indexx].name_full;
+ }
+ else
+ {
+ basenm = random_artifacts[indexx].name_short;
+ }
+ break;
+ }
+
+ case TV_RUNE2:
+ {
+ if (o_ptr->sval != RUNE_STONE)
+ {
+ modstr = basenm;
+ basenm = "& Rune~ [#]";
+ }
+ break;
+ }
+
+ case TV_RUNE1:
+ {
+ modstr = basenm;
+ basenm = "& Rune~ [#]";
+ break;
+ }
+
+ case TV_DAEMON_BOOK:
+ case TV_BOOK:
+ {
+ basenm = k_name + k_ptr->name;
+ if (o_ptr->sval == 255) modstr = school_spells[o_ptr->pval].name;
+ break;
+ }
+
+ /* Used in the "inventory" routine */
+ default:
+ {
+ if (process_hooks_ret(HOOK_ITEM_NAME, "ss", "(O,s,s)",
+ o_ptr, basenm, modstr))
+ {
+ basenm = process_hooks_return[0].str;
+ modstr = process_hooks_return[1].str;
+ break;
+ }
+ else
+ {
+ strcpy(buf, "(nothing)");
+ return;
+ }
+ }
+ }
+
+ /* Mega Hack */
+ if ((!hack_name) && known && (k_ptr->flags5 & TR5_FULL_NAME)) basenm = (k_name + k_ptr->name);
+
+
+ /* Start dumping the result */
+ t = tmp_val;
+
+ /* The object "expects" a "number" */
+ if (basenm[0] == '&')
+ {
+ monster_race* r_ptr;
+ cptr ego = NULL;
+
+ if (o_ptr->tval == TV_CORPSE) r_ptr = &r_info[o_ptr->pval2];
+ else r_ptr = &r_info[o_ptr->pval];
+
+ /* Grab any ego-item name */
+ if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (e_ptr->before)
+ {
+ ego = e_ptr->name + e_name;
+ }
+ else if (e2_ptr->before)
+ {
+ ego = e2_ptr->name + e_name;
+ }
+ }
+
+ /* Skip the ampersand (and space) */
+ s = basenm + 2;
+
+ /* No prefix */
+ if (pref <= 0)
+ {
+ /* Nothing */
+ }
+
+ /* Hack -- None left */
+ else if (o_ptr->number <= 0)
+ {
+ t = object_desc_str(t, "no more ");
+ }
+
+ /* Extract the number */
+ else if (o_ptr->number > 1)
+ {
+ t = object_desc_num(t, o_ptr->number);
+ t = object_desc_chr(t, ' ');
+ }
+
+ else if ((o_ptr->tval == TV_CORPSE) && (r_ptr->flags1 & RF1_UNIQUE))
+ {}
+
+
+ else if ((o_ptr->tval == TV_HYPNOS) && (r_ptr->flags1 & RF1_UNIQUE))
+ {}
+
+ /* Hack -- The only one of its kind */
+ else if (known && (artifact_p(o_ptr) || o_ptr->art_name))
+ {
+ t = object_desc_str(t, "The ");
+ }
+
+ else if (ego != NULL)
+ {
+ if (is_a_vowel(ego[0]))
+ {
+ t = object_desc_str(t, "an ");
+ }
+ else
+ {
+ t = object_desc_str(t, "a ");
+ }
+ }
+
+ /* A single one, with a vowel in the modifier */
+ else if ((*s == '#') && (is_a_vowel(modstr[0])))
+ {
+ t = object_desc_str(t, "an ");
+ }
+
+ /* A single one, with a vowel */
+ else if (is_a_vowel(*s))
+ {
+ t = object_desc_str(t, "an ");
+ }
+
+ /* A single one, without a vowel */
+ else
+ {
+ t = object_desc_str(t, "a ");
+ }
+
+ /* Grab any ego-item name */
+ if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (e_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ if (e2_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e2_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ }
+
+ /* -TM- Hack -- Add false-artifact names */
+ /* Dagger inscribed {@w0%Smelly} will be named
+ * Smelly Dagger {@w0} */
+
+ if (o_ptr->note)
+ {
+ str = strchr(quark_str(o_ptr->note), '%');
+
+ /* Add the false name */
+ if (str)
+ {
+ t = object_desc_str(t, &str[1]);
+ t = object_desc_chr(t, ' ');
+ }
+ }
+
+ }
+
+ /* Hack -- objects that "never" take an article */
+ else
+ {
+ /* No ampersand */
+ s = basenm;
+
+ /* No pref */
+ if (!pref)
+ {
+ /* Nothing */
+ }
+
+ /* Hack -- all gone */
+ else if (o_ptr->number <= 0)
+ {
+ t = object_desc_str(t, "no more ");
+ }
+
+ /* Prefix a number if required */
+ else if (o_ptr->number > 1)
+ {
+ t = object_desc_num(t, o_ptr->number);
+ t = object_desc_chr(t, ' ');
+ }
+
+ else if (o_ptr->tval == TV_RANDART)
+ {
+ /* Do nothing, since randarts have their prefix already included */
+ }
+
+ /* Hack -- The only one of its kind */
+ else if (known && (artifact_p(o_ptr) || o_ptr->art_name))
+ {
+ t = object_desc_str(t, "The ");
+ }
+
+ /* Hack -- single items get no prefix */
+ else
+ {
+ /* Nothing */
+ }
+
+ /* Grab any ego-item name */
+ if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (e_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ if (e2_ptr->before)
+ {
+ t = object_desc_str(t, (e_name + e2_ptr->name));
+ t = object_desc_chr(t, ' ');
+ }
+ }
+ }
+
+ /* Paranoia -- skip illegal tildes */
+ /* while (*s == '~') s++; */
+
+ /* Copy the string */
+ for (; *s; s++)
+ {
+ /* Pluralizer */
+ if (*s == '~')
+ {
+ /* Add a plural if needed */
+ if ((o_ptr->number != 1) && (pref >= 0))
+ {
+ char k = t[ -1];
+
+ /* XXX XXX XXX Mega-Hack */
+
+ /* Hack -- "Cutlass-es" and "Torch-es" */
+ if ((k == 's') || (k == 'h')) *t++ = 'e';
+
+ /* Add an 's' */
+ *t++ = 's';
+ }
+ }
+
+ /* Modifier */
+ else if (*s == '#')
+ {
+ /* Grab any ego-item name */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ t = object_desc_chr(t, ' ');
+
+ if (known && o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ }
+ }
+
+ /* Insert the modifier */
+ for (u = modstr; *u; u++) *t++ = *u;
+ }
+
+ /* Normal */
+ else
+ {
+ /* Copy */
+ *t++ = *s;
+ }
+ }
+
+ /* Terminate */
+ *t = '\0';
+
+
+ /* Append the "kind name" to the "base name" */
+ if ((append_name) && (!artifact_p(o_ptr)))
+ {
+ t = object_desc_str(t, " of ");
+
+ if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)))
+ {
+ t = object_desc_str(t, school_spells[o_ptr->pval2].name);
+ if (mode >= 1)
+ {
+ s32b bonus = o_ptr->pval3 & 0xFFFF;
+ s32b max = o_ptr->pval3 >> 16;
+
+ t = object_desc_chr(t, '[');
+ t = object_desc_num(t, bonus);
+ t = object_desc_chr(t, '|');
+ t = object_desc_num(t, max);
+ t = object_desc_chr(t, ']');
+ }
+ }
+ else
+ t = object_desc_str(t, (k_name + k_ptr->name));
+ }
+
+ /* Hack -- Append "Artifact" or "Special" names */
+ if (known)
+ {
+
+ /* -TM- Hack -- Add false-artifact names */
+ /* Dagger inscribed {@w0#of Smell} will be named
+ * Dagger of Smell {@w0} */
+ if (o_ptr->note)
+ {
+ str = strchr(quark_str(o_ptr->note), '#');
+
+ /* Add the false name */
+ if (str)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, &str[1]);
+ }
+ }
+
+ /* Is it a new random artifact ? */
+ if (o_ptr->art_name)
+ {
+ t = object_desc_chr(t, ' ');
+
+ t = object_desc_str(t, quark_str(o_ptr->art_name));
+ }
+
+
+ /* Grab any artifact name */
+ else if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Unique corpses don't require another name */
+ if (o_ptr->tval != TV_CORPSE)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, (a_name + a_ptr->name));
+ }
+ }
+
+ /* Grab any ego-item name */
+ else if ((o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN))
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+ ego_item_type *e2_ptr = &e_info[o_ptr->name2b];
+
+ if (o_ptr->name2 && !e_ptr->before)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, (e_name + e_ptr->name));
+ }
+ if (o_ptr->name2b && !e2_ptr->before)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, (e_name + e2_ptr->name));
+ }
+ }
+ }
+
+ /* It contains a spell */
+ if ((known) && (f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1))
+ {
+ t = object_desc_str(t, format(" [%s]", school_spells[o_ptr->pval2].name));
+ }
+
+ /* Add symbiote hp here, after the "fake-artifact" name. --dsb */
+ if (o_ptr->tval == TV_HYPNOS)
+ {
+ t = object_desc_str(t, " (");
+ t = object_desc_num(t, o_ptr->pval2);
+ t = object_desc_str(t, " hp)");
+ }
+
+ /* No more details wanted */
+ if (mode < 1) goto copyback;
+
+ /* Hack -- Some objects can have an exp level */
+ if ((f4 & TR4_LEVELS) && known)
+ {
+ t = object_desc_str(t, " (E:");
+ if (exp_need)
+ {
+ s32b need;
+ /* Formula from check_experience_obj(). */
+ need = player_exp[o_ptr->elevel - 1] * 5 / 2;
+ t = object_desc_num(t, need - o_ptr->exp);
+ }
+ else
+ {
+ t = object_desc_num(t, o_ptr->exp);
+ }
+ t = object_desc_str(t, ", L:");
+ t = object_desc_num(t, o_ptr->elevel);
+ t = object_desc_chr(t, ')');
+ }
+ if ((f4 & TR4_ART_EXP) && known)
+ {
+ t = object_desc_str(t, " (Exp:");
+ t = object_desc_num(t, o_ptr->exp);
+ t = object_desc_chr(t, ')');
+ }
+
+ /* Hack -- Chests must be described in detail */
+ if (o_ptr->tval == TV_CHEST)
+ {
+ /* Not searched yet */
+ if (!known)
+ {
+ /* Nothing */
+ }
+
+ /* May be "empty" */
+ else if (!o_ptr->pval)
+ {
+ t = object_desc_str(t, " (empty)");
+ }
+
+ /* May be "disarmed" */
+ else if (o_ptr->pval < 0)
+ {
+ t = object_desc_str(t, " (disarmed)");
+ }
+
+ /* Describe the traps, if any */
+ else
+ {
+ /* Describe the traps */
+ t = object_desc_str(t, " (");
+ if (t_info[o_ptr->pval].ident)
+ t = object_desc_str(t, t_name + t_info[o_ptr->pval].name);
+ else
+ t = object_desc_str(t, "trapped");
+ t = object_desc_str(t, ")");
+ }
+ }
+
+
+ /* Display the item like a weapon */
+ if (f3 & (TR3_SHOW_MODS)) show_weapon = TRUE;
+
+ /* Display the item like a weapon */
+ if (o_ptr->to_h && o_ptr->to_d) show_weapon = TRUE;
+
+ /* Display the item like armour */
+ if (o_ptr->ac) show_armour = TRUE;
+
+ /* Dump base weapon info */
+ switch (o_ptr->tval)
+ {
+ /* Missiles and Weapons */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+ /* Exploding arrow? */
+ if (o_ptr->pval2 != 0)
+ t = object_desc_str(t, " (exploding)");
+ /* No break, we want to continue the description */
+
+ case TV_BOOMERANG:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_AXE:
+ case TV_SWORD:
+ case TV_DAEMON_BOOK:
+ if ((o_ptr->tval == TV_DAEMON_BOOK) && (o_ptr->sval != SV_DEMONBLADE))
+ break;
+
+ /* Append a "damage" string */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_num(t, o_ptr->dd);
+ t = object_desc_chr(t, 'd');
+ t = object_desc_num(t, o_ptr->ds);
+ t = object_desc_chr(t, p2);
+
+ /* All done */
+ break;
+
+
+ /* Bows get a special "damage string" */
+ case TV_BOW:
+
+ /* Mega-Hack -- Extract the "base power" */
+ power = (o_ptr->sval % 10);
+
+ /* Apply the "Extra Might" flag */
+ if (f3 & (TR3_XTRA_MIGHT)) power += o_ptr->pval;
+
+ /* Append a special "damage" string */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_chr(t, 'x');
+ t = object_desc_num(t, power);
+ t = object_desc_chr(t, p2);
+
+ /* All done */
+ break;
+ }
+
+
+ /* Add the weapon bonuses */
+ if (known)
+ {
+ /* Show the tohit/todam on request */
+ if (show_weapon)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_int(t, o_ptr->to_h);
+ t = object_desc_chr(t, ',');
+ t = object_desc_int(t, o_ptr->to_d);
+ t = object_desc_chr(t, p2);
+ }
+
+ /* Show the tohit if needed */
+ else if (o_ptr->to_h)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_int(t, o_ptr->to_h);
+ if (!(f3 & (TR3_HIDE_TYPE)) || o_ptr->art_name)
+ t = object_desc_str(t, " to accuracy");
+ t = object_desc_chr(t, p2);
+ }
+
+ /* Show the todam if needed */
+ else if (o_ptr->to_d)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_int(t, o_ptr->to_d);
+ if (!(f3 & (TR3_HIDE_TYPE)) || o_ptr->art_name)
+ t = object_desc_str(t, " to damage");
+ t = object_desc_chr(t, p2);
+ }
+ }
+
+
+ /* Add the armor bonuses */
+ if (known)
+ {
+ /* Show the armor class info */
+ if (show_armour)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, b1);
+ t = object_desc_num(t, o_ptr->ac);
+ t = object_desc_chr(t, ',');
+ t = object_desc_int(t, o_ptr->to_a);
+ t = object_desc_chr(t, b2);
+ }
+
+ /* No base armor, but does increase armor */
+ else if (o_ptr->to_a)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, b1);
+ t = object_desc_int(t, o_ptr->to_a);
+ t = object_desc_chr(t, b2);
+ }
+ }
+
+ /* Hack -- always show base armor */
+ else if (show_armour)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, b1);
+ t = object_desc_num(t, o_ptr->ac);
+ t = object_desc_chr(t, b2);
+ }
+
+ if ((f1 & TR1_MANA) && (known) && (o_ptr->pval > 0))
+ {
+ t = object_desc_chr(t, '(');
+ if (munchkin_multipliers)
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 5);
+ }
+ else
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 10);
+ }
+ t = object_desc_str(t, "%)");
+ }
+
+ if ((known) && (f2 & TR2_LIFE) ) /* Can disp neg now -- Improv */
+ {
+ t = object_desc_chr(t, '(');
+ if (munchkin_multipliers)
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 5);
+ }
+ else
+ {
+ t = object_desc_num(t, 100 * o_ptr->pval / 10);
+ }
+ t = object_desc_str(t, "%)");
+ }
+
+ /* No more details wanted */
+ if (mode < 2) goto copyback;
+
+
+ /* Hack -- Wands and Staffs have charges */
+ if (known &&
+ ((o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND)))
+ {
+ /* Dump " (N charges)" */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+ t = object_desc_num(t, o_ptr->pval);
+ t = object_desc_str(t, " charge");
+ if (o_ptr->pval != 1) t = object_desc_chr(t, 's');
+ t = object_desc_chr(t, p2);
+ }
+
+ /*
+ * Hack -- Rods have a "charging" indicator.
+ */
+ else if (known && (o_ptr->tval == TV_ROD_MAIN))
+ {
+ /* Display prettily. */
+ t = object_desc_str(t, " (");
+ t = object_desc_num(t, o_ptr->timeout);
+ t = object_desc_chr(t, '/');
+ t = object_desc_num(t, o_ptr->pval2);
+ t = object_desc_chr(t, ')');
+ }
+
+ /*
+ * Hack -- Rods have a "charging" indicator.
+ */
+ else if (known && (o_ptr->tval == TV_ROD))
+ {
+ /* Display prettily. */
+ t = object_desc_str(t, " (");
+ t = object_desc_num(t, o_ptr->pval);
+ t = object_desc_str(t, " Mana to cast");
+ t = object_desc_chr(t, ')');
+ }
+
+ /* Hack -- Process Lanterns/Torches */
+ else if ((o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE))
+ {
+ /* Hack -- Turns of light for normal lites */
+ t = object_desc_str(t, " (with ");
+ t = object_desc_num(t, o_ptr->timeout);
+ t = object_desc_str(t, " turns of light)");
+ }
+
+
+ /* Dump "pval" flags for wearable items */
+ if (known && ((f1 & (TR1_PVAL_MASK)) || (f5 & (TR5_PVAL_MASK))))
+ {
+ /* Start the display */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, p1);
+
+ /* Dump the "pval" itself */
+ t = object_desc_int(t, o_ptr->pval);
+
+ /* Do not display the "pval" flags */
+ if (f3 & (TR3_HIDE_TYPE))
+ {
+ /* Nothing */
+ }
+
+ /* Speed */
+ else if (f1 & (TR1_SPEED))
+ {
+ /* Dump " to speed" */
+ t = object_desc_str(t, " to speed");
+ }
+
+ /* Attack speed */
+ else if (f1 & (TR1_BLOWS))
+ {
+ /* Add " attack" */
+ t = object_desc_str(t, " attack");
+
+ /* Add "attacks" */
+ if (ABS(o_ptr->pval) != 1) t = object_desc_chr(t, 's');
+ }
+
+ /* Critical chance */
+ else if (f5 & (TR5_CRIT))
+ {
+ /* Add " attack" */
+ t = object_desc_str(t, "% of critical hits");
+ }
+
+ /* Stealth */
+ else if (f1 & (TR1_STEALTH))
+ {
+ /* Dump " to stealth" */
+ t = object_desc_str(t, " to stealth");
+ }
+
+ /* Search */
+ else if (f1 & (TR1_SEARCH))
+ {
+ /* Dump " to searching" */
+ t = object_desc_str(t, " to searching");
+ }
+
+ /* Infravision */
+ else if (f1 & (TR1_INFRA))
+ {
+ /* Dump " to infravision" */
+ t = object_desc_str(t, " to infravision");
+ }
+
+ /* Tunneling */
+ else if (f1 & (TR1_TUNNEL))
+ {
+ /* Nothing */
+ }
+
+ /* Finish the display */
+ t = object_desc_chr(t, p2);
+ }
+
+
+ /* Indicate "charging" artifacts XXX XXX XXX */
+ if (known && (f3 & TR3_ACTIVATE) && o_ptr->timeout)
+ {
+ if(o_ptr->tval == TV_EGG)
+ /* Hack -- Dump " (stopped)" if relevant */
+ t = object_desc_str(t, " (stopped)");
+ else
+ /* Hack -- Dump " (charging)" if relevant */
+ t = object_desc_str(t, " (charging)");
+ }
+
+ /* Indicate "charging" Mage Staffs XXX XXX XXX */
+ if (known && o_ptr->timeout && (is_ego_p(o_ptr, EGO_MSTAFF_SPELL)))
+ {
+ /* Hack -- Dump " (charging spell1)" if relevant */
+ t = object_desc_str(t, " (charging spell1)");
+ }
+ if (known && o_ptr->xtra2 && (is_ego_p(o_ptr, EGO_MSTAFF_SPELL)))
+ {
+ /* Hack -- Dump " (charging spell2)" if relevant */
+ t = object_desc_str(t, " (charging spell2)");
+ }
+
+
+ /* No more details wanted */
+ if (mode < 3) goto copyback;
+
+
+ /* No inscription yet */
+ tmp_val2[0] = '\0';
+
+ /* Sensed stuff */
+ if (o_ptr->ident & (IDENT_SENSE))
+ {
+ strcpy(tmp_val2, sense_desc[o_ptr->sense]);
+ }
+
+ /* Hack - Note "cursed" if the item is 'known' and cursed */
+ if (cursed_p(o_ptr) && (known) && (!tmp_val2[0]))
+ {
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+ strcat(tmp_val2, "cursed");
+ }
+
+ /* Use the standard inscription if available */
+ if (o_ptr->note)
+ {
+ char *u = tmp_val2;
+
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+
+ strcat(tmp_val2, quark_str(o_ptr->note));
+
+ for (; *u && (*u != '#') && (*u != '%'); u++);
+
+ *u = '\0';
+ }
+
+ /* Mega-Hack -- note empty wands/staffs */
+ if (!known && (o_ptr->ident & (IDENT_EMPTY)))
+ {
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+ strcat(tmp_val2, "empty");
+ }
+
+ /* Note "tried" if the object has been tested unsuccessfully */
+ if (!aware && object_tried_p(o_ptr))
+ {
+ if (tmp_val2[0]) strcat(tmp_val2, ", ");
+ strcpy(tmp_val2, "tried");
+ }
+
+ /* Note the discount, if any */
+ if ((o_ptr->discount) && (!tmp_val2[0]))
+ {
+ object_desc_num(tmp_val2, o_ptr->discount);
+ strcat(tmp_val2, "% off");
+ }
+
+ /* Append the inscription, if any */
+ if (tmp_val2[0])
+ {
+ int n;
+
+ /* Hack -- How much so far */
+ n = (t - tmp_val);
+
+ /* Paranoia -- do not be stupid */
+ if (n > 75) n = 75;
+
+ /* Hack -- shrink the inscription */
+ tmp_val2[75 - n] = '\0';
+
+ /* Append the inscription */
+ t = object_desc_chr(t, ' ');
+ t = object_desc_chr(t, c1);
+ t = object_desc_str(t, tmp_val2);
+ t = object_desc_chr(t, c2);
+ }
+copyback:
+ /* Here's where we dump the built string into buf. */
+ tmp_val[79] = '\0';
+ t = tmp_val;
+ while ((*(buf++) = *(t++))); /* copy the string over */
+}
+
+
+/*
+ * Hack -- describe an item currently in a store's inventory
+ * This allows an item to *look* like the player is "aware" of it
+ */
+void object_desc_store(char *buf, object_type *o_ptr, int pref, int mode)
+{
+ /* Save the "aware" flag */
+ bool hack_aware = k_info[o_ptr->k_idx].aware;
+
+ /* Save the "known" flag */
+ bool hack_known = (o_ptr->ident & (IDENT_KNOWN)) ? TRUE : FALSE;
+
+
+ /* Set the "known" flag */
+ o_ptr->ident |= (IDENT_KNOWN);
+
+ /* Force "aware" for description */
+ k_info[o_ptr->k_idx].aware = TRUE;
+
+
+ /* Describe the object */
+ object_desc(buf, o_ptr, pref, mode);
+
+
+ /* Restore "aware" flag */
+ k_info[o_ptr->k_idx].aware = hack_aware;
+
+ /* Clear the known flag */
+ if (!hack_known) o_ptr->ident &= ~(IDENT_KNOWN);
+}
+
+
+
+
+/*
+ * Determine the "Activation" (if any) for an artifact
+ * Return a string, or NULL for "no activation"
+ */
+cptr item_activation(object_type *o_ptr, byte num)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Needed hacks */
+ static char rspell[2][80];
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Require activation ability */
+ if (!(f3 & (TR3_ACTIVATE))) return (NULL);
+
+
+ /*
+ * We need to deduce somehow that it is a random artifact -- one
+ * problem: It could be a random artifact which has NOT YET received
+ * a name. Thus we eliminate other possibilities instead of checking
+ * for art_name
+ */
+
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ int gf, mod, mana;
+
+ if (!num)
+ {
+ gf = o_ptr->pval & 0xFFFF;
+ mod = o_ptr->pval3 & 0xFFFF;
+ mana = o_ptr->pval2 & 0xFF;
+ }
+ else
+ {
+ gf = o_ptr->pval >> 16;
+ mod = o_ptr->pval3 >> 16;
+ mana = o_ptr->pval2 >> 8;
+ }
+ sprintf(rspell[num], "runespell(%s, %s, %d) every %d turns",
+ k_info[lookup_kind(TV_RUNE1, gf)].name + k_name,
+ k_info[lookup_kind(TV_RUNE2, mod)].name + k_name,
+ mana, mana * 5);
+ return rspell[num];
+ }
+
+ if (o_ptr->tval == TV_EGG)
+ {
+ return "stop or resume the egg development";
+ }
+
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ if (!((o_ptr->name1 && a_info[o_ptr->name1].activate) ||
+ (o_ptr->name2 && e_info[o_ptr->name2].activate) ||
+ (o_ptr->name2b && e_info[o_ptr->name2b].activate)))
+ {
+ if (o_ptr->sval == SV_HORN)
+ {
+ return "aggravate monster every 100 turns";
+ }
+ }
+ }
+
+ if (process_hooks_ret(HOOK_ACTIVATE_DESC, "s", "(O)", o_ptr))
+ return (process_hooks_return[0].str);
+
+ return activation_aux(o_ptr, FALSE, 0);
+}
+
+/* Grab the tval desc */
+bool grab_tval_desc(int tval)
+{
+ int tv = 0;
+
+ while (tval_descs[tv].tval && (tval_descs[tv].tval != tval))
+ {
+ tv++;
+ }
+
+ if (!tval_descs[tv].tval) return FALSE;
+
+ text_out_c(TERM_L_BLUE, tval_descs[tv].desc);
+ text_out("\n");
+
+ return TRUE;
+}
+
+#define CHECK_FIRST(txt, first) \
+if ((first)) { (first) = FALSE; text_out((txt)); } else text_out(", ");
+
+/*
+ * Display the damage done with a multiplier
+ */
+void output_dam(object_type *o_ptr, int mult, int mult2, cptr against, cptr against2, bool *first)
+{
+ int dam;
+
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5 * mult;
+ dam += (o_ptr->to_d + p_ptr->to_d + p_ptr->to_d_melee) * 10;
+ dam *= p_ptr->num_blow;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against));
+
+ if (mult2)
+ {
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5 * mult2;
+ dam += (o_ptr->to_d + p_ptr->to_d + p_ptr->to_d_melee) * 10;
+ dam *= p_ptr->num_blow;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against2));
+ }
+}
+
+/*
+ * Outputs the damage we do/would do with the weapon
+ */
+void display_weapon_damage(object_type *o_ptr)
+{
+ object_type forge, *old_ptr = &forge;
+ u32b f1, f2, f3, f4, f5, esp;
+ bool first = TRUE;
+ bool full = o_ptr->ident & (IDENT_MENTAL);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Ok now the hackish stuff, we replace the current weapon with this one */
+ object_copy(old_ptr, &p_ptr->inventory[INVEN_WIELD]);
+ object_copy(&p_ptr->inventory[INVEN_WIELD], o_ptr);
+ calc_bonuses(TRUE);
+
+ text_out("\nUsing it you would have ");
+ text_out_c(TERM_L_GREEN, format("%d ", p_ptr->num_blow));
+ text_out(format("blow%s and do an average damage per turn of ", (p_ptr->num_blow) ? "s" : ""));
+
+ if (full && (f1 & TR1_SLAY_ANIMAL)) output_dam(o_ptr, 2, 0, "animals", NULL, &first);
+ if (full && (f1 & TR1_SLAY_EVIL)) output_dam(o_ptr, 2, 0, "evil creatures", NULL, &first);
+ if (full && (f1 & TR1_SLAY_ORC)) output_dam(o_ptr, 3, 0, "orcs", NULL, &first);
+ if (full && (f1 & TR1_SLAY_TROLL)) output_dam(o_ptr, 3, 0, "trolls", NULL, &first);
+ if (full && (f1 & TR1_SLAY_GIANT)) output_dam(o_ptr, 3, 0, "giants", NULL, &first);
+ if (full && (f1 & TR1_KILL_DRAGON)) output_dam(o_ptr, 5, 0, "dragons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DRAGON)) output_dam(o_ptr, 3, 0, "dragons", NULL, &first);
+ if (full && (f5 & TR5_KILL_UNDEAD)) output_dam(o_ptr, 5, 0, "undead", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_UNDEAD)) output_dam(o_ptr, 3, 0, "undead", NULL, &first);
+ if (full && (f5 & TR5_KILL_DEMON)) output_dam(o_ptr, 5, 0, "demons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DEMON)) output_dam(o_ptr, 3, 0, "demons", NULL, &first);
+
+ if (full && (f1 & TR1_BRAND_FIRE)) output_dam(o_ptr, 3, 6, "non fire resistant creatures", "fire susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_COLD)) output_dam(o_ptr, 3, 6, "non cold resistant creatures", "cold susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ELEC)) output_dam(o_ptr, 3, 6, "non lightning resistant creatures", "lightning susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ACID)) output_dam(o_ptr, 3, 6, "non acid resistant creatures", "acid susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_POIS)) output_dam(o_ptr, 3, 6, "non poison resistant creatures", "poison susceptible creatures", &first);
+
+ output_dam(o_ptr, 1, 0, (first) ? "all monsters" : "other monsters", NULL, &first);
+
+ text_out(".");
+
+ /* get our weapon back */
+ object_copy(&p_ptr->inventory[INVEN_WIELD], old_ptr);
+ calc_bonuses(TRUE);
+}
+
+/*
+ * Display the ammo damage done with a multiplier
+ */
+void output_ammo_dam(object_type *o_ptr, int mult, int mult2, cptr against, cptr against2, bool *first)
+{
+ int dam;
+ object_type *b_ptr = &p_ptr->inventory[INVEN_BOW];
+ int is_boomerang = (o_ptr->tval == TV_BOOMERANG);
+ int tmul = get_shooter_mult(b_ptr) + p_ptr->xtra_might;
+ if (is_boomerang) tmul = p_ptr->throw_mult;
+
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5;
+ dam += o_ptr->to_d * 10;
+ if (!is_boomerang) dam += b_ptr->to_d * 10;
+ dam *= tmul;
+ if (!is_boomerang) dam += (p_ptr->to_d_ranged) * 10;
+ dam *= mult;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against));
+
+ if (mult2)
+ {
+ dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5;
+ dam += o_ptr->to_d * 10;
+ if (!is_boomerang) dam += b_ptr->to_d * 10;
+ dam *= tmul;
+ if (!is_boomerang) dam += (p_ptr->to_d_ranged) * 10;
+ dam *= mult2;
+ CHECK_FIRST("", *first);
+ if (dam > 0)
+ {
+ if (dam % 10)
+ text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10));
+ else
+ text_out_c(TERM_L_GREEN, format("%d", dam / 10));
+ }
+ else
+ text_out_c(TERM_L_RED, "0");
+ text_out(format(" against %s", against2));
+ }
+}
+
+/*
+ * Outputs the damage we do/would do with the current bow and this ammo
+ */
+void display_ammo_damage(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ bool first = TRUE;
+ int i;
+ bool full = o_ptr->ident & (IDENT_MENTAL);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (o_ptr->tval == TV_BOOMERANG)
+ text_out("\nUsing it you would do an average damage per throw of ");
+ else
+ text_out("\nUsing it with your current shooter you would do an average damage per shot of ");
+ if (full && (f1 & TR1_SLAY_ANIMAL)) output_ammo_dam(o_ptr, 2, 0, "animals", NULL, &first);
+ if (full && (f1 & TR1_SLAY_EVIL)) output_ammo_dam(o_ptr, 2, 0, "evil creatures", NULL, &first);
+ if (full && (f1 & TR1_SLAY_ORC)) output_ammo_dam(o_ptr, 3, 0, "orcs", NULL, &first);
+ if (full && (f1 & TR1_SLAY_TROLL)) output_ammo_dam(o_ptr, 3, 0, "trolls", NULL, &first);
+ if (full && (f1 & TR1_SLAY_GIANT)) output_ammo_dam(o_ptr, 3, 0, "giants", NULL, &first);
+ if (full && (f1 & TR1_KILL_DRAGON)) output_ammo_dam(o_ptr, 5, 0, "dragons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DRAGON)) output_ammo_dam(o_ptr, 3, 0, "dragons", NULL, &first);
+ if (full && (f5 & TR5_KILL_UNDEAD)) output_ammo_dam(o_ptr, 5, 0, "undeads", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_UNDEAD)) output_ammo_dam(o_ptr, 3, 0, "undeads", NULL, &first);
+ if (full && (f5 & TR5_KILL_DEMON)) output_ammo_dam(o_ptr, 5, 0, "demons", NULL, &first);
+ else if (full && (f1 & TR1_SLAY_DEMON)) output_ammo_dam(o_ptr, 3, 0, "demons", NULL, &first);
+
+ if (full && (f1 & TR1_BRAND_FIRE)) output_ammo_dam(o_ptr, 3, 6, "non fire resistant creatures", "fire susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_COLD)) output_ammo_dam(o_ptr, 3, 6, "non cold resistant creatures", "cold susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ELEC)) output_ammo_dam(o_ptr, 3, 6, "non lightning resistant creatures", "lightning susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_ACID)) output_ammo_dam(o_ptr, 3, 6, "non acid resistant creatures", "acid susceptible creatures", &first);
+ if (full && (f1 & TR1_BRAND_POIS)) output_ammo_dam(o_ptr, 3, 6, "non poison resistant creatures", "poison susceptible creatures", &first);
+
+ output_ammo_dam(o_ptr, 1, 0, (first) ? "all monsters" : "other monsters", NULL, &first);
+ text_out(". ");
+
+ if (o_ptr->pval2)
+ {
+ text_out("The explosion will be ");
+ i = 0;
+ while (gf_names[i].gf != -1)
+ {
+ if (gf_names[i].gf == o_ptr->pval2)
+ break;
+ i++;
+ }
+ text_out_c(TERM_L_GREEN, (gf_names[i].gf != -1) ? gf_names[i].name : "something weird");
+ text_out(".");
+ }
+}
+
+/*
+ * Describe a magic stick powers
+ */
+void describe_device(object_type *o_ptr)
+{
+ /* Wands/... of shcool spell */
+ if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)) && object_known_p(o_ptr))
+ {
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ text_out("\nSpell description:");
+ exec_lua(format("print_device_desc(%d)", o_ptr->pval2));
+
+ text_out("\nSpell level: ");
+ text_out_c(TERM_L_BLUE, string_exec_lua(format("return tostring(get_level(%d, 50, 0))", o_ptr->pval2)));
+
+ text_out("\nMinimum Magic Device level to increase spell level: ");
+ text_out_c(TERM_L_BLUE, format("%d", school_spells[o_ptr->pval2].skill_level));
+
+ text_out("\nSpell fail: ");
+ text_out_c(TERM_GREEN, string_exec_lua(format("return tostring(spell_chance(%d))", o_ptr->pval2)));
+
+ text_out("\nSpell info: ");
+ text_out_c(TERM_YELLOW, string_exec_lua(format("return __spell_info[%d]()", o_ptr->pval2)));
+
+ /* Leave device mode */
+ unset_stick_mode();
+
+ text_out("\n");
+ }
+}
+
+
+/*
+ * Helper for object_out_desc
+ *
+ * Print the level something was found on
+ *
+ */
+static cptr object_out_desc_where_found(s16b level, s16b dungeon)
+{
+ static char str[80];
+
+ if (dungeon == DUNGEON_WILDERNESS)
+ /* Taking care of older objects */
+ if (level == 0)
+ sprintf(str, "in the wilderness or in a town");
+ else if (wf_info[level].terrain_idx == TERRAIN_TOWN)
+ sprintf(str, "in the town of %s", wf_info[level].name + wf_name);
+ else
+ sprintf(str, "in %s", wf_info[level].text + wf_text);
+ else
+ sprintf(str, "on level %d of %s", level, d_info[dungeon].name + d_name);
+
+ return str;
+}
+
+/*
+ * Describe an item
+ */
+bool object_out_desc(object_type *o_ptr, FILE *fff, bool trim_down, bool wait_for_it)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char *txt;
+
+ cptr vp[64];
+ byte vc[64];
+ int vn;
+
+ bool first = TRUE;
+
+ /* Extract the flags */
+ if ((!(o_ptr->ident & (IDENT_MENTAL))) && (!fff))
+ {
+ f1 = o_ptr->art_oflags1;
+ f2 = o_ptr->art_oflags2;
+ f3 = o_ptr->art_oflags3;
+ f4 = o_ptr->art_oflags4;
+ f5 = o_ptr->art_oflags5;
+ esp = o_ptr->art_oesp;
+ }
+ else
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ }
+
+ if (fff)
+ {
+ /* Set up stuff for text_out */
+ text_out_file = fff;
+ text_out_hook = text_out_to_file;
+ }
+ else
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Set up stuff for text_out */
+ text_out_hook = text_out_to_screen;
+ text_out("\n");
+ }
+
+ /* No need to dump that */
+ if (!fff)
+ {
+ if (!trim_down) grab_tval_desc(o_ptr->tval);
+ }
+
+ if (object_known_p(o_ptr))
+ {
+ if (o_ptr->k_idx && (!trim_down))
+ {
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ text_out_c(TERM_ORANGE, k_text + k_ptr->text);
+ text_out("\n");
+ }
+
+ if (o_ptr->name1 && (!trim_down))
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ text_out_c(TERM_YELLOW, a_text + a_ptr->text);
+ text_out("\n");
+
+ if (a_ptr->set != -1)
+ {
+ set_type *set_ptr = &set_info[a_ptr->set];
+
+ text_out_c(TERM_GREEN, set_text + set_ptr->desc);
+ text_out("\n");
+ }
+ }
+
+ if ((f4 & TR4_LEVELS) && (!trim_down))
+ {
+ int j = 0;
+
+ if (count_bits(o_ptr->pval3) == 0) text_out("It is sentient");
+ else if (count_bits(o_ptr->pval3) > 1) text_out("It is sentient and can have access to the realms of ");
+ else text_out("It is sentient and can have access to the realm of ");
+
+ first = TRUE;
+ txt = "";
+ for (j = 0; j < MAX_FLAG_GROUP; j++)
+ {
+ if (BIT(j) & o_ptr->pval3)
+ {
+ CHECK_FIRST(txt, first);
+ text_out_c(flags_groups[j].color, flags_groups[j].name);
+ }
+ }
+
+ text_out(". ");
+ }
+
+ if (f4 & TR4_ULTIMATE)
+ {
+ if ((wield_slot(o_ptr) == INVEN_WIELD) ||
+ (wield_slot(o_ptr) == INVEN_BOW))
+ text_out_c(TERM_VIOLET, "It is part of the trinity of the ultimate weapons. ");
+ else
+ text_out_c(TERM_VIOLET, "It is the ultimate armor. ");
+ }
+
+ if (f4 & TR4_COULD2H) text_out("It can be wielded two-handed. ");
+ if (f4 & TR4_MUST2H) text_out("It must be wielded two-handed. ");
+
+ /* Mega-Hack -- describe activation */
+ if (f3 & (TR3_ACTIVATE))
+ {
+ text_out("It can be activated for ");
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ text_out(item_activation(o_ptr, 0));
+ text_out(" and ");
+ text_out(item_activation(o_ptr, 1));
+ }
+ else
+ text_out(item_activation(o_ptr, 0));
+
+ /* Mega-hack -- get rid of useless line for e.g. randarts */
+ if (f5 & (TR5_ACTIVATE_NO_WIELD))
+ text_out(". ");
+ else
+ text_out(" if it is being worn. ");
+ }
+ /* Granted power */
+ if (object_power(o_ptr) != -1)
+ {
+ text_out("It grants you the power of ");
+ text_out(powers_type[object_power(o_ptr)].name);
+ text_out(" if it is being worn. ");
+ }
+
+ /* Hack -- describe lites */
+ if ((o_ptr->tval == TV_LITE) || (f3 & TR3_LITE1) || (f4 & TR4_LITE2) || (f4 & TR4_LITE3))
+ {
+ int radius = 0;
+
+ if (f3 & TR3_LITE1) radius++;
+ if (f4 & TR4_LITE2) radius += 2;
+ if (f4 & TR4_LITE3) radius += 3;
+ if (radius > 5) radius = 5;
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ text_out(format("It provides light (radius %d) when fueled. ", radius));
+ }
+ else
+ {
+ text_out(format("It provides light (radius %d) forever. ", radius));
+ }
+ }
+
+ /* Mega Hack^3 -- describe the Anchor of Space-time */
+ if (o_ptr->name1 == ART_ANCHOR)
+ {
+ text_out("It prevents the space-time continuum from being disrupted. ");
+ }
+
+ if ((f4 & (TR4_ANTIMAGIC_50)) || (f4 & (TR4_ANTIMAGIC_30)) || (f4 & (TR4_ANTIMAGIC_20)) || (f4 & (TR4_ANTIMAGIC_10)))
+ {
+ text_out("It generates an antimagic field. ");
+ }
+
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ if (o_ptr->pval2 == -1)
+ text_out("It can be used to store a spell. ");
+ else
+ text_out("It has a spell stored inside. ");
+ }
+
+ /* Pick up stat bonuses */
+ vn = 0;
+ if (f1 & (TR1_STR)) vp[vn++] = "strength";
+ if (f1 & (TR1_INT)) vp[vn++] = "intelligence";
+ if (f1 & (TR1_WIS)) vp[vn++] = "wisdom";
+ if (f1 & (TR1_DEX)) vp[vn++] = "dexterity";
+ if (f1 & (TR1_CON)) vp[vn++] = "constitution";
+ if (f1 & (TR1_CHR)) vp[vn++] = "charisma";
+ if ((o_ptr->tval != TV_TRAPKIT) && (f1 & (TR1_STEALTH))) vp[vn++] = "stealth";
+ if (f1 & (TR1_SEARCH)) vp[vn++] = "searching";
+ if (f1 & (TR1_INFRA)) vp[vn++] = "infravision";
+ if (f1 & (TR1_TUNNEL)) vp[vn++] = "ability to tunnel";
+ if (f1 & (TR1_SPEED)) vp[vn++] = "speed";
+ if (f1 & (TR1_BLOWS)) vp[vn++] = "attack speed";
+ if (f5 & (TR5_CRIT)) vp[vn++] = "ability to score critical hits";
+ if (f5 & (TR5_LUCK)) vp[vn++] = "luck";
+ if (f1 & (TR1_SPELL)) vp[vn++] = "spell power";
+
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It ");
+
+ /* What it does */
+ if (o_ptr->pval > 0) text_out("increases ");
+ else text_out("decreases ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("your ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+
+ text_out(" by ");
+ if (o_ptr->pval > 0)
+ text_out_c(TERM_L_GREEN, format("%i", o_ptr->pval));
+ else
+ text_out_c(TERM_L_RED, format("%i", -o_ptr->pval));
+ text_out(". ");
+ }
+
+
+ vn = 0;
+ if (f1 & (TR1_MANA)) vp[vn++] = "mana capacity";
+ if (f2 & (TR2_LIFE)) vp[vn++] = "hit points";
+
+ /* Describe with percentuals */
+ if (vn)
+ {
+ int percent;
+
+ /* What it does */
+ if (o_ptr->pval > 0)
+ text_out("It increases");
+ else
+ text_out("It decreases");
+
+ text_out(" your ");
+ text_out(vp[0]);
+ if (vn == 2)
+ {
+ text_out(" and ");
+ text_out(vp[1]);
+ }
+
+ text_out(" by ");
+ percent = 100 * o_ptr->pval / ( munchkin_multipliers ? 5 : 10 );
+
+
+ if (o_ptr->pval > 0)
+ text_out_c(TERM_L_GREEN, format("%i%%", percent));
+ else
+ text_out_c(TERM_L_RED, format("%i%%", -percent));
+ text_out(". ");
+ }
+
+ if ((o_ptr->tval == TV_TRAPKIT) && (f1 & (TR1_STEALTH)))
+ {
+ text_out("It is well-hidden. ");
+ }
+
+ vn = 0;
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ vc[vn] = TERM_GREEN;
+ vp[vn++] = "acid";
+ }
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ vc[vn] = TERM_L_BLUE;
+ vp[vn++] = "electricity";
+ }
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ vc[vn] = TERM_RED;
+ vp[vn++] = "fire";
+ }
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ vc[vn] = TERM_L_WHITE;
+ vp[vn++] = "frost";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It does extra damage ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("from ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out_c(vc[i], vp[i]);
+ }
+ text_out(". ");
+ }
+
+
+ if (f1 & (TR1_BRAND_POIS))
+ {
+ text_out("It ");
+ text_out_c(TERM_L_GREEN, "poisons your foes");
+ text_out(". ");
+ }
+
+ if (f1 & (TR1_CHAOTIC))
+ {
+ text_out("It produces chaotic effects. ");
+ }
+
+ if (f1 & (TR1_VAMPIRIC))
+ {
+ text_out("It drains life from your foes. ");
+ }
+
+ if (f1 & (TR1_IMPACT))
+ {
+ text_out("It can cause earthquakes. ");
+ }
+
+ if (f1 & (TR1_VORPAL))
+ {
+ text_out("It is very sharp and can cut your foes. ");
+ }
+
+ if (f5 & (TR5_WOUNDING))
+ {
+ text_out("It is very sharp and can make your foes bleed. ");
+ }
+
+ if (f1 & (TR1_KILL_DRAGON))
+ {
+ text_out("It is a great bane of dragons. ");
+ }
+ else if (f1 & (TR1_SLAY_DRAGON))
+ {
+ text_out("It is especially deadly against dragons. ");
+ }
+ if (f1 & (TR1_SLAY_ORC))
+ {
+ text_out("It is especially deadly against orcs. ");
+ }
+ if (f1 & (TR1_SLAY_TROLL))
+ {
+ text_out("It is especially deadly against trolls. ");
+ }
+ if (f1 & (TR1_SLAY_GIANT))
+ {
+ text_out("It is especially deadly against giants. ");
+ }
+ if (f5 & (TR5_KILL_DEMON))
+ {
+ text_out("It is a great bane of demons. ");
+ }
+ else if (f1 & (TR1_SLAY_DEMON))
+ {
+ text_out("It strikes at demons with holy wrath. ");
+ }
+ if (f5 & (TR5_KILL_UNDEAD))
+ {
+ text_out("It is a great bane of undead. ");
+ }
+ else if (f1 & (TR1_SLAY_UNDEAD))
+ {
+ text_out("It strikes at undead with holy wrath. ");
+ }
+ if (f1 & (TR1_SLAY_EVIL))
+ {
+ text_out("It fights against evil with holy fury. ");
+ }
+ if (f1 & (TR1_SLAY_ANIMAL))
+ {
+ text_out("It is especially deadly against natural creatures. ");
+ }
+
+ if (f2 & (TR2_INVIS))
+ {
+ text_out("It makes you invisible. ");
+ }
+
+ if (o_ptr->tval != TV_TRAPKIT)
+ {
+ vn = 0;
+ if (f2 & (TR2_SUST_STR))
+ {
+ vp[vn++] = "strength";
+ }
+ if (f2 & (TR2_SUST_INT))
+ {
+ vp[vn++] = "intelligence";
+ }
+ if (f2 & (TR2_SUST_WIS))
+ {
+ vp[vn++] = "wisdom";
+ }
+ if (f2 & (TR2_SUST_DEX))
+ {
+ vp[vn++] = "dexterity";
+ }
+ if (f2 & (TR2_SUST_CON))
+ {
+ vp[vn++] = "constitution";
+ }
+ if (f2 & (TR2_SUST_CHR))
+ {
+ vp[vn++] = "charisma";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It sustains ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("your ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+ text_out(". ");
+ }
+
+ vn = 0;
+ if (f2 & (TR2_IM_ACID))
+ {
+ vc[vn] = TERM_GREEN;
+ vp[vn++] = "acid";
+ }
+ if (f2 & (TR2_IM_ELEC))
+ {
+ vc[vn] = TERM_L_BLUE;
+ vp[vn++] = "electricity";
+ }
+ if (f2 & (TR2_IM_FIRE))
+ {
+ vc[vn] = TERM_RED;
+ vp[vn++] = "fire";
+ }
+ if (f2 & (TR2_IM_COLD))
+ {
+ vc[vn] = TERM_L_WHITE;
+ vp[vn++] = "cold";
+ }
+ if (f4 & (TR4_IM_NETHER))
+ {
+ vc[vn] = TERM_L_GREEN;
+ vp[vn++] = "nether";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It provides immunity ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("to ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out_c(vc[i], vp[i]);
+ }
+ text_out(". ");
+ }
+ }
+ else
+ {
+ if (f2 & (TRAP2_AUTOMATIC_5))
+ {
+ text_out("It can rearm itself. ");
+ }
+ if (f2 & (TRAP2_AUTOMATIC_99))
+ {
+ text_out("It rearms itself. ");
+ }
+ if (f2 & (TRAP2_KILL_GHOST))
+ {
+ text_out("It is effective against Ghosts. ");
+ }
+ if (f2 & (TRAP2_TELEPORT_TO))
+ {
+ text_out("It can teleport monsters to you. ");
+ }
+ if (f2 & (TRAP2_ONLY_DRAGON))
+ {
+ text_out("It can only be set off by dragons. ");
+ }
+ if (f2 & (TRAP2_ONLY_DEMON))
+ {
+ text_out("It can only be set off by demons. ");
+ }
+ if (f2 & (TRAP2_ONLY_UNDEAD))
+ {
+ text_out("It can only be set off by undead. ");
+ }
+ if (f2 & (TRAP2_ONLY_ANIMAL))
+ {
+ text_out("It can only be set off by animals. ");
+ }
+ if (f2 & (TRAP2_ONLY_EVIL))
+ {
+ text_out("It can only be set off by evil creatures. ");
+ }
+ }
+
+ if (f2 & (TR2_FREE_ACT))
+ {
+ text_out("It provides immunity to paralysis. ");
+ }
+ if (f2 & (TR2_RES_FEAR))
+ {
+ text_out("It makes you completely fearless. ");
+ }
+
+ vn = 0;
+ if (f2 & (TR2_HOLD_LIFE))
+ {
+ vp[vn++] = "life draining";
+ }
+ if ((f2 & (TR2_RES_ACID)) && !(f2 & (TR2_IM_ACID)))
+ {
+ vp[vn++] = "acid";
+ }
+ if ((f2 & (TR2_RES_ELEC)) && !(f2 & (TR2_IM_ELEC)))
+ {
+ vp[vn++] = "electricity";
+ }
+ if ((f2 & (TR2_RES_FIRE)) && !(f2 & (TR2_IM_FIRE)))
+ {
+ vp[vn++] = "fire";
+ }
+ if ((f2 & (TR2_RES_COLD)) && !(f2 & (TR2_IM_COLD)))
+ {
+ vp[vn++] = "cold";
+ }
+ if (f2 & (TR2_RES_POIS))
+ {
+ vp[vn++] = "poison";
+ }
+ if (f2 & (TR2_RES_LITE))
+ {
+ vp[vn++] = "light";
+ }
+ if (f2 & (TR2_RES_DARK))
+ {
+ vp[vn++] = "dark";
+ }
+ if (f2 & (TR2_RES_BLIND))
+ {
+ vp[vn++] = "blindness";
+ }
+ if (f2 & (TR2_RES_CONF))
+ {
+ vp[vn++] = "confusion";
+ }
+ if (f2 & (TR2_RES_SOUND))
+ {
+ vp[vn++] = "sound";
+ }
+ if (f2 & (TR2_RES_SHARDS))
+ {
+ vp[vn++] = "shards";
+ }
+ if ((f2 & (TR2_RES_NETHER)) && !(f4 & (TR4_IM_NETHER)))
+ {
+ vp[vn++] = "nether";
+ }
+ if (f2 & (TR2_RES_NEXUS))
+ {
+ vp[vn++] = "nexus";
+ }
+ if (f2 & (TR2_RES_CHAOS))
+ {
+ vp[vn++] = "chaos";
+ }
+ if (f2 & (TR2_RES_DISEN))
+ {
+ vp[vn++] = "disenchantment";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It provides resistance ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("to ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+ text_out(". ");
+ }
+
+ if (f2 & (TR2_SENS_FIRE))
+ {
+ text_out("It renders you especially vulnerable to fire. ");
+ }
+ if (f3 & (TR3_WRAITH))
+ {
+ text_out("It renders you incorporeal. ");
+ }
+ if (f5 & (TR5_WATER_BREATH))
+ {
+ text_out("It allows you to breathe underwater. ");
+ }
+ if (f5 & (TR5_MAGIC_BREATH))
+ {
+ text_out("It allows you to breathe without air. ");
+ }
+ if (f3 & (TR3_FEATHER))
+ {
+ text_out("It allows you to levitate. ");
+ }
+ if (f4 & (TR4_FLY))
+ {
+ text_out("It allows you to fly. ");
+ }
+ if (f4 & (TR4_CLIMB))
+ {
+ text_out("It allows you to climb mountains. ");
+ }
+ if (f5 & (TR5_IMMOVABLE))
+ {
+ text_out("It renders you immovable. ");
+ }
+ if (f3 & (TR3_SEE_INVIS))
+ {
+ text_out("It allows you to see invisible monsters. ");
+ }
+ if (esp)
+ {
+ if (esp & ESP_ALL) text_out("It gives telepathic powers. ");
+ else
+ {
+ vn = 0;
+ if (esp & ESP_ORC) vp[vn++] = "orcs";
+ if (esp & ESP_TROLL) vp[vn++] = "trolls";
+ if (esp & ESP_DRAGON) vp[vn++] = "dragons";
+ if (esp & ESP_SPIDER) vp[vn++] = "spiders";
+ if (esp & ESP_GIANT) vp[vn++] = "giants";
+ if (esp & ESP_DEMON) vp[vn++] = "demons";
+ if (esp & ESP_UNDEAD) vp[vn++] = "undead";
+ if (esp & ESP_EVIL) vp[vn++] = "evil beings";
+ if (esp & ESP_ANIMAL) vp[vn++] = "animals";
+ if (esp & ESP_THUNDERLORD) vp[vn++] = "thunderlords";
+ if (esp & ESP_GOOD) vp[vn++] = "good beings";
+ if (esp & ESP_NONLIVING) vp[vn++] = "non-living things";
+ if (esp & ESP_UNIQUE) vp[vn++] = "unique beings";
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It allows you to sense the presence ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("of ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out(vp[i]);
+ }
+ text_out(". ");
+ }
+ }
+ }
+
+ if (f3 & (TR3_SLOW_DIGEST))
+ {
+ text_out("It slows your metabolism. ");
+ }
+ if (f3 & (TR3_REGEN))
+ {
+ text_out("It speeds your regenerative powers. ");
+ }
+ if (f2 & (TR2_REFLECT))
+ {
+ text_out("It reflects bolts and arrows. ");
+ }
+ if (f3 & (TR3_SH_FIRE))
+ {
+ text_out("It produces a fiery sheath. ");
+ }
+ if (f3 & (TR3_SH_ELEC))
+ {
+ text_out("It produces an electric sheath. ");
+ }
+ if (f3 & (TR3_NO_MAGIC))
+ {
+ text_out("It produces an anti-magic shell. ");
+ }
+ if (f3 & (TR3_NO_TELE))
+ {
+ text_out("It prevents teleportation. ");
+ }
+ if (f3 & (TR3_XTRA_MIGHT))
+ {
+ text_out("It fires missiles with extra might. ");
+ }
+ if (f3 & (TR3_XTRA_SHOTS))
+ {
+ text_out("It fires missiles excessively fast. ");
+ }
+
+ vn = 0;
+ if (f5 & (TR5_DRAIN_MANA))
+ {
+ vc[vn] = TERM_BLUE;
+ vp[vn++] = "mana";
+ }
+ if (f5 & (TR5_DRAIN_HP))
+ {
+ vc[vn] = TERM_RED;
+ vp[vn++] = "life";
+ }
+ if (f3 & (TR3_DRAIN_EXP))
+ {
+ vc[vn] = TERM_L_DARK;
+ vp[vn++] = "experience";
+ }
+ /* Describe */
+ if (vn)
+ {
+ int i;
+
+ /* Intro */
+ text_out("It ");
+
+ /* List */
+ for (i = 0; i < vn; i++)
+ {
+ /* Connectives */
+ if (i == 0) text_out("drains ");
+ else if (i < (vn - 1)) text_out(", ");
+ else text_out(" and ");
+
+ /* Dump the stat */
+ text_out_c(vc[i], vp[i]);
+ }
+ text_out(". ");
+ }
+
+ if (f3 & (TR3_BLESSED))
+ {
+ text_out("It has been blessed by the gods. ");
+ }
+ if (f4 & (TR4_AUTO_ID))
+ {
+ text_out("It identifies all items for you. ");
+ }
+
+ if (f3 & (TR3_TELEPORT))
+ {
+ text_out("It induces random teleportation. ");
+ }
+ if (f3 & (TR3_AGGRAVATE))
+ {
+ text_out("It aggravates nearby creatures. ");
+ }
+ if (f4 & (TR4_NEVER_BLOW))
+ {
+ text_out("It can't attack. ");
+ }
+ if (f4 & (TR4_BLACK_BREATH))
+ {
+ text_out("It fills you with the Black Breath. ");
+ }
+ if (cursed_p(o_ptr))
+ {
+ if (f3 & (TR3_PERMA_CURSE))
+ {
+ text_out("It is permanently cursed. ");
+ }
+ else if (f3 & (TR3_HEAVY_CURSE))
+ {
+ text_out("It is heavily cursed. ");
+ }
+ else
+ {
+ text_out("It is cursed. ");
+ }
+ }
+ if (f3 & (TR3_TY_CURSE))
+ {
+ text_out("It carries an ancient foul curse. ");
+ }
+
+ if (f4 & (TR4_DG_CURSE))
+ {
+ text_out("It carries an ancient Morgothian curse. ");
+ }
+ if (f4 & (TR4_CLONE))
+ {
+ text_out("It can clone monsters. ");
+ }
+ if (f4 & (TR4_CURSE_NO_DROP))
+ {
+ text_out("It cannot be dropped while cursed. ");
+ }
+ if (f3 & (TR3_AUTO_CURSE))
+ {
+ text_out("It can re-curse itself. ");
+ }
+
+ if (f4 & (TR4_CAPACITY))
+ {
+ text_out("It can hold more mana. ");
+ }
+ if (f4 & (TR4_CHEAPNESS))
+ {
+ text_out("It can cast spells for a lesser mana cost. ");
+ }
+ if (f4 & (TR4_FAST_CAST))
+ {
+ text_out("It can cast spells faster. ");
+ }
+ if (f4 & (TR4_CHARGING))
+ {
+ text_out("It regenerates its mana faster. ");
+ }
+
+ if (f5 & (TR5_RES_MORGUL))
+ {
+ text_out("It can resist being shattered by morgul beings. ");
+ }
+ if ((f3 & (TR3_IGNORE_ACID)) && (f3 & (TR3_IGNORE_FIRE)) && (f3 & (TR3_IGNORE_COLD)) && (f3 & (TR3_IGNORE_ELEC)))
+ {
+ text_out("It cannot be harmed by acid, cold, lightning or fire. ");
+ }
+ else
+ {
+ if (f3 & (TR3_IGNORE_ACID))
+ {
+ text_out("It cannot be harmed by acid. ");
+ }
+ if (f3 & (TR3_IGNORE_ELEC))
+ {
+ text_out("It cannot be harmed by electricity. ");
+ }
+ if (f3 & (TR3_IGNORE_FIRE))
+ {
+ text_out("It cannot be harmed by fire. ");
+ }
+ if (f3 & (TR3_IGNORE_COLD))
+ {
+ text_out("It cannot be harmed by cold. ");
+ }
+ }
+ }
+
+
+ if (!trim_down && !fff)
+ {
+ describe_device(o_ptr);
+
+ if (object_known_p(o_ptr))
+ {
+ /* Damage display for weapons */
+ if (wield_slot(o_ptr) == INVEN_WIELD)
+ display_weapon_damage(o_ptr);
+
+ /* Breakage/Damage display for boomerangs */
+ if (o_ptr->tval == TV_BOOMERANG)
+ {
+ if (artifact_p(o_ptr))
+ text_out("\nIt can never be broken.");
+ else
+ text_out("\nIt has 1% chance to break upon hit.");
+ display_ammo_damage(o_ptr);
+ }
+
+ /* Breakage/Damage display for ammo */
+ if (wield_slot(o_ptr) == INVEN_AMMO)
+ {
+ if (artifact_p(o_ptr))
+ {
+ text_out("\nIt can never be broken.");
+ }
+ /* Exclude exploding arrows */
+ else if (o_ptr->pval2 == 0)
+ {
+ text_out("\nIt has ");
+ text_out_c(TERM_L_RED, format("%d", breakage_chance(o_ptr)));
+ text_out("% chance to break upon hit.");
+ }
+ display_ammo_damage(o_ptr);
+ }
+
+ /* Monster recall for totems and corpses */
+ if (o_ptr->tval == TV_TOTEM)
+ {
+ monster_description_out(o_ptr->pval, 0);
+ }
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ monster_description_out(o_ptr->pval2, 0);
+ }
+ }
+
+ if (!object_known_p(o_ptr))
+ text_out("\nYou might need to identify the item to know some more about it...");
+ else if (!(o_ptr->ident & (IDENT_MENTAL)))
+ text_out("\nYou might need to *identify* the item to know more about it...");
+ }
+
+ /* Copying how others seem to do it. -- neil */
+ if (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET ||
+ !trim_down || (ego_item_p(o_ptr)) || (artifact_p(o_ptr)))
+ {
+ /* Where did we found it ? */
+ if (o_ptr->found == OBJ_FOUND_MONSTER)
+ {
+ char m_name[80];
+
+ monster_race_desc(m_name, o_ptr->found_aux1, o_ptr->found_aux2);
+ text_out(format("\nYou found it in the remains of %s %s.",
+ m_name, object_out_desc_where_found(o_ptr->found_aux4, o_ptr->found_aux3)));
+ }
+ else if (o_ptr->found == OBJ_FOUND_FLOOR)
+ {
+ text_out(format("\nYou found it lying on the ground %s.",
+ object_out_desc_where_found(o_ptr->found_aux2, o_ptr->found_aux1)));
+ }
+ else if (o_ptr->found == OBJ_FOUND_VAULT)
+ {
+ text_out(format("\nYou found it lying in a vault %s.",
+ object_out_desc_where_found(o_ptr->found_aux2, o_ptr->found_aux1)));
+ }
+ else if (o_ptr->found == OBJ_FOUND_SPECIAL)
+ {
+ text_out("\nYou found it lying on the floor of a special level.");
+ }
+ else if (o_ptr->found == OBJ_FOUND_RUBBLE)
+ {
+ text_out("\nYou found it while digging a rubble.");
+ }
+ else if (o_ptr->found == OBJ_FOUND_REWARD)
+ {
+ text_out("\nIt was given to you as a reward.");
+ }
+ else if (o_ptr->found == OBJ_FOUND_STORE)
+ {
+ text_out(format("\nYou bought it from the %s.",
+ st_info[o_ptr->found_aux1].name + st_name));
+ }
+ else if (o_ptr->found == OBJ_FOUND_STOLEN)
+ {
+ text_out(format("\nYou stole it from the %s.",
+ st_info[o_ptr->found_aux1].name + st_name));
+ }
+ else if (o_ptr->found == OBJ_FOUND_SELFMADE)
+ {
+ text_out("\nYou made it yourself.");
+ }
+ /* useful for debugging
+ else
+ {
+ text_out("\nYou ordered it from a catalog in the Town.");
+ }*/
+ }
+
+ if (fff)
+ {
+ /* Flush the line position. */
+ text_out("\n");
+ text_out_file = NULL;
+ }
+ else
+ {
+ if (wait_for_it)
+ {
+ /* Wait for it */
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+ }
+ character_icky = FALSE;
+
+ }
+
+ /* Reset stuff for text_out */
+ text_out_hook = text_out_to_screen;
+
+ /* Gave knowledge */
+ return (TRUE);
+}
+
+
+
+/*
+ * Convert an inventory index into a one character label
+ * Note that the label does NOT distinguish inven/equip.
+ */
+char index_to_label(int i)
+{
+ /* Indexes for "inven" are easy */
+ if (i < INVEN_WIELD) return (I2A(i));
+
+ /* Indexes for "equip" are offset */
+ return (I2A(i - INVEN_WIELD));
+}
+
+
+/*
+ * Convert a label into the index of an item in the "inven"
+ * Return "-1" if the label does not indicate a real item
+ */
+s16b label_to_inven(int c)
+{
+ int i;
+
+ /* Convert */
+ i = (islower(c) ? A2I(c) : -1);
+
+ /* Verify the index */
+ if ((i < 0) || (i > INVEN_PACK)) return ( -1);
+
+ /* Empty slots can never be chosen */
+ if (!p_ptr->inventory[i].k_idx) return ( -1);
+
+ /* Return the index */
+ return (i);
+}
+
+
+/*
+ * Convert a label into the index of a item in the "equip"
+ * Return "-1" if the label does not indicate a real item
+ */
+s16b label_to_equip(int c)
+{
+ int i;
+
+ /* Convert */
+ i = ((islower(c) || (c > 'z')) ? A2I(c) : -1) + INVEN_WIELD;
+
+ /* Verify the index */
+ if ((i < INVEN_WIELD) || (i >= INVEN_TOTAL)) return ( -1);
+
+ /* Empty slots can never be chosen */
+ if (!p_ptr->inventory[i].k_idx) return ( -1);
+
+ /* Return the index */
+ return (i);
+}
+
+/*
+ * Returns the next free slot of the given "type", return the first
+ * if all are used
+ */
+int get_slot(int slot)
+{
+ int i = 0;
+
+ /* If there are at least one body part corretsonding, the find the free one */
+ if (p_ptr->body_parts[slot - INVEN_WIELD] == slot)
+ {
+ /* Find a free body part */
+ while ((i < 6) && (slot + i < INVEN_TOTAL) && (p_ptr->body_parts[slot - INVEN_WIELD + i] == slot))
+ {
+ if (p_ptr->body_parts[slot + i - INVEN_WIELD])
+ {
+ /* Free ? return the slot */
+ if (!p_ptr->inventory[slot + i].k_idx) return (slot + i);
+ }
+ else break;
+
+ i++;
+ }
+ /* Found nothing ? return the first one */
+ return slot;
+ }
+ /* No body parts ? return -1 */
+ else return ( -1);
+}
+
+/*
+ * Determine which equipment slot (if any) an item likes, ignoring the player's
+ * current body and stuff if ideal == TRUE
+ */
+s16b wield_slot_ideal(object_type *o_ptr, bool ideal)
+{
+ /* Try for a script first */
+ if (process_hooks_ret(HOOK_WIELD_SLOT, "d", "(O,d)", o_ptr, ideal))
+ return process_hooks_return[0].num;
+
+ /* Slot for equipment */
+ switch (o_ptr->tval)
+ {
+ case TV_DIGGING:
+ case TV_TOOL:
+ {
+ return ideal ? INVEN_TOOL : get_slot(INVEN_TOOL);
+ }
+
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ {
+ return ideal ? INVEN_WIELD : get_slot(INVEN_WIELD);
+ }
+
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_INSTRUMENT:
+ {
+ return ideal ? INVEN_BOW : get_slot(INVEN_BOW);
+ }
+
+ case TV_RING:
+ {
+ return ideal ? INVEN_RING : get_slot(INVEN_RING);
+ }
+
+ case TV_AMULET:
+ {
+ return ideal ? INVEN_NECK : get_slot(INVEN_NECK);
+ }
+
+ case TV_LITE:
+ {
+ return ideal ? INVEN_LITE : get_slot(INVEN_LITE);
+ }
+
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ {
+ return ideal ? INVEN_BODY : get_slot(INVEN_BODY);
+ }
+
+ case TV_CLOAK:
+ {
+ return ideal ? INVEN_OUTER : get_slot(INVEN_OUTER);
+ }
+
+ case TV_SHIELD:
+ {
+ return ideal ? INVEN_ARM : get_slot(INVEN_ARM);
+ }
+
+ case TV_CROWN:
+ case TV_HELM:
+ {
+ return ideal ? INVEN_HEAD : get_slot(INVEN_HEAD);
+ }
+
+ case TV_GLOVES:
+ {
+ return ideal ? INVEN_HANDS : get_slot(INVEN_HANDS);
+ }
+
+ case TV_BOOTS:
+ {
+ return ideal ? INVEN_FEET : get_slot(INVEN_FEET);
+ }
+
+ case TV_HYPNOS:
+ {
+ return ideal ? INVEN_CARRY : get_slot(INVEN_CARRY);
+ }
+
+ case TV_SHOT:
+ {
+ if (ideal)
+ {
+ return INVEN_AMMO;
+ }
+ else if (p_ptr->inventory[INVEN_AMMO].k_idx &&
+ object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) &&
+ p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE)
+ {
+ return get_slot(INVEN_AMMO);
+ }
+ else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW))
+ {
+ if (p_ptr->inventory[INVEN_BOW].sval < 10)
+ return get_slot(INVEN_AMMO);
+ }
+ return -1;
+ }
+
+ case TV_ARROW:
+ {
+ if (ideal)
+ {
+ return INVEN_AMMO;
+ }
+ else if (p_ptr->inventory[INVEN_AMMO].k_idx &&
+ object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) &&
+ p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE)
+ {
+ return get_slot(INVEN_AMMO);
+ }
+ else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW))
+ {
+ if ((p_ptr->inventory[INVEN_BOW].sval >= 10) && (p_ptr->inventory[INVEN_BOW].sval < 20))
+ return get_slot(INVEN_AMMO);
+ }
+ return -1;
+ }
+
+ case TV_BOLT:
+ {
+ if (ideal)
+ {
+ return INVEN_AMMO;
+ }
+ else if (p_ptr->inventory[INVEN_AMMO].k_idx &&
+ object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) &&
+ p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE)
+ {
+ return get_slot(INVEN_AMMO);
+ }
+ else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW))
+ {
+ if (p_ptr->inventory[INVEN_BOW].sval >= 20)
+ return get_slot(INVEN_AMMO);
+ }
+ return -1;
+ }
+ }
+
+ /* No slot available */
+ return ( -1);
+}
+
+/*
+ * Determine which equipment slot (if any) an item likes for the player's
+ * current body and stuff
+ */
+s16b wield_slot(object_type *o_ptr)
+{
+ return wield_slot_ideal(o_ptr, FALSE);
+}
+
+/*
+ * Return a string mentioning how a given item is carried
+ */
+cptr mention_use(int i)
+{
+ cptr p;
+
+ /* Examine the location */
+ switch (i)
+ {
+ case INVEN_WIELD:
+ case INVEN_WIELD + 1:
+ case INVEN_WIELD + 2:
+ p = "Wielding";
+ break;
+ case INVEN_BOW:
+ p = "Shooting";
+ break;
+ case INVEN_RING:
+ case INVEN_RING + 1:
+ case INVEN_RING + 2:
+ case INVEN_RING + 3:
+ case INVEN_RING + 4:
+ case INVEN_RING + 5:
+ p = "On finger";
+ break;
+ case INVEN_NECK:
+ case INVEN_NECK + 1:
+ p = "Around neck";
+ break;
+ case INVEN_LITE:
+ p = "Light source";
+ break;
+ case INVEN_BODY:
+ p = "On body";
+ break;
+ case INVEN_OUTER:
+ p = "About body";
+ break;
+ case INVEN_ARM:
+ case INVEN_ARM + 1:
+ case INVEN_ARM + 2:
+ p = "On arm";
+ break;
+ case INVEN_HEAD:
+ case INVEN_HEAD + 1:
+ p = "On head";
+ break;
+ case INVEN_HANDS:
+ case INVEN_HANDS + 1:
+ case INVEN_HANDS + 2:
+ p = "On hands";
+ break;
+ case INVEN_FEET:
+ case INVEN_FEET + 1:
+ p = "On feet";
+ break;
+ case INVEN_CARRY:
+ p = "Symbiote";
+ break;
+ case INVEN_AMMO:
+ p = "Quiver";
+ break;
+ case INVEN_TOOL:
+ p = "Using";
+ break;
+ default:
+ p = "In pack";
+ break;
+ }
+
+ /* Hack -- Heavy weapons */
+ if ((INVEN_WIELD <= i) && (i <= INVEN_WIELD + 2))
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "Just lifting";
+ }
+ }
+
+ /* Hack -- music instruments and heavy bow */
+ if (i == INVEN_BOW)
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ p = "Playing";
+ }
+ else if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "Just holding";
+ }
+ }
+
+ /* Return the result */
+ return (p);
+}
+
+
+/*
+ * Return a string describing how a given item is being worn.
+ * Currently, only used for items in the equipment, not inventory.
+ */
+cptr describe_use(int i)
+{
+ cptr p;
+
+ switch (i)
+ {
+ case INVEN_WIELD:
+ case INVEN_WIELD + 1:
+ case INVEN_WIELD + 2:
+ p = "attacking monsters with";
+ break;
+ case INVEN_BOW:
+ p = "shooting missiles with";
+ break;
+ case INVEN_RING:
+ case INVEN_RING + 1:
+ case INVEN_RING + 2:
+ case INVEN_RING + 3:
+ case INVEN_RING + 4:
+ case INVEN_RING + 5:
+ p = "wearing on your finger";
+ break;
+ case INVEN_NECK:
+ case INVEN_NECK + 1:
+ p = "wearing around your neck";
+ break;
+ case INVEN_LITE:
+ p = "using to light the way";
+ break;
+ case INVEN_BODY:
+ p = "wearing on your body";
+ break;
+ case INVEN_OUTER:
+ p = "wearing on your back";
+ break;
+ case INVEN_ARM:
+ case INVEN_ARM + 1:
+ case INVEN_ARM + 2:
+ p = "wearing on your arm";
+ break;
+ case INVEN_HEAD:
+ case INVEN_HEAD + 1:
+ p = "wearing on your head";
+ break;
+ case INVEN_HANDS:
+ case INVEN_HANDS + 1:
+ case INVEN_HANDS + 2:
+ p = "wearing on your hands";
+ break;
+ case INVEN_FEET:
+ case INVEN_FEET + 1:
+ p = "wearing on your feet";
+ break;
+ case INVEN_CARRY:
+ p = "in symbiosis with";
+ break;
+ case INVEN_AMMO:
+ p = "carrying in your quiver";
+ break;
+ case INVEN_TOOL:
+ p = "using as a tool";
+ break;
+ default:
+ p = "carrying in your pack";
+ break;
+ }
+
+ /* Hack -- Heavy weapons */
+ if ((INVEN_WIELD <= i) && (i <= INVEN_WIELD + 2))
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "just lifting";
+ }
+ }
+
+ /* Hack -- Music instruments and heavy bow */
+ if (i == INVEN_BOW)
+ {
+ object_type *o_ptr;
+ o_ptr = &p_ptr->inventory[i];
+ if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ p = "playing music with";
+ }
+ else if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10)
+ {
+ p = "just holding";
+ }
+ }
+
+ /* Return the result */
+ return p;
+}
+
+/*
+ * Check an item against the item tester info
+ */
+bool item_tester_okay(object_type *o_ptr)
+{
+ /* Hack -- allow listing empty slots */
+ if (item_tester_full) return (TRUE);
+
+ /* Require an item */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* Hack -- ignore "gold" */
+ if (o_ptr->tval == TV_GOLD) return (FALSE);
+
+ /* Check the tval */
+ if (item_tester_tval)
+ {
+ if (!(item_tester_tval == o_ptr->tval)) return (FALSE);
+ }
+
+ /* Check the hook */
+ if (item_tester_hook)
+ {
+ if (!(*item_tester_hook)(o_ptr)) return (FALSE);
+ }
+
+ /* Assume okay */
+ return (TRUE);
+}
+
+
+
+
+void show_equip_aux(bool mirror, bool everything);
+void show_inven_aux(bool mirror, bool everything);
+
+/*
+ * Choice window "shadow" of the "show_inven()" function
+ */
+void display_inven(void)
+{
+ show_inven_aux(TRUE, inventory_no_move);
+}
+
+
+
+/*
+ * Choice window "shadow" of the "show_equip()" function
+ */
+void display_equip(void)
+{
+ show_equip_aux(TRUE, inventory_no_move);
+}
+
+
+
+/* Get the color of the letter idx */
+byte get_item_letter_color(object_type *o_ptr)
+{
+ byte color = TERM_WHITE;
+
+ /* Must have knowlegde */
+ if (!object_known_p(o_ptr)) return (TERM_SLATE);
+
+ if (ego_item_p(o_ptr)) color = TERM_L_BLUE;
+ if (artifact_p(o_ptr)) color = TERM_YELLOW;
+ if (o_ptr->name1 && ( -1 != a_info[o_ptr->name1].set)) color = TERM_GREEN;
+ if (o_ptr->name1 && (a_info[o_ptr->name1].flags4 & TR4_ULTIMATE) && (o_ptr->ident & (IDENT_MENTAL))) color = TERM_VIOLET;
+
+ return (color);
+}
+
+
+/*
+ * Display the inventory.
+ *
+ * Hack -- do not display "trailing" empty slots
+ */
+void show_inven_aux(bool mirror, bool everything)
+{
+ int i, j, k, l, z = 0;
+ int row, col, len, lim;
+ int wid, hgt;
+ object_type *o_ptr;
+ char o_name[80];
+ char tmp_val[80];
+ int out_index[23];
+ byte out_color[23];
+ char out_desc[23][80];
+
+
+ /* Retrive current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Starting row */
+ row = mirror ? 0 : 1;
+
+ /* Starting column */
+ col = mirror ? 0 : 50;
+
+ /* Default "max-length" */
+ len = 79 - col;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ /* Require space for icon */
+ if (show_inven_graph) lim -= 2;
+
+ /* Find the "final" slot */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Track */
+ z = i + 1;
+ }
+
+ /* Display the inventory */
+ for (k = 0, i = 0; i < z; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Is this item acceptable? */
+ if (!item_tester_okay(o_ptr))
+ {
+ if ( !everything )
+ continue;
+ out_index[k] = -i - 1;
+ }
+ else
+ {
+ /* Save the object index */
+ out_index[k] = i + 1;
+ }
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Save the object color, and description */
+ out_color[k] = tval_to_attr[o_ptr->tval % 128];
+ (void)strcpy(out_desc[k], o_name);
+
+ /* Find the predicted "line length" */
+ l = strlen(out_desc[k]) + 5;
+
+ /* Be sure to account for the weight */
+ if (show_weights) l += 9;
+
+ /* Account for icon if displayed */
+ if (show_inven_graph) l += 2;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+
+ /* Advance to next "line" */
+ k++;
+ }
+
+ /* Find the column to start in */
+ if (mirror) col = 0;
+ else col = (len > wid - 4) ? 0 : (wid - len - 1);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ /* Get the index */
+ i = out_index[j];
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[ABS(i) - 1];
+
+ /* Clear the line */
+ prt("", row + j, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ /* Prepare a blank index if not selectable */
+ if ( i > 0 )
+ sprintf(tmp_val, "%c)", index_to_label(i - 1));
+ else
+ sprintf(tmp_val, " ");
+
+ /* Clear the line with the (possibly indented) index */
+ c_put_str(get_item_letter_color(o_ptr), tmp_val, row + j, col);
+
+ /* Display graphics for object, if desired */
+ if (show_inven_graph)
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+#ifdef AMIGA
+ if (a & 0x80) a |= 0x40;
+#endif
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(col + 3, row + j, a, c);
+ }
+
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j,
+ show_inven_graph ? (col + 5) : (col + 3));
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ (void)sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, row + j, wid - 9);
+ }
+ }
+
+ /* Shadow windows */
+ if (mirror)
+ {
+ /* Erase the rest of the window */
+ for (j = row + k; j < Term->hgt; j++)
+ {
+ /* Erase the line */
+ Term_erase(0, j, 255);
+ }
+ }
+
+ /* Main window */
+ else
+ {
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", row + j, col ? col - 2 : col);
+ }
+}
+
+
+void show_inven(bool mirror)
+{
+ show_inven_aux(mirror, FALSE);
+}
+
+void show_equip(bool mirror)
+{
+ show_equip_aux(mirror, FALSE);
+}
+
+/*
+ * Display the equipment.
+ */
+void show_equip_aux(bool mirror, bool everything)
+{
+ int i, j, k, l;
+ int row, col, len, lim, idx;
+ int wid, hgt;
+ object_type *o_ptr;
+ char tmp_val[80];
+ char o_name[80];
+ int out_index[INVEN_TOOL - INVEN_WIELD];
+ int out_rindex[INVEN_TOOL - INVEN_WIELD];
+ byte out_color[INVEN_TOOL - INVEN_WIELD];
+ char out_desc[INVEN_TOOL - INVEN_WIELD][80];
+
+
+ /* Retrive current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Starting row */
+ row = mirror ? 0 : 1;
+
+ /* Starting column */
+ col = mirror ? 0 : 50;
+
+ /* Maximal length */
+ len = 79 - col;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for labels (if needed) */
+ if (show_labels) lim -= (14 + 2);
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ if (show_equip_graph) lim -= 2;
+
+ /* Scan the equipment list */
+ idx = 0;
+ for (k = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Is there actualy a body part here ? */
+ if (!p_ptr->body_parts[i - INVEN_WIELD]) continue;
+
+ /* Mega Hack -- don't show symbiote slot if we can't use it */
+ if ((i == INVEN_CARRY) && (!get_skill(SKILL_SYMBIOTIC))) continue;
+
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Inform the player that he/she can't use a shield */
+ if ((p_ptr->body_parts[i - INVEN_WIELD] == INVEN_ARM) &&
+ !o_ptr->k_idx &&
+ p_ptr->inventory[i - INVEN_ARM + INVEN_WIELD].k_idx)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_type *q_ptr = &p_ptr->inventory[i - INVEN_ARM + INVEN_WIELD];
+ char q_name[80];
+
+ /* Description */
+ object_desc(q_name, q_ptr, TRUE, 3);
+
+ /* Get weapon flags */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_MUST2H)
+ {
+ sprintf(o_name, "(two handed) %s", q_name);
+
+ /* Truncate the description */
+ o_name[lim] = 0;
+
+ /* Save the index */
+ out_index[k] = idx;
+ out_rindex[k] = i;
+ idx++;
+
+ /* Save the color */
+ out_color[k] = TERM_L_RED;
+ (void)strcpy(out_desc[k], o_name);
+ continue;
+ }
+ }
+
+ if ((p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD) &&
+ !o_ptr->k_idx)
+ {
+ sprintf(o_name, "(%s)", melee_names[get_melee_skill()]);
+
+ /* Truncate the description */
+ o_name[lim] = 0;
+
+ /* Save the index */
+ out_index[k] = idx;
+ out_rindex[k] = i;
+ idx++;
+
+ /* Save the color */
+ out_color[k] = TERM_L_BLUE;
+ (void)strcpy(out_desc[k], o_name);
+ }
+ else
+ {
+ idx++;
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Truncate the description */
+ o_name[lim] = 0;
+ /* Is this item acceptable? */
+ if (!item_tester_okay(o_ptr))
+ {
+ if (!everything) continue;
+ out_index[k] = -1;
+ }
+ else
+ {
+ /* Save the index */
+ out_index[k] = idx;
+ }
+ out_rindex[k] = i;
+
+ /* Save the color */
+ out_color[k] = tval_to_attr[o_ptr->tval % 128];
+ (void)strcpy(out_desc[k], o_name);
+ }
+
+ /* Extract the maximal length (see below) */
+ l = strlen(out_desc[k]) + (2 + 3);
+
+ /* Increase length for labels (if needed) */
+ if (show_labels) l += (14 + 2);
+
+ /* Increase length for weight (if needed) */
+ if (show_weights) l += 9;
+
+ if (show_equip_graph) l += 2;
+
+ /* Maintain the max-length */
+ if (l > len) len = l;
+
+ /* Advance the entry */
+ k++;
+ }
+
+ /* Hack -- Find a column to start in */
+ if (mirror) col = 0;
+ else col = (len > wid - 4) ? 0 : (wid - len - 1);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ if (j > 20) break;
+
+ /* Get the index */
+ i = out_index[j];
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[out_rindex[j]];
+
+ /* Clear the line */
+ prt("", row + j, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ if (out_index[j] >= 0 )
+ sprintf(tmp_val, "%c)", index_to_label(out_rindex[j]));
+ else
+ sprintf(tmp_val, " ");
+
+ /* Clear the line with the (possibly indented) index */
+ c_put_str(get_item_letter_color(o_ptr), tmp_val, row + j, col);
+
+ if (show_equip_graph)
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+#ifdef AMIGA
+ if (a & 0x80) a |= 0x40;
+#endif
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(col + 3, row + j, a, c);
+ }
+
+ /* Use labels */
+ if (show_labels)
+ {
+ /* Mention the use */
+ (void)sprintf(tmp_val, "%-14s: ", mention_use(out_rindex[j]));
+ put_str(tmp_val, row + j, show_equip_graph ? col + 5 : col + 3);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j, show_equip_graph ? col + 21 : col + 19);
+ }
+
+ /* No labels */
+ else
+ {
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j, show_equip_graph ? col + 5 : col + 3);
+ }
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ (void)sprintf(tmp_val, "%3d.%d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, row + j, wid - 9);
+ }
+ }
+
+
+ /* Shadow windows */
+ if (mirror)
+ {
+ /* Erase the rest of the window */
+ for (j = row + k; j < Term->hgt; j++)
+ {
+ /* Erase the line */
+ Term_erase(0, j, 255);
+ }
+ }
+
+ /* Main window */
+ else
+ {
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", row + j, col ? col - 2 : col);
+ }
+}
+
+
+
+
+/*
+ * Flip "inven" and "equip" in any sub-windows
+ */
+void toggle_inven_equip(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Flip inven to equip */
+ if (window_flag[j] & (PW_INVEN))
+ {
+ /* Flip flags */
+ window_flag[j] &= ~(PW_INVEN);
+ window_flag[j] |= (PW_EQUIP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Flip inven to equip */
+ else if (window_flag[j] & (PW_EQUIP))
+ {
+ /* Flip flags */
+ window_flag[j] &= ~(PW_EQUIP);
+ window_flag[j] |= (PW_INVEN);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+ }
+}
+
+
+
+/*
+ * Verify the choice of an item.
+ *
+ * The item can be negative to mean "item on floor".
+ */
+bool verify(cptr prompt, int item)
+{
+ char o_name[80];
+
+ char out_val[160];
+
+ object_type *o_ptr;
+
+ /* Inventory */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Floor */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Prompt */
+ (void)sprintf(out_val, "%s %s? ", prompt, o_name);
+
+ /* Query */
+ return (get_check(out_val));
+}
+
+
+/*
+ * Hack -- allow user to "prevent" certain choices
+ *
+ * The item can be negative to mean "item on floor".
+ */
+static bool get_item_allow(int item)
+{
+ cptr s;
+
+ object_type *o_ptr;
+
+ /* Inventory */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Floor */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* No inscription */
+ if (!o_ptr->note) return (TRUE);
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process preventions */
+ while (s)
+ {
+ /* Check the "restriction" */
+ if ((s[1] == command_cmd) || (s[1] == '*'))
+ {
+ /* Verify the choice */
+ if (!verify("Really try", item)) return (FALSE);
+ }
+
+ /* Find another '!' */
+ s = strchr(s + 1, '!');
+ }
+
+ /* Allow it */
+ return (TRUE);
+}
+
+
+
+/*
+ * Auxiliary function for "get_item()" -- test an index
+ */
+static bool get_item_okay(int i)
+{
+ /* Illegal items */
+ if ((i < 0) || (i >= INVEN_TOTAL)) return (FALSE);
+
+ /* Verify the item */
+ if (!item_tester_okay(&p_ptr->inventory[i])) return (FALSE);
+
+ /* Assume okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Find the "first" inventory object with the given "tag".
+ *
+ * A "tag" is a char "n" appearing as "@n" anywhere in the
+ * inscription of an object.
+ *
+ * Also, the tag "@xn" will work as well, where "n" is a tag-char,
+ * and "x" is the "current" command_cmd code.
+ */
+static int get_tag(int *cp, char tag)
+{
+ int i;
+ cptr s;
+
+
+ /* Check every object */
+ for (i = 0; i < INVEN_TOTAL; ++i)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip empty inscriptions */
+ if (!o_ptr->note) continue;
+
+ /* Find a '@' */
+ s = strchr(quark_str(o_ptr->note), '@');
+
+ /* Process all tags */
+ while (s)
+ {
+ /* Check the normal tags */
+ if (s[1] == tag)
+ {
+ /* Save the actual inventory ID */
+ *cp = i;
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Check the special tags */
+ if ((s[1] == command_cmd) && (s[2] == tag))
+ {
+ /* Save the actual inventory ID */
+ *cp = i;
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Find another '@' */
+ s = strchr(s + 1, '@');
+ }
+ }
+
+ /* No such tag */
+ return (FALSE);
+}
+
+/*
+ * scan_floor --
+ *
+ * Return a list of o_list[] indexes of items at the given cave
+ * location. Valid flags are:
+ *
+ * mode & 0x01 -- Item tester
+ * mode & 0x02 -- Marked items only
+ * mode & 0x04 -- Stop after first
+ */
+bool scan_floor(int *items, int *item_num, int y, int x, int mode)
+{
+ int this_o_idx, next_o_idx;
+
+ int num = 0;
+
+ (*item_num) = 0;
+
+ /* Sanity */
+ if (!in_bounds(y, x)) return (FALSE);
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = cave[y][x].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Item tester */
+ if ((mode & 0x01) && !item_tester_okay(o_ptr)) continue;
+
+ /* Marked */
+ if ((mode & 0x02) && !o_ptr->marked) continue;
+
+ /* Accept this item */
+ items[num++] = this_o_idx;
+
+ /* Only one */
+ if (mode & 0x04) break;
+
+ /* XXX Hack -- Enforce limit */
+ if (num == 23) break;
+ }
+
+ /* Number of items */
+ (*item_num) = num;
+
+ /* Result */
+ return (num != 0);
+}
+
+/*
+ * Display a list of the items on the floor at the given location.
+ */
+void show_floor(int y, int x)
+{
+ int i, j, k, l;
+ int col, len, lim;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char tmp_val[80];
+
+ int out_index[23];
+ byte out_color[23];
+ char out_desc[23][80];
+
+ int floor_list[23], floor_num;
+
+ /* Default length */
+ len = 79 - 50;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight (if needed) */
+ if (show_weights) lim -= 9;
+
+ /* Scan for objects in the grid, using item_tester_okay() */
+ (void) scan_floor(floor_list, &floor_num, y, x, 0x01);
+
+ /* Display the inventory */
+ for (k = 0, i = 0; i < floor_num; i++)
+ {
+ o_ptr = &o_list[floor_list[i]];
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Save the index */
+ out_index[k] = i;
+
+ /* Acquire inventory color */
+ out_color[k] = tval_to_attr[o_ptr->tval & 0x7F];
+
+ /* Save the object description */
+ strcpy(out_desc[k], o_name);
+
+ /* Find the predicted "line length" */
+ l = strlen(out_desc[k]) + 5;
+
+ /* Be sure to account for the weight */
+ if (show_weights) l += 9;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+
+ /* Advance to next "line" */
+ k++;
+ }
+
+ /* Find the column to start in */
+ col = (len > 76) ? 0 : (79 - len);
+
+ /* Output each entry */
+ for (j = 0; j < k; j++)
+ {
+ /* Get the index */
+ i = floor_list[out_index[j]];
+
+ /* Get the item */
+ o_ptr = &o_list[i];
+
+ /* Clear the line */
+ prt("", j + 1, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ sprintf(tmp_val, "%c)", index_to_label(j));
+
+ /* Clear the line with the (possibly indented) index */
+ put_str(tmp_val, j + 1, col);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], j + 1, col + 3);
+
+ /* Display the weight if needed */
+ if (show_weights)
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, j + 1, 71);
+ }
+ }
+
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", j + 1, col ? col - 2 : col);
+}
+
+/*
+ * This version of get_item() is called by get_item() when
+ * the easy_floor is on.
+ */
+bool (*get_item_extra_hook)(int *cp);
+bool get_item_floor(int *cp, cptr pmt, cptr str, int mode)
+{
+ char n1 = 0, n2 = 0, which = ' ';
+
+ int j, k, i1, i2, e1, e2;
+
+ bool done, item;
+
+ bool oops = FALSE;
+
+ bool equip = FALSE;
+ bool inven = FALSE;
+ bool floor = FALSE;
+ bool extra = FALSE;
+ bool automat = FALSE;
+
+ bool allow_equip = FALSE;
+ bool allow_inven = FALSE;
+ bool allow_floor = FALSE;
+
+ bool toggle = FALSE;
+
+ char tmp_val[160];
+ char out_val[160];
+
+ int floor_num, floor_list[23], floor_top = 0;
+
+ k = 0;
+#ifdef ALLOW_REPEAT
+
+ /* Get the item index */
+ if (repeat_pull(cp))
+ {
+ /* Floor item? */
+ if (*cp < 0)
+ {
+ object_type *o_ptr;
+
+ /* Special index */
+ k = 0 - (*cp);
+
+ /* Acquire object */
+ o_ptr = &o_list[k];
+
+ /* Validate the item */
+ if (item_tester_okay(o_ptr))
+ {
+ /* Forget the item_tester_tval restriction */
+ item_tester_tval = 0;
+
+ /* Forget the item_tester_hook restriction */
+ item_tester_hook = NULL;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Verify the item */
+ else if (get_item_okay(*cp))
+ {
+ /* Forget the item_tester_tval restriction */
+ item_tester_tval = 0;
+
+ /* Forget the item_tester_hook restriction */
+ item_tester_hook = NULL;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+#endif /* ALLOW_REPEAT */
+
+ /* Extract args */
+ if (mode & (USE_EQUIP)) equip = TRUE;
+ if (mode & (USE_INVEN)) inven = TRUE;
+ if (mode & (USE_FLOOR)) floor = TRUE;
+ if (mode & (USE_EXTRA)) extra = TRUE;
+ if (mode & (USE_AUTO)) automat = TRUE;
+
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Not done */
+ done = FALSE;
+
+ /* No item selected */
+ item = FALSE;
+
+
+ /* Full inventory */
+ i1 = 0;
+ i2 = INVEN_PACK - 1;
+
+ /* Forbid inventory */
+ if (!inven) i2 = -1;
+
+ /* Restrict inventory indexes */
+ while ((i1 <= i2) && (!get_item_okay(i1))) i1++;
+ while ((i1 <= i2) && (!get_item_okay(i2))) i2--;
+
+
+ /* Full equipment */
+ e1 = INVEN_WIELD;
+ e2 = INVEN_TOTAL - 1;
+
+ /* Forbid equipment */
+ if (!equip) e2 = -1;
+
+ /* Restrict equipment indexes */
+ while ((e1 <= e2) && (!get_item_okay(e1))) e1++;
+ while ((e1 <= e2) && (!get_item_okay(e2))) e2--;
+
+
+ /* Count "okay" floor items */
+ floor_num = 0;
+
+ /* Restrict floor usage */
+ if (floor)
+ {
+ /* Scan all objects in the grid */
+ (void) scan_floor(floor_list, &floor_num, p_ptr->py, p_ptr->px, 0x01);
+ }
+
+ /* Accept inventory */
+ if (i1 <= i2) allow_inven = TRUE;
+
+ /* Accept equipment */
+ if (e1 <= e2) allow_equip = TRUE;
+
+ /* Accept floor */
+ if (floor_num) allow_floor = TRUE;
+
+ /* Require at least one legal choice */
+ if (!allow_inven && !allow_equip && !allow_floor)
+ {
+ /* Cancel command_see */
+ command_see = FALSE;
+
+ /* Oops */
+ oops = TRUE;
+
+ /* Done */
+ done = TRUE;
+ }
+
+ /* Analyze choices */
+ else
+ {
+ /* Hack -- Start on equipment if requested */
+ if (command_see && (command_wrk == (USE_EQUIP))
+ && allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+
+ /* Use inventory if allowed */
+ else if (allow_inven)
+ {
+ command_wrk = (USE_INVEN);
+ }
+
+ /* Use equipment if allowed */
+ else if (allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+
+ /* Use floor if allowed */
+ else if (allow_floor)
+ {
+ command_wrk = (USE_FLOOR);
+ }
+ }
+
+ /* Hack -- start out in "display" mode */
+ if (command_see)
+ {
+ /* Save screen */
+ screen_save();
+ }
+
+ /* Repeat until done */
+ while (!done)
+ {
+ /* Show choices */
+ if (show_choices)
+ {
+ int ni = 0;
+ int ne = 0;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Count windows displaying inven */
+ if (window_flag[j] & (PW_INVEN)) ni++;
+
+ /* Count windows displaying equip */
+ if (window_flag[j] & (PW_EQUIP)) ne++;
+ }
+
+ /* Toggle if needed */
+ if ((command_wrk == (USE_EQUIP) && ni && !ne) ||
+ (command_wrk == (USE_INVEN) && !ni && ne))
+ {
+ /* Toggle */
+ toggle_inven_equip();
+
+ /* Track toggles */
+ toggle = !toggle;
+ }
+
+ /* Update */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Redraw windows */
+ window_stuff();
+ }
+
+ /* Inventory screen */
+ if (command_wrk == (USE_INVEN))
+ {
+ /* Extract the legal requests */
+ n1 = I2A(i1);
+ n2 = I2A(i2);
+
+ /* Redraw if needed */
+ if (command_see) show_inven(FALSE);
+ }
+
+ /* Equipment screen */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ /* Extract the legal requests */
+ n1 = I2A(e1 - INVEN_WIELD);
+ n2 = I2A(e2 - INVEN_WIELD);
+
+ /* Redraw if needed */
+ if (command_see) show_equip(FALSE);
+ }
+
+ /* Floor screen */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ j = floor_top;
+ k = MIN(floor_top + 23, floor_num) - 1;
+
+ /* Extract the legal requests */
+ n1 = I2A(j - floor_top);
+ n2 = I2A(k - floor_top);
+
+ /* Redraw if needed */
+ if (command_see) show_floor(p_ptr->py, p_ptr->px);
+ }
+
+ /* Viewing inventory */
+ if (command_wrk == (USE_INVEN))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Inven:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,",
+ index_to_label(i1), index_to_label(i2));
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Indicate ability to "view" */
+ if (!command_see) strcat(out_val, " * to see,");
+
+ /* Append */
+ if (allow_equip) strcat(out_val, " / for Equip,");
+
+ /* Append */
+ if (allow_floor) strcat(out_val, " - for floor,");
+ }
+
+ /* Viewing equipment */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Equip:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,",
+ index_to_label(e1), index_to_label(e2));
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Indicate ability to "view" */
+ if (!command_see) strcat(out_val, " * to see,");
+
+ /* Append */
+ if (allow_inven) strcat(out_val, " / for Inven,");
+
+ /* Append */
+ if (allow_floor) strcat(out_val, " - for floor,");
+ }
+
+ /* Viewing floor */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Floor:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,", n1, n2);
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Indicate ability to "view" */
+ if (!command_see) strcat(out_val, " * to see,");
+
+ /* Append */
+ if (allow_inven)
+ {
+ strcat(out_val, " / for Inven,");
+ }
+ else if (allow_equip)
+ {
+ strcat(out_val, " / for Equip,");
+ }
+ }
+
+ /* Extra? */
+ if (extra)
+ {
+ strcat(out_val, " @ for extra selection,");
+ }
+
+ /* Create automatizer rule?? */
+ if (automat)
+ {
+ if (automatizer_create)
+ strcat(out_val, " $ new automatizer rule(ON),");
+ else
+ strcat(out_val, " $ new automatizer rule(OFF),");
+ }
+
+ /* Finish the prompt */
+ strcat(out_val, " ESC");
+
+ /* Build the prompt */
+ sprintf(tmp_val, "(%s) %s", out_val, pmt);
+
+ /* Show the prompt */
+ prt(tmp_val, 0, 0);
+
+ /* Get a key */
+ which = inkey();
+
+ /* Parse it */
+ switch (which)
+ {
+ case ESCAPE:
+ {
+ done = TRUE;
+ break;
+ }
+
+ case '*':
+ case '?':
+ case ' ':
+ {
+ /* Hide the list */
+ if (command_see)
+ {
+ /* Flip flag */
+ command_see = FALSE;
+
+ /* Load screen */
+ screen_load();
+ }
+
+ /* Show the list */
+ else
+ {
+ /* Save screen */
+ screen_save();
+
+ /* Flip flag */
+ command_see = TRUE;
+ }
+ break;
+ }
+
+ case '/':
+ {
+ if (command_wrk == (USE_INVEN))
+ {
+ if (!allow_equip)
+ {
+ bell();
+ break;
+ }
+ command_wrk = (USE_EQUIP);
+ }
+ else if (command_wrk == (USE_EQUIP))
+ {
+ if (!allow_inven)
+ {
+ bell();
+ break;
+ }
+ command_wrk = (USE_INVEN);
+ }
+ else if (command_wrk == (USE_FLOOR))
+ {
+ if (allow_inven)
+ {
+ command_wrk = (USE_INVEN);
+ }
+ else if (allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+ else
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Hack -- Fix screen */
+ if (command_see)
+ {
+ /* Load screen */
+ screen_load();
+
+ /* Save screen */
+ screen_save();
+ }
+
+ /* Need to redraw */
+ break;
+ }
+
+ case '-':
+ {
+ if (!allow_floor)
+ {
+ bell();
+ break;
+ }
+
+ /*
+ * If we are already examining the floor, and there
+ * is only one item, we will always select it.
+ * If we aren't examining the floor and there is only
+ * one item, we will select it if floor_query_flag
+ * is FALSE.
+ */
+ if (floor_num == 1)
+ {
+ if (command_wrk == (USE_FLOOR))
+ {
+ /* Special index */
+ k = 0 - floor_list[0];
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+
+ break;
+ }
+ }
+
+ /* Hack -- Fix screen */
+ if (command_see)
+ {
+ /* Load screen */
+ screen_load();
+
+ /* Save screen */
+ screen_save();
+ }
+
+ command_wrk = (USE_FLOOR);
+
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* Look up the tag */
+ if (!get_tag(&k, which))
+ {
+ bell();
+ break;
+ }
+
+ /* Hack -- Validate the item */
+ if ((k < INVEN_WIELD) ? !inven : !equip)
+ {
+ bell();
+ break;
+ }
+
+ /* Validate the item */
+ if (!get_item_okay(k))
+ {
+ bell();
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+
+ case '\n':
+ case '\r':
+ {
+ /* Choose "default" inventory item */
+ if (command_wrk == (USE_INVEN))
+ {
+ k = ((i1 == i2) ? i1 : -1);
+ }
+
+ /* Choose "default" equipment item */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ k = ((e1 == e2) ? e1 : -1);
+ }
+
+ /* Choose "default" floor item */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ if (floor_num == 1)
+ {
+ /* Special index */
+ k = 0 - floor_list[0];
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ }
+ break;
+ }
+
+ /* Validate the item */
+ if (!get_item_okay(k))
+ {
+ bell();
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+
+ case '@':
+ {
+ int i;
+
+ if (extra && get_item_extra_hook(&i))
+ {
+ (*cp) = i;
+ item = TRUE;
+ done = TRUE;
+ }
+ break;
+ }
+
+ case '$':
+ {
+ automatizer_create = !automatizer_create;
+ break;
+ }
+
+ default:
+ {
+ int ver;
+
+ ver = isupper(which);
+ which = tolower(which);
+
+ /* Convert letter to inventory index */
+ if (command_wrk == (USE_INVEN))
+ {
+ k = label_to_inven(which);
+ if (k == -1)
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Convert letter to equipment index */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ k = label_to_equip(which);
+ if (k == -1)
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Convert letter to floor index */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ k = islower(which) ? A2I(which) : -1;
+ if (k < 0 || k >= floor_num)
+ {
+ bell();
+ break;
+ }
+
+ /* Special index */
+ k = 0 - floor_list[k];
+ }
+
+ /* Validate the item */
+ if ((k >= 0) && !get_item_okay(k))
+ {
+ bell();
+ break;
+ }
+
+ /* Verify the item */
+ if (ver && !verify("Try", k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Fix the screen if necessary */
+ if (command_see)
+ {
+ /* Load screen */
+ screen_load();
+
+ /* Hack -- Cancel "display" */
+ command_see = FALSE;
+ }
+
+
+ /* Forget the item_tester_tval restriction */
+ item_tester_tval = 0;
+
+ /* Forget the item_tester_hook restriction */
+ item_tester_hook = NULL;
+
+ /* Track */
+ if (item && done)
+ {
+ if (*cp >= 0)
+ {
+ object_track(&p_ptr->inventory[*cp]);
+ }
+ else
+ {
+ object_track(&o_list[0 - *cp]);
+ }
+ }
+
+ /* Clean up */
+ if (show_choices)
+ {
+ /* Toggle again if needed */
+ if (toggle) toggle_inven_equip();
+
+ /* Update */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Window stuff */
+ window_stuff();
+ }
+
+
+ /* Clear the prompt line */
+ prt("", 0, 0);
+
+ /* Warning if needed */
+ if (oops && str) msg_print(str);
+
+#ifdef ALLOW_REPEAT
+
+ if (item) repeat_push(*cp);
+
+#endif /* ALLOW_REPEAT */
+
+ /* Result */
+ return (item);
+}
+
+
+/*
+ * Let the user select an item, save its "index"
+ *
+ * Return TRUE only if an acceptable item was chosen by the user.
+ *
+ * The selected item must satisfy the "item_tester_hook()" function,
+ * if that hook is set, and the "item_tester_tval", if that value is set.
+ *
+ * All "item_tester" restrictions are cleared before this function returns.
+ *
+ * The user is allowed to choose acceptable items from the equipment,
+ * inventory, or floor, respectively, if the proper flag was given,
+ * and there are any acceptable items in that location.
+ *
+ * The equipment or inventory are displayed (even if no acceptable
+ * items are in that location) if the proper flag was given.
+ *
+ * If there are no acceptable items available anywhere, and "str" is
+ * not NULL, then it will be used as the text of a warning message
+ * before the function returns.
+ *
+ * Note that the user must press "-" to specify the item on the floor,
+ * and there is no way to "examine" the item on the floor, while the
+ * use of "capital" letters will "examine" an inventory/equipment item,
+ * and prompt for its use.
+ *
+ * If a legal item is selected from the inventory, we save it in "cp"
+ * directly (0 to 35), and return TRUE.
+ *
+ * If a legal item is selected from the floor, we save it in "cp" as
+ * a negative (-1 to -511), and return TRUE.
+ *
+ * If no item is available, we do nothing to "cp", and we display a
+ * warning message, using "str" if available, and return FALSE.
+ *
+ * If no item is selected, we do nothing to "cp", and return FALSE.
+ *
+ * Global "p_ptr->command_new" is used when viewing the inventory or equipment
+ * to allow the user to enter a command while viewing those screens, and
+ * also to induce "auto-enter" of stores, and other such stuff.
+ *
+ * Global "p_ptr->command_see" may be set before calling this function to start
+ * out in "browse" mode. It is cleared before this function returns.
+ *
+ * Global "p_ptr->command_wrk" is used to choose between equip/inven listings.
+ * If it is TRUE then we are viewing inventory, else equipment.
+ *
+ * We always erase the prompt when we are done, leaving a blank line,
+ * or a warning message, if appropriate, if no items are available.
+ */
+bool get_item(int *cp, cptr pmt, cptr str, int mode)
+{
+ automatizer_create = FALSE;
+
+ return get_item_floor(cp, pmt, str, mode);
+}
+
+/*
+ * Hook to determine if an object is getable
+ */
+static bool item_tester_hook_getable(object_type *o_ptr)
+{
+ if (!inven_carry_okay(o_ptr)) return (FALSE);
+
+ if ((o_ptr->tval == TV_HYPNOS) && (!get_skill(SKILL_SYMBIOTIC))) return FALSE;
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+/*
+ * Wear a single item from o_ptr
+ */
+int wear_ammo(object_type *o_ptr)
+{
+ int slot, num = 1;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Check the slot */
+ slot = wield_slot(o_ptr);
+
+ if(slot == -1)
+ return -1;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ num = o_ptr->number;
+
+ /* Modify quantity */
+ q_ptr->number = num;
+
+ /* Access the wield slot */
+ o_ptr = &p_ptr->inventory[slot];
+
+ q_ptr->number += o_ptr->number;
+
+ /* Wear the new stuff */
+ object_copy(o_ptr, q_ptr);
+
+ /* Increment the equip counter by hand */
+ equip_cnt++;
+
+ /* Cursed! */
+ if (cursed_p(o_ptr))
+ {
+ /* Warn the player */
+ msg_print("Oops! It feels deathly cold!");
+
+ /* Note the curse */
+ o_ptr->ident |= (IDENT_SENSE);
+ o_ptr->sense = SENSE_CURSED;
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_MH);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ return slot;
+}
+
+
+/*
+ * Try to pickup arrows
+ */
+void pickup_ammo()
+{
+ s16b this_o_idx, next_o_idx = 0, slot;
+ char o_name[80];
+
+ /* Scan the pile of objects */
+ for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ if (object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]))
+ {
+ msg_print("You add the ammo to your quiver.");
+ slot = wear_ammo(o_ptr);
+
+ if (slot != -1)
+ {
+ /* Get the item again */
+ o_ptr = &p_ptr->inventory[slot];
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(slot));
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+ }
+}
+
+
+/*
+ * Make the player carry everything in a grid
+ *
+ * If "pickup" is FALSE then only gold will be picked up
+ *
+ * This is called by py_pickup() when easy_floor is TRUE.
+ */
+bool can_carry_heavy(object_type *o_ptr)
+{
+ /* Query if object is heavy */
+ if (prompt_pickup_heavy)
+ {
+ int i, j;
+ int old_enc = 0;
+ int new_enc = 0;
+
+ /* Extract the "weight limit" (in tenth pounds) */
+ i = weight_limit();
+
+ /* Calculate current encumbarance */
+ j = calc_total_weight();
+
+ /* Apply encumbarance from weight */
+ if (j > i / 2) old_enc = ((j - (i / 2)) / (i / 10));
+
+ /* Increase the weight, recalculate encumbarance */
+ j += (o_ptr->number * o_ptr->weight);
+
+ /* Apply encumbarance from weight */
+ if (j > i / 2) new_enc = ((j - (i / 2)) / (i / 10));
+
+ /* Should we query? */
+ if (new_enc > old_enc)
+ {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/* Do the actuall picking up */
+void object_pickup(int this_o_idx)
+{
+ int slot = 0;
+ char o_name[80] = "";
+ object_type *q_ptr, *o_ptr;
+
+ /* Access the item */
+ o_ptr = &o_list[this_o_idx];
+
+ if (p_ptr->auto_id)
+ {
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Note that the pack is too full */
+ if (!inven_carry_okay(o_ptr) && !object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]))
+ {
+ msg_format("You have no room for %s.", o_name);
+ }
+
+ /* Pick up object */
+ else
+ {
+ /* Tell the scripts */
+ if (process_hooks(HOOK_GET, "(O,d)", o_ptr, this_o_idx))
+ return;
+
+ q_ptr = &p_ptr->inventory[INVEN_AMMO];
+
+ /* Carry the item */
+ if (object_similar(o_ptr, q_ptr))
+ {
+ msg_print("You add the ammo to your quiver.");
+ slot = wear_ammo(o_ptr);
+ }
+ else
+ {
+ slot = inven_carry(o_ptr, FALSE);
+ }
+
+ /* Sanity check */
+ if (slot != -1)
+ {
+ /* Get the item again */
+ o_ptr = &p_ptr->inventory[slot];
+
+ object_track(o_ptr);
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(slot));
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+ }
+ }
+}
+
+
+void py_pickup_floor(int pickup)
+{
+ s16b this_o_idx, next_o_idx = 0;
+
+ char o_name[80] = "";
+ object_type *o_ptr = 0;
+
+ int floor_num = 0, floor_o_idx = 0;
+
+ bool do_pickup = TRUE;
+
+ bool do_ask = TRUE;
+
+ /* Hack -- ignore monster traps */
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_MON_TRAP) return;
+
+ /* Try to grab ammo */
+ pickup_ammo();
+
+ /* Mega Hack -- If we have auto-Id, do an ID sweep *before* squleching,
+ * so that we don't have to walk over things twice to get them
+ * squelched. --dsb */
+ if (p_ptr->auto_id)
+ {
+ this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx;
+
+ for (; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Aquire the object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire the next object index */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Identify Object */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+ }
+
+ /* Squeltch the floor */
+ squeltch_grid();
+
+ /* Scan the pile of objects */
+ for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Hack -- disturb */
+ disturb(0, 0);
+
+ /* Pick up gold */
+ if (o_ptr->tval == TV_GOLD)
+ {
+ char goldname[80];
+ object_desc(goldname, o_ptr, TRUE, 3);
+ /* Message */
+ msg_format("You have found %ld gold pieces worth of %s.",
+ (long)o_ptr->pval, goldname);
+
+ /* Collect the gold */
+ p_ptr->au += o_ptr->pval;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Delete the gold */
+ delete_object_idx(this_o_idx);
+
+ continue;
+ }
+
+ {
+ char testdesc[80];
+
+ object_desc(testdesc, o_ptr, TRUE, 3);
+ if (0 != strncmp(testdesc, "(nothing)", 80))
+ {
+ strncpy(o_name, testdesc, 80);
+ }
+ }
+
+ /* Count non-gold */
+ floor_num++;
+
+ /* Remember this index */
+ floor_o_idx = this_o_idx;
+ }
+
+ /* There were no non-gold items */
+ if (!floor_num) return;
+
+ /* Mention number of items */
+ if (!pickup)
+ {
+ /* One item */
+ if (floor_num == 1)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[floor_o_idx];
+
+ /* Message */
+ msg_format("You see %s.", o_name);
+ }
+
+ /* Multiple items */
+ else
+ {
+ /* Message */
+ msg_format("You see a pile of %d items.", floor_num);
+ }
+
+ /* Done */
+ return;
+ }
+
+ /* One item */
+ if (floor_num == 1)
+ {
+ /* Hack -- query every item */
+ if (carry_query_flag || (!can_carry_heavy(&o_list[floor_o_idx])))
+ {
+ if (!inven_carry_okay(o_ptr) && !object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]))
+ {
+ object_desc(o_name, o_ptr, TRUE, 3);
+ msg_format("You have no room for %s.", o_name);
+ do_pickup = FALSE;
+ }
+ else
+ {
+ char out_val[160];
+ sprintf(out_val, "Pick up %s? ", o_name);
+ do_pickup = get_check(out_val);
+ }
+ }
+
+ /* Don't ask */
+ do_ask = FALSE;
+
+ if ((o_list[floor_o_idx].tval == TV_HYPNOS) && (!get_skill(SKILL_SYMBIOTIC)))
+ do_pickup = FALSE;
+ else
+ this_o_idx = floor_o_idx;
+ }
+
+ /* Ask */
+ if (do_ask)
+ {
+ cptr q, s;
+
+ int item;
+
+ /* Get an item */
+
+ item_tester_hook = item_tester_hook_getable;
+
+ q = "Get which item? ";
+ s = "You have no room in your pack for any of the items here.";
+ if (get_item(&item, q, s, (USE_FLOOR)))
+ {
+ this_o_idx = 0 - item;
+
+ if (!can_carry_heavy(&o_list[this_o_idx]))
+ {
+ char out_val[160];
+
+ /* Describe the object */
+ object_desc(o_name, &o_list[this_o_idx], TRUE, 3);
+
+ sprintf(out_val, "Pick up %s? ", o_name);
+ do_pickup = get_check(out_val);
+ }
+ }
+ else
+ {
+ do_pickup = FALSE;
+ }
+ }
+
+ /* Pick up the item */
+ if (do_pickup)
+ {
+ object_pickup(this_o_idx);
+ }
+}
+
+/* Add a flags group */
+void gain_flag_group(object_type *o_ptr, bool silent)
+{
+ int grp = 0;
+ int tries = 1000;
+
+ while (tries--)
+ {
+ grp = rand_int(MAX_FLAG_GROUP);
+
+ /* If we already got this group continue */
+ if (o_ptr->pval3 & BIT(grp)) continue;
+
+ /* Not enough points ? */
+ if (flags_groups[grp].price > o_ptr->pval2) continue;
+
+ /* Ok, enough points and not already got it */
+ break;
+ }
+
+ /* Ack, nothing found */
+ if (tries <= 1) return;
+
+ o_ptr->pval2 -= flags_groups[grp].price;
+ o_ptr->pval3 |= BIT(grp);
+
+ if (!silent)
+ {
+ char o_name[80];
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("%s gains access to the %s realm.", o_name, flags_groups[grp].name);
+ }
+}
+
+u32b get_flag(object_type *o_ptr, int grp, int k)
+{
+ u32b f = 0, flag_set = 0;
+ int tries = 1000;
+ u32b f1, f2, f3, f4, f5, esp, flag_test;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* get the corresponding flag set of the group */
+ switch (k)
+ {
+ case 0:
+ flag_set = flags_groups[grp].flags1;
+ flag_test = f1;
+ break;
+ case 1:
+ flag_set = flags_groups[grp].flags2;
+ flag_test = f2;
+ break;
+ case 2:
+ flag_set = flags_groups[grp].flags3;
+ flag_test = f3;
+ break;
+ case 3:
+ flag_set = flags_groups[grp].flags4;
+ flag_test = f4;
+ break;
+ case 4:
+ flag_set = flags_groups[grp].esp;
+ flag_test = esp;
+ break;
+ default:
+ flag_set = flags_groups[grp].flags1;
+ flag_test = f1;
+ break;
+ }
+
+ /* If no flags, no need to look */
+ if (!count_bits(flag_set)) return 0;
+
+ while (tries--)
+ {
+ /* get a random flag */
+ f = BIT(rand_int(32));
+
+ /* is it part of the group */
+ if (!(f & flag_set)) continue;
+
+ /* Already got it */
+ if (f & flag_test) continue;
+
+ /* Ok one */
+ break;
+ }
+
+ if (tries <= 1) return (0);
+ else return (f);
+}
+
+/* Add a flags from a flag group */
+void gain_flag_group_flag(object_type *o_ptr, bool silent)
+{
+ int grp = 0, k = 0;
+ u32b f = 0;
+ int tries = 20000;
+
+ if (!count_bits(o_ptr->pval3)) return;
+
+ while (tries--)
+ {
+ /* Get a flag set */
+ k = rand_int(5);
+
+ /* get a flag group */
+ grp = rand_int(MAX_FLAG_GROUP);
+
+ if (!(BIT(grp) & o_ptr->pval3)) continue;
+
+ /* Return a flag from the group/set */
+ f = get_flag(o_ptr, grp, k);
+
+ if (!f) continue;
+
+ break;
+ }
+
+ if (tries <= 1) return;
+
+ switch (k)
+ {
+ case 0:
+ o_ptr->art_flags1 |= f;
+ break;
+ case 1:
+ o_ptr->art_flags2 |= f;
+ break;
+ case 2:
+ o_ptr->art_flags3 |= f;
+ break;
+ case 3:
+ o_ptr->art_flags4 |= f;
+ break;
+ case 4:
+ o_ptr->art_esp |= f;
+ break;
+ }
+
+ if (!silent)
+ {
+ char o_name[80];
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+ msg_format("%s gains a new power from the %s realm.", o_name, flags_groups[grp].name);
+ }
+}
+
+/*
+ * When an object gain a level, he can gain some attributes
+ */
+void object_gain_level(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* First it can gain some tohit and todam */
+ if ((o_ptr->tval == TV_AXE) || (o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM) ||
+ (o_ptr->tval == TV_HAFTED) || (o_ptr->tval == TV_MSTAFF))
+ {
+ int k = rand_int(100);
+
+ /* gain +2,+1 */
+ if (k < 33)
+ {
+ o_ptr->to_h += randint(2);
+ o_ptr->to_d += 1;
+ }
+ /* +1 and 1 point */
+ else if (k < 66)
+ {
+ o_ptr->to_h += 1;
+ o_ptr->pval2++;
+
+ if (magik(NEW_GROUP_CHANCE)) gain_flag_group(o_ptr, FALSE);
+ }
+ else
+ {
+ if (!o_ptr->pval3) gain_flag_group(o_ptr, FALSE);
+
+ gain_flag_group_flag(o_ptr, FALSE);
+
+ if (!o_ptr->pval) o_ptr->pval = 1;
+ else
+ {
+ while (magik(20 - (o_ptr->pval * 2))) o_ptr->pval++;
+
+ if (o_ptr->pval > 5) o_ptr->pval = 5;
+ }
+ }
+ }
+}
+
+
+/*
+ * Item sets fcts
+ */
+bool wield_set(s16b a_idx, s16b set_idx, bool silent)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+ for (i = 0; i < s_ptr->num; i++)
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+ if (!s_ptr->arts[i].present)
+ {
+ s_ptr->num_use++;
+ s_ptr->arts[i].present = TRUE;
+ if (s_ptr->num_use > s_ptr->num) msg_print("ERROR!! s_ptr->num_use > s_ptr->use");
+ else if ((s_ptr->num_use == s_ptr->num) && (!silent)) cmsg_format(TERM_GREEN, "%s item set completed.", s_ptr->name + set_name);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool takeoff_set(s16b a_idx, s16b set_idx)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+ for (i = 0; i < s_ptr->num; i++)
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+
+ if (s_ptr->arts[i].present)
+ {
+ s_ptr->arts[i].present = FALSE;
+ s_ptr->num_use--;
+
+ if (s_ptr->num_use == 255) msg_print("ERROR!! s_ptr->num_use < 0");
+ if (s_ptr->num_use == s_ptr->num - 1) cmsg_format(TERM_GREEN, "%s item set not complete anymore.", s_ptr->name + set_name);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool apply_set(s16b a_idx, s16b set_idx)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i, j;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+ for (i = 0; i < s_ptr->num; i++)
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+ if (s_ptr->arts[i].present)
+ {
+ for (j = 0; j < s_ptr->num_use; j++)
+ {
+ apply_flags(s_ptr->arts[i].flags1[j],
+ s_ptr->arts[i].flags2[j],
+ s_ptr->arts[i].flags3[j],
+ s_ptr->arts[i].flags4[j],
+ s_ptr->arts[i].flags5[j],
+ s_ptr->arts[i].esp[j],
+ s_ptr->arts[i].pval[j],
+ 0, 0, 0, 0);
+ }
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+bool apply_flags_set(s16b a_idx, s16b set_idx,
+ u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ set_type *s_ptr = &set_info[set_idx];
+ int i, j;
+
+ if ( -1 == a_info[a_idx].set) return (FALSE);
+
+ for (i = 0; i < s_ptr->num; i++)
+ {
+ if (a_idx == s_ptr->arts[i].a_idx) break;
+ }
+
+ if (s_ptr->arts[i].present)
+ {
+ for (j = 0; j < s_ptr->num_use; j++)
+ {
+ (*f1) |= s_ptr->arts[i].flags1[j];
+ (*f2) |= s_ptr->arts[i].flags2[j];
+ (*f3) |= s_ptr->arts[i].flags3[j];
+ (*f4) |= s_ptr->arts[i].flags4[j];
+ (*f5) |= s_ptr->arts[i].flags5[j];
+ (*esp) |= s_ptr->arts[i].esp[j];
+ }
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+
+
+
+
diff --git a/src/object2.c b/src/object2.c
new file mode 100644
index 00000000..b4750a3a
--- /dev/null
+++ b/src/object2.c
@@ -0,0 +1,6663 @@
+/* File: object2.c */
+
+/* Purpose: Object code, part 2 */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * adds n flags to o_ptr chosen randomly from the masks f1..f4
+ */
+#if 0
+static void enhance_random(object_type *o_ptr, int n, u32b f1, u32b f2, u32b f3, u32b f4)
+{
+ int counter = 0, null_mask;
+ u32b *f, *t, x;
+
+ while (n)
+ {
+ /* inefficient, but simple */
+ x = BIT(rand_int(32));
+ switch (randint(4))
+ {
+ case 1:
+ null_mask = TR1_NULL_MASK;
+ f = &f1;
+ t = &o_ptr->art_flags1;
+ break;
+ case 2:
+ null_mask = TR2_NULL_MASK;
+ f = &f2;
+ t = &o_ptr->art_flags2;
+ break;
+ case 3:
+ null_mask = TR3_NULL_MASK;
+ f = &f3;
+ t = &o_ptr->art_flags3;
+ break;
+ case 4:
+ null_mask = TR4_NULL_MASK;
+ f = &f4;
+ t = &o_ptr->art_flags4;
+ break;
+ default:
+ null_mask = 0;
+ f = t = NULL;
+ return;
+ }
+ if (++counter > 10000) break;
+ if (x & null_mask) continue;
+ if (!(x & *f)) continue;
+ if (x & *t) continue;
+ /* success */
+ *f &= ~x;
+ *t |= x;
+ n--;
+ }
+}
+#endif
+
+/*
+ * Calculate the player's total inventory weight.
+ */
+s32b calc_total_weight(void)
+{
+ int i;
+ s32b total;
+ for (i = total = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx) total += o_ptr->weight * o_ptr->number;
+ }
+ return total;
+}
+
+/*
+ * Excise a dungeon object from any stacks
+ */
+void excise_object_idx(int o_idx)
+{
+ object_type *j_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ s16b prev_o_idx = 0;
+
+
+ /* Object */
+ j_ptr = &o_list[o_idx];
+
+ /* Monster */
+ if (j_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Monster */
+ m_ptr = &m_list[j_ptr->held_m_idx];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Done */
+ if (this_o_idx == o_idx)
+ {
+ /* No previous */
+ if (prev_o_idx == 0)
+ {
+ /* Remove from list */
+ m_ptr->hold_o_idx = next_o_idx;
+ }
+
+ /* Real previous */
+ else
+ {
+ object_type *k_ptr;
+
+ /* Previous object */
+ k_ptr = &o_list[prev_o_idx];
+
+ /* Remove from list */
+ k_ptr->next_o_idx = next_o_idx;
+ }
+
+ /* Forget next pointer */
+ o_ptr->next_o_idx = 0;
+
+ /* Done */
+ break;
+ }
+
+ /* Save prev_o_idx */
+ prev_o_idx = this_o_idx;
+ }
+ }
+
+ /* Dungeon */
+ else
+ {
+ cave_type *c_ptr;
+
+ int y = j_ptr->iy;
+ int x = j_ptr->ix;
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Done */
+ if (this_o_idx == o_idx)
+ {
+ /* No previous */
+ if (prev_o_idx == 0)
+ {
+ /* Remove from list */
+ c_ptr->o_idx = next_o_idx;
+ }
+
+ /* Real previous */
+ else
+ {
+ object_type *k_ptr;
+
+ /* Previous object */
+ k_ptr = &o_list[prev_o_idx];
+
+ /* Remove from list */
+ k_ptr->next_o_idx = next_o_idx;
+ }
+
+ /* Forget next pointer */
+ o_ptr->next_o_idx = 0;
+
+ /* Done */
+ break;
+ }
+
+ /* Save prev_o_idx */
+ prev_o_idx = this_o_idx;
+ }
+ }
+}
+
+
+/*
+ * Delete a dungeon object
+ *
+ * Handle "stacks" of objects correctly.
+ */
+void delete_object_idx(int o_idx)
+{
+ object_type *j_ptr;
+
+ /* Excise */
+ excise_object_idx(o_idx);
+
+ /* Object */
+ j_ptr = &o_list[o_idx];
+
+ /* Dungeon floor */
+ if (!(j_ptr->held_m_idx))
+ {
+ int y, x;
+
+ /* Location */
+ y = j_ptr->iy;
+ x = j_ptr->ix;
+
+ /* Visual update */
+ lite_spot(y, x);
+ }
+
+ /* Wipe the object */
+ object_wipe(j_ptr);
+
+ /* Count objects */
+ o_cnt--;
+}
+
+
+/*
+ * Deletes all objects at given location
+ */
+void delete_object(int y, int x)
+{
+ cave_type *c_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Refuse "illegal" locations */
+ if (!in_bounds(y, x)) return;
+
+
+ /* Grid */
+ c_ptr = &cave[y][x];
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Wipe the object */
+ object_wipe(o_ptr);
+
+ /* Count objects */
+ o_cnt--;
+ }
+
+ /* Objects are gone */
+ c_ptr->o_idx = 0;
+
+ /* Visual update */
+ lite_spot(y, x);
+}
+
+
+/*
+ * Move an object from index i1 to index i2 in the object list
+ */
+static void compact_objects_aux(int i1, int i2)
+{
+ int i;
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+
+
+ /* Do nothing */
+ if (i1 == i2) return;
+
+
+ /* Repair objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[i];
+
+ /* Skip "dead" objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Repair "next" pointers */
+ if (o_ptr->next_o_idx == i1)
+ {
+ /* Repair */
+ o_ptr->next_o_idx = i2;
+ }
+ }
+
+
+ /* Acquire object */
+ o_ptr = &o_list[i1];
+
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Repair monster */
+ if (m_ptr->hold_o_idx == i1)
+ {
+ /* Repair */
+ m_ptr->hold_o_idx = i2;
+ }
+ }
+
+ /* Dungeon */
+ else
+ {
+ int y, x;
+
+ /* Acquire location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+
+ /* Acquire grid */
+ c_ptr = &cave[y][x];
+
+ /* Repair grid */
+ if (c_ptr->o_idx == i1)
+ {
+ /* Repair */
+ c_ptr->o_idx = i2;
+ }
+ }
+
+
+ /* Structure copy */
+ o_list[i2] = o_list[i1];
+
+ /* Wipe the hole */
+ object_wipe(o_ptr);
+}
+
+
+/*
+ * Compact and Reorder the object list
+ *
+ * This function can be very dangerous, use with caution!
+ *
+ * When actually "compacting" objects, we base the saving throw on a
+ * combination of object level, distance from player, and current
+ * "desperation".
+ *
+ * After "compacting" (if needed), we "reorder" the objects into a more
+ * compact order, and we reset the allocation info, and the "live" array.
+ */
+void compact_objects(int size)
+{
+ int i, y, x, num;
+
+ int cur_lev, cur_dis, chance;
+
+ /* Compact */
+ if (size)
+ {
+ /* Message */
+ msg_print("Compacting objects...");
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+
+
+ /* Compact at least 'size' objects */
+ for (num = 0, cur_lev = 1; num < size; cur_lev++)
+ {
+ /* Get closer each iteration (start at distance 12). Around level 100 distance-protect nothing. */
+ cur_dis = 12 * (101 - cur_lev) / 100;
+
+ /* Examine the objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* High level objects are "immune" as long as we're not desperate enough */
+ if (k_ptr->level > cur_lev) continue;
+
+ /* Monster owned objects */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Acquire monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Monsters start with protecting objects well */
+ chance = 100;
+
+ /* Get the location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ /* Dungeon floor objects */
+ else
+ {
+ /* Floor objects start with lower protection */
+ chance = 90;
+
+ /* Get the location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Near enough objects are "immune", even if low level */
+ /* (like, importantly, food rations after hitting a trap of drop items) */
+ if ((cur_dis > 0) && (distance(p_ptr->py, p_ptr->px, y, x) < cur_dis)) continue;
+
+ /* object protection goes down as we get vicious */
+ /* around level 200 only artifacts have protection */
+ chance = chance - cur_lev / 2;
+
+ /* Artifacts */
+ if ( artifact_p(o_ptr) || o_ptr->art_name )
+ {
+ /* Artifacts are "immune if the level is lower */
+ /* than 300 + artifact level */
+ if ( cur_lev < 300 + k_ptr->level )
+ continue;
+
+ /* That's 400 + level for fixed artifacts */
+ if ( (k_ptr->flags3 & TR3_NORM_ART) && cur_lev < 400 + k_ptr->level )
+ continue;
+
+ /* Never protect if level is high enough; so we don't wipe a better artifact */
+ chance = -1;
+
+ /* rewind the level so we never wipe many */
+ /* artifacts of same level if one will do!!! */
+ cur_lev--;
+ }
+
+ /* Maybe some code to spare the God relic here. But I'd rather raise its level to 150 */
+
+ /* Apply the saving throw */
+ if (rand_int(100) < chance) continue;
+
+ /* Delete the object */
+ delete_object_idx(i);
+
+ /* Count it */
+ num++;
+ }
+ }
+
+
+ /* Excise dead objects (backwards!) */
+ for (i = o_max - 1; i >= 1; i--)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip real objects */
+ if (o_ptr->k_idx) continue;
+
+ /* Move last object into open hole */
+ compact_objects_aux(o_max - 1, i);
+
+ /* Compress "o_max" */
+ o_max--;
+ }
+}
+
+
+
+
+/*
+ * Delete all the items when player leaves the level
+ *
+ * Note -- we do NOT visually reflect these (irrelevant) changes
+ *
+ * Hack -- we clear the "c_ptr->o_idx" field for every grid,
+ * and the "m_ptr->next_o_idx" field for every monster, since
+ * we know we are clearing every object. Technically, we only
+ * clear those fields for grids/monsters containing objects,
+ * and we clear it once for every such object.
+ */
+void wipe_o_list(void)
+{
+ int i;
+
+ /* Delete the existing objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Mega-Hack -- preserve artifacts */
+ if (!character_dungeon || p_ptr->preserve)
+ {
+ /* Hack -- Preserve unknown artifacts */
+ if (artifact_p(o_ptr) && !object_known_p(o_ptr))
+ {
+ /* Mega-Hack -- Preserve the artifact */
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[o_ptr->k_idx].artifact = FALSE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ monster_type *m_ptr;
+
+ /* Monster */
+ m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Hack -- see above */
+ m_ptr->hold_o_idx = 0;
+ }
+
+ /* Dungeon */
+ else
+ {
+ cave_type *c_ptr;
+
+ /* Access location */
+ int y = o_ptr->iy;
+ int x = o_ptr->ix;
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Hack -- see above */
+ c_ptr->o_idx = 0;
+ }
+
+ /* Wipe the object */
+ o_ptr = WIPE(o_ptr, object_type);
+ }
+
+ /* Reset "o_max" */
+ o_max = 1;
+
+ /* Reset "o_cnt" */
+ o_cnt = 0;
+}
+
+
+/*
+ * Acquires and returns the index of a "free" object.
+ *
+ * This routine should almost never fail, but in case it does,
+ * we must be sure to handle "failure" of this routine.
+ */
+s16b o_pop(void)
+{
+ int i;
+
+
+ /* Initial allocation */
+ if (o_max < max_o_idx)
+ {
+ /* Get next space */
+ i = o_max;
+
+ /* Expand object array */
+ o_max++;
+
+ /* Count objects */
+ o_cnt++;
+
+ /* Use this object */
+ return (i);
+ }
+
+
+ /* Recycle dead objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[i];
+
+ /* Skip live objects */
+ if (o_ptr->k_idx) continue;
+
+ /* Count objects */
+ o_cnt++;
+
+ /* Use this object */
+ return (i);
+ }
+
+
+ /* Warn the player (except during dungeon creation) */
+ if (character_dungeon) msg_print("Too many objects!");
+
+ /* Oops */
+ return (0);
+}
+
+
+
+/*
+ * Apply a "object restriction function" to the "object allocation table"
+ */
+errr get_obj_num_prep(void)
+{
+ int i;
+
+ /* Get the entry */
+ alloc_entry *table = alloc_kind_table;
+
+ /* Scan the allocation table */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Accept objects which pass the restriction, if any */
+ if (!get_obj_num_hook || (*get_obj_num_hook)(table[i].index))
+ {
+ /* Accept this object */
+ table[i].prob2 = table[i].prob1;
+ }
+
+ /* Do not use this object */
+ else
+ {
+ /* Decline this object */
+ table[i].prob2 = 0;
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Choose an object kind that seems "appropriate" to the given level
+ *
+ * This function uses the "prob2" field of the "object allocation table",
+ * and various local information, to calculate the "prob3" field of the
+ * same table, which is then used to choose an "appropriate" object, in
+ * a relatively efficient manner.
+ *
+ * It is (slightly) more likely to acquire an object of the given level
+ * than one of a lower level. This is done by choosing several objects
+ * appropriate to the given level and keeping the "hardest" one.
+ *
+ * Note that if no objects are "appropriate", then this function will
+ * fail, and return zero, but this should *almost* never happen.
+ */
+s16b get_obj_num(int level)
+{
+ int i, j, p;
+ int k_idx;
+ long value, total;
+ object_kind *k_ptr;
+ alloc_entry *table = alloc_kind_table;
+
+
+ /* Boost level */
+ if (level > 0)
+ {
+ /* Occasional "boost" */
+ if (rand_int(GREAT_OBJ) == 0)
+ {
+ /* What a bizarre calculation */
+ level = 1 + (level * MAX_DEPTH / randint(MAX_DEPTH));
+ }
+ }
+
+
+ /* Reset total */
+ total = 0L;
+
+ /* Process probabilities */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Objects are sorted by depth */
+ if (table[i].level > level) break;
+
+ /* Default */
+ table[i].prob3 = 0;
+
+ /* Access the index */
+ k_idx = table[i].index;
+
+ /* Access the actual kind */
+ k_ptr = &k_info[k_idx];
+
+ /* Hack -- prevent embedded chests */
+ if (opening_chest && (k_ptr->tval == TV_CHEST)) continue;
+
+ /* Accept */
+ table[i].prob3 = table[i].prob2;
+
+ /* Total */
+ total += table[i].prob3;
+ }
+
+ /* No legal objects */
+ if (total <= 0) return (0);
+
+
+ /* Pick an object */
+ value = rand_int(total);
+
+ /* Find the object */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+
+ /* Power boost */
+ p = rand_int(100);
+
+ /* Try for a "better" object once (50%) or twice (10%) */
+ if (p < 60)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a object */
+ value = rand_int(total);
+
+ /* Find the monster */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+ /* Try for a "better" object twice (10%) */
+ if (p < 10)
+ {
+ /* Save old */
+ j = i;
+
+ /* Pick a object */
+ value = rand_int(total);
+
+ /* Find the object */
+ for (i = 0; i < alloc_kind_size; i++)
+ {
+ /* Found the entry */
+ if (value < table[i].prob3) break;
+
+ /* Decrement */
+ value = value - table[i].prob3;
+ }
+
+ /* Keep the "best" one */
+ if (table[i].level < table[j].level) i = j;
+ }
+
+
+ /* Result */
+ return (table[i].index);
+}
+
+
+
+
+
+
+
+
+/*
+ * Known is true when the "attributes" of an object are "known".
+ * These include tohit, todam, toac, cost, and pval (charges).
+ *
+ * Note that "knowing" an object gives you everything that an "awareness"
+ * gives you, and much more. In fact, the player is always "aware" of any
+ * item of which he has full "knowledge".
+ *
+ * But having full knowledge of, say, one "wand of wonder", does not, by
+ * itself, give you knowledge, or even awareness, of other "wands of wonder".
+ * It happens that most "identify" routines (including "buying from a shop")
+ * will make the player "aware" of the object as well as fully "know" it.
+ *
+ * This routine also removes any inscriptions generated by "feelings".
+ */
+void object_known(object_type *o_ptr)
+{
+
+ /* No Sensing */
+ o_ptr->sense = SENSE_NONE;
+
+ /* Clear the "Felt" info */
+ o_ptr->ident &= ~(IDENT_SENSE);
+
+ /* Clear the "Empty" info */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+
+ /* Now we know about the item */
+ o_ptr->ident |= (IDENT_KNOWN);
+}
+
+
+
+
+
+/*
+ * The player is now aware of the effects of the given object.
+ */
+void object_aware(object_type *o_ptr)
+{
+ /* Fully aware of the effects */
+ k_info[o_ptr->k_idx].aware = TRUE;
+}
+
+
+
+/*
+ * Something has been "sampled"
+ */
+void object_tried(object_type *o_ptr)
+{
+ /* Mark it as tried (even if "aware") */
+ k_info[o_ptr->k_idx].tried = TRUE;
+}
+
+
+
+/*
+ * Return the "value" of an "unknown" item
+ * Make a guess at the value of non-aware items
+ */
+static s32b object_value_base(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Aware item -- use template cost */
+ if ((object_aware_p(o_ptr)) && (o_ptr->tval != TV_EGG)) return (k_ptr->cost);
+
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Un-aware Food */
+ case TV_FOOD:
+ return (5L);
+
+ /* Un-aware Potions */
+ case TV_POTION2:
+ return (20L);
+
+ /* Un-aware Potions */
+ case TV_POTION:
+ return (20L);
+
+ /* Un-aware Scrolls */
+ case TV_SCROLL:
+ return (20L);
+
+ /* Un-aware Staffs */
+ case TV_STAFF:
+ return (70L);
+
+ /* Un-aware Wands */
+ case TV_WAND:
+ return (50L);
+
+ /* Un-aware Rods */
+ case TV_ROD:
+ return (90L);
+
+ /* Un-aware Rings */
+ case TV_RING:
+ return (45L);
+
+ /* Un-aware Amulets */
+ case TV_AMULET:
+ return (45L);
+
+ /* Eggs */
+ case TV_EGG:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Pay the monster level */
+ return (r_ptr->level * 100) + 100;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Paranoia -- Oops */
+ return (0L);
+}
+
+/* Return the value of the flags the object has... */
+s32b flag_cost(object_type * o_ptr, int plusses)
+{
+ s32b total = 0;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f5 & TR5_TEMPORARY)
+ {
+ return 0;
+ }
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ return 0;
+ }
+ if (f1 & TR1_STR) total += (1000 * plusses);
+ if (f1 & TR1_INT) total += (1000 * plusses);
+ if (f1 & TR1_WIS) total += (1000 * plusses);
+ if (f1 & TR1_DEX) total += (1000 * plusses);
+ if (f1 & TR1_CON) total += (1000 * plusses);
+ if (f1 & TR1_CHR) total += (250 * plusses);
+ if (f1 & TR1_CHAOTIC) total += 10000;
+ if (f1 & TR1_VAMPIRIC) total += 13000;
+ if (f1 & TR1_STEALTH) total += (250 * plusses);
+ if (f1 & TR1_SEARCH) total += (100 * plusses);
+ if (f1 & TR1_INFRA) total += (150 * plusses);
+ if (f1 & TR1_TUNNEL) total += (175 * plusses);
+ if ((f1 & TR1_SPEED) && (plusses > 0))
+ total += (10000 + (2500 * plusses));
+ if ((f1 & TR1_BLOWS) && (plusses > 0))
+ total += (10000 + (2500 * plusses));
+ if (f1 & TR1_MANA) total += (1000 * plusses);
+ if (f1 & TR1_SPELL) total += (2000 * plusses);
+ if (f1 & TR1_SLAY_ANIMAL) total += 3500;
+ if (f1 & TR1_SLAY_EVIL) total += 4500;
+ if (f1 & TR1_SLAY_UNDEAD) total += 3500;
+ if (f1 & TR1_SLAY_DEMON) total += 3500;
+ if (f1 & TR1_SLAY_ORC) total += 3000;
+ if (f1 & TR1_SLAY_TROLL) total += 3500;
+ if (f1 & TR1_SLAY_GIANT) total += 3500;
+ if (f1 & TR1_SLAY_DRAGON) total += 3500;
+ if (f5 & TR5_KILL_DEMON) total += 5500;
+ if (f5 & TR5_KILL_UNDEAD) total += 5500;
+ if (f1 & TR1_KILL_DRAGON) total += 5500;
+ if (f1 & TR1_VORPAL) total += 5000;
+ if (f1 & TR1_IMPACT) total += 5000;
+ if (f1 & TR1_BRAND_POIS) total += 7500;
+ if (f1 & TR1_BRAND_ACID) total += 7500;
+ if (f1 & TR1_BRAND_ELEC) total += 7500;
+ if (f1 & TR1_BRAND_FIRE) total += 5000;
+ if (f1 & TR1_BRAND_COLD) total += 5000;
+ if (f2 & TR2_SUST_STR) total += 850;
+ if (f2 & TR2_SUST_INT) total += 850;
+ if (f2 & TR2_SUST_WIS) total += 850;
+ if (f2 & TR2_SUST_DEX) total += 850;
+ if (f2 & TR2_SUST_CON) total += 850;
+ if (f2 & TR2_SUST_CHR) total += 250;
+ if (f2 & TR2_INVIS) total += 3000;
+ if (f2 & TR2_LIFE) total += (5000 * plusses);
+ if (f2 & TR2_IM_ACID) total += 10000;
+ if (f2 & TR2_IM_ELEC) total += 10000;
+ if (f2 & TR2_IM_FIRE) total += 10000;
+ if (f2 & TR2_IM_COLD) total += 10000;
+ if (f2 & TR2_SENS_FIRE) total -= 100;
+ if (f2 & TR2_REFLECT) total += 10000;
+ if (f2 & TR2_FREE_ACT) total += 4500;
+ if (f2 & TR2_HOLD_LIFE) total += 8500;
+ if (f2 & TR2_RES_ACID) total += 1250;
+ if (f2 & TR2_RES_ELEC) total += 1250;
+ if (f2 & TR2_RES_FIRE) total += 1250;
+ if (f2 & TR2_RES_COLD) total += 1250;
+ if (f2 & TR2_RES_POIS) total += 2500;
+ if (f2 & TR2_RES_FEAR) total += 2500;
+ if (f2 & TR2_RES_LITE) total += 1750;
+ if (f2 & TR2_RES_DARK) total += 1750;
+ if (f2 & TR2_RES_BLIND) total += 2000;
+ if (f2 & TR2_RES_CONF) total += 2000;
+ if (f2 & TR2_RES_SOUND) total += 2000;
+ if (f2 & TR2_RES_SHARDS) total += 2000;
+ if (f2 & TR2_RES_NETHER) total += 2000;
+ if (f2 & TR2_RES_NEXUS) total += 2000;
+ if (f2 & TR2_RES_CHAOS) total += 2000;
+ if (f2 & TR2_RES_DISEN) total += 10000;
+ if (f3 & TR3_SH_FIRE) total += 5000;
+ if (f3 & TR3_SH_ELEC) total += 5000;
+ if (f3 & TR3_DECAY) total += 0;
+ if (f3 & TR3_NO_TELE) total += 2500;
+ if (f3 & TR3_NO_MAGIC) total += 2500;
+ if (f3 & TR3_WRAITH) total += 250000;
+ if (f3 & TR3_TY_CURSE) total -= 15000;
+ if (f3 & TR3_EASY_KNOW) total += 0;
+ if (f3 & TR3_HIDE_TYPE) total += 0;
+ if (f3 & TR3_SHOW_MODS) total += 0;
+ if (f3 & TR3_INSTA_ART) total += 0;
+ if (f3 & TR3_LITE1) total += 750;
+ if (f4 & TR4_LITE2) total += 1250;
+ if (f4 & TR4_LITE3) total += 2750;
+ if (f3 & TR3_SEE_INVIS) total += 2000;
+ if (esp) total += (12500 * count_bits(esp));
+ if (f3 & TR3_SLOW_DIGEST) total += 750;
+ if (f3 & TR3_REGEN) total += 2500;
+ if (f3 & TR3_XTRA_MIGHT) total += 2250;
+ if (f3 & TR3_XTRA_SHOTS) total += 10000;
+ if (f3 & TR3_IGNORE_ACID) total += 100;
+ if (f3 & TR3_IGNORE_ELEC) total += 100;
+ if (f3 & TR3_IGNORE_FIRE) total += 100;
+ if (f3 & TR3_IGNORE_COLD) total += 100;
+ if (f3 & TR3_ACTIVATE) total += 100;
+ if (f3 & TR3_DRAIN_EXP) total -= 12500;
+ if (f3 & TR3_TELEPORT)
+ {
+ if (o_ptr->ident & IDENT_CURSED)
+ total -= 7500;
+ else
+ total += 250;
+ }
+ if (f3 & TR3_AGGRAVATE) total -= 10000;
+ if (f3 & TR3_BLESSED) total += 750;
+ if ((f3 & TR3_CURSED) && (o_ptr->ident & IDENT_CURSED)) total -= 5000;
+ if ((f3 & TR3_HEAVY_CURSE) && (o_ptr->ident & IDENT_CURSED)) total -= 12500;
+ if (f3 & TR3_PERMA_CURSE) total -= 15000;
+ if (f3 & TR3_FEATHER) total += 1250;
+ if (f4 & TR4_FLY) total += 10000;
+ if (f4 & TR4_NEVER_BLOW) total -= 15000;
+ if (f4 & TR4_PRECOGNITION) total += 250000;
+ if (f4 & TR4_BLACK_BREATH) total -= 12500;
+ if (f4 & TR4_DG_CURSE) total -= 25000;
+ if (f4 & TR4_CLONE) total -= 10000;
+ if (f4 & TR4_LEVELS) total += o_ptr->elevel * 2000;
+
+ /* Also, give some extra for activatable powers... */
+
+ if ((o_ptr->art_name) && (o_ptr->art_flags3 & (TR3_ACTIVATE)))
+ {
+ int type = o_ptr->xtra2;
+
+ if (type == ACT_SUNLIGHT) total += 250;
+ else if (type == ACT_BO_MISS_1) total += 250;
+ else if (type == ACT_BA_POIS_1) total += 300;
+ else if (type == ACT_BO_ELEC_1) total += 250;
+ else if (type == ACT_BO_ACID_1) total += 250;
+ else if (type == ACT_BO_COLD_1) total += 250;
+ else if (type == ACT_BO_FIRE_1) total += 250;
+ else if (type == ACT_BA_COLD_1) total += 750;
+ else if (type == ACT_BA_FIRE_1) total += 1000;
+ else if (type == ACT_DRAIN_1) total += 500;
+ else if (type == ACT_BA_COLD_2) total += 1250;
+ else if (type == ACT_BA_ELEC_2) total += 1500;
+ else if (type == ACT_DRAIN_2) total += 750;
+ else if (type == ACT_VAMPIRE_1) total += 1000;
+ else if (type == ACT_BO_MISS_2) total += 1000;
+ else if (type == ACT_BA_FIRE_2) total += 1750;
+ else if (type == ACT_BA_COLD_3) total += 2500;
+ else if (type == ACT_BA_ELEC_3) total += 2500;
+ else if (type == ACT_WHIRLWIND) total += 7500;
+ else if (type == ACT_VAMPIRE_2) total += 2500;
+ else if (type == ACT_CALL_CHAOS) total += 5000;
+ else if (type == ACT_ROCKET) total += 5000;
+ else if (type == ACT_DISP_EVIL) total += 4000;
+ else if (type == ACT_DISP_GOOD) total += 3500;
+ else if (type == ACT_BA_MISS_3) total += 5000;
+ else if (type == ACT_CONFUSE) total += 500;
+ else if (type == ACT_SLEEP) total += 750;
+ else if (type == ACT_QUAKE) total += 600;
+ else if (type == ACT_TERROR) total += 2500;
+ else if (type == ACT_TELE_AWAY) total += 2000;
+ else if (type == ACT_GENOCIDE) total += 10000;
+ else if (type == ACT_MASS_GENO) total += 10000;
+ else if (type == ACT_CHARM_ANIMAL) total += 7500;
+ else if (type == ACT_CHARM_UNDEAD) total += 10000;
+ else if (type == ACT_CHARM_OTHER) total += 10000;
+ else if (type == ACT_CHARM_ANIMALS) total += 12500;
+ else if (type == ACT_CHARM_OTHERS) total += 17500;
+ else if (type == ACT_SUMMON_ANIMAL) total += 10000;
+ else if (type == ACT_SUMMON_PHANTOM) total += 12000;
+ else if (type == ACT_SUMMON_ELEMENTAL) total += 15000;
+ else if (type == ACT_SUMMON_DEMON) total += 20000;
+ else if (type == ACT_SUMMON_UNDEAD) total += 20000;
+ else if (type == ACT_CURE_LW) total += 500;
+ else if (type == ACT_CURE_MW) total += 750;
+ else if (type == ACT_REST_LIFE) total += 7500;
+ else if (type == ACT_REST_ALL) total += 15000;
+ else if (type == ACT_CURE_700) total += 10000;
+ else if (type == ACT_CURE_1000) total += 15000;
+ else if (type == ACT_ESP) total += 1500;
+ else if (type == ACT_BERSERK) total += 800;
+ else if (type == ACT_PROT_EVIL) total += 5000;
+ else if (type == ACT_RESIST_ALL) total += 5000;
+ else if (type == ACT_SPEED) total += 15000;
+ else if (type == ACT_XTRA_SPEED) total += 25000;
+ else if (type == ACT_WRAITH) total += 25000;
+ else if (type == ACT_INVULN) total += 25000;
+ else if (type == ACT_LIGHT) total += 150;
+ else if (type == ACT_MAP_LIGHT) total += 500;
+ else if (type == ACT_DETECT_ALL) total += 1000;
+ else if (type == ACT_DETECT_XTRA) total += 12500;
+ else if (type == ACT_ID_FULL) total += 10000;
+ else if (type == ACT_ID_PLAIN) total += 1250;
+ else if (type == ACT_RUNE_EXPLO) total += 4000;
+ else if (type == ACT_RUNE_PROT) total += 10000;
+ else if (type == ACT_SATIATE) total += 2000;
+ else if (type == ACT_DEST_DOOR) total += 100;
+ else if (type == ACT_STONE_MUD) total += 1000;
+ else if (type == ACT_RECHARGE) total += 1000;
+ else if (type == ACT_ALCHEMY) total += 10000;
+ else if (type == ACT_DIM_DOOR) total += 10000;
+ else if (type == ACT_TELEPORT) total += 2000;
+ else if (type == ACT_RECALL) total += 7500;
+ }
+
+ return total;
+}
+
+
+
+/*
+ * Return the "real" price of a "known" item, not including discounts
+ *
+ * Wand and staffs get cost for each charge
+ *
+ * Armor is worth an extra 100 gold per bonus point to armor class.
+ *
+ * Weapons are worth an extra 100 gold per bonus point (AC,TH,TD).
+ *
+ * Missiles are only worth 5 gold per bonus point, since they
+ * usually appear in groups of 20, and we want the player to get
+ * the same amount of cash for any "equivalent" item. Note that
+ * missiles never have any of the "pval" flags, and in fact, they
+ * only have a few of the available flags, primarily of the "slay"
+ * and "brand" and "ignore" variety.
+ *
+ * Armor with a negative armor bonus is worthless.
+ * Weapons with negative hit+damage bonuses are worthless.
+ *
+ * Every wearable item with a "pval" bonus is worth extra (see below).
+ */
+s32b object_value_real(object_type *o_ptr)
+{
+ s32b value;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ if (o_ptr->tval == TV_RANDART)
+ {
+ return random_artifacts[o_ptr->sval].cost;
+ }
+
+ /* Hack -- "worthless" items */
+ if (!k_ptr->cost) return (0L);
+
+ /* Base cost */
+ value = k_ptr->cost;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f5 & TR5_TEMPORARY) return (0L);
+
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3)
+ {
+ value += flag_cost (o_ptr, o_ptr->pval);
+ }
+ /* Artifact */
+ else if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Hack -- "worthless" artifacts */
+ if (!a_ptr->cost) return (0L);
+
+ /* Hack -- Use the artifact cost instead */
+ value = a_ptr->cost;
+ }
+
+ /* Ego-Item */
+ else if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ /* Hack -- "worthless" ego-items */
+ if (!e_ptr->cost) return (0L);
+
+ /* Hack -- Reward the ego-item with a bonus */
+ value += e_ptr->cost;
+
+ if (o_ptr->name2b)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2b];
+
+ /* Hack -- "worthless" ego-items */
+ if (!e_ptr->cost) return (0L);
+
+ /* Hack -- Reward the ego-item with a bonus */
+ value += e_ptr->cost;
+ }
+ }
+
+ /* Pay the spell */
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ if (o_ptr->pval2 != -1)
+ value += 5000 + 500 * school_spells[o_ptr->pval2].skill_level;
+ else
+ value += 5000;
+ }
+
+ /* Analyze pval bonus */
+ switch (o_ptr->tval)
+ {
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_LITE:
+ case TV_AMULET:
+ case TV_RING:
+ case TV_MSTAFF:
+ case TV_TRAPKIT:
+ case TV_INSTRUMENT:
+ {
+#if 0 /* DG - no */
+ /* Hack -- Negative "pval" is always bad */
+ if (o_ptr->pval < 0) return (0L);
+#endif
+ /* No pval */
+ if (!o_ptr->pval) break;
+
+ /* Give credit for stat bonuses */
+ if (f1 & (TR1_STR)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_INT)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_WIS)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_DEX)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_CON)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_CHR)) value += (o_ptr->pval * 200L);
+
+ if (f5 & (TR5_CRIT)) value += (o_ptr->pval * 500L);
+
+ /* Give credit for stealth and searching */
+ if (f1 & (TR1_STEALTH)) value += (o_ptr->pval * 100L);
+ if (f1 & (TR1_SEARCH)) value += (o_ptr->pval * 100L);
+
+ /* Give credit for infra-vision and tunneling */
+ if (f1 & (TR1_INFRA)) value += (o_ptr->pval * 50L);
+ if (f1 & (TR1_TUNNEL)) value += (o_ptr->pval * 50L);
+
+ /* Give credit for extra attacks */
+ if (f1 & (TR1_BLOWS)) value += (o_ptr->pval * 2000L);
+
+ /* Give credit for speed bonus */
+ if (f1 & (TR1_SPEED)) value += (o_ptr->pval * 30000L);
+
+ break;
+ }
+ }
+
+
+ /* Analyze the item */
+ switch (o_ptr->tval)
+ {
+ /* Eggs */
+ case TV_EGG:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Pay the monster level */
+ value += r_ptr->level * 100;
+
+ /* Done */
+ break;
+ }
+
+ /* Wands/Staffs */
+ case TV_WAND:
+ {
+ /* Par for the spell */
+ value *= school_spells[o_ptr->pval2].skill_level;
+ /* Take the average of the base and max spell levels */
+ value *= (((o_ptr->pval3 >> 16) & 0xFFFF) + (o_ptr->pval3 & 0xFFFF)) / 2;
+ /* Hack */
+ value /= 6;
+
+ /* Pay extra for charges */
+ value += ((value / 20) * o_ptr->pval) / o_ptr->number;
+
+ /* Done */
+ break;
+ }
+ case TV_STAFF:
+ {
+ /* Par for the spell */
+ value *= school_spells[o_ptr->pval2].skill_level;
+ /* Take the average of the base and max spell levels */
+ value *= (((o_ptr->pval3 >> 16) & 0xFFFF) + (o_ptr->pval3 & 0xFFFF)) / 2;
+ /* Hack */
+ value /= 6;
+
+ /* Pay extra for charges */
+ value += ((value / 20) * o_ptr->pval);
+
+ /* Done */
+ break;
+ }
+ case TV_BOOK:
+ {
+ if (o_ptr->sval == 255)
+ {
+ /* Pay extra for the spell */
+ value = value * school_spells[o_ptr->pval].skill_level;
+ }
+ /* Done */
+ break;
+ }
+
+ /* Rods */
+ case TV_ROD_MAIN:
+ {
+ s16b tip_idx;
+
+ /* It's not combined */
+ if (o_ptr->pval == 0) break;
+
+ /* Look up the tip attached */
+ tip_idx = lookup_kind(TV_ROD, o_ptr->pval);
+
+ /* Paranoia */
+ if (tip_idx > 0)
+ {
+ /* Add its cost */
+ value += k_info[tip_idx].cost;
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Rings/Amulets */
+ case TV_RING:
+ case TV_AMULET:
+ {
+ /* Hack -- negative bonuses are bad */
+ if (o_ptr->to_a < 0 && !value) return (0L);
+ if (o_ptr->to_h < 0 && !value) return (0L);
+ if (o_ptr->to_d < 0 && !value) return (0L);
+
+ /* Give credit for bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 100L);
+
+ /* Done */
+ break;
+ }
+
+ /* Armor */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ /* Hack -- negative armor bonus */
+ if (o_ptr->to_a < 0 && !value) return (0L);
+
+ /* Give credit for bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 100L);
+
+ /* Done */
+ break;
+ }
+
+ /* Bows/Weapons */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_SWORD:
+ case TV_DAEMON_BOOK:
+ case TV_AXE:
+ case TV_POLEARM:
+ case TV_TRAPKIT:
+ {
+ /* Hack -- negative hit/damage bonuses */
+ if (o_ptr->to_h + o_ptr->to_d < 0 && !value) return (0L);
+
+ /* Factor in the bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d + o_ptr->to_a) * 100L);
+
+ /* Hack -- Factor in extra damage dice */
+ if ((o_ptr->dd > k_ptr->dd) && (o_ptr->ds == k_ptr->ds))
+ {
+ value += (o_ptr->dd - k_ptr->dd) * o_ptr->ds * 100L;
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ {
+ /* Hack -- negative hit/damage bonuses */
+ if (o_ptr->to_h + o_ptr->to_d < 0 && !value) return (0L);
+
+ /* Factor in the bonuses */
+ value += ((o_ptr->to_h + o_ptr->to_d) * 5L);
+
+ /* Hack -- Factor in extra damage dice */
+ if ((o_ptr->dd > k_ptr->dd) && (o_ptr->ds == k_ptr->ds))
+ {
+ value += (o_ptr->dd - k_ptr->dd) * o_ptr->ds * 5L;
+ }
+
+ /* Special attack (exploding arrow) */
+ if (o_ptr->pval2 != 0) value *= 14;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Return the value */
+ return (value);
+}
+
+
+/*
+ * Return the price of an item including plusses (and charges)
+ *
+ * This function returns the "value" of the given item (qty one)
+ *
+ * Never notice "unknown" bonuses or properties, including "curses",
+ * since that would give the player information he did not have.
+ *
+ * Note that discounted items stay discounted forever, even if
+ * the discount is "forgotten" by the player via memory loss.
+ */
+s32b object_value(object_type *o_ptr)
+{
+ s32b value;
+
+
+ /* Unknown items -- acquire a base value */
+ if (object_known_p(o_ptr))
+ {
+ /* Cursed items -- worthless */
+ if (cursed_p(o_ptr)) return (0L);
+
+ /* Real value (see above) */
+ value = object_value_real(o_ptr);
+ }
+
+ /* Known items -- acquire the actual value */
+ else
+ {
+ /* Hack -- Felt cursed items */
+ if ((o_ptr->ident & (IDENT_SENSE)) && cursed_p(o_ptr)) return (0L);
+
+ /* Base value (see above) */
+ value = object_value_base(o_ptr);
+ }
+
+
+ /* Apply discount (if any) */
+ if (o_ptr->discount) value -= (value * o_ptr->discount / 100L);
+
+
+ /* Return the final value */
+ return (value);
+}
+
+
+
+
+
+/*
+ * Determine if an item can "absorb" a second item
+ *
+ * See "object_absorb()" for the actual "absorption" code.
+ *
+ * If permitted, we allow wands/staffs (if they are known to have equal
+ * charges) and rods (if fully charged) to combine. They will unstack
+ * (if necessary) when they are used.
+ *
+ * If permitted, we allow weapons/armor to stack, if fully "known".
+ *
+ * Missiles will combine if both stacks have the same "known" status.
+ * This is done to make unidentified stacks of missiles useful.
+ *
+ * Food, potions, scrolls, and "easy know" items always stack.
+ *
+ * Chests, and activatable items, never stack (for various reasons).
+ */
+bool object_similar(object_type *o_ptr, object_type *j_ptr)
+{
+ int total = o_ptr->number + j_ptr->number;
+ u32b f1, f2, f3, f4, f5, esp, f11, f12, f13, f14, esp1, f15;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ object_flags(j_ptr, &f11, &f12, &f13, &f14, &f15, &esp1);
+
+
+ /* Require identical object types */
+ if (o_ptr->k_idx != j_ptr->k_idx) return (0);
+
+ if ((f5 & TR5_SPELL_CONTAIN) || (f15 & TR5_SPELL_CONTAIN))
+ return FALSE;
+
+ /* Analyze the items */
+ switch (o_ptr->tval)
+ {
+ /* School Book */
+ case TV_BOOK:
+ {
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return FALSE;
+
+ /* Beware artifatcs should not combibne with "lesser" thing */
+ if (artifact_p(o_ptr) != artifact_p(j_ptr)) return (FALSE);
+
+ /* Do not combine different ego or normal ones */
+ if (ego_item_p(o_ptr) != ego_item_p(j_ptr)) return (FALSE);
+
+ /* Random books should stack if they are identical */
+ if ((o_ptr->sval == 255) && (j_ptr->sval == 255))
+ {
+ if (o_ptr->pval != j_ptr->pval)
+ return (FALSE);
+ }
+
+ return (TRUE);
+ }
+
+ /* Chests */
+ case TV_CHEST:
+ {
+ /* Never okay */
+ return (0);
+ }
+
+ case TV_RANDART:
+ {
+ return FALSE;
+ }
+
+ case TV_RUNE1:
+ {
+ return TRUE;
+ }
+
+ case TV_RUNE2:
+ {
+ if ((o_ptr->sval == RUNE_STONE) || (j_ptr->sval == RUNE_STONE)) return FALSE;
+ else return TRUE;
+ }
+
+ case TV_INSTRUMENT:
+ {
+ return FALSE;
+ }
+
+ case TV_HYPNOS:
+ case TV_EGG:
+ {
+ return FALSE;
+ }
+
+ /* Totems */
+ case TV_TOTEM:
+ {
+ if ((o_ptr->pval == j_ptr->pval) && (o_ptr->pval2 == j_ptr->pval2)) return TRUE;
+ return FALSE;
+ }
+
+ /* Corpses*/
+ case TV_CORPSE:
+ {
+ return FALSE;
+ }
+
+ /* Food and Potions and Scrolls */
+ case TV_POTION:
+ case TV_POTION2:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return FALSE;
+
+ /* Assume okay */
+ break;
+ }
+
+ case TV_SCROLL:
+ {
+ if (o_ptr->pval != j_ptr->pval) return FALSE;
+ if (o_ptr->pval2 != j_ptr->pval2) return FALSE;
+ break;
+ }
+
+ /* Staffs */
+ case TV_STAFF:
+ {
+ /* Require either knowledge or known empty for both staffs. */
+ if ((!(o_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(o_ptr)) ||
+ (!(j_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(j_ptr))) return (0);
+
+ /* Require identical charges, since staffs are bulky. */
+ if (o_ptr->pval != j_ptr->pval) return (0);
+
+ /* Do not combine recharged ones with non recharged ones. */
+ if ((f4 & TR4_RECHARGED) != (f14 & TR4_RECHARGED)) return (0);
+
+ /* Do not combine different spells */
+ if (o_ptr->pval2 != j_ptr->pval2) return (0);
+
+ /* Do not combine different base levels */
+ if (o_ptr->pval3 != j_ptr->pval3) return (0);
+
+ /* Beware artifatcs should not combibne with "lesser" thing */
+ if (o_ptr->name1 != j_ptr->name1) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2 != j_ptr->name2) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (0);
+
+ /* Assume okay */
+ break;
+ }
+
+ /* Wands */
+ case TV_WAND:
+ {
+
+ /* Require either knowledge or known empty for both wands. */
+ if ((!(o_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(o_ptr)) ||
+ (!(j_ptr->ident & (IDENT_EMPTY)) &&
+ !object_known_p(j_ptr))) return (0);
+
+ /* Beware artifatcs should not combibne with "lesser" thing */
+ if (o_ptr->name1 != j_ptr->name1) return (0);
+
+ /* Do not combine recharged ones with non recharged ones. */
+ if ((f4 & TR4_RECHARGED) != (f14 & TR4_RECHARGED)) return (0);
+
+ /* Do not combine different spells */
+ if (o_ptr->pval2 != j_ptr->pval2) return (0);
+
+ /* Do not combine different base levels */
+ if (o_ptr->pval3 != j_ptr->pval3) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2 != j_ptr->name2) return (0);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (0);
+
+ /* Assume okay */
+ break;
+ }
+
+ /* Rod Tips */
+ case TV_ROD:
+ {
+ /* Probably okay */
+ break;
+ }
+
+ /* Rods */
+ case TV_ROD_MAIN:
+ {
+ return FALSE;
+ break;
+ }
+
+ /* Weapons and Armor */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_TRAPKIT:
+ case TV_DAEMON_BOOK:
+ {
+ /* Require permission */
+ if (!stack_allow_items) return (0);
+
+ /* Fall through */
+ }
+
+ /* Rings, Amulets, Lites */
+ case TV_RING:
+ case TV_AMULET:
+ case TV_LITE:
+ {
+ /* Require full knowledge of both items */
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+ /* Require identical "turns of light" */
+ if (o_ptr->timeout != j_ptr->timeout) return (FALSE);
+
+ /* Fall through */
+ }
+
+ /* Missiles */
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ /* Require identical knowledge of both items */
+ if (object_known_p(o_ptr) != object_known_p(j_ptr)) return (0);
+
+ /* Require identical "bonuses" */
+ if (o_ptr->to_h != j_ptr->to_h) return (FALSE);
+ if (o_ptr->to_d != j_ptr->to_d) return (FALSE);
+ if (o_ptr->to_a != j_ptr->to_a) return (FALSE);
+
+ /* Require identical "pval" code */
+ if (o_ptr->pval != j_ptr->pval) return (FALSE);
+
+ /* Require identical exploding status code */
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+
+ /* Require identical "artifact" names */
+ if (o_ptr->name1 != j_ptr->name1) return (FALSE);
+
+ /* Random artifacts never stack */
+ /*
+ I don't see why these shouldn't stack.
+ -- wilh
+ */
+#if 0
+ if (o_ptr->art_name || j_ptr->art_name) return (FALSE);
+#endif
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2 != j_ptr->name2) return (FALSE);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (FALSE);
+
+ /* Hack -- Never stack "powerful" items */
+ /*
+ Why?!
+ -- wilh
+ */
+ /* #if 0 */
+ if (o_ptr->xtra1 || j_ptr->xtra1) return (FALSE);
+ /* #endif */
+
+ /* Hack -- Never stack recharging items */
+ if ((o_ptr->timeout || j_ptr->timeout) &&
+ (o_ptr->tval != TV_LITE)) return (FALSE);
+
+ /* Require identical "values" */
+ if (o_ptr->ac != j_ptr->ac) return (FALSE);
+ if (o_ptr->dd != j_ptr->dd) return (FALSE);
+ if (o_ptr->ds != j_ptr->ds) return (FALSE);
+
+ /* Probably okay */
+ break;
+ }
+
+ /* UHH ugly hack for the mushroom quest, sorry */
+ case TV_FOOD:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+ break;
+ }
+
+ /* UHH ugly hack for the fireproof quest, sorry */
+ case TV_BATERIE:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+ break;
+ }
+
+ /* Various */
+ default:
+ {
+ /* Require knowledge */
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+ /* Probably okay */
+ break;
+ }
+ }
+
+
+ /* Hack -- Identical art_flags! */
+ if ((o_ptr->art_flags1 != j_ptr->art_flags1) ||
+ (o_ptr->art_flags2 != j_ptr->art_flags2) ||
+ (o_ptr->art_flags3 != j_ptr->art_flags3))
+ return (0);
+
+ /* Hack -- Require identical "cursed" status */
+ if ((o_ptr->ident & (IDENT_CURSED)) != (j_ptr->ident & (IDENT_CURSED))) return (0);
+
+ /* Hack -- require semi-matching "inscriptions" */
+ if (o_ptr->note && j_ptr->note && (o_ptr->note != j_ptr->note)) return (0);
+
+ /* Hack -- normally require matching "inscriptions" */
+ if (!stack_force_notes && (o_ptr->note != j_ptr->note)) return (0);
+
+ /* Hack -- normally require matching "discounts" */
+ if (!stack_force_costs && (o_ptr->discount != j_ptr->discount)) return (0);
+
+
+ /* Maximal "stacking" limit */
+ if (total >= MAX_STACK_SIZE) return (0);
+
+
+ /* They match, so they must be similar */
+ return (TRUE);
+}
+
+
+/*
+ * Allow one item to "absorb" another, assuming they are similar
+ */
+void object_absorb(object_type *o_ptr, object_type *j_ptr)
+{
+ int total = o_ptr->number + j_ptr->number;
+
+ /* Add together the item counts */
+ o_ptr->number = ((total < MAX_STACK_SIZE) ? total : (MAX_STACK_SIZE - 1));
+
+ /* Hack -- blend "known" status */
+ if (object_known_p(j_ptr)) object_known(o_ptr);
+
+ /* Hack -- clear "storebought" if only one has it */
+ if (((o_ptr->ident & IDENT_STOREB) || (j_ptr->ident & IDENT_STOREB)) &&
+ (!((o_ptr->ident & IDENT_STOREB) && (j_ptr->ident & IDENT_STOREB))))
+ {
+ if (j_ptr->ident & IDENT_STOREB) j_ptr->ident &= 0xEF;
+ if (o_ptr->ident & IDENT_STOREB) o_ptr->ident &= 0xEF;
+ }
+
+ /* Hack -- blend "mental" status */
+ if (j_ptr->ident & (IDENT_MENTAL)) o_ptr->ident |= (IDENT_MENTAL);
+
+ /* Hack -- blend "inscriptions" */
+ if (j_ptr->note) o_ptr->note = j_ptr->note;
+
+ /* Hack -- could average discounts XXX XXX XXX */
+ /* Hack -- save largest discount XXX XXX XXX */
+ if (o_ptr->discount < j_ptr->discount) o_ptr->discount = j_ptr->discount;
+
+ /* Hack -- if wands are stacking, combine the charges. -LM- */
+ if (o_ptr->tval == TV_WAND)
+ {
+ o_ptr->pval += j_ptr->pval;
+ }
+}
+
+
+
+/*
+ * Find the index of the object_kind with the given tval and sval
+ */
+s16b lookup_kind(int tval, int sval)
+{
+ int k;
+
+ /* Look for it */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Found a match */
+ if ((k_ptr->tval == tval) && (k_ptr->sval == sval)) return (k);
+ }
+
+ /* Oops */
+ if (wizard) msg_format("No object (%d,%d)", tval, sval);
+
+ /* Oops */
+ return (0);
+}
+
+
+/*
+ * Wipe an object clean.
+ */
+void object_wipe(object_type *o_ptr)
+{
+ /* Wipe the structure */
+ o_ptr = WIPE(o_ptr, object_type);
+}
+
+
+/*
+ * Prepare an object based on an existing object
+ */
+void object_copy(object_type *o_ptr, object_type *j_ptr)
+{
+ /* Copy the structure */
+ COPY(o_ptr, j_ptr, object_type);
+}
+
+
+/*
+ * Prepare an object based on an object kind.
+ */
+void object_prep(object_type *o_ptr, int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ /* Clear the record */
+ o_ptr = WIPE(o_ptr, object_type);
+
+ /* Save the kind index */
+ o_ptr->k_idx = k_idx;
+
+ /* Efficiency -- tval/sval */
+ o_ptr->tval = k_ptr->tval;
+ o_ptr->sval = k_ptr->sval;
+
+ /* Default "pval" */
+ o_ptr->pval = k_ptr->pval;
+ o_ptr->pval2 = k_ptr->pval2;
+
+ /* Default number */
+ o_ptr->number = 1;
+
+ /* Default weight */
+ o_ptr->weight = k_ptr->weight;
+
+ /* Default magic */
+ o_ptr->to_h = k_ptr->to_h;
+ o_ptr->to_d = k_ptr->to_d;
+ o_ptr->to_a = k_ptr->to_a;
+
+ /* Default power */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* Hack -- cursed items are always "cursed" */
+ if (k_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (k_ptr->flags4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ o_ptr->pval2 = 1; /* Start with one point */
+ o_ptr->pval3 = 0; /* No flags groups */
+ }
+}
+
+
+/*
+ * Help determine an "enchantment bonus" for an object.
+ *
+ * To avoid floating point but still provide a smooth distribution of bonuses,
+ * we simply round the results of division in such a way as to "average" the
+ * correct floating point value.
+ *
+ * This function has been changed. It uses "randnor()" to choose values from
+ * a normal distribution, whose mean moves from zero towards the max as the
+ * level increases, and whose standard deviation is equal to 1/4 of the max,
+ * and whose values are forced to lie between zero and the max, inclusive.
+ *
+ * Since the "level" rarely passes 100 before Morgoth is dead, it is very
+ * rare to get the "full" enchantment on an object, even a deep levels.
+ *
+ * It is always possible (albeit unlikely) to get the "full" enchantment.
+ *
+ * A sample distribution of values from "m_bonus(10, N)" is shown below:
+ *
+ * N 0 1 2 3 4 5 6 7 8 9 10
+ * --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+ * 0 66.37 13.01 9.73 5.47 2.89 1.31 0.72 0.26 0.12 0.09 0.03
+ * 8 46.85 24.66 12.13 8.13 4.20 2.30 1.05 0.36 0.19 0.08 0.05
+ * 16 30.12 27.62 18.52 10.52 6.34 3.52 1.95 0.90 0.31 0.15 0.05
+ * 24 22.44 15.62 30.14 12.92 8.55 5.30 2.39 1.63 0.62 0.28 0.11
+ * 32 16.23 11.43 23.01 22.31 11.19 7.18 4.46 2.13 1.20 0.45 0.41
+ * 40 10.76 8.91 12.80 29.51 16.00 9.69 5.90 3.43 1.47 0.88 0.65
+ * 48 7.28 6.81 10.51 18.27 27.57 11.76 7.85 4.99 2.80 1.22 0.94
+ * 56 4.41 4.73 8.52 11.96 24.94 19.78 11.06 7.18 3.68 1.96 1.78
+ * 64 2.81 3.07 5.65 9.17 13.01 31.57 13.70 9.30 6.04 3.04 2.64
+ * 72 1.87 1.99 3.68 7.15 10.56 20.24 25.78 12.17 7.52 4.42 4.62
+ * 80 1.02 1.23 2.78 4.75 8.37 12.04 27.61 18.07 10.28 6.52 7.33
+ * 88 0.70 0.57 1.56 3.12 6.34 10.06 15.76 30.46 12.58 8.47 10.38
+ * 96 0.27 0.60 1.25 2.28 4.30 7.60 10.77 22.52 22.51 11.37 16.53
+ * 104 0.22 0.42 0.77 1.36 2.62 5.33 8.93 13.05 29.54 15.23 22.53
+ * 112 0.15 0.20 0.56 0.87 2.00 3.83 6.86 10.06 17.89 27.31 30.27
+ * 120 0.03 0.11 0.31 0.46 1.31 2.48 4.60 7.78 11.67 25.53 45.72
+ * 128 0.02 0.01 0.13 0.33 0.83 1.41 3.24 6.17 9.57 14.22 64.07
+ */
+s16b m_bonus(int max, int level)
+{
+ int bonus, stand, extra, value;
+
+
+ /* Paranoia -- enforce maximal "level" */
+ if (level > MAX_DEPTH - 1) level = MAX_DEPTH - 1;
+
+
+ /* The "bonus" moves towards the max */
+ bonus = ((max * level) / MAX_DEPTH);
+
+ /* Hack -- determine fraction of error */
+ extra = ((max * level) % MAX_DEPTH);
+
+ /* Hack -- simulate floating point computations */
+ if (rand_int(MAX_DEPTH) < extra) bonus++;
+
+
+ /* The "stand" is equal to one quarter of the max */
+ stand = (max / 4);
+
+ /* Hack -- determine fraction of error */
+ extra = (max % 4);
+
+ /* Hack -- simulate floating point computations */
+ if (rand_int(4) < extra) stand++;
+
+
+ /* Choose an "interesting" value */
+ value = randnor(bonus, stand);
+
+ /* Enforce the minimum value */
+ if (value < 0) return (0);
+
+ /* Enforce the maximum value */
+ if (value > max) return (max);
+
+ /* Result */
+ return (value);
+}
+
+
+/*
+ * Tinker with the random artifact to make it acceptable
+ * for a certain depth; also connect a random artifact to an
+ * object.
+ */
+static void finalize_randart(object_type* o_ptr, int lev)
+{
+ int r;
+ int i = 0;
+ int foo = lev + randnor(0, 5);
+ bool flag = TRUE;
+
+ /* Paranoia */
+ if (o_ptr->tval != TV_RANDART) return;
+
+ if (foo < 1) foo = 1;
+ if (foo > 100) foo = 100;
+
+ while (flag)
+ {
+ r = rand_int(MAX_RANDARTS);
+
+ if (!(random_artifacts[r].generated) || i > 2000)
+ {
+ random_artifact* ra_ptr = &random_artifacts[r];
+
+ o_ptr->sval = r;
+ o_ptr->pval2 = ra_ptr->activation;
+ o_ptr->xtra2 = activation_info[ra_ptr->activation].spell;
+
+ ra_ptr->level = lev;
+ ra_ptr->generated = TRUE;
+ flag = FALSE;
+ }
+
+ i++;
+ }
+}
+
+
+
+/*
+ * Cheat -- describe a created object for the user
+ */
+static void object_mention(object_type *o_ptr)
+{
+ char o_name[80];
+
+ /* Describe */
+ object_desc_store(o_name, o_ptr, FALSE, 0);
+
+ /* Artifact */
+ if (artifact_p(o_ptr))
+ {
+ /* Silly message */
+ msg_format("Artifact (%s)", o_name);
+ }
+
+ /* Random Artifact */
+ else if (o_ptr->art_name)
+ {
+ msg_print("Random artifact");
+ }
+
+ /* Ego-item */
+ else if (ego_item_p(o_ptr))
+ {
+ /* Silly message */
+ msg_format("Ego-item (%s)", o_name);
+ }
+
+ /* Normal item */
+ else
+ {
+ /* Silly message */
+ msg_format("Object (%s)", o_name);
+ }
+}
+
+
+void random_artifact_resistance(object_type * o_ptr)
+{
+ bool give_resistance = FALSE, give_power = FALSE;
+
+ switch (o_ptr->name1)
+ {
+ case ART_CELEBORN:
+ case ART_ARVEDUI:
+ case ART_CASPANION:
+ case ART_TRON:
+ case ART_ROHIRRIM:
+ case ART_CELEGORM:
+ case ART_ANARION:
+ case ART_THRANDUIL:
+ case ART_LUTHIEN:
+ case ART_THROR:
+ case ART_THORIN:
+ case ART_NIMTHANC:
+ case ART_DETHANC:
+ case ART_NARTHANC:
+ case ART_STING:
+ case ART_TURMIL:
+ case ART_THALKETTOTH:
+ {
+ /* Give a resistance */
+ give_resistance = TRUE;
+ }
+ break;
+ case ART_MAEDHROS:
+ case ART_GLAMDRING:
+ case ART_ORCRIST:
+ case ART_ANDURIL:
+ case ART_ZARCUTHRA:
+ case ART_GURTHANG:
+ case ART_HARADEKKET:
+ case ART_CUBRAGOL:
+ case ART_DAWN:
+ {
+ /* Give a resistance OR a power */
+ if (randint(2) == 1) give_resistance = TRUE;
+ else give_power = TRUE;
+ }
+ break;
+ case ART_NENYA:
+ case ART_VILYA:
+ case ART_BERUTHIEL:
+ case ART_FINGOLFIN:
+ case ART_THINGOL:
+ case ART_ULMO:
+ case ART_OLORIN:
+ {
+ /* Give a power */
+ give_power = TRUE;
+ }
+ break;
+ case ART_POWER:
+ case ART_GONDOR:
+ case ART_AULE:
+ {
+ /* Give both */
+ give_power = TRUE;
+ give_resistance = TRUE;
+ }
+ break;
+ }
+
+ if (give_power)
+ {
+ o_ptr->xtra1 = EGO_XTRA_ABILITY;
+
+ /* Randomize the "xtra" power */
+ if (o_ptr->xtra1) o_ptr->xtra2 = randint(256);
+ }
+
+ artifact_bias = 0;
+
+ if (give_resistance)
+ {
+ random_resistance(o_ptr, FALSE, ((randint(22)) + 16));
+ }
+}
+
+
+/*
+ * Mega-Hack -- Attempt to create one of the "Special Objects"
+ *
+ * We are only called from "make_object()", and we assume that
+ * "apply_magic()" is called immediately after we return.
+ *
+ * Note -- see "make_artifact()" and "apply_magic()"
+ */
+static bool make_artifact_special(object_type *o_ptr)
+{
+ int i;
+ int k_idx = 0;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* No artifacts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Check the artifact list (just the "specials") */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+
+ /* Skip "empty" artifacts */
+ if (!a_ptr->name) continue;
+
+ /* Cannot make an artifact twice */
+ if (a_ptr->cur_num) continue;
+
+ /* Cannot generate non special ones */
+ if (!(a_ptr->flags3 & TR3_INSTA_ART)) continue;
+
+ /* Cannot generate some artifacts because they can only exists in special dungeons/quests/... */
+ if ((a_ptr->flags4 & TR4_SPECIAL_GENE) && (!a_allow_special[i]) && (!vanilla_town)) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (a_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (a_ptr->level - dun_level) * 2;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* Artifact "rarity roll" */
+ if (rand_int(a_ptr->rarity - luck( -(a_ptr->rarity / 2), a_ptr->rarity / 2)) != 0) continue;
+
+ /* Find the base object */
+ k_idx = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* XXX XXX Enforce minimum "object" level (loosely) */
+ if (k_info[k_idx].level > object_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (k_info[k_idx].level - object_level) * 5;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* Assign the template */
+ object_prep(o_ptr, k_idx);
+
+ /* Mega-Hack -- mark the item as an artifact */
+ o_ptr->name1 = i;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_info[k_idx].level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+
+/*
+ * Attempt to change an object into an artifact
+ *
+ * This routine should only be called by "apply_magic()"
+ *
+ * Note -- see "make_artifact_special()" and "apply_magic()"
+ */
+static bool make_artifact(object_type *o_ptr)
+{
+ int i;
+ u32b f1, f2, f3, f4, f5, esp;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* No artifacts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Paranoia -- no "plural" artifacts */
+ if (o_ptr->number != 1) return (FALSE);
+
+ /* Check the artifact list (skip the "specials") */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+
+ /* Skip "empty" items */
+ if (!a_ptr->name) continue;
+
+ /* Cannot make an artifact twice */
+ if (a_ptr->cur_num) continue;
+
+ /* Cannot generate special ones */
+ if (a_ptr->flags3 & TR3_INSTA_ART) continue;
+
+ /* Cannot generate some artifacts because they can only exists in special dungeons/quests/... */
+ if ((a_ptr->flags4 & TR4_SPECIAL_GENE) && (!a_allow_special[i]) && (!vanilla_town)) continue;
+
+ /* Must have the correct fields */
+ if (a_ptr->tval != o_ptr->tval) continue;
+ if (a_ptr->sval != o_ptr->sval) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (a_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (a_ptr->level - dun_level) * 2;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(a_ptr->rarity - luck( -(a_ptr->rarity / 2), a_ptr->rarity / 2)) != 0) continue;
+
+ /* Hack -- mark the item as an artifact */
+ o_ptr->name1 = i;
+
+ /* Hack: Some artifacts get random extra powers */
+ random_artifact_resistance(o_ptr);
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+/*
+ * Attempt to change an object into an ego
+ *
+ * This routine should only be called by "apply_magic()"
+ */
+static bool make_ego_item(object_type *o_ptr, bool good)
+{
+ int i = 0, j;
+ int *ok_ego, ok_num = 0;
+ bool ret = FALSE;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ if (artifact_p(o_ptr) || o_ptr->name2) return (FALSE);
+
+ C_MAKE(ok_ego, max_e_idx, int);
+
+ /* Grab the ok ego */
+ for (i = 0; i < max_e_idx; i++)
+ {
+ ego_item_type *e_ptr = &e_info[i];
+ bool ok = FALSE;
+
+ /* Skip "empty" items */
+ if (!e_ptr->name) continue;
+
+ /* Must have the correct fields */
+ for (j = 0; j < 6; j++)
+ {
+ if (e_ptr->tval[j] == o_ptr->tval)
+ {
+ if ((e_ptr->min_sval[j] <= o_ptr->sval) && (e_ptr->max_sval[j] >= o_ptr->sval)) ok = TRUE;
+ }
+
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ /* Doesnt count as a try*/
+ continue;
+ }
+
+ /* Good should be good, bad should be bad */
+ if (good && (!e_ptr->cost)) continue;
+ if ((!good) && e_ptr->cost) continue;
+
+ /* Must posses the good flags */
+ if (((k_ptr->flags1 & e_ptr->need_flags1) != e_ptr->need_flags1) ||
+ ((k_ptr->flags2 & e_ptr->need_flags2) != e_ptr->need_flags2) ||
+ ((k_ptr->flags3 & e_ptr->need_flags3) != e_ptr->need_flags3) ||
+ ((k_ptr->flags4 & e_ptr->need_flags4) != e_ptr->need_flags4) ||
+ ((k_ptr->flags5 & e_ptr->need_flags5) != e_ptr->need_flags5) ||
+ ((k_ptr->esp & e_ptr->need_esp) != e_ptr->need_esp))
+ continue;
+ if ((k_ptr->flags1 & e_ptr->forbid_flags1) ||
+ (k_ptr->flags2 & e_ptr->forbid_flags2) ||
+ (k_ptr->flags3 & e_ptr->forbid_flags3) ||
+ (k_ptr->flags4 & e_ptr->forbid_flags4) ||
+ (k_ptr->flags5 & e_ptr->forbid_flags5) ||
+ (k_ptr->esp & e_ptr->forbid_esp))
+ continue;
+
+ /* ok */
+ ok_ego[ok_num++] = i;
+ }
+
+ /* Now test them a few times */
+ for (i = 0; i < ok_num * 10; i++)
+ {
+ ego_item_type *e_ptr;
+
+ int j = ok_ego[rand_int(ok_num)];
+ e_ptr = &e_info[j];
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (e_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (e_ptr->level - dun_level);
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0)
+ {
+ continue;
+ }
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(e_ptr->mrarity - luck( -(e_ptr->mrarity / 2), e_ptr->mrarity / 2)) > e_ptr->rarity)
+ {
+ continue;
+ }
+
+ /* Hack -- mark the item as an ego */
+ o_ptr->name2 = j;
+
+ /* Success */
+ ret = TRUE;
+ break;
+ }
+
+ /*
+ * Sometimes(rarely) tries for a double ego
+ * Also make sure we dont already have a name2b, wchih would mean a special ego item
+ */
+ if (magik(7 + luck( -7, 7)) && (!o_ptr->name2b))
+ {
+ /* Now test them a few times */
+ for (i = 0; i < ok_num * 10; i++)
+ {
+ ego_item_type *e_ptr;
+
+ int j = ok_ego[rand_int(ok_num)];
+ e_ptr = &e_info[j];
+
+ /* Cannot be a double ego of the same ego type */
+ if (j == o_ptr->name2) continue;
+
+ /* Cannot have 2 suffixes or 2 prefixes */
+ if (e_info[o_ptr->name2].before && e_ptr->before) continue;
+ if ((!e_info[o_ptr->name2].before) && (!e_ptr->before)) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (e_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (e_ptr->level - dun_level);
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0)
+ {
+ continue;
+ }
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(e_ptr->mrarity - luck( -(e_ptr->mrarity / 2), e_ptr->mrarity / 2)) > e_ptr->rarity)
+ {
+ continue;
+ }
+
+ /* Hack -- mark the item as an ego */
+ o_ptr->name2b = j;
+
+ /* Success */
+ ret = TRUE;
+ break;
+ }
+ }
+
+ C_FREE(ok_ego, max_e_idx, int);
+
+ /* Return */
+ return (ret);
+}
+
+
+/*
+ * Charge a new stick.
+ */
+void charge_stick(object_type *o_ptr)
+{
+ o_ptr->pval = exec_lua(format("return get_stick_charges(%d)", o_ptr->pval2));
+}
+
+/*
+ * Apply magic to an item known to be a "weapon"
+ *
+ * Hack -- note special base damage dice boosting
+ * Hack -- note special processing for weapon/digger
+ * Hack -- note special rating boost for dragon scale mail
+ */
+static void a_m_aux_1(object_type *o_ptr, int level, int power)
+{
+ int tohit1 = randint(5) + m_bonus(5, level);
+ int todam1 = randint(5) + m_bonus(5, level);
+
+ int tohit2 = m_bonus(10, level);
+ int todam2 = m_bonus(10, level);
+
+ artifact_bias = 0;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if ((rand_int(RANDART_WEAPON) == 1) && (o_ptr->tval != TV_TRAPKIT)) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Good */
+ if (power > 0)
+ {
+ /* Enchant */
+ o_ptr->to_h += tohit1;
+ o_ptr->to_d += todam1;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Enchant again */
+ o_ptr->to_h += tohit2;
+ o_ptr->to_d += todam2;
+ }
+ }
+
+ /* Cursed */
+ else if (power < 0)
+ {
+ /* Penalize */
+ o_ptr->to_h -= tohit1;
+ o_ptr->to_d -= todam1;
+
+ /* Very cursed */
+ if (power < -1)
+ {
+ /* Penalize again */
+ o_ptr->to_h -= tohit2;
+ o_ptr->to_d -= todam2;
+ }
+
+ /* Cursed (if "bad") */
+ if (o_ptr->to_h + o_ptr->to_d < 0) o_ptr->ident |= (IDENT_CURSED);
+ }
+
+ /* Some special cases */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_TRAPKIT:
+ {
+ /* Good */
+ if (power > 0) o_ptr->to_a += randint(5);
+
+ /* Very good */
+ if (power > 1) o_ptr->to_a += randint(5);
+
+ /* Bad */
+ if (power < 0) o_ptr->to_a -= randint(5);
+
+ /* Very bad */
+ if (power < -1) o_ptr->to_a -= randint(5);
+
+ break;
+ }
+ case TV_MSTAFF:
+ {
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ int gf[2], i;
+
+ for (i = 0; i < 2; i++)
+ {
+ int k = 0;
+
+ gf[i] = 0;
+ while (!k)
+ {
+ k = lookup_kind(TV_RUNE1, (gf[i] = rand_int(MAX_GF)));
+ }
+ }
+
+ o_ptr->pval = gf[0] + (gf[1] << 16);
+ o_ptr->pval3 = rand_int(RUNE_MOD_MAX) + (rand_int(RUNE_MOD_MAX) << 16);
+ o_ptr->pval2 = randint(70) + (randint(70) << 8);
+ }
+ else
+ o_ptr->art_flags5 |= (TR5_SPELL_CONTAIN | TR5_WIELD_CAST);
+ break;
+ }
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ if ((power == 1) && !o_ptr->name2)
+ {
+ if (randint(100) < 30)
+ {
+ /* Exploding missile */
+ int power[27] = {GF_ELEC, GF_POIS, GF_ACID,
+ GF_COLD, GF_FIRE, GF_PLASMA, GF_LITE,
+ GF_DARK, GF_SHARDS, GF_SOUND,
+ GF_CONFUSION, GF_FORCE, GF_INERTIA,
+ GF_MANA, GF_METEOR, GF_ICE, GF_CHAOS,
+ GF_NETHER, GF_NEXUS, GF_TIME,
+ GF_GRAVITY, GF_KILL_WALL, GF_AWAY_ALL,
+ GF_TURN_ALL, GF_NUKE, GF_STUN,
+ GF_DISINTEGRATE};
+
+ o_ptr->pval2 = power[rand_int(27)];
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+static void dragon_resist(object_type * o_ptr)
+{
+ do
+ {
+ artifact_bias = 0;
+
+ if (randint(4) == 1)
+ random_resistance(o_ptr, FALSE, ((randint(14)) + 4));
+ else
+ random_resistance(o_ptr, FALSE, ((randint(22)) + 16));
+ }
+ while (randint(2) == 1);
+}
+
+
+/*
+ * Apply magic to an item known to be "armor"
+ *
+ * Hack -- note special processing for crown/helm
+ * Hack -- note special processing for robe of permanence
+ */
+static void a_m_aux_2(object_type *o_ptr, int level, int power)
+{
+ int toac1 = randint(5) + m_bonus(5, level);
+
+ int toac2 = m_bonus(10, level);
+
+ artifact_bias = 0;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if (rand_int(RANDART_ARMOR) == 1) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Good */
+ if (power > 0)
+ {
+ /* Enchant */
+ o_ptr->to_a += toac1;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Enchant again */
+ o_ptr->to_a += toac2;
+ }
+ }
+
+ /* Cursed */
+ else if (power < 0)
+ {
+ /* Penalize */
+ o_ptr->to_a -= toac1;
+
+ /* Very cursed */
+ if (power < -1)
+ {
+ /* Penalize again */
+ o_ptr->to_a -= toac2;
+ }
+
+ /* Cursed (if "bad") */
+ if (o_ptr->to_a < 0) o_ptr->ident |= (IDENT_CURSED);
+ }
+
+ /* Analyze type */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_CLOAK:
+ {
+ if (o_ptr->sval == SV_ELVEN_CLOAK)
+ o_ptr->pval = randint(4); /* No cursed elven cloaks...? */
+ else if (o_ptr->sval == SV_MIMIC_CLOAK)
+ {
+ s32b mimic;
+
+ call_lua("find_random_mimic_shape", "(d,d)", "d", level, TRUE, &mimic);
+ o_ptr->pval2 = mimic;
+ }
+ break;
+ }
+ case TV_DRAG_ARMOR:
+ {
+ /* Rating boost */
+ rating += 30;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ break;
+ }
+ case TV_SHIELD:
+ {
+ if (o_ptr->sval == SV_DRAGON_SHIELD)
+ {
+ /* Rating boost */
+ rating += 5;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ dragon_resist(o_ptr);
+ }
+ break;
+ }
+ case TV_HELM:
+ {
+ if (o_ptr->sval == SV_DRAGON_HELM)
+ {
+ /* Rating boost */
+ rating += 5;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ dragon_resist(o_ptr);
+ }
+ break;
+ }
+ }
+}
+
+
+
+/*
+ * Apply magic to an item known to be a "ring" or "amulet"
+ *
+ * Hack -- note special rating boost for ring of speed
+ * Hack -- note special rating boost for amulet of the magi
+ * Hack -- note special "pval boost" code for ring of speed
+ * Hack -- note that some items must be cursed (or blessed)
+ */
+static void a_m_aux_3(object_type *o_ptr, int level, int power)
+{
+
+ artifact_bias = 0;
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if (rand_int(RANDART_JEWEL) == 1) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Apply magic (good or bad) according to type */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_RING:
+ {
+ /* Analyze */
+ switch (o_ptr->sval)
+ {
+ /* Strength, Constitution, Dexterity, Intelligence */
+ case SV_RING_ATTACKS:
+ {
+ /* Stat bonus */
+ o_ptr->pval = m_bonus(3, level);
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Critical hits */
+ case SV_RING_CRIT:
+ {
+ /* Stat bonus */
+ o_ptr->pval = m_bonus(10, level);
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+
+ case SV_RING_STR:
+ case SV_RING_CON:
+ case SV_RING_DEX:
+ case SV_RING_INT:
+ {
+ /* Stat bonus */
+ o_ptr->pval = 1 + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Ring of Speed! */
+ case SV_RING_SPEED:
+ {
+ /* Base speed (1 to 10) */
+ o_ptr->pval = randint(5) + m_bonus(5, level);
+
+ /* Super-charge the ring */
+ while (rand_int(100) < 50) o_ptr->pval++;
+
+ /* Cursed Ring */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+
+ break;
+ }
+
+ /* Rating boost */
+ rating += 25;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ break;
+ }
+
+ case SV_RING_LORDLY:
+ {
+ do
+ {
+ random_resistance(o_ptr, FALSE, ((randint(20)) + 18));
+ }
+ while (randint(4) == 1);
+
+ /* Bonus to armor class */
+ o_ptr->to_a = 10 + randint(5) + m_bonus(10, level);
+ rating += 5;
+ }
+ break;
+
+ /* Searching */
+ case SV_RING_SEARCHING:
+ {
+ /* Bonus to searching */
+ o_ptr->pval = 1 + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse pval */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Flames, Acid, Ice */
+ case SV_RING_FLAMES:
+ case SV_RING_ACID:
+ case SV_RING_ICE:
+ {
+ /* Bonus to armor class */
+ o_ptr->to_a = 5 + randint(5) + m_bonus(10, level);
+ break;
+ }
+
+ /* Weakness, Stupidity */
+ case SV_RING_WEAKNESS:
+ case SV_RING_STUPIDITY:
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Penalize */
+ o_ptr->pval = 0 - (1 + m_bonus(5, level));
+
+ break;
+ }
+
+ /* WOE, Stupidity */
+ case SV_RING_WOE:
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Penalize */
+ o_ptr->to_a = 0 - (5 + m_bonus(10, level));
+ o_ptr->pval = 0 - (1 + m_bonus(5, level));
+
+ break;
+ }
+
+ /* Ring of damage */
+ case SV_RING_DAMAGE:
+ {
+ /* Bonus to damage */
+ o_ptr->to_d = 5 + randint(8) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonus */
+ o_ptr->to_d = 0 - (o_ptr->to_d);
+ }
+
+ break;
+ }
+
+ /* Ring of Accuracy */
+ case SV_RING_ACCURACY:
+ {
+ /* Bonus to hit */
+ o_ptr->to_h = 5 + randint(8) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse tohit */
+ o_ptr->to_h = 0 - (o_ptr->to_h);
+ }
+
+ break;
+ }
+
+ /* Ring of Protection */
+ case SV_RING_PROTECTION:
+ {
+ /* Bonus to armor class */
+ o_ptr->to_a = 5 + randint(8) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse toac */
+ o_ptr->to_a = 0 - (o_ptr->to_a);
+ }
+
+ break;
+ }
+
+ /* Ring of Slaying */
+ case SV_RING_SLAYING:
+ {
+ /* Bonus to damage and to hit */
+ o_ptr->to_d = randint(7) + m_bonus(10, level);
+ o_ptr->to_h = randint(7) + m_bonus(10, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->to_h = 0 - (o_ptr->to_h);
+ o_ptr->to_d = 0 - (o_ptr->to_d);
+ }
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case TV_AMULET:
+ {
+ /* Analyze */
+ switch (o_ptr->sval)
+ {
+ /* Amulet of Trickery */
+ case SV_AMULET_TRICKERY:
+ case SV_AMULET_DEVOTION:
+ {
+ o_ptr->pval = 1 + m_bonus(3, level);
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ break;
+ }
+
+ case SV_AMULET_WEAPONMASTERY:
+ {
+ o_ptr->pval = 1 + m_bonus(2, level);
+ o_ptr->to_a = 1 + m_bonus(4, level);
+ o_ptr->to_h = 1 + m_bonus(5, level);
+ o_ptr->to_d = 1 + m_bonus(5, level);
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ break;
+ }
+
+ /* Amulet of wisdom/charisma */
+ case SV_AMULET_BRILLANCE:
+ case SV_AMULET_CHARISMA:
+ case SV_AMULET_WISDOM:
+ case SV_AMULET_INFRA:
+ {
+ o_ptr->pval = 1 + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Amulet of the Serpents */
+ case SV_AMULET_SERPENT:
+ {
+ o_ptr->pval = 1 + m_bonus(5, level);
+ o_ptr->to_a = 1 + m_bonus(6, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ case SV_AMULET_NO_MAGIC:
+ case SV_AMULET_NO_TELE:
+ {
+ if (power < 0)
+ {
+ o_ptr->ident |= (IDENT_CURSED);
+ }
+ break;
+ }
+
+ case SV_AMULET_RESISTANCE:
+ {
+ if (randint(3) == 1) random_resistance(o_ptr, FALSE, ((randint(34)) + 4));
+ if (randint(5) == 1) o_ptr->art_flags2 |= TR2_RES_POIS;
+ }
+ break;
+
+ /* Amulet of searching */
+ case SV_AMULET_SEARCHING:
+ {
+ o_ptr->pval = randint(5) + m_bonus(5, level);
+
+ /* Cursed */
+ if (power < 0)
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Reverse bonuses */
+ o_ptr->pval = 0 - (o_ptr->pval);
+ }
+
+ break;
+ }
+
+ /* Amulet of the Magi -- never cursed */
+ case SV_AMULET_THE_MAGI:
+ {
+ o_ptr->pval = 1 + m_bonus(3, level);
+
+ if (randint(3) == 1) o_ptr->art_flags3 |= TR3_SLOW_DIGEST;
+
+ /* Boost the rating */
+ rating += 25;
+
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ break;
+ }
+
+ /* Amulet of Doom -- always cursed */
+ case SV_AMULET_DOOM:
+ {
+ /* Cursed */
+ o_ptr->ident |= (IDENT_CURSED);
+
+ /* Penalize */
+ o_ptr->pval = 0 - (randint(5) + m_bonus(5, level));
+ o_ptr->to_a = 0 - (randint(5) + m_bonus(5, level));
+
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Apply magic to an item known to be "boring"
+ *
+ * Hack -- note the special code for various items
+ */
+static void a_m_aux_4(object_type *o_ptr, int level, int power)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ s32b bonus_lvl, max_lvl;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Very good */
+ if (power > 1)
+ {
+ /* Make ego item */
+ if ((rand_int(RANDART_JEWEL) == 1) && (o_ptr->tval == TV_LITE)) create_artifact(o_ptr, FALSE, TRUE);
+ else make_ego_item(o_ptr, TRUE);
+ }
+ else if (power < -1)
+ {
+ /* Make ego item */
+ make_ego_item(o_ptr, FALSE);
+ }
+
+ /* Apply magic (good or bad) according to type */
+ if (process_hooks(HOOK_APPLY_MAGIC, "(O,d,d)", o_ptr, level, power))
+ return;
+ switch (o_ptr->tval)
+ {
+ case TV_BOOK:
+ {
+ /* Randomize random books */
+ if (o_ptr->sval == 255)
+ {
+ int i = 0;
+
+ /* Only random ones */
+ if (magik(75))
+ i = exec_lua(format("return get_random_spell(SKILL_MAGIC, %d)", level));
+ else
+ i = exec_lua(format("return get_random_spell(SKILL_SPIRITUALITY, %d)", level));
+
+ /* Use globe of light(or the first one) */
+ if (i == -1)
+ o_ptr->pval = 0;
+ else
+ o_ptr->pval = i;
+ }
+
+ break;
+ }
+
+ case TV_LITE:
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- random fuel */
+ if (f4 & TR4_FUEL_LITE)
+ {
+ if (k_info[o_ptr->k_idx].pval2 > 0) o_ptr->timeout = randint(k_info[o_ptr->k_idx].pval2);
+ }
+
+ break;
+ }
+
+ case TV_CORPSE:
+ {
+ /* Hack -- choose a monster */
+ monster_race* r_ptr;
+ int r_idx = get_mon_num(dun_level);
+ r_ptr = &r_info[r_idx];
+
+ if (!(r_ptr->flags1 & RF1_UNIQUE))
+ o_ptr->pval2 = r_idx;
+ else
+ o_ptr->pval2 = 2;
+ o_ptr->pval3 = 0;
+ break;
+ }
+
+ case TV_EGG:
+ {
+ /* Hack -- choose a monster */
+ monster_race* r_ptr;
+ int r_idx, count = 0;
+ bool OK = FALSE;
+
+ while ((!OK) && (count < 1000))
+ {
+ r_idx = get_mon_num(dun_level);
+ r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags9 & RF9_HAS_EGG)
+ {
+ o_ptr->pval2 = r_idx;
+ OK = TRUE;
+ }
+ count++;
+ }
+ if (count == 1000) o_ptr->pval2 = 940; /* Blue fire-lizard */
+
+ r_ptr = &r_info[o_ptr->pval2];
+ o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 100) + 1;
+ o_ptr->pval = r_ptr->weight * 3 + rand_int(r_ptr->weight) + 1;
+ break;
+ }
+
+ case TV_HYPNOS:
+ {
+ /* Hack -- choose a monster */
+ monster_race* r_ptr;
+ int r_idx = get_mon_num(dun_level);
+ r_ptr = &r_info[r_idx];
+
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ o_ptr->pval = r_idx;
+ else
+ o_ptr->pval = 20;
+
+ r_idx = o_ptr->pval;
+ r_ptr = &r_info[r_idx];
+
+ o_ptr->pval3 = maxroll(r_ptr->hdice, r_ptr->hside);
+ o_ptr->pval2 = o_ptr->pval2;
+ o_ptr->exp = 0;
+ o_ptr->elevel = r_ptr->level;
+ break;
+ }
+
+ case TV_WAND:
+ {
+ /* Decide the spell, pval == -1 means to bypass spell selection */
+ if (o_ptr->pval != -1)
+ {
+ int spl = exec_lua("return get_random_stick(TV_WAND, dun_level)");
+
+ if (spl == -1) spl = exec_lua("return find_spell('Manathrust')");
+
+ o_ptr->pval2 = spl;
+ }
+ /* Is the spell predefined by the object kind? */
+ else if (k_ptr->pval == -1)
+ {
+ o_ptr->pval2 = k_ptr->pval2;
+ }
+
+ /* Ok now get a base level */
+ call_lua("get_stick_base_level", "(d,d,d)", "d", TV_WAND, dun_level, o_ptr->pval2, &bonus_lvl);
+ call_lua("get_stick_max_level", "(d,d,d)", "d", TV_WAND, dun_level, o_ptr->pval2, &max_lvl);
+ o_ptr->pval3 = (max_lvl << 16) + (bonus_lvl & 0xFFFF);
+
+ /* Hack -- charge wands */
+ charge_stick(o_ptr);
+
+ break;
+ }
+
+ case TV_STAFF:
+ {
+ /* Decide the spell, pval == -1 means to bypass spell selection */
+ if (o_ptr->pval != -1)
+ {
+ int spl = exec_lua("return get_random_stick(TV_STAFF, dun_level)");
+
+ if (spl == -1) spl = exec_lua("return find_spell('Globe of Light')");
+
+ o_ptr->pval2 = spl;
+ }
+ /* Is the spell predefined by the object kind? */
+ else if (k_ptr->pval == -1)
+ {
+ o_ptr->pval2 = k_ptr->pval2;
+ }
+
+ /* Ok now get a base level */
+ call_lua("get_stick_base_level", "(d,d,d)", "d", TV_STAFF, dun_level, o_ptr->pval2, &bonus_lvl);
+ call_lua("get_stick_max_level", "(d,d,d)", "d", TV_STAFF, dun_level, o_ptr->pval2, &max_lvl);
+ o_ptr->pval3 = (max_lvl << 16) + (bonus_lvl & 0xFFFF);
+
+ /* Hack -- charge staffs */
+ charge_stick(o_ptr);
+
+ break;
+ }
+
+ case TV_CHEST:
+ {
+ /* Hack -- skip ruined chests */
+ if (k_info[o_ptr->k_idx].level <= 0) break;
+
+ /* Pick a trap */
+ place_trap_object(o_ptr);
+
+ /* Hack - set pval2 to the number of objects in it */
+ if (o_ptr->pval)
+ o_ptr->pval2 = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2;
+ break;
+ }
+ case TV_POTION:
+ if (o_ptr->sval == SV_POTION_BLOOD)
+ {
+ /* Rating boost */
+ rating += 25;
+ /* Mention the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ }
+ break;
+ case TV_POTION2:
+ if (o_ptr->sval == SV_POTION2_MIMIC)
+ {
+ s32b mimic;
+
+ call_lua("find_random_mimic_shape", "(d,d)", "d", level, FALSE, &mimic);
+ o_ptr->pval2 = mimic;
+ }
+ break;
+ case TV_INSTRUMENT:
+ {
+ if (o_ptr->sval != SV_HORN)
+ {
+ /* Nothing */
+ }
+ else
+ {
+ if (is_ego_p(o_ptr, EGO_INST_DRAGONKIND))
+ {
+ switch (randint(4))
+ {
+ case 1:
+ o_ptr->pval2 = GF_ELEC;
+ break;
+ case 2:
+ o_ptr->pval2 = GF_FIRE;
+ break;
+ case 3:
+ o_ptr->pval2 = GF_COLD;
+ break;
+ case 4:
+ o_ptr->pval2 = GF_ACID;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ case TV_TOOL:
+ {
+ /* Hack - set the weight of portable holes */
+#if 0 /* DGDGDGDG -- use a skill */
+ if ((o_ptr->sval == SV_PORTABLE_HOLE) &&
+ (cp_ptr->magic_key == MKEY_TELEKINESIS))
+ {
+ o_ptr->weight = portable_hole_weight();
+ }
+#endif
+ break;
+ }
+
+ }
+}
+
+void trap_hack(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_TRAPKIT) return;
+
+ switch (o_ptr->sval)
+ {
+ case SV_TRAPKIT_POTION:
+ case SV_TRAPKIT_SCROLL:
+ case SV_TRAPKIT_DEVICE:
+ o_ptr->to_h = 0;
+ o_ptr->to_d = 0;
+ default:
+ return;
+ }
+}
+
+/* Add a random glag to the ego item */
+void add_random_ego_flag(object_type *o_ptr, int fego, bool *limit_blows)
+{
+ if (fego & ETR4_SUSTAIN)
+ {
+ /* Make a random sustain */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ break;
+ case 2:
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ break;
+ case 3:
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ break;
+ case 4:
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ break;
+ case 5:
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ break;
+ case 6:
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ break;
+ }
+ }
+
+ if (fego & ETR4_OLD_RESIST)
+ {
+ /* Make a random resist, equal probabilities */
+ switch (randint(11))
+ {
+ case 1:
+ o_ptr->art_flags2 |= (TR2_RES_BLIND);
+ break;
+ case 2:
+ o_ptr->art_flags2 |= (TR2_RES_CONF);
+ break;
+ case 3:
+ o_ptr->art_flags2 |= (TR2_RES_SOUND);
+ break;
+ case 4:
+ o_ptr->art_flags2 |= (TR2_RES_SHARDS);
+ break;
+ case 5:
+ o_ptr->art_flags2 |= (TR2_RES_NETHER);
+ break;
+ case 6:
+ o_ptr->art_flags2 |= (TR2_RES_NEXUS);
+ break;
+ case 7:
+ o_ptr->art_flags2 |= (TR2_RES_CHAOS);
+ break;
+ case 8:
+ o_ptr->art_flags2 |= (TR2_RES_DISEN);
+ break;
+ case 9:
+ o_ptr->art_flags2 |= (TR2_RES_POIS);
+ break;
+ case 10:
+ o_ptr->art_flags2 |= (TR2_RES_DARK);
+ break;
+ case 11:
+ o_ptr->art_flags2 |= (TR2_RES_LITE);
+ break;
+ }
+ }
+
+ if (fego & ETR4_ABILITY)
+ {
+ /* Choose an ability */
+ switch (randint(8))
+ {
+ case 1:
+ o_ptr->art_flags3 |= (TR3_FEATHER);
+ break;
+ case 2:
+ o_ptr->art_flags3 |= (TR3_LITE1);
+ break;
+ case 3:
+ o_ptr->art_flags3 |= (TR3_SEE_INVIS);
+ break;
+ case 4:
+ o_ptr->art_esp |= (ESP_ALL);
+ break;
+ case 5:
+ o_ptr->art_flags3 |= (TR3_SLOW_DIGEST);
+ break;
+ case 6:
+ o_ptr->art_flags3 |= (TR3_REGEN);
+ break;
+ case 7:
+ o_ptr->art_flags2 |= (TR2_FREE_ACT);
+ break;
+ case 8:
+ o_ptr->art_flags2 |= (TR2_HOLD_LIFE);
+ break;
+ }
+ }
+
+ if (fego & ETR4_R_ELEM)
+ {
+ /* Make an acid/elec/fire/cold/poison resist */
+ random_resistance(o_ptr, FALSE, randint(14) + 4);
+ }
+ if (fego & ETR4_R_LOW)
+ {
+ /* Make an acid/elec/fire/cold resist */
+ random_resistance(o_ptr, FALSE, randint(12) + 4);
+ }
+
+ if (fego & ETR4_R_HIGH)
+ {
+ /* Make a high resist */
+ random_resistance(o_ptr, FALSE, randint(22) + 16);
+ }
+ if (fego & ETR4_R_ANY)
+ {
+ /* Make any resist */
+ random_resistance(o_ptr, FALSE, randint(34) + 4);
+ }
+
+ if (fego & ETR4_R_DRAGON)
+ {
+ /* Make "dragon resist" */
+ dragon_resist(o_ptr);
+ }
+
+ if (fego & ETR4_SLAY_WEAP)
+ {
+ /* Make a Weapon of Slaying */
+
+ if (randint(3) == 1) /* double damage */
+ o_ptr->dd *= 2;
+ else
+ {
+ do
+ {
+ o_ptr->dd++;
+ }
+ while (randint(o_ptr->dd) == 1);
+ do
+ {
+ o_ptr->ds++;
+ }
+ while (randint(o_ptr->ds) == 1);
+ }
+ if (randint(5) == 1)
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ }
+ if (o_ptr->tval == TV_SWORD && (randint(3) == 1))
+ {
+ o_ptr->art_flags1 |= TR1_VORPAL;
+ }
+ }
+
+ if (fego & ETR4_DAM_DIE)
+ {
+ /* Increase damage dice */
+ o_ptr->dd++;
+ }
+
+ if (fego & ETR4_DAM_SIZE)
+ {
+ /* Increase damage dice size */
+ o_ptr->ds++;
+ }
+
+ if (fego & ETR4_LIMIT_BLOWS)
+ {
+ /* Swap this flag */
+ *limit_blows = !(*limit_blows);
+ }
+
+ if (fego & ETR4_PVAL_M1)
+ {
+ /* Increase pval */
+ o_ptr->pval++;
+ }
+
+ if (fego & ETR4_PVAL_M2)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_PVAL_M3)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_PVAL_M5)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(5, dun_level);
+ }
+ if (fego & ETR4_AC_M1)
+ {
+ /* Increase ac */
+ o_ptr->to_a++;
+ }
+
+ if (fego & ETR4_AC_M2)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_AC_M3)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_AC_M5)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_TH_M1)
+ {
+ /* Increase to hit */
+ o_ptr->to_h++;
+ }
+
+ if (fego & ETR4_TH_M2)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_TH_M3)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_TH_M5)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_TD_M1)
+ {
+ /* Increase to dam */
+ o_ptr->to_d++;
+ }
+
+ if (fego & ETR4_TD_M2)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_TD_M3)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_TD_M5)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_R_P_ABILITY)
+ {
+ /* Add a random pval-affected ability */
+ /* This might cause boots with + to blows */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ break;
+ case 2:
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ break;
+ case 3:
+ o_ptr->art_flags1 |= TR1_INFRA;
+ break;
+ case 4:
+ o_ptr->art_flags1 |= TR1_TUNNEL;
+ break;
+ case 5:
+ o_ptr->art_flags1 |= TR1_SPEED;
+ break;
+ case 6:
+ o_ptr->art_flags1 |= TR1_BLOWS;
+ break;
+ }
+ }
+ if (fego & ETR4_R_STAT)
+ {
+ /* Add a random stat */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags1 |= TR1_STR;
+ break;
+ case 2:
+ o_ptr->art_flags1 |= TR1_INT;
+ break;
+ case 3:
+ o_ptr->art_flags1 |= TR1_WIS;
+ break;
+ case 4:
+ o_ptr->art_flags1 |= TR1_DEX;
+ break;
+ case 5:
+ o_ptr->art_flags1 |= TR1_CON;
+ break;
+ case 6:
+ o_ptr->art_flags1 |= TR1_CHR;
+ break;
+ }
+ }
+
+ if (fego & ETR4_R_STAT_SUST)
+ {
+ /* Add a random stat and sustain it */
+ switch (randint(6))
+ {
+ case 1:
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ break;
+ }
+
+ case 2:
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ break;
+ }
+
+ case 3:
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ break;
+ }
+
+ case 4:
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ break;
+ }
+
+ case 5:
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ break;
+ }
+ case 6:
+ {
+ o_ptr->art_flags1 |= TR1_CHR;
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ break;
+ }
+ }
+ }
+
+ if (fego & ETR4_R_IMMUNITY)
+ {
+ /* Give a random immunity */
+ switch (randint(4))
+ {
+ case 1:
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ o_ptr->art_flags3 |= TR3_IGNORE_FIRE;
+ break;
+ }
+ case 2:
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ o_ptr->art_flags3 |= TR3_IGNORE_ACID;
+ break;
+ }
+ case 3:
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ o_ptr->art_flags3 |= TR3_IGNORE_ELEC;
+ break;
+ }
+ case 4:
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ o_ptr->art_flags3 |= TR3_IGNORE_COLD;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Complete the "creation" of an object by applying "magic" to the item
+ *
+ * This includes not only rolling for random bonuses, but also putting the
+ * finishing touches on ego-items and artifacts, giving charges to wands and
+ * staffs, giving fuel to lites, and placing traps on chests.
+ *
+ * In particular, note that "Instant Artifacts", if "created" by an external
+ * routine, must pass through this function to complete the actual creation.
+ *
+ * The base "chance" of the item being "good" increases with the "level"
+ * parameter, which is usually derived from the dungeon level, being equal
+ * to the level plus 10, up to a maximum of 75. If "good" is true, then
+ * the object is guaranteed to be "good". If an object is "good", then
+ * the chance that the object will be "great" (ego-item or artifact), also
+ * increases with the "level", being equal to half the level, plus 5, up to
+ * a maximum of 20. If "great" is true, then the object is guaranteed to be
+ * "great". At dungeon level 65 and below, 15/100 objects are "great".
+ *
+ * If the object is not "good", there is a chance it will be "cursed", and
+ * if it is "cursed", there is a chance it will be "broken". These chances
+ * are related to the "good" / "great" chances above.
+ *
+ * Otherwise "normal" rings and amulets will be "good" half the time and
+ * "cursed" half the time, unless the ring/amulet is always good or cursed.
+ *
+ * If "okay" is true, and the object is going to be "great", then there is
+ * a chance that an artifact will be created. This is true even if both the
+ * "good" and "great" arguments are false. As a total hack, if "great" is
+ * true, then the item gets 3 extra "attempts" to become an artifact.
+ */
+int hack_apply_magic_power = 0;
+void apply_magic(object_type *o_ptr, int lev, bool okay, bool good, bool great)
+{
+ int i, rolls, f1, f2, power;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Aply luck */
+ lev += luck( -7, 7);
+
+ /* Spell in it ? no ! */
+ if (k_ptr->flags5 & TR5_SPELL_CONTAIN)
+ o_ptr->pval2 = -1;
+
+ /* Important to do before all else, be sure to have the basic obvious flags set */
+ o_ptr->art_oflags1 = k_ptr->oflags1;
+ o_ptr->art_oflags2 = k_ptr->oflags2;
+ o_ptr->art_oflags3 = k_ptr->oflags3;
+ o_ptr->art_oflags4 = k_ptr->oflags4;
+ o_ptr->art_oflags5 = k_ptr->oflags5;
+ o_ptr->art_oesp = k_ptr->oesp;
+
+ /* No need to touch normal artifacts */
+ if (k_ptr->flags3 & TR3_NORM_ART)
+ {
+ /* Ahah! we tried to trick us !! */
+ if (k_ptr->artifact ||
+ ((k_ptr->flags4 & TR4_SPECIAL_GENE) &&
+ (!k_allow_special[o_ptr->k_idx])))
+ {
+ object_prep(o_ptr, lookup_kind(k_ptr->btval, k_ptr->bsval));
+ if (wizard) msg_print("We've been tricked!");
+ }
+ else
+ {
+ /* Arg I hate so much to do that ... but I see no other way */
+ if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
+ {
+ s32b base_lvl, max_lvl;
+
+ /* Is the spell predefined by the object kind? */
+ if (k_ptr->pval == -1)
+ {
+ o_ptr->pval2 = k_ptr->pval2;
+ }
+
+ /* Determine a base and a max level */
+ call_lua("get_stick_base_level", "(d,d,d)", "d", o_ptr->tval, dun_level, o_ptr->pval2, &base_lvl);
+ call_lua("get_stick_max_level", "(d,d,d)", "d", o_ptr->tval, dun_level, o_ptr->pval2, &max_lvl);
+ o_ptr->pval3 = (max_lvl << 16) + (base_lvl & 0xFFFF);
+
+ /* Hack -- charge wands */
+ charge_stick(o_ptr);
+ }
+
+ k_ptr->artifact = TRUE;
+
+ if (cheat_peek || p_ptr->precognition) object_mention(o_ptr);
+ }
+
+ return;
+ }
+
+ /* Maximum "level" for various things */
+ if (lev > MAX_DEPTH - 1) lev = MAX_DEPTH - 1;
+
+
+ /* Base chance of being "good" */
+ f1 = lev + 10 + luck( -15, 15);
+
+ /* Maximal chance of being "good" */
+ if (f1 > 75) f1 = 75;
+
+ /* Base chance of being "great" */
+ f2 = f1 / 2;
+
+ /* Maximal chance of being "great" */
+ if (f2 > 20) f2 = 20;
+
+
+ /* Assume normal */
+ power = 0;
+
+ /* Roll for "good" */
+ if (good || magik(f1))
+ {
+ /* Assume "good" */
+ power = 1;
+
+ /* Roll for "great" */
+ if (great || magik(f2)) power = 2;
+ }
+
+ /* Roll for "cursed" */
+ else if (magik(f1))
+ {
+ /* Assume "cursed" */
+ power = -1;
+
+ /* Roll for "broken" */
+ if (magik(f2)) power = -2;
+ }
+
+ /* Mega hack */
+ if (hack_apply_magic_power)
+ {
+ if (hack_apply_magic_power == -99)
+ power = 0;
+ else
+ power = hack_apply_magic_power;
+ }
+ hack_apply_magic_power = 0;
+
+ /* Assume no rolls */
+ rolls = 0;
+
+ /* Get one roll if excellent */
+ if (power >= 2) rolls = 1;
+
+ /* Hack -- Get four rolls if forced great */
+ if (great) rolls = 4;
+
+ /* Hack -- Get no rolls if not allowed */
+ if (!okay || o_ptr->name1) rolls = 0;
+
+ /* Roll for artifacts if allowed */
+ for (i = 0; i < rolls; i++)
+ {
+ /* Roll for an artifact */
+ if (make_artifact(o_ptr)) break;
+ }
+
+ /* Mega hack -- to lazy to do it properly with hooks :) */
+ if ((o_ptr->name1 == ART_POWER) && (quest[QUEST_ONE].status < QUEST_STATUS_TAKEN))
+ {
+ o_ptr->name1 = 0;
+ o_ptr->name2 = 0;
+ o_ptr->name2b = 0;
+ object_prep(o_ptr, lookup_kind(TV_RING, SV_RING_INVIS));
+ }
+
+
+ /* Hack -- analyze artifacts */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Hack -- Mark the artifact as "created" */
+ a_ptr->cur_num = 1;
+
+ /* Extract the other fields */
+ o_ptr->pval = a_ptr->pval;
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+ o_ptr->to_a = a_ptr->to_a;
+ o_ptr->to_h = a_ptr->to_h;
+ o_ptr->to_d = a_ptr->to_d;
+ o_ptr->weight = a_ptr->weight;
+ o_ptr->number = 1;
+
+ /* Hack -- extract the "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Mega-Hack -- increase the rating */
+ rating += 10;
+
+ /* Mega-Hack -- increase the rating again */
+ if (a_ptr->cost > 50000L) rating += 10;
+
+ /* Set the good item flag */
+ good_item_flag = TRUE;
+
+ /* Cheat -- peek at the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ /* Spell in it ? no ! */
+ if (a_ptr->flags5 & TR5_SPELL_CONTAIN)
+ o_ptr->pval2 = -1;
+
+ /* Give a basic exp/exp level to an artifact that needs it */
+ if (a_ptr->flags4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Done */
+ return;
+ }
+
+
+ /* Apply magic */
+ switch (o_ptr->tval)
+ {
+ case TV_RANDART:
+ {
+ finalize_randart(o_ptr, lev);
+ break;
+ }
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_TRAPKIT:
+ {
+ if (power) a_m_aux_1(o_ptr, lev, power);
+ break;
+ }
+
+ case TV_DAEMON_BOOK:
+ {
+ /* UGLY, but needed, depending of sval teh demon stuff are eitehr weapon or armor */
+ if (o_ptr->sval == SV_DEMONBLADE)
+ {
+ if (power) a_m_aux_1(o_ptr, lev, power);
+ }
+ else
+ {
+ if (power) a_m_aux_2(o_ptr, lev, power);
+ }
+ break;
+ }
+
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_SHIELD:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_CLOAK:
+ case TV_GLOVES:
+ case TV_BOOTS:
+ {
+#if 0
+ if (power ||
+ ((o_ptr->tval == TV_HELM) && (o_ptr->sval == SV_DRAGON_HELM)) ||
+ ((o_ptr->tval == TV_SHIELD) && (o_ptr->sval == SV_DRAGON_SHIELD)) ||
+ ((o_ptr->tval == TV_CLOAK) && (o_ptr->sval == SV_ELVEN_CLOAK)))
+ a_m_aux_2(o_ptr, lev, power);
+#else
+ a_m_aux_2(o_ptr, lev, power);
+#endif
+ break;
+ }
+
+ case TV_RING:
+ case TV_AMULET:
+ {
+ if (!power && (rand_int(100) < 50)) power = -1;
+ a_m_aux_3(o_ptr, lev, power);
+ break;
+ }
+
+ default:
+ {
+ a_m_aux_4(o_ptr, lev, power);
+ break;
+ }
+ }
+
+ if (o_ptr->art_name) rating += 40;
+
+ /* Hack -- analyze ego-items */
+ else if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+ int j;
+ bool limit_blows = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+ s16b e_idx;
+
+ e_idx = o_ptr->name2;
+
+ /* Ok now, THAT is truly ugly */
+try_an_other_ego:
+ e_ptr = &e_info[e_idx];
+
+ /* Hack -- extra powers */
+ for (j = 0; j < 5; j++)
+ {
+ /* Rarity check */
+ if (magik(e_ptr->rar[j]))
+ {
+ o_ptr->art_flags1 |= e_ptr->flags1[j];
+ o_ptr->art_flags2 |= e_ptr->flags2[j];
+ o_ptr->art_flags3 |= e_ptr->flags3[j];
+ o_ptr->art_flags4 |= e_ptr->flags4[j];
+ o_ptr->art_flags5 |= e_ptr->flags5[j];
+ o_ptr->art_esp |= e_ptr->esp[j];
+
+ o_ptr->art_oflags1 |= e_ptr->oflags1[j];
+ o_ptr->art_oflags2 |= e_ptr->oflags2[j];
+ o_ptr->art_oflags3 |= e_ptr->oflags3[j];
+ o_ptr->art_oflags4 |= e_ptr->oflags4[j];
+ o_ptr->art_oflags5 |= e_ptr->oflags5[j];
+ o_ptr->art_oesp |= e_ptr->oesp[j];
+
+ add_random_ego_flag(o_ptr, e_ptr->fego[j], &limit_blows);
+ }
+ }
+
+ /* No insane number of blows */
+ if (limit_blows && (o_ptr->art_flags1 & TR1_BLOWS))
+ {
+ if (o_ptr->pval > 2) o_ptr->pval = randint(2);
+ }
+
+ /* get flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- acquire "cursed" flag */
+ if (f3 & TR3_CURSED) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack -- obtain bonuses */
+ if (e_ptr->max_to_h > 0) o_ptr->to_h += randint(e_ptr->max_to_h);
+ if (e_ptr->max_to_h < 0) o_ptr->to_h -= randint( -e_ptr->max_to_h);
+ if (e_ptr->max_to_d > 0) o_ptr->to_d += randint(e_ptr->max_to_d);
+ if (e_ptr->max_to_d < 0) o_ptr->to_d -= randint( -e_ptr->max_to_d);
+ if (e_ptr->max_to_a > 0) o_ptr->to_a += randint(e_ptr->max_to_a);
+ if (e_ptr->max_to_a < 0) o_ptr->to_a -= randint( -e_ptr->max_to_a);
+
+ /* Hack -- obtain pval */
+ if (e_ptr->max_pval > 0) o_ptr->pval += randint(e_ptr->max_pval);
+ if (e_ptr->max_pval < 0) o_ptr->pval -= randint( -e_ptr->max_pval);
+
+ /* Hack -- apply rating bonus */
+ rating += e_ptr->rating;
+
+ if (o_ptr->name2b && (o_ptr->name2b != e_idx))
+ {
+ e_idx = o_ptr->name2b;
+ goto try_an_other_ego;
+ }
+
+ /* Spell in it ? no ! */
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ /* Mega hack, mage staves of spell cannot SPELL_CONTAIN */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ o_ptr->art_flags5 &= ~TR5_SPELL_CONTAIN;
+ }
+ else
+ o_ptr->pval2 = -1;
+ }
+
+ /* Cheat -- describe the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+ }
+
+
+ /* Examine real objects */
+ if (o_ptr->k_idx)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Hack -- acquire "cursed" flag */
+ if (k_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Spell in it ? no ! */
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ /* Mega hack, mage staves of spell cannot SPELL_CONTAIN */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
+ {
+ o_ptr->art_flags5 &= ~TR5_SPELL_CONTAIN;
+ }
+ else
+ o_ptr->pval2 = -1;
+ }
+
+ /* Hacccccccckkkkk attack ! :) -- To prevent som ugly crashs */
+ if ((o_ptr->tval == TV_MSTAFF) && (o_ptr->sval == SV_MSTAFF) && (o_ptr->pval < 0))
+ {
+ o_ptr->pval = 0;
+ }
+
+ /* Hack, cant be done in a_m_aux4 because the ego flags are not yet in place */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Set the max mana and the current mana */
+ o_ptr->pval2 = (f4 & TR4_CAPACITY) ? o_ptr->sval * 2 : o_ptr->sval;
+
+ o_ptr->timeout = o_ptr->pval2;
+ }
+
+ /* Remove some unnecessary stuff hack */
+ if (o_ptr->tval == TV_TRAPKIT) trap_hack(o_ptr);
+ }
+}
+
+
+/* The themed objects to use */
+static obj_theme match_theme;
+
+/*
+ * XXX XXX XXX It relies on the fact that obj_theme is a four byte structure
+ * for its efficient operation. A horrendous hack, I'd say.
+ */
+void init_match_theme(obj_theme theme)
+{
+ /* Save the theme */
+ match_theme = theme;
+}
+
+/*
+ * Ditto XXX XXX XXX
+ */
+static bool theme_changed(obj_theme theme)
+{
+ /* Any of the themes has been changed */
+ if (theme.treasure != match_theme.treasure) return (TRUE);
+ if (theme.combat != match_theme.combat) return (TRUE);
+ if (theme.magic != match_theme.magic) return (TRUE);
+ if (theme.tools != match_theme.tools) return (TRUE);
+
+ /* No changes */
+ return (FALSE);
+}
+
+
+/*
+ * Maga-Hack -- match certain types of object only.
+ */
+bool kind_is_theme(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ s32b prob = 0;
+
+
+ /*
+ * Paranoia -- Prevent accidental "(Nothing)"
+ * that are results of uninitialised theme structs.
+ *
+ * Caution: Junks go into the allocation table.
+ */
+ if (match_theme.treasure + match_theme.combat +
+ match_theme.magic + match_theme.tools == 0) return (TRUE);
+
+
+ /* Pick probability to use */
+ switch (k_ptr->tval)
+ {
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_JUNK:
+ case TV_CORPSE:
+ case TV_EGG:
+ {
+ /*
+ * Degree of junk is defined in terms of the other
+ * 4 quantities XXX XXX XXX
+ * The type of prob should be *signed* as well as
+ * larger than theme components, or we would see
+ * unexpected, well, junks.
+ */
+ prob = 100 - (match_theme.treasure + match_theme.combat +
+ match_theme.magic + match_theme.tools);
+ break;
+ }
+ case TV_CHEST:
+ prob = match_theme.treasure;
+ break;
+ case TV_CROWN:
+ prob = match_theme.treasure;
+ break;
+ case TV_DRAG_ARMOR:
+ prob = match_theme.treasure;
+ break;
+ case TV_AMULET:
+ prob = match_theme.treasure;
+ break;
+ case TV_RING:
+ prob = match_theme.treasure;
+ break;
+
+ case TV_SHOT:
+ prob = match_theme.combat;
+ break;
+ case TV_ARROW:
+ prob = match_theme.combat;
+ break;
+ case TV_BOLT:
+ prob = match_theme.combat;
+ break;
+ case TV_BOOMERANG:
+ prob = match_theme.combat;
+ break;
+ case TV_BOW:
+ prob = match_theme.combat;
+ break;
+ case TV_HAFTED:
+ prob = match_theme.combat;
+ break;
+ case TV_POLEARM:
+ prob = match_theme.combat;
+ break;
+ case TV_SWORD:
+ prob = match_theme.combat;
+ break;
+ case TV_AXE:
+ prob = match_theme.combat;
+ break;
+ case TV_GLOVES:
+ prob = match_theme.combat;
+ break;
+ case TV_HELM:
+ prob = match_theme.combat;
+ break;
+ case TV_SHIELD:
+ prob = match_theme.combat;
+ break;
+ case TV_SOFT_ARMOR:
+ prob = match_theme.combat;
+ break;
+ case TV_HARD_ARMOR:
+ prob = match_theme.combat;
+ break;
+
+ case TV_MSTAFF:
+ prob = match_theme.magic;
+ break;
+ case TV_STAFF:
+ prob = match_theme.magic;
+ break;
+ case TV_WAND:
+ prob = match_theme.magic;
+ break;
+ case TV_ROD:
+ prob = match_theme.magic;
+ break;
+ case TV_ROD_MAIN:
+ prob = match_theme.magic;
+ break;
+ case TV_SCROLL:
+ prob = match_theme.magic;
+ break;
+ case TV_PARCHMENT:
+ prob = match_theme.magic;
+ break;
+ case TV_POTION:
+ prob = match_theme.magic;
+ break;
+ case TV_POTION2:
+ prob = match_theme.magic;
+ break;
+ case TV_BATERIE:
+ prob = match_theme.magic;
+ break;
+ case TV_RANDART:
+ prob = match_theme.magic;
+ break;
+ case TV_RUNE1:
+ prob = match_theme.magic;
+ break;
+ case TV_RUNE2:
+ prob = match_theme.magic;
+ break;
+ case TV_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_SYMBIOTIC_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_MUSIC_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_DRUID_BOOK:
+ prob = match_theme.magic;
+ break;
+ case TV_DAEMON_BOOK:
+ prob = match_theme.magic;
+ break;
+
+ case TV_LITE:
+ prob = match_theme.tools;
+ break;
+ case TV_CLOAK:
+ prob = match_theme.tools;
+ break;
+ case TV_BOOTS:
+ prob = match_theme.tools;
+ break;
+ case TV_SPIKE:
+ prob = match_theme.tools;
+ break;
+ case TV_DIGGING:
+ prob = match_theme.tools;
+ break;
+ case TV_FLASK:
+ prob = match_theme.tools;
+ break;
+ case TV_FOOD:
+ prob = match_theme.tools;
+ break;
+ case TV_TOOL:
+ prob = match_theme.tools;
+ break;
+ case TV_INSTRUMENT:
+ prob = match_theme.tools;
+ break;
+ case TV_TRAPKIT:
+ prob = match_theme.tools;
+ break;
+ }
+
+ /* Roll to see if it can be made */
+ if (rand_int(100) < prob) return (TRUE);
+
+ /* Not a match */
+ return (FALSE);
+}
+
+/*
+ * Determine if an object must not be generated.
+ */
+int kind_is_legal_special = -1;
+bool kind_is_legal(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (!kind_is_theme(k_idx)) return FALSE;
+
+ if (k_ptr->flags4 & TR4_SPECIAL_GENE)
+ {
+ if (k_allow_special[k_idx]) return TRUE;
+ else return FALSE;
+ }
+
+ /* No 2 times the same normal artifact */
+ if ((k_ptr->flags3 & TR3_NORM_ART) && (k_ptr->artifact))
+ {
+ return FALSE;
+ }
+
+ if (k_ptr->tval == TV_CORPSE)
+ {
+ if (k_ptr->sval != SV_CORPSE_SKULL && k_ptr->sval != SV_CORPSE_SKELETON &&
+ k_ptr->sval != SV_CORPSE_HEAD && k_ptr->sval != SV_CORPSE_CORPSE)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ if (k_ptr->tval == TV_HYPNOS) return FALSE;
+
+ /* Used only for the Nazgul rings */
+ if ((k_ptr->tval == TV_RING) && (k_ptr->sval == SV_RING_SPECIAL)) return FALSE;
+
+ /* Are we forced to one tval ? */
+ if ((kind_is_legal_special != -1) && (kind_is_legal_special != k_ptr->tval)) return (FALSE);
+
+ /* Assume legal */
+ return TRUE;
+}
+
+
+/*
+ * Hack -- determine if a template is "good"
+ */
+bool kind_is_good(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (!kind_is_legal(k_idx)) return FALSE;
+
+ /* Analyze the item type */
+ switch (k_ptr->tval)
+ {
+ /* Armor -- Good unless damaged */
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ {
+ if (k_ptr->to_a < 0) return (FALSE);
+ return (TRUE);
+ }
+
+ /* Weapons -- Good unless damaged */
+ case TV_BOW:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_DIGGING:
+ case TV_TRAPKIT:
+ case TV_MSTAFF:
+ case TV_BOOMERANG:
+ {
+ if (k_ptr->to_h < 0) return (FALSE);
+ if (k_ptr->to_d < 0) return (FALSE);
+ return (TRUE);
+ }
+
+ /* Ammo -- Arrows/Bolts are good */
+ case TV_BOLT:
+ case TV_ARROW:
+ {
+ return (TRUE);
+ }
+
+ /* Rods - Silver and better are good */
+ case TV_ROD_MAIN:
+ {
+ if (k_ptr->sval >= SV_ROD_SILVER) return (TRUE);
+ return FALSE;
+ }
+
+ /* Expensive rod tips are good */
+ case TV_ROD:
+ {
+ /* Probing is not good, but Recall is*/
+ if (k_ptr->cost >= 4500) return TRUE;
+ return FALSE;
+ }
+
+ /* The Tomes are good */
+ case TV_BOOK:
+ {
+ if (k_ptr->sval <= SV_BOOK_MAX_GOOD) return (TRUE);
+ return FALSE;
+ }
+
+ /* Rings -- Rings of Speed are good */
+ case TV_RING:
+ {
+ if (k_ptr->sval == SV_RING_SPEED) return (TRUE);
+ return (FALSE);
+ }
+
+ /* Amulets -- Some are good */
+ case TV_AMULET:
+ {
+ if (k_ptr->sval == SV_AMULET_THE_MAGI) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_DEVOTION) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_WEAPONMASTERY) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_TRICKERY) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_RESISTANCE) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_REFLECTION) return (TRUE);
+ if (k_ptr->sval == SV_AMULET_TELEPATHY) return (TRUE);
+ return (FALSE);
+ }
+ }
+
+ /* Assume not good */
+ return (FALSE);
+}
+
+/*
+* Determine if template is suitable for building a randart -- dsb
+*/
+bool kind_is_artifactable(int k_idx)
+{
+ int i, j;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (kind_is_good(k_idx))
+ {
+ /* We consider the item artifactable if there is at least one
+ * randart power in ra_info that could be added to this item. */
+ for (i = 0; i < max_ra_idx; i++)
+ {
+ randart_part_type *ra_ptr = &ra_info[i];
+
+ for (j = 0; j < 20; j++)
+ {
+ if (ra_ptr->tval[j] != k_ptr->tval) continue;
+ if (ra_ptr->min_sval[j] > k_ptr->sval) continue;
+ if (ra_ptr->max_sval[j] < k_ptr->sval) continue;
+ /* Winner */
+ return TRUE;
+ }
+ }
+ }
+
+ /* No match. Too bad. */
+ return FALSE;
+}
+
+
+/*
+ * Attempt to make an object (normal or good/great)
+ *
+ * This routine plays nasty games to generate the "special artifacts".
+ *
+ * This routine uses "object_level" for the "generation level".
+ *
+ * We assume that the given object has been "wiped".
+ *
+ * To Watch: The allocation table caching is heavily relies on
+ * an assumption that the SPECIAL_GENE objects should only be created
+ * through the forge--object_prep()--apply_magic() sequence and
+ * get_obj_num() should never be called for that purpose XXX XXX XXX
+ */
+bool make_object(object_type *j_ptr, bool good, bool great, obj_theme theme)
+{
+ int invprob, base;
+
+
+ /* Chance of "special object" */
+ invprob = (good ? 10 - luck( -9, 9) : 1000);
+
+ /* Base level for the object */
+ base = (good ? (object_level + 10) : object_level);
+
+
+ /* Generate a special object, or a normal object */
+ if ((rand_int(invprob) != 0) || !make_artifact_special(j_ptr))
+ {
+ int k_idx;
+
+ /* See if the theme has been changed XXX XXX XXX */
+ if (theme_changed(theme))
+ {
+ /* Select items based on "theme" */
+ init_match_theme(theme);
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+ }
+
+ /* Good objects */
+ if (good)
+ {
+ /* Activate restriction */
+ get_obj_num_hook = kind_is_good;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+ }
+
+ /* Normal objects -- only when the cache is invalidated */
+ else if (!alloc_kind_table_valid)
+ {
+ /* Activate normal restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+
+ /* The table is synchronised */
+ alloc_kind_table_valid = TRUE;
+ }
+
+ /* Pick a random object */
+ k_idx = get_obj_num(base);
+
+ /* Good objects */
+ if (good)
+ {
+ /* Restore normal restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+
+ /* The table is synchronised */
+ alloc_kind_table_valid = TRUE;
+ }
+
+ /* Handle failure */
+ if (!k_idx) return (FALSE);
+
+ /* Prepare the object */
+ object_prep(j_ptr, k_idx);
+ }
+
+ /* Apply magic (allow artifacts) */
+ apply_magic(j_ptr, object_level, TRUE, good, great);
+
+ /* Hack -- generate multiple spikes/missiles */
+ switch (j_ptr->tval)
+ {
+ case TV_SPIKE:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ {
+ j_ptr->number = (byte)damroll(6, 7);
+ }
+ }
+
+ /* hack, no multiple artifacts */
+ if (artifact_p(j_ptr)) j_ptr->number = 1;
+
+ /* Notice "okay" out-of-depth objects */
+ if (!cursed_p(j_ptr) &&
+ (k_info[j_ptr->k_idx].level > dun_level))
+ {
+ /* Rating increase */
+ rating += (k_info[j_ptr->k_idx].level - dun_level);
+
+ /* Cheat -- peek at items */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(j_ptr);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Attempt to place an object (normal or good/great) at the given location.
+ *
+ * This routine plays nasty games to generate the "special artifacts".
+ *
+ * This routine uses "object_level" for the "generation level".
+ *
+ * This routine requires a clean floor grid destination.
+ */
+void place_object(int y, int x, bool good, bool great, int where)
+{
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ object_type forge;
+ object_type *q_ptr;
+
+
+ /* Paranoia -- check bounds */
+ if (!in_bounds(y, x)) return;
+
+ /* Require clean floor space */
+ if (!cave_clean_bold(y, x)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make an object (if possible) */
+ if (!make_object(q_ptr, good, great, d_info[dungeon_type].objs)) return;
+
+ if (where == OBJ_FOUND_VAULT)
+ {
+ q_ptr->found = OBJ_FOUND_VAULT;
+ q_ptr->found_aux1 = dungeon_type;
+ q_ptr->found_aux2 = level_or_feat(dungeon_type, dun_level);
+ }
+ else if (where == OBJ_FOUND_FLOOR)
+ {
+ q_ptr->found = OBJ_FOUND_FLOOR;
+ q_ptr->found_aux1 = dungeon_type;
+ q_ptr->found_aux2 = level_or_feat(dungeon_type, dun_level);
+ }
+ else if (where == OBJ_FOUND_SPECIAL)
+ {
+ q_ptr->found = OBJ_FOUND_SPECIAL;
+ }
+ else if (where == OBJ_FOUND_RUBBLE)
+ {
+ q_ptr->found = OBJ_FOUND_RUBBLE;
+ }
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure Copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Location */
+ o_ptr->iy = y;
+ o_ptr->ix = x;
+
+ /* Acquire grid */
+ c_ptr = &cave[y][x];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* Object array overflow */
+ else
+ {
+ /* Hack -- Preserve artifacts */
+ if (q_ptr->name1)
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ else if (k_info[q_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[q_ptr->k_idx].artifact = 0;
+ }
+ else if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ }
+}
+
+
+/*
+ * XXX XXX XXX Do not use these hard-coded values.
+ */
+#define OBJ_GOLD_LIST 480 /* First "gold" entry */
+#define MAX_GOLD 18 /* Number of "gold" entries */
+
+/*
+ * Make a treasure object
+ *
+ * The location must be a legal, clean, floor grid.
+ */
+bool make_gold(object_type *j_ptr)
+{
+ int i;
+
+ s32b base;
+
+
+ /* Hack -- Pick a Treasure variety */
+ i = ((randint(object_level + 2) + 2) / 2) - 1;
+
+ /* Apply "extra" magic */
+ if (rand_int(GREAT_OBJ) == 0)
+ {
+ i += randint(object_level + 1);
+ }
+
+ /* Hack -- Creeping Coins only generate "themselves" */
+ if (coin_type) i = coin_type;
+
+ /* Do not create "illegal" Treasure Types */
+ if (i >= MAX_GOLD) i = MAX_GOLD - 1;
+
+ /* Prepare a gold object */
+ object_prep(j_ptr, OBJ_GOLD_LIST + i);
+
+ /* Hack -- Base coin cost */
+ base = k_info[OBJ_GOLD_LIST + i].cost;
+
+ /* Determine how much the treasure is "worth" */
+ j_ptr->pval = (base + (8L * randint(base)) + randint(8));
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Places a treasure (Gold or Gems) at given location
+ *
+ * The location must be a legal, clean, floor grid.
+ */
+void place_gold(int y, int x)
+{
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ object_type forge;
+ object_type *q_ptr;
+
+
+ /* Paranoia -- check bounds */
+ if (!in_bounds(y, x)) return;
+
+ /* Require clean floor space */
+ if (!cave_clean_bold(y, x)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make some gold */
+ if (!make_gold(q_ptr)) return;
+
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[o_idx];
+
+ /* Copy the object */
+ object_copy(o_ptr, q_ptr);
+
+ /* Save location */
+ o_ptr->iy = y;
+ o_ptr->ix = x;
+
+ /* Acquire grid */
+ c_ptr = &cave[y][x];
+
+ /* Build a stack */
+ o_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+}
+
+
+/*
+ * Let an object fall to the ground at or near a location.
+ *
+ * The initial location is assumed to be "in_bounds()".
+ *
+ * This function takes a parameter "chance". This is the percentage
+ * chance that the item will "disappear" instead of drop. If the object
+ * has been thrown, then this is the chance of disappearance on contact.
+ *
+ * Hack -- this function uses "chance" to determine if it should produce
+ * some form of "description" of the drop event (under the player).
+ *
+ * We check several locations to see if we can find a location at which
+ * the object can combine, stack, or be placed. Artifacts will try very
+ * hard to be placed, including "teleporting" to a useful grid if needed.
+ */
+s16b drop_near(object_type *j_ptr, int chance, int y, int x)
+{
+ int i, k, d, s;
+
+ int bs, bn;
+ int by, bx;
+ int dy, dx;
+ int ty, tx;
+
+ s16b o_idx = 0;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ cave_type *c_ptr;
+
+ char o_name[80];
+
+ bool flag = FALSE;
+ bool done = FALSE;
+
+ bool plural = FALSE;
+
+
+ /* Extract plural */
+ if (j_ptr->number != 1) plural = TRUE;
+
+ /* Describe object */
+ object_desc(o_name, j_ptr, FALSE, 0);
+
+
+ /* Handle normal "breakage" */
+ if (!(j_ptr->art_name || artifact_p(j_ptr)) && (rand_int(100) < chance))
+ {
+ /* Message */
+ msg_format("The %s disappear%s.",
+ o_name, (plural ? "" : "s"));
+
+ /* Debug */
+ if (wizard) msg_print("(breakage)");
+
+ /* Failure */
+ return (0);
+ }
+
+
+ /* Score */
+ bs = -1;
+
+ /* Picker */
+ bn = 0;
+
+ /* Default */
+ by = y;
+ bx = x;
+
+ /* Scan local grids */
+ for (dy = -3; dy <= 3; dy++)
+ {
+ /* Scan local grids */
+ for (dx = -3; dx <= 3; dx++)
+ {
+ bool comb = FALSE;
+
+ /* Calculate actual distance */
+ d = (dy * dy) + (dx * dx);
+
+ /* Ignore distant grids */
+ if (d > 10) continue;
+
+ /* Location */
+ ty = y + dy;
+ tx = x + dx;
+
+ /* Skip illegal grids */
+ if (!in_bounds(ty, tx)) continue;
+
+ /* Require line of sight */
+ if (!los(y, x, ty, tx)) continue;
+
+ /* Obtain grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) continue;
+
+ /* No traps */
+ if (c_ptr->t_idx) continue;
+
+ /* No objects */
+ k = 0;
+
+ /* Scan objects in that grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Check for possible combination */
+ if (object_similar(o_ptr, j_ptr)) comb = TRUE;
+
+ /* Count objects */
+ k++;
+ }
+
+ /* Add new object */
+ if (!comb) k++;
+
+ /* No stacking (allow combining) */
+ if (!testing_stack && (k > 1)) continue;
+
+ /* Paranoia */
+ if (k > 23) continue;
+
+ /* Calculate score */
+ s = 1000 - (d + k * 5);
+
+ /* Skip bad values */
+ if (s < bs) continue;
+
+ /* New best value */
+ if (s > bs) bn = 0;
+
+ /* Apply the randomizer to equivalent values */
+ if ((++bn >= 2) && (rand_int(bn) != 0)) continue;
+
+ /* Keep score */
+ bs = s;
+
+ /* Track it */
+ by = ty;
+ bx = tx;
+
+ /* Okay */
+ flag = TRUE;
+ }
+ }
+
+
+ /* Handle lack of space */
+ if (!flag && !(artifact_p(j_ptr) || j_ptr->art_name))
+ {
+ /* Message */
+ msg_format("The %s disappear%s.",
+ o_name, (plural ? "" : "s"));
+
+ /* Debug */
+ if (wizard) msg_print("(no floor space)");
+
+ /* Failure */
+ return (0);
+ }
+
+
+ /* Find a grid */
+ for (i = 0; !flag; i++)
+ {
+ /* Bounce around */
+ if (i < 1000)
+ {
+ ty = rand_spread(by, 1);
+ tx = rand_spread(bx, 1);
+ }
+
+ /* Random locations */
+ else
+ {
+ ty = rand_int(cur_hgt);
+ tx = rand_int(cur_wid);
+ }
+
+ /* Grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if ((c_ptr->feat != FEAT_FLOOR) &&
+ (c_ptr->feat != FEAT_SHAL_WATER) &&
+ (c_ptr->feat != FEAT_GRASS) &&
+ (c_ptr->feat != FEAT_DIRT) &&
+ (c_ptr->feat != FEAT_SHAL_LAVA)) continue;
+
+ /* Bounce to that location */
+ by = ty;
+ bx = tx;
+
+ /* Require floor space */
+ if (!cave_clean_bold(by, bx)) continue;
+
+ /* Okay */
+ flag = TRUE;
+ }
+
+
+ /* Grid */
+ c_ptr = &cave[by][bx];
+
+ /* Scan objects in that grid for combination */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Check for combination */
+ if (object_similar(o_ptr, j_ptr))
+ {
+ /* Combine the items */
+ object_absorb(o_ptr, j_ptr);
+
+ /* Success */
+ done = TRUE;
+
+ /* Done */
+ break;
+ }
+ }
+
+ /* Get new object */
+ if (!done) o_idx = o_pop();
+
+ /* Failure */
+ if (!done && !o_idx)
+ {
+ /* Message */
+ msg_format("The %s disappear%s.",
+ o_name, (plural ? "" : "s"));
+
+ /* Debug */
+ if (wizard) msg_print("(too many objects)");
+
+ /* Hack -- Preserve artifacts */
+ if (j_ptr->name1)
+ {
+ a_info[j_ptr->name1].cur_num = 0;
+ }
+ else if (k_info[j_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[j_ptr->k_idx].artifact = 0;
+ }
+ else if (j_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[j_ptr->sval].generated = FALSE;
+ }
+
+ /* Failure */
+ return (0);
+ }
+
+ /* Stack */
+ if (!done)
+ {
+ /* Structure copy */
+ object_copy(&o_list[o_idx], j_ptr);
+
+ /* Access new object */
+ j_ptr = &o_list[o_idx];
+
+ /* Locate */
+ j_ptr->iy = by;
+ j_ptr->ix = bx;
+
+ /* No monster */
+ j_ptr->held_m_idx = 0;
+
+ /* Build a stack */
+ j_ptr->next_o_idx = c_ptr->o_idx;
+
+ /* Place the object */
+ c_ptr->o_idx = o_idx;
+
+ /* Success */
+ done = TRUE;
+ }
+
+ /* Note the spot */
+ note_spot(by, bx);
+
+ /* Draw the spot */
+ lite_spot(by, bx);
+
+ /* Mega-Hack -- no message if "dropped" by player */
+ /* Message when an object falls under the player */
+ if (chance && (by == p_ptr->py) && (bx == p_ptr->px))
+ {
+ msg_print("You feel something roll beneath your feet.");
+ /* Sound */
+ sound(SOUND_DROP);
+ }
+
+ /* XXX XXX XXX */
+
+ /* Result */
+ return (o_idx);
+}
+
+
+
+
+/*
+ * Scatter some "great" objects near the player
+ */
+void acquirement(int y1, int x1, int num, bool great, bool known)
+{
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Acquirement */
+ while (num--)
+ {
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Wipe the object */
+ object_wipe(i_ptr);
+
+ /* Make a good (or great) object (if possible) */
+ if (!make_object(i_ptr, TRUE, great, d_info[dungeon_type].objs)) continue;
+
+ if (known)
+ {
+ object_aware(i_ptr);
+ object_known(i_ptr);
+ }
+
+ /* Drop the object */
+ drop_near(i_ptr, -1, y1, x1);
+ }
+}
+
+
+
+/*
+ * Hack -- instantiate a trap
+ *
+ * XXX XXX XXX This routine should be redone to reflect trap "level".
+ * That is, it does not make sense to have spiked pits at 50 feet.
+ * Actually, it is not this routine, but the "trap instantiation"
+ * code, which should also check for "trap doors" on quest levels.
+ */
+void pick_trap(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Paranoia */
+ if ((c_ptr->t_idx == 0) || (c_ptr->info & CAVE_TRDT)) return;
+
+ /* Activate the trap */
+ c_ptr->info |= (CAVE_TRDT);
+
+ /* Notice and redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+}
+
+/*
+ * Describe the charges on an item in the inventory.
+ */
+void inven_item_charges(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Require staff/wand */
+ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+
+ /* Require known item */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Multiple charges */
+ if (o_ptr->pval != 1)
+ {
+ /* Print a message */
+ msg_format("You have %d charges remaining.", o_ptr->pval);
+ }
+
+ /* Single charge */
+ else
+ {
+ /* Print a message */
+ msg_format("You have %d charge remaining.", o_ptr->pval);
+ }
+}
+
+
+/*
+ * Describe an item in the inventory.
+ */
+void inven_item_describe(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+ char o_name[80];
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Print a message */
+ msg_format("You have %s.", o_name);
+}
+
+
+/*
+ * Increase the "number" of an item in the inventory
+ */
+void inven_item_increase(int item, int num)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Apply */
+ num += o_ptr->number;
+
+ /* Bounds check */
+ if (num > 255) num = 255;
+ else if (num < 0) num = 0;
+
+ /* Un-apply */
+ num -= o_ptr->number;
+
+ /* Change the number and weight */
+ if (num)
+ {
+ /* Add the number */
+ o_ptr->number += num;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana XXX */
+ p_ptr->update |= (PU_MANA);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+}
+
+
+/*
+ * Erase an inventory slot if it has no more items
+ */
+bool inven_item_optimize(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Only optimize real items */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* Only optimize empty items */
+ if (o_ptr->number) return (FALSE);
+
+ /* The item is in the pack */
+ if (item < INVEN_WIELD)
+ {
+ int i;
+
+ /* One less item */
+ inven_cnt--;
+
+ /* Slide everything down */
+ for (i = item; i < INVEN_PACK; i++)
+ {
+ /* Structure copy */
+ p_ptr->inventory[i] = p_ptr->inventory[i + 1];
+ }
+
+ /* Erase the "final" slot */
+ object_wipe(&p_ptr->inventory[i]);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* The item is being wielded */
+ else
+ {
+ /* One less item */
+ equip_cnt--;
+
+ /* Take care of item sets*/
+ if (o_ptr->name1)
+ {
+ takeoff_set(p_ptr->inventory[item].name1, a_info[p_ptr->inventory[item].name1].set);
+ }
+
+ /* Erase the empty slot */
+ object_wipe(&p_ptr->inventory[item]);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate mana XXX */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Describe the charges on an item on the floor.
+ */
+void floor_item_charges(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Require staff/wand */
+ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+
+ /* Require known item */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Multiple charges */
+ if (o_ptr->pval != 1)
+ {
+ /* Print a message */
+ msg_format("There are %d charges remaining.", o_ptr->pval);
+ }
+
+ /* Single charge */
+ else
+ {
+ /* Print a message */
+ msg_format("There is %d charge remaining.", o_ptr->pval);
+ }
+}
+
+
+
+/*
+ * Describe an item in the inventory.
+ */
+void floor_item_describe(int item)
+{
+ object_type *o_ptr = &o_list[item];
+ char o_name[80];
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Print a message */
+ msg_format("You see %s.", o_name);
+}
+
+
+/*
+ * Increase the "number" of an item on the floor
+ */
+void floor_item_increase(int item, int num)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Apply */
+ num += o_ptr->number;
+
+ /* Bounds check */
+ if (num > 255) num = 255;
+ else if (num < 0) num = 0;
+
+ /* Un-apply */
+ num -= o_ptr->number;
+
+ /* Change the number */
+ o_ptr->number += num;
+}
+
+
+/*
+ * Optimize an item on the floor (destroy "empty" items)
+ */
+void floor_item_optimize(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Paranoia -- be sure it exists */
+ if (!o_ptr->k_idx) return;
+
+ /* Only optimize empty items */
+ if (o_ptr->number) return;
+
+ /* Delete the object */
+ delete_object_idx(item);
+}
+
+
+
+
+
+/*
+ * Check if we have space for an item in the pack without overflow
+ */
+bool inven_carry_okay(object_type *o_ptr)
+{
+ int j;
+
+ if (o_ptr->tval == TV_GOLD) return FALSE;
+
+ /* Empty slot? */
+ if (inven_cnt < INVEN_PACK) return (TRUE);
+
+ /* Similar slot? */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ object_type *j_ptr = &p_ptr->inventory[j];
+
+ /* Skip non-objects */
+ if (!j_ptr->k_idx) continue;
+
+ /* Check if the two items can be combined */
+ if (object_similar(j_ptr, o_ptr)) return (TRUE);
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * Add an item to the players inventory, and return the slot used.
+ *
+ * If the new item can combine with an existing item in the inventory,
+ * it will do so, using "object_similar()" and "object_absorb()", otherwise,
+ * the item will be placed into the "proper" location in the inventory.
+ *
+ * This function can be used to "over-fill" the player's pack, but only
+ * once, and such an action must trigger the "overflow" code immediately.
+ * Note that when the pack is being "over-filled", the new item must be
+ * placed into the "overflow" slot, and the "overflow" must take place
+ * before the pack is reordered, but (optionally) after the pack is
+ * combined. This may be tricky. See "dungeon.c" for info.
+ *
+ * Note that this code must remove any location/stack information
+ * from the object once it is placed into the inventory.
+ *
+ * The "final" flag tells this function to bypass the "combine"
+ * and "reorder" code until later.
+ */
+s16b inven_carry(object_type *o_ptr, bool final)
+{
+ int i, j, k;
+ int n = -1;
+ object_type *j_ptr;
+
+ /* Not final */
+ if (!final)
+ {
+ /* Check for combining */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Skip non-objects */
+ if (!j_ptr->k_idx) continue;
+
+ /* Hack -- track last item */
+ n = j;
+
+ /* Check if the two items can be combined */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Combine the items */
+ object_absorb(j_ptr, o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Success */
+ return (j);
+ }
+ }
+ }
+
+
+ /* Paranoia */
+ if (inven_cnt > INVEN_PACK) return ( -1);
+
+
+ /* Find an empty slot */
+ for (j = 0; j <= INVEN_PACK; j++)
+ {
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use it if found */
+ if (!j_ptr->k_idx) break;
+ }
+
+ /* Use that slot */
+ i = j;
+
+
+ /* Hack -- pre-reorder the pack */
+ if (!final && (i < INVEN_PACK))
+ {
+ s32b o_value, j_value;
+
+ /* Get the "value" of the item */
+ o_value = object_value(o_ptr);
+
+ /* Scan every occupied slot */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use empty slots */
+ if (!j_ptr->k_idx) break;
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Non-aware (flavored) items always come last */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Unidentified objects always come last */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+ /* Hack: otherwise identical rods sort by
+ increasing recharge time --dsb */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout > j_ptr->timeout) break;
+ if (o_ptr->timeout < j_ptr->timeout) continue;
+ }
+
+ /* Determine the "value" of the pack item */
+ j_value = object_value(j_ptr);
+
+ /* Objects sort by decreasing value */
+ if (o_value > j_value) break;
+ if (o_value < j_value) continue;
+ }
+
+ /* Use that slot */
+ i = j;
+
+ /* Slide objects */
+ for (k = n; k >= i; k--)
+ {
+ /* Hack -- Slide the item */
+ object_copy(&p_ptr->inventory[k + 1], &p_ptr->inventory[k]);
+ }
+
+ /* Wipe the empty slot */
+ object_wipe(&p_ptr->inventory[i]);
+ }
+
+
+ /* Acquire a copy of the item */
+ object_copy(&p_ptr->inventory[i], o_ptr);
+
+ /* Access new object */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Clean out unused fields */
+ o_ptr->iy = o_ptr->ix = 0;
+ o_ptr->next_o_idx = 0;
+ o_ptr->held_m_idx = 0;
+
+ /* Count the items */
+ inven_cnt++;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Return the slot */
+ return (i);
+}
+
+
+
+/*
+ * Take off (some of) a non-cursed equipment item
+ *
+ * Note that only one item at a time can be wielded per slot.
+ *
+ * Note that taking off an item when "full" may cause that item
+ * to fall to the ground.
+ *
+ * Return the inventory slot into which the item is placed.
+ */
+s16b inven_takeoff(int item, int amt, bool force_drop)
+{
+ int slot;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ cptr act;
+
+ char o_name[80];
+
+
+ /* Get the item to take off */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Paranoia */
+ if (amt <= 0) return ( -1);
+
+ /* Verify */
+ if (amt > o_ptr->number) amt = o_ptr->number;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Describe the object */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Took off weapon */
+ if (item == INVEN_WIELD)
+ {
+ act = "You were wielding";
+ }
+
+ /* Took off bow */
+ else if (item == INVEN_BOW)
+ {
+ act = "You were holding";
+ }
+
+ /* Took off light */
+ else if (item == INVEN_LITE)
+ {
+ act = "You were holding";
+ }
+
+ /* Took off ammo */
+ else if (item == INVEN_AMMO)
+ {
+ act = "You were carrying in your quiver";
+ }
+
+ /* Took off tool */
+ else if (item == INVEN_TOOL)
+ {
+ act = "You were using";
+ }
+
+ /* Took off something */
+ else
+ {
+ act = "You were wearing";
+ }
+
+ /* Modify, Optimize */
+ inven_item_increase(item, -amt);
+ inven_item_optimize(item);
+
+ if ((item == INVEN_CARRY) && (get_skill(SKILL_SYMBIOTIC)))
+ {
+ /* Drop the monster */
+ o_ptr->pval2 = 0;
+ msg_print("You carefully drop the poor monster on the floor.");
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+ slot = -1;
+ }
+ else if (force_drop)
+ {
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+ slot = -1;
+ }
+ else
+ {
+ /* Carry the object */
+ slot = inven_carry(q_ptr, FALSE);
+ }
+
+ /* Message */
+ msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
+
+ /* Return slot */
+ return (slot);
+}
+
+
+
+
+/*
+ * Drop (some of) a non-cursed inventory/equipment item
+ *
+ * The object will be dropped "near" the current location
+ */
+void inven_drop(int item, int amt, int dy, int dx, bool silent)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Access original object */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Error check */
+ if (amt <= 0) return;
+
+ /* Not too many */
+ if (amt > o_ptr->number) amt = o_ptr->number;
+
+
+ /* Take off equipment */
+ if (item >= INVEN_WIELD)
+ {
+ /* Take off first */
+ item = inven_takeoff(item, amt, FALSE);
+
+ /* Access original object */
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ if (item > -1)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ /*
+ * Hack -- If rods or wands are dropped, the total maximum timeout or
+ * charges need to be allocated between the two stacks. If all the items
+ * are being dropped, it makes for a neater message to leave the original
+ * stack's pval alone. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ if (amt < o_ptr->number) o_ptr->pval -= q_ptr->pval;
+ }
+ }
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Describe local object */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ if (!silent) msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it near the player */
+ drop_near(q_ptr, 0, dy, dx);
+
+ /* Modify, Describe, Optimize */
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+}
+
+
+
+/*
+ * Combine items in the pack
+ *
+ * Note special handling of the "overflow" slot
+ */
+void combine_pack(void)
+{
+ int i, j, k;
+ object_type *o_ptr;
+ object_type *j_ptr;
+ bool flag = FALSE;
+
+
+ /* Combine the pack (backwards) */
+ for (i = INVEN_PACK; i > 0; i--)
+ {
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty items */
+ if (!o_ptr->k_idx) continue;
+
+ /* Scan the items above that item */
+ for (j = 0; j < i; j++)
+ {
+ /* Get the item */
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Skip empty items */
+ if (!j_ptr->k_idx) continue;
+
+ /* Can we drop "o_ptr" onto "j_ptr"? */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Take note */
+ flag = TRUE;
+
+ /* Add together the item counts */
+ object_absorb(j_ptr, o_ptr);
+
+ /* One object is gone */
+ inven_cnt--;
+
+ /* Slide everything down */
+ for (k = i; k < INVEN_PACK; k++)
+ {
+ /* Structure copy */
+ p_ptr->inventory[k] = p_ptr->inventory[k + 1];
+ }
+
+ /* Erase the "final" slot */
+ object_wipe(&p_ptr->inventory[k]);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Message */
+ if (flag) msg_print("You combine some items in your pack.");
+}
+
+
+/*
+ * Reorder items in the pack
+ *
+ * Note special handling of the "overflow" slot
+ */
+void reorder_pack(void)
+{
+ int i, j, k;
+ s32b o_value;
+ s32b j_value;
+ object_type forge;
+ object_type *q_ptr;
+ object_type *j_ptr;
+ object_type *o_ptr;
+ bool flag = FALSE;
+
+
+ /* Re-order the pack (forwards) */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Mega-Hack -- allow "proper" over-flow */
+ if ((i == INVEN_PACK) && (inven_cnt == INVEN_PACK)) break;
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Get the "value" of the item */
+ o_value = object_value(o_ptr);
+
+ /* Scan every occupied slot */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ /* Get the item already there */
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use empty slots */
+ if (!j_ptr->k_idx) break;
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Non-aware (flavored) items always come last */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Unidentified objects always come last */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+
+ /* Hack: otherwise identical rods sort by
+ increasing recharge time --dsb */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout > j_ptr->timeout) break;
+ if (o_ptr->timeout < j_ptr->timeout) continue;
+ }
+
+ /* Determine the "value" of the pack item */
+ j_value = object_value(j_ptr);
+
+
+
+ /* Objects sort by decreasing value */
+ if (o_value > j_value) break;
+ if (o_value < j_value) continue;
+ }
+
+ /* Never move down */
+ if (j >= i) continue;
+
+ /* Take note */
+ flag = TRUE;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Save a copy of the moving item */
+ object_copy(q_ptr, &p_ptr->inventory[i]);
+
+ /* Slide the objects */
+ for (k = i; k > j; k--)
+ {
+ /* Slide the item */
+ object_copy(&p_ptr->inventory[k], &p_ptr->inventory[k - 1]);
+ }
+
+ /* Insert the moving item */
+ object_copy(&p_ptr->inventory[j], q_ptr);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* Message */
+ if (flag) msg_print("You reorder some items in your pack.");
+}
+
+/*
+ * Hack -- display an object kind in the current window
+ *
+ * Include list of usable spells for readible books
+ */
+void display_koff(int k_idx)
+{
+ int y;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Erase the window */
+ for (y = 0; y < Term->hgt; y++)
+ {
+ /* Erase the line */
+ Term_erase(0, y, 255);
+ }
+
+ /* No info */
+ if (!k_idx) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare the object */
+ object_wipe(q_ptr);
+
+ /* Prepare the object */
+ object_prep(q_ptr, k_idx);
+
+
+ /* Describe */
+ object_desc_store(o_name, q_ptr, FALSE, 0);
+
+ /* Mention the object name */
+ Term_putstr(0, 0, -1, TERM_WHITE, o_name);
+}
+
+
+/*
+ * Let the floor carry an object
+ */
+s16b floor_carry(int y, int x, object_type *j_ptr)
+{
+ int n = 0;
+
+ s16b o_idx;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Scan objects in that grid for combination */
+ for (this_o_idx = cave[y][x].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Check for combination */
+ if (object_similar(o_ptr, j_ptr))
+ {
+ /* Combine the items */
+ object_absorb(o_ptr, j_ptr);
+
+ /* Result */
+ return (this_o_idx);
+ }
+
+ /* Count objects */
+ n++;
+ }
+
+ /* The stack is already too large */
+ if (n > 23) return (0);
+
+ /* Make an object */
+ o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure Copy */
+ object_copy(o_ptr, j_ptr);
+
+ /* Location */
+ o_ptr->iy = y;
+ o_ptr->ix = x;
+
+ /* Forget monster */
+ o_ptr->held_m_idx = 0;
+
+ /* Build a stack */
+ o_ptr->next_o_idx = cave[y][x].o_idx;
+
+ /* Place the object */
+ cave[y][x].o_idx = o_idx;
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* Result */
+ return (o_idx);
+}
+
+/*
+ * Notice a decaying object in the pack
+ */
+void pack_decay(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int amt = o_ptr->number;
+
+ s16b m_type;
+ s32b wt;
+
+ byte known = o_ptr->name1;
+
+ byte gone = 1;
+
+ char desc[80];
+
+ /* Player notices each decaying object */
+ object_desc(desc, o_ptr, TRUE, 3);
+ msg_format("You feel %s decompose.", desc);
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object */
+ object_copy(i_ptr, o_ptr);
+
+ /* Remember what creature we were */
+ m_type = o_ptr->pval2;
+
+ /* and how much we weighed */
+ wt = r_ptr->weight;
+
+ /* Get rid of decayed object */
+ inven_item_increase(item, -amt);
+ inven_item_optimize(item);
+
+ if (i_ptr->tval == TV_CORPSE)
+ {
+ /* Monster must have a skull for its head to become one */
+ if (i_ptr->sval == SV_CORPSE_HEAD)
+ {
+ /* Replace the head with a skull */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKULL));
+ i_ptr->weight = wt / 60 + rand_int(wt) / 600;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Monster must have a skeleton for its corpse to become one */
+ if ((i_ptr->sval == SV_CORPSE_CORPSE) && (r_ptr->flags3 & RF9_DROP_SKELETON))
+ {
+ /* Replace the corpse with a skeleton */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+ i_ptr->weight = wt / 4 + rand_int(wt) / 40;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Don't restore if the item is gone */
+ if (!gone)
+ {
+ i_ptr->number = amt;
+ i_ptr->pval2 = m_type;
+
+ /* Should become "The skull of Farmer Maggot", not "A skull" */
+ if (known)
+ {
+ object_aware(i_ptr);
+
+ /* Named skeletons are artifacts */
+ i_ptr->name1 = 201;
+ }
+ inven_carry(i_ptr, TRUE);
+ }
+ }
+}
+
+/*
+ * Decay an object on the floor
+ */
+void floor_decay(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int amt = o_ptr->number;
+
+ s16b m_type;
+ s32b wt;
+
+ byte known = o_ptr->name1;
+
+ /* Assume we disappear */
+ byte gone = 1;
+
+ byte x = o_ptr->ix;
+ byte y = o_ptr->iy;
+
+ /* Maybe the player sees it */
+ bool visible = player_can_see_bold(o_ptr->iy, o_ptr->ix);
+ char desc[80];
+
+ if (visible)
+ {
+ /* Player notices each decaying object */
+ object_desc(desc, o_ptr, TRUE, 3);
+ msg_format("You see %s decompose.", desc);
+ }
+
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object */
+ object_copy(i_ptr, o_ptr);
+
+ /* Remember what creature we were */
+ m_type = o_ptr->pval2;
+
+ /* and how much we weighed */
+ wt = r_ptr->weight;
+
+ floor_item_increase(item, -amt);
+ floor_item_optimize(item);
+
+ if (i_ptr->tval == TV_CORPSE)
+ {
+ /* Monster must have a skull for its head to become one */
+ if (i_ptr->sval == SV_CORPSE_HEAD)
+ {
+ /* Replace the head with a skull */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKULL));
+ i_ptr->weight = wt / 60 + rand_int(wt) / 600;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Monster must have a skeleton for its corpse to become one */
+ if ((i_ptr->sval == SV_CORPSE_CORPSE) && (r_ptr->flags3 & RF9_DROP_SKELETON))
+ {
+ /* Replace the corpse with a skeleton */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+ i_ptr->weight = wt / 4 + rand_int(wt) / 40;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Don't restore if the item is gone */
+ if (!gone)
+ {
+ i_ptr->number = amt;
+ i_ptr->pval2 = m_type;
+
+ /* Should become "The skull of Farmer Maggot", not "A skull" */
+ if (known)
+ {
+ object_aware(i_ptr);
+
+ /* Named skeletons are artifacts */
+ i_ptr->name1 = 201;
+ }
+ floor_carry(y, x, i_ptr);
+ }
+ }
+}
+
+/* Return the item be it on the floor or in inven */
+object_type *get_object(int item)
+{
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ return &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ return &o_list[0 - item];
+ }
+}
+
diff --git a/src/player.pkg b/src/player.pkg
new file mode 100644
index 00000000..f6717476
--- /dev/null
+++ b/src/player.pkg
@@ -0,0 +1,3525 @@
+/* File: player.pkg */
+
+/*
+ * Purpose: Lua interface defitions for the player.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @def PY_MAX_LEVEL
+ * @note Maximum level
+ */
+#define PY_MAX_LEVEL 50
+
+/** @var player_exp[PY_MAX_LEVEL]
+ * @brief Number
+ * @note Array of experience points per level.
+ */
+extern s32b player_exp[PY_MAX_LEVEL];
+
+/** @name Attributes
+ * @brief Indexes of the various "stats" (hard-coded by savefiles, etc).
+ * @{ */
+/** @def A_STR
+ * @note Strength */
+#define A_STR 0
+
+/** @def A_INT
+ * @note Intelligence */
+#define A_INT 1
+
+/** @def A_WIS
+ * @note Wisdom */
+#define A_WIS 2
+
+/** @def A_DEX
+ * @note Dexterity */
+#define A_DEX 3
+
+/** @def A_CON
+ * @note Constitution */
+#define A_CON 4
+
+/** @def A_CHR
+ * @note Charisma */
+#define A_CHR 5
+/** @} */
+
+/* Ugly hack, should be in foo-info, the subrace saved to the savefile */
+/** @def SUBRACE_SAVE */
+#define SUBRACE_SAVE 9
+
+
+/** @name Sex
+ * @brief Player sex constants (hard-coded by save-files, arrays, etc)
+ * @{ */
+/** @def SEX_FEMALE */
+#define SEX_FEMALE 0
+
+/** @def SEX_MALE */
+#define SEX_MALE 1
+
+/** @def SEX_NEUTER */
+#define SEX_NEUTER 2
+
+
+/** @def MAX_SEXES */
+#define MAX_SEXES 3
+/** @} */
+
+/** @name Race flags
+ * @{ */
+/** @def PR1_EXPERIMENTAL
+ * @note Is still under developemnt
+ */
+#define PR1_EXPERIMENTAL 0x00000001L
+/* XXX */
+/** @def PR1_RESIST_BLACK_BREATH
+ * @note Resist black breath
+ */
+#define PR1_RESIST_BLACK_BREATH 0x00000004L
+/** @def PR1_NO_STUN
+ * @note Never stunned
+ */
+#define PR1_NO_STUN 0x00000008L
+/** @def PR1_XTRA_MIGHT_BOW
+ * @note Xtra might with bows
+ */
+#define PR1_XTRA_MIGHT_BOW 0x00000010L
+/** @def PR1_XTRA_MIGHT_XBOW
+ * @note Xtra might with xbows
+ */
+#define PR1_XTRA_MIGHT_XBOW 0x00000020L
+/** @def PR1_XTRA_MIGHT_SLING
+ * @note Xtra might with slings
+ */
+#define PR1_XTRA_MIGHT_SLING 0x00000040L
+/** @def PR1_AC_LEVEL
+ * @note More AC with levels
+ */
+#define PR1_AC_LEVEL 0x00000080L
+/** @def PR1_HURT_LITE
+ * @note Hurt by light
+ */
+#define PR1_HURT_LITE 0x00000100L
+/** @def PR1_VAMPIRE
+ * @note Vampire
+ */
+#define PR1_VAMPIRE 0x00000200L
+/** @def PR1_UNDEAD
+ * @note Undead
+ */
+#define PR1_UNDEAD 0x00000400L
+/** @def PR1_NO_CUT
+ * @note no cuts
+ */
+#define PR1_NO_CUT 0x00000800L
+/** @def PR1_CORRUPT
+ * @note hack-- corrupted
+ */
+#define PR1_CORRUPT 0x00001000L
+/** @def PR1_NO_FOOD
+ * @note little gain from food
+ */
+#define PR1_NO_FOOD 0x00002000L
+/** @def PR1_NO_GOD
+ * @note cannot worship
+ */
+#define PR1_NO_GOD 0x00004000L
+/* XXX */
+/** @def PR1_ELF
+ * @note Is an elf
+ */
+#define PR1_ELF 0x00010000L
+/** @def PR1_SEMI_WRAITH
+ * @note Takes damage when going in walls
+ */
+#define PR1_SEMI_WRAITH 0x00020000L
+/** @def PR1_NO_SUBRACE_CHANGE
+ * @note Impossible to change subrace
+ */
+#define PR1_NO_SUBRACE_CHANGE 0x00040000L
+/* XXX */
+/** @def PR1_ANTIMAGIC
+ * @note antimagic ... hack
+ */
+#define PR1_ANTIMAGIC 0x00100000L
+/** @def PR1_MOLD_FRIEND
+ * @note Not attacked by molds wielded
+ */
+#define PR1_MOLD_FRIEND 0x00200000L
+/** @def PR1_GOD_FRIEND
+ * @note Better grace
+ */
+#define PR1_GOD_FRIEND 0x00400000L
+/* XXX */
+/** @def PR1_INNATE_SPELLS
+ * @note KNown all spells, only need books
+ */
+#define PR1_INNATE_SPELLS 0x01000000L
+/* XXX */
+/* XXX */
+/** @def PR1_EASE_STEAL
+ * @note Gain xp by stealing
+ */
+#define PR1_EASE_STEAL 0x08000000L
+/* XXX */
+/* XXX */
+/* XXX */
+/* XXX */
+
+/* XXX */
+/** @def PR2_ASTRAL
+ * @note Is it an astral being coming from th halls of mandos ?
+ */
+#define PR2_ASTRAL 0x00000002L
+/* XXX */
+/** @} */
+
+/** @name Notice flags
+ * @brief Bit flags for the "p_ptr->notice" variable
+ * @{ */
+/** @def PN_COMBINE
+ * @note Combine the pack
+ */
+#define PN_COMBINE 0x00000001L
+/** @def PN_REORDER
+ * @note Reorder the pack
+ */
+#define PN_REORDER 0x00000002L
+/* xxx (many) */
+/** @} */
+
+
+/** @name Update flags
+ * @brief Bit flags for the "p_ptr->update" variable
+ * @{ */
+/** @def PU_BONUS
+ * @note Calculate bonuses
+ */
+#define PU_BONUS 0x00000001L
+/** @def PU_TORCH
+ * @note Calculate torch radius
+ */
+#define PU_TORCH 0x00000002L
+/** @def PU_BODY
+ * @note Calculate body parts
+ */
+#define PU_BODY 0x00000004L
+/** @def PU_SANITY
+ * @note Calculate csan and msan
+ */
+#define PU_SANITY 0x00000008L
+/** @def PU_HP
+ * @note Calculate chp and mhp
+ */
+#define PU_HP 0x00000010L
+/** @def PU_MANA
+ * @note Calculate csp and msp
+ */
+#define PU_MANA 0x00000020L
+/** @def PU_SPELLS
+ * @note Calculate spells
+ */
+#define PU_SPELLS 0x00000040L
+/** @def PU_POWERS
+ * @note Calculate powers
+ */
+#define PU_POWERS 0x00000080L
+/* xxx (many) */
+/** @def PU_UN_VIEW
+ * @note Forget view
+ */
+#define PU_UN_VIEW 0x00010000L
+/* xxx (many) */
+/** @def PU_VIEW
+ * @note Update view
+ */
+#define PU_VIEW 0x00100000L
+/** @def PU_MON_LITE
+ * @note Update monster light
+ */
+#define PU_MON_LITE 0x00200000L
+/* xxx */
+/** @def PU_MONSTERS
+ * @note Update monsters
+ */
+#define PU_MONSTERS 0x01000000L
+/** @def PU_DISTANCE
+ * @note Update distances
+ */
+#define PU_DISTANCE 0x02000000L
+/* xxx */
+/** @def PU_FLOW
+ * @note Update flow
+ */
+#define PU_FLOW 0x10000000L
+/* xxx (many) */
+/** @} */
+
+
+/** @name Redraw flags
+ * @brief Bit flags for the "p_ptr->redraw" variable
+ * @{ */
+/** @def PR_MISC
+ * @note Display Race/Class
+ */
+#define PR_MISC 0x00000001L
+/** @def PR_TITLE
+ * @note Display Title
+ */
+#define PR_TITLE 0x00000002L
+/** @def PR_LEV
+ * @note Display Level
+ */
+#define PR_LEV 0x00000004L
+/** @def PR_EXP
+ * @note Display Experience
+ */
+#define PR_EXP 0x00000008L
+/** @def PR_STATS
+ * @note Display Stats
+ */
+#define PR_STATS 0x00000010L
+/** @def PR_ARMOR
+ * @note Display Armor
+ */
+#define PR_ARMOR 0x00000020L
+/** @def PR_HP
+ * @note Display Hitpoints
+ */
+#define PR_HP 0x00000040L
+/** @def PR_MANA
+ * @note Display Mana
+ */
+#define PR_MANA 0x00000080L
+/** @def PR_GOLD
+ * @note Display Gold
+ */
+#define PR_GOLD 0x00000100L
+/** @def PR_DEPTH
+ * @note Display Depth
+ */
+#define PR_DEPTH 0x00000200L
+/****/
+/** @def PR_HEALTH
+ * @note Display Health Bar
+ */
+#define PR_HEALTH 0x00000800L
+/** @def PR_CUT
+ * @note Display Extra (Cut)
+ */
+#define PR_CUT 0x00001000L
+/** @def PR_STUN
+ * @note Display Extra (Stun)
+ */
+#define PR_STUN 0x00002000L
+/** @def PR_HUNGER
+ * @note Display Extra (Hunger)
+ */
+#define PR_HUNGER 0x00004000L
+/** @def PR_PIETY
+ * @note Display Piety
+ */
+#define PR_PIETY 0x00008000L
+/** @def PR_BLIND
+ * @note Display Extra (Blind)
+ */
+#define PR_BLIND 0x00010000L
+/** @def PR_CONFUSED
+ * @note Display Extra (Confused)
+ */
+#define PR_CONFUSED 0x00020000L
+/** @def PR_AFRAID
+ * @note Display Extra (Afraid)
+ */
+#define PR_AFRAID 0x00040000L
+/** @def PR_POISONED
+ * @note Display Extra (Poisoned)
+ */
+#define PR_POISONED 0x00080000L
+/** @def PR_STATE
+ * @note Display Extra (State)
+ */
+#define PR_STATE 0x00100000L
+/** @def PR_SPEED
+ * @note Display Extra (Speed)
+ */
+#define PR_SPEED 0x00200000L
+/** @def PR_STUDY
+ * @note Display Extra (Study)
+ */
+#define PR_STUDY 0x00400000L
+/** @def PR_SANITY
+ * @note Display Sanity
+ */
+#define PR_SANITY 0x00800000L
+/** @def PR_EXTRA
+ * @note Display Extra Info
+ */
+#define PR_EXTRA 0x01000000L
+/** @def PR_BASIC
+ * @note Display Basic Info
+ */
+#define PR_BASIC 0x02000000L
+/** @def PR_MAP
+ * @note Display Map
+ */
+#define PR_MAP 0x04000000L
+/** @def PR_WIPE
+ * @note Hack -- Total Redraw
+ */
+#define PR_WIPE 0x08000000L
+/** @def PR_MH
+ * @note Display Monster hitpoints
+ */
+#define PR_MH 0x10000000L
+/** @def PR_MH
+ * @note Display Monster hitpoints
+ */
+#define PR_MH 0x10000000L
+/** @def PR_DTRAP
+ * @note Display Extra (DTrap)
+ */
+#define PR_DTRAP 0x20000000L
+/* xxx */
+/* xxx */
+/** @} */
+
+
+/** @name Window flags
+ * @brief Bit flags for the "p_ptr->window" variable (etc)
+ * @{ */
+/** @def PW_INVEN
+ * @note Display inven/equip
+ */
+#define PW_INVEN 0x00000001L
+/** @def PW_EQUIP
+ * @note Display equip/inven
+ */
+#define PW_EQUIP 0x00000002L
+/* xxx */
+/** @def PW_PLAYER
+ * @note Display character
+ */
+#define PW_PLAYER 0x00000008L
+/** @def PW_M_LIST
+ * @note Show monster list
+ */
+#define PW_M_LIST 0x00000010L
+/* xxx */
+/** @def PW_MESSAGE
+ * @note Display messages
+ */
+#define PW_MESSAGE 0x00000040L
+/** @def PW_OVERHEAD
+ * @note Display overhead view
+ */
+#define PW_OVERHEAD 0x00000080L
+/** @def PW_MONSTER
+ * @note Display monster recall
+ */
+#define PW_MONSTER 0x00000100L
+/** @def PW_OBJECT
+ * @note Display object recall
+ */
+#define PW_OBJECT 0x00000200L
+/* xxx */
+/** @def PW_SNAPSHOT
+ * @note Display snap-shot
+ */
+#define PW_SNAPSHOT 0x00000800L
+/* xxx */
+/* xxx */
+/** @def PW_BORG_1
+ * @note Display borg messages
+ */
+#define PW_BORG_1 0x00004000L
+/** @def PW_BORG_2
+ * @note Display borg status
+ */
+#define PW_BORG_2 0x00008000L
+/** @} */
+
+/** @struct deity_type
+ */
+struct deity_type
+{
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+};
+/** @var deity_info[max_gods]
+ * @brief deity_type
+ * @note Array of gods.
+ */
+extern deity_type deity_info[max_gods];
+
+/** @name Body parts
+ * @{ */
+/** @def BODY_WEAPON */
+#define BODY_WEAPON 0
+
+/** @def BODY_TORSO */
+#define BODY_TORSO 1
+
+/** @def BODY_ARMS */
+#define BODY_ARMS 2
+
+/** @def BODY_FINGER */
+#define BODY_FINGER 3
+
+/** @def BODY_HEAD */
+#define BODY_HEAD 4
+
+/** @def BODY_LEGS */
+#define BODY_LEGS 5
+
+/** @def BODY_MAX */
+#define BODY_MAX 6
+/** @} */
+
+
+/** @struct player_type
+ */
+struct player_type
+{
+ /** @structvar lives
+ * @brief Number
+ * @note How many times we resurected
+ */
+ s32b lives;
+
+ /** @structvar oldpy
+ * @brief Number
+ * @note Previous player location -KMW-
+ */
+ s16b oldpy;
+ /** @structvar oldpx
+ * @brief Number
+ * @note Previous player location -KMW-
+ */
+ s16b oldpx;
+
+ /** @structvar py
+ * @brief Number
+ * @note Player location
+ */
+ s16b py;
+ /** @structvar px
+ * @brief Number
+ * @note Player location
+ */
+ s16b px;
+
+ /** @structvar psex
+ * @brief Number
+ * @note Sex index
+ */
+ byte psex;
+ /** @structvar prace
+ * @brief Number
+ * @note Race index
+ */
+ byte prace;
+ /** @structvar pracem
+ * @brief Number
+ * @note Race Mod index
+ */
+ byte pracem;
+ /** @structvar pclass
+ * @brief Number
+ * @note Class index
+ */
+ byte pclass;
+ /** @structvar mimic_form
+ * @brief Number
+ * @note Actualy transformation
+ */
+ byte mimic_form;
+ /** @structvar mimic_level
+ * @brief Number
+ * @note Level of the mimic effect
+ */
+ s16b mimic_level;
+ /** @structvar oops
+ * @brief Number
+ * @note Unused
+ */
+ byte oops;
+
+ object_type inventory[INVEN_TOTAL] @inventory_real;
+
+ /** @structvar hitdie
+ * @brief Number
+ * @note Hit dice (sides)
+ */
+ byte hitdie;
+ /** @structvar expfact
+ * @brief Number
+ * @note Experience factor
+ */
+ u16b expfact;
+
+ /** @structvar allow_one_death
+ * @brief Number
+ * @note Blood of life
+ */
+ byte allow_one_death;
+
+ /** @structvar age
+ * @brief Number
+ * @note Characters age
+ */
+ s16b age;
+ /** @structvar ht
+ * @brief Number
+ * @note Height
+ */
+ s16b ht;
+ /** @structvar wt
+ * @brief Number
+ * @note Weight
+ */
+ s16b wt;
+ /** @structvar sc
+ * @brief Number
+ * @note Social Class
+ */
+ s16b sc;
+
+
+ /** @structvar au
+ * @brief Number
+ * @note Current Gold
+ */
+ s32b au;
+
+ /** @structvar max_exp
+ * @brief Number
+ * @note Max experience
+ */
+ s32b max_exp;
+ /** @structvar exp
+ * @brief Number
+ * @note Cur experience
+ */
+ s32b exp;
+ /** @structvar exp_frac
+ * @brief Number
+ * @note Cur exp frac (times 2^16)
+ */
+ u16b exp_frac;
+
+ /** @structvar lev
+ * @brief Number
+ * @note Level
+ */
+ s16b lev;
+
+ /** @structvar town_num
+ * @brief Number
+ * @note Current town number
+ */
+ s16b town_num;
+ /** @structvar inside_quest
+ * @brief Number
+ * @note Inside quest level
+ */
+ s16b inside_quest;
+ /** @structvar exit_bldg
+ * @brief Boolean
+ * @note Goal obtained in arena? -KMW-
+ */
+ bool exit_bldg;
+
+ /** @structvar wilderness_x
+ * @brief Number
+ * @note Coordinates in the wilderness
+ */
+ s32b wilderness_x;
+ /** @structvar wilderness_y
+ * @brief Number
+ */
+ s32b wilderness_y;
+ /** @structvar wild_mode
+ * @brief Boolean
+ * @note TRUE = Small map, FLASE = Big map
+ */
+ bool wild_mode;
+ /** @structvar old_wild_mode
+ * @brief Boolean
+ * @note TRUE = Small map, FLASE = Big map
+ */
+ bool old_wild_mode;
+
+ /** @structvar mhp
+ * @brief Number
+ * @note Max hit pts
+ */
+ s16b mhp;
+ /** @structvar chp
+ * @brief Number
+ * @note Cur hit pts
+ */
+ s16b chp;
+ /** @structvar chp_frac
+ * @brief Number
+ * @note Cur hit frac (times 2^16)
+ */
+ u16b chp_frac;
+ /** @structvar hp_mod
+ * @brief Number
+ * @note A modificator(permanent)
+ */
+ s16b hp_mod;
+
+ /** @structvar msp
+ * @brief Number
+ * @note Max mana pts
+ */
+ s16b msp;
+ /** @structvar csp
+ * @brief Number
+ * @note Cur mana pts
+ */
+ s16b csp;
+ /** @structvar csp_frac
+ * @brief Number
+ * @note Cur mana frac (times 2^16)
+ */
+ u16b csp_frac;
+
+ /** @structvar msane
+ * @brief Number
+ * @note Max sanity
+ */
+ s16b msane;
+ /** @structvar csane
+ * @brief Number
+ * @note Cur sanity
+ */
+ s16b csane;
+ /** @structvar csane_frac
+ * @brief Number
+ * @note Cur sanity frac
+ */
+ u16b csane_frac;
+
+ /** @structvar grace
+ * @brief Number
+ * @note Your God's appreciation factor.
+ */
+ s32b grace;
+ /** @structvar pgod
+ * @brief Number
+ * @note Your God.
+ */
+ byte pgod;
+ /** @structvar praying
+ * @brief Boolean
+ * @note Praying to your god.
+ */
+ bool praying;
+
+ /** @structvar max_plv
+ * @brief Number
+ * @note Max Player Level
+ */
+ s16b max_plv;
+
+ /** @structvar stat_max[6]
+ * @brief Number
+ * @note Current "maximal" stat values
+ */
+ s16b stat_max[6];
+ /** @structvar stat_cur[6]
+ * @brief Number
+ * @note Current "natural" stat values
+ */
+ s16b stat_cur[6];
+
+ /** @structvar luck_cur
+ * @brief Number
+ * @note Current "natural" luck value (range -30 <> 30)
+ */
+ s16b luck_cur;
+ /** @structvar luck_max
+ * @brief Number
+ * @note Current "maximal base" luck value (range -30 <> 30)
+ */
+ s16b luck_max;
+ /** @structvar luck_base
+ * @brief Number
+ * @note Current "base" luck value (range -30 <> 30)
+ */
+ s16b luck_base;
+
+ /** @structvar fast
+ * @brief Number
+ * @note Timed -- Fast
+ */
+ s16b fast;
+ /** @structvar lightspeed
+ * @brief Number
+ * @note Timed -- Light Speed
+ */
+ s16b lightspeed;
+ /** @structvar slow
+ * @brief Number
+ * @note Timed -- Slow
+ */
+ s16b slow;
+ /** @structvar blind
+ * @brief Number
+ * @note Timed -- Blindness
+ */
+ s16b blind;
+ /** @structvar paralyzed
+ * @brief Number
+ * @note Timed -- Paralysis
+ */
+ s16b paralyzed;
+ /** @structvar confused
+ * @brief Number
+ * @note Timed -- Confusion
+ */
+ s16b confused;
+ /** @structvar afraid
+ * @brief Number
+ * @note Timed -- Fear
+ */
+ s16b afraid;
+ /** @structvar image
+ * @brief Number
+ * @note Timed -- Hallucination
+ */
+ s16b image;
+ /** @structvar poisoned
+ * @brief Number
+ * @note Timed -- Poisoned
+ */
+ s16b poisoned;
+ /** @structvar cut
+ * @brief Number
+ * @note Timed -- Cut
+ */
+ s16b cut;
+ /** @structvar stun
+ * @brief Number
+ * @note Timed -- Stun
+ */
+ s16b stun;
+
+ /** @structvar protevil
+ * @brief Number
+ * @note Timed -- Protection from Evil
+ */
+ s16b protevil;
+ /** @structvar protgood
+ * @brief Number
+ * @note Timed -- Protection from Good
+ */
+ s16b protgood;
+ /** @structvar protundead
+ * @brief Number
+ * @note Timed -- Protection from Undead
+ */
+ s16b protundead;
+ /** @structvar invuln
+ * @brief Number
+ * @note Timed -- Invulnerable
+ */
+ s16b invuln;
+ /** @structvar hero
+ * @brief Number
+ * @note Timed -- Heroism
+ */
+ s16b hero;
+ /** @structvar shero
+ * @brief Number
+ * @note Timed -- Super Heroism
+ */
+ s16b shero;
+ /** @structvar shield
+ * @brief Number
+ * @note Timed -- Shield Spell
+ */
+ s16b shield;
+ /** @structvar shield_power
+ * @brief Number
+ * @note Timed -- Shield Spell Power
+ */
+ s16b shield_power;
+ /** @structvar shield_opt
+ * @brief Number
+ * @note Timed -- Shield Spell options
+ */
+ s16b shield_opt;
+ /** @structvar blessed
+ * @brief Number
+ * @note Timed -- Blessed
+ */
+ s16b blessed;
+ /** @structvar tim_invis
+ * @brief Number
+ * @note Timed -- See Invisible
+ */
+ s16b tim_invis;
+ /** @structvar tim_infra
+ * @brief Number
+ * @note Timed -- Infra Vision
+ */
+ s16b tim_infra;
+
+ /** @structvar oppose_acid
+ * @brief Number
+ * @note Timed -- oppose acid
+ */
+ s16b oppose_acid;
+ /** @structvar oppose_elec
+ * @brief Number
+ * @note Timed -- oppose lightning
+ */
+ s16b oppose_elec;
+ /** @structvar oppose_fire
+ * @brief Number
+ * @note Timed -- oppose heat
+ */
+ s16b oppose_fire;
+ /** @structvar oppose_cold
+ * @brief Number
+ * @note Timed -- oppose cold
+ */
+ s16b oppose_cold;
+ /** @structvar oppose_pois
+ * @brief Number
+ * @note Timed -- oppose poison
+ */
+ s16b oppose_pois;
+ /** @structvar oppose_ld
+ * @brief Number
+ * @note Timed -- oppose light & dark
+ */
+ s16b oppose_ld;
+ /** @structvar oppose_cc
+ * @brief Number
+ * @note Timed -- oppose chaos & confusion
+ */
+ s16b oppose_cc;
+ /** @structvar oppose_ss
+ * @brief Number
+ * @note Timed -- oppose sound & shards
+ */
+ s16b oppose_ss;
+ /** @structvar oppose_nex
+ * @brief Number
+ * @note Timed -- oppose nexus
+ */
+ s16b oppose_nex;
+
+
+ /** @structvar tim_esp
+ * @brief Number
+ * @note Timed ESP
+ */
+ s16b tim_esp;
+ /** @structvar tim_wraith
+ * @brief Number
+ * @note Timed wraithform
+ */
+ s16b tim_wraith;
+ /** @structvar tim_ffall
+ * @brief Number
+ * @note Timed Levitation
+ */
+ s16b tim_ffall;
+ /** @structvar tim_fly
+ * @brief Number
+ * @note Timed Levitation
+ */
+ s16b tim_fly;
+ /** @structvar tim_fire_aura
+ * @brief Number
+ * @note Timed Fire Aura
+ */
+ s16b tim_fire_aura;
+ /** @structvar tim_regen
+ * @brief Number
+ * @note Timed regen
+ */
+ s16b tim_regen;
+ /** @structvar tim_regen_pow
+ * @brief Number
+ * @note Timed regen
+ */
+ s16b tim_regen_pow;
+ /** @structvar tim_poison
+ * @brief Number
+ * @note Timed poison hands
+ */
+ s16b tim_poison;
+ /** @structvar tim_thunder
+ * @brief Number
+ * @note Timed thunderstorm
+ */
+ s16b tim_thunder;
+ /** @structvar tim_thunder_p1
+ * @brief Number
+ * @note Timed thunderstorm
+ */
+ s16b tim_thunder_p1;
+ /** @structvar tim_thunder_p2
+ * @brief Number
+ * @note Timed thunderstorm
+ */
+ s16b tim_thunder_p2;
+
+ /** @structvar resist_magic
+ * @brief Number
+ * @note Timed Resist Magic (later)
+ */
+ s16b resist_magic;
+ /** @structvar tim_invisible
+ * @brief Number
+ * @note Timed Invisibility
+ */
+ s16b tim_invisible;
+ /** @structvar tim_inv_pow
+ * @brief Number
+ * @note Power of timed invisibility
+ */
+ s16b tim_inv_pow;
+ /** @structvar tim_mimic
+ * @brief Number
+ * @note Timed Mimic
+ */
+ s16b tim_mimic;
+ /** @structvar tim_lite
+ * @brief Number
+ * @note Timed Lite
+ */
+ s16b tim_lite;
+ /** @structvar holy
+ * @brief Number
+ * @note Holy Aura
+ */
+ s16b holy;
+ /** @structvar walk_water
+ * @brief Number
+ * @note Walk over water as a god
+ */
+ s16b walk_water;
+ /** @structvar tim_mental_barrier
+ * @brief Number
+ * @note Sustain Int&Wis
+ */
+ s16b tim_mental_barrier;
+ /** @structvar strike
+ * @brief Number
+ * @note True Strike(+25 hit)
+ */
+ s16b strike;
+ /** @structvar meditation
+ * @brief Number
+ * @note Meditation(+50 mana -25 to hit/to dam)
+ */
+ s16b meditation;
+ /** @structvar tim_reflect
+ * @brief Number
+ * @note Timed Reflection
+ */
+ s16b tim_reflect;
+ /** @structvar tim_res_time
+ * @brief Number
+ * @note Timed Resistance to Time
+ */
+ s16b tim_res_time;
+ /** @structvar tim_deadly
+ * @brief Number
+ * @note Timed deadly blow
+ */
+ s16b tim_deadly;
+ /** @structvar prob_travel
+ * @brief Number
+ * @note Timed probability travel
+ */
+ s16b prob_travel;
+ /** @structvar disrupt_shield
+ * @brief Number
+ * @note Timed disruption shield
+ */
+ s16b disrupt_shield;
+ /** @structvar parasite
+ * @brief Number
+ * @note Timed parasite
+ */
+ s16b parasite;
+ /** @structvar parasite_r_idx
+ * @brief Number
+ * @note Timed parasite monster
+ */
+ s16b parasite_r_idx;
+ /** @structvar loan
+ * @brief Number
+ */
+ u32b loan;
+ /** @structvar loan_time
+ * @brief Number
+ * @note Timer -- loan
+ */
+ u32b loan_time;
+ /** @structvar tim_magic_breath
+ * @brief Number
+ * @note Magical breathing -- can breath anywhere
+ */
+ s16b tim_magic_breath;
+ /** @structvar tim_water_breath
+ * @brief Number
+ * @note Water breathing -- can breath underwater
+ */
+ s16b tim_water_breath;
+
+ /** @structvar immov_cntr
+ * @brief Number
+ * @note Timed -- Last ``immovable'' command.
+ */
+ s16b immov_cntr;
+
+ /** @structvar music_extra
+ * @brief Number
+ * @note Music songs
+ */
+ u32b music_extra;
+ /** @structvar music_extra2
+ * @brief Number
+ * @note Music songs
+ */
+ u32b music_extra2;
+
+ /** @structvar chaos_patron
+ * @brief Number
+ */
+ s16b chaos_patron;
+
+ /** @structvar recall_dungeon
+ * @brief Number
+ * @note Recall in which dungeon
+ */
+ s16b recall_dungeon;
+ /** @structvar word_recall
+ * @brief Number
+ * @note Word of recall counter
+ */
+ s16b word_recall;
+
+ /** @structvar energy
+ * @brief Number
+ * @note Current energy
+ */
+ s32b energy;
+
+ /** @structvar food
+ * @brief Number
+ * @note Current nutrition
+ */
+ s16b food;
+
+ /** @structvar confusing
+ * @brief Number
+ * @note Glowing hands
+ */
+ byte confusing;
+ /** @structvar searching
+ * @brief Number
+ * @note Currently searching
+ */
+ byte searching;
+
+ /** @structvar new_spells
+ * @brief Number
+ * @note Number of spells available
+ */
+ s16b new_spells;
+
+ /** @structvar old_spells
+ * @brief Number
+ */
+ s16b old_spells;
+
+ /** @structvar xtra_spells
+ * @brief Number
+ * @note Number of xtra spell learned(via potion)
+ */
+ s16b xtra_spells;
+
+ /** @structvar cur_lite
+ * @brief Number
+ * @note Radius of lite (if any)
+ */
+ s16b cur_lite;
+
+ /*** Extra flags -- used for lua and easying stuff ***/
+ /** @structvar xtra_f1
+ * @brief Number
+ */
+ u32b xtra_f1;
+ /** @structvar xtra_f2
+ * @brief Number
+ */
+ u32b xtra_f2;
+ /** @structvar xtra_f3
+ * @brief Number
+ */
+ u32b xtra_f3;
+ /** @structvar xtra_f4
+ * @brief Number
+ */
+ u32b xtra_f4;
+ /** @structvar xtra_f5
+ * @brief Number
+ */
+ u32b xtra_f5;
+ /** @structvar xtra_esp
+ * @brief Number
+ */
+ u32b xtra_esp;
+
+ /** @structvar pspeed
+ * @brief Number
+ * @note Current speed
+ */
+ s16b pspeed;
+
+ /** @structvar notice
+ * @brief Number
+ * @note Special Updates (bit flags)
+ */
+ u32b notice;
+ /** @structvar update
+ * @brief Number
+ * @note Pending Updates (bit flags)
+ */
+ u32b update;
+ /** @structvar redraw
+ * @brief Number
+ * @note Normal Redraws (bit flags)
+ */
+ u32b redraw;
+ /** @structvar window
+ * @brief Number
+ * @note Window Redraws (bit flags)
+ */
+ u32b window;
+
+ /** @structvar stat_use[6]
+ * @brief Number
+ * @note Current modified stats
+ */
+ s16b stat_use[6];
+ /** @structvar stat_top[6]
+ * @brief Number
+ * @note Maximal modified stats
+ */
+ s16b stat_top[6];
+
+ /** @structvar stat_add[6]
+ * @brief Number
+ * @note Modifiers to stat values
+ */
+ s16b stat_add[6];
+ /** @structvar stat_ind[6]
+ * @brief Number
+ * @note Indexes into stat tables
+ */
+ s16b stat_ind[6];
+ /** @structvar stat_cnt[6]
+ * @brief Number
+ * @note Counter for temporary drains
+ */
+ s16b stat_cnt[6];
+ /** @structvar stat_los[6]
+ * @brief Number
+ * @note Amount of temporary drains
+ */
+ s16b stat_los[6];
+
+ /** @structvar immune_acid
+ * @brief Boolean
+ * @note Immunity to acid
+ */
+ bool immune_acid;
+ /** @structvar immune_elec
+ * @brief Boolean
+ * @note Immunity to lightning
+ */
+ bool immune_elec;
+ /** @structvar immune_fire
+ * @brief Boolean
+ * @note Immunity to fire
+ */
+ bool immune_fire;
+ /** @structvar immune_cold
+ * @brief Boolean
+ * @note Immunity to cold
+ */
+ bool immune_cold;
+ /** @structvar immune_neth
+ * @brief Boolean
+ * @note Immunity to nether
+ */
+ bool immune_neth;
+
+ /** @structvar resist_acid
+ * @brief Boolean
+ * @note Resist acid
+ */
+ bool resist_acid;
+ /** @structvar resist_elec
+ * @brief Boolean
+ * @note Resist lightning
+ */
+ bool resist_elec;
+ /** @structvar resist_fire
+ * @brief Boolean
+ * @note Resist fire
+ */
+ bool resist_fire;
+ /** @structvar resist_cold
+ * @brief Boolean
+ * @note Resist cold
+ */
+ bool resist_cold;
+ /** @structvar resist_pois
+ * @brief Boolean
+ * @note Resist poison
+ */
+ bool resist_pois;
+
+ /** @structvar resist_conf
+ * @brief Boolean
+ * @note Resist confusion
+ */
+ bool resist_conf;
+ /** @structvar resist_sound
+ * @brief Boolean
+ * @note Resist sound
+ */
+ bool resist_sound;
+ /** @structvar resist_lite
+ * @brief Boolean
+ * @note Resist light
+ */
+ bool resist_lite;
+ /** @structvar resist_dark
+ * @brief Boolean
+ * @note Resist darkness
+ */
+ bool resist_dark;
+ /** @structvar resist_chaos
+ * @brief Boolean
+ * @note Resist chaos
+ */
+ bool resist_chaos;
+ /** @structvar resist_disen
+ * @brief Boolean
+ * @note Resist disenchant
+ */
+ bool resist_disen;
+ /** @structvar resist_shard
+ * @brief Boolean
+ * @note Resist shards
+ */
+ bool resist_shard;
+ /** @structvar resist_nexus
+ * @brief Boolean
+ * @note Resist nexus
+ */
+ bool resist_nexus;
+ /** @structvar resist_blind
+ * @brief Boolean
+ * @note Resist blindness
+ */
+ bool resist_blind;
+ /** @structvar resist_neth
+ * @brief Boolean
+ * @note Resist nether
+ */
+ bool resist_neth;
+ /** @structvar resist_fear
+ * @brief Boolean
+ * @note Resist fear
+ */
+ bool resist_fear;
+ /** @structvar resist_continuum
+ * @brief Boolean
+ * @note Resist space-time continuum disruption
+ */
+ bool resist_continuum;
+
+ /** @structvar sensible_fire
+ * @brief Boolean
+ * @note Fire does more damage on the player
+ */
+ bool sensible_fire;
+ /** @structvar sensible_lite
+ * @brief Boolean
+ * @note Lite does more damage on the player and blinds her/him
+ */
+ bool sensible_lite;
+
+ /** @structvar reflect
+ * @brief Boolean
+ * @note Reflect 'bolt' attacks
+ */
+ bool reflect;
+ /** @structvar sh_fire
+ * @brief Boolean
+ * @note Fiery 'immolation' effect
+ */
+ bool sh_fire;
+ /** @structvar sh_elec
+ * @brief Boolean
+ * @note Electric 'immolation' effect
+ */
+ bool sh_elec;
+ /** @structvar wraith_form
+ * @brief Boolean
+ * @note wraithform
+ */
+ bool wraith_form;
+
+ /** @structvar anti_magic
+ * @brief Boolean
+ * @note Anti-magic
+ */
+ bool anti_magic;
+ /** @structvar anti_tele
+ * @brief Boolean
+ * @note Prevent teleportation
+ */
+ bool anti_tele;
+
+ /** @structvar sustain_str
+ * @brief Boolean
+ * @note Keep strength
+ */
+ bool sustain_str;
+ /** @structvar sustain_int
+ * @brief Boolean
+ * @note Keep intelligence
+ */
+ bool sustain_int;
+ /** @structvar sustain_wis
+ * @brief Boolean
+ * @note Keep wisdom
+ */
+ bool sustain_wis;
+ /** @structvar sustain_dex
+ * @brief Boolean
+ * @note Keep dexterity
+ */
+ bool sustain_dex;
+ /** @structvar sustain_con
+ * @brief Boolean
+ * @note Keep constitution
+ */
+ bool sustain_con;
+ /** @structvar sustain_chr
+ * @brief Boolean
+ * @note Keep charisma
+ */
+ bool sustain_chr;
+
+ /** @structvar aggravate
+ * @brief Boolean
+ * @note Aggravate monsters
+ */
+ bool aggravate;
+ /** @structvar teleport
+ * @brief Boolean
+ * @note Random teleporting
+ */
+ bool teleport;
+
+ /** @structvar exp_drain
+ * @brief Boolean
+ * @note Experience draining
+ */
+ bool exp_drain;
+ /** @structvar drain_mana
+ * @brief Number
+ * @note mana draining
+ */
+ byte drain_mana;
+ /** @structvar drain_life
+ * @brief Number
+ * @note hp draining
+ */
+ byte drain_life;
+
+ /** @structvar magical_breath
+ * @brief Boolean
+ * @note Magical breathing -- can breath anywhere
+ */
+ bool magical_breath;
+ /** @structvar water_breath
+ * @brief Boolean
+ * @note Water breathing -- can breath underwater
+ */
+ bool water_breath;
+ /** @structvar climb
+ * @brief Boolean
+ * @note Can climb mountains
+ */
+ bool climb;
+ /** @structvar fly
+ * @brief Boolean
+ * @note Can fly over some features
+ */
+ bool fly;
+ /** @structvar ffall
+ * @brief Boolean
+ * @note No damage falling
+ */
+ bool ffall;
+ /** @structvar lite
+ * @brief Boolean
+ * @note Permanent light
+ */
+ bool lite;
+ /** @structvar free_act
+ * @brief Boolean
+ * @note Never paralyzed
+ */
+ bool free_act;
+ /** @structvar see_inv
+ * @brief Boolean
+ * @note Can see invisible
+ */
+ bool see_inv;
+ /** @structvar regenerate
+ * @brief Boolean
+ * @note Regenerate hit pts
+ */
+ bool regenerate;
+ /** @structvar hold_life
+ * @brief Boolean
+ * @note Resist life draining
+ */
+ bool hold_life;
+ /** @structvar telepathy
+ * @brief Number
+ * @note Telepathy
+ */
+ u32b telepathy;
+ /** @structvar slow_digest
+ * @brief Boolean
+ * @note Slower digestion
+ */
+ bool slow_digest;
+ /** @structvar bless_blade
+ * @brief Boolean
+ * @note Blessed blade
+ */
+ bool bless_blade;
+ /** @structvar xtra_might
+ * @brief Number
+ * @note Extra might bow
+ */
+ byte xtra_might;
+ /** @structvar impact
+ * @brief Boolean
+ * @note Earthquake blows
+ */
+ bool impact;
+ /** @structvar auto_id
+ * @brief Boolean
+ * @note Auto id items
+ */
+ bool auto_id;
+
+ /** @structvar dis_to_h
+ * @brief Number
+ * @note Known bonus to hit
+ */
+ s16b dis_to_h;
+ /** @structvar dis_to_d
+ * @brief Number
+ * @note Known bonus to dam
+ */
+ s16b dis_to_d;
+ /** @structvar dis_to_a
+ * @brief Number
+ * @note Known bonus to ac
+ */
+ s16b dis_to_a;
+
+ /** @structvar dis_ac
+ * @brief Number
+ * @note Known base ac
+ */
+ s16b dis_ac;
+
+ /** @structvar to_m
+ * @brief Number
+ * @note Bonus to mana
+ */
+ s16b to_m;
+ /** @structvar to_s
+ * @brief Number
+ * @note Bonus to spell
+ */
+ s16b to_s;
+ /** @structvar to_h
+ * @brief Number
+ * @note Bonus to hit
+ */
+ s16b to_h;
+ /** @structvar to_d
+ * @brief Number
+ * @note Bonus to dam
+ */
+ s16b to_d;
+ /** @structvar to_a
+ * @brief Number
+ * @note Bonus to ac
+ */
+ s16b to_a;
+
+ /** @structvar to_h_melee
+ * @brief Number
+ * @note Bonus to hit
+ */
+ s16b to_h_melee;
+ /** @structvar to_d_melee
+ * @brief Number
+ * @note Bonus to dam
+ */
+ s16b to_d_melee;
+
+ /** @structvar to_h_ranged
+ * @brief Number
+ * @note Bonus to hit
+ */
+ s16b to_h_ranged;
+ /** @structvar to_d_ranged
+ * @brief Number
+ * @note Bonus to dam
+ */
+ s16b to_d_ranged;
+
+ /** @structvar num_blow
+ * @brief Number
+ * @note Number of blows
+ */
+ s16b num_blow;
+ /** @structvar num_fire
+ * @brief Number
+ * @note Number of shots
+ */
+ s16b num_fire;
+
+ /** @structvar ac
+ * @brief Number
+ * @note Base ac
+ */
+ s16b ac;
+
+ /** @structvar antimagic
+ * @brief Number
+ * @note Power of the anti magic field
+ */
+ byte antimagic;
+ /** @structvar antimagic_dis
+ * @brief Number
+ * @note Radius of the anti magic field
+ */
+ byte antimagic_dis;
+
+ /** @structvar see_infra
+ * @brief Number
+ * @note Infravision range
+ */
+ s16b see_infra;
+
+ /** @structvar skill_dis
+ * @brief Number
+ * @note Skill: Disarming
+ */
+ s16b skill_dis;
+ /** @structvar skill_dev
+ * @brief Number
+ * @note Skill: Magic Devices
+ */
+ s16b skill_dev;
+ /** @structvar skill_sav
+ * @brief Number
+ * @note Skill: Saving throw
+ */
+ s16b skill_sav;
+ /** @structvar skill_stl
+ * @brief Number
+ * @note Skill: Stealth factor
+ */
+ s16b skill_stl;
+ /** @structvar skill_srh
+ * @brief Number
+ * @note Skill: Searching ability
+ */
+ s16b skill_srh;
+ /** @structvar skill_fos
+ * @brief Number
+ * @note Skill: Searching frequency
+ */
+ s16b skill_fos;
+ /** @structvar skill_thn
+ * @brief Number
+ * @note Skill: To hit (normal)
+ */
+ s16b skill_thn;
+ /** @structvar skill_thb
+ * @brief Number
+ * @note Skill: To hit (shooting)
+ */
+ s16b skill_thb;
+ /** @structvar skill_tht
+ * @brief Number
+ * @note Skill: To hit (throwing)
+ */
+ s16b skill_tht;
+ /** @structvar skill_dig
+ * @brief Number
+ * @note Skill: Digging
+ */
+ s16b skill_dig;
+
+ /** @structvar skill_points
+ * @brief Number
+ */
+ s16b skill_points;
+
+ /** @structvar control
+ * @brief Number
+ * @note Controlled monster
+ */
+ s16b control;
+ /** @structvar control_dir
+ * @brief Number
+ * @note Controlled monster
+ */
+ byte control_dir;
+ /** @structvar companion_killed
+ * @brief Number
+ * @note Number of companion death
+ */
+ s16b companion_killed;
+ /** @structvar black_breath
+ * @brief Boolean
+ * @note The Tolkien's Black Breath
+ */
+ bool black_breath;
+ /** @structvar body_monster
+ * @brief Number
+ * @note In which body is the player
+ */
+ u16b body_monster;
+
+ /** @structvar body_parts[28]
+ * @brief Number
+ * @note Various body modifiers
+ */
+ byte body_parts[28];
+
+ /** @structvar extra_body_parts[BODY_MAX]
+ * @brief Number
+ * @note Various body modifiers
+ */
+ s16b extra_body_parts[BODY_MAX];
+
+ /** @structvar powers_mod[POWER_MAX_INIT]
+ * @brief Boolean
+ * @note Intrinsinc powers
+ */
+ bool powers_mod[POWER_MAX_INIT];
+ /** @structvar powers[power_max]
+ * @brief Boolean
+ */
+ bool powers[power_max];
+
+ /** @structvar spellbinder_num
+ * @brief Number
+ * @note Number of spells bound
+ */
+ byte spellbinder_num;
+ /** @structvar spellbinder[4]
+ * @brief Number
+ * @note Spell bounds
+ */
+ u32b spellbinder[4];
+ /** @structvar spellbinder_trigger
+ * @brief Number
+ * @note Spellbinder trigger condition
+ */
+ byte spellbinder_trigger;
+
+ /* Corruptions */
+ /** @structvar corruptions_aux;
+ * @brief Boolean
+ */
+ bool corruptions[max_corruptions] @ corruptions_aux;
+
+ /* Astral */
+ /** @structvar astral
+ * @brief Boolean
+ * @note We started at the bottom ?
+ */
+ bool astral;
+
+ /*** Temporary fields ***/
+
+ /** @structvar leaving
+ * @brief Boolean
+ * @note True if player is leaving
+ */
+ bool leaving;
+};
+
+/** @name Spellbinder triggers
+ * @{ */
+/** @def SPELLBINDER_HP75
+ * @note Trigger spellbinder at 75% maximum hit points */
+#define SPELLBINDER_HP75 1
+
+/** @def SPELLBINDER_HP50
+ * @note Trigger spellbinder at 50% maximum hit points */
+#define SPELLBINDER_HP50 2
+
+/** @def SPELLBINDER_HP25
+ * @note Trigger spellbinder at 25% maximum hit points */
+#define SPELLBINDER_HP25 3
+/** @} */
+
+
+/** @struct player_race
+ */
+struct player_race
+{
+ /** @structvar title
+ * @brief Number
+ * @note Type of race
+ */
+ s32b title;
+ /** @structvar desc
+ * @brief Number
+ */
+ s32b desc;
+
+ /** @structvar infra
+ * @brief Number
+ * @note Infra-vision range
+ */
+ byte infra;
+};
+
+/** @struct player_race_mod
+ */
+struct player_race_mod
+{
+ /** @structvar title
+ * @brief Number
+ * @note Type of race mod
+ */
+ s32b title;
+ /** @structvar desc
+ * @brief Number
+ * @note Desc
+ */
+ s32b desc;
+ /** @structvar place
+ * @brief Boolean
+ * @note TRUE = race race modifier, FALSE = Race modifier race
+ */
+ bool place;
+
+ /** @structvar r_adj[6]
+ * @brief Number
+ * @note (+) Racial stat bonuses
+ */
+ s16b r_adj[6];
+
+ /** @structvar luck
+ * @brief String
+ * @note Luck
+ */
+ char luck;
+ /** @structvar mana
+ * @brief Number
+ * @note Mana %
+ */
+ s16b mana;
+
+ /** @structvar r_dis
+ * @brief Number
+ * @note (+) disarming
+ */
+ s16b r_dis;
+ /** @structvar r_dev
+ * @brief Number
+ * @note (+) magic devices
+ */
+ s16b r_dev;
+ /** @structvar r_sav
+ * @brief Number
+ * @note (+) saving throw
+ */
+ s16b r_sav;
+ /** @structvar r_stl
+ * @brief Number
+ * @note (+) stealth
+ */
+ s16b r_stl;
+ /** @structvar r_srh
+ * @brief Number
+ * @note (+) search ability
+ */
+ s16b r_srh;
+ /** @structvar r_fos
+ * @brief Number
+ * @note (+) search frequency
+ */
+ s16b r_fos;
+ /** @structvar r_thn
+ * @brief Number
+ * @note (+) combat (normal)
+ */
+ s16b r_thn;
+ /** @structvar r_thb
+ * @brief Number
+ * @note (+) combat (shooting)
+ */
+ s16b r_thb;
+
+ /** @structvar r_mhp
+ * @brief String
+ * @note (+) Race mod hit-dice modifier
+ */
+ char r_mhp;
+ /** @structvar r_exp
+ * @brief Number
+ * @note (+) Race mod experience factor
+ */
+ s16b r_exp;
+
+ /** @structvar b_age
+ * @brief String
+ * @note (+) base age
+ */
+ char b_age;
+ /** @structvar m_age
+ * @brief String
+ * @note (+) mod age
+ */
+ char m_age;
+
+ /** @structvar m_b_ht
+ * @brief String
+ * @note (+) base height (males)
+ */
+ char m_b_ht;
+ /** @structvar m_m_ht
+ * @brief String
+ * @note (+) mod height (males)
+ */
+ char m_m_ht;
+ /** @structvar m_b_wt
+ * @brief String
+ * @note (+) base weight (males)
+ */
+ char m_b_wt;
+ /** @structvar m_m_wt
+ * @brief String
+ * @note (+) mod weight (males)
+ */
+ char m_m_wt;
+
+ /** @structvar f_b_ht
+ * @brief String
+ * @note (+) base height (females)
+ */
+ char f_b_ht;
+ /** @structvar f_m_ht
+ * @brief String
+ * @note (+) mod height (females)
+ */
+ char f_m_ht;
+ /** @structvar f_b_wt
+ * @brief String
+ * @note (+) base weight (females)
+ */
+ char f_b_wt;
+ /** @structvar f_m_wt
+ * @brief String
+ * @note (+) mod weight (females)
+ */
+ char f_m_wt;
+
+ /** @structvar infra
+ * @brief String
+ * @note (+) Infra-vision range
+ */
+ char infra;
+
+ /** @structvar choice[2]
+ * @brief Number
+ * @note Legal race choices
+ */
+ u32b choice[2];
+
+ /** @structvar pclass[2]
+ * @brief Number
+ * @note Classes allowed
+ */
+ u32b pclass[2];
+ /** @structvar mclass[2]
+ * @brief Number
+ * @note Classes restricted
+ */
+ u32b mclass[2];
+
+ /** @structvar powers[4]
+ * @brief Number
+ * @note Powers of the subrace
+ */
+ s16b powers[4];
+
+ /** @structvar body_parts[BODY_MAX]
+ * @brief String
+ * @note To help to decide what to use when body changing
+ */
+ char body_parts[BODY_MAX];
+
+ /** @structvar flags1
+ * @brief Number
+ */
+ u32b flags1;
+ /** @structvar flags2
+ * @brief Number
+ * @note flags
+ */
+ u32b flags2;
+
+ /** @structvar oflags1[51]
+ * @brief Number
+ */
+ u32b oflags1[51];
+ /** @structvar oflags2[51]
+ * @brief Number
+ */
+ u32b oflags2[51];
+ /** @structvar oflags3[51]
+ * @brief Number
+ */
+ u32b oflags3[51];
+ /** @structvar oflags4[51]
+ * @brief Number
+ */
+ u32b oflags4[51];
+ /** @structvar oflags5[51]
+ * @brief Number
+ */
+ u32b oflags5[51];
+ /** @structvar oesp[51]
+ * @brief Number
+ */
+ u32b oesp[51];
+ /** @structvar opval[51]
+ * @brief Number
+ */
+ s16b opval[51];
+
+ /** @structvar g_attr
+ * @brief Number
+ * @note Overlay graphic attribute
+ */
+ byte g_attr;
+ /** @structvar g_char
+ * @brief String
+ * @note Overlay graphic character
+ */
+ char g_char;
+
+ /** @structvar skill_basem[MAX_SKILLS]
+ * @brief String
+ */
+ char skill_basem[MAX_SKILLS];
+ /** @structvar skill_base[MAX_SKILLS]
+ * @brief Number
+ */
+ u32b skill_base[MAX_SKILLS];
+ /** @structvar skill_modm[MAX_SKILLS]
+ * @brief String
+ */
+ char skill_modm[MAX_SKILLS];
+ /** @structvar skill_mod[MAX_SKILLS]
+ * @brief Number
+ */
+ s16b skill_mod[MAX_SKILLS];
+};
+
+/** @var energy_use
+ * @brief Number
+ * @note Energy use for an action (0 if action does not take a turn).
+ */
+extern s32b energy_use;
+
+/** @var player;
+ * @brief player_type
+ * @note The player.
+ */
+extern player_type *p_ptr @ player;
+
+/** @var max_rp_idx
+ * @brief Number
+ * @note Maximum number of entries in player race array.
+ */
+extern u16b max_rp_idx;
+
+/** @var race_info[max_rp_idx]
+ * @brief player_race
+ * @note Array of player races.
+ */
+extern player_race race_info[max_rp_idx];
+
+/** @var *rp_name
+ * @brief String
+ * @note Name of player race.
+ */
+extern char *rp_name;
+
+/** @var *rp_text
+ * @brief String
+ */
+extern char *rp_text;
+
+/** @var max_rmp_idx
+ * @brief Number
+ * @note Maximum number of player subraces.
+ */
+extern u16b max_rmp_idx;
+
+/** @var _mod race_mod_info[max_rmp_idx]
+ * @brief player_race
+ * @note Array of player subraces.
+ */
+extern player_race_mod race_mod_info[max_rmp_idx];
+
+/** @var *rmp_name
+ * @brief String
+ * @note Name of player subrace.
+ */
+extern char *rmp_name;
+
+/** @var *rmp_text
+ * @brief String
+ */
+extern char *rmp_text;
+
+/** @var class_info[max_c_idx]
+ * @brief player_class
+ * @note Array of classes.
+ */
+extern player_class class_info[max_c_idx];
+
+/** @var *c_name
+ * @brief String
+ * @note Name of player class.
+ */
+extern char *c_name;
+
+/** @var *c_text
+ * @brief String
+ */
+extern char *c_text;
+
+/** @var flush_failure
+ * @brief Boolean
+ * @note TRUE if flush input on any failure, otherwise FALSE.
+ */
+extern bool flush_failure;
+
+/** @fn set_roots(int v, s16b ac, s16b dam)
+ * @brief Player has timed roots.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param ac Number \n bonus to AC
+ * @brief AC bonus
+ * @param dam Number \n bonus to melee to-damage
+ * @brief To-damage bonus
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_roots(int v, s16b ac, s16b dam);
+
+/** @fn set_shadow(int v)
+ * @brief Player has wraith form.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_shadow(int v);
+
+/** @fn set_parasite(int v, int r)
+ * @brief Player has timed parasite.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param r Number \n index of race in monster race array
+ * @brief Parasite race index
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * When the time remaining reaches 0, there is an 80% chance the parasite will
+ * be born, otherwise it will die away.
+ * @note (see file xtra2.c)
+ */
+extern bool set_parasite(int v, int r);
+
+/** @fn set_disrupt_shield(int v)
+ * @brief Player has timed disrupt shield (feels invulnerable).\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_disrupt_shield(int v);
+
+/** @fn set_prob_travel(int v)
+ * @brief Player has timed probability travel.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_prob_travel(int v);
+
+/** @fn set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag)
+ * @brief Player's weapon has a spell effect.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param gf Number \n spell effect
+ * @brief Spell effect
+ * @param dam Number \n damage caused by spell effect
+ * @brief Spell damage
+ * @param rad Number \n radius of spell effect
+ * @brief Spell radius
+ * @param flag Number \n spell projection effect
+ * @brief Spell properties
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag);
+
+/** @fn set_tim_deadly(int v)
+ * @brief Player has deadly accuracy.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_deadly(int v);
+
+/** @fn set_tim_res_time(int v)
+ * @brief Player has timed time resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_res_time(int v);
+
+/** @fn set_tim_reflect(int v)
+ * @brief Player has timed reflection.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_reflect(int v);
+
+/** @fn set_meditation(int v)
+ * @brief Player can meditate (forcibly pseudo-id).\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_meditation(int v);
+
+/** @fn set_strike(int v)
+ * @brief Player has true strike.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_strike(int v);
+
+/** @fn set_walk_water(int v)
+ * @brief Player can walk on water.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_walk_water(int v);
+
+/** @fn set_tim_ffall(int v)
+ * @brief Player has timed levitation (feather-fall).\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_ffall(int v);
+
+/** @fn set_tim_fire_aura(int v)
+ * @brief Player has a timed fiery aura.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_fire_aura(int v);
+
+/** @fn set_tim_regen(int v, int p)
+ * @brief Player has timed regeneration.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n power of regeneration
+ * @brief Regeneration power
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_regen(int v, int p);
+
+/** @fn set_holy(int v)
+ * @brief Player has a timed holy aura.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_holy(int v);
+
+/** @fn set_grace(s32b v)
+ * @brief Set the amount of grace a player has with a god.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range -30,000 to 30,000)
+ * @brief Grace
+ * @note (see file xtra2.c)
+ */
+extern void set_grace(s32b v);
+
+/** @fn set_mimic(int v, int p, int level)
+ * @brief Player has mimic form.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n the mimic form
+ * @brief Mimic form
+ * @param level Number \n the level of the mimic form
+ * @brief Mimic level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_mimic(int v, int p, int level);
+
+/** @fn set_no_breeders(int v)
+ * @brief Player has timed breeder prevention.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_no_breeders(int v);
+
+/** @fn set_tim_esp(int v)
+ * @brief Player has timed ESP.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_esp(int v);
+
+/** @fn set_invis(int v, int p)
+ * @brief Player has timed invisibility.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n power of invisibility
+ * @brief Invisibility power
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_invis(int v, int p);
+
+/** @fn set_lite(int v)
+ * @brief Player has timed light.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the use of "PU_VIEW", which is needed to
+ * memorize any terrain features which suddenly become "visible".
+ * @note
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ * @note (see file xtra2.c)
+ */
+extern bool set_lite(int v);
+
+/** @fn set_blind(int v)
+ * @brief Player has timed blindness.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the use of "PU_UN_VIEW", which is needed to memorize any terrain
+ * features which suddenly become "visible".
+ * @note
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ * @note (see file xtra2.c)
+ */
+extern bool set_blind(int v);
+
+/** @fn set_confused(int v)
+ * @brief Player has timed confusion.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_confused(int v);
+
+/** @fn set_poisoned(int v)
+ * @brief Player has timed poisoning.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_poisoned(int v);
+
+/** @fn set_afraid(int v)
+ * @brief Player has timed fear.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_afraid(int v);
+
+/** @fn set_paralyzed(int v)
+ * @brief Player has timed paralysis.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_paralyzed(int v);
+
+/** @fn set_image(int v)
+ * @brief Player has timed hallucination.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note that we must redraw the map when hallucination changes.
+ * @note (see file xtra2.c)
+ */
+extern bool set_image(int v);
+
+/** @fn set_fast(int v, int p)
+ * @brief Player has timed speed boost.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n speed factor
+ * @brief Speed factor
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_fast(int v, int p);
+
+/** @fn set_light_speed(int v)
+ * @brief Player has timed light speed.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_light_speed(int v);
+
+/** @fn set_slow(int v)
+ * @brief Player has timed slowness.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_slow(int v);
+
+/** @fn set_shield(int v, int p, s16b o, s16b d1, s16b d2)
+ * @brief Player has timed mystic shield.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p Number \n bonus to AC
+ * @brief AC bonus
+ * @param o Number \n type of shield (see SHIELD_foo fields)
+ * @brief Shield type
+ * @param d1 Number \n number of dice for damage roll
+ * @brief Damage dice
+ * @param d2 Number \n number of sides per die for damage roll
+ * @brief Damage sides
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_shield(int v, int p, s16b o, s16b d1, s16b d2);
+
+/* For calc_bonus hooks */
+/** @fn apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval = 0, s16b tval = 0, s16b to_h = 0, s16b to_d = 0, s16b to_a = 0)
+ * @brief Apply flags and values to the player.\n
+ * @param f1 Number \n flags to be applied to the player
+ * @brief Flag1
+ * @param f2 Number \n flags to be applied to the player
+ * @brief Flag2
+ * @param f3 Number \n flags to be applied to the player
+ * @brief Flag3
+ * @param f4 Number \n flags to be applied to the player
+ * @brief Flag4
+ * @param f5 Number \n flags to be applied to the player
+ * @brief Flag5
+ * @param esp Number \n ESP flag
+ * @brief Esp flag
+ * @param pval Number \n PVal to be applied to the player
+ * @brief Pval
+ * @param tval Number \n TVal to be applied to the player
+ * @brief Tval
+ * @param to_h Number \n to-hit bonus to be applied to the player
+ * @brief To-hit
+ * @param to_d Number \n to-damage bonus to be applied to the player
+ * @brief To-damage
+ * @param to_a Number \n AC bonus to be applied to the player
+ * @brief AC
+ * @note
+ * f1 can apply to attribuets, spell power, mana capacity, stealth, searching
+ * ability and frequency, infravision, digging, speed, extra blows, and
+ * earthquakes.
+ * @note
+ * f2 can apply to life capacity, sensible fire, invisibility, free action,
+ * hold life, immunities (except neither), resistances, reflection, and
+ * sustains.
+ * @note
+ * f3 can apply to extra shots, aggravate, teleport, drain XP, blessed, extra
+ * might, slow digestion, regeneration, lite, see invisible, wraith form,
+ * feather fall, fire sheath, electricity sheath, anti magic, and anti
+ * teleport.
+ * @note
+ * f4 can apply to lite, flying, climbing, nether immunity, precognition, and
+ * anti-magic power and radius.
+ * @note
+ * f5 can apply to luck, critical hits, drain mana, drain life, immovable,
+ * water breath, and magic breath.
+ * @note
+ * esp can apply to, well, just telepathy.
+ * @note
+ * pval can apply to attributes, luck, spell power, mana capacity, life
+ * capacity, stealth, search ability and frequency (x 5), infravision, digging
+ * (x 20), speed, extra blows, critical blows, invisibility (x 10), extra
+ * might, anti-magic power and radius.
+ * @note
+ * tval can apply to lite
+ * @note
+ * to_h, to_d, and to_ac can apply to anti-magic power and radius.
+ * @note (see file xtra1.c)
+ */
+extern void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval = 0, s16b tval = 0, s16b to_h = 0, s16b to_d = 0, s16b to_a = 0);
+
+/** @name Shield effect options
+ * @{ */
+/** @def SHIELD_NONE */
+#define SHIELD_NONE 0x0000
+
+/** @def SHIELD_COUNTER */
+#define SHIELD_COUNTER 0x0001
+
+/** @def SHIELD_FIRE */
+#define SHIELD_FIRE 0x0002
+
+/** @def SHIELD_GREAT_FIRE */
+#define SHIELD_GREAT_FIRE 0x0004
+
+/** @def SHIELD_FEAR */
+#define SHIELD_FEAR 0x0008
+/** @} */
+
+
+/** @fn set_tim_thunder(int v, int p1, int p2)
+ * @brief Player has timed thunderstorm.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param p1 Number \n number of dice for damage roll
+ * @brief Damage dice
+ * @param p2 Number \n number of sides per die for damage roll
+ * @brief Damage sides
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_thunder(int v, int p1, int p2);
+
+/** @fn set_tim_breath(int v, bool magical)
+ * @brief Player has timed magic/water breath.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @param magical Boolean \n TRUE if player has magic breath, or FALSE if the
+ * player has water breath
+ * @brief Magic breath?
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_breath(int v, bool magical);
+
+/** @fn set_tim_fly(int v)
+ * @brief Player has timed flight.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_fly(int v);
+
+/** @fn set_blessed(int v)
+ * @brief Player has timed blessing.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Blessing gives +5 bonus AC and +10 bonus to-hit.
+ * @note (see file xtra2.c)
+ */
+extern bool set_blessed(int v);
+
+/** @fn set_hero(int v)
+ * @brief Player has timed heroism.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Heroism gives +10 bonus max HP, +12 bonus to-hit, and resist fear.
+ * @note (see file xtra2.c)
+ */
+extern bool set_hero(int v);
+
+/** @fn set_shero(int v)
+ * @brief Player has timed berserk strength.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Berserk strength gives +30 bonus max HP, +24 bonus to-hit, -10 penalty AC,
+ * and resist fear.
+ * @note (see file xtra2.c)
+ */
+extern bool set_shero(int v);
+
+/** @fn set_protevil(int v)
+ * @brief Player has timed protection from evil.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Protection from evil gives the player a chance to repel evil monsters.
+ * @note (see file xtra2.c)
+ */
+extern bool set_protevil(int v);
+
+/** @fn set_protgood(int v)
+ * @brief Player has timed protection from good.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Protection from good gives the player a chance to repel good monsters.
+ * @note (see file xtra2.c)
+ */
+extern bool set_protgood(int v);
+
+/** @fn set_protundead(int v)
+ * @brief Player has timed protection from undead.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Protection from undead protects against getting the Black Breath in a melee
+ * attack.
+ * @note (see file xtra2.c)
+ */
+extern bool set_protundead(int v);
+
+/** @fn set_invuln(int v)
+ * @brief Player has timed invulnerability.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Invulnerability prevents damage from walking on lava, walking the Straight
+ * Road, poison, cuts, and starvation. It gives +100 bonus to AC.
+ * @note
+ * It can be ended by the player attacking a monster, firing a missile,
+ * throwing an object, or activating a power.
+ * @note (see file xtra2.c)
+ */
+extern bool set_invuln(int v);
+
+/** @fn set_tim_invis(int v)
+ * @brief Player has timed "see invisibile".\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_invis(int v);
+
+/** @fn set_tim_infra(int v)
+ * @brief Player has timed infravision.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_tim_infra(int v);
+
+/** @fn set_mental_barrier(int v)
+ * @brief Player has timed mental barrier.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Mental barrier sustains intelligence and wisdom.
+ * @note (see file xtra2.c)
+ */
+extern bool set_mental_barrier(int v);
+
+/** @fn set_poison(int v)
+ * @brief Player has timed poison hands.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_poison(int v);
+
+/** @fn set_oppose_acid(int v)
+ * @brief Player has timed acid resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_acid(int v);
+
+/** @fn set_oppose_elec(int v)
+ * @brief Player has timed electricity resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_elec(int v);
+
+/** @fn set_oppose_fire(int v)
+ * @brief Player has timed fire resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_fire(int v);
+
+/** @fn set_oppose_cold(int v)
+ * @brief Player has timed cold resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_cold(int v);
+
+/** @fn set_oppose_pois(int v)
+ * @brief Player has timed poison resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_pois(int v);
+
+/** @fn set_oppose_ld(int v)
+ * @brief Player has timed light and dark resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_ld(int v);
+
+/** @fn set_oppose_cc(int v)
+ * @brief Player has timed chaos and confusion resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_cc(int v);
+
+/** @fn set_oppose_ss(int v)
+ * @brief Player has timed sound and shard resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_ss(int v);
+
+/** @fn set_oppose_nex(int v)
+ * @brief Player has timed nexus resistance.\n
+ * @param v Number \n time remaining until effect expires
+ * (must be in the range 0 to 10,000)
+ * @brief Time remaining
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note (see file xtra2.c)
+ */
+extern bool set_oppose_nex(int v);
+
+/** @fn set_stun(int v)
+ * @brief Player stun level changes.\n
+ * @param v Number \n the level of stun (must be in the range 0 to 10,000)
+ * @brief Stun level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the special code to only notice "range" changes.
+ * @note
+ * Some races resist stunning.
+ * @note
+ * There is a v chance in 1000 or 1 in 16 that a stun will be the result of
+ * a vicious blow to the head. If so, the player will lose a point of
+ * intelligence, or wisdom, or both unless the stat is sustained.
+ * @note (see file xtra2.c)
+ */
+extern bool set_stun(int v);
+
+/** @fn set_cut(int v)
+ * @brief Player cut level changes.\n
+ * @param v Number \n the level of cut (must be in the range 0 to 10,000)
+ * @brief Cut level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * Note the special code to only notice "range" changes.
+ * @note
+ * Some races resist cutting.
+ * @note
+ * There is a v chance in 1000 or 1 in 16 that a cut will result in scarrring.
+ * If so, the player will lose a point of charisma unless it is sustained.
+ * @note (see file xtra2.c)
+ */
+extern bool set_cut(int v);
+
+/** @fn set_food(int v)
+ * @brief Player hunger level changes.\n
+ * @param v Number \n the level of cut (must be in the range 0 to 10,000)
+ * @brief Cut level
+ * @return Boolean \n TRUE if player notices the effect, otherwise FALSE.
+ * @note
+ * The "p_ptr->food" variable can get as large as 20000, allowing the
+ * addition of the most "filling" item, Elvish Waybread, which adds
+ * 7500 food units, without overflowing the 32767 maximum limit.
+ * @note
+ * Perhaps we should disturb the player with various messages,
+ * especially messages about hunger status changes. XXX XXX XXX
+ * @note
+ * Digestion of food is handled in "dungeon.c", in which, normally,
+ * the player digests about 20 food units per 100 game turns, more
+ * when "fast", more when "regenerating", less with "slow digestion",
+ * but when the player is "gorged", he digests 100 food units per 10
+ * game turns, or a full 1000 food units per 100 game turns.
+ * @note
+ * Note that the player's speed is reduced by 10 units while gorged,
+ * so if the player eats a single food ration (5000 food units) when
+ * full (15000 food units), he will be gorged for (5000/100)*10 = 500
+ * game turns, or 500/(100/5) = 25 player turns (if nothing else is
+ * affecting the player speed).
+ * @note (see file xtra2.c)
+ */
+extern bool set_food(int v);
+
+/** @name Hunger flags
+ * @brief Player "food" crucial values
+ * @{ */
+/** @def PY_FOOD_MAX
+ * @note Food value (Bloated)
+ */
+#define PY_FOOD_MAX 15000
+/** @def PY_FOOD_FULL
+ * @note Food value (Normal)
+ */
+#define PY_FOOD_FULL 10000
+/** @def PY_FOOD_ALERT
+ * @note Food value (Hungry)
+ */
+#define PY_FOOD_ALERT 2000
+/** @def PY_FOOD_WEAK
+ * @note Food value (Weak)
+ */
+#define PY_FOOD_WEAK 1000
+/** @def PY_FOOD_FAINT
+ * @note Food value (Fainting)
+ */
+#define PY_FOOD_FAINT 500
+/** @def PY_FOOD_STARVE
+ * @note Food value (Starving)
+ */
+#define PY_FOOD_STARVE 100
+/** @} */
+
+/** @fn check_experience(void)
+ * @brief Check if player experience level has changed.\n
+ * @note
+ * If a player has achieved a level for the first time, give a corruption
+ * (1 chance in 3) if it applies, increase skill points, check ability levels,
+ * and add a note if notes are taken.
+ * @note (see file xtra2.c)
+ */
+extern void check_experience(void);
+
+/** @fn check_experience_obj(object_type *o_ptr)
+ * @brief Check if object "o_ptr" experience level has changed.\n
+ * @param *o_ptr object_type \n the object
+ * @brief Object
+ * @note
+ * If an object has achieved a level for the first time, apply gains.
+ * @note (see file xtra2.c)
+ */
+extern void check_experience_obj(object_type *o_ptr);
+
+/** @fn gain_exp(s32b amount)
+ * @brief Gain "amount" of experience.\n
+ * @param amount Number \n the experience points to gain.
+ * @brief Experience
+ * @note
+ * Count the number of objects which will gain experience. The objects share
+ * equally 2/3 of "amount". Give corruption if it applies. Gain experience.
+ * If experience is less than maximum, then increase maximum experience by 20%
+ * of "amount". Check for level change and print experience (check_experience).
+ * @note (see file xtra2.c)
+ */
+extern void gain_exp(s32b amount);
+
+/** @fn lose_exp(s32b amount)
+ * @brief Decrease experience by "amount".\n
+ * @param amount Number \n the experience points to lose.
+ * @brief Experience
+ * @note
+ * Experience can not fall below zero. Check for level change and print
+ * experience (check_experience).
+ * @note (see file xtra2.c)
+ */
+extern void lose_exp(s32b amount);
+
+/** @fn no_lite(void)
+ * @brief Return true if the player's grid is dark.
+ * @return Boolean \n TRUE if the player's grid is dark, otherwise FALSE.
+ * @note (see file cave.c)
+ */
+extern bool no_lite(void);
+
+/** @var dun_level
+ * @brief Number
+ * @note Current dungeon level
+ */
+extern s16b dun_level;
+
+
+/** @name Gods
+ * @{ */
+/** @def GOD_ALL */
+#define GOD_ALL -1
+
+/** @def GOD_NONE */
+#define GOD_NONE 0
+
+/** @def GOD_ERU */
+#define GOD_ERU 1
+
+/** @def GOD_MANWE */
+#define GOD_MANWE 2
+
+/** @def GOD_TULKAS */
+#define GOD_TULKAS 3
+
+/** @def GOD_MELKOR */
+#define GOD_MELKOR 4
+
+/** @def GOD_YAVANNA */
+#define GOD_YAVANNA 5
+/** @} */
+
+
+/** @fn inc_piety(int god, s32b amt)
+ * @brief Increase piety for god "god" by amount "amt".\n
+ * @param god Number \n the god
+ * @brief God
+ * @param amt Number \n the amount of piety
+ * @brief Piety
+ * @note
+ * If the player worships all gods, or "god", the piety (grace) will increase.
+ * @note (see file gods.c)
+ */
+extern void inc_piety(int god, s32b amt);
+
+/** @fn abandon_god(int god)
+ * @brief Player renounces their religion.\n
+ * @param god Number \n the god
+ * @brief God
+ * @note
+ * If the player worships all gods or "god", the player worships no god and
+ * the piety score is set to 0.
+ * @note (see file gods.c)
+ */
+extern void abandon_god(int god);
+
+/** @fn wisdom_scale(int max)
+ * @brief Rescale the wisdom value to a 0 <-> max range.\n
+ * @param max Number \n the new maximum value of the rescaled wisdom
+ * @brief New maximum wisdom
+ * @return Number \n The rescaled value of player wisdom.
+ * @note (see file gods.c)
+ */
+extern int wisdom_scale(int max);
+
+/** @fn follow_god(int god, bool silent)
+ * @brief Player starts to follow god "god".\n
+ * @param god Number \n the god
+ * @brief God
+ * @param silent Boolean \n TRUE if Melkor message is displayed, otherwise
+ * FALSE.
+ * @brief Show message?
+ * @note
+ * Unbelievers can not follow a god.
+ * @note
+ * If the player does not worship a god, they start worshipping "god". If the
+ * god is Melkor, the player gains the Udun skill (show a message if silent is
+ * FALSE).
+ * @note (see file gods.c)
+ */
+extern void follow_god(int god, bool silent);
+
+/** @fn add_new_gods(char *name)
+ * @brief Add a new god to the deity array.\n
+ * @param *name String \n the name of the god
+ * @brief God name
+ * @return Number \n The index of the new god inthe deity array.
+ * @note (see file lua_bind.c)
+ */
+extern s16b add_new_gods(char *name);
+
+/** @fn desc_god(int g_idx, int d, char *desc)
+ * @brief Return line "d" of the description of god with god index "g_idx".\n
+ * @param g_idx Number \n the index of god in the deity array.
+ * @brief God index
+ * @param d Number \n the line of the description
+ * (must be in the range 0 to 9).
+ * @brief Line of description
+ * @param *desc String
+ * @brief Description
+ * @return *desc String \n Line "d" of the god description.
+ * @note (see file lua_bind.c)
+ */
+extern void desc_god(int g_idx, int d, char *desc);
+
+/** @name Powers
+ * @{ */
+/** @def PWR_SPIT_ACID
+ * @note Spit acid (GF_ACID) */
+#define PWR_SPIT_ACID 0
+
+/** @def PWR_BR_FIRE
+ * @note Breathe fire (GF_FIRE) */
+#define PWR_BR_FIRE 1
+
+/** @def PWR_HYPN_GAZE
+ * @note Hypnotic gaze */
+#define PWR_HYPN_GAZE 2
+
+/** @def PWR_TELEKINES
+ * @note Telekinesis (fetch an object) */
+#define PWR_TELEKINES 3
+
+/** @def PWR_VTELEPORT
+ * @note Teleport */
+#define PWR_VTELEPORT 4
+
+/** @def PWR_MIND_BLST
+ * @note Mind blast (GF_PSI) */
+#define PWR_MIND_BLST 5
+
+/** @def PWR_RADIATION
+ * @note Emit radiation (GF_NUKE) */
+#define PWR_RADIATION 6
+
+/** @def PWR_VAMPIRISM
+ * @note Vampire bite */
+#define PWR_VAMPIRISM 7
+
+/** @def PWR_SMELL_MET
+ * @note Detect treasure */
+#define PWR_SMELL_MET 8
+
+/** @def PWR_SMELL_MON
+ * @note Detect normal monsters */
+#define PWR_SMELL_MON 9
+
+/** @def PWR_BLINK
+ * @note Short teleport (up to 10 grids) */
+#define PWR_BLINK 10
+
+/** @def PWR_EAT_ROCK
+ * @note Eat rock for food (wall to mud) */
+#define PWR_EAT_ROCK 11
+
+/** @def PWR_SWAP_POS
+ * @note Swap position with a monster */
+#define PWR_SWAP_POS 12
+
+/** @def PWR_SHRIEK
+ * @note Shriek (GF_SOUND and aggravate) */
+#define PWR_SHRIEK 13
+
+/** @def PWR_ILLUMINE
+ * @note Lite area */
+#define PWR_ILLUMINE 14
+
+/** @def PWR_DET_CURSE
+ * @note Detect cursed items in inventory */
+#define PWR_DET_CURSE 15
+
+/** @def PWR_BERSERK
+ * @note Berserk rage */
+#define PWR_BERSERK 16
+
+/** @def PWR_POLYMORPH
+ * @note Polymorph self */
+#define PWR_POLYMORPH 17
+
+/** @def PWR_MIDAS_TCH
+ * @note Midas touch - turn an item into gold */
+#define PWR_MIDAS_TCH 18
+
+/** @def PWR_GROW_MOLD
+ * @note Summon mold */
+#define PWR_GROW_MOLD 19
+
+/** @def PWR_RESIST
+ * @note Temporary elemental resist */
+#define PWR_RESIST 20
+
+/** @def PWR_EARTHQUAKE
+ * @note Cause an earthquake (destruction) */
+#define PWR_EARTHQUAKE 21
+
+/** @def PWR_EAT_MAGIC
+ * @note Absorb energy from magic items */
+#define PWR_EAT_MAGIC 22
+
+/** @def PWR_WEIGH_MAG
+ * @note Report magic affecting player */
+#define PWR_WEIGH_MAG 23
+
+/** @def PWR_STERILITY
+ * @note Player experiences forced abstinence */
+#define PWR_STERILITY 24
+
+/** @def PWR_PANIC_HIT
+ * @note Hit a monster and run away */
+#define PWR_PANIC_HIT 25
+
+/** @def PWR_DAZZLE
+ * @note Stun, confuse, and turn monsters */
+#define PWR_DAZZLE 26
+
+/** @def PWR_DARKRAY
+ * @note Fire a beam of light (GF_LITE) */
+#define PWR_DARKRAY 27
+
+/** @def PWR_RECALL
+ * @note Recall to dungeon/town */
+#define PWR_RECALL 28
+
+/** @def PWR_BANISH
+ * @note Banish evil creatures */
+#define PWR_BANISH 29
+
+/** @def PWR_COLD_TOUCH
+ * @note Bolt of cold (GF_COLD) */
+#define PWR_COLD_TOUCH 30
+
+/** @def PWR_LAUNCHER
+ * @note Increase the multiplier for a thrown object */
+#define PWR_LAUNCHER 31
+
+/** @def PWR_PASSWALL
+ * @note Walk through a wall */
+#define PWR_PASSWALL 32
+
+/** @def PWR_DETECT_TD
+ * @note Detect traps, doors, and stairs */
+#define PWR_DETECT_TD 33
+
+/** @def PWR_COOK_FOOD
+ * @note Create some food */
+#define PWR_COOK_FOOD 34
+
+/** @def PWR_UNFEAR
+ * @note Remove fear */
+#define PWR_UNFEAR 35
+
+/** @def PWR_EXPL_RUNE
+ * @note Set an explosive rune */
+#define PWR_EXPL_RUNE 36
+
+/** @def PWR_STM
+ * @note Bash a wall (stone to mud) */
+#define PWR_STM 37
+
+/** @def PWR_POIS_DART
+ * @note Throw a poison dart (GF_POIS) */
+#define PWR_POIS_DART 38
+
+/** @def PWR_MAGIC_MISSILE
+ * @note Fire a magic missile (GF_MISSILE) */
+#define PWR_MAGIC_MISSILE 39
+
+/** @def PWR_GROW_TREE
+ * @note Grow trees around the player */
+#define PWR_GROW_TREE 40
+
+/** @def PWR_BR_COLD
+ * @note Breathe cold (GF_COLD) */
+#define PWR_BR_COLD 41
+
+/** @def PWR_BR_CHAOS
+ * @note Breathe chaos (GF_CHAOS) */
+#define PWR_BR_CHAOS 42
+
+/** @def PWR_BR_ELEM
+ * @note Breath elements (GF_MISSILE) */
+#define PWR_BR_ELEM 43
+
+/** @def PWR_WRECK_WORLD
+ * @note Change the world (new level) */
+#define PWR_WRECK_WORLD 44
+
+/** @def PWR_SCARE
+ * @note Howl to scare monsters */
+#define PWR_SCARE 45
+
+/** @def PWR_REST_LIFE
+ * @note Restore life levels */
+#define PWR_REST_LIFE 46
+
+/** @def PWR_SUMMON_MONSTER
+ * @note Beastmaster powers (summon pets) */
+#define PWR_SUMMON_MONSTER 47
+
+/** @def PWR_NECRO
+ * @note Cast a necromancy spell */
+#define PWR_NECRO 48
+
+/** @def PWR_ROHAN
+ * @note Use flash aura or light speed jump */
+#define PWR_ROHAN 49
+
+/** @def PWR_THUNDER
+ * @note Use thunder strike, ride the straight road, or go back in town */
+#define PWR_THUNDER 50
+
+/** @def PWR_DEATHMOLD
+ * @note Use deathmold powers:\n
+ * (a) Teleport to a specific place\n
+ * (b) Fetch an item\n
+ * (c) Go up 50'\n
+ * (d) Go down 50'
+ */
+#define PWR_DEATHMOLD 51
+
+/** @def PWR_HYPNO
+ * @note Hypnotise a pet */
+#define PWR_HYPNO 52
+
+/** @def PWR_UNHYPNO
+ * @note Unhypnotise a pet */
+#define PWR_UNHYPNO 53
+
+/** @def PWR_INCARNATE
+ * @note Incarnate into a body */
+#define PWR_INCARNATE 54
+
+/** @def PWR_MAGIC_MAP
+ * @note Magic mapping */
+#define PWR_MAGIC_MAP 55
+
+/** @def PWR_LAY_TRAP
+ * @note Set a trap */
+#define PWR_LAY_TRAP 56
+
+/** @def PWR_MERCHANT
+ * @note Appraise item, warp item, or identify item */
+#define PWR_MERCHANT 57
+
+/** @def PWR_COMPANION
+ * @note Create a companion */
+#define PWR_COMPANION 58
+
+/** @def PWR_BEAR
+ * @note Mimic a bear */
+#define PWR_BEAR 59
+
+/** @def PWR_DODGE
+ * @note Report chance of dodging a monster */
+#define PWR_DODGE 60
+
+/** @def PWR_BALROG
+ * @note Mimic a balrog */
+#define PWR_BALROG 61
+/** @} */
+
+/* Misc */
+/** @fn do_cmd_throw(void)
+ * @brief Throw an object from the pack or floor.
+ * @note
+ * Note: "unseen" monsters are very hard to hit.
+ * @note
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ * @note (see file cmd2.c)
+ */
+extern void do_cmd_throw(void);
+
+/** @fn change_wild_mode()
+ * @brief Toggle between big map and little map.
+ * @note
+ * If the player is immovable, and the map is big, the player receives a
+ * warning and is allowed to proceed.
+ * @note
+ * If the player is about to be recalled, and the map is big, the map is
+ * not changed.
+ * @note
+ * The map is changed. The game is saved if autosave is set to "levels".
+ * @note (see file spells2.c)
+ */
+extern void change_wild_mode();
+
+/** @fn switch_class(int sclass)
+ * @brief Change to an other class.\n
+ * @param sclass Number \n the inex of the new class in the class array
+ * @brief Class index
+ * @note (see file xtra2.c)
+ */
+extern void switch_class(int sclass);
+
+/** @fn switch_subclass(int sclass)
+ * @brief Change to an other subclass.\n
+ * @param sclass Number \n the new subclass
+ * @brief Subclass
+ * @note (see file xtra2.c)
+ */
+extern void switch_subclass(int sclass);
+
+/** @fn switch_subrace(int racem, bool copy_old)
+ * @brief Change to an other subrace.\n
+ * @param racem Number \n index of subrace in subrace array
+ * @brief Subrace index
+ * @param copy_old Boolean \n TRUE if the new subrace is to be saved,
+ * otherwise FALSE.
+ * @brief Copy old subrace?
+ * @note (see file xtra2.c)
+ */
+extern void switch_subrace(int racem, bool copy_old);
+
+/** @fn get_subrace_title(int racem)
+ * @brief Return the subrace title.\n
+ * @param racem Number \n index of subrace in subrace array
+ * @brief Subrace index
+ * @return String \n Title of subrace.
+ * @note (see file xtra2.c)
+ */
+extern cptr get_subrace_title(int racem);
+
+/** @fn set_subrace_title(int racem, cptr name)
+ * @brief Set the subrace title.\n
+ * @param racem Number \n index of subrace in subrace array
+ * @brief Subrace index
+ * @param name String \n new title of subrace
+ * @brief New title
+ * @note (see file xtra2.c)
+ */
+extern void set_subrace_title(int racem, cptr name);
+
+/** @fn do_rebirth()
+ * @brief The player is reborn after a class, race, or subrace change.
+ * @note
+ * The experience factor is recalculated. The hit dice are reset and new HP
+ * are calculated. There may be a level change involved.
+ * @note (see file xtra2.c)
+ */
+extern void do_rebirth();
+
+/* Player race flags */
+$static bool lua_test_race_flags(int slot, u32b flags) { if (slot == 1) return (PRACE_FLAG(flags)) ? TRUE : FALSE; else return (PRACE_FLAG2(flags)) ? TRUE : FALSE; }
+/** @fn test_race_flags(int slot, u32b flags);
+ * @brief Test flag "flags" against race flags, race modifier flags, class
+ * flags, and specialist flags.\n
+ * @param slot Number \n 1 if testing against first set of flags (PRACE_FLAG),
+ * 2 if testing against second set of flags (PRACE_FLAG2)
+ * @brief Flag selecter.
+ * @param flags Number \n the flags to be tested
+ * @brief Test flags
+ * @return Boolean \n TRUE if test flags match any of the corresponding race,
+ * race modifier, class, and specialist flags.
+ * @note (see file w_player.c)
+ */
+static bool lua_test_race_flags@test_race_flags(int slot, u32b flags);
+
+/** @name Winner states
+ * @{ */
+/** @def WINNER_NORMAL
+ * @note Player has killed Morgoth */
+#define WINNER_NORMAL 1
+
+/** @def WINNER_ULTRA
+ * @note Player has killed Melkor */
+#define WINNER_ULTRA 2
+/** @} */
+
+/** @var wizard
+ * @brief Boolean
+ * @note TRUE if player currently in Wizard mode, otherwise FALSE.
+ */
+extern bool wizard;
+
+/** @var total_winner
+ * @brief Number
+ * @note Game has been won (see WINNER_foo fields).
+ */
+extern u16b total_winner;
+
+/** @var has_won
+ * @brief Number
+ * @note Game has been won (see WINNER_foo fields).
+ */
+extern u16b has_won;
+
+/** @var joke_monsters
+ * @brief Boolean
+ * @note TRUE if allowing joke monsters, otherwise FALSE.
+ */
+extern bool joke_monsters;
+
+extern s16b max_dlv[999999];
diff --git a/src/player_c.pkg b/src/player_c.pkg
new file mode 100644
index 00000000..f55f9325
--- /dev/null
+++ b/src/player_c.pkg
@@ -0,0 +1,1060 @@
+/* File: player_c.pkg */
+
+/*
+ * Purpose: Lua interface defitions for player classes.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @struct player_class
+ * @brief Player class
+ */
+struct player_class
+{
+ /** @structvar title
+ * @brief Number
+ * @note Type of class
+ */
+ s32b title;
+ /** @structvar desc
+ * @brief Number
+ * @note Small desc of the class
+ */
+ s32b desc;
+ /** @structvar titles[10]
+ * @brief Number
+ */
+ s32b titles[10];
+
+ /** @structvar c_adj[6]
+ * @brief Number
+ * @note Class stat modifier
+ */
+ s16b c_adj[6];
+
+ /** @structvar c_dis
+ * @brief Number
+ * @note class disarming
+ */
+ s16b c_dis;
+ /** @structvar c_dev
+ * @brief Number
+ * @note class magic devices
+ */
+ s16b c_dev;
+ /** @structvar c_sav
+ * @brief Number
+ * @note class saving throws
+ */
+ s16b c_sav;
+ /** @structvar c_stl
+ * @brief Number
+ * @note class stealth
+ */
+ s16b c_stl;
+ /** @structvar c_srh
+ * @brief Number
+ * @note class searching ability
+ */
+ s16b c_srh;
+ /** @structvar c_fos
+ * @brief Number
+ * @note class searching frequency
+ */
+ s16b c_fos;
+ /** @structvar c_thn
+ * @brief Number
+ * @note class to hit (normal)
+ */
+ s16b c_thn;
+ /** @structvar c_thb
+ * @brief Number
+ * @note class to hit (bows)
+ */
+ s16b c_thb;
+
+ /** @structvar x_dis
+ * @brief Number
+ * @note extra disarming
+ */
+ s16b x_dis;
+ /** @structvar x_dev
+ * @brief Number
+ * @note extra magic devices
+ */
+ s16b x_dev;
+ /** @structvar x_sav
+ * @brief Number
+ * @note extra saving throws
+ */
+ s16b x_sav;
+ /** @structvar x_stl
+ * @brief Number
+ * @note extra stealth
+ */
+ s16b x_stl;
+ /** @structvar x_srh
+ * @brief Number
+ * @note extra searching ability
+ */
+ s16b x_srh;
+ /** @structvar x_fos
+ * @brief Number
+ * @note extra searching frequency
+ */
+ s16b x_fos;
+ /** @structvar x_thn
+ * @brief Number
+ * @note extra to hit (normal)
+ */
+ s16b x_thn;
+ /** @structvar x_thb
+ * @brief Number
+ * @note extra to hit (bows)
+ */
+ s16b x_thb;
+
+ /** @structvar c_mhp
+ * @brief Number
+ * @note Class hit-dice adjustment
+ */
+ s16b c_mhp;
+ /** @structvar c_exp
+ * @brief Number
+ * @note Class experience factor
+ */
+ s16b c_exp;
+
+ /** @structvar powers[4]
+ * @brief Number
+ * @note Powers of the class
+ */
+ s16b powers[4];
+
+ /** @structvar spell_book
+ * @brief Number
+ * @note Tval of spell books (if any)
+ */
+ s16b spell_book;
+ /** @structvar spell_stat
+ * @brief Number
+ * @note Stat for spells (if any)
+ */
+ s16b spell_stat;
+ /** @structvar spell_lev
+ * @brief Number
+ * @note The higher it is the higher the spells level are
+ */
+ s16b spell_lev;
+ /** @structvar spell_fail
+ * @brief Number
+ * @note The higher it is the higher the spells failure are
+ */
+ s16b spell_fail;
+ /** @structvar spell_mana
+ * @brief Number
+ * @note The higher it is the higher the spells mana are
+ */
+ s16b spell_mana;
+ /** @structvar spell_first
+ * @brief Number
+ * @note Level of first spell
+ */
+ s16b spell_first;
+ /** @structvar spell_weight
+ * @brief Number
+ * @note Weight that hurts spells
+ */
+ s16b spell_weight;
+ /** @structvar max_spell_level
+ * @brief Number
+ * @note Maximun spell level
+ */
+ byte max_spell_level;
+ /** @structvar magic_max_spell
+ * @brief Number
+ * @note Maximun numbner of spells one can learn by natural means
+ */
+ byte magic_max_spell;
+
+ /** @structvar flags1
+ * @brief Number
+ * @note flags
+ */
+ s32b flags1;
+
+ /** @structvar mana
+ * @brief Number
+ */
+ s16b mana;
+ /** @structvar blow_num
+ * @brief Number
+ */
+ s16b blow_num;
+ /** @structvar blow_wgt
+ * @brief Number
+ */
+ s16b blow_wgt;
+ /** @structvar blow_mul
+ * @brief Number
+ */
+ s16b blow_mul;
+ /** @structvar extra_blows
+ * @brief Number
+ */
+ s16b extra_blows;
+
+ /** @structvar sense_base
+ * @brief Number
+ */
+ s32b sense_base;
+ /** @structvar sense_pl
+ * @brief Number
+ */
+ s32b sense_pl;
+ /** @structvar sense_plus
+ * @brief Number
+ */
+ s32b sense_plus;
+ /** @structvar sense_heavy
+ * @brief Number
+ */
+ byte sense_heavy;
+ /** @structvar sense_heavy_magic
+ * @brief Number
+ */
+ byte sense_heavy_magic;
+};
+
+/** @var *cp_ptr
+ * @brief player_class
+ * @note Player class information
+ */
+extern player_class *cp_ptr;
+
+
+
+/** @struct skill_type
+ * @brief Skills
+ */
+struct skill_type
+{
+ /** @structvar name
+ * @brief Number
+ * @note Name
+ */
+ u32b name;
+ /** @structvar desc
+ * @brief Number
+ * @note Description
+ */
+ u32b desc;
+ /** @structvar action_desc
+ * @brief Number
+ * @note Action Description
+ */
+ u32b action_desc;
+
+ /** @structvar action_mkey
+ * @brief Number
+ * @note Action do to
+ */
+ s16b action_mkey;
+
+ /** @structvar i_value
+ * @brief Number
+ * @note Actual value
+ */
+ u32b i_value;
+ /** @structvar i_mod
+ * @brief Number
+ * @note Modifier(1 skill point = modifier skill)
+ */
+ u16b i_mod;
+
+ /** @structvar value
+ * @brief Number
+ * @note Actual value
+ */
+ u32b value;
+ /** @structvar mod
+ * @brief Number
+ * @note Modifier(1 skill point = modifier skill)
+ */
+ u16b mod;
+ /** @structvar rate
+ * @brief Number
+ * @note Modifier decreasing rate
+ */
+ s16b rate;
+
+ /** @structvar uses
+ * @brief Number
+ * @note Number of times used
+ */
+ u32b uses;
+
+ /** @structvar action[9999]
+ * @brief Number
+ * @note List of actions against other skills
+ */
+ s16b action[9999];
+
+ /** @structvar father
+ * @brief Number
+ * @note Father in the skill tree
+ */
+ s16b father;
+ /** @structvar dev
+ * @brief Boolean
+ * @note Is the branch developped ?
+ */
+ bool dev;
+ /** @structvar order
+ * @brief Number
+ * @note Order in the tree
+ */
+ s16b order;
+ /** @structvar hidden
+ * @brief Boolean
+ * @note Innactive
+ */
+ bool hidden;
+};
+
+/** @def MAX_SKILLS
+ * @brief Maximum number of skills
+ */
+#define MAX_SKILLS 100
+
+
+$static cptr get_skill_name(int i) { return s_name + s_info[i].name; }
+/** @fn get_skill_name(int i)
+ * @brief Return name of skill with index "i" in skill array.\n
+ * @param i Number \n the index of skill in skill array.
+ * @brief Skill index
+ * @return String \n The name of the skill with index "i" in the skill array.
+ * @note (see file w_play_c.c)
+ */
+static cptr get_skill_name(int i);
+
+/** @var old_max_s_idx
+ * @brief Number
+ * @note Previous maximum skill index
+ */
+extern u16b old_max_s_idx;
+/** @var max_s_idx
+ * @brief Number
+ * @note Current maximum skill index
+ */
+extern u16b max_s_idx;
+/** @var s_info[MAX_SKILLS]
+ * @brief skill_type
+ * @note Array of player skills
+ */
+skill_type s_info[MAX_SKILLS];
+
+/** @name Skills
+ * @{ */
+/** @def SKILL_CONVEYANCE
+ * @brief Conveyance
+ * @note
+ * Ability to learn and use spells from the Conveyance school
+ */
+#define SKILL_CONVEYANCE 1
+
+/** @def SKILL_MANA
+ * @brief Mana
+ * @note
+ * Ability to learn and use spells from the Mana school
+ */
+#define SKILL_MANA 2
+
+/** @def SKILL_FIRE
+ * @brief Fire
+ * @note
+ * Ability to learn and use spells from the Fire school
+ */
+#define SKILL_FIRE 3
+
+/** @def SKILL_AIR
+ * @brief Air
+ * @note
+ * Ability to learn and use spells from the Air school
+ */
+#define SKILL_AIR 4
+
+/** @def SKILL_WATER
+ * @brief Water
+ * @note
+ * Ability to learn and use spells from the Water school
+ */
+#define SKILL_WATER 5
+
+/** @def SKILL_NATURE
+ * @brief Nature
+ * @note
+ * Ability to learn and use spells from the Nature school
+ */
+#define SKILL_NATURE 6
+
+/** @def SKILL_EARTH
+ * @brief Earth
+ * @note
+ * Ability to learn and use spells from the Earth school
+ */
+#define SKILL_EARTH 7
+
+/** @def SKILL_SYMBIOTIC
+ * @brief Symbiosis
+ * @note
+ * Ability to enter in symbiosis with monsters unable to move by themselves
+ */
+#define SKILL_SYMBIOTIC 8
+
+/** @def SKILL_MUSIC
+ * @brief Music
+ * @note
+ * Ability to learn and sing songs
+ */
+#define SKILL_MUSIC 9
+
+/** @def SKILL_DIVINATION
+ * @brief Divination
+ * @note
+ * Ability to learn and use spells from the Divination school
+ */
+#define SKILL_DIVINATION 10
+
+/** @def SKILL_TEMPORAL
+ * @brief Temporal
+ * @note
+ * Ability to learn and use spells from the Temporal school
+ */
+#define SKILL_TEMPORAL 11
+
+/** @def SKILL_DRUID
+ * @brief Druidistic
+ * @note
+ * Ability to learn and use prayers from the Druidistic realm
+ */
+#define SKILL_DRUID 12
+
+/** @def SKILL_DAEMON
+ * @brief Demonology
+ * @note
+ * Ability to use incantations from the Demonblades
+ */
+#define SKILL_DAEMON 13
+
+/** @def SKILL_META
+ * @brief Meta
+ * @note
+ * Ability to learn and use spells from the Meta school
+ */
+#define SKILL_META 14
+
+/** @def SKILL_MAGIC
+ * @brief Magic
+ * @note
+ * General ability to do magic, also affect mana reserves and
+ * magic device ability. Helps pseudo-id of magic objects
+ */
+#define SKILL_MAGIC 15
+
+/** @def SKILL_COMBAT
+ * @brief Combat
+ * @note
+ * General ability to fight and to pseudo-id armours and weapons.
+ * It also allows to use heavier armours without penalties
+ */
+#define SKILL_COMBAT 16
+
+/** @def SKILL_MASTERY
+ * @brief Weaponmastery
+ * @note
+ * General ability to use melee weapons
+ */
+#define SKILL_MASTERY 17
+
+/** @def SKILL_SWORD
+ * @brief Sword-mastery
+ * @note
+ * Ability to use swords
+ */
+#define SKILL_SWORD 18
+
+/** @def SKILL_AXE
+ * @brief Axe-mastery
+ * @note
+ * Ability to use axes
+ */
+#define SKILL_AXE 19
+
+/** @def SKILL_POLEARM
+ * @brief Polearm-mastery
+ * @note
+ * Ability to use polearms
+ */
+#define SKILL_POLEARM 20
+
+/** @def SKILL_HAFTED
+ * @brief Hafted-mastery
+ * @note
+ * Ability to use hafted weapons
+ */
+#define SKILL_HAFTED 21
+
+/** @def SKILL_BACKSTAB
+ * @brief Backstab
+ * @note
+ * Ability to backstab fleeing and sleeping monsters to increase damage
+ */
+#define SKILL_BACKSTAB 22
+
+/** @def SKILL_ARCHERY
+ * @brief Archery
+ * @note
+ * General ability to use ranged weapons
+ */
+#define SKILL_ARCHERY 23
+
+/** @def SKILL_SLING
+ * @brief Sling-mastery
+ * @note
+ * Ability to use slings
+ */
+#define SKILL_SLING 24
+
+/** @def SKILL_BOW
+ * @brief Bow-mastery
+ * @note
+ * Ability to use bows
+ */
+#define SKILL_BOW 25
+
+/** @def SKILL_XBOW
+ * @brief Crossbow-mastery
+ * @note
+ * Ability to use crossbows
+ */
+#define SKILL_XBOW 26
+
+/** @def SKILL_BOOMERANG
+ * @brief Boomerang-mastery
+ * @note
+ * Ability to use boomerangs
+ */
+#define SKILL_BOOMERANG 27
+
+/** @def SKILL_SPIRITUALITY
+ * @brief Spirituality
+ * @note
+ * General ability to use spiritual skills and also influence Saving Throw
+ */
+#define SKILL_SPIRITUALITY 28
+
+/** @def SKILL_MINDCRAFT
+ * @brief Mindcraft
+ * @note
+ * Ability to focus the powers of the mind
+ */
+#define SKILL_MINDCRAFT 29
+
+/** @def SKILL_MISC
+ * @brief Misc
+ * @note
+ * Not a real skill, it is only used to regroup some skills
+ */
+#define SKILL_MISC 30
+
+/** @def SKILL_NECROMANCY
+ * @brief Necromancy
+ * @note
+ * Ability to harness the powers of the dead
+ */
+#define SKILL_NECROMANCY 31
+
+/** @def SKILL_MIMICRY
+ * @brief Mimicry
+ * @note
+ * Ability to use cloaks of mimicry to change form
+ */
+#define SKILL_MIMICRY 32
+
+/** @def SKILL_ANTIMAGIC
+ * @brief Antimagic
+ * @note
+ * Ability to generates an antimagic field
+ */
+#define SKILL_ANTIMAGIC 33
+
+/** @def SKILL_RUNECRAFT
+ * @brief Runecraft
+ * @note
+ * Ability to combine magic runes to create your own spells
+ */
+#define SKILL_RUNECRAFT 34
+
+/** @def SKILL_SNEAK
+ * @brief Sneakiness
+ * @note
+ * General ability at the sneakiness skills
+ */
+#define SKILL_SNEAK 35
+
+/** @def SKILL_STEALTH
+ * @brief Stealth
+ * @note
+ * Ability to move unnoticed, silently
+ */
+#define SKILL_STEALTH 36
+
+/** @def SKILL_DISARMING
+ * @brief Disarming
+ * @note
+ * Ability to disarm the various traps
+ */
+#define SKILL_DISARMING 37
+
+/* XXX */
+
+/** @def SKILL_ALCHEMY
+ * @brief Alchemy
+ * @note
+ * Ability to use essences to modify/create magic items
+ */
+#define SKILL_ALCHEMY 39
+
+/** @def SKILL_STEALING
+ * @brief Stealing
+ * @note
+ * Ability to steal objects
+ */
+#define SKILL_STEALING 40
+
+/** @def SKILL_SORCERY
+ * @brief Sorcery
+ * @note
+ * Ability to use all the magic schools as if their skill was sorcery
+ */
+#define SKILL_SORCERY 41
+
+/** @def SKILL_HAND
+ * @brief Barehand-combat
+ * @note
+ * Ability to fight barehanded
+ */
+#define SKILL_HAND 42
+
+/** @def SKILL_THAUMATURGY
+ * @brief Thaumaturgy
+ * @note
+ * Ability to gain and cast innate spells
+ */
+#define SKILL_THAUMATURGY 43
+
+/** @def SKILL_SUMMON
+ * @brief Summoning
+ * @note
+ * Ability to create totems from monsters and use them to summon monsters
+ */
+#define SKILL_SUMMON 44
+
+/** @def SKILL_SPELL
+ * @brief Spell-power
+ * @note
+ * Ability to increase the power of spells
+ */
+#define SKILL_SPELL 45
+
+/** @def SKILL_DODGE
+ * @brief Dodging
+ * @note
+ * Ability to dodge blows and bolts
+ */
+#define SKILL_DODGE 46
+
+/** @def SKILL_BEAR
+ * @brief Bearform-combat
+ * @note
+ * Ability to fight in bear form
+ */
+#define SKILL_BEAR 47
+
+/** @def SKILL_LORE
+ * @brief Monster-lore
+ * @note
+ * General ability at the monster related skills, ability to gain experience
+ * from friendly kills. It also affects the number of companions the player
+ * can have
+ */
+#define SKILL_LORE 48
+
+/** @def SKILL_PRESERVATION
+ * @brief Corpse-preservation
+ * @note
+ * Ability to not destroy the monster corpse when killing them
+ */
+#define SKILL_PRESERVATION 49
+
+/** @def SKILL_POSSESSION
+ * @brief Possession
+ * @note
+ * Ability to incarnate into monsters
+ */
+#define SKILL_POSSESSION 50
+
+/** @def SKILL_MIND
+ * @brief Mind
+ * @note
+ * Ability to learn and use spells from the Mind school
+ */
+#define SKILL_MIND 51
+
+/** @def SKILL_CRITS
+ * @brief Critical-hits
+ * @note
+ * Ability to deal critical hits with swords < 5lb
+ */
+#define SKILL_CRITS 52
+
+/** @def SKILL_PRAY
+ * @brief Prayer
+ * @note
+ * Ability to learn and use spells from the gods schools
+ */
+#define SKILL_PRAY 53
+
+/** @def SKILL_LEARN
+ * @brief Spell-learning
+ * @note
+ * You should not see that ! that is a BUG!
+ */
+#define SKILL_LEARN 54
+
+/** @def SKILL_UDUN
+ * @brief Udun
+ * @note
+ * Ability to learn and use spells from the Udun school
+ */
+#define SKILL_UDUN 55
+
+/** @def SKILL_DEVICE
+ * @brief Magic-Device
+ * @note
+ * Ease the use of magical devices, such as wands, staves and rods.
+ * It also helps pseudo-id of magic objects
+ */
+#define SKILL_DEVICE 56
+
+/** @def SKILL_STUN
+ * @brief Stunning-blows
+ * @note
+ * Ability to stun opponents when doing critical hits with hafted weapons > 5lb
+ */
+#define SKILL_STUN 57
+
+/** @def SKILL_BOULDER
+ * @brief Boulder-throwing
+ * @note
+ * Ability to make and throw boulders
+ */
+#define SKILL_BOULDER 58
+
+/** @def SKILL_GEOMANCY
+ * @brief Geomancy
+ * @note
+ * Ability to understand the raw elemental forces of nature and use
+ * them to advantage. Most spells need Fire/Water/Earth/Air skills
+ */
+#define SKILL_GEOMANCY 59
+
+
+/** @def SKILL_MAX
+ * @note Maximun skill value
+ */
+#define SKILL_MAX 50000
+/** @def SKILL_STEP
+ * @note 1 skill point
+ */
+#define SKILL_STEP 1000
+
+/** @} */
+
+/** @fn get_skill(int skill)
+ * @brief Return the value of skill with index "skill" in skill array.\n
+ * @param skill Number \n the index of skill in skill array.
+ * @brief Skill index
+ * @return Number \n The value of the skill with index "skill" in the skill
+ * array.
+ * @note (see file skills.c)
+ */
+extern s16b get_skill(int skill);
+
+/** @fn get_skill_scale(int skill, u32b scale)
+ * @brief Return the value of skill with index "skill" in skill array rescaled
+ * to a maximum of "scale".\n
+ * @param skill Number \n the index of skill in skill array.
+ * @brief Skill index
+ * @param scale Number \n the maximum rescaled skill value.
+ * @brief Scaled maximum
+ * @return Number \n The rescaled value of the skill with index "skill" in the
+ * skill array.
+ * @note (see file skills.c)
+ */
+extern s16b get_skill_scale(int skill, u32b scale);
+
+/** @fn do_get_new_skill()
+ * @brief Player select one of four new skills.
+ * @note (see file skills.c)
+ */
+extern void do_get_new_skill();
+
+/** @fn get_melee_skills()
+ * @brief Return the number of melee skills the player has.
+ * @return Number \n The number of melee skills.
+ * @note
+ * A skill is counted if the value > 0 and the skill is not hidden.
+ * @note (see file skills.c)
+ */
+extern s16b get_melee_skills();
+
+/** @fn find_skill(cptr name)
+ * @brief Return the index of skill with name "name".\n
+ * @param name String \n the name of the skill.
+ * @brief Skill name
+ * @return Number \n The index of the skill with name "name" in the skill
+ * array.
+ * @note
+ * The search is case sensitive.\n
+ * If no skills match the name, -1 is returned.
+ * @note (see file skills.c)
+ */
+extern s16b find_skill(cptr name);
+
+/** @fn find_skill_i(cptr name)
+ * @brief Return the index of skill with name "name".\n
+ * @param name String \n the name of the skill.
+ * @brief Skill name
+ * @return Number \n The index of the skill with name "name" in the skill
+ * array.
+ * @note
+ * The search ignores case.\n
+ * If no skills match the name, -1 is returned.
+ * @note (see file skills.c)
+ */
+extern s16b find_skill_i(cptr name);
+
+$static char *get_class_name() {return spp_ptr->title + c_name;}
+/** @fn *get_class_name()
+ * @brief Return the player's class.
+ * @return String \n The player's type of class + class name
+ * @note (see file w_play_c.c)
+ */
+char *get_class_name();
+
+$static char *get_race_name() {return rp_ptr->title + rp_name;}
+/** @fn *get_race_name()
+ * @brief Return the player's race.
+ * @return String \n The player's type of race + race name
+ * @note (see file w_play_c.c)
+ */
+char *get_race_name();
+
+$static char *get_subrace_name() {return rmp_ptr->title + rmp_name;}
+/** @fn *get_subrace_name()
+ * @brief Return the player's subrace.
+ * @return String \n The player's type of subrace + subrace name
+ * @note (see file w_play_c.c)
+ */
+char *get_subrace_name();
+
+/** @struct ability_type
+ * @brief Abilities
+ */
+struct ability_type
+{
+ /** @structvar action_mkey
+ * @brief Number
+ * @note Action do to
+ */
+ s16b action_mkey;
+
+ /** @structvar cost
+ * @brief Number
+ * @note Skill points cost
+ */
+ s16b cost;
+
+ /** @structvar acquired
+ * @brief Boolean
+ * @note Do the player actualylg ot it ?
+ */
+ bool acquired;
+};
+
+/** @fn find_ability(cptr name)
+ * @brief Return the index of ability with name "name".\n
+ * @param name String \n the name of the ability.
+ * @brief Ability name
+ * @return Number \n The index of the ability with name "name" in the ability
+ * array.
+ * @note
+ * The search is case sensitive.\n
+ * If no abilities match the name, -1 is returned.
+ * @note (see file skills.c)
+ */
+extern s16b find_ability(cptr name);
+
+/** @fn do_cmd_ability()
+ * @brief Allow the user to interact with abilities.
+ * @note
+ * This screen is typically used to view abilities, and increase them.
+ * @note (see file skills.c)
+ */
+extern void do_cmd_ability();
+
+/** @fn has_ability(int ab)
+ * @brief Does the player have ability "ab"?
+ * @param ab Number \n the index of ability in ability array.
+ * @brief Ability index
+ * @return Boolean \n TRUE if player has the ability, otherwise FALSE.
+ * @note (see file skills.c)
+ */
+extern bool has_ability(int ab);
+
+/** @var max_ab_idx
+ * @brief Number
+ * @note Maximum ability index
+ */
+extern s16b max_ab_idx;
+/** @var ab_info[max_ab_idx]
+ * @brief ability_type
+ * @note Array of player abilities
+ */
+extern ability_type ab_info[max_ab_idx];
+
+/** @name Abilities
+ * @{ */
+/** @def AB_SPREAD_BLOWS
+ * @brief Spread blows
+ * @note
+ * If a monster dies to an attack but the player still has blows left
+ * they won't lose the full turn, allowing them to attack some other
+ * monster in the same turn.
+ */
+#define AB_SPREAD_BLOWS 0
+
+/** @def AB_TREE_WALK
+ * @brief Tree walking
+ * @note
+ * Allows player to walk in dense forest.
+ */
+#define AB_TREE_WALK 1
+
+/** @def AB_PERFECT_CASTING
+ * @brief Perfect casting
+ * @note
+ * Allows player to reach 0% failure rate on spells.
+ */
+#define AB_PERFECT_CASTING 2
+
+/** @def AB_MAX_BLOW1
+ * @brief Extra Max Blow(1)
+ * @note
+ * Increases player "maximum possible blows" number by 1.
+ */
+#define AB_MAX_BLOW1 3
+
+/** @def AB_MAX_BLOW2
+ * @brief Extra Max Blow(2)
+ * @note
+ * Increases player "maximum possible blows" number by 1
+ * (Cumulative with Extra Max Blow(1)).
+ */
+#define AB_MAX_BLOW2 4
+
+/** @def AB_AMMO_CREATION
+ * @brief Ammo creation
+ * @note
+ * Allows player to create shots, arrows and bolts from various materials.
+ */
+#define AB_AMMO_CREATION 5
+
+/** @def AB_DEATH_TOUCH
+ * @brief Touch of death
+ * @note
+ * Player melee blows can insta-kill, but they only receive 1/3 of the
+ * experience for that kill.
+ */
+#define AB_DEATH_TOUCH 6
+
+/** @def AB_CREATE_ART
+ * @brief Artifact creation
+ * @note
+ * In combination with a high alchemy skill this ability will let the player
+ * design their very own artifacts.
+ */
+#define AB_CREATE_ART 7
+
+/** @def AB_FAR_REACHING
+ * @brief Far reaching attack
+ * @note
+ * The player can attack an enemy one square far using a polearm.
+ * At high levels of polearm skill, they can even hit two enemies at once.
+ */
+#define AB_FAR_REACHING 8
+
+/** @def AB_TRAPPING
+ * @brief Trapping
+ * @note
+ * Enables player to set traps which harm monsters.
+ */
+#define AB_TRAPPING 9
+
+/** @def AB_UNDEAD_FORM
+ * @brief Undead form
+ * @note
+ * Ability to turn into a weak undead being when you "die".
+ * You must then kill enough monsters to absorb enough life energy
+ * to come back to life.
+ */
+#define AB_UNDEAD_FORM 10
+
+/** @} */
+
diff --git a/src/plots.c b/src/plots.c
new file mode 100644
index 00000000..8588a96b
--- /dev/null
+++ b/src/plots.c
@@ -0,0 +1,473 @@
+/* File: plots.c */
+
+/* Purpose: plots & quests */
+
+/*
+ * Copyright (c) 2001 James E. Wilson, Robert A. Koeneke, DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#include "lua/lua.h"
+#include "tolua.h"
+extern lua_State* L;
+
+/* #define DEBUG_HOOK */
+
+/******** Hooks stuff *********/
+FILE *hook_file;
+
+#define MAX_ARGS 50
+
+static hooks_chain *hooks_heads[MAX_HOOKS];
+
+/* Wipe hooks and init them with quest hooks */
+void wipe_hooks()
+{
+ int i;
+
+ for (i = 0; i < MAX_HOOKS; i++)
+ {
+ hooks_heads[i] = NULL;
+ }
+}
+void init_hooks()
+{
+ int i;
+
+ for (i = 0; i < MAX_Q_IDX_INIT; i++)
+ {
+ if ((quest[i].type == HOOK_TYPE_C) && (quest[i].init != NULL)) quest[i].init(i);
+ }
+}
+
+void dump_hooks(int h_idx)
+{
+ int min = 0, max = MAX_HOOKS, i;
+
+ if (h_idx != -1)
+ {
+ min = h_idx;
+ max = h_idx + 1;
+ }
+
+ for (i = min; i < max; i++)
+ {
+ hooks_chain *c = hooks_heads[i];
+
+ /* Find it */
+ while (c != NULL)
+ {
+ msg_format("%s(%s)", c->name, (c->type == HOOK_TYPE_C) ? "C" : "Lua");
+
+ c = c->next;
+ }
+ }
+}
+
+/* Check a hook */
+bool check_hook(int h_idx)
+{
+ hooks_chain *c = hooks_heads[h_idx];
+
+ return (c != NULL);
+}
+
+/* Add a hook */
+hooks_chain* add_hook(int h_idx, hook_type hook, cptr name)
+{
+ hooks_chain *new, *c = hooks_heads[h_idx];
+
+ /* Find it */
+ while ((c != NULL) && (strcmp(c->name, name)))
+ {
+ c = c->next;
+ }
+
+ /* If not already in the list, add it */
+ if (c == NULL)
+ {
+ MAKE(new, hooks_chain);
+ new->hook = hook;
+ sprintf(new->name, name);
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK ADD: %s", name);
+ if (take_notes) add_note(format("HOOK ADD: %s", name), 'D');
+#endif
+ new->next = hooks_heads[h_idx];
+ hooks_heads[h_idx] = new;
+ return (new);
+ }
+ else return (c);
+}
+
+void add_hook_script(int h_idx, char *script, cptr name)
+{
+ hooks_chain *c = add_hook(h_idx, NULL, name);
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK LUA ADD: %s : %s", name, script);
+#endif
+ sprintf(c->script, "%s", script);
+ c->type = HOOK_TYPE_LUA;
+}
+
+/* Remove a hook */
+void del_hook(int h_idx, hook_type hook)
+{
+ hooks_chain *c = hooks_heads[h_idx], *p = NULL;
+
+ /* Find it */
+ while ((c != NULL) && (c->hook != hook))
+ {
+ p = c;
+ c = c->next;
+ }
+
+ /* Remove it */
+ if (c != NULL)
+ {
+ if (p == NULL)
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ hooks_heads[h_idx] = c->next;
+ FREE(c, hooks_chain);
+ }
+ else
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ p->next = c->next;
+ FREE(c, hooks_chain);
+ }
+ }
+}
+
+void del_hook_name(int h_idx, cptr name)
+{
+ hooks_chain *c = hooks_heads[h_idx], *p = NULL;
+
+ /* Find it */
+ while ((c != NULL) && (strcmp(c->name, name)))
+ {
+ p = c;
+ c = c->next;
+ }
+
+ /* Remove it */
+ if (c != NULL)
+ {
+ if (p == NULL)
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ hooks_heads[h_idx] = c->next;
+ FREE(c, hooks_chain);
+ }
+ else
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK DEL: %s", c->name);
+ if (take_notes) add_note(format("HOOK DEL: %s", c->name), 'D');
+#endif
+ p->next = c->next;
+ FREE(c, hooks_chain);
+ }
+ }
+}
+
+/* get the next argument */
+static hook_return param_pile[MAX_ARGS];
+static int get_next_arg_pos = 0;
+static int get_next_arg_pile_pos = 0;
+s32b get_next_arg(char *fmt)
+{
+ while (TRUE)
+ {
+ switch (fmt[get_next_arg_pos++])
+ {
+ case 'd':
+ case 'l':
+ return (param_pile[get_next_arg_pile_pos++].num);
+ case ')':
+ get_next_arg_pos--;
+ return 0;
+ case '(':
+ case ',':
+ break;
+ }
+ }
+}
+char* get_next_arg_str(char *fmt)
+{
+ while (TRUE)
+ {
+ switch (fmt[get_next_arg_pos++])
+ {
+ case 's':
+ return (char*)(param_pile[get_next_arg_pile_pos++].str);
+ case ')':
+ get_next_arg_pos--;
+ return 0;
+ case '(':
+ case ',':
+ break;
+ }
+ }
+}
+
+/* Actually process the hooks */
+int process_hooks_restart = FALSE;
+hook_return process_hooks_return[20];
+static bool vprocess_hooks_return (int h_idx, char *ret, char *fmt, va_list *ap)
+{
+ hooks_chain *c = hooks_heads[h_idx];
+ va_list real_ap;
+
+ while (c != NULL)
+ {
+#ifdef DEBUG_HOOK
+ if (wizard) cmsg_format(TERM_VIOLET, "HOOK: %s", c->name);
+ if (take_notes) add_note(format("HOOK PROCESS: %s", c->name), 'D');
+#endif
+ if (c->type == HOOK_TYPE_C)
+ {
+ int i = 0, nb = 0;
+
+ /* Push all args in the pile */
+ i = 0;
+ COPY(&real_ap, ap, va_list);
+ while (fmt[i])
+ {
+ switch (fmt[i])
+ {
+ case 'O':
+ param_pile[nb++].o_ptr = va_arg(real_ap, object_type *);
+ break;
+ case 's':
+ param_pile[nb++].str = va_arg(real_ap, char *);
+ break;
+ case 'd':
+ case 'l':
+ param_pile[nb++].num = va_arg(real_ap, s32b);
+ break;
+ case '(':
+ case ')':
+ case ',':
+ break;
+ }
+ i++;
+ }
+
+ get_next_arg_pos = 0;
+ get_next_arg_pile_pos = 0;
+ if (c->hook(fmt))
+ {
+ return TRUE;
+ }
+
+ /* Should we restart ? */
+ if (process_hooks_restart)
+ {
+ c = hooks_heads[h_idx];
+ process_hooks_restart = FALSE;
+ }
+ else
+ {
+ c = c->next;
+ }
+ }
+ else if (c->type == HOOK_TYPE_LUA)
+ {
+ int i = 0, nb = 0, nbr = 1;
+ int oldtop = lua_gettop(L), size;
+
+ /* Push the function */
+ lua_getglobal(L, c->script);
+
+ /* Push and count the arguments */
+ COPY(&real_ap, ap, va_list);
+ while (fmt[i])
+ {
+ switch (fmt[i++])
+ {
+ case 'd':
+ case 'l':
+ tolua_pushnumber(L, va_arg(real_ap, s32b));
+ nb++;
+ break;
+ case 's':
+ tolua_pushstring(L, va_arg(real_ap, char*));
+ nb++;
+ break;
+ case 'O':
+ tolua_pushusertype(L, (void*)va_arg(real_ap, object_type*), tolua_tag(L, "object_type"));
+ nb++;
+ break;
+ case 'M':
+ tolua_pushusertype(L, (void*)va_arg(real_ap, monster_type*), tolua_tag(L, "monster_type"));
+ nb++;
+ break;
+ case '(':
+ case ')':
+ case ',':
+ break;
+ }
+ }
+
+ /* Count returns */
+ nbr += strlen(ret);
+
+ /* Call the function */
+ if (lua_call(L, nb, nbr))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling '%s' lua hook script. Breaking the hook chain now.", c->script);
+ return FALSE;
+ }
+
+ /* Number of returned values, SHOULD be the same as nbr, but I'm paranoid */
+ size = lua_gettop(L) - oldtop;
+
+ /* get the extra returns if needed */
+ for (i = 0; i < nbr - 1; i++)
+ {
+ if ((ret[i] == 'd') || (ret[i] == 'l'))
+ {
+ if (lua_isnumber(L, ( -size) + 1 + i)) process_hooks_return[i].num = tolua_getnumber(L, ( -size) + 1 + i, 0);
+ else process_hooks_return[i].num = 0;
+ }
+ else if (ret[i] == 's')
+ {
+ if (lua_isstring(L, ( -size) + 1 + i)) process_hooks_return[i].str = tolua_getstring(L, ( -size) + 1 + i, "");
+ else process_hooks_return[i].str = NULL;
+ }
+ else if (ret[i] == 'O')
+ {
+ if (tolua_istype(L, ( -size) + 1 + i, tolua_tag(L, "object_type"), 0))
+ process_hooks_return[i].o_ptr = (object_type*)tolua_getuserdata(L, ( -size) + 1 + i, NULL);
+ else
+ process_hooks_return[i].o_ptr = NULL;
+ }
+ else if (ret[i] == 'M')
+ {
+ if (tolua_istype(L, ( -size) + 1 + i, tolua_tag(L, "monster_type"), 0))
+ process_hooks_return[i].m_ptr = (monster_type*)tolua_getuserdata(L, ( -size) + 1 + i, NULL);
+ else
+ process_hooks_return[i].m_ptr = NULL;
+ }
+ else process_hooks_return[i].num = 0;
+ }
+
+ /* Get the basic return(continue or stop the hook chain) */
+ if (tolua_getnumber(L, -size, 0))
+ {
+ lua_settop(L, oldtop);
+ return (TRUE);
+ }
+ if (process_hooks_restart)
+ {
+ c = hooks_heads[h_idx];
+ process_hooks_restart = FALSE;
+ }
+ else
+ c = c->next;
+ lua_settop(L, oldtop);
+ }
+ else
+ {
+ msg_format("Unkown hook type %d, name %s", c->type, c->name);
+ }
+ }
+
+ return FALSE;
+}
+
+bool process_hooks_ret(int h_idx, char *ret, char *fmt, ...)
+{
+ va_list ap;
+ bool r;
+
+ va_start(ap, fmt);
+ r = vprocess_hooks_return (h_idx, ret, fmt, &ap);
+ va_end(ap);
+ return (r);
+}
+
+bool process_hooks(int h_idx, char *fmt, ...)
+{
+ va_list ap;
+ bool ret;
+
+ va_start(ap, fmt);
+ ret = vprocess_hooks_return (h_idx, "", fmt, &ap);
+ va_end(ap);
+ return (ret);
+}
+
+/******** Plots & Quest stuff ********/
+
+static void quest_describe(int q_idx)
+{
+ int i = 0;
+
+ while ((i < 10) && (quest[q_idx].desc[i][0] != '\0'))
+ {
+ cmsg_print(TERM_YELLOW, quest[q_idx].desc[i++]);
+ }
+}
+
+/* Catch-all quest hook */
+bool quest_null_hook(int q)
+{
+ /* Do nothing */
+ return (FALSE);
+}
+
+/************************** Random Quests *************************/
+#include "q_rand.c"
+
+/**************************** Main plot ***************************/
+#include "q_main.c"
+#include "q_one.c"
+#include "q_ultrag.c"
+#include "q_ultrae.c"
+
+/**************************** Bree plot ***************************/
+#include "q_thief.c"
+#include "q_hobbit.c"
+#include "q_troll.c"
+#include "q_wight.c"
+#include "q_nazgul.c"
+#include "q_shroom.c"
+
+/*************************** Lorien plot **************************/
+#include "q_wolves.c"
+#include "q_spider.c"
+#include "q_poison.c"
+
+/************************** Gondolin plot *************************/
+#include "q_dragons.c"
+#include "q_eol.c"
+#include "q_nirna.c"
+#include "q_invas.c"
+
+/************************* Minas Anor plot ************************/
+#include "q_haunted.c"
+#include "q_betwen.c"
+
+/************************* Khazad-dum plot ************************/
+#include "q_evil.c"
+
+/*************************** Other plot ***************************/
+#include "q_narsil.c"
+#include "q_thrain.c"
diff --git a/src/plots.h b/src/plots.h
new file mode 100644
index 00000000..7f89c4ce
--- /dev/null
+++ b/src/plots.h
@@ -0,0 +1,47 @@
+/* File: plots.h */
+
+/* Purpose: extern plots declarations */
+
+extern bool quest_null_hook(int q);
+
+/******* Random Quests ********/
+extern bool is_randhero(int level);
+extern bool quest_random_init_hook(int q_idx);
+
+/******* Plot main ********/
+extern bool quest_necro_init_hook(int q_idx);
+extern bool quest_one_init_hook(int q_idx);
+extern bool quest_sauron_init_hook(int q_idx);
+extern bool quest_morgoth_init_hook(int q_idx);
+extern bool quest_ultra_good_init_hook(int q_idx);
+extern bool quest_ultra_evil_init_hook(int q_idx);
+
+/******* Plot Bree *********/
+extern bool quest_thieves_init_hook(int q_idx);
+extern bool quest_hobbit_init_hook(int q_idx);
+extern bool quest_troll_init_hook(int q_idx);
+extern bool quest_wight_init_hook(int q_idx);
+extern bool quest_nazgul_init_hook(int q_idx);
+extern bool quest_shroom_init_hook(int q_idx);
+
+/******* Plot Lorien *********/
+extern bool quest_wolves_init_hook(int q_idx);
+extern bool quest_spider_init_hook(int q_idx);
+extern bool quest_poison_init_hook(int q_idx);
+
+/******* Plot Gondolin *********/
+extern bool quest_dragons_init_hook(int q_idx);
+extern bool quest_eol_init_hook(int q_idx);
+extern bool quest_nirnaeth_init_hook(int q_idx);
+extern bool quest_invasion_init_hook(int q_idx);
+
+/******* Plot Minas Anor *********/
+extern bool quest_haunted_init_hook(int q_idx);
+extern bool quest_between_init_hook(int q_idx);
+
+/******* Plot Khazad-dum *********/
+extern bool quest_evil_init_hook(int q_idx);
+
+/******* Plot Other *********/
+extern bool quest_narsil_init_hook(int q_idx);
+extern bool quest_thrain_init_hook(int q_idx);
diff --git a/src/powers.c b/src/powers.c
new file mode 100644
index 00000000..d9a41561
--- /dev/null
+++ b/src/powers.c
@@ -0,0 +1,1412 @@
+/* File: powers.c */
+
+/* Purpose: Powers */
+
+/*
+ * Copyright (c) 2001 James E. Wilson, Robert A. Koeneke, DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Note: return value indicates the amount of mana to use
+ */
+bool power_chance(power_type *x_ptr)
+{
+ bool use_hp = FALSE;
+ int diff = x_ptr->diff;
+
+ /* Always true ? */
+ if (!x_ptr->cost) return TRUE;
+
+ /* Not enough mana - use hp */
+ if (p_ptr->csp < x_ptr->cost) use_hp = TRUE;
+
+ /* Power is not available yet */
+ if (p_ptr->lev < x_ptr->level)
+ {
+ msg_format("You need to attain level %d to use this power.", x_ptr->level);
+ energy_use = 0;
+ return (FALSE);
+ }
+
+ /* Too confused */
+ else if (p_ptr->confused)
+ {
+ msg_print("You are too confused to use this power.");
+ energy_use = 0;
+ return (FALSE);
+ }
+
+ /* Risk death? */
+ else if (use_hp && (p_ptr->chp < x_ptr->cost))
+ {
+ if (!(get_check("Really use the power in your weakened state? ")))
+ {
+ energy_use = 0;
+ return (FALSE);
+ }
+ }
+
+ /* Else attempt to do it! */
+
+ if (p_ptr->stun)
+ {
+ diff += p_ptr->stun;
+ }
+ else if (p_ptr->lev > x_ptr->level)
+ {
+ int lev_adj = ((p_ptr->lev - x_ptr->level) / 3);
+ if (lev_adj > 10) lev_adj = 10;
+ diff -= lev_adj;
+ }
+
+ if (diff < 5) diff = 5;
+
+ /* take time and pay the price */
+ if (use_hp)
+ {
+ take_hit(((x_ptr->cost / 2) + (randint(x_ptr->cost / 2))),
+ "concentrating too hard");
+ }
+ else
+ {
+ p_ptr->csp -= (x_ptr->cost / 2 ) + (randint(x_ptr->cost / 2));
+ }
+ energy_use = 100;
+
+ /* Redraw mana and hp */
+ p_ptr->redraw |= (PR_HP | PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Success? */
+ if (randint(p_ptr->stat_cur[x_ptr->stat]) >=
+ ((diff / 2) + randint(diff / 2)))
+ {
+ return (TRUE);
+ }
+
+ if (flush_failure) flush();
+ msg_print("You've failed to concentrate hard enough.");
+
+ return (FALSE);
+}
+
+static void power_activate(int power)
+{
+ s16b plev = p_ptr->lev;
+ char ch = 0;
+ int amber_power = 0;
+ int dir = 0, dummy;
+ object_type *q_ptr;
+ object_type forge;
+ int ii = 0, ij = 0;
+ /* char out_val[80]; */
+ /* cptr p = "Power of the flame: "; */
+ cptr q, s;
+ power_type *x_ptr = &powers_type[power], x_ptr_foo;
+ int x, y;
+ cave_type *c_ptr;
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+
+ if (!power_chance(x_ptr)) return;
+
+ switch (power)
+ {
+ case PWR_BALROG:
+ {
+ set_mimic(p_ptr->lev / 2, resolve_mimic_name("Balrog"), p_ptr->lev);
+ }
+ break;
+ case PWR_BEAR:
+ {
+ set_mimic(150 + (p_ptr->lev * 10) , resolve_mimic_name("Bear"), p_ptr->lev);
+ }
+ break;
+ case PWR_COMPANION:
+ {
+ if (!can_create_companion())
+ {
+ msg_print("You cannot have more companions.");
+ }
+ else
+ {
+ monster_type *m_ptr;
+ int ii, jj;
+
+ msg_print("Select the friendly monster:");
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ if (m_ptr->status != MSTATUS_PET)
+ {
+ msg_print("You cannot convert this monster.");
+ return;
+ }
+
+ m_ptr->status = MSTATUS_COMPANION;
+ }
+ }
+ }
+ break;
+ case PWR_MERCHANT:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("[A]ppraise item, [W]arp item or [I]dentify item? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'A' || ch == 'a')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'W' || ch == 'w')
+ {
+ amber_power = 2;
+ break;
+ }
+
+ if (ch == 'I' || ch == 'i')
+ {
+ amber_power = 3;
+ break;
+ }
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 5;
+ x_ptr_foo.cost = 5;
+ x_ptr_foo.stat = A_INT;
+ x_ptr_foo.diff = 5;
+ if (power_chance(&x_ptr_foo))
+ {
+ /* Appraise an object */
+ int idx;
+ cptr q, s;
+
+ /* Get the item */
+ q = "Appraise which item? ";
+ s = "You have nothing to appraise.";
+ if (get_item(&idx, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR)))
+ {
+ object_type *o_ptr;
+ char out_val[80], value[16];
+
+ /* The item is in the pack */
+ if (idx >= 0) o_ptr = &p_ptr->inventory[idx];
+ /* The item is on the floor */
+ else o_ptr = &o_list[0 - idx];
+
+ /* Appraise it */
+ sprintf(value, "%i au", (int)object_value(o_ptr));
+
+ /* Inscribe the value */
+ /* Get the original inscription */
+ if (o_ptr->note)
+ {
+ strcpy(out_val, quark_str(o_ptr->note));
+ strcat(out_val, " ");
+ }
+ else
+ out_val[0] = '\0';
+
+ strcat(out_val, value);
+
+ /* Save the new inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+ }
+ }
+ if (amber_power == 2)
+ {
+ x_ptr_foo.level = 15;
+ x_ptr_foo.cost = 10;
+ x_ptr_foo.stat = A_INT;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ int chest, item;
+ cptr q1, s1, q2, s2;
+ u32b flag = (USE_EQUIP | USE_INVEN | USE_FLOOR);
+ object_type *o1_ptr = &p_ptr->inventory[0], *o2_ptr = &p_ptr->inventory[1];
+ int ok = 0;
+
+ q1 = "Select a chest! ";
+ s1 = "You need a chest to warp items.";
+
+ q2 = "Warp which item? ";
+ s2 = "You have nothing to warp.";
+
+ item_tester_tval = TV_CHEST;
+
+ /* Get the chest */
+ if (get_item(&chest, q1, s1, flag))
+ {
+ if (chest >= 0) o1_ptr = &p_ptr->inventory[chest];
+ else o1_ptr = &o_list[0 - chest];
+
+ /* Is the chest disarmed? */
+ if (o1_ptr->pval > 0)
+ msg_print("This chest may be trapped.");
+
+ /* Is it ruined? */
+ else if (k_info[o1_ptr->k_idx].level <= 0)
+ msg_print("This chest is broken.");
+
+ /* Is it empty? */
+ else if (o1_ptr->pval2 >= (o1_ptr->sval % SV_CHEST_MIN_LARGE) * 2)
+ msg_print("This chest is full.");
+
+ else ok = 1;
+ }
+
+ /* Get the item */
+ if (ok && get_item(&item, q2, s2, flag))
+ {
+ ok = 0;
+
+ if (item >= 0) o2_ptr = &p_ptr->inventory[item];
+ else o2_ptr = &o_list[0 - item];
+
+ /* Is the item cursed? */
+ if ((item >= INVEN_WIELD) && cursed_p(o2_ptr))
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Is it the same chest? */
+ if (item == chest)
+ msg_print("You can't put a chest into itself.");
+
+ /* Is it another chest? */
+ if (o2_ptr->tval == TV_CHEST)
+ msg_print("You can't put a chest into another one.");
+
+ /* Try to use the power */
+ else ok = 1;
+ }
+
+ if (ok)
+ {
+ int tmp, level;
+
+ /* Calculate the level of objects */
+ tmp = o1_ptr->pval;
+
+ /* Get the level of the current object */
+ /* Cursed items/cheap items always break */
+ if (k_info[o2_ptr->k_idx].cost < 20) level = 0;
+ /* Not-so-cheap items break 90% of the time */
+ else if (k_info[o2_ptr->k_idx].cost < 100) level = 1;
+ else level = k_info[o2_ptr->k_idx].level;
+
+ /* Break some items */
+ if (randint(10) > level)
+ msg_print("The item disappeared!");
+ else
+ {
+ level /= (o1_ptr->sval % SV_CHEST_MIN_LARGE) * 2;
+
+ /* Increase the number of objects in
+ * the chest */
+ o1_ptr->pval2++;
+
+ /* Set the level of chest */
+ tmp = tmp - level;
+ o1_ptr->pval = tmp;
+ }
+
+ /* Destroy item */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ inven_item_increase(0 - item, -1);
+ inven_item_describe(0 - item);
+ inven_item_optimize(0 - item);
+ }
+ }
+ }
+ }
+ if (amber_power == 3)
+ {
+ x_ptr_foo.level = 30;
+ x_ptr_foo.cost = 20;
+ x_ptr_foo.stat = A_INT;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ ident_spell();
+ }
+ }
+ break;
+ case PWR_LAY_TRAP:
+ {
+ do_cmd_set_trap();
+ }
+ break;
+ case PWR_MAGIC_MAP:
+ {
+ msg_print("You sense the world around you.");
+ map_area();
+ }
+ break;
+
+ case PWR_PASSWALL:
+ {
+ if (!get_aim_dir(&dir))
+ break;
+ if (passwall(dir, TRUE))
+ msg_print("A passage opens, and you step through.");
+ else
+ msg_print("There is no wall there!");
+ }
+ break;
+
+ case PWR_COOK_FOOD:
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, 21);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+ msg_print("You cook some food.");
+ }
+ break;
+
+ case PWR_UNFEAR:
+ {
+ msg_print("You play tough.");
+ (void)set_afraid(0);
+ }
+ break;
+
+ case PWR_BERSERK:
+ {
+ msg_print("RAAAGH!");
+ (void)set_afraid(0);
+
+ (void)set_shero(p_ptr->shero + 10 + randint(plev));
+ (void)hp_player(30);
+ }
+ break;
+
+ case PWR_EXPL_RUNE:
+ {
+ msg_print("You carefully set an explosive rune...");
+ explosive_rune();
+ }
+ break;
+
+ case PWR_STM:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You bash at a stone wall.");
+ (void)wall_to_mud(dir);
+ }
+ break;
+
+ case PWR_ROHAN:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("Use [F]lash aura or [L]ight speed jump? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'F' || ch == 'f')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'L' || ch == 'l')
+ {
+ amber_power = 2;
+ break;
+ }
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 1;
+ x_ptr_foo.cost = 9;
+ x_ptr_foo.stat = A_CHR;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (!(get_aim_dir(&dir))) break;
+ msg_print("You flash a bright aura.");
+ if (p_ptr->lev < 10)
+ fire_bolt(GF_CONFUSION, dir, plev*2);
+ else
+ fire_ball(GF_CONFUSION, dir, plev*2, 2);
+ }
+ }
+ if (amber_power == 2)
+ {
+ x_ptr_foo.level = 30;
+ x_ptr_foo.cost = 30;
+ x_ptr_foo.stat = A_WIS;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ (void)set_light_speed(p_ptr->lightspeed + 3);
+ }
+ }
+ break;
+
+
+ case PWR_POIS_DART:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You throw a dart of poison.");
+ fire_bolt(GF_POIS, dir, plev);
+ }
+ break;
+
+ case PWR_DETECT_TD:
+ {
+ msg_print("You examine your surroundings.");
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_MAGIC_MISSILE:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You cast a magic missile.");
+ fire_bolt_or_beam(10, GF_MISSILE, dir,
+ damroll(3 + ((plev - 1) / 5), 4));
+ }
+ break;
+
+ case PWR_THUNDER:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("Use [T]hunder strike, [R]ide the straight road, go [B]ack in town? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'T' || ch == 't')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'R' || ch == 'r')
+ {
+ amber_power = 2;
+ break;
+ }
+
+ if (ch == 'B' || ch == 'b')
+ {
+ amber_power = 3;
+ break;
+ }
+
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 1;
+ x_ptr_foo.cost = p_ptr->lev;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_format("You conjure up thunder!");
+ fire_beam(GF_ELEC, dir, p_ptr->lev * 2);
+ fire_beam(GF_SOUND, dir, p_ptr->lev * 2);
+ p_ptr->energy -= 100;
+ }
+ }
+ if (amber_power == 2)
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels ...");
+ break;
+ }
+ x_ptr_foo.level = 3;
+ x_ptr_foo.cost = 15;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ msg_print("You enter the straight road and fly beside the world. Where to exit?");
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+ if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev*20 + 2) || !(cave[ij][ii].info & CAVE_MARK))
+ {
+ msg_print("You fail to exit correctly!");
+ p_ptr->energy -= 100;
+ teleport_player(10);
+ }
+ else teleport_player_to(ij, ii);
+ }
+
+ }
+ if (amber_power == 3)
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No recall on special levels..");
+ break;
+ }
+ x_ptr_foo.level = 7;
+ x_ptr_foo.cost = 30;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (dun_level == 0)
+ msg_print("You are already in town!");
+ else
+ {
+ msg_print("You enter the straight road and fly beside the world.");
+ p_ptr->energy -= 100;
+ p_ptr->word_recall = 1;
+ }
+ }
+ }
+ break;
+
+ case PWR_GROW_TREE:
+ {
+ msg_print("You make the trees grow!");
+ grow_trees((plev / 8 < 1) ? 1 : plev / 8);
+ }
+ break;
+
+ case PWR_DEATHMOLD:
+ do_cmd_immovable_special();
+ break;
+
+ case PWR_BR_COLD:
+ {
+ msg_print("You breathe cold...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_COLD, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_BR_CHAOS:
+ {
+ msg_print("You breathe chaos...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_BR_ELEM:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_format("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, (p_ptr->lev)*2,
+ ((p_ptr->lev) / 15) + 1);
+ }
+ break;
+
+ case PWR_SUMMON_MONSTER:
+ {
+ do_cmd_beastmaster();
+ }
+ break;
+
+ case PWR_WRECK_WORLD:
+ msg_print("The power of Eru Iluvatar flows through you!");
+ msg_print("The world changes!");
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ break;
+
+ case PWR_VAMPIRISM:
+ {
+ /* Only works on adjacent monsters */
+ if (!get_rep_dir(&dir)) break; /* was get_aim_dir */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You bite into thin air!");
+ break;
+ }
+
+ msg_print("You grin and bare your fangs...");
+ dummy = plev + randint(plev) * MAX(1, plev / 10); /* Dmg */
+ if (drain_life(dir, dummy))
+ {
+ if (p_ptr->food < PY_FOOD_FULL)
+ /* No heal if we are "full" */
+ (void)hp_player(dummy);
+ else
+ msg_print("You were not hungry.");
+ /* Gain nutritional sustenance: 150/hp drained */
+ /* A Food ration gives 5000 food points (by contrast) */
+ /* Don't ever get more than "Full" this way */
+ /* But if we ARE Gorged, it won't cure us */
+ dummy = p_ptr->food + MIN(5000, 100 * dummy);
+ if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */
+ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy);
+ }
+ else
+ msg_print("Yechh. That tastes foul.");
+ }
+ break;
+
+ case PWR_SCARE:
+ {
+ msg_print("You emit an eldritch howl!");
+ if (!get_aim_dir(&dir)) break;
+ (void)fear_monster(dir, plev);
+ }
+ break;
+
+ case PWR_REST_LIFE:
+ {
+ msg_print("You attempt to restore your lost energies.");
+ (void)restore_level();
+ }
+ break;
+
+ case PWR_HYPNO:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ object_type *q_ptr;
+ object_type forge;
+
+ msg_print("Hypnotize which pet?");
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx)
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if ((r_ptr->flags1 & RF1_NEVER_MOVE) && (m_ptr->status == MSTATUS_PET) && (!(r_ptr->flags9 & RF9_SPECIAL_GENE)))
+ {
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1));
+ q_ptr->number = 1;
+ q_ptr->pval = m_ptr->r_idx;
+ q_ptr->pval2 = m_ptr->hp;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ drop_near(q_ptr, 0, y, x);
+
+ delete_monster(y, x);
+ health_who = 0;
+ }
+ else
+ msg_print("You can only hypnotize monsters that can't move.");
+ }
+ else msg_print("There is no pet here!");
+ }
+ break;
+
+ case PWR_UNHYPNO:
+ {
+ monster_type *m_ptr;
+ int m_idx;
+ int item, x, y, d;
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_HYPNOS;
+
+ /* Get an item */
+ q = "Awaken which monster? ";
+ s = "You have no monster to awaken.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return;
+
+ o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d, 0);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ m_ptr = &m_list[m_idx];
+ m_ptr->hp = o_ptr->pval2;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ break;
+
+ case PWR_NECRO:
+ {
+ do_cmd_necromancer();
+ break;
+ }
+ case PWR_INCARNATE:
+ {
+ do_cmd_integrate_body();
+ break;
+ }
+
+ case PWR_SPIT_ACID:
+ {
+ msg_print("You spit acid...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_ACID, dir, p_ptr->lev, 1 + (p_ptr->lev / 30));
+ }
+ break;
+
+ case PWR_BR_FIRE:
+ {
+ msg_print("You breathe fire...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_FIRE, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_HYPN_GAZE:
+ {
+ msg_print("Your eyes look mesmerising...");
+ if (get_aim_dir(&dir))
+ (void) charm_monster(dir, p_ptr->lev);
+ }
+ break;
+
+ case PWR_TELEKINES:
+ {
+ msg_print("You concentrate...");
+ if (get_aim_dir(&dir))
+ fetch(dir, p_ptr->lev * 10, TRUE);
+ }
+ break;
+
+ case PWR_VTELEPORT:
+ {
+ msg_print("You concentrate...");
+ teleport_player(10 + 4*(p_ptr->lev));
+ }
+ break;
+
+ case PWR_MIND_BLST:
+ {
+ msg_print("You concentrate...");
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_PSI, dir, damroll(3 + ((p_ptr->lev - 1) / 5), 3));
+ }
+ break;
+
+ case PWR_RADIATION:
+ {
+ msg_print("Radiation flows from your body!");
+ fire_ball(GF_NUKE, 0, (p_ptr->lev * 2), 3 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_SMELL_MET:
+ {
+ (void)detect_treasure(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_SMELL_MON:
+ {
+ (void)detect_monsters_normal(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_BLINK:
+ {
+ teleport_player(10);
+ }
+ break;
+
+ case PWR_EAT_ROCK:
+ {
+ int x, y, ox, oy;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) break;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (cave_floor_bold(y, x))
+ {
+ msg_print("You bite into thin air!");
+ break;
+ }
+ else if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT) || (c_ptr->feat == FEAT_MOUNTAIN))
+ {
+ msg_print("Ouch! This wall is harder than your teeth!");
+ break;
+ }
+ else if (c_ptr->m_idx)
+ {
+ msg_print("There's something in the way!");
+ break;
+ }
+ else if (c_ptr->feat == FEAT_TREES)
+ {
+ msg_print("You don't like the woody taste!");
+ break;
+ }
+ else
+ {
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_RUBBLE))
+ {
+ (void)set_food(p_ptr->food + 3000);
+ }
+ else if ((c_ptr->feat >= FEAT_MAGMA) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K))
+ {
+ (void)set_food(p_ptr->food + 5000);
+ }
+ else if ((c_ptr->feat >= FEAT_SANDWALL) &&
+ (c_ptr->feat <= FEAT_SANDWALL_K))
+ {
+ (void)set_food(p_ptr->food + 500);
+ }
+ else
+ {
+ msg_print("This granite is very filling!");
+ (void)set_food(p_ptr->food + 10000);
+ }
+ }
+ (void)wall_to_mud(dir);
+
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ lite_spot(p_ptr->py, p_ptr->px);
+ lite_spot(oy, ox);
+
+ verify_panel();
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->update |= (PU_DISTANCE);
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ break;
+
+ case PWR_SWAP_POS:
+ {
+ if (!get_aim_dir(&dir)) return;
+ (void)teleport_swap(dir);
+ }
+ break;
+
+ case PWR_SHRIEK:
+ {
+ (void)fire_ball(GF_SOUND, 0, 4 * p_ptr->lev, 8);
+ (void)aggravate_monsters(0);
+ }
+ break;
+
+ case PWR_ILLUMINE:
+ {
+ (void)lite_area(damroll(2, (p_ptr->lev / 2)), (p_ptr->lev / 10) + 1);
+ }
+ break;
+
+ case PWR_DET_CURSE:
+ {
+ int i;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+ if (!cursed_p(o_ptr)) continue;
+
+ if (!o_ptr->sense) o_ptr->sense = SENSE_CURSED;
+ }
+ }
+ break;
+
+ case PWR_POLYMORPH:
+ {
+ do_poly_self();
+ }
+ break;
+
+ case PWR_MIDAS_TCH:
+ {
+ (void)alchemy();
+ }
+ break;
+
+ case PWR_GROW_MOLD:
+ {
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);
+ }
+ }
+ break;
+
+ case PWR_RESIST:
+ {
+ int num = p_ptr->lev / 10;
+ int dur = randint(20) + 20;
+
+ if (rand_int(5) < num)
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid + dur);
+ num--;
+ }
+ if (rand_int(4) < num)
+ {
+ (void)set_oppose_elec(p_ptr->oppose_elec + dur);
+ num--;
+ }
+ if (rand_int(3) < num)
+ {
+ (void)set_oppose_fire(p_ptr->oppose_fire + dur);
+ num--;
+ }
+ if (rand_int(2) < num)
+ {
+ (void)set_oppose_cold(p_ptr->oppose_cold + dur);
+ num--;
+ }
+ if (num)
+ {
+ (void)set_oppose_pois(p_ptr->oppose_pois + dur);
+ num--;
+ }
+ }
+ break;
+
+ case PWR_EARTHQUAKE:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ }
+ break;
+
+ case PWR_EAT_MAGIC:
+ {
+ object_type * o_ptr;
+ int lev, item;
+
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Drain which item? ";
+ s = "You have nothing to drain.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) break;
+
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ lev = k_info[o_ptr->k_idx].level;
+
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (!o_ptr->timeout)
+ {
+ msg_print("You can't absorb energy from a discharged rod.");
+ }
+ else
+ {
+ p_ptr->csp += o_ptr->timeout;
+ o_ptr->timeout = 0;
+ }
+ }
+ else
+ {
+ if (o_ptr->pval > 0)
+ {
+ p_ptr->csp += o_ptr->pval * lev;
+ o_ptr->pval = 0;
+ }
+ else
+ {
+ msg_print("There's no energy there to absorb!");
+ }
+ o_ptr->ident |= IDENT_EMPTY;
+ }
+
+ if (p_ptr->csp > p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ }
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN);
+ }
+ break;
+
+ case PWR_WEIGH_MAG:
+ {
+ report_magics();
+ }
+ break;
+
+ case PWR_STERILITY:
+ {
+ /* Fake a population explosion. */
+ msg_print("You suddenly have a headache!");
+ take_hit(randint(30) + 30, "the strain of forcing abstinence");
+ num_repro += MAX_REPRO;
+ }
+ break;
+
+ case PWR_PANIC_HIT:
+ {
+ int x, y;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ if (cave[y][x].m_idx)
+ {
+ py_attack(y, x, -1);
+ teleport_player(30);
+ }
+ else
+ {
+ msg_print("You don't see any monster in this direction.");
+ msg_print(NULL);
+ }
+ }
+ break;
+
+ case PWR_DAZZLE:
+ {
+ stun_monsters(p_ptr->lev * 4);
+ confuse_monsters(p_ptr->lev * 4);
+ turn_monsters(p_ptr->lev * 4);
+ }
+ break;
+
+ case PWR_DARKRAY:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_beam(GF_LITE, dir, 2*p_ptr->lev);
+ }
+ break;
+
+ case PWR_RECALL:
+ {
+ if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")))
+ {
+ recall_player(21, 15);
+ }
+ }
+ break;
+
+ case PWR_BANISH:
+ {
+ int x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You sense no evil there!");
+ break;
+ }
+
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags3 & RF3_EVIL)
+ {
+ /* Delete the monster, rather than killing it. */
+ delete_monster_idx(c_ptr->m_idx);
+ msg_print("The evil creature vanishes in a puff of sulphurous smoke!");
+ }
+ else
+ {
+ msg_print("Your invocation is ineffectual!");
+ }
+ }
+ break;
+
+ case PWR_COLD_TOUCH:
+ {
+ int x, y;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You wave your hands in the air.");
+ break;
+ }
+ fire_bolt(GF_COLD, dir, 2 * (p_ptr->lev));
+ }
+ break;
+
+ case PWR_LAUNCHER:
+ {
+ /* Gives a multiplier of 2 at first, up to 5 at 48th */
+ p_ptr->throw_mult = 2 + (p_ptr->lev / 16);
+ do_cmd_throw();
+ p_ptr->throw_mult = 1;
+ }
+ break;
+
+ case PWR_DODGE:
+ use_ability_blade();
+ break;
+
+ default:
+ if (!process_hooks(HOOK_ACTIVATE_POWER, "(d)", power))
+ {
+ msg_format("Warning power_activate() called with invalid power(%d).", power);
+ energy_use = 0;
+ }
+ break;
+ }
+
+ p_ptr->redraw |= (PR_HP | PR_MANA);
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Print a batch of power.
+ */
+static void print_power_batch(int *p, int start, int max, bool mode)
+{
+ char buff[80];
+ power_type* spell;
+ int i = start, j = 0;
+
+ if (mode) prt(format(" %-31s Level Mana Fail", "Name"), 1, 20);
+
+ for (i = start; i < (start + 20); i++)
+ {
+ if (i >= max) break;
+
+ spell = &powers_type[p[i]];
+
+ sprintf(buff, " %c-%3d) %-30s %5d %4d %s@%d", I2A(j), p[i] + 1, spell->name,
+ spell->level, spell->cost, stat_names[spell->stat], spell->diff);
+
+ if (mode) prt(buff, 2 + j, 20);
+ j++;
+ }
+ if (mode) prt("", 2 + j, 20);
+ prt(format("Select a power (a-%c), +/- to scroll:", I2A(j - 1)), 0, 0);
+}
+
+
+/*
+ * List powers and ask to pick one.
+ */
+
+static power_type* select_power(int *x_idx)
+{
+ char which;
+ int max = 0, i, start = 0;
+ power_type* ret;
+ bool mode = FALSE;
+ int *p;
+
+ C_MAKE(p, power_max, int);
+ /* Count the max */
+ for (i = 0; i < power_max; i++)
+ {
+ if (p_ptr->powers[i])
+ {
+ p[max++] = i;
+ }
+ }
+
+ /* Exit if there aren't powers */
+ if (max == 0)
+ {
+ *x_idx = -1;
+ ret = NULL;
+ msg_print("You don't have any special powers.");
+ }
+ else
+ {
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_power_batch(p, start, max, mode);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ *x_idx = -1;
+ ret = NULL;
+ break;
+ }
+ else if (which == '*' || which == '?' || which == ' ')
+ {
+ mode = (mode) ? FALSE : TRUE;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '+')
+ {
+ start += 20;
+ if (start >= max) start -= 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '-')
+ {
+ start -= 20;
+ if (start < 0) start += 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= max)
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+
+ *x_idx = p[start + A2I(which)];
+ ret = &powers_type[p[start + A2I(which)]];
+ break;
+ }
+ }
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ C_FREE(p, power_max, int);
+
+ return ret;
+}
+
+/* Ask & execute a power */
+void do_cmd_power()
+{
+ int x_idx;
+ power_type *x_ptr;
+ bool push = TRUE;
+
+ /* Get the skill, if available */
+ if (repeat_pull(&x_idx))
+ {
+ if ((x_idx < 0) || (x_idx >= power_max)) return;
+ x_ptr = &powers_type[x_idx];
+ push = FALSE;
+ }
+ else if (!command_arg) x_ptr = select_power(&x_idx);
+ else
+ {
+ x_idx = command_arg - 1;
+ if ((x_idx < 0) || (x_idx >= power_max)) return;
+ x_ptr = &powers_type[x_idx];
+ }
+
+ if (x_ptr == NULL) return;
+
+ if (push) repeat_push(x_idx);
+
+ if (p_ptr->powers[x_idx])
+ power_activate(x_idx);
+ else
+ msg_print("You do not have access to this power.");
+}
diff --git a/src/q_betwen.c b/src/q_betwen.c
new file mode 100644
index 00000000..f156b843
--- /dev/null
+++ b/src/q_betwen.c
@@ -0,0 +1,190 @@
+#undef cquest
+#define cquest (quest[QUEST_BETWEEN])
+
+bool quest_between_move_hook(char *fmt)
+{
+ s32b y;
+ s32b x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ /* The tower of Turgon */
+ if ((c_ptr->feat == FEAT_SHOP) && (c_ptr->special == 27))
+ {
+ cmsg_print(TERM_YELLOW, "Turgon is there.");
+ cmsg_print(TERM_YELLOW, "'Ah, thank you, noble hero! Now please return to Minas Anor to finish the link.'");
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ return TRUE;
+ }
+
+ /* Only 1 ambush */
+ if (cquest.data[0]) return (FALSE);
+
+ if (!p_ptr->wild_mode)
+ {
+ if (p_ptr->wilderness_y > 19) return (FALSE);
+ }
+ else
+ {
+ if (p_ptr->py > 19) return (FALSE);
+ }
+
+ /* Mark as entered */
+ cquest.data[0] = TRUE;
+
+ p_ptr->wild_mode = FALSE;
+ p_ptr->inside_quest = QUEST_BETWEEN;
+ p_ptr->leaving = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Looks like a full wing of thunderlords ambushes you!");
+ cmsg_print(TERM_YELLOW, "Trone steps forth and speaks: 'The secret of the Void Jumpgates");
+ cmsg_print(TERM_YELLOW, "will not be used by any but the thunderlords!'");
+
+ return FALSE;
+}
+bool quest_between_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_BETWEEN) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "between.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ /* Otherwise instadeath */
+ energy_use = 0;
+
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ return TRUE;
+}
+bool quest_between_finish_hook(char *fmt)
+{
+ s32b q_idx;
+ object_type forge, *q_ptr;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_BETWEEN) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Ah you finally arrived, I hope your travel wasn't too hard.", 8, 0);
+ c_put_str(TERM_YELLOW, "As a reward you can freely use the Void Jumpgates for quick travel.", 9, 0);
+ c_put_str(TERM_YELLOW, "Oh and take that horn, it shall serve you well.", 10, 0);
+
+ /* prepare the reward */
+ q_ptr = &forge;
+ object_prep(q_ptr, test_item_name("& Golden Horn~ of the Thunderlords"));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+
+
+ /* Mega-Hack -- Actually create the Golden Horn of the Thunderlords */
+ k_allow_special[test_item_name("& Golden Horn~ of the Thunderlords")] = TRUE;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ k_allow_special[test_item_name("& Golden Horn~ of the Thunderlords")] = FALSE;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_between_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_between_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_BETWEEN) return FALSE;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_NEUTRAL) mcnt++;
+ }
+
+ if (mcnt < 2)
+ {
+ cmsg_print(TERM_YELLOW, "You can escape now.");
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ cave[p_ptr->py][p_ptr->px].special = 0;
+
+ return FALSE;
+ }
+
+
+ return FALSE;
+}
+bool quest_between_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You established a permanent void jumpgates liaison between Minas Anor and Gondolin,");
+ fprintf(hook_file, "\n thus allowing the last alliance to exist.");
+ }
+ return (FALSE);
+}
+bool quest_between_forbid_hook(char *fmt)
+{
+ s32b q_idx;
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_BETWEEN) return (FALSE);
+
+ if (p_ptr->lev < 45)
+ {
+ c_put_str(TERM_WHITE, "I fear you are not ready for the next quest, come back later.", 8, 0);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+bool quest_between_init_hook(int q)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MOVE, quest_between_move_hook, "between_move");
+ add_hook(HOOK_GEN_QUEST, quest_between_gen_hook, "between_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_between_finish_hook, "between_finish");
+ add_hook(HOOK_MONSTER_DEATH, quest_between_death_hook, "between_death");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_between_dump_hook, "between_dump");
+ add_hook(HOOK_INIT_QUEST, quest_between_forbid_hook, "between_forbid");
+ return (FALSE);
+}
diff --git a/src/q_dragons.c b/src/q_dragons.c
new file mode 100644
index 00000000..a1501be8
--- /dev/null
+++ b/src/q_dragons.c
@@ -0,0 +1,149 @@
+#undef cquest
+#define cquest (quest[QUEST_DRAGONS])
+
+bool quest_dragons_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_DRAGONS) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(NULL, "dragons.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some columns */
+ for (i = 35; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ /* Bar columns on even squares so the whole level is guaranteed to be
+ accessible */
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags % 2) && !(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ --i;
+ cave_set_feat(y, x, FEAT_MOUNTAIN);
+ }
+ }
+
+ /* Place some random dragons */
+ for (i = 25; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ /* blue, white, red, black, bronze, gold, green, multi-hued */
+ int baby_dragons[8] = {163, 164, 167, 166, 218, 219, 165, 204};
+ int young_dragons[8] = {459, 460, 563, 546, 462, 559, 461, 556};
+ int mature_dragons[8] = {560, 549, 589, 592, 562, 590, 561, 593};
+ int happy_dragons[8] = {601, 617, 644, 624, 602, 645, 618, 675};
+
+ int chance, dragon, color;
+
+ color = rand_int(8);
+ chance = rand_int(100);
+ if (chance == 0)
+ dragon = happy_dragons[color];
+ else if (chance < 33)
+ dragon = baby_dragons[color];
+ else if (chance < 66)
+ dragon = young_dragons[color];
+ else
+ dragon = mature_dragons[color];
+
+ --i;
+ place_monster_one(y, x, dragon, 0, magik(33), MSTATUS_ENEMY);
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool quest_dragons_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_DRAGONS) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_dragons_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_dragons_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Gondolin is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool quest_dragons_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_DRAGONS) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for killing the dragons!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the cave as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_EOL;
+
+ return TRUE;
+}
+
+bool quest_dragons_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_dragons_death_hook, "dragons_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_dragons_finish_hook, "dragons_finish");
+ add_hook(HOOK_GEN_QUEST, quest_dragons_gen_hook, "dragons_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/q_eol.c b/src/q_eol.c
new file mode 100644
index 00000000..ef46ef5d
--- /dev/null
+++ b/src/q_eol.c
@@ -0,0 +1,194 @@
+#undef cquest
+#define cquest (quest[QUEST_EOL])
+
+bool quest_eol_gen_hook(char *fmt)
+{
+ int x, y;
+ bool done = FALSE;
+ int xsize = 50, ysize = 30, y0, x0;
+ int m_idx = 0;
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ x0 = 2 + (xsize / 2);
+ y0 = 2 + (ysize / 2);
+
+ feat_wall_outer = FEAT_WALL_OUTER;
+ feat_wall_inner = FEAT_WALL_INNER;
+
+ for (y = 0; y < 100; y++)
+ {
+ floor_type[y] = FEAT_FLOOR;
+ fill_type[y] = FEAT_WALL_OUTER;
+ }
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ while (!done)
+ {
+ int grd, roug, cutoff;
+
+ /* testing values for these parameters feel free to adjust*/
+ grd = 2 ^ (randint(4));
+
+ /* want average of about 16 */
+ roug = randint(8) * randint(4);
+
+ /* about size/2 */
+ cutoff = randint(xsize / 4) + randint(ysize / 4) + randint(xsize / 4) + randint(ysize / 4);
+
+ /* make it */
+ generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff);
+
+ /* Convert to normal format+ clean up*/
+ done = generate_fracave(y0, x0, xsize, ysize, cutoff, FALSE, TRUE);
+ }
+
+ /* Place a few traps */
+ for (x = xsize - 1; x >= 2; x--)
+ for (y = ysize - 1; y >= 2; y--)
+ {
+ if (!cave_clean_bold(y, x)) continue;
+
+ /* Place eol at the other end */
+ if (!m_idx)
+ {
+ m_allow_special[test_monster_name("Eol, the Dark Elf")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Eol, the Dark Elf"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Eol, the Dark Elf")] = FALSE;
+ }
+
+ if (magik(18))
+ {
+ place_trap(y, x);
+ }
+
+ /* Place player at one end */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+
+ return TRUE;
+}
+bool quest_eol_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_EOL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "A tragedy, but the deed needed to be done.", 8, 0);
+ c_put_str(TERM_YELLOW, "Accept my thanks, and that reward.", 9, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_DWARVEN));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->name2 = EGO_LITE_MAGI;
+ apply_magic(q_ptr, 1, FALSE, FALSE, FALSE);
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NIRNAETH;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_eol_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_eol_fail_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_EOL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "You fled ! I did not think you would flee...", 8, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FAIL, quest_eol_fail_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_eol_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ if (r_idx == test_monster_name("Eol, the Dark Elf"))
+ {
+ cmsg_print(TERM_YELLOW, "Such a sad end...");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_eol_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return FALSE;
+}
+bool quest_eol_stair_hook(char *fmt)
+{
+ monster_race *r_ptr = &r_info[test_monster_name("Eol, the Dark Elf")];
+ cptr down;
+
+ down = get_next_arg_str(fmt);
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return TRUE;
+
+ if (r_ptr->max_num)
+ {
+ if (!strcmp(down, "up"))
+ {
+ /* Flush input */
+ flush();
+
+ if (!get_check("Really abandon the quest?")) return TRUE;
+
+ cmsg_print(TERM_YELLOW, "You flee away from Eol...");
+ cquest.status = QUEST_STATUS_FAILED;
+ del_hook(HOOK_STAIR, quest_eol_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+ }
+
+ return FALSE;
+}
+bool quest_eol_init_hook(int q)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_eol_death_hook, "eol_death");
+ add_hook(HOOK_GEN_QUEST, quest_eol_gen_hook, "eol_gen");
+ add_hook(HOOK_STAIR, quest_eol_stair_hook, "eol_stair");
+ add_hook(HOOK_QUEST_FAIL, quest_eol_fail_hook, "eol_fail");
+ add_hook(HOOK_QUEST_FINISH, quest_eol_finish_hook, "eol_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_evil.c b/src/q_evil.c
new file mode 100644
index 00000000..5e8259dc
--- /dev/null
+++ b/src/q_evil.c
@@ -0,0 +1,116 @@
+#undef cquest
+#define cquest (quest[QUEST_EVIL])
+
+bool quest_evil_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_EVIL) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(NULL, "evil.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some random balrogs */
+ for (i = 6; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ place_monster_one(y, x, 996, 0, FALSE, MSTATUS_ENEMY);
+ --i;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool quest_evil_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_EVIL) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ /* TODO: change to COMPLETED and remove NULL when mayor is added */
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_FINISHED;
+ *(quest[p_ptr->inside_quest].plot) = QUEST_NULL;
+ del_hook(HOOK_MONSTER_DEATH, quest_evil_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_evil_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Khazad-Dum is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool quest_evil_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_EVIL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for saving us!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the cave as your house as a reward.", 9, 0);
+
+ /* End the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ return TRUE;
+}
+
+bool quest_evil_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_evil_death_hook, "evil_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_evil_finish_hook, "evil_finish");
+ add_hook(HOOK_GEN_QUEST, quest_evil_gen_hook, "evil_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/q_haunted.c b/src/q_haunted.c
new file mode 100644
index 00000000..d27e62e3
--- /dev/null
+++ b/src/q_haunted.c
@@ -0,0 +1,145 @@
+#undef cquest
+#define cquest (quest[QUEST_HAUNTED])
+
+bool quest_haunted_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_HAUNTED) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(NULL, "haunted.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some ghosts */
+ for (i = 12; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ place_monster_one(y, x, 477, 0, FALSE, MSTATUS_ENEMY);
+ --i;
+ }
+ }
+
+ /* Place some random monsters to haunt us */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ int monsters[22] = { 65, 100, 124, 125, 133, 231, 273, 327, 365, 416, 418,
+ 507, 508, 533, 534, 553, 554, 555, 577, 607, 622, 665};
+ int monster = monsters[rand_int(22)];
+ place_monster_one(y, x, monster, 0, FALSE, MSTATUS_ENEMY);
+ --i;
+ }
+ }
+
+ /* Place some random traps */
+ for (i = 10 + damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ --i;
+ place_trap(y, x);
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool quest_haunted_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_HAUNTED) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_haunted_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_haunted_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Minas Anor is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool quest_haunted_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_HAUNTED) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for saving us!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the building as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_BETWEEN;
+
+ return TRUE;
+}
+
+bool quest_haunted_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_haunted_death_hook, "haunted_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_haunted_finish_hook, "haunted_finish");
+ add_hook(HOOK_GEN_QUEST, quest_haunted_gen_hook, "haunted_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/q_hobbit.c b/src/q_hobbit.c
new file mode 100644
index 00000000..0dd4ef58
--- /dev/null
+++ b/src/q_hobbit.c
@@ -0,0 +1,195 @@
+#undef cquest
+#define cquest (quest[QUEST_HOBBIT])
+
+bool quest_hobbit_town_gen_hook(char *fmt)
+{
+ int x = 1, y = 1, try = 10000;
+ s32b small;
+
+ small = get_next_arg(fmt);
+
+ if ((turn < (cquest.data[1] + (DAY * 10L))) || (cquest.status > QUEST_STATUS_COMPLETED) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ y = randint(20) + (cur_hgt / 2) - 10;
+ x = randint(20) + (cur_wid / 2) - 10;
+
+ /* Is it a good spot ? */
+ /* Not in player los, and avoid shop grids */
+ if (!los(p_ptr->py, p_ptr->px, y, x) && cave_empty_bold(y, x) &&
+ cave_plain_floor_bold(y, x)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ /* Place Melinda */
+ m_allow_special[test_monster_name("Melinda Proudfoot")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Melinda Proudfoot"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Melinda Proudfoot")] = FALSE;
+
+ return FALSE;
+}
+bool quest_hobbit_gen_hook(char *fmt)
+{
+ int x = 1, y = 1, try = 10000;
+
+ if ((cquest.status != QUEST_STATUS_TAKEN) || (dun_level != cquest.data[0]) || (dungeon_type != DUNGEON_MAZE)) return FALSE;
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ /* Place the hobbit */
+ m_allow_special[test_monster_name("Merton Proudfoot, the lost hobbit")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Merton Proudfoot, the lost hobbit"), 0, FALSE, MSTATUS_FRIEND);
+ m_allow_special[test_monster_name("Merton Proudfoot, the lost hobbit")] = FALSE;
+
+ return FALSE;
+}
+bool quest_hobbit_give_hook(char *fmt)
+{
+ object_type *o_ptr;
+ monster_type *m_ptr;
+ s32b m_idx, item;
+
+ m_idx = get_next_arg(fmt);
+ item = get_next_arg(fmt);
+
+ o_ptr = &p_ptr->inventory[item];
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Merton Proudfoot, the lost hobbit")) return (FALSE);
+
+ if ((o_ptr->tval != TV_SCROLL) || (o_ptr->sval != SV_SCROLL_WORD_OF_RECALL)) return (FALSE);
+
+ msg_print("'Oh, thank you, noble one!'");
+ msg_print("Merton Proudfoot reads the scroll and is recalled to the safety of his home.");
+
+ delete_monster_idx(m_idx);
+ inven_item_increase(item, -1);
+ inven_item_optimize(item);
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook(HOOK_GIVE, quest_hobbit_give_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_hobbit_speak_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+
+ if (m_list[m_idx].r_idx != test_monster_name("Melinda Proudfoot")) return (FALSE);
+
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ cptr m_name;
+
+ m_name = get_next_arg_str(fmt);
+
+ msg_format("%^s begs for your help.", m_name);
+ }
+ return (TRUE);
+}
+bool quest_hobbit_chat_hook(char *fmt)
+{
+ monster_type *m_ptr;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Melinda Proudfoot")) return (FALSE);
+
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ msg_print("Oh! Oh!");
+ msg_print("My poor Merton, where is my poor Merton? He was playing near that dreadful");
+ msg_print("maze and never been seen again! Could you find him for me?");
+
+ cquest.status = QUEST_STATUS_TAKEN;
+ quest[QUEST_HOBBIT].init(QUEST_HOBBIT);
+ }
+ else if (cquest.status == QUEST_STATUS_COMPLETED)
+ {
+ object_type forge, *q_ptr;
+
+ msg_print("My Merton is back! You saved him, hero.");
+ msg_print("Take this as a proof of my gratitude. It was given to my family");
+ msg_print("by a famed wizard, but it should serve you better than me.");
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_ROD, SV_ROD_RECALL));
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_REWARD;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook(HOOK_MON_SPEAK, quest_hobbit_speak_hook);
+ process_hooks_restart = TRUE;
+ delete_monster_idx(m_idx);
+
+ return TRUE;
+ }
+ else
+ {
+ msg_print("Thanks again.");
+ }
+
+ return TRUE;
+}
+bool quest_hobbit_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You saved a young hobbit from an horrible fate.");
+ }
+ return (FALSE);
+}
+bool quest_hobbit_init_hook(int q_idx)
+{
+ /* Get a level to place the hobbit */
+ if (!cquest.data[0])
+ {
+ cquest.data[0] = rand_range(26, 34);
+ cquest.data[1] = turn;
+ if (wizard) message_add(MESSAGE_MSG, format("Hobbit level %d", cquest.data[0]), TERM_BLUE);
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_GIVE, quest_hobbit_give_hook, "hobbit_give");
+ add_hook(HOOK_GEN_LEVEL, quest_hobbit_gen_hook, "hobbit_gen");
+ add_hook(HOOK_WILD_GEN, quest_hobbit_town_gen_hook, "hobbit_town_gen");
+ add_hook(HOOK_CHAT, quest_hobbit_chat_hook, "hobbit_chat");
+ add_hook(HOOK_MON_SPEAK, quest_hobbit_speak_hook, "hobbit_speak");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MON_SPEAK, quest_hobbit_speak_hook, "hobbit_speak");
+ add_hook(HOOK_WILD_GEN, quest_hobbit_town_gen_hook, "hobbit_town_gen");
+ add_hook(HOOK_CHAT, quest_hobbit_chat_hook, "hobbit_chat");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_hobbit_dump_hook, "hobbit_dump");
+ return (FALSE);
+}
diff --git a/src/q_invas.c b/src/q_invas.c
new file mode 100644
index 00000000..1e916fd6
--- /dev/null
+++ b/src/q_invas.c
@@ -0,0 +1,233 @@
+#undef cquest
+#define cquest (quest[QUEST_INVASION])
+
+bool quest_invasion_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "maeglin.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ cquest.data[0] = y;
+ cquest.data[1] = x;
+ p_ptr->py = y;
+ p_ptr->px = x;
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+ }
+
+ return TRUE;
+}
+bool quest_invasion_ai_hook(char *fmt)
+{
+ monster_type *m_ptr;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+ m_ptr = &m_list[m_idx];
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ /* Ugly but thats better than a call to test_monster_name which is SLOW */
+ if (m_ptr->r_idx == 825)
+ {
+ /* Oups he fleed */
+ if ((m_ptr->fy == cquest.data[0]) && (m_ptr->fx == cquest.data[1]))
+ {
+ delete_monster_idx(m_idx);
+
+ cmsg_print(TERM_YELLOW, "Maeglin found the way to Gondolin! All hope is lost now!");
+ cquest.status = QUEST_STATUS_FAILED;
+ town_info[2].destroyed = TRUE;
+ return (FALSE);
+ }
+
+ /* Attack or flee ?*/
+ if (distance(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) <= 2)
+ {
+ return (FALSE);
+ }
+ else
+ {
+ process_hooks_return[0].num = cquest.data[0];
+ process_hooks_return[1].num = cquest.data[1];
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+bool quest_invasion_turn_hook(char *fmt)
+{
+ bool old_quick_messages = quick_messages;
+
+ if (cquest.status != QUEST_STATUS_UNTAKEN) return (FALSE);
+ if (p_ptr->lev < 45) return (FALSE);
+
+ /* Wait until the end of the current quest */
+ if (p_ptr->inside_quest) return ( FALSE);
+
+ /* Wait until the end of the astral mode */
+ if (p_ptr->astral) return ( FALSE);
+
+ /* Ok give the quest */
+ quick_messages = FALSE;
+ cmsg_print(TERM_YELLOW, "A Thunderlord appears in front of you and says:");
+ cmsg_print(TERM_YELLOW, "'Hello, noble hero. I am Liron, rider of Tolan. Turgon, King of Gondolin sent me.'");
+ cmsg_print(TERM_YELLOW, "'Gondolin is being invaded; he needs your help now or everything will be lost.'");
+ cmsg_print(TERM_YELLOW, "'I can bring you to Gondolin, but we must go now.'");
+ /* This is SO important a question that flush pending inputs */
+ flush();
+
+ if (!get_check("Will you come?"))
+ {
+ cmsg_print(TERM_YELLOW, "'Turgon overestimated you... Now Gondolin will fall.'");
+ cmsg_print(TERM_YELLOW, "'I will return alone and die there. May you be doomed!'");
+
+ cquest.status = QUEST_STATUS_FAILED;
+ town_info[2].destroyed = TRUE;
+
+ quick_messages = old_quick_messages;
+
+ del_hook(HOOK_END_TURN, quest_invasion_turn_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+ cmsg_print(TERM_YELLOW, "'You made the right decision! Quickly, jump on Tolan!'");
+ cmsg_print(TERM_YELLOW, "'Here we are: Gondolin. You must speak with Turgon now.'");
+
+ p_ptr->wild_mode = FALSE;
+ p_ptr->wilderness_x = 49;
+ p_ptr->wilderness_y = 11;
+ p_ptr->town_num = 2;
+ p_ptr->oldpx = p_ptr->px = 117;
+ p_ptr->oldpy = p_ptr->py = 24;
+ dun_level = 0;
+ p_ptr->leaving = TRUE;
+
+ cmsg_print(TERM_YELLOW, "'Turgon hails you.'");
+ quest_describe(QUEST_INVASION);
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ quick_messages = old_quick_messages;
+
+ quest_invasion_init_hook(QUEST_INVASION);
+ del_hook(HOOK_END_TURN, quest_invasion_turn_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+}
+bool quest_invasion_dump_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FAILED)
+ {
+ fprintf(hook_file, "\n You abandoned Gondolin when it most needed you, thus causing its destruction.");
+ }
+ if ((cquest.status == QUEST_STATUS_FINISHED) || (cquest.status == QUEST_STATUS_REWARDED) || (cquest.status == QUEST_STATUS_COMPLETED))
+ {
+ fprintf(hook_file, "\n You saved Gondolin from destruction.");
+ }
+ return (FALSE);
+}
+bool quest_invasion_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ if (r_idx == test_monster_name("Maeglin, the Traitor of Gondolin"))
+ {
+ cmsg_print(TERM_YELLOW, "You did it! Gondolin will remain hidden.");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_invasion_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return FALSE;
+}
+bool quest_invasion_stair_hook(char *fmt)
+{
+ cptr down;
+
+ down = get_next_arg_str(fmt);
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return TRUE;
+
+ if (!strcmp(down, "up"))
+ {
+ if (cquest.status == QUEST_STATUS_FAILED)
+ {
+ cmsg_print(TERM_YELLOW, "The armies of Morgoth totally devastated Gondolin, leaving nothing but ruins...");
+ }
+ else if (cquest.status == QUEST_STATUS_COMPLETED)
+ {
+ cmsg_print(TERM_YELLOW, "Turgon appears before you and speaks:");
+ cmsg_print(TERM_YELLOW, "'I will never be able to thank you enough.'");
+ cmsg_print(TERM_YELLOW, "'My most powerful mages will cast a powerful spell for you, giving you extra life.'");
+
+ p_ptr->hp_mod += 150;
+ p_ptr->update |= (PU_HP);
+ cquest.status = QUEST_STATUS_FINISHED;
+ }
+ else
+ {
+ /* Flush input */
+ flush();
+
+ if (!get_check("Really abandon the quest?")) return TRUE;
+ cmsg_print(TERM_YELLOW, "You flee away from Maeglin and his army...");
+ cquest.status = QUEST_STATUS_FAILED;
+ town_info[2].destroyed = TRUE;
+ }
+ del_hook(HOOK_STAIR, quest_invasion_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return TRUE;
+}
+bool quest_invasion_init_hook(int q_idx)
+{
+ add_hook(HOOK_END_TURN, quest_invasion_turn_hook, "invasion_turn");
+ add_hook(HOOK_CHAR_DUMP, quest_invasion_dump_hook, "invasion_dump");
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_AI, quest_invasion_ai_hook, "invasion_ai");
+ add_hook(HOOK_GEN_QUEST, quest_invasion_gen_hook, "invasion_gen");
+ add_hook(HOOK_MONSTER_DEATH, quest_invasion_death_hook, "invasion_death");
+ add_hook(HOOK_STAIR, quest_invasion_stair_hook, "invasion_stair");
+ }
+ return (FALSE);
+}
diff --git a/src/q_main.c b/src/q_main.c
new file mode 100644
index 00000000..15d15ef3
--- /dev/null
+++ b/src/q_main.c
@@ -0,0 +1,176 @@
+bool quest_main_monsters_hook(char *fmt)
+{
+ s32b r_idx;
+ r_idx = get_next_arg(fmt);
+
+ /* Sauron */
+ if (r_idx == 860)
+ {
+ /* No Sauron until Necromancer dies */
+ if (r_info[819].max_num) return TRUE;
+ }
+ /* Morgoth */
+ else if (r_idx == 862)
+ {
+ /* No Morgoth until Sauron dies */
+ if (r_info[860].max_num) return TRUE;
+ }
+ return FALSE;
+}
+bool quest_morgoth_hook(char *fmt)
+{
+ /* Using test_monster_name() here would be a lot less ugly, but would take much more time */
+ monster_race *r_ptr = &r_info[862];
+
+ /* Need to kill him */
+ if (!r_ptr->max_num)
+ {
+ /* Total winner */
+ total_winner = WINNER_NORMAL;
+ has_won = WINNER_NORMAL;
+ quest[QUEST_MORGOTH].status = QUEST_STATUS_FINISHED;
+
+ /* Redraw the "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Congratulations */
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ {
+ cmsg_print(TERM_L_GREEN, "*** CONGRATULATIONS ***");
+ cmsg_print(TERM_L_GREEN, "You have banished Morgoth's foul spirit from Ea, and as you watch, a cleansing");
+ cmsg_print(TERM_L_GREEN, "wind roars through the dungeon, dispersing the nether mists around where the");
+ cmsg_print(TERM_L_GREEN, "body fell. You feel thanks, and a touch of sorrow, from the Valar");
+ cmsg_print(TERM_L_GREEN, "for your deed. You will be forever heralded, your deed forever legendary.");
+ cmsg_print(TERM_L_GREEN, "You may retire (commit suicide) when you are ready.");
+ }
+ else
+ {
+ cmsg_print(TERM_VIOLET, "*** CONGRATULATIONS ***");
+ cmsg_print(TERM_VIOLET, "You have banished Morgoth from Arda, and made Ea a safer place.");
+ cmsg_print(TERM_VIOLET, "As you look down at the dispersing mists around Morgoth, a sudden intuition");
+ cmsg_print(TERM_VIOLET, "grasps you. Fingering the One Ring, you gather the nether mists around");
+ cmsg_print(TERM_VIOLET, "yourself, and inhale deeply their seductive power.");
+ cmsg_print(TERM_VIOLET, "You will be forever feared, your orders forever obeyed.");
+ cmsg_print(TERM_VIOLET, "You may retire (commit suicide) when you are ready.");
+ }
+
+ /* Continue the plot(maybe) */
+ del_hook(HOOK_MONSTER_DEATH, quest_morgoth_hook);
+ process_hooks_restart = TRUE;
+
+ /* Either ultra good if the one Ring is destroyed, or ultra evil if used */
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ *(quest[QUEST_MORGOTH].plot) = QUEST_ULTRA_GOOD;
+ else
+ *(quest[QUEST_MORGOTH].plot) = QUEST_ULTRA_EVIL;
+ quest[*(quest[QUEST_MORGOTH].plot)].init(*(quest[QUEST_MORGOTH].plot));
+ }
+ return (FALSE);
+}
+bool quest_morgoth_dump_hook(char *fmt)
+{
+ if (quest[QUEST_MORGOTH].status >= QUEST_STATUS_COMPLETED)
+ {
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ fprintf(hook_file, "\n You saved Arda and became a famed %s.", sp_ptr->winner);
+ else
+ fprintf(hook_file, "\n You became a new force of darkness and enslaved all free people.");
+ }
+ return (FALSE);
+}
+bool quest_morgoth_init_hook(int q_idx)
+{
+ if ((quest[QUEST_MORGOTH].status >= QUEST_STATUS_TAKEN) && (quest[QUEST_MORGOTH].status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_morgoth_hook, "morgort_death");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_morgoth_dump_hook, "morgoth_dump");
+ add_hook(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster");
+ return (FALSE);
+}
+
+bool quest_sauron_hook(char *fmt)
+{
+ /* Using test_monster_name() here would be a lot less ugly, but would take much more time */
+ monster_race *r_ptr = &r_info[860];
+
+ /* Need to kill him */
+ if (!r_ptr->max_num)
+ {
+ cmsg_print(TERM_YELLOW, "Well done! You are on the way to slaying Morgoth...");
+ quest[QUEST_SAURON].status = QUEST_STATUS_FINISHED;
+
+ quest[QUEST_MORGOTH].status = QUEST_STATUS_TAKEN;
+ quest_describe(QUEST_MORGOTH);
+
+ del_hook(HOOK_MONSTER_DEATH, quest_sauron_hook);
+ add_hook(HOOK_MONSTER_DEATH, quest_morgoth_hook, "morgort_death");
+ *(quest[QUEST_SAURON].plot) = QUEST_MORGOTH;
+ quest_morgoth_init_hook(QUEST_MORGOTH);
+
+ process_hooks_restart = TRUE;
+ }
+ return (FALSE);
+}
+
+bool quest_sauron_resurect_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ if ((r_ptr->flags7 & RF7_NAZGUL) && r_info[860].max_num)
+ {
+ msg_format("Somehow you feel %s is not totally destroyed...", (r_ptr->flags1 & RF1_FEMALE ? "she" : "he"));
+ r_ptr->max_num = 1;
+ }
+ else if ((m_ptr->r_idx == 860) && (quest[QUEST_ONE].status < QUEST_STATUS_FINISHED))
+ {
+ msg_print("Sauron will not be permanently defeated until the One Ring is either destroyed or used...");
+ r_ptr->max_num = 1;
+ }
+ return FALSE;
+}
+
+bool quest_sauron_init_hook(int q_idx)
+{
+ if ((quest[QUEST_SAURON].status >= QUEST_STATUS_TAKEN) && (quest[QUEST_SAURON].status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_sauron_hook, "sauron_death");
+ }
+ add_hook(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster");
+ add_hook(HOOK_MONSTER_DEATH, quest_sauron_resurect_hook, "sauron_resurect_death");
+ return (FALSE);
+}
+
+bool quest_necro_hook(char *fmt)
+{
+ /* Using test_monster_name() here would be a lot less ugly, but would take much more time */
+ monster_race *r_ptr = &r_info[819];
+
+ /* Need to kill him */
+ if (!r_ptr->max_num)
+ {
+ cmsg_print(TERM_YELLOW, "You see the spirit of the necromancer rise and flee...");
+ cmsg_print(TERM_YELLOW, "It looks like it was indeed Sauron...");
+ cmsg_print(TERM_YELLOW, "You should report that to Galadriel as soon as possible.");
+
+ quest[QUEST_NECRO].status = QUEST_STATUS_FINISHED;
+
+ *(quest[QUEST_NECRO].plot) = QUEST_ONE;
+ quest[*(quest[QUEST_NECRO].plot)].init(*(quest[QUEST_NECRO].plot));
+
+ del_hook(HOOK_MONSTER_DEATH, quest_necro_hook);
+ process_hooks_restart = TRUE;
+ }
+ return (FALSE);
+}
+bool quest_necro_init_hook(int q_idx)
+{
+ if ((quest[QUEST_NECRO].status >= QUEST_STATUS_TAKEN) && (quest[QUEST_NECRO].status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_necro_hook, "necro_death");
+ }
+ add_hook(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster");
+ return (FALSE);
+}
diff --git a/src/q_narsil.c b/src/q_narsil.c
new file mode 100644
index 00000000..a15d46c6
--- /dev/null
+++ b/src/q_narsil.c
@@ -0,0 +1,118 @@
+#undef cquest
+#define cquest (quest[QUEST_NARSIL])
+
+bool quest_narsil_move_hook(char *fmt)
+{
+ s32b y, x;
+ cave_type *c_ptr;
+ int i;
+ object_type *o_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ /* The castle of Aragorn */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 14)) return FALSE;
+
+ /* Look out for Narsil */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->name1 == ART_NARSIL) break;
+ }
+
+ if (i == INVEN_TOTAL) return FALSE;
+
+ cmsg_print(TERM_YELLOW, "I heard that the broken sword had been found!");
+ cmsg_print(TERM_YELLOW, "I thought it was only a rumor... until now.");
+ cmsg_print(TERM_YELLOW, "What you have is really the sword that was broken.");
+ cmsg_print(TERM_YELLOW, "I will get it reforged...");
+ cmsg_print(TERM_L_BLUE, "Aragorn leaves for a few hours then comes back...");
+ cmsg_print(TERM_YELLOW, "Here it is, Anduril, the sword that was forged and is");
+ cmsg_print(TERM_YELLOW, "reforged again. Take it; you will surely need it for your quest.");
+
+ object_prep(o_ptr, lookup_kind(TV_SWORD, SV_LONG_SWORD));
+ o_ptr->name1 = ART_ANDURIL;
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER | PW_INVEN);
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook(HOOK_MOVE, quest_narsil_move_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_narsil_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n The sword that was broken is now reforged.");
+ }
+ return (FALSE);
+}
+bool quest_narsil_identify_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ int i;
+ object_type *o_ptr;
+ s32b item;
+
+ item = get_next_arg(fmt);
+
+ /* Inventory */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Floor */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ if (o_ptr->name1 == ART_NARSIL)
+ {
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ for (i = 0; i < 5; i++)
+ {
+ if (quest[QUEST_NARSIL].desc[i][0] != '\0')
+ {
+ cmsg_print(TERM_YELLOW, quest[QUEST_NARSIL].desc[i]);
+ }
+ }
+
+ add_hook(HOOK_MOVE, quest_narsil_move_hook, "narsil_move");
+ del_hook(HOOK_IDENTIFY, quest_narsil_identify_hook);
+ process_hooks_restart = TRUE;
+ }
+ }
+
+ return (FALSE);
+}
+bool quest_narsil_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MOVE, quest_narsil_move_hook, "narsil_move");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN) add_hook(HOOK_IDENTIFY, quest_narsil_identify_hook, "narsil_id");
+ add_hook(HOOK_CHAR_DUMP, quest_narsil_dump_hook, "narsil_dump");
+ return (FALSE);
+}
diff --git a/src/q_nazgul.c b/src/q_nazgul.c
new file mode 100644
index 00000000..3114be13
--- /dev/null
+++ b/src/q_nazgul.c
@@ -0,0 +1,115 @@
+#undef cquest
+#define cquest (quest[QUEST_NAZGUL])
+
+bool quest_nazgul_gen_hook(char *fmt)
+{
+ int x = 1, y = 1, try = 10000;
+ s32b small;
+
+ small = get_next_arg(fmt);
+
+ if ((cquest.status != QUEST_STATUS_TAKEN) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ /* Not in player los */
+ if ((!los(p_ptr->py, p_ptr->px, y, x)) && cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ /* Place the nazgul */
+ m_allow_special[test_monster_name("Uvatha the Horseman")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Uvatha the Horseman"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Uvatha the Horseman")] = FALSE;
+
+ return FALSE;
+}
+bool quest_nazgul_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_NAZGUL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "I believe he will not come back! Thank you.", 8, 0);
+ c_put_str(TERM_YELLOW, "Some time ago a ranger gave me this.", 9, 0);
+ c_put_str(TERM_YELLOW, "I believe it will help you on your quest.", 10, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_ATHELAS));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 6;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* End the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_nazgul_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_nazgul_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You saved Bree from a dreadful Nazgul.");
+ }
+ return (FALSE);
+}
+bool quest_nazgul_forbid_hook(char *fmt)
+{
+ s32b q_idx;
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_NAZGUL) return (FALSE);
+
+ if (p_ptr->lev < 30)
+ {
+ c_put_str(TERM_WHITE, "I fear you are not ready for the next quest, come back later.", 8, 0);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+bool quest_nazgul_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+ if (r_idx != test_monster_name("Uvatha the Horseman")) return (FALSE);
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook(HOOK_MONSTER_DEATH, quest_nazgul_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+}
+bool quest_nazgul_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_nazgul_death_hook, "nazgul_death");
+ add_hook(HOOK_WILD_GEN, quest_nazgul_gen_hook, "nazgul_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_nazgul_finish_hook, "nazgul_finish");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_nazgul_dump_hook, "nazgul_dump");
+ add_hook(HOOK_INIT_QUEST, quest_nazgul_forbid_hook, "nazgul_forbid");
+ return (FALSE);
+}
diff --git a/src/q_nirna.c b/src/q_nirna.c
new file mode 100644
index 00000000..ab0d29ce
--- /dev/null
+++ b/src/q_nirna.c
@@ -0,0 +1,111 @@
+#undef cquest
+#define cquest (quest[QUEST_NIRNAETH])
+
+bool quest_nirnaeth_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "nirnaeth.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ /* Count the number of monsters */
+ cquest.data[0] = 0;
+ cquest.data[1] = 0;
+ for (x = 2; x < xstart; x++)
+ for (y = 2; y < ystart; y++)
+ {
+ if (cave[y][x].m_idx) cquest.data[0]++;
+ }
+
+ return TRUE;
+}
+bool quest_nirnaeth_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_NIRNAETH) return FALSE;
+
+ /* Killed at least 2/3 of them ? better reward ! */
+ if (cquest.data[1] >= (2 * cquest.data[0] / 3))
+ {
+ c_put_str(TERM_YELLOW, "Not only did you found a way out, but you also destroyed a good", 8, 0);
+ c_put_str(TERM_YELLOW, "number of trolls! Thank you so much. Take this gold please.", 9, 0);
+ c_put_str(TERM_YELLOW, "I also grant you access to the royal jewelry shop!", 10, 0);
+
+ p_ptr->au += 200000;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ c_put_str(TERM_YELLOW, "I thank you for your efforts.", 8, 0);
+ c_put_str(TERM_YELLOW, "I grant you access to the royal jewelry shop!", 9, 0);
+ }
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_nirnaeth_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_nirnaeth_death_hook(char *fmt)
+{
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ cquest.data[1]++;
+
+ return FALSE;
+}
+bool quest_nirnaeth_stair_hook(char *fmt)
+{
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "You found a way out!");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_STAIR, quest_nirnaeth_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+}
+bool quest_nirnaeth_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_nirnaeth_death_hook, "nirnaeth_death");
+ add_hook(HOOK_GEN_QUEST, quest_nirnaeth_gen_hook, "nirnaeth_gen");
+ add_hook(HOOK_STAIR, quest_nirnaeth_stair_hook, "nirnaeth_stair");
+ add_hook(HOOK_QUEST_FINISH, quest_nirnaeth_finish_hook, "nirnaeth_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_one.c b/src/q_one.c
new file mode 100644
index 00000000..a472979a
--- /dev/null
+++ b/src/q_one.c
@@ -0,0 +1,363 @@
+#undef cquest
+#define cquest (quest[QUEST_ONE])
+
+bool quest_one_move_hook(char *fmt)
+{
+ s32b y, x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ if (quest[QUEST_NECRO].status < QUEST_STATUS_FINISHED) return (FALSE);
+
+ /* The mirror of Galadriel */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 23)) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "You meet Galadriel; she seems worried.");
+ cmsg_print(TERM_YELLOW, "'So it was Sauron that lurked in Dol Guldur...'");
+ cmsg_print(TERM_YELLOW, "'The Enemy is growing in power. Morgoth will be unreachable as long'");
+ cmsg_print(TERM_YELLOW, "'as his most powerful servant, Sauron, lives. But the power of Sauron'");
+ cmsg_print(TERM_YELLOW, "'lies in the One Ring. Our only hope is that you find it'");
+ cmsg_print(TERM_YELLOW, "'and destroy it. I know it will tempt you, but *NEVER* use it'");
+ cmsg_print(TERM_YELLOW, "'or it will corrupt you forever.'");
+
+ GOD(GOD_ERU)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Eru will abandon you if you wear it.'");
+ }
+
+ GOD(GOD_MANWE)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Manwe will abandon you if you wear it.'");
+ }
+
+ GOD(GOD_TULKAS)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Tulkas will abandon you if you wear it.'");
+ }
+
+ GOD(GOD_YAVANNA)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Yavanna will abandon you if you wear it.'");
+ }
+
+ cmsg_print(TERM_YELLOW, "'Without the destruction of the ring, Sauron's death can only be temporary'");
+ cmsg_print(TERM_YELLOW, "'When you have it, bring it to Mount Doom, in Mordor,'");
+ cmsg_print(TERM_YELLOW, "'to destroy it in the Great Fire where it was forged.'");
+ cmsg_print(TERM_YELLOW, "'I do not know where to find it. Seek it through Middle-earth. Maybe there'");
+ cmsg_print(TERM_YELLOW, "'are other people that might know.'");
+ cmsg_print(TERM_YELLOW, "'Do not forget: the Ring must be cast back into the fires of Mount Doom!'");
+
+ GOD(GOD_MELKOR)
+ {
+ cmsg_print(TERM_YELLOW, "'Melkor will abandon you when you do, but you must do it anyway!'");
+ }
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest.init(QUEST_ONE);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+bool quest_one_drop_hook(char *fmt)
+{
+ s32b o_idx;
+ object_type *o_ptr;
+
+ o_idx = get_next_arg(fmt);
+ o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ if (o_ptr->name1 != ART_POWER) return FALSE;
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_GREAT_FIRE) return FALSE;
+
+ cmsg_print(TERM_YELLOW, "You throw the One Ring into the #RGreat Fire#y; it is rapidly consumed");
+ cmsg_print(TERM_YELLOW, "by the searing flames.");
+ cmsg_print(TERM_YELLOW, "You feel the powers of evil weakening.");
+ cmsg_print(TERM_YELLOW, "Now you can go onto the hunt for Sauron!");
+
+ inven_item_increase(o_idx, -99);
+ inven_item_optimize(o_idx);
+
+ abandon_god(GOD_MELKOR);
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FINISHED;
+ *(quest[QUEST_ONE].plot) = QUEST_SAURON;
+ quest[*(quest[QUEST_ONE].plot)].status = QUEST_STATUS_TAKEN;
+ quest[*(quest[QUEST_ONE].plot)].init(*(quest[QUEST_ONE].plot));
+
+ return TRUE;
+}
+bool quest_one_wield_hook(char *fmt)
+{
+ s32b o_idx;
+ object_type *o_ptr;
+
+ o_idx = get_next_arg(fmt);
+ o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ if (o_ptr->name1 != ART_POWER) return FALSE;
+
+ /* Flush input */
+ flush();
+
+ if (!get_check("You were warned not to wear it; are you sure?")) return TRUE;
+ /* Flush input */
+ flush();
+
+ if (!get_check("You were warned not to wear it; are you *REALLY* sure?")) return TRUE;
+
+ /* Flush input */
+ flush();
+
+ if (!get_check("You were *WARNED* not to wear it; are you *R*E*A*L*L*Y* sure?")) return TRUE;
+
+ cmsg_print(TERM_YELLOW, "As you put it on your finger you feel #Ddark powers #ysapping your soul.");
+ cmsg_print(TERM_YELLOW, "The ring firmly binds to your finger!");
+ cmsg_print(TERM_YELLOW, "You feel you are drawn to the shadow world! Your material form weakens.");
+
+ abandon_god(GOD_ERU);
+ abandon_god(GOD_MANWE);
+ abandon_god(GOD_TULKAS);
+ abandon_god(GOD_YAVANNA);
+
+ /*
+ * Ok now we are evil, right ?
+ * Towns aren't, right ?
+ * So let's destroy them !
+ */
+ town_info[1].destroyed = TRUE;
+ town_info[2].destroyed = TRUE;
+ town_info[3].destroyed = TRUE;
+ town_info[4].destroyed = TRUE;
+ town_info[5].destroyed = TRUE;
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ *(quest[QUEST_ONE].plot) = QUEST_SAURON;
+ quest[*(quest[QUEST_ONE].plot)].status = QUEST_STATUS_TAKEN;
+ quest[*(quest[QUEST_ONE].plot)].init(*(quest[QUEST_ONE].plot));
+
+ /* Ok lets reset the lives counter */
+ p_ptr->lives = 0;
+
+ return FALSE;
+}
+bool quest_one_hp_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ s32b mhp;
+ int i;
+
+ mhp = get_next_arg(fmt);
+
+ for (i = 0; i < p_ptr->lives + 1; i++)
+ mhp = (mhp * 2) / 3;
+
+ process_hooks_return[0].num = mhp;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+bool quest_one_die_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ if (p_ptr->mhp > 1)
+ {
+ cmsg_print(TERM_YELLOW, "You feel the power of the One Ring sustaining your life,");
+ cmsg_print(TERM_YELLOW, "but it drags you even more into the shadow world.");
+ return (TRUE);
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "The One Ring finally drags you totally to the shadow world.");
+ cmsg_print(TERM_YELLOW, "Your mortal existence ends there.");
+ strcpy(died_from, "being drawn to the shadow world");
+ }
+ }
+ return (FALSE);
+}
+bool quest_one_identify_hook(char *fmt)
+{
+ s32b item;
+
+ item = get_next_arg(fmt);
+
+ if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ object_type *o_ptr;
+
+ /* Inventory */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Floor */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ if ((o_ptr->name1 == ART_POWER) && (!object_known_p(o_ptr)))
+ {
+ cmsg_print(TERM_YELLOW, "You finally found the One Ring, source of Sauron's power, and key to");
+ cmsg_print(TERM_YELLOW, "its destruction. Remember, bring it to Mount Doom and destroy it.");
+ cmsg_print(TERM_YELLOW, "And *NEVER* use it.");
+ }
+ }
+
+ return (FALSE);
+}
+bool quest_one_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+ bool ok = FALSE;
+ monster_race *r_ptr;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+ r_ptr = &r_info[r_idx];
+
+ if (a_info[ART_POWER].cur_num) return FALSE;
+
+ /* Paranoia */
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+
+ if (magik(30) && (r_idx == test_monster_name("Sauron, the Sorcerer")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Ar-Pharazon the Golden")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Shelob, Spider of Darkness")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("The Watcher in the Water")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Glaurung, Father of the Dragons")))
+ {
+ ok = TRUE;
+ }
+ else if (magik(10) && (r_idx == test_monster_name("Feagwath, the Undead Sorcerer")))
+ {
+ ok = TRUE;
+ }
+
+ if (ok)
+ {
+ int i;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make "Grond" */
+ object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_POWER));
+
+ /* Mega-Hack -- Mark this item as "the one ring" */
+ q_ptr->name1 = ART_POWER;
+
+ /* Mega-Hack -- Actually create "the one ring" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Find a space */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Skip non-objects */
+ if (!p_ptr->inventory[i].k_idx) break;
+ }
+ /* Arg, no space ! */
+ if (i == INVEN_PACK)
+ {
+ char o_name[200];
+
+ object_desc(o_name, &p_ptr->inventory[INVEN_PACK - 1], FALSE, 0);
+
+ /* Drop the item */
+ inven_drop(INVEN_PACK - 1, 99, p_ptr->py, p_ptr->px, FALSE);
+
+ cmsg_format(TERM_VIOLET, "You feel the urge to drop your %s to make room in your inventory.", o_name);
+ }
+
+ /* Carry it */
+ cmsg_format(TERM_VIOLET, "You feel the urge to pick up that plain gold ring you see.");
+ inven_carry(q_ptr, FALSE);
+ }
+
+ return (FALSE);
+}
+bool quest_one_dump_hook(char *fmt)
+{
+ if (cquest.status == QUEST_STATUS_FINISHED)
+ {
+ fprintf(hook_file, "\n You destroyed the One Ring, thus weakening Sauron.");
+ }
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ fprintf(hook_file, "\n You fell under the evil influence of the One Ring and decided to wear it.");
+ }
+ return (FALSE);
+}
+bool quest_one_gen_hook(char *fmt)
+{
+ s32b x, y, try = 10000;
+
+ /* Paranoia */
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+ if ((dungeon_type != DUNGEON_ANGBAND) || (dun_level != 99)) return (FALSE);
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ if (try) place_monster_one(y, x, test_monster_name("Sauron, the Sorcerer"), 0, FALSE, MSTATUS_ENEMY);
+
+ return (FALSE);
+}
+bool quest_one_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_LEVEL_END_GEN, quest_one_gen_hook, "one_gen");
+ add_hook(HOOK_MONSTER_DEATH, quest_one_death_hook, "one_death");
+ add_hook(HOOK_DROP, quest_one_drop_hook, "one_drop");
+ add_hook(HOOK_WIELD, quest_one_wield_hook, "one_wield");
+ add_hook(HOOK_IDENTIFY, quest_one_identify_hook, "one_id");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MOVE, quest_one_move_hook, "one_move");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_one_dump_hook, "one_dump");
+ add_hook(HOOK_CALC_HP, quest_one_hp_hook, "one_hp");
+ add_hook(HOOK_DIE, quest_one_die_hook, "one_die");
+ return (FALSE);
+}
diff --git a/src/q_poison.c b/src/q_poison.c
new file mode 100644
index 00000000..e3e1e69a
--- /dev/null
+++ b/src/q_poison.c
@@ -0,0 +1,237 @@
+#undef cquest
+#define cquest (quest[QUEST_POISON])
+
+static int wild_locs[4][2] =
+{
+ { 32, 49, },
+ { 32, 48, },
+ { 33, 48, },
+ { 34, 48, },
+};
+
+static bool create_molds_hook(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->flags4 & RF4_MULTIPLY) return FALSE;
+
+ if (r_ptr->d_char == 'm') return TRUE;
+ else if (r_ptr->d_char == ',') return TRUE;
+ else if (r_ptr->d_char == 'e') return TRUE;
+ else return FALSE;
+}
+
+bool quest_poison_gen_hook(char *fmt)
+{
+ int cy = 1, cx = 1, x, y, try = 10000, r_idx;
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+ if (p_ptr->wilderness_y != wild_locs[cquest.data[0]][0]) return FALSE;
+ if (p_ptr->wilderness_x != wild_locs[cquest.data[0]][1]) return FALSE;
+ if (p_ptr->wild_mode) return FALSE;
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ cy = randint(cur_hgt - 24) + 22;
+ cx = randint(cur_wid - 34) + 32;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(cy, cx)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ /* Place the baddies */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_molds_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster, using the level calculation */
+ for (x = cx - 25; x <= cx + 25; x++)
+ for (y = cy - 25; y <= cy + 25; y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(cy, cx, y, x) > 25) continue;
+
+ if (magik(80) && ((cave[y][x].feat == FEAT_DEEP_WATER) || (cave[y][x].feat == FEAT_SHAL_WATER))) cave_set_feat(y, x, FEAT_TAINTED_WATER);
+
+ if (distance(cy, cx, y, x) > 10) continue;
+
+ if (magik(60))
+ {
+ int m_idx;
+
+ r_idx = get_mon_num(30);
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+
+ /* Sometimes make it up some levels */
+ if (magik(80) && m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (m_ptr->level < p_ptr->lev)
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + randint(p_ptr->lev - m_ptr->level));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ return FALSE;
+}
+bool quest_poison_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_POISON) return FALSE;
+
+ c_put_str(TERM_YELLOW, "The water is clean again! Thank you so much.", 8, 0);
+ c_put_str(TERM_YELLOW, "The beautiful Mallorns are safe. Take this as a proof of our gratitude.", 9, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_DRAG_ARMOR, SV_DRAGON_BLUE));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+ q_ptr->name2 = EGO_ELVENKIND;
+ apply_magic(q_ptr, 1, FALSE, FALSE, FALSE);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NULL;
+
+ del_hook(HOOK_QUEST_FINISH, quest_poison_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_poison_dump_hook(char *fmt)
+{
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(hook_file, "\n You saved the beautiful Mallorns of Lothlorien.");
+ }
+ return (FALSE);
+}
+bool quest_poison_quest_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_POISON) return FALSE;
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_POTION2, SV_POTION2_CURE_WATER));
+ q_ptr->number = 99;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ q_ptr->note = quark_add("quest");
+ (void)inven_carry(q_ptr, FALSE);
+
+ del_hook(HOOK_INIT_QUEST, quest_poison_quest_hook);
+ process_hooks_restart = TRUE;
+
+ return FALSE;
+}
+bool quest_poison_drop_hook(char *fmt)
+{
+ s32b mcnt = 0, i, x, y, o_idx;
+ object_type *o_ptr;
+
+ o_idx = get_next_arg(fmt);
+ o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+ if (p_ptr->wilderness_y != wild_locs[cquest.data[0]][0]) return FALSE;
+ if (p_ptr->wilderness_x != wild_locs[cquest.data[0]][1]) return FALSE;
+ if (p_ptr->wild_mode) return FALSE;
+
+ if (o_ptr->tval != TV_POTION2) return FALSE;
+ if (o_ptr->sval != SV_POTION2_CURE_WATER) return FALSE;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_NEUTRAL) mcnt++;
+ }
+
+ if (mcnt < 10)
+ {
+ for (x = 1; x < cur_wid - 1; x++)
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (cave[y][x].feat == FEAT_TAINTED_WATER) cave_set_feat(y, x, FEAT_SHAL_WATER);
+ }
+
+ cmsg_print(TERM_YELLOW, "Well done! The water seems to be clean now.");
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook(HOOK_DROP, quest_poison_drop_hook);
+ process_hooks_restart = TRUE;
+
+ return FALSE;
+ }
+ else
+ {
+ msg_print("There are too many monsters left to cure the water.");
+ return TRUE;
+ }
+ return FALSE;
+}
+bool quest_poison_init_hook(int q_idx)
+{
+ /* Get a place to place the poison */
+ if (!cquest.data[1])
+ {
+ cquest.data[1] = TRUE;
+ cquest.data[0] = rand_int(4);
+ if (wizard) message_add(MESSAGE_MSG, format("Wilderness poison %d, %d", wild_locs[cquest.data[0]][0], wild_locs[cquest.data[0]][1]), TERM_BLUE);
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_DROP, quest_poison_drop_hook, "poison_drop");
+ add_hook(HOOK_WILD_GEN, quest_poison_gen_hook, "poison_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_poison_finish_hook, "poison_finish");
+ }
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ add_hook(HOOK_INIT_QUEST, quest_poison_quest_hook, "poison_iquest");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_poison_dump_hook, "poison_dump");
+ return (FALSE);
+}
diff --git a/src/q_rand.c b/src/q_rand.c
new file mode 100644
index 00000000..73e5a5f3
--- /dev/null
+++ b/src/q_rand.c
@@ -0,0 +1,442 @@
+static int randquest_hero[] = { 20, 13, 15, 16, 9, 17, 18, 8, -1 };
+
+bool is_randhero(int level)
+{
+ int i;
+ bool result = FALSE;
+
+ for (i = 0; randquest_hero[i] != -1; i++)
+ {
+ if (random_quests[level].type == randquest_hero[i])
+ {
+ result = TRUE;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void do_get_new_obj(int y, int x)
+{
+ obj_theme theme;
+ char *items[3];
+ object_type *q_ptr[3], forge[3];
+ int max = 0, res, i;
+
+ /* Create 3 ones */
+ max = 0;
+ for (i = 0; i < 3; i++)
+ {
+ /* Get local object */
+ q_ptr[max] = &forge[max];
+
+ /* Wipe the object */
+ object_wipe(q_ptr[max]);
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+
+ /* Make a great object */
+ make_object(q_ptr[max], TRUE, TRUE, theme);
+ q_ptr[max]->found = OBJ_FOUND_REWARD;
+
+ C_MAKE(items[max], 100, char);
+ object_desc(items[max], q_ptr[max], 0, 0);
+ max++;
+ }
+
+
+ while (TRUE)
+ {
+ res = ask_menu("Choose a reward to get(a-c to choose, ESC to cancel)?", (char **)items, 3);
+
+ /* Ok ? lets learn ! */
+ if (res > -1)
+ {
+ /* Drop it in the dungeon */
+ drop_near(q_ptr[res], -1, y + 1, x);
+
+ cmsg_print(TERM_YELLOW, "There, Noble Hero. I put it there. Thanks again!");
+ break;
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+
+ object_type *o_ptr = q_ptr[i];
+
+ /* Check if there is any not chosen artifact */
+ if (i != res && artifact_p(o_ptr))
+ {
+ /* Mega-Hack -- Preserve the artifact */
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART)
+ {
+ k_info[o_ptr->k_idx].artifact = FALSE;
+ }
+ else if (o_ptr->name1)
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ C_KILL(items[i], 100, char);
+
+}
+
+void princess_death(s32b m_idx, s32b r_idx)
+{
+ int r;
+
+ cmsg_print(TERM_YELLOW, "O Great And Noble Hero, you saved me!");
+ cmsg_print(TERM_YELLOW, "I am heading home now. I cannot reward you as I should, but please take this.");
+
+ /* Look for the princess */
+ for (r = m_max - 1; r >= 1; r--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[r];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Is it the princess? */
+ if (m_ptr->r_idx == 969)
+ {
+ int x = m_ptr->fx;
+ int y = m_ptr->fy;
+ int i, j;
+
+ delete_monster_idx(r);
+
+ /* Wipe the glass walls and create a stair */
+ for (i = x - 1; i <= x + 1; i++)
+ for (j = y - 1; j <= y + 1; j++)
+ {
+ if (in_bounds(j, i)) cave_set_feat(j, i, FEAT_FLOOR);
+ }
+ cave_set_feat(y, x, FEAT_MORE);
+
+ do_get_new_obj(y, x);
+
+ random_quests[dun_level].done = TRUE;
+
+ break;
+ }
+ }
+}
+
+void hero_death(s32b m_idx, s32b r_idx)
+{
+ random_quests[dun_level].done = TRUE;
+
+ cmsg_print(TERM_YELLOW, "The adventurer steps out of the shadows and picks up his sword:");
+ cmsg_print(TERM_YELLOW, "'Ah! My sword! My trusty sword! Thanks.");
+
+ if (!can_create_companion())
+ {
+ cmsg_print(TERM_YELLOW, "I must go on my own way now.");
+ cmsg_print(TERM_YELLOW, "But before I go, I can help your skills.'");
+ cmsg_print(TERM_YELLOW, "He touches your forehead.");
+ do_get_new_skill();
+ return;
+ }
+ cmsg_print(TERM_YELLOW, "If you wish, I can help you in your adventures.'");
+
+ /* Flush input */
+ flush();
+
+ if (get_check("Do you want him to join you? "))
+ {
+ int x, y, i;
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d, 0);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) continue;
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ if (i < 20)
+ {
+ int m_idx;
+
+ m_allow_special[test_monster_name("Adventurer")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Adventurer"), 0, FALSE, MSTATUS_COMPANION);
+ m_allow_special[test_monster_name("Adventurer")] = FALSE;
+ if (m_idx)
+ {
+ m_list[m_idx].exp = MONSTER_EXP(1 + (dun_level * 3 / 2));
+ m_list[m_idx].status = MSTATUS_COMPANION;
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ else
+ msg_print("The adventurer suddenly seems afraid and flees...");
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "'As you wish, but I want to do something for you.'");
+ cmsg_print(TERM_YELLOW, "He touches your forehead.");
+ do_get_new_skill();
+ }
+}
+
+bool quest_random_death_hook(char *fmt)
+{
+ int r_idx;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (random_quests[dun_level].r_idx != r_idx) return (FALSE);
+
+ if (!(m_list[m_idx].mflag & MFLAG_QUEST)) return (FALSE);
+
+ /* Killed enough ?*/
+ quest[QUEST_RANDOM].data[0]++;
+ if (quest[QUEST_RANDOM].data[0] == random_quests[dun_level].type)
+ {
+ if (is_randhero(dun_level))
+ hero_death(m_idx, r_idx);
+ else
+ princess_death(m_idx, r_idx);
+ }
+
+ return (FALSE);
+}
+bool quest_random_turn_hook(char *fmt)
+{
+ quest[QUEST_RANDOM].data[0] = 0;
+ quest[QUEST_RANDOM].data[1] = 0;
+ return (FALSE);
+}
+bool quest_random_feeling_hook(char *fmt)
+{
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (!dun_level) return (FALSE);
+
+ if (is_randhero(dun_level))
+ {
+ cmsg_format(TERM_YELLOW, "A strange man wrapped in a dark cloak steps out of the shadows:");
+ cmsg_format(TERM_YELLOW, "'Oh, please help me! A horrible %s stole my sword! I'm nothing without it.'", r_info[random_quests[dun_level].r_idx].name + r_name);
+ }
+ else
+ cmsg_format(TERM_YELLOW, "You hear someone shouting: 'Leave me alone, stupid %s'", r_info[random_quests[dun_level].r_idx].name + r_name);
+ return (FALSE);
+}
+bool quest_random_gen_hero_hook(char *fmt)
+{
+ int i;
+
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (!is_randhero(dun_level)) return (FALSE);
+
+ i = random_quests[dun_level].type;
+
+ m_allow_special[random_quests[dun_level].r_idx] = TRUE;
+ while (i)
+ {
+ int m_idx, y = rand_range(1, cur_hgt - 2), x = rand_range(1, cur_wid - 2);
+
+ m_idx = place_monster_one(y, x, random_quests[dun_level].r_idx, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+ m_ptr->mflag |= MFLAG_QUEST;
+ i--;
+ }
+ }
+ m_allow_special[random_quests[dun_level].r_idx] = FALSE;
+
+ return (FALSE);
+}
+bool quest_random_gen_hook(char *fmt)
+{
+ s32b x, y, bx0, by0;
+ int xstart;
+ int ystart;
+ int y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return (FALSE);
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return (FALSE);
+ if (!random_quests[dun_level].type) return (FALSE);
+ if (random_quests[dun_level].done) return (FALSE);
+ if (p_ptr->inside_quest) return (FALSE);
+ if (quest[QUEST_RANDOM].data[1]) return (FALSE);
+ if (is_randhero(dun_level)) return (FALSE);
+
+ by0 = get_next_arg(fmt);
+ bx0 = get_next_arg(fmt);
+
+ /* Pick a room size */
+ xsize = 0;
+ ysize = 0;
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, format("qrand%d.map", random_quests[dun_level].type), &ysize, &xsize, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM|CAVE_GLOW);
+ }
+ }
+
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM | CAVE_GLOW);
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ xstart = x1;
+ ystart = y1;
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, format("qrand%d.map", random_quests[dun_level].type), &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ for (x = x1; x < xstart; x++)
+ for (y = y1; y < ystart; y++)
+ {
+ cave[y][x].info |= CAVE_ICKY | CAVE_ROOM;
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ monster_type *m_ptr;
+ int i;
+
+ m_allow_special[random_quests[dun_level].r_idx] = TRUE;
+ i = place_monster_one(y, x, random_quests[dun_level].r_idx, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[random_quests[dun_level].r_idx] = FALSE;
+ if (i)
+ {
+ m_ptr = &m_list[i];
+ m_ptr->mflag |= MFLAG_QUEST;
+ }
+ }
+ }
+
+ /* Dont try another one for this generation */
+ quest[QUEST_RANDOM].data[1] = 1;
+
+ /* Boost level feeling a bit - a la pits */
+ rating += 10;
+
+ return (TRUE);
+}
+bool quest_random_dump_hook(char *fmt)
+{
+ static char *number[] =
+ { "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
+ int i, valid = 0, lscnt = 0, pcnt = 0;
+
+ for (i = 0; i < MAX_RANDOM_QUEST; i++)
+ {
+ if (random_quests[i].type)
+ {
+ valid++;
+ if (random_quests[i].done)
+ {
+ if (is_randhero(i))
+ lscnt++;
+ else
+ pcnt++;
+ }
+ }
+ }
+
+ if (valid)
+ {
+ if (pcnt > 10)
+ fprintf(hook_file, "\n You have completed %d princess quests.", pcnt);
+ else if (pcnt > 1)
+ fprintf(hook_file, "\n You have completed %s princess quests.", number[pcnt-2]);
+ else if (pcnt == 1)
+ fprintf(hook_file, "\n You have completed one princess quest.");
+ else
+ fprintf(hook_file, "\n You haven't completed a single princess quest.");
+
+ if (lscnt > 10)
+ fprintf(hook_file, "\n You have completed %d lost sword quests.", pcnt);
+ else if (lscnt > 1)
+ fprintf(hook_file, "\n You have completed %s lost sword quests.", number[pcnt-2]);
+ else if (lscnt == 1)
+ fprintf(hook_file, "\n You have completed one lost sword quest.");
+ else
+ fprintf(hook_file, "\n You haven't completed a single lost sword quest.");
+ }
+
+ return (FALSE);
+}
+bool quest_random_init_hook(int q_idx)
+{
+ add_hook(HOOK_MONSTER_DEATH, quest_random_death_hook, "rand_death");
+ add_hook(HOOK_NEW_LEVEL, quest_random_turn_hook, "rand_new_lvl");
+ add_hook(HOOK_LEVEL_REGEN, quest_random_turn_hook, "rand_regen_lvl");
+ add_hook(HOOK_LEVEL_END_GEN, quest_random_gen_hero_hook, "rand_gen_hero");
+ add_hook(HOOK_BUILD_ROOM1, quest_random_gen_hook, "rand_gen");
+ add_hook(HOOK_FEELING, quest_random_feeling_hook, "rand_feel");
+ add_hook(HOOK_CHAR_DUMP, quest_random_dump_hook, "rand_dump");
+ return (FALSE);
+}
diff --git a/src/q_shroom.c b/src/q_shroom.c
new file mode 100644
index 00000000..87d48cf4
--- /dev/null
+++ b/src/q_shroom.c
@@ -0,0 +1,291 @@
+#undef cquest
+#define cquest (quest[QUEST_SHROOM])
+
+bool quest_shroom_speak_hook(char *fmt);
+
+bool quest_shroom_town_gen_hook(char *fmt)
+{
+ int x = 1, y = 1, try = 10000;
+ s32b small;
+
+ small = get_next_arg(fmt);
+
+ /* Generate the shrooms field */
+ if ((!small) && (p_ptr->wilderness_y == 21) && (p_ptr->wilderness_x == 33))
+ {
+ /* Create the field */
+ for (x = (cur_wid / 2) - 7; x <= (cur_wid / 2) + 7; x++)
+ for (y = (cur_hgt / 2) - 5; y <= (cur_hgt / 2) + 5; y++)
+ cave_set_feat(y, x, 181);
+
+ /* Throw in some 'shrooms */
+ for (x = 0; x < (cquest.data[1] - cquest.data[0]); x++)
+ {
+ object_type forge, *q_ptr = &forge;
+
+ object_prep(q_ptr, lookup_kind(TV_FOOD, rand_range(1, 18)));
+ q_ptr->number = 1;
+ /* Mark them */
+ q_ptr->pval2 = 1;
+ drop_near(q_ptr, -1, rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5), rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7));
+ }
+
+ /* Throw in some dogs ;) */
+ y = rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5);
+ x = rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7);
+ m_allow_special[test_monster_name("Grip, Farmer Maggot's dog")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Grip, Farmer Maggot's dog"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Grip, Farmer Maggot's dog")] = FALSE;
+
+ y = rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5);
+ x = rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7);
+ m_allow_special[test_monster_name("Wolf, Farmer Maggot's dog")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Wolf, Farmer Maggot's dog"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Wolf, Farmer Maggot's dog")] = FALSE;
+
+ y = rand_range((cur_hgt / 2) - 5, (cur_hgt / 2) + 5);
+ x = rand_range((cur_wid / 2) - 7, (cur_wid / 2) + 7);
+ m_allow_special[test_monster_name("Fang, Farmer Maggot's dog")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Fang, Farmer Maggot's dog"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Fang, Farmer Maggot's dog")] = FALSE;
+
+ msg_print("You hear frenzied barking.");
+ }
+
+ /* Generate maggot in town, in daylight */
+ if ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18) || (cquest.status > QUEST_STATUS_COMPLETED) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (try)
+ {
+ /* Get a random spot */
+ y = randint(20) + (cur_hgt / 2) - 10;
+ x = randint(20) + (cur_wid / 2) - 10;
+
+ /* Is it a good spot ? */
+ /* Not in player los, and avoid shop grids */
+ if (!los(p_ptr->py, p_ptr->px, y, x) && cave_empty_bold(y, x) &&
+ cave_plain_floor_bold(y, x)) break;
+
+ /* One less try */
+ try--;
+ }
+
+ /* Place Farmer Maggot */
+ m_allow_special[test_monster_name("Farmer Maggot")] = TRUE;
+ place_monster_one(y, x, test_monster_name("Farmer Maggot"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Farmer Maggot")] = FALSE;
+
+ return FALSE;
+}
+bool quest_shroom_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (cquest.status > QUEST_STATUS_COMPLETED) return FALSE;
+
+ if ((r_idx == test_monster_name("Wolf, Farmer Maggot's dog")) ||
+ (r_idx == test_monster_name("Grip, Farmer Maggot's dog")) ||
+ (r_idx == test_monster_name("Fang, Farmer Maggot's dog")))
+ {
+ msg_print("The dog yells a last time and drops dead on the grass.");
+ }
+
+ return FALSE;
+}
+bool quest_shroom_give_hook(char *fmt)
+{
+ object_type *o_ptr;
+ monster_type *m_ptr;
+ s32b m_idx, item;
+
+ m_idx = get_next_arg(fmt);
+ item = get_next_arg(fmt);
+
+ o_ptr = &p_ptr->inventory[item];
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Farmer Maggot")) return (FALSE);
+
+ /* If one is dead .. its bad */
+ if ((r_info[test_monster_name("Grip, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Wolf, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Fang, Farmer Maggot's dog")].max_num == 0))
+ {
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ msg_print("My puppy! My poor, defenceless puppy...");
+ msg_print("YOU MURDERER! Out of my sight!");
+ delete_monster_idx(m_idx);
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+
+ if ((o_ptr->tval != TV_FOOD) || (o_ptr->pval2 != 1)) return (FALSE);
+
+ /* Take a mushroom */
+ inven_item_increase(item, -1);
+ inven_item_optimize(item);
+ cquest.data[0]++;
+
+ if (cquest.data[0] == cquest.data[1])
+ {
+ object_type forge, *q_ptr;
+
+ msg_print("Oh thank you!");
+ msg_print("Take my sling and those mushrooms, may they help you!");
+ msg_print("Farmer Maggot heads back to his house.");
+
+ /* Mushrooms */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_CURE_SERIOUS));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = rand_range(15, 20);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ if (inven_carry_okay(q_ptr))
+ inven_carry(q_ptr, FALSE);
+ else
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* The sling of farmer maggot */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOW, SV_SLING));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+ q_ptr->name1 = 149;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ delete_monster_idx(m_idx);
+
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ process_hooks_restart = TRUE;
+ }
+ else
+ msg_format("Oh thank you, but you still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+
+ return TRUE;
+}
+bool quest_shroom_speak_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+
+ if (m_list[m_idx].r_idx != test_monster_name("Farmer Maggot")) return (FALSE);
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ cptr m_name;
+
+ m_name = get_next_arg_str(fmt);
+
+ msg_format("%^s asks your help.", m_name);
+ exec_lua("ingame_help('monster_chat')");
+ }
+ else
+ {
+ /* If one is dead .. its bad */
+ if ((r_info[test_monster_name("Grip, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Wolf, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Fang, Farmer Maggot's dog")].max_num == 0))
+ {
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ msg_print("My puppy! My poor, defenceless puppy...");
+ msg_print("YOU MURDERER! Out of my sight!");
+ delete_monster_idx(m_idx);
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+ msg_format("You still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+ }
+ return (TRUE);
+}
+bool quest_shroom_chat_hook(char *fmt)
+{
+ monster_type *m_ptr;
+ s32b m_idx;
+
+ m_idx = get_next_arg(fmt);
+
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != test_monster_name("Farmer Maggot")) return (FALSE);
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ msg_print("My mushrooms, my mushrooms!");
+ msg_print("The rain, a dark horrible rain, began so I had to return to my home.");
+ msg_print("But when I came back my dogs were all mad and didn't let me near the field.");
+ msg_print("Could you please bring me back all the mushrooms that have grown in my field");
+ msg_print("to the west of Bree? Please try to not harm my dogs. They are so lovely...");
+
+ cquest.status = QUEST_STATUS_TAKEN;
+ quest[QUEST_SHROOM].init(QUEST_SHROOM);
+ }
+ else
+ {
+ /* If one is dead .. its bad */
+ if ((r_info[test_monster_name("Grip, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Wolf, Farmer Maggot's dog")].max_num == 0) ||
+ (r_info[test_monster_name("Fang, Farmer Maggot's dog")].max_num == 0))
+ {
+ cquest.status = QUEST_STATUS_FAILED_DONE;
+ msg_print("My puppy! My poor, defenceless puppy...");
+ msg_print("YOU MURDERER! Out of my sight!");
+ delete_monster_idx(m_idx);
+
+ del_hook(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+ msg_format("You still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+ }
+
+ return TRUE;
+}
+bool quest_shroom_init_hook(int q_idx)
+{
+ /* Get a number of 'shrooms */
+ if (!cquest.data[1])
+ {
+ cquest.data[0] = 0;
+ cquest.data[1] = rand_range(7, 14);
+ if (wizard) message_add(MESSAGE_MSG, format("Shrooms number %d", cquest.data[1]), TERM_BLUE);
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_shroom_death_hook, "shroom_death");
+ add_hook(HOOK_GIVE, quest_shroom_give_hook, "shroom_give");
+ add_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook, "shroom_town_gen");
+ add_hook(HOOK_CHAT, quest_shroom_chat_hook, "shroom_chat");
+ add_hook(HOOK_MON_SPEAK, quest_shroom_speak_hook, "shroom_speak");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MON_SPEAK, quest_shroom_speak_hook, "shroom_speak");
+ add_hook(HOOK_WILD_GEN, quest_shroom_town_gen_hook, "shroom_town_gen");
+ add_hook(HOOK_CHAT, quest_shroom_chat_hook, "shroom_chat");
+ }
+ return (FALSE);
+}
diff --git a/src/q_spider.c b/src/q_spider.c
new file mode 100644
index 00000000..8d88524a
--- /dev/null
+++ b/src/q_spider.c
@@ -0,0 +1,110 @@
+#undef cquest
+#define cquest (quest[QUEST_SPIDER])
+
+bool quest_spider_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_SPIDER) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "spiders.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ return TRUE;
+}
+bool quest_spider_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_SPIDER) return FALSE;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ if (mcnt <= 1)
+ {
+ cmsg_print(TERM_YELLOW, "The forest is now safer, thanks to you.");
+
+ /* Yavanna LOVES saving forests */
+ GOD(GOD_YAVANNA)
+ {
+ cmsg_print(TERM_L_GREEN, "You feel the gentle touch of Yavanna, as she smiles at you.");
+ inc_piety(GOD_YAVANNA, 6000);
+ }
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_spider_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+bool quest_spider_finish_hook(char *fmt)
+{
+ object_type forge, *q_ptr;
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_SPIDER) return FALSE;
+
+ c_put_str(TERM_YELLOW, "All of us praise your mighty deed in driving back the", 8, 0);
+ c_put_str(TERM_YELLOW, "menace. Take this as a reward.", 9, 0);
+
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_POTION, SV_POTION_AUGMENTATION));
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_REWARD;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_POISON;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_spider_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_spider_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_spider_death_hook, "spider_death");
+ add_hook(HOOK_GEN_QUEST, quest_spider_gen_hook, "spider_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_spider_finish_hook, "spider_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_thief.c b/src/q_thief.c
new file mode 100644
index 00000000..96d95658
--- /dev/null
+++ b/src/q_thief.c
@@ -0,0 +1,172 @@
+#undef cquest
+#define cquest (quest[QUEST_THIEVES])
+
+bool quest_thieves_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+ bool again = TRUE;
+
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(NULL, "thieves.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Rip the inventory from the player */
+ cmsg_print(TERM_YELLOW, "You feel a vicious blow on your head.");
+ while (again)
+ {
+ again = FALSE;
+ for (x = 0; x < INVEN_TOTAL; x++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[x];
+
+ if (!o_ptr->k_idx) continue;
+
+ if ((x >= INVEN_WIELD) && cursed_p(o_ptr)) continue;
+
+ inven_drop(x, 99, 4, 24, TRUE);
+
+ /* Thats ugly .. but it works */
+ again = TRUE;
+ break;
+ }
+ }
+
+ del_hook(HOOK_GEN_QUEST, quest_thieves_gen_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_thieves_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ /* ALARM !!! */
+ if ((cave[17][22].feat == FEAT_OPEN) ||
+ (cave[17][22].feat == FEAT_BROKEN))
+ {
+ cmsg_print(TERM_L_RED, "An alarm rings!");
+ aggravate_monsters(0);
+ cave_set_feat(14, 20, FEAT_OPEN);
+ cave_set_feat(14, 16, FEAT_OPEN);
+ cave_set_feat(14, 12, FEAT_OPEN);
+ cave_set_feat(14, 8, FEAT_OPEN);
+ cave_set_feat(20, 20, FEAT_OPEN);
+ cave_set_feat(20, 16, FEAT_OPEN);
+ cave_set_feat(20, 12, FEAT_OPEN);
+ cave_set_feat(20, 8, FEAT_OPEN);
+ msg_print("The door explodes.");
+ cave_set_feat(17, 22, FEAT_FLOOR);
+ }
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status < MSTATUS_FRIEND) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (!mcnt)
+ {
+ msg_print("The magic hiding the stairs is now gone.");
+ cave_set_feat(23, 4, FEAT_LESS);
+ cave[23][4].special = 0;
+
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_END_TURN, quest_thieves_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "You stopped the thieves and saved Bree!");
+ return (FALSE);
+ }
+ return FALSE;
+}
+bool quest_thieves_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_THIEVES) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for killing the band of thieves!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the hideout as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+
+ /* 10% chance to randomly select, otherwise use the combat/magic skill ratio */
+ if (magik(10) || (s_info[SKILL_COMBAT].value == s_info[SKILL_MAGIC].value))
+ {
+ *(quest[q_idx].plot) = (magik(50)) ? QUEST_TROLL : QUEST_WIGHT;
+ }
+ else
+ {
+ if (s_info[SKILL_COMBAT].value > s_info[SKILL_MAGIC].value)
+ *(quest[q_idx].plot) = QUEST_TROLL;
+ else
+ *(quest[q_idx].plot) = QUEST_WIGHT;
+ }
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_thieves_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool quest_thieves_feeling_hook(char *fmt)
+{
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ msg_print("You wake up in a prison cell.");
+ msg_print("All your possessions have been stolen!");
+
+ del_hook(HOOK_FEELING, quest_thieves_feeling_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool quest_thieves_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_END_TURN, quest_thieves_hook, "thieves_end_turn");
+ add_hook(HOOK_QUEST_FINISH, quest_thieves_finish_hook, "thieves_finish");
+ add_hook(HOOK_GEN_QUEST, quest_thieves_gen_hook, "thieves_geb");
+ add_hook(HOOK_FEELING, quest_thieves_feeling_hook, "thieves_feel");
+ }
+ return (FALSE);
+}
diff --git a/src/q_thrain.c b/src/q_thrain.c
new file mode 100644
index 00000000..5f5095d1
--- /dev/null
+++ b/src/q_thrain.c
@@ -0,0 +1,246 @@
+#undef cquest
+#define cquest (quest[QUEST_THRAIN])
+
+bool quest_thrain_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+ int r, x, y;
+ monster_type *m_ptr;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if ((cquest.status >= QUEST_STATUS_FINISHED) || (dun_level !=cquest.data[0]) || (dungeon_type != DUNGEON_DOL_GULDUR)) return (FALSE);
+ m_ptr = &m_list[m_idx];
+ if ((m_ptr->r_idx != test_monster_name("Dwar, Dog Lord of Waw")) && (m_ptr->r_idx != test_monster_name("Hoarmurath of Dir"))) return (FALSE);
+
+ cquest.data[2]++;
+
+ if (cquest.data[2] < 2) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "The magic hiding the room dissipates.");
+ for (x = 0; x < cur_wid; x++)
+ for (y = 0; y < cur_hgt; y++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ if (c_ptr->mimic != 61) continue;
+ if (!(c_ptr->info & CAVE_FREE)) continue;
+
+ c_ptr->mimic = 0;
+ lite_spot(y, x);
+ }
+
+ cquest.status = QUEST_STATUS_FINISHED;
+ cmsg_print(TERM_YELLOW, "Thrain speaks:");
+ cmsg_print(TERM_YELLOW, "'Ah, at last you came to me! But... I fear it is too late for me.");
+ cmsg_print(TERM_YELLOW, "However your quest continues, you must beware for the Necromancer");
+ cmsg_print(TERM_YELLOW, "is in fact Sauron, the Dark Lord! He stole the Ring of Durin and tortured");
+ cmsg_print(TERM_YELLOW, "me... arrgh... please make him pay!'");
+
+ /* Look for Thrain */
+ for (r = m_max - 1; r >= 1; r--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[r];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Is it the princess? */
+ if (m_ptr->r_idx == test_monster_name("Thrain, the King Under the Mountain"))
+ {
+ int x = m_ptr->fx;
+ int y = m_ptr->fy;
+ int i, j;
+ object_type forge, *q_ptr;
+
+ delete_monster_idx(r);
+
+ /* Wipe the glass walls and create a stair */
+ for (i = x - 1; i <= x + 1; i++)
+ for (j = y - 1; j <= y + 1; j++)
+ {
+ if (in_bounds(j, i)) cave_set_feat(j, i, FEAT_FLOOR);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+ object_prep(q_ptr, lookup_kind(TV_HELM, SV_DRAGON_HELM));
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_REWARD;
+ create_artifact(q_ptr, FALSE, TRUE);
+ q_ptr->art_name = quark_add("of Thrain");
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ break;
+ }
+ }
+
+
+ del_hook(HOOK_MONSTER_DEATH, quest_thrain_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+}
+
+bool quest_thrain_gen_hook(char *fmt)
+{
+ s32b x, y, bx0, by0;
+ int xstart;
+ int ystart;
+ int y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+ monster_type *m_ptr;
+
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if (cquest.data[1]) return (FALSE);
+ if ((cquest.status < QUEST_STATUS_TAKEN) || (cquest.status >= QUEST_STATUS_FINISHED)) return (FALSE);
+
+ by0 = get_next_arg(fmt);
+ bx0 = get_next_arg(fmt);
+
+ /* Pick a room size */
+ xsize = 0;
+ ysize = 0;
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "thrain.map", &ysize, &xsize, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM|CAVE_GLOW);
+ }
+ }
+
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM | CAVE_GLOW);
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ xstart = x1;
+ ystart = y1;
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "thrain.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ for (x = x1; x < xstart; x++)
+ for (y = y1; y < ystart; y++)
+ {
+ cave[y][x].info |= CAVE_ICKY | CAVE_ROOM | CAVE_FREE;
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int i;
+
+ m_allow_special[test_monster_name("Thrain, the King Under the Mountain")] = TRUE;
+ i = place_monster_one(y, x, test_monster_name("Thrain, the King Under the Mountain"), 0, FALSE, MSTATUS_NEUTRAL);
+ m_allow_special[test_monster_name("Thrain, the King Under the Mountain")] = FALSE;
+ }
+ if (cave[y][x].m_idx)
+ {
+ m_ptr = &m_list[cave[y][x].m_idx];
+ if ((m_ptr->r_idx == test_monster_name("Dwar, Dog Lord of Waw")) || (m_ptr->r_idx == test_monster_name("Hoarmurath of Dir")))
+ {
+ m_ptr->mflag |= MFLAG_QUEST;
+ }
+ }
+ }
+
+ /* Don't try another one for this generation */
+ cquest.data[1] = 1;
+
+ return (TRUE);
+}
+bool quest_thrain_feeling_hook(char *fmt)
+{
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if (cquest.status != QUEST_STATUS_UNTAKEN) return (FALSE);
+
+ cmsg_format(TERM_YELLOW, "You hear someone shouting under the torture.");
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest.init(QUEST_THRAIN);
+
+ return (FALSE);
+}
+bool quest_thrain_move_hook(char *fmt)
+{
+ s32b y;
+ s32b x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if ((cquest.status < QUEST_STATUS_TAKEN) || (cquest.status >= QUEST_STATUS_FINISHED)) return (FALSE);
+ if (!(c_ptr->info & CAVE_FREE)) return (FALSE);
+ if (c_ptr->mimic != 61) return (FALSE);
+
+ cmsg_print(TERM_YELLOW, "The magic hiding the room dissipates.");
+ for (x = 0; x < cur_wid; x++)
+ for (y = 0; y < cur_hgt; y++)
+ {
+ c_ptr = &cave[y][x];
+
+ if (c_ptr->mimic != 61) continue;
+ if (!(c_ptr->info & CAVE_FREE)) continue;
+
+ c_ptr->mimic = 0;
+ lite_spot(y, x);
+ }
+
+ return (FALSE);
+}
+bool quest_thrain_turn_hook(char *fmt)
+{
+ cquest.data[1] = 0;
+ cquest.data[2] = 0;
+ return (FALSE);
+}
+bool quest_thrain_init_hook(int q)
+{
+ if (!cquest.data[0])
+ {
+ cquest.data[0] = rand_range(d_info[DUNGEON_DOL_GULDUR].mindepth + 1, d_info[DUNGEON_DOL_GULDUR].maxdepth - 1);
+ if (wizard) message_add(MESSAGE_MSG, format("Thrain lvl %d", cquest.data[0]), TERM_BLUE);
+ }
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MOVE, quest_thrain_move_hook, "thrain_move");
+ }
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_LEVEL_REGEN, quest_thrain_turn_hook, "thrain_regen_lvl");
+ add_hook(HOOK_NEW_LEVEL, quest_thrain_turn_hook, "thrain_new_lvl");
+ add_hook(HOOK_BUILD_ROOM1, quest_thrain_gen_hook, "thrain_gen");
+ add_hook(HOOK_FEELING, quest_thrain_feeling_hook, "thrain_feel");
+ add_hook(HOOK_MONSTER_DEATH, quest_thrain_death_hook, "thrain_death");
+ }
+ return (FALSE);
+}
diff --git a/src/q_troll.c b/src/q_troll.c
new file mode 100644
index 00000000..b019b673
--- /dev/null
+++ b/src/q_troll.c
@@ -0,0 +1,180 @@
+#undef cquest
+#define cquest (quest[QUEST_TROLL])
+
+bool quest_troll_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_TROLL) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "trolls.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int m_idx;
+
+ m_allow_special[test_monster_name("Tom the Stone Troll")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("Tom the Stone Troll"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("Tom the Stone Troll")] = FALSE;
+
+ if (m_idx)
+ {
+ int o_idx;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ a_allow_special[ART_GLAMDRING] = TRUE;
+
+ /* Mega-Hack -- Prepare to make "Glamdring" */
+ object_prep(q_ptr, lookup_kind(TV_SWORD, SV_BROAD_SWORD));
+
+ /* Mega-Hack -- Mark this item as "Glamdring" */
+ q_ptr->name1 = ART_GLAMDRING;
+
+ /* Mega-Hack -- Actually create "Glamdring" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ a_allow_special[ART_GLAMDRING] = FALSE;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ else
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+ }
+
+ /* Reinitialize the ambush ... hehehe */
+ cquest.data[0] = FALSE;
+ return TRUE;
+}
+bool quest_troll_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_TROLL) return FALSE;
+
+ c_put_str(TERM_YELLOW, "I heard about your noble deeds.", 8, 0);
+ c_put_str(TERM_YELLOW, "Keep what you found... may it serve you well.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NAZGUL;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_troll_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_troll_death_hook(char *fmt)
+{
+ int x, y, xstart = 2, ystart = 2;
+ s32b r_idx, m_idx;
+ ;
+
+ m_idx = get_next_arg(fmt);
+
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_TROLL) return FALSE;
+
+ if (r_idx == test_monster_name("Tom the Stone Troll"))
+ {
+ cave_set_feat(3, 3, FEAT_LESS);
+ cave[3][3].special = 0;
+
+ cmsg_print(TERM_YELLOW, "Without Tom, the trolls won't be able to do much.");
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_troll_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "trolls.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ if (cquest.data[0]) return FALSE;
+
+ cquest.data[0] = TRUE;
+
+ msg_print("Oops, seems like an ambush...");
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Ahah ! */
+ if (c_ptr->info & CAVE_SPEC)
+ {
+ int r_idx;
+
+ cave_set_feat(y, x, FEAT_GRASS);
+ c_ptr->info &= ~CAVE_SPEC;
+
+ r_idx = (rand_int(2) == 0) ? test_monster_name("Forest troll") : test_monster_name("Stone troll");
+ place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ }
+ }
+
+ return FALSE;
+}
+bool quest_troll_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_troll_death_hook, "troll_death");
+ add_hook(HOOK_GEN_QUEST, quest_troll_gen_hook, "troll_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_troll_finish_hook, "troll_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_ultrae.c b/src/q_ultrae.c
new file mode 100644
index 00000000..d34d8b8f
--- /dev/null
+++ b/src/q_ultrae.c
@@ -0,0 +1,11 @@
+/*
+ * Here takes place the Evil ultra ending
+ */
+
+#undef cquest
+#define cquest (quest[QUEST_ULTRA_EVIL])
+
+bool quest_ultra_evil_init_hook(int q)
+{
+ return (FALSE);
+}
diff --git a/src/q_ultrag.c b/src/q_ultrag.c
new file mode 100644
index 00000000..37d14b0d
--- /dev/null
+++ b/src/q_ultrag.c
@@ -0,0 +1,276 @@
+/*
+ * Here takes place the Good ultra ending
+ */
+
+#undef cquest
+#define cquest (quest[QUEST_ULTRA_GOOD])
+
+bool quest_ultra_good_move_hook(char *fmt)
+{
+ s32b y, x;
+ cave_type *c_ptr;
+
+ y = get_next_arg(fmt);
+ x = get_next_arg(fmt);
+ c_ptr = &cave[y][x];
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ bool old_quick_messages = quick_messages;
+
+ if (quest[QUEST_MORGOTH].status < QUEST_STATUS_FINISHED) return (FALSE);
+
+ /* The mirror of Galadriel */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 23)) return (FALSE);
+
+ quick_messages = FALSE;
+ cmsg_print(TERM_L_BLUE, "You meet Galadriel.");
+ cmsg_print(TERM_YELLOW, "'I still cannot believe this is all over.'");
+ cmsg_print(TERM_YELLOW, "'Morgoth's reign of terror is over at last!'");
+ cmsg_print(TERM_YELLOW, "'His spirit has been banished to the Void where he cannot do much harm.'");
+ cmsg_print(TERM_YELLOW, "'We can never thank you enough, hero!'");
+ cmsg_print(TERM_L_BLUE, "Although everything seems all right, Galadriel seems a little subdued.");
+ cmsg_print(TERM_YELLOW, "'The spirit of Morgoth is not destroyed however -- only banished.'");
+ cmsg_print(TERM_YELLOW, "'He can still control his allies left on Arda.'");
+ cmsg_print(TERM_YELLOW, "'Maybe... maybe there could be a way to remove the threat of evil forever.'");
+ cmsg_print(TERM_YELLOW, "'Somebody would have to go into the Void to do it.'");
+ cmsg_print(TERM_YELLOW, "'But going there is certain death; we cannot ask it of anyone.'");
+ cmsg_print(TERM_YELLOW, "'But you may choose, of your own free will, to attempt it...'");
+ cmsg_print(TERM_L_BLUE, "Galadriel plainly presents the choice that now lies before you:");
+
+ cmsg_print(TERM_YELLOW, "'You have earned the right to make whatever you wish of your future.'");
+ cmsg_print(TERM_YELLOW, "'Become a ruler of Arda if you so desire; reign long, enjoying'");
+ cmsg_print(TERM_YELLOW, "'the adulation of all, and have a happy life. Or, you can turn your'");
+ cmsg_print(TERM_YELLOW, "'back on safety. Enter the Void, alone, to fight a hopeless battle'");
+ cmsg_print(TERM_YELLOW, "'and face certain death.'");
+
+ /* This is SO important a question that flush pending inputs */
+ flush();
+
+ if (!get_check("Will you stay on Arda and lead a happy life?"))
+ {
+ cmsg_print(TERM_YELLOW, "'So be it. I will open a portal to the Void.'");
+ cmsg_print(TERM_YELLOW, "'But you must know this: the portal can only lead one way.'");
+ cmsg_print(TERM_YELLOW, "'It will close once you enter, so as not to permit the horrors'");
+ cmsg_print(TERM_YELLOW, "'that lurk in the Void to enter Arda. Your only way to come back'");
+ cmsg_print(TERM_YELLOW, "'is to defeat the spirit of Morgoth, known as Melkor.'");
+ cmsg_print(TERM_YELLOW, "'You will not be able to recall back either.'");
+ cmsg_print(TERM_YELLOW, "'You can still choose to retire; it is not too late'");
+ cmsg_print(TERM_YELLOW, "'to save your life.'");
+ cmsg_print(TERM_YELLOW, "'One last thing: It is quite certain that Melkor will have erected'");
+ cmsg_print(TERM_YELLOW, "'powerful magical barriers around him. You certainly will'");
+ cmsg_print(TERM_YELLOW, "'need to find a way to break them to get to him.'");
+
+ /* Create the entrance */
+ cave_set_feat(y - 5, x, FEAT_MORE);
+ cave[y - 5][x].special = 11;
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest.init(QUEST_ULTRA_GOOD);
+ }
+ quick_messages = old_quick_messages;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+bool quest_ultra_good_stair_hook(char *fmt)
+{
+ cptr dir;
+
+ dir = get_next_arg_str(fmt);
+
+ if (dungeon_type != DUNGEON_VOID)
+ return FALSE;
+
+ /* Cant leave */
+ if ((!strcmp(dir, "up")) && (dun_level == 128))
+ {
+ cmsg_print(TERM_YELLOW, "The portal to Arda is now closed.");
+ return TRUE;
+ }
+ /* there is no coming back */
+ if ((!strcmp(dir, "up")) && (dun_level == 150))
+ {
+ cmsg_print(TERM_YELLOW, "The barrier seems to be impenetrable from this side.");
+ cmsg_print(TERM_YELLOW, "You will have to move on.");
+ return TRUE;
+ }
+ /* Cant enter without the flame imperishable */
+ if ((!strcmp(dir, "down")) && (dun_level == 149))
+ {
+ int i;
+ bool ultimate = FALSE;
+
+ /* Now look for an ULTIMATE artifact, that is, one imbued with the flame */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the gloves */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_ULTIMATE)
+ {
+ ultimate = TRUE;
+ break;
+ }
+ }
+
+ if (!ultimate)
+ {
+ cmsg_print(TERM_YELLOW, "It seems the level is protected by an impassable barrier of pure magic.");
+ cmsg_print(TERM_YELLOW, "Only the most powerful magic could remove it. You will need to use");
+ cmsg_print(TERM_YELLOW, "the Flame Imperishable to pass. The source of Eru Iluvatar's own power.");
+ return TRUE;
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "The power of the Flame Imperishable shatters the magical barrier.");
+ cmsg_print(TERM_YELLOW, "The way before you is free.");
+ }
+ }
+
+ return FALSE;
+}
+
+bool quest_ultra_good_recall_hook(char *fmt)
+{
+ if ((dungeon_type != DUNGEON_VOID) && (dungeon_type != DUNGEON_NETHER_REALM))
+ return FALSE;
+
+ cmsg_print(TERM_YELLOW, "You cannot recall. The portal to Arda is closed.");
+ return TRUE;
+}
+
+bool quest_ultra_good_death_hook(char *fmt)
+{
+ s32b m_idx = get_next_arg(fmt);
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Melkor is dead! */
+ if (m_ptr->r_idx == 1044)
+ {
+ /* Total winner */
+ total_winner = WINNER_ULTRA;
+ has_won = WINNER_ULTRA;
+ quest[QUEST_ULTRA_GOOD].status = QUEST_STATUS_FINISHED;
+
+ /* Redraw the "title" */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Congratulations */
+ cmsg_print(TERM_L_GREEN, "****** CONGRATULATIONS ******");
+ cmsg_print(TERM_L_GREEN, "You have done more than the impossible. You ended the threat of");
+ cmsg_print(TERM_L_GREEN, "Melkor forever. Thanks to you, Arda will live in eternal peace.");
+ cmsg_print(TERM_L_GREEN, "You feel the spirit of Eru touching you. You feel your spirit rising!");
+ cmsg_print(TERM_L_GREEN, "Before you, a portal to Arda opens. You can now come back to your world");
+ cmsg_print(TERM_L_GREEN, "and live happily ever after.");
+ cmsg_print(TERM_L_GREEN, "What you do now is up to you, but your deeds shall ever be remembered.");
+ cmsg_print(TERM_L_GREEN, "You may retire (commit suicide) when you are ready.");
+
+ /* Create the entrance */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MORE);
+
+ /* Remove now used hook */
+ del_hook(HOOK_MONSTER_DEATH, quest_ultra_good_death_hook);
+ process_hooks_restart = TRUE;
+
+ /* End plot */
+ *(quest[QUEST_ULTRA_GOOD].plot) = QUEST_NULL;
+ }
+
+ /* Tik'svvrzllat is dead! */
+ if (m_ptr->r_idx == 1032)
+ {
+ int i;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make the Flame Imperishable */
+ object_prep(q_ptr, lookup_kind(TV_JUNK, 255));
+
+ /* Mega-Hack -- Actually create the Flame Imperishable */
+ k_allow_special[296] = TRUE;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ k_allow_special[296] = FALSE;
+
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Find a space */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Skip non-objects */
+ if (!p_ptr->inventory[i].k_idx) break;
+ }
+ /* Arg, no space ! */
+ if (i == INVEN_PACK)
+ {
+ char o_name[200];
+
+ object_desc(o_name, &p_ptr->inventory[INVEN_PACK - 1], FALSE, 0);
+
+ /* Drop the item */
+ inven_drop(INVEN_PACK - 1, 99, p_ptr->py, p_ptr->px, FALSE);
+
+ cmsg_format(TERM_VIOLET, "You feel the urge to drop your %s to make room in your inventory.", o_name);
+ }
+
+ /* Carry it */
+ cmsg_format(TERM_VIOLET, "You feel the urge to pick up the Flame Imperishable.");
+ inven_carry(q_ptr, FALSE);
+ }
+ return (FALSE);
+}
+bool quest_ultra_good_dump_hook(char *fmt)
+{
+ if (quest[QUEST_ULTRA_GOOD].status >= QUEST_STATUS_TAKEN)
+ {
+ /* Ultra winner ! */
+ if (total_winner == WINNER_ULTRA)
+ {
+ fprintf(hook_file, "\n You destroyed Melkor forever and have been elevated to the status of Vala by Eru Iluvatar.");
+ fprintf(hook_file, "\n Arda will forever be free.");
+ }
+ else
+ {
+ /* Tried and failed */
+ if (death)
+ {
+ fprintf(hook_file, "\n You tried to destroy Melkor forever, but died in the attempt.");
+ fprintf(hook_file, "\n Arda will be quiet, but not free from evil.");
+ }
+ }
+ }
+ return (FALSE);
+}
+
+
+bool quest_ultra_good_init_hook(int q)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_STAIR, quest_ultra_good_stair_hook, "ultrag_stair");
+ add_hook(HOOK_RECALL, quest_ultra_good_recall_hook, "ultrag_recall");
+ add_hook(HOOK_MONSTER_DEATH, quest_ultra_good_death_hook, "ultrag_death");
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook(HOOK_MOVE, quest_ultra_good_move_hook, "ultrag_move");
+ }
+ add_hook(HOOK_CHAR_DUMP, quest_ultra_good_dump_hook, "ultrag_dump");
+ return (FALSE);
+}
diff --git a/src/q_wight.c b/src/q_wight.c
new file mode 100644
index 00000000..a49e3088
--- /dev/null
+++ b/src/q_wight.c
@@ -0,0 +1,156 @@
+#undef cquest
+#define cquest (quest[QUEST_WIGHT])
+
+bool quest_wight_gen_hook(char *fmt)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_WIGHT) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file_full = TRUE;
+ process_dungeon_file(NULL, "wights.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ process_dungeon_file_full = FALSE;
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int m_idx = 0;
+
+ m_allow_special[test_monster_name("The Wight-King of the Barrow-downs")] = TRUE;
+ m_idx = place_monster_one(y, x, test_monster_name("The Wight-King of the Barrow-downs"), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[test_monster_name("The Wight-King of the Barrow-downs")] = FALSE;
+
+ if (m_idx)
+ {
+ int o_idx;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ /* Prepare to make the */
+ object_prep(q_ptr, lookup_kind(TV_SOFT_ARMOR, SV_FILTHY_RAG));
+
+ /* Name the rags */
+
+ q_ptr->art_name = quark_add("of the Wight");
+
+ q_ptr->art_flags1 |= ( TR1_INT | TR1_SEARCH );
+ q_ptr->art_flags2 |= ( TR2_RES_BLIND | TR2_SENS_FIRE | TR2_RES_CONF );
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD | TR3_SEE_INVIS);
+
+ /* For game balance... */
+ q_ptr->art_flags3 |= (TR3_CURSED | TR3_HEAVY_CURSE);
+ q_ptr->ident |= IDENT_CURSED;
+
+ if (randint(2) == 1)
+ {
+ q_ptr->art_flags1 |= (TR1_SPELL);
+ q_ptr->pval = 6;
+ }
+ else
+ {
+ q_ptr->art_flags1 |= (TR1_MANA);
+ q_ptr->pval = 2;
+ }
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->next_o_idx = m_list[m_idx].hold_o_idx;
+
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idx = o_idx;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+bool quest_wight_death_hook(char *fmt)
+{
+ s32b r_idx, m_idx;
+
+ m_idx = get_next_arg(fmt);
+ r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_WIGHT) return FALSE;
+
+ if (r_idx == test_monster_name("The Wight-King of the Barrow-downs"))
+ {
+ cmsg_print(TERM_YELLOW, "Without their King the wights won't be able to do much.");
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ cave[p_ptr->py][p_ptr->px].special = 0;
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_wight_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+bool quest_wight_finish_hook(char *fmt)
+{
+ s32b q_idx;
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_WIGHT) return FALSE;
+
+ c_put_str(TERM_YELLOW, "I heard about your noble deeds.", 8, 0);
+ c_put_str(TERM_YELLOW, "Keep what you found .. may it serve you well.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_NAZGUL;
+ quest[*(quest[q_idx].plot)].init(*(quest[q_idx].plot));
+
+ del_hook(HOOK_QUEST_FINISH, quest_wight_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+bool quest_wight_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_wight_death_hook, "wight_death");
+ add_hook(HOOK_GEN_QUEST, quest_wight_gen_hook, "wight_gen");
+ add_hook(HOOK_QUEST_FINISH, quest_wight_finish_hook, "wight_finish");
+ }
+ return (FALSE);
+}
diff --git a/src/q_wolves.c b/src/q_wolves.c
new file mode 100644
index 00000000..0b2825c7
--- /dev/null
+++ b/src/q_wolves.c
@@ -0,0 +1,128 @@
+#undef cquest
+#define cquest (quest[QUEST_WOLVES])
+
+bool quest_wolves_gen_hook(char *fmt)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_WOLVES) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(NULL, "wolves.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some random wolves */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ place_monster_one(y, x, 196, 0, magik(50), MSTATUS_ENEMY);
+ --i;
+ }
+ }
+
+ /* Place some random wargs */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ place_monster_one(y, x, 257, 0, magik(50), MSTATUS_ENEMY);
+ --i;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+bool quest_wolves_death_hook(char *fmt)
+{
+ int i, mcnt = 0;
+
+ if (p_ptr->inside_quest != QUEST_WOLVES) return FALSE;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status <= MSTATUS_ENEMY) mcnt++;
+ }
+
+ /* Nobody left ? */
+ if (mcnt <= 1)
+ {
+ quest[p_ptr->inside_quest].status = QUEST_STATUS_COMPLETED;
+ del_hook(HOOK_MONSTER_DEATH, quest_wolves_death_hook);
+ del_hook(HOOK_GEN_QUEST, quest_wolves_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Lothlorien is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+bool quest_wolves_finish_hook(char *fmt)
+{
+ s32b q_idx;
+
+ q_idx = get_next_arg(fmt);
+
+ if (q_idx != QUEST_WOLVES) return FALSE;
+
+ c_put_str(TERM_YELLOW, "Thank you for killing the pack of wolves!", 8, 0);
+ c_put_str(TERM_YELLOW, "You can use the hut as your house as a reward.", 9, 0);
+
+ /* Continue the plot */
+ *(quest[q_idx].plot) = QUEST_SPIDER;
+
+ return TRUE;
+}
+
+bool quest_wolves_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook(HOOK_MONSTER_DEATH, quest_wolves_death_hook, "wolves_monster_death");
+ add_hook(HOOK_QUEST_FINISH, quest_wolves_finish_hook, "wolves_finish");
+ add_hook(HOOK_GEN_QUEST, quest_wolves_gen_hook, "wolves_geb");
+ }
+ return (FALSE);
+}
diff --git a/src/quest.pkg b/src/quest.pkg
new file mode 100644
index 00000000..4ba93b7a
--- /dev/null
+++ b/src/quest.pkg
@@ -0,0 +1,170 @@
+/* File: quest.pkg */
+
+/*
+ * Purpose: Lua interface definitions for quests.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @name Quest Status Flags
+ * @brief Quest status
+ * @{ */
+
+/** @def QUEST_STATUS_IGNORED */
+#define QUEST_STATUS_IGNORED -1
+
+/** @def QUEST_STATUS_UNTAKEN */
+#define QUEST_STATUS_UNTAKEN 0
+
+/** @def QUEST_STATUS_TAKEN */
+#define QUEST_STATUS_TAKEN 1
+
+/** @def QUEST_STATUS_COMPLETED */
+#define QUEST_STATUS_COMPLETED 2
+
+/** @def QUEST_STATUS_REWARDED */
+#define QUEST_STATUS_REWARDED 3
+
+/** @def QUEST_STATUS_FAILED */
+#define QUEST_STATUS_FAILED 4
+
+/** @def QUEST_STATUS_FINISHED */
+#define QUEST_STATUS_FINISHED 5
+
+/** @def QUEST_STATUS_FAILED_DONE */
+#define QUEST_STATUS_FAILED_DONE 6
+/** @} */
+
+/** @struct quest_type
+ * @brief Quest
+ */
+struct quest_type
+{
+ /** @structvar silent
+ * @brief Boolean
+ * @note Does quest appear on quest list?
+ */
+ bool silent;
+
+ /** @structvar dynamic_desc
+ * @brief Boolean
+ * @note Do we need to ask a function to get the description ?
+ */
+ bool dynamic_desc;
+
+ /** @structvar status
+ * @brief Number
+ * @note Is the quest taken, completed, finished?
+ */
+ s16b status;
+
+ /** @structvar level
+ * @brief Number
+ * @note Dungeon level
+ */
+ s16b level;
+
+ /** @structvar type
+ * @brief Number
+ * @note Lua or C ?
+ */
+ byte type;
+};
+
+/** @var max_q_idx
+ * @brief Number
+ * @note Maximum number of quests in quest list
+ */
+extern s16b max_q_idx;
+
+/** @var quest_aux;
+ * @brief quest_type
+ * @note Array of quests
+ */
+extern quest_type quest[max_q_idx] @ quest_aux;
+
+$static quest_type *lua_get_quest(int q_idx){return &quest[q_idx];}
+
+/** @fn quest(int q_idx);
+ * @brief Return quest with index "q_idx" from quest array.\n
+ * @param q_idx Number \n the index of a quest in the quest array.
+ * @brief Quest index
+ * @return quest_type \n The quest at index "q_idx".
+ * @note (see file w_quest.c)
+ */
+static quest_type *lua_get_quest @ quest(int q_idx);
+
+/** @fn new_quest(char *name);
+ * @dgonly
+ * @brief Add a new quest to the end of the quest array.\n
+ * @param *name String \n the name of the new quest.
+ * @brief Quest name
+ * @return Number \n The index of the new quest in the quest array.
+ * @note (see file lua_bind.c)
+ */
+extern s16b add_new_quest @ new_quest(char *name);
+
+/** @fn quest_desc(int q_idx, int d, char *desc);
+ * @dgonly
+ * @brief Return the description of a quest.\n
+ * @param q_idx Number \n the index of a quest in the quest array.
+ * @brief Quest index
+ * @param d Number \n the index of a line in the quest description.
+ * @brief Description line
+ * @param *desc String
+ * @brief Description
+ * @return *desc String \n Line "d" of the description of quest with index
+ * "q_idx" in the quest array.
+ * @note (see file lua_bind.c)
+ */
+extern void desc_quest @ quest_desc(int q_idx, int d, char *desc);
+
+/** @fn get_new_bounty_monster(int lev);
+ * @brief Find a good random bounty monster.\n
+ * @param lev Number \n the level of the bounty monster.
+ * @brief Monster level
+ * @return Number \n The index of the monster in the r_info array.
+ * @note (see file lua_bind.c)
+ */
+extern int lua_get_new_bounty_monster@get_new_bounty_monster(int lev);
diff --git a/src/randart.c b/src/randart.c
new file mode 100644
index 00000000..de1af12f
--- /dev/null
+++ b/src/randart.c
@@ -0,0 +1,496 @@
+/* File: randart.c */
+
+/* Purpose: Randart creation code */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/* Chance of using syllables to form the name instead of the "template" files */
+#define TABLE_NAME 45
+#define A_CURSED 13
+#define WEIRD_LUCK 12
+#define ACTIVATION_CHANCE 3
+
+/*
+ * Attempt to add a power to a randart
+ */
+static bool grab_one_power(int *ra_idx, object_type *o_ptr, bool good, s16b *max_times)
+{
+ int i = 0, j;
+ int *ok_ra, ok_num = 0;
+ bool ret = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ C_MAKE(ok_ra, max_ra_idx, int);
+
+ /* Grab the ok randart */
+ for (i = 0; i < max_ra_idx; i++)
+ {
+ randart_part_type *ra_ptr = &ra_info[i];
+ bool ok = FALSE;
+
+ /* Must have the correct fields */
+ for (j = 0; j < 20; j++)
+ {
+ if (ra_ptr->tval[j] == o_ptr->tval)
+ {
+ if ((ra_ptr->min_sval[j] <= o_ptr->sval) && (ra_ptr->max_sval[j] >= o_ptr->sval)) ok = TRUE;
+ }
+
+ if (ok) break;
+ }
+ if ((0 < ra_ptr->max_pval) && (ra_ptr->max_pval < o_ptr->pval)) ok = FALSE;
+ if (!ok)
+ {
+ /* Doesnt count as a try*/
+ continue;
+ }
+
+ /* Good should be good, bad should be bad */
+ if (good && (ra_ptr->value <= 0)) continue;
+ if ((!good) && (ra_ptr->value > 0)) continue;
+
+ if (max_times[i] >= ra_ptr->max) continue;
+
+ /* Must NOT have the antagonic flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f1 & ra_ptr->aflags1) continue;
+ if (f2 & ra_ptr->aflags2) continue;
+ if (f3 & ra_ptr->aflags3) continue;
+ if (f4 & ra_ptr->aflags4) continue;
+ if (f5 & ra_ptr->aflags5) continue;
+ if (esp & ra_ptr->aesp) continue;
+
+ /* ok */
+ ok_ra[ok_num++] = i;
+ }
+
+ /* Now test them a few times */
+ for (i = 0; i < ok_num * 10; i++)
+ {
+ randart_part_type *ra_ptr;
+
+ i = ok_ra[rand_int(ok_num)];
+ ra_ptr = &ra_info[i];
+
+ /* XXX XXX Enforce minimum player level (loosely) */
+ if (ra_ptr->level > p_ptr->lev)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (ra_ptr->level - p_ptr->lev);
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0)
+ {
+ continue;
+ }
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(ra_ptr->mrarity) < ra_ptr->rarity)
+ {
+ continue;
+ }
+
+ /* Hack -- mark the item as an ego */
+ *ra_idx = i;
+ max_times[i]++;
+
+ /* Success */
+ ret = TRUE;
+ break;
+ }
+
+ C_FREE(ok_ra, max_ra_idx, int);
+
+ /* Return */
+ return (ret);
+}
+
+void give_activation_power (object_type * o_ptr)
+{
+ /* int type = 0, chance = 0; */
+
+
+ /* A type was chosen... */
+#if 0 /* DGDGDGDG -- Todo later */
+ o_ptr->xtra2 = type;
+ o_ptr->art_flags3 |= TR3_ACTIVATE;
+ o_ptr->timeout = 0;
+#else
+ o_ptr->xtra2 = 0;
+ o_ptr->art_flags3 &= ~TR3_ACTIVATE;
+ o_ptr->timeout = 0;
+#endif
+}
+
+
+int get_activation_power()
+{
+ object_type *o_ptr, forge;
+
+ o_ptr = &forge;
+
+ give_activation_power(o_ptr);
+
+ return o_ptr->xtra2;
+}
+
+#define MIN_NAME_LEN 5
+#define MAX_NAME_LEN 9
+#define S_WORD 26
+#define E_WORD S_WORD
+
+static long lprobs[S_WORD + 1][S_WORD + 1][S_WORD + 1];
+static long ltotal[S_WORD + 1][S_WORD + 1];
+
+/*
+ * Use W. Sheldon Simms' random name generator. This function builds
+ * probability tables which are used later on for letter selection. It
+ * relies on the ASCII character set.
+ */
+void build_prob(cptr learn)
+{
+ int c_prev, c_cur, c_next;
+
+ /* Build raw frequencies */
+ while (1)
+ {
+ c_prev = c_cur = S_WORD;
+
+ do
+ {
+ c_next = *learn++;
+ }
+ while (!isalpha(c_next) && (c_next != '\0'));
+
+ if (c_next == '\0') break;
+
+ do
+ {
+ c_next = A2I(tolower(c_next));
+ lprobs[c_prev][c_cur][c_next]++;
+ ltotal[c_prev][c_cur]++;
+ c_prev = c_cur;
+ c_cur = c_next;
+ c_next = *learn++;
+ }
+ while (isalpha(c_next));
+
+ lprobs[c_prev][c_cur][E_WORD]++;
+ ltotal[c_prev][c_cur]++;
+ }
+}
+
+
+/*
+ * Use W. Sheldon Simms' random name generator. Generate a random word using
+ * the probability tables we built earlier. Relies on the ASCII character
+ * set. Relies on European vowels (a, e, i, o, u). The generated name should
+ * be copied/used before calling this function again.
+ */
+static char *make_word(void)
+{
+ static char word_buf[90];
+ int r, totalfreq;
+ int tries, lnum, vow;
+ int c_prev, c_cur, c_next;
+ char *cp;
+
+startover:
+ vow = 0;
+ lnum = 0;
+ tries = 0;
+ cp = word_buf;
+ c_prev = c_cur = S_WORD;
+
+ while (1)
+ {
+getletter:
+ c_next = 0;
+ r = rand_int(ltotal[c_prev][c_cur]);
+ totalfreq = lprobs[c_prev][c_cur][c_next];
+
+ while (totalfreq <= r)
+ {
+ c_next++;
+ totalfreq += lprobs[c_prev][c_cur][c_next];
+ }
+
+ if (c_next == E_WORD)
+ {
+ if ((lnum < MIN_NAME_LEN) || vow == 0)
+ {
+ tries++;
+ if (tries < 10) goto getletter;
+ goto startover;
+ }
+ *cp = '\0';
+ break;
+ }
+
+ if (lnum >= MAX_NAME_LEN) goto startover;
+
+ *cp = I2A(c_next);
+
+ if (is_a_vowel(*cp)) vow++;
+
+ cp++;
+ lnum++;
+ c_prev = c_cur;
+ c_cur = c_next;
+ }
+
+ word_buf[0] = toupper(word_buf[0]);
+
+ return (word_buf);
+}
+
+
+void get_random_name(char * return_name)
+{
+ char *word = make_word();
+
+ if (rand_int(3) == 0)
+ sprintf(return_name, "'%s'", word);
+ else
+ sprintf(return_name, "of %s", word);
+}
+
+
+bool create_artifact(object_type *o_ptr, bool a_scroll, bool get_name)
+{
+ char new_name[80];
+ int powers = 0, i;
+ s32b total_flags, total_power = 0;
+ bool a_cursed = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+ s16b *max_times;
+ s16b pval = 0;
+ bool limit_blows = FALSE;
+
+ strcpy(new_name, "");
+
+ if ((!a_scroll) && (randint(A_CURSED) == 1)) a_cursed = TRUE;
+
+ i = 0;
+ while (ra_gen[i].chance)
+ {
+ powers += damroll(ra_gen[i].dd, ra_gen[i].ds) + ra_gen[i].plus;
+ i++;
+ }
+
+ if ((!a_cursed) && (randint(30) == 1)) powers *= 2;
+
+ if (a_cursed) powers /= 2;
+
+ C_MAKE(max_times, max_ra_idx, s16b);
+
+ /* Main loop */
+ while (powers)
+ {
+ int ra_idx;
+ randart_part_type *ra_ptr;
+
+ powers--;
+
+ if (!grab_one_power(&ra_idx, o_ptr, TRUE, max_times)) continue;
+
+ ra_ptr = &ra_info[ra_idx];
+
+ if (wizard) msg_format("Adding randart power: %d", ra_idx);
+
+ total_power += ra_ptr->value;
+
+ o_ptr->art_flags1 |= ra_ptr->flags1;
+ o_ptr->art_flags2 |= ra_ptr->flags2;
+ o_ptr->art_flags3 |= ra_ptr->flags3;
+ o_ptr->art_flags4 |= ra_ptr->flags4;
+ o_ptr->art_flags5 |= ra_ptr->flags5;
+ o_ptr->art_esp |= ra_ptr->esp;
+
+ add_random_ego_flag(o_ptr, ra_ptr->fego, &limit_blows);
+
+ /* get flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- acquire "cursed" flag */
+ if (f3 & TR3_CURSED) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack -- obtain bonuses */
+ if (ra_ptr->max_to_h > 0) o_ptr->to_h += randint(ra_ptr->max_to_h);
+ if (ra_ptr->max_to_h < 0) o_ptr->to_h -= randint( -ra_ptr->max_to_h);
+ if (ra_ptr->max_to_d > 0) o_ptr->to_d += randint(ra_ptr->max_to_d);
+ if (ra_ptr->max_to_d < 0) o_ptr->to_d -= randint( -ra_ptr->max_to_d);
+ if (ra_ptr->max_to_a > 0) o_ptr->to_a += randint(ra_ptr->max_to_a);
+ if (ra_ptr->max_to_a < 0) o_ptr->to_a -= randint( -ra_ptr->max_to_a);
+
+ /* Hack -- obtain pval */
+ if (((pval > ra_ptr->max_pval) && ra_ptr->max_pval) || (!pval)) pval = ra_ptr->max_pval;
+ };
+ C_FREE(max_times, max_ra_idx, s16b);
+
+ if (pval > 0) o_ptr->pval = randint(pval);
+ if (pval < 0) o_ptr->pval = randint( -pval);
+
+ /* No insane number of blows */
+ if (limit_blows && (o_ptr->art_flags1 & TR1_BLOWS))
+ {
+ if (o_ptr->pval > 2) o_ptr->pval = randint(2);
+ }
+
+ /* Just to be sure */
+ o_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD);
+
+ total_flags = flag_cost(o_ptr, o_ptr->pval);
+ if (cheat_peek) msg_format("%ld", total_flags);
+
+ if (a_cursed) curse_artifact(o_ptr);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (get_name)
+ {
+ if (a_scroll)
+ {
+ char dummy_name[80];
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= (IDENT_STOREB | IDENT_MENTAL);
+
+ strcpy(dummy_name, "");
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+
+ if (get_string("What do you want to call the artifact? ", dummy_name, 80))
+ {
+ strcpy(new_name, "called '");
+ strcat(new_name, dummy_name);
+ strcat(new_name, "'");
+ }
+ else
+ /* Default name = of 'player name' */
+ sprintf(new_name, "of '%s'", player_name);
+ }
+ else
+ {
+ get_random_name(new_name);
+ }
+ }
+
+ /* Save the inscription */
+ o_ptr->art_name = quark_add(new_name);
+ o_ptr->name2 = o_ptr->name2b = 0;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* HACKS for ToME */
+ if (o_ptr->tval == TV_CLOAK && o_ptr->sval == SV_MIMIC_CLOAK)
+ {
+ s32b mimic;
+ call_lua("find_random_mimic_shape", "(d,d)", "d", 127, TRUE, &mimic);
+ o_ptr->pval2 = mimic;
+ }
+ else if (f5 & TR5_SPELL_CONTAIN)
+ {
+ o_ptr->pval2 = -1;
+ }
+
+ return TRUE;
+}
+
+
+bool artifact_scroll(void)
+{
+ int item;
+ bool okay = FALSE;
+ object_type *o_ptr;
+ char o_name[80];
+
+ cptr q, s;
+
+
+ /* Enchant weapon/armour */
+ item_tester_hook = item_tester_hook_artifactable;
+
+ /* Get an item */
+ q = "Enchant which item? ";
+ s = "You have nothing to enchant.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Describe */
+ msg_format("%s %s radiate%s a blinding light!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+
+ if (artifact_p(o_ptr))
+ {
+ msg_format("The %s %s already %s!",
+ o_name, ((o_ptr->number > 1) ? "are" : "is"),
+ ((o_ptr->number > 1) ? "artifacts" : "an artifact"));
+ okay = FALSE;
+ }
+
+ else if (o_ptr->name2)
+ {
+ msg_format("The %s %s already %s!",
+ o_name, ((o_ptr->number > 1) ? "are" : "is"),
+ ((o_ptr->number > 1) ? "ego items" : "an ego item"));
+ okay = FALSE;
+ }
+
+ else
+ {
+ if (o_ptr->number > 1)
+ {
+ msg_print("Not enough enough energy to enchant more than one object!");
+ msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was"));
+ o_ptr->number = 1;
+ }
+ okay = create_artifact(o_ptr, TRUE, TRUE);
+ }
+
+ /* Failure */
+ if (!okay)
+ {
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The enchantment failed.");
+ }
+ else
+ o_ptr->found = OBJ_FOUND_SELFMADE;
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
diff --git a/src/readdib.c b/src/readdib.c
new file mode 100644
index 00000000..294c2702
--- /dev/null
+++ b/src/readdib.c
@@ -0,0 +1,342 @@
+/* File: readbits.c */
+
+/*
+ * This package provides a routine to read a DIB file and set up the
+ * device dependent version of the image.
+ *
+ * This file has been modified for use with "Angband 2.8.2"
+ *
+ * COPYRIGHT:
+ *
+ * (C) Copyright Microsoft Corp. 1993. All rights reserved.
+ *
+ * You have a royalty-free right to use, modify, reproduce and
+ * distribute the Sample Files (and/or any modified version) in
+ * any way you find useful, provided that you agree that
+ * Microsoft has no warranty obligations or liability for any
+ * Sample Application Files which are modified.
+ */
+
+#ifdef WINDOWS
+
+#include <windows.h>
+
+#include "readdib.h"
+
+
+/*
+ * Extract the "WIN32" flag from the compiler
+ */
+#if defined(__WIN32__) || defined(__WINNT__) || defined(__NT__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+
+/*
+ * Make sure "huge" is legal XXX XXX XXX
+ */
+#undef huge
+#ifdef WIN32
+# define huge /* oops */
+#endif
+
+
+/*
+ * Number of bytes to be read during each read operation
+ */
+#define MAXREAD 32768
+
+/*
+ * Private routine to read more than 64K at a time
+ *
+ * Reads data in steps of 32k till all the data has been read.
+ *
+ * Returns number of bytes requested, or zero if something went wrong.
+ */
+static DWORD PASCAL lread(int fh, VOID far *pv, DWORD ul)
+{
+ DWORD ulT = ul;
+ BYTE huge *hp = pv;
+
+ while (ul > (DWORD)MAXREAD)
+ {
+ if (_lread(fh, (LPSTR)hp, (WORD)MAXREAD) != MAXREAD)
+ return 0;
+ ul -= MAXREAD;
+ hp += MAXREAD;
+ }
+ if (_lread(fh, (LPSTR)hp, (WORD)ul) != (WORD)ul)
+ return 0;
+ return ulT;
+}
+
+
+/*
+ * Given a BITMAPINFOHEADER, create a palette based on the color table.
+ *
+ * Returns the handle of a palette, or zero if something went wrong.
+ */
+static HPALETTE PASCAL NEAR MakeDIBPalette(LPBITMAPINFOHEADER lpInfo)
+{
+ NPLOGPALETTE npPal;
+ RGBQUAD far *lpRGB;
+ HPALETTE hLogPal;
+ WORD i;
+
+ /*
+ * since biClrUsed field was filled during the loading of the DIB,
+ * we know it contains the number of colors in the color table.
+ */
+ if (lpInfo->biClrUsed)
+ {
+ npPal = (NPLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
+ (WORD)lpInfo->biClrUsed * sizeof(PALETTEENTRY));
+ if (!npPal)
+ return (FALSE);
+
+ npPal->palVersion = 0x300;
+ npPal->palNumEntries = (WORD)lpInfo->biClrUsed;
+
+ /* get pointer to the color table */
+ lpRGB = (RGBQUAD FAR *)((LPSTR)lpInfo + lpInfo->biSize);
+
+ /* copy colors from the color table to the LogPalette structure */
+ for (i = 0; i < lpInfo->biClrUsed; i++, lpRGB++)
+ {
+ npPal->palPalEntry[i].peRed = lpRGB->rgbRed;
+ npPal->palPalEntry[i].peGreen = lpRGB->rgbGreen;
+ npPal->palPalEntry[i].peBlue = lpRGB->rgbBlue;
+ npPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
+ }
+
+ hLogPal = CreatePalette((LPLOGPALETTE)npPal);
+ LocalFree((HANDLE)npPal);
+ return (hLogPal);
+ }
+
+ /*
+ * 24-bit DIB with no color table. return default palette. Another
+ * option would be to create a 256 color "rainbow" palette to provide
+ * some good color choices.
+ */
+ else
+ {
+ return (GetStockObject(DEFAULT_PALETTE));
+ }
+}
+
+
+/*
+ * Given a DIB, create a bitmap and corresponding palette to be used for a
+ * device-dependent representation of the image.
+ *
+ * Returns TRUE on success (phPal and phBitmap are filled with appropriate
+ * handles. Caller is responsible for freeing objects) and FALSE on failure
+ * (unable to create objects, both pointer are invalid).
+ */
+static BOOL NEAR PASCAL MakeBitmapAndPalette(HDC hDC, HANDLE hDIB,
+ HPALETTE * phPal, HBITMAP * phBitmap)
+{
+ LPBITMAPINFOHEADER lpInfo;
+ BOOL result = FALSE;
+ HBITMAP hBitmap;
+ HPALETTE hPalette, hOldPal;
+ LPSTR lpBits;
+
+ lpInfo = (LPBITMAPINFOHEADER) GlobalLock(hDIB);
+ if ((hPalette = MakeDIBPalette(lpInfo)) != 0)
+ {
+ /* Need to realize palette for converting DIB to bitmap. */
+ hOldPal = SelectPalette(hDC, hPalette, TRUE);
+ RealizePalette(hDC);
+
+ lpBits = ((LPSTR)lpInfo + (WORD)lpInfo->biSize +
+ (WORD)lpInfo->biClrUsed * sizeof(RGBQUAD));
+ hBitmap = CreateDIBitmap(hDC, lpInfo, CBM_INIT, lpBits,
+ (LPBITMAPINFO)lpInfo, DIB_RGB_COLORS);
+
+ SelectPalette(hDC, hOldPal, TRUE);
+ RealizePalette(hDC);
+
+ if (!hBitmap)
+ {
+ DeleteObject(hPalette);
+ }
+ else
+ {
+ *phBitmap = hBitmap;
+ *phPal = hPalette;
+ result = TRUE;
+ }
+ }
+ return (result);
+}
+
+
+
+/*
+ * Reads a DIB from a file, obtains a handle to its BITMAPINFO struct, and
+ * loads the DIB. Once the DIB is loaded, the function also creates a bitmap
+ * and palette out of the DIB for a device-dependent form.
+ *
+ * Returns TRUE if the DIB is loaded and the bitmap/palette created, in which
+ * case, the DIBINIT structure pointed to by pInfo is filled with the appropriate
+ * handles, and FALSE if something went wrong.
+ */
+BOOL ReadDIB(HWND hWnd, LPSTR lpFileName, DIBINIT *pInfo)
+{
+ unsigned fh;
+ LPBITMAPINFOHEADER lpbi;
+ OFSTRUCT of;
+ BITMAPFILEHEADER bf;
+ WORD nNumColors;
+ BOOL result = FALSE;
+ char str[128];
+ WORD offBits;
+ HDC hDC;
+ BOOL bCoreHead = FALSE;
+
+ /* Open the file and get a handle to it's BITMAPINFO */
+ fh = OpenFile(lpFileName, &of, OF_READ);
+ if (fh == -1)
+ {
+ wsprintf(str, "Can't open file '%ls'", (LPSTR)lpFileName);
+ MessageBox(NULL, str, "Error", MB_ICONSTOP | MB_OK);
+ return (FALSE);
+ }
+
+ pInfo->hDIB = GlobalAlloc(GHND, (DWORD)(sizeof(BITMAPINFOHEADER) +
+ 256 * sizeof(RGBQUAD)));
+
+ if (!pInfo->hDIB)
+ return (FALSE);
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(pInfo->hDIB);
+
+ /* read the BITMAPFILEHEADER */
+ if (sizeof (bf) != _lread(fh, (LPSTR)&bf, sizeof(bf)))
+ goto ErrExit;
+
+ /* 'BM' */
+ if (bf.bfType != 0x4d42)
+ goto ErrExit;
+
+ if (sizeof(BITMAPCOREHEADER) != _lread(fh, (LPSTR)lpbi, sizeof(BITMAPCOREHEADER)))
+ goto ErrExit;
+
+ if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
+ {
+ lpbi->biSize = sizeof(BITMAPINFOHEADER);
+ lpbi->biBitCount = ((LPBITMAPCOREHEADER)lpbi)->bcBitCount;
+ lpbi->biPlanes = ((LPBITMAPCOREHEADER)lpbi)->bcPlanes;
+ lpbi->biHeight = ((LPBITMAPCOREHEADER)lpbi)->bcHeight;
+ lpbi->biWidth = ((LPBITMAPCOREHEADER)lpbi)->bcWidth;
+ bCoreHead = TRUE;
+ }
+ else
+ {
+ /* get to the start of the header and read INFOHEADER */
+ _llseek(fh, sizeof(BITMAPFILEHEADER), SEEK_SET);
+ if (sizeof(BITMAPINFOHEADER) != _lread(fh, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER)))
+ goto ErrExit;
+ }
+
+ if (!(nNumColors = (WORD)lpbi->biClrUsed))
+ {
+ /* no color table for 24-bit, default size otherwise */
+ if (lpbi->biBitCount != 24)
+ nNumColors = 1 << lpbi->biBitCount;
+ }
+
+ /* fill in some default values if they are zero */
+ if (lpbi->biClrUsed == 0)
+ lpbi->biClrUsed = nNumColors;
+
+ if (lpbi->biSizeImage == 0)
+ {
+ lpbi->biSizeImage = (((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3)
+ * lpbi->biHeight);
+ }
+
+ /* otherwise wouldn't work with 16 color bitmaps -- S.K. */
+ else if ((nNumColors == 16) && (lpbi->biSizeImage > bf.bfSize))
+ {
+ lpbi->biSizeImage /= 2;
+ }
+
+ /* get a proper-sized buffer for header, color table and bits */
+ GlobalUnlock(pInfo->hDIB);
+ pInfo->hDIB = GlobalReAlloc(pInfo->hDIB, lpbi->biSize +
+ nNumColors * sizeof(RGBQUAD) +
+ lpbi->biSizeImage, 0);
+
+ /* can't resize buffer for loading */
+ if (!pInfo->hDIB)
+ goto ErrExit2;
+
+ lpbi = (LPBITMAPINFOHEADER)GlobalLock(pInfo->hDIB);
+
+ /* read the color table */
+ if (!bCoreHead)
+ {
+ _lread(fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD));
+ }
+ else
+ {
+ signed int i;
+ RGBQUAD FAR *pQuad;
+ RGBTRIPLE FAR *pTriple;
+
+ _lread(fh, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBTRIPLE));
+
+ pQuad = (RGBQUAD FAR *)((LPSTR)lpbi + lpbi->biSize);
+ pTriple = (RGBTRIPLE FAR *) pQuad;
+ for (i = nNumColors - 1; i >= 0; i--)
+ {
+ pQuad[i].rgbRed = pTriple[i].rgbtRed;
+ pQuad[i].rgbBlue = pTriple[i].rgbtBlue;
+ pQuad[i].rgbGreen = pTriple[i].rgbtGreen;
+ pQuad[i].rgbReserved = 0;
+ }
+ }
+
+ /* offset to the bits from start of DIB header */
+ offBits = (WORD)lpbi->biSize + nNumColors * sizeof(RGBQUAD);
+
+ if (bf.bfOffBits != 0L)
+ {
+ _llseek(fh, bf.bfOffBits, SEEK_SET);
+ }
+
+ /* Use local version of '_lread()' above */
+ if (lpbi->biSizeImage == lread(fh, (LPSTR)lpbi + offBits, lpbi->biSizeImage))
+ {
+ GlobalUnlock(pInfo->hDIB);
+
+ hDC = GetDC(hWnd);
+ if (!MakeBitmapAndPalette(hDC, pInfo->hDIB, &(pInfo->hPalette),
+ &(pInfo->hBitmap)))
+ {
+ ReleaseDC(hWnd, hDC);
+ goto ErrExit2;
+ }
+ else
+ {
+ ReleaseDC(hWnd, hDC);
+ result = TRUE;
+ }
+ }
+ else
+ {
+ErrExit:
+ GlobalUnlock(pInfo->hDIB);
+ErrExit2:
+ GlobalFree(pInfo->hDIB);
+ }
+
+ _lclose(fh);
+ return (result);
+}
+
+#endif
diff --git a/src/readdib.h b/src/readdib.h
new file mode 100644
index 00000000..c6402b50
--- /dev/null
+++ b/src/readdib.h
@@ -0,0 +1,21 @@
+/* File: readdib.h */
+
+/*
+ * This file has been modified for use with "Angband 2.8.2"
+ *
+ * Copyright 1991 Microsoft Corporation. All rights reserved.
+ */
+
+/*
+ * Information about a bitmap
+ */
+typedef struct {
+ HANDLE hDIB;
+ HANDLE hBitmap;
+ HANDLE hPalette;
+ BYTE CellWidth;
+ BYTE CellHeight;
+} DIBINIT;
+
+/* Read a DIB from a file */
+BOOL ReadDIB(HWND, LPSTR, DIBINIT *);
diff --git a/src/script.c b/src/script.c
new file mode 100644
index 00000000..1d3278d4
--- /dev/null
+++ b/src/script.c
@@ -0,0 +1,641 @@
+/* File: script.c */
+
+/* Purpose: scripting in lua */
+
+/*
+ * Copyright (c) 2001 Dark God
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#include "lua/lua.h"
+#include "lua/lualib.h"
+#include "lauxlib.h"
+#include "tolua.h"
+
+#ifdef RISCOS
+extern char *riscosify_name(const char *path);
+#endif
+
+
+int tolua_monster_open (lua_State *L);
+int tolua_player_open (lua_State *L);
+int tolua_player_c_open (lua_State *L);
+int tolua_util_open (lua_State *L);
+int tolua_z_pack_open (lua_State *L);
+int tolua_object_open (lua_State *L);
+int tolua_spells_open (lua_State *L);
+int tolua_quest_open (lua_State *L);
+int tolua_dungeon_open (lua_State *L);
+
+/*
+ * Lua state
+ */
+lua_State* L = NULL;
+
+/* ToME Lua error message handler */
+static int tome_errormessage(lua_State *L)
+{
+ char buf[200];
+ cptr str = luaL_check_string(L, 1);
+ int i = 0, j = 0;
+
+ while (str[i])
+ {
+ if (str[i] == '#')
+ {
+ buf[j++] = '$';
+ }
+ else if (str[i] != '\n')
+ {
+ buf[j++] = str[i];
+ }
+ else
+ {
+ buf[j] = '\0';
+ cmsg_format(TERM_VIOLET, "LUA: %s", buf);
+ j = 0;
+ }
+ i++;
+ }
+ buf[j] = '\0';
+ cmsg_format(TERM_VIOLET, "LUA: %s", buf);
+ return (0);
+}
+
+static struct luaL_reg tome_iolib[] =
+{
+ { "_ALERT", tome_errormessage },
+};
+
+#define luaL_check_bit(L, n) ((long)luaL_check_number(L, n))
+#define luaL_check_ubit(L, n) ((unsigned long)luaL_check_bit(L, n))
+
+#if 0
+
+/*
+ * Nuked because they can confuse some compilers (lcc for example),
+ * and because of their obscurity -- pelpel
+ */
+
+#define TDYADIC(name, op, t1, t2) \
+static int int_ ## name(lua_State* L) { \
+lua_pushnumber(L, \
+luaL_check_ ## t1 ## bit(L, 1) op luaL_check_ ## t2 ## bit(L, 2)); \
+return 1; \
+}
+
+#define DYADIC(name, op) \
+static int int_ ## name(lua_State* L) { \
+lua_pushnumber(L, \
+luaL_check_bit(L, 1) op luaL_check_bit(L, 2)); \
+return 1; \
+}
+
+#define MONADIC(name, op) \
+static int int_ ## name(lua_State* L) { \
+lua_pushnumber(L, op luaL_check_bit(L, 1)); \
+return 1; \
+}
+
+#define VARIADIC(name, op) \
+static int int_ ## name(lua_State *L) { \
+int n = lua_gettop(L), i; \
+long w = luaL_check_bit(L, 1); \
+for (i = 2; i <= n; i++) \
+w op ## = luaL_check_bit(L, i); \
+lua_pushnumber(L, w); \
+return 1; \
+}
+
+#endif
+
+
+/*
+ * Monadic bit negation operation
+ * MONADIC(not, ~)
+ */
+static int int_not(lua_State* L)
+{
+ lua_pushnumber(L, ~luaL_check_bit(L, 1));
+ return 1;
+}
+
+
+/*
+ * Dyadic integer modulus operation
+ * DYADIC(mod, %)
+ */
+static int int_mod(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_bit(L, 1) % luaL_check_bit(L, 2));
+ return 1;
+}
+
+
+/*
+ * Variable length bitwise AND operation
+ * VARIADIC(and, &)
+ */
+static int int_and(lua_State *L)
+{
+ int n = lua_gettop(L), i;
+ long w = luaL_check_bit(L, 1);
+
+ for (i = 2; i <= n; i++) w &= luaL_check_bit(L, i);
+ lua_pushnumber(L, w);
+
+ return 1;
+}
+
+
+/*
+ * Variable length bitwise OR operation
+ * VARIADIC(or, |)
+ */
+static int int_or(lua_State *L)
+{
+ int n = lua_gettop(L), i;
+ long w = luaL_check_bit(L, 1);
+
+ for (i = 2; i <= n; i++) w |= luaL_check_bit(L, i);
+ lua_pushnumber(L, w);
+
+ return 1;
+}
+
+
+/*
+ * Variable length bitwise XOR operation
+ * VARIADIC(xor, ^)
+ */
+static int int_xor(lua_State *L)
+{
+ int n = lua_gettop(L), i;
+ long w = luaL_check_bit(L, 1);
+
+ for (i = 2; i <= n; i++) w ^= luaL_check_bit(L, i);
+ lua_pushnumber(L, w);
+
+ return 1;
+}
+
+
+/*
+ * Binary left shift operation
+ * TDYADIC(lshift, <<, , u)
+ */
+static int int_lshift(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_bit(L, 1) << luaL_check_ubit(L, 2));
+ return 1;
+}
+
+/*
+ * Binary logical right shift operation
+ * TDYADIC(rshift, >>, u, u)
+ */
+static int int_rshift(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_ubit(L, 1) >> luaL_check_ubit(L, 2));
+ return 1;
+}
+
+/*
+ * Binary arithmetic right shift operation
+ * TDYADIC(arshift, >>, , u)
+ */
+static int int_arshift(lua_State* L)
+{
+ lua_pushnumber(L, luaL_check_bit(L, 1) >> luaL_check_ubit(L, 2));
+ return 1;
+}
+
+
+static const struct luaL_reg bitlib[] =
+{
+ {"bnot", int_not},
+ {"imod", int_mod}, /* "mod" already in Lua math library */
+ {"band", int_and},
+ {"bor", int_or},
+ {"bxor", int_xor},
+ {"lshift", int_lshift},
+ {"rshift", int_rshift},
+ {"arshift", int_arshift},
+};
+
+/*
+ * Some special wrappers
+ */
+static int lua_zsock_read(lua_State* L)
+{
+ if (!tolua_istype(L, 1, tolua_tag(L, "zsock_hooks"), 0) ||
+ !tolua_istype(L, 2, tolua_tag(L, "ip_connection"), 0) ||
+ !tolua_istype(L, 3, LUA_TNUMBER, 0) ||
+ !tolua_istype(L, 4, LUA_TNUMBER, 0) ||
+ !tolua_isnoobj(L, 5)
+ )
+ {
+ tolua_error(L, "#ferror in function 'read'.");
+ return 0;
+ }
+ else
+ {
+ zsock_hooks* self = (zsock_hooks*) tolua_getusertype(L, 1, 0);
+ ip_connection* conn = ((ip_connection*) tolua_getusertype(L, 2, 0));
+ int len = ((int) tolua_getnumber(L, 3, 0));
+ int start_len = len;
+ bool raw = ((bool) tolua_getnumber(L, 4, 0));
+ char *str;
+
+ if (!self) tolua_error(L, "invalid 'self' in function 'read'");
+ {
+ bool toluaI_ret;
+
+ C_MAKE(str, start_len + 1, char);
+
+ toluaI_ret = (bool)self->read(conn, str, &len, raw);
+ tolua_pushnumber(L, (long)toluaI_ret);
+ tolua_pushstring(L, str);
+ tolua_pushnumber(L, (long)len);
+
+ C_FREE(str, start_len + 1, char);
+
+ return 3;
+ }
+ return 0;
+ }
+}
+
+/*
+ * Initialize lua scripting
+ */
+static bool init_lua_done = FALSE;
+void init_lua()
+{
+ /* Hack -- Do not initialize more than once */
+ if (init_lua_done) return;
+ init_lua_done = TRUE;
+
+ /* Start the interpreter with default stack size */
+ L = lua_open(0);
+
+ /* Register the Lua base libraries */
+ lua_baselibopen(L);
+ lua_strlibopen(L);
+ lua_iolibopen(L);
+ lua_dblibopen(L);
+
+ /* Register tome lua debug library */
+ luaL_openl(L, tome_iolib);
+
+ /* Register the bitlib */
+ luaL_openl(L, bitlib);
+
+ /* Register the ToME main APIs */
+ tolua_player_open(L);
+ tolua_player_c_open(L);
+ tolua_util_open(L);
+ tolua_z_pack_open(L);
+ tolua_object_open(L);
+ tolua_monster_open(L);
+ tolua_spells_open(L);
+ tolua_quest_open(L);
+ tolua_dungeon_open(L);
+
+ /* Register some special wrappers */
+ tolua_function(L, "zsock_hooks", "read", lua_zsock_read);
+}
+
+void init_lua_init()
+{
+ int i, max;
+
+ /* Load the first lua file */
+ tome_dofile_anywhere(ANGBAND_DIR_CORE, "init.lua", TRUE);
+
+ /* Finish up schools */
+ max = exec_lua("return __schools_num");
+ init_schools(max);
+ for (i = 0; i < max; i++)
+ {
+ exec_lua(format("finish_school(%d)", i));
+ }
+
+ /* Finish up the spells */
+ max = exec_lua("return __tmp_spells_num");
+ init_spells(max);
+ for (i = 0; i < max; i++)
+ {
+ exec_lua(format("finish_spell(%d)", i));
+ }
+
+ /* Finish up the corruptions */
+ max = exec_lua("return __corruptions_max");
+ init_corruptions(max);
+}
+
+bool tome_dofile(char *file)
+{
+ char buf[1024];
+ int oldtop = lua_gettop(L);
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_SCPT, file);
+
+ if (!file_exist(buf))
+ {
+ /* No lua source(.lua), maybe a compiled one(.luo) ? */
+ if (suffix(buf, ".lua"))
+ {
+ int len = strlen(buf);
+ buf[len - 1] = 'o';
+ if (!file_exist(buf))
+ {
+ cmsg_format(TERM_VIOLET,
+ "tome_dofile(): file %s(%s) doesn't exist.", file, buf);
+ return (FALSE);
+ }
+ }
+ }
+
+#ifdef RISCOS
+ {
+ char *realname = riscosify_name(buf);
+ lua_dofile(L, realname);
+ }
+#else /* RISCOS */
+ lua_dofile(L, buf);
+#endif /* RISCOS */
+
+ lua_settop(L, oldtop);
+
+ return (TRUE);
+}
+
+bool tome_dofile_anywhere(cptr dir, char *file, bool test_exist)
+{
+ char buf[1024];
+ int oldtop = lua_gettop(L);
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), dir, file);
+
+ if (!file_exist(buf))
+ {
+ /* No lua source(.lua), maybe a compiled one(.luo) ? */
+ if (suffix(buf, ".lua"))
+ {
+ int len = strlen(buf);
+ buf[len - 1] = 'o';
+ if (!file_exist(buf))
+ {
+ if (test_exist)
+ cmsg_format(TERM_VIOLET,
+ "tome_dofile_anywhere(): file %s(%s) doesn't exist in %s.", dir, file, buf);
+ return (FALSE);
+ }
+ }
+ }
+
+#ifdef RISCOS
+ {
+ char *realname = riscosify_name(buf);
+ lua_dofile(L, realname);
+ }
+#else /* RISCOS */
+ lua_dofile(L, buf);
+#endif /* RISCOS */
+
+ lua_settop(L, oldtop);
+
+ return (TRUE);
+}
+
+int exec_lua(char *file)
+{
+ int oldtop = lua_gettop(L);
+ int res;
+
+ if (!lua_dostring(L, file))
+ {
+ int size = lua_gettop(L) - oldtop;
+ res = tolua_getnumber(L, -size, 0);
+ }
+ else
+ res = 0;
+
+ lua_settop(L, oldtop);
+ return (res);
+}
+
+cptr string_exec_lua(char *file)
+{
+ int oldtop = lua_gettop(L);
+ cptr res;
+
+ if (!lua_dostring(L, file))
+ {
+ int size = lua_gettop(L) - oldtop;
+ res = tolua_getstring(L, -size, "");
+ }
+ else
+ res = "";
+ lua_settop(L, oldtop);
+ return (res);
+}
+
+void dump_lua_stack(int min, int max)
+{
+ int i;
+
+ cmsg_print(TERM_YELLOW, "lua_stack:");
+ for (i = min; i <= max; i++)
+ {
+ if (lua_isnumber(L, i)) cmsg_format(TERM_YELLOW, "%d [n] = %d", i, tolua_getnumber(L, i, 0));
+ else if (lua_isstring(L, i)) cmsg_format(TERM_YELLOW, "%d [s] = '%s'", i, tolua_getstring(L, i, 0));
+ }
+ cmsg_print(TERM_YELLOW, "END lua_stack");
+}
+
+bool call_lua(cptr function, cptr args, cptr ret, ...)
+{
+ int i = 0, nb = 0, nbr = 0;
+ int oldtop = lua_gettop(L), size;
+ va_list ap;
+
+ va_start(ap, ret);
+
+ /* Push the function */
+ lua_getglobal(L, function);
+
+ /* Push and count the arguments */
+ while (args[i])
+ {
+ switch (args[i++])
+ {
+ case 'd':
+ case 'l':
+ tolua_pushnumber(L, va_arg(ap, s32b));
+ nb++;
+ break;
+ case 's':
+ tolua_pushstring(L, va_arg(ap, char*));
+ nb++;
+ break;
+ case 'O':
+ tolua_pushusertype(L, (void*)va_arg(ap, object_type*), tolua_tag(L, "object_type"));
+ nb++;
+ break;
+ case 'M':
+ tolua_pushusertype(L, (void*)va_arg(ap, monster_type*), tolua_tag(L, "monster_type"));
+ nb++;
+ break;
+ case 'n':
+ lua_pushnil(L);
+ nb++;
+ break;
+ case '(':
+ case ')':
+ case ',':
+ break;
+ }
+ }
+
+ /* Count returns */
+ nbr = strlen(ret);
+
+ /* Call the function */
+ if (lua_call(L, nb, nbr))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling '%s' from call_lua. Things should start breaking up from now on!", function);
+ return FALSE;
+ }
+
+ /* Number of returned values, SHOULD be the same as nbr, but I'm paranoid */
+ size = lua_gettop(L) - oldtop;
+
+ /* Get the returns */
+ for (i = 0; ret[i]; i++)
+ {
+ switch (ret[i])
+ {
+ case 'd':
+ case 'l':
+ {
+ s32b *tmp = va_arg(ap, s32b*);
+
+ if (lua_isnumber(L, ( -size) + i)) *tmp = tolua_getnumber(L, ( -size) + i, 0);
+ else *tmp = 0;
+ break;
+ }
+
+ case 's':
+ {
+ cptr *tmp = va_arg(ap, cptr*);
+
+ if (lua_isstring(L, ( -size) + i)) *tmp = tolua_getstring(L, ( -size) + i, "");
+ else *tmp = NULL;
+ break;
+ }
+
+ case 'O':
+ {
+ object_type **tmp = va_arg(ap, object_type**);
+
+ if (tolua_istype(L, ( -size) + i, tolua_tag(L, "object_type"), 0))
+ *tmp = (object_type*)tolua_getuserdata(L, ( -size) + i, NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ case 'M':
+ {
+ monster_type **tmp = va_arg(ap, monster_type**);
+
+ if (tolua_istype(L, ( -size) + i, tolua_tag(L, "monster_type"), 0))
+ *tmp = (monster_type*)tolua_getuserdata(L, ( -size) + i, NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ default:
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling '%s' from call_lua: Unknown return type '%c'", function, ret[i]);
+ return FALSE;
+ }
+ }
+
+ lua_settop(L, oldtop);
+
+ va_end(ap);
+
+ return TRUE;
+}
+
+bool get_lua_var(cptr name, char type, void *arg)
+{
+ int oldtop = lua_gettop(L), size;
+
+ /* Push the function */
+ lua_getglobal(L, name);
+
+ size = lua_gettop(L) - oldtop;
+
+ switch (type)
+ {
+ case 'd':
+ case 'l':
+ {
+ s32b *tmp = (s32b*)arg;
+
+ if (lua_isnumber(L, ( -size))) *tmp = tolua_getnumber(L, ( -size), 0);
+ else *tmp = 0;
+ break;
+ }
+
+ case 's':
+ {
+ cptr *tmp = (cptr*)arg;
+
+ if (lua_isstring(L, ( -size))) *tmp = tolua_getstring(L, ( -size), "");
+ else *tmp = NULL;
+ break;
+ }
+
+ case 'O':
+ {
+ object_type **tmp = (object_type**)arg;
+
+ if (tolua_istype(L, ( -size), tolua_tag(L, "object_type"), 0))
+ *tmp = (object_type*)tolua_getuserdata(L, ( -size), NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ case 'M':
+ {
+ monster_type **tmp = (monster_type**)arg;
+
+ if (tolua_istype(L, ( -size), tolua_tag(L, "monster_type"), 0))
+ *tmp = (monster_type*)tolua_getuserdata(L, ( -size), NULL);
+ else
+ *tmp = NULL;
+ break;
+ }
+
+ default:
+ cmsg_format(TERM_VIOLET, "ERROR in get_lua_var while calling '%s': Unknown return type '%c'", name, type);
+ return FALSE;
+ }
+
+ lua_settop(L, oldtop);
+
+ return TRUE;
+}
diff --git a/src/skills.c b/src/skills.c
new file mode 100644
index 00000000..07166072
--- /dev/null
+++ b/src/skills.c
@@ -0,0 +1,1795 @@
+/* File: skills.c */
+
+/* Purpose: player skills */
+
+/*
+ * Copyright (c) 2001 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Advance the skill point of the skill specified by i and
+ * modify related skills
+ */
+void increase_skill(int i, s16b *invest)
+{
+ s32b max_skill_overage;
+
+ /* No skill points to be allocated */
+ if (!p_ptr->skill_points) return;
+
+ /* The skill cannot be increased */
+ if (!s_info[i].mod) return;
+
+ /* The skill is already maxed */
+ if (s_info[i].value >= SKILL_MAX) return;
+
+ /* Cannot allocate more than player level + max_skill_overage levels */
+ call_lua("get_module_info", "(s)", "d", "max_skill_overage", &max_skill_overage);
+ if (((s_info[i].value + s_info[i].mod) / SKILL_STEP) >= (p_ptr->lev + max_skill_overage + 1))
+ {
+ int hgt, wid;
+
+ Term_get_size(&wid, &hgt);
+ msg_box(format("Cannot raise a skill value above %i + player level.", max_skill_overage), (int)(hgt / 2), (int)(wid / 2));
+ return;
+ }
+
+ /* Spend an unallocated skill point */
+ p_ptr->skill_points--;
+
+ /* Increase the skill */
+ s_info[i].value += s_info[i].mod;
+ invest[i]++;
+}
+
+
+/*
+ * Descrease the skill point of the skill specified by i and
+ * modify related skills
+ */
+void decrease_skill(int i, s16b *invest)
+{
+ /* Cannot decrease more */
+ if (!invest[i]) return;
+
+ /* The skill cannot be decreased */
+ if (!s_info[i].mod) return;
+
+ /* The skill already has minimal value */
+ if (!s_info[i].value) return;
+
+ /* Free a skill point */
+ p_ptr->skill_points++;
+
+ /* Decrease the skill */
+ s_info[i].value -= s_info[i].mod;
+ invest[i]--;
+}
+
+
+/*
+ * Given the name of a skill, returns skill index or -1 if no
+ * such skill is found
+ */
+s16b find_skill(cptr name)
+{
+ u16b i;
+
+ /* Scan skill list */
+ for (i = 1; i < max_s_idx; i++)
+ {
+ /* The name matches */
+ if (streq(s_info[i].name + s_name, name)) return (i);
+ }
+
+ /* No match found */
+ return ( -1);
+}
+s16b find_skill_i(cptr name)
+{
+ u16b i;
+
+ /* Scan skill list */
+ for (i = 1; i < max_s_idx; i++)
+ {
+ /* The name matches */
+ if (0 == stricmp(s_info[i].name + s_name, name)) return (i);
+ }
+
+ /* No match found */
+ return ( -1);
+}
+
+
+/*
+ *
+ */
+s16b get_skill(int skill)
+{
+ return (s_info[skill].value / SKILL_STEP);
+}
+
+
+/*
+ * Return "scale" (a misnomer -- this is max value) * (current skill value)
+ * / (max skill value)
+ */
+s16b get_skill_scale(int skill, u32b scale)
+{
+ s32b temp;
+
+ /*
+ * SKILL_STEP shouldn't matter here because the second parameter is
+ * relatively small (the largest one being somewhere around 200),
+ * AND because we could have used much simpler 0--50 if the ability
+ * progression were only possible at step boundaries.
+ *
+ * Because I'm not at all certain about my interpretation of the mysterious
+ * formula given above, I verified this works the same by using a tiny
+ * scheme program... -- pelpel
+ */
+ temp = scale * s_info[skill].value;
+
+ return (temp / SKILL_MAX);
+}
+
+
+/*
+ *
+ */
+int get_idx(int i)
+{
+ int j;
+
+ for (j = 1; j < max_s_idx; j++)
+ {
+ if (s_info[j].order == i)
+ return (j);
+ }
+ return (0);
+}
+
+static bool is_known(int s_idx)
+{
+ int i;
+
+ if (wizard) return TRUE;
+ if (s_info[s_idx].value || s_info[s_idx].mod) return TRUE;
+
+ for (i = 0; i < max_s_idx; i++)
+ {
+ /* It is our child, if we don't know it we continue to search, if we know it it is enough*/
+ if (s_info[i].father == s_idx)
+ {
+ if (is_known(i))
+ return TRUE;
+ }
+ }
+
+ /* Ok know none */
+ return FALSE;
+}
+
+/*
+ *
+ */
+void init_table_aux(int table[MAX_SKILLS][2], int *idx, int father, int lev,
+ bool full)
+{
+ int j, i;
+
+ for (j = 1; j < max_s_idx; j++)
+ {
+ i = get_idx(j);
+ if (s_info[i].father != father) continue;
+ if (s_info[i].hidden) continue;
+ if (!is_known(i)) continue;
+
+ table[*idx][0] = i;
+ table[*idx][1] = lev;
+ (*idx)++;
+ if (s_info[i].dev || full) init_table_aux(table, idx, i, lev + 1, full);
+ }
+}
+
+
+void init_table(int table[MAX_SKILLS][2], int *max, bool full)
+{
+ *max = 0;
+ init_table_aux(table, max, -1, 0, full);
+}
+
+
+bool has_child(int sel)
+{
+ int i;
+
+ for (i = 1; i < max_s_idx; i++)
+ {
+ if ((s_info[i].father == sel) && (is_known(i)))
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+/*
+ * Dump the skill tree
+ */
+void dump_skills(FILE *fff)
+{
+ int i, j, max = 0;
+ int table[MAX_SKILLS][2];
+ char buf[80];
+
+ init_table(table, &max, TRUE);
+
+ fprintf(fff, "\nSkills (points left: %d)", p_ptr->skill_points);
+
+ for (j = 0; j < max; j++)
+ {
+ int z;
+
+ i = table[j][0];
+
+ if ((s_info[i].value == 0) && (i != SKILL_MISC))
+ {
+ if (s_info[i].mod == 0) continue;
+ }
+
+ sprintf(buf, "\n");
+
+ for (z = 0; z < table[j][1]; z++) strcat(buf, " ");
+
+ if (!has_child(i))
+ {
+ strcat(buf, format(" . %s", s_info[i].name + s_name));
+ }
+ else
+ {
+ strcat(buf, format(" - %s", s_info[i].name + s_name));
+ }
+
+ fprintf(fff, "%-49s%s%02ld.%03ld [%01ld.%03ld]",
+ buf, s_info[i].value < 0 ? "-" : " ",
+ ABS(s_info[i].value) / SKILL_STEP,
+ ABS(s_info[i].value) % SKILL_STEP,
+ s_info[i].mod / 1000, s_info[i].mod % 1000);
+ }
+
+ fprintf(fff, "\n");
+}
+
+
+/*
+ * Draw the skill tree
+ */
+void print_skills(int table[MAX_SKILLS][2], int max, int sel, int start)
+{
+ int i, j;
+ int wid, hgt;
+ cptr keys;
+
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, format("%s Skills Screen", game_module), 0, 28);
+ keys = format("#BEnter#W to develop a branch, #Bup#W/#Bdown#W to move, #Bright#W/#Bleft#W to modify, #B?#W for help");
+ display_message(0, 1, strlen(keys), TERM_WHITE, keys);
+ c_prt((p_ptr->skill_points) ? TERM_L_BLUE : TERM_L_RED,
+ format("Skill points left: %d", p_ptr->skill_points), 2, 0);
+ print_desc_aux(s_info[table[sel][0]].desc + s_text, 3, 0);
+
+ for (j = start; j < start + (hgt - 7); j++)
+ {
+ byte color = TERM_WHITE;
+ char deb = ' ', end = ' ';
+
+ if (j >= max) break;
+
+ i = table[j][0];
+
+ if ((s_info[i].value == 0) && (i != SKILL_MISC))
+ {
+ if (s_info[i].mod == 0) color = TERM_L_DARK;
+ else color = TERM_ORANGE;
+ }
+ else if (s_info[i].value == SKILL_MAX) color = TERM_L_BLUE;
+ if (s_info[i].hidden) color = TERM_L_RED;
+ if (j == sel)
+ {
+ color = TERM_L_GREEN;
+ deb = '[';
+ end = ']';
+ }
+ if (!has_child(i))
+ {
+ c_prt(color, format("%c.%c%s", deb, end, s_info[i].name + s_name),
+ j + 7 - start, table[j][1] * 4);
+ }
+ else if (s_info[i].dev)
+ {
+ c_prt(color, format("%c-%c%s", deb, end, s_info[i].name + s_name),
+ j + 7 - start, table[j][1] * 4);
+ }
+ else
+ {
+ c_prt(color, format("%c+%c%s", deb, end, s_info[i].name + s_name),
+ j + 7 - start, table[j][1] * 4);
+ }
+ c_prt(color,
+ format("%s%02ld.%03ld [%01d.%03d]",
+ s_info[i].value < 0 ? "-" : " ",
+ ABS(s_info[i].value) / SKILL_STEP,
+ ABS(s_info[i].value) % SKILL_STEP,
+ ABS(s_info[i].mod) / 1000,
+ ABS(s_info[i].mod) % 1000),
+ j + 7 - start, 60);
+ }
+}
+
+/*
+ * Checks various stuff to do when skills change, like new spells, ...
+ */
+void recalc_skills(bool init)
+{
+ static int thaum_level = 0;
+
+ /* TODO: This should be a hook in ToME's lua */
+ if (init)
+ {
+ thaum_level = get_skill_scale(SKILL_THAUMATURGY, 100);
+ }
+ else
+ {
+ int thaum_gain = 0;
+
+ /* Gain thaum spells */
+ while (thaum_level < get_skill_scale(SKILL_THAUMATURGY, 100))
+ {
+ if (spell_num == MAX_SPELLS) break;
+ thaum_level++;
+ generate_spell((thaum_level + 1) / 2);
+ thaum_gain++;
+ }
+ if (thaum_gain)
+ {
+ if (thaum_gain == 1)
+ msg_print("You have gained one new thaumaturgy spell.");
+ else
+ msg_format("You have gained %d new thaumaturgy spells.", thaum_gain);
+ }
+
+ process_hooks(HOOK_RECALC_SKILLS, "()");
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Redraw various info */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+ }
+}
+
+/*
+ * Recalc the skill value
+ */
+void recalc_skills_theory(s16b *invest, s32b *base_val, s32b *base_mod, s32b *bonus)
+{
+ int i, j;
+
+ /* First we assign the normal points */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ /* Calc the base */
+ s_info[i].value = base_val[i] + (base_mod[i] * invest[i]) + bonus[i];
+
+ /* It cannot exceed SKILL_MAX */
+ if (s_info[i].value > SKILL_MAX) s_info[i].value = SKILL_MAX;
+ }
+
+ /* Then we modify related skills */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ for (j = 1; j < max_s_idx; j++)
+ {
+ /* Ignore self */
+ if (j == i) continue;
+
+ /* Exclusive skills */
+ if ((s_info[i].action[j] == SKILL_EXCLUSIVE) && invest[i])
+ {
+ /* Turn it off */
+ p_ptr->skill_points += invest[j];
+ invest[j] = 0;
+ s_info[j].value = 0;
+ }
+
+ /* Non-exclusive skills */
+ else if (s_info[i].action[j])
+ {
+ /* Increase / decrease with a % */
+ s32b val = s_info[j].value + (invest[i] * s_info[j].mod * s_info[i].action[j] / 100);
+
+ /* It cannot exceed SKILL_MAX */
+ if (val > SKILL_MAX) val = SKILL_MAX;
+
+ /* Save the modified value */
+ s_info[j].value = val;
+ }
+ }
+ }
+}
+
+/*
+ * Interreact with skills
+ */
+void do_cmd_skill()
+{
+ int sel = 0, start = 0, max;
+ char c;
+ int table[MAX_SKILLS][2];
+ int i;
+ int wid, hgt;
+ s16b skill_points_save;
+ s32b *skill_values_save;
+ s32b *skill_mods_save;
+ s16b *skill_rates_save;
+ s16b *skill_invest;
+ s32b *skill_bonus;
+
+ recalc_skills(TRUE);
+
+ /* Save the screen */
+ screen_save();
+
+ /* Allocate arrays to save skill values */
+ C_MAKE(skill_values_save, MAX_SKILLS, s32b);
+ C_MAKE(skill_mods_save, MAX_SKILLS, s32b);
+ C_MAKE(skill_rates_save, MAX_SKILLS, s16b);
+ C_MAKE(skill_invest, MAX_SKILLS, s16b);
+ C_MAKE(skill_bonus, MAX_SKILLS, s32b);
+
+ /* Save skill points */
+ skill_points_save = p_ptr->skill_points;
+
+ /* Save skill values */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ skill_type *s_ptr = &s_info[i];
+
+ skill_values_save[i] = s_ptr->value;
+ skill_mods_save[i] = s_ptr->mod;
+ skill_rates_save[i] = s_ptr->rate;
+ skill_invest[i] = 0;
+ }
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Initialise the skill list */
+ init_table(table, &max, FALSE);
+
+ while (TRUE)
+ {
+ Term_get_size(&wid, &hgt);
+
+ /* Display list of skills */
+ recalc_skills_theory(skill_invest, skill_values_save, skill_mods_save, skill_bonus);
+ print_skills(table, max, sel, start);
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the skill screen */
+ if (c == ESCAPE) break;
+
+ /* Expand / collapse list of skills */
+ else if (c == '\r')
+ {
+ if (s_info[table[sel][0]].dev) s_info[table[sel][0]].dev = FALSE;
+ else s_info[table[sel][0]].dev = TRUE;
+ init_table(table, &max, FALSE);
+ }
+
+ /* Next page */
+ else if (c == 'n')
+ {
+ sel += (hgt - 7);
+ if (sel >= max) sel = max - 1;
+ }
+
+ /* Previous page */
+ else if (c == 'p')
+ {
+ sel -= (hgt - 7);
+ if (sel < 0) sel = 0;
+ }
+
+ /* Select / increase a skill */
+ else
+ {
+ int dir;
+
+ /* Allow use of numpad / arrow keys / roguelike keys */
+ dir = get_keymap_dir(c);
+
+ /* Move cursor down */
+ if (dir == 2) sel++;
+
+ /* Move cursor up */
+ if (dir == 8) sel--;
+
+ /* Miscellaneous skills cannot be increased/decreased as a group */
+ if (table[sel][0] == SKILL_MISC) continue;
+
+ /* Increase the current skill */
+ if (dir == 6) increase_skill(table[sel][0], skill_invest);
+
+ /* Decrease the current skill */
+ if (dir == 4) decrease_skill(table[sel][0], skill_invest);
+
+ /* XXX XXX XXX Wizard mode commands outside of wizard2.c */
+
+ /* Increase the skill */
+ if (wizard && (c == '+')) skill_bonus[table[sel][0]] += SKILL_STEP;
+
+ /* Decrease the skill */
+ if (wizard && (c == '-')) skill_bonus[table[sel][0]] -= SKILL_STEP;
+
+ /* Contextual help */
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'skill', '%s')", s_info[table[sel][0]].name + s_name));
+ ;
+
+ /* Handle boundaries and scrolling */
+ if (sel < 0) sel = max - 1;
+ if (sel >= max) sel = 0;
+ if (sel < start) start = sel;
+ if (sel >= start + (hgt - 7)) start = sel - (hgt - 7) + 1;
+ }
+ }
+
+
+ /* Some skill points are spent */
+ if (p_ptr->skill_points != skill_points_save)
+ {
+ /* Flush input as we ask an important and irreversible question */
+ flush();
+
+ /* Ask we can commit the change */
+ if (msg_box("Save and use these skill values? (y/n)", (int)(hgt / 2), (int)(wid / 2)) != 'y')
+ {
+ /* User declines -- restore the skill values before exiting */
+
+ /* Restore skill points */
+ p_ptr->skill_points = skill_points_save;
+
+ /* Restore skill values */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ skill_type *s_ptr = &s_info[i];
+
+ s_ptr->value = skill_values_save[i];
+ s_ptr->mod = skill_mods_save[i];
+ s_ptr->rate = skill_rates_save[i];
+ }
+ }
+ }
+
+
+ /* Free arrays to save skill values */
+ C_FREE(skill_values_save, MAX_SKILLS, s32b);
+ C_FREE(skill_mods_save, MAX_SKILLS, s32b);
+ C_FREE(skill_rates_save, MAX_SKILLS, s16b);
+ C_FREE(skill_invest, MAX_SKILLS, s16b);
+ C_FREE(skill_bonus, MAX_SKILLS, s32b);
+
+ /* Load the screen */
+ screen_load();
+
+ recalc_skills(FALSE);
+}
+
+
+
+/*
+ * List of melee skills
+ */
+s16b melee_skills[MAX_MELEE] =
+{
+ SKILL_MASTERY,
+ SKILL_HAND,
+ SKILL_BEAR,
+};
+char *melee_names[MAX_MELEE] =
+{
+ "Weapon combat",
+ "Barehanded combat",
+ "Bearform combat",
+};
+static bool melee_bool[MAX_MELEE];
+static int melee_num[MAX_MELEE];
+
+s16b get_melee_skill()
+{
+ int i;
+
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if (p_ptr->melee_style == melee_skills[i])
+ return (i);
+ }
+ return (0);
+}
+
+s16b get_melee_skills()
+{
+ int i, j = 0;
+
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if ((s_info[melee_skills[i]].value > 0) && (!s_info[melee_skills[i]].hidden))
+ {
+ melee_bool[i] = TRUE;
+ j++;
+ }
+ else
+ melee_bool[i] = FALSE;
+ }
+
+ return (j);
+}
+
+static void choose_melee()
+{
+ int i, j, z = 0;
+ int force_drop = FALSE, style_unchanged = FALSE;
+
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ j = get_melee_skills();
+ prt("Choose a melee style:", 0, 0);
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if (melee_bool[i])
+ {
+ prt(format("%c) %s", I2A(z), melee_names[i]), z + 1, 0);
+ melee_num[z] = i;
+ z++;
+ }
+ }
+
+ while (TRUE)
+ {
+ char c = inkey();
+
+ if (c == ESCAPE) break;
+ if (A2I(c) < 0) continue;
+ if (A2I(c) >= j) continue;
+
+ for (i = 0, z = 0; z < A2I(c); i++)
+ if (melee_bool[i]) z++;
+
+ if (p_ptr->melee_style == melee_skills[melee_num[z]])
+ {
+ style_unchanged = TRUE;
+ break;
+ }
+
+ for (i = INVEN_WIELD; p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD; i++)
+ {
+ if (p_ptr->inventory[i].k_idx)
+ {
+ if (cursed_p(&p_ptr->inventory[i]))
+ {
+ char name[80];
+ object_desc(name, &p_ptr->inventory[i], 0, 0);
+ msg_format("Hmmm, your %s seems to be cursed.", name);
+ break;
+ }
+ else if (INVEN_PACK == inven_takeoff(i, 255, force_drop))
+ {
+ force_drop = TRUE;
+ }
+ }
+ }
+ p_ptr->melee_style = melee_skills[melee_num[z]];
+ energy_use = 100;
+ break;
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_MH);
+
+ Term_load();
+ character_icky = FALSE;
+
+ if (style_unchanged)
+ {
+ msg_format("You are already using %s.", melee_names[melee_num[z]]);
+ }
+}
+
+void select_default_melee()
+{
+ int i;
+
+ get_melee_skills();
+ p_ptr->melee_style = SKILL_MASTERY;
+ for (i = 0; i < MAX_MELEE; i++)
+ {
+ if (melee_bool[i])
+ {
+ p_ptr->melee_style = melee_skills[i];
+ break;
+ }
+ }
+}
+
+/*
+ * Print a batch of skills.
+ */
+static void print_skill_batch(int *p, cptr *p_desc, int start, int max, bool mode)
+{
+ char buff[80];
+ int i = start, j = 0;
+
+ if (mode) prt(format(" %-31s", "Name"), 1, 20);
+
+ for (i = start; i < (start + 20); i++)
+ {
+ if (i >= max) break;
+
+ if (p[i] > 0)
+ sprintf(buff, " %c - %d) %-30s", I2A(j), p[i], p_desc[i]);
+ else
+ sprintf(buff, " %c - %d) %-30s", I2A(j), p[i], "Change melee style");
+
+ if (mode) prt(buff, 2 + j, 20);
+ j++;
+ }
+ if (mode) prt("", 2 + j, 20);
+ prt(format("Select a skill (a-%c), @ to select by name, +/- to scroll:", I2A(j - 1)), 0, 0);
+}
+
+int do_cmd_activate_skill_aux()
+{
+ char which;
+ int max = 0, i, start = 0;
+ int ret;
+ bool mode = FALSE;
+ int *p;
+ cptr *p_desc;
+
+ C_MAKE(p, max_s_idx + max_ab_idx, int);
+ C_MAKE(p_desc, max_s_idx + max_ab_idx, cptr);
+
+ /* Count the max */
+
+ /* More than 1 melee skill ? */
+ if (get_melee_skills() > 1)
+ {
+ p_desc[max] = "Change melee mode";
+ p[max++] = 0;
+ }
+
+ for (i = 1; i < max_s_idx; i++)
+ {
+ if (s_info[i].action_mkey && s_info[i].value && ((!s_info[i].hidden) || (i == SKILL_LEARN)))
+ {
+ int j;
+ bool next = FALSE;
+
+ /* Already got it ? */
+ for (j = 0; j < max; j++)
+ {
+ if (s_info[i].action_mkey == p[j])
+ {
+ next = TRUE;
+ break;
+ }
+ }
+ if (next) continue;
+
+ p_desc[max] = s_text + s_info[i].action_desc;
+ p[max++] = s_info[i].action_mkey;
+ }
+ }
+
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].action_mkey && ab_info[i].acquired)
+ {
+ int j;
+ bool next = FALSE;
+
+ /* Already got it ? */
+ for (j = 0; j < max; j++)
+ {
+ if (ab_info[i].action_mkey == p[j])
+ {
+ next = TRUE;
+ break;
+ }
+ }
+ if (next) continue;
+
+ p_desc[max] = ab_text + ab_info[i].action_desc;
+ p[max++] = ab_info[i].action_mkey;
+ }
+ }
+
+ if (!max)
+ {
+ msg_print("You don't have any activable skills or abilities.");
+ return -1;
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_skill_batch(p, p_desc, start, max, mode);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = -1;
+ break;
+ }
+ else if (which == '*' || which == '?' || which == ' ')
+ {
+ mode = (mode) ? FALSE : TRUE;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '+')
+ {
+ start += 20;
+ if (start >= max) start -= 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '-')
+ {
+ start -= 20;
+ if (start < 0) start += 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+ else if (which == '@')
+ {
+ char buf[80];
+
+ strcpy(buf, "Cast a spell");
+ if (!get_string("Skill action? ", buf, 79))
+ return FALSE;
+
+ /* Find the skill it is related to */
+ for (i = 0; i < max; i++)
+ {
+ if (!strcmp(buf, p_desc[i]))
+ break;
+ }
+ if ((i < max))
+ {
+ ret = p[i];
+ break;
+ }
+
+ }
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= max)
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+
+ ret = p[start + A2I(which)];
+ break;
+ }
+ }
+ Term_load();
+ character_icky = FALSE;
+
+ C_FREE(p, max_s_idx + max_ab_idx, int);
+ C_FREE(p_desc, max_s_idx + max_ab_idx, cptr);
+
+ return ret;
+}
+
+/* Ask & execute a skill */
+void do_cmd_activate_skill()
+{
+ int x_idx;
+ bool push = TRUE;
+
+ /* Get the skill, if available */
+ if (repeat_pull(&x_idx))
+ {
+ push = FALSE;
+ }
+ else if (!command_arg) x_idx = do_cmd_activate_skill_aux();
+ else
+ {
+ int i, j;
+
+ x_idx = command_arg;
+
+ /* Check validity */
+ for (i = 1; i < max_s_idx; i++)
+ {
+ if (s_info[i].value && (s_info[i].action_mkey == x_idx))
+ break;
+ }
+ for (j = 0; j < max_ab_idx; j++)
+ {
+ if (ab_info[j].acquired && (ab_info[j].action_mkey == x_idx))
+ break;
+ }
+
+ if ((j == max_ab_idx) && (i == max_s_idx))
+ {
+ msg_print("Uh?");
+ return;
+ }
+ }
+
+ if (x_idx == -1) return;
+
+ if (push) repeat_push(x_idx);
+
+ if (!x_idx)
+ {
+ choose_melee();
+ return;
+ }
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ switch (x_idx)
+ {
+ case MKEY_ANTIMAGIC:
+ do_cmd_unbeliever();
+ break;
+ case MKEY_MINDCRAFT:
+ do_cmd_mindcraft();
+ break;
+ case MKEY_ALCHEMY:
+ do_cmd_alchemist();
+ break;
+ case MKEY_MIMIC:
+ do_cmd_mimic();
+ break;
+ case MKEY_POWER_MAGE:
+ do_cmd_powermage();
+ break;
+ case MKEY_RUNE:
+ do_cmd_runecrafter();
+ break;
+ case MKEY_FORGING:
+ do_cmd_archer();
+ break;
+ case MKEY_INCARNATION:
+ do_cmd_possessor();
+ break;
+ case MKEY_TELEKINESIS:
+ do_cmd_portable_hole();
+ break;
+ case MKEY_BLADE:
+ do_cmd_blade();
+ break;
+ case MKEY_SUMMON:
+ do_cmd_summoner();
+ break;
+ case MKEY_NECRO:
+ do_cmd_necromancer();
+ break;
+ case MKEY_SYMBIOTIC:
+ do_cmd_symbiotic();
+ break;
+ case MKEY_TRAP:
+ do_cmd_set_trap();
+ break;
+ case MKEY_STEAL:
+ do_cmd_steal();
+ break;
+ case MKEY_DODGE:
+ use_ability_blade();
+ break;
+ case MKEY_SCHOOL:
+ cast_school_spell();
+ break;
+ case MKEY_COPY:
+ do_cmd_copy_spell();
+ break;
+ case MKEY_BOULDER:
+ do_cmd_create_boulder();
+ break;
+ case MKEY_COMPANION:
+ if (get_skill(SKILL_LORE) >= 12)
+ do_cmd_companion();
+ else
+ msg_print("You need a skill level of at least 12.");
+ break;
+ case MKEY_PIERCING:
+ do_cmd_set_piercing();
+ break;
+ default:
+ process_hooks(HOOK_MKEY, "(d)", x_idx);
+ break;
+ }
+}
+
+
+/* Which magic forbids non FA gloves */
+bool forbid_gloves()
+{
+ if (get_skill(SKILL_SORCERY)) return (TRUE);
+ if (get_skill(SKILL_MANA)) return (TRUE);
+ if (get_skill(SKILL_FIRE)) return (TRUE);
+ if (get_skill(SKILL_AIR)) return (TRUE);
+ if (get_skill(SKILL_WATER)) return (TRUE);
+ if (get_skill(SKILL_EARTH)) return (TRUE);
+ if (get_skill(SKILL_THAUMATURGY)) return (TRUE);
+ return (FALSE);
+}
+
+/* Which gods forbid edged weapons */
+bool forbid_non_blessed()
+{
+ GOD(GOD_ERU) return (TRUE);
+ return (FALSE);
+}
+
+
+
+
+/*
+ * The autoskiller gets fed with the desired skill values at level 50 and determines
+ * where to invest each levels
+ */
+
+/* Checks if a given autoskill chart is realisable */
+int validate_autoskiller(s32b *ideal)
+{
+ int i;
+ s32b count = 0;
+
+ for (i = 0; i < max_s_idx; i++)
+ {
+ s32b z, c;
+
+ skill_type *s_ptr = &s_info[i];
+
+ if (!ideal[i]) continue;
+
+ /* How much */
+ z = (ideal[i] * SKILL_STEP) - s_ptr->value;
+
+ /* How many points will we need to get there ? */
+ if (s_ptr->mod)
+ c = z / s_ptr->mod;
+ else
+ c = 99999;
+ count += c;
+ }
+ /* DGDGDGDG: I dont work, fix me */
+ /* return (SKILL_NB_BASE * 49) - count;*/
+ return 0;
+}
+
+void autoskiller_level(s32b *ideal)
+{
+#if 0
+ /* DGDGDGDG */
+ int chart[196];
+ int final[196];
+ int ideal[3] = {50, 30, 25};
+ int real[3] = {0, 0, 0};
+ int mod[3] = {450, 1000, 1100};
+ float pl[3], left[3] = {0, 0, 0}, finalization = 1;
+ int c[3], z;
+ int i, max = 0, ok = 3, a;
+
+ for (i = 0; i < 3; i++)
+ {
+ c[i] = (ideal[i] * 1000) / mod[i];
+ printf("point need c[%d] = %d\n", i, c[i]);
+ max += c[i];
+
+ pl[i] = (float)c[i] / 50.0;
+ printf("pl[%d] = %f\n", i, pl[i]);
+ }
+ printf("skill balance %d\n", 196 - max);
+
+ for (i = 0; i < 196; i++)
+ {
+ chart[i] = -1;
+ }
+
+ a = 0;
+ while ((a < 196) && (ok))
+ {
+ int z = 0;
+
+ for (z = 0; z < 3; z++)
+ {
+ int tmp, ii;
+
+ if (real[z] == c[z])
+ continue;
+ left[z] += pl[z];
+ tmp = left[z];
+ left[z] -= tmp;
+ printf("skill %02d: left %f tmp %d\n", z, left[z], tmp);
+
+ while (tmp >= 1)
+ {
+ chart[a++] = z;
+ real[z]++;
+ tmp--;
+ if (real[z] == c[z])
+ {
+ ok--;
+ break;
+ }
+ if (a == 196)
+ {
+ ok = 0;
+ break;
+ }
+ }
+ if (!ok) break;
+ }
+ }
+ printf("ok %d, a %d interval %f\n", ok, a, (float)a / 196);
+ real[0] = real[1] = real[2] = 0;
+ for (i = 0; i < 196; i++)
+ {
+ real[chart[i]]++;
+ }
+ z = 0;
+ i = 0;
+ while (z < a)
+ {
+ if (finalization > 1)
+ {
+ final[i] = chart[z++];
+ finalization--;
+ }
+ else
+ final[i] = -1;
+ finalization += ((float)a / 196);
+ i++;
+ }
+ for (z = 0; z < 3; z++)
+ {
+ printf("skill %d real %d ideal %d\n", z, real[z], c[z]);
+ }
+
+ for (i = 2; i <= 50; i++)
+ {
+ printf("level %02d, skills %02d %02d %02d %02d\n", i, final[i * 4], final[i * 4 + 1], final[i * 4 + 2], final[i * 4 + 3]);
+ }
+#endif
+}
+
+/*
+ * Gets the base value of a skill, given a race/class/...
+ */
+void compute_skills(s32b *v, s32b *m, int i)
+{
+ s32b value, mod;
+
+ /***** general skills *****/
+
+ /* If the skill correspond to the magic school lets pump them a bit */
+ value = gen_skill_base[i];
+ mod = gen_skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, gen_skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, gen_skill_modm[i]);
+
+ /***** race skills *****/
+
+ value = rp_ptr->skill_base[i];
+ mod = rp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, rp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, rp_ptr->skill_modm[i]);
+
+ /***** race mod skills *****/
+
+ value = rmp_ptr->skill_base[i];
+ mod = rmp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, rmp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, rmp_ptr->skill_modm[i]);
+
+ /***** class skills *****/
+
+ value = cp_ptr->skill_base[i];
+ mod = cp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, cp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, cp_ptr->skill_modm[i]);
+
+ /***** class spec skills *****/
+
+ value = spp_ptr->skill_base[i];
+ mod = spp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, spp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, spp_ptr->skill_modm[i]);
+}
+
+/*
+ * Initialize a skill with given values
+ */
+void init_skill(s32b value, s32b mod, int i)
+{
+ s_info[i].value = value;
+ s_info[i].mod = mod;
+
+ if (s_info[i].flags1 & SKF1_HIDDEN)
+ s_info[i].hidden = TRUE;
+ else
+ s_info[i].hidden = FALSE;
+}
+
+void do_get_new_skill()
+{
+ char *items[4];
+ int skl[4];
+ s32b val[4], mod[4];
+ bool used[MAX_SKILLS];
+ int available_skills[MAX_SKILLS];
+ int max = 0, max_a = 0, res, i;
+
+ /* Check if some skills didn't influence other stuff */
+ recalc_skills(TRUE);
+
+ /* Grab the ones we can gain */
+ max = 0;
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if (s_info[i].flags1 & SKF1_RANDOM_GAIN)
+ available_skills[max++] = i;
+ }
+ available_skills[max++] = -1;
+
+ /* Init */
+ for (max = 0; max < MAX_SKILLS; max++)
+ {
+ used[max] = FALSE;
+ }
+
+ /* Count the number of available skills */
+ while (available_skills[max_a] != -1) max_a++;
+
+ /* Get 4 skills */
+ for (max = 0; max < 4; max++)
+ {
+ int i;
+ skill_type *s_ptr;
+
+ /* Get an non used skill */
+ do
+ {
+ i = rand_int(max_a);
+
+ /* Does it pass the check? */
+ if (!magik(s_info[available_skills[i]].random_gain_chance))
+ continue;
+ }
+ while (used[available_skills[i]]);
+
+ s_ptr = &s_info[available_skills[i]];
+ used[available_skills[i]] = TRUE;
+
+ if (s_ptr->mod)
+ {
+ if (s_ptr->mod < 300)
+ {
+ val[max] = 1000;
+ mod[max] = 300 - s_ptr->mod;
+ }
+ else if (s_ptr->mod < 500)
+ {
+ val[max] = s_ptr->mod * 1;
+ mod[max] = 100;
+ if (mod[max] + s_ptr->mod > 500)
+ mod[max] = 500 - s_ptr->mod;
+ }
+ else
+ {
+ val[max] = s_ptr->mod * 3;
+ mod[max] = 0;
+ }
+ }
+ else
+ {
+ mod[max] = 300;
+ val[max] = 1000;
+ }
+ if (s_ptr->value + val[max] > SKILL_MAX) val[max] = SKILL_MAX - s_ptr->value;
+ skl[max] = available_skills[i];
+ items[max] = (char *)string_make(format("%-40s: +%02ld.%03ld value, +%01d.%03d modifier", s_ptr->name + s_name, val[max] / SKILL_STEP, val[max] % SKILL_STEP, mod[max] / SKILL_STEP, mod[max] % SKILL_STEP));
+ }
+
+ while (TRUE)
+ {
+ res = ask_menu("Choose a skill to learn(a-d to choose, ESC to cancel)?", (char **)items, 4);
+
+ /* Ok ? lets learn ! */
+ if (res > -1)
+ {
+ skill_type *s_ptr;
+ bool oppose = FALSE;
+ int oppose_skill = -1;
+
+ /* Check we don't oppose an existing skill */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if ((s_info[i].action[skl[res]] == SKILL_EXCLUSIVE) &&
+ (s_info[i].value != 0))
+ {
+ oppose = TRUE;
+ oppose_skill = i;
+ break;
+ }
+ }
+
+ /* Ok we oppose, so be sure */
+ if (oppose)
+ {
+ cptr msg;
+
+ /*
+ * Because this is SO critical a question, we must flush
+ * input to prevent killing character off -- pelpel
+ */
+ flush();
+
+ /* Prepare prompt */
+ msg = format("This skill is mutually exclusive with "
+ "at least %s, continue?",
+ s_info[oppose_skill].name + s_name);
+
+ /* The player rejected the choice */
+ if (!get_check(msg)) continue;
+ }
+
+ s_ptr = &s_info[skl[res]];
+ s_ptr->value += val[res];
+ s_ptr->mod += mod[res];
+ if (mod[res])
+ {
+ msg_format("You can now learn the %s skill.",
+ s_ptr->name + s_name);
+ }
+ else
+ {
+ msg_format("Your knowledge of the %s skill increases.",
+ s_ptr->name + s_name);
+ }
+ break;
+ }
+ }
+
+ /* Free them ! */
+ for (max = 0; max < 4; max++)
+ {
+ string_free(items[max]);
+ }
+
+ /* Check if some skills didn't influence other stuff */
+ recalc_skills(FALSE);
+}
+
+
+
+
+/**************************************** ABILITIES *****************************************/
+
+/*
+ * Given the name of an ability, returns ability index or -1 if no
+ * such ability is found
+ */
+s16b find_ability(cptr name)
+{
+ u16b i;
+
+ /* Scan ability list */
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ /* The name matches */
+ if (streq(ab_info[i].name + ab_name, name)) return (i);
+ }
+
+ /* No match found */
+ return ( -1);
+}
+
+/*
+ * Do the player have the ability
+ */
+bool has_ability(int ab)
+{
+ return ab_info[ab].acquired;
+}
+
+/* Do we meet the requirements */
+bool can_learn_ability(int ab)
+{
+ ability_type *ab_ptr = &ab_info[ab];
+ int i;
+
+ if (ab_ptr->acquired)
+ return FALSE;
+
+ if (p_ptr->skill_points < ab_info[ab].cost)
+ return FALSE;
+
+ for (i = 0; i < 10; i++)
+ {
+ /* Must have skill level */
+ if (ab_ptr->skills[i] > -1)
+ {
+ if (get_skill(ab_ptr->skills[i]) < ab_ptr->skill_levels[i])
+ return FALSE;
+ }
+
+ /* Must have ability */
+ if (ab_ptr->need_abilities[i] > -1)
+ {
+ if (!ab_info[ab_ptr->need_abilities[i]].acquired)
+ return FALSE;
+ }
+
+ /* Must not have ability */
+ if (ab_ptr->forbid_abilities[i] > -1)
+ {
+ if (ab_info[ab_ptr->forbid_abilities[i]].acquired)
+ return FALSE;
+ }
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ /* Must have stat */
+ if (ab_ptr->stat[i] > -1)
+ {
+ if (p_ptr->stat_ind[i] < ab_ptr->stat[i] - 3)
+ return FALSE;
+ }
+ }
+
+ /* Do the script allow us? */
+ if (process_hooks(HOOK_LEARN_ABILITY, "(d)", ab))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Learn an ability */
+void gain_ability(int ab)
+{
+ int wid, hgt;
+ Term_get_size(&wid, &hgt);
+
+ if (!can_learn_ability(ab))
+ {
+ msg_box("You cannot learn this ability.", (int)(hgt / 2), (int)(wid / 2));
+ return;
+ }
+
+ /* Flush input as we ask an important and irreversible question */
+ flush();
+
+ /* Ask we can commit the change */
+ if (msg_box("Learn this ability(this is permanent)? (y/n)", (int)(hgt / 2), (int)(wid / 2)) != 'y')
+ {
+ return;
+ }
+
+ ab_info[ab].acquired = TRUE;
+ p_ptr->skill_points -= ab_info[ab].cost;
+}
+
+/* helper function to generate a sorted table */
+static void add_sorted_ability(int *table, int *max, int ab)
+{
+ int i;
+
+ for (i = 0; i < *max; i++)
+ {
+ if (strcmp(ab_name + ab_info[ab].name, ab_name + ab_info[table[i]].name) < 0)
+ {
+ int z;
+
+ /* Move all indexes up */
+ for (z = *max; z > i; z--)
+ {
+ table[z] = table[z - 1];
+ }
+ break;
+ }
+ }
+ table[i] = ab;
+
+ (*max)++;
+}
+
+/*
+ * Print the abilities list
+ */
+void dump_abilities(FILE *fff)
+{
+ int i, j;
+ int *table;
+ int max = 0;
+
+ C_MAKE(table, max_ab_idx, int);
+
+ /* Initialise the abilities list */
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].name && has_ability(i))
+ add_sorted_ability(table, &max, i);
+ }
+
+ if (max)
+ {
+ fprintf(fff, "\nAbilities");
+
+ for (j = 0; j < max; j++)
+ {
+ i = table[j];
+
+ fprintf(fff, "\n * %s", ab_info[i].name + ab_name);
+ }
+
+ fprintf(fff, "\n");
+ }
+}
+
+/*
+ * Draw the abilities list
+ */
+void print_abilities(int table[], int max, int sel, int start)
+{
+ int i, j;
+ int wid, hgt;
+ cptr keys;
+
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, format("%s Abilities Screen", game_module), 0, 28);
+ keys = format("#Bup#W/#Bdown#W to move, #Bright#W to buy, #B?#W for help");
+ display_message(0, 1, strlen(keys), TERM_WHITE, keys);
+ c_prt((p_ptr->skill_points) ? TERM_L_BLUE : TERM_L_RED,
+ format("Skill points left: %d", p_ptr->skill_points), 2, 0);
+
+ print_desc_aux(ab_info[table[sel]].desc + ab_text, 3, 0);
+
+ for (j = start; j < start + (hgt - 7); j++)
+ {
+ byte color = TERM_WHITE;
+ char deb = ' ', end = ' ';
+
+ if (j >= max) break;
+
+ i = table[j];
+
+ if (ab_info[i].acquired)
+ color = TERM_L_BLUE;
+ else if (can_learn_ability(i))
+ color = TERM_WHITE;
+ else
+ color = TERM_L_DARK;
+
+
+ if (j == sel)
+ {
+ color = TERM_L_GREEN;
+ deb = '[';
+ end = ']';
+ }
+
+ c_prt(color, format("%c.%c%s", deb, end, ab_info[i].name + ab_name),
+ j + 7 - start, 0);
+
+ if (!ab_info[i].acquired)
+ {
+ c_prt(color, format("%d", ab_info[i].cost), j + 7 - start, 60);
+ }
+ else
+ {
+ c_prt(color, "Known", j + 7 - start, 60);
+ }
+ }
+}
+
+/*
+ * Interreact with abilitiess
+ */
+void do_cmd_ability()
+{
+ int sel = 0, start = 0, max = 0;
+ char c;
+ int *table;
+ int i;
+ int wid, hgt;
+
+ C_MAKE(table, max_ab_idx, int);
+
+ /* Save the screen */
+ screen_save();
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Initialise the abilities list */
+ for (i = 0; i < max_ab_idx; i++)
+ {
+#if 0 /* Only show learnable */
+ if (ab_info[i].name && ((can_learn_ability(i) || has_ability(i)) || wizard))
+#else /* Show all */
+if (ab_info[i].name)
+#endif
+ add_sorted_ability(table, &max, i);
+ }
+
+ while (TRUE)
+ {
+ Term_get_size(&wid, &hgt);
+
+ /* Display list of skills */
+ print_abilities(table, max, sel, start);
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the skill screen */
+ if (c == ESCAPE) break;
+
+ /* Next page */
+ else if (c == 'n')
+ {
+ sel += (hgt - 7);
+ if (sel >= max) sel = max - 1;
+ }
+
+ /* Previous page */
+ else if (c == 'p')
+ {
+ sel -= (hgt - 7);
+ if (sel < 0) sel = 0;
+ }
+
+ /* Select / increase a skill */
+ else
+ {
+ int dir;
+
+ /* Allow use of numpad / arrow keys / roguelike keys */
+ dir = get_keymap_dir(c);
+
+ /* Move cursor down */
+ if (dir == 2) sel++;
+
+ /* Move cursor up */
+ if (dir == 8) sel--;
+
+ /* gain ability */
+ if (dir == 6) gain_ability(table[sel]);
+
+ /* XXX XXX XXX Wizard mode commands outside of wizard2.c */
+
+ if (wizard && (c == '+')) ab_info[table[sel]].acquired = TRUE;
+ if (wizard && (c == '-')) ab_info[table[sel]].acquired = FALSE;
+
+ /* Contextual help */
+ if (c == '?') exec_lua(format("ingame_help('select_context', 'ability', '%s')", ab_info[table[sel]].name + ab_name));
+ ;
+
+ /* Handle boundaries and scrolling */
+ if (sel < 0) sel = max - 1;
+ if (sel >= max) sel = 0;
+ if (sel < start) start = sel;
+ if (sel >= start + (hgt - 7)) start = sel - (hgt - 7) + 1;
+ }
+ }
+
+ /* Load the screen */
+ screen_load();
+
+ C_FREE(table, max_ab_idx, int);
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Redraw various info */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+}
+
+/*
+ * Apply abilities to be granted this level
+ */
+void apply_level_abilities(int level)
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ if (cp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[cp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[cp_ptr->abilities[i].ability].name);
+ ab_info[cp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ if (spp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[spp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[spp_ptr->abilities[i].ability].name);
+ ab_info[spp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ if (rp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[rp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[rp_ptr->abilities[i].ability].name);
+ ab_info[rp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ if (rmp_ptr->abilities[i].level == level)
+ {
+ if ((level > 1) && (!ab_info[rmp_ptr->abilities[i].ability].acquired))
+ cmsg_format(TERM_L_GREEN, "You have learned the ability '%s'.", ab_name + ab_info[rmp_ptr->abilities[i].ability].name);
+ ab_info[rmp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ }
+}
diff --git a/src/spells.pkg b/src/spells.pkg
new file mode 100644
index 00000000..65081aff
--- /dev/null
+++ b/src/spells.pkg
@@ -0,0 +1,2498 @@
+/* File: spells.pkg */
+
+/*
+ * Purpose: Lua interface defitions for spells.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+$#include "lua.h"
+
+/** @typedef *mcptr
+ * @note String
+ */
+typedef char *mcptr;
+
+/** @typedef cptr
+ * @note String
+ */
+typedef const char* cptr;
+
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @def DEFAULT_RADIUS */
+#define DEFAULT_RADIUS 25
+
+/** @name Spell Damage Types
+ * @brief Type of damage caused by spell
+ * @{ */
+/** @def GF_ELEC */
+#define GF_ELEC 1
+
+/** @def GF_POIS */
+#define GF_POIS 2
+
+/** @def GF_ACID */
+#define GF_ACID 3
+
+/** @def GF_COLD */
+#define GF_COLD 4
+
+/** @def GF_FIRE */
+#define GF_FIRE 5
+
+/** @def GF_UNBREATH */
+#define GF_UNBREATH 6
+
+/** @def GF_CORPSE_EXPL */
+#define GF_CORPSE_EXPL 7
+
+/** @def GF_MISSILE */
+#define GF_MISSILE 10
+
+/** @def GF_ARROW */
+#define GF_ARROW 11
+
+/** @def GF_PLASMA */
+#define GF_PLASMA 12
+
+/** @def GF_WAVE */
+#define GF_WAVE 13
+
+/** @def GF_WATER */
+#define GF_WATER 14
+
+/** @def GF_LITE */
+#define GF_LITE 15
+
+/** @def GF_DARK */
+#define GF_DARK 16
+
+/** @def GF_LITE_WEAK */
+#define GF_LITE_WEAK 17
+
+/** @def GF_DARK_WEAK */
+#define GF_DARK_WEAK 18
+
+/** @def GF_SHARDS */
+#define GF_SHARDS 20
+
+/** @def GF_SOUND */
+#define GF_SOUND 21
+
+/** @def GF_CONFUSION */
+#define GF_CONFUSION 22
+
+/** @def GF_FORCE */
+#define GF_FORCE 23
+
+/** @def GF_INERTIA */
+#define GF_INERTIA 24
+
+/** @def GF_MANA */
+#define GF_MANA 26
+
+/** @def GF_METEOR */
+#define GF_METEOR 27
+
+/** @def GF_ICE */
+#define GF_ICE 28
+
+/** @def GF_CHAOS */
+#define GF_CHAOS 30
+
+/** @def GF_NETHER */
+#define GF_NETHER 31
+
+/** @def GF_DISENCHANT */
+#define GF_DISENCHANT 32
+
+/** @def GF_NEXUS */
+#define GF_NEXUS 33
+
+/** @def GF_TIME */
+#define GF_TIME 34
+
+/** @def GF_GRAVITY */
+#define GF_GRAVITY 35
+
+/** @def GF_KILL_WALL */
+#define GF_KILL_WALL 40
+
+/** @def GF_KILL_DOOR */
+#define GF_KILL_DOOR 41
+
+/** @def GF_KILL_TRAP */
+#define GF_KILL_TRAP 42
+
+/** @def GF_MAKE_WALL */
+#define GF_MAKE_WALL 45
+
+/** @def GF_MAKE_DOOR */
+#define GF_MAKE_DOOR 46
+
+/** @def GF_MAKE_TRAP */
+#define GF_MAKE_TRAP 47
+
+/** @def GF_OLD_CLONE */
+#define GF_OLD_CLONE 51
+
+/** @def GF_OLD_POLY */
+#define GF_OLD_POLY 52
+
+/** @def GF_OLD_HEAL */
+#define GF_OLD_HEAL 53
+
+/** @def GF_OLD_SPEED */
+#define GF_OLD_SPEED 54
+
+/** @def GF_OLD_SLOW */
+#define GF_OLD_SLOW 55
+
+/** @def GF_OLD_CONF */
+#define GF_OLD_CONF 56
+
+/** @def GF_OLD_SLEEP */
+#define GF_OLD_SLEEP 57
+
+/** @def GF_OLD_DRAIN */
+#define GF_OLD_DRAIN 58
+
+/** @def GF_AWAY_UNDEAD */
+#define GF_AWAY_UNDEAD 61
+
+/** @def GF_AWAY_EVIL */
+#define GF_AWAY_EVIL 62
+
+/** @def GF_AWAY_ALL */
+#define GF_AWAY_ALL 63
+
+/** @def GF_TURN_UNDEAD */
+#define GF_TURN_UNDEAD 64
+
+/** @def GF_TURN_EVIL */
+#define GF_TURN_EVIL 65
+
+/** @def GF_TURN_ALL */
+#define GF_TURN_ALL 66
+
+/** @def GF_DISP_UNDEAD */
+#define GF_DISP_UNDEAD 67
+
+/** @def GF_DISP_EVIL */
+#define GF_DISP_EVIL 68
+
+/** @def GF_DISP_ALL */
+#define GF_DISP_ALL 69
+
+/* New types for Zangband begin here... */
+
+/** @def GF_DISP_DEMON */
+#define GF_DISP_DEMON 70
+
+/** @def GF_DISP_LIVING */
+#define GF_DISP_LIVING 71
+
+/** @def GF_ROCKET */
+#define GF_ROCKET 72
+
+/** @def GF_NUKE */
+#define GF_NUKE 73
+
+/** @def GF_MAKE_GLYPH */
+#define GF_MAKE_GLYPH 74
+
+/** @def GF_STASIS */
+#define GF_STASIS 75
+
+/** @def GF_STONE_WALL */
+#define GF_STONE_WALL 76
+
+/** @def GF_DEATH_RAY */
+#define GF_DEATH_RAY 77
+
+/** @def GF_STUN */
+#define GF_STUN 78
+
+/** @def GF_HOLY_FIRE */
+#define GF_HOLY_FIRE 79
+
+/** @def GF_HELL_FIRE */
+#define GF_HELL_FIRE 80
+
+/** @def GF_DISINTEGRATE */
+#define GF_DISINTEGRATE 81
+
+/** @def GF_CHARM */
+#define GF_CHARM 82
+
+/** @def GF_CONTROL_UNDEAD */
+#define GF_CONTROL_UNDEAD 83
+
+/** @def GF_CONTROL_ANIMAL */
+#define GF_CONTROL_ANIMAL 84
+
+/** @def GF_PSI */
+#define GF_PSI 85
+
+/** @def GF_PSI_DRAIN */
+#define GF_PSI_DRAIN 86
+
+/** @def GF_TELEKINESIS */
+#define GF_TELEKINESIS 87
+
+/** @def GF_JAM_DOOR */
+#define GF_JAM_DOOR 88
+
+/** @def GF_DOMINATION */
+#define GF_DOMINATION 89
+
+/** @def GF_DISP_GOOD */
+#define GF_DISP_GOOD 90
+
+/** @def GF_IDENTIFY */
+#define GF_IDENTIFY 91
+
+/** @def GF_RAISE */
+#define GF_RAISE 92
+
+/** @def GF_STAR_IDENTIFY */
+#define GF_STAR_IDENTIFY 93
+
+/** @def GF_DESTRUCTION */
+#define GF_DESTRUCTION 94
+
+/** @def GF_STUN_CONF */
+#define GF_STUN_CONF 95
+
+/** @def GF_STUN_DAM */
+#define GF_STUN_DAM 96
+
+/** @def GF_CONF_DAM */
+#define GF_CONF_DAM 98
+
+/** @def GF_STAR_CHARM */
+#define GF_STAR_CHARM 99
+
+/** @def GF_IMPLOSION */
+#define GF_IMPLOSION 100
+
+/** @def GF_LAVA_FLOW */
+#define GF_LAVA_FLOW 101
+
+/** @def GF_FEAR */
+#define GF_FEAR 102
+
+/** @def GF_BETWEEN_GATE */
+#define GF_BETWEEN_GATE 103
+
+/** @def GF_WINDS_MANA */
+#define GF_WINDS_MANA 104
+
+/** @def GF_DEATH */
+#define GF_DEATH 105
+
+/** @def GF_CONTROL_DEMON */
+#define GF_CONTROL_DEMON 106
+
+/** @def GF_RAISE_DEMON */
+#define GF_RAISE_DEMON 107
+
+/** @def GF_TRAP_DEMONSOUL */
+#define GF_TRAP_DEMONSOUL 108
+
+/** @def GF_ATTACK */
+#define GF_ATTACK 109
+
+/** @def GF_CHARM_UNMOVING */
+#define GF_CHARM_UNMOVING 110
+
+/** @def MAX_GF */
+#define MAX_GF 111
+/** @} */
+
+/** @name Spell Projection Flags
+ * @brief Area affected by spell
+ * @{ */
+/** @def PROJECT_JUMP
+ * @note Jump directly to the target location (this is a hack)
+ */
+#define PROJECT_JUMP 0x00000001
+
+/** @def PROJECT_BEAM
+ * @note Work as a beam weapon (affect every grid passed through)
+ */
+#define PROJECT_BEAM 0x00000002
+
+/** @def PROJECT_THRU
+ * @note Continue "through" the target (used for "bolts"/"beams")
+ */
+#define PROJECT_THRU 0x00000004
+
+/** @def PROJECT_STOP
+ * @note Stop as soon as we hit a monster (used for "bolts")
+ */
+#define PROJECT_STOP 0x00000008
+
+/** @def PROJECT_GRID
+ * @note Affect each grid in the "blast area" in some way
+ */
+#define PROJECT_GRID 0x00000010
+
+/** @def PROJECT_ITEM
+ * @note Affect each object in the "blast area" in some way
+ */
+#define PROJECT_ITEM 0x00000020
+
+/** @def PROJECT_KILL
+ * @note Affect each monster in the "blast area" in some way
+ */
+#define PROJECT_KILL 0x00000040
+
+/** @def PROJECT_HIDE
+ * @note Hack -- disable "visual" feedback from projection
+ */
+#define PROJECT_HIDE 0x00000080
+
+/** @def PROJECT_VIEWABLE
+ * @note Affect monsters in LOS
+ */
+#define PROJECT_VIEWABLE 0x00000100
+
+/** @def PROJECT_METEOR_SHOWER
+ * @note Affect random grids
+ */
+#define PROJECT_METEOR_SHOWER 0x00000200
+
+/** @def PROJECT_BLAST
+ * @note Like Mega_blast, but will only affect viewable grids
+ */
+#define PROJECT_BLAST 0x00000400
+
+/** @def PROJECT_PANEL
+ * @note Affect everything in the panel.
+ */
+#define PROJECT_PANEL 0x00000800
+
+/** @def PROJECT_ALL
+ * @note Affect every single grid.
+ */
+#define PROJECT_ALL 0x00001000
+
+/** @def PROJECT_WALL
+ * @note Continue "through" the walls
+ */
+#define PROJECT_WALL 0x00002000
+
+/** @def PROJECT_MANA_PATH
+ * @note Follow a mana path.
+ */
+#define PROJECT_MANA_PATH 0x00004000
+
+/** @def PROJECT_ABSORB_MANA
+ * @note The spell increase in power as it absord grid's mana.
+ */
+#define PROJECT_ABSORB_MANA 0x00008000
+
+/** @def PROJECT_STAY */
+#define PROJECT_STAY 0x00010000
+/** @} */
+
+/** @var project_time
+ * @brief Number
+ * @note The length of time a spell effect exists.
+ */
+extern int project_time;
+
+/** @fn teleport_player_directed(int rad, int dir)
+ * @brief Teleport a player up to "rad" grids away roughly in "dir"
+ * direction.\n
+ * @param rad Number \n rad must not exceed 200. The distance teleported is
+ * at least a quarter of rad.
+ * @brief Distance
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @note
+ * Teleport player, using a distance and a direction as a rough guide.\n\n
+ * This function is not at all obsessive about correctness.\n
+ * This function allows teleporting into vaults (!)
+ * @note (see file spells1.c)
+ */
+extern void teleport_player_directed(int rad, int dir);
+
+/** @fn teleport_away(int m_idx, int dis)
+ * @brief Teleport monster indicated by "m_idx" up to "dis" grids away.\n
+ * @param m_idx Number \n m_idx is the index of the monster in m_list[].
+ * @brief Monster index
+ * @param dis Number \n dis must not exceed 200. The distance teleported
+ * is a minimum of a quarter of "dis".
+ * @brief Distance
+ * @note
+ * Teleport a monster, normally up to "dis" grids away.\n\n
+ * Attempt to move the monster at least "dis/2" grids away.\n\n
+ * But allow variation to prevent infinite loops.
+ * @note (see file spells1.c)
+ */
+extern void teleport_away(int m_idx, int dis);
+
+/** @fn teleport_player(int dis)
+ * @brief Teleport player up to "dis" grids away.\n
+ * @param dis Number \n dis must not exceed 200. The distance teleported
+ * is a minimum of a quarter of dis.
+ * @brief Distance
+ * @note
+ * Teleport the player to a location up to "dis" grids away.\n\n
+ * If no such spaces are readily available, the distance may increase.\n
+ * Try very hard to move the player at least a quarter that distance.
+ * @note (see file spells1.c)
+ */
+extern void teleport_player(int dis);
+
+/** @fn teleport_player_to(int ny, int nx)
+ * @brief Teleport player to a grid near coordinate ("ny", "nx").\n
+ * @param ny Number \n ny is the y co-ordinate of the location.
+ * @brief Y coordinate
+ * @param nx Number \n nx is the x co-ordinate of the location.
+ * @brief X coordinate
+ * @note
+ * Teleport player to a grid near the given location\n\n
+ * This function is slightly obsessive about correctness.\n
+ * This function allows teleporting into vaults (!)\n\n
+ * If the location is empty, the player goes there, otherwise they go to
+ * a grid as close as possible to the location.
+ * @note (see file spells1.c)
+ */
+extern void teleport_player_to(int ny, int nx);
+
+/** @fn teleport_monster_to(int m_idx, int ny, int nx)
+ * @brief Teleport monster indicated by "m_idx" to a grid near coordinate
+ * ("ny", "nx").\n
+ * @param m_idx Number \n m_idx is the index of the monster in m_list[].
+ * @brief Monster index
+ * @param ny Number \n ny is the y co-ordinate of the location.
+ * @brief Y coordinate
+ * @param nx Number \n nx is the x co-ordinate of the location.
+ * @brief X coordinate
+ * @note
+ * Teleport a monster to a grid near the given location\n\n
+ * This function is slightly obsessive about correctness.\n\n
+ * If the location is empty, the monster goes there, otherwise they go to
+ * a grid as close as possible to the location.
+ * @note (see file spells1.c)
+ */
+extern void teleport_monster_to(int m_idx, int ny, int nx);
+
+/** @fn teleport_monster(int dir)
+ * @brief Teleport away all monsters in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * All monsters in direction "dir" are teleported away and sustain
+ * MAX_SIGHT (20) x 5 damage.\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool teleport_monster(int dir);
+
+/** @fn teleport_player_level(void)
+ * @brief Teleport the player one level up or down at random.
+ * @note
+ * Teleport the player one level up or down (random when legal)
+ * @note (see file spells1.c)
+ */
+extern void teleport_player_level(void);
+
+/** @fn fetch(int dir, int wgt, bool require_los)
+ * @brief Fetch an item in direction "dir" with weight "wgt" possibly not in
+ * line of sight.\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param wgt Number \n maximum weight of object.
+ * @brief Weight
+ * @param require_los Boolean \n TRUE if line of sight is required, otherwise
+ * FALSE.
+ * @brief Require-line-of-sight flag
+ * @note
+ * Fetch an item (teleport it right underneath the caster)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * Fetch will fail if player is standing on something, or if the object is
+ * too far away, or if require_los is TRUE and player does not have line
+ * of sight to the object, or the object is too heavy. Otherwise the
+ * object appears at the player's feet (same grid as player).
+ * @note (see file cmd5.c)
+ */
+extern void fetch(int dir, int wgt, bool require_los);
+
+/** @fn recall_player(int d, int f)
+ * @brief Recall the player to town (if in dungeon) or dungeon (if in town).\n
+ * @param d Number \n Random time interval
+ * @brief Dice
+ * @param f Number \n Fixed time interval
+ * @brief Fixed
+ * @note (see file spells1.c)
+ */
+extern void recall_player(int d, int f);
+
+/** @fn take_hit(int damage, cptr kb_str)
+ * @brief Reduce player hit points by "damage" inflicted by "kb_str".\n
+ * @param damage Number \n damage is the number of hit points of damage.
+ * @brief Damage
+ * @param kb_str String \n kb_str describes what killed the player
+ * (in the event the player dies)
+ * @brief Killed by
+ * @note
+ * Decreases players hit points and sets death flag if necessary\n\n
+ * XXX XXX XXX Invulnerability needs to be changed into a "shield"\n\n
+ * XXX XXX XXX Hack -- this function allows the user to save (or quit)
+ * the game when he dies, since the "You die." message is shown before
+ * setting the player to "dead".
+ * @note (see file spells1.c)
+ */
+extern void take_hit(int damage, cptr kb_str);
+
+/** @fn take_sanity_hit(int damage, cptr hit_from)
+ * @brief Reduce player sanity points by "damage" inflicted by "hit_from".\n
+ * @param damage Number \n damage is the number of sanity points of damage.
+ * @brief Damage
+ * @param hit_from String \n hit_from describes what caused the damage.
+ * @brief Hit from
+ * @note
+ * Decrease player's sanity. This is a copy of the function above.\n\n
+ * Reduce the player's current sanity points by "damage" points. If the
+ * player dies, "hit_from" is used to record what the player was killed
+ * by (see high-score table).
+ * @note (see file spells1.c)
+ */
+extern void take_sanity_hit(int damage, cptr hit_from);
+
+/** @fn project(int who, int rad, int y, int x, int dam, int typ, int flg)
+ * @brief Generate a beam/bolt/ball with properties "flg" starting from "who"
+ * with a radius of "rad" at target grid "y,x" for "dam" points of "typ"
+ * damage.\n
+ * @param who Number \n who is > 0 (index of monster in m_list[]), < 0 and
+ * not -100 or -101 (player), -100 or -101 (trap).
+ * @brief Source
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-9 for a ball.
+ * @brief Radius
+ * @param y Number \n y is the y coordinate of the target grid.
+ * @brief Y-coordinate
+ * @param x Number \n x is the x co-ordinate of the target grid.
+ * @brief X-coordinate
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param flg Number \n flg is the projection effect (PROJECT field).
+ * @brief Properties flag
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Generic "beam"/"bolt"/"ball" projection routine.\n\n
+ * Input:\n
+ * who: Index of "source" monster (negative for "player")\n
+ * jk -- -2 for traps, only used with project_jump\n
+ * rad: Radius of explosion (0 = beam/bolt, 1 to 9 = ball)\n
+ * y,x: Target location (or location to travel "towards")\n
+ * dam: Base damage roll to apply to affected monsters (or player)\n
+ * typ: Type of damage to apply to monsters (and objects)\n
+ * flg: Extra bit flags (see PROJECT_xxxx in "defines.h")\n\n
+ * Return:\n
+ * TRUE if any "effects" of the projection were observed, else FALSE\n\n
+ * Allows a monster (or player) to project a beam/bolt/ball of a given kind
+ * towards a given location (optionally passing over the heads of interposing
+ * monsters), and have it do a given amount of damage to the monsters (and
+ * optionally objects) within the given radius of the final location.\n\n
+ * A "bolt" travels from source to target and affects only the target grid.\n
+ * A "beam" travels from source to target, affecting all grids passed through.\n
+ * A "ball" travels from source to the target, exploding at the target, and
+ * affecting everything within the given radius of the target location.\n\n
+ * Traditionally, a "bolt" does not affect anything on the ground, and does
+ * not pass over the heads of interposing monsters, much like a traditional
+ * missile, and will "stop" abruptly at the "target" even if no monster is
+ * positioned there, while a "ball", on the other hand, passes over the heads
+ * of monsters between the source and target, and affects everything except
+ * the source monster which lies within the final radius, while a "beam"
+ * affects every monster between the source and target, except for the casting
+ * monster (or player), and rarely affects things on the ground.\n\n
+ * Two special flags allow us to use this function in special ways, the
+ * "PROJECT_HIDE" flag allows us to perform "invisible" projections, while
+ * the "PROJECT_JUMP" flag allows us to affect a specific grid, without
+ * actually projecting from the source monster (or player).\n\n
+ * The player will only get "experience" for monsters killed by himself
+ * Unique monsters can only be destroyed by attacks from the player\n\n
+ * Only 256 grids can be affected per projection, limiting the effective
+ * "radius" of standard ball attacks to nine units (diameter nineteen).\n\n
+ * One can project in a given "direction" by combining PROJECT_THRU with small
+ * offsets to the initial location (see "line_spell()"), or by calculating
+ * "virtual targets" far away from the player.\n\n
+ * One can also use PROJECT_THRU to send a beam/bolt along an angled path,
+ * continuing until it actually hits something (useful for "stone to mud").\n\n
+ * Bolts and Beams explode INSIDE walls, so that they can destroy doors.\n\n
+ * Balls must explode BEFORE hitting walls, or they would affect monsters
+ * on both sides of a wall. Some bug reports indicate that this is still
+ * happening in 2.7.8 for Windows, though it appears to be impossible.\n\n
+ * We "pre-calculate" the blast area only in part for efficiency.
+ * More importantly, this lets us do "explosions" from the "inside" out.
+ * This results in a more logical distribution of "blast" treasure.
+ * It also produces a better (in my opinion) animation of the explosion.
+ * It could be (but is not) used to have the treasure dropped by monsters
+ * in the middle of the explosion fall "outwards", and then be damaged by
+ * the blast as it spreads outwards towards the treasure drop location.\n\n
+ * Walls and doors are included in the blast area, so that they can be
+ * "burned" or "melted" in later versions.\n\n
+ * This algorithm is intended to maximise simplicity, not necessarily
+ * efficiency, since this function is not a bottleneck in the code.\n\n
+ * We apply the blast effect from ground zero outwards, in several passes,
+ * first affecting features, then objects, then monsters, then the player.
+ * This allows walls to be removed before checking the object or monster
+ * in the wall, and protects objects which are dropped by monsters killed
+ * in the blast, and allows the player to see all affects before he is
+ * killed or teleported away. The semantics of this method are open to
+ * various interpretations, but they seem to work well in practice.\n\n
+ * We process the blast area from ground-zero outwards to allow for better
+ * distribution of treasure dropped by monsters, and because it provides a
+ * pleasing visual effect at low cost.\n\n
+ * Note that the damage done by "ball" explosions decreases with distance.
+ * This decrease is rapid, grids at radius "dist" take "1/dist" damage.\n\n
+ * Notice the "napalm" effect of "beam" weapons. First they "project" to
+ * the target, and then the damage "flows" along this beam of destruction.
+ * The damage at every grid is the same as at the "centre" of a "ball"
+ * explosion, since the "beam" grids are treated as if they ARE at the
+ * centre of a "ball" explosion.\n\n
+ * Currently, specifying "beam" plus "ball" means that locations which are
+ * covered by the initial "beam", and also covered by the final "ball", except
+ * for the final grid (the epicentre of the ball), will be "hit twice", once
+ * by the initial beam, and once by the exploding ball. For the grid right
+ * next to the epicentre, this results in 150% damage being done. The centre
+ * does not have this problem, for the same reason the final grid in a "beam"
+ * plus "bolt" does not -- it is explicitly removed. Simply removing "beam"
+ * grids which are covered by the "ball" will NOT work, as then they will
+ * receive LESS damage than they should. Do not combine "beam" with "ball".\n\n
+ * The array "gy[],gx[]" with current size "grids" is used to hold the
+ * collected locations of all grids in the "blast area" plus "beam path".\n\n
+ * Note the rather complex usage of the "gm[]" array. First, gm[0] is always
+ * zero. Second, for N>1, gm[N] is always the index (in gy[],gx[]) of the
+ * first blast grid (see above) with radius "N" from the blast centre. Note
+ * that only the first gm[1] grids in the blast area thus take full damage.
+ * Also, note that gm[rad+1] is always equal to "grids", which is the total
+ * number of blast grids.\n\n
+ * Note that once the projection is complete, (y2,x2) holds the final location
+ * of bolts/beams, and the "epicentre" of balls.\n\n
+ * Note also that "rad" specifies the "inclusive" radius of projection blast,
+ * so that a "rad" of "one" actually covers 5 or 9 grids, depending on the
+ * implementation of the "distance" function. Also, a bolt can be properly
+ * viewed as a "ball" with a "rad" of "zero".\n\n
+ * Note that if no "target" is reached before the beam/bolt/ball travels the
+ * maximum distance allowed (MAX_RANGE), no "blast" will be induced. This
+ * may be relevant even for bolts, since they have a "1x1" mini-blast.\n\n
+ * Note that for consistency, we "pretend" that the bolt actually takes "time"
+ * to move from point A to point B, even if the player cannot see part of the
+ * projection path. Note that in general, the player will *always* see part
+ * of the path, since it either starts at the player or ends on the player.\n\n
+ * Hack -- we assume that every "projection" is "self-illuminating".\n\n
+ * Hack -- when only a single monster is affected, we automatically track
+ * (and recall) that monster, unless "PROJECT_JUMP" is used.\n\n
+ * Note that all projections now "explode" at their final destination, even
+ * if they were being projected at a more distant destination. This means
+ * that "ball" spells will *always* explode.\n\n
+ * Note that we must call "handle_stuff()" after affecting terrain features
+ * in the blast radius, in case the "illumination" of the grid was changed,
+ * and "update_view()" and "update_monsters()" need to be called.
+ * @note (see file spells1.c)
+ */
+extern bool project(int who, int rad, int y, int x, int dam, int typ, int flg);
+
+/** @fn corrupt_player(void)
+ * @brief Swap two of the player's stats at random.
+ * @note (see file spells1.c)
+ */
+extern void corrupt_player(void);
+
+/** @fn grow_things(s16b type, int rad)
+ * @brief Grow "type" things within "rad" distance of the player.\n
+ * @param type Number \n type of thing to grow (FEAT field).
+ * @brief Type
+ * @param rad Number \n rad is the radius of the area where things may grow.
+ * @brief Radius
+ * @note
+ * Grow things\n\n
+ * Up to (rad * (rad + 11)) things can be grown around the player. The
+ * grids must support growth.
+ * @note (see file spells2.c)
+ */
+extern void grow_things(s16b type, int rad);
+
+/** @fn grow_grass(int rad)
+ * @brief Grow grass within "rad" distance of the player.\n
+ * @param rad Number \n rad is the radius of the area where grass may grow.
+ * @brief Radius
+ * @note
+ * Grow grass\n\n
+ * Up to (rad * (rad + 11)) grass can be grown around the player. The
+ * grids must support growth.
+ * @note (see file spells2.c)
+ */
+extern void grow_grass(int rad);
+
+/** @fn grow_trees(int rad)
+ * @brief Grow trees within "rad" distance of the player.\n
+ * @param rad Number \n rad is the radius of the area where trees may grow.
+ * @brief Radius
+ * @note
+ * Grow trees\n\n
+ * Up to (rad * (rad + 11)) trees can be grown around the player. The
+ * grids must support growth.
+ * @note (see file spells2.c)
+ */
+extern void grow_trees(int rad);
+
+/** @fn hp_player(int num)
+ * @brief Add "num" points to the player's current hit points.\n
+ * @param num Number \n num is the number of points to add.
+ * @brief Number
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Increase players hit points, notice effects\n\n
+ * The total can not exceed the maximum.
+ * @note (see file spells2.c)
+ */
+extern bool hp_player(int num);
+
+/** @fn heal_insanity(int val)
+ * @brief Add "val" points to the player's current sanity points.\n
+ * @param val Number \n val is the number of points to add.
+ * @brief Value
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Heal insanity.\n\n
+ * The total can not exceed the maximum.
+ * @note (see file spells2.c)
+ */
+extern bool heal_insanity(int val);
+
+/** @fn warding_glyph(void)
+ * @brief Place a glyph at the player's location.
+ * @note
+ * Leave a "glyph of warding" which prevents monster movement\n\n
+ * The location must be bare.
+ * @note (see file spells2.c)
+ */
+extern void warding_glyph(void);
+
+/** @fn explosive_rune(void)
+ * @brief Place a minor glyph (explosive rune) at the player's location.
+ * @note
+ * The location must be bare.
+ * @note (see file spells2.c)
+ */
+extern void explosive_rune(void);
+
+/** @fn do_dec_stat(int stat, int mode)
+ * @brief Attempt to reduce the player's "stat" statistic by a point.\n
+ * @param stat Number \n stat is the statistic
+ * @brief Statistic
+ * @param mode Number \n mode is the type of decrease: temporary, normal,
+ * or permanent
+ * @brief Mode
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Lose a "point"
+ * @note (see file spells2.c)
+ */
+extern bool do_dec_stat(int stat, int mode);
+
+/** @fn do_res_stat(int stat, bool full)
+ * @brief Restore the player's "stat" statistic.\n
+ * @param stat Number \n stat is the statistic.
+ * @brief Statistic
+ * @param full Boolean \n TRUE if full restore is required, otherwise FALSE.
+ * @brief Full restore flag
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Restore lost "points" in a stat
+ * @note (see file spells2.c)
+ */
+extern bool do_res_stat(int stat, bool full);
+
+/** @fn do_inc_stat(int stat)
+ * @brief Increase the player's "stat" statistic by a point.\n
+ * @param stat Number \n stat is the statistic.
+ * @brief Statistic
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Gain a "point" in a stat
+ * @note (see file spells2.c)
+ */
+extern bool do_inc_stat(int stat);
+
+/** @fn identify_pack(void)
+ * @brief Identify all items in the inventory.
+ * @note
+ * Identify everything being carried.\n
+ * Done by a potion of "self knowledge".
+ * @note (see file spells2.c)
+ */
+extern void identify_pack(void);
+
+/** @fn remove_curse(void)
+ * @brief Remove all curses except for heavy curses.
+ * @return Boolean \n TRUE if at least one item was uncursed, otherwise FALSE.
+ * @note
+ * Remove most curses\n\n
+ * There is a 1 in (55 - level) chance of reversing the curse effects for
+ * items which are not artifacts. For example, a Ring of Damage (-15) will
+ * become a Ring of Damage (+15).
+ * @note (see file spells2.c)
+ */
+extern bool remove_curse(void);
+
+/** @fn remove_all_curse(void)
+ * @brief Remove all curses including heavy curses.
+ * @return Boolean \n TRUE if at least one item was uncursed, otherwise FALSE.
+ * @note
+ * Remove all curses\n\n
+ * There is a 1 in (55 - level) chance of reversing the curse effects for
+ * items which are not artifacts. For example, a Ring of Damage (-15) will
+ * become a Ring of Damage (+15).
+ * @note (see file spells2.c)
+ */
+extern bool remove_all_curse(void);
+
+/** @fn restore_level(void)
+ * @brief Restore all drained experience points (if any).
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Restores any drained experience
+ * @note (see file spells2.c)
+ */
+extern bool restore_level(void);
+
+/** @fn self_knowledge(FILE *fff=NULL)
+ * @brief Show all attributes including racial powers, mutations, and
+ * equipment effects.\n
+ * @param *fff FILE \n write info to screen if fff is NULL,
+ * otherwise write info to file fff.
+ * @brief Output file
+ * @note
+ * self-knowledge... idea from nethack. Useful for determining powers and
+ * resistances of items. It saves the screen, clears it, then starts listing
+ * attributes, a screenful at a time. (There are a LOT of attributes to
+ * list. It will probably take 2 or 3 screens for a powerful character whose
+ * using several artifacts...) -CFT\n\n
+ * It is now a lot more efficient. -BEN-\n\n
+ * See also "identify_fully()".\n\n
+ * XXX XXX XXX Use the "show_file()" method, perhaps.
+ * @note (see file spells2.c)
+ */
+extern void self_knowledge(FILE *fff=NULL);
+
+/** @fn lose_all_info(void)
+ * @brief Forget about objects and the map.
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Forget everything
+ * @note (see file spells2.c)
+ */
+extern bool lose_all_info(void);
+
+/** @fn detect_traps(int rad)
+ * @brief Detect all traps within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE (always).
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if traps are detected.
+ * @note (see file spells2.c)
+ */
+extern bool detect_traps(int rad);
+
+/** @fn detect_doors(int rad)
+ * @brief Detect all doors within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if doors were detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if doors are detected.
+ * @note (see file spells2.c)
+ */
+extern bool detect_doors(int rad);
+
+/** @fn detect_stairs(int rad)
+ * @brief Detect all exits within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if exits were detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if exits are detected. Exits can be stairs,
+ * shafts, and ways out.
+ * @note (see file spells2.c)
+ */
+extern bool detect_stairs(int rad);
+
+/** @fn detect_treasure(int rad)
+ * @brief Detect all buried treasure within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if buried treasure was detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if buried treasure is detected. Treasure can be
+ * buried in magma, quartz, or sandwall.
+ * @note (see file spells2.c)
+ */
+extern bool detect_treasure(int rad);
+
+/** @var hack_no_detect_message
+ * @brief Boolean
+ * @note Suppress messages generated by "detect" spells?
+ */
+extern bool hack_no_detect_message;
+
+/** @fn detect_objects_gold(int rad)
+ * @brief Detect all gold within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if gold was detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if gold is detected. Gold can be coins or mimics.
+ * Monsters of type "$" are detected but not shown or reported.
+ * @note (see file spells2.c)
+ */
+extern bool detect_objects_gold(int rad);
+
+/** @fn detect_objects_normal(int rad)
+ * @brief Detect all normal (not gold) items within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if normal items were detected, otherwise FALSE.
+ * @note
+ * All grids within the radius are searched.\n
+ * A message is displayed if normal items are detected. Items include mimics.
+ * Monsters of type "!=?|" are detected but not shown or reported.
+ * @note (see file spells2.c)
+ */
+extern bool detect_objects_normal(int rad);
+
+/** @fn detect_objects_magic(int rad)
+ * @brief Detect all magic (not gold) items within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if magic items were detected, otherwise FALSE.
+ * @note
+ * This will light up all spaces with "magic" items, including artifacts,
+ * ego-items, potions, scrolls, books, rods, wands, staves, amulets, rings,
+ * and "enchanted" items of the "good" variety.\n\n
+ * It can probably be argued that this function is now too powerful.\n\n
+ * All grids within the radius are searched.\n
+ * A message is displayed if magic items are detected. Items include mimics.
+ * @note (see file spells2.c)
+ */
+extern bool detect_objects_magic(int rad);
+
+/** @fn detect_monsters_normal(int rad)
+ * @brief Detect all non-invisible monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if non-invisible monsters were detected,
+ * otherwise FALSE.
+ * @note
+ * A non-invisible monster is one which is visible, or one which is invisible
+ * but the player can see invisible monsters.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_normal(int rad);
+
+/** @fn detect_monsters_invis(int rad)
+ * @brief Detect all invisible monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if invisible monsters were detected,
+ * otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_invis(int rad);
+
+/** @fn detect_monsters_evil(int rad)
+ * @brief Detect all evil monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if evil monsters were detected, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_evil(int rad);
+
+/** @fn detect_monsters_good(int rad)
+ * @brief Detect all good monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if good monsters were detected, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_good(int rad);
+
+/** @fn detect_monsters_xxx(u32b match_flag, int rad)
+ * @brief Detect all monsters with flag "match_flag" within radius "rad" of the
+ * player.\n
+ * @param match_flag Number \n match_flag is the type of monster. It must be
+ * a RF3_ flag (see defines.h).
+ * @brief Match flag
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if monsters were detected, otherwise FALSE.
+ * @note
+ * A "generic" detect monsters routine, tagged to flags3\n\n
+ * This routine will work with ANY RF3 flag, but messages will only be
+ * printed if the following monsters are detected: demon, undead, good.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_xxx(u32b match_flag, int rad);
+
+/** @fn detect_monsters_string(cptr chars, int rad)
+ * @brief Detect all monsters whose monster symbol is in "chars" within
+ * radius "rad" of the player.\n
+ * @param chars String \n chars is the string of monster types. For
+ * available characters, see the "symbol" field of the graphics (G)
+ * line of r_info.txt.
+ * @brief Symbols
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if monsters were detected, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_string(cptr chars, int rad);
+
+/** @fn detect_monsters_nonliving(int rad)
+ * @brief Detect all nonliving monsters within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if nonliving monsters were detected,
+ * otherwise FALSE.
+ * @note
+ * Detect all "nonliving", "undead" or "demonic" monsters on current panel\n\n
+ * Nonliving monsters are either RF3_NONLIVING, RF3_UNDEAD, or RF3_DEMON.
+ * @note (see file spells2.c)
+ */
+extern bool detect_monsters_nonliving(int rad);
+
+/** @fn detect_all(int rad)
+ * @brief Detect everything within radius "rad" of the player.\n
+ * @param rad Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @return Boolean \n TRUE if something was detected, otherwise FALSE.
+ * @note
+ * Detect everything\n\n
+ * Detects traps, doors, stairs, treasure, gold objects, normal objects,
+ * invisible monsters, non-invisible monsters.
+ */
+extern bool detect_all(int rad);
+
+/** @fn stair_creation(void)
+ * @brief Create stairs at the player location
+ * @note
+ * This is not allowed if the grid is not empty, the player is not in a
+ * dungeon, the player is on a special level, the player is in an arena
+ * or quest. If the player is in the town or wilderness the stairs will
+ * go down. If the player is on a quest level or at the bottom of a
+ * dungeon, the stairs will go up. Otherwise there is an even chance the
+ * stairs will go up or down.
+ */
+extern void stair_creation(void);
+
+/** @fn tgt_pt (int *x=0, int *y=0)
+ * @brief Set a target point\n
+ * @param *x Number
+ * @brief X-coordinate
+ * @param *y Number
+ * @brief Y-coordinate
+ * @return *x Number \n X-coordinate of target.
+ * @return *y Number \n Y-coordinate of target.
+ * @return Boolean \n True if a target was selected, otherwise FALSE.
+ * @note
+ * Allow the user to move the cursor around the screen to select a target.
+ * The user must press the space key to set the target.
+ * @note (see file xtra2.c)
+ */
+extern bool tgt_pt (int *x=0, int *y=0);
+
+/** @fn wall_stone(int y, int x)
+ * @brief Create a stone wall at dungeon grid ("y", "x").\n
+ * @param y Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool wall_stone(int y, int x);
+
+/** @fn create_artifact(object_type *o_ptr, bool a_scroll, bool get_name)
+ * @brief Create an artifact from object "o_ptr".\n
+ * @param *o_ptr object_type \n object to become an artifact
+ * @brief Object
+ * @param a_scroll Boolean \n Is a scroll used to create the artifact?\n
+ * TRUE if the artifact is created by reading a scroll.
+ * @brief Use scroll?
+ * @param get_name Boolean \n Get a name for the artifact?\n
+ * TRUE if the artifact is to be named by the player (if a_scroll is true) or
+ * created randomly (a_scroll is false), or FALSE if an inscription is used.
+ * @brief Get name?
+ * @return *o_ptr object_type \n The artifact.
+ * @return Boolean \n TRUE (always).
+ * @note (see file randart.c)
+ */
+extern bool create_artifact(object_type *o_ptr, bool a_scroll, bool get_name);
+
+/** @fn wall_to_mud(int dir)
+ * @brief Cast a wall-to-mud spell in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool wall_to_mud(int dir);
+
+/** @fn ident_spell(void)
+ * @brief Identify an object in the inventory (or on the floor).
+ * @return Boolean \n TRUE if object is identified, otherwise FALSE.
+ * @note
+ * Identify an object in the inventory (or on the floor).
+ * This routine does *not* automatically combine objects.
+ * @note (see file spells2.c)
+ */
+extern bool ident_spell(void);
+
+/** @fn identify_fully(void)
+ * @brief Fully "identify" an object in the inventory (or on the floor).
+ * @return Boolean \n TRUE if object is identified, otherwise FALSE.
+ * @note
+ * Fully "identify" an object in the inventory -BEN-
+ * @note (see file spells2.c)
+ */
+extern bool identify_fully(void);
+
+/** @fn recharge(int num)
+ * @brief Recharge an object in the inventory (or on the floor) with "num"
+ * power.\n
+ * @param num Number \n num is the power used in recharging. It is compared
+ * to the object's level to determine whether the item is recharged
+ * successfully or destroyed. If it is recharged, it also determines
+ * how many charges are added, or how much recharge time is reduced.
+ * @brief Power
+ * @return Boolean \n TRUE if something was recharged, otherwise FALSE.
+ * @note
+ * Recharge a wand/staff/rod from the pack or on the floor.\n
+ * This function has been rewritten in Oangband. -LM-\n\n
+ * Mage -- Recharge I --> recharge(90)\n
+ * Mage -- Recharge II --> recharge(150)\n
+ * Mage -- Recharge III --> recharge(220)\n\n
+ * Priest or Necromancer -- Recharge --> recharge(140)\n
+ * Scroll of recharging --> recharge(130)\n
+ * Scroll of *recharging* --> recharge(200)\n\n
+ * It is harder to recharge high level, and highly charged wands,
+ * staffs, and rods. The more wands in a stack, the more easily and
+ * strongly they recharge. Staffs, however, each get fewer charges if
+ * stacked.\n\n
+ * XXX XXX XXX Beware of "sliding index errors".
+ * @note (see file spells2.c)
+ */
+extern bool recharge(int num);
+
+/** @fn aggravate_monsters(int who)
+ * @brief Aggravate monsters, originating from "who".\n
+ * @param who Number \n who is the index of monster in m_list[]
+ * (1 if it is the player) which triggers the aggravation.
+ * @brief Source
+ * @note
+ * Wake up all monsters, and speed up "los" monsters.
+ * @note (see file spells2.c)
+ */
+extern void aggravate_monsters(int who);
+
+/** @fn genocide_aux(bool player_cast, char typ)
+ * @brief Genocide a monster race.\n
+ * @param player_cast Boolean \n player_cast is true if the player cast the
+ * spell so the player can take damage.
+ * @param typ Char \n typ is the letetr of the genocided monsters
+ * @return Boolean \n TRUE if genocide was cast, otherwise FALSE.
+ * @note
+ * Genocide will not work on DF2_NO_GENO dungeon levels, or on "fated to
+ * die" levels.
+ * The player gets 4 points of damage per monster genocided.
+ * @note (see file spells2.c)
+ */
+extern bool genocide_aux(bool player_cast, char typ);
+
+/** @fn genocide(bool player_cast)
+ * @brief Genocide a monster race.\n
+ * @param player_cast Boolean \n player_cast is true if the player cast the
+ * spell so the player can take damage.
+ * @brief Player cast spell?
+ * @return Boolean \n TRUE if genocide was cast, otherwise FALSE.
+ * @note
+ * Genocide will not work on DF2_NO_GENO dungeon levels, or on "fated to
+ * die" levels.
+ * The player gets 4 points of damage per monster genocided.
+ * @note (see file spells2.c)
+ */
+extern bool genocide(bool player_cast);
+
+/** @fn mass_genocide(bool player_cast)
+ * @brief Delete all nearby (non-unique) monsters.\n
+ * @param player_cast Boolean \n player_cast is true if the player cast the
+ * spell so the player can take damage.
+ * @brief Player cast spell?
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Genocide will not work on DF2_NO_GENO dungeon levels, or on "fated to
+ * die" levels.\n
+ * The player gets 3 points of damage per monster genocided.
+ * @note (see file spells2.c)
+ */
+extern bool mass_genocide(bool player_cast);
+
+/** @fn probing(void)
+ * @brief Probe all nearby monsters.
+ * @return Boolean \n TRUE if probe was successful, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool probing(void);
+
+/** @fn banish_evil(int dist)
+ * @brief Banish nearby evil monsters doing "dist" points of GF_AWAY_EVIL
+ * damage.\n
+ * @param dist Number \n dist is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool banish_evil(int dist);
+
+/** @fn dispel_evil(int dam)
+ * @brief Dispel nearby evil monsters doing "dam" points of GF_DISP_EVIL
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_evil(int dam);
+
+/** @fn dispel_good(int dam)
+ * @brief Dispel nearby good monsters doing "dam" points of GF_DISP_GOOD
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_good(int dam);
+
+/** @fn dispel_undead(int dam)
+ * @brief Dispel nearby undead monsters doing "dam" points of GF_DISP_UNDEAD
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_undead(int dam);
+
+/** @fn dispel_monsters(int dam)
+ * @brief Dispel all nearby monsters doing "dam" points of GF_DISP_ALL
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_monsters(int dam);
+
+/** @fn dispel_living(int dam)
+ * @brief Dispel nearby living monsters doing "dam" points of GF_DISP_LIVING
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_living(int dam);
+
+/** @fn dispel_demons(int dam)
+ * @brief Dispel nearby demon monsters doing "dam" points of GF_DISP_DEMON
+ * damage.\n
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool dispel_demons(int dam);
+
+/** @fn turn_undead(void)
+ * @brief Turn nearby undead monsters doing a point of GF_TURN_UNDEAD damage
+ * for each player level.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool turn_undead(void);
+
+/** @fn door_creation(void)
+ * @brief Create doors in all grids adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool door_creation(void);
+
+/** @fn trap_creation(void)
+ * @brief Create traps in all grids adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool trap_creation(void);
+
+/** @fn glyph_creation(void)
+ * @brief Create glyphs in all grids adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool glyph_creation(void);
+
+/** @fn wipe(int y1, int x1, int r)
+ * @brief Delete monsters and objects from an area of the dungeon centred at
+ * grid "y1,x1" for a radius "r".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @param r Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @note
+ * Wipe -- Empties a part of the dungeon\n\n
+ * This does not work on special levels or quests. The player may become
+ * blinded. The player forgets the affected area and it becomes dark.
+ * All grids become floor.
+ * @note (see file spells2.c)
+ */
+extern void wipe(int y1, int x1, int r);
+
+/** @fn destroy_area(int y1, int x1, int r, bool full, bool bypass)
+ * @brief Delete monsters and objects from an area of the dungeon centred at
+ * grid "y1,x1" for a radius "r".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @param r Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @param full Boolean \n unused
+ * @brief *Unused*
+ * @param bypass Boolean \n TRUE if quest levels are not destroyed, otherwise
+ * FALSE.
+ * @brief Exempt quest levels?
+ * @note
+ * The spell of destruction\n\n
+ * This spell "deletes" monsters (instead of "killing" them).\n\n
+ * Later we may use one function for both "destruction" and
+ * "earthquake" by using the "full" to select "destruction".\n\n
+ * This does not work on special levels. This does not work on quests if the
+ * bypass flag is set. The epicentre is NOT affected. The player may become
+ * blinded. The player forgets the affected area and it becomes dark. The
+ * grids can become granite, quartz, magma, or floor.
+ * @note (see file spells2.c)
+ */
+extern void destroy_area(int y1, int x1, int r, bool full, bool bypass);
+
+/** @fn earthquake(int cy, int cx, int r)
+ * @brief Create an earthquake centred on grid "cy,cx" with a radius of "r".\n
+ * @param cy Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param cx Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @param r Number \n rad is the radius of circle of detection.
+ * @brief Radius
+ * @note
+ * Induce an "earthquake" of the given radius at the given location.\n\n
+ * This will turn some walls into floors and some floors into walls.\n\n
+ * The player will take damage and "jump" into a safe grid if possible,
+ * otherwise, he will "tunnel" through the rubble instantaneously.\n\n
+ * Monsters will take damage, and "jump" into a safe grid if possible,
+ * otherwise they will be "buried" in the rubble, disappearing from
+ * the level in the same way that they do when genocided.\n\n
+ * Note that thus the player and monsters (except eaters of walls and
+ * passers through walls) will never occupy the same grid as a wall.
+ * Note that as of now (2.7.8) no monster may occupy a "wall" grid, even
+ * for a single turn, unless that monster can pass_walls or kill_walls.
+ * This has allowed massive simplification of the "monster" code.\n\n
+ * This does not work on quest levels. The epicentre is NOT affected.
+ * Only about 15% of the grids are affected. The player takes 300 points
+ * of damage if they can't be moved to a safe grid, otherwise damage is
+ * from 10 to 40 points. The player forgets the affected area and it
+ * becomes dark. The grids can become granite, quartz, magma, or floor.
+ * @note (see file spells2.c)
+ */
+extern void earthquake(int cy, int cx, int r);
+
+/** @fn lite_room(int y1, int x1)
+ * @brief Lite room containing grid "y1,x1".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @note
+ * Illuminate any room containing the given location.
+ * @note (see file spells2.c)
+ */
+extern void lite_room(int y1, int x1);
+
+/** @fn unlite_room(int y1, int x1)
+ * @brief Unlite room containing grid "y1,x1".\n
+ * @param y1 Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x1 Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @note
+ * Darken all rooms containing the given location.
+ * @note (see file spells2.c)
+ */
+extern void unlite_room(int y1, int x1);
+
+/** @fn lite_area(int dam, int rad)
+ * @brief Lite area around player of radius "rad" causing "dam" points of
+ * damage to monsters.
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is the radius of circle of lite.
+ * @brief Radius
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Hack -- call light around the player\n
+ * Affect all monsters in the projection radius\n\n
+ * Generate a ball of spell type GF_LITE_WEAK.\n
+ * @note (see file spells2.c)
+ */
+extern bool lite_area(int dam, int rad);
+
+/** @fn unlite_area(int dam, int rad)
+ * @brief Unlite area around player of radius "rad" causing "dam" points of
+ * damage to monsters.
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is the radius of circle of lite.
+ * @brief Radius
+ * @return Boolean \n TRUE (always).
+ * @note
+ * Hack -- call darkness around the player\n
+ * Affect all monsters in the projection radius\n\n
+ * Generate a ball of spell type GF_DARK_WEAK.\n
+ * @note (see file spells2.c)
+ */
+extern bool unlite_area(int dam, int rad);
+
+/** @fn fire_ball_beam(int typ, int dir, int dam, int rad)
+ * @brief Generate a ball spell of type "typ" with radius "rad" aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a ball-beamed spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * Any radius >16 is treated as 16.
+ * @note (see file spells2.c)
+ */
+extern bool fire_ball_beam(int typ, int dir, int dam, int rad);
+
+/** @fn make_wish(void)
+ * @brief Allow the player to make a wish.
+ * @note (see file xtra2.c)
+ */
+extern void make_wish(void);
+
+/** @fn fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff)
+ * @brief Generate a ball spell of type "typ" with radius "rad" and effect
+ * "eff" lasting "time" turns aimed in direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @param time Number \n time is the number of turns the spell lasts.
+ * @brief Duration
+ * @param eff Number \n eff is the spell effect (EFF field)
+ * @brief Effect
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a wave spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * Any radius >16 is treated as 16.
+ * @note (see file spells2.c)
+ */
+extern bool fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff);
+
+/** @name Spell Effect Flags
+ * @brief Effect of spell
+ * @{ */
+/** @def EFF_WAVE
+ * @note A circle whose radius increase
+ */
+#define EFF_WAVE 0x00000001
+
+/** @def EFF_LAST
+ * @note The wave lasts
+ */
+#define EFF_LAST 0x00000002
+
+/** @def EFF_STORM
+ * @note the effect follows the player
+ */
+#define EFF_STORM 0x00000004
+
+/** @name Spell Effect Direction Flags
+ * @brief Direction of the spell
+ * @{ */
+#define EFF_DIR1 0x00000008 /* Directed effect */
+#define EFF_DIR2 0x00000010 /* Directed effect */
+#define EFF_DIR3 0x00000020 /* Directed effect */
+#define EFF_DIR4 0x00000040 /* Directed effect */
+#define EFF_DIR6 0x00000080 /* Directed effect */
+#define EFF_DIR7 0x00000100 /* Directed effect */
+#define EFF_DIR8 0x00000200 /* Directed effect */
+#define EFF_DIR9 0x00000400 /* Directed effect */
+/** @} */
+/** @} */
+
+/** @fn fire_cloud(int typ, int dir, int dam, int rad, int time)
+ * @brief Generate a ball spell of type "typ" with radius "rad" lasting
+ * "time" turns aimed in direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @param time Number \n time is the number of turns the spell lasts.
+ * @brief Duration
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a cloud spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * Any radius >16 is treated as 16.
+ * @note (see file spells2.c)
+ */
+extern bool fire_cloud(int typ, int dir, int dam, int rad, int time);
+
+/** @fn fire_wall(int typ, int dir, int dam, int time)
+ * @brief Generate a beam spell of type "typ" lasting "time" turns aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param time Number \n time is the number of turns the spell lasts.
+ * @brief Duration
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a persistant beam spell\n
+ * Pass through monsters, as a "beam"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_wall(int typ, int dir, int dam, int time);
+
+/** @fn fire_ball(int typ, int dir, int dam, int rad)
+ * @brief Generate a ball spell of type "typ" with radius "rad" aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a ball spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_ball(int typ, int dir, int dam, int rad);
+
+/** @fn fire_bolt(int typ, int dir, int dam)
+ * @brief Generate a bolt spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a bolt spell\n
+ * Stop if we hit a monster, as a "bolt"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_bolt(int typ, int dir, int dam);
+
+/** @fn fire_beam(int typ, int dir, int dam)
+ * @brief Generate a beam spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a beam spell\n
+ * Pass through monsters, as a "beam"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool fire_beam(int typ, int dir, int dam);
+
+/** @fn fire_druid_ball(int typ, int dir, int dam, int rad)
+ * @brief Generate a druid ball spell of type "typ" with radius "rad" aimed in
+ * direction "dir" for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param rad Number \n rad is 0 for a beam/bolt and 1-16 for a ball.
+ * @brief Radius
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a druidistic ball spell\n
+ * Stop if we hit a monster, act as a "ball"\n
+ * Allow "target" mode to pass over monsters\n
+ * Affect grids, objects, and monsters\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * The spells follows a mana path\n\n
+ * WARNING: This routine has been deprecated.
+ * @note (see file spells2.c)
+ */
+extern bool fire_druid_ball(int typ, int dir, int dam, int rad);
+
+/** @fn fire_druid_bolt(int typ, int dir, int dam)
+ * @brief Generate a druid bolt spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a druidistic bolt spell\n
+ * Stop if we hit a monster, as a "bolt"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * The spells follows a mana path\n\n
+ * WARNING: This routine has been deprecated.
+ * @note (see file spells2.c)
+ */
+extern bool fire_druid_bolt(int typ, int dir, int dam);
+
+/** @fn fire_druid_beam(int typ, int dir, int dam)
+ * @brief Generate a druid beam spell of type "typ" aimed in direction "dir"
+ * for "dam" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a druidistic beam spell\n
+ * Pass through monsters, as a "beam"\n
+ * Affect monsters (not grids or objects)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * The spells follows a mana path\n\n
+ * WARNING: This routine has been deprecated.
+ * @note (see file spells2.c)
+ */
+extern bool fire_druid_beam(int typ, int dir, int dam);
+
+/** @fn fire_bolt_or_beam(int prob, int typ, int dir, int dam)
+ * @brief Generate a bolt spell of type "typ" aimed in direction "dir"
+ * for "dam" damage with "prob" percent chance of a beam.\n
+ * @param prob Number \n prob is the percentage chance the spell will be a
+ * beam instead of a bolt.
+ * @brief Beam probability percentage
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Cast a bolt spell, or rarely, a beam spell\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".\n\n
+ * @note (see file spells2.c)
+ */
+extern bool fire_bolt_or_beam(int prob, int typ, int dir, int dam);
+
+/** @fn alchemy(void)
+ * @brief Turns an object into gold, gain some of its value in a shop
+ * @return Boolean \n TRUE if object turns to gold, otherwise FALSE.
+ * @note
+ * The player selects an object (and quantity if it applies) from the
+ * inventory or the floor and attempts to turn it into gold. If the
+ * price of the item is < 0 then the player gains nothing (fool's gold),
+ * otherwise the player gets a third of the price in gold. Artifacts are
+ * not affected.
+ * @note (see file spells2.c)
+ */
+extern bool alchemy(void);
+
+/** @fn alter_reality(void)
+ * @brief The player leaves the level immediately.
+ * @note (see file spells2.c)
+ */
+extern void alter_reality(void);
+
+/** @fn swap_position(int lty, int ltx)
+ * @brief Swap the position of the player with whatever is in grid "lty,ltx".\n
+ * @param lty Number \n Y-coordinate of target location.
+ * @brief Y-coordinate
+ * @param ltx Number \n X-coordinate of target location.
+ * @brief X-coordinate
+ * @note
+ * Player moves to target location. If there is a monster at the target
+ * location, it is moved to the player location. This is not allowed if
+ * the space-time continuum can not be disrupted.
+ * @note (see file spells2.c)
+ */
+extern void swap_position(int lty, int ltx);
+
+/** @fn teleport_swap(int dir)
+ * @brief Player swaps places with target in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @note
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, the target is the grid adjacent to the player in direction
+ * "dir".\n\n
+ * The target must be a monster. It will not work if the space-time continuum
+ * can not be disrupted or if the monster resists teleportation.
+ * @note (see file spells2.c)
+ */
+extern void teleport_swap(int dir);
+
+/** @fn project_meteor(int radius, int typ, int dam, u32b flg)
+ * @brief Generate from "radius" to ("radius" x2) ball spells with properties
+ * "flg" of type "typ" for "dam" damage.\n
+ * @param radius Number \n rad is the minimum number of balls created.
+ * rad + randint("rad") balls are created.
+ * @brief Balls
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param flg Number \n flg is the projection effect (PROJECT field).
+ * @brief Properties flag
+ * @note
+ * Apply a "project()" a la meteor shower\n\n
+ * Each ball has a radius of 2 grids. Each target grid is within 5 grids of
+ * the player.
+ * @note (see file spells2.c)
+ */
+extern void project_meteor(int radius, int typ, int dam, u32b flg);
+
+/** @fn passwall(int dir, bool safe)
+ * @brief Move the player through walls in direction "dir", to a "safe"
+ * location.\n
+ * @param dir Number \n dir must be a value from 0 to 9. It can not be 5.
+ * @brief Direction
+ * @param safe Boolean \n TRUE if location must be a safe one, otherwise FALSE.
+ * @brief Safe location?
+ * @return Boolean \n TRUE if move was successful, otherwise FALSE.
+ * @note
+ * Send the player shooting through walls in the given direction until
+ * they reach a non-wall space, or a monster, or a permanent wall.\n\n
+ * If the player ends up in a wall, they take 10d8 damage and the wall is
+ * replaced by a floor.\n\n
+ * This does not work in the wilderness, on quest levels, or if teleport is
+ * not allowed. Stopping on monsters or inside vaults is not allowed.
+ * @note (see file spells2.c)
+ */
+extern bool passwall(int dir, bool safe);
+
+/** @fn project_hook(int typ, int dir, int dam, int flg)
+ * @brief Generate a bolt/beam with properties "flg" in direction "dir" for
+ * "dam" points of "typ" damage.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @param flg Number \n flg is the projection effect (PROJECT field).
+ * @brief Properties flag
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Hack -- apply a "projection()" in a direction (or at the target)\n\n
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool project_hook(int typ, int dir, int dam, int flg);
+
+/** @fn wizard_lock(int dir)
+ * @brief Cast a wizard_lock spell in direction "dir".\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * If direction is 5 and a target has been selected, then the target is used.
+ * Otherwise, a target is calculated based on a distance of 999 grids away
+ * from the player in direction "dir".
+ * @note (see file spells2.c)
+ */
+extern bool wizard_lock(int dir);
+
+/** @fn reset_recall(bool no_trepas_max_depth)
+ * @brief Ask the user to set a recall level in a dungeon, possibly no
+ * deeper than maximum dungeon depth.\n
+ * @param no_trepas_max_depth Boolean \n TRUE if user can select maximum
+ * dungeon depth, FALSE if user can select up to player's maximum depth
+ * in the dungeon so far.
+ * @brief Allow maximum dungeon depth?
+ * @return Boolean \n TRUE of recall level was reset, otherwise FALSE.
+ * @note
+ * Ask the user for a dungeon and appropriate level within the dungeon.\n
+ * The user can not specify a dungeon the player has not gone to yet.\n
+ * If the depth is <1, reset fails. If depth is 99 or 100, the level is set
+ * to 98.
+ * @note (see file spells2.c)
+ */
+extern bool reset_recall(bool no_trepas_max_depth);
+
+/** @fn get_aim_dir(int *dp=0)
+ * @brief Get an aiming direction from the user and store it in "dp".\n
+ * @param *dp Number
+ * @brief Direction
+ * @return *dp Number \n Aiming direction.
+ * @return Boolean \n TRUE if a valid direction was returned, otherwise FALSE.
+ * @note
+ * Get an "aiming direction" from the user.\n\n
+ * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
+ * "0" for "current target", and "-1" for "entry aborted".\n\n
+ * Note that "Force Target", if set, will pre-empt user interaction,
+ * if there is a usable target already set.\n\n
+ * Note that confusion over-rides any (explicit?) user choice.
+ * @note (see file xtra2.c)
+ */
+extern bool get_aim_dir(int *dp=0);
+
+/** @fn get_rep_dir(int *dp=0)
+ * @brief Get a movement direction from the user and store it in "dp".\n
+ * @param *dp Number
+ * @brief Direction
+ * @return *dp Number \n Movement direction.
+ * @return Boolean \n TRUE if a valid direction was returned, otherwise FALSE.
+ * @note
+ * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user,
+ * and place it into "command_dir", unless we already have one.\n\n
+ * This function should be used for all "repeatable" commands, such as
+ * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well
+ * as all commands which must reference a grid adjacent to the player,
+ * and which may not reference the grid under the player. Note that,
+ * for example, it is no longer possible to "disarm" or "open" chests
+ * in the same grid as the player.\n\n
+ * Direction "5" is illegal and will (cleanly) abort the command.\n\n
+ * This function tracks and uses the "global direction", and uses
+ * that as the "desired direction", to which "confusion" is applied.
+ * @note (see file xtra2.c)
+ */
+extern bool get_rep_dir(int *dp=0);
+
+/** @fn project_los(int typ, int dam);
+ * @brief Generate a bolt/beam for "dam" points of "typ" damage to all
+ * viewable monsters in line of sight.\n
+ * @param typ Number \n typ is the type of damage (GF field).
+ * @brief Type
+ * @param dam Number \n dam is the number of hit points of damage.
+ * @brief Damage
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note
+ * Apply a "project()" directly to all viewable monsters\n\n
+ * Note that affected monsters are NOT auto-tracked by this usage.
+ * @note (see file spells2.c)
+ */
+extern bool project_hack @ project_los(int typ, int dam);
+
+/** @fn map_area(void)
+ * @brief Map current area.
+ * @note
+ * Hack -- map the current panel (plus some) ala "magic mapping"\n\n
+ * Up to 10 grids above and below, and up to 20 grids either side of the
+ * panel are mapped.
+ * @note (see file cave.c)
+ */
+extern void map_area(void);
+
+/** @fn wiz_lite(void)
+ * @brief Lite level using "clairvoyance".
+ * @note
+ * This function "illuminates" every grid in the dungeon, memorizes all
+ * "objects", memorizes all grids as with magic mapping, and, under the
+ * standard option settings (view_perma_grids but not view_torch_grids)
+ * memorizes all floor grids too.\n\n
+ * Note that if "view_perma_grids" is not set, we do not memorize floor
+ * grids, since this would defeat the purpose of "view_perma_grids", not
+ * that anyone seems to play without this option.\n\n
+ * Note that if "view_torch_grids" is set, we do not memorize floor grids,
+ * since this would prevent the use of "view_torch_grids" as a method to
+ * keep track of what grids have been observed directly.
+ * @note (see file cave.c)
+ */
+extern void wiz_lite(void);
+
+/** @fn wiz_lite_extra(void)
+ * @brief Lite and memorize level.
+ * @note (see file cave.c)
+ */
+extern void wiz_lite_extra(void);
+
+/** @fn wiz_dark(void)
+ * @brief Forget all grids and objects.
+ * @note
+ * Forget the dungeon map (ala "Thinking of Maud...").
+ * @note (see file cave.c)
+ */
+extern void wiz_dark(void);
+
+/** @fn create_between_gate(int dist, int y, int x)
+ * @brief Create a between gate at grid "y,x" or at a target grid within
+ * distance "dist" of the player.\n
+ * @param dist Number \n dist is the maximum distance from the player of the
+ * between gate.
+ * @brief Distance
+ * @param y Number \n Y-coordinate of dungeon grid.
+ * @brief Y-coordinate
+ * @param x Number \n X-coordinate of dungeon grid.
+ * @brief X-coordinate
+ * @note
+ * Creates a between gate\n\n
+ * This will fail if teleporting is not allowed on the level.\n\n
+ * If the coordinates are given, a between gate is created under the player
+ * and at the given coordinate.\n\n
+ * If there are no coordinates, a target is selected. The gate will not be
+ * created if the grid is not empty, or the grid is in a vault, or the grid
+ * is too far away. There is always a chance (1 in (Conveyance Skill *
+ * Conveyance Skill / 2)) the gate will not be created.
+ * @note (see file spells2.c)
+ */
+extern void create_between_gate(int dist, int y, int x);
+
+/** @fn destroy_doors_touch(void)
+ * @brief Destroy all doors adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool destroy_doors_touch(void);
+
+/** @fn destroy_traps_touch(void)
+ * @brief Destroy all traps adjacent to the player.
+ * @return Boolean \n TRUE if player notices, otherwise FALSE.
+ * @note (see file spells2.c)
+ */
+extern bool destroy_traps_touch(void);
+
+/** @struct magic_power
+ * @brief Innate powers
+ */
+struct magic_power
+{
+ /** @structvar min_lev
+ * @brief Number
+ */
+ int min_lev;
+
+ /** @structvar mana_cost
+ * @brief Number
+ */
+ int mana_cost;
+
+ /** @structvar fail
+ * @brief Number
+ */
+ int fail;
+
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+
+ /** @structvar desc
+ * @brief String
+ */
+ cptr desc;
+};
+
+/** @fn *new_magic_power(int num)
+ * @dgonly
+ * @brief Create a wiped array of "num" magic powers.\n
+ * @param num Number \n num is the number of magic powers.
+ * @brief Number
+ * @return magic_power \n Array of magic powers.
+ * @note
+ * Get a new magic type\n\n
+ * Note: do not call this function directly.\n
+ * Please use add_magic() in powers.lua instead.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern magic_power *new_magic_power(int num);
+
+/** @fn get_magic_power(magic_power *m_ptr, int num);
+ * @dgonly
+ * @brief Get magic power number "num" from array "m_ptr" of magic powers.\n
+ * @param *m_ptr magic_power \n m_ptr is the array of magic powers.
+ * @brief Powers
+ * @param num Number \n num is the index to the array.
+ * @brief Index
+ * @return magic_power \n A magic power.
+ * @note
+ * Note: do not call this function.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern magic_power *grab_magic_power @ get_magic_power(magic_power *m_ptr, int num);
+
+/** @fn select_magic_power(int *sn, magic_power *powers, int max_powers, char *info_fct, int plev, int cast_stat);
+ * @dgonly
+ * @brief Select a magic power from array of powers.\n
+ * @param *sn Number
+ * @brief Power (spell) number
+ * @param *powers magic_power \n powers is the array of magic powers.
+ * @brief Powers
+ * @param max_powers Number \n max_powers is the maximum number of magic
+ * powers.
+ * @brief Maximum powers
+ * @param *info_fct String \n info_fct is the name of a function which will
+ * return power info.
+ * @brief Name of power info function
+ * @param plev Number \n plev is the player's level of magic skill.
+ * @brief Player magic level
+ * @param cast_stat Number \n cast_stat is the required casting statistic
+ * (INT or WIS).
+ * @brief Casting statistic
+ * @return *sn Number \n sn is the index of the power in the array of magic
+ * powers.
+ * @return Boolean \n TRUE if power was selected, otherwise FALSE.
+ * @note
+ * Allow user to choose a magic power.\n\n
+ * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
+ * If the user hits escape, returns FALSE, and set '*sn' to -1\n
+ * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n\n
+ * The "prompt" should be "cast", "recite", or "study".
+ * The "known" should be TRUE for cast/pray, FALSE for study\n\n
+ * Note: do not call this function directly.\n
+ * Please use execute_magic() in powers.lua instead.\n
+ * By order of DG.
+ * @note (see files lua_bind.c, cmd7.c)
+ */
+extern bool get_magic_power_lua @ select_magic_power(int *sn, magic_power *powers, int max_powers, char *info_fct, int plev, int cast_stat);
+
+/** @fn magic_power_sucess(magic_power *spell, int stat, char *oups_fct=NULL);
+ * @dgonly
+ * @brief Determine if using a magic power succeeds.\n
+ * @param *spell magic_power \n Spell is the magic power the player is using.
+ * @brief Power (spell)
+ * @param stat Number \n stat is the required casting statistic (INT or WIS).
+ * @brief Casting statistic
+ * @param *oups_fct String \n oups_fct is the message displayed when the power
+ * fails.
+ * @brief Fail message
+ * @return Boolean \n TRUE if spell succeeds, otherwise FALSE.
+ * @note
+ * The chance of using a power is adjusted for player magic skill, casting
+ * statistic, player mana points, and stunning. There is always at least a
+ * 5% chance the power works.\n\n
+ * Note: do not call this function.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern bool lua_spell_success @ magic_power_sucess(magic_power *spell, int stat, char *oups_fct=NULL);
+
+/** @fn add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff)
+ * @dgonly
+ * @brief Add a new power to the array of magic powers.\n
+ * @param name String \n name is the name of the power.
+ * @brief Name
+ * @param desc String \n desc is the description of the power.
+ * @brief Description
+ * @param gain String \n gain describes the effect when the power starts
+ * working.
+ * @brief Gain message
+ * @param lose String \n loss describes the effect when the power stops
+ * working.
+ * @brief Lose message
+ * @param level Number \n level is the magic skill level a player needs to
+ * use the power.
+ * @brief Level
+ * @param cost Number \n cost is the number of mana points required to use the
+ * power.
+ * @brief Mana cost
+ * @param stat Number \n stat is the required casting statistic (INT or WIS).
+ * @brief Casting statistic
+ * @param diff Number \n diff is the difficulty.
+ * @brief Difficulty
+ * @return Number \n The index of the new power in the magic power array.
+ * @note
+ * Note: do not call this function.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+s16b add_new_power(cptr name, cptr desc, cptr gain, cptr lose, byte level, byte cost, byte stat, byte diff);
+
+/** @var power_max
+ * @brief Number
+ * @note Maximum number of innate powers.
+ */
+extern s16b power_max;
+
+/* Schools */
+
+/** @struct school_spell_type
+ * @brief Spell
+ * @note The spell function must provide the desc
+ */
+struct spell_type@school_spell_type
+{
+ /** @structvar name
+ * @brief String
+ */
+ cptr name;
+
+ /** @structvar skill_level
+ * @brief Number
+ * @note Required level (to learn)
+ */
+ byte skill_level;
+
+ /** @structvar mana
+ * @brief Number
+ * @note Required mana at lvl 1
+ */
+ byte mana;
+
+ /** @structvar mana_max
+ * @brief Number
+ * @note Required mana at max lvl
+ */
+ byte mana_max;
+
+ /** @structvar fail
+ * @brief Number
+ * @note Minimum chance of failure
+ */
+ s16b fail;
+
+ /** @structvar level
+ * @brief Number
+ * @note Spell level(0 = not learnt)
+ */
+ s16b level;
+};
+
+/** @struct school_type
+ * @brief Spell school
+ */
+struct school_type
+{
+ /** @structvar name
+ * @brief String
+ * @note Name
+ */
+ cptr name;
+ /** @structvar skill
+ * @brief Number
+ * @note Skil used for that school
+ */
+ s16b skill;
+};
+
+/** @fn new_school(int i, cptr name, s16b skill)
+ * @dgonly
+ * @brief Add school to array of schools.\n
+ * @param i Number \n i is index of school array where school is added.
+ * There is no range checking.
+ * @brief Index
+ * @param name String \n name is the name of the school.
+ * @brief Name
+ * @param skill Number \n skill is the skill of the school.
+ * @brief Skill
+ * @return Number \ The index parameter.
+ * @note
+ * Note: do not call this function directly.\n
+ * Please use add_school() in s_aux.lua instead.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s16b new_school(int i, cptr name, s16b skill);
+
+/** @fn new_spell(int i, cptr name)
+ * @dgonly
+ * @brief Add spell to array of spells for a school.\n
+ * @param i Number \n i is index of school spell array where spell is added.
+ * There is no range checking.
+ * @brief Index
+ * @param name String \n name is the name of the spell.
+ * @brief Name
+ * @return Number \ The index parameter.
+ * @note
+ * Spell level is set to zero.\n\n
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s16b new_spell(int i, cptr name);
+
+/** @fn spell(s16b num);
+ * @dgonly
+ * @brief Get spell "num" from array of spells for a school.\n
+ * @param num Number \n num is the index of the spell.
+ * There is no range checking.
+ * @brief Index
+ * @return spell_type \n The spell.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern spell_type *grab_spell_type @ spell(s16b num);
+
+/** @fn school(s16b num);
+ * @dgonly
+ * @brief Get school "num" from array of schools.\n
+ * @param num Number \n num is the index of the school.
+ * There is no range checking.
+ * @brief Index
+ * @return school_type \n The school.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern school_type *grab_school_type @ school(s16b num);
+
+/** @fn lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus)
+ * @dgonly
+ * @brief Get the casting level of school spell "s".\n
+ * @param s Number \n s is the index of the spell in array of school spells.
+ * There is no range checking.
+ * @brief Spell index
+ * @param lvl Number \n lvl represents the level of player skill.
+ * @brief Player skill level
+ * @param max Number \n max is the maximum level for the spell.
+ * @brief Maximum spell level
+ * @param min Number \n min is the minimum level for the spell.
+ * @brief Minimum spell level
+ * @param bonus Number \n bonus is any bonus to final level.
+ * @brief Bonus
+ * @return Number \n Casting level.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s32b lua_get_level(s32b s, s32b lvl, s32b max, s32b min, s32b bonus);
+
+/** @fn lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat)
+ * @dgonly
+ * @brief Get the chance a spell will fail.\n
+ * @param chance Number \n chance is the inital chance a spell will work.
+ * @brief Initial chance
+ * @param level Number \n level represents the level of player skill.
+ * @brief Player skill level
+ * @param skill_level Number \n *unused*.
+ * @brief *Unused*
+ * @param mana Number \n mana is the mana required by the spell.
+ * @brief Spell mana
+ * @param cur_mana Number \n cur_mana is the player's current mana.
+ * @brief Player mana
+ * @param stat Number \n stat is the required casting statistic (INT or WIS).
+ * @brief Casting statistic
+ * @return Number \n Chance of failure.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s32b lua_spell_chance(s32b chance, int level, int skill_level, int mana, int cur_mana, int stat);
+
+/** @fn lua_spell_device_chance(s32b chance, int level, int base_level)
+ * @dgonly
+ * @brief Get the chance a device will fail.\n
+ * @param chance Number \n chance is the inital chance a spell will work.
+ * @brief Initial chance
+ * @param level Number \n level represents the level of player skill.
+ * @brief Player skill level
+ * @param base_level Number \n *unused*
+ * @brief *Unused*
+ * @return Number \n Chance of failure.
+ * @note
+ * Note: do not call this function directly.\n
+ * By order of DG.
+ * @note (see file lua_bind.c)
+ */
+extern s32b lua_spell_device_chance(s32b chance, int level, int base_level);
+
+/** @fn get_school_spell(cptr do_what, cptr check_fct, s16b force_book)
+ * @brief Get a spell from a book.\n
+ * @param do_what String \n what the player wants to do with the spell,
+ * for example "cast" or "copy".
+ * @brief Action
+ * @param check_fct String \n check_fct is the name of a function which checks
+ * if the player has access to the spell.
+ * @brief Check function
+ * @param force_book Number \n If it is different from 0 it for'ces the use of
+ * a spellbook, bypassing spellbook selection
+ * @brief Bypass book selection
+ * @return Number \n Spell number.
+ * @note
+ * Get a spell from a book\n\n
+ * The player must have a book to select a spell. When a book is chosen, the
+ * player is given a choice of spells to select. The player must be able to
+ * access the spell.\n\n
+ * If no spell is chosen, -1 is returned.
+ * @note (see file cmd5.c)
+ */
+extern u32b get_school_spell(cptr do_what, cptr check_fct, s16b force_book);
+
+/** @name Last Teleportation
+ * @brief Coordinates of last successful teleportation
+ * @{ */
+/** @var last_teleportation_y
+ * @brief Number
+ * @note y-coordinate of last successful teleportation
+ */
+extern s16b last_teleportation_y;
+
+/** @var last_teleportation_x
+ * @brief Number
+ * @note x-coordinate of last successful teleportation
+ */
+extern s16b last_teleportation_x;
+/** @} */
+
+/** @fn get_pos_player(int dis, int *ny=0, int *nx=0)
+ * @brief Get a grid near the player.\n
+ * @param dis Number \n is the maximum distance away from the player.
+ * This is limited to 200.
+ * @brief Distance from player
+ * @return y Number \n Y-coordinate of grid.
+ * @return x Number \n X-coordinate of grid.
+ * @note
+ * This function is slightly obsessive about correctness.\n\n
+ * Minimum distance is half the maximum distance. The function attempts to
+ * find a valid grid up to 500 times. If no valid grid is found, the maximum
+ * distance is doubled (though no more than 200) and the minimum distance is
+ * halved. The function does this 100 times.
+ * @note (see file spells1.c)
+ */
+extern void get_pos_player(int dis, int *ny=0, int *nx=0);
diff --git a/src/spells1.c b/src/spells1.c
new file mode 100644
index 00000000..6a4c362f
--- /dev/null
+++ b/src/spells1.c
@@ -0,0 +1,9412 @@
+/* File: spells1.c */
+
+/* Purpose: Spell code (part 1) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/* 1/x chance of reducing stats (for elemental attacks) */
+#define HURT_CHANCE 32
+
+/* 1/x chance of hurting even if invulnerable!*/
+#define PENETRATE_INVULNERABILITY 13
+
+/* Maximum number of tries for teleporting */
+#define MAX_TRIES 100
+
+
+/*
+ * Helper function -- return a "nearby" race for polymorphing
+ *
+ * Note that this function is one of the more "dangerous" ones...
+ */
+s16b poly_r_idx(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int i, r;
+
+#if 0 /* No more -- hehehe -- DG */
+ /* Allowable range of "levels" for resulting monster */
+ int lev1 = r_ptr->level - ((randint(20) / randint(9)) + 1);
+ int lev2 = r_ptr->level + ((randint(20) / randint(9)) + 1);
+#endif
+
+ /* Hack -- Uniques never polymorph */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ return (r_idx);
+
+ /* Pick a (possibly new) non-unique race */
+ for (i = 0; i < 1000; i++)
+ {
+ /* Pick a new race, using a level calculation */
+ r = get_mon_num((dun_level + r_ptr->level) / 2 + 5);
+
+ /* Handle failure */
+ if (!r) break;
+
+ /* Obtain race */
+ r_ptr = &r_info[r];
+
+ /* Ignore unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+#if 0
+ /* Ignore monsters with incompatible levels */
+ if ((r_ptr->level < lev1) || (r_ptr->level > lev2)) continue;
+#endif
+
+ /* Use that index */
+ r_idx = r;
+
+ /* Done */
+ break;
+ }
+
+ /* Result */
+ return (r_idx);
+}
+
+/*
+ * Teleport player, using a distance and a direction as a rough guide.
+ *
+ * This function is not at all obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+void teleport_player_directed(int rad, int dir)
+{
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+ int yfoo = ddy[dir];
+ int xfoo = ddx[dir];
+ int min = rad / 4;
+ int dis = rad;
+ int i, d;
+ bool look = TRUE;
+ bool y_major = FALSE;
+ bool x_major = FALSE;
+ int y_neg = 1;
+ int x_neg = 1;
+ cave_type *c_ptr;
+
+ if (xfoo == 0 && yfoo == 0)
+ {
+ teleport_player(rad);
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (yfoo == 0) x_major = TRUE;
+ if (xfoo == 0) y_major = TRUE;
+ if (yfoo < 0) y_neg = -1;
+ if (xfoo < 0) x_neg = -1;
+
+ /* Look until done */
+ while (look)
+ {
+ /* Verify max distance */
+ if (dis > 200)
+ {
+ teleport_player(rad);
+ return;
+ }
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ if (y_major)
+ {
+ y = rand_spread(p_ptr->py + y_neg * dis / 2, dis / 2);
+ }
+ else
+ {
+ y = rand_spread(p_ptr->py, dis / 3);
+ }
+
+ if (x_major)
+ {
+ x = rand_spread(p_ptr->px + x_neg * dis / 2, dis / 2);
+ }
+ else
+ {
+ x = rand_spread(p_ptr->px, dis / 3);
+ }
+
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Move player */
+ teleport_player_to(y, x);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+
+ c_ptr = &cave[y][x];
+
+ /* Hack -- enter a store if we are on one */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Hack -- enter store */
+ command_new = '_';
+ }
+}
+
+
+/*
+ * Teleport a monster, normally up to "dis" grids away.
+ *
+ * Attempt to move the monster at least "dis/2" grids away.
+ *
+ * But allow variation to prevent infinite loops.
+ */
+void teleport_away(int m_idx, int dis)
+{
+ int ny = 0, nx = 0, oy, ox, d, i, min;
+ int tries = 0;
+
+ bool look = TRUE;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Paranoia */
+ if (!m_ptr->r_idx) return;
+
+ /* Save the old location */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ ny = rand_spread(oy, dis);
+ nx = rand_spread(ox, dis);
+ d = distance(oy, ox, ny, nx);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Require "empty" floor space */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ /* Hack -- no teleport onto glyph of warding */
+ if (cave[ny][nx].feat == FEAT_GLYPH) continue;
+ if (cave[ny][nx].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* ...nor onto the Pattern */
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* No teleporting into vaults and such */
+ if (!(p_ptr->inside_quest || p_ptr->inside_arena))
+ if (cave[ny][nx].info & (CAVE_ICKY)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ /* Stop after MAX_TRIES tries */
+ if (tries > MAX_TRIES) return;
+ }
+
+ /* Sound */
+ sound(SOUND_TPOTHER);
+
+ /* Update the new location */
+ cave[ny][nx].m_idx = m_idx;
+ last_teleportation_y = ny;
+ last_teleportation_x = nx;
+
+ /* Update the old location */
+ cave[oy][ox].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = ny;
+ m_ptr->fx = nx;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(oy, ox);
+
+ /* Redraw the new grid */
+ lite_spot(ny, nx);
+
+ /* Update monster light */
+ if (r_ptr->flags9 & RF9_HAS_LITE) p_ptr->update |= (PU_MON_LITE);
+}
+
+
+/*
+ * Teleport monster next to the player
+ */
+void teleport_to_player(int m_idx)
+{
+ int ny = 0, nx = 0, oy, ox, d, i, min;
+ int dis = 2;
+
+ bool look = TRUE;
+
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ int attempts = 500;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Paranoia */
+ if (!m_ptr->r_idx) return;
+
+ /* "Skill" test */
+ if (randint(100) > m_ptr->level) return;
+
+ /* Save the old location */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look && --attempts)
+ {
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ ny = rand_spread(p_ptr->py, dis);
+ nx = rand_spread(p_ptr->px, dis);
+ d = distance(p_ptr->py, p_ptr->px, ny, nx);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Require "empty" floor space */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ /* Hack -- no teleport onto glyph of warding */
+ if (cave[ny][nx].feat == FEAT_GLYPH) continue;
+ if (cave[ny][nx].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* ...nor onto the Pattern */
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* No teleporting into vaults and such */
+ /* if (cave[ny][nx].info & (CAVE_ICKY)) continue; */
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+ }
+
+ if (attempts < 1) return;
+
+ /* Sound */
+ sound(SOUND_TPOTHER);
+
+ /* Update the new location */
+ cave[ny][nx].m_idx = m_idx;
+ last_teleportation_y = ny;
+ last_teleportation_x = nx;
+
+ /* Update the old location */
+ cave[oy][ox].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = ny;
+ m_ptr->fx = nx;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(oy, ox);
+
+ /* Redraw the new grid */
+ lite_spot(ny, nx);
+
+ /* Update monster light */
+ if (r_ptr->flags9 & RF9_HAS_LITE) p_ptr->update |= (PU_MON_LITE);
+}
+
+
+/*
+ * Teleport the player to a location up to "dis" grids away.
+ *
+ * If no such spaces are readily available, the distance may increase.
+ * Try very hard to move the player at least a quarter that distance.
+ */
+/* It'd be better if this was made an argument ... */
+bool teleport_player_bypass = FALSE;
+
+void teleport_player(int dis)
+{
+ int d, i, min, ox, oy, x = 0, y = 0;
+ int tries = 0;
+
+ int xx = -1, yy = -1;
+
+ bool look = TRUE;
+
+ if (p_ptr->resist_continuum && (!teleport_player_bypass))
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if (p_ptr->wild_mode) return;
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (p_ptr->anti_tele && (!teleport_player_bypass))
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if ((dungeon_flags2 & DF2_NO_TELEPORT) && (!teleport_player_bypass))
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+
+
+ if (dis > 200) dis = 200; /* To be on the safe side... */
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ y = rand_spread(p_ptr->py, dis);
+ x = rand_spread(p_ptr->px, dis);
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* No teleporting into vaults and such */
+ if (cave[y][x].info & (CAVE_ICKY)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ /* Stop after MAX_TRIES tries */
+ if (tries > MAX_TRIES) return;
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Save the old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ last_teleportation_y = y;
+ last_teleportation_x = x;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ while (xx < 2)
+ {
+ yy = -1;
+
+ while (yy < 2)
+ {
+ if (xx == 0 && yy == 0)
+ {
+ /* Do nothing */
+ }
+ else
+ {
+ if (cave[oy + yy][ox + xx].m_idx)
+ {
+ monster_race *r_ptr = race_inf(&m_list[cave[oy + yy][ox + xx].m_idx]);
+
+ if ((r_ptr->flags6
+ & RF6_TPORT) &&
+ !(r_ptr->flags3
+ & RF3_RES_TELE))
+ /*
+ * The latter limitation is to avoid
+ * totally unkillable suckers...
+ */
+ {
+ if (!(m_list[cave[oy + yy][ox + xx].m_idx].csleep))
+ teleport_to_player(cave[oy + yy][ox + xx].m_idx);
+ }
+ }
+ }
+ yy++;
+ }
+ xx++;
+ }
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+}
+
+
+/*
+ * get a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ */
+void get_pos_player(int dis, int *ny, int *nx)
+{
+ int d, i, min, x = 0, y = 0;
+ int tries = 0;
+
+ bool look = TRUE;
+
+ if (dis > 200) dis = 200; /* To be on the safe side... */
+
+ /* Minimum distance */
+ min = dis / 2;
+
+ /* Look until done */
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ y = rand_spread(p_ptr->py, dis);
+ x = rand_spread(p_ptr->px, dis);
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* No teleporting into vaults and such */
+ if (cave[y][x].info & (CAVE_ICKY)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ /* Stop after MAX_TRIES tries */
+ if (tries > MAX_TRIES) return;
+ }
+
+ *ny = y;
+ *nx = x;
+}
+
+/*
+ * Teleport a monster to a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ */
+void teleport_monster_to(int m_idx, int ny, int nx)
+{
+ int y, x, oy, ox, dis = 0, ctr = 0;
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ /* Find a usable location */
+ while (1)
+ {
+ /* Pick a nearby legal location */
+ while (1)
+ {
+ y = rand_spread(ny, dis);
+ x = rand_spread(nx, dis);
+ if (in_bounds(y, x)) break;
+ }
+
+ /* Not on the player's grid */
+ /* Accept "naked" floor grids */
+ if (cave_naked_bold(y, x) && (y != p_ptr->py) && (x != p_ptr->px)) break;
+
+ /* Occasionally advance the distance */
+ if (++ctr > (4 * dis * dis + 4 * dis + 1))
+ {
+ ctr = 0;
+ dis++;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_TPOTHER);
+
+ /* Save the old position */
+ oy = m_ptr->fy;
+ ox = m_ptr->fx;
+ cave[oy][ox].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = y;
+ m_ptr->fx = x;
+ cave[y][x].m_idx = m_idx;
+ last_teleportation_y = y;
+ last_teleportation_x = x;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(m_ptr->fy, m_ptr->fx);
+}
+
+
+/*
+ * Teleport player to a grid near the given location
+ *
+ * This function is slightly obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+void teleport_player_to(int ny, int nx)
+{
+ int y, x, oy, ox, dis = 0, ctr = 0;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ /* Find a usable location */
+ while (1)
+ {
+ /* Pick a nearby legal location */
+ while (1)
+ {
+ y = rand_spread(ny, dis);
+ x = rand_spread(nx, dis);
+ if (in_bounds(y, x)) break;
+ }
+
+ /* Accept "naked" floor grids */
+ if (cave_naked_bold2(y, x)) break;
+
+ /* Occasionally advance the distance */
+ if (++ctr > (4 * dis * dis + 4 * dis + 1))
+ {
+ ctr = 0;
+ dis++;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Save the old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ last_teleportation_y = y;
+ last_teleportation_x = x;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+}
+
+
+
+/*
+ * Teleport the player one level up or down (random when legal)
+ */
+void teleport_player_level(void)
+{
+ /* No effect in arena or quest */
+ if (p_ptr->inside_arena || p_ptr->inside_quest)
+ {
+ msg_print("There is no effect.");
+ return;
+ }
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerfull force prevents your from teleporting.");
+ return;
+ }
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (!dun_level)
+ {
+ msg_print("You sink through the floor.");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else if (is_quest(dun_level) || (dun_level >= MAX_DEPTH - 1))
+ {
+ msg_print("You rise up through the ceiling.");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ dun_level--;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else if (rand_int(100) < 50)
+ {
+ msg_print("You rise up through the ceiling.");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ dun_level--;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You sink through the floor.");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+}
+
+
+
+/*
+ * Recall the player to town or dungeon
+ */
+void recall_player(int d, int f)
+{
+#if 0
+ if (!p_ptr->town_num)
+ {
+ /* TODO: Recall the player to the last visited town */
+ msg_print("Nothing happens.");
+ return;
+ }
+#endif
+ /* Rooted means no move */
+ if (p_ptr->tim_roots)
+ {
+ msg_print("Your roots prevent the recall.");
+ return;
+ }
+
+
+ if (dun_level && (max_dlv[dungeon_type] > dun_level) &&
+ !p_ptr->inside_quest)
+ {
+ if (get_check("Reset recall depth? "))
+ max_dlv[dungeon_type] = dun_level;
+
+ }
+ if (!p_ptr->word_recall)
+ {
+ p_ptr->word_recall = rand_int(d) + f;
+ msg_print("The air about you becomes charged...");
+ }
+ else
+ {
+ p_ptr->word_recall = 0;
+ msg_print("A tension leaves the air around you...");
+ }
+ p_ptr->redraw |= (PR_DEPTH);
+}
+
+
+
+/*
+ * Get a legal "multi-hued" color for drawing "spells"
+ */
+static byte mh_attr(int max)
+{
+ switch (randint(max))
+ {
+ case 1:
+ return (TERM_RED);
+ case 2:
+ return (TERM_GREEN);
+ case 3:
+ return (TERM_BLUE);
+ case 4:
+ return (TERM_YELLOW);
+ case 5:
+ return (TERM_ORANGE);
+ case 6:
+ return (TERM_VIOLET);
+ case 7:
+ return (TERM_L_RED);
+ case 8:
+ return (TERM_L_GREEN);
+ case 9:
+ return (TERM_L_BLUE);
+ case 10:
+ return (TERM_UMBER);
+ case 11:
+ return (TERM_L_UMBER);
+ case 12:
+ return (TERM_SLATE);
+ case 13:
+ return (TERM_WHITE);
+ case 14:
+ return (TERM_L_WHITE);
+ case 15:
+ return (TERM_L_DARK);
+ }
+
+ return (TERM_WHITE);
+}
+
+
+/*
+ * Return a color to use for the bolt/ball spells
+ */
+byte spell_color(int type)
+{
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_COLOR, "d", "(d,d)", type, streq(ANGBAND_GRAF, "new")))
+ {
+ return process_hooks_return[0].num;
+ }
+
+ /* Check if A.B.'s new graphics should be used (rr9) */
+ if (streq(ANGBAND_GRAF, "new"))
+ {
+ /* Analyze */
+ switch (type)
+ {
+ case GF_MISSILE:
+ return (0x0F);
+ case GF_ACID:
+ return (0x04);
+ case GF_ELEC:
+ return (0x02);
+ case GF_FIRE:
+ return (0x00);
+ case GF_COLD:
+ return (0x01);
+ case GF_POIS:
+ return (0x03);
+ case GF_UNBREATH:
+ return (0x03);
+ case GF_HOLY_FIRE:
+ return (0x00);
+ case GF_HELL_FIRE:
+ return (0x00);
+ case GF_MANA:
+ return (0x0E);
+ case GF_ARROW:
+ return (0x0F);
+ case GF_WATER:
+ return (0x04);
+ case GF_WAVE:
+ return (0x04);
+ case GF_NETHER:
+ return (0x07);
+ case GF_CHAOS:
+ return (mh_attr(15));
+ case GF_DISENCHANT:
+ return (0x05);
+ case GF_NEXUS:
+ return (0x0C);
+ case GF_CONFUSION:
+ return (mh_attr(4));
+ case GF_SOUND:
+ return (0x09);
+ case GF_SHARDS:
+ return (0x08);
+ case GF_FORCE:
+ return (0x09);
+ case GF_INERTIA:
+ return (0x09);
+ case GF_GRAVITY:
+ return (0x09);
+ case GF_TIME:
+ return (0x09);
+ case GF_LITE_WEAK:
+ return (0x06);
+ case GF_LITE:
+ return (0x06);
+ case GF_DARK_WEAK:
+ return (0x07);
+ case GF_DARK:
+ return (0x07);
+ case GF_PLASMA:
+ return (0x0B);
+ case GF_METEOR:
+ return (0x00);
+ case GF_ICE:
+ return (0x01);
+ case GF_ROCKET:
+ return (0x0F);
+ case GF_DEATH_RAY:
+ return (0x07);
+ case GF_NUKE:
+ return (mh_attr(2));
+ case GF_DISINTEGRATE:
+ return (0x05);
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ return (0x09);
+ }
+
+ }
+
+ /* Normal tiles or ASCII */
+ else if (use_color)
+ {
+ /* Analyze */
+ switch (type)
+ {
+ case GF_MISSILE:
+ return (TERM_SLATE);
+ case GF_ACID:
+ return (randint(5) < 3 ? TERM_YELLOW : TERM_L_GREEN);
+ case GF_ELEC:
+ return (randint(7) < 6 ? TERM_WHITE : (randint(4) == 1 ? TERM_BLUE : TERM_L_BLUE));
+ case GF_FIRE:
+ return (randint(6) < 4 ? TERM_YELLOW : (randint(4) == 1 ? TERM_RED : TERM_L_RED));
+ case GF_COLD:
+ return (randint(6) < 4 ? TERM_WHITE : TERM_L_WHITE);
+ case GF_POIS:
+ return (randint(5) < 3 ? TERM_L_GREEN : TERM_GREEN);
+ case GF_UNBREATH:
+ return (randint(7) < 3 ? TERM_L_GREEN : TERM_GREEN);
+ case GF_HOLY_FIRE:
+ return (randint(5) == 1 ? TERM_ORANGE : TERM_WHITE);
+ case GF_HELL_FIRE:
+ return (randint(6) == 1 ? TERM_RED : TERM_L_DARK);
+ case GF_MANA:
+ return (randint(5) != 1 ? TERM_VIOLET : TERM_L_BLUE);
+ case GF_ARROW:
+ return (TERM_L_UMBER);
+ case GF_WATER:
+ return (randint(4) == 1 ? TERM_L_BLUE : TERM_BLUE);
+ case GF_WAVE:
+ return (randint(4) == 1 ? TERM_L_BLUE : TERM_BLUE);
+ case GF_NETHER:
+ return (randint(4) == 1 ? TERM_SLATE : TERM_L_DARK);
+ case GF_CHAOS:
+ return (mh_attr(15));
+ case GF_DISENCHANT:
+ return (randint(5) != 1 ? TERM_L_BLUE : TERM_VIOLET);
+ case GF_NEXUS:
+ return (randint(5) < 3 ? TERM_L_RED : TERM_VIOLET);
+ case GF_CONFUSION:
+ return (mh_attr(4));
+ case GF_SOUND:
+ return (randint(4) == 1 ? TERM_VIOLET : TERM_WHITE);
+ case GF_SHARDS:
+ return (randint(5) < 3 ? TERM_UMBER : TERM_SLATE);
+ case GF_FORCE:
+ return (randint(5) < 3 ? TERM_L_WHITE : TERM_ORANGE);
+ case GF_INERTIA:
+ return (randint(5) < 3 ? TERM_SLATE : TERM_L_WHITE);
+ case GF_GRAVITY:
+ return (randint(3) == 1 ? TERM_L_UMBER : TERM_UMBER);
+ case GF_TIME:
+ return (randint(2) == 1 ? TERM_WHITE : TERM_L_DARK);
+ case GF_LITE_WEAK:
+ return (randint(3) == 1 ? TERM_ORANGE : TERM_YELLOW);
+ case GF_LITE:
+ return (randint(4) == 1 ? TERM_ORANGE : TERM_YELLOW);
+ case GF_DARK_WEAK:
+ return (randint(3) == 1 ? TERM_DARK : TERM_L_DARK);
+ case GF_DARK:
+ return (randint(4) == 1 ? TERM_DARK : TERM_L_DARK);
+ case GF_PLASMA:
+ return (randint(5) == 1 ? TERM_RED : TERM_L_RED);
+ case GF_METEOR:
+ return (randint(3) == 1 ? TERM_RED : TERM_UMBER);
+ case GF_ICE:
+ return (randint(4) == 1 ? TERM_L_BLUE : TERM_WHITE);
+ case GF_ROCKET:
+ return (randint(6) < 4 ? TERM_L_RED : (randint(4) == 1 ? TERM_RED : TERM_L_UMBER));
+ case GF_DEATH:
+ case GF_DEATH_RAY:
+ return (TERM_L_DARK);
+ case GF_NUKE:
+ return (mh_attr(2));
+ case GF_DISINTEGRATE:
+ return (randint(3) != 1 ? TERM_L_DARK : (randint(2) == 1 ? TERM_ORANGE : TERM_L_UMBER));
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ return (randint(3) != 1 ? TERM_L_BLUE : TERM_WHITE);
+ }
+ }
+
+ /* Standard "color" */
+ return (TERM_WHITE);
+}
+
+
+/*
+ * Find the attr/char pair to use for a spell effect
+ *
+ * It is moving (or has moved) from (x,y) to (nx,ny).
+ *
+ * If the distance is not "one", we (may) return "*".
+ */
+static u16b bolt_pict(int y, int x, int ny, int nx, int typ)
+{
+ int base;
+
+ byte k;
+
+ byte a;
+ char c;
+
+ /* No motion (*) */
+ if ((ny == y) && (nx == x)) base = 0x30;
+
+ /* Vertical (|) */
+ else if (nx == x) base = 0x40;
+
+ /* Horizontal (-) */
+ else if (ny == y) base = 0x50;
+
+ /* Diagonal (/) */
+ else if ((ny - y) == (x - nx)) base = 0x60;
+
+ /* Diagonal (\) */
+ else if ((ny - y) == (nx - x)) base = 0x70;
+
+ /* Weird (*) */
+ else base = 0x30;
+
+ /* Basic spell color */
+ k = spell_color(typ);
+
+ /* Obtain attr/char */
+ a = misc_to_attr[base + k];
+ c = misc_to_char[base + k];
+
+ /* Create pict */
+ return (PICT(a, c));
+}
+
+/*
+ * Cast the spelbound spells
+ */
+void spellbinder_trigger()
+{
+ int i;
+
+ cmsg_print(TERM_L_GREEN, "The spellbinder is triggered!");
+ for (i = 0; i < p_ptr->spellbinder_num; i++)
+ {
+ msg_format("Triggering spell %s.", school_spells[p_ptr->spellbinder[i]].name);
+ exec_lua(format("cast_school_spell(%d, spell(%d), TRUE)", p_ptr->spellbinder[i], p_ptr->spellbinder[i]));
+ }
+ p_ptr->spellbinder_num = 0;
+ p_ptr->spellbinder_trigger = 0;
+}
+
+
+/*
+ * Decreases players hit points and sets death flag if necessary
+ *
+ * XXX XXX XXX Invulnerability needs to be changed into a "shield"
+ *
+ * XXX XXX XXX Hack -- this function allows the user to save (or quit)
+ * the game when he dies, since the "You die." message is shown before
+ * setting the player to "dead".
+ */
+void take_hit(int damage, cptr hit_from)
+{
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ int old_chp = p_ptr->chp;
+
+ bool pen_invuln = FALSE;
+ bool monster_take = FALSE;
+
+ char death_message[80];
+
+ int warning = (p_ptr->mhp * hitpoint_warn / 10);
+ int percent;
+
+ /* Paranoia */
+ if (death) return;
+
+ /* Disturb */
+ disturb(1, 0);
+
+ /* Apply "invulnerability" */
+ if (p_ptr->invuln && (damage < 9000))
+ {
+ if (randint(PENETRATE_INVULNERABILITY) == 1)
+ {
+ pen_invuln = TRUE;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ /* Apply disruption shield */
+ if (p_ptr->disrupt_shield)
+ {
+ if (p_ptr->csp > (damage * 2))
+ {
+ p_ptr->csp -= (damage * 2);
+ damage = 0;
+ }
+ else
+ {
+ damage -= p_ptr->csp / 2;
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Display the mana */
+ p_ptr->redraw |= (PR_MANA);
+ }
+
+ /* Hurt the wielded monster if any */
+ if ((o_ptr->k_idx) && (magik(5 + get_skill(SKILL_SYMBIOTIC))) && (!carried_monster_hit))
+ {
+ cptr sym_name = symbiote_name(TRUE);
+
+ if (o_ptr->pval2 - damage <= 0)
+ {
+ cmsg_format(TERM_L_RED,
+ "%s dies from protecting you, you feel very sad...",
+ sym_name);
+ inven_item_increase(INVEN_CARRY, -1);
+ inven_item_optimize(INVEN_CARRY);
+ damage -= o_ptr->pval2;
+ o_ptr->pval2 = 0;
+ p_ptr->redraw |= PR_MH;
+ }
+ else
+ {
+ msg_format("%s takes the damage instead of you.", sym_name);
+ o_ptr->pval2 -= damage;
+ monster_take = TRUE;
+ }
+
+ carried_monster_hit = FALSE;
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+ }
+
+ /* Hurt the player */
+ if (!monster_take) p_ptr->chp -= damage;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ if (pen_invuln)
+ cmsg_print(TERM_YELLOW, "The attack penetrates your shield of invulnerability!");
+
+ /* Dead player */
+ if (p_ptr->chp < 0)
+ {
+ /* Necromancers get a special treatment */
+ if (((!has_ability(AB_UNDEAD_FORM)) || ((p_ptr->necro_extra & CLASS_UNDEAD))))
+ {
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ if (!last_words)
+ {
+ cmsg_print(TERM_RED, "You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ (void)get_rnd_line("death.txt", death_message);
+ cmsg_print(TERM_RED, death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, hit_from);
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* No longer a winner */
+ total_winner = FALSE;
+
+
+ /* Note death */
+ death = TRUE;
+
+ if (get_check("Dump the screen? "))
+ {
+ do_cmd_html_dump();
+ }
+
+ /* Dead */
+ return;
+ }
+ /* Just turn the necromancer into an undead */
+ else
+ {
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = p_ptr->lev + (rand_int(p_ptr->lev / 2) - (p_ptr->lev / 4));
+ if (p_ptr->necro_extra2 < 1) p_ptr->necro_extra2 = 1;
+ cmsg_format(TERM_L_DARK, "You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce maximum */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ do_cmd_wiz_cure_all();
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->chp < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_chp > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ cmsg_print(TERM_RED, "*** LOW DEATHPOINT WARNING! ***");
+ else
+ cmsg_print(TERM_RED, "*** LOW HITPOINT WARNING! ***");
+ msg_print(NULL);
+ }
+
+ /* How much life is left ? */
+ percent = p_ptr->chp * 100 / p_ptr->mhp;
+
+ /* Check the spellbinder trigger */
+ if (p_ptr->spellbinder_trigger == SPELLBINDER_HP75)
+ {
+ /* Trigger ?! */
+ if (percent <= 75)
+ spellbinder_trigger();
+ }
+ else if (p_ptr->spellbinder_trigger == SPELLBINDER_HP50)
+ {
+ /* Trigger ?! */
+ if (percent <= 50)
+ spellbinder_trigger();
+ }
+ else if (p_ptr->spellbinder_trigger == SPELLBINDER_HP25)
+ {
+ /* Trigger ?! */
+ if (percent <= 25)
+ spellbinder_trigger();
+ }
+
+ /* Melkor acn summon to help you */
+ if (percent < 25)
+ {
+ PRAY_GOD(GOD_MELKOR)
+ {
+ int chance = p_ptr->grace / 500; /* * 100 / 50000; */
+
+ if (magik(chance - 10))
+ {
+ int i;
+ int type = SUMMON_DEMON;
+
+ if (magik(50))
+ type = SUMMON_UNDEAD;
+
+ if (p_ptr->grace > 10000)
+ {
+ if (type == SUMMON_DEMON)
+ type = SUMMON_HI_DEMON;
+ else
+ type = SUMMON_HI_UNDEAD;
+ }
+
+ chance /= 10;
+ if (chance < 1) chance = 1;
+ for (i = 0; i < chance; i++)
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level / 2, type, FALSE);
+ msg_print("Melkor summons monsters to help you!");
+ }
+ }
+ }
+
+ if (player_char_health)
+ lite_spot(p_ptr->py, p_ptr->px);
+}
+
+
+/* Decrease player's sanity. This is a copy of the function above. */
+void take_sanity_hit(int damage, cptr hit_from)
+{
+ int old_csane = p_ptr->csane;
+
+ char death_message[80];
+
+ int warning = (p_ptr->msane * hitpoint_warn / 10);
+
+
+ /* Paranoia */
+ if (death) return;
+
+ /* Disturb */
+ disturb(1, 0);
+
+
+ /* Hurt the player */
+ p_ptr->csane -= damage;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_SANITY);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Dead player */
+ if (p_ptr->csane < 0)
+ {
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ cmsg_print(TERM_VIOLET, "You turn into an unthinking vegetable.");
+ if (!last_words)
+ {
+ cmsg_print(TERM_RED, "You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ (void)get_rnd_line("death.txt", death_message);
+ cmsg_print(TERM_RED, death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, hit_from);
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Note death */
+ death = TRUE;
+
+ if (get_check("Dump the screen? "))
+ {
+ do_cmd_html_dump();
+ }
+
+ /* Dead */
+ return;
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->csane < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_csane > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ cmsg_print(TERM_RED, "*** LOW SANITY WARNING! ***");
+ msg_print(NULL);
+ }
+}
+
+
+/*
+ * Note that amulets, rods, and high-level spell books are immune
+ * to "inventory damage" of any kind. Also sling ammo and shovels.
+ */
+
+
+/*
+ * Does a given class of objects (usually) hate acid?
+ * Note that acid can either melt or corrode something.
+ */
+static bool hates_acid(object_type *o_ptr)
+{
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Wearable items */
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ return (TRUE);
+ }
+
+ /* Staffs/Scrolls are wood/paper */
+ case TV_STAFF:
+ case TV_SCROLL:
+ {
+ return (TRUE);
+ }
+
+ /* Ouch */
+ case TV_CHEST:
+ {
+ return (TRUE);
+ }
+
+ /* Junk is useless */
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate electricity?
+ */
+static bool hates_elec(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_RING:
+ case TV_WAND:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate fire?
+ * Hafted/Polearm weapons have wooden shafts.
+ * Arrows/Bows are mostly wooden.
+ */
+static bool hates_fire(object_type *o_ptr)
+{
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Special case for archers */
+ case TV_ARROW:
+ {
+ return TRUE;
+ };
+
+ /* Wearable */
+ case TV_LITE:
+ case TV_BOW:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ {
+ return (TRUE);
+ }
+
+ /* Books */
+ case TV_BOOK:
+ case TV_SYMBIOTIC_BOOK:
+ case TV_MUSIC_BOOK:
+ {
+ return (TRUE);
+ }
+
+ /* Chests */
+ case TV_CHEST:
+ {
+ return (TRUE);
+ }
+
+ /* Staffs/Scrolls burn */
+ case TV_STAFF:
+ case TV_SCROLL:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate cold?
+ */
+static bool hates_cold(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_POTION2:
+ case TV_POTION:
+ case TV_FLASK:
+ case TV_BOTTLE:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+
+
+
+
+
+
+
+/*
+ * Melt something
+ */
+static int set_acid_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_acid(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_ACID)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Electrical damage
+ */
+static int set_elec_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_elec(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_ELEC)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Burn something
+ */
+static int set_fire_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_fire(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_FIRE)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Freeze things
+ */
+static int set_cold_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_cold(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_COLD)) return (FALSE);
+ return (TRUE);
+}
+
+
+
+
+/*
+ * This seems like a pretty standard "typedef"
+ */
+typedef int (*inven_func)(object_type *);
+
+/*
+ * Destroys a type of item on a given percent chance
+ * Note that missiles are no longer necessarily all destroyed
+ * Destruction taken from "melee.c" code for "stealing".
+ * Returns number of items destroyed.
+ */
+static int inven_damage(inven_func typ, int perc)
+{
+ int i, j, k, amt;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Count the casualties */
+ k = 0;
+
+ /* Scan through the slots backwards */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack -- for now, skip artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) continue;
+
+ /* Give this item slot a shot at death */
+ if ((*typ)(o_ptr))
+ {
+ /* Count the casualties */
+ for (amt = j = 0; j < o_ptr->number; ++j)
+ {
+ if (rand_int(100) < perc) amt++;
+ }
+
+ /* Some casualities */
+ if (amt)
+ {
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) %s destroyed!",
+ ((o_ptr->number > 1) ?
+ ((amt == o_ptr->number) ? "All of y" :
+ (amt > 1 ? "Some of y" : "One of y")) : "Y"),
+ o_name, index_to_label(i),
+ ((amt > 1) ? "were" : "was"));
+
+ /* Potions smash open */
+ if (k_info[o_ptr->k_idx].tval == TV_POTION)
+ {
+ (void)potion_smash_effect(0, p_ptr->py, p_ptr->px, o_ptr->sval);
+ }
+
+ /*
+ * Hack -- If rods or wand are destroyed, the total maximum
+ * timeout or charges of the stack needs to be reduced,
+ * unless all the items are being destroyed. -LM-
+ */
+ if ((o_ptr->tval == TV_WAND)
+ && (amt < o_ptr->number))
+ {
+ o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Destroy "amt" items */
+ inven_item_increase(i, -amt);
+ inven_item_optimize(i);
+
+ /* Count the casualties */
+ k += amt;
+ }
+ }
+ }
+
+ /* Return the casualty count */
+ return (k);
+}
+
+
+
+
+/*
+ * Acid has hit the player, attempt to affect some armor.
+ *
+ * Note that the "base armor" of an object never changes.
+ *
+ * If any armor is damaged (or resists), the player takes less damage.
+ */
+static int minus_ac(void)
+{
+ object_type *o_ptr = NULL;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char o_name[80];
+
+
+ /* Pick a (possibly empty) inventory slot */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr = &p_ptr->inventory[INVEN_BODY];
+ break;
+ case 2:
+ o_ptr = &p_ptr->inventory[INVEN_ARM];
+ break;
+ case 3:
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+ break;
+ case 4:
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+ break;
+ case 5:
+ o_ptr = &p_ptr->inventory[INVEN_HEAD];
+ break;
+ case 6:
+ o_ptr = &p_ptr->inventory[INVEN_FEET];
+ break;
+ }
+
+ /* Nothing to damage */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* No damage left to be done */
+ if (o_ptr->ac + o_ptr->to_a <= 0) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Object resists */
+ if (f3 & (TR3_IGNORE_ACID))
+ {
+ msg_format("Your %s is unaffected!", o_name);
+
+ return (TRUE);
+ }
+
+ /* Message */
+ msg_format("Your %s is damaged!", o_name);
+
+ /* Damage the item */
+ o_ptr->to_a--;
+
+ /* Calculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+
+ /* Item was damaged */
+ return (TRUE);
+}
+
+
+/*
+ * Hurt the player with Acid
+ */
+void acid_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total Immunity */
+ if (p_ptr->immune_acid || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_acid || p_ptr->resist_acid)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+
+ /* If any armor gets hit, defend the player */
+ if (minus_ac()) dam = (dam + 1) / 2;
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->oppose_acid && p_ptr->resist_acid))
+ inven_damage(set_acid_destroy, inv);
+}
+
+
+/*
+ * Hurt the player with electricity
+ */
+void elec_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total immunity */
+ if (p_ptr->immune_elec || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->oppose_elec) dam = (dam + 2) / 3;
+ if (p_ptr->resist_elec) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_elec || p_ptr->resist_elec)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->oppose_elec && p_ptr->resist_elec))
+ inven_damage(set_elec_destroy, inv);
+}
+
+
+
+
+/*
+ * Hurt the player with Fire
+ */
+void fire_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Totally immune */
+ if (p_ptr->immune_fire || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->sensible_fire) dam = (dam + 2) * 2;
+ if (p_ptr->resist_fire) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_fire) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_fire || p_ptr->resist_fire)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->resist_fire && p_ptr->oppose_fire))
+ inven_damage(set_fire_destroy, inv);
+}
+
+
+/*
+ * Hurt the player with Cold
+ */
+void cold_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total immunity */
+ if (p_ptr->immune_cold || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->resist_cold) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_cold) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_cold || p_ptr->resist_cold)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->resist_cold && p_ptr->oppose_cold))
+ inven_damage(set_cold_destroy, inv);
+}
+
+
+
+
+
+/*
+ * Increases a stat by one randomized level -RAK-
+ *
+ * Note that this function (used by stat potions) now restores
+ * the stat BEFORE increasing it.
+ */
+bool inc_stat(int stat)
+{
+ int value, gain;
+
+ /* Then augment the current/max stat */
+ value = p_ptr->stat_cur[stat];
+
+ /* Cannot go above 18/100 */
+ if (value < 18 + 100)
+ {
+ /* Gain one (sometimes two) points */
+ if (value < 18)
+ {
+ gain = ((rand_int(100) < 75) ? 1 : 2);
+ value += gain;
+ }
+
+ /* Gain 1/6 to 1/3 of distance to 18/100 */
+ else if (value < 18 + 98)
+ {
+ /* Approximate gain value */
+ gain = (((18 + 100) - value) / 2 + 3) / 2;
+
+ /* Paranoia */
+ if (gain < 1) gain = 1;
+
+ /* Apply the bonus */
+ value += randint(gain) + gain / 2;
+
+ /* Maximal value */
+ if (value > 18 + 99) value = 18 + 99;
+ }
+
+ /* Gain one point at a time */
+ else
+ {
+ value++;
+ }
+
+ /* Save the new value */
+ p_ptr->stat_cur[stat] = value;
+
+ /* Bring up the maximum too */
+ if (value > p_ptr->stat_max[stat])
+ {
+ p_ptr->stat_max[stat] = value;
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Nothing to gain */
+ return (FALSE);
+}
+
+
+
+/*
+ * Decreases a stat by an amount indended to vary from 0 to 100 percent.
+ *
+ * Amount could be a little higher in extreme cases to mangle very high
+ * stats from massive assaults. -CWS
+ *
+ * Note that "permanent" means that the *given* amount is permanent,
+ * not that the new value becomes permanent. This may not work exactly
+ * as expected, due to "weirdness" in the algorithm, but in general,
+ * if your stat is already drained, the "max" value will not drop all
+ * the way down to the "cur" value.
+ */
+bool dec_stat(int stat, int amount, int mode)
+{
+ int cur, max, loss = 0, same, res = FALSE;
+
+
+ /* Acquire current value */
+ cur = p_ptr->stat_cur[stat];
+ max = p_ptr->stat_max[stat];
+
+ /* Note when the values are identical */
+ same = (cur == max);
+
+ /* Damage "current" value */
+ if (cur > 3)
+ {
+ /* Handle "low" values */
+ if (cur <= 18)
+ {
+ if (amount > 90) cur--;
+ if (amount > 50) cur--;
+ if (amount > 20) cur--;
+ cur--;
+ }
+
+ /* Handle "high" values */
+ else
+ {
+ /* Hack -- Decrement by a random amount between one-quarter */
+ /* and one-half of the stat bonus times the percentage, with a */
+ /* minimum damage of half the percentage. -CWS */
+ loss = (((cur - 18) / 2 + 1) / 2 + 1);
+
+ /* Paranoia */
+ if (loss < 1) loss = 1;
+
+ /* Randomize the loss */
+ loss = ((randint(loss) + loss) * amount) / 100;
+
+ /* Maximal loss */
+ if (loss < amount / 2) loss = amount / 2;
+
+ /* Lose some points */
+ cur = cur - loss;
+
+ /* Hack -- Only reduce stat to 17 sometimes */
+ if (cur < 18) cur = (amount <= 20) ? 18 : 17;
+ }
+
+ /* Prevent illegal values */
+ if (cur < 3) cur = 3;
+
+ /* Something happened */
+ if (cur != p_ptr->stat_cur[stat]) res = TRUE;
+ }
+
+ /* Damage "max" value */
+ if ((mode == STAT_DEC_PERMANENT) && (max > 3))
+ {
+ /* Handle "low" values */
+ if (max <= 18)
+ {
+ if (amount > 90) max--;
+ if (amount > 50) max--;
+ if (amount > 20) max--;
+ max--;
+ }
+
+ /* Handle "high" values */
+ else
+ {
+ /* Hack -- Decrement by a random amount between one-quarter */
+ /* and one-half of the stat bonus times the percentage, with a */
+ /* minimum damage of half the percentage. -CWS */
+ loss = (((max - 18) / 2 + 1) / 2 + 1);
+ loss = ((randint(loss) + loss) * amount) / 100;
+ if (loss < amount / 2) loss = amount / 2;
+
+ /* Lose some points */
+ max = max - loss;
+
+ /* Hack -- Only reduce stat to 17 sometimes */
+ if (max < 18) max = (amount <= 20) ? 18 : 17;
+ }
+
+ /* Hack -- keep it clean */
+ if (same || (max < cur)) max = cur;
+
+ /* Something happened */
+ if (max != p_ptr->stat_max[stat]) res = TRUE;
+ }
+
+ /* Apply changes */
+ if (res)
+ {
+ if (mode == STAT_DEC_TEMPORARY)
+ {
+ u16b dectime;
+
+ /* a little crude, perhaps */
+ dectime = rand_int(max_dlv[dungeon_type] * 50) + 50;
+
+ /* Calculate loss */
+ loss = p_ptr->stat_cur[stat] - cur;
+
+ /* prevent overflow, stat_cnt = u16b */
+ /* or add another temporary drain... */
+ if ( ((p_ptr->stat_cnt[stat] + dectime) < p_ptr->stat_cnt[stat]) ||
+ (p_ptr->stat_los[stat] > 0) )
+
+ {
+ p_ptr->stat_cnt[stat] += dectime;
+ p_ptr->stat_los[stat] += loss;
+ }
+ else
+ {
+ p_ptr->stat_cnt[stat] = dectime;
+ p_ptr->stat_los[stat] = loss;
+ }
+ }
+
+ /* Actually set the stat to its new value. */
+ p_ptr->stat_cur[stat] = cur;
+ p_ptr->stat_max[stat] = max;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+ }
+
+ /* Done */
+ return (res);
+}
+
+
+/*
+ * Restore a stat. Return TRUE only if this actually makes a difference.
+ */
+bool res_stat(int stat, bool full)
+{
+ /* Fully restore */
+ if (full)
+ {
+ /* Restore if needed */
+ if (p_ptr->stat_cur[stat] != p_ptr->stat_max[stat])
+ {
+ /* Restore */
+ p_ptr->stat_cur[stat] = p_ptr->stat_max[stat];
+
+ /* Remove temporary drain */
+ p_ptr->stat_cnt[stat] = 0;
+ p_ptr->stat_los[stat] = 0;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Something happened */
+ return (TRUE);
+ }
+ }
+
+ /* Restore temporary drained stat */
+ else
+ {
+ /* Restore if needed */
+ if (p_ptr->stat_los[stat])
+ {
+ /* Restore */
+ p_ptr->stat_cur[stat] += p_ptr->stat_los[stat];
+
+ /* Remove temporary drain */
+ p_ptr->stat_cnt[stat] = 0;
+ p_ptr->stat_los[stat] = 0;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Something happened */
+ return (TRUE);
+ }
+ }
+
+ /* Nothing to restore */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Apply disenchantment to the player's stuff
+ *
+ * XXX XXX XXX This function is also called from the "melee" code
+ *
+ * If "mode is set to 0 then a random slot will be used, if not the "mode"
+ * slot will be used.
+ *
+ * Return "TRUE" if the player notices anything
+ */
+bool apply_disenchant(int mode)
+{
+ int t = mode;
+ object_type *o_ptr;
+ char o_name[80];
+
+ if (!mode)
+ {
+ /* Pick a random slot */
+ switch (randint(8))
+ {
+ case 1:
+ t = INVEN_WIELD;
+ break;
+ case 2:
+ t = INVEN_BOW;
+ break;
+ case 3:
+ t = INVEN_BODY;
+ break;
+ case 4:
+ t = INVEN_OUTER;
+ break;
+ case 5:
+ t = INVEN_ARM;
+ break;
+ case 6:
+ t = INVEN_HEAD;
+ break;
+ case 7:
+ t = INVEN_HANDS;
+ break;
+ case 8:
+ t = INVEN_FEET;
+ break;
+ }
+ }
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[t];
+
+ /* No item, nothing happens */
+ if (!o_ptr->k_idx) return (FALSE);
+
+
+ /* Nothing to disenchant */
+ if ((o_ptr->to_h <= 0) && (o_ptr->to_d <= 0) && (o_ptr->to_a <= 0))
+ {
+ /* Nothing to notice */
+ return (FALSE);
+ }
+
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+
+ /* Artifacts have 71% chance to resist */
+ if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 71))
+ {
+ /* Message */
+ msg_format("Your %s (%c) resist%s disenchantment!",
+ o_name, index_to_label(t),
+ ((o_ptr->number != 1) ? "" : "s"));
+
+ /* Notice */
+ return (TRUE);
+ }
+
+
+ /* Disenchant tohit */
+ if (o_ptr->to_h > 0) o_ptr->to_h--;
+ if ((o_ptr->to_h > 5) && (rand_int(100) < 20)) o_ptr->to_h--;
+
+ /* Disenchant todam */
+ if (o_ptr->to_d > 0) o_ptr->to_d--;
+ if ((o_ptr->to_d > 5) && (rand_int(100) < 20)) o_ptr->to_d--;
+
+ /* Disenchant toac */
+ if (o_ptr->to_a > 0) o_ptr->to_a--;
+ if ((o_ptr->to_a > 5) && (rand_int(100) < 20)) o_ptr->to_a--;
+
+ /* Message */
+ msg_format("Your %s (%c) %s disenchanted!",
+ o_name, index_to_label(t),
+ ((o_ptr->number != 1) ? "were" : "was"));
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+void corrupt_player(void)
+{
+ int max1, cur1, max2, cur2, ii, jj;
+
+ /* Pick a pair of stats */
+ ii = rand_int(6);
+ for (jj = ii; jj == ii; jj = rand_int(6)) /* loop */;
+
+ max1 = p_ptr->stat_max[ii];
+ cur1 = p_ptr->stat_cur[ii];
+ max2 = p_ptr->stat_max[jj];
+ cur2 = p_ptr->stat_cur[jj];
+
+ p_ptr->stat_max[ii] = max2;
+ p_ptr->stat_cur[ii] = cur2;
+ p_ptr->stat_max[jj] = max1;
+ p_ptr->stat_cur[jj] = cur1;
+
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Apply Nexus
+ */
+static void apply_nexus(monster_type *m_ptr)
+{
+ if (m_ptr == NULL) return;
+
+ if (!(dungeon_flags2 & DF2_NO_TELEPORT))
+ {
+ switch (randint(7))
+ {
+ case 1:
+ case 2:
+ case 3:
+ {
+ teleport_player(200);
+ break;
+ }
+
+ case 4:
+ case 5:
+ {
+ teleport_player_to(m_ptr->fy, m_ptr->fx);
+ break;
+ }
+
+ case 6:
+ {
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ break;
+ }
+
+ /* Teleport Level */
+ teleport_player_level();
+ break;
+ }
+
+ case 7:
+ {
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ break;
+ }
+
+ msg_print("Your body starts to scramble...");
+ corrupt_player();
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Convert 2 couples of coordonates to a direction
+ */
+int yx_to_dir(int y2, int x2, int y1, int x1)
+{
+ int y = y2 - y1, x = x2 - x1;
+
+ if ((y == 0) && (x == 1)) return 6;
+ if ((y == 0) && (x == -1)) return 4;
+ if ((y == -1) && (x == 0)) return 8;
+ if ((y == 1) && (x == 0)) return 2;
+ if ((y == -1) && (x == -1)) return 7;
+ if ((y == -1) && (x == 1)) return 9;
+ if ((y == 1) && (x == 1)) return 3;
+ if ((y == 1) && (x == -1)) return 1;
+
+ return 5;
+}
+
+/*
+ * Give the opposate direction of the given one
+ */
+int invert_dir(int dir)
+{
+ if (dir == 4) return 6;
+ if (dir == 6) return 4;
+ if (dir == 8) return 2;
+ if (dir == 2) return 8;
+ if (dir == 7) return 3;
+ if (dir == 9) return 1;
+ if (dir == 1) return 9;
+ if (dir == 3) return 7;
+ return 5;
+}
+
+
+/*
+ * Determine which way the mana path follow
+ */
+int get_mana_path_dir(int y, int x, int oy, int ox, int pdir, int mana)
+{
+ int dir[8] = {5, 5, 5, 5, 5, 5, 5, 5}, n = 0, i, r = 0;
+
+ /* Check which case are allowed */
+ if (cave[y - 1][x].mana == mana) dir[n++] = 8;
+ if (cave[y + 1][x].mana == mana) dir[n++] = 2;
+ if (cave[y][x - 1].mana == mana) dir[n++] = 4;
+ if (cave[y][x + 1].mana == mana) dir[n++] = 6;
+
+ /* If only 2 possibilities select the only good one */
+ if (n == 2)
+ {
+ if (invert_dir(yx_to_dir(y, x, oy, ox)) != dir[0]) return dir[0];
+ if (invert_dir(yx_to_dir(y, x, oy, ox)) != dir[1]) return dir[1];
+
+ /* Should never happen */
+ return 5;
+ }
+
+
+ /* Check if it's not your last place */
+ for (i = 0; i < n; i++)
+ {
+ if ((oy == y + ddy[dir[i]]) && (ox == x + ddx[dir[i]]))
+ {
+ if (dir[i] == 8) dir[i] = 2;
+ else if (dir[i] == 2) dir[i] = 8;
+ else if (dir[i] == 6) dir[i] = 4;
+ else if (dir[i] == 4) dir[i] = 6;
+ }
+ }
+
+ /* Select the desired one if possible */
+ for (i = 0; i < n; i++)
+ {
+ if ((dir[i] == pdir) &&
+ (cave[y + ddy[dir[i]]][x + ddx[dir[i]]].mana == mana))
+ {
+ return dir[i];
+ }
+ }
+
+ /* If not select a random one */
+ if (n > 2)
+ {
+ byte nb = 200;
+
+ while (nb)
+ {
+ nb--;
+
+ r = rand_int(n);
+ if ((dir[r] != 5) && (yx_to_dir(y, x, oy, ox) != dir[r])) break;
+ }
+ return dir[r];
+ }
+ /* If nothing is found return 5 */
+ else return 5;
+}
+
+
+/*
+ * Determine the path taken by a projection.
+ *
+ * The projection will always start from the grid (y1,x1), and will travel
+ * towards the grid (y2,x2), touching one grid per unit of distance along
+ * the major axis, and stopping when it enters the destination grid or a
+ * wall grid, or has travelled the maximum legal distance of "range".
+ *
+ * Note that "distance" in this function (as in the "update_view()" code)
+ * is defined as "MAX(dy,dx) + MIN(dy,dx)/2", which means that the player
+ * actually has an "octagon of projection" not a "circle of projection".
+ *
+ * The path grids are saved into the grid array pointed to by "gp", and
+ * there should be room for at least "range" grids in "gp". Note that
+ * due to the way in which distance is calculated, this function normally
+ * uses fewer than "range" grids for the projection path, so the result
+ * of this function should never be compared directly to "range". Note
+ * that the initial grid (y1,x1) is never saved into the grid array, not
+ * even if the initial grid is also the final grid. XXX XXX XXX
+ *
+ * The "flg" flags can be used to modify the behavior of this function.
+ *
+ * In particular, the "PROJECT_STOP" and "PROJECT_THRU" flags have the same
+ * semantics as they do for the "project" function, namely, that the path
+ * will stop as soon as it hits a monster, or that the path will continue
+ * through the destination grid, respectively.
+ *
+ * The "PROJECT_JUMP" flag, which for the "project()" function means to
+ * start at a special grid (which makes no sense in this function), means
+ * that the path should be "angled" slightly if needed to avoid any wall
+ * grids, allowing the player to "target" any grid which is in "view".
+ * This flag is non-trivial and has not yet been implemented, but could
+ * perhaps make use of the "vinfo" array (above). XXX XXX XXX
+ *
+ * This function returns the number of grids (if any) in the path. This
+ * function will return zero if and only if (y1,x1) and (y2,x2) are equal.
+ *
+ * This algorithm is similar to, but slightly different from, the one used
+ * by "update_view_los()", and very different from the one used by "los()".
+ */
+sint project_path(u16b *gp, int range, int y1, int x1, int y2, int x2, int flg)
+{
+ int y, x, mana = 0, dir = 0;
+
+ int n = 0;
+ int k = 0;
+
+ /* Absolute */
+ int ay, ax;
+
+ /* Offsets */
+ int sy, sx;
+
+ /* Fractions */
+ int frac;
+
+ /* Scale factors */
+ int full, half;
+
+ /* Slope */
+ int m;
+
+
+ /* No path necessary (or allowed) */
+ if ((x1 == x2) && (y1 == y2)) return (0);
+
+ /* Hack -- to make a bolt/beam/ball follow a mana path */
+ if (flg & PROJECT_MANA_PATH)
+ {
+ int oy = y1, ox = x1, pdir = yx_to_dir(y2, x2, y1, x1);
+
+ /* Get the mana path level to follow */
+ mana = cave[y1][x1].mana;
+
+ /* Start */
+ dir = get_mana_path_dir(y1, x1, y1, x1, pdir, mana);
+ y = y1 + ddy[dir];
+ x = x1 + ddx[dir];
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if (n >= range + 10) return n;
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) return n;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) return n;
+ }
+
+ /* Get the new direction */
+ dir = get_mana_path_dir(y, x, oy, ox, pdir, mana);
+ if (dir == 5) return n;
+ oy = y;
+ ox = x;
+ y += ddy[dir];
+ x += ddx[dir];
+ }
+ }
+
+ /* Analyze "dy" */
+ if (y2 < y1)
+ {
+ ay = (y1 - y2);
+ sy = -1;
+ }
+ else
+ {
+ ay = (y2 - y1);
+ sy = 1;
+ }
+
+ /* Analyze "dx" */
+ if (x2 < x1)
+ {
+ ax = (x1 - x2);
+ sx = -1;
+ }
+ else
+ {
+ ax = (x2 - x1);
+ sx = 1;
+ }
+
+
+ /* Number of "units" in one "half" grid */
+ half = (ay * ax);
+
+ /* Number of "units" in one "full" grid */
+ full = half << 1;
+
+
+ /* Vertical */
+ if (ay > ax)
+ {
+ /* Start at tile edge */
+ frac = ax * ax;
+
+ /* Let m = ((dx/dy) * full) = (dx * dx * 2) = (frac * 2) */
+ m = frac << 1;
+
+ /* Start */
+ y = y1 + sy;
+ x = x1;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if ((n + (k >> 1)) >= range) break;
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)))
+ {
+ if ((x == x2) && (y == y2)) break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x)) && !(flg & PROJECT_WALL)) break;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) break;
+ }
+
+ /* Slant */
+ if (m)
+ {
+ /* Advance (X) part 1 */
+ frac += m;
+
+ /* Horizontal change */
+ if (frac >= half)
+ {
+ /* Advance (X) part 2 */
+ x += sx;
+
+ /* Advance (X) part 3 */
+ frac -= full;
+
+ /* Track distance */
+ k++;
+ }
+ }
+
+ /* Advance (Y) */
+ y += sy;
+ }
+ }
+
+ /* Horizontal */
+ else if (ax > ay)
+ {
+ /* Start at tile edge */
+ frac = ay * ay;
+
+ /* Let m = ((dy/dx) * full) = (dy * dy * 2) = (frac * 2) */
+ m = frac << 1;
+
+ /* Start */
+ y = y1;
+ x = x1 + sx;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if ((n + (k >> 1)) >= range) break;
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)))
+ {
+ if ((x == x2) && (y == y2)) break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x)) && !(flg & PROJECT_WALL)) break;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) break;
+ }
+
+ /* Slant */
+ if (m)
+ {
+ /* Advance (Y) part 1 */
+ frac += m;
+
+ /* Vertical change */
+ if (frac >= half)
+ {
+ /* Advance (Y) part 2 */
+ y += sy;
+
+ /* Advance (Y) part 3 */
+ frac -= full;
+
+ /* Track distance */
+ k++;
+ }
+ }
+
+ /* Advance (X) */
+ x += sx;
+ }
+ }
+
+ /* Diagonal */
+ else
+ {
+ /* Start */
+ y = y1 + sy;
+ x = x1 + sx;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp[n++] = GRID(y, x);
+
+ /* Hack -- Check maximum range */
+ if ((n + (n >> 1)) >= range) break;
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)))
+ {
+ if ((x == x2) && (y == y2)) break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((n > 0) && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x)) && !(flg & PROJECT_WALL)) break;
+
+ /* Sometimes stop at non-initial monsters/players */
+ if (flg & (PROJECT_STOP))
+ {
+ if ((n > 0) && (cave[y][x].m_idx != 0)) break;
+ }
+
+ /* Advance (Y) */
+ y += sy;
+
+ /* Advance (X) */
+ x += sx;
+ }
+ }
+
+
+ /* Length */
+ return (n);
+}
+
+
+
+/*
+ * Mega-Hack -- track "affected" monsters (see "project()" comments)
+ */
+static int project_m_n;
+static int project_m_x;
+static int project_m_y;
+
+
+
+/*
+ * We are called from "project()" to "damage" terrain features
+ *
+ * We are called both for "beam" effects and "ball" effects.
+ *
+ * The "r" parameter is the "distance from ground zero".
+ *
+ * Note that we determine if the player can "see" anything that happens
+ * by taking into account: blindness, line-of-sight, and illumination.
+ *
+ * We return "TRUE" if the effect of the projection is "obvious".
+ *
+ * XXX XXX XXX We also "see" grids which are "memorized", probably a hack
+ *
+ * XXX XXX XXX Perhaps we should affect doors?
+ */
+static bool project_f(int who, int r, int y, int x, int dam, int typ)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ bool obvious = FALSE;
+
+ bool flag = FALSE;
+
+ bool seen;
+
+
+ /* XXX XXX XXX */
+ who = who ? who : 0;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+ /* Remember if the grid is with the LoS of player */
+ seen = player_can_see_bold(y, x);
+
+ /* Analyze the type */
+ switch (typ)
+ {
+ /* Ignore most effects */
+ case GF_ELEC:
+ case GF_SOUND:
+ case GF_MANA:
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ {
+ break;
+ }
+
+ case GF_COLD:
+ case GF_ICE:
+ {
+ int percent = c_ptr->feat == GF_COLD ? 20 : 50;
+
+ /* Only affects "boring" grids */
+ if (!cave_plain_floor_bold(y, x)) break;
+
+ if (rand_int(100) < percent)
+ {
+ cave_set_feat(y, x, FEAT_ICE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_BETWEEN_GATE:
+ {
+ int y1 = randint(cur_hgt) - 1;
+ int x1 = randint(cur_wid) - 1;
+ int y2 = y1;
+ int x2 = x1;
+ int try = 1000;
+
+ /*
+ * Avoid "interesting" and/or permanent features
+ *
+ * If we can make sure that all the "permanent" features
+ * have the remember flag set as well, we can simplify
+ * the conditional... -- pelpel
+ */
+ if (!cave_plain_floor_bold(y, x) ||
+ (f_info[cave[y][x].feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Destination shouldn't be "interesting" either */
+ while (try &&
+ (!cave_plain_floor_bold(y2, x2) ||
+ (f_info[cave[y2][x2].feat].flags1 & FF1_PERMANENT)))
+ {
+ y2 = y1 = randint(cur_hgt) - 1;
+ x2 = x1 = randint(cur_wid) - 1;
+ scatter(&y2, &x2, y1, x1, 20, 0);
+ try --;
+ }
+
+ /* No boarding grids found */
+ if (!try) break;
+
+ /* Place a pair of between gates */
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ cave[y][x].special = x2 + (y2 << 8);
+
+ cave_set_feat(y2, x2, FEAT_BETWEEN);
+ cave[y2][x2].special = x + (y << 8);
+
+ if (seen)
+ {
+ obvious = TRUE;
+ note_spot(y, x);
+ }
+
+ if (player_can_see_bold(y2, x2))
+ {
+ obvious = TRUE;
+ note_spot(y2, x2);
+ }
+
+ break;
+ }
+
+ /* Burn trees & melt ice */
+ case GF_FIRE:
+ case GF_METEOR:
+ case GF_PLASMA:
+ case GF_HOLY_FIRE:
+ case GF_HELL_FIRE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Trees *will* burn */
+ if (c_ptr->feat == FEAT_TREES)
+ {
+ cave_set_feat(y, x, FEAT_DEAD_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -50);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Trees *will* burn */
+ if (c_ptr->feat == FEAT_SMALL_TREES)
+ {
+ cave_set_feat(y, x, FEAT_DEAD_SMALL_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -60);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Ice can melt (chance == 30%) */
+ else if (c_ptr->feat == FEAT_ICE)
+ {
+ int k = rand_int(100);
+
+ if (k >= 30) break;
+
+ /* Melt ice */
+ if (k < 10) cave_set_feat(y, x, FEAT_DIRT);
+ else if (k < 30) cave_set_feat(y, x, FEAT_SHAL_WATER);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Floors can become ash or lava (chance == 25%) */
+ else if (f_info[c_ptr->feat].flags1 & FF1_FLOOR)
+ {
+ int k = rand_int(100);
+
+ if (k >= 25) break;
+
+ /* Burn floor */
+ if (k < 10) cave_set_feat(y, x, FEAT_SHAL_LAVA);
+ else if (k < 25) cave_set_feat(y, x, FEAT_ASH);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Sandwall can be turned into glass (chance == 30%) */
+ else if ((c_ptr->feat == FEAT_SANDWALL) ||
+ (c_ptr->feat == FEAT_SANDWALL_H) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ int k = rand_int(100);
+
+ /* Glass it */
+ if (k < 30)
+ {
+ cave_set_feat(y, x, FEAT_GLASS_WALL);
+
+ /* Visibility change */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ }
+
+ break;
+ }
+
+ case GF_WAVE:
+ case GF_WATER:
+ {
+ int p1 = 0;
+ int p2 = 0;
+ int f1 = 0;
+ int f2 = 0;
+ int f = 0;
+ int k;
+
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Needs more than 30 damage */
+ if (dam < 30) break;
+
+ if ((c_ptr->feat == FEAT_FLOOR) ||
+ (c_ptr->feat == FEAT_DIRT) ||
+ (c_ptr->feat == FEAT_GRASS))
+ {
+ /* 35% chance to create shallow water */
+ p1 = 35;
+ f1 = FEAT_SHAL_WATER;
+
+ /* 5% chance to create deep water */
+ p2 = 40;
+ f2 = FEAT_DEEP_WATER;
+ }
+ else if ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_MAGMA_H) ||
+ (c_ptr->feat == FEAT_MAGMA_K) ||
+ (c_ptr->feat == FEAT_SHAL_LAVA))
+ {
+ /* 15% chance to convert it to normal floor */
+ p1 = 15;
+ f1 = FEAT_FLOOR;
+ }
+ else if (c_ptr->feat == FEAT_DEEP_LAVA)
+ {
+ /* 10% chance to convert it to shallow lava */
+ p1 = 10;
+ f1 = FEAT_SHAL_LAVA;
+
+ /* 5% chance to convert it to normal floor */
+ p2 = 15;
+ f2 = FEAT_FLOOR;
+ }
+ else if ((c_ptr->feat == FEAT_SHAL_WATER) ||
+ (c_ptr->feat == FEAT_DARK_PIT))
+ {
+ /* 10% chance to convert it to deep water */
+ p1 = 10;
+ f1 = FEAT_DEEP_WATER;
+ }
+
+ k = rand_int(100);
+
+ if (k < p1) f = f1;
+ else if (k < p2) f = f2;
+
+ if (f)
+ {
+ if (f == FEAT_FLOOR) place_floor(y, x);
+ else cave_set_feat(y, x, f);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_NETHER:
+ case GF_NEXUS:
+ case GF_ACID:
+ case GF_SHARDS:
+ case GF_TIME:
+ case GF_FORCE:
+ case GF_NUKE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ if ((c_ptr->feat == FEAT_TREES) ||
+ (c_ptr->feat == FEAT_SMALL_TREES))
+ {
+ /* Destroy the grid */
+ cave_set_feat(y, x, FEAT_DEAD_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -50);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_DISINTEGRATE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ if (((c_ptr->feat == FEAT_TREES) ||
+ (c_ptr->feat == FEAT_SMALL_TREES) ||
+ (f_info[c_ptr->feat].flags1 & FF1_FLOOR)) &&
+ (rand_int(100) < 30))
+ {
+ /* Flow change */
+ if (c_ptr->feat == FEAT_TREES) p_ptr->update |= (PU_FLOW);
+
+ cave_set_feat(y, x, FEAT_ASH);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ if (c_ptr->feat == FEAT_TREES || c_ptr->feat == FEAT_SMALL_TREES)
+ inc_piety(GOD_YAVANNA, -50);
+
+ /* Visibility change */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ /* Destroy Traps (and Locks) */
+ case GF_KILL_TRAP:
+ {
+ /* Destroy normal traps and disarm monster traps */
+ if ((c_ptr->t_idx != 0) || (c_ptr->feat == FEAT_MON_TRAP))
+ {
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ msg_print("There is a bright flash of light!");
+ obvious = TRUE;
+ }
+
+ /* Forget the trap */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Destroy normal traps */
+ c_ptr->t_idx = 0;
+
+ /* Disarm monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ c_ptr->special = c_ptr->special2 = 0;
+
+ /* Remove the feature */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ place_floor(y, x);
+ }
+
+ /* Hack -- Force redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ /* Secret / Locked doors are found and unlocked */
+ else if ((c_ptr->feat == FEAT_SECRET) ||
+ ((c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) &&
+ (c_ptr->feat <= FEAT_DOOR_HEAD + 0x07)))
+ {
+
+ /* Check line of sound */
+ if (player_has_los_bold(y, x))
+ {
+ msg_print("Click!");
+ obvious = TRUE;
+ }
+
+ /* Remove feature mimic */
+ cave[y][x].mimic = 0;
+
+ /* Unlock the door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ break;
+ }
+
+ /* Destroy Doors (and traps) */
+ case GF_KILL_DOOR:
+ {
+ /* Destroy all doors and traps, and disarm monster traps */
+ if ((c_ptr->feat == FEAT_OPEN) ||
+ (c_ptr->feat == FEAT_BROKEN) ||
+ (c_ptr->t_idx != 0) ||
+ (c_ptr->feat == FEAT_MON_TRAP) ||
+ ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ /* Message */
+ msg_print("There is a bright flash of light!");
+ obvious = TRUE;
+
+ /* Visibility change */
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+ }
+ }
+
+ /* Forget the door */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Remove normal traps */
+ c_ptr->t_idx = 0;
+
+ /* Disarm monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ c_ptr->special = c_ptr->special2 = 0;
+
+ /* Remove the feature */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ place_floor(y, x);
+
+ /* Hack -- Force redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ break;
+ }
+
+ case GF_JAM_DOOR: /* Jams a door (as if with a spike) */
+ {
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Convert "locked" to "stuck" XXX XXX XXX */
+ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08;
+
+ /* Add one spike to the door */
+ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++;
+
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ /* Message */
+ msg_print("The door seems stuck.");
+ obvious = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ /* Destroy walls (and doors) */
+ case GF_KILL_WALL:
+ {
+ /* Non-walls (etc) */
+ if (cave_floor_bold(y, x)) break;
+
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Granite -- How about other wall types? */
+ if ((c_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (c_ptr->feat <= FEAT_WALL_SOLID))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The wall turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Quartz / Magma / Sand with treasure */
+ else if (((c_ptr->feat >= FEAT_MAGMA_H) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The vein turns into mud!");
+ msg_print("You have found something!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Place some gold */
+ place_gold(y, x);
+ }
+
+ /* Quartz / Magma / Sand */
+ else if ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ) ||
+ (c_ptr->feat == FEAT_SANDWALL) ||
+ (c_ptr->feat == FEAT_SANDWALL_H))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The vein turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Rubble */
+ else if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The rubble turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the rubble */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Hack -- place an object */
+ if (rand_int(100) < 10)
+ {
+ /* Found something */
+ if (seen)
+ {
+ msg_print("There was something buried in the rubble!");
+ obvious = TRUE;
+ }
+
+ /* Place gold */
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE);
+ }
+ }
+
+ /* Destroy doors (and secret doors) */
+ else if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ /* Hack -- special message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The door turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Remove mimic */
+ c_ptr->mimic = 0;
+
+ /* Destroy the feature */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ /* Make doors */
+ case GF_MAKE_DOOR:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Create a closed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+
+ /* Observe */
+ if (c_ptr->info & (CAVE_MARK)) obvious = TRUE;
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ /* Make traps */
+ case GF_MAKE_TRAP:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ /* Place a trap */
+ place_trap(y, x);
+
+ break;
+ }
+
+
+ case GF_MAKE_GLYPH:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ cave_set_feat(y, x, FEAT_GLYPH);
+
+ if (seen) obvious = TRUE;
+
+ break;
+ }
+
+
+
+ case GF_STONE_WALL:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) break;
+
+ /* Place a wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+
+ if (seen) obvious = TRUE;
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ case GF_WINDS_MANA:
+ {
+ if (dam >= 256)
+ {
+ /* With erase mana */
+
+ /* Absorb some of the mana of the grid */
+ p_ptr->csp += cave[y][x].mana / 80;
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ /* Set the new amount */
+ cave[y][x].mana = dam - 256;
+ }
+ else
+ {
+ /* Without erase mana */
+ int amt = cave[y][x].mana + dam;
+
+ /* Check if not overflow */
+ if (amt > 255) amt = 255;
+
+ /* Set the new amount */
+ cave[y][x].mana = amt;
+ }
+
+ break;
+ }
+
+ case GF_LAVA_FLOW:
+ {
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Shallow Lava */
+ if (dam == 1)
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_naked_bold(y, x)) break;
+
+ /* Place a shallow lava */
+ cave_set_feat(y, x, FEAT_SHAL_LAVA);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Deep Lava */
+ else
+ {
+ /* Require a "naked" floor grid */
+ if (cave_perma_bold(y, x) || !dam) break;
+
+ /* Place a deep lava */
+ cave_set_feat(y, x, FEAT_DEEP_LAVA);
+
+ if (seen) obvious = TRUE;
+
+ /* Dam is used as a counter for the number of grid to convert */
+ dam--;
+ }
+
+ break;
+ }
+
+ /* Lite up the grid */
+ case GF_LITE_WEAK:
+ case GF_LITE:
+ {
+ /* Turn on the light */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Observe */
+ if (seen) obvious = TRUE;
+
+ /*
+ * Mega-Hack -- Update the monster in the affected grid
+ * This allows "spear of light" (etc) to work "correctly"
+ */
+ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE);
+
+ break;
+ }
+
+ /* Darken the grid */
+ case GF_DARK_WEAK:
+ case GF_DARK:
+ {
+ /* Notice */
+ if (seen) obvious = TRUE;
+
+ /* Turn off the light. */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ note_spot(y, x);
+ }
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /*
+ * Mega-Hack -- Update the monster in the affected grid
+ * This allows "spear of light" (etc) to work "correctly"
+ */
+ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE);
+
+ /* All done */
+ break;
+ }
+
+ case GF_DESTRUCTION:
+ {
+ int t;
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ /* Hack -- Notice player affect */
+ if ((x == p_ptr->px) && (y == p_ptr->py))
+ {
+ /* Hurt the player later */
+ flag = TRUE;
+
+ /* Do not hurt this grid */
+ break;
+ ;
+ }
+
+ /* Delete the monster (if any) */
+ delete_monster(y, x);
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Destroy "valid" grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 60)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 90)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Sand */
+ else if (t < 110)
+ {
+ /* Create sand vein */
+ cave_set_feat(y, x, FEAT_SANDWALL);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Visibility and flow changes */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+ obvious = TRUE;
+ break;
+ }
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dd", "(s,d,d,d,d,d,d)", "grid", who, typ, dam, r, y, x))
+ {
+ obvious = process_hooks_return[0].num;
+ flag = process_hooks_return[1].num;
+ }
+ break;
+ }
+ }
+
+ /* Hack -- Affect player */
+ if (flag)
+ {
+ /* Message */
+ msg_print("There is a searing blast of light!");
+
+ /* Blind the player */
+ if (!p_ptr->resist_blind && !p_ptr->resist_lite)
+ {
+ /* Become blind */
+ (void)set_blind(p_ptr->blind + 10 + randint(10));
+ }
+ }
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+
+/* Array of raisable ego monster */
+#define MAX_RAISE 10
+static int raise_ego[MAX_RAISE] =
+{
+ 1, /* Skeleton */
+ 1, /* Skeleton */
+ 1, /* Skeleton */
+ 1, /* Skeleton */
+ 2, /* Zombie */
+ 2, /* Zombie */
+ 2, /* Zombie */
+ 4, /* Spectre */
+ 4, /* Spectre */
+ 3, /* Lich */
+};
+
+
+/*
+ * We are called from "project()" to "damage" objects
+ *
+ * We are called both for "beam" effects and "ball" effects.
+ *
+ * Perhaps we should only SOMETIMES damage things on the ground.
+ *
+ * The "r" parameter is the "distance from ground zero".
+ *
+ * Note that we determine if the player can "see" anything that happens
+ * by taking into account: blindness, line-of-sight, and illumination.
+ *
+ * XXX XXX XXX We also "see" grids which are "memorized", probably a hack
+ *
+ * We return "TRUE" if the effect of the projection is "obvious".
+ */
+static bool project_o(int who, int r, int y, int x, int dam, int typ)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ bool obvious = FALSE;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char o_name[80];
+
+ int o_sval = 0;
+ bool is_potion = FALSE;
+ int xx, yy;
+
+
+ /* XXX XXX XXX */
+ who = who ? who : 0;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ bool is_art = FALSE;
+ bool ignore = FALSE;
+ bool plural = FALSE;
+ bool do_kill = FALSE;
+
+ cptr note_kill = NULL;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Get the "plural"-ness */
+ if (o_ptr->number > 1) plural = TRUE;
+
+ /* Check for artifact */
+ if ((artifact_p(o_ptr) || o_ptr->art_name)) is_art = TRUE;
+
+ /* Analyze the type */
+ switch (typ)
+ {
+ /* makes corpses explode */
+ case GF_CORPSE_EXPL:
+ {
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+ s32b dama, radius = 7;
+
+ if (r_ptr->flags1 & RF1_FORCE_MAXHP)
+ dama = maxroll(r_ptr->hdice, r_ptr->hside);
+ else
+ dama = damroll(r_ptr->hdice, r_ptr->hside);
+
+ /* Adjust the damage */
+ dama = dama * dam / 100;
+
+ /* Adjust the radius */
+ radius = radius * dam / 100;
+
+ do_kill = TRUE;
+ note_kill = (plural ? " explode!" : " explodes!");
+ project(who, radius, y, x, dama, GF_SHARDS, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL);
+ }
+ break;
+ }
+
+ /* Acid -- Lots of things */
+ case GF_ACID:
+ {
+ if (hates_acid(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " melt!" : " melts!");
+ if (f3 & (TR3_IGNORE_ACID)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Elec -- Rings and Wands */
+ case GF_ELEC:
+ {
+ if (hates_elec(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ if (f3 & (TR3_IGNORE_ELEC)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Fire -- Flammable objects */
+ case GF_FIRE:
+ {
+ if (hates_fire(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " burn up!" : " burns up!");
+ if (f3 & (TR3_IGNORE_FIRE)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Cold -- potions and flasks */
+ case GF_COLD:
+ {
+ if (hates_cold(o_ptr))
+ {
+ note_kill = (plural ? " shatter!" : " shatters!");
+ do_kill = TRUE;
+ if (f3 & (TR3_IGNORE_COLD)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Fire + Elec */
+ case GF_PLASMA:
+ {
+ if (hates_fire(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " burn up!" : " burns up!");
+ if (f3 & (TR3_IGNORE_FIRE)) ignore = TRUE;
+ }
+ if (hates_elec(o_ptr))
+ {
+ ignore = FALSE;
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ if (f3 & (TR3_IGNORE_ELEC)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Fire + Cold */
+ case GF_METEOR:
+ {
+ if (hates_fire(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " burn up!" : " burns up!");
+ if (f3 & (TR3_IGNORE_FIRE)) ignore = TRUE;
+ }
+ if (hates_cold(o_ptr))
+ {
+ ignore = FALSE;
+ do_kill = TRUE;
+ note_kill = (plural ? " shatter!" : " shatters!");
+ if (f3 & (TR3_IGNORE_COLD)) ignore = TRUE;
+ }
+ break;
+ }
+
+ /* Hack -- break potions and such */
+ case GF_ICE:
+ case GF_SHARDS:
+ case GF_FORCE:
+ case GF_SOUND:
+ {
+ if (hates_cold(o_ptr))
+ {
+ note_kill = (plural ? " shatter!" : " shatters!");
+ do_kill = TRUE;
+ }
+ break;
+ }
+
+ /* Mana and Chaos -- destroy everything */
+ case GF_MANA:
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ break;
+ }
+
+ case GF_DISINTEGRATE:
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " evaporate!" : " evaporates!");
+ break;
+ }
+
+ case GF_CHAOS:
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ if (f2 & (TR2_RES_CHAOS)) ignore = TRUE;
+ break;
+ }
+
+ /* Holy Fire and Hell Fire -- destroys cursed non-artifacts */
+ case GF_HOLY_FIRE:
+ case GF_HELL_FIRE:
+ {
+ if (cursed_p(o_ptr))
+ {
+ do_kill = TRUE;
+ note_kill = (plural ? " are destroyed!" : " is destroyed!");
+ }
+ break;
+ }
+
+ /* Unlock chests */
+ case GF_KILL_TRAP:
+ case GF_KILL_DOOR:
+ {
+ /* Chests are noticed only if trapped or locked */
+ if (o_ptr->tval == TV_CHEST)
+ {
+ /* Disarm/Unlock traps */
+ if (o_ptr->pval > 0)
+ {
+ /* Disarm or Unlock */
+ o_ptr->pval = (0 - o_ptr->pval);
+
+ /* Identify */
+ object_known(o_ptr);
+
+ /* Notice */
+ if (o_ptr->marked)
+ {
+ msg_print("Click!");
+ obvious = TRUE;
+ }
+ }
+ }
+
+ break;
+ }
+ case GF_STAR_IDENTIFY:
+ {
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Mark the item as fully known */
+ o_ptr->ident |= (IDENT_MENTAL);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", 0 - this_o_idx, "full");
+
+ /* Squelch ! */
+ squeltch_grid();
+
+ break;
+ }
+ case GF_IDENTIFY:
+ {
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", 0 - this_o_idx, "normal");
+
+ /* Squelch ! */
+ squeltch_grid();
+
+ break;
+ }
+ case GF_RAISE:
+ {
+ xx = x;
+ yy = y;
+ get_pos_player(7, &y, &x);
+
+ /* Only corpses can be raised */
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ int ego = raise_ego[rand_int(MAX_RAISE)];
+
+ if (place_monster_one(y, x, o_ptr->pval2, ego, FALSE, (!who) ? MSTATUS_PET : MSTATUS_ENEMY))
+ msg_print("A monster rises from the grave!");
+ do_kill = TRUE;
+ }
+ break;
+ }
+ case GF_RAISE_DEMON:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+ cptr name;
+
+ if (o_ptr->tval != TV_CORPSE) break;
+
+ if (randint(100) > r_ptr->level - p_ptr->lev)
+ {
+ if (r_ptr->level < 10) name = "Manes";
+ else if (r_ptr->level < 18) name = "Tengu";
+ else if (r_ptr->level < 26) name = "Imp";
+ else if (r_ptr->level < 34) name = "Arch-vile";
+ else if (r_ptr->level < 42) name = "Bodak";
+ else if (r_ptr->level < 50) name = "Erynies";
+ else if (r_ptr->level < 58) name = "Vrock";
+ else if (r_ptr->level < 66) name = "Hezrou";
+ else if (r_ptr->level < 74) name = "Glabrezu";
+ else if (r_ptr->level < 82) name = "Nalfeshnee";
+ else if (r_ptr->level < 90) name = "Marilith";
+ else name = "Nycadaemon";
+
+ if (place_monster_one(y, x, test_monster_name(name), 0, FALSE, (!who) ? MSTATUS_PET : MSTATUS_ENEMY))
+ msg_print("A demon emerges from Hell!");
+ }
+
+ do_kill = TRUE;
+ break;
+ }
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dd", "(s,d,d,d,d,d,d,O)", "object", who, typ, dam, r, y, x, o_ptr))
+ {
+ obvious = process_hooks_return[0].num;
+ do_kill = process_hooks_return[1].num;
+ }
+ break;
+ }
+ }
+
+
+ /* Attempt to destroy the object */
+ if (do_kill)
+ {
+ /* Effect "observed" */
+ if (o_ptr->marked)
+ {
+ obvious = TRUE;
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+
+ /* Artifacts, and other objects, get to resist */
+ if (is_art || ignore)
+ {
+ /* Observe the resist */
+ if (o_ptr->marked)
+ {
+ msg_format("The %s %s unaffected!",
+ o_name, (plural ? "are" : "is"));
+ }
+ }
+
+ /* Kill it */
+ else
+ {
+ /* Describe if needed */
+ if (o_ptr->marked && note_kill)
+ {
+ msg_format("The %s%s", o_name, note_kill);
+ }
+
+ o_sval = o_ptr->sval;
+ is_potion = ((k_info[o_ptr->k_idx].tval == TV_POTION) || (k_info[o_ptr->k_idx].tval == TV_POTION2));
+
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Potions produce effects when 'shattered' */
+ if (is_potion)
+ {
+ (void)potion_smash_effect(who, y, x, o_sval);
+ }
+
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+/* Can the monster be hurt ? */
+bool hurt_monster(monster_type *m_ptr)
+{
+ if (m_ptr->status == MSTATUS_COMPANION) return FALSE;
+ else return TRUE;
+}
+
+/*
+ * Helper function for "project()" below.
+ *
+ * Handle a beam/bolt/ball causing damage to a monster.
+ *
+ * This routine takes a "source monster" (by index) which is mostly used to
+ * determine if the player is causing the damage, and a "radius" (see below),
+ * which is used to decrease the power of explosions with distance, and a
+ * location, via integers which are modified by certain types of attacks
+ * (polymorph and teleport being the obvious ones), a default damage, which
+ * is modified as needed based on various properties, and finally a "damage
+ * type" (see below).
+ *
+ * Note that this routine can handle "no damage" attacks (like teleport) by
+ * taking a "zero" damage, and can even take "parameters" to attacks (like
+ * confuse) by accepting a "damage", using it to calculate the effect, and
+ * then setting the damage to zero. Note that the "damage" parameter is
+ * divided by the radius, so monsters not at the "epicenter" will not take
+ * as much damage (or whatever)...
+ *
+ * Note that "polymorph" is dangerous, since a failure in "place_monster()"'
+ * may result in a dereference of an invalid pointer. XXX XXX XXX
+ *
+ * Various messages are produced, and damage is applied.
+ *
+ * Just "casting" a substance (i.e. plasma) does not make you immune, you must
+ * actually be "made" of that substance, or "breathe" big balls of it.
+ *
+ * We assume that "Plasma" monsters, and "Plasma" breathers, are immune
+ * to plasma.
+ *
+ * We assume "Nether" is an evil, necromantic force, so it doesn't hurt undead,
+ * and hurts evil less. If can breath nether, then it resists it as well.
+ *
+ * Damage reductions use the following formulas:
+ * Note that "dam = dam * 6 / (randint(6) + 6);"
+ * gives avg damage of .655, ranging from .858 to .500
+ * Note that "dam = dam * 5 / (randint(6) + 6);"
+ * gives avg damage of .544, ranging from .714 to .417
+ * Note that "dam = dam * 4 / (randint(6) + 6);"
+ * gives avg damage of .444, ranging from .556 to .333
+ * Note that "dam = dam * 3 / (randint(6) + 6);"
+ * gives avg damage of .327, ranging from .427 to .250
+ * Note that "dam = dam * 2 / (randint(6) + 6);"
+ * gives something simple.
+ *
+ * In this function, "result" messages are postponed until the end, where
+ * the "note" string is appended to the monster name, if not NULL. So,
+ * to make a spell have "no effect" just set "note" to NULL. You should
+ * also set "notice" to FALSE, or the player will learn what the spell does.
+ *
+ * We attempt to return "TRUE" if the player saw anything "useful" happen.
+ */
+bool project_m(int who, int r, int y, int x, int dam, int typ)
+{
+ int tmp;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ char killer [80];
+
+ cptr name = (r_name + r_ptr->name);
+
+ /* Is the monster "seen"? */
+ bool seen;
+
+ /* Were the effects "obvious" (if seen)? */
+ bool obvious = FALSE;
+
+ /* Were the effects "irrelevant"? */
+ bool skipped = FALSE;
+
+
+ /* Move setting */
+ int x1 = 0;
+ int y1 = 0;
+ int a = 0;
+ int b = 0;
+ int do_move = 0;
+
+ /* Polymorph setting (true or false) */
+ int do_poly = 0;
+
+ /* Teleport setting (max distance) */
+ int do_dist = 0;
+
+ /* Confusion setting (amount to confuse) */
+ int do_conf = 0;
+
+ /* Stunning setting (amount to stun) */
+ int do_stun = 0;
+
+ /* Bleeding amount */
+ int do_cut = 0;
+
+ /* Poison amount */
+ int do_pois = 0;
+
+ /* Sleep amount (amount to sleep) */
+ int do_sleep = 0;
+
+ /* Fear amount (amount to fear) */
+ int do_fear = 0;
+
+
+ /* Hold the monster name */
+ char m_name[80];
+
+ /* Assume no note */
+ cptr note = NULL;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+
+#if 0
+
+ /* Walls protect monsters */
+ /* (No, they don't) */
+ if (!cave_floor_bold(y, x)) return (FALSE);
+
+#endif
+
+ /* Nobody here */
+ if (!c_ptr->m_idx) return (FALSE);
+
+ /* Never affect projector */
+ if (who && (c_ptr->m_idx == who)) return (FALSE);
+
+ /*
+ * Don't affect already dead monsters
+ * Prevents problems with chain reactions of exploding monsters
+ */
+ if (m_ptr->hp < 0) return (FALSE);
+
+
+ /* Remember if the monster is within player's line of sight */
+ seen = (m_ptr->ml && ((who != -101) && (who != -100))) ? TRUE : FALSE;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* Get the monster name (BEFORE polymorphing) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Mega Gachk */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ msg_format("%^s is immune to magic.", m_name);
+ return seen;
+ }
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+ if (!who && (is_friend(m_ptr) >= 0))
+ {
+ bool get_angry = FALSE;
+ /* Grrr? */
+ switch (typ)
+ {
+ case GF_AWAY_UNDEAD:
+ case GF_AWAY_EVIL:
+ case GF_AWAY_ALL:
+ case GF_CHARM:
+ case GF_CHARM_UNMOVING:
+ case GF_STAR_CHARM:
+ case GF_CONTROL_UNDEAD:
+ case GF_CONTROL_ANIMAL:
+ case GF_CONTROL_DEMON:
+ case GF_OLD_HEAL:
+ case GF_OLD_SPEED:
+ case GF_DARK_WEAK:
+ case GF_JAM_DOOR:
+ case GF_RAISE:
+ case GF_RAISE_DEMON:
+ case GF_IDENTIFY:
+ break; /* none of the above anger */
+ case GF_TRAP_DEMONSOUL:
+ if (r_ptr->flags3 & RF3_DEMON)
+ get_angry = TRUE;
+ break;
+ case GF_KILL_WALL:
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ get_angry = TRUE;
+ break;
+ case GF_HOLY_FIRE:
+ if (!(r_ptr->flags3 & (RF3_GOOD)))
+ get_angry = TRUE;
+ break;
+ case GF_TURN_UNDEAD:
+ case GF_DISP_UNDEAD:
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ get_angry = TRUE;
+ break;
+ case GF_TURN_EVIL:
+ case GF_DISP_EVIL:
+ if (r_ptr->flags3 & RF3_EVIL)
+ get_angry = TRUE;
+ break;
+ case GF_DISP_GOOD:
+ if (r_ptr->flags3 & RF3_GOOD)
+ get_angry = TRUE;
+ break;
+ case GF_DISP_DEMON:
+ if (r_ptr->flags3 & RF3_DEMON)
+ get_angry = TRUE;
+ break;
+ case GF_DISP_LIVING:
+ case GF_UNBREATH:
+ if (!(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_NONLIVING)))
+ get_angry = TRUE;
+ break;
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ if (!(r_ptr->flags2 & (RF2_EMPTY_MIND)))
+ get_angry = TRUE;
+ break;
+ case GF_DOMINATION:
+ if (!(r_ptr->flags3 & (RF3_NO_CONF)))
+ get_angry = TRUE;
+ break;
+ case GF_OLD_POLY:
+ case GF_OLD_CLONE:
+ if (randint(8) == 1)
+ get_angry = TRUE;
+ break;
+ case GF_LITE:
+ case GF_LITE_WEAK:
+ if (r_ptr->flags3 & RF3_HURT_LITE)
+ get_angry = TRUE;
+ break;
+ default:
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "d", "(s,d,d,d,d,d,d,M)", "angry", who, typ, dam, r, y, x, m_ptr))
+ {
+ get_angry = process_hooks_return[0].num;
+ }
+ else
+ get_angry = TRUE;
+ }
+
+ /* Now anger it if appropriate */
+ if (get_angry == TRUE && !(who))
+ {
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ if (change_side(m_ptr)) msg_format("%^s gets angry!", m_name);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+
+
+ /* Analyze the damage type */
+ switch (typ)
+ {
+ case GF_ATTACK:
+ {
+ if (seen) obvious = TRUE;
+
+ py_attack(y, x, dam);
+
+ skipped = TRUE;
+
+ dam = 0;
+ break;
+ }
+
+ case GF_IDENTIFY:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Probe */
+ do_probe(c_ptr->m_idx);
+
+ dam = 0;
+ break;
+ }
+
+ /* Death -- instant death */
+ case GF_DEATH:
+ {
+ if (seen) obvious = TRUE;
+
+ if (r_ptr->r_flags1 & RF1_UNIQUE)
+ {
+ note = " resists.";
+ dam = 0;
+ }
+ else
+ {
+ /* It KILLS */
+ dam = 32535;
+ }
+ break;
+ }
+ /* Magic Missile -- pure damage */
+ case GF_MISSILE:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ /* Acid */
+ case GF_ACID:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags9 & (RF9_SUSCEP_ACID))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags9 |= (RF9_SUSCEP_ACID);
+ }
+ if (r_ptr->flags3 & (RF3_IM_ACID))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_ACID);
+ }
+ break;
+ }
+
+ /* Electricity */
+ case GF_ELEC:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags9 & (RF9_SUSCEP_ELEC))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC);
+ }
+ if (r_ptr->flags3 & (RF3_IM_ELEC))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_ELEC);
+ }
+ break;
+ }
+
+ /* Fire damage */
+ case GF_FIRE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_SUSCEP_FIRE))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE);
+ }
+ if (r_ptr->flags3 & (RF3_IM_FIRE))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_FIRE);
+ }
+ break;
+ }
+
+ /* Cold */
+ case GF_COLD:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ break;
+ }
+
+ /* Poison */
+ case GF_POIS:
+ {
+ if (seen) obvious = TRUE;
+ if (magik(25)) do_pois = (10 + randint(11) + r) / (r + 1);
+ if (r_ptr->flags9 & (RF9_SUSCEP_POIS))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ do_pois *= 2;
+ if (seen) r_ptr->r_flags9 |= (RF9_SUSCEP_POIS);
+ }
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ do_pois = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ break;
+ }
+
+
+ /* Thick Poison */
+ case GF_UNBREATH:
+ {
+ if (seen) obvious = TRUE;
+ if (magik(15)) do_pois = (10 + randint(11) + r) / (r + 1);
+ if ((r_ptr->flags3 & (RF3_NONLIVING)) || (r_ptr->flags3 & (RF3_UNDEAD)))
+ {
+ note = " is immune.";
+ dam = 0;
+ do_pois = 0;
+ }
+ break;
+ }
+
+ /* Nuclear waste */
+ case GF_NUKE:
+ {
+ if (seen) obvious = TRUE;
+
+ if (r_ptr->flags3 & (RF3_IM_POIS))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_POIS);
+ }
+ else if (randint(3) == 1) do_poly = TRUE;
+ break;
+ }
+
+ /* Holy Orb -- hurts Evil (replaced with Hellfire) */
+ case GF_HELL_FIRE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ dam *= 2;
+ note = " is hit hard.";
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+ break;
+ }
+
+ /* Holy Fire -- hurts Evil, Good are immune, others _resist_ */
+ case GF_HOLY_FIRE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_GOOD))
+ {
+ dam = 0;
+ note = " is immune.";
+ if (seen) r_ptr->r_flags3 |= (RF3_GOOD);
+ }
+ else if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ dam *= 2;
+ note = " is hit hard.";
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+ else
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Arrow -- XXX no defense */
+ case GF_ARROW:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ /* Plasma -- XXX perhaps check ELEC or FIRE */
+ case GF_PLASMA:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_RES_PLAS))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen)
+ r_ptr->r_flags3 |= (RF3_RES_PLAS);
+ }
+ break;
+ }
+
+ /* Nether -- see above */
+ case GF_NETHER:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ note = " is immune.";
+ dam = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+ else if (r_ptr->flags3 & (RF3_RES_NETH))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_NETH);
+ }
+ else if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ dam /= 2;
+ note = " resists somewhat.";
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ }
+ break;
+ }
+
+ /* Water (acid) damage -- Water spirits/elementals are immune */
+ case GF_WATER:
+ {
+ if (seen) obvious = TRUE;
+ if ((r_ptr->d_char == 'E') &&
+ (prefix(name, "W") ||
+ (strstr((r_name + r_ptr->name), "Unmaker"))))
+ {
+ note = " is immune.";
+ dam = 0;
+ }
+ else if (r_ptr->flags3 & (RF3_RES_WATE))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_WATE);
+ }
+ break;
+ }
+
+ /* Wave = Water + Force */
+ case GF_WAVE:
+ {
+ if (seen) obvious = TRUE;
+ if ((r_ptr->d_char == 'E') &&
+ (prefix(name, "W") ||
+ (strstr((r_name + r_ptr->name), "Unmaker"))))
+ {
+ note = " is immune.";
+ dam = 0;
+ }
+ else if (r_ptr->flags3 & (RF3_RES_WATE))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_WATE);
+ }
+
+ if (who == 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (m_ptr->fx - p_ptr->px) * 10;
+ y1 = (m_ptr->fy - p_ptr->py) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction monster is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = m_ptr->fy;
+ x1 = m_ptr->fx;
+
+ /* Monster move offsets in a,b */
+ note = " is thrown away.";
+ }
+ break;
+ }
+
+ /* Chaos -- Chaos breathers resist */
+ case GF_CHAOS:
+ {
+ if (seen) obvious = TRUE;
+ do_poly = TRUE;
+ do_conf = (5 + randint(11) + r) / (r + 1);
+ if ((r_ptr->flags4 & (RF4_BR_CHAO)) ||
+ ((r_ptr->flags3 & (RF3_DEMON)) && (randint(3) == 1)))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ do_poly = FALSE;
+ }
+ break;
+ }
+
+ /* Shards -- Shard breathers resist */
+ case GF_SHARDS:
+ {
+ if (seen) obvious = TRUE;
+ if (magik(33)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_SHAR))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ do_cut = 0;
+ }
+ break;
+ }
+
+ /* Rocket: Shard resistance helps */
+ case GF_ROCKET:
+ {
+ if (seen) obvious = TRUE;
+
+ if (magik(12)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_SHAR))
+ {
+ note = " resists somewhat.";
+ dam /= 2;
+ do_cut = 0;
+ }
+ break;
+ }
+
+
+ /* Sound -- Sound breathers resist */
+ case GF_SOUND:
+ {
+ if (seen) obvious = TRUE;
+ if (who <= 0)
+ {
+ if (rand_int(100 - p_ptr->lev) < 50)
+ do_stun = (10 + randint(15) + r) / (r + 1);
+ }
+ else
+ do_stun = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_SOUN))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Confusion */
+ case GF_CONFUSION:
+ {
+ if (seen) obvious = TRUE;
+ do_conf = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags4 & (RF4_BR_CONF))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ else if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ note = " resists somewhat.";
+ dam /= 2;
+ }
+ break;
+ }
+
+ /* Disenchantment -- Breathers and Disenchanters resist */
+ case GF_DISENCHANT:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_RES_DISE))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_DISE);
+ }
+ break;
+ }
+
+ /* Nexus -- Breathers and Existers resist */
+ case GF_NEXUS:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_RES_NEXU))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ if (seen) r_ptr->r_flags3 |= (RF3_RES_NEXU);
+ }
+ break;
+ }
+
+ /* Force */
+ case GF_FORCE:
+ {
+ if (seen) obvious = TRUE;
+
+ /*
+ * If fired by player, try pushing monster.
+ * First get vector from player to monster.
+ * x10 so we can use pseudo-fixed point maths.
+ *
+ * Really should use get_angle_to_grid (util.c)
+ */
+ if (who == 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (m_ptr->fx - p_ptr->px) * 10;
+ y1 = (m_ptr->fy - p_ptr->py) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction monster is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = m_ptr->fy;
+ x1 = m_ptr->fx;
+
+ /* Monster move offsets in a,b */
+ note = " is thrown away.";
+ }
+
+ /* --hack-- Only stun if a monster fired it */
+ else do_stun = (randint(15) + r) / (r + 1);
+
+ if (r_ptr->flags4 & (RF4_BR_WALL))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Inertia -- breathers resist */
+ case GF_INERTIA:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_INER))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ /* Powerful monsters can resist */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ obvious = FALSE;
+ }
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+ }
+ break;
+ }
+
+ /* Time -- breathers resist */
+ case GF_TIME:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_TIME))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+ /* Gravity -- breathers resist */
+ case GF_GRAVITY:
+ {
+ bool resist_tele = FALSE;
+
+ if (seen) obvious = TRUE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resist_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resist_tele = TRUE;
+ }
+ }
+
+ if (!resist_tele) do_dist = 10;
+ else do_dist = 0;
+
+ if (r_ptr->flags4 & (RF4_BR_GRAV))
+ {
+ note = " resists.";
+ dam *= 3;
+ dam /= (randint(6) + 6);
+ do_dist = 0;
+ }
+ else
+ {
+ /* 1. slowness */
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ obvious = FALSE;
+ }
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+
+ /* 2. stun */
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ }
+ break;
+ }
+
+ /* Pure damage */
+ case GF_MANA:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+
+ /* Pure damage */
+ case GF_DISINTEGRATE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_ROCK);
+ note = " loses some skin!";
+ note_dies = " evaporates!";
+ dam *= 2;
+ }
+
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ if (rand_int(m_ptr->level + 10) > rand_int(p_ptr->lev))
+ {
+ note = " resists.";
+ dam >>= 3;
+ }
+ }
+ break;
+ }
+
+ case GF_FEAR:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + (dam / 2) + randint(dam / 2));
+
+ /* No damage */
+ dam = 0;
+ break;
+ }
+
+ case GF_PSI:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ {
+ dam = 0;
+ note = " is immune!";
+ }
+ else if ((r_ptr->flags2 & RF2_STUPID) ||
+ (r_ptr->flags2 & RF2_WEIRD_MIND) ||
+ (r_ptr->flags3 & RF3_ANIMAL) ||
+ (m_ptr->level > randint(3 * dam)))
+ {
+ dam /= 3;
+ note = " resists.";
+
+ /* Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Injure +/- confusion */
+ monster_desc(killer, m_ptr, 0x88);
+ take_hit(dam, killer); /* has already been /3 */
+ if (randint(4) == 1)
+ {
+ switch (randint(4))
+ {
+ case 1:
+ set_confused(p_ptr->confused + 3 + randint(dam));
+ break;
+ case 2:
+ set_stun(p_ptr->stun + randint(dam));
+ break;
+ case 3:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + 3 + randint(dam));
+ break;
+ }
+ default:
+ if (!p_ptr->free_act)
+ (void)set_paralyzed(p_ptr->paralyzed + randint(dam));
+ break;
+ }
+ }
+ }
+ dam = 0;
+ }
+ }
+
+ if ((dam > 0) && (randint(4) == 1))
+ {
+ switch (randint(4))
+ {
+ case 1:
+ do_conf = 3 + randint(dam);
+ break;
+ case 2:
+ do_stun = 3 + randint(dam);
+ break;
+ case 3:
+ do_fear = 3 + randint(dam);
+ break;
+ default:
+ do_sleep = 3 + randint(dam);
+ break;
+ }
+ }
+
+ note_dies = " collapses, a mindless husk.";
+ break;
+ }
+
+ case GF_PSI_DRAIN:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ {
+ dam = 0;
+ note = " is immune!";
+ }
+ else if ((r_ptr->flags2 & RF2_STUPID) ||
+ (r_ptr->flags2 & RF2_WEIRD_MIND) ||
+ (r_ptr->flags3 & RF3_ANIMAL) ||
+ (m_ptr->level > randint(3 * dam)))
+ {
+ dam /= 3;
+ note = " resists.";
+
+ /*
+ * Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them
+ */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Injure + mana drain */
+ monster_desc(killer, m_ptr, 0x88);
+ msg_print("Your psychic energy is drained!");
+ p_ptr->csp = MAX(0, p_ptr->csp - damroll(5, dam) / 2);
+ p_ptr->redraw |= PR_MANA;
+ take_hit(dam, killer); /* has already been /3 */
+ }
+ dam = 0;
+ }
+ }
+ else if (dam > 0)
+ {
+ int b = damroll(5, dam) / 4;
+ msg_format("You convert %s%s pain into psychic energy!",
+ m_name, (seen ? "'s" : "s"));
+ b = MIN(p_ptr->msp, p_ptr->csp + b);
+ p_ptr->csp = b;
+ p_ptr->redraw |= PR_MANA;
+ }
+
+ note_dies = " collapses, a mindless husk.";
+ break;
+ }
+
+ case GF_TELEKINESIS:
+ {
+ if (seen) obvious = TRUE;
+ do_dist = 7;
+ /* 1. stun */
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > 5 + randint(dam)))
+ {
+ /* Resist */
+ do_stun = 0;
+ /* No obvious effect */
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Meteor -- powerful magic missile */
+ case GF_METEOR:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ case GF_DOMINATION:
+ {
+ if (is_friend(m_ptr) > 0) break;
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /*
+ * Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them
+ */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Confuse, stun, terrify */
+ switch (randint(4))
+ {
+ case 1:
+ set_stun(p_ptr->stun + dam / 2);
+ break;
+ case 2:
+ set_confused(p_ptr->confused + dam / 2);
+ break;
+ default:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + dam);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ }
+ else
+ {
+ if ((dam > 29) && (randint(100) < dam))
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ else
+ {
+ switch (randint(4))
+ {
+ case 1:
+ do_stun = dam / 2;
+ break;
+ case 2:
+ do_conf = dam / 2;
+ break;
+ default:
+ do_fear = dam;
+ }
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+
+ /* Ice -- Cold + Cuts + Stun */
+ case GF_ICE:
+ {
+ if (seen) obvious = TRUE;
+ do_stun = (randint(15) + 1) / (r + 1);
+ if (magik(33)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ do_cut *= 2;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ do_cut = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ break;
+ }
+
+
+ /* Drain Life */
+ case GF_OLD_DRAIN:
+ {
+ if (seen) obvious = TRUE;
+
+ if ((r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Egv", r_ptr->d_char)))
+ {
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+ if (r_ptr->flags3 & (RF3_DEMON))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ note = " is unaffected!";
+ obvious = FALSE;
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Death Ray */
+ case GF_DEATH_RAY:
+ {
+#if 0
+ dam = 0;
+#endif
+ if (seen) obvious = TRUE;
+ if ((r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)))
+ {
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ note = " is immune.";
+ obvious = FALSE;
+ dam = 0;
+ }
+ else if (((r_ptr->flags1 & (RF1_UNIQUE)) &&
+ (randint(888) != 666)) ||
+ (((m_ptr->level + randint(20)) > randint((dam) + randint(10))) &&
+ randint(100) != 66 ))
+ {
+ note = " resists!";
+ obvious = FALSE;
+ dam = 0;
+ }
+
+ else dam = (p_ptr->lev) * 200;
+
+ break;
+ }
+
+ /* Polymorph monster (Use "dam" as "power") */
+ case GF_OLD_POLY:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt to polymorph (see below) */
+ do_poly = TRUE;
+
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ do_poly = FALSE;
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+
+ break;
+ }
+
+
+ /* Clone monsters (Ignore "dam") */
+ case GF_OLD_CLONE:
+ {
+ bool is_frien = FALSE;
+
+ if (seen) obvious = TRUE;
+ if ((is_friend(m_ptr) > 0) && (randint(3) != 1))
+ is_frien = TRUE;
+
+ /* Heal fully */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Speed up */
+ if (m_ptr->mspeed < 150) m_ptr->mspeed += 10;
+
+ /* Attempt to clone. */
+ if (multiply_monster(c_ptr->m_idx, is_frien, TRUE))
+ {
+ note = " spawns!";
+ }
+
+ /* No "real" damage */
+ dam = 0;
+
+ break;
+ }
+
+
+ /* Heal Monster (use "dam" as amount of healing) */
+ case GF_OLD_HEAL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Heal */
+ m_ptr->hp += dam;
+
+ /* No overflow */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Message */
+ note = " looks healthier.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Speed Monster (Ignore "dam") */
+ case GF_OLD_SPEED:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Speed up */
+ if (m_ptr->mspeed < m_ptr->speed + 15) m_ptr->mspeed += 10;
+ note = " starts moving faster.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Slow Monster (Use "dam" as "power") */
+ case GF_OLD_SLOW:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Sleep (Use "dam" as "power") */
+ case GF_OLD_SLEEP:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_SLEEP)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_SLEEP))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_SLEEP);
+ }
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else
+ {
+ /* Go to sleep (much) later */
+ note = " falls asleep!";
+ do_sleep = 500;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Sleep (Use "dam" as "power") */
+ case GF_STASIS:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else
+ {
+ /* Go to sleep (much) later */
+ note = " is suspended!";
+ do_sleep = 500;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Charm monster */
+ case GF_CHARM:
+ {
+ dam += (adj_con_fix[p_ptr->stat_ind[A_CHR]] - 1);
+
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->mflag & MFLAG_QUEST) ||
+ (r_ptr->flags3 & RF3_NO_CONF) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 5))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ if (is_friend(m_ptr) < 0)
+ {
+ note = " suddenly seems friendly!";
+ m_ptr->status = MSTATUS_FRIEND;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* *Charm* monster */
+ case GF_STAR_CHARM:
+ {
+ dam += (adj_con_fix[p_ptr->stat_ind[A_CHR]] - 1);
+
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->mflag & MFLAG_QUEST) ||
+ (r_ptr->flags3 & RF3_NO_CONF) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 5))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ if (is_friend(m_ptr) < 0)
+ {
+ note = " suddenly seems friendly!";
+ if (can_create_companion()) m_ptr->status = MSTATUS_COMPANION;
+ else m_ptr->status = MSTATUS_PET;
+
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control undead */
+ case GF_CONTROL_UNDEAD:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & RF3_UNDEAD)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+
+#if 0
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+#endif
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control never-moving */
+ case GF_CHARM_UNMOVING:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags1 & RF1_NEVER_MOVE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Tame animal */
+ case GF_CONTROL_ANIMAL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_ANIMAL))) ||
+ (r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is tamed!";
+ m_ptr->status = MSTATUS_PET;
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control demon */
+ case GF_CONTROL_DEMON:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_DEMON))) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " obeys your commands!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Confusion (Use "dam" as "power") */
+ case GF_OLD_CONF:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ case GF_STUN:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Confusion (Use "dam" as "power") */
+ case GF_CONF_DAM:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ case GF_STUN_DAM:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Implosion is the same than Stun_dam but only affect the living */
+ case GF_IMPLOSION:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* Non_living resists */
+ if (r_ptr->flags3 & (RF3_NONLIVING))
+ {
+ /* Resist */
+ do_stun = 0;
+ dam = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Confusion & Stunning (Use "dam" as "power") */
+ case GF_STUN_CONF:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+
+ /* Lite, but only hurts susceptible creatures */
+ case GF_LITE_WEAK:
+ {
+ /* Hurt by light */
+ if (r_ptr->flags3 & (RF3_HURT_LITE))
+ {
+ /* Obvious effect */
+ if (seen) obvious = TRUE;
+
+ /* Memorize the effects */
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
+
+ /* Special effect */
+ note = " cringes from the light!";
+ note_dies = " shrivels away in the light!";
+ }
+
+ /* Normally no damage */
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+
+ /* Lite -- opposite of Dark */
+ case GF_LITE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_LITE))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ else if (r_ptr->flags3 & (RF3_HURT_LITE))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
+ note = " cringes from the light!";
+ note_dies = " shrivels away in the light!";
+ dam *= 2;
+ }
+ break;
+ }
+
+
+ /* Dark -- opposite of Lite */
+ case GF_DARK:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Likes darkness... */
+ if ((r_ptr->flags4 & (RF4_BR_DARK)) ||
+ (r_ptr->flags3 & RF3_ORC) ||
+ (r_ptr->flags3 & RF3_HURT_LITE))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+
+ /* Stone to Mud */
+ case GF_KILL_WALL:
+ {
+ /* Hurt by rock remover */
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ {
+ /* Notice effect */
+ if (seen) obvious = TRUE;
+
+ /* Memorize the effects */
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_ROCK);
+
+ /* Cute little message */
+ note = " loses some skin!";
+ note_dies = " dissolves!";
+ }
+
+ /* Usually, ignore the effects */
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+ /* Teleport undead (Use "dam" as "power") */
+ case GF_AWAY_UNDEAD:
+ {
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ bool resists_tele = FALSE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ if (seen) obvious = TRUE;
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ do_dist = dam;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Teleport evil (Use "dam" as "power") */
+ case GF_AWAY_EVIL:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ bool resists_tele = FALSE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ if (seen) obvious = TRUE;
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ do_dist = dam;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Teleport monster (Use "dam" as "power") */
+ case GF_AWAY_ALL:
+ {
+ bool resists_tele = FALSE;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Prepare to teleport */
+ do_dist = dam;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn undead (Use "dam" as "power") */
+ case GF_TURN_UNDEAD:
+ {
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn evil (Use "dam" as "power") */
+ case GF_TURN_EVIL:
+ {
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn monster (Use "dam" as "power") */
+ case GF_TURN_ALL:
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (r_ptr->flags3 & (RF3_NO_FEAR)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Dispel undead */
+ case GF_DISP_UNDEAD:
+ {
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+ /* Dispel evil */
+ case GF_DISP_EVIL:
+ {
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel good */
+ case GF_DISP_GOOD:
+ {
+ /* Only affect good */
+ if (r_ptr->flags3 & (RF3_GOOD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_GOOD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel living */
+ case GF_DISP_LIVING:
+ {
+ /* Only affect non-undead */
+ if (!(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_NONLIVING)))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel demons */
+ case GF_DISP_DEMON:
+ {
+ /* Only affect demons */
+ if (r_ptr->flags3 & (RF3_DEMON))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_DEMON);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel monster */
+ case GF_DISP_ALL:
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+
+ break;
+ }
+
+ /* Raise Death -- Heal monster */
+ case GF_RAISE:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Heal */
+ m_ptr->hp += dam;
+
+ /* No overflow */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Message */
+ note = " looks healthier.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Trap the soul of a demon and leave body */
+ case GF_TRAP_DEMONSOUL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Check race */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_DEMON))))
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ dam = 0;
+ }
+ /* Hack : drop corpse if the demon is killed by this
+ * spell */
+ else if (dam > m_ptr->hp)
+ {
+ object_type forge, *i_ptr = &forge;
+
+ /* Wipe the object */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ object_aware(i_ptr);
+ i_ptr->name1 = 201;
+ }
+
+ /* Length of decay - very long time */
+ i_ptr->pval = 100000;
+
+ /* Set weight */
+ i_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+
+ /* Remember what we are */
+ i_ptr->pval2 = m_ptr->r_idx;
+
+ /* Give HP */
+ i_ptr->pval3 = maxroll(r_ptr->hdice, r_ptr->hside);
+
+ /* Drop it */
+ drop_near(i_ptr, -1, y, x);
+ }
+
+ break;
+ }
+
+ /* Default */
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dddddddddss", "(s,d,d,d,d,d,d,M)", "monster", who, typ, dam, r, y, x, m_ptr))
+ {
+ obvious = process_hooks_return[0].num;
+ dam = process_hooks_return[1].num;
+ do_stun = process_hooks_return[2].num;
+ do_fear = process_hooks_return[3].num;
+ do_conf = process_hooks_return[4].num;
+ do_dist = process_hooks_return[5].num;
+ do_pois = process_hooks_return[6].num;
+ do_cut = process_hooks_return[7].num;
+ do_poly = process_hooks_return[8].num;
+ if (process_hooks_return[9].str != NULL)
+ note = process_hooks_return[9].str;
+ if (process_hooks_return[10].str != NULL)
+ note_dies = process_hooks_return[10].str;
+ }
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+ break;
+ }
+ }
+
+
+ /* Absolutely no effect */
+ if (skipped) return (FALSE);
+
+
+ /* "Unique" monsters cannot be polymorphed */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) do_poly = FALSE;
+
+ /*
+ * "Quest" monsters cannot be polymorphed
+ */
+ if (m_ptr->mflag & MFLAG_QUEST)
+ do_poly = FALSE;
+
+ /* "Unique" monsters can only be "killed" by the player unless they are player's friends */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && (m_ptr->status <= MSTATUS_NEUTRAL_P))
+ {
+ /* Uniques may only be killed by the player */
+ if (who && (who != -2) && (dam > m_ptr->hp)) dam = m_ptr->hp;
+ }
+
+ /*
+ * "Quest" monsters can only be "killed" by the player
+ */
+ if (m_ptr->mflag & MFLAG_QUEST)
+ {
+ if ((who > 0) && (dam > m_ptr->hp)) dam = m_ptr->hp;
+ }
+
+ if (do_pois && (!(r_ptr->flags3 & RF3_IM_POIS)) && (!(r_ptr->flags3 & RF4_BR_POIS)) && hurt_monster(m_ptr))
+ {
+ if (m_ptr->poisoned) note = " is more poisoned.";
+ else note = " is poisoned.";
+ m_ptr->poisoned += do_pois;
+ }
+
+ if (do_cut && (!(r_ptr->flags4 & RF4_BR_WALL)) && hurt_monster(m_ptr))
+ {
+ if (m_ptr->bleeding) note = " bleeds more strongly.";
+ else note = " starts bleeding.";
+ m_ptr->bleeding += do_cut;
+ }
+
+ /* Check for death */
+ if ((dam > m_ptr->hp) && hurt_monster(m_ptr))
+ {
+ /* Extract method of death */
+ note = note_dies;
+ }
+
+ /* Mega-Hack -- Handle "polymorph" -- monsters get a saving throw */
+ else if (do_poly && cave_floor_bold(y, x) && (randint(90) > m_ptr->level))
+ {
+ /* Default -- assume no polymorph */
+ note = " is unaffected!";
+
+ /* Handle polymorph */
+ if (do_poly_monster(y, x))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Monster polymorphs */
+ note = " changes!";
+
+ /* Turn off the damage */
+ dam = 0;
+
+ /* Hack -- Get new monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Hack -- Get new race */
+ r_ptr = race_inf(m_ptr);
+ }
+ }
+
+ /* Handle moving the monster.
+ *
+ * Note: This is a effect of force, but only when used
+ * by the player. (For the moment). The usual stun effect
+ * is not applied.
+ */
+ else if (do_move && hurt_monster(m_ptr))
+ {
+ int back = 0;
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ back = 0; /* Default of no movement */
+
+ /* How far can we push the monster? */
+ for (do_move = 1; do_move < 3; do_move++)
+ {
+ /* Get monster coords */
+ /* And offset position */
+ y1 = m_ptr->fy + (b * do_move);
+ x1 = m_ptr->fx + (a * do_move);
+
+ if (!in_bounds(y1, x1)) continue;
+
+ /* Require "empty" floor space */
+ if (!in_bounds(y1, x1) || !cave_empty_bold(y1, x1)) continue;
+
+ /* Hack -- no teleport onto glyph of warding */
+ if (cave[y1][x1].feat == FEAT_GLYPH) continue;
+
+ /* amount moved */
+ back = do_move;
+ }
+
+ /* Move the monster */
+ if (back)
+ {
+ y1 = m_ptr->fy + (b * back);
+ x1 = m_ptr->fx + (a * back);
+ monster_swap(m_ptr->fy, m_ptr->fx, y1, x1);
+
+ if (back == 2)
+ {
+ note = " is knocked back!";
+ }
+ if (back == 1)
+ {
+ note = " is knocked back and crushed!";
+
+ /* was kept from being pushed all the way, do extra dam */
+ dam = dam * 13 / 10;
+ }
+
+ /* Get new position */
+ y = y1;
+ x = x1;
+
+ /* Hack -- get new grid */
+ c_ptr = &cave[y][x];
+ }
+ else /* could not move the monster */
+ {
+ note = " is severely crushed!";
+
+ /* Do extra damage (1/3)*/
+ dam = dam * 15 / 10;
+ }
+
+ }
+
+ /* Handle "teleport" */
+ else if (do_dist)
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " disappears!";
+
+ /* Teleport */
+ teleport_away(c_ptr->m_idx, do_dist);
+
+ /* Hack -- get new location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Hack -- get new grid */
+ c_ptr = &cave[y][x];
+ }
+
+ /* Sound and Impact breathers never stun */
+ else if (do_stun &&
+ !(r_ptr->flags4 & (RF4_BR_SOUN)) &&
+ !(r_ptr->flags4 & (RF4_BR_WALL)) && hurt_monster(m_ptr))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Get confused */
+ if (m_ptr->stunned)
+ {
+ note = " is more dazed.";
+ tmp = m_ptr->stunned + (do_stun / 2);
+ }
+ else
+ {
+ note = " is dazed.";
+ tmp = do_stun;
+ }
+
+ /* Apply stun */
+ m_ptr->stunned = (tmp < 200) ? tmp : 200;
+ }
+
+ /* Confusion and Chaos breathers (and sleepers) never confuse */
+ else if (do_conf &&
+ !(r_ptr->flags3 & (RF3_NO_CONF)) &&
+ !(r_ptr->flags4 & (RF4_BR_CONF)) &&
+ !(r_ptr->flags4 & (RF4_BR_CHAO)) && hurt_monster(m_ptr))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Already partially confused */
+ if (m_ptr->confused)
+ {
+ note = " looks more confused.";
+ tmp = m_ptr->confused + (do_conf / 2);
+ }
+
+ /* Was not confused */
+ else
+ {
+ note = " looks confused.";
+ tmp = do_conf;
+ }
+
+ /* Apply confusion */
+ m_ptr->confused = (tmp < 200) ? tmp : 200;
+ }
+
+
+ /* Fear */
+ if (do_fear && hurt_monster(m_ptr))
+ {
+ /* Increase fear */
+ tmp = m_ptr->monfear + do_fear;
+
+ /* Set fear */
+ m_ptr->monfear = (tmp < 200) ? tmp : 200;
+ }
+
+
+ /* If another monster did the damage, hurt the monster by hand */
+ if (who > 0)
+ {
+ bool fear = FALSE;
+
+ /* Dead monster */
+ if (mon_take_hit_mon(who, c_ptr->m_idx, dam, &fear, note_dies))
+ {}
+
+ /* Damaged monster */
+ else
+ {
+ /* Give detailed messages if visible or destroyed */
+ if (note && seen) msg_format("%^s%s", m_name, note);
+
+ /* Hack -- Pain message */
+ else if (dam > 0) message_pain(c_ptr->m_idx, dam);
+
+ /* Hack -- handle sleep */
+ if (do_sleep) m_ptr->csleep = do_sleep;
+ }
+ }
+ /* If the player did it, give him experience, check fear */
+ else if (hurt_monster(m_ptr))
+ {
+ bool fear = FALSE;
+
+ /* Hurt the monster, check for fear and death */
+ if (mon_take_hit(c_ptr->m_idx, dam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* Damaged monster */
+ else
+ {
+ /* Give detailed messages if visible or destroyed */
+ if (note && seen) msg_format("%^s%s", m_name, note);
+
+ /* Hack -- Pain message */
+ else if (dam > 0) message_pain(c_ptr->m_idx, dam);
+
+ /* Take note */
+ if ((fear || do_fear) && (m_ptr->ml))
+ {
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+
+ /* Hack -- handle sleep */
+ if (do_sleep) m_ptr->csleep = do_sleep;
+ }
+ }
+
+
+ /* XXX XXX XXX Verify this code */
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+
+ /* Redraw the monster grid */
+ lite_spot(y, x);
+
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+
+ /* Track it */
+ project_m_n++;
+ project_m_x = x;
+ project_m_y = y;
+
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+
+/* Is the spell unsafe for the player ? */
+bool unsafe = FALSE;
+
+
+/*
+ * Helper function for "project()" below.
+ *
+ * Handle a beam/bolt/ball causing damage to the player.
+ *
+ * This routine takes a "source monster" (by index), a "distance", a default
+ * "damage", and a "damage type". See "project_m()" above.
+ *
+ * If "rad" is non-zero, then the blast was centered elsewhere, and the damage
+ * is reduced (see "project_m()" above). This can happen if a monster breathes
+ * at the player and hits a wall instead.
+ *
+ * NOTE (Zangband): 'Bolt' attacks can be reflected back, so we need to know
+ * if this is actually a ball or a bolt spell
+ *
+ *
+ * We return "TRUE" if any "obvious" effects were observed. XXX XXX Actually,
+ * we just assume that the effects were obvious, for historical reasons.
+ */
+static bool project_p(int who, int r, int y, int x, int dam, int typ, int a_rad)
+{
+ int k = 0, do_move = 0, a = 0, b = 0, x1 = 0, y1 = 0;
+
+ /* Hack -- assume obvious */
+ bool obvious = TRUE;
+
+ /* Player blind-ness */
+ bool blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Player needs a "description" (he is blind) */
+ bool fuzzy = FALSE;
+
+ /* Source monster */
+ monster_type *m_ptr = NULL;
+
+ /* Monster name (for attacks) */
+ char m_name[80];
+
+ /* Monster name (for damage) */
+ char killer[80];
+
+ /* Hack -- messages */
+ cptr act = NULL;
+
+
+ /* Player is not here */
+ if ((x != p_ptr->px) || (y != p_ptr->py)) return (FALSE);
+
+ /* Player cannot hurt himself */
+ if ((!who) && (!unsafe)) return (FALSE);
+
+ /* Bolt attack from a monster */
+ if ((!a_rad) && get_skill(SKILL_DODGE) && (who > 0))
+ {
+ int chance = (p_ptr->dodge_chance - ((r_info[who].level * 5) / 6)) / 3;
+
+ if ((chance > 0) && magik(chance))
+ {
+ msg_print("You dodge a magical attack!");
+ return (TRUE);
+ }
+ }
+
+ /* Effects done by the plane cannot bounce */
+ if (p_ptr->reflect && !a_rad && !(randint(10) == 1) && ((who != -101) && (who != -100)))
+ {
+ int t_y, t_x;
+ int max_attempts = 10;
+
+ if (blind) msg_print("Something bounces!");
+ else msg_print("The attack bounces!");
+
+ /* Choose 'new' target */
+ do
+ {
+ t_y = m_list[who].fy - 1 + randint(3);
+ t_x = m_list[who].fx - 1 + randint(3);
+ max_attempts--;
+ }
+ while (max_attempts && in_bounds2(t_y, t_x) &&
+ !(player_has_los_bold(t_y, t_x)));
+
+ if (max_attempts < 1)
+ {
+ t_y = m_list[who].fy;
+ t_x = m_list[who].fx;
+ }
+
+ project(0, 0, t_y, t_x, dam, typ, (PROJECT_STOP | PROJECT_KILL));
+
+ disturb(1, 0);
+ return TRUE;
+ }
+
+ /* XXX XXX XXX */
+ /* Limit maximum damage */
+ if (dam > 1600) dam = 1600;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* If the player is blind, be more descriptive */
+ if (blind) fuzzy = TRUE;
+
+ /* If the player is hit by a trap, be more descritive */
+ if (who == -2) fuzzy = TRUE;
+
+ /* Did ``God'' do it? */
+ if (who == -99)
+ {
+ if (p_ptr->pgod)
+ {
+ /* Find out the name of player's god. */
+ sprintf(killer, "%s",
+ deity_info[p_ptr->pgod].name);
+ }
+ else strcpy(killer, "Divine Wrath");
+ }
+
+ /* Did the dungeon do it? */
+ if (who == -100)
+ {
+ sprintf(killer, "%s",
+ d_name + d_info[dungeon_type].name);
+ }
+ if (who == -101)
+ {
+ sprintf(killer, "%s",
+ f_name + f_info[cave[p_ptr->py][p_ptr->px].feat].name);
+ }
+
+ if (who >= -1)
+ {
+ /* Get the source monster */
+ m_ptr = &m_list[who];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the monster's real name */
+ monster_desc(killer, m_ptr, 0x88);
+ }
+
+ if (who == -2)
+ {
+ sprintf(killer, t_name + t_info[cave[p_ptr->py][p_ptr->px].t_idx].name);
+ }
+
+ /* Analyze the damage */
+ switch (typ)
+ {
+ case GF_DEATH_RAY:
+ {
+ if (fuzzy) msg_print("You are hit by pure death!");
+ take_hit(32000, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_ACID:
+ {
+ if (fuzzy) msg_print("You are hit by acid!");
+ acid_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by fire!");
+ fire_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_COLD:
+ {
+ if (fuzzy) msg_print("You are hit by cold!");
+ cold_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_ELEC:
+ {
+ if (fuzzy) msg_print("You are hit by lightning!");
+ elec_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- also poisons player */
+ case GF_POIS:
+ {
+ if (fuzzy) msg_print("You are hit by poison!");
+ if (p_ptr->resist_pois) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_pois) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_pois || p_ptr->resist_pois)) &&
+ randint(HURT_CHANCE) == 1)
+ {
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ }
+
+ take_hit(dam, killer);
+
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(dam) + 10);
+ }
+ break;
+ }
+
+ /* Standard damage -- also poisons / mutates player */
+ case GF_NUKE:
+ {
+ if (fuzzy) msg_print("You are hit by radiation!");
+ if (p_ptr->resist_pois) dam = (2 * dam + 2) / 5;
+ if (p_ptr->oppose_pois) dam = (2 * dam + 2) / 5;
+ take_hit(dam, killer);
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(dam) + 10);
+
+ if (randint(5) == 1) /* 6 */
+ {
+ msg_print("You undergo a freakish metamorphosis!");
+ if (randint(4) == 1) /* 4 */
+ do_poly_self();
+ else
+ corrupt_player();
+ }
+
+ if (randint(6) == 1)
+ {
+ inven_damage(set_acid_destroy, 2);
+ }
+ }
+ break;
+ }
+
+ /* Standard damage */
+ case GF_MISSILE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Holy Orb -- Player only takes partial damage */
+ case GF_HOLY_FIRE:
+ {
+#if 0 /* DGDGDGDGD */
+ int diff = get_skill(SKILL_VALARIN) - get_skill(SKILL_NETHER);
+#else
+ int diff = 0;
+#endif
+ if (fuzzy) msg_print("You are hit by something!");
+ if (diff > 0)
+ dam /= 2;
+ else if (diff < 0)
+ dam *= 2;
+ take_hit(dam, killer);
+ break;
+ }
+
+ case GF_HELL_FIRE:
+ {
+#if 0 /* DGDGDGDGD */
+ int diff = get_skill(SKILL_VALARIN) - get_skill(SKILL_NETHER);
+#else
+ int diff = 0;
+#endif
+
+ if (fuzzy) msg_print("You are hit by something!");
+ if (diff < 0)
+ dam /= 2;
+ else if (diff > 0)
+ dam *= 2;
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Arrow -- XXX no dodging */
+ case GF_ARROW:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Plasma -- XXX No resist */
+ case GF_PLASMA:
+ {
+ if (fuzzy) msg_print("You are hit by something *HOT*!");
+ take_hit(dam, killer);
+
+ if (!p_ptr->resist_sound)
+ {
+ int k = (randint((dam > 40) ? 35 : (dam * 3 / 4 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ if (!(p_ptr->resist_fire ||
+ p_ptr->oppose_fire ||
+ p_ptr->immune_fire))
+ {
+ inven_damage(set_acid_destroy, 3);
+ }
+
+ break;
+ }
+
+ /* Nether -- drain experience */
+ case GF_NETHER:
+ {
+ if (fuzzy) msg_print("You are hit by nether forces!");
+ {
+ if (p_ptr->immune_neth)
+ {
+ dam = 0;
+ }
+ else if (p_ptr->resist_neth)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ take_hit(dam, killer);
+ }
+
+ break;
+ }
+
+ /* Water -- stun/confuse */
+ case GF_WAVE:
+ case GF_WATER:
+ {
+ if (fuzzy) msg_print("You are hit by something wet!");
+ if (!p_ptr->resist_sound)
+ {
+ set_stun(p_ptr->stun + randint(40));
+ }
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + randint(5) + 5);
+ }
+
+ if (randint(5) == 1)
+ {
+ inven_damage(set_cold_destroy, 3);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Chaos -- many effects */
+ case GF_CHAOS:
+ {
+ if (fuzzy) msg_print("You are hit by a wave of anarchy!");
+ if (p_ptr->resist_chaos)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(20) + 10);
+ }
+ if (!p_ptr->resist_chaos)
+ {
+ (void)set_image(p_ptr->image + randint(10));
+ }
+ if (!p_ptr->resist_neth && !p_ptr->resist_chaos)
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+ if ((!p_ptr->resist_chaos) || (randint(9) == 1))
+ {
+ inven_damage(set_elec_destroy, 2);
+ inven_damage(set_fire_destroy, 2);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Shards -- mostly cutting */
+ case GF_SHARDS:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp!");
+ if (p_ptr->resist_shard)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)set_cut(p_ptr->cut + dam);
+ }
+
+ if ((!p_ptr->resist_shard) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Sound -- mostly stunning */
+ case GF_SOUND:
+ {
+ if (fuzzy) msg_print("You are hit by a loud noise!");
+ if (p_ptr->resist_sound)
+ {
+ dam *= 5;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ int k = (randint((dam > 90) ? 35 : (dam / 3 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ if ((!p_ptr->resist_sound) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Pure confusion */
+ case GF_CONFUSION:
+ {
+ if (fuzzy) msg_print("You are hit by something puzzling!");
+ if (p_ptr->resist_conf)
+ {
+ dam *= 5;
+ dam /= (randint(6) + 6);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + randint(20) + 10);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Disenchantment -- see above */
+ case GF_DISENCHANT:
+ {
+ if (fuzzy) msg_print("You are hit by something static!");
+ if (p_ptr->resist_disen)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)apply_disenchant(0);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Nexus -- see above */
+ case GF_NEXUS:
+ {
+ if (fuzzy) msg_print("You are hit by something strange!");
+ if (p_ptr->resist_nexus)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ apply_nexus(m_ptr);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Force -- mostly stun */
+ case GF_FORCE:
+ {
+ if (fuzzy) msg_print("You are hit by kinetic force!");
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(20));
+ /*
+ * If fired by player, try pushing monster.
+ * First get vector from player to monster.
+ * x10 so we can use pseudo-fixed point maths.
+ *
+ * Really should use get_angle_to_grid (util.c)
+ */
+ if (who > 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (p_ptr->px - m_ptr->fx) * 10;
+ y1 = (p_ptr->py - m_ptr->fy) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction player is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = p_ptr->py;
+ x1 = p_ptr->px;
+ }
+ }
+ else
+ take_hit(dam, killer);
+ break;
+ }
+
+
+ /* Rocket -- stun, cut */
+ case GF_ROCKET:
+ {
+ if (fuzzy) msg_print("There is an explosion!");
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(20));
+ }
+ if (p_ptr->resist_shard)
+ {
+ dam /= 2;
+ }
+ else
+ {
+ (void)set_cut(p_ptr-> cut + ( dam / 2) );
+ }
+
+ if ((!p_ptr->resist_shard) || (randint(12) == 1))
+ {
+ inven_damage(set_cold_destroy, 3);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Inertia -- slowness */
+ case GF_INERTIA:
+ {
+ if (fuzzy) msg_print("You are hit by something slow!");
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Lite -- blinding */
+ case GF_LITE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ if (p_ptr->resist_lite)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ }
+ else if (!blind && !p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + randint(5) + 2);
+ }
+ if (p_ptr->sensible_lite)
+ {
+ msg_print("The light scorches your flesh!");
+ dam *= 3;
+ }
+ take_hit(dam, killer);
+
+ if (p_ptr->tim_wraith)
+ {
+ p_ptr->tim_wraith = 0;
+ msg_print("The light forces you out of your incorporeal shadow form.");
+
+ p_ptr->redraw |= PR_MAP;
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+
+ break;
+ }
+
+ /* Dark -- blinding */
+ case GF_DARK:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ if (p_ptr->resist_dark)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ }
+ else if (!blind && !p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + randint(5) + 2);
+ }
+ if (p_ptr->wraith_form) hp_player(dam);
+ else take_hit(dam, killer);
+ break;
+ }
+
+ /* Time -- bolt fewer effects XXX */
+ case GF_TIME:
+ {
+ if (fuzzy) msg_print("You are hit by a blast from the past!");
+
+ if (p_ptr->resist_continuum)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ msg_print("You feel as if time is passing you by.");
+ }
+ else
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ switch (randint(6))
+ {
+ case 1:
+ k = A_STR;
+ act = "strong";
+ break;
+ case 2:
+ k = A_INT;
+ act = "bright";
+ break;
+ case 3:
+ k = A_WIS;
+ act = "wise";
+ break;
+ case 4:
+ k = A_DEX;
+ act = "agile";
+ break;
+ case 5:
+ k = A_CON;
+ act = "hardy";
+ break;
+ case 6:
+ k = A_CHR;
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Gravity -- stun plus slowness plus teleport */
+ case GF_GRAVITY:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ if (fuzzy) msg_print("You are hit by something heavy!");
+ msg_print("Gravity warps around you.");
+ if (!unsafe)
+ {
+ teleport_player(5);
+ if (!p_ptr->ffall)
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ if (!(p_ptr->resist_sound || p_ptr->ffall))
+ {
+ int k = (randint((dam > 90) ? 35 : (dam / 3 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+ if (p_ptr->ffall)
+ {
+ dam = (dam * 2) / 3;
+ }
+
+ if ((!p_ptr->ffall) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ }
+ else
+ teleport_player(dam);
+ break;
+ }
+
+ /* Standard damage */
+ case GF_DISINTEGRATE:
+ {
+ if (fuzzy) msg_print("You are hit by pure energy!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ case GF_OLD_HEAL:
+ {
+ if (fuzzy) msg_print("You are hit by something invigorating!");
+ (void)hp_player(dam);
+ dam = 0;
+ break;
+ }
+
+ case GF_OLD_SPEED:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ (void)set_fast(p_ptr->fast + randint(5), 10);
+ dam = 0;
+ break;
+ }
+
+ case GF_OLD_SLOW:
+ {
+ if (fuzzy) msg_print("You are hit by something slow!");
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ break;
+ }
+
+ case GF_OLD_SLEEP:
+ {
+ if (p_ptr->free_act) break;
+ if (fuzzy) msg_print("You fall asleep!");
+ set_paralyzed(p_ptr->paralyzed + dam);
+ dam = 0;
+ break;
+ }
+
+ /* Pure damage */
+ case GF_MANA:
+ {
+ if (fuzzy) msg_print("You are hit by an aura of magic!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Pure damage */
+ case GF_METEOR:
+ {
+ if (fuzzy) msg_print("Something falls from the sky on you!");
+ take_hit(dam, killer);
+ if ((!p_ptr->resist_shard) || (randint(13) == 1))
+ {
+ if (!p_ptr->immune_fire) inven_damage(set_fire_destroy, 2);
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ break;
+ }
+
+ /* Ice -- cold plus stun plus cuts */
+ case GF_ICE:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp and cold!");
+ cold_dam(dam, killer);
+ if (!p_ptr->resist_shard)
+ {
+ (void)set_cut(p_ptr->cut + damroll(5, 8));
+ }
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(15));
+ }
+
+ if ((!(p_ptr->resist_cold || p_ptr->oppose_cold)) || (randint(12) == 1))
+ {
+ if (!(p_ptr->immune_cold)) inven_damage(set_cold_destroy, 3);
+ }
+
+ break;
+ }
+
+ /* Knowledge */
+ case GF_IDENTIFY:
+ {
+ if (fuzzy) msg_print("You are hit by pure knowledge!");
+ self_knowledge(NULL);
+ break;
+ }
+
+ /* Psi -- ESP */
+ case GF_PSI:
+ {
+ if (fuzzy) msg_print("You are hit by pure psionic energy!");
+ set_tim_esp(p_ptr->tim_esp + dam);
+ break;
+ }
+
+ /* Statis -- paralyse */
+ case GF_STASIS:
+ {
+ if (fuzzy) msg_print("You are hit by something paralyzing!");
+ set_paralyzed(p_ptr->paralyzed + dam);
+ break;
+ }
+
+ /* Raise Death -- restore life */
+ case GF_RAISE:
+ {
+ if (fuzzy) msg_print("You are hit by pure anti-death energy!");
+ restore_level();
+ break;
+ }
+
+ /* Make Glyph -- Shield */
+ case GF_MAKE_GLYPH:
+ {
+ if (fuzzy) msg_print("You are hit by pure protection!");
+ set_shield(p_ptr->shield + dam, 50, 0, 0, 0);
+ break;
+ }
+
+ /* Default */
+ default:
+ {
+ /* Hooks! */
+ if (process_hooks_ret(HOOK_GF_EXEC, "dd", "(s,d,d,d,d,d,d)", "player", who, typ, dam, r, y, x))
+ {
+ obvious = process_hooks_return[0].num;
+ dam = process_hooks_return[1].num;
+ }
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+ break;
+ }
+ }
+
+
+ /* Handle moving the player.
+ *
+ * Note: This is a effect of force
+ */
+ if (do_move)
+ {
+ int back = 0;
+
+ back = 0; /* Default of no movement */
+
+ /* How far can we push the monster? */
+ for (do_move = 1; do_move < 3; do_move++)
+ {
+ /* Get monster coords */
+ /* And offset position */
+ y1 = p_ptr->py + (b * do_move);
+ x1 = p_ptr->px + (a * do_move);
+
+ if (!in_bounds(y1, x1)) continue;
+
+ /* Require "empty" floor space */
+ if (!in_bounds(y1, x1) || !cave_empty_bold(y1, x1)) continue;
+
+ /* amount moved */
+ back = do_move;
+ }
+
+ /* Move the monster */
+ if (back)
+ {
+ y1 = p_ptr->py + (b * back);
+ x1 = p_ptr->px + (a * back);
+ swap_position(y1, x1);
+
+ if (back == 2)
+ {
+ msg_print("You are knocked back!");
+ }
+ if (back == 1)
+ {
+ msg_print("You are knocked back and crushed!");
+
+ /* was kept from being pushed all the way, do extra dam */
+ dam = dam * 13 / 10;
+ }
+
+ /* Get new position */
+ y = y1;
+ x = x1;
+ }
+ else /* could not move the monster */
+ {
+ msg_print("You are severely crushed!");
+
+ /* Do extra damage (1/3)*/
+ dam = dam * 15 / 10;
+ }
+
+ take_hit(dam, killer);
+
+ }
+
+
+ /* Disturb */
+ disturb(1, 0);
+
+
+ /* Return "Anything seen?" */
+ return (obvious);
+}
+
+
+
+/*
+ * Generic "beam"/"bolt"/"ball" projection routine.
+ *
+ * Input:
+ * who: Index of "source" monster (negative for "player")
+ * jk -- -2 for traps, only used with project_jump
+ * rad: Radius of explosion (0 = beam/bolt, 1 to 9 = ball)
+ * y,x: Target location (or location to travel "towards")
+ * dam: Base damage roll to apply to affected monsters (or player)
+ * typ: Type of damage to apply to monsters (and objects)
+ * flg: Extra bit flags (see PROJECT_xxxx in "defines.h")
+ *
+ * Return:
+ * TRUE if any "effects" of the projection were observed, else FALSE
+ *
+ * Allows a monster (or player) to project a beam/bolt/ball of a given kind
+ * towards a given location (optionally passing over the heads of interposing
+ * monsters), and have it do a given amount of damage to the monsters (and
+ * optionally objects) within the given radius of the final location.
+ *
+ * A "bolt" travels from source to target and affects only the target grid.
+ * A "beam" travels from source to target, affecting all grids passed through.
+ * A "ball" travels from source to the target, exploding at the target, and
+ * affecting everything within the given radius of the target location.
+ *
+ * Traditionally, a "bolt" does not affect anything on the ground, and does
+ * not pass over the heads of interposing monsters, much like a traditional
+ * missile, and will "stop" abruptly at the "target" even if no monster is
+ * positioned there, while a "ball", on the other hand, passes over the heads
+ * of monsters between the source and target, and affects everything except
+ * the source monster which lies within the final radius, while a "beam"
+ * affects every monster between the source and target, except for the casting
+ * monster (or player), and rarely affects things on the ground.
+ *
+ * Two special flags allow us to use this function in special ways, the
+ * "PROJECT_HIDE" flag allows us to perform "invisible" projections, while
+ * the "PROJECT_JUMP" flag allows us to affect a specific grid, without
+ * actually projecting from the source monster (or player).
+ *
+ * The player will only get "experience" for monsters killed by himself
+ * Unique monsters can only be destroyed by attacks from the player
+ *
+ * Only 256 grids can be affected per projection, limiting the effective
+ * "radius" of standard ball attacks to nine units (diameter nineteen).
+ *
+ * One can project in a given "direction" by combining PROJECT_THRU with small
+ * offsets to the initial location (see "line_spell()"), or by calculating
+ * "virtual targets" far away from the player.
+ *
+ * One can also use PROJECT_THRU to send a beam/bolt along an angled path,
+ * continuing until it actually hits something (useful for "stone to mud").
+ *
+ * Bolts and Beams explode INSIDE walls, so that they can destroy doors.
+ *
+ * Balls must explode BEFORE hitting walls, or they would affect monsters
+ * on both sides of a wall. Some bug reports indicate that this is still
+ * happening in 2.7.8 for Windows, though it appears to be impossible.
+ *
+ * We "pre-calculate" the blast area only in part for efficiency.
+ * More importantly, this lets us do "explosions" from the "inside" out.
+ * This results in a more logical distribution of "blast" treasure.
+ * It also produces a better (in my opinion) animation of the explosion.
+ * It could be (but is not) used to have the treasure dropped by monsters
+ * in the middle of the explosion fall "outwards", and then be damaged by
+ * the blast as it spreads outwards towards the treasure drop location.
+ *
+ * Walls and doors are included in the blast area, so that they can be
+ * "burned" or "melted" in later versions.
+ *
+ * This algorithm is intended to maximize simplicity, not necessarily
+ * efficiency, since this function is not a bottleneck in the code.
+ *
+ * We apply the blast effect from ground zero outwards, in several passes,
+ * first affecting features, then objects, then monsters, then the player.
+ * This allows walls to be removed before checking the object or monster
+ * in the wall, and protects objects which are dropped by monsters killed
+ * in the blast, and allows the player to see all affects before he is
+ * killed or teleported away. The semantics of this method are open to
+ * various interpretations, but they seem to work well in practice.
+ *
+ * We process the blast area from ground-zero outwards to allow for better
+ * distribution of treasure dropped by monsters, and because it provides a
+ * pleasing visual effect at low cost.
+ *
+ * Note that the damage done by "ball" explosions decreases with distance.
+ * This decrease is rapid, grids at radius "dist" take "1/dist" damage.
+ *
+ * Notice the "napalm" effect of "beam" weapons. First they "project" to
+ * the target, and then the damage "flows" along this beam of destruction.
+ * The damage at every grid is the same as at the "center" of a "ball"
+ * explosion, since the "beam" grids are treated as if they ARE at the
+ * center of a "ball" explosion.
+ *
+ * Currently, specifying "beam" plus "ball" means that locations which are
+ * covered by the initial "beam", and also covered by the final "ball", except
+ * for the final grid (the epicenter of the ball), will be "hit twice", once
+ * by the initial beam, and once by the exploding ball. For the grid right
+ * next to the epicenter, this results in 150% damage being done. The center
+ * does not have this problem, for the same reason the final grid in a "beam"
+ * plus "bolt" does not -- it is explicitly removed. Simply removing "beam"
+ * grids which are covered by the "ball" will NOT work, as then they will
+ * receive LESS damage than they should. Do not combine "beam" with "ball".
+ *
+ * The array "gy[],gx[]" with current size "grids" is used to hold the
+ * collected locations of all grids in the "blast area" plus "beam path".
+ *
+ * Note the rather complex usage of the "gm[]" array. First, gm[0] is always
+ * zero. Second, for N>1, gm[N] is always the index (in gy[],gx[]) of the
+ * first blast grid (see above) with radius "N" from the blast center. Note
+ * that only the first gm[1] grids in the blast area thus take full damage.
+ * Also, note that gm[rad+1] is always equal to "grids", which is the total
+ * number of blast grids.
+ *
+ * Note that once the projection is complete, (y2,x2) holds the final location
+ * of bolts/beams, and the "epicenter" of balls.
+ *
+ * Note also that "rad" specifies the "inclusive" radius of projection blast,
+ * so that a "rad" of "one" actually covers 5 or 9 grids, depending on the
+ * implementation of the "distance" function. Also, a bolt can be properly
+ * viewed as a "ball" with a "rad" of "zero".
+ *
+ * Note that if no "target" is reached before the beam/bolt/ball travels the
+ * maximum distance allowed (MAX_RANGE), no "blast" will be induced. This
+ * may be relevant even for bolts, since they have a "1x1" mini-blast.
+ *
+ * Note that for consistency, we "pretend" that the bolt actually takes "time"
+ * to move from point A to point B, even if the player cannot see part of the
+ * projection path. Note that in general, the player will *always* see part
+ * of the path, since it either starts at the player or ends on the player.
+ *
+ * Hack -- we assume that every "projection" is "self-illuminating".
+ *
+ * Hack -- when only a single monster is affected, we automatically track
+ * (and recall) that monster, unless "PROJECT_JUMP" is used.
+ *
+ * Note that all projections now "explode" at their final destination, even
+ * if they were being projected at a more distant destination. This means
+ * that "ball" spells will *always* explode.
+ *
+ * Note that we must call "handle_stuff()" after affecting terrain features
+ * in the blast radius, in case the "illumination" of the grid was changed,
+ * and "update_view()" and "update_monsters()" need to be called.
+ */
+bool project(int who, int rad, int y, int x, int dam, int typ, int flg)
+{
+ int i, t, dist;
+
+ int y1, x1;
+ int y2, x2;
+
+ int dist_hack = 0;
+
+ int y_saver, x_saver; /* For reflecting monsters */
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+ /* Assume the player sees nothing */
+ bool notice = FALSE;
+
+ /* Assume the player has seen nothing */
+ bool visual = FALSE;
+
+ /* Assume the player has seen no blast grids */
+ bool drawn = FALSE;
+
+ /* Is the player blind? */
+ bool blind = (p_ptr->blind ? TRUE : FALSE);
+
+ /* Number of grids in the "path" */
+ int path_n = 0;
+
+ /* Actual grids in the "path" */
+ u16b path_g[1024];
+
+ /* Number of grids in the "blast area" (including the "beam" path) */
+ int grids = 0;
+
+ int effect = 0;
+
+ /* Coordinates of the affected grids */
+ byte gx[1024], gy[1024];
+
+ /* Encoded "radius" info (see above) */
+ byte gm[64];
+
+
+ /* Hack -- Jump to target */
+ if (flg & (PROJECT_JUMP))
+ {
+ x1 = x;
+ y1 = y;
+
+ /* Clear the flag */
+ flg &= ~(PROJECT_JUMP);
+ }
+
+ /* Start at player */
+ else if ((who <= 0) && ((who != -101) && (who != -100)))
+ {
+ x1 = p_ptr->px;
+ y1 = p_ptr->py;
+ }
+
+ /* Start at monster */
+ else if (who > 0)
+ {
+ x1 = m_list[who].fx;
+ y1 = m_list[who].fy;
+ }
+
+ /* Oops */
+ else
+ {
+ x1 = x;
+ y1 = y;
+ }
+
+ y_saver = y1;
+ x_saver = x1;
+
+ /* Default "destination" */
+ y2 = y;
+ x2 = x;
+
+
+ /* Hack -- verify stuff */
+ if (flg & (PROJECT_THRU))
+ {
+ if ((x1 == x2) && (y1 == y2))
+ {
+ flg &= ~(PROJECT_THRU);
+ }
+ }
+
+
+ /* Hack -- Assume there will be no blast (max radius 16) */
+ for (dist = 0; dist < 64; dist++) gm[dist] = 0;
+
+
+ /* Initial grid */
+ y = y1;
+ x = x1;
+ dist = 0;
+
+ /* Collect beam grids */
+ if (flg & (PROJECT_BEAM))
+ {
+ gy[grids] = y;
+ gx[grids] = x;
+ grids++;
+ }
+
+
+ /* Calculate the projection path */
+ if ((who == -101) || (who == -100))
+ path_n = 0;
+ else
+ path_n = project_path(path_g, MAX_RANGE, y1, x1, y2, x2, flg);
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Project along the path */
+ for (i = 0; i < path_n; ++i)
+ {
+ int oy = y;
+ int ox = x;
+
+ int ny = GRID_Y(path_g[i]);
+ int nx = GRID_X(path_g[i]);
+
+ /* Hack -- Balls explode before reaching walls */
+ if (!cave_floor_bold(ny, nx) && (rad > 0)) break;
+
+ /* Advance */
+ y = ny;
+ x = nx;
+
+ /* Collect beam grids */
+ if (flg & (PROJECT_BEAM))
+ {
+ gy[grids] = y;
+ gx[grids] = x;
+ grids++;
+ }
+
+ /* Only do visuals if requested */
+ if (!blind && !(flg & (PROJECT_HIDE)))
+ {
+ /* Only do visuals if the player can "see" the bolt */
+ if (panel_contains(y, x) && player_has_los_bold(y, x))
+ {
+ u16b p;
+
+ byte a;
+ char c;
+
+ /* Obtain the bolt pict */
+ p = bolt_pict(oy, ox, y, x, typ);
+
+ /* Extract attr/char */
+ a = PICT_A(p);
+ c = PICT_C(p);
+
+ /* Visual effects */
+ print_rel(c, a, y, x);
+ move_cursor_relative(y, x);
+ if (fresh_before) Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ lite_spot(y, x);
+ if (fresh_before) Term_fresh();
+
+ /* Display "beam" grids */
+ if (flg & (PROJECT_BEAM))
+ {
+ /* Obtain the explosion pict */
+ p = bolt_pict(y, x, y, x, typ);
+
+ /* Extract attr/char */
+ a = PICT_A(p);
+ c = PICT_C(p);
+
+ /* Visual effects */
+ print_rel(c, a, y, x);
+ }
+
+ /* Hack -- Activate delay */
+ visual = TRUE;
+ }
+
+ /* Hack -- delay anyway for consistency */
+ else if (visual)
+ {
+ /* Delay for consistency */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ }
+ }
+
+
+ /* Save the "blast epicenter" */
+ y2 = y;
+ x2 = x;
+
+ /* Start the "explosion" */
+ gm[0] = 0;
+
+ /* Hack -- make sure beams get to "explode" */
+ gm[1] = grids;
+
+ dist_hack = dist;
+
+ /* Explode */
+ if (TRUE)
+ {
+ /* Hack -- remove final beam grid */
+ if (flg & (PROJECT_BEAM))
+ {
+ grids--;
+ }
+
+ /* Determine the blast area, work from the inside out */
+ for (dist = 0; dist <= rad; dist++)
+ {
+ /* Scan the maximal blast area of radius "dist" */
+ for (y = y2 - dist; y <= y2 + dist; y++)
+ {
+ for (x = x2 - dist; x <= x2 + dist; x++)
+ {
+ /* Ignore "illegal" locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Enforce a "circular" explosion */
+ if (distance(y2, x2, y, x) != dist) continue;
+
+ /* Ball explosions are stopped by walls */
+ if (typ == GF_DISINTEGRATE)
+ {
+ if (cave_valid_bold(y, x) &&
+ (cave[y][x].feat < FEAT_PATTERN_START
+ || cave[y][x].feat > FEAT_PATTERN_XTRA2))
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Update some things -- similar to GF_KILL_WALL */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+ else
+ {
+ if (!los(y2, x2, y, x)) continue;
+ }
+
+ /* Save this grid */
+ gy[grids] = y;
+ gx[grids] = x;
+ grids++;
+ }
+ }
+
+ /* Encode some more "radius" info */
+ gm[dist + 1] = grids;
+ }
+ }
+
+
+ /* Speed -- ignore "non-explosions" */
+ if (!grids) return (FALSE);
+
+
+ /* Display the "blast area" if requested */
+ if (!blind && !(flg & (PROJECT_HIDE)))
+ {
+ /* Then do the "blast", from inside out */
+ for (t = 0; t <= rad; t++)
+ {
+ /* Dump everything with this radius */
+ for (i = gm[t]; i < gm[t + 1]; i++)
+ {
+ /* Extract the location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Only do visuals if the player can "see" the blast */
+ if (panel_contains(y, x) && player_has_los_bold(y, x))
+ {
+ u16b p;
+
+ byte a;
+ char c;
+
+ drawn = TRUE;
+
+ /* Obtain the explosion pict */
+ p = bolt_pict(y, x, y, x, typ);
+
+ /* Extract attr/char */
+ a = PICT_A(p);
+ c = PICT_C(p);
+
+ /* Visual effects -- Display */
+ print_rel(c, a, y, x);
+ }
+ }
+
+ /* Hack -- center the cursor */
+ move_cursor_relative(y2, x2);
+
+ /* Flush each "radius" seperately */
+ if (fresh_before) Term_fresh();
+
+ /* Delay (efficiently) */
+ if (visual || drawn)
+ {
+ Term_xtra(TERM_XTRA_DELAY, msec);
+ }
+ }
+
+ /* Flush the erasing */
+ if (drawn)
+ {
+ /* Erase the explosion drawn above */
+ for (i = 0; i < grids; i++)
+ {
+ /* Extract the location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Hack -- Erase if needed */
+ if (panel_contains(y, x) && player_has_los_bold(y, x))
+ {
+ lite_spot(y, x);
+ }
+ }
+
+ /* Hack -- center the cursor */
+ move_cursor_relative(y2, x2);
+
+ /* Flush the explosion */
+ if (fresh_before) Term_fresh();
+ }
+ }
+
+
+ /* Check features */
+ if (flg & (PROJECT_GRID))
+ {
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Effect ? */
+ if (flg & PROJECT_STAY)
+ {
+ effect = new_effect(typ, dam, project_time, p_ptr->py, p_ptr->px, rad, project_time_effect);
+ project_time = 0;
+ project_time_effect = 0;
+ }
+
+ /* Scan for features */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Affect the feature in that grid */
+ if (project_f(who, dist, y, x, dam, typ)) notice = TRUE;
+
+ /* Effect ? */
+ if (flg & PROJECT_STAY)
+ {
+ cave[y][x].effect = effect;
+ lite_spot(y, x);
+ }
+ }
+ }
+
+
+ /* Update stuff if needed */
+ if (p_ptr->update) update_stuff();
+
+
+ /* Check objects */
+ if (flg & (PROJECT_ITEM))
+ {
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Scan for objects */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Affect the object in the grid */
+ if (project_o(who, dist, y, x, dam, typ)) notice = TRUE;
+ }
+ }
+
+
+ /* Check monsters */
+ if (flg & (PROJECT_KILL))
+ {
+ /* Mega-Hack */
+ project_m_n = 0;
+ project_m_x = 0;
+ project_m_y = 0;
+
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Scan for monsters */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ if (grids > 1)
+ {
+ /* Affect the monster in the grid */
+ if (project_m(who, dist, y, x, dam, typ)) notice = TRUE;
+ }
+ else
+ {
+ monster_race *ref_ptr = race_inf(&m_list[cave[y][x].m_idx]);
+
+ if ((ref_ptr->flags2 & (RF2_REFLECTING)) && (randint(10) != 1)
+ && (dist_hack > 1))
+ {
+ int t_y, t_x;
+ int max_attempts = 10;
+
+ /* Choose 'new' target */
+ do
+ {
+ t_y = y_saver - 1 + randint(3);
+ t_x = x_saver - 1 + randint(3);
+ max_attempts--;
+ }
+
+ while (max_attempts && in_bounds2(t_y, t_x) &&
+ !(los(y, x, t_y, t_x)));
+
+ if (max_attempts < 1)
+ {
+ t_y = y_saver;
+ t_x = x_saver;
+ }
+
+ if (m_list[cave[y][x].m_idx].ml)
+ {
+ msg_print("The attack bounces!");
+ ref_ptr->r_flags2 |= RF2_REFLECTING;
+ }
+
+ project(cave[y][x].m_idx, 0, t_y, t_x, dam, typ, flg);
+ }
+ else
+ {
+ if (project_m(who, dist, y, x, dam, typ)) notice = TRUE;
+ }
+ }
+ }
+
+ /* Player affected one monster (without "jumping") */
+ if ((who < 0) && (project_m_n == 1) && !(flg & (PROJECT_JUMP)))
+ {
+ /* Location */
+ x = project_m_x;
+ y = project_m_y;
+
+ /* Track if possible */
+ if (cave[y][x].m_idx > 0)
+ {
+ monster_type *m_ptr = &m_list[cave[y][x].m_idx];
+
+ /* Hack -- auto-recall */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack - auto-track */
+ if (m_ptr->ml) health_track(cave[y][x].m_idx);
+ }
+ }
+ }
+
+
+ /* Check player */
+ if (flg & (PROJECT_KILL))
+ {
+ /* Start with "dist" of zero */
+ dist = 0;
+
+ /* Scan for player */
+ for (i = 0; i < grids; i++)
+ {
+ /* Hack -- Notice new "dist" values */
+ if (gm[dist + 1] == i) dist++;
+
+ /* Get the grid location */
+ y = gy[i];
+ x = gx[i];
+
+ /* Affect the player */
+ if (project_p(who, dist, y, x, dam, typ, rad)) notice = TRUE;
+ }
+ }
+
+ /* Return "something was noticed" */
+ return (notice);
+}
+
+
+
+/*
+ * Potions "smash open" and cause an area effect when
+ * (1) they are shattered while in the player's inventory,
+ * due to cold (etc) attacks;
+ * (2) they are thrown at a monster, or obstacle;
+ * (3) they are shattered by a "cold ball" or other such spell
+ * while lying on the floor.
+ *
+ * Arguments:
+ * who --- who caused the potion to shatter (0=player)
+ * potions that smash on the floor are assumed to
+ * be caused by no-one (who = 1), as are those that
+ * shatter inside the player inventory.
+ * (Not anymore -- I changed this; TY)
+ * y, x --- coordinates of the potion (or player if
+ * the potion was in her inventory);
+ * o_ptr --- pointer to the potion object.
+ */
+bool potion_smash_effect(int who, int y, int x, int o_sval)
+{
+ int radius = 2;
+ int dt = 0;
+ int dam = 0;
+ bool ident = FALSE;
+ bool angry = FALSE;
+
+ switch (o_sval)
+ {
+ case SV_POTION_SALT_WATER:
+ case SV_POTION_SLIME_MOLD:
+ case SV_POTION_LOSE_MEMORIES:
+ case SV_POTION_DEC_STR:
+ case SV_POTION_DEC_INT:
+ case SV_POTION_DEC_WIS:
+ case SV_POTION_DEC_DEX:
+ case SV_POTION_DEC_CON:
+ case SV_POTION_DEC_CHR:
+ case SV_POTION_WATER: /* perhaps a 'water' attack? */
+ case SV_POTION_APPLE_JUICE:
+ return TRUE;
+
+ case SV_POTION_INFRAVISION:
+ case SV_POTION_DETECT_INVIS:
+ case SV_POTION_SLOW_POISON:
+ case SV_POTION_CURE_POISON:
+ case SV_POTION_BOLDNESS:
+ case SV_POTION_RESIST_HEAT:
+ case SV_POTION_RESIST_COLD:
+ case SV_POTION_HEROISM:
+ case SV_POTION_BESERK_STRENGTH:
+ case SV_POTION_RESTORE_EXP:
+ case SV_POTION_RES_STR:
+ case SV_POTION_RES_INT:
+ case SV_POTION_RES_WIS:
+ case SV_POTION_RES_DEX:
+ case SV_POTION_RES_CON:
+ case SV_POTION_RES_CHR:
+ case SV_POTION_INC_STR:
+ case SV_POTION_INC_INT:
+ case SV_POTION_INC_WIS:
+ case SV_POTION_INC_DEX:
+ case SV_POTION_INC_CON:
+ case SV_POTION_INC_CHR:
+ case SV_POTION_AUGMENTATION:
+ case SV_POTION_ENLIGHTENMENT:
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ case SV_POTION_SELF_KNOWLEDGE:
+ case SV_POTION_EXPERIENCE:
+ case SV_POTION_RESISTANCE:
+ case SV_POTION_INVULNERABILITY:
+ case SV_POTION_NEW_LIFE:
+ /* All of the above potions have no effect when shattered */
+ return FALSE;
+ case SV_POTION_SLOWNESS:
+ dt = GF_OLD_SLOW;
+ dam = 5;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_POISON:
+ dt = GF_POIS;
+ dam = 3;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_BLINDNESS:
+ dt = GF_DARK;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_CONFUSION: /* Booze */
+ dt = GF_OLD_CONF;
+ ident = TRUE;
+ angry = TRUE;
+ break;
+ case SV_POTION_SLEEP:
+ dt = GF_OLD_SLEEP;
+ angry = TRUE;
+ ident = TRUE;
+ break;
+ case SV_POTION_RUINATION:
+ case SV_POTION_DETONATIONS:
+ dt = GF_SHARDS;
+ dam = damroll(25, 25);
+ angry = TRUE;
+ ident = TRUE;
+ break;
+ case SV_POTION_DEATH:
+ dt = GF_MANA; /* !! */
+ dam = damroll(10, 10);
+ angry = TRUE;
+ radius = 1;
+ ident = TRUE;
+ break;
+ case SV_POTION_SPEED:
+ dt = GF_OLD_SPEED;
+ ident = TRUE;
+ break;
+ case SV_POTION_CURE_LIGHT:
+ dt = GF_OLD_HEAL;
+ dam = damroll(2, 3);
+ ident = TRUE;
+ break;
+ case SV_POTION_CURE_SERIOUS:
+ dt = GF_OLD_HEAL;
+ dam = damroll(4, 3);
+ ident = TRUE;
+ break;
+ case SV_POTION_CURE_CRITICAL:
+ case SV_POTION_CURING:
+ dt = GF_OLD_HEAL;
+ dam = damroll(6, 3);
+ ident = TRUE;
+ break;
+ case SV_POTION_HEALING:
+ dt = GF_OLD_HEAL;
+ dam = damroll(10, 10);
+ ident = TRUE;
+ break;
+ case SV_POTION_STAR_HEALING:
+ case SV_POTION_LIFE:
+ dt = GF_OLD_HEAL;
+ dam = damroll(50, 50);
+ radius = 1;
+ ident = TRUE;
+ break;
+ case SV_POTION_RESTORE_MANA: /* MANA */
+ dt = GF_MANA;
+ dam = damroll(10, 10);
+ radius = 1;
+ ident = TRUE;
+ break;
+ default:
+ /* Do nothing */
+ ;
+ }
+
+ (void) project(who, radius, y, x, dam, dt,
+ (PROJECT_JUMP | PROJECT_ITEM | PROJECT_KILL));
+
+ /* XXX those potions that explode need to become "known" */
+ return angry;
+}
+
+/* This is for Thaumaturgy */
+static const int destructive_attack_types[10] =
+{
+ GF_KILL_WALL,
+ GF_KILL_WALL,
+ GF_KILL_WALL,
+ GF_STONE_WALL,
+ GF_STONE_WALL,
+ GF_STONE_WALL,
+ GF_DESTRUCTION,
+ GF_DESTRUCTION,
+ GF_DESTRUCTION,
+ GF_DESTRUCTION,
+};
+
+/* Also for Power-mages */
+static const int attack_types[25] =
+{
+ GF_ARROW,
+ GF_MISSILE,
+ GF_MANA,
+ GF_WATER,
+ GF_PLASMA,
+ GF_METEOR,
+ GF_ICE,
+ GF_GRAVITY,
+ GF_INERTIA,
+ GF_FORCE,
+ GF_TIME,
+ GF_ACID,
+ GF_ELEC,
+ GF_FIRE,
+ GF_COLD,
+ GF_POIS,
+ GF_LITE,
+ GF_DARK,
+ GF_CONFUSION,
+ GF_SOUND,
+ GF_SHARDS,
+ GF_NEXUS,
+ GF_NETHER,
+ GF_CHAOS,
+ GF_DISENCHANT,
+};
+
+/*
+ * Describe the attack using normal names.
+ */
+
+void describe_attack_fully(int type, char* r)
+{
+ switch (type)
+ {
+ case GF_ARROW:
+ strcpy(r, "arrows");
+ break;
+ case GF_MISSILE:
+ strcpy(r, "magic missiles");
+ break;
+ case GF_MANA:
+ strcpy(r, "mana");
+ break;
+ case GF_LITE_WEAK:
+ strcpy(r, "light");
+ break;
+ case GF_DARK_WEAK:
+ strcpy(r, "dark");
+ break;
+ case GF_WATER:
+ strcpy(r, "water");
+ break;
+ case GF_PLASMA:
+ strcpy(r, "plasma");
+ break;
+ case GF_METEOR:
+ strcpy(r, "meteors");
+ break;
+ case GF_ICE:
+ strcpy(r, "ice");
+ break;
+ case GF_GRAVITY:
+ strcpy(r, "gravity");
+ break;
+ case GF_INERTIA:
+ strcpy(r, "inertia");
+ break;
+ case GF_FORCE:
+ strcpy(r, "force");
+ break;
+ case GF_TIME:
+ strcpy(r, "pure time");
+ break;
+ case GF_ACID:
+ strcpy(r, "acid");
+ break;
+ case GF_ELEC:
+ strcpy(r, "lightning");
+ break;
+ case GF_FIRE:
+ strcpy(r, "flames");
+ break;
+ case GF_COLD:
+ strcpy(r, "cold");
+ break;
+ case GF_POIS:
+ strcpy(r, "poison");
+ break;
+ case GF_LITE:
+ strcpy(r, "pure light");
+ break;
+ case GF_DARK:
+ strcpy(r, "pure dark");
+ break;
+ case GF_CONFUSION:
+ strcpy(r, "confusion");
+ break;
+ case GF_SOUND:
+ strcpy(r, "sound");
+ break;
+ case GF_SHARDS:
+ strcpy(r, "shards");
+ break;
+ case GF_NEXUS:
+ strcpy(r, "nexus");
+ break;
+ case GF_NETHER:
+ strcpy(r, "nether");
+ break;
+ case GF_CHAOS:
+ strcpy(r, "chaos");
+ break;
+ case GF_DISENCHANT:
+ strcpy(r, "disenchantment");
+ break;
+ case GF_KILL_WALL:
+ strcpy(r, "wall destruction");
+ break;
+ case GF_KILL_DOOR:
+ strcpy(r, "door destruction");
+ break;
+ case GF_KILL_TRAP:
+ strcpy(r, "trap destruction");
+ break;
+ case GF_STONE_WALL:
+ strcpy(r, "wall creation");
+ break;
+ case GF_MAKE_DOOR:
+ strcpy(r, "door creation");
+ break;
+ case GF_MAKE_TRAP:
+ strcpy(r, "trap creation");
+ break;
+ case GF_DESTRUCTION:
+ strcpy(r, "destruction");
+ break;
+
+ default:
+ strcpy(r, "something unknown");
+ break;
+ }
+}
+
+/*
+ * Give a randomly-generated spell a name.
+ * Note that it only describes the first effect!
+ */
+
+static void name_spell(random_spell* s_ptr)
+{
+ char buff[30];
+ cptr buff2 = "???";
+
+ if (s_ptr->proj_flags & PROJECT_STOP && s_ptr->radius == 0)
+ {
+ buff2 = "Bolt";
+ }
+ else if (s_ptr->proj_flags & PROJECT_BEAM)
+ {
+ buff2 = "Beam";
+ }
+ else if (s_ptr->proj_flags & PROJECT_STOP && s_ptr->radius > 0)
+ {
+ buff2 = "Ball";
+ }
+ else if (s_ptr->proj_flags & PROJECT_BLAST)
+ {
+ buff2 = "Blast";
+ }
+ else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER)
+ {
+ buff2 = "Area";
+ }
+ else if (s_ptr->proj_flags & PROJECT_VIEWABLE)
+ {
+ buff2 = "View";
+ }
+
+ describe_attack_fully(s_ptr->GF, buff);
+ strnfmt(s_ptr->name, 30, "%s - %s", buff2, buff);
+}
+
+void generate_spell(int plev)
+{
+ random_spell* rspell;
+ int dice, sides, chance, mana, power;
+ bool destruc_gen = FALSE;
+ bool simple_gen = TRUE;
+ bool ball_desc = FALSE;
+
+ if (spell_num == MAX_SPELLS) return;
+
+ rspell = &random_spells[spell_num];
+
+ power = rand_int(15);
+
+ dice = plev / 5;
+ sides = plev * 2;
+ mana = plev;
+
+ /* Make the spell more or less powerful. */
+ dice += power / 5;
+ sides += power / 2;
+ mana += (plev * power) / 8;
+
+ /* Stay within reasonable bounds. */
+ if (dice < 1) dice = 1;
+ if (dice > 10) dice = 10;
+
+ if (sides < 5) sides = 5;
+ if (sides > 100) sides = 100;
+
+ if (mana < 1) mana = 1;
+
+ rspell->level = plev;
+ rspell->mana = mana;
+ rspell->untried = TRUE;
+
+ /* Spells are always maximally destructive. */
+ rspell->proj_flags = PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID;
+
+ chance = randint(100);
+
+ /* Hack -- Always start with Magic Missile or derivative at lev. 1 */
+ if (plev == 1 || chance < 25)
+ {
+ rspell->proj_flags |= PROJECT_STOP;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ rspell->radius = 0;
+ }
+ else if (chance < 50)
+ {
+ rspell->proj_flags |= PROJECT_BEAM;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ rspell->radius = 0;
+ }
+ else if (chance < 76)
+ {
+ rspell->proj_flags |= PROJECT_STOP;
+ rspell->radius = dice;
+ rspell->dam_dice = sides;
+ rspell->dam_sides = 1;
+ ball_desc = TRUE;
+ }
+ else if (chance < 83)
+ {
+ rspell->proj_flags |= PROJECT_BLAST;
+ rspell->radius = sides / 3;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+
+ destruc_gen = TRUE;
+ simple_gen = FALSE;
+ }
+ else if (chance < 90)
+ {
+ rspell->proj_flags |= PROJECT_METEOR_SHOWER;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ rspell->radius = sides / 3;
+ if (rspell->radius < 4) rspell->radius = 4;
+
+ destruc_gen = TRUE;
+ }
+ else
+ {
+ rspell->proj_flags |= PROJECT_VIEWABLE;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ }
+
+ /* Both a destructive and a simple spell requested --
+ * pick one or the other. */
+ if (destruc_gen && simple_gen)
+ {
+ if (magik(25))
+ {
+ simple_gen = FALSE;
+ }
+ else
+ {
+ destruc_gen = FALSE;
+ }
+ }
+
+ /* Pick a simple spell */
+ if (simple_gen)
+ {
+ rspell->GF = attack_types[rand_int(25)];
+ }
+ /* Pick a destructive spell */
+ else
+ {
+ rspell->GF = destructive_attack_types[rand_int(10)];
+ }
+
+ /* Give the spell a name. */
+ name_spell(rspell);
+ if (ball_desc)
+ {
+ /* 30 character limit on the string! */
+ sprintf(rspell->desc, "Dam: %d, Rad: %d, Pow: %d",
+ sides, dice, power);
+ }
+ else
+ {
+ sprintf(rspell->desc, "Damage: %dd%d, Power: %d",
+ dice, sides, power);
+ }
+
+ spell_num++;
+}
+
+/*
+ * Polymorph a monster at given location.
+ */
+s16b do_poly_monster(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr;
+
+ s16b hack_m_idx;
+ s16b old_m_idx;
+ s16b new_m_idx = 0;
+
+ s16b new_r_idx;
+
+ /* Get a "old" monster */
+ old_m_idx = c_ptr->m_idx;
+
+ /* Giga-Hack -- Remember monster */
+ hack_m_idx = old_m_idx;
+
+ /* Get a monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Pick a "new" monster race */
+ new_r_idx = poly_r_idx(m_ptr->r_idx);
+
+ /* No polymorph happend */
+ if (new_r_idx == m_ptr->r_idx) return 0;
+
+ /* Giga-Hack -- Removes the moster XXX XXX XXX XXX */
+ c_ptr->m_idx = 0;
+
+ /*
+ * Handle polymorph --
+ * Create a new monster (no groups)
+ */
+ if (place_monster_aux(y, x, new_r_idx, FALSE, FALSE, m_ptr->status))
+ {
+ /* Get a "new" monster */
+ new_m_idx = c_ptr->m_idx;
+
+ /* Giga-Hack -- Remember "new" monster */
+ hack_m_idx = new_m_idx;
+
+ /* "Kill" the "old" monster */
+ delete_monster_idx(old_m_idx);
+
+ p_ptr->redraw |= (PR_MAP);
+ }
+
+ /* Giga-Hack -- restore saved monster XXX XXX XXX */
+ c_ptr->m_idx = hack_m_idx;
+
+ return new_m_idx;
+}
diff --git a/src/spells2.c b/src/spells2.c
new file mode 100644
index 00000000..06f5b443
--- /dev/null
+++ b/src/spells2.c
@@ -0,0 +1,8214 @@
+/* File: spells2.c */
+
+/* Purpose: Spell code (part 2) */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+#define WEIRD_LUCK 12
+#define BIAS_LUCK 20
+/*
+ * Bias luck needs to be higher than weird luck,
+ * since it is usually tested several times...
+ */
+
+void summon_dragon_riders();
+
+
+/*
+ * Grow things
+ */
+void grow_things(s16b type, int rad)
+{
+ int a, i, j;
+
+ for (a = 0; a < rad * rad + 11; a++)
+ {
+ i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+
+ if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
+ if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
+
+ if (cave_clean_bold(p_ptr->py + j, p_ptr->px + i))
+ {
+ cave_set_feat(p_ptr->py + j, p_ptr->px + i, type);
+ }
+ }
+}
+
+/*
+ * Grow trees
+ */
+void grow_trees(int rad)
+{
+ int a, i, j;
+
+ for (a = 0; a < rad * rad + 11; a++)
+ {
+ i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+
+ if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
+ if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
+
+ if (cave_clean_bold(p_ptr->py + j, p_ptr->px + i) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_SUPPORT_GROWTH))
+ {
+ cave_set_feat(p_ptr->py + j, p_ptr->px + i, FEAT_TREES);
+ }
+ }
+}
+
+/*
+ * Grow grass
+ */
+void grow_grass(int rad)
+{
+ int a, i, j;
+
+ for (a = 0; a < rad * rad + 11; a++)
+ {
+ i = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+ j = (Rand_mod((rad * 2) + 1)-rad + Rand_mod((rad * 2) + 1)-rad) / 2;
+
+ if (!in_bounds(p_ptr->py + j, p_ptr->px + i)) continue;
+ if (distance(p_ptr->py, p_ptr->px, p_ptr->py + j, p_ptr->px + i) > rad) continue;
+
+ if (cave_clean_bold(p_ptr->py + j, p_ptr->px + i) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_SUPPORT_GROWTH))
+ {
+ cave_set_feat(p_ptr->py + j, p_ptr->px + i, FEAT_GRASS);
+ }
+ }
+}
+
+/*
+ * Increase players hit points, notice effects
+ */
+bool hp_player(int num)
+{
+ bool dead = p_ptr->chp < 0;
+
+ /* Healing needed */
+ if (p_ptr->chp < p_ptr->mhp)
+ {
+ /* Gain hitpoints */
+ p_ptr->chp += num;
+
+ /* Enforce maximum */
+ if ((p_ptr->chp >= p_ptr->mhp) ||
+ /* prevent wrapping */
+ (!dead && (p_ptr->chp < 0)))
+ {
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Heal 0-4 */
+ if (num < 5)
+ {
+ msg_print("You feel a little better.");
+ }
+
+ /* Heal 5-14 */
+ else if (num < 15)
+ {
+ msg_print("You feel better.");
+ }
+
+ /* Heal 15-34 */
+ else if (num < 35)
+ {
+ msg_print("You feel much better.");
+ }
+
+ /* Heal 35+ */
+ else
+ {
+ msg_print("You feel very good.");
+ }
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Ignore */
+ return (FALSE);
+}
+
+
+
+/*
+ * Leave a "glyph of warding" which prevents monster movement
+ */
+void warding_glyph(void)
+{
+ /* XXX XXX XXX */
+ if (!cave_clean_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("The object resists the spell.");
+ return;
+ }
+
+ /* Create a glyph */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_GLYPH);
+}
+
+void explosive_rune(void)
+{
+ /* XXX XXX XXX */
+ if (!cave_clean_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("The object resists the spell.");
+ return;
+ }
+
+ /* Create a glyph */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MINOR_GLYPH);
+}
+
+
+
+/*
+ * Array of stat "descriptions"
+ */
+static cptr desc_stat_pos[] =
+{
+ "strong",
+ "smart",
+ "wise",
+ "dextrous",
+ "healthy",
+ "cute"
+};
+
+/*
+ * Array of long descriptions of stat
+ */
+
+static cptr long_desc_stat[] =
+{
+ "strength",
+ "intelligence",
+ "wisdom",
+ "dexterity",
+ "constitution",
+ "charisma"
+};
+
+/*
+ * Array of stat "descriptions"
+ */
+static cptr desc_stat_neg[] =
+{
+ "weak",
+ "stupid",
+ "naive",
+ "clumsy",
+ "sickly",
+ "ugly"
+};
+
+
+/*
+ * Lose a "point"
+ */
+bool do_dec_stat(int stat, int mode)
+{
+ bool sust = FALSE;
+
+ /* Access the "sustain" */
+ switch (stat)
+ {
+ case A_STR:
+ if (p_ptr->sustain_str) sust = TRUE;
+ break;
+ case A_INT:
+ if (p_ptr->sustain_int) sust = TRUE;
+ break;
+ case A_WIS:
+ if (p_ptr->sustain_wis) sust = TRUE;
+ break;
+ case A_DEX:
+ if (p_ptr->sustain_dex) sust = TRUE;
+ break;
+ case A_CON:
+ if (p_ptr->sustain_con) sust = TRUE;
+ break;
+ case A_CHR:
+ if (p_ptr->sustain_chr) sust = TRUE;
+ break;
+ }
+
+ /* Sustain */
+ if (sust)
+ {
+ /* Message */
+ msg_format("You feel %s for a moment, but the feeling passes.",
+ desc_stat_neg[stat]);
+
+ /* Notice effect */
+ return (TRUE);
+ }
+
+ /* Attempt to reduce the stat */
+ if (dec_stat(stat, 10, mode))
+ {
+ /* Message */
+ msg_format("You feel very %s.", desc_stat_neg[stat]);
+
+ /* Notice effect */
+ return (TRUE);
+ }
+
+ /* Nothing obvious */
+ return (FALSE);
+}
+
+
+/*
+ * Restore lost "points" in a stat
+ */
+bool do_res_stat(int stat, bool full)
+{
+ /* Keep a copy of the current stat, so we can evaluate it if necessary */
+ int cur_stat = p_ptr->stat_cur[stat];
+
+ /* Attempt to increase */
+ if (res_stat(stat, full))
+ {
+ /* Message, depending on whether we got stronger or weaker */
+ if (cur_stat > p_ptr->stat_max[stat])
+ {
+ msg_format("You feel your %s boost drain away.", long_desc_stat[stat]);
+ }
+ else
+ {
+ msg_format("You feel less %s.", desc_stat_neg[stat]);
+ }
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Nothing obvious */
+ return (FALSE);
+}
+
+
+/*
+ * Gain a "point" in a stat
+ */
+bool do_inc_stat(int stat)
+{
+ bool res;
+
+ /* Restore strength */
+ res = res_stat(stat, TRUE);
+
+ /* Attempt to increase */
+ if (inc_stat(stat))
+ {
+ /* Message */
+ msg_format("Wow! You feel very %s!", desc_stat_pos[stat]);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Restoration worked */
+ if (res)
+ {
+ /* Message */
+ msg_format("You feel less %s.", desc_stat_neg[stat]);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Nothing obvious */
+ return (FALSE);
+}
+
+
+
+/*
+ * Identify everything being carried.
+ * Done by a potion of "self knowledge".
+ */
+void identify_pack(void)
+{
+ int i;
+
+ /* Simply identify and know every item */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Aware and Known */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", i, "normal");
+ }
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+/*
+ * common portions of identify_fully and identify_pack_fully
+ */
+static void make_item_fully_identified(object_type *o_ptr)
+{
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Mark the item as fully known */
+ o_ptr->ident |= (IDENT_MENTAL);
+
+ /* For those with alchemist skills, learn how to create it */
+ alchemist_learn_object(o_ptr);
+}
+
+/*
+ * Identify everything being carried.
+ * Done by a potion of "self knowledge".
+ */
+void identify_pack_fully(void)
+{
+ int i;
+
+ /* Simply identify and know every item */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ make_item_fully_identified(o_ptr);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", i, "full");
+ }
+
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+/*
+ * Used by the "enchant" function (chance of failure)
+ * (modified for Zangband, we need better stuff there...) -- TY
+ */
+static int enchant_table[16] =
+{
+ 0, 10, 50, 100, 200,
+ 300, 400, 500, 650, 800,
+ 950, 987, 993, 995, 998,
+ 1000
+};
+
+bool remove_curse_object(object_type *o_ptr, bool all)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) return FALSE;
+
+ /* Uncursed already */
+ if (!cursed_p(o_ptr)) return FALSE;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Heavily Cursed Items need a special spell */
+ if (!all && (f3 & (TR3_HEAVY_CURSE))) return FALSE;
+
+ /* Perma-Cursed Items can NEVER be uncursed */
+ if (f3 & (TR3_PERMA_CURSE)) return FALSE;
+
+ /* Uncurse it */
+ o_ptr->ident &= ~(IDENT_CURSED);
+
+ /* Hack -- Assume felt */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ /* Take note */
+ o_ptr->sense = SENSE_UNCURSED;
+
+ /* Reverse the curse effect */
+ /* jk - scrolls of *remove curse* have a 1 in (55-level chance to */
+ /* reverse the curse effects - a ring of damage(-15) {cursed} then */
+ /* becomes a ring of damage (+15) */
+ /* this does not go for artifacts - a Sword of Mormegil +40,+60 would */
+ /* be somewhat unbalancing */
+ /* due to the nature of this procedure, it only works on cursed items */
+ /* ie you get only one chance! */
+ if ((randint(55-p_ptr->lev) == 1) && !artifact_p(o_ptr))
+ {
+ if (o_ptr->to_a < 0) o_ptr->to_a = -o_ptr->to_a;
+ if (o_ptr->to_h < 0) o_ptr->to_h = -o_ptr->to_h;
+ if (o_ptr->to_d < 0) o_ptr->to_d = -o_ptr->to_d;
+ if (o_ptr->pval < 0) o_ptr->pval = -o_ptr->pval;
+ }
+
+ /* Recalculate the bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+
+ return TRUE;
+}
+
+/*
+ * Removes curses from items in inventory
+ *
+ * Note that Items which are "Perma-Cursed" (The One Ring,
+ * The Crown of Morgoth) can NEVER be uncursed.
+ *
+ * Note that if "all" is FALSE, then Items which are
+ * "Heavy-Cursed" (Mormegil, Calris, and Weapons of Morgul)
+ * will not be uncursed.
+ */
+static int remove_curse_aux(int all)
+{
+ int i, cnt = 0;
+
+ /* Attempt to uncurse items being worn */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!remove_curse_object(o_ptr, all)) continue;
+
+ /* Count the uncursings */
+ cnt++;
+ }
+
+ /* Return "something uncursed" */
+ return (cnt);
+}
+
+
+/*
+ * Remove most curses
+ */
+bool remove_curse(void)
+{
+ return (remove_curse_aux(FALSE) ? TRUE : FALSE);
+}
+
+/*
+ * Remove all curses
+ */
+bool remove_all_curse(void)
+{
+ return (remove_curse_aux(TRUE) ? TRUE : FALSE);
+}
+
+
+
+/*
+ * Restores any drained experience
+ */
+bool restore_level(void)
+{
+ /* Restore experience */
+ if (p_ptr->exp < p_ptr->max_exp)
+ {
+ /* Message */
+ msg_print("You feel your life energies returning.");
+
+ /* Restore the experience */
+ p_ptr->exp = p_ptr->max_exp;
+
+ /* Check the experience */
+ check_experience();
+
+ /* Did something */
+ return (TRUE);
+ }
+
+ /* No effect */
+ return (FALSE);
+}
+
+
+bool alchemy(void) /* Turns an object into gold, gain some of its value in a shop */
+{
+ int item, amt = 1;
+ int old_number;
+ long price;
+ bool force = FALSE;
+ object_type *o_ptr;
+ char o_name[80];
+ char out_val[160];
+
+ cptr q, s;
+
+ /* Hack -- force destruction */
+ if (command_arg > 0) force = TRUE;
+
+ /* Get an item */
+ q = "Turn which item to gold? ";
+ s = "You have nothing to turn to gold.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return FALSE;
+ }
+
+
+ /* Describe the object */
+ old_number = o_ptr->number;
+ o_ptr->number = amt;
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_ptr->number = old_number;
+
+ /* Verify unless quantity given */
+ if (!force)
+ {
+ if (!((auto_destroy) && (object_value(o_ptr) < 1)))
+ {
+ /* Make a verification */
+ sprintf(out_val, "Really turn %s to gold? ", o_name);
+ if (!get_check(out_val)) return FALSE;
+ }
+ }
+
+ /* Artifacts cannot be destroyed */
+ if (artifact_p(o_ptr) || o_ptr->art_name)
+ {
+ byte feel = SENSE_SPECIAL;
+
+ /* Message */
+ msg_format("You fail to turn %s to gold!", o_name);
+
+ /* Hack -- Handle icky artifacts */
+ if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE;
+
+ /* Hack -- inscribe the artifact */
+ o_ptr->sense = feel;
+
+ /* We have "felt" it (again) */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ return FALSE;
+ }
+
+ price = object_value_real(o_ptr);
+
+ if (price <= 0)
+ /* Message */
+ msg_format("You turn %s to fool's gold.", o_name);
+ else
+ {
+ price /= 3;
+
+ if (amt > 1) price *= amt;
+
+ msg_format("You turn %s to %ld coins worth of gold.", o_name, price);
+ p_ptr->au += price;
+
+ /* Redraw gold */
+ p_ptr->redraw |= (PR_GOLD);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Eliminate the item (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Eliminate the item (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -amt);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ return TRUE;
+}
+
+
+
+
+/*
+ * self-knowledge... idea from nethack. Useful for determining powers and
+ * resistances of items. It saves the screen, clears it, then starts listing
+ * attributes, a screenful at a time. (There are a LOT of attributes to
+ * list. It will probably take 2 or 3 screens for a powerful character whose
+ * using several artifacts...) -CFT
+ *
+ * It is now a lot more efficient. -BEN-
+ *
+ * See also "identify_fully()".
+ *
+ * XXX XXX XXX Use the "show_file()" method, perhaps.
+ */
+void self_knowledge(FILE *fff)
+{
+ int i = 0, j, k;
+
+ u32b f1 = 0L, f2 = 0L, f3 = 0L, f4 = 0L, f5 = 0L, esp = 0L;
+
+ int iter; /* Iterator for a loop */
+
+ object_type *o_ptr;
+
+ char Dummy[80];
+
+ cptr info[200];
+
+ strcpy (Dummy, "");
+
+ /* Acquire item flags from equipment */
+ for (k = INVEN_WIELD; k < INVEN_TOTAL; k++)
+ {
+ u32b t1, t2, t3, t4, t5, esp;
+
+ o_ptr = &p_ptr->inventory[k];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &t1, &t2, &t3, &t4, &t5, &esp);
+
+ /* Extract flags */
+ f1 |= t1;
+ f2 |= t2;
+ f3 |= t3;
+ }
+
+ if (death)
+ {
+ static char buf[250];
+
+ sprintf(buf, "You are dead, killed by %s %s.",
+ died_from, describe_player_location());
+ info[i++] = buf;
+ }
+
+ /* Racial powers... */
+ if (p_ptr->body_monster != 0)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ if (r_ptr->flags1 & RF1_CHAR_CLEAR ||
+ r_ptr->flags1 & RF1_ATTR_CLEAR)
+ info[i++] = "You are transparent.";
+ if ((r_ptr->flags1 & RF1_CHAR_MULTI) ||
+ (r_ptr->flags2 & RF2_SHAPECHANGER))
+ info[i++] = "Your form constantly changes.";
+ if (r_ptr->flags1 & RF1_ATTR_MULTI)
+ info[i++] = "Your color constantly changes.";
+ if (r_ptr->flags1 & RF1_NEVER_BLOW)
+ info[i++] = "You do not have a physical weapon.";
+ if (r_ptr->flags1 & RF1_NEVER_MOVE)
+ info[i++] = "You cannot move.";
+ if ((r_ptr->flags1 & RF1_RAND_25) &&
+ (r_ptr->flags1 & RF1_RAND_50))
+ info[i++] = "You move extremely erratically.";
+ else if (r_ptr->flags1 & RF1_RAND_50)
+ info[i++] = "You move somewhat erratically.";
+ else if (r_ptr->flags1 & RF1_RAND_25)
+ info[i++] = "You move a bit erratically.";
+ if (r_ptr->flags2 & RF2_STUPID)
+ info[i++] = "You are very stupid (INT -4).";
+ if (r_ptr->flags2 & RF2_SMART)
+ info[i++] = "You are very smart (INT +4).";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_CAN_SPEAK)
+ info[i++] = "You can speak.";
+ else
+ info[i++] = "You cannot speak.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_COLD_BLOOD)
+ info[i++] = "You are cold blooded.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ info[i++] = "You have an empty mind.";
+ if (r_ptr->flags2 & RF2_WEIRD_MIND)
+ info[i++] = "You have a weird mind.";
+ if (r_ptr->flags4 & RF4_MULTIPLY)
+ info[i++] = "You can multiply.";
+ if (r_ptr->flags2 & RF2_POWERFUL)
+ info[i++] = "You have strong breath.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_ELDRITCH_HORROR)
+ info[i++] = "You are an eldritch horror.";
+ if (r_ptr->flags2 & RF2_OPEN_DOOR)
+ info[i++] = "You can open doors.";
+ else
+ info[i++] = "You cannot open doors.";
+ if (r_ptr->flags2 & RF2_BASH_DOOR)
+ info[i++] = "You can bash doors.";
+ else
+ info[i++] = "You cannot bash doors.";
+ if (r_ptr->flags2 & RF2_PASS_WALL)
+ info[i++] = "You can pass walls.";
+ if (r_ptr->flags2 & RF2_KILL_WALL)
+ info[i++] = "You destroy walls.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_MOVE_BODY)
+ info[i++] = "You can move monsters.";
+ /* Not implemented */
+#if 0
+ /* They are disabled, because the r_info.txt array has to
+ * few RF2_TAKE_ITEM flags... */
+ if (r_ptr->flags2 & RF2_TAKE_ITEM)
+ info[i++] = "You can pick up items.";
+ else
+ info[i++] = "You cannot pick up items.";
+#endif
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_ORC)
+ info[i++] = "You have orc blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_TROLL)
+ info[i++] = "You have troll blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_GIANT)
+ info[i++] = "You have giant blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_DRAGON)
+ info[i++] = "You have dragon blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_DEMON)
+ info[i++] = "You have demon blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_UNDEAD)
+ info[i++] = "You are an undead.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_ANIMAL)
+ info[i++] = "You are an animal.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_THUNDERLORD)
+ info[i++] = "You have thunderlord blood in your veins.";
+ if (r_ptr->flags3 & RF3_EVIL)
+ info[i++] = "You are inherently evil.";
+ else if (r_ptr->flags3 & RF3_GOOD)
+ info[i++] = "You are inherently good.";
+ if (r_ptr->flags3 & RF3_AURA_COLD)
+ info[i++] = "You are surrounded by a chilly aura.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_NONLIVING)
+ info[i++] = "You are not living.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_HURT_LITE)
+ info[i++] = "Your eyes are vulnerable to bright light.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_HURT_ROCK)
+ info[i++] = "You can be hurt by rock remover.";
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE)
+ info[i++] = "You are vulnerable to fire.";
+ if (r_ptr->flags3 & RF3_SUSCEP_COLD)
+ info[i++] = "You are vulnerable to cold.";
+ if (r_ptr->flags3 & RF3_RES_TELE)
+ info[i++] = "You are resistant to teleportation.";
+ if (r_ptr->flags3 & RF3_RES_NETH)
+ info[i++] = "You are resistant to nether.";
+ if (r_ptr->flags3 & RF3_RES_WATE)
+ info[i++] = "You are resistant to water.";
+ if (r_ptr->flags3 & RF3_RES_PLAS)
+ info[i++] = "You are resistant to plasma.";
+ if (r_ptr->flags3 & RF3_RES_WATE)
+ info[i++] = "You are resistant to nexus.";
+ if (r_ptr->flags3 & RF3_RES_DISE)
+ info[i++] = "You are resistant to disease.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ info[i++] = "You cannot be slept.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_UNIQUE_4)
+ info[i++] = "You are a Nazgul.";
+ if (r_ptr->flags3 & RF3_NO_FEAR)
+ info[i++] = "You are immune to fear.";
+ if (r_ptr->flags3 & RF3_NO_STUN)
+ info[i++] = "You are immune to stun.";
+ if (r_ptr->flags3 & RF3_NO_CONF)
+ info[i++] = "You are immune to confusion.";
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ info[i++] = "You are immune to sleep.";
+
+ if (r_ptr->flags4 & RF4_SHRIEK)
+ info[i++] = "You can aggravate monsters.";
+ if (r_ptr->flags4 & RF4_ROCKET)
+ info[i++] = "You can fire a rocket.";
+ if (r_ptr->flags4 & RF4_ARROW_1)
+ info[i++] = "You can fire a light arrow.";
+ if (r_ptr->flags4 & RF4_ARROW_2)
+ info[i++] = "You can fire a heavy arrow.";
+ if (r_ptr->flags4 & RF4_ARROW_3)
+ info[i++] = "You can fire a light missile.";
+ if (r_ptr->flags4 & RF4_ARROW_4)
+ info[i++] = "You can fire a heavy missile.";
+ if (r_ptr->flags4 & RF4_BR_ACID)
+ info[i++] = "You can breathe acid.";
+ if (r_ptr->flags4 & RF4_BR_ELEC)
+ info[i++] = "You can breathe electricity.";
+ if (r_ptr->flags4 & RF4_BR_FIRE)
+ info[i++] = "You can breathe fire.";
+ if (r_ptr->flags4 & RF4_BR_COLD)
+ info[i++] = "You can breathe cold.";
+ if (r_ptr->flags4 & RF4_BR_POIS)
+ info[i++] = "You can breathe poison.";
+ if (r_ptr->flags4 & RF4_BR_NETH)
+ info[i++] = "You can breathe nether.";
+ if (r_ptr->flags4 & RF4_BR_LITE)
+ info[i++] = "You can breathe light.";
+ if (r_ptr->flags4 & RF4_BR_DARK)
+ info[i++] = "You can breathe darkness.";
+ if (r_ptr->flags4 & RF4_BR_CONF)
+ info[i++] = "You can breathe confusion.";
+ if (r_ptr->flags4 & RF4_BR_SOUN)
+ info[i++] = "You can breathe sound.";
+ if (r_ptr->flags4 & RF4_BR_CHAO)
+ info[i++] = "You can breathe chaos.";
+ if (r_ptr->flags4 & RF4_BR_DISE)
+ info[i++] = "You can breathe disenchantment.";
+ if (r_ptr->flags4 & RF4_BR_NEXU)
+ info[i++] = "You can breathe nexus.";
+ if (r_ptr->flags4 & RF4_BR_TIME)
+ info[i++] = "You can breathe time.";
+ if (r_ptr->flags4 & RF4_BR_INER)
+ info[i++] = "You can breathe inertia.";
+ if (r_ptr->flags4 & RF4_BR_GRAV)
+ info[i++] = "You can breathe gravity.";
+ if (r_ptr->flags4 & RF4_BR_SHAR)
+ info[i++] = "You can breathe shards.";
+ if (r_ptr->flags4 & RF4_BR_PLAS)
+ info[i++] = "You can breathe plasma.";
+ if (r_ptr->flags4 & RF4_BR_WALL)
+ info[i++] = "You can breathe force.";
+ if (r_ptr->flags4 & RF4_BR_MANA)
+ info[i++] = "You can breathe mana.";
+ if (r_ptr->flags4 & RF4_BR_NUKE)
+ info[i++] = "You can breathe nuke.";
+ if (r_ptr->flags4 & RF4_BR_DISI)
+ info[i++] = "You can breathe disintegration.";
+ if (r_ptr->flags5 & RF5_BA_ACID)
+ info[i++] = "You can cast a ball of acid.";
+ if (r_ptr->flags5 & RF5_BA_ELEC)
+ info[i++] = "You can cast a ball of electricity.";
+ if (r_ptr->flags5 & RF5_BA_FIRE)
+ info[i++] = "You can cast a ball of fire.";
+ if (r_ptr->flags5 & RF5_BA_COLD)
+ info[i++] = "You can cast a ball of cold.";
+ if (r_ptr->flags5 & RF5_BA_POIS)
+ info[i++] = "You can cast a ball of poison.";
+ if (r_ptr->flags5 & RF5_BA_NETH)
+ info[i++] = "You can cast a ball of nether.";
+ if (r_ptr->flags5 & RF5_BA_WATE)
+ info[i++] = "You can cast a ball of water.";
+ /* Not implemented */
+ if (r_ptr->flags5 & RF5_DRAIN_MANA)
+ info[i++] = "You can drain mana.";
+ if (r_ptr->flags5 & RF5_MIND_BLAST)
+ info[i++] = "You can cause mind blasting.";
+ if (r_ptr->flags5 & RF5_BRAIN_SMASH)
+ info[i++] = "You can cause brain smashing.";
+ if (r_ptr->flags5 & RF5_CAUSE_1)
+ info[i++] = "You can cause light wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_2)
+ info[i++] = "You can cause serious wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_3)
+ info[i++] = "You can cause critical wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_4)
+ info[i++] = "You can cause mortal wounds.";
+ if (r_ptr->flags5 & RF5_BO_ACID)
+ info[i++] = "You can cast a bolt of acid.";
+ if (r_ptr->flags5 & RF5_BO_ELEC)
+ info[i++] = "You can cast a bolt of electricity.";
+ if (r_ptr->flags5 & RF5_BO_FIRE)
+ info[i++] = "You can cast a bolt of fire.";
+ if (r_ptr->flags5 & RF5_BO_COLD)
+ info[i++] = "You can cast a bolt of cold.";
+ if (r_ptr->flags5 & RF5_BO_POIS)
+ info[i++] = "You can cast a bolt of poison.";
+ if (r_ptr->flags5 & RF5_BO_NETH)
+ info[i++] = "You can cast a bolt of nether.";
+ if (r_ptr->flags5 & RF5_BO_WATE)
+ info[i++] = "You can cast a bolt of water.";
+ if (r_ptr->flags5 & RF5_BO_MANA)
+ info[i++] = "You can cast a bolt of mana.";
+ if (r_ptr->flags5 & RF5_BO_PLAS)
+ info[i++] = "You can cast a bolt of plasma.";
+ if (r_ptr->flags5 & RF5_BO_ICEE)
+ info[i++] = "You can cast a bolt of ice.";
+ if (r_ptr->flags5 & RF5_MISSILE)
+ info[i++] = "You can cast magic missile.";
+ if (r_ptr->flags5 & RF5_SCARE)
+ info[i++] = "You can terrify.";
+ if (r_ptr->flags5 & RF5_BLIND)
+ info[i++] = "You can blind.";
+ if (r_ptr->flags5 & RF5_CONF)
+ info[i++] = "You can use confusion.";
+ if (r_ptr->flags5 & RF5_SLOW)
+ info[i++] = "You can cast slow.";
+ if (r_ptr->flags5 & RF5_HOLD)
+ info[i++] = "You can touch to paralyze.";
+ if (r_ptr->flags6 & RF6_HASTE)
+ info[i++] = "You can haste yourself.";
+ if (r_ptr->flags6 & RF6_HAND_DOOM)
+ info[i++] = "You can invoke Hand of Doom.";
+ if (r_ptr->flags6 & RF6_HEAL)
+ info[i++] = "You can heal yourself.";
+ if (r_ptr->flags6 & RF6_BLINK)
+ info[i++] = "You can blink.";
+ if (r_ptr->flags6 & RF6_TPORT)
+ info[i++] = "You can teleport.";
+ if (r_ptr->flags6 & RF6_TELE_TO)
+ info[i++] = "You can go between places.";
+ if (r_ptr->flags6 & RF6_TELE_AWAY)
+ info[i++] = "You can teleport away.";
+ if (r_ptr->flags6 & RF6_TELE_LEVEL)
+ info[i++] = "You can teleport level.";
+ if (r_ptr->flags6 & RF6_DARKNESS)
+ info[i++] = "You can create darkness.";
+ if (r_ptr->flags6 & RF6_TRAPS)
+ info[i++] = "You can create traps.";
+ /* Not implemented */
+ if (r_ptr->flags6 & RF6_FORGET)
+ info[i++] = "You can fade memories.";
+ if (r_ptr->flags6 & RF6_RAISE_DEAD)
+ info[i++] = "You can Raise the Dead.";
+ if (r_ptr->flags6 & RF6_S_BUG)
+ info[i++] = "You can magically summon a Software Bugs.";
+ if (r_ptr->flags6 & RF6_S_RNG)
+ info[i++] = "You can magically summon the RNG.";
+ if (r_ptr->flags6 & RF6_S_THUNDERLORD)
+ info[i++] = "You can magically summon some Thunderlords.";
+ if (r_ptr->flags6 & RF6_S_KIN)
+ info[i++] = "You can magically summon some Kins.";
+ if (r_ptr->flags6 & RF6_S_HI_DEMON)
+ info[i++] = "You can magically summon greater demons.";
+ if (r_ptr->flags6 & RF6_S_MONSTER)
+ info[i++] = "You can magically summon a monster.";
+ if (r_ptr->flags6 & RF6_S_MONSTERS)
+ info[i++] = "You can magically summon monsters.";
+ if (r_ptr->flags6 & RF6_S_ANT)
+ info[i++] = "You can magically summon ants.";
+ if (r_ptr->flags6 & RF6_S_SPIDER)
+ info[i++] = "You can magically summon spiders.";
+ if (r_ptr->flags6 & RF6_S_HOUND)
+ info[i++] = "You can magically summon hounds.";
+ if (r_ptr->flags6 & RF6_S_HYDRA)
+ info[i++] = "You can magically summon hydras.";
+ if (r_ptr->flags6 & RF6_S_ANGEL)
+ info[i++] = "You can magically summon an angel.";
+ if (r_ptr->flags6 & RF6_S_DEMON)
+ info[i++] = "You can magically summon a demon.";
+ if (r_ptr->flags6 & RF6_S_UNDEAD)
+ info[i++] = "You can magically summon an undead.";
+ if (r_ptr->flags6 & RF6_S_DRAGON)
+ info[i++] = "You can magically summon a dragon.";
+ if (r_ptr->flags6 & RF6_S_HI_UNDEAD)
+ info[i++] = "You can magically summon greater undead.";
+ if (r_ptr->flags6 & RF6_S_HI_DRAGON)
+ info[i++] = "You can magically summon greater dragons.";
+ if (r_ptr->flags6 & RF6_S_WRAITH)
+ info[i++] = "You can magically summon a wraith.";
+ /* Not implemented */
+ if (r_ptr->flags6 & RF6_S_UNIQUE)
+ info[i++] = "You can magically summon an unique monster.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ info[i++] = "You are aquatic.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_CAN_SWIM)
+ info[i++] = "You can swim.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ info[i++] = "You can fly.";
+ if ((r_ptr->flags7 & RF7_MORTAL) == 0)
+ info[i++] = "You are immortal.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ info[i++] = "You are a Nazgul.";
+
+ if (r_ptr->flags7 & RF7_SPIDER)
+ info[i++] = "You are a spider.";
+
+ if (r_ptr->flags8 & RF8_WILD_TOWN)
+ info[i++] = "You appear in towns.";
+ if (r_ptr->flags8 & RF8_WILD_SHORE)
+ info[i++] = "You appear on the shore.";
+ if (r_ptr->flags8 & RF8_WILD_OCEAN)
+ info[i++] = "You appear in the ocean.";
+ if (r_ptr->flags8 & RF8_WILD_WASTE)
+ info[i++] = "You appear in the waste.";
+ if (r_ptr->flags8 & RF8_WILD_WOOD)
+ info[i++] = "You appear in woods.";
+ if (r_ptr->flags8 & RF8_WILD_VOLCANO)
+ info[i++] = "You appear in volcanos.";
+ if (r_ptr->flags8 & RF8_WILD_MOUNTAIN)
+ info[i++] = "You appear in the mountains.";
+ if (r_ptr->flags8 & RF8_WILD_GRASS)
+ info[i++] = "You appear in grassy areas.";
+
+ if (r_ptr->flags9 & RF9_SUSCEP_ACID)
+ info[i++] = "You are vulnerable to acid.";
+ if (r_ptr->flags9 & RF9_SUSCEP_ELEC)
+ info[i++] = "You are vulnerable to electricity.";
+ if (r_ptr->flags9 & RF9_SUSCEP_POIS)
+ info[i++] = "You are vulnerable to poison.";
+ if (r_ptr->flags9 & RF9_KILL_TREES)
+ info[i++] = "You can eat trees.";
+ if (r_ptr->flags9 & RF9_WYRM_PROTECT)
+ info[i++] = "You are protected by great wyrms of power.";
+ }
+
+ /* List powers */
+ for (iter = 0; iter < power_max; iter++)
+ {
+ if (p_ptr->powers[iter])
+ {
+ info[i++] = powers_type[iter].desc_text;
+ }
+ }
+
+ if (p_ptr->allow_one_death)
+ {
+ info[i++] = "The Blood of Life flows through your veins.";
+ }
+ if (p_ptr->blind)
+ {
+ info[i++] = "You cannot see.";
+ }
+ if (p_ptr->confused)
+ {
+ info[i++] = "You are confused.";
+ }
+ if (p_ptr->afraid)
+ {
+ info[i++] = "You are terrified.";
+ }
+ if (p_ptr->cut)
+ {
+ info[i++] = "You are bleeding.";
+ }
+ if (p_ptr->stun)
+ {
+ info[i++] = "You are stunned.";
+ }
+ if (p_ptr->poisoned)
+ {
+ info[i++] = "You are poisoned.";
+ }
+ if (p_ptr->image)
+ {
+ info[i++] = "You are hallucinating.";
+ }
+ if (p_ptr->aggravate)
+ {
+ info[i++] = "You aggravate monsters.";
+ }
+ if (p_ptr->teleport)
+ {
+ info[i++] = "Your position is very uncertain.";
+ }
+ if (p_ptr->blessed)
+ {
+ info[i++] = "You feel righteous.";
+ }
+ if (p_ptr->hero)
+ {
+ info[i++] = "You feel heroic.";
+ }
+ if (p_ptr->shero)
+ {
+ info[i++] = "You are in a battle rage.";
+ }
+ if (p_ptr->protevil)
+ {
+ info[i++] = "You are protected from evil.";
+ }
+ if (p_ptr->protgood)
+ {
+ info[i++] = "You are protected from good.";
+ }
+ if (p_ptr->shield)
+ {
+ info[i++] = "You are protected by a mystic shield.";
+ }
+ if (p_ptr->invuln)
+ {
+ info[i++] = "You are temporarily invulnerable.";
+ }
+ if (p_ptr->confusing)
+ {
+ info[i++] = "Your hands are glowing dull red.";
+ }
+ if (p_ptr->searching)
+ {
+ info[i++] = "You are looking around very carefully.";
+ }
+ if (p_ptr->new_spells)
+ {
+ info[i++] = "You can learn some spells/prayers.";
+ }
+ if (p_ptr->word_recall)
+ {
+ info[i++] = "You will soon be recalled.";
+ }
+ if (p_ptr->see_infra)
+ {
+ info[i++] = "Your eyes are sensitive to infrared light.";
+ }
+ if (p_ptr->see_inv)
+ {
+ info[i++] = "You can see invisible creatures.";
+ }
+ if (p_ptr->magical_breath)
+ {
+ info[i++] = "You can breathe without air.";
+ }
+ else if (p_ptr->water_breath)
+ {
+ info[i++] = "You can breathe underwater.";
+ }
+ if (p_ptr->ffall)
+ {
+ info[i++] = "You levitate just over the ground.";
+ }
+ if (p_ptr->climb)
+ {
+ info[i++] = "You can climb high mountains.";
+ }
+ if (p_ptr->free_act)
+ {
+ info[i++] = "You have free action.";
+ }
+ if (p_ptr->regenerate)
+ {
+ info[i++] = "You regenerate quickly.";
+ }
+ if (p_ptr->slow_digest)
+ {
+ info[i++] = "Your appetite is small.";
+ }
+ if (p_ptr->telepathy)
+ {
+ if (p_ptr->telepathy & ESP_ALL) info[i++] = "You have ESP.";
+ else
+ {
+ if (p_ptr->telepathy & ESP_ORC) info[i++] = "You can sense the presence of orcs.";
+ if (p_ptr->telepathy & ESP_TROLL) info[i++] = "You can sense the presence of trolls.";
+ if (p_ptr->telepathy & ESP_DRAGON) info[i++] = "You can sense the presence of dragons.";
+ if (p_ptr->telepathy & ESP_SPIDER) info[i++] = "You can sense the presence of spiders.";
+ if (p_ptr->telepathy & ESP_GIANT) info[i++] = "You can sense the presence of giants.";
+ if (p_ptr->telepathy & ESP_DEMON) info[i++] = "You can sense the presence of demons.";
+ if (p_ptr->telepathy & ESP_UNDEAD) info[i++] = "You can sense presence of undead.";
+ if (p_ptr->telepathy & ESP_EVIL) info[i++] = "You can sense the presence of evil beings.";
+ if (p_ptr->telepathy & ESP_ANIMAL) info[i++] = "You can sense the presence of animals.";
+ if (p_ptr->telepathy & ESP_THUNDERLORD) info[i++] = "You can sense the presence of thunderlords.";
+ if (p_ptr->telepathy & ESP_GOOD) info[i++] = "You can sense the presence of good beings.";
+ if (p_ptr->telepathy & ESP_NONLIVING) info[i++] = "You can sense the presence of non-living things.";
+ if (p_ptr->telepathy & ESP_UNIQUE) info[i++] = "You can sense the presence of unique beings.";
+ }
+ }
+ if (!luck( -100, 100))
+ {
+ info[i++] = "You have normal luck.";
+ }
+ else if (luck( -100, 100) < 0)
+ {
+ if (luck( -100, 100) < -90)
+ {
+ info[i++] = "You are incredibly unlucky.";
+ }
+ else if (luck( -100, 100) < -60)
+ {
+ info[i++] = "You are extremely unlucky.";
+ }
+ else if (luck( -100, 100) < -30)
+ {
+ info[i++] = "You are very unlucky.";
+ }
+ else
+ {
+ info[i++] = "You are unlucky.";
+ }
+ }
+ else if (luck( -100, 100) > 0)
+ {
+ if (luck( -100, 100) > 90)
+ {
+ info[i++] = "You are incredibly lucky.";
+ }
+ else if (luck( -100, 100) > 60)
+ {
+ info[i++] = "You are extremely lucky.";
+ }
+ else if (luck( -100, 100) > 30)
+ {
+ info[i++] = "You are very lucky.";
+ }
+ else
+ {
+ info[i++] = "You are lucky.";
+ }
+ }
+ if (p_ptr->auto_id)
+ {
+ info[i++] = "You know everything.";
+ }
+ if (p_ptr->hold_life)
+ {
+ info[i++] = "You have a firm hold on your life force.";
+ }
+ if (p_ptr->reflect)
+ {
+ info[i++] = "You reflect arrows and bolts.";
+ }
+ if (p_ptr->sh_fire)
+ {
+ info[i++] = "You are surrounded with a fiery aura.";
+ }
+ if (p_ptr->sh_elec)
+ {
+ info[i++] = "You are surrounded with electricity.";
+ }
+ if (p_ptr->antimagic)
+ {
+ info[i++] = "You are surrounded by an anti-magic field.";
+ }
+ if (p_ptr->anti_magic)
+ {
+ info[i++] = "You are surrounded by an anti-magic shell.";
+ }
+ if (p_ptr->wraith_form)
+ {
+ info[i++] = "You are incorporeal.";
+ }
+ if (p_ptr->anti_tele)
+ {
+ info[i++] = "You cannot teleport.";
+ }
+ if (p_ptr->lite)
+ {
+ info[i++] = "You are carrying a permanent light.";
+ }
+
+ if (p_ptr->immune_acid)
+ {
+ info[i++] = "You are completely immune to acid.";
+ }
+ else if ((p_ptr->resist_acid) && (p_ptr->oppose_acid))
+ {
+ info[i++] = "You resist acid exceptionally well.";
+ }
+ else if ((p_ptr->resist_acid) || (p_ptr->oppose_acid))
+ {
+ info[i++] = "You are resistant to acid.";
+ }
+
+ if (p_ptr->immune_elec)
+ {
+ info[i++] = "You are completely immune to lightning.";
+ }
+ else if ((p_ptr->resist_elec) && (p_ptr->oppose_elec))
+ {
+ info[i++] = "You resist lightning exceptionally well.";
+ }
+ else if ((p_ptr->resist_elec) || (p_ptr->oppose_elec))
+ {
+ info[i++] = "You are resistant to lightning.";
+ }
+
+ if (p_ptr->immune_fire)
+ {
+ info[i++] = "You are completely immune to fire.";
+ }
+ else if ((p_ptr->resist_fire) && (p_ptr->oppose_fire))
+ {
+ info[i++] = "You resist fire exceptionally well.";
+ }
+ else if ((p_ptr->resist_fire) || (p_ptr->oppose_fire))
+ {
+ info[i++] = "You are resistant to fire.";
+ }
+ else if (p_ptr->sensible_fire)
+ {
+ info[i++] = "You are very vulnerable to fire.";
+ }
+
+ if (p_ptr->immune_cold)
+ {
+ info[i++] = "You are completely immune to cold.";
+ }
+ else if ((p_ptr->resist_cold) && (p_ptr->oppose_cold))
+ {
+ info[i++] = "You resist cold exceptionally well.";
+ }
+ else if ((p_ptr->resist_cold) || (p_ptr->oppose_cold))
+ {
+ info[i++] = "You are resistant to cold.";
+ }
+
+ if ((p_ptr->resist_pois) && (p_ptr->oppose_pois))
+ {
+ info[i++] = "You resist poison exceptionally well.";
+ }
+ else if ((p_ptr->resist_pois) || (p_ptr->oppose_pois))
+ {
+ info[i++] = "You are resistant to poison.";
+ }
+
+ if (p_ptr->resist_lite)
+ {
+ info[i++] = "You are resistant to bright light.";
+ }
+ if (p_ptr->resist_dark)
+ {
+ info[i++] = "You are resistant to darkness.";
+ }
+ if (p_ptr->resist_conf)
+ {
+ info[i++] = "You are resistant to confusion.";
+ }
+ if (p_ptr->resist_sound)
+ {
+ info[i++] = "You are resistant to sonic attacks.";
+ }
+ if (p_ptr->resist_disen)
+ {
+ info[i++] = "You are resistant to disenchantment.";
+ }
+ if (p_ptr->resist_chaos)
+ {
+ info[i++] = "You are resistant to chaos.";
+ }
+ if (p_ptr->resist_shard)
+ {
+ info[i++] = "You are resistant to blasts of shards.";
+ }
+ if (p_ptr->resist_nexus)
+ {
+ info[i++] = "You are resistant to nexus attacks.";
+ }
+ if (p_ptr->immune_neth)
+ {
+ info[i++] = "You are immune to nether forces.";
+ }
+ else if (p_ptr->resist_neth)
+ {
+ info[i++] = "You are resistant to nether forces.";
+ }
+ if (p_ptr->resist_fear)
+ {
+ info[i++] = "You are completely fearless.";
+ }
+ if (p_ptr->resist_blind)
+ {
+ info[i++] = "Your eyes are resistant to blindness.";
+ }
+ if (p_ptr->resist_continuum)
+ {
+ info[i++] = "The space-time continuum cannot be disrupted near you.";
+ }
+
+ if (p_ptr->sustain_str)
+ {
+ info[i++] = "Your strength is sustained.";
+ }
+ if (p_ptr->sustain_int)
+ {
+ info[i++] = "Your intelligence is sustained.";
+ }
+ if (p_ptr->sustain_wis)
+ {
+ info[i++] = "Your wisdom is sustained.";
+ }
+ if (p_ptr->sustain_con)
+ {
+ info[i++] = "Your constitution is sustained.";
+ }
+ if (p_ptr->sustain_dex)
+ {
+ info[i++] = "Your dexterity is sustained.";
+ }
+ if (p_ptr->sustain_chr)
+ {
+ info[i++] = "Your charisma is sustained.";
+ }
+ if (p_ptr->black_breath)
+ {
+ info[i++] = "You suffer from Black Breath.";
+ }
+
+ if (f1 & (TR1_STR))
+ {
+ info[i++] = "Your strength is affected by your equipment.";
+ }
+ if (f1 & (TR1_INT))
+ {
+ info[i++] = "Your intelligence is affected by your equipment.";
+ }
+ if (f1 & (TR1_WIS))
+ {
+ info[i++] = "Your wisdom is affected by your equipment.";
+ }
+ if (f1 & (TR1_DEX))
+ {
+ info[i++] = "Your dexterity is affected by your equipment.";
+ }
+ if (f1 & (TR1_CON))
+ {
+ info[i++] = "Your constitution is affected by your equipment.";
+ }
+ if (f1 & (TR1_CHR))
+ {
+ info[i++] = "Your charisma is affected by your equipment.";
+ }
+
+ if (f1 & (TR1_STEALTH))
+ {
+ info[i++] = "Your stealth is affected by your equipment.";
+ }
+ if (f1 & (TR1_SEARCH))
+ {
+ info[i++] = "Your searching ability is affected by your equipment.";
+ }
+ if (f1 & (TR1_INFRA))
+ {
+ info[i++] = "Your infravision is affected by your equipment.";
+ }
+ if (f1 & (TR1_TUNNEL))
+ {
+ info[i++] = "Your digging ability is affected by your equipment.";
+ }
+ if (f1 & (TR1_SPEED))
+ {
+ info[i++] = "Your speed is affected by your equipment.";
+ }
+ if (f1 & (TR1_BLOWS))
+ {
+ info[i++] = "Your attack speed is affected by your equipment.";
+ }
+ if (f5 & (TR5_CRIT))
+ {
+ info[i++] = "Your ability to score critical hits is affected by your equipment.";
+ }
+
+
+ /* Access the current weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Analyze the weapon */
+ if (o_ptr->k_idx)
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Indicate Blessing */
+ if (f3 & (TR3_BLESSED))
+ {
+ info[i++] = "Your weapon has been blessed by the gods.";
+ }
+
+ if (f1 & (TR1_CHAOTIC))
+ {
+ info[i++] = "Your weapon is branded with the Sign of Chaos.";
+ }
+
+ /* Hack */
+ if (f1 & (TR1_IMPACT))
+ {
+ info[i++] = "The impact of your weapon can cause earthquakes.";
+ }
+
+ if (f1 & (TR1_VORPAL))
+ {
+ info[i++] = "Your weapon is very sharp.";
+ }
+
+ if (f1 & (TR1_VAMPIRIC))
+ {
+ info[i++] = "Your weapon drains life from your foes.";
+ }
+
+ /* Special "Attack Bonuses" */
+ if (f1 & (TR1_BRAND_ACID))
+ {
+ info[i++] = "Your weapon melts your foes.";
+ }
+ if (f1 & (TR1_BRAND_ELEC))
+ {
+ info[i++] = "Your weapon shocks your foes.";
+ }
+ if (f1 & (TR1_BRAND_FIRE))
+ {
+ info[i++] = "Your weapon burns your foes.";
+ }
+ if (f1 & (TR1_BRAND_COLD))
+ {
+ info[i++] = "Your weapon freezes your foes.";
+ }
+ if (f1 & (TR1_BRAND_POIS))
+ {
+ info[i++] = "Your weapon poisons your foes.";
+ }
+
+ /* Special "slay" flags */
+ if (f1 & (TR1_SLAY_ANIMAL))
+ {
+ info[i++] = "Your weapon strikes at animals with extra force.";
+ }
+ if (f1 & (TR1_SLAY_EVIL))
+ {
+ info[i++] = "Your weapon strikes at evil with extra force.";
+ }
+ if (f1 & (TR1_SLAY_UNDEAD))
+ {
+ info[i++] = "Your weapon strikes at undead with holy wrath.";
+ }
+ if (f1 & (TR1_SLAY_DEMON))
+ {
+ info[i++] = "Your weapon strikes at demons with holy wrath.";
+ }
+ if (f1 & (TR1_SLAY_ORC))
+ {
+ info[i++] = "Your weapon is especially deadly against orcs.";
+ }
+ if (f1 & (TR1_SLAY_TROLL))
+ {
+ info[i++] = "Your weapon is especially deadly against trolls.";
+ }
+ if (f1 & (TR1_SLAY_GIANT))
+ {
+ info[i++] = "Your weapon is especially deadly against giants.";
+ }
+ if (f1 & (TR1_SLAY_DRAGON))
+ {
+ info[i++] = "Your weapon is especially deadly against dragons.";
+ }
+
+ /* Special "kill" flags */
+ if (f1 & (TR1_KILL_DRAGON))
+ {
+ info[i++] = "Your weapon is a great bane of dragons.";
+ }
+ /* Special "kill" flags */
+ if (f5 & (TR5_KILL_DEMON))
+ {
+ info[i++] = "Your weapon is a great bane of demons.";
+ }
+ /* Special "kill" flags */
+ if (f5 & (TR5_KILL_UNDEAD))
+ {
+ info[i++] = "Your weapon is a great bane of undeads.";
+ }
+ }
+
+ /* Print on screen or in a file ? */
+ if (fff == NULL)
+ {
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Erase the screen */
+ for (k = 1; k < 24; k++) prt("", k, 13);
+
+ /* Label the information */
+ prt(" Your Attributes:", 1, 15);
+
+ /* We will print on top of the map (column 13) */
+ for (k = 2, j = 0; j < i; j++)
+ {
+ /* Show the info */
+ prt(info[j], k++, 15);
+
+ /* Every 20 entries (lines 2 to 21), start over */
+ if ((k == 22) && (j + 1 < i))
+ {
+ prt("-- more --", k, 15);
+ inkey();
+ for (; k > 2; k--) prt("", k, 15);
+ }
+ }
+
+ /* Pause */
+ prt("[Press any key to continue]", k, 13);
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+ else
+ {
+ /* Label the information */
+ fprintf(fff, " Your Attributes:\n");
+
+ /* We will print on top of the map (column 13) */
+ for (j = 0; j < i; j ++)
+ {
+ /* Show the info */
+ fprintf(fff, "%s\n", info[j]);
+ }
+ }
+}
+
+
+static int report_magics_aux(int dur)
+{
+ if (dur <= 5)
+ {
+ return 0;
+ }
+ else if (dur <= 10)
+ {
+ return 1;
+ }
+ else if (dur <= 20)
+ {
+ return 2;
+ }
+ else if (dur <= 50)
+ {
+ return 3;
+ }
+ else if (dur <= 100)
+ {
+ return 4;
+ }
+ else if (dur <= 200)
+ {
+ return 5;
+ }
+ else
+ {
+ return 6;
+ }
+}
+
+static cptr report_magic_durations[] =
+{
+ "for a short time",
+ "for a little while",
+ "for a while",
+ "for a long while",
+ "for a long time",
+ "for a very long time",
+ "for an incredibly long time",
+ "until you hit a monster"
+};
+
+
+void report_magics(void)
+{
+ int i = 0, j, k;
+
+ char Dummy[80];
+
+ cptr info[128];
+ int info2[128];
+
+ if (p_ptr->blind)
+ {
+ info2[i] = report_magics_aux(p_ptr->blind);
+ info[i++] = "You cannot see";
+ }
+ if (p_ptr->confused)
+ {
+ info2[i] = report_magics_aux(p_ptr->confused);
+ info[i++] = "You are confused";
+ }
+ if (p_ptr->afraid)
+ {
+ info2[i] = report_magics_aux(p_ptr->afraid);
+ info[i++] = "You are terrified";
+ }
+ if (p_ptr->poisoned)
+ {
+ info2[i] = report_magics_aux(p_ptr->poisoned);
+ info[i++] = "You are poisoned";
+ }
+ if (p_ptr->image)
+ {
+ info2[i] = report_magics_aux(p_ptr->image);
+ info[i++] = "You are hallucinating";
+ }
+
+ if (p_ptr->blessed)
+ {
+ info2[i] = report_magics_aux(p_ptr->blessed);
+ info[i++] = "You feel righteous";
+ }
+ if (p_ptr->hero)
+ {
+ info2[i] = report_magics_aux(p_ptr->hero);
+ info[i++] = "You feel heroic";
+ }
+ if (p_ptr->shero)
+ {
+ info2[i] = report_magics_aux(p_ptr->shero);
+ info[i++] = "You are in a battle rage";
+ }
+ if (p_ptr->protevil)
+ {
+ info2[i] = report_magics_aux(p_ptr->protevil);
+ info[i++] = "You are protected from evil";
+ }
+ if (p_ptr->protgood)
+ {
+ info2[i] = report_magics_aux(p_ptr->protgood);
+ info[i++] = "You are protected from good";
+ }
+ if (p_ptr->shield)
+ {
+ info2[i] = report_magics_aux(p_ptr->shield);
+ info[i++] = "You are protected by a mystic shield";
+ }
+ if (p_ptr->invuln)
+ {
+ info2[i] = report_magics_aux(p_ptr->invuln);
+ info[i++] = "You are invulnerable";
+ }
+ if (p_ptr->tim_wraith)
+ {
+ info2[i] = report_magics_aux(p_ptr->tim_wraith);
+ info[i++] = "You are incorporeal";
+ }
+ if (p_ptr->confusing)
+ {
+ info2[i] = 7;
+ info[i++] = "Your hands are glowing dull red.";
+ }
+ if (p_ptr->word_recall)
+ {
+ info2[i] = report_magics_aux(p_ptr->word_recall);
+ info[i++] = "You waiting to be recalled";
+ }
+ if (p_ptr->oppose_acid)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_acid);
+ info[i++] = "You are resistant to acid";
+ }
+ if (p_ptr->oppose_elec)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_elec);
+ info[i++] = "You are resistant to lightning";
+ }
+ if (p_ptr->oppose_fire)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_fire);
+ info[i++] = "You are resistant to fire";
+ }
+ if (p_ptr->oppose_cold)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_cold);
+ info[i++] = "You are resistant to cold";
+ }
+ if (p_ptr->oppose_pois)
+ {
+ info2[i] = report_magics_aux(p_ptr->oppose_pois);
+ info[i++] = "You are resistant to poison";
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Erase the screen */
+ for (k = 1; k < 24; k++) prt("", k, 13);
+
+ /* Label the information */
+ prt(" Your Current Magic:", 1, 15);
+
+ /* We will print on top of the map (column 13) */
+ for (k = 2, j = 0; j < i; j++)
+ {
+ /* Show the info */
+ sprintf( Dummy, "%s %s.", info[j],
+ report_magic_durations[info2[j]] );
+ prt(Dummy, k++, 15);
+
+ /* Every 20 entries (lines 2 to 21), start over */
+ if ((k == 22) && (j + 1 < i))
+ {
+ prt("-- more --", k, 15);
+ inkey();
+ for (; k > 2; k--) prt("", k, 15);
+ }
+ }
+
+ /* Pause */
+ prt("[Press any key to continue]", k, 13);
+ inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Forget everything
+ */
+bool lose_all_info(void)
+{
+ int i;
+
+ /* Forget info about objects */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Allow "protection" by the MENTAL flag */
+ if (o_ptr->ident & (IDENT_MENTAL)) continue;
+
+ /* Remove sensing */
+ o_ptr->sense = SENSE_NONE;
+
+ /* Hack -- Clear the "empty" flag */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+
+ /* Hack -- Clear the "known" flag */
+ o_ptr->ident &= ~(IDENT_KNOWN);
+
+ /* Hack -- Clear the "felt" flag */
+ o_ptr->ident &= ~(IDENT_SENSE);
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Mega-Hack -- Forget the map */
+ wiz_dark();
+
+ /* It worked */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Detect all traps on current panel
+ */
+bool detect_traps(int rad)
+{
+ int x, y;
+ bool detect = FALSE;
+ cave_type *c_ptr;
+
+
+ /* Scan the current panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ /* Reject locations outside of dungeon */
+ if (!in_bounds(y, x)) continue;
+
+ /* Reject those out of radius */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* mark as detected */
+ c_ptr->info |= CAVE_DETECT;
+
+ /* Detect invisible traps */
+ if (c_ptr->t_idx != 0)
+ {
+ /* Hack -- Remember detected traps */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Pick a trap */
+ pick_trap(y, x);
+
+ /* Obvious */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of traps!");
+ }
+
+ /*
+ * This reveals un-identified trap detection items,
+ * but so does leaving/entering trap-detected areas...
+ * There are a couple of possible solutions:
+ * (1) Immediately self-id such items (i.e. always returns TRUE)
+ * (2) add another parameter to function which tells if unaware
+ * item is used for trap detection, and if it is the case,
+ * do two-pass scanning, first scanning for traps if an unaware
+ * item is used and return FALSE there are none,
+ * followed by current implementation --pelpel
+ */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Result -- see my comment above -- pelpel */
+ /* return (detect); */
+ return (TRUE);
+}
+
+
+
+/*
+ * Detect all doors on current panel
+ */
+bool detect_doors(int rad)
+{
+ int y, x;
+
+ bool detect = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Scan the panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ c_ptr = &cave[y][x];
+
+ /* Detect secret doors */
+ if (c_ptr->feat == FEAT_SECRET)
+ {
+ /* Remove feature mimics */
+ cave[y][x].mimic = 0;
+
+ /* Pick a door XXX XXX XXX */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ /* Detect doors */
+ if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ ((c_ptr->feat == FEAT_OPEN) ||
+ (c_ptr->feat == FEAT_BROKEN)))
+ {
+ /* Hack -- Memorize */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Reveal it */
+ /* c_ptr->mimic = 0; */
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Obvious */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of doors!");
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect all stairs on current panel
+ */
+bool detect_stairs(int rad)
+{
+ int y, x;
+
+ bool detect = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Scan the panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ c_ptr = &cave[y][x];
+
+ /* Detect stairs */
+ if ((c_ptr->feat == FEAT_LESS) ||
+ (c_ptr->feat == FEAT_MORE) ||
+ (c_ptr->feat == FEAT_SHAFT_DOWN) ||
+ (c_ptr->feat == FEAT_SHAFT_UP) ||
+ (c_ptr->feat == FEAT_WAY_LESS) ||
+ (c_ptr->feat == FEAT_WAY_MORE))
+ {
+ /* Hack -- Memorize */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Obvious */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of ways out of this area!");
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect any treasure on the current panel
+ */
+bool detect_treasure(int rad)
+{
+ int y, x;
+
+ bool detect = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Scan the current panel */
+ for (y = p_ptr->py - rad; y <= p_ptr->py + rad; y++)
+ {
+ for (x = p_ptr->px - rad; x <= p_ptr->px + rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ c_ptr = &cave[y][x];
+
+ /* Notice embedded gold */
+ if ((c_ptr->feat == FEAT_MAGMA_H) ||
+ (c_ptr->feat == FEAT_QUARTZ_H))
+ {
+ /* Expose the gold */
+ cave_set_feat(y, x, c_ptr->feat + 0x02);
+ }
+ else if (c_ptr->feat == FEAT_SANDWALL_H)
+ {
+ /* Expose the gold */
+ cave_set_feat(y, x, FEAT_SANDWALL_K);
+ }
+
+ /* Magma/Quartz + Known Gold */
+ if ((c_ptr->feat == FEAT_MAGMA_K) ||
+ (c_ptr->feat == FEAT_QUARTZ_K) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ /* Hack -- Memorize */
+ c_ptr->info |= (CAVE_MARK);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of buried treasure!");
+ }
+
+
+
+ /* Result */
+ return (detect);
+}
+
+
+
+/*
+ * Detect all "gold" objects on the current panel
+ */
+bool hack_no_detect_message = FALSE;
+bool detect_objects_gold(int rad)
+{
+ int i, y, x;
+
+ bool detect = FALSE;
+
+
+ /* Scan objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags9 & RF9_MIMIC)) continue;
+ else
+ {
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ }
+ else
+ {
+ /* Location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Only detect nearby objects */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Detect "gold" objects */
+ if (o_ptr->tval == TV_GOLD)
+ {
+ /* Hack -- memorize it */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect && (!hack_no_detect_message))
+ {
+ msg_print("You sense the presence of treasure!");
+ }
+
+ if (detect_monsters_string("$", rad))
+ {
+ detect = TRUE;
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect all "normal" objects on the current panel
+ */
+bool detect_objects_normal(int rad)
+{
+ int i, y, x;
+
+ bool detect = FALSE;
+
+
+ /* Scan objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags9 & RF9_MIMIC)) continue;
+ else
+ {
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ }
+ else
+ {
+ /* Location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Only detect nearby objects */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Detect "real" objects */
+ if (o_ptr->tval != TV_GOLD)
+ {
+ /* Hack -- memorize it */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect && (!hack_no_detect_message))
+ {
+ msg_print("You sense the presence of objects!");
+ }
+
+ if (detect_monsters_string("!=?|", rad))
+ {
+ detect = TRUE;
+ }
+
+ /* Result */
+ return (detect);
+}
+
+
+/*
+ * Detect all "magic" objects on the current panel.
+ *
+ * This will light up all spaces with "magic" items, including artifacts,
+ * ego-items, potions, scrolls, books, rods, wands, staves, amulets, rings,
+ * and "enchanted" items of the "good" variety.
+ *
+ * It can probably be argued that this function is now too powerful.
+ */
+bool detect_objects_magic(int rad)
+{
+ int i, y, x, tv;
+
+ bool detect = FALSE;
+
+
+ /* Scan all objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags9 & RF9_MIMIC)) continue;
+ else
+ {
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+ }
+ }
+ else
+ {
+ /* Location */
+ y = o_ptr->iy;
+ x = o_ptr->ix;
+ }
+
+ /* Only detect nearby objects */
+ if (distance(p_ptr->py, p_ptr->px, y, x) > rad) continue;
+
+ /* Examine the tval */
+ tv = o_ptr->tval;
+
+ /* Artifacts, misc magic items, or enchanted wearables */
+ if (artifact_p(o_ptr) || ego_item_p(o_ptr) || o_ptr->art_name ||
+ (tv == TV_AMULET) || (tv == TV_RING) || (tv == TV_BATERIE) ||
+ (tv == TV_STAFF) || (tv == TV_WAND) || (tv == TV_ROD) || (tv == TV_ROD_MAIN) ||
+ (tv == TV_SCROLL) || (tv == TV_POTION) || (tv == TV_POTION2) ||
+ (tv == TV_DAEMON_BOOK) ||
+ (tv == TV_SYMBIOTIC_BOOK) || (tv == TV_MUSIC_BOOK) ||
+ ((o_ptr->to_a > 0) || (o_ptr->to_h + o_ptr->to_d > 0)))
+ {
+ /* Memorize the item */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print("You sense the presence of magic objects!");
+ }
+
+ /* Return result */
+ return (detect);
+}
+
+
+/*
+ * Detect all "normal" monsters on the current panel
+ */
+bool detect_monsters_normal(int rad)
+{
+ int i, y, x;
+
+ bool flag = FALSE;
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect all non-invisible monsters */
+ if ((!(r_ptr->flags2 & (RF2_INVISIBLE))) ||
+ p_ptr->see_inv || p_ptr->tim_invis)
+ {
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of monsters!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+/*
+ * Detect all "invisible" monsters on current panel
+ */
+bool detect_monsters_invis(int rad)
+{
+ int i, y, x;
+ bool flag = FALSE;
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect invisible monsters */
+ if (r_ptr->flags2 & (RF2_INVISIBLE))
+ {
+ /* Take note that they are invisible */
+ r_ptr->r_flags2 |= (RF2_INVISIBLE);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of invisible creatures!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+
+/*
+ * Detect all "evil" monsters on current panel
+ */
+bool detect_monsters_evil(int rad)
+{
+ int i, y, x;
+ bool flag = FALSE;
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Take note that they are evil */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of evil creatures!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+
+
+/*
+ * Detect all (string) monsters on current panel
+ */
+bool detect_monsters_string(cptr chars, int rad)
+{
+ int i, y, x;
+ bool flag = FALSE;
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if (strchr(chars, r_ptr->d_char))
+ {
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of monsters!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+/*
+ * A "generic" detect monsters routine, tagged to flags3
+ */
+bool detect_monsters_xxx(u32b match_flag, int rad)
+{
+ int i, y, x;
+ bool flag = FALSE;
+ cptr desc_monsters = "weird monsters";
+
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if (r_ptr->flags3 & (match_flag))
+ {
+ /* Take note that they are something */
+ r_ptr->r_flags3 |= (match_flag);
+
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ switch (match_flag)
+ {
+ case RF3_DEMON:
+ desc_monsters = "demons";
+ break;
+ case RF3_UNDEAD:
+ desc_monsters = "the undead";
+ break;
+ case RF3_GOOD:
+ desc_monsters = "good";
+ break;
+ }
+
+ /* Describe result */
+ msg_format("You sense the presence of %s!", desc_monsters);
+ msg_print(NULL);
+ }
+
+ /* Result */
+ return (flag);
+}
+
+/* Detect good monsters */
+bool detect_monsters_good(int rad)
+{
+ return (detect_monsters_xxx(RF3_GOOD, rad));
+}
+
+
+/*
+ * Detect everything
+ */
+bool detect_all(int rad)
+{
+ bool detect = FALSE;
+
+ /* Detect everything */
+ if (detect_traps(rad)) detect = TRUE;
+ if (detect_doors(rad)) detect = TRUE;
+ if (detect_stairs(rad)) detect = TRUE;
+ if (detect_treasure(rad)) detect = TRUE;
+ if (detect_objects_gold(rad)) detect = TRUE;
+ if (detect_objects_normal(rad)) detect = TRUE;
+ if (detect_monsters_invis(rad)) detect = TRUE;
+ if (detect_monsters_normal(rad)) detect = TRUE;
+
+ /* Result */
+ return (detect);
+}
+
+
+
+/*
+ * Create stairs at the player location
+ */
+void stair_creation(void)
+{
+ /* XXX XXX XXX */
+ if (!cave_valid_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("The object resists the spell.");
+ return;
+ }
+
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ msg_print("No stair creation in non dungeons...");
+ return;
+ }
+
+ if (dungeon_flags2 & DF2_SPECIAL)
+ {
+ msg_print("No stair creation on special levels...");
+ return;
+ }
+
+ /* XXX XXX XXX */
+ delete_object(p_ptr->py, p_ptr->px);
+
+ /* Create a staircase */
+ if (p_ptr->inside_arena || p_ptr->inside_quest)
+ {
+ /* arena or quest */
+ msg_print("There is no effect!");
+ }
+ else if (!dun_level)
+ {
+ /* Town/wilderness */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MORE);
+ }
+ else if (is_quest(dun_level) || (dun_level >= MAX_DEPTH - 1))
+ {
+ /* Quest level */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+ else if (rand_int(100) < 50)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+}
+
+
+
+
+/*
+ * Hook to specify "weapon"
+ */
+static bool item_tester_hook_weapon(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_MSTAFF:
+ case TV_BOOMERANG:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_BOW:
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ return (TRUE);
+ }
+ case TV_DAEMON_BOOK:
+ {
+ switch (o_ptr->sval)
+ {
+ case SV_DEMONBLADE:
+ {
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Hook to specify "armour"
+ */
+bool item_tester_hook_armour(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ {
+ return (TRUE);
+ }
+ case TV_DAEMON_BOOK:
+ {
+ switch (o_ptr->sval)
+ {
+ case SV_DEMONHORN:
+ case SV_DEMONSHIELD:
+ {
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Check if an object is weapon or armour (but not arrow, bolt, or shot)
+ */
+bool item_tester_hook_weapon_armour(object_type *o_ptr)
+{
+ return (item_tester_hook_weapon(o_ptr) ||
+ item_tester_hook_armour(o_ptr));
+}
+
+/*
+ * Check if an object is artifactable
+ */
+bool item_tester_hook_artifactable(object_type *o_ptr)
+{
+ return ((item_tester_hook_weapon(o_ptr) ||
+ item_tester_hook_armour(o_ptr) ||
+ (o_ptr->tval == TV_DIGGING) ||
+ (o_ptr->tval == TV_RING) || (o_ptr->tval == TV_AMULET))
+ /* be nice: allow only normal items */
+ && (!artifact_p(o_ptr)) && (!ego_item_p(o_ptr)));
+}
+
+
+/*
+ * Enchants a plus onto an item. -RAK-
+ *
+ * Revamped! Now takes item pointer, number of times to try enchanting,
+ * and a flag of what to try enchanting. Artifacts resist enchantment
+ * some of the time, and successful enchantment to at least +0 might
+ * break a curse on the item. -CFT-
+ *
+ * Note that an item can technically be enchanted all the way to +15 if
+ * you wait a very, very, long time. Going from +9 to +10 only works
+ * about 5% of the time, and from +10 to +11 only about 1% of the time.
+ *
+ * Note that this function can now be used on "piles" of items, and
+ * the larger the pile, the lower the chance of success.
+ */
+bool enchant(object_type *o_ptr, int n, int eflag)
+{
+ int i, chance, prob;
+ bool res = FALSE;
+ bool a = (artifact_p(o_ptr) || o_ptr->art_name);
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Large piles resist enchantment */
+ prob = o_ptr->number * 100;
+
+ /* Missiles are easy to enchant */
+ if ((o_ptr->tval == TV_BOLT) ||
+ (o_ptr->tval == TV_ARROW) ||
+ (o_ptr->tval == TV_SHOT))
+ {
+ prob = prob / 20;
+ }
+
+ /* Try "n" times */
+ for (i = 0; i < n; i++)
+ {
+ /* Hack -- Roll for pile resistance */
+ if (rand_int(prob) >= 100) continue;
+
+ /* Enchant to hit */
+ if (eflag & (ENCH_TOHIT))
+ {
+ if (o_ptr->to_h < 0) chance = 0;
+ else if (o_ptr->to_h > 15) chance = 1000;
+ else chance = enchant_table[o_ptr->to_h];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->to_h++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->to_h >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+
+ /* Enchant to damage */
+ if (eflag & (ENCH_TODAM))
+ {
+ if (o_ptr->to_d < 0) chance = 0;
+ else if (o_ptr->to_d > 15) chance = 1000;
+ else chance = enchant_table[o_ptr->to_d];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->to_d++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->to_d >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+
+
+ /* Enchant to damage */
+ if (eflag & (ENCH_PVAL))
+ {
+ if (o_ptr->pval < 0) chance = 0;
+ else if (o_ptr->pval > 6) chance = 1000;
+ else chance = enchant_table[o_ptr->pval * 2];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->pval++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->pval >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+
+ /* Enchant to armor class */
+ if (eflag & (ENCH_TOAC))
+ {
+ if (o_ptr->to_a < 0) chance = 0;
+ else if (o_ptr->to_a > 15) chance = 1000;
+ else chance = enchant_table[o_ptr->to_a];
+
+ if ((randint(1000) > chance) && (!a || (rand_int(100) < 50)))
+ {
+ o_ptr->to_a++;
+ res = TRUE;
+
+ /* only when you get it above -1 -CFT */
+ if (cursed_p(o_ptr) &&
+ (!(f3 & (TR3_PERMA_CURSE))) &&
+ (o_ptr->to_a >= 0) && (rand_int(100) < 25))
+ {
+ msg_print("The curse is broken!");
+ o_ptr->ident &= ~(IDENT_CURSED);
+ o_ptr->ident |= (IDENT_SENSE);
+
+ if (o_ptr->art_flags3 & (TR3_CURSED))
+ o_ptr->art_flags3 &= ~(TR3_CURSED);
+ if (o_ptr->art_flags3 & (TR3_HEAVY_CURSE))
+ o_ptr->art_flags3 &= ~(TR3_HEAVY_CURSE);
+
+ o_ptr->sense = SENSE_UNCURSED;
+ }
+ }
+ }
+ }
+
+ /* Failure */
+ if (!res) return (FALSE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Enchant an item (in the inventory or on the floor)
+ * Note that "num_ac" requires armour, else weapon
+ * Returns TRUE if attempted, FALSE if cancelled
+ */
+bool enchant_spell(int num_hit, int num_dam, int num_ac, int num_pval)
+{
+ int item;
+ bool okay = FALSE;
+ object_type *o_ptr;
+ char o_name[80];
+ cptr q, s;
+
+
+ /* Assume enchant weapon */
+ item_tester_hook = item_tester_hook_weapon;
+
+ /* Enchant armor if requested */
+ if (num_ac) item_tester_hook = item_tester_hook_armour;
+
+ /* Get an item */
+ q = "Enchant which item? ";
+ s = "You have nothing to enchant.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Describe */
+ msg_format("%s %s glow%s brightly!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+
+ /* Enchant */
+ if (enchant(o_ptr, num_hit, ENCH_TOHIT)) okay = TRUE;
+ if (enchant(o_ptr, num_dam, ENCH_TODAM)) okay = TRUE;
+ if (enchant(o_ptr, num_ac, ENCH_TOAC)) okay = TRUE;
+ if (enchant(o_ptr, num_pval, ENCH_PVAL)) okay = TRUE;
+
+ /* Failure */
+ if (!okay)
+ {
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The enchantment failed.");
+ }
+
+ /* Something happened */
+ return (TRUE);
+}
+
+void curse_artifact(object_type * o_ptr)
+{
+ if (o_ptr->pval) o_ptr->pval = 0 - ((o_ptr->pval) + randint(4));
+ if (o_ptr->to_a) o_ptr->to_a = 0 - ((o_ptr->to_a) + randint(4));
+ if (o_ptr->to_h) o_ptr->to_h = 0 - ((o_ptr->to_h) + randint(4));
+ if (o_ptr->to_d) o_ptr->to_d = 0 - ((o_ptr->to_d) + randint(4));
+ o_ptr->art_flags3 |= ( TR3_HEAVY_CURSE | TR3_CURSED );
+#if 0 /* Silly */
+ if (randint(4) == 1) o_ptr-> art_flags3 |= TR3_PERMA_CURSE;
+#endif
+ if (randint(3) == 1) o_ptr-> art_flags3 |= TR3_TY_CURSE;
+ if (randint(2) == 1) o_ptr-> art_flags3 |= TR3_AGGRAVATE;
+ if (randint(3) == 1) o_ptr-> art_flags3 |= TR3_DRAIN_EXP;
+ if (randint(3) == 1) o_ptr-> art_flags4 |= TR4_BLACK_BREATH;
+ if (randint(2) == 1) o_ptr-> art_flags3 |= TR3_TELEPORT;
+ else if (randint(3) == 1) o_ptr->art_flags3 |= TR3_NO_TELE;
+ o_ptr->ident |= IDENT_CURSED;
+}
+
+
+/*
+ * Should be merged with randart code.
+ * looks like BASIC coder's work...
+ */
+void random_plus(object_type * o_ptr, bool is_scroll)
+{
+ int this_type = (o_ptr->tval < TV_BOOTS ? 23 : 19);
+
+ if (artifact_bias == BIAS_WARRIOR)
+ {
+ if (!(o_ptr->art_flags1 & TR1_STR))
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ if (randint(2) == 1) return ; /* 50% chance of being a "free" power */
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_CON))
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ if (randint(2) == 1) return;
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_DEX))
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_MAGE)
+ {
+ if (!(o_ptr->art_flags1 & TR1_INT))
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_PRIESTLY)
+ {
+ if (!(o_ptr->art_flags1 & TR1_WIS))
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_RANGER)
+ {
+ if (!(o_ptr->art_flags1 & TR1_CON))
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ if (randint(2) == 1) return ; /* 50% chance of being a "free" power */
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_DEX))
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ if (randint(2) == 1) return;
+ }
+
+ if (!(o_ptr->art_flags1 & TR1_STR))
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_ROGUE)
+ {
+ if (!(o_ptr->art_flags1 & TR1_STEALTH))
+ {
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_SEARCH))
+ {
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_STR)
+ {
+ if (!(o_ptr->art_flags1 & TR1_STR))
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_WIS)
+ {
+ if (!(o_ptr->art_flags1 & TR1_WIS))
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_INT)
+ {
+ if (!(o_ptr->art_flags1 & TR1_INT))
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_DEX)
+ {
+ if (!(o_ptr->art_flags1 & TR1_DEX))
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CON)
+ {
+ if (!(o_ptr->art_flags1 & TR1_CON))
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHR)
+ {
+ if (!(o_ptr->art_flags1 & TR1_CHR))
+ {
+ o_ptr->art_flags1 |= TR1_CHR;
+ if (randint(2) == 1) return;
+ }
+ }
+
+
+ switch (randint(this_type))
+ {
+ case 1:
+ case 2:
+ o_ptr->art_flags1 |= TR1_STR;
+ /* if (is_scroll) msg_print("It makes you feel strong!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_STR;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_WARRIOR;
+ break;
+ case 3:
+ case 4:
+ o_ptr->art_flags1 |= TR1_INT;
+ /* if (is_scroll) msg_print("It makes you feel smart!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_INT;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_MAGE;
+ break;
+ case 5:
+ case 6:
+ o_ptr->art_flags1 |= TR1_WIS;
+ /* if (is_scroll) msg_print("It makes you feel wise!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_WIS;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+ case 7:
+ case 8:
+ o_ptr->art_flags1 |= TR1_DEX;
+ /* if (is_scroll) msg_print("It makes you feel nimble!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_DEX;
+ else if (!(artifact_bias) && randint(7) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 9:
+ case 10:
+ o_ptr->art_flags1 |= TR1_CON;
+ /* if (is_scroll) msg_print("It makes you feel healthy!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_CON;
+ else if (!(artifact_bias) && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+ case 11:
+ case 12:
+ o_ptr->art_flags1 |= TR1_CHR;
+ /* if (is_scroll) msg_print("It makes you look great!"); */
+ if (!(artifact_bias) && randint(13) != 1)
+ artifact_bias = BIAS_CHR;
+ break;
+ case 13:
+ case 14:
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ /* if (is_scroll) msg_print("It looks muffled."); */
+ if (!(artifact_bias) && randint(3) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 15:
+ case 16:
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ /* if (is_scroll) msg_print("It makes you see better."); */
+ if (!(artifact_bias) && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+ case 17:
+ case 18:
+ o_ptr->art_flags1 |= TR1_INFRA;
+ /* if (is_scroll) msg_print("It makes you see tiny red animals.");*/
+ break;
+ case 19:
+ o_ptr->art_flags1 |= TR1_SPEED;
+ /* if (is_scroll) msg_print("It makes you move faster!"); */
+ if (!(artifact_bias) && randint(11) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 20:
+ case 21:
+ o_ptr->art_flags1 |= TR1_TUNNEL;
+ /* if (is_scroll) msg_print("Gravel flies from it!"); */
+ break;
+ case 22:
+ case 23:
+ if (o_ptr->tval == TV_BOW) random_plus(o_ptr, is_scroll);
+ else
+ {
+ o_ptr->art_flags1 |= TR1_BLOWS;
+ /* if (is_scroll) msg_print("It seems faster!"); */
+ if (!(artifact_bias) && randint(11) == 1)
+ artifact_bias = BIAS_WARRIOR;
+ }
+ break;
+ }
+}
+
+
+void random_resistance (object_type * o_ptr, bool is_scroll, int specific)
+{
+ /* To avoid a number of possible bugs */
+ if (!specific)
+ {
+ if (artifact_bias == BIAS_ACID)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_ACID))
+ {
+ o_ptr->art_flags2 |= TR2_RES_ACID;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_ACID))
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_ELEC)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_ELEC))
+ {
+ o_ptr->art_flags2 |= TR2_RES_ELEC;
+ if (rand_int(2) == 0) return;
+ }
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR &&
+ !(o_ptr->art_flags3 & TR3_SH_ELEC))
+ {
+ o_ptr->art_flags2 |= TR3_SH_ELEC;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_ELEC))
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ if (rand_int(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_FIRE)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_FIRE))
+ {
+ o_ptr->art_flags2 |= TR2_RES_FIRE;
+ if (rand_int(2) == 0) return;
+ }
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR &&
+ !(o_ptr->art_flags3 & TR3_SH_FIRE))
+ {
+ o_ptr->art_flags2 |= TR3_SH_FIRE;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_FIRE))
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_COLD)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_COLD))
+ {
+ o_ptr->art_flags2 |= TR2_RES_COLD;
+ if (rand_int(2) == 0) return;
+ }
+ if (rand_int(BIAS_LUCK) == 0 && !(o_ptr->art_flags2 & TR2_IM_COLD))
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_POIS)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_POIS))
+ {
+ o_ptr->art_flags2 |= TR2_RES_POIS;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_WARRIOR)
+ {
+ if (rand_int(3) && (!(o_ptr->art_flags2 & TR2_RES_FEAR)))
+ {
+ o_ptr->art_flags2 |= TR2_RES_FEAR;
+ if (rand_int(2) == 0) return;
+ }
+ if ((rand_int(3) == 0) && (!(o_ptr->art_flags3 & TR3_NO_MAGIC)))
+ {
+ o_ptr->art_flags3 |= TR3_NO_MAGIC;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_NECROMANTIC)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_NETHER))
+ {
+ o_ptr->art_flags2 |= TR2_RES_NETHER;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_POIS))
+ {
+ o_ptr->art_flags2 |= TR2_RES_POIS;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_DARK))
+ {
+ o_ptr->art_flags2 |= TR2_RES_DARK;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHAOS)
+ {
+ if (!(o_ptr->art_flags2 & TR2_RES_CHAOS))
+ {
+ o_ptr->art_flags2 |= TR2_RES_CHAOS;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_CONF))
+ {
+ o_ptr->art_flags2 |= TR2_RES_CONF;
+ if (rand_int(2) == 0) return;
+ }
+ if (!(o_ptr->art_flags2 & TR2_RES_DISEN))
+ {
+ o_ptr->art_flags2 |= TR2_RES_DISEN;
+ if (rand_int(2) == 0) return;
+ }
+ }
+ }
+
+ switch (specific ? specific : randint(41))
+ {
+ case 1 :
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ /* if (is_scroll) msg_print("It looks totally incorruptible."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ACID;
+ }
+ break;
+ case 2:
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ /* if (is_scroll) msg_print("It looks completely grounded."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ELEC;
+ }
+ break;
+ case 3:
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ /* if (is_scroll) msg_print("It feels very warm."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_COLD;
+ }
+ break;
+ case 4:
+ if (randint(WEIRD_LUCK) != 1)
+ random_resistance(o_ptr, is_scroll, specific);
+ else
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ /* if (is_scroll) msg_print("It feels very cool."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_FIRE;
+ }
+ break;
+ case 5:
+ case 6:
+ case 13:
+ o_ptr->art_flags2 |= TR2_RES_ACID;
+ /* if (is_scroll) msg_print("It makes your stomach rumble."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ACID;
+ break;
+ case 7:
+ case 8:
+ case 14:
+ o_ptr->art_flags2 |= TR2_RES_ELEC;
+ /* if (is_scroll) msg_print("It makes you feel grounded."); */
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ELEC;
+ break;
+ case 9:
+ case 10:
+ case 15:
+ o_ptr->art_flags2 |= TR2_RES_FIRE;
+ /* if (is_scroll) msg_print("It makes you feel cool!");*/
+ if (!(artifact_bias))
+ artifact_bias = BIAS_FIRE;
+ break;
+ case 11:
+ case 12:
+ case 16:
+ o_ptr->art_flags2 |= TR2_RES_COLD;
+ /* if (is_scroll) msg_print("It makes you feel full of hot air!");*/
+ if (!(artifact_bias))
+ artifact_bias = BIAS_COLD;
+ break;
+ case 17:
+ case 18:
+ o_ptr->art_flags2 |= TR2_RES_POIS;
+ /* if (is_scroll) msg_print("It makes breathing easier for you."); */
+ if (!(artifact_bias) && randint(4) != 1)
+ artifact_bias = BIAS_POIS;
+ else if (!(artifact_bias) && randint(2) == 1)
+ artifact_bias = BIAS_NECROMANTIC;
+ else if (!(artifact_bias) && randint(2) == 1)
+ artifact_bias = BIAS_ROGUE;
+ break;
+ case 19:
+ case 20:
+ o_ptr->art_flags2 |= TR2_RES_FEAR;
+ /* if (is_scroll) msg_print("It makes you feel brave!"); */
+ if (!(artifact_bias) && randint(3) == 1)
+ artifact_bias = BIAS_WARRIOR;
+ break;
+ case 21:
+ o_ptr->art_flags2 |= TR2_RES_LITE;
+ /* if (is_scroll) msg_print("It makes everything look darker.");*/
+ break;
+ case 22:
+ o_ptr->art_flags2 |= TR2_RES_DARK;
+ /* if (is_scroll) msg_print("It makes everything look brigher.");*/
+ break;
+ case 23:
+ case 24:
+ o_ptr->art_flags2 |= TR2_RES_BLIND;
+ /* if (is_scroll) msg_print("It makes you feel you are wearing glasses.");*/
+ break;
+ case 25:
+ case 26:
+ o_ptr->art_flags2 |= TR2_RES_CONF;
+ /* if (is_scroll) msg_print("It makes you feel very determined.");*/
+ if (!(artifact_bias) && randint(6) == 1)
+ artifact_bias = BIAS_CHAOS;
+ break;
+ case 27:
+ case 28:
+ o_ptr->art_flags2 |= TR2_RES_SOUND;
+ /* if (is_scroll) msg_print("It makes you feel deaf!");*/
+ break;
+ case 29:
+ case 30:
+ o_ptr->art_flags2 |= TR2_RES_SHARDS;
+ /* if (is_scroll) msg_print("It makes your skin feel thicker.");*/
+ break;
+ case 31:
+ case 32:
+ o_ptr->art_flags2 |= TR2_RES_NETHER;
+ /* if (is_scroll) msg_print("It makes you feel like visiting a graveyard!");*/
+ if (!(artifact_bias) && randint(3) == 1)
+ artifact_bias = BIAS_NECROMANTIC;
+ break;
+ case 33:
+ case 34:
+ o_ptr->art_flags2 |= TR2_RES_NEXUS;
+ /* if (is_scroll) msg_print("It makes you feel normal.");*/
+ break;
+ case 35:
+ case 36:
+ o_ptr->art_flags2 |= TR2_RES_CHAOS;
+ /* if (is_scroll) msg_print("It makes you feel very firm.");*/
+ if (!(artifact_bias) && randint(2) == 1)
+ artifact_bias = BIAS_CHAOS;
+ break;
+ case 37:
+ case 38:
+ o_ptr->art_flags2 |= TR2_RES_DISEN;
+ /* if (is_scroll) msg_print("It is surrounded by a static feeling.");*/
+ break;
+ case 39:
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR)
+ o_ptr->art_flags3 |= TR3_SH_ELEC;
+ else
+ random_resistance(o_ptr, is_scroll, specific);
+ if (!(artifact_bias))
+ artifact_bias = BIAS_ELEC;
+ break;
+ case 40:
+ if (o_ptr->tval >= TV_CLOAK && o_ptr->tval <= TV_HARD_ARMOR)
+ o_ptr->art_flags3 |= TR3_SH_FIRE;
+ else
+ random_resistance(o_ptr, is_scroll, specific);
+ if (!(artifact_bias))
+ artifact_bias = BIAS_FIRE;
+ break;
+ case 41:
+ if (o_ptr->tval == TV_SHIELD || o_ptr->tval == TV_CLOAK ||
+ o_ptr->tval == TV_HELM || o_ptr->tval == TV_HARD_ARMOR)
+ o_ptr->art_flags2 |= TR2_REFLECT;
+ else
+ random_resistance(o_ptr, is_scroll, specific);
+ break;
+ }
+}
+
+void random_misc(object_type * o_ptr, bool is_scroll)
+{
+
+ if (artifact_bias == BIAS_RANGER)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_CON))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_STR)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_STR))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_WIS)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_WIS))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_INT)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_INT))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_DEX)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_DEX))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CON)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_CON))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHR)
+ {
+ if (!(o_ptr->art_flags2 & TR2_SUST_CHR))
+ {
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_CHAOS)
+ {
+ if (!(o_ptr->art_flags3 & TR3_TELEPORT))
+ {
+ o_ptr->art_flags3 |= TR3_TELEPORT;
+ if (randint(2) == 1) return;
+ }
+ }
+ else if (artifact_bias == BIAS_FIRE)
+ {
+ if (!(o_ptr->art_flags3 & TR3_LITE1))
+ {
+ o_ptr->art_flags3 |= TR3_LITE1; /* Freebie */
+ }
+ }
+
+
+ switch (randint(31))
+ {
+ case 1:
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become weaker."); */
+ if (!artifact_bias)
+ artifact_bias = BIAS_STR;
+ break;
+
+ case 2:
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become more stupid.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_INT;
+ break;
+
+ case 3:
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become simpler.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_WIS;
+ break;
+
+ case 4:
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become clumsier.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_DEX;
+ break;
+
+ case 5:
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become less healthy.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_CON;
+ break;
+
+ case 6:
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ /* if (is_scroll) msg_print("It makes you feel you cannot become uglier.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_CHR;
+ break;
+
+ case 7:
+ case 8:
+ case 14:
+ o_ptr->art_flags2 |= TR2_FREE_ACT;
+ /* if (is_scroll) msg_print("It makes you feel like a young rebel!");*/
+ break;
+
+ case 9:
+ o_ptr->art_flags2 |= TR2_HOLD_LIFE;
+ /* if (is_scroll) msg_print("It makes you feel immortal.");*/
+ if (!artifact_bias && (randint(5) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ else if (!artifact_bias && (randint(6) == 1))
+ artifact_bias = BIAS_NECROMANTIC;
+ break;
+
+ case 10:
+ case 11:
+ o_ptr->art_flags3 |= TR3_LITE1;
+ /* if (is_scroll) msg_print("It starts shining.");*/
+ break;
+
+ case 12:
+ case 13:
+ o_ptr->art_flags3 |= TR3_FEATHER;
+ /* if (is_scroll) msg_print("It feels lighter.");*/
+ break;
+
+ case 15:
+ case 16:
+ case 17:
+ o_ptr->art_flags3 |= TR3_SEE_INVIS;
+ /* if (is_scroll) msg_print("It makes you see the air!");*/
+ break;
+
+ case 18:
+ o_ptr->art_esp |= 1 << (rand_int(32));
+ /* if (is_scroll) msg_print("It makes you hear voices inside your head!");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_MAGE;
+ break;
+
+ case 19:
+ case 20:
+ o_ptr->art_flags3 |= TR3_SLOW_DIGEST;
+ /* if (is_scroll) msg_print("It makes you feel less hungry.");*/
+ break;
+
+ case 21:
+ case 22:
+ o_ptr->art_flags3 |= TR3_REGEN;
+ /* if (is_scroll) msg_print("It looks as good as new.");*/
+ break;
+
+ case 23:
+ o_ptr->art_flags3 |= TR3_TELEPORT;
+ /* if (is_scroll) msg_print("Its position feels uncertain!");*/
+ break;
+
+ case 24:
+ case 25:
+ case 26:
+ if (o_ptr->tval >= TV_BOOTS) random_misc(o_ptr, is_scroll);
+ else
+ {
+ o_ptr->art_flags3 |= TR3_SHOW_MODS;
+ o_ptr->to_a = 4 + (randint(11));
+ }
+ break;
+
+ case 27:
+ case 28:
+ case 29:
+ o_ptr->art_flags3 |= TR3_SHOW_MODS;
+ o_ptr->to_h += 4 + (randint(11));
+ o_ptr->to_d += 4 + (randint(11));
+ break;
+
+ case 30:
+ o_ptr->art_flags3 |= TR3_NO_MAGIC;
+ break;
+
+ case 31:
+ o_ptr->art_flags3 |= TR3_NO_TELE;
+ break;
+ }
+
+
+}
+
+
+void random_slay (object_type * o_ptr, bool is_scroll)
+{
+ if (artifact_bias == BIAS_CHAOS && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_CHAOTIC))
+ {
+ o_ptr->art_flags1 |= TR1_CHAOTIC;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_PRIESTLY &&
+ (o_ptr->tval == TV_SWORD || o_ptr->tval == TV_POLEARM || o_ptr->tval == TV_AXE) &&
+ !(o_ptr->art_flags3 & TR3_BLESSED))
+ {
+ /* A free power for "priestly" random artifacts */
+ o_ptr->art_flags3 |= TR3_BLESSED;
+ }
+
+ else if (artifact_bias == BIAS_NECROMANTIC && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_VAMPIRIC))
+ {
+ o_ptr->art_flags1 |= TR1_VAMPIRIC;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_BRAND_POIS) && (randint(2) == 1))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_RANGER && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_SLAY_ANIMAL))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_ANIMAL;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_ROGUE && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_POIS))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_POIS && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_POIS))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_FIRE && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_FIRE))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_FIRE;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_COLD && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_COLD))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_COLD;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_ELEC && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_ELEC))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_ELEC;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_ACID && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_BRAND_ACID))
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_ACID;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ else if (artifact_bias == BIAS_LAW && !(o_ptr->tval == TV_BOW))
+ {
+ if (!(o_ptr->art_flags1 & TR1_SLAY_EVIL))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_EVIL;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_SLAY_UNDEAD))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_UNDEAD;
+ if (randint(2) == 1) return;
+ }
+ if (!(o_ptr->art_flags1 & TR1_SLAY_DEMON))
+ {
+ o_ptr->art_flags1 |= TR1_SLAY_DEMON;
+ if (randint(2) == 1) return;
+ }
+ }
+
+ if (!(o_ptr->tval == TV_BOW))
+ {
+ switch (randint(34))
+ {
+ case 1:
+ case 2:
+ o_ptr->art_flags1 |= TR1_SLAY_ANIMAL;
+ /* if (is_scroll) msg_print("You start hating animals.");*/
+ break;
+
+ case 3:
+ case 4:
+ o_ptr->art_flags1 |= TR1_SLAY_EVIL;
+ /* if (is_scroll) msg_print("You hate evil creatures.");*/
+ if (!artifact_bias && (randint(2) == 1))
+ artifact_bias = BIAS_LAW;
+ else if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+
+ case 5:
+ case 6:
+ o_ptr->art_flags1 |= TR1_SLAY_UNDEAD;
+ /* if (is_scroll) msg_print("You hate undead creatures.");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+
+ case 7:
+ case 8:
+ o_ptr->art_flags1 |= TR1_SLAY_DEMON;
+ /* if (is_scroll) msg_print("You hate demons.");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_PRIESTLY;
+ break;
+
+ case 9:
+ case 10:
+ o_ptr->art_flags1 |= TR1_SLAY_ORC;
+ /* if (is_scroll) msg_print("You hate orcs.");*/
+ break;
+
+ case 11:
+ case 12:
+ o_ptr->art_flags1 |= TR1_SLAY_TROLL;
+ /* if (is_scroll) msg_print("You hate trolls.");*/
+ break;
+
+ case 13:
+ case 14:
+ o_ptr->art_flags1 |= TR1_SLAY_GIANT;
+ /* if (is_scroll) msg_print("You hate giants.");*/
+ break;
+
+ case 15:
+ case 16:
+ o_ptr->art_flags1 |= TR1_SLAY_DRAGON;
+ /* if (is_scroll) msg_print("You hate dragons.");*/
+ break;
+
+ case 17:
+ o_ptr->art_flags1 |= TR1_KILL_DRAGON;
+ /* if (is_scroll) msg_print("You feel an intense hatred of dragons.");*/
+ break;
+
+ case 18:
+ case 19:
+ if (o_ptr->tval == TV_SWORD)
+ {
+ o_ptr->art_flags1 |= TR1_VORPAL;
+ /* if (is_scroll) msg_print("It looks extremely sharp!");*/
+ if (!artifact_bias && (randint(9) == 1))
+ artifact_bias = BIAS_WARRIOR;
+ }
+ else random_slay(o_ptr, is_scroll);
+ break;
+
+ case 20:
+ o_ptr->art_flags1 |= TR1_IMPACT;
+ /* if (is_scroll) msg_print("The ground trembles beneath you.");*/
+ break;
+
+ case 21:
+ case 22:
+ o_ptr->art_flags1 |= TR1_BRAND_FIRE;
+ /* if (is_scroll) msg_print("It feels hot!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_FIRE;
+ break;
+
+ case 23:
+ case 24:
+ o_ptr->art_flags1 |= TR1_BRAND_COLD;
+ /* if (is_scroll) msg_print("It feels cold!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_COLD;
+ break;
+
+ case 25:
+ case 26:
+ o_ptr->art_flags1 |= TR1_BRAND_ELEC;
+ /* if (is_scroll) msg_print("Ouch! You get zapped!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_ELEC;
+ break;
+
+ case 27:
+ case 28:
+ o_ptr->art_flags1 |= TR1_BRAND_ACID;
+ /* if (is_scroll) msg_print("Its smell makes you feel dizzy.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_ACID;
+ break;
+
+ case 29:
+ case 30:
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ /* if (is_scroll) msg_print("It smells rotten.");*/
+ if (!artifact_bias && (randint(3) != 1))
+ artifact_bias = BIAS_POIS;
+ else if (!artifact_bias && randint(6) == 1)
+ artifact_bias = BIAS_NECROMANTIC;
+ else if (!artifact_bias)
+ artifact_bias = BIAS_ROGUE;
+ break;
+
+ case 31:
+ case 32:
+ o_ptr->art_flags1 |= TR1_VAMPIRIC;
+ /* if (is_scroll) msg_print("You think it bit you!");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_NECROMANTIC;
+ break;
+
+ default:
+ o_ptr->art_flags1 |= TR1_CHAOTIC;
+ /* if (is_scroll) msg_print("It looks very confusing.");*/
+ if (!artifact_bias)
+ artifact_bias = BIAS_CHAOS;
+ break;
+ }
+ }
+ else
+ {
+ switch (randint(6))
+ {
+ case 1:
+ case 2:
+ case 3:
+ o_ptr->art_flags3 |= TR3_XTRA_MIGHT;
+ /* if (is_scroll) msg_print("It looks mightier than before."); */
+ if (!artifact_bias && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+
+ default:
+ o_ptr->art_flags3 |= TR3_XTRA_SHOTS;
+ /* if (is_scroll) msg_print("It seems faster!"); */
+ if (!artifact_bias && randint(9) == 1)
+ artifact_bias = BIAS_RANGER;
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Determines if an item is not identified
+ */
+static bool item_tester_hook_unknown(object_type *o_ptr)
+{
+ return (object_known_p(o_ptr) ? FALSE : TRUE);
+}
+
+
+/*
+ * Identify an object in the inventory (or on the floor)
+ * This routine does *not* automatically combine objects.
+ * Returns TRUE if something was identified, else FALSE.
+ */
+bool ident_spell(void)
+{
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ cptr q, s;
+
+ /* Get an item */
+ item_tester_hook = item_tester_hook_unknown;
+ q = "Identify which item? ";
+ s = "You have nothing to identify.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ if (item >= INVEN_WIELD)
+ {
+ msg_format("%^s: %s (%c).",
+ describe_use(item), o_name, index_to_label(item));
+ }
+ else if (item >= 0)
+ {
+ msg_format("In your pack: %s (%c).",
+ o_name, index_to_label(item));
+ }
+ else
+ {
+ msg_format("On the ground: %s.",
+ o_name);
+ }
+
+ /* If the item was an artifact, and if the auto-note is selected, write a message. */
+ if (take_notes && auto_notes && (artifact_p(o_ptr) || o_ptr->name1))
+ {
+ char note[150];
+ char item_name[80];
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+
+ sprintf(note, "has found The %s", item_name);
+ irc_emote(note);
+ }
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", item, "normal");
+
+ /* Something happened */
+ return (TRUE);
+}
+
+/*
+ * Identify all objects in the level
+ */
+bool ident_all(void)
+{
+ int i;
+
+ object_type *o_ptr;
+
+ for (i = 1; i < o_max; i++)
+ {
+ /* Acquire object */
+ o_ptr = &o_list[i];
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* If the item was an artifact, and if the auto-note is selected, write a message. */
+ if (take_notes && auto_notes && (artifact_p(o_ptr) || o_ptr->name1))
+ {
+ char note[150];
+ char item_name[80];
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+
+ sprintf(note, "has found The %s", item_name);
+ irc_emote(note);
+ }
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", -i, "normal");
+ }
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+
+/*
+ * Determine if an object is not fully identified
+ */
+static bool item_tester_hook_no_mental(object_type *o_ptr)
+{
+ return ((o_ptr->ident & (IDENT_MENTAL)) ? FALSE : TRUE);
+}
+
+/*
+ * Fully "identify" an object in the inventory -BEN-
+ * This routine returns TRUE if an item was identified.
+ */
+bool identify_fully(void)
+{
+ int item;
+ object_type *o_ptr;
+ char o_name[80];
+
+ cptr q, s;
+
+ /* Get an item */
+ item_tester_hook = item_tester_hook_no_mental;
+ q = "Identify which item? ";
+ s = "You have nothing to identify.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Do the identification */
+ make_item_fully_identified(o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ if (item >= INVEN_WIELD)
+ {
+ msg_format("%^s: %s (%c).",
+ describe_use(item), o_name, index_to_label(item));
+ }
+ else if (item >= 0)
+ {
+ msg_format("In your pack: %s (%c).",
+ o_name, index_to_label(item));
+ }
+ else
+ {
+ msg_format("On the ground: %s.",
+ o_name);
+ }
+
+ /* If the item was an artifact, and if the auto-note is selected, write a message. */
+ if (take_notes && auto_notes && (artifact_p(o_ptr) || o_ptr->name1))
+ {
+ char note[150];
+ char item_name[80];
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+
+ sprintf(note, "has found The %s", item_name);
+ irc_emote(note);
+ }
+
+ /* Describe it fully */
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_IDENTIFY, "(d,s)", item, "full");
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Hook for "get_item()". Determine if something is rechargable.
+ */
+bool item_tester_hook_recharge(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Some objects cannot be recharged */
+ if (f4 & TR4_NO_RECHARGE) return (FALSE);
+
+ /* Recharge staffs */
+ if (o_ptr->tval == TV_STAFF) return (TRUE);
+
+ /* Recharge wands */
+ if (o_ptr->tval == TV_WAND) return (TRUE);
+
+ /* Hack -- Recharge rods */
+ if (o_ptr->tval == TV_ROD_MAIN) return (TRUE);
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * Recharge a wand/staff/rod from the pack or on the floor.
+ * This function has been rewritten in Oangband. -LM-
+ *
+ * Mage -- Recharge I --> recharge(90)
+ * Mage -- Recharge II --> recharge(150)
+ * Mage -- Recharge III --> recharge(220)
+ *
+ * Priest or Necromancer -- Recharge --> recharge(140)
+ *
+ * Scroll of recharging --> recharge(130)
+ * Scroll of *recharging* --> recharge(200)
+ *
+ * It is harder to recharge high level, and highly charged wands,
+ * staffs, and rods. The more wands in a stack, the more easily and
+ * strongly they recharge. Staffs, however, each get fewer charges if
+ * stacked.
+ *
+ * XXX XXX XXX Beware of "sliding index errors".
+ */
+bool recharge(int power)
+{
+ int recharge_strength, recharge_amount;
+ int item, lev;
+
+ bool fail = FALSE;
+ byte fail_type = 1;
+
+
+ cptr q, s;
+
+ u32b f1, f2, f3, f4, f5, esp;
+ char o_name[80];
+
+ object_type *o_ptr;
+ object_kind *k_ptr;
+
+ /* Only accept legal items */
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Recharge which item? ";
+ s = "You have nothing to recharge.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return (FALSE);
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Extract the object "level" */
+ lev = k_info[o_ptr->k_idx].level;
+ k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Recharge a rod */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Extract a recharge strength by comparing object level to power. */
+ recharge_strength = ((power > lev) ? (power - lev) : 0) / 5;
+
+ /* Paranoia */
+ if (recharge_strength < 0) recharge_strength = 0;
+
+ /* Back-fire */
+ if ((rand_int(recharge_strength) == 0) && (!(f4 & TR4_RECHARGE)))
+ {
+ /* Activate the failure code. */
+ fail = TRUE;
+ }
+
+ /* Recharge */
+ else
+ {
+ /* Recharge amount */
+ recharge_amount = (power * damroll(3, 2));
+
+ /* Recharge by that amount */
+ if (o_ptr->timeout + recharge_amount < o_ptr->pval2)
+ o_ptr->timeout += recharge_amount;
+ else
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+
+ /* Recharge wand/staff */
+ else
+ {
+ /* Extract a recharge strength by comparing object level to power.
+ * Divide up a stack of wands' charges to calculate charge penalty.
+ */
+ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1))
+ recharge_strength = (100 + power - lev -
+ (8 * o_ptr->pval / o_ptr->number)) / 15;
+
+ /* All staffs, unstacked wands. */
+ else recharge_strength = (100 + power - lev -
+ (8 * o_ptr->pval)) / 15;
+
+
+ /* Back-fire XXX XXX XXX */
+ if (((rand_int(recharge_strength) == 0) && (!(f4 & TR4_RECHARGE))) ||
+ (f4 & TR4_NO_RECHARGE))
+ {
+ /* Activate the failure code. */
+ fail = TRUE;
+ }
+
+ /* If the spell didn't backfire, recharge the wand or staff. */
+ else
+ {
+ /* Recharge based on the standard number of charges. */
+ recharge_amount = randint((power / (lev + 2)) + 1);
+
+ /* Multiple wands in a stack increase recharging somewhat. */
+ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1))
+ {
+ recharge_amount +=
+ (randint(recharge_amount * (o_ptr->number - 1))) / 2;
+ if (recharge_amount < 1) recharge_amount = 1;
+ if (recharge_amount > 12) recharge_amount = 12;
+ }
+
+ /* But each staff in a stack gets fewer additional charges,
+ * although always at least one.
+ */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ recharge_amount /= o_ptr->number;
+ if (recharge_amount < 1) recharge_amount = 1;
+ }
+
+ /* Recharge the wand or staff. */
+ o_ptr->pval += recharge_amount;
+
+ if (!(f4 & TR4_RECHARGE))
+ {
+ /* Hack -- we no longer "know" the item */
+ o_ptr->ident &= ~(IDENT_KNOWN);
+ }
+
+ /* Hack -- we no longer think the item is empty */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+ }
+ }
+
+ /* Mark as recharged -- For alchemists */
+ o_ptr->art_flags4 |= TR4_RECHARGED;
+
+ /* Inflict the penalties for failing a recharge. */
+ if (fail)
+ {
+ /* Artifacts are never destroyed. */
+ if (artifact_p(o_ptr))
+ {
+ object_desc(o_name, o_ptr, TRUE, 0);
+ msg_format("The recharging backfires - %s is completely drained!", o_name);
+
+ /* Artifact rods. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ o_ptr->timeout = 0;
+
+ /* Artifact wands and staffs. */
+ else if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
+ o_ptr->pval = 0;
+ }
+ else
+ {
+ /* Get the object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /*** Determine Seriousness of Failure ***/
+
+ /* Mages recharge objects more safely. */
+ if (has_ability(AB_PERFECT_CASTING))
+ {
+ /* 10% chance to blow up one rod, otherwise draining. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (randint(10) == 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 75% chance to blow up one wand, otherwise draining. */
+ else if (o_ptr->tval == TV_WAND)
+ {
+ if (randint(3) != 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 50% chance to blow up one staff, otherwise no effect. */
+ else if (o_ptr->tval == TV_STAFF)
+ {
+ if (randint(2) == 1) fail_type = 2;
+ else fail_type = 0;
+ }
+ }
+
+ /* All other classes get no special favors. */
+ else
+ {
+ /* 33% chance to blow up one rod, otherwise draining. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (randint(3) == 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 20% chance of the entire stack, else destroy one wand. */
+ else if (o_ptr->tval == TV_WAND)
+ {
+ if (randint(5) == 1) fail_type = 3;
+ else fail_type = 2;
+ }
+ /* Blow up one staff. */
+ else if (o_ptr->tval == TV_STAFF)
+ {
+ fail_type = 2;
+ }
+ }
+
+ /*** Apply draining and destruction. ***/
+
+ /* Drain object or stack of objects. */
+ if (fail_type == 1)
+ {
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ msg_print("The recharge backfires, draining the rod further!");
+ if (o_ptr->timeout < 10000)
+ o_ptr->timeout = 0;
+ }
+ else if (o_ptr->tval == TV_WAND)
+ {
+ msg_format("You save your %s from destruction, but all charges are lost.", o_name);
+ o_ptr->pval = 0;
+ }
+ /* Staffs aren't drained. */
+ }
+
+ /* Destroy an object or one in a stack of objects. */
+ if (fail_type == 2)
+ {
+ if (o_ptr->number > 1)
+ msg_format("Wild magic consumes one of your %s!", o_name);
+ else
+ msg_format("Wild magic consumes your %s!", o_name);
+
+ /* Reduce rod stack maximum timeout, drain wands. */
+ if (o_ptr->tval == TV_WAND) o_ptr->pval = 0;
+
+ /* Reduce and describe inventory */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Reduce and describe floor item */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+
+ /* Destroy all memebers of a stack of objects. */
+ if (fail_type == 3)
+ {
+ if (o_ptr->number > 1)
+ msg_format("Wild magic consumes all your %s!", o_name);
+ else
+ msg_format("Wild magic consumes your %s!", o_name);
+
+
+ /* Reduce and describe inventory */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -999);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Reduce and describe floor item */
+ else
+ {
+ floor_item_increase(0 - item, -999);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Something was done */
+ return (TRUE);
+}
+
+
+
+/*
+ * Apply a "project()" directly to all viewable monsters
+ *
+ * Note that affected monsters are NOT auto-tracked by this usage.
+ */
+bool project_hack(int typ, int dam)
+{
+ int i, x, y;
+ int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
+ bool obvious = FALSE;
+
+
+ /* Affect all (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Require line of sight */
+ if (!player_has_los_bold(y, x)) continue;
+
+ /* Jump directly to the target monster */
+ if (project(0, 0, y, x, dam, typ, flg)) obvious = TRUE;
+ }
+
+ /* Result */
+ return (obvious);
+}
+
+/*
+ * Apply a "project()" a la meteor shower
+ */
+void project_meteor(int radius, int typ, int dam, u32b flg)
+{
+ cave_type *c_ptr;
+ int x, y, dx, dy, d, count = 0, i;
+ int b = radius + randint(radius);
+ for (i = 0; i < b; i++)
+ {
+ for (count = 0; count < 1000; count++)
+ {
+ x = p_ptr->px - 5 + randint(10);
+ y = p_ptr->py - 5 + randint(10);
+ if ((x < 0) || (x >= cur_wid) ||
+ (y < 0) || (y >= cur_hgt)) continue;
+ dx = (p_ptr->px > x) ? (p_ptr->px - x) : (x - p_ptr->px);
+ dy = (p_ptr->py > y) ? (p_ptr->py - y) : (y - p_ptr->py);
+ /* Approximate distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+ c_ptr = &cave[y][x];
+ /* Check distance */
+ if ((d <= 5) &&
+ /* Check line of sight */
+ (player_has_los_bold(y, x)) &&
+ /* But don't explode IN a wall, letting the player attack through walls */
+ !(c_ptr->info & CAVE_WALL))
+ break;
+ }
+ if (count >= 1000) break;
+ project(0, 2, y, x, dam, typ, PROJECT_JUMP | flg);
+ }
+}
+
+
+/*
+ * Speed monsters
+ */
+bool speed_monsters(void)
+{
+ return (project_hack(GF_OLD_SPEED, p_ptr->lev));
+}
+
+/*
+ * Slow monsters
+ */
+bool slow_monsters(void)
+{
+ return (project_hack(GF_OLD_SLOW, p_ptr->lev));
+}
+
+/*
+ * Paralyzation monsters
+ */
+bool conf_monsters(void)
+{
+ return (project_hack(GF_OLD_CONF, p_ptr->lev));
+}
+
+/*
+ * Sleep monsters
+ */
+bool sleep_monsters(void)
+{
+ return (project_hack(GF_OLD_SLEEP, p_ptr->lev));
+}
+
+/*
+ * Scare monsters
+ */
+bool scare_monsters(void)
+{
+ return (project_hack(GF_FEAR, p_ptr->lev));
+}
+
+
+/*
+ * Banish evil monsters
+ */
+bool banish_evil(int dist)
+{
+ return (project_hack(GF_AWAY_EVIL, dist));
+}
+
+
+/*
+ * Turn undead
+ */
+bool turn_undead(void)
+{
+ return (project_hack(GF_TURN_UNDEAD, p_ptr->lev));
+}
+
+
+/*
+ * Dispel undead monsters
+ */
+bool dispel_undead(int dam)
+{
+ return (project_hack(GF_DISP_UNDEAD, dam));
+}
+
+/*
+ * Dispel evil monsters
+ */
+bool dispel_evil(int dam)
+{
+ return (project_hack(GF_DISP_EVIL, dam));
+}
+
+/*
+ * Dispel good monsters
+ */
+bool dispel_good(int dam)
+{
+ return (project_hack(GF_DISP_GOOD, dam));
+}
+
+/*
+ * Dispel all monsters
+ */
+bool dispel_monsters(int dam)
+{
+ return (project_hack(GF_DISP_ALL, dam));
+}
+
+/*
+ * Dispel 'living' monsters
+ */
+bool dispel_living(int dam)
+{
+ return (project_hack(GF_DISP_LIVING, dam));
+}
+
+/*
+ * Dispel demons
+ */
+bool dispel_demons(int dam)
+{
+ return (project_hack(GF_DISP_DEMON, dam));
+}
+
+
+/*
+ * Wake up all monsters, and speed up "los" monsters.
+ */
+void aggravate_monsters(int who)
+{
+ int i;
+ bool sleep = FALSE;
+ bool speed = FALSE;
+
+
+ /* Aggravate everyone nearby */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Skip aggravating monster (or player) */
+ if (i == who) continue;
+
+ /* Wake up nearby sleeping monsters */
+ if (m_ptr->cdis < MAX_SIGHT * 2)
+ {
+ /* Wake up */
+ if (m_ptr->csleep)
+ {
+ /* Wake up */
+ m_ptr->csleep = 0;
+ sleep = TRUE;
+ }
+ }
+
+ /* Speed up monsters in line of sight */
+ if (player_has_los_bold(m_ptr->fy, m_ptr->fx))
+ {
+ /* Speed up (instantly) to racial base + 10 */
+ if (m_ptr->mspeed < r_ptr->speed + 10)
+ {
+ /* Speed up */
+ m_ptr->mspeed = r_ptr->speed + 10;
+ speed = TRUE;
+ }
+
+ /* Pets may get angry (50% chance) */
+ if (is_friend(m_ptr))
+ {
+ if (randint(2) == 1)
+ {
+ change_side(m_ptr);
+ }
+ }
+ }
+ }
+
+ /* Messages */
+ if (speed) msg_print("You feel a sudden stirring nearby!");
+ else if (sleep) msg_print("You hear a sudden stirring in the distance!");
+}
+
+/*
+ * Generic genocide race selection
+ */
+bool get_genocide_race(cptr msg, char *typ)
+{
+ int i, j;
+ cave_type *c_ptr;
+
+ msg_print(msg);
+ if (!tgt_pt(&i, &j)) return FALSE;
+
+ c_ptr = &cave[j][i];
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ *typ = r_ptr->d_char;
+ return TRUE;
+ }
+
+ msg_print("You must select a monster.");
+ return FALSE;
+}
+
+/*
+ * Inflict dam damage of type typee to all monster of the given race
+ */
+bool invoke(int dam, int typee)
+{
+ int i;
+ char typ;
+ bool result = FALSE;
+ int msec = delay_factor * delay_factor * delay_factor;
+
+ if (dungeon_flags2 & DF2_NO_GENO) return (FALSE);
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force stops the genocide.");
+ return FALSE;
+ }
+
+ /* Get a monster symbol */
+ if (!get_genocide_race("Target a monster to select the race to invoke on.", &typ)) return FALSE;
+
+ /* Delete the monsters of that "type" */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- Skip Unique Monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Hack -- Skip Quest Monsters */
+ if (m_ptr->mflag & MFLAG_QUEST) continue;
+
+ /* Skip "wrong" monsters */
+ if (r_ptr->d_char != typ) continue;
+
+ project_m(0, 0, m_ptr->fy, m_ptr->fx, dam, typee);
+
+ /* Visual feedback */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+
+ /* Take note */
+ result = TRUE;
+ }
+
+ return (result);
+}
+
+
+/*
+ * Delete all non-unique/non-quest monsters of a given "type" from the level
+ */
+bool genocide_aux(bool player_cast, char typ)
+{
+ int i;
+ bool result = FALSE;
+ int msec = delay_factor * delay_factor * delay_factor;
+ int dam = 0;
+
+ /* Delete the monsters of that "type" */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- Skip Unique Monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Hack -- Skip Quest Monsters */
+ if (m_ptr->mflag & MFLAG_QUEST) continue;
+
+ /* Skip "wrong" monsters */
+ if (r_ptr->d_char != typ) continue;
+
+ /* Oups */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, m_ptr->r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, m_ptr->fy, m_ptr->fx, 10, 0);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_aux(wy, wx, m_ptr->r_idx, FALSE, TRUE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "The spell seems to produce an ... interesting effect on the %s.", r_name);
+ }
+
+ return TRUE;
+ }
+
+ /* Delete the monster */
+ delete_monster_idx(i);
+
+ if (player_cast)
+ {
+ /* Keep track of damage */
+ dam += randint(4);
+ }
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+
+ /* Take note */
+ result = TRUE;
+ }
+
+ if (player_cast)
+ {
+ /* Take damage */
+ take_hit(dam, "the strain of casting Genocide");
+
+ /* Visual feedback */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+ }
+
+ return (result);
+}
+
+bool genocide(bool player_cast)
+{
+ char typ;
+
+ if (dungeon_flags2 & DF2_NO_GENO) return (FALSE);
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force stops the genocide.");
+ return FALSE;
+ }
+
+ /* Mega-Hack -- Get a monster symbol */
+ if (!get_genocide_race("Target a monster to select the race to genocide.", &typ)) return FALSE;
+
+ return (genocide_aux(player_cast, typ));
+}
+
+
+/*
+ * Delete all nearby (non-unique) monsters
+ */
+bool mass_genocide(bool player_cast)
+{
+ int i;
+ bool result = FALSE;
+ int msec = delay_factor * delay_factor * delay_factor;
+ int dam = 0;
+
+ if (dungeon_flags2 & DF2_NO_GENO) return (FALSE);
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force stops the genocide.");
+ return FALSE;
+ }
+
+ /* Delete the (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Hack -- Skip unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Hack -- Skip Quest Monsters */
+ if (m_ptr->mflag & MFLAG_QUEST) continue;
+
+ /* Skip distant monsters */
+ if (m_ptr->cdis > MAX_SIGHT) continue;
+
+ /* Oups */
+ if (r_ptr->flags2 & RF2_DEATH_ORB)
+ {
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, m_ptr->r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, m_ptr->fy, m_ptr->fx, 10, 0);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_aux(wy, wx, m_ptr->r_idx, FALSE, TRUE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "The spell seems to produce an ... interesting effect on the %s.", r_name);
+ }
+
+ return TRUE;
+ }
+
+ /* Delete the monster */
+ delete_monster_idx(i);
+
+ if (player_cast)
+ {
+ /* Keep track of damage. */
+ dam += randint(3);
+ }
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, msec);
+
+ /* Note effect */
+ result = TRUE;
+ }
+
+ if (player_cast)
+ {
+ /* Take damage */
+ take_hit(dam, "the strain of casting Mass Genocide");
+
+ /* Visual feedback */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle */
+ handle_stuff();
+
+ /* Fresh */
+ Term_fresh();
+ }
+
+ return (result);
+}
+
+/* Probe a monster */
+void do_probe(int m_idx)
+{
+ char m_name[80];
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Get "the monster" or "something" */
+ monster_desc(m_name, m_ptr, 0x04);
+
+ /* Describe the monster */
+ if (!wizard && (m_ptr->status != MSTATUS_COMPANION)) msg_format("%^s has %d hit points.", m_name, m_ptr->hp);
+ else
+ {
+ int i;
+ char t_name[80];
+ msg_format("%^s has %d(%d) hit points, %d ac, %d speed.", m_name, m_ptr->hp, m_ptr->maxhp, m_ptr->ac, m_ptr->mspeed - 110);
+ msg_format("%^s attacks with:", m_name);
+
+ for (i = 0; i < 4; i++)
+ {
+ msg_format(" Blow %d: %dd%d", i, m_ptr->blow[i].d_dice, m_ptr->blow[i].d_side);
+ }
+
+ if (m_ptr->target > 0)
+ monster_desc(t_name, &m_list[m_ptr->target], 0x04);
+ else if (!m_ptr->target)
+ sprintf(t_name, "you");
+ else
+ sprintf(t_name, "nothing");
+ msg_format("%^s target is %s.", m_name, t_name);
+
+ msg_format("%^s has %ld exp and needs %ld.", m_name, m_ptr->exp, MONSTER_EXP(m_ptr->level + 1));
+ }
+
+ /* Learn all of the non-spell, non-treasure flags */
+ lore_do_probe(m_idx);
+}
+
+/*
+ * Probe nearby monsters
+ */
+bool probing(void)
+{
+ int i;
+
+ bool probe = FALSE;
+
+
+ /* Probe all (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Require line of sight */
+ if (!player_has_los_bold(m_ptr->fy, m_ptr->fx)) continue;
+
+ /* Probe visible monsters */
+ if (m_ptr->ml)
+ {
+ /* Start the message */
+ if (!probe) msg_print("Probing...");
+
+ /* Actualy probe */
+ do_probe(i);
+
+ /* Probe worked */
+ probe = TRUE;
+ }
+ }
+
+ /* Done */
+ if (probe)
+ {
+ msg_print("That's all.");
+ }
+
+ /* Result */
+ return (probe);
+}
+
+
+/*
+ * Wipe -- Empties a part of the dungeon
+ */
+void wipe(int y1, int x1, int r)
+{
+ int y, x, k;
+
+ cave_type *c_ptr;
+
+ bool flag = FALSE;
+
+ if (dungeon_flags2 & DF2_NO_GENO)
+ {
+ msg_print("Not on special levels!");
+ return;
+ }
+ if (p_ptr->inside_quest)
+ {
+ return;
+ }
+
+ /* Big area of affect */
+ for (y = (y1 - r); y <= (y1 + r); y++)
+ {
+ for (x = (x1 - r); x <= (x1 + r); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k > r) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ if (m_list[c_ptr->m_idx].status != MSTATUS_COMPANION) delete_monster(y, x);
+ delete_object(y, x);
+ place_floor(y, x);
+ }
+ }
+
+
+ /* Hack -- Affect player */
+ if (flag)
+ {
+ /* Message */
+ msg_print("There is a searing blast of light!");
+
+ /* Blind the player */
+ if (!p_ptr->resist_blind && !p_ptr->resist_lite)
+ {
+ /* Become blind */
+ (void)set_blind(p_ptr->blind + 10 + randint(10));
+ }
+ }
+
+
+ /* Mega-Hack -- Forget the view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * The spell of destruction
+ *
+ * This spell "deletes" monsters (instead of "killing" them).
+ *
+ * Later we may use one function for both "destruction" and
+ * "earthquake" by using the "full" to select "destruction".
+ */
+void destroy_area(int y1, int x1, int r, bool full, bool bypass)
+{
+ int y, x, k, t;
+
+ cave_type *c_ptr;
+
+ bool flag = FALSE;
+
+
+ /* XXX XXX */
+ full = full ? full : 0;
+
+ if (dungeon_flags2 & DF2_NO_GENO)
+ {
+ msg_print("Not on special levels!");
+ return;
+ }
+ if (p_ptr->inside_quest && (!bypass))
+ {
+ return;
+ }
+
+ /* Big area of affect */
+ for (y = (y1 - r); y <= (y1 + r); y++)
+ {
+ for (x = (x1 - r); x <= (x1 + r); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k > r) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ /* Hack -- Notice player affect */
+ if ((x == p_ptr->px) && (y == p_ptr->py))
+ {
+ /* Hurt the player later */
+ flag = TRUE;
+
+ /* Do not hurt this grid */
+ continue;
+ }
+
+ /* Hack -- Skip the epicenter */
+ if ((y == y1) && (x == x1)) continue;
+
+ /* Delete the monster (if any) */
+ if ((m_list[c_ptr->m_idx].status != MSTATUS_COMPANION) ||
+ (!(m_list[c_ptr->m_idx].mflag & MFLAG_QUEST)) ||
+ (!(m_list[c_ptr->m_idx].mflag & MFLAG_QUEST2)))
+ delete_monster(y, x);
+
+ /* Destroy "valid" grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 70)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 100)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+ }
+ }
+ }
+
+
+ /* Hack -- Affect player */
+ if (flag)
+ {
+ /* Message */
+ msg_print("There is a searing blast of light!");
+
+ /* Blind the player */
+ if (!p_ptr->resist_blind && !p_ptr->resist_lite)
+ {
+ /* Become blind */
+ (void)set_blind(p_ptr->blind + 10 + randint(10));
+ }
+ }
+
+
+ /* Mega-Hack -- Forget the view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Induce an "earthquake" of the given radius at the given location.
+ *
+ * This will turn some walls into floors and some floors into walls.
+ *
+ * The player will take damage and "jump" into a safe grid if possible,
+ * otherwise, he will "tunnel" through the rubble instantaneously.
+ *
+ * Monsters will take damage, and "jump" into a safe grid if possible,
+ * otherwise they will be "buried" in the rubble, disappearing from
+ * the level in the same way that they do when genocided.
+ *
+ * Note that thus the player and monsters (except eaters of walls and
+ * passers through walls) will never occupy the same grid as a wall.
+ * Note that as of now (2.7.8) no monster may occupy a "wall" grid, even
+ * for a single turn, unless that monster can pass_walls or kill_walls.
+ * This has allowed massive simplification of the "monster" code.
+ */
+void earthquake(int cy, int cx, int r)
+{
+ int i, t, y, x, yy, xx, dy, dx, oy, ox;
+ int damage = 0;
+ int sn = 0, sy = 0, sx = 0;
+ bool hurt = FALSE;
+ cave_type *c_ptr;
+ bool map[32][32];
+
+ if (p_ptr->inside_quest)
+ {
+ return;
+ }
+
+ /* Paranoia -- Enforce maximum range */
+ if (r > 12) r = 12;
+
+ /* Clear the "maximal blast" area */
+ for (y = 0; y < 32; y++)
+ {
+ for (x = 0; x < 32; x++)
+ {
+ map[y][x] = FALSE;
+ }
+ }
+
+ /* Check around the epicenter */
+ for (dy = -r; dy <= r; dy++)
+ {
+ for (dx = -r; dx <= r; dx++)
+ {
+ /* Extract the location */
+ yy = cy + dy;
+ xx = cx + dx;
+
+ /* Skip illegal grids */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Skip distant grids */
+ if (distance(cy, cx, yy, xx) > r) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Skip the epicenter */
+ if (!dx && !dy) continue;
+
+ /* Skip most grids */
+ if (rand_int(100) < 85) continue;
+
+ /* Damage this grid */
+ map[16 + yy - cy][16 + xx - cx] = TRUE;
+
+ /* Hack -- Take note of player damage */
+ if ((yy == p_ptr->py) && (xx == p_ptr->px)) hurt = TRUE;
+ }
+ }
+
+ /* First, affect the player (if necessary) */
+ if (hurt && !p_ptr->wraith_form)
+ {
+ /* Check around the player */
+ for (i = 0; i < 8; i++)
+ {
+ /* Access the location */
+ y = p_ptr->py + ddy[i];
+ x = p_ptr->px + ddx[i];
+
+ /* Skip non-empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Important -- Skip "quake" grids */
+ if (map[16 + y - cy][16 + x - cx]) continue;
+
+ /* Count "safe" grids */
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+
+ /* Save the safe location */
+ sy = y;
+ sx = x;
+ }
+
+ /* Random message */
+ switch (randint(3))
+ {
+ case 1:
+ {
+ msg_print("The cave ceiling collapses!");
+ break;
+ }
+ case 2:
+ {
+ msg_print("The cave floor twists in an unnatural way!");
+ break;
+ }
+ default:
+ {
+ msg_print("The cave quakes! You are pummeled with debris!");
+ break;
+ }
+ }
+
+ /* Hurt the player a lot */
+ if (!sn)
+ {
+ /* Message and damage */
+ msg_print("You are severely crushed!");
+ damage = 300;
+ }
+
+ /* Destroy the grid, and push the player to safety */
+ else
+ {
+ /* Calculate results */
+ switch (randint(3))
+ {
+ case 1:
+ {
+ msg_print("You nimbly dodge the blast!");
+ damage = 0;
+ break;
+ }
+ case 2:
+ {
+ msg_print("You are bashed by rubble!");
+ damage = damroll(10, 4);
+ (void)set_stun(p_ptr->stun + randint(50));
+ break;
+ }
+ case 3:
+ {
+ msg_print("You are crushed between the floor and ceiling!");
+ damage = damroll(10, 4);
+ (void)set_stun(p_ptr->stun + randint(50));
+ break;
+ }
+ }
+
+ /* Save the old location */
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ /* Move the player to the safe location */
+ p_ptr->py = sy;
+ p_ptr->px = sx;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel */
+ verify_panel();
+ }
+
+ /* Important -- no wall on player */
+ map[16 + p_ptr->py - cy][16 + p_ptr->px - cx] = FALSE;
+
+ /* Semi-wraiths have to be hurt *some*, says DG */
+ if (PRACE_FLAG(PR1_SEMI_WRAITH))
+ damage /= 4;
+
+ /* Take some damage */
+ if (damage) take_hit(damage, "an earthquake");
+ }
+
+
+ /* Examine the quaked region */
+ for (dy = -r; dy <= r; dy++)
+ {
+ for (dx = -r; dx <= r; dx++)
+ {
+ /* Extract the location */
+ yy = cy + dy;
+ xx = cx + dx;
+
+ /* Skip unaffected grids */
+ if (!map[16 + yy - cy][16 + xx - cx]) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Process monsters */
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Most monsters cannot co-exist with rock */
+ if (!(r_ptr->flags2 & (RF2_KILL_WALL)) &&
+ !(r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ char m_name[80];
+
+ /* Assume not safe */
+ sn = 0;
+
+ /* Monster can move to escape the wall */
+ if (!(r_ptr->flags1 & (RF1_NEVER_MOVE)))
+ {
+ /* Look for safety */
+ for (i = 0; i < 8; i++)
+ {
+ /* Access the grid */
+ y = yy + ddy[i];
+ x = xx + ddx[i];
+
+ /* Skip non-empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no safety on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) &&
+ (cave[y][x].feat >= FEAT_PATTERN_START))
+ continue;
+
+ /* Important -- Skip "quake" grids */
+ if (map[16 + y - cy][16 + x - cx]) continue;
+
+ /* Count "safe" grids */
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+
+ /* Save the safe grid */
+ sy = y;
+ sx = x;
+ }
+ }
+
+ /* Describe the monster */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Scream in pain */
+ msg_format("%^s wails out in pain!", m_name);
+
+ /* Take damage from the quake */
+ damage = (sn ? damroll(4, 8) : 200);
+
+ /* Monster is certainly awake */
+ m_ptr->csleep = 0;
+
+ /* Apply damage directly */
+ m_ptr->hp -= damage;
+
+ /* Delete (not kill) "dead" monsters */
+ if (m_ptr->hp < 0)
+ {
+ /* Message */
+ msg_format("%^s is embedded in the rock!", m_name);
+
+ /* Delete the monster */
+ delete_monster(yy, xx);
+
+ /* No longer safe */
+ sn = 0;
+ }
+
+ /* Hack -- Escape from the rock */
+ if (sn)
+ {
+ int m_idx = cave[yy][xx].m_idx;
+
+ /* Update the new location */
+ cave[sy][sx].m_idx = m_idx;
+
+ /* Update the old location */
+ cave[yy][xx].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = sy;
+ m_ptr->fx = sx;
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(yy, xx);
+
+ /* Redraw the new grid */
+ lite_spot(sy, sx);
+ }
+ }
+ }
+ }
+ }
+
+
+ /* Examine the quaked region */
+ for (dy = -r; dy <= r; dy++)
+ {
+ for (dx = -r; dx <= r; dx++)
+ {
+ /* Extract the location */
+ yy = cy + dy;
+ xx = cx + dx;
+
+ /* Skip unaffected grids */
+ if (!map[16 + yy - cy][16 + xx - cx]) continue;
+
+ /* Access the cave grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Paranoia -- never affect player */
+ if ((yy == p_ptr->py) && (xx == p_ptr->px)) continue;
+
+ /* Destroy location (if valid) */
+ if (cave_valid_bold(yy, xx))
+ {
+ bool floor = cave_floor_bold(yy, xx);
+
+ /* Delete objects */
+ delete_object(yy, xx);
+
+ /* Wall (or floor) type */
+ t = (floor ? rand_int(100) : 200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(yy, xx, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 70)
+ {
+ /* Create quartz vein */
+ cave_set_feat(yy, xx, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 100)
+ {
+ /* Create magma vein */
+ cave_set_feat(yy, xx, FEAT_MAGMA);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(yy, xx, FEAT_FLOOR);
+ }
+ }
+ }
+ }
+
+
+ /* Mega-Hack -- Forget the view */
+ p_ptr->update |= (PU_UN_VIEW);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Update the health bar */
+ p_ptr->redraw |= (PR_HEALTH);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * This routine clears the entire "temp" set.
+ *
+ * This routine will Perma-Lite all "temp" grids.
+ *
+ * This routine is used (only) by "lite_room()"
+ *
+ * Dark grids are illuminated.
+ *
+ * Also, process all affected monsters.
+ *
+ * SMART monsters always wake up when illuminated
+ * NORMAL monsters wake up 1/4 the time when illuminated
+ * STUPID monsters wake up 1/10 the time when illuminated
+ */
+static void cave_temp_room_lite(void)
+{
+ int i;
+
+ /* Apply flag changes */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ cave_type *c_ptr = &cave[y][x];
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Update only non-CAVE_GLOW grids */
+ /* if (c_ptr->info & (CAVE_GLOW)) continue; */
+
+ /* Perma-Lite */
+ c_ptr->info |= (CAVE_GLOW);
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Process the grids */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Redraw the grid */
+ lite_spot(y, x);
+
+ /* Process affected monsters */
+ if (c_ptr->m_idx)
+ {
+ int chance = 25;
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+
+ /* Stupid monsters rarely wake up */
+ if (r_ptr->flags2 & (RF2_STUPID)) chance = 10;
+
+ /* Smart monsters always wake up */
+ if (r_ptr->flags2 & (RF2_SMART)) chance = 100;
+
+ /* Sometimes monsters wake up */
+ if (m_ptr->csleep && (rand_int(100) < chance))
+ {
+ /* Wake up! */
+ m_ptr->csleep = 0;
+
+ /* Notice the "waking up" */
+ if (m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Acquire the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Dump a message */
+ msg_format("%^s wakes up.", m_name);
+ }
+ }
+ }
+ }
+
+ /* None left */
+ temp_n = 0;
+}
+
+
+
+/*
+ * This routine clears the entire "temp" set.
+ *
+ * This routine will "darken" all "temp" grids.
+ *
+ * In addition, some of these grids will be "unmarked".
+ *
+ * This routine is used (only) by "unlite_room()"
+ *
+ * Also, process all affected monsters
+ */
+static void cave_temp_room_unlite(void)
+{
+ int i;
+
+ /* Apply flag changes */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ cave_type *c_ptr = &cave[y][x];
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Darken the grid */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ /* note_spot(y, x); */
+ }
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Process the grids */
+ for (i = 0; i < temp_n; i++)
+ {
+ int y = temp_y[i];
+ int x = temp_x[i];
+
+ /* Redraw the grid */
+ lite_spot(y, x);
+ }
+
+ /* None left */
+ temp_n = 0;
+}
+
+
+
+
+/*
+ * Aux function -- see below
+ */
+static void cave_temp_room_aux(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Avoid infinite recursion */
+ if (c_ptr->info & (CAVE_TEMP)) return;
+
+ /* Do not "leave" the current room */
+ if (!(c_ptr->info & (CAVE_ROOM))) return;
+
+ /* Paranoia -- verify space */
+ if (temp_n == TEMP_MAX) return;
+
+ /* Mark the grid as "seen" */
+ c_ptr->info |= (CAVE_TEMP);
+
+ /* Add it to the "seen" set */
+ temp_y[temp_n] = y;
+ temp_x[temp_n] = x;
+ temp_n++;
+}
+
+
+
+
+/*
+ * Illuminate any room containing the given location.
+ */
+void lite_room(int y1, int x1)
+{
+ int i, x, y;
+
+ /* Add the initial grid */
+ cave_temp_room_aux(y1, x1);
+
+ /* While grids are in the queue, add their neighbors */
+ for (i = 0; i < temp_n; i++)
+ {
+ x = temp_x[i], y = temp_y[i];
+
+ /* Walls get lit, but stop light */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Spread adjacent */
+ cave_temp_room_aux(y + 1, x);
+ cave_temp_room_aux(y - 1, x);
+ cave_temp_room_aux(y, x + 1);
+ cave_temp_room_aux(y, x - 1);
+
+ /* Spread diagonal */
+ cave_temp_room_aux(y + 1, x + 1);
+ cave_temp_room_aux(y - 1, x - 1);
+ cave_temp_room_aux(y - 1, x + 1);
+ cave_temp_room_aux(y + 1, x - 1);
+ }
+
+ /* Now, lite them all up at once */
+ cave_temp_room_lite();
+}
+
+
+/*
+ * Darken all rooms containing the given location
+ */
+void unlite_room(int y1, int x1)
+{
+ int i, x, y;
+
+ /* Add the initial grid */
+ cave_temp_room_aux(y1, x1);
+
+ /* Spread, breadth first */
+ for (i = 0; i < temp_n; i++)
+ {
+ x = temp_x[i], y = temp_y[i];
+
+ /* Walls get dark, but stop darkness */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Spread adjacent */
+ cave_temp_room_aux(y + 1, x);
+ cave_temp_room_aux(y - 1, x);
+ cave_temp_room_aux(y, x + 1);
+ cave_temp_room_aux(y, x - 1);
+
+ /* Spread diagonal */
+ cave_temp_room_aux(y + 1, x + 1);
+ cave_temp_room_aux(y - 1, x - 1);
+ cave_temp_room_aux(y - 1, x + 1);
+ cave_temp_room_aux(y + 1, x - 1);
+ }
+
+ /* Now, darken them all at once */
+ cave_temp_room_unlite();
+}
+
+
+
+/*
+ * Hack -- call light around the player
+ * Affect all monsters in the projection radius
+ */
+bool lite_area(int dam, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_KILL;
+
+ /* Hack -- Message */
+ if (!p_ptr->blind)
+ {
+ msg_print("You are surrounded by a white light.");
+ }
+
+ /* Hook into the "project()" function */
+ (void)project(0, rad, p_ptr->py, p_ptr->px, dam, GF_LITE_WEAK, flg);
+
+ /* Lite up the room */
+ lite_room(p_ptr->py, p_ptr->px);
+
+ /* Assume seen */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- call darkness around the player
+ * Affect all monsters in the projection radius
+ */
+bool unlite_area(int dam, int rad)
+{
+ int flg = PROJECT_GRID | PROJECT_KILL;
+
+ /* Hack -- Message */
+ if (!p_ptr->blind)
+ {
+ msg_print("Darkness surrounds you.");
+ }
+
+ /* Hook into the "project()" function */
+ (void)project(0, rad, p_ptr->py, p_ptr->px, dam, GF_DARK_WEAK, flg);
+
+ /* Lite up the room */
+ unlite_room(p_ptr->py, p_ptr->px);
+
+ /* Assume seen */
+ return (TRUE);
+}
+
+
+/*
+ * Cast a ball spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool fire_ball(int typ, int dir, int dam, int rad)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+/*
+ * Cast a cloud spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool fire_cloud(int typ, int dir, int dam, int rad, int time)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_STAY;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+ project_time = time;
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+/*
+ * Cast a wave spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool fire_wave(int typ, int dir, int dam, int rad, int time, s32b eff)
+{
+ project_time_effect = eff;
+ return (fire_cloud(typ, dir, dam, rad, time));
+}
+
+/*
+ * Cast a persistant beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+bool fire_wall(int typ, int dir, int dam, int time)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL | PROJECT_STAY | PROJECT_GRID;
+ project_time = time;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+/*
+ * Cast a druidistic ball spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool fire_druid_ball(int typ, int dir, int dam, int rad)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_MANA_PATH;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+
+/*
+ * Cast a ball-beamed spell
+ * Stop if we hit a monster, act as a "ball"
+ * Allow "target" mode to pass over monsters
+ * Affect grids, objects, and monsters
+ */
+bool fire_ball_beam(int typ, int dir, int dam, int rad)
+{
+ int tx, ty;
+
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_BEAM;
+
+ /* Use the given direction */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ flg &= ~(PROJECT_STOP);
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target". Hurt items on floor. */
+ return (project(0, (rad > 16) ? 16 : rad, ty, tx, dam, typ, flg));
+}
+
+
+void teleport_swap(int dir)
+{
+ int tx, ty;
+ cave_type * c_ptr;
+ monster_type * m_ptr;
+ monster_race * r_ptr;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ else
+ {
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+ }
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->m_idx)
+ {
+ msg_print("You can't trade places with that!");
+ }
+ else
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (r_ptr->flags3 & RF3_RES_TELE)
+ {
+ msg_print("Your teleportation is blocked!");
+ }
+ else
+ {
+ sound(SOUND_TELEPORT);
+
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+
+ /* Update the old location */
+ c_ptr->m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->px = tx;
+ p_ptr->py = ty;
+
+ tx = m_ptr->fx;
+ ty = m_ptr->fy;
+
+ /* Update the monster (new location) */
+ update_mon(cave[ty][tx].m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Execute the inscription */
+ c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+ if (c_ptr->inscription)
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ execute_inscription(c_ptr->inscription, m_ptr->fy, m_ptr->fx);
+ }
+ }
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+ if (c_ptr->inscription)
+ {
+ msg_format("There is an inscription here: %s", inscription_info[c_ptr->inscription].text);
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK)
+ {
+ execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px);
+ }
+ }
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+ }
+}
+
+void swap_position(int lty, int ltx)
+{
+ int tx = ltx, ty = lty;
+ cave_type * c_ptr;
+ monster_type * m_ptr;
+ monster_race * r_ptr;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->m_idx)
+ {
+ sound(SOUND_TELEPORT);
+
+ /* Keep trace of the old location */
+ tx = p_ptr->px;
+ ty = p_ptr->py;
+
+ /* Move the player */
+ p_ptr->px = ltx;
+ p_ptr->py = lty;
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+ else
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ sound(SOUND_TELEPORT);
+
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+
+ /* Update the old location */
+ c_ptr->m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->px = tx;
+ p_ptr->py = ty;
+
+ tx = m_ptr->fx;
+ ty = m_ptr->fy;
+
+ /* Update the monster (new location) */
+ update_mon(cave[ty][tx].m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+}
+
+
+/*
+ * Hack -- apply a "projection()" in a direction (or at the target)
+ */
+bool project_hook(int typ, int dir, int dam, int flg)
+{
+ int tx, ty;
+
+ /* Pass through the target if needed */
+ flg |= (PROJECT_THRU);
+
+ /* Use the given direction */
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+ /* Analyze the "dir" and the "target", do NOT explode */
+ return (project(0, 0, ty, tx, dam, typ, flg));
+}
+
+
+/*
+ * Cast a bolt spell
+ * Stop if we hit a monster, as a "bolt"
+ * Affect monsters (not grids or objects)
+ */
+bool fire_bolt(int typ, int dir, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+/*
+ * Cast a druidistic bolt spell
+ * Stop if we hit a monster, as a "bolt"
+ * Affect monsters (not grids or objects)
+ */
+bool fire_druid_bolt(int typ, int dir, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL | PROJECT_MANA_PATH;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+
+/*
+ * Cast a druidistic beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+bool fire_druid_beam(int typ, int dir, int dam)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL | PROJECT_MANA_PATH;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+/*
+ * Cast a beam spell
+ * Pass through monsters, as a "beam"
+ * Affect monsters (not grids or objects)
+ */
+bool fire_beam(int typ, int dir, int dam)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL;
+ return (project_hook(typ, dir, dam, flg));
+}
+
+
+/*
+ * Cast a bolt spell, or rarely, a beam spell
+ */
+bool fire_bolt_or_beam(int prob, int typ, int dir, int dam)
+{
+ if (rand_int(100) < prob)
+ {
+ return (fire_beam(typ, dir, dam));
+ }
+ else
+ {
+ return (fire_bolt(typ, dir, dam));
+ }
+}
+
+bool fire_godly_wrath(int y, int x, int typ, int rad, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ return (project(0, rad, y, x, dam, typ, flg));
+}
+
+bool fire_explosion(int y, int x, int typ, int rad, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+
+ return (project(0, rad, y, x, dam, typ, flg));
+}
+
+/*
+ * Some of the old functions
+ */
+bool lite_line(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL;
+ return (project_hook(GF_LITE_WEAK, dir, damroll(6, 8), flg));
+}
+
+
+bool drain_life(int dir, int dam)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_DRAIN, dir, dam, flg));
+}
+
+
+bool wall_to_mud(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ return (project_hook(GF_KILL_WALL, dir, 20 + randint(30), flg));
+}
+
+
+bool wizard_lock(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ return (project_hook(GF_JAM_DOOR, dir, 20 + randint(30), flg));
+}
+
+
+bool destroy_door(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM;
+ return (project_hook(GF_KILL_DOOR, dir, 0, flg));
+}
+
+
+bool disarm_trap(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM;
+ return (project_hook(GF_KILL_TRAP, dir, 0, flg));
+}
+
+
+bool heal_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_HEAL, dir, damroll(4, 6), flg));
+}
+
+
+bool speed_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_SPEED, dir, p_ptr->lev, flg));
+}
+
+
+bool slow_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_SLOW, dir, p_ptr->lev, flg));
+}
+
+
+bool sleep_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_SLEEP, dir, p_ptr->lev, flg));
+}
+
+
+bool stasis_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_STASIS, dir, p_ptr->lev, flg));
+}
+
+
+bool confuse_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_CONF, dir, plev, flg));
+}
+
+
+bool stun_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_STUN, dir, plev, flg));
+}
+
+
+bool poly_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_POLY, dir, p_ptr->lev, flg));
+}
+
+
+bool clone_monster(int dir)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_CLONE, dir, 0, flg));
+}
+
+
+bool fear_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_TURN_ALL, dir, plev, flg));
+}
+
+
+bool death_ray(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_DEATH_RAY, dir, plev, flg));
+}
+
+
+bool teleport_monster(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_KILL;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return FALSE;
+ }
+
+ return (project_hook(GF_AWAY_ALL, dir, MAX_SIGHT * 5, flg));
+}
+
+
+/*
+ * Hooks -- affect adjacent grids (radius 1 ball attack)
+ */
+bool door_creation(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_MAKE_DOOR, flg));
+}
+
+
+bool trap_creation(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_MAKE_TRAP, flg));
+}
+
+
+bool glyph_creation(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_MAKE_GLYPH, flg));
+}
+
+
+bool wall_stone(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+ int flg = PROJECT_GRID | PROJECT_ITEM;
+ int featflags = f_info[c_ptr->feat].flags1;
+
+ bool dummy = (project(0, 1, y, x, 0, GF_STONE_WALL, flg));
+
+ if (!(featflags & FF1_PERMANENT) && !(featflags & FF1_WALL))
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ return dummy;
+}
+
+
+bool destroy_doors_touch(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_KILL_DOOR, flg));
+}
+
+bool destroy_traps_touch(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_KILL_TRAP, flg));
+}
+
+bool sleep_monsters_touch(void)
+{
+ int flg = PROJECT_KILL | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, p_ptr->lev, GF_OLD_SLEEP, flg));
+}
+
+
+void call_chaos(void)
+{
+ int Chaos_type, dummy, dir;
+ int plev = p_ptr->lev;
+ bool line_chaos = FALSE;
+
+ int hurt_types[30] =
+ {
+ GF_ELEC, GF_POIS, GF_ACID, GF_COLD,
+ GF_FIRE, GF_MISSILE, GF_ARROW, GF_PLASMA,
+ GF_HOLY_FIRE, GF_WATER, GF_LITE, GF_DARK,
+ GF_FORCE, GF_INERTIA, GF_MANA, GF_METEOR,
+ GF_ICE, GF_CHAOS, GF_NETHER, GF_DISENCHANT,
+ GF_SHARDS, GF_SOUND, GF_NEXUS, GF_CONFUSION,
+ GF_TIME, GF_GRAVITY, GF_ROCKET, GF_NUKE,
+ GF_HELL_FIRE, GF_DISINTEGRATE
+ };
+
+ Chaos_type = hurt_types[randint(30) - 1];
+ if (randint(4) == 1) line_chaos = TRUE;
+
+#if 0
+ /* Probably a meaningless line, a remnant from earlier code */
+ while (Chaos_type > GF_GRAVITY && Chaos_type < GF_ROCKET);
+#endif
+
+ if (randint(6) == 1)
+ {
+ for (dummy = 1; dummy < 10; dummy++)
+ {
+ if (dummy - 5)
+ {
+ if (line_chaos)
+ fire_beam(Chaos_type, dummy, 75);
+ else
+ fire_ball(Chaos_type, dummy, 75, 2);
+ }
+ }
+ }
+ else if (randint(3) == 1)
+ {
+ fire_ball(Chaos_type, 0, 300, 8);
+ }
+ else
+ {
+ if (!get_aim_dir(&dir)) return;
+ if (line_chaos)
+ fire_beam(Chaos_type, dir, 150);
+ else
+ fire_ball(Chaos_type, dir, 150, 3 + (plev / 35));
+ }
+}
+
+
+/*
+ * Activate the evil Topi Ylinen curse
+ * rr9: Stop the nasty things when a Cyberdemon is summoned
+ * or the player gets paralyzed.
+ */
+void activate_ty_curse(void)
+{
+ int i = 0;
+ bool stop_ty = FALSE;
+
+ do
+ {
+ switch (randint(27))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 16:
+ case 17:
+ aggravate_monsters(1);
+ if (randint(6) != 1) break;
+ case 4:
+ case 5:
+ case 6:
+ activate_hi_summon();
+ if (randint(6) != 1) break;
+case 7: case 8: case 9: case 18:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ if (randint(6) != 1) break;
+case 10: case 11: case 12:
+ msg_print("You feel your life draining away...");
+ lose_exp(p_ptr->exp / 16);
+ if (randint(6) != 1) break;
+case 13: case 14: case 15: case 19: case 20:
+ if (p_ptr->free_act && (randint(100) < p_ptr->skill_sav))
+ {
+ /* Do nothing */ ;
+ }
+ else
+ {
+ msg_print("You feel like a statue!");
+ if (p_ptr->free_act)
+ set_paralyzed (p_ptr->paralyzed + randint(3));
+ else
+ set_paralyzed (p_ptr->paralyzed + randint(13));
+ stop_ty = TRUE;
+ }
+ if (randint(6) != 1) break;
+case 21: case 22: case 23:
+ (void)do_dec_stat((randint(6)) - 1, STAT_DEC_NORMAL);
+ if (randint(6) != 1) break;
+ case 24:
+ msg_print("Huh? Who am I? What am I doing here?");
+ lose_all_info();
+ break;
+ case 25:
+ /*
+ * Only summon Cyberdemons deep in the dungeon.
+ */
+ if ((dun_level > 65) && !stop_ty)
+ {
+ summon_cyber();
+ stop_ty = TRUE;
+ break;
+ }
+ default:
+ while (i < 6)
+ {
+ do
+ {
+ (void)do_dec_stat(i, STAT_DEC_NORMAL);
+ }
+ while (randint(2) == 1);
+
+ i++;
+ }
+ }
+ }
+ while ((randint(3) == 1) && !stop_ty);
+}
+
+/*
+ * Activate the ultra evil Dark God curse
+ */
+void activate_dg_curse(void)
+{
+ int i = 0;
+ bool stop_dg = FALSE;
+
+ do
+ {
+ switch (randint(30))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 16:
+ case 17:
+ aggravate_monsters(1);
+ if (randint(8) != 1) break;
+ case 4:
+ case 5:
+ case 6:
+ msg_print("Oh! You feel that the curse is replicating itself!");
+ curse_equipment_dg(100, 50 * randint(2));
+ if (randint(8) != 1) break;
+ case 7:
+ case 8:
+ case 9:
+ case 18:
+ curse_equipment(100, 50 * randint(2));
+ if (randint(8) != 1) break;
+ case 10:
+ case 11:
+ case 12:
+ msg_print("You feel your life draining away...");
+ lose_exp(p_ptr->exp / 12);
+ if (rand_int(2))
+ {
+ msg_print("You feel the coldness of the Black Breath attacking you!");
+ p_ptr->black_breath = TRUE;
+ }
+ if (randint(8) != 1) break;
+ case 13:
+ case 14:
+ case 15:
+ if (p_ptr->free_act && (randint(100) < p_ptr->skill_sav))
+ {
+ /* Do nothing */ ;
+ }
+ else
+ {
+ msg_print("You feel like a statue!");
+ if (p_ptr->free_act)
+ set_paralyzed (p_ptr->paralyzed + randint(3));
+ else
+ set_paralyzed (p_ptr->paralyzed + randint(13));
+ stop_dg = TRUE;
+ }
+ if (randint(7) != 1) break;
+ case 19:
+ case 20:
+ {
+ msg_print("Woah! You see 10 little Morgoths dancing before you!");
+ set_confused(p_ptr->confused + randint(13 * 2));
+ if (rand_int(2)) stop_dg = TRUE;
+ }
+ if (randint(7) != 1) break;
+ case 21:
+ case 22:
+ case 23:
+ (void)do_dec_stat((randint(6)) - 1, STAT_DEC_PERMANENT);
+ if (randint(7) != 1) break;
+ case 24:
+ msg_print("Huh? Who am I? What am I doing here?");
+ lose_all_info();
+ break;
+case 27: case 28: case 29:
+ if (p_ptr->inventory[INVEN_WIELD].k_idx)
+ {
+ msg_print("Your weapon now seems useless...");
+ p_ptr->inventory[INVEN_WIELD].art_flags4 = TR4_NEVER_BLOW;
+ }
+ break;
+ case 25:
+ /*
+ * Only summon Thunderlords not too shallow in the dungeon.
+ */
+ if ((dun_level > 25) && !stop_dg)
+ {
+ msg_print("Oh! You attracted some evil Thunderlords!");
+ summon_dragon_riders();
+
+ /* This is evil -- DG */
+ if (rand_int(2)) stop_dg = TRUE;
+ break;
+ }
+ default:
+ while (i < 6)
+ {
+ do
+ {
+ (void)do_dec_stat(i, STAT_DEC_NORMAL);
+ }
+ while (randint(2) == 1);
+
+ i++;
+ }
+ }
+ }
+ while ((randint(4) == 1) && !stop_dg);
+}
+
+
+void activate_hi_summon(void)
+{
+ int i;
+
+ for (i = 0; i < (randint(9) + (dun_level / 40)); i++)
+ {
+ switch (randint(26) + (dun_level / 20) )
+ {
+ case 1:
+ case 2:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT);
+ break;
+ case 3:
+ case 4:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER);
+ break;
+ case 5:
+ case 6:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND);
+ break;
+ case 7:
+ case 8:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA);
+ break;
+ case 9:
+ case 10:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL);
+ break;
+ case 11:
+ case 12:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD);
+ break;
+ case 13:
+ case 14:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON);
+ break;
+ case 15:
+ case 16:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON);
+ break;
+ case 17:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH);
+ break;
+ case 18:
+ case 19:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE);
+ break;
+ case 20:
+ case 21:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD);
+ break;
+ case 22:
+ case 23:
+ (void) summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON);
+ break;
+ case 24:
+ case 25:
+ (void) summon_specific(p_ptr->py, p_ptr->px, 100, SUMMON_HI_DEMON);
+ break;
+ default:
+ (void) summon_specific(p_ptr->py, p_ptr->px, (((dun_level * 3) / 2) + 5), 0);
+ }
+ }
+}
+
+
+void summon_cyber(void)
+{
+ int i;
+ int max_cyber = (dun_level / 50) + randint(6);
+
+ for (i = 0; i < max_cyber; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, 100, SUMMON_HI_DEMON);
+ }
+}
+
+void summon_dragon_riders()
+{
+ int i;
+ int max_dr = (dun_level / 50) + randint(6);
+
+ for (i = 0; i < max_dr; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, 100, SUMMON_THUNDERLORD);
+ }
+}
+
+
+void wall_breaker(void)
+{
+ int dummy = 5;
+
+ if (randint(80 + p_ptr->lev) < 70)
+ {
+ do
+ {
+ dummy = randint(9);
+ }
+ while ((dummy == 5) || (dummy == 0));
+
+ wall_to_mud (dummy);
+ }
+ else if (randint(100) > 30)
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 1);
+ }
+ else
+ {
+ for (dummy = 1; dummy < 10; dummy++)
+ {
+ if (dummy - 5) wall_to_mud(dummy);
+ }
+ }
+}
+
+
+void bless_weapon(void)
+{
+ int item;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+ char o_name[80];
+ cptr q, s;
+
+ /* Assume enchant weapon */
+ item_tester_hook = item_tester_hook_weapon;
+
+ /* Get an item */
+ q = "Bless which weapon? ";
+ s = "You have weapon to bless.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (o_ptr->ident & (IDENT_CURSED))
+ {
+
+ if (((f3 & (TR3_HEAVY_CURSE)) && (randint (100) < 33)) ||
+ (f3 & (TR3_PERMA_CURSE)))
+ {
+
+ msg_format("The black aura on %s %s disrupts the blessing!",
+ ((item >= 0) ? "your" : "the"), o_name);
+ return;
+ }
+
+ msg_format("A malignant aura leaves %s %s.",
+ ((item >= 0) ? "your" : "the"), o_name);
+
+ /* Uncurse it */
+ o_ptr->ident &= ~(IDENT_CURSED);
+
+ /* Hack -- Assume felt */
+ o_ptr->ident |= (IDENT_SENSE);
+
+ /* Take note */
+ o_ptr->sense = SENSE_UNCURSED;
+
+ /* Recalculate the bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /*
+ * Next, we try to bless it. Artifacts have a 1/3 chance of
+ * being blessed, otherwise, the operation simply disenchants
+ * them, godly power negating the magic. Ok, the explanation
+ * is silly, but otherwise priests would always bless every
+ * artifact weapon they find. Ego weapons and normal weapons
+ * can be blessed automatically.
+ */
+ if (f3 & TR3_BLESSED)
+ {
+ msg_format("%s %s %s blessed already.",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "were" : "was"));
+ return;
+ }
+
+ if (!(o_ptr->art_name || o_ptr->name1) || (randint(3) == 1))
+ {
+ /* Describe */
+ msg_format("%s %s shine%s!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+ o_ptr->art_flags3 |= TR3_BLESSED;
+ }
+ else
+ {
+ bool dis_happened = FALSE;
+
+ msg_print("The artifact resists your blessing!");
+
+ /* Disenchant tohit */
+ if (o_ptr->to_h > 0)
+ {
+ o_ptr->to_h--;
+ dis_happened = TRUE;
+ }
+
+ if ((o_ptr->to_h > 5) && (rand_int(100) < 33)) o_ptr->to_h--;
+
+ /* Disenchant todam */
+ if (o_ptr->to_d > 0)
+ {
+ o_ptr->to_d--;
+ dis_happened = TRUE;
+ }
+
+ if ((o_ptr->to_d > 5) && (rand_int(100) < 33)) o_ptr->to_d--;
+
+ /* Disenchant toac */
+ if (o_ptr->to_a > 0)
+ {
+ o_ptr->to_a--;
+ dis_happened = TRUE;
+ }
+
+ if ((o_ptr->to_a > 5) && (rand_int(100) < 33)) o_ptr->to_a--;
+
+ if (dis_happened)
+ {
+ msg_print("There is a static feeling in the air...");
+ msg_format("%s %s %s disenchanted!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "were" : "was"));
+ }
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+}
+
+
+/*
+ * Detect all "nonliving", "undead" or "demonic" monsters on current panel
+ */
+bool detect_monsters_nonliving(int rad)
+{
+ int i, y, x;
+ bool flag = FALSE;
+
+ /* Scan monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > rad) continue;
+
+ /* Detect evil monsters */
+ if ((r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_DEMON)))
+ {
+ /* Update monster recall window */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MONSTER);
+ }
+
+ /* Repair visibility later */
+ repair_monsters = TRUE;
+
+ /* Hack -- Detect monster */
+ m_ptr->mflag |= (MFLAG_MARK | MFLAG_SHOW);
+
+ /* Hack -- See monster */
+ m_ptr->ml = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ flag = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (flag)
+ {
+ /* Describe result */
+ msg_print("You sense the presence of unnatural beings!");
+ }
+
+ /* Result */
+ return (flag);
+}
+
+
+/*
+ * Confuse monsters
+ */
+bool confuse_monsters(int dam)
+{
+ return (project_hack(GF_OLD_CONF, dam));
+}
+
+
+/*
+ * Charm monsters
+ */
+bool charm_monsters(int dam)
+{
+ return (project_hack(GF_CHARM, dam));
+}
+
+
+/*
+ * Charm animals
+ */
+bool charm_animals(int dam)
+{
+ return (project_hack(GF_CONTROL_ANIMAL, dam));
+}
+
+/*
+ * Charm demons
+ */
+bool charm_demons(int dam)
+{
+ return (project_hack(GF_CONTROL_DEMON, dam));
+}
+
+
+/*
+ * Stun monsters
+ */
+bool stun_monsters(int dam)
+{
+ return (project_hack(GF_STUN, dam));
+}
+
+
+/*
+ * Stasis monsters
+ */
+bool stasis_monsters(int dam)
+{
+ return (project_hack(GF_STASIS, dam));
+}
+
+
+/*
+ * Mindblast monsters
+ */
+bool mindblast_monsters(int dam)
+{
+ return (project_hack(GF_PSI, dam));
+}
+
+
+/*
+ * Banish all monsters
+ */
+bool banish_monsters(int dist)
+{
+ return (project_hack(GF_AWAY_ALL, dist));
+}
+
+
+/*
+ * Turn evil
+ */
+bool turn_evil(int dam)
+{
+ return (project_hack(GF_TURN_EVIL, dam));
+}
+
+
+/*
+ * Turn everyone
+ */
+bool turn_monsters(int dam)
+{
+ return (project_hack(GF_TURN_ALL, dam));
+}
+
+
+/*
+ * Death-ray all monsters (note: OBSCENELY powerful)
+ */
+bool deathray_monsters(void)
+{
+ return (project_hack(GF_DEATH_RAY, p_ptr->lev));
+}
+
+
+bool charm_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CHARM, dir, plev, flg));
+}
+
+bool star_charm_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_STAR_CHARM, dir, plev, flg));
+}
+
+
+bool control_one_undead(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CONTROL_UNDEAD, dir, plev, flg));
+}
+
+
+bool charm_animal(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CONTROL_ANIMAL, dir, plev, flg));
+}
+
+void change_wild_mode(void)
+{
+ if (p_ptr->immovable && !p_ptr->wild_mode)
+ {
+ msg_print("Hmm, blinking there will take time.");
+ }
+
+ if (p_ptr->word_recall && !p_ptr->wild_mode)
+ {
+ msg_print("You will soon be recalled.");
+ return;
+ }
+
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+void alter_reality(void)
+{
+ msg_print("The world changes!");
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+/* Heal insanity. */
+bool heal_insanity(int val)
+{
+ if (p_ptr->csane < p_ptr->msane)
+ {
+ p_ptr->csane += val;
+
+ if (p_ptr->csane >= p_ptr->msane)
+ {
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+ }
+
+ p_ptr->redraw |= PR_SANITY;
+ p_ptr->window |= (PW_PLAYER);
+
+ if (val < 5)
+ {
+ msg_print("You feel a little better.");
+ }
+ else if (val < 15)
+ {
+ msg_print("You feel better.");
+ }
+ else if (val < 35)
+ {
+ msg_print("You feel much better.");
+ }
+ else
+ {
+ msg_print("You feel very good.");
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Send the player shooting through walls in the given direction until
+ * they reach a non-wall space, or a monster, or a permanent wall.
+ */
+bool passwall(int dir, bool safe)
+{
+ int x = p_ptr->px, y = p_ptr->py, ox = p_ptr->px, oy = p_ptr->py, lx = p_ptr->px, ly = p_ptr->py;
+ cave_type *c_ptr;
+ bool ok = FALSE;
+
+ if (p_ptr->wild_mode) return FALSE;
+ if (p_ptr->inside_quest) return FALSE;
+ if (dungeon_flags2 & DF2_NO_TELEPORT) return FALSE;
+
+ /* Must go somewhere */
+ if (dir == 5) return FALSE;
+
+ while (TRUE)
+ {
+ x += ddx[dir];
+ y += ddy[dir];
+ c_ptr = &cave[y][x];
+
+ /* Perm walls stops the transfer */
+ if ((!in_bounds(y, x)) && (f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ {
+ /* get the last working position */
+ x -= ddx[dir];
+ y -= ddy[dir];
+ ok = FALSE;
+ break;
+ }
+
+ /* Never on a monster */
+ if (c_ptr->m_idx) continue;
+
+ /* Never stop in vaults */
+ if (c_ptr->info & CAVE_ICKY) continue;
+
+ /* From now on, the location COULD be used in special case */
+ lx = x;
+ ly = y;
+
+ /* Pass over walls */
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue;
+
+ /* So it must be ok */
+ ok = TRUE;
+ break;
+ }
+
+ if (!ok)
+ {
+ x = lx;
+ y = ly;
+
+ if (!safe)
+ {
+ msg_print("You emerge in the wall!");
+ take_hit(damroll(10, 8), "becoming one with a wall");
+ }
+ place_floor(y, x);
+ }
+
+ /* Move */
+ p_ptr->px = x;
+ p_ptr->py = y;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_DTRAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+
+ return (TRUE);
+}
+
+/*
+ * Print a batch of dungeons.
+ */
+static void print_dungeon_batch(int *p, int start, int max, bool mode)
+{
+ char buf[80];
+ int i, j;
+ byte attr;
+
+
+ if (mode) prt(format(" %-31s", "Name"), 1, 20);
+
+ for (i = 0, j = start; i < 20 && j < max; i++, j++)
+ {
+ dungeon_info_type *d_ptr = &d_info[p[j]];
+
+ strnfmt(buf, 80, " %c) %-30s", I2A(i), d_name + d_ptr->name);
+ if (mode)
+ {
+ if (d_ptr->min_plev > p_ptr->lev)
+ {
+ attr = TERM_L_DARK;
+ }
+ else
+ {
+ attr = TERM_WHITE;
+ }
+ c_prt(attr, buf, 2 + i, 20);
+ }
+ }
+ if (mode) prt("", 2 + i, 20);
+
+ prt(format("Select a dungeon (a-%c), * to list, @ to select by name, +/- to scroll:", I2A(i - 1)), 0, 0);
+}
+
+int reset_recall_aux()
+{
+ char which;
+ int *p;
+ int max = 0, i, start = 0;
+ int ret;
+ bool mode = FALSE;
+
+
+ C_MAKE(p, max_d_idx, int);
+
+ /* Count the max */
+ for (i = 1; i < max_d_idx; i++)
+ {
+ /* skip "blocked" dungeons */
+ if (d_info[i].flags1 & DF1_NO_RECALL) continue;
+
+ if (max_dlv[i])
+ {
+ p[max++] = i;
+ }
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_dungeon_batch(p, start, max, mode);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = -1;
+ break;
+ }
+
+ else if (which == '*' || which == '?' || which == ' ')
+ {
+ mode = (mode) ? FALSE : TRUE;
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ else if (which == '+')
+ {
+ start += 20;
+ if (start >= max) start -= 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ else if (which == '-')
+ {
+ start -= 20;
+ if (start < 0) start += 20;
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ else if (which == '@')
+ {
+ char buf[80], buf2[80];
+
+ strcpy(buf, (d_info[p_ptr->recall_dungeon].name + d_name));
+ if (!get_string("Which dungeon? ", buf, 79)) continue;
+
+ /* Find the index corresponding to the name */
+ for (i = 1; i < max_d_idx; i++)
+ {
+ sprintf(buf2, "%s", d_info[i].name + d_name);
+
+ /* Lowercase the name */
+ strlower(buf);
+ strlower(buf2);
+ if (strstr(buf2, buf))
+ {
+ /* valid dungeon found */
+ break;
+ }
+ }
+
+ if (i >= max_d_idx)
+ {
+ msg_print("Never heard of that place!");
+ msg_print(NULL);
+ continue;
+ }
+ else if (d_info[i].flags1 & DF1_NO_RECALL)
+ {
+ msg_print("This place blocks my magic!");
+ msg_print(NULL);
+ continue;
+ }
+ else if (d_info[i].min_plev > p_ptr->lev)
+ {
+ msg_print("You cannot go there yet!");
+ msg_print(NULL);
+ continue;
+ }
+ ret = i;
+ break;
+ }
+
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= max)
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+ ret = p[start + A2I(which)];
+ break;
+ }
+ }
+
+ Term_load();
+ character_icky = FALSE;
+
+ C_FREE(p, max_d_idx, int);
+
+ return ret;
+}
+
+bool reset_recall(bool no_trepas_max_depth)
+{
+ int dun, depth, max;
+
+ /* Choose dungeon */
+ dun = reset_recall_aux();
+
+ if (dun < 1) return FALSE;
+
+ /* Choose depth */
+ if (!no_trepas_max_depth)
+ max = d_info[dun].maxdepth;
+ else
+ max = max_dlv[dun];
+ depth = get_quantity(format("Which level in %s(%d-%d)? ",
+ d_info[dun].name + d_name,
+ d_info[dun].mindepth, max),
+ max);
+
+ if (depth < 1) return FALSE;
+
+ /* Enforce minimum level */
+ if (depth < d_info[dun].mindepth) depth = d_info[dun].mindepth;
+
+ /* Mega hack -- Forbid levels 99 and 100 */
+ if ((depth == 99) || (depth == 100)) depth = 98;
+
+ p_ptr->recall_dungeon = dun;
+ max_dlv[p_ptr->recall_dungeon] = depth;
+
+ return TRUE;
+}
+
+/* The only way to get rid of the dreaded DG_CURSE*/
+void remove_dg_curse()
+{
+ int k;
+
+ /* Parse all the items */
+ for (k = INVEN_WIELD; k < INVEN_TOTAL; k++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[k];
+
+ if (o_ptr->k_idx && (o_ptr->art_flags4 & TR4_DG_CURSE))
+ {
+ o_ptr->art_flags3 &= ~TR3_HEAVY_CURSE;
+ o_ptr->art_flags3 &= ~TR3_CURSED;
+ o_ptr->art_flags4 &= ~TR4_DG_CURSE;
+ msg_print("The Morgothian Curse withers away.");
+ }
+ }
+}
+
+/*
+ * Creates a between gate
+ */
+void create_between_gate(int dist, int y, int x)
+{
+ int ii, ij, plev = get_skill(SKILL_CONVEYANCE);
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ return;
+ }
+
+ if ((!x) || (!y))
+ {
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > dist) ||
+ (rand_int(plev * plev / 2) == 0))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10, &ij, &ii);
+ }
+ }
+ else
+ {
+ ij = y;
+ ii = x;
+ }
+ if (!(f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_PERMANENT))
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ }
+ if (!(f_info[cave[ij][ii].feat].flags1 & FF1_PERMANENT))
+ {
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+ }
+}
diff --git a/src/squeltch.c b/src/squeltch.c
new file mode 100644
index 00000000..89104f63
--- /dev/null
+++ b/src/squeltch.c
@@ -0,0 +1,551 @@
+/* File: squeltch.c */
+
+/* Purpose: Automatizer */
+
+/*
+ * Copyright (c) 2002 DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+#include "lua/lua.h"
+#include "tolua.h"
+
+extern lua_State *L;
+
+/*
+ * The functions here use direct lua stack manipulation for calls instead of
+ * exec_lua(format()) because string manipulations are too slow for such
+ * functions
+ */
+
+/* Check the floor for "crap" */
+void squeltch_grid(void)
+{
+ int oldtop;
+ s16b this_o_idx, next_o_idx = 0;
+
+ if (!automatizer_enabled) return;
+
+ oldtop = lua_gettop(L);
+
+ /* Scan the pile of objects */
+ for (this_o_idx = cave[p_ptr->py][p_ptr->px].o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_o_idx];
+
+ /* We've now seen one of these */
+ if (!k_info[o_ptr->k_idx].flavor)
+ {
+ object_aware(o_ptr);
+ }
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Push the function */
+ lua_settop(L, oldtop);
+ lua_getglobal(L, "apply_rules");
+
+ /* Push the args */
+ tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
+ tolua_pushnumber(L, -this_o_idx);
+
+ /* Call the function */
+ if (lua_call(L, 2, 0))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'apply_rules'.");
+ lua_settop(L, oldtop);
+ return;
+ }
+ }
+ lua_settop(L, oldtop);
+}
+
+
+/* Check the inventory for "crap" */
+void squeltch_inventory(void)
+{
+ int oldtop;
+ int i;
+ int num_iter = 0;
+ bool found = TRUE;
+
+ if (!automatizer_enabled) return;
+
+ oldtop = lua_gettop(L);
+ while (found && num_iter ++ < 100)
+ {
+ /* Sometimes an index in the inventory is skipped */
+ found = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Push the function */
+ lua_settop(L, oldtop);
+ lua_getglobal(L, "apply_rules");
+
+ /* Push the args */
+ tolua_pushusertype(L, o_ptr, tolua_tag(L, "object_type"));
+ tolua_pushnumber(L, i);
+
+ /* Call the function */
+ if (lua_call(L, 2, 1))
+ {
+ cmsg_format(TERM_VIOLET, "ERROR in lua_call while calling 'apply_rules'.");
+ lua_settop(L, oldtop);
+ return;
+ }
+
+ /* Did it return TRUE */
+ if (tolua_getnumber(L, -(lua_gettop(L) - oldtop), 0))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ if (num_iter >= 100)
+ {
+ cmsg_format(TERM_VIOLET, "'apply_rules' ran too often.");
+ }
+ lua_settop(L, oldtop);
+}
+
+/********************** The interface **********************/
+static cptr *get_rule_list(int *max)
+{
+ cptr *list;
+ int i;
+
+ *max = exec_lua("return __rules_max");
+ C_MAKE(list, *max, cptr);
+
+ for (i = 0; i < *max; i++)
+ {
+ list[i] = string_exec_lua(format("return __rules[%d].table.args.name", i));
+ }
+
+ return list;
+}
+
+static cptr types_list[] =
+{
+ "and",
+ "or",
+ "not",
+ "name",
+ "contain",
+ "inscribed",
+ "discount",
+ "symbol",
+ "state",
+ "status",
+ "tval",
+ "sval",
+ "race",
+ "subrace",
+ "class",
+ "level",
+ "skill",
+ "ability",
+ "comment",
+ NULL,
+};
+
+/* Create a new rule */
+static int automatizer_new_rule()
+{
+ int wid, hgt, max, begin = 0, sel = 0;
+ char c;
+
+ /* Get the number of types */
+ max = 0;
+ while (types_list[max] != NULL)
+ max++;
+
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ display_list(0, 0, hgt - 1, 15, "Rule types", types_list, max, begin, sel, TERM_L_GREEN);
+
+ exec_lua(format("auto_aux:display_desc('%s')", types_list[sel]));
+ c_prt(TERM_YELLOW, "Example:", 5, 17);
+ exec_lua(format("xml.write_out_y = 6; xml.write_out_x = 16; xml.write_out_h = %d; xml.write_out_w = %d; xml.write_y = 0; xml.write_x = 0; xml:display_xml(auto_aux.types_desc['%s'][2][1], '')", hgt - 3 - 5, wid - 1 - 15 - 1, types_list[sel]));
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ else if (c == '8')
+ {
+ sel--;
+ if (sel < 0)
+ {
+ sel = max - 1;
+ begin = max - hgt;
+ if (begin < 0) begin = 0;
+ }
+ if (sel < begin) begin = sel;
+ }
+ else if (c == '2')
+ {
+ sel++;
+ if (sel >= max)
+ {
+ sel = 0;
+ begin = 0;
+ }
+ if (sel >= begin + hgt - 1) begin++;
+ }
+ else if (c == '\r')
+ {
+ return sel;
+ }
+ }
+ return -1;
+}
+
+static void adjust_begin(int *begin, int *sel, int max, int hgt)
+{
+ if (*sel < 0)
+ {
+ *sel = max - 1;
+ *begin = *sel - hgt + 3;
+ if (*begin < 0) *begin = 0;
+ }
+ if (*sel < *begin) *begin = *sel;
+
+ if (*sel >= max)
+ {
+ *sel = 0;
+ *begin = 0;
+ }
+ if (*sel >= *begin + hgt - 2) (*begin)++;
+}
+
+#define ACTIVE_LIST 0
+#define ACTIVE_RULE 1
+void do_cmd_automatizer()
+{
+ int wid = 0, hgt = 0;
+ char c;
+ cptr *list = NULL;
+ int max, begin = 0, sel = 0;
+ int active = ACTIVE_LIST;
+ cptr keys;
+ cptr keys2;
+ cptr keys3;
+
+ Term_get_size(&wid, &hgt);
+
+ if (!automatizer_enabled)
+ {
+ if (msg_box("Automatizer is currently disabled, enable it? (y/n)", hgt / 2, wid / 2) == 'y')
+ {
+ automatizer_enabled = TRUE;
+ }
+ else
+ return;
+ }
+
+ screen_save();
+
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ list = get_rule_list(&max);
+ display_list(0, 0, hgt - 1, 15, "Rules", list, max, begin, sel, (active == ACTIVE_LIST) ? TERM_L_GREEN : TERM_GREEN);
+ C_FREE(list, max, cptr);
+
+ draw_box(0, 15, hgt - 4, wid - 1 - 15);
+ if (active == ACTIVE_RULE)
+ {
+ keys = "#Bup#W/#Bdown#W/#Bleft#W/#Bright#W to navitage rule, #B9#W/#B3#W/#B7#W/#B1#W to scroll";
+ keys2 = "#Btab#W for switch, #Ba#Wdd clause, #Bd#Welete clause/rule";
+ keys3 = "#Bx#W to toggle english/xml, #G?#W for Automatizer help";
+ exec_lua("xml.write_active = not nil");
+ }
+ else
+ {
+ keys = "#Bup#W/#Bdown#W to scroll, #Btab#W to switch to the rule window";
+ keys2 = "#Bu#W/#Bd#W to move rules, #Bn#Wew rule, #Br#Wename rule, #Bs#Wave rules";
+ keys3 = "#Rk#W to #rdisable#W the automatizer, #G?#W for Automatizer help";
+ exec_lua("xml.write_active = nil");
+ }
+ display_message(17, hgt - 3, strlen(keys), TERM_WHITE, keys);
+ display_message(17, hgt - 2, strlen(keys2), TERM_WHITE, keys2);
+ display_message(17, hgt - 1, strlen(keys3), TERM_WHITE, keys3);
+
+ if (max) exec_lua(format("xml.write_out_y = 1; xml.write_out_x = 16; xml.write_out_h = %d; xml.write_out_w = %d; xml.write_y = 0; xml.write_x = 0; xml:display_xml(__rules[%d].table, '')", hgt - 4 - 1, wid - 1 - 15 - 1, sel));
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ if (active == ACTIVE_LIST)
+ {
+ if (c == '?')
+ {
+ screen_save();
+ show_file("automat.txt", "Automatizer help", 0, 0);
+ screen_load();
+ }
+ else if (c == '8')
+ {
+ if (!max) continue;
+ sel--;
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == '2')
+ {
+ if (!max) continue;
+ sel++;
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == 'u')
+ {
+ if (!max) continue;
+ sel = exec_lua(format("return auto_aux:move_up(%d)", sel));
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == 'd')
+ {
+ if (!max) continue;
+ sel = exec_lua(format("return auto_aux:move_down(%d)", sel));
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (c == 'n')
+ {
+ char name[20];
+ char typ;
+
+ sprintf(name, "No name");
+ if (input_box("Name?", hgt / 2, wid / 2, name, 15))
+ {
+ cptr styp = "nothing";
+ typ = msg_box("[D]estroy, [P]ickup, [I]nscribe, [N]othing rule?", hgt / 2, wid / 2);
+ if ((typ == 'd') || (typ == 'D')) styp = "destroy";
+ else if ((typ == 'p') || (typ == 'P')) styp = "pickup";
+ else if ((typ == 'i') || (typ == 'I')) styp = "inscribe";
+ exec_lua(format("auto_aux:new_rule(%d, '%s', '%s', ''); auto_aux:adjust_current(%d)", sel, name, styp, sel));
+ active = ACTIVE_RULE;
+ }
+ }
+ else if (c == 's')
+ {
+ char name[20];
+
+ sprintf(name, "automat.atm");
+ if (input_box("Save name?", hgt / 2, wid / 2, name, 30))
+ {
+ char buf[1025];
+ char ch;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ if (file_exist(buf))
+ {
+ c_put_str(TERM_WHITE, "File exists, continue?[y/n]", hgt / 2, wid / 2 - 14);
+ ch = inkey();
+ if ((ch != 'Y') && (ch != 'y'))
+ continue;
+ }
+
+ /* Open the non-existing file */
+ hook_file = my_fopen(buf, "w");
+
+ /* Invalid file */
+ if (!hook_file)
+ {
+ /* Message */
+ c_put_str(TERM_WHITE, "Saving rules failed! ", hgt / 2, wid / 2 - 14);
+ (void) inkey();
+
+ /* Error */
+ continue;
+ }
+
+
+
+ exec_lua("auto_aux:save_ruleset()");
+ my_fclose(hook_file);
+ /* Overwrite query message */
+ c_put_str(TERM_WHITE, "Saved rules in file ", hgt / 2, wid / 2 - 14);
+ (void) inkey();
+ }
+ }
+ else if (c == 'r')
+ {
+ char name[20];
+
+ if (!max) continue;
+
+ sprintf(name, string_exec_lua(format("return __rules[%d].table.args.name", sel)));
+ if (input_box("New name?", hgt / 2, wid / 2, name, 15))
+ {
+ exec_lua(format("auto_aux:rename_rule(%d, '%s')", sel, name));
+ }
+
+ continue;
+ }
+ else if (c == 'k')
+ {
+ automatizer_enabled = FALSE;
+ break;
+ }
+ else if (c == '\t')
+ {
+ if (!max) continue;
+ active = ACTIVE_RULE;
+ }
+ else if (c == 'x')
+ {
+ exec_lua("xml.display_english = not xml.display_english");
+ }
+ }
+ else if (active == ACTIVE_RULE)
+ {
+ if (c == '?')
+ {
+ screen_save();
+ show_file("automat.txt", "Automatizer help", 0, 0);
+ screen_load();
+ }
+ else if (c == '8')
+ {
+ exec_lua("auto_aux:go_up()");
+ }
+ else if (c == '2')
+ {
+ exec_lua("auto_aux:go_down()");
+ }
+ else if (c == '6')
+ {
+ exec_lua("auto_aux:go_right()");
+ }
+ else if (c == '4')
+ {
+ exec_lua(format("auto_aux:go_left(%d)", sel));
+ }
+ else if (c == '9')
+ {
+ exec_lua("auto_aux:scroll_up()");
+ }
+ else if (c == '3')
+ {
+ exec_lua("auto_aux:scroll_down()");
+ }
+ else if (c == '7')
+ {
+ exec_lua("auto_aux:scroll_left()");
+ }
+ else if (c == '1')
+ {
+ exec_lua("auto_aux:scroll_right()");
+ }
+ else if (c == 'a')
+ {
+ int s = automatizer_new_rule();
+ if (s == -1) continue;
+ exec_lua(format("auto_aux:add_child('%s')", types_list[s]));
+ }
+ else if (c == 'd')
+ {
+ if (max)
+ {
+ int new_sel;
+
+ new_sel = exec_lua(format("return auto_aux:del_self(%d)", sel));
+ if ((sel != new_sel) && (new_sel >= 0))
+ {
+ sel = new_sel;
+ adjust_begin(&begin, &sel, max, hgt);
+ exec_lua(format("auto_aux:adjust_current(%d)", sel));
+ }
+ else if (new_sel == -1)
+ {
+ active = ACTIVE_LIST;
+ }
+ }
+ }
+ else if (c == '\t')
+ {
+ active = ACTIVE_LIST;
+ }
+ else if (c == 'x')
+ {
+ exec_lua("xml.display_english = not xml.display_english");
+ }
+ }
+ }
+
+ /* Recalculate the rules */
+ exec_lua("auto_aux.regen_ruleset()");
+
+ screen_load();
+}
+
+/* Add a new rule in an easy way */
+bool automatizer_create = FALSE;
+void automatizer_add_rule(object_type *o_ptr, bool destroy)
+{
+ char ch;
+ bool do_status = FALSE;
+ cptr type = "destroy";
+
+ if (!destroy)
+ {
+ type = "pickup";
+ }
+
+ while (TRUE)
+ {
+ if (!get_com(format("%s all of the same [T]ype, [F]amily or [N]ame, also use [S]tatus (%s)? ", (destroy) ? "Destroy" : "Pickup", (do_status) ? "Yes" : "No"), &ch))
+ {
+ break;
+ }
+
+ if (ch == 'S' || ch == 's')
+ {
+ do_status = !do_status;
+ continue;
+ }
+
+ if (ch == 'T' || ch == 't')
+ {
+ call_lua("easy_add_rule", "(s,s,d,O)", "", type, "tsval", do_status, o_ptr);
+ break;
+ }
+
+ if (ch == 'F' || ch == 'f')
+ {
+ call_lua("easy_add_rule", "(s,s,d,O)", "", type, "tval", do_status, o_ptr);
+ break;
+ }
+
+ if (ch == 'N' || ch == 'n')
+ {
+ call_lua("easy_add_rule", "(s,s,d,O)", "", type, "name", do_status, o_ptr);
+ break;
+ }
+ }
+}
diff --git a/src/status.c b/src/status.c
new file mode 100644
index 00000000..de531593
--- /dev/null
+++ b/src/status.c
@@ -0,0 +1,778 @@
+/* File status.c */
+
+/* Purpose: Status information */
+
+/* Written by Pat Gunn <qc@apk.net> for ToME
+ * This file is released into the public domain
+ */
+
+/* Throughout this file, I make use of 2-d arrays called flag_arr.
+ * I fill them out with esp as the 0th element because then the second
+ * index is equal to the f-number that they are in the attributes, and
+ * that makes it more intuitive to use. I do need to fill them out in an
+ * odd order, but that's all abstracted nicely away. The 6th element is used
+ * to mark if the rest of the array is filled, that is, if there's an object
+ * there.
+ */
+
+#include "angband.h"
+
+static void row_trival(char*, s16b, u32b, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_bival(char*, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_npval(char*, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void statline(char*, int, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_hd_bon(int, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_count(char*, s16b, u32b, int, s16b, u32b, int, s16b, u32b, int, s16b, u32b, int, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static int row_x_start = 0;
+
+static void status_count(s32b val1, int v1, s32b val2, int v2, s32b val3, int v3, s32b val4, int v4, byte ypos, byte xpos);
+static void status_trival(s32b, s32b, byte, byte);
+static void status_bival(s32b, byte, byte);
+static void status_numeric(s32b, byte, byte);
+static void status_curses(void);
+static void status_companion(void);
+void status_sight(void);
+void status_attr(void);
+void status_combat(void);
+void status_main(void);
+void status_move(void);
+void status_item(void);
+void az_line(int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+#define STATNM_LENGTH 11
+#define SL_LENGTH 11
+
+#define INVEN_PLAYER (INVEN_TOTAL - INVEN_WIELD + 1)
+
+void status_attr(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 0;
+ char c;
+
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Statistics", yo++, 1);
+ yo += 2;
+ az_line(SL_LENGTH, flag_arr);
+ statline("Str", A_STR, TR1_STR, yo++, flag_arr);
+ statline("Int", A_INT, TR1_INT, yo++, flag_arr);
+ statline("Wis", A_INT, TR1_WIS, yo++, flag_arr);
+ statline("Con", A_CON, TR1_CON, yo++, flag_arr);
+ statline("Dex", A_DEX, TR1_DEX, yo++, flag_arr);
+ statline("Chr", A_CHR, TR1_CHR, yo++, flag_arr);
+ row_npval("Luck", 5, TR5_LUCK, yo++, flag_arr);
+ yo++;
+ row_npval("Life", 2, TR2_LIFE, yo++, flag_arr);
+ row_npval("Mana", 1, TR1_MANA, yo++, flag_arr);
+
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ c = inkey();
+ if (c == ESCAPE) break;
+ }
+}
+
+void status_move(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Movement", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_trival("Fly/Lev", 4, TR4_FLY, 3, TR3_FEATHER, yo++, flag_arr);
+ row_bival("Climb", 4, TR4_CLIMB, yo++, flag_arr);
+ row_npval("Dig", 1, TR1_TUNNEL, yo++, flag_arr);
+ row_npval("Speed", 1, TR1_SPEED, yo++, flag_arr);
+ row_bival("Wraith", 3, TR3_WRAITH, yo++, flag_arr);
+ yo++;
+ row_npval("Stealth", 1, TR1_STEALTH, yo++, flag_arr);
+ row_bival("Telep", 3, TR3_TELEPORT, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_sight(void)
+/* Tell player about ESP, infravision, auto-id, see invis, and similar */
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Sight", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_bival("SeeInvis", 3, TR3_SEE_INVIS, yo++, flag_arr);
+ row_npval("Invis", 2, TR2_INVIS, yo++, flag_arr);
+ row_npval("Infra", 1, TR1_INFRA, yo++, flag_arr);
+ row_npval("Search", 1, TR1_SEARCH, yo++, flag_arr);
+ row_bival("AutoID", 4, TR4_AUTO_ID, yo++, flag_arr);
+ row_count("Light", 3, TR3_LITE1, 1, 4, TR4_LITE2, 2, 4, TR4_LITE3, 3, 0, 0, 0, yo++, flag_arr);
+ row_bival("Full ESP", 0, ESP_ALL, yo++, flag_arr);
+ row_bival("Orc ESP", 0, ESP_ORC, yo++, flag_arr);
+ row_bival("Trol ESP", 0, ESP_TROLL, yo++, flag_arr);
+ row_bival("Drag ESP", 0, ESP_DRAGON, yo++, flag_arr);
+ row_bival("GiantESP", 0, ESP_GIANT, yo++, flag_arr);
+ row_bival("DemonESP", 0, ESP_DEMON, yo++, flag_arr);
+ row_bival("Undd ESP", 0, ESP_UNDEAD, yo++, flag_arr);
+ row_bival("Evil ESP", 0, ESP_EVIL, yo++, flag_arr);
+ row_bival("Anim ESP", 0, ESP_ANIMAL, yo++, flag_arr);
+ row_bival("Drid ESP", 0, ESP_THUNDERLORD, yo++, flag_arr);
+ row_bival("Good ESP", 0, ESP_GOOD, yo++, flag_arr);
+ row_bival("SpidrESP", 0, ESP_SPIDER, yo++, flag_arr);
+ row_bival("NonlvESP", 0, ESP_NONLIVING, yo++, flag_arr);
+ row_bival("Uniq ESP", 0, ESP_UNIQUE, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_item(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Misc", 0, 1);
+
+ row_x_start = 40;
+ az_line(STATNM_LENGTH + row_x_start, flag_arr);
+ row_bival("Blessed", 3, TR3_BLESSED, yo++, flag_arr);
+ row_bival("Activate", 3, TR3_ACTIVATE, yo++, flag_arr);
+ row_bival("EasyKnow", 3, TR3_EASY_KNOW, yo++, flag_arr);
+ row_bival("HideType", 3, TR3_HIDE_TYPE, yo++, flag_arr);
+ yo++;
+ row_bival("SafeAcid", 3, TR3_IGNORE_ACID, yo++, flag_arr);
+ row_bival("SafeElec", 3, TR3_IGNORE_ELEC, yo++, flag_arr);
+ row_bival("SafeFire", 3, TR3_IGNORE_FIRE, yo++, flag_arr);
+ row_bival("SafeCold", 3, TR3_IGNORE_COLD, yo++, flag_arr);
+ row_bival("ResMorgul", 5, TR5_RES_MORGUL, yo++, flag_arr);
+
+ yo = 3;
+ row_x_start = 0;
+ az_line(STATNM_LENGTH, flag_arr);
+ row_bival("Sh.fire", 3, TR3_SH_FIRE, yo++, flag_arr);
+ row_bival("Sh.elec", 3, TR3_SH_ELEC, yo++, flag_arr);
+ row_bival("Regen", 3, TR3_REGEN, yo++, flag_arr);
+ row_bival("SlowDigest", 3, TR3_SLOW_DIGEST, yo++, flag_arr);
+ row_bival("Precog", 4, TR4_PRECOGNITION, yo++, flag_arr);
+ row_bival("Auto.Id", 4, TR4_AUTO_ID, yo++, flag_arr);
+ row_bival("Spell.In", 5, TR5_SPELL_CONTAIN, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ loop_exit = TRUE;
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_combat(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Combat", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_npval("Spell", 1, TR1_SPELL, yo++, flag_arr);
+ row_npval("Blows", 1, TR1_BLOWS, yo++, flag_arr);
+ row_npval("Crits", 5, TR5_CRIT, yo++, flag_arr);
+ row_npval("Ammo_Mgt", 3, TR3_XTRA_MIGHT, yo++, flag_arr);
+ row_npval("Ammo_Sht", 3, TR3_XTRA_SHOTS, yo++, flag_arr);
+ row_bival("Vorpal", 1, TR1_VORPAL, yo++, flag_arr);
+ row_bival("Quake", 1, TR1_IMPACT, yo++, flag_arr);
+ row_bival("Chaotic", 1, TR1_CHAOTIC, yo++, flag_arr);
+ row_bival("Vampiric", 1, TR1_VAMPIRIC, yo++, flag_arr);
+ row_bival("Poison", 1, TR1_BRAND_POIS, yo++, flag_arr);
+ row_bival("Acidic", 1, TR1_BRAND_ACID, yo++, flag_arr);
+ row_bival("Shocks", 1, TR1_BRAND_ELEC, yo++, flag_arr);
+ row_bival("Burns", 1, TR1_BRAND_FIRE, yo++, flag_arr);
+ row_bival("Chills", 1, TR1_BRAND_COLD, yo++, flag_arr);
+ row_bival("Wound", 5, TR5_WOUNDING, yo++, flag_arr);
+
+ row_x_start = 40;
+ yo = 3;
+ az_line(row_x_start + STATNM_LENGTH, flag_arr);
+ row_bival("No.Blow", 4, TR4_NEVER_BLOW, yo++, flag_arr);
+ row_trival("S/K Undd", 1, TR1_SLAY_UNDEAD, 5, TR5_KILL_UNDEAD, yo++, flag_arr);
+ row_trival("S/K Dmn", 1, TR1_SLAY_DEMON, 5, TR5_KILL_DEMON, yo++, flag_arr);
+ row_trival("S/K Drag", 1, TR1_SLAY_DRAGON, 1, TR1_KILL_DRAGON, yo++, flag_arr);
+ row_bival("Sl.Orc", 1, TR1_SLAY_ORC, yo++, flag_arr);
+ row_bival("Sl.Troll", 1, TR1_SLAY_TROLL, yo++, flag_arr);
+ row_bival("Sl.Giant", 1, TR1_SLAY_GIANT, yo++, flag_arr);
+ row_bival("Sl.Evil", 1, TR1_SLAY_EVIL, yo++, flag_arr);
+ row_bival("Sl.Animal", 1, TR1_SLAY_ANIMAL, yo++, flag_arr);
+ row_hd_bon(0, yo++, flag_arr);
+ row_hd_bon(1, yo++, flag_arr);
+ row_x_start = 0;
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool loop_exit = FALSE;
+ char c;
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ loop_exit = TRUE;
+ }
+ if (loop_exit)
+ {
+ break;
+ }
+ }
+}
+
+void status_curses(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Curses", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_trival("Hvy/Nrm", 3, TR3_HEAVY_CURSE, 3, TR3_CURSED, yo++, flag_arr);
+ row_bival("Perma", 3, TR3_PERMA_CURSE, yo++, flag_arr);
+ row_trival("DG/Ty", 4, TR4_DG_CURSE, 3, TR3_TY_CURSE, yo++, flag_arr);
+ row_trival("Prm/Auto", 3, TR3_PERMA_CURSE, 3, TR3_AUTO_CURSE, yo++, flag_arr);
+ row_bival("NoDrop", 4, TR4_CURSE_NO_DROP, yo++, flag_arr);
+ yo++;
+ row_bival("B.Breath", 4, TR4_BLACK_BREATH, yo++, flag_arr);
+ row_bival("Dr.Exp", 3, TR3_DRAIN_EXP, yo++, flag_arr);
+ row_bival("Dr.Mana", 5, TR5_DRAIN_MANA, yo++, flag_arr);
+ row_bival("Dr.HP", 5, TR5_DRAIN_HP, yo++, flag_arr);
+ row_bival("No Hit", 4, TR4_NEVER_BLOW, yo++, flag_arr);
+ row_bival("NoTelep", 3, TR3_NO_TELE, yo++, flag_arr);
+ row_bival("NoMagic", 3, TR3_NO_MAGIC, yo++, flag_arr);
+ row_bival("Aggrav", 3, TR3_AGGRAVATE, yo++, flag_arr);
+ row_bival("Clone", 4, TR4_CLONE, yo++, flag_arr);
+ row_bival("Temp", 5, TR5_TEMPORARY, yo++, flag_arr);
+ yo++;
+ row_count("Antimagic", 4, TR4_ANTIMAGIC_50, 5, 4, TR4_ANTIMAGIC_30, 3, 4, TR4_ANTIMAGIC_20, 2, 4, TR4_ANTIMAGIC_10, 1, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool loop_exit = FALSE;
+ char c;
+
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit == TRUE)
+ {
+ break;
+ }
+ }
+}
+
+void status_res(void)
+{
+ u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7];
+ int yo = 3;
+
+ clear_from(0);
+ c_put_str(TERM_L_BLUE, "Resistances", 0, 1);
+ az_line(STATNM_LENGTH, flag_arr);
+
+ row_trival("Fire", 2, TR2_IM_FIRE, 2, TR2_RES_FIRE, yo++, flag_arr);
+ row_trival("Cold", 2, TR2_IM_COLD, 2, TR2_RES_COLD, yo++, flag_arr);
+ row_trival("Acid", 2, TR2_IM_ACID, 2, TR2_RES_ACID, yo++, flag_arr);
+ row_trival("Lightning", 2, TR2_IM_ELEC, 2, TR2_RES_ELEC, yo++, flag_arr);
+ row_bival("Poison", 2, TR2_RES_POIS, yo++, flag_arr);
+ row_bival("Lite", 2, TR2_RES_LITE, yo++, flag_arr);
+ row_bival("Dark", 2, TR2_RES_DARK, yo++, flag_arr);
+ row_bival("Sound", 2, TR2_RES_SOUND, yo++, flag_arr);
+ row_bival("Shards", 2, TR2_RES_SHARDS, yo++, flag_arr);
+ row_trival("Nether", 4, TR4_IM_NETHER, 2, TR2_RES_NETHER, yo++, flag_arr);
+ row_bival("Nexus", 2, TR2_RES_NEXUS, yo++, flag_arr);
+ row_bival("Chaos", 2, TR2_RES_CHAOS, yo++, flag_arr);
+ row_bival("Disen.", 2, TR2_RES_DISEN, yo++, flag_arr);
+ row_bival("Confusion", 2, TR2_RES_CONF, yo++, flag_arr);
+ row_bival("Blindness", 2, TR2_RES_BLIND, yo++, flag_arr);
+ row_bival("Fear", 2, TR2_RES_FEAR, yo++, flag_arr);
+ row_bival("Free Act", 2, TR2_FREE_ACT, yo++, flag_arr);
+ row_bival("Reflect", 2, TR2_REFLECT, yo++, flag_arr);
+ row_bival("Hold Life", 2, TR2_HOLD_LIFE, yo++, flag_arr);
+
+ c_put_str(TERM_WHITE, "Press ESC to continue", 23, 0);
+ Term_fresh();
+ while (1)
+ {
+ bool loop_exit = FALSE;
+ char c;
+
+ c = inkey();
+ switch (c)
+ {
+ case ESCAPE:
+ {
+ loop_exit = TRUE;
+ }
+ }
+ if (loop_exit == TRUE)
+ {
+ break;
+ }
+ }
+}
+
+void status_main(void)
+{
+ int do_quit = 0;
+ char c;
+
+ character_icky = TRUE;
+ Term_save();
+ while (1)
+ {
+ clear_from(0);
+ c_put_str(TERM_WHITE, format("%s Character Status screen", game_module), 0, 10);
+ c_put_str(TERM_WHITE, "1) Statistics", 2, 5);
+ c_put_str(TERM_WHITE, "2) Movement", 3, 5);
+ c_put_str(TERM_WHITE, "3) Combat", 4, 5);
+ c_put_str(TERM_WHITE, "4) Resistances", 5, 5);
+ c_put_str(TERM_WHITE, "5) Misc", 6, 5);
+ c_put_str(TERM_WHITE, "6) Curses", 7, 5);
+ c_put_str(TERM_WHITE, "7) Sight", 8, 5);
+ c_put_str(TERM_WHITE, "8) Companions", 9, 5);
+ c_put_str(TERM_RED, "Press 'q' to Quit", 23, 5);
+ c = inkey();
+ switch (c)
+ {
+ case '1':
+ status_attr();
+ break;
+ case '2':
+ status_move();
+ break;
+ case '3':
+ status_combat();
+ break;
+ case '4':
+ status_res();
+ break;
+ case '5':
+ status_item();
+ break;
+ case '6':
+ status_curses();
+ break;
+ case '7':
+ status_sight();
+ break;
+ case '8':
+ status_companion();
+ break;
+ case 'q':
+ case ESCAPE:
+ do_quit = 1; /* Schedule leaving the outer loop */
+ break;
+ }
+ Term_fresh();
+ if (do_quit) break;
+ }
+ Term_load();
+ character_icky = FALSE;
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+ handle_stuff();
+}
+
+void az_line(int xo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int index = xo; /* Leave room for description */
+ int i;
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->inventory[i].k_idx) /* Wearing anything here? */
+ {
+ char cstrng[2];
+ cstrng[0] = (i - INVEN_WIELD) + 'a'; /* Assumes ASCII */
+ cstrng[1] = '\0'; /* terminate it */
+ c_put_str(TERM_WHITE, cstrng, 2, index++); /* Assumes ASCII */
+
+ /* DGDGDGDG */
+ /* object_flags_known(&inventory[i],*/
+ object_flags(&p_ptr->inventory[i], /* Help me debug */
+ &flag_arr[i - INVEN_WIELD][1], /* f1 */
+ &flag_arr[i - INVEN_WIELD][2], /* f2 */
+ &flag_arr[i - INVEN_WIELD][3], /* f3 */
+ &flag_arr[i - INVEN_WIELD][4], /* f4 */
+ &flag_arr[i - INVEN_WIELD][5], /* f5 */
+ &flag_arr[i - INVEN_WIELD][0]); /* esp */
+ flag_arr[i - INVEN_WIELD][6] = 1; /* And mark it to display */
+ }
+ else flag_arr[i - INVEN_WIELD][6] = 0; /* Otherwise don't display it */
+ }
+ c_put_str(TERM_WHITE, "@", 2, index++);
+ player_flags(
+ &flag_arr[INVEN_PLAYER][1], /* f1 */
+ &flag_arr[INVEN_PLAYER][2], /* f2 */
+ &flag_arr[INVEN_PLAYER][3], /* f3 */
+ &flag_arr[INVEN_PLAYER][4], /* f4 */
+ &flag_arr[INVEN_PLAYER][5], /* f5 */
+ &flag_arr[INVEN_PLAYER][0] /* esp */
+ );
+ flag_arr[INVEN_PLAYER][6] = 1;
+}
+
+static void status_trival(s32b val1, s32b val2, byte ypos, byte xpos)
+{
+ if (val1 != 0)
+ c_put_str(TERM_L_BLUE, "*", ypos, xpos);
+ else if (val2 != 0)
+ c_put_str(TERM_L_BLUE, "+", ypos, xpos);
+ else
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+}
+
+static void status_bival(s32b val, byte ypos, byte xpos)
+{
+ if (val != 0)
+ c_put_str(TERM_L_BLUE, "+", ypos, xpos);
+ else
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+}
+
+static void status_numeric(s32b val, byte ypos, byte xpos)
+{
+ if (val == 0)
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+ else if (val < 0)
+ {
+ val *= -1;
+ if (val > 9)
+ c_put_str(TERM_RED, "*", ypos, xpos);
+ else
+ {
+ char strnum[2];
+ sprintf(strnum, "%lu", val);
+ c_put_str(TERM_RED, strnum, ypos, xpos);
+ }
+ }
+ else if (val > 0)
+ {
+ if (val > 9)
+ c_put_str(TERM_GREEN, "*", ypos, xpos);
+ else
+ {
+ char strnum[2];
+ sprintf(strnum, "%lu", val);
+ c_put_str(TERM_GREEN, strnum, ypos, xpos);
+ }
+ }
+}
+
+static void status_count(s32b val1, int v1, s32b val2, int v2, s32b val3, int v3, s32b val4, int v4, byte ypos, byte xpos)
+{
+ int v = 0;
+
+ if (val1 != 0) v += v1;
+ if (val2 != 0) v += v2;
+ if (val3 != 0) v += v3;
+ if (val4 != 0) v += v4;
+
+ status_numeric(v, ypos, xpos);
+}
+
+static void row_count(char* statname, s16b row1, u32b flag1, int v1, s16b row2, u32b flag2, int v2, s16b row3, u32b flag3, int v3, s16b row4, u32b flag4, int v4, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int i;
+ int x = row_x_start;
+
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ status_count((flag_arr[i][row1] & flag1), v1, (flag_arr[i][row2] & flag2), v2, (flag_arr[i][row3] & flag3), v3, (flag_arr[i][row4] & flag4), v4, yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void row_trival(char* statname, s16b row, u32b flag, s16b row2, u32b flag2, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int i;
+ int x = row_x_start;
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ status_trival(
+ (flag_arr[i][row] & flag),
+ (flag_arr[i][row2] & flag2),
+ yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void row_bival(char* statname, s16b row, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int i;
+ int x = row_x_start;
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ status_bival((flag_arr[i][row] & flag), yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void row_npval(char* statname, s16b row, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* Displays nicely a pval-based status row */
+{
+ int i;
+ int x = row_x_start;
+ c_put_str(TERM_L_GREEN, statname, yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ if (i == INVEN_PLAYER)
+ /* Special case, player_flags */
+ /* Players lack a pval, no way to calc value */
+ {
+ if (flag_arr[i][row] & flag)
+ c_put_str(TERM_YELLOW, "*", yo, x + STATNM_LENGTH);
+ else c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if (flag_arr[i][row] & flag)
+ status_numeric(p_ptr->inventory[i + INVEN_WIELD].pval, yo, x + STATNM_LENGTH);
+ else
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void statline(char* statname, int statidx, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* Displays a status row for a primary stat */
+{
+ int i;
+ int x = row_x_start;
+ char statstr[8];
+ byte stat_color = TERM_L_RED;
+
+ cnv_stat(p_ptr->stat_use[statidx], statstr);
+
+ c_put_str(TERM_L_GREEN, statstr, yo, 4 + row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ byte color = TERM_L_RED;
+
+ if (flag_arr[i][6] == 1)
+ {
+ switch (statidx)
+ {
+ case A_STR:
+ if (flag_arr[i][2] & TR2_SUST_STR)
+ color = TERM_L_BLUE;
+ break;
+ case A_INT:
+ if (flag_arr[i][2] & TR2_SUST_INT)
+ color = TERM_L_BLUE;
+ break;
+ case A_WIS:
+ if (flag_arr[i][2] & TR2_SUST_WIS)
+ color = TERM_L_BLUE;
+ break;
+ case A_DEX:
+ if (flag_arr[i][2] & TR2_SUST_DEX)
+ color = TERM_L_BLUE;
+ break;
+ case A_CON:
+ if (flag_arr[i][2] & TR2_SUST_CON)
+ color = TERM_L_BLUE;
+ break;
+ case A_CHR:
+ if (flag_arr[i][2] & TR2_SUST_CHR)
+ color = TERM_L_BLUE;
+ break;
+ }
+
+ if (i == INVEN_PLAYER ) /* Player flags */
+ {
+ if (flag_arr[i][1] & flag)
+ c_put_str((color == TERM_L_RED) ? TERM_YELLOW : color, "*", yo, x + SL_LENGTH);
+ else c_put_str((color == TERM_L_RED) ? TERM_WHITE : color, ".", yo, x + SL_LENGTH);
+ x++;
+ continue;
+ }
+ if (flag_arr[i][1] & flag)
+ status_numeric(p_ptr->inventory[i + INVEN_WIELD].pval, yo, x + SL_LENGTH);
+ else
+ c_put_str((color == TERM_L_RED) ? TERM_WHITE : color, ".", yo, x + SL_LENGTH);
+
+ if (color != TERM_L_RED)
+ stat_color = color;
+
+ x++;
+ }
+ }
+
+ c_put_str(stat_color, statname, yo, row_x_start);
+}
+
+static void row_hd_bon(int which, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* To-hit/dmg modifiers, selected by 1st argument */
+{
+ int i;
+ int x = row_x_start;
+ if ((which != 0) && (which != 1)) return;
+ c_put_str(TERM_L_GREEN, ((which == 0) ? "To-Hit" : "To-Dmg"), yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ if (i == INVEN_PLAYER) /* Player? */
+ {
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if ( (which == 0) && (p_ptr->inventory[INVEN_WIELD + i].to_h != 0))
+ {
+ status_numeric(p_ptr->inventory[INVEN_WIELD + i].to_h, yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if ( (which == 1) && (p_ptr->inventory[INVEN_WIELD + i].to_d != 0))
+ {
+ status_numeric(p_ptr->inventory[INVEN_WIELD + i].to_d, yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void status_companion(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ Term_clear();
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Calculate companions */
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_COMPANION)
+ {
+ char m_name[80];
+ int b, y = 0;
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0x80);
+
+ fprintf(fff, "#####BCompanion: %s\n", m_name);
+
+ fprintf(fff, " Lev/Exp : [[[[[G%d / %ld]\n", m_ptr->level, m_ptr->exp);
+ if (m_ptr->level < MONSTER_LEVEL_MAX) fprintf(fff, " Next lvl: [[[[[G%ld]\n", MONSTER_EXP((s32b)m_ptr->level + 1));
+ else fprintf(fff, " Next lvl: [[[[[G****]\n");
+
+ fprintf(fff, " HP : [[[[[G%ld / %ld]\n", m_ptr->hp, m_ptr->maxhp);
+ fprintf(fff, " AC : [[[[[G%d]\n", m_ptr->ac);
+ fprintf(fff, " Speed : [[[[[G%d]\n", m_ptr->mspeed - 110);
+
+ for (b = 0; b < 4; b++)
+ {
+ if (!m_ptr->blow[b].d_dice) continue;
+ if (!m_ptr->blow[b].d_side) continue;
+
+ fprintf(fff, " Blow %1d : [[[[[G%dd%d]\n", y + 1, m_ptr->blow[b].d_dice, m_ptr->blow[b].d_side);
+ y++;
+ }
+
+ fprintf(fff, "\n");
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Companion List", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
diff --git a/src/store.c b/src/store.c
new file mode 100644
index 00000000..a54635e8
--- /dev/null
+++ b/src/store.c
@@ -0,0 +1,4531 @@
+/* File: store.c */
+
+/* Purpose: Store commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+#define RUMOR_CHANCE 8
+
+#define MAX_COMMENT_1 6
+
+static cptr comment_1[MAX_COMMENT_1] =
+{
+ "Okay.",
+ "Fine.",
+ "Accepted!",
+ "Agreed!",
+ "Done!",
+ "Taken!"
+};
+
+#define MAX_COMMENT_2A 2
+
+static cptr comment_2a[MAX_COMMENT_2A] =
+{
+ "You try my patience. %s is final.",
+ "My patience grows thin. %s is final."
+};
+
+#define MAX_COMMENT_2B 12
+
+static cptr comment_2b[MAX_COMMENT_2B] =
+{
+ "I can take no less than %s gold pieces.",
+ "I will accept no less than %s gold pieces.",
+ "Ha! No less than %s gold pieces.",
+ "You knave! No less than %s gold pieces.",
+ "That's a pittance! I want %s gold pieces.",
+ "That's an insult! I want %s gold pieces.",
+ "As if! How about %s gold pieces?",
+ "My gosh! How about %s gold pieces?",
+ "May the fleas of 1000 orcs molest you! Try %s gold pieces.",
+ "May your most favourite weapons rust! Try %s gold pieces.",
+ "May Morgoth find you tasty! Perhaps %s gold pieces?",
+ "Your mother was an Ogre! Perhaps %s gold pieces?"
+};
+
+#define MAX_COMMENT_3A 2
+
+static cptr comment_3a[MAX_COMMENT_3A] =
+{
+ "You try my patience. %s is final.",
+ "My patience grows thin. %s is final."
+};
+
+
+#define MAX_COMMENT_3B 12
+
+static cptr comment_3b[MAX_COMMENT_3B] =
+{
+ "Perhaps %s gold pieces?",
+ "How about %s gold pieces?",
+ "I will pay no more than %s gold pieces.",
+ "I can afford no more than %s gold pieces.",
+ "Be reasonable. How about %s gold pieces?",
+ "I'll buy it as scrap for %s gold pieces.",
+ "That is too much! How about %s gold pieces?",
+ "That looks war surplus! Say %s gold pieces?",
+ "Never! %s is more like it.",
+ "That's an insult! %s is more like it.",
+ "%s gold pieces and be thankful for it!",
+ "%s gold pieces and not a copper more!"
+};
+
+#define MAX_COMMENT_4A 4
+
+static cptr comment_4a[MAX_COMMENT_4A] =
+{
+ "Enough! You have abused me once too often!",
+ "Arghhh! I have had enough abuse for one day!",
+ "That does it! You shall waste my time no more!",
+ "This is getting nowhere! I'm going to Londis!"
+};
+
+#define MAX_COMMENT_4B 4
+
+static cptr comment_4b[MAX_COMMENT_4B] =
+{
+ "Leave my store!",
+ "Get out of my sight!",
+ "Begone, you scoundrel!",
+ "Out, out, out!"
+};
+
+#define MAX_COMMENT_5 8
+
+static cptr comment_5[MAX_COMMENT_5] =
+{
+ "Try again.",
+ "Ridiculous!",
+ "You will have to do better than that!",
+ "Do you wish to do business or not?",
+ "You've got to be kidding!",
+ "You'd better be kidding!",
+ "You try my patience.",
+ "Hmmm, nice weather we're having."
+};
+
+#define MAX_COMMENT_6 4
+
+static cptr comment_6[MAX_COMMENT_6] =
+{
+ "I must have heard you wrong.",
+ "I'm sorry, I missed that.",
+ "I'm sorry, what was that?",
+ "Sorry, what was that again?"
+};
+
+
+
+/*
+ * Successful haggle.
+ */
+static void say_comment_1(void)
+{
+ char rumour[80];
+
+ msg_print(comment_1[rand_int(MAX_COMMENT_1)]);
+
+ if (randint(RUMOR_CHANCE) == 1 && speak_unique)
+ {
+ msg_print("The shopkeeper whispers something into your ear:");
+ get_rnd_line("rumors.txt", rumour);
+ msg_print(rumour);
+ }
+}
+
+
+/*
+ * Continue haggling (player is buying)
+ */
+static void say_comment_2(s32b value, int annoyed)
+{
+ char tmp_val[80];
+
+ /* Prepare a string to insert */
+ strnfmt(tmp_val, 80, "%ld", (long)value);
+
+ /* Final offer */
+ if (annoyed > 0)
+ {
+ /* Formatted message */
+ msg_format(comment_2a[rand_int(MAX_COMMENT_2A)], tmp_val);
+ }
+
+ /* Normal offer */
+ else
+ {
+ /* Formatted message */
+ msg_format(comment_2b[rand_int(MAX_COMMENT_2B)], tmp_val);
+ }
+}
+
+
+/*
+ * Continue haggling (player is selling)
+ */
+static void say_comment_3(s32b value, int annoyed)
+{
+ char tmp_val[80];
+
+ /* Prepare a string to insert */
+ strnfmt(tmp_val, 80, "%ld", (long)value);
+
+ /* Final offer */
+ if (annoyed > 0)
+ {
+ /* Formatted message */
+ msg_format(comment_3a[rand_int(MAX_COMMENT_3A)], tmp_val);
+ }
+
+ /* Normal offer */
+ else
+ {
+ /* Formatted message */
+ msg_format(comment_3b[rand_int(MAX_COMMENT_3B)], tmp_val);
+ }
+}
+
+
+/*
+ * Kick 'da bum out. -RAK-
+ */
+static void say_comment_4(void)
+{
+ msg_print(comment_4a[rand_int(MAX_COMMENT_4A)]);
+ msg_print(comment_4b[rand_int(MAX_COMMENT_4B)]);
+}
+
+
+/*
+ * You are insulting me
+ */
+static void say_comment_5(void)
+{
+ msg_print(comment_5[rand_int(MAX_COMMENT_5)]);
+}
+
+
+/*
+ * That makes no sense.
+ */
+static void say_comment_6(void)
+{
+ msg_print(comment_6[rand_int(5)]);
+}
+
+
+
+/*
+ * Messages for reacting to purchase prices.
+ */
+
+#define MAX_COMMENT_7A 4
+
+static cptr comment_7a[MAX_COMMENT_7A] =
+{
+ "Arrgghh!",
+ "You moron!",
+ "You hear someone sobbing...",
+ "The shopkeeper howls in agony!"
+};
+
+#define MAX_COMMENT_7B 4
+
+static cptr comment_7b[MAX_COMMENT_7B] =
+{
+ "Darn!",
+ "You fiend!",
+ "The shopkeeper yells at you.",
+ "The shopkeeper glares at you."
+};
+
+#define MAX_COMMENT_7C 4
+
+static cptr comment_7c[MAX_COMMENT_7C] =
+{
+ "Cool!",
+ "You've made my day!",
+ "The shopkeeper giggles.",
+ "The shopkeeper laughs loudly."
+};
+
+#define MAX_COMMENT_7D 4
+
+static cptr comment_7d[MAX_COMMENT_7D] =
+{
+ "Yippee!",
+ "I think I'll retire!",
+ "The shopkeeper jumps for joy.",
+ "The shopkeeper smiles gleefully."
+};
+
+/*
+ * Let a shop-keeper React to a purchase
+ *
+ * We paid "price", it was worth "value", and we thought it was worth "guess"
+ */
+static void purchase_analyze(s32b price, s32b value, s32b guess)
+{
+ /* Item was worthless, but we bought it */
+ if ((value <= 0) && (price > value))
+ {
+ /* Comment */
+ msg_print(comment_7a[rand_int(MAX_COMMENT_7A)]);
+
+ /* Sound */
+ sound(SOUND_STORE1);
+ }
+
+ /* Item was cheaper than we thought, and we paid more than necessary */
+ else if ((value < guess) && (price > value))
+ {
+ /* Comment */
+ msg_print(comment_7b[rand_int(MAX_COMMENT_7B)]);
+
+ /* Sound */
+ sound(SOUND_STORE2);
+ }
+
+ /* Item was a good bargain, and we got away with it */
+ else if ((value > guess) && (value < (4 * guess)) && (price < value))
+ {
+ /* Comment */
+ msg_print(comment_7c[rand_int(MAX_COMMENT_7C)]);
+
+ /* Sound */
+ sound(SOUND_STORE3);
+ }
+
+ /* Item was a great bargain, and we got away with it */
+ else if ((value > guess) && (price < value))
+ {
+ /* Comment */
+ msg_print(comment_7d[rand_int(MAX_COMMENT_7D)]);
+
+ /* Sound */
+ sound(SOUND_STORE4);
+ }
+}
+
+
+
+
+
+/*
+ * We store the current "store number" here so everyone can access it
+ */
+static int cur_store_num = 7;
+
+/*
+ * We store the current "store page" here so everyone can access it
+ */
+static int store_top = 0;
+
+/*
+ * We store the current "store pointer" here so everyone can access it
+ */
+static store_type *st_ptr = NULL;
+
+/*
+ * We store the current "owner type" here so everyone can access it
+ */
+static owner_type *ot_ptr = NULL;
+
+
+
+/*
+ * Determine the price of an item (qty one) in a store.
+ *
+ * This function takes into account the player's charisma, and the
+ * shop-keepers friendliness, and the shop-keeper's base greed, but
+ * never lets a shop-keeper lose money in a transaction.
+ *
+ * The "greed" value should exceed 100 when the player is "buying" the
+ * item, and should be less than 100 when the player is "selling" it.
+ *
+ * Hack -- the black market always charges twice as much as it should.
+ *
+ * Charisma adjustment runs from 80 to 130
+ * Racial adjustment runs from 95 to 130
+ *
+ * Since greed/charisma/racial adjustments are centered at 100, we need
+ * to adjust (by 200) to extract a usable multiplier. Note that the
+ * "greed" value is always something (?).
+ */
+static s32b price_item(object_type *o_ptr, int greed, bool flip)
+{
+ int factor;
+ int adjust;
+ s32b price;
+
+
+ /* Get the value of one of the items */
+ price = object_value(o_ptr);
+
+ /* Worthless items */
+ if (price <= 0) return (0L);
+
+ /* Compute the racial factor */
+ if (is_state(st_ptr, STORE_LIKED))
+ {
+ factor = ot_ptr->costs[STORE_LIKED];
+ }
+ else if (is_state(st_ptr, STORE_HATED))
+ {
+ factor = ot_ptr->costs[STORE_HATED];
+ }
+ else
+ {
+ factor = ot_ptr->costs[STORE_NORMAL];
+ }
+
+ /* Add in the charisma factor */
+ factor += adj_chr_gold[p_ptr->stat_ind[A_CHR]];
+
+ /* Hack - merchants have better prices */
+#if 0 /* DGDGDGDG -- use a skill */
+ if (cp_ptr->magic_key == MKEY_TELEKINESIS)
+ factor -= p_ptr->lev / 2;
+#endif
+ /* Shop is buying */
+ if (flip)
+ {
+ /* Mega Hack^3 */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ price /= 5;
+ break;
+ }
+
+ /* Adjust for greed */
+ adjust = 100 + (300 - (greed + factor));
+
+ /* Never get "silly" */
+ if (adjust > 100) adjust = 100;
+
+ /* Mega-Hack -- Black market sucks */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM) price = price / 2;
+ }
+
+ /* Shop is selling */
+ else
+ {
+ /* Adjust for greed */
+ adjust = 100 + ((greed + factor) - 300);
+
+ /* Never get "silly" */
+ if (adjust < 100) adjust = 100;
+
+ /* Mega-Hack -- Black market sucks */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM) price = price * 2;
+ }
+
+ /* Compute the final price (with rounding) */
+ price = (price * adjust + 50L) / 100L;
+
+ /* Note -- Never become "free" */
+ if (price <= 0L) return (1L);
+
+ /* Return the price */
+ return (price);
+}
+
+
+/*
+ * Special "mass production" computation
+ */
+static int mass_roll(int num, int max)
+{
+ int i, t = 0;
+ for (i = 0; i < num; i++) t += rand_int(max);
+ return (t);
+}
+
+
+/*
+ * Certain "cheap" objects should be created in "piles"
+ * Some objects can be sold at a "discount" (in small piles)
+ */
+static void mass_produce(object_type *o_ptr)
+{
+ int size = 1;
+ int discount = 0;
+
+ s32b cost = object_value(o_ptr);
+
+
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Food, Flasks, and Lites */
+ case TV_FOOD:
+ case TV_FLASK:
+ case TV_LITE:
+ {
+ if (cost <= 5L) size += mass_roll(3, 5);
+ if (cost <= 20L) size += mass_roll(3, 5);
+ break;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ {
+ if (cost <= 60L) size += mass_roll(3, 5);
+ if (cost <= 240L) size += mass_roll(1, 5);
+ break;
+ }
+
+ case TV_SYMBIOTIC_BOOK:
+ case TV_MUSIC_BOOK:
+ case TV_DRUID_BOOK:
+ case TV_DAEMON_BOOK:
+ case TV_BOOK:
+ {
+ if (cost <= 50L) size += mass_roll(2, 3);
+ if (cost <= 500L) size += mass_roll(1, 3);
+ break;
+ }
+
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SHIELD:
+ case TV_GLOVES:
+ case TV_BOOTS:
+ case TV_CLOAK:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_POLEARM:
+ case TV_HAFTED:
+ case TV_DIGGING:
+ case TV_BOW:
+ {
+ if (o_ptr->name2) break;
+ if (cost <= 10L) size += mass_roll(3, 5);
+ if (cost <= 100L) size += mass_roll(3, 5);
+ break;
+ }
+
+ case TV_SPIKE:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ {
+ if (cost <= 5L) size += mass_roll(5, 5);
+ if (cost <= 50L) size += mass_roll(5, 5);
+ if (cost <= 500L) size += mass_roll(5, 5);
+ break;
+ }
+
+ /* Because many rods (and a few wands and staffs) are useful mainly
+ * in quantity, the Black Market will occasionally have a bunch of
+ * one kind. -LM- */
+ case TV_ROD:
+ case TV_WAND:
+ case TV_STAFF:
+ {
+ if (cost < 1601L) size += mass_roll(1, 5);
+ else if (cost < 3201L) size += mass_roll(1, 3);
+ break;
+ }
+ }
+
+
+ /* Pick a discount */
+ if (cost < 5)
+ {
+ discount = 0;
+ }
+ else if (rand_int(25) == 0)
+ {
+ discount = 25;
+ }
+ else if (rand_int(150) == 0)
+ {
+ discount = 50;
+ }
+ else if (rand_int(300) == 0)
+ {
+ discount = 75;
+ }
+ else if (rand_int(500) == 0)
+ {
+ discount = 90;
+ }
+
+
+ if (o_ptr->art_name)
+ {
+ if (cheat_peek && discount)
+ {
+ msg_print("No discount on random artifacts.");
+ }
+ discount = 0;
+ }
+
+ /* Save the discount */
+ o_ptr->discount = discount;
+
+ /* Save the total pile size */
+ o_ptr->number = size - (size * discount / 100);
+}
+
+
+
+
+
+
+
+
+/*
+ * Determine if a store item can "absorb" another item
+ *
+ * See "object_similar()" for the same function for the "player"
+ */
+static bool store_object_similar(object_type *o_ptr, object_type *j_ptr)
+{
+ /* Hack -- Identical items cannot be stacked */
+ if (o_ptr == j_ptr) return (0);
+
+ /* Different objects cannot be stacked */
+ if (o_ptr->k_idx != j_ptr->k_idx) return (0);
+
+ /* Different charges (etc) cannot be stacked, unless wands or rods. */
+ if ((o_ptr->pval != j_ptr->pval) && (o_ptr->tval != TV_WAND)) return (0);
+
+ /* Require many identical values */
+ if (o_ptr->pval2 != j_ptr->pval2) return (0);
+ if (o_ptr->pval3 != j_ptr->pval3) return (0);
+
+ /* Require many identical values */
+ if (o_ptr->to_h != j_ptr->to_h) return (0);
+ if (o_ptr->to_d != j_ptr->to_d) return (0);
+ if (o_ptr->to_a != j_ptr->to_a) return (0);
+
+ /* Require identical "artifact" names */
+ if (o_ptr->name1 != j_ptr->name1) return (0);
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2 != j_ptr->name2) return (0);
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2b != j_ptr->name2b) return (0);
+
+ /* Random artifacts don't stack !*/
+ if (o_ptr->art_name || j_ptr->art_name) return (0);
+
+ /* Hack -- Identical art_flags! */
+ if ((o_ptr->art_flags1 != j_ptr->art_flags1) ||
+ (o_ptr->art_flags2 != j_ptr->art_flags2) ||
+ (o_ptr->art_flags3 != j_ptr->art_flags3))
+ return (0);
+
+ /* Hack -- Never stack "powerful" items */
+ if (o_ptr->xtra1 || j_ptr->xtra1) return (0);
+
+ if (o_ptr->tval == TV_LITE)
+ {
+ /* Require identical "turns of light" */
+ if (o_ptr->timeout != j_ptr->timeout) return (0);
+ }
+ else
+ {
+ /* Hack -- Never stack recharging items */
+ if (o_ptr->timeout || j_ptr->timeout) return (0);
+ }
+
+ /* Require many identical values */
+ if (o_ptr->ac != j_ptr->ac) return (0);
+ if (o_ptr->dd != j_ptr->dd) return (0);
+ if (o_ptr->ds != j_ptr->ds) return (0);
+
+ /* Hack -- Never stack chests */
+ if (o_ptr->tval == TV_CHEST) return (0);
+
+ /* Require matching discounts */
+ if (o_ptr->discount != j_ptr->discount) return (0);
+
+ /* They match, so they must be similar */
+ return (TRUE);
+}
+
+
+/*
+ * Allow a store item to absorb another item
+ */
+static void store_object_absorb(object_type *o_ptr, object_type *j_ptr)
+{
+ int total = o_ptr->number + j_ptr->number;
+
+ /* Combine quantity, lose excess items */
+ o_ptr->number = (total > 99) ? 99 : total;
+
+ /* Hack -- if wands are stacking, combine the charges. -LM- */
+ if (o_ptr->tval == TV_WAND)
+ {
+ o_ptr->pval += j_ptr->pval;
+ }
+}
+
+
+/*
+ * Check to see if the shop will be carrying too many objects -RAK-
+ * Note that the shop, just like a player, will not accept things
+ * it cannot hold. Before, one could "nuke" potions this way.
+ */
+static bool store_check_num(object_type *o_ptr)
+{
+ int i;
+ object_type *j_ptr;
+
+ /* Free space is always usable */
+ if (st_ptr->stock_num < st_ptr->stock_size) return TRUE;
+
+ /* The "home" acts like the player */
+ if ((cur_store_num == 7) ||
+ (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM))
+ {
+ /* Check all the items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[i];
+
+ /* Can the new object be combined with the old one? */
+ if (object_similar(j_ptr, o_ptr)) return (TRUE);
+ }
+ }
+
+ /* Normal stores do special stuff */
+ else
+ {
+ /* Check all the items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[i];
+
+ /* Can the new object be combined with the old one? */
+ if (store_object_similar(j_ptr, o_ptr)) return (TRUE);
+ }
+ }
+
+ /* But there was no room at the inn... */
+ return (FALSE);
+}
+
+
+bool is_blessed(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags_known(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & TR3_BLESSED) return (TRUE);
+ else return (FALSE);
+}
+
+
+
+/*
+ * Determine if the current store will purchase the given item
+ *
+ * Note that a shop-keeper must refuse to buy "worthless" items
+ */
+static bool store_will_buy(object_type *o_ptr)
+{
+ /* Hack -- The Home is simple */
+ if (cur_store_num == 7) return (TRUE);
+
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) return TRUE;
+
+ /* XXX XXX XXX Ignore "worthless" items */
+ if (object_value(o_ptr) <= 0) return (FALSE);
+
+ /* Lua can define things to buy */
+ if (process_hooks_ret(HOOK_STORE_BUY, "d", "(d,s,O)", st_ptr->st_idx, st_info[st_ptr->st_idx].name + st_name, o_ptr))
+ {
+ return process_hooks_return[0].num;
+ }
+
+ /* Assume not okay */
+ return (FALSE);
+}
+
+
+
+/*
+ * Add the item "o_ptr" to the inventory of the "Home"
+ *
+ * In all cases, return the slot (or -1) where the object was placed
+ *
+ * Note that this is a hacked up version of "inven_carry()".
+ *
+ * Also note that it may not correctly "adapt" to "knowledge" bacoming
+ * known, the player may have to pick stuff up and drop it again.
+ */
+static int home_carry(object_type *o_ptr)
+{
+ int slot;
+ s32b value, j_value;
+ int i;
+ object_type *j_ptr;
+
+
+ /* Check each existing item (try to combine) */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* The home acts just like the player */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Save the new number of items */
+ object_absorb(j_ptr, o_ptr);
+
+ /* All done */
+ return (slot);
+ }
+ }
+
+ /* No space? */
+ if (st_ptr->stock_num >= st_ptr->stock_size) return ( -1);
+
+
+ /* Determine the "value" of the item */
+ value = object_value(o_ptr);
+
+ /* Check existing slots to see if we must "slide" */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get that item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Can happen in the home */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Objects in the home can be unknown */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+
+ /*
+ * Hack: otherwise identical rods sort by
+ * increasing recharge time --dsb
+ */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout < j_ptr->timeout) break;
+ if (o_ptr->timeout > j_ptr->timeout) continue;
+ }
+
+ /* Objects sort by decreasing value */
+ j_value = object_value(j_ptr);
+ if (value > j_value) break;
+ if (value < j_value) continue;
+ }
+
+ /* Slide the others up */
+ for (i = st_ptr->stock_num; i > slot; i--)
+ {
+ st_ptr->stock[i] = st_ptr->stock[i - 1];
+ }
+
+ /* More stuff now */
+ st_ptr->stock_num++;
+
+ /* Insert the new item */
+ st_ptr->stock[slot] = *o_ptr;
+
+ /* Return the location */
+ return (slot);
+}
+
+
+/*
+ * Add the item "o_ptr" to a real stores inventory.
+ *
+ * If the item is "worthless", it is thrown away (except in the home).
+ *
+ * If the item cannot be combined with an object already in the inventory,
+ * make a new slot for it, and calculate its "per item" price. Note that
+ * this price will be negative, since the price will not be "fixed" yet.
+ * Adding an item to a "fixed" price stack will not change the fixed price.
+ *
+ * In all cases, return the slot (or -1) where the object was placed
+ */
+static int store_carry(object_type *o_ptr)
+{
+ int i, slot;
+ s32b value, j_value;
+ object_type *j_ptr;
+
+
+ /* Evaluate the object */
+ value = object_value(o_ptr);
+
+ /* Cursed/Worthless items "disappear" when sold */
+ if (value <= 0) return ( -1);
+
+ /* All store items are fully *identified* */
+ o_ptr->ident |= IDENT_MENTAL;
+
+ /* Erase the inscription */
+ o_ptr->note = 0;
+
+ /* Check each existing item (try to combine) */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get the existing item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* Can the existing items be incremented? */
+ if (store_object_similar(j_ptr, o_ptr))
+ {
+ /* Hack -- extra items disappear */
+ store_object_absorb(j_ptr, o_ptr);
+
+ /* All done */
+ return (slot);
+ }
+ }
+
+ /* No space? */
+ if (st_ptr->stock_num >= st_ptr->stock_size) return ( -1);
+
+
+ /* Check existing slots to see if we must "slide" */
+ for (slot = 0; slot < st_ptr->stock_num; slot++)
+ {
+ /* Get that item */
+ j_ptr = &st_ptr->stock[slot];
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+
+ /*
+ * Hack: otherwise identical rods sort by
+ * increasing recharge time --dsb
+ */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout < j_ptr->timeout) break;
+ if (o_ptr->timeout > j_ptr->timeout) continue;
+ }
+
+ /* Evaluate that slot */
+ j_value = object_value(j_ptr);
+
+ /* Objects sort by decreasing value */
+ if (value > j_value) break;
+ if (value < j_value) continue;
+ }
+
+ /* Slide the others up */
+ for (i = st_ptr->stock_num; i > slot; i--)
+ {
+ st_ptr->stock[i] = st_ptr->stock[i - 1];
+ }
+
+ /* More stuff now */
+ st_ptr->stock_num++;
+
+ /* Insert the new item */
+ st_ptr->stock[slot] = *o_ptr;
+
+ /* Return the location */
+ return (slot);
+}
+
+
+/*
+ * Increase, by a given amount, the number of a certain item
+ * in a certain store. This can result in zero items.
+ */
+static void store_item_increase(int item, int num)
+{
+ int cnt;
+ object_type *o_ptr;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Verify the number */
+ cnt = o_ptr->number + num;
+ if (cnt > 255) cnt = 255;
+ else if (cnt < 0) cnt = 0;
+ num = cnt - o_ptr->number;
+
+ /* Save the new number */
+ o_ptr->number += num;
+}
+
+
+/*
+ * Remove a slot if it is empty
+ */
+static void store_item_optimize(int item)
+{
+ int j;
+ object_type *o_ptr;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Must exist */
+ if (!o_ptr->k_idx) return;
+
+ /* Must have no items */
+ if (o_ptr->number) return;
+
+ /* One less item */
+ st_ptr->stock_num--;
+
+ /* Slide everyone */
+ for (j = item; j < st_ptr->stock_num; j++)
+ {
+ st_ptr->stock[j] = st_ptr->stock[j + 1];
+ }
+
+ /* Nuke the final slot */
+ object_wipe(&st_ptr->stock[j]);
+}
+
+
+/*
+ * This function will keep 'crap' out of the black market.
+ * Crap is defined as any item that is "available" elsewhere
+ * Based on a suggestion by "Lee Vogt" <lvogt@cig.mcel.mot.com>
+ */
+static bool black_market_crap(object_type *o_ptr)
+{
+ int i, j;
+
+ /* Ego items are never crap */
+ if (o_ptr->name2) return (FALSE);
+
+ /* Good items are never crap */
+ if (o_ptr->to_a > 0) return (FALSE);
+ if (o_ptr->to_h > 0) return (FALSE);
+ if (o_ptr->to_d > 0) return (FALSE);
+
+ /* Check all stores */
+ for (i = 0; i < max_st_idx; i++)
+ {
+ if (i == STORE_HOME) continue;
+ if (st_info[i].flags1 & SF1_MUSEUM) continue;
+
+ /* Check every item in the store */
+ for (j = 0; j < town_info[p_ptr->town_num].store[i].stock_num; j++)
+ {
+ object_type *j_ptr = &town_info[p_ptr->town_num].store[i].stock[j];
+
+ /* Duplicate item "type", assume crappy */
+ if (o_ptr->k_idx == j_ptr->k_idx) return (TRUE);
+ }
+ }
+
+ /* Assume okay */
+ return (FALSE);
+}
+
+
+/*
+ * Attempt to delete (some of) a random item from the store
+ * Hack -- we attempt to "maintain" piles of items when possible.
+ */
+static void store_delete(void)
+{
+ int what, num;
+
+ /* Pick a random slot */
+ what = rand_int(st_ptr->stock_num);
+
+ /* Determine how many items are here */
+ num = st_ptr->stock[what].number;
+
+ /* Hack -- sometimes, only destroy half the items */
+ if (rand_int(100) < 50) num = (num + 1) / 2;
+
+ /* Hack -- sometimes, only destroy a single item */
+ if (rand_int(100) < 50) num = 1;
+
+ /* Hack -- decrement the maximum timeouts and total charges of rods and wands. -LM- */
+ if (st_ptr->stock[what].tval == TV_WAND)
+ {
+ st_ptr->stock[what].pval -= num * st_ptr->stock[what].pval / st_ptr->stock[what].number;
+ }
+
+ /* Actually destroy (part of) the item */
+ store_item_increase(what, -num);
+ store_item_optimize(what);
+}
+
+/* Analyze store flags and return a level */
+int return_level()
+{
+ store_info_type *sti_ptr = &st_info[st_ptr->st_idx];
+ int level;
+
+ if (sti_ptr->flags1 & SF1_RANDOM) level = 0;
+ else level = rand_range(1, STORE_OBJ_LEVEL);
+
+ if (sti_ptr->flags1 & SF1_DEPEND_LEVEL) level += dun_level;
+
+ if (sti_ptr->flags1 & SF1_SHALLOW_LEVEL) level += 5 + rand_int(5);
+ if (sti_ptr->flags1 & SF1_MEDIUM_LEVEL) level += 25 + rand_int(25);
+ if (sti_ptr->flags1 & SF1_DEEP_LEVEL) level += 45 + rand_int(45);
+
+ if (sti_ptr->flags1 & SF1_ALL_ITEM) level += p_ptr->lev;
+
+ return (level);
+}
+
+/* Is it an ok object ? */
+static int store_tval = 0, store_level = 0;
+
+/*
+ * Hack -- determine if a template is "good"
+ */
+static bool kind_is_storeok(int k_idx)
+{
+ object_kind *k_ptr = &k_info[k_idx];
+
+ if (k_info[k_idx].flags3 & TR3_NORM_ART)
+ return ( FALSE );
+
+ if (k_info[k_idx].flags3 & TR3_INSTA_ART)
+ return ( FALSE );
+
+ if (!kind_is_legal(k_idx)) return FALSE;
+
+ if (k_ptr->tval != store_tval) return (FALSE);
+ if (k_ptr->level < (store_level / 2)) return (FALSE);
+
+ return (TRUE);
+}
+
+/*
+ * Creates a random item and gives it to a store
+ * This algorithm needs to be rethought. A lot.
+ *
+ * Note -- the "level" given to "obj_get_num()" is a "favored"
+ * level, that is, there is a much higher chance of getting
+ * items with a level approaching that of the given level...
+ *
+ * Should we check for "permission" to have the given item?
+ */
+static void store_create(void)
+{
+ int i = 0, tries, level = 0, chance, item;
+
+ object_type forge;
+ object_type *q_ptr = NULL;
+ bool obj_all_done = FALSE;
+
+
+ /* Paranoia -- no room left */
+ if (st_ptr->stock_num >= st_ptr->stock_size) return;
+
+
+ /* Hack -- consider up to four items */
+ for (tries = 0; tries < 4; tries++)
+ {
+ obj_all_done = FALSE;
+
+ /* Lua can define things to buy */
+ if (process_hooks_ret(HOOK_STORE_STOCK, "O", "(d,s,d)", st_ptr->st_idx, st_info[st_ptr->st_idx].name + st_name, return_level()))
+ {
+ obj_all_done = TRUE;
+ q_ptr = process_hooks_return[0].o_ptr;
+ }
+
+ /* Black Market */
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM)
+ {
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /*
+ * Even in Black Markets, illegal objects can be
+ * problematic -- Oxymoron?
+ */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild the allocation table */
+ get_obj_num_prep();
+
+ /* Pick a level for object/magic */
+ level = return_level();
+
+ /* Random item (usually of given level) */
+ i = get_obj_num(level);
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ /* Handle failure */
+ if (!i) continue;
+
+ }
+
+ /* Normal Store */
+ else
+ {
+ /* Hack -- Pick an item to sell */
+ item = rand_int(st_info[st_ptr->st_idx].table_num);
+ i = st_info[st_ptr->st_idx].table[item][0];
+ chance = st_info[st_ptr->st_idx].table[item][1];
+
+ /* Don't allow k_info artifacts */
+ if ((i <= 10000) && (k_info[i].flags3 & TR3_NORM_ART))
+ continue;
+
+ /* Does it passes the rarity check ? */
+ if (!magik(chance)) continue;
+
+ /* Hack -- fake level for apply_magic() */
+ level = return_level();
+
+ /* Hack -- i > 10000 means it's a tval and all svals are allowed */
+ if (i > 10000)
+ {
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /* Activate restriction */
+ get_obj_num_hook = kind_is_storeok;
+ store_tval = i - 10000;
+
+ /* Do we forbid too shallow items ? */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_FORCE_LEVEL) store_level = level;
+ else store_level = 0;
+
+ /* Prepare allocation table */
+ get_obj_num_prep();
+
+ /* Get it ! */
+ i = get_obj_num(level);
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+ }
+
+ if (!i) continue;
+ }
+
+ /* Only if not already done */
+ if (!obj_all_done)
+ {
+ /* Don't allow k_info artifacts */
+ if (k_info[i].flags3 & TR3_NORM_ART)
+ continue;
+
+ /* Don't allow artifacts */
+ if (k_info[i].flags3 & TR3_INSTA_ART)
+ continue;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create a new object of the chosen kind */
+ object_prep(q_ptr, i);
+
+ /* Apply some "low-level" magic (no artifacts) */
+ apply_magic(q_ptr, level, FALSE, FALSE, FALSE);
+
+ /* Hack -- Charge lite's */
+ if (q_ptr->tval == TV_LITE)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FUEL_LITE) q_ptr->timeout = k_info[q_ptr->k_idx].pval2;
+ }
+
+ }
+
+ /* The item is "known" */
+ object_known(q_ptr);
+
+ /* Mark it storebought */
+ q_ptr->ident |= IDENT_STOREB;
+
+ /* Mega-Hack -- no chests in stores */
+ if (q_ptr->tval == TV_CHEST) continue;
+
+ /* Prune the black market */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM)
+ {
+ /* Hack -- No "crappy" items */
+ if (black_market_crap(q_ptr)) continue;
+
+ /* Hack -- No "cheap" items */
+ if (object_value(q_ptr) < 10) continue;
+ }
+
+ /* Prune normal stores */
+ else
+ {
+ /* No "worthless" items */
+ if (object_value(q_ptr) <= 0) continue;
+ }
+
+
+ /* Mass produce and/or Apply discount */
+ mass_produce(q_ptr);
+
+ /* The charges an wands are per each, so multiply to get correct number */
+ if (!obj_all_done && q_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval *= q_ptr->number;
+ }
+
+ /* Attempt to carry the (known) item */
+ (void)store_carry(q_ptr);
+
+ /* Definitely done */
+ break;
+ }
+}
+
+
+
+/*
+ * Eliminate need to bargain if player has haggled well in the past
+ */
+static bool noneedtobargain(s32b minprice)
+{
+ s32b good = st_ptr->good_buy;
+ s32b bad = st_ptr->bad_buy;
+
+ /* Cheap items are "boring" */
+ if (minprice < 10L) return (TRUE);
+
+ /* Perfect haggling */
+ if (good == MAX_SHORT) return (TRUE);
+
+ /* Reward good haggles, punish bad haggles, notice price */
+ if (good > ((3 * bad) + (5 + (minprice / 50)))) return (TRUE);
+
+ /* Return the flag */
+ return (FALSE);
+}
+
+
+/*
+ * Update the bargain info
+ */
+static void updatebargain(s32b price, s32b minprice)
+{
+ /* Hack -- auto-haggle */
+ if (auto_haggle) return;
+
+ /* Cheap items are "boring" */
+ if (minprice < 10L) return;
+
+ /* Count the successful haggles */
+ if (price == minprice)
+ {
+ /* Just count the good haggles */
+ if (st_ptr->good_buy < MAX_SHORT)
+ {
+ st_ptr->good_buy++;
+ }
+ }
+
+ /* Count the failed haggles */
+ else
+ {
+ /* Just count the bad haggles */
+ if (st_ptr->bad_buy < MAX_SHORT)
+ {
+ st_ptr->bad_buy++;
+ }
+ }
+}
+
+
+
+/*
+ * Re-displays a single store entry
+ */
+static void display_entry(int pos)
+{
+ int i, cur_col;
+ object_type *o_ptr;
+ s32b x;
+
+ char o_name[80];
+ char out_val[160];
+
+
+ int maxwid = 75;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[pos];
+
+ /* Get the "offset" */
+ i = (pos % 12);
+
+ /* Label it, clear the line --(-- */
+ strnfmt(out_val, 160, "%c) ", I2A(i));
+ c_prt(get_item_letter_color(o_ptr), out_val, i + 6, 0);
+
+
+ cur_col = 3;
+ if (show_store_graph)
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+#ifdef AMIGA
+ if (a & 0x80)
+ a |= 0x40;
+#endif
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(cur_col, i + 6, a, c);
+ if (use_bigtile)
+ {
+ cur_col++;
+ if (a & 0x80)
+ Term_draw(cur_col, i + 6, 255, 255);
+ }
+ cur_col += 2;
+ }
+
+ /* Describe an item in the home */
+ if ((cur_store_num == 7) ||
+ (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM))
+ {
+ maxwid = 75;
+
+ /* Leave room for weights, if necessary -DRS- */
+ if (show_weights) maxwid -= 10;
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_name[maxwid] = '\0';
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, i + 6, cur_col);
+
+ /* Show weights */
+ if (show_weights)
+ {
+ /* Only show the weight of an individual item */
+ int wgt = o_ptr->weight;
+ strnfmt(out_val, 160, "%3d.%d lb", wgt / 10, wgt % 10);
+ put_str(out_val, i + 6, 68);
+ }
+ }
+
+ /* Describe an item (fully) in a store */
+ else
+ {
+ byte color = TERM_WHITE;
+
+ /* Must leave room for the "price" */
+ maxwid = 65;
+
+ /* Leave room for weights, if necessary -DRS- */
+ if (show_weights) maxwid -= 7;
+
+ /* Describe the object (fully) */
+ object_desc_store(o_name, o_ptr, TRUE, 3);
+ o_name[maxwid] = '\0';
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, i + 6, cur_col);
+
+ /* Show weights */
+ if (show_weights)
+ {
+ /* Only show the weight of an individual item */
+ int wgt = o_ptr->weight;
+ strnfmt(out_val, 160, "%3d.%d", wgt / 10, wgt % 10);
+ put_str(out_val, i + 6, 61);
+ }
+
+ /* Display a "fixed" cost */
+ if (o_ptr->ident & (IDENT_FIXED))
+ {
+ /* Extract the "minimum" price */
+ x = price_item(o_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price (not fixed) */
+ strnfmt(out_val, 160, "%9ld F", (long)x);
+ c_put_str(color, out_val, i + 6, 68);
+ }
+
+ /* Display a "taxed" cost */
+ else if (auto_haggle)
+ {
+ /* Extract the "minimum" price */
+ x = price_item(o_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Hack -- Apply Sales Tax if needed */
+ if (!noneedtobargain(x)) x += x / 10;
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price (with tax) */
+ strnfmt(out_val, 160, "%9ld ", (long)x);
+ c_put_str(color, out_val, i + 6, 68);
+ }
+
+ /* Display a "haggle" cost */
+ else
+ {
+ /* Extrect the "maximum" price */
+ x = price_item(o_ptr, ot_ptr->max_inflate, FALSE);
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price (not fixed) */
+ strnfmt(out_val, 160, "%9ld ", (long)x);
+ c_put_str(color, out_val, i + 6, 68);
+ }
+ }
+}
+
+
+/*
+ * Displays a store's inventory -RAK-
+ * All prices are listed as "per individual object". -BEN-
+ */
+static void display_inventory(void)
+{
+ int i, k;
+
+ /* Display the next 12 items */
+ for (k = 0; k < 12; k++)
+ {
+ /* Do not display "dead" items */
+ if (store_top + k >= st_ptr->stock_num) break;
+
+ /* Display that line */
+ display_entry(store_top + k);
+ }
+
+ /* Erase the extra lines and the "more" prompt */
+ for (i = k; i < 13; i++) prt("", i + 6, 0);
+
+ /* Assume "no current page" */
+ put_str(" ", 5, 20);
+
+ /* Visual reminder of "more items" */
+ if (st_ptr->stock_num > 12)
+ {
+ /* Show "more" reminder (after the last item) */
+ prt("-more-", k + 6, 3);
+
+ /* Indicate the "current page" */
+ put_str(format("(Page %d) ", store_top / 12 + 1), 5, 20);
+ }
+}
+
+
+/*
+ * Displays players gold -RAK-
+ */
+void store_prt_gold(void)
+{
+ char out_val[64];
+
+ prt("Gold Remaining: ", 19, 53);
+
+ strnfmt(out_val, 64, "%9ld", (long)p_ptr->au);
+ prt(out_val, 19, 68);
+}
+
+
+/*
+ * Displays store (after clearing screen) -RAK-
+ */
+void display_store(void)
+{
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* The "Home" is special */
+ if (cur_store_num == 7)
+ {
+ /* Put the owner name -- mega hack */
+#if 0 /* DGDGDGDG -- use a skill */
+ if ((cp_ptr->magic_key == MKEY_TELEKINESIS) &&
+ (p_ptr->town_num == TOWN_RANDOM)) put_str("Hole Contents", 3, 30);
+ else
+#endif
+ put_str("Your Home", 3, 30);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ if (show_weights)
+ {
+ put_str("Weight", 5, 70);
+ }
+ }
+
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM)
+ {
+ cptr store_name = (st_name + st_info[cur_store_num].name);
+
+ /* Show the name of the store */
+ strnfmt(buf, 80, "%s", store_name);
+ prt(buf, 3, 30);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ if (show_weights)
+ {
+ put_str("Weight", 5, 70);
+ }
+ }
+
+ /* Normal stores */
+ else
+ {
+ cptr store_name = (st_name + st_info[cur_store_num].name);
+ cptr owner_name = (ow_name + ot_ptr->name);
+
+ /* Put the owner name and race */
+ strnfmt(buf, 80, "%s", owner_name);
+ put_str(buf, 3, 10);
+
+ /* Show the max price in the store (above prices) */
+ strnfmt(buf, 80, "%s (%ld)", store_name, (long)(ot_ptr->max_cost));
+ prt(buf, 3, 50);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ if (show_weights)
+ {
+ put_str("Weight", 5, 60);
+ }
+
+ /* Label the asking price (in stores) */
+ put_str("Price", 5, 72);
+ }
+
+ /* Display the current gold */
+ store_prt_gold();
+
+ /* Draw in the inventory */
+ display_inventory();
+}
+
+
+
+/*
+ * Get the ID of a store item and return its value -RAK-
+ */
+static int get_stock(int *com_val, cptr pmt, int i, int j)
+{
+ char command;
+
+ char out_val[160];
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Get the item index */
+ if (repeat_pull(com_val))
+ {
+
+ /* Verify the item */
+ if ((*com_val >= i) && (*com_val <= j))
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Assume failure */
+ *com_val = ( -1);
+
+ /* Build the prompt */
+ strnfmt(out_val, 160, "(Items %c-%c, ESC to exit) %s",
+ I2A(i), I2A(j), pmt);
+
+ /* Ask until done */
+ while (TRUE)
+ {
+ int k;
+
+ /* Escape */
+ if (!get_com(out_val, &command)) break;
+
+ /* Convert */
+ k = (islower(command) ? A2I(command) : -1);
+
+ /* Legal responses */
+ if ((k >= i) && (k <= j))
+ {
+ *com_val = k;
+ break;
+ }
+
+ /* Oops */
+ bell();
+ }
+
+ /* Clear the prompt */
+ prt("", 0, 0);
+
+ /* Cancel */
+ if (command == ESCAPE) return (FALSE);
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ repeat_push(*com_val);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Increase the insult counter and get angry if too many -RAK-
+ */
+static int increase_insults(void)
+{
+ /* Increase insults */
+ st_ptr->insult_cur++;
+
+ /* Become insulted */
+ if (st_ptr->insult_cur > ot_ptr->insult_max)
+ {
+ /* Complain */
+ say_comment_4();
+
+ /* Reset insults */
+ st_ptr->insult_cur = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+ /* Open tomorrow */
+ st_ptr->store_open = turn + 25000 + randint(25000);
+
+ /* Closed */
+ return (TRUE);
+ }
+
+ /* Not closed */
+ return (FALSE);
+}
+
+
+/*
+ * Decrease insults -RAK-
+ */
+static void decrease_insults(void)
+{
+ /* Decrease insults */
+ if (st_ptr->insult_cur) st_ptr->insult_cur--;
+}
+
+
+/*
+ * Have insulted while haggling -RAK-
+ */
+static int haggle_insults(void)
+{
+ /* Increase insults */
+ if (increase_insults()) return (TRUE);
+
+ /* Display and flush insult */
+ say_comment_5();
+
+ /* Still okay */
+ return (FALSE);
+}
+
+
+/*
+ * Mega-Hack -- Enable "increments"
+ */
+static bool allow_inc = FALSE;
+
+/*
+ * Mega-Hack -- Last "increment" during haggling
+ */
+static s32b last_inc = 0L;
+
+
+/*
+ * Get a haggle
+ */
+static int get_haggle(cptr pmt, s32b *poffer, s32b price, int final)
+{
+ s32b i;
+
+ cptr p;
+
+ char buf[128];
+ char out_val[160];
+
+
+ /* Clear old increment if necessary */
+ if (!allow_inc) last_inc = 0L;
+
+
+ /* Final offer */
+ if (final)
+ {
+ strnfmt(buf, 128, "%s [accept] ", pmt);
+ }
+
+ /* Old (negative) increment, and not final */
+ else if (last_inc < 0)
+ {
+ strnfmt(buf, 128, "%s [-%ld] ", pmt, (long)(ABS(last_inc)));
+ }
+
+ /* Old (positive) increment, and not final */
+ else if (last_inc > 0)
+ {
+ strnfmt(buf, 128, "%s [+%ld] ", pmt, (long)(ABS(last_inc)));
+ }
+
+ /* Normal haggle */
+ else
+ {
+ strnfmt(buf, 128, "%s ", pmt);
+ }
+
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Ask until done */
+ while (TRUE)
+ {
+ /* Default */
+ strcpy(out_val, "");
+
+ /* Ask the user for a response */
+ if (!get_string(buf, out_val, 32)) return (FALSE);
+
+ /* Skip leading spaces */
+ for (p = out_val; *p == ' '; p++) /* loop */;
+
+ /* Empty response */
+ if (*p == '\0')
+ {
+ /* Accept current price */
+ if (final)
+ {
+ *poffer = price;
+ last_inc = 0L;
+ break;
+ }
+
+ /* Use previous increment */
+ if (allow_inc && last_inc)
+ {
+ *poffer += last_inc;
+ break;
+ }
+ }
+
+ /* Normal response */
+ else
+ {
+ /* Extract a number */
+ i = atol(p);
+
+ /* Handle "incremental" number */
+ if ((*p == '+' || *p == '-'))
+ {
+ /* Allow increments */
+ if (allow_inc)
+ {
+ /* Use the given "increment" */
+ *poffer += i;
+ last_inc = i;
+ break;
+ }
+ }
+
+ /* Handle normal number */
+ else
+ {
+ /* Use the given "number" */
+ *poffer = i;
+ last_inc = 0L;
+ break;
+ }
+ }
+
+ /* Warning */
+ msg_print("Invalid response.");
+ msg_print(NULL);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Receive an offer (from the player)
+ *
+ * Return TRUE if offer is NOT okay
+ */
+static bool receive_offer(cptr pmt, s32b *poffer,
+ s32b last_offer, int factor,
+ s32b price, int final)
+{
+ /* Haggle till done */
+ while (TRUE)
+ {
+ /* Get a haggle (or cancel) */
+ if (!get_haggle(pmt, poffer, price, final)) return (TRUE);
+
+ /* Acceptable offer */
+ if (((*poffer) * factor) >= (last_offer * factor)) break;
+
+ /* Insult, and check for kicked out */
+ if (haggle_insults()) return (TRUE);
+
+ /* Reject offer (correctly) */
+ (*poffer) = last_offer;
+ }
+
+ /* Success */
+ return (FALSE);
+}
+
+
+/*
+ * Haggling routine -RAK-
+ *
+ * Return TRUE if purchase is NOT successful
+ */
+static bool purchase_haggle(object_type *o_ptr, s32b *price)
+{
+ s32b cur_ask, final_ask;
+ s32b last_offer, offer;
+ s32b x1, x2, x3;
+ s32b min_per, max_per;
+ int flag, loop_flag, noneed;
+ int annoyed = 0, final = FALSE;
+
+ bool cancel = FALSE;
+
+ cptr pmt = "Asking";
+
+ char out_val[160];
+
+
+ *price = 0;
+
+
+ /* Extract the starting offer and the final offer */
+ cur_ask = price_item(o_ptr, ot_ptr->max_inflate, FALSE);
+ final_ask = price_item(o_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Determine if haggling is necessary */
+ noneed = noneedtobargain(final_ask);
+
+ /* No need to haggle */
+ if (noneed || auto_haggle)
+ {
+ /* No need to haggle */
+ if (noneed)
+ {
+ /* Message summary */
+ msg_print("You eventually agree upon the price.");
+ msg_print(NULL);
+ }
+
+ /* No haggle option */
+ else
+ {
+ /* Message summary */
+ msg_print("You quickly agree upon the price.");
+ msg_print(NULL);
+
+ /* Apply Sales Tax */
+ final_ask += final_ask / 10;
+ }
+
+ /* Final price */
+ cur_ask = final_ask;
+
+ /* Go to final offer */
+ pmt = "Final Offer";
+ final = TRUE;
+ }
+
+
+ /* Haggle for the whole pile */
+ cur_ask *= o_ptr->number;
+ final_ask *= o_ptr->number;
+
+
+ /* Haggle parameters */
+ min_per = ot_ptr->haggle_per;
+ max_per = min_per * 3;
+
+ /* Mega-Hack -- artificial "last offer" value */
+ last_offer = object_value(o_ptr) * o_ptr->number;
+ last_offer = last_offer * (200 - (int)(ot_ptr->max_inflate)) / 100L;
+ if (last_offer <= 0) last_offer = 1;
+
+ /* No offer yet */
+ offer = 0;
+
+ /* No incremental haggling yet */
+ allow_inc = FALSE;
+
+ /* Haggle until done */
+ for (flag = FALSE; !flag; )
+ {
+ loop_flag = TRUE;
+
+ while (!flag && loop_flag)
+ {
+ strnfmt(out_val, 160, "%s : %ld", pmt, (long)cur_ask);
+ put_str(out_val, 1, 0);
+ cancel = receive_offer("What do you offer? ",
+ &offer, last_offer, 1, cur_ask, final);
+
+ if (cancel)
+ {
+ flag = TRUE;
+ }
+ else if (offer > cur_ask)
+ {
+ say_comment_6();
+ offer = last_offer;
+ }
+ else if (offer == cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+ else
+ {
+ loop_flag = FALSE;
+ }
+ }
+
+ if (!flag)
+ {
+ x1 = 100 * (offer - last_offer) / (cur_ask - last_offer);
+ if (x1 < min_per)
+ {
+ if (haggle_insults())
+ {
+ flag = TRUE;
+ cancel = TRUE;
+ }
+ }
+ else if (x1 > max_per)
+ {
+ x1 = x1 * 3 / 4;
+ if (x1 < max_per) x1 = max_per;
+ }
+ x2 = rand_range(x1 - 2, x1 + 2);
+ x3 = ((cur_ask - offer) * x2 / 100L) + 1;
+ /* don't let the price go up */
+ if (x3 < 0) x3 = 0;
+ cur_ask -= x3;
+
+ /* Too little */
+ if (cur_ask < final_ask)
+ {
+ final = TRUE;
+ cur_ask = final_ask;
+ pmt = "Final Offer";
+ annoyed++;
+ if (annoyed > 3)
+ {
+ (void)(increase_insults());
+ cancel = TRUE;
+ flag = TRUE;
+ }
+ }
+ else if (offer >= cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+
+ if (!flag)
+ {
+ last_offer = offer;
+ allow_inc = TRUE;
+ prt("", 1, 0);
+ strnfmt(out_val, 160, "Your last offer: %ld",
+ (long)last_offer);
+ put_str(out_val, 1, 39);
+ say_comment_2(cur_ask, annoyed);
+ }
+ }
+ }
+
+ /* Cancel */
+ if (cancel) return (TRUE);
+
+ /* Update bargaining info */
+ updatebargain(*price, final_ask);
+
+ /* Do not cancel */
+ return (FALSE);
+}
+
+
+/*
+ * Haggling routine -RAK-
+ *
+ * Return TRUE if purchase is NOT successful
+ */
+static bool sell_haggle(object_type *o_ptr, s32b *price)
+{
+ s32b purse, cur_ask, final_ask;
+ s32b last_offer = 0, offer = 0;
+ s32b x1, x2, x3;
+ s32b min_per, max_per;
+
+ int flag, loop_flag, noneed;
+ int annoyed = 0, final = FALSE;
+
+ bool cancel = FALSE;
+
+ cptr pmt = "Offer";
+
+ char out_val[160];
+
+
+ *price = 0;
+
+
+ /* Obtain the starting offer and the final offer */
+ cur_ask = price_item(o_ptr, ot_ptr->max_inflate, TRUE);
+ final_ask = price_item(o_ptr, ot_ptr->min_inflate, TRUE);
+
+ /* Determine if haggling is necessary */
+ noneed = noneedtobargain(final_ask);
+
+ /* Get the owner's payout limit */
+ purse = (s32b)(ot_ptr->max_cost);
+
+ /* No need to haggle */
+ if (noneed || auto_haggle || (final_ask >= purse))
+ {
+ /* No reason to haggle */
+ if (final_ask >= purse)
+ {
+ /* Message */
+ msg_print("You instantly agree upon the price.");
+ msg_print(NULL);
+
+ /* Offer full purse */
+ final_ask = purse;
+ }
+
+ /* No need to haggle */
+ else if (noneed)
+ {
+ /* Message */
+ msg_print("You eventually agree upon the price.");
+ msg_print(NULL);
+ }
+
+ /* No haggle option */
+ else
+ {
+ /* Message summary */
+ msg_print("You quickly agree upon the price.");
+ msg_print(NULL);
+
+ /* Apply Sales Tax */
+ final_ask -= final_ask / 10;
+ }
+
+ /* Final price */
+ cur_ask = final_ask;
+
+ /* Final offer */
+ final = TRUE;
+ pmt = "Final Offer";
+ }
+
+ /* Haggle for the whole pile */
+ cur_ask *= o_ptr->number;
+ final_ask *= o_ptr->number;
+
+
+ /* XXX XXX XXX Display commands */
+
+ /* Haggling parameters */
+ min_per = ot_ptr->haggle_per;
+ max_per = min_per * 3;
+
+ /* Mega-Hack -- artificial "last offer" value */
+ last_offer = object_value(o_ptr) * o_ptr->number;
+ last_offer = last_offer * ot_ptr->max_inflate / 100L;
+
+ /* No offer yet */
+ offer = 0;
+
+ /* No incremental haggling yet */
+ allow_inc = FALSE;
+
+ /* Haggle */
+ for (flag = FALSE; !flag; )
+ {
+ while (1)
+ {
+ loop_flag = TRUE;
+
+ strnfmt(out_val, 160, "%s : %ld", pmt, (long)cur_ask);
+ put_str(out_val, 1, 0);
+ cancel = receive_offer("What price do you ask? ",
+ &offer, last_offer, -1, cur_ask, final);
+
+ if (cancel)
+ {
+ flag = TRUE;
+ }
+ else if (offer < cur_ask)
+ {
+ say_comment_6();
+ /* rejected, reset offer for incremental haggling */
+ offer = last_offer;
+ }
+ else if (offer == cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+ else
+ {
+ loop_flag = FALSE;
+ }
+
+ /* Stop */
+ if (flag || !loop_flag) break;
+ }
+
+ if (!flag)
+ {
+ x1 = 100 * (last_offer - offer) / (last_offer - cur_ask);
+ if (x1 < min_per)
+ {
+ if (haggle_insults())
+ {
+ flag = TRUE;
+ cancel = TRUE;
+ }
+ }
+ else if (x1 > max_per)
+ {
+ x1 = x1 * 3 / 4;
+ if (x1 < max_per) x1 = max_per;
+ }
+ x2 = rand_range(x1 - 2, x1 + 2);
+ x3 = ((offer - cur_ask) * x2 / 100L) + 1;
+ /* don't let the price go down */
+ if (x3 < 0) x3 = 0;
+ cur_ask += x3;
+
+ if (cur_ask > final_ask)
+ {
+ cur_ask = final_ask;
+ final = TRUE;
+ pmt = "Final Offer";
+ annoyed++;
+ if (annoyed > 3)
+ {
+ flag = TRUE;
+ (void)(increase_insults());
+ }
+ }
+ else if (offer <= cur_ask)
+ {
+ flag = TRUE;
+ *price = offer;
+ }
+
+ if (!flag)
+ {
+ last_offer = offer;
+ allow_inc = TRUE;
+ prt("", 1, 0);
+ strnfmt(out_val, 160,
+ "Your last bid %ld", (long)last_offer);
+ put_str(out_val, 1, 39);
+ say_comment_3(cur_ask, annoyed);
+ }
+ }
+ }
+
+ /* Cancel */
+ if (cancel) return (TRUE);
+
+ /* Update bargaining info */
+ updatebargain(*price, final_ask);
+
+ /* Do not cancel */
+ return (FALSE);
+}
+
+/*
+ * Will the owner retire?
+ */
+static bool retire_owner_p(void)
+{
+ store_info_type *sti_ptr = &st_info[town_info[p_ptr->town_num].store[cur_store_num].st_idx];
+
+ if ((sti_ptr->owners[0] == sti_ptr->owners[1]) &&
+ (sti_ptr->owners[0] == sti_ptr->owners[2]) &&
+ (sti_ptr->owners[0] == sti_ptr->owners[3]))
+ {
+ /* there is no other owner */
+ return FALSE;
+ }
+
+ if (rand_int(STORE_SHUFFLE) != 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Stole an item from a store -DG-
+ */
+void store_stole(void)
+{
+ int i, amt;
+ int item, item_new;
+
+ object_type forge;
+ object_type *j_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ if (cur_store_num == 7)
+ {
+ msg_print("You can't steal from your home!");
+ return;
+ }
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ msg_print("There is no item to steal.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ strnfmt(out_val, 160, "Which item do you want to steal? ");
+
+ /* Get the item number to be bought */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Assume the player wants just one of them */
+ amt = 1;
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = amt;
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many different items.");
+ return;
+ }
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get desired object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = amt;
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many items.");
+ return;
+ }
+
+ /* Player tries to stole it */
+ if (rand_int((40 - p_ptr->stat_ind[A_DEX]) +
+ ((j_ptr->weight * amt) / (5 + get_skill_scale(SKILL_STEALING, 15))) -
+ (get_skill_scale(SKILL_STEALING, 15))) <= 10)
+ {
+ /* Hack -- buying an item makes you aware of it */
+ object_aware(j_ptr);
+
+ /* Be aware of how you found it */
+ j_ptr->found = OBJ_FOUND_STOLEN;
+ j_ptr->found_aux1 = st_ptr->st_idx;
+
+ /* Hack -- clear the "fixed" flag from the item */
+ j_ptr->ident &= ~(IDENT_FIXED);
+
+ /* "Hot" merchandise can't be sold back. It doesn't make sense
+ to be able to sell back to a guy what you just stole from him.
+ Also, without the discount one could fairly easily macro himself
+ an infinite money supply */
+ j_ptr->discount = 100;
+
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Describe the transaction */
+ object_desc(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You steal %s.", o_name);
+
+ /* Erase the inscription */
+ j_ptr->note = 0;
+
+ /* Give it to the player */
+ item_new = inven_carry(j_ptr, FALSE);
+
+ /* Describe the final result */
+ object_desc(o_name, &p_ptr->inventory[item_new], TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).",
+ o_name, index_to_label(item_new));
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Note how many slots the store used to have */
+ i = st_ptr->stock_num;
+
+ /* Remove the bought items from the store */
+ store_item_increase(item, -amt);
+ store_item_optimize(item);
+
+ /* Store is empty */
+ if (st_ptr->stock_num == 0)
+ {
+ /* Shuffle */
+ if (retire_owner_p())
+ {
+ /* Message */
+ msg_print("The shopkeeper retires.");
+
+ /* Shuffle the store */
+ store_shuffle(cur_store_num);
+ }
+
+ /* Maintain */
+ else
+ {
+ /* Message */
+ msg_print("The shopkeeper brings out some new stock.");
+ }
+
+ /* New inventory */
+ for (i = 0; i < 10; i++)
+ {
+ /* Maintain the store */
+ store_maint(p_ptr->town_num, cur_store_num);
+ }
+
+ /* Start over */
+ store_top = 0;
+
+ /* Redraw everything */
+ display_inventory();
+ }
+
+ /* The item is gone */
+ else if (st_ptr->stock_num != i)
+ {
+ /* Pick the correct screen */
+ if (store_top >= st_ptr->stock_num) store_top -= 12;
+
+ /* Redraw everything */
+ display_inventory();
+ }
+
+ /* Item is still here */
+ else
+ {
+ /* Redraw the item */
+ display_entry(item);
+ }
+ }
+ else
+ {
+ /* Complain */
+ say_comment_4();
+
+ /* Reset insults */
+ st_ptr->insult_cur = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+ /* Kicked out for a LONG time */
+ st_ptr->store_open = turn + 500000 + randint(500000);
+ }
+
+ /* Not kicked out */
+ return;
+}
+
+/*
+ * Buy an item from a store -RAK-
+ */
+void store_purchase(void)
+{
+ int i, amt = 1, choice;
+ int item, item_new;
+
+ s32b price, best;
+
+ object_type forge;
+ object_type *j_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ /* Museum? */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM)
+ {
+ msg_print("You cannot take items from the museum!");
+ return;
+ }
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ if (cur_store_num == 7) msg_print("Your home is empty.");
+ else msg_print("I am currently out of stock.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ if (cur_store_num == 7)
+ {
+ strnfmt(out_val, 160, "Which item do you want to take? ");
+ }
+ else
+ {
+ strnfmt(out_val, 160, "Which item are you interested in? ");
+ }
+
+ /* Get the item number to be bought */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get a copy of one object to determine the price */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = 1;
+
+ /* Hack -- If a wand, allocate the number of charges of one wand */
+ if (j_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval / o_ptr->number;
+ }
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many different items.");
+ return;
+ }
+
+ /* Determine the "best" price (per item) */
+ best = price_item(j_ptr, ot_ptr->min_inflate, FALSE);
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ s32b q;
+
+
+ /* Hack -- note cost of "fixed" items */
+ if ((cur_store_num != 7) && (o_ptr->ident & (IDENT_FIXED)))
+ {
+ msg_format("That costs %ld gold per item.", (long)(best));
+ }
+
+ /* How many can we buy ? 99 if price is 0*/
+ if (cur_store_num == STORE_HOME)
+ {
+ q = 99;
+ }
+ else if (best == 0)
+ {
+ q = 99;
+ }
+ else
+ {
+ if (auto_haggle)
+ q = p_ptr->au / (best + (best / 10));
+ else
+ q = p_ptr->au / best;
+ }
+ if (o_ptr->number < q)
+ q = o_ptr->number;
+
+ /* None ? ahh too bad */
+ if (!q)
+ {
+ msg_print("You do not have enough gold to buy one.");
+ return;
+ }
+
+ /* Get a quantity */
+ amt = get_quantity(NULL, q);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ j_ptr = &forge;
+
+ /* Get desired object */
+ object_copy(j_ptr, o_ptr);
+
+ /* Modify quantity */
+ j_ptr->number = amt;
+
+ /* Hack -- If a rod or wand, allocate total maximum timeouts or charges
+ * between those purchased and left on the shelf. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Hack -- require room in pack */
+ if (!inven_carry_okay(j_ptr))
+ {
+ msg_print("You cannot carry that many items.");
+ return;
+ }
+
+ /* Attempt to buy it */
+ if (cur_store_num != 7)
+ {
+ /* Fixed price, quick buy */
+ if (o_ptr->ident & (IDENT_FIXED))
+ {
+ /* Assume accept */
+ choice = 0;
+
+ /* Go directly to the "best" deal */
+ price = (best * j_ptr->number);
+ }
+
+ /* Haggle for it */
+ else
+ {
+ /* Describe the object (fully) */
+ object_desc_store(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("Buying %s (%c).", o_name, I2A(item));
+ msg_print(NULL);
+
+ /* Haggle for a final price */
+ choice = purchase_haggle(j_ptr, &price);
+
+ /* Hack -- Got kicked out */
+ if (st_ptr->store_open >= turn) return;
+ }
+
+
+ /* Player wants it */
+ if (choice == 0)
+ {
+ /* Fix the item price (if "correctly" haggled) */
+ if (price == (best * j_ptr->number)) o_ptr->ident |= (IDENT_FIXED);
+
+ /* Player can afford it */
+ if (p_ptr->au >= price)
+ {
+ /* Say "okay" */
+ say_comment_1();
+
+ /* Make a sound */
+ sound(SOUND_BUY);
+
+ /* Be happy */
+ decrease_insults();
+
+ /* Spend the money */
+ p_ptr->au -= price;
+
+ /* Update the display */
+ store_prt_gold();
+
+ /* Hack -- buying an item makes you aware of it */
+ object_aware(j_ptr);
+
+ /* Be aware of how you found it */
+ j_ptr->found = OBJ_FOUND_STORE;
+ j_ptr->found_aux1 = st_ptr->st_idx;
+
+ /* Hack -- clear the "fixed" flag from the item */
+ j_ptr->ident &= ~(IDENT_FIXED);
+
+ /* Describe the transaction */
+ object_desc(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You bought %s for %ld gold.", o_name, (long)price);
+
+ /* Erase the inscription */
+ j_ptr->note = 0;
+
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges between those picked up and
+ * those left behind. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Give it to the player */
+ item_new = inven_carry(j_ptr, FALSE);
+
+ /* Describe the final result */
+ object_desc(o_name, &p_ptr->inventory[item_new], TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).",
+ o_name, index_to_label(item_new));
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Note how many slots the store used to have */
+ i = st_ptr->stock_num;
+
+ /* Remove the bought items from the store */
+ store_item_increase(item, -amt);
+ store_item_optimize(item);
+
+ /* Store is empty */
+ if (st_ptr->stock_num == 0)
+ {
+ /* Shuffle */
+ if (retire_owner_p())
+ {
+ /* Message */
+ msg_print("The shopkeeper retires.");
+
+ /* Shuffle the store */
+ store_shuffle(cur_store_num);
+ }
+
+ /* Maintain */
+ else
+ {
+ /* Message */
+ msg_print("The shopkeeper brings out some new stock.");
+ }
+
+ /* New inventory */
+ for (i = 0; i < 10; i++)
+ {
+ /* Maintain the store */
+ store_maint(p_ptr->town_num, cur_store_num);
+ }
+
+ /* Start over */
+ store_top = 0;
+ }
+
+ /* The item is gone */
+ else if (st_ptr->stock_num != i)
+ {
+ /* Pick the correct screen */
+ if (store_top >= st_ptr->stock_num) store_top -= 12;
+ }
+
+ /* Redraw everything */
+ display_inventory();
+ }
+
+ /* Player cannot afford it */
+ else
+ {
+ /* Simple message (no insult) */
+ msg_print("You do not have enough gold.");
+ }
+ }
+ }
+
+ /* Home is much easier */
+ else
+ {
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges between those picked up and
+ * those left behind. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ j_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ o_ptr->pval -= j_ptr->pval;
+ }
+
+ /* Give it to the player */
+ item_new = inven_carry(j_ptr, FALSE);
+
+ /* Describe just the result */
+ object_desc(o_name, &p_ptr->inventory[item_new], TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(item_new));
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Take note if we take the last one */
+ i = st_ptr->stock_num;
+
+ /* Remove the items from the home */
+ store_item_increase(item, -amt);
+ store_item_optimize(item);
+
+ /* Hack -- Item is still here */
+ if (i == st_ptr->stock_num)
+ {
+ /* Redraw the item */
+ display_entry(item);
+ }
+
+ /* The item is gone */
+ else
+ {
+ /* Nothing left */
+ if (st_ptr->stock_num == 0) store_top = 0;
+
+ /* Nothing left on that screen */
+ else if (store_top >= st_ptr->stock_num) store_top -= 12;
+
+ /* Redraw everything */
+ display_inventory();
+ }
+ }
+
+ /* Not kicked out */
+ return;
+}
+
+
+/*
+ * Sell an item to the store (or home)
+ */
+void store_sell(void)
+{
+ int choice;
+ int item, item_pos;
+ int amt;
+
+ s32b price, value, dummy;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ char o_name[80];
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ bool museum = (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) ? TRUE : FALSE;
+
+ /* Prepare a prompt */
+ if (cur_store_num == 7) q = "Drop which item? ";
+ else if (museum) q = "Donate which item?";
+ else q = "Sell which item? ";
+
+ /* Only allow items the store will buy */
+ item_tester_hook = store_will_buy;
+
+ /* Get an item */
+ if (cur_store_num == STORE_HOME)
+ {
+ s = "You have nothing to drop.";
+ }
+ else if (museum)
+ {
+ s = "You have nothing to donate.";
+ }
+ else
+ {
+ s = "You have nothing that I want.";
+ }
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- Cannot remove cursed items */
+ if (cursed_p(o_ptr))
+ {
+ if (item >= INVEN_WIELD)
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+ else
+ {
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to drop it.");
+
+ /* Nope */
+ return;
+ }
+ }
+ }
+
+
+ /* Hack -- Cannot put a portable hole into home */
+#if 0 /* DGDGDGDG -- use a skill */
+ if ((cp_ptr->magic_key == MKEY_TELEKINESIS) &&
+ (o_ptr->tval == TV_TOOL) && (o_ptr->sval == SV_PORTABLE_HOLE))
+ {
+ msg_print("Putting it into your home has extra-dimensional problems");
+
+ return;
+ }
+#endif
+
+ /* Assume one item */
+ amt = 1;
+
+ /* Find out how many the player wants (letter means "all") */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges to those being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Get a full description */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Remove any inscription for stores */
+ if ((cur_store_num != 7) && !museum)
+ {
+ q_ptr->note = 0;
+ }
+
+ /* Is there room in the store (or the home?) */
+ if (!store_check_num(q_ptr))
+ {
+ if (cur_store_num == 7) msg_print("Your home is full.");
+ else if (museum) msg_print("The museum is full.");
+ else msg_print("I have not the room in my store to keep it.");
+ return;
+ }
+
+
+ /* Real store */
+ if ((cur_store_num != 7) && !museum)
+ {
+ /* Describe the transaction */
+ msg_format("Selling %s (%c).", o_name, index_to_label(item));
+ msg_print(NULL);
+
+ /* Haggle for it */
+ choice = sell_haggle(q_ptr, &price);
+
+ /* Kicked out */
+ if (st_ptr->store_open >= turn) return;
+
+ /* Sold... */
+ if (choice == 0)
+ {
+ /* Say "okay" */
+ say_comment_1();
+
+ /* Make a sound */
+ sound(SOUND_SELL);
+
+ /* Be happy */
+ decrease_insults();
+
+ /* Get some money */
+ p_ptr->au += price;
+
+ /* Update the display */
+ store_prt_gold();
+
+ /* Get the "apparent" value */
+ dummy = object_value(q_ptr) * q_ptr->number;
+
+ /* Identify original item */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /*
+ * Hack -- If a rod or wand, let the shopkeeper know just
+ * how many charges he really paid for. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Get the "actual" value */
+ value = object_value(q_ptr) * q_ptr->number;
+
+ /* Get the description all over again */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Describe the result (in message buffer) */
+ msg_format("You sold %s for %ld gold.", o_name, (long)price);
+
+ /* Analyze the prices (and comment verbally) */
+ purchase_analyze(price, value, dummy);
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Take the item from the player, describe the result */
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* The store gets that (known) item */
+ item_pos = store_carry(q_ptr);
+
+ /* Re-display if item is now in store */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Player is at museum */
+ else if (museum)
+ {
+ char o2_name[80];
+ object_desc(o2_name, q_ptr, TRUE, 0);
+
+ msg_print("Once you donate something, you cannot take it back.");
+ if (!get_check(format("Do you really want to donate %s?", o2_name))) return;
+
+ /* Identify it */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+
+ /* Describe */
+ msg_format("You donate %s (%c).", o_name, index_to_label(item));
+
+ choice = 0;
+
+ /* Take it from the players inventory */
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Update store display */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+
+ /* Player is at home */
+ else
+ {
+ /* Describe */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Take it from the players inventory */
+ inven_item_increase(item, -amt);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Update store display */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+}
+
+
+
+/*
+ * Examine an item in a store -JDL-
+ */
+void store_examine(void)
+{
+ int i;
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ if (cur_store_num == 7) msg_print("Your home is empty.");
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) msg_print("The museum is empty.");
+ else msg_print("I am currently out of stock.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ strnfmt(out_val, 160, "Which item do you want to examine? ");
+
+ /* Get the item number to be examined */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Debug hack */
+ if (wizard)
+ {
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+ }
+
+ /* Require full knowledge */
+ if (!(o_ptr->ident & (IDENT_MENTAL)))
+ {
+ /* This can only happen in the home */
+ msg_print("You have no special knowledge about that item.");
+ return;
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ msg_format("Examining %s...", o_name);
+
+ /* Show the object's powers. */
+ if (!object_out_desc(o_ptr, NULL, FALSE, TRUE))
+ {
+ msg_print("You see nothing special.");
+ }
+
+ /* Show spell listing for instruments, daemonwear and spellbooks. */
+ if ((o_ptr->tval == TV_INSTRUMENT) || (o_ptr->tval == TV_DAEMON_BOOK)
+ || (o_ptr->tval == TV_BOOK))
+ {
+ do_cmd_browse_aux(o_ptr);
+ }
+
+ return;
+}
+
+
+
+
+
+/*
+ * Hack -- set this to leave the store
+ */
+static bool leave_store = FALSE;
+
+
+/*
+ * Process a command in a store
+ *
+ * Note that we must allow the use of a few "special" commands
+ * in the stores which are not allowed in the dungeon, and we
+ * must disable some commands which are allowed in the dungeon
+ * but not in the stores, to prevent chaos.
+ */
+static bool store_process_command(void)
+{
+ bool validcmd = FALSE;
+ int i;
+ store_action_type *ba_ptr;
+ bool recreate = FALSE;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_info[st_ptr->st_idx].actions[i]];
+
+ if (ba_ptr->letter)
+ {
+ if (ba_ptr->letter == command_cmd)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ if (ba_ptr->letter_aux)
+ {
+ if (ba_ptr->letter_aux == command_cmd)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (validcmd)
+ {
+ recreate = bldg_process_command(st_ptr, i);
+ }
+ else
+ {
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Leave */
+ case ESCAPE:
+ {
+ leave_store = TRUE;
+ break;
+ }
+
+ /* Browse */
+ case ' ':
+ {
+ if (st_ptr->stock_num <= 12)
+ {
+ msg_print("Entire inventory is shown.");
+ }
+ else
+ {
+ store_top += 12;
+ if (store_top >= st_ptr->stock_num) store_top = 0;
+ display_inventory();
+ }
+ break;
+ }
+
+ /* Browse backwards */
+ case '-':
+ {
+ if (st_ptr->stock_num <= 12)
+ {
+ msg_print("Entire inventory is shown.");
+ }
+ else
+ {
+ store_top -= 12;
+ if (store_top < 0)
+ {
+ store_top = ((st_ptr->stock_num - 1) / 12) * 12;
+ }
+ display_inventory();
+ }
+ }
+
+ /* Redraw */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ display_store();
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ do_cmd_takeoff();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+
+ /*** Use various objects ***/
+
+ /* Browse a book */
+ case 'b':
+ {
+ do_cmd_browse();
+ break;
+ }
+
+ /* Inscribe an object */
+ case '{':
+ {
+ do_cmd_inscribe();
+ break;
+ }
+
+ /* Uninscribe an object */
+ case '}':
+ {
+ do_cmd_uninscribe();
+ break;
+ }
+
+
+
+ /*** Help and Such ***/
+
+ /* Help */
+ case '?':
+ {
+ do_cmd_help();
+ break;
+ }
+
+ /* Identify symbol */
+ case '/':
+ {
+ do_cmd_query_symbol();
+ break;
+ }
+
+ /* Character description */
+ case 'C':
+ {
+ do_cmd_change_name();
+ display_store();
+ break;
+ }
+
+
+ /*** System Commands ***/
+
+ /* Hack -- User interface */
+ case '!':
+ {
+ (void)Term_user(0);
+ break;
+ }
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Check artifacts, uniques etc. */
+ case '~':
+ case '|':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Load "screen dump" */
+ case '(':
+ {
+ do_cmd_load_screen();
+ break;
+ }
+
+ /* Save "screen dump" */
+ case ')':
+ {
+ do_cmd_save_screen();
+ break;
+ }
+
+
+ /* Hack -- Unknown command */
+ default:
+ {
+ if (st_ptr->st_idx == STORE_HOME)
+ msg_print("That command does not work in this home.");
+ else
+ msg_print("That command does not work in this store.");
+ break;
+ }
+ }
+ }
+
+ return recreate;
+}
+
+
+/*
+ * Enter a store, and interact with it.
+ *
+ * Note that we use the standard "request_command()" function
+ * to get a command, allowing us to use "command_arg" and all
+ * command macros and other nifty stuff, but we use the special
+ * "shopping" argument, to force certain commands to be converted
+ * into other commands, normally, we convert "p" (pray) and "m"
+ * (cast magic) into "g" (get), and "s" (search) into "d" (drop).
+ */
+void do_cmd_store(void)
+{
+ int which;
+ int maintain_num;
+ int tmp_chr;
+ int i;
+ bool recreate = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Access the player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Verify a store */
+ if (c_ptr->feat != FEAT_SHOP)
+ {
+ msg_print("You see no store here.");
+ return;
+ }
+
+ /* Extract the store code */
+ which = c_ptr->special;
+
+ /* Hack -- Check the "locked doors" */
+ if (town_info[p_ptr->town_num].store[which].store_open >= turn)
+ {
+ msg_print("The doors are locked.");
+ return;
+ }
+
+ /* Calculate the number of store maintainances since the last visit */
+ maintain_num = (turn - town_info[p_ptr->town_num].store[which].last_visit) / (10L * STORE_TURNS);
+
+ /* Maintain the store max. 10 times */
+ if (maintain_num > 10) maintain_num = 10;
+
+ if (maintain_num)
+ {
+ /* Maintain the store */
+ for (i = 0; i < maintain_num; i++)
+ store_maint(p_ptr->town_num, which);
+
+ /* Save the visit */
+ town_info[p_ptr->town_num].store[which].last_visit = turn;
+ }
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+
+ /* Hack -- Character is in "icky" mode */
+ character_icky = TRUE;
+
+
+ /* No command argument */
+ command_arg = 0;
+
+ /* No repeated command */
+ command_rep = 0;
+
+ /* No automatic command */
+ command_new = 0;
+
+
+ /* Save the store number */
+ cur_store_num = which;
+
+ /* Save the store and owner pointers */
+ st_ptr = &town_info[p_ptr->town_num].store[cur_store_num];
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Start at the beginning */
+ store_top = 0;
+
+ /* Display the store */
+ display_store();
+
+ /* Mega-Hack -- Ignore keymaps on store action letters */
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *ba_ptr =
+ &ba_info[st_info[st_ptr->st_idx].actions[i]];
+ request_command_ignore_keymaps[2*i] = ba_ptr->letter;
+ request_command_ignore_keymaps[2*i+1] = ba_ptr->letter_aux;
+
+ }
+
+ /* Do not leave */
+ leave_store = FALSE;
+
+ /* Interact with player */
+ while (!leave_store)
+ {
+ /* Hack -- Clear line 1 */
+ prt("", 1, 0);
+
+ /* Hack -- Check the charisma */
+ tmp_chr = p_ptr->stat_use[A_CHR];
+
+ /* Clear */
+ clear_from(21);
+
+
+ /* Basic commands */
+ c_prt(TERM_YELLOW, " ESC.", 22, 0);
+ prt(") Exit.", 22, 4);
+
+ /* Browse if necessary */
+ if (st_ptr->stock_num > 12)
+ {
+ c_prt(TERM_YELLOW, " SPACE", 23, 0);
+ prt(") Next page", 23, 6);
+ }
+
+ /* Prompt */
+ prt("You may: ", 21, 0);
+
+ /* Show the commands */
+ show_building(st_ptr);
+
+ /* Get a command */
+ request_command(TRUE);
+
+ /* Process the command */
+ if (store_process_command()) recreate = TRUE;
+
+ /* Hack -- Character is still in "icky" mode */
+ character_icky = TRUE;
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* XXX XXX XXX Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Hack -- Flee from the store */
+ if (cur_store_num != 7)
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee the store...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Flee from the home */
+ else if (!store_check_num(o_ptr))
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee your home...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Drop items into the home */
+ else
+ {
+ int item_pos;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Give a message */
+ msg_print("Your pack overflows!");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Grab a copy of the item */
+ object_copy(q_ptr, o_ptr);
+
+ /* Describe it */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Remove it from the players inventory */
+ inven_item_increase(item, -255);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Redraw the home */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Hack -- Redisplay store prices if charisma changes */
+ if (tmp_chr != p_ptr->stat_use[A_CHR]) display_inventory();
+
+ /* Hack -- get kicked out of the store */
+ if (st_ptr->store_open >= turn) leave_store = TRUE;
+ }
+
+ /* Free turn XXX XXX XXX */
+ energy_use = 0;
+
+ /* Recreate the level only when needed */
+ if (recreate)
+ {
+ /* Reinit wilderness to activate quests ... */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Hack -- Character is no longer in "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Hack -- Cancel automatic command */
+ command_new = 0;
+
+ /* Hack -- Cancel "see" mode */
+ command_see = FALSE;
+
+ /* Mega-Hack -- Clear the 'ignore-keymaps' list */
+ memset(request_command_ignore_keymaps, 0, 12);
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Update everything */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * Shuffle one of the stores.
+ */
+void store_shuffle(int which)
+{
+ int i, j;
+
+
+ /* Ignore home */
+ if (which == STORE_HOME) return;
+
+ /* Ignoer Museum */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) return;
+
+
+ /* Save the store index */
+ cur_store_num = which;
+
+ /* Activate that store */
+ st_ptr = &town_info[p_ptr->town_num].store[cur_store_num];
+
+ /* Pick a new owner */
+ for (j = st_ptr->owner; j == st_ptr->owner; )
+ {
+ st_ptr->owner = st_info[st_ptr->st_idx].owners[rand_int(4)];
+ }
+
+ /* Activate the new owner */
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Reset the owner data */
+ st_ptr->insult_cur = 0;
+ st_ptr->store_open = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+
+ /* Hack -- discount all the items */
+ for (i = 0; i < st_ptr->stock_num; i++)
+ {
+ object_type *o_ptr;
+
+ /* Get the item */
+ o_ptr = &st_ptr->stock[i];
+
+ /* Hack -- Sell all old items for "half price" */
+ if (!(o_ptr->art_name))
+ o_ptr->discount = 50;
+
+ /* Hack -- Items are no longer "fixed price" */
+ o_ptr->ident &= ~(IDENT_FIXED);
+
+ /* Mega-Hack -- Note that the item is "on sale" */
+ o_ptr->note = quark_add("on sale");
+ }
+}
+
+
+/*
+ * Maintain the inventory at the stores.
+ */
+void store_maint(int town_num, int store_num)
+{
+ int j, tries = 100;
+
+ int old_rating = rating;
+
+ cur_store_num = store_num;
+
+ /* Ignore home */
+ if (store_num == STORE_HOME) return;
+
+ /* Activate that store */
+ st_ptr = &town_info[town_num].store[store_num];
+
+ /* Ignoer Museum */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) return;
+
+ /* Activate the owner */
+ ot_ptr = &ow_info[st_ptr->owner];
+
+ /* Store keeper forgives the player */
+ st_ptr->insult_cur = 0;
+
+ /* Mega-Hack -- prune the black market */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM)
+ {
+ /* Destroy crappy black market items */
+ for (j = st_ptr->stock_num - 1; j >= 0; j--)
+ {
+ object_type *o_ptr = &st_ptr->stock[j];
+
+ /* Destroy crappy items */
+ if (black_market_crap(o_ptr))
+ {
+ /* Destroy the item */
+ store_item_increase(j, 0 - o_ptr->number);
+ store_item_optimize(j);
+ }
+ }
+ }
+
+
+ /* Choose the number of slots to keep */
+ j = st_ptr->stock_num;
+
+ /* Sell a few items */
+ j = j - randint(STORE_TURNOVER);
+
+ /* Never keep more than "STORE_MAX_KEEP" slots */
+ if (j > STORE_MAX_KEEP) j = STORE_MAX_KEEP;
+
+ /* Always "keep" at least "STORE_MIN_KEEP" items */
+ if (j < STORE_MIN_KEEP) j = STORE_MIN_KEEP;
+
+ /* Hack -- prevent "underflow" */
+ if (j < 0) j = 0;
+
+ /* Destroy objects until only "j" slots are left */
+ while (st_ptr->stock_num > j) store_delete();
+
+
+ /* Choose the number of slots to fill */
+ j = st_ptr->stock_num;
+
+ /* Buy some more items */
+ j = j + randint(STORE_TURNOVER);
+
+ /* Never keep more than "STORE_MAX_KEEP" slots */
+ if (j > STORE_MAX_KEEP) j = STORE_MAX_KEEP;
+
+ /* Always "keep" at least "STORE_MIN_KEEP" items */
+ if (j < STORE_MIN_KEEP) j = STORE_MIN_KEEP;
+
+ /* Hack -- prevent "overflow" */
+ if (j >= st_ptr->stock_size) j = st_ptr->stock_size - 1;
+
+ /* Acquire some new items */
+ while ((st_ptr->stock_num < j) && tries)
+ {
+ store_create();
+ tries--;
+ }
+
+ /* Hack -- Restore the rating */
+ rating = old_rating;
+}
+
+
+/*
+ * Initialize the stores
+ */
+void store_init(int town_num, int store_num)
+{
+ int k;
+
+ cur_store_num = store_num;
+
+ /* Activate that store */
+ st_ptr = &town_info[town_num].store[store_num];
+
+
+ /* Pick an owner */
+ st_ptr->owner = st_info[st_ptr->st_idx].owners[rand_int(4)];
+
+ /* Activate the new owner */
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Initialize the store */
+ st_ptr->store_open = 0;
+ st_ptr->insult_cur = 0;
+ st_ptr->good_buy = 0;
+ st_ptr->bad_buy = 0;
+
+ /* Nothing in stock */
+ st_ptr->stock_num = 0;
+
+ /*
+ * MEGA-HACK - Last visit to store is
+ * BEFORE player birth to enable store restocking
+ */
+ st_ptr->last_visit = -100L * STORE_TURNS;
+
+ /* Clear any old items */
+ for (k = 0; k < st_ptr->stock_size; k++)
+ {
+ object_wipe(&st_ptr->stock[k]);
+ }
+}
+
+
+void move_to_black_market(object_type * o_ptr)
+{
+ st_ptr = &town_info[p_ptr->town_num].store[6];
+ o_ptr->ident |= IDENT_STOREB;
+ (void)store_carry(o_ptr);
+ object_wipe(o_ptr); /* Don't leave a bogus object behind... */
+}
+
+/*
+ * Enter the home, and interact with it from the dungeon (trump magic).
+ *
+ * Note that we use the standard "request_command()" function
+ * to get a command, allowing us to use "command_arg" and all
+ * command macros and other nifty stuff, but we use the special
+ * "shopping" argument, to force certain commands to be converted
+ * into other commands, normally, we convert "p" (pray) and "m"
+ * (cast magic) into "g" (get), and "s" (search) into "d" (drop).
+ */
+void do_cmd_home_trump(void)
+{
+ int which;
+ int maintain_num;
+ int tmp_chr;
+ int i;
+ int town_num;
+
+ /* Extract the store code */
+ which = 7;
+
+ if (p_ptr->town_num) town_num = p_ptr->town_num;
+ else town_num = 1;
+
+ /* Hack -- Check the "locked doors" */
+ if (town_info[town_num].store[which].store_open >= turn)
+ {
+ msg_print("The doors are locked.");
+ return;
+ }
+
+ /* Calculate the number of store maintainances since the last visit */
+ maintain_num = (turn - town_info[town_num].store[which].last_visit) / (10L * STORE_TURNS);
+
+ /* Maintain the store max. 10 times */
+ if (maintain_num > 10) maintain_num = 10;
+
+ if (maintain_num)
+ {
+ /* Maintain the store */
+ for (i = 0; i < maintain_num; i++)
+ store_maint(town_num, which);
+
+ /* Save the visit */
+ town_info[town_num].store[which].last_visit = turn;
+ }
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+
+ /* Hack -- Character is in "icky" mode */
+ character_icky = TRUE;
+
+
+ /* No command argument */
+ command_arg = 0;
+
+ /* No repeated command */
+ command_rep = 0;
+
+ /* No automatic command */
+ command_new = 0;
+
+
+ /* Save the store number */
+ cur_store_num = which;
+
+ /* Save the store and owner pointers */
+ st_ptr = &town_info[town_num].store[cur_store_num];
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Start at the beginning */
+ store_top = 0;
+
+ /* Display the store */
+ display_store();
+
+ /* Mega-Hack -- Ignore keymaps on store action letters */
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *ba_ptr =
+ &ba_info[st_info[st_ptr->st_idx].actions[i]];
+ request_command_ignore_keymaps[2*i] = ba_ptr->letter;
+ request_command_ignore_keymaps[2*i+1] = ba_ptr->letter_aux;
+ }
+
+ /* Do not leave */
+ leave_store = FALSE;
+
+ /* Interact with player */
+ while (!leave_store)
+ {
+ /* Hack -- Clear line 1 */
+ prt("", 1, 0);
+
+ /* Hack -- Check the charisma */
+ tmp_chr = p_ptr->stat_use[A_CHR];
+
+ /* Clear */
+ clear_from(21);
+
+
+ /* Basic commands */
+ prt(" ESC) Exit from Building.", 22, 0);
+
+ /* Browse if necessary */
+ if (st_ptr->stock_num > 12)
+ {
+ prt(" SPACE) Next page of stock", 23, 0);
+ }
+
+ /* Home commands */
+ if (cur_store_num == 7)
+ {
+ prt(" g) Get an item.", 22, 31);
+ prt(" d) Drop an item.", 23, 31);
+ }
+
+ /* Shop commands XXX XXX XXX */
+ else
+ {
+ prt(" p) Purchase an item.", 22, 31);
+ prt(" s) Sell an item.", 23, 31);
+ }
+
+ /* Add in the eXamine option */
+ prt(" x) eXamine an item.", 22, 56);
+
+ /* Prompt */
+ prt("You may: ", 21, 0);
+
+ /* Get a command */
+ request_command(TRUE);
+
+ /* Process the command */
+ store_process_command();
+
+ /* Hack -- Character is still in "icky" mode */
+ character_icky = TRUE;
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* XXX XXX XXX Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Hack -- Flee from the store */
+ if (cur_store_num != 7)
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee the store...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Flee from the home */
+ else if (!store_check_num(o_ptr))
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee your home...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Drop items into the home */
+ else
+ {
+ int item_pos;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Give a message */
+ msg_print("Your pack overflows!");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Grab a copy of the item */
+ object_copy(q_ptr, o_ptr);
+
+ /* Describe it */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Remove it from the players inventory */
+ inven_item_increase(item, -255);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Redraw the home */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Hack -- Redisplay store prices if charisma changes */
+ if (tmp_chr != p_ptr->stat_use[A_CHR]) display_inventory();
+
+ /* Hack -- get kicked out of the store */
+ if (st_ptr->store_open >= turn) leave_store = TRUE;
+ }
+
+
+ /* Hack -- Character is no longer in "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Hack -- Cancel automatic command */
+ command_new = 0;
+
+ /* Hack -- Cancel "see" mode */
+ command_see = FALSE;
+
+ /* Mega-Hack -- Clear the 'ignore-keymaps' list */
+ memset(request_command_ignore_keymaps, 0, 12);
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Update everything */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_BASIC | PR_EXTRA);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+static void pay_for_requested_item(int value, object_type *q_ptr)
+{
+ msg_format("It'll cost %i gold pieces. ", value);
+
+ if (get_check("Do you wish to pay?"))
+ {
+ if (p_ptr->au < value)
+ msg_print("You don't have enough money for it.");
+ else
+ {
+ if (store_carry(q_ptr) != -1)
+ {
+ msg_print("The item has arrived in the Black Market.");
+ p_ptr->au -= value;
+
+ p_ptr->redraw |= PR_GOLD;
+ }
+ else
+ msg_print("There isn't enough room for it.");
+ }
+ }
+}
+
+/*
+ * Request item for merchants
+ */
+void store_request_item(void)
+{
+ char buf[80], name[80];
+ object_type forge, *q_ptr = &forge;
+ store_type *ost_ptr = st_ptr;
+
+ /* Paranoia */
+#if 0 /* DGDGDGDG -- use a skill */
+ if (cp_ptr->magic_key == MKEY_TELEKINESIS)
+ {
+ st_ptr = ost_ptr;
+ return;
+ }
+#endif
+ /* Get the Black Market */
+ st_ptr = &town_info[p_ptr->town_num].store[6];
+
+ /* Make an empty string */
+ buf[0] = 0;
+
+ /* Ask for the wish */
+ if (!get_string("Request what? ", buf, 80))
+ {
+ st_ptr = ost_ptr;
+ return;
+ }
+
+ clean_wish_name(buf, name);
+
+ if (test_object_wish(name, q_ptr, &forge, "request"))
+ {
+ int value = object_value_real(q_ptr) * 5;
+
+ /* Pay for the delivery */
+ pay_for_requested_item(value, q_ptr);
+
+ /* Don't search any more */
+ st_ptr = ost_ptr;
+ return;
+ }
+
+ st_ptr = ost_ptr;
+}
diff --git a/src/tables.c b/src/tables.c
new file mode 100644
index 00000000..6c83ee04
--- /dev/null
+++ b/src/tables.c
@@ -0,0 +1,4804 @@
+/* File: tables.c */
+
+/* Purpose: Angband Tables */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+
+/*
+ * Global array for looping through the "keypad directions"
+ */
+s16b ddd[9] =
+ { 2, 8, 6, 4, 3, 1, 9, 7, 5 };
+
+/*
+ * Global arrays for converting "keypad direction" into offsets
+ */
+s16b ddx[10] =
+ { 0, -1, 0, 1, -1, 0, 1, -1, 0, 1 };
+
+s16b ddy[10] =
+ { 0, 1, 1, 1, 0, 0, 0, -1, -1, -1 };
+
+/*
+ * Global arrays for optimizing "ddx[ddd[i]]" and "ddy[ddd[i]]"
+ */
+s16b ddx_ddd[9] =
+ { 0, 0, 1, -1, 1, -1, 1, -1, 0 };
+
+s16b ddy_ddd[9] =
+ { 1, -1, 0, 0, 1, 1, -1, -1, 0 };
+
+
+
+/*
+* Global array for converting numbers to uppercase hexadecimal digit
+ * This array can also be used to convert a number to an octal digit
+ */
+char hexsym[16] =
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+
+/*
+ * Stat Table (INT/WIS) -- Number of half-spells per level
+ */
+byte adj_mag_study[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 2 /* 12 */,
+ 2 /* 13 */,
+ 2 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 2 /* 18/00-18/09 */,
+ 2 /* 18/10-18/19 */,
+ 2 /* 18/20-18/29 */,
+ 2 /* 18/30-18/39 */,
+ 2 /* 18/40-18/49 */,
+ 3 /* 18/50-18/59 */,
+ 3 /* 18/60-18/69 */,
+ 3 /* 18/70-18/79 */,
+ 3 /* 18/80-18/89 */,
+ 4 /* 18/90-18/99 */,
+ 4 /* 18/100-18/109 */,
+ 4 /* 18/110-18/119 */,
+ 5 /* 18/120-18/129 */,
+ 5 /* 18/130-18/139 */,
+ 5 /* 18/140-18/149 */,
+ 5 /* 18/150-18/159 */,
+ 5 /* 18/160-18/169 */,
+ 5 /* 18/170-18/179 */,
+ 5 /* 18/180-18/189 */,
+ 5 /* 18/190-18/199 */,
+ 5 /* 18/200-18/209 */,
+ 6 /* 18/210-18/219 */,
+ 6 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT/WIS) -- extra half-mana-points per level
+ */
+byte adj_mag_mana[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 2 /* 9 */,
+ 2 /* 10 */,
+ 2 /* 11 */,
+ 2 /* 12 */,
+ 2 /* 13 */,
+ 2 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 3 /* 18/30-18/39 */,
+ 3 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 5 /* 18/70-18/79 */,
+ 6 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 9 /* 18/110-18/119 */,
+ 10 /* 18/120-18/129 */,
+ 11 /* 18/130-18/139 */,
+ 12 /* 18/140-18/149 */,
+ 13 /* 18/150-18/159 */,
+ 14 /* 18/160-18/169 */,
+ 15 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 16 /* 18/190-18/199 */,
+ 17 /* 18/200-18/209 */,
+ 17 /* 18/210-18/219 */,
+ 18 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT/WIS) -- Minimum failure rate (percentage)
+ */
+byte adj_mag_fail[] =
+{
+ 99 /* 3 */,
+ 99 /* 4 */,
+ 99 /* 5 */,
+ 99 /* 6 */,
+ 99 /* 7 */,
+ 50 /* 8 */,
+ 30 /* 9 */,
+ 20 /* 10 */,
+ 15 /* 11 */,
+ 12 /* 12 */,
+ 11 /* 13 */,
+ 10 /* 14 */,
+ 9 /* 15 */,
+ 8 /* 16 */,
+ 7 /* 17 */,
+ 6 /* 18/00-18/09 */,
+ 6 /* 18/10-18/19 */,
+ 5 /* 18/20-18/29 */,
+ 5 /* 18/30-18/39 */,
+ 5 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 4 /* 18/70-18/79 */,
+ 4 /* 18/80-18/89 */,
+ 3 /* 18/90-18/99 */,
+ 3 /* 18/100-18/109 */,
+ 2 /* 18/110-18/119 */,
+ 2 /* 18/120-18/129 */,
+ 2 /* 18/130-18/139 */,
+ 2 /* 18/140-18/149 */,
+ 1 /* 18/150-18/159 */,
+ 1 /* 18/160-18/169 */,
+ 1 /* 18/170-18/179 */,
+ 1 /* 18/180-18/189 */,
+ 1 /* 18/190-18/199 */,
+ 0 /* 18/200-18/209 */,
+ 0 /* 18/210-18/219 */,
+ 0 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT/WIS) -- Various things
+ */
+byte adj_mag_stat[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 3 /* 18/30-18/39 */,
+ 3 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 5 /* 18/70-18/79 */,
+ 6 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 9 /* 18/110-18/119 */,
+ 10 /* 18/120-18/129 */,
+ 11 /* 18/130-18/139 */,
+ 12 /* 18/140-18/149 */,
+ 13 /* 18/150-18/159 */,
+ 14 /* 18/160-18/169 */,
+ 15 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 17 /* 18/190-18/199 */,
+ 18 /* 18/200-18/209 */,
+ 19 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (CHR) -- payment percentages
+ */
+byte adj_chr_gold[] =
+{
+ 130 /* 3 */,
+ 125 /* 4 */,
+ 122 /* 5 */,
+ 120 /* 6 */,
+ 118 /* 7 */,
+ 116 /* 8 */,
+ 114 /* 9 */,
+ 112 /* 10 */,
+ 110 /* 11 */,
+ 108 /* 12 */,
+ 106 /* 13 */,
+ 104 /* 14 */,
+ 103 /* 15 */,
+ 102 /* 16 */,
+ 101 /* 17 */,
+ 100 /* 18/00-18/09 */,
+ 99 /* 18/10-18/19 */,
+ 98 /* 18/20-18/29 */,
+ 97 /* 18/30-18/39 */,
+ 96 /* 18/40-18/49 */,
+ 95 /* 18/50-18/59 */,
+ 94 /* 18/60-18/69 */,
+ 93 /* 18/70-18/79 */,
+ 92 /* 18/80-18/89 */,
+ 91 /* 18/90-18/99 */,
+ 90 /* 18/100-18/109 */,
+ 89 /* 18/110-18/119 */,
+ 88 /* 18/120-18/129 */,
+ 87 /* 18/130-18/139 */,
+ 86 /* 18/140-18/149 */,
+ 85 /* 18/150-18/159 */,
+ 84 /* 18/160-18/169 */,
+ 83 /* 18/170-18/179 */,
+ 82 /* 18/180-18/189 */,
+ 81 /* 18/190-18/199 */,
+ 80 /* 18/200-18/209 */,
+ 79 /* 18/210-18/219 */,
+ 78 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT) -- Magic devices
+ */
+byte adj_int_dev[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 4 /* 18/20-18/29 */,
+ 4 /* 18/30-18/39 */,
+ 5 /* 18/40-18/49 */,
+ 5 /* 18/50-18/59 */,
+ 6 /* 18/60-18/69 */,
+ 6 /* 18/70-18/79 */,
+ 7 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 9 /* 18/110-18/119 */,
+ 10 /* 18/120-18/129 */,
+ 11 /* 18/130-18/139 */,
+ 12 /* 18/140-18/149 */,
+ 13 /* 18/150-18/159 */,
+ 14 /* 18/160-18/169 */,
+ 15 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 17 /* 18/190-18/199 */,
+ 18 /* 18/200-18/209 */,
+ 19 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (WIS) -- Saving throw
+ */
+byte adj_wis_sav[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 3 /* 18/30-18/39 */,
+ 3 /* 18/40-18/49 */,
+ 4 /* 18/50-18/59 */,
+ 4 /* 18/60-18/69 */,
+ 5 /* 18/70-18/79 */,
+ 5 /* 18/80-18/89 */,
+ 6 /* 18/90-18/99 */,
+ 7 /* 18/100-18/109 */,
+ 8 /* 18/110-18/119 */,
+ 9 /* 18/120-18/129 */,
+ 10 /* 18/130-18/139 */,
+ 11 /* 18/140-18/149 */,
+ 12 /* 18/150-18/159 */,
+ 13 /* 18/160-18/169 */,
+ 14 /* 18/170-18/179 */,
+ 15 /* 18/180-18/189 */,
+ 16 /* 18/190-18/199 */,
+ 17 /* 18/200-18/209 */,
+ 18 /* 18/210-18/219 */,
+ 19 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- disarming
+ */
+byte adj_dex_dis[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 0 /* 8 */,
+ 0 /* 9 */,
+ 0 /* 10 */,
+ 0 /* 11 */,
+ 0 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 1 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 4 /* 18/00-18/09 */,
+ 4 /* 18/10-18/19 */,
+ 4 /* 18/20-18/29 */,
+ 4 /* 18/30-18/39 */,
+ 5 /* 18/40-18/49 */,
+ 5 /* 18/50-18/59 */,
+ 5 /* 18/60-18/69 */,
+ 6 /* 18/70-18/79 */,
+ 6 /* 18/80-18/89 */,
+ 7 /* 18/90-18/99 */,
+ 8 /* 18/100-18/109 */,
+ 8 /* 18/110-18/119 */,
+ 8 /* 18/120-18/129 */,
+ 8 /* 18/130-18/139 */,
+ 8 /* 18/140-18/149 */,
+ 9 /* 18/150-18/159 */,
+ 9 /* 18/160-18/169 */,
+ 9 /* 18/170-18/179 */,
+ 9 /* 18/180-18/189 */,
+ 9 /* 18/190-18/199 */,
+ 10 /* 18/200-18/209 */,
+ 10 /* 18/210-18/219 */,
+ 10 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (INT) -- disarming
+ */
+byte adj_int_dis[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 1 /* 8 */,
+ 1 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 2 /* 15 */,
+ 2 /* 16 */,
+ 2 /* 17 */,
+ 3 /* 18/00-18/09 */,
+ 3 /* 18/10-18/19 */,
+ 3 /* 18/20-18/29 */,
+ 4 /* 18/30-18/39 */,
+ 4 /* 18/40-18/49 */,
+ 5 /* 18/50-18/59 */,
+ 6 /* 18/60-18/69 */,
+ 7 /* 18/70-18/79 */,
+ 8 /* 18/80-18/89 */,
+ 9 /* 18/90-18/99 */,
+ 10 /* 18/100-18/109 */,
+ 10 /* 18/110-18/119 */,
+ 11 /* 18/120-18/129 */,
+ 12 /* 18/130-18/139 */,
+ 13 /* 18/140-18/149 */,
+ 14 /* 18/150-18/159 */,
+ 15 /* 18/160-18/169 */,
+ 16 /* 18/170-18/179 */,
+ 17 /* 18/180-18/189 */,
+ 18 /* 18/190-18/199 */,
+ 19 /* 18/200-18/209 */,
+ 19 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- bonus to ac (plus 128)
+ */
+byte adj_dex_ta[] =
+{
+ 128 + -4 /* 3 */,
+ 128 + -3 /* 4 */,
+ 128 + -2 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 1 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 1 /* 17 */,
+ 128 + 2 /* 18/00-18/09 */,
+ 128 + 2 /* 18/10-18/19 */,
+ 128 + 2 /* 18/20-18/29 */,
+ 128 + 2 /* 18/30-18/39 */,
+ 128 + 2 /* 18/40-18/49 */,
+ 128 + 3 /* 18/50-18/59 */,
+ 128 + 3 /* 18/60-18/69 */,
+ 128 + 3 /* 18/70-18/79 */,
+ 128 + 4 /* 18/80-18/89 */,
+ 128 + 5 /* 18/90-18/99 */,
+ 128 + 6 /* 18/100-18/109 */,
+ 128 + 7 /* 18/110-18/119 */,
+ 128 + 8 /* 18/120-18/129 */,
+ 128 + 9 /* 18/130-18/139 */,
+ 128 + 9 /* 18/140-18/149 */,
+ 128 + 10 /* 18/150-18/159 */,
+ 128 + 11 /* 18/160-18/169 */,
+ 128 + 12 /* 18/170-18/179 */,
+ 128 + 13 /* 18/180-18/189 */,
+ 128 + 14 /* 18/190-18/199 */,
+ 128 + 15 /* 18/200-18/209 */,
+ 128 + 15 /* 18/210-18/219 */,
+ 128 + 16 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- bonus to dam (plus 128)
+ */
+byte adj_str_td[] =
+{
+ 128 + -2 /* 3 */,
+ 128 + -2 /* 4 */,
+ 128 + -1 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 0 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 2 /* 17 */,
+ 128 + 2 /* 18/00-18/09 */,
+ 128 + 2 /* 18/10-18/19 */,
+ 128 + 3 /* 18/20-18/29 */,
+ 128 + 3 /* 18/30-18/39 */,
+ 128 + 3 /* 18/40-18/49 */,
+ 128 + 3 /* 18/50-18/59 */,
+ 128 + 3 /* 18/60-18/69 */,
+ 128 + 4 /* 18/70-18/79 */,
+ 128 + 5 /* 18/80-18/89 */,
+ 128 + 5 /* 18/90-18/99 */,
+ 128 + 6 /* 18/100-18/109 */,
+ 128 + 7 /* 18/110-18/119 */,
+ 128 + 8 /* 18/120-18/129 */,
+ 128 + 9 /* 18/130-18/139 */,
+ 128 + 10 /* 18/140-18/149 */,
+ 128 + 11 /* 18/150-18/159 */,
+ 128 + 12 /* 18/160-18/169 */,
+ 128 + 13 /* 18/170-18/179 */,
+ 128 + 14 /* 18/180-18/189 */,
+ 128 + 15 /* 18/190-18/199 */,
+ 128 + 16 /* 18/200-18/209 */,
+ 128 + 18 /* 18/210-18/219 */,
+ 128 + 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- bonus to hit (plus 128)
+ */
+byte adj_dex_th[] =
+{
+ 128 + -3 /* 3 */,
+ 128 + -2 /* 4 */,
+ 128 + -2 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + -1 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 0 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 2 /* 17 */,
+ 128 + 3 /* 18/00-18/09 */,
+ 128 + 3 /* 18/10-18/19 */,
+ 128 + 3 /* 18/20-18/29 */,
+ 128 + 3 /* 18/30-18/39 */,
+ 128 + 3 /* 18/40-18/49 */,
+ 128 + 4 /* 18/50-18/59 */,
+ 128 + 4 /* 18/60-18/69 */,
+ 128 + 4 /* 18/70-18/79 */,
+ 128 + 4 /* 18/80-18/89 */,
+ 128 + 5 /* 18/90-18/99 */,
+ 128 + 6 /* 18/100-18/109 */,
+ 128 + 7 /* 18/110-18/119 */,
+ 128 + 8 /* 18/120-18/129 */,
+ 128 + 9 /* 18/130-18/139 */,
+ 128 + 9 /* 18/140-18/149 */,
+ 128 + 10 /* 18/150-18/159 */,
+ 128 + 11 /* 18/160-18/169 */,
+ 128 + 12 /* 18/170-18/179 */,
+ 128 + 13 /* 18/180-18/189 */,
+ 128 + 14 /* 18/190-18/199 */,
+ 128 + 15 /* 18/200-18/209 */,
+ 128 + 15 /* 18/210-18/219 */,
+ 128 + 16 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- bonus to hit (plus 128)
+ */
+byte adj_str_th[] =
+{
+ 128 + -3 /* 3 */,
+ 128 + -2 /* 4 */,
+ 128 + -1 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 0 /* 15 */,
+ 128 + 0 /* 16 */,
+ 128 + 0 /* 17 */,
+ 128 + 1 /* 18/00-18/09 */,
+ 128 + 1 /* 18/10-18/19 */,
+ 128 + 1 /* 18/20-18/29 */,
+ 128 + 1 /* 18/30-18/39 */,
+ 128 + 1 /* 18/40-18/49 */,
+ 128 + 1 /* 18/50-18/59 */,
+ 128 + 1 /* 18/60-18/69 */,
+ 128 + 2 /* 18/70-18/79 */,
+ 128 + 3 /* 18/80-18/89 */,
+ 128 + 4 /* 18/90-18/99 */,
+ 128 + 5 /* 18/100-18/109 */,
+ 128 + 6 /* 18/110-18/119 */,
+ 128 + 7 /* 18/120-18/129 */,
+ 128 + 8 /* 18/130-18/139 */,
+ 128 + 9 /* 18/140-18/149 */,
+ 128 + 10 /* 18/150-18/159 */,
+ 128 + 11 /* 18/160-18/169 */,
+ 128 + 12 /* 18/170-18/179 */,
+ 128 + 13 /* 18/180-18/189 */,
+ 128 + 14 /* 18/190-18/199 */,
+ 128 + 15 /* 18/200-18/209 */,
+ 128 + 15 /* 18/210-18/219 */,
+ 128 + 16 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- weight limit in deca-pounds
+ */
+byte adj_str_wgt[] =
+{
+ 5 /* 3 */,
+ 6 /* 4 */,
+ 7 /* 5 */,
+ 8 /* 6 */,
+ 9 /* 7 */,
+ 10 /* 8 */,
+ 11 /* 9 */,
+ 12 /* 10 */,
+ 13 /* 11 */,
+ 14 /* 12 */,
+ 15 /* 13 */,
+ 16 /* 14 */,
+ 17 /* 15 */,
+ 18 /* 16 */,
+ 19 /* 17 */,
+ 20 /* 18/00-18/09 */,
+ 22 /* 18/10-18/19 */,
+ 24 /* 18/20-18/29 */,
+ 26 /* 18/30-18/39 */,
+ 28 /* 18/40-18/49 */,
+ 30 /* 18/50-18/59 */,
+ 31 /* 18/60-18/69 */,
+ 31 /* 18/70-18/79 */,
+ 32 /* 18/80-18/89 */,
+ 32 /* 18/90-18/99 */,
+ 33 /* 18/100-18/109 */,
+ 33 /* 18/110-18/119 */,
+ 34 /* 18/120-18/129 */,
+ 34 /* 18/130-18/139 */,
+ 35 /* 18/140-18/149 */,
+ 35 /* 18/150-18/159 */,
+ 36 /* 18/160-18/169 */,
+ 36 /* 18/170-18/179 */,
+ 37 /* 18/180-18/189 */,
+ 37 /* 18/190-18/199 */,
+ 38 /* 18/200-18/209 */,
+ 38 /* 18/210-18/219 */,
+ 39 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- weapon weight limit in pounds
+ */
+byte adj_str_hold[] =
+{
+ 4 /* 3 */,
+ 5 /* 4 */,
+ 6 /* 5 */,
+ 7 /* 6 */,
+ 8 /* 7 */,
+ 10 /* 8 */,
+ 12 /* 9 */,
+ 14 /* 10 */,
+ 16 /* 11 */,
+ 18 /* 12 */,
+ 20 /* 13 */,
+ 22 /* 14 */,
+ 24 /* 15 */,
+ 26 /* 16 */,
+ 28 /* 17 */,
+ 30 /* 18/00-18/09 */,
+ 30 /* 18/10-18/19 */,
+ 35 /* 18/20-18/29 */,
+ 40 /* 18/30-18/39 */,
+ 45 /* 18/40-18/49 */,
+ 50 /* 18/50-18/59 */,
+ 55 /* 18/60-18/69 */,
+ 60 /* 18/70-18/79 */,
+ 65 /* 18/80-18/89 */,
+ 70 /* 18/90-18/99 */,
+ 80 /* 18/100-18/109 */,
+ 80 /* 18/110-18/119 */,
+ 80 /* 18/120-18/129 */,
+ 80 /* 18/130-18/139 */,
+ 80 /* 18/140-18/149 */,
+ 90 /* 18/150-18/159 */,
+ 90 /* 18/160-18/169 */,
+ 90 /* 18/170-18/179 */,
+ 90 /* 18/180-18/189 */,
+ 90 /* 18/190-18/199 */,
+ 100 /* 18/200-18/209 */,
+ 100 /* 18/210-18/219 */,
+ 100 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- digging value
+ */
+byte adj_str_dig[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 1 /* 5 */,
+ 2 /* 6 */,
+ 3 /* 7 */,
+ 4 /* 8 */,
+ 4 /* 9 */,
+ 5 /* 10 */,
+ 5 /* 11 */,
+ 6 /* 12 */,
+ 6 /* 13 */,
+ 7 /* 14 */,
+ 7 /* 15 */,
+ 8 /* 16 */,
+ 8 /* 17 */,
+ 9 /* 18/00-18/09 */,
+ 10 /* 18/10-18/19 */,
+ 12 /* 18/20-18/29 */,
+ 15 /* 18/30-18/39 */,
+ 20 /* 18/40-18/49 */,
+ 25 /* 18/50-18/59 */,
+ 30 /* 18/60-18/69 */,
+ 35 /* 18/70-18/79 */,
+ 40 /* 18/80-18/89 */,
+ 45 /* 18/90-18/99 */,
+ 50 /* 18/100-18/109 */,
+ 55 /* 18/110-18/119 */,
+ 60 /* 18/120-18/129 */,
+ 65 /* 18/130-18/139 */,
+ 70 /* 18/140-18/149 */,
+ 75 /* 18/150-18/159 */,
+ 80 /* 18/160-18/169 */,
+ 85 /* 18/170-18/179 */,
+ 90 /* 18/180-18/189 */,
+ 95 /* 18/190-18/199 */,
+ 100 /* 18/200-18/209 */,
+ 100 /* 18/210-18/219 */,
+ 100 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (STR) -- help index into the "blow" table
+ */
+byte adj_str_blow[] =
+{
+ 3 /* 3 */,
+ 4 /* 4 */,
+ 5 /* 5 */,
+ 6 /* 6 */,
+ 7 /* 7 */,
+ 8 /* 8 */,
+ 9 /* 9 */,
+ 10 /* 10 */,
+ 11 /* 11 */,
+ 12 /* 12 */,
+ 13 /* 13 */,
+ 14 /* 14 */,
+ 15 /* 15 */,
+ 16 /* 16 */,
+ 17 /* 17 */,
+ 20 /* 18/00-18/09 */,
+ 30 /* 18/10-18/19 */,
+ 40 /* 18/20-18/29 */,
+ 50 /* 18/30-18/39 */,
+ 60 /* 18/40-18/49 */,
+ 70 /* 18/50-18/59 */,
+ 80 /* 18/60-18/69 */,
+ 90 /* 18/70-18/79 */,
+ 100 /* 18/80-18/89 */,
+ 110 /* 18/90-18/99 */,
+ 120 /* 18/100-18/109 */,
+ 130 /* 18/110-18/119 */,
+ 140 /* 18/120-18/129 */,
+ 150 /* 18/130-18/139 */,
+ 160 /* 18/140-18/149 */,
+ 170 /* 18/150-18/159 */,
+ 180 /* 18/160-18/169 */,
+ 190 /* 18/170-18/179 */,
+ 200 /* 18/180-18/189 */,
+ 210 /* 18/190-18/199 */,
+ 220 /* 18/200-18/209 */,
+ 230 /* 18/210-18/219 */,
+ 240 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- index into the "blow" table
+ */
+byte adj_dex_blow[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 0 /* 8 */,
+ 0 /* 9 */,
+ 1 /* 10 */,
+ 1 /* 11 */,
+ 1 /* 12 */,
+ 1 /* 13 */,
+ 1 /* 14 */,
+ 1 /* 15 */,
+ 1 /* 16 */,
+ 1 /* 17 */,
+ 1 /* 18/00-18/09 */,
+ 2 /* 18/10-18/19 */,
+ 2 /* 18/20-18/29 */,
+ 2 /* 18/30-18/39 */,
+ 2 /* 18/40-18/49 */,
+ 3 /* 18/50-18/59 */,
+ 3 /* 18/60-18/69 */,
+ 4 /* 18/70-18/79 */,
+ 4 /* 18/80-18/89 */,
+ 5 /* 18/90-18/99 */,
+ 6 /* 18/100-18/109 */,
+ 7 /* 18/110-18/119 */,
+ 8 /* 18/120-18/129 */,
+ 9 /* 18/130-18/139 */,
+ 10 /* 18/140-18/149 */,
+ 11 /* 18/150-18/159 */,
+ 12 /* 18/160-18/169 */,
+ 14 /* 18/170-18/179 */,
+ 16 /* 18/180-18/189 */,
+ 18 /* 18/190-18/199 */,
+ 20 /* 18/200-18/209 */,
+ 20 /* 18/210-18/219 */,
+ 20 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (DEX) -- chance of avoiding "theft" and "falling"
+ */
+byte adj_dex_safe[] =
+{
+ 0 /* 3 */,
+ 1 /* 4 */,
+ 2 /* 5 */,
+ 3 /* 6 */,
+ 4 /* 7 */,
+ 5 /* 8 */,
+ 5 /* 9 */,
+ 6 /* 10 */,
+ 6 /* 11 */,
+ 7 /* 12 */,
+ 7 /* 13 */,
+ 8 /* 14 */,
+ 8 /* 15 */,
+ 9 /* 16 */,
+ 9 /* 17 */,
+ 10 /* 18/00-18/09 */,
+ 10 /* 18/10-18/19 */,
+ 15 /* 18/20-18/29 */,
+ 15 /* 18/30-18/39 */,
+ 20 /* 18/40-18/49 */,
+ 25 /* 18/50-18/59 */,
+ 30 /* 18/60-18/69 */,
+ 35 /* 18/70-18/79 */,
+ 40 /* 18/80-18/89 */,
+ 45 /* 18/90-18/99 */,
+ 50 /* 18/100-18/109 */,
+ 60 /* 18/110-18/119 */,
+ 70 /* 18/120-18/129 */,
+ 80 /* 18/130-18/139 */,
+ 90 /* 18/140-18/149 */,
+ 100 /* 18/150-18/159 */,
+ 100 /* 18/160-18/169 */,
+ 100 /* 18/170-18/179 */,
+ 100 /* 18/180-18/189 */,
+ 100 /* 18/190-18/199 */,
+ 100 /* 18/200-18/209 */,
+ 100 /* 18/210-18/219 */,
+ 100 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (CON) -- base regeneration rate
+ */
+byte adj_con_fix[] =
+{
+ 0 /* 3 */,
+ 0 /* 4 */,
+ 0 /* 5 */,
+ 0 /* 6 */,
+ 0 /* 7 */,
+ 0 /* 8 */,
+ 0 /* 9 */,
+ 0 /* 10 */,
+ 0 /* 11 */,
+ 0 /* 12 */,
+ 0 /* 13 */,
+ 1 /* 14 */,
+ 1 /* 15 */,
+ 1 /* 16 */,
+ 1 /* 17 */,
+ 2 /* 18/00-18/09 */,
+ 2 /* 18/10-18/19 */,
+ 2 /* 18/20-18/29 */,
+ 2 /* 18/30-18/39 */,
+ 2 /* 18/40-18/49 */,
+ 3 /* 18/50-18/59 */,
+ 3 /* 18/60-18/69 */,
+ 3 /* 18/70-18/79 */,
+ 3 /* 18/80-18/89 */,
+ 3 /* 18/90-18/99 */,
+ 4 /* 18/100-18/109 */,
+ 4 /* 18/110-18/119 */,
+ 5 /* 18/120-18/129 */,
+ 6 /* 18/130-18/139 */,
+ 6 /* 18/140-18/149 */,
+ 7 /* 18/150-18/159 */,
+ 7 /* 18/160-18/169 */,
+ 8 /* 18/170-18/179 */,
+ 8 /* 18/180-18/189 */,
+ 8 /* 18/190-18/199 */,
+ 9 /* 18/200-18/209 */,
+ 9 /* 18/210-18/219 */,
+ 9 /* 18/220+ */
+};
+
+
+/*
+ * Stat Table (CON) -- extra half-hitpoints per level (plus 128)
+ */
+byte adj_con_mhp[] =
+{
+ 128 + -5 /* 3 */,
+ 128 + -3 /* 4 */,
+ 128 + -2 /* 5 */,
+ 128 + -1 /* 6 */,
+ 128 + 0 /* 7 */,
+ 128 + 0 /* 8 */,
+ 128 + 0 /* 9 */,
+ 128 + 0 /* 10 */,
+ 128 + 0 /* 11 */,
+ 128 + 0 /* 12 */,
+ 128 + 0 /* 13 */,
+ 128 + 0 /* 14 */,
+ 128 + 1 /* 15 */,
+ 128 + 1 /* 16 */,
+ 128 + 2 /* 17 */,
+ 128 + 3 /* 18/00-18/09 */,
+ 128 + 4 /* 18/10-18/19 */,
+ 128 + 4 /* 18/20-18/29 */,
+ 128 + 4 /* 18/30-18/39 */,
+ 128 + 4 /* 18/40-18/49 */,
+ 128 + 5 /* 18/50-18/59 */,
+ 128 + 6 /* 18/60-18/69 */,
+ 128 + 7 /* 18/70-18/79 */,
+ 128 + 8 /* 18/80-18/89 */,
+ 128 + 9 /* 18/90-18/99 */,
+ 128 + 10 /* 18/100-18/109 */,
+ 128 + 11 /* 18/110-18/119 */,
+ 128 + 12 /* 18/120-18/129 */,
+ 128 + 13 /* 18/130-18/139 */,
+ 128 + 14 /* 18/140-18/149 */,
+ 128 + 15 /* 18/150-18/159 */,
+ 128 + 16 /* 18/160-18/169 */,
+ 128 + 18 /* 18/170-18/179 */,
+ 128 + 20 /* 18/180-18/189 */,
+ 128 + 22 /* 18/190-18/199 */,
+ 128 + 25 /* 18/200-18/209 */,
+ 128 + 26 /* 18/210-18/219 */,
+ 128 + 27 /* 18/220+ */
+};
+
+
+/*
+ * This table is used to help calculate the number of blows the player can
+ * make in a single round of attacks (one player turn) with a normal weapon.
+ *
+ * This number ranges from a single blow/round for weak players to up to six
+ * blows/round for powerful warriors.
+ *
+ * Note that certain artifacts and ego-items give "bonus" blows/round.
+ *
+ * First, from the player class, we extract some values:
+ *
+ * Warrior --> num = 6; mul = 5; div = MAX(30, weapon_weight);
+ * Mage --> num = 4; mul = 2; div = MAX(40, weapon_weight);
+ * Priest --> num = 5; mul = 3; div = MAX(35, weapon_weight);
+ * Rogue --> num = 5; mul = 3; div = MAX(30, weapon_weight);
+ * Ranger --> num = 5; mul = 4; div = MAX(35, weapon_weight);
+ * Paladin --> num = 5; mul = 4; div = MAX(30, weapon_weight);
+ *
+ * To get "P", we look up the relevant "adj_str_blow[]" (see above),
+ * multiply it by "mul", and then divide it by "div", rounding down.
+ *
+ * To get "D", we look up the relevant "adj_dex_blow[]" (see above),
+ * note especially column 6 (DEX 18/101) and 11 (DEX 18/150).
+ *
+ * The player gets "blows_table[P][D]" blows/round, as shown below,
+ * up to a maximum of "num" blows/round, plus any "bonus" blows/round.
+ */
+byte blows_table[12][12] =
+{
+ /* P/D */
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11+ */
+
+ /* 0 */
+ { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3 },
+
+ /* 1 */
+ { 1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4 },
+
+ /* 2 */
+ { 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5 },
+
+ /* 3 */
+ { 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 5 },
+
+ /* 4 */
+ { 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5 },
+
+ /* 5 */
+ { 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 6 },
+
+ /* 6 */
+ { 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 6 },
+
+ /* 7 */
+ { 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6 },
+
+ /* 8 */
+ { 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6 },
+
+ /* 9 */
+ { 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6 },
+
+ /* 10 */
+ { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6 },
+
+ /* 11+ */
+ { 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6 },
+};
+
+
+s16b arena_monsters[MAX_ARENA_MONS] =
+{
+ 30, 43, 102, 118, 126, 149, 173,
+ 183, 188, 191, 216, 230, 238, 244,
+ 255, 262, 293, 297, 321, 349, 372,
+ 401, 415, 454, 464, 485, 538, 631,
+ 641
+};
+
+
+/*
+ * This table allows quick conversion from "speed" to "energy"
+ * The basic function WAS ((S>=110) ? (S-110) : (100 / (120-S)))
+ * Note that table access is *much* quicker than computation.
+ *
+ * Note that the table has been changed at high speeds. From
+ * "Slow (-40)" to "Fast (+30)" is pretty much unchanged, but
+ * at speeds above "Fast (+30)", one approaches an asymptotic
+ * effective limit of 50 energy per turn. This means that it
+ * is relatively easy to reach "Fast (+30)" and get about 40
+ * energy per turn, but then speed becomes very "expensive",
+ * and you must get all the way to "Fast (+50)" to reach the
+ * point of getting 45 energy per turn. After that point,
+ * further increases in speed are more or less pointless,
+ * except to balance out heavy inventory.
+ *
+ * Note that currently the fastest monster is "Fast (+30)".
+ *
+ * It should be possible to lower the energy threshold from
+ * 100 units to 50 units, though this may interact badly with
+ * the (compiled out) small random energy boost code. It may
+ * also tend to cause more "clumping" at high speeds.
+ */
+byte extract_energy[300] =
+{
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* S-50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* S-40 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* S-30 */ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
+ /* S-20 */ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ /* S-10 */ 5, 5, 5, 5, 6, 6, 7, 7, 8, 9,
+ /* Norm */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ /* F+10 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ /* F+20 */ 30, 31, 32, 33, 34, 35, 36, 36, 37, 37,
+ /* F+30 */ 38, 38, 39, 39, 40, 40, 40, 41, 41, 41,
+ /* F+40 */ 42, 42, 42, 43, 43, 43, 44, 44, 44, 44,
+ /* F+50 */ 45, 45, 45, 45, 45, 46, 46, 46, 46, 46,
+ /* F+60 */ 47, 47, 47, 47, 47, 48, 48, 48, 48, 48,
+ /* F+70 */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Fast */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+};
+
+
+
+
+/*
+ * Base experience levels, may be adjusted up for race and/or class
+ */
+s32b player_exp[PY_MAX_LEVEL] =
+{
+ 10,
+ 25,
+ 45,
+ 70,
+ 100,
+ 140,
+ 200,
+ 280,
+ 380,
+ 500,
+ 650,
+ 850,
+ 1100,
+ 1400,
+ 1800,
+ 2300,
+ 2900,
+ 3600,
+ 4400,
+ 5400,
+ 6800,
+ 8400,
+ 10200,
+ 12500,
+ 17500,
+ 25000,
+ 35000L,
+ 50000L,
+ 75000L,
+ 100000L,
+ 150000L,
+ 200000L,
+ 275000L,
+ 350000L,
+ 450000L,
+ 550000L,
+ 700000L,
+ 850000L,
+ 1000000L,
+ 1250000L,
+ 1500000L,
+ 1800000L,
+ 2100000L,
+ 2400000L,
+ 2700000L,
+ 3000000L,
+ 3500000L,
+ 4000000L,
+ 4500000L,
+ 5000000L
+};
+
+
+/*
+ * Player Sexes
+ *
+ * Title,
+ * Winner
+ */
+player_sex sex_info[MAX_SEXES] =
+{
+ {
+ "Female",
+ "Queen"
+ },
+
+{
+"Male",
+"King"
+},
+{
+"Neuter",
+"Ruler"
+}
+};
+
+/*
+ * Hack -- the "basic" color names (see "TERM_xxx")
+ */
+cptr color_names[16] =
+{
+ "Dark",
+ "White",
+ "Slate",
+ "Orange",
+ "Red",
+ "Green",
+ "Blue",
+ "Umber",
+ "Light Dark",
+ "Light Slate",
+ "Violet",
+ "Yellow",
+ "Light Red",
+ "Light Green",
+ "Light Blue",
+ "Light Umber",
+};
+
+
+/*
+ * Abbreviations of healthy stats
+ */
+cptr stat_names[6] =
+{
+ "STR", "INT", "WIS", "DEX", "CON", "CHR"
+};
+
+/*
+ * Abbreviations of damaged stats
+ */
+cptr stat_names_reduced[6] =
+{
+ "Str", "Int", "Wis", "Dex", "Con", "Chr"
+};
+
+
+/*
+ * Certain "screens" always use the main screen, including News, Birth,
+ * Dungeon, Tomb-stone, High-scores, Macros, Colors, Visuals, Options.
+ *
+ * Later, special flags may allow sub-windows to "steal" stuff from the
+ * main window, including File dump (help), File dump (artifacts, uniques),
+ * Character screen, Small scale map, Previous Messages, Store screen, etc.
+ *
+ * The "ctrl-i" (tab) command flips the "Display inven/equip" and "Display
+ * equip/inven" flags for all windows.
+ *
+ * The "ctrl-g" command (or pseudo-command) should perhaps grab a snapshot
+ * of the main screen into any interested windows.
+ */
+cptr window_flag_desc[32] =
+{
+ "Display inven/equip",
+ "Display equip/inven",
+ NULL,
+ "Display character",
+ "Show visible monsters",
+ "Display IRC messages",
+ "Display messages",
+ "Display overhead view",
+ "Display monster recall",
+ "Display object recall",
+ NULL,
+ "Display snap-shot",
+ NULL,
+ NULL,
+ "Display borg messages",
+ "Display borg status",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/*
+ * Available Options
+ *
+ * Option Screen Sets:
+ *
+ * Set 1: User Interface
+ * Set 2: Disturbance
+ * Set 3: Inventory
+ * Set 4: Game Play
+ * Set 5: ToME
+ * Set 6: Birth
+ *
+ * Note that bits 28-31 of set 0 are currently unused.
+ */
+option_type option_info[] =
+{
+ /*** User-Interface ***/
+
+ { &rogue_like_commands, FALSE, 1, 0,
+ "rogue_like_commands", "Rogue-like commands" },
+
+ { &quick_messages, TRUE, 1, 1,
+ "quick_messages", "Activate quick messages" },
+
+ { &other_query_flag, FALSE, 1, 2,
+ "other_query_flag", "Prompt for various information" },
+
+ { &carry_query_flag, FALSE, 1, 3,
+ "carry_query_flag", "Prompt before picking things up" },
+
+ { &use_old_target, FALSE, 1, 4,
+ "use_old_target", "Use old target by default" },
+
+ { &always_pickup, FALSE, 1, 5,
+ "always_pickup", "Pick things up by default" },
+
+ { &prompt_pickup_heavy, TRUE, 1, 6,
+ "prompt_pickup_heavy", "Prompt before picking up heavy objects" },
+
+ { &always_repeat, TRUE, 1, 7,
+ "always_repeat", "Repeat obvious commands" },
+
+ { &depth_in_feet, FALSE, 1, 8,
+ "depth_in_feet", "Show dungeon level in feet" },
+
+ { &stack_force_notes, TRUE, 1, 9,
+ "stack_force_notes", "Merge inscriptions when stacking" },
+
+ { &stack_force_costs, FALSE, 1, 10,
+ "stack_force_costs", "Merge discounts when stacking" },
+
+ { &show_labels, TRUE, 1, 11,
+ "show_labels", "Show labels in object listings" },
+
+ { &show_weights, TRUE, 1, 12,
+ "show_weights", "Show weights in object listings" },
+
+ { &show_inven_graph, TRUE, 1, 13,
+ "show_inven_graph", "Show graphics in inventory list" },
+
+ { &show_equip_graph, TRUE, 1, 14,
+ "show_equip_graph", "Show graphics in equipment list" },
+
+ { &show_store_graph, TRUE, 1, 15,
+ "show_store_graph", "Show graphics in stores" },
+
+ { &show_choices, TRUE, 1, 16,
+ "show_choices", "Show choices in certain sub-windows" },
+
+ { &show_details, TRUE, 1, 17,
+ "show_details", "Show details in certain sub-windows" },
+
+ { &ring_bell, FALSE, 1, 18,
+ "ring_bell", "Audible bell (on errors, etc)" },
+ /* Changed to default to FALSE -- it's so extremely annoying!!! -TY */
+
+ { &use_color, TRUE, 1, 19,
+ "use_color", "Use color if possible (slow)" },
+
+ /*** Disturbance ***/
+
+ { &find_ignore_stairs, FALSE, 2, 0,
+ "find_ignore_stairs", "Run past stairs" },
+
+ { &find_ignore_doors, TRUE, 2, 1,
+ "find_ignore_doors", "Run through open doors" },
+
+ { &find_cut, FALSE, 2, 2,
+ "find_cut", "Run past known corners" },
+
+ { &find_examine, TRUE, 2, 3,
+ "find_examine", "Run into potential corners" },
+
+ { &disturb_move, FALSE, 2, 4,
+ "disturb_move", "Disturb whenever any monster moves" },
+
+ { &disturb_near, TRUE, 2, 5,
+ "disturb_near", "Disturb whenever viewable monster moves" },
+
+ { &disturb_panel, TRUE, 2, 6,
+ "disturb_panel", "Disturb whenever map panel changes" },
+
+ { &disturb_detect, TRUE, 2, 21,
+ "disturb_detect", "Disturb whenever leaving trap-detected area" },
+
+ { &disturb_state, TRUE, 2, 7,
+ "disturb_state", "Disturb whenever player state changes" },
+
+ { &disturb_minor, TRUE, 2, 8,
+ "disturb_minor", "Disturb whenever boring things happen" },
+
+ { &disturb_other, FALSE, 2, 9,
+ "disturb_other", "Disturb whenever random things happen" },
+
+ { &alert_hitpoint, FALSE, 2, 10,
+ "alert_hitpoint", "Alert user to critical hitpoints" },
+
+ { &alert_failure, FALSE, 2, 11,
+ "alert_failure", "Alert user to various failures" },
+
+ { &last_words, TRUE, 2, 12,
+ "last_words", "Get last words when the character dies" },
+
+ { &speak_unique, TRUE, 2, 13,
+ "speak_unique", "Allow shopkeepers and uniques to speak" },
+
+ { &auto_destroy, TRUE, 2, 14,
+ "auto_destroy", "No query to destroy known worthless items" },
+
+ { &wear_confirm, TRUE, 2, 15,
+ "confirm_wear", "Confirm to wear/wield known cursed items" },
+
+ { &confirm_stairs, FALSE, 2, 16,
+ "confirm_stairs", "Prompt before exiting a dungeon level" },
+
+ { &disturb_pets, FALSE, 2, 17,
+ "disturb_pets", "Disturb when visible pets move" },
+
+#ifdef ALLOW_EASY_OPEN
+ { &easy_open, TRUE, 2, 18,
+ "easy_open", "Automatically open doors" },
+#endif /* ALLOW_EASY_OPEN */
+
+#ifdef ALLOW_EASY_DISARM
+ { &easy_disarm, TRUE, 2, 19,
+ "easy_disarm", "Automatically disarm traps" },
+#endif /* ALLOW_EASY_DISARM */
+
+ { &easy_tunnel, FALSE, 2, 20,
+ "easy_tunnel", "Automatically tunnel walls" },
+
+ /*** Game-Play ***/
+
+ { &auto_haggle, TRUE, 3, 0,
+ "auto_haggle", "Auto-haggle in stores" },
+
+ { &auto_scum, TRUE, 3, 1,
+ "auto_scum", "Auto-scum for good levels" },
+
+ { &stack_allow_items, TRUE, 3, 2,
+ "stack_allow_items", "Allow weapons and armour to stack" },
+
+ { &stack_allow_wands, TRUE, 3, 3,
+ "stack_allow_wands", "Allow wands/staffs/rods to stack" },
+
+ { &expand_look, FALSE, 3, 4,
+ "expand_look", "Expand the power of the look command" },
+
+ { &expand_list, FALSE, 3, 5,
+ "expand_list", "Expand the power of the list commands" },
+
+ { &view_perma_grids, TRUE, 3, 6,
+ "view_perma_grids", "Map remembers all perma-lit grids" },
+
+ { &view_torch_grids, FALSE, 3, 7,
+ "view_torch_grids", "Map remembers all torch-lit grids" },
+
+ { &monster_lite, TRUE, 3, 19,
+ "monster_lite", "Allow some monsters to carry light" },
+
+ { &dungeon_align, TRUE, 3, 8,
+ "dungeon_align", "Generate dungeons with aligned rooms" },
+
+ { &dungeon_stair, TRUE, 3, 9,
+ "dungeon_stair", "Generate dungeons with connected stairs" },
+
+ { &flow_by_sound, FALSE, 3, 10,
+ "flow_by_sound", "Monsters chase current location (v.slow)" },
+
+ { &flow_by_smell, FALSE, 3, 11,
+ "flow_by_smell", "Monsters chase recent locations (v.slow)" },
+
+ { &player_symbols, FALSE, 3, 12,
+ "player_symbols", "Use special symbols for the player char"},
+
+ { &plain_descriptions, TRUE, 3, 13,
+ "plain_descriptions", "Plain object descriptions" },
+
+ { &smart_learn, FALSE, 3, 14,
+ "smart_learn", "Monsters learn from their mistakes" },
+
+ { &smart_cheat, FALSE, 3, 15,
+ "smart_cheat", "Monsters exploit players weaknesses" },
+
+ { &stupid_monsters, FALSE, 3, 16,
+ "stupid_monsters", "Monsters behave stupidly" },
+
+ { &small_levels, TRUE, 3, 17,
+ "small_levels", "Allow unusually small dungeon levels" },
+
+ { &empty_levels, TRUE, 3, 18,
+ "empty_levels", "Allow empty 'arena' levels" },
+
+ /*** Efficiency ***/
+
+ { &view_reduce_lite, FALSE, 4, 0,
+ "view_reduce_lite", "Reduce lite-radius when running" },
+
+ { &view_reduce_view, FALSE, 4, 1,
+ "view_reduce_view", "Reduce view-radius in town" },
+
+ { &avoid_abort, FALSE, 4, 2,
+ "avoid_abort", "Avoid checking for user abort" },
+
+ { &avoid_shimmer, FALSE, 4, 17,
+ "avoid_shimmer", "Avoid extra shimmering (fast)" },
+
+ { &avoid_other, FALSE, 4, 3,
+ "avoid_other", "Avoid processing special colors (fast)" },
+
+ { &flush_failure, TRUE, 4, 4,
+ "flush_failure", "Flush input on various failures" },
+
+ { &flush_disturb, FALSE, 4, 5,
+ "flush_disturb", "Flush input whenever disturbed" },
+
+ { &flush_command, FALSE, 4, 6,
+ "flush_command", "Flush input before every command" },
+
+ { &fresh_before, TRUE, 4, 7,
+ "fresh_before", "Flush output before every command" },
+
+ { &fresh_after, FALSE, 4, 8,
+ "fresh_after", "Flush output after every command" },
+
+ { &fresh_message, FALSE, 4, 9,
+ "fresh_message", "Flush output after every message" },
+
+ { &compress_savefile, TRUE, 4, 10,
+ "compress_savefile", "Compress messages in savefiles" },
+
+ { &hilite_player, FALSE, 4, 11,
+ "hilite_player", "Hilite the player with the cursor" },
+
+ { &view_yellow_lite, FALSE, 4, 12,
+ "view_yellow_lite", "Use special colors for torch-lit grids" },
+
+ { &view_bright_lite, FALSE, 4, 13,
+ "view_bright_lite", "Use special colors for 'viewable' grids" },
+
+ { &view_granite_lite, FALSE, 4, 14,
+ "view_granite_lite", "Use special colors for wall grids (slow)" },
+
+ { &view_special_lite, FALSE, 4, 15,
+ "view_special_lite", "Use special colors for floor grids (slow)" },
+
+ { &center_player, FALSE, 4, 16,
+ "center_player", "Center the view on the player (very slow)" },
+
+ /*** ToME options ***/
+
+#if 0 /* It's controlled by insanity instead :) - pelpel */
+
+ { &flavored_attacks, TRUE, 5, 0,
+ "flavored_attacks", "Show silly messages when fighting" },
+
+#endif /* 0 */
+
+ { &option_ingame_help, TRUE, 5, 1,
+ "ingame_help", "Ingame contextual help" },
+
+ { &exp_need, FALSE, 5, 2,
+ "exp_need", "Show the experience needed for next level" },
+
+ { &autoload_old_colors, FALSE, 5, 3,
+ "old_colors", "Use the old(Z) coloring scheme(reload the game)" },
+
+ { &auto_more, FALSE, 5, 4,
+ "auto_more", "Automatically clear '-more-' prompts" },
+
+ { &player_char_health, TRUE, 5, 6,
+ "player_char_health", "Player char represent his/her health" },
+
+ { &linear_stats, TRUE, 5, 7,
+ "linear_stats", "Stats are represented in a linear way" },
+
+ { &inventory_no_move, FALSE, 5, 8,
+ "inventory_no_move", "In option windows, just omit the select char" },
+
+
+ /*** Birth Options ***/
+
+#if 0 /* XXX free -- no more used */
+ { &vanilla_town, FALSE, 6, 0,
+ "vanilla_town", "Use 'vanilla' town without quests and wilderness" },
+#endif
+
+ { &maximize, TRUE, 6, 1,
+ "maximize", "Maximise stats" },
+
+ { &preserve, TRUE, 6, 2,
+ "preserve", "Preserve artifacts" },
+
+ { &autoroll, TRUE, 6, 3,
+ "autoroll", "Specify 'minimal' stats" },
+
+ { &point_based, FALSE, 6, 17,
+ "point_based", "Generate character using a point system" },
+#if 0
+ { &special_lvls, TRUE, 6, 4,
+ "special_lvls", "Allow the use of special, unique, levels" },
+#endif
+ { &permanent_levels, FALSE, 6, 5,
+ "permanent_levels", "Generate persistent dungeons [EXPERIMENTAL]" },
+
+ { &ironman_rooms, FALSE, 6, 6,
+ "ironman_rooms", "Always generate very unusual rooms" },
+
+ { &take_notes, TRUE, 6, 7,
+ "take_notes", "Allow notes to be written to a file" },
+
+ { &auto_notes, TRUE, 6, 8,
+ "auto_notes", "Automatically note important events" },
+
+#if 0 /* when Ill get some ideas */
+ { &rand_birth, FALSE, 6, 9,
+ "rand_birth", "Random present at birth" },
+#endif
+ { &fast_autoroller, FALSE, 6, 10,
+ "fast_autoroller", "Fast autoroller(NOT on multiuser systems)" },
+
+ { &joke_monsters, FALSE, 6, 14,
+ "joke_monsters", "Allow use of some 'joke' monsters" },
+
+ /* XXX */
+
+ { &always_small_level, FALSE, 6, 16,
+ "always_small_level", "Always make small levels" },
+
+ { &fate_option, TRUE, 6, 18,
+ "fate_option", "You can receive fates, good or bad" },
+
+ /* XXX 17 is used BEFORE */
+
+ /*** Stacking ***/
+
+ { &testing_stack, TRUE, 7, 0,
+ "testing_stack", "Allow objects to stack on floor" },
+
+ { &testing_carry, TRUE, 7, 1,
+ "testing_carry", "Allow monsters to carry objects" },
+
+
+ /*** End of Table ***/
+
+ { NULL, 0, 0, 0,
+ NULL, NULL }
+};
+
+
+cptr chaos_patrons[MAX_PATRON] =
+{
+ "Slortar",
+ "Mabelode",
+ "Chardros",
+ "Hionhurn",
+ "Xiombarg",
+
+ "Pyaray",
+ "Balaan",
+ "Arioch",
+ "Eequor",
+ "Narjhan",
+
+ "Balo",
+ "Khorne",
+ "Slaanesh",
+ "Nurgle",
+ "Tzeentch",
+
+ "Khaine"
+};
+
+int chaos_stats[MAX_PATRON] =
+{
+ A_CON, /* Slortar */
+ A_CON, /* Mabelode */
+ A_STR, /* Chardros */
+ A_STR, /* Hionhurn */
+ A_STR, /* Xiombarg */
+
+ A_INT, /* Pyaray */
+ A_STR, /* Balaan */
+ A_INT, /* Arioch */
+ A_CON, /* Eequor */
+ A_CHR, /* Narjhan */
+
+ -1, /* Balo */
+ A_STR, /* Khorne */
+ A_CHR, /* Slaanesh */
+ A_CON, /* Nurgle */
+ A_INT, /* Tzeentch */
+
+ A_STR, /* Khaine */
+};
+
+
+
+
+int chaos_rewards[MAX_PATRON][20] =
+{
+ /* Slortar the Old: */
+ {
+ REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_SLF,
+ REW_POLY_SLF, REW_POLY_SLF, REW_GAIN_ABL, REW_GAIN_ABL, REW_GAIN_EXP,
+ REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_AUGM_ABL, REW_AUGM_ABL
+ },
+
+ /* Mabelode the Faceless: */
+ {
+ REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_H_SUMMON, REW_SUMMON_M,
+ REW_SUMMON_M, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_WND,
+ REW_POLY_SLF, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_ABL, REW_SER_UNDE,
+ REW_CHAOS_WP, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_GOOD_OBS, REW_GOOD_OBS
+ },
+
+ /* Chardros the Reaper: */
+ {
+ REW_WRATH, REW_WRATH, REW_HURT_LOT, REW_PISS_OFF, REW_H_SUMMON,
+ REW_SUMMON_M, REW_IGNORE, REW_IGNORE, REW_DESTRUCT, REW_SER_UNDE,
+ REW_GENOCIDE, REW_MASS_GEN, REW_MASS_GEN, REW_DISPEL_C, REW_GOOD_OBJ,
+ REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_AUGM_ABL, REW_AUGM_ABL
+ },
+
+ /* Hionhurn the Executioner: */
+ {
+ REW_WRATH, REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL,
+ REW_IGNORE, REW_IGNORE, REW_SER_UNDE, REW_DESTRUCT, REW_GENOCIDE,
+ REW_MASS_GEN, REW_MASS_GEN, REW_HEAL_FUL, REW_GAIN_ABL, REW_GAIN_ABL,
+ REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_AUGM_ABL, REW_AUGM_ABL
+ },
+
+ /* Xiombarg the Sword-Queen: */
+ {
+ REW_TY_CURSE, REW_TY_CURSE, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND, REW_POLY_WND,
+ REW_GENOCIDE, REW_DISPEL_C, REW_GOOD_OBJ, REW_GOOD_OBJ, REW_SER_MONS,
+ REW_GAIN_ABL, REW_CHAOS_WP, REW_GAIN_EXP, REW_AUGM_ABL, REW_GOOD_OBS
+ },
+
+
+ /* Pyaray the Tentacled Whisperer of Impossible Secretes: */
+ {
+ REW_WRATH, REW_TY_CURSE, REW_PISS_OFF, REW_H_SUMMON, REW_H_SUMMON,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_POLY_SLF,
+ REW_POLY_SLF, REW_SER_DEMO, REW_HEAL_FUL, REW_GAIN_ABL, REW_GAIN_ABL,
+ REW_CHAOS_WP, REW_DO_HAVOC, REW_GOOD_OBJ, REW_GREA_OBJ, REW_GREA_OBS
+ },
+
+ /* Balaan the Grim: */
+ {
+ REW_TY_CURSE, REW_HURT_LOT, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL,
+ REW_SUMMON_M, REW_LOSE_EXP, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND,
+ REW_SER_UNDE, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_EXP, REW_GAIN_EXP,
+ REW_CHAOS_WP, REW_GOOD_OBJ, REW_GOOD_OBS, REW_GREA_OBS, REW_AUGM_ABL
+ },
+
+ /* Arioch, Duke of Hell: */
+ {
+ REW_WRATH, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_EXP, REW_H_SUMMON,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF,
+ REW_POLY_SLF, REW_MASS_GEN, REW_SER_DEMO, REW_HEAL_FUL, REW_CHAOS_WP,
+ REW_CHAOS_WP, REW_GOOD_OBJ, REW_GAIN_EXP, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Eequor, Blue Lady of Dismay: */
+ {
+ REW_WRATH, REW_TY_CURSE, REW_PISS_OFF, REW_CURSE_WP, REW_RUIN_ABL,
+ REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_WND,
+ REW_GOOD_OBJ, REW_GOOD_OBJ, REW_SER_MONS, REW_HEAL_FUL, REW_GAIN_EXP,
+ REW_GAIN_ABL, REW_CHAOS_WP, REW_GOOD_OBS, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Narjhan, Lord of Beggars: */
+ {
+ REW_WRATH, REW_CURSE_AR, REW_CURSE_WP, REW_CURSE_WP, REW_CURSE_AR,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF,
+ REW_POLY_WND, REW_HEAL_FUL, REW_HEAL_FUL, REW_GAIN_EXP, REW_AUGM_ABL,
+ REW_GOOD_OBJ, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_GREA_OBS
+ },
+
+ /* Balo the Jester: */
+ {
+ REW_WRATH, REW_SER_DEMO, REW_CURSE_WP, REW_CURSE_AR, REW_LOSE_EXP,
+ REW_GAIN_ABL, REW_LOSE_ABL, REW_POLY_WND, REW_POLY_SLF, REW_IGNORE,
+ REW_DESTRUCT, REW_MASS_GEN, REW_CHAOS_WP, REW_GREA_OBJ, REW_HURT_LOT,
+ REW_AUGM_ABL, REW_RUIN_ABL, REW_H_SUMMON, REW_GREA_OBS, REW_AUGM_ABL
+ },
+
+ /* Khorne the Bloodgod: */
+ {
+ REW_WRATH, REW_HURT_LOT, REW_HURT_LOT, REW_H_SUMMON, REW_H_SUMMON,
+ REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_SER_MONS, REW_SER_DEMO,
+ REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GOOD_OBJ,
+ REW_CHAOS_WP, REW_GOOD_OBS, REW_GOOD_OBS, REW_GREA_OBJ, REW_GREA_OBS
+ },
+
+ /* Slaanesh: */
+ {
+ REW_WRATH, REW_PISS_OFF, REW_PISS_OFF, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_POLY_WND, REW_SER_DEMO,
+ REW_POLY_SLF, REW_HEAL_FUL, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GAIN_EXP,
+ REW_GAIN_EXP, REW_CHAOS_WP, REW_GAIN_ABL, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Nurgle: */
+ {
+ REW_WRATH, REW_PISS_OFF, REW_HURT_LOT, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_LOSE_EXP, REW_IGNORE, REW_IGNORE, REW_IGNORE, REW_POLY_SLF,
+ REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_GOOD_OBJ, REW_GAIN_ABL,
+ REW_GAIN_ABL, REW_SER_UNDE, REW_CHAOS_WP, REW_GREA_OBJ, REW_AUGM_ABL
+ },
+
+ /* Tzeentch: */
+ {
+ REW_WRATH, REW_CURSE_WP, REW_CURSE_AR, REW_RUIN_ABL, REW_LOSE_ABL,
+ REW_LOSE_EXP, REW_IGNORE, REW_POLY_SLF, REW_POLY_SLF, REW_POLY_SLF,
+ REW_POLY_SLF, REW_POLY_WND, REW_HEAL_FUL, REW_CHAOS_WP, REW_GREA_OBJ,
+ REW_GAIN_ABL, REW_GAIN_ABL, REW_GAIN_EXP, REW_GAIN_EXP, REW_AUGM_ABL
+ },
+
+ /* Khaine: */
+ {
+ REW_WRATH, REW_HURT_LOT, REW_PISS_OFF, REW_LOSE_ABL, REW_LOSE_EXP,
+ REW_IGNORE, REW_IGNORE, REW_DISPEL_C, REW_DO_HAVOC, REW_DO_HAVOC,
+ REW_POLY_SLF, REW_POLY_SLF, REW_GAIN_EXP, REW_GAIN_ABL, REW_GAIN_ABL,
+ REW_SER_MONS, REW_GOOD_OBJ, REW_CHAOS_WP, REW_GREA_OBJ, REW_GOOD_OBS
+ }
+};
+
+/* Names used for random artifact name generation */
+cptr artifact_names_list =
+ "adanedhel\n"
+ "adurant\n"
+ "aeglos\n"
+ "aegnor\n"
+ "aelin\n"
+ "aeluin\n"
+ "aerandir\n"
+ "aerin\n"
+ "agarwaen\n"
+ "aglareb\n"
+ "aglarond\n"
+ "aglon\n"
+ "ainulindale\n"
+ "ainur\n"
+ "alcarinque\n"
+ "aldaron\n"
+ "aldudenie\n"
+ "almaren\n"
+ "alqualonde\n"
+ "aman\n"
+ "amandil\n"
+ "amarie\n"
+ "amarth\n"
+ "amlach\n"
+ "amon\n"
+ "amras\n"
+ "amrod\n"
+ "anach\n"
+ "anar\n"
+ "anarion\n"
+ "ancalagon\n"
+ "ancalimon\n"
+ "anarrima\n"
+ "andor\n"
+ "andram\n"
+ "androth\n"
+ "anduin\n"
+ "andunie\n"
+ "anfauglir\n"
+ "anfauglith\n"
+ "angainor\n"
+ "angband\n"
+ "anghabar\n"
+ "anglachel\n"
+ "angrenost\n"
+ "angrim\n"
+ "angrist\n"
+ "angrod\n"
+ "anguirel\n"
+ "annael\n"
+ "annatar\n"
+ "annon\n"
+ "annuminas\n"
+ "apanonar\n"
+ "aradan\n"
+ "aragorn\n"
+ "araman\n"
+ "aranel\n"
+ "aranruth\n"
+ "aranwe\n"
+ "aras\n"
+ "aratan\n"
+ "aratar\n"
+ "arathorn\n"
+ "arda\n"
+ "ard-galen\n"
+ "aredhel\n"
+ "ar-feiniel\n"
+ "argonath\n"
+ "arien\n"
+ "armenelos\n"
+ "arminas\n"
+ "arnor\n"
+ "aros\n"
+ "arossiach\n"
+ "arthad\n"
+ "arvernien\n"
+ "arwen\n"
+ "ascar\n"
+ "astaldo\n"
+ "atalante\n"
+ "atanamir\n"
+ "atanatari\n"
+ "atani\n"
+ "aule\n"
+ "avallone\n"
+ "avari\n"
+ "avathar\n"
+ "balan\n"
+ "balar\n"
+ "balrog\n"
+ "barad\n"
+ "baragund\n"
+ "barahir\n"
+ "baran\n"
+ "baranduin\n"
+ "bar\n"
+ "bauglir\n"
+ "beleg\n"
+ "belegaer\n"
+ "belegost\n"
+ "belegund\n"
+ "beleriand\n"
+ "belfalas\n"
+ "belthil\n"
+ "belthronding\n"
+ "beor\n"
+ "beraid\n"
+ "bereg\n"
+ "beren\n"
+ "boromir\n"
+ "boron\n"
+ "bragollach\n"
+ "brandir\n"
+ "bregolas\n"
+ "bregor\n"
+ "brethil\n"
+ "brilthor\n"
+ "brithiach\n"
+ "brithombar\n"
+ "brithon\n"
+ "cabed\n"
+ "calacirya\n"
+ "calaquendi\n"
+ "calenardhon\n"
+ "calion\n"
+ "camlost\n"
+ "caragdur\n"
+ "caranthir\n"
+ "carcharoth\n"
+ "cardolan\n"
+ "carnil\n"
+ "celeborn\n"
+ "celebrant\n"
+ "celebrimbor\n"
+ "celebrindal\n"
+ "celebros\n"
+ "celegorm\n"
+ "celon\n"
+ "cirdan\n"
+ "cirith\n"
+ "cirth\n"
+ "ciryatan\n"
+ "ciryon\n"
+ "coimas\n"
+ "corollaire\n"
+ "crissaegrim\n"
+ "cuarthal\n"
+ "cuivienen\n"
+ "culurien\n"
+ "curufin\n"
+ "curufinwe\n"
+ "curunir\n"
+ "cuthalion\n"
+ "daedeloth\n"
+ "daeron\n"
+ "dagnir\n"
+ "dagor\n"
+ "dagorlad\n"
+ "dairuin\n"
+ "danwedh\n"
+ "delduwath\n"
+ "denethor\n"
+ "dimbar\n"
+ "dimrost\n"
+ "dinen\n"
+ "dior\n"
+ "dirnen\n"
+ "dolmed\n"
+ "doriath\n"
+ "dorlas\n"
+ "dorthonion\n"
+ "draugluin\n"
+ "drengist\n"
+ "duath\n"
+ "duinath\n"
+ "duilwen\n"
+ "dunedain\n"
+ "dungortheb\n"
+ "earendil\n"
+ "earendur\n"
+ "earnil\n"
+ "earnur\n"
+ "earrame\n"
+ "earwen\n"
+ "echor\n"
+ "echoriath\n"
+ "ecthelion\n"
+ "edain\n"
+ "edrahil\n"
+ "eglador\n"
+ "eglarest\n"
+ "eglath\n"
+ "eilinel\n"
+ "eithel\n"
+ "ekkaia\n"
+ "elbereth\n"
+ "eldalie\n"
+ "eldalieva\n"
+ "eldamar\n"
+ "eldar\n"
+ "eledhwen\n"
+ "elemmire\n"
+ "elende\n"
+ "elendil\n"
+ "elendur\n"
+ "elenna\n"
+ "elentari\n"
+ "elenwe\n"
+ "elerrina\n"
+ "elleth\n"
+ "elmoth\n"
+ "elostirion\n"
+ "elrond\n"
+ "elros\n"
+ "elu\n"
+ "eluchil\n"
+ "elured\n"
+ "elurin\n"
+ "elwe\n"
+ "elwing\n"
+ "emeldir\n"
+ "endor\n"
+ "engrin\n"
+ "engwar\n"
+ "eol\n"
+ "eonwe\n"
+ "ephel\n"
+ "erchamion\n"
+ "ereb\n"
+ "ered\n"
+ "erech\n"
+ "eregion\n"
+ "ereinion\n"
+ "erellont\n"
+ "eressea\n"
+ "eriador\n"
+ "eru\n"
+ "esgalduin\n"
+ "este\n"
+ "estel\n"
+ "estolad\n"
+ "ethir\n"
+ "ezellohar\n"
+ "faelivrin\n"
+ "falas\n"
+ "falathar\n"
+ "falathrim\n"
+ "falmari\n"
+ "faroth\n"
+ "fauglith\n"
+ "feanor\n"
+ "feanturi\n"
+ "felagund\n"
+ "finarfin\n"
+ "finduilas\n"
+ "fingolfin\n"
+ "fingon\n"
+ "finwe\n"
+ "firimar\n"
+ "formenos\n"
+ "fornost\n"
+ "frodo\n"
+ "fuin\n"
+ "fuinur\n"
+ "gabilgathol\n"
+ "galad\n"
+ "galadriel\n"
+ "galathilion\n"
+ "galdor\n"
+ "galen\n"
+ "galvorn\n"
+ "gandalf\n"
+ "gaurhoth\n"
+ "gelion\n"
+ "gelmir\n"
+ "gelydh\n"
+ "gil\n"
+ "gildor\n"
+ "giliath\n"
+ "ginglith\n"
+ "girith\n"
+ "glaurung\n"
+ "glingal\n"
+ "glirhuin\n"
+ "gloredhel\n"
+ "glorfindel\n"
+ "golodhrim\n"
+ "gondolin\n"
+ "gondor\n"
+ "gonnhirrim\n"
+ "gorgoroth\n"
+ "gorlim\n"
+ "gorthaur\n"
+ "gorthol\n"
+ "gothmog\n"
+ "guilin\n"
+ "guinar\n"
+ "guldur\n"
+ "gundor\n"
+ "gurthang\n"
+ "gwaith\n"
+ "gwareth\n"
+ "gwindor\n"
+ "hadhodrond\n"
+ "hador\n"
+ "haladin\n"
+ "haldad\n"
+ "haldan\n"
+ "haldar\n"
+ "haldir\n"
+ "haleth\n"
+ "halmir\n"
+ "handir\n"
+ "harad\n"
+ "hareth\n"
+ "hathaldir\n"
+ "hathol\n"
+ "haudh\n"
+ "helcar\n"
+ "helcaraxe\n"
+ "helevorn\n"
+ "helluin\n"
+ "herumor\n"
+ "herunumen\n"
+ "hildorien\n"
+ "himlad\n"
+ "himring\n"
+ "hirilorn\n"
+ "hisilome\n"
+ "hithaeglir\n"
+ "hithlum\n"
+ "hollin\n"
+ "huan\n"
+ "hunthor\n"
+ "huor\n"
+ "hurin\n"
+ "hyarmendacil\n"
+ "hyarmentir\n"
+ "iant\n"
+ "iaur\n"
+ "ibun\n"
+ "idril\n"
+ "illuin\n"
+ "ilmare\n"
+ "ilmen\n"
+ "iluvatar\n"
+ "imlach\n"
+ "imladris\n"
+ "indis\n"
+ "ingwe\n"
+ "irmo\n"
+ "isil\n"
+ "isildur\n"
+ "istari\n"
+ "ithil\n"
+ "ivrin\n"
+ "kelvar\n"
+ "kementari\n"
+ "ladros\n"
+ "laiquendi\n"
+ "lalaith\n"
+ "lamath\n"
+ "lammoth\n"
+ "lanthir\n"
+ "laurelin\n"
+ "leithian\n"
+ "legolin\n"
+ "lembas\n"
+ "lenwe\n"
+ "linaewen\n"
+ "lindon\n"
+ "lindorie\n"
+ "loeg\n"
+ "lomelindi\n"
+ "lomin\n"
+ "lomion\n"
+ "lorellin\n"
+ "lorien\n"
+ "lorindol\n"
+ "losgar\n"
+ "lothlann\n"
+ "lothlorien\n"
+ "luin\n"
+ "luinil\n"
+ "lumbar\n"
+ "luthien\n"
+ "mablung\n"
+ "maedhros\n"
+ "maeglin\n"
+ "maglor\n"
+ "magor\n"
+ "mahanaxar\n"
+ "mahtan\n"
+ "maiar\n"
+ "malduin\n"
+ "malinalda\n"
+ "mandos\n"
+ "manwe\n"
+ "mardil\n"
+ "melian\n"
+ "melkor\n"
+ "menegroth\n"
+ "meneldil\n"
+ "menelmacar\n"
+ "meneltarma\n"
+ "minas\n"
+ "minastir\n"
+ "mindeb\n"
+ "mindolluin\n"
+ "mindon\n"
+ "minyatur\n"
+ "mirdain\n"
+ "miriel\n"
+ "mithlond\n"
+ "mithrandir\n"
+ "mithrim\n"
+ "mordor\n"
+ "morgoth\n"
+ "morgul\n"
+ "moria\n"
+ "moriquendi\n"
+ "mormegil\n"
+ "morwen\n"
+ "nahar\n"
+ "naeramarth\n"
+ "namo\n"
+ "nandor\n"
+ "nargothrond\n"
+ "narog\n"
+ "narsil\n"
+ "narsilion\n"
+ "narya\n"
+ "nauglamir\n"
+ "naugrim\n"
+ "ndengin\n"
+ "neithan\n"
+ "neldoreth\n"
+ "nenar\n"
+ "nenning\n"
+ "nenuial\n"
+ "nenya\n"
+ "nerdanel\n"
+ "nessa\n"
+ "nevrast\n"
+ "nibin\n"
+ "nienna\n"
+ "nienor\n"
+ "nimbrethil\n"
+ "nimloth\n"
+ "nimphelos\n"
+ "nimrais\n"
+ "nimras\n"
+ "ningloron\n"
+ "niniel\n"
+ "ninniach\n"
+ "ninquelote\n"
+ "niphredil\n"
+ "nirnaeth\n"
+ "nivrim\n"
+ "noegyth\n"
+ "nogrod\n"
+ "noldolante\n"
+ "noldor\n"
+ "numenor\n"
+ "nurtale\n"
+ "obel\n"
+ "ohtar\n"
+ "oiolosse\n"
+ "oiomure\n"
+ "olorin\n"
+ "olvar\n"
+ "olwe\n"
+ "ondolinde\n"
+ "orfalch\n"
+ "ormal\n"
+ "orocarni\n"
+ "orodreth\n"
+ "orodruin\n"
+ "orome\n"
+ "oromet\n"
+ "orthanc\n"
+ "osgiliath\n"
+ "osse\n"
+ "ossiriand\n"
+ "palantir\n"
+ "pelargir\n"
+ "pelori\n"
+ "periannath\n"
+ "quendi\n"
+ "quenta\n"
+ "quenya\n"
+ "radagast\n"
+ "radhruin\n"
+ "ragnor\n"
+ "ramdal\n"
+ "rana\n"
+ "rathloriel\n"
+ "rauros\n"
+ "region\n"
+ "rerir\n"
+ "rhovanion\n"
+ "rhudaur\n"
+ "rhun\n"
+ "rhunen\n"
+ "rian\n"
+ "ringil\n"
+ "ringwil\n"
+ "romenna\n"
+ "rudh\n"
+ "rumil\n"
+ "saeros\n"
+ "salmar\n"
+ "saruman\n"
+ "sauron\n"
+ "serech\n"
+ "seregon\n"
+ "serinde\n"
+ "shelob\n"
+ "silmarien\n"
+ "silmaril\n"
+ "silpion\n"
+ "sindar\n"
+ "singollo\n"
+ "sirion\n"
+ "soronume\n"
+ "sul\n"
+ "sulimo\n"
+ "talath\n"
+ "taniquetil\n"
+ "tar\n"
+ "taras\n"
+ "tarn\n"
+ "tathren\n"
+ "taur\n"
+ "tauron\n"
+ "teiglin\n"
+ "telchar\n"
+ "telemnar\n"
+ "teleri\n"
+ "telperion\n"
+ "telumendil\n"
+ "thalion\n"
+ "thalos\n"
+ "thangorodrim\n"
+ "thargelion\n"
+ "thingol\n"
+ "thoronath\n"
+ "thorondor\n"
+ "thranduil\n"
+ "thuringwethil\n"
+ "tilion\n"
+ "tintalle\n"
+ "tinuviel\n"
+ "tirion\n"
+ "tirith\n"
+ "tol\n"
+ "tulkas\n"
+ "tumhalad\n"
+ "tumladen\n"
+ "tuna\n"
+ "tuor\n"
+ "turambar\n"
+ "turgon\n"
+ "turin\n"
+ "uial\n"
+ "uilos\n"
+ "uinen\n"
+ "ulairi\n"
+ "ulmo\n"
+ "ulumuri\n"
+ "umanyar\n"
+ "umarth\n"
+ "umbar\n"
+ "ungoliant\n"
+ "urthel\n"
+ "uruloki\n"
+ "utumno\n"
+ "vaire\n"
+ "valacirca\n"
+ "valandil\n"
+ "valaquenta\n"
+ "valar\n"
+ "valaraukar\n"
+ "valaroma\n"
+ "valier\n"
+ "valimar\n"
+ "valinor\n"
+ "valinoreva\n"
+ "valmar\n"
+ "vana\n"
+ "vanyar\n"
+ "varda\n"
+ "vasa\n"
+ "vilya\n"
+ "vingilot\n"
+ "vinyamar\n"
+ "voronwe\n"
+ "wethrin\n"
+ "wilwarin\n"
+ "yavanna\n"
+ ;
+
+
+martial_arts ma_blows[MAX_MA] =
+{
+ { "You punch %s.", 1, 0, 2, 4, 0, 0 },
+ { "You kick %s.", 2, 0, 2, 6, 0, 0 },
+ { "You strike %s.", 3, 0, 2, 7, 0, 0 },
+ { "You hit %s with your knee.", 5, 5, 4, 3, MA_KNEE, 0 },
+ { "You hit %s with your elbow.", 7, 5, 2, 8, 0, 0 },
+ { "You butt %s.", 9, 10, 4, 5, 0, 0 },
+ { "You kick %s.", 11, 10, 6, 4, MA_SLOW, 0 },
+ { "You uppercut %s.", 13, 12, 8, 4, MA_STUN, 6 },
+ { "You double-kick %s.", 16, 15, 10, 4, MA_STUN, 8 },
+ { "You hit %s with a Cat's Claw.", 20, 20, 10, 5, 0, 0 },
+ { "You hit %s with a jump kick.", 25, 25, 10, 6, MA_STUN, 10 },
+ { "You hit %s with an Eagle's Claw.", 29, 25, 12, 6, 0, 0 },
+ { "You hit %s with a circle kick.", 33, 30, 12, 8, MA_STUN, 10 },
+ { "You hit %s with an Iron Fist.", 37, 35, 16, 8, MA_STUN, 10 },
+ { "You hit %s with a flying kick.", 41, 35, 16, 10, MA_STUN, 12 },
+ { "You hit %s with a Dragon Fist.", 45, 35, 20, 10, MA_STUN, 16 },
+ { "You hit %s with a Crushing Blow.", 48, 35, 20, 12, MA_STUN, 18 },
+};
+
+/*
+ * cptr desc; A verbose attack description
+ * int min_level; Minimum level to use
+ * int chance; Chance of 'success
+ * int dd; Damage dice
+ * int ds; Damage sides
+ * s16b effect; Special effects
+ * s16b power; Special effects power
+ */
+martial_arts bear_blows[MAX_BEAR] =
+{
+ { "You claw %s.", 1, 0, 3, 4, MA_STUN, 4 },
+ { "You swat %s.", 4, 0, 4, 4, MA_WOUND, 20 },
+ { "You bite %s.", 9, 2, 4, 4, MA_WOUND, 30 },
+ { "You hug %s.", 15, 5, 6, 4, MA_FULL_SLOW, 0 },
+ { "You swat and rake %s.", 25, 10, 6, 5, MA_STUN | MA_WOUND, 10 },
+ { "You hug and claw %s.", 30, 15, 6, 6, MA_FULL_SLOW | MA_WOUND, 60 },
+ { "You double swat %s.", 35, 20, 9, 7, MA_STUN | MA_WOUND, 20 },
+ { "You double swat and rake %s.", 40, 25, 10, 10, MA_STUN | MA_WOUND, 25 },
+};
+
+
+magic_power mindcraft_powers[MAX_MINDCRAFT_POWERS] =
+{
+ /* Level gained, cost, %fail, name, desc */
+ {
+ /* Det. monsters/traps */
+ 1, 1, 15,
+ "Precognition",
+ "Detect monsters, traps and level layout and lights up at higher levels."
+ },
+ {
+ /* ~MM */
+ 2, 1, 20,
+ "Neural Blast",
+ "Blast the minds of your foes."
+ },
+ {
+ /* Phase/Between gate */
+ 3, 2, 25,
+ "Minor Displacement",
+ "Short distance teleportation"
+ },
+ {
+ /* Tele. Self / All */
+ 7, 6, 35,
+ "Major Displacement",
+ "Teleport you and others at high levels."
+ },
+ {
+ 9, 7, 50,
+ "Domination",
+ "Charm monsters"
+ },
+ {
+ /* Telekinetic "bolt" */
+ 11, 7, 30,
+ "Pulverise",
+ "Fires a bolt of pure sound."
+ },
+ {
+ /* Psychic/physical defenses */
+ 13, 12, 50,
+ "Character Armour",
+ "Sets up physical/elemental shield."
+ },
+ {
+ 15, 12, 60,
+ "Psychometry",
+ "Senses/identifies objects."
+ },
+ {
+ /* Ball -> LOS */
+ 18, 10, 45,
+ "Mind Wave",
+ "Projects psi waves to crush the minds of your foes."
+ },
+ {
+ 23, 15, 50,
+ "Adrenaline Channeling",
+ "Heals you, cures you and speeds you."
+ },
+ {
+ /* Convert enemy HP to mana */
+ 25, 10, 40,
+ "Psychic Drain",
+ "Drain your foes' life into your mana reserves"
+ },
+ {
+ /* Ball -> LOS */
+ 28, 20, 45,
+ "Telekinetic Wave",
+ "Powerful wave of pure telekinetic forces."
+ },
+ };
+
+ magic_power necro_powers[MAX_NECRO_POWERS] =
+ {
+ /* Level gained, cost, %fail, name, desc */
+ {
+ /* Bolt/beam/ball/LOS of stun/scare */
+ 1, 2, 10,
+ "Horrify",
+ "Calls upon the darkness to stun and scare your foes."
+ },
+ {
+ /* Ball */
+ 5, 6, 20,
+ "Raise Dead",
+ "Brings back your foes in the form of various undead. Also, can heal monsters."
+ },
+ {
+ /* Summons weapon */
+ 12, 20, 25,
+ "Necromantic Teeth",
+ "Conjures a temporary vampiric weapon."
+ },
+ {
+ /* Heals when killing a monster */
+ 20, 10, 25,
+ "Absorb Soul",
+ "Gives back some life for each kill."
+ },
+ {
+ /* Bolt */
+ 30, 15, 20,
+ "Vampirism",
+ "Drain the life of your foes into your own."
+ },
+ {
+ /* The Death word, always bolt put your HP to 1 */
+ 35, 100, 25,
+ "Death",
+ "Instantly kills your opponent and you, turning yourself into an undead."
+ },
+};
+
+magic_power mimic_powers[MAX_MIMIC_POWERS] =
+{
+ /* Level gained, cost, %fail, name */
+ {
+ /* Use a book of lore */
+ 1, 2, 0,
+ "Mimic",
+ "Lets you use the powers of a Cloak of Mimicry."
+ },
+ {
+ /* Invisibility */
+ 10, 6, 20,
+ "Invisibility",
+ "Hides you from the sight of mortals."
+ },
+ {
+ /* +1 pair of legs */
+ 25, 20, 25,
+ "Legs Mimicry",
+ "Temporarily provides a new pair of legs."
+ },
+ {
+ /* wall form */
+ 30, 40, 30,
+ "Wall Mimicry",
+ "Temporarily lets you walk in walls, and ONLY in walls."
+ },
+ {
+ /* +1 pair of arms, +1 weapon */
+ 35, 100, 40,
+ "Arms Mimicry",
+ "Temporarily provides a new pair of arms."
+ },
+};
+
+magic_power symbiotic_powers[MAX_SYMBIOTIC_POWERS] =
+{
+ /* Level gained, cost, %fail, name */
+ {
+ 1, 1, 0,
+ "Hypnotise",
+ "Hypnotise a non-moving pet to allow you to enter symbiosis(wear) with it."
+ },
+ {
+ 1, 1, 0,
+ "Release",
+ "Release an hypnotised pet."
+ },
+ {
+ 3, 2, 10,
+ "Charm Never-Moving",
+ "Tries to charm a never-moving monster."
+ },
+ {
+ 5, 5, 20,
+ "Life Share",
+ "Evens out your life with your symbiote."
+ },
+ {
+ 10, 10, 20,
+ "Use Minor Powers",
+ "Allows you to use some of the powers of your symbiote."
+ },
+ {
+ 15, 14, 25,
+ "Heal Symbiote",
+ "Heals your symbiotic monster."
+ },
+ {
+ 25, 30, 40,
+ "Use major powers",
+ "Allows you to use all the powers of your symbiote."
+ },
+ {
+ 30, 35, 40,
+ "Summon Never-Moving Pet",
+ "Summons a never-moving pet."
+ },
+ {
+ 40, 60, 70,
+ "Force Symbiosis",
+ "Allows you to use all the powers of a monster in your line of sight."
+ },
+};
+
+
+/*
+ * Textual translation of your god's "niceness".
+ */
+
+cptr deity_niceness[10] =
+{
+ "a lovable deity",
+ "a friendly deity",
+ "an easygoing deity",
+ "a forgiving deity",
+ "an uncaring deity",
+ "a wary deity",
+ "an unforgiving deity",
+ "an impatient deity",
+ "a wrathful deity",
+ "an easily angered deity"
+};
+
+/*
+ * Textual translation of your standing with your god.
+ */
+
+cptr deity_standing[11] =
+{
+ "cursed",
+ "persecuted",
+ "punished",
+ "despised",
+ "disliked",
+ "watched",
+ "unnoticed",
+ "noticed",
+ "rewarded",
+ "favored",
+ "championed"
+};
+
+/*
+ * Name and description (max. 10 lines) of the gods.
+ * Only the first four lines are printed at birth.
+ */
+
+deity_type deity_info_init[MAX_GODS_INIT] =
+{
+ {
+ "Nobody",
+ {
+ "Atheist",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Eru Iluvatar",
+ {
+ "He is the supreme god, he created the world, and most of its inhabitants.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Manwe Sulimo",
+ {
+ "He is the king of the Valar, most powerful of them after Melkor.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Tulkas",
+ {
+ "He is the last of the Valar that came to the world, and the fiercest fighter.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Melkor Bauglir",
+ {
+ "He is the most powerful of the Valar. He became corrupted and he's now ",
+ "the greatest threat of Arda, he is also known as Morgoth, the dark enemy.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ "Yavanna Kementari",
+ {
+ "She is the Vala of nature, protectress of the great forests of "
+ "Middle-earth.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+};
+
+/* jk - to hit, to dam, to ac, to stealth, to disarm, to saving throw */
+/* this concept is taken from Adom, where Thomas Biskup thought it out, */
+/* as far as I know. */
+tactic_info_type tactic_info[9] =
+{
+ /* hit dam ac stl dis sav */
+ { -10, -10, + 15, + 3, + 15, + 14, "coward"}, /* 4-4 */
+ { -8, -8, + 10, + 2, + 9, + 9, "meek"}, /* 4-3 */
+ { -4, -4, + 5, + 1, + 5, + 5, "wary"}, /* 4-2 */
+ { -2, -2, + 2, + 1, + 2, + 2, "careful"}, /* 4-1 */
+ { 0, 0, 0, 0, 0, 0, "normal"}, /* 4+0 */
+ { 2, 2, -2, -1, -2, -3, "confident"}, /* 4+1 */
+ { 4, 4, -5, -2, -5, -7, "aggressive"}, /* 4+2 */
+ { 6, 6, -10, -3, -11, -12, "furious"}, /* 4+3 */
+ { 8, 12, -25, -5, -18, -18, "berserker"} /* 4+4 */
+};
+
+/*
+ * Random artifact activations.
+ */
+activation activation_info[MAX_T_ACT] =
+{
+ { "death", 0, ACT_DEATH },
+ { "ruination", 0, ACT_RUINATION },
+ { "destruction", 1000, ACT_DESTRUC },
+ { "stupidity", 0, ACT_UNINT },
+ { "weakness", 0, ACT_UNSTR },
+ { "unhealth", 0, ACT_UNCON },
+ { "ugliness", 0, ACT_UNCHR },
+ { "clumsiness", 0, ACT_UNDEX },
+ { "naivete", 0, ACT_UNWIS },
+ { "stat loss", 0, ACT_STATLOSS },
+ { "huge stat loss", 0, ACT_HISTATLOSS },
+ { "experience loss", 0, ACT_EXPLOSS },
+ { "huge experience loss", 0, ACT_HIEXPLOSS },
+ { "teleportation", 1000, ACT_TELEPORT },
+ { "monster summoning", 5, ACT_SUMMON_MONST },
+ { "paralyzation", 0, ACT_PARALYZE },
+ { "hallucination", 100, ACT_HALLU },
+ { "poisoning", 0, ACT_POISON },
+ { "hunger", 0, ACT_HUNGER },
+ { "stun", 0, ACT_STUN },
+ { "cuts", 0, ACT_CUTS },
+ { "paranoia", 0, ACT_PARANO },
+ { "confusion", 0, ACT_CONFUSION },
+ { "blindness", 0, ACT_BLIND },
+ { "pet summoning", 1010, ACT_PET_SUMMON },
+ { "cure paralyzation", 5000, ACT_CURE_PARA },
+ { "cure hallucination", 1000, ACT_CURE_HALLU },
+ { "cure poison", 1000, ACT_CURE_POIS },
+ { "cure hunger", 1000, ACT_CURE_HUNGER },
+ { "cure stun", 1000, ACT_CURE_STUN },
+ { "cure cut", 1000, ACT_CURE_CUTS },
+ { "cure fear", 1000, ACT_CURE_FEAR },
+ { "cure confusion", 1000, ACT_CURE_CONF },
+ { "cure blindness", 1000, ACT_CURE_BLIND },
+ { "cure light wounds", 500, ACT_CURE_LW },
+ { "cure serious wounds", 750, ACT_CURE_MW },
+ { "cure critical wounds", 1000, ACT_CURE_700 },
+ { "curing", 1100, ACT_CURING },
+ { "genocide", 5000, ACT_GENOCIDE },
+ { "mass genocide", 10000, ACT_MASS_GENO },
+ { "restoration", 2000, ACT_REST_ALL },
+ { "light", 1000, ACT_LIGHT },
+ { "darkness", 0, ACT_DARKNESS },
+ { "teleportation", 1000, ACT_TELEPORT },
+ { "level teleportation", 500, ACT_LEV_TELE },
+ { "acquirement", 30000, ACT_ACQUIREMENT },
+ { "something weird", 50, ACT_WEIRD },
+ { "aggravation", 0, ACT_AGGRAVATE },
+ { "corruption", 100, ACT_MUT },
+ { "cure insanity", 2000, ACT_CURE_INSANITY },
+ { "light absortion", 800, ACT_LIGHT_ABSORBTION },
+#if 0 /* No more for the time being, ehehhe evil I am :> */
+ { "cure corruption", 2000, ACT_CURE_MUT },
+#endif
+};
+
+/*
+ * Possible movement type.
+ */
+move_info_type move_info[9] =
+{
+ /* speed, searching, stealth, perception */
+ { -10, 17, 4, 20, "slug-like"},
+ { -8, 12, 4, 16, "very slow"},
+ { -6, 8, 3, 10, "slow"},
+ { -3, 4, 2, 6, "leisurely"},
+ { 0, 0, 0, 0, "normal"},
+ { 1, -4, -1, -4, "brisk"},
+ { 2, -6, -4, -8, "fast"},
+ { 3, -10, -7, -14, "very fast"},
+ { 4, -16, -10, -20, "running"}
+};
+
+/*
+ * Possible inscriptions type.
+ */
+inscription_info_type inscription_info[MAX_INSCRIPTIONS] =
+{
+ { /* Nothing */
+ "",
+ 0,
+ TRUE,
+ 0,
+ },
+ { /* Light up the room(Adunaic) */
+ "ure nimir", /* sun shine */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 30,
+ },
+ { /* Darkness in room(Adunaic) */
+ "lomi gimli", /* night stars */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 10,
+ },
+ { /* Storm(Adunaic) */
+ "dulgi bawiba", /* black winds */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 40,
+ },
+ { /* Protection(Sindarin) */
+ "pedo mellon a minno", /* say friend and enter */
+ INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 8,
+ },
+ { /* Dwarves summoning(Khuzdul) */
+ "Baruk Khazad! Khazad aimenu!", /* Axes of the Dwarves, the Dwarves are upon you! */
+ INSCRIP_EXEC_ENGRAVE,
+ FALSE,
+ 100,
+ },
+ { /* Open Chasm(Nandorin) */
+ "dunna hrassa", /* black precipice */
+ INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 50,
+ },
+ { /* Blast of Black Fire(Orcish) */
+ "burz ghash ronk", /* black fire pool */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 60,
+ },
+};
+
+/*
+ * Inscriptions for pseudo-id
+ */
+cptr sense_desc[] =
+{
+ "whoops",
+ "cursed",
+ "average",
+ "good",
+ "good",
+ "excellent",
+ "worthless",
+ "terrible",
+ "special",
+ "broken",
+ ""
+};
+
+/*
+ * Flag groups used for art creation, level gaining weapons, ...
+ * -----
+ * Name,
+ * Price,
+ * Flags 1,
+ * Flags 2,
+ * Flags 3,
+ * Flags 4,
+ * ESP,
+ */
+flags_group flags_groups[MAX_FLAG_GROUP] =
+{
+ {
+ "Fire",
+ TERM_L_RED,
+ 1,
+ TR1_SLAY_UNDEAD | TR1_BRAND_FIRE,
+ TR2_RES_FIRE,
+ TR3_SH_FIRE | TR3_LITE1 | TR3_IGNORE_FIRE,
+ 0,
+ 0,
+ },
+ {
+ "Cold",
+ TERM_WHITE,
+ 1,
+ TR1_SLAY_DRAGON | TR1_SLAY_DEMON | TR1_BRAND_COLD,
+ TR2_RES_COLD | TR2_INVIS,
+ TR3_SLOW_DIGEST | TR3_IGNORE_COLD,
+ 0,
+ 0,
+ },
+ {
+ "Acid",
+ TERM_GREEN,
+ 3,
+ TR1_SLAY_ANIMAL | TR1_IMPACT | TR1_TUNNEL | TR1_BRAND_ACID,
+ TR2_RES_ACID,
+ TR3_IGNORE_ACID,
+ 0,
+ 0,
+ },
+ {
+ "Lightning",
+ TERM_L_BLUE,
+ 1,
+ TR1_SLAY_EVIL | TR1_BRAND_ELEC,
+ TR2_RES_ELEC,
+ TR3_IGNORE_ELEC | TR3_SH_ELEC | TR3_TELEPORT,
+ 0,
+ 0,
+ },
+ {
+ "Poison",
+ TERM_L_GREEN,
+ 2,
+ TR1_CHR | TR1_VAMPIRIC | TR1_SLAY_ANIMAL | TR1_BRAND_POIS,
+ TR2_SUST_CHR | TR2_RES_POIS,
+ TR3_DRAIN_EXP,
+ 0,
+ ESP_TROLL | ESP_GIANT,
+ },
+ {
+ "Air",
+ TERM_BLUE,
+ 5,
+ TR1_WIS | TR1_STEALTH | TR1_INFRA | TR1_SPEED,
+ TR2_RES_LITE | TR2_RES_DARK | TR2_RES_BLIND | TR2_SUST_WIS,
+ TR3_FEATHER | TR3_SEE_INVIS | TR3_BLESSED,
+ 0,
+ ESP_GOOD,
+ },
+ {
+ "Earth",
+ TERM_L_UMBER,
+ 5,
+ TR1_STR | TR1_CON | TR1_TUNNEL | TR1_BLOWS | TR1_SLAY_TROLL | TR1_SLAY_GIANT | TR1_IMPACT,
+ TR2_SUST_STR | TR2_SUST_CON | TR2_FREE_ACT | TR2_RES_FEAR | TR2_RES_SHARDS,
+ TR3_REGEN,
+ 0,
+ ESP_TROLL | ESP_GIANT,
+ },
+ {
+ "Mind",
+ TERM_YELLOW,
+ 7,
+ TR1_INT | TR1_SEARCH,
+ TR2_SUST_INT | TR2_RES_CONF | TR2_RES_FEAR,
+ 0,
+ 0,
+ ESP_ORC | ESP_TROLL | ESP_GIANT | ESP_ANIMAL | ESP_UNIQUE | ESP_SPIDER | ESP_DEMON,
+ },
+ {
+ "Shield",
+ TERM_RED,
+ 7,
+ TR1_DEX,
+ TR2_SUST_DEX | TR2_INVIS | TR2_REFLECT | TR2_HOLD_LIFE | TR2_RES_SOUND | TR2_RES_NEXUS,
+ TR3_REGEN,
+ 0,
+ 0,
+ },
+ {
+ "Chaos",
+ TERM_VIOLET,
+ 7,
+ TR1_CHAOTIC | TR1_IMPACT,
+ TR2_RES_CHAOS | TR2_RES_DISEN,
+ TR3_REGEN,
+ 0,
+ ESP_ALL,
+ },
+ {
+ "Magic",
+ TERM_L_BLUE,
+ 10,
+ TR1_MANA | TR1_SPELL,
+ TR2_RES_CHAOS | TR2_RES_DISEN,
+ TR3_WRAITH,
+ TR4_PRECOGNITION | TR4_FLY | TR4_CLONE,
+ 0,
+ },
+ {
+ "Antimagic",
+ TERM_L_DARK,
+ 10,
+ TR1_VAMPIRIC | TR1_CHAOTIC | TR1_BLOWS | TR1_SPEED,
+ TR2_LIFE | TR2_REFLECT | TR2_FREE_ACT | TR2_HOLD_LIFE,
+ TR3_NO_MAGIC | TR3_NO_TELE | TR3_SEE_INVIS,
+ TR4_ANTIMAGIC_10 | TR4_ANTIMAGIC_20,
+ 0,
+ },
+};
+
+/* Powers */
+power_type powers_type_init[POWER_MAX_INIT] =
+{
+ {
+ "spit acid",
+ "You can spit acid.",
+ "You gain the ability to spit acid.",
+ "You lose the ability to spit acid.",
+ 9, 9, A_DEX, 15,
+ },
+ {
+ "fire breath",
+ "You can breath fire.",
+ "You gain the ability to breathe fire.",
+ "You lose the ability to breathe fire.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "hypnotic gaze",
+ "Your gaze is hypnotic.",
+ "Your eyes look mesmerising...",
+ "Your eyes look uninteresting.",
+ 12, 12, A_CHR, 18,
+ },
+ {
+ "telekinesis",
+ "You are telekinetic.",
+ "You gain the ability to move objects telekinetically.",
+ "You lose the ability to move objects telekinetically.",
+ 9, 9, A_WIS, 14,
+ },
+ {
+ "teleport",
+ "You can teleport at will.",
+ "You gain the power of teleportation at will.",
+ "You lose the power of teleportation at will.",
+ 7, 7, A_WIS, 15,
+ },
+ {
+ "mind blast",
+ "You can mind blast your enemies.",
+ "You gain the power of Mind Blast.",
+ "You lose the power of Mind Blast.",
+ 5, 3, A_WIS, 15,
+ },
+ {
+ "emit radiation",
+ "You can emit hard radiation at will.",
+ "You start emitting hard radiation.",
+ "You stop emitting hard radiation.",
+ 15, 15, A_CON, 14,
+ },
+ {
+ "vampiric drain",
+ "You can drain life from a foe.",
+ "You become vampiric.",
+ "You are no longer vampiric.",
+ 4, 5, A_CON, 9,
+ },
+ {
+ "smell metal",
+ "You can smell nearby precious metal.",
+ "You smell a metallic odour.",
+ "You no longer smell a metallic odour.",
+ 3, 2, A_INT, 12,
+ },
+ {
+ "smell monsters",
+ "You can smell nearby monsters.",
+ "You smell filthy monsters.",
+ "You no longer smell filthy monsters.",
+ 5, 4, A_INT, 15,
+ },
+ {
+ "blink",
+ "You can teleport yourself short distances.",
+ "You gain the power of minor teleportation.",
+ "You lose the power of minor teleportation.",
+ 3, 3, A_WIS, 12,
+ },
+ {
+ "eat rock",
+ "You can consume solid rock.",
+ "The walls look delicious.",
+ "The walls look unappetising.",
+ 8, 12, A_CON, 18,
+ },
+ {
+ "swap position",
+ "You can switch locations with another being.",
+ "You feel like walking a mile in someone else's shoes.",
+ "You feel like staying in your own shoes.",
+ 15, 12, A_DEX, 16,
+ },
+ {
+ "shriek",
+ "You can emit a horrible shriek.",
+ "Your vocal cords get much tougher.",
+ "Your vocal cords get much weaker.",
+ 4, 4, A_CON, 6,
+ },
+ {
+ "illuminate",
+ "You can emit bright light.",
+ "You can light up rooms with your presence.",
+ "You can no longer light up rooms with your presence.",
+ 3, 2, A_INT, 10,
+ },
+ {
+ "detect curses",
+ "You can feel the danger of evil magic.",
+ "You can feel evil magic.",
+ "You can no longer feel evil magic.",
+ 7, 14, A_WIS, 14,
+ },
+ {
+ "berserk",
+ "You can drive yourself into a berserk frenzy.",
+ "You feel a controlled rage.",
+ "You no longer feel a controlled rage.",
+ 8, 8, A_STR, 14,
+ },
+ {
+ "polymorph",
+ "You can polymorph yourself at will.",
+ "Your body seems mutable.",
+ "Your body seems stable.",
+ 18, 20, A_CON, 18,
+ },
+ {
+ "Midas touch",
+ "You can turn ordinary items to gold.",
+ "You gain the Midas touch.",
+ "You lose the Midas touch.",
+ 10, 5, A_INT, 12,
+ },
+ {
+ "grow mold",
+ "You can cause mold to grow near you.",
+ "You feel a sudden affinity for mold.",
+ "You feel a sudden dislike for mold.",
+ 1, 6, A_CON, 14,
+ },
+ {
+ "resist elements",
+ "You can harden yourself to the ravages of the elements.",
+ "You feel like you can protect yourself.",
+ "You feel like you might be vulnerable.",
+ 10, 12, A_CON, 12,
+ },
+ {
+ "earthquake",
+ "You can bring down the dungeon around your ears.",
+ "You gain the ability to wreck the dungeon.",
+ "You lose the ability to wreck the dungeon.",
+ 12, 12, A_STR, 16,
+ },
+ {
+ "eat magic",
+ "You can consume magic energy for your own use.",
+ "Your magic items look delicious.",
+ "Your magic items no longer look delicious.",
+ 17, 1, A_WIS, 15,
+ },
+ {
+ "weigh magic",
+ "You can feel the strength of the magics affecting you.",
+ "You feel you can better understand the magic around you.",
+ "You no longer sense magic.",
+ 6, 6, A_INT, 10,
+ },
+ {
+ "sterilise",
+ "You can cause mass impotence.",
+ "You can give everything around you a headache.",
+ "You hear a massed sigh of relief.",
+ 20, 40, A_CHR, 18,
+ },
+ {
+ "panic hit",
+ "You can run for your life after hitting something.",
+ "You suddenly understand how thieves feel.",
+ "You no longer feel jumpy.",
+ 10, 12, A_DEX, 14,
+ },
+ {
+ "dazzle",
+ "You can emit confusing, blinding radiation.",
+ "You gain the ability to emit dazzling lights.",
+ "You lose the ability to emit dazzling lights.",
+ 7, 15, A_CHR, 8,
+ },
+ {
+ "spear of darkness",
+ "You can create a spear of darkness.",
+ "An illusory spear of darkness appears in your hand.",
+ "The spear of darkness disappear.",
+ 7, 10, A_WIS, 9,
+ },
+ {
+ "recall",
+ "You can travel between towns and the depths.",
+ "You feel briefly homesick, but it passes.",
+ "You feel briefly homesick.",
+ 17, 50, A_INT, 16,
+ },
+ {
+ "banish evil",
+ "You can send evil creatures directly to the Nether Realm.",
+ "You feel a holy wrath fill you.",
+ "You no longer feel a holy wrath.",
+ 25, 25, A_WIS, 18,
+ },
+ {
+ "cold touch",
+ "You can freeze things with a touch.",
+ "Your hands get very cold.",
+ "Your hands warm up.",
+ 2, 2, A_CON, 11,
+ },
+ {
+ "throw object",
+ "You can hurl objects with great force.",
+ "Your throwing arm feels much stronger.",
+ "Your throwing arm feels much weaker.",
+ 1, 10, A_STR, 6,
+ },
+ {
+ "find secret passages",
+ "You can use secret passages.",
+ "You suddenly notice lots of hidden ways.",
+ "You no longer can use hidden ways.",
+ 15, 15, A_DEX, 12,
+ },
+ {
+ "detect doors and traps",
+ "You can detect hidden doors and traps.",
+ "You develop an affinity for traps.",
+ "You no longer can detect hidden doors and traps.",
+ 5, 3, A_WIS, 10,
+ },
+ {
+ "create food",
+ "You can create food.",
+ "Your cooking skills greatly improve.",
+ "Your cooking skills return to a normal level.",
+ 15, 10, A_INT, 10,
+ },
+ {
+ "remove fear",
+ "You can embolden yourself.",
+ "You feel your fears lessening.",
+ "You feel your fears growing again.",
+ 3, 5, A_WIS, 8,
+ },
+ {
+ "set explosive rune",
+ "You can set explosive runes.",
+ "You suddenly understand how explosive runes work.",
+ "You suddenly forget how explosive runes work.",
+ 25, 35, A_INT, 15,
+ },
+ {
+ "stone to mud",
+ "You can destroy walls.",
+ "You can destroy walls.",
+ "You cannot destroy walls anymore.",
+ 20, 10, A_STR, 12,
+ },
+ {
+ "poison dart",
+ "You can throw poisoned darts.",
+ "You get an infinite supply of poisoned darts.",
+ "You lose your infinite supply of poisoned darts.",
+ 12, 8, A_DEX, 14,
+ },
+ {
+ "magic missile",
+ "You can cast magic missiles.",
+ "You suddenly understand the basics of magic.",
+ "You forget the basics of magic.",
+ 2, 2, A_INT, 9,
+ },
+ {
+ "grow trees",
+ "You can grow trees.",
+ "You feel an affinity for trees.",
+ "You no longer feel an affinity for trees.",
+ 2, 6, A_CHR, 3,
+ },
+ {
+ "cold breath",
+ "You can breath cold.",
+ "You gain the ability to breathe cold.",
+ "You lose the ability to breathe cold.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "chaos breath",
+ "You can breath chaos.",
+ "You gain the ability to breathe chaos.",
+ "You lose the ability to breathe chaos.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "elemental breath",
+ "You can breath the elements.",
+ "You gain the ability to breathe the elements.",
+ "You lose the ability to breathe the elements.",
+ 20, 10, A_CON, 18,
+ },
+ {
+ "change the world",
+ "You can wreck the world around you.",
+ "You gain the ability to wreck the world.",
+ "You lose the ability to wreck the world.",
+ 1, 30, A_CHR, 6,
+ },
+ {
+ "scare monster",
+ "You can scare monsters.",
+ "You gain the ability to scare monsters.",
+ "You lose the ability to scare monsters.",
+ 4, 3, A_INT, 3,
+ },
+ {
+ "restore life",
+ "You can restore lost life forces.",
+ "You gain the ability to restore your life force.",
+ "You lose the ability to restore your life force.",
+ 30, 30, A_WIS, 18,
+ },
+ {
+ "summon monsters",
+ "You can call upon monsters.",
+ "You gain the ability to call upon monsters.",
+ "You lose the ability to call upon monsters.",
+ 0, 0, 0, 0,
+ },
+ {
+ "necromantic powers",
+ "You can use the foul necromantic magic.",
+ "You gain the ability to use the foul necromantic magic.",
+ "You lose the ability to use the foul necromantic magic.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Rohan Knight's Powers",
+ "You can use rohir powers.",
+ "You gain the ability to use rohir powers.",
+ "You lose the ability to use rohir powers.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Thunderlord's Powers",
+ "You can use thunderlords powers.",
+ "You gain the ability to use thunderlords powers.",
+ "You lose the ability to use thunderlords powers.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Death Mold's Powers",
+ "You can use the foul deathmold magic.",
+ "You gain the ability to use the foul deathmold magic.",
+ "You lose the ability to use the foul deathmold magic.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Hypnotise Pet",
+ "You can mystify pets.",
+ "You gain the ability to mystify pets.",
+ "You lose the ability to mystify pets.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Awaken Hypnotised Pet",
+ "You can wake up a pet.",
+ "You gain the ability to wake up a pet.",
+ "You lose the ability to wake up a pet.",
+ 0, 0, 0, 0,
+ },
+ {
+ "Incarnate",
+ "You can incarnate into a body.",
+ "You feel the need to get a body.",
+ "You no longer feel the need for a new body.",
+ 0, 0, 0, 0,
+ },
+ {
+ "magic map",
+ "You can sense what is beyond walls.",
+ "You feel you can sense what is beyond walls.",
+ "You no longer can sense what is beyond walls.",
+ 7, 10, A_WIS, 15,
+ },
+ {
+ "lay trap",
+ "You can lay monster traps.",
+ "You suddenly understand how rogues work.",
+ "You no longer understand how rogues work.",
+ 1, 1, A_DEX, 1,
+ },
+ {
+ "Merchant abilities",
+ "You can request items and get loans.",
+ "From now on you can use the merchant abilities.",
+ "You can no longer use the merchant abilities.",
+ 0, 0, 0, 0,
+ },
+ {
+ "turn pet into companion",
+ "You can turn a pet into a companion.",
+ "You suddenly gain authority over your pets.",
+ "You can no longer convert pets into companions.",
+ 2, 10, A_CHR, 10,
+ },
+ {
+ "turn into a bear",
+ "You can turn into a bear.",
+ "You suddenly gain beorning powers.",
+ "You can no longer shapeshift into a bear.",
+ 2, 5, A_CON, 5,
+ },
+ {
+ "sense dodge success",
+ "You can sense your dodging success chance.",
+ "You suddenly can sense your dodging success chance.",
+ "You can no longer sense your dodging success chance.",
+ 0, 0, 0, 0,
+ },
+ {
+ "turn into a Balrog",
+ "You can turn into a Balrog at will.",
+ "You feel the fire of Udun burning in you.",
+ "You no longer feel the fire of Udun in you.",
+ 35, 80, A_WIS, 25,
+ },
+};
+
+/*
+ * The Quests
+ */
+quest_type quest_init_tome[MAX_Q_IDX_INIT] =
+{
+ {
+ FALSE,
+ FALSE,
+ "",
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 0,
+
+ NULL,
+ HOOK_TYPE_C,
+ quest_null_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Dol Guldur",
+ {
+ "The forest of Mirkwood is a very dangerous place to go, mainly due to",
+ "the activities of the Necromancer that lurks in Dol Guldur.",
+ "Find him, and free Mirkwood from his spells.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_TAKEN,
+ 70,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_necro_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Sauron",
+ {
+ "It is time to take the battle to Morgoth. But, before you can",
+ "reach it, you must find and kill Sauron. Only after defeating",
+ "this powerful sorcerer will the stairs leading to Morgoth's",
+ "room be opened.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 99,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_sauron_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Morgoth",
+ {
+ "Your final quest is the ultimate quest that has always been",
+ "required of you. You must enter the fetid depths of Angband, where",
+ "Morgoth is waiting. Travel deep, and defeat this source of all our",
+ "problems. Be prepared, be patient, and good luck. May the light",
+ "shine on you.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 100,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_morgoth_init_hook,
+ {0, 0},
+ },
+
+ /* Bree plot */
+ {
+ FALSE,
+ FALSE,
+ "Thieves!",
+ {
+ "There are thieves robbing my people! They live in a small",
+ "burrow outside the city walls, but they get inside the walls",
+ "with a tunnel to a building here! Your task is to go into",
+ "the building and kill these ruffians.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 5,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_thieves_init_hook,
+ {0, 0},
+ },
+
+ {
+ FALSE,
+ TRUE,
+ "Random Quest",
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 5,
+
+ NULL,
+ HOOK_TYPE_C,
+ quest_random_init_hook,
+ {0, 0},
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "Lost Hobbit",
+ {
+ "Merton Proudfoot, a young hobbit, seems to have disappeared.",
+ "Last time anyone saw him was near the horrible maze to the south of Bree.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 25,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_hobbit_init_hook,
+ {0, 0},
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The Dark Horseman",
+ {
+ "A dark-cloaked horseman has been spotted several times in town.",
+ "He carries an aura of fear with him and people seem to get sick",
+ "wherever he goes. Please do something, but be careful...",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 40,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_nazgul_init_hook,
+ {0, 0},
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The Trolls Glade",
+ {
+ "A group of Forest Trolls settled in an abandoned forest in the",
+ "south east of our town. They are killing our people. You must",
+ "put an end to this! It might be best to look for them at night.",
+ "Local hobbits claim that the mighty swords Orcrist and Glamdring",
+ "can be found there! Bring back one of them as a proof!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_troll_init_hook,
+ {FALSE, 0},
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The Wight Grave",
+ {
+ "The Barrow-Downs hides many mysteries and dangers.",
+ "Lately many people, both men and hobbits, have disappeared there.",
+ "Please put an end to this threat!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_BREE],
+ HOOK_TYPE_C,
+ quest_wight_init_hook,
+ {FALSE, 0},
+ },
+
+ /* Lorien plot */
+ {
+ FALSE,
+ FALSE,
+ "Spiders of Mirkwood",
+ {
+ "Powers lurk deep within Mirkwood. Spiders have blocked the",
+ "path through the forest, and Thranduil's folk have been",
+ "unable to hold them off. It is your task to drive them",
+ "away. Be careful -- many traps have been laid by their",
+ "webs, and their venom is dangerous indeed.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 25,
+
+ &plots[PLOT_LORIEN],
+ HOOK_TYPE_C,
+ quest_spider_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Poisoned Water",
+ {
+ "A curse has beset Lothlorien. All trees along the shorelines of Nimrodel",
+ "are withering away. We fear the blight could spread to the whole forest.",
+ "The cause seems to be an unknown poison. You are to go to the West and",
+ "travel along Celebrant and Nimrodel until you discover the source of",
+ "the poisoning. Then you must destroy it and drop these potions on",
+ "the tainted water.",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_LORIEN],
+ HOOK_TYPE_C,
+ quest_poison_init_hook,
+ {0, 0},
+ },
+ /* Other quests */
+ {
+ FALSE,
+ FALSE,
+ "The Broken Sword",
+ {
+ "You have found Narsil, a broken sword. It is said that the sword that",
+ "was broken shall be reforged... Maybe it is this one.",
+ "You should bring it to Aragorn at Minas Anor -- he would know.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 20,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_narsil_init_hook,
+ {0, 0},
+ },
+ /* Gondolin plot */
+ {
+ FALSE,
+ FALSE,
+ "Eol the Dark Elf",
+ {
+ "We have disturbing tidings. Eol the Dark Elf has come seeking his kin in",
+ "Gondolin. We cannot let anyone pass the borders of the city without the",
+ "King's leave. Go forth to the eastern mountains and apprehend him. If",
+ "he resists, use whatever means possible to hinder him from reaching the",
+ "city. Be wary -- the mountain caves may have many hidden traps.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 30,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_eol_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Nirnaeth Arnoediad",
+ {
+ "The fortunes of war in the north turn against us.",
+ "Morgoth's treachery has driven our armies back nigh",
+ "to the city's walls. Go forth from the city gates",
+ "and clear a path for them to retreat. You need not",
+ "destroy the troll army, simply drive a path through.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 37,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_nirnaeth_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Invasion of Gondolin",
+ {
+ "Morgoth is upon us! Dragons and Balrogs have poured over secret",
+ "ways of the Echoriath, and are looking for our city. They are",
+ "conducted by Maeglin! You must stop him or they will find us.",
+ "Do not let Maeglin get to the stairs or everything will be lost!",
+ "Go now, be brave.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 80,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_invasion_init_hook,
+ {0, 0},
+ },
+ /* Minas Anor Plot*/
+ {
+ FALSE,
+ FALSE,
+ "The Last Alliance",
+ {
+ "The armies of Morgoth are closing in on the last remaining strongholds",
+ "of resistance against him. We are too far apart to help each other.",
+ "The arrival of our new Thunderlord allies has helped, but can only delay",
+ "the inevitable. We must be able to stand together and reinforce each other,",
+ "or both our kingdoms will fall separately. The Thunderlords have taught us",
+ "how to use the Void Jumpgates: we need you to open a Void Jumpgate in our",
+ "own city, and that of Gondolin.",
+ "Simply travel to Gondolin, but beware of rebel thunderlords.",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 80,
+
+ &plots[PLOT_MINAS],
+ HOOK_TYPE_C,
+ quest_between_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "The One Ring",
+ {
+ "Find the One Ring, then bring it to Mount Doom, in Mordor, to drop",
+ "it in the Great Fire where it was once forged.",
+ "But beware: *NEVER* use it, or you will be corrupted.",
+ "Once it is destroyed you will be able to permanently defeat Sauron.",
+ "The ring must be cast back into the fires of Mount Doom!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 99,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_one_init_hook,
+ {0, 0},
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "Mushroom supplies",
+ {
+ "Farmer Maggot asked you to bring him back his mushrooms.",
+ "Do not harm his dogs.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 3,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_shroom_init_hook,
+ {0, 0},
+ },
+
+ {
+ FALSE,
+ FALSE,
+ "The prisoner of Dol Guldur",
+ {
+ "You keep hearing distress cries in the dark tower of",
+ "Dol Guldur...",
+ "Maybe there is someone being held prisoner and tortured!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 60,
+
+ &plots[PLOT_OTHER],
+ HOOK_TYPE_C,
+ quest_thrain_init_hook,
+ {0, 0},
+ },
+
+ /* The 2 ultra endings go here */
+ {
+ FALSE,
+ FALSE,
+ "Falling Toward Apotheosis",
+ {
+ "You must enter the Void where Melkor spirit lurks to destroy",
+ "him forever. Remember however that it is likely to be your own",
+ "death that awaits you.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 150,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_ultra_good_init_hook,
+ {0, 0},
+ },
+ {
+ FALSE,
+ FALSE,
+ "Falling Toward Apotheosis",
+ {
+ "You must now launch an onslaught on Valinor itself to eliminate",
+ "once and for all any posible resistance to your dominance of Arda.",
+ "Remember however that it is likely to be your own death that awaits you.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 150,
+
+ &plots[PLOT_MAIN],
+ HOOK_TYPE_C,
+ quest_ultra_evil_init_hook,
+ {0, 0},
+ },
+ /* More Lorien */
+ {
+ FALSE,
+ FALSE,
+ "Wolves!",
+ {
+ "There are wolves pestering my people! They gather in a hut",
+ "on the edge of town and menace everyone nearby. Your task",
+ "is to go in there and clear them out.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 15,
+
+ &plots[PLOT_LORIEN],
+ HOOK_TYPE_C,
+ quest_wolves_init_hook,
+ {0, 0},
+ },
+ /* More Gondolin */
+ {
+ FALSE,
+ FALSE,
+ "Dragons!",
+ {
+ "There are dragons pestering my people! They gather in a",
+ "building on the edge of town and menace everyone nearby.",
+ "Your task is to go into the building and clear them out.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 25,
+
+ &plots[PLOT_GONDOLIN],
+ HOOK_TYPE_C,
+ quest_dragons_init_hook,
+ {0, 0},
+ },
+ /* More Minas Anor */
+ {
+ FALSE,
+ FALSE,
+ "Haunted House!",
+ {
+ "There are undead pestering my people! They gather in a hut",
+ "on the edge of town and menace everyone nearby. Your task",
+ "is to go into the building and clear out the beasts.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 45,
+
+ &plots[PLOT_MINAS],
+ HOOK_TYPE_C,
+ quest_haunted_init_hook,
+ {0, 0},
+ },
+ /* Khazad-Dum Plot*/
+ {
+ FALSE,
+ FALSE,
+ "Evil!",
+ {
+ "We have burrowed too deep, and let out some creatures of",
+ "Morgoth's that threaten to kill us all! Your task is to save us",
+ "from them!",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 60,
+
+ &plots[PLOT_KHAZAD],
+ HOOK_TYPE_C,
+ quest_evil_init_hook,
+ {0, 0},
+ },
+};
+
+
+/* List of powers for Symbiants/Powers */
+monster_power monster_powers[96] =
+ {
+ { RF4_SHRIEK, "Aggravate Monster", 1, FALSE },
+ { RF4_MULTIPLY, "Multiply", 10, FALSE },
+ { RF4_S_ANIMAL, "Summon Animal", 30, FALSE },
+ { RF4_ROCKET, "Fire a Rocket", 40, TRUE },
+ { RF4_ARROW_1, "Light Arrow", 1, FALSE },
+ { RF4_ARROW_2, "Minor Arrow", 3, FALSE },
+ { RF4_ARROW_3, "Major Arrow", 7, TRUE },
+ { RF4_ARROW_4, "Great Arrow", 9, TRUE },
+ { RF4_BR_ACID, "Breathe Acid", 10, FALSE },
+ { RF4_BR_ELEC, "Breathe Lightning", 10, FALSE },
+ { RF4_BR_FIRE, "Breathe Fire", 10, FALSE },
+ { RF4_BR_COLD, "Breathe Cold", 10, FALSE },
+ { RF4_BR_POIS, "Breathe Poison", 15, TRUE },
+ { RF4_BR_NETH, "Breathe Nether", 30, TRUE },
+ { RF4_BR_LITE, "Breathe Light", 20, TRUE },
+ { RF4_BR_DARK, "Breathe Dark", 20, TRUE },
+ { RF4_BR_CONF, "Breathe Confusion", 15, TRUE },
+ { RF4_BR_SOUN, "Breathe Sound", 30, TRUE },
+ { RF4_BR_CHAO, "Breathe Chaos", 30, TRUE },
+ { RF4_BR_DISE, "Breathe Disenchantment", 30, TRUE },
+ { RF4_BR_NEXU, "Breathe Nexus", 30, TRUE },
+ { RF4_BR_TIME, "Breathe Time", 30, TRUE },
+ { RF4_BR_INER, "Breathe Inertia", 30, TRUE },
+ { RF4_BR_GRAV, "Breathe Gravity", 30, TRUE },
+ { RF4_BR_SHAR, "Breathe Shards", 30, TRUE },
+ { RF4_BR_PLAS, "Breathe Plasma", 30, TRUE },
+ { RF4_BR_WALL, "Breathe Force", 30, TRUE },
+ { RF4_BR_MANA, "Breathe Mana", 40, TRUE },
+ { RF4_BA_NUKE, "Nuke Ball", 30, TRUE },
+ { RF4_BR_NUKE, "Breathe Nuke", 40, TRUE },
+ { RF4_BA_CHAO, "Chaos Ball", 30, TRUE },
+ { RF4_BR_DISI, "Breathe Disintegration", 40, TRUE },
+
+ { RF5_BA_ACID, "Acid Ball", 8, FALSE },
+ { RF5_BA_ELEC, "Lightning Ball", 8, FALSE },
+ { RF5_BA_FIRE, "Fire Ball", 8, FALSE },
+ { RF5_BA_COLD, "Cold Ball", 8, FALSE },
+ { RF5_BA_POIS, "Poison Ball", 20, TRUE },
+ { RF5_BA_NETH, "Nether Ball", 20, TRUE },
+ { RF5_BA_WATE, "Water Ball", 20, TRUE },
+ { RF5_BA_MANA, "Mana Ball", 50, TRUE },
+ { RF5_BA_DARK, "Darkness Ball", 20, TRUE },
+ { 0, "(none)", 0, FALSE },
+ { 0, "(none)", 0, FALSE },
+ { 0, "(none)", 0, FALSE },
+ { RF5_CAUSE_1, "Cause Light Wounds", 20, FALSE },
+ { RF5_CAUSE_2, "Cause Medium Wounds", 30, FALSE },
+ { RF5_CAUSE_3, "Cause Critical Wounds", 35, TRUE },
+ { RF5_CAUSE_4, "Cause Mortal Wounds", 45, TRUE },
+ { RF5_BO_ACID, "Acid Bolt", 5, FALSE },
+ { RF5_BO_ELEC, "Lightning Bolt", 5, FALSE },
+ { RF5_BO_FIRE, "Fire Bolt", 5, FALSE },
+ { RF5_BO_COLD, "Cold Bolt", 5, FALSE },
+ { RF5_BO_POIS, "Poison Bolt", 10, TRUE },
+ { RF5_BO_NETH, "Nether Bolt", 15, TRUE },
+ { RF5_BO_WATE, "Water Bolt", 20, TRUE },
+ { RF5_BO_MANA, "Mana Bolt", 25, TRUE },
+ { RF5_BO_PLAS, "Plasma Bolt", 20, TRUE },
+ { RF5_BO_ICEE, "Ice Bolt", 20, TRUE },
+ { RF5_MISSILE, "Magic Missile", 1, FALSE },
+ { RF5_SCARE, "Scare", 4, FALSE },
+ { RF5_BLIND, "Blindness", 6, FALSE },
+ { RF5_CONF, "Confusion", 7, FALSE },
+ { RF5_SLOW, "Slowness", 10, FALSE },
+ { RF5_HOLD, "Paralyse", 10, FALSE },
+
+ { RF6_HASTE, "Haste Self", 50, FALSE },
+ { RF6_HAND_DOOM, "Hand of Doom", 30, TRUE },
+ { RF6_HEAL, "Healing", 60, FALSE },
+ { RF6_S_ANIMALS, "Summon Animals", 60, TRUE },
+ { RF6_BLINK, "Phase Door", 2, FALSE },
+ { RF6_TPORT, "Teleport", 10, FALSE },
+ { RF6_TELE_TO, "Teleport To", 20, TRUE },
+ { RF6_TELE_AWAY, "Teleport Away", 20, FALSE },
+ { RF6_TELE_LEVEL, "Teleport Level", 20, TRUE },
+ { RF6_DARKNESS, "Darkness", 3, FALSE },
+ { RF6_TRAPS, "Create Traps", 10, TRUE },
+ { 0, "(none)", 0, FALSE },
+ { RF6_RAISE_DEAD, "Raise the Dead", 400, TRUE },
+ { 0, "(none)", 0, FALSE },
+ { 0, "(none)", 0, FALSE },
+ { RF6_S_THUNDERLORD, "Summon Thunderlords", 90, TRUE },
+ { RF6_S_KIN, "Summon Kin", 80, FALSE },
+ { RF6_S_HI_DEMON, "Summon Greater Demons", 90, TRUE },
+ { RF6_S_MONSTER, "Summon Monster", 50, FALSE },
+ { RF6_S_MONSTERS, "Summon Monsters", 60, TRUE },
+ { RF6_S_ANT, "Summon Ants", 30, FALSE },
+ { RF6_S_SPIDER, "Summon Spider", 30, FALSE },
+ { RF6_S_HOUND, "Summon Hound", 50, TRUE },
+ { RF6_S_HYDRA, "Summon Hydra", 40, TRUE },
+ { RF6_S_ANGEL, "Summon Angel", 60, TRUE },
+ { RF6_S_DEMON, "Summon Demon", 60, TRUE },
+ { RF6_S_UNDEAD, "Summon Undead", 70, TRUE },
+ { RF6_S_DRAGON, "Summon Dragon", 70, TRUE },
+ { RF6_S_HI_UNDEAD, "Summon High Undead", 90, TRUE },
+ { RF6_S_HI_DRAGON, "Summon High Dragon", 90, TRUE },
+ { RF6_S_WRAITH, "Summon Wraith", 90, TRUE },
+ { 0, "(none)", 0, FALSE },
+ };
+
+/* Tval descriptions */
+tval_desc tval_descs[] =
+{
+ {
+ TV_BATERIE,
+ "Essences contain primitive magic forces which users of the "
+ "Alchemy skill can use to create powerful magic items from "
+ "other magic items."
+ },
+ {
+ TV_MSTAFF,
+ "Mage Staves are the spellcaster's weapons of choice. "
+ "They all reduce spellcasting time to 80% of "
+ "normal time and some will yield even greater powers."
+ },
+ {
+ 3,
+ "XXX"
+ },
+ {
+ TV_PARCHMENT,
+ "Parchments can contain useful information ... or useless "
+ "junk."
+ },
+ {
+ TV_EGG,
+ "Eggs are laid by some monsters. If they hatch in your "
+ "inventory the monster will be your friend."
+ },
+ {
+ TV_TOOL,
+ "Tools can be digging implements, climbing equipment and such. "
+ "They have their own slot in your inventory."
+
+ },
+ {
+ TV_INSTRUMENT,
+ "Musical instruments can be used with the Music skill to play "
+ "magical songs. Some of them can also be activated."
+ },
+ {
+ TV_BOOMERANG,
+ "Boomerangs can be used instead of bows or slings. They "
+ "are more like melee weapons than bows."
+ },
+ {
+ TV_SHOT,
+ "Shots are small, hard balls. They are the standard ammunition "
+ "for slings. You can carry them in your quiver if you have a sling "
+ "equipped."
+ },
+ {
+ TV_ARROW,
+ "Arrows are the standard ammunition for bows. You can carry "
+ "them in your quiver if you have a bow equipped."
+ },
+ {
+ TV_BOLT,
+ "Bolts are the standard ammunition for crossbows. You can "
+ "carry them in your quiver if you have a crossbow equipped."
+ },
+ {
+ TV_BOW,
+ "Slings, bows and crossbows are used to attack monsters "
+ "from a distance."
+ },
+ {
+ TV_DIGGING,
+ "Tools can be digging implements, climbing equipment and such. "
+ "They have their own slot in your inventory."
+ },
+ {
+ TV_HAFTED,
+ "Hafted weapons are melee weapons. Eru followers can use them "
+ "without penalties."
+ },
+ {
+ TV_SWORD,
+ "Swords are melee weapons."
+ },
+ {
+ TV_AXE,
+ "Axes are melee weapons."
+ },
+ {
+ TV_POLEARM,
+ "Polearms are melee weapons."
+ },
+ {
+ TV_DRAG_ARMOR,
+ "Dragon armour is made from the scales of dead dragons. "
+ "These mighty sets of armour usually yield great power to "
+ "their wearer."
+ },
+ {
+ TV_LITE,
+ "Lights allow you to read things and see from afar. Some of "
+ "them need to be fueled but some do not."
+ },
+ {
+ TV_AMULET,
+ "Amulets are fine pieces of jewelry, usually imbued with "
+ "arcane magics."
+ },
+ {
+ TV_RING,
+ "Rings are fine pieces of jewelry, usually imbued with "
+ "arcane magics."
+ },
+ {
+ TV_TRAPKIT,
+ "Trapping kits are used with the trapping ability to set "
+ "deadly monster traps."
+ },
+ {
+ TV_STAFF,
+ "Staves are objects imbued with mystical powers."
+ },
+ {
+ TV_WAND,
+ "Wands are like small staves and usually have a targeted "
+ "effect."
+ },
+ {
+ TV_ROD,
+ "Rod tips are the physical bindings of powerful "
+ "spells. Zap (attach) them to a rod to get a fully "
+ "functional rod. Each spell takes some mana from the rod "
+ "it is attached to to work."
+ },
+ {
+ TV_ROD_MAIN,
+ "Rods contain mana reserves used to cast spells in rod "
+ "tips. Zap (attach) a rod tip to them to get a fully "
+ "functional rod. Each spell takes some mana from the rod "
+ "it is attached to to work."
+ },
+ {
+ TV_SCROLL,
+ "Scrolls are magical parchments imbued with magic spells. "
+ "Some are good, some...are not. When a scroll is read, its "
+ "magic is released and the scroll is destroyed."
+ },
+ {
+ TV_POTION,
+ "Potions are magical liquids. Some of them are "
+ "beneficial...some not."
+ },
+ {
+ TV_POTION2,
+ "Potions are magical liquids. Some of them are "
+ "beneficial...some not."
+ },
+ {
+ TV_FLASK,
+ "Flasks of oil can be used to refill lanterns."
+ },
+ {
+ TV_FOOD,
+ "Everybody needs to eat, even you."
+ },
+ {
+ TV_HYPNOS,
+ "This monster seems to be hypnotised and friendly."
+ },
+ {
+ TV_RANDART,
+ "Those objects are only known of by rumours. It is said that "
+ "they can be activated for great or strange effects..."
+ },
+ {
+ TV_RUNE1,
+ "Runes are used with the Runecraft skill to create brand new spells."
+ },
+ {
+ TV_RUNE2,
+ "Runes are used with the Runecraft skill to create brand new spells."
+ },
+ {
+ TV_JUNK,
+ "Junk is usually worthless, though experienced archers can "
+ "create ammo with them."
+ },
+ {
+ TV_SKELETON,
+ "It looks dead..."
+ },
+ {
+ TV_BOTTLE,
+ "An empty bottle. Maybe an alchemist could refill it."
+ },
+ {
+ TV_SPIKE,
+ "Spikes can be used to jam doors."
+ },
+ {
+ TV_CORPSE,
+ "It looks dead..."
+ },
+ {
+ TV_BOOTS,
+ "Boots can help your armour rating. Some of these are magical."
+ },
+ {
+ TV_GLOVES,
+ "Handgear is used to protect hands, but nonmagical ones "
+ "can sometimes hinder spellcasting. Alchemists need "
+ "gloves in order to do alchemy."
+ },
+ {
+ TV_HELM,
+ "Headgear will protect your head."
+ },
+ {
+ TV_CROWN,
+ "Headgear will protect your head."
+ },
+ {
+ TV_SHIELD,
+ "Shields will help improve your defence rating, but you "
+ "cannot use them with two handed weapons."
+ },
+ {
+ TV_CLOAK,
+ "Cloaks can shield you from damage. Sometimes they also "
+ "provide magical powers."
+ },
+ {
+ TV_SOFT_ARMOR,
+ "Soft armour is light, and will not hinder your combat much."
+ },
+ {
+ TV_HARD_ARMOR,
+ "Hard armour provides much more protection than soft "
+ "armour but also hinders combat much more."
+ },
+ {
+ TV_SYMBIOTIC_BOOK,
+ "This mystical book is used by symbiants to extend their "
+ "symbiosis."
+ },
+ {
+ TV_MUSIC_BOOK,
+ "This song book is used by bards to play songs."
+ },
+ {
+ TV_DRUID_BOOK,
+ "This mystical book is used by druids to call upon the "
+ "powers of nature."
+ },
+ {
+ TV_DAEMON_BOOK,
+ "This unholy demon equipment is used with the Demonology skill to control "
+ "the school of demon power."
+ },
+ {0, ""},
+};
+
+/*
+ * List of the between exits
+ * s16b corresp; Corresponding between gate
+ * bool dungeon; Do we exit in a dungeon or in the wild ?
+ *
+ * s16b wild_x, wild_y; Wilderness spot to land onto
+ * s16b p_ptr->px, p_ptr->py; Location of the map
+ *
+ * s16b d_idx; Dungeon to land onto
+ * s16b level;
+ */
+between_exit between_exits[MAX_BETWEEN_EXITS] =
+{
+ {
+ 1,
+ FALSE,
+ 49, 11,
+ 119, 25,
+ 0, 0
+ },
+ {
+ 0,
+ FALSE,
+ 60, 56,
+ 10, 35,
+ 0, 0
+ },
+};
+
+/*
+ * Months
+ */
+int month_day[9] =
+{
+ 0, /* 1 day */
+
+ 1, /* 54 days */
+ 55, /* 72 days */
+ 127, /* 54 days */
+
+ 181, /* 3 days */
+
+ 184, /* 54 days */
+ 238, /* 72 days */
+ 310, /* 54 days */
+
+ 364, /* 1 day */
+};
+
+cptr month_name[9] =
+{
+ "Yestare",
+
+ "Tuile",
+ "Laire",
+ "Yavie",
+
+ "Enderi",
+
+ "Quelle",
+ "Hrive",
+ "Coire",
+
+ "Mettare",
+};
+
+/*
+ * max body parts
+ */
+int max_body_part[BODY_MAX] =
+{
+ 3, /* Weapon */
+ 1, /* Torso */
+ 3, /* Arms */
+ 6, /* Finger */
+ 2, /* Head */
+ 2, /* Legs */
+};
+
+/*
+ * Description of GF_FOO
+ */
+gf_name_type gf_names[] =
+{
+ { GF_ELEC, "electricity" },
+ { GF_POIS, "poison" },
+ { GF_ACID, "acid" },
+ { GF_COLD, "cold" },
+ { GF_FIRE, "fire" },
+ { GF_UNBREATH, "asphyxiating gas" },
+ { GF_CORPSE_EXPL, "corpse explosion" },
+ { GF_MISSILE, "missile" },
+ { GF_ARROW, "arrow" },
+ { GF_PLASMA, "plasma" },
+ { GF_WAVE, "a tidal wave" },
+ { GF_WATER, "water" },
+ { GF_LITE, "light" },
+ { GF_DARK, "darkness" },
+ { GF_LITE_WEAK, "weak light" },
+ { GF_DARK_WEAK, "weak darkness" },
+ { GF_SHARDS, "shards" },
+ { GF_SOUND, "sound" },
+ { GF_CONFUSION, "confusion" },
+ { GF_FORCE, "force" },
+ { GF_INERTIA, "inertia" },
+ { GF_MANA, "pure mana" },
+ { GF_METEOR, "meteor" },
+ { GF_ICE, "ice" },
+ { GF_CHAOS, "chaos" },
+ { GF_NETHER, "nether" },
+ { GF_DISENCHANT, "disenchantment" },
+ { GF_NEXUS, "nexus" },
+ { GF_TIME, "time" },
+ { GF_GRAVITY, "gravity" },
+ { GF_KILL_WALL, "wall destruction" },
+ { GF_KILL_DOOR, "door destruction" },
+ { GF_KILL_TRAP, "trap destruction" },
+ { GF_MAKE_WALL, "wall creation" },
+ { GF_MAKE_DOOR, "door creation" },
+ { GF_MAKE_TRAP, "trap creation" },
+ { GF_OLD_CLONE, "clone" },
+ { GF_OLD_POLY, "polymorph" },
+ { GF_OLD_HEAL, "healing" },
+ { GF_OLD_SPEED, "speed" },
+ { GF_OLD_SLOW, "slowness" },
+ { GF_OLD_CONF, "confusion" },
+ { GF_OLD_SLEEP, "sleep" },
+ { GF_OLD_DRAIN, "drain life" },
+ { GF_AWAY_UNDEAD, "teleport away undead" },
+ { GF_AWAY_EVIL, "teleport away evil" },
+ { GF_AWAY_ALL, "teleport away" },
+ { GF_TURN_UNDEAD, "scare undead" },
+ { GF_TURN_EVIL, "scare evil" },
+ { GF_TURN_ALL, "scare" },
+ { GF_DISP_UNDEAD, "dispel undead" },
+ { GF_DISP_EVIL, "dispel evil" },
+ { GF_DISP_ALL, "dispel" },
+ { GF_DISP_DEMON, "dispel demons" },
+ { GF_DISP_LIVING, "dispel living creatures" },
+ { GF_ROCKET, "rocket" },
+ { GF_NUKE, "nuke" },
+ { GF_MAKE_GLYPH, "glyph creation" },
+ { GF_STASIS, "stasis" },
+ { GF_STONE_WALL, "stone wall creation" },
+ { GF_DEATH_RAY, "death ray" },
+ { GF_STUN, "stunning" },
+ { GF_HOLY_FIRE, "holy fire" },
+ { GF_HELL_FIRE, "hellfire" },
+ { GF_DISINTEGRATE, "disintegration" },
+ { GF_CHARM, "charming" },
+ { GF_CONTROL_UNDEAD, "undead control" },
+ { GF_CONTROL_ANIMAL, "animal control" },
+ { GF_PSI, "psionic energy" },
+ { GF_PSI_DRAIN, "psionic drain" },
+ { GF_TELEKINESIS, "telekinesis" },
+ { GF_JAM_DOOR, "door jamming" },
+ { GF_DOMINATION, "domination" },
+ { GF_DISP_GOOD, "dispel good" },
+ { GF_IDENTIFY, "identification" },
+ { GF_RAISE, "raise dead" },
+ { GF_STAR_IDENTIFY, "*identification*" },
+ { GF_DESTRUCTION, "destruction" },
+ { GF_STUN_CONF, "stunning and confusion" },
+ { GF_STUN_DAM, "stunning and damage" },
+ { GF_CONF_DAM, "confusion and damage" },
+ { GF_STAR_CHARM, "*charming*" },
+ { GF_IMPLOSION, "implosion" },
+ { GF_LAVA_FLOW, "lava" },
+ { GF_FEAR, "fear" },
+ { GF_BETWEEN_GATE, "jumpgate creation" },
+ { GF_WINDS_MANA, "" },
+ { GF_DEATH, "death" },
+ { GF_CONTROL_DEMON, "control demon" },
+ { GF_RAISE_DEMON, "raise demon" },
+ { GF_TRAP_DEMONSOUL, "*control demon*" },
+ { GF_ATTACK, "projected melee attacks" },
+ { -1, NULL },
+};
diff --git a/src/traps.c b/src/traps.c
new file mode 100644
index 00000000..d387efea
--- /dev/null
+++ b/src/traps.c
@@ -0,0 +1,3426 @@
+/* File: traps.c */
+
+/* Purpose: handle traps */
+
+/* the below copyright probably still applies, but it is heavily changed
+ * copied, adapted & re-engineered by JK.
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+bool do_player_trap_call_out(void)
+{
+ s16b i, sn, cx, cy;
+ s16b h_index = 0;
+ s16b h_level = 0;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ char m_name[80];
+ bool ident = FALSE;
+
+ for (i = 1; i < m_max; i++)
+ {
+ m_ptr = &m_list[i];
+ r_ptr = race_inf(m_ptr);
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->level >= h_level)
+ {
+ h_level = m_ptr->level;
+ h_index = i;
+ }
+ }
+
+ /* if the level is empty of monsters, h_index will be 0 */
+ if (!h_index) return (FALSE);
+
+ m_ptr = &m_list[h_index];
+
+ sn = 0;
+ for (i = 0; i < 8; i++)
+ {
+ cx = p_ptr->px + ddx[i];
+ cy = p_ptr->py + ddy[i];
+
+ /* Skip non-empty grids */
+ if (!cave_valid_bold(cy, cx)) continue;
+ if (cave[cy][cx].feat == FEAT_GLYPH) continue;
+ if ((cx == p_ptr->px) && (cy == p_ptr->py)) continue;
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+ cave[cy][cx].m_idx = h_index;
+ cave[m_ptr->fy][m_ptr->fx].m_idx = 0;
+ m_ptr->fx = cx;
+ m_ptr->fy = cy;
+
+ /* we do not change the sublevel! */
+ ident = TRUE;
+ update_mon(h_index, TRUE);
+ monster_desc(m_name, m_ptr, 0x08);
+ msg_format("You hear a rapid-shifting wail, and %s appears!", m_name);
+ break;
+ }
+
+ return (ident);
+}
+
+static bool do_trap_teleport_away(object_type *i_ptr, s16b y, s16b x)
+{
+ bool ident = FALSE;
+ char o_name[80];
+
+ s16b o_idx = 0;
+ object_type *o_ptr;
+ cave_type *c_ptr;
+
+ s16b x1;
+ s16b y1;
+
+ if (i_ptr == NULL) return (FALSE);
+
+ if (i_ptr->name1 == ART_POWER) return (FALSE);
+
+ while (o_idx == 0)
+ {
+ x1 = rand_int(cur_wid);
+ y1 = rand_int(cur_hgt);
+
+ /* Obtain grid */
+ c_ptr = &cave[y1][x1];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) continue;
+
+ o_idx = drop_near(i_ptr, 0, y1, x1);
+ }
+
+ o_ptr = &o_list[o_idx];
+
+ x1 = o_ptr->ix;
+ y1 = o_ptr->iy;
+
+ if (!p_ptr->blind)
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ ident = TRUE;
+ object_desc(o_name, i_ptr, FALSE, 0);
+ if (player_has_los_bold(y1, x1))
+ {
+ lite_spot(y1, x1);
+ msg_format("The %s suddenly stands elsewhere.", o_name);
+
+ }
+ else
+ {
+ msg_format("You suddenly don't see the %s any more!", o_name);
+ }
+ }
+ else
+ {
+ msg_print("You hear something move.");
+ }
+ return (ident);
+}
+
+/*
+ * this handles a trap that places walls around the player
+ */
+static bool player_handle_trap_of_walls(void)
+{
+ bool ident;
+
+ s16b dx, dy, cx, cy;
+ s16b sx = 0, sy = 0, sn, i;
+ cave_type *cv_ptr;
+ bool map[5][5] =
+ {
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE}
+ };
+
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ if (!in_bounds(cy, cx)) continue;
+
+ cv_ptr = &cave[cy][cx];
+
+ if (cv_ptr->m_idx) continue;
+
+ /* Lose room and vault */
+ cv_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+ /* Lose light and knowledge */
+ cv_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Skip the center */
+ if (!dx && !dy) continue;
+
+ /* test for dungeon level */
+ if (randint(100) > 10 + max_dlv[dungeon_type]) continue;
+
+ /* Damage this grid */
+ map[2 + dx][2 + dy] = TRUE;
+ }
+
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ /* Skip unaffected grids */
+ if (!map[2 + dx][2 + dy]) continue;
+
+ cv_ptr = &cave[cy][cx];
+
+ if (cv_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[cv_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Most monsters cannot co-exist with rock */
+ if ((!(r_ptr->flags2 & RF2_KILL_WALL)) &&
+ (!(r_ptr->flags2 & RF2_PASS_WALL)))
+ {
+ char m_name[80];
+
+ /* Assume not safe */
+ sn = 0;
+
+ /* Monster can move to escape the wall */
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ /* Look for safety */
+ for (i = 0; i < 8; i++)
+ {
+ /* Access the grid */
+ cy = p_ptr->py + ddy[i];
+ cx = p_ptr->px + ddx[i];
+
+ /* Skip non-empty grids */
+ if (!cave_clean_bold(cy, cx)) continue;
+
+ /* Hack -- no safety on glyph of warding */
+ if (cave[cy][cx].feat == FEAT_GLYPH) continue;
+
+ /* Important -- Skip "quake" grids */
+ if (map[2 + (cx - p_ptr->px)][2 + (cy - p_ptr->py)]) continue;
+
+ /* Count "safe" grids */
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+
+ /* Save the safe grid */
+ sx = cx;
+ sy = cy;
+
+ ident = TRUE;
+
+ break; /* discontinue for loop - safe grid found */
+ }
+ }
+
+ /* Describe the monster */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Scream in pain */
+ msg_format("%^s wails out in pain!", m_name);
+
+ /* Monster is certainly awake */
+ m_ptr->csleep = 0;
+
+ /* Apply damage directly */
+ m_ptr->hp -= (sn ? damroll(4, 8) : 200);
+
+ /* Delete (not kill) "dead" monsters */
+ if (m_ptr->hp < 0)
+ {
+ /* Message */
+ msg_format("%^s is entombed in the rock!", m_name);
+
+ /* Delete the monster */
+ delete_monster_idx(cave[cy][cx].m_idx);
+
+ /* No longer safe */
+ sn = 0;
+ }
+
+ /* Hack -- Escape from the rock */
+ if (sn)
+ {
+ s16b m_idx = cave[cy][cx].m_idx;
+
+ /* Update the new location */
+ cave[sy][sx].m_idx = m_idx;
+
+ /* Update the old location */
+ cave[cy][cx].m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = sy;
+ m_ptr->fx = sx;
+
+ /* do not change fz */
+ /* don't make rock on that square! */
+ if ((sx >= (p_ptr->px - 2)) && (sx <= (p_ptr->px + 2)) &&
+ (sy >= (p_ptr->py - 2)) && (sy <= (p_ptr->py + 2)))
+ {
+ map[2 + (sx - p_ptr->px)][2 + (sy - p_ptr->py)] = FALSE;
+ }
+
+ /* Update the monster (new location) */
+ update_mon(m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(cy, cx);
+
+ /* Redraw the new grid */
+ lite_spot(sy, sx);
+ } /* if sn */
+ } /* if monster can co-exist with rock */
+ } /* if monster on square */
+ }
+
+ /* Examine the quaked region */
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ /* Skip unaffected grids */
+ if (!map[2 + dx][2 + dy]) continue;
+
+ /* Access the cave grid */
+ cv_ptr = &cave[cy][cx];
+
+ /* Paranoia -- never affect player */
+ if (!dy && !dx) continue;
+
+ /* Destroy location (if valid) */
+ if ((cx < cur_wid) && (cy < cur_hgt) && cave_valid_bold(cy, cx))
+ {
+ bool floor = (f_info[cave[cy][cx].feat].flags1 & FF1_FLOOR);
+
+ /* Delete any object that is still there */
+ delete_object(cy, cx);
+
+ if (floor)
+ {
+ cave_set_feat(cy, cx, FEAT_WALL_OUTER);
+ }
+ else
+ {
+ /* Clear previous contents, add floor */
+ cave_set_feat(cy, cx, FEAT_FLOOR);
+ }
+ }
+ }
+
+ /* Mega-Hack -- Forget the view and lite */
+ p_ptr->update |= PU_UN_VIEW;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Update the health bar */
+ p_ptr->redraw |= (PR_HEALTH);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ handle_stuff();
+
+ msg_print("Suddenly the cave shifts around you. The air is getting stale!");
+
+ ident = TRUE;
+
+ return (ident);
+}
+
+
+/*
+ * this function handles arrow & dagger traps, in various types.
+ * num = number of missiles
+ * tval, sval = kind of missiles
+ * dd,ds = damage roll for missiles
+ * poison_dam = additional poison damage
+ * name = name given if you should die from it...
+ *
+ * return value = ident (always TRUE)
+ */
+static bool player_handle_missile_trap(s16b num, s16b tval, s16b sval, s16b dd, s16b ds,
+ s16b pdam, cptr name)
+{
+ object_type *o_ptr, forge;
+ s16b i, k_idx = lookup_kind(tval, sval);
+ char i_name[80];
+
+ o_ptr = &forge;
+ object_prep(o_ptr, k_idx);
+ o_ptr->number = num;
+ apply_magic(o_ptr, max_dlv[dungeon_type], FALSE, FALSE, FALSE);
+ object_desc(i_name, o_ptr, TRUE, 0);
+
+ msg_format("Suddenly %s hit%s you!", i_name,
+ ((num == 1) ? "" : "s"));
+
+ for (i = 0; i < num; i++)
+ {
+ take_hit(damroll(dd, ds), name);
+
+ redraw_stuff();
+
+ if (pdam > 0)
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ (void)set_poisoned(p_ptr->poisoned + pdam);
+ }
+ }
+ }
+
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+
+ return TRUE;
+}
+
+/*
+ * this function handles a "breath" type trap - acid bolt, lightning balls etc.
+ */
+static bool player_handle_breath_trap(s16b rad, s16b type, u16b trap)
+{
+ trap_type *t_ptr = &t_info[trap];
+ bool ident;
+ s16b my_dd, my_ds, dam;
+
+ my_dd = t_ptr->dd;
+ my_ds = t_ptr->ds;
+
+ /* these traps gets nastier as levels progress */
+ if (max_dlv[dungeon_type] > (2 * t_ptr->minlevel))
+ {
+ my_dd += (max_dlv[dungeon_type] / 15);
+ my_ds += (max_dlv[dungeon_type] / 15);
+ }
+ dam = damroll(my_dd, my_ds);
+
+ ident = project( -2, rad, p_ptr->py, p_ptr->px, dam, type, PROJECT_KILL | PROJECT_JUMP);
+
+ return (ident);
+}
+
+/*
+ * This function damages the player by a trap
+ */
+static void trap_hit(s16b trap)
+{
+ s16b dam;
+ trap_type *t_ptr = &t_info[trap];
+
+ dam = damroll(t_ptr->dd, t_ptr->ds);
+
+ take_hit(dam, t_name + t_ptr->name);
+}
+
+/*
+ * this function activates one trap type, and returns
+ * a bool indicating if this trap is now identified
+ */
+bool player_activate_trap_type(s16b y, s16b x, object_type *i_ptr, s16b item)
+{
+ bool ident = FALSE;
+ s16b trap;
+
+ s16b k, l;
+
+ trap = cave[y][x].t_idx;
+
+ if (i_ptr != NULL)
+ {
+ trap = i_ptr->pval;
+ }
+
+ if ((i_ptr == NULL) && (cave[y][x].o_idx != 0))
+ {
+ i_ptr = &o_list[cave[y][x].o_idx];
+ }
+
+ switch (trap)
+ {
+ /* stat traps */
+ case TRAP_OF_WEAKNESS_I:
+ ident = do_dec_stat(A_STR, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_WEAKNESS_II:
+ ident = do_dec_stat(A_STR, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_WEAKNESS_III:
+ ident = do_dec_stat(A_STR, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_INTELLIGENCE_I:
+ ident = do_dec_stat(A_INT, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_INTELLIGENCE_II:
+ ident = do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_INTELLIGENCE_III:
+ ident = do_dec_stat(A_INT, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_WISDOM_I:
+ ident = do_dec_stat(A_WIS, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_WISDOM_II:
+ ident = do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_WISDOM_III:
+ ident = do_dec_stat(A_WIS, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_FUMBLING_I:
+ ident = do_dec_stat(A_DEX, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_FUMBLING_II:
+ ident = do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_FUMBLING_III:
+ ident = do_dec_stat(A_DEX, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_WASTING_I:
+ ident = do_dec_stat(A_CON, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_WASTING_II:
+ ident = do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_WASTING_III:
+ ident = do_dec_stat(A_CON, STAT_DEC_PERMANENT);
+ break;
+ case TRAP_OF_BEAUTY_I:
+ ident = do_dec_stat(A_CHR, STAT_DEC_TEMPORARY);
+ break;
+ case TRAP_OF_BEAUTY_II:
+ ident = do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+ break;
+ case TRAP_OF_BEAUTY_III:
+ ident = do_dec_stat(A_CHR, STAT_DEC_PERMANENT);
+ break;
+
+ /* Trap of Curse Weapon */
+ case TRAP_OF_CURSE_WEAPON:
+ {
+ ident = curse_weapon();
+ break;
+ }
+
+ /* Trap of Curse Armor */
+ case TRAP_OF_CURSE_ARMOR:
+ {
+ ident = curse_armor();
+ break;
+ }
+
+ /* Earthquake Trap */
+ case TRAP_OF_EARTHQUAKE:
+ {
+ msg_print("As you touch the trap, the ground starts to shake.");
+ earthquake(y, x, 10);
+ ident = TRUE;
+ break;
+ }
+
+ /* Poison Needle Trap */
+ case TRAP_OF_POISON_NEEDLE:
+ {
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ msg_print("You prick yourself on a poisoned needle.");
+ (void)set_poisoned(p_ptr->poisoned + rand_int(15) + 10);
+ ident = TRUE;
+ }
+ else
+ {
+ msg_print("You prick yourself on a needle.");
+ }
+ break;
+ }
+
+ /* Summon Monster Trap */
+ case TRAP_OF_SUMMON_MONSTER:
+ {
+ msg_print("A spell hangs in the air.");
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type], 0);
+ }
+ break;
+ }
+
+ /* Summon Undead Trap */
+ case TRAP_OF_SUMMON_UNDEAD:
+ {
+ msg_print("A mighty spell hangs in the air.");
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type],
+ SUMMON_UNDEAD);
+ }
+ break;
+ }
+
+ /* Summon Greater Undead Trap */
+ case TRAP_OF_SUMMON_GREATER_UNDEAD:
+ {
+ msg_print("An old and evil spell hangs in the air.");
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type],
+ SUMMON_HI_UNDEAD);
+ }
+ break;
+ }
+
+ /* Teleport Trap */
+ case TRAP_OF_TELEPORT:
+ {
+ msg_print("The world whirls around you.");
+ teleport_player(RATIO * 67);
+ ident = TRUE;
+ break;
+ }
+
+ /* Paralyzing Trap */
+ case TRAP_OF_PARALYZING:
+ {
+ if (!p_ptr->free_act)
+ {
+ msg_print("You touch a poisoned part and can't move.");
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(10) + 10);
+ ident = TRUE;
+ }
+ else
+ {
+ msg_print("You prick yourself on a needle.");
+ }
+ break;
+ }
+
+ /* Explosive Device */
+ case TRAP_OF_EXPLOSIVE_DEVICE:
+ {
+ msg_print("A hidden explosive device explodes in your face.");
+ take_hit(damroll(5, 8), "an explosion");
+ ident = TRUE;
+ break;
+ }
+
+ /* Teleport Away Trap */
+ case TRAP_OF_TELEPORT_AWAY:
+ {
+ int item, amt;
+ object_type *o_ptr;
+
+ /* teleport away all items */
+ while (cave[y][x].o_idx != 0)
+ {
+ item = cave[y][x].o_idx;
+
+ o_ptr = &o_list[item];
+
+ amt = o_ptr->number;
+
+ ident = do_trap_teleport_away(o_ptr, y, x);
+
+ floor_item_increase(item, -amt);
+ floor_item_optimize(item);
+ }
+ break;
+ }
+
+ /* Lose Memory Trap */
+ case TRAP_OF_LOSE_MEMORY:
+ {
+ lose_exp(p_ptr->exp / 4);
+
+ ident |= dec_stat(A_WIS, rand_int(20) + 10, STAT_DEC_NORMAL);
+ ident |= dec_stat(A_INT, rand_int(20) + 10, STAT_DEC_NORMAL);
+
+ if (!p_ptr->resist_conf)
+ {
+ ident |= set_confused(p_ptr->confused + rand_int(100) + 50);
+ }
+
+ if (ident)
+ {
+ msg_print("You suddenly don't remember what you were doing.");
+ }
+ else
+ {
+ msg_print("You feel an alien force probing your mind.");
+ }
+ break;
+ }
+ /* Bitter Regret Trap */
+ case TRAP_OF_BITTER_REGRET:
+ {
+ msg_print("An age-old and hideous-sounding spell reverberates off the walls.");
+
+ ident |= dec_stat(A_DEX, 25, TRUE);
+ ident |= dec_stat(A_WIS, 25, TRUE);
+ ident |= dec_stat(A_CON, 25, TRUE);
+ ident |= dec_stat(A_STR, 25, TRUE);
+ ident |= dec_stat(A_CHR, 25, TRUE);
+ ident |= dec_stat(A_INT, 25, TRUE);
+ break;
+ }
+
+ /* Bowel Cramps Trap */
+ case TRAP_OF_BOWEL_CRAMPS:
+ {
+ msg_print("A wretched-smelling gas cloud upsets your stomach.");
+
+ (void)set_food(PY_FOOD_STARVE - 1);
+ (void)set_poisoned(0);
+
+ if (!p_ptr->free_act)
+ {
+ (void)set_paralyzed(p_ptr->paralyzed + rand_int(dun_level) + 6);
+ }
+ ident = TRUE;
+ break;
+ }
+
+ /* Blindness/Confusion Trap */
+ case TRAP_OF_BLINDNESS_CONFUSION:
+ {
+ msg_print("A powerful magic protected this.");
+
+ if (!p_ptr->resist_blind)
+ {
+ ident |= set_blind(p_ptr->blind + rand_int(100) + 100);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ ident |= set_confused(p_ptr->confused + rand_int(20) + 15);
+ }
+ break;
+ }
+
+ /* Aggravation Trap */
+ case TRAP_OF_AGGRAVATION:
+ {
+ msg_print("You hear a hollow noise echoing through the dungeons.");
+ aggravate_monsters(1);
+ break;
+ }
+
+ /* Multiplication Trap */
+ case TRAP_OF_MULTIPLICATION:
+ {
+ msg_print("You hear a loud click.");
+ for (k = -1; k <= 1; k++)
+ for (l = -1; l <= 1; l++)
+ {
+ if ((in_bounds(p_ptr->py + l, p_ptr->px + k)) &&
+ (!cave[p_ptr->py + l][p_ptr->px + k].t_idx))
+ {
+ place_trap(p_ptr->py + l, p_ptr->px + k);
+ }
+ }
+ ident = TRUE;
+ break;
+ }
+
+ /* Steal Item Trap */
+ case TRAP_OF_STEAL_ITEM:
+ {
+ /*
+ * please note that magical stealing is not so
+ * easily circumvented
+ */
+ if (!p_ptr->paralyzed &&
+ (rand_int(160) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("Your backpack seems to vibrate strangely!");
+ break;
+ }
+
+ /* Find an item */
+ for (k = 0; k < rand_int(10); k++)
+ {
+ char i_name[80];
+ object_type *j_ptr, *q_ptr, forge;
+
+ /* Pick an item */
+ s16b i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ j_ptr = &p_ptr->inventory[i];
+
+ /* Accept real items */
+ if (!j_ptr->k_idx) continue;
+
+ /* Don't steal artifacts -CFT */
+ if (artifact_p(j_ptr)) continue;
+
+ /* Get a description */
+ object_desc(i_name, j_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) was stolen!",
+ ((j_ptr->number > 1) ? "One of y" : "Y"),
+ i_name, index_to_label(i));
+
+ /* Create the item */
+ q_ptr = &forge;
+ object_copy(q_ptr, j_ptr);
+ q_ptr->number = 1;
+
+ /* Drop it somewhere */
+ do_trap_teleport_away(q_ptr, y, x);
+
+ inven_item_increase(i, -1);
+ inven_item_optimize(i);
+ ident = TRUE;
+ }
+ break;
+ }
+
+ /* Summon Fast Quylthulgs Trap */
+ case TRAP_OF_SUMMON_FAST_QUYLTHULGS:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type], SUMMON_QUYLTHULG);
+ }
+
+ if (ident)
+ {
+ msg_print("You suddenly have company.");
+ (void)set_slow(p_ptr->slow + randint(25) + 15);
+ }
+ break;
+ }
+
+ /* Trap of Sinking */
+ case TRAP_OF_SINKING:
+ {
+ msg_print("You fell through a trap door!");
+
+ if (p_ptr->ffall)
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ msg_print("You float gently down to the previous level.");
+ }
+ else
+ {
+ msg_print("You float gently down to the next level.");
+ }
+ }
+ else
+ {
+ take_hit(damroll(2, 8), "a trap door");
+ }
+
+ /* Still alive and autosave enabled */
+ if (autosave_l && (p_ptr->chp >= 0))
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ if (dungeon_flags1 & DF1_TOWER) dun_level--;
+ else dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ break;
+ }
+
+ /* Trap of Mana Drain */
+ case TRAP_OF_MANA_DRAIN:
+ {
+ if (p_ptr->csp > 0)
+ {
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ p_ptr->redraw |= (PR_MANA);
+ msg_print("You sense a great loss.");
+ ident = TRUE;
+ }
+ else if (p_ptr->msp == 0)
+ {
+ /* no sense saying this unless you never have mana */
+ msg_format("Suddenly you feel glad you're a mere %s",
+ spp_ptr->title + c_name);
+ }
+ else
+ {
+ msg_print("Your head feels dizzy for a moment.");
+ }
+ break;
+ }
+ /* Trap of Missing Money */
+ case TRAP_OF_MISSING_MONEY:
+ {
+ s32b gold = (p_ptr->au / 10) + randint(25);
+
+ if (gold < 2) gold = 2;
+ if (gold > 5000) gold = (p_ptr->au / 20) + randint(3000);
+ if (gold > p_ptr->au) gold = p_ptr->au;
+
+ p_ptr->au -= gold;
+ if (gold <= 0)
+ {
+ msg_print("You feel something touching you.");
+ }
+ else if (p_ptr->au)
+ {
+ msg_print("Your purse feels lighter.");
+ msg_format("%ld coins were stolen!", (long)gold);
+ ident = TRUE;
+ }
+ else
+ {
+ msg_print("Your purse feels empty.");
+ msg_print("All of your coins were stolen!");
+ ident = TRUE;
+ }
+ p_ptr->redraw |= (PR_GOLD);
+ break;
+ }
+
+ /* Trap of No Return */
+ case TRAP_OF_NO_RETURN:
+ {
+ object_type *j_ptr;
+ s16b j;
+
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ if (!p_ptr->inventory[j].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[j];
+
+ if ((j_ptr->tval == TV_SCROLL) &&
+ (j_ptr->sval == SV_SCROLL_WORD_OF_RECALL))
+ {
+ inven_item_increase(j, -j_ptr->number);
+ inven_item_optimize(j);
+ combine_pack();
+ reorder_pack();
+ if (!ident)
+ {
+ msg_print("A small fire works its way through your backpack. "
+ "Some scrolls are burnt.");
+ }
+ else
+ {
+ msg_print("The fire hasn't finished.");
+ }
+ ident = TRUE;
+ }
+ else if ((j_ptr->tval == TV_ROD_MAIN) &&
+ (j_ptr->pval == SV_ROD_RECALL))
+ {
+ j_ptr->timeout = 0; /* a long time */
+ if (!ident) msg_print("You feel the air stabilise around you.");
+ ident = TRUE;
+ }
+ }
+ if ((!ident) && (p_ptr->word_recall))
+ {
+ msg_print("You feel like staying around.");
+ p_ptr->word_recall = 0;
+ ident = TRUE;
+ }
+ break;
+ }
+
+ /* Trap of Silent Switching */
+ case TRAP_OF_SILENT_SWITCHING:
+ {
+ s16b i, j, slot1, slot2;
+ object_type *j_ptr, *k_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ j_ptr = &p_ptr->inventory[i];
+
+ if (!j_ptr->k_idx) continue;
+
+ /* Do not allow this trap to touch the One Ring */
+ object_flags(j_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if(f3 & TR3_PERMA_CURSE) continue;
+
+ slot1 = wield_slot(j_ptr);
+
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ k_ptr = &p_ptr->inventory[j];
+
+ if (!k_ptr->k_idx) continue;
+
+ /* Do not allow this trap to touch the One Ring */
+ object_flags(k_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if(f3 & TR3_PERMA_CURSE) continue;
+
+ /* this is a crude hack, but it prevent wielding 6 torches... */
+ if (k_ptr->number > 1) continue;
+
+ slot2 = wield_slot(k_ptr);
+
+ /* a chance of 4 in 5 of switching something, then 2 in 5 to do it again */
+ if ((slot1 == slot2) &&
+ (rand_int(100) < (80 - (ident * 40))))
+ {
+ object_type tmp_obj;
+
+ tmp_obj = p_ptr->inventory[j];
+ p_ptr->inventory[j] = p_ptr->inventory[i];
+ p_ptr->inventory[i] = tmp_obj;
+ ident = TRUE;
+ }
+ }
+ }
+
+ if (ident)
+ {
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->update |= (PU_TORCH);
+ p_ptr->update |= (PU_MANA);
+ msg_print("You somehow feel like another person.");
+ }
+ else
+ {
+ msg_print("You feel a lack of useful items.");
+ }
+ break;
+ }
+
+ /* Trap of Walls */
+ case TRAP_OF_WALLS:
+ {
+ ident = player_handle_trap_of_walls();
+ break;
+ }
+
+ /* Trap of Calling Out */
+ case TRAP_OF_CALLING_OUT:
+ {
+ ident = do_player_trap_call_out();
+
+ if (!ident)
+ {
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You feel as if you had a nightmare!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You remember having a nightmare!");
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(40)))
+ {
+ msg_print("You have a vision of a powerful enemy.");
+ }
+ }
+ }
+ break;
+ }
+
+ /* Trap of Sliding */
+ case TRAP_OF_SLIDING:
+ break;
+
+ /* Trap of Charges Drain */
+ case TRAP_OF_CHARGES_DRAIN:
+ {
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ s16b i = rand_int(INVEN_PACK);
+
+ object_type *j_ptr = &p_ptr->inventory[i];
+
+ /* Drain charged wands/staffs
+ Hack -- don't let artifacts get drained */
+ if (((j_ptr->tval == TV_STAFF) ||
+ (j_ptr->tval == TV_WAND)) &&
+ (j_ptr->pval) &&
+ !artifact_p(j_ptr))
+ {
+ ident = TRUE;
+ j_ptr->pval = j_ptr->pval / (randint(4) + 1);
+
+ /* 60% chance of only 1 */
+ if (randint(10) > 3) break;
+ }
+ }
+
+ if (ident)
+ {
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN;
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ msg_print("Your backpack seems to be turned upside down.");
+ }
+ else
+ {
+ msg_print("You hear a wail of great disappointment.");
+ }
+ break;
+ }
+
+ /* Trap of Stair Movement */
+ case TRAP_OF_STAIR_MOVEMENT:
+ {
+ s16b cx, cy, i, j;
+ s16b cnt = 0;
+ s16b cnt_seen = 0;
+ s16b tmps, tmpx;
+ s16b tmpspecial, tmpspecial2;
+ u32b tmpf;
+ bool seen = FALSE;
+ s16b index_x[20], index_y[20]; /* 20 stairs per level is enough? */
+ cave_type *cv_ptr;
+
+ if (max_dlv[dungeon_type] == 99)
+ {
+ /* no sense in relocating that stair! */
+ msg_print("You have a feeling that this trap could be dangerous.");
+ break;
+ }
+
+ for (cx = 0; cx < cur_wid; cx++)
+ for (cy = 0; cy < cur_hgt; cy++)
+ {
+ cv_ptr = &cave[cy][cx];
+
+ if ((cv_ptr->feat != FEAT_LESS) &&
+ (cv_ptr->feat != FEAT_MORE) &&
+ (cv_ptr->feat != FEAT_SHAFT_UP) &&
+ (cv_ptr->feat != FEAT_SHAFT_DOWN)) continue;
+
+ index_x[cnt] = cx;
+ index_y[cnt] = cy;
+ cnt++;
+ }
+
+ if (cnt == 0)
+ {
+ if (wizard) msg_print("Executing moving stairs trap on level with no stairs!");
+ break;
+ }
+
+ for (i = 0; i < cnt; i++)
+ {
+ seen = FALSE;
+
+ for (j = 0; j < 10; j++) /* try 10 times to relocate */
+ {
+ cave_type *cv_ptr = &cave[index_y[i]][index_x[i]];
+ cave_type *cv_ptr2;
+
+ cx = rand_int(cur_wid);
+ cy = rand_int(cur_hgt);
+
+ if ((cx == index_x[i]) || (cy == index_y[i])) continue;
+
+ cv_ptr2 = &cave[cy][cx];
+
+ if (!cave_valid_bold(cy, cx) || cv_ptr2->o_idx != 0) continue;
+
+ /* don't put anything in vaults */
+ if (cv_ptr2->info & CAVE_ICKY) continue;
+
+ tmpx = cv_ptr2->mimic;
+ tmps = cv_ptr2->info;
+ tmpf = cv_ptr2->feat;
+ tmpspecial = cv_ptr2->special;
+ tmpspecial2 = cv_ptr2->special2;
+ cave[cy][cx].mimic = cv_ptr->mimic;
+ cave[cy][cx].info = cv_ptr->info;
+ cave[cy][cx].special = cv_ptr->special;
+ cave[cy][cx].special2 = cv_ptr->special2;
+ cave_set_feat(cy, cx, cv_ptr->feat);
+ cv_ptr->mimic = tmpx;
+ cv_ptr->info = tmps;
+ cv_ptr->special = tmpspecial;
+ cv_ptr->special2 = tmpspecial2;
+ cave_set_feat(index_y[i], index_x[i], tmpf);
+
+ /* if we are placing walls in rooms, make them rubble instead */
+ if ((cv_ptr->info & CAVE_ROOM) &&
+ (cv_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (cv_ptr->feat <= FEAT_PERM_SOLID))
+ {
+ cave_set_feat(index_y[i], index_x[i], FEAT_RUBBLE);
+ }
+
+ if (player_has_los_bold(cy, cx))
+ {
+ note_spot(cy, cx);
+ lite_spot(cy, cx);
+ seen = TRUE;
+ }
+ else
+ {
+ cv_ptr2->info &= ~CAVE_MARK;
+ }
+
+ if (player_has_los_bold(index_y[i], index_x[i]))
+ {
+ note_spot(index_y[i], index_x[i]);
+ lite_spot(index_y[i], index_x[i]);
+ seen = TRUE;
+ }
+ else
+ {
+ cv_ptr->info &= ~CAVE_MARK;
+ }
+ break;
+ }
+
+ if (seen) cnt_seen++;
+ }
+
+ ident = (cnt_seen > 0);
+
+ if ((ident) && (cnt_seen > 1))
+ {
+ msg_print("You see some stairs move.");
+ }
+ else if (ident)
+ {
+ msg_print("You see a stair move.");
+ }
+ else
+ {
+ msg_print("You hear distant scraping noises.");
+ }
+ p_ptr->redraw |= PR_MAP;
+ break;
+ }
+
+ /* Trap of New Trap */
+ case TRAP_OF_NEW:
+ {
+ /* if we're on a floor or on a door, place a new trap */
+ if ((item == -1) || (item == -2))
+ {
+ place_trap(y, x);
+ if (player_has_los_bold(y, x))
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+ }
+ else
+ {
+ /* re-trap the chest */
+ place_trap(y, x);
+ }
+ msg_print("You hear a noise, and then its echo.");
+ ident = FALSE;
+ break;
+ }
+
+ /* Trap of Acquirement */
+ case TRAP_OF_ACQUIREMENT:
+ {
+ /* Get a nice thing */
+ msg_print("You notice something falling off the trap.");
+ acquirement(y, x, 1, TRUE, FALSE);
+
+ /* If we're on a floor or on a door, place a new trap */
+ if ((item == -1) || (item == -2))
+ {
+ place_trap(y, x);
+ if (player_has_los_bold(y, x))
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+ }
+ else
+ {
+ /* Re-trap the chest */
+ place_trap(y, x);
+ }
+ msg_print("You hear a noise, and then its echo.");
+
+ /* Never known */
+ ident = FALSE;
+ }
+ break;
+
+ /* Trap of Scatter Items */
+ case TRAP_OF_SCATTER_ITEMS:
+ {
+ s16b i, j;
+ bool message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+
+ if (rand_int(10) < 3) continue;
+
+ for (j = 0; j < 10; j++)
+ {
+ object_type tmp_obj, *j_ptr = &tmp_obj;
+ s16b cx = x + 15 - rand_int(30);
+ s16b cy = y + 15 - rand_int(30);
+
+ if (!in_bounds(cy, cx)) continue;
+
+ if (!cave_floor_bold(cy, cx)) continue;
+
+ object_copy(j_ptr, &p_ptr->inventory[i]);
+ inven_item_increase(i, -999);
+ inven_item_optimize(i);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ (void)floor_carry(cy, cx, j_ptr);
+
+ if (!message)
+ {
+ msg_print("You feel light-footed.");
+ message = TRUE;
+ }
+
+ if (player_has_los_bold(cy, cx))
+ {
+ char i_name[80];
+
+ object_desc(i_name, &tmp_obj, TRUE, 3);
+ note_spot(cy, cx);
+ lite_spot(cy, cx);
+ ident = TRUE;
+ msg_format("Suddenly %s appear%s!", i_name,
+ (j_ptr->number > 1) ? "" : "s");
+ }
+ break;
+ }
+ }
+ ident = message;
+ break;
+ }
+
+ /* Trap of Decay */
+ case TRAP_OF_DECAY:
+ break;
+
+ /* Trap of Wasting Wands */
+ case TRAP_OF_WASTING_WANDS:
+ {
+ s16b i;
+ object_type *j_ptr;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ if (!p_ptr->inventory[i].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[i];
+
+ if ((j_ptr->tval == TV_WAND) && (rand_int(5) == 1))
+ {
+ if (object_known_p(j_ptr)) ident = TRUE;
+
+ /* Create a Wand of Nothing */
+ object_prep(j_ptr, lookup_kind(TV_WAND, SV_WAND_NOTHING));
+ hack_apply_magic_power = -99;
+ apply_magic(j_ptr, 0, FALSE, FALSE, FALSE);
+ j_ptr->ident &= ~IDENT_KNOWN;
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ else if ((j_ptr->tval == TV_STAFF) && (rand_int(5) == 1))
+ {
+ if (object_known_p(j_ptr)) ident = TRUE;
+
+ /* Create a Staff of Nothing */
+ object_prep(j_ptr, lookup_kind(TV_STAFF, SV_STAFF_NOTHING));
+ hack_apply_magic_power = -99;
+ apply_magic(j_ptr, 0, FALSE, FALSE, FALSE);
+ j_ptr->ident &= ~IDENT_KNOWN;
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+ if (ident)
+ {
+ msg_print("You have lost trust in your backpack!");
+ }
+ else
+ {
+ msg_print("You hear an echoing cry of rage.");
+ }
+ break;
+ }
+
+ /* Trap of Filling */
+ case TRAP_OF_FILLING:
+ {
+ s16b nx, ny;
+
+ for (nx = x - 8; nx <= x + 8; nx++)
+ for (ny = y - 8; ny <= y + 8; ny++)
+ {
+ if (!in_bounds (ny, nx)) continue;
+
+ if (rand_int(distance(ny, nx, y, x)) > 3)
+ {
+ place_trap(ny, nx);
+ }
+ }
+
+ msg_print("The floor vibrates in a strange way.");
+ ident = FALSE;
+ break;
+ }
+
+ case TRAP_OF_DRAIN_SPEED:
+ {
+ object_type *j_ptr;
+ s16b j, chance = 75;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ for (j = 0; j < INVEN_TOTAL; j++)
+ {
+ /* don't bother the overflow slot */
+ if (j == INVEN_PACK) continue;
+
+ if (!p_ptr->inventory[j].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[j];
+ object_flags(j_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* is it a non-artifact speed item? */
+ if ((!j_ptr->name1) && (f1 & TR1_SPEED))
+ {
+ if (randint(100) < chance)
+ {
+ j_ptr->pval = j_ptr->pval / 2;
+ if (j_ptr->pval == 0)
+ {
+ j_ptr->pval--;
+ }
+ chance /= 2;
+ ident = TRUE;
+ }
+ inven_item_optimize(j);
+ }
+ }
+ if (!ident)
+ {
+ msg_print("You feel some things in your pack vibrating.");
+ }
+ else
+ {
+ combine_pack();
+ reorder_pack();
+ msg_print("You suddenly feel you have time for self-reflection.");
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ break;
+ }
+
+ /*
+ * single missile traps
+ */
+ case TRAP_OF_ARROW_I:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap");
+ break;
+ case TRAP_OF_ARROW_II:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap");
+ break;
+ case TRAP_OF_ARROW_III:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap");
+ break;
+ case TRAP_OF_ARROW_IV:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_I:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_II:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_III:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_IV:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap");
+ break;
+ case TRAP_OF_DAGGER_I:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_DAGGER_II:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGER_I:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGER_II:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap");
+ break;
+
+ /*
+ * multiple missile traps
+ * numbers range from 2 (level 0 to 14) to 10 (level 120 and up)
+ */
+ case TRAP_OF_ARROWS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap");
+ break;
+ case TRAP_OF_ARROWS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap");
+ break;
+ case TRAP_OF_ARROWS_III:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap");
+ break;
+ case TRAP_OF_ARROWS_IV:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_III:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_IV:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap");
+ break;
+ case TRAP_OF_DAGGERS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_DAGGERS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGERS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGERS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap");
+ break;
+
+ case TRAP_OF_DROP_ITEMS:
+ {
+ s16b i;
+ bool message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type tmp_obj;
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 80) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+
+ /* drop carefully */
+ drop_near(&tmp_obj, 0, y, x);
+ inven_item_increase(i, -999);
+ inven_item_optimize(i);
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ case TRAP_OF_DROP_ALL_ITEMS:
+ {
+ s16b i;
+ bool message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type tmp_obj;
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 10) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+
+ /* drop carefully */
+ drop_near(&tmp_obj, 0, y, x);
+ inven_item_increase(i, -999);
+ inven_item_optimize(i);
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are greatly startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ case TRAP_OF_DROP_EVERYTHING:
+ {
+ s16b i;
+ bool message = FALSE;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type tmp_obj;
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 30) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+ /* drop carefully */
+
+ drop_near(&tmp_obj, 0, y, x);
+ inven_item_increase(i, -999);
+ inven_item_optimize(i);
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are completely startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ /* Bolt Trap */
+ case TRAP_G_ELEC_BOLT:
+ ident = player_handle_breath_trap(1, GF_ELEC, TRAP_G_ELEC_BOLT);
+ break;
+ case TRAP_G_POIS_BOLT:
+ ident = player_handle_breath_trap(1, GF_POIS, TRAP_G_POIS_BOLT);
+ break;
+ case TRAP_G_ACID_BOLT:
+ ident = player_handle_breath_trap(1, GF_ACID, TRAP_G_ACID_BOLT);
+ break;
+ case TRAP_G_COLD_BOLT:
+ ident = player_handle_breath_trap(1, GF_COLD, TRAP_G_COLD_BOLT);
+ break;
+ case TRAP_G_FIRE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FIRE, TRAP_G_FIRE_BOLT);
+ break;
+ case TRAP_OF_ELEC_BOLT:
+ ident = player_handle_breath_trap(1, GF_ELEC, TRAP_OF_ELEC_BOLT);
+ break;
+ case TRAP_OF_POIS_BOLT:
+ ident = player_handle_breath_trap(1, GF_POIS, TRAP_OF_POIS_BOLT);
+ break;
+ case TRAP_OF_ACID_BOLT:
+ ident = player_handle_breath_trap(1, GF_ACID, TRAP_OF_ACID_BOLT);
+ break;
+ case TRAP_OF_COLD_BOLT:
+ ident = player_handle_breath_trap(1, GF_COLD, TRAP_OF_COLD_BOLT);
+ break;
+ case TRAP_OF_FIRE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FIRE, TRAP_OF_FIRE_BOLT);
+ break;
+ case TRAP_OF_PLASMA_BOLT:
+ ident = player_handle_breath_trap(1, GF_PLASMA, TRAP_OF_PLASMA_BOLT);
+ break;
+ case TRAP_OF_WATER_BOLT:
+ ident = player_handle_breath_trap(1, GF_WATER, TRAP_OF_WATER_BOLT);
+ break;
+ case TRAP_OF_LITE_BOLT:
+ ident = player_handle_breath_trap(1, GF_LITE, TRAP_OF_LITE_BOLT);
+ break;
+ case TRAP_OF_DARK_BOLT:
+ ident = player_handle_breath_trap(1, GF_DARK, TRAP_OF_DARK_BOLT);
+ break;
+ case TRAP_OF_SHARDS_BOLT:
+ ident = player_handle_breath_trap(1, GF_SHARDS, TRAP_OF_SHARDS_BOLT);
+ break;
+ case TRAP_OF_SOUND_BOLT:
+ ident = player_handle_breath_trap(1, GF_SOUND, TRAP_OF_SOUND_BOLT);
+ break;
+ case TRAP_OF_CONFUSION_BOLT:
+ ident = player_handle_breath_trap(1, GF_CONFUSION, TRAP_OF_CONFUSION_BOLT);
+ break;
+ case TRAP_OF_FORCE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FORCE, TRAP_OF_FORCE_BOLT);
+ break;
+ case TRAP_OF_INERTIA_BOLT:
+ ident = player_handle_breath_trap(1, GF_INERTIA, TRAP_OF_INERTIA_BOLT);
+ break;
+ case TRAP_OF_MANA_BOLT:
+ ident = player_handle_breath_trap(1, GF_MANA, TRAP_OF_MANA_BOLT);
+ break;
+ case TRAP_OF_ICE_BOLT:
+ ident = player_handle_breath_trap(1, GF_ICE, TRAP_OF_ICE_BOLT);
+ break;
+ case TRAP_OF_CHAOS_BOLT:
+ ident = player_handle_breath_trap(1, GF_CHAOS, TRAP_OF_CHAOS_BOLT);
+ break;
+ case TRAP_OF_NETHER_BOLT:
+ ident = player_handle_breath_trap(1, GF_NETHER, TRAP_OF_NETHER_BOLT);
+ break;
+ case TRAP_OF_DISENCHANT_BOLT:
+ ident = player_handle_breath_trap(1, GF_DISENCHANT, TRAP_OF_DISENCHANT_BOLT);
+ break;
+ case TRAP_OF_NEXUS_BOLT:
+ ident = player_handle_breath_trap(1, GF_NEXUS, TRAP_OF_NEXUS_BOLT);
+ break;
+ case TRAP_OF_TIME_BOLT:
+ ident = player_handle_breath_trap(1, GF_TIME, TRAP_OF_TIME_BOLT);
+ break;
+ case TRAP_OF_GRAVITY_BOLT:
+ ident = player_handle_breath_trap(1, GF_GRAVITY, TRAP_OF_GRAVITY_BOLT);
+ break;
+
+ /* Ball Trap */
+ case TRAP_OF_ELEC_BALL:
+ ident = player_handle_breath_trap(3, GF_ELEC, TRAP_OF_ELEC_BALL);
+ break;
+ case TRAP_OF_POIS_BALL:
+ ident = player_handle_breath_trap(3, GF_POIS, TRAP_OF_POIS_BALL);
+ break;
+ case TRAP_OF_ACID_BALL:
+ ident = player_handle_breath_trap(3, GF_ACID, TRAP_OF_ACID_BALL);
+ break;
+ case TRAP_OF_COLD_BALL:
+ ident = player_handle_breath_trap(3, GF_COLD, TRAP_OF_COLD_BALL);
+ break;
+ case TRAP_OF_FIRE_BALL:
+ ident = player_handle_breath_trap(3, GF_FIRE, TRAP_OF_FIRE_BALL);
+ break;
+ case TRAP_OF_PLASMA_BALL:
+ ident = player_handle_breath_trap(3, GF_PLASMA, TRAP_OF_PLASMA_BALL);
+ break;
+ case TRAP_OF_WATER_BALL:
+ ident = player_handle_breath_trap(3, GF_WATER, TRAP_OF_WATER_BALL);
+ break;
+ case TRAP_OF_LITE_BALL:
+ ident = player_handle_breath_trap(3, GF_LITE, TRAP_OF_LITE_BALL);
+ break;
+ case TRAP_OF_DARK_BALL:
+ ident = player_handle_breath_trap(3, GF_DARK, TRAP_OF_DARK_BALL);
+ break;
+ case TRAP_OF_SHARDS_BALL:
+ ident = player_handle_breath_trap(3, GF_SHARDS, TRAP_OF_SHARDS_BALL);
+ break;
+ case TRAP_OF_SOUND_BALL:
+ ident = player_handle_breath_trap(3, GF_SOUND, TRAP_OF_SOUND_BALL);
+ break;
+ case TRAP_OF_CONFUSION_BALL:
+ ident = player_handle_breath_trap(3, GF_CONFUSION, TRAP_OF_CONFUSION_BALL);
+ break;
+ case TRAP_OF_FORCE_BALL:
+ ident = player_handle_breath_trap(3, GF_FORCE, TRAP_OF_FORCE_BALL);
+ break;
+ case TRAP_OF_INERTIA_BALL:
+ ident = player_handle_breath_trap(3, GF_INERTIA, TRAP_OF_INERTIA_BALL);
+ break;
+ case TRAP_OF_MANA_BALL:
+ ident = player_handle_breath_trap(3, GF_MANA, TRAP_OF_MANA_BALL);
+ break;
+ case TRAP_OF_ICE_BALL:
+ ident = player_handle_breath_trap(3, GF_ICE, TRAP_OF_ICE_BALL);
+ break;
+ case TRAP_OF_CHAOS_BALL:
+ ident = player_handle_breath_trap(3, GF_CHAOS, TRAP_OF_CHAOS_BALL);
+ break;
+ case TRAP_OF_NETHER_BALL:
+ ident = player_handle_breath_trap(3, GF_NETHER, TRAP_OF_NETHER_BALL);
+ break;
+ case TRAP_OF_DISENCHANT_BALL:
+ ident = player_handle_breath_trap(3, GF_DISENCHANT, TRAP_OF_DISENCHANT_BALL);
+ break;
+ case TRAP_OF_NEXUS_BALL:
+ ident = player_handle_breath_trap(3, GF_NEXUS, TRAP_OF_NEXUS_BALL);
+ break;
+ case TRAP_OF_TIME_BALL:
+ ident = player_handle_breath_trap(3, GF_TIME, TRAP_OF_TIME_BALL);
+ break;
+ case TRAP_OF_GRAVITY_BALL:
+ ident = player_handle_breath_trap(3, GF_GRAVITY, TRAP_OF_GRAVITY_BALL);
+ break;
+
+ /* -SC- */
+ case TRAP_OF_FEMINITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_FEMALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_MASCULINITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_MALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_NEUTRALITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_NEUTER;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_AGING:
+ {
+ msg_print("Colors are scintillating around you. "
+ "You see your past running before your eyes.");
+ p_ptr->age += randint((rp_ptr->b_age + rmp_ptr->b_age) / 2);
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_GROWING:
+ {
+ s16b tmp;
+
+ msg_print("Heavy fumes sprout out... you feel yourself transmute.");
+ if (p_ptr->psex == SEX_FEMALE) tmp = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ else tmp = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+
+ p_ptr->ht += randint(tmp / 4);
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_SHRINKING:
+ {
+ s16b tmp;
+
+ msg_print("Heavy fumes sprout out... you feel yourself transmute.");
+ if (p_ptr->psex == SEX_FEMALE) tmp = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ else tmp = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+
+ p_ptr->ht -= randint(tmp / 4);
+ if (p_ptr->ht <= tmp / 4) p_ptr->ht = tmp / 4;
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ /* Trap of Divine Anger */
+ case TRAP_OF_DIVINE_ANGER:
+ {
+ if (p_ptr->pgod == 0)
+ {
+ msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title + c_name);
+ }
+ else
+ {
+ cptr name;
+
+ name = deity_info[p_ptr->pgod].name;
+ msg_format("You feel you have angered %s.", name);
+ inc_piety(p_ptr->pgod, -3000);
+ }
+ break;
+ }
+
+ /* Trap of Divine Wrath */
+ case TRAP_OF_DIVINE_WRATH:
+ {
+ if (p_ptr->pgod == 0)
+ {
+ msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title + c_name);
+ }
+ else
+ {
+ cptr name;
+
+ name = deity_info[p_ptr->pgod].name;
+
+ msg_format("%s quakes in rage: ``Thou art supremely insolent, mortal!!''", name);
+ inc_piety(p_ptr->pgod, -500 * p_ptr->lev);
+ }
+ break;
+ }
+
+ /* Trap of hallucination */
+ case TRAP_OF_HALLUCINATION:
+ {
+ msg_print("Scintillating colors hypnotise you for a moment.");
+
+ set_image(80);
+ }
+ break;
+
+ /* Bolt Trap */
+ case TRAP_OF_ROCKET:
+ ident = player_handle_breath_trap(1, GF_ROCKET, trap);
+ break;
+ case TRAP_OF_NUKE_BOLT:
+ ident = player_handle_breath_trap(1, GF_NUKE, trap);
+ break;
+ case TRAP_OF_HOLY_FIRE:
+ ident = player_handle_breath_trap(1, GF_HOLY_FIRE, trap);
+ break;
+ case TRAP_OF_HELL_FIRE:
+ ident = player_handle_breath_trap(1, GF_HELL_FIRE, trap);
+ break;
+ case TRAP_OF_PSI_BOLT:
+ ident = player_handle_breath_trap(1, GF_PSI, trap);
+ break;
+ case TRAP_OF_PSI_DRAIN:
+ ident = player_handle_breath_trap(1, GF_PSI_DRAIN, trap);
+ break;
+
+ /* Ball Trap */
+ case TRAP_OF_NUKE_BALL:
+ ident = player_handle_breath_trap(3, GF_NUKE, TRAP_OF_NUKE_BALL);
+ break;
+ case TRAP_OF_PSI_BALL:
+ ident = player_handle_breath_trap(3, GF_PSI, TRAP_OF_NUKE_BALL);
+ break;
+
+ default:
+ {
+ msg_print(format("Executing unknown trap %d", trap));
+ }
+ }
+ return ident;
+}
+
+void player_activate_door_trap(s16b y, s16b x)
+{
+ cave_type *c_ptr;
+ bool ident = FALSE;
+
+ c_ptr = &cave[y][x];
+
+ /* Return if trap or door not found */
+ if ((c_ptr->t_idx == 0) ||
+ !(f_info[c_ptr->feat].flags1 & FF1_DOOR)) return;
+
+ /* Disturb */
+ disturb(0, 0);
+
+ /* Message */
+ msg_print("You found a trap!");
+
+ /* Pick a trap */
+ pick_trap(y, x);
+
+ /* Hit the trap */
+ ident = player_activate_trap_type(y, x, NULL, -1);
+ if (ident)
+ {
+ t_info[c_ptr->t_idx].ident = TRUE;
+ msg_format("You identified that trap as %s.",
+ t_name + t_info[c_ptr->t_idx].name);
+ }
+}
+
+
+/*
+ * Places a random trap at the given location.
+ *
+ * The location must be a valid, empty, clean, floor grid.
+ */
+void place_trap(int y, int x)
+{
+ s16b trap;
+ trap_type *t_ptr;
+ int cnt;
+ u32b flags;
+ cave_type *c_ptr = &cave[y][x];
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ /* No traps in town or on first level */
+ if (dun_level <= 1) return;
+
+ /*
+ * Avoid open doors -- because DOOR flag is added to make much more
+ * important processing faster
+ */
+ if (c_ptr->feat == FEAT_OPEN) return;
+ if (c_ptr->feat == FEAT_BROKEN) return;
+
+ /* Traps only appears on empty floor */
+ if (!cave_floor_grid(c_ptr) &&
+ !(f_info[c_ptr->feat].flags1 & (FF1_DOOR))) return;
+
+ /* Set flags */
+ if (f_info[c_ptr->feat].flags1 & FF1_DOOR) flags = FTRAP_DOOR;
+ else flags = FTRAP_FLOOR;
+
+ /* Try 100 times */
+ cnt = 100;
+ while (cnt--)
+ {
+ trap = randint(max_t_idx - 1);
+ t_ptr = &t_info[trap];
+
+ /* No traps below their minlevel */
+ if (t_ptr->minlevel > dun_level) continue;
+
+ /* is this a correct trap now? */
+ if (!(t_ptr->flags & flags)) continue;
+
+ /*
+ * Hack -- No trap door at the bottom of dungeon or in flat
+ * (non dungeon) places or on quest levels
+ */
+ if ((trap == TRAP_OF_SINKING) &&
+ ((d_ptr->maxdepth == dun_level) ||
+ (dungeon_flags1 & DF1_FLAT) || (is_quest(dun_level))) )
+ {
+ continue;
+ }
+
+ /* How probable is this trap */
+ if (rand_int(100) < t_ptr->probability)
+ {
+ c_ptr->t_idx = trap;
+ break;
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * Places a random trap on the given chest.
+ *
+ * The object must be a valid chest.
+ */
+void place_trap_object(object_type *o_ptr)
+{
+ s16b trap;
+ trap_type *t_ptr;
+ int cnt;
+
+ /* No traps in town or on first level */
+ if (dun_level <= 1)
+ {
+ /* empty chest were already looted, therefore known */
+ o_ptr->ident |= IDENT_KNOWN;
+ return;
+ }
+
+ /* Try 100 times */
+ cnt = 100;
+ while (cnt--)
+ {
+ trap = randint(max_t_idx - 1);
+ t_ptr = &t_info[trap];
+
+ /* no traps below their minlevel */
+ if (t_ptr->minlevel > dun_level) continue;
+
+ /* Is this a correct trap now? */
+ if (!(t_ptr->flags & FTRAP_CHEST)) continue;
+
+ /* How probable is this trap */
+ if (rand_int(100) < t_ptr->probability)
+ {
+ o_ptr->pval = trap;
+ break;
+ }
+ }
+
+ return;
+}
+
+/* Dangerous trap placing function */
+void wiz_place_trap(int y, int x, int idx)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Dangerous enough as it is... */
+ if (!cave_floor_grid(c_ptr) && (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))) return;
+
+ c_ptr->t_idx = idx;
+}
+
+/*
+ * Here begin monster traps code
+ */
+
+/*
+ * Hook to determine if an object is a device
+ */
+static bool item_tester_hook_device(object_type *o_ptr)
+{
+ if (((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->pval != 0)) ||
+ (o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * Hook to determine if an object is a potion
+ */
+static bool item_tester_hook_potion(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_POTION) ||
+ (o_ptr->tval == TV_POTION2)) return (TRUE);
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * The trap setting code for rogues -MWK-
+ *
+ * Also, it will fail or give weird results if the tvals are resorted!
+ */
+void do_cmd_set_trap(void)
+{
+ int item_kit, item_load, i;
+ int num;
+
+ object_type *o_ptr, *j_ptr, *i_ptr;
+
+ cptr q, s, c;
+
+ object_type object_type_body;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Check some conditions */
+ if (p_ptr->blind)
+ {
+ msg_print("You can't see anything.");
+ return;
+ }
+ if (no_lite())
+ {
+ msg_print("You don't dare to set a trap in the darkness.");
+ return;
+ }
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Only set traps on clean floor grids */
+ if (!cave_clean_bold(p_ptr->py, p_ptr->px))
+ {
+ msg_print("You cannot set a trap on this.");
+ return;
+ }
+
+ /* Restrict choices to trapkits */
+ item_tester_tval = TV_TRAPKIT;
+
+ /* Get an item */
+ q = "Use which trapping kit? ";
+ s = "You have no trapping kits.";
+ if (!get_item(&item_kit, q, s, USE_INVEN)) return;
+
+ o_ptr = &p_ptr->inventory[item_kit];
+
+ /* Trap kits need a second object */
+ switch (o_ptr->sval)
+ {
+ case SV_TRAPKIT_BOW:
+ item_tester_tval = TV_ARROW;
+ break;
+ case SV_TRAPKIT_XBOW:
+ item_tester_tval = TV_BOLT;
+ break;
+ case SV_TRAPKIT_SLING:
+ item_tester_tval = TV_SHOT;
+ break;
+ case SV_TRAPKIT_POTION:
+ item_tester_hook = item_tester_hook_potion;
+ break;
+ case SV_TRAPKIT_SCROLL:
+ item_tester_tval = TV_SCROLL;
+ break;
+ case SV_TRAPKIT_DEVICE:
+ item_tester_hook = item_tester_hook_device;
+ break;
+ default:
+ msg_print("Unknown trapping kit type!");
+ break;
+ }
+
+ /* Get the second item */
+ q = "Load with what? ";
+ s = "You have nothing to load that trap with.";
+ if (!get_item(&item_load, q, s, USE_INVEN)) return;
+
+ /* Get the second object */
+ j_ptr = &p_ptr->inventory[item_load];
+
+ /* Assume a single object */
+ num = 1;
+
+ /* In some cases, take multiple objects to load */
+ if (o_ptr->sval != SV_TRAPKIT_DEVICE)
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f3 & TR3_XTRA_SHOTS) && (o_ptr->pval > 0)) num += o_ptr->pval;
+
+ if (f2 & (TRAP2_AUTOMATIC_5 | TRAP2_AUTOMATIC_99)) num = 99;
+
+ if (num > j_ptr->number) num = j_ptr->number;
+
+ c = format("How many (1-%d)? ", num);
+
+ /* Ask for number of items to use */
+ num = get_quantity(c, num);
+ }
+
+ /* Canceled */
+ if (!num) return;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object for trap content */
+ object_copy(i_ptr, j_ptr);
+
+ /* Set number */
+ i_ptr->number = num;
+
+ /* Drop it here */
+ cave[p_ptr->py][p_ptr->px].special = floor_carry(p_ptr->py, p_ptr->px, i_ptr);
+
+ /* Obtain local object for trap trigger kit */
+ object_copy(i_ptr, o_ptr);
+
+ /* Set number */
+ i_ptr->number = 1;
+
+ /* Drop it here */
+ cave[p_ptr->py][p_ptr->px].special2 = floor_carry(p_ptr->py, p_ptr->px, i_ptr);
+
+ /* Modify, Describe, Optimize */
+ inven_item_increase(item_kit, -1);
+ inven_item_describe(item_kit);
+ inven_item_increase(item_load, -num);
+ inven_item_describe(item_load);
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ if (inven_item_optimize(i)) break;
+ }
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ inven_item_optimize(i);
+ }
+
+ /* Actually set the trap */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MON_TRAP);
+}
+
+/*
+ * Monster hitting a rod trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool mon_hit_trap_aux_rod(int m_idx, object_type *o_ptr)
+{
+ int dam = 0, typ = 0;
+ int rad = 0;
+ monster_type *m_ptr = &m_list[m_idx];
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* Depend on rod type */
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_DETECT_TRAP:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ break;
+ case SV_ROD_DETECTION:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ break;
+ case SV_ROD_ILLUMINATION:
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 15);
+ rad = 3;
+ lite_room(y, x);
+ break;
+ case SV_ROD_CURING:
+ typ = GF_OLD_HEAL;
+ dam = damroll(3, 4); /* and heal conf? */
+ break;
+ case SV_ROD_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 300;
+ break;
+ case SV_ROD_SPEED:
+ typ = GF_OLD_SPEED;
+ dam = 50;
+ break;
+ case SV_ROD_TELEPORT_AWAY:
+ typ = GF_AWAY_ALL;
+ dam = MAX_SIGHT * 5;
+ break;
+ case SV_ROD_DISARMING:
+ break;
+ case SV_ROD_LITE:
+ typ = GF_LITE_WEAK;
+ dam = damroll(6, 8);
+ break;
+ case SV_ROD_SLEEP_MONSTER:
+ typ = GF_OLD_SLEEP;
+ dam = 50;
+ break;
+ case SV_ROD_SLOW_MONSTER:
+ typ = GF_OLD_SLOW;
+ dam = 50;
+ break;
+ case SV_ROD_DRAIN_LIFE:
+ typ = GF_OLD_DRAIN;
+ dam = 75;
+ break;
+ case SV_ROD_POLYMORPH:
+ typ = GF_OLD_POLY;
+ dam = 50;
+ break;
+ case SV_ROD_ACID_BOLT:
+ typ = GF_ACID;
+ dam = damroll(6, 8);
+ break;
+ case SV_ROD_ELEC_BOLT:
+ typ = GF_ELEC;
+ dam = damroll(3, 8);
+ break;
+ case SV_ROD_FIRE_BOLT:
+ typ = GF_FIRE;
+ dam = damroll(8, 8);
+ break;
+ case SV_ROD_COLD_BOLT:
+ typ = GF_COLD;
+ dam = damroll(5, 8);
+ break;
+ case SV_ROD_ACID_BALL:
+ typ = GF_ACID;
+ dam = 60;
+ rad = 2;
+ break;
+ case SV_ROD_ELEC_BALL:
+ typ = GF_ELEC;
+ dam = 32;
+ rad = 2;
+ break;
+ case SV_ROD_FIRE_BALL:
+ typ = GF_FIRE;
+ dam = 72;
+ rad = 2;
+ break;
+ case SV_ROD_COLD_BALL:
+ typ = GF_COLD;
+ dam = 48;
+ rad = 2;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ if (typ) (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a staff trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool mon_hit_trap_aux_staff(int m_idx, object_type *o_ptr)
+{
+ /* Monster pointer and position */
+ monster_type *m_ptr = &m_list[m_idx];
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* sval and base level of the staff */
+ int sval = o_ptr->sval;
+
+ /* Damage amount, type, and radius */
+ int dam = 0, typ = 0;
+ int rad = 0;
+
+ /* Depend on staff type */
+ switch (sval)
+ {
+#if 0 /*must be tested*/
+ case SV_STAFF_IDENTIFY:
+ case SV_STAFF_MANA:
+ case SV_STAFF_REMOVE_CURSES:
+ case SV_STAFF_REVEAL_WAYS:
+ case SV_STAFF_SENSE_MONSTER:
+ case SV_STAFF_VISION:
+ case SV_STAFF_DISARM:
+ return (FALSE);
+
+ case SV_STAFF_LIGHT:
+ lite_room(y, x);
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 8);
+ rad = 2;
+ break;
+
+ case SV_STAFF_SUMMON:
+ for (k = 0; k < randint(4) ; k++)
+ (void)summon_specific(y, x, dun_level, 0);
+ return (FALSE);
+
+ case SV_STAFF_TELEPORTATION:
+ typ = GF_AWAY_ALL;
+ dam = 100 + 2 * level;
+ break;
+
+ case SV_STAFF_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = m_ptr->maxhp * (150 + 7 * level) / 1000;
+ break;
+
+ case SV_STAFF_SHAKE:
+ earthquake(y, x, 4 + level / 5); /* was 10 */
+ return (FALSE);
+
+ case SV_STAFF_RECOVERY:
+ m_ptr->bleeding = 0;
+ m_ptr->poisoned = 0;
+ return (FALSE);
+
+ case SV_STAFF_GENOCIDE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ genocide_aux(FALSE, r_ptr->d_char);
+ /* although there's no point in a multiple genocide trap... */
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+ }
+
+ case SV_STAFF_SENSE_HIDDEN:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ return (FALSE);
+
+ case SV_STAFF_WISH:
+ acquirement(y, x, randint(2) + 1, TRUE, FALSE);
+ return (FALSE);
+
+ case SV_STAFF_MITHRANDIR:
+ typ = GF_HOLY_FIRE;
+ dam = 50 + 6 * level;
+ rad = 9; /* instead of LOS */
+
+ /* How to implement these ? */
+ case SV_STAFF_FIERY_SHIELD:
+ case SV_STAFF_WINGS_WIND:
+ case SV_STAFF_PROBABILITY_TRAVEL:
+
+#endif
+
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a scroll trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool mon_hit_trap_aux_scroll(int m_idx, int sval)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int dam = 0, typ = 0;
+ int rad = 0;
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+ int k;
+
+ /* Depend on scroll type */
+ switch (sval)
+ {
+ case SV_SCROLL_CURSE_ARMOR:
+ case SV_SCROLL_CURSE_WEAPON:
+ case SV_SCROLL_TRAP_CREATION: /* these don't work :-( */
+ case SV_SCROLL_WORD_OF_RECALL: /* should these? */
+ case SV_SCROLL_IDENTIFY:
+ case SV_SCROLL_STAR_IDENTIFY:
+ case SV_SCROLL_MAPPING:
+ case SV_SCROLL_DETECT_GOLD:
+ case SV_SCROLL_DETECT_ITEM:
+ case SV_SCROLL_REMOVE_CURSE:
+ case SV_SCROLL_STAR_REMOVE_CURSE:
+ case SV_SCROLL_ENCHANT_ARMOR:
+ case SV_SCROLL_ENCHANT_WEAPON_TO_HIT:
+ case SV_SCROLL_ENCHANT_WEAPON_TO_DAM:
+ case SV_SCROLL_STAR_ENCHANT_ARMOR:
+ case SV_SCROLL_STAR_ENCHANT_WEAPON:
+ case SV_SCROLL_RECHARGING:
+ case SV_SCROLL_DETECT_DOOR:
+ case SV_SCROLL_DETECT_INVIS:
+ case SV_SCROLL_SATISFY_HUNGER:
+ case SV_SCROLL_RUNE_OF_PROTECTION:
+ case SV_SCROLL_TRAP_DOOR_DESTRUCTION:
+ case SV_SCROLL_PROTECTION_FROM_EVIL:
+ return (FALSE);
+ case SV_SCROLL_DARKNESS:
+ unlite_room(y, x);
+ typ = GF_DARK_WEAK;
+ dam = 10;
+ rad = 3;
+ break;
+ case SV_SCROLL_AGGRAVATE_MONSTER:
+ aggravate_monsters(m_idx);
+ return (FALSE);
+ case SV_SCROLL_SUMMON_MONSTER:
+ for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, 0);
+ return (FALSE);
+ case SV_SCROLL_SUMMON_UNDEAD:
+ for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, SUMMON_UNDEAD);
+ return (FALSE);
+ case SV_SCROLL_PHASE_DOOR:
+ typ = GF_AWAY_ALL;
+ dam = 10;
+ break;
+ case SV_SCROLL_TELEPORT:
+ typ = GF_AWAY_ALL;
+ dam = 100;
+ break;
+ case SV_SCROLL_TELEPORT_LEVEL:
+ delete_monster(y, x);
+ return (TRUE);
+ case SV_SCROLL_LIGHT:
+ lite_room(y, x);
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 8);
+ rad = 2;
+ break;
+ case SV_SCROLL_DETECT_TRAP:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ return (FALSE);
+ case SV_SCROLL_BLESSING:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(1, 4);
+ break;
+ case SV_SCROLL_HOLY_CHANT:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(2, 4);
+ break;
+ case SV_SCROLL_HOLY_PRAYER:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(4, 4);
+ break;
+ case SV_SCROLL_MONSTER_CONFUSION:
+ typ = GF_OLD_CONF;
+ dam = damroll(5, 10);
+ break;
+ case SV_SCROLL_STAR_DESTRUCTION:
+ destroy_area(y, x, 15, TRUE, FALSE);
+ return (FALSE);
+ case SV_SCROLL_DISPEL_UNDEAD:
+ typ = GF_DISP_UNDEAD;
+ rad = 5;
+ dam = 60;
+ break;
+ case SV_SCROLL_GENOCIDE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ genocide_aux(FALSE, r_ptr->d_char);
+ /* although there's no point in a multiple genocide trap... */
+ return (!(r_ptr->flags1 & RF1_UNIQUE));
+ }
+ case SV_SCROLL_MASS_GENOCIDE:
+ for (k = 0; k < 8; k++)
+ delete_monster(y + ddy[k], x + ddx[k]);
+ delete_monster(y, x);
+ return (TRUE);
+ case SV_SCROLL_ACQUIREMENT:
+ acquirement(y, x, 1, TRUE, FALSE);
+ return (FALSE);
+ case SV_SCROLL_STAR_ACQUIREMENT:
+ acquirement(y, x, randint(2) + 1, TRUE, FALSE);
+ return (FALSE);
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a wand trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool mon_hit_trap_aux_wand(int m_idx, object_type *o_ptr)
+{
+ /* Monster pointer and position */
+ monster_type *m_ptr = &m_list[m_idx];
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* sval and bonus level of the wand */
+ int sval = o_ptr->sval;
+
+ /* Damage amount, type, and radius */
+ int dam = 0, typ = 0;
+ int rad = 0;
+
+ /* Depend on wand type */
+ switch (sval)
+ {
+
+#if 0 /* must be tested */
+
+ case SV_WAND_MANATHRUST:
+ typ = GF_MANA;
+ dam = damroll(3 + level, 1 + 2 * level / 5 );
+ break;
+
+ case SV_WAND_FIREFLASH:
+ typ = GF_FIRE;
+ dam = 20 + level * 10;
+ rad = 2 + level / 10;
+ break;
+
+ case SV_WAND_NOXIOUS_CLOUD:
+ typ = GF_POIS;
+ dam = 7 + 3 * level;
+ rad = 3;
+ break;
+
+ case SV_WAND_THUNDERSTORM:
+ typ = GF_ELEC; /* GF_LITE, GF_SOUND ??? */
+ dam = damroll(5 + level / 5, 10 + level / 2);
+ break;
+
+ case SV_WAND_DIG:
+ case SV_WAND_THRAIN:
+ typ = GF_KILL_WALL;
+ dam = 20 + randint(30);
+ break;
+
+ case SV_WAND_STRIKE:
+ typ = GF_FORCE;
+ dam = 50 + level;
+ break;
+
+ case SV_WAND_TELEPORT_AWAY:
+ typ = GF_AWAY_ALL;
+ dam = MAX_SIGHT * 5;
+ break;
+
+ case SV_WAND_SUMMON_ANIMAL:
+ summon_specific(y, x, dun_level, SUMMON_ANIMAL); /* friendly ?*/
+ return (FALSE);
+
+ case SV_WAND_SLOW_MONSTER:
+ typ = GF_OLD_SLOW;
+ dam = 40 + 16 * level / 5;
+ break;
+
+ case SV_WAND_BANISHMENT:
+ typ = GF_AWAY_ALL;
+ dam = 40 + 16 * level / 5;
+ rad = 9; /* instead of LOS */
+ break;
+
+ case SV_WAND_CHARM:
+ typ = GF_CHARM;
+ dam = 10 + 3 * level;
+ break;
+
+ case SV_WAND_CONFUSE:
+ typ = GF_OLD_CONF;
+ dam = 10 + 3 * level;
+ break;
+
+ case SV_WAND_HEAL_MONSTER:
+ typ = GF_OLD_HEAL;
+ dam = 20 + 38 * level / 5;
+ break;
+
+ case SV_WAND_SPEED:
+ case SV_WAND_HASTE_MONSTER:
+ typ = GF_OLD_SPEED;
+ dam = damroll(5, 10);
+ break;
+
+ case SV_WAND_STONE_PRISON:
+ wall_stone(y, x);
+ return (FALSE);
+
+ case SV_WAND_DISPERSE_MAGIC:
+ m_ptr->confused = 0;
+ m_ptr->mspeed = 0;
+ return (FALSE);
+
+ case SV_WAND_ICE_STORM:
+ typ = GF_COLD;
+ dam = 80 + 4 * level;
+ rad = 1 + 3 * level / 50;
+ break;
+
+ case SV_WAND_TIDAL_WAVE:
+ typ = GF_WAVE;
+ dam = 40 + 4 * level;
+ rad = 6 + level / 5;
+ break;
+
+ case SV_WAND_FIREWALL:
+ typ = GF_FIRE;
+ dam = 40 + 3 * level;
+ rad = 1; /*instead of beam*/
+
+ /* Not sure about these */
+ case SV_WAND_MAGELOCK:
+ case SV_WAND_DEMON_BLADE:
+ case SV_WAND_POISON_BLOOD:
+
+#endif
+
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a potions trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool mon_hit_trap_aux_potion(int m_idx, object_type *o_ptr)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int dam = 0, typ = 0;
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+ int sval = o_ptr->sval;
+
+ /* Depend on potion type */
+ if (o_ptr->tval == TV_POTION)
+ {
+ switch (sval)
+ {
+ /* Nothing happens */
+ case SV_POTION_WATER:
+ case SV_POTION_APPLE_JUICE:
+ case SV_POTION_SLIME_MOLD:
+ case SV_POTION_SALT_WATER:
+ case SV_POTION_DEC_STR:
+ case SV_POTION_DEC_INT:
+ case SV_POTION_DEC_WIS:
+ case SV_POTION_DEC_DEX:
+ case SV_POTION_DEC_CON:
+ case SV_POTION_DEC_CHR:
+ case SV_POTION_INFRAVISION:
+ case SV_POTION_DETECT_INVIS:
+ case SV_POTION_SLOW_POISON:
+ case SV_POTION_CURE_POISON:
+ case SV_POTION_RESIST_HEAT:
+ case SV_POTION_RESIST_COLD:
+ case SV_POTION_RESTORE_MANA:
+ case SV_POTION_RESTORE_EXP:
+ case SV_POTION_RES_STR:
+ case SV_POTION_RES_INT:
+ case SV_POTION_RES_WIS:
+ case SV_POTION_RES_DEX:
+ case SV_POTION_RES_CON:
+ case SV_POTION_RES_CHR:
+ case SV_POTION_INC_STR:
+ case SV_POTION_INC_INT:
+ case SV_POTION_INC_WIS:
+ case SV_POTION_INC_DEX:
+ case SV_POTION_INC_CON:
+ case SV_POTION_INC_CHR:
+ case SV_POTION_AUGMENTATION:
+ case SV_POTION_RUINATION: /* ??? */
+ case SV_POTION_ENLIGHTENMENT:
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ case SV_POTION_SELF_KNOWLEDGE:
+ return (FALSE);
+
+ case SV_POTION_EXPERIENCE:
+ if (m_ptr->level < MONSTER_LEVEL_MAX)
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + 1);
+ monster_check_experience(m_idx, FALSE);
+ }
+ return (FALSE);
+ case SV_POTION_SLOWNESS:
+ typ = GF_OLD_SLOW;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_POISON:
+ typ = GF_POIS;
+ dam = damroll(8, 6);
+ break;
+ case SV_POTION_CONFUSION:
+ typ = GF_CONFUSION;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_BLINDNESS:
+ typ = GF_DARK;
+ dam = 10;
+ break;
+ case SV_POTION_SLEEP:
+ typ = GF_OLD_SLEEP;
+ dam = damroll (4, 6);
+ break;
+ case SV_POTION_LOSE_MEMORIES:
+ typ = GF_OLD_CONF;
+ dam = damroll(10, 10);
+ break;
+ case SV_POTION_DETONATIONS:
+ typ = GF_DISINTEGRATE;
+ dam = damroll(20, 20);
+ break;
+ case SV_POTION_DEATH:
+ typ = GF_NETHER;
+ dam = damroll(100, 20);
+ break;
+ case SV_POTION_BOLDNESS:
+ m_ptr->monfear = 0;
+ return (FALSE);
+ case SV_POTION_SPEED:
+ typ = GF_OLD_SPEED;
+ dam = damroll(5, 10);
+ break;
+ case SV_POTION_HEROISM:
+ case SV_POTION_BESERK_STRENGTH:
+ m_ptr->monfear = 0;
+ typ = GF_OLD_HEAL;
+ dam = damroll(2, 10);
+ break;
+ case SV_POTION_CURE_LIGHT:
+ typ = GF_OLD_HEAL;
+ dam = damroll(3, 4);
+ break;
+ case SV_POTION_CURE_SERIOUS:
+ typ = GF_OLD_HEAL;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_CURE_CRITICAL:
+ typ = GF_OLD_HEAL;
+ dam = damroll(6, 8);
+ break;
+ case SV_POTION_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 300;
+ break;
+ case SV_POTION_STAR_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 1000;
+ break;
+ case SV_POTION_LIFE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ {
+ typ = GF_HOLY_FIRE;
+ dam = damroll(20, 20);
+ }
+ else
+ {
+ typ = GF_OLD_HEAL;
+ dam = 5000;
+ }
+ break;
+ }
+ default:
+ return (FALSE);
+
+ }
+ }
+ else
+ {}
+
+ /* Actually hit the monster */
+ (void) project_m( -2, 0, y, x, dam, typ);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a monster trap -MWK-
+ * Returns True if the monster died, false otherwise
+ */
+bool mon_hit_trap(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+#if 0 /* DGDGDGDG */
+ monster_lore *l_ptr = &l_list[m_ptr->r_idx];
+#endif
+
+ object_type *kit_o_ptr, *load_o_ptr, *j_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_type object_type_body;
+
+ int mx = m_ptr->fx;
+ int my = m_ptr->fy;
+
+ int difficulty;
+ int smartness;
+
+ char m_name[80];
+
+ bool notice = FALSE;
+ bool disarm = FALSE;
+ bool remove = FALSE;
+ bool dead = FALSE;
+ bool fear = FALSE;
+ s32b special = 0;
+
+ int dam, chance, shots;
+ int mul = 0;
+ int breakage = -1;
+
+ int cost = 0;
+
+ /* Get the trap objects */
+ kit_o_ptr = &o_list[cave[my][mx].special2];
+ load_o_ptr = &o_list[cave[my][mx].special];
+ j_ptr = &object_type_body;
+
+ /* Get trap properties */
+ object_flags(kit_o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can set off check */
+ /* Ghosts only set off Ghost traps */
+ if ((r_ptr->flags2 & RF2_PASS_WALL) && !(f2 & TRAP2_KILL_GHOST)) return (FALSE);
+
+ /* Some traps are specialized to some creatures */
+ if (f2 & TRAP2_ONLY_MASK)
+ {
+ bool affect = FALSE;
+ if ((f2 & TRAP2_ONLY_DRAGON) && (r_ptr->flags3 & RF3_DRAGON)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_DEMON) && (r_ptr->flags3 & RF3_DEMON)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_UNDEAD) && (r_ptr->flags3 & RF3_UNDEAD)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_EVIL) && (r_ptr->flags3 & RF3_EVIL)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_ANIMAL) && (r_ptr->flags3 & RF3_ANIMAL)) affect = TRUE;
+
+ /* Don't set it off if forbidden */
+ if (!affect) return (FALSE);
+ }
+
+ /* Get detection difficulty */
+ difficulty = 25;
+
+ /* Some traps are well-hidden */
+ if (f1 & TR1_STEALTH)
+ {
+ difficulty += 10 * (kit_o_ptr->pval);
+ }
+
+ /* Get monster smartness for trap detection */
+ /* Higher level monsters are smarter */
+ smartness = r_ptr->level;
+
+ /* Smart monsters are better at detecting traps */
+ if (r_ptr->flags2 & RF2_SMART) smartness += 10;
+
+ /* Some monsters are great at detecting traps */
+#if 0 /* DGDGDGDG */
+ if (r_ptr->flags2 & RF2_NOTICE_TRAP) smartness += 20;
+#endif
+ /* Some monsters have already noticed one of out traps */
+ if (m_ptr->smart & SM_NOTE_TRAP) smartness += 20;
+
+ /* Stupid monsters are no good at detecting traps */
+ if (r_ptr->flags2 & (RF2_STUPID | RF2_EMPTY_MIND)) smartness = -150;
+
+ /* Check if the monster notices the trap */
+ if (randint(300) > (difficulty - smartness + 150)) notice = TRUE;
+
+ /* Disarm check */
+ if (notice)
+ {
+ /* The next traps will be easier to spot! */
+ m_ptr->smart |= SM_NOTE_TRAP;
+
+ /* Tell the player about it */
+#if 0 /* DGDGDGDG */
+ if (m_ptr->ml) l_ptr->r_flags2 |= (RF2_NOTICE_TRAP & r_ptr->flags2);
+#endif
+ /* Get trap disarming difficulty */
+ difficulty = (kit_o_ptr->ac + kit_o_ptr->to_a);
+
+ /* Get monster disarming ability */
+ /* Higher level monsters are better */
+ smartness = r_ptr->level / 5;
+
+ /* Some monsters are great at disarming */
+#if 0 /* DGDGDGDG */
+ if (r_ptr->flags2 & RF2_DISARM_TRAP) smartness += 20;
+#endif
+ /* After disarming one trap, the next is easier */
+#if 0 /* DGDGDGDG */
+ if (m_ptr->status & STATUS_DISARM_TRAP) smartness += 20;
+#endif
+ /* Smart monsters are better at disarming */
+ if (r_ptr->flags2 & RF2_SMART) smartness *= 2;
+
+ /* Stupid monsters never disarm traps */
+ if (r_ptr->flags2 & RF2_STUPID) smartness = -150;
+
+ /* Nonsmart animals never disarm traps */
+ if ((r_ptr->flags3 & RF3_ANIMAL) && !(r_ptr->flags2 & RF2_SMART)) smartness = -150;
+
+ /* Check if the monster disarms the trap */
+ if (randint(120) > (difficulty - smartness + 80)) disarm = TRUE;
+ }
+
+ /* If disarmed, remove the trap and print a message */
+ if (disarm)
+ {
+ remove = TRUE;
+
+ /* Next time disarming will be easier */
+#if 0 /* DGDGDGDG */
+ m_ptr->status |= STATUS_DISARM_TRAP;
+#endif
+ if (m_ptr->ml)
+ {
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Tell the player about it */
+#if 0 /* DGDGDGDG */
+ l_ptr->r_flags2 |= (RF2_DISARM_TRAP & r_ptr->flags2);
+#endif
+ /* Print a message */
+ msg_format("%^s disarms a trap!", m_name);
+ }
+ }
+
+ /* Otherwise, activate the trap! */
+ else
+ {
+ /* Message for visible monster */
+ if (m_ptr->ml)
+ {
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s sets off a trap!", m_name);
+ }
+ else
+ {
+ /* No message if monster isn't visible ? */
+ }
+
+ /* Next time be more careful */
+ if (randint(100) < 80) m_ptr->smart |= SM_NOTE_TRAP;
+
+ /* Actually activate the trap */
+ switch (kit_o_ptr->sval)
+ {
+ case SV_TRAPKIT_BOW:
+ case SV_TRAPKIT_XBOW:
+ case SV_TRAPKIT_SLING:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+ /* Total base damage */
+ dam = damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d + kit_o_ptr->to_d;
+
+ /* Total hit probability */
+ chance = (kit_o_ptr->to_h + load_o_ptr->to_h + 20) * BTH_PLUS_ADJ;
+
+ /* Damage multiplier */
+ if (kit_o_ptr->sval == SV_TRAPKIT_BOW) mul = 3;
+ if (kit_o_ptr->sval == SV_TRAPKIT_XBOW) mul = 4;
+ if (kit_o_ptr->sval == SV_TRAPKIT_SLING) mul = 2;
+ if (f3 & TR3_XTRA_MIGHT) mul += kit_o_ptr->pval;
+ if (mul < 0) mul = 0;
+
+ /* Multiply damage */
+ dam *= mul;
+
+ /* Check if we hit the monster */
+ if (test_hit_fire(chance, r_ptr->ac, TRUE))
+ {
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s is hit by a missile.", m_name);
+ }
+
+ /* Apply slays, brand, critical hits */
+ dam = tot_dam_aux(load_o_ptr, dam, m_ptr, &special);
+ dam = critical_shot(load_o_ptr->weight, load_o_ptr->to_h, dam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (dam < 0) dam = 0;
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(m_idx, dam, &fear, note_dies))
+ {
+ /* Dead monster */
+ dead = TRUE;
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(m_idx, dam);
+
+ if (special) attack_special(m_ptr, special, dam);
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+
+ }
+
+ /* Exploding ammo */
+ if (load_o_ptr->pval2 != 0)
+ {
+ int rad = 0;
+ int dam = (damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d)*2;
+ int flag = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL |
+ PROJECT_JUMP;
+
+ switch (load_o_ptr->sval)
+ {
+ case SV_AMMO_LIGHT:
+ rad = 2;
+ dam /= 2;
+ break;
+ case SV_AMMO_NORMAL:
+ rad = 3;
+ break;
+ case SV_AMMO_HEAVY:
+ rad = 4;
+ dam *= 2;
+ break;
+ }
+
+ project(0, rad, my, mx, dam, load_o_ptr->pval2, flag);
+
+ breakage = 100;
+ }
+ else
+ {
+ breakage = breakage_chance(load_o_ptr);
+ }
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_ptr->next_o_idx);
+ kit_o_ptr->next_o_idx = 0;
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(j_ptr, breakage, my, mx);
+
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_POTION:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s is hit by fumes.", m_name);
+ }
+
+ /* Get the potion effect */
+ dead = mon_hit_trap_aux_potion(m_idx, load_o_ptr);
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_ptr->next_o_idx);
+ kit_o_ptr->next_o_idx = 0;
+ }
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_SCROLL:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s activates a spell!", m_name);
+ }
+
+ /* Get the potion effect */
+ dead = mon_hit_trap_aux_scroll(m_idx, load_o_ptr->sval);
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_ptr->next_o_idx);
+ kit_o_ptr->next_o_idx = 0;
+ }
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_DEVICE:
+ {
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Extract mana cost of the rod tip */
+ u32b tf1, tf2, tf3, tf4, tf5, tesp;
+ object_kind *tip_o_ptr = &k_info[lookup_kind(TV_ROD, load_o_ptr->pval)];
+ object_flags(load_o_ptr, &tf1, &tf2, &tf3, &tf4, &tf5, &tesp);
+ cost = (tf4 & TR4_CHEAPNESS) ? tip_o_ptr->pval / 2 : tip_o_ptr->pval;
+ if (cost <= 0) cost = 1;
+ }
+
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (shots > load_o_ptr->timeout / cost) shots = load_o_ptr->timeout / cost;
+ }
+ else
+ {
+ if (shots > load_o_ptr->pval) shots = load_o_ptr->pval;
+ }
+
+ while (shots-- && !dead)
+ {
+#if 0
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s is hit by some magic.", m_name);
+ }
+#endif
+ /* Get the effect effect */
+ switch (load_o_ptr->tval)
+ {
+ case TV_ROD_MAIN:
+ dead = mon_hit_trap_aux_rod(m_idx, load_o_ptr);
+ break;
+ case TV_WAND:
+ dead = mon_hit_trap_aux_wand(m_idx, load_o_ptr);
+ break;
+ case TV_STAFF:
+ dead = mon_hit_trap_aux_staff(m_idx, load_o_ptr);
+ break;
+ }
+
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* decrease stored mana (timeout) for rods */
+ load_o_ptr->timeout -= cost;
+ }
+ else
+ {
+ /* decrease charges for wands and staves */
+ load_o_ptr->pval--;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ msg_print("oops! nonexistant trap!");
+
+ }
+
+ /* Non-automatic traps are removed */
+ if (!(f2 & (TRAP2_AUTOMATIC_5 | TRAP2_AUTOMATIC_99)))
+ {
+ remove = TRUE;
+ }
+ else if (f2 & TRAP2_AUTOMATIC_5) remove = (randint(5) == 1);
+
+ }
+
+ /* Special trap effect -- teleport to */
+ if ((f2 & TRAP2_TELEPORT_TO) && (!disarm) && (!dead))
+ {
+ teleport_monster_to(m_idx, p_ptr->py, p_ptr->px);
+ }
+
+ /* Remove the trap if inactive now */
+ if (remove) place_floor(my, mx);
+
+ /* did it die? */
+ return (dead);
+}
+
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 00000000..01b5a8e8
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,2551 @@
+/* File: types.h */
+
+/* Purpose: global type declarations */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+/*
+ * This file should ONLY be included by "angband.h"
+ */
+
+
+/*
+ * Note that "char" may or may not be signed, and that "signed char"
+ * may or may not work on all machines. So always use "s16b" or "s32b"
+ * for signed values. Also, note that unsigned values cause math problems
+ * in many cases, so try to only use "u16b" and "u32b" for "bit flags",
+ * unless you really need the extra bit of information, or you really
+ * need to restrict yourself to a single byte for storage reasons.
+ *
+ * Also, if possible, attempt to restrict yourself to sub-fields of
+ * known size (use "s16b" or "s32b" instead of "int", and "byte" instead
+ * of "bool"), and attempt to align all fields along four-byte words, to
+ * optimize storage issues on 32-bit machines. Also, avoid "bit flags"
+ * since these increase the code size and slow down execution. When
+ * you need to store bit flags, use one byte per flag, or, where space
+ * is an issue, use a "byte" or "u16b" or "u32b", and add special code
+ * to access the various bit flags.
+ *
+ * Many of these structures were developed to reduce the number of global
+ * variables, facilitate structured program design, allow the use of ascii
+ * template files, simplify access to indexed data, or facilitate efficient
+ * clearing of many variables at once.
+ *
+ * Certain data is saved in multiple places for efficient access, currently,
+ * this includes the tval/sval/weight fields in "object_type", various fields
+ * in "header_type", and the "m_idx" and "o_idx" fields in "cave_type". All
+ * of these could be removed, but this would, in general, slow down the game
+ * and increase the complexity of the code.
+ */
+
+
+
+
+
+/*
+ * Template file header information (see "init.c"). 16 bytes.
+ *
+ * Note that the sizes of many of the "arrays" are between 32768 and
+ * 65535, and so we must use "unsigned" values to hold the "sizes" of
+ * these arrays below. Normally, I try to avoid using unsigned values,
+ * since they can cause all sorts of bizarre problems, but I have no
+ * choice here, at least, until the "race" array is split into "normal"
+ * and "unique" monsters, which may or may not actually help.
+ *
+ * Note that, on some machines, for example, the Macintosh, the standard
+ * "read()" and "write()" functions cannot handle more than 32767 bytes
+ * at one time, so we need replacement functions, see "util.c" for details.
+ *
+ * Note that, on some machines, for example, the Macintosh, the standard
+ * "malloc()" function cannot handle more than 32767 bytes at one time,
+ * but we may assume that the "ralloc()" function can handle up to 65535
+ * butes at one time. We should not, however, assume that the "ralloc()"
+ * function can handle more than 65536 bytes at a time, since this might
+ * result in segmentation problems on certain older machines, and in fact,
+ * we should not assume that it can handle exactly 65536 bytes at a time,
+ * since the internal functions may use an unsigned short to specify size.
+ *
+ * In general, these problems occur only on machines (such as most personal
+ * computers) which use 2 byte "int" values, and which use "int" for the
+ * arguments to the relevent functions.
+ */
+
+typedef struct header header;
+
+struct header
+{
+ byte v_major; /* Version -- major */
+ byte v_minor; /* Version -- minor */
+ byte v_patch; /* Version -- patch */
+ byte v_extra; /* Version -- extra */
+
+
+ u16b info_num; /* Number of "info" records */
+
+ u16b info_len; /* Size of each "info" record */
+
+
+ u32b head_size; /* Size of the "header" in bytes */
+
+ u32b info_size; /* Size of the "info" array in bytes */
+
+ u32b name_size; /* Size of the "name" array in bytes */
+
+ u32b text_size; /* Size of the "text" array in bytes */
+};
+
+
+/*
+ * "Themed" objects.
+ * Probability in percent for each class of objects to be dropped.
+ * This could perhaps be an array - but that wouldn't be as clear.
+ */
+typedef struct obj_theme obj_theme;
+struct obj_theme
+{
+ byte treasure;
+ byte combat;
+ byte magic;
+ byte tools;
+};
+
+
+/*
+ * Information about terrain "features"
+ */
+
+typedef struct feature_type feature_type;
+
+struct feature_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+ u32b tunnel; /* Text for tunneling */
+ u32b block; /* Text for blocking */
+
+ byte mimic; /* Feature to mimic */
+
+ u32b flags1; /* First set of flags */
+
+ byte extra; /* Extra byte (unused) */
+
+ s16b unused; /* Extra bytes (unused) */
+
+ byte d_attr; /* Default feature attribute */
+ char d_char; /* Default feature character */
+
+
+ byte x_attr; /* Desired feature attribute */
+ char x_char; /* Desired feature character */
+
+ byte shimmer[7]; /* Shimmer colors */
+
+ int d_dice[4]; /* Number of dices */
+ int d_side[4]; /* Number of sides */
+ int d_frequency[4]; /* Frequency of damage (1 is the minimum) */
+ int d_type[4]; /* Type of damage */
+};
+
+
+/*
+ * Information about object "kinds", including player knowledge.
+ *
+ * Only "aware" and "tried" are saved in the savefile
+ */
+
+typedef struct object_kind object_kind;
+
+struct object_kind
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Object type */
+ byte sval; /* Object sub type */
+
+ s32b pval; /* Object extra info */
+ s32b pval2; /* Object extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b activate; /* Activation number */
+
+ s16b ac; /* Base armor */
+
+ byte dd, ds; /* Damage dice/sides */
+
+ s32b weight; /* Weight */
+
+ s32b cost; /* Object "base cost" */
+
+ u32b flags1; /* Flags, set 1 */
+ u32b flags2; /* Flags, set 2 */
+ u32b flags3; /* Flags, set 3 */
+ u32b flags4; /* Flags, set 4 */
+ u32b flags5; /* Flags, set 5 */
+
+ u32b oflags1; /* Obvious Flags, set 1 */
+ u32b oflags2; /* Obvious Flags, set 2 */
+ u32b oflags3; /* Obvious Flags, set 3 */
+ u32b oflags4; /* Obvious Flags, set 4 */
+ u32b oflags5; /* Obvious Flags, set 5 */
+
+ byte locale[4]; /* Allocation level(s) */
+ byte chance[4]; /* Allocation chance(s) */
+
+ byte level; /* Level */
+ byte extra; /* Something */
+
+
+ byte d_attr; /* Default object attribute */
+ char d_char; /* Default object character */
+
+
+ byte x_attr; /* Desired object attribute */
+ char x_char; /* Desired object character */
+
+
+ byte flavor; /* Special object flavor (or zero) */
+
+ bool easy_know; /* This object is always known (if aware) */
+
+
+ bool aware; /* The player is "aware" of the item's effects */
+
+ bool tried; /* The player has "tried" one of the items */
+
+ bool know; /* extractable flag for the alchemist */
+
+ u32b esp; /* ESP flags */
+ u32b oesp; /* Obvious ESP flags */
+
+ byte btval; /* Become Object type */
+ byte bsval; /* Become Object sub type */
+ bool artifact; /* Is it a normal artifact(already generated) */
+
+ s16b power; /* Power granted(if any) */
+};
+
+
+
+/*
+ * Information about "artifacts".
+ *
+ * Note that the save-file only writes "cur_num" to the savefile.
+ *
+ * Note that "max_num" is always "1" (if that artifact "exists")
+ */
+
+typedef struct artifact_type artifact_type;
+
+struct artifact_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte tval; /* Artifact type */
+ byte sval; /* Artifact sub type */
+
+ s16b pval; /* Artifact extra info */
+
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to damage */
+ s16b to_a; /* Bonus to armor */
+
+ s16b activate; /* Activation Number */
+
+ s16b ac; /* Base armor */
+
+ byte dd, ds; /* Damage when hits */
+
+ s16b weight; /* Weight */
+
+ s32b cost; /* Artifact "cost" */
+
+ u32b flags1; /* Artifact Flags, set 1 */
+ u32b flags2; /* Artifact Flags, set 2 */
+ u32b flags3; /* Artifact Flags, set 3 */
+ u32b flags4; /* Artifact Flags, set 4 */
+ u32b flags5; /* Artifact Flags, set 5 */
+
+ u32b oflags1; /* Obvious Flags, set 1 */
+ u32b oflags2; /* Obvious Flags, set 2 */
+ u32b oflags3; /* Obvious Flags, set 3 */
+ u32b oflags4; /* Obvious Flags, set 4 */
+ u32b oflags5; /* Obvious Flags, set 5 */
+
+ byte level; /* Artifact level */
+ byte rarity; /* Artifact rarity */
+
+ byte cur_num; /* Number created (0 or 1) */
+ byte max_num; /* Unused (should be "1") */
+
+ u32b esp; /* ESP flags */
+ u32b oesp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+
+ s16b set; /* Does it belongs to a set ?*/
+};
+
+
+/*
+ * Information about "ego-items".
+ */
+
+typedef struct ego_item_type ego_item_type;
+
+struct ego_item_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ bool before; /* Before or after the object name ? */
+
+ byte tval[10];
+ byte min_sval[10];
+ byte max_sval[10];
+
+ byte rating; /* Rating boost */
+
+ byte level; /* Minimum level */
+ byte rarity; /* Object rarity */
+ byte mrarity; /* Object rarity */
+
+ s16b max_to_h; /* Maximum to-hit bonus */
+ s16b max_to_d; /* Maximum to-dam bonus */
+ s16b max_to_a; /* Maximum to-ac bonus */
+
+ s16b activate; /* Activation Number */
+
+ s32b max_pval; /* Maximum pval */
+
+ s32b cost; /* Ego-item "cost" */
+
+ byte rar[5];
+ u32b flags1[5]; /* Ego-Item Flags, set 1 */
+ u32b flags2[5]; /* Ego-Item Flags, set 2 */
+ u32b flags3[5]; /* Ego-Item Flags, set 3 */
+ u32b flags4[5]; /* Ego-Item Flags, set 4 */
+ u32b flags5[5]; /* Ego-Item Flags, set 5 */
+ u32b esp[5]; /* ESP flags */
+ u32b oflags1[5]; /* Ego-Item Obvious Flags, set 1 */
+ u32b oflags2[5]; /* Ego-Item Obvious Flags, set 2 */
+ u32b oflags3[5]; /* Ego-Item Obvious Flags, set 3 */
+ u32b oflags4[5]; /* Ego-Item Obvious Flags, set 4 */
+ u32b oflags5[5]; /* Ego-Item Obvious Flags, set 5 */
+ u32b oesp[5]; /* Obvious ESP flags */
+ u32b fego[5]; /* ego flags */
+
+ u32b need_flags1; /* Ego-Item Flags, set 1 */
+ u32b need_flags2; /* Ego-Item Flags, set 2 */
+ u32b need_flags3; /* Ego-Item Flags, set 3 */
+ u32b need_flags4; /* Ego-Item Flags, set 4 */
+ u32b need_flags5; /* Ego-Item Flags, set 5 */
+ u32b need_esp; /* ESP flags */
+ u32b forbid_flags1; /* Ego-Item Flags, set 1 */
+ u32b forbid_flags2; /* Ego-Item Flags, set 2 */
+ u32b forbid_flags3; /* Ego-Item Flags, set 3 */
+ u32b forbid_flags4; /* Ego-Item Flags, set 4 */
+ u32b forbid_flags5; /* Ego-Item Flags, set 5 */
+ u32b forbid_esp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+
+/*
+ * Information about "random artifacts parts".
+ */
+typedef struct randart_part_type randart_part_type;
+struct randart_part_type
+{
+ byte tval[20];
+ byte min_sval[20];
+ byte max_sval[20];
+
+ byte level; /* Minimum level */
+ byte rarity; /* Object rarity */
+ byte mrarity; /* Object rarity */
+
+ s16b max_to_h; /* Maximum to-hit bonus */
+ s16b max_to_d; /* Maximum to-dam bonus */
+ s16b max_to_a; /* Maximum to-ac bonus */
+
+ s32b max_pval; /* Maximum pval */
+
+ s32b value; /* power value */
+ s16b max; /* Number of time it can appear on a single item */
+
+ u32b flags1; /* Ego-Item Flags, set 1 */
+ u32b flags2; /* Ego-Item Flags, set 2 */
+ u32b flags3; /* Ego-Item Flags, set 3 */
+ u32b flags4; /* Ego-Item Flags, set 4 */
+ u32b flags5; /* Ego-Item Flags, set 5 */
+ u32b esp; /* ESP flags */
+ u32b fego; /* ego flags */
+
+ u32b aflags1; /* Ego-Item Flags, set 1 */
+ u32b aflags2; /* Ego-Item Flags, set 2 */
+ u32b aflags3; /* Ego-Item Flags, set 3 */
+ u32b aflags4; /* Ego-Item Flags, set 4 */
+ u32b aflags5; /* Ego-Item Flags, set 5 */
+ u32b aesp; /* ESP flags */
+
+ s16b power; /* Power granted(if any) */
+};
+
+typedef struct randart_gen_type randart_gen_type;
+struct randart_gen_type
+{
+ int chance; /* Chance to have that number of powers */
+ int dd;
+ int ds;
+ int plus; /* xdy+plus power */
+};
+
+
+/*
+ * Monster blow structure
+ *
+ * - Method (RBM_*)
+ * - Effect (RBE_*)
+ * - Damage Dice
+ * - Damage Sides
+ */
+
+typedef struct monster_blow monster_blow;
+
+struct monster_blow
+{
+ byte method;
+ byte effect;
+ byte d_dice;
+ byte d_side;
+};
+
+
+
+/*
+ * Monster "race" information, including racial memories
+ *
+ * Note that "d_attr" and "d_char" are used for MORE than "visual" stuff.
+ *
+ * Note that "x_attr" and "x_char" are used ONLY for "visual" stuff.
+ *
+ * Note that "cur_num" (and "max_num") represent the number of monsters
+ * of the given race currently on (and allowed on) the current level.
+ * This information yields the "dead" flag for Unique monsters.
+ *
+ * Note that "max_num" is reset when a new player is created.
+ * Note that "cur_num" is reset when a new level is created.
+ *
+ * Note that several of these fields, related to "recall", can be
+ * scrapped if space becomes an issue, resulting in less "complete"
+ * monster recall (no knowledge of spells, etc). All of the "recall"
+ * fields have a special prefix to aid in searching for them.
+ */
+
+
+typedef struct monster_race monster_race;
+
+struct monster_race
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ u16b hdice; /* Creatures hit dice count */
+ u16b hside; /* Creatures hit dice sides */
+
+ s16b ac; /* Armour Class */
+
+ s16b sleep; /* Inactive counter (base) */
+ byte aaf; /* Area affect radius (1-100) */
+ byte speed; /* Speed (normally 110) */
+
+ s32b mexp; /* Exp value for kill */
+
+ s32b weight; /* Weight of the monster */
+
+ byte freq_inate; /* Inate spell frequency */
+ byte freq_spell; /* Other spell frequency */
+
+ u32b flags1; /* Flags 1 (general) */
+ u32b flags2; /* Flags 2 (abilities) */
+ u32b flags3; /* Flags 3 (race/resist) */
+ u32b flags4; /* Flags 4 (inate/breath) */
+ u32b flags5; /* Flags 5 (normal spells) */
+ u32b flags6; /* Flags 6 (special spells) */
+ u32b flags7; /* Flags 7 (movement related abilities) */
+ u32b flags8; /* Flags 8 (wilderness info) */
+ u32b flags9; /* Flags 9 (drops info) */
+
+ monster_blow blow[4]; /* Up to four blows per round */
+
+ byte body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ byte level; /* Level of creature */
+ byte rarity; /* Rarity of creature */
+
+
+ byte d_attr; /* Default monster attribute */
+ char d_char; /* Default monster character */
+
+
+ byte x_attr; /* Desired monster attribute */
+ char x_char; /* Desired monster character */
+
+
+ s16b max_num; /* Maximum population allowed per level */
+
+ byte cur_num; /* Monster population on current level */
+
+
+ s16b r_sights; /* Count sightings of this monster */
+ s16b r_deaths; /* Count deaths from this monster */
+
+ s16b r_pkills; /* Count monsters killed in this life */
+ s16b r_tkills; /* Count monsters killed in all lives */
+
+ byte r_wake; /* Number of times woken up (?) */
+ byte r_ignore; /* Number of times ignored (?) */
+
+ byte r_xtra1; /* Something (unused) */
+ byte r_xtra2; /* Something (unused) */
+
+ byte r_drop_gold; /* Max number of gold dropped at once */
+ byte r_drop_item; /* Max number of item dropped at once */
+
+ byte r_cast_inate; /* Max number of inate spells seen */
+ byte r_cast_spell; /* Max number of other spells seen */
+
+ byte r_blows[4]; /* Number of times each blow type was seen */
+
+ u32b r_flags1; /* Observed racial flags */
+ u32b r_flags2; /* Observed racial flags */
+ u32b r_flags3; /* Observed racial flags */
+ u32b r_flags4; /* Observed racial flags */
+ u32b r_flags5; /* Observed racial flags */
+ u32b r_flags6; /* Observed racial flags */
+ u32b r_flags7; /* Observed racial flags */
+ u32b r_flags8; /* Observed racial flags */
+ u32b r_flags9; /* Observed racial flags */
+
+ bool on_saved; /* Is the (unique) on a saved level ? */
+
+ byte total_visible; /* Amount of this race that are visible */
+
+ obj_theme drops; /* The drops type */
+};
+
+
+typedef struct monster_ego monster_ego;
+
+struct monster_ego
+{
+ u32b name; /* Name (offset) */
+ bool before; /* Display ego before or after */
+
+ monster_blow blow[4]; /* Up to four blows per round */
+ byte blowm[4][2];
+
+ s16b hdice; /* Creatures hit dice count */
+ s16b hside; /* Creatures hit dice sides */
+
+ s16b ac; /* Armour Class */
+
+ s16b sleep; /* Inactive counter (base) */
+ s16b aaf; /* Area affect radius (1-100) */
+ s16b speed; /* Speed (normally 110) */
+
+ s32b mexp; /* Exp value for kill */
+
+ s32b weight; /* Weight of the monster */
+
+ byte freq_inate; /* Inate spell frequency */
+ byte freq_spell; /* Other spell frequency */
+
+ /* Ego flags */
+ u32b flags1; /* Flags 1 */
+ u32b flags2; /* Flags 1 */
+ u32b flags3; /* Flags 1 */
+ u32b flags7; /* Flags 1 */
+ u32b flags8; /* Flags 1 */
+ u32b flags9; /* Flags 1 */
+ u32b hflags1; /* Flags 1 */
+ u32b hflags2; /* Flags 1 */
+ u32b hflags3; /* Flags 1 */
+ u32b hflags7; /* Flags 1 */
+ u32b hflags8; /* Flags 1 */
+ u32b hflags9; /* Flags 1 */
+
+ /* Monster flags */
+ u32b mflags1; /* Flags 1 (general) */
+ u32b mflags2; /* Flags 2 (abilities) */
+ u32b mflags3; /* Flags 3 (race/resist) */
+ u32b mflags4; /* Flags 4 (inate/breath) */
+ u32b mflags5; /* Flags 5 (normal spells) */
+ u32b mflags6; /* Flags 6 (special spells) */
+ u32b mflags7; /* Flags 7 (movement related abilities) */
+ u32b mflags8; /* Flags 8 (wilderness info) */
+ u32b mflags9; /* Flags 9 (drops info) */
+
+ /* Negative Flags, to be removed from the monster flags */
+ u32b nflags1; /* Flags 1 (general) */
+ u32b nflags2; /* Flags 2 (abilities) */
+ u32b nflags3; /* Flags 3 (race/resist) */
+ u32b nflags4; /* Flags 4 (inate/breath) */
+ u32b nflags5; /* Flags 5 (normal spells) */
+ u32b nflags6; /* Flags 6 (special spells) */
+ u32b nflags7; /* Flags 7 (movement related abilities) */
+ u32b nflags8; /* Flags 8 (wilderness info) */
+ u32b nflags9; /* Flags 9 (drops info) */
+
+ s16b level; /* Level of creature */
+ s16b rarity; /* Rarity of creature */
+
+
+ byte d_attr; /* Default monster attribute */
+ char d_char; /* Default monster character */
+
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+
+ char r_char[5]; /* Monster race allowed */
+ char nr_char[5]; /* Monster race not allowed */
+};
+
+
+
+/*
+ * Information about "vault generation"
+ */
+
+typedef struct vault_type vault_type;
+
+struct vault_type
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+
+ byte typ; /* Vault type */
+
+ byte rat; /* Vault rating */
+
+ byte hgt; /* Vault height */
+ byte wid; /* Vault width */
+
+ s16b lvl; /* level of special (if any) */
+ byte dun_type; /* Dungeon type where the level will show up */
+
+ s16b mon[10]; /* special monster */
+ int item[3]; /* number of item (usually artifact) */
+};
+
+
+/* jk */
+/* name and description are in some other arrays */
+typedef struct trap_type trap_type;
+
+struct trap_type
+{
+ s16b probability; /* probability of existence */
+ s16b another; /* does this trap easily combine */
+ s16b p1valinc; /* how much does this trap attribute to p1val */
+ byte difficulty; /* how difficult to disarm */
+ byte minlevel; /* what is the minimum level on which the traps should be */
+ byte color; /* what is the color on screen */
+ u32b flags; /* where can these traps go - and perhaps other flags */
+ bool ident; /* do we know the name */
+ s16b known; /* how well is this trap known */
+ s16b name; /* normal name like weakness */
+ s16b dd, ds; /* base damage */
+ s16b text; /* longer description once you've met this trap */
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+};
+
+
+
+/*
+ * A single "grid" in a Cave
+ *
+ * Note that several aspects of the code restrict the actual cave
+ * to a max size of 256 by 256. In partcular, locations are often
+ * saved as bytes, limiting each coordinate to the 0-255 range.
+ *
+ * The "o_idx" and "m_idx" fields are very interesting. There are
+ * many places in the code where we need quick access to the actual
+ * monster or object(s) in a given cave grid. The easiest way to
+ * do this is to simply keep the index of the monster and object
+ * (if any) with the grid, but this takes 198*66*4 bytes of memory.
+ * Several other methods come to mind, which require only half this
+ * amound of memory, but they all seem rather complicated, and would
+ * probably add enough code that the savings would be lost. So for
+ * these reasons, we simply store an index into the "o_list" and
+ * "m_list" arrays, using "zero" when no monster/object is present.
+ *
+ * Note that "o_idx" is the index of the top object in a stack of
+ * objects, using the "next_o_idx" field of objects (see below) to
+ * create the singly linked list of objects. If "o_idx" is zero
+ * then there are no objects in the grid.
+ *
+ * Note the special fields for the "MONSTER_FLOW" code.
+ */
+
+typedef struct cave_type cave_type;
+
+struct cave_type
+{
+ u16b info; /* Hack -- cave flags */
+
+ byte feat; /* Hack -- feature type */
+
+ s16b o_idx; /* Object in this grid */
+
+ s16b m_idx; /* Monster in this grid */
+
+ s16b t_idx; /* trap index (in t_list) or zero */
+
+ s16b special, special2; /* Special cave info */
+
+ s16b inscription; /* Inscription of the grid */
+
+ byte mana; /* Magical energy of the grid */
+
+ byte mimic; /* Feature to mimic */
+
+#ifdef MONSTER_FLOW
+
+ byte cost; /* Hack -- cost of flowing */
+ byte when; /* Hack -- when cost was computed */
+
+#endif
+ s16b effect; /* The lasting effects */
+};
+
+/* Lasting spell effects(clouds, ..) */
+typedef struct effect_type effect_type;
+struct effect_type
+{
+ s16b time; /* For how long */
+ s16b dam; /* How much damage */
+ s16b type; /* Of which type */
+ s16b cy; /* Center of the cast*/
+ s16b cx; /* Center of the cast*/
+ s16b rad; /* Radius -- if needed */
+ u32b flags; /* Flags */
+};
+
+/*
+ * Object information, for a specific object.
+ *
+ * Note that a "discount" on an item is permanent and never goes away.
+ *
+ * Note that inscriptions are now handled via the "quark_str()" function
+ * applied to the "note" field, which will return NULL if "note" is zero.
+ *
+ * Note that "object" records are "copied" on a fairly regular basis,
+ * and care must be taken when handling such objects.
+ *
+ * Note that "object flags" must now be derived from the object kind,
+ * the artifact and ego-item indexes, and the two "xtra" fields.
+ *
+ * Each cave grid points to one (or zero) objects via the "o_idx"
+ * field (above). Each object then points to one (or zero) objects
+ * via the "next_o_idx" field, forming a singly linked list, which
+ * in game terms, represents a "stack" of objects in the same grid.
+ *
+ * Each monster points to one (or zero) objects via the "hold_o_idx"
+ * field (below). Each object then points to one (or zero) objects
+ * via the "next_o_idx" field, forming a singly linked list, which
+ * in game terms, represents a pile of objects held by the monster.
+ *
+ * The "held_m_idx" field is used to indicate which monster, if any,
+ * is holding the object. Objects being held have "ix=0" and "iy=0".
+ */
+
+typedef struct object_type object_type;
+
+struct object_type
+{
+ s16b k_idx; /* Kind index (zero if "dead") */
+
+ byte iy; /* Y-position on map, or zero */
+ byte ix; /* X-position on map, or zero */
+
+ byte tval; /* Item type (from kind) */
+ byte sval; /* Item sub-type (from kind) */
+
+ s32b pval; /* Item extra-parameter */
+ s16b pval2; /* Item extra-parameter for some special
+ items*/
+ s32b pval3; /* Item extra-parameter for some special
+ items*/
+
+ byte discount; /* Discount (if any) */
+
+ byte number; /* Number of items */
+
+ s32b weight; /* Item weight */
+
+ byte elevel; /* Item exp level */
+ s32b exp; /* Item exp */
+
+ byte name1; /* Artifact type, if any */
+ s16b name2; /* Ego-Item type, if any */
+ s16b name2b; /* Second Ego-Item type, if any */
+
+ byte xtra1; /* Extra info type */
+ s16b xtra2; /* Extra info index */
+
+ s16b to_h; /* Plusses to hit */
+ s16b to_d; /* Plusses to damage */
+ s16b to_a; /* Plusses to AC */
+
+ s16b ac; /* Normal AC */
+
+ byte dd, ds; /* Damage dice/sides */
+
+ s16b timeout; /* Timeout Counter */
+
+ byte ident; /* Special flags */
+
+ byte marked; /* Object is marked */
+
+ u16b note; /* Inscription index */
+ u16b art_name; /* Artifact name (random artifacts) */
+
+ u32b art_flags1; /* Flags, set 1 Alas, these were necessary */
+ u32b art_flags2; /* Flags, set 2 for the random artifacts of*/
+ u32b art_flags3; /* Flags, set 3 Zangband */
+ u32b art_flags4; /* Flags, set 4 PernAngband */
+ u32b art_flags5; /* Flags, set 5 PernAngband */
+ u32b art_esp; /* Flags, set esp PernAngband */
+
+ u32b art_oflags1; /* Obvious Flags, set 1 */
+ u32b art_oflags2; /* Obvious Flags, set 2 */
+ u32b art_oflags3; /* Obvious Flags, set 3 */
+ u32b art_oflags4; /* Obvious Flags, set 4 */
+ u32b art_oflags5; /* Obvious Flags, set 5 */
+ u32b art_oesp; /* Obvious Flags, set esp */
+
+ s16b next_o_idx; /* Next object in stack (if any) */
+
+ s16b held_m_idx; /* Monster holding us (if any) */
+
+ byte sense; /* Pseudo-id status */
+
+ byte found; /* How did we find it */
+ s16b found_aux1; /* Stores info for found */
+ s16b found_aux2; /* Stores info for found */
+ s16b found_aux3; /* Stores info for found */
+ s16b found_aux4; /* Stores info for found */
+};
+
+
+/*
+ * Monster mind, use for skills and such
+ */
+typedef struct monster_mind monster_mind;
+struct monster_mind
+{
+ /*
+ * Without this, bcc can't compile because it does not
+ * allow empty structure. Remove this when you add some
+ * variables to this structure. -- Kusunose
+ */
+ byte dummy;
+};
+
+
+
+/*
+ * Monster information, for a specific monster.
+ *
+ * Note: fy, fx constrain dungeon size to 256x256
+ *
+ * The "hold_o_idx" field points to the first object of a stack
+ * of objects (if any) being carried by the monster (see above).
+ */
+
+typedef struct monster_type monster_type;
+
+struct monster_type
+{
+ s16b r_idx; /* Monster race index */
+
+ u16b ego; /* Ego monster type */
+
+ byte fy; /* Y location on map */
+ byte fx; /* X location on map */
+
+ s32b hp; /* Current Hit points */
+ s32b maxhp; /* Max Hit points */
+
+ monster_blow blow[4]; /* Up to four blows per round */
+
+ byte speed; /* Speed (normally 110) */
+ byte level; /* Level of creature */
+ s16b ac; /* Armour Class */
+ u32b exp; /* Experience */
+
+ s16b csleep; /* Inactive counter */
+
+ byte mspeed; /* Monster "speed" */
+ byte energy; /* Monster "energy" */
+
+ byte stunned; /* Monster is stunned */
+ byte confused; /* Monster is confused */
+ byte monfear; /* Monster is afraid */
+
+ s16b bleeding; /* Monster is bleeding */
+ s16b poisoned; /* Monster is poisoned */
+
+ byte cdis; /* Current dis from player */
+
+ s32b mflag; /* Extra monster flags */
+
+ bool ml; /* Monster is "visible" */
+
+ s16b hold_o_idx; /* Object being held (if any) */
+
+#ifdef WDT_TRACK_OPTIONS
+
+ byte ty; /* Y location of target */
+ byte tx; /* X location of target */
+
+ byte t_dur; /* How long are we tracking */
+
+ byte t_bit; /* Up to eight bit flags */
+
+#endif
+
+ u32b smart; /* Field for "smart_learn" */
+
+ s16b status; /* Status(friendly, pet, companion, ..) */
+
+ s16b target; /* Monster target */
+
+ s16b possessor; /* Is it under the control of a possessor ? */
+
+ monster_race *sr_ptr; /* Does it have a specific race(not in r_info) */
+
+ monster_mind *mind; /* Does it have a mind? */
+};
+
+
+
+
+/*
+ * An entry for the object/monster allocation functions
+ *
+ * Pass 1 is determined from allocation information
+ * Pass 2 is determined from allocation restriction
+ * Pass 3 is determined from allocation calculation
+ */
+
+typedef struct alloc_entry alloc_entry;
+
+struct alloc_entry
+{
+ s16b index; /* The actual index */
+
+ byte level; /* Base dungeon level */
+ byte prob1; /* Probability, pass 1 */
+ byte prob2; /* Probability, pass 2 */
+ byte prob3; /* Probability, pass 3 */
+
+ u16b total; /* Unused for now */
+};
+
+
+
+/*
+ * Available "options"
+ *
+ * - Address of actual option variable (or NULL)
+ *
+ * - Normal Value (TRUE or FALSE)
+ *
+ * - Option Page Number (or zero)
+ *
+ * - Savefile Set (or zero)
+ * - Savefile Bit in that set
+ *
+ * - Textual name (or NULL)
+ * - Textual description
+ */
+
+typedef struct option_type option_type;
+
+struct option_type
+{
+ bool *o_var;
+
+ byte o_norm;
+
+ byte o_page;
+
+ byte o_bit;
+
+ cptr o_text;
+ cptr o_desc;
+};
+
+/*
+ * A store owner
+ */
+typedef struct owner_type owner_type;
+
+struct owner_type
+{
+ u32b name; /* Name (offset) */
+
+ s16b max_cost; /* Purse limit */
+
+ byte max_inflate; /* Inflation (max) */
+ byte min_inflate; /* Inflation (min) */
+
+ byte haggle_per; /* Haggle unit */
+
+ byte insult_max; /* Insult limit */
+
+ u32b races[2][2]; /* Liked/hated races */
+ u32b classes[2][2]; /* Liked/hated classes */
+
+ s16b costs[3]; /* Costs for liked people */
+};
+
+
+
+
+/*
+ * A store, with an owner, various state flags, a current stock
+ * of items, and a table of items that are often purchased.
+ */
+typedef struct store_type store_type;
+
+struct store_type
+{
+ u16b st_idx;
+
+ u16b owner; /* Owner index */
+
+ s16b insult_cur; /* Insult counter */
+
+ s16b good_buy; /* Number of "good" buys */
+ s16b bad_buy; /* Number of "bad" buys */
+
+ s32b store_open; /* Closed until this turn */
+
+ s32b last_visit; /* Last visited on this turn */
+
+ byte stock_num; /* Stock -- Number of entries */
+ s16b stock_size; /* Stock -- Total Size of Array */
+ object_type *stock; /* Stock -- Actual stock items */
+};
+
+/*
+ * A store/building type
+ */
+typedef struct store_info_type store_info_type;
+
+struct store_info_type
+{
+ u32b name; /* Name (offset) */
+
+ s16b table[STORE_CHOICES][2]; /* Table -- Legal item kinds */
+ byte table_num; /* Number of items */
+ s16b max_obj; /* Number of items this store can hold */
+
+ u16b owners[4]; /* List of owners(refers to ow_info) */
+
+ u16b actions[6]; /* Actions(refers to ba_info) */
+
+ byte d_attr; /* Default building attribute */
+ char d_char; /* Default building character */
+
+ byte x_attr; /* Desired building attribute */
+ char x_char; /* Desired building character */
+
+ u32b flags1; /* Flags */
+};
+
+/*
+ * Stores/buildings actions
+ */
+typedef struct store_action_type store_action_type;
+
+struct store_action_type
+{
+ u32b name; /* Name (offset) */
+
+ s16b costs[3]; /* Costs for liked people */
+ char letter; /* Action letter */
+ char letter_aux; /* Action letter */
+ s16b action; /* Action code */
+ s16b action_restr; /* Action restriction */
+};
+
+/*
+ * the spell function must provide the desc
+ */
+typedef struct magic_type magic_type;
+
+struct magic_type
+{
+ byte slevel; /* Required level (to learn) */
+ byte smana; /* Required mana (to cast) */
+ byte sfail; /* Minimum chance of failure */
+ byte sexp; /* Encoded experience bonus */
+};
+
+/*
+ * Player sex info
+ */
+
+typedef struct player_sex player_sex;
+
+struct player_sex
+{
+ cptr title; /* Type of sex */
+
+ cptr winner; /* Name of winner */
+};
+
+
+/*
+ * Player racial info
+ */
+
+typedef struct player_race player_race;
+
+struct player_race
+{
+ s32b title; /* Type of race */
+ s32b desc;
+
+ s16b r_adj[6]; /* Racial stat bonuses */
+
+ char luck; /* Luck */
+
+ s16b r_dis; /* disarming */
+ s16b r_dev; /* magic devices */
+ s16b r_sav; /* saving throw */
+ s16b r_stl; /* stealth */
+ s16b r_srh; /* search ability */
+ s16b r_fos; /* search frequency */
+ s16b r_thn; /* combat (normal) */
+ s16b r_thb; /* combat (shooting) */
+
+ byte r_mhp; /* Race hit-dice modifier */
+ u16b r_exp; /* Race experience factor */
+
+ byte b_age; /* base age */
+ byte m_age; /* mod age */
+
+ byte m_b_ht; /* base height (males) */
+ byte m_m_ht; /* mod height (males) */
+ byte m_b_wt; /* base weight (males) */
+ byte m_m_wt; /* mod weight (males) */
+
+ byte f_b_ht; /* base height (females) */
+ byte f_m_ht; /* mod height (females) */
+ byte f_b_wt; /* base weight (females) */
+ byte f_m_wt; /* mod weight (females) */
+
+ byte infra; /* Infra-vision range */
+
+ u32b choice[2]; /* Legal class choices */
+
+ s16b powers[4]; /* Powers of the race */
+
+ byte body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ s16b chart; /* Chart history */
+
+ u32b flags1;
+ u32b flags2; /* flags */
+
+ u32b oflags1[PY_MAX_LEVEL + 1];
+ u32b oflags2[PY_MAX_LEVEL + 1];
+ u32b oflags3[PY_MAX_LEVEL + 1];
+ u32b oflags4[PY_MAX_LEVEL + 1];
+ u32b oflags5[PY_MAX_LEVEL + 1];
+ u32b oesp[PY_MAX_LEVEL + 1];
+ s16b opval[PY_MAX_LEVEL + 1];
+
+ char skill_basem[MAX_SKILLS];
+ u32b skill_base[MAX_SKILLS];
+ char skill_modm[MAX_SKILLS];
+ s16b skill_mod[MAX_SKILLS];
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+typedef struct player_race_mod player_race_mod;
+
+struct player_race_mod
+{
+ s32b title; /* Type of race mod */
+ s32b desc; /* Desc */
+ bool place; /* TRUE = race race modifier, FALSE = Race modifier race */
+
+ s16b r_adj[6]; /* (+) Racial stat bonuses */
+
+ char luck; /* Luck */
+ s16b mana; /* Mana % */
+
+ s16b r_dis; /* (+) disarming */
+ s16b r_dev; /* (+) magic devices */
+ s16b r_sav; /* (+) saving throw */
+ s16b r_stl; /* (+) stealth */
+ s16b r_srh; /* (+) search ability */
+ s16b r_fos; /* (+) search frequency */
+ s16b r_thn; /* (+) combat (normal) */
+ s16b r_thb; /* (+) combat (shooting) */
+
+ char r_mhp; /* (+) Race mod hit-dice modifier */
+ s16b r_exp; /* (+) Race mod experience factor */
+
+ char b_age; /* (+) base age */
+ char m_age; /* (+) mod age */
+
+ char m_b_ht; /* (+) base height (males) */
+ char m_m_ht; /* (+) mod height (males) */
+ char m_b_wt; /* (+) base weight (males) */
+ char m_m_wt; /* (+) mod weight (males) */
+
+ char f_b_ht; /* (+) base height (females) */
+ char f_m_ht; /* (+) mod height (females) */
+ char f_b_wt; /* (+) base weight (females) */
+ char f_m_wt; /* (+) mod weight (females) */
+
+ char infra; /* (+) Infra-vision range */
+
+ u32b choice[2]; /* Legal race choices */
+
+ u32b pclass[2]; /* Classes allowed */
+ u32b mclass[2]; /* Classes restricted */
+
+ s16b powers[4]; /* Powers of the subrace */
+
+ char body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ u32b flags1;
+ u32b flags2; /* flags */
+
+ u32b oflags1[PY_MAX_LEVEL + 1];
+ u32b oflags2[PY_MAX_LEVEL + 1];
+ u32b oflags3[PY_MAX_LEVEL + 1];
+ u32b oflags4[PY_MAX_LEVEL + 1];
+ u32b oflags5[PY_MAX_LEVEL + 1];
+ u32b oesp[PY_MAX_LEVEL + 1];
+ s16b opval[PY_MAX_LEVEL + 1];
+
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+
+ char skill_basem[MAX_SKILLS];
+ u32b skill_base[MAX_SKILLS];
+ char skill_modm[MAX_SKILLS];
+ s16b skill_mod[MAX_SKILLS];
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+
+/*
+ * Player class info
+ */
+
+typedef struct player_spec player_spec;
+
+struct player_spec
+{
+ s32b title; /* Type of class spec */
+ s32b desc; /* Small desc of the class spec */
+
+ char skill_basem[MAX_SKILLS]; /* Mod for value */
+ u32b skill_base[MAX_SKILLS]; /* value */
+ char skill_modm[MAX_SKILLS]; /* mod for mod */
+ s16b skill_mod[MAX_SKILLS]; /* mod */
+
+ u32b skill_ideal[MAX_SKILLS]; /* Ideal skill levels at level 50 */
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ u32b gods;
+
+ u32b flags1;
+ u32b flags2; /* flags */
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+typedef struct player_class player_class;
+
+struct player_class
+{
+ s32b title; /* Type of class */
+ s32b desc; /* Small desc of the class */
+ s32b titles[PY_MAX_LEVEL / 5];
+
+ s16b c_adj[6]; /* Class stat modifier */
+
+ s16b c_dis; /* class disarming */
+ s16b c_dev; /* class magic devices */
+ s16b c_sav; /* class saving throws */
+ s16b c_stl; /* class stealth */
+ s16b c_srh; /* class searching ability */
+ s16b c_fos; /* class searching frequency */
+ s16b c_thn; /* class to hit (normal) */
+ s16b c_thb; /* class to hit (bows) */
+
+ s16b x_dis; /* extra disarming */
+ s16b x_dev; /* extra magic devices */
+ s16b x_sav; /* extra saving throws */
+ s16b x_stl; /* extra stealth */
+ s16b x_srh; /* extra searching ability */
+ s16b x_fos; /* extra searching frequency */
+ s16b x_thn; /* extra to hit (normal) */
+ s16b x_thb; /* extra to hit (bows) */
+
+ s16b c_mhp; /* Class hit-dice adjustment */
+ s16b c_exp; /* Class experience factor */
+
+ s16b powers[4]; /* Powers of the class */
+
+ s16b spell_book; /* Tval of spell books (if any) */
+ s16b spell_stat; /* Stat for spells (if any) */
+ s16b spell_lev; /* The higher it is the higher the spells level are */
+ s16b spell_fail; /* The higher it is the higher the spells failure are */
+ s16b spell_mana; /* The higher it is the higher the spells mana are */
+ s16b spell_first; /* Level of first spell */
+ s16b spell_weight; /* Weight that hurts spells */
+ byte max_spell_level; /* Maximun spell level */
+ byte magic_max_spell; /* Maximun numbner of spells one can learn by natural means */
+
+ u32b flags1; /* flags */
+ u32b flags2; /* flags */
+
+ s16b mana;
+ s16b blow_num;
+ s16b blow_wgt;
+ s16b blow_mul;
+ s16b extra_blows;
+
+ s32b sense_base;
+ s32b sense_pl;
+ s32b sense_plus;
+ byte sense_heavy;
+ byte sense_heavy_magic;
+
+ s16b obj_tval[5];
+ s16b obj_sval[5];
+ s16b obj_pval[5];
+ s16b obj_dd[5];
+ s16b obj_ds[5];
+ s16b obj_num;
+
+ char body_parts[BODY_MAX]; /* To help to decide what to use when body changing */
+
+ u32b oflags1[PY_MAX_LEVEL + 1];
+ u32b oflags2[PY_MAX_LEVEL + 1];
+ u32b oflags3[PY_MAX_LEVEL + 1];
+ u32b oflags4[PY_MAX_LEVEL + 1];
+ u32b oflags5[PY_MAX_LEVEL + 1];
+ u32b oesp[PY_MAX_LEVEL + 1];
+ s16b opval[PY_MAX_LEVEL + 1];
+
+ char skill_basem[MAX_SKILLS];
+ u32b skill_base[MAX_SKILLS];
+ char skill_modm[MAX_SKILLS];
+ s16b skill_mod[MAX_SKILLS];
+
+ u32b gods;
+
+ player_spec spec[MAX_SPEC];
+
+ struct
+ {
+ s16b ability;
+ s16b level;
+ } abilities[10]; /* Abilitiers to be gained by level(doesnt take prereqs in account) */
+};
+
+typedef struct meta_class_type meta_class_type;
+struct meta_class_type
+{
+ char name[80]; /* Name */
+ byte color;
+ s16b *classes; /* list of classes */
+};
+
+/* Help type */
+typedef struct help_info help_info;
+struct help_info
+{
+ bool enabled; /* ingame help enabled */
+
+ u32b help1; /* help flags 1 */
+};
+
+
+/*
+ * Most of the "player" information goes here.
+ *
+ * This stucture gives us a large collection of player variables.
+ *
+ * This structure contains several "blocks" of information.
+ * (1) the "permanent" info
+ * (2) the "variable" info
+ * (3) the "transient" info
+ *
+ * All of the "permanent" info, and most of the "variable" info,
+ * is saved in the savefile. The "transient" info is recomputed
+ * whenever anything important changes.
+ */
+
+typedef struct player_type player_type;
+
+struct player_type
+{
+ s32b lives; /* How many times we resurected */
+
+ s16b oldpy; /* Previous player location -KMW- */
+ s16b oldpx; /* Previous player location -KMW- */
+
+ s16b py; /* Player location */
+ s16b px; /* Player location */
+
+ byte psex; /* Sex index */
+ byte prace; /* Race index */
+ byte pracem; /* Race Mod index */
+ byte pclass; /* Class index */
+ byte pspec; /* Class spec index */
+ byte mimic_form; /* Actualy transformation */
+ s16b mimic_level; /* Level of the mimic effect */
+ byte oops; /* Unused */
+
+ object_type inventory[INVEN_TOTAL]; /* Player inventory */
+
+ byte hitdie; /* Hit dice (sides) */
+ u16b expfact; /* Experience factor */
+
+ byte maximize; /* Maximize stats */
+ byte preserve; /* Preserve artifacts */
+ byte special; /* Special levels */
+ byte allow_one_death; /* Blood of life */
+
+ s16b age; /* Characters age */
+ s16b ht; /* Height */
+ s16b wt; /* Weight */
+ s16b sc; /* Social Class */
+
+
+ s32b au; /* Current Gold */
+
+ s32b max_exp; /* Max experience */
+ s32b exp; /* Cur experience */
+ u16b exp_frac; /* Cur exp frac (times 2^16) */
+
+ s16b lev; /* Level */
+
+ s16b town_num; /* Current town number */
+ s16b arena_number; /* monster number in arena -KMW- */
+ s16b inside_arena; /* Is character inside arena? */
+ s16b inside_quest; /* Inside quest level */
+ bool exit_bldg; /* Goal obtained in arena? -KMW- */
+
+ s32b wilderness_x; /* Coordinates in the wilderness */
+ s32b wilderness_y;
+ bool wild_mode; /* TRUE = Small map, FLASE = Big map */
+ bool old_wild_mode; /* TRUE = Small map, FLASE = Big map */
+
+ s16b mhp; /* Max hit pts */
+ s16b chp; /* Cur hit pts */
+ u16b chp_frac; /* Cur hit frac (times 2^16) */
+ s16b hp_mod; /* A modificator(permanent) */
+
+ s16b msp; /* Max mana pts */
+ s16b csp; /* Cur mana pts */
+ u16b csp_frac; /* Cur mana frac (times 2^16) */
+
+ s16b msane; /* Max sanity */
+ s16b csane; /* Cur sanity */
+ u16b csane_frac; /* Cur sanity frac */
+
+ s32b grace; /* Your God's appreciation factor. */
+ byte pgod; /* Your God. */
+ bool praying; /* Praying to your god. */
+ s16b melkor_sacrifice; /* How much hp has been sacrified for damage */
+
+ s16b max_plv; /* Max Player Level */
+
+ s16b stat_max[6]; /* Current "maximal" stat values */
+ s16b stat_cur[6]; /* Current "natural" stat values */
+
+ s16b luck_cur; /* Current "natural" luck value (range -30 <> 30) */
+ s16b luck_max; /* Current "maximal base" luck value (range -30 <> 30) */
+ s16b luck_base; /* Current "base" luck value (range -30 <> 30) */
+
+ s16b speed_factor; /* Timed -- Fast */
+ s16b fast; /* Timed -- Fast */
+ s16b lightspeed; /* Timed -- Light Speed */
+ s16b slow; /* Timed -- Slow */
+ s16b blind; /* Timed -- Blindness */
+ s16b paralyzed; /* Timed -- Paralysis */
+ s16b confused; /* Timed -- Confusion */
+ s16b afraid; /* Timed -- Fear */
+ s16b image; /* Timed -- Hallucination */
+ s16b poisoned; /* Timed -- Poisoned */
+ s16b cut; /* Timed -- Cut */
+ s16b stun; /* Timed -- Stun */
+
+ s16b protevil; /* Timed -- Protection from Evil*/
+ s16b protgood; /* Timed -- Protection from Good*/
+ s16b protundead; /* Timed -- Protection from Undead*/
+ s16b invuln; /* Timed -- Invulnerable */
+ s16b hero; /* Timed -- Heroism */
+ s16b shero; /* Timed -- Super Heroism */
+ s16b shield; /* Timed -- Shield Spell */
+ s16b shield_power; /* Timed -- Shield Spell Power */
+ s16b shield_opt; /* Timed -- Shield Spell options */
+ s16b shield_power_opt; /* Timed -- Shield Spell Power */
+ s16b shield_power_opt2; /* Timed -- Shield Spell Power */
+ s16b blessed; /* Timed -- Blessed */
+ s16b tim_invis; /* Timed -- See Invisible */
+ s16b tim_infra; /* Timed -- Infra Vision */
+
+ s16b oppose_acid; /* Timed -- oppose acid */
+ s16b oppose_elec; /* Timed -- oppose lightning */
+ s16b oppose_fire; /* Timed -- oppose heat */
+ s16b oppose_cold; /* Timed -- oppose cold */
+ s16b oppose_pois; /* Timed -- oppose poison */
+ s16b oppose_ld; /* Timed -- oppose light & dark */
+ s16b oppose_cc; /* Timed -- oppose chaos & confusion */
+ s16b oppose_ss; /* Timed -- oppose sound & shards */
+ s16b oppose_nex; /* Timed -- oppose nexus */
+
+ s16b rush; /* Rush and Bush */
+
+ s16b tim_esp; /* Timed ESP */
+ s16b tim_wraith; /* Timed wraithform */
+ s16b tim_ffall; /* Timed Levitation */
+ s16b tim_fly; /* Timed Levitation */
+ s16b tim_fire_aura; /* Timed Fire Aura */
+ s16b tim_poison; /* Timed poison hands */
+ s16b tim_thunder; /* Timed thunderstorm */
+ s16b tim_thunder_p1; /* Timed thunderstorm */
+ s16b tim_thunder_p2; /* Timed thunderstorm */
+
+ s16b tim_project; /* Timed project upon melee blow */
+ s16b tim_project_dam;
+ s16b tim_project_gf;
+ s16b tim_project_rad;
+ s16b tim_project_flag;
+
+ s16b tim_roots; /* Timed roots */
+ s16b tim_roots_ac;
+ s16b tim_roots_dam;
+
+ s16b resist_magic; /* Timed Resist Magic (later) */
+ s16b tim_invisible; /* Timed Invisibility */
+ s16b tim_inv_pow; /* Power of timed invisibility */
+ s16b tim_mimic; /* Timed Mimic */
+ s16b tim_lite; /* Timed Lite */
+ s16b tim_regen; /* Timed extra regen */
+ s16b tim_regen_pow; /* Timed extra regen power */
+ s16b holy; /* Holy Aura */
+ s16b walk_water; /* Walk over water as a god */
+ s16b tim_mental_barrier; /* Sustain Int&Wis */
+ s16b strike; /* True Strike(+25 hit) */
+ s16b meditation; /* Meditation(+50 mana -25 to hit/to dam) */
+ s16b tim_reflect; /* Timed Reflection */
+ s16b tim_res_time; /* Timed Resistance to Time */
+ s16b tim_deadly; /* Timed deadly blow */
+ s16b prob_travel; /* Timed probability travel */
+ s16b disrupt_shield;/* Timed disruption shield */
+ s16b parasite; /* Timed parasite */
+ s16b parasite_r_idx;/* Timed parasite monster */
+ s32b loan; /* Amount of loan */
+ s32b loan_time; /* Timer -- time to payback loan */
+ s16b absorb_soul; /* Timed soul absordtion */
+ s16b tim_magic_breath; /* Magical breathing -- can breath anywhere */
+ s16b tim_water_breath; /* Water breathing -- can breath underwater */
+
+ s16b immov_cntr; /* Timed -- Last ``immovable'' command. */
+
+ s16b chaos_patron;
+
+ s16b recall_dungeon; /* Recall in which dungeon */
+ s16b word_recall; /* Word of recall counter */
+
+ s32b energy; /* Current energy */
+
+ s16b food; /* Current nutrition */
+
+ byte confusing; /* Glowing hands */
+ byte searching; /* Currently searching */
+
+ s16b new_spells; /* Number of spells available */
+
+ s16b old_spells;
+
+ s16b xtra_spells; /* Number of xtra spell learned(via potion) */
+
+ bool old_cumber_armor;
+ bool old_cumber_glove;
+ bool old_heavy_wield;
+ bool old_heavy_shoot;
+ bool old_icky_wield;
+
+ s16b old_lite; /* Old radius of lite (if any) */
+ s16b old_view; /* Old radius of view (if any) */
+
+ s16b old_food_aux; /* Old value of food */
+
+
+ bool cumber_armor; /* Mana draining armor */
+ bool cumber_glove; /* Mana draining gloves */
+ bool heavy_wield; /* Heavy weapon */
+ bool heavy_shoot; /* Heavy shooter */
+ bool icky_wield; /* Icky weapon */
+ bool immovable; /* Immovable character */
+
+ s16b cur_lite; /* Radius of lite (if any) */
+
+
+ u32b notice; /* Special Updates (bit flags) */
+ u32b update; /* Pending Updates (bit flags) */
+ u32b redraw; /* Normal Redraws (bit flags) */
+ u32b window; /* Window Redraws (bit flags) */
+
+ s16b stat_use[6]; /* Current modified stats */
+ s16b stat_top[6]; /* Maximal modified stats */
+
+ s16b stat_add[6]; /* Modifiers to stat values */
+ s16b stat_ind[6]; /* Indexes into stat tables */
+ s16b stat_cnt[6]; /* Counter for temporary drains */
+ s16b stat_los[6]; /* Amount of temporary drains */
+
+ bool immune_acid; /* Immunity to acid */
+ bool immune_elec; /* Immunity to lightning */
+ bool immune_fire; /* Immunity to fire */
+ bool immune_cold; /* Immunity to cold */
+ bool immune_neth; /* Immunity to nether */
+
+ bool resist_acid; /* Resist acid */
+ bool resist_elec; /* Resist lightning */
+ bool resist_fire; /* Resist fire */
+ bool resist_cold; /* Resist cold */
+ bool resist_pois; /* Resist poison */
+
+ bool resist_conf; /* Resist confusion */
+ bool resist_sound; /* Resist sound */
+ bool resist_lite; /* Resist light */
+ bool resist_dark; /* Resist darkness */
+ bool resist_chaos; /* Resist chaos */
+ bool resist_disen; /* Resist disenchant */
+ bool resist_shard; /* Resist shards */
+ bool resist_nexus; /* Resist nexus */
+ bool resist_blind; /* Resist blindness */
+ bool resist_neth; /* Resist nether */
+ bool resist_fear; /* Resist fear */
+ bool resist_continuum; /* Resist space-time continuum disruption */
+
+ bool sensible_fire; /* Fire does more damage on the player */
+ bool sensible_lite; /* Lite does more damage on the player and blinds her/him */
+
+ bool reflect; /* Reflect 'bolt' attacks */
+ bool sh_fire; /* Fiery 'immolation' effect */
+ bool sh_elec; /* Electric 'immolation' effect */
+ bool wraith_form; /* wraithform */
+
+ bool anti_magic; /* Anti-magic */
+ bool anti_tele; /* Prevent teleportation */
+
+ bool sustain_str; /* Keep strength */
+ bool sustain_int; /* Keep intelligence */
+ bool sustain_wis; /* Keep wisdom */
+ bool sustain_dex; /* Keep dexterity */
+ bool sustain_con; /* Keep constitution */
+ bool sustain_chr; /* Keep charisma */
+
+ bool aggravate; /* Aggravate monsters */
+ bool teleport; /* Random teleporting */
+
+ bool exp_drain; /* Experience draining */
+ byte drain_mana; /* mana draining */
+ byte drain_life; /* hp draining */
+
+ bool magical_breath; /* Magical breathing -- can breath anywhere */
+ bool water_breath; /* Water breathing -- can breath underwater */
+ bool climb; /* Can climb mountains */
+ bool fly; /* Can fly over some features */
+ bool ffall; /* No damage falling */
+ bool lite; /* Permanent light */
+ bool free_act; /* Never paralyzed */
+ bool see_inv; /* Can see invisible */
+ bool regenerate; /* Regenerate hit pts */
+ bool hold_life; /* Resist life draining */
+ u32b telepathy; /* Telepathy */
+ bool slow_digest; /* Slower digestion */
+ bool bless_blade; /* Blessed blade */
+ byte xtra_might; /* Extra might bow */
+ bool impact; /* Earthquake blows */
+ bool auto_id; /* Auto id items */
+
+ s16b invis; /* Invisibility */
+
+ s16b dis_to_h; /* Known bonus to hit */
+ s16b dis_to_d; /* Known bonus to dam */
+ s16b dis_to_a; /* Known bonus to ac */
+
+ s16b dis_ac; /* Known base ac */
+
+ s16b to_l; /* Bonus to life */
+ s16b to_m; /* Bonus to mana */
+ s16b to_s; /* Bonus to spell */
+ s16b to_h; /* Bonus to hit */
+ s16b to_d; /* Bonus to dam */
+ s16b to_h_melee; /* Bonus to hit for melee */
+ s16b to_d_melee; /* Bonus to dam for melee */
+ s16b to_h_ranged; /* Bonus to hit for ranged */
+ s16b to_d_ranged; /* Bonus to dam for ranged */
+ s16b to_a; /* Bonus to ac */
+
+ s16b ac; /* Base ac */
+
+ byte antimagic; /* Power of the anti magic field */
+ byte antimagic_dis; /* Radius of the anti magic field */
+
+ s16b see_infra; /* Infravision range */
+
+ s16b skill_dis; /* Skill: Disarming */
+ s16b skill_dev; /* Skill: Magic Devices */
+ s16b skill_sav; /* Skill: Saving throw */
+ s16b skill_stl; /* Skill: Stealth factor */
+ s16b skill_srh; /* Skill: Searching ability */
+ s16b skill_fos; /* Skill: Searching frequency */
+ s16b skill_thn; /* Skill: To hit (normal) */
+ s16b skill_thb; /* Skill: To hit (shooting) */
+ s16b skill_tht; /* Skill: To hit (throwing) */
+ s16b skill_dig; /* Skill: Digging */
+
+ s16b num_blow; /* Number of blows */
+ s16b num_fire; /* Number of shots */
+ s16b xtra_crit; /* % of increased crits */
+
+ byte throw_mult; /* Multiplier for throw damage */
+
+ byte tval_xtra; /* Correct xtra tval */
+
+ byte tval_ammo; /* Correct ammo tval */
+
+ s16b pspeed; /* Current speed */
+
+ u32b mimic_extra; /* Mimicry powers use that */
+ u32b antimagic_extra; /* Antimagic powers */
+ u32b druid_extra; /* Druid powers */
+ u32b druid_extra2; /* Druid powers */
+ u32b druid_extra3; /* Druid powers */
+ u32b music_extra; /* Music songs */
+ u32b music_extra2; /* Music songs */
+ u32b necro_extra; /* Necro powers */
+ u32b necro_extra2; /* Necro powers */
+
+ u32b race_extra1; /* Variable for race */
+ u32b race_extra2; /* Variable for race */
+ u32b race_extra3; /* Variable for race */
+ u32b race_extra4; /* Variable for race */
+ u32b race_extra5; /* Variable for race */
+ u32b race_extra6; /* Variable for race */
+ u32b race_extra7; /* Variable for race */
+
+ s16b dodge_chance; /* Dodging chance */
+
+ u32b maintain_sum; /* Do we have partial summons */
+
+ byte spellbinder_num; /* Number of spells bound */
+ u32b spellbinder[4]; /* Spell bounds */
+ byte spellbinder_trigger; /* Spellbinder trigger condition */
+
+ cptr mimic_name;
+
+ char tactic; /* from 128-4 extremely coward to */
+ /* 128+4 berserker */
+ char movement; /* base movement way */
+
+ s16b companion_killed; /* Number of companion death */
+
+ bool no_mortal; /* Fated to never die by the hand of a mortal being */
+
+ bool black_breath; /* The Tolkien's Black Breath */
+
+ bool precognition; /* Like the cheat mode */
+
+ /*** Extra flags -- used for lua and easying stuff ***/
+ u32b xtra_f1;
+ u32b xtra_f2;
+ u32b xtra_f3;
+ u32b xtra_f4;
+ u32b xtra_f5;
+ u32b xtra_esp;
+
+ /* Corruptions */
+ bool *corruptions;
+
+ /*** Pet commands ***/
+ byte pet_follow_distance; /* Length of the imaginary "leash" for pets */
+ byte pet_open_doors; /* flag - allow pets to open doors */
+ byte pet_pickup_items; /* flag - allow pets to pickup items */
+
+ s16b control; /* Controlled monster */
+ byte control_dir; /* Controlled monster */
+
+ /*** Body changing variables ***/
+ u16b body_monster; /* In which body is the player */
+ bool disembodied; /* Is the player in a body ? */
+ byte body_parts[INVEN_TOTAL - INVEN_WIELD]; /* Which body parts does he have ? */
+
+ s16b extra_body_parts[BODY_MAX]; /* Various body modifiers */
+
+ /* Astral */
+ bool astral; /* We started at the bottom ? */
+
+ /* Powers */
+ bool *powers; /* Actual powers */
+ bool powers_mod[POWER_MAX_INIT]; /* Intrinsinc powers */
+
+ /* Skills */
+ s16b skill_points;
+ s16b skill_last_level; /* Prevents gaining skills by losing level and regaining them */
+ s16b melee_style; /* How are */
+ s16b use_piercing_shots; /* for archery */
+
+ /* Help */
+ help_info help;
+
+ /*** Temporary fields ***/
+
+ bool did_nothing; /* True if the last action wasnt a real action */
+ bool leaving; /* True if player is leaving */
+};
+
+
+/* For Monk martial arts */
+
+typedef struct martial_arts martial_arts;
+
+struct martial_arts
+{
+ cptr desc; /* A verbose attack description */
+ int min_level; /* Minimum level to use */
+ int chance; /* Chance of 'success' */
+ int dd; /* Damage dice */
+ int ds; /* Damage sides */
+ s16b effect; /* Special effects */
+ s16b power; /* Special effects power */
+};
+
+
+
+/* Powers - used by Mindcrafters and Necromancers */
+typedef struct magic_power magic_power;
+
+struct magic_power
+{
+ int min_lev;
+ int mana_cost;
+ int fail;
+ cptr name;
+ cptr desc;
+};
+
+/* Border */
+typedef struct border_type border_type;
+
+struct border_type
+{
+ byte north[MAX_WID];
+ byte south[MAX_WID];
+ byte east[MAX_HGT];
+ byte west[MAX_HGT];
+ byte north_west;
+ byte north_east;
+ byte south_west;
+ byte south_east;
+};
+
+
+/*
+ * A structure describing a wilderness area
+ * with a terrain, a town or a dungeon entrance
+ */
+typedef struct wilderness_type_info wilderness_type_info;
+
+struct wilderness_type_info
+{
+ u32b name; /* Name (offset) */
+ u32b text; /* Text (offset) */
+ u16b entrance; /* Which town is there(<1000 i's a town, >=1000 it a dungeon) */
+ s32b wild_x; /* Map coordinates (backed out while parsing map) */
+ s32b wild_y;
+ byte road; /* Flags of road */
+ int level; /* Difficulty level */
+ u32b flags1; /* Some flags */
+ byte feat; /* The feature of f_info.txt that is used to allow passing, ... and to get a char/color/graph */
+ byte terrain_idx; /* Terrain index(defined in defines.h) */
+
+ byte terrain[MAX_WILD_TERRAIN];/* Feature types for the plasma generator */
+};
+
+/*
+ * A structure describing a wilderness map
+ */
+typedef struct wilderness_map wilderness_map;
+
+struct wilderness_map
+{
+ int feat; /* Wilderness feature */
+ u32b seed; /* Seed for the RNG */
+ u16b entrance; /* Entrance for dungeons */
+
+ bool known; /* Is it seen by the player ? */
+};
+
+/*
+ * A structure describing a town with
+ * stores and buildings
+ */
+typedef struct town_type town_type;
+struct town_type
+{
+ cptr name;
+ u32b seed; /* Seed for RNG */
+ store_type *store; /* The stores [max_st_idx] */
+ byte numstores;
+
+ byte flags; /* Town flags */
+ /* Left this for the sake of compatibility */
+ bool stocked; /* Is the town actualy stocked ? */
+
+ bool destroyed; /* Is the town destroyed? */
+};
+
+
+/* Alchemists */
+
+typedef struct tval_desc2
+{
+ int tval;
+ cptr desc;
+} tval_desc2;
+
+typedef struct alchemist_recipe alchemist_recipe;
+struct alchemist_recipe
+{
+ int sval_essence;
+ byte tval;
+ byte sval;
+ byte qty;
+};
+
+typedef struct artifact_select_flag artifact_select_flag;
+struct artifact_select_flag {
+ byte group; /* Flag group to display it in */
+ int flag; /* item flag to set */
+ byte level; /* Player skill level to start at */
+ int desc; /* Display this description to select flag */
+ u32b xp; /* xp cost for this flag */
+ bool pval; /* indicates this flag benifits from pval */
+ int item_desc; /* Description of required item */
+ int item_descp; /* Description of required item */
+ byte rtval; /* Required items' tval */
+ byte rsval; /* Required items' sval */
+ int rpval; /* Required items' pval (zero for no req) */
+ int rflag[6]; /* Monster Race flags for required Corpses */
+};
+
+/*
+ A structure for deity information.
+ */
+typedef struct deity_type deity_type;
+struct deity_type
+{
+ cptr name;
+ char desc[10][80];
+};
+
+/* A structure for tactics */
+typedef struct tactic_info_type tactic_info_type;
+
+struct tactic_info_type
+{
+ s16b to_hit;
+ s16b to_dam;
+ s16b to_ac;
+ s16b to_stealth;
+ s16b to_disarm;
+ s16b to_saving;
+ cptr name;
+};
+
+/* A structure to describe a random artifact. */
+typedef struct random_artifact random_artifact;
+
+struct random_artifact
+{
+ char name_full[80]; /* Full name for the artifact */
+ char name_short[80]; /* Un-Id'd name */
+ byte level; /* Level of the artifact */
+ byte attr; /* Color that is used on the screen */
+ u32b cost; /* Object's value */
+ byte activation; /* Activation. */
+ s16b timeout; /* Timeout. */
+ byte generated; /* Does it exist already? */
+};
+
+/* A structure to describe an activation. */
+typedef struct activation activation;
+
+struct activation
+{
+ char desc[80]; /* Desc of the activation */
+ u32b cost; /* costs value */
+ s16b spell; /* Spell. */
+};
+
+/* A structure to describe a music. */
+typedef struct music music;
+
+struct music
+{
+ char desc[80]; /* Desc of the music */
+ s16b music; /* Music. */
+ s16b dur; /* Duration(if any) */
+ s16b init_recharge; /* Minimal recharge time */
+ s16b turn_recharge; /* Recharge time for each more turn */
+ byte min_inst; /* Minimum instrument for the music */
+ byte rarity; /* Rarity of the music(use 100 to unallow to be randomly generated) */
+};
+
+/* A structure to describe the random spells of the Power Mages */
+typedef struct random_spell random_spell;
+
+struct random_spell
+{
+ char desc[30]; /* Desc of the spell */
+ char name[30]; /* Name of the spell */
+ s16b mana; /* Mana cost */
+ s16b fail; /* Failure rate */
+ u32b proj_flags; /* Project function flags */
+ byte GF; /* Type of the projection */
+ byte radius;
+ byte dam_sides;
+ byte dam_dice;
+ byte level; /* Level needed */
+ bool untried; /* Is the spell was tried? */
+};
+
+/* A structure to describe the fate of the player */
+typedef struct fate fate;
+
+struct fate
+{
+ byte fate; /* Which fate */
+ byte level; /* On which level */
+ byte serious; /* Is it sure? */
+ s16b o_idx; /* Object to find */
+ s16b e_idx; /* Ego-Item to find */
+ s16b a_idx; /* Artifact to find */
+ s16b v_idx; /* Vault to find */
+ s16b r_idx; /* Monster to find */
+ s16b count; /* Number of things */
+ s16b time; /* Turn before */
+ bool know; /* Has it been predicted? */
+ bool icky; /* Hackish runtime-only flag */
+};
+
+/* A structure for movements */
+typedef struct move_info_type move_info_type;
+
+struct move_info_type
+{
+ s16b to_speed;
+ s16b to_search;
+ s16b to_stealth;
+ s16b to_percep;
+ cptr name;
+};
+
+/* Define monster generation rules */
+typedef struct rule_type rule_type;
+struct rule_type
+{
+ byte mode; /* Mode of combination of the monster flags */
+ byte percent; /* Percent of monsters affected by the rule */
+
+ u32b mflags1; /* The monster flags that are allowed */
+ u32b mflags2;
+ u32b mflags3;
+ u32b mflags4;
+ u32b mflags5;
+ u32b mflags6;
+ u32b mflags7;
+ u32b mflags8;
+ u32b mflags9;
+
+ char r_char[5]; /* Monster race allowed */
+};
+
+/* A structure for the != dungeon types */
+typedef struct dungeon_info_type dungeon_info_type;
+struct dungeon_info_type
+{
+ u32b name; /* Name */
+ u32b text; /* Description */
+ char short_name[3]; /* Short name */
+
+ char generator[30]; /* Name of the level generator */
+
+ s16b floor1; /* Floor tile 1 */
+ byte floor_percent1[2]; /* Chance of type 1 */
+ s16b floor2; /* Floor tile 2 */
+ byte floor_percent2[2]; /* Chance of type 2 */
+ s16b floor3; /* Floor tile 3 */
+ byte floor_percent3[2]; /* Chance of type 3 */
+ s16b outer_wall; /* Outer wall tile */
+ s16b inner_wall; /* Inner wall tile */
+ s16b fill_type1; /* Cave tile 1 */
+ byte fill_percent1[2]; /* Chance of type 1 */
+ s16b fill_type2; /* Cave tile 2 */
+ byte fill_percent2[2]; /* Chance of type 2 */
+ s16b fill_type3; /* Cave tile 3 */
+ byte fill_percent3[2]; /* Chance of type 3 */
+ byte fill_method; /* Smoothing parameter for the above */
+
+ s16b mindepth; /* Minimal depth */
+ s16b maxdepth; /* Maximal depth */
+
+ bool principal; /* If it's a part of the main dungeon */
+ byte next; /* The next part of the main dungeon */
+ byte min_plev; /* Minimal plev needed to enter -- it's an anti-cheating mesure */
+
+ int min_m_alloc_level; /* Minimal number of monsters per level */
+ int max_m_alloc_chance; /* There is a 1/max_m_alloc_chance chance per round of creating a new monster */
+
+ u32b flags1; /* Flags 1 */
+ u32b flags2; /* Flags 1 */
+
+ int size_x, size_y; /* Desired numers of panels */
+
+ byte rule_percents[100]; /* Flat rule percents */
+ rule_type rules[5]; /* Monster generation rules */
+
+ int final_object; /* The object you'll find at the bottom */
+ int final_artifact; /* The artifact you'll find at the bottom */
+ int final_guardian; /* The artifact's guardian. If an artifact is specified, then it's NEEDED */
+
+ int ix, iy, ox, oy; /* Wilderness coordinates of the entrance/output of the dungeon */
+
+ obj_theme objs; /* The drops type */
+
+ int d_dice[4]; /* Number of dices */
+ int d_side[4]; /* Number of sides */
+ int d_frequency[4]; /* Frequency of damage (1 is the minimum) */
+ int d_type[4]; /* Type of damage */
+
+ s16b t_idx[TOWN_DUNGEON]; /* The towns */
+ s16b t_level[TOWN_DUNGEON]; /* The towns levels */
+ s16b t_num; /* Number of towns */
+};
+
+/* A structure for inscriptions */
+typedef struct inscription_info_type inscription_info_type;
+struct inscription_info_type
+{
+ char text[40]; /* The inscription itself */
+ byte when; /* When it is executed */
+ bool know; /* Is the inscription know ? */
+ byte mana; /* Grid mana needed */
+};
+
+/* To hold Runecrafters prefered spells */
+typedef struct rune_spell rune_spell;
+struct rune_spell
+{
+ char name[30]; /* name */
+
+ s16b type; /* Type of the spell(GF) */
+ s16b rune2; /* Modifiers */
+ s16b mana; /* Mana involved */
+};
+
+/* For level gaining artifacts, artifact creation, ... */
+typedef struct flags_group flags_group;
+struct flags_group
+{
+ char name[30]; /* Name */
+ byte color; /* Color */
+
+ byte price; /* Price to "buy" it */
+
+ u32b flags1; /* Flags set 1 */
+ u32b flags2; /* Flags set 2 */
+ u32b flags3; /* Flags set 3 */
+ u32b flags4; /* Flags set 4 */
+ u32b esp; /* ESP flags set */
+};
+
+/* For powers(racial, class, mutation, artifacts, ... */
+typedef struct power_type power_type;
+struct power_type
+{
+ char *name; /* Name */
+ char *desc_text; /* Text describing power */
+ char *gain_text; /* Text displayed on gaining the power */
+ char *lose_text; /* Text displayed on losing the power */
+
+ byte level; /* Min level */
+ byte cost; /* Mana/Life cost */
+ byte stat; /* Stat used */
+ byte diff; /* Difficulty */
+};
+
+/* Hooks */
+typedef bool (*hook_type)(char *fmt);
+
+/*
+ * Structure for the "quests"
+ */
+typedef struct quest_type quest_type;
+
+struct quest_type
+{
+ bool silent;
+
+ bool dynamic_desc; /* Do we need to ask a function to get the description ? */
+
+ char name[40]; /* Quest name */
+
+ char desc[10][80]; /* Quest desc */
+
+ s16b status; /* Is the quest taken, completed, finished? */
+
+ s16b level; /* Dungeon level */
+
+ s16b *plot; /* Which plot does it belongs to? */
+
+ byte type; /* Lua or C ? */
+
+ bool (*init)(int q); /* Function that takes care of generating hardcoded quests */
+
+ s32b data[4]; /* Various datas used by the quests */
+};
+typedef struct random_quest random_quest;
+struct random_quest
+{
+ byte type; /* Type/number of monsters to kill(0 = no quest) */
+ s16b r_idx; /* Monsters to crush */
+ bool done; /* Done ? */
+};
+
+/* Monster powers for player uses */
+typedef struct monster_power monster_power;
+struct monster_power
+{
+ u32b power; /* Power RF?_xxx */
+ cptr name; /* Name of it */
+ int mana; /* Mana needed */
+ bool great; /* Need the use of great spells */
+};
+
+/* Tval descs */
+typedef struct tval_desc tval_desc;
+struct tval_desc
+{
+ int tval; /* tval */
+ cptr desc; /* desc */
+};
+
+/*
+ * Between exit
+ */
+typedef struct between_exit between_exit;
+struct between_exit
+{
+ s16b corresp; /* Corresponding between gate */
+ bool dungeon; /* Do we exit in a dungeon or in the wild ? */
+
+ s16b wild_x, wild_y; /* Wilderness spot to land onto */
+ s16b px, py; /* Location of the map */
+
+ s16b d_idx; /* Dungeon to land onto */
+ s16b level;
+};
+
+/*
+ * A structure to hold "rolled" information
+ */
+typedef struct birther birther;
+struct birther
+{
+ s16b sex;
+ s16b race;
+ s16b rmod;
+ s16b pclass;
+ s16b spec;
+
+ byte quests;
+
+ byte god;
+ s32b grace;
+ s32b god_favor;
+
+ s16b age;
+ s16b wt;
+ s16b ht;
+ s16b sc;
+
+ s32b au;
+
+ s16b stat[6];
+ s16b luck;
+
+ s16b chaos_patron;
+
+ u32b weapon;
+
+ char history[4][60];
+
+ bool quick_ok;
+};
+
+typedef struct hooks_chain hooks_chain;
+struct hooks_chain
+{
+ hook_type hook;
+ char name[40];
+ char script[40];
+ byte type;
+ hooks_chain *next;
+};
+
+typedef union hook_return hook_return;
+union hook_return
+{
+ s32b num;
+ cptr str;
+ object_type *o_ptr;
+ monster_type *m_ptr;
+};
+
+/*
+ * Forward declare
+ */
+typedef struct hist_type hist_type;
+
+/*
+ * Player background information
+ */
+struct hist_type
+{
+ s32b info; /* Textual History -- uses rp_text */
+
+ byte roll; /* Frequency of this entry */
+ s16b chart; /* Chart index */
+ s16b next; /* Next chart index */
+ byte bonus; /* Social Class Bonus + 50 */
+};
+
+/*
+ * Item sets
+ */
+typedef struct set_type set_type;
+struct set_type
+{
+ u32b name; /* Name */
+ u32b desc; /* Desc */
+
+ byte num; /* Number of artifacts used */
+ byte num_use; /* Number actually wore */
+ struct /* the various items */
+ {
+ bool present; /* Is it actually wore ? */
+ s16b a_idx; /* What artifact ? */
+ s16b pval[6]; /* Pval for each combination */
+ u32b flags1[6]; /* Flags */
+ u32b flags2[6]; /* Flags */
+ u32b flags3[6]; /* Flags */
+ u32b flags4[6]; /* Flags */
+ u32b flags5[6]; /* Flags */
+ u32b esp[6]; /* Flags */
+ } arts[6];
+};
+
+/* A structure for CLI commands. */
+typedef struct cli_comm cli_comm;
+struct cli_comm
+{
+ cptr comm; /* Extended name of the command. */
+ cptr descrip; /* Description of the command. */
+ s16b key; /* Key to convert command to. */
+};
+
+/*
+ * Skills !
+ */
+typedef struct skill_type skill_type;
+struct skill_type
+{
+ u32b name; /* Name */
+ u32b desc; /* Description */
+ u32b action_desc; /* Action Description */
+
+ s16b action_mkey; /* Action do to */
+
+ s32b i_value; /* Actual value */
+ s32b i_mod; /* Modifier(1 skill point = modifier skill) */
+
+ s32b value; /* Actual value */
+ s32b mod; /* Modifier(1 skill point = modifier skill) */
+ s16b rate; /* Modifier decreasing rate */
+
+ u32b uses; /* Number of times used */
+
+ s16b action[MAX_SKILLS]; /* List of actions against other skills */
+
+ s16b father; /* Father in the skill tree */
+ bool dev; /* Is the branch developped ? */
+ s16b order; /* Order in the tree */
+ bool hidden; /* Innactive */
+
+ byte random_gain_chance; /* random gain chance, still needs the flag */
+
+ u32b flags1; /* Skill flags */
+};
+
+
+/*
+ * The spell function must provide the desc
+ */
+typedef struct spell_type spell_type;
+struct spell_type
+{
+ cptr name; /* Name */
+ byte skill_level; /* Required level (to learn) */
+ byte mana; /* Required mana at lvl 1 */
+ byte mana_max; /* Required mana at max lvl */
+ s16b fail; /* Minimum chance of failure */
+ s16b level; /* Spell level(0 = not learnt) */
+};
+
+typedef struct school_type school_type;
+struct school_type
+{
+ cptr name; /* Name */
+ s16b skill; /* Skill used for that school */
+};
+
+/*
+ * Desc for GF_FOO
+ */
+typedef struct gf_name_type gf_name_type;
+struct gf_name_type
+{
+ int gf;
+ cptr name;
+};
+
+/*
+ * Timers
+ */
+typedef struct timer_type timer_type;
+struct timer_type
+{
+ timer_type *next; /* The next timer in the list */
+
+ bool enabled; /* Is it currently counting? */
+
+ s32b delay; /* Delay between activations */
+ s32b countdown; /* The current number of turns passed, when it reaches delay it fires */
+
+ cptr callback; /* The lua function to call upon firing(no C callback yet .. maybe) */
+};
+
+/*
+ * This is for lua functions that need to pass table to c functions
+ */
+typedef struct list_type list_type;
+struct list_type
+{
+ cptr *list;
+};
+
+/*
+ * Abilities
+ */
+typedef struct ability_type ability_type;
+struct ability_type
+{
+ u32b name; /* Name */
+ u32b desc; /* Description */
+ u32b action_desc; /* Action Description */
+
+ s16b action_mkey; /* Action do to */
+
+ s16b cost; /* Skill points cost */
+
+ bool acquired; /* Do the player actualylg ot it ? */
+
+ /* Prereqs */
+ s16b skills[10]; /* List of prereq skills(10 max) */
+ s16b skill_levels[10]; /* List of prereq skills(10 max) */
+ s16b stat[6]; /* List of prereq stats */
+ s16b need_abilities[10]; /* List of prereq abilities(10 max) */
+ s16b forbid_abilities[10]; /* List of forbidden abilities(10 max) */
+};
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 00000000..d3152d8f
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,4873 @@
+/* File: util.c */
+
+/* Purpose: Angband utilities -BEN- */
+
+
+#include "angband.h"
+
+
+
+
+#ifndef HAS_MEMSET
+
+/*
+* For those systems that don't have "memset()"
+*
+* Set the value of each of 'n' bytes starting at 's' to 'c', return 's'
+* If 'n' is negative, you will erase a whole lot of memory.
+*/
+char *memset(char *s, int c, huge n)
+{
+ char *t;
+ for (t = s; len--; ) *t++ = c;
+ return (s);
+}
+
+#endif
+
+
+
+#ifndef HAS_STRICMP
+
+/*
+* For those systems that don't have "stricmp()"
+*
+* Compare the two strings "a" and "b" ala "strcmp()" ignoring case.
+*/
+int stricmp(cptr a, cptr b)
+{
+ cptr s1, s2;
+ char z1, z2;
+
+ /* Scan the strings */
+ for (s1 = a, s2 = b; TRUE; s1++, s2++)
+ {
+ z1 = FORCEUPPER(*s1);
+ z2 = FORCEUPPER(*s2);
+ if (z1 < z2) return ( -1);
+ if (z1 > z2) return (1);
+ if (!z1) return (0);
+ }
+}
+
+#endif
+
+
+#ifdef SET_UID
+
+# ifndef HAS_USLEEP
+
+/*
+* For those systems that don't have "usleep()" but need it.
+*
+* Fake "usleep()" function grabbed from the inl netrek server -cba
+*/
+int usleep(huge usecs)
+{
+ struct timeval Timer;
+
+ int nfds = 0;
+
+#ifdef FD_SET
+ fd_set *no_fds = NULL;
+#else
+int *no_fds = NULL;
+#endif
+
+
+ /* Was: int readfds, writefds, exceptfds; */
+ /* Was: readfds = writefds = exceptfds = 0; */
+
+
+ /* Paranoia -- No excessive sleeping */
+ if (usecs > 4000000L) core("Illegal usleep() call");
+
+
+ /* Wait for it */
+ Timer.tv_sec = (usecs / 1000000L);
+ Timer.tv_usec = (usecs % 1000000L);
+
+ /* Wait for it */
+ if (select(nfds, no_fds, no_fds, no_fds, &Timer) < 0)
+ {
+ /* Hack -- ignore interrupts */
+ if (errno != EINTR) return -1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+# endif
+
+
+/*
+* Hack -- External functions
+*/
+extern struct passwd *getpwuid();
+extern struct passwd *getpwnam();
+
+
+/*
+* Find a default user name from the system.
+*/
+void user_name(char *buf, int id)
+{
+#ifdef SET_UID
+ struct passwd *pw;
+
+ /* Look up the user name */
+ if ((pw = getpwuid(id)))
+ {
+ (void)strcpy(buf, pw->pw_name);
+ buf[16] = '\0';
+
+#ifdef CAPITALIZE_USER_NAME
+ /* Hack -- capitalize the user name */
+ if (islower(buf[0])) buf[0] = toupper(buf[0]);
+#endif /* CAPITALIZE_USER_NAME */
+
+ return;
+ }
+#endif /* SET_UID */
+
+ /* Oops. Hack -- default to "PLAYER" */
+ strcpy(buf, "PLAYER");
+}
+
+#endif /* SET_UID */
+
+
+
+
+/*
+* The concept of the "file" routines below (and elsewhere) is that all
+* file handling should be done using as few routines as possible, since
+* every machine is slightly different, but these routines always have the
+* same semantics.
+*
+* In fact, perhaps we should use the "path_parse()" routine below to convert
+* from "canonical" filenames (optional leading tilde's, internal wildcards,
+* slash as the path seperator, etc) to "system" filenames (no special symbols,
+* system-specific path seperator, etc). This would allow the program itself
+* to assume that all filenames are "Unix" filenames, and explicitly "extract"
+* such filenames if needed (by "path_parse()", or perhaps "path_canon()").
+*
+* Note that "path_temp" should probably return a "canonical" filename.
+*
+* Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
+* and "my_move()" and "my_copy()" should all take "canonical" filenames.
+*
+* Note that "canonical" filenames use a leading "slash" to indicate an absolute
+* path, and a leading "tilde" to indicate a special directory, and default to a
+* relative path, but MSDOS uses a leading "drivename plus colon" to indicate the
+* use of a "special drive", and then the rest of the path is parsed "normally",
+* and MACINTOSH uses a leading colon to indicate a relative path, and an embedded
+* colon to indicate a "drive plus absolute path", and finally defaults to a file
+* in the current working directory, which may or may not be defined.
+*
+* We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?)
+*/
+
+
+#ifdef ACORN
+
+
+/*
+* Most of the "file" routines for "ACORN" should be in "main-acn.c"
+*/
+
+
+#else /* ACORN */
+
+
+#ifdef SET_UID
+
+/*
+* Extract a "parsed" path from an initial filename
+* Normally, we simply copy the filename into the buffer
+* But leading tilde symbols must be handled in a special way
+* Replace "~user/" by the home directory of the user named "user"
+* Replace "~/" by the home directory of the current user
+*/
+errr path_parse(char *buf, int max, cptr file)
+{
+ cptr u, s;
+ struct passwd *pw;
+ char user[128];
+
+
+ /* Assume no result */
+ buf[0] = '\0';
+
+ /* No file? */
+ if (!file) return ( -1);
+
+ /* File needs no parsing */
+ if (file[0] != '~')
+ {
+ strcpy(buf, file);
+ return (0);
+ }
+
+ /* Point at the user */
+ u = file + 1;
+
+ /* Look for non-user portion of the file */
+ s = strstr(u, PATH_SEP);
+
+ /* Hack -- no long user names */
+ if (s && (s >= u + sizeof(user))) return (1);
+
+#ifdef GETLOGIN_BROKEN
+ /* Ask the environment for the home directory */
+ u = getenv("HOME");
+
+ if (!u) return (1);
+
+ (void)strcpy(buf, u);
+#else
+ /* Extract a user name */
+ if (s)
+ {
+ int i;
+ for (i = 0; u < s; ++i) user[i] = *u++;
+ user[i] = '\0';
+ u = user;
+ }
+
+ /* Look up the "current" user */
+ if (u[0] == '\0') u = getlogin();
+
+ /* Look up a user (or "current" user) */
+ if (u) pw = getpwnam(u);
+ else pw = getpwuid(getuid());
+
+ /* Nothing found? */
+ if (!pw) return (1);
+
+ /* Make use of the info */
+ (void)strcpy(buf, pw->pw_dir);
+#endif
+
+ /* Append the rest of the filename, if any */
+ if (s) (void)strcat(buf, s);
+
+ /* Success */
+ return (0);
+}
+
+
+#else /* SET_UID */
+
+
+/*
+* Extract a "parsed" path from an initial filename
+*
+* This requires no special processing on simple machines,
+* except for verifying the size of the filename.
+*/
+errr path_parse(char *buf, int max, cptr file)
+{
+ /* Accept the filename */
+ strnfmt(buf, max, "%s", file);
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* SET_UID */
+
+
+/*
+* Hack -- acquire a "temporary" file name if possible
+*
+* This filename is always in "system-specific" form.
+*/
+errr path_temp(char *buf, int max)
+{
+#ifdef WINDOWS
+ static u32b tmp_counter;
+ static char valid_characters[] =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char rand_ext[4];
+
+ rand_ext[0] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[1] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[2] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[3] = '\0';
+ strnfmt(buf, max, "%s/t_%ud.%s", ANGBAND_DIR_XTRA, tmp_counter, rand_ext);
+ tmp_counter++;
+#else
+ cptr s;
+
+ /* Temp file */
+ s = tmpnam(NULL);
+
+ /* Oops */
+ if (!s) return ( -1);
+
+ /* Format to length */
+ strnfmt(buf, max, "%s", s);
+#endif
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Create a new path by appending a file (or directory) to a path
+*
+* This requires no special processing on simple machines, except
+* for verifying the size of the filename, but note the ability to
+* bypass the given "path" with certain special file-names.
+*
+* Note that the "file" may actually be a "sub-path", including
+* a path and a file.
+*
+* Note that this function yields a path which must be "parsed"
+* using the "parse" function above.
+*/
+errr path_build(char *buf, int max, cptr path, cptr file)
+{
+ /* Special file */
+ if (file[0] == '~')
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Absolute file, on "normal" systems */
+ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* No path given */
+ else if (!path[0])
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Path and File */
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- replacement for "fopen()"
+*/
+FILE *my_fopen(cptr file, cptr mode)
+{
+#ifndef MACH_O_CARBON
+
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return (NULL);
+
+ /* Attempt to fopen the file anyway */
+ return (fopen(buf, mode));
+
+#else /* MACH_O_CARBON */
+
+char buf[1024];
+FILE *s;
+
+/* Hack -- Try to parse the path */
+if (path_parse(buf, 1024, file)) return (NULL);
+
+/* Attempt to fopen the file anyway */
+s = fopen(buf, mode);
+
+/* Set creator and type if the file is successfully opened */
+if (s) fsetfileinfo(buf, _fcreator, _ftype);
+
+/* Done */
+return (s);
+
+#endif /* MACH_O_CARBON */
+}
+
+
+/*
+* Hack -- replacement for "fclose()"
+*/
+errr my_fclose(FILE *fff)
+{
+ /* Require a file */
+ if (!fff) return ( -1);
+
+ /* Close, check for error */
+ if (fclose(fff) == EOF) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* ACORN */
+
+
+/*
+* Like "fgets()" but for strings
+*
+* Process tabs, strip internal non-printables
+*/
+errr my_str_fgets(cptr full_text, char *buf, huge n)
+{
+ static huge last_read = 0;
+ static huge full_size;
+ huge i = 0;
+
+ char *s;
+
+ char tmp[1024];
+
+ /* Initialize */
+ if (!buf)
+ {
+ last_read = 0;
+ full_size = strlen(full_text);
+ return 0;
+ }
+
+ /* The end! */
+ if (last_read >= full_size)
+ {
+ return 1;
+ }
+
+ while (last_read < full_size)
+ {
+ char c = full_text[last_read++];
+
+ tmp[i++] = c;
+
+ /* Dont overflow */
+ if (i >= 1024)
+ {
+ /* Nothing */
+ buf[0] = '\0';
+
+ /* Failure */
+ return (1);
+ }
+
+ if ((c == '\n') || (c == '\r'))
+ {
+ break;
+ }
+ }
+
+ tmp[i++] = '\n';
+ tmp[i++] = '\0';
+
+ /* We need it again */
+ i = 0;
+
+ /* Convert weirdness */
+ for (s = tmp; *s; s++)
+ {
+ /* Handle newline */
+ if (*s == '\n')
+ {
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle tabs */
+ else if (*s == '\t')
+ {
+ /* Hack -- require room */
+ if (i + 8 >= n) break;
+
+ /* Append a space */
+ buf[i++] = ' ';
+
+ /* Append some more spaces */
+ while (!(i % 8)) buf[i++] = ' ';
+ }
+
+ /* Handle printables */
+ else if (isprint(*s))
+ {
+ /* Copy */
+ buf[i++] = *s;
+
+ /* Check length */
+ if (i >= n) break;
+ }
+ }
+
+ /* Nothing */
+ buf[0] = '\0';
+
+ /* Failure */
+ return (1);
+}
+
+/*
+* Hack -- replacement for "fgets()"
+*
+* Read a string, without a newline, to a file
+*
+* Process tabs, strip internal non-printables
+*/
+errr my_fgets(FILE *fff, char *buf, huge n)
+{
+ huge i = 0;
+
+ while (TRUE)
+ {
+ int c = fgetc(fff);
+
+ if (c == EOF)
+ {
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success (0) if some characters were read */
+ return (i == 0);
+ }
+
+ /* Handle newline -- DOS (\015\012), Mac (\015), UNIX (\012) */
+ else if (c == '\r')
+ {
+ c = fgetc(fff);
+ if (c != '\n') ungetc(c, fff);
+
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+ else if (c == '\n')
+ {
+ c = fgetc(fff);
+ if (c != '\r') ungetc(c, fff);
+
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle tabs */
+ else if (c == '\t')
+ {
+ /* Hack -- require room */
+ if (i + 8 >= n) break;
+
+ /* Append 1-8 spaces */
+ do { buf[i++] = ' '; } while (i % 8);
+ }
+
+ /* Handle printables */
+ else if (isprint(c))
+ {
+ /* Copy */
+ buf[i++] = c;
+
+ /* Check length */
+ if (i >= n) break;
+ }
+ }
+
+ /* Nothing */
+ buf[0] = '\0';
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+* Hack -- replacement for "fputs()"
+*
+* Dump a string, plus a newline, to a file
+*
+* XXX XXX XXX Process internal weirdness?
+*/
+errr my_fputs(FILE *fff, cptr buf, huge n)
+{
+ /* XXX XXX */
+ n = n ? n : 0;
+
+ /* Dump, ignore errors */
+ (void)fprintf(fff, "%s\n", buf);
+
+ /* Success */
+ return (0);
+}
+
+
+#ifdef ACORN
+
+
+/*
+* Most of the "file" routines for "ACORN" should be in "main-acn.c"
+*
+* Many of them can be rewritten now that only "fd_open()" and "fd_make()"
+* and "my_fopen()" should ever create files.
+*/
+
+
+#else /* ACORN */
+
+
+/*
+* Code Warrior is a little weird about some functions
+*/
+#ifdef BEN_HACK
+extern int open(const char *, int, ...);
+extern int close(int);
+extern int read(int, void *, unsigned int);
+extern int write(int, const void *, unsigned int);
+extern long lseek(int, long, int);
+#endif /* BEN_HACK */
+
+
+/*
+* The Macintosh is a little bit brain-dead sometimes
+*/
+#ifdef MACINTOSH
+# define open(N, F, M) \
+((M), open((char*)(N), F))
+# define write(F, B, S) \
+write(F, (char*)(B), S)
+#endif /* MACINTOSH */
+
+
+/*
+* Several systems have no "O_BINARY" flag
+*/
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif /* O_BINARY */
+
+
+/*
+* Hack -- attempt to delete a file
+*/
+errr fd_kill(cptr file)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+ /* Remove */
+ (void)remove(buf);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to move a file
+*/
+errr fd_move(cptr file, cptr what)
+{
+ char buf[1024];
+ char aux[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(aux, 1024, what)) return ( -1);
+
+ /* Rename */
+ (void)rename(buf, aux);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to copy a file
+*/
+errr fd_copy(cptr file, cptr what)
+{
+ char buf[1024];
+ char aux[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(aux, 1024, what)) return ( -1);
+
+ /* Copy XXX XXX XXX */
+ /* (void)rename(buf, aux); */
+
+ /* XXX XXX XXX */
+ return (1);
+}
+
+
+/*
+* Hack -- attempt to open a file descriptor (create file)
+*
+* This function should fail if the file already exists
+*
+* Note that we assume that the file should be "binary"
+*
+* XXX XXX XXX The horrible "BEN_HACK" code is for compiling under
+* the CodeWarrior compiler, in which case, for some reason, none
+* of the "O_*" flags are defined, and we must fake the definition
+* of "O_RDONLY", "O_WRONLY", and "O_RDWR" in "A-win-h", and then
+* we must simulate the effect of the proper "open()" call below.
+*/
+int fd_make(cptr file, int mode)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+#ifdef BEN_HACK
+
+ /* Check for existance */
+ /* if (fd_close(fd_open(file, O_RDONLY | O_BINARY))) return (1); */
+
+ /* Mega-Hack -- Create the file */
+ (void)my_fclose(my_fopen(file, "wb"));
+
+ /* Re-open the file for writing */
+ return (open(buf, O_WRONLY | O_BINARY, mode));
+
+#else /* BEN_HACK */
+
+#ifdef MACH_O_CARBON
+
+{
+int fdes;
+
+/* Create the file, fail if exists, write-only, binary */
+fdes = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode);
+
+/* Set creator and type if the file is successfully opened */
+if (fdes >= 0) fsetfileinfo(buf, _fcreator, _ftype);
+
+/* Return the descriptor */
+return (fdes);
+}
+
+#else /* MACH_O_CARBON */
+
+/* Create the file, fail if exists, write-only, binary */
+return (open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode));
+
+#endif /* MACH_O_CARBON */
+
+#endif /* BEN_HACK */
+
+}
+
+
+/*
+* Hack -- attempt to open a file descriptor (existing file)
+*
+* Note that we assume that the file should be "binary"
+*/
+int fd_open(cptr file, int flags)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+#ifdef MACH_O_CARBON
+
+ {
+ int fdes;
+
+ /* Attempt to open the file */
+ fdes = open(buf, flags | O_BINARY, 0);
+
+ /* Set creator and type if the file is successfully opened */
+ if (fdes >= 0) fsetfileinfo(buf, _fcreator, _ftype);
+
+ /* Return the descriptor */
+ return (fdes);
+ }
+
+#else /* MACH_O_CARBON */
+
+/* Attempt to open the file */
+return (open(buf, flags | O_BINARY, 0));
+
+#endif /* MACH_O_CARBON */
+}
+
+
+/*
+* Hack -- attempt to lock a file descriptor
+*
+* Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
+*/
+errr fd_lock(int fd, int what)
+{
+ /* XXX XXX */
+ what = what ? what : 0;
+
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifdef SET_UID
+
+# ifdef USG
+
+# if defined(F_ULOCK) && defined(F_LOCK)
+
+ /* Un-Lock */
+ if (what == F_UNLCK)
+ {
+ /* Unlock it, Ignore errors */
+ lockf(fd, F_ULOCK, 0);
+ }
+
+ /* Lock */
+ else
+ {
+ /* Lock the score file */
+ if (lockf(fd, F_LOCK, 0) != 0) return (1);
+ }
+
+# endif
+
+# else
+
+# if defined(LOCK_UN) && defined(LOCK_EX)
+
+ /* Un-Lock */
+ if (what == F_UNLCK)
+ {
+ /* Unlock it, Ignore errors */
+ (void)flock(fd, LOCK_UN);
+ }
+
+ /* Lock */
+ else
+ {
+ /* Lock the score file */
+ if (flock(fd, LOCK_EX) != 0) return (1);
+ }
+
+# endif
+
+# endif
+
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to seek on a file descriptor
+*/
+errr fd_seek(int fd, huge n)
+{
+ s32b p;
+
+ /* Verify fd */
+ if (fd < 0) return ( -1);
+
+ /* Seek to the given position */
+ p = lseek(fd, n, SEEK_SET);
+
+ /* Failure */
+ if (p < 0) return (1);
+
+ /* Failure */
+ if ((huge)p != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to truncate a file descriptor
+*/
+errr fd_chop(int fd, huge n)
+{
+ /* XXX XXX */
+ n = n ? n : 0;
+
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#if defined(SUNOS) || defined(ULTRIX) || defined(NeXT)
+ /* Truncate */
+ ftruncate(fd, n);
+#endif
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to read data from a file descriptor
+*/
+errr fd_read(int fd, char *buf, huge n)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifndef SET_UID
+
+ /* Read pieces */
+ while (n >= 16384)
+ {
+ /* Read a piece */
+ if (read(fd, buf, 16384) != 16384) return (1);
+
+ /* Shorten the task */
+ buf += 16384;
+
+ /* Shorten the task */
+ n -= 16384;
+ }
+
+#endif
+
+ /* Read the final piece */
+ if ((huge)read(fd, buf, n) != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- Attempt to write data to a file descriptor
+*/
+errr fd_write(int fd, cptr buf, huge n)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifndef SET_UID
+
+ /* Write pieces */
+ while (n >= 16384)
+ {
+ /* Write a piece */
+ if (write(fd, buf, 16384) != 16384) return (1);
+
+ /* Shorten the task */
+ buf += 16384;
+
+ /* Shorten the task */
+ n -= 16384;
+ }
+
+#endif
+
+ /* Write the final piece */
+ if ((huge)write(fd, buf, n) != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to close a file descriptor
+*/
+errr fd_close(int fd)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+ /* Close */
+ (void)close(fd);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+#endif /* ACORN */
+
+
+
+
+/*
+* XXX XXX XXX Important note about "colors" XXX XXX XXX
+*
+* The "TERM_*" color definitions list the "composition" of each
+* "Angband color" in terms of "quarters" of each of the three color
+* components (Red, Green, Blue), for example, TERM_UMBER is defined
+* as 2/4 Red, 1/4 Green, 0/4 Blue.
+*
+* The following info is from "Torbjorn Lindgren" (see "main-xaw.c").
+*
+* These values are NOT gamma-corrected. On most machines (with the
+* Macintosh being an important exception), you must "gamma-correct"
+* the given values, that is, "correct for the intrinsic non-linearity
+* of the phosphor", by converting the given intensity levels based
+* on the "gamma" of the target screen, which is usually 1.7 (or 1.5).
+*
+* The actual formula for conversion is unknown to me at this time,
+* but you can use the table below for the most common gamma values.
+*
+* So, on most machines, simply convert the values based on the "gamma"
+* of the target screen, which is usually in the range 1.5 to 1.7, and
+* usually is closest to 1.7. The converted value for each of the five
+* different "quarter" values is given below:
+*
+* Given Gamma 1.0 Gamma 1.5 Gamma 1.7 Hex 1.7
+* ----- ---- ---- ---- ---
+* 0/4 0.00 0.00 0.00 #00
+* 1/4 0.25 0.27 0.28 #47
+* 2/4 0.50 0.55 0.56 #8f
+* 3/4 0.75 0.82 0.84 #d7
+* 4/4 1.00 1.00 1.00 #ff
+*
+* Note that some machines (i.e. most IBM machines) are limited to a
+* hard-coded set of colors, and so the information above is useless.
+*
+* Also, some machines are limited to a pre-determined set of colors,
+* for example, the IBM can only display 16 colors, and only 14 of
+* those colors resemble colors used by Angband, and then only when
+* you ignore the fact that "Slate" and "cyan" are not really matches,
+* so on the IBM, we use "orange" for both "Umber", and "Light Umber"
+* in addition to the obvious "Orange", since by combining all of the
+* "indeterminate" colors into a single color, the rest of the colors
+* are left with "meaningful" values.
+*/
+
+
+/*
+* Move the cursor
+*/
+void move_cursor(int row, int col)
+{
+ Term_gotoxy(col, row);
+}
+
+
+
+/*
+* Convert a decimal to a single digit octal number
+*/
+static char octify(uint i)
+{
+ return (hexsym[i % 8]);
+}
+
+/*
+* Convert a decimal to a single digit hex number
+*/
+static char hexify(uint i)
+{
+ return (hexsym[i % 16]);
+}
+
+
+/*
+* Convert a octal-digit into a decimal
+*/
+static int deoct(char c)
+{
+ if (isdigit(c)) return (D2I(c));
+ return (0);
+}
+
+/*
+* Convert a hexidecimal-digit into a decimal
+*/
+static int dehex(char c)
+{
+ if (isdigit(c)) return (D2I(c));
+ if (islower(c)) return (A2I(c) + 10);
+ if (isupper(c)) return (A2I(tolower(c)) + 10);
+ return (0);
+}
+
+
+static int my_stricmp(cptr a, cptr b)
+{
+ cptr s1, s2;
+ char z1, z2;
+
+ /* Scan the strings */
+ for (s1 = a, s2 = b; TRUE; s1++, s2++)
+ {
+ z1 = FORCEUPPER(*s1);
+ z2 = FORCEUPPER(*s2);
+ if (z1 < z2) return ( -1);
+ if (z1 > z2) return (1);
+ if (!z1) return (0);
+ }
+}
+
+static int my_strnicmp(cptr a, cptr b, int n)
+{
+ cptr s1, s2;
+ char z1, z2;
+
+ /* Scan the strings */
+ for (s1 = a, s2 = b; n > 0; s1++, s2++, n--)
+ {
+ z1 = FORCEUPPER(*s1);
+ z2 = FORCEUPPER(*s2);
+ if (z1 < z2) return ( -1);
+ if (z1 > z2) return (1);
+ if (!z1) return (0);
+ }
+ return 0;
+}
+
+
+static void trigger_text_to_ascii(char **bufptr, cptr *strptr)
+{
+ char *s = *bufptr;
+ cptr str = *strptr;
+ bool mod_status[MAX_MACRO_MOD];
+
+ int i, len = 0;
+ int shiftstatus = 0;
+ cptr key_code;
+
+ if (macro_template == NULL)
+ return;
+
+ for (i = 0; macro_modifier_chr[i]; i++)
+ mod_status[i] = FALSE;
+ str++;
+
+ /* Examine modifier keys */
+ while (1)
+ {
+ for (i = 0; macro_modifier_chr[i]; i++)
+ {
+ len = strlen(macro_modifier_name[i]);
+
+ if (!my_strnicmp(str, macro_modifier_name[i], len))
+ break;
+ }
+ if (!macro_modifier_chr[i]) break;
+ str += len;
+ mod_status[i] = TRUE;
+ if ('S' == macro_modifier_chr[i])
+ shiftstatus = 1;
+ }
+ for (i = 0; i < max_macrotrigger; i++)
+ {
+ len = strlen(macro_trigger_name[i]);
+ if (!my_strnicmp(str, macro_trigger_name[i], len) && ']' == str[len])
+ {
+ /* a trigger name found */
+ break;
+ }
+ }
+
+ /* Invalid trigger name? */
+ if (i == max_macrotrigger)
+ {
+ str = strchr(str, ']');
+ if (str)
+ {
+ *s++ = (char)31;
+ *s++ = (char)13;
+ *bufptr = s;
+ *strptr = str; /* where **strptr == ']' */
+ }
+ return;
+ }
+
+ key_code = macro_trigger_keycode[shiftstatus][i];
+ str += len;
+
+ *s++ = (char)31;
+ for (i = 0; macro_template[i]; i++)
+ {
+ char ch = macro_template[i];
+ int j;
+
+ switch (ch)
+ {
+ case '&':
+ for (j = 0; macro_modifier_chr[j]; j++)
+ {
+ if (mod_status[j])
+ *s++ = macro_modifier_chr[j];
+ }
+ break;
+ case '#':
+ strcpy(s, key_code);
+ s += strlen(key_code);
+ break;
+ default:
+ *s++ = ch;
+ break;
+ }
+ }
+ *s++ = (char)13;
+
+ *bufptr = s;
+ *strptr = str; /* where **strptr == ']' */
+ return;
+}
+
+
+/*
+* Hack -- convert a printable string into real ascii
+*
+* I have no clue if this function correctly handles, for example,
+* parsing "\xFF" into a (signed) char. Whoever thought of making
+* the "sign" of a "char" undefined is a complete moron. Oh well.
+*/
+void text_to_ascii(char *buf, cptr str)
+{
+ char *s = buf;
+
+ /* Analyze the "ascii" string */
+ while (*str)
+ {
+ /* Backslash codes */
+ if (*str == '\\')
+ {
+ /* Skip the backslash */
+ str++;
+
+ /* Macro Trigger */
+ if (*str == '[')
+ {
+ trigger_text_to_ascii(&s, &str);
+ }
+
+ /* Hex-mode XXX */
+ else if (*str == 'x')
+ {
+ *s = 16 * dehex(*++str);
+ *s++ += dehex(*++str);
+ }
+
+ /* Hack -- simple way to specify "backslash" */
+ else if (*str == '\\')
+ {
+ *s++ = '\\';
+ }
+
+ /* Hack -- simple way to specify "caret" */
+ else if (*str == '^')
+ {
+ *s++ = '^';
+ }
+
+ /* Hack -- simple way to specify "space" */
+ else if (*str == 's')
+ {
+ *s++ = ' ';
+ }
+
+ /* Hack -- simple way to specify Escape */
+ else if (*str == 'e')
+ {
+ *s++ = ESCAPE;
+ }
+
+ /* Backspace */
+ else if (*str == 'b')
+ {
+ *s++ = '\b';
+ }
+
+ /* Newline */
+ else if (*str == 'n')
+ {
+ *s++ = '\n';
+ }
+
+ /* Return */
+ else if (*str == 'r')
+ {
+ *s++ = '\r';
+ }
+
+ /* Tab */
+ else if (*str == 't')
+ {
+ *s++ = '\t';
+ }
+
+ /* Octal-mode */
+ else if (*str == '0')
+ {
+ *s = 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Octal-mode */
+ else if (*str == '1')
+ {
+ *s = 64 + 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Octal-mode */
+ else if (*str == '2')
+ {
+ *s = 64 * 2 + 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Octal-mode */
+ else if (*str == '3')
+ {
+ *s = 64 * 3 + 8 * deoct(*++str);
+ *s++ += deoct(*++str);
+ }
+
+ /* Skip the final char */
+ str++;
+ }
+
+ /* Normal Control codes */
+ else if (*str == '^')
+ {
+ str++;
+ *s++ = (*str++ & 037);
+ }
+
+ /* Normal chars */
+ else
+ {
+ *s++ = *str++;
+ }
+ }
+
+ /* Terminate */
+ *s = '\0';
+}
+
+
+bool trigger_ascii_to_text(char **bufptr, cptr *strptr)
+{
+ char *s = *bufptr;
+ cptr str = *strptr;
+ char key_code[100];
+ int i;
+ cptr tmp;
+
+ if (macro_template == NULL)
+ return FALSE;
+
+ *s++ = '\\';
+ *s++ = '[';
+
+ for (i = 0; macro_template[i]; i++)
+ {
+ int j;
+ char ch = macro_template[i];
+
+ switch (ch)
+ {
+ case '&':
+ while ((tmp = strchr(macro_modifier_chr, *str)))
+ {
+ j = (int)(tmp - macro_modifier_chr);
+ tmp = macro_modifier_name[j];
+ while (*tmp) *s++ = *tmp++;
+ str++;
+ }
+ break;
+ case '#':
+ for (j = 0; *str && *str != (char)13; j++)
+ key_code[j] = *str++;
+ key_code[j] = '\0';
+ break;
+ default:
+ if (ch != *str) return FALSE;
+ str++;
+ }
+ }
+ if (*str++ != (char)13) return FALSE;
+
+ for (i = 0; i < max_macrotrigger; i++)
+ {
+ if (!my_stricmp(key_code, macro_trigger_keycode[0][i])
+ || !my_stricmp(key_code, macro_trigger_keycode[1][i]))
+ break;
+ }
+ if (i == max_macrotrigger)
+ return FALSE;
+
+ tmp = macro_trigger_name[i];
+ while (*tmp) *s++ = *tmp++;
+
+ *s++ = ']';
+
+ *bufptr = s;
+ *strptr = str;
+ return TRUE;
+}
+
+
+/*
+* Hack -- convert a string into a printable form
+*/
+void ascii_to_text(char *buf, cptr str)
+{
+ char *s = buf;
+
+ /* Analyze the "ascii" string */
+ while (*str)
+ {
+ byte i = (byte)(*str++);
+
+ /* Macro Trigger */
+ if (i == 31)
+ {
+ if (!trigger_ascii_to_text(&s, &str))
+ {
+ *s++ = '^';
+ *s++ = '_';
+ }
+ }
+ else if (i == ESCAPE)
+ {
+ *s++ = '\\';
+ *s++ = 'e';
+ }
+ else if (i == ' ')
+ {
+ *s++ = '\\';
+ *s++ = 's';
+ }
+ else if (i == '\b')
+ {
+ *s++ = '\\';
+ *s++ = 'b';
+ }
+ else if (i == '\t')
+ {
+ *s++ = '\\';
+ *s++ = 't';
+ }
+ else if (i == '\n')
+ {
+ *s++ = '\\';
+ *s++ = 'n';
+ }
+ else if (i == '\r')
+ {
+ *s++ = '\\';
+ *s++ = 'r';
+ }
+ else if (i == '^')
+ {
+ *s++ = '\\';
+ *s++ = '^';
+ }
+ else if (i == '\\')
+ {
+ *s++ = '\\';
+ *s++ = '\\';
+ }
+ else if (i < 32)
+ {
+ *s++ = '^';
+ *s++ = i + 64;
+ }
+ else if (i < 127)
+ {
+ *s++ = i;
+ }
+ else if (i < 64)
+ {
+ *s++ = '\\';
+ *s++ = '0';
+ *s++ = octify(i / 8);
+ *s++ = octify(i % 8);
+ }
+ else
+ {
+ *s++ = '\\';
+ *s++ = 'x';
+ *s++ = hexify(i / 16);
+ *s++ = hexify(i % 16);
+ }
+ }
+
+ /* Terminate */
+ *s = '\0';
+}
+
+
+
+/*
+* The "macro" package
+*
+* Functions are provided to manipulate a collection of macros, each
+* of which has a trigger pattern string and a resulting action string
+* and a small set of flags.
+*/
+
+
+
+/*
+* Determine if any macros have ever started with a given character.
+*/
+static bool macro__use[256];
+
+
+/*
+* Find the macro (if any) which exactly matches the given pattern
+*/
+sint macro_find_exact(cptr pat)
+{
+ int i;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which do not match the pattern */
+ if (!streq(macro__pat[i], pat)) continue;
+
+ /* Found one */
+ return (i);
+ }
+
+ /* No matches */
+ return ( -1);
+}
+
+
+/*
+* Find the first macro (if any) which contains the given pattern
+*/
+static sint macro_find_check(cptr pat)
+{
+ int i;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which do not contain the pattern */
+ if (!prefix(macro__pat[i], pat)) continue;
+
+ /* Found one */
+ return (i);
+ }
+
+ /* Nothing */
+ return ( -1);
+}
+
+
+/*
+* Find the first macro (if any) which contains the given pattern and more
+*/
+static sint macro_find_maybe(cptr pat)
+{
+ int i;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which do not contain the pattern */
+ if (!prefix(macro__pat[i], pat)) continue;
+
+ /* Skip macros which exactly match the pattern XXX XXX */
+ if (streq(macro__pat[i], pat)) continue;
+
+ /* Found one */
+ return (i);
+ }
+
+ /* Nothing */
+ return ( -1);
+}
+
+
+/*
+* Find the longest macro (if any) which starts with the given pattern
+*/
+static sint macro_find_ready(cptr pat)
+{
+ int i, t, n = -1, s = -1;
+
+ /* Nothing possible */
+ if (!macro__use[(byte)(pat[0])])
+ {
+ return ( -1);
+ }
+
+ /* Scan the macros */
+ for (i = 0; i < macro__num; ++i)
+ {
+ /* Skip macros which are not contained by the pattern */
+ if (!prefix(pat, macro__pat[i])) continue;
+
+ /* Obtain the length of this macro */
+ t = strlen(macro__pat[i]);
+
+ /* Only track the "longest" pattern */
+ if ((n >= 0) && (s > t)) continue;
+
+ /* Track the entry */
+ n = i;
+ s = t;
+ }
+
+ /* Result */
+ return (n);
+}
+
+
+/*
+* Add a macro definition (or redefinition).
+*
+* We should use "act == NULL" to "remove" a macro, but this might make it
+* impossible to save the "removal" of a macro definition. XXX XXX XXX
+*
+* We should consider refusing to allow macros which contain existing macros,
+* or which are contained in existing macros, because this would simplify the
+* macro analysis code. XXX XXX XXX
+*
+* We should consider removing the "command macro" crap, and replacing it
+* with some kind of "powerful keymap" ability, but this might make it hard
+* to change the "roguelike" option from inside the game. XXX XXX XXX
+*/
+errr macro_add(cptr pat, cptr act)
+{
+ int n;
+
+
+ /* Paranoia -- require data */
+ if (!pat || !act) return ( -1);
+
+
+ /* Look for any existing macro */
+ n = macro_find_exact(pat);
+
+ /* Replace existing macro */
+ if (n >= 0)
+ {
+ /* Free the old macro action */
+ string_free(macro__act[n]);
+ }
+
+ /* Create a new macro */
+ else
+ {
+ /* Acquire a new index */
+ n = macro__num++;
+
+ /* Save the pattern */
+ macro__pat[n] = string_make(pat);
+ }
+
+ /* Save the action */
+ macro__act[n] = string_make(act);
+
+ /* Efficiency */
+ macro__use[(byte)(pat[0])] = TRUE;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+* Initialize the "macro" package
+*/
+errr macro_init(void)
+{
+ /* Macro patterns */
+ C_MAKE(macro__pat, MACRO_MAX, cptr);
+
+ /* Macro actions */
+ C_MAKE(macro__act, MACRO_MAX, cptr);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Local "need flush" variable
+*/
+static bool flush_later = FALSE;
+
+
+/*
+* Local variable -- we are inside a "macro action"
+*
+* Do not match any macros until "ascii 30" is found.
+*/
+static bool parse_macro = FALSE;
+
+/*
+* Local variable -- we are inside a "macro trigger"
+*
+* Strip all keypresses until a low ascii value is found.
+*/
+static bool parse_under = FALSE;
+
+
+/*
+* Flush all input chars. Actually, remember the flush,
+* and do a "special flush" before the next "inkey()".
+*
+* This is not only more efficient, but also necessary to make sure
+* that various "inkey()" codes are not "lost" along the way.
+*/
+void flush(void)
+{
+ /* Do it later */
+ flush_later = TRUE;
+}
+
+
+/*
+* Flush the screen, make a noise
+*/
+void bell(void)
+{
+ /* Mega-Hack -- Flush the output */
+ Term_fresh();
+
+ /* Make a bell noise (if allowed) */
+ if (ring_bell) Term_xtra(TERM_XTRA_NOISE, 0);
+
+ /* Flush the input (later!) */
+ flush();
+}
+
+
+/*
+* Hack -- Make a (relevant?) sound
+*/
+void sound(int val)
+{
+ /* No sound */
+ if (!use_sound) return;
+
+ /* Make a sound (if allowed) */
+ Term_xtra(TERM_XTRA_SOUND, val);
+}
+
+
+
+/*
+* Helper function called only from "inkey()"
+*
+* This function does almost all of the "macro" processing.
+*
+* We use the "Term_key_push()" function to handle "failed" macros, as well
+* as "extra" keys read in while choosing the proper macro, and also to hold
+* the action for the macro, plus a special "ascii 30" character indicating
+* that any macro action in progress is complete. Embedded macros are thus
+* illegal, unless a macro action includes an explicit "ascii 30" character,
+* which would probably be a massive hack, and might break things.
+*
+* Only 500 (0+1+2+...+29+30) milliseconds may elapse between each key in
+* the macro trigger sequence. If a key sequence forms the "prefix" of a
+* macro trigger, 500 milliseconds must pass before the key sequence is
+* known not to be that macro trigger. XXX XXX XXX
+*/
+static char inkey_aux(void)
+{
+ int k = 0, n, p = 0, w = 0;
+
+ char ch;
+
+ cptr pat, act;
+
+ char buf[1024];
+
+
+ /* Wait for a keypress */
+ (void)(Term_inkey(&ch, TRUE, TRUE));
+
+
+ /* End "macro action" */
+ if (ch == 30) parse_macro = FALSE;
+
+ /* Inside "macro action" */
+ if (ch == 30) return (ch);
+
+ /* Inside "macro action" */
+ if (parse_macro) return (ch);
+
+ /* Inside "macro trigger" */
+ if (parse_under) return (ch);
+
+
+ /* Save the first key, advance */
+ buf[p++] = ch;
+ buf[p] = '\0';
+
+
+ /* Check for possible macro */
+ k = macro_find_check(buf);
+
+ /* No macro pending */
+ if (k < 0) return (ch);
+
+
+ /* Wait for a macro, or a timeout */
+ while (TRUE)
+ {
+ /* Check for pending macro */
+ k = macro_find_maybe(buf);
+
+ /* No macro pending */
+ if (k < 0) break;
+
+ /* Check for (and remove) a pending key */
+ if (0 == Term_inkey(&ch, FALSE, TRUE))
+ {
+ /* Append the key */
+ buf[p++] = ch;
+ buf[p] = '\0';
+
+ /* Restart wait */
+ w = 0;
+ }
+
+ /* No key ready */
+ else
+ {
+ /* Increase "wait" */
+ w += 10;
+
+ /* Excessive delay */
+ if (w >= 100) break;
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, w);
+ }
+ }
+
+
+ /* Check for available macro */
+ k = macro_find_ready(buf);
+
+ /* No macro available */
+ if (k < 0)
+ {
+ /* Push all the keys back on the queue */
+ while (p > 0)
+ {
+ /* Push the key, notice over-flow */
+ if (Term_key_push(buf[--p])) return (0);
+ }
+
+ /* Wait for (and remove) a pending key */
+ (void)Term_inkey(&ch, TRUE, TRUE);
+
+ /* Return the key */
+ return (ch);
+ }
+
+
+ /* Get the pattern */
+ pat = macro__pat[k];
+
+ /* Get the length of the pattern */
+ n = strlen(pat);
+
+ /* Push the "extra" keys back on the queue */
+ while (p > n)
+ {
+ /* Push the key, notice over-flow */
+ if (Term_key_push(buf[--p])) return (0);
+ }
+
+
+ /* Begin "macro action" */
+ parse_macro = TRUE;
+
+ /* Push the "end of macro action" key */
+ if (Term_key_push(30)) return (0);
+
+
+ /* Access the macro action */
+ act = macro__act[k];
+
+ /* Get the length of the action */
+ n = strlen(act);
+
+ /* Push the macro "action" onto the key queue */
+ while (n > 0)
+ {
+ /* Push the key, notice over-flow */
+ if (Term_key_push(act[--n])) return (0);
+ }
+
+
+ /* Hack -- Force "inkey()" to call us again */
+ return (0);
+}
+
+
+/*
+* Mega-Hack -- special "inkey_next" pointer. XXX XXX XXX
+*
+* This special pointer allows a sequence of keys to be "inserted" into
+* the stream of keys returned by "inkey()". This key sequence will not
+* trigger any macros, and cannot be bypassed by the Borg. It is used
+* in Angband to handle "keymaps".
+*/
+static cptr inkey_next = NULL;
+
+
+#ifdef ALLOW_BORG
+
+/*
+* Mega-Hack -- special "inkey_hack" hook. XXX XXX XXX
+*
+* This special function hook allows the "Borg" (see elsewhere) to take
+* control of the "inkey()" function, and substitute in fake keypresses.
+*/
+char (*inkey_hack)(int flush_first) = NULL;
+
+#endif /* ALLOW_BORG */
+
+
+
+/*
+* Get a keypress from the user.
+*
+* This function recognizes a few "global parameters". These are variables
+* which, if set to TRUE before calling this function, will have an effect
+* on this function, and which are always reset to FALSE by this function
+* before this function returns. Thus they function just like normal
+* parameters, except that most calls to this function can ignore them.
+*
+* If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
+* and any macro processing in progress will be aborted. This flag is
+* set by the "flush()" function, which does not actually flush anything
+* itself, but rather, triggers delayed input flushing via "inkey_xtra".
+*
+* If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+* keypress is available, instead of waiting for a keypress.
+*
+* If "inkey_base" is TRUE, then all macro processing will be bypassed.
+* If "inkey_base" and "inkey_scan" are both TRUE, then this function will
+* not return immediately, but will wait for a keypress for as long as the
+* normal macro matching code would, allowing the direct entry of macro
+* triggers. The "inkey_base" flag is extremely dangerous!
+*
+* If "inkey_flag" is TRUE, then we will assume that we are waiting for a
+* normal command, and we will only show the cursor if "hilite_player" is
+* TRUE (or if the player is in a store), instead of always showing the
+* cursor. The various "main-xxx.c" files should avoid saving the game
+* in response to a "menu item" request unless "inkey_flag" is TRUE, to
+* prevent savefile corruption.
+*
+* If we are waiting for a keypress, and no keypress is ready, then we will
+* refresh (once) the window which was active when this function was called.
+*
+* Note that "back-quote" is automatically converted into "escape" for
+* convenience on machines with no "escape" key. This is done after the
+* macro matching, so the user can still make a macro for "backquote".
+*
+* Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
+* and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
+* provide support for simple keyboard "macros". These keys are so strange
+* that their loss as normal keys will probably be noticed by nobody. The
+* "ascii 30" key is used to indicate the "end" of a macro action, which
+* allows recursive macros to be avoided. The "ascii 31" key is used by
+* some of the "main-xxx.c" files to introduce macro trigger sequences.
+*
+* Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key,
+* which can be used to give a variety of "sub-commands" which can be used
+* any time. These sub-commands could include commands to take a picture of
+* the current screen, to start/stop recording a macro action, etc.
+*
+* If "angband_term[0]" is not active, we will make it active during this
+* function, so that the various "main-xxx.c" files can assume that input
+* is only requested (via "Term_inkey()") when "angband_term[0]" is active.
+*
+* Mega-Hack -- This function is used as the entry point for clearing the
+* "signal_count" variable, and of the "character_saved" variable.
+*
+* Hack -- Note the use of "inkey_next" to allow "keymaps" to be processed.
+*
+* Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
+* control of the keyboard from the user.
+*/
+char inkey(void)
+{
+ int v;
+
+ char kk;
+
+ char ch = 0;
+
+ bool done = FALSE;
+
+ term *old = Term;
+
+ /* Hack -- Use the "inkey_next" pointer */
+ if (inkey_next && *inkey_next && !inkey_xtra)
+ {
+ /* Get next character, and advance */
+ ch = *inkey_next++;
+
+ /* Cancel the various "global parameters" */
+ inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
+
+ /* Accept result */
+ macro_recorder_add(ch);
+ return (ch);
+ }
+
+ /* Forget pointer */
+ inkey_next = NULL;
+
+
+#ifdef ALLOW_BORG
+
+ /* Mega-Hack -- Use the special hook */
+ if (inkey_hack && ((ch = (*inkey_hack)(inkey_xtra)) != 0))
+ {
+ /* Cancel the various "global parameters" */
+ inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
+
+ /* Accept result */
+ macro_recorder_add(ch);
+ return (ch);
+ }
+
+#endif /* ALLOW_BORG */
+
+
+ /* Hack -- handle delayed "flush()" */
+ if (inkey_xtra)
+ {
+ /* End "macro action" */
+ parse_macro = FALSE;
+
+ /* End "macro trigger" */
+ parse_under = FALSE;
+
+ /* Forget old keypresses */
+ Term_flush();
+ }
+
+
+ /* Access cursor state */
+ (void)Term_get_cursor(&v);
+
+ /* Show the cursor if waiting, except sometimes in "command" mode */
+ if (!inkey_scan && (!inkey_flag || hilite_player || character_icky))
+ {
+ /* Show the cursor */
+ (void)Term_set_cursor(1);
+ }
+
+
+ /* Hack -- Activate main screen */
+ Term_activate(angband_term[0]);
+
+
+ /* Get a key */
+ while (!ch)
+ {
+ /* Hack -- Handle "inkey_scan" */
+ if (!inkey_base && inkey_scan &&
+ (0 != Term_inkey(&kk, FALSE, FALSE)))
+ {
+ break;
+ }
+
+
+ /* Hack -- Flush output once when no key ready */
+ if (!done && (0 != Term_inkey(&kk, FALSE, FALSE)))
+ {
+ /* Hack -- activate proper term */
+ Term_activate(old);
+
+ /* Flush output */
+ Term_fresh();
+
+ /* Hack -- activate main screen */
+ Term_activate(angband_term[0]);
+
+ /* Mega-Hack -- reset saved flag */
+ character_saved = FALSE;
+
+ /* Mega-Hack -- reset signal counter */
+ signal_count = 0;
+
+ /* Only once */
+ done = TRUE;
+ }
+
+
+ /* Hack -- Handle "inkey_base" */
+ if (inkey_base)
+ {
+ int w = 0;
+
+ /* Wait forever */
+ if (!inkey_scan)
+ {
+ /* Wait for (and remove) a pending key */
+ if (0 == Term_inkey(&ch, TRUE, TRUE))
+ {
+ /* Done */
+ break;
+ }
+
+ /* Oops */
+ break;
+ }
+
+ /* Wait */
+ while (TRUE)
+ {
+ /* Check for (and remove) a pending key */
+ if (0 == Term_inkey(&ch, FALSE, TRUE))
+ {
+ /* Done */
+ break;
+ }
+
+ /* No key ready */
+ else
+ {
+ /* Increase "wait" */
+ w += 10;
+
+ /* Excessive delay */
+ if (w >= 100) break;
+
+ /* Delay */
+ Term_xtra(TERM_XTRA_DELAY, w);
+ }
+ }
+
+ /* Done */
+ break;
+ }
+
+
+ /* Get a key (see above) */
+ ch = inkey_aux();
+
+
+ /* Handle "control-right-bracket" */
+ if ((ch == 29) || ((!rogue_like_commands) && (ch == KTRL('D'))))
+ {
+ /* Strip this key */
+ ch = 0;
+
+ if (!do_movies)
+ /* Do an html dump */
+ do_cmd_html_dump();
+ else
+ /* Do a text box in the cmovie */
+ do_cmovie_insert();
+
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Treat back-quote as escape */
+ if (ch == '`') ch = ESCAPE;
+
+
+ /* End "macro trigger" */
+ if (parse_under && (ch <= 32))
+ {
+ /* Strip this key */
+ ch = 0;
+
+ /* End "macro trigger" */
+ parse_under = FALSE;
+ }
+
+
+ /* Handle "control-caret" */
+ if (ch == 30)
+ {
+ /* Strip this key */
+ ch = 0;
+ }
+
+ /* Handle "control-underscore" */
+ else if (ch == 31)
+ {
+ /* Strip this key */
+ ch = 0;
+
+ /* Begin "macro trigger" */
+ parse_under = TRUE;
+ }
+
+ /* Inside "macro trigger" */
+ else if (parse_under)
+ {
+ /* Strip this key */
+ ch = 0;
+ }
+ }
+
+
+ /* Hack -- restore the term */
+ Term_activate(old);
+
+
+ /* Restore the cursor */
+ Term_set_cursor(v);
+
+
+ /* Cancel the various "global parameters" */
+ inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
+
+
+ /* Return the keypress */
+ macro_recorder_add(ch);
+ return (ch);
+}
+
+
+
+
+/*
+* We use a global array for all inscriptions to reduce the memory
+* spent maintaining inscriptions. Of course, it is still possible
+* to run out of inscription memory, especially if too many different
+* inscriptions are used, but hopefully this will be rare.
+*
+* We use dynamic string allocation because otherwise it is necessary
+* to pre-guess the amount of quark activity. We limit the total
+* number of quarks, but this is much easier to "expand" as needed.
+*
+* Any two items with the same inscription will have the same "quark"
+* index, which should greatly reduce the need for inscription space.
+*
+* Note that "quark zero" is NULL and should not be "dereferenced".
+*/
+
+/*
+* Add a new "quark" to the set of quarks.
+*/
+s16b quark_add(cptr str)
+{
+ int i;
+
+ /* Look for an existing quark */
+ for (i = 1; i < quark__num; i++)
+ {
+ /* Check for equality */
+ if (streq(quark__str[i], str)) return (i);
+ }
+
+ /* Paranoia -- Require room */
+ if (quark__num == QUARK_MAX) return (0);
+
+ /* New maximal quark */
+ quark__num = i + 1;
+
+ /* Add a new quark */
+ quark__str[i] = string_make(str);
+
+ /* Return the index */
+ return (i);
+}
+
+
+/*
+* This function looks up a quark
+*/
+cptr quark_str(s16b i)
+{
+ cptr q;
+
+ /* Verify */
+ if ((i < 0) || (i >= quark__num)) i = 0;
+
+ /* Access the quark */
+ q = quark__str[i];
+
+ /* Return the quark */
+ return (q);
+}
+
+
+
+
+/*
+* Second try for the "message" handling routines.
+*
+* Each call to "message_add(s)" will add a new "most recent" message
+* to the "message recall list", using the contents of the string "s".
+*
+* The messages will be stored in such a way as to maximize "efficiency",
+* that is, we attempt to maximize the number of sequential messages that
+* can be retrieved, given a limited amount of storage space.
+*
+* We keep a buffer of chars to hold the "text" of the messages, not
+* necessarily in "order", and an array of offsets into that buffer,
+* representing the actual messages. This is made more complicated
+* by the fact that both the array of indexes, and the buffer itself,
+* are both treated as "circular arrays" for efficiency purposes, but
+* the strings may not be "broken" across the ends of the array.
+*
+* The "message_add()" function is rather "complex", because it must be
+* extremely efficient, both in space and time, for use with the Borg.
+*/
+
+
+
+/*
+* How many messages are "available"?
+*/
+s16b message_num(void)
+{
+ int last, next, n;
+
+ /* Extract the indexes */
+ last = message__last;
+ next = message__next;
+
+ /* Handle "wrap" */
+ if (next < last) next += MESSAGE_MAX;
+
+ /* Extract the space */
+ n = (next - last);
+
+ /* Return the result */
+ return (n);
+}
+
+
+
+/*
+* Recall the "text" of a saved message
+*/
+cptr message_str(int age)
+{
+ static char buf[1024];
+ s16b x;
+ s16b o;
+ cptr s;
+
+ /* Forgotten messages have no text */
+ if ((age < 0) || (age >= message_num())) return ("");
+
+ /* Acquire the "logical" index */
+ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
+
+ /* Get the "offset" for the message */
+ o = message__ptr[x];
+
+ /* Access the message text */
+ s = &message__buf[o];
+
+ /* Hack -- Handle repeated messages */
+ if (message__count[x] > 1)
+ {
+ strnfmt(buf, 1024, "%s <%dx>", s, message__count[x]);
+ s = buf;
+ }
+
+ /* Return the message text */
+ return (s);
+}
+
+/*
+* Recall the color of a saved message
+*/
+byte message_color(int age)
+{
+ s16b x;
+ byte color = TERM_WHITE;
+
+ /* Forgotten messages have no text */
+ if ((age < 0) || (age >= message_num())) return (TERM_WHITE);
+
+ /* Acquire the "logical" index */
+ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
+
+ /* Get the "offset" for the message */
+ color = message__color[x];
+
+ /* Return the message text */
+ return (color);
+}
+
+/*
+ * Recall the type of a saved message
+ */
+byte message_type(int age)
+{
+ s16b x;
+ byte type;
+
+ /* Forgotten messages have no text */
+ if ((age < 0) || (age >= message_num())) return (MESSAGE_NONE);
+
+ /* Acquire the "logical" index */
+ x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
+
+ /* Get the "offset" for the message */
+ type = message__type[x];
+
+ /* Return the message text */
+ return (type);
+}
+
+
+
+/*
+* Add a new message, with great efficiency
+*/
+void message_add(byte type, cptr str, byte color)
+{
+ int i, k, x, n;
+ cptr s;
+
+
+ /*** Step 1 -- Analyze the message ***/
+
+ /* Hack -- Ignore "non-messages" */
+ if (!str) return;
+
+ /* Message length */
+ n = strlen(str);
+
+ /* Important Hack -- Ignore "long" messages */
+ if (n >= MESSAGE_BUF / 4) return;
+
+
+ /*** Step 2 -- Handle repeated messages ***/
+
+ /* Acquire the "logical" last index */
+ x = (message__next + MESSAGE_MAX - 1) % MESSAGE_MAX;
+
+ /* Get the last message text */
+ s = &message__buf[message__ptr[x]];
+
+ /* Last message repeated? */
+ if (streq(str, s))
+ {
+ /* Increase the message count */
+ message__count[x]++;
+
+ /* Success */
+ return;
+ }
+
+
+ /*** Step 3 -- Attempt to optimize ***/
+
+ /* Limit number of messages to check */
+ k = message_num() / 4;
+
+ /* Limit number of messages to check */
+ if (k > MESSAGE_MAX / 32) k = MESSAGE_MAX / 32;
+
+ /* Check the last few messages (if any to count) */
+ for (i = message__next; k; k--)
+ {
+ u16b q;
+
+ cptr old;
+
+ /* Back up and wrap if needed */
+ if (i-- == 0) i = MESSAGE_MAX - 1;
+
+ /* Stop before oldest message */
+ if (i == message__last) break;
+
+ /* Extract "distance" from "head" */
+ q = (message__head + MESSAGE_BUF - message__ptr[i]) % MESSAGE_BUF;
+
+ /* Do not optimize over large distance */
+ if (q > MESSAGE_BUF / 2) continue;
+
+ /* Access the old string */
+ old = &message__buf[message__ptr[i]];
+
+ /* Compare */
+ if (!streq(old, str)) continue;
+
+ /* Get the next message index, advance */
+ x = message__next++;
+
+ /* Handle wrap */
+ if (message__next == MESSAGE_MAX) message__next = 0;
+
+ /* Kill last message if needed */
+ if (message__next == message__last) message__last++;
+
+ /* Handle wrap */
+ if (message__last == MESSAGE_MAX) message__last = 0;
+
+ /* Assign the starting address */
+ message__ptr[x] = message__ptr[i];
+ message__color[x] = color;
+ message__type[x] = type;
+ message__count[x] = 1;
+
+ /* Success */
+ return;
+ }
+
+
+ /*** Step 4 -- Ensure space before end of buffer ***/
+
+ /* Kill messages and Wrap if needed */
+ if (message__head + n + 1 >= MESSAGE_BUF)
+ {
+ /* Kill all "dead" messages */
+ for (i = message__last; TRUE; i++)
+ {
+ /* Wrap if needed */
+ if (i == MESSAGE_MAX) i = 0;
+
+ /* Stop before the new message */
+ if (i == message__next) break;
+
+ /* Kill "dead" messages */
+ if (message__ptr[i] >= message__head)
+ {
+ /* Track oldest message */
+ message__last = i + 1;
+ }
+ }
+
+ /* Wrap "tail" if needed */
+ if (message__tail >= message__head) message__tail = 0;
+
+ /* Start over */
+ message__head = 0;
+ }
+
+
+ /*** Step 5 -- Ensure space before next message ***/
+
+ /* Kill messages if needed */
+ if (message__head + n + 1 > message__tail)
+ {
+ /* Grab new "tail" */
+ message__tail = message__head + n + 1;
+
+ /* Advance tail while possible past first "nul" */
+ while (message__buf[message__tail - 1]) message__tail++;
+
+ /* Kill all "dead" messages */
+ for (i = message__last; TRUE; i++)
+ {
+ /* Wrap if needed */
+ if (i == MESSAGE_MAX) i = 0;
+
+ /* Stop before the new message */
+ if (i == message__next) break;
+
+ /* Kill "dead" messages */
+ if ((message__ptr[i] >= message__head) &&
+ (message__ptr[i] < message__tail))
+ {
+ /* Track oldest message */
+ message__last = i + 1;
+ }
+ }
+ }
+
+
+ /*** Step 6 -- Grab a new message index ***/
+
+ /* Get the next message index, advance */
+ x = message__next++;
+
+ /* Handle wrap */
+ if (message__next == MESSAGE_MAX) message__next = 0;
+
+ /* Kill last message if needed */
+ if (message__next == message__last) message__last++;
+
+ /* Handle wrap */
+ if (message__last == MESSAGE_MAX) message__last = 0;
+
+
+
+ /*** Step 7 -- Insert the message text ***/
+
+ /* Assign the starting address */
+ message__ptr[x] = message__head;
+ message__color[x] = color;
+ message__type[x] = type;
+ message__count[x] = 1;
+
+ /* Append the new part of the message */
+ for (i = 0; i < n; i++)
+ {
+ /* Copy the message */
+ message__buf[message__head + i] = str[i];
+ }
+
+ /* Terminate */
+ message__buf[message__head + i] = '\0';
+
+ /* Advance the "head" pointer */
+ message__head += n + 1;
+}
+
+
+
+/*
+* Hack -- flush
+*/
+static void msg_flush(int x)
+{
+ byte a = TERM_L_BLUE;
+
+ /* Hack -- fake monochrome */
+ if (!use_color) a = TERM_WHITE;
+
+ /* Pause for response */
+ Term_putstr(x, 0, -1, a, "-more-");
+
+ /* Get an acceptable keypress */
+ while (1)
+ {
+ int cmd = inkey();
+ if (quick_messages) break;
+ if ((cmd == ESCAPE) || (cmd == ' ')) break;
+ if ((cmd == '\n') || (cmd == '\r')) break;
+ bell();
+ }
+
+ /* Clear the line */
+ Term_erase(0, 0, 255);
+}
+
+/* Display a message */
+void display_message(int x, int y, int split, byte color, cptr t)
+{
+ int i = 0, j = 0;
+
+ while (i < split)
+ {
+ if (t[i] == '#')
+ {
+ if (t[i + 1] == '#')
+ {
+ Term_putstr(x + j, y, 1, color, "#");
+ i += 2;
+ j++;
+ }
+ else
+ {
+ color = color_char_to_attr(t[i + 1]);
+ i += 2;
+ }
+ }
+ else
+ {
+ Term_putstr(x + j, y, 1, color, t + i);
+ i++;
+ j++;
+ }
+ }
+}
+
+/*
+* Output a message to the top line of the screen.
+*
+* Break long messages into multiple pieces (40-72 chars).
+*
+* Allow multiple short messages to "share" the top line.
+*
+* Prompt the user to make sure he has a chance to read them.
+*
+* These messages are memorized for later reference (see above).
+*
+* We could do "Term_fresh()" to provide "flicker" if needed.
+*
+* The global "msg_flag" variable can be cleared to tell us to
+* "erase" any "pending" messages still on the screen.
+*
+* XXX XXX XXX Note that we must be very careful about using the
+* "msg_print()" functions without explicitly calling the special
+* "msg_print(NULL)" function, since this may result in the loss
+* of information if the screen is cleared, or if anything is
+* displayed on the top line.
+*
+* XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
+* even if no messages are pending. This is probably a hack.
+*/
+void cmsg_print(byte color, cptr msg)
+{
+ static int p = 0;
+
+ int n;
+
+ char *t;
+
+ char buf[1024];
+
+ int lim = Term->wid - 8;
+
+
+ /* Hack -- Reset */
+ if (!msg_flag) p = 0;
+
+ /* Message Length */
+ n = (msg ? strlen(msg) : 0);
+
+ /* Hack -- flush when requested or needed */
+ if (p && (!msg || ((p + n) > lim)))
+ {
+ /* Flush */
+ msg_flush(p);
+
+ /* Forget it */
+ msg_flag = FALSE;
+
+ /* Reset */
+ p = 0;
+ }
+
+
+ /* No message */
+ if (!msg) return;
+
+ /* Paranoia */
+ if (n > 1000) return;
+
+
+ /* Memorize the message */
+ if (character_generated) message_add(MESSAGE_MSG, msg, color);
+
+ /* Handle "auto_more" */
+ if (auto_more)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE);
+
+ /* Force window update */
+ window_stuff();
+
+ /* Done */
+ return;
+ }
+
+
+ /* Copy it */
+ strcpy(buf, msg);
+
+ /* Analyze the buffer */
+ t = buf;
+
+ /* Split message */
+ while (n > lim)
+ {
+ char oops;
+
+ int check, split;
+
+ /* Default split */
+ split = lim;
+
+ /* Find the "best" split point */
+ for (check = 40; check < lim; check++)
+ {
+ /* Found a valid split point */
+ if (t[check] == ' ') split = check;
+ }
+
+ /* Save the split character */
+ oops = t[split];
+
+ /* Split the message */
+ t[split] = '\0';
+
+ /* Display part of the message */
+ display_message(0, 0, split, color, t);
+
+ /* Flush it */
+ msg_flush(split + 1);
+
+ /* Memorize the piece */
+ /* if (character_generated) message_add(t); */
+
+ /* Restore the split character */
+ t[split] = oops;
+
+ /* Insert a space */
+ t[--split] = ' ';
+
+ /* Prepare to recurse on the rest of "buf" */
+ t += split;
+ n -= split;
+ }
+
+
+ /* Display the tail of the message */
+ display_message(p, 0, n, color, t);
+
+ /* Memorize the tail */
+ /* if (character_generated) message_add(t); */
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE);
+
+ /* Remember the message */
+ msg_flag = TRUE;
+
+ /* Remember the position */
+ p += n + 1;
+
+ /* Optional refresh */
+ if (fresh_message) Term_fresh();
+}
+
+/* Hack -- for compatibility and easy sake */
+void msg_print(cptr msg)
+{
+ cmsg_print(TERM_WHITE, msg);
+}
+
+
+/*
+ * Hack -- prevent "accidents" in "screen_save()" or "screen_load()"
+ */
+static int screen_depth = 0;
+
+
+/*
+ * Save the screen, and increase the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_load()".
+ */
+void screen_save(void)
+{
+ /* Hack -- Flush messages */
+ msg_print(NULL);
+
+ /* Save the screen (if legal) */
+ if (screen_depth++ == 0) Term_save();
+
+ /* Increase "icky" depth */
+ character_icky++;
+}
+
+
+/*
+ * Load the screen, and decrease the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_save()".
+ */
+void screen_load(void)
+{
+ /* Hack -- Flush messages */
+ msg_print(NULL);
+
+ /* Load the screen (if legal) */
+ if (--screen_depth == 0) Term_load();
+
+ /* Decrease "icky" depth */
+ character_icky--;
+}
+
+
+/*
+* Display a formatted message, using "vstrnfmt()" and "msg_print()".
+*/
+void msg_format(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ cmsg_print(TERM_WHITE, buf);
+}
+
+void cmsg_format(byte color, cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ cmsg_print(color, buf);
+}
+
+
+
+/*
+* Display a string on the screen using an attribute.
+*
+* At the given location, using the given attribute, if allowed,
+* add the given string. Do not clear the line.
+*/
+void c_put_str(byte attr, cptr str, int row, int col)
+{
+ /* Hack -- fake monochrome */
+ if (!use_color) attr = TERM_WHITE;
+
+ /* Position cursor, Dump the attr/text */
+ Term_putstr(col, row, -1, attr, str);
+}
+
+/*
+* As above, but in "white"
+*/
+void put_str(cptr str, int row, int col)
+{
+ /* Spawn */
+ Term_putstr(col, row, -1, TERM_WHITE, str);
+}
+
+
+
+/*
+* Display a string on the screen using an attribute, and clear
+* to the end of the line.
+*/
+void c_prt(byte attr, cptr str, int row, int col)
+{
+ /* Hack -- fake monochrome */
+ if (!use_color) attr = TERM_WHITE;
+
+ /* Clear line, position cursor */
+ Term_erase(col, row, 255);
+
+ /* Dump the attr/text */
+ Term_addstr( -1, attr, str);
+}
+
+/*
+* As above, but in "white"
+*/
+void prt(cptr str, int row, int col)
+{
+ /* Spawn */
+ c_prt(TERM_WHITE, str, row, col);
+}
+
+
+
+/*
+ * Print some (colored) text to the screen at the current cursor position,
+ * automatically "wrapping" existing text (at spaces) when necessary to
+ * avoid placing any text into the last column, and clearing every line
+ * before placing any text in that line. Also, allow "newline" to force
+ * a "wrap" to the next line. Advance the cursor as needed so sequential
+ * calls to this function will work correctly.
+ *
+ * Once this function has been called, the cursor should not be moved
+ * until all the related "text_out()" calls to the window are complete.
+ *
+ * This function will correctly handle any width up to the maximum legal
+ * value of 256, though it works best for a standard 80 character width.
+ */
+void text_out_to_screen(byte a, cptr str)
+{
+ int x, y;
+
+ int wid, h;
+
+ int wrap;
+
+ cptr s;
+
+
+ /* Obtain the size */
+ (void)Term_get_size(&wid, &h);
+
+ /* Obtain the cursor */
+ (void)Term_locate(&x, &y);
+
+ /* Use special wrapping boundary? */
+ if ((text_out_wrap > 0) && (text_out_wrap < wid))
+ wrap = text_out_wrap;
+ else
+ wrap = wid;
+
+ /* Process the string */
+ for (s = str; *s; s++)
+ {
+ char ch;
+
+ /* Force wrap */
+ if (*s == '\n')
+ {
+ /* Wrap */
+ x = text_out_indent;
+ y++;
+
+ /* Clear line, move cursor */
+ Term_erase(x, y, 255);
+
+ continue;
+ }
+
+ /* Clean up the char */
+ ch = (isprint((unsigned char) * s) ? *s : ' ');
+
+ /* Wrap words as needed */
+ if ((x >= wrap - 1) && (ch != ' '))
+ {
+ int i, n = 0;
+
+ byte av[256];
+ char cv[256];
+
+ /* Wrap word */
+ if (x < wrap)
+ {
+ /* Scan existing text */
+ for (i = wrap - 2; i >= 0; i--)
+ {
+ /* Grab existing attr/char */
+ Term_what(i, y, &av[i], &cv[i]);
+
+ /* Break on space */
+ if (cv[i] == ' ') break;
+
+ /* Track current word */
+ n = i;
+ }
+ }
+
+ /* Special case */
+ if (n == 0) n = wrap;
+
+ /* Clear line */
+ Term_erase(n, y, 255);
+
+ /* Wrap */
+ x = text_out_indent;
+ y++;
+
+ /* Clear line, move cursor */
+ Term_erase(x, y, 255);
+
+ /* Wrap the word (if any) */
+ for (i = n; i < wrap - 1; i++)
+ {
+ /* Dump */
+ Term_addch(av[i], cv[i]);
+
+ /* Advance (no wrap) */
+ if (++x > wrap) x = wrap;
+ }
+ }
+
+ /* Dump */
+ Term_addch(a, ch);
+
+ /* Advance */
+ if (++x > wrap) x = wrap;
+ }
+}
+
+
+/*
+ * Write text to the given file and apply line-wrapping.
+ *
+ * Hook function for text_out(). Make sure that text_out_file points
+ * to an open text-file.
+ *
+ * Long lines will be wrapped at text_out_wrap, or at column 75 if that
+ * is not set; or at a newline character.
+ *
+ * You must be careful to end all file output with a newline character
+ * to "flush" the stored line position.
+ */
+void text_out_to_file(byte a, cptr str)
+{
+ /* Current position on the line */
+ static int pos = 0;
+
+ /* Wrap width */
+ int wrap = (text_out_wrap ? text_out_wrap : 75);
+
+ /* Current location within "str" */
+ cptr s = str;
+
+ /* Unused parameter */
+ (void)a;
+
+ /* Process the string */
+ while (*s)
+ {
+ char ch;
+ int n = 0;
+ int len = wrap - pos;
+ int l_space = 0;
+
+ /* If we are at the start of the line... */
+ if (pos == 0)
+ {
+ int i;
+
+ /* Output the indent */
+ for (i = 0; i < text_out_indent; i++)
+ {
+ fputc(' ', text_out_file);
+ pos++;
+ }
+ }
+
+ /* Find length of line up to next newline or end-of-string */
+ while ((n < len) && !((s[n] == '\n') || (s[n] == '\0')))
+ {
+ /* Mark the most recent space in the string */
+ if (s[n] == ' ') l_space = n;
+
+ /* Increment */
+ n++;
+ }
+
+ /* If we have encountered no spaces */
+ if ((l_space == 0) && (n == len))
+ {
+ /* If we are at the start of a new line */
+ if (pos == text_out_indent)
+ {
+ len = n;
+ }
+ else
+ {
+ /* Begin a new line */
+ fputc('\n', text_out_file);
+
+ /* Reset */
+ pos = 0;
+
+ continue;
+ }
+ }
+ else
+ {
+ /* Wrap at the newline */
+ if ((s[n] == '\n') || (s[n] == '\0')) len = n;
+
+ /* Wrap at the last space */
+ else len = l_space;
+ }
+
+ /* Write that line to file */
+ for (n = 0; n < len; n++)
+ {
+ /* Ensure the character is printable */
+ ch = (isprint(s[n]) ? s[n] : ' ');
+
+ /* Write out the character */
+ fputc(ch, text_out_file);
+
+ /* Increment */
+ pos++;
+ }
+
+ /* Move 's' past the stuff we've written */
+ s += len;
+
+ /* If we are at the end of the string, end */
+ if (*s == '\0') return;
+
+ /* Skip newlines */
+ if (*s == '\n') s++;
+
+ /* Begin a new line */
+ fputc('\n', text_out_file);
+
+ /* Reset */
+ pos = 0;
+
+ /* Skip whitespace */
+ while (*s == ' ') s++;
+ }
+
+ /* We are done */
+ return;
+}
+
+
+/*
+ * Output text to the screen or to a file depending on the selected
+ * text_out hook.
+ */
+void text_out(cptr str)
+{
+ text_out_c(TERM_WHITE, str);
+}
+
+
+/*
+ * Output text to the screen (in color) or to a file depending on the
+ * selected hook.
+ */
+void text_out_c(byte a, cptr str)
+{
+ text_out_hook(a, str);
+}
+
+
+
+
+/*
+* Clear part of the screen
+*/
+void clear_from(int row)
+{
+ int y;
+
+ /* Erase requested rows */
+ for (y = row; y < Term->hgt; y++)
+ {
+ /* Erase part of the screen */
+ Term_erase(0, y, 255);
+ }
+}
+
+/*
+ * Try to find a matching command completion.
+ * Note that this is not so friendly since it doesn't give
+ * a list of possible completions.
+ *
+ * First arg is the string to be completed, second is it's length,
+ * third is it's maximum length.
+ */
+static int complete_where = 0;
+static char complete_buf[100];
+static int complete_command(char *buf, int clen, int mlen)
+{
+ int i, j = 1, max = clen;
+ bool gotone = FALSE;
+
+ /* Forget the characters after the end of the string. */
+ complete_buf[clen] = '\0';
+
+ for (i = 0; i < cli_total; i++)
+ {
+ cli_comm *cli_ptr = cli_info + i;
+
+ if (!strncmp(cli_ptr->comm, complete_buf, clen))
+ {
+ Term_erase(0, j, 80);
+ Term_putstr(0, j++, -1, TERM_WHITE, cli_ptr->comm);
+
+ /* For the first match, copy the whole string to buf. */
+ if (!gotone)
+ {
+ sprintf(buf, "%.*s", mlen, cli_ptr->comm);
+ gotone = TRUE;
+ }
+ /* For later matches, simply notice how much of buf it
+ * matches. */
+ else
+ {
+ for (max = clen; max < mlen; max++)
+ {
+ if (cli_ptr->comm[max] == '\0') break;
+ if (cli_ptr->comm[max] != buf[max]) break;
+ }
+ if (max < mlen) buf[max] = '\0';
+ }
+ }
+ }
+
+ return strlen(buf) + 1;
+}
+
+
+/*
+* Get some input at the cursor location.
+* Assume the buffer is initialized to a default string.
+* Note that this string is often "empty" (see below).
+* The default buffer is displayed in yellow until cleared.
+* Pressing RETURN right away accepts the default entry.
+* Normal chars clear the default and append the char.
+* Backspace clears the default or deletes the final char.
+* ESCAPE clears the buffer and the window and returns FALSE.
+* RETURN accepts the current buffer contents and returns TRUE.
+*/
+bool askfor_aux_complete = FALSE;
+bool askfor_aux(char *buf, int len)
+{
+ int y, x;
+
+ int i = 0;
+
+ int k = 0;
+
+ bool done = FALSE;
+
+
+ /* Locate the cursor */
+ Term_locate(&x, &y);
+
+
+ /* Paranoia -- check len */
+ if (len < 1) len = 1;
+
+ /* Paranoia -- check column */
+ if ((x < 0) || (x >= 80)) x = 0;
+
+ /* Restrict the length */
+ if (x + len > 80) len = 80 - x;
+
+
+ /* Paranoia -- Clip the default entry */
+ buf[len] = '\0';
+
+
+ /* Display the default answer */
+ Term_erase(x, y, len);
+ Term_putstr(x, y, -1, TERM_YELLOW, buf);
+
+ if (askfor_aux_complete)
+ {
+ screen_save();
+ complete_where = 0;
+ strncpy(complete_buf, buf, 100);
+ }
+
+ /* Process input */
+ while (!done)
+ {
+ /* Place cursor */
+ Term_gotoxy(x + k, y);
+
+ /* Get a key */
+ i = inkey();
+
+ /* Analyze the key */
+ switch (i)
+ {
+ case ESCAPE:
+ k = 0;
+ done = TRUE;
+ break;
+
+ case '\n':
+ case '\r':
+ k = strlen(buf);
+ done = TRUE;
+ break;
+
+ case '\t':
+ if (askfor_aux_complete && k)
+ {
+ screen_load();
+ screen_save();
+ k = complete_command(buf, k, len);
+ }
+ else
+ {
+ bell();
+ }
+
+ case 0x7F:
+ case '\010':
+ if (k > 0) k--;
+ strncpy(complete_buf, buf, k);
+ break;
+
+ default:
+ if ((k < len) && (isprint(i)))
+ {
+ buf[k++] = i;
+ strncpy(complete_buf, buf, k);
+ }
+ else
+ {
+ bell();
+ }
+ break;
+ }
+
+ /* Terminate */
+ buf[k] = '\0';
+
+ /* Update the entry */
+ Term_erase(x, y, len);
+ Term_putstr(x, y, -1, TERM_WHITE, buf);
+ }
+
+ if (askfor_aux_complete)
+ {
+ screen_load();
+ }
+
+ /* Aborted */
+ if (i == ESCAPE) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Get a string from the user
+*
+* The "prompt" should take the form "Prompt: "
+*
+* Note that the initial contents of the string is used as
+* the default response, so be sure to "clear" it if needed.
+*
+* We clear the input, and return FALSE, on "ESCAPE".
+*/
+bool get_string(cptr prompt, char *buf, int len)
+{
+ bool res;
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display prompt */
+ prt(prompt, 0, 0);
+
+ /* Ask the user for a string */
+ res = askfor_aux(buf, len);
+
+ /* Clear prompt */
+ prt("", 0, 0);
+
+ /* Result */
+ return (res);
+}
+
+
+/*
+* Verify something with the user
+*
+* The "prompt" should take the form "Query? "
+*
+* Note that "[y/n]" is appended to the prompt.
+*/
+bool get_check(cptr prompt)
+{
+ int i;
+
+ char buf[80];
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Hack -- Build a "useful" prompt */
+ strnfmt(buf, 78, "%.70s[y/n] ", prompt);
+
+ /* Prompt for it */
+ prt(buf, 0, 0);
+
+ /* Get an acceptable answer */
+ while (TRUE)
+ {
+ i = inkey();
+ if (quick_messages) break;
+ if (i == ESCAPE) break;
+ if (strchr("YyNn", i)) break;
+ bell();
+ }
+
+ /* Erase the prompt */
+ prt("", 0, 0);
+
+ /* Normal negation */
+ if ((i != 'Y') && (i != 'y')) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Prompts for a keypress
+*
+* The "prompt" should take the form "Command: "
+*
+* Returns TRUE unless the character is "Escape"
+*/
+bool get_com(cptr prompt, char *command)
+{
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display a prompt */
+ prt(prompt, 0, 0);
+
+ /* Get a key */
+ *command = inkey();
+
+ /* Clear the prompt */
+ prt("", 0, 0);
+
+ /* Handle "cancel" */
+ if (*command == ESCAPE) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Request a "quantity" from the user
+*
+* Hack -- allow "command_arg" to specify a quantity
+*/
+s32b get_quantity(cptr prompt, s32b max)
+{
+ s32b amt;
+ int aamt;
+
+ char tmp[80];
+
+ char buf[80];
+
+
+ /* Use "command_arg" */
+ if (command_arg)
+ {
+ /* Extract a number */
+ amt = command_arg;
+
+ /* Clear "command_arg" */
+ command_arg = 0;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Use it */
+ return (amt);
+ }
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Get the item index */
+ if ((max != 1) && repeat_pull(&aamt))
+ {
+ amt = aamt;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Enforce the minimum */
+ if (amt < 0) amt = 0;
+
+ /* Use it */
+ return (amt);
+ }
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Build a prompt if needed */
+ if (!prompt)
+ {
+ /* Build a prompt */
+ sprintf(tmp, "Quantity (1-%ld): ", max);
+
+ /* Use that prompt */
+ prompt = tmp;
+ }
+
+
+ /* Default to one */
+ amt = 1;
+
+ /* Build the default */
+ sprintf(buf, "%ld", amt);
+
+ /* Ask for a quantity */
+ if (!get_string(prompt, buf, 9)) return (0);
+
+ /* Extract a number */
+ amt = atoi(buf);
+
+ /* A letter means "all" */
+ if (isalpha(buf[0])) amt = max;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Enforce the minimum */
+ if (amt < 0) amt = 0;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ if (amt) repeat_push(amt);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Return the result */
+ return (amt);
+}
+
+
+/*
+* Pause for user response XXX XXX XXX
+*/
+void pause_line(int row)
+{
+ int i;
+ prt("", row, 0);
+ put_str("[Press any key to continue]", row, 23);
+ i = inkey();
+ prt("", row, 0);
+}
+
+
+/*
+* Hack -- special buffer to hold the action of the current keymap
+*/
+static char request_command_buffer[256];
+
+/*
+* Mega-Hack -- characters for which keymaps should be ignored in
+* request_command(). This MUST have at least twice as many characters as
+* there are building actions in the actions[] array in store_info_type.
+*/
+#define MAX_IGNORE_KEYMAPS 12
+char request_command_ignore_keymaps[MAX_IGNORE_KEYMAPS];
+
+/*
+* Mega-Hack -- flag set by do_cmd_{inven,equip}() to allow keymaps in
+* auto-command mode.
+*/
+bool request_command_inven_mode = FALSE;
+
+
+/*
+* Request a command from the user.
+*
+* Sets p_ptr->command_cmd, p_ptr->command_dir, p_ptr->command_rep,
+* p_ptr->command_arg. May modify p_ptr->command_new.
+*
+* Note that "caret" ("^") is treated specially, and is used to
+* allow manual input of control characters. This can be used
+* on many machines to request repeated tunneling (Ctrl-H) and
+* on the Macintosh to request "Control-Caret".
+*
+* Note that "backslash" is treated specially, and is used to bypass any
+* keymap entry for the following character. This is useful for macros.
+*
+* Note that this command is used both in the dungeon and in
+* stores, and must be careful to work in both situations.
+*
+* Note that "p_ptr->command_new" may not work any more. XXX XXX XXX
+*/
+void request_command(int shopping)
+{
+ int i;
+
+ s16b cmd;
+ char cmd_char;
+
+ int mode;
+
+ cptr act;
+
+
+ /* Roguelike */
+ if (rogue_like_commands)
+ {
+ mode = KEYMAP_MODE_ROGUE;
+ }
+
+ /* Original */
+ else
+ {
+ mode = KEYMAP_MODE_ORIG;
+ }
+
+
+ /* No command yet */
+ command_cmd = 0;
+
+ /* No "argument" yet */
+ command_arg = 0;
+
+ /* No "direction" yet */
+ command_dir = 0;
+
+
+ /* Get command */
+ while (1)
+ {
+ /* Hack -- auto-commands */
+ if (command_new)
+ {
+ /* Flush messages */
+ msg_print(NULL);
+
+ /* Use auto-command */
+ cmd = command_new;
+
+ /* Forget it */
+ command_new = 0;
+
+ /* Hack - bypass keymaps, unless asked not to */
+ if (!inkey_next && !request_command_inven_mode)
+ {
+ inkey_next = "";
+ }
+
+ /* Mega-Hack -- turn off this flag immediately */
+ request_command_inven_mode = FALSE;
+ }
+
+ /* Get a keypress in "command" mode */
+ else
+ {
+ /* Hack -- no flush needed */
+ msg_flag = FALSE;
+
+ /* Activate "command mode" */
+ inkey_flag = TRUE;
+
+ /* Get a command */
+ cmd = inkey();
+ }
+
+ /* Clear top line */
+ prt("", 0, 0);
+
+
+ /* Command Count */
+ if (cmd == '0')
+ {
+ int old_arg = command_arg;
+
+ /* Reset */
+ command_arg = 0;
+
+ /* Begin the input */
+ prt("Count: ", 0, 0);
+
+ /* Get a command count */
+ while (1)
+ {
+ /* Get a new keypress */
+ cmd = inkey();
+
+ /* Simple editing (delete or backspace) */
+ if ((cmd == 0x7F) || (cmd == KTRL('H')))
+ {
+ /* Delete a digit */
+ command_arg = command_arg / 10;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Actual numeric data */
+ else if (cmd >= '0' && cmd <= '9')
+ {
+ /* Stop count at 9999 */
+ if (command_arg >= 1000)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ command_arg = 9999;
+ }
+
+ /* Increase count */
+ else
+ {
+ /* Incorporate that digit */
+ command_arg = command_arg * 10 + D2I(cmd);
+ }
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Exit on "unusable" input */
+ else
+ {
+ break;
+ }
+ }
+
+ /* Hack -- Handle "zero" */
+ if (command_arg == 0)
+ {
+ /* Default to 99 */
+ command_arg = 99;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Hack -- Handle "old_arg" */
+ if (old_arg != 0)
+ {
+ /* Restore old_arg */
+ command_arg = old_arg;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Hack -- white-space means "enter command now" */
+ if ((cmd == ' ') || (cmd == '\n') || (cmd == '\r'))
+ {
+ /* Get a real command */
+ bool temp = get_com("Command: ", &cmd_char);
+ cmd = cmd_char;
+
+ if (!temp)
+ {
+ /* Clear count */
+ command_arg = 0;
+
+ /* Continue */
+ continue;
+ }
+ }
+ }
+
+
+ /* Allow "keymaps" to be bypassed */
+ if (cmd == '\\')
+ {
+ /* Get a real command */
+ (void)get_com("Command: ", &cmd_char);
+
+ cmd = cmd_char;
+
+ /* Hack -- bypass keymaps */
+ if (!inkey_next) inkey_next = "";
+ }
+
+
+ /* Allow "control chars" to be entered */
+ if (cmd == '^')
+ {
+ /* Get a new command and controlify it */
+ if (get_com("Control: ", &cmd_char)) cmd = KTRL(cmd_char);
+ else cmd = 0;
+ }
+
+
+ /* Look up applicable keymap */
+ act = keymap_act[mode][(byte)(cmd)];
+
+ /* Mega-Hack -- Ignore certain keymaps */
+ if (shopping && cmd > 0)
+ {
+ for (i = 0; i < MAX_IGNORE_KEYMAPS; i++)
+ if (cmd == request_command_ignore_keymaps[i])
+ {
+ act = NULL;
+ break;
+ }
+ }
+
+ /* Apply keymap if not inside a keymap already */
+ if (act && !inkey_next)
+ {
+ /* Install the keymap (limited buffer size) */
+ strnfmt(request_command_buffer, 256, "%s", act);
+
+ /* Start using the buffer */
+ inkey_next = request_command_buffer;
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Paranoia */
+ if (!cmd) continue;
+
+
+ /* Use command */
+ command_cmd = cmd;
+
+ /* Done */
+ break;
+ }
+
+ /* Hack -- Auto-repeat certain commands */
+ if (always_repeat && (command_arg <= 0))
+ {
+ /* Hack -- auto repeat certain commands */
+ if (strchr("TBDoc+", command_cmd))
+ {
+ /* Repeat 99 times */
+ command_arg = 99;
+ }
+ }
+
+ /* Hack -- Scan equipment */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ cptr s;
+
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* No inscription */
+ if (!o_ptr->note) continue;
+
+ /* Obtain the inscription */
+ s = quark_str(o_ptr->note);
+
+ /* Find a '^' */
+ s = strchr(s, '^');
+
+ /* Process preventions */
+ while (s)
+ {
+ /* Check the "restriction" character */
+ if ((s[1] == command_cmd) || (s[1] == '*'))
+ {
+ /* Hack -- Verify command */
+ if (!get_check("Are you sure? "))
+ {
+ /* Hack -- Use space */
+ command_cmd = ' ';
+ }
+ }
+
+ /* Find another '^' */
+ s = strchr(s + 1, '^');
+ }
+ }
+
+
+ /* Hack -- erase the message line. */
+ prt("", 0, 0);
+}
+
+
+
+
+/*
+ * Check a char for "vowel-hood"
+ */
+bool is_a_vowel(int ch)
+{
+ switch (ch)
+ {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'A':
+ case 'E':
+ case 'I':
+ case 'O':
+ case 'U':
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+
+#if 0
+
+/*
+* Replace the first instance of "target" in "buf" with "insert"
+* If "insert" is NULL, just remove the first instance of "target"
+* In either case, return TRUE if "target" is found.
+*
+* XXX Could be made more efficient, especially in the
+* case where "insert" is smaller than "target".
+*/
+static bool insert_str(char *buf, cptr target, cptr insert)
+{
+ int i, len;
+ int b_len, t_len, i_len;
+
+ /* Attempt to find the target (modify "buf") */
+ buf = strstr(buf, target);
+
+ /* No target found */
+ if (!buf) return (FALSE);
+
+ /* Be sure we have an insertion string */
+ if (!insert) insert = "";
+
+ /* Extract some lengths */
+ t_len = strlen(target);
+ i_len = strlen(insert);
+ b_len = strlen(buf);
+
+ /* How much "movement" do we need? */
+ len = i_len - t_len;
+
+ /* We need less space (for insert) */
+ if (len < 0)
+ {
+ for (i = t_len; i < b_len; ++i) buf[i + len] = buf[i];
+ }
+
+ /* We need more space (for insert) */
+ else if (len > 0)
+ {
+ for (i = b_len - 1; i >= t_len; --i) buf[i + len] = buf[i];
+ }
+
+ /* If movement occured, we need a new terminator */
+ if (len) buf[b_len + len] = '\0';
+
+ /* Now copy the insertion string */
+ for (i = 0; i < i_len; ++i) buf[i] = insert[i];
+
+ /* Successful operation */
+ return (TRUE);
+}
+
+
+#endif
+
+
+/*
+ * GH
+ * Called from cmd4.c and a few other places. Just extracts
+ * a direction from the keymap for ch (the last direction,
+ * in fact) byte or char here? I'm thinking that keymaps should
+ * generally only apply to single keys, which makes it no more
+ * than 128, so a char should suffice... but keymap_act is 256...
+ */
+int get_keymap_dir(char ch)
+{
+ int d = 0;
+
+ int mode;
+
+ cptr act;
+
+ cptr s;
+
+
+ /* Already a direction? */
+ if (isdigit(ch))
+ {
+ d = D2I(ch);
+ }
+ else
+ {
+ /* Roguelike */
+ if (rogue_like_commands)
+ {
+ mode = KEYMAP_MODE_ROGUE;
+ }
+
+ /* Original */
+ else
+ {
+ mode = KEYMAP_MODE_ORIG;
+ }
+
+ /* Extract the action (if any) */
+ act = keymap_act[mode][(byte)(ch)];
+
+ /* Analyze */
+ if (act)
+ {
+ /* Convert to a direction */
+ for (s = act; *s; ++s)
+ {
+ /* Use any digits in keymap */
+ if (isdigit(*s)) d = D2I(*s);
+ }
+ }
+ }
+
+ /* Paranoia */
+ if (d == 5) d = 0;
+
+ /* Return direction */
+ return (d);
+}
+
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+#define REPEAT_MAX 20
+
+/* Number of chars saved */
+static int repeat__cnt = 0;
+
+/* Current index */
+static int repeat__idx = 0;
+
+/* Saved "stuff" */
+static int repeat__key[REPEAT_MAX];
+
+
+void repeat_push(int what)
+{
+ /* Too many keys */
+ if (repeat__cnt == REPEAT_MAX) return;
+
+ /* Push the "stuff" */
+ repeat__key[repeat__cnt++] = what;
+
+ /* Prevents us from pulling keys */
+ ++repeat__idx;
+}
+
+
+bool repeat_pull(int *what)
+{
+ /* All out of keys */
+ if (repeat__idx == repeat__cnt) return (FALSE);
+
+ /* Grab the next key, advance */
+ *what = repeat__key[repeat__idx++];
+
+ /* Success */
+ return (TRUE);
+}
+
+void repeat_check(void)
+{
+ int what;
+
+ /* Ignore some commands */
+ if (command_cmd == ESCAPE) return;
+ if (command_cmd == ' ') return;
+ if (command_cmd == '\r') return;
+ if (command_cmd == '\n') return;
+
+ /* Repeat Last Command */
+ if (command_cmd == 'n')
+ {
+ /* Reset */
+ repeat__idx = 0;
+
+ /* Get the command */
+ if (repeat_pull(&what))
+ {
+ /* Save the command */
+ command_cmd = what;
+ }
+ }
+
+ /* Start saving new command */
+ else
+ {
+ /* Reset */
+ repeat__cnt = 0;
+ repeat__idx = 0;
+
+ what = command_cmd;
+
+ /* Save this command */
+ repeat_push(what);
+ }
+}
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+/*
+ * Read a number at a specific location on the screen
+ *
+ * Allow numbers of any size and save the last keypress.
+ */
+u32b get_number(u32b def, u32b max, int y, int x, char *cmd)
+{
+ u32b res = def;
+
+ /* Player has not typed anything yet */
+ bool no_keys = TRUE;
+
+ /* Begin the input with default */
+ prt(format("%lu", def), y, x);
+
+ /* Get a command count */
+ while (1)
+ {
+ /* Get a new keypress */
+ *cmd = inkey();
+
+ /* Simple editing (delete or backspace) */
+ if ((*cmd == 0x7F) || (*cmd == KTRL('H')))
+ {
+ /* Override the default */
+ no_keys = FALSE;
+
+ /* Delete a digit */
+ res = res / 10;
+
+ prt(format("%lu", res), y, x);
+ }
+
+ /* Actual numeric data */
+ else if (*cmd >= '0' && *cmd <= '9')
+ {
+ /* Override the default */
+ if (no_keys)
+ {
+ no_keys = FALSE;
+ res = 0;
+ }
+
+ /* Don't overflow */
+ if (((u32b)(0 - 1) - D2I(*cmd)) / 10 < res)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ res = (max + 1 == 0) ? (u32b)(0 - 1) : max;
+ }
+
+ /* Stop count at maximum */
+ else if (res * 10 + D2I(*cmd) > max)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ res = max;
+ }
+
+ /* Increase count */
+ else
+ {
+ /* Incorporate that digit */
+ res = res * 10 + D2I(*cmd);
+ }
+
+ /* Show current count */
+ prt(format("%lu", res), y, x);
+ }
+
+ /* Escape cancels */
+ else if (*cmd == ESCAPE)
+ {
+ res = 0;
+ break;
+ }
+
+ /* Exit on "unusable" input */
+ else
+ {
+ break;
+ }
+ }
+
+ return res;
+}
+
+/*
+ * Allow the user to select multiple items without pressing '0'
+ */
+void get_count(int number, int max)
+{
+ char cmd;
+
+ /* Use the default */
+ command_arg = number;
+
+ /* Hack -- Optional flush */
+ if (flush_command) flush();
+
+ /* Clear top line */
+ prt("", 0, 0);
+
+ /* Begin the input */
+ prt("How many?", 0, 0);
+
+ /* Actually get a number */
+ command_arg = get_number(command_arg, max, 0, 10, &cmd);
+
+ prt("", 0, 0);
+}
+
+byte count_bits(u32b array)
+{
+ byte k = 0, i;
+
+ if (array)
+ for (i = 0; i < 32; i++)
+ if (array & (1 << i)) k++;
+
+ return k;
+}
+
+/* Return the lowered string */
+void strlower(char *buf)
+{
+ u16b i;
+
+ for (i = 0; (buf[i] != 0) && (i < 256) ;i++)
+ {
+ if (isupper(buf[i])) buf[i] = tolower(buf[i]);
+ }
+}
+
+/*
+ * Given monster name as string, return the index in r_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -GSN-
+ */
+
+int test_monster_name(cptr name)
+{
+ int i;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+ cptr mon_name = r_name + r_ptr->name;
+
+ /* If name matches, give us the number */
+ if (stricmp(name, mon_name) == 0) return (i);
+ }
+ return (0);
+}
+int test_mego_name(cptr name)
+{
+ int i;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_re_idx; i++)
+ {
+ monster_ego *re_ptr = &re_info[i];
+ cptr mon_name = re_name + re_ptr->name;
+
+ /* If name matches, give us the number */
+ if (stricmp(name, mon_name) == 0) return (i);
+ }
+ return (0);
+}
+
+/*
+ * Given item name as string, return the index in k_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -DG-
+ */
+
+int test_item_name(cptr name)
+{
+ int i;
+
+ /* Scan the items */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+ cptr obj_name = k_name + k_ptr->name;
+
+ /* If name matches, give us the number */
+ if (stricmp(name, obj_name) == 0) return (i);
+ }
+ return (0);
+}
+
+/*
+ * Break scalar time
+ */
+s32b bst(s32b what, s32b t)
+{
+ s32b turns = t + (10 * DAY_START);
+
+ switch (what)
+ {
+ case MINUTE:
+ return ((turns / 10 / MINUTE) % 60);
+ case HOUR:
+ return (turns / 10 / (HOUR) % 24);
+ case DAY:
+ return (turns / 10 / (DAY) % 365);
+ case YEAR:
+ return (turns / 10 / (YEAR));
+ default:
+ return (0);
+ }
+}
+
+cptr get_month_name(int day, bool full, bool compact)
+{
+ int i = 8;
+ static char buf[40];
+
+ /* Find the period name */
+ while ((i > 0) && (day < month_day[i]))
+ {
+ i--;
+ }
+
+ switch (i)
+ {
+ /* Yestare/Mettare */
+ case 0:
+ case 8:
+ {
+ char buf2[20];
+
+ sprintf(buf2, get_day(day + 1));
+ if (full) sprintf(buf, "%s (%s day)", month_name[i], buf2);
+ else sprintf(buf, "%s", month_name[i]);
+ break;
+ }
+ /* 'Normal' months + Enderi */
+ default:
+ {
+ char buf2[20];
+ char buf3[20];
+
+ sprintf(buf2, get_day(day + 1 - month_day[i]));
+ sprintf(buf3, get_day(day + 1));
+
+ if (full) sprintf(buf, "%s day of %s (%s day)", buf2, month_name[i], buf3);
+ else if (compact) sprintf(buf, "%s day of %s", buf2, month_name[i]);
+ else sprintf(buf, "%s %s", buf2, month_name[i]);
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+cptr get_day(int day)
+{
+ static char buf[20];
+ cptr p = "th";
+
+ if ((day / 10) == 1) ;
+ else if ((day % 10) == 1) p = "st";
+ else if ((day % 10) == 2) p = "nd";
+ else if ((day % 10) == 3) p = "rd";
+
+ sprintf(buf, "%d%s", day, p);
+ return (buf);
+}
+
+cptr get_player_race_name(int pr, int ps)
+{
+ static char buf[50];
+
+ if (ps)
+ {
+ if (race_mod_info[ps].place) sprintf(buf, "%s %s", race_info[pr].title + rp_name, race_mod_info[ps].title + rmp_name);
+ else sprintf(buf, "%s %s", race_mod_info[ps].title + rmp_name, race_info[pr].title + rp_name);
+ }
+ else
+ {
+ sprintf(buf, "%s", race_info[pr].title + rp_name);
+ }
+
+ return (buf);
+}
+
+#ifdef SUPPORT_GAMMA
+
+/* Table of gamma values */
+byte gamma_table[256];
+
+/* Table of ln(x / 256) * 256 for x going from 0 -> 255 */
+static const s16b gamma_helper[256] =
+{
+ 0, -1420, -1242, -1138, -1065, -1007, -961, -921, -887, -857, -830,
+ -806, -783, -762, -744, -726, -710, -694, -679, -666, -652, -640,
+ -628, -617, -606, -596, -586, -576, -567, -577, -549, -541, -532,
+ -525, -517, -509, -502, -495, -488, -482, -475, -469, -463, -457,
+ -451, -455, -439, -434, -429, -423, -418, -413, -408, -403, -398,
+ -394, -389, -385, -380, -376, -371, -367, -363, -359, -355, -351,
+ -347, -343, -339, -336, -332, -328, -325, -321, -318, -314, -311,
+ -308, -304, -301, -298, -295, -291, -288, -285, -282, -279, -276,
+ -273, -271, -268, -265, -262, -259, -257, -254, -251, -248, -246,
+ -243, -241, -238, -236, -233, -231, -228, -226, -223, -221, -219,
+ -216, -214, -212, -209, -207, -205, -203, -200, -198, -196, -194,
+ -192, -190, -188, -186, -184, -182, -180, -178, -176, -174, -172,
+ -170, -168, -166, -164, -162, -160, -158, -156, -155, -153, -151,
+ -149, -147, -146, -144, -142, -140, -139, -137, -135, -134, -132,
+ -130, -128, -127, -125, -124, -122, -120, -119, -117, -116, -114,
+ -112, -111, -109, -108, -106, -105, -103, -102, -100, -99, -97, -96,
+ -95, -93, -92, -90, -89, -87, -86, -85, -83, -82, -80, -79, -78,
+ -76, -75, -74, -72, -71, -70, -68, -67, -66, -65, -63, -62, -61,
+ -59, -58, -57, -56, -54, -53, -52, -51, -50, -48, -47, -46, -45,
+ -44, -42, -41, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30,
+ -29, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16,
+ -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
+};
+
+
+/*
+ * Build the gamma table so that floating point isn't needed.
+ *
+ * Note gamma goes from 0->256. The old value of 100 is now 128.
+ */
+void build_gamma_table(int gamma)
+{
+ int i, n;
+
+ /*
+ * value is the current sum.
+ * diff is the new term to add to the series.
+ */
+ long value, diff;
+
+ /* Hack - convergence is bad in these cases. */
+ gamma_table[0] = 0;
+ gamma_table[255] = 255;
+
+ for (i = 1; i < 255; i++)
+ {
+ /*
+ * Initialise the Taylor series
+ *
+ * value and diff have been scaled by 256
+ */
+ n = 1;
+ value = 256 * 256;
+ diff = ((long)gamma_helper[i]) * (gamma - 256);
+
+ while (diff)
+ {
+ value += diff;
+ n++;
+
+ /*
+ * Use the following identiy to calculate the gamma table.
+ * exp(x) = 1 + x + x^2/2 + x^3/(2*3) + x^4/(2*3*4) +...
+ *
+ * n is the current term number.
+ *
+ * The gamma_helper array contains a table of
+ * ln(x/256) * 256
+ * This is used because a^b = exp(b*ln(a))
+ *
+ * In this case:
+ * a is i / 256
+ * b is gamma.
+ *
+ * Note that everything is scaled by 256 for accuracy,
+ * plus another factor of 256 for the final result to
+ * be from 0-255. Thus gamma_helper[] * gamma must be
+ * divided by 256*256 each itteration, to get back to
+ * the original power series.
+ */
+ diff = (((diff / 256) * gamma_helper[i]) * (gamma - 256)) / (256 * n);
+ }
+
+ /*
+ * Store the value in the table so that the
+ * floating point pow function isn't needed.
+ */
+ gamma_table[i] = ((long)(value / 256) * i) / 256;
+ }
+}
+
+#endif /* SUPPORT_GAMMA */
+
+/*
+ * Ask to select an item in a list
+ */
+int ask_menu(cptr ask, char **items, int max)
+{
+ int ret = -1, i, start = 0;
+ char c;
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ while (TRUE)
+ {
+ /* Display list */
+ Term_load();
+ Term_save();
+ prt(ask, 0, 0);
+ for (i = start; (i < max) && (i < start + 20); i++)
+ {
+ prt(format("%c) %s", I2A(i - start), items[i]), i - start + 1, 0);
+ }
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the screen */
+ if (c == ESCAPE) break;
+
+ /* Scroll */
+ else if (c == '+')
+ {
+ if (start + 20 < max)
+ start += 20;
+ continue;
+ }
+
+ /* Scroll */
+ else if (c == '-')
+ {
+ start -= 20;
+ if (start < 0) start = 0;
+ continue;
+ }
+
+ /* Good selection */
+ else
+ {
+ c = tolower(c);
+ if (A2I(c) + start >= max)
+ {
+ bell();
+ continue;
+ }
+ if (A2I(c) + start < 0)
+ {
+ bell();
+ continue;
+ }
+
+ ret = A2I(c) + start;
+ break;
+ }
+ }
+
+ /* Load the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ return ret;
+}
+
+/*
+ * Determine if string "t" is a prefix of string "s"
+ */
+bool prefix(cptr s, cptr t)
+{
+ /* Paranoia */
+ if (!s || !t)
+ {
+ if (alert_failure) message_add(MESSAGE_MSG, "prefix() called with null argument!", TERM_RED);
+ return FALSE;
+ }
+
+ /* Scan "t" */
+ while (*t)
+ {
+ /* Compare content and length */
+ if (*t++ != *s++) return (FALSE);
+ }
+
+ /* Matched, we have a prefix */
+ return (TRUE);
+}
+
+/*
+ * Rescale a value
+ */
+s32b value_scale(int value, int vmax, int max, int min)
+{
+ s32b full_max = max - min;
+
+ value = (value * full_max) / vmax;
+ value += min;
+
+ return value;
+}
+
+/*
+ * Displays a box
+ */
+void draw_box(int y, int x, int h, int w)
+{
+ int i, j;
+
+ for (i = x + 1; i < x + w; i++)
+ for (j = y + 1; j < y + h; j++)
+ Term_putch(i, j, TERM_L_BLUE, ' ');
+
+ for (i = x; i < x + w; i++)
+ {
+ c_put_str(TERM_L_BLUE, "-", y, i);
+ c_put_str(TERM_L_BLUE, "-", y + h, i);
+ }
+ for (i = y; i < y + h; i++)
+ {
+ c_put_str(TERM_L_BLUE, "|", i, x);
+ c_put_str(TERM_L_BLUE, "|", i, x + w);
+ }
+ Term_putch(x, y, TERM_L_BLUE, '/');
+ Term_putch(x + w, y, TERM_L_BLUE, '\\');
+ Term_putch(x, y + h, TERM_L_BLUE, '\\');
+ Term_putch(x + w, y + h, TERM_L_BLUE, '/');
+}
+
+
+/*
+ * Displays a scrollable boxed list with a selected item
+ */
+void display_list(int y, int x, int h, int w, cptr title, cptr *list, int max, int begin, int sel, byte sel_color)
+{
+ int i;
+
+ draw_box(y, x, h, w);
+ c_put_str(TERM_L_BLUE, title, y, x + ((w - strlen(title)) / 2));
+
+ for (i = 0; i < h - 1; i++)
+ {
+ byte color = TERM_WHITE;
+
+ if (i + begin >= max) break;
+
+ if (i + begin == sel) color = sel_color;
+ c_put_str(color, list[i + begin], y + 1 + i, x + 1);
+ }
+}
+
+/*
+ * Creates an input box
+ */
+bool input_box(cptr text, int y, int x, char *buf, int max)
+{
+ int smax = strlen(text);
+
+ if (max > smax) smax = max;
+ smax++;
+
+ draw_box(y - 1, x - (smax / 2), 3, smax);
+ c_put_str(TERM_WHITE, text, y, x - (strlen(text) / 2));
+
+ Term_gotoxy(x - (smax / 2) + 1, y + 1);
+ return askfor_aux(buf, max);
+}
+
+/*
+ * Creates a msg bbox and ask a question
+ */
+char msg_box(cptr text, int y, int x)
+{
+ if (x == -1)
+ {
+ int wid = 0, hgt = 0;
+ Term_get_size(&wid, &hgt);
+ x = wid / 2;
+ y = hgt / 2;
+ }
+
+ draw_box(y - 1, x - ((strlen(text) + 1) / 2), 2, strlen(text) + 1);
+ c_put_str(TERM_WHITE, text, y, x - ((strlen(text) + 1) / 2) + 1);
+ return inkey();
+}
+
+/* Rescale a value */
+s32b rescale(s32b x, s32b max, s32b new_max)
+{
+ return (x * new_max) / max;
+}
+
+/* Nicer wrapper around TERM_XTRA_SCANSUBDIR */
+void scansubdir(cptr dir)
+{
+ strnfmt(scansubdir_dir, 1024, "%s", dir);
+ Term_xtra(TERM_XTRA_SCANSUBDIR, 0);
+}
+
+/*
+ * Timers
+ */
+timer_type *new_timer(cptr callback, s32b delay)
+{
+ timer_type *t_ptr;
+
+ MAKE(t_ptr, timer_type);
+ t_ptr->next = gl_timers;
+ gl_timers = t_ptr;
+
+ t_ptr->callback = string_make(callback);
+ t_ptr->delay = delay;
+ t_ptr->countdown = delay;
+ t_ptr->enabled = FALSE;
+
+ return t_ptr;
+}
+
+void del_timer(timer_type *t_ptr)
+{
+ timer_type *i, *old;
+
+ old = NULL;
+ for (i = gl_timers; (i != NULL) && (i != t_ptr); old = i, i = i->next)
+ ;
+ if (i)
+ {
+ if (old == NULL)
+ gl_timers = t_ptr->next;
+ else
+ old->next = t_ptr->next;
+ string_free(t_ptr->callback);
+ FREE(t_ptr, timer_type);
+ }
+ else
+ cmsg_print(TERM_VIOLET, "Unknown timer!");
+}
diff --git a/src/util.pkg b/src/util.pkg
new file mode 100644
index 00000000..bb1de5b0
--- /dev/null
+++ b/src/util.pkg
@@ -0,0 +1,2735 @@
+/* File: util.pkg */
+
+/*
+ * Purpose: Lua interface defitions for miscellaneous routines.
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+$#include "plots.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @def TRUE */
+#define TRUE
+
+/** @def FALSE */
+#define FALSE
+
+
+/** @def ESCAPE */
+#define ESCAPE '\033'
+
+/** @name Terminal Colours
+ * @{
+ */
+/** @def TERM_DARK
+ * @note 'd' (0,0,0)
+ */
+#define TERM_DARK 0 /* 'd' */
+/** @def TERM_WHITE
+ * @note 'w' (4,4,4)
+ */
+#define TERM_WHITE 1 /* 'w' */
+/** @def TERM_SLATE
+ * @note 's' (2,2,2)
+ */
+#define TERM_SLATE 2 /* 's' */
+/** @def TERM_ORANGE
+ * @note 'o' (4,2,0)
+ */
+#define TERM_ORANGE 3 /* 'o' */
+/** @def TERM_RED
+ * @note 'r' (3,0,0)
+ */
+#define TERM_RED 4 /* 'r' */
+/** @def TERM_GREEN
+ * @note 'g' (0,2,1)
+ */
+#define TERM_GREEN 5 /* 'g' */
+/** @def TERM_BLUE
+ * @note 'b' (0,0,4)
+ */
+#define TERM_BLUE 6 /* 'b' */
+/** @def TERM_UMBER
+ * @note 'u' (2,1,0)
+ */
+#define TERM_UMBER 7 /* 'u' */
+/** @def TERM_L_DARK
+ * @note 'D' (1,1,1)
+ */
+#define TERM_L_DARK 8 /* 'D' */
+/** @def TERM_L_WHITE
+ * @note 'W' (3,3,3)
+ */
+#define TERM_L_WHITE 9 /* 'W' */
+/** @def TERM_VIOLET
+ * @note 'v' (4,0,4)
+ */
+#define TERM_VIOLET 10 /* 'v' */
+/** @def TERM_YELLOW
+ * @note 'y' (4,4,0)
+ */
+#define TERM_YELLOW 11 /* 'y' */
+/** @def TERM_L_RED
+ * @note 'R' (4,0,0)
+ */
+#define TERM_L_RED 12 /* 'R' */
+/** @def TERM_L_GREEN
+ * @note 'G' (0,4,0)
+ */
+#define TERM_L_GREEN 13 /* 'G' */
+/** @def TERM_L_BLUE
+ * @note 'B' (0,4,4)
+ */
+#define TERM_L_BLUE 14 /* 'B' */
+/** @def TERM_L_UMBER
+ * @note 'U' (3,2,1)
+ */
+#define TERM_L_UMBER 15 /* 'U' */
+/** @} */
+
+/** @name Event Hooks
+ * @{
+ */
+/** @def HOOK_MONSTER_DEATH
+ * @brief Monster dies.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @note (see file xtra2.c)
+ */
+#define HOOK_MONSTER_DEATH 0
+
+/** @def HOOK_OPEN
+ * @brief Open door or chest.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file cmd2.c)
+ */
+#define HOOK_OPEN 1
+
+/** @def HOOK_GEN_QUEST
+ * @brief Generate quest level.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file generate.c)
+ */
+#define HOOK_GEN_QUEST 2
+
+/** @def HOOK_END_TURN
+ * @brief Turn ends.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file dungeon.c)
+ */
+#define HOOK_END_TURN 3
+
+/** @def HOOK_FEELING
+ * @brief Display level feeling.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @return Boolean \n TRUE if a level feeling was displayed, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, then no other feelings are displayed and
+ * do_cmd_feeling() returns.
+ * @note (see file cmd4.c)
+ */
+#define HOOK_FEELING 4
+
+/** @def HOOK_NEW_MONSTER
+ * @brief Generate monster.\n
+ * @param Number r_idx \n index of monster in monster race (r_info) array.
+ * @brief Monster index
+ * @return Boolean \n TRUE if monster is not allowed to be created,
+ * otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, then the monster is "killed".
+ * @note (see file monster2.c)
+ */
+#define HOOK_NEW_MONSTER 5
+
+/** @def HOOK_GEN_LEVEL
+ * @brief Generate dungeon level.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file generate.c)
+ */
+#define HOOK_GEN_LEVEL 6
+
+/** @def HOOK_BUILD_ROOM1
+ * @brief Generate room (type 1 - normal rectangular room).\n
+ * @param Number by0 \n y-coordinate of dungeon block where room is built.
+ * @brief Block y-coordinate
+ * @param Number bx0 \n x-coordinate of dungeon block where room is built.
+ * @brief Block x-coordinate
+ * @return Boolean \n TRUE if room was created, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, then the room has been built and build_type1()
+ * returns.
+ * @note (see file generate.c)
+ */
+#define HOOK_BUILD_ROOM1 7
+
+/** @def HOOK_NEW_LEVEL
+ * @brief Start dungeon level.\n
+ * @param Number quest \n if 0, then player is not on a quest level,
+ * otherwise the player is on a quest.
+ * @brief On quest?
+ * @note (see file dungeon.c)
+ */
+#define HOOK_NEW_LEVEL 8
+
+/** @def HOOK_QUEST_FINISH
+ * @brief Quest finished.\n
+ * @param Number plot \n a plot from the plots array.
+ * @brief Plot
+ * @note (see file bldg.c)
+ */
+#define HOOK_QUEST_FINISH 9
+
+/** @def HOOK_QUEST_FAIL
+ * @brief Quest failed.\n
+ * @param Number plot \n a plot from the plots array.
+ * @brief Plot
+ * @note (see file bldg.c)
+ */
+#define HOOK_QUEST_FAIL 10
+
+/** @def HOOK_GIVE
+ * @brief Give item to monster.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @param Number item \n the item to be given.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was given to monster, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, then the message "The monster does not want
+ * your item." is displayed.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_GIVE 11
+
+/** @def HOOK_CHAR_DUMP
+ * @brief Add a line to the character sheet.
+ * @note (see files.c)
+ */
+#define HOOK_CHAR_DUMP 12
+
+/** @def HOOK_INIT_QUEST
+ * @brief Quest initialised.\n
+ * @param Number plot \n a plot from the plots array.
+ * @brief Plot
+ * @return Boolean \n TRUE if quest was not initialised, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, castle_quest() returns FALSE.
+ * @note (see file bldg.c)
+ */
+#define HOOK_INIT_QUEST 13
+
+/** @def HOOK_WILD_GEN
+ * @brief Generate wilderness.\n
+ * @param Number wilderness \n if TRUE, then this is overhead wilderness
+ * processing, otherwise it is regular wilderness processing.
+ * @brief Overhead?
+ * @note (see file wild.c)
+ */
+#define HOOK_WILD_GEN 14
+
+/** @def HOOK_DROP
+ * @brief Drop an item.\n
+ * @param Number item \n the item to drop.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was dropped, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_drop() returns, otherwise the function
+ * continues.
+ * @note (see file cmd3.c)
+ */
+#define HOOK_DROP 15
+
+/** @def HOOK_IDENTIFY
+ * @brief Identfy an item.\n
+ * @param Number item \n the item to identify.
+ * @brief Item number
+ * @param String type \n "normal" to identify the item, or "full" to fully
+ * identify an item.
+ * @brief Type
+ * @note (see files spells1.c, spells2.c)
+ */
+#define HOOK_IDENTIFY 16
+
+/** @def HOOK_MOVE
+ * @brief Player moves.\n
+ * @param Number y \n the y-coordinate of the new location.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the new location.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if player is not allowed to move, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, move_player_aux() returns, otherwise the function
+ * continues.
+ * @note (see file cmd1.c)
+ */
+#define HOOK_MOVE 17
+
+/** @def HOOK_STAIR
+ * @brief Player uses stairs.\n
+ * @param String direction \n "up" if the player is going up stairs, or
+ * "down" if the player is going down stairs.
+ * @brief Direction
+ * @return Boolean \n TRUE if player is not allowed to use stairs, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_go_up() or do_cmd_go_down() returns,
+ * otherwise the function continues.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_STAIR 18
+
+/** @def HOOK_MONSTER_AI
+ * @brief Monster moves.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @return Boolean \n TRUE if monster AI was applied, otherwise FALSE.
+ * @return Number y2 \n New y-coordinate of monster target.
+ * @return Number x2 \n New x-coordinate of monster target.
+ * @note
+ * If the hook returns TRUE, the monster moves toward the hook position.
+ * @note (see file melee2.c)
+ */
+#define HOOK_MONSTER_AI 19
+
+/** @def HOOK_PLAYER_LEVEL
+ * @brief Player gains (or loses) a level.\n
+ * @param Number gained \n the number of levels gained (or lost).
+ * @brief Levels gained
+ * @note (see file xtra2.c)
+ */
+#define HOOK_PLAYER_LEVEL 20
+
+/** @def HOOK_WIELD
+ * @brief Player wields an item.\n
+ * @param Number item \n the item to wield.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was not wielded, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_wield() returns, otherwise the function
+ * continues.
+ * @note (see file cmd3.c)
+ */
+#define HOOK_WIELD 21
+
+/** @def HOOK_INIT
+ * @brief Game initialised.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_INIT 22
+
+/** @def HOOK_QUAFF
+ * @brief Player quaffs a potion.\n
+ * @param Object o_ptr \n the potion to quaff.
+ * @brief Potion
+ * @return Boolean \n TRUE if potion was quaffed, otherwise FALSE.
+ * @return Number ident \n TRUE if the potion was identifed, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "potion identified" flag.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_QUAFF 23
+
+/** @def HOOK_AIM */
+#define HOOK_AIM 24
+
+/** @def HOOK_USE */
+#define HOOK_USE 25
+
+/** @def HOOK_ACTIVATE
+ * @brief Player activates an item.\n
+ * @param Number item \n the item to activate.
+ * @brief Item number
+ * @return Boolean \n TRUE if item was activated, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_activate() returns, otherwise the function
+ * continues.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_ACTIVATE 26
+
+/** @def HOOK_ZAP
+ * @brief Player zaps a rod.\n
+ * @param Number tval \n type of rod to zap.
+ * @brief Type
+ * @param Number sval \n sub-type of rod to zap.
+ * @brief Sub-type
+ * @note (see file cmd6.c)
+ */
+#define HOOK_ZAP 27
+
+/** @def HOOK_READ
+ * @brief Player reads a scroll.\n
+ * @param Object o_ptr \n the scroll to read.
+ * @brief Scroll
+ * @return Boolean \n TRUE if scroll was read, otherwise FALSE.
+ * @return Number used_up \n TRUE if the scroll was used up, otherwise FALSE.
+ * @return Number ident \n TRUE if the scroll was identifed, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "scroll used up" and
+ * "scroll identified" flags.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_READ 28
+
+/** @def HOOK_CALC_BONUS
+ * @brief Calculate player "state" bonuses.
+ * @note (see xtra1.c)
+ */
+#define HOOK_CALC_BONUS 29
+
+/** @def HOOK_CALC_BONUS
+ * @brief Calculate player "state" bonuses, after all calcs are done.
+ * @note (see xtra1.c)
+ */
+#define HOOK_CALC_BONUS_END 77
+
+/** @def HOOK_CALC_POWERS
+ * @brief Calculate player powers.
+ * @note (see xtra1.c)
+ */
+#define HOOK_CALC_POWERS 30
+
+/** @def HOOK_KEYPRESS
+ * @brief User enters a command.\n
+ * @param Number command \n the pressed key (command_cmd).
+ * @brief Command
+ * @return Boolean \n TRUE if special processing was done, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, process_command() returns, otherwise the function
+ * continues.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_KEYPRESS 31
+
+/** @def HOOK_CHAT
+ * @brief Player chats to monster.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @return Boolean \n TRUE if monster chats, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, the message "There is no monster there." is
+ * printed.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_CHAT 32
+
+/** @def HOOK_MON_SPEAK
+ * @brief Monster speaks.\n
+ * @param Number m_idx \n index of monster in monster (m_list) array.
+ * @brief Monster index
+ * @param String m_name \n name of the monster.
+ * @brief Monster name
+ * @return Boolean \n TRUE if monster speaks, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, the monster may say something else.
+ * @note (see file melee2.c)
+ */
+#define HOOK_MON_SPEAK 33
+
+/** @def HOOK_MKEY
+ * @brief Player uses skill.\n
+ * @param Number x_idx \n the skill to execute.
+ * @brief Skill index
+ * @note (see file skills.c)
+ */
+#define HOOK_MKEY 34
+
+/** @def HOOK_BIRTH_OBJECTS
+ * @brief Player receives objects at birth.
+ * @note (see file birth.c)
+ */
+#define HOOK_BIRTH_OBJECTS 35
+
+/** @def HOOK_ACTIVATE_DESC
+ * @brief Display activation description.\n
+ * @param Object o_ptr \n the item to activate.
+ * @brief Object
+ * @return Boolean \n TRUE if item has an activation, otherwise FALSE.
+ * @return String desc \n the activation description.
+ * @note
+ * If the hook returns TRUE, item_activation() returns the hook's activation
+ * description.
+ * @note (see file object1.c)
+ */
+#define HOOK_ACTIVATE_DESC 36
+
+/** @def HOOK_INIT_GAME
+ * @brief Game initialised.\n
+ * @param String when \n "begin" if done at the start of game initialisation,
+ * or "end" if done at end of game initialisation.
+ * @brief When?
+ * @note (see file init2.c)
+ */
+#define HOOK_INIT_GAME 37
+
+/** @def HOOK_ACTIVATE_POWER
+ * @brief Player activates a power.\n
+ * @param Number power \n the power to activate.
+ * @brief Power
+ * @return Boolean \n TRUE if power was activated, otherwise FALSE.
+ * @note
+ * If the hook returns FALSE, power_activate() displays the message
+ * "Warning power_activate() called with invalid power(xx)." where
+ * xx = power.
+ * @note (see file powers.c)
+ */
+#define HOOK_ACTIVATE_POWER 38
+
+/** @def HOOK_ITEM_NAME
+ * @brief Get an item name.\n
+ * @param Object o_ptr \n the item whose name is required.
+ * @brief Object
+ * @return Boolean \n TRUE if name was found, otherwise FALSE.
+ * @return String basenm \n The item name.
+ * @return String modstr \n The item modifier string.
+ * @note (see file object1.c)
+ */
+#define HOOK_ITEM_NAME 39
+
+/** @def HOOK_SAVE_GAME
+ * @brief Save the game.
+ * @note (see file loadsave.c)
+ */
+#define HOOK_SAVE_GAME 40
+
+/** @def HOOK_LOAD_GAME
+ * @brief Load the game.
+ * @note (see file loadsave.c)
+ */
+#define HOOK_LOAD_GAME 41
+
+/** @def HOOK_LEVEL_REGEN
+ * @brief Start generation of a special level.
+ * @note (see file generate.c)
+ */
+#define HOOK_LEVEL_REGEN 42
+
+/** @def HOOK_LEVEL_END_GEN
+ * @brief End generation of a special level.
+ * @note (see file generate.c)
+ */
+#define HOOK_LEVEL_END_GEN 43
+
+/** @def HOOK_BUILDING_ACTION
+ * @brief Player performs an action in a building.\n
+ * @param Number action \n the action performed in the building
+ * @brief Action flag
+ * @return Boolean \n TRUE if player performed the action, otherwise FALSE.
+ * @return Number paid \n TRUE if player paid to perform the action, otherwise
+ * FALSE.
+ * @return Number recreate \n TRUE if something is recreated, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "paid" and "recreate" flags.
+ * @note (see file bldg.c)
+ */
+#define HOOK_BUILDING_ACTION 44
+
+/** @def HOOK_PROCESS_WORLD
+ * @brief Update world every ten turns.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_PROCESS_WORLD 45
+
+/** @def HOOK_WIELD_SLOT
+ * @brief Find equipment slot for object.\n
+ * @param Object o_ptr \n the object to wield.
+ * @brief Object
+ * @param Number ideal \n TRUE if current body and stuff is ignore, otherwise
+ * FALSE.
+ * @return Boolean \n TRUE if hook processed the object, otherwise FALSE.
+ * @return Number slot \n The equipent slot where the object will go (-1 if
+ * there are no available slots).
+ * @note
+ * If the hook returns TRUE, wield_slot_ideal() returns the slot from the hook.
+ * @note (see file objects1.c)
+ */
+#define HOOK_WIELD_SLOT 46
+
+/** @def HOOK_STORE_STOCK
+ * @brief Stock a store.\n
+ * @param Number st_idx \n the index of the store in st_info array.
+ * @brief Store index
+ * @param String name \n the name of the store.
+ * @brief Store name
+ * @param Number level \n the "dungeon level" of the store.
+ * @brief Store level
+ * @return Boolean \n TRUE if hook has selected an object, otherwise FALSE.
+ * @return Object q_ptr \n The item to be stocked in the store.
+ * @note
+ * If the hook returns TRUE, store_create() will create the hook's object and
+ * put it in the store.
+ * @note (see file store.c)
+ */
+#define HOOK_STORE_STOCK 47
+
+/** @def HOOK_STORE_BUY
+ * @brief Store buys an item.\n
+ * @param Number st_idx \n the index of the store in st_info array.
+ * @brief Store index
+ * @param String name \n the name of the store.
+ * @brief Store name
+ * @param Object o_ptr \n the object to buy.
+ * @brief Object
+ * @return Boolean \n TRUE if the hook has processed the object, otherwise
+ * FALSE.
+ * @return Number buy \n TRUE if the store will buy the object, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, store_will_buy() will return "buy".
+ * @note (see file store.c)
+ */
+#define HOOK_STORE_BUY 48
+
+/** @def HOOK_GEN_LEVEL_BEGIN
+ * @brief Generate a random dungeon level.
+ * @note (see file generate.c)
+ */
+#define HOOK_GEN_LEVEL_BEGIN 49
+
+/** @def HOOK_GET
+ * @brief Player gets an object.\n
+ * @param Object o_ptr \n the object to get.
+ * @brief Object
+ * @param Number o_idx \n the index of the object in o_list array.
+ * @brief Object index
+ * @return Boolean \n TRUE if hooks processes the object, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, object_pickup() returns, otherwise the function
+ * continues.
+ * @note (see object1.c)
+ */
+#define HOOK_GET 50
+
+/** @def HOOK_REDRAW
+ * @brief Redraw the screen.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_REDRAW 51
+
+/** @def HOOK_RECALC_SKILLS
+ * @brief Recalculate player skills.
+ * @note (see skills.c)
+ */
+#define HOOK_RECALC_SKILLS 52
+
+/** @def HOOK_ENTER_DUNGEON
+ * @brief Player goes down one dungeon level.\n
+ * @param Number special \n special information for player's dungeon grid.
+ * @brief Special info
+ * @return Boolean \n TRUE if the hook prevents the player going down,
+ * otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the player remains on the current dungeon level
+ * and do_cmd_go_down() returns.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_ENTER_DUNGEON 53
+
+/** @def HOOK_FIRE
+ * @brief Player fires an object (bow slot of inventory).\n
+ * @param Object \n the object to fire.
+ * @brief Object
+ * @return Boolean \n TRUE if the hook has fired the object, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, process_command() returns.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_FIRE 54
+
+/** @def HOOK_EAT
+ * @brief Player eats.\n
+ * @param Object o_ptr \n the object the player eats.
+ * @brief Object
+ * @return Boolean \n TRUE if hook processes the object, otherwise FALSE.
+ * @return Number ident \n TRUE if the object was identified, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "food identified" flag.
+ * @note (see file cmd6.c)
+ */
+#define HOOK_EAT 55
+
+/** @def HOOK_DIE
+ * @brief Player dies.
+ * @return Boolean \n TRUE if player does not die, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the player cheats death.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_DIE 56
+
+/** @def HOOK_CALC_HP
+ * @brief Recalculate player HP (hit points).\n
+ * @param Number mhp \n the player's new maximum hit points.
+ * @brief Maximum hit points.
+ * @return Boolean \n TRUE if hook has processed player hit points, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, the player's maximum hit points are updated.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_HP 57
+
+/** @def HOOK_GF_COLOR
+ * @brief Set color for spell.
+ * @param Number type \n type of spell.
+ * @brief Type
+ * @param Number file \n if this is 0 use ANGBAND_GRAF, otherwise use "new".
+ * @brief File
+ * @return Boolean \n TRUE if hook sets a color, otherwise FALSE.
+ * @return Number color \n The color for the spell.
+ * @note
+ * If the hook returns TRUE, spell_color() returns the hook's color, otherwise
+ * the function continues.
+ * @note (see file spells1.c)
+ */
+#define HOOK_GF_COLOR 58
+
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage terrain features.\n
+ * @param String target \n "grid" to indicate spell damages terrain.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number flag \n TRUE if the player is affected, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious" and "flag" fields.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage objects.\n
+ * @param String target \n "object" to indicate spell damages objects.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @param Object o_ptr \n the object which is the target of the spell.
+ * @brief Object
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number flag \n TRUE if the player is affected, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious" and "do_kill" fields.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage monsters.\n
+ * @param String target \n "angry" to indicate spell angers a friend.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @param Monster m_ptr \n the monster which is the target of the spell.
+ * @brief Monster
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number get_angry \n TRUE if the monster gets angry, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, the hook sets the "get_angry" field.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage monsters.\n
+ * @param String target \n "monster" to indicate spell damages monsters.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @param Monster m_ptr \n the monster which is the target of the spell.
+ * @brief Monster
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number dam \n The damage the monster takes.
+ * @return Number do_stun \n TRUE if the monster is stunned, otherwise FALSE.
+ * @return Number do_fear \n TRUE if the monster is frightened, otherwise
+ * FALSE.
+ * @return Number do_conf \n TRUE if the monster is confused, otherwise FALSE.
+ * @return Number do_dist \n TRUE if the monster is disturbed, otherwise FALSE.
+ * @return Number do_pois \n TRUE if the monster is poisoned, otherwise FALSE.
+ * @return Number do_cut \n TRUE if the monster is wounded, otherwise FALSE.
+ * @return Number do_poly \n TRUE if the monster is polymorphed, otherwise
+ * FALSE.
+ * @return String note \n The message displayed if the monster if affected.
+ * @return String note_dies \n The message displayed if the monster dies.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious", "dam", "do_stun",
+ * "do_fear", "do_conf", "do_dist", "do_pois", "do_cut", "do_poly", "note",
+ * and "note dies" fields, otherwise the spell has no effect and does no
+ * damage.
+ * @note (see file spells1.c)
+ */
+/** @def HOOK_GF_EXEC
+ * @brief A spell to damage the player.\n
+ * @param String target \n "player" to indicate spell damages the player.
+ * @brief Target
+ * @param Number who \n the source of the spell.
+ * @brief Source
+ * @param Number type \n the type of spell.
+ * @brief Type
+ * @param Number dam \n the number of hit points of damage.
+ * @brief Damage
+ * @param Number r \n the radius of the spell.
+ * @brief Radius
+ * @param Number y \n the y-coordinate of the target.
+ * @brief Y-coordinate
+ * @param Number x \n the x-coordinate of the target.
+ * @brief X-coordinate
+ * @return Boolean \n TRUE if spell was cast, otherwise FALSE.
+ * @return Number obvious \n TRUE if the player notices the spell, otherwise
+ * FALSE.
+ * @return Number dam \n The damage the player takes.
+ * @note
+ * If the hook returns TRUE, the hook sets the "obvious" and "dam" fields,
+ * otherwise there is no damage.
+ * @note (see file spells1.c)
+ */
+#define HOOK_GF_EXEC 59
+
+/** @def HOOK_CALC_MANA
+ * @brief Recalculate player SP (spell points).\n
+ * @param Number msp \n the player's new maximum spell points.
+ * @brief Maximum spell points.
+ * @return Boolean \n TRUE if hook has processed player spell points, otherwise
+ * FALSE.
+ * @note
+ * If the hook returns TRUE, the player's maximum spell points are updated.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_MANA 60
+
+/** @def HOOK_LOAD_END
+ * @brief Load a savefile.\n
+ * @param Number death \n TRUE if the character is dead, otherwise FALSE.
+ * @brief Dead character?
+ * @return Boolean \n TRUE if hook has processed savefile, otherwise FALSE.
+ * @return Number death \n
+ * @note
+ * If the hook returns TRUE, then "character_loaded" (real living player) is
+ * set to TRUE. The player has been revived.
+ * @note (see file loadsave.c)
+ */
+#define HOOK_LOAD_END 61
+
+/** @def HOOK_RECALL
+ * @brief Player recalls from/to dungeon/town.
+ * @return Boolean \n TRUE if player is not allowed to recall, otherwise
+ * FALSE.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_RECALL 62
+
+/** @def HOOK_FOLLOW_GOD
+ * @brief Player follows a god (gets religion).\n
+ * @param Number god \n the god to follow.
+ * @brief God
+ * @param String action \n "ask" to check if player can follow the god, or
+ * "done" to do something with the god.
+ * @brief Action
+ * @return Boolean \n For "ask": TRUE if player can not follow the god,
+ * otherwise FALSE.
+ * @note
+ * If the action is "ask" and the hook returns TRUE, follow_god() returns.
+ * If the action is "done" the return code is ignored.
+ * @note (see file gods.c)
+ */
+#define HOOK_FOLLOW_GOD 63
+
+/** @def HOOK_SACRIFICE_GOD
+ * @brief Player sacrifices to a god.
+ * @note (see file cmd2.c)
+ */
+#define HOOK_SACRIFICE_GOD 64
+
+/** @def HOOK_BODY_PARTS
+ * @brief Calculate which body parts the player has.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_BODY_PARTS 65
+
+/** @def HOOK_APPLY_MAGIC
+ * @brief Apply magic to an item.\n
+ * @param Object o_ptr \n the item to which magic is applied
+ * @brief Object
+ * @param Number level \n the level of the object
+ * @brief Object level
+ * @param Number power \n the power of the object (0 = normal, 1 = good,
+ * 2 = great, -1 = cursed, -2 = broken)
+ * @brief Power
+ * @return Boolean \n TRUE if hook has applied magic, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, a_m_aux_n() (where n=1 to 4) returns.
+ * @note (see file object2.c)
+ */
+#define HOOK_APPLY_MAGIC 66
+
+/** @def HOOK_PLAYER_EXP
+ * @brief Player gains/loses experience points (XP).\n
+ * @param Number amount \n the number of experience points to gain/lose
+ * @brief Points
+ * @note (see file xtra2.c)
+ */
+#define HOOK_PLAYER_EXP 67
+
+/** @def HOOK_BIRTH
+ * @brief Player is born.
+ * @note (see file birth.c)
+ */
+#define HOOK_BIRTH 68
+
+/** @def HOOK_CALC_LITE
+ * @brief Calculate the lite radius.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_LITE 69
+
+/** @def HOOK_LEARN_ABILITY
+ * @brief Player learns an ability.\n
+ * @param Number ab \n index of ability in ability (ab_info) array.
+ * @brief Ability index
+ * @return Boolean \n TRUE if player is not to gain the ability, otherwise
+ * FALSE.
+ * @note If the hook returns TRUE, can_learn_ability() returns FALSE.
+ * @note (see file skills.c)
+ */
+#define HOOK_LEARN_ABILITY 70
+
+/** @def HOOK_MOVED
+ * @brief Player finishes moving.
+ * @note (see file cmd1.c)
+ */
+#define HOOK_MOVED 71
+
+/** @def HOOK_GAME_START
+ * @brief Game begins.
+ * @note (see file dungeon.c)
+ */
+#define HOOK_GAME_START 72
+
+/** @def HOOK_TAKEOFF
+ * @brief Player takes off an item.\n
+ * @param Number item \n the item to take off.
+ * @brief Item
+ * @return Booelan \n TRUE if item can not be taken off, otherwise FALSE.
+ * @note
+ * If the hook returns TRUE, do_cmd_takeoff() returns.
+ * @note (see file cmd3.c)
+ */
+#define HOOK_TAKEOFF 73
+
+/** @def HOOK_CALC_WEIGHT
+ * @brief Calculate player weight limit.\n
+ * @param Number weight \n the current weight limit.
+ * @brief Weight
+ * @return Boolean \n TRUE if weight was processed, otherwise FALSE.
+ * @return Number weight \n The new maximum weight.
+ * @note
+ * If the hook returns TRUE, weight_limit() returns the hook's weight.
+ * @note (see file xtra1.c)
+ */
+#define HOOK_CALC_WEIGHT 74
+
+/** @def HOOK_FORBID_TRAVEL
+ * @brief Check if the player may press < and travel.\n
+ * @return Boolean \n TRUE if travel is forbidden, otherwise FALSE.
+ */
+#define HOOK_FORBID_TRAVEL 75
+
+/** @def HOOK_DEBUG_COMMAND
+ * @brief User enters a debug command.\n
+ * @param Number command \n the pressed key (cmd).
+ * @brief Command
+ * @return Boolean \n TRUE if special processing was done, otherwise FALSE.
+ */
+#define HOOK_DEBUG_COMMAND 76
+
+
+/** @} */
+
+
+/** @var turn
+ * @brief Number
+ * @note Current game turn
+ */
+extern s32b turn;
+/** @var old_turn
+ * @brief Number
+ * @note Turn when level began (feelings)
+ */
+extern s32b old_turn;
+/** @var cur_wid
+ * @brief Number
+ * @note Current dungeon width
+ */
+extern s16b cur_wid;
+/** @var cur_hgt
+ * @brief Number
+ * @note Current dungeon height
+ */
+extern s16b cur_hgt;
+
+/** @fn disturb(int stop_search, int flush_output)
+ * @brief Disturb the player.\n
+ * @param stop_search Number \n if 0, this will not disturb searching,
+ * otherwise searching is stopped.
+ * @brief Stop search?
+ * @param flush_output Number \n *unused*
+ * @brief *Unused*
+ * @note
+ * Something has happened to disturb the player.\n\n
+ * The first arg indicates a major disturbance, which affects search.\n
+ * The second arg is currently unused, but could induce output flush.\n\n
+ * All disturbance cancels repeated commands, resting, and running.
+ * @note (see file cave.c)
+ */
+extern void disturb(int stop_search, int flush_output);
+
+/** @fn bst(s32b what, s32b t)
+ * @brief Break scalar time.\n
+ * @param what Number \n the unit time "t" is to be broken into. The following
+ * values can be used: MINUTE, HOUR, DAY, YEAR
+ * @brief Unit of time
+ * @param t Number \n the time to be broken.
+ * @brief Time
+ * @return Number \n The number of unit in time "t".
+ * @note (see file util.c)
+ */
+extern s32b bst(s32b what, s32b t);
+
+$static char *path_build_lua(cptr path, cptr file){static char buf[1025]; path_build(buf, 1024, path, file); return buf;}
+
+/** @fn path_build(cptr path, cptr file);
+ * @brief Create a new path by appending a file (or directory) to a path.\n
+ * @param path String \n the original path.
+ * @brief Path
+ * @param file String \n the file or directory to append to the path.
+ * @brief File or directory
+ * @return String \n The new path.
+ * @note
+ * This requires no special processing on simple machines, except
+ * for verifying the size of the filename, but note the ability to
+ * bypass the given "path" with certain special file-names.\n\n
+ * Note that the "file" may actually be a "sub-path", including
+ * a path and a file.\n\n
+ * @note (see file util.c)
+ */
+static char *path_build_lua@path_build(cptr path, cptr file);
+
+/** @fn move_cursor(int row, int col)
+ * @brief Move the cursor of a terminal to row "row" and column "col".\n
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note (see file util.c)
+ */
+extern void move_cursor(int row, int col);
+
+/** @fn flush(void)
+ * @brief Flush all input chars.
+ * @note
+ * Actually, remember the flush, and do a "special flush" before the next
+ * "inkey()".
+ * This is not only more efficient, but also necessary to make sure
+ * that various "inkey()" codes are not "lost" along the way.
+ * @note (see file util.c)
+ */
+extern void flush(void);
+
+/** @var inkey_scan
+ * @brief Boolean
+ * @note
+ * If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+ * keypress is available, instead of waiting for a keypress.
+ */
+extern bool inkey_scan;
+
+/** @fn inkey(void)
+ * @brief Get a keypress from the user.
+ * @return String \n the key pressed by the user.
+ * @note
+ * This function recognizes a few "global parameters". These are variables
+ * which, if set to TRUE before calling this function, will have an effect
+ * on this function, and which are always reset to FALSE by this function
+ * before this function returns. Thus they function just like normal
+ * parameters, except that most calls to this function can ignore
+ * them.\n\n
+ * If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
+ * and any macro processing in progress will be aborted. This flag is
+ * set by the "flush()" function, which does not actually flush anything
+ * itself, but rather, triggers delayed input flushing via
+ * "inkey_xtra".\n\n
+ * If "inkey_scan" is TRUE, then we will immediately return "zero" if no
+ * keypress is available, instead of waiting for a
+ * keypress.\n\n
+ * If "inkey_base" is TRUE, then all macro processing will be bypassed.
+ * If "inkey_base" and "inkey_scan" are both TRUE, then this function will
+ * not return immediately, but will wait for a keypress for as long as the
+ * normal macro matching code would, allowing the direct entry of macro
+ * triggers. The "inkey_base" flag is extremely
+ * dangerous!\n\n
+ * If "inkey_flag" is TRUE, then we will assume that we are waiting for a
+ * normal command, and we will only show the cursor if "hilite_player" is
+ * TRUE (or if the player is in a store), instead of always showing the
+ * cursor. The various "main-xxx.c" files should avoid saving the game
+ * in response to a "menu item" request unless "inkey_flag" is TRUE, to
+ * prevent savefile
+ * corruption.\n\n
+ * If we are waiting for a keypress, and no keypress is ready, then we will
+ * refresh (once) the window which was active when this function was
+ * called.\n\n
+ * Note that "back-quote" is automatically converted into "escape" for
+ * convenience on machines with no "escape" key. This is done after the
+ * macro matching, so the user can still make a macro for
+ * "backquote".\n\n
+ * Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
+ * and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
+ * provide support for simple keyboard "macros". These keys are so strange
+ * that their loss as normal keys will probably be noticed by nobody. The
+ * "ascii 30" key is used to indicate the "end" of a macro action, which
+ * allows recursive macros to be avoided. The "ascii 31" key is used by
+ * some of the "main-xxx.c" files to introduce macro trigger
+ * sequences.\n\n
+ * Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key,
+ * which can be used to give a variety of "sub-commands" which can be used
+ * any time. These sub-commands could include commands to take a picture of
+ * the current screen, to start/stop recording a macro action,
+ * etc.\n\n
+ * If "angband_term[0]" is not active, we will make it active during this
+ * function, so that the various "main-xxx.c" files can assume that input
+ * is only requested (via "Term_inkey()") when "angband_term[0]" is
+ * active.\n\n
+ * Mega-Hack -- This function is used as the entry point for clearing the
+ * "signal_count" variable, and of the "character_saved"
+ * variable.\n\n
+ * Hack -- Note the use of "inkey_next" to allow "keymaps" to be
+ * processed.\n\n
+ * Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
+ * control of the keyboard from the user.
+ * @note (see file util.c)
+ */
+extern char inkey(void);
+
+/** @fn cmsg_print(byte color, cptr msg)
+ * @brief Output message "msg" in colour "color" to the top line of the
+ * screen.\n
+ * @param color Number \n the colour of the message (see TERM_ fields).
+ * @brief Colour
+ * @param msg String \n the message.
+ * @brief Message
+ * @note
+ * Break long messages into multiple pieces (40-72 chars).\n\n
+ * Allow multiple short messages to "share" the top line.\n\n
+ * Prompt the user to make sure he has a chance to read them.\n\n
+ * These messages are memorized for later reference (see above).\n\n
+ * We could do "Term_fresh()" to provide "flicker" if needed.\n\n
+ * The global "msg_flag" variable can be cleared to tell us to
+ * "erase" any "pending" messages still on the
+ * screen.\n\n
+ * XXX XXX XXX Note that we must be very careful about using the
+ * "msg_print()" functions without explicitly calling the special
+ * "msg_print(NULL)" function, since this may result in the loss
+ * of information if the screen is cleared, or if anything is
+ * displayed on the top
+ * line.\n\n
+ * XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
+ * even if no messages are pending. This is probably a hack.
+ * @note (see file util.c)
+ */
+extern void cmsg_print(byte color, cptr msg);
+
+/** @fn msg_print(cptr msg)
+ * @brief Output message "msg" in white to the top line of the screen.\n
+ * @param msg String \n the message.
+ * @brief Message
+ * @note (see file util.c)
+ */
+extern void msg_print(cptr msg);
+
+/** @fn screen_save(void)
+ * @brief Save the screen.
+ * @note
+ * Increase the "icky" depth.\n\n
+ * This function must match exactly one call to "screen_load()".
+ * @note (see file util.c)
+ */
+extern void screen_save(void);
+
+/** @fn screen_load(void)
+ * @brief Load the screen.
+ * @note
+ * Decrease the "icky" depth.\n\n
+ * This function must match exactly one call to "screen_save()".
+ * @note (see file util.c)
+ */
+extern void screen_load(void);
+
+/** @fn Term_save(void)
+ * @brief Save the "requested" screen into the "memorized" screen.
+ * @return Number \n 0 (always).
+ * @note
+ * Every "Term_save()" should match exactly one "Term_load()"
+ * @note (see file z-term.c)
+ */
+extern errr Term_save(void);
+
+/** @fn Term_load(void)
+ * @brief Restore the "requested" contents from the "memorized" screen.
+ * @return Number \n 0 (always).
+ * @note
+ * Every "Term_save()" should match exactly one "Term_load()"
+ * @note (see file z-term.c)
+ */
+extern errr Term_load(void);
+
+/** @fn c_put_str(byte attr, cptr str, int row, int col)
+ * @brief Add string "str" with attributes "attr" to screen at row "row"
+ * and column "col".\n
+ * @param attr Number \n the attribute of the string
+ * @brief Attribute
+ * @param str String \n the string
+ * @brief String
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note
+ * Display a string on the screen using an attribute.\n\n
+ * At the given location, using the given attribute, if allowed,
+ * add the given string. Do not clear the line.
+ * @note (see file util.c)
+ */
+extern void c_put_str(byte attr, cptr str, int row, int col);
+
+/** @fn c_prt(byte attr, cptr str, int row, int col)
+ * @brief Add string "str" with attributes "attr" to screen at row "row"
+ * and column "col", clearing to the end of the row.\n
+ * @param attr Number \n the attribute of the string
+ * @brief Attribute
+ * @param str String \n the string
+ * @brief String
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note (see file util.c)
+ */
+extern void c_prt(byte attr, cptr str, int row, int col);
+
+/** @fn prt(cptr str, int row, int col)
+ * @brief Add white string "str" to screen at row "row" and column "col",
+ * clearing to the end of the row.\n
+ * @param str String \n the string
+ * @brief String
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @param col Number \n the target column on the screen.
+ * @brief Column
+ * @note (see file util.c)
+ */
+extern void prt(cptr str, int row, int col);
+
+/** @fn message_add(byte type, cptr msg, byte color)
+ * @brief Add a message "msg" of type "type" and colour "color" to the
+ * message array.\n
+ * @param type Number \n the type of message. MESSAGE_MSG for regular
+ * messages, MESSAGE_IRC for IRC messages.
+ * @brief Type
+ * @param msg String \n the message.
+ * @brief Message
+ * @param color Number \n the colour of the message (see TERM_ fields).
+ * @brief Colour
+ * @note
+ * Use "msg_print() instead. If you insist on using this function, be
+ * careful.
+ * @note (see file util.c)
+ */
+extern void message_add(byte type, cptr msg, byte color);
+
+/** @fn display_message(int x, int y, int split, byte color, cptr t)
+ * @brief Display a message.\n
+ * @param x Number \n the x-coordinate of the screen where the message starts.
+ * @brief X-coordinate
+ * @param y Number \n the y-coordinate of the screen where the message starts.
+ * @brief Y-coordinate
+ * @param split Number \n the position in the message where it is split. The
+ * rest of the message will not appear.
+ * @brief Split position
+ * @param color Number \n the colour of the message (see TERM_ fields).
+ * @brief Colour
+ * @param t String \n the message.
+ * @brief Message
+ * @note
+ * @note (see file util.c)
+ */
+extern void display_message(int x, int y, int split, byte color, cptr t);
+
+/** @fn clear_from(int row)
+ * @brief Clear part of the screen.\n
+ * @param row Number \n the target row on the screen.
+ * @brief Row
+ * @note
+ * Clear all rows from the starting row to the end of the screen.
+ * @note (see file util.c)
+ */
+extern void clear_from(int row);
+
+/** @fn askfor_aux(char *buf, int len)
+ * @brief Get some input at the cursor location.\n
+ * @param *buf String \n Default string (optional).
+ * @brief String
+ * @param len Number \n the maximum length of the string.
+ * @brief Length of string
+ * @return Boolean \n TRUE if string was entered, otherwise FALSE.
+ * @return *buf \n The entered string.
+ * @note
+ * Assume the buffer is initialized to a default string.\n
+ * Note that this string is often "empty" (see below).\n
+ * The default buffer is displayed in yellow until cleared.\n
+ * Pressing RETURN right away accepts the default entry.\n
+ * Normal chars clear the default and append the char.\n
+ * Backspace clears the default or deletes the final char.\n
+ * ESCAPE clears the buffer and the window and returns FALSE.\n
+ * RETURN accepts the current buffer contents and returns TRUE.
+ * @note (see file util.c)
+ */
+extern bool askfor_aux(char *buf, int len);
+
+/** @fn get_string(cptr prompt, char *buf, int len)
+ * @brief Get a string from the user.\n
+ * @param prompt String \n the prompt, which should take the form "Prompt: "
+ * @brief Prompt
+ * @param *buf String
+ * @brief String
+ * @param len Number \n the maximum length of the string.
+ * @brief Length of string
+ * @return Boolean \n TRUE if string was entered, otherwise FALSE.
+ * @return *buf \n The entered string.
+ * @note
+ * Note that the initial contents of the string is used as
+ * the default response, so be sure to "clear" it if needed.\n\n
+ * We clear the input, and return FALSE, on "ESCAPE".
+ * @note (see file util.c)
+ */
+extern bool get_string(cptr prompt, char *buf, int len);
+
+/** @fn get_check(cptr prompt)
+ * @brief Verify something with the user.\n
+ * @param prompt String \n the prompt, which should take the form "Query? "
+ * @brief Prompt
+ * @return Boolean \n TRUE if "y" or "Y" is entered, otherwise FALSE.
+ * @note
+ * Note that "[y/n]" is appended to the prompt.
+ * @note (see file util.c)
+ */
+extern bool get_check(cptr prompt);
+
+/** @fn get_com(cptr promtp, int *com = 0);
+ * @brief Prompts for a keypress.\n
+ * @param promtp String \n the prompt, which should take the form "Command: "
+ * @brief Prompt
+ * @param *com Number
+ * @brief Command
+ * @return Boolean \n FALSE if "Escape" was pressed, otherwise TRUE.
+ * @return *com \n The entered command.
+ * @note (see file util.c)
+ */
+extern bool get_com_lua @ get_com(cptr promtp, int *com = 0);
+
+/** @fn get_quantity(cptr prompt, s32b max)
+ * @brief Request a "quantity" from the user.\n
+ * @param prompt String \n the prompt
+ * @brief Prompt
+ * @param max Number \n the maximum quantity
+ * @brief Maximum quantity
+ * @return Number \n the returned quantity.
+ * @note
+ * Hack -- allow "command_arg" to specify a quantity\n\n
+ * The quantity is in the range 0 to "max" inclusive. The default is 1. A
+ * letter means the maximum.
+ * @note (see file util.c)
+ */
+extern s32b get_quantity(cptr prompt, s32b max);
+
+/** @fn test_monster_name(cptr name)
+ * @brief Given monster name as string, return the index in r_info array.\n
+ * @param name String \n the monster name.
+ * @brief Monster name
+ * @return Number \n The index of the monster in r_info[], or 0 if the name
+ * does not match a monster.
+ * @note
+ * Name must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter.
+ * @note (see file util.c)
+ */
+extern int test_monster_name(cptr name);
+
+/** @fn test_item_name(cptr name)
+ * @brief Given item name as string, return the index in k_info array.\n
+ * @param name String \n the item name.
+ * @brief Item name
+ * @return Number \n The index of the item in k_info[], or 0 if the name
+ * does not match an item.
+ * @note
+ * Name must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter.
+ * @note (see file util.c)
+ */
+extern int test_item_name(cptr name);
+
+/** @fn luck(int min, int max)
+ * @brief Return a luck number between a certain range.\n
+ * @param min Number \n the minimum luck value returned.
+ * @brief Mimimum
+ * @param max Number \n the maximum luck value returned.
+ * @brief Maximum
+ * @return Number \n The scaled value of player's luck.
+ * @note
+ * Player lucked is cap at a minimum of -30 and maximum of +30 before it is
+ * scaled to the range defined by "min" and "max".
+ * @note (see file xtra1.c)
+ */
+extern int luck(int min, int max);
+
+/** @fn get_player_race_name(int pr, int ps)
+ * @brief Return the player's race (and sub-race) name.\n
+ * @param pr Number \n the player's race. It is an index to race_info[].
+ * @brief Player race
+ * @param ps Number \n the player's subrace, if any. It is an index to
+ * race_mod_info[].
+ * @brief Player subrace
+ * @return String \n The player's full race name.
+ * @note (see file util.c)
+ */
+extern cptr get_player_race_name(int pr, int ps);
+
+/** @fn quit(cptr str)
+ * @brief Quit the game.
+ * @param str String \n an error code or a message which is logged.
+ * @brief String
+ * @note
+ * Exit (ala "exit()"). If 'str' is NULL, do "exit(0)".\n
+ * If 'str' begins with "+" or "-", do "exit(atoi(str))".\n
+ * Otherwise, plog() 'str' and exit with an error code of -1.\n
+ * But always use 'quit_aux', if set, before anything else.
+ * @note (see file z-util.c)
+ */
+extern void quit(cptr str);
+
+/** @fn value_scale(int value, int vmax, int max, int min)
+ * @brief Rescale a value
+ * @param value Number \n the original value
+ * @brief Original value
+ * @param vmax Number \n the maximum the original value can be
+ * @brief Original maximum
+ * @param max Number \n the maximum new value
+ * @brief New maximum
+ * @param min Number \n the minimum new value
+ * @brief New minimum
+ * @return Number \n The rescaled value
+ * @note (see file util.c)
+ */
+extern s32b value_scale(int value, int vmax, int max, int min);
+
+/** @fn text_out_c(byte a, cptr str)
+ * @brief Output text to the screen (in color) or to a file depending on the
+ * selected hook.\n
+ * @param a Number \n the attribute of the string
+ * @brief Attribute
+ * @param str String \n the string
+ * @brief String
+ * @note (see file util.c)
+ */
+extern void text_out_c(byte a, cptr str);
+
+/** @fn text_out(cptr str)
+ * @brief Output text to the screen (in white) or to a file depending on the
+ * selected hook.\n
+ * @param str String \n the string
+ * @brief String
+ * @note (see file util.c)
+ */
+extern void text_out(cptr str);
+
+/** @fn change_option(cptr name, bool value)
+ * @brief Switch an option by only knowing its name.\n
+ * @param name String \n the name of the option.
+ * @brief Option name
+ * @param value Boolean \n the new value of the option.
+ * @brief Option value
+ * @return Boolean \n the old value of the option, or FALSE if "name" is not
+ * an option.
+ * @note (see file cmd4.c)
+ */
+extern bool change_option(cptr name, bool value);
+
+/** @var process_hooks_restart
+ * @brief Number
+ * @note
+ * Set this to TRUE after deleting a C-hook (from a quest) to clean up hook
+ * processing. This is not required for Lua hooks as they are not deleted.
+ */
+extern int process_hooks_restart;
+
+/** @fn dump_hooks(int h_idx)
+ * @brief Print the name and type (language) of all hooks in a hook chain.\n
+ * @param h_idx Number \n the index of the hook chain in the hook_chain array.
+ * If this is -1, then all hook chains will be printed.
+ * @brief Hook chain index
+ * @note (see file plots.c)
+ */
+extern void dump_hooks(int h_idx);
+
+/** @fn add_hook_script(int h_idx, char *script, cptr name)
+ * @brief Add Lua script "name" in file "script" to hook_chain.\n
+ * @param h_idx Number \n the index of the hook chain in the hook_chain array.
+ * @brief Hook chain index
+ * @param *script String \n the name of the Lua script file.
+ * @brief Script filename
+ * @param name String \n the name of the script.
+ * @brief Script name
+ * @note (see file plots.c)
+ */
+extern void add_hook_script(int h_idx, char *script, cptr name);
+
+/** @fn del_hook_name(int h_idx, cptr name)
+ * @brief Delete hook with name "name" from a hook chain.\n
+ * @param h_idx Number \n the index of the hook chain in the hook_chain array.
+ * @brief Hook chain index
+ * @param name String \n the name of the hook to delete
+ * @brief Hook name
+ * @note (see file plots.c)
+ */
+extern void del_hook_name(int h_idx, cptr name);
+
+/** @fn tome_dofile(char *file)
+ * @brief Load a Lua file from lib/scpts.\n
+ * @param *file String \n the name of a Lua file to load.
+ * @brief Filename
+ * @return Boolean \n TRUE if file was loaded, otherwise FALSE.
+ * @note (see file script.c)
+ */
+extern bool tome_dofile(char *file);
+
+/** @fn tome_dofile_anywhere(cptr dir, char *file, bool test_exist = TRUE)
+ * @brief Load a Lua file from any directory.\n
+ * @param dir String \n the name of a Lua file directory
+ * @brief Directory
+ * @param *file String \n the name of a Lua file to load.
+ * @brief Filename
+ * @param test_exist Boolean \n TRUE if a message is printed if the file does
+ * not exist, otherwise FALSE.
+ * @brief Message if file does not exist?
+ * @return Boolean \n TRUE if file was loaded, otherwise FALSE.
+ * @note (see file script.c)
+ */
+extern bool tome_dofile_anywhere(cptr dir, char *file, bool test_exist = TRUE);
+
+/** @fn exec_lua(char *file)
+ * @brief Execute Lua command "file" and return the integer result.\n
+ * @param *file String \n the Lua command to execute.
+ * @brief Command
+ * @return Number \n the result of the Lua command.
+ * @note (see file script.c)
+ */
+extern int exec_lua(char *file);
+
+/** @fn dump_lua_stack(int min, int max)
+ * @brief Display part of the Lua stack.\n
+ * @param min Number \n the starting item of the stack dump.
+ * @brief Start item
+ * @param max Number \n the ending item of the stack dump.
+ * @brief End item
+ * @note (see file script.c)
+ */
+extern void dump_lua_stack(int min, int max);
+
+/** @fn string_exec_lua(char *file)
+ * @brief Execute Lua command "file" and return the string result.\n
+ * @param *file String \n the Lua command to execute.
+ * @brief Command
+ * @return String \n the result of the Lua command.
+ * @note (see file script.c)
+ */
+extern cptr string_exec_lua(char *file);
+
+/** @fn print_hook(cptr str);
+ * @brief Print string "string" to the hook file.\n
+ * @param str String \ the string.
+ * @brief String
+ * @note (see file lua_bind.c)
+ */
+extern void lua_print_hook@print_hook(cptr str);
+
+/* Savefile stuff */
+/** @fn register_savefile(int num)
+ * @brief Add "num" slots to the savefile.\n
+ * @param num Number \n the number of slots to add.
+ * @brief Slots
+ * @note (see file loadsave.c)
+ */
+extern void register_savefile(int num);
+
+/** @fn save_number_key(char *key, s32b val)
+ * @brief Save a key-value combination in the save file.\n
+ * @param *key String \n the key to save.
+ * @brief Key
+ * @param val Number \n the value of the key.
+ * @brief Value
+ * @note
+ * The length of the key is stored first, then the key, then the value.
+ * @note (see file loadsave.c)
+ */
+extern void save_number_key(char *key, s32b val);
+
+
+/* Tables */
+/** @var adj_mag_study[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- Number of half-spells per level
+ */
+extern byte adj_mag_study[100];
+
+/** @var adj_mag_mana[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- extra half-mana-points per level
+ */
+extern byte adj_mag_mana[100];
+
+/** @var adj_mag_fail[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- Minimum failure rate (percentage)
+ */
+extern byte adj_mag_fail[100];
+
+/** @var adj_mag_stat[100]
+ * @brief Number
+ * @note Stat Table (INT/WIS) -- Various things
+ */
+extern byte adj_mag_stat[100];
+
+/** @var adj_chr_gold[100]
+ * @brief Number
+ * @note Stat Table (CHR) -- payment percentages
+ */
+extern byte adj_chr_gold[100];
+
+/** @var adj_int_dev[100]
+ * @brief Number
+ * @note Stat Table (INT) -- Magic devices
+ */
+extern byte adj_int_dev[100];
+
+/** @var adj_wis_sav[100]
+ * @brief Number
+ * @note Stat Table (WIS) -- Saving throw
+ */
+extern byte adj_wis_sav[100];
+
+/** @var adj_dex_dis[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- disarming
+ */
+extern byte adj_dex_dis[100];
+
+/** @var adj_int_dis[100]
+ * @brief Number
+ * @note Stat Table (INT) -- disarming
+ */
+extern byte adj_int_dis[100];
+
+/** @var adj_dex_ta[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- bonus to ac (plus 128)
+ */
+extern byte adj_dex_ta[100];
+
+/** @var adj_str_td[100]
+ * @brief Number
+ * @note Stat Table (STR) -- bonus to dam (plus 128)
+ */
+extern byte adj_str_td[100];
+
+/** @var adj_dex_th[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- bonus to hit (plus 128)
+ */
+extern byte adj_dex_th[100];
+
+/** @var adj_str_th[100]
+ * @brief Number
+ * @note Stat Table (STR) -- bonus to hit (plus 128)
+ */
+extern byte adj_str_th[100];
+
+/** @var adj_str_wgt[100]
+ * @brief Number
+ * @note Stat Table (STR) -- weight limit in deca-pounds
+ */
+extern byte adj_str_wgt[100];
+
+/** @var adj_str_hold[100]
+ * @brief Number
+ * @note Stat Table (STR) -- weapon weight limit in pounds
+ */
+extern byte adj_str_hold[100];
+
+/** @var adj_str_dig[100]
+ * @brief Number
+ * @note Stat Table (STR) -- digging value
+ */
+extern byte adj_str_dig[100];
+
+/** @var adj_str_blow[100]
+ * @brief Number
+ * @note Stat Table (STR) -- help index into the "blow" table
+ */
+extern byte adj_str_blow[100];
+
+/** @var adj_dex_blow[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- index into the "blow" table
+ */
+extern byte adj_dex_blow[100];
+
+/** @var adj_dex_safe[100]
+ * @brief Number
+ * @note Stat Table (DEX) -- chance of avoiding "theft" and "falling"
+ */
+extern byte adj_dex_safe[100];
+
+/** @var adj_con_fix[100]
+ * @brief Number
+ * @note Stat Table (CON) -- base regeneration rate
+ */
+extern byte adj_con_fix[100];
+
+/** @var adj_con_mhp[100]
+ * @brief Number
+ * @note Stat Table (CON) -- extra half-hitpoints per level (plus 128)
+ */
+extern byte adj_con_mhp[100];
+
+/* Repeat stuff */
+/** @fn repeat_push(int what)
+ * @brief Push key "what" onto the end of the repeat_key array.\n
+ * @param what Number \n the key to be repeated.
+ * @brief Key
+ * @note (see file util.c)
+ */
+extern void repeat_push(int what);
+
+/** @fn repeat_pull(int *what = 0)
+ * @brief Pull key from the repeat__key array.\n
+ * @param *what Number
+ * @brief Key
+ * @return Boolean \n TRUE if key was pulled, otherwise FALSE.
+ * @return *what Number \n the key pulled.
+ * @note
+ * This functions starts from an index, which may not be at the start of the
+ * array.
+ * @note (see file util.c)
+ */
+extern bool repeat_pull(int *what = 0);
+
+/** @fn repeat_check(void)
+ * @brief Check if the last command is repeated.
+ * @note
+ * Ignore certain commands: ESC, space, newline.\n
+ * 'n' repeats the last command (index is set to 0).\n
+ * Other commands reset the repeat array (index and count are set to 0).
+ * @note (see file util.c)
+ */
+extern void repeat_check(void);
+
+/** @fn get_count(int number, int max)
+ * @brief Allow the user to select multiple items without pressing '0'.\n
+ * @param number Number \n the default number.
+ * @brief Default
+ * @param max Number \n the maximum value allowed.
+ * @brief Maximum
+ * The user is prompted with "How many?"
+ * @note (see file util.c)
+ */
+extern void get_count(int number, int max);
+
+/** @name Feature Flags
+ * @{
+ */
+/** @def FF1_NO_WALK */
+#define FF1_NO_WALK 0x00000001L
+
+/** @def FF1_NO_VISION */
+#define FF1_NO_VISION 0x00000002L
+
+/** @def FF1_CAN_LEVITATE */
+#define FF1_CAN_LEVITATE 0x00000004L
+
+/** @def FF1_CAN_PASS */
+#define FF1_CAN_PASS 0x00000008L
+
+/** @def FF1_FLOOR */
+#define FF1_FLOOR 0x00000010L
+
+/** @def FF1_WALL */
+#define FF1_WALL 0x00000020L
+
+/** @def FF1_PERMANENT */
+#define FF1_PERMANENT 0x00000040L
+
+/** @def FF1_CAN_FLY */
+#define FF1_CAN_FLY 0x00000080L
+
+/** @def FF1_REMEMBER */
+#define FF1_REMEMBER 0x00000100L
+
+/** @def FF1_NOTICE */
+#define FF1_NOTICE 0x00000200L
+
+/** @def FF1_DONT_NOTICE_RUNNING */
+#define FF1_DONT_NOTICE_RUNNING 0x00000400L
+
+/** @def FF1_CAN_RUN */
+#define FF1_CAN_RUN 0x00000800L
+
+/** @def FF1_DOOR */
+#define FF1_DOOR 0x00001000L
+
+/** @def FF1_SUPPORT_LIGHT */
+#define FF1_SUPPORT_LIGHT 0x00002000L
+
+/** @def FF1_CAN_CLIMB */
+#define FF1_CAN_CLIMB 0x00004000L
+
+/** @def FF1_TUNNELABLE */
+#define FF1_TUNNELABLE 0x00008000L
+
+/** @def FF1_WEB */
+#define FF1_WEB 0x00010000L
+
+/** @def FF1_ATTR_MULTI */
+#define FF1_ATTR_MULTI 0x00020000L
+
+/** @def FF1_SUPPORT_GROWTH */
+#define FF1_SUPPORT_GROWTH 0x00040000L
+/** @} */
+
+
+/* Cave stuff */
+/** @struct cave_type
+ */
+struct cave_type
+{
+ /** @structvar info
+ * @brief Number
+ * @note Hack -- cave flags
+ */
+ u16b info;
+
+ /** @structvar feat
+ * @brief Number
+ * @note Hack -- feature type
+ */
+ byte feat;
+
+ /** @structvar o_idx
+ * @brief Number
+ * @note Object in this grid
+ */
+ s16b o_idx;
+
+ /** @structvar m_idx
+ * @brief Number
+ * @note Monster in this grid
+ */
+ s16b m_idx;
+
+ /** @structvar t_idx
+ * @brief Number
+ * @note trap index (in t_list) or zero
+ */
+ s16b t_idx;
+
+ /** @structvar special
+ * @brief Number
+ */
+ s16b special;
+ /** @structvar special2
+ * @brief Number
+ * @note Special cave info
+ */
+ s16b special2;
+
+ /** @structvar inscription
+ * @brief Number
+ * @note Inscription of the grid
+ */
+ s16b inscription;
+
+ /** @structvar mana
+ * @brief Number
+ * @note Magical energy of the grid
+ */
+ byte mana;
+
+ /** @structvar mimic
+ * @brief Number
+ * @note Feature to mimic
+ */
+ byte mimic;
+
+ /** @structvar effect
+ * @brief Number
+ * @note The lasting effects
+ */
+ s16b effect;
+};
+
+/** @var ANGBAND_SYS
+ * @brief String
+ * @note
+ * Hack -- The special Angband "System Suffix"\n
+ * This variable is used to choose an appropriate "pref-xxx" file
+ */
+extern cptr ANGBAND_SYS;
+
+/** @var ANGBAND_KEYBOARD
+ * @brief String
+ * @note
+ * Hack -- The special Angband "Keyboard Suffix"\n
+ * This variable is used to choose an appropriate macro-trigger definition
+ */
+extern cptr ANGBAND_KEYBOARD;
+
+/** @var ANGBAND_GRAF
+ * @brief String
+ * @note
+ * Hack -- The special Angband "Graphics Suffix"\n
+ * This variable is used to choose an appropriate "graf-xxx" file
+ */
+extern cptr ANGBAND_GRAF;
+
+/** @var ANGBAND_DIR
+ * @brief String
+ * @note
+ * Path name: The main "lib" directory\n
+ * This variable is not actually used anywhere in the code
+ */
+extern cptr ANGBAND_DIR;
+
+/** @var ANGBAND_DIR_APEX
+ * @brief String
+ * @note
+ * High score files (binary)\n
+ * These files may be portable between platforms
+ */
+extern cptr ANGBAND_DIR_APEX;
+
+/** @var ANGBAND_DIR_BONE
+ * @brief String
+ * @note
+ * Bone files for player ghosts (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_BONE;
+
+/** @var ANGBAND_DIR_CORE
+ * @brief String
+ * @note
+ * Core lua system\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_CORE;
+
+/** @var ANGBAND_DIR_DNGN
+ * @brief String
+ * @note
+ * Textual dungeon level definition files\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_DNGN;
+
+/** @var ANGBAND_DIR_DATA
+ * @brief String
+ * @note
+ * Binary image files for the "*_info" arrays (binary)\n
+ * These files are not portable between platforms
+ */
+extern cptr ANGBAND_DIR_DATA;
+
+/** @var ANGBAND_DIR_EDIT
+ * @brief String
+ * @note
+ * Textual template files for the "*_info" arrays (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_EDIT;
+
+/** @var ANGBAND_DIR_FILE
+ * @brief String
+ * @note
+ * Various extra files (ascii)\n
+ * These files may be portable between platforms
+ */
+extern cptr ANGBAND_DIR_FILE;
+
+/** @var ANGBAND_DIR_HELP
+ * @brief String
+ * @note
+ * Help files (normal) for the online help (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_HELP;
+
+/** @var ANGBAND_DIR_INFO
+ * @brief String
+ * @note
+ * Help files (spoilers) for the online help (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_INFO;
+
+/** @var ANGBAND_DIR_MODULES
+ * @brief String
+ * @note
+ * Modules, those subdirectories are half-mirrors of lib/
+ */
+extern cptr ANGBAND_DIR_MODULES;
+
+/** @var ANGBAND_DIR_NOTE
+ * @brief String
+ * @note
+ * Textual template files for the plot files (ascii)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_NOTE;
+
+/** @var ANGBAND_DIR_SAVE
+ * @brief String
+ * @note
+ * Savefiles for current characters (binary)\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_SAVE;
+
+/** @var ANGBAND_DIR_SCPT
+ * @brief String
+ * @note
+ * Scripts.\n
+ * These files are portable between platforms
+ */
+extern cptr ANGBAND_DIR_SCPT;
+
+/** @var ANGBAND_DIR_PREF
+ * @brief String
+ * @note
+ * Default "preference" files (ascii)\n
+ * These files are rarely portable between platforms
+ */
+extern cptr ANGBAND_DIR_PREF;
+
+/** @var ANGBAND_DIR_PATCH
+ * @brief String
+ * @note
+ * Patches, contains one subdir per patch with a patch.lua file
+ * in it and a patch_init() function in it
+ */
+extern cptr ANGBAND_DIR_PATCH;
+
+/** @var ANGBAND_DIR_USER
+ * @brief String
+ * @note
+ * User "preference" files (ascii)\n
+ * These files are rarely portable between platforms
+ */
+extern cptr ANGBAND_DIR_USER;
+
+/** @var ANGBAND_DIR_XTRA
+ * @brief String
+ * @note
+ * Various extra files (binary)\n
+ * These files are rarely portable between platforms
+ */
+extern cptr ANGBAND_DIR_XTRA;
+
+/** @var ANGBAND_DIR_CMOV
+ * @brief String
+ * @note
+ * Cmovie files of entire games (ascii)\n
+ * Apart from possible newline things, likely portable btw platforms
+ */
+extern cptr ANGBAND_DIR_CMOV;
+
+
+/** @fn los(int y1, int x1, int y2, int x2)
+ * @brief Determine if a line of sight can be traced from (x1,y1) to (x2,y2).\n
+ * @param y1 Number \n y-coordinate of the origin.
+ * @brief Origin y-coordinate
+ * @param x1 Number \n x-coordinate of the origin.
+ * @brief Origin x-coordinate
+ * @param y2 Number \n y-coordinate of the target.
+ * @brief Target y-coordinate
+ * @param x2 Number \n x-coordinate of the target.
+ * @brief Target x-coordinate
+ * @return Boolean \n TRUE if origin has line of sight to target, otherwise
+ * FALSE.
+ * @note
+ * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall,
+ * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu.\n\n
+ * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2).\n\n
+ * The LOS begins at the center of the tile (x1,y1) and ends at the center of
+ * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line
+ * passes through must be floor tiles, except for (x1,y1) and (x2,y2).\n\n
+ * We assume that the "mathematical corner" of a non-floor tile does not
+ * block line of sight.\n\n
+ * Because this function uses (short) ints for all calculations, overflow may
+ * occur if dx and dy exceed 90.\n\n
+ * Once all the degenerate cases are eliminated, the values "qx", "qy", and
+ * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that
+ * we can use integer arithmetic.\n\n
+ * We travel from start to finish along the longer axis, starting at the border
+ * between the first and second tiles, where the y offset = .5 * slope, taking
+ * into account the scale factor. See below.\n\n
+ * Also note that this function and the "move towards target" code do NOT
+ * share the same properties. Thus, you can see someone, target them, and
+ * then fire a bolt at them, but the bolt may hit a wall, not them. However,
+ * by clever choice of target locations, you can sometimes throw a "curve".\n\n
+ * Note that "line of sight" is not "reflexive" in all cases.\n\n
+ * Use the "projectable()" routine to test "spell/missile line of sight".\n\n*
+ * Use the "update_view()" function to determine player line-of-sight.\n\n
+ * @note (see file cave.c)
+ */
+extern bool los(int y1, int x1, int y2, int x2);
+$static bool lua_cave_is(cave_type *c_ptr, s32b flag) { return (f_info[c_ptr->feat].flags1 & flag) ? TRUE : FALSE; }
+
+/** @fn cave_is(cave_type *c_ptr, s32b flag);
+ * @brief Determine if cave "c_ptr" has feature "flag".\n
+ * @param *c_ptr cave_type \n the cave.
+ * @brief Cave
+ * @param flag Number \n the required feature flag.
+ * @brief Feature
+ * @return Boolean \n TRUE if the cave features include "flag", otherwise
+ * FALSE.
+ * @note (see file w_util.c)
+ */
+static bool lua_cave_is @ cave_is(cave_type *c_ptr, s32b flag);
+
+/** @fn cave(int y, int x);
+ * @brief Return the type of cave at grid coordinate (x,y).\n
+ * @param y Number \n y-coordinate of grid.
+ * @brief Y-coordinate
+ * @param x Number \n x-coordinate of grid.
+ * @brief X-coordinate
+ * @return cave_type \n The type of cave at grid coordinate (x,y).
+ * @note (see file lua_bind.c)
+ */
+extern cave_type *lua_get_cave @ cave(int y, int x);
+
+/** @fn set_target(int y, int x)
+ * @brief Set grid coordinate (x,y) as the target grid.\n
+ * @param y Number \n y-coordinate of grid.
+ * @brief Y-coordinate
+ * @param x Number \n x-coordinate of grid.
+ * @brief X-coordinate
+ * @note (see file lua_bind.c)
+ */
+extern void set_target(int y, int x);
+
+/** @fn get_target(int dir, int *y = 0, int *x = 0)
+ * @brief Get a target based on direction "dir" from the player.\n
+ * @param dir Number \n dir must be a value from 0 to 9.
+ * @brief Direction
+ * @param *y Number
+ * @brief Target y-coordinate
+ * @param *x Number
+ * @brief Target x-coordinate
+ * @return *y Number \n The y-coordinate of the target.
+ * @return *x Number \n The x-coordinate of the target.
+ * @note
+ * The target is actually 100 grids away in direction "dir". If "dir" is 5,
+ * the actual target, if one is set, is returned.
+ * @note (see file lua_bind.c)
+ */
+extern void get_target(int dir, int *y = 0, int *x = 0);
+
+/** @var m_allow_special[max_r_idx]
+ * @brief Boolean
+ * @note "Special gene" flags for monsters
+ */
+extern bool m_allow_special[max_r_idx];
+
+/** @var k_allow_special[max_k_idx]
+ * @brief Boolean
+ * @note "Special gene" flags for objects
+ */
+extern bool k_allow_special[max_k_idx];
+
+/** @var a_allow_special[max_a_idx]
+ * @brief Boolean
+ * @note "Special gene" flags for artifacts
+ */
+extern bool a_allow_special[max_a_idx];
+
+/** @fn cave_set_feat(int y, int x, int feat)
+ * @brief Change the "feat" flag for a grid, and notice/redraw the grid
+ * @param y Number \n y-coordinate of grid.
+ * @brief Y-coordinate
+ * @param x Number \n x-coordinate of grid.
+ * @brief X-coordinate
+ * @param feat Number \n new set of feature flags.
+ * @brief Features
+ * @note (see file cave.c)
+ */
+extern void cave_set_feat(int y, int x, int feat);
+
+/** @fn show_file(cptr name, cptr what, int line, int mode)
+ * @brief Show a help file.\n
+ * @param name String \n name of the help file.
+ * @brief Filename
+ * @param what String \n hyperlink caption.
+ * @brief Caption
+ * @param line Number \n the line number from where to start the display of
+ * the file.
+ * @brief Starting line
+ * @param mode Number \n *unused*
+ * @brief *Unused*
+ * @return Boolean \n TRUE if file was shown successfully, otherwise FALSE.\n
+ * @note
+ * If the file is not found, the function will search the help, info, and file
+ * directories for the file. If it is still not found, a message is displayed
+ * and the function returns FALSE.\n\n
+ * The file is parsed once to extract colour, tag, and hyperlink
+ * information.\n\n
+ * The file is parse again to show it on the screen.
+ * @note (see file files.c)
+ */
+extern bool show_file(cptr name, cptr what, int line, int mode);
+
+/** @var target_who
+ * @brief Number
+ * @note
+ * If this is -1, the target is the player.\n
+ * If this is 0, there is no target.\n
+ * If this is >0, the target is the monster m_idx[target_who].
+ */
+extern s16b target_who;
+
+/** @var target_col
+ * @brief Number
+ * @note The column of the target grid
+ */
+extern s16b target_col;
+
+/** @var target_row
+ * @brief Number
+ * @note The row of the target grid
+ */
+extern s16b target_row;
+
+/** @var max_bact
+ * @brief Number
+ * @note Maximum building actions
+ */
+extern int max_bact;
+
+/** @var ddd[9]
+ * @brief Number
+ * @note Global array for looping through the "keypad directions"
+ */
+extern s16b ddd[9];
+
+/** @var ddx[10]
+ * @brief Number
+ * @note Global array for converting "keypad direction" into x offsets
+ */
+extern s16b ddx[10];
+
+/** @var ddy[10]
+ * @brief Number
+ * @note Global array for converting "keypad direction" into y offsets
+ */
+extern s16b ddy[10];
+
+/** @var ddx_ddd[9]
+ * @brief Number
+ * @note Global array for optimizing "ddx[ddd[i]]"
+ */
+extern s16b ddx_ddd[9];
+
+/** @var ddy_ddd[9]
+ * @brief Number
+ * @note Global array for optimizing "ddy[ddd[i]]"
+ */
+extern s16b ddy_ddd[9];
+
+
+/* Gen stuff */
+/** @fn get_map_size(bool full_text, char *name, int *ysize = 0, int *xsize = 0)
+ * @brief Return the size of the map in file "name".\n
+ * @param full_text Boolean \n if TRUE, "name" contains the map itself,
+ * otherwise "name" is the name of the map file.
+ * @brief Name is a map?
+ * @param *name String \n the map itself, or the name of the map file.
+ * @brief Map
+ * @param *ysize Number
+ * @brief Maximum y-coordinate
+ * @param *xsize Number
+ * @brief Maximum x-coordinate
+ * @return *ysize Number \n The maximum y-coordinate of the map.
+ * @return *xsize Number \n The maximum x-coordinate of the map.
+ * @note (see file lua_bind.c, init1.c)
+ */
+extern void get_map_size(bool full_text, char *name, int *ysize = 0, int *xsize = 0);
+
+/** @fn load_map(bool full_text, char *name, int *y = 2, int *x = 2)
+ * @brief Load the map in file "name".\n
+ * @param full_text Boolean \n if TRUE, "name" contains the map itself,
+ * otherwise "name" is the name of the map file.
+ * @brief Name is a map?
+ * @param *name String \n the map itself, or the name of the map file.
+ * @brief Map
+ * @param *y Number
+ * @brief Maximum y-coordinate
+ * @param *x Number
+ * @brief Maximum x-coordinate
+ * @return *y Number \n The maximum y-coordinate of the map.
+ * @return *x Number \n The maximum x-coordinate of the map.
+ * @note
+ * The map is loaded and the player is placed at the starting position.
+ * @note (see file lua_bind.c)
+ */
+extern void load_map(bool full_text, char *name, int *y = 2, int *x = 2);
+
+/** @fn alloc_room(int by0, int bx0, int ysize, int xsize, int *y1 = 0, int *x1 = 0, int *y2 = 0, int *x2 = 0)
+ * @brief Allocate the space needed by a room in the room_map array.\n
+ * @param by0 Number \n the y-coordinate of the block to contain the room.
+ * @brief Block y-coordinate
+ * @param bx0 Number \n the x-coordinate of the block to contain the room.
+ * @brief Block x-coordinate
+ * @param ysize Number \n the vertical size (height) of the room.
+ * @brief Room height
+ * @param xsize Number \n the horizontal size (width) of the room.
+ * @brief Room width
+ * @param *y1 Number
+ * @brief Top-right y-coordinate
+ * @param *x1 Number
+ * @brief Top-right x-coordinate
+ * @param *y2 Number
+ * @brief Bottom-left y-coordinate
+ * @param *x2 Number
+ * @brief Bottom-right x-coordinate
+ * @return Boolean \n TRUE if the room was allocated successfully, otherwise
+ * FALSE.
+ * @return *y1 Number \n The y-coordinate of the top left corner.
+ * @return *x1 Number \n The x-coordinate of the top left corner.
+ * @return *y2 Number \n The y-coordinate of the bottom right corner.
+ * @return *x2 Number \n The x-coordinate of the bottom right corner.
+ * @note
+ * Dungeon generation is not something to be messed around with unless you
+ * really, really, really know what you are doing (or you are DarkGod).
+ * @note (see file lua_bind.c, generate.c)
+ */
+extern bool alloc_room(int by0, int bx0, int ysize, int xsize, int *y1 = 0, int *x1 = 0, int *y2 = 0, int *x2 = 0);
+
+/** @var option_ingame_help
+ * @brief Boolean
+ * @note Ingame contextual help flag
+ */
+extern bool option_ingame_help;
+
+/* Misc stuff */
+/** @fn input_box(cptr title, int max);
+ * @brief Create an input box and ask the user a question.\n
+ * @param title String \n the title of the box, which should take the form of
+ * a question. For example, "New name?".
+ * @brief Title
+ * @param max Number \n the maximum length of the response.
+ * @brief Maximum response length
+ * @return String \n The answer to the question.
+ * @note
+ * The input box is placed in the middle of the screen. The default reponse is
+ * blank, and can be up to 79 characters long.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern char *lua_input_box@input_box(cptr title, int max);
+
+/** @fn msg_box(cptr title);
+ * @brief Create a msg box and ask a question.\n
+ * @param title String \n the question.
+ * @brief Question
+ * @return String \n The answer.
+ * @note
+ * The message box is placed in the middle of the screen. The answer is a
+ * single character / key press.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern char lua_msg_box@msg_box(cptr title);
+
+/** @fn rescale(s32b x, s32b max, s32b new_max)
+ * @brief Rescale value "x".\n
+ * @param x Number \n the original value.
+ * @brief Value
+ * @param max Number \n the original maximum that value could have.
+ * @brief Original maximum
+ * @param new_max Number \n the new maximum that value can have.
+ * @brief New maximum
+ * @return Number \n The rescaled value of "x".
+ * @note
+ * There is no error checking here. Please don't set "max" to zero.
+ * @note (see file util.c)
+ */
+extern s32b rescale(s32b x, s32b max, s32b new_max);
+$static const char *player_name_lua(void){return (const char *)player_name;}
+
+/** @fn player_name()
+ * @brief Return the player's name.
+ * @return String \n The player's name.
+ * @note (see file w_util.c)
+ */
+const char *player_name_lua@player_name();
+
+/* Temp files */
+/** @fn make_temp_file();
+ * @brief Create a temporary file.
+ * @note
+ * If a temp file already exists, or one can't be generated, the function
+ * will fail. Otherwise a file is created and the temp_file flag is set to
+ * TRUE.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern void lua_make_temp_file@make_temp_file();
+
+/** @fn close_temp_file();
+ * @brief Close a temporary file.
+ * @note
+ * The file is closed but not released.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern void lua_close_temp_file@close_temp_file();
+
+/** @fn end_temp_file();
+ * @brief End a temporary file.
+ * @note
+ * The file is released and the temp_file flag is set to FALSE.
+ * @note (see file lua_bind.c, util.c)
+ */
+extern void lua_end_temp_file@end_temp_file();
+
+/** @fn get_temp_name();
+ * @brief Return the name of the temporary file.
+ * @return String \n The name of the temporary file.
+ * @note (see file lua_bind.c)
+ */
+extern cptr lua_get_temp_name@get_temp_name();
+
+/* Quarks */
+/** @fn quark_str(s16b num)
+ * @brief Return a quark (inscription) from the quark array.\n
+ * @param num Number \n the index to the quark string array. If this is less
+ * than zero or more than the maximum number of quarks, it is treated as zero.
+ * @brief Quark index
+ * @return String \n The quark.
+ * @note
+ * We use a global array for all inscriptions to reduce the memory
+ * spent maintaining inscriptions. Of course, it is still possible
+ * to run out of inscription memory, especially if too many different
+ * inscriptions are used, but hopefully this will be rare.\n\n
+ * We use dynamic string allocation because otherwise it is necessary
+ * to pre-guess the amount of quark activity. We limit the total
+ * number of quarks, but this is much easier to "expand" as needed.\n\n
+ * Any two items with the same inscription will have the same "quark"
+ * index, which should greatly reduce the need for inscription space.\n\n
+ * Note that "quark zero" is NULL and should not be "dereferenced".
+ * @note (see file util.c)
+ */
+extern cptr quark_str(s16b num);
+
+/** @fn quark_add(cptr str)
+ * @brief Add a quark (inscription) to the quark array.\n
+ * @param str String \n the quark to add to the array.
+ * @brief Quark
+ * @return Number \n The index to the quark array for this quark
+ * @note
+ * The array is searched to see if the quark already exists. If so, the index
+ * to the existing quark is returned.\n
+ * If there is no room, 0 (NULL reference) is returned.
+ * @note (see file util.c)
+ */
+extern s16b quark_add(cptr str);
+
+/* Modules */
+/** @fn module_reset_dir(cptr dir, cptr new_path)
+ * @brief Redirect one of the ToME directories.\n
+ * @param dir String \n the name of the directory (not the full path).
+ * @brief Directory
+ * @param new_path String \n the new path of "dir" under ANGBAND_DIR_MODULES.\n
+ * @brief New path
+ * @note (see file modules.c)
+ */
+extern void module_reset_dir(cptr dir, cptr new_path);
+
+/** @fn scansubdir(cptr dir)
+ * @brief Scan sub-directory "dir".\n
+ * @param dir String \n the sub-directory to scan.
+ * @brief Directory
+ * @note
+ * Nicer wrapper around TERM_XTRA_SCANSUBDIR\n\n
+ * This function sets scansubdir_dir and calls the SCANSUBDIR terminal hook.
+ * @note (see file util.c)
+ */
+extern void scansubdir(cptr dir);
+
+/** @fn file_exist(char *buf)
+ * @brief Check if file "buf" exists.\n
+ * @param *buf String \n the file to be tested.
+ * @brief Filename
+ * @return Boolean \n TRUE if the file exists, otherwise FALSE.
+ * @note (see file loadsave.c)
+ */
+extern bool file_exist(char *buf);
+
+/** @var game_module
+ * @brief String
+ * @note The name of the current game module
+ */
+extern cptr game_module;
+
+/* Input */
+/** @fn get_keymap_dir(char ch)
+ * @brief Get a direction from the keyboard according to the keymap.\n
+ * @param ch String \n the character representing a direction.
+ * @brief Direction
+ * @return Number \n The direction represented by "ch". It will be in the
+ * range 0 to 9.
+ * @note
+ * If "ch" is a number, the number is used. Otherwise the direction is
+ * chosen from the Original or Rogue keymaps.\n
+ * If the direction is 5, it is set to 0.
+ * @note (see file util.c)
+ */
+extern int get_keymap_dir(char ch);
+
+/*
+ * Timers
+ */
+/** @struct timer_type
+ */
+struct timer_type
+{
+ /** @structvar *next
+ * @brief timer_type
+ * @note The next timer in the list
+ */
+ timer_type *next;
+
+ /** @structvar enabled
+ * @brief Boolean
+ * @note Is it currently counting?
+ */
+ bool enabled;
+
+ /** @structvar delay
+ * @brief Number
+ * @note Delay between activations
+ */
+ s32b delay;
+ /** @structvar countdown
+ * @brief Number
+ * @note The current number of turns passed, when it reaches delay it fires
+ */
+ s32b countdown;
+
+ /** @structvar callback
+ * @brief String
+ * @note The lua function to call upon firing(no C callback yet .. maybe)
+ */
+ cptr callback;
+};
+
+/** @fn *new_timer(cptr callback, s32b delay)
+ * @brief Create a timer with callback "callback" and delay "delay".\n
+ * @param callback String \n the callback associated with the timer.
+ * @brief Callback
+ * @param delay Number \n the delay associated with the timer.
+ * @brief Delay
+ * @return timer_type \n The new timer.
+ * @note
+ * The timer's countdown is also set to "delay". The timer is disabled.
+ * @note (see file util.c)
+ */
+extern timer_type *new_timer(cptr callback, s32b delay);
+
+/** @fn del_timer(timer_type *t_ptr)
+ * @brief Delete timer "t_ptr".\n
+ * @param *t_ptr timer_type \n the timer to be deleted.
+ * @brief Timer
+ * @note (see file util.c)
+ */
+extern void del_timer(timer_type *t_ptr);
+
+/*
+ * Lists
+ */
+/** @struct list_type
+ */
+struct list_type
+{
+};
+
+/** @fn create_list(int size);
+ * @dgonly
+ * @brief Create an empty list big enough to store "size" strings.\n
+ * @param size Number \n the number of strings the list will hold.
+ * @brief List size
+ * @return list_type \n The empty list.
+ * @note (see file lua_bind.c)
+ */
+extern list_type *lua_create_list@create_list(int size);
+
+/** @fn delete_list(list_type *, int size);
+ * @dgonly
+ * @brief Delete the list of strings.\n
+ * @param * list_type \n the list of strings.
+ * @brief List
+ * @param size Number \n the number of strings the list holds.
+ * @brief List size
+ * @note
+ * All the strings in the list are deleted first, then the list is deleted.
+ * @note (see file lua_bind.c)
+ */
+extern void lua_delete_list@delete_list(list_type *, int size);
+
+/** @fn add_to_list(list_type *, int idx, cptr str);
+ * @dgonly
+ * @brief Add string "str" to list in position "idx".\n
+ * @param * list_type \n the list of strings.
+ * @brief List
+ * @param idx Number \n the index of the list where the string will be added.
+ * @brief Index
+ * @param str String \n the string to be added.
+ * @brief String
+ * @note
+ * Too bad if there was something in that position already.
+ * You have been warned.
+ * @note (see file lua_bind.c)
+ */
+extern void lua_add_to_list@add_to_list(list_type *, int idx, cptr str);
+
+/** @fn display_list(int y, int x, int h, int w, cptr title, list_type *list, int max, int begin, int sel, byte sel_color);
+ * @dgonly
+ * @brief Display a scrollable boxed list with a selected item.\n
+ * @param y Number \n the y-coordinate of the top-left corner of the box.
+ * @brief Top-left y-coordinate
+ * @param x Number \n the x-coordinate of the top-left corner of the box.
+ * @brief Top-left x-coordinate
+ * @param h Number \n the height of the box.
+ * @brief Height
+ * @param w Number \n the width of the box.
+ * @brief Width
+ * @param title String \n the title for the list box.
+ * @brief Title
+ * @param *list list_type \n the list of strings to be displayed.
+ * @brief List
+ * @param max Number \n the maximum number of strings to display.
+ * @brief Maximum displayed strings
+ * @param begin Number \n the index of the first string to display.
+ * @brief Start index
+ * @param sel Number \n the index of the selected string.
+ * @brief Selected index
+ * @param sel_color Number \n the colour of the selected string.
+ * @brief Selected colour
+ * @note
+ * The title of the list is displayed in TERM_L_BLUE and the unselected strings
+ * are displayed in TERM_WHITE.
+ * @note (see file util.c)
+ */
+extern void lua_display_list@display_list(int y, int x, int h, int w, cptr title, list_type *list, int max, int begin, int sel, byte sel_color);
+
+extern errr file_character(cptr name, bool full);
+extern void calc_bonuses(bool silent);
+
+extern void note_spot(int y, int x);
+extern void lite_spot(int y, int x);
diff --git a/src/variable.c b/src/variable.c
new file mode 100644
index 00000000..453c5209
--- /dev/null
+++ b/src/variable.c
@@ -0,0 +1,1628 @@
+/* File: variable.c */
+
+/* Purpose: Angband variables */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Hack -- Link a copyright message into the executable
+ */
+cptr copyright[5] =
+{
+ "Copyright (c) 1989 James E. Wilson, Robert A. Keoneke",
+ "",
+ "This software may be copied and distributed for educational, research,",
+ "and not for profit purposes provided that this copyright and statement",
+ "are included in all such copies."
+};
+
+int max_macrotrigger = 0;
+char *macro_template = NULL;
+char *macro_modifier_chr;
+char *macro_modifier_name[MAX_MACRO_MOD];
+char *macro_trigger_name[MAX_MACRO_TRIG];
+char *macro_trigger_keycode[2][MAX_MACRO_TRIG];
+
+/*
+ * Executable version
+ */
+byte version_major;
+byte version_minor;
+byte version_patch;
+byte version_extra = VERSION_EXTRA;
+
+/*
+ * Savefile version
+ */
+byte sf_major; /* Savefile's "version_major" */
+byte sf_minor; /* Savefile's "version_minor" */
+byte sf_patch; /* Savefile's "version_patch" */
+byte sf_extra; /* Savefile's "version_extra" */
+u32b vernum;
+
+/*
+ * Savefile information
+ */
+u32b sf_xtra; /* Operating system info */
+u32b sf_when; /* Time when savefile created */
+u16b sf_lives; /* Number of past "lives" with this file */
+u16b sf_saves; /* Number of "saves" during this life */
+
+/*
+ * Run-time arguments
+ */
+bool arg_fiddle; /* Command arg -- Request fiddle mode */
+bool arg_wizard; /* Command arg -- Request wizard mode */
+bool arg_sound; /* Command arg -- Request special sounds */
+bool arg_graphics; /* Command arg -- Request graphics mode */
+bool arg_force_original; /* Command arg -- Request original keyset */
+bool arg_force_roguelike; /* Command arg -- Request roguelike keyset */
+bool arg_bigtile = FALSE; /* Command arg -- Request big tile mode */
+
+/*
+ * Various things
+ */
+
+bool character_generated; /* The character exists */
+bool character_dungeon; /* The character has a dungeon */
+bool character_loaded; /* The character was loaded from a savefile */
+bool character_saved; /* The character was just saved to a savefile */
+
+bool character_icky; /* The game is in an icky full screen mode */
+bool character_xtra; /* The game is in an icky startup mode */
+
+u32b seed_flavor; /* Hack -- consistent object colors */
+u32b seed_town; /* Hack -- consistent town layout */
+u32b seed_dungeon; /* Simulate persisten dungeons */
+
+s16b command_cmd; /* Current "Angband Command" */
+
+s16b command_arg; /* Gives argument of current command */
+s16b command_rep; /* Gives repetition of current command */
+s16b command_dir; /* Gives direction of current command */
+
+s16b command_see; /* See "cmd1.c" */
+s16b command_wrk; /* See "cmd1.c" */
+
+s16b command_new; /* Command chaining from inven/equip view */
+
+s32b energy_use; /* Energy use this turn */
+
+bool create_up_stair; /* Auto-create "up stairs" */
+bool create_down_stair; /* Auto-create "down stairs" */
+
+bool create_up_shaft; /* Auto-create "up shaft" */
+bool create_down_shaft; /* Auto-create "down shaft" */
+
+bool msg_flag; /* Used in msg_print() for "buffering" */
+
+bool alive; /* True if game is running */
+
+bool death; /* True if player has died */
+
+s16b running; /* Current counter for running, if any */
+s16b resting; /* Current counter for resting, if any */
+
+s16b cur_hgt; /* Current dungeon height */
+s16b cur_wid; /* Current dungeon width */
+s16b dun_level; /* Current dungeon level */
+s16b old_dun_level; /* Old dungeon level */
+s16b num_repro; /* Current reproducer count */
+s16b object_level; /* Current object creation level */
+s16b monster_level; /* Current monster creation level */
+
+s32b turn; /* Current game turn */
+s32b old_turn; /* Turn when level began (feelings) */
+
+bool wizard; /* Is the player currently in Wizard mode? */
+
+bool use_sound; /* The "sound" mode is enabled */
+bool use_graphics; /* The "graphics" mode is enabled */
+bool use_bigtile = FALSE;
+byte graphics_mode; /* Current graphics mode */
+
+u16b total_winner; /* Semi-Hack -- Game has been won */
+u16b has_won; /* Semi-Hack -- Game has been won */
+
+u16b panic_save; /* Track some special "conditions" */
+u16b noscore; /* Track various "cheating" conditions */
+
+s16b signal_count; /* Hack -- Count interupts */
+
+bool inkey_base; /* See the "inkey()" function */
+bool inkey_xtra; /* See the "inkey()" function */
+bool inkey_scan; /* See the "inkey()" function */
+bool inkey_flag; /* See the "inkey()" function */
+
+s16b coin_type; /* Hack -- force coin type */
+
+bool opening_chest; /* Hack -- prevent chest generation */
+
+bool shimmer_monsters; /* Hack -- optimize multi-hued monsters */
+bool shimmer_objects; /* Hack -- optimize multi-hued objects */
+
+bool repair_monsters; /* Hack -- optimize detect monsters */
+bool repair_objects; /* Hack -- optimize detect objects */
+
+s16b inven_nxt; /* Hack -- unused */
+bool hack_mind;
+bool hack_corruption;
+int artifact_bias;
+bool is_autosave = FALSE;
+
+s16b inven_cnt; /* Number of items in inventory */
+s16b equip_cnt; /* Number of items in equipment */
+
+s16b o_max = 1; /* Number of allocated objects */
+s16b o_cnt = 0; /* Number of live objects */
+
+s16b m_max = 1; /* Number of allocated monsters */
+s16b m_cnt = 0; /* Number of live monsters */
+
+s16b hack_m_idx = 0; /* Hack -- see "process_monsters()" */
+s16b hack_m_idx_ii = 0;
+bool multi_rew = FALSE;
+char summon_kin_type; /* Hack, by Julian Lighton: summon 'relatives' */
+
+int total_friends = 0;
+s32b total_friend_levels = 0;
+
+int leaving_quest = 0;
+
+
+
+/*
+ * Hack - the destination file for text_out_to_file.
+ */
+FILE *text_out_file = NULL;
+
+
+/*
+ * Hack -- function hook to output (colored) text to the
+ * screen or to a file.
+ */
+void (*text_out_hook)(byte a, cptr str) = text_out_to_screen;
+
+
+/*
+ * Hack -- Where to wrap the text when using text_out(). Use the default
+ * value (for example the screen width) when 'text_out_wrap' is 0.
+ */
+int text_out_wrap = 0;
+
+
+/*
+ * Hack -- Indentation for the text when using text_out().
+ */
+int text_out_indent = 0;
+
+
+/*
+ * The "highscore" file descriptor, if available.
+ */
+int highscore_fd = -1;
+
+
+/*
+ * Software options (set via the '=' command). See "tables.c"
+ */
+
+
+/* Option Set 1 -- User Interface */
+
+bool rogue_like_commands; /* Rogue-like commands */
+bool quick_messages; /* Activate quick messages */
+bool other_query_flag; /* Prompt for various information */
+bool carry_query_flag; /* Prompt before picking things up */
+bool use_old_target; /* Use old target by default */
+bool always_pickup; /* Pick things up by default */
+bool prompt_pickup_heavy; /* Don't pick up the corpses */
+bool always_repeat; /* Repeat obvious commands */
+bool depth_in_feet; /* Show dungeon level in feet */
+
+bool stack_force_notes; /* Merge inscriptions when stacking */
+bool stack_force_costs; /* Merge discounts when stacking */
+
+bool show_labels; /* Show labels in object listings */
+bool show_weights; /* Show weights in object listings */
+bool show_choices; /* Show choices in certain sub-windows */
+bool show_details; /* Show details in certain sub-windows */
+
+bool ring_bell; /* Ring the bell (on errors, etc) */
+bool use_color; /* Use color if possible (slow) */
+
+bool show_inven_graph; /* Show graphics in inventory */
+bool show_equip_graph; /* Show graphics in equip list */
+bool show_store_graph; /* Show graphics in store */
+
+
+
+/* Option Set 2 -- Disturbance */
+
+bool find_ignore_stairs; /* Run past stairs */
+bool find_ignore_doors; /* Run through open doors */
+bool find_cut; /* Run past known corners */
+bool find_examine; /* Run into potential corners */
+
+bool disturb_move; /* Disturb whenever any monster moves */
+bool disturb_near; /* Disturb whenever viewable monster moves */
+bool disturb_panel; /* Disturb whenever map panel changes */
+bool disturb_detect; /* Disturb whenever leaving trap-detected area */
+bool disturb_state; /* Disturn whenever player state changes */
+bool disturb_minor; /* Disturb whenever boring things happen */
+bool disturb_other; /* Disturb whenever various things happen */
+
+bool alert_hitpoint; /* Alert user to critical hitpoints */
+bool alert_failure; /* Alert user to various failures */
+bool last_words; /* Get last words upon dying */
+bool speak_unique; /* Speaking uniques + shopkeepers */
+bool small_levels; /* Allow unusually small dungeon levels */
+bool empty_levels; /* Allow empty 'arena' levels */
+bool always_small_level; /* Small levels */
+#if 0 /* It's controlled by insanity -- pelpel */
+bool flavored_attacks; /* Show silly messages when fighting */
+#endif
+bool player_symbols; /* Use varying symbols for the player char */
+bool plain_descriptions; /* Plain object descriptions */
+bool stupid_monsters; /* Monsters use old AI */
+bool auto_destroy; /* Known worthless items are destroyed without confirmation */
+bool confirm_stairs; /* Prompt before staircases... */
+bool wear_confirm; /* Confirm before putting on known cursed items */
+bool disturb_pets; /* Pets moving nearby disturb us */
+
+
+/* Option Set 3 -- Game-Play */
+
+bool auto_haggle; /* Auto-haggle in stores */
+
+bool auto_scum; /* Auto-scum for good levels */
+
+bool stack_allow_items; /* Allow weapons and armor to stack */
+bool stack_allow_wands; /* Allow wands/staffs/rods to stack */
+
+bool expand_look; /* Expand the power of the look command */
+bool expand_list; /* Expand the power of the list commands */
+
+bool view_perma_grids; /* Map remembers all perma-lit grids */
+bool view_torch_grids; /* Map remembers all torch-lit grids */
+
+bool monster_lite; /* Allow some monsters to carry light */
+
+bool dungeon_align; /* Generate dungeons with aligned rooms */
+bool dungeon_stair; /* Generate dungeons with connected stairs */
+
+bool flow_by_sound; /* Monsters track new player location */
+bool flow_by_smell; /* Monsters track old player location */
+
+bool track_follow; /* Monsters follow the player */
+bool track_target; /* Monsters target the player */
+
+bool smart_learn; /* Monsters learn from their mistakes */
+bool smart_cheat; /* Monsters exploit player weaknesses */
+
+
+/* Option Set 4 -- Efficiency */
+
+bool view_reduce_lite; /* Reduce lite-radius when running */
+bool view_reduce_view; /* Reduce view-radius in town */
+
+bool avoid_abort; /* Avoid checking for user abort */
+bool avoid_shimmer; /* Avoid processing extra shimmering */
+bool avoid_other; /* Avoid processing special colors */
+
+bool flush_failure; /* Flush input on any failure */
+bool flush_disturb; /* Flush input on disturbance */
+bool flush_command; /* Flush input before every command */
+
+bool fresh_before; /* Flush output before normal commands */
+bool fresh_after; /* Flush output after normal commands */
+bool fresh_message; /* Flush output after all messages */
+
+bool compress_savefile; /* Compress messages in savefiles */
+
+bool hilite_player; /* Hilite the player with the cursor */
+
+bool view_yellow_lite; /* Use special colors for torch-lit grids */
+bool view_bright_lite; /* Use special colors for 'viewable' grids */
+
+bool view_granite_lite; /* Use special colors for wall grids (slow) */
+bool view_special_lite; /* Use special colors for floor grids (slow) */
+
+/* Option set 5 -- Testing */
+
+bool testing_stack; /* Test the stacking code */
+
+bool testing_carry; /* Test the carrying code */
+
+
+/* Cheating options */
+
+bool cheat_peek; /* Peek into object creation */
+bool cheat_hear; /* Peek into monster creation */
+bool cheat_room; /* Peek into dungeon creation */
+bool cheat_xtra; /* Peek into something else */
+bool cheat_know; /* Know complete monster info */
+bool cheat_live; /* Allow player to avoid death */
+
+
+/* Special options */
+
+byte hitpoint_warn; /* Hitpoint warning (0 to 9) */
+
+byte delay_factor; /* Delay factor (0 to 9) */
+
+bool autosave_l; /* Autosave before entering new levels */
+bool autosave_t; /* Timed autosave */
+s16b autosave_freq; /* Autosave frequency */
+
+
+/*
+ * Dungeon variables
+ */
+
+s16b feeling; /* Most recent feeling */
+s16b rating; /* Level's current rating */
+
+bool good_item_flag; /* True if "Artifact" on this level */
+
+bool closing_flag; /* Dungeon is closing */
+
+/*
+ * Dungeon size info
+ */
+
+s16b max_panel_rows, max_panel_cols;
+s16b panel_row_min, panel_row_max;
+s16b panel_col_min, panel_col_max;
+s16b panel_col_prt, panel_row_prt;
+
+/*
+ * Dungeon graphics info
+ * Why the first two are byte and the rest s16b???
+ */
+byte feat_wall_outer = FEAT_WALL_OUTER; /* Outer wall of rooms */
+byte feat_wall_inner = FEAT_WALL_INNER; /* Inner wall of rooms */
+s16b floor_type[100]; /* Dungeon floor */
+s16b fill_type[100]; /* Dungeon filler */
+
+/*
+ * Targetting variables
+ */
+s16b target_who;
+s16b target_col;
+s16b target_row;
+
+/*
+ * Health bar variable -DRS-
+ */
+s16b health_who;
+
+/*
+ * Monster race to track
+ */
+s16b monster_race_idx;
+s16b monster_ego_idx;
+
+/*
+ * Object to track
+ */
+object_type *tracked_object;
+
+
+
+/*
+ * User info
+ */
+int player_uid;
+int player_euid;
+int player_egid;
+
+/*
+ * Current player's character name
+ */
+char player_name[32];
+
+/*
+ * Stripped version of "player_name"
+ */
+char player_base[32];
+
+/*
+ * What killed the player
+ */
+char died_from[80];
+
+/*
+ * Hack -- Textual "history" for the Player
+ */
+char history[4][60];
+
+/*
+ * Buffer to hold the current savefile name
+ */
+char savefile[1024];
+bool savefile_setuid = TRUE;
+
+
+/*
+ * Array of grids lit by player lite (see "cave.c")
+ */
+s16b lite_n;
+s16b lite_y[LITE_MAX];
+s16b lite_x[LITE_MAX];
+
+/*
+ * Array of grids viewable to the player (see "cave.c")
+ */
+s16b view_n;
+byte view_y[VIEW_MAX];
+byte view_x[VIEW_MAX];
+
+/*
+ * Array of grids for use by various functions (see "cave.c")
+ */
+s16b temp_n;
+byte temp_y[TEMP_MAX];
+byte temp_x[TEMP_MAX];
+
+
+/*
+ * Number of active macros.
+ */
+s16b macro__num;
+
+/*
+ * Array of macro patterns [MACRO_MAX]
+ */
+cptr *macro__pat;
+
+/*
+ * Array of macro actions [MACRO_MAX]
+ */
+cptr *macro__act;
+
+/*
+ * Array of macro types [MACRO_MAX]
+ */
+bool *macro__cmd;
+
+/*
+ * Current macro action [1024]
+ */
+char *macro__buf;
+
+
+/*
+ * The number of quarks
+ */
+s16b quark__num;
+
+/*
+ * The pointers to the quarks [QUARK_MAX]
+ */
+cptr *quark__str;
+
+
+/*
+ * The next "free" index to use
+ */
+u16b message__next;
+
+/*
+ * The index of the oldest message (none yet)
+ */
+u16b message__last;
+
+/*
+ * The next "free" offset
+ */
+u16b message__head;
+
+/*
+ * The offset to the oldest used char (none yet)
+ */
+u16b message__tail;
+
+/*
+ * The array of offsets, by index [MESSAGE_MAX]
+ */
+u16b *message__ptr;
+
+/*
+ * The array of colors, by index [MESSAGE_MAX]
+ */
+byte *message__color;
+
+/*
+ * The array of type, by index [MESSAGE_MAX]
+ */
+byte *message__type;
+
+/*
+ * The array of message counts, by index [MESSAGE_MAX]
+ */
+u16b *message__count;
+
+/*
+ * The array of chars, by offset [MESSAGE_BUF]
+ */
+char *message__buf;
+
+
+/*
+ * The array of normal options
+ */
+u32b option_flag[8];
+u32b option_mask[8];
+
+
+/*
+ * The array of window options
+ */
+u32b window_flag[8];
+u32b window_mask[8];
+
+
+/*
+ * The array of window pointers
+ */
+term *angband_term[ANGBAND_TERM_MAX];
+
+
+/*
+ * Standard window names
+ */
+char angband_term_name[ANGBAND_TERM_MAX][80] =
+{
+ "ToME",
+ "Mirror",
+ "Recall",
+ "Choice",
+ "Term-4",
+ "Term-5",
+ "Term-6",
+ "Term-7"
+};
+
+
+/*
+ * Global table of color definitions
+ */
+byte angband_color_table[256][4] =
+{
+ {0x00, 0x00, 0x00, 0x00}, /* TERM_DARK */
+ {0x00, 0xFF, 0xFF, 0xFF}, /* TERM_WHITE */
+ {0x00, 0x80, 0x80, 0x80}, /* TERM_SLATE */
+ {0x00, 0xFF, 0x80, 0x00}, /* TERM_ORANGE */
+ {0x00, 0xC0, 0x00, 0x00}, /* TERM_RED */
+ {0x00, 0x00, 0x80, 0x40}, /* TERM_GREEN */
+ {0x00, 0x00, 0x00, 0xFF}, /* TERM_BLUE */
+ {0x00, 0x80, 0x40, 0x00}, /* TERM_UMBER */
+ {0x00, 0x40, 0x40, 0x40}, /* TERM_L_DARK */
+ {0x00, 0xC0, 0xC0, 0xC0}, /* TERM_L_WHITE */
+ {0x00, 0xFF, 0x00, 0xFF}, /* TERM_VIOLET */
+ {0x00, 0xFF, 0xFF, 0x00}, /* TERM_YELLOW */
+ {0x00, 0xFF, 0x00, 0x00}, /* TERM_L_RED */
+ {0x00, 0x00, 0xFF, 0x00}, /* TERM_L_GREEN */
+ {0x00, 0x00, 0xFF, 0xFF}, /* TERM_L_BLUE */
+ {0x00, 0xC0, 0x80, 0x40} /* TERM_L_UMBER */
+};
+
+
+#ifdef SUPPORT_GAMMA
+
+/*
+ * Gamma correction - gamma_val == (int)(256 / gamma)
+ * The value of 0 means no gamma correction (== 1.0)
+ */
+u16b gamma_val;
+
+#endif /* SUPPORT_GAMMA */
+
+
+/*
+ * Standard sound names
+ */
+char angband_sound_name[SOUND_MAX][16] =
+{
+ "",
+ "hit",
+ "miss",
+ "flee",
+ "drop",
+ "kill",
+ "level",
+ "death",
+ "study",
+ "teleport",
+ "shoot",
+ "quaff",
+ "zap",
+ "walk",
+ "tpother",
+ "hitwall",
+ "eat",
+ "store1",
+ "store2",
+ "store3",
+ "store4",
+ "dig",
+ "opendoor",
+ "shutdoor",
+ "tplevel",
+ "scroll",
+ "buy",
+ "sell",
+ "warn",
+ "rocket",
+ "n_kill",
+ "u_kill",
+ "quest",
+ "heal",
+ "x_heal",
+ "bite",
+ "claw",
+ "m_spell",
+ "summon",
+ "breath",
+ "ball",
+ "m_heal",
+ "atkspell",
+ "evil",
+ "touch",
+ "sting",
+ "crush",
+ "slime",
+ "wail",
+ "winner",
+ "fire",
+ "acid",
+ "elec",
+ "cold",
+ "illegal",
+ "fail",
+ "wakeup",
+ "invuln",
+ "fall",
+ "pain",
+ "destitem",
+ "moan",
+ "show",
+ "unused",
+ "explode",
+};
+
+
+/*
+ * The array of "cave grids" [MAX_WID][MAX_HGT].
+ * Not completely allocated, that would be inefficient
+ * Not completely hardcoded, that would overflow memory
+ */
+cave_type *cave[MAX_HGT];
+
+/*
+ * The array of dungeon items [max_o_idx]
+ */
+object_type *o_list;
+
+/*
+ * The array of dungeon monsters [max_m_idx]
+ */
+monster_type *m_list;
+
+/*
+ * The array of to keep monsters [max_m_idx]
+ */
+monster_type *km_list;
+
+
+/*
+ * Maximum number of towns
+ */
+u16b max_towns;
+u16b max_real_towns;
+
+/*
+ * The towns [max_towns]
+ */
+town_type *town_info;
+
+/*
+ * The size of "alloc_kind_table" (at most max_k_idx * 4)
+ */
+s16b alloc_kind_size;
+
+/*
+ * The entries in the "kind allocator table"
+ */
+alloc_entry *alloc_kind_table;
+
+/*
+ * The flag to tell if alloc_kind_table contains valid entries
+ * for normal (i.e. kind_is_legal) object allocation
+ */
+bool alloc_kind_table_valid = FALSE;
+
+
+/*
+ * The size of "alloc_race_table" (at most max_r_idx)
+ */
+s16b alloc_race_size;
+
+/*
+ * The entries in the "race allocator table"
+ */
+alloc_entry *alloc_race_table;
+
+
+/*
+ * Specify attr/char pairs for visual special effects
+ * Be sure to use "index & 0x7F" to avoid illegal access
+ */
+byte misc_to_attr[256];
+char misc_to_char[256];
+
+
+/*
+ * Specify attr/char pairs for inventory items (by tval)
+ * Be sure to use "index & 0x7F" to avoid illegal access
+ */
+byte tval_to_attr[128];
+char tval_to_char[128];
+
+
+/*
+ * Keymaps for each "mode" associated with each keypress.
+ */
+cptr keymap_act[KEYMAP_MODES][256];
+
+
+
+/*** Player information ***/
+
+/*
+ * Static player info record
+ */
+player_type p_body;
+
+/*
+ * Pointer to the player info
+ */
+player_type *p_ptr = &p_body;
+
+/*
+ * Pointer to the player tables
+ * (sex, race, race mod, class, magic)
+ */
+player_sex *sp_ptr;
+player_race *rp_ptr;
+player_race_mod *rmp_ptr;
+player_class *cp_ptr;
+player_spec *spp_ptr;
+
+
+/*
+ * More spell info
+ */
+u32b alchemist_known_egos[32];
+u32b alchemist_known_artifacts[6];
+u32b alchemist_gained;
+
+
+/*
+ * Calculated base hp values for player at each level,
+ * store them so that drain life + restore life does not
+ * affect hit points. Also prevents shameless use of backup
+ * savefiles for hitpoint acquirement.
+ */
+s16b player_hp[PY_MAX_LEVEL];
+
+/*
+ * The alchemy recipe arrays
+ */
+header *al_head;
+alchemist_recipe *alchemist_recipes;
+char *al_name;
+artifact_select_flag *a_select_flags;
+
+/*
+ * The vault generation arrays
+ */
+header *v_head;
+vault_type *v_info;
+char *v_name;
+char *v_text;
+
+/*
+ * The terrain feature arrays
+ */
+header *f_head;
+feature_type *f_info;
+char *f_name;
+char *f_text;
+
+/*
+ * The object kind arrays
+ */
+header *k_head;
+object_kind *k_info;
+char *k_name;
+char *k_text;
+
+/*
+ * The artifact arrays
+ */
+header *a_head;
+artifact_type *a_info;
+char *a_name;
+char *a_text;
+
+/*
+ * The item set arrays
+ */
+header *set_head;
+set_type *set_info;
+char *set_name;
+char *set_text;
+
+/*
+ * The ego-item arrays
+ */
+header *e_head;
+ego_item_type *e_info;
+char *e_name;
+char *e_text;
+
+/*
+ * The randart arrays
+ */
+header *ra_head;
+randart_part_type *ra_info;
+randart_gen_type ra_gen[30];
+
+/* jk */
+/* the trap-arrays */
+header *t_head;
+trap_type *t_info;
+char *t_name;
+char *t_text;
+
+/*
+ * The monster race arrays
+ */
+header *r_head;
+monster_race *r_info;
+char *r_name;
+char *r_text;
+
+/*
+ * The monster ego race arrays
+ */
+header *re_head;
+monster_ego *re_info;
+char *re_name;
+
+/*
+ * The dungeon types arrays
+ */
+header *d_head;
+dungeon_info_type *d_info;
+char *d_name;
+char *d_text;
+
+/*
+ * Player abilities arrays
+ */
+header *ab_head;
+ability_type *ab_info;
+char *ab_name;
+char *ab_text;
+
+/*
+ * Player skills arrays
+ */
+header *s_head;
+skill_type *s_info;
+char *s_name;
+char *s_text;
+
+/*
+ * Player race arrays
+ */
+header *rp_head;
+player_race *race_info;
+char *rp_name;
+char *rp_text;
+
+/*
+ * Player mod race arrays
+ */
+header *rmp_head;
+player_race_mod *race_mod_info;
+char *rmp_name;
+char *rmp_text;
+
+/*
+ * Player class arrays
+ */
+header *c_head;
+player_class *class_info;
+char *c_name;
+char *c_text;
+meta_class_type *meta_class_info;
+
+/*
+ * The wilderness features arrays
+ */
+header *wf_head;
+wilderness_type_info *wf_info;
+char *wf_name;
+char *wf_text;
+int wildc2i[256];
+
+/*
+ * The store/building types arrays
+ */
+header *st_head;
+store_info_type *st_info;
+char *st_name;
+/* char *st_text; */
+
+/*
+ * The building actions types arrays
+ */
+header *ba_head;
+store_action_type *ba_info;
+char *ba_name;
+/* char *ba_text; */
+
+/*
+ * The owner types arrays
+ */
+header *ow_head;
+owner_type *ow_info;
+char *ow_name;
+/* char *ow_text; */
+
+/*
+ * The dungeon types arrays
+ */
+header *d_head;
+dungeon_info_type *d_info;
+char *d_name;
+char *d_text;
+
+/*
+ * Hack -- The special Angband "System Suffix"
+ * This variable is used to choose an appropriate "pref-xxx" file
+ */
+cptr ANGBAND_SYS = "xxx";
+
+/*
+ * Hack -- The special Angband "Keyboard Suffix"
+ * This variable is used to choose an appropriate macro-trigger definition
+ */
+#ifdef JP
+cptr ANGBAND_KEYBOARD = "JAPAN";
+#else
+cptr ANGBAND_KEYBOARD = "0";
+#endif
+
+/*
+ * Hack -- The special Angband "Graphics Suffix"
+ * This variable is used to choose an appropriate "graf-xxx" file
+ */
+cptr ANGBAND_GRAF = "old";
+
+/*
+ * Path name: The main "lib" directory
+ * This variable is not actually used anywhere in the code
+ */
+cptr ANGBAND_DIR;
+
+/*
+ * High score files (binary)
+ * These files may be portable between platforms
+ */
+cptr ANGBAND_DIR_APEX;
+
+/*
+ * Bone files for player ghosts (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_BONE;
+
+/*
+ * Core lua system
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_CORE;
+
+/*
+ * Textual dungeon level definition files
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_DNGN;
+
+/*
+ * Binary image files for the "*_info" arrays (binary)
+ * These files are not portable between platforms
+ */
+cptr ANGBAND_DIR_DATA;
+
+/*
+ * Textual template files for the "*_info" arrays (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_EDIT;
+
+/*
+ * Various extra files (ascii)
+ * These files may be portable between platforms
+ */
+cptr ANGBAND_DIR_FILE;
+
+/*
+ * Help files (normal) for the online help (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_HELP;
+
+/*
+ * Help files (spoilers) for the online help (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_INFO;
+
+/*
+ * Modules, those subdirectories are half-mirrors of lib/
+ */
+cptr ANGBAND_DIR_MODULES;
+
+/*
+ * Patches, contains one subdir per patch with a patch.lua file
+ * in it and a patch_init() function in it
+ */
+cptr ANGBAND_DIR_PATCH;
+
+/*
+ * Textual template files for the plot files (ascii)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_NOTE;
+
+/*
+ * Savefiles for current characters (binary)
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_SAVE;
+
+/*
+ * Scripts.
+ * These files are portable between platforms
+ */
+cptr ANGBAND_DIR_SCPT;
+
+/*
+ * Default "preference" files (ascii)
+ * These files are rarely portable between platforms
+ */
+cptr ANGBAND_DIR_PREF;
+
+/*
+ * User "preference" files (ascii)
+ * These files are rarely portable between platforms
+ */
+cptr ANGBAND_DIR_USER;
+
+/*
+ * Various extra files (binary)
+ * These files are rarely portable between platforms
+ */
+cptr ANGBAND_DIR_XTRA;
+
+/*
+ * Cmovie files of entire games (ascii)
+ * Apart from possible newline things, likely portable btw platforms
+ */
+
+cptr ANGBAND_DIR_CMOV;
+
+/*
+ * Some variables values are created on the fly XXX XXX
+ */
+
+char pref_tmp_value[8];
+
+
+
+/*
+ * Total Hack -- allow all items to be listed (even empty ones)
+ * This is only used by "do_cmd_inven_e()" and is cleared there.
+ */
+bool item_tester_full;
+
+
+/*
+ * Here is a "pseudo-hook" used during calls to "get_item()" and
+ * "show_inven()" and "show_equip()", and the choice window routines.
+ */
+byte item_tester_tval;
+
+
+/*
+ * Here is a "hook" used during calls to "get_item()" and
+ * "show_inven()" and "show_equip()", and the choice window routines.
+ */
+bool (*item_tester_hook)(object_type*);
+
+
+
+/*
+ * Current "comp" function for ang_sort()
+ */
+bool (*ang_sort_comp)(vptr u, vptr v, int a, int b);
+
+
+/*
+ * Current "swap" function for ang_sort()
+ */
+void (*ang_sort_swap)(vptr u, vptr v, int a, int b);
+
+
+
+/*
+ * Hack -- function hooks to restrict "get_mon_num_prep()" function
+ */
+bool (*get_mon_num_hook)(int r_idx);
+bool (*get_mon_num2_hook)(int r_idx);
+
+
+/*
+ * Hack -- function hook to restrict "get_obj_num_prep()" function
+ */
+bool (*get_obj_num_hook)(int k_idx);
+
+
+/* Hack, monk armour */
+bool monk_armour_aux;
+bool monk_notify_aux;
+
+#ifdef ALLOW_EASY_OPEN /* TNB */
+bool easy_open = TRUE;
+#endif /* ALLOW_EASY_OPEN -- TNB */
+
+#ifdef ALLOW_EASY_DISARM /* TNB */
+bool easy_disarm = TRUE;
+#endif /* ALLOW_EASY_DISARM -- TNB */
+
+bool easy_tunnel = FALSE;
+
+
+/*
+ * Maximum size of the wilderness map
+ */
+u16b max_wild_x;
+u16b max_wild_y;
+
+/*
+ * Wilderness map
+ */
+wilderness_map **wild_map;
+
+
+/*
+ * Maximum number of skills in s_info.txt
+ */
+u16b old_max_s_idx = 0;
+u16b max_s_idx;
+
+/*
+ * Maximum number of abilities in ab_info.txt
+ */
+u16b max_ab_idx;
+
+/*
+ * Maximum number of monsters in r_info.txt
+ */
+u16b max_r_idx;
+
+/*
+ * Maximum number of ego monsters in re_info.txt
+ */
+u16b max_re_idx;
+
+/*
+ * Maximum number of items in k_info.txt
+ */
+u16b max_k_idx;
+
+/*
+ * Maximum number of vaults in v_info.txt
+ */
+u16b max_v_idx;
+
+/*
+ * Maximum number of terrain features in f_info.txt
+ */
+u16b max_f_idx;
+
+/*
+ * Maximum number of alchemist recipies in al_info.txt
+ */
+u16b max_al_idx;
+
+/*
+ * Maximum number of artifacts in a_info.txt
+ */
+u16b max_a_idx;
+
+/*
+ * Maximum number of ego-items in e_info.txt
+ */
+u16b max_e_idx;
+
+/*
+ * Maximum number of randarts in ra_info.txt
+ */
+u16b max_ra_idx;
+
+/*
+ * Maximum number of dungeon types in d_info.txt
+ */
+u16b max_d_idx;
+
+/*
+ * Maximum number of stores types in st_info.txt
+ */
+u16b max_st_idx;
+
+/*
+ * Item sets
+ */
+s16b max_set_idx = 1;
+
+/*
+ * Maximum number of players info in p_info.txt
+ */
+u16b max_rp_idx;
+u16b max_rmp_idx;
+u16b max_c_idx;
+u16b max_mc_idx;
+
+/*
+ * Maximum number of actions types in ba_info.txt
+ */
+u16b max_ba_idx;
+
+/*
+ * Maximum number of owner types in ow_info.txt
+ */
+u16b max_ow_idx;
+
+/*
+ * Maximum number of objects in the level
+ */
+u16b max_o_idx;
+
+/*
+ * Maximum number of monsters in the level
+ */
+u16b max_m_idx;
+
+/*
+ * Maximum number of traps in tr_info.txt
+ */
+u16b max_t_idx;
+
+/*
+ * Maximum number of wilderness features in wf_info.txt
+ */
+u16b max_wf_idx;
+
+/*
+ * Flags for initialization
+ */
+int init_flags;
+
+/* True if on an ambush */
+bool ambush_flag;
+
+/* True if on fated level */
+bool fate_flag;
+
+/* No breeders */
+u16b no_breeds;
+
+/* Carried monsters can't take the damage if this is them which attack the player */
+bool carried_monster_hit = FALSE;
+
+/*
+ * Random artifacts.
+ */
+random_artifact random_artifacts[MAX_RANDARTS];
+/* These three used to be constants but now are set by modules */
+s32b RANDART_WEAPON;
+s32b RANDART_ARMOR;
+s32b RANDART_JEWEL;
+
+/*
+ * Current bounties. An array of tuples of two, with the first being the
+ * r_idx of the monster, and the second the monster's worth.
+ */
+s16b bounties[MAX_BOUNTIES][2];
+
+/*
+ * Spell description
+ */
+bool info_spell = FALSE;
+char spell_txt[50];
+
+/*
+ * Random spells.
+ */
+random_spell random_spells[MAX_SPELLS];
+s16b spell_num;
+
+/*
+ * Runecrafter's selfmade spells.
+ */
+rune_spell rune_spells[MAX_RUNES];
+s16b rune_num;
+
+/*
+ * Fate.
+ */
+fate fates[MAX_FATES];
+
+/*
+ * Vanilla town.
+ */
+byte vanilla_town = FALSE;
+
+/*
+ * Which dungeon ?
+ * 0 = Wilderness
+ * 1 = Mirkwood
+ * 2 = Mordor
+ * 3 = Angband
+ * 4 = Barrow Downs
+ * 5 = Mount Doom
+ * 6 = Nether Realm
+ * etc. (see d_info.txt)
+ */
+byte dungeon_type;
+s16b *max_dlv;
+
+/*
+ * Number of total bounties the player had had.
+ */
+u32b total_bounties;
+
+/* The Doppleganger index in m_list */
+s16b doppleganger;
+
+/* To allow wilderness encounters */
+bool generate_encounter;
+
+/* Permanent dungeons ? */
+bool permanent_levels;
+
+/* Autoroler */
+bool autoroll;
+
+/* Point based */
+bool point_based;
+
+/* Maximize, preserve, special levels, ironman_rooms */
+bool maximize, preserve, special_lvls, ironman_rooms;
+
+/* In inventory option window, just erase the letters,
+ * rather that displaying the list without the invalid
+ * selections */
+bool inventory_no_move;
+
+/* Notes patch */
+bool take_notes, auto_notes;
+
+/*
+ * Such an ugly hack ...
+ */
+bool *m_allow_special;
+bool *k_allow_special;
+bool *a_allow_special;
+
+/*
+ * Gives a random object to newly created characters
+ */
+bool rand_birth;
+
+/*
+ * Fast autoroller
+ */
+bool fast_autoroller;
+
+/*
+ * Which monsters are allowed ?
+ */
+bool joke_monsters;
+
+/*
+ * How will mana staf & weapons of life act
+ */
+bool munchkin_multipliers = TRUE;
+
+/*
+ * Center view
+ */
+bool center_player = FALSE;
+
+/*
+ * Plots
+ */
+s16b plots[MAX_PLOTS];
+
+/*
+ * Random quest
+ */
+random_quest random_quests[MAX_RANDOM_QUEST];
+
+/*
+ * Show exp left
+ */
+bool exp_need;
+
+/*
+ * Auto load old colors;
+ */
+bool autoload_old_colors;
+
+/*
+ * Fated ?
+ */
+bool fate_option;
+
+/*
+ * Special levels
+ */
+bool *special_lvl[MAX_DUNGEON_DEPTH];
+bool generate_special_feeling = FALSE;
+
+/*
+ * Auto more
+ */
+bool auto_more;
+
+/*
+ * Dungeon flags
+ */
+u32b dungeon_flags1;
+u32b dungeon_flags2;
+
+/*
+ * The last character displayed
+ */
+birther previous_char;
+
+/*
+ * Race histories
+ */
+hist_type *bg;
+int max_bg_idx;
+
+/*
+ * Powers
+ */
+s16b power_max = POWER_MAX_INIT;
+power_type *powers_type;
+
+/*
+ * Variable savefile stuff
+ */
+s32b extra_savefile_parts = 0;
+
+/*
+ * Quests
+ */
+s16b max_q_idx = MAX_Q_IDX_INIT;
+quest_type *quest;
+
+/*
+ * Display the player as a special symbol when in bad health ?
+ */
+bool player_char_health;
+
+
+/*
+ * The spell list of schools
+ */
+s16b max_spells;
+spell_type *school_spells;
+s16b max_schools;
+school_type *schools;
+
+/*
+ * Lasting spell effects
+ */
+int project_time = 0;
+s32b project_time_effect = 0;
+effect_type effects[MAX_EFFECTS];
+
+/*
+ * General skills set
+ */
+char gen_skill_basem[MAX_SKILLS];
+u32b gen_skill_base[MAX_SKILLS];
+char gen_skill_modm[MAX_SKILLS];
+s16b gen_skill_mod[MAX_SKILLS];
+
+/*
+ * Display stats as linear
+ */
+bool linear_stats;
+
+/*
+ * Table of "cli" macros.
+ */
+cli_comm *cli_info;
+int cli_total = 0;
+
+/*
+ * max_bact, only used so that lua scripts can add new bacts without worrying about the numbers
+ */
+int max_bact = 54;
+
+/*
+ * Max corruptions
+ */
+s16b max_corruptions = 0;
+
+/*
+ * Ingame contextual help
+ */
+bool option_ingame_help = TRUE;
+
+/*
+ * Automatizer enabled status
+ */
+bool automatizer_enabled = FALSE;
+
+/*
+ * Location of the last teleportation thath affected the level
+ */
+s16b last_teleportation_y = -1;
+s16b last_teleportation_x = -1;
+
+/*
+ * The current game module
+ */
+cptr game_module;
+s32b VERSION_MAJOR;
+s32b VERSION_MINOR;
+s32b VERSION_PATCH;
+
+/*
+ * Some module info
+ */
+s32b max_plev = 50;
+s32b DUNGEON_DEATH = 28;
+
+/*
+ * Gods
+ */
+deity_type *deity_info;
+s32b max_gods = MAX_GODS_INIT;
+
+/*
+ * Timers
+ */
+timer_type *gl_timers = NULL;
diff --git a/src/wild.c b/src/wild.c
new file mode 100644
index 00000000..c22de8ab
--- /dev/null
+++ b/src/wild.c
@@ -0,0 +1,1316 @@
+/* File: generate.c */
+
+/* Purpose: Wilderness & Town related things */
+
+/*
+ * Copyright (c) 2001 James E. Wilson, Robert A. Koeneke, DarkGod
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+
+/*
+ * Various defines for the wilderness
+ */
+#define DUN_WILD_VAULT 50 /* Chance of finding a wilderness vault. */
+
+/*
+ * Various defines for the towns
+ */
+#define TOWN_NORMAL_FLOOR 70
+#define TOWN_BORDER 90
+
+
+/*
+ * Helper for plasma generation.
+ */
+static void perturb_point_mid(int x1, int x2, int x3, int x4,
+ int xmid, int ymid, int rough, int depth_max)
+{
+ /*
+ * Average the four corners & perturb it a bit.
+ * tmp is a random int +/- rough
+ */
+ int tmp2 = rough * 2 + 1;
+ int tmp = randint(tmp2) - (rough + 1);
+
+ int avg = ((x1 + x2 + x3 + x4) / 4) + tmp;
+
+ /* Division always rounds down, so we round up again */
+ if (((x1 + x2 + x3 + x4) % 4) > 1) avg++;
+
+ /* Normalize */
+ if (avg < 0) avg = 0;
+ if (avg > depth_max) avg = depth_max;
+
+ /* Set the new value. */
+ cave[ymid][xmid].feat = (byte)avg;
+}
+
+
+static void perturb_point_end(int x1, int x2, int x3,
+ int xmid, int ymid, int rough, int depth_max)
+{
+ /*
+ * Average the three corners & perturb it a bit.
+ * tmp is a random int +/- rough
+ */
+ int tmp2 = rough * 2 + 1;
+ int tmp = randint(tmp2) - (rough + 1);
+
+ int avg = ((x1 + x2 + x3) / 3) + tmp;
+
+ /* Division always rounds down, so we round up again */
+ if ((x1 + x2 + x3) % 3) avg++;
+
+ /* Normalize */
+ if (avg < 0) avg = 0;
+ if (avg > depth_max) avg = depth_max;
+
+ /* Set the new value. */
+ cave[ymid][xmid].feat = (byte)avg;
+}
+
+
+/*
+ * A generic function to generate the plasma fractal.
+ * Note that it uses ``cave_feat'' as temporary storage.
+ * The values in ``cave_feat'' after this function
+ * are NOT actual features; They are raw heights which
+ * need to be converted to features.
+ *
+ * So we shouldn't call cave_set_feat in the helper functions
+ * above.
+ */
+static void plasma_recursive(int x1, int y1, int x2, int y2,
+ int depth_max, int rough)
+{
+ /* Find middle */
+ int xmid = (x2 - x1) / 2 + x1;
+ int ymid = (y2 - y1) / 2 + y1;
+
+ /* Are we done? */
+ if (x1 + 1 == x2) return;
+
+ perturb_point_mid(cave[y1][x1].feat, cave[y2][x1].feat, cave[y1][x2].feat,
+ cave[y2][x2].feat, xmid, ymid, rough, depth_max);
+
+ perturb_point_end(cave[y1][x1].feat, cave[y1][x2].feat, cave[ymid][xmid].feat,
+ xmid, y1, rough, depth_max);
+
+ perturb_point_end(cave[y1][x2].feat, cave[y2][x2].feat, cave[ymid][xmid].feat,
+ x2, ymid, rough, depth_max);
+
+ perturb_point_end(cave[y2][x2].feat, cave[y2][x1].feat, cave[ymid][xmid].feat,
+ xmid, y2, rough, depth_max);
+
+ perturb_point_end(cave[y2][x1].feat, cave[y1][x1].feat, cave[ymid][xmid].feat,
+ x1, ymid, rough, depth_max);
+
+
+ /* Recurse the four quadrants */
+ plasma_recursive(x1, y1, xmid, ymid, depth_max, rough);
+ plasma_recursive(xmid, y1, x2, ymid, depth_max, rough);
+ plasma_recursive(x1, ymid, xmid, y2, depth_max, rough);
+ plasma_recursive(xmid, ymid, x2, y2, depth_max, rough);
+}
+
+
+/*
+ * Load a town or generate a terrain level using "plasma" fractals.
+ *
+ * x and y are the coordinates of the area in the wilderness.
+ * Border and corner are optimization flags to speed up the
+ * generation of the fractal terrain.
+ * If border is set then only the border of the terrain should
+ * be generated (for initializing the border structure).
+ * If corner is set then only the corners of the area are needed.
+ *
+ * Return the number of floor grids
+ */
+int generate_area(int y, int x, bool border, bool corner, bool refresh)
+{
+ int road, entrance;
+ int x1, y1;
+ int hack_floor = 0;
+
+ /* Number of the town (if any) */
+ p_ptr->town_num = wf_info[wild_map[y][x].feat].entrance;
+ if (!p_ptr->town_num) p_ptr->town_num = wild_map[y][x].entrance;
+
+ {
+ int roughness = 1; /* The roughness of the level. */
+ int terrain[3][3]; /* The terrain around the current area */
+ int ym, xm, yp, xp;
+
+ /* Place the player at the center */
+ if (!p_ptr->oldpx) p_ptr->oldpx = MAX_WID / 2;
+ if (!p_ptr->oldpy) p_ptr->oldpy = MAX_HGT / 2;
+
+ /* Initialize the terrain array */
+ ym = ((y - 1) < 0) ? 0 : (y - 1);
+ xm = ((x - 1) < 0) ? 0 : (x - 1);
+ yp = ((y + 1) >= max_wild_y) ? (max_wild_y - 1) : (y + 1);
+ xp = ((x + 1) >= max_wild_x) ? (max_wild_x - 1) : (x + 1);
+ terrain[0][0] = wild_map[ym][xm].feat;
+ terrain[0][1] = wild_map[ym][x].feat;
+ terrain[0][2] = wild_map[ym][xp].feat;
+ terrain[1][0] = wild_map[y][xm].feat;
+ terrain[1][1] = wild_map[y][x].feat;
+ terrain[1][2] = wild_map[y][xp].feat;
+ terrain[2][0] = wild_map[yp][xm].feat;
+ terrain[2][1] = wild_map[yp][x].feat;
+ terrain[2][2] = wild_map[yp][xp].feat;
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant town layout */
+ Rand_value = wild_map[y][x].seed;
+
+ if (!corner)
+ {
+ /* Create level background */
+ for (y1 = 0; y1 < MAX_HGT; y1++)
+ {
+ for (x1 = 0; x1 < MAX_WID; x1++)
+ {
+ cave_set_feat(y1, x1, MAX_WILD_TERRAIN / 2);
+ }
+ }
+ }
+
+ /*
+ * Initialize the four corners
+ * ToDo: calculate the medium height of the adjacent
+ * terrains for every corner.
+ */
+ cave_set_feat(1, 1, (byte)rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(MAX_HGT - 2, 1, (byte)rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(1, MAX_WID - 2, (byte)rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(MAX_HGT - 2, MAX_WID - 2, (byte)rand_int(MAX_WILD_TERRAIN));
+
+ if (!corner)
+ {
+ /* x1, y1, x2, y2, num_depths, roughness */
+ plasma_recursive(1, 1, MAX_WID - 2, MAX_HGT - 2, MAX_WILD_TERRAIN - 1, roughness);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ for (y1 = 1; y1 < MAX_HGT - 1; y1++)
+ {
+ for (x1 = 1; x1 < MAX_WID - 1; x1++)
+ {
+ cave_set_feat(y1, x1,
+ wf_info[terrain[1][1]].terrain[cave[y1][x1].feat]);
+ }
+ }
+
+ }
+
+ /* Should we create a town ? */
+ if ((p_ptr->town_num > 0) && (p_ptr->town_num < 1000))
+ {
+ /* Create the town */
+ int xstart = 0;
+ int ystart = 0;
+
+ /* Initialize the town */
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(NULL, "t_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+ }
+ else
+ {
+ /* Reset the town flag */
+ p_ptr->town_num = 0;
+ }
+
+ if (!corner)
+ {
+ /*
+ * Place roads in the wilderness
+ * ToDo: make the road a bit more interresting
+ */
+ road = wf_info[wild_map[y][x].feat].road;
+
+ if (road & ROAD_NORTH)
+ {
+ /* North road */
+ for (y1 = 1; y1 < MAX_HGT / 2; y1++)
+ {
+ x1 = MAX_WID / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_SOUTH)
+ {
+ /* North road */
+ for (y1 = MAX_HGT / 2; y1 < MAX_HGT - 1; y1++)
+ {
+ x1 = MAX_WID / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_EAST)
+ {
+ /* East road */
+ for (x1 = MAX_WID / 2; x1 < MAX_WID - 1; x1++)
+ {
+ y1 = MAX_HGT / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_WEST)
+ {
+ /* West road */
+ for (x1 = 1; x1 < MAX_WID / 2; x1++)
+ {
+ y1 = MAX_HGT / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+ }
+
+#if 0
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant town layout */
+ Rand_value = wilderness[y][x].seed;
+
+ /* Generate a wilderness vault. */
+ if (magik(DUN_WILD_VAULT))
+ {
+ vault_type *v_ptr;
+ int vindex, vy, vx;
+ int i;
+
+ /* Pick a wilderness vault */
+ for (i = 0; i < 1000; i++)
+ {
+ /* Access a random vault record */
+ vindex = rand_int(max_v_idx);
+ v_ptr = &v_info[vindex];
+
+ /* Accept the first greater vault */
+ if (v_ptr->typ == 10) break;
+ }
+
+ /* Message */
+ if (cheat_room) msg_format("Wilderness Vault %d", vindex);
+
+ /* Boost the rating */
+ rating += v_ptr->rat;
+
+ vy = rand_range((v_ptr->hgt / 2) + 1, MAX_HGT - (v_ptr->hgt / 2)-1);
+ vx = rand_range((v_ptr->wid / 2) + 1, MAX_WID - (v_ptr->wid / 2)-1);
+
+ build_vault(vy, vx, v_ptr->hgt, v_ptr->wid, v_text + v_ptr->text);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+#endif
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant town layout */
+ Rand_value = wild_map[y][x].seed;
+
+ entrance = wf_info[wild_map[y][x].feat].entrance;
+ if (!entrance) entrance = wild_map[y][x].entrance;
+
+ /* Create the dungeon if requested on the map */
+ if (entrance >= 1000)
+ {
+ int dy, dx;
+
+ dy = rand_range(6, cur_hgt - 6);
+ dx = rand_range(6, cur_wid - 6);
+
+ cave_set_feat(dy, dx, FEAT_MORE);
+ cave[dy][dx].special = entrance - 1000;
+ cave[dy][dx].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ /* MEGA HACK -- set at least one floor grid */
+ for (y1 = 1; y1 < cur_hgt - 1; y1++)
+ {
+ for (x1 = 1; x1 < cur_wid - 1; x1++)
+ {
+ if (cave_floor_bold(y1, x1)) hack_floor++;
+ }
+ }
+
+ /* NO floor ? put one */
+ if (!hack_floor)
+ {
+ cave_set_feat(cur_hgt / 2, cur_wid / 2, FEAT_GRASS);
+ cave[cur_hgt / 2][cur_wid / 2].special = 0;
+ hack_floor = 1;
+ }
+
+ /* Set the monster generation level to the wilderness level */
+ monster_level = wf_info[wild_map[y][x].feat].level;
+
+ /* Set the object generation level to the wilderness level */
+ object_level = wf_info[wild_map[y][x].feat].level;
+
+ return hack_floor;
+}
+
+/*
+ * Border of the wilderness area
+ */
+static border_type border;
+
+/*
+ * Build the wilderness area outside of the town.
+ * -KMW-
+ */
+void wilderness_gen(int refresh)
+{
+ int i, y, x, hack_floor;
+ bool daytime;
+ int xstart = 0;
+ int ystart = 0;
+ cave_type *c_ptr;
+
+ /* Init the wilderness */
+ process_dungeon_file(NULL, "w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+
+ x = p_ptr->wilderness_x;
+ y = p_ptr->wilderness_y;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* North border */
+ generate_area(y - 1, x, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_WID - 1; i++)
+ {
+ border.north[i] = cave[MAX_HGT - 2][i].feat;
+ }
+
+ /* South border */
+ generate_area(y + 1, x, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_WID - 1; i++)
+ {
+ border.south[i] = cave[1][i].feat;
+ }
+
+ /* West border */
+ generate_area(y, x - 1, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_HGT - 1; i++)
+ {
+ border.west[i] = cave[i][MAX_WID - 2].feat;
+ }
+
+ /* East border */
+ generate_area(y, x + 1, TRUE, FALSE, refresh);
+
+ for (i = 1; i < MAX_HGT - 1; i++)
+ {
+ border.east[i] = cave[i][1].feat;
+ }
+
+ /* North west corner */
+ generate_area(y - 1, x - 1, FALSE, TRUE, refresh);
+ border.north_west = cave[MAX_HGT - 2][MAX_WID - 2].feat;
+
+ /* North east corner */
+ generate_area(y - 1, x + 1, FALSE, TRUE, refresh);
+ border.north_east = cave[MAX_HGT - 2][1].feat;
+
+ /* South west corner */
+ generate_area(y + 1, x - 1, FALSE, TRUE, refresh);
+ border.south_west = cave[1][MAX_WID - 2].feat;
+
+ /* South east corner */
+ generate_area(y + 1, x + 1, FALSE, TRUE, refresh);
+ border.south_east = cave[1][1].feat;
+
+
+ /* Create terrain of the current area */
+ hack_floor = generate_area(y, x, FALSE, FALSE, refresh);
+
+
+ /* Special boundary walls -- North */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ cave[0][i].mimic = border.north[i];
+ cave_set_feat(0, i, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- South */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ cave[MAX_HGT - 1][i].mimic = border.south[i];
+ cave_set_feat(MAX_HGT - 1, i, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- West */
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ cave[i][0].mimic = border.west[i];
+ cave_set_feat(i, 0, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- East */
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ cave[i][MAX_WID - 1].mimic = border.east[i];
+ cave_set_feat(i, MAX_WID - 1, FEAT_PERM_SOLID);
+ }
+
+ /* North west corner */
+ cave[0][0].mimic = border.north_west;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(0, 0, cave[0][0].feat);
+
+ /* North east corner */
+ cave[0][MAX_WID - 1].mimic = border.north_east;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(0, MAX_WID - 1, cave[0][MAX_WID - 1].feat);
+
+ /* South west corner */
+ cave[MAX_HGT - 1][0].mimic = border.south_west;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(MAX_HGT - 1, 0, cave[MAX_HGT - 1][0].feat);
+
+ /* South east corner */
+ cave[MAX_HGT - 1][MAX_WID - 1].mimic = border.south_east;
+
+ /* Make sure it has correct CAVE_WALL flag set */
+ cave_set_feat(MAX_HGT - 1, MAX_WID - 1, cave[MAX_HGT - 1][MAX_WID - 1].feat);
+
+
+ /* Day time */
+ if ((turn % (10L * DAY)) < ((10L * DAY) / 2))
+ daytime = TRUE;
+ else
+ daytime = FALSE;
+
+ /* Light up or darken the area */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ if (daytime)
+ {
+ /* Assume lit */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Hack -- Memorize lit grids if allowed */
+ if (view_perma_grids) c_ptr->info |= (CAVE_MARK);
+ }
+ else
+ {
+ /* Darken "boring" features */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_REMEMBER))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ }
+ }
+
+ player_place(p_ptr->oldpy, p_ptr->oldpx);
+
+ if (!refresh)
+ {
+ int lim = (generate_encounter == TRUE) ? 60 : MIN_M_ALLOC_TN;
+
+ /*
+ * Can't have more monsters than floor grids -1(for the player,
+ * not needed but safer
+ */
+ if (lim > hack_floor - 1) lim = hack_floor - 1;
+
+ /* Make some residents */
+ for (i = 0; i < lim; i++)
+ {
+ /* Make a resident */
+ (void)alloc_monster((generate_encounter == TRUE) ? 0 : 3, (generate_encounter == TRUE) ? FALSE : TRUE);
+ }
+
+ if (generate_encounter) ambush_flag = TRUE;
+ generate_encounter = FALSE;
+ }
+
+ /* Set rewarded quests to finished */
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (quest[i].status == QUEST_STATUS_REWARDED)
+ quest[i].status = QUEST_STATUS_FINISHED;
+ }
+
+ process_hooks(HOOK_WILD_GEN, "(d)", FALSE);
+}
+
+/*
+ * Build the wilderness area.
+ * -DG-
+ */
+void wilderness_gen_small()
+{
+ int i, j, entrance;
+ int xstart = 0;
+ int ystart = 0;
+
+ /* To prevent stupid things */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ for (j = 0; j < MAX_HGT; j++)
+ {
+ cave_set_feat(j, i, FEAT_EKKAIA);
+ }
+ }
+
+ /* Init the wilderness */
+ process_dungeon_file(NULL, "w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE);
+
+ /* Fill the map */
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ entrance = wf_info[wild_map[j][i].feat].entrance;
+ if (!entrance) entrance = wild_map[j][i].entrance;
+
+ if (wild_map[j][i].entrance)
+ {
+ cave_set_feat(j, i, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(j, i, wf_info[wild_map[j][i].feat].feat);
+ }
+
+ if ((cave[j][i].feat == FEAT_MORE) && (entrance >= 1000))
+ {
+ cave[j][i].special = entrance - 1000;
+ }
+
+ /* Show it if we know it */
+ if (wild_map[j][i].known)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ }
+
+ /* Place the player */
+ p_ptr->px = p_ptr->wilderness_x;
+ p_ptr->py = p_ptr->wilderness_y;
+
+ /* Set rewarded quests to finished */
+ for (i = 0; i < max_q_idx; i++)
+ {
+ if (quest[i].status == QUEST_STATUS_REWARDED)
+ quest[i].status = QUEST_STATUS_FINISHED;
+ }
+
+ process_hooks(HOOK_WILD_GEN, "(d)", TRUE);
+}
+
+/* Show a small radius of wilderness around the player */
+void reveal_wilderness_around_player(int y, int x, int h, int w)
+{
+ int i, j;
+
+ /* Circle or square ? */
+ if (h == 0)
+ {
+ for (i = x - w; i < x + w; i++)
+ {
+ for (j = y - w; j < y + w; j++)
+ {
+ /* Bound checking */
+ if (!in_bounds(j, i)) continue;
+
+ /* Severe bound checking */
+ if ((i < 0) || (i >= max_wild_x) || (j < 0) || (j >= max_wild_y)) continue;
+
+ /* We want a radius, not a "squarus" :) */
+ if (distance(y, x, j, i) >= w) continue;
+
+ /* New we know here */
+ wild_map[j][i].known = TRUE;
+
+ /* Only if we are in overview */
+ if (p_ptr->wild_mode)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+
+ /* Show it */
+ lite_spot(j, i);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (i = x; i < x + w; i++)
+ {
+ for (j = y; j < y + h; j++)
+ {
+ /* Bound checking */
+ if (!in_bounds(j, i)) continue;
+
+ /* New we know here */
+ wild_map[j][i].known = TRUE;
+
+ /* Only if we are in overview */
+ if (p_ptr->wild_mode)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+
+ /* Show it */
+ lite_spot(j, i);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Builds a store at a given pseudo-location
+ *
+ * As of 2.8.1 (?) the town is actually centered in the middle of a
+ * complete level, and thus the top left corner of the town itself
+ * is no longer at (0,0), but rather, at (qy,qx), so the constants
+ * in the comments below should be mentally modified accordingly.
+ *
+ * As of 2.7.4 (?) the stores are placed in a more "user friendly"
+ * configuration, such that the four "center" buildings always
+ * have at least four grids between them, to allow easy running,
+ * and the store doors tend to face the middle of town.
+ *
+ * The stores now lie inside boxes from 3-9 and 12-18 vertically,
+ * and from 7-17, 21-31, 35-45, 49-59. Note that there are thus
+ * always at least 2 open grids between any disconnected walls.
+ *
+ * Note the use of "town_illuminate()" to handle all "illumination"
+ * and "memorization" issues.
+ */
+static void build_store(int qy, int qx, int n, int yy, int xx)
+{
+ int y, x, y0, x0, y1, x1, y2, x2, tmp;
+
+ /* Find the "center" of the store */
+ y0 = qy + yy * 9 + 6;
+ x0 = qx + xx * 14 + 12;
+
+ /* Determine the store boundaries */
+ y1 = y0 - randint((yy == 0) ? 3 : 2);
+ y2 = y0 + randint((yy == 1) ? 3 : 2);
+ x1 = x0 - randint(5);
+ x2 = x0 + randint(5);
+
+ /* Build an invulnerable rectangular building */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ /* Create the building */
+ cave_set_feat(y, x, FEAT_PERM_EXTRA);
+ }
+ }
+
+ /* Pick a door direction (S,N,E,W) */
+ tmp = rand_int(4);
+
+ /* Re-roll "annoying" doors */
+ if (((tmp == 0) && (yy == 1)) ||
+ ((tmp == 1) && (yy == 0)) ||
+ ((tmp == 2) && (xx == 3)) ||
+ ((tmp == 3) && (xx == 0)))
+ {
+ /* Pick a new direction */
+ tmp = rand_int(4);
+ }
+
+ /* Extract a "door location" */
+ switch (tmp)
+ {
+ /* Bottom side */
+ case 0:
+ {
+ y = y2;
+ x = rand_range(x1, x2);
+ break;
+ }
+
+ /* Top side */
+ case 1:
+ {
+ y = y1;
+ x = rand_range(x1, x2);
+ break;
+ }
+
+ /* Right side */
+ case 2:
+ {
+ y = rand_range(y1, y2);
+ x = x2;
+ break;
+ }
+
+ /* Left side */
+ default:
+ {
+ y = rand_range(y1, y2);
+ x = x1;
+ break;
+ }
+ }
+
+ /* Clear previous contents, add a store door */
+ cave_set_feat(y, x, FEAT_SHOP);
+ cave[y][x].special = n;
+ cave[y][x].info |= CAVE_FREE;
+}
+
+static void build_store_circle(int qy, int qx, int n, int yy, int xx)
+{
+ int tmp, y, x, y0, x0, rad = 2 + rand_int(2);
+
+ /* Find the "center" of the store */
+ y0 = qy + yy * 9 + 6;
+ x0 = qx + xx * 14 + 12;
+
+ /* Determine the store boundaries */
+
+ /* Build an invulnerable circular building */
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ if (distance(y0, x0, y, x) > rad) continue;
+
+ /* Create the building */
+ cave_set_feat(y, x, FEAT_PERM_EXTRA);
+ }
+ }
+
+ /* Pick a door direction (S,N,E,W) */
+ tmp = rand_int(4);
+
+ /* Re-roll "annoying" doors */
+ if (((tmp == 0) && (yy == 1)) ||
+ ((tmp == 1) && (yy == 0)) ||
+ ((tmp == 2) && (xx == 3)) ||
+ ((tmp == 3) && (xx == 0)))
+ {
+ /* Pick a new direction */
+ tmp = rand_int(4);
+ }
+
+ /* Extract a "door location" */
+ switch (tmp)
+ {
+ /* Bottom side */
+ case 0:
+ {
+ for (y = y0; y <= y0 + rad; y++) cave_set_feat(y, x0, FEAT_FLOOR);
+ break;
+ }
+
+ /* Top side */
+ case 1:
+ {
+ for (y = y0 - rad; y <= y0; y++) cave_set_feat(y, x0, FEAT_FLOOR);
+ break;
+ }
+
+ /* Right side */
+ case 2:
+ {
+ for (x = x0; x <= x0 + rad; x++) cave_set_feat(y0, x, FEAT_FLOOR);
+ break;
+ }
+
+ /* Left side */
+ default:
+ {
+ for (x = x0 - rad; x <= x0; x++) cave_set_feat(y0, x, FEAT_FLOOR);
+ break;
+ }
+ }
+
+ /* Clear previous contents, add a store door */
+ cave_set_feat(y0, x0, FEAT_SHOP);
+ cave[y0][x0].special = n;
+ cave[y0][x0].info |= CAVE_FREE;
+}
+
+static void build_store_hidden(int n, int yy, int xx)
+{
+ /* Clear previous contents, add a store door */
+ cave_set_feat(yy, xx, FEAT_SHOP);
+ cave[yy][xx].special = n;
+ cave[yy][xx].info |= CAVE_FREE;
+}
+
+/* Return a list of stores */
+static int get_shops(int *rooms)
+{
+ int i, n, num = 0;
+
+ for (n = 0; n < max_st_idx; n++)
+ {
+ rooms[n] = 0;
+ }
+
+ for (n = 0; n < max_st_idx; n++)
+ {
+ int chance = 50;
+
+ if (st_info[n].flags1 & SF1_COMMON) chance += 30;
+ if (st_info[n].flags1 & SF1_RARE) chance -= 20;
+ if (st_info[n].flags1 & SF1_VERY_RARE) chance -= 30;
+
+ if (!magik(chance)) continue;
+
+ for (i = 0; i < num; ++i)
+ if (rooms[i] == n)
+ continue;
+
+ if (st_info[n].flags1 & SF1_RANDOM) rooms[num++] = n;
+ }
+
+ return num;
+}
+
+/* Generate town borders */
+static void set_border(int y, int x)
+{
+ cave_type *c_ptr;
+
+ /* Paranoia */
+ if (!in_bounds(y, x)) return;
+
+ /* Was a floor */
+ if (cave_floor_bold(y, x) ||
+ (f_info[cave[y][x].feat].flags1 & FF1_DOOR))
+ {
+ cave_set_feat(y, x, FEAT_DOOR_HEAD);
+ }
+
+ /* Was a wall */
+ else
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+
+ }
+
+ /* Access grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear special attrs */
+ c_ptr->mimic = 0;
+ c_ptr->special = 0;
+ c_ptr->info |= (CAVE_ROOM);
+}
+
+
+static void town_borders(int t_idx, int qy, int qx)
+{
+ int y, x;
+
+ x = qx;
+ for (y = qy; y < qy + SCREEN_HGT - 1; y++)
+ {
+ set_border(y, x);
+ }
+
+ x = qx + SCREEN_WID - 1;
+ for (y = qy; y < qy + SCREEN_HGT - 1; y++)
+ {
+ set_border(y, x);
+ }
+
+ y = qy;
+ for (x = qx; x < qx + SCREEN_WID - 1; x++)
+ {
+ set_border(y, x);
+ }
+
+ y = qy + SCREEN_HGT - 1;
+ for (x = qx; x < qx + SCREEN_WID; x++)
+ {
+ set_border(y, x);
+ }
+}
+
+static bool create_townpeople_hook(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ if (r_ptr->d_char == 't') return TRUE;
+ else return FALSE;
+}
+
+
+/*
+ * Generate the "consistent" town features, and place the player
+ *
+ * Hack -- play with the R.N.G. to always yield the same town
+ * layout, including the size and shape of the buildings, the
+ * locations of the doorways, and the location of the stairs.
+ */
+static void town_gen_hack(int t_idx, int qy, int qx)
+{
+ int y, x, floor, num = 0;
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ int *rooms;
+
+ /* Do we use dungeon floor or normal one */
+ if (magik(TOWN_NORMAL_FLOOR)) floor = FEAT_FLOOR;
+ else floor = 0;
+
+ /* Place some floors */
+ for (y = qy + 1; y < qy + SCREEN_HGT - 1; y++)
+ {
+ for (x = qx + 1; x < qx + SCREEN_WID - 1; x++)
+ {
+ /* Create empty floor */
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM | CAVE_FREE);
+ }
+ }
+
+ /* Prepare an Array of "remaining stores", and count them */
+ C_MAKE(rooms, max_st_idx, int);
+ num = get_shops(rooms);
+
+ /* Place two rows of stores */
+ for (y = 0; y < 2; y++)
+ {
+ /* Place four stores per row */
+ for (x = 0; x < 4; x++)
+ {
+ if(--num > -1)
+ {
+ /* Build that store at the proper location */
+ build_store(qy, qx, rooms[num], y, x);
+ }
+ }
+ }
+ C_FREE(rooms, max_st_idx, int);
+
+ /* Generates the town's borders */
+ if (magik(TOWN_NORMAL_FLOOR)) town_borders(t_idx, qy, qx);
+
+
+ /* Some inhabitants(leveled .. hehe :) */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_townpeople_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ for (x = qx; x < qx + SCREEN_WID; x++)
+ for (y = qy; y < qy + SCREEN_HGT; y++)
+ {
+ int m_idx, r_idx;
+
+ /* Only in town */
+ if (!in_bounds(y, x)) continue;
+ if (!(cave[y][x].info & CAVE_FREE)) continue;
+ if (!cave_empty_bold(y, x)) continue;
+
+ if (rand_int(100)) continue;
+
+ r_idx = get_mon_num(0);
+ m_allow_special[r_idx] = TRUE;
+ m_idx = place_monster_one(y, x, r_idx, 0, TRUE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+ if (m_ptr->level < (dun_level / 2))
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + (dun_level / 2) + randint(dun_level / 2));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+}
+
+static void town_gen_circle(int t_idx, int qy, int qx)
+{
+ int y, x, cy, cx, rad, floor, num = 0;
+ bool (*old_get_mon_num_hook)(int r_idx);
+
+ int *rooms;
+
+ /* Do we use dungeon floor or normal one */
+ if (magik(TOWN_NORMAL_FLOOR)) floor = FEAT_FLOOR;
+ else floor = 0;
+
+ rad = (SCREEN_HGT / 2);
+
+ y = qy;
+ for (x = qx + rad; x < qx + SCREEN_WID - rad; x++)
+ {
+ set_border(y, x);
+ }
+
+ y = qy + SCREEN_HGT - 1;
+ for (x = qx + rad; x < qx + SCREEN_WID - rad; x++)
+ {
+ set_border(y, x);
+ }
+ /* Place some floors */
+ for (y = qy + 1; y < qy + SCREEN_HGT - 1; y++)
+ {
+ for (x = qx + rad; x < qx + SCREEN_WID - rad; x++)
+ {
+ /* Create empty floor */
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= CAVE_ROOM | CAVE_FREE;
+ }
+ }
+
+ cy = qy + (SCREEN_HGT / 2);
+
+ cx = qx + rad;
+ for (y = cy - rad; y < cy + rad; y++)
+ for (x = cx - rad; x < cx + 1; x++)
+ {
+ int d = distance(cy, cx, y, x);
+
+ if ((d == rad) || (d == rad - 1)) set_border(y, x);
+
+ if (d < rad - 1)
+ {
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= CAVE_ROOM | CAVE_FREE;
+ }
+ }
+
+ cx = qx + SCREEN_WID - rad - 1;
+ for (y = cy - rad; y < cy + rad; y++)
+ for (x = cx; x < cx + rad + 1; x++)
+ {
+ int d = distance(cy, cx, y, x);
+
+ if ((d == rad) || (d == rad - 1)) set_border(y, x);
+
+ if (d < rad - 1)
+ {
+ cave_set_feat(y, x, (floor) ? floor : floor_type[rand_int(100)]);
+ cave[y][x].info |= CAVE_ROOM | CAVE_FREE;
+ }
+ }
+
+ /* Prepare an Array of "remaining stores", and count them */
+ C_MAKE(rooms, max_st_idx, int);
+ num = get_shops(rooms);
+
+ /* Place two rows of stores */
+ for (y = 0; y < 2; y++)
+ {
+ /* Place four stores per row */
+ for (x = 0; x < 4; x++)
+ {
+ if(--num > -1)
+ {
+ /* Build that store at the proper location */
+ build_store_circle(qy, qx, rooms[num], y, x);
+ }
+ }
+ }
+ C_FREE(rooms, max_st_idx, int);
+
+ /* Some inhabitants(leveled .. hehe :) */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_townpeople_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ for (x = qx; x < qx + SCREEN_WID; x++)
+ for (y = qy; y < qy + SCREEN_HGT; y++)
+ {
+ int m_idx, r_idx;
+
+ /* Only in town */
+ if (!in_bounds(y, x)) continue;
+ if (!(cave[y][x].info & CAVE_FREE)) continue;
+ if (!cave_empty_bold(y, x)) continue;
+
+ if (rand_int(100)) continue;
+
+ r_idx = get_mon_num(0);
+ m_allow_special[r_idx] = TRUE;
+ m_idx = place_monster_one(y, x, r_idx, 0, TRUE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+ if (m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+ if (m_ptr->level < (dun_level / 2))
+ {
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + (dun_level / 2) + randint(dun_level / 2));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+}
+
+
+static void town_gen_hidden(int t_idx, int qy, int qx)
+{
+ int y, x, n, num = 0, i;
+
+ int *rooms;
+
+ /* Prepare an Array of "remaining stores", and count them */
+ C_MAKE(rooms, max_st_idx, int);
+ num = get_shops(rooms);
+
+ /* Get a number of stores to place */
+ n = rand_int(num / 2) + (num / 2);
+
+ /* Place k stores */
+ for (i = 0; i < n; i++)
+ {
+ /* Find a good spot */
+ while (TRUE)
+ {
+ y = rand_range(1, cur_hgt - 2);
+ x = rand_range(1, cur_wid - 2);
+
+ if (cave_empty_bold(y, x)) break;
+ }
+
+ if(--num > -1)
+ {
+ /* Build that store at the proper location */
+ build_store_hidden(rooms[num], y, x);
+ }
+ }
+ C_FREE(rooms, max_st_idx, int);
+}
+
+
+
+/*
+ * Town logic flow for generation of new town
+ *
+ * We start with a fully wiped cave of normal floors.
+ *
+ * Note that town_gen_hack() plays games with the R.N.G.
+ *
+ * This function does NOT do anything about the owners of the stores,
+ * nor the contents thereof. It only handles the physical layout.
+ *
+ * We place the player on the stairs at the same time we make them.
+ *
+ * Hack -- since the player always leaves the dungeon by the stairs,
+ * he is always placed on the stairs, even if he left the dungeon via
+ * word of recall or teleport level.
+ */
+void town_gen(int t_idx)
+{
+ int qy, qx;
+
+ /* Level too small to contain a town */
+ if (cur_hgt < SCREEN_HGT) return;
+ if (cur_wid < SCREEN_WID) return;
+
+ /* Center fo the level */
+ qy = (cur_hgt - SCREEN_HGT) / 2;
+ qx = (cur_wid - SCREEN_WID) / 2;
+
+ /* Build stuff */
+ switch (rand_int(3))
+ {
+ case 0:
+ {
+ town_gen_hack(t_idx, qy, qx);
+ if (wizard)
+ {
+ msg_format("Town level(normal) (%d, seed %d)",
+ t_idx, town_info[t_idx].seed);
+ }
+ break;
+ }
+
+ case 1:
+ {
+ town_gen_circle(t_idx, qy, qx);
+ if (wizard)
+ {
+ msg_format("Town level(circle)(%d, seed %d)",
+ t_idx, town_info[t_idx].seed);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ town_gen_hidden(t_idx, qy, qx);
+ if (wizard)
+ {
+ msg_format("Town level(hidden)(%d, seed %d)",
+ t_idx, town_info[t_idx].seed);
+ }
+ break;
+ }
+ }
+
+ p_ptr->town_num = t_idx;
+}
diff --git a/src/wizard1.c b/src/wizard1.c
new file mode 100644
index 00000000..04c5fb0b
--- /dev/null
+++ b/src/wizard1.c
@@ -0,0 +1,2830 @@
+/* File: wizard1.c */
+
+/* Purpose: Spoiler generation -BEN- */
+
+#include "angband.h"
+
+
+#ifdef ALLOW_SPOILERS
+
+
+/*
+ * The spoiler file being created
+ */
+static FILE *fff = NULL;
+
+
+/*
+ * Write out `n' of the character `c' to the spoiler file
+ */
+static void spoiler_out_n_chars(int n, char c)
+{
+ while (--n >= 0) fputc(c, fff);
+}
+
+
+/*
+ * Write out `n' blank lines to the spoiler file
+ */
+static void spoiler_blanklines(int n)
+{
+ spoiler_out_n_chars(n, '\n');
+}
+
+
+/*
+ * Write a line to the spoiler file and then "underline" it with hyphens
+ */
+static void spoiler_underline(cptr str)
+{
+ fprintf(fff, "%s\n", str);
+ spoiler_out_n_chars(strlen(str), '-');
+ fprintf(fff, "\n");
+}
+
+
+/*
+ * Buffer text to the given file. (-SHAWN-)
+ * This is basically c_roff() from mon-desc.c with a few changes.
+ */
+static void spoil_out(cptr str)
+{
+ cptr r;
+
+ /* Line buffer */
+ static char roff_buf[256];
+
+ /* Current pointer into line roff_buf */
+ static char *roff_p = roff_buf;
+
+ /* Last space saved into roff_buf */
+ static char *roff_s = NULL;
+
+ /* Special handling for "new sequence" */
+ if (!str)
+ {
+ if (roff_p != roff_buf) roff_p--;
+ while (*roff_p == ' ' && roff_p != roff_buf) roff_p--;
+ if (roff_p == roff_buf) fprintf(fff, "\n");
+ else
+ {
+ *(roff_p + 1) = '\0';
+ fprintf(fff, "%s\n\n", roff_buf);
+ }
+ roff_p = roff_buf;
+ roff_s = NULL;
+ roff_buf[0] = '\0';
+ return;
+ }
+
+ /* Scan the given string, character at a time */
+ for (; *str; str++)
+ {
+ char ch = *str;
+ int wrap = (ch == '\n');
+
+ if (!isprint(ch)) ch = ' ';
+ if (roff_p >= roff_buf + 75) wrap = 1;
+ if ((ch == ' ') && (roff_p + 2 >= roff_buf + 75)) wrap = 1;
+
+ /* Handle line-wrap */
+ if (wrap)
+ {
+ *roff_p = '\0';
+ r = roff_p;
+ if (roff_s && (ch != ' '))
+ {
+ *roff_s = '\0';
+ r = roff_s + 1;
+ }
+ fprintf(fff, "%s\n", roff_buf);
+ roff_s = NULL;
+ roff_p = roff_buf;
+ while (*r) *roff_p++ = *r++;
+ }
+
+ /* Save the char */
+ if ((roff_p > roff_buf) || (ch != ' '))
+ {
+ if (ch == ' ') roff_s = roff_p;
+ *roff_p++ = ch;
+ }
+ }
+}
+
+
+/*
+ * Extract a textual representation of an attribute
+ */
+static cptr attr_to_text(byte a)
+{
+ switch (a)
+ {
+ case TERM_DARK:
+ return ("xxx");
+ case TERM_WHITE:
+ return ("White");
+ case TERM_SLATE:
+ return ("Slate");
+ case TERM_ORANGE:
+ return ("Orange");
+ case TERM_RED:
+ return ("Red");
+ case TERM_GREEN:
+ return ("Green");
+ case TERM_BLUE:
+ return ("Blue");
+ case TERM_UMBER:
+ return ("Umber");
+ case TERM_L_DARK:
+ return ("L.Dark");
+ case TERM_L_WHITE:
+ return ("L.Slate");
+ case TERM_VIOLET:
+ return ("Violet");
+ case TERM_YELLOW:
+ return ("Yellow");
+ case TERM_L_RED:
+ return ("L.Red");
+ case TERM_L_GREEN:
+ return ("L.Green");
+ case TERM_L_BLUE:
+ return ("L.Blue");
+ case TERM_L_UMBER:
+ return ("L.Umber");
+ }
+
+ /* Oops */
+ return ("Icky");
+}
+
+
+
+/*
+ * A tval grouper
+ */
+typedef struct
+{
+ byte tval;
+ cptr name;
+}
+grouper;
+
+
+
+/*
+ * Item Spoilers by: benh@phial.com (Ben Harrison)
+ */
+
+
+/*
+ * The basic items categorized by type
+ */
+static grouper group_item[] =
+{
+ { TV_SWORD, "Melee Weapons" },
+ { TV_POLEARM, NULL },
+ { TV_HAFTED, NULL },
+ { TV_AXE, NULL },
+ { TV_MSTAFF, NULL },
+
+ { TV_BOW, "Bows and Slings" },
+
+ { TV_SHOT, "Ammo" },
+ { TV_ARROW, NULL },
+ { TV_BOLT, NULL },
+
+ { TV_BOOMERANG, "Boomerangs" },
+
+ { TV_INSTRUMENT, "Instruments" },
+
+ { TV_SOFT_ARMOR, "Armour (Body)" },
+ { TV_HARD_ARMOR, NULL },
+ { TV_DRAG_ARMOR, NULL },
+
+ { TV_SHIELD, "Armour (Misc)" },
+ { TV_HELM, NULL },
+ { TV_CROWN, NULL },
+ { TV_GLOVES, NULL },
+ { TV_BOOTS, NULL },
+
+ { TV_CLOAK, "Cloaks" },
+ { TV_AMULET, "Amulets" },
+ { TV_RING, "Rings" },
+
+ { TV_SCROLL, "Scrolls" },
+ { TV_POTION, "Potions" },
+ { TV_POTION2, NULL },
+
+ { TV_FOOD, "Food" },
+
+ { TV_ROD_MAIN, "Rods" },
+ { TV_ROD, "Rod Tips" },
+ { TV_WAND, "Wands" },
+ { TV_STAFF, "Staves" },
+
+ { TV_BOOK, "Books (Magic, Gods, Music)" },
+ { TV_DAEMON_BOOK, "Demonic Equipment" },
+
+ { TV_RUNE1, "Runes" },
+ { TV_RUNE2, NULL },
+
+ { TV_BATERIE, "Essences" },
+
+ { TV_PARCHMENT, "Parchments" },
+
+ { TV_DIGGING, "Tools" },
+ { TV_TOOL, NULL },
+
+ { TV_TRAPKIT, "Trapping Kits" },
+
+ { TV_CHEST, "Chests" },
+
+ { TV_SPIKE, "Various" },
+ { TV_LITE, NULL },
+ { TV_FLASK, NULL },
+ { TV_BOTTLE, NULL },
+ { TV_JUNK, NULL },
+
+ { TV_SKELETON, "Corpses and Eggs" },
+ { TV_CORPSE, NULL },
+ { TV_EGG, NULL },
+
+ { 0, "" }
+};
+
+
+/*
+ * Describe the kind
+ */
+static void kind_info(char *buf, char *dam, char *wgt, int *lev, s32b *val, int k)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ object_kind *k_ptr;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare a fake item */
+ object_prep(q_ptr, k);
+
+ /* Obtain the "kind" info */
+ k_ptr = &k_info[q_ptr->k_idx];
+
+ /* It is known */
+ q_ptr->ident |= (IDENT_KNOWN);
+
+ /* Cancel bonuses */
+ q_ptr->to_a = 0;
+ q_ptr->to_h = 0;
+ q_ptr->to_d = 0;
+
+ if ((k_ptr->tval == TV_WAND) || (k_ptr->tval == TV_STAFF))
+ {
+ hack_apply_magic_power = -99;
+ apply_magic(q_ptr, 0, FALSE, FALSE, FALSE);
+ }
+
+ /* Level */
+ (*lev) = k_ptr->level;
+
+ /* Value */
+ (*val) = object_value(q_ptr);
+
+
+ /* Hack */
+ if (!buf || !dam || !wgt) return;
+
+
+ /* Description (too brief) */
+ object_desc_store(buf, q_ptr, FALSE, 0);
+
+
+ /* Misc info */
+ strcpy(dam, "");
+
+ /* Damage */
+ switch (q_ptr->tval)
+ {
+ /* Bows */
+ case TV_BOW:
+ {
+ break;
+ }
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+
+ /* Boomerangs */
+ case TV_BOOMERANG:
+
+ /* Weapons */
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_MSTAFF:
+
+ /* Tools */
+ case TV_DIGGING:
+ {
+ sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
+ break;
+ }
+
+ /* Armour */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ sprintf(dam, "%d", q_ptr->ac);
+ break;
+ }
+ }
+
+
+ /* Weight */
+ sprintf(wgt, "%3ld.%ld", q_ptr->weight / 10, q_ptr->weight % 10);
+}
+
+
+/*
+ * Create a spoiler file for items
+ */
+static void spoil_obj_desc(cptr fname)
+{
+ int i, k, s, t, n = 0;
+
+ u16b who[200];
+
+ char buf[1024];
+
+ char wgt[80];
+ char dam[80];
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+
+ /* Header */
+ sprintf(buf, "Basic Items Spoilers for %s %ld.%ld.%ld%s",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /* More Header */
+ fprintf(fff, "%-45s %8s%7s%5s%9s\n",
+ "Description", "Dam/AC", "Wgt", "Lev", "Cost");
+ fprintf(fff, "%-45s %8s%7s%5s%9s\n",
+ "----------------------------------------",
+ "------", "---", "---", "----");
+
+ /* List the groups */
+ for (i = 0; TRUE; i++)
+ {
+ /* Write out the group title */
+ if (group_item[i].name)
+ {
+ /* Hack -- bubble-sort by cost and then level */
+ for (s = 0; s < n - 1; s++)
+ {
+ for (t = 0; t < n - 1; t++)
+ {
+ int i1 = t;
+ int i2 = t + 1;
+
+ int e1;
+ int e2;
+
+ s32b t1;
+ s32b t2;
+
+ kind_info(NULL, NULL, NULL, &e1, &t1, who[i1]);
+ kind_info(NULL, NULL, NULL, &e2, &t2, who[i2]);
+
+ if ((t1 > t2) || ((t1 == t2) && (e1 > e2)))
+ {
+ int tmp = who[i1];
+ who[i1] = who[i2];
+ who[i2] = tmp;
+ }
+ }
+ }
+
+ /* Spoil each item */
+ for (s = 0; s < n; s++)
+ {
+ int e;
+ s32b v;
+
+ /* Describe the kind */
+ kind_info(buf, dam, wgt, &e, &v, who[s]);
+
+ /* Dump it */
+ fprintf(fff, " %-45s%8s%7s%5d%9ld\n",
+ buf, dam, wgt, e, (long)(v));
+ }
+
+ /* Start a new set */
+ n = 0;
+
+ /* Notice the end */
+ if (!group_item[i].tval) break;
+
+ /* Start a new set */
+ fprintf(fff, "\n\n%s\n\n", group_item[i].name);
+ }
+
+ /* Acquire legal item types */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Skip wrong tval's */
+ if (k_ptr->tval != group_item[i].tval) continue;
+
+ /* Hack -- Skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART | TR3_NORM_ART)) continue;
+
+ /* Hack -- Skip Ring of Powers */
+ if (k == 785) continue;
+
+ /* Save the index */
+ who[n++] = k;
+ }
+ }
+
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+/*
+ * Artifact Spoilers by: randy@PICARD.tamu.edu (Randy Hutson)
+ */
+
+
+/*
+ * Returns a "+" string if a number is non-negative and an empty
+ * string if negative
+ */
+#define POSITIZE(v) (((v) >= 0) ? "+" : "")
+
+/*
+ * These are used to format the artifact spoiler file. INDENT1 is used
+ * to indent all but the first line of an artifact spoiler. INDENT2 is
+ * used when a line "wraps". (Bladeturner's resistances cause this.)
+ */
+#define INDENT1 " "
+#define INDENT2 " "
+
+/*
+ * MAX_LINE_LEN specifies when a line should wrap.
+ */
+#define MAX_LINE_LEN 75
+
+/*
+ * Given an array, determine how many elements are in the array
+ */
+#define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0]))
+
+/*
+ * The artifacts categorized by type
+ */
+static grouper group_artifact[] =
+{
+ { TV_SWORD, "Edged Weapons" },
+ { TV_POLEARM, "Polearms" },
+ { TV_HAFTED, "Hafted Weapons" },
+ { TV_AXE, "Axes" },
+
+ { TV_MSTAFF, "Mage Staffs" },
+
+ { TV_BOW, "Bows" },
+
+ { TV_SHOT, "Ammo" },
+ { TV_ARROW, NULL },
+ { TV_BOLT, NULL },
+
+ { TV_BOOMERANG, "Boomerangs" },
+
+ { TV_INSTRUMENT, "Instruments" },
+
+ { TV_SOFT_ARMOR, "Body Armor" },
+ { TV_HARD_ARMOR, NULL },
+ { TV_DRAG_ARMOR, NULL },
+
+ { TV_CLOAK, "Cloaks" },
+ { TV_SHIELD, "Shields" },
+ { TV_HELM, "Helms/Crowns" },
+ { TV_CROWN, NULL },
+ { TV_GLOVES, "Gloves" },
+ { TV_BOOTS, "Boots" },
+
+ { TV_DAEMON_BOOK, "Demonic Equipment" },
+
+ { TV_LITE, "Light Sources" },
+ { TV_AMULET, "Amulets" },
+ { TV_RING, "Rings" },
+
+ { TV_TOOL, "Tools" },
+ { TV_DIGGING, NULL },
+ { TV_TRAPKIT, "Trapping Kits" },
+
+ { 0, NULL }
+};
+
+
+
+/*
+ * Pair together a constant flag with a textual description.
+ *
+ * Used by both "init.c" and "wiz-spo.c".
+ *
+ * Note that it sometimes more efficient to actually make an array
+ * of textual names, where entry 'N' is assumed to be paired with
+ * the flag whose value is "1L << N", but that requires hard-coding.
+ */
+
+typedef struct flag_desc flag_desc;
+
+struct flag_desc
+{
+ const u32b flag;
+ const char *const desc;
+};
+
+
+
+/*
+ * These are used for "+3 to STR, DEX", etc. These are separate from
+ * the other pval affected traits to simplify the case where an object
+ * affects all stats. In this case, "All stats" is used instead of
+ * listing each stat individually.
+ */
+
+static flag_desc stat_flags_desc[] =
+{
+ { TR1_STR, "STR" },
+ { TR1_INT, "INT" },
+ { TR1_WIS, "WIS" },
+ { TR1_DEX, "DEX" },
+ { TR1_CON, "CON" },
+ { TR1_CHR, "CHR" }
+};
+
+/*
+ * Besides stats, these are the other player traits
+ * which may be affected by an object's pval
+ */
+
+static flag_desc pval_flags1_desc[] =
+{
+ { TR1_STEALTH, "Stealth" },
+ { TR1_SEARCH, "Searching" },
+ { TR1_INFRA, "Infravision" },
+ { TR1_TUNNEL, "Tunnelling" },
+ { TR1_BLOWS, "Attacks" },
+ { TR1_SPEED, "Speed" }
+};
+
+/*
+ * Slaying preferences for weapons
+ */
+
+static flag_desc slay_flags_desc[] =
+{
+ { TR1_SLAY_ANIMAL, "Animal" },
+ { TR1_SLAY_EVIL, "Evil" },
+ { TR1_SLAY_UNDEAD, "Undead" },
+ { TR1_SLAY_DEMON, "Demon" },
+ { TR1_SLAY_ORC, "Orc" },
+ { TR1_SLAY_TROLL, "Troll" },
+ { TR1_SLAY_GIANT, "Giant" },
+ { TR1_SLAY_DRAGON, "Dragon" },
+ { TR1_KILL_DRAGON, "Xdragon" }
+};
+
+/*
+ * Elemental brands for weapons
+ *
+ * Clearly, TR1_IMPACT is a bit out of place here. To simplify
+ * coding, it has been included here along with the elemental
+ * brands. It does seem to fit in with the brands and slaying
+ * more than the miscellaneous section.
+ */
+static flag_desc brand_flags_desc[] =
+{
+ { TR1_BRAND_ACID, "Acid Brand" },
+ { TR1_BRAND_ELEC, "Lightning Brand" },
+ { TR1_BRAND_FIRE, "Flame Tongue" },
+ { TR1_BRAND_COLD, "Frost Brand" },
+ { TR1_BRAND_POIS, "Poisoned" },
+
+ { TR1_CHAOTIC, "Mark of Chaos" },
+ { TR1_VAMPIRIC, "Vampiric" },
+ { TR1_IMPACT, "Earthquake impact on hit" },
+ { TR1_VORPAL, "Very sharp" },
+};
+
+
+/*
+ * The 15 resistables
+ */
+static const flag_desc resist_flags_desc[] =
+{
+ { TR2_RES_ACID, "Acid" },
+ { TR2_RES_ELEC, "Lightning" },
+ { TR2_RES_FIRE, "Fire" },
+ { TR2_RES_COLD, "Cold" },
+ { TR2_RES_POIS, "Poison" },
+ { TR2_RES_FEAR, "Fear"},
+ { TR2_RES_LITE, "Light" },
+ { TR2_RES_DARK, "Dark" },
+ { TR2_RES_BLIND, "Blindness" },
+ { TR2_RES_CONF, "Confusion" },
+ { TR2_RES_SOUND, "Sound" },
+ { TR2_RES_SHARDS, "Shards" },
+ { TR2_RES_NETHER, "Nether" },
+ { TR2_RES_NEXUS, "Nexus" },
+ { TR2_RES_CHAOS, "Chaos" },
+ { TR2_RES_DISEN, "Disenchantment" },
+};
+
+/*
+ * Elemental immunities (along with poison)
+ */
+
+static const flag_desc immune_flags_desc[] =
+{
+ { TR2_IM_ACID, "Acid" },
+ { TR2_IM_ELEC, "Lightning" },
+ { TR2_IM_FIRE, "Fire" },
+ { TR2_IM_COLD, "Cold" },
+};
+
+/*
+ * Sustain stats - these are given their "own" line in the
+ * spoiler file, mainly for simplicity
+ */
+static const flag_desc sustain_flags_desc[] =
+{
+ { TR2_SUST_STR, "STR" },
+ { TR2_SUST_INT, "INT" },
+ { TR2_SUST_WIS, "WIS" },
+ { TR2_SUST_DEX, "DEX" },
+ { TR2_SUST_CON, "CON" },
+ { TR2_SUST_CHR, "CHR" },
+};
+
+/*
+ * Miscellaneous magic given by an object's "flags2" field
+ */
+
+static const flag_desc misc_flags2_desc[] =
+{
+ { TR2_REFLECT, "Reflection" },
+ { TR2_FREE_ACT, "Free Action" },
+ { TR2_HOLD_LIFE, "Hold Life" },
+};
+
+/*
+ * Miscellaneous magic given by an object's "flags3" field
+ *
+ * Note that cursed artifacts and objects with permanent light
+ * are handled "directly" -- see analyze_misc_magic()
+ */
+
+static const flag_desc misc_flags3_desc[] =
+{
+ { TR3_SH_FIRE, "Fiery Aura" },
+ { TR3_SH_ELEC, "Electric Aura" },
+ { TR3_NO_TELE, "Prevent Teleportation" },
+ { TR3_NO_MAGIC, "Anti-Magic" },
+ { TR3_WRAITH, "Wraith Form" },
+ { TR3_FEATHER, "Levitation" },
+ { TR3_SEE_INVIS, "See Invisible" },
+ { TR3_SLOW_DIGEST, "Slow Digestion" },
+ { TR3_REGEN, "Regeneration" },
+ { TR3_XTRA_SHOTS, "+1 Extra Shot" }, /* always +1? */
+ { TR3_DRAIN_EXP, "Drains Experience" },
+ { TR3_AGGRAVATE, "Aggravates" },
+ { TR3_BLESSED, "Blessed Blade" },
+};
+
+
+/*
+ * A special type used just for dealing with pvals
+ */
+typedef struct
+{
+ /*
+ * This will contain a string such as "+2", "-10", etc.
+ */
+ char pval_desc[12];
+
+ /*
+ * A list of various player traits affected by an object's pval such
+ * as stats, speed, stealth, etc. "Extra attacks" is NOT included in
+ * this list since it will probably be desirable to format its
+ * description differently.
+ *
+ * Note that room need only be reserved for the number of stats - 1
+ * since the description "All stats" is used if an object affects all
+ * all stats. Also, room must be reserved for a sentinel NULL pointer.
+ *
+ * This will be a list such as ["STR", "DEX", "Stealth", NULL] etc.
+ *
+ * This list includes extra attacks, for simplicity.
+ */
+ cptr pval_affects[N_ELEMENTS(stat_flags_desc) - 1 +
+ N_ELEMENTS(pval_flags1_desc) + 1];
+
+}
+pval_info_type;
+
+
+/*
+ * An "object analysis structure"
+ *
+ * It will be filled with descriptive strings detailing an object's
+ * various magical powers. The "ignore X" traits are not noted since
+ * all artifacts ignore "normal" destruction.
+ */
+
+typedef struct
+{
+ /* "The Longsword Dragonsmiter (6d4) (+20, +25)" */
+ char description[160];
+
+ /* Description of what is affected by an object's pval */
+ pval_info_type pval_info;
+
+ /* A list of an object's slaying preferences */
+ cptr slays[N_ELEMENTS(slay_flags_desc) + 1];
+
+ /* A list if an object's elemental brands */
+ cptr brands[N_ELEMENTS(brand_flags_desc) + 1];
+
+ /* A list of immunities granted by an object */
+ cptr immunities[N_ELEMENTS(immune_flags_desc) + 1];
+
+ /* A list of resistances granted by an object */
+ cptr resistances[N_ELEMENTS(resist_flags_desc) + 1];
+
+ /* A list of stats sustained by an object */
+ cptr sustains[N_ELEMENTS(sustain_flags_desc) - 1 + 1];
+
+ /* A list of various magical qualities an object may have */
+ cptr misc_magic[N_ELEMENTS(misc_flags2_desc) + N_ELEMENTS(misc_flags3_desc)
+ + 1 /* Permanent Light */
+ + 1 /* type of curse */
+ + 1]; /* sentinel NULL */
+
+ /* A string describing an artifact's activation */
+ cptr activation;
+
+ /* "Level 20, Rarity 30, 3.0 lbs, 20000 Gold" */
+ char misc_desc[80];
+}
+obj_desc_list;
+
+
+
+
+
+
+/*
+ * This function does most of the actual "analysis". Given a set of bit flags
+ * (which will be from one of the flags fields from the object in question),
+ * a "flag description structure", a "description list", and the number of
+ * elements in the "flag description structure", this function sets the
+ * "description list" members to the appropriate descriptions contained in
+ * the "flag description structure".
+ *
+ * The possibly updated description pointer is returned.
+ */
+static cptr *spoiler_flag_aux(const u32b art_flags, const flag_desc *flag_ptr,
+ cptr *desc_ptr, const int n_elmnts)
+{
+ int i;
+
+ for (i = 0; i < n_elmnts; ++i)
+ {
+ if (art_flags & flag_ptr[i].flag)
+ {
+ *desc_ptr++ = flag_ptr[i].desc;
+ }
+ }
+
+ return desc_ptr;
+}
+
+
+/*
+ * Acquire a "basic" description "The Cloak of Death [1,+10]"
+ */
+static void analyze_general (object_type *o_ptr, char *desc_ptr)
+{
+ /* Get a "useful" description of the object */
+ object_desc_store(desc_ptr, o_ptr, TRUE, 1);
+}
+
+
+/*
+ * List "player traits" altered by an artifact's pval. These include stats,
+ * speed, infravision, tunnelling, stealth, searching, and extra attacks.
+ */
+static void analyze_pval (object_type *o_ptr, pval_info_type *p_ptr)
+{
+ const u32b all_stats = (TR1_STR | TR1_INT | TR1_WIS |
+ TR1_DEX | TR1_CON | TR1_CHR);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr *affects_list;
+
+ /* If pval == 0, there is nothing to do. */
+ if (!o_ptr->pval)
+ {
+ /* An "empty" pval description indicates that pval == 0 */
+ p_ptr->pval_desc[0] = '\0';
+ return;
+ }
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ affects_list = p_ptr->pval_affects;
+
+ /* Create the "+N" string */
+ sprintf(p_ptr->pval_desc, "%s%ld", POSITIZE(o_ptr->pval), o_ptr->pval);
+
+ /* First, check to see if the pval affects all stats */
+ if ((f1 & all_stats) == all_stats)
+ {
+ *affects_list++ = "All stats";
+ }
+
+ /* Are any stats affected? */
+ else if (f1 & all_stats)
+ {
+ affects_list = spoiler_flag_aux(f1, stat_flags_desc,
+ affects_list,
+ N_ELEMENTS(stat_flags_desc));
+ }
+
+ /* And now the "rest" */
+ affects_list = spoiler_flag_aux(f1, pval_flags1_desc,
+ affects_list,
+ N_ELEMENTS(pval_flags1_desc));
+
+ /* Terminate the description list */
+ *affects_list = NULL;
+}
+
+
+/* Note the slaying specialties of a weapon */
+static void analyze_slay (object_type *o_ptr, cptr *slay_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ slay_list = spoiler_flag_aux(f1, slay_flags_desc, slay_list,
+ N_ELEMENTS(slay_flags_desc));
+
+ /* Terminate the description list */
+ *slay_list = NULL;
+}
+
+/* Note an object's elemental brands */
+static void analyze_brand (object_type *o_ptr, cptr *brand_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ brand_list = spoiler_flag_aux(f1, brand_flags_desc, brand_list,
+ N_ELEMENTS(brand_flags_desc));
+
+ /* Terminate the description list */
+ *brand_list = NULL;
+}
+
+
+/* Note the resistances granted by an object */
+static void analyze_resist (object_type *o_ptr, cptr *resist_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ resist_list = spoiler_flag_aux(f2, resist_flags_desc,
+ resist_list, N_ELEMENTS(resist_flags_desc));
+
+ /* Terminate the description list */
+ *resist_list = NULL;
+}
+
+
+/* Note the immunities granted by an object */
+static void analyze_immune (object_type *o_ptr, cptr *immune_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ immune_list = spoiler_flag_aux(f2, immune_flags_desc,
+ immune_list, N_ELEMENTS(immune_flags_desc));
+
+ /* Terminate the description list */
+ *immune_list = NULL;
+}
+
+/* Note which stats an object sustains */
+
+static void analyze_sustains (object_type *o_ptr, cptr *sustain_list)
+{
+ const u32b all_sustains = (TR2_SUST_STR | TR2_SUST_INT | TR2_SUST_WIS |
+ TR2_SUST_DEX | TR2_SUST_CON | TR2_SUST_CHR);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Simplify things if an item sustains all stats */
+ if ((f2 & all_sustains) == all_sustains)
+ {
+ *sustain_list++ = "All stats";
+ }
+
+ /* Should we bother? */
+ else if ((f2 & all_sustains))
+ {
+ sustain_list = spoiler_flag_aux(f2, sustain_flags_desc,
+ sustain_list,
+ N_ELEMENTS(sustain_flags_desc));
+ }
+
+ /* Terminate the description list */
+ *sustain_list = NULL;
+}
+
+
+/*
+ * Note miscellaneous powers bestowed by an artifact such as see invisible,
+ * free action, permanent light, etc.
+ */
+static void analyze_misc_magic (object_type *o_ptr, cptr *misc_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ int radius = 0;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ misc_list = spoiler_flag_aux(f2, misc_flags2_desc, misc_list,
+ N_ELEMENTS(misc_flags2_desc));
+
+ misc_list = spoiler_flag_aux(f3, misc_flags3_desc, misc_list,
+ N_ELEMENTS(misc_flags3_desc));
+
+ /*
+ * Glowing artifacts -- small radius light.
+ */
+
+ if (f3 & TR3_LITE1) radius++;
+ if (f4 & TR4_LITE2) radius += 2;
+ if (f4 & TR4_LITE3) radius += 3;
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ *misc_list++ = format("It provides light (radius %d) forever.", radius);
+ }
+ else
+ {
+ *misc_list++ = format("It provides light (radius %d) when fueled.", radius);
+ }
+
+ /*
+ * Handle cursed objects here to avoid redundancies such as noting
+ * that a permanently cursed object is heavily cursed as well as
+ * being "lightly cursed".
+ */
+
+ if (cursed_p(o_ptr))
+ {
+ if (f3 & (TR3_TY_CURSE))
+ {
+ *misc_list++ = "Ancient Curse";
+ }
+ if (f3 & (TR3_PERMA_CURSE))
+ {
+ *misc_list++ = "Permanently Cursed";
+ }
+ else if (f3 & (TR3_HEAVY_CURSE))
+ {
+ *misc_list++ = "Heavily Cursed";
+ }
+ else
+ {
+ *misc_list++ = "Cursed";
+ }
+ }
+
+ /* Terminate the description list */
+ *misc_list = NULL;
+}
+
+
+
+
+/*
+ * Determine the minimum depth an artifact can appear, its rarity, its weight,
+ * and its value in gold pieces
+ */
+static void analyze_misc (object_type *o_ptr, char *misc_desc)
+{
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ sprintf(misc_desc, "Level %u, Rarity %u, %d.%d lbs, %ld Gold",
+ a_ptr->level, a_ptr->rarity,
+ a_ptr->weight / 10, a_ptr->weight % 10, a_ptr->cost);
+}
+
+
+/*
+ * Fill in an object description structure for a given object
+ */
+static void object_analyze(object_type *o_ptr, obj_desc_list *desc_ptr)
+{
+ analyze_general(o_ptr, desc_ptr->description);
+
+ analyze_pval(o_ptr, &desc_ptr->pval_info);
+
+ analyze_brand(o_ptr, desc_ptr->brands);
+
+ analyze_slay(o_ptr, desc_ptr->slays);
+
+ analyze_immune(o_ptr, desc_ptr->immunities);
+
+ analyze_resist(o_ptr, desc_ptr->resistances);
+
+ analyze_sustains(o_ptr, desc_ptr->sustains);
+
+ analyze_misc_magic(o_ptr, desc_ptr->misc_magic);
+
+ analyze_misc(o_ptr, desc_ptr->misc_desc);
+
+ desc_ptr->activation = item_activation(o_ptr, 0);
+}
+
+
+static void print_header(void)
+{
+ char buf[80];
+
+ sprintf(buf, "Artifact Spoilers for %s %ld.%ld.%ld%s",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+ spoiler_underline(buf);
+}
+
+/*
+ * This is somewhat ugly.
+ *
+ * Given a header ("Resist", e.g.), a list ("Fire", "Cold", Acid", e.g.),
+ * and a separator character (',', e.g.), write the list to the spoiler file
+ * in a "nice" format, such as:
+ *
+ * Resist Fire, Cold, Acid
+ *
+ * That was a simple example, but when the list is long, a line wrap
+ * should occur, and this should induce a new level of indention if
+ * a list is being spread across lines. So for example, Bladeturner's
+ * list of resistances should look something like this
+ *
+ * Resist Acid, Lightning, Fire, Cold, Poison, Light, Dark, Blindness,
+ * Confusion, Sound, Shards, Nether, Nexus, Chaos, Disenchantment
+ *
+ * However, the code distinguishes between a single list of many items vs.
+ * many lists. (The separator is used to make this determination.) A single
+ * list of many items will not cause line wrapping (since there is no
+ * apparent reason to do so). So the lists of Ulmo's miscellaneous traits
+ * might look like this:
+ *
+ * Free Action; Hold Life; See Invisible; Slow Digestion; Regeneration
+ * Blessed Blade
+ *
+ * So comparing the two, "Regeneration" has no trailing separator and
+ * "Blessed Blade" was not indented. (Also, Ulmo's lists have no headers,
+ * but that's not relevant to line wrapping and indention.)
+ */
+
+/* ITEM_SEP separates items within a list */
+#define ITEM_SEP ','
+
+
+/* LIST_SEP separates lists */
+#define LIST_SEP ';'
+
+
+/* Create a spoiler file entry for an artifact */
+
+static void spoiler_print_art(obj_desc_list *art_ptr, int name1, int set, object_type *o_ptr)
+{
+ /* Don't indent the first line */
+#if 0 // DGDGDGDG
+ fprintf(fff, "<P>%s<BR>", art_ptr->description);
+#else
+fprintf(fff, "%s\n ", art_ptr->description);
+#endif
+ text_out_indent = 4;
+ object_out_desc(o_ptr, fff, FALSE, TRUE);
+ text_out_indent = 0;
+
+ /* End with the miscellaneous facts */
+#if 0 // DGDGDGDG
+ fprintf(fff, "<BR>%s</P>", art_ptr->misc_desc);
+#else
+ fprintf(fff, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
+#endif
+}
+
+
+/*
+ * Hack -- Create a "forged" artifact
+ */
+static bool make_fake_artifact(object_type *o_ptr, int name1)
+{
+ int i;
+ int cur;
+
+ artifact_type *a_ptr = &a_info[name1];
+
+
+ /* Ignore "empty" artifacts */
+ if (!a_ptr->name) return FALSE;
+
+ /* Acquire the "kind" index */
+ i = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Oops */
+ if (!i) return (FALSE);
+
+ /* Create the artifact */
+ object_prep(o_ptr, i);
+
+ /* Save the name */
+ o_ptr->name1 = name1;
+
+ /* Extract the fields */
+#if 0
+ o_ptr->pval = a_ptr->pval;
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+ o_ptr->to_a = a_ptr->to_a;
+ o_ptr->to_h = a_ptr->to_h;
+ o_ptr->to_d = a_ptr->to_d;
+ o_ptr->weight = a_ptr->weight;
+#else
+ /* Keep the One Ring untouched by apply_magic */
+ if (name1 != ART_POWER)
+ {
+ cur = a_ptr->cur_num;
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+ a_ptr->cur_num = cur;
+ }
+ else
+ {
+ o_ptr->pval = a_ptr->pval;
+ }
+#endif
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Create a spoiler file for artifacts
+ */
+static void spoil_artifact(cptr fname)
+{
+ int i, j;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ obj_desc_list artifact;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+#if 0 // DGDGDGDG
+ fprintf(fff, "<HTML><BODY BGCOLOR=#000000 TEXT=#CCCCCC>");
+#endif
+ /* Dump the header */
+ print_header();
+
+ /* List the artifacts by tval */
+ for (i = 0; group_artifact[i].tval; i++)
+ {
+ /* Write out the group title */
+ if (group_artifact[i].name)
+ {
+ spoiler_blanklines(2);
+ spoiler_underline(group_artifact[i].name);
+ spoiler_blanklines(1);
+ }
+
+ /* Now search through all of the artifacts */
+ for (j = 1; j < max_a_idx; ++j)
+ {
+ artifact_type *a_ptr = &a_info[j];
+
+ /* We only want objects in the current group */
+ if (a_ptr->tval != group_artifact[i].tval) continue;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Attempt to "forge" the artifact */
+ if (!make_fake_artifact(q_ptr, j)) continue;
+
+ /* Aware and Known */
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Analyze the artifact */
+ object_analyze(q_ptr, &artifact);
+
+ /* Write out the artifact description to the spoiler file */
+ spoiler_print_art(&artifact, j, a_ptr->set, q_ptr);
+ }
+ }
+
+#if 0 // DGDGDGDG
+ fprintf(fff, "</BODY></HTML>");
+#endif
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+
+
+/*
+ * Create a spoiler file for monsters -BEN-
+ */
+static void spoil_mon_desc(cptr fname)
+{
+ int i, n = 0;
+
+ s16b *who;
+
+ char buf[1024];
+
+ char nam[80];
+ char lev[80];
+ char rar[80];
+ char spd[80];
+ char ac[80];
+ char hp[80];
+ char exp[80];
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Allocate the "who" array */
+ C_MAKE(who, max_r_idx, s16b);
+
+ /* Dump the header */
+ sprintf(buf, "Monster Spoilers for %s %ld.%ld.%ld%s",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /* Dump the header */
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ "Name", "Lev", "Rar", "Spd", "Hp", "Ac", "Visual Info");
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ "----", "---", "---", "---", "--", "--", "-----------");
+
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Use that monster */
+ if (r_ptr->name) who[n++] = i;
+ }
+
+
+ /* Scan again */
+ for (i = 0; i < n; i++)
+ {
+ monster_race *r_ptr = &r_info[who[i]];
+
+ cptr name = (r_name + r_ptr->name);
+
+ /* Get the "name" */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ sprintf(nam, "[U] %s", name);
+ }
+ else
+ {
+ sprintf(nam, "The %s", name);
+ }
+
+
+ /* Level */
+ sprintf(lev, "%d", r_ptr->level);
+
+ /* Rarity */
+ sprintf(rar, "%d", r_ptr->rarity);
+
+ /* Speed */
+ if (r_ptr->speed >= 110)
+ {
+ sprintf(spd, "+%d", (r_ptr->speed - 110));
+ }
+ else
+ {
+ sprintf(spd, "-%d", (110 - r_ptr->speed));
+ }
+
+ /* Armor Class */
+ sprintf(ac, "%d", r_ptr->ac);
+
+ /* Hitpoints */
+ if ((r_ptr->flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
+ {
+ sprintf(hp, "%d", r_ptr->hdice * r_ptr->hside);
+ }
+ else
+ {
+ sprintf(hp, "%dd%d", r_ptr->hdice, r_ptr->hside);
+ }
+
+
+ /* Experience */
+ sprintf(exp, "%ld", (long)(r_ptr->mexp));
+
+ /* Hack -- use visual instead */
+ sprintf(exp, "%s '%c'", attr_to_text(r_ptr->d_attr), r_ptr->d_char);
+
+ /* Dump the info */
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ nam, lev, rar, spd, hp, ac, exp);
+ }
+
+ /* End it */
+ fprintf(fff, "\n");
+
+ /* Free the "who" array */
+ C_KILL(who, max_r_idx, s16b);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Worked */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+
+/*
+ * Monster spoilers by: smchorse@ringer.cs.utsa.edu (Shawn McHorse)
+ *
+ * Primarily based on code already in mon-desc.c, mostly by -BEN-
+ */
+
+/*
+ * Pronoun arrays
+ */
+static cptr wd_che[3] = { "It", "He", "She" };
+static cptr wd_lhe[3] = { "it", "he", "she" };
+
+
+
+/*
+ * Create a spoiler file for monsters (-SHAWN-)
+ */
+static void spoil_mon_info(cptr fname)
+{
+ char buf[1024];
+ int msex, vn, i, j, k, n;
+ bool breath, magic, sin;
+ cptr p, q;
+ cptr vp[64];
+ u32b flags1, flags2, flags3, flags4, flags5, flags6, flags9;
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+
+ /* Dump the header */
+ sprintf(buf, "Monster Spoilers for %s %ld.%ld.%ld%s",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /*
+ * List all monsters in order.
+ */
+ for (n = 1; n < max_r_idx; n++)
+ {
+ monster_race *r_ptr = &r_info[n];
+
+ /* Extract the flags */
+ flags1 = r_ptr->flags1;
+ flags2 = r_ptr->flags2;
+ flags3 = r_ptr->flags3;
+ flags4 = r_ptr->flags4;
+ flags5 = r_ptr->flags5;
+ flags6 = r_ptr->flags6;
+ flags9 = r_ptr->flags9;
+ breath = FALSE;
+ magic = FALSE;
+
+ /* Extract a gender (if applicable) */
+ if (flags1 & (RF1_FEMALE)) msex = 2;
+ else if (flags1 & (RF1_MALE)) msex = 1;
+ else msex = 0;
+
+
+ /* Prefix */
+ if (flags1 & (RF1_UNIQUE))
+ {
+ spoil_out("[U] ");
+ }
+ else
+ {
+ spoil_out("The ");
+ }
+
+ /* Name */
+ sprintf(buf, "%s (", (r_name + r_ptr->name)); /* ---)--- */
+ spoil_out(buf);
+
+ /* Color */
+ spoil_out(attr_to_text(r_ptr->d_attr));
+
+ /* Symbol --(-- */
+ sprintf(buf, " '%c')\n", r_ptr->d_char);
+ spoil_out(buf);
+
+
+ /* Indent */
+ sprintf(buf, "=== ");
+ spoil_out(buf);
+
+ /* Number */
+ sprintf(buf, "Num:%d ", n);
+ spoil_out(buf);
+
+ /* Level */
+ sprintf(buf, "Lev:%d ", r_ptr->level);
+ spoil_out(buf);
+
+ /* Rarity */
+ sprintf(buf, "Rar:%d ", r_ptr->rarity);
+ spoil_out(buf);
+
+ /* Speed */
+ if (r_ptr->speed >= 110)
+ {
+ sprintf(buf, "Spd:+%d ", (r_ptr->speed - 110));
+ }
+ else
+ {
+ sprintf(buf, "Spd:-%d ", (110 - r_ptr->speed));
+ }
+ spoil_out(buf);
+
+ /* Hitpoints */
+ if ((flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
+ {
+ sprintf(buf, "Hp:%d ", r_ptr->hdice * r_ptr->hside);
+ }
+ else
+ {
+ sprintf(buf, "Hp:%dd%d ", r_ptr->hdice, r_ptr->hside);
+ }
+ spoil_out(buf);
+
+ /* Armor Class */
+ sprintf(buf, "Ac:%d ", r_ptr->ac);
+ spoil_out(buf);
+
+ /* Experience */
+ sprintf(buf, "Exp:%ld\n", (long)(r_ptr->mexp));
+ spoil_out(buf);
+
+
+ /* Describe */
+ spoil_out(r_text + r_ptr->text);
+ spoil_out(" ");
+
+
+ spoil_out("This");
+
+ if (flags2 & (RF2_ELDRITCH_HORROR)) spoil_out (" sanity-blasting");
+ if (flags3 & (RF3_ANIMAL)) spoil_out(" natural");
+ if (flags3 & (RF3_EVIL)) spoil_out(" evil");
+ if (flags3 & (RF3_GOOD)) spoil_out(" good");
+ if (flags3 & (RF3_UNDEAD)) spoil_out(" undead");
+
+ if (flags3 & (RF3_DRAGON)) spoil_out(" dragon");
+ else if (flags3 & (RF3_DEMON)) spoil_out(" demon");
+ else if (flags3 & (RF3_GIANT)) spoil_out(" giant");
+ else if (flags3 & (RF3_TROLL)) spoil_out(" troll");
+ else if (flags3 & (RF3_ORC)) spoil_out(" orc");
+ else if (flags3 & (RF3_THUNDERLORD)) spoil_out (" Thunderlord");
+ else spoil_out(" creature");
+
+ spoil_out(" moves");
+
+ if ((flags1 & (RF1_RAND_50)) && (flags1 & (RF1_RAND_25)))
+ {
+ spoil_out(" extremely erratically");
+ }
+ else if (flags1 & (RF1_RAND_50))
+ {
+ spoil_out(" somewhat erratically");
+ }
+ else if (flags1 & (RF1_RAND_25))
+ {
+ spoil_out(" a bit erratically");
+ }
+ else
+ {
+ spoil_out(" normally");
+ }
+
+ if (flags1 & (RF1_NEVER_MOVE))
+ {
+ spoil_out(", but does not deign to chase intruders");
+ }
+
+ spoil_out(". ");
+
+ if (!r_ptr->level || (flags1 & (RF1_FORCE_DEPTH)))
+ {
+ sprintf(buf, "%s is never found out of depth. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags1 & (RF1_FORCE_SLEEP))
+ {
+ sprintf(buf, "%s is always created sluggish. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags2 & (RF2_AURA_FIRE))
+ {
+ sprintf(buf, "%s is surrounded by flames. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags2 & (RF2_AURA_ELEC))
+ {
+ sprintf(buf, "%s is surrounded by electricity. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags2 & (RF2_REFLECTING))
+ {
+ sprintf(buf, "%s reflects bolt spells. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ if (flags1 & (RF1_ESCORT))
+ {
+ sprintf(buf, "%s usually appears with ", wd_che[msex]);
+ spoil_out(buf);
+ if (flags1 & (RF1_ESCORTS)) spoil_out("escorts. ");
+ else spoil_out("an escort. ");
+ }
+
+ if ((flags1 & (RF1_FRIEND)) || (flags1 & (RF1_FRIENDS)))
+ {
+ sprintf(buf, "%s usually appears in groups. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ /* Collect innate attacks */
+ vn = 0;
+ if (flags4 & (RF4_SHRIEK)) vp[vn++] = "shriek for help";
+ if (flags4 & (RF4_ROCKET)) vp[vn++] = "shoot a rocket";
+ if (flags4 & (RF4_ARROW_1)) vp[vn++] = "fire arrows";
+ if (flags4 & (RF4_ARROW_2)) vp[vn++] = "fire arrows";
+ if (flags4 & (RF4_ARROW_3)) vp[vn++] = "fire missiles";
+ if (flags4 & (RF4_ARROW_4)) vp[vn++] = "fire missiles";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" may ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect breaths */
+ vn = 0;
+ if (flags4 & (RF4_BR_ACID)) vp[vn++] = "acid";
+ if (flags4 & (RF4_BR_ELEC)) vp[vn++] = "lightning";
+ if (flags4 & (RF4_BR_FIRE)) vp[vn++] = "fire";
+ if (flags4 & (RF4_BR_COLD)) vp[vn++] = "frost";
+ if (flags4 & (RF4_BR_POIS)) vp[vn++] = "poison";
+ if (flags4 & (RF4_BR_NETH)) vp[vn++] = "nether";
+ if (flags4 & (RF4_BR_LITE)) vp[vn++] = "light";
+ if (flags4 & (RF4_BR_DARK)) vp[vn++] = "darkness";
+ if (flags4 & (RF4_BR_CONF)) vp[vn++] = "confusion";
+ if (flags4 & (RF4_BR_SOUN)) vp[vn++] = "sound";
+ if (flags4 & (RF4_BR_CHAO)) vp[vn++] = "chaos";
+ if (flags4 & (RF4_BR_DISE)) vp[vn++] = "disenchantment";
+ if (flags4 & (RF4_BR_NEXU)) vp[vn++] = "nexus";
+ if (flags4 & (RF4_BR_TIME)) vp[vn++] = "time";
+ if (flags4 & (RF4_BR_INER)) vp[vn++] = "inertia";
+ if (flags4 & (RF4_BR_GRAV)) vp[vn++] = "gravity";
+ if (flags4 & (RF4_BR_SHAR)) vp[vn++] = "shards";
+ if (flags4 & (RF4_BR_PLAS)) vp[vn++] = "plasma";
+ if (flags4 & (RF4_BR_WALL)) vp[vn++] = "force";
+ if (flags4 & (RF4_BR_MANA)) vp[vn++] = "mana";
+ if (flags4 & (RF4_BR_NUKE)) vp[vn++] = "toxic waste";
+ if (flags4 & (RF4_BR_DISI)) vp[vn++] = "disintegration";
+
+ if (vn)
+ {
+ breath = TRUE;
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" may breathe ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ if (flags2 & (RF2_POWERFUL)) spoil_out(" powerfully");
+ }
+
+ /* Collect spells */
+ vn = 0;
+ if (flags5 & (RF5_BA_ACID)) vp[vn++] = "produce acid balls";
+ if (flags5 & (RF5_BA_ELEC)) vp[vn++] = "produce lightning balls";
+ if (flags5 & (RF5_BA_FIRE)) vp[vn++] = "produce fire balls";
+ if (flags5 & (RF5_BA_COLD)) vp[vn++] = "produce frost balls";
+ if (flags5 & (RF5_BA_POIS)) vp[vn++] = "produce poison balls";
+ if (flags5 & (RF5_BA_NETH)) vp[vn++] = "produce nether balls";
+ if (flags5 & (RF5_BA_WATE)) vp[vn++] = "produce water balls";
+ if (flags4 & (RF4_BA_NUKE)) vp[vn++] = "produce balls of radiation";
+ if (flags5 & (RF5_BA_MANA)) vp[vn++] = "produce mana storms";
+ if (flags5 & (RF5_BA_DARK)) vp[vn++] = "produce darkness storms";
+ if (flags4 & (RF4_BA_CHAO)) vp[vn++] = "invoke raw Chaos";
+ if (flags6 & (RF6_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom";
+ if (flags5 & (RF5_DRAIN_MANA)) vp[vn++] = "drain mana";
+ if (flags5 & (RF5_MIND_BLAST)) vp[vn++] = "cause mind blasting";
+ if (flags5 & (RF5_BRAIN_SMASH)) vp[vn++] = "cause brain smashing";
+ if (flags5 & (RF5_CAUSE_1)) vp[vn++] = "cause light wounds and cursing";
+ if (flags5 & (RF5_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing";
+ if (flags5 & (RF5_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing";
+ if (flags5 & (RF5_CAUSE_4)) vp[vn++] = "cause mortal wounds";
+ if (flags5 & (RF5_BO_ACID)) vp[vn++] = "produce acid bolts";
+ if (flags5 & (RF5_BO_ELEC)) vp[vn++] = "produce lightning bolts";
+ if (flags5 & (RF5_BO_FIRE)) vp[vn++] = "produce fire bolts";
+ if (flags5 & (RF5_BO_COLD)) vp[vn++] = "produce frost bolts";
+ if (flags5 & (RF5_BO_POIS)) vp[vn++] = "produce poison bolts";
+ if (flags5 & (RF5_BO_NETH)) vp[vn++] = "produce nether bolts";
+ if (flags5 & (RF5_BO_WATE)) vp[vn++] = "produce water bolts";
+ if (flags5 & (RF5_BO_MANA)) vp[vn++] = "produce mana bolts";
+ if (flags5 & (RF5_BO_PLAS)) vp[vn++] = "produce plasma bolts";
+ if (flags5 & (RF5_BO_ICEE)) vp[vn++] = "produce ice bolts";
+ if (flags5 & (RF5_MISSILE)) vp[vn++] = "produce magic missiles";
+ if (flags5 & (RF5_SCARE)) vp[vn++] = "terrify";
+ if (flags5 & (RF5_BLIND)) vp[vn++] = "blind";
+ if (flags5 & (RF5_CONF)) vp[vn++] = "confuse";
+ if (flags5 & (RF5_SLOW)) vp[vn++] = "slow";
+ if (flags5 & (RF5_HOLD)) vp[vn++] = "paralyse";
+ if (flags6 & (RF6_HASTE)) vp[vn++] = "haste-self";
+ if (flags6 & (RF6_HEAL)) vp[vn++] = "heal-self";
+ if (flags6 & (RF6_BLINK)) vp[vn++] = "blink-self";
+ if (flags6 & (RF6_TPORT)) vp[vn++] = "teleport-self";
+ if (flags6 & (RF6_S_BUG)) vp[vn++] = "summon software bugs";
+ if (flags6 & (RF6_S_RNG)) vp[vn++] = "summon RNGs";
+ if (flags6 & (RF6_TELE_TO)) vp[vn++] = "teleport to";
+ if (flags6 & (RF6_TELE_AWAY)) vp[vn++] = "teleport away";
+ if (flags6 & (RF6_TELE_LEVEL)) vp[vn++] = "teleport level";
+ if (flags6 & (RF6_DARKNESS)) vp[vn++] = "create darkness";
+ if (flags6 & (RF6_TRAPS)) vp[vn++] = "create traps";
+ if (flags6 & (RF6_FORGET)) vp[vn++] = "cause amnesia";
+ if (flags6 & (RF6_RAISE_DEAD)) vp[vn++] = "raise dead";
+ if (flags6 & (RF6_S_THUNDERLORD)) vp[vn++] = "summon a thunderlord";
+ if (flags6 & (RF6_S_MONSTER)) vp[vn++] = "summon a monster";
+ if (flags6 & (RF6_S_MONSTERS)) vp[vn++] = "summon monsters";
+ if (flags6 & (RF6_S_KIN)) vp[vn++] = "summon aid";
+ if (flags6 & (RF6_S_ANT)) vp[vn++] = "summon ants";
+ if (flags6 & (RF6_S_SPIDER)) vp[vn++] = "summon spiders";
+ if (flags6 & (RF6_S_HOUND)) vp[vn++] = "summon hounds";
+ if (flags6 & (RF6_S_HYDRA)) vp[vn++] = "summon hydras";
+ if (flags6 & (RF6_S_ANGEL)) vp[vn++] = "summon an angel";
+ if (flags6 & (RF6_S_DEMON)) vp[vn++] = "summon a demon";
+ if (flags6 & (RF6_S_UNDEAD)) vp[vn++] = "summon an undead";
+ if (flags6 & (RF6_S_DRAGON)) vp[vn++] = "summon a dragon";
+ if (flags4 & (RF4_S_ANIMAL)) vp[vn++] = "summon animal";
+ if (flags6 & (RF6_S_ANIMALS)) vp[vn++] = "summon animals";
+ if (flags6 & (RF6_S_HI_UNDEAD)) vp[vn++] = "summon greater undead";
+ if (flags6 & (RF6_S_HI_DRAGON)) vp[vn++] = "summon ancient dragons";
+ if (flags6 & (RF6_S_HI_DEMON)) vp[vn++] = "summon greater demons";
+ if (flags6 & (RF6_S_WRAITH)) vp[vn++] = "summon Ringwraith";
+ if (flags6 & (RF6_S_UNIQUE)) vp[vn++] = "summon unique monsters";
+
+ if (vn)
+ {
+ magic = TRUE;
+ if (breath)
+ {
+ spoil_out(", and is also");
+ }
+ else
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is");
+ }
+
+ spoil_out(" magical, casting spells");
+ if (flags2 & (RF2_SMART)) spoil_out(" intelligently");
+
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" which ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ }
+
+ if (breath || magic)
+ {
+ int times = r_ptr->freq_inate + r_ptr->freq_spell;
+ sprintf(buf, "; 1 time in %d. ",
+ 200 / ((times) ? times : 1));
+ spoil_out(buf);
+ }
+
+ /* Collect special abilities. */
+ vn = 0;
+ if (flags2 & (RF2_OPEN_DOOR)) vp[vn++] = "open doors";
+ if (flags2 & (RF2_BASH_DOOR)) vp[vn++] = "bash down doors";
+ if (flags2 & (RF2_PASS_WALL)) vp[vn++] = "pass through walls";
+ if (flags2 & (RF2_KILL_WALL)) vp[vn++] = "bore through walls";
+ if (flags2 & (RF2_MOVE_BODY)) vp[vn++] = "push past weaker monsters";
+ if (flags2 & (RF2_KILL_BODY)) vp[vn++] = "destroy weaker monsters";
+ if (flags2 & (RF2_TAKE_ITEM)) vp[vn++] = "pick up objects";
+ if (flags2 & (RF2_KILL_ITEM)) vp[vn++] = "destroy objects";
+ if (flags9 & (RF9_HAS_LITE)) vp[vn++] = "illuminate the dungeon";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" can ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ if (flags2 & (RF2_INVISIBLE))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is invisible. ");
+ }
+ if (flags2 & (RF2_COLD_BLOOD))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is cold blooded. ");
+ }
+ if (flags2 & (RF2_EMPTY_MIND))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is not detected by telepathy. ");
+ }
+ if (flags2 & (RF2_WEIRD_MIND))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" is rarely detected by telepathy. ");
+ }
+ if (flags4 & (RF4_MULTIPLY))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" breeds explosively. ");
+ }
+ if (flags2 & (RF2_REGENERATE))
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" regenerates quickly. ");
+ }
+
+ /* Collect susceptibilities */
+ vn = 0;
+ if (flags3 & (RF3_HURT_ROCK)) vp[vn++] = "rock remover";
+ if (flags3 & (RF3_HURT_LITE)) vp[vn++] = "bright light";
+ if (flags3 & (RF3_SUSCEP_FIRE)) vp[vn++] = "fire";
+ if (flags3 & (RF3_SUSCEP_COLD)) vp[vn++] = "cold";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" is hurt by ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect immunities */
+ vn = 0;
+ if (flags3 & (RF3_IM_ACID)) vp[vn++] = "acid";
+ if (flags3 & (RF3_IM_ELEC)) vp[vn++] = "lightning";
+ if (flags3 & (RF3_IM_FIRE)) vp[vn++] = "fire";
+ if (flags3 & (RF3_IM_COLD)) vp[vn++] = "cold";
+ if (flags3 & (RF3_IM_POIS)) vp[vn++] = "poison";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" resists ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect resistances */
+ vn = 0;
+ if (flags3 & (RF3_RES_NETH)) vp[vn++] = "nether";
+ if (flags3 & (RF3_RES_WATE)) vp[vn++] = "water";
+ if (flags3 & (RF3_RES_PLAS)) vp[vn++] = "plasma";
+ if (flags3 & (RF3_RES_NEXU)) vp[vn++] = "nexus";
+ if (flags3 & (RF3_RES_DISE)) vp[vn++] = "disenchantment";
+ if (flags3 & (RF3_RES_TELE)) vp[vn++] = "teleportation";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" resists ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" and ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ /* Collect non-effects */
+ vn = 0;
+ if (flags3 & (RF3_NO_STUN)) vp[vn++] = "stunned";
+ if (flags3 & (RF3_NO_FEAR)) vp[vn++] = "frightened";
+ if (flags3 & (RF3_NO_CONF)) vp[vn++] = "confused";
+ if (flags3 & (RF3_NO_SLEEP)) vp[vn++] = "slept";
+
+ if (vn)
+ {
+ spoil_out(wd_che[msex]);
+ for (i = 0; i < vn; i++)
+ {
+ if (!i) spoil_out(" cannot be ");
+ else if (i < vn - 1) spoil_out(", ");
+ else spoil_out(" or ");
+ spoil_out(vp[i]);
+ }
+ spoil_out(". ");
+ }
+
+ spoil_out(wd_che[msex]);
+ if (r_ptr->sleep > 200) spoil_out(" prefers to ignore");
+ else if (r_ptr->sleep > 95) spoil_out(" pays very little attention to");
+ else if (r_ptr->sleep > 75) spoil_out(" pays little attention to");
+ else if (r_ptr->sleep > 45) spoil_out(" tends to overlook");
+ else if (r_ptr->sleep > 25) spoil_out(" takes quite a while to see");
+ else if (r_ptr->sleep > 10) spoil_out(" takes a while to see");
+ else if (r_ptr->sleep > 5) spoil_out(" is fairly observant of");
+ else if (r_ptr->sleep > 3) spoil_out(" is observant of");
+ else if (r_ptr->sleep > 1) spoil_out(" is very observant of");
+ else if (r_ptr->sleep > 0) spoil_out(" is vigilant for");
+ else spoil_out(" is ever vigilant for");
+
+ sprintf(buf, " intruders, which %s may notice from %d feet. ",
+ wd_lhe[msex], 10 * r_ptr->aaf);
+ spoil_out(buf);
+
+ i = 0;
+ if (flags1 & (RF1_DROP_60)) i += 1;
+ if (flags1 & (RF1_DROP_90)) i += 2;
+ if (flags1 & (RF1_DROP_1D2)) i += 2;
+ if (flags1 & (RF1_DROP_2D2)) i += 4;
+ if (flags1 & (RF1_DROP_3D2)) i += 6;
+ if (flags1 & (RF1_DROP_4D2)) i += 8;
+
+ /* Drops gold and/or items */
+ if (i)
+ {
+ sin = FALSE;
+ spoil_out(wd_che[msex]);
+ spoil_out(" will carry");
+
+ if (i == 1)
+ {
+ spoil_out(" a");
+ sin = TRUE;
+ }
+ else if (i == 2)
+ {
+ spoil_out(" one or two");
+ }
+ else
+ {
+ sprintf(buf, " up to %u", i);
+ spoil_out(buf);
+ }
+
+ if (flags1 & (RF1_DROP_GREAT))
+ {
+ if (sin) spoil_out("n");
+ spoil_out(" exceptional object");
+ }
+ else if (flags1 & (RF1_DROP_GOOD))
+ {
+ spoil_out(" good object");
+ }
+ else if (flags1 & (RF1_DROP_USEFUL))
+ {
+ spoil_out(" useful object");
+ }
+ else if (flags1 & (RF1_ONLY_ITEM))
+ {
+ spoil_out(" object");
+ }
+ else if (flags1 & (RF1_ONLY_GOLD))
+ {
+ spoil_out(" treasure");
+ }
+ else
+ {
+ if (sin) spoil_out("n");
+ spoil_out(" object");
+ if (i > 1) spoil_out("s");
+ spoil_out(" or treasure");
+ }
+ if (i > 1) spoil_out("s");
+
+ if (flags1 & (RF1_DROP_CHOSEN))
+ {
+ spoil_out(", in addition to chosen objects");
+ }
+
+ spoil_out(". ");
+ }
+
+ /* Count the actual attacks */
+ for (i = 0, j = 0; j < 4; j++)
+ {
+ if (r_ptr->blow[j].method) i++;
+ }
+
+ /* Examine the actual attacks */
+ for (k = 0, j = 0; j < 4; j++)
+ {
+ if (!r_ptr->blow[j].method) continue;
+
+ /* No method yet */
+ p = "???";
+
+ /* Acquire the method */
+ switch (r_ptr->blow[j].method)
+ {
+ case RBM_HIT:
+ p = "hit";
+ break;
+ case RBM_TOUCH:
+ p = "touch";
+ break;
+ case RBM_PUNCH:
+ p = "punch";
+ break;
+ case RBM_KICK:
+ p = "kick";
+ break;
+ case RBM_CLAW:
+ p = "claw";
+ break;
+ case RBM_BITE:
+ p = "bite";
+ break;
+ case RBM_STING:
+ p = "sting";
+ break;
+ case RBM_XXX1:
+ break;
+ case RBM_BUTT:
+ p = "butt";
+ break;
+ case RBM_CRUSH:
+ p = "crush";
+ break;
+ case RBM_ENGULF:
+ p = "engulf";
+ break;
+ case RBM_CHARGE:
+ p = "charge";
+ break;
+ case RBM_CRAWL:
+ p = "crawl on you";
+ break;
+ case RBM_DROOL:
+ p = "drool on you";
+ break;
+ case RBM_SPIT:
+ p = "spit";
+ break;
+ case RBM_EXPLODE:
+ p = "explode";
+ break;
+ case RBM_GAZE:
+ p = "gaze";
+ break;
+ case RBM_WAIL:
+ p = "wail";
+ break;
+ case RBM_SPORE:
+ p = "release spores";
+ break;
+ case RBM_XXX4:
+ break;
+ case RBM_BEG:
+ p = "beg";
+ break;
+ case RBM_INSULT:
+ p = "insult";
+ break;
+ case RBM_MOAN:
+ p = "moan";
+ break;
+ case RBM_SHOW:
+ p = "sing";
+ break;
+ }
+
+
+ /* Default effect */
+ q = "???";
+
+ /* Acquire the effect */
+ switch (r_ptr->blow[j].effect)
+ {
+ case RBE_HURT:
+ q = "attack";
+ break;
+ case RBE_POISON:
+ q = "poison";
+ break;
+ case RBE_UN_BONUS:
+ q = "disenchant";
+ break;
+ case RBE_UN_POWER:
+ q = "drain charges";
+ break;
+ case RBE_EAT_GOLD:
+ q = "steal gold";
+ break;
+ case RBE_EAT_ITEM:
+ q = "steal items";
+ break;
+ case RBE_EAT_FOOD:
+ q = "eat your food";
+ break;
+ case RBE_EAT_LITE:
+ q = "absorb light";
+ break;
+ case RBE_ACID:
+ q = "shoot acid";
+ break;
+ case RBE_ELEC:
+ q = "electrocute";
+ break;
+ case RBE_FIRE:
+ q = "burn";
+ break;
+ case RBE_COLD:
+ q = "freeze";
+ break;
+ case RBE_BLIND:
+ q = "blind";
+ break;
+ case RBE_CONFUSE:
+ q = "confuse";
+ break;
+ case RBE_TERRIFY:
+ q = "terrify";
+ break;
+ case RBE_PARALYZE:
+ q = "paralyze";
+ break;
+ case RBE_LOSE_STR:
+ q = "reduce strength";
+ break;
+ case RBE_LOSE_INT:
+ q = "reduce intelligence";
+ break;
+ case RBE_LOSE_WIS:
+ q = "reduce wisdom";
+ break;
+ case RBE_LOSE_DEX:
+ q = "reduce dexterity";
+ break;
+ case RBE_LOSE_CON:
+ q = "reduce constitution";
+ break;
+ case RBE_LOSE_CHR:
+ q = "reduce charisma";
+ break;
+ case RBE_LOSE_ALL:
+ q = "reduce all stats";
+ break;
+ case RBE_SHATTER:
+ q = "shatter";
+ break;
+ case RBE_EXP_10:
+ q = "lower experience (by 10d6+)";
+ break;
+ case RBE_EXP_20:
+ q = "lower experience (by 20d6+)";
+ break;
+ case RBE_EXP_40:
+ q = "lower experience (by 40d6+)";
+ break;
+ case RBE_EXP_80:
+ q = "lower experience (by 80d6+)";
+ break;
+ case RBE_DISEASE:
+ q = "disease";
+ break;
+ case RBE_TIME:
+ q = "time";
+ break;
+ case RBE_SANITY:
+ q = "make insane";
+ break;
+ case RBE_HALLU:
+ q = "cause hallucinations";
+ break;
+ case RBE_PARASITE:
+ q = "parasite";
+ break;
+ }
+
+
+ if (!k)
+ {
+ spoil_out(wd_che[msex]);
+ spoil_out(" can ");
+ }
+ else if (k < i - 1)
+ {
+ spoil_out(", ");
+ }
+ else
+ {
+ spoil_out(", and ");
+ }
+
+ /* Describe the method */
+ spoil_out(p);
+
+ /* Describe the effect, if any */
+ if (r_ptr->blow[j].effect)
+ {
+ spoil_out(" to ");
+ spoil_out(q);
+ if (r_ptr->blow[j].d_dice && r_ptr->blow[j].d_side)
+ {
+ spoil_out(" with damage");
+ if (r_ptr->blow[j].d_side == 1)
+ sprintf(buf, " %d", r_ptr->blow[j].d_dice);
+ else
+ sprintf(buf, " %dd%d",
+ r_ptr->blow[j].d_dice, r_ptr->blow[j].d_side);
+ spoil_out(buf);
+ }
+ }
+
+ k++;
+ }
+
+ if (k)
+ {
+ spoil_out(". ");
+ }
+ else if (flags1 & (RF1_NEVER_BLOW))
+ {
+ sprintf(buf, "%s has no physical attacks. ", wd_che[msex]);
+ spoil_out(buf);
+ }
+
+ spoil_out(NULL);
+ }
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ msg_print("Successfully created a spoiler file.");
+}
+
+#if 0 /* not used anymore -- masmarangio */
+static char* get_tval_name(int tval)
+{
+ switch (tval)
+ {
+ case TV_SWORD:
+ return "Sword";
+ case TV_POLEARM:
+ return "Polearm";
+ case TV_HAFTED:
+ return "Hafted";
+ case TV_AXE:
+ return "Axe";
+ case TV_CROWN:
+ return "Crown";
+ case TV_HELM:
+ return "Helm";
+ case TV_GLOVES:
+ return "Gloves";
+ case TV_CLOAK:
+ return "Cloak";
+ case TV_BOOTS:
+ return "Boots";
+ case TV_SOFT_ARMOR:
+ return "Soft armor";
+ case TV_HARD_ARMOR:
+ return "Hard armor";
+ }
+ return "";
+}
+#endif
+
+char *long_intro =
+ "Essences are the tools of the trade for Alchemists, "
+ "and unfortunately are useless for any other class. "
+ "Alchemists use essences to create magical items for them to use.\n\n"
+ "They can be either found on the floor while exploring the dungeon, "
+ "or extracted from other magical items the alchemist finds "
+ "during their adventures.\n\n"
+ "To create an artifact, the alchemist will have to sacrifice 10 hit points, "
+ "and an amount of magic essence similar to his skill in alchemy. "
+ "The alchemist then allows the artifact to gain experience, "
+ "and when it has enough, "
+ "uses that experience to add abilities to the artifact. "
+ "The alchemist can allow the artifact to continue to gain experience, "
+ "thus keeping open the option to add more abilities later. "
+ "This requires a similar amount of magic essence, "
+ "but does not require the sacrifice of other hit points.\n\n"
+ "Note that the experience you gain is divided among the artifacts "
+ "that you have as well as going to yourself, "
+ "so you will gain levels more slowly when empowering artifacts. "
+ "Also, the artifact only gets 60% of the experience. "
+ "So killing a creature worth 20xp would gain 10 for you, "
+ "and 6 for the artifact.\n\n"
+ "You can also modify existing artifacts when you attain skill level 50. "
+ "Also at skill level 50 you will gain the ability to make temporary artifacts, "
+ "which don't require the complex empowerments that regular items require, "
+ "but also vanish after awhile.\n\n"
+ "You cannot give an artifact an ability "
+ "unless you have *Identified* an artifact which has that ability.\n\n"
+ "For every four levels gained in the alchemy skill, "
+ "the alchemist learns about objects of level (skill level)/4, "
+ "starting by learning about level 1 objects at skill level 0. "
+ "(actually 1, but who's counting?)\n\n"
+ "At skill level 5 you gain the ability to make ego items - but watch it! "
+ "Your base failure rate will be 90%, "
+ "and won't be 0% until you reach skill level 50. "
+ "Adding gold will increase the chances of success "
+ "in direct proportion to the value of the item you are trying to create. "
+ "Note that this results in automatic success "
+ "when the item you are trying to create "
+ "happens to pick up a curse in the process.\n\n"
+ "At skill level 5 you also gain knowledge of some basic ego item recipes. "
+ "These are: Acidic, Shocking, Fiery, Frozen, Venomous, and Chaotic weapons, "
+ "Resist Fire armour, and light sources of Fearlessness.\n\n"
+ "At skill level 10 you will gain knowledge of digging ego items, "
+ "if you have selected the option "
+ "'always generate very unusual rooms' (ironman_rooms).\n\n"
+ "At skill level 15 you can create ego wands, staves, rings, etc.\n\n"
+ "At skill level 25 you gain the ability to empower artifacts "
+ "and double ego items.\n\n"
+ "At skill level 50 you gain the ability to create temporary artifacts, "
+ "which don't require any exotic ingredients "
+ "beyond a single corpse of any type.\n\n"
+ "Between skill levels 25 and 50, "
+ "you will steadily gain the ability to set more and more flags.\n\n"
+ "To finalise an artifact, you 'P'ower it, and select the powers you want.\n"
+ "Powers are divided into the following six categories:\n"
+ "*****essences.txt*03[Stats, Sustains, Luck, Speed, Vision, etc.]\n"
+ "*****essences.txt*04[Misc. (Auras, Light, See Invisibility, etc.)]\n"
+ "*****essences.txt*05[Weapon Brands]\n"
+ "*****essences.txt*06[Resistances and Immunities]\n"
+ "*****essences.txt*07[ESP and Curses]\n"
+ "*****essences.txt*08[Artifact Activations]\n";
+
+/*
+ * Create a spoiler file for essences
+ */
+static void spoil_bateries(cptr fname)
+{
+ int j, i, tval, sval, group;
+ object_type forge, *o_ptr;
+
+ char buf[1024];
+
+ tval = sval = group = -1;
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+
+ fprintf(fff,
+ "|||||oy\n"
+ "~~~~~01|Spoilers|Essences\n"
+ "~~~~~02|Alchemist|Essence Spoiler\n"
+ "#####REssence Spoiler for %s %ld.%ld.%ld%s\n"
+ "#####R-----------------------------------\n\n",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+
+
+ /*New code starts here -*/
+ /*print header, including artifact header*/
+ /*Cycle through artifact flags*/
+ /* print desc*/
+ /* cycle through all alchemist_recipies*/
+ /* print matching*/
+ /*Print items header.*/
+ /*Cycle through alchemist_recipies*/
+ /* sval or tval changed?*/
+ /* skip artifacts (tval=0)*/
+ /* print item desc (ego (tval=1) or item)*/
+ /* print essences required*/
+ /*Done!*/
+
+ /*Print basic header.*/
+ spoil_out(long_intro);
+
+ /*Cycle through artifact flags*/
+ for ( i = 0 ; a_select_flags[i].group ; i++ )
+ {
+ if ( a_select_flags[i].group != group )
+ {
+ group = a_select_flags[i].group;
+ spoil_out("\n~~~~~");
+ switch (group)
+ {
+ case 1:
+ spoil_out("03\n#####GStats, Sustains, Luck, Speed, Vision, etc.\n");
+ break;
+ case 2:
+ spoil_out("04\n#####GMisc. (Auras, Light, See Invisibility, etc.)\n");
+ break;
+ case 3:
+ spoil_out("05\n#####GWeapon Brands\n");
+ break;
+ case 4:
+ spoil_out("06\n#####GResistances and Immunities\n");
+ break;
+ case 5:
+ spoil_out("07\n#####GESP and Curses\n");
+ break;
+ case 88:
+ spoil_out("08\n#####GArtifact Activations\n");
+ break;
+ default:
+ spoil_out(format("09\n#####GExtra Group=%d\n", group));
+ }
+ spoil_out("lvl xp Power\n");
+
+ }
+ /* print desc*/
+ spoil_out(format("%-2d %8d %-24s %s\n",
+ a_select_flags[i].level,
+ a_select_flags[i].xp,
+ al_name + a_select_flags[i].desc,
+ al_name + a_select_flags[i].item_desc));
+ /* cycle through all alchemist_recipies*/
+ for ( j = 0 ; j < max_al_idx ; j++ )
+ {
+ /* print matching*/
+ if (alchemist_recipes[j].tval == 0
+ && alchemist_recipes[j].sval == a_select_flags[i].flag
+ && alchemist_recipes[j].qty
+ )
+ {
+ spoil_out(format(" %d essences of %s\n", alchemist_recipes[j].qty,
+ k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[j].sval_essence)].name));
+ }
+ }
+ }
+
+ spoil_out("\n\nThe following basic item recipes also exist:\n");
+ /*Cycle through alchemist_recipies*/
+ for ( i = 0 ; i < max_al_idx ; i ++)
+ {
+ alchemist_recipe *ar_ptr = &alchemist_recipes[i];
+
+ /* sval or tval changed?*/
+ if (tval != ar_ptr->tval || sval != ar_ptr->sval)
+ {
+ char o_name[80];
+ /* skip artifacts (tval=0)*/
+ if (ar_ptr->tval == 0 )
+ continue;
+
+ tval = ar_ptr->tval;
+ sval = ar_ptr->sval;
+
+ /* print item desc (ego (tval=1)or item)*/
+ if (ar_ptr->tval == 1)
+ {
+ strcpy(o_name, e_name + e_info[ar_ptr->sval].name);
+ }
+ else
+ {
+ /* Find the name of that object */
+ o_ptr = &forge;
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ apply_magic(o_ptr, 1, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->name2 = o_ptr->name2b = 0;
+ /* the 0 mode means only the text, leaving off any numbers */
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+ spoil_out("\n");
+ spoil_out(o_name);
+ }
+ /* print essence required*/
+ spoil_out(format(" %d %s", ar_ptr->qty,
+ k_name + k_info[lookup_kind(TV_BATERIE, ar_ptr->sval_essence)].name));
+ }
+
+ spoil_out(NULL);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+ return;
+}
+
+
+/*
+ * Print a bookless spell list
+ */
+void print_magic_powers( magic_power *powers, int max_powers, void(*power_info)(char *p, int power), int skill_num )
+{
+ int i, save_skill;
+
+ char buf[80];
+
+ magic_power spell;
+
+ /* Use a maximal skill */
+ save_skill = s_info[skill_num].value;
+ s_info[skill_num].value = SKILL_MAX;
+
+ /* Dump the header line */
+ spoiler_blanklines(2);
+ sprintf(buf, "%s", s_info[skill_num].name + s_name);
+ spoiler_underline(buf);
+ spoiler_blanklines(1);
+
+ fprintf(fff, " Name Lvl Mana Fail Info\n");
+
+ /* Dump the spells */
+ for (i = 0; i < max_powers; i++)
+ {
+ /* Access the spell */
+ spell = powers[i];
+
+ /* Get the additional info */
+ power_info(buf, i);
+
+ /* Dump the spell */
+ spoil_out(format("%c) %-30s%2d %4d %3d%%%s\n",
+ I2A(i), spell.name,
+ spell.min_lev, spell.mana_cost, spell.fail, buf));
+ spoil_out(format("%s\n", spell.desc));
+ }
+
+ /* Restore skill */
+ s_info[skill_num].value = save_skill;
+}
+
+
+/*
+ * Create a spoiler file for spells
+ */
+
+static void spoil_spells(cptr fname)
+{
+ char buf[1024];
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* File type is "TEXT" */
+ FILE_TYPE(FILE_TYPE_TEXT);
+
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+ sprintf(buf, "Spell Spoiler (Skill Level 50) for %s %ld.%ld.%ld%s",
+ game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS);
+ spoiler_underline(buf);
+
+ /* Dump the bookless magic powers in alphabetical order */
+
+ /* Mimicry */
+ print_magic_powers(mimic_powers, MAX_MIMIC_POWERS, mimic_info, SKILL_MIMICRY);
+
+ /* Mindcraft */
+ print_magic_powers(mindcraft_powers, MAX_MINDCRAFT_POWERS, mindcraft_info, SKILL_MINDCRAFT);
+
+ /* Necromancy */
+ print_magic_powers(necro_powers, MAX_NECRO_POWERS, necro_info, SKILL_NECROMANCY);
+
+ /* Symbiosis */
+ print_magic_powers(symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info, SKILL_SYMBIOTIC);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+/*
+ * Forward declare
+ */
+extern void do_cmd_spoilers(void);
+
+/*
+ * Create Spoiler files -BEN-
+ */
+void do_cmd_spoilers(void)
+{
+ int i;
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Interact */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Info */
+ prt("Create a spoiler file.", 2, 0);
+
+ /* Prompt for a file */
+ prt("(1) Brief Object Info (obj-desc.spo)", 5, 5);
+ prt("(2) Full Artifact Info (artifact.spo)", 6, 5);
+ prt("(3) Brief Monster Info (mon-desc.spo)", 7, 5);
+ prt("(4) Full Monster Info (mon-info.spo)", 8, 5);
+ prt("(5) Full Essences Info (ess-info.spo)", 9, 5);
+ prt("(6) Spell Info (spell.spo)", 10, 5);
+
+ /* Prompt */
+ prt("Command: ", 12, 0);
+
+ /* Get a choice */
+ i = inkey();
+
+ /* Escape */
+ if (i == ESCAPE)
+ {
+ break;
+ }
+
+ /* Option (1) */
+ else if (i == '1')
+ {
+ spoil_obj_desc("obj-desc.spo");
+ }
+
+ /* Option (2) */
+ else if (i == '2')
+ {
+ spoil_artifact("artifact.spo");
+ }
+
+ /* Option (3) */
+ else if (i == '3')
+ {
+ spoil_mon_desc("mon-desc.spo");
+ }
+
+ /* Option (4) */
+ else if (i == '4')
+ {
+ spoil_mon_info("mon-info.spo");
+ }
+
+ /* Option (5) */
+ else if (i == '5')
+ {
+ spoil_bateries("ess-info.spo");
+ }
+
+ /* Option (6) */
+ else if (i == '6')
+ {
+ spoil_spells("spell.spo");
+ }
+
+ /* Oops */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+#else
+
+#ifdef MACINTOSH
+static int i = 0;
+#endif /* MACINTOSH */
+
+#endif
diff --git a/src/wizard2.c b/src/wizard2.c
new file mode 100644
index 00000000..31c74de5
--- /dev/null
+++ b/src/wizard2.c
@@ -0,0 +1,2065 @@
+/* File: wizard2.c */
+
+/* Purpose: Wizard commands */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+void do_cmd_wizard_body(s16b);
+extern void status_main(void);
+
+/*
+ * Adds a lvl to a monster
+ */
+void wiz_inc_monster_level(int level)
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ m_ptr->exp = MONSTER_EXP(m_ptr->level + level);
+ monster_check_experience(cave[jj][ii].m_idx, FALSE);
+ }
+}
+
+void wiz_align_monster(int status)
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!tgt_pt(&ii, &jj)) return;
+
+ if (cave[jj][ii].m_idx)
+ {
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ m_ptr->status = status;
+ }
+}
+
+/*
+ * Teleport directly to a town
+ */
+void teleport_player_town(int town)
+{
+ int x = 0, y = 0;
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Change town */
+ dun_level = 0;
+ p_ptr->town_num = town;
+
+ for (x = 0; x < max_wild_x; x++)
+ for (y = 0; y < max_wild_y; y++)
+ if (p_ptr->town_num == wf_info[wild_map[y][x].feat].entrance) goto finteletown;
+finteletown:
+ p_ptr->wilderness_y = y;
+ p_ptr->wilderness_x = x;
+
+ p_ptr->inside_arena = 0;
+ leaving_quest = p_ptr->inside_quest;
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Hack -- Rerate Hitpoints
+ */
+void do_cmd_rerate(void)
+{
+ int min_value, max_value, i, percent;
+
+#ifdef TEST
+ int fubar, mlk = 0;
+
+ for (fubar = 0; fubar < max_k_idx; fubar++)
+ {
+ if ((k_info[fubar].tval == TV_POTION) || (k_info[fubar].tval == TV_POTION2))
+ {
+ k_info[fubar].x_attr = 0xBC;
+ mlk++;
+ }
+ }
+
+ msg_format ("%d changes made.", mlk);
+
+#else /* TEST */
+
+#endif /* TEST */
+
+ min_value = (PY_MAX_LEVEL * 3 * (p_ptr->hitdie - 1)) / 8;
+ min_value += PY_MAX_LEVEL;
+
+ max_value = (PY_MAX_LEVEL * 5 * (p_ptr->hitdie - 1)) / 8;
+ max_value += PY_MAX_LEVEL;
+
+ player_hp[0] = p_ptr->hitdie;
+
+ /* Rerate */
+ while (1)
+ {
+ /* Collect values */
+ for (i = 1; i < PY_MAX_LEVEL; i++)
+ {
+ player_hp[i] = randint(p_ptr->hitdie);
+ player_hp[i] += player_hp[i - 1];
+ }
+
+ /* Legal values */
+ if ((player_hp[PY_MAX_LEVEL - 1] >= min_value) &&
+ (player_hp[PY_MAX_LEVEL - 1] <= max_value)) break;
+ }
+
+ percent = (int)(((long)player_hp[PY_MAX_LEVEL - 1] * 200L) /
+ (p_ptr->hitdie + ((PY_MAX_LEVEL - 1) * p_ptr->hitdie)));
+
+ /* Update and redraw hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Message */
+ msg_format("Current Life Rating is %d/100.", percent);
+}
+
+
+#ifdef ALLOW_WIZARD
+
+/*
+ * Create the artifact of the specified number -- DAN
+ *
+ */
+static void wiz_create_named_art()
+{
+ object_type forge;
+ object_type *q_ptr;
+ int i, a_idx;
+ cptr p = "Number of the artifact :";
+ char out_val[80];
+ artifact_type *a_ptr;
+
+ if (!get_string(p, out_val, 4)) return;
+ a_idx = atoi(out_val);
+
+ /* Return if out-of-bounds */
+ if ((a_idx <= 0) || (a_idx >= max_a_idx)) return;
+
+ a_ptr = &a_info[a_idx];
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Ignore "empty" artifacts */
+ if (!a_ptr->name) return;
+
+#if 0
+ /* Ignore generated artifacts */
+ if (a_ptr->cur_num) return;
+#endif
+
+ /* Acquire the "kind" index */
+ i = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Oops */
+ if (!i) return;
+
+ /* Create the artifact */
+ object_prep(q_ptr, i);
+
+ /* Save the name */
+ q_ptr->name1 = a_idx;
+
+#if 0 /* Old ugly method */
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ k_ptr = &k_info[q_ptr->k_idx];
+
+ /* Extract some flags */
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ q_ptr->elevel = (k_ptr->level / 10) + 1;
+ q_ptr->exp = player_exp[q_ptr->elevel - 1];
+ }
+
+ random_artifact_resistance(q_ptr);
+
+ a_ptr->cur_num = 1;
+#else
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+#endif
+
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+/*
+ * Hack -- quick debugging hook
+ */
+void do_cmd_wiz_hack_ben(int num)
+{
+ s32b a;
+
+ /* MAKE(r_ptr, monster_race);
+ COPY(r_ptr, &r_info[500], monster_race);
+
+ r_ptr->level = 1;
+ r_ptr->flags6 |= RF6_BLINK;
+ r_ptr->freq_inate = r_ptr->freq_spell = 90;
+
+ place_monster_one_race = r_ptr;
+ place_monster_one(p_ptr->py - 1, p_ptr->px, 500, 0, TRUE, MSTATUS_PET);*/
+
+ get_lua_var("a", 'd', &a);
+ msg_format("a: %d", a);
+
+ /* Success */
+ return;
+}
+
+void do_cmd_lua_script()
+{
+ char script[80] = "tome_dofile_anywhere(ANGBAND_DIR_CORE, 'gen_idx.lua')";
+
+ if (!get_string("Script:", script, 80)) return;
+
+ exec_lua(script);
+
+ /* Success */
+ return;
+}
+
+
+#ifdef MONSTER_HORDES
+
+/* Summon a horde of monsters */
+static void do_cmd_summon_horde()
+{
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 1000;
+
+ while (--attempts)
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3, 0);
+ if (cave_naked_bold(wy, wx)) break;
+ }
+
+ (void)alloc_horde(wy, wx);
+}
+
+#endif /* MONSTER_HORDES */
+
+
+/*
+ * Output a long int in binary format.
+ */
+static void prt_binary(u32b flags, int row, int col)
+{
+ int i;
+ u32b bitmask;
+
+ /* Scan the flags */
+ for (i = bitmask = 1; i <= 32; i++, bitmask *= 2)
+ {
+ /* Dump set bits */
+ if (flags & bitmask)
+ {
+ Term_putch(col++, row, TERM_BLUE, '*');
+ }
+
+ /* Dump unset bits */
+ else
+ {
+ Term_putch(col++, row, TERM_WHITE, '-');
+ }
+ }
+}
+
+
+/*
+ * Hack -- Teleport to the target
+ */
+static void do_cmd_wiz_bamf(void)
+{
+ /* Must have a target */
+ if (!target_who) return;
+
+ /* Teleport to the target */
+ teleport_player_to(target_row, target_col);
+}
+
+
+/*
+ * Aux function for "do_cmd_wiz_change()". -RAK-
+ */
+static void do_cmd_wiz_change_aux(void)
+{
+ int i;
+ int tmp_int;
+ long tmp_long;
+ char tmp_val[160];
+ char ppp[80];
+
+
+ /* Query the stats */
+ for (i = 0; i < 6; i++)
+ {
+ /* Prompt */
+ sprintf(ppp, "%s: (3-118): ", stat_names[i]);
+
+ /* Default */
+ sprintf(tmp_val, "%d", p_ptr->stat_max[i]);
+
+ /* Query */
+ if (!get_string(ppp, tmp_val, 3)) return;
+
+ /* Extract */
+ tmp_int = atoi(tmp_val);
+
+ /* Verify */
+ if (tmp_int > 18 + 100) tmp_int = 18 + 100;
+ else if (tmp_int < 3) tmp_int = 3;
+
+ /* Save it */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = tmp_int;
+ }
+
+
+ /* Default */
+ sprintf(tmp_val, "%ld", (long)(p_ptr->au));
+
+ /* Query */
+ if (!get_string("Gold: ", tmp_val, 9)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Verify */
+ if (tmp_long < 0) tmp_long = 0L;
+
+ /* Save */
+ p_ptr->au = tmp_long;
+
+
+ /* Default */
+ sprintf(tmp_val, "%ld", (long)(p_ptr->max_exp));
+
+ /* Query */
+ if (!get_string("Experience: ", tmp_val, 9)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Verify */
+ if (tmp_long < 0) tmp_long = 0L;
+
+ /* Save */
+ p_ptr->max_exp = tmp_long;
+ p_ptr->exp = tmp_long;
+
+ /* Update */
+ check_experience();
+
+ /* Default */
+ sprintf(tmp_val, "%d", p_ptr->luck_base);
+
+ /* Query */
+ if (!get_string("Luck(base): ", tmp_val, 3)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Save */
+ p_ptr->luck_base = tmp_long;
+ p_ptr->luck_max = tmp_long;
+}
+
+
+/*
+ * Change various "permanent" player variables.
+ */
+static void do_cmd_wiz_change(void)
+{
+ /* Interact */
+ do_cmd_wiz_change_aux();
+
+ /* Redraw everything */
+ do_cmd_redraw();
+}
+
+
+/*
+ * Wizard routines for creating objects -RAK-
+ * And for manipulating them! -Bernd-
+ *
+ * This has been rewritten to make the whole procedure
+ * of debugging objects much easier and more comfortable.
+ *
+ * The following functions are meant to play with objects:
+ * Create, modify, roll for them (for statistic purposes) and more.
+ * The original functions were by RAK.
+ * The function to show an item's debug information was written
+ * by David Reeve Sward <sward+@CMU.EDU>.
+ * Bernd (wiebelt@mathematik.hu-berlin.de)
+ *
+ * Here are the low-level functions
+ * - wiz_display_item()
+ * display an item's debug-info
+ * - wiz_create_itemtype()
+ * specify tval and sval (type and subtype of object)
+ * - wiz_tweak_item()
+ * specify pval, +AC, +tohit, +todam
+ * Note that the wizard can leave this function anytime,
+ * thus accepting the default-values for the remaining values.
+ * pval comes first now, since it is most important.
+ * - wiz_reroll_item()
+ * apply some magic to the item or turn it into an artifact.
+ * - wiz_roll_item()
+ * Get some statistics about the rarity of an item:
+ * We create a lot of fake items and see if they are of the
+ * same type (tval and sval), then we compare pval and +AC.
+ * If the fake-item is better or equal it is counted.
+ * Note that cursed items that are better or equal (absolute values)
+ * are counted, too.
+ * HINT: This is *very* useful for balancing the game!
+ * - wiz_quantity_item()
+ * change the quantity of an item, but be sane about it.
+ *
+ * And now the high-level functions
+ * - do_cmd_wiz_play()
+ * play with an existing object
+ * - wiz_create_item()
+ * create a new object
+ *
+ * Note -- You do not have to specify "pval" and other item-properties
+ * directly. Just apply magic until you are satisfied with the item.
+ *
+ * Note -- For some items (such as wands, staffs, some rings, etc), you
+ * must apply magic, or you will get "broken" or "uncharged" objects.
+ *
+ * Note -- Redefining artifacts via "do_cmd_wiz_play()" may destroy
+ * the artifact. Be careful.
+ *
+ * Hack -- this function will allow you to create multiple artifacts.
+ * This "feature" may induce crashes or other nasty effects.
+ */
+
+/*
+ * Just display an item's properties (debug-info)
+ * Originally by David Reeve Sward <sward+@CMU.EDU>
+ * Verbose item flags by -Bernd-
+ */
+static void wiz_display_item(object_type *o_ptr)
+{
+ int i, j = 13;
+ u32b f1, f2, f3, f4, f5, esp;
+ char buf[256];
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Clear the screen */
+ for (i = 1; i <= 23; i++) prt("", i, j - 2);
+
+ /* Describe fully */
+ object_desc_store(buf, o_ptr, TRUE, 3);
+
+ prt(buf, 2, j);
+
+ prt(format("kind = %-5d level = %-4d tval = %-5d sval = %-5d",
+ o_ptr->k_idx, k_info[o_ptr->k_idx].level,
+ o_ptr->tval, o_ptr->sval), 4, j);
+
+ prt(format("number = %-3d wgt = %-6d ac = %-5d damage = %dd%d",
+ o_ptr->number, o_ptr->weight,
+ o_ptr->ac, o_ptr->dd, o_ptr->ds), 5, j);
+
+ prt(format("pval = %-5d toac = %-5d tohit = %-4d todam = %-4d",
+ o_ptr->pval, o_ptr->to_a, o_ptr->to_h, o_ptr->to_d), 6, j);
+
+ prt(format("name1 = %-4d name2 = %-4d cost = %ld pval2 = %-5d",
+ o_ptr->name1, o_ptr->name2, (long)object_value(o_ptr), o_ptr->pval2), 7, j);
+
+ prt(format("ident = %04x timeout = %-d",
+ o_ptr->ident, o_ptr->timeout), 8, j);
+
+ prt("+------------FLAGS1------------+", 10, j);
+ prt("AFFECT........SLAY........BRAND.", 11, j);
+ prt(" cvae xsqpaefc", 12, j);
+ prt("siwdcc ssidsahanvudotgddhuoclio", 13, j);
+ prt("tnieoh trnipttmiinmrrnrrraiierl", 14, j);
+ prt("rtsxna..lcfgdkcpmldncltggpksdced", 15, j);
+ prt_binary(f1, 16, j);
+
+ prt("+------------FLAGS2------------+", 17, j);
+ prt("SUST....IMMUN.RESIST............", 18, j);
+ prt(" aefcprpsaefcpfldbc sn ", 19, j);
+ prt("siwdcc cliooeatcliooeialoshtncd", 20, j);
+ prt("tnieoh ierlifraierliatrnnnrhehi", 21, j);
+ prt("rtsxna..dcedslatdcedsrekdfddrxss", 22, j);
+ prt_binary(f2, 23, j);
+
+ prt("+------------FLAGS3------------+", 10, j + 32);
+ prt("fe ehsi st iiiiadta hp", 11, j + 32);
+ prt("il n taihnf ee ggggcregb vr", 12, j + 32);
+ prt("re nowysdose eld nnnntalrl ym", 13, j + 32);
+ prt("ec omrcyewta ieirmsrrrriieaeccc", 14, j + 32);
+ prt("aa taauktmatlnpgeihaefcvnpvsuuu", 15, j + 32);
+ prt("uu egirnyoahivaeggoclioaeoasrrr", 16, j + 32);
+ prt("rr litsopdretitsehtierltxrtesss", 17, j + 32);
+ prt("aa echewestreshtntsdcedeptedeee", 18, j + 32);
+ prt_binary(f3, 19, j + 32);
+}
+
+
+/*
+ * A list of tvals and their textual names
+ */
+tval_desc2 tvals[] =
+{
+ { TV_SWORD, "Sword" },
+ { TV_POLEARM, "Polearm" },
+ { TV_HAFTED, "Hafted Weapon" },
+ { TV_AXE, "Axe" },
+ { TV_BOW, "Bow" },
+ { TV_BOOMERANG, "Boomerang" },
+ { TV_ARROW, "Arrows" },
+ { TV_BOLT, "Bolts" },
+ { TV_SHOT, "Shots" },
+ { TV_SHIELD, "Shield" },
+ { TV_CROWN, "Crown" },
+ { TV_HELM, "Helm" },
+ { TV_GLOVES, "Gloves" },
+ { TV_BOOTS, "Boots" },
+ { TV_CLOAK, "Cloak" },
+ { TV_DRAG_ARMOR, "Dragon Scale Mail" },
+ { TV_HARD_ARMOR, "Hard Armor" },
+ { TV_SOFT_ARMOR, "Soft Armor" },
+ { TV_RING, "Ring" },
+ { TV_AMULET, "Amulet" },
+ { TV_LITE, "Lite" },
+ { TV_POTION, "Potion" },
+ { TV_POTION2, "Potion" },
+ { TV_SCROLL, "Scroll" },
+ { TV_WAND, "Wand" },
+ { TV_STAFF, "Staff" },
+ { TV_ROD_MAIN, "Rod" },
+ { TV_ROD, "Rod Tip" },
+ { TV_BOOK, "Schools Spellbook", },
+ { TV_SYMBIOTIC_BOOK, "Symbiotic Spellbook", },
+ { TV_DRUID_BOOK, "Elemental Stone" },
+ { TV_MUSIC_BOOK, "Music Book" },
+ { TV_DAEMON_BOOK, "Daemon Book" },
+ { TV_SPIKE, "Spikes" },
+ { TV_DIGGING, "Digger" },
+ { TV_CHEST, "Chest" },
+ { TV_FOOD, "Food" },
+ { TV_FLASK, "Flask" },
+ { TV_MSTAFF, "Mage Staff" },
+ { TV_BATERIE, "Essence" },
+ { TV_PARCHMENT, "Parchment" },
+ { TV_INSTRUMENT, "Musical Instrument" },
+ { TV_RUNE1, "Rune 1" },
+ { TV_RUNE2, "Rune 2" },
+ { TV_JUNK, "Junk" },
+ { TV_TRAPKIT, "Trapping Kit" },
+ { 0, NULL }
+};
+
+
+/*
+ * Strip an "object name" into a buffer
+ */
+static void strip_name(char *buf, int k_idx)
+{
+ char *t;
+
+ object_kind *k_ptr = &k_info[k_idx];
+
+ cptr str = (k_name + k_ptr->name);
+
+
+ /* Skip past leading characters */
+ while ((*str == ' ') || (*str == '&')) str++;
+
+ /* Copy useful chars */
+ for (t = buf; *str; str++)
+ {
+ if (*str != '~') *t++ = *str;
+ }
+
+ /* Terminate the new name */
+ *t = '\0';
+}
+
+
+/*
+ * Hack -- title for each column
+ *
+ * XXX XXX XXX This will not work with "EBCDIC", I would think.
+ */
+static char head[4] = { 'a', 'A', '0', ':' };
+
+
+/*
+ * Print a string as required by wiz_create_itemtype().
+ * Trims characters from the beginning until it fits in the space
+ * before the next row or the edge of the screen.
+ */
+static void wci_string(cptr string, int num)
+{
+ int row = 2 + (num % 20), col = 30 * (num / 20);
+ int ch = head[num / 20] + (num % 20), max_len = 0;
+
+ if (76-col < (signed)max_len)
+ max_len = 76-col;
+ else
+ max_len = 30-6;
+
+ if (strlen(string) > (unsigned)max_len)
+ string = string + (strlen(string) - max_len);
+
+ prt(format("[%c] %s", ch, string), row, col);
+}
+
+/*
+ * Specify tval and sval (type and subtype of object) originally
+ * by RAK, heavily modified by -Bernd-
+ *
+ * This function returns the k_idx of an object type, or zero if failed
+ *
+ * List up to 50 choices in three columns
+ */
+static int wiz_create_itemtype(void)
+{
+ int i, num, max_num;
+ int tval;
+
+ cptr tval_desc2;
+ char ch;
+
+ int choice[60];
+
+ char buf[160];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Print all tval's and their descriptions */
+ for (num = 0; (num < 60) && tvals[num].tval; num++)
+ {
+ wci_string(tvals[num].desc, num);
+ }
+
+ /* Me need to know the maximal possible tval_index */
+ max_num = num;
+
+ /* Choose! */
+ if (!get_com("Get what type of object? ", &ch)) return (0);
+
+ /* Analyze choice */
+ num = -1;
+ if ((ch >= head[0]) && (ch < head[0] + 20)) num = ch - head[0];
+ if ((ch >= head[1]) && (ch < head[1] + 20)) num = ch - head[1] + 20;
+ if ((ch >= head[2]) && (ch < head[2] + 17)) num = ch - head[2] + 40;
+
+ /* Bail out if choice is illegal */
+ if ((num < 0) || (num >= max_num)) return (0);
+
+ /* Base object type chosen, fill in tval */
+ tval = tvals[num].tval;
+ tval_desc2 = tvals[num].desc;
+
+
+ /*** And now we go for k_idx ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* We have to search the whole itemlist. */
+ for (num = 0, i = 1; (num < 60) && (i < max_k_idx); i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze matching items */
+ if (k_ptr->tval == tval)
+ {
+ /* Hack -- Skip instant artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* Acquire the "name" of object "i" */
+ strip_name(buf, i);
+
+ /* Print it */
+ wci_string(buf, num);
+
+ /* Remember the object index */
+ choice[num++] = i;
+ }
+ }
+
+ /* Me need to know the maximal possible remembered object_index */
+ max_num = num;
+
+ /* Choose! */
+ if (!get_com(format("What Kind of %s? ", tval_desc2), &ch)) return (0);
+
+ /* Analyze choice */
+ num = -1;
+ if ((ch >= head[0]) && (ch < head[0] + 20)) num = ch - head[0];
+ if ((ch >= head[1]) && (ch < head[1] + 20)) num = ch - head[1] + 20;
+ if ((ch >= head[2]) && (ch < head[2] + 17)) num = ch - head[2] + 40;
+
+ /* Bail out if choice is "illegal" */
+ if ((num < 0) || (num >= max_num)) return (0);
+
+ /* And return successful */
+ return (choice[num]);
+}
+
+
+/*
+ * Tweak an item
+ */
+static void wiz_tweak_item(object_type *o_ptr)
+{
+ cptr p;
+ char tmp_val[80];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+#if 0 /* DG -- A Wizard can do whatever he/she wants */
+ /* Hack -- leave artifacts alone */
+ if (artifact_p(o_ptr) || o_ptr->art_name) return;
+#endif
+
+ p = "Enter new 'pval' setting: ";
+ sprintf(tmp_val, "%ld", o_ptr->pval);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'pval2' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->pval2);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval2 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'pval3' setting: ";
+ sprintf(tmp_val, "%ld", o_ptr->pval3);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval3 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_a' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_a);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_a = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_h' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_h);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_h = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_d' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_d);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_d = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'name2' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->name2);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->name2 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'name2b' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->name2b);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->name2b = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'sval' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->sval);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->sval = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'obj exp' setting: ";
+ sprintf(tmp_val, "%ld", o_ptr->exp);
+ if (!get_string(p, tmp_val, 9)) return;
+ wiz_display_item(o_ptr);
+ o_ptr->exp = atoi(tmp_val);
+ if (f4 & TR4_LEVELS) check_experience_obj(o_ptr);
+
+ p = "Enter new 'timeout' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->timeout);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->timeout = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+}
+
+
+/*
+ * Apply magic to an item or turn it into an artifact. -Bernd-
+ */
+static void wiz_reroll_item(object_type *o_ptr)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ char ch;
+
+ bool changed = FALSE;
+
+
+ /* Hack -- leave artifacts alone */
+ if (artifact_p(o_ptr) || o_ptr->art_name) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+
+ /* Main loop. Ask for magification and artifactification */
+ while (TRUE)
+ {
+ /* Display full item debug information */
+ wiz_display_item(q_ptr);
+
+ /* Ask wizard what to do. */
+ if (!get_com("[a]ccept, [b]ad, [n]ormal, [g]ood, [e]xcellent, [r]andart? ", &ch))
+ {
+ changed = FALSE;
+ break;
+ }
+
+ /* Create/change it! */
+ if (ch == 'A' || ch == 'a')
+ {
+ changed = TRUE;
+ break;
+ }
+
+ /* Apply bad magic, but first clear object */
+ else if (ch == 'b' || ch == 'B')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ hack_apply_magic_power = -2;
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+
+ /* Apply normal magic, but first clear object */
+ else if (ch == 'n' || ch == 'N')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+
+ /* Apply good magic, but first clear object */
+ else if (ch == 'g' || ch == 'g')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, TRUE, FALSE);
+ }
+
+ /* Apply great magic, but first clear object */
+ else if (ch == 'e' || ch == 'e')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, TRUE, TRUE);
+ }
+
+ /* Apply great magic, but first clear object */
+ else if (ch == 'r' || ch == 'r')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ create_artifact(q_ptr, FALSE, TRUE);
+ }
+ }
+
+
+ /* Notice change */
+ if (changed)
+ {
+ /* Apply changes */
+ object_copy(o_ptr, q_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+}
+
+
+
+/*
+ * Maximum number of rolls
+ */
+#define TEST_ROLL 100000
+
+
+/*
+ * Try to create an item again. Output some statistics. -Bernd-
+ *
+ * The statistics are correct now. We acquire a clean grid, and then
+ * repeatedly place an object in this grid, copying it into an item
+ * holder, and then deleting the object. We fiddle with the artifact
+ * counter flags to prevent weirdness. We use the items to collect
+ * statistics on item creation relative to the initial item.
+ */
+static void wiz_statistics(object_type *o_ptr)
+{
+ long i, matches, better, worse, other;
+
+ char ch;
+ char *quality;
+
+ bool good, great;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ obj_theme theme;
+
+ cptr q = "Rolls: %ld, Matches: %ld, Better: %ld, Worse: %ld, Other: %ld";
+
+ /* We can have everything */
+ theme.treasure = OBJ_GENE_TREASURE;
+ theme.combat = OBJ_GENE_COMBAT;
+ theme.magic = OBJ_GENE_MAGIC;
+ theme.tools = OBJ_GENE_TOOL;
+
+ /* XXX XXX XXX Mega-Hack -- allow multiple artifacts */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = FALSE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 0;
+ }
+ }
+
+
+ /* Interact */
+ while (TRUE)
+ {
+ cptr pmt = "Roll for [n]ormal, [g]ood, or [e]xcellent treasure? ";
+
+ /* Display item */
+ wiz_display_item(o_ptr);
+
+ /* Get choices */
+ if (!get_com(pmt, &ch)) break;
+
+ if (ch == 'n' || ch == 'N')
+ {
+ good = FALSE;
+ great = FALSE;
+ quality = "normal";
+ }
+ else if (ch == 'g' || ch == 'G')
+ {
+ good = TRUE;
+ great = FALSE;
+ quality = "good";
+ }
+ else if (ch == 'e' || ch == 'E')
+ {
+ good = TRUE;
+ great = TRUE;
+ quality = "excellent";
+ }
+ else
+ {
+ good = FALSE;
+ great = FALSE;
+ break;
+ }
+
+ /* Let us know what we are doing */
+ msg_format("Creating a lot of %s items. Base level = %d.",
+ quality, dun_level);
+ msg_print(NULL);
+
+ /* Set counters to zero */
+ matches = better = worse = other = 0;
+
+ /* Let's rock and roll */
+ for (i = 0; i <= TEST_ROLL; i++)
+ {
+ /* Output every few rolls */
+ if ((i < 100) || (i % 100 == 0))
+ {
+ /* Do not wait */
+ inkey_scan = TRUE;
+
+ /* Allow interupt */
+ if (inkey())
+ {
+ /* Flush */
+ flush();
+
+ /* Stop rolling */
+ break;
+ }
+
+ /* Dump the stats */
+ prt(format(q, i, matches, better, worse, other), 0, 0);
+ Term_fresh();
+ }
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Create an object */
+ make_object(q_ptr, good, great, theme);
+
+
+ /* XXX XXX XXX Mega-Hack -- allow multiple artifacts */
+ if (artifact_p(q_ptr))
+ {
+ if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ else
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ }
+
+
+ /* Test for the same tval and sval. */
+ if ((o_ptr->tval) != (q_ptr->tval)) continue;
+ if ((o_ptr->sval) != (q_ptr->sval)) continue;
+
+ /* Check for match */
+ if ((q_ptr->pval == o_ptr->pval) &&
+ (q_ptr->to_a == o_ptr->to_a) &&
+ (q_ptr->to_h == o_ptr->to_h) &&
+ (q_ptr->to_d == o_ptr->to_d))
+ {
+ matches++;
+ }
+
+ /* Check for better */
+ else if ((q_ptr->pval >= o_ptr->pval) &&
+ (q_ptr->to_a >= o_ptr->to_a) &&
+ (q_ptr->to_h >= o_ptr->to_h) &&
+ (q_ptr->to_d >= o_ptr->to_d))
+ {
+ better++;
+ }
+
+ /* Check for worse */
+ else if ((q_ptr->pval <= o_ptr->pval) &&
+ (q_ptr->to_a <= o_ptr->to_a) &&
+ (q_ptr->to_h <= o_ptr->to_h) &&
+ (q_ptr->to_d <= o_ptr->to_d))
+ {
+ worse++;
+ }
+
+ /* Assume different */
+ else
+ {
+ other++;
+ }
+ }
+
+ /* Final dump */
+ msg_format(q, i, matches, better, worse, other);
+ msg_print(NULL);
+ }
+
+
+ /* Hack -- Normally only make a single artifact */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = TRUE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 1;
+ }
+ }
+}
+
+
+/*
+ * Change the quantity of a the item
+ */
+static void wiz_quantity_item(object_type *o_ptr)
+{
+ int tmp_int, tmp_qnt;
+
+ char tmp_val[100];
+
+
+#if 0 /* DG -- A Wizard can do whatever he/she/it wants */
+ /* Never duplicate artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) return;
+#endif
+
+ tmp_qnt = o_ptr->number;
+
+ /* Default */
+ sprintf(tmp_val, "%d", o_ptr->number);
+
+ /* Query */
+ if (get_string("Quantity: ", tmp_val, 2))
+ {
+ /* Extract */
+ tmp_int = atoi(tmp_val);
+
+ /* Paranoia */
+ if (tmp_int < 1) tmp_int = 1;
+ if (tmp_int > 99) tmp_int = 99;
+
+ /* Accept modifications */
+ o_ptr->number = tmp_int;
+ }
+}
+
+
+
+/*
+ * Play with an item. Options include:
+ * - Output statistics (via wiz_roll_item)
+ * - Reroll item (via wiz_reroll_item)
+ * - Change properties (via wiz_tweak_item)
+ * - Change the number of items (via wiz_quantity_item)
+ */
+static void do_cmd_wiz_play(void)
+{
+ int item;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ char ch;
+
+ bool changed;
+
+ cptr q, s;
+
+ /* Get an item */
+ q = "Play with which object? ";
+ s = "You have nothing to play with.";
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+
+ /* The item was not changed */
+ changed = FALSE;
+
+
+ /* Icky */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy object */
+ object_copy(q_ptr, o_ptr);
+
+
+ /* The main loop */
+ while (TRUE)
+ {
+ /* Display the item */
+ wiz_display_item(q_ptr);
+
+ /* Get choice */
+ if (!get_com("[a]ccept [s]tatistics [r]eroll [t]weak [q]uantity apply[m]agic? ", &ch))
+ {
+ changed = FALSE;
+ break;
+ }
+
+ if (ch == 'A' || ch == 'a')
+ {
+ changed = TRUE;
+ break;
+ }
+
+ if (ch == 's' || ch == 'S')
+ {
+ wiz_statistics(q_ptr);
+ }
+
+ if (ch == 'r' || ch == 'r')
+ {
+ wiz_reroll_item(q_ptr);
+ }
+
+ if (ch == 't' || ch == 'T')
+ {
+ wiz_tweak_item(q_ptr);
+ }
+
+ if (ch == 'q' || ch == 'Q')
+ {
+ wiz_quantity_item(q_ptr);
+ }
+
+ if (ch == 'm' || ch == 'M')
+ {
+ int e = q_ptr->name2, eb = q_ptr->name2b;
+
+ object_prep(q_ptr, q_ptr->k_idx);
+ q_ptr->name2 = e;
+ q_ptr->name2b = eb;
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Not Icky */
+ character_icky = FALSE;
+
+
+ /* Accept change */
+ if (changed)
+ {
+ /* Message */
+ msg_print("Changes accepted.");
+
+ /* Change */
+ object_copy(o_ptr, q_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ /* Ignore change */
+ else
+ {
+ msg_print("Changes ignored.");
+ }
+}
+
+
+/*
+ * Wizard routine for creating objects -RAK-
+ * Heavily modified to allow magification and artifactification -Bernd-
+ *
+ * Note that wizards cannot create objects on top of other objects.
+ *
+ * Hack -- this routine always makes a "dungeon object", and applies
+ * magic to it, and attempts to decline cursed items.
+ */
+static void wiz_create_item(void)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ int k_idx;
+
+
+ /* Icky */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Get object base type */
+ k_idx = wiz_create_itemtype();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Not Icky */
+ character_icky = FALSE;
+
+
+ /* Return if failed */
+ if (!k_idx) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, k_idx);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+/*
+ * As above, but takes the k_idx as a parameter instead of using menus.
+ */
+static void wiz_create_item_2(void)
+{
+ object_type forge;
+ object_type *q_ptr;
+ int a_idx;
+ cptr p = "Number of the object :";
+ char out_val[80] = "";
+
+ if (!get_string(p, out_val, 4)) return;
+ a_idx = atoi(out_val);
+
+ /* Return if failed or out-of-bounds */
+ if ((a_idx <= 0) || (a_idx >= max_k_idx)) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, a_idx);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+
+/*
+ * Cure everything instantly
+ */
+void do_cmd_wiz_cure_all(void)
+{
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Remove curses */
+ (void)remove_all_curse();
+
+ /* Restore stats */
+ (void)res_stat(A_STR, TRUE);
+ (void)res_stat(A_INT, TRUE);
+ (void)res_stat(A_WIS, TRUE);
+ (void)res_stat(A_CON, TRUE);
+ (void)res_stat(A_DEX, TRUE);
+ (void)res_stat(A_CHR, TRUE);
+
+ /* Restore the level */
+ (void)restore_level();
+
+ /* Heal the player */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Cure insanity of player */
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+
+ /* Heal the player monster */
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ if (o_ptr->k_idx)
+ {
+ r_ptr = &r_info[o_ptr->pval];
+ o_ptr->pval2 = o_ptr->pval3;
+ }
+
+ /* Restore mana */
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+
+ /* Cure stuff */
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_poisoned(0);
+ (void)set_afraid(0);
+ (void)set_paralyzed(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_slow(0);
+ p_ptr->black_breath = FALSE;
+
+ /* No longer hungry */
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Redraw everything */
+ do_cmd_redraw();
+}
+
+
+/*
+ * Go to any level
+ */
+static void do_cmd_wiz_jump(void)
+{
+ /* Ask for level */
+ if (command_arg <= 0)
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ msg_format("dungeon_type : %d", dungeon_type);
+ sprintf(ppp, "Jump to level (0-%d): ", d_info[dungeon_type].maxdepth);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > d_info[dungeon_type].maxdepth) command_arg = d_info[dungeon_type].maxdepth;
+
+ /* Accept request */
+ msg_format("You jump to dungeon level %d.", command_arg);
+
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+
+ /* Change level */
+ dun_level = command_arg;
+
+ p_ptr->inside_arena = 0;
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Become aware of a lot of objects
+ */
+static void do_cmd_wiz_learn(void)
+{
+ int i;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Scan every object */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Induce awareness */
+ if (k_ptr->level <= command_arg)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare object */
+ object_prep(q_ptr, i);
+
+ /* Awareness */
+ object_aware(q_ptr);
+ }
+ }
+}
+
+
+/*
+ * Summon some creatures
+ */
+static void do_cmd_wiz_summon(int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ }
+}
+
+
+/*
+ * Summon a creature of the specified type
+ *
+ * XXX XXX XXX This function is rather dangerous
+ */
+static void do_cmd_wiz_named(int r_idx, bool slp)
+{
+ int i, x, y;
+
+ /* Paranoia */
+ /* if (!r_idx) return; */
+
+ /* Prevent illegal monsters */
+ if (r_idx >= max_r_idx) return;
+
+ /* Try 10 times */
+ for (i = 0; i < 10; i++)
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d, 0);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place it (allow groups) */
+ m_allow_special[r_idx] = TRUE;
+ if (place_monster_aux(y, x, r_idx, slp, TRUE, MSTATUS_ENEMY)) break;
+ m_allow_special[r_idx] = FALSE;
+ }
+}
+
+
+/*
+ * Summon a creature of the specified type
+ *
+ * XXX XXX XXX This function is rather dangerous
+ */
+void do_cmd_wiz_named_friendly(int r_idx, bool slp)
+{
+ int i, x, y;
+
+ /* Paranoia */
+ /* if (!r_idx) return; */
+
+ /* Prevent illegal monsters */
+ if (r_idx >= max_r_idx) return;
+
+ /* Try 10 times */
+ m_allow_special[r_idx] = TRUE;
+ for (i = 0; i < 10; i++)
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d, 0);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place it (allow groups) */
+ if (place_monster_aux(y, x, r_idx, slp, TRUE, MSTATUS_PET)) break;
+ }
+ m_allow_special[r_idx] = FALSE;
+}
+
+
+
+/*
+ * Hack -- Delete all nearby monsters
+ */
+static void do_cmd_wiz_zap(void)
+{
+ int i;
+
+ /* Genocide everyone nearby */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Delete nearby monsters */
+ if (m_ptr->cdis <= MAX_SIGHT) delete_monster_idx(i);
+ }
+}
+
+
+extern void do_cmd_wiz_body(s16b bidx)
+ /* Might create problems with equipment slots. For safety,
+ be nude when calling this function */
+{
+ p_ptr->body_monster = bidx;
+ p_ptr->disembodied = FALSE;
+ p_ptr->chp = maxroll( (&r_info[bidx])->hdice, (&r_info[bidx])->hside);
+ do_cmd_redraw();
+}
+
+#ifdef ALLOW_SPOILERS
+
+/*
+ * External function
+ */
+extern void do_cmd_spoilers(void);
+
+#endif /* ALLOW_SPOILERS */
+
+
+
+/*
+ * Hack -- declare external function
+ */
+extern void do_cmd_debug(void);
+
+
+
+/*
+ * Ask for and parse a "debug command"
+ * The "command_arg" may have been set.
+ */
+void do_cmd_debug(void)
+{
+ int x, y;
+ char cmd;
+
+
+ /* Get a "debug command" */
+ get_com("Debug Command: ", &cmd);
+
+ /* Analyze the command */
+ switch (cmd)
+ {
+ /* Nothing */
+ case ESCAPE:
+ case ' ':
+ case '\n':
+ case '\r':
+ break;
+
+
+#ifdef ALLOW_SPOILERS
+
+ /* Hack -- Generate Spoilers */
+ case '"':
+ do_cmd_spoilers();
+ break;
+
+#endif /* ALLOW_SPOILERS */
+
+ case 'A':
+ status_main();
+ break;
+
+ /* Hack -- Help */
+ case '?':
+ do_cmd_help();
+ break;
+
+
+ /* Cure all maladies */
+ case 'a':
+ do_cmd_wiz_cure_all();
+ break;
+
+ /* Teleport to target */
+ case 'b':
+ do_cmd_wiz_bamf();
+ break;
+
+ case 'B':
+ do_cmd_wiz_body(command_arg);
+ break;
+
+ /* Create any object */
+ case '-':
+ wiz_create_item_2();
+ break;
+
+ /* Create any object */
+ case 'c':
+ wiz_create_item();
+ break;
+
+ /* Create a named artifact */
+ case 'C':
+ wiz_create_named_art();
+ break;
+
+ /* Detect everything */
+ case 'd':
+ detect_all(DEFAULT_RADIUS);
+ break;
+
+ /* Change of Dungeon type */
+ case 'D':
+ if ((command_arg >= 0) && (command_arg < max_d_idx))
+ {
+ dungeon_type = command_arg;
+ dun_level = d_info[dungeon_type].mindepth;
+ msg_format("You go into %s", d_text + d_info[dungeon_type].text);
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ break;
+
+ /* Edit character */
+ case 'e':
+ do_cmd_wiz_change();
+ break;
+
+ /* Change grid's mana */
+ case 'E':
+ cave[p_ptr->py][p_ptr->px].mana = command_arg;
+ break;
+
+ /* View item info */
+ case 'f':
+ identify_fully();
+ break;
+
+ /* Good Objects */
+ case 'g':
+ if (command_arg <= 0) command_arg = 1;
+ acquirement(p_ptr->py, p_ptr->px, command_arg, FALSE, TRUE);
+ break;
+
+ /* Hitpoint rerating */
+ case 'h':
+ do_cmd_rerate(); break;
+
+#ifdef MONSTER_HORDES
+ case 'H':
+ do_cmd_summon_horde(); break;
+#endif /* MONSTER_HORDES */
+
+ /* Identify */
+ case 'i':
+ (void)ident_spell();
+ break;
+
+ /* Go up or down in the dungeon */
+ case 'j':
+ do_cmd_wiz_jump();
+ break;
+
+ /* Self-Knowledge */
+ case 'k':
+ self_knowledge(NULL);
+ break;
+
+ /* Learn about objects */
+ case 'l':
+ do_cmd_wiz_learn();
+ break;
+
+ /* Magic Mapping */
+ case 'm':
+ map_area();
+ break;
+
+ /* corruption */
+ case 'M':
+ (void) gain_random_corruption(command_arg);
+ break;
+
+ /* Specific reward */
+ case 'r':
+ (void) gain_level_reward(command_arg);
+ break;
+
+ /* Create a trap */
+ case 'R':
+ wiz_place_trap(p_ptr->py, p_ptr->px, command_arg);
+ break;
+
+ /* Summon _friendly_ named monster */
+ case 'N':
+ do_cmd_wiz_named_friendly(command_arg, TRUE);
+ break;
+
+ /* Summon Named Monster */
+ case 'n':
+ do_cmd_wiz_named(command_arg, TRUE);
+ break;
+
+ /* Object playing routines */
+ case 'o':
+ do_cmd_wiz_play();
+ break;
+
+ /* Phase Door */
+ case 'p':
+ teleport_player(10);
+ break;
+
+ /* Panic save the game */
+ case 'P':
+ exit_game_panic();
+ break;
+
+ /* get a Quest */
+ case 'q':
+ {
+ /* if (quest[command_arg].status == QUEST_STATUS_UNTAKEN)*/
+ if ((command_arg >= 1) && (command_arg < MAX_Q_IDX_INIT) && (command_arg != QUEST_RANDOM))
+ {
+ quest[command_arg].status = QUEST_STATUS_TAKEN;
+ *(quest[command_arg].plot) = command_arg;
+ if (quest[command_arg].type == HOOK_TYPE_C) quest[command_arg].init(command_arg);
+ break;
+ }
+ break;
+ }
+
+ /* Make every dungeon square "known" to test streamers -KMW- */
+ case 'u':
+ {
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ wiz_lite();
+ break;
+ }
+
+ case 'U':
+ {
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ do_cmd_wiz_named(5, TRUE);
+
+ p_ptr->necro_extra2 = 1;
+
+ /* Display the hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ break;
+ }
+
+ /* Summon Random Monster(s) */
+ case 's':
+ if (command_arg <= 0) command_arg = 1;
+ do_cmd_wiz_summon(command_arg);
+ break;
+
+ /* Change the feature of the map */
+ case 'S':
+ cave[p_ptr->py][p_ptr->px].special = command_arg;
+ break;
+
+ /* Teleport */
+ case 't':
+ teleport_player_bypass = TRUE;
+ teleport_player(100);
+ teleport_player_bypass = FALSE;
+ break;
+
+ /* Teleport to a town */
+ case 'T':
+ if ((command_arg >= 1) && (command_arg <= max_real_towns))
+ teleport_player_town(command_arg);
+ break;
+
+ /* Very Good Objects */
+ case 'v':
+ if (command_arg <= 0) command_arg = 1;
+ acquirement(p_ptr->py, p_ptr->px, command_arg, TRUE, TRUE);
+ break;
+
+ /* Wizard Light the Level */
+ case 'w':
+ wiz_lite();
+ break;
+
+ /* Make a wish */
+ case 'W':
+ make_wish();
+ break;
+
+ /* Increase Experience */
+ case 'x':
+ if (command_arg)
+ {
+ gain_exp(command_arg);
+ }
+ else
+ {
+ gain_exp(p_ptr->exp + 1);
+ }
+ break;
+
+ /* Zap Monsters (Genocide) */
+ case 'z':
+ do_cmd_wiz_zap();
+ break;
+
+ /* Hack -- whatever I desire */
+ case '_':
+ do_cmd_wiz_hack_ben(command_arg);
+ break;
+
+ /* Mimic shape changing */
+ case '*':
+ p_ptr->tim_mimic = 100;
+ p_ptr->mimic_form = command_arg;
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+ break;
+
+ /* Gain a fate */
+ case '+':
+ {
+ int i;
+ gain_fate(command_arg);
+ for (i = 0; i < MAX_FATES; i++)
+ fates[i].know = TRUE;
+ break;
+ }
+
+ /* Change the feature of the map */
+ case 'F':
+ msg_format("Trap: %d", cave[p_ptr->py][p_ptr->px].t_idx);
+ msg_format("Old feature: %d", cave[p_ptr->py][p_ptr->px].feat);
+ msg_format("Special: %d", cave[p_ptr->py][p_ptr->px].special);
+ cave_set_feat(p_ptr->py, p_ptr->px, command_arg);
+ break;
+
+ case '=':
+ wiz_align_monster(command_arg);
+ break;
+
+ case '@':
+ wiz_inc_monster_level(command_arg);
+ break;
+
+ case '/':
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], command_arg);
+ break;
+
+ case '>':
+ do_cmd_lua_script();
+ break;
+
+ /* Not a Wizard Command */
+ default:
+ if (!process_hooks(HOOK_DEBUG_COMMAND, "(d)", cmd))
+ msg_print("That is not a valid debug command.");
+ break;
+ }
+}
+
+
+#else
+
+#ifdef MACINTOSH
+static int i = 0;
+#endif
+
+#endif
+
diff --git a/src/xtra1.c b/src/xtra1.c
new file mode 100644
index 00000000..88a2f0c7
--- /dev/null
+++ b/src/xtra1.c
@@ -0,0 +1,4855 @@
+/* File: misc.c */
+
+/* Purpose: misc code */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+
+/*
+ * Converts stat num into a six-char (right justified) string
+ */
+void cnv_stat(int val, char *out_val)
+{
+ if (!linear_stats)
+ {
+ /* Above 18 */
+ if (val > 18)
+ {
+ int bonus = (val - 18);
+
+ if (bonus >= 220)
+ {
+ sprintf(out_val, "18/%3s", "***");
+ }
+ else if (bonus >= 100)
+ {
+ sprintf(out_val, "18/%03d", bonus);
+ }
+ else
+ {
+ sprintf(out_val, " 18/%02d", bonus);
+ }
+ }
+
+ /* From 3 to 18 */
+ else
+ {
+ sprintf(out_val, " %2d", val);
+ }
+ }
+ else
+ {
+ /* Above 18 */
+ if (val > 18)
+ {
+ int bonus = (val - 18);
+
+ if (bonus >= 220)
+ {
+ sprintf(out_val, " 40");
+ }
+ else
+ {
+ sprintf(out_val, " %2d", 18 + (bonus / 10));
+ }
+ }
+
+ /* From 3 to 18 */
+ else
+ {
+ sprintf(out_val, " %2d", val);
+ }
+ }
+}
+
+
+
+/*
+ * Modify a stat value by a "modifier", return new value
+ *
+ * Stats go up: 3,4,...,17,18,18/10,18/20,...,18/220
+ * Or even: 18/13, 18/23, 18/33, ..., 18/220
+ *
+ * Stats go down: 18/220, 18/210,..., 18/10, 18, 17, ..., 3
+ * Or even: 18/13, 18/03, 18, 17, ..., 3
+ */
+s16b modify_stat_value(int value, int amount)
+{
+ int i;
+
+ /* Reward */
+ if (amount > 0)
+ {
+ /* Apply each point */
+ for (i = 0; i < amount; i++)
+ {
+ /* One point at a time */
+ if (value < 18) value++;
+
+ /* Ten "points" at a time */
+ else value += 10;
+ }
+ }
+
+ /* Penalty */
+ else if (amount < 0)
+ {
+ /* Apply each point */
+ for (i = 0; i < (0 - amount); i++)
+ {
+ /* Ten points at a time */
+ if (value >= 18 + 10) value -= 10;
+
+ /* Hack -- prevent weirdness */
+ else if (value > 18) value = 18;
+
+ /* One point at a time */
+ else if (value > 3) value--;
+ }
+ }
+
+ /* Return new value */
+ return (value);
+}
+
+
+
+/*
+ * Print character info at given row, column in a 13 char field
+ */
+static void prt_field(cptr info, int row, int col)
+{
+ /* Dump 13 spaces to clear */
+ c_put_str(TERM_WHITE, " ", row, col);
+
+ /* Dump the info itself */
+ c_put_str(TERM_L_BLUE, info, row, col);
+}
+
+/*
+ * Prints players max/cur piety
+ */
+static void prt_piety(void)
+{
+ char tmp[32];
+
+ /* Do not show piety unless it matters */
+ if (!p_ptr->pgod) return;
+
+ c_put_str(TERM_L_WHITE, "Pt ", ROW_PIETY, COL_PIETY);
+
+ sprintf(tmp, "%9ld", p_ptr->grace);
+
+ c_put_str((p_ptr->praying) ? TERM_L_BLUE : TERM_GREEN, tmp, ROW_PIETY,
+ COL_PIETY + 3);
+}
+
+
+/*
+ * Prints the player's current sanity.
+ */
+static void prt_sane(void)
+{
+ char tmp[32];
+ byte color;
+ int perc;
+
+ if (p_ptr->msane == 0)
+ {
+ perc = 100;
+ }
+ else
+ {
+ perc = (100 * p_ptr->csane) / p_ptr->msane;
+ }
+
+ c_put_str(TERM_ORANGE, "SN ", ROW_SANITY, COL_SANITY);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->csane, p_ptr->msane);
+
+ if (perc >= 100)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (perc > (10 * hitpoint_warn))
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+
+ c_put_str(color, tmp, ROW_SANITY, COL_SANITY + 3);
+}
+
+
+/*
+ * Print character stat in given row, column
+ */
+static void prt_stat(int stat)
+{
+ char tmp[32];
+
+ cnv_stat(p_ptr->stat_use[stat], tmp);
+
+ /* Display "injured" stat */
+ if (p_ptr->stat_cur[stat] < p_ptr->stat_max[stat])
+ {
+ int colour;
+
+ if (p_ptr->stat_cnt[stat])
+ colour = TERM_ORANGE;
+ else
+ colour = TERM_YELLOW;
+
+ put_str(format("%s: ", stat_names_reduced[stat]), ROW_STAT + stat, 0);
+ c_put_str(colour, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Display "healthy" stat */
+ else
+ {
+ put_str(format("%s: ", stat_names[stat]), ROW_STAT + stat, 0);
+ c_put_str(TERM_L_GREEN, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Display "boosted" stat */
+ if (p_ptr->stat_cur[stat] > p_ptr->stat_max[stat])
+ {
+ put_str(format("%s: ", stat_names[stat]), ROW_STAT + stat, 0);
+ c_put_str(TERM_VIOLET, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Indicate natural maximum */
+ if (p_ptr->stat_max[stat] == 18 + 100)
+ {
+ put_str("!", ROW_STAT + stat, 3);
+ }
+}
+
+
+
+
+/*
+ * Prints "title", including "wizard" or "winner" as needed.
+ */
+static void prt_title(void)
+{
+ cptr p = "";
+
+ /* Mimic shape */
+ if (p_ptr->mimic_form)
+ {
+ call_lua("get_mimic_info", "(d,s)", "s", p_ptr->mimic_form, "show_name", &p);
+ }
+
+ /* Wizard */
+ else if (wizard)
+ {
+ p = "[=-WIZARD-=]";
+ }
+
+ /* Winner */
+ else if (total_winner == WINNER_NORMAL)
+ {
+ p = "***WINNER***";
+ }
+
+ /* Ultra Winner */
+ else if (total_winner == WINNER_ULTRA)
+ {
+ p = "***GOD***";
+ }
+
+ /* Normal */
+ else
+ {
+ p = cp_ptr->titles[(p_ptr->lev - 1) / 5] + c_text;
+
+ }
+
+ prt_field(p, ROW_TITLE, COL_TITLE);
+}
+
+
+/*
+ * Prints level
+ */
+static void prt_level(void)
+{
+ char tmp[32];
+
+ sprintf(tmp, "%6d", p_ptr->lev);
+
+ if (p_ptr->lev >= p_ptr->max_plv)
+ {
+ put_str("LEVEL ", ROW_LEVEL, 0);
+ c_put_str(TERM_L_GREEN, tmp, ROW_LEVEL, COL_LEVEL + 6);
+ }
+ else
+ {
+ put_str("Level ", ROW_LEVEL, 0);
+ c_put_str(TERM_YELLOW, tmp, ROW_LEVEL, COL_LEVEL + 6);
+ }
+}
+
+
+/*
+ * Display the experience
+ */
+static void prt_exp(void)
+{
+ char out_val[32];
+
+ if (!exp_need)
+ {
+ (void)sprintf(out_val, "%8ld", (long)p_ptr->exp);
+ }
+ else
+ {
+ if ((p_ptr->lev >= PY_MAX_LEVEL) || (p_ptr->lev >= max_plev))
+ {
+ (void)sprintf(out_val, "********");
+ }
+ else
+ {
+ (void)sprintf(out_val, "%8ld", (long)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L) - p_ptr->exp);
+ }
+ }
+
+ if (p_ptr->exp >= p_ptr->max_exp)
+ {
+ put_str("EXP ", ROW_EXP, 0);
+ c_put_str(TERM_L_GREEN, out_val, ROW_EXP, COL_EXP + 4);
+ }
+ else
+ {
+ put_str("Exp ", ROW_EXP, 0);
+ c_put_str(TERM_YELLOW, out_val, ROW_EXP, COL_EXP + 4);
+ }
+}
+
+
+/*
+ * Prints current gold
+ */
+static void prt_gold(void)
+{
+ char tmp[32];
+
+ put_str("AU ", ROW_GOLD, COL_GOLD);
+ sprintf(tmp, "%9ld", (long)p_ptr->au);
+ c_put_str(TERM_L_GREEN, tmp, ROW_GOLD, COL_GOLD + 3);
+}
+
+
+
+/*
+ * Prints current AC
+ */
+static void prt_ac(void)
+{
+ char tmp[32];
+
+ put_str("Cur AC ", ROW_AC, COL_AC);
+ sprintf(tmp, "%5d", p_ptr->dis_ac + p_ptr->dis_to_a);
+ c_put_str(TERM_L_GREEN, tmp, ROW_AC, COL_AC + 7);
+}
+
+
+/*
+ * Prints Cur/Max hit points
+ */
+static void prt_hp(void)
+{
+ char tmp[32];
+
+ byte color;
+
+ if (player_char_health) lite_spot(p_ptr->py, p_ptr->px);
+
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ c_put_str(TERM_L_DARK, "DP ", ROW_HP, COL_HP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->chp, p_ptr->mhp);
+
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_BLUE;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_VIOLET;
+ }
+ else
+ {
+ color = TERM_L_RED;
+ }
+ c_put_str(color, tmp, ROW_HP, COL_HP + 3);
+ }
+ else
+ {
+ c_put_str(TERM_RED, "HP ", ROW_HP, COL_HP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->chp, p_ptr->mhp);
+
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_HP, COL_HP + 3);
+ }
+}
+
+/*
+ * Prints Cur/Max monster hit points
+ */
+static void prt_mh(void)
+{
+ char tmp[32];
+
+ byte color;
+
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (!o_ptr->pval2)
+ {
+ put_str(" ", ROW_MH, COL_MH);
+ return;
+ }
+
+ r_ptr = &r_info[o_ptr->pval];
+
+ put_str("MH ", ROW_MH, COL_MH);
+
+ sprintf(tmp, "%4d/%4d", o_ptr->pval2, (int)o_ptr->pval3);
+ if (o_ptr->pval2 >= o_ptr->pval3)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (o_ptr->pval2 > (o_ptr->pval3 * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_MH, COL_MH + 3);
+}
+
+
+/*
+ * Prints players max/cur spell points
+ */
+static void prt_sp(void)
+{
+ char tmp[32];
+ byte color;
+
+
+ c_put_str(TERM_L_GREEN, "SP ", ROW_SP, COL_SP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->csp, p_ptr->msp);
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_SP, COL_SP + 3);
+}
+
+
+/*
+ * Prints depth in stat area
+ */
+static void prt_depth(void)
+{
+ char depths[32];
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ if (p_ptr->wild_mode)
+ {
+ strcpy(depths, " ");
+ }
+ else if (p_ptr->inside_arena)
+ {
+ strcpy(depths, "Arena");
+ }
+ else if (get_dungeon_name(depths))
+ {
+ /* Empty */
+ }
+ else if (dungeon_flags2 & DF2_SPECIAL)
+ {
+ strcpy(depths, "Special");
+ }
+ else if (p_ptr->inside_quest)
+ {
+ strcpy(depths, "Quest");
+ }
+ else if (!dun_level)
+ {
+ if (wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].name + wf_name)
+ strcpy(depths, wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].name + wf_name);
+ else
+ strcpy(depths, "Town/Wild");
+ }
+ else if (depth_in_feet)
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ (void)strnfmt(depths, 32, "%c%c%c -%d ft",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level * 50);
+ }
+ else
+ {
+ (void)strnfmt(depths, 32, "%c%c%c %d ft",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level * 50);
+ }
+ }
+ else
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ (void)strnfmt(depths, 32, "%c%c%c -%d",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level);
+ }
+ else
+ {
+ (void)strnfmt(depths, 32, "%c%c%c %d",
+ d_ptr->short_name[0],
+ d_ptr->short_name[1],
+ d_ptr->short_name[2],
+ dun_level);
+ }
+ }
+
+ /* Right-Adjust the "depth", and clear old values */
+ if (p_ptr->word_recall)
+ c_prt(TERM_ORANGE, format("%13s", depths), ROW_DEPTH, COL_DEPTH);
+ else
+ prt(format("%13s", depths), ROW_DEPTH, COL_DEPTH);
+}
+
+
+/*
+ * Prints status of hunger
+ */
+static void prt_hunger(void)
+{
+ /* Fainting / Starving */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ c_put_str(TERM_RED, "Weak ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Weak */
+ else if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ c_put_str(TERM_ORANGE, "Weak ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Hungry */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ c_put_str(TERM_YELLOW, "Hungry", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Normal */
+ else if (p_ptr->food < PY_FOOD_FULL)
+ {
+ c_put_str(TERM_L_GREEN, " ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Full */
+ else if (p_ptr->food < PY_FOOD_MAX)
+ {
+ c_put_str(TERM_L_GREEN, "Full ", ROW_HUNGRY, COL_HUNGRY);
+ }
+
+ /* Gorged */
+ else
+ {
+ c_put_str(TERM_GREEN, "Gorged", ROW_HUNGRY, COL_HUNGRY);
+ }
+}
+
+
+/*
+ * Prints Blind status
+ */
+static void prt_blind(void)
+{
+ if (p_ptr->blind)
+ {
+ c_put_str(TERM_ORANGE, "Blind", ROW_BLIND, COL_BLIND);
+ }
+ else
+ {
+ put_str(" ", ROW_BLIND, COL_BLIND);
+ }
+}
+
+
+/*
+ * Prints Confusion status
+ */
+static void prt_confused(void)
+{
+ if (p_ptr->confused)
+ {
+ c_put_str(TERM_ORANGE, "Conf", ROW_CONFUSED, COL_CONFUSED);
+ }
+ else
+ {
+ put_str(" ", ROW_CONFUSED, COL_CONFUSED);
+ }
+}
+
+
+/*
+ * Prints Fear status
+ */
+static void prt_afraid(void)
+{
+ if (p_ptr->afraid)
+ {
+ c_put_str(TERM_ORANGE, "Afraid", ROW_AFRAID, COL_AFRAID);
+ }
+ else
+ {
+ put_str(" ", ROW_AFRAID, COL_AFRAID);
+ }
+}
+
+
+/*
+ * Prints Poisoned status
+ */
+static void prt_poisoned(void)
+{
+ if (p_ptr->poisoned)
+ {
+ c_put_str(TERM_ORANGE, "Poison", ROW_POISONED, COL_POISONED);
+ }
+ else
+ {
+ put_str(" ", ROW_POISONED, COL_POISONED);
+ }
+}
+
+
+/*
+ * Prints trap detection status
+ */
+static void prt_dtrap(void)
+{
+ if (cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT)
+ {
+ c_put_str(TERM_L_GREEN, "DTrap", ROW_DTRAP, COL_DTRAP);
+ }
+ else
+ {
+ put_str(" ", ROW_DTRAP, COL_DTRAP);
+ }
+}
+
+
+/*
+ * Prints Searching, Resting, Paralysis, or 'count' status
+ * Display is always exactly 10 characters wide (see below)
+ *
+ * This function was a major bottleneck when resting, so a lot of
+ * the text formatting code was optimized in place below.
+ */
+static void prt_state(void)
+{
+ byte attr = TERM_WHITE;
+
+ char text[16];
+
+
+ /* Paralysis */
+ if (p_ptr->paralyzed)
+ {
+ attr = TERM_RED;
+
+ strcpy(text, "Paralyzed!");
+ }
+
+ /* Resting */
+ else if (resting)
+ {
+ int i;
+
+ /* Start with "Rest" */
+ strcpy(text, "Rest ");
+
+ /* Extensive (timed) rest */
+ if (resting >= 1000)
+ {
+ i = resting / 100;
+ text[9] = '0';
+ text[8] = '0';
+ text[7] = '0' + (i % 10);
+ if (i >= 10)
+ {
+ i = i / 10;
+ text[6] = '0' + (i % 10);
+ if (i >= 10)
+ {
+ text[5] = '0' + (i / 10);
+ }
+ }
+ }
+
+ /* Long (timed) rest */
+ else if (resting >= 100)
+ {
+ i = resting;
+ text[9] = '0' + (i % 10);
+ i = i / 10;
+ text[8] = '0' + (i % 10);
+ text[7] = '0' + (i / 10);
+ }
+
+ /* Medium (timed) rest */
+ else if (resting >= 10)
+ {
+ i = resting;
+ text[9] = '0' + (i % 10);
+ text[8] = '0' + (i / 10);
+ }
+
+ /* Short (timed) rest */
+ else if (resting > 0)
+ {
+ i = resting;
+ text[9] = '0' + (i);
+ }
+
+ /* Rest until healed */
+ else if (resting == -1)
+ {
+ text[5] = text[6] = text[7] = text[8] = text[9] = '*';
+ }
+
+ /* Rest until done */
+ else if (resting == -2)
+ {
+ text[5] = text[6] = text[7] = text[8] = text[9] = '&';
+ }
+ }
+
+ /* Repeating */
+ else if (command_rep)
+ {
+ if (command_rep > 999)
+ {
+ (void)sprintf(text, "Rep. %3d00", command_rep / 100);
+ }
+ else
+ {
+ (void)sprintf(text, "Repeat %3d", command_rep);
+ }
+ }
+
+ /* Searching */
+ else if (p_ptr->searching)
+ {
+ strcpy(text, "Searching ");
+ }
+
+ /* Nothing interesting */
+ else
+ {
+ strcpy(text, " ");
+ }
+
+ /* Display the info (or blanks) */
+ c_put_str(attr, text, ROW_STATE, COL_STATE);
+}
+
+
+/*
+ * Prints the speed of a character. -CJS-
+ */
+static void prt_speed(void)
+{
+ int i = p_ptr->pspeed;
+
+ byte attr = TERM_WHITE;
+ char buf[32] = "";
+
+ /* Hack -- Visually "undo" the Search Mode Slowdown */
+ if (p_ptr->searching) i += 10;
+
+ /* Fast */
+ if (i > 110)
+ {
+ attr = TERM_L_GREEN;
+ sprintf(buf, "Fast (+%d)", (i - 110));
+ }
+
+ /* Slow */
+ else if (i < 110)
+ {
+ attr = TERM_L_UMBER;
+ sprintf(buf, "Slow (-%d)", (110 - i));
+ }
+
+ /* Display the speed */
+ c_put_str(attr, format("%-10s", buf), ROW_SPEED, COL_SPEED);
+}
+
+
+static void prt_study(void)
+{
+ if (p_ptr->skill_points)
+ {
+ put_str("Skill", ROW_STUDY, COL_STUDY);
+ }
+ else
+ {
+ put_str(" ", ROW_STUDY, COL_STUDY);
+ }
+}
+
+
+static void prt_cut(void)
+{
+ int c = p_ptr->cut;
+
+ if (c > 1000)
+ {
+ c_put_str(TERM_L_RED, "Mortal wound", ROW_CUT, COL_CUT);
+ }
+ else if (c > 200)
+ {
+ c_put_str(TERM_RED, "Deep gash ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 100)
+ {
+ c_put_str(TERM_RED, "Severe cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 50)
+ {
+ c_put_str(TERM_ORANGE, "Nasty cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 25)
+ {
+ c_put_str(TERM_ORANGE, "Bad cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c > 10)
+ {
+ c_put_str(TERM_YELLOW, "Light cut ", ROW_CUT, COL_CUT);
+ }
+ else if (c)
+ {
+ c_put_str(TERM_YELLOW, "Graze ", ROW_CUT, COL_CUT);
+ }
+ else
+ {
+ put_str(" ", ROW_CUT, COL_CUT);
+ }
+}
+
+
+
+static void prt_stun(void)
+{
+ int s = p_ptr->stun;
+
+ if (s > 100)
+ {
+ c_put_str(TERM_RED, "Knocked out ", ROW_STUN, COL_STUN);
+ }
+ else if (s > 50)
+ {
+ c_put_str(TERM_ORANGE, "Heavy stun ", ROW_STUN, COL_STUN);
+ }
+ else if (s)
+ {
+ c_put_str(TERM_ORANGE, "Stun ", ROW_STUN, COL_STUN);
+ }
+ else
+ {
+ put_str(" ", ROW_STUN, COL_STUN);
+ }
+}
+
+
+
+/*
+ * Redraw the "monster health bar" -DRS-
+ * Rather extensive modifications by -BEN-
+ *
+ * The "monster health bar" provides visual feedback on the "health"
+ * of the monster currently being "tracked". There are several ways
+ * to "track" a monster, including targetting it, attacking it, and
+ * affecting it (and nobody else) with a ranged attack.
+ *
+ * Display the monster health bar (affectionately known as the
+ * "health-o-meter"). Clear health bar if nothing is being tracked.
+ * Auto-track current target monster when bored. Note that the
+ * health-bar stops tracking any monster that "disappears".
+ */
+static void health_redraw(void)
+{
+
+#ifdef DRS_SHOW_HEALTH_BAR
+
+ /* Not tracking */
+ if (!health_who)
+ {
+ /* Erase the health bar */
+ Term_erase(COL_INFO, ROW_INFO, 12);
+ }
+
+ /* Tracking an unseen monster */
+ else if (!m_list[health_who].ml)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a hallucinatory monster */
+ else if (p_ptr->image)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a dead monster (???) */
+ else if (!m_list[health_who].hp < 0)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a visible monster */
+ else
+ {
+ int pct, len;
+
+ monster_type *m_ptr = &m_list[health_who];
+
+ /* Default to almost dead */
+ byte attr = TERM_RED;
+
+ /* Extract the "percent" of health */
+ pct = 100L * m_ptr->hp / m_ptr->maxhp;
+
+ /* Badly wounded */
+ if (pct >= 10) attr = TERM_L_RED;
+
+ /* Wounded */
+ if (pct >= 25) attr = TERM_ORANGE;
+
+ /* Somewhat Wounded */
+ if (pct >= 60) attr = TERM_YELLOW;
+
+ /* Healthy */
+ if (pct >= 100) attr = TERM_L_GREEN;
+
+ /* Afraid */
+ if (m_ptr->monfear) attr = TERM_VIOLET;
+
+ /* Asleep */
+ if (m_ptr->csleep) attr = TERM_BLUE;
+
+ /* Poisoned */
+ if (m_ptr->poisoned) attr = TERM_GREEN;
+
+ /* Bleeding */
+ if (m_ptr->bleeding) attr = TERM_RED;
+
+ /* Convert percent into "health" */
+ len = (pct < 10) ? 1 : (pct < 90) ? (pct / 10 + 1) : 10;
+
+ /* Default to "unknown" */
+ Term_putstr(COL_INFO, ROW_INFO, 12, TERM_WHITE, "[----------]");
+
+ /* Dump the current "health" (use '*' symbols) */
+ Term_putstr(COL_INFO + 1, ROW_INFO, len, attr, "**********");
+ }
+
+#endif
+
+}
+
+
+
+/*
+ * Display basic info (mostly left of map)
+ */
+static void prt_frame_basic(void)
+{
+ int i;
+
+ /* Race and Class */
+ prt_field(rp_ptr->title + rp_name, ROW_RACE, COL_RACE);
+ prt_field(spp_ptr->title + c_name, ROW_CLASS, COL_CLASS);
+
+ /* Title */
+ prt_title();
+
+ /* Level/Experience */
+ prt_level();
+ prt_exp();
+
+ /* All Stats */
+ for (i = 0; i < 6; i++) prt_stat(i);
+
+ /* Armor */
+ prt_ac();
+
+ /* Hitpoints */
+ prt_hp();
+
+ /* Current sanity */
+ prt_sane();
+
+ /* Spellpoints */
+ prt_sp();
+
+ /* Piety */
+ prt_piety();
+
+ /* Monster hitpoints */
+ prt_mh();
+
+ /* Gold */
+ prt_gold();
+
+ /* Current depth */
+ prt_depth();
+
+ /* Special */
+ health_redraw();
+}
+
+
+/*
+ * Display extra info (mostly below map)
+ */
+static void prt_frame_extra(void)
+{
+ /* Cut/Stun */
+ prt_cut();
+ prt_stun();
+
+ /* Food */
+ prt_hunger();
+
+ /* Various */
+ prt_blind();
+ prt_confused();
+ prt_afraid();
+ prt_poisoned();
+ prt_dtrap();
+
+ /* State */
+ prt_state();
+
+ /* Speed */
+ prt_speed();
+
+ /* Study spells */
+ prt_study();
+}
+
+
+/*
+ * Hack -- display inventory in sub-windows
+ */
+static void fix_inven(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_INVEN))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display inventory */
+ display_inven();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+
+/*
+ * Hack -- display equipment in sub-windows
+ */
+static void fix_equip(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_EQUIP))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display equipment */
+ display_equip();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+/*
+ * Hack -- display character in sub-windows
+ */
+static void fix_player(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_PLAYER))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display player */
+ display_player(0);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+
+/*
+ * Hack -- display recent messages in sub-windows
+ *
+ * XXX XXX XXX Adjust for width and split messages
+ */
+void fix_message(void)
+{
+ int j, i;
+ int w, h;
+ int x, y;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_MESSAGE))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Get size */
+ Term_get_size(&w, &h);
+
+ /* Dump messages */
+ for (i = 0; i < h; i++)
+ {
+ /* Dump the message on the appropriate line */
+ display_message(0, (h - 1) - i, strlen(message_str((s16b)i)), message_color((s16b)i), message_str((s16b)i));
+
+ /* Cursor */
+ Term_locate(&x, &y);
+
+ /* Clear to end of line */
+ Term_erase(x, y, 255);
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display recent IRC messages in sub-windows
+ *
+ * XXX XXX XXX Adjust for width and split messages
+ */
+void fix_irc_message(void)
+{
+ int j, i, k;
+ int w, h;
+ int x, y;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_IRC))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Get size */
+ Term_get_size(&w, &h);
+
+ Term_clear();
+
+ /* Dump messages */
+ k = 0;
+ for (i = 0; ; i++)
+ {
+ byte type = message_type((s16b)i);
+
+ if (k >= h) break;
+ if (MESSAGE_NONE == type) break;
+ if (MESSAGE_IRC != type) continue;
+
+ /* Dump the message on the appropriate line */
+ display_message(0, (h - 1) - k, strlen(message_str((s16b)i)), message_color((s16b)i), message_str((s16b)i));
+
+ /* Cursor */
+ Term_locate(&x, &y);
+
+ /* Clear to end of line */
+ Term_erase(x, y, 255);
+
+ k++;
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display overhead view in sub-windows
+ *
+ * Note that the "player" symbol does NOT appear on the map.
+ */
+static void fix_overhead(void)
+{
+ int j;
+
+ int cy, cx;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_OVERHEAD))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Redraw map */
+ display_map(&cy, &cx);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display monster recall in sub-windows
+ */
+static void fix_monster(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_MONSTER))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display monster race info */
+ if (monster_race_idx) display_roff(monster_race_idx, monster_ego_idx);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display object recall in sub-windows
+ */
+static void fix_object(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_OBJECT))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Clear */
+ Term_clear();
+
+ /* Display object info */
+ if (tracked_object)
+ if (!object_out_desc(tracked_object, NULL, FALSE, FALSE)) text_out("You see nothing special.");
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+/* Show the monster list in a window */
+
+static void fix_m_list(void)
+{
+ int i, j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ int c = 0;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_M_LIST))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Clear */
+ Term_clear();
+
+ /* Hallucination */
+ if (p_ptr->image)
+ {
+ c_prt(TERM_WHITE, "You can not see clearly", 0, 0);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+
+ return;
+ }
+
+ /* reset visible count */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ r_ptr->total_visible = 0;
+ }
+
+ /* Count up the number visible in each race */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Skip dead monsters */
+ if (m_ptr->hp < 0) continue;
+
+ /* Skip unseen monsters */
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ /* Memorized objects */
+ if (!o_ptr->marked) continue;
+ }
+ else
+ {
+ if (!m_ptr->ml) continue;
+ }
+
+ /* Increase for this race */
+ r_ptr->total_visible++;
+
+ /* Increase total Count */
+ c++;
+ }
+
+ /* Are monsters visible? */
+ if (c)
+ {
+ int w, h, num = 0;
+
+ (void)Term_get_size(&w, &h);
+
+ c_prt(TERM_WHITE, format("You can see %d monster%s", c, (c > 1 ? "s:" : ":")), 0, 0);
+
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Default Colour */
+ byte attr = TERM_SLATE;
+
+ /* Only visible monsters */
+ if (!r_ptr->total_visible) continue;
+
+ /* Uniques */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ attr = TERM_L_BLUE;
+ }
+
+ /* Have we ever killed one? */
+ if (r_ptr->r_tkills)
+ {
+ if (r_ptr->level > dun_level)
+ {
+ attr = TERM_VIOLET;
+
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ attr = TERM_RED;
+ }
+ }
+ }
+ else
+ {
+ if (!(r_ptr->flags1 & RF1_UNIQUE)) attr = TERM_GREEN;
+ }
+
+
+ /* Dump the monster name */
+ if (r_ptr->total_visible == 1)
+ {
+ c_prt(attr, (r_name + r_ptr->name), (num % (h - 1)) + 1, (num / (h - 1) * 26));
+ }
+ else
+ {
+ c_prt(attr, format("%s (x%d)", r_name + r_ptr->name, r_ptr->total_visible), (num % (h - 1)) + 1, (num / (h - 1)) * 26);
+ }
+
+ num++;
+
+ }
+
+ }
+ else
+ {
+ c_prt(TERM_WHITE, "You see no monsters.", 0, 0);
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Calculate number of spells player should have, and forget,
+ * or remember, spells until that number is properly reflected.
+ *
+ * Note that this function induces various "status" messages,
+ * which must be bypasses until the character is created.
+ */
+static void calc_spells(void)
+{
+ p_ptr->new_spells = 0;
+}
+
+/* Ugly hack */
+bool calc_powers_silent = FALSE;
+
+/* Calc the player powers */
+static void calc_powers(void)
+{
+ int i, p = 0;
+ bool *old_powers;
+
+ /* Hack -- wait for creation */
+ if (!character_generated) return;
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ C_MAKE(old_powers, power_max, bool);
+
+ /* Save old powers */
+ for (i = 0; i < power_max; i++) old_powers[i] = p_ptr->powers[i];
+
+ /* Get intrinsincs */
+ for (i = 0; i < POWER_MAX_INIT; i++) p_ptr->powers[i] = p_ptr->powers_mod[i];
+ for (; i < power_max; i++) p_ptr->powers[i] = 0;
+
+ /* Hooked powers */
+ process_hooks(HOOK_CALC_POWERS, "()");
+
+ /* Add objects powers */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ p = object_power(o_ptr);
+ if (p != -1) p_ptr->powers[p] = TRUE;
+ }
+
+ if ((!p_ptr->tim_mimic) && (!p_ptr->body_monster))
+ {
+ /* Add in racial and subracial powers */
+ for (i = 0; i < 4; i++)
+ {
+ p = rp_ptr->powers[i];
+ if (p != -1) p_ptr->powers[p] = TRUE;
+
+ p = rmp_ptr->powers[i];
+ if (p != -1) p_ptr->powers[p] = TRUE;
+ }
+ }
+ else if (p_ptr->mimic_form)
+ call_lua("calc_mimic_power", "(d)", "", p_ptr->mimic_form);
+
+ /* Add in class powers */
+ for (i = 0; i < 4; i++)
+ {
+ p = cp_ptr->powers[i];
+ if (p != -1) p_ptr->powers[p] = TRUE;
+ }
+
+ if (p_ptr->disembodied)
+ {
+ p = PWR_INCARNATE;
+ p_ptr->powers[p] = TRUE;
+ }
+
+ /* Now lets warn the player */
+ for (i = 0; i < power_max; i++)
+ {
+ s32b old = old_powers[i];
+ s32b new = p_ptr->powers[i];
+
+ if (new > old)
+ {
+ if (!calc_powers_silent) cmsg_print(TERM_GREEN, powers_type[i].gain_text);
+ }
+ else if (new < old)
+ {
+ if (!calc_powers_silent) cmsg_print(TERM_RED, powers_type[i].lose_text);
+ }
+ }
+
+ calc_powers_silent = FALSE;
+ C_FREE(old_powers, power_max, bool);
+}
+
+
+/*
+ * Calculate the player's sanity
+ */
+
+void calc_sanity(void)
+{
+ int bonus, msane;
+
+ /* Hack -- use the con/hp table for sanity/wis */
+ bonus = ((int)(adj_con_mhp[p_ptr->stat_ind[A_WIS]]) - 128);
+
+ /* Hack -- assume 5 sanity points per level. */
+ msane = 5 * (p_ptr->lev + 1) + (bonus * p_ptr->lev / 2);
+
+ if (msane < p_ptr->lev + 1) msane = p_ptr->lev + 1;
+
+ if (p_ptr->msane != msane)
+ {
+ /* Sanity carries over between levels. */
+ p_ptr->csane += (msane - p_ptr->msane);
+
+ p_ptr->msane = msane;
+
+ if (p_ptr->csane >= msane)
+ {
+ p_ptr->csane = msane;
+ p_ptr->csane_frac = 0;
+ }
+
+ p_ptr->redraw |= (PR_SANITY);
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * Calculate maximum mana. You do not need to know any spells.
+ * Note that mana is lowered by heavy (or inappropriate) armor.
+ *
+ * This function induces status messages.
+ */
+static void calc_mana(void)
+{
+ int msp, levels, cur_wgt, max_wgt;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_type *o_ptr;
+
+ levels = p_ptr->lev;
+
+ /* Hack -- no negative mana */
+ if (levels < 0) levels = 0;
+
+ /* Extract total mana */
+ msp = get_skill_scale(SKILL_MAGIC, 200) +
+ (adj_mag_mana[
+ (p_ptr->stat_ind[A_INT] > p_ptr->stat_ind[A_WIS]) ?
+ p_ptr->stat_ind[A_INT] : p_ptr->stat_ind[A_WIS]
+ ] * levels / 4);
+
+ /* Hack -- usually add one mana */
+ if (msp) msp++;
+
+ /* Possessors mana is different */
+ if (p_ptr->body_monster && (!p_ptr->disembodied))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ int f = 100 / (r_ptr->freq_spell ? r_ptr->freq_spell : 1);
+
+ msp = 21 - f;
+
+ if (msp < 1) msp = 1;
+ }
+
+ /* Apply race mod mana */
+ msp = msp * rmp_ptr->mana / 100;
+
+ /* Apply class mana */
+ msp += msp * cp_ptr->mana / 100;
+
+ /* Apply Eru mana */
+ GOD(GOD_ERU)
+ {
+ s32b tmp = p_ptr->grace;
+
+ if (tmp >= 35000) tmp = 35000;
+ tmp /= 100;
+ msp += msp * tmp / 1000;
+ }
+
+ /* Only mages are affected */
+ if (forbid_gloves())
+ {
+ /* Assume player is not encumbered by gloves */
+ p_ptr->cumber_glove = FALSE;
+
+ /* Get the gloves */
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+
+ /* Examine the gloves */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Normal gloves hurt mage-type spells */
+ if (o_ptr->k_idx &&
+ !(f2 & (TR2_FREE_ACT)) &&
+ !((f1 & (TR1_DEX)) && (o_ptr->pval > 0)) &&
+ !(f5 & TR5_SPELL_CONTAIN))
+ {
+ /* Encumbered */
+ p_ptr->cumber_glove = TRUE;
+
+ /* Reduce mana */
+ msp = (3 * msp) / 4;
+ }
+ }
+
+ /* Augment mana */
+ if (munchkin_multipliers)
+ {
+ if (p_ptr->to_m) msp += msp * p_ptr->to_m / 5;
+ }
+ else
+ {
+ if (p_ptr->to_m) msp += msp * p_ptr->to_m / 10;
+ }
+
+ /* Assume player not encumbered by armor */
+ p_ptr->cumber_armor = FALSE;
+
+ /* Weigh the armor */
+ cur_wgt = 0;
+ cur_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ cur_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ cur_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ cur_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ /* Determine the weight allowance */
+ max_wgt = 200 + get_skill_scale(SKILL_COMBAT, 500);
+
+ /* Heavy armor penalizes mana */
+ if (((cur_wgt - max_wgt) / 10) > 0)
+ {
+ /* Encumbered */
+ p_ptr->cumber_armor = TRUE;
+
+ /* Reduce mana */
+ msp -= ((cur_wgt - max_wgt) / 10);
+ }
+
+ /* When meditating your mana is increased ! */
+ if (p_ptr->meditation)
+ {
+ msp += 50;
+ p_ptr->csp += 50;
+ }
+
+ /* Sp mods? */
+ if (process_hooks_ret(HOOK_CALC_MANA, "d", "(d)", msp))
+ {
+ msp = process_hooks_return[0].num;
+ }
+
+ /* Mana can never be negative */
+ if (msp < 0) msp = 0;
+
+
+ /* Maximum mana has changed */
+ if (p_ptr->msp != msp)
+ {
+ /* Save new limit */
+ p_ptr->msp = msp;
+
+ /* Enforce new limit */
+ if (p_ptr->csp >= msp)
+ {
+ p_ptr->csp = msp;
+ p_ptr->csp_frac = 0;
+ }
+
+ /* Display mana later */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ /* Take note when "glove state" changes */
+ if (p_ptr->old_cumber_glove != p_ptr->cumber_glove)
+ {
+ /* Message */
+ if (p_ptr->cumber_glove)
+ {
+ msg_print("Your covered hands feel unsuitable for spellcasting.");
+ }
+ else
+ {
+ msg_print("Your hands feel more suitable for spellcasting.");
+ }
+
+ /* Save it */
+ p_ptr->old_cumber_glove = p_ptr->cumber_glove;
+ }
+
+
+ /* Take note when "armor state" changes */
+ if (p_ptr->old_cumber_armor != p_ptr->cumber_armor)
+ {
+ /* Message */
+ if (p_ptr->cumber_armor)
+ {
+ msg_print("The weight of your armor encumbers your movement.");
+ }
+ else
+ {
+ msg_print("You feel able to move more freely.");
+ }
+
+ /* Save it */
+ p_ptr->old_cumber_armor = p_ptr->cumber_armor;
+ }
+}
+
+
+
+/*
+ * Calculate the players (maximal) hit points
+ * Adjust current hitpoints if necessary
+ */
+void calc_hitpoints(void)
+{
+ int bonus, mhp;
+
+ /* Un-inflate "half-hitpoint bonus per level" value */
+ bonus = ((int)(adj_con_mhp[p_ptr->stat_ind[A_CON]]) - 128);
+
+ /* Calculate hitpoints */
+ mhp = player_hp[p_ptr->lev - 1] + (bonus * p_ptr->lev / 2);
+
+ /* Always have at least one hitpoint per level */
+ if (mhp < p_ptr->lev + 1) mhp = p_ptr->lev + 1;
+
+ /* Factor in the pernament hp modifications */
+ mhp += p_ptr->hp_mod;
+ if (mhp < 1) mhp = 1;
+
+ /* Hack: Sorcery impose a hp penality */
+ if (mhp && (get_skill(SKILL_SORCERY)))
+ {
+ mhp -= mhp * get_skill_scale(SKILL_SORCERY, 50) / 100;
+ if (mhp < 1) mhp = 1;
+ }
+
+ /* Factor in the melkor hp modifications */
+ GOD(GOD_MELKOR)
+ {
+ mhp -= (p_ptr->melkor_sacrifice * 10);
+ if (mhp < 1) mhp = 1;
+ }
+
+ /* Factor in the hero / superhero settings */
+ if (p_ptr->hero) mhp += 10;
+ if (p_ptr->shero) mhp += 30;
+
+ /* Augment Hitpoint */
+ if (munchkin_multipliers)
+ {
+ mhp += mhp * p_ptr->to_l / 5;
+ }
+ else
+ {
+ mhp += mhp * p_ptr->to_l / 10;
+ }
+ if (mhp < 1) mhp = 1;
+
+ if (p_ptr->body_monster)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ u32b rhp = maxroll(r_ptr->hdice, r_ptr->hside);
+
+ /* Adjust the hp with the possession skill */
+ rhp = (rhp * (20 + get_skill_scale(SKILL_POSSESSION, 80))) / 100;
+
+ mhp = (rhp + sroot(rhp) + mhp) / 3;
+ }
+ if (p_ptr->disembodied) mhp = 1;
+
+ /* HACK - being undead means less DP */
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ int divisor = p_ptr->lev / 4;
+
+ /* Beware of the horrible division by zero ! :) */
+ if (divisor == 0) divisor = 1;
+
+ /* Actually decrease the max hp */
+ mhp /= divisor;
+
+ /* Never less than 1 */
+ if (mhp < 1) mhp = 1;
+ }
+
+ /* Hp mods? */
+ if (process_hooks_ret(HOOK_CALC_HP, "d", "(d)", mhp))
+ {
+ mhp = process_hooks_return[0].num;
+ }
+
+ /* Never less than 1 */
+ if (mhp < 1) mhp = 1;
+
+ /* New maximum hitpoints */
+ if (p_ptr->mhp != mhp)
+ {
+ /* XXX XXX XXX New hitpoint maintenance */
+
+ /* Enforce maximum */
+ if (p_ptr->chp >= mhp)
+ {
+ p_ptr->chp = mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Save the new max-hitpoints */
+ p_ptr->mhp = mhp;
+
+ /* Display hitpoints (later) */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+
+/*
+ * Extract and set the current "lite radius"
+ *
+ * SWD: Experimental modification: multiple light sources have additive effect.
+ *
+ */
+static void calc_torch(void)
+{
+ int i;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Assume no light */
+ p_ptr->cur_lite = 0;
+
+ /* Loop through all wielded items */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* does this item glow? */
+ if (((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0)) || (!(f4 & TR4_FUEL_LITE)))
+ {
+ if (f3 & TR3_LITE1) p_ptr->cur_lite++;
+ if (f4 & TR4_LITE2) p_ptr->cur_lite += 2;
+ if (f4 & TR4_LITE3) p_ptr->cur_lite += 3;
+ }
+
+ }
+
+ if (p_ptr->tim_lite) p_ptr->cur_lite += 2;
+
+ if (p_ptr->holy) p_ptr->cur_lite += 1;
+
+ /* max radius is 5 without rewriting other code -- */
+ /* see cave.c:update_lite() and defines.h:LITE_MAX */
+ if (p_ptr->cur_lite > 5) p_ptr->cur_lite = 5;
+
+ /* check if the player doesn't have a lite source, */
+ /* but does glow as an intrinsic. */
+ if (p_ptr->cur_lite == 0 && p_ptr->lite) p_ptr->cur_lite = 1;
+
+ /* Hooked powers */
+ process_hooks(HOOK_CALC_LITE, "()");
+
+ /* end experimental mods */
+
+ /* Reduce lite in the small-scale wilderness map */
+ if (p_ptr->wild_mode)
+ {
+ /* Reduce the lite radius if needed */
+ if (p_ptr->cur_lite > WILDERNESS_SEE_RADIUS)
+ {
+ p_ptr->cur_lite = WILDERNESS_SEE_RADIUS;
+ }
+ }
+
+
+ /* Reduce lite when running if requested */
+ if (running && view_reduce_lite)
+ {
+ /* Reduce the lite radius if needed */
+ if (p_ptr->cur_lite > 1) p_ptr->cur_lite = 1;
+ }
+
+ /* Notice changes in the "lite radius" */
+ if (p_ptr->old_lite != p_ptr->cur_lite)
+ {
+ /* Update the view */
+ p_ptr->update |= (PU_VIEW);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Remember the old lite */
+ p_ptr->old_lite = p_ptr->cur_lite;
+ }
+}
+
+
+
+/*
+ * Computes current weight limit.
+ */
+int weight_limit(void)
+{
+ int i;
+
+ /* Weight limit based only on strength */
+ i = adj_str_wgt[p_ptr->stat_ind[A_STR]] * 100;
+
+ if (process_hooks_ret(HOOK_CALC_WEIGHT, "d", "(d)", i))
+ i = process_hooks_return[0].num;
+
+ /* Return the result */
+ return (i);
+}
+
+void calc_wield_monster()
+{
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ r_ptr = &r_info[o_ptr->pval];
+
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ p_ptr->invis += 20;
+ if (r_ptr->flags2 & RF2_REFLECTING)
+ p_ptr->reflect = TRUE;
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ p_ptr->ffall = TRUE;
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ p_ptr->water_breath = TRUE;
+ }
+}
+
+/*
+ * Calc which body parts the player have, based on the
+ * monster he incarnate, note that that's bnot a hack
+ * since body parts of the player when in it's own body
+ * are also defined in r_info(monster 0)
+ */
+void calc_body()
+{
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ int i, b_weapon, b_legs, b_arms;
+ byte *body_parts, bp[BODY_MAX];
+
+ if (!p_ptr->body_monster)
+ {
+ body_parts = bp;
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = rp_ptr->body_parts[i] + rmp_ptr->body_parts[i];
+ if (b < 0) b = 0;
+
+ if (p_ptr->mimic_form == resolve_mimic_name("Bear"))
+ {
+ if (i == BODY_ARMS) b = 0;
+ else if (i == BODY_LEGS) b = 0;
+ }
+
+ bp[i] = b;
+ }
+ }
+ else
+ {
+ body_parts = bp;
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = r_ptr->body_parts[i];
+ if (b < 0) b = 0;
+
+ bp[i] = b;
+ }
+ }
+
+ /* Do we need more parts ? ;) */
+ for (i = 0; i < BODY_MAX; i++)
+ p_ptr->extra_body_parts[i] = 0;
+ process_hooks(HOOK_BODY_PARTS, "()");
+
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = bp[i] + cp_ptr->body_parts[i] + p_ptr->extra_body_parts[i];
+ if (b < 0) b = 0;
+ if (b > max_body_part[i]) b = max_body_part[i];
+
+ bp[i] = b;
+ }
+
+ b_weapon = body_parts[BODY_WEAPON];
+ b_arms = body_parts[BODY_ARMS];
+ b_legs = body_parts[BODY_LEGS];
+
+ if (p_ptr->mimic_extra & CLASS_ARMS)
+ {
+ b_weapon++;
+ b_arms++;
+
+ if (b_weapon > 3) b_weapon = 3;
+ if (b_arms > 3) b_arms = 3;
+ }
+
+ if (p_ptr->mimic_extra & CLASS_LEGS)
+ {
+ b_legs++;
+
+ if (b_legs > 2) b_legs = 2;
+ }
+
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ p_ptr->body_parts[i] = 0;
+
+ for (i = 0; i < b_weapon; i++)
+ p_ptr->body_parts[INVEN_WIELD - INVEN_WIELD + i] = INVEN_WIELD;
+ if (body_parts[BODY_WEAPON])
+ p_ptr->body_parts[INVEN_BOW - INVEN_WIELD] = INVEN_BOW;
+
+ for (i = 0; i < body_parts[BODY_TORSO]; i++)
+ {
+ p_ptr->body_parts[INVEN_BODY - INVEN_WIELD + i] = INVEN_BODY;
+ p_ptr->body_parts[INVEN_OUTER - INVEN_WIELD + i] = INVEN_OUTER;
+ p_ptr->body_parts[INVEN_LITE - INVEN_WIELD + i] = INVEN_LITE;
+ p_ptr->body_parts[INVEN_AMMO - INVEN_WIELD + i] = INVEN_AMMO;
+ p_ptr->body_parts[INVEN_CARRY - INVEN_WIELD + i] = INVEN_CARRY;
+ }
+
+ for (i = 0; i < body_parts[BODY_FINGER]; i++)
+ p_ptr->body_parts[INVEN_RING - INVEN_WIELD + i] = INVEN_RING;
+
+ for (i = 0; i < body_parts[BODY_HEAD]; i++)
+ {
+ p_ptr->body_parts[INVEN_HEAD - INVEN_WIELD + i] = INVEN_HEAD;
+ p_ptr->body_parts[INVEN_NECK - INVEN_WIELD + i] = INVEN_NECK;
+ }
+
+ for (i = 0; i < b_arms; i++)
+ {
+ p_ptr->body_parts[INVEN_ARM - INVEN_WIELD + i] = INVEN_ARM;
+ p_ptr->body_parts[INVEN_HANDS - INVEN_WIELD + i] = INVEN_HANDS;
+ }
+ if (body_parts[BODY_ARMS])
+ p_ptr->body_parts[INVEN_TOOL - INVEN_WIELD] = INVEN_TOOL;
+
+ for (i = 0; i < b_legs; i++)
+ p_ptr->body_parts[INVEN_FEET - INVEN_WIELD + i] = INVEN_FEET;
+
+ /* Ok now if the player lost a body part, he must drop the object he had on it */
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ {
+ if ((!p_ptr->body_parts[i]) && (p_ptr->inventory[i + INVEN_WIELD].k_idx))
+ {
+ /* Drop it NOW ! */
+ inven_takeoff(i + INVEN_WIELD, 255, TRUE);
+ }
+ }
+}
+
+/* Should be called by every calc_bonus call */
+void calc_body_bonus()
+{
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* If in the player body nothing have to be done */
+ if (!p_ptr->body_monster) return;
+
+ if (p_ptr->disembodied)
+ {
+ p_ptr->wraith_form = TRUE;
+ return;
+ }
+
+ p_ptr->ac += r_ptr->ac;
+ p_ptr->pspeed = r_ptr->speed;
+
+ if (r_ptr->flags1 & RF1_NEVER_MOVE) p_ptr->immovable = TRUE;
+ if (r_ptr->flags2 & RF2_STUPID) p_ptr->stat_add[A_INT] -= 1;
+ if (r_ptr->flags2 & RF2_SMART) p_ptr->stat_add[A_INT] += 1;
+ if (r_ptr->flags2 & RF2_REFLECTING) p_ptr->reflect = TRUE;
+ if (r_ptr->flags2 & RF2_INVISIBLE) p_ptr->invis += 20;
+ if (r_ptr->flags2 & RF2_REGENERATE) p_ptr->regenerate = TRUE;
+ if (r_ptr->flags2 & RF2_AURA_FIRE) p_ptr->sh_fire = TRUE;
+ if (r_ptr->flags2 & RF2_AURA_ELEC) p_ptr->sh_elec = TRUE;
+ if (r_ptr->flags2 & RF2_PASS_WALL) p_ptr->wraith_form = TRUE;
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE) p_ptr->sensible_fire = TRUE;
+ if (r_ptr->flags3 & RF3_IM_ACID) p_ptr->resist_acid = TRUE;
+ if (r_ptr->flags3 & RF3_IM_ELEC) p_ptr->resist_elec = TRUE;
+ if (r_ptr->flags3 & RF3_IM_FIRE) p_ptr->resist_fire = TRUE;
+ if (r_ptr->flags3 & RF3_IM_POIS) p_ptr->resist_pois = TRUE;
+ if (r_ptr->flags3 & RF3_IM_COLD) p_ptr->resist_cold = TRUE;
+ if (r_ptr->flags3 & RF3_RES_NETH) p_ptr->resist_neth = TRUE;
+ if (r_ptr->flags3 & RF3_RES_NEXU) p_ptr->resist_nexus = TRUE;
+ if (r_ptr->flags3 & RF3_RES_DISE) p_ptr->resist_disen = TRUE;
+ if (r_ptr->flags3 & RF3_NO_FEAR) p_ptr->resist_fear = TRUE;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) p_ptr->free_act = TRUE;
+ if (r_ptr->flags3 & RF3_NO_CONF) p_ptr->resist_conf = TRUE;
+ if (r_ptr->flags7 & RF7_CAN_FLY) p_ptr->ffall = TRUE;
+ if (r_ptr->flags7 & RF7_AQUATIC) p_ptr->water_breath = TRUE;
+}
+
+
+byte calc_mimic()
+{
+ s32b blow = 0;
+
+ call_lua("calc_mimic", "(d)", "d", p_ptr->mimic_form, &blow);
+ return blow;
+}
+
+/* Returns the blow information based on class */
+void analyze_blow(int *num, int *wgt, int *mul)
+{
+ *num = cp_ptr->blow_num;
+ *wgt = cp_ptr->blow_wgt;
+ *mul = cp_ptr->blow_mul;
+
+ /* Count bonus abilities */
+ if (has_ability(AB_MAX_BLOW1)) (*num)++;
+ if (has_ability(AB_MAX_BLOW2)) (*num)++;
+}
+
+/* Are all the weapons wielded of the right type ? */
+int get_weaponmastery_skill()
+{
+ int i, skill = 0;
+ object_type *o_ptr;
+
+ i = 0;
+ /* All weapons must be of the same type */
+ while ((p_ptr->body_parts[i] == INVEN_WIELD) && (i < INVEN_TOTAL))
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ if (!o_ptr->k_idx)
+ {
+ i++;
+ continue;
+ }
+ switch (o_ptr->tval)
+ {
+ case TV_DAEMON_BOOK:
+ case TV_SWORD:
+ if ((!skill) || (skill == SKILL_SWORD)) skill = SKILL_SWORD;
+ else skill = -1;
+ break;
+ case TV_AXE:
+ if ((!skill) || (skill == SKILL_AXE)) skill = SKILL_AXE;
+ else skill = -1;
+ break;
+ case TV_HAFTED:
+ if ((!skill) || (skill == SKILL_HAFTED)) skill = SKILL_HAFTED;
+ else skill = -1;
+ break;
+ case TV_POLEARM:
+ if ((!skill) || (skill == SKILL_POLEARM)) skill = SKILL_POLEARM;
+ else skill = -1;
+ break;
+ }
+ i++;
+ }
+
+ /* Everything is ok */
+ return skill;
+}
+
+/* Are all the ranged weapons wielded of the right type ? */
+int get_archery_skill()
+{
+ int i, skill = 0;
+ object_type *o_ptr;
+
+ i = INVEN_BOW - INVEN_WIELD;
+ /* All weapons must be of the same type */
+ while (p_ptr->body_parts[i] == INVEN_BOW)
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ if (p_ptr->inventory[INVEN_WIELD + i].tval == TV_BOW)
+ {
+ switch (p_ptr->inventory[INVEN_WIELD + i].sval / 10)
+ {
+ case 0:
+ if ((!skill) || (skill == SKILL_SLING)) skill = SKILL_SLING;
+ else skill = -1;
+ break;
+ case 1:
+ if ((!skill) || (skill == SKILL_BOW)) skill = SKILL_BOW;
+ else skill = -1;
+ break;
+ case 2:
+ if ((!skill) || (skill == SKILL_XBOW)) skill = SKILL_XBOW;
+ else skill = -1;
+ break;
+ }
+ }
+ else
+ {
+ if ((!skill) || (skill == SKILL_BOOMERANG)) skill = SKILL_BOOMERANG;
+ else skill = -1;
+ }
+
+ i++;
+ }
+
+ /* Everything is ok */
+ return skill;
+}
+
+/* Apply gods */
+void calc_gods()
+{
+ /* Boost WIS if the player follows Eru */
+ GOD(GOD_ERU)
+ {
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_WIS] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_WIS] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_WIS] += 1;
+ }
+
+ /* Boost str, con, chr and reduce int, wis if the player follows Melkor */
+ GOD(GOD_MELKOR)
+ {
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_STR] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_CON] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_CHR] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_CHR] += 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_CHR] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_INT] -= 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_INT] -= 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_INT] -= 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_WIS] -= 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_WIS] -= 1;
+ if (p_ptr->grace > 30000) p_ptr->stat_add[A_WIS] -= 1;
+
+ PRAY_GOD(GOD_MELKOR)
+ {
+ if (p_ptr->grace > 5000) p_ptr->invis += 30;
+ if (p_ptr->grace > 15000) p_ptr->immune_fire = TRUE;
+ }
+ p_ptr->resist_fire = TRUE;
+ }
+
+ /* Gifts of Manwe if the player is praying to Manwe */
+ PRAY_GOD(GOD_MANWE)
+ {
+ s32b add = p_ptr->grace;
+
+ /* provides speed every 5000 grace */
+ if (add > 35000) add = 35000;
+ add /= 5000;
+ p_ptr->pspeed += add;
+
+ /* Provides fly & FA */
+ if (p_ptr->grace >= 7000) p_ptr->free_act = TRUE;
+ if (p_ptr->grace >= 15000) p_ptr->fly = TRUE;
+ }
+
+ /* Manwe bonus not requiring the praying status */
+ GOD(GOD_MANWE)
+ {
+ if (p_ptr->grace >= 2000) p_ptr->ffall = TRUE;
+ }
+
+ /* Boost Str and Con if the player is following Tulkas */
+ GOD(GOD_TULKAS)
+ {
+ if (p_ptr->grace > 5000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_CON] += 1;
+ if (p_ptr->grace > 15000) p_ptr->stat_add[A_CON] += 1;
+
+ if (p_ptr->grace > 10000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 15000) p_ptr->stat_add[A_STR] += 1;
+ if (p_ptr->grace > 20000) p_ptr->stat_add[A_STR] += 1;
+ }
+}
+
+/* Apply flags */
+static int extra_blows;
+static int extra_shots;
+void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval, s16b tval, s16b to_h, s16b to_d, s16b to_a)
+{
+ /* Affect stats */
+ if (f1 & (TR1_STR)) p_ptr->stat_add[A_STR] += pval;
+ if (f1 & (TR1_INT)) p_ptr->stat_add[A_INT] += pval;
+ if (f1 & (TR1_WIS)) p_ptr->stat_add[A_WIS] += pval;
+ if (f1 & (TR1_DEX)) p_ptr->stat_add[A_DEX] += pval;
+ if (f1 & (TR1_CON)) p_ptr->stat_add[A_CON] += pval;
+ if (f1 & (TR1_CHR)) p_ptr->stat_add[A_CHR] += pval;
+ if (f5 & (TR5_LUCK)) p_ptr->luck_cur += pval;
+
+ /* Affect spell power */
+ if (f1 & (TR1_SPELL)) p_ptr->to_s += pval;
+
+ /* Affect mana capacity */
+ if (f1 & (TR1_MANA)) p_ptr->to_m += pval;
+
+ /* Affect life capacity */
+ if (f2 & (TR2_LIFE)) p_ptr->to_l += pval;
+
+ /* Affect stealth */
+ if (f1 & (TR1_STEALTH)) p_ptr->skill_stl += pval;
+
+ /* Affect searching ability (factor of five) */
+ if (f1 & (TR1_SEARCH)) p_ptr->skill_srh += (pval * 5);
+
+ /* Affect searching frequency (factor of five) */
+ if (f1 & (TR1_SEARCH)) p_ptr->skill_fos += (pval * 5);
+
+ /* Affect infravision */
+ if (f1 & (TR1_INFRA)) p_ptr->see_infra += pval;
+
+ /* Affect digging (factor of 20) */
+ if (f1 & (TR1_TUNNEL)) p_ptr->skill_dig += (pval * 20);
+
+ /* Affect speed */
+ if (f1 & (TR1_SPEED)) p_ptr->pspeed += pval;
+
+ /* Affect blows */
+ if (f1 & (TR1_BLOWS)) extra_blows += pval;
+ if (f5 & (TR5_CRIT)) p_ptr->xtra_crit += pval;
+
+ /* Hack -- Sensible fire */
+ if (f2 & (TR2_SENS_FIRE)) p_ptr->sensible_fire = TRUE;
+
+ /* Hack -- cause earthquakes */
+ if (f1 & (TR1_IMPACT)) p_ptr->impact = TRUE;
+
+ /* Affect invisibility */
+ if (f2 & (TR2_INVIS)) p_ptr->invis += (pval * 10);
+
+ /* Boost shots */
+ if (f3 & (TR3_XTRA_SHOTS)) extra_shots++;
+
+ /* Various flags */
+ if (f3 & (TR3_AGGRAVATE)) p_ptr->aggravate = TRUE;
+ if (f3 & (TR3_TELEPORT)) p_ptr->teleport = TRUE;
+ if (f5 & (TR5_DRAIN_MANA)) p_ptr->drain_mana++;
+ if (f5 & (TR5_DRAIN_HP)) p_ptr->drain_life++;
+ if (f3 & (TR3_DRAIN_EXP)) p_ptr->exp_drain = TRUE;
+ if (f3 & (TR3_BLESSED)) p_ptr->bless_blade = TRUE;
+ if (f3 & (TR3_XTRA_MIGHT)) p_ptr->xtra_might += pval;
+ if (f3 & (TR3_SLOW_DIGEST)) p_ptr->slow_digest = TRUE;
+ if (f3 & (TR3_REGEN)) p_ptr->regenerate = TRUE;
+ if (esp) p_ptr->telepathy |= esp;
+ if ((tval != TV_LITE) && (f3 & (TR3_LITE1))) p_ptr->lite = TRUE;
+ if ((tval != TV_LITE) && (f4 & (TR4_LITE2))) p_ptr->lite = TRUE;
+ if ((tval != TV_LITE) && (f4 & (TR4_LITE3))) p_ptr->lite = TRUE;
+ if (f3 & (TR3_SEE_INVIS)) p_ptr->see_inv = TRUE;
+ if (f2 & (TR2_FREE_ACT)) p_ptr->free_act = TRUE;
+ if (f2 & (TR2_HOLD_LIFE)) p_ptr->hold_life = TRUE;
+ if (f3 & (TR3_WRAITH)) p_ptr->wraith_form = TRUE;
+ if (f3 & (TR3_FEATHER)) p_ptr->ffall = TRUE;
+ if (f4 & (TR4_FLY)) p_ptr->fly = TRUE;
+ if (f4 & (TR4_CLIMB)) p_ptr->climb = TRUE;
+
+ /* Immunity flags */
+ if (f2 & (TR2_IM_FIRE)) p_ptr->immune_fire = TRUE;
+ if (f2 & (TR2_IM_ACID)) p_ptr->immune_acid = TRUE;
+ if (f2 & (TR2_IM_COLD)) p_ptr->immune_cold = TRUE;
+ if (f2 & (TR2_IM_ELEC)) p_ptr->immune_elec = TRUE;
+
+ /* Resistance flags */
+ if (f2 & (TR2_RES_ACID)) p_ptr->resist_acid = TRUE;
+ if (f2 & (TR2_RES_ELEC)) p_ptr->resist_elec = TRUE;
+ if (f2 & (TR2_RES_FIRE)) p_ptr->resist_fire = TRUE;
+ if (f2 & (TR2_RES_COLD)) p_ptr->resist_cold = TRUE;
+ if (f2 & (TR2_RES_POIS)) p_ptr->resist_pois = TRUE;
+ if (f2 & (TR2_RES_FEAR)) p_ptr->resist_fear = TRUE;
+ if (f2 & (TR2_RES_CONF)) p_ptr->resist_conf = TRUE;
+ if (f2 & (TR2_RES_SOUND)) p_ptr->resist_sound = TRUE;
+ if (f2 & (TR2_RES_LITE)) p_ptr->resist_lite = TRUE;
+ if (f2 & (TR2_RES_DARK)) p_ptr->resist_dark = TRUE;
+ if (f2 & (TR2_RES_CHAOS)) p_ptr->resist_chaos = TRUE;
+ if (f2 & (TR2_RES_DISEN)) p_ptr->resist_disen = TRUE;
+ if (f2 & (TR2_RES_SHARDS)) p_ptr->resist_shard = TRUE;
+ if (f2 & (TR2_RES_NEXUS)) p_ptr->resist_nexus = TRUE;
+ if (f2 & (TR2_RES_BLIND)) p_ptr->resist_blind = TRUE;
+ if (f2 & (TR2_RES_NETHER)) p_ptr->resist_neth = TRUE;
+ if (f4 & (TR4_IM_NETHER)) p_ptr->immune_neth = TRUE;
+
+ if (f2 & (TR2_REFLECT)) p_ptr->reflect = TRUE;
+ if (f3 & (TR3_SH_FIRE)) p_ptr->sh_fire = TRUE;
+ if (f3 & (TR3_SH_ELEC)) p_ptr->sh_elec = TRUE;
+ if (f3 & (TR3_NO_MAGIC)) p_ptr->anti_magic = TRUE;
+ if (f3 & (TR3_NO_TELE)) p_ptr->anti_tele = TRUE;
+
+ /* Sustain flags */
+ if (f2 & (TR2_SUST_STR)) p_ptr->sustain_str = TRUE;
+ if (f2 & (TR2_SUST_INT)) p_ptr->sustain_int = TRUE;
+ if (f2 & (TR2_SUST_WIS)) p_ptr->sustain_wis = TRUE;
+ if (f2 & (TR2_SUST_DEX)) p_ptr->sustain_dex = TRUE;
+ if (f2 & (TR2_SUST_CON)) p_ptr->sustain_con = TRUE;
+ if (f2 & (TR2_SUST_CHR)) p_ptr->sustain_chr = TRUE;
+
+ if (f4 & (TR4_PRECOGNITION)) p_ptr->precognition = TRUE;
+
+ if (f4 & (TR4_ANTIMAGIC_50))
+ {
+ s32b tmp;
+
+ tmp = 10 + get_skill_scale(SKILL_ANTIMAGIC, 40)
+ - to_h - to_d - pval - to_a;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 4)
+ - (to_h + to_d + pval + to_a) / 15;
+ if (tmp > 0) p_ptr->antimagic_dis += tmp;
+ }
+
+ if (f4 & (TR4_ANTIMAGIC_30))
+ {
+ s32b tmp;
+
+ tmp = 7 + get_skill_scale(SKILL_ANTIMAGIC, 33)
+ - to_h - to_d - pval - to_a;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 2)
+ - (to_h + to_d + pval + to_a) / 15;
+ if (tmp > 0) p_ptr->antimagic_dis += tmp;
+ }
+
+ if (f4 & (TR4_ANTIMAGIC_20))
+ {
+ s32b tmp;
+
+ tmp = 5 + get_skill_scale(SKILL_ANTIMAGIC, 15)
+ - to_h - to_d - pval - to_a;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ p_ptr->antimagic_dis += 2;
+ }
+
+ if (f4 & (TR4_ANTIMAGIC_10))
+ {
+ s32b tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 9)
+ - to_h - to_d - pval - to_a;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ p_ptr->antimagic_dis += 1;
+ }
+
+ if (f4 & (TR4_AUTO_ID))
+ {
+ p_ptr->auto_id = TRUE;
+ }
+
+ /* The new code implementing Tolkien's concept of "Black Breath"
+ * takes advantage of the existing drain_exp character flag, renamed
+ * "black_breath". This flag can also be set by a unlucky blow from
+ * an undead. -LM-
+ */
+ if (f4 & (TR4_BLACK_BREATH)) p_ptr->black_breath = TRUE;
+
+ if (f5 & (TR5_IMMOVABLE)) p_ptr->immovable = TRUE;
+
+ /* Breaths */
+ if (f5 & (TR5_WATER_BREATH)) p_ptr->water_breath = TRUE;
+ if (f5 & (TR5_MAGIC_BREATH))
+ {
+ p_ptr->magical_breath = TRUE;
+ p_ptr->water_breath = TRUE;
+ }
+}
+
+/*
+ * Calculate the players current "state", taking into account
+ * not only race/class intrinsics, but also objects being worn
+ * and temporary spell effects.
+ *
+ * See also calc_mana() and calc_hitpoints().
+ *
+ * Take note of the new "speed code", in particular, a very strong
+ * player will start slowing down as soon as he reaches 150 pounds,
+ * but not until he reaches 450 pounds will he be half as fast as
+ * a normal kobold. This both hurts and helps the player, hurts
+ * because in the old days a player could just avoid 300 pounds,
+ * and helps because now carrying 300 pounds is not very painful.
+ *
+ * The "weapon" and "bow" do *not* add to the bonuses to hit or to
+ * damage, since that would affect non-combat things. These values
+ * are actually added in later, at the appropriate place.
+ *
+ * This function induces various "status" messages, unless silent is
+ * TRUE.
+ */
+void calc_bonuses(bool silent)
+{
+ int i, j, hold;
+ int old_invis;
+ int old_speed;
+ u32b old_telepathy;
+ int old_see_inv;
+ int old_dis_ac;
+ int old_dis_to_a;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Save the old speed */
+ old_speed = p_ptr->pspeed;
+
+ /* Save the old vision stuff */
+ old_telepathy = p_ptr->telepathy;
+ old_see_inv = p_ptr->see_inv;
+
+ /* Save the old armor class */
+ old_dis_ac = p_ptr->dis_ac;
+ old_dis_to_a = p_ptr->dis_to_a;
+
+ /* Save the old invisibility */
+ old_invis = p_ptr->invis;
+
+ /* Clear extra blows/shots */
+ extra_blows = extra_shots = 0;
+
+ /* Clear the stat modifiers */
+ for (i = 0; i < 6; i++) p_ptr->stat_add[i] = 0;
+
+ /* Mana multiplier */
+ p_ptr->to_m = 0;
+
+ /* Life multiplier */
+ p_ptr->to_l = 0;
+
+ /* Spell power */
+ p_ptr->to_s = 0;
+
+ /* Clear the Displayed/Real armor class */
+ p_ptr->dis_ac = p_ptr->ac = 0;
+
+ /* Clear the Displayed/Real Bonuses */
+ p_ptr->dis_to_h = p_ptr->to_h = p_ptr->to_h_melee = p_ptr->to_h_ranged = 0;
+ p_ptr->dis_to_d = p_ptr->to_d = p_ptr->to_d_melee = p_ptr->to_d_ranged = 0;
+ p_ptr->dis_to_a = p_ptr->to_a = 0;
+
+ /* Start with "normal" speed */
+ p_ptr->pspeed = 110;
+
+ /* Start with 0% additionnal crits */
+ p_ptr->xtra_crit = 0;
+
+ /* Start with a single blow per turn */
+ p_ptr->num_blow = 1;
+
+ /* Start with a single shot per turn */
+ p_ptr->num_fire = 1;
+
+ /* Starts with single throwing damage */
+ p_ptr->throw_mult = 1;
+
+ /* Reset the "xtra" tval */
+ p_ptr->tval_xtra = 0;
+
+ /* Reset the "ammo" tval */
+ p_ptr->tval_ammo = 0;
+
+ /* Clear all the flags */
+ p_ptr->invis = 0;
+ p_ptr->immovable = FALSE;
+ p_ptr->aggravate = FALSE;
+ p_ptr->teleport = FALSE;
+ p_ptr->exp_drain = FALSE;
+ p_ptr->drain_mana = 0;
+ p_ptr->drain_life = 0;
+ p_ptr->bless_blade = FALSE;
+ p_ptr->xtra_might = 0;
+ p_ptr->auto_id = FALSE;
+ p_ptr->impact = FALSE;
+ p_ptr->see_inv = FALSE;
+ p_ptr->free_act = FALSE;
+ p_ptr->slow_digest = FALSE;
+ p_ptr->regenerate = FALSE;
+ p_ptr->fly = FALSE;
+ p_ptr->climb = FALSE;
+ p_ptr->ffall = FALSE;
+ p_ptr->hold_life = FALSE;
+ p_ptr->telepathy = 0;
+ p_ptr->lite = FALSE;
+ p_ptr->sustain_str = FALSE;
+ p_ptr->sustain_int = FALSE;
+ p_ptr->sustain_wis = FALSE;
+ p_ptr->sustain_con = FALSE;
+ p_ptr->sustain_dex = FALSE;
+ p_ptr->sustain_chr = FALSE;
+ p_ptr->resist_acid = FALSE;
+ p_ptr->resist_elec = FALSE;
+ p_ptr->resist_fire = FALSE;
+ p_ptr->resist_cold = FALSE;
+ p_ptr->resist_pois = FALSE;
+ p_ptr->resist_conf = FALSE;
+ p_ptr->resist_sound = FALSE;
+ p_ptr->resist_lite = FALSE;
+ p_ptr->resist_dark = FALSE;
+ p_ptr->resist_chaos = FALSE;
+ p_ptr->resist_disen = FALSE;
+ p_ptr->resist_shard = FALSE;
+ p_ptr->resist_nexus = FALSE;
+ p_ptr->resist_blind = FALSE;
+ p_ptr->resist_neth = FALSE;
+ p_ptr->immune_neth = FALSE;
+ p_ptr->resist_fear = FALSE;
+ p_ptr->resist_continuum = FALSE;
+ p_ptr->reflect = FALSE;
+ p_ptr->sh_fire = FALSE;
+ p_ptr->sh_elec = FALSE;
+ p_ptr->anti_magic = FALSE;
+ p_ptr->anti_tele = FALSE;
+ p_ptr->water_breath = FALSE;
+ p_ptr->magical_breath = FALSE;
+
+ p_ptr->sensible_fire = FALSE;
+ p_ptr->sensible_lite = FALSE;
+
+ p_ptr->immune_acid = FALSE;
+ p_ptr->immune_elec = FALSE;
+ p_ptr->immune_fire = FALSE;
+ p_ptr->immune_cold = FALSE;
+
+ p_ptr->precognition = FALSE;
+
+ p_ptr->wraith_form = FALSE;
+
+ /* The anti magic field surrounding the player */
+ p_ptr->antimagic = 0;
+ p_ptr->antimagic_dis = 0;
+
+
+ /* Base infravision (purely racial) */
+ p_ptr->see_infra = rp_ptr->infra + rmp_ptr->infra;
+
+
+ /* Base skill -- disarming */
+ p_ptr->skill_dis = 0;
+
+ /* Base skill -- magic devices */
+ p_ptr->skill_dev = 0;
+
+ /* Base skill -- saving throw */
+ p_ptr->skill_sav = 0;
+
+ /* Base skill -- stealth */
+ p_ptr->skill_stl = 0;
+
+ /* Base skill -- searching ability */
+ p_ptr->skill_srh = 0;
+
+ /* Base skill -- searching frequency */
+ p_ptr->skill_fos = 0;
+
+ /* Base skill -- combat (normal) */
+ p_ptr->skill_thn = 0;
+
+ /* Base skill -- combat (shooting) */
+ p_ptr->skill_thb = 0;
+
+ /* Base skill -- combat (throwing) */
+ p_ptr->skill_tht = 0;
+
+
+ /* Base skill -- digging */
+ p_ptr->skill_dig = 0;
+
+ /* Xtra player flags */
+ p_ptr->xtra_f1 = 0;
+ p_ptr->xtra_f2 = 0;
+ p_ptr->xtra_f3 = 0;
+ p_ptr->xtra_f4 = 0;
+ p_ptr->xtra_f5 = 0;
+ p_ptr->xtra_esp = 0;
+
+ /* Hide the skills that should auto hide */
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if (s_info[i].flags1 & SKF1_AUTO_HIDE)
+ s_info[i].hidden = TRUE;
+ }
+
+ /* Base Luck */
+ p_ptr->luck_cur = p_ptr->luck_base;
+
+ /* Mimic override body's bonuses */
+ if (p_ptr->mimic_form)
+ {
+ extra_blows += calc_mimic();
+ }
+ else
+ {
+ calc_body_bonus();
+ }
+
+ /* Let the scripts do what they need */
+ process_hooks(HOOK_CALC_BONUS, "()");
+
+ /* The powers gived by the wielded monster */
+ calc_wield_monster();
+
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ apply_flags(cp_ptr->oflags1[i], cp_ptr->oflags2[i], cp_ptr->oflags3[i], cp_ptr->oflags4[i], cp_ptr->oflags5[i], cp_ptr->oesp[i], cp_ptr->opval[i], 0, 0, 0, 0);
+ }
+
+ if (p_ptr->melee_style == SKILL_HAND)
+ {
+ /* Unencumbered Monks become faster every 10 levels */
+ if (!(monk_heavy_armor()))
+ p_ptr->pspeed += get_skill_scale(SKILL_HAND, 5);
+
+ /* Free action if unencumbered at level 25 */
+ if ((get_skill(SKILL_HAND) > 24) && !(monk_heavy_armor()))
+ p_ptr->free_act = TRUE;
+ }
+
+ if (get_skill(SKILL_ANTIMAGIC))
+ {
+ p_ptr->antimagic += get_skill(SKILL_ANTIMAGIC);
+ p_ptr->antimagic_dis += get_skill_scale(SKILL_ANTIMAGIC, 10) + 1;
+
+ if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC)
+ {
+ p_ptr->anti_tele = TRUE;
+ p_ptr->resist_continuum = TRUE;
+ }
+ }
+
+ if (get_skill(SKILL_DAEMON) > 20) p_ptr->resist_conf = TRUE;
+ if (get_skill(SKILL_DAEMON) > 30) p_ptr->resist_fear = TRUE;
+
+ if ( get_skill(SKILL_MINDCRAFT) >= 40 ) p_ptr->telepathy = ESP_ALL;
+
+ if (p_ptr->astral)
+ {
+ p_ptr->wraith_form = TRUE;
+ }
+
+ /***** Races ****/
+ if ((!p_ptr->mimic_form) && (!p_ptr->body_monster))
+ {
+ int i;
+
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ apply_flags(rp_ptr->oflags1[i], rp_ptr->oflags2[i], rp_ptr->oflags3[i], rp_ptr->oflags4[i], rp_ptr->oflags5[i], rp_ptr->oesp[i], rp_ptr->opval[i], 0, 0, 0, 0);
+ apply_flags(rmp_ptr->oflags1[i], rmp_ptr->oflags2[i], rmp_ptr->oflags3[i], rmp_ptr->oflags4[i], rmp_ptr->oflags5[i], rmp_ptr->oesp[i], rmp_ptr->opval[i], 0, 0, 0, 0);
+ }
+
+ if (PRACE_FLAG(PR1_HURT_LITE))
+ p_ptr->sensible_lite = TRUE;
+ }
+
+ /* The extra flags */
+ apply_flags(p_ptr->xtra_f1, p_ptr->xtra_f2, p_ptr->xtra_f3, p_ptr->xtra_f4, p_ptr->xtra_f5, p_ptr->xtra_esp, 0, 0, 0, 0, 0);
+
+ /* Hack -- apply racial/class stat maxes */
+ if (p_ptr->maximize)
+ {
+ /* Apply the racial modifiers */
+ for (i = 0; i < 6; i++)
+ {
+ /* Modify the stats for "race" */
+ p_ptr->stat_add[i] += (rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]);
+ }
+ }
+
+
+ /* Scan the usable inventory */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the item flags */
+ object_flags_no_set = TRUE;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ object_flags_no_set = FALSE;
+
+ /* MEGA ugly hack -- set spacetime distortion resistance */
+ if (o_ptr->name1 == ART_ANCHOR)
+ {
+ p_ptr->resist_continuum = TRUE;
+ }
+
+ /* Hack - don't give the Black Breath when merely inspecting a weapon */
+ if (silent)
+ {
+ f4 &= ~TR4_BLACK_BREATH;
+ }
+
+ apply_flags(f1, f2, f3, f4, f5, esp, o_ptr->pval, o_ptr->tval, o_ptr->to_h, o_ptr->to_d, o_ptr->to_a);
+
+ if (o_ptr->name1)
+ {
+ apply_set(o_ptr->name1, a_info[o_ptr->name1].set);
+ }
+
+ /* Modify the base armor class */
+ p_ptr->ac += o_ptr->ac;
+
+ /* The base armor class is always known */
+ p_ptr->dis_ac += o_ptr->ac;
+
+ /* Apply the bonuses to armor class */
+ p_ptr->to_a += o_ptr->to_a;
+
+ /* Apply the mental bonuses to armor class, if known */
+ if (object_known_p(o_ptr)) p_ptr->dis_to_a += o_ptr->to_a;
+
+ /* Hack -- do not apply "weapon" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD) continue;
+
+ /* Hack -- do not apply "bow" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_BOW) continue;
+
+ /* Hack -- do not apply "ammo" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_AMMO) continue;
+
+ /* Hack -- do not apply "tool" bonuses */
+ if (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_TOOL) continue;
+
+ /* Apply the bonuses to hit/damage */
+ p_ptr->to_h += o_ptr->to_h;
+ p_ptr->to_d += o_ptr->to_d;
+
+ /* Apply the mental bonuses tp hit/damage, if known */
+ if (object_known_p(o_ptr)) p_ptr->dis_to_h += o_ptr->to_h;
+ if (object_known_p(o_ptr)) p_ptr->dis_to_d += o_ptr->to_d;
+ }
+
+ /* Monks get extra ac for armour _not worn_ */
+ if ((p_ptr->melee_style == SKILL_HAND) && !(monk_heavy_armor()))
+ {
+ if (!(p_ptr->inventory[INVEN_BODY].k_idx))
+ {
+ p_ptr->to_a += get_skill_scale(SKILL_HAND, 75);
+ p_ptr->dis_to_a += get_skill_scale(SKILL_HAND, 75);
+ }
+ if (!(p_ptr->inventory[INVEN_OUTER].k_idx) && (get_skill(SKILL_HAND) > 15))
+ {
+ p_ptr->to_a += ((get_skill(SKILL_HAND) - 13) / 3);
+ p_ptr->dis_to_a += ((get_skill(SKILL_HAND) - 13) / 3);
+ }
+ if (!(p_ptr->inventory[INVEN_ARM].k_idx) && (get_skill(SKILL_HAND) > 10))
+ {
+ p_ptr->to_a += ((get_skill(SKILL_HAND) - 8) / 3);
+ p_ptr->dis_to_a += ((get_skill(SKILL_HAND) - 8) / 3);
+ }
+ if (!(p_ptr->inventory[INVEN_HEAD].k_idx) && (get_skill(SKILL_HAND) > 4))
+ {
+ p_ptr->to_a += (get_skill(SKILL_HAND) - 2) / 3;
+ p_ptr->dis_to_a += (get_skill(SKILL_HAND) - 2) / 3;
+ }
+ if (!(p_ptr->inventory[INVEN_HANDS].k_idx))
+ {
+ p_ptr->to_a += (get_skill(SKILL_HAND) / 2);
+ p_ptr->dis_to_a += (get_skill(SKILL_HAND) / 2);
+ }
+ if (!(p_ptr->inventory[INVEN_FEET].k_idx))
+ {
+ p_ptr->to_a += (get_skill(SKILL_HAND) / 3);
+ p_ptr->dis_to_a += (get_skill(SKILL_HAND) / 3);
+ }
+ }
+
+ /* Hack -- aura of fire also provides light */
+ if (p_ptr->sh_fire) p_ptr->lite = TRUE;
+
+ if (PRACE_FLAG(PR1_AC_LEVEL))
+ {
+ p_ptr->to_a += 20 + (p_ptr->lev / 5);
+ p_ptr->dis_to_a += 20 + (p_ptr->lev / 5);
+ }
+
+ /* Take care of gods */
+ calc_gods();
+
+ /* Calculate stats */
+ for (i = 0; i < 6; i++)
+ {
+ int top, use, ind;
+
+
+ /* Extract the new "stat_use" value for the stat */
+ top = modify_stat_value(p_ptr->stat_max[i], p_ptr->stat_add[i]);
+
+ /* Notice changes */
+ if (p_ptr->stat_top[i] != top)
+ {
+ /* Save the new value */
+ p_ptr->stat_top[i] = top;
+
+ /* Redisplay the stats later */
+ p_ptr->redraw |= (PR_STATS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ /* Extract the new "stat_use" value for the stat */
+ use = modify_stat_value(p_ptr->stat_cur[i], p_ptr->stat_add[i]);
+
+ /* Notice changes */
+ if (p_ptr->stat_use[i] != use)
+ {
+ /* Save the new value */
+ p_ptr->stat_use[i] = use;
+
+ /* Redisplay the stats later */
+ p_ptr->redraw |= (PR_STATS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+
+ /* Values: 3, 4, ..., 17 */
+ if (use <= 18) ind = (use - 3);
+
+ /* Ranges: 18/00-18/09, ..., 18/210-18/219 */
+ else if (use <= 18 + 219) ind = (15 + (use - 18) / 10);
+
+ /* Range: 18/220+ */
+ else ind = (37);
+
+ /* Notice changes */
+ if (p_ptr->stat_ind[i] != ind)
+ {
+ /* Save the new index */
+ p_ptr->stat_ind[i] = ind;
+
+ /* Change in CON affects Hitpoints */
+ if (i == A_CON)
+ {
+ p_ptr->update |= (PU_HP);
+ }
+
+ /* Change in WIS affects Sanity Points */
+ else if (i == A_WIS)
+ {
+ p_ptr->update |= (PU_MANA | PU_SANITY);
+ }
+
+ /* Change in spell stat affects Mana/Spells */
+ if (i == A_INT)
+ {
+ p_ptr->update |= (PU_MANA | PU_SPELLS);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ }
+
+ /* Provide the damage we get from sacrifice */
+ GOD(GOD_MELKOR)
+ {
+ int x = wisdom_scale(4);
+ if (x < 1) x = 1;
+
+ p_ptr->dis_to_d += x * p_ptr->melkor_sacrifice;
+ p_ptr->to_d += x * p_ptr->melkor_sacrifice;
+ }
+
+ /* jk - add in the tactics */
+ p_ptr->dis_to_h += tactic_info[(byte)p_ptr->tactic].to_hit;
+ p_ptr->to_h += tactic_info[(byte)p_ptr->tactic].to_hit;
+ p_ptr->dis_to_d += tactic_info[(byte)p_ptr->tactic].to_dam;
+ p_ptr->to_d += tactic_info[(byte)p_ptr->tactic].to_dam;
+ p_ptr->dis_to_a += tactic_info[(byte)p_ptr->tactic].to_ac;
+ p_ptr->to_a += tactic_info[(byte)p_ptr->tactic].to_ac;
+
+ p_ptr->skill_stl += tactic_info[(byte)p_ptr->tactic].to_stealth;
+ p_ptr->skill_dis += tactic_info[(byte)p_ptr->tactic].to_disarm;
+ p_ptr->skill_sav += tactic_info[(byte)p_ptr->tactic].to_saving;
+
+ p_ptr->pspeed += move_info[(byte)p_ptr->movement].to_speed;
+ p_ptr->skill_srh += move_info[(byte)p_ptr->movement].to_search;
+ p_ptr->skill_fos += move_info[(byte)p_ptr->movement].to_percep;
+ p_ptr->skill_stl += move_info[(byte)p_ptr->movement].to_stealth;
+
+ /* Apply temporary "stun" */
+ if (p_ptr->stun > 50)
+ {
+ p_ptr->to_h -= 20;
+ p_ptr->dis_to_h -= 20;
+ p_ptr->to_d -= 20;
+ p_ptr->dis_to_d -= 20;
+ }
+ else if (p_ptr->stun)
+ {
+ p_ptr->to_h -= 5;
+ p_ptr->dis_to_h -= 5;
+ p_ptr->to_d -= 5;
+ p_ptr->dis_to_d -= 5;
+ }
+
+
+ /* Invulnerability */
+ if (p_ptr->invuln)
+ {
+ p_ptr->to_a += 100;
+ p_ptr->dis_to_a += 100;
+ }
+
+ /* Breath */
+ if (p_ptr->tim_water_breath)
+ {
+ p_ptr->water_breath = TRUE;
+ }
+ if (p_ptr->tim_magic_breath)
+ {
+ p_ptr->magical_breath = TRUE;
+ }
+
+ /* wraith_form */
+ if (p_ptr->tim_wraith)
+ {
+ if (p_ptr->disembodied)
+ {
+ p_ptr->to_a += 10;
+ p_ptr->dis_to_a += 10;
+ }
+ else
+ {
+ p_ptr->to_a += 50;
+ p_ptr->dis_to_a += 50;
+ p_ptr->reflect = TRUE;
+ }
+ p_ptr->wraith_form = TRUE;
+ }
+
+ /* Temporary holy aura */
+ if (p_ptr->holy)
+ {
+ p_ptr->hold_life = TRUE;
+ p_ptr->luck_cur += 5;
+ }
+
+ /* Temporary blessing */
+ if (p_ptr->blessed)
+ {
+ p_ptr->to_a += 5;
+ p_ptr->dis_to_a += 5;
+ p_ptr->to_h += 10;
+ p_ptr->dis_to_h += 10;
+ }
+
+ /* Temporary invisibility */
+ if (p_ptr->tim_invisible)
+ {
+ p_ptr->invis += p_ptr->tim_inv_pow;
+ }
+
+ /* Temporary shield */
+ if (p_ptr->shield)
+ {
+ p_ptr->to_a += p_ptr->shield_power;
+ p_ptr->dis_to_a += p_ptr->shield_power;
+ }
+
+ /* Temporary "Hero" */
+ if (p_ptr->hero)
+ {
+ p_ptr->to_h += 12;
+ p_ptr->dis_to_h += 12;
+ }
+
+ /* Temporary "roots" */
+ if (p_ptr->tim_roots)
+ {
+ set_stun(0);
+ p_ptr->to_d_melee += p_ptr->tim_roots_dam;
+ p_ptr->to_a += p_ptr->tim_roots_ac;
+ p_ptr->dis_to_a += p_ptr->tim_roots_ac;
+ }
+
+ /* Temporary "Beserk" */
+ if (p_ptr->shero)
+ {
+ p_ptr->to_h += 24;
+ p_ptr->dis_to_h += 24;
+ p_ptr->to_a -= 10;
+ p_ptr->dis_to_a -= 10;
+ }
+
+ /* Temporary "Accurancy" */
+ if (p_ptr->strike)
+ {
+ p_ptr->to_d += 15;
+ p_ptr->dis_to_d += 15;
+ p_ptr->to_h += 15;
+ p_ptr->dis_to_h += 15;
+ }
+
+ /* Temporary "Meditation" */
+ if (p_ptr->meditation)
+ {
+ p_ptr->to_d -= 25;
+ p_ptr->dis_to_d -= 25;
+ p_ptr->to_h -= 25;
+ p_ptr->dis_to_h -= 25;
+ }
+
+ /* Temporary "Reflection" */
+ if (p_ptr->tim_reflect)
+ {
+ p_ptr->reflect = TRUE;
+ }
+
+ /* Temporary "Time Resistance" */
+ if (p_ptr->tim_res_time)
+ {
+ p_ptr->resist_continuum = TRUE;
+ }
+
+ /* Temporary "Levitation" and "Flying" */
+ if (p_ptr->tim_ffall)
+ {
+ p_ptr->ffall = TRUE;
+ }
+ if (p_ptr->tim_fly)
+ {
+ p_ptr->fly = TRUE;
+ }
+
+ /* Temporary "Fire Aura" */
+ if (p_ptr->tim_fire_aura)
+ {
+ p_ptr->sh_fire = TRUE;
+ }
+
+ /* Oppose Light & Dark */
+ if (p_ptr->oppose_ld)
+ {
+ p_ptr->resist_lite = TRUE;
+ p_ptr->resist_dark = TRUE;
+ }
+
+ /* Oppose Chaos & Confusion */
+ if (p_ptr->oppose_cc)
+ {
+ p_ptr->resist_chaos = TRUE;
+ p_ptr->resist_conf = TRUE;
+ }
+
+ /* Oppose Sound & Shards */
+ if (p_ptr->oppose_ss)
+ {
+ p_ptr->resist_sound = TRUE;
+ p_ptr->resist_shard = TRUE;
+ }
+
+ /* Oppose Nexus */
+ if (p_ptr->oppose_nex)
+ {
+ p_ptr->resist_nexus = TRUE;
+ }
+
+ /* Mental barrier */
+ if (p_ptr->tim_mental_barrier)
+ {
+ p_ptr->sustain_int = TRUE;
+ p_ptr->sustain_wis = TRUE;
+ }
+
+ /* Temporary "fast" */
+ if (p_ptr->fast)
+ {
+ p_ptr->pspeed += p_ptr->speed_factor;
+ }
+
+ /* Temporary "light speed" */
+ if (p_ptr->lightspeed)
+ {
+ p_ptr->pspeed += 50;
+ }
+
+ /* Temporary "slow" */
+ if (p_ptr->slow)
+ {
+ p_ptr->pspeed -= 10;
+ }
+
+ if (p_ptr->tim_esp)
+ {
+ p_ptr->telepathy |= ESP_ALL;
+ }
+
+ /* Temporary see invisible */
+ if (p_ptr->tim_invis)
+ {
+ p_ptr->see_inv = TRUE;
+ }
+
+ /* Temporary infravision boost */
+ if (p_ptr->tim_infra)
+ {
+ p_ptr->see_infra++;
+ }
+
+ /* Hack -- Magic breath -> Water breath */
+ if (p_ptr->magical_breath)
+ {
+ p_ptr->water_breath = TRUE;
+ }
+
+ /* Hack -- Can Fly -> Can Levitate */
+ if (p_ptr->fly)
+ {
+ p_ptr->ffall = TRUE;
+ }
+
+ /* Hack -- Res Chaos -> Res Conf */
+ if (p_ptr->resist_chaos)
+ {
+ p_ptr->resist_conf = TRUE;
+ }
+
+ /* Hack -- Hero/Shero -> Res fear */
+ if (p_ptr->hero || p_ptr->shero)
+ {
+ p_ptr->resist_fear = TRUE;
+ }
+
+
+ /* Hack -- Telepathy Change */
+ if (p_ptr->telepathy != old_telepathy)
+ {
+ p_ptr->update |= (PU_MONSTERS);
+ }
+
+ /* Hack -- See Invis Change */
+ if (p_ptr->see_inv != old_see_inv)
+ {
+ p_ptr->update |= (PU_MONSTERS);
+ }
+
+
+ /* Extract the current weight (in tenth pounds) */
+ j = calc_total_weight();
+
+ /* Extract the "weight limit" (in tenth pounds) */
+ i = weight_limit();
+
+ /* XXX XXX XXX Apply "encumbrance" from weight */
+ if (j > i / 2) p_ptr->pspeed -= ((j - (i / 2)) / (i / 10));
+
+ /* Bloating slows the player down (a little) */
+ if (p_ptr->food >= PY_FOOD_MAX) p_ptr->pspeed -= 10;
+
+ /* Searching slows the player down */
+ if (p_ptr->searching) p_ptr->pspeed -= 10;
+
+ /* In order to get a "nice" mana path druids need to ahve a 0 speed */
+ if ((p_ptr->druid_extra2 == CLASS_MANA_PATH) && (p_ptr->pspeed > 110))
+ p_ptr->pspeed = 110;
+
+ /* Display the speed (if needed) */
+ if (p_ptr->pspeed != old_speed) p_ptr->redraw |= (PR_SPEED);
+
+
+ /* Actual Modifier Bonuses (Un-inflate stat bonuses) */
+ p_ptr->to_a += ((int)(adj_dex_ta[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->to_d += ((int)(adj_str_td[p_ptr->stat_ind[A_STR]]) - 128);
+ p_ptr->to_h += ((int)(adj_dex_th[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->to_h += ((int)(adj_str_th[p_ptr->stat_ind[A_STR]]) - 128);
+
+ /* Displayed Modifier Bonuses (Un-inflate stat bonuses) */
+ p_ptr->dis_to_a += ((int)(adj_dex_ta[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->dis_to_d += ((int)(adj_str_td[p_ptr->stat_ind[A_STR]]) - 128);
+ p_ptr->dis_to_h += ((int)(adj_dex_th[p_ptr->stat_ind[A_DEX]]) - 128);
+ p_ptr->dis_to_h += ((int)(adj_str_th[p_ptr->stat_ind[A_STR]]) - 128);
+
+ /* Redraw armor (if needed) */
+ if ((p_ptr->dis_ac != old_dis_ac) || (p_ptr->dis_to_a != old_dis_to_a))
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_ARMOR);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+
+ /* Obtain the "hold" value */
+ hold = adj_str_hold[p_ptr->stat_ind[A_STR]];
+
+
+ /* Examine the "current bow" */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+
+ /* Assume not heavy */
+ p_ptr->heavy_shoot = FALSE;
+
+ /* It is hard to carholdry a heavy bow */
+ if (hold < o_ptr->weight / 10)
+ {
+ /* Hard to wield a heavy bow */
+ p_ptr->to_h += 2 * (hold - o_ptr->weight / 10);
+ p_ptr->dis_to_h += 2 * (hold - o_ptr->weight / 10);
+
+ /* Heavy Bow */
+ p_ptr->heavy_shoot = TRUE;
+ }
+
+ /* Take note of required "tval" for missiles */
+ switch (o_ptr->sval)
+ {
+ case SV_SLING:
+ {
+ p_ptr->tval_ammo = TV_SHOT;
+ break;
+ }
+
+ case SV_SHORT_BOW:
+ case SV_LONG_BOW:
+ {
+ p_ptr->tval_ammo = TV_ARROW;
+ break;
+ }
+
+ case SV_LIGHT_XBOW:
+ case SV_HEAVY_XBOW:
+ {
+ p_ptr->tval_ammo = TV_BOLT;
+ break;
+ }
+ }
+
+ /* Compute "extra shots" if needed */
+ if (o_ptr->k_idx && !p_ptr->heavy_shoot)
+ {
+ int archery = get_archery_skill();
+
+ if (archery != -1)
+ {
+ p_ptr->to_h_ranged += get_skill_scale(archery, 25);
+ p_ptr->num_fire += (get_skill(archery) / 16);
+ p_ptr->xtra_might += (get_skill(archery) / 25);
+ switch (archery)
+ {
+ case SKILL_SLING:
+ if (p_ptr->tval_ammo == TV_SHOT) p_ptr->xtra_might += get_skill(archery) / 30;
+ break;
+ case SKILL_BOW:
+ if (p_ptr->tval_ammo == TV_ARROW) p_ptr->xtra_might += get_skill(archery) / 30;
+ break;
+ case SKILL_XBOW:
+ if (p_ptr->tval_ammo == TV_BOLT) p_ptr->xtra_might += get_skill(archery) / 30;
+ break;
+ }
+ }
+
+ /* Add in the "bonus shots" */
+ p_ptr->num_fire += extra_shots;
+
+ /* Require at least one shot */
+ if (p_ptr->num_fire < 1) p_ptr->num_fire = 1;
+ }
+
+ if (PRACE_FLAG(PR1_XTRA_MIGHT_BOW) && p_ptr->tval_ammo == TV_ARROW)
+ p_ptr->xtra_might += 1;
+
+ if (PRACE_FLAG(PR1_XTRA_MIGHT_SLING) && p_ptr->tval_ammo == TV_SHOT)
+ p_ptr->xtra_might += 1;
+
+ if (PRACE_FLAG(PR1_XTRA_MIGHT_XBOW) && p_ptr->tval_ammo == TV_BOLT)
+ p_ptr->xtra_might += 1;
+
+ /* Examine the "current tool" */
+ o_ptr = &p_ptr->inventory[INVEN_TOOL];
+
+ /* Boost digging skill by tool weight */
+ if (o_ptr->k_idx && (o_ptr->tval == TV_DIGGING))
+ {
+ p_ptr->skill_dig += (o_ptr->weight / 10);
+ }
+
+ /* Examine the main weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Assume not heavy */
+ p_ptr->heavy_wield = FALSE;
+
+ /* Normal weapons */
+ if (o_ptr->k_idx && !p_ptr->heavy_wield)
+ {
+ int str_index, dex_index;
+
+ int num = 0, wgt = 0, mul = 0, div = 0;
+
+ analyze_blow(&num, &wgt, &mul);
+
+ /* Enforce a minimum "weight" (tenth pounds) */
+ div = ((o_ptr->weight < wgt) ? wgt : o_ptr->weight);
+
+ /* Access the strength vs weight */
+ str_index = (adj_str_blow[p_ptr->stat_ind[A_STR]] * mul / div);
+
+ /* Maximal value */
+ if (str_index > 11) str_index = 11;
+
+ /* Index by dexterity */
+ dex_index = (adj_dex_blow[p_ptr->stat_ind[A_DEX]]);
+
+ /* Maximal value */
+ if (dex_index > 11) dex_index = 11;
+
+ /* Use the blows table */
+ p_ptr->num_blow = blows_table[str_index][dex_index];
+
+ /* Maximal value */
+ if (p_ptr->num_blow > num) p_ptr->num_blow = num;
+
+ /* Add in the "bonus blows" */
+ p_ptr->num_blow += extra_blows;
+
+ /* Special class bonus blows */
+ p_ptr->num_blow += p_ptr->lev * cp_ptr->extra_blows / 50;
+
+ /* Weapon specialization bonus blows */
+ if (get_weaponmastery_skill() != -1)
+ p_ptr->num_blow += get_skill_scale(get_weaponmastery_skill(), 2);
+
+ /* Bonus blows for plain weaponmastery skill */
+ p_ptr->num_blow += get_skill_scale(SKILL_MASTERY, 3);
+
+ /* Require at least one blow */
+ if (p_ptr->num_blow < 1) p_ptr->num_blow = 1;
+ }
+ /* Different calculation for bear form with empty hands */
+ else if ((p_ptr->melee_style == SKILL_HAND) && monk_empty_hands())
+ {
+ int plev = get_skill(SKILL_HAND);
+
+ p_ptr->num_blow = 0;
+
+ if (plev > 9) p_ptr->num_blow++;
+ if (plev > 19) p_ptr->num_blow++;
+ if (plev > 29) p_ptr->num_blow++;
+ if (plev > 34) p_ptr->num_blow++;
+ if (plev > 39) p_ptr->num_blow++;
+ if (plev > 44) p_ptr->num_blow++;
+ if (plev > 49) p_ptr->num_blow++;
+
+ if (monk_heavy_armor()) p_ptr->num_blow /= 2;
+
+ p_ptr->num_blow += 1 + extra_blows;
+
+ if (!monk_heavy_armor())
+ {
+ p_ptr->to_h += (plev / 3);
+ p_ptr->to_d += (plev / 3);
+
+ p_ptr->dis_to_h += (plev / 3);
+ p_ptr->dis_to_d += (plev / 3);
+ }
+ }
+
+ /* Monsters that only have their "natural" attacks */
+ else if (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON])
+ {
+ int str_index, dex_index;
+ int num = 0, wgt = 0, mul = 0;
+ monster_race *r_ptr = race_info_idx(p_ptr->body_monster, 0);
+
+ analyze_blow(&num, &wgt, &mul);
+
+ /* Access the strength vs weight */
+ str_index = (adj_str_blow[p_ptr->stat_ind[A_STR]] * mul / 3);
+
+ /* Maximal value */
+ if (str_index > 11) str_index = 11;
+
+ /* Index by dexterity */
+ dex_index = (adj_dex_blow[p_ptr->stat_ind[A_DEX]]);
+
+ /* Maximal value */
+ if (dex_index > 11) dex_index = 11;
+
+ /* Use the blows table */
+ p_ptr->num_blow = blows_table[str_index][dex_index];
+
+ /* Add in the "bonus blows" */
+ p_ptr->num_blow += extra_blows;
+
+ /* Maximal value */
+ if (p_ptr->num_blow > 4) p_ptr->num_blow = 4;
+
+ /* Require at least one blow */
+ if (p_ptr->num_blow < 1) p_ptr->num_blow = 1;
+
+ /* Limit as defined by monster body */
+ for (num = 0; num < p_ptr->num_blow; num++)
+ if (!r_ptr->blow[num].effect)
+ break;
+ p_ptr->num_blow = num;
+ }
+
+ /* Different calculation for monks with empty hands */
+ else if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) && (p_ptr->melee_style == SKILL_BEAR))
+ {
+ int plev = get_skill(SKILL_BEAR);
+
+ p_ptr->num_blow = 0;
+
+ p_ptr->num_blow += 2 + (plev / 5) + extra_blows;
+
+ p_ptr->to_h -= (plev / 5);
+ p_ptr->dis_to_h -= (plev / 5);
+
+ p_ptr->to_d += (plev / 2);
+ p_ptr->dis_to_d += (plev / 2);
+ }
+
+ /* Assume okay */
+ p_ptr->icky_wield = FALSE;
+ monk_armour_aux = FALSE;
+
+ if (get_weaponmastery_skill() != -1)
+ {
+ int lev = get_skill(get_weaponmastery_skill());
+
+ p_ptr->to_h_melee += lev;
+ p_ptr->to_d_melee += lev / 2;
+ }
+
+ if (get_skill(SKILL_COMBAT))
+ {
+ int lev = get_skill_scale(SKILL_COMBAT, 10);
+
+ p_ptr->to_d += lev;
+ p_ptr->dis_to_d += lev;
+ }
+
+ if (get_skill(SKILL_DODGE))
+ {
+ /* Get the armor weight */
+ int cur_wgt = 0;
+
+ cur_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ cur_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ cur_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ cur_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ cur_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ /* Base dodge chance */
+ p_ptr->dodge_chance = get_skill_scale(SKILL_DODGE, 150) + get_skill(SKILL_HAND);
+
+ /* Armor weight bonus/penalty */
+ p_ptr->dodge_chance -= cur_wgt * 2;
+
+ /* Encumberance bonus/penalty */
+ p_ptr->dodge_chance = p_ptr->dodge_chance - (calc_total_weight() / 100);
+
+ /* Never below 0 */
+ if (p_ptr->dodge_chance < 0) p_ptr->dodge_chance = 0;
+ }
+ else
+ {
+ p_ptr->dodge_chance = 0;
+ }
+
+ /* Parse all the weapons */
+ i = 0;
+ while (p_ptr->body_parts[i] == INVEN_WIELD)
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ /* 2handed weapon and shield = less damage */
+ if (p_ptr->inventory[INVEN_WIELD + i].k_idx && p_ptr->inventory[INVEN_ARM + i].k_idx)
+ {
+ /* Extract the item flags */
+ object_flags(&p_ptr->inventory[INVEN_WIELD + i], &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_COULD2H)
+ {
+ int tmp;
+
+ /* Reduce the bonuses */
+ tmp = o_ptr->to_h / 2;
+ if (tmp < 0) tmp = -tmp;
+ p_ptr->to_h_melee -= tmp;
+
+ tmp = o_ptr->to_d / 2;
+ if (tmp < 0) tmp = -tmp;
+ tmp += (o_ptr->dd * o_ptr->ds) / 2;
+ p_ptr->to_d_melee -= tmp;
+ }
+ }
+
+ /* Priest weapon penalty for non-blessed edged weapons */
+ if (((forbid_non_blessed()) && (!p_ptr->bless_blade) &&
+ ((o_ptr->tval == TV_AXE) || (o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM))) && (o_ptr->k_idx))
+ {
+ /* Reduce the real bonuses */
+ p_ptr->to_h -= 15;
+ p_ptr->to_d -= 15;
+
+ /* Reduce the mental bonuses */
+ p_ptr->dis_to_h -= 15;
+ p_ptr->dis_to_d -= 15;
+
+ /* Icky weapon */
+ p_ptr->icky_wield = TRUE;
+ }
+
+ /* Sorcerer can't wield a weapon unless it's a mage staff */
+ if (get_skill(SKILL_SORCERY))
+ {
+ int malus = get_skill_scale(SKILL_SORCERY, 100);
+
+ if ((o_ptr->tval != TV_MSTAFF) && (o_ptr->k_idx))
+ {
+ /* Reduce the real bonuses */
+ p_ptr->to_h -= malus;
+ p_ptr->to_d -= malus;
+
+ /* Reduce the mental bonuses */
+ p_ptr->dis_to_h -= malus;
+ p_ptr->dis_to_d -= malus;
+
+ /* Icky weapon */
+ p_ptr->icky_wield = TRUE;
+ }
+ else
+ {
+ /* Reduce the real bonuses */
+ p_ptr->to_h -= malus / 10;
+ p_ptr->to_d -= malus / 10;
+
+ /* Reduce the mental bonuses */
+ p_ptr->dis_to_h -= malus / 10;
+ p_ptr->dis_to_d -= malus / 10;
+ }
+ }
+
+ /* Check next weapon */
+ i++;
+ }
+
+ if (monk_heavy_armor())
+ {
+ monk_armour_aux = TRUE;
+ }
+
+ /* Affect Skill -- stealth (bonus one) */
+ p_ptr->skill_stl += 1;
+
+ /* Affect Skill -- disarming (DEX and INT) */
+ p_ptr->skill_dis += adj_dex_dis[p_ptr->stat_ind[A_DEX]];
+ p_ptr->skill_dis += adj_int_dis[p_ptr->stat_ind[A_INT]];
+
+ /* Affect Skill -- magic devices (INT) */
+ p_ptr->skill_dev += get_skill_scale(SKILL_DEVICE, 20);
+
+ /* Affect Skill -- saving throw (WIS) */
+ p_ptr->skill_sav += adj_wis_sav[p_ptr->stat_ind[A_WIS]];
+
+ /* Affect Skill -- digging (STR) */
+ p_ptr->skill_dig += adj_str_dig[p_ptr->stat_ind[A_STR]];
+
+ /* Affect Skill -- disarming (skill) */
+ p_ptr->skill_dis += (get_skill_scale(SKILL_DISARMING, 75));
+
+ /* Affect Skill -- magic devices (skill) */
+ p_ptr->skill_dev += (get_skill_scale(SKILL_DEVICE, 150));
+
+ /* Affect Skill -- saving throw (skill and level) */
+ p_ptr->skill_sav += (get_skill_scale(SKILL_SPIRITUALITY, 75));
+
+ /* Affect Skill -- stealth (skill) */
+ p_ptr->skill_stl += (get_skill_scale(SKILL_STEALTH, 25));
+
+ /* Affect Skill -- search ability (Sneakiness skill) */
+ p_ptr->skill_srh += (get_skill_scale(SKILL_SNEAK, 35));
+
+ /* Affect Skill -- search frequency (Sneakiness skill) */
+ p_ptr->skill_fos += (get_skill_scale(SKILL_SNEAK, 25));
+
+ /* Affect Skill -- combat (Combat skill + mastery) */
+ p_ptr->skill_thn += (50 * (((7 * get_skill(p_ptr->melee_style)) + (3 * get_skill(SKILL_COMBAT))) / 10) / 10);
+
+ /* Affect Skill -- combat (shooting) (Level, by Class) */
+ p_ptr->skill_thb += (50 * (((7 * get_skill(SKILL_ARCHERY)) + (3 * get_skill(SKILL_COMBAT))) / 10) / 10);
+
+ /* Affect Skill -- combat (throwing) (Level) */
+ p_ptr->skill_tht += (50 * p_ptr->lev / 10);
+
+
+ /* Limit Skill -- stealth from 0 to 30 */
+ if (p_ptr->skill_stl > 30) p_ptr->skill_stl = 30;
+ if (p_ptr->skill_stl < 0) p_ptr->skill_stl = 0;
+
+ /* Limit Skill -- digging from 1 up */
+ if (p_ptr->skill_dig < 1) p_ptr->skill_dig = 1;
+
+ if ((p_ptr->anti_magic) && (p_ptr->skill_sav < 95)) p_ptr->skill_sav = 95;
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ /* Take note when "heavy bow" changes */
+ if (p_ptr->old_heavy_shoot != p_ptr->heavy_shoot)
+ {
+ if (silent)
+ {
+ /* do nothing */
+ }
+ /* Message */
+ else if (p_ptr->heavy_shoot)
+ {
+ msg_print("You have trouble wielding such a heavy bow.");
+ }
+ else if (p_ptr->inventory[INVEN_BOW].k_idx)
+ {
+ msg_print("You have no trouble wielding your bow.");
+ }
+ else
+ {
+ msg_print("You feel relieved to put down your heavy bow.");
+ }
+
+ /* Save it */
+ p_ptr->old_heavy_shoot = p_ptr->heavy_shoot;
+ }
+
+
+ /* Take note when "heavy weapon" changes */
+ if (p_ptr->old_heavy_wield != p_ptr->heavy_wield)
+ {
+ if (silent)
+ {
+ /* do nothing */
+ }
+ /* Message */
+ else if (p_ptr->heavy_wield)
+ {
+ msg_print("You have trouble wielding such a heavy weapon.");
+ }
+ else if (p_ptr->inventory[INVEN_WIELD].k_idx)
+ {
+ msg_print("You have no trouble wielding your weapon.");
+ }
+ else
+ {
+ msg_print("You feel relieved to put down your heavy weapon.");
+ }
+
+ /* Save it */
+ p_ptr->old_heavy_wield = p_ptr->heavy_wield;
+ }
+
+
+ /* Take note when "illegal weapon" changes */
+ if (p_ptr->old_icky_wield != p_ptr->icky_wield)
+ {
+ if (silent)
+ {
+ /* do nothing */
+ }
+ /* Message */
+ else if (p_ptr->icky_wield)
+ {
+ msg_print("You do not feel comfortable with your weapon.");
+ }
+ else if (p_ptr->inventory[INVEN_WIELD].k_idx)
+ {
+ msg_print("You feel comfortable with your weapon.");
+ }
+ else
+ {
+ msg_print("You feel more comfortable after removing your weapon.");
+ }
+
+ /* Save it */
+ p_ptr->old_icky_wield = p_ptr->icky_wield;
+ }
+
+ if (monk_armour_aux != monk_notify_aux)
+ {
+ if ((p_ptr->melee_style != SKILL_HAND) || silent)
+ {
+ /* do nothing */
+ }
+ else if (monk_heavy_armor())
+ msg_print("The weight of your armor disrupts your balance.");
+ else
+ msg_print("You regain your balance.");
+ monk_notify_aux = monk_armour_aux;
+ }
+
+ /* Resist lite & senseible lite negates one an other */
+ if (p_ptr->resist_lite && p_ptr->sensible_lite)
+ {
+ p_ptr->resist_lite = p_ptr->sensible_lite = FALSE;
+ }
+
+ /* resistance to fire cancel sensibility to fire */
+ if (p_ptr->resist_fire || p_ptr->oppose_fire || p_ptr->immune_fire)
+ p_ptr->sensible_fire = FALSE;
+
+ /* Minimum saving throw */
+ if(p_ptr->skill_sav <= 10)
+ p_ptr->skill_sav = 10;
+ else
+ p_ptr->skill_sav += 10;
+
+ /* Let the scripts do what they need */
+ process_hooks(HOOK_CALC_BONUS_END, "(d)", silent);
+}
+
+
+
+/*
+ * Handle "p_ptr->notice"
+ */
+void notice_stuff(void)
+{
+ /* Notice stuff */
+ if (!p_ptr->notice) return;
+
+
+ /* Combine the pack */
+ if (p_ptr->notice & (PN_COMBINE))
+ {
+ p_ptr->notice &= ~(PN_COMBINE);
+ combine_pack();
+ }
+
+ /* Reorder the pack */
+ if (p_ptr->notice & (PN_REORDER))
+ {
+ p_ptr->notice &= ~(PN_REORDER);
+ reorder_pack();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->update"
+ */
+void update_stuff(void)
+{
+ /* Update stuff */
+ if (!p_ptr->update) return;
+
+
+ if (p_ptr->update & (PU_BODY))
+ {
+ p_ptr->update &= ~(PU_BODY);
+ calc_body();
+ }
+
+ if (p_ptr->update & (PU_BONUS))
+ {
+ /* Ok now THAT is an ugly hack */
+ p_ptr->update &= ~(PU_POWERS);
+ calc_powers();
+
+ p_ptr->update &= ~(PU_BONUS);
+ calc_bonuses(FALSE);
+ }
+
+ if (p_ptr->update & (PU_TORCH))
+ {
+ p_ptr->update &= ~(PU_TORCH);
+ calc_torch();
+ }
+
+ if (p_ptr->update & (PU_HP))
+ {
+ p_ptr->update &= ~(PU_HP);
+ calc_hitpoints();
+ }
+
+ if (p_ptr->update & (PU_SANITY))
+ {
+ p_ptr->update &= ~(PU_SANITY);
+ calc_sanity();
+ }
+
+ if (p_ptr->update & (PU_MANA))
+ {
+ p_ptr->update &= ~(PU_MANA);
+ calc_mana();
+ }
+
+ if (p_ptr->update & (PU_SPELLS))
+ {
+ p_ptr->update &= ~(PU_SPELLS);
+ calc_spells();
+ }
+
+ if (p_ptr->update & (PU_POWERS))
+ {
+ p_ptr->update &= ~(PU_POWERS);
+ calc_powers();
+ }
+
+ /* Character is not ready yet, no screen updates */
+ if (!character_generated) return;
+
+
+ /* Character is in "icky" mode, no screen updates */
+ if (character_icky) return;
+
+
+ if (p_ptr->update & (PU_UN_VIEW))
+ {
+ p_ptr->update &= ~(PU_UN_VIEW);
+ forget_view();
+ }
+
+ if (p_ptr->update & (PU_VIEW))
+ {
+ p_ptr->update &= ~(PU_VIEW);
+ update_view();
+ }
+
+ if (p_ptr->update & (PU_FLOW))
+ {
+ p_ptr->update &= ~(PU_FLOW);
+ update_flow();
+ }
+
+ if (p_ptr->update & (PU_DISTANCE))
+ {
+ p_ptr->update &= ~(PU_DISTANCE);
+ p_ptr->update &= ~(PU_MONSTERS);
+ update_monsters(TRUE);
+ }
+
+ if (p_ptr->update & (PU_MONSTERS))
+ {
+ p_ptr->update &= ~(PU_MONSTERS);
+ update_monsters(FALSE);
+ }
+
+ if (p_ptr->update & (PU_MON_LITE))
+ {
+ p_ptr->update &= ~(PU_MON_LITE);
+ if (monster_lite) update_mon_lite();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->redraw"
+ */
+void redraw_stuff(void)
+{
+ /* Redraw stuff */
+ if (!p_ptr->redraw) return;
+
+
+ /* Character is not ready yet, no screen updates */
+ if (!character_generated) return;
+
+
+ /* Character is in "icky" mode, no screen updates */
+ if (character_icky) return;
+
+
+ /* Should we tell lua to redisplay too ? */
+ process_hooks(HOOK_REDRAW, "()");
+
+
+ /* Hack -- clear the screen */
+ if (p_ptr->redraw & (PR_WIPE))
+ {
+ p_ptr->redraw &= ~(PR_WIPE);
+ msg_print(NULL);
+ Term_clear();
+ }
+
+
+ if (p_ptr->redraw & (PR_MAP))
+ {
+ p_ptr->redraw &= ~(PR_MAP);
+ prt_map();
+ }
+
+
+ if (p_ptr->redraw & (PR_BASIC))
+ {
+ p_ptr->redraw &= ~(PR_BASIC);
+ p_ptr->redraw &= ~(PR_MISC | PR_TITLE | PR_STATS);
+ p_ptr->redraw &= ~(PR_LEV | PR_EXP | PR_GOLD);
+ p_ptr->redraw &= ~(PR_ARMOR | PR_HP | PR_MANA | PR_PIETY | PR_MH);
+ p_ptr->redraw &= ~(PR_DEPTH | PR_HEALTH);
+ prt_frame_basic();
+ }
+
+ if (p_ptr->redraw & (PR_MISC))
+ {
+ p_ptr->redraw &= ~(PR_MISC);
+ prt_field(rp_ptr->title + rp_name, ROW_RACE, COL_RACE);
+ prt_field(spp_ptr->title + c_name, ROW_CLASS, COL_CLASS);
+ }
+
+ if (p_ptr->redraw & (PR_TITLE))
+ {
+ p_ptr->redraw &= ~(PR_TITLE);
+ prt_title();
+ }
+
+ if (p_ptr->redraw & (PR_LEV))
+ {
+ p_ptr->redraw &= ~(PR_LEV);
+ prt_level();
+ }
+
+ if (p_ptr->redraw & (PR_EXP))
+ {
+ p_ptr->redraw &= ~(PR_EXP);
+ prt_exp();
+ }
+
+ if (p_ptr->redraw & (PR_STATS))
+ {
+ p_ptr->redraw &= ~(PR_STATS);
+ prt_stat(A_STR);
+ prt_stat(A_INT);
+ prt_stat(A_WIS);
+ prt_stat(A_DEX);
+ prt_stat(A_CON);
+ prt_stat(A_CHR);
+ }
+
+ if (p_ptr->redraw & (PR_ARMOR))
+ {
+ p_ptr->redraw &= ~(PR_ARMOR);
+ prt_ac();
+ }
+
+ if (p_ptr->redraw & (PR_HP))
+ {
+ p_ptr->redraw &= ~(PR_HP);
+ prt_hp();
+ }
+
+ if (p_ptr->redraw & (PR_MANA))
+ {
+ p_ptr->redraw &= ~(PR_MANA);
+ prt_sp();
+ }
+
+ if (p_ptr->redraw & (PR_PIETY))
+ {
+ p_ptr->redraw &= ~(PR_PIETY);
+ prt_piety();
+ }
+
+ if (p_ptr->redraw & (PR_MH))
+ {
+ p_ptr->redraw &= ~(PR_MH);
+ prt_mh();
+ }
+
+ if (p_ptr->redraw & (PR_GOLD))
+ {
+ p_ptr->redraw &= ~(PR_GOLD);
+ prt_gold();
+ }
+
+ if (p_ptr->redraw & (PR_DEPTH))
+ {
+ p_ptr->redraw &= ~(PR_DEPTH);
+ prt_depth();
+ }
+
+ if (p_ptr->redraw & (PR_HEALTH))
+ {
+ p_ptr->redraw &= ~(PR_HEALTH);
+ health_redraw();
+ }
+
+
+ if (p_ptr->redraw & (PR_EXTRA))
+ {
+ p_ptr->redraw &= ~(PR_EXTRA);
+ p_ptr->redraw &= ~(PR_CUT | PR_STUN);
+ p_ptr->redraw &= ~(PR_HUNGER);
+ p_ptr->redraw &= ~(PR_BLIND | PR_CONFUSED);
+ p_ptr->redraw &= ~(PR_AFRAID | PR_POISONED);
+ p_ptr->redraw &= ~(PR_STATE | PR_SPEED | PR_STUDY | PR_SANITY);
+ prt_frame_extra();
+ }
+
+ if (p_ptr->redraw & (PR_CUT))
+ {
+ p_ptr->redraw &= ~(PR_CUT);
+ prt_cut();
+ }
+
+ if (p_ptr->redraw & (PR_STUN))
+ {
+ p_ptr->redraw &= ~(PR_STUN);
+ prt_stun();
+ }
+
+ if (p_ptr->redraw & (PR_HUNGER))
+ {
+ p_ptr->redraw &= ~(PR_HUNGER);
+ prt_hunger();
+ }
+
+ if (p_ptr->redraw & (PR_BLIND))
+ {
+ p_ptr->redraw &= ~(PR_BLIND);
+ prt_blind();
+ }
+
+ if (p_ptr->redraw & (PR_CONFUSED))
+ {
+ p_ptr->redraw &= ~(PR_CONFUSED);
+ prt_confused();
+ }
+
+ if (p_ptr->redraw & (PR_AFRAID))
+ {
+ p_ptr->redraw &= ~(PR_AFRAID);
+ prt_afraid();
+ }
+
+ if (p_ptr->redraw & (PR_POISONED))
+ {
+ p_ptr->redraw &= ~(PR_POISONED);
+ prt_poisoned();
+ }
+
+ if (p_ptr->redraw & (PR_DTRAP))
+ {
+ p_ptr->redraw &= ~(PR_DTRAP);
+ prt_dtrap();
+ }
+
+ if (p_ptr->redraw & (PR_STATE))
+ {
+ p_ptr->redraw &= ~(PR_STATE);
+ prt_state();
+ }
+
+ if (p_ptr->redraw & (PR_SPEED))
+ {
+ p_ptr->redraw &= ~(PR_SPEED);
+ prt_speed();
+ }
+
+ if (p_ptr->redraw & (PR_STUDY))
+ {
+ p_ptr->redraw &= ~(PR_STUDY);
+ prt_study();
+ }
+
+ if (p_ptr->redraw & (PR_SANITY))
+ {
+ p_ptr->redraw &= ~(PR_SANITY);
+ prt_sane();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->window"
+ */
+void window_stuff(void)
+{
+ int j;
+
+ u32b mask = 0L;
+
+
+ /* Nothing to do */
+ if (!p_ptr->window) return;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Save usable flags */
+ if (angband_term[j]) mask |= window_flag[j];
+ }
+
+ /* Apply usable flags */
+ p_ptr->window &= mask;
+
+ /* Nothing to do */
+ if (!p_ptr->window) return;
+
+
+ /* Display p_ptr->inventory */
+ if (p_ptr->window & (PW_INVEN))
+ {
+ p_ptr->window &= ~(PW_INVEN);
+ fix_inven();
+ }
+
+ /* Display equipment */
+ if (p_ptr->window & (PW_EQUIP))
+ {
+ p_ptr->window &= ~(PW_EQUIP);
+ fix_equip();
+ }
+
+ /* Display player */
+ if (p_ptr->window & (PW_PLAYER))
+ {
+ p_ptr->window &= ~(PW_PLAYER);
+ fix_player();
+ }
+
+ /* Display monster list */
+ if (p_ptr->window & (PW_M_LIST))
+ {
+ p_ptr->window &= ~(PW_M_LIST);
+ fix_m_list();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_MESSAGE))
+ {
+ p_ptr->window &= ~(PW_MESSAGE);
+ fix_message();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_IRC))
+ {
+ p_ptr->window &= ~(PW_IRC);
+ fix_irc_message();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_OVERHEAD))
+ {
+ p_ptr->window &= ~(PW_OVERHEAD);
+ fix_overhead();
+ }
+
+ /* Display monster recall */
+ if (p_ptr->window & (PW_MONSTER))
+ {
+ p_ptr->window &= ~(PW_MONSTER);
+ fix_monster();
+ }
+
+ /* Display object recall */
+ if (p_ptr->window & (PW_OBJECT))
+ {
+ p_ptr->window &= ~(PW_OBJECT);
+ fix_object();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->update" and "p_ptr->redraw" and "p_ptr->window"
+ */
+void handle_stuff(void)
+{
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+}
+
+
+bool monk_empty_hands(void)
+{
+ int i;
+ object_type *o_ptr;
+
+ if (p_ptr->melee_style != SKILL_HAND) return FALSE;
+
+ i = 0;
+ while (p_ptr->body_parts[i] == INVEN_WIELD)
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ if (o_ptr->k_idx) return FALSE;
+
+ i++;
+ }
+
+ return TRUE;
+}
+
+bool monk_heavy_armor(void)
+{
+ u16b monk_arm_wgt = 0;
+
+ if (p_ptr->melee_style != SKILL_HAND) return FALSE;
+
+ /* Weight the armor */
+ monk_arm_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ return (monk_arm_wgt > (100 + (get_skill(SKILL_HAND) * 4))) ;
+}
+
+static int get_artifact_idx(int level)
+{
+ int count = 0, i;
+ bool OK = FALSE;
+
+ while (count < 1000)
+ {
+ artifact_type *a_ptr;
+
+ count++;
+ i = randint(max_a_idx - 1);
+ a_ptr = &a_info[i];
+ if (!a_ptr->tval) continue;
+
+ /* It is found/lost */
+ if (a_ptr->cur_num) continue;
+
+ /* OoD */
+ if (a_ptr->level > level) continue;
+
+ /* Avoid granting SPECIAL_GENE artifacts */
+ if (a_ptr->flags4 & TR4_SPECIAL_GENE) continue;
+
+ OK = TRUE;
+ break;
+ }
+
+ /* No matches found */
+ if (OK == FALSE)
+ {
+#if 0 /* pelpel */
+ /* XXX XXX XXX Grant the Phial */
+ i = 1;
+#endif /* pelpel */
+
+ /* Grant a randart */
+ i = 0;
+ }
+
+ return i;
+}
+
+/* Chose a fate */
+void gain_fate(byte fate)
+{
+ int i;
+ int level;
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if (!fates[i].fate)
+ {
+ fates[i].level = 0;
+
+ cmsg_print(TERM_VIOLET, "More of your prophecy has been unearthed!");
+ cmsg_print(TERM_VIOLET, "You should see a soothsayer quickly.");
+
+ if (fate)
+ fates[i].fate = fate;
+ else
+ /* If lucky (current luck > 0) avoid death fate */
+ switch (rand_int(p_ptr->luck_cur > 0 ? 17 : 18))
+ {
+ case 6:
+ case 2:
+ case 3:
+ case 7:
+ case 8:
+ case 9:
+ case 13:
+ fates[i].fate = FATE_FIND_O;
+ break;
+ case 1:
+ case 4:
+ case 5:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ fates[i].fate = FATE_FIND_R;
+ break;
+ case 15:
+ case 16:
+ fates[i].fate = FATE_FIND_A;
+ break;
+ case 17:
+ fates[i].fate = FATE_DIE;
+ break;
+ case 0:
+ {
+ /* The deepest the better */
+ int chance = dun_level / 4;
+
+ /* No more than 1/2 chances */
+ if (chance > 50) chance = 50;
+
+ /* It's HARD to get now */
+ if (magik(chance))
+ {
+ fates[i].fate = FATE_NO_DIE_MORTAL;
+ }
+ else
+ {
+ fates[i].fate = FATE_FIND_O;
+ }
+ break;
+ }
+ }
+
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_O:
+ {
+ while (TRUE)
+ {
+ object_kind *k_ptr;
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ fates[i].o_idx = get_obj_num(max_dlv[dungeon_type] + randint(10));
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ k_ptr = &k_info[fates[i].o_idx];
+
+ if (!(k_ptr->flags3 & TR3_INSTA_ART) && !(k_ptr->flags3 & TR3_NORM_ART)) break;
+ }
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = rand_int(2);
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Find object %d on level %d", fates[i].o_idx, fates[i].level);
+ break;
+ }
+ case FATE_FIND_R:
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ fates[i].r_idx = get_mon_num(max_dlv[dungeon_type] + randint(10));
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = rand_int(2);
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Meet monster %d on level %d", fates[i].r_idx, fates[i].level);
+ break;
+
+ case FATE_FIND_A:
+ fates[i].a_idx = get_artifact_idx(max_dlv[dungeon_type] + randint(10));
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = TRUE;
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Find artifact %d on level %d", fates[i].a_idx, fates[i].level);
+ break;
+
+ case FATE_DIE:
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = TRUE;
+ fates[i].know = FALSE;
+ if ((wizard) || (p_ptr->precognition)) msg_format("New fate : Death on level %d", fates[i].level);
+ break;
+
+ case FATE_NO_DIE_MORTAL:
+ fates[i].serious = TRUE;
+ p_ptr->no_mortal = TRUE;
+ if ((wizard) || (p_ptr->precognition)) msg_format("New fate : Never to die by the hand of a mortal being.");
+ break;
+ }
+
+ break;
+ }
+ }
+}
+
+void fate_desc(char *desc, int fate)
+{
+ char buf[120];
+
+ if (fates[fate].serious)
+ {
+ strcpy(desc, "You are fated to ");
+ }
+ else
+ {
+ strcpy(desc, "You may ");
+ }
+ switch (fates[fate].fate)
+ {
+ case FATE_FIND_O:
+ {
+ object_type *o_ptr, forge;
+ char o_name[80];
+
+ o_ptr = &forge;
+ object_prep(o_ptr, fates[fate].o_idx);
+ object_desc_store(o_name, o_ptr, 1, 0);
+
+ sprintf(buf, "find %s on level %d.", o_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_FIND_A:
+ {
+ object_type *q_ptr, forge;
+ char o_name[80];
+ artifact_type *a_ptr = &a_info[fates[fate].a_idx];
+ int I_kind;
+
+ /* Failed artefact allocation XXX XXX XXX */
+ if (fates[fate].a_idx == 0)
+ {
+ strcpy(o_name, "something special");
+ }
+
+ /* Legal artefacts */
+ else
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = fates[fate].a_idx;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ random_artifact_resistance(q_ptr);
+
+ object_desc_store(o_name, q_ptr, 1, 0);
+ }
+
+ sprintf(buf, "find %s on level %d.", o_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ char m_name[80];
+
+ monster_race_desc(m_name, fates[fate].r_idx, 0);
+ sprintf(buf, "meet %s on level %d.", m_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_DIE:
+ {
+ sprintf(buf, "die on level %d.", fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_NO_DIE_MORTAL:
+ {
+ strcat(desc, "never to die by the hand of a mortal being.");
+ break;
+ }
+ }
+}
+
+void dump_fates(FILE *outfile)
+{
+ int i;
+ char buf[120];
+
+ if (!outfile) return;
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if ((fates[i].fate) && (fates[i].know))
+ {
+ fate_desc(buf, i);
+ fprintf(outfile, "%s\n", buf);
+ }
+ }
+}
+
+/*
+ * Return a luck number between a certain range
+ */
+int luck(int min, int max)
+{
+ int luck = p_ptr->luck_cur;
+ int range = max - min;
+
+ if (luck < -30) luck = -30;
+ if (luck > 30) luck = 30;
+ luck += 30;
+
+ luck *= range;
+ luck /= 60;
+
+ return (luck + min);
+}
diff --git a/src/xtra2.c b/src/xtra2.c
new file mode 100644
index 00000000..a3d8471c
--- /dev/null
+++ b/src/xtra2.c
@@ -0,0 +1,7795 @@
+/* File: xtra2.c */
+/* File: xtra2.c */
+
+/* Purpose: effects of various "objects", targetting and panel handling */
+
+/*
+ * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+#include "angband.h"
+
+/*
+ * Invoke The Rush
+ */
+bool set_rush(int v)
+{
+ int j;
+
+ /* Invoke The Bust */
+ if (!v)
+ {
+ p_ptr->rush = 0;
+
+ j = 50 - randint(p_ptr->lev);
+ set_paralyzed(j);
+ set_slow(j + 50 - randint(p_ptr->lev));
+ return TRUE;
+ }
+
+ /* When is The Bust going to happen? */
+ p_ptr->rush = v;
+
+ /* The bonuses of The Rush */
+ set_hero(p_ptr->hero + v);
+ set_tim_deadly(p_ptr->tim_deadly + v);
+ set_strike(p_ptr->strike + v);
+ if (magik(p_ptr->lev / 2))
+ {
+ set_light_speed(p_ptr->lightspeed + v);
+ }
+ else
+ {
+ set_fast(p_ptr->fast + v, 10);
+ }
+ if (magik(p_ptr->lev / 2)) set_tim_esp(p_ptr->tim_esp + v);
+ return TRUE;
+}
+
+
+/*
+ * Set "p_ptr->parasite" and "p_ptr->parasite_r_idx"
+ * notice observable changes
+ */
+bool set_parasite(int v, int r)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->parasite)
+ {
+ msg_print("You feel something growing in you.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->parasite)
+ {
+ if (magik(80))
+ {
+ char r_name[80];
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, p_ptr->parasite_r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 10, 0);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_one(wy, wx, p_ptr->parasite_r_idx, 0, FALSE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "Your body convulses and spawns %s.", r_name);
+ p_ptr->food -= 750;
+ if (p_ptr->food < 100) p_ptr->food = 100;
+ }
+ }
+ else
+ {
+ cmsg_print(TERM_L_BLUE, "The hideous thing growing in you seems to die.");
+ }
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->parasite = v;
+ p_ptr->parasite_r_idx = r;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_project" and others
+ * notice observable changes
+ */
+bool set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_project)
+ {
+ msg_print("Your weapon starts glowing.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_project)
+ {
+ msg_print("Your weapon stops glowing.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_project = v;
+ p_ptr->tim_project_gf = gf;
+ p_ptr->tim_project_dam = dam;
+ p_ptr->tim_project_rad = rad;
+ p_ptr->tim_project_flag = flag;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_roots" and others
+ * notice observable changes
+ */
+bool set_roots(int v, s16b ac, s16b dam)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_roots)
+ {
+ msg_print("Roots dive into the floor from your feet.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_roots)
+ {
+ msg_print("The roots of your feet suddenly vanish.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_roots = v;
+ p_ptr->tim_roots_dam = dam;
+ p_ptr->tim_roots_ac = ac;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_(magic|water)_breath" and others
+ * notice observable changes
+ */
+bool set_tim_breath(int v, bool magical)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (magical)
+ {
+ if (!p_ptr->tim_magic_breath)
+ {
+ msg_print("Air seems to fill your lungs without breathing.");
+ notice = TRUE;
+ }
+ }
+ else
+ {
+ if (!p_ptr->tim_water_breath)
+ {
+ msg_print("Water seems to fill your lungs.");
+ notice = TRUE;
+ }
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (magical)
+ {
+ if (p_ptr->tim_magic_breath)
+ {
+ msg_print("You need to breathe again.");
+ notice = TRUE;
+ }
+ }
+ else
+ {
+ if (p_ptr->tim_water_breath)
+ {
+ msg_print("The water filling your lungs evaporates.");
+ notice = TRUE;
+ }
+ }
+ }
+
+ /* Use the value */
+ if (magical)
+ p_ptr->tim_magic_breath = v;
+ else
+ p_ptr->tim_water_breath = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->absorb_soul"
+ * notice observable changes
+ */
+bool set_absorb_soul(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->absorb_soul)
+ {
+ cmsg_print(TERM_L_DARK, "You start absorbing the souls of your foes.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->absorb_soul)
+ {
+ cmsg_print(TERM_L_DARK, "You stop absorbing the souls of dead foes.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->absorb_soul = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->disrupt_shield"
+ * notice observable changes
+ */
+bool set_disrupt_shield(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->disrupt_shield)
+ {
+ cmsg_print(TERM_L_BLUE, "You feel invulnerable.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->disrupt_shield)
+ {
+ cmsg_print(TERM_L_RED, "You are more vulnerable.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->disrupt_shield = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->prob_travel"
+ * notice observable changes
+ */
+bool set_prob_travel(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->prob_travel)
+ {
+ msg_print("You feel instable.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->prob_travel)
+ {
+ msg_print("You are more stable.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->prob_travel = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_invis", and "p_ptr->tim_inv_pow",
+ * notice observable changes
+ */
+bool set_invis(int v, int p)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_invisible)
+ {
+ msg_print("You feel your body fade away.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_invisible)
+ {
+ msg_print("You are no longer invisible.");
+ notice = TRUE;
+ p = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_invisible = v;
+ p_ptr->tim_inv_pow = p;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_poison",
+ * notice observable changes
+ */
+bool set_poison(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_poison)
+ {
+ msg_print("Your hands are dripping with venom.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_poison)
+ {
+ msg_print("The venom source dries out.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_poison = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "no_breeds"
+ */
+bool set_no_breeders(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!no_breeds)
+ {
+ msg_print("You feel an anti-sexual aura.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (no_breeds)
+ {
+ msg_print("You no longer feel an anti-sexual aura.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ no_breeds = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_deadly"
+ */
+bool set_tim_deadly(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_deadly)
+ {
+ msg_print("You feel extremely accurate.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_deadly)
+ {
+ msg_print("You are suddenly much less accurate.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_deadly = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_ffall"
+ */
+bool set_tim_ffall(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_ffall)
+ {
+ msg_print("You feel very light.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_ffall)
+ {
+ msg_print("You are suddenly heavier.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_ffall = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_fly"
+ */
+bool set_tim_fly(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_fly)
+ {
+ msg_print("You feel able to reach the clouds.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_fly)
+ {
+ msg_print("You are suddenly a lot heavier.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_fly = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->meditation"
+ */
+bool set_meditation(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->meditation)
+ {
+ msg_print("You start meditating on yourself...");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->meditation)
+ {
+ msg_print("You stop your self meditation.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->meditation = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->update |= (PU_MANA);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_reflect"
+ */
+bool set_tim_reflect(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_reflect)
+ {
+ msg_print("You start reflecting the world around you.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_reflect)
+ {
+ msg_print("You stop reflecting.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_reflect = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_res_time"
+ */
+bool set_tim_res_time(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_res_time)
+ {
+ msg_print("You are now protected against space-time distortions.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_res_time)
+ {
+ msg_print("You are no longer protected against space-time distortions.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_res_time = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_fire_aura"
+ */
+bool set_tim_fire_aura(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_fire_aura)
+ {
+ msg_print("You are enveloped in flames.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_fire_aura)
+ {
+ msg_print("You are no longer enveloped in flames.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_fire_aura = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->strike"
+ */
+bool set_strike(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->strike)
+ {
+ msg_print("You feel very accurate.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->strike)
+ {
+ msg_print("You are no longer very accurate.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->strike = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->oppose_ld"
+ */
+bool set_oppose_ld(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_ld)
+ {
+ msg_print("You feel protected against light's fluctuation.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_ld)
+ {
+ msg_print("You are no longer protected against light's fluctuation.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_ld = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->oppose_cc"
+ */
+bool set_oppose_cc(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_cc)
+ {
+ msg_print("You feel protected against raw chaos.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_cc)
+ {
+ msg_print("You are no longer protected against chaos.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_cc = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->oppose_ss"
+ */
+bool set_oppose_ss(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_ss)
+ {
+ msg_print("You feel protected against the ravages of sound and shards.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_ss)
+ {
+ msg_print("You are no longer protected against the ravages of sound and shards.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_ss = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->oppose_nex"
+ */
+bool set_oppose_nex(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_nex)
+ {
+ msg_print("You feel protected against the strange forces of nexus.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_nex)
+ {
+ msg_print("You are no longer protected against the strange forces of nexus.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_nex = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_mimic", and "p_ptr->mimic_form",
+ * notice observable changes
+ */
+bool set_mimic(int v, int p, int level)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_mimic)
+ {
+ msg_print("You feel your body change.");
+ p_ptr->mimic_form = p;
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_mimic)
+ {
+ msg_print("You are no longer transformed.");
+ p_ptr->mimic_form = 0;
+ notice = TRUE;
+ if (p == resolve_mimic_name("Bear"))
+ {
+ s_info[SKILL_BEAR].hidden = TRUE;
+ select_default_melee();
+ }
+ p = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_mimic = v;
+ p_ptr->mimic_level = level;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BODY | PU_BONUS | PU_SANITY);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->blind", notice observable changes
+ *
+ * Note the use of "PU_UN_VIEW", which is needed to memorize any terrain
+ * features which suddenly become "visible".
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ */
+bool set_blind(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->blind)
+ {
+ msg_print("You are blind!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->blind)
+ {
+ msg_print("You can see again.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->blind = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Redraw the "blind" */
+ p_ptr->redraw |= (PR_BLIND);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_lite", notice observable changes
+ *
+ * Note the use of "PU_VIEW", which is needed to
+ * memorize any terrain features which suddenly become "visible".
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ */
+bool set_lite(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_lite)
+ {
+ msg_print("You suddenly seem brighter!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_lite)
+ {
+ msg_print("You are no longer bright.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_lite = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->confused", notice observable changes
+ */
+bool set_confused(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->confused)
+ {
+ msg_print("You are confused!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->confused)
+ {
+ msg_print("You feel less confused now.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->confused = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw the "confused" */
+ p_ptr->redraw |= (PR_CONFUSED);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->poisoned", notice observable changes
+ */
+bool set_poisoned(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->poisoned)
+ {
+ msg_print("You are poisoned!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->poisoned)
+ {
+ msg_print("You are no longer poisoned.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->poisoned = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw the "poisoned" */
+ p_ptr->redraw |= (PR_POISONED);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->afraid", notice observable changes
+ */
+bool set_afraid(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->afraid)
+ {
+ msg_print("You are terrified!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->afraid)
+ {
+ msg_print("You feel bolder now.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->afraid = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw the "afraid" */
+ p_ptr->redraw |= (PR_AFRAID);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->paralyzed", notice observable changes
+ */
+bool set_paralyzed(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->paralyzed)
+ {
+ msg_print("You are paralyzed!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->paralyzed)
+ {
+ msg_print("You can move again.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->paralyzed = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_STATE);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->image", notice observable changes
+ *
+ * Note that we must redraw the map when hallucination changes.
+ */
+bool set_image(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->image)
+ {
+ msg_print("Oh, wow! Everything looks so cosmic now!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->image)
+ {
+ msg_print("You can see clearly again.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->image = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD | PW_M_LIST);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->lightspeed", notice observable changes
+ */
+bool set_light_speed(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->lightspeed)
+ {
+ msg_print("You feel as if time has stopped!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->lightspeed)
+ {
+ msg_print("You feel time returning to its normal rate.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->lightspeed = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+bool set_fast(int v, int p)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->fast)
+ {
+ msg_print("You feel yourself moving faster!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->fast)
+ {
+ msg_print("You feel yourself slow down.");
+ p = 0;
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->fast = v;
+ p_ptr->speed_factor = p;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->slow", notice observable changes
+ */
+bool set_slow(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->slow)
+ {
+ msg_print("You feel yourself moving slower!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->slow)
+ {
+ msg_print("You feel yourself speed up.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->slow = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->shield", notice observable changes
+ */
+bool set_shield(int v, int p, s16b o, s16b d1, s16b d2)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->shield)
+ {
+ msg_print("A mystic shield forms around your body!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->shield)
+ {
+ msg_print("Your mystic shield crumbles away.");
+ notice = TRUE;
+ p = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->shield = v;
+ p_ptr->shield_power = p;
+ p_ptr->shield_opt = o;
+ p_ptr->shield_power_opt = d1;
+ p_ptr->shield_power_opt2 = d2;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+
+/*
+ * Set "p_ptr->blessed", notice observable changes
+ */
+bool set_blessed(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->blessed)
+ {
+ msg_print("You feel righteous!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->blessed)
+ {
+ msg_print("The prayer has expired.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->blessed = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->hero", notice observable changes
+ */
+bool set_hero(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->hero)
+ {
+ msg_print("You feel like a hero!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->hero)
+ {
+ msg_print("The heroism wears off.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->hero = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate hitpoints */
+ p_ptr->update |= (PU_HP);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->holy", notice observable changes
+ */
+bool set_holy(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->holy)
+ {
+ msg_print("You feel a holy aura around you!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->holy)
+ {
+ msg_print("The holy aura vanishes.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->holy = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->walk_water", notice observable changes
+ */
+bool set_walk_water(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->walk_water)
+ {
+ msg_print("You feel strangely buoyant!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->walk_water)
+ {
+ msg_print("You feel much less buoyant.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->walk_water = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->shero", notice observable changes
+ */
+bool set_shero(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->shero)
+ {
+ msg_print("You feel like a killing machine!");
+ notice = TRUE;
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->shero)
+ {
+ msg_print("You feel less berserk.");
+ notice = TRUE;
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /* Use the value */
+ p_ptr->shero = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate hitpoints */
+ p_ptr->update |= (PU_HP);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->protevil", notice observable changes
+ */
+bool set_protevil(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->protevil)
+ {
+ msg_print("You feel safe from evil!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->protevil)
+ {
+ msg_print("You no longer feel safe from evil.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->protevil = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->protgood", notice observable changes
+ */
+bool set_protgood(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->protgood)
+ {
+ msg_print("You feel safe from good!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->protgood)
+ {
+ msg_print("You no longer feel safe from good.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->protgood = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->protundead", notice observable changes
+ */
+bool set_protundead(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->protundead)
+ {
+ msg_print("You feel safe from undead!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->protundead)
+ {
+ msg_print("You no longer feel safe from undead.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->protundead = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->set_shadow", notice observable changes
+ */
+bool set_shadow(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_wraith)
+ {
+
+ msg_print("You leave the physical world and turn into a wraith-being!");
+ notice = TRUE;
+
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_wraith)
+ {
+ msg_print("You feel opaque.");
+ notice = TRUE;
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_wraith = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+
+
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+
+}
+
+
+
+
+/*
+ * Set "p_ptr->invuln", notice observable changes
+ */
+bool set_invuln(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->invuln)
+ {
+
+ cmsg_print(TERM_L_BLUE, "Invulnerability!");
+ notice = TRUE;
+
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->invuln)
+ {
+ cmsg_print(TERM_L_RED, "The invulnerability wears off.");
+ notice = TRUE;
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+ }
+
+ /* Use the value */
+ p_ptr->invuln = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+
+
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+
+}
+
+
+
+/*
+ * Set "p_ptr->tim_esp", notice observable changes
+ */
+bool set_tim_esp(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_esp)
+ {
+ msg_print("You feel your consciousness expand!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_esp)
+ {
+ msg_print("Your consciousness contracts again.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_esp = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_thunder", notice observable changes
+ */
+bool set_tim_thunder(int v, int p1, int p2)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_thunder)
+ {
+ msg_print("The air around you charges with lightning!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_thunder)
+ {
+ msg_print("The air around you discharges.");
+ notice = TRUE;
+ p1 = p2 = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_thunder = v;
+ p_ptr->tim_thunder_p1 = p1;
+ p_ptr->tim_thunder_p2 = p2;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_invis", notice observable changes
+ */
+bool set_tim_invis(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_invis)
+ {
+ msg_print("Your eyes feel very sensitive!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_invis)
+ {
+ msg_print("Your eyes feel less sensitive.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_invis = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->tim_infra", notice observable changes
+ */
+bool set_tim_infra(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_infra)
+ {
+ msg_print("Your eyes begin to tingle!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_infra)
+ {
+ msg_print("Your eyes stop tingling.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_infra = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->tim_mental_barrier", notice observable changes
+ */
+bool set_mental_barrier(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_mental_barrier)
+ {
+ msg_print("Your mind grows stronger!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_mental_barrier)
+ {
+ msg_print("Your mind is no longer especially strong.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_mental_barrier = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->oppose_acid", notice observable changes
+ */
+bool set_oppose_acid(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_acid)
+ {
+ msg_print("You feel resistant to acid!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_acid)
+ {
+ msg_print("You feel less resistant to acid.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_acid = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->oppose_elec", notice observable changes
+ */
+bool set_oppose_elec(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_elec)
+ {
+ msg_print("You feel resistant to electricity!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_elec)
+ {
+ msg_print("You feel less resistant to electricity.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_elec = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->oppose_fire", notice observable changes
+ */
+bool set_oppose_fire(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_fire)
+ {
+ msg_print("You feel resistant to fire!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_fire)
+ {
+ msg_print("You feel less resistant to fire.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_fire = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->oppose_cold", notice observable changes
+ */
+bool set_oppose_cold(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_cold)
+ {
+ msg_print("You feel resistant to cold!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_cold)
+ {
+ msg_print("You feel less resistant to cold.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_cold = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->oppose_pois", notice observable changes
+ */
+bool set_oppose_pois(int v)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->oppose_pois)
+ {
+ msg_print("You feel resistant to poison!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->oppose_pois)
+ {
+ msg_print("You feel less resistant to poison.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->oppose_pois = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->tim_regen", notice observable changes
+ */
+bool set_tim_regen(int v, int p)
+{
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_regen)
+ {
+ msg_print("Your body regenerates much more quickly!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_regen)
+ {
+ p = 0;
+ msg_print("Your body regenerates much more slowly.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_regen = v;
+ p_ptr->tim_regen_pow = p;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->stun", notice observable changes
+ *
+ * Note the special code to only notice "range" changes.
+ */
+bool set_stun(int v)
+{
+ int old_aux, new_aux;
+ bool notice = FALSE;
+
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ if (PRACE_FLAG(PR1_NO_STUN)) v = 0;
+
+ /* Knocked out */
+ if (p_ptr->stun > 100)
+ {
+ old_aux = 3;
+ }
+
+ /* Heavy stun */
+ else if (p_ptr->stun > 50)
+ {
+ old_aux = 2;
+ }
+
+ /* Stun */
+ else if (p_ptr->stun > 0)
+ {
+ old_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ old_aux = 0;
+ }
+
+ /* Knocked out */
+ if (v > 100)
+ {
+ new_aux = 3;
+ }
+
+ /* Heavy stun */
+ else if (v > 50)
+ {
+ new_aux = 2;
+ }
+
+ /* Stun */
+ else if (v > 0)
+ {
+ new_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ new_aux = 0;
+ }
+
+ /* Increase cut */
+ if (new_aux > old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Stun */
+ case 1:
+ msg_print("You have been stunned.");
+ break;
+
+ /* Heavy stun */
+ case 2:
+ msg_print("You have been heavily stunned.");
+ break;
+
+ /* Knocked out */
+ case 3:
+ msg_print("You have been knocked out.");
+ break;
+ }
+
+ if (randint(1000) < v || randint(16) == 1)
+ {
+
+ msg_print("A vicious blow hits your head.");
+ if (randint(3) == 1)
+ {
+ if (!p_ptr->sustain_int)
+ {
+ (void) do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ }
+ if (!p_ptr->sustain_wis)
+ {
+ (void) do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ }
+ }
+ else if (randint(2) == 1)
+ {
+ if (!p_ptr->sustain_int)
+ {
+ (void) do_dec_stat(A_INT, STAT_DEC_NORMAL);
+ }
+ }
+ else
+ {
+ if (!p_ptr->sustain_wis)
+ {
+ (void) do_dec_stat(A_WIS, STAT_DEC_NORMAL);
+ }
+ }
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Decrease cut */
+ else if (new_aux < old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* None */
+ case 0:
+ msg_print("You are no longer stunned.");
+ if (disturb_state) disturb(0, 0);
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->stun = v;
+
+ /* No change */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the "stun" */
+ p_ptr->redraw |= (PR_STUN);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->cut", notice observable changes
+ *
+ * Note the special code to only notice "range" changes.
+ */
+bool set_cut(int v)
+{
+ int old_aux, new_aux;
+
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ if (PRACE_FLAG(PR1_NO_CUT)) v = 0;
+
+ /* Mortal wound */
+ if (p_ptr->cut > 1000)
+ {
+ old_aux = 7;
+ }
+
+ /* Deep gash */
+ else if (p_ptr->cut > 200)
+ {
+ old_aux = 6;
+ }
+
+ /* Severe cut */
+ else if (p_ptr->cut > 100)
+ {
+ old_aux = 5;
+ }
+
+ /* Nasty cut */
+ else if (p_ptr->cut > 50)
+ {
+ old_aux = 4;
+ }
+
+ /* Bad cut */
+ else if (p_ptr->cut > 25)
+ {
+ old_aux = 3;
+ }
+
+ /* Light cut */
+ else if (p_ptr->cut > 10)
+ {
+ old_aux = 2;
+ }
+
+ /* Graze */
+ else if (p_ptr->cut > 0)
+ {
+ old_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ old_aux = 0;
+ }
+
+ /* Mortal wound */
+ if (v > 1000)
+ {
+ new_aux = 7;
+ }
+
+ /* Deep gash */
+ else if (v > 200)
+ {
+ new_aux = 6;
+ }
+
+ /* Severe cut */
+ else if (v > 100)
+ {
+ new_aux = 5;
+ }
+
+ /* Nasty cut */
+ else if (v > 50)
+ {
+ new_aux = 4;
+ }
+
+ /* Bad cut */
+ else if (v > 25)
+ {
+ new_aux = 3;
+ }
+
+ /* Light cut */
+ else if (v > 10)
+ {
+ new_aux = 2;
+ }
+
+ /* Graze */
+ else if (v > 0)
+ {
+ new_aux = 1;
+ }
+
+ /* None */
+ else
+ {
+ new_aux = 0;
+ }
+
+ /* Increase cut */
+ if (new_aux > old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Graze */
+ case 1:
+ msg_print("You have been given a graze.");
+ break;
+
+ /* Light cut */
+ case 2:
+ msg_print("You have been given a light cut.");
+ break;
+
+ /* Bad cut */
+ case 3:
+ msg_print("You have been given a bad cut.");
+ break;
+
+ /* Nasty cut */
+ case 4:
+ msg_print("You have been given a nasty cut.");
+ break;
+
+ /* Severe cut */
+ case 5:
+ msg_print("You have been given a severe cut.");
+ break;
+
+ /* Deep gash */
+ case 6:
+ msg_print("You have been given a deep gash.");
+ break;
+
+ /* Mortal wound */
+ case 7:
+ msg_print("You have been given a mortal wound.");
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+
+ if (randint(1000) < v || randint(16) == 1)
+ {
+ if (!p_ptr->sustain_chr)
+ {
+ msg_print("You have been horribly scarred.");
+
+ do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+ }
+ }
+ }
+
+ /* Decrease cut */
+ else if (new_aux < old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* None */
+ case 0:
+ msg_print("You are no longer bleeding.");
+ if (disturb_state) disturb(0, 0);
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->cut = v;
+
+ /* No change */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the "cut" */
+ p_ptr->redraw |= (PR_CUT);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+void drop_from_wild()
+{
+ /* Hack -- Not if player were in normal mode in previous turn */
+ if (!p_ptr->old_wild_mode) return;
+
+ if (p_ptr->wild_mode && (!dun_level))
+ {
+ p_ptr->wilderness_x = p_ptr->px;
+ p_ptr->wilderness_y = p_ptr->py;
+ change_wild_mode();
+ p_ptr->energy = 100;
+ energy_use = 0;
+ }
+}
+
+/*
+ * Set "p_ptr->food", notice observable changes
+ *
+ * The "p_ptr->food" variable can get as large as 20000, allowing the
+ * addition of the most "filling" item, Elvish Waybread, which adds
+ * 7500 food units, without overflowing the 32767 maximum limit.
+ *
+ * Perhaps we should disturb the player with various messages,
+ * especially messages about hunger status changes. XXX XXX XXX
+ *
+ * Digestion of food is handled in "dungeon.c", in which, normally,
+ * the player digests about 20 food units per 100 game turns, more
+ * when "fast", more when "regenerating", less with "slow digestion",
+ * but when the player is "gorged", he digests 100 food units per 10
+ * game turns, or a full 1000 food units per 100 game turns.
+ *
+ * Note that the player's speed is reduced by 10 units while gorged,
+ * so if the player eats a single food ration (5000 food units) when
+ * full (15000 food units), he will be gorged for (5000/100)*10 = 500
+ * game turns, or 500/(100/5) = 25 player turns (if nothing else is
+ * affecting the player speed).
+ */
+bool set_food(int v)
+{
+ int old_aux, new_aux;
+
+ bool notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 20000) ? 20000 : (v < 0) ? 0 : v;
+
+ /* Fainting / Starving */
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ old_aux = 0;
+ }
+
+ /* Weak */
+ else if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ old_aux = 1;
+ }
+
+ /* Hungry */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ old_aux = 2;
+ }
+
+ /* Normal */
+ else if (p_ptr->food < PY_FOOD_FULL)
+ {
+ old_aux = 3;
+ }
+
+ /* Full */
+ else if (p_ptr->food < PY_FOOD_MAX)
+ {
+ old_aux = 4;
+ }
+
+ /* Gorged */
+ else
+ {
+ old_aux = 5;
+ }
+
+ /* Fainting / Starving */
+ if (v < PY_FOOD_FAINT)
+ {
+ new_aux = 0;
+ }
+
+ /* Weak */
+ else if (v < PY_FOOD_WEAK)
+ {
+ new_aux = 1;
+ }
+
+ /* Hungry */
+ else if (v < PY_FOOD_ALERT)
+ {
+ new_aux = 2;
+ }
+
+ /* Normal */
+ else if (v < PY_FOOD_FULL)
+ {
+ new_aux = 3;
+ }
+
+ /* Full */
+ else if (v < PY_FOOD_MAX)
+ {
+ new_aux = 4;
+ }
+
+ /* Gorged */
+ else
+ {
+ new_aux = 5;
+ }
+
+ /* Food increase */
+ if (new_aux > old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Weak */
+ case 1:
+ msg_print("You are still weak.");
+ break;
+
+ /* Hungry */
+ case 2:
+ msg_print("You are still hungry.");
+ break;
+
+ /* Normal */
+ case 3:
+ msg_print("You are no longer hungry.");
+ break;
+
+ /* Full */
+ case 4:
+ msg_print("You are full!");
+ break;
+
+ /* Bloated */
+ case 5:
+ msg_print("You have gorged yourself!");
+ break;
+ }
+
+ /* Change */
+ notice = TRUE;
+ }
+
+ /* Food decrease */
+ else if (new_aux < old_aux)
+ {
+ /* Describe the state */
+ switch (new_aux)
+ {
+ /* Fainting / Starving */
+ case 0:
+ msg_print("You are getting faint from hunger!");
+ drop_from_wild();
+ break;
+
+ /* Weak */
+ case 1:
+ msg_print("You are getting weak from hunger!");
+ drop_from_wild();
+ break;
+
+ /* Hungry */
+ case 2:
+ msg_print("You are getting hungry.");
+ break;
+
+ /* Normal */
+ case 3:
+ msg_print("You are no longer full.");
+ break;
+
+ /* Full */
+ case 4:
+ msg_print("You are no longer gorged.");
+ break;
+ }
+
+ /* Change */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->food = v;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0, 0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw hunger */
+ p_ptr->redraw |= (PR_HUNGER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Advance experience levels and print experience
+ */
+void check_experience(void)
+{
+ int i, gained = 0;
+ bool level_reward = FALSE;
+ bool level_corruption = FALSE;
+
+
+ /* Note current level */
+ i = p_ptr->lev;
+
+ /* Hack -- lower limit */
+ if (p_ptr->exp < 0) p_ptr->exp = 0;
+
+ /* Hack -- lower limit */
+ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0;
+
+ /* Hack -- upper limit */
+ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP;
+
+ /* Hack -- upper limit */
+ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP;
+
+ /* Hack -- maintain "max" experience */
+ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp;
+
+ /* Redraw experience */
+ p_ptr->redraw |= (PR_EXP);
+
+ /* Handle stuff */
+ handle_stuff();
+
+
+ /* Lose levels while possible */
+ while ((p_ptr->lev > 1) &&
+ (p_ptr->exp < (player_exp[p_ptr->lev - 2] * p_ptr->expfact / 100L)))
+ {
+ /* Lose a level */
+ p_ptr->lev--;
+ gained--;
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Update some stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SANITY);
+
+ /* Redraw some stuff */
+ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+
+ /* Gain levels while possible */
+ while ((p_ptr->lev < PY_MAX_LEVEL) && (p_ptr->lev < max_plev) &&
+ (p_ptr->exp >= (player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L)))
+ {
+ /* Gain a level */
+ p_ptr->lev++;
+ gained++;
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Save the highest level */
+ if (p_ptr->lev > p_ptr->max_plv)
+ {
+ p_ptr->max_plv = p_ptr->lev;
+ if ((PRACE_FLAG(PR1_CORRUPT)) &&
+ (randint(3) == 1))
+ {
+ level_corruption = TRUE;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_LEVEL);
+
+ /* Message */
+ cmsg_format(TERM_L_GREEN, "Welcome to level %d.", p_ptr->lev);
+
+ if (p_ptr->skill_last_level < p_ptr->lev)
+ {
+ s32b pts;
+ call_lua("exec_module_info", "(s)", "d", "skill_per_level", &pts);
+
+ p_ptr->skill_last_level = p_ptr->lev;
+ p_ptr->skill_points += pts;
+ cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);
+ p_ptr->redraw |= PR_STUDY;
+ }
+
+ /* Gain this level's abilities */
+ apply_level_abilities(p_ptr->lev);
+
+ /* If auto-note taking enabled, write a note to the file.
+ * Only write this note when the level is gained for the first
+ * time.
+ */
+ if (take_notes && auto_notes)
+ {
+ char note[80];
+
+ /* Write note */
+ sprintf(note, "Reached level %d", p_ptr->lev);
+ add_note(note, 'L');
+ }
+
+ /* Update some stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SANITY);
+
+ /* Redraw some stuff */
+ p_ptr->redraw |= (PR_LEV | PR_TITLE | PR_EXP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ if (level_reward)
+ {
+ gain_level_reward(0);
+ level_reward = FALSE;
+ }
+
+ if (level_corruption)
+ {
+ msg_print("You feel different...");
+ corrupt_corrupted();
+ level_corruption = FALSE;
+ }
+ }
+
+ /* Hook it! */
+ process_hooks(HOOK_PLAYER_LEVEL, "(d)", gained);
+}
+/*
+ * Advance experience levels and print experience
+ */
+void check_experience_obj(object_type *o_ptr)
+{
+ int i;
+
+ /* Note current level */
+ i = o_ptr->elevel;
+
+ /* Hack -- lower limit */
+ if (o_ptr->exp < 0) o_ptr->exp = 0;
+
+ /* Hack -- upper limit */
+ if (o_ptr->exp > PY_MAX_EXP) o_ptr->exp = PY_MAX_EXP;
+
+ /* Gain levels while possible */
+ while ((o_ptr->elevel < PY_MAX_LEVEL) &&
+ (o_ptr->exp >= (player_exp[o_ptr->elevel - 1] * 5 / 2)))
+ {
+ char buf[100];
+
+ /* Add a level */
+ o_ptr->elevel++;
+
+ /* Get object name */
+ object_desc(buf, o_ptr, 1, 0);
+ cmsg_format(TERM_L_BLUE, "%s gains a level!", buf);
+
+ /* What does it gains ? */
+ object_gain_level(o_ptr);
+ }
+}
+
+
+/*
+ * Gain experience (share it to objects if needed)
+ */
+void gain_exp(s32b amount)
+{
+ int i, num = 1;
+
+ /* Count the gaining xp objects */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!o_ptr->k_idx) continue;
+
+ if (f4 & TR4_ART_EXP) num++;
+ }
+
+ /* Now give the xp */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (!o_ptr->k_idx) continue;
+
+ if (f4 & TR4_ART_EXP)
+ {
+ o_ptr->exp += 2 * amount / (num * 3);
+
+ /* Hack -- upper limit */
+ if (o_ptr->exp > PY_MAX_EXP) o_ptr->exp = PY_MAX_EXP;
+ }
+ }
+
+ if ((p_ptr->max_exp > 0) && (PRACE_FLAG(PR1_CORRUPT)))
+ {
+ if ((randint(p_ptr->max_exp) < amount) || (randint(12000000) < amount))
+ {
+ msg_print("You feel different...");
+ corrupt_corrupted();
+ };
+ /* 12,000,000 is equal to double Morgoth's raw XP value (60,000 * his Dlev (100))*/
+ };
+
+ /* Gain some experience */
+ p_ptr->exp += amount / num;
+
+ /* Hook it! */
+ process_hooks(HOOK_PLAYER_EXP, "(d)", amount / num);
+
+ /* Slowly recover from experience drainage */
+ if (p_ptr->exp < p_ptr->max_exp)
+ {
+ /* Gain max experience (20%) (was 10%) */
+ p_ptr->max_exp += amount / 5;
+ }
+
+ /* Check Experience */
+ check_experience();
+}
+
+
+/*
+ * Lose experience
+ */
+void lose_exp(s32b amount)
+{
+ /* Never drop below zero experience */
+ if (amount > p_ptr->exp) amount = p_ptr->exp;
+
+ /* Lose some experience */
+ p_ptr->exp -= amount;
+
+ /* Hook it! */
+ process_hooks(HOOK_PLAYER_EXP, "(d)", amount);
+
+ /* Check Experience */
+ check_experience();
+}
+
+
+
+
+/*
+ * Hack -- Return the "automatic coin type" of a monster race
+ * Used to allocate proper treasure when "Creeping coins" die
+ *
+ * XXX XXX XXX Note the use of actual "monster names"
+ */
+int get_coin_type(monster_race *r_ptr)
+{
+ cptr name = (r_name + r_ptr->name);
+
+ /* Analyze "coin" monsters */
+ if (r_ptr->d_char == '$')
+ {
+ /* Look for textual clues */
+ if (strstr(name, " copper ")) return (2);
+ if (strstr(name, " silver ")) return (5);
+ if (strstr(name, " gold ")) return (10);
+ if (strstr(name, " mithril ")) return (16);
+ if (strstr(name, " adamantite ")) return (17);
+
+ /* Look for textual clues */
+ if (strstr(name, "Copper ")) return (2);
+ if (strstr(name, "Silver ")) return (5);
+ if (strstr(name, "Gold ")) return (10);
+ if (strstr(name, "Mithril ")) return (16);
+ if (strstr(name, "Adamantite ")) return (17);
+ }
+
+ /* Assume nothing */
+ return (0);
+}
+
+/*
+ * This routine handles the production of corpses/skeletons/heads/skulls
+ * when a monster is killed.
+ */
+void place_corpse(monster_type *m_ptr)
+{
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int x = m_ptr->fx;
+ int y = m_ptr->fy;
+
+#if 0 /* If I can find some time to implement the decapitation ... */
+ object_type *w_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ int i = w_ptr->weight + ((p_ptr->to_h + w_ptr->to_h) * 5) + (p_ptr->lev * 3);
+
+ /* Handle decapitations. This is not allowed with hafted weapons. */
+ bool crit = (randint(5000) <= i);
+
+ bool decapitate = ((rand_int(m_ptr->maxhp) <= -(m_ptr->hp)) &&
+ (w_ptr->tval != TV_HAFTED) && crit);
+#endif
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* It has a physical form */
+ if (r_ptr->flags9 & RF9_DROP_CORPSE)
+ {
+ /* Wipe the object */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ object_aware(i_ptr);
+ i_ptr->name1 = 201;
+ }
+
+ /* Calculate length of time before decay */
+ i_ptr->pval = r_ptr->weight + rand_int(r_ptr->weight);
+
+ /* Set weight */
+ i_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+
+ /* Remember what we are */
+ i_ptr->pval2 = m_ptr->r_idx;
+
+ /* Some hp */
+ i_ptr->pval3 = ((maxroll(r_ptr->hdice, r_ptr->hside) + p_ptr->mhp) / 2);
+ i_ptr->pval3 -= randint(i_ptr->pval3) / 3;
+
+ i_ptr->found = OBJ_FOUND_MONSTER;
+ i_ptr->found_aux1 = m_ptr->r_idx;
+ i_ptr->found_aux2 = m_ptr->ego;
+ i_ptr->found_aux3 = dungeon_type;
+ i_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(i_ptr, -1, y, x);
+ }
+
+ /* The creature is an animated skeleton. */
+ if (!(r_ptr->flags9 & RF9_DROP_CORPSE) && (r_ptr->flags9 & RF9_DROP_SKELETON))
+ {
+ /* Wipe the object */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ object_aware(i_ptr);
+ i_ptr->name1 = 201;
+ }
+
+ i_ptr->pval = 0;
+
+ /* Set weight */
+ i_ptr->weight = (r_ptr->weight / 4 + rand_int(r_ptr->weight) / 40) + 1;
+
+ /* Remember what we are */
+ i_ptr->pval2 = m_ptr->r_idx;
+
+ i_ptr->found = OBJ_FOUND_MONSTER;
+ i_ptr->found_aux1 = m_ptr->r_idx;
+ i_ptr->found_aux2 = m_ptr->ego;
+ i_ptr->found_aux3 = dungeon_type;
+ i_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(i_ptr, -1, y, x);
+ }
+
+}
+
+
+/*
+ * Handle the "death" of a monster.
+ *
+ * Disperse treasures centered at the monster location based on the
+ * various flags contained in the monster flags fields.
+ *
+ * Check for "Quest" completion when a quest monster is killed.
+ *
+ * Note that only the player can induce "monster_death()" on Uniques.
+ * Thus (for now) all Quest monsters should be Uniques.
+ *
+ * Note that monsters can now carry objects, and when a monster dies,
+ * it drops all of its objects, which may disappear in crowded rooms.
+ */
+void monster_death(int m_idx)
+{
+ int i, y, x, ny, nx;
+
+ int dump_item = 0;
+ int dump_gold = 0;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ bool visible = (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE)));
+
+
+ bool cloned = FALSE;
+ bool create_stairs = FALSE;
+ int force_coin = get_coin_type(r_ptr);
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get the location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Process the appropriate hooks */
+ process_hooks(HOOK_MONSTER_DEATH, "(d)", m_idx);
+
+ /* If companion dies, take note */
+ if (m_ptr->status == MSTATUS_COMPANION) p_ptr->companion_killed++;
+
+ /* Handle reviving if undead */
+ if ((p_ptr->necro_extra & CLASS_UNDEAD) && p_ptr->necro_extra2)
+ {
+ p_ptr->necro_extra2--;
+
+ if (!p_ptr->necro_extra2)
+ {
+ msg_print("Your death has been avenged -- you return to life.");
+ p_ptr->necro_extra &= ~CLASS_UNDEAD;
+
+ /* Display the hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ msg_format("You still have to kill %d monster%s.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+ }
+ }
+
+ /* Handle the possibility of player vanquishing arena combatant -KMW- */
+ if (p_ptr->inside_arena)
+ {
+ p_ptr->exit_bldg = TRUE;
+ msg_print("Victorious! You're on your way to becoming Champion.");
+ p_ptr->arena_number++;
+ }
+
+ if (m_ptr->smart &(SM_CLONED)) cloned = TRUE;
+
+ /* If the doppleganger die, the variable must be set accordingly */
+ if (r_ptr->flags9 & RF9_DOPPLEGANGER) doppleganger = 0;
+
+ /* Drop objects being carried */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Paranoia */
+ o_ptr->held_m_idx = 0;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ if (q_ptr->tval == TV_GOLD) dump_gold++;
+ else dump_item++;
+
+ /* Drop it */
+ drop_near(q_ptr, -1, y, x);
+ }
+
+ /* Forget objects */
+ m_ptr->hold_o_idx = 0;
+
+ /* Average dungeon and monster levels */
+ object_level = (dun_level + m_ptr->level) / 2;
+
+ /* Mega^2-hack -- destroying the Stormbringer gives it us! */
+ if (strstr((r_name + r_ptr->name), "Stormbringer"))
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare to make the Stormbringer */
+ object_prep(q_ptr, lookup_kind(TV_SWORD, SV_BLADE_OF_CHAOS));
+
+ /* Mega-Hack -- Name the sword */
+
+ q_ptr->art_name = quark_add("'Stormbringer'");
+ q_ptr->to_h = 16;
+ q_ptr->to_d = 16;
+ q_ptr->ds = 6;
+ q_ptr->dd = 6;
+ q_ptr->pval = 2;
+
+ q_ptr->art_flags1 |= ( TR1_VAMPIRIC | TR1_STR | TR1_CON | TR1_BLOWS );
+ q_ptr->art_flags2 |= ( TR2_FREE_ACT | TR2_HOLD_LIFE |
+ TR2_RES_NEXUS | TR2_RES_CHAOS | TR2_RES_NETHER |
+ TR2_RES_CONF ); /* No longer resist_disen */
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD);
+ /* Just to be sure */
+
+ q_ptr->art_flags3 |= TR3_NO_TELE; /* How's that for a downside? */
+
+ /* For game balance... */
+ q_ptr->art_flags3 |= (TR3_CURSED | TR3_HEAVY_CURSE);
+ q_ptr->ident |= IDENT_CURSED;
+
+ if (randint(2) == 1)
+ q_ptr->art_flags3 |= (TR3_DRAIN_EXP);
+ else
+ q_ptr->art_flags3 |= (TR3_AGGRAVATE);
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+
+ /*
+ * Mega^3-hack: killing a 'Warrior of the Dawn' is likely to
+ * spawn another in the fallen one's place!
+ */
+ else if (strstr((r_name + r_ptr->name), "the Dawn"))
+ {
+ if (!(randint(20) == 13))
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 20, 0);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (attempts > 0)
+ {
+ if (is_friend(m_ptr) > 0)
+ {
+ if (summon_specific_friendly(wy, wx, 100, SUMMON_DAWN, FALSE))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A new warrior steps forth!");
+ }
+ }
+ else
+ {
+ if (summon_specific(wy, wx, 100, SUMMON_DAWN))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A new warrior steps forth!");
+ }
+ }
+ }
+ }
+ }
+
+ /* One more ultra-hack: An Unmaker goes out with a big bang! */
+ else if (strstr((r_name + r_ptr->name), "Unmaker"))
+ {
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ (void)project(m_idx, 6, y, x, 100, GF_CHAOS, flg);
+ }
+ /* Pink horrors are replaced with 2 Blue horrors */
+ else if (strstr((r_name + r_ptr->name), "ink horror"))
+ {
+ for (i = 0; i < 2; i++)
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3, 0);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (attempts > 0)
+ {
+ if (summon_specific(wy, wx, 100, SUMMON_BLUE_HORROR))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A blue horror appears!");
+ }
+ }
+ }
+ }
+
+ /* Mega-Hack -- drop "winner" treasures */
+ else if (r_ptr->flags1 & (RF1_DROP_CHOSEN))
+ {
+ if (strstr((r_name + r_ptr->name), "Morgoth, Lord of Darkness"))
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make "Grond" */
+ object_prep(q_ptr, lookup_kind(TV_HAFTED, SV_GROND));
+
+ /* Mega-Hack -- Mark this item as "Grond" */
+ q_ptr->name1 = ART_GROND;
+
+ /* Mega-Hack -- Actually create "Grond" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Mega-Hack -- Prepare to make "Morgoth" */
+ object_prep(q_ptr, lookup_kind(TV_CROWN, SV_MORGOTH));
+
+ /* Mega-Hack -- Mark this item as "Morgoth" */
+ q_ptr->name1 = ART_MORGOTH;
+
+ /* Mega-Hack -- Actually create "Morgoth" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+ else if (strstr((r_name + r_ptr->name), "Smeagol"))
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ object_wipe(q_ptr);
+
+ /* Mega-Hack -- Prepare to make a ring of invisibility */
+ object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_INVIS));
+ q_ptr->number = 1;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, FALSE);
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+ else if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ object_wipe(q_ptr);
+
+ /* Mega-Hack -- Prepare to make a Ring of Power */
+ object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_SPECIAL));
+ q_ptr->number = 1;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, FALSE);
+
+ /* Create a random artifact */
+ create_artifact(q_ptr, TRUE, FALSE);
+
+ /* Save the inscription */
+ q_ptr->art_name = quark_add(format("of %s", r_name + r_ptr->name));
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, y, x);
+ }
+ else
+ {
+ byte a_idx = 0;
+ int chance = 0;
+ int I_kind = 0;
+
+ if (strstr((r_name + r_ptr->name), "Marda, rider of the Gold Laronth"))
+ {
+ a_idx = ART_MARDA;
+ chance = 50;
+ }
+ else if (strstr((r_name + r_ptr->name), "Saruman of Many Colours"))
+ {
+ a_idx = ART_PALANTIR;
+ chance = 30;
+ }
+ else if (strstr((r_name + r_ptr->name), "Hagen, son of Alberich"))
+ {
+ a_idx = ART_NIMLOTH;
+ chance = 66;
+ }
+ else if (strstr((r_name + r_ptr->name), "Durin's Bane"))
+ {
+ a_idx = ART_CALRIS;
+ chance = 60;
+ }
+ else if (strstr((r_name + r_ptr->name), "Gothmog, the High Captain of Balrogs"))
+ {
+ a_idx = ART_GOTHMOG;
+ chance = 50;
+ }
+ else if (strstr((r_name + r_ptr->name), "Eol, the Dark Elf"))
+ {
+ a_idx = ART_ANGUIREL;
+ chance = 50;
+ }
+
+ if ((a_idx > 0) && ((randint(99) < chance) || (wizard)))
+ {
+ if (a_info[a_idx].cur_num == 0)
+ {
+ artifact_type *a_ptr = &a_info[a_idx];
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = a_idx;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ random_artifact_resistance(q_ptr);
+
+ a_info[a_idx].cur_num = 1;
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, y, x);
+ }
+ }
+ }
+ }
+
+ /* Hack - the protected monsters must be advanged */
+ else if (r_ptr->flags9 & RF9_WYRM_PROTECT)
+ {
+ int xx = x, yy = y;
+ int attempts = 100;
+
+ cmsg_print(TERM_VIOLET, "This monster was under the protection of a Great Wyrm of Power!");
+
+ do
+ {
+ scatter(&yy, &xx, y, x, 6, 0);
+ }
+ while (!(in_bounds(yy, xx) && cave_floor_bold(yy, xx)) && --attempts);
+
+ place_monster_aux(yy, xx, test_monster_name("Great Wyrm of Power"), FALSE, FALSE, m_ptr->status);
+ }
+
+ /* Let monsters explode! */
+ for (i = 0; i < 4; i++)
+ {
+ if (m_ptr->blow[i].method == RBM_EXPLODE)
+ {
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
+ int typ = GF_MISSILE;
+ int d_dice = m_ptr->blow[i].d_dice;
+ int d_side = m_ptr->blow[i].d_side;
+ int damage = damroll(d_dice, d_side);
+
+ switch (m_ptr->blow[i].effect)
+ {
+ case RBE_HURT:
+ typ = GF_MISSILE;
+ break;
+ case RBE_POISON:
+ typ = GF_POIS;
+ break;
+ case RBE_UN_BONUS:
+ typ = GF_DISENCHANT;
+ break;
+ case RBE_UN_POWER:
+ typ = GF_MISSILE;
+ break; /* ToDo: Apply the correct effects */
+ case RBE_EAT_GOLD:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EAT_ITEM:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EAT_FOOD:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EAT_LITE:
+ typ = GF_MISSILE;
+ break;
+ case RBE_ACID:
+ typ = GF_ACID;
+ break;
+ case RBE_ELEC:
+ typ = GF_ELEC;
+ break;
+ case RBE_FIRE:
+ typ = GF_FIRE;
+ break;
+ case RBE_COLD:
+ typ = GF_COLD;
+ break;
+ case RBE_BLIND:
+ typ = GF_MISSILE;
+ break;
+ case RBE_HALLU:
+ typ = GF_CONFUSION;
+ break;
+ case RBE_CONFUSE:
+ typ = GF_CONFUSION;
+ break;
+ case RBE_TERRIFY:
+ typ = GF_MISSILE;
+ break;
+ case RBE_PARALYZE:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_STR:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_DEX:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_CON:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_INT:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_WIS:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_CHR:
+ typ = GF_MISSILE;
+ break;
+ case RBE_LOSE_ALL:
+ typ = GF_MISSILE;
+ break;
+ case RBE_PARASITE:
+ typ = GF_MISSILE;
+ break;
+ case RBE_SHATTER:
+ typ = GF_ROCKET;
+ break;
+ case RBE_EXP_10:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EXP_20:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EXP_40:
+ typ = GF_MISSILE;
+ break;
+ case RBE_EXP_80:
+ typ = GF_MISSILE;
+ break;
+ case RBE_DISEASE:
+ typ = GF_POIS;
+ break;
+ case RBE_TIME:
+ typ = GF_TIME;
+ break;
+ case RBE_SANITY:
+ typ = GF_MISSILE;
+ break;
+ }
+
+ project(m_idx, 3, y, x, damage, typ, flg);
+ break;
+ }
+ }
+
+ if ((!force_coin) && (magik(10 + get_skill_scale(SKILL_PRESERVATION, 75))) && (!(m_ptr->mflag & MFLAG_NO_DROP)))
+ place_corpse(m_ptr);
+
+ /* Take note of any dropped treasure */
+ if (visible && (dump_item || dump_gold))
+ {
+ /* Take notes on treasure */
+ lore_treasure(m_idx, dump_item, dump_gold);
+ }
+
+ /* Current quest */
+ i = p_ptr->inside_quest;
+
+ /* Create a magical staircase */
+ if (create_stairs && (dun_level < d_info[dungeon_type].maxdepth))
+ {
+ int i, j;
+
+ for (i = -1; i <= 1; i++)
+ for (j = -1; j <= 1; j++)
+ if (!(f_info[cave[y + j][x + i].feat].flags1 & FF1_PERMANENT)) cave_set_feat(y + j, x + i, d_info[dungeon_type].floor1);
+
+ /* Stagger around */
+ while (!cave_valid_bold(y, x))
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&ny, &nx, y, x, d, 0);
+
+ /* Stagger */
+ y = ny;
+ x = nx;
+ }
+
+ /* Destroy any objects */
+ delete_object(y, x);
+
+ /* Explain the staircase */
+ msg_print("A magical staircase appears...");
+
+ /* Create stairs down */
+ cave_set_feat(y, x, FEAT_MORE);
+
+ /* Remember to update everything */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS);
+ }
+}
+
+
+
+
+/*
+ * Decreases monsters hit points, handling monster death.
+ *
+ * We return TRUE if the monster has been killed (and deleted).
+ *
+ * We announce monster death (using an optional "death message"
+ * if given, and a otherwise a generic killed/destroyed message).
+ *
+ * Only "physical attacks" can induce the "You have slain" message.
+ * Missile and Spell attacks will induce the "dies" message, or
+ * various "specialized" messages. Note that "You have destroyed"
+ * and "is destroyed" are synonyms for "You have slain" and "dies".
+ *
+ * Hack -- unseen monsters yield "You have killed it." message.
+ *
+ * Added fear (DGK) and check whether to print fear messages -CWS
+ *
+ * Genericized name, sex, and capitilization -BEN-
+ *
+ * Hack -- we "delay" fear messages by passing around a "fear" flag.
+ *
+ * XXX XXX XXX Consider decreasing monster experience over time, say,
+ * by using "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))"
+ * instead of simply "(m_exp * m_lev) / (p_lev)", to make the first
+ * monster worth more than subsequent monsters. This would also need
+ * to induce changes in the monster recall code.
+ */
+bool mon_take_hit(int m_idx, int dam, bool *fear, cptr note)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+ s32b div, new_exp, new_exp_frac;
+
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
+
+ /* Some mosnters are immune to death */
+ if (r_ptr->flags7 & RF7_NO_DEATH) return FALSE;
+
+ /* Wake it up */
+ m_ptr->csleep = 0;
+
+ /* Hurt it */
+ m_ptr->hp -= dam;
+
+ /* It is dead now */
+ if (m_ptr->hp < 0)
+ {
+ char m_name[80];
+
+ /* Lets face it, you cannot get rid of a possessor that easily */
+ if (m_ptr->possessor)
+ {
+ ai_deincarnate(m_idx);
+
+ return FALSE;
+ }
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ if ((r_ptr->flags7 & RF7_DG_CURSE) && (randint(2) == 1))
+ {
+ int curses = 2 + randint(5);
+
+ cmsg_format(TERM_VIOLET, "%^s puts a terrible Morgothian curse on you!", m_name);
+ curse_equipment_dg(100, 50);
+
+ do
+ {
+ activate_dg_curse();
+ }
+ while (--curses);
+ }
+
+ if (speak_unique && (r_ptr->flags2 & (RF2_CAN_SPEAK)))
+ {
+ char line_got[80];
+ /* Dump a message */
+
+ get_rnd_line("mondeath.txt", line_got);
+ msg_format("%^s says: %s", m_name, line_got);
+ }
+
+
+ /* Make a sound */
+ sound(SOUND_KILL);
+
+ /* Death by Missile/Spell attack */
+ if (note)
+ {
+ cmsg_format(TERM_L_RED, "%^s%s", m_name, note);
+ }
+
+ /* Death by physical attack -- invisible monster */
+ else if (!m_ptr->ml)
+ {
+ cmsg_format(TERM_L_RED, "You have killed %s.", m_name);
+ }
+
+ /* Death by Physical attack -- non-living monster */
+ else if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ cmsg_format(TERM_L_RED, "You have destroyed %s.", m_name);
+ }
+
+ /* Death by Physical attack -- living monster */
+ else
+ {
+ cmsg_format(TERM_L_RED, "You have slain %s.", m_name);
+ }
+
+ /* Maximum player level */
+ div = p_ptr->max_plv;
+
+ if (m_ptr->status < MSTATUS_FRIEND)
+ {
+ /* Give some experience for the kill */
+ new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;
+
+ /* Handle fractional experience */
+ new_exp_frac = ((((long)r_ptr->mexp * m_ptr->level) % div)
+ * 0x10000L / div) + p_ptr->exp_frac;
+
+ /* Keep track of experience */
+ if (new_exp_frac >= 0x10000L)
+ {
+ new_exp++;
+ p_ptr->exp_frac = new_exp_frac - 0x10000L;
+ }
+ else
+ {
+ p_ptr->exp_frac = new_exp_frac;
+ }
+
+ /* Gain experience */
+ gain_exp(new_exp);
+ }
+
+ if (!note)
+ {
+ object_type *o_ptr;
+ object_kind *k_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Access the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ k_ptr = &k_info[o_ptr->k_idx];
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can the weapon gain levels ? */
+ if ((o_ptr->k_idx) && (f4 & TR4_LEVELS))
+ {
+ /* Give some experience for the kill */
+ new_exp = ((long)r_ptr->mexp * m_ptr->level) / (div * 2);
+
+ /* Gain experience */
+ o_ptr->exp += new_exp;
+ check_experience_obj(o_ptr);
+ }
+ }
+
+ /* When the player kills a Unique, it stays dead */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ r_ptr->max_num = 0;
+ }
+
+ /* Generate treasure */
+ monster_death(m_idx);
+
+ /* Eru doesn't appreciate good monster death */
+ if (r_ptr->flags3 & RF3_GOOD)
+ {
+ inc_piety(GOD_ERU, -7 * m_ptr->level);
+ inc_piety(GOD_MANWE, -10 * m_ptr->level);
+ inc_piety(GOD_MELKOR, 3 * m_ptr->level);
+ }
+ else
+ {
+ inc_piety(GOD_MELKOR, 1 + m_ptr->level / 2);
+ }
+
+ /* Manwe appreciate evil monster death */
+ if (r_ptr->flags3 & RF3_EVIL)
+ {
+ int inc = m_ptr->level / 2;
+
+ if (!inc) inc = 1;
+ PRAY_GOD(GOD_MANWE) inc_piety(GOD_MANWE, inc);
+
+ if (inc < 2) inc = 2;
+ inc_piety(GOD_TULKAS, inc / 2);
+ PRAY_GOD(GOD_TULKAS)
+ {
+ inc_piety(GOD_TULKAS, inc / 2);
+ if (r_ptr->flags3 & RF3_DEMON) inc_piety(GOD_TULKAS, inc);
+ }
+ }
+
+ /* Yavanna likes when corruption is destroyed */
+ if ((r_ptr->flags3 & RF3_NONLIVING) || (r_ptr->flags3 & RF3_DEMON) || (r_ptr->flags3 & RF3_UNDEAD))
+ {
+ int inc = m_ptr->level / 2;
+
+ if (!inc) inc = 1;
+ inc_piety(GOD_YAVANNA, inc);
+ }
+
+ /* Yavanna doesnt like any killing in her name */
+ PRAY_GOD(GOD_YAVANNA)
+ {
+ int inc = m_ptr->level / 2;
+
+ if (!inc) inc = 1;
+ inc_piety(GOD_YAVANNA, -inc);
+
+ /* Killing animals in her name is a VERY bad idea */
+ if (r_ptr->flags3 & RF3_ANIMAL)
+ inc_piety(GOD_YAVANNA, -(inc * 3));
+ }
+
+ /* SHould we absorb its soul? */
+ if (p_ptr->absorb_soul && (!(r_ptr->flags3 & RF3_UNDEAD)) && (!(r_ptr->flags3 & RF3_NONLIVING)))
+ {
+ msg_print("You absorb the life of the dying soul.");
+ hp_player(1 + (m_ptr->level / 2) + get_skill_scale(SKILL_NECROMANCY, 40));
+ }
+
+ /*
+ * XXX XXX XXX Mega-Hack -- Remove random quest rendered
+ * impossible
+ */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ int i;
+
+ /* Search for all the random quests */
+ for (i = 0; i < MAX_RANDOM_QUEST; i++)
+ {
+ random_quest *q_ptr = &random_quests[i];
+
+ /* Ignore invalid entries */
+ if (q_ptr->type == 0) continue;
+
+ /* It's done */
+ if (q_ptr->done) continue;
+
+ /*
+ * XXX XXX XXX Not yet completed quest is
+ * to kill a unique you've just killed
+ */
+ if (r_ptr == &r_info[q_ptr->r_idx])
+ {
+ /* Invalidate it */
+ q_ptr->type = 0;
+ }
+ }
+ }
+
+#if 0 /* DGDGDG -- pfft */
+ /* XXX XXX Mega-Hack -- allow another ghost later
+ * Remove the slain bone file */
+ if (m_ptr->r_idx == max_r_idx - 1)
+ {
+ r_ptr->max_num = 1;
+
+ /* Delete the bones file */
+ sprintf(tmp, "%s%sbone%03d.%03d", ANGBAND_DIR_BONE, PATH_SEP, dungeon_type, dun_level);
+
+ /* Grab permission */
+ safe_setuid_grab();
+
+ /* Remove the bone file */
+ fd_kill(tmp);
+
+ /* Drop permission */
+ safe_setuid_drop();
+ }
+#endif
+ /* If the player kills a Unique, and the notes options are on, write a note */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && take_notes && auto_notes)
+ {
+ char note[80];
+
+ /* Get true name even if blinded/hallucinating */
+ cptr monst = (r_name + r_ptr->name);
+
+ /* Write note */
+ sprintf(note, "Killed %s", monst);
+
+ add_note(note, 'U');
+ }
+
+ /* Recall even invisible uniques or winners */
+ if (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ /* Count kills this life */
+ if (r_ptr->r_pkills < MAX_SHORT) r_ptr->r_pkills++;
+
+ /* Count kills in all lives */
+ if (r_ptr->r_tkills < MAX_SHORT) r_ptr->r_tkills++;
+
+ /* Hack -- Auto-recall */
+ monster_race_track(m_ptr->r_idx, m_ptr->ego);
+ }
+
+ /* Delete the monster */
+ delete_monster_idx(m_idx);
+
+ /* Not afraid */
+ (*fear) = FALSE;
+
+ /* Monster is dead */
+ return (TRUE);
+ }
+
+
+#ifdef ALLOW_FEAR
+
+ /* Mega-Hack -- Pain cancels fear */
+ if (m_ptr->monfear && (dam > 0))
+ {
+ int tmp = randint(dam);
+
+ /* Cure a little fear */
+ if (tmp < m_ptr->monfear)
+ {
+ /* Reduce fear */
+ m_ptr->monfear -= tmp;
+ }
+
+ /* Cure all the fear */
+ else
+ {
+ /* Cure fear */
+ m_ptr->monfear = 0;
+
+ /* No more fear */
+ (*fear) = FALSE;
+ }
+ }
+
+ /* Sometimes a monster gets scared by damage */
+ if (!m_ptr->monfear && !(r_ptr->flags3 & (RF3_NO_FEAR)))
+ {
+ int percentage;
+
+ /* Percentage of fully healthy */
+ percentage = (100L * m_ptr->hp) / m_ptr->maxhp;
+
+ /*
+ * Run (sometimes) if at 10% or less of max hit points,
+ * or (usually) when hit for half its current hit points
+ */
+ if (((percentage <= 10) && (rand_int(10) < percentage)) ||
+ ((dam >= m_ptr->hp) && (rand_int(100) < 80)))
+ {
+ /* Hack -- note fear */
+ (*fear) = TRUE;
+
+ /* XXX XXX XXX Hack -- Add some timed fear */
+ m_ptr->monfear = (randint(10) +
+ (((dam >= m_ptr->hp) && (percentage > 7)) ?
+ 20 : ((11 - percentage) * 5)));
+ }
+ }
+
+#endif
+
+
+ /* Not dead yet */
+ return (FALSE);
+}
+
+
+/*
+ * Get term size and calculate screen size
+ */
+void get_screen_size(int *wid_p, int *hgt_p)
+{
+ Term_get_size(wid_p, hgt_p);
+ *hgt_p -= ROW_MAP + 1;
+ *wid_p -= COL_MAP + 1;
+ if (use_bigtile) *wid_p /= 2;
+}
+
+/*
+ * Calculates current boundaries
+ * Called below.
+ */
+static void panel_bounds(void)
+{
+ int wid, hgt;
+
+ /* Retrieve current screen size */
+ get_screen_size(&wid, &hgt);
+
+ /* + 24 - 1 - 2 = + 21 */
+ panel_row_max = panel_row_min + hgt - 1;
+ panel_row_prt = panel_row_min - ROW_MAP;
+
+ /* Paranoia -- Boundary check */
+ if (panel_row_max > cur_hgt - 1) panel_row_max = cur_hgt - 1;
+
+ panel_col_max = panel_col_min + wid - 1;
+ panel_col_prt = panel_col_min - COL_MAP;
+
+ /* Paranoia -- Boundary check */
+ if (panel_col_max > cur_wid - 1) panel_col_max = cur_wid - 1;
+}
+
+
+/*
+ * Handle a request to change the current panel
+ *
+ * Return TRUE if the panel was changed.
+ *
+ * Also used in do_cmd_locate()
+ */
+bool change_panel(int dy, int dx)
+{
+ int y, x;
+ int wid, hgt;
+
+ /* Get size */
+ get_screen_size(&wid, &hgt);
+
+ /* Apply the motion */
+ y = panel_row_min + dy * (hgt / 2);
+ x = panel_col_min + dx * (wid / 2);
+
+ /* Calculate bounds */
+ if (y > cur_hgt - hgt) y = cur_hgt - hgt;
+ if (y < 0) y = 0;
+ if (x > cur_wid - wid) x = cur_wid - wid;
+ if (x < 0) x = 0;
+
+ /* Handle changes */
+ if ((y != panel_row_min) || (x != panel_col_min))
+ {
+ /* Save the new panel info */
+ panel_row_min = y;
+ panel_col_min = x;
+
+ /* Recalculate the boundaries */
+ panel_bounds();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* No changes */
+ return (FALSE);
+}
+
+
+/*
+ * Given an row (y) and col (x), this routine detects when a move
+ * off the screen has occurred and figures new borders. -RAK-
+ *
+ * "Update" forces a "full update" to take place.
+ *
+ * The map is reprinted if necessary, and "TRUE" is returned.
+ */
+void verify_panel(void)
+{
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+
+ int wid, hgt;
+
+ int prow_min;
+ int pcol_min;
+
+ int panel_hgt, panel_wid;
+
+ int max_prow_min;
+ int max_pcol_min;
+
+
+ /*
+ * Make sure panel_row/col_max have correct values -- now taken care of
+ * by the hook function below, which eliminates glitches, but does it
+ * in a very hackish way XXX XXX XXX
+ */
+ /* panel_bounds(); */
+
+ /* Get size */
+ get_screen_size(&wid, &hgt);
+
+ /* Calculate scroll amount */
+ panel_hgt = hgt / 2;
+ panel_wid = wid / 2;
+
+ /* Upper boundary of panel_row/col_min */
+ max_prow_min = cur_hgt - hgt;
+ max_pcol_min = cur_wid - wid;
+
+ /* Boundary check */
+ if (max_prow_min < 0) max_prow_min = 0;
+ if (max_pcol_min < 0) max_pcol_min = 0;
+
+ /* An option: center on player */
+ if (center_player)
+ {
+ /* Center vertically */
+ prow_min = y - panel_hgt;
+
+ /* Boundary check */
+ if (prow_min < 0) prow_min = 0;
+ else if (prow_min > max_prow_min) prow_min = max_prow_min;
+
+ /* Center horizontally */
+ pcol_min = x - panel_wid;
+
+ /* Boundary check */
+ if (pcol_min < 0) pcol_min = 0;
+ else if (pcol_min > max_pcol_min) pcol_min = max_pcol_min;
+ }
+
+ /* No centering */
+ else
+ {
+ prow_min = panel_row_min;
+ pcol_min = panel_col_min;
+
+ /* Scroll screen when 2 grids from top/bottom edge */
+ if (y > panel_row_max - 2)
+ {
+ while (y > prow_min + hgt - 2)
+ {
+ prow_min += panel_hgt;
+ }
+
+ if (prow_min > max_prow_min) prow_min = max_prow_min;
+ }
+
+ if (y < panel_row_min + 2)
+ {
+ while (y < prow_min + 2)
+ {
+ prow_min -= panel_hgt;
+ }
+
+ if (prow_min < 0) prow_min = 0;
+ }
+
+ /* Scroll screen when 4 grids from left/right edge */
+ if (x > panel_col_max - 4)
+ {
+ while (x > pcol_min + wid - 4)
+ {
+ pcol_min += panel_wid;
+ }
+
+ if (pcol_min > max_pcol_min) pcol_min = max_pcol_min;
+ }
+
+ if (x < panel_col_min + 4)
+ {
+ while (x < pcol_min + 4)
+ {
+ pcol_min -= panel_wid;
+ }
+
+ if (pcol_min < 0) pcol_min = 0;
+ }
+ }
+
+ /* Check for "no change" */
+ if ((prow_min == panel_row_min) && (pcol_min == panel_col_min)) return;
+
+ /* Save the new panel info */
+ panel_row_min = prow_min;
+ panel_col_min = pcol_min;
+
+ /* Hack -- optional disturb on "panel change" */
+ if (disturb_panel && !center_player) disturb(0, 0);
+
+ /* Recalculate the boundaries */
+ panel_bounds();
+
+ /* Hack - merchants detect items */
+#if 0 /* DGDGDGDG -- use a skill */
+ if (cp_ptr->magic_key == MKEY_TELEKINESIS)
+ {
+ hack_no_detect_message = TRUE;
+ detect_objects_normal(DEFAULT_RADIUS);
+ detect_objects_gold(DEFAULT_RADIUS);
+ hack_no_detect_message = FALSE;
+ }
+#endif
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Map resizing whenever the main term changes size
+ */
+void resize_map(void)
+{
+ /* Only if the dungeon exists */
+ if (!character_dungeon) return;
+
+ /* Mega-Hack -- No panel yet, assume illegal panel */
+ panel_row_min = cur_hgt;
+ panel_row_max = 0;
+ panel_col_min = cur_wid;
+ panel_col_max = 0;
+
+ /* Select player panel */
+ verify_panel();
+
+ /*
+ * The following should be the same as the main window code
+ * in the do_cmd_redraw()
+ */
+
+ /* Combine and reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Update torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Forget and update view */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP);
+
+ /* Hack -- update */
+ handle_stuff();
+
+ /* Redraw */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+/*
+ * Redraw a term when it is resized
+ */
+void resize_window(void)
+{
+ /* Only if the dungeon exists */
+ if (!character_dungeon) return;
+
+ /* Hack -- Activate the Angband window for the redraw */
+ Term_activate(&term_screen[0]);
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_M_LIST | PW_IRC | PW_MESSAGE | PW_OVERHEAD |
+ PW_MONSTER | PW_OBJECT);
+
+
+ /* Hack -- update */
+ handle_stuff();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+
+
+/*
+ * Monster health description
+ */
+cptr look_mon_desc(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ bool living = TRUE;
+ int perc;
+
+
+ /* Determine if the monster is "living" (vs "undead") */
+ if (r_ptr->flags3 & (RF3_UNDEAD)) living = FALSE;
+ if (r_ptr->flags3 & (RF3_DEMON)) living = FALSE;
+ if (r_ptr->flags3 & (RF3_NONLIVING)) living = FALSE;
+ if (strchr("Egv", r_ptr->d_char)) living = FALSE;
+
+
+ /* Healthy monsters */
+ if (m_ptr->hp >= m_ptr->maxhp)
+ {
+ /* No damage */
+ return (living ? "unhurt" : "undamaged");
+ }
+
+
+ /* Calculate a health "percentage" */
+ perc = 100L * m_ptr->hp / m_ptr->maxhp;
+
+ if (perc >= 60)
+ {
+ return (living ? "somewhat wounded" : "somewhat damaged");
+ }
+
+ if (perc >= 25)
+ {
+ return (living ? "wounded" : "damaged");
+ }
+
+ if (perc >= 10)
+ {
+ return (living ? "badly wounded" : "badly damaged");
+ }
+
+ return (living ? "almost dead" : "almost destroyed");
+}
+
+
+
+/*
+ * Angband sorting algorithm -- quick sort in place
+ *
+ * Note that the details of the data we are sorting is hidden,
+ * and we rely on the "ang_sort_comp()" and "ang_sort_swap()"
+ * function hooks to interact with the data, which is given as
+ * two pointers, and which may have any user-defined form.
+ */
+void ang_sort_aux(vptr u, vptr v, int p, int q)
+{
+ int z, a, b;
+
+ /* Done sort */
+ if (p >= q) return;
+
+ /* Pivot */
+ z = p;
+
+ /* Begin */
+ a = p;
+ b = q;
+
+ /* Partition */
+ while (TRUE)
+ {
+ /* Slide i2 */
+ while (!(*ang_sort_comp)(u, v, b, z)) b--;
+
+ /* Slide i1 */
+ while (!(*ang_sort_comp)(u, v, z, a)) a++;
+
+ /* Done partition */
+ if (a >= b) break;
+
+ /* Swap */
+ (*ang_sort_swap)(u, v, a, b);
+
+ /* Advance */
+ a++, b--;
+ }
+
+ /* Recurse left side */
+ ang_sort_aux(u, v, p, b);
+
+ /* Recurse right side */
+ ang_sort_aux(u, v, b + 1, q);
+}
+
+
+/*
+ * Angband sorting algorithm -- quick sort in place
+ *
+ * Note that the details of the data we are sorting is hidden,
+ * and we rely on the "ang_sort_comp()" and "ang_sort_swap()"
+ * function hooks to interact with the data, which is given as
+ * two pointers, and which may have any user-defined form.
+ */
+void ang_sort(vptr u, vptr v, int n)
+{
+ /* Sort the array */
+ ang_sort_aux(u, v, 0, n - 1);
+}
+
+
+
+/*** Targetting Code ***/
+
+
+/*
+ * Determine is a monster makes a reasonable target
+ *
+ * The concept of "targetting" was stolen from "Morgul" (?)
+ *
+ * The player can target any location, or any "target-able" monster.
+ *
+ * Currently, a monster is "target_able" if it is visible, and if
+ * the player can hit it with a projection, and the player is not
+ * hallucinating. This allows use of "use closest target" macros.
+ *
+ * Future versions may restrict the ability to target "trappers"
+ * and "mimics", but the semantics is a little bit weird.
+ */
+bool target_able(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Monster must be alive */
+ if (!m_ptr->r_idx) return (FALSE);
+
+ /* Monster must be visible */
+ if (!m_ptr->ml) return (FALSE);
+
+ /* Monster must be projectable */
+ if (!projectable(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) return (FALSE);
+
+ /* Hack -- no targeting hallucinations */
+ if (p_ptr->image) return (FALSE);
+
+ /* Dont target pets */
+ if (is_friend(m_ptr) > 0) return (FALSE);
+
+ /* Honor flag */
+ if (r_info[m_ptr->r_idx].flags7 & RF7_NO_TARGET) return (FALSE);
+
+ /* XXX XXX XXX Hack -- Never target trappers */
+ /* if (CLEAR_ATTR && (CLEAR_CHAR)) return (FALSE); */
+
+ /* Assume okay */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Update (if necessary) and verify (if possible) the target.
+ *
+ * We return TRUE if the target is "okay" and FALSE otherwise.
+ */
+bool target_okay(void)
+{
+ /* Accept stationary targets */
+ if (target_who < 0) return (TRUE);
+
+ /* Check moving targets */
+ if (target_who > 0)
+ {
+ /* Accept reasonable targets */
+ if (target_able(target_who))
+ {
+ monster_type *m_ptr = &m_list[target_who];
+
+ /* Acquire monster location */
+ target_row = m_ptr->fy;
+ target_col = m_ptr->fx;
+
+ /* Good target */
+ return (TRUE);
+ }
+ }
+
+ /* Assume no target */
+ return (FALSE);
+}
+
+
+
+/*
+ * Sorting hook -- comp function -- by "distance to player"
+ *
+ * We use "u" and "v" to point to arrays of "x" and "y" positions,
+ * and sort the arrays by double-distance to the player.
+ */
+static bool ang_sort_comp_distance(vptr u, vptr v, int a, int b)
+{
+ byte *x = (byte*)(u);
+ byte *y = (byte*)(v);
+
+ int da, db, kx, ky;
+
+ /* Absolute distance components */
+ kx = x[a];
+ kx -= p_ptr->px;
+ kx = ABS(kx);
+ ky = y[a];
+ ky -= p_ptr->py;
+ ky = ABS(ky);
+
+ /* Approximate Double Distance to the first point */
+ da = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx));
+
+ /* Absolute distance components */
+ kx = x[b];
+ kx -= p_ptr->px;
+ kx = ABS(kx);
+ ky = y[b];
+ ky -= p_ptr->py;
+ ky = ABS(ky);
+
+ /* Approximate Double Distance to the first point */
+ db = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx));
+
+ /* Compare the distances */
+ return (da <= db);
+}
+
+
+/*
+ * Sorting hook -- swap function -- by "distance to player"
+ *
+ * We use "u" and "v" to point to arrays of "x" and "y" positions,
+ * and sort the arrays by distance to the player.
+ */
+static void ang_sort_swap_distance(vptr u, vptr v, int a, int b)
+{
+ byte *x = (byte*)(u);
+ byte *y = (byte*)(v);
+
+ byte temp;
+
+ /* Swap "x" */
+ temp = x[a];
+ x[a] = x[b];
+ x[b] = temp;
+
+ /* Swap "y" */
+ temp = y[a];
+ y[a] = y[b];
+ y[b] = temp;
+}
+
+
+
+/*
+ * Hack -- help "select" a location (see below)
+ */
+static s16b target_pick(int y1, int x1, int dy, int dx)
+{
+ int i, v;
+
+ int x2, y2, x3, y3, x4, y4;
+
+ int b_i = -1, b_v = 9999;
+
+
+ /* Scan the locations */
+ for (i = 0; i < temp_n; i++)
+ {
+ /* Point 2 */
+ x2 = temp_x[i];
+ y2 = temp_y[i];
+
+ /* Directed distance */
+ x3 = (x2 - x1);
+ y3 = (y2 - y1);
+
+ /* Verify quadrant */
+ if (dx && (x3 * dx <= 0)) continue;
+ if (dy && (y3 * dy <= 0)) continue;
+
+ /* Absolute distance */
+ x4 = ABS(x3);
+ y4 = ABS(y3);
+
+ /* Verify quadrant */
+ if (dy && !dx && (x4 > y4)) continue;
+ if (dx && !dy && (y4 > x4)) continue;
+
+ /* Approximate Double Distance */
+ v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4));
+
+ /* XXX XXX XXX Penalize location */
+
+ /* Track best */
+ if ((b_i >= 0) && (v >= b_v)) continue;
+
+ /* Track best */
+ b_i = i;
+ b_v = v;
+ }
+
+ /* Result */
+ return (b_i);
+}
+
+
+/*
+ * Hack -- determine if a given location is "interesting"
+ */
+static bool target_set_accept(int y, int x)
+{
+ cave_type *c_ptr;
+
+ s16b this_o_idx, next_o_idx = 0;
+
+
+ /* Player grid is always interesting */
+ if ((y == p_ptr->py) && (x == p_ptr->px)) return (TRUE);
+
+
+ /* Handle hallucination */
+ if (p_ptr->image) return (FALSE);
+
+
+ /* Examine the grid */
+ c_ptr = &cave[y][x];
+
+ /* Visible monsters */
+ if (c_ptr->m_idx && c_ptr->m_idx < max_r_idx)
+ {
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ /* Visible monsters */
+ if (m_ptr->ml) return (TRUE);
+ }
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Memorized object */
+ if (o_ptr->marked) return (TRUE);
+ }
+
+ /* Interesting memorized features */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ /* Traps are interesting */
+ if (c_ptr->info & (CAVE_TRDT)) return (TRUE);
+
+ /* Hack -- Doors are boring */
+ if (c_ptr->feat == FEAT_OPEN) return (FALSE);
+ if (c_ptr->feat == FEAT_BROKEN) return (FALSE);
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) return (FALSE);
+
+ /* Accept 'naturally' interesting features */
+ if (f_info[c_ptr->feat].flags1 & FF1_NOTICE) return (TRUE);
+ }
+
+ /* Nope */
+ return (FALSE);
+}
+
+
+/*
+ * Prepare the "temp" array for "target_set"
+ *
+ * Return the number of target_able monsters in the set.
+ */
+static void target_set_prepare(int mode)
+{
+ int y, x;
+
+ /* Reset "temp" array */
+ temp_n = 0;
+
+ /* Scan the current panel */
+ for (y = panel_row_min; y <= panel_row_max; y++)
+ {
+ for (x = panel_col_min; x <= panel_col_max; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Require line of sight, unless "look" is "expanded" */
+ if (!expand_look && !player_has_los_bold(y, x)) continue;
+
+ /* Require "interesting" contents */
+ if (!target_set_accept(y, x)) continue;
+
+ /* Require target_able monsters for "TARGET_KILL" */
+ if ((mode & (TARGET_KILL)) && !target_able(c_ptr->m_idx)) continue;
+
+ /* Save the location */
+ temp_x[temp_n] = x;
+ temp_y[temp_n] = y;
+ temp_n++;
+ }
+ }
+
+ /* Set the sort hooks */
+ ang_sort_comp = ang_sort_comp_distance;
+ ang_sort_swap = ang_sort_swap_distance;
+
+ /* Sort the positions */
+ ang_sort(temp_x, temp_y, temp_n);
+}
+
+
+bool target_object(int y, int x, int mode, cptr info, bool *boring,
+ object_type *o_ptr, char *out_val, cptr *s1, cptr *s2, cptr *s3,
+ int *query)
+{
+ char o_name[80];
+
+ /* Not boring */
+ *boring = FALSE;
+
+ /* Obtain an object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe the object */
+ sprintf(out_val, "%s%s%s%s [%s]", *s1, *s2, *s3, o_name, info);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ *query = inkey();
+
+ /* Always stop at "normal" keys */
+ if ((*query != '\r') && (*query != '\n') && (*query != ' ')) return (TRUE);
+
+ /* Sometimes stop at "space" key */
+ if ((*query == ' ') && !(mode & (TARGET_LOOK))) return (TRUE);
+
+ /* Change the intro */
+ *s1 = "It is ";
+
+ /* Plurals */
+ if (o_ptr->number != 1) *s1 = "They are ";
+
+ /* Preposition */
+ *s2 = "on ";
+ return (FALSE);
+}
+
+/*
+ * Examine a grid, return a keypress.
+ *
+ * The "mode" argument contains the "TARGET_LOOK" bit flag, which
+ * indicates that the "space" key should scan through the contents
+ * of the grid, instead of simply returning immediately. This lets
+ * the "look" command get complete information, without making the
+ * "target" command annoying.
+ *
+ * The "info" argument contains the "commands" which should be shown
+ * inside the "[xxx]" text. This string must never be empty, or grids
+ * containing monsters will be displayed with an extra comma.
+ *
+ * Note that if a monster is in the grid, we update both the monster
+ * recall info and the health bar info to track that monster.
+ *
+ * Eventually, we may allow multiple objects per grid, or objects
+ * and terrain features in the same grid. XXX XXX XXX
+ *
+ * This function must handle blindness/hallucination.
+ */
+static int target_set_aux(int y, int x, int mode, cptr info)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ s16b this_o_idx, next_o_idx = 0;
+
+ cptr s1, s2, s3;
+
+ bool boring;
+
+ int feat;
+
+ int query;
+
+ char out_val[160];
+
+
+ /* Repeat forever */
+ while (1)
+ {
+ /* Paranoia */
+ query = ' ';
+
+ /* Assume boring */
+ boring = TRUE;
+
+ /* Default */
+ s1 = "You see ";
+ s2 = "";
+ s3 = "";
+
+ /* Hack -- under the player */
+ if ((y == p_ptr->py) && (x == p_ptr->px))
+ {
+ /* Description */
+ s1 = "You are ";
+
+ /* Preposition */
+ s2 = "on ";
+ }
+
+
+ /* Hack -- hallucination */
+ if (p_ptr->image)
+ {
+ cptr name = "something strange";
+
+ /* Display a message */
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Stop on everything but "return" */
+ if ((query != '\r') && (query != '\n')) break;
+
+ /* Repeat forever */
+ continue;
+ }
+
+
+ /* Actual monsters */
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ monster_race *r_ptr = race_inf(m_ptr);
+
+ /* Mimics special treatment -- looks like an object */
+ if ((r_ptr->flags9 & RF9_MIMIC) && (m_ptr->csleep))
+ {
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[m_ptr->hold_o_idx];
+
+ if (o_ptr->marked)
+ {
+ if (target_object(y, x, mode, info, &boring, o_ptr, out_val, &s1, &s2, &s3, &query)) break;
+ }
+ }
+ else
+ {
+ /* Visible */
+ if (m_ptr->ml)
+ {
+ bool recall = FALSE;
+
+ char m_name[80];
+
+ /* Not boring */
+ boring = FALSE;
+
+ /* Get the monster name ("a kobold") */
+ monster_desc(m_name, m_ptr, 0x08);
+
+ /* Hack -- track this monster race */
+ monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- health bar for this monster */
+ health_track(c_ptr->m_idx);
+
+ /* Hack -- handle stuff */
+ handle_stuff();
+
+ /* Interact */
+ while (1)
+ {
+ /* Recall */
+ if (recall)
+ {
+ /* Save */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Recall on screen */
+ screen_roff(m_ptr->r_idx, m_ptr->ego, 0);
+
+ /* Hack -- Complete the prompt (again) */
+ Term_addstr( -1, TERM_WHITE, format(" [r,%s]", info));
+
+ /* Command */
+ query = inkey();
+
+ /* Restore */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Normal */
+ else
+ {
+ cptr mstat;
+
+ switch (m_ptr->status)
+ {
+ case MSTATUS_NEUTRAL:
+ case MSTATUS_NEUTRAL_M:
+ case MSTATUS_NEUTRAL_P:
+ mstat = " (neutral) ";
+ break;
+ case MSTATUS_PET:
+ mstat = " (pet) ";
+ break;
+ case MSTATUS_FRIEND:
+ mstat = " (coaligned) ";
+ break;
+ case MSTATUS_COMPANION:
+ mstat = " (companion) ";
+ break;
+ default:
+ mstat = " ";
+ break;
+ }
+ if (m_ptr->mflag & MFLAG_PARTIAL) mstat = " (partial) ";
+
+ /* Describe, and prompt for recall */
+ sprintf(out_val, "%s%s%s%s (level %d, %s%s)%s%s[r,%s]",
+ s1, s2, s3, m_name,
+ m_ptr->level, look_mon_desc(c_ptr->m_idx),
+ (m_ptr->mflag & MFLAG_QUEST) ? ", quest" : "",
+ (m_ptr->smart & SM_CLONED ? " (clone)" : ""),
+ (mstat), info);
+
+ prt(out_val, 0, 0);
+
+ /* Place cursor */
+ move_cursor_relative(y, x);
+
+ /* Command */
+ query = inkey();
+ }
+
+ /* Normal commands */
+ if (query != 'r') break;
+
+ /* Toggle recall */
+ recall = !recall;
+ }
+
+ /* Always stop at "normal" keys */
+ if ((query != '\r') && (query != '\n') && (query != ' ')) break;
+
+ /* Sometimes stop at "space" key */
+ if ((query == ' ') && !(mode & (TARGET_LOOK))) break;
+
+ /* Change the intro */
+ s1 = "It is ";
+
+ /* Hack -- take account of gender */
+ if (r_ptr->flags1 & (RF1_FEMALE)) s1 = "She is ";
+ else if (r_ptr->flags1 & (RF1_MALE)) s1 = "He is ";
+
+ /* Use a preposition */
+ s2 = "carrying ";
+
+ /* Scan all objects being carried */
+ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ char o_name[80];
+
+ object_type *o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Obtain an object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe the object */
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, o_name, info);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Always stop at "normal" keys */
+ if ((query != '\r') && (query != '\n') && (query != ' ')) break;
+
+ /* Sometimes stop at "space" key */
+ if ((query == ' ') && !(mode & (TARGET_LOOK))) break;
+
+ /* Change the intro */
+ s2 = "also carrying ";
+ }
+
+ /* Double break */
+ if (this_o_idx) break;
+
+ /* Use a preposition */
+ s2 = "on ";
+ }
+ }
+ }
+
+
+
+ /* Scan all objects in the grid */
+ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_o_idx];
+
+ /* Acquire next object */
+ next_o_idx = o_ptr->next_o_idx;
+
+ /* Describe it */
+ if (o_ptr->marked)
+ {
+ if (target_object(y, x, mode, info, &boring, o_ptr, out_val, &s1, &s2, &s3, &query)) break;
+ }
+ }
+
+ /* Double break */
+ if (this_o_idx) break;
+
+ /* Actual traps */
+ if ((c_ptr->info & (CAVE_TRDT)) && c_ptr->t_idx)
+ {
+ cptr name = "a trap", s4;
+
+ /* Name trap */
+ if (t_info[c_ptr->t_idx].ident)
+ {
+ s4 = format("(%s)", t_name + t_info[c_ptr->t_idx].name);
+ }
+ else
+ {
+ s4 = "an unknown trap";
+ }
+
+ /* Display a message */
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, s4);
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Stop on everything but "return" */
+ if ((query != '\r') && (query != '\n')) break;
+
+ /* Repeat forever */
+ continue;
+ }
+
+ /* Feature (apply "mimic") */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[c_ptr->feat].mimic;
+ }
+
+ /* Require knowledge about grid, or ability to see grid */
+ if (!(c_ptr->info & (CAVE_MARK)) && !player_can_see_bold(y, x))
+ {
+ /* Forget feature */
+ feat = FEAT_NONE;
+ }
+
+ /* Terrain feature if needed */
+ if (boring || (feat >= FEAT_GLYPH))
+ {
+ cptr name;
+
+ /* Hack -- special handling for building doors */
+ if (feat == FEAT_SHOP)
+ {
+ name = st_name + st_info[c_ptr->special].name;
+ }
+ else
+ {
+ name = f_name + f_info[feat].name;
+ }
+
+ /* Hack -- handle unknown grids */
+ if (feat == FEAT_NONE) name = "unknown grid";
+
+ /* Pick a prefix */
+ if (*s2 &&
+ (((feat >= FEAT_MINOR_GLYPH) &&
+ (feat <= FEAT_PATTERN_XTRA2)) ||
+ (feat == FEAT_DIRT) ||
+ (feat == FEAT_GRASS) ||
+ (feat == FEAT_FLOWER))) s2 = "on ";
+ else if (*s2 && (feat == FEAT_SMALL_TREES)) s2 = "by ";
+ else if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in ";
+
+ /* Pick proper indefinite article */
+ s3 = (is_a_vowel(name[0])) ? "an " : "a ";
+
+ /* Hack -- special introduction for store & building doors */
+ if (feat == FEAT_SHOP)
+ {
+ s3 = "the entrance to the ";
+ }
+
+ if ((feat == FEAT_MORE) && c_ptr->special)
+ {
+ s3 = "";
+ name = d_text + d_info[c_ptr->special].text;
+ }
+
+ if (p_ptr->wild_mode && (feat == FEAT_TOWN))
+ {
+ s3 = "";
+ name = format("%s(%s)",
+ wf_name + wf_info[wild_map[y][x].feat].name,
+ wf_text + wf_info[wild_map[y][x].feat].text);
+ }
+
+ if ((feat == FEAT_FOUNTAIN) && (c_ptr->info & CAVE_IDNT))
+ {
+ object_kind *k_ptr;
+ int tv, sv;
+
+ if (c_ptr->special <= SV_POTION_LAST)
+ {
+ tv = TV_POTION;
+ sv = c_ptr->special;
+ }
+ else
+ {
+ tv = TV_POTION2;
+ sv = c_ptr->special - SV_POTION_LAST;
+ }
+
+ k_ptr = &k_info[lookup_kind(tv, sv)];
+ info = k_name + k_ptr->name;
+ }
+
+ /* Display a message */
+ if (!wizard)
+ {
+ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info);
+ }
+ else
+ {
+ sprintf(out_val, "%s%s%s%s [%s] (%d:%d:%d)",
+ s1, s2, s3, name, info,
+ c_ptr->feat, c_ptr->mimic, c_ptr->special);
+ }
+ prt(out_val, 0, 0);
+ move_cursor_relative(y, x);
+ query = inkey();
+
+ /* Always stop at "normal" keys */
+ if ((query != '\r') && (query != '\n') && (query != ' ')) break;
+ }
+
+ /* Stop on everything but "return" */
+ if ((query != '\r') && (query != '\n')) break;
+ }
+
+ /* Keep going */
+ return (query);
+}
+
+
+
+
+/*
+ * Handle "target" and "look".
+ *
+ * Note that this code can be called from "get_aim_dir()".
+ *
+ * All locations must be on the current panel. Consider the use of
+ * "panel_bounds()" to allow "off-panel" targets, perhaps by using
+ * some form of "scrolling" the map around the cursor. XXX XXX XXX
+ * That is, consider the possibility of "auto-scrolling" the screen
+ * while the cursor moves around. This may require changes in the
+ * "update_mon()" code to allow "visibility" even if off panel, and
+ * may require dynamic recalculation of the "temp" grid set.
+ *
+ * Hack -- targetting/observing an "outer border grid" may induce
+ * problems, so this is not currently allowed.
+ *
+ * The player can use the direction keys to move among "interesting"
+ * grids in a heuristic manner, or the "space", "+", and "-" keys to
+ * move through the "interesting" grids in a sequential manner, or
+ * can enter "location" mode, and use the direction keys to move one
+ * grid at a time in any direction. The "t" (set target) command will
+ * only target a monster (as opposed to a location) if the monster is
+ * target_able and the "interesting" mode is being used.
+ *
+ * The current grid is described using the "look" method above, and
+ * a new command may be entered at any time, but note that if the
+ * "TARGET_LOOK" bit flag is set (or if we are in "location" mode,
+ * where "space" has no obvious meaning) then "space" will scan
+ * through the description of the current grid until done, instead
+ * of immediately jumping to the next "interesting" grid. This
+ * allows the "target" command to retain its old semantics.
+ *
+ * The "*", "+", and "-" keys may always be used to jump immediately
+ * to the next (or previous) interesting grid, in the proper mode.
+ *
+ * The "return" key may always be used to scan through a complete
+ * grid description (forever).
+ *
+ * This command will cancel any old target, even if used from
+ * inside the "look" command.
+ */
+bool target_set(int mode)
+{
+ int i, d, m;
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+
+ bool done = FALSE;
+
+ bool flag = TRUE;
+
+ char query;
+
+ char info[80];
+
+ cave_type *c_ptr;
+
+ int screen_wid, screen_hgt;
+ int panel_wid, panel_hgt;
+
+ /* Get size */
+ get_screen_size(&screen_wid, &screen_hgt);
+
+ /* Calculate the amount of panel movement */
+ panel_hgt = screen_hgt / 2;
+ panel_wid = screen_wid / 2;
+
+ /* Cancel target */
+ target_who = 0;
+
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+
+ /* Prepare the "temp" array */
+ target_set_prepare(mode);
+
+ /* Start near the player */
+ m = 0;
+
+ /* Interact */
+ while (!done)
+ {
+ /* Interesting grids */
+ if (flag && temp_n)
+ {
+ y = temp_y[m];
+ x = temp_x[m];
+
+ /* Access */
+ c_ptr = &cave[y][x];
+
+ /* Allow target */
+ if (target_able(c_ptr->m_idx))
+ {
+ strcpy(info, "q,t,p,o,+,-,'dir'");
+ }
+
+ /* Dis-allow target */
+ else
+ {
+ strcpy(info, "q,p,o,+,-,'dir'");
+ }
+
+ /* Describe and Prompt */
+ query = target_set_aux(y, x, mode, info);
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+ /* Assume no "direction" */
+ d = 0;
+
+ /* Analyze */
+ switch (query)
+ {
+ case ESCAPE:
+ case 'q':
+ {
+ done = TRUE;
+ break;
+ }
+
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ if (target_able(c_ptr->m_idx))
+ {
+ health_track(c_ptr->m_idx);
+ target_who = c_ptr->m_idx;
+ target_row = y;
+ target_col = x;
+ done = TRUE;
+ }
+ else
+ {
+ bell();
+ }
+ break;
+ }
+
+ case ' ':
+ case '*':
+ case '+':
+ {
+ if (++m == temp_n)
+ {
+ m = 0;
+ if (!expand_list) done = TRUE;
+ }
+ break;
+ }
+
+ case '-':
+ {
+ if (m-- == 0)
+ {
+ m = temp_n - 1;
+ if (!expand_list) done = TRUE;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Fall through... */
+ }
+
+ case 'o':
+ {
+ flag = FALSE;
+ break;
+ }
+
+ case 'm':
+ {
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ d = get_keymap_dir(query);
+
+ if (!d) bell();
+ break;
+ }
+ }
+
+ /* Hack -- move around */
+ if (d)
+ {
+ /* Find a new monster */
+ i = target_pick(temp_y[m], temp_x[m], ddy[d], ddx[d]);
+
+ /* Scroll to find interesting grid */
+ if (i < 0)
+ {
+ int dy;
+ int dx;
+
+ dy = ddy[d];
+ dx = ddx[d];
+
+ /* Note panel change */
+ if (change_panel(dy, dx))
+ {
+ int ty = temp_y[m];
+ int tx = temp_x[m];
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+
+ /* Find a new monster */
+ i = target_pick(ty, tx, dy, dx);
+
+ /* Restore panel if needed */
+ if (i < 0)
+ {
+ /* Restore panel */
+ change_panel( -dy, -dx);
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+ }
+ }
+ }
+
+ /* Use that grids */
+ if (i >= 0) m = i;
+ }
+ }
+
+ /* Arbitrary grids */
+ else
+ {
+ /* Access */
+ c_ptr = &cave[y][x];
+
+ /* Default prompt */
+ strcpy(info, "q,t,p,m,+,-,'dir'");
+
+ /* Describe and Prompt (enable "TARGET_LOOK") */
+ query = target_set_aux(y, x, mode | TARGET_LOOK, info);
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+ /* Assume no direction */
+ d = 0;
+
+ /* Analyze the keypress */
+ switch (query)
+ {
+ case ESCAPE:
+ case 'q':
+ {
+ done = TRUE;
+ break;
+ }
+
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ target_who = -1;
+ target_row = y;
+ target_col = x;
+ done = TRUE;
+ break;
+ }
+
+ case ' ':
+ case '*':
+ case '+':
+ case '-':
+ {
+ break;
+ }
+
+ case 'p':
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+
+ case 'o':
+ {
+ break;
+ }
+
+ case 'm':
+ {
+ flag = TRUE;
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ d = get_keymap_dir(query);
+
+ if (!d) bell();
+ break;
+ }
+ }
+
+ /* Handle "direction" */
+ if (d)
+ {
+ int dy = ddy[d];
+ int dx = ddx[d];
+
+ /* Move */
+ y += dy;
+ x += dx;
+
+ /* Do not move horizontally if unnecessary */
+ if (((x < panel_col_min + panel_wid) && (dx > 0)) ||
+ ((x > panel_col_min + panel_wid) && (dx < 0)))
+ {
+ dx = 0;
+ }
+
+ /* Do not move vertically if unnecessary */
+ if (((y < panel_row_min + panel_hgt) && (dy > 0)) ||
+ ((y > panel_row_min + panel_hgt) && (dy < 0)))
+ {
+ dy = 0;
+ }
+ /* Apply the motion */
+ if ((y >= panel_row_min + screen_hgt) ||
+ (y < panel_row_min) ||
+ (x > panel_col_min + screen_wid) ||
+ (x < panel_col_min))
+ {
+ /* Change panel and recalculate interesting grids */
+ if (change_panel(dy, dx)) target_set_prepare(mode);
+ }
+
+ /* Boundary checks */
+ if (!wizard)
+ {
+ /* Hack -- Verify y */
+ if (y <= 0) y = 1;
+ else if (y >= cur_hgt - 1) y = cur_hgt - 2;
+
+ /* Hack -- Verify x */
+ if (x <= 0) x = 1;
+ else if (x >= cur_wid - 1) x = cur_wid - 2;
+ }
+ else
+ {
+ /* Hack -- Verify y */
+ if (y < 0) y = 0;
+ else if (y > cur_hgt - 1) y = cur_hgt - 1;
+
+ /* Hack -- Verify x */
+ if (x < 0) x = 0;
+ else if (x > cur_wid - 1) x = cur_wid - 1;
+ }
+ }
+ }
+ }
+
+ /* Forget */
+ temp_n = 0;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Failure to set target */
+ if (!target_who) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Get an "aiming direction" from the user.
+ *
+ * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
+ * "0" for "current target", and "-1" for "entry aborted".
+ *
+ * Note that "Force Target", if set, will pre-empt user interaction,
+ * if there is a usable target already set.
+ *
+ * Note that confusion over-rides any (explicit?) user choice.
+ */
+bool get_aim_dir(int *dp)
+{
+ int dir;
+
+ char command;
+
+ cptr p;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ if (repeat_pull(dp))
+ {
+ /* Confusion? */
+
+ /* Verify */
+ if (!(*dp == 5 && !target_okay()))
+ {
+ return (TRUE);
+ }
+ }
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = command_dir;
+
+ /* Hack -- auto-target if requested */
+ if (use_old_target && target_okay()) dir = 5;
+
+ /* Ask until satisfied */
+ while (!dir)
+ {
+ /* Choose a prompt */
+ if (!target_okay())
+ {
+ p = "Direction ('*' to choose a target, Escape to cancel)? ";
+ }
+ else
+ {
+ p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? ";
+ }
+
+ /* Get a command (or Cancel) */
+ if (!get_com(p, &command)) break;
+
+ /* Convert various keys to "standard" keys */
+ switch (command)
+ {
+ /* Use current target */
+ case 'T':
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ dir = 5;
+ break;
+ }
+
+ /* Set new target */
+ case '*':
+ {
+ if (target_set(TARGET_KILL)) dir = 5;
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ dir = get_keymap_dir(command);
+
+ break;
+ }
+ }
+
+ /* Verify requested targets */
+ if ((dir == 5) && !target_okay()) dir = 0;
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) return (FALSE);
+
+ /* Save the direction */
+ command_dir = dir;
+
+ /* Check for confusion */
+ if (p_ptr->confused)
+ {
+ /* XXX XXX XXX */
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ repeat_push(dir);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* A "valid" direction was entered */
+ return (TRUE);
+}
+
+
+
+/*
+ * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user,
+ * and place it into "command_dir", unless we already have one.
+ *
+ * This function should be used for all "repeatable" commands, such as
+ * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well
+ * as all commands which must reference a grid adjacent to the player,
+ * and which may not reference the grid under the player. Note that,
+ * for example, it is no longer possible to "disarm" or "open" chests
+ * in the same grid as the player.
+ *
+ * Direction "5" is illegal and will (cleanly) abort the command.
+ *
+ * This function tracks and uses the "global direction", and uses
+ * that as the "desired direction", to which "confusion" is applied.
+ */
+bool get_rep_dir(int *dp)
+{
+ int dir;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ if (repeat_pull(dp))
+ {
+ return (TRUE);
+ }
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = command_dir;
+
+ /* Get a direction */
+ while (!dir)
+ {
+ char ch;
+
+ /* Get a command (or Cancel) */
+ if (!get_com("Direction (Escape to cancel)? ", &ch)) break;
+
+ /* Look up the direction */
+ dir = get_keymap_dir(ch);
+
+ /* Oops */
+ if (!dir) bell();
+ }
+
+ /* Prevent weirdness */
+ if (dir == 5) dir = 0;
+
+ /* Aborted */
+ if (!dir) return (FALSE);
+
+ /* Save desired direction */
+ command_dir = dir;
+
+ /* Apply "confusion" */
+ if (p_ptr->confused)
+ {
+ /* Standard confusion */
+ if (rand_int(100) < 75)
+ {
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ repeat_push(dir);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+int get_chaos_patron(void)
+{
+ return (((p_ptr->age) + (p_ptr->sc)) % MAX_PATRON);
+}
+
+
+void gain_level_reward(int chosen_reward)
+{
+ object_type *q_ptr;
+ object_type forge;
+ char wrath_reason[32] = "";
+ int nasty_chance = 6;
+ int dummy = 0, dummy2 = 0;
+ int type, effect;
+
+
+#if 0
+ if (!chosen_reward)
+ {
+ if (multi_rew) return;
+ else multi_rew = TRUE;
+ }
+#endif
+
+ if (p_ptr->lev == 13) nasty_chance = 2;
+ else if (!(p_ptr->lev % 13)) nasty_chance = 3;
+ else if (!(p_ptr->lev % 14)) nasty_chance = 12;
+
+ if (randint(nasty_chance) == 1)
+ type = randint(20); /* Allow the 'nasty' effects */
+ else
+ type = randint(15) + 5; /* Or disallow them */
+
+ if (type < 1) type = 1;
+ if (type > 20) type = 20;
+ type--;
+
+
+ sprintf(wrath_reason, "the Wrath of %s",
+ chaos_patrons[p_ptr->chaos_patron]);
+
+ effect = chaos_rewards[p_ptr->chaos_patron][type];
+
+ if ((randint(6) == 1) && !chosen_reward)
+ {
+ msg_format("%^s rewards you with a corruption!",
+ chaos_patrons[p_ptr->chaos_patron]);
+ (void)gain_random_corruption(0);
+ return;
+ }
+
+ switch (chosen_reward ? chosen_reward : effect)
+ {
+ case REW_POLY_SLF :
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou needst a new form, mortal!'");
+ do_poly_self();
+ break;
+
+ case REW_GAIN_EXP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Well done, mortal! Lead on!'");
+ if (p_ptr->exp < PY_MAX_EXP)
+ {
+ s32b ee = (p_ptr->exp / 2) + 10;
+ if (ee > 100000L) ee = 100000L;
+ msg_print("You feel more experienced.");
+ gain_exp(ee);
+ }
+ break;
+
+ case REW_LOSE_EXP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou didst not deserve that, slave.'");
+ lose_exp(p_ptr->exp / 6);
+ break;
+
+ case REW_GOOD_OBJ:
+ msg_format("The voice of %s whispers:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Use my gift wisely.'");
+ acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE);
+ break;
+
+ case REW_GREA_OBJ:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Use my gift wisely.'");
+ acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE);
+ break;
+
+ case REW_CHAOS_WP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thy deed hath earned thee a worthy blade.'");
+ /* Get local object */
+ q_ptr = &forge;
+ dummy = TV_SWORD;
+ switch (randint(p_ptr->lev))
+ {
+ case 0:
+ case 1:
+ dummy2 = SV_DAGGER;
+ break;
+ case 2:
+ case 3:
+ dummy2 = SV_MAIN_GAUCHE;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ dummy2 = SV_RAPIER;
+ break;
+ case 7:
+ case 8:
+ dummy2 = SV_SMALL_SWORD;
+ break;
+ case 9:
+ case 10:
+ dummy2 = SV_BASILLARD;
+ break;
+ case 11:
+ case 12:
+ case 13:
+ dummy2 = SV_SHORT_SWORD;
+ break;
+ case 14:
+ case 15:
+ dummy2 = SV_SABRE;
+ break;
+ case 16:
+ case 17:
+ dummy2 = SV_CUTLASS;
+ break;
+ case 18:
+ case 19:
+ dummy2 = SV_KHOPESH;
+ break;
+ case 20:
+ dummy2 = SV_TULWAR;
+ break;
+ case 21:
+ dummy2 = SV_BROAD_SWORD;
+ break;
+ case 22:
+ case 23:
+ dummy2 = SV_LONG_SWORD;
+ break;
+ case 24:
+ case 25:
+ dummy2 = SV_SCIMITAR;
+ break;
+ case 26:
+ case 27:
+ dummy2 = SV_KATANA;
+ break;
+ case 28:
+ case 29:
+ dummy2 = SV_BASTARD_SWORD;
+ break;
+ case 30:
+ dummy2 = SV_GREAT_SCIMITAR;
+ break;
+ case 31:
+ dummy2 = SV_CLAYMORE;
+ break;
+ case 32:
+ dummy2 = SV_ESPADON;
+ break;
+ case 33:
+ dummy2 = SV_TWO_HANDED_SWORD;
+ break;
+ case 34:
+ dummy2 = SV_FLAMBERGE;
+ break;
+ case 35:
+ case 36:
+ dummy2 = SV_EXECUTIONERS_SWORD;
+ break;
+ case 37:
+ dummy2 = SV_ZWEIHANDER;
+ break;
+ default:
+ dummy2 = SV_BLADE_OF_CHAOS;
+ }
+
+ object_prep(q_ptr, lookup_kind(dummy, dummy2));
+ q_ptr->to_h = 3 + (randint(dun_level)) % 10;
+ q_ptr->to_d = 3 + (randint(dun_level)) % 10;
+ random_resistance(q_ptr, FALSE, (randint(34) + 4));
+ q_ptr->name2 = EGO_CHAOTIC;
+
+ /* Apply the ego */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop it in the dungeon */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+ break;
+
+ case REW_GOOD_OBS:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thy deed hath earned thee a worthy reward.'");
+ acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, FALSE, FALSE);
+ break;
+
+ case REW_GREA_OBS:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Behold, mortal, how generously I reward thy loyalty.'");
+ acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE);
+ break;
+
+ case REW_TY_CURSE:
+ msg_format("The voice of %s thunders:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou art growing arrogant, mortal.'");
+ activate_ty_curse();
+ break;
+
+ case REW_SUMMON_M:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'My pets, destroy the arrogant mortal!'");
+ for (dummy = 0; dummy < randint(5) + 1; dummy++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ }
+ break;
+
+ case REW_H_SUMMON:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou needst worthier opponents!'");
+ activate_hi_summon();
+ break;
+
+ case REW_DO_HAVOC:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Death and destruction! This pleaseth me!'");
+ call_chaos();
+ break;
+
+ case REW_GAIN_ABL:
+ msg_format("The voice of %s rings out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Stay, mortal, and let me mold thee.'");
+ if ((randint(3) == 1) &&
+ !(chaos_stats[p_ptr->chaos_patron] < 0))
+ do_inc_stat(chaos_stats[p_ptr->chaos_patron]);
+ else
+ do_inc_stat(randint(6) - 1);
+ break;
+
+ case REW_LOSE_ABL:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'I grow tired of thee, mortal.'");
+ if ((randint(3) == 1) &&
+ !(chaos_stats[p_ptr->chaos_patron] < 0))
+ do_dec_stat(chaos_stats[p_ptr->chaos_patron], STAT_DEC_NORMAL);
+ else
+ (void)do_dec_stat(randint(6) - 1, STAT_DEC_NORMAL);
+ break;
+
+ case REW_RUIN_ABL:
+ msg_format("The voice of %s thunders:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou needst a lesson in humility, mortal!'");
+ msg_print("You feel less powerful!");
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void)dec_stat(dummy, 10 + randint(15), TRUE);
+ }
+ break;
+
+ case REW_POLY_WND:
+ msg_format("You feel the power of %s touch you.",
+ chaos_patrons[p_ptr->chaos_patron]);
+ do_poly_wounds();
+ break;
+
+ case REW_AUGM_ABL:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Receive this modest gift from me!'");
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) do_inc_stat(dummy);
+ }
+ break;
+
+ case REW_HURT_LOT:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Suffer, pathetic fool!'");
+ fire_ball(GF_DISINTEGRATE, 0, (p_ptr->lev * 4), 4);
+ take_hit(p_ptr->lev * 4, wrath_reason);
+ break;
+
+ case REW_HEAL_FUL:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Rise, my servant!'");
+ restore_level();
+ (void)set_poisoned(0);
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ hp_player(5000);
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) do_res_stat(dummy, TRUE);
+ }
+ break;
+
+ case REW_CURSE_WP:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou reliest too much on thy weapon.'");
+ (void)curse_weapon();
+ break;
+
+ case REW_CURSE_AR:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Thou reliest too much on thine equipment.'");
+ (void)curse_armor();
+ break;
+
+ case REW_PISS_OFF:
+ msg_format("The voice of %s whispers:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Now thou shalt pay for annoying me.'");
+ switch (randint(4))
+ {
+ case 1:
+ activate_ty_curse();
+ break;
+ case 2:
+ activate_hi_summon();
+ break;
+ case 3:
+ if (randint(2) == 1) (void)curse_weapon();
+ else (void)curse_armor();
+ break;
+ default:
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) dec_stat(dummy, 10 + randint(15), TRUE);
+ }
+ break;
+ }
+ break;
+
+ case REW_WRATH:
+ msg_format("The voice of %s thunders:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Die, mortal!'");
+ take_hit(p_ptr->lev * 4, wrath_reason);
+ for (dummy = 0; dummy < 6; dummy++)
+ {
+ (void) dec_stat(dummy, 10 + randint(15), FALSE);
+ }
+ activate_hi_summon();
+ activate_ty_curse();
+ if (randint(2) == 1) (void)curse_weapon();
+ if (randint(2) == 1) (void)curse_armor();
+ break;
+
+ case REW_DESTRUCT:
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Death and destruction! This pleaseth me!'");
+ destroy_area(p_ptr->py, p_ptr->px, 25, TRUE, FALSE);
+ }
+ break;
+
+ case REW_GENOCIDE:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Let me relieve thee of thine oppressors!'");
+ (void) genocide(FALSE);
+ break;
+
+ case REW_MASS_GEN:
+ msg_format("The voice of %s booms out:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_print("'Let me relieve thee of thine oppressors!'");
+ (void) mass_genocide(FALSE);
+ break;
+
+ case REW_DISPEL_C:
+ msg_format("You can feel the power of %s assault your enemies!",
+ chaos_patrons[p_ptr->chaos_patron]);
+ (void) dispel_monsters(p_ptr->lev * 4);
+ break;
+
+ case REW_IGNORE:
+ msg_format("%s ignores you.",
+ chaos_patrons[p_ptr->chaos_patron]);
+ break;
+
+ case REW_SER_DEMO:
+ msg_format("%s rewards you with a demonic servant!", chaos_patrons[p_ptr->chaos_patron]);
+ if (!(summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE)))
+ msg_print("Nobody ever turns up...");
+ break;
+
+ case REW_SER_MONS:
+ msg_format("%s rewards you with a servant!", chaos_patrons[p_ptr->chaos_patron]);
+ if (!(summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_NO_UNIQUES, FALSE)))
+ msg_print("Nobody ever turns up...");
+ break;
+
+ case REW_SER_UNDE:
+ msg_format("%s rewards you with an undead servant!", chaos_patrons[p_ptr->chaos_patron]);
+ if (!(summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE)))
+ msg_print("Nobody ever turns up...");
+ break;
+
+ default:
+ msg_format("The voice of %s stammers:",
+ chaos_patrons[p_ptr->chaos_patron]);
+ msg_format("'Uh... uh... the answer's %d/%d, what's the question?'", type,
+ effect );
+ break;
+ }
+}
+
+
+/*
+ * old -- from PsiAngband.
+ */
+bool tgt_pt(int *x, int *y)
+{
+ char ch = 0;
+ int d, cu, cv;
+ int screen_wid, screen_hgt;
+ bool success = FALSE;
+
+ *x = p_ptr->px;
+ *y = p_ptr->py;
+
+ /* Get size */
+ get_screen_size(&screen_wid, &screen_hgt);
+
+ cu = Term->scr->cu;
+ cv = Term->scr->cv;
+ Term->scr->cu = 0;
+ Term->scr->cv = 1;
+ msg_print("Select a point and press space.");
+
+ while ((ch != 27) && (ch != ' '))
+ {
+ move_cursor_relative(*y, *x);
+ ch = inkey();
+ switch (ch)
+ {
+ case 27:
+ break;
+ case ' ':
+ success = TRUE;
+ break;
+ default:
+ /* Look up the direction */
+ d = get_keymap_dir(ch);
+
+ if (!d) break;
+
+ *x += ddx[d];
+ *y += ddy[d];
+
+ /* Hack -- Verify x */
+ if ((*x >= cur_wid - 1) || (*x >= panel_col_min + screen_wid)) (*x)--;
+ else if ((*x <= 0) || (*x <= panel_col_min)) (*x)++;
+
+ /* Hack -- Verify y */
+ if ((*y >= cur_hgt - 1) || (*y >= panel_row_min + screen_hgt)) (*y)--;
+ else if ((*y <= 0) || (*y <= panel_row_min)) (*y)++;
+
+ break;
+ }
+ }
+
+ Term->scr->cu = cu;
+ Term->scr->cv = cv;
+ Term_fresh();
+ return success;
+}
+
+
+bool gain_random_corruption(int choose_mut)
+{
+ exec_lua("gain_corruption()");
+ return (FALSE);
+}
+
+bool lose_corruption(int choose_mut)
+{
+ exec_lua("lose_corruption()");
+ return (FALSE);
+}
+
+bool lose_all_corruptions(void)
+{
+ exec_lua("lose_all_corruptions()");
+ return (FALSE);
+}
+
+bool get_hack_dir(int *dp)
+{
+ int dir;
+ cptr p;
+ char command;
+
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = 0;
+
+ /* (No auto-targetting */
+
+ /* Ask until satisfied */
+ while (!dir)
+ {
+ /* Choose a prompt */
+ if (!target_okay())
+ {
+ p = "Direction ('*' to choose a target, Escape to cancel)? ";
+ }
+ else
+ {
+ p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? ";
+ }
+
+ /* Get a command (or Cancel) */
+ if (!get_com(p, &command)) break;
+
+ /* Convert various keys to "standard" keys */
+ switch (command)
+ {
+ /* Use current target */
+ case 'T':
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ dir = 5;
+ break;
+ }
+
+ /* Set new target */
+ case '*':
+ {
+ if (target_set(TARGET_KILL)) dir = 5;
+ break;
+ }
+
+ default:
+ {
+ /* Look up the direction */
+ dir = get_keymap_dir(command);
+
+ break;
+ }
+ }
+
+ /* Verify requested targets */
+ if ((dir == 5) && !target_okay()) dir = 0;
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) return (FALSE);
+
+ /* Save the direction */
+ command_dir = dir;
+
+ /* Check for confusion */
+ if (p_ptr->confused)
+ {
+ /* XXX XXX XXX */
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+ /* A "valid" direction was entered */
+ return (TRUE);
+}
+
+/*
+ * Do we have at least one corruption?
+ */
+bool got_corruptions()
+{
+ int i, max;
+
+ max = exec_lua("return __corruptions_max");
+
+ for (i = 0; i < max; i++)
+ {
+ if (exec_lua(format("if test_depend_corrupt(%d) == TRUE then return TRUE else return FALSE end", i)))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Dump the corruption list
+ */
+void dump_corruptions(FILE *fff, bool color)
+{
+ int i, max;
+
+ if (!fff) return;
+
+ max = exec_lua("return __corruptions_max");
+
+ for (i = 0; i < max; i++)
+ {
+ if (exec_lua(format("if test_depend_corrupt(%d) == TRUE then return TRUE else return FALSE end", i)))
+ {
+ int c = exec_lua(format("return __corruptions[%d].color", i));
+
+ if (color)
+ fprintf(fff, "#####%c%s:\n", conv_color[c], string_exec_lua(format("return __corruptions[%d].name", i)));
+ else
+ fprintf(fff, "%s:\n", string_exec_lua(format("return __corruptions[%d].name", i)));
+ fprintf(fff, "%s\n", string_exec_lua(format("return __corruptions[%d].desc", i)));
+ }
+ }
+}
+
+/*
+ * Set "p_ptr->grace", notice observable changes
+ */
+void set_grace(s32b v)
+{
+ if (v < -300000) v = -300000;
+ if (v > 300000) v = 300000;
+ p_ptr->grace = v;
+ p_ptr->update |= PU_BONUS;
+ p_ptr->redraw |= (PR_PIETY);
+ handle_stuff();
+}
+
+bool test_object_wish(char *name, object_type *o_ptr, object_type *forge, char *what)
+{
+ int i, j, jb, save_aware;
+ char buf[200];
+
+ /* try all objects, this *IS* a very ugly and slow method :( */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ o_ptr = forge;
+
+ if (!k_ptr->name) continue;
+ if (k_ptr->flags3 & TR3_NORM_ART) continue;
+ if (k_ptr->flags3 & TR3_INSTA_ART) continue;
+ if (k_ptr->tval == TV_GOLD) continue;
+
+ object_prep(o_ptr, i);
+ o_ptr->name1 = 0;
+ o_ptr->name2 = 0;
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ /* Hack : aware status must be restored after describing the item name */
+ save_aware = k_ptr->aware;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ object_desc(buf, o_ptr, FALSE, 0);
+ strlower(buf);
+ k_ptr->aware = save_aware;
+
+ if (strstr(name, buf) ||
+ /* Hack hack hackery */
+ (o_ptr->tval == TV_ROD_MAIN && strstr(name, "rod of")))
+ {
+#if 0 // DGDGDGDG
+ /* You can't wish for a wish! */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->sval == SV_STAFF_WISHING))
+ {
+ msg_format("You cannot %s for a wish!", what);
+ return FALSE;
+ }
+#endif
+ /* try all ego */
+ for (j = max_e_idx - 1; j >= 0; j--)
+ {
+ ego_item_type *e_ptr = &e_info[j];
+ bool ok = FALSE;
+
+ if (j && !e_ptr->name) continue;
+
+ /* Must have the correct fields */
+ if (j)
+ {
+ int z;
+
+ for (z = 0; z < 6; z++)
+ {
+ if (e_ptr->tval[z] == k_ptr->tval)
+ {
+ if ((e_ptr->min_sval[z] <= k_ptr->sval) &&
+ (e_ptr->max_sval[z] >= k_ptr->sval)) ok = TRUE;
+ }
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ continue;
+ }
+ }
+
+ /* try all ego */
+ for (jb = max_e_idx - 1; jb >= 0; jb--)
+ {
+ ego_item_type *eb_ptr = &e_info[jb];
+ bool ok = FALSE;
+
+ if (jb && !eb_ptr->name) continue;
+
+ if (j && jb && (e_ptr->before == eb_ptr->before)) continue;
+
+ /* Must have the correct fields */
+ if (jb)
+ {
+ int z;
+
+ for (z = 0; z < 6; z++)
+ {
+ if (eb_ptr->tval[z] == k_ptr->tval)
+ {
+ if ((eb_ptr->min_sval[z] <= k_ptr->sval) &&
+ (eb_ptr->max_sval[z] >= k_ptr->sval)) ok = TRUE;
+ }
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ continue;
+ }
+ }
+
+ object_prep(o_ptr, i);
+ o_ptr->name1 = 0;
+ o_ptr->name2 = j;
+ o_ptr->name2b = jb;
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ object_desc(buf, o_ptr, FALSE, 0);
+ strlower(buf);
+
+ if (!stricmp(buf, name))
+ {
+ /* Don't search any more */
+ return TRUE;
+ }
+ else
+ {
+ /* Restore again the aware status */
+ k_ptr->aware = save_aware;
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+void clean_wish_name(char *buf, char *name)
+{
+ char *p;
+ int i, j;
+
+ /* Lowercase the wish */
+ strlower(buf);
+
+ /* Nuke uneccesary spaces */
+ p = buf;
+ while (*p == ' ') p++;
+ i = 0;
+ j = 0;
+ while (p[i])
+ {
+ if ((p[i] == ' ') && (p[i + 1] == ' '))
+ {
+ i++;
+ continue;
+ }
+ name[j++] = p[i++];
+ }
+ name[j++] = '\0';
+ if (j)
+ {
+ j--;
+ while (j && (name[j] == ' '))
+ {
+ name[j] = '\0';
+ j--;
+ }
+ }
+}
+
+/*
+ * Allow the player to make a wish
+ */
+void make_wish(void)
+{
+ char buf[200], name[200], *mname;
+ int i, j, mstatus = MSTATUS_ENEMY;
+ object_type forge, *o_ptr = &forge;
+
+ /* Make an empty string */
+ buf[0] = 0;
+
+ /* Ask for the wish */
+ if (!get_string("Wish for what? ", buf, 80)) return;
+
+ clean_wish_name(buf, name);
+
+ /* You can't wish for a wish! */
+ if (strstr(name, "wish"))
+ {
+ msg_print("You can't wish for a wish!");
+ return;
+ }
+
+ if (test_object_wish(name, o_ptr, &forge, "wish"))
+ {
+ msg_print("Your wish becomes truth!");
+
+ /* Give it to the player */
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+
+ return;
+ }
+
+ /* try monsters */
+ if (prefix(name, "enemy "))
+ {
+ mstatus = MSTATUS_ENEMY;
+ mname = name + 6;
+ }
+ else if (prefix(name, "neutral "))
+ {
+ mstatus = MSTATUS_NEUTRAL;
+ mname = name + 8;
+ }
+ else if (prefix(name, "friendly "))
+ {
+ mstatus = MSTATUS_FRIEND;
+ mname = name + 9;
+ }
+ else if (prefix(name, "pet "))
+ {
+ mstatus = MSTATUS_PET;
+ mname = name + 4;
+ }
+ else if (prefix(name, "companion "))
+ {
+ if (can_create_companion()) mstatus = MSTATUS_COMPANION;
+ else mstatus = MSTATUS_PET;
+ mname = name + 10;
+ }
+ else mname = name;
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ if (!r_ptr->name) continue;
+
+ if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue;
+ if (r_ptr->flags9 & RF9_NEVER_GENE) continue;
+ if (r_ptr->flags1 & RF1_UNIQUE) continue;
+
+ sprintf(buf, "%s", r_ptr->name + r_name);
+ strlower(buf);
+
+ if (strstr(mname, buf))
+ {
+ /* try all ego */
+ for (j = max_re_idx - 1; j >= 0; j--)
+ {
+ monster_ego *re_ptr = &re_info[j];
+
+ if (j && !re_ptr->name) continue;
+
+ if (!mego_ok(i, j)) continue;
+
+ if (j)
+ {
+ if (re_ptr->before) sprintf(buf, "%s %s", re_name + re_ptr->name, r_ptr->name + r_name);
+ else sprintf(buf, "%s %s", r_ptr->name + r_name, re_name + re_ptr->name);
+ }
+ else
+ {
+ sprintf(buf, "%s", r_ptr->name + r_name);
+ }
+ strlower(buf);
+
+ if (!stricmp(mname, buf))
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 5, 0);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ /* Create the monster */
+ if (place_monster_one(wy, wx, i, j, FALSE, mstatus))
+ msg_print("Your wish becomes truth!");
+
+ /* Don't search any more */
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Corrupted have a 1/3 chance of losing a mutation each time this is called,
+ * assuming they have any in the first place
+ */
+void corrupt_corrupted(void)
+{
+ if (magik(45))
+ {
+ lose_corruption(0);
+ }
+ else
+ {
+ gain_random_corruption(0);
+ }
+
+ /* We are done. */
+ return;
+}
+
+/*
+ * Change to an other class
+ */
+void switch_class(int sclass)
+{
+ p_ptr->pclass = sclass;
+ cp_ptr = &class_info[p_ptr->pclass];
+}
+
+/*
+ * Change to an other subclass
+ */
+void switch_subclass(int sclass)
+{
+ p_ptr->pspec = sclass;
+ spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec];
+}
+
+/*
+ * Change to an other subrace
+ */
+void switch_subrace(int racem, bool copy_old)
+{
+ if ((racem < 0) && (racem >= max_rmp_idx)) return;
+
+ /* If we switch to the saved subrace, we copy over the old subrace data */
+ if (copy_old && (racem == SUBRACE_SAVE))
+ {
+ s32b old_title = race_mod_info[SUBRACE_SAVE].title;
+ s32b old_desc = race_mod_info[SUBRACE_SAVE].desc;
+
+ COPY(&race_mod_info[SUBRACE_SAVE], &race_mod_info[p_ptr->pracem], player_race_mod);
+
+ race_mod_info[SUBRACE_SAVE].title = old_title;
+ race_mod_info[SUBRACE_SAVE].desc = old_desc;
+ strcpy(race_mod_info[SUBRACE_SAVE].title + rmp_name, race_mod_info[p_ptr->pracem].title + rmp_name);
+ }
+
+ p_ptr->pracem = racem;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+}
+
+cptr get_subrace_title(int racem)
+{
+ return race_mod_info[racem].title + rmp_name;
+}
+
+void set_subrace_title(int racem, cptr name)
+{
+ strcpy(race_mod_info[racem].title + rmp_name, name);
+}
+
+/*
+ * Rebirth, recalc hp & exp/level
+ */
+void do_rebirth()
+{
+ /* Experience factor */
+ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp;
+
+ /* Hitdice */
+ p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp;
+
+ /* Recalc HP */
+ do_cmd_rerate();
+
+ /* Change the level if needed */
+ check_experience();
+ p_ptr->max_plv = p_ptr->lev;
+
+ /* Redraw/calc stuff */
+ p_ptr->redraw |= (PR_BASIC);
+ p_ptr->update |= (PU_BONUS);
+ handle_stuff();
+
+ lite_spot(p_ptr->py, p_ptr->px);
+}
+
+/*
+ * Quick mimic name to index function
+ */
+int resolve_mimic_name(cptr name)
+{
+ s32b idx;
+
+ call_lua("resolve_mimic_name", "(s)", "d", name, &idx);
+ return idx;
+}
diff --git a/src/z-form.c b/src/z-form.c
new file mode 100644
index 00000000..1ad92156
--- /dev/null
+++ b/src/z-form.c
@@ -0,0 +1,831 @@
+/* File: z-form.c */
+
+/* Purpose: Low level text formatting -BEN- */
+
+#include "z-form.h"
+
+#include "z-util.h"
+#include "z-virt.h"
+
+
+/*
+ * Here is some information about the routines in this file.
+ *
+ * In general, the following routines take a "buffer", a "max length",
+ * a "format string", and some "arguments", and use the format string
+ * and the arguments to create a (terminated) string in the buffer
+ * (using only the first "max length" bytes), and return the "length"
+ * of the resulting string, not including the (mandatory) terminator.
+ *
+ * The format strings allow the basic "sprintf()" format sequences, though
+ * some of them are processed slightly more carefully or portably, as well
+ * as a few "special" sequences, including the "%r" and "%v" sequences, and
+ * the "capilitization" sequences of "%C", "%S", and "%V".
+ *
+ * Note that some "limitations" are enforced by the current implementation,
+ * for example, no "format sequence" can exceed 100 characters, including any
+ * "length" restrictions, and the result of combining and "format sequence"
+ * with the relevent "arguments" must not exceed 1000 characters.
+ *
+ * These limitations could be fixed by stealing some of the code from,
+ * say, "vsprintf()" and placing it into my "vstrnfmt()" function.
+ *
+ * Note that a "^" inside a "format sequence" causes the first non-space
+ * character in the string resulting from the combination of the format
+ * sequence and the argument(s) to be "capitalized" if possible. Note
+ * that the "^" character is removed before the "standard" formatting
+ * routines are called. Likewise, a "*" inside a "format sequence" is
+ * removed from the "format sequence", and replaced by the textual form
+ * of the next argument in the argument list. See examples below.
+ *
+ * Legal format characters: %,n,p,c,s,d,i,o,u,X,x,E,e,F,f,G,g,r,v.
+ *
+ * Format("%%")
+ * Append the literal "%".
+ * No legal modifiers.
+ *
+ * Format("%n", int *np)
+ * Save the current length into (*np).
+ * No legal modifiers.
+ *
+ * Format("%p", vptr v)
+ * Append the pointer "v" (implementation varies).
+ * No legal modifiers.
+ *
+ * Format("%E", double r)
+ * Format("%F", double r)
+ * Format("%G", double r)
+ * Format("%e", double r)
+ * Format("%f", double r)
+ * Format("%g", double r)
+ * Append the double "r", in various formats.
+ *
+ * Format("%ld", long int i)
+ * Append the long integer "i".
+ *
+ * Format("%d", int i)
+ * Append the integer "i".
+ *
+ * Format("%lu", unsigned long int i)
+ * Append the unsigned long integer "i".
+ *
+ * Format("%u", unsigned int i)
+ * Append the unsigned integer "i".
+ *
+ * Format("%lo", unsigned long int i)
+ * Append the unsigned long integer "i", in octal.
+ *
+ * Format("%o", unsigned int i)
+ * Append the unsigned integer "i", in octal.
+ *
+ * Format("%lX", unsigned long int i)
+ * Note -- use all capital letters
+ * Format("%lx", unsigned long int i)
+ * Append the unsigned long integer "i", in hexidecimal.
+ *
+ * Format("%X", unsigned int i)
+ * Note -- use all capital letters
+ * Format("%x", unsigned int i)
+ * Append the unsigned integer "i", in hexidecimal.
+ *
+ * Format("%c", char c)
+ * Append the character "c".
+ * Do not use the "+" or "0" flags.
+ *
+ * Format("%s", cptr s)
+ * Append the string "s".
+ * Do not use the "+" or "0" flags.
+ * Note that a "NULL" value of "s" is converted to the empty string.
+ *
+ * Format("%V", vptr v)
+ * Note -- possibly significant mode flag
+ * Format("%v", vptr v)
+ * Append the object "v", using the current "user defined print routine".
+ * User specified modifiers, often ignored.
+ *
+ * Format("%r", vstrnfmt_aux_func *fp)
+ * Set the "user defined print routine" (vstrnfmt_aux) to "fp".
+ * No legal modifiers.
+ *
+ *
+ * For examples below, assume "int n = 0; int m = 100; char buf[100];",
+ * plus "char *s = NULL;", and unknown values "char *txt; int i;".
+ *
+ * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a
+ * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);".
+ *
+ * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar
+ * effect as "strncpy(buf, txt, 16); buf[15] = '\0';".
+ *
+ * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have
+ * a similar effect as "strcpy(buf, txt)" but with bounds checking.
+ *
+ * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow
+ * multiple "appends" to "buf" (at the cost of losing the max-length info).
+ *
+ * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow
+ * multiple bounded "appends" to "buf", with constant access to "strlen(buf)".
+ *
+ * For example: "format("The %r%v was destroyed!", obj_desc, obj);"
+ * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description"
+ * of the given object to the given buffer, and return the total length)
+ * will return a "useful message" about the object "obj", for example,
+ * "The Large Shield was destroyed!".
+ *
+ * For example: "format("%^-.*s", i, txt)" will produce a string containing
+ * the first "i" characters of "txt", left justified, with the first non-space
+ * character capitilized, if reasonable.
+ */
+
+
+
+
+
+/*
+ * The "type" of the "user defined print routine" pointer
+ */
+typedef uint (*vstrnfmt_aux_func)(char *buf, uint max, cptr fmt, vptr arg);
+
+/*
+ * The "default" user defined print routine. Ignore the "fmt" string.
+ */
+static uint vstrnfmt_aux_dflt(char *buf, uint max, cptr fmt, vptr arg)
+{
+ uint len;
+ char tmp[32];
+
+ /* XXX XXX */
+ fmt = fmt ? fmt : 0;
+
+ /* Pointer display */
+ sprintf(tmp, "<<%p>>", arg);
+ len = strlen(tmp);
+ if (len >= max) len = max - 1;
+ tmp[len] = '\0';
+ strcpy(buf, tmp);
+ return (len);
+}
+
+/*
+ * The "current" user defined print routine. It can be changed
+ * dynamically by sending the proper "%r" sequence to "vstrnfmt()"
+ */
+static vstrnfmt_aux_func vstrnfmt_aux = vstrnfmt_aux_dflt;
+
+
+
+/*
+ * Basic "vararg" format function.
+ *
+ * This function takes a buffer, a max byte count, a format string, and
+ * a va_list of arguments to the format string, and uses the format string
+ * and the arguments to create a string to the buffer. The string is
+ * derived from the format string and the arguments in the manner of the
+ * "sprintf()" function, but with some extra "format" commands. Note that
+ * this function will never use more than the given number of bytes in the
+ * buffer, preventing messy invalid memory references. This function then
+ * returns the total number of non-null bytes written into the buffer.
+ *
+ * Method: Let "str" be the (unlimited) created string, and let "len" be the
+ * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of
+ * "str" into "buf", place "\0" into buf[len], and return "len".
+ *
+ * In English, we do a sprintf() into "buf", a buffer with size "max",
+ * and we return the resulting value of "strlen(buf)", but we allow some
+ * special format commands, and we are more careful than "sprintf()".
+ *
+ * Typically, "max" is in fact the "size" of "buf", and thus represents
+ * the "number" of chars in "buf" which are ALLOWED to be used. An
+ * alternative definition would have required "buf" to hold at least
+ * "max+1" characters, and would have used that extra character only
+ * in the case where "buf" was too short for the result. This would
+ * give an easy test for "overflow", but a less "obvious" semantics.
+ *
+ * Note that if the buffer was "too short" to hold the result, we will
+ * always return "max-1", but we also return "max-1" if the buffer was
+ * "just long enough". We could have returned "max" if the buffer was
+ * too short, not written a null, and forced the programmer to deal with
+ * this special case, but I felt that it is better to at least give a
+ * "usable" result when the buffer was too long instead of either giving
+ * a memory overwrite like "sprintf()" or a non-terminted string like
+ * "strncpy()". Note that "strncpy()" also "null-pads" the result.
+ *
+ * Note that in most cases "just long enough" is probably "too short".
+ *
+ * We should also consider extracting and processing the "width" and other
+ * "flags" by hand, it might be more "accurate", and it would allow us to
+ * remove the limit (1000 chars) on the result of format sequences.
+ *
+ * Also, some sequences, such as "%+d" by hand, do not work on all machines,
+ * and could thus be correctly handled here.
+ *
+ * Error detection in this routine is not very graceful, in particular,
+ * if an error is detected in the format string, we simply "pre-terminate"
+ * the given buffer to a length of zero, and return a "length" of zero.
+ * The contents of "buf", except for "buf[0]", may then be undefined.
+ */
+uint vstrnfmt(char *buf, uint max, cptr fmt, va_list vp)
+{
+ cptr s;
+
+ /* The argument is "long" */
+ bool do_long;
+
+ /* The argument needs "processing" */
+ bool do_xtra;
+
+ /* Bytes used in buffer */
+ uint n;
+
+ /* Bytes used in format sequence */
+ uint q;
+
+ /* Format sequence */
+ char aux[128];
+
+ /* Resulting string */
+ char tmp[1024];
+
+
+ /* Mega-Hack -- treat "illegal" length as "infinite" */
+ if (!max) max = 32767;
+
+ /* Mega-Hack -- treat "no format" as "empty string" */
+ if (!fmt) fmt = "";
+
+
+ /* Begin the buffer */
+ n = 0;
+
+ /* Begin the format string */
+ s = fmt;
+
+ /* Scan the format string */
+ while (TRUE)
+ {
+ /* All done */
+ if (!*s) break;
+
+ /* Normal character */
+ if (*s != '%')
+ {
+ /* Check total length */
+ if (n == max - 1) break;
+
+ /* Save the character */
+ buf[n++] = *s++;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Skip the "percent" */
+ s++;
+
+ /* Pre-process "%%" */
+ if (*s == '%')
+ {
+ /* Check total length */
+ if (n == max - 1) break;
+
+ /* Save the percent */
+ buf[n++] = '%';
+
+ /* Skip the "%" */
+ s++;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Pre-process "%n" */
+ if (*s == 'n')
+ {
+ int *arg;
+
+ /* Access the next argument */
+ arg = va_arg(vp, int *);
+
+ /* Save the current length */
+ (*arg) = n;
+
+ /* Skip the "n" */
+ s++;
+
+ /* Continue */
+ continue;
+ }
+
+ /* Hack -- Pre-process "%r" */
+ if (*s == 'r')
+ {
+ /* Extract the next argument, and save it (globally) */
+ vstrnfmt_aux = va_arg(vp, vstrnfmt_aux_func);
+
+ /* Skip the "r" */
+ s++;
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Begin the "aux" string */
+ q = 0;
+
+ /* Save the "percent" */
+ aux[q++] = '%';
+
+ /* Assume no "long" argument */
+ do_long = FALSE;
+
+ /* Assume no "xtra" processing */
+ do_xtra = FALSE;
+
+ /* Build the "aux" string */
+ while (TRUE)
+ {
+ /* Error -- format sequence is not terminated */
+ if (!*s)
+ {
+ /* Terminate the buffer */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+
+ /* Error -- format sequence may be too long */
+ if (q > 100)
+ {
+ /* Terminate the buffer */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+
+ /* Handle "alphabetic" chars */
+ if (isalpha(*s))
+ {
+ /* Hack -- handle "long" request */
+ if (*s == 'l')
+ {
+ /* Save the character */
+ aux[q++] = *s++;
+
+ /* Note the "long" flag */
+ do_long = TRUE;
+ }
+
+ /* Mega-Hack -- handle "extra-long" request */
+ else if (*s == 'L')
+ {
+ /* Error -- illegal format char */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+
+ /* Handle normal end of format sequence */
+ else
+ {
+ /* Save the character */
+ aux[q++] = *s++;
+
+ /* Stop processing the format sequence */
+ break;
+ }
+ }
+
+ /* Handle "non-alphabetic" chars */
+ else
+ {
+ /* Hack -- Handle 'star' (for "variable length" argument) */
+ if (*s == '*')
+ {
+ int arg;
+
+ /* Access the next argument */
+ arg = va_arg(vp, int);
+
+ /* Hack -- append the "length" */
+ sprintf(aux + q, "%d", arg);
+
+ /* Hack -- accept the "length" */
+ while (aux[q]) q++;
+
+ /* Skip the "*" */
+ s++;
+ }
+
+ /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
+ else if (*s == '^')
+ {
+ /* Note the "xtra" flag */
+ do_xtra = TRUE;
+
+ /* Skip the "^" */
+ s++;
+ }
+
+ /* Collect "normal" characters (digits, "-", "+", ".", etc) */
+ else
+ {
+ /* Save the character */
+ aux[q++] = *s++;
+ }
+ }
+ }
+
+
+ /* Terminate "aux" */
+ aux[q] = '\0';
+
+ /* Clear "tmp" */
+ tmp[0] = '\0';
+
+ /* Process the "format" char */
+ switch (aux[q - 1])
+ {
+ /* Simple Character -- standard format */
+ case 'c':
+ {
+ int arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, int);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* Signed Integers -- standard format */
+ case 'd':
+ case 'i':
+ {
+ if (do_long)
+ {
+ long arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, long);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+ else
+ {
+ int arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, int);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Unsigned Integers -- various formats */
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ {
+ if (do_long)
+ {
+ unsigned long arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, unsigned long);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+ else
+ {
+ unsigned int arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, unsigned int);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+ }
+
+ /* Done */
+ break;
+ }
+
+ /* Floating Point -- various formats */
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ {
+ double arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, double);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* Pointer -- implementation varies */
+ case 'p':
+ {
+ vptr arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, vptr);
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* String */
+ case 's':
+ {
+ cptr arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, cptr);
+
+ /* Hack -- convert NULL to EMPTY */
+ if (!arg) arg = "";
+
+ /* Format the argument */
+ sprintf(tmp, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+ /* User defined data */
+ case 'V':
+ case 'v':
+ {
+ vptr arg;
+
+ /* Access next argument */
+ arg = va_arg(vp, vptr);
+
+ /* Format the "user data" */
+ (void)vstrnfmt_aux(tmp, 1000, aux, arg);
+
+ /* Done */
+ break;
+ }
+
+
+ /* Oops */
+ default:
+ {
+ /* Error -- illegal format char */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+ }
+
+
+ /* Mega-Hack -- handle "capitilization" */
+ if (do_xtra)
+ {
+ /* Now append "tmp" to "buf" */
+ for (q = 0; tmp[q]; q++)
+ {
+ /* Notice first non-space */
+ if (!isspace(tmp[q]))
+ {
+ /* Capitalize if possible */
+ if (islower(tmp[q])) tmp[q] = toupper(tmp[q]);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Now append "tmp" to "buf" */
+ for (q = 0; tmp[q]; q++)
+ {
+ /* Check total length */
+ if (n == max - 1) break;
+
+ /* Save the character */
+ buf[n++] = tmp[q];
+ }
+ }
+
+
+ /* Terminate buffer */
+ buf[n] = '\0';
+
+ /* Return length */
+ return (n);
+}
+
+
+/*
+ * Do a vstrnfmt (see above) into a (growable) static buffer.
+ * This buffer is usable for very short term formatting of results.
+ */
+char *vformat(cptr fmt, va_list vp)
+{
+ static char *format_buf = NULL;
+ static huge format_len = 0;
+
+ /* Initial allocation */
+ if (!format_buf)
+ {
+ format_len = 1024;
+ C_MAKE(format_buf, format_len, char);
+ }
+
+ /* Null format yields last result */
+ if (!fmt) return (format_buf);
+
+ /* Keep going until successful */
+ while (1)
+ {
+ uint len;
+
+ /* Build the string */
+ len = vstrnfmt(format_buf, format_len, fmt, vp);
+
+ /* Success */
+ if (len < format_len - 1) break;
+
+ /* Grow the buffer */
+ C_KILL(format_buf, format_len, char);
+ format_len = format_len * 2;
+ C_MAKE(format_buf, format_len, char);
+ }
+
+ /* Return the new buffer */
+ return (format_buf);
+}
+
+
+
+/*
+ * Do a vstrnfmt (see above) into a buffer of a given size.
+ */
+uint strnfmt(char *buf, uint max, cptr fmt, ...)
+{
+ uint len;
+
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Do a virtual fprintf to stderr */
+ len = vstrnfmt(buf, max, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Return the number of bytes written */
+ return (len);
+}
+
+
+/*
+ * Do a vstrnfmt (see above) into a buffer of unknown size.
+ * Since the buffer size is unknown, the user better verify the args.
+ */
+uint strfmt(char *buf, cptr fmt, ...)
+{
+ uint len;
+
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Build the string, assume 32K buffer */
+ len = vstrnfmt(buf, 32767, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Return the number of bytes written */
+ return (len);
+}
+
+
+
+
+/*
+ * Do a vstrnfmt() into (see above) into a (growable) static buffer.
+ * This buffer is usable for very short term formatting of results.
+ * Note that the buffer is (technically) writable, but only up to
+ * the length of the string contained inside it.
+ */
+char *format(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Return the result */
+ return (res);
+}
+
+
+
+
+/*
+ * Vararg interface to plog()
+ */
+void plog_fmt(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Call plog */
+ plog(res);
+}
+
+
+
+/*
+ * Vararg interface to quit()
+ */
+void quit_fmt(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Call quit() */
+ quit(res);
+}
+
+
+
+/*
+ * Vararg interface to core()
+ */
+void core_fmt(cptr fmt, ...)
+{
+ char *res;
+ va_list vp;
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* If requested, Do a virtual fprintf to stderr */
+ res = vformat(fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Call core() */
+ core(res);
+}
+
+
diff --git a/src/z-form.h b/src/z-form.h
new file mode 100644
index 00000000..69b46794
--- /dev/null
+++ b/src/z-form.h
@@ -0,0 +1,47 @@
+/* File z-form.h */
+
+#ifndef INCLUDED_Z_FORM_H
+#define INCLUDED_Z_FORM_H
+
+#include "h-basic.h"
+
+/*
+ * This file provides functions very similar to "sprintf()", but which
+ * not only parse some additional "format sequences", but also enforce
+ * bounds checking, and allow repeated "appends" to the same buffer.
+ *
+ * See "z-form.c" for more detailed information about the routines,
+ * including a list of the legal "format sequences".
+ *
+ * This file makes use of both "z-util.c" and "z-virt.c"
+ */
+
+
+/**** Available Functions ****/
+
+/* Format arguments into given bounded-length buffer */
+extern uint vstrnfmt(char *buf, uint max, cptr fmt, va_list vp);
+
+/* Simple interface to "vstrnfmt()" */
+extern uint strnfmt(char *buf, uint max, cptr fmt, ...);
+
+/* Simple interface to "vstrnfmt()", assuming infinite length */
+extern uint strfmt(char *buf, cptr fmt, ...);
+
+/* Format arguments into a static resizing buffer */
+extern char *vformat(cptr fmt, va_list vp);
+
+/* Simple interface to "vformat()" */
+extern char *format(cptr fmt, ...);
+
+/* Vararg interface to "plog()", using "format()" */
+extern void plog_fmt(cptr fmt, ...);
+
+/* Vararg interface to "quit()", using "format()" */
+extern void quit_fmt(cptr fmt, ...);
+
+/* Vararg interface to "core()", using "format()" */
+extern void core_fmt(cptr fmt, ...);
+
+
+#endif
diff --git a/src/z-rand.c b/src/z-rand.c
new file mode 100644
index 00000000..2f24b9b0
--- /dev/null
+++ b/src/z-rand.c
@@ -0,0 +1,355 @@
+/* File: z-rand.c */
+
+/* Purpose: a simple random number generator -BEN- */
+
+#include "z-rand.h"
+
+
+
+
+/*
+ * Angband 2.7.9 introduced a new (optimized) random number generator,
+ * based loosely on the old "random.c" from Berkeley but with some major
+ * optimizations and algorithm changes. See below for more details.
+ *
+ * Code by myself (benh@phial.com) and Randy (randy@stat.tamu.edu).
+ *
+ * This code provides (1) a "decent" RNG, based on the "BSD-degree-63-RNG"
+ * used in Angband 2.7.8, but rather optimized, and (2) a "simple" RNG,
+ * based on the simple "LCRNG" currently used in Angband, but "corrected"
+ * to give slightly better values. Both of these are available in two
+ * flavors, first, the simple "mod" flavor, which is fast, but slightly
+ * biased at high values, and second, the simple "div" flavor, which is
+ * less fast (and potentially non-terminating) but which is not biased
+ * and is much less subject to low-bit-non-randomness problems.
+ *
+ * You can select your favorite flavor by proper definition of the
+ * "rand_int()" macro in the "defines.h" file.
+ *
+ * Note that, in Angband 2.8.0, the "state" table will be saved in the
+ * savefile, so a special "initialization" phase will be necessary.
+ *
+ * Note the use of the "simple" RNG, first you activate it via
+ * "Rand_quick = TRUE" and "Rand_value = seed" and then it is used
+ * automatically used instead of the "complex" RNG, and when you are
+ * done, you de-activate it via "Rand_quick = FALSE" or choose a new
+ * seed via "Rand_value = seed".
+ */
+
+
+/*
+ * Random Number Generator -- Linear Congruent RNG
+ */
+#define LCRNG(X) ((X) * 1103515245 + 12345)
+
+
+
+/*
+ * Use the "simple" LCRNG
+ */
+bool Rand_quick = TRUE;
+
+
+/*
+ * Current "value" of the "simple" RNG
+ */
+u32b Rand_value;
+
+
+/*
+ * Current "index" for the "complex" RNG
+ */
+u16b Rand_place;
+
+/*
+ * Current "state" table for the "complex" RNG
+ */
+u32b Rand_state[RAND_DEG];
+
+
+
+/*
+ * Initialize the "complex" RNG using a new seed
+ */
+void Rand_state_init(u32b seed)
+{
+ int i, j;
+
+ /* Seed the table */
+ Rand_state[0] = seed;
+
+ /* Propagate the seed */
+ for (i = 1; i < RAND_DEG; i++) Rand_state[i] = LCRNG(Rand_state[i - 1]);
+
+ /* Cycle the table ten times per degree */
+ for (i = 0; i < RAND_DEG * 10; i++)
+ {
+ /* Acquire the next index */
+ j = Rand_place + 1;
+ if (j == RAND_DEG) j = 0;
+
+ /* Update the table, extract an entry */
+ Rand_state[j] += Rand_state[Rand_place];
+
+ /* Advance the index */
+ Rand_place = j;
+ }
+}
+
+
+/*
+ * Extract a "random" number from 0 to m-1, via "modulus"
+ *
+ * Note that "m" should probably be less than 500000, or the
+ * results may be rather biased towards low values.
+ */
+s32b Rand_mod(s32b m)
+{
+ int j;
+ u32b r;
+
+ /* Hack -- simple case */
+ if (m <= 1) return (0);
+
+ /* Use the "simple" RNG */
+ if (Rand_quick)
+ {
+ /* Cycle the generator */
+ r = (Rand_value = LCRNG(Rand_value));
+
+ /* Mutate a 28-bit "random" number */
+ r = (r >> 4) % m;
+ }
+
+ /* Use the "complex" RNG */
+ else
+ {
+ /* Acquire the next index */
+ j = Rand_place + 1;
+ if (j == RAND_DEG) j = 0;
+
+ /* Update the table, extract an entry */
+ r = (Rand_state[j] += Rand_state[Rand_place]);
+
+ /* Advance the index */
+ Rand_place = j;
+
+ /* Extract a "random" number */
+ r = (r >> 4) % m;
+ }
+
+ /* Use the value */
+ return (r);
+}
+
+
+/*
+ * Extract a "random" number from 0 to m-1, via "division"
+ *
+ * This method selects "random" 28-bit numbers, and then uses
+ * division to drop those numbers into "m" different partitions,
+ * plus a small non-partition to reduce bias, taking as the final
+ * value the first "good" partition that a number falls into.
+ *
+ * This method has no bias, and is much less affected by patterns
+ * in the "low" bits of the underlying RNG's.
+ */
+s32b Rand_div(s32b m)
+{
+ u32b r, n;
+
+ /* Hack -- simple case */
+ if (m <= 1) return (0);
+
+ /* Partition size */
+ n = (0x10000000 / m);
+
+ /* Use a simple RNG */
+ if (Rand_quick)
+ {
+ /* Wait for it */
+ while (1)
+ {
+ /* Cycle the generator */
+ r = (Rand_value = LCRNG(Rand_value));
+
+ /* Mutate a 28-bit "random" number */
+ r = ((r >> 4) & 0x0FFFFFFF) / n;
+
+ /* Done */
+ if (r < (u32b)m) break;
+ }
+ }
+
+ /* Use a complex RNG */
+ else
+ {
+ /* Wait for it */
+ while (1)
+ {
+ int j;
+
+ /* Acquire the next index */
+ j = Rand_place + 1;
+ if (j == RAND_DEG) j = 0;
+
+ /* Update the table, extract an entry */
+ r = (Rand_state[j] += Rand_state[Rand_place]);
+
+ /* Hack -- extract a 28-bit "random" number */
+ r = ((r >> 4) & 0x0FFFFFFF) / n;
+
+ /* Advance the index */
+ Rand_place = j;
+
+ /* Done */
+ if (r < (u32b)m) break;
+ }
+ }
+
+ /* Use the value */
+ return (r);
+}
+
+
+
+
+/*
+ * The number of entries in the "randnor_table"
+ */
+#define RANDNOR_NUM 256
+
+/*
+ * The standard deviation of the "randnor_table"
+ */
+#define RANDNOR_STD 64
+
+/*
+ * The normal distribution table for the "randnor()" function (below)
+ */
+static s16b randnor_table[RANDNOR_NUM] =
+{
+ 206, 613, 1022, 1430, 1838, 2245, 2652, 3058,
+ 3463, 3867, 4271, 4673, 5075, 5475, 5874, 6271,
+ 6667, 7061, 7454, 7845, 8234, 8621, 9006, 9389,
+ 9770, 10148, 10524, 10898, 11269, 11638, 12004, 12367,
+ 12727, 13085, 13440, 13792, 14140, 14486, 14828, 15168,
+ 15504, 15836, 16166, 16492, 16814, 17133, 17449, 17761,
+ 18069, 18374, 18675, 18972, 19266, 19556, 19842, 20124,
+ 20403, 20678, 20949, 21216, 21479, 21738, 21994, 22245,
+
+ 22493, 22737, 22977, 23213, 23446, 23674, 23899, 24120,
+ 24336, 24550, 24759, 24965, 25166, 25365, 25559, 25750,
+ 25937, 26120, 26300, 26476, 26649, 26818, 26983, 27146,
+ 27304, 27460, 27612, 27760, 27906, 28048, 28187, 28323,
+ 28455, 28585, 28711, 28835, 28955, 29073, 29188, 29299,
+ 29409, 29515, 29619, 29720, 29818, 29914, 30007, 30098,
+ 30186, 30272, 30356, 30437, 30516, 30593, 30668, 30740,
+ 30810, 30879, 30945, 31010, 31072, 31133, 31192, 31249,
+
+ 31304, 31358, 31410, 31460, 31509, 31556, 31601, 31646,
+ 31688, 31730, 31770, 31808, 31846, 31882, 31917, 31950,
+ 31983, 32014, 32044, 32074, 32102, 32129, 32155, 32180,
+ 32205, 32228, 32251, 32273, 32294, 32314, 32333, 32352,
+ 32370, 32387, 32404, 32420, 32435, 32450, 32464, 32477,
+ 32490, 32503, 32515, 32526, 32537, 32548, 32558, 32568,
+ 32577, 32586, 32595, 32603, 32611, 32618, 32625, 32632,
+ 32639, 32645, 32651, 32657, 32662, 32667, 32672, 32677,
+
+ 32682, 32686, 32690, 32694, 32698, 32702, 32705, 32708,
+ 32711, 32714, 32717, 32720, 32722, 32725, 32727, 32729,
+ 32731, 32733, 32735, 32737, 32739, 32740, 32742, 32743,
+ 32745, 32746, 32747, 32748, 32749, 32750, 32751, 32752,
+ 32753, 32754, 32755, 32756, 32757, 32757, 32758, 32758,
+ 32759, 32760, 32760, 32761, 32761, 32761, 32762, 32762,
+ 32763, 32763, 32763, 32764, 32764, 32764, 32764, 32765,
+ 32765, 32765, 32765, 32766, 32766, 32766, 32766, 32767,
+};
+
+
+
+/*
+ * Generate a random integer number of NORMAL distribution
+ *
+ * The table above is used to generate a psuedo-normal distribution,
+ * in a manner which is much faster than calling a transcendental
+ * function to calculate a true normal distribution.
+ *
+ * Basically, entry 64*N in the table above represents the number of
+ * times out of 32767 that a random variable with normal distribution
+ * will fall within N standard deviations of the mean. That is, about
+ * 68 percent of the time for N=1 and 95 percent of the time for N=2.
+ *
+ * The table above contains a "faked" final entry which allows us to
+ * pretend that all values in a normal distribution are strictly less
+ * than four standard deviations away from the mean. This results in
+ * "conservative" distribution of approximately 1/32768 values.
+ *
+ * Note that the binary search takes up to 16 quick iterations.
+ */
+s16b randnor(int mean, int stand)
+{
+ s16b tmp;
+ s16b offset;
+
+ s16b low = 0;
+ s16b high = RANDNOR_NUM;
+
+ /* Paranoia */
+ if (stand < 1) return (mean);
+
+ /* Roll for probability */
+ tmp = (s16b)rand_int(32768);
+
+ /* Binary Search */
+ while (low < high)
+ {
+ int mid = (low + high) >> 1;
+
+ /* Move right if forced */
+ if (randnor_table[mid] < tmp)
+ {
+ low = mid + 1;
+ }
+
+ /* Move left otherwise */
+ else
+ {
+ high = mid;
+ }
+ }
+
+ /* Convert the index into an offset */
+ offset = (long)stand * (long)low / RANDNOR_STD;
+
+ /* One half should be negative */
+ if (rand_int(100) < 50) return (mean - offset);
+
+ /* One half should be positive */
+ return (mean + offset);
+}
+
+
+
+/*
+ * Generates damage for "2d6" style dice rolls
+ */
+s32b damroll(s16b num, s16b sides)
+{
+ int i;
+ s32b sum = 0;
+ for (i = 0; i < num; i++) sum += randint(sides);
+ return (sum);
+}
+
+
+/*
+ * Same as above, but always maximal
+ */
+s32b maxroll(s16b num, s16b sides)
+{
+ return (num * sides);
+}
+
+
+
diff --git a/src/z-rand.h b/src/z-rand.h
new file mode 100644
index 00000000..69a531b4
--- /dev/null
+++ b/src/z-rand.h
@@ -0,0 +1,89 @@
+/* File: z-rand.h */
+
+#ifndef INCLUDED_Z_RAND_H
+#define INCLUDED_Z_RAND_H
+
+#include "h-basic.h"
+
+
+
+/**** Available constants ****/
+
+
+/*
+ * Random Number Generator -- Degree of "complex" RNG -- see "misc.c"
+ * This value is hard-coded at 63 for a wide variety of reasons.
+ */
+#define RAND_DEG 63
+
+
+
+
+/**** Available macros ****/
+
+
+/*
+ * Generates a random long integer X where O<=X<M.
+ * The integer X falls along a uniform distribution.
+ * For example, if M is 100, you get "percentile dice"
+ */
+#define rand_int(M) \
+ (Rand_div(M))
+
+/*
+ * Generates a random long integer X where A<=X<=B
+ * The integer X falls along a uniform distribution.
+ * Note: rand_range(0,N-1) == rand_int(N)
+ */
+#define rand_range(A,B) \
+ ((A) + (rand_int(1+(B)-(A))))
+
+/*
+ * Generate a random long integer X where A-D<=X<=A+D
+ * The integer X falls along a uniform distribution.
+ * Note: rand_spread(A,D) == rand_range(A-D,A+D)
+ */
+#define rand_spread(A,D) \
+ ((A) + (rand_int(1+(D)+(D))) - (D))
+
+
+/*
+ * Generate a random long integer X where 1<=X<=M
+ * Also, "correctly" handle the case of M<=1
+ */
+#define randint(M) \
+ (rand_int(M) + 1)
+
+
+/*
+ * Evaluate to TRUE "P" percent of the time
+ */
+#define magik(P) \
+ (rand_int(100) < (P))
+
+
+
+
+/**** Available Variables ****/
+
+
+extern bool Rand_quick;
+extern u32b Rand_value;
+extern u16b Rand_place;
+extern u32b Rand_state[RAND_DEG];
+
+
+/**** Available Functions ****/
+
+
+extern void Rand_state_init(u32b seed);
+extern s32b Rand_mod(s32b m);
+extern s32b Rand_div(s32b m);
+extern s16b randnor(int mean, int stand);
+extern s32b damroll(s16b num, s16b sides);
+extern s32b maxroll(s16b num, s16b sides);
+
+
+#endif
+
+
diff --git a/src/z-sock.c b/src/z-sock.c
new file mode 100644
index 00000000..fcc6cd1c
--- /dev/null
+++ b/src/z-sock.c
@@ -0,0 +1,787 @@
+/* File: z-term.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: a generic, IP connection package */
+
+#include "angband.h"
+#include "z-term.h"
+#include "z-virt.h"
+
+
+/*
+ * System independant functions
+ */
+
+/* Call all the callbacks */
+void zsock_handle_callbacks()
+{
+ timer_callback_list *c = zsock.__timer_callbacks;
+
+ while (c != NULL)
+ {
+ c->callback();
+ c = c->next;
+ }
+}
+
+/* Add a callback in the list */
+void zsock_add_timer_callback(timer_callback callback)
+{
+ timer_callback_list *c;
+
+ /* Create it */
+ MAKE(c, timer_callback_list);
+ c->callback = callback;
+
+ /* Add it into the lsit */
+ c->next = zsock.__timer_callbacks;
+ zsock.__timer_callbacks = c;
+
+ /* Increase the timer count */
+ zsock.__timers++;
+}
+
+/* Remove a callback in the list */
+void zsock_remove_timer_callback(timer_callback callback)
+{
+ timer_callback_list *c = zsock.__timer_callbacks, *old = NULL;
+
+ /* Find it */
+ while ((c != NULL) && (c->callback != callback))
+ {
+ old = c;
+ c = c->next;
+ }
+
+ if (c->callback == callback)
+ {
+ /* Skip it */
+ if (old == NULL)
+ zsock.__timer_callbacks = c->next;
+ else
+ old->next = c->next;
+
+ /* Delete it */
+ FREE(c, timer_callback_list);
+
+ /* Increase the timer count */
+ zsock.__timers--;
+ }
+ else
+ {
+ cmsg_print(TERM_VIOLET, "WARNING: tried to remove a non existing timer callback!");
+ }
+}
+
+/* Creates a connection struct */
+ip_connection *zsock_new_connection()
+{
+ ip_connection *c;
+
+ MAKE(c, ip_connection);
+ return c;
+}
+void zsock_free_connection(ip_connection *c)
+{
+ FREE(c, ip_connection);
+}
+
+
+/* Set the dying connection callback */
+void zsock_set_lose_connection(ip_connection *conn, lose_connection_hook hook)
+{
+ conn->lost_conn = hook;
+}
+
+/* Send data on the connection -- easy to use */
+bool zsock_write_simple(ip_connection *conn, cptr str)
+{
+ int len = 0;
+
+ return zsock.write(conn, str, &len);
+}
+
+/* Read data on the connection -- easy to use */
+bool zsock_read_simple(ip_connection *conn, char *str, int len)
+{
+ int rlen = len;
+
+ return zsock.read(conn, str, &rlen, FALSE);
+}
+
+/*
+ ******************************* Here comes the Windows socket part ********************************
+ */
+#ifdef USE_WINSOCK
+#include <windows.h>
+#include <winsock2.h>
+
+/* Needed for timers -- pfft */
+extern HWND get_main_hwnd();
+
+#define ZSOCK_TIMER_ID 1
+
+VOID CALLBACK zsock_timer_callback_win(HWND hwnd, UINT message, UINT idTimer, DWORD dwTime)
+{
+ zsock_handle_callbacks();
+}
+
+bool zsock_add_timer_win(timer_callback callback)
+{
+ zsock_add_timer_callback(callback);
+
+ /* Is it the first callback ? then we must create the timer */
+ if (zsock.__timers == 1)
+ {
+ SetTimer(get_main_hwnd(), ZSOCK_TIMER_ID, ZSOCK_TIMER_DELAY, zsock_timer_callback_win);
+ }
+ return TRUE;
+}
+
+bool zsock_remove_timer_win(timer_callback callback)
+{
+ zsock_remove_timer_callback(callback);
+
+ /* No more callbacks ? no need for the timer then */
+ if (!zsock.__timers)
+ {
+ KillTimer(get_main_hwnd(), ZSOCK_TIMER_ID);
+ }
+ return TRUE;
+}
+
+
+bool zsock_setup_win(ip_connection *conn, cptr conn_ip, int port, byte conn_type, bool server)
+{
+ /* Already setup! */
+ if (conn->setup) return FALSE;
+
+ MAKE(conn->socket, SOCKET);
+
+ if (!server)
+ {
+ struct hostent *host;
+
+ host = gethostbyname(conn_ip);
+ conn->conn_ip = ((struct in_addr *)(host->h_addr))->s_addr;
+ }
+ else
+ conn->conn_ip = 0;
+
+ conn->conn_type = conn_type;
+ conn->conn_port = htons(port);
+
+ conn->setup = TRUE;
+ conn->server = server;
+
+ conn->lost_conn = NULL;
+
+ return TRUE;
+}
+
+bool zsock_unsetup_win(ip_connection *conn)
+{
+ /* Already unsetup */
+ if (!conn->setup) return FALSE;
+
+ FREE(conn->socket, SOCKET);
+
+ return TRUE;
+}
+
+bool zsock_open_win(ip_connection *conn)
+{
+ struct sockaddr_in sin;
+
+ /* Already connected */
+ if (conn->connected) return FALSE;
+
+ *((SOCKET*)conn->socket) = socket(AF_INET, SOCK_STREAM, 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = conn->conn_ip;
+ sin.sin_port = conn->conn_port;
+
+ if (conn->server)
+ {
+ if (SOCKET_ERROR == bind(*((SOCKET*)conn->socket), &sin, sizeof(sin)))
+ return FALSE;
+
+ if (SOCKET_ERROR == listen(*((SOCKET*)conn->socket), 10)) return FALSE;
+ }
+ else
+ {
+ if (connect(*((SOCKET*)conn->socket), &sin, sizeof sin) == SOCKET_ERROR)
+ {
+ /* could not connect to server */
+ return (FALSE);
+ }
+ }
+
+ conn->connected = TRUE;
+ return TRUE;
+}
+
+bool zsock_can_read_win(ip_connection *conn)
+{
+ struct timeval t;
+ fd_set rd;
+ SOCKET *c = conn->socket;
+
+ if (!conn->connected) return FALSE;
+
+ FD_ZERO(&rd);
+ FD_SET(*c, &rd);
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ select(*c + 1, &rd, NULL, NULL, &t);
+ if (FD_ISSET(*c, &rd)) return TRUE;
+ else return (FALSE);
+}
+
+bool zsock_wait_win(ip_connection *conn, int seconds)
+{
+ struct timeval t;
+ fd_set rd;
+ SOCKET *c = conn->socket;
+
+ if (!conn->connected) return FALSE;
+
+ t.tv_sec = seconds;
+ t.tv_usec = 0;
+
+ FD_ZERO(&rd);
+ FD_SET(*c, &rd);
+ select(*c + 1, &rd, NULL, NULL, &t);
+ if (FD_ISSET(*c, &rd)) return TRUE;
+ else return (FALSE);
+}
+
+bool zsock_close_win(ip_connection *conn)
+{
+ SOCKET *c = conn->socket;
+
+ /* Already disconnected */
+ if (!conn->connected) return FALSE;
+
+ closesocket(*c);
+ conn->connected = FALSE;
+ return TRUE;
+}
+
+bool zsock_write_win(ip_connection *conn, cptr str, int *size)
+{
+ SOCKET *c = conn->socket;
+
+ if (!conn->connected) return FALSE;
+
+ if ((*size = send(*c, str, (!*size) ? strlen(str) : *size, 0)) <= 0)
+ {
+ /* Oups connection died! */
+ if (conn->lost_conn) conn->lost_conn(conn);
+ zsock.close(conn);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool zsock_read_win(ip_connection *conn, char *str, int *len, bool raw)
+{
+ char c;
+ int l = 0;
+ SOCKET *cc = conn->socket;
+
+ if (!conn->connected) return FALSE;
+
+ if (!raw)
+ {
+ /* This *IS* fucking slow */
+ while ((l < *len) && zsock_can_read_win(conn))
+ {
+ if (recv(*cc, &c, 1, 0) <= 0)
+ {
+ /* Oups connection died! */
+ if (conn->lost_conn) conn->lost_conn(conn);
+ zsock.close(conn);
+ return FALSE;
+ }
+ if (c == '\r') continue;
+ if (c == '\n') break;
+ str[l++] = c;
+ }
+ str[l] = '\0';
+ *len = l;
+ return TRUE;
+ }
+ else
+ {
+ if ((*len = recv(*cc, str, *len, 0)) <= 0)
+ {
+ /* Oups connection died! */
+ if (conn->lost_conn) conn->lost_conn(conn);
+ zsock.close(conn);
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+
+bool zsock_accept_win(ip_connection *conn, ip_connection *child)
+{
+ SOCKET *s = conn->socket;
+ SOCKET sock;
+ struct sockaddr_in sin;
+ int len;
+
+ if (!conn->server) return FALSE;
+ if (!conn->connected) return FALSE;
+
+ len = sizeof(sin);
+ sock = accept(*s, (struct sockaddr*) & sin, &len);
+
+ if (sock == SOCKET_ERROR) return FALSE;
+
+ /* Initialize the connection with a fake destination */
+ zsock.setup(child, "127.0.0.1", 0, ZSOCK_TYPE_TCP, FALSE);
+
+ /* Set the correct socket */
+ *((SOCKET*)child->socket) = sock;
+ child->connected = TRUE;
+
+ return TRUE;
+}
+
+bool init_socks_win()
+{
+ WSADATA wsaData;
+ WORD version;
+ int error;
+
+ version = MAKEWORD( 2, 0 );
+
+ error = WSAStartup( version, &wsaData );
+
+ /* check for error */
+ if ( error != 0 )
+ {
+ /* error occured */
+ return FALSE;
+ }
+
+ /* check for correct version */
+ if ( LOBYTE( wsaData.wVersion ) != 2 ||
+ HIBYTE( wsaData.wVersion ) != 0 )
+ {
+ /* incorrect WinSock version */
+ WSACleanup();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
+
+/*
+ ****************************** And there is the unix sockets **************************
+ */
+#ifdef USE_UNIXSOCK
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <signal.h>
+
+void handle_timer(int sig)
+{
+ zsock_handle_callbacks();
+}
+
+static struct sigaction handle_old_alarm;
+bool zsock_add_timer_unix(timer_callback callback)
+{
+ zsock_add_timer_callback(callback);
+
+ /* Is it the first callback ? then we must create the timer */
+ if (zsock.__timers == 1)
+ {
+ struct sigaction new_sig;
+
+ /* Register the timer */
+ new_sig.sa_handler = handle_timer;
+ new_sig.sa_flags = 0;
+ sigaction(SIGALRM, &new_sig, &handle_old_alarm);
+
+ ualarm(ZSOCK_TIMER_DELAY * 1000, ZSOCK_TIMER_DELAY * 1000);
+ }
+ return TRUE;
+}
+
+bool zsock_remove_timer_unix(timer_callback callback)
+{
+ zsock_remove_timer_callback(callback);
+
+ /* No more callbacks ? no need for the timer then */
+ if (!zsock.__timers)
+ {
+ alarm(0);
+ sigaction(SIGALRM, &handle_old_alarm, NULL);
+ }
+ return TRUE;
+}
+
+bool zsock_setup_unix(ip_connection *conn, cptr conn_ip, int port, byte conn_type, bool server)
+{
+ /* Already setup! */
+ if (conn->setup) return FALSE;
+
+ MAKE(conn->socket, int);
+
+ if (!server)
+ {
+ struct hostent *host;
+
+ host = gethostbyname(conn_ip);
+ conn->conn_ip = ((struct in_addr *)(host->h_addr))->s_addr;
+ }
+ else
+ conn->conn_ip = 0;
+
+ conn->conn_type = conn_type;
+ conn->conn_port = htons(port);
+
+ conn->setup = TRUE;
+ conn->server = server;
+
+ conn->lost_conn = NULL;
+
+ return TRUE;
+}
+
+bool zsock_unsetup_unix(ip_connection *conn)
+{
+ /* Already unsetup */
+ if (!conn->setup) return FALSE;
+
+ FREE(conn->socket, int);
+
+ return TRUE;
+}
+
+bool zsock_open_unix(ip_connection *conn)
+{
+ struct sockaddr_in sin;
+
+ /* Already connected */
+ if (conn->connected) return FALSE;
+
+ *((int*)conn->socket) = socket(AF_INET, SOCK_STREAM, 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = conn->conn_ip;
+ sin.sin_port = conn->conn_port;
+
+ if (conn->server)
+ {
+ int option = 1;
+
+ /* Set this so we don't wait forever on startups */
+ if ( -1 == setsockopt(*((int*)conn->socket), SOL_SOCKET, SO_REUSEADDR , (void*)&option, sizeof(int))) return FALSE;
+
+ if ( -1 == bind(*((int*)conn->socket), (struct sockaddr*)&sin, sizeof(sin)))
+ return FALSE;
+
+ if ( -1 == listen(*((int*)conn->socket), 10)) return FALSE;
+ }
+ else
+ {
+ if (connect(*((int*)conn->socket), (struct sockaddr*)&sin, sizeof sin) == -1)
+ {
+ /* could not connect to server */
+ return (FALSE);
+ }
+ }
+
+ conn->connected = TRUE;
+ return TRUE;
+}
+
+bool zsock_can_read_unix(ip_connection *conn)
+{
+ struct timeval t;
+ fd_set rd;
+ int *c = conn->socket;
+
+ if (!conn->connected) return FALSE;
+
+ FD_ZERO(&rd);
+ FD_SET(*c, &rd);
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ select(*c + 1, &rd, NULL, NULL, &t);
+ if (FD_ISSET(*c, &rd)) return TRUE;
+ else return (FALSE);
+}
+
+bool zsock_wait_unix(ip_connection *conn, int seconds)
+{
+ struct timeval t;
+ fd_set rd;
+ int *c = conn->socket;
+
+ if (!conn->connected) return FALSE;
+
+ t.tv_sec = seconds;
+ t.tv_usec = 0;
+
+ FD_ZERO(&rd);
+ FD_SET(*c, &rd);
+ select(*c + 1, &rd, NULL, NULL, &t);
+ if (FD_ISSET(*c, &rd)) return TRUE;
+ else return (FALSE);
+}
+
+bool zsock_close_unix(ip_connection *conn)
+{
+ int *c = conn->socket;
+
+ /* Already disconnected */
+ if (!conn->connected) return FALSE;
+
+ close(*c);
+ conn->connected = FALSE;
+ return TRUE;
+}
+
+bool zsock_write_unix(ip_connection *conn, cptr str, int *size)
+{
+ int *c = conn->socket;
+
+ if (conn->server) return FALSE;
+ if (!conn->connected) return FALSE;
+
+ if ((*size = send(*c, str, (!*size) ? (s32b)strlen(str) : *size, 0)) <= 0)
+ {
+ /* Oups connection died! */
+ if (conn->lost_conn) conn->lost_conn(conn);
+ zsock.close(conn);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool zsock_read_unix(ip_connection *conn, char *str, int *len, bool raw)
+{
+ char c;
+ int l = 0;
+ int *cc = conn->socket;
+
+ if (conn->server) return FALSE;
+ if (!conn->connected) return FALSE;
+
+ if (!raw)
+ {
+ /* This *IS* fucking slow */
+ while ((l < *len) && zsock_can_read_unix(conn))
+ {
+ if (recv(*cc, &c, 1, 0) <= 0)
+ {
+ /* Oups connection died! */
+ if (conn->lost_conn) conn->lost_conn(conn);
+ zsock.close(conn);
+ return FALSE;
+ }
+ if (c == '\r') continue;
+ if (c == '\n') break;
+ str[l++] = c;
+ }
+ str[l] = '\0';
+ *len = l;
+ return TRUE;
+ }
+ else
+ {
+ if ((*len = recv(*cc, str, *len, 0)) <= 0)
+ {
+ /* Oups connection died! */
+ if (conn->lost_conn) conn->lost_conn(conn);
+ zsock.close(conn);
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+
+bool zsock_accept_unix(ip_connection *conn, ip_connection *child)
+{
+ int *s = conn->socket;
+ int sock;
+ struct sockaddr_in sin;
+ unsigned int len;
+
+ if (!conn->server) return FALSE;
+ if (!conn->connected) return FALSE;
+
+ len = sizeof(sin);
+ sock = accept(*s, (struct sockaddr*) & sin, &len);
+
+ if (sock == -1) return FALSE;
+
+ /* Initialize the connection with a fake destination */
+ zsock.setup(child, "127.0.0.1", 0, ZSOCK_TYPE_TCP, FALSE);
+
+ /* Set the correct socket */
+ *((int*)child->socket) = sock;
+ child->connected = TRUE;
+
+ return TRUE;
+}
+
+#endif
+
+/* The zsock hook list */
+zsock_hooks zsock;
+
+/*
+ * Dummy hooks for systems without socks
+ */
+bool zsock_setup_dummy(ip_connection *conn, cptr conn_ip, int port, byte conn_type, bool server)
+{
+ return FALSE;
+}
+
+bool zsock_unsetup_dummy(ip_connection *conn)
+{
+ return FALSE;
+}
+
+bool zsock_open_dummy(ip_connection *conn)
+{
+ return FALSE;
+}
+
+bool zsock_can_read_dummy(ip_connection *conn)
+{
+ return (FALSE);
+}
+
+bool zsock_wait_dummy(ip_connection *conn, int seconds)
+{
+ return (FALSE);
+}
+
+bool zsock_close_dummy(ip_connection *conn)
+{
+ return FALSE;
+}
+
+bool zsock_write_dummy(ip_connection *conn, cptr str, int *size)
+{
+ return FALSE;
+}
+
+bool zsock_read_dummy(ip_connection *conn, char *str, int *len, bool raw)
+{
+ return FALSE;
+}
+
+bool zsock_accept_dummy(ip_connection *conn, ip_connection *child)
+{
+ return FALSE;
+}
+
+bool zsock_add_timer_dummy(timer_callback callback)
+{
+ return TRUE;
+}
+
+bool zsock_remove_timer_dummy(timer_callback callback)
+{
+ return TRUE;
+}
+
+void dummy_socks()
+{
+ zsock.setup = zsock_setup_dummy;
+ zsock.unsetup = zsock_unsetup_dummy;
+ zsock.open = zsock_open_dummy;
+ zsock.close = zsock_close_dummy;
+ zsock.write = zsock_write_dummy;
+ zsock.read = zsock_read_dummy;
+ zsock.accept = zsock_accept_dummy;
+ zsock.can_read = zsock_can_read_dummy;
+ zsock.wait = zsock_wait_dummy;
+ zsock.add_timer = zsock_add_timer_dummy;
+ zsock.remove_timer = zsock_remove_timer_dummy;
+}
+
+/*
+ * Initialize the hooks
+ */
+static bool init_once = TRUE;
+bool zsock_init()
+{
+ /* Sadly on some platforms we will be called 2 times ... */
+ if (init_once)
+ {
+ /* Set the timers */
+ zsock.__timers = 0;
+ zsock.__timer_callbacks = NULL;
+ init_once = FALSE;
+ }
+
+ /* Set the few system independants functions */
+ zsock.new_connection = zsock_new_connection;
+ zsock.free_connection = zsock_free_connection;
+ zsock.set_lose_connection = zsock_set_lose_connection;
+ zsock.write_simple = zsock_write_simple;
+ zsock.read_simple = zsock_read_simple;
+
+#ifdef USE_WINSOCK
+ if (init_socks_win())
+ {
+ zsock.setup = zsock_setup_win;
+ zsock.unsetup = zsock_unsetup_win;
+ zsock.open = zsock_open_win;
+ zsock.close = zsock_close_win;
+ zsock.write = zsock_write_win;
+ zsock.read = zsock_read_win;
+ zsock.accept = zsock_accept_win;
+ zsock.can_read = zsock_can_read_win;
+ zsock.wait = zsock_wait_win;
+ zsock.add_timer = zsock_add_timer_win;
+ zsock.remove_timer = zsock_remove_timer_win;
+ return TRUE;
+ }
+ else
+ {
+ dummy_socks();
+ return TRUE;
+ }
+#elif defined(USE_UNIXSOCK)
+zsock.setup = zsock_setup_unix;
+ zsock.unsetup = zsock_unsetup_unix;
+ zsock.open = zsock_open_unix;
+ zsock.close = zsock_close_unix;
+ zsock.write = zsock_write_unix;
+ zsock.read = zsock_read_unix;
+ zsock.accept = zsock_accept_unix;
+ zsock.can_read = zsock_can_read_unix;
+ zsock.wait = zsock_wait_unix;
+ zsock.add_timer = zsock_add_timer_unix;
+ zsock.remove_timer = zsock_remove_timer_unix;
+ return TRUE;
+#else
+ dummy_socks();
+ return TRUE;
+#endif
+}
diff --git a/src/z-sock.h b/src/z-sock.h
new file mode 100644
index 00000000..c842668e
--- /dev/null
+++ b/src/z-sock.h
@@ -0,0 +1,127 @@
+/* File: z-sock.h */
+
+/*
+ * Copyright (c) 2002 DarkGod
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifndef INCLUDED_Z_SOCK_H
+#define INCLUDED_Z_SOCK_H
+
+#include "h-basic.h"
+
+/*
+ * This represents an IP connection
+ */
+typedef struct ip_connection ip_connection;
+
+/* A callback used when the connection suddently dies */
+typedef void (*lose_connection_hook)(ip_connection *conn);
+
+struct ip_connection
+{
+ bool setup; /* Has it been setted up yet? */
+
+ long conn_ip; /* The IP where to connect to */
+ int conn_port; /* The port where to connect to */
+ byte conn_type; /* Type of connection */
+
+ bool connected; /* The connection status */
+ void *socket; /* The socket for the connection */
+
+ lose_connection_hook lost_conn; /* Called when the conenction dies */
+
+ bool server; /* Is it a server socket ? */
+};
+
+/*
+ * Possible connection types
+ */
+#define ZSOCK_TYPE_TCP 1
+/* #define ZSOCK_TYPE_UDP 2 */
+
+
+/*
+ * The time in milliseconds when to call the sockets callbacks for the timer
+ */
+#define ZSOCK_TIMER_DELAY 100
+
+/* Timer callbacks */
+typedef void (*timer_callback)(void);
+typedef struct timer_callback_list timer_callback_list;
+struct timer_callback_list
+{
+ timer_callback callback;
+ timer_callback_list *next;
+};
+
+/*
+ * Hooks needed for a main-foo.c to be sock-able
+ */
+typedef struct zsock_hooks zsock_hooks;
+struct zsock_hooks
+{
+ /* Creates a struct */
+ ip_connection *(*new_connection)(void);
+
+ /* Free it */
+ void (*free_connection)(ip_connection *c);
+
+ /* Setup a connection, but do NOT connect */
+ bool (*setup)(ip_connection *conn, cptr conn_ip, int port, byte conn_type, bool server);
+
+ /* Unsetup a connection, but and DO close before if needed */
+ bool (*unsetup)(ip_connection *conn);
+
+ /* Open(connect) a well setup-ed connection */
+ bool (*open)(ip_connection *conn);
+
+ /* Close a connected connection */
+ bool (*close)(ip_connection *conn);
+
+ /* Send data on the connection */
+ bool (*write)(ip_connection *conn, cptr str, int *size);
+
+ /* Read data on the connection */
+ bool (*read)(ip_connection *conn, char *str, int *len, bool raw);
+
+ /* Send data on the connection -- easy to use */
+ bool (*write_simple)(ip_connection *conn, cptr str);
+
+ /* Read data on the connection -- easy to use */
+ bool (*read_simple)(ip_connection *conn, char *str, int len);
+
+ /* Set the dying connection callback */
+ void (*set_lose_connection)(ip_connection *conn, lose_connection_hook hook);
+
+ /* Accept a connection */
+ bool (*accept)(ip_connection *conn, ip_connection *child);
+
+ /* Check if there is any data to be read and return instantly in any case */
+ bool (*can_read)(ip_connection *conn);
+
+ /* Wait until there is any data to be read and return after seconds time in any case */
+ bool (*wait)(ip_connection *conn, int seconds);
+
+
+ /*
+ * Timer stuff, I hope I can make that look better
+ */
+ int __timers;
+ timer_callback_list *__timer_callbacks;
+
+ /* Setup the timer */
+ bool (*add_timer)(timer_callback callback);
+
+ /* Remove the timer */
+ bool (*remove_timer)(timer_callback callback);
+};
+
+extern zsock_hooks zsock;
+
+extern bool zsock_init(void);
+
+#endif
diff --git a/src/z-term.c b/src/z-term.c
new file mode 100644
index 00000000..b93d8470
--- /dev/null
+++ b/src/z-term.c
@@ -0,0 +1,3137 @@
+/* File: z-term.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: a generic, efficient, terminal window package -BEN- */
+
+#include "angband.h"
+
+#include "z-term.h"
+
+#include "z-virt.h"
+
+
+/*
+ * This file provides a generic, efficient, terminal window package,
+ * which can be used not only on standard terminal environments such
+ * as dumb terminals connected to a Unix box, but also in more modern
+ * "graphic" environments, such as the Macintosh or Unix/X11.
+ *
+ * Each "window" works like a standard "dumb terminal", that is, it
+ * can display a two dimensional array of grids containing colored
+ * textual symbols, plus an optional cursor, and it can be used to
+ * get keypress events from the user.
+ *
+ * In fact, this package can simply be used, if desired, to support
+ * programs which will look the same on a dumb terminal as they do
+ * on a graphic platform such as the Macintosh.
+ *
+ * This package was designed to help port the game "Angband" to a wide
+ * variety of different platforms. Angband, like many other games in
+ * the "rogue-like" heirarchy, requires, at the minimum, the ability
+ * to display "colored textual symbols" in a standard 80x24 "window",
+ * such as that provided by most dumb terminals, and many old personal
+ * computers, and to check for "keypresses" from the user. The major
+ * concerns were thus portability and efficiency, so Angband could be
+ * easily ported to many different systems, with minimal effort, and
+ * yet would run quickly on each of these systems, no matter what kind
+ * of underlying hardware/software support was being used.
+ *
+ * It is important to understand the differences between the older
+ * "dumb terminals" and the newer "graphic interface" machines, since
+ * this package was designed to work with both types of systems.
+ *
+ * New machines:
+ * waiting for a keypress is complex
+ * checking for a keypress is often cheap
+ * changing "colors" may be expensive
+ * the "color" of a "blank" is rarely important
+ * moving the "cursor" is relatively cheap
+ * use a "software" cursor (only moves when requested)
+ * drawing characters normally will not erase old ones
+ * drawing a character on the cursor often erases it
+ * may have fast routines for "clear a region"
+ * the bottom right corner is usually not special
+ *
+ * Old machines:
+ * waiting for a keypress is simple
+ * checking for a keypress is often expensive
+ * changing "colors" is usually cheap
+ * the "color" of a "blank" may be important
+ * moving the "cursor" may be expensive
+ * use a "hardware" cursor (moves during screen updates)
+ * drawing new symbols automatically erases old ones
+ * characters may only be drawn at the cursor location
+ * drawing a character on the cursor will move the cursor
+ * may have fast routines for "clear entire window"
+ * may have fast routines for "clear to end of line"
+ * the bottom right corner is often dangerous
+ *
+ *
+ * This package provides support for multiple windows, each of an
+ * arbitrary size (up to 255x255), each with its own set of flags,
+ * and its own hooks to handle several low-level procedures which
+ * differ from platform to platform. Then the main program simply
+ * creates one or more "term" structures, setting the various flags
+ * and hooks in a manner appropriate for the current platform, and
+ * then it can use the various "term" structures without worrying
+ * about the underlying platform.
+ *
+ *
+ * This package allows each "grid" in each window to hold an attr/char
+ * pair, with each ranging from 0 to 255, and makes very few assumptions
+ * about the meaning of any attr/char values. Normally, we assume that
+ * "attr 0" is "black", with the semantics that "black" text should be
+ * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
+ * modified if either the "always_pict" or the "always_text" flags are
+ * set. We assume that "char 0" is "dangerous", since placing such a
+ * "char" in the middle of a string "terminates" the string, and usually
+ * we prevent its use.
+ *
+ * Finally, we use a special attr/char pair, defaulting to "attr 0" and
+ * "char 32", also known as "black space", when we "erase" or "clear"
+ * any window, but this pair can be redefined to any pair, including
+ * the standard "white space", or the bizarre "emptiness" ("attr 0"
+ * and "char 0"), as long as various obscure restrictions are met.
+ *
+ *
+ * This package provides several functions which allow a program to
+ * interact with the "term" structures. Most of the functions allow
+ * the program to "request" certain changes to the current "term",
+ * such as moving the cursor, drawing an attr/char pair, erasing a
+ * region of grids, hiding the cursor, etc. Then there is a special
+ * function which causes all of the "pending" requests to be performed
+ * in an efficient manner. There is another set of functions which
+ * allow the program to query the "requested state" of the current
+ * "term", such as asking for the cursor location, or what attr/char
+ * is at a given location, etc. There is another set of functions
+ * dealing with "keypress" events, which allows the program to ask if
+ * the user has pressed any keys, or to forget any keys the user pressed.
+ * There is a pair of functions to allow this package to memorize the
+ * contents of the current "term", and to restore these contents at
+ * a later time. There is a special function which allows the program
+ * to specify which "term" structure should be the "current" one. At
+ * the lowest level, there is a set of functions which allow a new
+ * "term" to be initialized or destroyed, and which allow this package,
+ * or a program, to access the special "hooks" defined for the current
+ * "term", and a set of functions which those "hooks" can use to inform
+ * this package of the results of certain occurances, for example, one
+ * such function allows this package to learn about user keypresses,
+ * detected by one of the special "hooks".
+ *
+ * We provide, among other things, the functions "Term_keypress()"
+ * to "react" to keypress events, and "Term_redraw()" to redraw the
+ * entire window, plus "Term_resize()" to note a new size.
+ *
+ *
+ * Note that the current "term" contains two "window images". One of
+ * these images represents the "requested" contents of the "term", and
+ * the other represents the "actual" contents of the "term", at the time
+ * of the last performance of pending requests. This package uses these
+ * two images to determine the "minimal" amount of work needed to make
+ * the "actual" contents of the "term" match the "requested" contents of
+ * the "term". This method is not perfect, but it often reduces the
+ * amount of work needed to perform the pending requests, which thus
+ * increases the speed of the program itself. This package promises
+ * that the requested changes will appear to occur either "all at once"
+ * or in a "top to bottom" order. In addition, a "cursor" is maintained,
+ * and this cursor is updated along with the actual window contents.
+ *
+ * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
+ * number of physical updates, in terms of total "work" done by the hooks
+ * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
+ * adjacent characters of the same color can both be drawn together using
+ * the "Term_text()" hook, and that "black" text can often be sent to the
+ * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
+ * is already displayed in a window, then it is not necessary to display
+ * it again. Unfortunately, this may induce slightly non-optimal results
+ * in some cases, in particular, those in which, say, a string of ten
+ * characters needs to be written, but the fifth character has already
+ * been displayed. Currently, this will cause the "Term_text()" routine
+ * to be called once for each half of the string, instead of once for the
+ * whole string, which, on some machines, may be non-optimal behavior.
+ *
+ * The new formalism includes a "displayed" screen image (old) which
+ * is actually seen by the user, a "requested" screen image (scr)
+ * which is being prepared for display, a "memorized" screen image
+ * (mem) which is used to save and restore screen images, and a
+ * "temporary" screen image (tmp) which is currently unused.
+ *
+ *
+ * Several "flags" are available in each "term" to allow the underlying
+ * visual system (which initializes the "term" structure) to "optimize"
+ * the performance of this package for the given system, or to request
+ * certain behavior which is helpful/required for the given system.
+ *
+ * The "soft_cursor" flag indicates the use of a "soft" cursor, which
+ * only moves when explicitly requested,and which is "erased" when
+ * any characters are drawn on top of it. This flag is used for all
+ * "graphic" systems which handle the cursor by "drawing" it.
+ *
+ * The "icky_corner" flag indicates that the bottom right "corner"
+ * of the windows are "icky", and "printing" anything there may
+ * induce "messy" behavior, such as "scrolling". This flag is used
+ * for most old "dumb terminal" systems.
+ *
+ *
+ * The "term" structure contains the following function "hooks":
+ *
+ * Term->init_hook = Init the term
+ * Term->nuke_hook = Nuke the term
+ * Term->user_hook = Perform user actions
+ * Term->xtra_hook = Perform extra actions
+ * Term->curs_hook = Draw (or Move) the cursor
+ * Term->wipe_hook = Draw some blank spaces
+ * Term->text_hook = Draw some text in the window
+ * Term->pict_hook = Draw some attr/chars in the window
+ *
+ * The "Term->user_hook" hook provides a simple hook to an implementation
+ * defined function, with application defined semantics. It is available
+ * to the program via the "Term_user()" function.
+ *
+ * The "Term->xtra_hook" hook provides a variety of different functions,
+ * based on the first parameter (which should be taken from the various
+ * TERM_XTRA_* defines) and the second parameter (which may make sense
+ * only for some first parameters). It is available to the program via
+ * the "Term_xtra()" function, though some first parameters are only
+ * "legal" when called from inside this package.
+ *
+ * The "Term->curs_hook" hook provides this package with a simple way
+ * to "move" or "draw" the cursor to the grid "x,y", depending on the
+ * setting of the "soft_cursor" flag. Note that the cursor is never
+ * redrawn if "nothing" has happened to the screen (even temporarily).
+ * This hook is required.
+ *
+ * The "Term->wipe_hook" hook provides this package with a simple way
+ * to "erase", starting at "x,y", the next "n" grids. This hook assumes
+ * that the input is valid. This hook is required, unless the setting
+ * of the "always_pict" or "always_text" flags makes it optional.
+ *
+ * The "Term->text_hook" hook provides this package with a simple way
+ * to "draw", starting at "x,y", the "n" chars contained in "cp", using
+ * the attr "a". This hook assumes that the input is valid, and that
+ * "n" is between 1 and 256 inclusive, but it should NOT assume that
+ * the contents of "cp" are null-terminated. This hook is required,
+ * unless the setting of the "always_pict" flag makes it optional.
+ *
+ * The "Term->pict_hook" hook provides this package with a simple way
+ * to "draw", starting at "x,y", the "n" attr/char pairs contained in
+ * the arrays "ap" and "cp". This hook assumes that the input is valid,
+ * and that "n" is between 1 and 256 inclusive, but it should NOT assume
+ * that the contents of "cp" are null-terminated. This hook is optional,
+ * unless the setting of the "always_pict" or "higher_pict" flags make
+ * it required. Note that recently, this hook was changed from taking
+ * a byte "a" and a char "c" to taking a length "n", an array of bytes
+ * "ap" and an array of chars "cp". Old implementations of this hook
+ * should now iterate over all "n" attr/char pairs.
+ *
+ *
+ * The game "Angband" uses a set of files called "main-xxx.c", for
+ * various "xxx" suffixes. Most of these contain a function called
+ * "init_xxx()", that will prepare the underlying visual system for
+ * use with Angband, and then create one or more "term" structures,
+ * using flags and hooks appropriate to the given platform, so that
+ * the "main()" function can call one (or more) of the "init_xxx()"
+ * functions, as appropriate, to prepare the required "term" structs
+ * (one for each desired sub-window), and these "init_xxx()" functions
+ * are called from a centralized "main()" function in "main.c". Other
+ * "main-xxx.c" systems contain their own "main()" function which, in
+ * addition to doing everything needed to initialize the actual program,
+ * also does everything that the normal "init_xxx()" functions would do.
+ *
+ * The game "Angband" defines, in addition to "attr 0", all of the
+ * attr codes from 1 to 15, using definitions in "defines.h", and
+ * thus the "main-xxx.c" files used by Angband must handle these
+ * attr values correctly. Also, they must handle all other attr
+ * values, though they may do so in any way they wish, for example,
+ * by always taking every attr code mod 16. Many of the "main-xxx.c"
+ * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
+ * any window, for efficiency.
+ *
+ * The game "Angband" uses the "Term_user" hook to allow any of the
+ * "main-xxx.c" files to interact with the user, by calling this hook
+ * whenever the user presses the "!" key when the game is waiting for
+ * a new command. This could be used, for example, to provide "unix
+ * shell commands" to the Unix versions of the game.
+ *
+ * See "main-xxx.c" for a simple skeleton file which can be used to
+ * create a "visual system" for a new platform when porting Angband.
+ */
+
+
+
+/*
+ * The current "term"
+ */
+term *Term = NULL;
+
+/* File handler for saving movies */
+FILE *movfile = NULL;
+int do_movies = 0; /* Later set this as a global */
+/* set to 1 if you want movies made */
+time_t lastc;
+int last_paused = 0;
+int cmovie_get_msecond(void);
+
+/* Record cmovies with millisecond frame rate */
+long cmov_last_time_msec;
+long cmov_delta_time_msec;
+
+
+/*** Local routines ***/
+
+
+/*
+ * Nuke a term_win (see below)
+ */
+static errr term_win_nuke(term_win *s, int w, int h)
+{
+ /* Free the window access arrays */
+ C_KILL(s->a, h, byte*);
+ C_KILL(s->c, h, char*);
+
+ /* Free the window content arrays */
+ C_KILL(s->va, h * w, byte);
+ C_KILL(s->vc, h * w, char);
+
+#ifdef USE_TRANSPARENCY
+
+ /* Free the terrain access arrays */
+ C_KILL(s->ta, h, byte*);
+ C_KILL(s->tc, h, char*);
+
+ /* Free the terrain content arrays */
+ C_KILL(s->vta, h * w, byte);
+ C_KILL(s->vtc, h * w, char);
+
+#ifdef USE_EGO_GRAPHICS
+
+ /* Free the ego graphics access arrays */
+ C_KILL(s->ea, h, byte*);
+ C_KILL(s->ec, h, char*);
+
+ /* Free the ego graphics content arrays */
+ C_KILL(s->vea, h * w, byte);
+ C_KILL(s->vec, h * w, char);
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize a "term_win" (using the given window size)
+ */
+static errr term_win_init(term_win *s, int w, int h)
+{
+ int y;
+
+ /* Make the window access arrays */
+ C_MAKE(s->a, h, byte*);
+ C_MAKE(s->c, h, char*);
+
+ /* Make the window content arrays */
+ C_MAKE(s->va, h * w, byte);
+ C_MAKE(s->vc, h * w, char);
+
+#ifdef USE_TRANSPARENCY
+
+ /* Make the terrain access arrays */
+ C_MAKE(s->ta, h, byte*);
+ C_MAKE(s->tc, h, char*);
+
+ /* Make the terrain content arrays */
+ C_MAKE(s->vta, h * w, byte);
+ C_MAKE(s->vtc, h * w, char);
+
+#ifdef USE_EGO_GRAPHICS
+
+ /* Make the ego graphics access arrays */
+ C_MAKE(s->ea, h, byte*);
+ C_MAKE(s->ec, h, char*);
+
+ /* Make the ego graphics content arrays */
+ C_MAKE(s->vea, h * w, byte);
+ C_MAKE(s->vec, h * w, char);
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Prepare the window access arrays */
+ for (y = 0; y < h; y++)
+ {
+ s->a[y] = s->va + w * y;
+ s->c[y] = s->vc + w * y;
+
+#ifdef USE_TRANSPARENCY
+
+ s->ta[y] = s->vta + w * y;
+ s->tc[y] = s->vtc + w * y;
+
+#ifdef USE_EGO_GRAPHICS
+
+ s->ea[y] = s->vea + w * y;
+ s->ec[y] = s->vec + w * y;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Copy a "term_win" from another
+ */
+static errr term_win_copy(term_win *s, term_win *f, int w, int h)
+{
+ int x, y;
+
+ /* Copy contents */
+ for (y = 0; y < h; y++)
+ {
+ byte *f_aa = f->a[y];
+ char *f_cc = f->c[y];
+
+ byte *s_aa = s->a[y];
+ char *s_cc = s->c[y];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *f_taa = f->ta[y];
+ char *f_tcc = f->tc[y];
+
+ byte *s_taa = s->ta[y];
+ char *s_tcc = s->tc[y];
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *f_eaa = f->ea[y];
+ char *f_ecc = f->ec[y];
+
+ byte *s_eaa = s->ea[y];
+ char *s_ecc = s->ec[y];
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ for (x = 0; x < w; x++)
+ {
+ *s_aa++ = *f_aa++;
+ *s_cc++ = *f_cc++;
+
+#ifdef USE_TRANSPARENCY
+ *s_taa++ = *f_taa++;
+ *s_tcc++ = *f_tcc++;
+
+#ifdef USE_EGO_GRAPHICS
+ *s_eaa++ = *f_eaa++;
+ *s_ecc++ = *f_ecc++;
+#endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ }
+ }
+
+ /* Copy cursor */
+ s->cx = f->cx;
+ s->cy = f->cy;
+ s->cu = f->cu;
+ s->cv = f->cv;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** External hooks ***/
+
+
+/*
+ * Execute the "Term->user_hook" hook, if available (see above).
+ */
+errr Term_user(int n)
+{
+ /* Verify the hook */
+ if (!Term->user_hook) return ( -1);
+
+ /* Call the hook */
+ return ((*Term->user_hook)(n));
+}
+
+/*
+ * Execute the "Term->xtra_hook" hook, if available (see above).
+ * And *hacky* get a return code
+ */
+long Term_xtra_long;
+char scansubdir_dir[1024];
+int scansubdir_max = 0;
+cptr scansubdir_result[255];
+errr Term_xtra(int n, int v)
+{
+ /* Verify the hook */
+ if (!Term->xtra_hook) return ( -1);
+
+ /* Call the hook */
+ return ((*Term->xtra_hook)(n, v));
+}
+
+
+
+/*** Fake hooks ***/
+
+
+/*
+ * Hack -- fake hook for "Term_curs()" (see above)
+ */
+static errr Term_curs_hack(int x, int y)
+{
+ /* Compiler silliness */
+ if (x || y) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+/*
+ * Hack -- fake hook for "Term_wipe()" (see above)
+ */
+static errr Term_wipe_hack(int x, int y, int n)
+{
+ /* Compiler silliness */
+ if (x || y || n) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+/*
+ * Hack -- fake hook for "Term_text()" (see above)
+ */
+static errr Term_text_hack(int x, int y, int n, byte a, const char *cp)
+{
+ /* Compiler silliness */
+ if (x || y || n || a || cp) return ( -2);
+
+ /* Oops */
+ return ( -1);
+}
+
+/*
+ * Hack -- fake hook for "Term_pict()" (see above)
+ */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp)
+#else /* USE_EGO_GRAPHICS */
+static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp)
+#endif /* USE_TRANSPARENCY */
+{
+ /* Compiler silliness */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ if (x || y || n || ap || cp || tap || tcp || eap || ecp) return ( -2);
+#else /* USE_EGO_GRAPHICS */
+if (x || y || n || ap || cp || tap || tcp) return ( -2);
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ if (x || y || n || ap || cp) return ( -2);
+#endif /* USE_TRANSPARENCY */
+
+ /* Oops */
+ return ( -1);
+}
+
+
+
+/*** Efficient routines ***/
+
+
+/*
+ * Mentally draw an attr/char at a given location
+ *
+ * Assumes given location and values are valid.
+ */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc, byte ea, char ec)
+#else /* USE_EGO_GRAPHICS */
+void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc)
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+void Term_queue_char(int x, int y, byte a, char c)
+#endif /* USE_TRANSPARENCY */
+{
+ term_win *scrn = Term->scr;
+
+ byte *scr_aa = &scrn->a[y][x];
+ char *scr_cc = &scrn->c[y][x];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *scr_taa = &scrn->ta[y][x];
+ char *scr_tcc = &scrn->tc[y][x];
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *scr_eaa = &scrn->ea[y][x];
+ char *scr_ecc = &scrn->ec[y][x];
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == a) && (*scr_cc == c) &&
+ (*scr_taa == ta) && (*scr_tcc == tc) &&
+ (*scr_eaa == ea) && (*scr_ecc == ec)) return;
+
+#else /* USE_EGO_GRAPHICS */
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == a) && (*scr_cc == c) &&
+ (*scr_taa == ta) && (*scr_tcc == tc)) return;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == a) && (*scr_cc == c)) return;
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Save the "literal" information */
+ *scr_aa = a;
+ *scr_cc = c;
+
+#ifdef USE_TRANSPARENCY
+
+ *scr_taa = ta;
+ *scr_tcc = tc;
+
+#ifdef USE_EGO_GRAPHICS
+
+ *scr_eaa = ea;
+ *scr_ecc = ec;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info for this row */
+ if (x < Term->x1[y]) Term->x1[y] = x;
+ if (x > Term->x2[y]) Term->x2[y] = x;
+}
+
+
+/*
+ * Mentally draw a string of attr/chars at a given location
+ *
+ * Assumes given location and values are valid.
+ *
+ * This function is designed to be fast, with no consistancy checking.
+ * It is used to update the map in the game.
+ */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc, byte *ea, char *ec)
+#else /* USE_EGO_GRAPHICS */
+void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc)
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+void Term_queue_line(int x, int y, int n, byte *a, char *c)
+#endif /* USE_TRANSPARENCY */
+{
+ term_win *scrn = Term->scr;
+
+ int x1 = -1;
+ int x2 = -1;
+
+ byte *scr_aa = &scrn->a[y][x];
+ char *scr_cc = &scrn->c[y][x];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *scr_taa = &scrn->ta[y][x];
+ char *scr_tcc = &scrn->tc[y][x];
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *scr_eaa = &scrn->ea[y][x];
+ char *scr_ecc = &scrn->ec[y][x];
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ while (n--)
+ {
+
+#ifdef USE_TRANSPARENCY
+
+#ifdef USE_EGO_GRAPHICS
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == *a) && (*scr_cc == *c) &&
+ (*scr_taa == *ta) && (*scr_tcc == *tc) &&
+ (*scr_eaa == *ea) && (*scr_ecc == *ec))
+#else /* USE_EGO_GRAPHICS */
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == *a) && (*scr_cc == *c) &&
+ (*scr_taa == *ta) && (*scr_tcc == *tc))
+#endif /* USE_EGO_GRAPHICS */
+ {
+ x++;
+ a++;
+ c++;
+ ta++;
+ tc++;
+#ifdef USE_EGO_GRAPHICS
+ ea++;
+ ec++;
+#endif /* USE_EGO_GRAPHICS */
+ scr_aa++;
+ scr_cc++;
+ scr_taa++;
+ scr_tcc++;
+#ifdef USE_EGO_GRAPHICS
+ scr_eaa++;
+ scr_ecc++;
+#endif /* USE_EGO_GRAPHICS */
+ continue;
+ }
+
+ /* Save the "literal" information */
+ *scr_taa++ = *ta++;
+ *scr_tcc++ = *tc++;
+
+#ifdef USE_EGO_GRAPHICS
+ /* Save the "literal" information */
+ *scr_eaa++ = *ea++;
+ *scr_ecc++ = *ec++;
+#endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == *a) && (*scr_cc == *c))
+ {
+ x++;
+ a++;
+ c++;
+ scr_aa++;
+ scr_cc++;
+ continue;
+ }
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Save the "literal" information */
+ *scr_aa++ = *a++;
+ *scr_cc++ = *c++;
+
+ /* Track minimum changed column */
+ if (x1 < 0) x1 = x;
+
+ /* Track maximum changed column */
+ x2 = x;
+
+ x++;
+ }
+
+ /* Expand the "change area" as needed */
+ if (x1 >= 0)
+ {
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info in this row */
+ if (x1 < Term->x1[y]) Term->x1[y] = x1;
+ if (x2 > Term->x2[y]) Term->x2[y] = x2;
+ }
+}
+
+
+
+/*
+ * Mentally draw some attr/chars at a given location
+ *
+ * Assumes that (x,y) is a valid location, that the first "n" characters
+ * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
+ * a valid location, so the first "n" characters of "s" can all be added
+ * starting at (x,y) without causing any illegal operations.
+ */
+void Term_queue_chars(int x, int y, int n, byte a, cptr s)
+{
+ int x1 = -1, x2 = -1;
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+#endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ /* Queue the attr/chars */
+ for ( ; n; x++, s++, n--)
+ {
+ int oa = scr_aa[x];
+ int oc = scr_cc[x];
+
+#ifdef USE_TRANSPARENCY
+
+ int ota = scr_taa[x];
+ int otc = scr_tcc[x];
+
+#ifdef USE_EGO_GRAPHICS
+
+ int oea = scr_eaa[x];
+ int oec = scr_ecc[x];
+
+ /* Hack -- Ignore non-changes */
+ if ((oa == a) && (oc == *s) &&
+ (ota == 0) && (otc == 0) &&
+ (oea == 0) && (oec == 0)) continue;
+
+#else /* USE_EGO_GRAPHICS */
+
+ /* Hack -- Ignore non-changes */
+ if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0)) continue;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Hack -- Ignore non-changes */
+ if ((oa == a) && (oc == *s)) continue;
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Save the "literal" information */
+ scr_aa[x] = a;
+ scr_cc[x] = *s;
+
+#ifdef USE_TRANSPARENCY
+
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+#ifdef USE_EGO_GRAPHICS
+
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Note the "range" of window updates */
+ if (x1 < 0) x1 = x;
+ x2 = x;
+ }
+
+ /* Expand the "change area" as needed */
+ if (x1 >= 0)
+ {
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info in this row */
+ if (x1 < Term->x1[y]) Term->x1[y] = x1;
+ if (x2 > Term->x2[y]) Term->x2[y] = x2;
+ }
+}
+
+
+
+/*** Refresh routines ***/
+
+
+/*
+ * Flush a row of the current window (see "Term_fresh")
+ *
+ * Display text using "Term_pict()"
+ */
+static void Term_fresh_row_pict(int y, int x1, int x2)
+{
+ int x;
+
+ byte *old_aa = Term->old->a[y];
+ char *old_cc = Term->old->c[y];
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *old_taa = Term->old->ta[y];
+ char *old_tcc = Term->old->tc[y];
+
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+ byte ota;
+ char otc;
+
+ byte nta;
+ char ntc;
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *old_eaa = Term->old->ea[y];
+ char *old_ecc = Term->old->ec[y];
+
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+ byte oea;
+ char oec;
+
+ byte nea;
+ char nec;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ byte oa;
+ char oc;
+
+ byte na;
+ char nc;
+
+ /* Scan "modified" columns */
+ for (x = x1; x <= x2; x++)
+ {
+ /* See what is currently here */
+ oa = old_aa[x];
+ oc = old_cc[x];
+
+ /* See what is desired there */
+ na = scr_aa[x];
+ nc = scr_cc[x];
+
+#ifdef USE_TRANSPARENCY
+
+ ota = old_taa[x];
+ otc = old_tcc[x];
+
+ nta = scr_taa[x];
+ ntc = scr_tcc[x];
+
+#ifdef USE_EGO_GRAPHICS
+
+ oea = old_eaa[x];
+ oec = old_ecc[x];
+
+ nea = scr_eaa[x];
+ nec = scr_ecc[x];
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc) &&
+ (nta == ota) && (ntc == otc) &&
+ (nea == oea) && (nec == oec))
+
+#else /* USE_EGO_GRAPHICS */
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
+
+#endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc))
+
+#endif /* USE_TRANSPARENCY */
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending attr/char pairs */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ (void)((*Term->pict_hook)(fx, y, fn,
+ &scr_aa[fx], &scr_cc[fx],
+ &scr_taa[fx], &scr_tcc[fx],
+ &scr_eaa[fx], &scr_ecc[fx]));
+#else /* USE_EGO_GRAPHICS */
+ (void)((*Term->pict_hook)(fx, y, fn,
+ &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
+#endif
+#else /* USE_TRANSPARENCY */
+ (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx]));
+#endif /* USE_TRANSPARENCY */
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Skip */
+ continue;
+ }
+ /* Save new contents */
+ old_aa[x] = na;
+ old_cc[x] = nc;
+
+#ifdef USE_TRANSPARENCY
+ old_taa[x] = nta;
+ old_tcc[x] = ntc;
+
+#ifdef USE_EGO_GRAPHICS
+ old_eaa[x] = nea;
+ old_ecc[x] = nec;
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending attr/char pairs */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ (void)((*Term->pict_hook)(fx, y, fn,
+ &scr_aa[fx], &scr_cc[fx],
+ &scr_taa[fx], &scr_tcc[fx],
+ &scr_eaa[fx], &scr_ecc[fx]));
+#else /* USE_EGO_GRAPHICS */
+ (void)((*Term->pict_hook)(fx, y, fn,
+ &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx]));
+#endif /* USE_TRANSPARENCY */
+ }
+}
+
+
+
+/*
+ * Flush a row of the current window (see "Term_fresh")
+ *
+ * Display text using "Term_text()" and "Term_wipe()",
+ * but use "Term_pict()" for high-bit attr/char pairs
+ */
+static void Term_fresh_row_both(int y, int x1, int x2)
+{
+ int x;
+
+ byte *old_aa = Term->old->a[y];
+ char *old_cc = Term->old->c[y];
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+#ifdef USE_TRANSPARENCY
+ byte *old_taa = Term->old->ta[y];
+ char *old_tcc = Term->old->tc[y];
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+ byte ota;
+ char otc;
+ byte nta;
+ char ntc;
+
+#ifdef USE_EGO_GRAPHICS
+ byte *old_eaa = Term->old->ea[y];
+ char *old_ecc = Term->old->ec[y];
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+
+ byte oea;
+ char oec;
+ byte nea;
+ char nec;
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* The "always_text" flag */
+ int always_text = Term->always_text;
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ /* Pending attr */
+ byte fa = Term->attr_blank;
+
+ byte oa;
+ char oc;
+
+ byte na;
+ char nc;
+
+ /* Scan "modified" columns */
+ for (x = x1; x <= x2; x++)
+ {
+ /* See what is currently here */
+ oa = old_aa[x];
+ oc = old_cc[x];
+
+ /* See what is desired there */
+ na = scr_aa[x];
+ nc = scr_cc[x];
+
+#ifdef USE_TRANSPARENCY
+
+ ota = old_taa[x];
+ otc = old_tcc[x];
+
+ nta = scr_taa[x];
+ ntc = scr_tcc[x];
+
+#ifdef USE_EGO_GRAPHICS
+ oea = old_eaa[x];
+ oec = old_ecc[x];
+
+ nea = scr_eaa[x];
+ nec = scr_ecc[x];
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc) &&
+ (nta == ota) && (ntc == otc) &&
+ (nea == oea) && (nec == oec))
+
+#else /* USE_EGO_GRAPHICS */
+
+/* Handle unchanged grids */
+ if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
+
+#endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc))
+
+#endif /* USE_TRANSPARENCY */
+
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Skip */
+ continue;
+ }
+
+ /* Save new contents */
+ old_aa[x] = na;
+ old_cc[x] = nc;
+
+#ifdef USE_TRANSPARENCY
+
+ old_taa[x] = nta;
+ old_tcc[x] = ntc;
+
+#ifdef USE_EGO_GRAPHICS
+
+ old_eaa[x] = nea;
+ old_ecc[x] = nec;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* 2nd byte of bigtile */
+ if (na == 255) continue;
+
+ /* Handle high-bit attr/chars */
+ if (na & 0x80)
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ /* Forget */
+ fn = 0;
+ }
+
+#ifdef USE_TRANSPARENCY
+
+#ifdef USE_EGO_GRAPHICS
+
+ /* Hack -- Draw the special attr/char pair */
+ (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc, &nea, &nec));
+
+#else /* USE_EGO_GRAPHICS */
+
+/* Hack -- Draw the special attr/char pair */
+ (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
+
+#endif /* USE_EGO_GRAPHICS */
+
+#else /* USE_TRANSPARENCY */
+
+ /* Hack -- Draw the special attr/char pair */
+ (void)((*Term->pict_hook)(x, y, 1, &na, &nc));
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Skip */
+ continue;
+ }
+
+ /* Notice new color */
+ if (fa != na)
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw the pending chars */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Hack -- Erase "leading" spaces */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Save the new color */
+ fa = na;
+ }
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ }
+}
+
+
+/*
+ * Flush a row of the current window (see "Term_fresh")
+ *
+ * Display text using "Term_text()" and "Term_wipe()"
+ */
+static void Term_fresh_row_text(int y, int x1, int x2)
+{
+ int x;
+
+ byte *old_aa = Term->old->a[y];
+ char *old_cc = Term->old->c[y];
+
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+ /* The "always_text" flag */
+ int always_text = Term->always_text;
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ /* Pending attr */
+ byte fa = Term->attr_blank;
+
+ byte oa;
+ char oc;
+
+ byte na;
+ char nc;
+
+ /* Scan "modified" columns */
+ for (x = x1; x <= x2; x++)
+ {
+ /* See what is currently here */
+ oa = old_aa[x];
+ oc = old_cc[x];
+
+ /* See what is desired there */
+ na = scr_aa[x];
+ nc = scr_cc[x];
+
+ /* Handle unchanged grids */
+ if ((na == oa) && (nc == oc))
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Skip */
+ continue;
+ }
+
+ /* Save new contents */
+ old_aa[x] = na;
+ old_cc[x] = nc;
+
+ /* Notice new color */
+ if (fa != na)
+ {
+ /* Flush */
+ if (fn)
+ {
+ /* Draw the pending chars */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+
+ /* Hack -- Erase "leading" spaces */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Save the new color */
+ fa = na;
+ }
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ /* Draw pending chars (normal) */
+ if (fa || always_text)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+
+ /* Draw pending chars (black) */
+ else
+ {
+ (void)((*Term->wipe_hook)(fx, y, fn));
+ }
+ }
+}
+
+
+
+
+
+/*
+ * Actually perform all requested changes to the window
+ *
+ * If absolutely nothing has changed, not even temporarily, or if the
+ * current "Term" is not mapped, then this function will return 1 and
+ * do absolutely nothing.
+ *
+ * Note that when "soft_cursor" is true, we erase the cursor (if needed)
+ * whenever anything has changed, and redraw it (if needed) after all of
+ * the screen updates are complete. This will induce a small amount of
+ * "cursor flicker" but only when the screen has been updated. If the
+ * screen is updated and then restored, you may still get this flicker.
+ *
+ * When "soft_cursor" is not true, we make the cursor invisible before
+ * doing anything else if it is supposed to be invisible by the time we
+ * are done, and we make it visible after moving it to its final location
+ * after all of the screen updates are complete.
+ *
+ * Note that "Term_xtra(TERM_XTRA_CLEAR,0)" must erase the entire screen,
+ * including the cursor, if needed, and may place the cursor anywhere.
+ *
+ * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
+ * after any row "y" has been "flushed", unless the "Term->never_frosh"
+ * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
+ * all of the rows have been "flushed".
+ *
+ * Note the use of three different functions to handle the actual flush,
+ * based on the settings of the "Term->always_pict" and "Term->higher_pict"
+ * flags (see below).
+ *
+ * The three helper functions (above) work by collecting similar adjacent
+ * grids into stripes, and then sending each stripe to "Term->pict_hook",
+ * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
+ * "Term->always_pict" and "Term->higher_pict" flags, which select which
+ * of the helper functions to call to flush each row.
+ *
+ * The helper functions currently "skip" any grids which already contain
+ * the desired contents. This may or may not be the best method, especially
+ * when the desired content fits nicely into the current stripe. For example,
+ * it might be better to go ahead and queue them while allowed, but keep a
+ * count of the "trailing skipables", then, when time to flush, or when a
+ * "non skippable" is found, force a flush if there are too many skippables.
+ *
+ * Perhaps an "initialization" stage, where the "text" (and "attr")
+ * buffers are "filled" with information, converting "blanks" into
+ * a convenient representation, and marking "skips" with "zero chars",
+ * and then some "processing" is done to determine which chars to skip.
+ *
+ * Currently, the helper functions are optimal for systems which prefer
+ * to "print a char + move a char + print a char" to "print three chars",
+ * and for applications that do a lot of "detailed" color printing.
+ *
+ * In the two "queue" functions, total "non-changes" are "pre-skipped".
+ * The helper functions must also handle situations in which the contents
+ * of a grid are changed, but then changed back to the original value,
+ * and situations in which two grids in the same row are changed, but
+ * the grids between them are unchanged.
+ *
+ * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
+ * will be used instead of "Term_fresh_row_text()". This allows all the
+ * modified grids to be collected into stripes of attr/char pairs, which
+ * are then sent to the "Term->pict_hook" hook, which can draw these pairs
+ * in whatever way it would like.
+ *
+ * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
+ * will be used instead of "Term_fresh_row_text()". This allows all the
+ * "special" attr/char pairs (in which both the attr and char have the
+ * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
+ * hook, which can draw these pairs in whatever way it would like.
+ *
+ * Normally, the "Term_wipe()" function is used only to display "blanks"
+ * that were induced by "Term_clear()" or "Term_erase()", and then only
+ * if the "attr_blank" and "char_blank" fields have not been redefined
+ * to use "white space" instead of the default "black space". Actually,
+ * the "Term_wipe()" function is used to display all "black" text, such
+ * as the default "spaces" created by "Term_clear()" and "Term_erase()".
+ *
+ * Note that the "Term->always_text" flag will disable the use of the
+ * "Term_wipe()" function hook entirely, and force all text, even text
+ * drawn in the color "black", to be explicitly drawn. This is useful
+ * for machines which implement "Term_wipe()" by just drawing spaces.
+ *
+ * Note that the "Term->always_pict" flag will disable the use of the
+ * "Term_wipe()" function entirely, and force everything, even text
+ * drawn in the attr "black", to be explicitly drawn.
+ *
+ * Note that if no "black" text is ever drawn, and if "attr_blank" is
+ * not "zero", then the "Term_wipe" hook will never be used, even if
+ * the "Term->always_text" flag is not set.
+ *
+ * This function does nothing unless the "Term" is "mapped", which allows
+ * certain systems to optimize the handling of "closed" windows.
+ *
+ * On systems with a "soft" cursor, we must explicitly erase the cursor
+ * before flushing the output, if needed, to prevent a "jumpy" refresh.
+ * The actual method for this is horrible, but there is very little that
+ * we can do to simplify it efficiently. XXX XXX XXX
+ *
+ * On systems with a "hard" cursor, we will "hide" the cursor before
+ * flushing the output, if needed, to avoid a "flickery" refresh. It
+ * would be nice to *always* hide the cursor during the refresh, but
+ * this might be expensive (and/or ugly) on some machines.
+ *
+ * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
+ * or "Term_pict()" or "Term_text()" on the bottom right corner of the
+ * window, which might induce "scrolling" or other nasty stuff on old
+ * dumb terminals. This flag is handled very efficiently. We assume
+ * that the "Term_curs()" call will prevent placing the cursor in the
+ * corner, if needed, though I doubt such placement is ever a problem.
+ * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
+ * together may result in undefined behavior.
+ */
+errr Term_fresh(void)
+{
+ int x, y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ int y1 = Term->y1;
+ int y2 = Term->y2;
+
+ term_win *old = Term->old;
+ term_win *scr = Term->scr;
+
+
+ /* Do nothing unless "mapped" */
+ if (!Term->mapped_flag) return (1);
+
+
+ /* Trivial Refresh */
+ if ((y1 > y2) &&
+ (scr->cu == old->cu) &&
+ (scr->cv == old->cv) &&
+ (scr->cx == old->cx) &&
+ (scr->cy == old->cy) &&
+ !(Term->total_erase))
+ {
+ /* Nothing */
+ return (1);
+ }
+
+
+ /* Paranoia -- use "fake" hooks to prevent core dumps */
+ if (!Term->curs_hook) Term->curs_hook = Term_curs_hack;
+ if (!Term->wipe_hook) Term->wipe_hook = Term_wipe_hack;
+ if (!Term->text_hook) Term->text_hook = Term_text_hack;
+ if (!Term->pict_hook) Term->pict_hook = Term_pict_hack;
+
+
+ /* Handle "total erase" */
+ if (Term->total_erase)
+ {
+ byte na = Term->attr_blank;
+ char nc = Term->char_blank;
+
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ if (!cmovie_get_msecond())
+ {
+ fprintf(movfile, "S:%ld:\n", cmov_delta_time_msec);
+ }
+ fprintf(movfile, "C:\n");
+ last_paused = 0;
+ }
+
+ /* Physically erase the entire window */
+ Term_xtra(TERM_XTRA_CLEAR, 0);
+
+ /* Hack -- clear all "cursor" data */
+ old->cv = old->cu = old->cx = old->cy = 0;
+
+ /* Wipe each row */
+ for (y = 0; y < h; y++)
+ {
+ byte *aa = old->a[y];
+ char *cc = old->c[y];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *taa = old->ta[y];
+ char *tcc = old->tc[y];
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *eaa = old->ea[y];
+ char *ecc = old->ec[y];
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+
+ /* Wipe each column */
+ for (x = 0; x < w; x++)
+ {
+ /* Wipe each grid */
+ *aa++ = na;
+ *cc++ = nc;
+
+#ifdef USE_TRANSPARENCY
+
+ *taa++ = na;
+ *tcc++ = nc;
+
+#ifdef USE_EGO_GRAPHICS
+
+ *eaa++ = na;
+ *ecc++ = nc;
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ }
+ }
+
+ /* Redraw every row */
+ Term->y1 = y1 = 0;
+ Term->y2 = y2 = h - 1;
+
+ /* Redraw every column */
+ for (y = 0; y < h; y++)
+ {
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Forget "total erase" */
+ Term->total_erase = FALSE;
+ }
+
+
+ /* Cursor update -- Erase old Cursor */
+ if (Term->soft_cursor)
+ {
+ /* Cursor was visible */
+ if (!old->cu && old->cv)
+ {
+ int tx = old->cx;
+ int ty = old->cy;
+
+ byte *old_aa = old->a[ty];
+ char *old_cc = old->c[ty];
+
+ byte oa = old_aa[tx];
+ char oc = old_cc[tx];
+
+#ifdef USE_TRANSPARENCY
+
+ byte *old_taa = old->ta[ty];
+ char *old_tcc = old->tc[ty];
+
+ byte ota = old_taa[tx];
+ char otc = old_tcc[tx];
+
+#ifdef USE_EGO_GRAPHICS
+
+ byte *old_eaa = old->ea[ty];
+ char *old_ecc = old->ec[ty];
+
+ byte oea = old_eaa[tx];
+ char oec = old_ecc[tx];
+
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+ /* Hack -- use "Term_pict()" always */
+ if (Term->always_pict)
+ {
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc, &oea, &oec));
+#else /* USE_EGO_GRAPHICS */
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc));
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc));
+#endif /* USE_TRANSPARENCY */
+ }
+
+ /* Hack -- use "Term_pict()" sometimes */
+ else if (Term->higher_pict && (oa & 0x80))
+ {
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc, &oea, &oec));
+#else /* USE_EGO_GRAPHICS */
+(void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc));
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc));
+#endif /* USE_TRANSPARENCY */
+ }
+
+ /* Hack -- restore the actual character */
+ else if (oa || Term->always_text)
+ {
+ (void)((*Term->text_hook)(tx, ty, 1, oa, &oc));
+ }
+
+ /* Hack -- erase the grid */
+ else
+ {
+ (void)((*Term->wipe_hook)(tx, ty, 1));
+ }
+ }
+ }
+
+ /* Cursor Update -- Erase old Cursor */
+ else
+ {
+ /* Cursor will be invisible */
+ if (scr->cu || !scr->cv)
+ {
+ /* Make the cursor invisible */
+ Term_xtra(TERM_XTRA_SHAPE, 0);
+ }
+ }
+
+
+ /* Something to update */
+ if (y1 <= y2)
+ {
+ /* Handle "icky corner" */
+ if (Term->icky_corner)
+ {
+ /* Avoid the corner */
+ if (y2 >= h - 1)
+ {
+ /* Avoid the corner */
+ if (Term->x2[h - 1] > w - 2)
+ {
+ /* Avoid the corner */
+ Term->x2[h - 1] = w - 2;
+ }
+ }
+ }
+
+
+ /* Scan the "modified" rows */
+ for (y = y1; y <= y2; ++y)
+ {
+ int x1 = Term->x1[y];
+ int x2 = Term->x2[y];
+
+ /* Flush each "modified" row */
+ if (x1 <= x2)
+ {
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ /* Most magic happens here */
+ cmovie_record_line(y);
+ last_paused = 0;
+ }
+
+ /* Always use "Term_pict()" */
+ if (Term->always_pict)
+ {
+ /* Flush the row */
+ Term_fresh_row_pict(y, x1, x2);
+ }
+
+ /* Sometimes use "Term_pict()" */
+ else if (Term->higher_pict)
+ {
+ /* Flush the row */
+ Term_fresh_row_both(y, x1, x2);
+ }
+
+ /* Never use "Term_pict()" */
+ else
+ {
+ /* Flush the row */
+ Term_fresh_row_text(y, x1, x2);
+ }
+
+ /* This row is all done */
+ Term->x1[y] = w;
+ Term->x2[y] = 0;
+
+ /* Hack -- Flush that row (if allowed) */
+ if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
+ }
+ }
+
+ /* No rows are invalid */
+ Term->y1 = h;
+ Term->y2 = 0;
+ }
+
+
+ /* Cursor update -- Show new Cursor */
+ if (Term->soft_cursor)
+ {
+ /* Draw the cursor */
+ if (!scr->cu && scr->cv)
+ {
+ /* Call the cursor display routine */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+ }
+ }
+
+ /* Cursor Update -- Show new Cursor */
+ else
+ {
+ /* The cursor is useless, hide it */
+ if (scr->cu)
+ {
+ /* Paranoia -- Put the cursor NEAR where it belongs */
+ (void)((*Term->curs_hook)(w - 1, scr->cy));
+
+ /* Make the cursor invisible */
+ /* Term_xtra(TERM_XTRA_SHAPE, 0); */
+ }
+
+ /* The cursor is invisible, hide it */
+ else if (!scr->cv)
+ {
+ /* Paranoia -- Put the cursor where it belongs */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+
+ /* Make the cursor invisible */
+ /* Term_xtra(TERM_XTRA_SHAPE, 0); */
+ }
+
+ /* The cursor is visible, display it correctly */
+ else
+ {
+ /* Put the cursor where it belongs */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+
+ /* Make the cursor visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+ }
+ }
+
+
+ /* Save the "cursor state" */
+ old->cu = scr->cu;
+ old->cv = scr->cv;
+ old->cx = scr->cx;
+ old->cy = scr->cy;
+
+
+ /* Actually flush the output */
+ Term_xtra(TERM_XTRA_FRESH, 0);
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Output routines ***/
+
+
+/*
+ * Set the cursor visibility
+ */
+errr Term_set_cursor(int v)
+{
+ /* Already done */
+ if (Term->scr->cv == v) return (1);
+
+ /* Change */
+ Term->scr->cv = v;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place the cursor at a given location
+ *
+ * Note -- "illegal" requests do not move the cursor.
+ */
+errr Term_gotoxy(int x, int y)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Remember the cursor */
+ Term->scr->cx = x;
+ Term->scr->cy = y;
+
+ /* The cursor is not useless */
+ Term->scr->cu = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * At a given location, place an attr/char
+ * Do not change the cursor position
+ * No visual changes until "Term_fresh()".
+ */
+errr Term_draw(int x, int y, byte a, char c)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify location */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Paranoia -- illegal char */
+ if (!c) return ( -2);
+
+ /* Queue it for later */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ Term_queue_char(x, y, a, c, 0, 0, 0, 0);
+#else /* USE_EGO_GRAPHICS */
+Term_queue_char(x, y, a, c, 0, 0);
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ Term_queue_char(x, y, a, c);
+#endif /* USE_TRANSPARENCY */
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Using the given attr, add the given char at the cursor.
+ *
+ * We return "-2" if the character is "illegal". XXX XXX
+ *
+ * We return "-1" if the cursor is currently unusable.
+ *
+ * We queue the given attr/char for display at the current
+ * cursor location, and advance the cursor to the right,
+ * marking it as unuable and returning "1" if it leaves
+ * the screen, and otherwise returning "0".
+ *
+ * So when this function, or the following one, return a
+ * positive value, future calls to either function will
+ * return negative ones.
+ */
+errr Term_addch(byte a, char c)
+{
+ int w = Term->wid;
+
+ /* Handle "unusable" cursor */
+ if (Term->scr->cu) return ( -1);
+
+ /* Paranoia -- no illegal chars */
+ if (!c) return ( -2);
+
+ /* Queue the given character for display */
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0, 0, 0);
+#else /* USE_EGO_GRAPHICS */
+ Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ Term_queue_char(Term->scr->cx, Term->scr->cy, a, c);
+#endif /* USE_TRANSPARENCY */
+
+ /* Advance the cursor */
+ Term->scr->cx++;
+
+ /* Success */
+ if (Term->scr->cx < w) return (0);
+
+ /* Note "Useless" cursor */
+ Term->scr->cu = 1;
+
+ /* Note "Useless" cursor */
+ return (1);
+}
+
+
+/*
+ * At the current location, using an attr, add a string
+ *
+ * We also take a length "n", using negative values to imply
+ * the largest possible value, and then we use the minimum of
+ * this length and the "actual" length of the string as the
+ * actual number of characters to attempt to display, never
+ * displaying more characters than will actually fit, since
+ * we do NOT attempt to "wrap" the cursor at the screen edge.
+ *
+ * We return "-1" if the cursor is currently unusable.
+ * We return "N" if we were "only" able to write "N" chars,
+ * even if all of the given characters fit on the screen,
+ * and mark the cursor as unusable for future attempts.
+ *
+ * So when this function, or the preceding one, return a
+ * positive value, future calls to either function will
+ * return negative ones.
+ */
+errr Term_addstr(int n, byte a, cptr s)
+{
+ int k;
+
+ int w = Term->wid;
+
+ errr res = 0;
+
+ /* Handle "unusable" cursor */
+ if (Term->scr->cu) return ( -1);
+
+ /* Obtain maximal length */
+ k = (n < 0) ? (w + 1) : n;
+
+ /* Obtain the usable string length */
+ for (n = 0; (n < k) && s[n]; n++) /* loop */;
+
+ /* React to reaching the edge of the screen */
+ if (Term->scr->cx + n >= w) res = n = w - Term->scr->cx;
+
+ /* Queue the first "n" characters for display */
+ Term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
+
+ /* Advance the cursor */
+ Term->scr->cx += n;
+
+ /* Hack -- Notice "Useless" cursor */
+ if (res) Term->scr->cu = 1;
+
+ /* Success (usually) */
+ return (res);
+}
+
+
+/*
+ * Move to a location and, using an attr, add a char
+ */
+errr Term_putch(int x, int y, byte a, char c)
+{
+ errr res;
+
+ /* Move first */
+ if ((res = Term_gotoxy(x, y)) != 0) return (res);
+
+ /* Then add the char */
+ if ((res = Term_addch(a, c)) != 0) return (res);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Move to a location and, using an attr, add a string
+ */
+errr Term_putstr(int x, int y, int n, byte a, cptr s)
+{
+ errr res;
+
+ /* Move first */
+ if ((res = Term_gotoxy(x, y)) != 0) return (res);
+
+ /* Then add the string */
+ if ((res = Term_addstr(n, a, s)) != 0) return (res);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Place cursor at (x,y), and clear the next "n" chars
+ */
+errr Term_erase(int x, int y, int n)
+{
+ int i;
+
+ int w = Term->wid;
+ /* int h = Term->hgt; */
+
+ int x1 = -1;
+ int x2 = -1;
+
+ int na = Term->attr_blank;
+ int nc = Term->char_blank;
+
+ byte *scr_aa;
+ char *scr_cc;
+
+#ifdef USE_TRANSPARENCY
+ byte *scr_taa;
+ char *scr_tcc;
+
+#ifdef USE_EGO_GRAPHICS
+ byte *scr_eaa;
+ char *scr_ecc;
+#endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ /* Place cursor */
+ if (Term_gotoxy(x, y)) return ( -1);
+
+ /* Force legal size */
+ if (x + n > w) n = w - x;
+
+ /* Fast access */
+ scr_aa = Term->scr->a[y];
+ scr_cc = Term->scr->c[y];
+
+#ifdef USE_TRANSPARENCY
+ scr_taa = Term->scr->ta[y];
+ scr_tcc = Term->scr->tc[y];
+
+#ifdef USE_EGO_GRAPHICS
+ scr_eaa = Term->scr->ea[y];
+ scr_ecc = Term->scr->ec[y];
+#endif
+#endif /* USE_TRANSPARENCY */
+
+ if (n > 0 && (byte)scr_cc[x] == 255 && scr_aa[x] == 255)
+ {
+ x--;
+ n++;
+ }
+
+ /* Scan every column */
+ for (i = 0; i < n; i++, x++)
+ {
+ int oa = scr_aa[x];
+ int oc = scr_cc[x];
+
+ /* Hack -- Ignore "non-changes" */
+ if ((oa == na) && (oc == nc)) continue;
+
+ /* Save the "literal" information */
+ scr_aa[x] = na;
+ scr_cc[x] = nc;
+
+#ifdef USE_TRANSPARENCY
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+#ifdef USE_EGO_GRAPHICS
+ scr_eaa[x] = 0;
+ scr_ecc[x] = 0;
+#endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ /* Track minimum changed column */
+ if (x1 < 0) x1 = x;
+
+ /* Track maximum changed column */
+ x2 = x;
+ }
+
+ /* Expand the "change area" as needed */
+ if (x1 >= 0)
+ {
+ /* Check for new min/max row info */
+ if (y < Term->y1) Term->y1 = y;
+ if (y > Term->y2) Term->y2 = y;
+
+ /* Check for new min/max col info in this row */
+ if (x1 < Term->x1[y]) Term->x1[y] = x1;
+ if (x2 > Term->x2[y]) Term->x2[y] = x2;
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Clear the entire window, and move to the top left corner
+ *
+ * Note the use of the special "total_erase" code
+ */
+errr Term_clear(void)
+{
+ int x, y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ byte na = Term->attr_blank;
+ char nc = Term->char_blank;
+
+ /* Cursor usable */
+ Term->scr->cu = 0;
+
+ /* Cursor to the top left */
+ Term->scr->cx = Term->scr->cy = 0;
+
+ /* Wipe each row */
+ for (y = 0; y < h; y++)
+ {
+ byte *scr_aa = Term->scr->a[y];
+ char *scr_cc = Term->scr->c[y];
+
+#ifdef USE_TRANSPARENCY
+ byte *scr_taa = Term->scr->ta[y];
+ char *scr_tcc = Term->scr->tc[y];
+
+#ifdef USE_EGO_GRAPHICS
+ byte *scr_eaa = Term->scr->ea[y];
+ char *scr_ecc = Term->scr->ec[y];
+#endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+
+ /* Wipe each column */
+ for (x = 0; x < w; x++)
+ {
+ scr_aa[x] = na;
+ scr_cc[x] = nc;
+
+#ifdef USE_TRANSPARENCY
+ scr_taa[x] = 0;
+ scr_tcc[x] = 0;
+
+#ifdef USE_EGO_GRAPHICS
+ scr_eaa[x] = 0;
+ scr_ecc[x] = 0;
+#endif /* USE_EGO_GRAPHICS */
+#endif /* USE_TRANSPARENCY */
+ }
+
+ /* This row has changed */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Every row has changed */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Success */
+ return (0);
+}
+
+
+
+
+
+/*
+ * Redraw (and refresh) the whole window.
+ */
+errr Term_redraw(void)
+{
+ /* Pat */
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ if (!cmovie_get_msecond())
+ {
+ fprintf(movfile, "S:%ld:\n", cmov_delta_time_msec);
+ }
+ last_paused = 1;
+ }
+ /* Endpat */
+
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Hack -- Refresh */
+ Term_fresh();
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Redraw part of a window.
+ */
+errr Term_redraw_section(int x1, int y1, int x2, int y2)
+{
+ int i, j;
+
+ char *c_ptr;
+
+#if 0 // DGDGDGDG
+ /* Pat */
+ if ((do_movies == 1) && IN_MAINWINDOW)
+ {
+ if (!cmovie_get_msecond())
+ {
+ fprintf(movfile, "W:1:\n");
+ }
+ }
+ /* Endpat */
+#endif
+
+ /* Bounds checking */
+ if (y2 >= Term->hgt) y2 = Term->hgt - 1;
+ if (x2 >= Term->wid) x2 = Term->wid - 1;
+ if (y1 < 0) y1 = 0;
+ if (x1 < 0) x1 = 0;
+
+ /* Set y limits */
+ Term->y1 = y1;
+ Term->y2 = y2;
+
+ /* Set the x limits */
+ for (i = Term->y1; i <= Term->y2; i++)
+ {
+ Term->x1[i] = x1;
+ Term->x2[i] = x2;
+
+ c_ptr = Term->old->c[i];
+
+ /* Clear the section so it is redrawn */
+ for (j = x1; j <= x2; j++)
+ {
+ /* Hack - set the old character to "none" */
+ c_ptr[j] = 0;
+ }
+ }
+
+ /* Hack -- Refresh */
+ Term_fresh();
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Access routines ***/
+
+
+/*
+ * Extract the cursor visibility
+ */
+errr Term_get_cursor(int *v)
+{
+ /* Extract visibility */
+ (*v) = Term->scr->cv;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Extract the current window size
+ */
+errr Term_get_size(int *w, int *h)
+{
+ /* Access the cursor */
+ (*w) = Term->wid;
+ (*h) = Term->hgt;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Extract the current cursor location
+ */
+errr Term_locate(int *x, int *y)
+{
+ /* Access the cursor */
+ (*x) = Term->scr->cx;
+ (*y) = Term->scr->cy;
+
+ /* Warn about "useless" cursor */
+ if (Term->scr->cu) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * At a given location, determine the "current" attr and char
+ * Note that this refers to what will be on the window after the
+ * next call to "Term_fresh()". It may or may not already be there.
+ */
+errr Term_what(int x, int y, byte *a, char *c)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify location */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Direct access */
+ (*a) = Term->scr->a[y][x];
+ (*c) = Term->scr->c[y][x];
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Input routines ***/
+
+
+/*
+ * Flush and forget the input
+ */
+errr Term_flush(void)
+{
+ /* Hack -- Flush all events */
+ Term_xtra(TERM_XTRA_FLUSH, 0);
+
+ /* Forget all keypresses */
+ Term->key_head = Term->key_tail = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Add a keypress to the "queue"
+ */
+errr Term_keypress(int k)
+{
+ /* Hack -- Refuse to enqueue non-keys */
+ if (!k) return ( -1);
+
+ /* Store the char, advance the queue */
+ Term->key_queue[Term->key_head++] = k;
+
+ /* Circular queue, handle wrap */
+ if (Term->key_head == Term->key_size) Term->key_head = 0;
+
+ /* Success (unless overflow) */
+ if (Term->key_head != Term->key_tail) return (0);
+
+#if 0
+ /* Hack -- Forget the oldest key */
+ if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
+#endif
+
+ /* Problem */
+ return (1);
+}
+
+
+/*
+ * Add a keypress to the FRONT of the "queue"
+ */
+errr Term_key_push(int k)
+{
+ /* Hack -- Refuse to enqueue non-keys */
+ if (!k) return ( -1);
+
+ /* Hack -- Overflow may induce circular queue */
+ if (Term->key_tail == 0) Term->key_tail = Term->key_size;
+
+ /* Back up, Store the char */
+ Term->key_queue[--Term->key_tail] = k;
+
+ /* Success (unless overflow) */
+ if (Term->key_head != Term->key_tail) return (0);
+
+#if 0
+ /* Hack -- Forget the oldest key */
+ if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
+#endif
+
+ /* Problem */
+ return (1);
+}
+
+
+
+
+
+/*
+ * Check for a pending keypress on the key queue.
+ *
+ * Store the keypress, if any, in "ch", and return "0".
+ * Otherwise store "zero" in "ch", and return "1".
+ *
+ * Wait for a keypress if "wait" is true.
+ *
+ * Remove the keypress if "take" is true.
+ */
+errr Term_inkey(char *ch, bool wait, bool take)
+{
+ /* Assume no key */
+ (*ch) = '\0';
+
+ /* Hack -- get bored */
+ if (!Term->never_bored)
+ {
+ /* Process random events */
+ Term_xtra(TERM_XTRA_BORED, 0);
+ }
+
+ /* PatN */
+ if ((do_movies == 1) && (last_paused == 0) && (!cmovie_get_msecond()))
+ {
+ fprintf(movfile, "S:%ld:\n", cmov_delta_time_msec);
+ last_paused = 1;
+ }
+ /* PatNEnd */
+
+ /* Wait */
+ if (wait)
+ {
+ /* Process pending events while necessary */
+ while (Term->key_head == Term->key_tail)
+ {
+ /* Process events (wait for one) */
+ Term_xtra(TERM_XTRA_EVENT, TRUE);
+ }
+ }
+
+ /* Do not Wait */
+ else
+ {
+ /* Process pending events if necessary */
+ if (Term->key_head == Term->key_tail)
+ {
+ /* Process events (do not wait) */
+ Term_xtra(TERM_XTRA_EVENT, FALSE);
+ }
+ }
+
+ /* No keys are ready */
+ if (Term->key_head == Term->key_tail) return (1);
+
+ /* Extract the next keypress */
+ (*ch) = Term->key_queue[Term->key_tail];
+
+ /* If requested, advance the queue, wrap around if necessary */
+ if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Extra routines ***/
+
+
+/*
+ * Save the "requested" screen into the "memorized" screen
+ *
+ * Every "Term_save()" should match exactly one "Term_load()"
+ */
+errr Term_save(void)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Create */
+ if (!Term->mem)
+ {
+ /* Allocate window */
+ MAKE(Term->mem, term_win);
+
+ /* Initialize window */
+ term_win_init(Term->mem, w, h);
+ }
+
+ /* Grab */
+ term_win_copy(Term->mem, Term->scr, w, h);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Same as before but can save more than once
+ */
+term_win* Term_save_to(void)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+ term_win *save;
+
+ /* Allocate window */
+ MAKE(save, term_win);
+
+ /* Initialize window */
+ term_win_init(save, w, h);
+
+ /* Grab */
+ term_win_copy(save, Term->scr, w, h);
+
+ /* Success */
+ return (save);
+}
+
+/*
+ * Restore the "requested" contents (see above).
+ *
+ * Every "Term_save()" should match exactly one "Term_load()"
+ */
+errr Term_load(void)
+{
+ int y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Create */
+ if (!Term->mem)
+ {
+ /* Allocate window */
+ MAKE(Term->mem, term_win);
+
+ /* Initialize window */
+ term_win_init(Term->mem, w, h);
+ }
+
+ /* Load */
+ term_win_copy(Term->scr, Term->mem, w, h);
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Same as previous but allow to save more than one
+ */
+errr Term_load_from(term_win *save, bool final)
+{
+ int y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Create */
+ if (!save)
+ {
+ return (1);
+ }
+
+ /* Load */
+ term_win_copy(Term->scr, save, w, h);
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Free is requested */
+ if (final)
+ FREE(save, term_win);
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Exchange the "requested" screen with the "tmp" screen
+ */
+errr Term_exchange(void)
+{
+ int y;
+
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ term_win *exchanger;
+
+
+ /* Create */
+ if (!Term->tmp)
+ {
+ /* Allocate window */
+ MAKE(Term->tmp, term_win);
+
+ /* Initialize window */
+ term_win_init(Term->tmp, w, h);
+ }
+
+ /* Swap */
+ exchanger = Term->scr;
+ Term->scr = Term->tmp;
+ Term->tmp = exchanger;
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * React to a new physical window size.
+ */
+errr Term_resize(int w, int h)
+{
+ int i;
+
+ int wid, hgt;
+
+ byte *hold_x1;
+ byte *hold_x2;
+
+ term_win *hold_old;
+ term_win *hold_scr;
+ term_win *hold_mem;
+ term_win *hold_tmp;
+
+ /* Resizing is forbidden */
+ if (Term->fixed_shape) return ( -1);
+
+ /* Ignore illegal changes */
+ if ((w < 1) || (h < 1)) return ( -1);
+
+
+ /* Ignore non-changes */
+ if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile)) return (1);
+ use_bigtile = arg_bigtile;
+
+ /* Minimum dimensions */
+ wid = MIN(Term->wid, w);
+ hgt = MIN(Term->hgt, h);
+
+ /* Save scanners */
+ hold_x1 = Term->x1;
+ hold_x2 = Term->x2;
+
+ /* Save old window */
+ hold_old = Term->old;
+
+ /* Save old window */
+ hold_scr = Term->scr;
+
+ /* Save old window */
+ hold_mem = Term->mem;
+
+ /* Save old window */
+ hold_tmp = Term->tmp;
+
+ /* Create new scanners */
+ C_MAKE(Term->x1, h, byte);
+ C_MAKE(Term->x2, h, byte);
+
+ /* Create new window */
+ MAKE(Term->old, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->old, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->old, hold_old, wid, hgt);
+
+ /* Create new window */
+ MAKE(Term->scr, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->scr, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->scr, hold_scr, wid, hgt);
+
+ /* If needed */
+ if (hold_mem)
+ {
+ /* Create new window */
+ MAKE(Term->mem, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->mem, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->mem, hold_mem, wid, hgt);
+ }
+
+ /* If needed */
+ if (hold_tmp)
+ {
+ /* Create new window */
+ MAKE(Term->tmp, term_win);
+
+ /* Initialize new window */
+ term_win_init(Term->tmp, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->tmp, hold_tmp, wid, hgt);
+ }
+
+ /* Free some arrays */
+ C_KILL(hold_x1, Term->hgt, byte);
+ C_KILL(hold_x2, Term->hgt, byte);
+
+ /* Nuke */
+ term_win_nuke(hold_old, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_old, term_win);
+
+ /* Illegal cursor */
+ if (Term->old->cx >= w) Term->old->cu = 1;
+ if (Term->old->cy >= h) Term->old->cu = 1;
+
+ /* Nuke */
+ term_win_nuke(hold_scr, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_scr, term_win);
+
+ /* Illegal cursor */
+ if (Term->scr->cx >= w) Term->scr->cu = 1;
+ if (Term->scr->cy >= h) Term->scr->cu = 1;
+
+ /* If needed */
+ if (hold_mem)
+ {
+ /* Nuke */
+ term_win_nuke(hold_mem, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_mem, term_win);
+
+ /* Illegal cursor */
+ if (Term->mem->cx >= w) Term->mem->cu = 1;
+ if (Term->mem->cy >= h) Term->mem->cu = 1;
+ }
+
+ /* If needed */
+ if (hold_tmp)
+ {
+ /* Nuke */
+ term_win_nuke(hold_tmp, Term->wid, Term->hgt);
+
+ /* Kill */
+ KILL(hold_tmp, term_win);
+
+ /* Illegal cursor */
+ if (Term->tmp->cx >= w) Term->tmp->cu = 1;
+ if (Term->tmp->cy >= h) Term->tmp->cu = 1;
+ }
+
+ /* Save new size */
+ Term->wid = w;
+ Term->hgt = h;
+
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Assume change */
+ for (i = 0; i < h; i++)
+ {
+ /* Assume change */
+ Term->x1[i] = 0;
+ Term->x2[i] = w - 1;
+ }
+
+ /* Assume change */
+ Term->y1 = 0;
+ Term->y2 = h - 1;
+
+ /* Execute the "resize_hook" hook, if available */
+ if (Term->resize_hook)
+ {
+ Term->resize_hook();
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Activate a new Term (and deactivate the current Term)
+ *
+ * This function is extremely important, and also somewhat bizarre.
+ * It is the only function that should "modify" the value of "Term".
+ *
+ * To "create" a valid "term", one should do "term_init(t)", then
+ * set the various flags and hooks, and then do "Term_activate(t)".
+ */
+errr Term_activate(term *t)
+{
+ /* Hack -- already done */
+ if (Term == t) return (1);
+
+ /* Deactivate the old Term */
+ if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
+
+ /* Hack -- Call the special "init" hook */
+ if (t && !t->active_flag)
+ {
+ /* Call the "init" hook */
+ if (t->init_hook) (*t->init_hook)(t);
+
+ /* Remember */
+ t->active_flag = TRUE;
+
+ /* Assume mapped */
+ t->mapped_flag = TRUE;
+ }
+
+ /* Remember the Term */
+ Term = t;
+
+ /* Activate the new Term */
+ if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Nuke a term
+ */
+errr term_nuke(term *t)
+{
+ int w = t->wid;
+ int h = t->hgt;
+
+ /* Hack -- Call the special "nuke" hook */
+ if (t->active_flag)
+ {
+ /* Call the "nuke" hook */
+ if (t->nuke_hook) (*t->nuke_hook)(t);
+
+ /* Remember */
+ t->active_flag = FALSE;
+
+ /* Assume not mapped */
+ t->mapped_flag = FALSE;
+ }
+
+
+ /* Nuke "displayed" */
+ term_win_nuke(t->old, w, h);
+
+ /* Kill "displayed" */
+ KILL(t->old, term_win);
+
+ /* Nuke "requested" */
+ term_win_nuke(t->scr, w, h);
+
+ /* Kill "requested" */
+ KILL(t->scr, term_win);
+
+ /* If needed */
+ if (t->mem)
+ {
+ /* Nuke "memorized" */
+ term_win_nuke(t->mem, w, h);
+
+ /* Kill "memorized" */
+ KILL(t->mem, term_win);
+ }
+
+ /* If needed */
+ if (t->tmp)
+ {
+ /* Nuke "temporary" */
+ term_win_nuke(t->tmp, w, h);
+
+ /* Kill "temporary" */
+ KILL(t->tmp, term_win);
+ }
+
+ /* Free some arrays */
+ C_KILL(t->x1, h, byte);
+ C_KILL(t->x2, h, byte);
+
+ /* Free the input queue */
+ C_KILL(t->key_queue, t->key_size, char);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize a term, using a window of the given size.
+ * Also prepare the "input queue" for "k" keypresses
+ * By default, the cursor starts out "invisible"
+ * By default, we "erase" using "black spaces"
+ */
+errr term_init(term *t, int w, int h, int k)
+{
+ int y;
+
+ /* Wipe it */
+ (void)WIPE(t, term);
+
+
+ /* Prepare the input queue */
+ t->key_head = t->key_tail = 0;
+
+ /* Determine the input queue size */
+ t->key_size = k;
+
+ /* Allocate the input queue */
+ C_MAKE(t->key_queue, t->key_size, char);
+
+
+ /* Save the size */
+ t->wid = w;
+ t->hgt = h;
+
+ /* Allocate change arrays */
+ C_MAKE(t->x1, h, byte);
+ C_MAKE(t->x2, h, byte);
+
+
+ /* Allocate "displayed" */
+ MAKE(t->old, term_win);
+
+ /* Initialize "displayed" */
+ term_win_init(t->old, w, h);
+
+
+ /* Allocate "requested" */
+ MAKE(t->scr, term_win);
+
+ /* Initialize "requested" */
+ term_win_init(t->scr, w, h);
+
+
+ /* Assume change */
+ for (y = 0; y < h; y++)
+ {
+ /* Assume change */
+ t->x1[y] = 0;
+ t->x2[y] = w - 1;
+ }
+
+ /* Assume change */
+ t->y1 = 0;
+ t->y2 = h - 1;
+
+ /* Force "total erase" */
+ t->total_erase = TRUE;
+
+
+ /* Default "blank" */
+ t->attr_blank = 0;
+ t->char_blank = ' ';
+
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Determine if we are called in the same second as the last time?
+ * This *ASSUMES* that time_t is seconds past something. Is this portable?
+ */
+int cmovie_get_msecond(void)
+{
+#ifndef USE_PRECISE_CMOVIE
+ /* Not very precise, but portable */
+ time_t thisc;
+
+ thisc = time(NULL);
+
+ cmov_delta_time_msec = 300;
+
+ if (thisc == lastc)
+ {
+ return 1;
+ }
+ return 0;
+#else /* Very precise but needs main-foo.c to define TERM_XTRA_GET_DELAY */
+Term_xtra(TERM_XTRA_GET_DELAY, 0);
+
+ cmov_delta_time_msec = Term_xtra_long - cmov_last_time_msec;
+ cmov_last_time_msec = Term_xtra_long;
+ return 0;
+#endif
+}
+
+void cmovie_init_second()
+{
+#ifndef USE_PRECISE_CMOVIE
+ /* Not very precise, but portable */
+ cmov_last_time_msec = 0;
+#else /* Precise but need main-foo.c to have TERM_XTRA_GET_DELAY */
+ Term_xtra(TERM_XTRA_GET_DELAY, 0);
+ cmov_last_time_msec = Term_xtra_long;
+#endif
+}
diff --git a/src/z-term.h b/src/z-term.h
new file mode 100644
index 00000000..d7a34530
--- /dev/null
+++ b/src/z-term.h
@@ -0,0 +1,363 @@
+/* File: z-term.h */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifndef INCLUDED_Z_TERM_H
+#define INCLUDED_Z_TERM_H
+
+#include "h-basic.h"
+
+#define IN_MAINWINDOW (Term == term_screen)
+
+/*
+ * A term_win is a "window" for a Term
+ *
+ * - Cursor Useless/Visible codes
+ * - Cursor Location (see "Useless")
+ *
+ * - Array[h] -- Access to the attribute array
+ * - Array[h] -- Access to the character array
+ *
+ * - Array[h*w] -- Attribute array
+ * - Array[h*w] -- Character array
+ *
+ * Note that the attr/char pair at (x,y) is a[y][x]/c[y][x]
+ * and that the row of attr/chars at (0,y) is a[y]/c[y]
+ */
+
+typedef struct term_win term_win;
+
+struct term_win
+{
+ bool cu, cv;
+ byte cx, cy;
+
+ byte **a;
+ char **c;
+
+ byte *va;
+ char *vc;
+
+#ifdef USE_TRANSPARENCY
+ byte **ta;
+ char **tc;
+
+ byte *vta;
+ char *vtc;
+
+#ifdef USE_EGO_GRAPHICS
+ byte **ea;
+ char **ec;
+
+ byte *vea;
+ char *vec;
+#endif /* USE_EGO_GRAPHICS */
+
+#endif /* USE_TRANSPARENCY */
+
+};
+
+
+
+/*
+ * An actual "term" structure
+ *
+ * - Extra "user" info (used by application)
+ *
+ * - Extra "data" info (used by implementation)
+ *
+ *
+ * - Flag "user_flag"
+ * An extra "user" flag (used by application)
+ *
+ *
+ * - Flag "data_flag"
+ * An extra "data" flag (used by implementation)
+ *
+ *
+ * - Flag "active_flag"
+ * This "term" is "active"
+ *
+ * - Flag "mapped_flag"
+ * This "term" is "mapped"
+ *
+ * - Flag "total_erase"
+ * This "term" should be fully erased
+ *
+ * - Flag "fixed_shape"
+ * This "term" is not allowed to resize
+ *
+ * - Flag "icky_corner"
+ * This "term" has an "icky" corner grid
+ *
+ * - Flag "soft_cursor"
+ * This "term" uses a "software" cursor
+ *
+ * - Flag "always_pict"
+ * Use the "Term_pict()" routine for all text
+ *
+ * - Flag "higher_pict"
+ * Use the "Term_pict()" routine for special text
+ *
+ * - Flag "always_text"
+ * Use the "Term_text()" routine for invisible text
+ *
+ * - Flag "unused_flag"
+ * Reserved for future use
+ *
+ * - Flag "never_bored"
+ * Never call the "TERM_XTRA_BORED" action
+ *
+ * - Flag "never_frosh"
+ * Never call the "TERM_XTRA_FROSH" action
+ *
+ *
+ * - Value "attr_blank"
+ * Use this "attr" value for "blank" grids
+ *
+ * - Value "char_blank"
+ * Use this "char" value for "blank" grids
+ *
+ *
+ * - Ignore this pointer
+ *
+ * - Keypress Queue -- various data
+ *
+ * - Keypress Queue -- pending keys
+ *
+ *
+ * - Window Width (max 255)
+ * - Window Height (max 255)
+ *
+ * - Minimum modified row
+ * - Maximum modified row
+ *
+ * - Minimum modified column (per row)
+ * - Maximum modified column (per row)
+ *
+ *
+ * - Displayed screen image
+ * - Requested screen image
+ *
+ * - Temporary screen image
+ * - Memorized screen image
+ *
+ *
+ * - Hook for init-ing the term
+ * - Hook for nuke-ing the term
+ *
+ * - Hook for user actions
+ *
+ * - Hook for extra actions
+ *
+ * - Hook for placing the cursor
+ *
+ * - Hook for drawing some blank spaces
+ *
+ * - Hook for drawing a string of chars using an attr
+ *
+ * - Hook for drawing a sequence of special attr/char pairs
+ */
+
+typedef struct term term;
+
+struct term
+{
+ vptr user;
+
+ vptr data;
+
+ bool user_flag;
+
+ bool data_flag;
+
+ bool active_flag;
+ bool mapped_flag;
+ bool total_erase;
+ bool fixed_shape;
+ bool icky_corner;
+ bool soft_cursor;
+ bool always_pict;
+ bool higher_pict;
+ bool always_text;
+ bool unused_flag;
+ bool never_bored;
+ bool never_frosh;
+
+ byte attr_blank;
+ char char_blank;
+
+ char *key_queue;
+
+ u16b key_head;
+ u16b key_tail;
+ u16b key_xtra;
+ u16b key_size;
+
+ byte wid;
+ byte hgt;
+
+ byte y1;
+ byte y2;
+
+ byte *x1;
+ byte *x2;
+
+ term_win *old;
+ term_win *scr;
+
+ term_win *tmp;
+ term_win *mem;
+
+ void (*init_hook)(term *t);
+ void (*nuke_hook)(term *t);
+
+ errr (*user_hook)(int n);
+
+ errr (*xtra_hook)(int n, int v);
+
+ errr (*curs_hook)(int x, int y);
+
+ errr (*wipe_hook)(int x, int y, int n);
+
+ errr (*text_hook)(int x, int y, int n, byte a, cptr s);
+
+ void (*resize_hook)(void);
+
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+ errr (*pict_hook)(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp);
+#else /* USE_EGO_GRAPHICS */
+ errr (*pict_hook)(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp);
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+ errr (*pict_hook)(int x, int y, int n, const byte *ap, const char *cp);
+#endif /* USE_TRANSPARENCY */
+
+};
+
+
+
+
+
+
+
+/**** Available Constants ****/
+
+
+/*
+ * Definitions for the "actions" of "Term_xtra()"
+ *
+ * These values may be used as the first parameter of "Term_xtra()",
+ * with the second parameter depending on the "action" itself. Many
+ * of the actions shown below are optional on at least one platform.
+ *
+ * The "TERM_XTRA_EVENT" action uses "v" to "wait" for an event
+ * The "TERM_XTRA_SHAPE" action uses "v" to "show" the cursor
+ * The "TERM_XTRA_FROSH" action uses "v" for the index of the row
+ * The "TERM_XTRA_SOUND" action uses "v" for the index of a sound
+ * The "TERM_XTRA_ALIVE" action uses "v" to "activate" (or "close")
+ * The "TERM_XTRA_LEVEL" action uses "v" to "resume" (or "suspend")
+ * The "TERM_XTRA_DELAY" action uses "v" as a "millisecond" value
+ *
+ * The other actions do not need a "v" code, so "zero" is used.
+ */
+#define TERM_XTRA_EVENT 1 /* Process some pending events */
+#define TERM_XTRA_FLUSH 2 /* Flush all pending events */
+#define TERM_XTRA_CLEAR 3 /* Clear the entire window */
+#define TERM_XTRA_SHAPE 4 /* Set cursor shape (optional) */
+#define TERM_XTRA_FROSH 5 /* Flush one row (optional) */
+#define TERM_XTRA_FRESH 6 /* Flush all rows (optional) */
+#define TERM_XTRA_NOISE 7 /* Make a noise (optional) */
+#define TERM_XTRA_SOUND 8 /* Make a sound (optional) */
+#define TERM_XTRA_BORED 9 /* Handle stuff when bored (optional) */
+#define TERM_XTRA_REACT 10 /* React to global changes (optional) */
+#define TERM_XTRA_ALIVE 11 /* Change the "hard" level (optional) */
+#define TERM_XTRA_LEVEL 12 /* Change the "soft" level (optional) */
+#define TERM_XTRA_DELAY 13 /* Delay some milliseconds (optional) */
+#define TERM_XTRA_GET_DELAY 14 /* Get the cuyrrent time in milliseconds (optional) */
+#define TERM_XTRA_SCANSUBDIR 15 /* Scan for subdir in a dir */
+#define TERM_XTRA_RENAME_MAIN_WIN 16 /* Rename the main game window */
+
+
+/**** Available Variables ****/
+
+extern term *Term;
+extern FILE *movfile;
+extern int do_movies;
+extern int last_paused;
+
+
+/**** Available Functions ****/
+
+extern errr Term_user(int n);
+extern errr Term_xtra(int n, int v);
+extern long Term_xtra_long;
+extern char scansubdir_dir[1024];
+extern int scansubdir_max;
+extern cptr scansubdir_result[255];
+
+#ifdef USE_TRANSPARENCY
+#ifdef USE_EGO_GRAPHICS
+extern void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc, byte ea, char ec);
+
+extern void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc, byte *ea, char *ec);
+#else /* USE_EGO_GRAPHICS */
+extern void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc);
+
+extern void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc);
+#endif /* USE_EGO_GRAPHICS */
+#else /* USE_TRANSPARENCY */
+extern void Term_queue_char(int x, int y, byte a, char c);
+
+extern void Term_queue_line(int x, int y, int n, byte *a, char *c);
+#endif /* USE_TRANSPARENCY */
+
+extern void Term_queue_chars(int x, int y, int n, byte a, cptr s);
+
+extern errr Term_fresh(void);
+extern errr Term_set_cursor(int v);
+extern errr Term_gotoxy(int x, int y);
+extern errr Term_draw(int x, int y, byte a, char c);
+extern errr Term_addch(byte a, char c);
+extern errr Term_addstr(int n, byte a, cptr s);
+extern errr Term_putch(int x, int y, byte a, char c);
+extern errr Term_putstr(int x, int y, int n, byte a, cptr s);
+extern errr Term_erase(int x, int y, int n);
+extern errr Term_clear(void);
+extern errr Term_redraw(void);
+extern errr Term_redraw_section(int x1, int y1, int x2, int y2);
+
+extern errr Term_get_cursor(int *v);
+extern errr Term_get_size(int *w, int *h);
+extern errr Term_locate(int *x, int *y);
+extern errr Term_what(int x, int y, byte *a, char *c);
+
+extern errr Term_flush(void);
+extern errr Term_keypress(int k);
+extern errr Term_key_push(int k);
+extern errr Term_inkey(char *ch, bool wait, bool take);
+
+extern errr Term_save(void);
+extern term_win* Term_save_to(void);
+extern errr Term_load(void);
+extern errr Term_load_from(term_win *save, bool final);
+
+extern errr Term_exchange(void);
+
+extern errr Term_resize(int w, int h);
+
+extern errr Term_activate(term *t);
+
+extern errr term_nuke(term *t);
+extern errr term_init(term *t, int w, int h, int k);
+
+#endif
+
+
diff --git a/src/z-util.c b/src/z-util.c
new file mode 100644
index 00000000..f0842b07
--- /dev/null
+++ b/src/z-util.c
@@ -0,0 +1,231 @@
+/* File: z-util.c */
+
+/* Purpose: Low level utilities -BEN- */
+
+#include "z-util.h"
+
+
+
+/*
+ * Global variables for temporary use
+ */
+char char_tmp = 0;
+byte byte_tmp = 0;
+sint sint_tmp = 0;
+uint uint_tmp = 0;
+long long_tmp = 0;
+huge huge_tmp = 0;
+errr errr_tmp = 0;
+
+
+/*
+ * Global pointers for temporary use
+ */
+cptr cptr_tmp = NULL;
+vptr vptr_tmp = NULL;
+
+
+
+
+/*
+ * Constant bool meaning true
+ */
+bool bool_true = 1;
+
+/*
+ * Constant bool meaning false
+ */
+bool bool_false = 0;
+
+
+/*
+ * Global NULL cptr
+ */
+cptr cptr_null = NULL;
+
+
+/*
+ * Global NULL vptr
+ */
+vptr vptr_null = NULL;
+
+
+
+/*
+ * Global SELF vptr
+ */
+vptr vptr_self = (vptr)(&vptr_self);
+
+
+
+/*
+ * Convenient storage of the program name
+ */
+cptr argv0 = NULL;
+
+
+
+/*
+ * A routine that does nothing
+ */
+void func_nothing(void)
+{
+ /* Do nothing */
+}
+
+
+/*
+ * A routine that always returns "success"
+ */
+errr func_success(void)
+{
+ return (0);
+}
+
+
+/*
+ * A routine that always returns a simple "problem code"
+ */
+errr func_problem(void)
+{
+ return (1);
+}
+
+
+/*
+ * A routine that always returns a simple "failure code"
+ */
+errr func_failure(void)
+{
+ return ( -1);
+}
+
+
+
+/*
+ * A routine that always returns "true"
+ */
+bool func_true(void)
+{
+ return (1);
+}
+
+
+/*
+ * A routine that always returns "false"
+ */
+bool func_false(void)
+{
+ return (0);
+}
+
+
+
+
+/*
+ * Determine if string "t" is equal to string "t"
+ */
+bool streq(cptr a, cptr b)
+{
+ return (!strcmp(a, b));
+}
+
+
+/*
+ * Determine if string "t" is a suffix of string "s"
+ */
+bool suffix(cptr s, cptr t)
+{
+ int tlen = strlen(t);
+ int slen = strlen(s);
+
+ /* Check for incompatible lengths */
+ if (tlen > slen) return (FALSE);
+
+ /* Compare "t" to the end of "s" */
+ return (!strcmp(s + slen - tlen, t));
+}
+
+
+
+
+/*
+ * Redefinable "plog" action
+ */
+void (*plog_aux)(cptr) = NULL;
+
+/*
+ * Print (or log) a "warning" message (ala "perror()")
+ * Note the use of the (optional) "plog_aux" hook.
+ */
+void plog(cptr str)
+{
+ /* Use the "alternative" function if possible */
+ if (plog_aux) (*plog_aux)(str);
+
+ /* Just do a labeled fprintf to stderr */
+ else (void)(fprintf(stderr, "%s: %s\n", argv0 ? argv0 : "???", str));
+}
+
+
+
+/*
+ * Redefinable "quit" action
+ */
+void (*quit_aux)(cptr) = NULL;
+
+/*
+ * Exit (ala "exit()"). If 'str' is NULL, do "exit(0)".
+ * If 'str' begins with "+" or "-", do "exit(atoi(str))".
+ * Otherwise, plog() 'str' and exit with an error code of -1.
+ * But always use 'quit_aux', if set, before anything else.
+ */
+void quit(cptr str)
+{
+ /* Attempt to use the aux function */
+ if (quit_aux) (*quit_aux)(str);
+
+ /* Success */
+ if (!str) (void)(exit(0));
+
+ /* Extract a "special error code" */
+ if ((str[0] == '-') || (str[0] == '+')) (void)(exit(atoi(str)));
+
+ /* Send the string to plog() */
+ plog(str);
+
+ /* Failure */
+ (void)(exit( -1));
+}
+
+
+
+/*
+ * Redefinable "core" action
+ */
+void (*core_aux)(cptr) = NULL;
+
+/*
+ * Dump a core file, after printing a warning message
+ * As with "quit()", try to use the "core_aux()" hook first.
+ */
+void core(cptr str)
+{
+ char *crash = NULL;
+
+ /* Use the aux function */
+ if (core_aux) (*core_aux)(str);
+
+ /* Dump the warning string */
+ if (str) plog(str);
+
+ /* Attempt to Crash */
+ (*crash) = (*crash);
+
+ /* Be sure we exited */
+ quit("core() failed");
+}
+
+
+
+
diff --git a/src/z-util.h b/src/z-util.h
new file mode 100644
index 00000000..64871cf2
--- /dev/null
+++ b/src/z-util.h
@@ -0,0 +1,84 @@
+/* File z-util.h */
+
+#ifndef INCLUDED_Z_UTIL_H
+#define INCLUDED_Z_UTIL_H
+
+#include "h-basic.h"
+
+
+/*
+ * Extremely basic stuff, like global temp and constant variables.
+ * Also, some very useful low level functions, such as "streq()".
+ * All variables and functions in this file are "addressable".
+ */
+
+
+/**** Available variables ****/
+
+/* Temporary Vars */
+extern char char_tmp;
+extern byte byte_tmp;
+extern sint sint_tmp;
+extern uint uint_tmp;
+extern long long_tmp;
+extern huge huge_tmp;
+extern errr errr_tmp;
+
+/* Temporary Pointers */
+extern cptr cptr_tmp;
+extern vptr vptr_tmp;
+
+
+/* Constant pointers (NULL) */
+extern cptr cptr_null;
+extern vptr vptr_null;
+
+
+/* A bizarre vptr that always points at itself */
+extern vptr vptr_self;
+
+
+/* A cptr to the name of the program */
+extern cptr argv0;
+
+
+/* Aux functions */
+extern void (*plog_aux)(cptr);
+extern void (*quit_aux)(cptr);
+extern void (*core_aux)(cptr);
+
+
+/**** Available Functions ****/
+
+/* Function that does nothing */
+extern void func_nothing(void);
+
+/* Functions that return basic "errr" codes */
+extern errr func_success(void);
+extern errr func_problem(void);
+extern errr func_failure(void);
+
+/* Functions that return bools */
+extern bool func_true(void);
+extern bool func_false(void);
+
+
+/* Test equality, prefix, suffix */
+extern bool streq(cptr s, cptr t);
+extern bool prefix(cptr s, cptr t);
+extern bool suffix(cptr s, cptr t);
+
+
+/* Print an error message */
+extern void plog(cptr str);
+
+/* Exit, with optional message */
+extern void quit(cptr str);
+
+/* Dump core, with optional message */
+extern void core(cptr str);
+
+
+
+#endif
+
diff --git a/src/z-virt.c b/src/z-virt.c
new file mode 100644
index 00000000..c9277166
--- /dev/null
+++ b/src/z-virt.c
@@ -0,0 +1,187 @@
+/* File: z-virt.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+/* Purpose: Memory management routines -BEN- */
+
+#include "z-virt.h"
+
+#include "z-util.h"
+
+
+/*
+ * Allow debugging messages to track memory usage.
+ */
+#ifdef VERBOSE_RALLOC
+static long virt_make = 0;
+static long virt_kill = 0;
+static long virt_size = 0;
+#endif
+
+
+/*
+ * Optional auxiliary "rnfree" function
+ */
+vptr (*rnfree_aux)(vptr, huge) = NULL;
+
+/*
+ * Free some memory (allocated by ralloc), return NULL
+ */
+vptr rnfree(vptr p, huge len)
+{
+ /* Easy to free zero bytes */
+ if (len == 0) return (NULL);
+
+#ifdef VERBOSE_RALLOC
+
+ /* Decrease memory count */
+ virt_kill += len;
+
+ /* Message */
+ if (len > virt_size)
+ {
+ char buf[80];
+ sprintf(buf, "Kill (%ld): %ld - %ld = %ld.",
+ len, virt_make, virt_kill, virt_make - virt_kill);
+ plog(buf);
+ }
+
+#endif
+
+ /* Use the "aux" function */
+ if (rnfree_aux) return ((*rnfree_aux)(p, len));
+
+ /* Use "free" */
+ free ((char*)(p));
+
+ /* Done */
+ return (NULL);
+}
+
+
+/*
+ * Optional auxiliary "rpanic" function
+ */
+vptr (*rpanic_aux)(huge) = NULL;
+
+/*
+ * The system is out of memory, so panic. If "rpanic_aux" is set,
+ * it can be used to free up some memory and do a new "ralloc()",
+ * or if not, it can be used to save things, clean up, and exit.
+ * By default, this function simply crashes the computer.
+ */
+vptr rpanic(huge len)
+{
+ /* Hopefully, we have a real "panic" function */
+ if (rpanic_aux) return ((*rpanic_aux)(len));
+
+ /* Attempt to crash before icky things happen */
+ core("Out of Memory!");
+
+ /* Paranoia */
+ return ((vptr)(NULL));
+}
+
+
+/*
+ * Optional auxiliary "ralloc" function
+ */
+vptr (*ralloc_aux)(huge) = NULL;
+
+
+/*
+ * Allocate some memory
+ */
+vptr ralloc(huge len)
+{
+ vptr mem;
+
+ /* Allow allocation of "zero bytes" */
+ if (len == 0) return ((vptr)(NULL));
+
+#ifdef VERBOSE_RALLOC
+
+ /* Count allocated memory */
+ virt_make += len;
+
+ /* Log important allocations */
+ if (len > virt_size)
+ {
+ char buf[80];
+ sprintf(buf, "Make (%ld): %ld - %ld = %ld.",
+ len, virt_make, virt_kill, virt_make - virt_kill);
+ plog(buf);
+ }
+
+#endif
+
+ /* Use the aux function if set */
+ if (ralloc_aux) mem = (*ralloc_aux)(len);
+
+ /* Use malloc() to allocate some memory */
+ else mem = ((vptr)(malloc((size_t)(len))));
+
+ /* We were able to acquire memory */
+ if (!mem) mem = rpanic(len);
+
+ /* Return the memory, if any */
+ return (mem);
+}
+
+
+
+
+/*
+ * Allocate a constant string, containing the same thing as 'str'
+ */
+cptr string_make(cptr str)
+{
+ huge len = 0;
+ cptr t = str;
+ char *s, *res;
+
+ /* Simple sillyness */
+ if (!str) return (str);
+
+ /* Get the number of chars in the string, including terminator */
+ while (str[len++]) /* loop */;
+
+ /* Allocate space for the string */
+ s = res = (char*)(ralloc(len));
+
+ /* Copy the string (with terminator) */
+ while ((*s++ = *t++) != 0) /* loop */;
+
+ /* Return the allocated, initialized, string */
+ return (res);
+}
+
+
+/*
+ * Un-allocate a string allocated above.
+ * Depends on no changes being made to the string.
+ */
+errr string_free(cptr str)
+{
+ huge len = 0;
+
+ /* Succeed on non-strings */
+ if (!str) return (0);
+
+ /* Count the number of chars in 'str' plus the terminator */
+ while (str[len++]) /* loop */;
+
+ /* Kill the buffer of chars we must have allocated above */
+ rnfree((vptr)(str), len);
+
+ /* Success */
+ return (0);
+}
+
+
diff --git a/src/z-virt.h b/src/z-virt.h
new file mode 100644
index 00000000..b45f3905
--- /dev/null
+++ b/src/z-virt.h
@@ -0,0 +1,179 @@
+/* File: z-virt.h */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifndef INCLUDED_Z_VIRT_H
+#define INCLUDED_Z_VIRT_H
+
+#include "h-basic.h"
+
+#ifdef CHECK_MEMORY_LEAKS
+#include <leak_detector.h>
+#endif
+
+/*
+ * Memory management routines.
+ *
+ * Set ralloc_aux to modify the memory allocation routine.
+ * Set rnfree_aux to modify the memory de-allocation routine.
+ * Set rpanic_aux to let the program react to memory failures.
+ *
+ * These routines work best as a *replacement* for malloc/free.
+ *
+ * The string_make() and string_free() routines handle dynamic strings.
+ * A dynamic string is a string allocated at run-time, which should not
+ * be modified once it has been created.
+ *
+ * Note the macros below which simplify the details of allocation,
+ * deallocation, setting, clearing, casting, size extraction, etc.
+ *
+ * The macros MAKE/C_MAKE and KILL/C_KILL have a "procedural" metaphor,
+ * and they actually modify their arguments.
+ *
+ * Note that, for some reason, some allocation macros may disallow
+ * "stars" in type names, but you can use typedefs to circumvent
+ * this. For example, instead of "type **p; MAKE(p,type*);" you
+ * can use "typedef type *type_ptr; type_ptr *p; MAKE(p,type_ptr)".
+ *
+ * Note that it is assumed that "memset()" will function correctly,
+ * in particular, that it returns its first argument.
+ */
+
+
+
+/**** Available macros ****/
+
+
+/* Size of 'N' things of type 'T' */
+#define C_SIZE(N,T) \
+ ((huge)((N)*(sizeof(T))))
+
+/* Size of one thing of type 'T' */
+#define SIZE(T) \
+ ((huge)(sizeof(T)))
+
+
+/* Compare two arrays of type T[N], at locations P1 and P2 */
+#define C_DIFF(P1,P2,N,T) \
+ (memcmp((char*)(P1),(char*)(P2),C_SIZE(N,T)))
+
+/* Compare two things of type T, at locations P1 and P2 */
+#define DIFF(P1,P2,T) \
+ (memcmp((char*)(P1),(char*)(P2),SIZE(T)))
+
+
+/* Set every byte in an array of type T[N], at location P, to V, and return P */
+#define C_BSET(P,V,N,T) \
+ (T*)(memset((char*)(P),(V),C_SIZE(N,T)))
+
+/* Set every byte in a thing of type T, at location P, to V, and return P */
+#define BSET(P,V,T) \
+ (T*)(memset((char*)(P),(V),SIZE(T)))
+
+
+/* Wipe an array of type T[N], at location P, and return P */
+#define C_WIPE(P,N,T) \
+ (T*)(memset((char*)(P),0,C_SIZE(N,T)))
+
+/* Wipe a thing of type T, at location P, and return P */
+#define WIPE(P,T) \
+ (T*)(memset((char*)(P),0,SIZE(T)))
+
+
+/* Load an array of type T[N], at location P1, from another, at location P2 */
+#define C_COPY(P1,P2,N,T) \
+ (T*)(memcpy((char*)(P1),(char*)(P2),C_SIZE(N,T)))
+
+/* Load a thing of type T, at location P1, from another, at location P2 */
+#define COPY(P1,P2,T) \
+ (T*)(memcpy((char*)(P1),(char*)(P2),SIZE(T)))
+
+
+/* Free an array of N things of type T at P, return NULL */
+#define C_FREE(P,N,T) \
+ (T*)(rnfree(P,C_SIZE(N,T)))
+
+/* Free one thing of type T at P, return NULL */
+#define FREE(P,T) \
+ (T*)(rnfree(P,SIZE(T)))
+
+
+/* Allocate, and return, an array of type T[N] */
+#define C_RNEW(N,T) \
+ ((T*)(ralloc(C_SIZE(N,T))))
+
+/* Allocate, and return, a thing of type T */
+#define RNEW(T) \
+ ((T*)(ralloc(SIZE(T))))
+
+
+/* Allocate, wipe, and return an array of type T[N] */
+#define C_ZNEW(N,T) \
+ ((T*)(C_WIPE(C_RNEW(N,T),N,T)))
+
+/* Allocate, wipe, and return a thing of type T */
+#define ZNEW(T) \
+ ((T*)(WIPE(RNEW(T),T)))
+
+
+/* Allocate a wiped array of type T[N], assign to pointer P */
+#define C_MAKE(P,N,T) \
+ ((P)=C_ZNEW(N,T))
+
+/* Allocate a wiped thing of type T, assign to pointer P */
+#define MAKE(P,T) \
+ ((P)=ZNEW(T))
+
+
+/* Free an array of type T[N], at location P, and set P to NULL */
+#define C_KILL(P,N,T) \
+ ((P)=C_FREE(P,N,T))
+
+/* Free a thing of type T, at location P, and set P to NULL */
+#define KILL(P,T) \
+ ((P)=FREE(P,T))
+
+
+
+/**** Available variables ****/
+
+/* Replacement hook for "rnfree()" */
+extern vptr (*rnfree_aux)(vptr, huge);
+
+/* Replacement hook for "rpanic()" */
+extern vptr (*rpanic_aux)(huge);
+
+/* Replacement hook for "ralloc()" */
+extern vptr (*ralloc_aux)(huge);
+
+
+/**** Available functions ****/
+
+/* De-allocate a given amount of memory */
+extern vptr rnfree(vptr p, huge len);
+
+/* Panic, attempt to Allocate 'len' bytes */
+extern vptr rpanic(huge len);
+
+/* Allocate (and return) 'len', or dump core */
+extern vptr ralloc(huge len);
+
+/* Create a "dynamic string" */
+extern cptr string_make(cptr str);
+
+/* Free a string allocated with "string_make()" */
+extern errr string_free(cptr str);
+
+
+
+
+#endif
+
+
+
diff --git a/src/z_pack.pkg b/src/z_pack.pkg
new file mode 100644
index 00000000..c2582e45
--- /dev/null
+++ b/src/z_pack.pkg
@@ -0,0 +1,693 @@
+/* File: z_pack.pkg */
+
+/*
+ * Purpose: Lua interface defitions for z-*.c
+ * To be processed by tolua to generate C source code.
+ */
+
+$#include "angband.h"
+
+/** @typedef cptr
+ * @note String
+ */
+typedef char* cptr;
+/** @typedef errr
+ * @note Number
+ */
+typedef int errr;
+/** @typedef bool
+ * @note Boolean
+ */
+typedef unsigned char bool;
+/** @typedef byte
+ * @note Number
+ */
+typedef unsigned char byte;
+/** @typedef s16b
+ * @note Number
+ */
+typedef signed short s16b;
+/** @typedef u16b
+ * @note Number
+ */
+typedef unsigned short u16b;
+/** @typedef s32b
+ * @note Number
+ */
+typedef signed int s32b;
+/** @typedef u32b
+ * @note Number
+ */
+typedef unsigned int u32b;
+
+/** @name Terminal actions
+ * @{ */
+/** @def TERM_XTRA_EVENT
+ * @note Process some pending events
+ */
+#define TERM_XTRA_EVENT 1
+/** @def TERM_XTRA_FLUSH
+ * @note Flush all pending events
+ */
+#define TERM_XTRA_FLUSH 2
+/** @def TERM_XTRA_CLEAR
+ * @note Clear the entire window
+ */
+#define TERM_XTRA_CLEAR 3
+/** @def TERM_XTRA_SHAPE
+ * @note Set cursor shape (optional)
+ */
+#define TERM_XTRA_SHAPE 4
+/** @def TERM_XTRA_FROSH
+ * @note Flush one row (optional)
+ */
+#define TERM_XTRA_FROSH 5
+/** @def TERM_XTRA_FRESH
+ * @note Flush all rows (optional)
+ */
+#define TERM_XTRA_FRESH 6
+/** @def TERM_XTRA_NOISE
+ * @note Make a noise (optional)
+ */
+#define TERM_XTRA_NOISE 7
+/** @def TERM_XTRA_SOUND
+ * @note Make a sound (optional)
+ */
+#define TERM_XTRA_SOUND 8
+/** @def TERM_XTRA_BORED
+ * @note Handle stuff when bored (optional)
+ */
+#define TERM_XTRA_BORED 9
+/** @def TERM_XTRA_REACT
+ * @note React to global changes (optional)
+ */
+#define TERM_XTRA_REACT 10
+/** @def TERM_XTRA_ALIVE
+ * @note Change the "hard" level (optional)
+ */
+#define TERM_XTRA_ALIVE 11
+/** @def TERM_XTRA_LEVEL
+ * @note Change the "soft" level (optional)
+ */
+#define TERM_XTRA_LEVEL 12
+/** @def TERM_XTRA_DELAY
+ * @note Delay some milliseconds (optional)
+ */
+#define TERM_XTRA_DELAY 13
+/** @def TERM_XTRA_GET_DELAY
+ * @note Get the cuyrrent time in milliseconds (optional)
+ */
+#define TERM_XTRA_GET_DELAY 14
+/** @def TERM_XTRA_SCANSUBDIR
+ * @note Scan for subdir in a dir
+ */
+#define TERM_XTRA_SCANSUBDIR 15
+/** @} */
+
+/** @var Term_xtra_long
+ * @brief Number
+ */
+extern long Term_xtra_long;
+
+/** @var scansubdir_dir[1024]
+ * @brief String
+ * @note The directory which is scanned for sub-directories.
+ */
+char scansubdir_dir[1024];
+
+/** @var scansubdir_max
+ * @brief Number
+ * @note The number of entries in the scansubdir_result array.
+ */
+int scansubdir_max;
+
+/** @var scansubdir_result[scansubdir_max]
+ * @brief String
+ * @note The sub-directories of scansubdir_dir directory.
+ */
+cptr scansubdir_result[scansubdir_max];
+
+/** @fn Term_xtra(int n, int v)
+ * @brief Generic function to perform system dependant terminal actions.\n
+ * @param n Number \n a terminal action (see TERM_XTRA fields).
+ * @brief Terminal action
+ * @param v Number \n variable depending on the terminal action.
+ * @brief Variable
+ * @return Number \n Result of the terminal action.
+ * @note
+ * The "Term->xtra_hook" hook provides a variety of different functions,
+ * based on the first parameter (which should be taken from the various
+ * TERM_XTRA_* defines) and the second parameter (which may make sense
+ * only for some first parameters). It is available to the program via
+ * the "Term_xtra()" function, though some first parameters are only
+ * "legal" when called from inside this package.
+ * @note (see file z-term.c)
+ */
+extern errr Term_xtra(int n, int v);
+
+/** @fn Term_set_cursor(int v)
+ * @brief Set the cursor visibility.\n
+ * @param v Number \n v is the visibility.
+ * @brief Visibility
+ * @return Number \n 1 if visibility was unchanged, otherwise 0.
+ * @note
+ * Cursor visibility (field "cv") is defined as a boolean, so take care what
+ * value is assigned to "v".
+ * @note (see file z-term.c)
+ */
+extern errr Term_set_cursor(int v);
+
+/** @fn Term_gotoxy(int x, int y)
+ * @brief Place the cursor at a given location.\n
+ * @param x Number \n x-coordinate of target location.
+ * @brief X-coordinate
+ * @param y Number \n y-coordinate of target location.
+ * @brief Y-coordinate
+ * @return Number \n -1 if cursor could not be placed at given location,
+ * otherwise 0.
+ * @note
+ * Note -- "illegal" requests do not move the cursor.\n\n
+ * The cursor is flagged as useful if it placed okay.
+ * @note (see file z-term.c)
+ */
+extern errr Term_gotoxy(int x, int y);
+
+/** @fn Term_putch(int x, int y, byte a, char c)
+ * @brief Move to a location and, using an attr, add a char.\n
+ * @param x Number \n x-coordinate of target location.
+ * @brief X-coordinate
+ * @param y Number \n y-coordinate of target location.
+ * @brief Y-coordinate
+ * @param a Number \n attribute of character.
+ * @brief Attribute
+ * @param c String \n the character.
+ * @brief Character
+ * @return Number \n <0 if error, 0 if success, 1 if success but cursor is
+ * useless.
+ * @note
+ * We return "-2" if the character is "illegal". XXX XXX\n\n
+ * We return "-1" if the cursor is currently unusable.\n\n
+ * We queue the given attr/char for display at the current
+ * cursor location, and advance the cursor to the right,
+ * marking it as unuable and returning "1" if it leaves
+ * the screen, and otherwise returning "0".\n\n
+ * So when this function returns a positive value, future calls to this
+ * function will return negative ones.
+ * @note (see file z-term.c)
+ */
+extern errr Term_putch(int x, int y, byte a, char c);
+
+/** @fn Term_putstr(int x, int y, int n, byte a, cptr s)
+ * @brief Move to a location and, using an attr, add a string.\n
+ * @param x Number \n x-coordinate of target location.
+ * @brief X-coordinate
+ * @param y Number \n y-coordinate of target location.
+ * @brief Y-coordinate
+ * @param n Number \n length of string.
+ * @brief Length
+ * @param a Number \n attribute of string.
+ * @brief Attribute
+ * @param s String \n the string.
+ * @brief String
+ * @return Number \n <0 if error, 0 if success, 1 if success but cursor is
+ * useless.
+ * @note
+ * For length "n", using negative values to imply the largest possible value,
+ * and then we use the minimum of this length and the "actual" length of the
+ * string as the actual number of characters to attempt to display, never
+ * displaying more characters than will actually fit, since we do NOT attempt
+ * to "wrap" the cursor at the screen edge.\n\n
+ * We return "-1" if the cursor is currently unusable.\n
+ * We return "N" if we were "only" able to write "N" chars, even if all of the
+ * given characters fit on the screen, and mark the cursor as unusable for
+ * future attempts.\n\n
+ * So when this function, returns a positive value, future calls to this
+ * function will return negative ones.
+ * @note (see file z-term.c)
+ */
+extern errr Term_putstr(int x, int y, int n, byte a, cptr s);
+
+/** @fn Term_clear(void)
+ * @brief Clear the entire window, and move to the top left corner.
+ * @return Number \n 0 (always).
+ * @note
+ * Note the use of the special "total_erase" code
+ * @note (see file z-term.c)
+ */
+extern errr Term_clear(void);
+
+/** @fn Term_redraw(void)
+ * @brief Redraw (and refresh) the whole window.
+ * @return Number \n 0 (always).
+ * @note (see file z-term.c)
+ */
+extern errr Term_redraw(void);
+
+/** @fn Term_redraw_section(int x1, int y1, int x2, int y2)
+ * @brief Redraw part of a window.\n
+ * @param x1 Number \n x-coordinate of top-left location.
+ * @brief X-coordinate (top left)
+ * @param y1 Number \n y-coordinate of top-left location.
+ * @brief Y-coordinate (top left)
+ * @param x2 Number \n x-coordinate of bottom-right location.
+ * @brief X-coordinate (bottom right)
+ * @param y2 Number \n y-coordinate of bottom-right location.
+ * @brief Y-coordinate (bottom right)
+ * @return Number \n 0 (always).
+ * @note (see file z-term.c)
+ */
+extern errr Term_redraw_section(int x1, int y1, int x2, int y2);
+
+/** @fn Term_get_size(int *w, int *h)
+ * @brief Extract the current window size.\n
+ * @param *w Number
+ * @brief Screen width
+ * @param *h Number
+ * @brief Screen height
+ * @return Number \n 0 (always).
+ * @return *w Number \n The width of the screen (in characters).
+ * @return *h Number \n The height of the screen (in characters).
+ * @note (see file z-term.c)
+ */
+extern errr Term_get_size(int *w, int *h);
+
+/*
+ * random numbers
+ */
+$static s32b lua_rand_int(s32b m) {return rand_int(m);}
+
+/** @fn rand_int(s32b m);
+ * @brief Generate a random integer between 0 and (m - 1).\n
+ * @param m Number \n maximum value of random integer. The random integer
+ * will be less than "m".
+ * @brief Maximum
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_rand_int @ rand_int(s32b m);
+
+/*
+ * Generates a random long integer X where A<=X<=B
+ * The integer X falls along a uniform distribution.
+ * Note: rand_range(0,N-1) == rand_int(N)
+ */
+$static s32b lua_rand_range(s32b A, s32b B) {return ((A) + (rand_int(1+(B)-(A))));}
+
+/** @fn rand_range(s32b A, s32b B);
+ * @brief Generate a random integer between A and B inclusive.\n
+ * @param A Number \n minimum number.
+ * @brief Minimum
+ * @param B Number \n maximum number.
+ * @brief Maximum
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_rand_range @ rand_range(s32b A, s32b B);
+
+/*
+ * Generate a random long integer X where A-D<=X<=A+D
+ * The integer X falls along a uniform distribution.
+ * Note: rand_spread(A,D) == rand_range(A-D,A+D)
+ */
+$static s32b lua_rand_spread(s32b A, s32b D) {return ((A) + (rand_int(1+(D)+(D))) - (D));}
+
+/** @fn rand_spread(s32b A, s32b D);
+ * @brief Generate a radom integer between A-D and A+D inclusive.\n
+ * @param A Number \n average number.
+ * @brief Average
+ * @param D Number \n deviation from average.
+ * @brief Deviation
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_rand_spread @ rand_spread(s32b A, s32b D);
+
+
+/*
+ * Generate a random long integer X where 1<=X<=M
+ * Also, "correctly" handle the case of M<=1
+ */
+$static s32b lua_randint(s32b m) {return rand_int(m) + 1;}
+
+/** @fn randint(s32b m);
+ * @brief Generate a random integer between 1 and M inclusive.\n
+ * @param m Number \n maximum value of random integer.
+ * @brief Maximum
+ * @return Number \n The random number.
+ * @note (see file w_z_pack.c)
+ */
+static s32b lua_randint @ randint(s32b m);
+
+
+/*
+ * Evaluate to TRUE "P" percent of the time
+ */
+$static bool lua_magik(s32b P) {return (rand_int(100) < (P));}
+
+/** @fn magik(s32b P);
+ * @brief Return TRUE "P" % of the time.
+ * @param P Number \n percent chance the function returns TRUE.
+ * @brief Percent true
+ * @return Boolean \n TRUE if a random number from 0 to 99 is less than P,
+ * otherwise FALSE.
+ * @note (see file w_z_pack.c)
+ */
+static bool lua_magik @ magik(s32b P);
+
+
+/**** Available Variables ****/
+/** @var Rand_quick
+ * @brief Boolean
+ * @note
+ * If this is TRUE, then use the "simple" Random Number Generator.\n
+ * If this is FALSE, then use the "complex" Random Number Generator.
+ */
+extern bool Rand_quick;
+
+/** @var Rand_value
+ * @brief Number
+ * @note
+ * The current value (seed) of the simple Random Number Generator.
+ */
+extern u32b Rand_value;
+
+/**** Available Functions ****/
+/** @fn damroll(int num, int sides)
+ * @brief Generates damage for "2d6" style dice rolls.\n
+ * @param num Number \n the number of "dice" used.
+ * @brief Number
+ * @param sides Number \n the number of sides on each "die".
+ * @brief Sides
+ * @return Number \n The random number.
+ * @note
+ * The function simulates the rolling of "num" "sides"-sided dice. Each die
+ * will result in a random number from 1 to "sides".
+ * @note (see file z-rand.c)
+ */
+extern s16b damroll(int num, int sides);
+
+/** @fn maxroll(int num, int sides)
+ * @brief Generate the maximum damage for "num" dice with "sides" sides each.
+ * @param num Number \n The number of "dice" used.
+ * @brief Number
+ * @param sides Number \n The number of sides on each "die".
+ * @brief Sides
+ * @return Number \n "num" * "sides".
+ * @note (see file z-rand.c)
+ */
+extern s16b maxroll(int num, int sides);
+
+
+
+
+
+/*
+ ****************** ZSOCKS ***************
+ */
+
+/** @struct ip_connection
+ * This represents an IP connection
+ */
+struct ip_connection
+{
+ /** @var setup
+ * @brief Boolean
+ * @note Has it been setted up yet?
+ */
+ bool setup;
+
+ /** @var conn_ip
+ * @brief Number
+ * @note The IP where to connect to
+ */
+ long conn_ip;
+
+ /** @var conn_port
+ * @brief Number
+ * @note The port where to connect to
+ */
+ int conn_port;
+
+ /** @var conn_type
+ * @brief Number
+ * @note Type of connection
+ */
+ byte conn_type;
+
+ /** @var connected
+ * @brief Boolean
+ * @note The connection status
+ */
+ bool connected;
+
+ /** @var *socket
+ * @brief void
+ * @note The socket for the connection
+ */
+ void *socket;
+
+ /** @var server
+ * @brief Boolean
+ * @note Is it a server socket ?
+ */
+ bool server;
+};
+
+/*
+ * Possible connection types
+ */
+/** @def ZSOCK_TYPE_TCP */
+#define ZSOCK_TYPE_TCP 1
+
+/* #define ZSOCK_TYPE_UDP 2 */
+
+
+/** @def ZSOCK_TIMER_DELAY
+ * @note The time in milliseconds when to call the sockets callbacks for the
+ * timer
+ */
+#define ZSOCK_TIMER_DELAY 100
+
+
+/** @struct zsock_hooks
+ * @note Hooks needed for a main-foo.c to be sock-able
+ */
+struct zsock_hooks
+{
+ /** @fn *new_connection()
+ * @brief Creates an IP connection.
+ * @return ip_connection \n An IP connection.
+ * @note (see file z-sock.c)
+ */
+ ip_connection *new_connection();
+
+ /** @fn free_connection(ip_connection *c)
+ * @brief Free IP connection "c".\n
+ * @param *c ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @note (see file z-sock.c)
+ */
+ void free_connection(ip_connection *c);
+
+ /** @fn setup(ip_connection *conn, cptr conn_ip, int port, byte conn_type, bool server)
+ * @brief Setup a connection, but do NOT connect.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @param conn_ip String \n IP address of host machine.
+ * @brief Host ip address
+ * @param port Number \n port of host machine.
+ * @brief Host port
+ * @param conn_type Number \n type of connection.
+ * @brief Connection type
+ * @param server Boolean \n TRUE if this is a server socket,
+ * otherwise FALSE.
+ * @brief Server socket?
+ * @return Boolean \n TRUE if socket setup successfully, otherwise FALSE.
+ * @note
+ * You can not setup a connection if it is setup.
+ * @note (see file z-sock.c)
+ */
+ bool setup(ip_connection *conn, cptr conn_ip, int port, byte conn_type, bool server);
+
+ /** @fn unsetup(ip_connection *conn)
+ * @brief Unsetup a connection, but and DO close before if needed.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @return Boolean \n TRUE if socket was setup, otherwise FALSE.
+ * @note
+ * You can not unset a connection if it is not setup.
+ * @note (see file z-sock.c)
+ */
+ bool unsetup(ip_connection *conn);
+
+ /** @fn open(ip_connection *conn)
+ * @brief Open(connect) a well setup-ed connection.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @return Boolean \n TRUE if connection was opened, otherwise FALSE.
+ * @note
+ * You can not open a connection if it is open.
+ * @note (see file z-sock.c)
+ */
+ bool open(ip_connection *conn);
+
+ /** @fn close(ip_connection *conn)
+ * @brief Close a connected connection.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @return Boolean \n TRUE if connection was closed, otherwise FALSE.
+ * @note
+ * You can not close a connection if it is closed.
+ * @note (see file z-sock.c)
+ */
+ bool close(ip_connection *conn);
+
+ /** @fn write(ip_connection *conn, cptr str, int *size)
+ * @brief Send data on the connection.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @param str String \n the string to send along the connection.
+ * @brief String
+ * @param *size Number
+ * @brief Bytes sent
+ * @return Boolean \n TRUE if string was sent successfully,
+ * otherwise FALSE.
+ * @return *size \n the number of bytes sent.
+ * @note
+ * This function will return FALSE if you write to a connection which
+ * is not connected, or if the connection has died.
+ * @note (see file z-sock.c)
+ */
+ bool write(ip_connection *conn, cptr str, int *size);
+
+ /* Read data on the connection */
+ /** @fn read(ip_connection *conn, char *str, int *len, bool raw)
+ * @brief Read data on connection.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @param *str String
+ * @brief String
+ * @param *len Number \n the number of bytes to read.
+ * @brief Bytes to read
+ * @param raw Boolean \n TRUE if all data is to be read at once,
+ * otherwise FALSE.
+ * @brief Read raw data?
+ * @return Boolean \n TRUE if a string was returned, otherwise FALSE.
+ * @return *str String \n The string read.
+ * @return *len Number \n The number of bytes read.
+ * @note
+ * @note (see file z-sock.c)
+ */
+ /* -- DG -- This is done in script.c since it needs a specific wrapper :(
+ bool read(ip_connection *conn, char *str, int *len, bool raw);
+ */
+
+ /** @fn write_simple(ip_connection *conn, cptr str)
+ * @brief Send data on the connection.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @param str String \n the string to send along the connection.
+ * @brief String
+ * @return Boolean \n TRUE if string was sent successfully,
+ * otherwise FALSE.
+ * @note
+ * This is easy to use.
+ * @note (see file z-sock.c)
+ */
+ bool write_simple(ip_connection *conn, cptr str);
+
+ /* Read data on the connection -- easy to use */
+ /** @fn read_simple(ip_connection *conn, char *str, int len)
+ * @brief Read data on the connection.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @param *str String
+ * @brief String
+ * @param len Number \n the number of bytes to read.
+ * @brief Bytes to read
+ * @return Boolean \n TRUE if a string was returned, otherwise FALSE.
+ * @return *str String \n The string read.
+ * @note
+ * This function will return FALSE if you read from a connection which
+ * is not connected, or if the connection has died.
+ * @note (see file z-sock.c)
+ */
+ bool read_simple(ip_connection *conn, char *str, int len);
+
+ /* Accept a connection */
+ /** @fn accept(ip_connection *conn, ip_connection *child)
+ * @brief Allow "conn" to accept a connection from "child".\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Parent ip connection
+ * @param *child ip_connection \n another IP connection.
+ * @brief Child ip connection
+ * @return Boolean \n TRUE if child socket was accepted, otherwise
+ * FALSE.
+ * @note
+ * "conn" must be a server and must be connected.
+ * @note (see file z-sock.c)
+ */
+ bool accept(ip_connection *conn, ip_connection *child);
+
+ /** @fn can_read(ip_connection *conn)
+ * @brief Check if there is any data to be read and return instantly
+ * in any case.\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @return Boolean \n TRUE if there is something to read, otherwise
+ * FALSE.
+ * @note
+ * This function will return FALSE if you read from a connection which
+ * is not connected.
+ * @note (see file z-sock.c)
+ */
+ bool can_read(ip_connection *conn);
+
+ /** @fn wait(ip_connection *conn, int seconds)
+ * @brief Wait for up to "seconds" seconds for data to read from
+ * "conn".\n
+ * @param *conn ip_connection \n an IP connection.
+ * @brief Ip connection
+ * @param seconds Number \n number of seconds to wait for something to
+ * read.
+ * @brief Seconds to wait
+ * @return Boolean TRUE if there is something to read, otherwise
+ * FALSE.
+ * @note
+ * This function will return FALSE if you wait for a connection which
+ * is not connected.
+ * @note (see file z-sock.c)
+ */
+ bool wait(ip_connection *conn, int seconds);
+
+
+ /*
+ * Timer stuff, I hope I can make that look better
+ */
+ /** @fn add_timer(timer_callback callback)
+ * @brief Add "callback" to the list.\n
+ * @param callback timer_callback \n a callback timer.
+ * @brief Callback
+ * @return Boolean \n TRUE (always).
+ * @note
+ * If this is the first callback, the timer will be created.
+ * @note (see file z-sock.c)
+ */
+ bool add_timer(timer_callback callback);
+
+ /** @fn remove_timer(timer_callback callback)
+ * @brief Remove "callback" from the list.\n
+ * @param callback timer_callback \n a callback timer.
+ * @brief Callback
+ * @return Boolean \n TRUE (always).
+ * @note
+ * If this is the last callback, the timer will be deleted.
+ * @note (see file z-sock.c)
+ */
+ bool remove_timer(timer_callback callback);
+};
+
+/** @var zsock
+ * @brief zsock_hooks
+ */
+extern zsock_hooks zsock;
diff --git a/tome.ini b/tome.ini
new file mode 100644
index 00000000..b69fa9e7
--- /dev/null
+++ b/tome.ini
@@ -0,0 +1,94 @@
+[Angband]
+LibPath=.\lib
+Graphics=0
+Bigtile=0
+Sound=0
+GammaVal=0
+
+[Term-0]
+Visible=1
+Font=8X13.FON
+Bizarre=0
+TileWid=8
+TileHgt=13
+NumCols=126
+NumRows=28
+PositionX=0
+PositionY=0
+
+[Term-1]
+Visible=1
+Font=7X13.FON
+Bizarre=0
+TileWid=7
+TileHgt=13
+NumCols=77
+NumRows=24
+PositionX=2
+PositionY=398
+
+[Term-2]
+Visible=0
+Font=6X12.FON
+Bizarre=0
+TileWid=6
+TileHgt=12
+NumCols=76
+NumRows=24
+PositionX=331
+PositionY=126
+
+[Term-3]
+Visible=1
+Font=6X12.FON
+Bizarre=0
+TileWid=6
+TileHgt=12
+NumCols=78
+NumRows=24
+PositionX=549
+PositionY=423
+
+[Term-4]
+Visible=0
+Font=8X13.FON
+Bizarre=0
+TileWid=8
+TileHgt=13
+NumCols=80
+NumRows=24
+PositionX=90
+PositionY=60
+
+[Term-5]
+Visible=0
+Font=8X13.FON
+Bizarre=0
+TileWid=8
+TileHgt=13
+NumCols=80
+NumRows=24
+PositionX=60
+PositionY=40
+
+[Term-6]
+Visible=0
+Font=8X13.FON
+Bizarre=0
+TileWid=8
+TileHgt=13
+NumCols=80
+NumRows=24
+PositionX=30
+PositionY=20
+
+[Term-7]
+Visible=0
+Font=8X13.FON
+Bizarre=0
+TileWid=8
+TileHgt=13
+NumCols=80
+NumRows=24
+PositionX=0
+PositionY=0